Merge "[rdkb][ccn34][crypto][Change eip-related packages to meta-filogic]"
diff --git a/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air.h b/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air.h
index c62174f..bedfaf9 100644
--- a/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air.h
+++ b/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air.h
@@ -1,216 +1,216 @@
-/* FILE NAME:   air.h

- * PURPOSE:

- *      Define the initialization in AIR SDK.

- * NOTES:

- */

-

-#ifndef AIR_H

-#define AIR_H

-

-/* INCLUDE FILE DECLARATIONS

- */

-#include <stdio.h>

-#include <string.h>

-#include "air_types.h"

-#include "air_error.h"

-#include "air_reg.h"

-#include "air_aml.h"

-#include "air_port.h"

-#include "air_vlan.h"

-#include "air_lag.h"

-#include "air_l2.h"

-#include "air_stp.h"

-#include "air_mirror.h"

-#include "air_mib.h"

-#include "air_diag.h"

-#include "air_led.h"

-#include "air_cmd.h"

-#include "air_qos.h"

-#include "air_switch.h"

-#include "air_ver.h"

-#include "air_sec.h"

-#include "air_acl.h"

-#include "air_sptag.h"

-

-/* NAMING CONSTANT DECLARATIONS

- */

-

-/* MACRO FUNCTION DECLARATIONS

- */

-#define AIR_CHECK_PTR(__ptr__) do                                          \

-{                                                                           \

-    if (NULL == (__ptr__))                                                  \

-    {                                                                       \

-        return  AIR_E_BAD_PARAMETER;                                       \

-    }                                                                       \

-} while (0)

-

-#define AIR_PARAM_CHK(expr, errCode) do                                    \

-{                                                                           \

-    if ((I32_T)(expr))                                                      \

-    {                                                                       \

-        return errCode;                                                     \

-    }                                                                       \

-} while (0)

-

-#define AIR_PRINT(fmt,...) do                                              \

-{                                                                           \

-    if (_ext_printf)                                                        \

-    {                                                                       \

-        _ext_printf(fmt, ##__VA_ARGS__);                                    \

-    }                                                                       \

-}while (0)

-

-#define AIR_UDELAY(us) do                                                  \

-{                                                                           \

-    if (_ext_udelay)                                                        \

-    {                                                                       \

-        _ext_udelay(us);                                                    \

-    }                                                                       \

-}while (0)

-

-#define AIR_MALLOC(size) (_ext_malloc ? _ext_malloc(size) : NULL)

-

-#define AIR_FREE(ptr) do                                                   \

-    {                                                                       \

-        if (ptr && _ext_free)                                               \

-        {                                                                   \

-            _ext_free(ptr);                                                 \

-        }                                                                   \

-    }while (0)

-

-#ifndef BIT

-#define BIT(nr) (1UL << (nr))

-#endif	/* End of BIT */

-

-/* bits range: for example BITS(16,23) = 0xFF0000*/

-#ifndef BITS

-#define BITS(m, n)   (~(BIT(m) - 1) & ((BIT(n) - 1) | BIT(n)))

-#endif	/* End of BITS */

-

-/* bits range: for example BITS_RANGE(16,4) = 0x0F0000*/

-#ifndef BITS_RANGE

-#define BITS_RANGE(offset, range)   BITS(offset, ((offset)+(range)-1))

-#endif	/* End of BITS_RANGE */

-

-/* bits offset right: for example BITS_OFF_R(0x1234, 8, 4) = 0x2 */

-#ifndef BITS_OFF_R

-#define BITS_OFF_R(val, offset, range)   ((val >> offset) & (BIT(range) - 1))

-#endif	/* End of BITS_OFF_R */

-

-/* bits offset left: for example BITS_OFF_L(0x1234, 8, 4) = 0x400 */

-#ifndef BITS_OFF_L

-#define BITS_OFF_L(val, offset, range)   ((val & (BIT(range) - 1)) << offset)

-#endif	/* End of BITS_OFF_L */

-

-#ifndef MAX

-#define MAX(a, b)   (((a)>(b))?(a):(b))

-#endif	/* End of MAX */

-

-#ifndef MIN

-#define MIN(a, b)   (((a)<(b))?(a):(b))

-#endif	/* End of MIN */

-

-/* DATA TYPE DECLARATIONS

- */

-typedef AIR_ERROR_NO_T

-(*AIR_PRINTF)(

-    C8_T* fmt,

-    ...);

-

-typedef void

-(*AIR_UDELAY)(

-    UI32_T us);

-

-typedef void*

-(*AIR_MALLOC)(

-    UI32_T size);

-

-typedef void

-(*AIR_FREE)(

-    void *ptr);

-

-typedef struct

-{

-    AML_DEV_ACCESS_T    dev_access;

-    AIR_PRINTF         printf;

-    AIR_UDELAY         udelay;

-    AIR_MALLOC         malloc;

-    AIR_FREE           free;

-}AIR_INIT_PARAM_T;

-

-/* EXPORTED SUBPROGRAM SPECIFICATIONS

- */

-extern AIR_PRINTF _ext_printf;

-extern AIR_UDELAY _ext_udelay;

-extern AIR_MALLOC _ext_malloc;

-extern AIR_FREE   _ext_free;

-

-

-/* FUNCTION NAME:   air_init

- * PURPOSE:

- *      This API is used to initialize the SDK.

- *

- * INPUT:

- *      unit            --  The device unit

- *      ptr_init_param  --  The sdk callback functions.

- *

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_OTHERS

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_init(

-    const UI32_T unit,

-    AIR_INIT_PARAM_T *ptr_init_param);

-

-/* FUNCTION NAME:   air_hw_reset

- * PURPOSE:

- *      This API is used to reset hardware.

- *

- * INPUT:

- *      unit            --  The device unit

- *

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_OTHERS

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_hw_reset(

-    const UI32_T unit);

-

-/* FUNCTION NAME:   air_set_gpio_pin_mux

- * PURPOSE:

- *      This API is used to set gpio pin mux.

- *

- * INPUT:

- *      unit            --  The device unit

- *

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_OTHERS

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_set_gpio_pin_mux(

-    const UI32_T unit);

-

-#endif  /* AIR_H */

-

+/* FILE NAME:   air.h
+ * PURPOSE:
+ *      Define the initialization in AIR SDK.
+ * NOTES:
+ */
+
+#ifndef AIR_H
+#define AIR_H
+
+/* INCLUDE FILE DECLARATIONS
+ */
+#include <stdio.h>
+#include <string.h>
+#include "air_types.h"
+#include "air_error.h"
+#include "air_reg.h"
+#include "air_aml.h"
+#include "air_port.h"
+#include "air_vlan.h"
+#include "air_lag.h"
+#include "air_l2.h"
+#include "air_stp.h"
+#include "air_mirror.h"
+#include "air_mib.h"
+#include "air_diag.h"
+#include "air_led.h"
+#include "air_cmd.h"
+#include "air_qos.h"
+#include "air_switch.h"
+#include "air_ver.h"
+#include "air_sec.h"
+#include "air_acl.h"
+#include "air_sptag.h"
+
+/* NAMING CONSTANT DECLARATIONS
+ */
+
+/* MACRO FUNCTION DECLARATIONS
+ */
+#define AIR_CHECK_PTR(__ptr__) do                                          \
+{                                                                           \
+    if (NULL == (__ptr__))                                                  \
+    {                                                                       \
+        return  AIR_E_BAD_PARAMETER;                                       \
+    }                                                                       \
+} while (0)
+
+#define AIR_PARAM_CHK(expr, errCode) do                                    \
+{                                                                           \
+    if ((I32_T)(expr))                                                      \
+    {                                                                       \
+        return errCode;                                                     \
+    }                                                                       \
+} while (0)
+
+#define AIR_PRINT(fmt,...) do                                              \
+{                                                                           \
+    if (_ext_printf)                                                        \
+    {                                                                       \
+        _ext_printf(fmt, ##__VA_ARGS__);                                    \
+    }                                                                       \
+}while (0)
+
+#define AIR_UDELAY(us) do                                                  \
+{                                                                           \
+    if (_ext_udelay)                                                        \
+    {                                                                       \
+        _ext_udelay(us);                                                    \
+    }                                                                       \
+}while (0)
+
+#define AIR_MALLOC(size) (_ext_malloc ? _ext_malloc(size) : NULL)
+
+#define AIR_FREE(ptr) do                                                   \
+    {                                                                       \
+        if (ptr && _ext_free)                                               \
+        {                                                                   \
+            _ext_free(ptr);                                                 \
+        }                                                                   \
+    }while (0)
+
+#ifndef BIT
+#define BIT(nr) (1UL << (nr))
+#endif	/* End of BIT */
+
+/* bits range: for example BITS(16,23) = 0xFF0000*/
+#ifndef BITS
+#define BITS(m, n)   (~(BIT(m) - 1) & ((BIT(n) - 1) | BIT(n)))
+#endif	/* End of BITS */
+
+/* bits range: for example BITS_RANGE(16,4) = 0x0F0000*/
+#ifndef BITS_RANGE
+#define BITS_RANGE(offset, range)   BITS(offset, ((offset)+(range)-1))
+#endif	/* End of BITS_RANGE */
+
+/* bits offset right: for example BITS_OFF_R(0x1234, 8, 4) = 0x2 */
+#ifndef BITS_OFF_R
+#define BITS_OFF_R(val, offset, range)   ((val >> offset) & (BIT(range) - 1))
+#endif	/* End of BITS_OFF_R */
+
+/* bits offset left: for example BITS_OFF_L(0x1234, 8, 4) = 0x400 */
+#ifndef BITS_OFF_L
+#define BITS_OFF_L(val, offset, range)   ((val & (BIT(range) - 1)) << offset)
+#endif	/* End of BITS_OFF_L */
+
+#ifndef MAX
+#define MAX(a, b)   (((a)>(b))?(a):(b))
+#endif	/* End of MAX */
+
+#ifndef MIN
+#define MIN(a, b)   (((a)<(b))?(a):(b))
+#endif	/* End of MIN */
+
+/* DATA TYPE DECLARATIONS
+ */
+typedef int
+(*AIR_PRINTF)(
+    const C8_T* fmt,
+    ...);
+
+typedef int
+(*AIR_UDELAY)(
+    UI32_T us);
+
+typedef void*
+(*AIR_MALLOC)(
+    long unsigned int size);
+
+typedef void
+(*AIR_FREE)(
+    void *ptr);
+
+typedef struct
+{
+    AML_DEV_ACCESS_T    dev_access;
+    AIR_PRINTF         printf;
+    AIR_UDELAY         udelay;
+    AIR_MALLOC         malloc;
+    AIR_FREE           free;
+}AIR_INIT_PARAM_T;
+
+/* EXPORTED SUBPROGRAM SPECIFICATIONS
+ */
+extern AIR_PRINTF _ext_printf;
+extern AIR_UDELAY _ext_udelay;
+extern AIR_MALLOC _ext_malloc;
+extern AIR_FREE   _ext_free;
+
+
+/* FUNCTION NAME:   air_init
+ * PURPOSE:
+ *      This API is used to initialize the SDK.
+ *
+ * INPUT:
+ *      unit            --  The device unit
+ *      ptr_init_param  --  The sdk callback functions.
+ *
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_OTHERS
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_init(
+    const UI32_T unit,
+    AIR_INIT_PARAM_T *ptr_init_param);
+
+/* FUNCTION NAME:   air_hw_reset
+ * PURPOSE:
+ *      This API is used to reset hardware.
+ *
+ * INPUT:
+ *      unit            --  The device unit
+ *
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_OTHERS
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_hw_reset(
+    const UI32_T unit);
+
+/* FUNCTION NAME:   air_set_gpio_pin_mux
+ * PURPOSE:
+ *      This API is used to set gpio pin mux.
+ *
+ * INPUT:
+ *      unit            --  The device unit
+ *
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_OTHERS
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_set_gpio_pin_mux(
+    const UI32_T unit);
+
+#endif  /* AIR_H */
+
diff --git a/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_aml.h b/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_aml.h
index 49a38b6..8d40bee 100644
--- a/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_aml.h
+++ b/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_aml.h
@@ -1,214 +1,214 @@
-/* FILE NAME:   air_aml.h

- * PURPOSE:

- *      Define the access management layer function in AIR SDK.

- * NOTES:

- */

-

-#ifndef AIR_AML_H

-#define AIR_AML_H

-

-/* INCLUDE FILE DECLARATIONS

- */

-

-/* NAMING CONSTANT DECLARATIONS

- */

-

-/* MACRO FUNCTION DECLARATIONS

- */

-

-/* DATA TYPE DECLARATIONS

- */

-typedef AIR_ERROR_NO_T

-(*AML_DEV_READ_FUNC_T)(

-    const UI32_T    unit,

-    const UI32_T    addr_offset,

-    UI32_T          *ptr_data);

-

-typedef AIR_ERROR_NO_T

-(*AML_DEV_WRITE_FUNC_T)(

-    const UI32_T    unit,

-    const UI32_T    addr_offset,

-    const UI32_T    data);

-

-typedef AIR_ERROR_NO_T

-(*AML_DEV_PHY_READ_FUNC_T)(

-    const UI32_T    unit,

-    const UI32_T    port_id,

-    const UI32_T    addr_offset,

-    UI32_T          *ptr_data);

-

-typedef AIR_ERROR_NO_T

-(*AML_DEV_PHY_WRITE_FUNC_T)(

-    const UI32_T    unit,

-    const UI32_T    port_id,

-    const UI32_T    addr_offset,

-    const UI32_T    data);

-

-typedef AIR_ERROR_NO_T

-(*AML_DEV_PHY_READ_CL45_FUNC_T)(

-    const UI32_T    unit,

-    const UI32_T    port_id,

-    const UI32_T    dev_type,

-    const UI32_T    addr_offset,

-    UI32_T          *ptr_data);

-

-typedef AIR_ERROR_NO_T

-(*AML_DEV_PHY_WRITE_CL45_FUNC_T)(

-    const UI32_T    unit,

-    const UI32_T    port_id,

-    const UI32_T    dev_type,

-    const UI32_T    addr_offset,

-    const UI32_T    data);

-

-/* To read or write the HW-intf registers. */

-typedef struct

-{

-    AML_DEV_READ_FUNC_T             read_callback;

-    AML_DEV_WRITE_FUNC_T            write_callback;

-    AML_DEV_PHY_READ_FUNC_T         phy_read_callback;

-    AML_DEV_PHY_WRITE_FUNC_T        phy_write_callback;

-    AML_DEV_PHY_READ_CL45_FUNC_T    phy_cl45_read_callback;

-    AML_DEV_PHY_WRITE_CL45_FUNC_T   phy_cl45_write_callback;

-}AML_DEV_ACCESS_T;

-

-/* EXPORTED SUBPROGRAM SPECIFICATIONS

- */

-extern AML_DEV_ACCESS_T _ext_dev_access;

-

-/* FUNCTION NAME:   aml_readReg

- * PURPOSE:

- *      To read data from the register of the specified chip unit.

- * INPUT:

- *      unit        -- the device unit

- *      addr_offset -- the address of register

- * OUTPUT:

- *      ptr_data    -- pointer for the register data

- * RETURN:

- *      NPS_E_OK     -- Successfully read the data.

- *      NPS_E_OTHERS -- Failed to read the data.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-aml_readReg(

-    const UI32_T    unit,

-    const UI32_T    addr_offset,

-    UI32_T          *ptr_data);

-

-/* FUNCTION NAME:   aml_writeReg

- * PURPOSE:

- *      To write data to the register of the specified chip unit.

- * INPUT:

- *      unit        -- the device unit

- *      addr_offset -- the address of register

- *      data        -- written data

- * OUTPUT:

- *      none

- * RETURN:

- *      NPS_E_OK     -- Successfully write the data.

- *      NPS_E_OTHERS -- Failed to write the data.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-aml_writeReg(

-    const UI32_T    unit,

-    const UI32_T    addr_offset,

-    const UI32_T    data);

-

-/* FUNCTION NAME:   aml_readPhyReg

- * PURPOSE:

- *      To read data from the phy register of the specified chip unit in Clause22.

- * INPUT:

- *      unit        -- the device unit

- *      port_id     -- physical port number

- *      addr_offset -- the address of phy register

- * OUTPUT:

- *      ptr_data    -- pointer for the register data

- * RETURN:

- *      NPS_E_OK     -- Successfully read the data.

- *      NPS_E_OTHERS -- Failed to read the data.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-aml_readPhyReg(

-    const UI32_T    unit,

-    const UI32_T    port_id,

-    const UI32_T    addr_offset,

-    UI32_T          *ptr_data);

-

-/* FUNCTION NAME:   aml_writePhyReg

- * PURPOSE:

- *      To write data to the phy register of the specified chip unit in Clause22.

- * INPUT:

- *      unit        -- the device unit

- *      port_id     -- physical port number

- *      addr_offset -- the address of phy register

- *      data        -- written data

- * OUTPUT:

- *      none

- * RETURN:

- *      NPS_E_OK     -- Successfully write the data.

- *      NPS_E_OTHERS -- Failed to write the data.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-aml_writePhyReg(

-    const UI32_T    unit,

-    const UI32_T    port_id,

-    const UI32_T    addr_offset,

-    const UI32_T    ptr_data);

-

-/* FUNCTION NAME:   aml_readPhyRegCL45

- * PURPOSE:

- *      To read data from the phy register of the specified chip unit in Clause45.

- * INPUT:

- *      unit        -- the device unit

- *      port_id     -- physical port number

- *      dev_type    -- phy register type

- *      addr_offset -- the address of phy register

- * OUTPUT:

- *      ptr_data    -- pointer for the register data

- * RETURN:

- *      NPS_E_OK     -- Successfully read the data.

- *      NPS_E_OTHERS -- Failed to read the data.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-aml_readPhyRegCL45(

-    const UI32_T    unit,

-    const UI32_T    port_id,

-    const UI32_T    dev_type,

-    const UI32_T    addr_offset,

-    UI32_T          *ptr_data);

-

-/* FUNCTION NAME:   aml_writePhyRegCL45

- * PURPOSE:

- *      To write data to the phy register of the specified chip unit in Clause45.

- * INPUT:

- *      unit        -- the device unit

- *      port_id     -- physical port number

- *      dev_type    -- phy register offset

- *      addr_offset -- the address of phy register

- *      data        -- written data

- * OUTPUT:

- *      none

- * RETURN:

- *      NPS_E_OK     -- Successfully write the data.

- *      NPS_E_OTHERS -- Failed to write the data.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-aml_writePhyRegCL45(

-    const UI32_T    unit,

-    const UI32_T    port_id,

-    const UI32_T    dev_type,

-    const UI32_T    addr_offset,

-    const UI32_T    data);

-

-#endif  /* AIR_AML_H */

-

+/* FILE NAME:   air_aml.h
+ * PURPOSE:
+ *      Define the access management layer function in AIR SDK.
+ * NOTES:
+ */
+
+#ifndef AIR_AML_H
+#define AIR_AML_H
+
+/* INCLUDE FILE DECLARATIONS
+ */
+
+/* NAMING CONSTANT DECLARATIONS
+ */
+
+/* MACRO FUNCTION DECLARATIONS
+ */
+
+/* DATA TYPE DECLARATIONS
+ */
+typedef AIR_ERROR_NO_T
+(*AML_DEV_READ_FUNC_T)(
+    const UI32_T    unit,
+    const UI32_T    addr_offset,
+    UI32_T          *ptr_data);
+
+typedef AIR_ERROR_NO_T
+(*AML_DEV_WRITE_FUNC_T)(
+    const UI32_T    unit,
+    const UI32_T    addr_offset,
+    const UI32_T    data);
+
+typedef AIR_ERROR_NO_T
+(*AML_DEV_PHY_READ_FUNC_T)(
+    const UI32_T    unit,
+    const UI32_T    port_id,
+    const UI32_T    addr_offset,
+    UI32_T          *ptr_data);
+
+typedef AIR_ERROR_NO_T
+(*AML_DEV_PHY_WRITE_FUNC_T)(
+    const UI32_T    unit,
+    const UI32_T    port_id,
+    const UI32_T    addr_offset,
+    const UI32_T    data);
+
+typedef AIR_ERROR_NO_T
+(*AML_DEV_PHY_READ_CL45_FUNC_T)(
+    const UI32_T    unit,
+    const UI32_T    port_id,
+    const UI32_T    dev_type,
+    const UI32_T    addr_offset,
+    UI32_T          *ptr_data);
+
+typedef AIR_ERROR_NO_T
+(*AML_DEV_PHY_WRITE_CL45_FUNC_T)(
+    const UI32_T    unit,
+    const UI32_T    port_id,
+    const UI32_T    dev_type,
+    const UI32_T    addr_offset,
+    const UI32_T    data);
+
+/* To read or write the HW-intf registers. */
+typedef struct
+{
+    AML_DEV_READ_FUNC_T             read_callback;
+    AML_DEV_WRITE_FUNC_T            write_callback;
+    AML_DEV_PHY_READ_FUNC_T         phy_read_callback;
+    AML_DEV_PHY_WRITE_FUNC_T        phy_write_callback;
+    AML_DEV_PHY_READ_CL45_FUNC_T    phy_cl45_read_callback;
+    AML_DEV_PHY_WRITE_CL45_FUNC_T   phy_cl45_write_callback;
+}AML_DEV_ACCESS_T;
+
+/* EXPORTED SUBPROGRAM SPECIFICATIONS
+ */
+extern AML_DEV_ACCESS_T _ext_dev_access;
+
+/* FUNCTION NAME:   aml_readReg
+ * PURPOSE:
+ *      To read data from the register of the specified chip unit.
+ * INPUT:
+ *      unit        -- the device unit
+ *      addr_offset -- the address of register
+ * OUTPUT:
+ *      ptr_data    -- pointer for the register data
+ * RETURN:
+ *      NPS_E_OK     -- Successfully read the data.
+ *      NPS_E_OTHERS -- Failed to read the data.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+aml_readReg(
+    const UI32_T    unit,
+    const UI32_T    addr_offset,
+    UI32_T          *ptr_data);
+
+/* FUNCTION NAME:   aml_writeReg
+ * PURPOSE:
+ *      To write data to the register of the specified chip unit.
+ * INPUT:
+ *      unit        -- the device unit
+ *      addr_offset -- the address of register
+ *      data        -- written data
+ * OUTPUT:
+ *      none
+ * RETURN:
+ *      NPS_E_OK     -- Successfully write the data.
+ *      NPS_E_OTHERS -- Failed to write the data.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+aml_writeReg(
+    const UI32_T    unit,
+    const UI32_T    addr_offset,
+    const UI32_T    data);
+
+/* FUNCTION NAME:   aml_readPhyReg
+ * PURPOSE:
+ *      To read data from the phy register of the specified chip unit in Clause22.
+ * INPUT:
+ *      unit        -- the device unit
+ *      port_id     -- physical port number
+ *      addr_offset -- the address of phy register
+ * OUTPUT:
+ *      ptr_data    -- pointer for the register data
+ * RETURN:
+ *      NPS_E_OK     -- Successfully read the data.
+ *      NPS_E_OTHERS -- Failed to read the data.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+aml_readPhyReg(
+    const UI32_T    unit,
+    const UI32_T    port_id,
+    const UI32_T    addr_offset,
+    UI32_T          *ptr_data);
+
+/* FUNCTION NAME:   aml_writePhyReg
+ * PURPOSE:
+ *      To write data to the phy register of the specified chip unit in Clause22.
+ * INPUT:
+ *      unit        -- the device unit
+ *      port_id     -- physical port number
+ *      addr_offset -- the address of phy register
+ *      data        -- written data
+ * OUTPUT:
+ *      none
+ * RETURN:
+ *      NPS_E_OK     -- Successfully write the data.
+ *      NPS_E_OTHERS -- Failed to write the data.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+aml_writePhyReg(
+    const UI32_T    unit,
+    const UI32_T    port_id,
+    const UI32_T    addr_offset,
+    const UI32_T    ptr_data);
+
+/* FUNCTION NAME:   aml_readPhyRegCL45
+ * PURPOSE:
+ *      To read data from the phy register of the specified chip unit in Clause45.
+ * INPUT:
+ *      unit        -- the device unit
+ *      port_id     -- physical port number
+ *      dev_type    -- phy register type
+ *      addr_offset -- the address of phy register
+ * OUTPUT:
+ *      ptr_data    -- pointer for the register data
+ * RETURN:
+ *      NPS_E_OK     -- Successfully read the data.
+ *      NPS_E_OTHERS -- Failed to read the data.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+aml_readPhyRegCL45(
+    const UI32_T    unit,
+    const UI32_T    port_id,
+    const UI32_T    dev_type,
+    const UI32_T    addr_offset,
+    UI32_T          *ptr_data);
+
+/* FUNCTION NAME:   aml_writePhyRegCL45
+ * PURPOSE:
+ *      To write data to the phy register of the specified chip unit in Clause45.
+ * INPUT:
+ *      unit        -- the device unit
+ *      port_id     -- physical port number
+ *      dev_type    -- phy register offset
+ *      addr_offset -- the address of phy register
+ *      data        -- written data
+ * OUTPUT:
+ *      none
+ * RETURN:
+ *      NPS_E_OK     -- Successfully write the data.
+ *      NPS_E_OTHERS -- Failed to write the data.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+aml_writePhyRegCL45(
+    const UI32_T    unit,
+    const UI32_T    port_id,
+    const UI32_T    dev_type,
+    const UI32_T    addr_offset,
+    const UI32_T    data);
+
+#endif  /* AIR_AML_H */
+
diff --git a/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_cmd.h b/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_cmd.h
index 071d063..766a9a3 100644
--- a/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_cmd.h
+++ b/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_cmd.h
@@ -1,50 +1,50 @@
-/* FILE NAME:   air_cmd.h

- * PURPOSE:

- *      Define the command line function in AIR SDK.

- * NOTES:

- */

-

-#ifndef AIR_CMD_H

-#define AIR_CMD_H

-

-/* INCLUDE FILE DECLARATIONS

- */

-

-/* NAMING CONSTANT DECLARATIONS

- */

-

-/* MACRO FUNCTION DECLARATIONS

- */

-

-/* DATA TYPE DECLARATIONS

- */

-

-/* EXPORTED SUBPROGRAM SPECIFICATIONS

- */

-/* FUNCTION NAME:   air_parse_cmd

- * PURPOSE:

- *      This function is used process diagnostic cmd

- * INPUT:

- *      argc         -- parameter number

- *      argv         -- parameter strings

- * OUTPUT:

- *      None

- * RETURN:

- *      NPS_E_OK     -- Successfully read the data.

- *      NPS_E_OTHERS -- Failed to read the data.

- * NOTES:

- *

- */

-AIR_ERROR_NO_T

-air_parse_cmd(

-    const UI32_T argc,

-    const C8_T **argv);

-

-UI32_T

-_strtoul(

-    const C8_T *cp,

-    C8_T **endp,

-    UI32_T base);

-

-#endif  /* AIR_CMD_H */

-

+/* FILE NAME:   air_cmd.h
+ * PURPOSE:
+ *      Define the command line function in AIR SDK.
+ * NOTES:
+ */
+
+#ifndef AIR_CMD_H
+#define AIR_CMD_H
+
+/* INCLUDE FILE DECLARATIONS
+ */
+
+/* NAMING CONSTANT DECLARATIONS
+ */
+
+/* MACRO FUNCTION DECLARATIONS
+ */
+
+/* DATA TYPE DECLARATIONS
+ */
+
+/* EXPORTED SUBPROGRAM SPECIFICATIONS
+ */
+/* FUNCTION NAME:   air_parse_cmd
+ * PURPOSE:
+ *      This function is used process diagnostic cmd
+ * INPUT:
+ *      argc         -- parameter number
+ *      argv         -- parameter strings
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      NPS_E_OK     -- Successfully read the data.
+ *      NPS_E_OTHERS -- Failed to read the data.
+ * NOTES:
+ *
+ */
+AIR_ERROR_NO_T
+air_parse_cmd(
+    const UI32_T argc,
+    const C8_T **argv);
+
+UI32_T
+_strtoul(
+    const C8_T *cp,
+    C8_T **endp,
+    UI32_T base);
+
+#endif  /* AIR_CMD_H */
+
diff --git a/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_error.h b/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_error.h
index 836d400..a8852ca 100644
--- a/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_error.h
+++ b/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_error.h
@@ -1,56 +1,56 @@
-/* FILE NAME:   air_error.h

- * PURPOSE:

- *      Define the error code in AIR SDK.

- * NOTES:

- */

-

-#ifndef AIR_ERROR_H

-#define AIR_ERROR_H

-

-/* INCLUDE FILE DECLARATIONS

- */

-

-/* NAMING CONSTANT DECLARATIONS

- */

-

-/* MACRO FUNCTION DECLARATIONS

- */

-

-/* DATA TYPE DECLARATIONS

- */

-typedef enum

-{

-    AIR_E_OK = 0,              /* Ok and no error */

-    AIR_E_OTHERS,              /* Operation is unsuccessful */

-    AIR_E_BAD_PARAMETER,       /* Parameter is wrong */

-    AIR_E_TABLE_FULL,          /* Table is full */

-    AIR_E_ENTRY_NOT_FOUND,     /* Entry is not found */

-    AIR_E_ENTRY_EXISTS,        /* Entry already exists */

-    AIR_E_NOT_SUPPORT,         /* Feature is not supported */

-    AIR_E_TIMEOUT,             /* Time out error */

-    AIR_E_LAST

-} AIR_ERROR_NO_T;

-

-/* EXPORTED SUBPROGRAM SPECIFICATIONS

- */

-/* FUNCTION NAME:   air_error_getString

- * PURPOSE:

- *      To obtain the error string of the specified error code

- *

- * INPUT:

- *      cause  -- The specified error code

- * OUTPUT:

- *      None

- * RETURN:

- *      Pointer to the target error string

- *

- * NOTES:

- *

- *

- */

-C8_T *

-air_error_getString(

-    const AIR_ERROR_NO_T cause );

-

-#endif  /* AIR_ERROR_H */

-

+/* FILE NAME:   air_error.h
+ * PURPOSE:
+ *      Define the error code in AIR SDK.
+ * NOTES:
+ */
+
+#ifndef AIR_ERROR_H
+#define AIR_ERROR_H
+
+/* INCLUDE FILE DECLARATIONS
+ */
+
+/* NAMING CONSTANT DECLARATIONS
+ */
+
+/* MACRO FUNCTION DECLARATIONS
+ */
+
+/* DATA TYPE DECLARATIONS
+ */
+typedef enum
+{
+    AIR_E_OK = 0,              /* Ok and no error */
+    AIR_E_OTHERS,              /* Operation is unsuccessful */
+    AIR_E_BAD_PARAMETER,       /* Parameter is wrong */
+    AIR_E_TABLE_FULL,          /* Table is full */
+    AIR_E_ENTRY_NOT_FOUND,     /* Entry is not found */
+    AIR_E_ENTRY_EXISTS,        /* Entry already exists */
+    AIR_E_NOT_SUPPORT,         /* Feature is not supported */
+    AIR_E_TIMEOUT,             /* Time out error */
+    AIR_E_LAST
+} AIR_ERROR_NO_T;
+
+/* EXPORTED SUBPROGRAM SPECIFICATIONS
+ */
+/* FUNCTION NAME:   air_error_getString
+ * PURPOSE:
+ *      To obtain the error string of the specified error code
+ *
+ * INPUT:
+ *      cause  -- The specified error code
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      Pointer to the target error string
+ *
+ * NOTES:
+ *
+ *
+ */
+C8_T *
+air_error_getString(
+    const AIR_ERROR_NO_T cause );
+
+#endif  /* AIR_ERROR_H */
+
diff --git a/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_port.h b/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_port.h
index a8745eb..3a39d9b 100644
--- a/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_port.h
+++ b/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_port.h
@@ -1,963 +1,963 @@
-/* FILE NAME:   air_port.h

- * PURPOSE:

- *      Define port function in AIR SDK.

- *

- * NOTES:

- *      None

- */

-

-#ifndef AIR_PORT_H

-#define AIR_PORT_H

-

-/* INCLUDE FILE DECLARATIONS

- */

-

-/* NAMING CONSTANT DECLARATIONS

- */

-#define AIR_MAX_NUM_OF_UNIT            (1)

-#define AIR_DST_DEFAULT_PORT           (31)

-#define AIR_PORT_TX                    (0x00)

-#define AIR_PORT_RX                    (0x01)

-#define AIR_MAX_NUM_OF_PORTS           (7)

-#define AIR_MAX_NUM_OF_GIGA_PORTS      (5)

-#define AIR_SGMII_PORT_OFFSET_BEGIN    (5)

-#define AIR_SGMII_PORT_OFFSET_END      (6)

-#define AIR_ALL_PORT_BITMAP            (0x7F)

-

-/* Definition of Power Saving mode */

-#define AIR_PORT_PS_LINKSTATUS         (0x1 << 0)

-#define AIR_PORT_PS_EEE                (0x1 << 1)

-#define AIR_PORT_PS_MASK               (0x3)

-

-/* MACRO FUNCTION DECLARATIONS

- */

-

-/* DATA TYPE DECLARATIONS

- */

-/* AIR_PORT_BITMAP_T is the data type for physical port bitmap. */

-#define AIR_BITMAP_SIZE(bit_num)                    ((((bit_num) - 1) / AIR_MAX_NUM_OF_PORTS) + 1)

-#define AIR_PORT_BITMAP_SIZE           AIR_BITMAP_SIZE(AIR_MAX_NUM_OF_PORTS)

-typedef UI32_T   AIR_PORT_BITMAP_T[AIR_PORT_BITMAP_SIZE];

-

-#define AIR_INVALID_ID      (0xFFFFFFFF)

-#define AIR_PORT_INVALID    (AIR_INVALID_ID)

-

-/* Definition of SGMII mode */

-typedef enum

-{

-    AIR_PORT_SGMII_MODE_AN,

-    AIR_PORT_SGMII_MODE_FORCE,

-    AIR_PORT_SGMII_MODE_LAST

-}AIR_PORT_SGMII_MODE_T;

-

-/* Definition of port speed */

-typedef enum

-{

-    AIR_PORT_SPEED_10M,

-    AIR_PORT_SPEED_100M,

-    AIR_PORT_SPEED_1000M,

-    AIR_PORT_SPEED_2500M,

-    AIR_PORT_SPEED_LAST

-}AIR_PORT_SPEED_T;

-

-typedef enum

-{

-    AIR_PORT_DUPLEX_HALF,

-    AIR_PORT_DUPLEX_FULL,

-    AIR_PORT_DUPLEX_LAST

-}AIR_PORT_DUPLEX_T;

-

-typedef enum

-{

-    AIR_PORT_LINK_DOWN,

-    AIR_PORT_LINK_UP,

-    AIR_PORT_LINK_LAST

-}AIR_PORT_LINK_T;

-

-/* Definition of Smart speed down will occur after AN failed how many times */

-typedef enum

-{

-    AIR_PORT_SSD_2T,

-    AIR_PORT_SSD_3T,

-    AIR_PORT_SSD_4T,

-    AIR_PORT_SSD_5T,

-    AIR_PORT_SSD_LAST

-}AIR_PORT_SSD_T;

-

-typedef enum

-{

-    AIR_PORT_VLAN_MODE_PORT_MATRIX = 0,    /* Port matrix mode  */

-    AIR_PORT_VLAN_MODE_FALLBACK,           /* Fallback mode  */

-    AIR_PORT_VLAN_MODE_CHECK,              /* Check mode  */

-    AIR_PORT_VLAN_MODE_SECURITY,           /* Security mode  */

-    AIR_PORT_VLAN_MODE_LAST

-} AIR_PORT_VLAN_MODE_T;

-

-/* Definition of AN Advertisement Register */

-typedef struct AIR_AN_ADV_S

-{

-    BOOL_T advCap10HDX;         /* Advertises 10 BASE-T HDX */

-    BOOL_T advCap10FDX;         /* Advertises 10 BASE-T FDX */

-    BOOL_T advCap100HDX;        /* Advertises 100 BASE-T HDX */

-    BOOL_T advCap100FDX;        /* Advertises 100 BASE-T FDX */

-    BOOL_T advCap1000FDX;       /* Advertises 1000 BASE-T FDX */

-    BOOL_T advPause;            /* Advertieses Asynchronous Pause */

-}AIR_AN_ADV_T;

-

-/* Definition of Link Status of a specific port */

-typedef struct AIR_PORT_STATUS_S

-{

-    BOOL_T link;

-    BOOL_T duplex;

-    UI32_T speed;

-}AIR_PORT_STATUS_T;

-

-/* EXPORTED SUBPROGRAM SPECIFICATIONS

- */

-/* FUNCTION NAME: air_port_setPortMatrix

- * PURPOSE:

- *      Set port matrix from the specified device.

- *

- * INPUT:

- *      unit            --  Unit id

- *      port            --  Port id

- *      port_bitmap     --  Matrix port bitmap

- *

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_OTHERS

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_port_setPortMatrix(

-    const UI32_T    unit,

-    const UI32_T    port,

-    const UI32_T    port_bitmap);

-

-/* FUNCTION NAME: air_port_getPortMatrix

- * PURPOSE:

- *      Get port matrix from the specified device.

- *

- * INPUT:

- *      unit            --  Unit id

- *      port            --  Port id

- *

- * OUTPUT:

- *      p_port_bitmap   --  Matrix port bitmap

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_OTHERS

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_port_getPortMatrix(

-    const UI32_T    unit,

-    const UI32_T    port,

-    UI32_T          *p_port_bitmap);

-

-/* FUNCTION NAME: air_port_setVlanMode

- * PURPOSE:

- *      Set port-based vlan mechanism from the specified device.

- *

- * INPUT:

- *      unit            --  Unit id

- *      port            --  Port id

- *      mode            --  Port vlan mode

- *

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_OTHERS

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_port_setVlanMode(

-    const UI32_T    unit,

-    const UI32_T    port,

-    const AIR_PORT_VLAN_MODE_T mode);

-

-/* FUNCTION NAME: air_port_getVlanMode

- * PURPOSE:

- *      Get port-based vlan mechanism from the specified device.

- *

- * INPUT:

- *      unit            --  Unit id

- *      port            --  Port id

- *

- * OUTPUT:

- *      p_mode          --  Port vlan mode

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_OTHERS

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_port_getVlanMode(

-    const UI32_T    unit,

-    const UI32_T    port,

-    AIR_PORT_VLAN_MODE_T *p_mode);

-

-/* FUNCTION NAME: air_port_setAnMode

- * PURPOSE:

- *      Set the auto-negotiation mode for a specific port.(Auto or Forced)

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- *      state           --  FALSE:Disable

- *                          TRUE: Enable

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_setAnMode(

-    const UI32_T unit,

-    const UI32_T port,

-    const BOOL_T state);

-

-/* FUNCTION NAME: air_port_getAnMode

- * PURPOSE:

- *      Get the auto-negotiation mode for a specific port.

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- *

- * OUTPUT:

- *      state           --  FALSE:Disable

- *                          TRUE: Enable

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_getAnMode(

-    const UI32_T unit,

-    const UI32_T port,

-    BOOL_T *ptr_state);

-

-/* FUNCTION NAME: air_port_setLocalAdvAbility

- * PURPOSE:

- *      Set the auto-negotiation advertisement for a

- *      specific port.

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- *      adv             --  AN advertisement setting

- *

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_setLocalAdvAbility(

-    const UI32_T unit,

-    const UI32_T port,

-    const AIR_AN_ADV_T adv);

-

-/* FUNCTION NAME: air_port_getLocalAdvAbility

- * PURPOSE:

- *      Get the auto-negotiation advertisement for a

- *      specific port.

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- *

- * OUTPUT:

- *      ptr_adv         --  AN advertisement setting

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_getLocalAdvAbility(

-    const UI32_T unit,

-    const UI32_T port,

-    AIR_AN_ADV_T *ptr_adv);

-

-/* FUNCTION NAME: air_port_getRemoteAdvAbility

- * PURPOSE:

- *      Get the auto-negotiation remote advertisement for a

- *      specific port.

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- *

- * OUTPUT:

- *      ptr_lp_adv      --  AN advertisement of link partner

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_getRemoteAdvAbility(

-    const UI32_T unit,

-    const UI32_T port,

-    AIR_AN_ADV_T *ptr_lp_adv);

-

-/* FUNCTION NAME: air_port_setSpeed

- * PURPOSE:

- *      Set the speed for a specific port.

- *      This setting is used on force mode only.

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- *      speed           --  AIR_PORT_SPEED_10M:  10Mbps

- *                          AIR_PORT_SPEED_100M: 100Mbps

- *                          AIR_PORT_SPEED_1000M:1Gbps

- *                          AIR_PORT_SPEED_2500M:2.5Gbps (Port5, Port6)

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_setSpeed(

-    const UI32_T unit,

-    const UI32_T port,

-    const UI32_T speed);

-

-/* FUNCTION NAME: air_port_getSpeed

- * PURPOSE:

- *      Get the speed for a specific port.

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- *

- * OUTPUT:

- *      ptr_speed       --  AIR_PORT_SPEED_10M:  10Mbps

- *                          AIR_PORT_SPEED_100M: 100Mbps

- *                          AIR_PORT_SPEED_1000M:1Gbps

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_getSpeed(

-    const UI32_T unit,

-    const UI32_T port,

-    UI32_T *ptr_speed);

-

-/* FUNCTION NAME: air_port_setDuplex

- * PURPOSE:

- *      Get the duplex for a specific port.

- *      This setting is used on force mode only.

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- *      duplex          --  AIR_PORT_DUPLEX_HALF

- *                          AIR_PORT_DUPLEX_FULL

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_setDuplex(

-    const UI32_T unit,

-    const UI32_T port,

-    const BOOL_T duplex);

-

-/* FUNCTION NAME: air_port_getDuplex

- * PURPOSE:

- *      Get the duplex for a specific port.

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- *

- * OUTPUT:

- *      ptr_duplex      --  AIR_PORT_DUPLEX_HALF

- *                          AIR_PORT_DUPLEX_FULL

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_getDuplex(

-    const UI32_T unit,

-    const UI32_T port,

-    BOOL_T *ptr_duplex);

-

-/* FUNCTION NAME: air_port_getLink

- * PURPOSE:

- *      Get the physical link status for a specific port.

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- *

- * OUTPUT:

- *      ptr_ps          --  AIR_PORT_STATUS_T

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_getLink(

-    const UI32_T unit,

-    const UI32_T port,

-    AIR_PORT_STATUS_T *ptr_ps);

-

-/* FUNCTION NAME: air_port_setBckPres

- * PURPOSE:

- *      Set the back pressure configuration for a specific port.

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- *      bckPres         --  FALSE:Disable

- *                          TRUE: Enable

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_setBckPres(

-    const UI32_T unit,

-    const UI32_T port,

-    const BOOL_T bckPres);

-

-/* FUNCTION NAME: air_port_getBckPres

- * PURPOSE:

- *      Get the back pressure configuration for a specific port.

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- *

- * OUTPUT:

- *      ptr_bckPres     --  FALSE:Disable

- *                          TRUE: Enable

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_getBckPres(

-    const UI32_T unit,

-    const UI32_T port,

-    BOOL_T *ptr_bckPres);

-

-/* FUNCTION NAME: air_port_setFlowCtrl

- * PURPOSE:

- *      Set the flow control configuration for specific port.

- *

- * INPUT:

- *      unit            --  Select device ID

- *      port            --  Select port number (0 - 6)

- *      dir             --  Directions of AIR_PORT_TX or AIR_PORT_RX

- *      fc_en           --  TRUE: Enable select port flow control

- *                          FALSE:Disable select port flow control

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_setFlowCtrl(

-    const UI32_T unit,

-    const UI32_T port,

-    const BOOL_T dir,

-    const BOOL_T fc_en);

-

-/* FUNCTION NAME: air_port_getFlowCtrl

- * PURPOSE:

- *      Get the flow control configuration for specific port.

- *

- * INPUT:

- *      unit            --  Select device ID

- *      port            --  Select port number (0..6)

- *      dir             --  AIR_PORT_TX

- *                          AIR_PORT_RX

- * OUTPUT:

- *      ptr_fc_en       --  FALSE: Port flow control disable

- *                          TRUE: Port flow control enable

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_getFlowCtrl(

-    const UI32_T unit,

-    const UI32_T port,

-    const BOOL_T dir,

-    BOOL_T *ptr_fc_en);

-

-/* FUNCTION NAME: air_port_setJumbo

- * PURPOSE:

- *      Set accepting jumbo frmes with specificied size.

- *

- * INPUT:

- *      unit            --  Select device ID

- *      pkt_len         --  Select max packet length

- *                          RX_PKT_LEN_1518

- *                          RX_PKT_LEN_1536

- *                          RX_PKT_LEN_1552

- *                          RX_PKT_LEN_MAX_JUMBO

- *      frame_len       --  Select max lenght of jumbo frames

- *                          Range : 2 - 15

- *                          Units : K Bytes

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_setJumbo(

-    const UI32_T unit,

-    const UI32_T pkt_len,

-    const UI32_T frame_len);

-

-/* FUNCTION NAME: air_port_getJumbo

- * PURPOSE:

- *      Get accepting jumbo frmes with specificied size.

- *

- * INPUT:

- *      unit            --  Select device ID

- *

- * OUTPUT:

- *      ptr_pkt_len     --  Select max packet length

- *                          RX_PKT_LEN_1518

- *                          RX_PKT_LEN_1536

- *                          RX_PKT_LEN_1552

- *                          RX_PKT_LEN_MAX_JUMBO

- *      ptr_frame_len   --  Select max lenght of jumbo frames

- *                          Range : 2 - 15

- *                          Units : K Bytes

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_getJumbo(

-    const UI32_T unit,

-    UI32_T *ptr_pkt_len,

-    UI32_T *ptr_frame_len);

-

-

-/* FUNCTION NAME: air_port_setPsMode

- * PURPOSE:

- *      Set the power saving mode for a specific port.

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- *      mode            --  Bit-map:

- *                          AIR_PORT_PS_LINKSTATUS

- *                          AIR_PORT_PS_EEE

- *                          FALSE: Disable / TRUE: Enable

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_setPsMode(

-    const UI32_T unit,

-    const UI32_T port,

-    const UI32_T mode);

-

-/* FUNCTION NAME: air_port_getPsMode

- * PURPOSE:

- *      Get the power saving mode for a specific port.

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- * OUTPUT:

- *      ptr_mode        --  Bit-map:

- *                          AIR_PORT_PS_LINKSTATUS

- *                          AIR_PORT_PS_EEE

- *                          FALSE: Disable / TRUE: Enable

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_getPsMode(

-    const UI32_T unit,

-    const UI32_T port,

-    UI32_T *ptr_mode);

-

-/* FUNCTION NAME: air_port_setSmtSpdDwn

- * PURPOSE:

- *      Set Smart speed down feature for a specific port.

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- *      state           --  FALSE:Disable

- *                          TRUE: Enable

- *      time            --  AIR_PORT_SSD_2T

- *                          AIR_PORT_SSD_3T

- *                          AIR_PORT_SSD_4T

- *                          AIR_PORT_SSD_5T

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_setSmtSpdDwn(

-    const UI32_T unit,

-    const UI32_T port,

-    const BOOL_T state,

-    const UI32_T time);

-

-/* FUNCTION NAME: air_port_getSmtSpdDwn

- * PURPOSE:

- *      Get Smart speed down feature for a specific port.

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- *

- * OUTPUT:

- *      ptr_state       --  FALSE:Disable

- *                          TRUE: Enable

- *      ptr_time        --  AIR_PORT_SSD_2T

- *                          AIR_PORT_SSD_3T

- *                          AIR_PORT_SSD_4T

- *                          AIR_PORT_SSD_5T

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_getSmtSpdDwn(

-    const UI32_T unit,

-    const UI32_T port,

-    UI32_T *ptr_state,

-    UI32_T *ptr_time);

-

-/* FUNCTION NAME: air_port_setEnable

- * PURPOSE:

- *      Set powerdown state for a specific port.

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- *      state           --  FALSE:Disable

- *                          TRUE: Enable

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_setEnable(

-    const UI32_T unit,

-    const UI32_T port,

-    const BOOL_T state);

-

-/* FUNCTION NAME: air_port_getEnable

- * PURPOSE:

- *      Get powerdown state for a specific port.

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- *

- * OUTPUT:

- *      ptr_state       --  FALSE:Disable

- *                          TRUE: Enable

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_getEnable(

-    const UI32_T unit,

-    const UI32_T port,

-    UI32_T *ptr_state);

-

-/* FUNCTION NAME: air_port_setSpTag

- * PURPOSE:

- *      Set special tag state of a specific port.

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- *      sptag_en        --  TRUE:  Enable special tag

- *                          FALSE: Disable special tag

- * OUTPUT:

- *        None

- *

- * RETURN:

- *        AIR_E_OK

- *        AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_setSpTag(

-    const UI32_T unit,

-    const UI32_T port,

-    const BOOL_T sptag_en);

-

-/* FUNCTION NAME: air_port_getSpTag

- * PURPOSE:

- *      Get special tag state of a specific port.

- *

- * INPUT:

- *      unit            --  Device ID

- *      port            --  Index of port number

- * OUTPUT:

- *      ptr_sptag_en    --  TRUE:  Special tag enable

- *                          FALSE: Special tag disable

- *

- * RETURN:

- *        AIR_E_OK

- *        AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_getSpTag(

-    const UI32_T unit,

-    const UI32_T port,

-    BOOL_T *ptr_sptag_en);

-

-/* FUNCTION NAME: air_port_set5GBaseRModeEnable

- * PURPOSE:

- *      Set the port5 5GBase-R mode enable

- *

- * INPUT:

- *      unit            --  Device ID

- *

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_set5GBaseRModeEn(

-    const UI32_T unit);

-

-/* FUNCTION NAME: air_port_setHsgmiiModeEnable

- * PURPOSE:

- *      Set the port5 HSGMII mode enable

- *

- * INPUT:

- *      unit            --  Device ID

- *

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_setHsgmiiModeEn(

-    const UI32_T unit);

-

-/* FUNCTION NAME: air_port_setSgmiiMode

- * PURPOSE:

- *      Set the port5 SGMII mode for AN or force

- *

- * INPUT:

- *      unit            --  Device ID

- *      mode            --  AIR_PORT_SGMII_MODE_AN

- *                          AIR_PORT_SGMII_MODE_FORCE

- *      speed           --  AIR_PORT_SPEED_10M:   10Mbps

- *                          AIR_PORT_SPEED_100M:  100Mbps

- *                          AIR_PORT_SPEED_1000M: 1Gbps

- *

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_setSgmiiMode(

-    const UI32_T unit,

-    const UI32_T mode,

-    const UI32_T speed);

-

-

-/* FUNCTION NAME: air_port_setRmiiMode

- * PURPOSE:

- *      Set the port5 RMII mode for 100Mbps or 10Mbps

- *

- * INPUT:

- *      unit            --  Device ID

- *      speed           --  AIR_PORT_SPEED_10M:  10Mbps

- *                          AIR_PORT_SPEED_100M: 100Mbps

- *

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_setRmiiMode(

-    const UI32_T unit,

-    const UI32_T speed);

-

-/* FUNCTION NAME: air_port_setRgmiiMode

- * PURPOSE:

- *      Set the port5 RGMII mode for 1Gbps or 100Mbps or 10Mbps

- *

- * INPUT:

- *      unit            --  Device ID

- *      speed           --  AIR_PORT_SPEED_10M:   10Mbps

- *                          AIR_PORT_SPEED_100M:  100Mbps

- *                          AIR_PORT_SPEED_1000M: 1Gbps

- *

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_BAD_PARAMETER

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_port_setRgmiiMode(

-    const UI32_T unit,

-    const UI32_T speed);

-

-#endif  /* AIR_PORT_H */

-

+/* FILE NAME:   air_port.h
+ * PURPOSE:
+ *      Define port function in AIR SDK.
+ *
+ * NOTES:
+ *      None
+ */
+
+#ifndef AIR_PORT_H
+#define AIR_PORT_H
+
+/* INCLUDE FILE DECLARATIONS
+ */
+
+/* NAMING CONSTANT DECLARATIONS
+ */
+#define AIR_MAX_NUM_OF_UNIT            (1)
+#define AIR_DST_DEFAULT_PORT           (31)
+#define AIR_PORT_TX                    (0x00)
+#define AIR_PORT_RX                    (0x01)
+#define AIR_MAX_NUM_OF_PORTS           (7)
+#define AIR_MAX_NUM_OF_GIGA_PORTS      (5)
+#define AIR_SGMII_PORT_OFFSET_BEGIN    (5)
+#define AIR_SGMII_PORT_OFFSET_END      (6)
+#define AIR_ALL_PORT_BITMAP            (0x7F)
+
+/* Definition of Power Saving mode */
+#define AIR_PORT_PS_LINKSTATUS         (0x1 << 0)
+#define AIR_PORT_PS_EEE                (0x1 << 1)
+#define AIR_PORT_PS_MASK               (0x3)
+
+/* MACRO FUNCTION DECLARATIONS
+ */
+
+/* DATA TYPE DECLARATIONS
+ */
+/* AIR_PORT_BITMAP_T is the data type for physical port bitmap. */
+#define AIR_BITMAP_SIZE(bit_num)                    ((((bit_num) - 1) / AIR_MAX_NUM_OF_PORTS) + 1)
+#define AIR_PORT_BITMAP_SIZE           AIR_BITMAP_SIZE(AIR_MAX_NUM_OF_PORTS)
+typedef UI32_T   AIR_PORT_BITMAP_T[AIR_PORT_BITMAP_SIZE];
+
+#define AIR_INVALID_ID      (0xFFFFFFFF)
+#define AIR_PORT_INVALID    (AIR_INVALID_ID)
+
+/* Definition of SGMII mode */
+typedef enum
+{
+    AIR_PORT_SGMII_MODE_AN,
+    AIR_PORT_SGMII_MODE_FORCE,
+    AIR_PORT_SGMII_MODE_LAST
+}AIR_PORT_SGMII_MODE_T;
+
+/* Definition of port speed */
+typedef enum
+{
+    AIR_PORT_SPEED_10M,
+    AIR_PORT_SPEED_100M,
+    AIR_PORT_SPEED_1000M,
+    AIR_PORT_SPEED_2500M,
+    AIR_PORT_SPEED_LAST
+}AIR_PORT_SPEED_T;
+
+typedef enum
+{
+    AIR_PORT_DUPLEX_HALF,
+    AIR_PORT_DUPLEX_FULL,
+    AIR_PORT_DUPLEX_LAST
+}AIR_PORT_DUPLEX_T;
+
+typedef enum
+{
+    AIR_PORT_LINK_DOWN,
+    AIR_PORT_LINK_UP,
+    AIR_PORT_LINK_LAST
+}AIR_PORT_LINK_T;
+
+/* Definition of Smart speed down will occur after AN failed how many times */
+typedef enum
+{
+    AIR_PORT_SSD_2T,
+    AIR_PORT_SSD_3T,
+    AIR_PORT_SSD_4T,
+    AIR_PORT_SSD_5T,
+    AIR_PORT_SSD_LAST
+}AIR_PORT_SSD_T;
+
+typedef enum
+{
+    AIR_PORT_VLAN_MODE_PORT_MATRIX = 0,    /* Port matrix mode  */
+    AIR_PORT_VLAN_MODE_FALLBACK,           /* Fallback mode  */
+    AIR_PORT_VLAN_MODE_CHECK,              /* Check mode  */
+    AIR_PORT_VLAN_MODE_SECURITY,           /* Security mode  */
+    AIR_PORT_VLAN_MODE_LAST
+} AIR_PORT_VLAN_MODE_T;
+
+/* Definition of AN Advertisement Register */
+typedef struct AIR_AN_ADV_S
+{
+    BOOL_T advCap10HDX;         /* Advertises 10 BASE-T HDX */
+    BOOL_T advCap10FDX;         /* Advertises 10 BASE-T FDX */
+    BOOL_T advCap100HDX;        /* Advertises 100 BASE-T HDX */
+    BOOL_T advCap100FDX;        /* Advertises 100 BASE-T FDX */
+    BOOL_T advCap1000FDX;       /* Advertises 1000 BASE-T FDX */
+    BOOL_T advPause;            /* Advertieses Asynchronous Pause */
+}AIR_AN_ADV_T;
+
+/* Definition of Link Status of a specific port */
+typedef struct AIR_PORT_STATUS_S
+{
+    BOOL_T link;
+    BOOL_T duplex;
+    UI32_T speed;
+}AIR_PORT_STATUS_T;
+
+/* EXPORTED SUBPROGRAM SPECIFICATIONS
+ */
+/* FUNCTION NAME: air_port_setPortMatrix
+ * PURPOSE:
+ *      Set port matrix from the specified device.
+ *
+ * INPUT:
+ *      unit            --  Unit id
+ *      port            --  Port id
+ *      port_bitmap     --  Matrix port bitmap
+ *
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_OTHERS
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_port_setPortMatrix(
+    const UI32_T    unit,
+    const UI32_T    port,
+    const UI32_T    port_bitmap);
+
+/* FUNCTION NAME: air_port_getPortMatrix
+ * PURPOSE:
+ *      Get port matrix from the specified device.
+ *
+ * INPUT:
+ *      unit            --  Unit id
+ *      port            --  Port id
+ *
+ * OUTPUT:
+ *      p_port_bitmap   --  Matrix port bitmap
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_OTHERS
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_port_getPortMatrix(
+    const UI32_T    unit,
+    const UI32_T    port,
+    UI32_T          *p_port_bitmap);
+
+/* FUNCTION NAME: air_port_setVlanMode
+ * PURPOSE:
+ *      Set port-based vlan mechanism from the specified device.
+ *
+ * INPUT:
+ *      unit            --  Unit id
+ *      port            --  Port id
+ *      mode            --  Port vlan mode
+ *
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_OTHERS
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_port_setVlanMode(
+    const UI32_T    unit,
+    const UI32_T    port,
+    const AIR_PORT_VLAN_MODE_T mode);
+
+/* FUNCTION NAME: air_port_getVlanMode
+ * PURPOSE:
+ *      Get port-based vlan mechanism from the specified device.
+ *
+ * INPUT:
+ *      unit            --  Unit id
+ *      port            --  Port id
+ *
+ * OUTPUT:
+ *      p_mode          --  Port vlan mode
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_OTHERS
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_port_getVlanMode(
+    const UI32_T    unit,
+    const UI32_T    port,
+    AIR_PORT_VLAN_MODE_T *p_mode);
+
+/* FUNCTION NAME: air_port_setAnMode
+ * PURPOSE:
+ *      Set the auto-negotiation mode for a specific port.(Auto or Forced)
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ *      state           --  FALSE:Disable
+ *                          TRUE: Enable
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_setAnMode(
+    const UI32_T unit,
+    const UI32_T port,
+    const BOOL_T state);
+
+/* FUNCTION NAME: air_port_getAnMode
+ * PURPOSE:
+ *      Get the auto-negotiation mode for a specific port.
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ *
+ * OUTPUT:
+ *      state           --  FALSE:Disable
+ *                          TRUE: Enable
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_getAnMode(
+    const UI32_T unit,
+    const UI32_T port,
+    BOOL_T *ptr_state);
+
+/* FUNCTION NAME: air_port_setLocalAdvAbility
+ * PURPOSE:
+ *      Set the auto-negotiation advertisement for a
+ *      specific port.
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ *      adv             --  AN advertisement setting
+ *
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_setLocalAdvAbility(
+    const UI32_T unit,
+    const UI32_T port,
+    const AIR_AN_ADV_T adv);
+
+/* FUNCTION NAME: air_port_getLocalAdvAbility
+ * PURPOSE:
+ *      Get the auto-negotiation advertisement for a
+ *      specific port.
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ *
+ * OUTPUT:
+ *      ptr_adv         --  AN advertisement setting
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_getLocalAdvAbility(
+    const UI32_T unit,
+    const UI32_T port,
+    AIR_AN_ADV_T *ptr_adv);
+
+/* FUNCTION NAME: air_port_getRemoteAdvAbility
+ * PURPOSE:
+ *      Get the auto-negotiation remote advertisement for a
+ *      specific port.
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ *
+ * OUTPUT:
+ *      ptr_lp_adv      --  AN advertisement of link partner
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_getRemoteAdvAbility(
+    const UI32_T unit,
+    const UI32_T port,
+    AIR_AN_ADV_T *ptr_lp_adv);
+
+/* FUNCTION NAME: air_port_setSpeed
+ * PURPOSE:
+ *      Set the speed for a specific port.
+ *      This setting is used on force mode only.
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ *      speed           --  AIR_PORT_SPEED_10M:  10Mbps
+ *                          AIR_PORT_SPEED_100M: 100Mbps
+ *                          AIR_PORT_SPEED_1000M:1Gbps
+ *                          AIR_PORT_SPEED_2500M:2.5Gbps (Port5, Port6)
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_setSpeed(
+    const UI32_T unit,
+    const UI32_T port,
+    const UI32_T speed);
+
+/* FUNCTION NAME: air_port_getSpeed
+ * PURPOSE:
+ *      Get the speed for a specific port.
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ *
+ * OUTPUT:
+ *      ptr_speed       --  AIR_PORT_SPEED_10M:  10Mbps
+ *                          AIR_PORT_SPEED_100M: 100Mbps
+ *                          AIR_PORT_SPEED_1000M:1Gbps
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_getSpeed(
+    const UI32_T unit,
+    const UI32_T port,
+    UI32_T *ptr_speed);
+
+/* FUNCTION NAME: air_port_setDuplex
+ * PURPOSE:
+ *      Get the duplex for a specific port.
+ *      This setting is used on force mode only.
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ *      duplex          --  AIR_PORT_DUPLEX_HALF
+ *                          AIR_PORT_DUPLEX_FULL
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_setDuplex(
+    const UI32_T unit,
+    const UI32_T port,
+    const BOOL_T duplex);
+
+/* FUNCTION NAME: air_port_getDuplex
+ * PURPOSE:
+ *      Get the duplex for a specific port.
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ *
+ * OUTPUT:
+ *      ptr_duplex      --  AIR_PORT_DUPLEX_HALF
+ *                          AIR_PORT_DUPLEX_FULL
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_getDuplex(
+    const UI32_T unit,
+    const UI32_T port,
+    BOOL_T *ptr_duplex);
+
+/* FUNCTION NAME: air_port_getLink
+ * PURPOSE:
+ *      Get the physical link status for a specific port.
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ *
+ * OUTPUT:
+ *      ptr_ps          --  AIR_PORT_STATUS_T
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_getLink(
+    const UI32_T unit,
+    const UI32_T port,
+    AIR_PORT_STATUS_T *ptr_ps);
+
+/* FUNCTION NAME: air_port_setBckPres
+ * PURPOSE:
+ *      Set the back pressure configuration for a specific port.
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ *      bckPres         --  FALSE:Disable
+ *                          TRUE: Enable
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_setBckPres(
+    const UI32_T unit,
+    const UI32_T port,
+    const BOOL_T bckPres);
+
+/* FUNCTION NAME: air_port_getBckPres
+ * PURPOSE:
+ *      Get the back pressure configuration for a specific port.
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ *
+ * OUTPUT:
+ *      ptr_bckPres     --  FALSE:Disable
+ *                          TRUE: Enable
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_getBckPres(
+    const UI32_T unit,
+    const UI32_T port,
+    BOOL_T *ptr_bckPres);
+
+/* FUNCTION NAME: air_port_setFlowCtrl
+ * PURPOSE:
+ *      Set the flow control configuration for specific port.
+ *
+ * INPUT:
+ *      unit            --  Select device ID
+ *      port            --  Select port number (0 - 6)
+ *      dir             --  Directions of AIR_PORT_TX or AIR_PORT_RX
+ *      fc_en           --  TRUE: Enable select port flow control
+ *                          FALSE:Disable select port flow control
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_setFlowCtrl(
+    const UI32_T unit,
+    const UI32_T port,
+    const BOOL_T dir,
+    const BOOL_T fc_en);
+
+/* FUNCTION NAME: air_port_getFlowCtrl
+ * PURPOSE:
+ *      Get the flow control configuration for specific port.
+ *
+ * INPUT:
+ *      unit            --  Select device ID
+ *      port            --  Select port number (0..6)
+ *      dir             --  AIR_PORT_TX
+ *                          AIR_PORT_RX
+ * OUTPUT:
+ *      ptr_fc_en       --  FALSE: Port flow control disable
+ *                          TRUE: Port flow control enable
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_getFlowCtrl(
+    const UI32_T unit,
+    const UI32_T port,
+    const BOOL_T dir,
+    BOOL_T *ptr_fc_en);
+
+/* FUNCTION NAME: air_port_setJumbo
+ * PURPOSE:
+ *      Set accepting jumbo frmes with specificied size.
+ *
+ * INPUT:
+ *      unit            --  Select device ID
+ *      pkt_len         --  Select max packet length
+ *                          RX_PKT_LEN_1518
+ *                          RX_PKT_LEN_1536
+ *                          RX_PKT_LEN_1552
+ *                          RX_PKT_LEN_MAX_JUMBO
+ *      frame_len       --  Select max lenght of jumbo frames
+ *                          Range : 2 - 15
+ *                          Units : K Bytes
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_setJumbo(
+    const UI32_T unit,
+    const UI32_T pkt_len,
+    const UI32_T frame_len);
+
+/* FUNCTION NAME: air_port_getJumbo
+ * PURPOSE:
+ *      Get accepting jumbo frmes with specificied size.
+ *
+ * INPUT:
+ *      unit            --  Select device ID
+ *
+ * OUTPUT:
+ *      ptr_pkt_len     --  Select max packet length
+ *                          RX_PKT_LEN_1518
+ *                          RX_PKT_LEN_1536
+ *                          RX_PKT_LEN_1552
+ *                          RX_PKT_LEN_MAX_JUMBO
+ *      ptr_frame_len   --  Select max lenght of jumbo frames
+ *                          Range : 2 - 15
+ *                          Units : K Bytes
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_getJumbo(
+    const UI32_T unit,
+    UI32_T *ptr_pkt_len,
+    UI32_T *ptr_frame_len);
+
+
+/* FUNCTION NAME: air_port_setPsMode
+ * PURPOSE:
+ *      Set the power saving mode for a specific port.
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ *      mode            --  Bit-map:
+ *                          AIR_PORT_PS_LINKSTATUS
+ *                          AIR_PORT_PS_EEE
+ *                          FALSE: Disable / TRUE: Enable
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_setPsMode(
+    const UI32_T unit,
+    const UI32_T port,
+    const UI32_T mode);
+
+/* FUNCTION NAME: air_port_getPsMode
+ * PURPOSE:
+ *      Get the power saving mode for a specific port.
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ * OUTPUT:
+ *      ptr_mode        --  Bit-map:
+ *                          AIR_PORT_PS_LINKSTATUS
+ *                          AIR_PORT_PS_EEE
+ *                          FALSE: Disable / TRUE: Enable
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_getPsMode(
+    const UI32_T unit,
+    const UI32_T port,
+    UI32_T *ptr_mode);
+
+/* FUNCTION NAME: air_port_setSmtSpdDwn
+ * PURPOSE:
+ *      Set Smart speed down feature for a specific port.
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ *      state           --  FALSE:Disable
+ *                          TRUE: Enable
+ *      time            --  AIR_PORT_SSD_2T
+ *                          AIR_PORT_SSD_3T
+ *                          AIR_PORT_SSD_4T
+ *                          AIR_PORT_SSD_5T
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_setSmtSpdDwn(
+    const UI32_T unit,
+    const UI32_T port,
+    const BOOL_T state,
+    const UI32_T time);
+
+/* FUNCTION NAME: air_port_getSmtSpdDwn
+ * PURPOSE:
+ *      Get Smart speed down feature for a specific port.
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ *
+ * OUTPUT:
+ *      ptr_state       --  FALSE:Disable
+ *                          TRUE: Enable
+ *      ptr_time        --  AIR_PORT_SSD_2T
+ *                          AIR_PORT_SSD_3T
+ *                          AIR_PORT_SSD_4T
+ *                          AIR_PORT_SSD_5T
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_getSmtSpdDwn(
+    const UI32_T unit,
+    const UI32_T port,
+    UI32_T *ptr_state,
+    UI32_T *ptr_time);
+
+/* FUNCTION NAME: air_port_setEnable
+ * PURPOSE:
+ *      Set powerdown state for a specific port.
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ *      state           --  FALSE:Disable
+ *                          TRUE: Enable
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_setEnable(
+    const UI32_T unit,
+    const UI32_T port,
+    const BOOL_T state);
+
+/* FUNCTION NAME: air_port_getEnable
+ * PURPOSE:
+ *      Get powerdown state for a specific port.
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ *
+ * OUTPUT:
+ *      ptr_state       --  FALSE:Disable
+ *                          TRUE: Enable
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_getEnable(
+    const UI32_T unit,
+    const UI32_T port,
+    UI32_T *ptr_state);
+
+/* FUNCTION NAME: air_port_setSpTag
+ * PURPOSE:
+ *      Set special tag state of a specific port.
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ *      sptag_en        --  TRUE:  Enable special tag
+ *                          FALSE: Disable special tag
+ * OUTPUT:
+ *        None
+ *
+ * RETURN:
+ *        AIR_E_OK
+ *        AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_setSpTag(
+    const UI32_T unit,
+    const UI32_T port,
+    const BOOL_T sptag_en);
+
+/* FUNCTION NAME: air_port_getSpTag
+ * PURPOSE:
+ *      Get special tag state of a specific port.
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      port            --  Index of port number
+ * OUTPUT:
+ *      ptr_sptag_en    --  TRUE:  Special tag enable
+ *                          FALSE: Special tag disable
+ *
+ * RETURN:
+ *        AIR_E_OK
+ *        AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_getSpTag(
+    const UI32_T unit,
+    const UI32_T port,
+    BOOL_T *ptr_sptag_en);
+
+/* FUNCTION NAME: air_port_set5GBaseRModeEnable
+ * PURPOSE:
+ *      Set the port5 5GBase-R mode enable
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_set5GBaseRModeEn(
+    const UI32_T unit);
+
+/* FUNCTION NAME: air_port_setHsgmiiModeEnable
+ * PURPOSE:
+ *      Set the port5 HSGMII mode enable
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_setHsgmiiModeEn(
+    const UI32_T unit);
+
+/* FUNCTION NAME: air_port_setSgmiiMode
+ * PURPOSE:
+ *      Set the port5 SGMII mode for AN or force
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      mode            --  AIR_PORT_SGMII_MODE_AN
+ *                          AIR_PORT_SGMII_MODE_FORCE
+ *      speed           --  AIR_PORT_SPEED_10M:   10Mbps
+ *                          AIR_PORT_SPEED_100M:  100Mbps
+ *                          AIR_PORT_SPEED_1000M: 1Gbps
+ *
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_setSgmiiMode(
+    const UI32_T unit,
+    const UI32_T mode,
+    const UI32_T speed);
+
+
+/* FUNCTION NAME: air_port_setRmiiMode
+ * PURPOSE:
+ *      Set the port5 RMII mode for 100Mbps or 10Mbps
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      speed           --  AIR_PORT_SPEED_10M:  10Mbps
+ *                          AIR_PORT_SPEED_100M: 100Mbps
+ *
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_setRmiiMode(
+    const UI32_T unit,
+    const UI32_T speed);
+
+/* FUNCTION NAME: air_port_setRgmiiMode
+ * PURPOSE:
+ *      Set the port5 RGMII mode for 1Gbps or 100Mbps or 10Mbps
+ *
+ * INPUT:
+ *      unit            --  Device ID
+ *      speed           --  AIR_PORT_SPEED_10M:   10Mbps
+ *                          AIR_PORT_SPEED_100M:  100Mbps
+ *                          AIR_PORT_SPEED_1000M: 1Gbps
+ *
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_BAD_PARAMETER
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_port_setRgmiiMode(
+    const UI32_T unit,
+    const UI32_T speed);
+
+#endif  /* AIR_PORT_H */
+
diff --git a/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_ver.h b/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_ver.h
index 0681b6c..d702769 100644
--- a/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_ver.h
+++ b/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_ver.h
@@ -1,29 +1,29 @@
-/* FILE NAME: air_ver.h

- * PURPOSE:

- *      Define the version for AIR SDK.

- *

- * NOTES:

- *      None

- */

-

-#ifndef AIR_VER_H

-#define AIR_VER_H

-

-/* INCLUDE FILE DECLARATIONS

- */

-

-/* NAMING CONSTANT DECLARATIONS

- */

-#define AIR_VER_SDK    "v1.0.1"

-

-/* MACRO FUNCTION DECLARATIONS

- */

-

-/* DATA TYPE DECLARATIONS

- */

-

-/* EXPORTED SUBPROGRAM SPECIFICATIONS

- */

-

-#endif  /* AIR_VER_H */

-

+/* FILE NAME: air_ver.h
+ * PURPOSE:
+ *      Define the version for AIR SDK.
+ *
+ * NOTES:
+ *      None
+ */
+
+#ifndef AIR_VER_H
+#define AIR_VER_H
+
+/* INCLUDE FILE DECLARATIONS
+ */
+
+/* NAMING CONSTANT DECLARATIONS
+ */
+#define AIR_VER_SDK    "v1.0.1"
+
+/* MACRO FUNCTION DECLARATIONS
+ */
+
+/* DATA TYPE DECLARATIONS
+ */
+
+/* EXPORTED SUBPROGRAM SPECIFICATIONS
+ */
+
+#endif  /* AIR_VER_H */
+
diff --git a/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_vlan.h b/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_vlan.h
index 1ae6237..3db12e2 100644
--- a/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_vlan.h
+++ b/recipes-devtools/switch/files/src/an8855_sdk/api/inc/air_vlan.h
@@ -1,977 +1,977 @@
-/* FILE NAME:   air_vlan.h

- * PURPOSE:

- *      Define the vlan functions in AIR SDK.

- * NOTES:

- */

-

-#ifndef AIR_VLAN_H

-#define AIR_VLAN_H

-

-/* INCLUDE FILE DECLARATIONS

- */

-

-/* NAMING CONSTANT DECLARATIONS

- */

-#define AIR_VLAN_ID_MIN                             0

-#define AIR_VLAN_ID_MAX                             4095

-#define AIR_DEFAULT_VLAN_ID                         1

-

-#define AIR_FILTER_ID_MAX                           7

-

-/* MACRO FUNCTION DECLARATIONS

- */

-

-/* DATA TYPE DECLARATIONS

- */

-typedef enum

-{

-    AIR_VLAN_PORT_EGS_TAG_CTL_TYPE_UNTAGGED = 0,

-    AIR_VLAN_PORT_EGS_TAG_CTL_TYPE_TAGGED = 2,

-    AIR_VLAN_PORT_EGS_TAG_CTL_TYPE_LAST,

-} AIR_VLAN_PORT_EGS_TAG_CTL_TYPE_T;

-

-typedef enum

-{

-    AIR_PORT_EGS_TAG_ATTR_UNTAGGED = 0,

-    AIR_PORT_EGS_TAG_ATTR_SWAP,

-    AIR_PORT_EGS_TAG_ATTR_TAGGED,

-    AIR_PORT_EGS_TAG_ATTR_STACK,

-    AIR_PORT_EGS_TAG_ATTR_LAST

-} AIR_PORT_EGS_TAG_ATTR_T;

-

-typedef enum

-{

-    AIR_VLAN_ACCEPT_FRAME_TYPE_ALL = 0,            /* untagged, priority-tagged and tagged  */

-    AIR_VLAN_ACCEPT_FRAME_TYPE_TAG_ONLY,           /* tagged                                */

-    AIR_VLAN_ACCEPT_FRAME_TYPE_UNTAG_ONLY,         /* untagged and priority-tagged          */

-    AIR_VLAN_ACCEPT_FRAME_TYPE_RESERVED,           /* reserved                              */

-    AIR_VLAN_ACCEPT_FRAME_TYPE_LAST

-} AIR_VLAN_ACCEPT_FRAME_TYPE_T;

-

-typedef enum

-{

-    AIR_LEAKY_PKT_TYPE_UNICAST = 0,                /* unicast pkt      */

-    AIR_LEAKY_PKT_TYPE_MULTICAST,                  /* multicast pkt    */

-    AIR_LEAKY_PKT_TYPE_BROADCAST,                  /* broadcast pkt    */

-    AIR_LEAKY_PKT_TYPE_LAST

-} AIR_LEAKY_PKT_TYPE_T;

-

-typedef enum

-{

-    AIR_VLAN_PORT_ATTR_USER_PORT = 0,              /* user port        */

-    AIR_VLAN_PORT_ATTR_STACK_PORT,                 /* stack port       */

-    AIR_VLAN_PORT_ATTR_TRANSLATION_PORT,           /* translation port */

-    AIR_VLAN_PORT_ATTR_TRANSPARENT_PORT,           /* transparent port */

-    AIR_VLAN_PORT_ATTR_LAST

-} AIR_VLAN_PORT_ATTR_T;

-

-typedef enum

-{

-    AIR_IGR_PORT_EG_TAG_ATTR_DISABLE = 0,

-    AIR_IGR_PORT_EG_TAG_ATTR_CONSISTENT,

-    AIR_IGR_PORT_EG_TAG_ATTR_UNTAGGED = 4,

-    AIR_IGR_PORT_EG_TAG_ATTR_SWAP,

-    AIR_IGR_PORT_EG_TAG_ATTR_TAGGED,

-    AIR_IGR_PORT_EG_TAG_ATTR_STACK,

-    AIR_IGR_PORT_EG_TAG_ATTR_LAST

-} AIR_IGR_PORT_EG_TAG_ATTR_T;

-

-typedef union AIR_VLAN_ENTRY_S

-{

-    UI8_T valid : 1;

-    struct

-    {

-        UI32_T  vlan_table0;

-        UI32_T  vlan_table1;

-    } vlan_table;

-    struct

-    {

-        UI64_T   valid             : 1;

-        UI64_T   fid               : 4;

-        UI64_T   ivl               : 1;

-        UI64_T   copy_pri          : 1;

-        UI64_T   user_pri          : 3;

-        UI64_T   eg_ctrl_en        : 1;

-        UI64_T   eg_con            : 1;

-        UI64_T   eg_ctrl           : 14;

-        UI64_T   port_mem          : 7;

-        UI64_T   port_stag         : 1;

-        UI64_T   stag              : 12;

-        UI64_T   unm_vlan_drop     : 1;

-    } vlan_entry_format;

-} AIR_VLAN_ENTRY_T;

-

-typedef union AIR_VLAN_ENTRY_ATTR_S

-{

-    UI8_T valid : 1;

-    struct

-    {

-        UI32_T  vlan_table0;

-        UI32_T  vlan_table1;

-        UI32_T  vlan_table2;

-        UI32_T  vlan_table3;

-        UI32_T  vlan_table4;

-    } vlan_table;

-    struct

-    {

-        UI64_T   valid             : 1;

-        UI64_T   fid               : 4;

-        UI64_T   ivl               : 1;

-        UI64_T   copy_pri          : 1;

-        UI64_T   user_pri          : 3;

-        UI64_T   eg_ctrl_en        : 1;

-        UI64_T   eg_con            : 1;

-        UI64_T   eg_ctrl           : 14;

-        UI64_T   port_mem          : 7;

-        UI64_T   port_stag         : 1;

-        UI64_T   stag              : 12;

-        UI64_T   unm_vlan_drop     : 1;

-    } vlan_entry_format;

-#if 0

-    struct

-    {

-        UI64_T   valid             : 1;

-        UI64_T   type              : 3;

-        UI64_T   mac_addr          : 48;

-        UI64_T   mac_mask_len      : 6;

-        UI64_T   priority          : 3;

-        UI64_T   :0;

-        UI64_T   vid               : 12;

-    } mac_based_vlan_entry_format;

-    struct

-    {

-        UI64_T   valid             : 1;

-        UI64_T   type              : 3;

-        UI64_T   check_field       : 36;

-        UI64_T   :0;

-        UI64_T   check_field_mask  : 36;

-        UI64_T   untagged_packet   : 1;

-        UI64_T   vlan_priority     : 3;

-        UI64_T   svid              : 12;

-    } qinq_based_vlan_entry_format;

-    struct

-    {

-        UI64_T   valid             : 1;

-        UI64_T   type              : 3;

-        UI64_T   ipv4              : 32;

-        UI64_T   :0;

-        UI64_T   subnetmask        : 32;

-        UI64_T   priority          : 3;

-        UI64_T   cvid              : 12;

-    } ipv4_based_vlan_entry_format;

-    struct

-    {

-        UI64_T   valid             : 1;

-        UI64_T   :0;

-        UI64_T   ipv6_high         : 64;

-        UI64_T   ipv6_low          : 64;

-        UI64_T   subnetmask        : 8;

-        UI64_T   priority          : 3;

-        UI64_T   cvid              : 12;

-    } ipv6_based_vlan_entry_format;

-#endif

-} AIR_VLAN_ENTRY_ATTR_T;

-

-void

-_air_vlan_readEntry(

-    const UI32_T unit,

-    const UI16_T vid,

-    AIR_VLAN_ENTRY_T* vlan_entry);

-

-void

-_air_vlan_writeEntry(

-    const UI32_T unit,

-    const UI16_T vid,

-    AIR_VLAN_ENTRY_T* vlan_entry);

-

-void

-_air_untagged_vlan_readEntry(

-    const UI32_T unit,

-    const UI16_T vid,

-    AIR_VLAN_ENTRY_ATTR_T* vlan_entry);

-

-void

-_air_untagged_vlan_writeEntry(

-    const UI32_T unit,

-    const UI16_T vid,

-    AIR_VLAN_ENTRY_ATTR_T* vlan_entry);

-

-/* EXPORTED SUBPROGRAM SPECIFICATIONS

- */

-/* FUNCTION NAME:   air_vlan_create

- * PURPOSE:

- *      Create the vlan in the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      p_attr      -- vlan attr

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Vlan creation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_create(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    AIR_VLAN_ENTRY_ATTR_T *p_attr);

-

-/* FUNCTION NAME:   air_vlan_destroy

- * PURPOSE:

- *      Destroy the vlan in the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK     -- Successfully read the data.

- *      AIR_E_OTHERS -- Vlan destroy failed.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_destroy(

-    const UI32_T    unit,

-    const UI16_T    vid);

-

-/* FUNCTION NAME:   air_vlan_destroyAll

- * PURPOSE:

- *      Destroy the vlan in the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK     -- Successfully read the data.

- *      AIR_E_OTHERS -- Vlan destroy failed.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_destroyAll(

-    const UI32_T    unit,

-    const UI32_T    keep_and_restore_default_vlan);

-

-/* FUNCTION NAME:   air_vlan_reset

- * PURPOSE:

- *      Destroy the vlan in the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK     -- Successfully reset the data.

- *      AIR_E_OTHERS -- Vlan reset failed.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_reset(

-    const UI32_T    unit,

-    const UI16_T    vid);

-

-/* FUNCTION NAME:   air_vlan_setFid

- * PURPOSE:

- *      Set the filter id of the vlan to the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      fid         -- filter id

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setFid(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const UI8_T     fid);

-

-/* FUNCTION NAME:   air_vlan_getFid

- * PURPOSE:

- *      Get the filter id of the vlan from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id to be created

- * OUTPUT:

- *      p_fid       -- filter id

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getFid(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    UI8_T           *p_fid);

-

-/* FUNCTION NAME:   air_vlan_addMemberPort

- * PURPOSE:

- *      Add one vlan member to the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      port        -- port id

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_addMemberPort(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const UI32_T    port);

-

-/* FUNCTION NAME:   air_vlan_delMemberPort

- * PURPOSE:

- *      Delete one vlan member from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      port        -- port id

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_delMemberPort(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const UI32_T    port);

-

-/* FUNCTION NAME:   air_vlan_setMemberPort

- * PURPOSE:

- *      Replace the vlan members in the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      port_bitmap -- member port bitmap

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setMemberPort(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const UI32_T    port_bitmap);

-

-/* FUNCTION NAME:   air_vlan_getMemberPort

- * PURPOSE:

- *      Get the vlan members from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      port_bitmap -- member port bitmap

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getMemberPort(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    UI32_T          *p_port_bitmap);

-

-/* FUNCTION NAME:   air_vlan_setIVL

- * PURPOSE:

- *      Set L2 lookup mode IVL/SVL for L2 traffic.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      enable      -- enable IVL

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setIVL(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const BOOL_T    enable);

-

-/* FUNCTION NAME:   air_vlan_getIVL

- * PURPOSE:

- *      Get L2 lookup mode IVL/SVL for L2 traffic.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      p_enable    -- enable IVL

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getIVL(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    BOOL_T          *p_enable);

-

-/* FUNCTION NAME:   air_vlan_setPortAcceptFrameType

- * PURPOSE:

- *      Set vlan accept frame type of the port from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- *      type        -- accept frame type

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setPortAcceptFrameType(

-    const UI32_T    unit,

-    const UI32_T    port,

-    const AIR_VLAN_ACCEPT_FRAME_TYPE_T type);

-

-/* FUNCTION NAME:   air_vlan_getPortAcceptFrameType

- * PURPOSE:

- *      Get vlan accept frame type of the port from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- * OUTPUT:

- *      p_type      -- accept frame type

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getPortAcceptFrameType(

-    const UI32_T    unit,

-    const UI32_T    port,

-    AIR_VLAN_ACCEPT_FRAME_TYPE_T *p_type);

-

-/* FUNCTION NAME:   air_vlan_setPortLeakyVlanEnable

- * PURPOSE:

- *      Set leaky vlan enable of the port from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- *      pkt_type    -- packet type

- *      enable      -- enable leaky

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setPortLeakyVlanEnable(

-    const UI32_T    unit,

-    const UI32_T    port,

-    AIR_LEAKY_PKT_TYPE_T   pkt_type,

-    const BOOL_T    enable);

-

-/* FUNCTION NAME:   air_vlan_getPortLeakyVlanEnable

- * PURPOSE:

- *      Get leaky vlan enable of the port from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- *      pkt_type    -- packet type

- * OUTPUT:

- *      p_enable    -- enable leaky

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getPortLeakyVlanEnable(

-    const UI32_T    unit,

-    const UI32_T    port,

-    AIR_LEAKY_PKT_TYPE_T   pkt_type,

-    BOOL_T          *p_enable);

-

-/* FUNCTION NAME:   air_vlan_setPortAttr

- * PURPOSE:

- *      Set vlan port attribute from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- *      attr        -- vlan port attr

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setPortAttr(

-    const UI32_T    unit,

-    const UI32_T    port,

-    const AIR_VLAN_PORT_ATTR_T attr);

-

-/* FUNCTION NAME:   air_vlan_getPortAttr

- * PURPOSE:

- *      Get vlan port attribute from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- * OUTPUT:

- *      p_attr      -- vlan port attr

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getPortAttr(

-    const UI32_T    unit,

-    const UI32_T    port,

-    AIR_VLAN_PORT_ATTR_T *p_attr);

-

-/* FUNCTION NAME:   air_vlan_setIgrPortTagAttr

- * PURPOSE:

- *      Set vlan incoming port egress tag attribute from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- *      attr        -- egress tag attr

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setIgrPortTagAttr(

-    const UI32_T    unit,

-    const UI32_T    port,

-    const AIR_IGR_PORT_EG_TAG_ATTR_T attr);

-

-/* FUNCTION NAME:   air_vlan_getIgrPortTagAttr

- * PURPOSE:

- *      Get vlan incoming port egress tag attribute from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- * OUTPUT:

- *      p_attr      -- egress tag attr

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getIgrPortTagAttr(

-    const UI32_T    unit,

-    const UI32_T    port,

-    AIR_IGR_PORT_EG_TAG_ATTR_T *p_attr);

-

-/* FUNCTION NAME:   air_vlan_setPortEgsTagAttr

- * PURPOSE:

- *      Set vlan port egress tag attribute from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- *      attr        -- egress tag attr

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setPortEgsTagAttr(

-    const UI32_T    unit,

-    const UI32_T    port,

-    const AIR_PORT_EGS_TAG_ATTR_T attr);

-

-/* FUNCTION NAME:   air_vlan_getPortEgsTagAttr

- * PURPOSE:

- *      Get vlan port egress tag attribute from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- * OUTPUT:

- *      p_attr      -- egress tag attr

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getPortEgsTagAttr(

-    const UI32_T    unit,

-    const UI32_T    port,

-    AIR_PORT_EGS_TAG_ATTR_T *p_attr);

-

-/* FUNCTION NAME:   air_vlan_setPortOuterTPID

- * PURPOSE:

- *      Set stack tag TPID of the port from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- *      tpid        -- TPID

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setPortOuterTPID(

-    const UI32_T    unit,

-    const UI32_T    port,

-    const UI16_T    tpid);

-

-/* FUNCTION NAME:   air_vlan_getPortOuterTPID

- * PURPOSE:

- *      Get stack tag TPID of the port from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- * OUTPUT:

- *      p_tpid        -- TPID

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getPortOuterTPID(

-    const UI32_T    unit,

-    const UI32_T    port,

-    UI16_T          *p_tpid);

-

-/* FUNCTION NAME:   air_vlan_setPortPVID

- * PURPOSE:

- *      Set PVID of the port from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- *      pvid        -- native vlan id

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setPortPVID(

-    const UI32_T    unit,

-    const UI32_T    port,

-    const UI16_T    pvid);

-

-/* FUNCTION NAME:   air_vlan_getPortPVID

- * PURPOSE:

- *      Get PVID of the port from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- * OUTPUT:

- *      p_pvid      -- native vlan id

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getPortPVID(

-    const UI32_T    unit,

-    const UI32_T    port,

-    UI16_T          *p_pvid);

-

-/* FUNCTION NAME:   air_vlan_setServiceTag

- * PURPOSE:

- *      Set Vlan service tag.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      stag        -- service stag

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setServiceTag(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const UI16_T    stag);

-

-/* FUNCTION NAME:   air_vlan_getServiceTag

- * PURPOSE:

- *      Get Vlan service tag.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      p_stag      -- service stag

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getServiceTag(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    UI16_T          *p_stag);

-

-/* FUNCTION NAME:   air_vlan_setEgsTagCtlEnable

- * PURPOSE:

- *      Set per vlan egress tag control.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      enable      -- enable vlan egress tag control

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setEgsTagCtlEnable(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const BOOL_T    enable);

-

-/* FUNCTION NAME:   air_vlan_getEgsTagCtlEnable

- * PURPOSE:

- *      Get per vlan egress tag control.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      p_enable    -- enable vlan egress tag control

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getEgsTagCtlEnable(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    BOOL_T          *p_enable);

-

-/* FUNCTION NAME:   air_vlan_setEgsTagConsistent

- * PURPOSE:

- *      Set per vlan egress tag consistent.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      enable      -- enable vlan egress tag consistent

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setEgsTagConsistent(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const BOOL_T    enable);

-

-/* FUNCTION NAME:   air_vlan_getEgsTagConsistent

- * PURPOSE:

- *      Get per vlan egress tag consistent.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      p_enable    -- enable vlan egress tag consistent

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getEgsTagConsistent(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    BOOL_T          *p_enable);

-

-/* FUNCTION NAME:   air_vlan_setPortBasedStag

- * PURPOSE:

- *      Set vlan port based stag enable.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      enable      -- vlan port based stag enable

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setPortBasedStag(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const BOOL_T    enable);

-

-/* FUNCTION NAME:   air_vlan_getPortBasedStag

- * PURPOSE:

- *      Get vlan port based stag enable.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      p_enable    -- vlan port based stag enable

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getPortBasedStag(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    BOOL_T          *p_enable);

-

-/* FUNCTION NAME:   air_vlan_setPortEgsTagCtl

- * PURPOSE:

- *      Set vlan port egress tag control.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      port        -- port id

- *      tag_ctl     -- egress tag control

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setPortEgsTagCtl(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const UI32_T    port,

-    const AIR_VLAN_PORT_EGS_TAG_CTL_TYPE_T    tag_ctl);

-

-/* FUNCTION NAME:   air_vlan_getPortEgsTagCtl

- * PURPOSE:

- *      Get vlan port egress tag control.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      p_tag_ctl   -- egress tag control

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getPortEgsTagCtl(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const UI32_T    port,

-    AIR_VLAN_PORT_EGS_TAG_CTL_TYPE_T   *ptr_tag_ctl);

-

-#endif  /* AIR_VLAN_H */

-

+/* FILE NAME:   air_vlan.h
+ * PURPOSE:
+ *      Define the vlan functions in AIR SDK.
+ * NOTES:
+ */
+
+#ifndef AIR_VLAN_H
+#define AIR_VLAN_H
+
+/* INCLUDE FILE DECLARATIONS
+ */
+
+/* NAMING CONSTANT DECLARATIONS
+ */
+#define AIR_VLAN_ID_MIN                             0
+#define AIR_VLAN_ID_MAX                             4095
+#define AIR_DEFAULT_VLAN_ID                         1
+
+#define AIR_FILTER_ID_MAX                           7
+
+/* MACRO FUNCTION DECLARATIONS
+ */
+
+/* DATA TYPE DECLARATIONS
+ */
+typedef enum
+{
+    AIR_VLAN_PORT_EGS_TAG_CTL_TYPE_UNTAGGED = 0,
+    AIR_VLAN_PORT_EGS_TAG_CTL_TYPE_TAGGED = 2,
+    AIR_VLAN_PORT_EGS_TAG_CTL_TYPE_LAST,
+} AIR_VLAN_PORT_EGS_TAG_CTL_TYPE_T;
+
+typedef enum
+{
+    AIR_PORT_EGS_TAG_ATTR_UNTAGGED = 0,
+    AIR_PORT_EGS_TAG_ATTR_SWAP,
+    AIR_PORT_EGS_TAG_ATTR_TAGGED,
+    AIR_PORT_EGS_TAG_ATTR_STACK,
+    AIR_PORT_EGS_TAG_ATTR_LAST
+} AIR_PORT_EGS_TAG_ATTR_T;
+
+typedef enum
+{
+    AIR_VLAN_ACCEPT_FRAME_TYPE_ALL = 0,            /* untagged, priority-tagged and tagged  */
+    AIR_VLAN_ACCEPT_FRAME_TYPE_TAG_ONLY,           /* tagged                                */
+    AIR_VLAN_ACCEPT_FRAME_TYPE_UNTAG_ONLY,         /* untagged and priority-tagged          */
+    AIR_VLAN_ACCEPT_FRAME_TYPE_RESERVED,           /* reserved                              */
+    AIR_VLAN_ACCEPT_FRAME_TYPE_LAST
+} AIR_VLAN_ACCEPT_FRAME_TYPE_T;
+
+typedef enum
+{
+    AIR_LEAKY_PKT_TYPE_UNICAST = 0,                /* unicast pkt      */
+    AIR_LEAKY_PKT_TYPE_MULTICAST,                  /* multicast pkt    */
+    AIR_LEAKY_PKT_TYPE_BROADCAST,                  /* broadcast pkt    */
+    AIR_LEAKY_PKT_TYPE_LAST
+} AIR_LEAKY_PKT_TYPE_T;
+
+typedef enum
+{
+    AIR_VLAN_PORT_ATTR_USER_PORT = 0,              /* user port        */
+    AIR_VLAN_PORT_ATTR_STACK_PORT,                 /* stack port       */
+    AIR_VLAN_PORT_ATTR_TRANSLATION_PORT,           /* translation port */
+    AIR_VLAN_PORT_ATTR_TRANSPARENT_PORT,           /* transparent port */
+    AIR_VLAN_PORT_ATTR_LAST
+} AIR_VLAN_PORT_ATTR_T;
+
+typedef enum
+{
+    AIR_IGR_PORT_EG_TAG_ATTR_DISABLE = 0,
+    AIR_IGR_PORT_EG_TAG_ATTR_CONSISTENT,
+    AIR_IGR_PORT_EG_TAG_ATTR_UNTAGGED = 4,
+    AIR_IGR_PORT_EG_TAG_ATTR_SWAP,
+    AIR_IGR_PORT_EG_TAG_ATTR_TAGGED,
+    AIR_IGR_PORT_EG_TAG_ATTR_STACK,
+    AIR_IGR_PORT_EG_TAG_ATTR_LAST
+} AIR_IGR_PORT_EG_TAG_ATTR_T;
+
+typedef union AIR_VLAN_ENTRY_S
+{
+    UI8_T valid : 1;
+    struct
+    {
+        UI32_T  vlan_table0;
+        UI32_T  vlan_table1;
+    } vlan_table;
+    struct
+    {
+        UI64_T   valid             : 1;
+        UI64_T   fid               : 4;
+        UI64_T   ivl               : 1;
+        UI64_T   copy_pri          : 1;
+        UI64_T   user_pri          : 3;
+        UI64_T   eg_ctrl_en        : 1;
+        UI64_T   eg_con            : 1;
+        UI64_T   eg_ctrl           : 14;
+        UI64_T   port_mem          : 7;
+        UI64_T   port_stag         : 1;
+        UI64_T   stag              : 12;
+        UI64_T   unm_vlan_drop     : 1;
+    } vlan_entry_format;
+} AIR_VLAN_ENTRY_T;
+
+typedef union AIR_VLAN_ENTRY_ATTR_S
+{
+    UI8_T valid : 1;
+    struct
+    {
+        UI32_T  vlan_table0;
+        UI32_T  vlan_table1;
+        UI32_T  vlan_table2;
+        UI32_T  vlan_table3;
+        UI32_T  vlan_table4;
+    } vlan_table;
+    struct
+    {
+        UI64_T   valid             : 1;
+        UI64_T   fid               : 4;
+        UI64_T   ivl               : 1;
+        UI64_T   copy_pri          : 1;
+        UI64_T   user_pri          : 3;
+        UI64_T   eg_ctrl_en        : 1;
+        UI64_T   eg_con            : 1;
+        UI64_T   eg_ctrl           : 14;
+        UI64_T   port_mem          : 7;
+        UI64_T   port_stag         : 1;
+        UI64_T   stag              : 12;
+        UI64_T   unm_vlan_drop     : 1;
+    } vlan_entry_format;
+#if 0
+    struct
+    {
+        UI64_T   valid             : 1;
+        UI64_T   type              : 3;
+        UI64_T   mac_addr          : 48;
+        UI64_T   mac_mask_len      : 6;
+        UI64_T   priority          : 3;
+        UI64_T   :0;
+        UI64_T   vid               : 12;
+    } mac_based_vlan_entry_format;
+    struct
+    {
+        UI64_T   valid             : 1;
+        UI64_T   type              : 3;
+        UI64_T   check_field       : 36;
+        UI64_T   :0;
+        UI64_T   check_field_mask  : 36;
+        UI64_T   untagged_packet   : 1;
+        UI64_T   vlan_priority     : 3;
+        UI64_T   svid              : 12;
+    } qinq_based_vlan_entry_format;
+    struct
+    {
+        UI64_T   valid             : 1;
+        UI64_T   type              : 3;
+        UI64_T   ipv4              : 32;
+        UI64_T   :0;
+        UI64_T   subnetmask        : 32;
+        UI64_T   priority          : 3;
+        UI64_T   cvid              : 12;
+    } ipv4_based_vlan_entry_format;
+    struct
+    {
+        UI64_T   valid             : 1;
+        UI64_T   :0;
+        UI64_T   ipv6_high         : 64;
+        UI64_T   ipv6_low          : 64;
+        UI64_T   subnetmask        : 8;
+        UI64_T   priority          : 3;
+        UI64_T   cvid              : 12;
+    } ipv6_based_vlan_entry_format;
+#endif
+} AIR_VLAN_ENTRY_ATTR_T;
+
+void
+_air_vlan_readEntry(
+    const UI32_T unit,
+    const UI16_T vid,
+    AIR_VLAN_ENTRY_T* vlan_entry);
+
+void
+_air_vlan_writeEntry(
+    const UI32_T unit,
+    const UI16_T vid,
+    AIR_VLAN_ENTRY_T* vlan_entry);
+
+void
+_air_untagged_vlan_readEntry(
+    const UI32_T unit,
+    const UI16_T vid,
+    AIR_VLAN_ENTRY_ATTR_T* vlan_entry);
+
+void
+_air_untagged_vlan_writeEntry(
+    const UI32_T unit,
+    const UI16_T vid,
+    AIR_VLAN_ENTRY_ATTR_T* vlan_entry);
+
+/* EXPORTED SUBPROGRAM SPECIFICATIONS
+ */
+/* FUNCTION NAME:   air_vlan_create
+ * PURPOSE:
+ *      Create the vlan in the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      p_attr      -- vlan attr
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Vlan creation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_create(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    AIR_VLAN_ENTRY_ATTR_T *p_attr);
+
+/* FUNCTION NAME:   air_vlan_destroy
+ * PURPOSE:
+ *      Destroy the vlan in the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK     -- Successfully read the data.
+ *      AIR_E_OTHERS -- Vlan destroy failed.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_destroy(
+    const UI32_T    unit,
+    const UI16_T    vid);
+
+/* FUNCTION NAME:   air_vlan_destroyAll
+ * PURPOSE:
+ *      Destroy the vlan in the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK     -- Successfully read the data.
+ *      AIR_E_OTHERS -- Vlan destroy failed.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_destroyAll(
+    const UI32_T    unit,
+    const UI32_T    keep_and_restore_default_vlan);
+
+/* FUNCTION NAME:   air_vlan_reset
+ * PURPOSE:
+ *      Destroy the vlan in the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK     -- Successfully reset the data.
+ *      AIR_E_OTHERS -- Vlan reset failed.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_reset(
+    const UI32_T    unit,
+    const UI16_T    vid);
+
+/* FUNCTION NAME:   air_vlan_setFid
+ * PURPOSE:
+ *      Set the filter id of the vlan to the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      fid         -- filter id
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setFid(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const UI8_T     fid);
+
+/* FUNCTION NAME:   air_vlan_getFid
+ * PURPOSE:
+ *      Get the filter id of the vlan from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id to be created
+ * OUTPUT:
+ *      p_fid       -- filter id
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getFid(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    UI8_T           *p_fid);
+
+/* FUNCTION NAME:   air_vlan_addMemberPort
+ * PURPOSE:
+ *      Add one vlan member to the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      port        -- port id
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_addMemberPort(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const UI32_T    port);
+
+/* FUNCTION NAME:   air_vlan_delMemberPort
+ * PURPOSE:
+ *      Delete one vlan member from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      port        -- port id
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_delMemberPort(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const UI32_T    port);
+
+/* FUNCTION NAME:   air_vlan_setMemberPort
+ * PURPOSE:
+ *      Replace the vlan members in the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      port_bitmap -- member port bitmap
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setMemberPort(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const UI32_T    port_bitmap);
+
+/* FUNCTION NAME:   air_vlan_getMemberPort
+ * PURPOSE:
+ *      Get the vlan members from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      port_bitmap -- member port bitmap
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getMemberPort(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    UI32_T          *p_port_bitmap);
+
+/* FUNCTION NAME:   air_vlan_setIVL
+ * PURPOSE:
+ *      Set L2 lookup mode IVL/SVL for L2 traffic.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      enable      -- enable IVL
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setIVL(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const BOOL_T    enable);
+
+/* FUNCTION NAME:   air_vlan_getIVL
+ * PURPOSE:
+ *      Get L2 lookup mode IVL/SVL for L2 traffic.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      p_enable    -- enable IVL
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getIVL(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    BOOL_T          *p_enable);
+
+/* FUNCTION NAME:   air_vlan_setPortAcceptFrameType
+ * PURPOSE:
+ *      Set vlan accept frame type of the port from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ *      type        -- accept frame type
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setPortAcceptFrameType(
+    const UI32_T    unit,
+    const UI32_T    port,
+    const AIR_VLAN_ACCEPT_FRAME_TYPE_T type);
+
+/* FUNCTION NAME:   air_vlan_getPortAcceptFrameType
+ * PURPOSE:
+ *      Get vlan accept frame type of the port from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ * OUTPUT:
+ *      p_type      -- accept frame type
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getPortAcceptFrameType(
+    const UI32_T    unit,
+    const UI32_T    port,
+    AIR_VLAN_ACCEPT_FRAME_TYPE_T *p_type);
+
+/* FUNCTION NAME:   air_vlan_setPortLeakyVlanEnable
+ * PURPOSE:
+ *      Set leaky vlan enable of the port from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ *      pkt_type    -- packet type
+ *      enable      -- enable leaky
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setPortLeakyVlanEnable(
+    const UI32_T    unit,
+    const UI32_T    port,
+    AIR_LEAKY_PKT_TYPE_T   pkt_type,
+    const BOOL_T    enable);
+
+/* FUNCTION NAME:   air_vlan_getPortLeakyVlanEnable
+ * PURPOSE:
+ *      Get leaky vlan enable of the port from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ *      pkt_type    -- packet type
+ * OUTPUT:
+ *      p_enable    -- enable leaky
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getPortLeakyVlanEnable(
+    const UI32_T    unit,
+    const UI32_T    port,
+    AIR_LEAKY_PKT_TYPE_T   pkt_type,
+    BOOL_T          *p_enable);
+
+/* FUNCTION NAME:   air_vlan_setPortAttr
+ * PURPOSE:
+ *      Set vlan port attribute from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ *      attr        -- vlan port attr
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setPortAttr(
+    const UI32_T    unit,
+    const UI32_T    port,
+    const AIR_VLAN_PORT_ATTR_T attr);
+
+/* FUNCTION NAME:   air_vlan_getPortAttr
+ * PURPOSE:
+ *      Get vlan port attribute from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ * OUTPUT:
+ *      p_attr      -- vlan port attr
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getPortAttr(
+    const UI32_T    unit,
+    const UI32_T    port,
+    AIR_VLAN_PORT_ATTR_T *p_attr);
+
+/* FUNCTION NAME:   air_vlan_setIgrPortTagAttr
+ * PURPOSE:
+ *      Set vlan incoming port egress tag attribute from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ *      attr        -- egress tag attr
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setIgrPortTagAttr(
+    const UI32_T    unit,
+    const UI32_T    port,
+    const AIR_IGR_PORT_EG_TAG_ATTR_T attr);
+
+/* FUNCTION NAME:   air_vlan_getIgrPortTagAttr
+ * PURPOSE:
+ *      Get vlan incoming port egress tag attribute from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ * OUTPUT:
+ *      p_attr      -- egress tag attr
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getIgrPortTagAttr(
+    const UI32_T    unit,
+    const UI32_T    port,
+    AIR_IGR_PORT_EG_TAG_ATTR_T *p_attr);
+
+/* FUNCTION NAME:   air_vlan_setPortEgsTagAttr
+ * PURPOSE:
+ *      Set vlan port egress tag attribute from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ *      attr        -- egress tag attr
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setPortEgsTagAttr(
+    const UI32_T    unit,
+    const UI32_T    port,
+    const AIR_PORT_EGS_TAG_ATTR_T attr);
+
+/* FUNCTION NAME:   air_vlan_getPortEgsTagAttr
+ * PURPOSE:
+ *      Get vlan port egress tag attribute from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ * OUTPUT:
+ *      p_attr      -- egress tag attr
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getPortEgsTagAttr(
+    const UI32_T    unit,
+    const UI32_T    port,
+    AIR_PORT_EGS_TAG_ATTR_T *p_attr);
+
+/* FUNCTION NAME:   air_vlan_setPortOuterTPID
+ * PURPOSE:
+ *      Set stack tag TPID of the port from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ *      tpid        -- TPID
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setPortOuterTPID(
+    const UI32_T    unit,
+    const UI32_T    port,
+    const UI16_T    tpid);
+
+/* FUNCTION NAME:   air_vlan_getPortOuterTPID
+ * PURPOSE:
+ *      Get stack tag TPID of the port from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ * OUTPUT:
+ *      p_tpid        -- TPID
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getPortOuterTPID(
+    const UI32_T    unit,
+    const UI32_T    port,
+    UI16_T          *p_tpid);
+
+/* FUNCTION NAME:   air_vlan_setPortPVID
+ * PURPOSE:
+ *      Set PVID of the port from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ *      pvid        -- native vlan id
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setPortPVID(
+    const UI32_T    unit,
+    const UI32_T    port,
+    const UI16_T    pvid);
+
+/* FUNCTION NAME:   air_vlan_getPortPVID
+ * PURPOSE:
+ *      Get PVID of the port from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ * OUTPUT:
+ *      p_pvid      -- native vlan id
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getPortPVID(
+    const UI32_T    unit,
+    const UI32_T    port,
+    UI16_T          *p_pvid);
+
+/* FUNCTION NAME:   air_vlan_setServiceTag
+ * PURPOSE:
+ *      Set Vlan service tag.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      stag        -- service stag
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setServiceTag(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const UI16_T    stag);
+
+/* FUNCTION NAME:   air_vlan_getServiceTag
+ * PURPOSE:
+ *      Get Vlan service tag.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      p_stag      -- service stag
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getServiceTag(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    UI16_T          *p_stag);
+
+/* FUNCTION NAME:   air_vlan_setEgsTagCtlEnable
+ * PURPOSE:
+ *      Set per vlan egress tag control.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      enable      -- enable vlan egress tag control
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setEgsTagCtlEnable(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const BOOL_T    enable);
+
+/* FUNCTION NAME:   air_vlan_getEgsTagCtlEnable
+ * PURPOSE:
+ *      Get per vlan egress tag control.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      p_enable    -- enable vlan egress tag control
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getEgsTagCtlEnable(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    BOOL_T          *p_enable);
+
+/* FUNCTION NAME:   air_vlan_setEgsTagConsistent
+ * PURPOSE:
+ *      Set per vlan egress tag consistent.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      enable      -- enable vlan egress tag consistent
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setEgsTagConsistent(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const BOOL_T    enable);
+
+/* FUNCTION NAME:   air_vlan_getEgsTagConsistent
+ * PURPOSE:
+ *      Get per vlan egress tag consistent.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      p_enable    -- enable vlan egress tag consistent
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getEgsTagConsistent(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    BOOL_T          *p_enable);
+
+/* FUNCTION NAME:   air_vlan_setPortBasedStag
+ * PURPOSE:
+ *      Set vlan port based stag enable.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      enable      -- vlan port based stag enable
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setPortBasedStag(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const BOOL_T    enable);
+
+/* FUNCTION NAME:   air_vlan_getPortBasedStag
+ * PURPOSE:
+ *      Get vlan port based stag enable.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      p_enable    -- vlan port based stag enable
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getPortBasedStag(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    BOOL_T          *p_enable);
+
+/* FUNCTION NAME:   air_vlan_setPortEgsTagCtl
+ * PURPOSE:
+ *      Set vlan port egress tag control.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      port        -- port id
+ *      tag_ctl     -- egress tag control
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setPortEgsTagCtl(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const UI32_T    port,
+    const AIR_VLAN_PORT_EGS_TAG_CTL_TYPE_T    tag_ctl);
+
+/* FUNCTION NAME:   air_vlan_getPortEgsTagCtl
+ * PURPOSE:
+ *      Get vlan port egress tag control.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      p_tag_ctl   -- egress tag control
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getPortEgsTagCtl(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const UI32_T    port,
+    AIR_VLAN_PORT_EGS_TAG_CTL_TYPE_T   *ptr_tag_ctl);
+
+#endif  /* AIR_VLAN_H */
+
diff --git a/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_aml.c b/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_aml.c
index 38b85a4..2e4119c 100644
--- a/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_aml.c
+++ b/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_aml.c
@@ -1,218 +1,218 @@
-/* FILE NAME:  air_aml.c

- * PURPOSE:

- *      It provides access management layer function.

- * NOTES:

- *

- */

-

-/* INCLUDE FILE DECLARATIONS

- */

-#include "air.h"

-

-/* NAMING CONSTANT DECLARATIONS

- */

-

-/* MACRO FUNCTION DECLARATIONS

- */

-

-/* DATA TYPE DECLARATIONS

- */

-

-/* GLOBAL VARIABLE DECLARATIONS

- */

-AML_DEV_ACCESS_T _ext_dev_access;

-

-/* EXPORTED SUBPROGRAM BODIES

- */

-

-/* LOCAL SUBPROGRAM BODIES

- */

-/* FUNCTION NAME:   aml_readReg

- * PURPOSE:

- *      To read data from the register of the specified chip unit.

- * INPUT:

- *      unit        -- the device unit

- *      addr_offset -- the address of register

- * OUTPUT:

- *      ptr_data    -- pointer for the register data

- * RETURN:

- *      NPS_E_OK     -- Successfully read the data.

- *      NPS_E_OTHERS -- Failed to read the data.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-aml_readReg(

-    const UI32_T    unit,

-    const UI32_T    addr_offset,

-    UI32_T          *ptr_data)

-{

-    AIR_CHECK_PTR(ptr_data);

-

-    if (!_ext_dev_access.read_callback)

-    {

-        return AIR_E_OTHERS;

-    }

-

-    return _ext_dev_access.read_callback(unit, addr_offset, ptr_data);

-}

-

-/* FUNCTION NAME:   aml_writeReg

- * PURPOSE:

- *      To write data to the register of the specified chip unit.

- * INPUT:

- *      unit        -- the device unit

- *      addr_offset -- the address of register

- *      data        -- written data

- * OUTPUT:

- *      none

- * RETURN:

- *      NPS_E_OK     -- Successfully write the data.

- *      NPS_E_OTHERS -- Failed to write the data.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-aml_writeReg(

-    const UI32_T    unit,

-    const UI32_T    addr_offset,

-    const UI32_T    data)

-{

-    if (!_ext_dev_access.write_callback)

-    {

-        return AIR_E_OTHERS;

-    }

-

-    return _ext_dev_access.write_callback(unit, addr_offset, data);

-}

-

-/* FUNCTION NAME:   aml_readPhyReg

- * PURPOSE:

- *      To read data from the phy register of the specified chip unit in Clause22.

- * INPUT:

- *      unit        -- the device unit

- *      port_id     -- physical port number

- *      addr_offset -- the address of phy register

- * OUTPUT:

- *      ptr_data    -- pointer for the register data

- * RETURN:

- *      NPS_E_OK     -- Successfully read the data.

- *      NPS_E_OTHERS -- Failed to read the data.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-aml_readPhyReg(

-    const UI32_T    unit,

-    const UI32_T    port_id,

-    const UI32_T    addr_offset,

-    UI32_T          *ptr_data)

-{

-    AIR_CHECK_PTR(ptr_data);

-

-    if (!_ext_dev_access.phy_read_callback)

-    {

-        return AIR_E_OTHERS;

-    }

-

-    return _ext_dev_access.phy_read_callback(unit, port_id, addr_offset, ptr_data);

-}

-

-/* FUNCTION NAME:   aml_writePhyReg

- * PURPOSE:

- *      To write data to the phy register of the specified chip unit in Clause22.

- * INPUT:

- *      unit        -- the device unit

- *      port_id     -- physical port number

- *      addr_offset -- the address of phy register

- *      data        -- written data

- * OUTPUT:

- *      none

- * RETURN:

- *      NPS_E_OK     -- Successfully write the data.

- *      NPS_E_OTHERS -- Failed to write the data.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-aml_writePhyReg(

-    const UI32_T    unit,

-    const UI32_T    port_id,

-    const UI32_T    addr_offset,

-    const UI32_T    data)

-{

-    if (!_ext_dev_access.phy_write_callback)

-    {

-        return AIR_E_OTHERS;

-    }

-

-    return _ext_dev_access.phy_write_callback(unit, port_id, addr_offset, data);

-}

-

-/* FUNCTION NAME:   aml_readPhyRegCL45

- * PURPOSE:

- *      To read data from the phy register of the specified chip unit in Clause45.

- * INPUT:

- *      unit        -- the device unit

- *      port_id     -- physical port number

- *      dev_type    -- phy register type

- *      addr_offset -- the address of phy register

- * OUTPUT:

- *      ptr_data    -- pointer for the register data

- * RETURN:

- *      NPS_E_OK     -- Successfully read the data.

- *      NPS_E_OTHERS -- Failed to read the data.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-aml_readPhyRegCL45(

-    const UI32_T    unit,

-    const UI32_T    port_id,

-    const UI32_T    dev_type,

-    const UI32_T    addr_offset,

-    UI32_T          *ptr_data)

-{

-    AIR_CHECK_PTR(ptr_data);

-

-    if (!_ext_dev_access.phy_cl45_read_callback)

-    {

-        return AIR_E_OTHERS;

-    }

-

-    return _ext_dev_access.phy_cl45_read_callback(unit, port_id, dev_type, addr_offset, ptr_data);

-}

-

-/* FUNCTION NAME:   aml_writePhyRegCL45

- * PURPOSE:

- *      To write data to the phy register of the specified chip unit in Clause45.

- * INPUT:

- *      unit        -- the device unit

- *      port_id     -- physical port number

- *      dev_type    -- phy register offset

- *      addr_offset -- the address of phy register

- *      data        -- written data

- * OUTPUT:

- *      none

- * RETURN:

- *      NPS_E_OK     -- Successfully write the data.

- *      NPS_E_OTHERS -- Failed to write the data.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-aml_writePhyRegCL45(

-    const UI32_T    unit,

-    const UI32_T    port_id,

-    const UI32_T    dev_type,

-    const UI32_T    addr_offset,

-    const UI32_T    data)

-{

-    if (!_ext_dev_access.phy_cl45_write_callback)

-    {

-        return AIR_E_OTHERS;

-    }

-

-    return _ext_dev_access.phy_cl45_write_callback(unit, port_id, dev_type, addr_offset, data);

-}

-

+/* FILE NAME:  air_aml.c
+ * PURPOSE:
+ *      It provides access management layer function.
+ * NOTES:
+ *
+ */
+
+/* INCLUDE FILE DECLARATIONS
+ */
+#include "air.h"
+
+/* NAMING CONSTANT DECLARATIONS
+ */
+
+/* MACRO FUNCTION DECLARATIONS
+ */
+
+/* DATA TYPE DECLARATIONS
+ */
+
+/* GLOBAL VARIABLE DECLARATIONS
+ */
+AML_DEV_ACCESS_T _ext_dev_access;
+
+/* EXPORTED SUBPROGRAM BODIES
+ */
+
+/* LOCAL SUBPROGRAM BODIES
+ */
+/* FUNCTION NAME:   aml_readReg
+ * PURPOSE:
+ *      To read data from the register of the specified chip unit.
+ * INPUT:
+ *      unit        -- the device unit
+ *      addr_offset -- the address of register
+ * OUTPUT:
+ *      ptr_data    -- pointer for the register data
+ * RETURN:
+ *      NPS_E_OK     -- Successfully read the data.
+ *      NPS_E_OTHERS -- Failed to read the data.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+aml_readReg(
+    const UI32_T    unit,
+    const UI32_T    addr_offset,
+    UI32_T          *ptr_data)
+{
+    AIR_CHECK_PTR(ptr_data);
+
+    if (!_ext_dev_access.read_callback)
+    {
+        return AIR_E_OTHERS;
+    }
+
+    return _ext_dev_access.read_callback(unit, addr_offset, ptr_data);
+}
+
+/* FUNCTION NAME:   aml_writeReg
+ * PURPOSE:
+ *      To write data to the register of the specified chip unit.
+ * INPUT:
+ *      unit        -- the device unit
+ *      addr_offset -- the address of register
+ *      data        -- written data
+ * OUTPUT:
+ *      none
+ * RETURN:
+ *      NPS_E_OK     -- Successfully write the data.
+ *      NPS_E_OTHERS -- Failed to write the data.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+aml_writeReg(
+    const UI32_T    unit,
+    const UI32_T    addr_offset,
+    const UI32_T    data)
+{
+    if (!_ext_dev_access.write_callback)
+    {
+        return AIR_E_OTHERS;
+    }
+
+    return _ext_dev_access.write_callback(unit, addr_offset, data);
+}
+
+/* FUNCTION NAME:   aml_readPhyReg
+ * PURPOSE:
+ *      To read data from the phy register of the specified chip unit in Clause22.
+ * INPUT:
+ *      unit        -- the device unit
+ *      port_id     -- physical port number
+ *      addr_offset -- the address of phy register
+ * OUTPUT:
+ *      ptr_data    -- pointer for the register data
+ * RETURN:
+ *      NPS_E_OK     -- Successfully read the data.
+ *      NPS_E_OTHERS -- Failed to read the data.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+aml_readPhyReg(
+    const UI32_T    unit,
+    const UI32_T    port_id,
+    const UI32_T    addr_offset,
+    UI32_T          *ptr_data)
+{
+    AIR_CHECK_PTR(ptr_data);
+
+    if (!_ext_dev_access.phy_read_callback)
+    {
+        return AIR_E_OTHERS;
+    }
+
+    return _ext_dev_access.phy_read_callback(unit, port_id, addr_offset, ptr_data);
+}
+
+/* FUNCTION NAME:   aml_writePhyReg
+ * PURPOSE:
+ *      To write data to the phy register of the specified chip unit in Clause22.
+ * INPUT:
+ *      unit        -- the device unit
+ *      port_id     -- physical port number
+ *      addr_offset -- the address of phy register
+ *      data        -- written data
+ * OUTPUT:
+ *      none
+ * RETURN:
+ *      NPS_E_OK     -- Successfully write the data.
+ *      NPS_E_OTHERS -- Failed to write the data.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+aml_writePhyReg(
+    const UI32_T    unit,
+    const UI32_T    port_id,
+    const UI32_T    addr_offset,
+    const UI32_T    data)
+{
+    if (!_ext_dev_access.phy_write_callback)
+    {
+        return AIR_E_OTHERS;
+    }
+
+    return _ext_dev_access.phy_write_callback(unit, port_id, addr_offset, data);
+}
+
+/* FUNCTION NAME:   aml_readPhyRegCL45
+ * PURPOSE:
+ *      To read data from the phy register of the specified chip unit in Clause45.
+ * INPUT:
+ *      unit        -- the device unit
+ *      port_id     -- physical port number
+ *      dev_type    -- phy register type
+ *      addr_offset -- the address of phy register
+ * OUTPUT:
+ *      ptr_data    -- pointer for the register data
+ * RETURN:
+ *      NPS_E_OK     -- Successfully read the data.
+ *      NPS_E_OTHERS -- Failed to read the data.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+aml_readPhyRegCL45(
+    const UI32_T    unit,
+    const UI32_T    port_id,
+    const UI32_T    dev_type,
+    const UI32_T    addr_offset,
+    UI32_T          *ptr_data)
+{
+    AIR_CHECK_PTR(ptr_data);
+
+    if (!_ext_dev_access.phy_cl45_read_callback)
+    {
+        return AIR_E_OTHERS;
+    }
+
+    return _ext_dev_access.phy_cl45_read_callback(unit, port_id, dev_type, addr_offset, ptr_data);
+}
+
+/* FUNCTION NAME:   aml_writePhyRegCL45
+ * PURPOSE:
+ *      To write data to the phy register of the specified chip unit in Clause45.
+ * INPUT:
+ *      unit        -- the device unit
+ *      port_id     -- physical port number
+ *      dev_type    -- phy register offset
+ *      addr_offset -- the address of phy register
+ *      data        -- written data
+ * OUTPUT:
+ *      none
+ * RETURN:
+ *      NPS_E_OK     -- Successfully write the data.
+ *      NPS_E_OTHERS -- Failed to write the data.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+aml_writePhyRegCL45(
+    const UI32_T    unit,
+    const UI32_T    port_id,
+    const UI32_T    dev_type,
+    const UI32_T    addr_offset,
+    const UI32_T    data)
+{
+    if (!_ext_dev_access.phy_cl45_write_callback)
+    {
+        return AIR_E_OTHERS;
+    }
+
+    return _ext_dev_access.phy_cl45_write_callback(unit, port_id, dev_type, addr_offset, data);
+}
+
diff --git a/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_cmd.c b/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_cmd.c
index 95c11e8..6dfe416 100644
--- a/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_cmd.c
+++ b/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_cmd.c
@@ -1,8022 +1,8046 @@
-/* FILE NAME:   air_cmd.c

- * PURPOSE:

- *      Define the command line function in AIR SDK.

- * NOTES:

- */

-

-/* INCLUDE FILE DECLARATIONS

-*/

-#include "air.h"

-

-/* NAMING CONSTANT DECLARATIONS

-*/

-

-/* MACRO FUNCTION DECLARATIONS

-*/

-#define MAC_STR         "%02X%02X%02X%02X%02X%02X"

-#define MAC2STR(m)      (m)[0],(m)[1],(m)[2],(m)[3],(m)[4],(m)[5]

-#define AIR_MAC_LEN    (12)

-#define CMD_NO_PARA     (0xFFFFFFFF)

-#define CMD_VARIABLE_PARA (0xFFFFFFFE)

-#define L2_WDOG_KICK_NUM            (100)

-

-#define TOLOWER(x)      ((x) | 0x20)

-#define isxdigit(c)     (('0' <= (c) && (c) <= '9') || ('a' <= (c) && (c) <= 'f') || ('A' <= (c) && (c) <= 'F'))

-#define isdigit(c)      ('0' <= (c) && (c) <= '9')

-#define CMD_CHECK_PARA(__shift__, __op__, __size__) do          \

-{                                                               \

-    if ((__shift__) __op__ (__size__))                          \

-    {                                                           \

-        ;                                                       \

-    }                                                           \

-    else                                                        \

-    {                                                           \

-        return (AIR_E_BAD_PARAMETER);                           \

-    }                                                           \

-} while(0)

-

-/* DATA TYPE DECLARATIONS

-*/

-typedef struct {

-    C8_T*               name;

-    AIR_ERROR_NO_T     (*func)(UI32_T argc, C8_T *argv[]);

-    UI32_T              argc_min;

-    C8_T*               argc_errmsg;

-} AIR_CMD_T;

-

-/* GLOBAL VARIABLE DECLARATIONS

-*/

-

-/* LOCAL SUBPROGRAM DECLARATIONS

-*/

-/* String Utility */

-static BOOL_T _strcmp(const char *s1, const char *s2);

-static C8_T * _strtok_r(C8_T *s, const C8_T *delim, C8_T **last);

-static C8_T * _strtok(C8_T *s, const C8_T *delim, C8_T **last);

-UI32_T _strtoul(const C8_T *cp, C8_T **endp, UI32_T base);

-static I32_T _strtol(const C8_T *cp, C8_T **endp, UI32_T base);

-

-/* Type Converter */

-static AIR_ERROR_NO_T _str2mac(C8_T *str, C8_T *mac);

-static AIR_ERROR_NO_T _hex2bit(const UI32_T hex, UI32_T *ptr_bit);

-static AIR_ERROR_NO_T _hex2bitstr(const UI32_T hex, C8_T *ptr_bit_str, UI32_T str_len);

-static AIR_ERROR_NO_T _portListStr2Ary(const C8_T *str, UI32_T *ary, const UI32_T ary_num);

-

-/* Register Operation */

-static AIR_ERROR_NO_T doRegRead(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doRegWrite(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doReg(UI32_T argc, C8_T *argv[]);

-

-/* PHY Operation */

-static AIR_ERROR_NO_T doPhyCL22Read(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPhyCL22Write(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPhyCL22(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPhyCL45Read(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPhyCL45Write(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPhyCL45(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPhy(UI32_T argc, C8_T *argv[]);

-

-/* Porting setting */

-static AIR_ERROR_NO_T doPortSetMatrix(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPortSetVlanMode(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPortSet(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T doPortGetMatrix(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPortGetVlanMode(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPortGet(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T doPort(UI32_T argc, C8_T *argv[]);

-

-/* Vlan setting */

-static AIR_ERROR_NO_T doVlanInitiate(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanCreate(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanDestroy(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanDestroyAll(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanDump(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanAddPortMem(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanDelPortMem(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T doVlanSetFid(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanSetMemPort(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanSetIVL(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanSetPortBaseStag(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanSetStag(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanSetEgsTagCtlEn(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanSetEgsTagCtlCon(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanSetEgsTagCtl(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanSetPortActFrame(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanSetLeakyVlanEn(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanSetPortVlanAttr(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanSetIgsPortETagAttr(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanSetPortETagAttr(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanSetPortOuterTPID(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanSetPvid(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanSet(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T doVlanGetPortActFrame(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanGetLeakyVlanEn(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanGetPortVlanAttr(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanGetIgsPortETagAttr(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanGetPortETagAttr(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanGetPortOuterTPID(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanGetPvid(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doVlanGet(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T doVlan(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T doFlowCtrl(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doJumbo(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T doL2Add(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doL2Del(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doL2Clear(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doL2Get(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doL2Set(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doL2Dump(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doL2(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T doAnMode(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doLocalAdv(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doRemoteAdv(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPortSpeed(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPortDuplex(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPortStatus(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPortBckPres(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPortPsMode(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPortSmtSpdDwn(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPortSpTag(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPortEnable(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPort5GBaseRMode(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPortHsgmiiMode(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPortSgmiiMode(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPortRmiiMode(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doPortRgmiiMode(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T doSptagEn(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doSptagMode(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doSptagDecode(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doSptagEncode(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doSptag(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T doMacAddr(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T _printMacEntry(AIR_MAC_ENTRY_T * mt, UI32_T age_unit, UI8_T count, UI8_T title);

-static AIR_ERROR_NO_T doGetMacAddr(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doMacAddrAgeOut(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doDumpMacAddr(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T doLagMember(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doLagMemberCnt(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doLagPtseed(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doLagHashtype(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doLagDstInfo(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doLagState(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doLagSpsel(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doLagGet(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doLagSet(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doLag(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T doStpPortstate(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doStpGet(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doStpSet(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doStp(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T doMirrorGetSid(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doMirrorDelSid(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doMirrorAddRlist(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doMirrorAddTlist(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doMirrorSetSessionEnable(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doMirrorSetSession(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doMirrorSet(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doMirrorAdd(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doMirrorGet(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doMirrorDel(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doMirror(UI32_T argc,C8_T *argv[]);

-

-static AIR_ERROR_NO_T doMibClearPort(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doMibClearAcl(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doMibGetPort(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doMibGetAcl(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doMibClear(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doMibGet(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doMib(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T doQosScheduleAlgo(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doQosTrustMode(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doQosPri2Queue(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doQosDscp2Pri(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doQosRateLimitEnable(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doQosRateLimit(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doQosPortPriority(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doQosRateLimitExMngFrm(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doQosGet(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doQosSet(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doQos(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T doDiagTxComply(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doDiagSet(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doDiagGet(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doDiag(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T doLedMode(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doLedState(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doLedUsrDef(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doLedBlkTime(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doLedSet(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doLedGet(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doLed(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T doSwitchCpuPortEn(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doSwitchCpuPort(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doSwitchPhyLCIntrEn(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doSwitchPhyLCIntrSts(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doSwitchSet(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doSwitchGet(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doSwitch(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T doShowVersion(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doShow(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T doStormEnable(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doStormRate(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doFldMode(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doSaLearning(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doSaLimit(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doSecGet(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doSecSet(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doSec(UI32_T argc, C8_T *argv[]);

-

-static void _air_acl_printRuleMap(UI32_T *rule_map, UI32_T ary_num);

-static AIR_ERROR_NO_T doAclEn(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclRule(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclUdfRule(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclRmvRule(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclRmvUdfRule(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclAction(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclRmvAction(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclDumpAction(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclTrtcm(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclTrtcmEn(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclRmvTrtcm(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclPortEn(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclDropEn(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclDropThrsh(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclDropPbb(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclMeter(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclDump(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclSet(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclGet(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclDel(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAclClear(UI32_T argc, C8_T *argv[]);

-static AIR_ERROR_NO_T doAcl(UI32_T argc, C8_T *argv[]);

-

-static AIR_ERROR_NO_T subcmd(const AIR_CMD_T tab[], UI32_T argc, C8_T *argv[]);

-

-/* STATIC VARIABLE DECLARATIONS

-*/

-const static C8_T *_sptag_vpm[] =

-{

-    "untagged",

-    "8100",

-    "predefined",

-    "unknown"

-};

-

-const static C8_T *_sptag_pt[] =

-{

-    "disable pass through",

-    "enable pass through"

-};

-

-const static C8_T *_air_mac_address_forward_control_string [] =

-{

-    "Default",

-    "CPU include",

-    "CPU exclude",

-    "CPU only",

-    "Drop"

-};

-

-static AIR_CMD_T regCmds[] =

-{

-    {"r",           doRegRead,      1,      "reg r <reg(4'hex)>"},

-    {"w",           doRegWrite,     2,      "reg w <reg(4'hex)> <value(8'hex)>"},

-

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T phyCL22Cmds[] =

-{

-    {"r",           doPhyCL22Read,  2,      "phy cl22 r <port(0..4)> <reg(2'hex)>"},

-    {"w",           doPhyCL22Write, 3,      "phy cl22 w <port(0..4)> <reg(2'hex)> <value(4'hex)>"},

-

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T phyCL45Cmds[] =

-{

-    {"r",           doPhyCL45Read,  3,      "phy cl45 r <port(0..4)> <dev(2'hex)> <reg(3'hex)>"},

-    {"w",           doPhyCL45Write, 4,      "phy cl45 w <port(0..4)> <dev(2'hex)> <reg(3'hex)> <value(4'hex)>"},

-

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T phyCmds[] =

-{

-    {"cl22",         doPhyCL22,     0,      NULL},

-    {"cl45",         doPhyCL45,     0,      NULL},

-

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T portSetCmds[] =

-{

-    {"matrix",      doPortSetMatrix,        2,                "port set matrix <port(0..6)> <matrix(6:0)>"},

-    {"vlanMode",    doPortSetVlanMode,      2,                "port set vlanMode <port(0..6)> <vlanMode(0:matrix,1:fallback,2:check,3:security)>"},

-    {"flowCtrl",    doFlowCtrl,             3,                "port set flowCtrl <port(0..6)> <dir(0:Tx,1:Rx)> <fc_en(1:En,0:Dis)>"},

-    {"jumbo",       doJumbo,                2,                "port set jumbo <pkt_len(0:1518,1:1536,2:1552,3:max)> <frame_len(2..15)>"},

-    {"anMode",      doAnMode,               2,                "port set anMode <port(0..4)> <en(0:force,1:AN)>"},

-    {"localAdv",    doLocalAdv,             7,                "port set localAdv <port(0..4)> <10H(1:En,0:Dis)> <10F(1:En,0:Dis)> <100H(1:En,0:Dis)> <100F(1:En,0:Dis)> <1000F(1:En,0:Dis)> <pause(1:En,0:Dis)>"},

-    {"speed",       doPortSpeed,            2,                "port set speed <port(0..4)> <speed(0:10M,1:100M,2:1G,3:2.5G)>"},

-    {"duplex",      doPortDuplex,           2,                "port set duplex <port(0..4)> <duplex(0:half,1:full)>"},

-    {"bckPres",     doPortBckPres,          2,                "port set bckPres <port(0..6)> <bckPres(1:En,0:Dis)>"},

-    {"psMode",      doPortPsMode,           3,                "port set psMode <port(0..4)> <ls(1:En,0:Dis)> <eee(1:En,0:Dis)>"},

-    {"smtSpdDwn",   doPortSmtSpdDwn,        3,                "port set smtSpdDwn <port(0..4)> <en(1:En,0:Dis)> <retry(2..5)>"},

-    {"spTag",       doPortSpTag,            2,                "port set spTag <port(0..6)> <en(1:En,0:Dis)>"},

-    {"enable",      doPortEnable,           2,                "port set enable <port(0..4)> <en(1:En,0:Dis)>"},

-    {"5GBaseRMode", doPort5GBaseRMode,      CMD_NO_PARA,      "port set 5GBaseRMode"},

-    {"hsgmiiMode",  doPortHsgmiiMode,       CMD_NO_PARA,      "port set hsgmiiMode"},

-    {"sgmiiMode",   doPortSgmiiMode,        2,                "port set sgmiiMode <mode(0:AN,1:Force)> <speed(0:10M,1:100M,2:1G)>"},

-    {"rmiiMode",    doPortRmiiMode,         1,                "port set rmiiMode <speed(0:10M,1:100M)>"},

-    {"rgmiiMode",   doPortRgmiiMode,        1,                "port set rgmiiMode <speed(0:10M,1:100M,2:1G)>"},

-

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T portGetCmds[] =

-{

-    {"matrix",      doPortGetMatrix,        1,                "port get matrix <port(0..6)>"},

-    {"vlanMode",    doPortGetVlanMode,      1,                "port get vlanMode <port(0..6)>"},

-    {"flowCtrl",    doFlowCtrl,             2,                "port get flowCtrl <port(0..6)> <dir(0:Tx,1:Rx)>"},

-    {"jumbo",       doJumbo,                CMD_NO_PARA,      "port get jumbo"},

-    {"anMode",      doAnMode,               1,                "port get anMode <port(0..4)>"},

-    {"localAdv",    doLocalAdv,             1,                "port get localAdv <port(0..4)>"},

-    {"remoteAdv",   doRemoteAdv,            1,                "port get remoteAdv <port(0..4)>"},

-    {"speed",       doPortSpeed,            1,                "port get speed <port(0..4)>"},

-    {"duplex",      doPortDuplex,           1,                "port get duplex <port(0..4)>"},

-    {"status",      doPortStatus,           1,                "port get status <port(0..4)>"},

-    {"bckPres",     doPortBckPres,          1,                "port get bckPres <port(0..6)>"},

-    {"psMode",      doPortPsMode,           1,                "port get psMode <port(0..4)>"},

-    {"smtSpdDwn",   doPortSmtSpdDwn,        1,                "port get smtSpdDwn <port(0..4)>"},

-    {"spTag",       doPortSpTag,            1,                "port get spTag <port(0..6)>"},

-    {"enable",      doPortEnable,           1,                "port get enable <port(0..4)>"},

-

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T portCmds[] =

-{

-    {"set",         doPortSet,      0,      NULL},

-    {"get",         doPortGet,      0,      NULL},

-

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T sptagCmds[] =

-{

-    {"setEnable",       doSptagEn,          2,      "sptag setEnable port<port(0..6)> enable<1:enable 0:disable>"},

-    {"getEnable",       doSptagEn,          1,      "sptag getEnable port<port(0..6)>"},

-    {"setmode",         doSptagMode,        2,      "sptag setmode port<port(0..6)> mode<0:inset 1:replace>"},

-    {"getmode",         doSptagMode,        1,      "sptag getmode port<port(0..6)>"},

-    {"encode",          doSptagEncode,      7,      "sptag encode mode={ insert | replace } opc={ portmap | portid | lookup } dp={bitimap hex} vpm={ untagged | 8100 | 88a8 } pri=<UINT> cfi=<UINT> vid=<UINT> "},

-    {"decode",          doSptagDecode,      4,      "sptag decode <byte(hex)> <byte(hex)> <byte(hex)> <byte(hex)>"},

-

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T vlanSetCmds[] =

-{

-    {"fid",                 doVlanSetFid,               2,      "vlan set fid <vid(0..4095)> <fid(0..7)>"},

-    {"memPort",             doVlanSetMemPort,           2,      "vlan set memPort <vid(0..4095)> <bitmap(6:0)>"},

-    {"ivl",                 doVlanSetIVL,               2,      "vlan set ivl <vid(0..4095)> <(1:En,0:Dis)>"},

-    {"portBaseStag",        doVlanSetPortBaseStag,      2,      "vlan set portBaseStag <vid(0..4095)> <(1:En,0:Dis)>"},

-    {"stag",                doVlanSetStag,              2,      "vlan set stag <vid(0..4095)> <stag(0..4095)>"},

-    {"egsTagCtlEn",         doVlanSetEgsTagCtlEn,       2,      "vlan set egsTagCtlEn <vid(0..4095)> <(1:En,0:Dis)>"},

-    {"egsTagCtlCon",        doVlanSetEgsTagCtlCon,      2,      "vlan set egsTagCtlCon <vid(0..4095)> <(1:En,0:Dis)>"},

-    {"egsTagCtl",           doVlanSetEgsTagCtl,         3,      "vlan set egsTagCtl <vid(0..4095)> <port(0..6)> <ctlType(0:untag,2:tagged)>"},

-

-    {"portActFrame",        doVlanSetPortActFrame,      2,      "vlan set portActFrame <port(0..6)> <frameType(0:all,1:tagged,2:untagged)>"},

-    {"leakyVlanEn",         doVlanSetLeakyVlanEn,       3,      "vlan set LeakyVlanEn <port(0..6)> <pktType(0:uc,1:mc,2:bc,3:ipmc)> <(1:En,0:Dis)>"},

-    {"portVlanAttr",        doVlanSetPortVlanAttr,      2,      "vlan set portVlanAttr <port(0..6)> <vlanAttr(0:user,1:stack,2:translation,3:transparent)>"},

-    {"igsPortETagAttr",     doVlanSetIgsPortETagAttr,   2,      "vlan set igsPortETagAttr <port(0..6)> <egsTagAttr(0:disable,1:consistent,4:untagged,5:swap,6:tagged,7:stack)>"},

-    {"portEgsTagAttr",      doVlanSetPortETagAttr,      2,      "vlan set portEgsTagAttr <port(0..6)> <egsTagAttr(0:untagged,1:swap,2:tagged,3:stack)>"},

-    {"portOuterTPID",       doVlanSetPortOuterTPID,     2,      "vlan set portOuterTPID <port(0..6)> <TPID(hex)>"},

-    {"pvid",                doVlanSetPvid,              2,      "vlan set pvid <port(0..6)> <vid(0..4095)>"},

-

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T vlanGetCmds[] =

-{

-    {"portActFrame",        doVlanGetPortActFrame,      1,      "vlan get portActFrame <port(0..6)>"},

-    {"leakyVlanEn",         doVlanGetLeakyVlanEn,       1,      "vlan get leakyVlanEn <port(0..6)>"},

-    {"portVlanAttr",        doVlanGetPortVlanAttr,      1,      "vlan get portVlanAttr <port(0..6)>"},

-    {"igsPortETagAttr",     doVlanGetIgsPortETagAttr,   1,      "vlan get igsPortETagAttr <port(0..6)>"},

-    {"portEgsTagAttr",      doVlanGetPortETagAttr,      1,      "vlan get portEgsTagAttr <port(0..6)>"},

-    {"portOuterTPID",       doVlanGetPortOuterTPID,     1,      "vlan get portOuterTPID <port(0..6)>"},

-    {"pvid",                doVlanGetPvid,              1,      "vlan get pvid <port(0..6)>"},

-

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T vlanCmds[] =

-{

-    {"initiate",        doVlanInitiate,         9,      "vlan initiate <vid(0..4095)> <fid(0..7)> <bitmap(6:0)> <ivl(1:En,0:Dis)> <portbasestag(1:En,0:Dis)> <stag(0..4095)> <egstagctlen(1:En,0:Dis)> <egstagcon(1:En,0:Dis)> <taggedbitmap(6:0)>"},

-    {"create",          doVlanCreate,           1,      "vlan create <vid(0..4095)>"},

-    {"destroy",         doVlanDestroy,          1,      "vlan destroy [ <vid(0..4095)> | <vidRange(vid0-vid1)> ]"},

-    {"destroyAll",      doVlanDestroyAll,       0,      "vlan destroyAll [ <restoreDefVlan(0:false,1:true)> | ]"},

-    {"dump",            doVlanDump,             0,      "vlan dump [ <vid(0..4095)> | <vidRange(vid0-vid1)> | ]"},

-    {"addPortMem",      doVlanAddPortMem,       2,      "vlan addPortMem <vid(0..4095)> <port(0..6)>"},

-    {"delPortMem",      doVlanDelPortMem,       2,      "vlan addPortMem <vid(0..4095)> <port(0..6)>"},

-    {"set",             doVlanSet,              0,      NULL},

-    {"get",             doVlanGet,              0,      NULL},

-

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T l2ClearCmds[] =

-{

-    {"mac",              doMacAddr,          CMD_NO_PARA,    "l2 clear mac"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T l2DelCmds[] =

-{

-    {"mac",             doMacAddr,           3,    "l2 del mac <mac(12'hex)> [ vid <vid(0..4095)> | fid <fid(0..15)> ]"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T l2AddCmds[] =

-{

-    {"mac",             doMacAddr,           7,    "l2 add mac <static(0:dynamic,1:static)> <unauth(0:auth,1:unauth)> <mac(12'hex)> <portlist(uintlist)> [ vid <vid(0..4095)> | fid <fid(0..15)> ] <src_mac_forward=(0:default,1:cpu-exclude,2:cpu-include,3:cpu-only,4:drop)>"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T l2SetCmds[] =

-{

-    {"macAddrAgeOut",   doMacAddrAgeOut,    1,    "l2 set macAddrAgeOut <time(1, 1000000)>"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T l2GetCmds[] =

-{

-    {"mac",             doGetMacAddr,       3,              "l2 get mac <mac(12'hex)> [ vid <vid(0..4095)> | fid <fid(0..15)> ]"},

-    {"macAddrAgeOut",   doMacAddrAgeOut,    CMD_NO_PARA,    "l2 get macAddrAgeOut"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T l2DumpCmds[] =

-{

-    {"mac",                doDumpMacAddr,        0,    "l2 dump mac"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T l2Cmds[] =

-{

-    {"add",         doL2Add,        0,        NULL},

-    {"del",         doL2Del,        0,        NULL},

-    {"clear",       doL2Clear,      0,        NULL},

-    {"get",         doL2Get,        0,        NULL},

-    {"set",         doL2Set,        0,        NULL},

-    {"dump",        doL2Dump,       0,        NULL},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T lagGetCmds[] =

-{

-    {"member",      doLagMember,    1,              "lag get member group_id(0 or 1)"},

-    {"dstInfo",     doLagDstInfo,   CMD_NO_PARA,    "lag get dstInfo"},

-    {"ptseed",      doLagPtseed,    CMD_NO_PARA,    "lag get ptseed"},

-    {"hashtype",    doLagHashtype,  CMD_NO_PARA,    "lag get hashtype"},

-    {"state",       doLagState,     CMD_NO_PARA,    "lag get state"},

-    {"spsel",       doLagSpsel,     CMD_NO_PARA,    "lag get spsel"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T lagSetCmds[] =

-{

-    {"member",       doLagMember,        4,    "lag set member <group_id(0 or 1)> <member_index(0..3)> <enable(0,1)> <port index(0..6)>"},

-    {"dstInfo",      doLagDstInfo,       7,    "lag set dstInfo <sp(1:En,0:Dis)> <sa(1:En,0:Dis)> <da(1:En,0:Dis)> <sip(1:En,0:Dis)> <dip(1:En,0:Dis)> <sport(1:En,0:Dis)> <dport(1:En,0:Dis)>"},

-    {"ptseed",       doLagPtseed,        1,    "lag set ptseed <hex32>"},

-    {"hashtype",     doLagHashtype,      1,    "lag set hashtype <0-crc32lsb;1-crc32msb;2-crc16;3-xor4>"},

-    {"state",        doLagState,         1,    "lag set state <state(1:En,0:Dis)>"},

-    {"spsel",        doLagSpsel,         1,    "lag set spsel <soure port enable(1:En,0:Dis)>"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T lagCmds[] =

-{

-    {"get",          doLagGet,        0,        NULL},

-    {"set",          doLagSet,        0,        NULL},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T stpGetCmds[] =

-{

-    {"portstate",    doStpPortstate,  2,    "stp get portstate <port(0..6)> <fid(0..15)>"},

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T stpSetCmds[] =

-{

-    {"portstate",    doStpPortstate,  3,    "stp set portstate <port(0..6)> <fid(0..15)> <state(0:disable,1:listen,2:learn,3:forward)>"},

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T stpCmds[] =

-{

-    {"get",         doStpGet,           0,      NULL},

-    {"set",         doStpSet,           0,      NULL},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T mirrorSetCmds[] =

-{

-    {"session",        doMirrorSetSession,       6,      "mirror set session <sid(0,1)> <dst_port(UINT)> <state(1:En,0:Dis)> <tag(1:on, 0:off)> <list(UINTLIST)> <dir(0:none,1:tx,2:rx,3:both)>"},

-    {"session-enable", doMirrorSetSessionEnable, 2,      "mirror set session-enable <sid(0,1)> <state(1:En,0:Dis)>"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T mirrorAddCmds[] =

-{

-    {"session-rlist",  doMirrorAddRlist,       2,      "mirror add session-rlist <sid(0,1)> <list(UINTLIST)>"},

-    {"session-tlist",  doMirrorAddTlist,       2,      "mirror add session-tlist <sid(0,1)> <list(UINTLIST)>"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T mirrorGetCmds[] =

-{

-    {"session",        doMirrorGetSid,       1,      "mirror get session <sid(0,1)>"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T mirrorDelCmds[] =

-{

-    {"session",        doMirrorDelSid,       1,      "mirror del session <sid(0,1)>"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T mirrorCmds[] =

-{

-    {"set",         doMirrorSet,        0,      NULL},

-    {"add",         doMirrorAdd,        0,      NULL},

-    {"get",         doMirrorGet,        0,      NULL},

-    {"del",         doMirrorDel,        0,      NULL},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T mibClearCmds[] =

-{

-    {"port",        doMibClearPort,     1,      "mib clear port <port(0..6)>"},

-    {"all",         doMibClearPort,     0,      "mib clear all"},

-    {"acl",         doMibClearAcl,      0,      "mib clear acl"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T mibGetCmds[] =

-{

-    {"port",        doMibGetPort,       1,      "mib get port <port(0..6)>"},

-    {"acl",         doMibGetAcl,        1,      "mib get acl <event(0..7)>"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T mibCmds[] =

-{

-    {"clear",       doMibClear,         0,      NULL},

-    {"get",         doMibGet,           0,      NULL},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T qosGetCmds[] =

-{

-    {"scheduleAlgo",    doQosScheduleAlgo,      2,  "qos get scheduleAlgo <portlist(UINTLIST)> <queue(UINT)>"},

-    {"trustMode",       doQosTrustMode,         1,  "qos get trustMode <portlist(UINTLIST)>"},

-    {"pri2Queue",       doQosPri2Queue,         0,  "qos get pri2Queue"},

-    {"dscp2Pri",        doQosDscp2Pri,          1,  "qos get dscp2Pri <dscp(0..63)>"},

-    {"rateLimitEnable", doQosRateLimitEnable,   1,  "qos get rateLimitEnable <portlist(UINTLIST)>"},

-    {"rateLimit",       doQosRateLimit,         1,  "qos get rateLimit <portlist(UINTLIST)>"},

-    {"portPriority",    doQosPortPriority,      1,  "qos get portPriority <portlist(UINTLIST)>"},

-    {"rateLmtExMngFrm", doQosRateLimitExMngFrm, 0,  "qos get rateLmtExMngFrm"},

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T qosSetCmds[] =

-{

-    {"scheduleAlgo",    doQosScheduleAlgo,      4,  "qos set scheduleAlgo <portlist(UINTLIST)> <queue(UINT)> <scheduler(0:SP,1:WRR,2:WFQ)> <weight(0..128)>, weight 0 is valid only on sp mode"},

-    {"trustMode",       doQosTrustMode,         2,  "qos set trustMode <portlist(UINTLIST)> <mode(0:port,1:1p-port,2:dscp-port,3:dscp-1p-port>"},

-    {"pri2Queue",       doQosPri2Queue,         2,  "qos set pri2Queue <priority(0..7)> <queue(0..7)>"},

-    {"dscp2Pri",        doQosDscp2Pri,          2,  "qos set dscp2Pri <dscp(0..63)> <priority(0..7)>"},

-    {"rateLimitEnable", doQosRateLimitEnable,   3,  "qos set rateLimitEnable <portlist(UINTLIST)> <dir(0:egress,1:ingress)> <rate_en(1:En,0:Dis)>"},

-    {"rateLimit",       doQosRateLimit,         5,  "qos set rateLimit <portlist(UINTLIST)> <I_CIR(0..80000)> <I_CBS(0..127)> <E_CIR(0..80000)> <E_CBS(0..127)>"},

-    {"portPriority",    doQosPortPriority,      2,  "qos set portPriority <portlist(UINTLIST)> <priority(0..7)>"},

-    {"rateLmtExMngFrm", doQosRateLimitExMngFrm, 2,  "qos set rateLmtExMngFrm <dir(0:egress)> <en(0:include,1:exclude)>"},

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T qosCmds[] =

-{

-    {"get",          doQosGet,        0,        NULL},

-    {"set",          doQosSet,        0,        NULL},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T diagSetCmds[] =

-{

-    {"txComply",    doDiagTxComply,     2,      "diag set txComply <phy(0..5)> <mode(0..8)>"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T diagGetCmds[] =

-{

-    {"txComply",    doDiagTxComply,     1,      "diag get txComply <phy(0..5)>"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T diagCmds[] =

-{

-    {"set",         doDiagSet,          0,      NULL},

-    {"get",         doDiagGet,          0,      NULL},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T ledSetCmds[] =

-{

-    {"mode",        doLedMode,          1,      "led set mode <mode(0:disable, 1..3:2 LED, 4:user-define)>"},

-    {"state",       doLedState,         2,      "led set state <led(0..1)> <state(1:En,0:Dis)>"},

-    {"usr",         doLedUsrDef,        4,      "led set usr <led(0..1)> <polarity(0:low, 1:high)> <on_evt(7'bin)> <blink_evt(10'bin)>"},

-    {"time",        doLedBlkTime,       1,      "led set time <time(0..5:32ms~1024ms)>"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T ledGetCmds[] =

-{

-    {"mode",        doLedMode,          CMD_NO_PARA,      "led get mode"},

-    {"state",       doLedState,         1,                "led get state <led(0..1)>"},

-    {"usr",         doLedUsrDef,        1,                "led get usr <led(0..1)>"},

-    {"time",        doLedBlkTime,       CMD_NO_PARA,      "led get time"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T ledCmds[] =

-{

-    {"set",         doLedSet,           0,      NULL},

-    {"get",         doLedGet,           0,      NULL},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T showCmds[] =

-{

-    {"version",     doShowVersion,        0,        NULL},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T secGetCmds[] =

-{

-    {"stormEnable",     doStormEnable,   2,  "sec get stormEnable <port(0..6)> <type(0:bcst,1:mcst,2:ucst)>"},

-    {"stormRate",       doStormRate,     2,  "sec get stormRate <port(0..6)> <type(0:bcst,1:mcst,2:ucst)>"},

-    {"fldMode",         doFldMode,       2,  "sec get fldMode <port(0..6)> <type(0:bcst,1:mcst,2:ucst,3:qury>"},

-    {"saLearning",      doSaLearning,    1,  "sec get saLearning <port(0..6)>"},

-    {"saLimit",         doSaLimit,       1,  "sec get saLimit <port(0..6)>"},

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T secSetCmds[] =

-{

-    {"stormEnable",     doStormEnable,   3,  "sec set stormEnable <port(0..6)> <type(0:bcst,1:mcst,2:ucst)> <en(1:En,0:Dis)>"},

-    {"stormRate",       doStormRate,     4,  "sec set stormRate <port(0..6)> <type(0:bcst,1:mcst,2:ucst)> <count(0..255)> <unit(0:64k,1:256k,2:1M,3:4M,4:16M)>"},

-    {"fldMode",         doFldMode,       3,  "sec set fldMode <port(0..6)> <type(0:bcst,1:mcst,2:ucst,3:qury> <en(1:En,0:Dis)>"},

-    {"saLearning",      doSaLearning,    2,  "sec set saLearning <port(0..6)> <learn(0:disable,1:enable)>"},

-    {"saLimit",         doSaLimit,       3,  "sec set saLimit <port(0..6)> <mode(0:disable,1:enable)> <count(0..4095)>"},

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T secCmds[] =

-{

-    {"get",          doSecGet,        0,        NULL},

-    {"set",          doSecSet,        0,        NULL},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T switchSetCmds[] =

-{

-    {"cpuPortEn",   doSwitchCpuPortEn,   1,              "switch set cpuPortEn <cpu_en(1:En,0:Dis)>"},

-    {"cpuPort",     doSwitchCpuPort,     1,              "switch set cpuPort <port_number>"},

-    {"phyLCIntrEn",     doSwitchPhyLCIntrEn,     2,      "switch set phyLCIntrEn <phy(0..6)> <(1:En,0:Dis)>"},

-    {"phyLCIntrSts",    doSwitchPhyLCIntrSts,    2,      "switch set phyLCIntrSts <phy(0..6)> <(1:Clear)>"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T switchGetCmds[] =

-{

-    {"cpuPortEn",   doSwitchCpuPortEn,   CMD_NO_PARA,      "switch get cpuPortEn"},

-    {"cpuPort",     doSwitchCpuPort,     CMD_NO_PARA,      "switch get cpuPort"},

-    {"phyLCIntrEn",     doSwitchPhyLCIntrEn,     1,        "switch get phyLCIntrEn <phy(0..6)>"},

-    {"phyLCIntrSts",    doSwitchPhyLCIntrSts,    1,        "switch get phyLCIntrSts <phy(0..6)>"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T switchCmds[] =

-{

-    {"set",         doSwitchSet,        0,      NULL},

-    {"get",         doSwitchGet,        0,      NULL},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T aclSetCmds[] =

-{

-    {"en",              doAclEn,            1,                     "acl set en <en(1:En,0:Dis)>"},

-    {"rule",            doAclRule,          CMD_VARIABLE_PARA,     "acl set rule <idx(0..127)>\n <state(0:Dis,1:En)> <reverse(0:Dis,1:En)> <end(0:Dis,1:En)>\n <portmap(7'bin)><ipv6(0:Dis,1:En,2:Not care)>\n[ dmac <dmac(12'hex)> <dmac_mask(12'hex)> ]\n[ smac <smac(12'hex)> <smac_mask(12'hex)> ]\n[ stag <stag(4'hex)> <stag_mask(4'hex)> ]\n[ ctag <ctag(4'hex)> <ctag_mask(4'hex)> ]\n[ etype <etype(4'hex)> <etype_mask(4'hex)> ]\n[ dip <dip(IPADDR)> <dip_mask(IPADDR)> ]\n[ sip <sip(IPADDR)> <sip_mask(IPADDR)> ]\n[ dscp <dscp(2'hex)> <dscp_mask(2'hex)> ]\n[ protocol <protocol(12'hex)> <protocol_mask(12'hex)> ]\n[ dport <dport(4'hex)> <dport_mask(4'hex)> ]\n[ sport <sport(4'hex)> <sport_mask(4'hex)> ]\n[ flow_label <flow_label(4'hex)> <flow_label_mask(4'hex)> ]\n[ udf <udf(4'hex)> <udf_mask(4'hex)> ] "},

-    {"udfRule",         doAclUdfRule,       7,                     "acl set udfRule <idx(0..15)> <mode(0:pattern, 1:threshold)> [ <pat(4'hex)> <mask(4'hex)> | <low(4'hex)> <high(4'hex)> ] <start(0:MAC header, 1:L2 payload, 2:IPv4 header, 3:IPv6 header, 4:L3 payload, 5:TCP header, 6:UDP header, 7: L4 payload)> <offset(0..127,unit:2 bytes)> <portmap(7'bin)>"},

-    {"action",          doAclAction,        CMD_VARIABLE_PARA,     "acl set action <idx(0..127)> \n[ forward <forward(0:Default,4:Exclude CPU,5:Include CPU,6:CPU only,7:Drop)> ]\n[ egtag <egtag(0:Default,1:Consistent,4:Untag,5:Swap,6:Tag,7:Stack)> ]\n[ mirrormap <mirrormap(2'bin)> ]\n[ priority <priority(0..7)> ]\n[ redirect <redirect(0:Dst,1:Vlan)> <portmap(7'bin)> ]\n[ leaky_vlan <leaky_vlan(1:En,0:Dis)> ]\n[ cnt_idx <cnt_idx(0..63)> ]\n[ rate_idx <rate_idx(0..31)> ] \n[ attack_idx <attack_idx(0..95)> ] \n[ vid <vid(0..4095)> ] \n[ manage <manage(1:En,0:Dis)> ] \n[ bpdu <bpdu(1:En,0:Dis)> ]\n[ class <class(0:Original,1:Defined)>[0..7] ]\n[ drop_pcd <drop_pcd(0:Original,1:Defined)> [red <red(0..7)>][yellow <yellow(0..7)>][green <green(0..7)>] ]\n[ color <color(0:Defined,1:Trtcm)> [ <defined_color(0:Dis,1:Green,2:Yellow,3:Red)> | <trtcm_idx(0..31)> ] ]"},

-    {"trtcm",           doAclTrtcm,         5,                     "acl set trtcm <idx(1..31)> <cir(4'hex)> <pir(4'hex)> <cbs(4'hex)> <pbs(4'hex)>"},

-    {"trtcmEn",         doAclTrtcmEn,       1,                     "acl set trtcmEn <en(1:En,0:Dis)>"},

-    {"portEn",          doAclPortEn,        2,                     "acl set portEn <port(0..6)> <en(1:En,0:Dis)>"},

-    {"dropEn",          doAclDropEn,        2,                     "acl set dropEn <port(0..6)> <en(1:En,0:Dis)>"},

-    {"dropThrsh",       doAclDropThrsh,     5,                     "acl set dropThrsh <port(0..6)> <color(0:green,1:yellow,2:red)> <queue(0..7)> <high(0..2047)> <low(0..2047)>"},

-    {"dropPbb",         doAclDropPbb,       4,                     "acl set dropPbb <port(0..6)> <color(0:green,1:yellow,2:red)> <queue(0..7)> <probability(0..1023)>"},

-    {"meter",           doAclMeter,         3,                     "acl set meter <idx(0..31)> <en(1:En,0:Dis)> <rate(0..65535)>\n Note: Limit rate = rate * 64Kbps"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T aclGetCmds[] =

-{

-    {"en",              doAclEn,            CMD_NO_PARA,    "acl get en"},

-    {"rule",            doAclRule,          1,              "acl get rule <idx(0..127)> "},

-    {"udfRule",         doAclUdfRule,       1,              "acl get udfRule <idx(0..15)>"},

-    {"action",          doAclAction,        1,              "acl get action <idx(0..127)>"},

-    {"trtcm",           doAclTrtcm,         1,              "acl get trtcm <idx(1..31)>"},

-    {"trtcmEn",         doAclTrtcmEn,       CMD_NO_PARA,    "acl get trtcmEn"},

-    {"portEn",          doAclPortEn,        1,              "acl get portEn <port(0..6)>"},

-    {"dropEn",          doAclDropEn,        1,              "acl get dropEn <port(0..6)>"},

-    {"dropThrsh",       doAclDropThrsh,     3,              "acl get dropThrsh <port(0..6)> <color(0:green,1:yellow,2:red)> <queue(0..7)>"},

-    {"dropPbb",         doAclDropPbb,       3,              "acl get dropPbb <port(0..6)> <color(0:green,1:yellow,2:red)> <queue(0..7)>"},

-    {"meter",           doAclMeter,         1,              "acl get meter <idx(0..31)>"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T aclDelCmds[] =

-{

-    {"rule",        doAclRmvRule,          1,         "acl del rule <idx(0..127)>"},

-    {"udfRule",     doAclRmvUdfRule,       1,         "acl del udfRule <idx(0..15)>"},

-    {"action",      doAclRmvAction,        1,         "acl del action <idx(0..127)>"},

-    {"trtcm",       doAclRmvTrtcm,         1,         "acl del trtcm <idx(0..31)>"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T aclClearCmds[] =

-{

-    {"rule",        doAclRmvRule,          CMD_NO_PARA,       "acl clear rule"},

-    {"udfRule",     doAclRmvUdfRule,       CMD_NO_PARA,       "acl clear udfRule"},

-    {"action",      doAclRmvAction,        CMD_NO_PARA,       "acl clear action"},

-    {"trtcm",       doAclRmvTrtcm,         CMD_NO_PARA,       "acl clear trtcm"},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T aclCmds[] =

-{

-    {"set",         doAclSet,           0,      NULL},

-    {"get",         doAclGet,           0,      NULL},

-    {"del",         doAclDel,           0,      NULL},

-    {"clear",       doAclClear,         0,      NULL},

-    {"dump",        doAclDump,          0,      NULL},

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-static AIR_CMD_T Cmds[] =

-{

-    {"reg",         doReg,          0,      NULL},

-    {"phy",         doPhy,          0,      NULL},

-    {"port",        doPort,         0,      NULL},

-    {"vlan",        doVlan,         0,      NULL},

-    {"l2",          doL2,           0,      NULL},

-    {"lag",         doLag,          0,      NULL},

-    {"stp",         doStp,          0,      NULL},

-    {"mirror",      doMirror,       0,      NULL},

-    {"mib",         doMib,          0,      NULL},

-    {"qos",         doQos,          0,      NULL},

-    {"diag",        doDiag,         0,      NULL},

-    {"led",         doLed,          0,      NULL},

-    {"switch",      doSwitch,       0,      NULL},

-    {"show",        doShow,         0,      NULL},

-    {"sec",         doSec,          0,      NULL},

-    {"acl",         doAcl,          0,      NULL},

-    {"sptag",       doSptag,        0,      NULL},

-

-    /* last entry, do not modify this entry */

-    {NULL, NULL, 0, NULL},

-};

-

-/* EXPORTED SUBPROGRAM BODIES

-*/

-

-/* LOCAL SUBPROGRAM BODIES

-*/

-static BOOL_T

-_strcmp(const char *s1, const char *s2)

-{

-    while(*s1 == *s2++)

-        if (*s1++ == '\0')

-            return (0);

-    return (*(const unsigned char *)s1 - *(const unsigned char *)(s2 -1));

-}

-

-static C8_T *

-_strtok_r(

-    C8_T *s,

-    const C8_T *delim,

-    C8_T **last)

-{

-    char *spanp;

-    int c = 0, sc = 0;

-    char *tok;

-

-    if (s == NULL && (s = *last) == NULL)

-    {

-        return (NULL);

-    }

-

-    /*

-     * Skip (span) leading delimiters (s += strspn(s, delim), sort of).

-     */

-    for (;;)

-    {

-        c = *s++;

-        spanp = (char *)delim;

-        do

-        {

-            if (c == (sc = *spanp++))

-            {

-                break;

-            }

-        } while (sc != 0);

-        if (sc == 0)

-        {

-            break;

-        }

-    }

-

-    if (c == 0)

-    {   /* no non-delimiter characters */

-        *last = NULL;

-        return (NULL);

-    }

-    tok = s - 1;

-

-    /*

-     * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).

-     * Note that delim must have one NUL; we stop if we see that, too.

-     */

-    for (;;)

-    {

-        c = *s++;

-        spanp = (char *)delim;

-        do

-        {

-            if ((sc = *spanp++) == c)

-            {

-                if (c == 0)

-                {

-                    s = NULL;

-                }

-                else

-                {

-                    s[-1] = 0;

-                }

-                *last = s;

-                return (tok);

-            }

-        } while (sc != 0);

-    }

-    /* NOTREACHED */

-}

-

-static C8_T *

-_strtok(

-    C8_T *s,

-    const C8_T *delim,

-    C8_T **last)

-{

-    return _strtok_r(s, delim, last);

-}

-

-UI32_T

-_strtoul(

-    const C8_T *cp,

-    C8_T **endp,

-    UI32_T base)

-{

-    UI32_T result = 0, value = 0;

-

-    if (!base)

-    {

-        base = 10;

-        if (*cp == '0')

-        {

-            base = 8;

-            cp++;

-            if ((TOLOWER(*cp) == 'x') && isxdigit(cp[1]))

-            {

-                cp++;

-                base = 16;

-            }

-        }

-    }

-    else if (base == 16)

-    {

-        if (cp[0] == '0' && TOLOWER(cp[1]) == 'x')

-        {

-            cp += 2;

-        }

-    }

-    while (isxdigit(*cp) &&

-           (value = isdigit(*cp) ? *cp-'0' : TOLOWER(*cp)-'a'+10) < base)

-    {

-        result = result*base + value;

-        cp++;

-    }

-    if (endp)

-    {

-        *endp = (char *)cp;

-    }

-    return result;

-}

-

-static I32_T

-_strtol(

-    const C8_T *cp,

-    C8_T **endp,

-    UI32_T base)

-{

-    if(*cp=='-')

-    {

-        return -_strtoul(cp + 1, endp, base);

-    }

-    return _strtoul(cp, endp, base);

-}

-

-static AIR_ERROR_NO_T

-_str2mac(

-        C8_T *str,

-        C8_T *mac)

-{

-    UI32_T i = 0;

-    C8_T tmpstr[3];

-

-    /* check str */

-    AIR_CHECK_PTR(str);

-    AIR_PARAM_CHK(strlen(str) != AIR_MAC_LEN, AIR_E_BAD_PARAMETER);

-    AIR_CHECK_PTR(mac);

-

-    for(i=0; i<6; i++)

-    {

-        strncpy(tmpstr, str+(i*2), 2);

-        tmpstr[2] = '\0';

-        mac[i] = _strtoul(tmpstr, NULL, 16);

-    }

-

-    return AIR_E_OK;

-}

-

-static AIR_ERROR_NO_T

-_str2ipv4(

-    const C8_T *ptr_str,

-    UI32_T     *ptr_addr)

-{

-    UI32_T value = 0, idx = 0, shift = 0;

-

-    AIR_CHECK_PTR(ptr_str);

-    AIR_CHECK_PTR(ptr_addr);

-

-    /* e.g. 192.168.1.2, strlen = 11 */

-    for (idx = 0; idx < strlen(ptr_str); idx++)

-    {

-        if (('0' <= ptr_str[idx]) && ('9' >= ptr_str[idx]))

-        {

-            value = (value * 10) + (ptr_str[idx] - '0');

-        }

-        else if ('.' == ptr_str[idx])

-        {

-            CMD_CHECK_PARA(value, <, 256); /* Error: invalid value */

-            CMD_CHECK_PARA(shift, <, 4);   /* Error: mem-overwrite */

-            *ptr_addr |= value << (24 - shift * 8);

-            shift += 1;

-            value = 0;

-        }

-        else

-        {

-            return AIR_E_BAD_PARAMETER; /* Error: not a digit number or dot */

-        }

-    }

-    CMD_CHECK_PARA(value, <, 256); /* Error: invalid value */

-    CMD_CHECK_PARA(shift, ==, 3);  /* Error: not an ipv4 addr */

-    *ptr_addr |= value << (24 - shift * 8);

-

-    return AIR_E_OK;

-}

-

-AIR_ERROR_NO_T

-_str2ipv6(

-    const C8_T  *ptr_str,

-    UI8_T       *ptr_addr)

-{

-    UI32_T              hex_value = 0, dec_value = 0, idx = 0;

-    BOOL_T              double_colon = FALSE, ipv4_compatible = FALSE;

-    UI32_T              double_colon_pos = 0, last_pos = 0;

-    UI8_T               tmp_ipv6[16] = {0};

-

-    AIR_CHECK_PTR(ptr_str);

-    AIR_CHECK_PTR(ptr_addr);

-

-    /* e.g. invalid:

-     * 3ffe::c0a8:0:      last cannot be colon except double-colon

-     * 3ffe:::c0a8:0      triple-colon

-     * 3ffe::c0a8::0      two double-colons

-     */

-

-    /* e.g. valid:

-     * 3ffe::c0a8:0       strlen = 12 (double-colon in middle)

-     * 3ffe:c0a8:0::      strlen = 13 (double-colon in last)

-     * ::3ffe:c0a8:0      strlen = 13 (double-colon in first)

-     * 3ffe::192.168.0.0  strlen = 17 (IPv4-compatible address)

-     */

-    for (idx = 0; idx < strlen(ptr_str); idx++)

-    {

-        if (('0' <= ptr_str[idx]) && ('9' >= ptr_str[idx]))

-        {

-            hex_value = (hex_value << 4) + (ptr_str[idx] - '0');

-            dec_value = (dec_value * 10) + (ptr_str[idx] - '0');

-        }

-        else if (('a' <= ptr_str[idx]) && ('f' >= ptr_str[idx]))

-        {

-            hex_value = (hex_value << 4) + (ptr_str[idx] - 'a') + 10;

-        }

-        else if (('A' <= ptr_str[idx]) && ('F' >= ptr_str[idx]))

-        {

-            hex_value = (hex_value << 4) + (ptr_str[idx] - 'A') + 10;

-        }

-        else if (':' == ptr_str[idx])

-        {

-            /* must belong to double-colon, calculate from last */

-            if (0 == idx)

-            {

-                continue;

-            }

-            /* not the first ch but a double-colon */

-            else if (':' == ptr_str[idx - 1])

-            {

-                CMD_CHECK_PARA(double_colon, ==, FALSE); /* Error: triple-colon or two double-colons */

-                double_colon = TRUE;

-            }

-            /* not the first ch and a double-colon */

-            else

-            {

-                CMD_CHECK_PARA(double_colon_pos, <, 15); /* Error: only 16 units for UI8_T  */

-                CMD_CHECK_PARA(last_pos,         <, 15); /* Error: only 16 units for UI8_T  */

-                tmp_ipv6[last_pos]     = (UI8_T)((hex_value >> 8) & 0xff);

-                tmp_ipv6[last_pos + 1] = (UI8_T)((hex_value >> 0) & 0xff);

-                double_colon_pos += (FALSE == double_colon)? 2 : 0;

-                last_pos += 2;

-                hex_value = 0;

-                dec_value = 0;

-            }

-        }

-        else if ('.' == ptr_str[idx])

-        {

-            CMD_CHECK_PARA(last_pos, <, 16); /* Error: only 16 units for UI8_T  */

-            tmp_ipv6[last_pos] = dec_value;

-            last_pos += 1;

-            dec_value = 0;

-            ipv4_compatible = TRUE;

-        }

-        else

-        {

-            return AIR_E_BAD_PARAMETER; /* Error: not a hex number or colon */

-        }

-    }

-

-    /* last data */

-    if (':' != ptr_str[idx - 1])

-    {

-        if (FALSE == ipv4_compatible)

-        {

-            CMD_CHECK_PARA(last_pos, <, 15); /* Error: only 16 units for UI8_T  */

-            tmp_ipv6[last_pos]     = (UI8_T)((hex_value >> 8) & 0xff);

-            tmp_ipv6[last_pos + 1] = (UI8_T)((hex_value >> 0) & 0xff);

-            last_pos += 2;

-        }

-        else

-        {

-            CMD_CHECK_PARA(last_pos, <, 16); /* Error: only 16 units for UI8_T  */

-            tmp_ipv6[last_pos] = dec_value;

-            last_pos += 1;

-        }

-    }

-    else

-    {

-        if (':' != ptr_str[idx - 2])

-        {

-            return AIR_E_BAD_PARAMETER; /* Error: last cannot be colon except double-colon */

-        }

-    }

-

-    /* move tmp_ipv6 to ptr_value */

-    if (TRUE == double_colon)

-    {

-        /* e.g.

-         * 3ffe::c0a8:0       double_colon_pos = 2, last_pos = 4+2, tmp_ipv6 = {3f,fe,c0,a8,00,00,...}

-         * 3ffe:c0a8:0::      double_colon_pos = 6, last_pos = 6,   tmp_ipv6 = {3f,fe,c0,a8,00,00,...}

-         * ::3ffe:c0a8:0      double_colon_pos = 0, last_pos = 4+2, tmp_ipv6 = {3f,fe,c0,a8,00,00,...}

-         * 3ffe::192.168.0.0  double_colon_pos = 2, last_pos = 5+1, tmp_ipv6 = {3f,fe,c0,a8,00,00,...}

-         *

-         *                                 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f

-         * 3ffe::c0a8:0       ptr_value = {3f,fe,--,--,--,--,--,--,--,--,--,--,--,--,--,--}

-         * 3ffe:c0a8:0::      ptr_value = {3f,fe,c0,a8,00,00,--,--,--,--,--,--,--,--,--,--}

-         * ::3ffe:c0a8:0      ptr_value = {--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--}

-         * 3ffe::192.168.0.0  ptr_value = {3f,fe,--,--,--,--,--,--,--,--,--,--,--,--,--,--}

-         */

-        for (idx = 0; idx < double_colon_pos; idx++)

-        {

-            ptr_addr[idx] = tmp_ipv6[idx];

-        }

-        /* e.g.

-         *                                 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f

-         * 3ffe::c0a8:0       ptr_value = {3f,fe,--,--,--,--,--,--,--,--,--,--,c0,a8,00,00}

-         * 3ffe:c0a8:0::      ptr_value = {3f,fe,c0,a8,00,00,--,--,--,--,--,--,--,--,--,--}

-         * ::3ffe:c0a8:0      ptr_value = {--,--,--,--,--,--,--,--,--,--,3f,fe,c0,a8,00,00}

-         * 3ffe::192.168.0.0  ptr_value = {3f,fe,--,--,--,--,--,--,--,--,--,--,c0,a8,00,00}

-         */

-        for (idx = double_colon_pos; idx < last_pos; idx++)

-        {

-            ptr_addr[16 - (last_pos - idx)] = tmp_ipv6[idx];

-        }

-    }

-    else

-    {

-        for (idx = 0; idx < 16; idx++)

-        {

-            ptr_addr[idx] = tmp_ipv6[idx];

-        }

-    }

-

-    return AIR_E_OK;

-}

-

-void

-_showIpv6Str(

-    const UI8_T *ptr_ipv6,

-    C8_T *ptr_str)

-{

-    UI32_T idx = 0, next = 0, last = 16;

-    UI32_T cont_zero = 0;

-

-    while (idx < last)

-    {

-        if ((0 == cont_zero) && (0 == ptr_ipv6[idx]) && (0 == ptr_ipv6[idx + 1]))

-        {

-            next = idx + 2;

-

-            while (next < last)

-            {

-                if ((ptr_ipv6[next]) || (ptr_ipv6[next + 1]))

-                {

-                    AIR_PRINT(

-                            ptr_str + strlen(ptr_str),

-                            40 - strlen(ptr_str),

-                            "%s", (cont_zero) ? (":") : (":0"));

-                    break;

-                }

-

-                if (0 == cont_zero)

-                {

-                    cont_zero = 1;

-                }

-                next += 2;

-            }

-

-            if (next == last)

-            {

-

-                AIR_PRINT(

-                        ptr_str + strlen(ptr_str),

-                        40 - strlen(ptr_str),

-                        "%s", (cont_zero) ? ("::") : (":0"));

-            }

-

-            idx = next;

-        }

-        else

-        {

-            if (idx)

-            {

-                AIR_PRINT(

-                    ptr_str + strlen(ptr_str),

-                    40 - strlen(ptr_str),

-                    ":");

-            }

-

-            if (ptr_ipv6[idx])

-            {

-                AIR_PRINT(

-                    ptr_str + strlen(ptr_str),

-                    40 - strlen(ptr_str),

-                    "%0x%02x", ptr_ipv6[idx], ptr_ipv6[idx + 1]);

-            }

-            else

-            {

-                AIR_PRINT(

-                    ptr_str + strlen(ptr_str),

-                    40 - strlen(ptr_str),

-                    "%0x", ptr_ipv6[idx + 1]);

-            }

-

-            idx += 2;

-        }

-    }

-}

-

-static AIR_ERROR_NO_T

-_hex2bit(

-        const UI32_T hex,

-        UI32_T *ptr_bit)

-{

-    UI32_T i = 0;

-

-    /* Mistake proofing */

-    AIR_CHECK_PTR(ptr_bit);

-

-    (*ptr_bit) = 0;

-    for(i=0; i<AIR_MAX_NUM_OF_PORTS; i++)

-    {

-        if(hex & BIT(i))

-        {

-            (*ptr_bit) |= BITS_OFF_L(1UL, 4*(AIR_MAX_NUM_OF_PORTS - i - 1), 4);

-        }

-    }

-    return AIR_E_OK;

-}

-

-static AIR_ERROR_NO_T

-_hex2bitstr(

-        const UI32_T hex,

-        C8_T *ptr_bit_str,

-        UI32_T str_len)

-{

-    UI32_T i = 0;

-    C8_T str_bitmap[AIR_MAX_NUM_OF_PORTS+1];

-

-    /* Mistake proofing */

-    AIR_CHECK_PTR(ptr_bit_str);

-    AIR_PARAM_CHK(str_len <= AIR_MAX_NUM_OF_PORTS, AIR_E_BAD_PARAMETER);

-

-    memset(str_bitmap, 0, AIR_MAX_NUM_OF_PORTS+1);

-

-    for(i=0; i<AIR_MAX_NUM_OF_PORTS; i++)

-    {

-        if(hex & BIT(i))

-        {

-            str_bitmap[i] = '1';

-        }

-        else

-        {

-            str_bitmap[i] = '-';

-        }

-    }

-    str_bitmap[i] = '\0';

-    strncpy(ptr_bit_str, str_bitmap, i+1);

-

-    return AIR_E_OK;

-}

-

-static AIR_ERROR_NO_T

-_portListStr2Ary(

-    const C8_T *str,

-    UI32_T *ary,

-    const UI32_T ary_num)

-{

-    UI32_T i = 0;

-    UI32_T str_len = 0;

-    UI32_T val = 0;

-    C8_T *str2;

-    C8_T *pch;

-    C8_T *last;

-

-    /* Mistake proofing */

-    AIR_CHECK_PTR(str);

-    AIR_CHECK_PTR(ary);

-    AIR_PARAM_CHK(0 == ary_num, AIR_E_BAD_PARAMETER);

-

-    /* Allocate new string */

-    str_len = strlen(str);

-    str2 = AIR_MALLOC(str_len+1);

-    AIR_CHECK_PTR(str2);

-    memset(str2, 0, str_len+1);

-    strncpy(str2, str, str_len+1);

-

-    /* clear array */

-    memset(ary, 0, ary_num*4);

-

-    /* split string by ',' */

-    pch = _strtok(str2, ",", &last);

-    while(NULL != pch)

-    {

-        val = _strtoul(pch, NULL, 0);

-        ary[val/32] |= BIT(val%32);

-        pch = _strtok(NULL, ",", &last);

-    }

-

-    AIR_FREE(str2);

-    return AIR_E_OK;

-}

-

-static AIR_ERROR_NO_T

-doRegRead(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    UI32_T reg = 0, val = 0;

-

-    reg = _strtoul(argv[0], NULL, 16);

-    aml_readReg(0, reg, &val);

-    AIR_PRINT("Read reg=0x%x, value=0x%x\n", reg, val);

-

-    return AIR_E_OK;

-}

-

-static AIR_ERROR_NO_T

-doRegWrite(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    UI32_T reg = 0, val = 0;

-

-    reg = _strtoul(argv[0], NULL, 16);

-    val = _strtoul(argv[1], NULL, 16);

-    aml_writeReg(0, reg, val);

-    AIR_PRINT("Write reg=0x%x, value=0x%x\n", reg, val);

-

-    return AIR_E_OK;

-}

-

-static AIR_ERROR_NO_T

-doReg(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(regCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doPhyCL22Read(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    UI32_T port = 0, reg = 0, val = 0;

-

-    port = _strtoul(argv[0], NULL, 0);

-    reg  = _strtoul(argv[1], NULL, 16);

-    aml_readPhyReg(0, port, reg, &val);

-    AIR_PRINT("Phy read port=%d, reg=0x%x, value=0x%x\n", port, reg, val);

-

-    return AIR_E_OK;

-}

-

-static AIR_ERROR_NO_T

-doPhyCL22Write(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    UI32_T port = 0, reg = 0, val = 0;

-

-    port = _strtoul(argv[0], NULL, 0);

-    reg  = _strtoul(argv[1], NULL, 16);

-    val  = _strtoul(argv[2], NULL, 16);

-    aml_writePhyReg(0, port, reg, val);

-    AIR_PRINT("Phy write port=%d, reg=0x%x, value=0x%x\n", port, reg, val);

-

-    return AIR_E_OK;

-}

-

-static AIR_ERROR_NO_T

-doPhyCL22(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(phyCL22Cmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doPhyCL45Read(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    UI32_T port = 0, dev = 0, reg = 0, val = 0;

-

-    port = _strtoul(argv[0], NULL, 0);

-    dev  = _strtoul(argv[1], NULL, 16);

-    reg  = _strtoul(argv[2], NULL, 16);

-    aml_readPhyRegCL45(0, port, dev, reg, &val);

-    AIR_PRINT("Phy read port=%d, dev=0x%x, reg=0x%x, value=0x%x\n", port, dev, reg, val);

-

-    return AIR_E_OK;

-}

-

-static AIR_ERROR_NO_T

-doPhyCL45Write(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    UI32_T port = 0, dev = 0, reg = 0, val = 0;

-

-    port = _strtoul(argv[0], NULL, 0);

-    dev  = _strtoul(argv[1], NULL, 16);

-    reg  = _strtoul(argv[2], NULL, 16);

-    val  = _strtoul(argv[3], NULL, 16);

-    aml_writePhyRegCL45(0, port, dev, reg, val);

-    AIR_PRINT("Phy write port=%d, dev=0x%x, reg=0x%x, value=0x%x\n", port, dev, reg, val);

-

-    return AIR_E_OK;

-}

-

-static AIR_ERROR_NO_T

-doPhyCL45(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(phyCL45Cmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doPhy(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(phyCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doPortSetMatrix(UI32_T argc, C8_T *argv[])

-{

-    UI32_T port = 0;

-    UI32_T matrix = 0;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port   = _strtoul(argv[0], NULL, 0);

-    matrix = _strtoul(argv[1], NULL, 16);

-    rc = air_port_setPortMatrix(0, port, matrix);

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("Error: Operation failed!\n");

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doPortSetVlanMode(UI32_T argc, C8_T *argv[])

-{

-    UI32_T port = 0;

-    AIR_PORT_VLAN_MODE_T vlan_mode;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port      = _strtoul(argv[0], NULL, 0);

-    vlan_mode = _strtoul(argv[1], NULL, 0);

-    rc = air_port_setVlanMode(0, port, vlan_mode);

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("Error: Operation failed!\n");

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doPortSet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(portSetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doPortGetMatrix(UI32_T argc, C8_T *argv[])

-{

-    UI32_T port = 0;

-    UI32_T matrix = 0;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port = _strtoul(argv[0], NULL, 0);

-    rc = air_port_getPortMatrix(0, port, &matrix);

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("Error: Operation failed!\n");

-        return rc;

-    }

-    AIR_PRINT("Port %d Matrix: %2x\n", port, matrix);

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doPortGetVlanMode(UI32_T argc, C8_T *argv[])

-{

-    UI32_T port = 0;

-    AIR_PORT_VLAN_MODE_T vlan_mode;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port = _strtoul(argv[0], NULL, 0);

-    rc = air_port_getVlanMode(0, port, &vlan_mode);

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("Error: Operation failed!\n");

-        return rc;

-    }

-    AIR_PRINT("Port %d Vlan Mode: ", port);

-    switch(vlan_mode)

-    {

-        case AIR_PORT_VLAN_MODE_PORT_MATRIX:

-            AIR_PRINT("matrix(%d)\n", vlan_mode);

-            break;

-        case AIR_PORT_VLAN_MODE_FALLBACK:

-            AIR_PRINT("fallback(%d)\n", vlan_mode);

-            break;

-        case AIR_PORT_VLAN_MODE_CHECK:

-            AIR_PRINT("check(%d)\n", vlan_mode);

-            break;

-        case AIR_PORT_VLAN_MODE_SECURITY:

-            AIR_PRINT("security(%d)\n", vlan_mode);

-            break;

-        default:

-            AIR_PRINT("unknown(%d)\n", vlan_mode);

-            break;

-    };

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doPortGet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(portGetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doPort(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(portCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doVlanInitiate(UI32_T argc, C8_T *argv[])

-{

-    UI16_T vid = 0;

-    AIR_VLAN_ENTRY_ATTR_T vlan_entry = {0};

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    vid = _strtoul(argv[0], NULL, 0);

-    if (9 == argc)

-    {

-        vlan_entry.vlan_entry_format.fid           = _strtoul(argv[1], NULL, 0);

-        vlan_entry.vlan_entry_format.port_mem      = _strtoul(argv[2], NULL, 0);

-        vlan_entry.vlan_entry_format.ivl           = _strtoul(argv[3], NULL, 0);

-        vlan_entry.vlan_entry_format.port_stag     = _strtoul(argv[4], NULL, 0);

-        vlan_entry.vlan_entry_format.stag          = _strtoul(argv[5], NULL, 0);

-        vlan_entry.vlan_entry_format.eg_ctrl_en    = _strtoul(argv[6], NULL, 0);

-        vlan_entry.vlan_entry_format.eg_con        = _strtoul(argv[7], NULL, 0);

-        vlan_entry.vlan_entry_format.eg_ctrl       = _strtoul(argv[8], NULL, 0);

-

-        rc = air_vlan_create(0, vid, &vlan_entry);

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        rc = AIR_E_BAD_PARAMETER;

-    }

-

-    switch (rc)

-    {

-        case     AIR_E_OK:                                                            break;

-        case     AIR_E_ENTRY_EXISTS:  AIR_PRINT("VLAN already exist!\n");             break;

-        default:                      AIR_PRINT("Error %d: Operation failed!\n", rc); break;

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanCreate(UI32_T argc, C8_T *argv[])

-{

-    UI16_T vid = 0;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    vid = _strtoul(argv[0], NULL, 0);

-    rc  = air_vlan_create(0, vid, NULL);

-

-    switch (rc)

-    {

-        case     AIR_E_OK:                                                            break;

-        case     AIR_E_ENTRY_EXISTS:  AIR_PRINT("VLAN already exist!\n");             break;

-        default:                      AIR_PRINT("Error %d: Operation failed!\n", rc); break;

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanDestroy(UI32_T argc, C8_T *argv[])

-{

-    C8_T *token = NULL;

-    UI16_T vid = 0, vid_limit = AIR_VLAN_ID_MAX;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    if (argc > 0)

-    {

-        if (isdigit(argv[0][0]))

-        {

-            token = _strtok(argv[0], "-", &argv[0]);

-            vid = _strtoul(token, NULL, 0);

-            if ((token = _strtok(argv[0], "-", &argv[0])))

-                vid_limit = _strtoul(token, NULL, 0);

-            else

-                vid_limit = vid;

-            if (AIR_VLAN_ID_MAX < vid_limit)

-            {

-                AIR_PRINT("vid number should less than %d!\n", AIR_VLAN_ID_MAX);

-                return AIR_E_BAD_PARAMETER;

-            }

-            if (vid > vid_limit)

-            {

-                AIR_PRINT("vid0 should less than vid1!\n");

-                return AIR_E_BAD_PARAMETER;

-            }

-        }

-        else

-        {

-            AIR_PRINT("Bad parameter!\n");

-            return AIR_E_BAD_PARAMETER;

-        }

-    }

-

-    for (; vid <= vid_limit; vid++)

-    {

-        rc = air_vlan_destroy(0, vid);

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanDestroyAll(UI32_T argc, C8_T *argv[])

-{

-    UI32_T restore_def_vlan = 0;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    if (argc > 0)

-    {

-        restore_def_vlan = _strtoul(argv[0], NULL, 0);

-    }

-

-    rc = air_vlan_destroyAll(0, restore_def_vlan);

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("Error: Operation failed!\n");

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanDump(UI32_T argc, C8_T *argv[])

-{

-    C8_T *token = NULL;

-    UI16_T port = 0, valid_count = 0, vid = 0, vid_limit = AIR_VLAN_ID_MAX;

-    AIR_PORT_EGS_TAG_ATTR_T tag_ctl[AIR_MAX_NUM_OF_PORTS] = {0};

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    if (argc > 0)

-    {

-        if (isdigit(argv[0][0]))

-        {

-            token = _strtok(argv[0], "-", &argv[0]);

-            vid = _strtoul(token, NULL, 0);

-            if ((token = _strtok(argv[0], "-", &argv[0])))

-                vid_limit = _strtoul(token, NULL, 0);

-            else

-                vid_limit = vid;

-            if (AIR_VLAN_ID_MAX < vid_limit)

-            {

-                AIR_PRINT("vid number should less than %d!\n", AIR_VLAN_ID_MAX);

-                return AIR_E_BAD_PARAMETER;

-            }

-            if (vid > vid_limit)

-            {

-                AIR_PRINT("vid0 should less than vid1!\n");

-                return AIR_E_BAD_PARAMETER;

-            }

-        }

-        else

-        {

-            AIR_PRINT("Bad parameter!\n");

-            return AIR_E_BAD_PARAMETER;

-        }

-    }

-

-    for (valid_count = 0; vid <= vid_limit; vid++)

-    {

-        _air_vlan_readEntry(0, vid, &vlan_entry);

-        if (vlan_entry.valid)

-        {

-            valid_count++;

-            if (1 == valid_count)

-                AIR_PRINT(" Vid Fid MemPort Ivl PortBaseStag Stag EgsTagCtlEn EgsTagCon EgsTagCtl\n======================================================================\n");

-            for (port = 0; port < AIR_MAX_NUM_OF_PORTS; port++)

-                tag_ctl[port] = (vlan_entry.vlan_entry_format.eg_ctrl >> (port * 2)) & 0x3;

-            AIR_PRINT("%4d %3d      %2x %3d %12d %4d %11d %9d   %1x%1x%1x%1x%1x%1x%1x\n",

-                vid, vlan_entry.vlan_entry_format.fid, vlan_entry.vlan_entry_format.port_mem, vlan_entry.vlan_entry_format.ivl,

-                vlan_entry.vlan_entry_format.port_stag, vlan_entry.vlan_entry_format.stag, vlan_entry.vlan_entry_format.eg_ctrl_en, vlan_entry.vlan_entry_format.eg_con,

-                tag_ctl[6], tag_ctl[5], tag_ctl[4], tag_ctl[3], tag_ctl[2], tag_ctl[1], tag_ctl[0]);

-        }

-    }

-

-    if (!valid_count)

-        AIR_PRINT("not found!\n");

-    else

-        AIR_PRINT("Found %d valid entries!\n", valid_count);

-

-    return AIR_E_OK;

-}

-

-static AIR_ERROR_NO_T

-doVlanAddPortMem(UI32_T argc, C8_T *argv[])

-{

-    UI16_T vid = 0, port = 0;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    vid  = _strtoul(argv[0], NULL, 0);

-    port = _strtoul(argv[1], NULL, 0);

-    rc = air_vlan_addMemberPort(0, vid, port);

-    switch (rc)

-    {

-        case     AIR_E_OK:                                                               break;

-        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;

-        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanDelPortMem(UI32_T argc, C8_T *argv[])

-{

-    UI16_T vid = 0, port = 0;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    vid  = _strtoul(argv[0], NULL, 0);

-    port = _strtoul(argv[1], NULL, 0);

-    rc = air_vlan_delMemberPort(0, vid, port);

-    switch (rc)

-    {

-        case     AIR_E_OK:                                                               break;

-        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;

-        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanSetFid(UI32_T argc, C8_T *argv[])

-{

-    UI16_T vid = 0;

-    UI8_T  fid = 0;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    vid = _strtoul(argv[0], NULL, 0);

-    fid = _strtoul(argv[1], NULL, 0);

-    rc = air_vlan_setFid(0, vid, fid);

-    switch (rc)

-    {

-        case     AIR_E_OK:                                                               break;

-        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;

-        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanSetMemPort(UI32_T argc, C8_T *argv[])

-{

-    UI16_T vid = 0, port_bitmap = 0;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    vid         = _strtoul(argv[0], NULL, 0);

-    port_bitmap = _strtoul(argv[1], NULL, 16);

-    rc = air_vlan_setMemberPort(0, vid, port_bitmap);

-    switch (rc)

-    {

-        case     AIR_E_OK:                                                               break;

-        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;

-        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanSetIVL(UI32_T argc, C8_T *argv[])

-{

-    UI16_T vid = 0;

-    BOOL_T enable = TRUE;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    vid    = _strtoul(argv[0], NULL, 0);

-    enable = _strtoul(argv[1], NULL, 0);

-    rc = air_vlan_setIVL(0, vid, enable);

-    switch (rc)

-    {

-        case     AIR_E_OK:                                                               break;

-        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;

-        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanSetPortBaseStag(UI32_T argc, C8_T *argv[])

-{

-    UI16_T vid = 0;

-    BOOL_T enable = TRUE;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    vid    = _strtoul(argv[0], NULL, 0);

-    enable = _strtoul(argv[1], NULL, 0);

-    rc = air_vlan_setPortBasedStag(0, vid, enable);

-    switch (rc)

-    {

-        case     AIR_E_OK:                                                               break;

-        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;

-        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanSetStag(UI32_T argc, C8_T *argv[])

-{

-    UI16_T vid = 0, stag = 0;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    vid  = _strtoul(argv[0], NULL, 0);

-    stag = _strtoul(argv[1], NULL, 0);

-    rc = air_vlan_setServiceTag(0, vid, stag);

-    switch (rc)

-    {

-        case     AIR_E_OK:                                                               break;

-        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;

-        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanSetEgsTagCtlEn(UI32_T argc, C8_T *argv[])

-{

-    UI16_T vid = 0;

-    BOOL_T enable = TRUE;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    vid    = _strtoul(argv[0], NULL, 0);

-    enable = _strtoul(argv[1], NULL, 0);

-    rc = air_vlan_setEgsTagCtlEnable(0, vid, enable);

-    switch (rc)

-    {

-        case     AIR_E_OK:                                                               break;

-        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;

-        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanSetEgsTagCtlCon(UI32_T argc, C8_T *argv[])

-{

-    UI16_T vid = 0;

-    BOOL_T enable = TRUE;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    vid    = _strtoul(argv[0], NULL, 0);

-    enable = _strtoul(argv[1], NULL, 0);

-    rc = air_vlan_setEgsTagConsistent(0, vid, enable);

-    switch (rc)

-    {

-        case     AIR_E_OK:                                                               break;

-        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;

-        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanSetEgsTagCtl(UI32_T argc, C8_T *argv[])

-{

-    UI16_T vid = 0, port = 0;

-    AIR_PORT_EGS_TAG_ATTR_T tag_ctl = AIR_PORT_EGS_TAG_ATTR_UNTAGGED;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    vid     = _strtoul(argv[0], NULL, 0);

-    port    = _strtoul(argv[1], NULL, 0);

-    tag_ctl = _strtoul(argv[2], NULL, 0);

-    rc = air_vlan_setPortEgsTagCtl(0, vid, port, tag_ctl);

-    switch (rc)

-    {

-        case     AIR_E_OK:                                                               break;

-        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;

-        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanSetPortActFrame(UI32_T argc, C8_T *argv[])

-{

-    UI16_T port = 0;

-    AIR_VLAN_ACCEPT_FRAME_TYPE_T type = AIR_VLAN_ACCEPT_FRAME_TYPE_ALL;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port = _strtoul(argv[0], NULL, 0);

-    type = _strtoul(argv[1], NULL, 0);

-    rc = air_vlan_setPortAcceptFrameType(0, port, type);

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("Error: Operation failed!\n");

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanSetLeakyVlanEn(UI32_T argc, C8_T *argv[])

-{

-    UI16_T port = 0;

-    AIR_LEAKY_PKT_TYPE_T pkt_type = AIR_LEAKY_PKT_TYPE_UNICAST;

-    BOOL_T enable = TRUE;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port     = _strtoul(argv[0], NULL, 0);

-    pkt_type = _strtoul(argv[1], NULL, 0);

-    enable   = _strtoul(argv[2], NULL, 0);

-    rc = air_vlan_setPortLeakyVlanEnable(0, port, pkt_type, enable);

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("Error: Operation failed!\n");

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanSetPortVlanAttr(UI32_T argc, C8_T *argv[])

-{

-    UI16_T port = 0;

-    AIR_VLAN_PORT_ATTR_T attr = AIR_VLAN_PORT_ATTR_USER_PORT;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port = _strtoul(argv[0], NULL, 0);

-    attr = _strtoul(argv[1], NULL, 0);

-    rc = air_vlan_setPortAttr(0, port, attr);

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("Error: Operation failed!\n");

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanSetIgsPortETagAttr(UI32_T argc, C8_T *argv[])

-{

-    UI16_T port = 0;

-    AIR_IGR_PORT_EG_TAG_ATTR_T attr = AIR_IGR_PORT_EG_TAG_ATTR_DISABLE;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port = _strtoul(argv[0], NULL, 0);

-    attr = _strtoul(argv[1], NULL, 0);

-    rc = air_vlan_setIgrPortTagAttr(0, port, attr);

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("Error: Operation failed!\n");

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanSetPortETagAttr(UI32_T argc, C8_T *argv[])

-{

-    UI16_T port = 0;

-    AIR_PORT_EGS_TAG_ATTR_T attr = AIR_PORT_EGS_TAG_ATTR_UNTAGGED;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port = _strtoul(argv[0], NULL, 0);

-    attr = _strtoul(argv[1], NULL, 0);

-    rc = air_vlan_setPortEgsTagAttr(0, port, attr);

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("Error: Operation failed!\n");

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanSetPortOuterTPID(UI32_T argc, C8_T *argv[])

-{

-    UI16_T port = 0, tpid = 0;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port = _strtoul(argv[0], NULL, 0);

-    tpid = _strtoul(argv[1], NULL, 16);

-    rc = air_vlan_setPortOuterTPID(0, port, tpid);

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("Error: Operation failed!\n");

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanSetPvid(UI32_T argc, C8_T *argv[])

-{

-    UI16_T port = 0, pvid = 0;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port = _strtoul(argv[0], NULL, 0);

-    pvid = _strtoul(argv[1], NULL, 0);

-    rc = air_vlan_setPortPVID(0, port, pvid);

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("Error: Operation failed!\n");

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanSet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(vlanSetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doVlanGetPortActFrame(UI32_T argc, C8_T *argv[])

-{

-    UI32_T port = 0;

-    AIR_VLAN_ACCEPT_FRAME_TYPE_T type;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port = _strtoul(argv[0], NULL, 0);

-    rc = air_vlan_getPortAcceptFrameType(0, port, &type);

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("Error: Operation failed!\n");

-        return rc;

-    }

-    AIR_PRINT("Port %d Acceptable Frame Type: ", port);

-    switch(type)

-    {

-        case AIR_VLAN_ACCEPT_FRAME_TYPE_ALL:

-            AIR_PRINT("all(%d)\n", type);

-            break;

-        case AIR_VLAN_ACCEPT_FRAME_TYPE_TAG_ONLY:

-            AIR_PRINT("tagged-only(%d)\n", type);

-            break;

-        case AIR_VLAN_ACCEPT_FRAME_TYPE_UNTAG_ONLY:

-            AIR_PRINT("untagged-only(%d)\n", type);

-            break;

-        default:

-            AIR_PRINT("unknown(%d)\n", type);

-            break;

-    };

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanGetLeakyVlanEn(UI32_T argc, C8_T *argv[])

-{

-    UI32_T port = 0;

-    BOOL_T uc = FALSE, mc = FALSE, bc = FALSE;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port = _strtoul(argv[0], NULL, 0);

-    rc += air_vlan_getPortLeakyVlanEnable(0, port, AIR_LEAKY_PKT_TYPE_UNICAST, &uc);

-    rc += air_vlan_getPortLeakyVlanEnable(0, port, AIR_LEAKY_PKT_TYPE_MULTICAST, &mc);

-    rc += air_vlan_getPortLeakyVlanEnable(0, port, AIR_LEAKY_PKT_TYPE_BROADCAST, &bc);

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("Error: Operation failed!\n");

-        return rc;

-    }

-

-    AIR_PRINT("Port %d Leaky Vlan Enable\n", port);

-    AIR_PRINT("Unicast     : %s\n", uc ? "TRUE" : "FALSE");

-    AIR_PRINT("Multicast   : %s\n", mc ? "TRUE" : "FALSE");

-    AIR_PRINT("Broadcast   : %s\n", bc ? "TRUE" : "FALSE");

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanGetPortVlanAttr(UI32_T argc, C8_T *argv[])

-{

-    UI32_T port = 0;

-    AIR_VLAN_PORT_ATTR_T attr;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port = _strtoul(argv[0], NULL, 0);

-    rc = air_vlan_getPortAttr(0, port, &attr);

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("Error: Operation failed!\n");

-        return rc;

-    }

-    AIR_PRINT("Port %d Vlan Attr: ", port);

-    switch(attr)

-    {

-        case AIR_VLAN_PORT_ATTR_USER_PORT:

-            AIR_PRINT("user port(%d)\n", attr);

-            break;

-        case AIR_VLAN_PORT_ATTR_STACK_PORT:

-            AIR_PRINT("stack port(%d)\n", attr);

-            break;

-        case AIR_VLAN_PORT_ATTR_TRANSLATION_PORT:

-            AIR_PRINT("translation port(%d)\n", attr);

-            break;

-        case AIR_VLAN_PORT_ATTR_TRANSPARENT_PORT:

-            AIR_PRINT("transparent port(%d)\n", attr);

-            break;

-        default:

-            AIR_PRINT("unknown(%d)\n", attr);

-            break;

-    };

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanGetIgsPortETagAttr(UI32_T argc, C8_T *argv[])

-{

-    UI32_T port = 0;

-    AIR_IGR_PORT_EG_TAG_ATTR_T attr;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port = _strtoul(argv[0], NULL, 0);

-    rc = air_vlan_getIgrPortTagAttr(0, port, &attr);

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("Error: Operation failed!\n");

-        return rc;

-    }

-    AIR_PRINT("Port %d Incomming Port Egress Tag Attr: ", port);

-    switch(attr)

-    {

-        case AIR_IGR_PORT_EG_TAG_ATTR_DISABLE:

-            AIR_PRINT("disable(%d)\n", attr);

-            break;

-        case AIR_IGR_PORT_EG_TAG_ATTR_CONSISTENT:

-            AIR_PRINT("consistent(%d)\n", attr);

-            break;

-        case AIR_IGR_PORT_EG_TAG_ATTR_UNTAGGED:

-            AIR_PRINT("untagged(%d)\n", attr);

-            break;

-        case AIR_IGR_PORT_EG_TAG_ATTR_SWAP:

-            AIR_PRINT("swap(%d)\n", attr);

-            break;

-        case AIR_IGR_PORT_EG_TAG_ATTR_TAGGED:

-            AIR_PRINT("tagged(%d)\n", attr);

-            break;

-        case AIR_IGR_PORT_EG_TAG_ATTR_STACK:

-            AIR_PRINT("stack(%d)\n", attr);

-            break;

-        default:

-            AIR_PRINT("unknown(%d)\n", attr);

-            break;

-    };

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanGetPortETagAttr(UI32_T argc, C8_T *argv[])

-{

-    UI32_T port = 0;

-    AIR_PORT_EGS_TAG_ATTR_T attr;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port = _strtoul(argv[0], NULL, 0);

-    rc = air_vlan_getPortEgsTagAttr(0, port, &attr);

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("Error: Operation failed!\n");

-        return rc;

-    }

-    AIR_PRINT("Port %d Egress Tag Attr: ", port);

-    switch(attr)

-    {

-        case AIR_PORT_EGS_TAG_ATTR_UNTAGGED:

-            AIR_PRINT("untagged(%d)\n", attr);

-            break;

-        case AIR_PORT_EGS_TAG_ATTR_SWAP:

-            AIR_PRINT("swap(%d)\n", attr);

-            break;

-        case AIR_PORT_EGS_TAG_ATTR_TAGGED:

-            AIR_PRINT("tagged(%d)\n", attr);

-            break;

-        case AIR_PORT_EGS_TAG_ATTR_STACK:

-            AIR_PRINT("stack(%d)\n", attr);

-            break;

-        default:

-            AIR_PRINT("unknown(%d)\n", attr);

-            break;

-    };

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanGetPortOuterTPID(UI32_T argc, C8_T *argv[])

-{

-    UI32_T port = 0;

-    UI16_T tpid = 0;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port = _strtoul(argv[0], NULL, 0);

-    rc = air_vlan_getPortOuterTPID(0, port, &tpid);

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("Error: Operation failed!\n");

-        return rc;

-    }

-    AIR_PRINT("Port %d Outer TPID: %4x\n", port, tpid);

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanGetPvid(UI32_T argc, C8_T *argv[])

-{

-    UI32_T port = 0;

-    UI16_T pvid = 0;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port = _strtoul(argv[0], NULL, 0);

-    rc = air_vlan_getPortPVID(0, port, &pvid);

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("Error: Operation failed!\n");

-        return rc;

-    }

-    AIR_PRINT("Port %d PVID: %d\n", port, pvid);

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doVlanGet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(vlanGetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doVlan(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(vlanCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doJumbo(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    I32_T pkt_len = 0, frame_len = 0;

-

-    if(0 == argc)

-    {

-        /* get command */

-        ret = air_port_getJumbo(0, &pkt_len, &frame_len);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Get ");

-            switch(pkt_len)

-            {

-                case 0:

-                    AIR_PRINT("RX_1518 ");

-                    break;

-                case 1:

-                    AIR_PRINT("RX_1536 ");

-                    break;

-                case 2:

-                    AIR_PRINT("RX_1552 ");

-                    break;

-                case 3:

-                    AIR_PRINT("RX_JUMBO ");

-                    break;

-            }

-            AIR_PRINT("frames lengths %d KBytes\n", frame_len);

-        }

-        else

-        {

-            AIR_PRINT("Get Jumbo Fail.\n");

-        }

-    }

-    else

-    {

-        /* set command */

-        pkt_len = _strtol(argv[0], NULL, 10);

-        frame_len = _strtol(argv[1], NULL, 10);

-

-        ret = air_port_setJumbo(0, pkt_len, frame_len);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Set ");

-            switch(pkt_len)

-            {

-                case 0:

-                    AIR_PRINT("RX_1518 ");

-                    break;

-                case 1:

-                    AIR_PRINT("RX_1536 ");

-                    break;

-                case 2:

-                    AIR_PRINT("RX_1552 ");

-                    break;

-                case 3:

-                    AIR_PRINT("RX_JUMBO ");

-                    break;

-            }

-            AIR_PRINT("frames lengths %d KBytes\n", frame_len);

-        }

-        else

-            AIR_PRINT("Set Jumbo Fail.\n");

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doFlowCtrl(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    BOOL_T fc_en = 0, dir = 0;

-    I32_T port = 0;

-

-    port = _strtol(argv[0], NULL, 10);

-    dir = _strtol(argv[1], NULL, 10);

-

-    if(2 == argc)

-    {

-        /* get command */

-        ret = air_port_getFlowCtrl(0, port, dir, &fc_en);

-        if(ret == AIR_E_OK)

-            AIR_PRINT("Get Port%02d %s Flow Control %s\n", port, ((dir)?"RX":"TX"), ((fc_en)?"Enable":"Disable"));

-        else

-            AIR_PRINT("Get Flow Control Fail.\n");

-    }

-    else

-    {

-        /* set command */

-        fc_en = _strtol(argv[2], NULL, 10);

-

-        ret = air_port_setFlowCtrl(0, port, dir, fc_en);

-        if(ret == AIR_E_OK)

-            AIR_PRINT("Set Port%02d %s Flow Control %s\n", port, ((dir)?"RX":"TX"), ((fc_en)?"Enable":"Disable"));

-        else

-            AIR_PRINT("Set Flow Control Fail.\n");

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doL2Set(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(l2SetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doL2Get(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(l2GetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doL2Clear(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(l2ClearCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doL2Del(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(l2DelCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doL2Add(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(l2AddCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doL2(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(l2Cmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doAnMode(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0;

-    BOOL_T en = 0;

-

-    port = _strtol(argv[0], NULL, 10);

-

-    if(1 == argc)

-    {

-        /* port get anCap <port> */

-        ret = air_port_getAnMode(0, port, &en);

-        if(ret == AIR_E_OK)

-            AIR_PRINT("Get Port%02d Auto-Negotiation %s\n", port, ((en)?"Enabled":"Disabled"));

-        else

-            AIR_PRINT("Get Port%02d Auto-Negotiation Fail.\n", port);

-    }

-    else if(2 == argc)

-    {

-        /* "port set anMode <port> <en> */

-        en = _strtol(argv[1], NULL, 10);

-        ret = air_port_setAnMode(0, port, en);

-        if(ret == AIR_E_OK)

-            AIR_PRINT("Set Port%02d Auto-Negotiation Mode:%s\n", port, ((en)?"Enabled":"Disabled"));

-        else

-            AIR_PRINT("Set Port%02d Auto-Negotiation Fail.\n", port);

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doLocalAdv(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0;

-    AIR_AN_ADV_T adv;

-

-    memset(&adv, 0, sizeof(AIR_AN_ADV_T));

-    port = _strtol(argv[0], NULL, 10);

-

-    if(1 == argc)

-    {

-        /* port get localAdv <port> */

-        ret = air_port_getLocalAdvAbility(0, port, &adv);

-        AIR_PRINT("Get Port%02d Local Auto-Negotiation Advertisement: ", port);

-        if(AIR_E_OK != ret)

-        {

-            AIR_PRINT("Fail!\n");

-        }

-    }

-    else if(7 == argc)

-    {

-        /* port set localAdv <port> <10H> <10F> <100H> <100F> <1000F> <pause> */

-        adv.advCap10HDX = _strtol(argv[1], NULL, 0) & BIT(0);

-        adv.advCap10FDX = _strtol(argv[2], NULL, 0) & BIT(0);

-        adv.advCap100HDX = _strtol(argv[3], NULL, 0) & BIT(0);

-        adv.advCap100FDX = _strtol(argv[4], NULL, 0) & BIT(0);

-        adv.advCap1000FDX = _strtol(argv[5], NULL, 0) & BIT(0);

-        adv.advPause = _strtol(argv[6], NULL, 0) & BIT(0);

-        ret = air_port_setLocalAdvAbility(0, port, adv);

-        AIR_PRINT("Set Port%02d Local Auto-Negotiation Advertisement: ", port);

-        if(AIR_E_OK != ret)

-        {

-            AIR_PRINT("Fail!\n");

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    if(ret == AIR_E_OK)

-    {

-        AIR_PRINT("\n");

-        AIR_PRINT("\tAdvertise 10BASE-T Half Duplex: %s\n", (adv.advCap10HDX)?"Effective":"Not Effective" );

-        AIR_PRINT("\tAdvertise 10BASE-T Full Duplex: %s\n", (adv.advCap10FDX)?"Effective":"Not Effective" );

-        AIR_PRINT("\tAdvertise 100BASE-T Half Duplex: %s\n", (adv.advCap100HDX)?"Effective":"Not Effective" );

-        AIR_PRINT("\tAdvertise 100BASE-T Full Duplex: %s\n", (adv.advCap100FDX)?"Effective":"Not Effective" );

-        AIR_PRINT("\tAdvertise 1000BASE-T Full Duplex: %s\n", (adv.advCap1000FDX)?"Effective":"Not Effective" );

-        AIR_PRINT("\tAdvertise Asynchronous Pause: %s\n", (adv.advPause)?"Effective":"Not Effective" );

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doRemoteAdv(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0;

-    AIR_AN_ADV_T lp_adv;

-

-    memset(&lp_adv, 0, sizeof(AIR_AN_ADV_T));

-    port = _strtol(argv[0], NULL, 10);

-

-    if(1 == argc)

-    {

-        /* port get remoteAdv <port> */

-        ret = air_port_getRemoteAdvAbility(0, port, &lp_adv);

-        AIR_PRINT("Get Port%02d Remote Auto-Negotiation Advertisement: ", port);

-        if(AIR_E_OK != ret)

-        {

-            AIR_PRINT("Fail!\n");

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    if(ret == AIR_E_OK)

-    {

-        AIR_PRINT("\n");

-        AIR_PRINT("\tAdvertise 10BASE-T Half Duplex: %s\n", lp_adv.advCap10HDX?"Effective":"Not Effective" );

-        AIR_PRINT("\tAdvertise 10BASE-T Full Duplex: %s\n", lp_adv.advCap10FDX?"Effective":"Not Effective" );

-        AIR_PRINT("\tAdvertise 100BASE-T Half Duplex: %s\n", lp_adv.advCap100HDX?"Effective":"Not Effective" );

-        AIR_PRINT("\tAdvertise 100BASE-T Full Duplex: %s\n", lp_adv.advCap100FDX?"Effective":"Not Effective" );

-        AIR_PRINT("\tAdvertise 1000BASE-T Full Duplex: %s\n", (lp_adv.advCap1000FDX)?"Effective":"Not Effective" );

-        AIR_PRINT("\tAdvertise Asynchronous Pause: %s\n", (lp_adv.advPause)?"Effective":"Not Effective" );

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doPortSpeed(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0;

-    UI32_T speed = 0;

-

-    port = _strtol(argv[0], NULL, 10);

-

-    if(1 == argc)

-    {

-        /* port get speed <port> */

-        ret = air_port_getSpeed(0, port, &speed);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Get Port%02d Speed:", port);

-        }

-        else

-        {

-            AIR_PRINT("Get Port%02d Speed Fail!\n", port);

-        }

-    }

-    else if(2 == argc)

-    {

-        /* port set speed <port> <speed> */

-        speed = _strtol(argv[1], NULL, 10);

-        ret = air_port_setSpeed(0, port, speed);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Set Port%02d Speed:", port);

-        }

-        else

-        {

-            AIR_PRINT("Set Port%02d Speed Fail!\n", port);

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    if(ret == AIR_E_OK)

-    {

-        switch(speed)

-        {

-            case AIR_PORT_SPEED_10M:

-                AIR_PRINT(" 10 Mbps\n");

-                break;

-            case AIR_PORT_SPEED_100M:

-                AIR_PRINT(" 100 Mbps\n");

-                break;

-            case AIR_PORT_SPEED_1000M:

-                AIR_PRINT(" 1 Gbps\n");

-                break;

-            case AIR_PORT_SPEED_2500M:

-                AIR_PRINT(" 2.5 Gbps\n");

-                break;

-            default:

-                AIR_PRINT(" Reserved\n");

-                break;

-        }

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doPortDuplex(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0;

-    UI32_T duplex = 0;

-

-    port = _strtol(argv[0], NULL, 10);

-

-    if(1 == argc)

-    {

-        /* port get duplex <port> */

-        ret = air_port_getDuplex(0, port, &duplex);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Get Port%02d Duplex:%s\n", port, duplex?"Full":"Half");

-        }

-        else

-        {

-            AIR_PRINT("Get Port%02d Duplex Fail!\n", port);

-        }

-    }

-    else if(2 == argc)

-    {

-        /* port set duplex <port> <duplex> */

-        duplex = _strtol(argv[1], NULL, 10);

-        ret = air_port_setDuplex(0, port, duplex);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Set Port%02d Duplex:%s\n", port, duplex?"Full":"Half");

-        }

-        else

-        {

-            AIR_PRINT("Set Port%02d Duplex Fail!\n", port);

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doPortStatus(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0;

-    AIR_PORT_STATUS_T ps;

-

-    memset(&ps, 0, sizeof(AIR_PORT_STATUS_T));

-    port = _strtol(argv[0], NULL, 10);

-

-    if(1 == argc)

-    {

-        /* port get status <port> */

-        ret = air_port_getLink(0, port, &ps);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Get Port%02d Link-Status\n", port);

-            AIR_PRINT("\tLink: %s\n", ps.link?"Up":"Down");

-            AIR_PRINT("\tDuplex: %s\n", ps.duplex?"Full":"Half");

-            AIR_PRINT("\tSpeed: ");

-            switch(ps.speed)

-            {

-                case AIR_PORT_SPEED_10M:

-                    AIR_PRINT("10 Mbps\n");

-                    break;

-                case AIR_PORT_SPEED_100M:

-                    AIR_PRINT("100 Mbps\n");

-                    break;

-                case AIR_PORT_SPEED_1000M:

-                    AIR_PRINT("1 Gbps\n");

-                    break;

-                case AIR_PORT_SPEED_2500M:

-                    AIR_PRINT("2.5 Gbps\n");

-                    break;

-                default:

-                    AIR_PRINT("Reserved\n");

-                    break;

-            }

-        }

-        else

-            AIR_PRINT("Get Port%02d Link-Status Fail!", port);

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doPortBckPres(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0;

-    UI32_T bckPres = 0;

-

-    port = _strtol(argv[0], NULL, 10);

-

-    if(1 == argc)

-    {

-        /* port get bckPres <port> */

-        ret = air_port_getBckPres(0, port, &bckPres);

-        if(ret == AIR_E_OK)

-            AIR_PRINT("Get Port%02d BckPres:%s\n", port, bckPres?"Enabled":"Disabled");

-        else

-            AIR_PRINT("Get Port%02d BckPres Fail!\n", port);

-    }

-    else if(2 == argc)

-    {

-        /* port set bckPres <port> <bckPres> */

-        bckPres = _strtol(argv[1], NULL, 10);

-        ret = air_port_setBckPres(0, port, bckPres);

-        if(ret == AIR_E_OK)

-            AIR_PRINT("Set Port%02d BckPres:%s\n", port, bckPres?"Enabled":"Disabled");

-        else

-            AIR_PRINT("Set Port%02d BckPres Fail!\n", port);

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doPortPsMode(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0;

-    UI32_T mode = 0;

-    BOOL_T ls_en = 0;

-    BOOL_T eee_en = 0;

-

-    port = _strtol(argv[0], NULL, 10);

-

-    if(1 == argc)

-    {

-        /* port get psMode <port> */

-        ret = air_port_getPsMode(0, port, &mode);

-        AIR_PRINT("Get Port%02d Power-Saving: ", port);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Done\n");

-        }

-        else

-        {

-            AIR_PRINT("Fail!\n");

-        }

-    }

-    else if(3 == argc)

-    {

-        /* port set psMode <port> <ls> <eee> */

-        ls_en = _strtol(argv[1], NULL, 0);

-        eee_en = _strtol(argv[2], NULL, 0);

-        if(TRUE == ls_en)

-        {

-            mode |= AIR_PORT_PS_LINKSTATUS;

-        }

-        if(TRUE == eee_en)

-        {

-            mode |= AIR_PORT_PS_EEE;

-        }

-        ret = air_port_setPsMode(0, port, mode);

-        AIR_PRINT("Set Port%02d Power-Saving: ", port);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Done\n");

-        }

-        else

-        {

-            AIR_PRINT("Fail!\n");

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-    if(ret == AIR_E_OK)

-    {

-        AIR_PRINT("\tLink status:%s\n", (mode & AIR_PORT_PS_LINKSTATUS)?"Enable":"Disable");

-        AIR_PRINT("\tEEE:%s\n", (mode & AIR_PORT_PS_EEE)?"Enable":"Disable");

-    }

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doPortSmtSpdDwn(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0;

-    UI32_T state = 0;

-    UI32_T retry = 0;

-

-    port = _strtol(argv[0], NULL, 10);

-

-    if(1 == argc)

-    {

-        /* port get smtSpdDwn <port> */

-        ret = air_port_getSmtSpdDwn(0, port, &state, &retry);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Get Port%02d Smart Speed Down: %s\n", port, state?"Enabled":"Disabled");

-            AIR_PRINT("Get Port%02d Retry Time: %d\n", port, retry + 2);

-        }

-        else

-            AIR_PRINT("Get Port%02d Smart-SpeedDown Fail!\n", port);

-    }

-    else if(3 == argc)

-    {

-        /* port set smtSpdDwn <port> <en> <retry> */

-        state = _strtol(argv[1], NULL, 10);

-        retry = _strtol(argv[2], NULL, 10);

-        if(retry >= 2)

-        {

-            ret = air_port_setSmtSpdDwn(0, port, state, retry - 2);

-            if(ret == AIR_E_OK)

-            {

-                AIR_PRINT("Set Port%02d Smart Speed Down: %s\n", port, state?"Enabled":"Disabled");

-                AIR_PRINT("Set Port%02d Retry Time: %d\n", port, retry);

-            }

-            else

-                AIR_PRINT("Set Port%02d Smart-SpeedDown Fail!\n", port);

-        }

-        else

-        {

-            ret = AIR_E_BAD_PARAMETER;

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doPortSpTag(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0;

-    BOOL_T sptag_en = FALSE;

-

-    port = _strtol(argv[0], NULL, 10);

-

-    if(1 == argc)

-    {

-        /* port get spTag <port> */

-        ret = air_port_getSpTag(0, port, &sptag_en);

-        if(AIR_E_OK == ret)

-        {

-            AIR_PRINT("Get Port%02d Special Tag %s\n", port, ((sptag_en)?"Enabled":"Disabled"));

-        }

-        else

-        {

-            AIR_PRINT("Get Port%02d Special Tag Fail.\n", port);

-        }

-    }

-    else if(2 == argc)

-    {

-        /* port set spTag <port> <en> */

-        sptag_en = _strtol(argv[1], NULL, 10);

-        ret = air_port_setSpTag(0, port, sptag_en);

-        if(AIR_E_OK == ret)

-        {

-            AIR_PRINT("Set Port%02d Special Tag:%s\n", port, ((sptag_en)?"Enabled":"Disabled"));

-        }

-        else

-        {

-            AIR_PRINT("Set Port%02d Special Tag Fail.\n", port);

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doPortEnable(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0;

-    UI32_T state = 0;

-

-    port = _strtol(argv[0], NULL, 10);

-

-    if(1 == argc)

-    {

-        /* port get enable <port> */

-        ret = air_port_getEnable(0, port, &state);

-        if(ret == AIR_E_OK)

-            AIR_PRINT("Get Port%02d State:%s\n", port, state?"Enable":"Disable");

-        else

-            AIR_PRINT("Get Port%02d State Fail!\n", port);

-    }

-    else if(2 == argc)

-    {

-        /* port set enable <port> <en> */

-        state = _strtol(argv[1], NULL, 10);

-        ret = air_port_setEnable(0, port, state);

-        if(ret == AIR_E_OK)

-            AIR_PRINT("Set Port%02d State:%s\n", port, state?"Enable":"Disable");

-        else

-            AIR_PRINT("Set Port%02d State Fail!\n", port);

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doPort5GBaseRMode(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-

-    if(0 == argc)

-    {

-        /* port set 5GBaseRMode */

-        ret = air_port_set5GBaseRModeEn(0);

-        if(ret == AIR_E_OK)

-            AIR_PRINT("Set Port05 Mode: 5GBase-R\n");

-        else

-            AIR_PRINT("Set Port05 HSGMII Mode Fail.\n");

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doPortHsgmiiMode(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-

-    if(0 == argc)

-    {

-        /* port set hsgmiiMode */

-        ret = air_port_setHsgmiiModeEn(0);

-        if(ret == AIR_E_OK)

-            AIR_PRINT("Set Port05 Mode: HSGMII\n");

-        else

-            AIR_PRINT("Set Port05 HSGMII Mode Fail.\n");

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doPortSgmiiMode(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T mode = 0;

-    UI32_T speed = 0;

-

-    if(2 == argc)

-    {

-        /* port set sgmiiMode <mode(0:AN,1:Force)> <speed> */

-        mode = _strtol(argv[0], NULL, 10);

-        speed = _strtol(argv[1], NULL, 10);

-        ret = air_port_setSgmiiMode(0, mode, speed);

-        if(ret == AIR_E_OK)

-            AIR_PRINT("Set Port05 SGMII Mode:%s\nIf in Force Mode, speed:", mode?"Force":"AN");

-        else

-            AIR_PRINT("Set Port05 SGMII Mode Fail.\n");

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    if(ret == AIR_E_OK)

-    {

-        switch(speed)

-        {

-            case AIR_PORT_SPEED_10M:

-                AIR_PRINT(" 10 Mbps\n");

-                break;

-            case AIR_PORT_SPEED_100M:

-                AIR_PRINT(" 100 Mbps\n");

-                break;

-            case AIR_PORT_SPEED_1000M:

-                AIR_PRINT(" 1 Gbps\n");

-                break;

-            default:

-                AIR_PRINT(" Reserved\n");

-                break;

-        }

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doPortRmiiMode(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T speed = 0;

-

-    if(1 == argc)

-    {

-        /* port set rmiiMode <speed> */

-        speed = _strtol(argv[0], NULL, 10);

-        ret = air_port_setRmiiMode(0, speed);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Set Port05 RMII Mode Speed:");

-        }

-        else

-        {

-            AIR_PRINT("Set Port05 RMII Mode Fail!\n");

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    if(ret == AIR_E_OK)

-    {

-        switch(speed)

-        {

-            case AIR_PORT_SPEED_10M:

-                AIR_PRINT(" 10 Mbps\n");

-                break;

-            case AIR_PORT_SPEED_100M:

-                AIR_PRINT(" 100 Mbps\n");

-                break;

-            default:

-                AIR_PRINT(" Reserved\n");

-                break;

-        }

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doPortRgmiiMode(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T speed = 0;

-

-    if(1 == argc)

-    {

-        /* port set rgmiiMode <speed> */

-        speed = _strtol(argv[0], NULL, 10);

-        ret = air_port_setRgmiiMode(0, speed);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Set Port05 RGMII Mode Speed:");

-        }

-        else

-        {

-            AIR_PRINT("Set Port05 RGMII Mode Fail!\n");

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    if(ret == AIR_E_OK)

-    {

-        switch(speed)

-        {

-            case AIR_PORT_SPEED_10M:

-                AIR_PRINT(" 10 Mbps\n");

-                break;

-            case AIR_PORT_SPEED_100M:

-                AIR_PRINT(" 100 Mbps\n");

-                break;

-            case AIR_PORT_SPEED_1000M:

-                AIR_PRINT(" 1 Gbps\n");

-                break;

-            default:

-                AIR_PRINT(" Reserved\n");

-                break;

-        }

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doSptagEn(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0;

-    BOOL_T sp_en = FALSE;

-

-    port =  _strtol(argv[0], NULL, 10);

-    if (2 == argc)

-    {

-        sp_en =  _strtol(argv[1], NULL, 10);

-        ret = air_sptag_setState(0,port,sp_en);

-        if(AIR_E_OK == ret)

-        {

-            AIR_PRINT("set port %d SpTag state %s sucess\n", port,sp_en?"Enable":"Disable");

-        }

-        else

-        {

-            AIR_PRINT("set port %d SpTag state %s fail\n", port,sp_en?"Enable":"Disable");

-        }

-    }

-    else if(1 == argc)

-    {

-        air_sptag_getState(0,port,&sp_en);

-        AIR_PRINT("get port %d SpTag state: %s \n", port,sp_en?"Enable":"Disable");

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doSptagMode(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0;

-    BOOL_T sp_mode = FALSE;

-

-

-    port =  _strtol(argv[0], NULL, 10);

-    if (2 == argc)

-    {

-        sp_mode  =  _strtol(argv[1], NULL, 10);

-        ret = air_sptag_setMode(0,port,sp_mode);

-        if(AIR_E_OK == ret)

-        {

-            AIR_PRINT("set port %d SpTag Mode  %s sucess\n", port,sp_mode?"replace":"insert");

-        }

-        else

-        {

-            AIR_PRINT("set port %d SpTag state %s fail\n", port,sp_mode?"replace":"insert");

-        }

-    }

-    else if(1 == argc)

-    {

-        air_sptag_getMode(0,port,&sp_mode);

-        AIR_PRINT("get port %d SpTag state: %s \n", port,sp_mode?"replace":"insert");

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doSptagDecode(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    AIR_SPTAG_RX_PARA_T sptag_rx = {0};

-    UI8_T buf[AIR_STAG_BUF_LEN] = {0};

-    UI32_T len = AIR_STAG_BUF_LEN, i = 0;

-

-    if (4 == argc)

-    {

-        for(i = 0; i < len; i++)

-        {

-            buf[i] = _strtoul(argv[i], NULL, 16);

-        }

-

-        ret = air_sptag_decodeRx(0, buf, len, &sptag_rx);

-        if (AIR_E_OK != ret)

-        {

-            AIR_PRINT("SpTag decode fail\n");

-            return ret;

-        }

-

-        AIR_PRINT("SpTag decode success:\n");

-        AIR_PRINT("RSN : %s\n", _sptag_pt[sptag_rx.rsn]);

-        AIR_PRINT("VPM : %s\n", _sptag_vpm[sptag_rx.vpm]);

-        AIR_PRINT("SPN : %d\n", sptag_rx.spn);

-        AIR_PRINT("PRI : %d\n", sptag_rx.pri);

-        AIR_PRINT("CFI : %d\n", sptag_rx.cfi);

-        AIR_PRINT("VID : %d\n", sptag_rx.vid);

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doSptagEncode(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    AIR_STAG_TX_PARA_T sptag_tx = {0};

-    UI8_T buf[AIR_STAG_BUF_LEN] = {0};

-    UI32_T len = AIR_STAG_BUF_LEN;

-    char str[128] = {'\0'};

-    UI32_T data = 0;

-    AIR_STAG_MODE_T mode = AIR_STAG_MODE_LAST;

-

-    if (7 == argc)

-    {

-        if(_strcmp(argv[0],"mode=replace") == 0)

-            mode = AIR_STAG_MODE_REPLACE;

-        else if(_strcmp(argv[0],"mode=insert") == 0)

-            mode = AIR_STAG_MODE_INSERT;

-        else

-            printf("mode is wrong!!");

-

-        if(_strcmp(argv[1],"opc=portmap") == 0)

-            sptag_tx.opc = AIR_STAG_OPC_PORTMAP;

-        else if(_strcmp(argv[1],"opc=portid") == 0)

-            sptag_tx.opc = AIR_STAG_OPC_PORTID;

-        else if(_strcmp(argv[1],"opc=lookup") == 0)

-            sptag_tx.opc = AIR_STAG_OPC_LOOKUP;

-        else

-            printf("opc is wrong!!");

-

-        if(sscanf(argv[2],"dp=%x",&data) != -1)

-        {

-            sptag_tx.pbm = data;

-            AIR_PRINT("sptag_tx.pbm %x\n",sptag_tx.pbm);

-        }

-

-        if(_strcmp(argv[3],"vpm=untagged") == 0)

-            sptag_tx.vpm = AIR_STAG_VPM_UNTAG;

-        else if(_strcmp(argv[3],"vpm=8100") == 0)

-            sptag_tx.vpm = AIR_STAG_VPM_TPID_8100;

-        else if(_strcmp(argv[3],"vpm=88a8") == 0)

-            sptag_tx.vpm = AIR_STAG_VPM_TPID_88A8;

-        else

-            printf("vpm is wrong!!");

-

-        if(sscanf(argv[4],"pri=%d",&data) != -1)

-        {

-            sptag_tx.pri = data;

-            AIR_PRINT("sptag_tx.pri %d\n",sptag_tx.pri);

-        }

-

-        if(sscanf(argv[5],"cfi=%d",&data) != -1)

-        {

-            sptag_tx.cfi  = data;

-            AIR_PRINT("sptag_tx.cfi %d\n",sptag_tx.cfi);

-        }

-

-        if(sscanf(argv[6],"vid=%d",&data) != -1)

-        {

-            sptag_tx.vid = data;

-            AIR_PRINT("sptag_tx.vid %d\n",sptag_tx.vid);

-        }

-

-        ret = air_sptag_encodeTx(0,mode, &sptag_tx, (UI8_T *)&buf, &len);

-        if(AIR_E_OK == ret)

-        {

-            AIR_PRINT("SpTag encode sucess, returned len=%d\n", len);

-            AIR_PRINT("Encoded SpTag: %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]);

-        }

-        else

-        {

-            AIR_PRINT("SpTag encode fail\n");

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doSptag(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(sptagCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doL2Dump(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(l2DumpCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doMacAddr(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    AIR_MAC_ENTRY_T mt;

-    UI32_T fwd = 0;

-

-    memset(&mt, 0, sizeof(AIR_MAC_ENTRY_T));

-

-    if(0 == argc)

-    {

-        /* l2 clear mac */

-        ret = air_l2_clearMacAddr(0);

-        if(ret == AIR_E_OK)

-            AIR_PRINT("Clear MAC Address Table Done.\n");

-        else

-            AIR_PRINT("Clear MAC Address Table Fail.\n");

-    }

-    else if(3 == argc)

-    {

-        /* l2 del mac <mac(12'hex)> { vid <vid(0..4095)> | fid <fid(0..15)> } */

-        ret = _str2mac(argv[0], (C8_T *)mt.mac);

-        if(ret != AIR_E_OK)

-        {

-            AIR_PRINT("Unrecognized command.\n");

-            return ret;

-        }

-

-        /* check argument 1 */

-        if(FALSE == _strcmp(argv[1], "vid"))

-        {

-            /* get mac entry by MAC address & vid */

-            mt.cvid = _strtoul(argv[2], NULL, 0);

-            mt.flags |= AIR_L2_MAC_ENTRY_FLAGS_IVL;

-            AIR_PRINT("Get MAC Address:" MAC_STR " with vid:%u", MAC2STR(mt.mac), mt.cvid);

-        }

-        else if(FALSE == _strcmp(argv[1], "fid"))

-        {

-            /* get mac entry by MAC address & fid */

-            mt.fid = _strtoul(argv[2], NULL, 0);

-            AIR_PRINT("Get MAC Address:" MAC_STR " with fid:%u", MAC2STR(mt.mac), mt.fid);

-        }

-        else

-        {

-            AIR_PRINT("Unrecognized command.\n");

-            return AIR_E_BAD_PARAMETER;

-        }

-        ret = air_l2_delMacAddr(0, &mt);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT(" Done.\n");

-        }

-        else

-            AIR_PRINT("\n Fail!\n");

-    }

-    else if(7 == argc)

-    {

-        /* l2 add mac <static(0:dynamic,1:static)> <unauth(0:auth,1:unauth)> <mac(12'hex)> <portlist(uintlist)> [ vid <vid(0..4095)> | fid <fid(0..15)> ] <src_mac_forward=(0:default,1:cpu-include,2:cpu-exclude,3:cpu-only,4:drop)> */

-        if(argv[0])

-            mt.flags |= AIR_L2_MAC_ENTRY_FLAGS_STATIC;

-

-        if(argv[1])

-            mt.flags |= AIR_L2_MAC_ENTRY_FLAGS_UNAUTH;

-

-        ret = _str2mac(argv[2], (C8_T *)mt.mac);

-        if(ret != AIR_E_OK)

-        {

-            AIR_PRINT("Unrecognized command.\n");

-            return ret;

-        }

-

-        ret = _portListStr2Ary(argv[3], mt.port_bitmap, 1);

-        if(ret != AIR_E_OK)

-        {

-            AIR_PRINT("Unrecognized command.\n");

-            return ret;

-        }

-

-        /* check argument fid or vid */

-        if(FALSE == _strcmp(argv[4], "vid"))

-        {

-            /* get mac entry by MAC address & vid */

-            mt.cvid = _strtoul(argv[5], NULL, 0);

-            mt.flags |= AIR_L2_MAC_ENTRY_FLAGS_IVL;

-        }

-        else if(FALSE == _strcmp(argv[4], "fid"))

-        {

-            /* get mac entry by MAC address & fid */

-            mt.fid = _strtoul(argv[5], NULL, 0);

-        }

-        else

-        {

-            AIR_PRINT("Unrecognized command.\n");

-            return AIR_E_BAD_PARAMETER;

-        }

-        fwd = _strtoul(argv[6], NULL, 0);

-        if(0 == fwd)

-            mt.sa_fwd = AIR_L2_FWD_CTRL_DEFAULT;

-        else if(1 == fwd)

-            mt.sa_fwd = AIR_L2_FWD_CTRL_CPU_INCLUDE;

-        else if(2 == fwd)

-            mt.sa_fwd = AIR_L2_FWD_CTRL_CPU_EXCLUDE;

-        else if(3 == fwd)

-            mt.sa_fwd = AIR_L2_FWD_CTRL_CPU_ONLY;

-        else if(4 == fwd)

-            mt.sa_fwd = AIR_L2_FWD_CTRL_DROP;

-        else

-        {

-            AIR_PRINT("Unrecognized command.\n");

-            return AIR_E_BAD_PARAMETER;

-        }

-        ret = air_l2_addMacAddr(0, &mt);

-        AIR_PRINT("Add MAC Address:" MAC_STR, MAC2STR(mt.mac));

-        if(ret == AIR_E_OK)

-            AIR_PRINT(" Done.\n");

-        else

-            AIR_PRINT(" Fail.\n");

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-_printMacEntry(

-        AIR_MAC_ENTRY_T *mt,

-        UI32_T age_unit,

-        UI8_T count,

-        UI8_T title)

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    I32_T i = 0, j = 0;

-    UI8_T first = 0;

-    UI8_T find = 0;

-    if(title)

-    {

-        AIR_PRINT("%-6s%-15s%-5s%-5s%-5s%-10s%-10s%-6s\n",

-                "unit",

-                "mac",

-                "ivl",

-                "vid",

-                "fid",

-                "age-time",

-                "forward",

-                "port");

-        return ret;

-    }

-    for(i = 0; i < count; i++)

-    {

-        AIR_PRINT("%-6d", age_unit);

-        AIR_PRINT(MAC_STR, MAC2STR(mt[i].mac));

-        AIR_PRINT("...");

-        if(mt[i].flags & AIR_L2_MAC_ENTRY_FLAGS_IVL)

-        {

-            AIR_PRINT("%-3s..", "ivl");

-            AIR_PRINT("%-5d", mt[i].cvid);

-            AIR_PRINT("%-5s", ".....");

-        }

-        else

-        {

-            AIR_PRINT("%-3s..", "svl");

-            AIR_PRINT("%-5s", ".....");

-            AIR_PRINT("%-5d", mt[i].fid);

-        }

-        if(mt[i].flags & AIR_L2_MAC_ENTRY_FLAGS_STATIC)

-        {

-            AIR_PRINT("%-10s.", "static");

-        }

-        else

-        {

-            AIR_PRINT("%d sec..", mt[i].timer);

-        }

-        AIR_PRINT("%-10s", _air_mac_address_forward_control_string[mt[i].sa_fwd]);

-        first = 0;

-        find = 0;

-        for (j = (AIR_MAX_NUM_OF_PORTS - 1); j >= 0; j--)

-        {

-            if((mt[i].port_bitmap[0]) & (1 << j))

-            {

-                first = j;

-                find = 1;

-                break;

-            }

-        }

-        if(find)

-        {

-            for (j = 0; j < AIR_MAX_NUM_OF_PORTS; j++)

-            {

-                if((mt[i].port_bitmap[0]) & (1 << j))

-                {

-                    if(j == first)

-                        AIR_PRINT("%-2d", j);

-                    else

-                        AIR_PRINT("%-2d,", j);

-                }

-            }

-        }

-        else

-            AIR_PRINT("no dst port");

-        AIR_PRINT("\n");

-    }

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doGetMacAddr(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI8_T count = 0;

-    AIR_MAC_ENTRY_T * ptr_mt;

-

-    if(3 == argc)

-    {

-        ptr_mt = AIR_MALLOC(sizeof(AIR_MAC_ENTRY_T));

-        if (NULL == ptr_mt)

-        {

-            AIR_PRINT("***Error***, allocate memory fail\n");

-            return AIR_E_OTHERS;

-        }

-        memset(ptr_mt, 0, sizeof(AIR_MAC_ENTRY_T));

-        /* l2 get mac <mac(12'hex)> { vid <vid(0..4095)> | fid <fid(0..15)> } */

-        ret = _str2mac(argv[0], (C8_T *)ptr_mt->mac);

-        if(ret != AIR_E_OK)

-        {

-            AIR_PRINT("Unrecognized command.\n");

-            AIR_FREE(ptr_mt);

-            return ret;

-        }

-

-        /* check argument 1 */

-        if(FALSE == _strcmp(argv[1], "vid"))

-        {

-            /* get mac entry by MAC address & vid */

-            ptr_mt->cvid = _strtoul(argv[2], NULL, 0);

-            ptr_mt->flags |= AIR_L2_MAC_ENTRY_FLAGS_IVL;

-            AIR_PRINT("Get MAC Address:" MAC_STR " with vid:%u", MAC2STR(ptr_mt->mac), ptr_mt->cvid);

-        }

-        else if(FALSE == _strcmp(argv[1], "fid"))

-        {

-            /* get mac entry by MAC address & fid */

-            ptr_mt->fid = _strtoul(argv[2], NULL, 0);

-            AIR_PRINT("Get MAC Address:" MAC_STR " with fid:%u", MAC2STR(ptr_mt->mac), ptr_mt->fid);

-        }

-        else

-        {

-            AIR_PRINT("Unrecognized command.\n");

-            AIR_FREE(ptr_mt);

-            return AIR_E_BAD_PARAMETER;

-        }

-        ret = air_l2_getMacAddr(0, &count, ptr_mt);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT(" Done.\n");

-            _printMacEntry(ptr_mt, 0, 1, TRUE);

-            _printMacEntry(ptr_mt, 0, 1, FALSE);

-        }

-        else

-            AIR_PRINT("\n Not found!\n");

-        AIR_FREE(ptr_mt);

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doMacAddrAgeOut(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T time = 0;

-    if(0 == argc)

-    {

-        /* l2 get macAddrAgeOut */

-        ret = air_l2_getMacAddrAgeOut(0, &time);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Get MAC Address Age Out Time Done.\n");

-        }

-        else

-        {

-            AIR_PRINT("Get MAC Address Age Out Time Fail.\n");

-        }

-    }

-    else if(1 == argc)

-    {

-        /* l2 set macAddrAgeOut <time(1, 1000000)> */

-        time = _strtoul(argv[0], NULL, 0);

-        if(time < 1 || time > 1000000)

-        {

-            AIR_PRINT("Unrecognized command.\n");

-            return AIR_E_BAD_PARAMETER;

-        }

-        ret = air_l2_setMacAddrAgeOut(0, time);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Set MAC Address Age Out Time Done.\n");

-        }

-        else

-        {

-            AIR_PRINT("Set MAC Address Age Out Time Fail.\n");

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    if(ret == AIR_E_OK)

-    {

-        AIR_PRINT("MAC Address Age Out Time: %u seconds.\n", time);

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doDumpMacAddr(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    AIR_MAC_ENTRY_T *ptr_mt;

-    UI8_T count = 0;

-    UI32_T bucket_size = 0;

-    UI32_T total_count = 0;

-

-    if(0 != argc)

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-    /* get unit of aging time */

-    ret = air_l2_getMacBucketSize(0, &bucket_size);

-    if(ret != AIR_E_OK)

-    {

-        AIR_PRINT("Get MAC Age Time Fail!\n");

-        return ret;

-    }

-    ptr_mt = AIR_MALLOC(sizeof(AIR_MAC_ENTRY_T) * bucket_size);

-    if (NULL == ptr_mt)

-    {

-        AIR_PRINT("***Error***, allocate memory fail\n");

-        return AIR_E_OTHERS;

-    }

-    memset(ptr_mt, 0, sizeof(AIR_MAC_ENTRY_T) * bucket_size);

-    _printMacEntry(ptr_mt, 0, count, TRUE);

-    /* get 1st entry of MAC table */

-    ret = air_l2_getMacAddr(0, &count, ptr_mt);

-    switch(ret)

-    {

-        case AIR_E_ENTRY_NOT_FOUND:

-            AIR_FREE(ptr_mt);

-            AIR_PRINT("Not Found!\n");

-            return ret;

-        case AIR_E_TIMEOUT:

-            AIR_FREE(ptr_mt);

-            AIR_PRINT("Time Out!\n");

-            return ret;

-        case AIR_E_BAD_PARAMETER:

-            AIR_FREE(ptr_mt);

-            AIR_PRINT("Bad Parameter!\n");

-            return ret;

-        default:

-            break;

-    }

-    total_count += count;

-    _printMacEntry(ptr_mt, 0, count, FALSE);

-

-    /* get other entries of MAC table */

-    while(1)

-    {

-        memset(ptr_mt, 0, sizeof(AIR_MAC_ENTRY_T) * bucket_size);

-        ret = air_l2_getNextMacAddr(0, &count, ptr_mt);

-        if(AIR_E_OK != ret)

-        {

-            break;

-        }

-        total_count += count;

-        _printMacEntry(ptr_mt, 0, count, FALSE);

-    }

-    switch(ret)

-    {

-        case AIR_E_TIMEOUT:

-            AIR_PRINT("Time Out!\n");

-            break;

-        case AIR_E_BAD_PARAMETER:

-            AIR_PRINT("Bad Parameter!\n");

-            break;

-        default:

-            AIR_PRINT("Found %u %s\n", total_count, (total_count>1)?"entries":"entry");

-            break;

-    }

-    AIR_FREE(ptr_mt);

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doLagMember(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T portrunk_index = 0, member_index = 0, member_enable = 0, port_index = 0, i = 0;

-    AIR_LAG_PTGINFO_T  member;

-    memset(&member,0,sizeof(AIR_LAG_PTGINFO_T));

-

-    if(4 == argc)

-    {

-        /* lag set member <port trunk index> <member index> <member enable> <port_index>*/

-        portrunk_index  = _strtol(argv[0], NULL, 10);

-        member_index    = _strtol(argv[1], NULL, 10);

-        member_enable   = _strtol(argv[2], NULL, 10);

-        port_index      = _strtol(argv[3], NULL, 10);

-        ret = air_lag_setMember(0, portrunk_index, member_index, member_enable,port_index);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Set port trunk index %d member_index:%d member_enable:%d, port_index:%d ok.\n", portrunk_index, member_index, member_enable,port_index);

-        }

-        else

-        {

-            AIR_PRINT("Set port trunk index %d member_index:%d member_enable:%d, port_index:%d fail.\n", portrunk_index, member_index, member_enable,port_index);

-        }

-        memset(&member,0,sizeof(member));

-        air_lag_getMember(0, portrunk_index, &member);

-        if(! member.csr_gp_enable[0])

-        {

-            AIR_PRINT("\r\n!!!!!!!!!Port trunk index %d member_index:0 must be set,or else have taffic issues.\n", portrunk_index);

-        }

-    }

-    else if(1 == argc)

-    {

-        portrunk_index = _strtol(argv[0], NULL, 10);

-

-        /* lag get member <port> */

-        memset(&member,0,sizeof(member));

-        ret = air_lag_getMember(0, portrunk_index, &member);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Get port trunk %u member:\n", portrunk_index);

-            for(i = 0; i < AIR_LAG_MAX_MEM_NUM; i++)

-            {

-                if(member.csr_gp_enable[i])

-                    AIR_PRINT("port %d \r\n", member.csr_gp_port[i]);

-            }

-            AIR_PRINT("\r\n");

-        }

-        else

-        {

-            AIR_PRINT("Get port trunk:%u Member Fail.\n", portrunk_index);

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-

-static AIR_ERROR_NO_T

-doLagDstInfo(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    AIR_LAG_DISTINFO_T dstInfo;

-

-    memset(&dstInfo, 0, sizeof(AIR_LAG_DISTINFO_T));

-    if(7 == argc)

-    {

-        /* lag set dstInfo <sp> <sa> <da> <sip> <dip> <sport> <dport> */

-        dstInfo.sp = _strtol(argv[0], NULL, 10) & BIT(0);

-        dstInfo.sa = _strtol(argv[1], NULL, 10) & BIT(0);

-        dstInfo.da = _strtol(argv[2], NULL, 10) & BIT(0);

-        dstInfo.sip = _strtol(argv[3], NULL, 10) & BIT(0);

-        dstInfo.dip = _strtol(argv[4], NULL, 10) & BIT(0);

-        dstInfo.sport = _strtol(argv[5], NULL, 10) & BIT(0);

-        dstInfo.dport = _strtol(argv[6], NULL, 10) & BIT(0);

-        ret = air_lag_setDstInfo(0, dstInfo);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Set LAG packet distrubution.\n");

-        }

-        else

-        {

-            AIR_PRINT("Set LAG packet distrubution Fail.\n");

-        }

-    }

-    else if(0 == argc)

-    {

-        /* lag get dstInfo */

-        ret = air_lag_getDstInfo(0, &dstInfo);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Get LAG packet distrubution:\n");

-        }

-        else

-        {

-            AIR_PRINT("Get LAG packet distrubution Fail.\n");

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-    if(ret == AIR_E_OK)

-    {

-        AIR_PRINT("%-5s|%-5s|%-5s|%-5s|%-5s|%-5s|%-5s\n",

-                "SP", "SA", "DA", "SIP", "DIP", "SPORT", "DPORT");

-        AIR_PRINT("%-5s|%-5s|%-5s|%-5s|%-5s|%-5s|%-5s\n",

-                (dstInfo.sp)?"En":"Dis",

-                (dstInfo.sa)?"En":"Dis",

-                (dstInfo.da)?"En":"Dis",

-                (dstInfo.sip)?"En":"Dis",

-                (dstInfo.dip)?"En":"Dis",

-                (dstInfo.sport)?"En":"Dis",

-                (dstInfo.dport)?"En":"Dis");

-    }

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doLagHashtype(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T hashtype = 0;

-

-    if(1 == argc)

-    {

-        hashtype = _strtol(argv[0], NULL, 10);

-        ret = air_lag_sethashtype(0, hashtype);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Set LAG hashtype Ok.\n");

-        }

-        else

-        {

-            AIR_PRINT("Set LAG hashtype Fail.\n");

-        }

-    }

-    else if(0 == argc)

-    {

-        /* lag get dstInfo */

-        ret = air_lag_gethashtype(0, &hashtype);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Get LAG hashtype:\n");

-        }

-        else

-        {

-            AIR_PRINT("Get LLAG hashtype Fail.\n");

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-    if(ret == AIR_E_OK)

-    {

-        switch (hashtype)

-        {

-            case 0:

-                AIR_PRINT("hashtype:crc32lsb.\n");

-                break;

-            case 1:

-                AIR_PRINT("hashtype:crc32msb.\n");

-                break;

-            case 2:

-                AIR_PRINT("hashtype:crc16.\n");

-                break;

-            case 3:

-                AIR_PRINT("hashtype:xor4.\n");

-                break;

-            default:

-                AIR_PRINT("wrong hashtype:%d.\n",hashtype);

-        }

-

-    }

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doLagPtseed(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T ptseed = 0;

-

-    if(1 == argc)

-    {

-        ptseed = _strtol(argv[0], NULL, 16);

-        ret = air_lag_setPTSeed(0, ptseed);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Set LAG Port Seed:%x(hex) ok\n",ptseed);

-        }

-        else

-        {

-            AIR_PRINT("Set LAG Port Seed:%x(hex) fail\n",ptseed);

-        }

-    }

-    else if(0 == argc)

-    {

-        /* lag get seed */

-        ret = air_lag_getPTSeed(0, &ptseed);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Get port trunk seed: %x(hex)\n",ptseed);

-        }

-        else

-        {

-            AIR_PRINT("Get port trunk seed Fail.\n");

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doLagSpsel(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0;

-    UI32_T state = 0;

-

-    if(1 == argc)

-    {

-        /* lag set spsel <state> */

-        state = _strtol(argv[0], NULL, 10);

-        ret = air_lag_setSpSel(0,state);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Set source port compare function:%s.\n", (state)?"Enabled":"Disabled");

-        }

-        else

-        {

-            AIR_PRINT("Set source port compare function Fail.\n");

-        }

-    }

-    else if(0 == argc)

-    {

-        /* lag get spsel*/

-        ret = air_lag_getSpSel(0, &state);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Get source port compare function:%s.\n", (state)?"Enabled":"Disabled");

-        }

-        else

-        {

-            AIR_PRINT("Get source port compare function Fail.\n");

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-

-static AIR_ERROR_NO_T

-doLagState(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0;

-    UI32_T state = 0;

-

-    if(1 == argc)

-    {

-        /* lag set state <state> */

-        state = _strtol(argv[0], NULL, 10);

-        ret = air_lag_set_ptgc_state(0,state);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Set LAG Port Trunk State:%s.\n", (state)?"Enabled":"Disabled");

-        }

-        else

-        {

-            AIR_PRINT("Set LAG Port Trunk State Fail.\n");

-        }

-    }

-    else if(0 == argc)

-    {

-        /* lag get state*/

-        ret = air_lag_get_ptgc_state(0, &state);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Get LAG Port Trunk State:%s.\n", (state)?"Enabled":"Disabled");

-        }

-        else

-        {

-            AIR_PRINT("Get LAG Port Trunk State Fail.\n");

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doLagGet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(lagGetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doLagSet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(lagSetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doLag(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(lagCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doStpPortstate(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0;

-    UI32_T fid = 0;

-    UI32_T state = 0;

-

-    port = _strtol(argv[0], NULL, 10);

-    fid = _strtol(argv[1], NULL, 10);

-    if(3 == argc)

-    {

-        /* stp set portstate <port> <fid(0..15)> <state> */

-        state = _strtol(argv[2], NULL, 10);

-        ret = air_stp_setPortstate(0, port, fid, state);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Set STP Port:%u FID:%u State:", port, fid);

-            switch(state)

-            {

-                case AIR_STP_STATE_DISABLE:

-                    AIR_PRINT("Disable(STP) / Discard(RSTP).\n");

-                    break;

-                case AIR_STP_STATE_LISTEN:

-                    AIR_PRINT("Listening(STP) / Discard(RSTP).\n");

-                    break;

-                case AIR_STP_STATE_LEARN:

-                    AIR_PRINT("Learning(STP) / Learning(RSTP).\n");

-                    break;

-                case AIR_STP_STATE_FORWARD:

-                    AIR_PRINT("Forwarding(STP) / Forwarding(RSTP).\n");

-                    break;

-                default:

-                    break;

-            }

-        }

-        else

-        {

-            AIR_PRINT("Set STP Port:%u FID:%u State Fail.", port, fid);

-        }

-    }

-    else if(2 == argc)

-    {

-        /* stp get portstate <port> <fid(0..15)> */

-        ret = air_stp_getPortstate(0, port, fid, &state);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Get STP Port:%u FID:%u State:", port, fid);

-            switch(state)

-            {

-                case AIR_STP_STATE_DISABLE:

-                    AIR_PRINT("Disable(STP) / Discard(RSTP).\n");

-                    break;

-                case AIR_STP_STATE_LISTEN:

-                    AIR_PRINT("Listening(STP) / Discard(RSTP).\n");

-                    break;

-                case AIR_STP_STATE_LEARN:

-                    AIR_PRINT("Learning(STP) / Learning(RSTP).\n");

-                    break;

-                case AIR_STP_STATE_FORWARD:

-                    AIR_PRINT("Forwarding(STP) / Forwarding(RSTP).\n");

-                    break;

-                default:

-                    break;

-            }

-        }

-        else

-        {

-            AIR_PRINT("Get STP Port:%u FID:%u State Fail.", port, fid);

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doStpGet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(stpGetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doStpSet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(stpSetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doStp(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(stpCmds, argc, argv);

-}

-

-static void

-_mir_printPortList(UI32_T * mt)

-{

-    I8_T j = 0;

-    UI8_T first = 0;

-    UI8_T find = 0;

-    for(j = (AIR_MAX_NUM_OF_PORTS - 1); j >= 0; j--)

-    {

-        if((*mt) & (1 << j))

-        {

-            first = j;

-            find = 1;

-            break;

-        }

-    }

-    if(find)

-    {

-        for(j = 0; j < AIR_MAX_NUM_OF_PORTS; j++)

-        {

-            if((*mt) & (1 << j))

-            {

-                if(j == first)

-                    AIR_PRINT("%-2d", j);

-                else

-                    AIR_PRINT("%-2d,", j);

-            }

-        }

-    }

-    else

-        AIR_PRINT("NULL");

-    AIR_PRINT("\n");

-}

-

-static void

-_mir_printSrcPortList(

-    const UI32_T         unit,

-    const UI32_T         sessionid)

-{

-

-    I8_T i = 0;

-    AIR_MIR_SESSION_T   session;

-    AIR_PORT_BITMAP_T txPbm = {0}, rxPbm = {0};

-

-    for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)

-    {

-         memset(&session, 0, sizeof(session));

-         session.src_port = i;

-         air_mir_getMirrorPort(unit, sessionid, &session);

-

-         if(session.flags & AIR_MIR_SESSION_FLAGS_DIR_TX)

-         {

-            txPbm[0] |= (1 << i);

-         }

-         if(session.flags & AIR_MIR_SESSION_FLAGS_DIR_RX)

-         {

-            rxPbm[0] |= (1 << i);

-         }

-    }

-    AIR_PRINT("Src PortList\n");

-    AIR_PRINT(" - Rx portlist = ");

-    _mir_printPortList(rxPbm);

-    AIR_PRINT(" - Tx portlist = ");

-    _mir_printPortList(txPbm);

-}

-

-static void

-_mir_printSession(

-    const UI32_T            unit,

-    const UI32_T            session_id,

-    const AIR_MIR_SESSION_T *ptr_session)

-{

-

-    AIR_PRINT("Session id: %d\n", session_id);

-    AIR_PRINT("State: %s \n", (ptr_session->flags & AIR_MIR_SESSION_FLAGS_ENABLE)? "enable": "disable");

-    AIR_PRINT("Tx tag: %s \n", (ptr_session->flags & AIR_MIR_SESSION_FLAGS_TX_TAG_OBEY_CFG)? "On": "Off");

-    AIR_PRINT("Dst port: %d \n", ptr_session->dst_port);

-    _mir_printSrcPortList(unit,session_id);

-}

-

-static AIR_ERROR_NO_T

-doMirrorGetSid(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T      rc = AIR_E_OK;

-    UI32_T              session_id = 0;

-    AIR_MIR_SESSION_T   session = {0};

-    I8_T i = 0;

-

-    session_id = _strtoul(argv[0], NULL, 0);

-    rc = air_mir_getSession(0, session_id, &session);

-    if (AIR_E_OK != rc)

-    {

-        AIR_PRINT("***Error***, get mirror session fail\n");

-        return rc;

-    }

-    /* print session information */

-    if(session.dst_port == AIR_PORT_INVALID)

-    {

-        AIR_PRINT("Session id %d not found\n", session_id);

-    }

-    else

-    {

-        _mir_printSession(0, session_id, &session);

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doMirrorDelSid(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T      rc = AIR_E_OK;

-    UI32_T              session_id = 0;

-    AIR_MIR_SESSION_T   session = {0};

-    UI8_T i = 0;

-

-    session_id = _strtoul(argv[0], NULL, 0);

-    rc = air_mir_delSession(0, session_id);

-    if (AIR_E_OK != rc)

-    {

-        AIR_PRINT("***Error***, del mirror session fail\n");

-        return rc;

-    }

-    for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)

-    {

-        session.src_port = i;

-        rc = air_mir_setMirrorPort(0, session_id, &session);

-        if (AIR_E_OK != rc)

-        {

-            AIR_PRINT("***Error***,port=%u error\n", i);

-            return rc;

-        }

-    }

-    if (rc != AIR_E_OK)

-    {

-        AIR_PRINT("***Error***, delete mirror session fail\n");

-    }

-    else

-        AIR_PRINT("***OK***, delete mirror session success\n");

-

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doMirrorAddRlist(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T      rc = AIR_E_OK;

-    UI32_T              session_id = 0;

-    AIR_MIR_SESSION_T   session = {0};

-    AIR_PORT_BITMAP_T rxPbm = {0};

-    UI8_T i = 0;

-

-    /*mirror add session-rlist <sid(0..3)> <list(UINTLIST)>*/

-    session_id = _strtoul(argv[0], NULL, 0);

-    rc = _portListStr2Ary(argv[1], rxPbm, 1);

-    if(rc != AIR_E_OK)

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return rc;

-    }

-    if(!rxPbm[0])

-    {

-        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)

-        {

-            memset(&session, 0, sizeof(AIR_MIR_SESSION_T));

-            session.src_port = i;

-            rc = air_mir_getMirrorPort(0, session_id, &session);

-            if (AIR_E_OK != rc)

-            {

-                AIR_PRINT("***Error***,get port=%u error\n", i);

-                return rc;

-            }

-

-            session.flags &= ~AIR_MIR_SESSION_FLAGS_DIR_RX;

-            session.src_port = i;

-            rc = air_mir_setMirrorPort(0, session_id, &session);

-            if (AIR_E_OK != rc)

-            {

-                AIR_PRINT("***Error***,set rx port=%u error\n", i);

-                return rc;

-            }

-        }

-    }

-    else

-    {

-        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)

-        {

-            if(rxPbm[0] & (1 << i))

-            {

-                memset(&session, 0, sizeof(AIR_MIR_SESSION_T));

-                session.src_port = i;

-                rc = air_mir_getMirrorPort(0, session_id, &session);

-                if (AIR_E_OK != rc)

-                {

-                    AIR_PRINT("***Error***,get port=%u error\n", i);

-                    return rc;

-                }

-

-                session.flags |= AIR_MIR_SESSION_FLAGS_DIR_RX;

-                session.src_port = i;

-                rc = air_mir_setMirrorPort(0, session_id, &session);

-                if (AIR_E_OK != rc)

-                {

-                    AIR_PRINT("***Error***,port=%u error\n", i);

-                    return rc;

-                }

-            }

-        }

-    }

-

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doMirrorAddTlist(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T      rc = AIR_E_OK;

-    UI32_T              session_id = 0;

-    AIR_MIR_SESSION_T   session = {0};

-    AIR_PORT_BITMAP_T txPbm = {0};

-    UI8_T i = 0;

-

-    /*mirror add session-tlist <sid(0..3)> <list(UINTLIST)>*/

-    session_id = _strtoul(argv[0], NULL, 0);

-    rc = _portListStr2Ary(argv[1], txPbm, 1);

-    if(rc != AIR_E_OK)

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return rc;

-    }

-    if(!txPbm[0])

-    {

-        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)

-        {

-            memset(&session, 0, sizeof(AIR_MIR_SESSION_T));

-            session.src_port = i;

-            rc = air_mir_getMirrorPort(0, session_id, &session);

-            if (AIR_E_OK != rc)

-            {

-                AIR_PRINT("***Error***,get port=%u error\n", i);

-                return rc;

-            }

-

-            session.flags &= ~AIR_MIR_SESSION_FLAGS_DIR_TX;

-            session.src_port = i;

-            rc = air_mir_setMirrorPort(0, session_id, &session);

-            if (AIR_E_OK != rc)

-            {

-                AIR_PRINT("***Error***,set rx port=%u error\n", i);

-                return rc;

-            }

-        }

-    }

-    else

-    {

-        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)

-        {

-            if(txPbm[0] & (1 << i))

-            {

-                memset(&session, 0, sizeof(AIR_MIR_SESSION_T));

-                session.src_port = i;

-                rc = air_mir_getMirrorPort(0, session_id, &session);

-                if (AIR_E_OK != rc)

-                {

-                    AIR_PRINT("***Error***,get port=%u error\n", i);

-                    return rc;

-                }

-

-                session.flags |= AIR_MIR_SESSION_FLAGS_DIR_TX;

-                session.src_port = i;

-                rc = air_mir_setMirrorPort(0, session_id, &session);

-                if (AIR_E_OK != rc)

-                {

-                    AIR_PRINT("***Error***,port=%u error\n", i);

-                    return rc;

-                }

-            }

-        }

-    }

-

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doMirrorSetSessionEnable(

-    UI32_T argc,

-    C8_T *argv[])

-

-{

-    AIR_ERROR_NO_T      rc = AIR_E_OK;

-    UI32_T              session_id = 0;

-    UI32_T              enable = 0;

-    BOOL_T              tmp_en = FALSE;

-

-    /*mirror set session-enable <sid(0..3)> <state(1:En,0:Dis)>*/

-    session_id = _strtoul(argv[0], NULL, 0);

-    enable = _strtoul(argv[1], NULL, 0);

-    if(enable)

-        tmp_en = TRUE;

-    /* set port mirror state */

-    rc = air_mir_setSessionAdminMode(0, session_id, tmp_en);

-    if(AIR_E_OK!=rc)

-    {

-        AIR_PRINT("***Error***\n");

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doMirrorSetSession(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T      rc = AIR_E_OK;

-    UI32_T              session_id = 0;

-    UI32_T              dst_port = 0;

-    UI8_T               enable = 0;

-    UI8_T               tag_en = 0;

-    UI8_T               dir = 0;

-    AIR_MIR_SESSION_T  session = {0};

-    AIR_PORT_BITMAP_T   rxPbm = {0};

-    I8_T               i = 0;

-

-    /*mirror set session <sid(0..3)> <dst_port(UINT)> <state(1:En,0:Dis)> <tag(1:on, 0:off)> <list(UINTLIST)> <dir(0:none,1:tx,2:rx,3:both)>*/

-    session_id = _strtoul(argv[0], NULL, 0);

-    dst_port = _strtoul(argv[1], NULL, 0);

-    AIR_PRINT("session id %d dst port %d.\n", session_id, dst_port);

-    session.dst_port = dst_port;

-    enable = _strtoul(argv[2], NULL, 0);

-    if(enable)

-    {

-        session.flags |= AIR_MIR_SESSION_FLAGS_ENABLE;

-    }

-    else

-    {

-        session.flags &= ~AIR_MIR_SESSION_FLAGS_ENABLE;

-    }

-    tag_en = _strtoul(argv[3], NULL, 0);

-    if(tag_en)

-    {

-        session.flags |= AIR_MIR_SESSION_FLAGS_TX_TAG_OBEY_CFG;

-    }

-    else

-    {

-        session.flags &= ~AIR_MIR_SESSION_FLAGS_TX_TAG_OBEY_CFG;

-    }

-    rc = _portListStr2Ary(argv[4], rxPbm, 1);

-    if(rc != AIR_E_OK)

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return rc;

-    }

-    AIR_PRINT("pbm %x.\n", rxPbm);

-    dir = _strtoul(argv[5], NULL, 0);

-    if(dir == 1)

-    {

-        session.flags |= AIR_MIR_SESSION_FLAGS_DIR_TX;

-    }

-    else if(dir == 2)

-    {

-        session.flags |= AIR_MIR_SESSION_FLAGS_DIR_RX;

-    }

-    else if(dir == 3)

-    {

-        session.flags |= AIR_MIR_SESSION_FLAGS_DIR_TX;

-        session.flags |= AIR_MIR_SESSION_FLAGS_DIR_RX;

-    }

-    else if (!dir)

-    {

-        session.flags &= ~AIR_MIR_SESSION_FLAGS_DIR_TX;

-        session.flags &= ~AIR_MIR_SESSION_FLAGS_DIR_RX;

-    }

-    else

-    {

-        return AIR_E_BAD_PARAMETER;

-    }

-    for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)

-    {

-        if(rxPbm[0] & (1 << i))

-        {

-            session.src_port = i;

-            /* set port mirror session */

-            rc = air_mir_addSession(0, session_id, &session);

-

-            if(AIR_E_OK!=rc)

-            {

-                AIR_PRINT("***Error***,dst-port=%u, src-port=%u error\n", session.dst_port, session.src_port);

-                return rc;

-            }

-            else

-                AIR_PRINT("add session %d,dst-port=%u, src-port=%u\n", session_id, session.dst_port, session.src_port);

-        }

-    }

-

-    return rc;

-}

-

-

-

-static AIR_ERROR_NO_T

-doMirrorSet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(mirrorSetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doMirrorAdd(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(mirrorAddCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doMirrorGet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(mirrorGetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doMirrorDel(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(mirrorDelCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doMirror(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(mirrorCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doMibClearPort(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0;

-

-    if(1 == argc)

-    {

-        /* mib clear port */

-        port = _strtoul(argv[0], NULL, 0);

-        ret = air_mib_clear_by_port(0,port);

-        AIR_PRINT("Clear port %d mib stats",port);

-        if(AIR_E_OK == ret)

-        {

-            AIR_PRINT("Done.\n");

-        }

-        else

-        {

-            AIR_PRINT("Fail.\n");

-        }

-    }

-    else if(0 == argc)

-    {

-        /*restart mib counter*/

-        air_mib_clear(0);

-        AIR_PRINT("Clear all mib stats",port);

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doMibClearAcl(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0;

-

-    if(0 == argc)

-    {

-        /* mib clear acl */

-        ret = air_mib_clearAclEvent(0);

-        AIR_PRINT("Clear ACL Event Counter ");

-        if(AIR_E_OK == ret)

-        {

-            AIR_PRINT("Done.\n");

-        }

-        else

-        {

-            AIR_PRINT("Fail.\n");

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doMibGetPort(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0;

-    UI32_T tmp32 = 0xffffffff;

-    AIR_MIB_CNT_RX_T rx_mib = {0};

-    AIR_MIB_CNT_TX_T tx_mib = {0};

-

-    port = _strtoul(argv[0], NULL, 0);

-    if(1 == argc)

-    {

-        /* mib get <port(0..6)> */

-        ret = air_mib_get(0, port, &rx_mib, &tx_mib);

-        AIR_PRINT("Get MIB Counter of Port %u ", port);

-        if(AIR_E_OK == ret)

-        {

-            AIR_PRINT("Done.\n");

-            AIR_PRINT("RX Drop Packet                    : %u\n", rx_mib.RDPC);

-            AIR_PRINT("RX filtering Packet               : %u\n", rx_mib.RFPC);

-            AIR_PRINT("RX Unicast Packet                 : %u\n", rx_mib.RUPC);

-            AIR_PRINT("RX Multicast Packet               : %u\n", rx_mib.RMPC);

-            AIR_PRINT("RX Broadcast Packet               : %u\n", rx_mib.RBPC);

-            AIR_PRINT("RX Alignment Error Packet         : %u\n", rx_mib.RAEPC);

-            AIR_PRINT("RX CRC Packet                     : %u\n", rx_mib.RCEPC);

-            AIR_PRINT("RX Undersize Packet               : %u\n", rx_mib.RUSPC);

-            AIR_PRINT("RX Fragment Error Packet          : %u\n", rx_mib.RFEPC);

-            AIR_PRINT("RX Oversize Packet                : %u\n", rx_mib.ROSPC);

-            AIR_PRINT("RX Jabber Error Packet            : %u\n", rx_mib.RJEPC);

-            AIR_PRINT("RX Pause Packet                   : %u\n", rx_mib.RPPC);

-            AIR_PRINT("RX Packet Length 64 bytes         : %u\n", rx_mib.RL64PC);

-            AIR_PRINT("RX Packet Length 65 ~ 127 bytes   : %u\n", rx_mib.RL65PC);

-            AIR_PRINT("RX Packet Length 128 ~ 255 bytes  : %u\n", rx_mib.RL128PC);

-            AIR_PRINT("RX Packet Length 256 ~ 511 bytes  : %u\n", rx_mib.RL256PC);

-            AIR_PRINT("RX Packet Length 512 ~ 1023 bytes : %u\n", rx_mib.RL512PC);

-            AIR_PRINT("RX Packet Length 1024 ~ 1518 bytes: %u\n", rx_mib.RL1024PC);

-            AIR_PRINT("RX Packet Length 1519 ~ max bytes : %u\n", rx_mib.RL1519PC);

-            AIR_PRINT("RX_CTRL Drop Packet               : %u\n", rx_mib.RCDPC);

-            AIR_PRINT("RX Ingress Drop Packet            : %u\n", rx_mib.RIDPC);

-            AIR_PRINT("RX ARL Drop Packet                : %u\n", rx_mib.RADPC);

-            AIR_PRINT("FLow Control Drop Packet          : %u\n", rx_mib.FCDPC);

-            AIR_PRINT("WRED Drop Packtet                 : %u\n", rx_mib.WRDPC);

-            AIR_PRINT("Mirror Drop Packet                : %u\n", rx_mib.MRDPC);

-            AIR_PRINT("RX  sFlow Sampling Packet         : %u\n", rx_mib.SFSPC);

-            AIR_PRINT("Rx sFlow Total Packet             : %u\n", rx_mib.SFTPC);

-            AIR_PRINT("Port Control Drop Packet          : %u\n", rx_mib.RXC_DPC);

-            AIR_PRINT("RX Octets good or bad packtes l32 : %u\n", (UI32_T)(rx_mib.ROC & tmp32));

-            AIR_PRINT("RX Octets good or bad packtes h32 : %u\n", (UI32_T)((rx_mib.ROC >> 32) & tmp32));

-            AIR_PRINT("RX Octets bad packets l32         : %u\n", (UI32_T)(rx_mib.ROC2 & tmp32));

-            AIR_PRINT("RX Octets bad packets h32         : %u\n", (UI32_T)((rx_mib.ROC2 >> 32) & tmp32));

-            AIR_PRINT("\n");

-            AIR_PRINT("TX Drop Packet                    : %u\n", tx_mib.TDPC);

-            AIR_PRINT("TX CRC Packet                     : %u\n", tx_mib.TCRC);

-            AIR_PRINT("TX Unicast Packet                 : %u\n", tx_mib.TUPC);

-            AIR_PRINT("TX Multicast Packet               : %u\n", tx_mib.TMPC);

-            AIR_PRINT("TX Broadcast Packet               : %u\n", tx_mib.TBPC);

-            AIR_PRINT("TX Collision Event Count          : %u\n", tx_mib.TCEC);

-            AIR_PRINT("TX Single Collision Event Count   : %u\n", tx_mib.TSCEC);

-            AIR_PRINT("TX Multiple Conllision Event Count: %u\n", tx_mib.TMCEC);

-            AIR_PRINT("TX Deferred Event Count           : %u\n", tx_mib.TDEC);

-            AIR_PRINT("TX Late Collision Event Count     : %u\n", tx_mib.TLCEC);

-            AIR_PRINT("TX Excessive Collision Event Count: %u\n", tx_mib.TXCEC);

-            AIR_PRINT("TX Pause Packet                   : %u\n", tx_mib.TPPC);

-            AIR_PRINT("TX Packet Length 64 bytes         : %u\n", tx_mib.TL64PC);

-            AIR_PRINT("TX Packet Length 65 ~ 127 bytes   : %u\n", tx_mib.TL65PC);

-            AIR_PRINT("TX Packet Length 128 ~ 255 bytes  : %u\n", tx_mib.TL128PC);

-            AIR_PRINT("TX Packet Length 256 ~ 511 bytes  : %u\n", tx_mib.TL256PC);

-            AIR_PRINT("TX Packet Length 512 ~ 1023 bytes : %u\n", tx_mib.TL512PC);

-            AIR_PRINT("TX Packet Length 1024 ~ 1518 bytes: %u\n", tx_mib.TL1024PC);

-            AIR_PRINT("TX Packet Length 1519 ~ max bytes : %u\n", tx_mib.TL1519PC);

-            AIR_PRINT("TX Oversize Drop Packet           : %u\n", tx_mib.TODPC);

-            AIR_PRINT("TX Octets good or bad packtes l32 : %u\n", (UI32_T)(tx_mib.TOC & tmp32));

-            AIR_PRINT("TX Octets good or bad packtes h32 : %u\n", (UI32_T)((tx_mib.TOC >> 32) & tmp32));

-            AIR_PRINT("TX Octets bad packets l32         : %u\n", (UI32_T)(tx_mib.TOC2 & tmp32));

-            AIR_PRINT("TX Octets bad packets h32         : %u\n", (UI32_T)((tx_mib.TOC2 >> 32) & tmp32));

-        }

-        else

-        {

-            AIR_PRINT("Fail.\n");

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doMibGetAcl(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T event = 0;

-    UI32_T cnt = 0;

-

-    if(1 == argc)

-    {

-        /* mib get acl <event(0..7)> */

-        event = _strtoul(argv[0], NULL, 0);

-        ret = air_mib_getAclEvent(0, event, &cnt);

-        AIR_PRINT("Get counter of ACL event %u ", event);

-        if(AIR_E_OK == ret)

-        {

-            AIR_PRINT("Done.\n");

-            AIR_PRINT("ACL Event Counter:%u\n", cnt);

-        }

-        else

-        {

-            AIR_PRINT("Fail.\n");

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doMibClear(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(mibClearCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doMibGet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(mibGetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doMib(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(mibCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doQosRateLimitExMngFrm(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T dir = 0;

-    BOOL_T enable = FALSE;

-

-    dir = _strtoul(argv[0], NULL, 0);

-    if(2 == argc)

-    {

-        if(dir == 0)

-            dir = AIR_QOS_RATE_DIR_EGRESS;

-        else if(dir == 1)

-            dir = AIR_QOS_RATE_DIR_INGRESS;

-        else

-        {

-            AIR_PRINT("Unrecognized command.\n");

-            ret = AIR_E_BAD_PARAMETER;

-            return ret;

-        }

-        enable = _strtoul(argv[1], NULL, 0);

-        ret = air_qos_setRateLimitExMngFrm(0, dir, enable);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Success.\n");

-            AIR_PRINT("Set %s Rate Limit Control %s management frame.\n",

-                    (AIR_QOS_RATE_DIR_INGRESS == dir)?"Ingress":"Egress",

-                    (TRUE == enable)?"exclude":"include");

-        }

-        else

-        {

-            AIR_PRINT("Fail.\n");

-            return ret;

-        }

-    }

-    else if(0 == argc)

-    {

-        dir = AIR_QOS_RATE_DIR_EGRESS;

-        ret = air_qos_getRateLimitExMngFrm(0, dir, &enable);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Success.\n");

-            AIR_PRINT("Get %s Rate Limit Control %s management frame.\n",

-                    (AIR_QOS_RATE_DIR_INGRESS == dir)?"Ingress":"Egress",

-                    (TRUE == enable)?"exclude":"include");

-        }

-        else

-        {

-            AIR_PRINT("Fail.\n");

-            return ret;

-        }

-        dir = AIR_QOS_RATE_DIR_INGRESS;

-        ret = air_qos_getRateLimitExMngFrm(0, dir, &enable);

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT("Success.\n");

-            AIR_PRINT("Get %s Rate Limit Control %s management frame.\n",

-                    (AIR_QOS_RATE_DIR_INGRESS == dir)?"Ingress":"Egress",

-                    (TRUE == enable)?"exclude":"include");

-        }

-        else

-        {

-            AIR_PRINT("Fail.\n");

-            return ret;

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-

-}

-

-static AIR_ERROR_NO_T

-doQosPortPriority(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    AIR_PORT_BITMAP_T portlist = {0};

-    UI32_T priority = 0;

-    UI8_T i = 0;

-

-    ret = _portListStr2Ary(argv[0], portlist, 1);

-    if(ret != AIR_E_OK)

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return ret;

-    }

-    if(2 == argc)

-    {

-        priority = _strtoul(argv[1], NULL, 0);

-        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)

-        {

-            if(portlist[0] & (1 << i))

-            {

-                ret = air_qos_setPortPriority(0, i, priority);

-                if(ret == AIR_E_OK)

-                {

-                    AIR_PRINT("Set Port%02d port based priority %d Success.\n", i, priority);

-                }

-                else

-                {

-                    AIR_PRINT("Set Port%02d port based priority %d Fail.\n", i, priority);

-                }

-            }

-        }

-    }

-    else if(1 == argc)

-    {

-        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)

-        {

-            if(portlist[0] & (1 << i))

-            {

-                ret = air_qos_getPortPriority(0, i, &priority);

-                if(ret == AIR_E_OK)

-                {

-                    AIR_PRINT("Get Port%d port based priority %d.\n", i, priority);

-                }

-                else

-                {

-                    AIR_PRINT("Get Port%d port based priority Fail.\n", i);

-                }

-            }

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doQosRateLimit(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    AIR_PORT_BITMAP_T portlist = {0};

-    AIR_QOS_RATE_LIMIT_CFG_T rl = {0};

-    UI8_T i = 0;

-

-    ret = _portListStr2Ary(argv[0], portlist, 1);

-    if(ret != AIR_E_OK)

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return ret;

-    }

-    if(5 == argc)

-    {

-        rl.ingress_cir = _strtoul(argv[1], NULL, 0);

-        rl.ingress_cbs = _strtoul(argv[2], NULL, 0);

-        rl.egress_cir = _strtoul(argv[3], NULL, 0);

-        rl.egress_cbs = _strtoul(argv[4], NULL, 0);

-        rl.flags |= AIR_QOS_RATE_LIMIT_CFG_FLAGS_ENABLE_INGRESS;

-        rl.flags |= AIR_QOS_RATE_LIMIT_CFG_FLAGS_ENABLE_EGRESS;

-        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)

-        {

-            if(portlist[0] & (1 << i))

-            {

-                ret = air_qos_setRateLimit(0, i, &rl);

-                if(ret == AIR_E_OK)

-                {

-                    AIR_PRINT("Set Port%02d Ingress CIR %d CBS %d Egress CIR %d CBS %d Success.\n", i, rl.ingress_cir, rl.ingress_cbs, rl.egress_cir, rl.egress_cbs);

-                }

-                else

-                {

-                    AIR_PRINT("Set Port%02d Ingress CIR %d CBS %d Egress CIR %d CBS %d Fail.\n", i, rl.ingress_cir, rl.ingress_cbs, rl.egress_cir, rl.egress_cbs);

-                }

-            }

-        }

-    }

-    else if(1 == argc)

-    {

-        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)

-        {

-            if(portlist[0] & (1 << i))

-            {

-                ret = air_qos_getRateLimit(0, i, &rl);

-                if(ret == AIR_E_OK)

-                {

-                    AIR_PRINT("Get Port%02d Ingress CIR %d CBS %d Egress CIR %d CBS %d\n", i, rl.ingress_cir, rl.ingress_cbs, rl.egress_cir, rl.egress_cbs);

-                }

-                else

-                {

-                    AIR_PRINT("Get Port%02d Rate Info Fail.\n", i);

-                }

-            }

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doQosRateLimitEnable(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    AIR_PORT_BITMAP_T portlist = {0};

-    C8_T sten[2][10] = {"Disable", "Enable"};

-    C8_T stdir[2][10] = {"Egress", "Ingress"};

-    UI32_T dir = 0, en = 0;

-    AIR_QOS_RATE_DIR_T tmp_dir = AIR_QOS_RATE_DIR_LAST;

-    BOOL_T state = FALSE;

-    UI8_T i = 0;

-

-    ret = _portListStr2Ary(argv[0], portlist, 1);

-    if(ret != AIR_E_OK)

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return ret;

-    }

-    if(3 == argc)

-    {

-        dir = _strtoul(argv[1], NULL, 0);

-        en = _strtoul(argv[2], NULL, 0);

-        if(dir == 0)

-            tmp_dir = AIR_QOS_RATE_DIR_EGRESS;

-        else if(dir == 1)

-            tmp_dir = AIR_QOS_RATE_DIR_INGRESS;

-        else

-        {

-            AIR_PRINT("Unrecognized command.\n");

-            return AIR_E_BAD_PARAMETER;

-        }

-        if(en)

-            state= TRUE;

-        else

-            state = FALSE;

-

-        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)

-        {

-            if(portlist[0] & (1 << i))

-            {

-                ret = air_qos_setRateLimitEnable(0, i, tmp_dir, state);

-                if(AIR_E_OK == ret)

-                {

-                    AIR_PRINT("Set Port%02d %s rate %s Success.\n", i, stdir[dir], sten[en]);

-                }

-                else

-                {

-                    AIR_PRINT("Set Port%02d %s rate %s Fail.\n", i, stdir[dir], sten[en]);

-                }

-            }

-        }

-    }

-    else if(1 == argc)

-    {

-        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)

-        {

-            if(portlist[0] & (1 << i))

-            {

-                tmp_dir = AIR_QOS_RATE_DIR_EGRESS;

-                dir = 0;

-                ret = air_qos_getRateLimitEnable(0, i, tmp_dir, &state);

-                if(AIR_E_OK == ret)

-                {

-                    AIR_PRINT("Get Port%02d %s rate %s Success.\n", i, stdir[dir], sten[state]);

-                }

-                else

-                {

-                    AIR_PRINT("Get Port%02d %s rate state Fail.\n", i, stdir[dir]);

-                }

-                tmp_dir = AIR_QOS_RATE_DIR_INGRESS;

-                dir = 1;

-                ret = air_qos_getRateLimitEnable(0, i, tmp_dir, &state);

-                if(AIR_E_OK == ret)

-                {

-                    AIR_PRINT("Get Port%02d %s rate %s Success.\n", i, stdir[dir], sten[state]);

-                }

-                else

-                {

-                    AIR_PRINT("Get Port%02d %s rate state Fail.\n", i, stdir[dir]);

-                }

-            }

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-

-}

-

-static AIR_ERROR_NO_T

-doQosDscp2Pri(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T dscp = 0, priority = 0;

-

-    dscp = _strtoul(argv[0], NULL, 0);

-    if(2 == argc)

-    {

-        priority = _strtoul(argv[1], NULL, 0);

-        ret = air_qos_setDscp2Pri(0, dscp, priority);

-        if(AIR_E_OK == ret)

-        {

-            AIR_PRINT("Set DSCP %d to priority %d Success.\n", dscp, priority);

-        }

-        else

-        {

-            AIR_PRINT("Set DSCP %d to priority %d Fail.\n", dscp, priority);

-        }

-    }

-    else if(1 == argc)

-    {

-        ret = air_qos_getDscp2Pri(0, dscp, &priority);

-        if(AIR_E_OK == ret)

-        {

-            AIR_PRINT("Get DSCP %d to priority %d\n", dscp, priority);

-        }

-        else

-        {

-            AIR_PRINT("Get DSCP %d to priority Fail.\n", dscp);

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doQosPri2Queue(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T priority = 0, queue = 0;

-

-    priority = _strtoul(argv[1], NULL, 0);

-

-    if(2 == argc)

-    {

-        priority = _strtoul(argv[0], NULL, 0);

-        queue = _strtoul(argv[1], NULL, 0);

-        ret = air_qos_setPri2Queue(0, priority, queue);

-        if(AIR_E_OK == ret)

-        {

-            AIR_PRINT("Set priority %d to queue %d Success.\n", priority, queue);

-        }

-        else

-        {

-            AIR_PRINT("Set priority %d to queue %d Fail.\n", priority, queue);

-        }

-    }

-    else

-    {

-        for(; priority < AIR_QOS_QUEUE_MAX_NUM; priority++)

-        {

-            ret = air_qos_getPri2Queue(0, priority, &queue);

-            if(AIR_E_OK == ret)

-            {

-                AIR_PRINT("Get priority %d to queue %d\n", priority, queue);

-            }

-            else

-            {

-                AIR_PRINT("Get priority %d to queue Fail.\n", priority);

-            }

-        }

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doQosTrustMode(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T mode = 0;

-    C8_T bs[4][13] = {"port", "1p_port", "dscp_port", "dscp_1p_port"};

-    AIR_QOS_TRUST_MODE_T mode_t = AIR_QOS_TRUST_MODE_LAST;

-    AIR_PORT_BITMAP_T portlist = {0};

-    UI8_T i = 0;

-

-    ret = _portListStr2Ary(argv[0], portlist, 1);

-    if(ret != AIR_E_OK)

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return ret;

-    }

-    if(2 == argc)

-    {

-        mode = _strtoul(argv[1], NULL, 0);

-        if(mode == 0)

-            mode_t = AIR_QOS_TRUST_MODE_PORT;

-        else if(mode == 1)

-            mode_t = AIR_QOS_TRUST_MODE_1P_PORT;

-        else if(mode == 2)

-            mode_t = AIR_QOS_TRUST_MODE_DSCP_PORT;

-        else if(mode == 3)

-            mode_t = AIR_QOS_TRUST_MODE_DSCP_1P_PORT;

-        else

-        {

-            AIR_PRINT("Unrecognized command.\n");

-            return AIR_E_BAD_PARAMETER;

-        }

-        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)

-        {

-            if(portlist[0] & (1 << i))

-            {

-                ret = air_qos_setTrustMode(0, i, mode_t);

-                if(AIR_E_OK == ret)

-                {

-                    AIR_PRINT("port %d Set Trust mode %s Success.\n", i, bs[mode]);

-                }

-                else

-                {

-                    AIR_PRINT("port %d Set Trust mode %s Fail.\n", i, bs[mode]);

-                }

-            }

-        }

-    }

-    else if(1 == argc)

-    {

-        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)

-        {

-            if(portlist[0] & (1 << i))

-            {

-                mode_t = AIR_QOS_TRUST_MODE_LAST;

-                ret = air_qos_getTrustMode(0, i, &mode_t);

-                if(AIR_E_OK == ret)

-                {

-                    if(mode_t == AIR_QOS_TRUST_MODE_PORT)

-                        mode = 0;

-                    else if(mode_t == AIR_QOS_TRUST_MODE_1P_PORT)

-                        mode = 1;

-                    else if(mode_t == AIR_QOS_TRUST_MODE_DSCP_PORT)

-                        mode = 2;

-                    else if(mode_t == AIR_QOS_TRUST_MODE_DSCP_1P_PORT)

-                        mode = 3;

-                    else

-                    {

-                        AIR_PRINT("port %d Get Trust mode Fail.\n", i);

-                        return AIR_E_OTHERS;

-                    }

-                    AIR_PRINT("port %d Get Trust mode %s\n", i, bs[mode]);

-                }

-                else

-                {

-                    AIR_PRINT("port %d Get Trust mode Fail.\n", i);

-                }

-            }

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doQosScheduleAlgo(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    AIR_PORT_BITMAP_T portlist = {0};

-    AIR_QOS_SCH_MODE_T sch_mode = AIR_QOS_SCH_MODE_LAST;

-    UI32_T scheduler = 0;

-    UI8_T queue = 0;

-    C8_T sche[3][5] = {"SP", "WRR", "WFQ"};

-    UI32_T weight = AIR_QOS_SHAPER_NOSETTING;

-    UI8_T i = 0;

-

-    ret = _portListStr2Ary(argv[0], portlist, 1);

-    if(ret != AIR_E_OK)

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return ret;

-    }

-    AIR_PRINT("port list is %d\n", portlist[0]);

-    if(4 == argc)

-    {

-        queue = _strtoul(argv[1], NULL, 0);

-        AIR_PRINT("queue is %d\n", queue);

-        scheduler = _strtoul(argv[2], NULL, 0);

-        AIR_PRINT("scheduler is %d\n", scheduler);

-        weight = _strtoul(argv[3], NULL, 0);

-        AIR_PRINT("weight is %d\n", weight);

-        if(scheduler == 0)

-        {

-            sch_mode = AIR_QOS_SCH_MODE_SP;

-            weight = AIR_QOS_SHAPER_NOSETTING;

-            if(weight != AIR_QOS_SHAPER_NOSETTING)

-                AIR_PRINT("[Warning] SP schedule mode no need weight\n");

-        }

-        else if(scheduler == 1)

-        {

-            sch_mode = AIR_QOS_SCH_MODE_WRR;

-            if(weight == AIR_QOS_SHAPER_NOSETTING)

-            {

-                AIR_PRINT("[Warning] No weight value input , plz check\n");

-                return AIR_E_BAD_PARAMETER;

-            }

-            AIR_PRINT("sch_mode is 1\n");

-        }

-        else if(scheduler == 2)

-        {

-            sch_mode = AIR_QOS_SCH_MODE_WFQ;

-            if(weight == AIR_QOS_SHAPER_NOSETTING)

-            {

-                AIR_PRINT("[Warning] No weight value input , plz check\n");

-                return AIR_E_BAD_PARAMETER;

-            }

-        }

-        else

-        {

-            AIR_PRINT("Unknown schedule mode, plz check again\n");

-            return AIR_E_BAD_PARAMETER;

-        }

-        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)

-        {

-            if(portlist[0] & (1 << i))

-            {

-                AIR_PRINT("port %d\n", i);

-                ret = air_qos_setScheduleAlgo(0, i, queue, sch_mode, weight);

-                if(AIR_E_OK == ret)

-                {

-                    AIR_PRINT("Set Port%02d Scheduler %s Success.\n", i, sche[scheduler]);

-                }

-                else

-                {

-                    AIR_PRINT("Set Port%02d Scheduler %s Fail.\n", i, sche[scheduler]);

-                }

-            }

-        }

-    }

-    else if(2 == argc)

-    {

-        queue = _strtoul(argv[1], NULL, 0);

-        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)

-        {

-            if(portlist[0] & (1 << i))

-            {

-                ret = air_qos_getScheduleAlgo(0, i, queue, &sch_mode, &weight);

-                if(AIR_E_OK == ret)

-                {

-                    if(sch_mode == AIR_QOS_SCH_MODE_SP)

-                        AIR_PRINT("Get Port%02d queue %d Scheduler %s\n", i, queue, sche[sch_mode]);

-                    else if((sch_mode == AIR_QOS_SCH_MODE_WRR) || (sch_mode == AIR_QOS_SCH_MODE_WFQ))

-                        AIR_PRINT("Get Port%02d queue %d Scheduler %s weight %d\n", i, queue, sche[sch_mode], weight);

-                    else

-                        AIR_PRINT("Get Port%02d queue %d Scheduler unknown\n", i, queue);

-                }

-                else

-                {

-                    AIR_PRINT("Get Port%02d queue %d Scheduler Fail.\n", i, queue);

-                }

-            }

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doQosGet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(qosGetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doQosSet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(qosSetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doQos(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(qosCmds, argc, argv);

-}

-static AIR_ERROR_NO_T

-doDiagTxComply(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T phy = 0;

-    UI32_T mode = 0;

-

-    phy = _strtoul(argv[0], NULL, 0);

-    if(2 == argc)

-    {

-        /* diag set txComply <phy(0~5)> <mode(0~8)> */

-        mode = _strtoul(argv[1], NULL, 0);

-        ret = air_diag_setTxComplyMode(0, phy, mode);

-        AIR_PRINT("Set diagnostic function: PHY %u Tx Compliance mode = %u ", phy, mode);

-    }

-    else if(1 == argc)

-    {

-        /* diag get txComply <phy(0~5)> */

-        ret = air_diag_getTxComplyMode(0, phy, &mode);

-        AIR_PRINT("Get diagnostic function: PHY %u Tx Compliance mode ", phy);

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    if(AIR_E_OK == ret)

-    {

-        AIR_PRINT("Done.\n\tMode=");

-        switch(mode)

-        {

-            case AIR_DIAG_TXCOMPLY_MODE_10M_NLP:

-                AIR_PRINT("%s\n", "10M_NLP");

-                break;

-            case AIR_DIAG_TXCOMPLY_MODE_10M_RANDOM:

-                AIR_PRINT("%s\n", "10M_Random");

-                break;

-            case AIR_DIAG_TXCOMPLY_MODE_10M_SINE:

-                AIR_PRINT("%s\n", "10M_Sine");

-                break;

-            case AIR_DIAG_TXCOMPLY_MODE_100M_PAIR_A:

-                AIR_PRINT("%s\n", "100M_Pair_a");

-                break;

-            case AIR_DIAG_TXCOMPLY_MODE_100M_PAIR_B:

-                AIR_PRINT("%s\n", "100M_Pair_b");

-                break;

-            case AIR_DIAG_TXCOMPLY_MODE_1000M_TM1:

-                AIR_PRINT("%s\n", "1000M_TM1");

-                break;

-            case AIR_DIAG_TXCOMPLY_MODE_1000M_TM2:

-                AIR_PRINT("%s\n", "1000M_TM2");

-                break;

-            case AIR_DIAG_TXCOMPLY_MODE_1000M_TM3:

-                AIR_PRINT("%s\n", "1000M_TM3");

-                break;

-            case AIR_DIAG_TXCOMPLY_MODE_1000M_TM4:

-                AIR_PRINT("%s\n", "1000M_TM4");

-                break;

-            default:

-                break;

-        }

-    }

-    else

-    if(AIR_E_OTHERS == ret)

-    {

-        AIR_PRINT("isn't setting.\n");

-    }

-    else

-    {

-        AIR_PRINT("Fail.\n");

-    }

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doDiagSet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(diagSetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doDiagGet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(diagGetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doDiag(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(diagCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doLedMode(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T mode = 0;

-

-    if(1 == argc)

-    {

-        /* led set mode <mode(0:disable, 1~3:2 LED, 4:User-Define)> */

-        mode = _strtoul(argv[0], NULL, 0);

-        ret = air_led_setMode(0, 0, mode);

-        AIR_PRINT("Set LED mode ");

-    }

-    else if(0 == argc)

-    {

-        /* led get mode */

-        ret = air_led_getMode(0, 0, &mode);

-        AIR_PRINT("Get LED mode ");

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    if(AIR_E_OK == ret)

-    {

-        switch(mode)

-        {

-            case AIR_LED_MODE_DISABLE:

-                AIR_PRINT(": Disabled.\n");

-                break;

-            case AIR_LED_MODE_2LED_MODE0:

-                AIR_PRINT(": LED 0:Link / LED 1:Activity.\n");

-                break;

-            case AIR_LED_MODE_2LED_MODE1:

-                AIR_PRINT(": LED 0:1000M Activity / LED 1:100M Activity.\n");

-                break;

-            case AIR_LED_MODE_2LED_MODE2:

-                AIR_PRINT(": LED 0:1000M Activity / LED 1:10&100M Activity.\n");

-                break;

-            case AIR_LED_MODE_USER_DEFINE:

-                AIR_PRINT(": User-Defined.\n");

-                break;

-            default:

-                AIR_PRINT(": Fail.\n");

-                break;

-        }

-    }

-    else

-    if(AIR_E_OTHERS == ret)

-    {

-        AIR_PRINT(": Unrecognized.\n");

-    }

-    else

-    {

-        AIR_PRINT("Fail.\n");

-    }

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doLedState(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI8_T entity = 0;

-    BOOL_T state = FALSE;

-

-    entity = _strtoul(argv[0], NULL, 0);

-    if(2 == argc)

-    {

-        /* led set state <led(0..1)> <state(1:En 0:Dis)> */

-        state = _strtoul(argv[1], NULL, 0);

-        ret = air_led_setState(0, 0, entity, state);

-        AIR_PRINT("Set LED %u state ", entity);

-    }

-    else if(1 == argc)

-    {

-        /* led get state <led(0..1)> */

-        ret = air_led_getState(0, 0, entity, &state);

-        AIR_PRINT("Get LED %u state ", entity );

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    if(AIR_E_OK == ret)

-    {

-        AIR_PRINT(": %s.\n", (state)?"Enable":"Disabled");

-    }

-    else

-    if(AIR_E_OTHERS == ret)

-    {

-        AIR_PRINT(": Unrecognized.\n");

-    }

-    else

-    {

-        AIR_PRINT("Fail.\n");

-    }

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doLedUsrDef(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T i = 0;

-    UI8_T entity = 0;

-    BOOL_T polarity = LOW;

-    UI32_T on_evt_map = 0;

-    UI32_T blk_evt_map = 0;

-    AIR_LED_ON_EVT_T on_evt;

-    AIR_LED_BLK_EVT_T blk_evt;

-

-    entity = _strtoul(argv[0], NULL, 0);

-    if(4 == argc)

-    {

-        /* led set usr <led(0..1)> <polarity(0:low, 1:high)> <on_evt(7'bin)> <blink_evt(10'bin)> */

-        polarity = _strtoul(argv[1], NULL, 0);

-        on_evt_map = _strtoul(argv[2], NULL, 2);

-        blk_evt_map = _strtoul(argv[3], NULL, 2);

-

-        memset(&on_evt, 0, sizeof(AIR_LED_ON_EVT_T));

-        if(on_evt_map & BIT(0))

-        {

-            on_evt.link_1000m = TRUE;

-        }

-        if(on_evt_map & BIT(1))

-        {

-            on_evt.link_100m = TRUE;

-        }

-        if(on_evt_map & BIT(2))

-        {

-            on_evt.link_10m = TRUE;

-        }

-        if(on_evt_map & BIT(3))

-        {

-            on_evt.link_dn = TRUE;

-        }

-        if(on_evt_map & BIT(4))

-        {

-            on_evt.fdx = TRUE;

-        }

-        if(on_evt_map & BIT(5))

-        {

-            on_evt.hdx = TRUE;

-        }

-        if(on_evt_map & BIT(6))

-        {

-            on_evt.force = TRUE;

-        }

-

-        memset(&blk_evt, 0, sizeof(AIR_LED_BLK_EVT_T));

-        if(blk_evt_map & BIT(0))

-        {

-            blk_evt.tx_act_1000m = TRUE;

-        }

-        if(blk_evt_map & BIT(1))

-        {

-            blk_evt.rx_act_1000m = TRUE;

-        }

-        if(blk_evt_map & BIT(2))

-        {

-            blk_evt.tx_act_100m = TRUE;

-        }

-        if(blk_evt_map & BIT(3))

-        {

-            blk_evt.rx_act_100m = TRUE;

-        }

-        if(blk_evt_map & BIT(4))

-        {

-            blk_evt.tx_act_10m = TRUE;

-        }

-        if(blk_evt_map & BIT(5))

-        {

-            blk_evt.rx_act_10m = TRUE;

-        }

-        if(blk_evt_map & BIT(6))

-        {

-            blk_evt.cls = TRUE;

-        }

-        if(blk_evt_map & BIT(7))

-        {

-            blk_evt.rx_crc = TRUE;

-        }

-        if(blk_evt_map & BIT(8))

-        {

-            blk_evt.rx_idle = TRUE;

-        }

-        if(blk_evt_map & BIT(9))

-        {

-            blk_evt.force = TRUE;

-        }

-        ret = air_led_setUsrDef(0, 0, entity, polarity, on_evt, blk_evt);

-        AIR_PRINT("Set LED %u User-define ", entity);

-    }

-    else if(1 == argc)

-    {

-        /* led get usr <led(0..1)> */

-        ret = air_led_getUsrDef(0, 0, entity, &polarity, &on_evt, &blk_evt);

-        AIR_PRINT("Get LED %u User-define ", entity );

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    if(AIR_E_OK == ret)

-    {

-        AIR_PRINT("Done.\n");

-        AIR_PRINT("Polarity:%u.\n", polarity);

-        AIR_PRINT("On Event:\n");

-        i = 6;

-        AIR_PRINT("\t(%u)Force on :%s\n", i--, (on_evt.force)?"On":"Off");

-        AIR_PRINT("\t(%u)Half Duplex :%s\n", i--, (on_evt.hdx)?"On":"Off");

-        AIR_PRINT("\t(%u)Full Duplex :%s\n", i--, (on_evt.fdx)?"On":"Off");

-        AIR_PRINT("\t(%u)Link Down :%s\n", i--, (on_evt.link_dn)?"On":"Off");

-        AIR_PRINT("\t(%u)Link 10M :%s\n", i--, (on_evt.link_10m)?"On":"Off");

-        AIR_PRINT("\t(%u)Link 100M :%s\n", i--, (on_evt.link_100m)?"On":"Off");

-        AIR_PRINT("\t(%u)Link 1000M :%s\n", i--, (on_evt.link_1000m)?"On":"Off");

-

-        AIR_PRINT("Blinking Event:\n");

-        i = 9;

-        AIR_PRINT("\t(%u)Force blinks :%s\n", i--, (blk_evt.force)?"On":"Off");

-        AIR_PRINT("\t(%u)Rx Idle Error :%s\n", i--, (blk_evt.rx_idle)?"On":"Off");

-        AIR_PRINT("\t(%u)Rx CRC Error :%s\n", i--, (blk_evt.rx_crc)?"On":"Off");

-        AIR_PRINT("\t(%u)Collision :%s\n", i--, (blk_evt.cls)?"On":"Off");

-        AIR_PRINT("\t(%u)10Mbps RX Activity :%s\n", i--, (blk_evt.rx_act_10m)?"On":"Off");

-        AIR_PRINT("\t(%u)10Mbps TX Activity :%s\n", i--, (blk_evt.tx_act_10m)?"On":"Off");

-        AIR_PRINT("\t(%u)100Mbps RX Activity :%s\n", i--, (blk_evt.rx_act_100m)?"On":"Off");

-        AIR_PRINT("\t(%u)100Mbps TX Activity :%s\n", i--, (blk_evt.tx_act_100m)?"On":"Off");

-        AIR_PRINT("\t(%u)1000Mbps RX Activity :%s\n", i--, (blk_evt.rx_act_1000m)?"On":"Off");

-        AIR_PRINT("\t(%u)1000Mbps TX Activity :%s\n", i--, (blk_evt.tx_act_1000m)?"On":"Off");

-    }

-    else

-    {

-        AIR_PRINT("Fail.\n");

-    }

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doLedBlkTime(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    AIR_LED_BLK_DUR_T time = 0;

-

-    if(1 == argc)

-    {

-        /* led set time <time(0~5)> */

-        time = _strtoul(argv[0], NULL, 0);

-        ret = air_led_setBlkTime(0, 0, time);

-        AIR_PRINT("Set Blinking Duration ");

-    }

-    else if(0 == argc)

-    {

-        /* led get time */

-        ret = air_led_getBlkTime(0, 0, &time);

-        AIR_PRINT("Get Blinking Duration ");

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    if(AIR_E_OK == ret)

-    {

-        AIR_PRINT("Done.\n");

-        AIR_PRINT("\tBlinking duration : %u (ms)\n", (32 << time) );

-    }

-    else

-    {

-        AIR_PRINT("Fail.\n");

-    }

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doLedSet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(ledSetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doLedGet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(ledGetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doLed(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(ledCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doShowVersion(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_PRINT("VERSION: %s\n", AIR_VER_SDK);

-

-    return AIR_E_OK;

-}

-

-static AIR_ERROR_NO_T

-doShow(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(showCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doStormRate(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0, type = 0;

-    UI32_T unit = 0, count = 0;

-    C8_T stype[3][5] = {"Bcst", "Mcst", "Ucst"};

-    UI32_T kb = 0;

-

-    port = _strtol(argv[0], NULL, 10);

-    type = _strtol(argv[1], NULL, 10);

-    if(4 == argc)

-    {

-        count = _strtol(argv[2], NULL, 10);

-        unit = _strtol(argv[3], NULL, 10);

-

-        if(0 == unit)

-            kb = 64;

-        else if(1 == unit)

-            kb = 256;

-        else if(2 == unit)

-            kb = 1024;

-        else if(3 == unit)

-            kb = 4096;

-        else

-            kb = 16384;

-        ret = air_sec_setStormRate(0, port, type, count, unit);

-        if(AIR_E_OK == ret)

-        {

-            AIR_PRINT("Set Port%02d %s storm rate (%d * %d) = %d Kbps\n", port, stype[type], count, kb, (count*kb));

-        }

-        else

-        {

-            AIR_PRINT("Set Port%02d %s storm rate Fail.\n", port, stype[type]);

-            AIR_PRINT("Note: Port(0..4) can only select unit(0..3), port(5..6) can only select unit(4)\n");

-        }

-    }

-    else if(2 == argc)

-    {

-        ret = air_sec_getStormRate(0, port, type, &count, &unit);

-        if(AIR_E_OK == ret)

-        {

-            if(0 == unit)

-                kb = 64;

-            else if(1 == unit)

-                kb = 256;

-            else if(2 == unit)

-                kb = 1024;

-            else if(3 == unit)

-                kb = 4096;

-            else

-                kb = 16384;

-            AIR_PRINT("Port%02d %s storm rate (%d * %d) = %d Kbps\n", port, stype[type], count, kb, (count*kb));

-        }

-        else

-        {

-            AIR_PRINT("Get Port%02d %s storm rate Fail\n", port, stype[type]);

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doFldMode(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0, type = 0;

-    BOOL_T fld_en = 0;

-    C8_T stype[4][5] = {"Bcst", "Mcst", "Ucst", "Qury"};

-    C8_T sen[2][10] = {"Disable", "Enable"};

-

-    port = _strtol(argv[0], NULL, 10);

-    type = _strtol(argv[1], NULL, 10);

-

-    if(2 == argc)

-    {

-        ret = air_sec_getFldMode(0, port, type, &fld_en);

-        if(AIR_E_OK == ret)

-        {

-            AIR_PRINT("Get Port%02d flooding %s frame %s\n", port, stype[type], sen[fld_en]);

-        }

-        else

-        {

-            AIR_PRINT("Get Port%02d flooding %s frame Fail\n", port, stype[type]);

-        }

-    }

-    else if(3 == argc)

-    {

-        fld_en = _strtol(argv[2], NULL, 10);

-        ret = air_sec_setFldMode(0, port, type, fld_en);

-        if(AIR_E_OK == ret)

-        {

-            AIR_PRINT("Set Port%02d flooding %s frame %s Success\n", port, stype[type], sen[fld_en]);

-        }

-        else

-        {

-            AIR_PRINT("Set Port%02d flooding %s frame %s Fail\n", port, stype[type], sen[fld_en]);

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doStormEnable(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T port = 0, type = 0;

-    BOOL_T en = 0;

-    C8_T sen[2][10] = {"Disable", "Enable"};

-    C8_T stype[3][5] = {"Bcst", "Mcst", "Ucst"};

-

-    port = _strtol(argv[0], NULL, 10);

-    type = _strtol(argv[1], NULL, 10);

-    if(3 == argc)

-    {

-        en = _strtol(argv[2], NULL, 10);

-        ret = air_sec_setStormEnable(0, port, type, en);

-        if(AIR_E_OK == ret)

-        {

-            AIR_PRINT("Set Port%02d %s storm %s Success.\n", port, stype[type], sen[en]);

-        }

-        else

-        {

-            AIR_PRINT("Set Port%02d %s storm %s Fail.\n", port, stype[type], sen[en]);

-        }

-    }

-    else if(2 == argc)

-    {

-        ret = air_sec_getStormEnable(0, port, type, &en);

-        if(AIR_E_OK == ret)

-        {

-            AIR_PRINT("Port%02d %s storm %s\n", port, stype[type], sen[en]);

-        }

-        else

-        {

-            AIR_PRINT("Get Port%02d %s storm Fail\n", port, stype[type]);

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doSaLearning(UI32_T argc, C8_T *argv[])

-{

-    UI32_T port = 0;

-    AIR_SEC_PORTSEC_PORT_CONFIG_T port_config;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port = _strtoul(argv[0], NULL, 0);

-    if(2 == argc)

-    {

-        memset(&port_config, 0, sizeof(AIR_SEC_PORTSEC_PORT_CONFIG_T));

-        rc = air_sec_getPortSecPortCfg(0, port, &port_config);

-        port_config.sa_lrn_en = _strtoul(argv[1], NULL, 0);

-        rc = air_sec_setPortSecPortCfg(0, port, port_config);

-        if(AIR_E_OK == rc)

-        {

-            AIR_PRINT("Set Port%02d sa learn %s Success.\n", port, port_config.sa_lrn_en?"Enable":"Disable");

-        }

-        else

-        {

-            AIR_PRINT("Set Port%02d sa learn %s Fail.\n", port, port_config.sa_lrn_en?"Enable":"Disable");

-        }

-    }

-    else if(1 == argc)

-    {

-        rc = air_sec_getPortSecPortCfg(0, port, &port_config);

-        if(AIR_E_OK == rc)

-        {

-            AIR_PRINT("Port%02d sa learn: %s\n", port, port_config.sa_lrn_en?"Enable":"Disable");

-        }

-        else

-        {

-            AIR_PRINT("Get Port%02d sa learn Fail\n", port);

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        rc = AIR_E_BAD_PARAMETER;

-    }

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doSaLimit(UI32_T argc, C8_T *argv[])

-{

-    UI32_T port = 0;

-    AIR_SEC_PORTSEC_PORT_CONFIG_T port_config;

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-

-    port = _strtoul(argv[0], NULL, 0);

-    if(3 == argc)

-    {

-        memset(&port_config, 0, sizeof(AIR_SEC_PORTSEC_PORT_CONFIG_T));

-        rc = air_sec_getPortSecPortCfg(0, port, &port_config);

-        port_config.sa_lmt_en = _strtoul(argv[1], NULL, 0);

-        port_config.sa_lmt_cnt = _strtoul(argv[2], NULL, 0);

-        rc = air_sec_setPortSecPortCfg(0, port, port_config);

-        if(AIR_E_OK == rc)

-        {

-            AIR_PRINT("Set Port%02d sa limit %s Success.\n", port, port_config.sa_lmt_en?"Enable":"Disable");

-        }

-        else

-        {

-            AIR_PRINT("Set Port%02d sa limit %s Fail.\n", port, port_config.sa_lmt_en?"Enable":"Disable");

-        }

-    }

-    else if(1 == argc)

-    {

-        rc = air_sec_getPortSecPortCfg(0, port, &port_config);

-        if(AIR_E_OK == rc)

-        {

-            AIR_PRINT("Port%02d ", port);

-            AIR_PRINT("sa limit: %s\n", port_config.sa_lmt_en?"Enable":"Disable");

-            if(TRUE == (port_config.sa_lmt_en && (AIR_MAX_NUM_OF_MAC ==  port_config.sa_lmt_cnt)))

-            {

-                AIR_PRINT("Sa learning without limitation\n");

-            }

-            else if(TRUE == (port_config.sa_lmt_en && (AIR_MAX_NUM_OF_MAC >  port_config.sa_lmt_cnt)))

-            {

-                AIR_PRINT("Rx sa allowable learning number: %d\n", port_config.sa_lmt_cnt);

-            }

-        }

-        else

-        {

-            AIR_PRINT("Get Port%02d sa limit Fail\n", port);

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        rc = AIR_E_BAD_PARAMETER;

-    }

-

-    return rc;

-}

-

-static AIR_ERROR_NO_T

-doSecGet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(secGetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doSecSet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(secSetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doSec(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(secCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doSwitchCpuPortEn(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    BOOL_T cpu_en = FALSE;

-

-    if(0 == argc)

-    {

-        /* switch get sysPhyEn */

-        ret = air_switch_getCpuPortEn(0, &cpu_en);

-        AIR_PRINT("Get Cpu Port State ");

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT(": %s\n", cpu_en?"Enable":"Disable");

-        }

-        else

-        {

-            AIR_PRINT("Fail!\n");

-        }

-    }

-    else if(1 == argc)

-    {

-        /* switch set sysPhyEn <phy_en> */

-        cpu_en = _strtol(argv[0], NULL, 0);

-        ret = air_switch_setCpuPortEn(0, cpu_en);

-        AIR_PRINT("Set CPU port State ");

-        if(ret == AIR_E_OK)

-        {

-            AIR_PRINT(": %s\n", cpu_en?"Enable":"Disable");

-        }

-        else

-        {

-            AIR_PRINT("Fail!\n");

-        }

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doSwitchCpuPort(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    BOOL_T cpu_en = FALSE;

-    UI32_T port = 0;

-    C8_T str_temp[AIR_MAX_NUM_OF_PORTS+1];

-

-    if(1 == argc)

-    {

-        /* switch set cpuPort <portnumber> */

-        port = _strtol(argv[0], NULL, 10);

-        ret = air_switch_setCpuPort(0, port);

-        AIR_PRINT("Set CPU Port ");

-    }

-    else if(0 == argc)

-    {

-        /* switch get cpuPort */

-        ret = air_switch_getCpuPort(0, &port);

-        AIR_PRINT("Get CPU Port ");

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    if(AIR_E_OK == ret)

-    {

-        AIR_PRINT(": %d\n", port);

-    }

-    else

-    {

-        AIR_PRINT("Fail!\n");

-    }

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doSwitchPhyLCIntrEn(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T phy = 0;

-    BOOL_T enable = FALSE;

-

-    if(2 == argc)

-    {

-        /* switch set phyLCIntrEn <phy(0..6)> <(1:En,0:Dis)> */

-        phy    = _strtol(argv[0], NULL, 10);

-        enable = _strtol(argv[1], NULL, 10);

-        ret    = air_switch_setSysIntrEn(0, (phy + AIR_SYS_INTR_TYPE_PHY0_LC), enable);

-    }

-    else if(1 == argc)

-    {

-        /* switch get phyLCIntrEn <phy(0..6)> */

-        phy = _strtol(argv[0], NULL, 10);

-        ret = air_switch_getSysIntrEn(0, (phy + AIR_SYS_INTR_TYPE_PHY0_LC), &enable);

-        AIR_PRINT("PHY(%d) LinkChange interrupt : %s\n", phy, (TRUE == enable) ? "enable" : "disable");

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doSwitchPhyLCIntrSts(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T phy = 0;

-    BOOL_T enable = FALSE;

-

-    if(2 == argc)

-    {

-        /* switch set phyLCIntrSts <phy(0..6)> <(1:En)> */

-        phy    = _strtol(argv[0], NULL, 10);

-        enable = _strtol(argv[1], NULL, 10);

-        ret    = air_switch_setSysIntrStatus(0, (phy + AIR_SYS_INTR_TYPE_PHY0_LC), enable);

-    }

-    else if(1 == argc)

-    {

-        /* switch get phyLCIntrSts <phy(0..6)> */

-        phy = _strtol(argv[0], NULL, 10);

-        ret = air_switch_getSysIntrStatus(0, (phy + AIR_SYS_INTR_TYPE_PHY0_LC), &enable);

-        AIR_PRINT("PHY(%d) LinkChange interrupt state : %s\n", phy, (TRUE == enable) ? "set" : "unset");

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        ret = AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-

-static AIR_ERROR_NO_T

-doSwitchSet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(switchSetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doSwitchGet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(switchGetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doSwitch(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(switchCmds, argc, argv);

-}

-

-static void _air_acl_printRuleMap(UI32_T *rule_map, UI32_T ary_num)

-{

-    UI32_T i;

-    BOOL_T first;

-

-    first = TRUE;

-    for(i=0; i<ary_num*32; i++)

-    {

-        if(rule_map[i/32] & BIT(i%32))

-        {

-            if(TRUE == first)

-            {

-                AIR_PRINT("%u", i);

-                first = FALSE;

-            }

-            else

-            {

-                AIR_PRINT(",%u", i);

-            }

-        }

-    }

-    AIR_PRINT("\n");

-}

-

-static AIR_ERROR_NO_T

-doAclEn(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T argi=0;

-    UI32_T port = 0;

-    BOOL_T en = FALSE;

-

-    if(1 == argc)

-    {

-        /* acl set en <en(1:En,0:Dis)> */

-        en = _strtoul(argv[argi++], NULL, 2);

-        ret = air_acl_setGlobalState(0, en);

-        AIR_PRINT("Set Global ACL function ");

-    }

-    else if(0 == argc)

-    {

-        /* acl get en */

-        ret = air_acl_getGlobalState(0, &en);

-        AIR_PRINT("Get Global ACL function ");

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    if(ret == AIR_E_OK)

-    {

-        AIR_PRINT(": %s\n", (TRUE == en)?"Enable":"Disable");

-    }

-    else

-    {

-        AIR_PRINT("Fail!\n");

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doAclRule(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T rule_idx = 0;

-    BOOL_T state = FALSE, reverse = FALSE, end = FALSE;

-    UI32_T argi = 0, ipv6 = 0, i = 0;

-    AIR_ACL_RULE_T rule;

-    UI8_T tmp_ip[16] = {0};

-    C8_T str_temp[AIR_MAX_NUM_OF_PORTS+1];

-    C8_T str[40];

-

-    memset(&rule, 0, sizeof(AIR_ACL_RULE_T));

-    if(argc >= 6)

-    {

-        /* acl set rule <idx(0..255)> <state(0:Dis,1:En)> <reverse(0:Dis,1:En)> <end(0:Dis,1:En)> <portmap(7'bin)>

-        <ipv6(0:Dis,1:En,2:Not care)>

-        [ dmac <dmac(12'hex)> <dmac_mask(12'hex)> ]

-        [ smac <smac(12'hex)> <smac_mask(12'hex)> ]

-        [ stag <stag(4'hex)> <stag_mask(4'hex)> ]

-        [ ctag <ctag(4'hex)> <ctag_mask(4'hex)> ]

-        [ etype <etype(4'hex)> <etype_mask(4'hex)> ]

-        [ dip <dip(IPADDR)> <dip_mask(IPADDR)> ]

-        [ sip <sip(IPADDR)> <sip_mask(IPADDR)> ]

-        [ dscp <dscp(2'hex)> <dscp_mask(2'hex)> ]

-        [ protocol <protocol(2'hex)> <protocol_mask(2'hex)> ]

-        [ dport <dport(4'hex)> <dport_mask(4'hex)> ]

-        [ sport <sport(4'hex)> <sport_mask(4'hex)> ]

-        [ flow_label <flow_label(4'hex)> <flow_label_mask(4'hex)> ]

-        [ udf <udf(4'hex)> <udf_mask(4'hex)> ] */

-

-        rule_idx = _strtoul(argv[argi++], NULL, 0);

-

-        rule.ctrl.rule_en = _strtoul(argv[argi++], NULL, 0);

-        rule.ctrl.reverse = _strtoul(argv[argi++], NULL, 0);

-        rule.ctrl.end = _strtoul(argv[argi++], NULL, 0);

-

-        rule.key.portmap = _strtoul(argv[argi++], NULL, 2);

-        rule.mask.portmap = (~rule.key.portmap) & AIR_ALL_PORT_BITMAP;

-

-        ipv6 = _strtoul(argv[argi++], NULL, 0);

-        if(0 == ipv6)

-        {

-            rule.key.isipv6 = FALSE;

-            rule.mask.isipv6 = TRUE;

-        }

-        else if(1 == ipv6)

-        {

-            rule.key.isipv6 = TRUE;

-            rule.mask.isipv6 = TRUE;

-        }

-        else

-        {

-            rule.mask.isipv6 = FALSE;

-        }

-

-        if(0 == _strcmp(argv[argi], "dmac"))

-        {

-            argi++;

-            _str2mac(argv[argi++], rule.key.dmac);

-            _str2mac(argv[argi++], rule.mask.dmac);

-            rule.key.fieldmap |= 1 << AIR_ACL_DMAC;

-        }

-

-        if(0 == _strcmp(argv[argi], "smac"))

-        {

-            argi++;

-            _str2mac(argv[argi++], rule.key.smac);

-            _str2mac(argv[argi++], rule.mask.smac);

-            rule.key.fieldmap |= 1 << AIR_ACL_SMAC;

-        }

-

-        if(0 == _strcmp(argv[argi], "stag"))

-        {

-            argi++;

-            rule.key.stag = _strtoul(argv[argi++], NULL, 16);

-            rule.mask.stag = _strtoul(argv[argi++], NULL, 16);

-            rule.key.fieldmap |= 1 << AIR_ACL_STAG;

-        }

-

-        if(0 == _strcmp(argv[argi], "ctag"))

-        {

-            argi++;

-            rule.key.ctag = _strtoul(argv[argi++], NULL, 16);

-            rule.mask.ctag = _strtoul(argv[argi++], NULL, 16);

-            rule.key.fieldmap |= 1 << AIR_ACL_CTAG;

-        }

-

-        if(0 == _strcmp(argv[argi], "etype"))

-        {

-            argi++;

-            rule.key.etype= _strtoul(argv[argi++], NULL, 16);

-            rule.mask.etype = _strtoul(argv[argi++], NULL, 16);

-            rule.key.fieldmap |= 1 << AIR_ACL_ETYPE;

-        }

-

-        if(0 == _strcmp(argv[argi], "dip"))

-        {

-            argi++;

-            if(0 == ipv6)

-            {

-                _str2ipv4(argv[argi++], rule.key.dip);

-                _str2ipv4(argv[argi++], rule.mask.dip);

-            }

-            else if(1 == ipv6)

-            {

-                _str2ipv6(argv[argi++], tmp_ip);

-                rule.key.dip[3] = (tmp_ip[0]<<24) | (tmp_ip[1]<<16) | (tmp_ip[2]<<8) | tmp_ip[3];

-                rule.key.dip[2] = (tmp_ip[4]<<24) | (tmp_ip[5]<<16) | (tmp_ip[6]<<8) | tmp_ip[7];

-                rule.key.dip[1] = (tmp_ip[8]<<24) | (tmp_ip[9]<<16) | (tmp_ip[10]<<8) | tmp_ip[11];

-                rule.key.dip[0] = (tmp_ip[12]<<24) | (tmp_ip[13]<<16) | (tmp_ip[14]<<8) | tmp_ip[15];

-                _str2ipv6(argv[argi++], tmp_ip);

-                rule.mask.dip[3] = (tmp_ip[0]<<24) | (tmp_ip[1]<<16) | (tmp_ip[2]<<8) | tmp_ip[3];

-                rule.mask.dip[2] = (tmp_ip[4]<<24) | (tmp_ip[5]<<16) | (tmp_ip[6]<<8) | tmp_ip[7];

-                rule.mask.dip[1] = (tmp_ip[8]<<24) | (tmp_ip[9]<<16) | (tmp_ip[10]<<8) | tmp_ip[11];

-                rule.mask.dip[0] = (tmp_ip[12]<<24) | (tmp_ip[13]<<16) | (tmp_ip[14]<<8) | tmp_ip[15];

-            }

-            rule.key.fieldmap |= 1 << AIR_ACL_DIP;

-        }

-

-        if(0 == _strcmp(argv[argi], "sip"))

-        {

-            argi++;

-            if(0 == ipv6)

-            {

-                _str2ipv4(argv[argi++], rule.key.sip);

-                _str2ipv4(argv[argi++], rule.mask.sip);

-            }

-            else if(1 == ipv6)

-            {

-                _str2ipv6(argv[argi++], tmp_ip);

-                rule.key.sip[3] = (tmp_ip[0]<<24) | (tmp_ip[1]<<16) | (tmp_ip[2]<<8) | tmp_ip[3];

-                rule.key.sip[2] = (tmp_ip[4]<<24) | (tmp_ip[5]<<16) | (tmp_ip[6]<<8) | tmp_ip[7];

-                rule.key.sip[1] = (tmp_ip[8]<<24) | (tmp_ip[9]<<16) | (tmp_ip[10]<<8) | tmp_ip[11];

-                rule.key.sip[0] = (tmp_ip[12]<<24) | (tmp_ip[13]<<16) | (tmp_ip[14]<<8) | tmp_ip[15];

-                _str2ipv6(argv[argi++], tmp_ip);

-                rule.mask.sip[3] = (tmp_ip[0]<<24) | (tmp_ip[1]<<16) | (tmp_ip[2]<<8) | tmp_ip[3];

-                rule.mask.sip[2] = (tmp_ip[4]<<24) | (tmp_ip[5]<<16) | (tmp_ip[6]<<8) | tmp_ip[7];

-                rule.mask.sip[1] = (tmp_ip[8]<<24) | (tmp_ip[9]<<16) | (tmp_ip[10]<<8) | tmp_ip[11];

-                rule.mask.sip[0] = (tmp_ip[12]<<24) | (tmp_ip[13]<<16) | (tmp_ip[14]<<8) | tmp_ip[15];

-            }

-            rule.key.fieldmap |= 1 << AIR_ACL_SIP;

-        }

-

-        if(0 == _strcmp(argv[argi], "dscp"))

-        {

-            argi++;

-            rule.key.dscp = _strtoul(argv[argi++], NULL, 16);

-            rule.mask.dscp = _strtoul(argv[argi++], NULL, 16);

-            rule.key.fieldmap |= 1 << AIR_ACL_DSCP;

-        }

-

-        if(0 == _strcmp(argv[argi], "protocol"))

-        {

-            argi++;

-            rule.key.protocol = _strtoul(argv[argi++], NULL, 16);

-            rule.mask.protocol = _strtoul(argv[argi++], NULL, 16);

-            rule.key.fieldmap |= 1 << AIR_ACL_PROTOCOL;

-        }

-

-        if(0 == _strcmp(argv[argi], "dport"))

-        {

-            argi++;

-            rule.key.dport = _strtoul(argv[argi++], NULL, 16);

-            rule.mask.dport = _strtoul(argv[argi++], NULL, 16);

-            rule.key.fieldmap |= 1 << AIR_ACL_DPORT;

-        }

-

-        if(0 == _strcmp(argv[argi], "sport"))

-        {

-            argi++;

-            rule.key.sport = _strtoul(argv[argi++], NULL, 16);

-            rule.mask.sport = _strtoul(argv[argi++], NULL, 16);

-            rule.key.fieldmap |= 1 << AIR_ACL_SPORT;

-        }

-

-        if(0 == _strcmp(argv[argi], "flow_label"))

-        {

-            argi++;

-            rule.key.flow_label= _strtoul(argv[argi++], NULL, 16);

-            rule.mask.flow_label= _strtoul(argv[argi++], NULL, 16);

-            rule.key.fieldmap |= 1 << AIR_ACL_FLOW_LABEL;

-        }

-

-        if(0 == _strcmp(argv[argi], "udf"))

-        {

-            argi++;

-            rule.key.udf = _strtoul(argv[argi++], NULL, 16);

-            rule.mask.udf = _strtoul(argv[argi++], NULL, 16);

-            rule.key.fieldmap |= 1 << AIR_ACL_UDF;

-        }

-

-        rule.mask.fieldmap = rule.key.fieldmap;

-        ret = air_acl_setRule(0, rule_idx, &rule);

-        if(ret < AIR_E_LAST)

-            AIR_PRINT("Set ACL Rule(%u): %s\n", rule_idx, air_error_getString(ret));

-    }

-    else if(1 == argc)

-    {

-        rule_idx = _strtoul(argv[0], NULL, 0);

-        ret = air_acl_getRule(0, rule_idx, &rule);

-        if(ret < AIR_E_LAST)

-            AIR_PRINT("Get ACL Rule(%u): %s\n", rule_idx, air_error_getString(ret));

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    if(AIR_E_OK == ret)

-    {

-        if(TRUE == rule.ctrl.rule_en)

-        {

-            AIR_PRINT("\t Rule end          : %s\n", (TRUE == rule.ctrl.end)?"Enable":"Disable");

-            AIR_PRINT("\t Rule reverse      : %s\n", (TRUE == rule.ctrl.reverse)?"Enable":"Disable");

-            _hex2bitstr((~rule.mask.portmap) & AIR_ALL_PORT_BITMAP, str_temp, AIR_MAX_NUM_OF_PORTS+1);

-            AIR_PRINT("\t Portmap[0:6]      : %s\n", str_temp);

-            for(i = AIR_ACL_DMAC; i < AIR_ACL_FIELD_TYPE_LAST; i++)

-            {

-                if((1 << i) & rule.mask.fieldmap)

-                {

-                    switch (i)

-                    {

-                        case AIR_ACL_DMAC:

-                            AIR_PRINT("\t dmac: ");

-                            AIR_PRINT("%02x-%02x-%02x-%02x-%02x-%02x",

-                            rule.key.dmac[0], rule.key.dmac[1], rule.key.dmac[2],

-                            rule.key.dmac[3], rule.key.dmac[4], rule.key.dmac[5]);

-                            AIR_PRINT(", dmac-mask: ");

-                            AIR_PRINT("%02x-%02x-%02x-%02x-%02x-%02x\n",

-                            rule.mask.dmac[0], rule.mask.dmac[1], rule.mask.dmac[2],

-                            rule.mask.dmac[3], rule.mask.dmac[4], rule.mask.dmac[5]);

-                            break;

-                        case AIR_ACL_SMAC:

-                            AIR_PRINT("\t smac: ");

-                            AIR_PRINT("%02x-%02x-%02x-%02x-%02x-%02x",

-                            rule.key.smac[0], rule.key.smac[1], rule.key.smac[2],

-                            rule.key.smac[3], rule.key.smac[4], rule.key.smac[5]);

-                            AIR_PRINT(", smac-mask: ");

-                            AIR_PRINT("%02x-%02x-%02x-%02x-%02x-%02x\n",

-                            rule.mask.smac[0], rule.mask.smac[1], rule.mask.smac[2],

-                            rule.mask.smac[3], rule.mask.smac[4], rule.mask.smac[5]);

-                            break;

-                        case AIR_ACL_ETYPE:

-                            AIR_PRINT("\t etype: 0x%x, etype-mask: 0x%x\n", rule.key.etype, rule.mask.etype);

-                            break;

-                        case AIR_ACL_STAG:

-                            AIR_PRINT("\t stag: 0x%x, stag-mask: 0x%x\n", rule.key.stag, rule.mask.stag);

-                            break;

-                        case AIR_ACL_CTAG:

-                            AIR_PRINT("\t ctag: 0x%x, ctag-mask: 0x%x\n", rule.key.ctag, rule.mask.ctag);

-                            break;

-                        case AIR_ACL_DPORT:

-                            AIR_PRINT("\t dport: 0x%x, dport-mask: 0x%x\n", rule.key.dport, rule.mask.dport);

-                            break;

-                        case AIR_ACL_SPORT:

-                            AIR_PRINT("\t sport: 0x%x, sport-mask: 0x%x\n", rule.key.sport, rule.mask.sport);

-                            break;

-                        case AIR_ACL_UDF:

-                            AIR_PRINT("\t udf: 0x%x, udf-mask: 0x%x\n", rule.key.udf, rule.mask.udf);

-                            break;

-                        case AIR_ACL_DIP:

-                            if (0 == rule.key.isipv6)

-                            {

-                                AIR_PRINT("\t dip: ");

-                                AIR_PRINT("%d.%d.%d.%d",

-                                ((rule.key.dip[0])&0xFF000000)>>24,((rule.key.dip[0])&0x00FF0000)>>16,

-                                ((rule.key.dip[0])&0x0000FF00)>>8, ((rule.key.dip[0])&0x000000FF));

-                                AIR_PRINT(", dip-mask: ");

-                                AIR_PRINT("%d.%d.%d.%d\n ",

-                                ((rule.mask.dip[0])&0xFF000000)>>24,((rule.mask.dip[0])&0x00FF0000)>>16,

-                                ((rule.mask.dip[0])&0x0000FF00)>>8, ((rule.mask.dip[0])&0x000000FF));

-                            }

-                            else

-                            {

-                                for(i=0; i<4; i++){

-                                    tmp_ip[i] = (rule.key.dip[3] >> (8*(3-i))) & 0xff;

-                                    AIR_PRINT("get tmp_ip[%d]=0x%x\n", i, tmp_ip[i]);

-                                }

-                                for(i=4; i<8; i++){

-                                    tmp_ip[i] = (rule.key.dip[2] >> (8*(7-i))) & 0xff;

-                                }

-                                for(i=8; i<12; i++){

-                                    tmp_ip[i] = (rule.key.dip[1] >> (8*(11-i))) & 0xff;

-                                }

-                                for(i=12; i<16; i++){

-                                    tmp_ip[i] = (rule.key.dip[0] >> (8*(15-i))) & 0xff;

-                                }

-

-                                AIR_PRINT("\t dip: ");

-                                AIR_PRINT("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",

-                                    tmp_ip[0], tmp_ip[1],tmp_ip[2], tmp_ip[3],tmp_ip[4], tmp_ip[5],tmp_ip[6], tmp_ip[7],

-                                    tmp_ip[8], tmp_ip[9],tmp_ip[10], tmp_ip[11],tmp_ip[12], tmp_ip[13],tmp_ip[14], tmp_ip[15]);

-                                for(i=0; i<4; i++){

-                                    tmp_ip[i] = (rule.mask.dip[3] >> (8*(3-i))) & 0xff;

-                                }

-                                for(i=4; i<8; i++){

-                                    tmp_ip[i] = (rule.mask.dip[2] >> (8*(7-i))) & 0xff;

-                                }

-                                for(i=8; i<12; i++){

-                                    tmp_ip[i] = (rule.mask.dip[1] >> (8*(11-i))) & 0xff;

-                                }

-                                for(i=12; i<16; i++){

-                                    tmp_ip[i] = (rule.mask.dip[0] >> (8*(15-i))) & 0xff;

-                                }

-                                AIR_PRINT(", dip-mask: ");

-                                AIR_PRINT("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n",

-                                    tmp_ip[0], tmp_ip[1],tmp_ip[2], tmp_ip[3],tmp_ip[4], tmp_ip[5],tmp_ip[6], tmp_ip[7],

-                                    tmp_ip[8], tmp_ip[9],tmp_ip[10], tmp_ip[11],tmp_ip[12], tmp_ip[13],tmp_ip[14], tmp_ip[15]);

-                            }

-                            break;

-                        case AIR_ACL_SIP:

-                            if (0 == rule.key.isipv6)

-                            {

-                                AIR_PRINT("\t sip: ");

-                                AIR_PRINT("%d.%d.%d.%d ",

-                                ((rule.key.sip[0])&0xFF000000)>>24,((rule.key.sip[0])&0x00FF0000)>>16,

-                                ((rule.key.sip[0])&0x0000FF00)>>8, ((rule.key.sip[0])&0x000000FF));

-                                AIR_PRINT(", sip-mask: ");

-                                AIR_PRINT("%d.%d.%d.%d\n ",

-                                ((rule.mask.sip[0])&0xFF000000)>>24,((rule.mask.sip[0])&0x00FF0000)>>16,

-                                ((rule.mask.sip[0])&0x0000FF00)>>8, ((rule.mask.sip[0])&0x000000FF));

-                            }

-                            else

-                            {

-                                for(i=0; i<4; i++){

-                                    tmp_ip[i] = (rule.key.sip[3] >> (8*(3-i))) & 0xff;

-                                }

-                                for(i=4; i<8; i++){

-                                    tmp_ip[i] = (rule.key.sip[2] >> (8*(7-i))) & 0xff;

-                                }

-                                for(i=8; i<12; i++){

-                                    tmp_ip[i] = (rule.key.sip[1] >> (8*(11-i))) & 0xff;

-                                }

-                                for(i=12; i<16; i++){

-                                    tmp_ip[i] = (rule.key.sip[0] >> (8*(15-i))) & 0xff;

-                                }

-                                AIR_PRINT("\t sip: ");

-                                AIR_PRINT("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",

-                                    tmp_ip[0], tmp_ip[1],tmp_ip[2], tmp_ip[3],tmp_ip[4], tmp_ip[5],tmp_ip[6], tmp_ip[7],

-                                    tmp_ip[8], tmp_ip[9],tmp_ip[10], tmp_ip[11],tmp_ip[12], tmp_ip[13],tmp_ip[14], tmp_ip[15]);

-                                for(i=0; i<4; i++){

-                                    tmp_ip[i] = (rule.mask.sip[3] >> (8*(3-i))) & 0xff;

-                                }

-                                for(i=4; i<8; i++){

-                                    tmp_ip[i] = (rule.mask.sip[2] >> (8*(7-i))) & 0xff;

-                                }

-                                for(i=8; i<12; i++){

-                                    tmp_ip[i] = (rule.mask.sip[1] >> (8*(11-i))) & 0xff;

-                                }

-                                for(i=12; i<16; i++){

-                                    tmp_ip[i] = (rule.mask.sip[0] >> (8*(15-i))) & 0xff;

-                                }

-                                AIR_PRINT(", sip-mask: ");

-                                AIR_PRINT("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n",

-                                    tmp_ip[0], tmp_ip[1],tmp_ip[2], tmp_ip[3],tmp_ip[4], tmp_ip[5],tmp_ip[6], tmp_ip[7],

-                                    tmp_ip[8], tmp_ip[9],tmp_ip[10], tmp_ip[11],tmp_ip[12], tmp_ip[13],tmp_ip[14], tmp_ip[15]);

-                            }

-                            break;

-                        case AIR_ACL_DSCP:

-                            AIR_PRINT("\t dscp: 0x%x, dscp-mask: 0x%x\n", rule.key.dscp, rule.mask.dscp);

-                            break;

-                        case AIR_ACL_PROTOCOL:

-                            AIR_PRINT("\t protocol: 0x%x, protocol-mask: 0x%x\n", rule.key.protocol, rule.mask.protocol);

-                            break;

-                        case AIR_ACL_FLOW_LABEL:

-                            AIR_PRINT("\t flow-label: 0x%x, flow-label-mask: 0x%x\n", rule.key.flow_label, rule.mask.flow_label);

-                            break;

-                        default:

-                            AIR_PRINT("error\n");

-                            break;

-                    }

-                }

-            }

-        }

-        else

-        {

-            AIR_PRINT("Entry is Invalid.\n");

-        }

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doAclRmvRule(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T rule_idx = 0;

-

-    if(1 == argc)

-    {

-        /* acl del rule <idx(0..127)> */

-        rule_idx = _strtoul(argv[0], NULL, 0);

-        ret = air_acl_delRule(0, rule_idx);

-        if(ret < AIR_E_LAST)

-            AIR_PRINT("Delete ACL Rule(%u): %s\n", rule_idx, air_error_getString(ret));

-    }

-    else if(0 == argc)

-    {

-        /* acl clear rule */

-        ret = air_acl_clearRule(0);

-        if(ret < AIR_E_LAST)

-            AIR_PRINT("Clear ACL Rule: %s\n", air_error_getString(ret));

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doAclUdfRule(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T rule_idx;

-    AIR_ACL_UDF_RULE_T rule;

-    C8_T start_addr[8][12]=

-    {

-        "MAC header",

-        "L2 payload",

-        "IPv4 header",

-        "IPv6 header",

-        "L3 payload",

-        "TCP header",

-        "UDP header",

-        "L4 payload"

-    };

-    C8_T str_temp[AIR_MAX_NUM_OF_PORTS+1];

-

-    memset(&rule, 0, sizeof(AIR_ACL_UDF_RULE_T));

-    if(7 == argc)

-    {

-        /* acl set rule <idx(0..255)> <mode(0:pattern, 1:threshold)> [ <pat(4'hex)> <mask(4'hex)> | <low(4'hex)> <high(4'hex)> ] <start(0:MAC,1:ether,2:IP,3:IP_data,4:TCP/UDP,5:TCP/UDP data,6:IPv6)> <offset(0..62,unit:2 bytes)> <portmap(7'bin)> */

-        rule_idx = _strtoul(argv[0], NULL, 0);

-        rule.cmp_sel = _strtoul(argv[1], NULL, 2);

-        if(AIR_ACL_RULE_CMP_SEL_PATTERN == rule.cmp_sel)

-        {

-            rule.pattern = _strtoul(argv[2], NULL, 16);

-            rule.mask = _strtoul(argv[3], NULL, 16);

-        }

-        else

-        {

-            rule.low_threshold = _strtoul(argv[2], NULL, 16);

-            rule.high_threshold = _strtoul(argv[3], NULL, 16);

-        }

-        rule.offset_format = _strtoul(argv[4], NULL, 0);

-        rule.offset = _strtoul(argv[5], NULL, 0);

-        rule.portmap = _strtoul(argv[6], NULL, 2);

-        rule.valid = TRUE;

-        ret = air_acl_setUdfRule(0, rule_idx, rule);

-        if(ret < AIR_E_LAST)

-            AIR_PRINT("Set ACL UDF Rule(%u): %s\n", rule_idx, air_error_getString(ret));

-    }

-    else if(1 == argc)

-    {

-        rule_idx = _strtoul(argv[0], NULL, 0);

-        ret = air_acl_getUdfRule(0, rule_idx, &rule);

-        if(ret < AIR_E_LAST)

-            AIR_PRINT("Get ACL UDF Rule(%u): %s\n", rule_idx, air_error_getString(ret));

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    if(AIR_E_OK == ret)

-    {

-        if(TRUE == rule.valid)

-        {

-            AIR_PRINT("\tMode          : %s\n", (AIR_ACL_RULE_CMP_SEL_PATTERN == rule.cmp_sel)?"Pattern":"Threshold");

-            if(AIR_ACL_RULE_CMP_SEL_PATTERN == rule.cmp_sel)

-            {

-                AIR_PRINT("\tPattern       : 0x%04X\n", rule.pattern);

-                AIR_PRINT("\tMask          : 0x%04X\n", rule.mask);

-            }

-            else

-            {

-                AIR_PRINT("\tLow Threshold : 0x%04X\n", rule.low_threshold);

-                AIR_PRINT("\tHigh Threshold: 0x%04X\n", rule.high_threshold);

-            }

-            AIR_PRINT("\tOffset Start  : %s\n", start_addr[rule.offset_format]);

-            AIR_PRINT("\tOffset        : %u %s\n", rule.offset*2, (0==rule.offset)?"Byte":"Bytes");

-            _hex2bitstr(rule.portmap, str_temp, AIR_MAX_NUM_OF_PORTS+1);

-            AIR_PRINT("\tPortmap[0:6]  : %s\n", str_temp);

-        }

-        else

-        {

-            AIR_PRINT("Entry is Invalid.\n");

-        }

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doAclRmvUdfRule(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T rule_idx;

-

-    if(1 == argc)

-    {

-        /* acl del udfRule <idx(0..15)> */

-        rule_idx = _strtoul(argv[0], NULL, 0);

-        ret = air_acl_delUdfRule(0, rule_idx);

-        if(ret < AIR_E_LAST)

-            AIR_PRINT("Delete ACL UDF Rule(%u): %s\n", rule_idx, air_error_getString(ret));

-    }

-    else if(0 == argc)

-    {

-        /* acl clear udfRule */

-        ret = air_acl_clearUdfRule(0);

-        if(ret < AIR_E_LAST)

-            AIR_PRINT("Clear ACL UDF Rule: %s\n", air_error_getString(ret));

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doAclAction(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T argi = 0;

-    UI32_T act_idx;

-    AIR_ACL_ACTION_T act;

-    UI32_T redirect, trtcm, fwd;

-    C8_T fwding[AIR_ACL_ACT_FWD_LAST][25] =

-    {

-        "Default",

-        "Default",

-        "Default",

-        "Default",

-        "Default & CPU excluded",

-        "Default & CPU included",

-        "CPU only",

-        "Drop"

-    };

-    C8_T trtcm_usr[AIR_ACL_ACT_USR_TCM_LAST][8] =

-    {

-        "Default",

-        "Green",

-        "Yellow",

-        "Red"

-    };

-    C8_T egtag[AIR_ACL_ACT_EGTAG_LAST][11] =

-    {

-        "Default",

-        "Consistent",

-        "",

-        "",

-        "Untag",

-        "Swap",

-        "Tag",

-        "Stack"

-    };

-    C8_T str_temp[20];

-

-    memset(&act, 0, sizeof(AIR_ACL_ACTION_T));

-    if(2 < argc)

-    {

-        /* acl set action <idx(0..127)>

-        [ forward <forward(0:Default,4:Exclude CPU,5:Include CPU,6:CPU only,7:Drop)> ]

-        [ egtag <egtag(0:Default,1:Consistent,4:Untag,5:Swap,6:Tag,7:Stack)> ]

-        [ mirrormap <mirrormap(2'bin)> ]

-        [ priority <priority(0..7)> ]

-        [ redirect <redirect(0:Dst,1:Vlan)> <portmap(7'bin)> ]

-        [ leaky_vlan <leaky_vlan(1:En,0:Dis)> ]

-        [ cnt_idx <cnt_idx(0..63)> ]

-        [ rate_idx <rate_idx(0..31)> ]

-        [ attack_idx <attack_idx(0..95)> ]

-        [ vid <vid(0..4095)> ]

-        [ manage <manage(1:En,0:Dis)> ]

-        [ bpdu <bpdu(1:En,0:Dis)> ]

-        [ class <class(0:Original,1:Defined)>[0..7] ]

-        [ drop_pcd <drop_pcd(0:Original,1:Defined)> [red <red(0..7)>][yellow <yellow(0..7)>][green <green(0..7)>] ]

-        [ color <color(0:Defined,1:Trtcm)> [ <defined_color(0:Dis,1:Green,2:Yellow,3:Red)> | <trtcm_idx(0..31)> ] ]*/

-

-        act_idx = _strtoul(argv[argi++], NULL, 0);

-        if(0 == _strcmp(argv[argi], "forward"))

-        {

-            argi++;

-            fwd = _strtoul(argv[argi++], NULL, 0);

-            act.fwd_en = TRUE;

-            act.fwd = fwd;

-        }

-

-        if(0 == _strcmp(argv[argi], "egtag"))

-        {

-            argi++;

-            act.egtag_en = TRUE;

-            act.egtag = _strtoul(argv[argi++], NULL, 0);

-        }

-

-        if(0 == _strcmp(argv[argi], "mirrormap"))

-        {

-            argi++;

-            act.mirrormap = _strtoul(argv[argi++], NULL, 2);

-        }

-

-        if(0 == _strcmp(argv[argi], "priority"))

-        {

-            argi++;

-            act.pri_user_en = TRUE;

-            act.pri_user= _strtoul(argv[argi++], NULL, 0);

-        }

-

-        if(0 == _strcmp(argv[argi], "redirect"))

-        {

-            argi++;

-            redirect = _strtoul(argv[argi++], NULL, 0);

-            if(0 ==  redirect)

-            {

-                act.port_en = TRUE;

-                act.dest_port_sel = TRUE;

-                act.portmap = _strtoul(argv[argi++], NULL, 2);

-            }

-            else

-            {

-                act.port_en = TRUE;

-                act.vlan_port_sel = TRUE;

-                act.portmap = _strtoul(argv[argi++], NULL, 2);

-            }

-        }

-

-        if(0 == _strcmp(argv[argi], "leaky_vlan"))

-        {

-            argi++;

-            act.lyvlan_en = TRUE;

-            act.lyvlan = _strtoul(argv[argi++], NULL, 0);

-        }

-

-        /* ACL event counter */

-        if(0 == _strcmp(argv[argi], "cnt_idx"))

-        {

-            argi++;

-            act.cnt_en = TRUE;

-            act.cnt_idx = _strtol(argv[argi++], NULL, 0);

-        }

-

-        if(0 == _strcmp(argv[argi], "rate_idx"))

-        {

-            argi++;

-            act.rate_en = TRUE;

-            act.rate_idx = _strtol(argv[argi++], NULL, 0);

-        }

-

-        if(0 == _strcmp(argv[argi], "attack_idx"))

-        {

-            argi++;

-            act.attack_en = TRUE;

-            act.attack_idx = _strtol(argv[argi++], NULL, 0);

-        }

-

-        if(0 == _strcmp(argv[argi], "vid"))

-        {

-            argi++;

-            act.vlan_en = TRUE;

-            act.vlan_idx = _strtol(argv[argi++], NULL, 0);

-        }

-

-        /* Management frame */

-        if(0 == _strcmp(argv[argi], "manage"))

-        {

-            argi++;

-            act.mang = _strtoul(argv[argi++], NULL, 2);

-        }

-

-        if(0 == _strcmp(argv[argi], "bpdu"))

-        {

-            argi++;

-            act.bpdu = _strtoul(argv[argi++], NULL, 2);

-        }

-

-        /* DSCP class remap */

-        if(0 == _strcmp(argv[argi], "class"))

-        {

-            argi++;

-            act.trtcm_en = TRUE;

-            act.trtcm.cls_slr_sel = _strtoul(argv[argi++], NULL, 2);

-            if(TRUE == act.trtcm.cls_slr_sel)

-            {

-                act.trtcm.cls_slr = _strtoul(argv[argi++], NULL, 0);

-            }

-        }

-        if(0 == _strcmp(argv[argi], "drop_pcd"))

-        {

-            argi++;

-            act.trtcm_en = TRUE;

-            act.trtcm.drop_pcd_sel = _strtoul(argv[argi++], NULL, 2);

-            if(TRUE == act.trtcm.drop_pcd_sel)

-            {

-                if(0 == _strcmp(argv[argi], "red"))

-                {

-                    argi++;

-                    act.trtcm.drop_pcd_r= _strtoul(argv[argi++], NULL, 0);

-                }

-                if(0 == _strcmp(argv[argi], "yellow"))

-                {

-                    argi++;

-                    act.trtcm.drop_pcd_y= _strtoul(argv[argi++], NULL, 0);

-                }

-                if(0 == _strcmp(argv[argi], "green"))

-                {

-                    argi++;

-                    act.trtcm.drop_pcd_g= _strtoul(argv[argi++], NULL, 0);

-                }

-            }

-        }

-

-        /* trTCM */

-        if(0 == _strcmp(argv[argi], "color"))

-        {

-            argi++;

-            act.trtcm_en = TRUE;

-            act.trtcm.tcm_sel = _strtoul(argv[argi++], NULL, 2);

-            trtcm = _strtoul(argv[argi++], NULL, 0);

-            if(FALSE == act.trtcm.tcm_sel)

-            {

-                act.trtcm.usr_tcm = trtcm;

-            }

-            else

-            {

-                act.trtcm.tcm_idx = trtcm;

-            }

-        }

-        ret = air_acl_setAction(0, act_idx, act);

-        AIR_PRINT("Set ACL Action(%u): %s\n", act_idx, air_error_getString(ret));

-    }

-    else if(1 == argc)

-    {

-        /* acl get action <idx(0..127)> */

-        act_idx = _strtoul(argv[argi++], NULL, 0);

-        ret = air_acl_getAction(0, act_idx, &act);

-        AIR_PRINT("Get ACL Action(%u): %s\n", act_idx, air_error_getString(ret));

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    if(AIR_E_OK == ret)

-    {

-        if(TRUE == act.fwd_en)

-        {

-            AIR_PRINT("\t Forwarding           : %s\n", fwding[act.fwd]);

-        }

-

-        if(TRUE == act.egtag_en)

-        {

-            AIR_PRINT("\t Egress tag           : %s\n", egtag[act.egtag]);

-        }

-

-        if(act.mirrormap)

-        {

-            AIR_PRINT("\t Mirror Session Map   : %u\n", act.mirrormap);

-        }

-

-        if(TRUE == act.pri_user_en)

-        {

-            AIR_PRINT("\t User Priority        : %u\n", act.pri_user);

-        }

-

-        if(TRUE == act.port_en)

-        {

-            _hex2bitstr(act.portmap, str_temp, AIR_MAX_NUM_OF_PORTS+1);

-            if(TRUE == act.dest_port_sel)

-            {

-                AIR_PRINT("\t Destination Port[0:6]: %s\n", str_temp);

-            }

-            else

-            {

-                AIR_PRINT("\t VLAN Port[0:6]       : %s\n", str_temp);

-            }

-        }

-

-        if(TRUE == act.lyvlan_en)

-        {

-            AIR_PRINT("\t Leaky VLAN           : %s\n", (TRUE == act.lyvlan)?"Enable":"Disable");

-        }

-        AIR_PRINT("\t Management Frame     : %s\n", (TRUE == act.mang)?"Enable":"Disable");

-        AIR_PRINT("\t BPDU Frame           : %s\n", (TRUE == act.bpdu)?"Enable":"Disable");

-

-        if(TRUE == act.cnt_en)

-        {

-            AIR_PRINT("\t Event Index          : %u\n", act.cnt_idx);

-        }

-

-        /* trTCM*/

-        if(TRUE == act.trtcm_en)

-        {

-            if(TRUE == act.trtcm.cls_slr_sel)

-            {

-                AIR_PRINT("\t Class Selector Remap : %u\n", act.trtcm.cls_slr);

-            }

-            else

-            {

-                AIR_PRINT("\t Class Selector Remap : %s\n", "Disable");

-            }

-            if(TRUE == act.trtcm.drop_pcd_sel)

-            {

-                AIR_PRINT("\t Drop Precedence Remap(Red): %u\n", act.trtcm.drop_pcd_r);

-                AIR_PRINT("\t Drop Precedence Remap(Yel): %u\n", act.trtcm.drop_pcd_y);

-                AIR_PRINT("\t Drop Precedence Remap(Gre): %u\n", act.trtcm.drop_pcd_g);

-            }

-            else

-            {

-                AIR_PRINT("\t Drop Precedence Remap: %s\n", "Disable");

-            }

-

-            if(TRUE == act.trtcm.tcm_sel)

-            {

-                AIR_PRINT("\t trTCM Meter Index    : %u\n", act.trtcm.tcm_idx);

-            }

-            else

-            {

-                AIR_PRINT("\t trTCM User Defined   : %s\n", trtcm_usr[act.trtcm.usr_tcm]);

-            }

-        }

-        /* rate control */

-        if(TRUE == act.rate_en)

-        {

-            AIR_PRINT("\t Rate Control Index   : %u\n", act.rate_idx);

-        }

-

-        if(TRUE == act.attack_en)

-        {

-            AIR_PRINT("\t Attack Rate Control Index: %u\n", act.attack_idx);

-        }

-

-        if(TRUE == act.vlan_en)

-        {

-            AIR_PRINT("\t ACL VLAN Index       : %u\n", act.vlan_idx);

-        }

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doAclRmvAction(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T act_idx;

-

-    if(1 == argc)

-    {

-        /* acl del action <idx(0..127)> */

-        act_idx = _strtoul(argv[0], NULL, 0);

-        ret = air_acl_delAction(0, act_idx);

-        if(ret < AIR_E_LAST)

-            AIR_PRINT("Delete ACL Action(%u): %s\n", act_idx, air_error_getString(ret));

-    }

-    else if(0 == argc)

-    {

-        /* acl clear action */

-        ret = air_acl_clearAction(0);

-        if(ret < AIR_E_LAST)

-            AIR_PRINT("Clear ACL Action: %s\n", air_error_getString(ret));

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doAclTrtcm(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T argi = 0;

-    UI32_T tcm_idx;

-    AIR_ACL_TRTCM_T tcm;

-

-    memset(&tcm, 0, sizeof(AIR_ACL_TRTCM_T));

-    if(5 == argc)

-    {

-        /* acl set trtcm <idx(0..31)> <cir(4'hex)> <pir(4'hex)> <cbs(4'hex)> <pbs(4'hex)> */

-        tcm_idx = _strtoul(argv[argi++], NULL, 0);

-        tcm.cir = _strtoul(argv[argi++], NULL, 16);

-        tcm.pir = _strtoul(argv[argi++], NULL, 16);

-        tcm.cbs = _strtoul(argv[argi++], NULL, 16);

-        tcm.pbs = _strtoul(argv[argi++], NULL, 16);

-

-        ret = air_acl_setTrtcm(0, tcm_idx, tcm);

-        if(ret < AIR_E_LAST)

-            AIR_PRINT("Set ACL trTCM(%u): %s\n", tcm_idx, air_error_getString(ret));

-    }

-    else if(1 == argc)

-    {

-        /* acl get trtcm <idx(1..31)> */

-        tcm_idx = _strtoul(argv[argi++], NULL, 0);

-        ret = air_acl_getTrtcm(0, tcm_idx, &tcm);

-        if(ret < AIR_E_LAST)

-            AIR_PRINT("Get ACL trTCM(%u): %s\n", tcm_idx, air_error_getString(ret));

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    if(AIR_E_OK == ret)

-    {

-        AIR_PRINT("\t CIR: 0x%04X(unit:64Kbps)\n", tcm.cir);

-        AIR_PRINT("\t PIR: 0x%04X(unit:64Kbps)\n", tcm.pir);

-        AIR_PRINT("\t CBS: 0x%04X(unit:Byte)\n", tcm.cbs);

-        AIR_PRINT("\t PBS: 0x%04X(unit:Byte)\n", tcm.pbs);

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doAclTrtcmEn(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    BOOL_T state = FALSE;

-

-    if (1 == argc)

-    {

-        /* acl set trtcmEn <en(1:En,0:Dis)> */

-        state = _strtol(argv[0], NULL, 10);

-        ret = air_acl_setTrtcmEnable(0, state);

-        AIR_PRINT("Set trTCM State ");

-    }

-    else if (0 == argc)

-    {

-        /* acl get trtcmEn */

-        ret = air_acl_getTrtcmEnable(0, &state);

-        AIR_PRINT("Get trTCM State ");

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    if (ret == AIR_E_OK)

-    {

-        AIR_PRINT(": %s\n", (TRUE == state) ? "Enable" : "Disable");

-    }

-    else

-    {

-        AIR_PRINT("Fail!\n");

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doAclRmvTrtcm(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T tcm_idx;

-

-    if(1 == argc)

-    {

-        /* acl del trtcm <idx(1..31)> */

-        tcm_idx = _strtoul(argv[0], NULL, 0);

-        ret = air_acl_delTrtcm(0, tcm_idx);

-        if(ret < AIR_E_LAST)

-            AIR_PRINT("Delete ACL TRTCM(%u): %s\n", tcm_idx, air_error_getString(ret));

-    }

-    else if(0 == argc)

-    {

-        /* acl clear trtcm */

-        ret = air_acl_clearTrtcm(0);

-        if(ret < AIR_E_LAST)

-            AIR_PRINT("Clear ACL TRTCM: %s\n", air_error_getString(ret));

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doAclPortEn(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T argi=0;

-    UI32_T port = 0;

-    BOOL_T en;

-

-    if(2 == argc)

-    {

-        /* acl set portEn <port(0..6)> <en(1:En,0:Dis)> */

-        port = _strtoul(argv[argi++], NULL, 0);

-        en = _strtoul(argv[argi++], NULL, 2);

-        ret = air_acl_setPortEnable(0, port, en);

-        AIR_PRINT("Set Port:%u ACL function ", port);

-    }

-    else if(1 == argc)

-    {

-        /* acl get portEn <port(0..6)> */

-        port = _strtoul(argv[argi++], NULL, 0);

-        ret = air_acl_getPortEnable(0, port, &en);

-        AIR_PRINT("Get Port:%u ACL function ", port);

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    if(ret == AIR_E_OK)

-    {

-        AIR_PRINT(": %s\n", (TRUE == en)?"Enable":"Disable");

-    }

-    else

-    {

-        AIR_PRINT("Fail!\n");

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doAclDropEn(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T argi=0;

-    UI32_T port = 0;

-    BOOL_T en;

-

-    if(2 == argc)

-    {

-        /* acl set dropEn <port(0..6)> <en(1:En,0:Dis)> */

-        port = _strtoul(argv[argi++], NULL, 0);

-        en = _strtoul(argv[argi++], NULL, 2);

-        ret = air_acl_setDropEnable(0, port, en);

-        AIR_PRINT("Set ACL Drop precedence ");

-    }

-    else if(1 == argc)

-    {

-        /* acl set dropEn <port(0..6)> */

-        port = _strtoul(argv[argi++], NULL, 0);

-        ret = air_acl_getDropEnable(0, port, &en);

-        AIR_PRINT("Get ACL Drop precedence ");

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    if(ret == AIR_E_OK)

-    {

-        AIR_PRINT("(Port %u):%s\n",

-                port,

-                (TRUE == en)?"Enable":"Disable");

-    }

-    else

-    {

-        AIR_PRINT("Fail!\n");

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doAclDropThrsh(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T argi=0;

-    UI32_T port = 0;

-    AIR_ACL_DP_COLOR_T color;

-    UI8_T queue;

-    UI32_T high, low;

-    C8_T dp_color[AIR_ACL_DP_COLOR_LAST][7] =

-    {

-        "Green",

-        "Yellow",

-        "Red"

-    };

-

-    if(5 == argc)

-    {

-        /* acl set dropThrsh <port(0..6)> <color(0:green,1:yellow,2:red)> <queue(0..7)> <high(0..2047)> <low(0..2047) */

-        port = _strtoul(argv[argi++], NULL, 0);

-        color = _strtoul(argv[argi++], NULL, 0);

-        queue = _strtoul(argv[argi++], NULL, 0);

-        high = _strtoul(argv[argi++], NULL, 0);

-        low = _strtoul(argv[argi++], NULL, 0);

-        ret = air_acl_setDropThreshold(0, port, color, queue, high, low);

-        AIR_PRINT("Set ACL Drop precedence ");

-    }

-    else if(3 == argc)

-    {

-        /* acl get dropThrsh <port(0..6)> <color(0:green,1:yellow,2:red)> <queue(0..7)> */

-        port = _strtoul(argv[argi++], NULL, 0);

-        color = _strtoul(argv[argi++], NULL, 0);

-        queue = _strtoul(argv[argi++], NULL, 0);

-        ret = air_acl_getDropThreshold(0, port, color, queue, &high, &low);

-        AIR_PRINT("Get ACL Drop precedence ");

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    if(ret == AIR_E_OK)

-    {

-        AIR_PRINT("(Port %u, color:%s, queue:%u):\n",

-                port,

-                dp_color[color],

-                queue);

-        AIR_PRINT("\tHigh Threshold :%u\n", high);

-        AIR_PRINT("\tLow Threshold  :%u\n", low);

-    }

-    else

-    {

-        AIR_PRINT("Fail!\n");

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doAclDropPbb(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T argi=0;

-    UI32_T port = 0;

-    AIR_ACL_DP_COLOR_T color;

-    UI8_T queue;

-    UI32_T pbb;

-    C8_T dp_color[AIR_ACL_DP_COLOR_LAST][7] =

-    {

-        "Green",

-        "Yellow",

-        "Red"

-    };

-

-    if(4 == argc)

-    {

-        /* acl set dropPbb <port(0..6)> <color(0:green,1:yellow,2:red)> <queue(0..7)> <probability(0..1023) */

-        port = _strtoul(argv[argi++], NULL, 0);

-        color = _strtoul(argv[argi++], NULL, 0);

-        queue = _strtoul(argv[argi++], NULL, 0);

-        pbb = _strtoul(argv[argi++], NULL, 0);

-        ret = air_acl_setDropProbability(0, port, color, queue, pbb);

-        AIR_PRINT("Set ACL Drop precedence ");

-    }

-    else if(3 == argc)

-    {

-        /* acl get dropPbb <port(0..6)> <color(0:green,1:yellow,2:red)> <queue(0..7)> */

-        port = _strtoul(argv[argi++], NULL, 0);

-        color = _strtoul(argv[argi++], NULL, 0);

-        queue = _strtoul(argv[argi++], NULL, 0);

-        ret = air_acl_getDropProbability(0, port, color, queue, &pbb);

-        AIR_PRINT("Get ACL Drop precedence ");

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    if(ret == AIR_E_OK)

-    {

-        AIR_PRINT("(Port %u, color:%s, queue %u):\n",

-                port,

-                dp_color[color],

-                queue);

-        AIR_PRINT("\tDrop probability:%u(unit=1/1023)\n", pbb);

-    }

-    else

-    {

-        AIR_PRINT("Fail!\n");

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doAclMeter(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T argi = 0;

-    UI32_T meter_idx, state, rate;

-

-    if(3 == argc)

-    {

-        /* acl set meter <idx(0..31)> <en(1:En,0:Dis)> <rate(0..65535)>

-           Note: Limit rate = rate * 64Kbps */

-        meter_idx = _strtoul(argv[argi++], NULL, 0);

-        state = _strtoul(argv[argi++], NULL, 2);

-        rate = _strtoul(argv[argi++], NULL, 0);

-

-        ret = air_acl_setMeterTable(0, meter_idx, state, rate);

-        if(ret < AIR_E_LAST)

-            AIR_PRINT("Set ACL Meter(%u): %s\n", meter_idx, air_error_getString(ret));

-    }

-    else if(1 == argc)

-    {

-        /* acl get meter <idx(0..31)> */

-        meter_idx = _strtoul(argv[argi++], NULL, 0);

-        ret = air_acl_getMeterTable(0, meter_idx, &state, &rate);

-        if(ret < AIR_E_LAST)

-            AIR_PRINT("Get ACL Meter(%u): %s\n", meter_idx, air_error_getString(ret));

-    }

-    else

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    if(AIR_E_OK == ret)

-    {

-        AIR_PRINT("\t State: %s\n", (TRUE == state)?"Enable":"Disable");

-        if(TRUE == state)

-        {

-            AIR_PRINT("\t Rate : %u(unit:64Kbps)\n", rate);

-        }

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doAclDump(

-    UI32_T argc,

-    C8_T *argv[])

-{

-    AIR_ERROR_NO_T ret = AIR_E_OK;

-    UI32_T i, cnt = 0;

-    AIR_ACL_CTRL_T ctrl;

-

-    if(0 != argc)

-    {

-        AIR_PRINT("Unrecognized command.\n");

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    for(i=0; i<ACL_MAX_RULE_NUM; i++)

-    {

-        memset(&ctrl, 0, sizeof(AIR_ACL_CTRL_T));

-        ret = air_acl_getRuleCtrl(0, i, &ctrl);

-        if(AIR_E_OK == ret)

-        {

-            if(TRUE == ctrl.rule_en)

-            {

-                cnt++;

-                AIR_PRINT("\t Entry-%d vaild\n", i);

-            }

-        }

-    }

-    if(0 == cnt)

-    {

-        AIR_PRINT("\t No entry vaild\n");

-    }

-

-    return ret;

-}

-

-static AIR_ERROR_NO_T

-doAclSet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(aclSetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doAclGet(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(aclGetCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doAclDel(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(aclDelCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doAclClear(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(aclClearCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-doAcl(

-        UI32_T argc,

-        C8_T *argv[])

-{

-    return subcmd(aclCmds, argc, argv);

-}

-

-static AIR_ERROR_NO_T

-subcmd(

-        const AIR_CMD_T tab[],

-        UI32_T argc,

-        C8_T *argv[])

-{

-    const AIR_CMD_T *cmdp;

-    I32_T found = 0;

-    I32_T i, len;

-

-    if (argc < 1)

-    {

-        goto print_out_cmds;

-    }

-

-    for (cmdp = tab; cmdp->name != NULL; cmdp++)

-    {

-        if (strlen(argv[0]) == strlen(cmdp->name))

-        {

-            if (strncmp(argv[0], cmdp->name, strlen(argv[0])) == 0)

-            {

-                found = 1;

-                break;

-            }

-        }

-    }

-

-    if(!found)

-    {

-        C8_T buf[66];

-

-print_out_cmds:

-        AIR_PRINT("valid subcommands:\n");

-        memset(buf, ' ', sizeof(buf));

-        buf[64] = '\n';

-        buf[65] = '\0';

-

-        for (i=0, cmdp = tab; cmdp->name != NULL; cmdp++)

-        {

-            len = strlen(cmdp->name);

-            strncpy(&buf[i*16], cmdp->name, (len > 16) ? 16 : len);

-            if(3 == i)

-            {

-                AIR_PRINT("%s\n", buf);

-                memset(buf, ' ', sizeof(buf));

-                buf[64] = '\n';

-                buf[65] = '\0';

-            }

-            i = (i + 1) % 4;

-        }

-

-        if (0 != i)

-            AIR_PRINT("%s\n", buf);

-

-        return AIR_E_BAD_PARAMETER;

-    }

-

-    if (CMD_NO_PARA == cmdp->argc_min)

-    {

-        if (argc != 1)

-        {

-            if (cmdp->argc_errmsg != NULL)

-            {

-                AIR_PRINT("Usage: %s\n", cmdp->argc_errmsg);

-            }

-

-            return AIR_E_BAD_PARAMETER;

-        }

-    }

-    else if (CMD_VARIABLE_PARA == cmdp->argc_min)

-    {

-        if (argc < 3)

-        {

-            if (cmdp->argc_errmsg != NULL)

-            {

-                AIR_PRINT("Usage: %s\n", cmdp->argc_errmsg);

-            }

-

-            return AIR_E_BAD_PARAMETER;

-        }

-    }

-    else

-    {

-        if ((argc <= cmdp->argc_min) || ((cmdp->argc_min != 0) && (argc != (cmdp->argc_min + 1))))

-        {

-            if (cmdp->argc_errmsg != NULL)

-            {

-                AIR_PRINT("Usage: %s\n", cmdp->argc_errmsg);

-            }

-

-            return AIR_E_BAD_PARAMETER;

-        }

-    }

-

-    if (cmdp->func)

-    {

-        argc--;

-        argv++;

-        return (*cmdp->func)(argc, argv);

-    }

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_parse_cmd

- * PURPOSE:

- *      This function is used process diagnostic cmd

- * INPUT:

- *      argc         -- parameter number

- *      argv         -- parameter strings

- * OUTPUT:

- *      None

- * RETURN:

- *      NPS_E_OK     -- Successfully read the data.

- *      NPS_E_OTHERS -- Failed to read the data.

- * NOTES:

- *

- */

-AIR_ERROR_NO_T

-air_parse_cmd(

-        const UI32_T argc,

-        const C8_T **argv)

-{

-    return subcmd(Cmds, argc, (C8_T **)argv);

-}

-

+/* FILE NAME:   air_cmd.c
+ * PURPOSE:
+ *      Define the command line function in AIR SDK.
+ * NOTES:
+ */
+
+/* INCLUDE FILE DECLARATIONS
+*/
+#include "air.h"
+
+/* NAMING CONSTANT DECLARATIONS
+*/
+
+/* MACRO FUNCTION DECLARATIONS
+*/
+#define MAC_STR         "%02X%02X%02X%02X%02X%02X"
+#define MAC2STR(m)      (m)[0],(m)[1],(m)[2],(m)[3],(m)[4],(m)[5]
+#define AIR_MAC_LEN    (12)
+#define CMD_NO_PARA     (0xFFFFFFFF)
+#define CMD_VARIABLE_PARA (0xFFFFFFFE)
+#define L2_WDOG_KICK_NUM            (100)
+
+#define TOLOWER(x)      ((x) | 0x20)
+#define isxdigit(c)     (('0' <= (c) && (c) <= '9') || ('a' <= (c) && (c) <= 'f') || ('A' <= (c) && (c) <= 'F'))
+#define isdigit(c)      ('0' <= (c) && (c) <= '9')
+#define CMD_CHECK_PARA(__shift__, __op__, __size__) do          \
+{                                                               \
+    if ((__shift__) __op__ (__size__))                          \
+    {                                                           \
+        ;                                                       \
+    }                                                           \
+    else                                                        \
+    {                                                           \
+        return (AIR_E_BAD_PARAMETER);                           \
+    }                                                           \
+} while(0)
+
+/* DATA TYPE DECLARATIONS
+*/
+typedef struct {
+    C8_T*               name;
+    AIR_ERROR_NO_T     (*func)(UI32_T argc, C8_T *argv[]);
+    UI32_T              argc_min;
+    C8_T*               argc_errmsg;
+} AIR_CMD_T;
+
+/* GLOBAL VARIABLE DECLARATIONS
+*/
+
+/* LOCAL SUBPROGRAM DECLARATIONS
+*/
+/* String Utility */
+static BOOL_T _strcmp(const char *s1, const char *s2);
+static C8_T * _strtok_r(C8_T *s, const C8_T *delim, C8_T **last);
+static C8_T * _strtok(C8_T *s, const C8_T *delim, C8_T **last);
+UI32_T _strtoul(const C8_T *cp, C8_T **endp, UI32_T base);
+static I32_T _strtol(const C8_T *cp, C8_T **endp, UI32_T base);
+
+/* Type Converter */
+static AIR_ERROR_NO_T _str2mac(C8_T *str, C8_T *mac);
+static AIR_ERROR_NO_T _hex2bit(const UI32_T hex, UI32_T *ptr_bit);
+static AIR_ERROR_NO_T _hex2bitstr(const UI32_T hex, C8_T *ptr_bit_str, UI32_T str_len);
+static AIR_ERROR_NO_T _portListStr2Ary(const C8_T *str, UI32_T *ary, const UI32_T ary_num);
+
+/* Register Operation */
+static AIR_ERROR_NO_T doRegRead(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doRegWrite(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doReg(UI32_T argc, C8_T *argv[]);
+
+/* PHY Operation */
+static AIR_ERROR_NO_T doPhyCL22Read(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPhyCL22Write(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPhyCL22(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPhyCL45Read(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPhyCL45Write(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPhyCL45(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPhy(UI32_T argc, C8_T *argv[]);
+
+/* Porting setting */
+static AIR_ERROR_NO_T doPortSetMatrix(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPortSetVlanMode(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPortSet(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T doPortGetMatrix(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPortGetVlanMode(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPortGet(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T doPort(UI32_T argc, C8_T *argv[]);
+
+/* Vlan setting */
+static AIR_ERROR_NO_T doVlanInitiate(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanCreate(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanDestroy(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanDestroyAll(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanDump(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanAddPortMem(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanDelPortMem(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T doVlanSetFid(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanSetMemPort(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanSetIVL(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanSetPortBaseStag(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanSetStag(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanSetEgsTagCtlEn(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanSetEgsTagCtlCon(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanSetEgsTagCtl(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanSetPortActFrame(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanSetLeakyVlanEn(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanSetPortVlanAttr(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanSetIgsPortETagAttr(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanSetPortETagAttr(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanSetPortOuterTPID(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanSetPvid(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanSet(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T doVlanGetPortActFrame(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanGetLeakyVlanEn(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanGetPortVlanAttr(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanGetIgsPortETagAttr(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanGetPortETagAttr(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanGetPortOuterTPID(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanGetPvid(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doVlanGet(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T doVlan(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T doFlowCtrl(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doJumbo(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T doL2Add(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doL2Del(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doL2Clear(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doL2Get(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doL2Set(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doL2Dump(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doL2(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T doAnMode(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doLocalAdv(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doRemoteAdv(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPortSpeed(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPortDuplex(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPortStatus(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPortBckPres(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPortPsMode(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPortSmtSpdDwn(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPortSpTag(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPortEnable(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPort5GBaseRMode(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPortHsgmiiMode(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPortSgmiiMode(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPortRmiiMode(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doPortRgmiiMode(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T doSptagEn(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doSptagMode(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doSptagDecode(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doSptagEncode(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doSptag(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T doMacAddr(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T _printMacEntry(AIR_MAC_ENTRY_T * mt, UI32_T age_unit, UI8_T count, UI8_T title);
+static AIR_ERROR_NO_T doGetMacAddr(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doMacAddrAgeOut(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doDumpMacAddr(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T doLagMember(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doLagMemberCnt(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doLagPtseed(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doLagHashtype(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doLagDstInfo(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doLagState(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doLagSpsel(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doLagGet(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doLagSet(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doLag(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T doStpPortstate(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doStpGet(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doStpSet(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doStp(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T doMirrorGetSid(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doMirrorDelSid(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doMirrorAddRlist(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doMirrorAddTlist(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doMirrorSetSessionEnable(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doMirrorSetSession(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doMirrorSet(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doMirrorAdd(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doMirrorGet(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doMirrorDel(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doMirror(UI32_T argc,C8_T *argv[]);
+
+static AIR_ERROR_NO_T doMibClearPort(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doMibClearAcl(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doMibGetPort(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doMibGetAcl(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doMibClear(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doMibGet(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doMib(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T doQosScheduleAlgo(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doQosTrustMode(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doQosPri2Queue(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doQosDscp2Pri(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doQosRateLimitEnable(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doQosRateLimit(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doQosPortPriority(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doQosRateLimitExMngFrm(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doQosGet(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doQosSet(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doQos(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T doDiagTxComply(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doDiagSet(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doDiagGet(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doDiag(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T doLedMode(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doLedState(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doLedUsrDef(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doLedBlkTime(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doLedSet(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doLedGet(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doLed(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T doSwitchCpuPortEn(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doSwitchCpuPort(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doSwitchPhyLCIntrEn(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doSwitchPhyLCIntrSts(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doSwitchSet(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doSwitchGet(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doSwitch(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T doShowVersion(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doShow(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T doStormEnable(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doStormRate(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doFldMode(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doSaLearning(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doSaLimit(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doSecGet(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doSecSet(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doSec(UI32_T argc, C8_T *argv[]);
+
+static void _air_acl_printRuleMap(UI32_T *rule_map, UI32_T ary_num);
+static AIR_ERROR_NO_T doAclEn(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclRule(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclUdfRule(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclRmvRule(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclRmvUdfRule(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclAction(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclRmvAction(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclDumpAction(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclTrtcm(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclTrtcmEn(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclRmvTrtcm(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclPortEn(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclDropEn(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclDropThrsh(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclDropPbb(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclMeter(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclDump(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclSet(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclGet(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclDel(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAclClear(UI32_T argc, C8_T *argv[]);
+static AIR_ERROR_NO_T doAcl(UI32_T argc, C8_T *argv[]);
+
+static AIR_ERROR_NO_T subcmd(const AIR_CMD_T tab[], UI32_T argc, C8_T *argv[]);
+
+/* STATIC VARIABLE DECLARATIONS
+*/
+const static C8_T *_sptag_vpm[] =
+{
+    "untagged",
+    "8100",
+    "predefined",
+    "unknown"
+};
+
+const static C8_T *_sptag_pt[] =
+{
+    "disable pass through",
+    "enable pass through"
+};
+
+const static C8_T *_air_mac_address_forward_control_string [] =
+{
+    "Default",
+    "CPU include",
+    "CPU exclude",
+    "CPU only",
+    "Drop"
+};
+
+static AIR_CMD_T regCmds[] =
+{
+    {"r",           doRegRead,      1,      "reg r <reg(4'hex)>"},
+    {"w",           doRegWrite,     2,      "reg w <reg(4'hex)> <value(8'hex)>"},
+
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T phyCL22Cmds[] =
+{
+    {"r",           doPhyCL22Read,  2,      "phy cl22 r <port(0..4)> <reg(2'hex)>"},
+    {"w",           doPhyCL22Write, 3,      "phy cl22 w <port(0..4)> <reg(2'hex)> <value(4'hex)>"},
+
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T phyCL45Cmds[] =
+{
+    {"r",           doPhyCL45Read,  3,      "phy cl45 r <port(0..4)> <dev(2'hex)> <reg(3'hex)>"},
+    {"w",           doPhyCL45Write, 4,      "phy cl45 w <port(0..4)> <dev(2'hex)> <reg(3'hex)> <value(4'hex)>"},
+
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T phyCmds[] =
+{
+    {"cl22",         doPhyCL22,     0,      NULL},
+    {"cl45",         doPhyCL45,     0,      NULL},
+
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T portSetCmds[] =
+{
+    {"matrix",      doPortSetMatrix,        2,                "port set matrix <port(0..6)> <matrix(6:0)>"},
+    {"vlanMode",    doPortSetVlanMode,      2,                "port set vlanMode <port(0..6)> <vlanMode(0:matrix,1:fallback,2:check,3:security)>"},
+    {"flowCtrl",    doFlowCtrl,             3,                "port set flowCtrl <port(0..6)> <dir(0:Tx,1:Rx)> <fc_en(1:En,0:Dis)>"},
+    {"jumbo",       doJumbo,                2,                "port set jumbo <pkt_len(0:1518,1:1536,2:1552,3:max)> <frame_len(2..15)>"},
+    {"anMode",      doAnMode,               2,                "port set anMode <port(0..4)> <en(0:force,1:AN)>"},
+    {"localAdv",    doLocalAdv,             7,                "port set localAdv <port(0..4)> <10H(1:En,0:Dis)> <10F(1:En,0:Dis)> <100H(1:En,0:Dis)> <100F(1:En,0:Dis)> <1000F(1:En,0:Dis)> <pause(1:En,0:Dis)>"},
+    {"speed",       doPortSpeed,            2,                "port set speed <port(0..4)> <speed(0:10M,1:100M,2:1G,3:2.5G)>"},
+    {"duplex",      doPortDuplex,           2,                "port set duplex <port(0..4)> <duplex(0:half,1:full)>"},
+    {"bckPres",     doPortBckPres,          2,                "port set bckPres <port(0..6)> <bckPres(1:En,0:Dis)>"},
+    {"psMode",      doPortPsMode,           3,                "port set psMode <port(0..4)> <ls(1:En,0:Dis)> <eee(1:En,0:Dis)>"},
+    {"smtSpdDwn",   doPortSmtSpdDwn,        3,                "port set smtSpdDwn <port(0..4)> <en(1:En,0:Dis)> <retry(2..5)>"},
+    {"spTag",       doPortSpTag,            2,                "port set spTag <port(0..6)> <en(1:En,0:Dis)>"},
+    {"enable",      doPortEnable,           2,                "port set enable <port(0..4)> <en(1:En,0:Dis)>"},
+    {"5GBaseRMode", doPort5GBaseRMode,      CMD_NO_PARA,      "port set 5GBaseRMode"},
+    {"hsgmiiMode",  doPortHsgmiiMode,       CMD_NO_PARA,      "port set hsgmiiMode"},
+    {"sgmiiMode",   doPortSgmiiMode,        2,                "port set sgmiiMode <mode(0:AN,1:Force)> <speed(0:10M,1:100M,2:1G)>"},
+    {"rmiiMode",    doPortRmiiMode,         1,                "port set rmiiMode <speed(0:10M,1:100M)>"},
+    {"rgmiiMode",   doPortRgmiiMode,        1,                "port set rgmiiMode <speed(0:10M,1:100M,2:1G)>"},
+
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T portGetCmds[] =
+{
+    {"matrix",      doPortGetMatrix,        1,                "port get matrix <port(0..6)>"},
+    {"vlanMode",    doPortGetVlanMode,      1,                "port get vlanMode <port(0..6)>"},
+    {"flowCtrl",    doFlowCtrl,             2,                "port get flowCtrl <port(0..6)> <dir(0:Tx,1:Rx)>"},
+    {"jumbo",       doJumbo,                CMD_NO_PARA,      "port get jumbo"},
+    {"anMode",      doAnMode,               1,                "port get anMode <port(0..4)>"},
+    {"localAdv",    doLocalAdv,             1,                "port get localAdv <port(0..4)>"},
+    {"remoteAdv",   doRemoteAdv,            1,                "port get remoteAdv <port(0..4)>"},
+    {"speed",       doPortSpeed,            1,                "port get speed <port(0..4)>"},
+    {"duplex",      doPortDuplex,           1,                "port get duplex <port(0..4)>"},
+    {"status",      doPortStatus,           1,                "port get status <port(0..4)>"},
+    {"bckPres",     doPortBckPres,          1,                "port get bckPres <port(0..6)>"},
+    {"psMode",      doPortPsMode,           1,                "port get psMode <port(0..4)>"},
+    {"smtSpdDwn",   doPortSmtSpdDwn,        1,                "port get smtSpdDwn <port(0..4)>"},
+    {"spTag",       doPortSpTag,            1,                "port get spTag <port(0..6)>"},
+    {"enable",      doPortEnable,           1,                "port get enable <port(0..4)>"},
+
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T portCmds[] =
+{
+    {"set",         doPortSet,      0,      NULL},
+    {"get",         doPortGet,      0,      NULL},
+
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T sptagCmds[] =
+{
+    {"setEnable",       doSptagEn,          2,      "sptag setEnable port<port(0..6)> enable<1:enable 0:disable>"},
+    {"getEnable",       doSptagEn,          1,      "sptag getEnable port<port(0..6)>"},
+    {"setmode",         doSptagMode,        2,      "sptag setmode port<port(0..6)> mode<0:inset 1:replace>"},
+    {"getmode",         doSptagMode,        1,      "sptag getmode port<port(0..6)>"},
+    {"encode",          doSptagEncode,      7,      "sptag encode mode={ insert | replace } opc={ portmap | portid | lookup } dp={bitimap hex} vpm={ untagged | 8100 | 88a8 } pri=<UINT> cfi=<UINT> vid=<UINT> "},
+    {"decode",          doSptagDecode,      4,      "sptag decode <byte(hex)> <byte(hex)> <byte(hex)> <byte(hex)>"},
+
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T vlanSetCmds[] =
+{
+    {"fid",                 doVlanSetFid,               2,      "vlan set fid <vid(0..4095)> <fid(0..7)>"},
+    {"memPort",             doVlanSetMemPort,           2,      "vlan set memPort <vid(0..4095)> <bitmap(6:0)>"},
+    {"ivl",                 doVlanSetIVL,               2,      "vlan set ivl <vid(0..4095)> <(1:En,0:Dis)>"},
+    {"portBaseStag",        doVlanSetPortBaseStag,      2,      "vlan set portBaseStag <vid(0..4095)> <(1:En,0:Dis)>"},
+    {"stag",                doVlanSetStag,              2,      "vlan set stag <vid(0..4095)> <stag(0..4095)>"},
+    {"egsTagCtlEn",         doVlanSetEgsTagCtlEn,       2,      "vlan set egsTagCtlEn <vid(0..4095)> <(1:En,0:Dis)>"},
+    {"egsTagCtlCon",        doVlanSetEgsTagCtlCon,      2,      "vlan set egsTagCtlCon <vid(0..4095)> <(1:En,0:Dis)>"},
+    {"egsTagCtl",           doVlanSetEgsTagCtl,         3,      "vlan set egsTagCtl <vid(0..4095)> <port(0..6)> <ctlType(0:untag,2:tagged)>"},
+
+    {"portActFrame",        doVlanSetPortActFrame,      2,      "vlan set portActFrame <port(0..6)> <frameType(0:all,1:tagged,2:untagged)>"},
+    {"leakyVlanEn",         doVlanSetLeakyVlanEn,       3,      "vlan set LeakyVlanEn <port(0..6)> <pktType(0:uc,1:mc,2:bc,3:ipmc)> <(1:En,0:Dis)>"},
+    {"portVlanAttr",        doVlanSetPortVlanAttr,      2,      "vlan set portVlanAttr <port(0..6)> <vlanAttr(0:user,1:stack,2:translation,3:transparent)>"},
+    {"igsPortETagAttr",     doVlanSetIgsPortETagAttr,   2,      "vlan set igsPortETagAttr <port(0..6)> <egsTagAttr(0:disable,1:consistent,4:untagged,5:swap,6:tagged,7:stack)>"},
+    {"portEgsTagAttr",      doVlanSetPortETagAttr,      2,      "vlan set portEgsTagAttr <port(0..6)> <egsTagAttr(0:untagged,1:swap,2:tagged,3:stack)>"},
+    {"portOuterTPID",       doVlanSetPortOuterTPID,     2,      "vlan set portOuterTPID <port(0..6)> <TPID(hex)>"},
+    {"pvid",                doVlanSetPvid,              2,      "vlan set pvid <port(0..6)> <vid(0..4095)>"},
+
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T vlanGetCmds[] =
+{
+    {"portActFrame",        doVlanGetPortActFrame,      1,      "vlan get portActFrame <port(0..6)>"},
+    {"leakyVlanEn",         doVlanGetLeakyVlanEn,       1,      "vlan get leakyVlanEn <port(0..6)>"},
+    {"portVlanAttr",        doVlanGetPortVlanAttr,      1,      "vlan get portVlanAttr <port(0..6)>"},
+    {"igsPortETagAttr",     doVlanGetIgsPortETagAttr,   1,      "vlan get igsPortETagAttr <port(0..6)>"},
+    {"portEgsTagAttr",      doVlanGetPortETagAttr,      1,      "vlan get portEgsTagAttr <port(0..6)>"},
+    {"portOuterTPID",       doVlanGetPortOuterTPID,     1,      "vlan get portOuterTPID <port(0..6)>"},
+    {"pvid",                doVlanGetPvid,              1,      "vlan get pvid <port(0..6)>"},
+
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T vlanCmds[] =
+{
+    {"initiate",        doVlanInitiate,         9,      "vlan initiate <vid(0..4095)> <fid(0..7)> <bitmap(6:0)> <ivl(1:En,0:Dis)> <portbasestag(1:En,0:Dis)> <stag(0..4095)> <egstagctlen(1:En,0:Dis)> <egstagcon(1:En,0:Dis)> <taggedbitmap(6:0)>"},
+    {"create",          doVlanCreate,           1,      "vlan create <vid(0..4095)>"},
+    {"destroy",         doVlanDestroy,          1,      "vlan destroy [ <vid(0..4095)> | <vidRange(vid0-vid1)> ]"},
+    {"destroyAll",      doVlanDestroyAll,       0,      "vlan destroyAll [ <restoreDefVlan(0:false,1:true)> | ]"},
+    {"dump",            doVlanDump,             0,      "vlan dump [ <vid(0..4095)> | <vidRange(vid0-vid1)> | ]"},
+    {"addPortMem",      doVlanAddPortMem,       2,      "vlan addPortMem <vid(0..4095)> <port(0..6)>"},
+    {"delPortMem",      doVlanDelPortMem,       2,      "vlan addPortMem <vid(0..4095)> <port(0..6)>"},
+    {"set",             doVlanSet,              0,      NULL},
+    {"get",             doVlanGet,              0,      NULL},
+
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T l2ClearCmds[] =
+{
+    {"mac",              doMacAddr,          CMD_NO_PARA,    "l2 clear mac"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T l2DelCmds[] =
+{
+    {"mac",             doMacAddr,           3,    "l2 del mac <mac(12'hex)> [ vid <vid(0..4095)> | fid <fid(0..15)> ]"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T l2AddCmds[] =
+{
+    {"mac",             doMacAddr,           7,    "l2 add mac <static(0:dynamic,1:static)> <unauth(0:auth,1:unauth)> <mac(12'hex)> <portlist(uintlist)> [ vid <vid(0..4095)> | fid <fid(0..15)> ] <src_mac_forward=(0:default,1:cpu-exclude,2:cpu-include,3:cpu-only,4:drop)>"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T l2SetCmds[] =
+{
+    {"macAddrAgeOut",   doMacAddrAgeOut,    1,    "l2 set macAddrAgeOut <time(1, 1000000)>"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T l2GetCmds[] =
+{
+    {"mac",             doGetMacAddr,       3,              "l2 get mac <mac(12'hex)> [ vid <vid(0..4095)> | fid <fid(0..15)> ]"},
+    {"macAddrAgeOut",   doMacAddrAgeOut,    CMD_NO_PARA,    "l2 get macAddrAgeOut"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T l2DumpCmds[] =
+{
+    {"mac",                doDumpMacAddr,        0,    "l2 dump mac"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T l2Cmds[] =
+{
+    {"add",         doL2Add,        0,        NULL},
+    {"del",         doL2Del,        0,        NULL},
+    {"clear",       doL2Clear,      0,        NULL},
+    {"get",         doL2Get,        0,        NULL},
+    {"set",         doL2Set,        0,        NULL},
+    {"dump",        doL2Dump,       0,        NULL},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T lagGetCmds[] =
+{
+    {"member",      doLagMember,    1,              "lag get member group_id(0 or 1)"},
+    {"dstInfo",     doLagDstInfo,   CMD_NO_PARA,    "lag get dstInfo"},
+    {"ptseed",      doLagPtseed,    CMD_NO_PARA,    "lag get ptseed"},
+    {"hashtype",    doLagHashtype,  CMD_NO_PARA,    "lag get hashtype"},
+    {"state",       doLagState,     CMD_NO_PARA,    "lag get state"},
+    {"spsel",       doLagSpsel,     CMD_NO_PARA,    "lag get spsel"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T lagSetCmds[] =
+{
+    {"member",       doLagMember,        4,    "lag set member <group_id(0 or 1)> <member_index(0..3)> <enable(0,1)> <port index(0..6)>"},
+    {"dstInfo",      doLagDstInfo,       7,    "lag set dstInfo <sp(1:En,0:Dis)> <sa(1:En,0:Dis)> <da(1:En,0:Dis)> <sip(1:En,0:Dis)> <dip(1:En,0:Dis)> <sport(1:En,0:Dis)> <dport(1:En,0:Dis)>"},
+    {"ptseed",       doLagPtseed,        1,    "lag set ptseed <hex32>"},
+    {"hashtype",     doLagHashtype,      1,    "lag set hashtype <0-crc32lsb;1-crc32msb;2-crc16;3-xor4>"},
+    {"state",        doLagState,         1,    "lag set state <state(1:En,0:Dis)>"},
+    {"spsel",        doLagSpsel,         1,    "lag set spsel <soure port enable(1:En,0:Dis)>"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T lagCmds[] =
+{
+    {"get",          doLagGet,        0,        NULL},
+    {"set",          doLagSet,        0,        NULL},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T stpGetCmds[] =
+{
+    {"portstate",    doStpPortstate,  2,    "stp get portstate <port(0..6)> <fid(0..15)>"},
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T stpSetCmds[] =
+{
+    {"portstate",    doStpPortstate,  3,    "stp set portstate <port(0..6)> <fid(0..15)> <state(0:disable,1:listen,2:learn,3:forward)>"},
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T stpCmds[] =
+{
+    {"get",         doStpGet,           0,      NULL},
+    {"set",         doStpSet,           0,      NULL},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T mirrorSetCmds[] =
+{
+    {"session",        doMirrorSetSession,       6,      "mirror set session <sid(0,1)> <dst_port(UINT)> <state(1:En,0:Dis)> <tag(1:on, 0:off)> <list(UINTLIST)> <dir(0:none,1:tx,2:rx,3:both)>"},
+    {"session-enable", doMirrorSetSessionEnable, 2,      "mirror set session-enable <sid(0,1)> <state(1:En,0:Dis)>"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T mirrorAddCmds[] =
+{
+    {"session-rlist",  doMirrorAddRlist,       2,      "mirror add session-rlist <sid(0,1)> <list(UINTLIST)>"},
+    {"session-tlist",  doMirrorAddTlist,       2,      "mirror add session-tlist <sid(0,1)> <list(UINTLIST)>"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T mirrorGetCmds[] =
+{
+    {"session",        doMirrorGetSid,       1,      "mirror get session <sid(0,1)>"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T mirrorDelCmds[] =
+{
+    {"session",        doMirrorDelSid,       1,      "mirror del session <sid(0,1)>"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T mirrorCmds[] =
+{
+    {"set",         doMirrorSet,        0,      NULL},
+    {"add",         doMirrorAdd,        0,      NULL},
+    {"get",         doMirrorGet,        0,      NULL},
+    {"del",         doMirrorDel,        0,      NULL},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T mibClearCmds[] =
+{
+    {"port",        doMibClearPort,     1,      "mib clear port <port(0..6)>"},
+    {"all",         doMibClearPort,     0,      "mib clear all"},
+    {"acl",         doMibClearAcl,      0,      "mib clear acl"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T mibGetCmds[] =
+{
+    {"port",        doMibGetPort,       1,      "mib get port <port(0..6)>"},
+    {"acl",         doMibGetAcl,        1,      "mib get acl <event(0..7)>"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T mibCmds[] =
+{
+    {"clear",       doMibClear,         0,      NULL},
+    {"get",         doMibGet,           0,      NULL},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T qosGetCmds[] =
+{
+    {"scheduleAlgo",    doQosScheduleAlgo,      2,  "qos get scheduleAlgo <portlist(UINTLIST)> <queue(UINT)>"},
+    {"trustMode",       doQosTrustMode,         1,  "qos get trustMode <portlist(UINTLIST)>"},
+    {"pri2Queue",       doQosPri2Queue,         0,  "qos get pri2Queue"},
+    {"dscp2Pri",        doQosDscp2Pri,          1,  "qos get dscp2Pri <dscp(0..63)>"},
+    {"rateLimitEnable", doQosRateLimitEnable,   1,  "qos get rateLimitEnable <portlist(UINTLIST)>"},
+    {"rateLimit",       doQosRateLimit,         1,  "qos get rateLimit <portlist(UINTLIST)>"},
+    {"portPriority",    doQosPortPriority,      1,  "qos get portPriority <portlist(UINTLIST)>"},
+    {"rateLmtExMngFrm", doQosRateLimitExMngFrm, 0,  "qos get rateLmtExMngFrm"},
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T qosSetCmds[] =
+{
+    {"scheduleAlgo",    doQosScheduleAlgo,      4,  "qos set scheduleAlgo <portlist(UINTLIST)> <queue(UINT)> <scheduler(0:SP,1:WRR,2:WFQ)> <weight(0..128)>, weight 0 is valid only on sp mode"},
+    {"trustMode",       doQosTrustMode,         2,  "qos set trustMode <portlist(UINTLIST)> <mode(0:port,1:1p-port,2:dscp-port,3:dscp-1p-port>"},
+    {"pri2Queue",       doQosPri2Queue,         2,  "qos set pri2Queue <priority(0..7)> <queue(0..7)>"},
+    {"dscp2Pri",        doQosDscp2Pri,          2,  "qos set dscp2Pri <dscp(0..63)> <priority(0..7)>"},
+    {"rateLimitEnable", doQosRateLimitEnable,   3,  "qos set rateLimitEnable <portlist(UINTLIST)> <dir(0:egress,1:ingress)> <rate_en(1:En,0:Dis)>"},
+    {"rateLimit",       doQosRateLimit,         5,  "qos set rateLimit <portlist(UINTLIST)> <I_CIR(0..80000)> <I_CBS(0..127)> <E_CIR(0..80000)> <E_CBS(0..127)>"},
+    {"portPriority",    doQosPortPriority,      2,  "qos set portPriority <portlist(UINTLIST)> <priority(0..7)>"},
+    {"rateLmtExMngFrm", doQosRateLimitExMngFrm, 2,  "qos set rateLmtExMngFrm <dir(0:egress)> <en(0:include,1:exclude)>"},
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T qosCmds[] =
+{
+    {"get",          doQosGet,        0,        NULL},
+    {"set",          doQosSet,        0,        NULL},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T diagSetCmds[] =
+{
+    {"txComply",    doDiagTxComply,     2,      "diag set txComply <phy(0..5)> <mode(0..8)>"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T diagGetCmds[] =
+{
+    {"txComply",    doDiagTxComply,     1,      "diag get txComply <phy(0..5)>"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T diagCmds[] =
+{
+    {"set",         doDiagSet,          0,      NULL},
+    {"get",         doDiagGet,          0,      NULL},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T ledSetCmds[] =
+{
+    {"mode",        doLedMode,          1,      "led set mode <mode(0:disable, 1..3:2 LED, 4:user-define)>"},
+    {"state",       doLedState,         2,      "led set state <led(0..1)> <state(1:En,0:Dis)>"},
+    {"usr",         doLedUsrDef,        4,      "led set usr <led(0..1)> <polarity(0:low, 1:high)> <on_evt(7'bin)> <blink_evt(10'bin)>"},
+    {"time",        doLedBlkTime,       1,      "led set time <time(0..5:32ms~1024ms)>"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T ledGetCmds[] =
+{
+    {"mode",        doLedMode,          CMD_NO_PARA,      "led get mode"},
+    {"state",       doLedState,         1,                "led get state <led(0..1)>"},
+    {"usr",         doLedUsrDef,        1,                "led get usr <led(0..1)>"},
+    {"time",        doLedBlkTime,       CMD_NO_PARA,      "led get time"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T ledCmds[] =
+{
+    {"set",         doLedSet,           0,      NULL},
+    {"get",         doLedGet,           0,      NULL},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T showCmds[] =
+{
+    {"version",     doShowVersion,        0,        NULL},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T secGetCmds[] =
+{
+    {"stormEnable",     doStormEnable,   2,  "sec get stormEnable <port(0..6)> <type(0:bcst,1:mcst,2:ucst)>"},
+    {"stormRate",       doStormRate,     2,  "sec get stormRate <port(0..6)> <type(0:bcst,1:mcst,2:ucst)>"},
+    {"fldMode",         doFldMode,       2,  "sec get fldMode <port(0..6)> <type(0:bcst,1:mcst,2:ucst,3:qury>"},
+    {"saLearning",      doSaLearning,    1,  "sec get saLearning <port(0..6)>"},
+    {"saLimit",         doSaLimit,       1,  "sec get saLimit <port(0..6)>"},
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T secSetCmds[] =
+{
+    {"stormEnable",     doStormEnable,   3,  "sec set stormEnable <port(0..6)> <type(0:bcst,1:mcst,2:ucst)> <en(1:En,0:Dis)>"},
+    {"stormRate",       doStormRate,     4,  "sec set stormRate <port(0..6)> <type(0:bcst,1:mcst,2:ucst)> <count(0..255)> <unit(0:64k,1:256k,2:1M,3:4M,4:16M)>"},
+    {"fldMode",         doFldMode,       3,  "sec set fldMode <port(0..6)> <type(0:bcst,1:mcst,2:ucst,3:qury> <en(1:En,0:Dis)>"},
+    {"saLearning",      doSaLearning,    2,  "sec set saLearning <port(0..6)> <learn(0:disable,1:enable)>"},
+    {"saLimit",         doSaLimit,       3,  "sec set saLimit <port(0..6)> <mode(0:disable,1:enable)> <count(0..4095)>"},
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T secCmds[] =
+{
+    {"get",          doSecGet,        0,        NULL},
+    {"set",          doSecSet,        0,        NULL},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T switchSetCmds[] =
+{
+    {"cpuPortEn",   doSwitchCpuPortEn,   1,              "switch set cpuPortEn <cpu_en(1:En,0:Dis)>"},
+    {"cpuPort",     doSwitchCpuPort,     1,              "switch set cpuPort <port_number>"},
+    {"phyLCIntrEn",     doSwitchPhyLCIntrEn,     2,      "switch set phyLCIntrEn <phy(0..6)> <(1:En,0:Dis)>"},
+    {"phyLCIntrSts",    doSwitchPhyLCIntrSts,    2,      "switch set phyLCIntrSts <phy(0..6)> <(1:Clear)>"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T switchGetCmds[] =
+{
+    {"cpuPortEn",   doSwitchCpuPortEn,   CMD_NO_PARA,      "switch get cpuPortEn"},
+    {"cpuPort",     doSwitchCpuPort,     CMD_NO_PARA,      "switch get cpuPort"},
+    {"phyLCIntrEn",     doSwitchPhyLCIntrEn,     1,        "switch get phyLCIntrEn <phy(0..6)>"},
+    {"phyLCIntrSts",    doSwitchPhyLCIntrSts,    1,        "switch get phyLCIntrSts <phy(0..6)>"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T switchCmds[] =
+{
+    {"set",         doSwitchSet,        0,      NULL},
+    {"get",         doSwitchGet,        0,      NULL},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T aclSetCmds[] =
+{
+    {"en",              doAclEn,            1,                     "acl set en <en(1:En,0:Dis)>"},
+    {"rule",            doAclRule,          CMD_VARIABLE_PARA,     "acl set rule <idx(0..127)>\n <state(0:Dis,1:En)> <reverse(0:Dis,1:En)> <end(0:Dis,1:En)>\n <portmap(7'bin)><ipv6(0:Dis,1:En,2:Not care)>\n[ dmac <dmac(12'hex)> <dmac_mask(12'hex)> ]\n[ smac <smac(12'hex)> <smac_mask(12'hex)> ]\n[ stag <stag(4'hex)> <stag_mask(4'hex)> ]\n[ ctag <ctag(4'hex)> <ctag_mask(4'hex)> ]\n[ etype <etype(4'hex)> <etype_mask(4'hex)> ]\n[ dip <dip(IPADDR)> <dip_mask(IPADDR)> ]\n[ sip <sip(IPADDR)> <sip_mask(IPADDR)> ]\n[ dscp <dscp(2'hex)> <dscp_mask(2'hex)> ]\n[ protocol <protocol(12'hex)> <protocol_mask(12'hex)> ]\n[ dport <dport(4'hex)> <dport_mask(4'hex)> ]\n[ sport <sport(4'hex)> <sport_mask(4'hex)> ]\n[ flow_label <flow_label(4'hex)> <flow_label_mask(4'hex)> ]\n[ udf <udf(4'hex)> <udf_mask(4'hex)> ] "},
+    {"udfRule",         doAclUdfRule,       7,                     "acl set udfRule <idx(0..15)> <mode(0:pattern, 1:threshold)> [ <pat(4'hex)> <mask(4'hex)> | <low(4'hex)> <high(4'hex)> ] <start(0:MAC header, 1:L2 payload, 2:IPv4 header, 3:IPv6 header, 4:L3 payload, 5:TCP header, 6:UDP header, 7: L4 payload)> <offset(0..127,unit:2 bytes)> <portmap(7'bin)>"},
+    {"action",          doAclAction,        CMD_VARIABLE_PARA,     "acl set action <idx(0..127)> \n[ forward <forward(0:Default,4:Exclude CPU,5:Include CPU,6:CPU only,7:Drop)> ]\n[ egtag <egtag(0:Default,1:Consistent,4:Untag,5:Swap,6:Tag,7:Stack)> ]\n[ mirrormap <mirrormap(2'bin)> ]\n[ priority <priority(0..7)> ]\n[ redirect <redirect(0:Dst,1:Vlan)> <portmap(7'bin)> ]\n[ leaky_vlan <leaky_vlan(1:En,0:Dis)> ]\n[ cnt_idx <cnt_idx(0..63)> ]\n[ rate_idx <rate_idx(0..31)> ] \n[ attack_idx <attack_idx(0..95)> ] \n[ vid <vid(0..4095)> ] \n[ manage <manage(1:En,0:Dis)> ] \n[ bpdu <bpdu(1:En,0:Dis)> ]\n[ class <class(0:Original,1:Defined)>[0..7] ]\n[ drop_pcd <drop_pcd(0:Original,1:Defined)> [red <red(0..7)>][yellow <yellow(0..7)>][green <green(0..7)>] ]\n[ color <color(0:Defined,1:Trtcm)> [ <defined_color(0:Dis,1:Green,2:Yellow,3:Red)> | <trtcm_idx(0..31)> ] ]"},
+    {"trtcm",           doAclTrtcm,         5,                     "acl set trtcm <idx(1..31)> <cir(4'hex)> <pir(4'hex)> <cbs(4'hex)> <pbs(4'hex)>"},
+    {"trtcmEn",         doAclTrtcmEn,       1,                     "acl set trtcmEn <en(1:En,0:Dis)>"},
+    {"portEn",          doAclPortEn,        2,                     "acl set portEn <port(0..6)> <en(1:En,0:Dis)>"},
+    {"dropEn",          doAclDropEn,        2,                     "acl set dropEn <port(0..6)> <en(1:En,0:Dis)>"},
+    {"dropThrsh",       doAclDropThrsh,     5,                     "acl set dropThrsh <port(0..6)> <color(0:green,1:yellow,2:red)> <queue(0..7)> <high(0..2047)> <low(0..2047)>"},
+    {"dropPbb",         doAclDropPbb,       4,                     "acl set dropPbb <port(0..6)> <color(0:green,1:yellow,2:red)> <queue(0..7)> <probability(0..1023)>"},
+    {"meter",           doAclMeter,         3,                     "acl set meter <idx(0..31)> <en(1:En,0:Dis)> <rate(0..65535)>\n Note: Limit rate = rate * 64Kbps"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T aclGetCmds[] =
+{
+    {"en",              doAclEn,            CMD_NO_PARA,    "acl get en"},
+    {"rule",            doAclRule,          1,              "acl get rule <idx(0..127)> "},
+    {"udfRule",         doAclUdfRule,       1,              "acl get udfRule <idx(0..15)>"},
+    {"action",          doAclAction,        1,              "acl get action <idx(0..127)>"},
+    {"trtcm",           doAclTrtcm,         1,              "acl get trtcm <idx(1..31)>"},
+    {"trtcmEn",         doAclTrtcmEn,       CMD_NO_PARA,    "acl get trtcmEn"},
+    {"portEn",          doAclPortEn,        1,              "acl get portEn <port(0..6)>"},
+    {"dropEn",          doAclDropEn,        1,              "acl get dropEn <port(0..6)>"},
+    {"dropThrsh",       doAclDropThrsh,     3,              "acl get dropThrsh <port(0..6)> <color(0:green,1:yellow,2:red)> <queue(0..7)>"},
+    {"dropPbb",         doAclDropPbb,       3,              "acl get dropPbb <port(0..6)> <color(0:green,1:yellow,2:red)> <queue(0..7)>"},
+    {"meter",           doAclMeter,         1,              "acl get meter <idx(0..31)>"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T aclDelCmds[] =
+{
+    {"rule",        doAclRmvRule,          1,         "acl del rule <idx(0..127)>"},
+    {"udfRule",     doAclRmvUdfRule,       1,         "acl del udfRule <idx(0..15)>"},
+    {"action",      doAclRmvAction,        1,         "acl del action <idx(0..127)>"},
+    {"trtcm",       doAclRmvTrtcm,         1,         "acl del trtcm <idx(0..31)>"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T aclClearCmds[] =
+{
+    {"rule",        doAclRmvRule,          CMD_NO_PARA,       "acl clear rule"},
+    {"udfRule",     doAclRmvUdfRule,       CMD_NO_PARA,       "acl clear udfRule"},
+    {"action",      doAclRmvAction,        CMD_NO_PARA,       "acl clear action"},
+    {"trtcm",       doAclRmvTrtcm,         CMD_NO_PARA,       "acl clear trtcm"},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T aclCmds[] =
+{
+    {"set",         doAclSet,           0,      NULL},
+    {"get",         doAclGet,           0,      NULL},
+    {"del",         doAclDel,           0,      NULL},
+    {"clear",       doAclClear,         0,      NULL},
+    {"dump",        doAclDump,          0,      NULL},
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+static AIR_CMD_T Cmds[] =
+{
+    {"reg",         doReg,          0,      NULL},
+    {"phy",         doPhy,          0,      NULL},
+    {"port",        doPort,         0,      NULL},
+    {"vlan",        doVlan,         0,      NULL},
+    {"l2",          doL2,           0,      NULL},
+    {"lag",         doLag,          0,      NULL},
+    {"stp",         doStp,          0,      NULL},
+    {"mirror",      doMirror,       0,      NULL},
+    {"mib",         doMib,          0,      NULL},
+    {"qos",         doQos,          0,      NULL},
+    {"diag",        doDiag,         0,      NULL},
+    {"led",         doLed,          0,      NULL},
+    {"switch",      doSwitch,       0,      NULL},
+    {"show",        doShow,         0,      NULL},
+    {"sec",         doSec,          0,      NULL},
+    {"acl",         doAcl,          0,      NULL},
+    {"sptag",       doSptag,        0,      NULL},
+
+    /* last entry, do not modify this entry */
+    {NULL, NULL, 0, NULL},
+};
+
+/* EXPORTED SUBPROGRAM BODIES
+*/
+
+/* LOCAL SUBPROGRAM BODIES
+*/
+static BOOL_T
+_strcmp(const char *s1, const char *s2)
+{
+    while(*s1 == *s2++)
+        if (*s1++ == '\0')
+            return (0);
+    return (*(const unsigned char *)s1 - *(const unsigned char *)(s2 -1));
+}
+
+static C8_T *
+_strtok_r(
+    C8_T *s,
+    const C8_T *delim,
+    C8_T **last)
+{
+    char *spanp;
+    int c = 0, sc = 0;
+    char *tok;
+
+    if (s == NULL && (s = *last) == NULL)
+    {
+        return (NULL);
+    }
+
+    /*
+     * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
+     */
+    for (;;)
+    {
+        c = *s++;
+        spanp = (char *)delim;
+        do
+        {
+            if (c == (sc = *spanp++))
+            {
+                break;
+            }
+        } while (sc != 0);
+        if (sc == 0)
+        {
+            break;
+        }
+    }
+
+    if (c == 0)
+    {   /* no non-delimiter characters */
+        *last = NULL;
+        return (NULL);
+    }
+    tok = s - 1;
+
+    /*
+     * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
+     * Note that delim must have one NUL; we stop if we see that, too.
+     */
+    for (;;)
+    {
+        c = *s++;
+        spanp = (char *)delim;
+        do
+        {
+            if ((sc = *spanp++) == c)
+            {
+                if (c == 0)
+                {
+                    s = NULL;
+                }
+                else
+                {
+                    s[-1] = 0;
+                }
+                *last = s;
+                return (tok);
+            }
+        } while (sc != 0);
+    }
+    /* NOTREACHED */
+}
+
+static C8_T *
+_strtok(
+    C8_T *s,
+    const C8_T *delim,
+    C8_T **last)
+{
+    return _strtok_r(s, delim, last);
+}
+
+UI32_T
+_strtoul(
+    const C8_T *cp,
+    C8_T **endp,
+    UI32_T base)
+{
+    UI32_T result = 0, value = 0;
+
+    if (!base)
+    {
+        base = 10;
+        if (*cp == '0')
+        {
+            base = 8;
+            cp++;
+            if ((TOLOWER(*cp) == 'x') && isxdigit(cp[1]))
+            {
+                cp++;
+                base = 16;
+            }
+        }
+    }
+    else if (base == 16)
+    {
+        if (cp[0] == '0' && TOLOWER(cp[1]) == 'x')
+        {
+            cp += 2;
+        }
+    }
+    while (isxdigit(*cp) &&
+           (value = isdigit(*cp) ? *cp-'0' : TOLOWER(*cp)-'a'+10) < base)
+    {
+        result = result*base + value;
+        cp++;
+    }
+    if (endp)
+    {
+        *endp = (char *)cp;
+    }
+    return result;
+}
+
+static I32_T
+_strtol(
+    const C8_T *cp,
+    C8_T **endp,
+    UI32_T base)
+{
+    if(*cp=='-')
+    {
+        return -_strtoul(cp + 1, endp, base);
+    }
+    return _strtoul(cp, endp, base);
+}
+
+static AIR_ERROR_NO_T
+_str2mac(
+        C8_T *str,
+        C8_T *mac)
+{
+    UI32_T i = 0;
+    C8_T tmpstr[3];
+
+    /* check str */
+    AIR_CHECK_PTR(str);
+    AIR_PARAM_CHK(strlen(str) != AIR_MAC_LEN, AIR_E_BAD_PARAMETER);
+    AIR_CHECK_PTR(mac);
+
+    for(i=0; i<6; i++)
+    {
+        strncpy(tmpstr, str+(i*2), 2);
+        tmpstr[2] = '\0';
+        mac[i] = _strtoul(tmpstr, NULL, 16);
+    }
+
+    return AIR_E_OK;
+}
+
+static AIR_ERROR_NO_T
+_str2ipv4(
+    const C8_T *ptr_str,
+    UI32_T     *ptr_addr)
+{
+    UI32_T value = 0, idx = 0, shift = 0;
+
+    AIR_CHECK_PTR(ptr_str);
+    AIR_CHECK_PTR(ptr_addr);
+
+    /* e.g. 192.168.1.2, strlen = 11 */
+    for (idx = 0; idx < strlen(ptr_str); idx++)
+    {
+        if (('0' <= ptr_str[idx]) && ('9' >= ptr_str[idx]))
+        {
+            value = (value * 10) + (ptr_str[idx] - '0');
+        }
+        else if ('.' == ptr_str[idx])
+        {
+            CMD_CHECK_PARA(value, <, 256); /* Error: invalid value */
+            CMD_CHECK_PARA(shift, <, 4);   /* Error: mem-overwrite */
+            *ptr_addr |= value << (24 - shift * 8);
+            shift += 1;
+            value = 0;
+        }
+        else
+        {
+            return AIR_E_BAD_PARAMETER; /* Error: not a digit number or dot */
+        }
+    }
+    CMD_CHECK_PARA(value, <, 256); /* Error: invalid value */
+    CMD_CHECK_PARA(shift, ==, 3);  /* Error: not an ipv4 addr */
+    *ptr_addr |= value << (24 - shift * 8);
+
+    return AIR_E_OK;
+}
+
+AIR_ERROR_NO_T
+_str2ipv6(
+    const C8_T  *ptr_str,
+    UI8_T       *ptr_addr)
+{
+    UI32_T              hex_value = 0, dec_value = 0, idx = 0;
+    BOOL_T              double_colon = FALSE, ipv4_compatible = FALSE;
+    UI32_T              double_colon_pos = 0, last_pos = 0;
+    UI8_T               tmp_ipv6[16] = {0};
+
+    AIR_CHECK_PTR(ptr_str);
+    AIR_CHECK_PTR(ptr_addr);
+
+    /* e.g. invalid:
+     * 3ffe::c0a8:0:      last cannot be colon except double-colon
+     * 3ffe:::c0a8:0      triple-colon
+     * 3ffe::c0a8::0      two double-colons
+     */
+
+    /* e.g. valid:
+     * 3ffe::c0a8:0       strlen = 12 (double-colon in middle)
+     * 3ffe:c0a8:0::      strlen = 13 (double-colon in last)
+     * ::3ffe:c0a8:0      strlen = 13 (double-colon in first)
+     * 3ffe::192.168.0.0  strlen = 17 (IPv4-compatible address)
+     */
+    for (idx = 0; idx < strlen(ptr_str); idx++)
+    {
+        if (('0' <= ptr_str[idx]) && ('9' >= ptr_str[idx]))
+        {
+            hex_value = (hex_value << 4) + (ptr_str[idx] - '0');
+            dec_value = (dec_value * 10) + (ptr_str[idx] - '0');
+        }
+        else if (('a' <= ptr_str[idx]) && ('f' >= ptr_str[idx]))
+        {
+            hex_value = (hex_value << 4) + (ptr_str[idx] - 'a') + 10;
+        }
+        else if (('A' <= ptr_str[idx]) && ('F' >= ptr_str[idx]))
+        {
+            hex_value = (hex_value << 4) + (ptr_str[idx] - 'A') + 10;
+        }
+        else if (':' == ptr_str[idx])
+        {
+            /* must belong to double-colon, calculate from last */
+            if (0 == idx)
+            {
+                continue;
+            }
+            /* not the first ch but a double-colon */
+            else if (':' == ptr_str[idx - 1])
+            {
+                CMD_CHECK_PARA(double_colon, ==, FALSE); /* Error: triple-colon or two double-colons */
+                double_colon = TRUE;
+            }
+            /* not the first ch and a double-colon */
+            else
+            {
+                CMD_CHECK_PARA(double_colon_pos, <, 15); /* Error: only 16 units for UI8_T  */
+                CMD_CHECK_PARA(last_pos,         <, 15); /* Error: only 16 units for UI8_T  */
+                tmp_ipv6[last_pos]     = (UI8_T)((hex_value >> 8) & 0xff);
+                tmp_ipv6[last_pos + 1] = (UI8_T)((hex_value >> 0) & 0xff);
+                double_colon_pos += (FALSE == double_colon)? 2 : 0;
+                last_pos += 2;
+                hex_value = 0;
+                dec_value = 0;
+            }
+        }
+        else if ('.' == ptr_str[idx])
+        {
+            CMD_CHECK_PARA(last_pos, <, 16); /* Error: only 16 units for UI8_T  */
+            tmp_ipv6[last_pos] = dec_value;
+            last_pos += 1;
+            dec_value = 0;
+            ipv4_compatible = TRUE;
+        }
+        else
+        {
+            return AIR_E_BAD_PARAMETER; /* Error: not a hex number or colon */
+        }
+    }
+
+    /* last data */
+    if ((idx != 0) && (':' != ptr_str[idx - 1]))
+    {
+        if (FALSE == ipv4_compatible)
+        {
+            CMD_CHECK_PARA(last_pos, <, 15); /* Error: only 16 units for UI8_T  */
+            tmp_ipv6[last_pos]     = (UI8_T)((hex_value >> 8) & 0xff);
+            tmp_ipv6[last_pos + 1] = (UI8_T)((hex_value >> 0) & 0xff);
+            last_pos += 2;
+        }
+        else
+        {
+            CMD_CHECK_PARA(last_pos, <, 16); /* Error: only 16 units for UI8_T  */
+            tmp_ipv6[last_pos] = dec_value;
+            last_pos += 1;
+        }
+    }
+    else
+    {
+        if ((idx >= 2) && (':' != ptr_str[idx - 2]))
+        {
+            return AIR_E_BAD_PARAMETER; /* Error: last cannot be colon except double-colon */
+        }
+    }
+
+    /* move tmp_ipv6 to ptr_value */
+    if (TRUE == double_colon)
+    {
+        /* e.g.
+         * 3ffe::c0a8:0       double_colon_pos = 2, last_pos = 4+2, tmp_ipv6 = {3f,fe,c0,a8,00,00,...}
+         * 3ffe:c0a8:0::      double_colon_pos = 6, last_pos = 6,   tmp_ipv6 = {3f,fe,c0,a8,00,00,...}
+         * ::3ffe:c0a8:0      double_colon_pos = 0, last_pos = 4+2, tmp_ipv6 = {3f,fe,c0,a8,00,00,...}
+         * 3ffe::192.168.0.0  double_colon_pos = 2, last_pos = 5+1, tmp_ipv6 = {3f,fe,c0,a8,00,00,...}
+         *
+         *                                 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
+         * 3ffe::c0a8:0       ptr_value = {3f,fe,--,--,--,--,--,--,--,--,--,--,--,--,--,--}
+         * 3ffe:c0a8:0::      ptr_value = {3f,fe,c0,a8,00,00,--,--,--,--,--,--,--,--,--,--}
+         * ::3ffe:c0a8:0      ptr_value = {--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--}
+         * 3ffe::192.168.0.0  ptr_value = {3f,fe,--,--,--,--,--,--,--,--,--,--,--,--,--,--}
+         */
+        for (idx = 0; idx < double_colon_pos; idx++)
+        {
+            ptr_addr[idx] = tmp_ipv6[idx];
+        }
+        /* e.g.
+         *                                 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
+         * 3ffe::c0a8:0       ptr_value = {3f,fe,--,--,--,--,--,--,--,--,--,--,c0,a8,00,00}
+         * 3ffe:c0a8:0::      ptr_value = {3f,fe,c0,a8,00,00,--,--,--,--,--,--,--,--,--,--}
+         * ::3ffe:c0a8:0      ptr_value = {--,--,--,--,--,--,--,--,--,--,3f,fe,c0,a8,00,00}
+         * 3ffe::192.168.0.0  ptr_value = {3f,fe,--,--,--,--,--,--,--,--,--,--,c0,a8,00,00}
+         */
+        for (idx = double_colon_pos; idx < last_pos; idx++)
+        {
+            ptr_addr[16 - (last_pos - idx)] = tmp_ipv6[idx];
+        }
+    }
+    else
+    {
+        for (idx = 0; idx < 16; idx++)
+        {
+            ptr_addr[idx] = tmp_ipv6[idx];
+        }
+    }
+
+    return AIR_E_OK;
+}
+
+void
+_showIpv6Str(
+    const UI8_T *ptr_ipv6,
+    C8_T *ptr_str)
+{
+    UI32_T idx = 0, next = 0, last = 16;
+    UI32_T cont_zero = 0;
+
+    while (idx < last)
+    {
+        if ((0 == cont_zero) && (0 == ptr_ipv6[idx]) && (0 == ptr_ipv6[idx + 1]))
+        {
+            next = idx + 2;
+
+            while (next < last)
+            {
+                if ((ptr_ipv6[next]) || (ptr_ipv6[next + 1]))
+                {
+                    AIR_PRINT(
+                            ptr_str + strlen(ptr_str),
+                            40 - strlen(ptr_str),
+                            "%s", (cont_zero) ? (":") : (":0"));
+                    break;
+                }
+
+                if (0 == cont_zero)
+                {
+                    cont_zero = 1;
+                }
+                next += 2;
+            }
+
+            if (next == last)
+            {
+
+                AIR_PRINT(
+                        ptr_str + strlen(ptr_str),
+                        40 - strlen(ptr_str),
+                        "%s", (cont_zero) ? ("::") : (":0"));
+            }
+
+            idx = next;
+        }
+        else
+        {
+            if (idx)
+            {
+                AIR_PRINT(
+                    ptr_str + strlen(ptr_str),
+                    40 - strlen(ptr_str),
+                    ":");
+            }
+
+            if (ptr_ipv6[idx])
+            {
+                AIR_PRINT(
+                    ptr_str + strlen(ptr_str),
+                    40 - strlen(ptr_str),
+                    "%0x%02x", ptr_ipv6[idx], ptr_ipv6[idx + 1]);
+            }
+            else
+            {
+                AIR_PRINT(
+                    ptr_str + strlen(ptr_str),
+                    40 - strlen(ptr_str),
+                    "%0x", ptr_ipv6[idx + 1]);
+            }
+
+            idx += 2;
+        }
+    }
+}
+
+static AIR_ERROR_NO_T
+_hex2bit(
+        const UI32_T hex,
+        UI32_T *ptr_bit)
+{
+    UI32_T i = 0;
+
+    /* Mistake proofing */
+    AIR_CHECK_PTR(ptr_bit);
+
+    (*ptr_bit) = 0;
+    for(i=0; i<AIR_MAX_NUM_OF_PORTS; i++)
+    {
+        if(hex & BIT(i))
+        {
+            (*ptr_bit) |= BITS_OFF_L(1UL, 4*(AIR_MAX_NUM_OF_PORTS - i - 1), 4);
+        }
+    }
+    return AIR_E_OK;
+}
+
+static AIR_ERROR_NO_T
+_hex2bitstr(
+        const UI32_T hex,
+        C8_T *ptr_bit_str,
+        UI32_T str_len)
+{
+    UI32_T i = 0;
+    C8_T str_bitmap[AIR_MAX_NUM_OF_PORTS+1];
+
+    /* Mistake proofing */
+    AIR_CHECK_PTR(ptr_bit_str);
+    AIR_PARAM_CHK(str_len <= AIR_MAX_NUM_OF_PORTS, AIR_E_BAD_PARAMETER);
+
+    memset(str_bitmap, 0, AIR_MAX_NUM_OF_PORTS+1);
+
+    for(i=0; i<AIR_MAX_NUM_OF_PORTS; i++)
+    {
+        if(hex & BIT(i))
+        {
+            str_bitmap[i] = '1';
+        }
+        else
+        {
+            str_bitmap[i] = '-';
+        }
+    }
+    str_bitmap[i] = '\0';
+    strncpy(ptr_bit_str, str_bitmap, i+1);
+
+    return AIR_E_OK;
+}
+
+static AIR_ERROR_NO_T
+_portListStr2Ary(
+    const C8_T *str,
+    UI32_T *ary,
+    const UI32_T ary_num)
+{
+    UI32_T i = 0;
+    UI32_T str_len = 0;
+    UI32_T val = 0;
+    C8_T *str2;
+    C8_T *pch;
+    C8_T *last;
+
+    /* Mistake proofing */
+    AIR_CHECK_PTR(str);
+    AIR_CHECK_PTR(ary);
+    AIR_PARAM_CHK(0 == ary_num, AIR_E_BAD_PARAMETER);
+
+    /* Allocate new string */
+    str_len = strlen(str);
+    str2 = AIR_MALLOC(str_len+1);
+    AIR_CHECK_PTR(str2);
+    memset(str2, 0, str_len+1);
+    strncpy(str2, str, str_len+1);
+
+    /* clear array */
+    memset(ary, 0, ary_num*4);
+
+    /* split string by ',' */
+    pch = _strtok(str2, ",", &last);
+    while(NULL != pch)
+    {
+        val = _strtoul(pch, NULL, 0);
+        ary[val/32] |= BIT(val%32);
+        pch = _strtok(NULL, ",", &last);
+    }
+
+    AIR_FREE(str2);
+    return AIR_E_OK;
+}
+
+static AIR_ERROR_NO_T
+doRegRead(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    UI32_T reg = 0, val = 0;
+
+    reg = _strtoul(argv[0], NULL, 16);
+    aml_readReg(0, reg, &val);
+    AIR_PRINT("Read reg=0x%x, value=0x%x\n", reg, val);
+
+    return AIR_E_OK;
+}
+
+static AIR_ERROR_NO_T
+doRegWrite(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    UI32_T reg = 0, val = 0;
+
+    reg = _strtoul(argv[0], NULL, 16);
+    val = _strtoul(argv[1], NULL, 16);
+    aml_writeReg(0, reg, val);
+    AIR_PRINT("Write reg=0x%x, value=0x%x\n", reg, val);
+
+    return AIR_E_OK;
+}
+
+static AIR_ERROR_NO_T
+doReg(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(regCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doPhyCL22Read(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    UI32_T port = 0, reg = 0, val = 0;
+
+    port = _strtoul(argv[0], NULL, 0);
+    reg  = _strtoul(argv[1], NULL, 16);
+    aml_readPhyReg(0, port, reg, &val);
+    AIR_PRINT("Phy read port=%d, reg=0x%x, value=0x%x\n", port, reg, val);
+
+    return AIR_E_OK;
+}
+
+static AIR_ERROR_NO_T
+doPhyCL22Write(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    UI32_T port = 0, reg = 0, val = 0;
+
+    port = _strtoul(argv[0], NULL, 0);
+    reg  = _strtoul(argv[1], NULL, 16);
+    val  = _strtoul(argv[2], NULL, 16);
+    aml_writePhyReg(0, port, reg, val);
+    AIR_PRINT("Phy write port=%d, reg=0x%x, value=0x%x\n", port, reg, val);
+
+    return AIR_E_OK;
+}
+
+static AIR_ERROR_NO_T
+doPhyCL22(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(phyCL22Cmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doPhyCL45Read(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    UI32_T port = 0, dev = 0, reg = 0, val = 0;
+
+    port = _strtoul(argv[0], NULL, 0);
+    dev  = _strtoul(argv[1], NULL, 16);
+    reg  = _strtoul(argv[2], NULL, 16);
+    aml_readPhyRegCL45(0, port, dev, reg, &val);
+    AIR_PRINT("Phy read port=%d, dev=0x%x, reg=0x%x, value=0x%x\n", port, dev, reg, val);
+
+    return AIR_E_OK;
+}
+
+static AIR_ERROR_NO_T
+doPhyCL45Write(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    UI32_T port = 0, dev = 0, reg = 0, val = 0;
+
+    port = _strtoul(argv[0], NULL, 0);
+    dev  = _strtoul(argv[1], NULL, 16);
+    reg  = _strtoul(argv[2], NULL, 16);
+    val  = _strtoul(argv[3], NULL, 16);
+    aml_writePhyRegCL45(0, port, dev, reg, val);
+    AIR_PRINT("Phy write port=%d, dev=0x%x, reg=0x%x, value=0x%x\n", port, dev, reg, val);
+
+    return AIR_E_OK;
+}
+
+static AIR_ERROR_NO_T
+doPhyCL45(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(phyCL45Cmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doPhy(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(phyCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doPortSetMatrix(UI32_T argc, C8_T *argv[])
+{
+    UI32_T port = 0;
+    UI32_T matrix = 0;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port   = _strtoul(argv[0], NULL, 0);
+    matrix = _strtoul(argv[1], NULL, 16);
+    rc = air_port_setPortMatrix(0, port, matrix);
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("Error: Operation failed!\n");
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doPortSetVlanMode(UI32_T argc, C8_T *argv[])
+{
+    UI32_T port = 0;
+    AIR_PORT_VLAN_MODE_T vlan_mode;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port      = _strtoul(argv[0], NULL, 0);
+    vlan_mode = _strtoul(argv[1], NULL, 0);
+    rc = air_port_setVlanMode(0, port, vlan_mode);
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("Error: Operation failed!\n");
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doPortSet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(portSetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doPortGetMatrix(UI32_T argc, C8_T *argv[])
+{
+    UI32_T port = 0;
+    UI32_T matrix = 0;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port = _strtoul(argv[0], NULL, 0);
+    rc = air_port_getPortMatrix(0, port, &matrix);
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("Error: Operation failed!\n");
+        return rc;
+    }
+    AIR_PRINT("Port %d Matrix: %2x\n", port, matrix);
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doPortGetVlanMode(UI32_T argc, C8_T *argv[])
+{
+    UI32_T port = 0;
+    AIR_PORT_VLAN_MODE_T vlan_mode;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port = _strtoul(argv[0], NULL, 0);
+    rc = air_port_getVlanMode(0, port, &vlan_mode);
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("Error: Operation failed!\n");
+        return rc;
+    }
+    AIR_PRINT("Port %d Vlan Mode: ", port);
+    switch(vlan_mode)
+    {
+        case AIR_PORT_VLAN_MODE_PORT_MATRIX:
+            AIR_PRINT("matrix(%d)\n", vlan_mode);
+            break;
+        case AIR_PORT_VLAN_MODE_FALLBACK:
+            AIR_PRINT("fallback(%d)\n", vlan_mode);
+            break;
+        case AIR_PORT_VLAN_MODE_CHECK:
+            AIR_PRINT("check(%d)\n", vlan_mode);
+            break;
+        case AIR_PORT_VLAN_MODE_SECURITY:
+            AIR_PRINT("security(%d)\n", vlan_mode);
+            break;
+        default:
+            AIR_PRINT("unknown(%d)\n", vlan_mode);
+            break;
+    };
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doPortGet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(portGetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doPort(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(portCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doVlanInitiate(UI32_T argc, C8_T *argv[])
+{
+    UI16_T vid = 0;
+    AIR_VLAN_ENTRY_ATTR_T vlan_entry = {0};
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    vid = _strtoul(argv[0], NULL, 0);
+    if (9 == argc)
+    {
+        vlan_entry.vlan_entry_format.fid           = _strtoul(argv[1], NULL, 0);
+        vlan_entry.vlan_entry_format.port_mem      = _strtoul(argv[2], NULL, 0);
+        vlan_entry.vlan_entry_format.ivl           = _strtoul(argv[3], NULL, 0);
+        vlan_entry.vlan_entry_format.port_stag     = _strtoul(argv[4], NULL, 0);
+        vlan_entry.vlan_entry_format.stag          = _strtoul(argv[5], NULL, 0);
+        vlan_entry.vlan_entry_format.eg_ctrl_en    = _strtoul(argv[6], NULL, 0);
+        vlan_entry.vlan_entry_format.eg_con        = _strtoul(argv[7], NULL, 0);
+        vlan_entry.vlan_entry_format.eg_ctrl       = _strtoul(argv[8], NULL, 0);
+
+        rc = air_vlan_create(0, vid, &vlan_entry);
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        rc = AIR_E_BAD_PARAMETER;
+    }
+
+    switch (rc)
+    {
+        case     AIR_E_OK:                                                            break;
+        case     AIR_E_ENTRY_EXISTS:  AIR_PRINT("VLAN already exist!\n");             break;
+        default:                      AIR_PRINT("Error %d: Operation failed!\n", rc); break;
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanCreate(UI32_T argc, C8_T *argv[])
+{
+    UI16_T vid = 0;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    vid = _strtoul(argv[0], NULL, 0);
+    rc  = air_vlan_create(0, vid, NULL);
+
+    switch (rc)
+    {
+        case     AIR_E_OK:                                                            break;
+        case     AIR_E_ENTRY_EXISTS:  AIR_PRINT("VLAN already exist!\n");             break;
+        default:                      AIR_PRINT("Error %d: Operation failed!\n", rc); break;
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanDestroy(UI32_T argc, C8_T *argv[])
+{
+    C8_T *token = NULL;
+    UI16_T vid = 0, vid_limit = AIR_VLAN_ID_MAX;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    if (argc > 0)
+    {
+        if (isdigit(argv[0][0]))
+        {
+            token = _strtok(argv[0], "-", &argv[0]);
+            vid = _strtoul(token, NULL, 0);
+            if ((token = _strtok(argv[0], "-", &argv[0])))
+                vid_limit = _strtoul(token, NULL, 0);
+            else
+                vid_limit = vid;
+            if (AIR_VLAN_ID_MAX < vid_limit)
+            {
+                AIR_PRINT("vid number should less than %d!\n", AIR_VLAN_ID_MAX);
+                return AIR_E_BAD_PARAMETER;
+            }
+            if (vid > vid_limit)
+            {
+                AIR_PRINT("vid0 should less than vid1!\n");
+                return AIR_E_BAD_PARAMETER;
+            }
+        }
+        else
+        {
+            AIR_PRINT("Bad parameter!\n");
+            return AIR_E_BAD_PARAMETER;
+        }
+    }
+
+    for (; vid <= vid_limit; vid++)
+    {
+        rc = air_vlan_destroy(0, vid);
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanDestroyAll(UI32_T argc, C8_T *argv[])
+{
+    UI32_T restore_def_vlan = 0;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    if (argc > 0)
+    {
+        restore_def_vlan = _strtoul(argv[0], NULL, 0);
+    }
+
+    rc = air_vlan_destroyAll(0, restore_def_vlan);
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("Error: Operation failed!\n");
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanDump(UI32_T argc, C8_T *argv[])
+{
+    C8_T *token = NULL;
+    UI16_T port = 0, valid_count = 0, vid = 0, vid_limit = AIR_VLAN_ID_MAX;
+    AIR_PORT_EGS_TAG_ATTR_T tag_ctl[AIR_MAX_NUM_OF_PORTS] = {0};
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    if (argc > 0)
+    {
+        if (isdigit(argv[0][0]))
+        {
+            token = _strtok(argv[0], "-", &argv[0]);
+            vid = _strtoul(token, NULL, 0);
+            if ((token = _strtok(argv[0], "-", &argv[0])))
+                vid_limit = _strtoul(token, NULL, 0);
+            else
+                vid_limit = vid;
+            if (AIR_VLAN_ID_MAX < vid_limit)
+            {
+                AIR_PRINT("vid number should less than %d!\n", AIR_VLAN_ID_MAX);
+                return AIR_E_BAD_PARAMETER;
+            }
+            if (vid > vid_limit)
+            {
+                AIR_PRINT("vid0 should less than vid1!\n");
+                return AIR_E_BAD_PARAMETER;
+            }
+        }
+        else
+        {
+            AIR_PRINT("Bad parameter!\n");
+            return AIR_E_BAD_PARAMETER;
+        }
+    }
+
+    for (valid_count = 0; vid <= vid_limit; vid++)
+    {
+        _air_vlan_readEntry(0, vid, &vlan_entry);
+        if (vlan_entry.valid)
+        {
+            valid_count++;
+            if (1 == valid_count)
+                AIR_PRINT(" Vid Fid MemPort Ivl PortBaseStag Stag EgsTagCtlEn EgsTagCon EgsTagCtl\n======================================================================\n");
+            for (port = 0; port < AIR_MAX_NUM_OF_PORTS; port++)
+                tag_ctl[port] = (vlan_entry.vlan_entry_format.eg_ctrl >> (port * 2)) & 0x3;
+            AIR_PRINT("%4d %3d      %2x %3d %12d %4d %11d %9d   %1x%1x%1x%1x%1x%1x%1x\n",
+                vid, vlan_entry.vlan_entry_format.fid, vlan_entry.vlan_entry_format.port_mem, vlan_entry.vlan_entry_format.ivl,
+                vlan_entry.vlan_entry_format.port_stag, vlan_entry.vlan_entry_format.stag, vlan_entry.vlan_entry_format.eg_ctrl_en, vlan_entry.vlan_entry_format.eg_con,
+                tag_ctl[6], tag_ctl[5], tag_ctl[4], tag_ctl[3], tag_ctl[2], tag_ctl[1], tag_ctl[0]);
+        }
+    }
+
+    if (!valid_count)
+        AIR_PRINT("not found!\n");
+    else
+        AIR_PRINT("Found %d valid entries!\n", valid_count);
+
+    return AIR_E_OK;
+}
+
+static AIR_ERROR_NO_T
+doVlanAddPortMem(UI32_T argc, C8_T *argv[])
+{
+    UI16_T vid = 0, port = 0;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    vid  = _strtoul(argv[0], NULL, 0);
+    port = _strtoul(argv[1], NULL, 0);
+    rc = air_vlan_addMemberPort(0, vid, port);
+    switch (rc)
+    {
+        case     AIR_E_OK:                                                               break;
+        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;
+        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanDelPortMem(UI32_T argc, C8_T *argv[])
+{
+    UI16_T vid = 0, port = 0;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    vid  = _strtoul(argv[0], NULL, 0);
+    port = _strtoul(argv[1], NULL, 0);
+    rc = air_vlan_delMemberPort(0, vid, port);
+    switch (rc)
+    {
+        case     AIR_E_OK:                                                               break;
+        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;
+        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanSetFid(UI32_T argc, C8_T *argv[])
+{
+    UI16_T vid = 0;
+    UI8_T  fid = 0;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    vid = _strtoul(argv[0], NULL, 0);
+    fid = _strtoul(argv[1], NULL, 0);
+    rc = air_vlan_setFid(0, vid, fid);
+    switch (rc)
+    {
+        case     AIR_E_OK:                                                               break;
+        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;
+        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanSetMemPort(UI32_T argc, C8_T *argv[])
+{
+    UI16_T vid = 0, port_bitmap = 0;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    vid         = _strtoul(argv[0], NULL, 0);
+    port_bitmap = _strtoul(argv[1], NULL, 16);
+    rc = air_vlan_setMemberPort(0, vid, port_bitmap);
+    switch (rc)
+    {
+        case     AIR_E_OK:                                                               break;
+        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;
+        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanSetIVL(UI32_T argc, C8_T *argv[])
+{
+    UI16_T vid = 0;
+    BOOL_T enable = TRUE;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    vid    = _strtoul(argv[0], NULL, 0);
+    enable = _strtoul(argv[1], NULL, 0);
+    rc = air_vlan_setIVL(0, vid, enable);
+    switch (rc)
+    {
+        case     AIR_E_OK:                                                               break;
+        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;
+        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanSetPortBaseStag(UI32_T argc, C8_T *argv[])
+{
+    UI16_T vid = 0;
+    BOOL_T enable = TRUE;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    vid    = _strtoul(argv[0], NULL, 0);
+    enable = _strtoul(argv[1], NULL, 0);
+    rc = air_vlan_setPortBasedStag(0, vid, enable);
+    switch (rc)
+    {
+        case     AIR_E_OK:                                                               break;
+        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;
+        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanSetStag(UI32_T argc, C8_T *argv[])
+{
+    UI16_T vid = 0, stag = 0;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    vid  = _strtoul(argv[0], NULL, 0);
+    stag = _strtoul(argv[1], NULL, 0);
+    rc = air_vlan_setServiceTag(0, vid, stag);
+    switch (rc)
+    {
+        case     AIR_E_OK:                                                               break;
+        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;
+        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanSetEgsTagCtlEn(UI32_T argc, C8_T *argv[])
+{
+    UI16_T vid = 0;
+    BOOL_T enable = TRUE;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    vid    = _strtoul(argv[0], NULL, 0);
+    enable = _strtoul(argv[1], NULL, 0);
+    rc = air_vlan_setEgsTagCtlEnable(0, vid, enable);
+    switch (rc)
+    {
+        case     AIR_E_OK:                                                               break;
+        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;
+        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanSetEgsTagCtlCon(UI32_T argc, C8_T *argv[])
+{
+    UI16_T vid = 0;
+    BOOL_T enable = TRUE;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    vid    = _strtoul(argv[0], NULL, 0);
+    enable = _strtoul(argv[1], NULL, 0);
+    rc = air_vlan_setEgsTagConsistent(0, vid, enable);
+    switch (rc)
+    {
+        case     AIR_E_OK:                                                               break;
+        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;
+        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanSetEgsTagCtl(UI32_T argc, C8_T *argv[])
+{
+    UI16_T vid = 0, port = 0;
+    AIR_PORT_EGS_TAG_ATTR_T tag_ctl = AIR_PORT_EGS_TAG_ATTR_UNTAGGED;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    vid     = _strtoul(argv[0], NULL, 0);
+    port    = _strtoul(argv[1], NULL, 0);
+    tag_ctl = _strtoul(argv[2], NULL, 0);
+    rc = air_vlan_setPortEgsTagCtl(0, vid, port, tag_ctl);
+    switch (rc)
+    {
+        case     AIR_E_OK:                                                               break;
+        case     AIR_E_ENTRY_NOT_FOUND:  AIR_PRINT("VLAN not found!\n");                 break;
+        default:                         AIR_PRINT("Error %d: Operation failed!\n", rc); break;
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanSetPortActFrame(UI32_T argc, C8_T *argv[])
+{
+    UI16_T port = 0;
+    AIR_VLAN_ACCEPT_FRAME_TYPE_T type = AIR_VLAN_ACCEPT_FRAME_TYPE_ALL;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port = _strtoul(argv[0], NULL, 0);
+    type = _strtoul(argv[1], NULL, 0);
+    rc = air_vlan_setPortAcceptFrameType(0, port, type);
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("Error: Operation failed!\n");
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanSetLeakyVlanEn(UI32_T argc, C8_T *argv[])
+{
+    UI16_T port = 0;
+    AIR_LEAKY_PKT_TYPE_T pkt_type = AIR_LEAKY_PKT_TYPE_UNICAST;
+    BOOL_T enable = TRUE;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port     = _strtoul(argv[0], NULL, 0);
+    pkt_type = _strtoul(argv[1], NULL, 0);
+    enable   = _strtoul(argv[2], NULL, 0);
+    rc = air_vlan_setPortLeakyVlanEnable(0, port, pkt_type, enable);
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("Error: Operation failed!\n");
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanSetPortVlanAttr(UI32_T argc, C8_T *argv[])
+{
+    UI16_T port = 0;
+    AIR_VLAN_PORT_ATTR_T attr = AIR_VLAN_PORT_ATTR_USER_PORT;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port = _strtoul(argv[0], NULL, 0);
+    attr = _strtoul(argv[1], NULL, 0);
+    rc = air_vlan_setPortAttr(0, port, attr);
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("Error: Operation failed!\n");
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanSetIgsPortETagAttr(UI32_T argc, C8_T *argv[])
+{
+    UI16_T port = 0;
+    AIR_IGR_PORT_EG_TAG_ATTR_T attr = AIR_IGR_PORT_EG_TAG_ATTR_DISABLE;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port = _strtoul(argv[0], NULL, 0);
+    attr = _strtoul(argv[1], NULL, 0);
+    rc = air_vlan_setIgrPortTagAttr(0, port, attr);
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("Error: Operation failed!\n");
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanSetPortETagAttr(UI32_T argc, C8_T *argv[])
+{
+    UI16_T port = 0;
+    AIR_PORT_EGS_TAG_ATTR_T attr = AIR_PORT_EGS_TAG_ATTR_UNTAGGED;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port = _strtoul(argv[0], NULL, 0);
+    attr = _strtoul(argv[1], NULL, 0);
+    rc = air_vlan_setPortEgsTagAttr(0, port, attr);
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("Error: Operation failed!\n");
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanSetPortOuterTPID(UI32_T argc, C8_T *argv[])
+{
+    UI16_T port = 0, tpid = 0;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port = _strtoul(argv[0], NULL, 0);
+    tpid = _strtoul(argv[1], NULL, 16);
+    rc = air_vlan_setPortOuterTPID(0, port, tpid);
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("Error: Operation failed!\n");
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanSetPvid(UI32_T argc, C8_T *argv[])
+{
+    UI16_T port = 0, pvid = 0;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port = _strtoul(argv[0], NULL, 0);
+    pvid = _strtoul(argv[1], NULL, 0);
+    rc = air_vlan_setPortPVID(0, port, pvid);
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("Error: Operation failed!\n");
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanSet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(vlanSetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doVlanGetPortActFrame(UI32_T argc, C8_T *argv[])
+{
+    UI32_T port = 0;
+    AIR_VLAN_ACCEPT_FRAME_TYPE_T type;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port = _strtoul(argv[0], NULL, 0);
+    rc = air_vlan_getPortAcceptFrameType(0, port, &type);
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("Error: Operation failed!\n");
+        return rc;
+    }
+    AIR_PRINT("Port %d Acceptable Frame Type: ", port);
+    switch(type)
+    {
+        case AIR_VLAN_ACCEPT_FRAME_TYPE_ALL:
+            AIR_PRINT("all(%d)\n", type);
+            break;
+        case AIR_VLAN_ACCEPT_FRAME_TYPE_TAG_ONLY:
+            AIR_PRINT("tagged-only(%d)\n", type);
+            break;
+        case AIR_VLAN_ACCEPT_FRAME_TYPE_UNTAG_ONLY:
+            AIR_PRINT("untagged-only(%d)\n", type);
+            break;
+        default:
+            AIR_PRINT("unknown(%d)\n", type);
+            break;
+    };
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanGetLeakyVlanEn(UI32_T argc, C8_T *argv[])
+{
+    UI32_T port = 0;
+    BOOL_T uc = FALSE, mc = FALSE, bc = FALSE;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port = _strtoul(argv[0], NULL, 0);
+    rc += air_vlan_getPortLeakyVlanEnable(0, port, AIR_LEAKY_PKT_TYPE_UNICAST, &uc);
+    rc += air_vlan_getPortLeakyVlanEnable(0, port, AIR_LEAKY_PKT_TYPE_MULTICAST, &mc);
+    rc += air_vlan_getPortLeakyVlanEnable(0, port, AIR_LEAKY_PKT_TYPE_BROADCAST, &bc);
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("Error: Operation failed!\n");
+        return rc;
+    }
+
+    AIR_PRINT("Port %d Leaky Vlan Enable\n", port);
+    AIR_PRINT("Unicast     : %s\n", uc ? "TRUE" : "FALSE");
+    AIR_PRINT("Multicast   : %s\n", mc ? "TRUE" : "FALSE");
+    AIR_PRINT("Broadcast   : %s\n", bc ? "TRUE" : "FALSE");
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanGetPortVlanAttr(UI32_T argc, C8_T *argv[])
+{
+    UI32_T port = 0;
+    AIR_VLAN_PORT_ATTR_T attr;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port = _strtoul(argv[0], NULL, 0);
+    rc = air_vlan_getPortAttr(0, port, &attr);
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("Error: Operation failed!\n");
+        return rc;
+    }
+    AIR_PRINT("Port %d Vlan Attr: ", port);
+    switch(attr)
+    {
+        case AIR_VLAN_PORT_ATTR_USER_PORT:
+            AIR_PRINT("user port(%d)\n", attr);
+            break;
+        case AIR_VLAN_PORT_ATTR_STACK_PORT:
+            AIR_PRINT("stack port(%d)\n", attr);
+            break;
+        case AIR_VLAN_PORT_ATTR_TRANSLATION_PORT:
+            AIR_PRINT("translation port(%d)\n", attr);
+            break;
+        case AIR_VLAN_PORT_ATTR_TRANSPARENT_PORT:
+            AIR_PRINT("transparent port(%d)\n", attr);
+            break;
+        default:
+            AIR_PRINT("unknown(%d)\n", attr);
+            break;
+    };
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanGetIgsPortETagAttr(UI32_T argc, C8_T *argv[])
+{
+    UI32_T port = 0;
+    AIR_IGR_PORT_EG_TAG_ATTR_T attr;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port = _strtoul(argv[0], NULL, 0);
+    rc = air_vlan_getIgrPortTagAttr(0, port, &attr);
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("Error: Operation failed!\n");
+        return rc;
+    }
+    AIR_PRINT("Port %d Incomming Port Egress Tag Attr: ", port);
+    switch(attr)
+    {
+        case AIR_IGR_PORT_EG_TAG_ATTR_DISABLE:
+            AIR_PRINT("disable(%d)\n", attr);
+            break;
+        case AIR_IGR_PORT_EG_TAG_ATTR_CONSISTENT:
+            AIR_PRINT("consistent(%d)\n", attr);
+            break;
+        case AIR_IGR_PORT_EG_TAG_ATTR_UNTAGGED:
+            AIR_PRINT("untagged(%d)\n", attr);
+            break;
+        case AIR_IGR_PORT_EG_TAG_ATTR_SWAP:
+            AIR_PRINT("swap(%d)\n", attr);
+            break;
+        case AIR_IGR_PORT_EG_TAG_ATTR_TAGGED:
+            AIR_PRINT("tagged(%d)\n", attr);
+            break;
+        case AIR_IGR_PORT_EG_TAG_ATTR_STACK:
+            AIR_PRINT("stack(%d)\n", attr);
+            break;
+        default:
+            AIR_PRINT("unknown(%d)\n", attr);
+            break;
+    };
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanGetPortETagAttr(UI32_T argc, C8_T *argv[])
+{
+    UI32_T port = 0;
+    AIR_PORT_EGS_TAG_ATTR_T attr;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port = _strtoul(argv[0], NULL, 0);
+    rc = air_vlan_getPortEgsTagAttr(0, port, &attr);
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("Error: Operation failed!\n");
+        return rc;
+    }
+    AIR_PRINT("Port %d Egress Tag Attr: ", port);
+    switch(attr)
+    {
+        case AIR_PORT_EGS_TAG_ATTR_UNTAGGED:
+            AIR_PRINT("untagged(%d)\n", attr);
+            break;
+        case AIR_PORT_EGS_TAG_ATTR_SWAP:
+            AIR_PRINT("swap(%d)\n", attr);
+            break;
+        case AIR_PORT_EGS_TAG_ATTR_TAGGED:
+            AIR_PRINT("tagged(%d)\n", attr);
+            break;
+        case AIR_PORT_EGS_TAG_ATTR_STACK:
+            AIR_PRINT("stack(%d)\n", attr);
+            break;
+        default:
+            AIR_PRINT("unknown(%d)\n", attr);
+            break;
+    };
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanGetPortOuterTPID(UI32_T argc, C8_T *argv[])
+{
+    UI32_T port = 0;
+    UI16_T tpid = 0;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port = _strtoul(argv[0], NULL, 0);
+    rc = air_vlan_getPortOuterTPID(0, port, &tpid);
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("Error: Operation failed!\n");
+        return rc;
+    }
+    AIR_PRINT("Port %d Outer TPID: %4x\n", port, tpid);
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanGetPvid(UI32_T argc, C8_T *argv[])
+{
+    UI32_T port = 0;
+    UI16_T pvid = 0;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port = _strtoul(argv[0], NULL, 0);
+    rc = air_vlan_getPortPVID(0, port, &pvid);
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("Error: Operation failed!\n");
+        return rc;
+    }
+    AIR_PRINT("Port %d PVID: %d\n", port, pvid);
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doVlanGet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(vlanGetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doVlan(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(vlanCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doJumbo(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    I32_T pkt_len = 0, frame_len = 0;
+
+    if(0 == argc)
+    {
+        /* get command */
+        ret = air_port_getJumbo(0, &pkt_len, &frame_len);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Get ");
+            switch(pkt_len)
+            {
+                case 0:
+                    AIR_PRINT("RX_1518 ");
+                    break;
+                case 1:
+                    AIR_PRINT("RX_1536 ");
+                    break;
+                case 2:
+                    AIR_PRINT("RX_1552 ");
+                    break;
+                case 3:
+                    AIR_PRINT("RX_JUMBO ");
+                    break;
+            }
+            AIR_PRINT("frames lengths %d KBytes\n", frame_len);
+        }
+        else
+        {
+            AIR_PRINT("Get Jumbo Fail.\n");
+        }
+    }
+    else
+    {
+        /* set command */
+        pkt_len = _strtol(argv[0], NULL, 10);
+        frame_len = _strtol(argv[1], NULL, 10);
+
+        ret = air_port_setJumbo(0, pkt_len, frame_len);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Set ");
+            switch(pkt_len)
+            {
+                case 0:
+                    AIR_PRINT("RX_1518 ");
+                    break;
+                case 1:
+                    AIR_PRINT("RX_1536 ");
+                    break;
+                case 2:
+                    AIR_PRINT("RX_1552 ");
+                    break;
+                case 3:
+                    AIR_PRINT("RX_JUMBO ");
+                    break;
+            }
+            AIR_PRINT("frames lengths %d KBytes\n", frame_len);
+        }
+        else
+            AIR_PRINT("Set Jumbo Fail.\n");
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doFlowCtrl(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    BOOL_T fc_en = 0, dir = 0;
+    I32_T port = 0;
+
+    port = _strtol(argv[0], NULL, 10);
+    dir = _strtol(argv[1], NULL, 10);
+
+    if(2 == argc)
+    {
+        /* get command */
+        ret = air_port_getFlowCtrl(0, port, dir, &fc_en);
+        if(ret == AIR_E_OK)
+            AIR_PRINT("Get Port%02d %s Flow Control %s\n", port, ((dir)?"RX":"TX"), ((fc_en)?"Enable":"Disable"));
+        else
+            AIR_PRINT("Get Flow Control Fail.\n");
+    }
+    else
+    {
+        /* set command */
+        fc_en = _strtol(argv[2], NULL, 10);
+
+        ret = air_port_setFlowCtrl(0, port, dir, fc_en);
+        if(ret == AIR_E_OK)
+            AIR_PRINT("Set Port%02d %s Flow Control %s\n", port, ((dir)?"RX":"TX"), ((fc_en)?"Enable":"Disable"));
+        else
+            AIR_PRINT("Set Flow Control Fail.\n");
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doL2Set(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(l2SetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doL2Get(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(l2GetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doL2Clear(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(l2ClearCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doL2Del(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(l2DelCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doL2Add(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(l2AddCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doL2(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(l2Cmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doAnMode(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0;
+    BOOL_T en = 0;
+
+    port = _strtol(argv[0], NULL, 10);
+
+    if(1 == argc)
+    {
+        /* port get anCap <port> */
+        ret = air_port_getAnMode(0, port, &en);
+        if(ret == AIR_E_OK)
+            AIR_PRINT("Get Port%02d Auto-Negotiation %s\n", port, ((en)?"Enabled":"Disabled"));
+        else
+            AIR_PRINT("Get Port%02d Auto-Negotiation Fail.\n", port);
+    }
+    else if(2 == argc)
+    {
+        /* "port set anMode <port> <en> */
+        en = _strtol(argv[1], NULL, 10);
+        ret = air_port_setAnMode(0, port, en);
+        if(ret == AIR_E_OK)
+            AIR_PRINT("Set Port%02d Auto-Negotiation Mode:%s\n", port, ((en)?"Enabled":"Disabled"));
+        else
+            AIR_PRINT("Set Port%02d Auto-Negotiation Fail.\n", port);
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doLocalAdv(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0;
+    AIR_AN_ADV_T adv;
+
+    memset(&adv, 0, sizeof(AIR_AN_ADV_T));
+    port = _strtol(argv[0], NULL, 10);
+
+    if(1 == argc)
+    {
+        /* port get localAdv <port> */
+        ret = air_port_getLocalAdvAbility(0, port, &adv);
+        AIR_PRINT("Get Port%02d Local Auto-Negotiation Advertisement: ", port);
+        if(AIR_E_OK != ret)
+        {
+            AIR_PRINT("Fail!\n");
+        }
+    }
+    else if(7 == argc)
+    {
+        /* port set localAdv <port> <10H> <10F> <100H> <100F> <1000F> <pause> */
+        adv.advCap10HDX = _strtol(argv[1], NULL, 0) & BIT(0);
+        adv.advCap10FDX = _strtol(argv[2], NULL, 0) & BIT(0);
+        adv.advCap100HDX = _strtol(argv[3], NULL, 0) & BIT(0);
+        adv.advCap100FDX = _strtol(argv[4], NULL, 0) & BIT(0);
+        adv.advCap1000FDX = _strtol(argv[5], NULL, 0) & BIT(0);
+        adv.advPause = _strtol(argv[6], NULL, 0) & BIT(0);
+        ret = air_port_setLocalAdvAbility(0, port, adv);
+        AIR_PRINT("Set Port%02d Local Auto-Negotiation Advertisement: ", port);
+        if(AIR_E_OK != ret)
+        {
+            AIR_PRINT("Fail!\n");
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    if(ret == AIR_E_OK)
+    {
+        AIR_PRINT("\n");
+        AIR_PRINT("\tAdvertise 10BASE-T Half Duplex: %s\n", (adv.advCap10HDX)?"Effective":"Not Effective" );
+        AIR_PRINT("\tAdvertise 10BASE-T Full Duplex: %s\n", (adv.advCap10FDX)?"Effective":"Not Effective" );
+        AIR_PRINT("\tAdvertise 100BASE-T Half Duplex: %s\n", (adv.advCap100HDX)?"Effective":"Not Effective" );
+        AIR_PRINT("\tAdvertise 100BASE-T Full Duplex: %s\n", (adv.advCap100FDX)?"Effective":"Not Effective" );
+        AIR_PRINT("\tAdvertise 1000BASE-T Full Duplex: %s\n", (adv.advCap1000FDX)?"Effective":"Not Effective" );
+        AIR_PRINT("\tAdvertise Asynchronous Pause: %s\n", (adv.advPause)?"Effective":"Not Effective" );
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doRemoteAdv(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0;
+    AIR_AN_ADV_T lp_adv;
+
+    memset(&lp_adv, 0, sizeof(AIR_AN_ADV_T));
+    port = _strtol(argv[0], NULL, 10);
+
+    if(1 == argc)
+    {
+        /* port get remoteAdv <port> */
+        ret = air_port_getRemoteAdvAbility(0, port, &lp_adv);
+        AIR_PRINT("Get Port%02d Remote Auto-Negotiation Advertisement: ", port);
+        if(AIR_E_OK != ret)
+        {
+            AIR_PRINT("Fail!\n");
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    if(ret == AIR_E_OK)
+    {
+        AIR_PRINT("\n");
+        AIR_PRINT("\tAdvertise 10BASE-T Half Duplex: %s\n", lp_adv.advCap10HDX?"Effective":"Not Effective" );
+        AIR_PRINT("\tAdvertise 10BASE-T Full Duplex: %s\n", lp_adv.advCap10FDX?"Effective":"Not Effective" );
+        AIR_PRINT("\tAdvertise 100BASE-T Half Duplex: %s\n", lp_adv.advCap100HDX?"Effective":"Not Effective" );
+        AIR_PRINT("\tAdvertise 100BASE-T Full Duplex: %s\n", lp_adv.advCap100FDX?"Effective":"Not Effective" );
+        AIR_PRINT("\tAdvertise 1000BASE-T Full Duplex: %s\n", (lp_adv.advCap1000FDX)?"Effective":"Not Effective" );
+        AIR_PRINT("\tAdvertise Asynchronous Pause: %s\n", (lp_adv.advPause)?"Effective":"Not Effective" );
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doPortSpeed(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0;
+    UI32_T speed = 0;
+
+    port = _strtol(argv[0], NULL, 10);
+
+    if(1 == argc)
+    {
+        /* port get speed <port> */
+        ret = air_port_getSpeed(0, port, &speed);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Get Port%02d Speed:", port);
+        }
+        else
+        {
+            AIR_PRINT("Get Port%02d Speed Fail!\n", port);
+        }
+    }
+    else if(2 == argc)
+    {
+        /* port set speed <port> <speed> */
+        speed = _strtol(argv[1], NULL, 10);
+        ret = air_port_setSpeed(0, port, speed);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Set Port%02d Speed:", port);
+        }
+        else
+        {
+            AIR_PRINT("Set Port%02d Speed Fail!\n", port);
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    if(ret == AIR_E_OK)
+    {
+        switch(speed)
+        {
+            case AIR_PORT_SPEED_10M:
+                AIR_PRINT(" 10 Mbps\n");
+                break;
+            case AIR_PORT_SPEED_100M:
+                AIR_PRINT(" 100 Mbps\n");
+                break;
+            case AIR_PORT_SPEED_1000M:
+                AIR_PRINT(" 1 Gbps\n");
+                break;
+            case AIR_PORT_SPEED_2500M:
+                AIR_PRINT(" 2.5 Gbps\n");
+                break;
+            default:
+                AIR_PRINT(" Reserved\n");
+                break;
+        }
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doPortDuplex(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0;
+    UI32_T duplex = 0;
+
+    port = _strtol(argv[0], NULL, 10);
+
+    if(1 == argc)
+    {
+        /* port get duplex <port> */
+        ret = air_port_getDuplex(0, port, &duplex);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Get Port%02d Duplex:%s\n", port, duplex?"Full":"Half");
+        }
+        else
+        {
+            AIR_PRINT("Get Port%02d Duplex Fail!\n", port);
+        }
+    }
+    else if(2 == argc)
+    {
+        /* port set duplex <port> <duplex> */
+        duplex = _strtol(argv[1], NULL, 10);
+        ret = air_port_setDuplex(0, port, duplex);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Set Port%02d Duplex:%s\n", port, duplex?"Full":"Half");
+        }
+        else
+        {
+            AIR_PRINT("Set Port%02d Duplex Fail!\n", port);
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doPortStatus(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0;
+    AIR_PORT_STATUS_T ps;
+
+    memset(&ps, 0, sizeof(AIR_PORT_STATUS_T));
+    port = _strtol(argv[0], NULL, 10);
+
+    if(1 == argc)
+    {
+        /* port get status <port> */
+        ret = air_port_getLink(0, port, &ps);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Get Port%02d Link-Status\n", port);
+            AIR_PRINT("\tLink: %s\n", ps.link?"Up":"Down");
+            AIR_PRINT("\tDuplex: %s\n", ps.duplex?"Full":"Half");
+            AIR_PRINT("\tSpeed: ");
+            switch(ps.speed)
+            {
+                case AIR_PORT_SPEED_10M:
+                    AIR_PRINT("10 Mbps\n");
+                    break;
+                case AIR_PORT_SPEED_100M:
+                    AIR_PRINT("100 Mbps\n");
+                    break;
+                case AIR_PORT_SPEED_1000M:
+                    AIR_PRINT("1 Gbps\n");
+                    break;
+                case AIR_PORT_SPEED_2500M:
+                    AIR_PRINT("2.5 Gbps\n");
+                    break;
+                default:
+                    AIR_PRINT("Reserved\n");
+                    break;
+            }
+        }
+        else
+            AIR_PRINT("Get Port%02d Link-Status Fail!", port);
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doPortBckPres(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0;
+    UI32_T bckPres = 0;
+
+    port = _strtol(argv[0], NULL, 10);
+
+    if(1 == argc)
+    {
+        /* port get bckPres <port> */
+        ret = air_port_getBckPres(0, port, &bckPres);
+        if(ret == AIR_E_OK)
+            AIR_PRINT("Get Port%02d BckPres:%s\n", port, bckPres?"Enabled":"Disabled");
+        else
+            AIR_PRINT("Get Port%02d BckPres Fail!\n", port);
+    }
+    else if(2 == argc)
+    {
+        /* port set bckPres <port> <bckPres> */
+        bckPres = _strtol(argv[1], NULL, 10);
+        ret = air_port_setBckPres(0, port, bckPres);
+        if(ret == AIR_E_OK)
+            AIR_PRINT("Set Port%02d BckPres:%s\n", port, bckPres?"Enabled":"Disabled");
+        else
+            AIR_PRINT("Set Port%02d BckPres Fail!\n", port);
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doPortPsMode(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0;
+    UI32_T mode = 0;
+    BOOL_T ls_en = 0;
+    BOOL_T eee_en = 0;
+
+    port = _strtol(argv[0], NULL, 10);
+
+    if(1 == argc)
+    {
+        /* port get psMode <port> */
+        ret = air_port_getPsMode(0, port, &mode);
+        AIR_PRINT("Get Port%02d Power-Saving: ", port);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Done\n");
+        }
+        else
+        {
+            AIR_PRINT("Fail!\n");
+        }
+    }
+    else if(3 == argc)
+    {
+        /* port set psMode <port> <ls> <eee> */
+        ls_en = _strtol(argv[1], NULL, 0);
+        eee_en = _strtol(argv[2], NULL, 0);
+        if(TRUE == ls_en)
+        {
+            mode |= AIR_PORT_PS_LINKSTATUS;
+        }
+        if(TRUE == eee_en)
+        {
+            mode |= AIR_PORT_PS_EEE;
+        }
+        ret = air_port_setPsMode(0, port, mode);
+        AIR_PRINT("Set Port%02d Power-Saving: ", port);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Done\n");
+        }
+        else
+        {
+            AIR_PRINT("Fail!\n");
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+    if(ret == AIR_E_OK)
+    {
+        AIR_PRINT("\tLink status:%s\n", (mode & AIR_PORT_PS_LINKSTATUS)?"Enable":"Disable");
+        AIR_PRINT("\tEEE:%s\n", (mode & AIR_PORT_PS_EEE)?"Enable":"Disable");
+    }
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doPortSmtSpdDwn(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0;
+    UI32_T state = 0;
+    UI32_T retry = 0;
+
+    port = _strtol(argv[0], NULL, 10);
+
+    if(1 == argc)
+    {
+        /* port get smtSpdDwn <port> */
+        ret = air_port_getSmtSpdDwn(0, port, &state, &retry);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Get Port%02d Smart Speed Down: %s\n", port, state?"Enabled":"Disabled");
+            AIR_PRINT("Get Port%02d Retry Time: %d\n", port, retry + 2);
+        }
+        else
+            AIR_PRINT("Get Port%02d Smart-SpeedDown Fail!\n", port);
+    }
+    else if(3 == argc)
+    {
+        /* port set smtSpdDwn <port> <en> <retry> */
+        state = _strtol(argv[1], NULL, 10);
+        retry = _strtol(argv[2], NULL, 10);
+        if(retry >= 2)
+        {
+            ret = air_port_setSmtSpdDwn(0, port, state, retry - 2);
+            if(ret == AIR_E_OK)
+            {
+                AIR_PRINT("Set Port%02d Smart Speed Down: %s\n", port, state?"Enabled":"Disabled");
+                AIR_PRINT("Set Port%02d Retry Time: %d\n", port, retry);
+            }
+            else
+                AIR_PRINT("Set Port%02d Smart-SpeedDown Fail!\n", port);
+        }
+        else
+        {
+            ret = AIR_E_BAD_PARAMETER;
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doPortSpTag(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0;
+    BOOL_T sptag_en = FALSE;
+
+    port = _strtol(argv[0], NULL, 10);
+
+    if(1 == argc)
+    {
+        /* port get spTag <port> */
+        ret = air_port_getSpTag(0, port, &sptag_en);
+        if(AIR_E_OK == ret)
+        {
+            AIR_PRINT("Get Port%02d Special Tag %s\n", port, ((sptag_en)?"Enabled":"Disabled"));
+        }
+        else
+        {
+            AIR_PRINT("Get Port%02d Special Tag Fail.\n", port);
+        }
+    }
+    else if(2 == argc)
+    {
+        /* port set spTag <port> <en> */
+        sptag_en = _strtol(argv[1], NULL, 10);
+        ret = air_port_setSpTag(0, port, sptag_en);
+        if(AIR_E_OK == ret)
+        {
+            AIR_PRINT("Set Port%02d Special Tag:%s\n", port, ((sptag_en)?"Enabled":"Disabled"));
+        }
+        else
+        {
+            AIR_PRINT("Set Port%02d Special Tag Fail.\n", port);
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doPortEnable(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0;
+    UI32_T state = 0;
+
+    port = _strtol(argv[0], NULL, 10);
+
+    if(1 == argc)
+    {
+        /* port get enable <port> */
+        ret = air_port_getEnable(0, port, &state);
+        if(ret == AIR_E_OK)
+            AIR_PRINT("Get Port%02d State:%s\n", port, state?"Enable":"Disable");
+        else
+            AIR_PRINT("Get Port%02d State Fail!\n", port);
+    }
+    else if(2 == argc)
+    {
+        /* port set enable <port> <en> */
+        state = _strtol(argv[1], NULL, 10);
+        ret = air_port_setEnable(0, port, state);
+        if(ret == AIR_E_OK)
+            AIR_PRINT("Set Port%02d State:%s\n", port, state?"Enable":"Disable");
+        else
+            AIR_PRINT("Set Port%02d State Fail!\n", port);
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doPort5GBaseRMode(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+
+    if(0 == argc)
+    {
+        /* port set 5GBaseRMode */
+        ret = air_port_set5GBaseRModeEn(0);
+        if(ret == AIR_E_OK)
+            AIR_PRINT("Set Port05 Mode: 5GBase-R\n");
+        else
+            AIR_PRINT("Set Port05 HSGMII Mode Fail.\n");
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doPortHsgmiiMode(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+
+    if(0 == argc)
+    {
+        /* port set hsgmiiMode */
+        ret = air_port_setHsgmiiModeEn(0);
+        if(ret == AIR_E_OK)
+            AIR_PRINT("Set Port05 Mode: HSGMII\n");
+        else
+            AIR_PRINT("Set Port05 HSGMII Mode Fail.\n");
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doPortSgmiiMode(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T mode = 0;
+    UI32_T speed = 0;
+
+    if(2 == argc)
+    {
+        /* port set sgmiiMode <mode(0:AN,1:Force)> <speed> */
+        mode = _strtol(argv[0], NULL, 10);
+        speed = _strtol(argv[1], NULL, 10);
+        ret = air_port_setSgmiiMode(0, mode, speed);
+        if(ret == AIR_E_OK)
+            AIR_PRINT("Set Port05 SGMII Mode:%s\nIf in Force Mode, speed:", mode?"Force":"AN");
+        else
+            AIR_PRINT("Set Port05 SGMII Mode Fail.\n");
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    if(ret == AIR_E_OK)
+    {
+        switch(speed)
+        {
+            case AIR_PORT_SPEED_10M:
+                AIR_PRINT(" 10 Mbps\n");
+                break;
+            case AIR_PORT_SPEED_100M:
+                AIR_PRINT(" 100 Mbps\n");
+                break;
+            case AIR_PORT_SPEED_1000M:
+                AIR_PRINT(" 1 Gbps\n");
+                break;
+            default:
+                AIR_PRINT(" Reserved\n");
+                break;
+        }
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doPortRmiiMode(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T speed = 0;
+
+    if(1 == argc)
+    {
+        /* port set rmiiMode <speed> */
+        speed = _strtol(argv[0], NULL, 10);
+        ret = air_port_setRmiiMode(0, speed);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Set Port05 RMII Mode Speed:");
+        }
+        else
+        {
+            AIR_PRINT("Set Port05 RMII Mode Fail!\n");
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    if(ret == AIR_E_OK)
+    {
+        switch(speed)
+        {
+            case AIR_PORT_SPEED_10M:
+                AIR_PRINT(" 10 Mbps\n");
+                break;
+            case AIR_PORT_SPEED_100M:
+                AIR_PRINT(" 100 Mbps\n");
+                break;
+            default:
+                AIR_PRINT(" Reserved\n");
+                break;
+        }
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doPortRgmiiMode(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T speed = 0;
+
+    if(1 == argc)
+    {
+        /* port set rgmiiMode <speed> */
+        speed = _strtol(argv[0], NULL, 10);
+        ret = air_port_setRgmiiMode(0, speed);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Set Port05 RGMII Mode Speed:");
+        }
+        else
+        {
+            AIR_PRINT("Set Port05 RGMII Mode Fail!\n");
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    if(ret == AIR_E_OK)
+    {
+        switch(speed)
+        {
+            case AIR_PORT_SPEED_10M:
+                AIR_PRINT(" 10 Mbps\n");
+                break;
+            case AIR_PORT_SPEED_100M:
+                AIR_PRINT(" 100 Mbps\n");
+                break;
+            case AIR_PORT_SPEED_1000M:
+                AIR_PRINT(" 1 Gbps\n");
+                break;
+            default:
+                AIR_PRINT(" Reserved\n");
+                break;
+        }
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doSptagEn(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0;
+    BOOL_T sp_en = FALSE;
+
+    port =  _strtol(argv[0], NULL, 10);
+    if (2 == argc)
+    {
+        sp_en =  _strtol(argv[1], NULL, 10);
+        ret = air_sptag_setState(0,port,sp_en);
+        if(AIR_E_OK == ret)
+        {
+            AIR_PRINT("set port %d SpTag state %s sucess\n", port,sp_en?"Enable":"Disable");
+        }
+        else
+        {
+            AIR_PRINT("set port %d SpTag state %s fail\n", port,sp_en?"Enable":"Disable");
+        }
+    }
+    else if(1 == argc)
+    {
+        air_sptag_getState(0,port,&sp_en);
+        AIR_PRINT("get port %d SpTag state: %s \n", port,sp_en?"Enable":"Disable");
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doSptagMode(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0;
+    BOOL_T sp_mode = FALSE;
+
+
+    port =  _strtol(argv[0], NULL, 10);
+    if (2 == argc)
+    {
+        sp_mode  =  _strtol(argv[1], NULL, 10);
+        ret = air_sptag_setMode(0,port,sp_mode);
+        if(AIR_E_OK == ret)
+        {
+            AIR_PRINT("set port %d SpTag Mode  %s sucess\n", port,sp_mode?"replace":"insert");
+        }
+        else
+        {
+            AIR_PRINT("set port %d SpTag state %s fail\n", port,sp_mode?"replace":"insert");
+        }
+    }
+    else if(1 == argc)
+    {
+        air_sptag_getMode(0,port,&sp_mode);
+        AIR_PRINT("get port %d SpTag state: %s \n", port,sp_mode?"replace":"insert");
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doSptagDecode(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    AIR_SPTAG_RX_PARA_T sptag_rx = {0};
+    UI8_T buf[AIR_STAG_BUF_LEN] = {0};
+    UI32_T len = AIR_STAG_BUF_LEN, i = 0;
+
+    if (4 == argc)
+    {
+        for(i = 0; i < len; i++)
+        {
+            buf[i] = _strtoul(argv[i], NULL, 16);
+        }
+
+        ret = air_sptag_decodeRx(0, buf, len, &sptag_rx);
+        if (AIR_E_OK != ret)
+        {
+            AIR_PRINT("SpTag decode fail\n");
+            return ret;
+        }
+
+        AIR_PRINT("SpTag decode success:\n");
+        AIR_PRINT("RSN : %s\n", _sptag_pt[sptag_rx.rsn]);
+        AIR_PRINT("VPM : %s\n", _sptag_vpm[sptag_rx.vpm]);
+        AIR_PRINT("SPN : %d\n", sptag_rx.spn);
+        AIR_PRINT("PRI : %d\n", sptag_rx.pri);
+        AIR_PRINT("CFI : %d\n", sptag_rx.cfi);
+        AIR_PRINT("VID : %d\n", sptag_rx.vid);
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doSptagEncode(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    AIR_STAG_TX_PARA_T sptag_tx = {0};
+    C8_T *token = NULL;
+    UI8_T buf[AIR_STAG_BUF_LEN] = {0};
+    UI32_T len = AIR_STAG_BUF_LEN;
+    char str[128] = {'\0'};
+    UI32_T data = 0;
+    AIR_STAG_MODE_T mode = AIR_STAG_MODE_LAST;
+
+    if (7 == argc)
+    {
+        if(_strcmp(argv[0],"mode=replace") == 0)
+            mode = AIR_STAG_MODE_REPLACE;
+        else if(_strcmp(argv[0],"mode=insert") == 0)
+            mode = AIR_STAG_MODE_INSERT;
+        else
+            printf("mode is wrong!!");
+
+        if(_strcmp(argv[1],"opc=portmap") == 0)
+            sptag_tx.opc = AIR_STAG_OPC_PORTMAP;
+        else if(_strcmp(argv[1],"opc=portid") == 0)
+            sptag_tx.opc = AIR_STAG_OPC_PORTID;
+        else if(_strcmp(argv[1],"opc=lookup") == 0)
+            sptag_tx.opc = AIR_STAG_OPC_LOOKUP;
+        else
+            printf("opc is wrong!!");
+
+        token = _strtok(argv[2], "=", &argv[2]);
+        if(_strcmp(token,"dp") != 0) {
+            AIR_PRINT("Bad parameter\r\n");
+        } else {
+            if ((token = _strtok(argv[2], "=", &argv[2]))) {
+                data = _strtoul(token, NULL, 16);
+                sptag_tx.pbm = data;
+                AIR_PRINT("sptag_tx.pbm %x\n",sptag_tx.pbm);
+            }
+        }
+
+        if(_strcmp(argv[3],"vpm=untagged") == 0)
+            sptag_tx.vpm = AIR_STAG_VPM_UNTAG;
+        else if(_strcmp(argv[3],"vpm=8100") == 0)
+            sptag_tx.vpm = AIR_STAG_VPM_TPID_8100;
+        else if(_strcmp(argv[3],"vpm=88a8") == 0)
+            sptag_tx.vpm = AIR_STAG_VPM_TPID_88A8;
+        else
+            printf("vpm is wrong!!");
+
+        token = _strtok(argv[4], "=", &argv[4]);
+        if(_strcmp(token, "pri") != 0) {
+            AIR_PRINT("Bad parameter\r\n");
+        } else {
+            if ((token = _strtok(argv[4], "=", &argv[4]))) {
+                data = _strtoul(token, NULL, 0);
+                sptag_tx.pri = data;
+                AIR_PRINT("sptag_tx.pri %d\n",sptag_tx.pri);
+            }
+        }
+
+        token = _strtok(argv[5], "=", &argv[5]);
+        if(_strcmp(token, "cfi") != 0) {
+            AIR_PRINT("Bad parameter\r\n");
+        } else {
+            if ((token = _strtok(argv[5], "=", &argv[5]))) {
+                data = _strtoul(token, NULL, 0);
+                sptag_tx.cfi  = data;
+                AIR_PRINT("sptag_tx.cfi %d\n",sptag_tx.cfi);
+            }
+        }
+
+        token = _strtok(argv[6], "=", &argv[6]);
+        if(_strcmp(token, "vid") != 0) {
+            AIR_PRINT("Bad parameter\r\n");
+        } else {
+            if ((token = _strtok(argv[6], "=", &argv[6]))) {
+                data = _strtoul(token, NULL, 0);
+                sptag_tx.vid = data;
+                AIR_PRINT("sptag_tx.vid %d\n",sptag_tx.vid);
+            }
+        }
+
+        ret = air_sptag_encodeTx(0,mode, &sptag_tx, (UI8_T *)&buf, &len);
+        if(AIR_E_OK == ret)
+        {
+            AIR_PRINT("SpTag encode sucess, returned len=%d\n", len);
+            AIR_PRINT("Encoded SpTag: %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]);
+        }
+        else
+        {
+            AIR_PRINT("SpTag encode fail\n");
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doSptag(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(sptagCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doL2Dump(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(l2DumpCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doMacAddr(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    AIR_MAC_ENTRY_T mt;
+    UI32_T fwd = 0;
+
+    memset(&mt, 0, sizeof(AIR_MAC_ENTRY_T));
+
+    if(0 == argc)
+    {
+        /* l2 clear mac */
+        ret = air_l2_clearMacAddr(0);
+        if(ret == AIR_E_OK)
+            AIR_PRINT("Clear MAC Address Table Done.\n");
+        else
+            AIR_PRINT("Clear MAC Address Table Fail.\n");
+    }
+    else if(3 == argc)
+    {
+        /* l2 del mac <mac(12'hex)> { vid <vid(0..4095)> | fid <fid(0..15)> } */
+        ret = _str2mac(argv[0], (C8_T *)mt.mac);
+        if(ret != AIR_E_OK)
+        {
+            AIR_PRINT("Unrecognized command.\n");
+            return ret;
+        }
+
+        /* check argument 1 */
+        if(FALSE == _strcmp(argv[1], "vid"))
+        {
+            /* get mac entry by MAC address & vid */
+            mt.cvid = _strtoul(argv[2], NULL, 0);
+            mt.flags |= AIR_L2_MAC_ENTRY_FLAGS_IVL;
+            AIR_PRINT("Get MAC Address:" MAC_STR " with vid:%u", MAC2STR(mt.mac), mt.cvid);
+        }
+        else if(FALSE == _strcmp(argv[1], "fid"))
+        {
+            /* get mac entry by MAC address & fid */
+            mt.fid = _strtoul(argv[2], NULL, 0);
+            AIR_PRINT("Get MAC Address:" MAC_STR " with fid:%u", MAC2STR(mt.mac), mt.fid);
+        }
+        else
+        {
+            AIR_PRINT("Unrecognized command.\n");
+            return AIR_E_BAD_PARAMETER;
+        }
+        ret = air_l2_delMacAddr(0, &mt);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT(" Done.\n");
+        }
+        else
+            AIR_PRINT("\n Fail!\n");
+    }
+    else if(7 == argc)
+    {
+        /* l2 add mac <static(0:dynamic,1:static)> <unauth(0:auth,1:unauth)> <mac(12'hex)> <portlist(uintlist)> [ vid <vid(0..4095)> | fid <fid(0..15)> ] <src_mac_forward=(0:default,1:cpu-include,2:cpu-exclude,3:cpu-only,4:drop)> */
+        if(argv[0])
+            mt.flags |= AIR_L2_MAC_ENTRY_FLAGS_STATIC;
+
+        if(argv[1])
+            mt.flags |= AIR_L2_MAC_ENTRY_FLAGS_UNAUTH;
+
+        ret = _str2mac(argv[2], (C8_T *)mt.mac);
+        if(ret != AIR_E_OK)
+        {
+            AIR_PRINT("Unrecognized command.\n");
+            return ret;
+        }
+
+        ret = _portListStr2Ary(argv[3], mt.port_bitmap, 1);
+        if(ret != AIR_E_OK)
+        {
+            AIR_PRINT("Unrecognized command.\n");
+            return ret;
+        }
+
+        /* check argument fid or vid */
+        if(FALSE == _strcmp(argv[4], "vid"))
+        {
+            /* get mac entry by MAC address & vid */
+            mt.cvid = _strtoul(argv[5], NULL, 0);
+            mt.flags |= AIR_L2_MAC_ENTRY_FLAGS_IVL;
+        }
+        else if(FALSE == _strcmp(argv[4], "fid"))
+        {
+            /* get mac entry by MAC address & fid */
+            mt.fid = _strtoul(argv[5], NULL, 0);
+        }
+        else
+        {
+            AIR_PRINT("Unrecognized command.\n");
+            return AIR_E_BAD_PARAMETER;
+        }
+        fwd = _strtoul(argv[6], NULL, 0);
+        if(0 == fwd)
+            mt.sa_fwd = AIR_L2_FWD_CTRL_DEFAULT;
+        else if(1 == fwd)
+            mt.sa_fwd = AIR_L2_FWD_CTRL_CPU_INCLUDE;
+        else if(2 == fwd)
+            mt.sa_fwd = AIR_L2_FWD_CTRL_CPU_EXCLUDE;
+        else if(3 == fwd)
+            mt.sa_fwd = AIR_L2_FWD_CTRL_CPU_ONLY;
+        else if(4 == fwd)
+            mt.sa_fwd = AIR_L2_FWD_CTRL_DROP;
+        else
+        {
+            AIR_PRINT("Unrecognized command.\n");
+            return AIR_E_BAD_PARAMETER;
+        }
+        ret = air_l2_addMacAddr(0, &mt);
+        AIR_PRINT("Add MAC Address:" MAC_STR, MAC2STR(mt.mac));
+        if(ret == AIR_E_OK)
+            AIR_PRINT(" Done.\n");
+        else
+            AIR_PRINT(" Fail.\n");
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+_printMacEntry(
+        AIR_MAC_ENTRY_T *mt,
+        UI32_T age_unit,
+        UI8_T count,
+        UI8_T title)
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    I32_T i = 0, j = 0;
+    UI8_T first = 0;
+    UI8_T find = 0;
+    if(title)
+    {
+        AIR_PRINT("%-6s%-15s%-5s%-5s%-5s%-10s%-10s%-6s\n",
+                "unit",
+                "mac",
+                "ivl",
+                "vid",
+                "fid",
+                "age-time",
+                "forward",
+                "port");
+        return ret;
+    }
+    for(i = 0; i < count; i++)
+    {
+        AIR_PRINT("%-6d", age_unit);
+        AIR_PRINT(MAC_STR, MAC2STR(mt[i].mac));
+        AIR_PRINT("...");
+        if(mt[i].flags & AIR_L2_MAC_ENTRY_FLAGS_IVL)
+        {
+            AIR_PRINT("%-3s..", "ivl");
+            AIR_PRINT("%-5d", mt[i].cvid);
+            AIR_PRINT("%-5s", ".....");
+        }
+        else
+        {
+            AIR_PRINT("%-3s..", "svl");
+            AIR_PRINT("%-5s", ".....");
+            AIR_PRINT("%-5d", mt[i].fid);
+        }
+        if(mt[i].flags & AIR_L2_MAC_ENTRY_FLAGS_STATIC)
+        {
+            AIR_PRINT("%-10s.", "static");
+        }
+        else
+        {
+            AIR_PRINT("%d sec..", mt[i].timer);
+        }
+        AIR_PRINT("%-10s", _air_mac_address_forward_control_string[mt[i].sa_fwd]);
+        first = 0;
+        find = 0;
+        for (j = (AIR_MAX_NUM_OF_PORTS - 1); j >= 0; j--)
+        {
+            if((mt[i].port_bitmap[0]) & (1 << j))
+            {
+                first = j;
+                find = 1;
+                break;
+            }
+        }
+        if(find)
+        {
+            for (j = 0; j < AIR_MAX_NUM_OF_PORTS; j++)
+            {
+                if((mt[i].port_bitmap[0]) & (1 << j))
+                {
+                    if(j == first)
+                        AIR_PRINT("%-2d", j);
+                    else
+                        AIR_PRINT("%-2d,", j);
+                }
+            }
+        }
+        else
+            AIR_PRINT("no dst port");
+        AIR_PRINT("\n");
+    }
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doGetMacAddr(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI8_T count = 0;
+    AIR_MAC_ENTRY_T * ptr_mt;
+
+    if(3 == argc)
+    {
+        ptr_mt = AIR_MALLOC(sizeof(AIR_MAC_ENTRY_T));
+        if (NULL == ptr_mt)
+        {
+            AIR_PRINT("***Error***, allocate memory fail\n");
+            return AIR_E_OTHERS;
+        }
+        memset(ptr_mt, 0, sizeof(AIR_MAC_ENTRY_T));
+        /* l2 get mac <mac(12'hex)> { vid <vid(0..4095)> | fid <fid(0..15)> } */
+        ret = _str2mac(argv[0], (C8_T *)ptr_mt->mac);
+        if(ret != AIR_E_OK)
+        {
+            AIR_PRINT("Unrecognized command.\n");
+            AIR_FREE(ptr_mt);
+            return ret;
+        }
+
+        /* check argument 1 */
+        if(FALSE == _strcmp(argv[1], "vid"))
+        {
+            /* get mac entry by MAC address & vid */
+            ptr_mt->cvid = _strtoul(argv[2], NULL, 0);
+            ptr_mt->flags |= AIR_L2_MAC_ENTRY_FLAGS_IVL;
+            AIR_PRINT("Get MAC Address:" MAC_STR " with vid:%u", MAC2STR(ptr_mt->mac), ptr_mt->cvid);
+        }
+        else if(FALSE == _strcmp(argv[1], "fid"))
+        {
+            /* get mac entry by MAC address & fid */
+            ptr_mt->fid = _strtoul(argv[2], NULL, 0);
+            AIR_PRINT("Get MAC Address:" MAC_STR " with fid:%u", MAC2STR(ptr_mt->mac), ptr_mt->fid);
+        }
+        else
+        {
+            AIR_PRINT("Unrecognized command.\n");
+            AIR_FREE(ptr_mt);
+            return AIR_E_BAD_PARAMETER;
+        }
+        ret = air_l2_getMacAddr(0, &count, ptr_mt);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT(" Done.\n");
+            _printMacEntry(ptr_mt, 0, 1, TRUE);
+            _printMacEntry(ptr_mt, 0, 1, FALSE);
+        }
+        else
+            AIR_PRINT("\n Not found!\n");
+        AIR_FREE(ptr_mt);
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doMacAddrAgeOut(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T time = 0;
+    if(0 == argc)
+    {
+        /* l2 get macAddrAgeOut */
+        ret = air_l2_getMacAddrAgeOut(0, &time);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Get MAC Address Age Out Time Done.\n");
+        }
+        else
+        {
+            AIR_PRINT("Get MAC Address Age Out Time Fail.\n");
+        }
+    }
+    else if(1 == argc)
+    {
+        /* l2 set macAddrAgeOut <time(1, 1000000)> */
+        time = _strtoul(argv[0], NULL, 0);
+        if(time < 1 || time > 1000000)
+        {
+            AIR_PRINT("Unrecognized command.\n");
+            return AIR_E_BAD_PARAMETER;
+        }
+        ret = air_l2_setMacAddrAgeOut(0, time);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Set MAC Address Age Out Time Done.\n");
+        }
+        else
+        {
+            AIR_PRINT("Set MAC Address Age Out Time Fail.\n");
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    if(ret == AIR_E_OK)
+    {
+        AIR_PRINT("MAC Address Age Out Time: %u seconds.\n", time);
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doDumpMacAddr(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    AIR_MAC_ENTRY_T *ptr_mt;
+    UI8_T count = 0;
+    UI32_T bucket_size = 0;
+    UI32_T total_count = 0;
+
+    if(0 != argc)
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+    /* get unit of aging time */
+    ret = air_l2_getMacBucketSize(0, &bucket_size);
+    if(ret != AIR_E_OK)
+    {
+        AIR_PRINT("Get MAC Age Time Fail!\n");
+        return ret;
+    }
+    ptr_mt = AIR_MALLOC(sizeof(AIR_MAC_ENTRY_T) * bucket_size);
+    if (NULL == ptr_mt)
+    {
+        AIR_PRINT("***Error***, allocate memory fail\n");
+        return AIR_E_OTHERS;
+    }
+    memset(ptr_mt, 0, sizeof(AIR_MAC_ENTRY_T) * bucket_size);
+    _printMacEntry(ptr_mt, 0, count, TRUE);
+    /* get 1st entry of MAC table */
+    ret = air_l2_getMacAddr(0, &count, ptr_mt);
+    switch(ret)
+    {
+        case AIR_E_ENTRY_NOT_FOUND:
+            AIR_FREE(ptr_mt);
+            AIR_PRINT("Not Found!\n");
+            return ret;
+        case AIR_E_TIMEOUT:
+            AIR_FREE(ptr_mt);
+            AIR_PRINT("Time Out!\n");
+            return ret;
+        case AIR_E_BAD_PARAMETER:
+            AIR_FREE(ptr_mt);
+            AIR_PRINT("Bad Parameter!\n");
+            return ret;
+        default:
+            break;
+    }
+    total_count += count;
+    _printMacEntry(ptr_mt, 0, count, FALSE);
+
+    /* get other entries of MAC table */
+    while(1)
+    {
+        memset(ptr_mt, 0, sizeof(AIR_MAC_ENTRY_T) * bucket_size);
+        ret = air_l2_getNextMacAddr(0, &count, ptr_mt);
+        if(AIR_E_OK != ret)
+        {
+            break;
+        }
+        total_count += count;
+        _printMacEntry(ptr_mt, 0, count, FALSE);
+    }
+    switch(ret)
+    {
+        case AIR_E_TIMEOUT:
+            AIR_PRINT("Time Out!\n");
+            break;
+        case AIR_E_BAD_PARAMETER:
+            AIR_PRINT("Bad Parameter!\n");
+            break;
+        default:
+            AIR_PRINT("Found %u %s\n", total_count, (total_count>1)?"entries":"entry");
+            break;
+    }
+    AIR_FREE(ptr_mt);
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doLagMember(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T portrunk_index = 0, member_index = 0, member_enable = 0, port_index = 0, i = 0;
+    AIR_LAG_PTGINFO_T  member;
+    memset(&member,0,sizeof(AIR_LAG_PTGINFO_T));
+
+    if(4 == argc)
+    {
+        /* lag set member <port trunk index> <member index> <member enable> <port_index>*/
+        portrunk_index  = _strtol(argv[0], NULL, 10);
+        member_index    = _strtol(argv[1], NULL, 10);
+        member_enable   = _strtol(argv[2], NULL, 10);
+        port_index      = _strtol(argv[3], NULL, 10);
+        ret = air_lag_setMember(0, portrunk_index, member_index, member_enable,port_index);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Set port trunk index %d member_index:%d member_enable:%d, port_index:%d ok.\n", portrunk_index, member_index, member_enable,port_index);
+        }
+        else
+        {
+            AIR_PRINT("Set port trunk index %d member_index:%d member_enable:%d, port_index:%d fail.\n", portrunk_index, member_index, member_enable,port_index);
+        }
+        memset(&member,0,sizeof(member));
+        air_lag_getMember(0, portrunk_index, &member);
+        if(! member.csr_gp_enable[0])
+        {
+            AIR_PRINT("\r\n!!!!!!!!!Port trunk index %d member_index:0 must be set,or else have taffic issues.\n", portrunk_index);
+        }
+    }
+    else if(1 == argc)
+    {
+        portrunk_index = _strtol(argv[0], NULL, 10);
+
+        /* lag get member <port> */
+        memset(&member,0,sizeof(member));
+        ret = air_lag_getMember(0, portrunk_index, &member);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Get port trunk %u member:\n", portrunk_index);
+            for(i = 0; i < AIR_LAG_MAX_MEM_NUM; i++)
+            {
+                if(member.csr_gp_enable[i])
+                    AIR_PRINT("port %d \r\n", member.csr_gp_port[i]);
+            }
+            AIR_PRINT("\r\n");
+        }
+        else
+        {
+            AIR_PRINT("Get port trunk:%u Member Fail.\n", portrunk_index);
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+
+static AIR_ERROR_NO_T
+doLagDstInfo(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    AIR_LAG_DISTINFO_T dstInfo;
+
+    memset(&dstInfo, 0, sizeof(AIR_LAG_DISTINFO_T));
+    if(7 == argc)
+    {
+        /* lag set dstInfo <sp> <sa> <da> <sip> <dip> <sport> <dport> */
+        dstInfo.sp = _strtol(argv[0], NULL, 10) & BIT(0);
+        dstInfo.sa = _strtol(argv[1], NULL, 10) & BIT(0);
+        dstInfo.da = _strtol(argv[2], NULL, 10) & BIT(0);
+        dstInfo.sip = _strtol(argv[3], NULL, 10) & BIT(0);
+        dstInfo.dip = _strtol(argv[4], NULL, 10) & BIT(0);
+        dstInfo.sport = _strtol(argv[5], NULL, 10) & BIT(0);
+        dstInfo.dport = _strtol(argv[6], NULL, 10) & BIT(0);
+        ret = air_lag_setDstInfo(0, dstInfo);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Set LAG packet distrubution.\n");
+        }
+        else
+        {
+            AIR_PRINT("Set LAG packet distrubution Fail.\n");
+        }
+    }
+    else if(0 == argc)
+    {
+        /* lag get dstInfo */
+        ret = air_lag_getDstInfo(0, &dstInfo);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Get LAG packet distrubution:\n");
+        }
+        else
+        {
+            AIR_PRINT("Get LAG packet distrubution Fail.\n");
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+    if(ret == AIR_E_OK)
+    {
+        AIR_PRINT("%-5s|%-5s|%-5s|%-5s|%-5s|%-5s|%-5s\n",
+                "SP", "SA", "DA", "SIP", "DIP", "SPORT", "DPORT");
+        AIR_PRINT("%-5s|%-5s|%-5s|%-5s|%-5s|%-5s|%-5s\n",
+                (dstInfo.sp)?"En":"Dis",
+                (dstInfo.sa)?"En":"Dis",
+                (dstInfo.da)?"En":"Dis",
+                (dstInfo.sip)?"En":"Dis",
+                (dstInfo.dip)?"En":"Dis",
+                (dstInfo.sport)?"En":"Dis",
+                (dstInfo.dport)?"En":"Dis");
+    }
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doLagHashtype(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T hashtype = 0;
+
+    if(1 == argc)
+    {
+        hashtype = _strtol(argv[0], NULL, 10);
+        ret = air_lag_sethashtype(0, hashtype);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Set LAG hashtype Ok.\n");
+        }
+        else
+        {
+            AIR_PRINT("Set LAG hashtype Fail.\n");
+        }
+    }
+    else if(0 == argc)
+    {
+        /* lag get dstInfo */
+        ret = air_lag_gethashtype(0, &hashtype);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Get LAG hashtype:\n");
+        }
+        else
+        {
+            AIR_PRINT("Get LLAG hashtype Fail.\n");
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+    if(ret == AIR_E_OK)
+    {
+        switch (hashtype)
+        {
+            case 0:
+                AIR_PRINT("hashtype:crc32lsb.\n");
+                break;
+            case 1:
+                AIR_PRINT("hashtype:crc32msb.\n");
+                break;
+            case 2:
+                AIR_PRINT("hashtype:crc16.\n");
+                break;
+            case 3:
+                AIR_PRINT("hashtype:xor4.\n");
+                break;
+            default:
+                AIR_PRINT("wrong hashtype:%d.\n",hashtype);
+        }
+
+    }
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doLagPtseed(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T ptseed = 0;
+
+    if(1 == argc)
+    {
+        ptseed = _strtol(argv[0], NULL, 16);
+        ret = air_lag_setPTSeed(0, ptseed);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Set LAG Port Seed:%x(hex) ok\n",ptseed);
+        }
+        else
+        {
+            AIR_PRINT("Set LAG Port Seed:%x(hex) fail\n",ptseed);
+        }
+    }
+    else if(0 == argc)
+    {
+        /* lag get seed */
+        ret = air_lag_getPTSeed(0, &ptseed);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Get port trunk seed: %x(hex)\n",ptseed);
+        }
+        else
+        {
+            AIR_PRINT("Get port trunk seed Fail.\n");
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doLagSpsel(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0;
+    UI32_T state = 0;
+
+    if(1 == argc)
+    {
+        /* lag set spsel <state> */
+        state = _strtol(argv[0], NULL, 10);
+        ret = air_lag_setSpSel(0,state);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Set source port compare function:%s.\n", (state)?"Enabled":"Disabled");
+        }
+        else
+        {
+            AIR_PRINT("Set source port compare function Fail.\n");
+        }
+    }
+    else if(0 == argc)
+    {
+        /* lag get spsel*/
+        ret = air_lag_getSpSel(0, &state);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Get source port compare function:%s.\n", (state)?"Enabled":"Disabled");
+        }
+        else
+        {
+            AIR_PRINT("Get source port compare function Fail.\n");
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+
+static AIR_ERROR_NO_T
+doLagState(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0;
+    UI32_T state = 0;
+
+    if(1 == argc)
+    {
+        /* lag set state <state> */
+        state = _strtol(argv[0], NULL, 10);
+        ret = air_lag_set_ptgc_state(0,state);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Set LAG Port Trunk State:%s.\n", (state)?"Enabled":"Disabled");
+        }
+        else
+        {
+            AIR_PRINT("Set LAG Port Trunk State Fail.\n");
+        }
+    }
+    else if(0 == argc)
+    {
+        /* lag get state*/
+        ret = air_lag_get_ptgc_state(0, &state);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Get LAG Port Trunk State:%s.\n", (state)?"Enabled":"Disabled");
+        }
+        else
+        {
+            AIR_PRINT("Get LAG Port Trunk State Fail.\n");
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doLagGet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(lagGetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doLagSet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(lagSetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doLag(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(lagCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doStpPortstate(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0;
+    UI32_T fid = 0;
+    UI32_T state = 0;
+
+    port = _strtol(argv[0], NULL, 10);
+    fid = _strtol(argv[1], NULL, 10);
+    if(3 == argc)
+    {
+        /* stp set portstate <port> <fid(0..15)> <state> */
+        state = _strtol(argv[2], NULL, 10);
+        ret = air_stp_setPortstate(0, port, fid, state);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Set STP Port:%u FID:%u State:", port, fid);
+            switch(state)
+            {
+                case AIR_STP_STATE_DISABLE:
+                    AIR_PRINT("Disable(STP) / Discard(RSTP).\n");
+                    break;
+                case AIR_STP_STATE_LISTEN:
+                    AIR_PRINT("Listening(STP) / Discard(RSTP).\n");
+                    break;
+                case AIR_STP_STATE_LEARN:
+                    AIR_PRINT("Learning(STP) / Learning(RSTP).\n");
+                    break;
+                case AIR_STP_STATE_FORWARD:
+                    AIR_PRINT("Forwarding(STP) / Forwarding(RSTP).\n");
+                    break;
+                default:
+                    break;
+            }
+        }
+        else
+        {
+            AIR_PRINT("Set STP Port:%u FID:%u State Fail.", port, fid);
+        }
+    }
+    else if(2 == argc)
+    {
+        /* stp get portstate <port> <fid(0..15)> */
+        ret = air_stp_getPortstate(0, port, fid, &state);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Get STP Port:%u FID:%u State:", port, fid);
+            switch(state)
+            {
+                case AIR_STP_STATE_DISABLE:
+                    AIR_PRINT("Disable(STP) / Discard(RSTP).\n");
+                    break;
+                case AIR_STP_STATE_LISTEN:
+                    AIR_PRINT("Listening(STP) / Discard(RSTP).\n");
+                    break;
+                case AIR_STP_STATE_LEARN:
+                    AIR_PRINT("Learning(STP) / Learning(RSTP).\n");
+                    break;
+                case AIR_STP_STATE_FORWARD:
+                    AIR_PRINT("Forwarding(STP) / Forwarding(RSTP).\n");
+                    break;
+                default:
+                    break;
+            }
+        }
+        else
+        {
+            AIR_PRINT("Get STP Port:%u FID:%u State Fail.", port, fid);
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doStpGet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(stpGetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doStpSet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(stpSetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doStp(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(stpCmds, argc, argv);
+}
+
+static void
+_mir_printPortList(UI32_T * mt)
+{
+    I8_T j = 0;
+    UI8_T first = 0;
+    UI8_T find = 0;
+    for(j = (AIR_MAX_NUM_OF_PORTS - 1); j >= 0; j--)
+    {
+        if((*mt) & (1 << j))
+        {
+            first = j;
+            find = 1;
+            break;
+        }
+    }
+    if(find)
+    {
+        for(j = 0; j < AIR_MAX_NUM_OF_PORTS; j++)
+        {
+            if((*mt) & (1 << j))
+            {
+                if(j == first)
+                    AIR_PRINT("%-2d", j);
+                else
+                    AIR_PRINT("%-2d,", j);
+            }
+        }
+    }
+    else
+        AIR_PRINT("NULL");
+    AIR_PRINT("\n");
+}
+
+static void
+_mir_printSrcPortList(
+    const UI32_T         unit,
+    const UI32_T         sessionid)
+{
+    I8_T i = 0;
+    AIR_MIR_SESSION_T   session;
+    AIR_PORT_BITMAP_T txPbm = {0}, rxPbm = {0};
+	AIR_ERROR_NO_T      rc = AIR_E_OK;
+
+    for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)
+    {
+         memset(&session, 0, sizeof(session));
+         session.src_port = i;
+         rc = air_mir_getMirrorPort(unit, sessionid, &session);
+		 if (AIR_E_OK != rc)
+		 {
+			AIR_PRINT("***Error***,get port=%u error\n", i);
+			return rc;
+		 }
+
+         if(session.flags & AIR_MIR_SESSION_FLAGS_DIR_TX)
+         {
+            txPbm[0] |= (1 << i);
+         }
+         if(session.flags & AIR_MIR_SESSION_FLAGS_DIR_RX)
+         {
+            rxPbm[0] |= (1 << i);
+         }
+    }
+    AIR_PRINT("Src PortList\n");
+    AIR_PRINT(" - Rx portlist = ");
+    _mir_printPortList(rxPbm);
+    AIR_PRINT(" - Tx portlist = ");
+    _mir_printPortList(txPbm);
+}
+
+static void
+_mir_printSession(
+    const UI32_T            unit,
+    const UI32_T            session_id,
+    const AIR_MIR_SESSION_T *ptr_session)
+{
+
+    AIR_PRINT("Session id: %d\n", session_id);
+    AIR_PRINT("State: %s \n", (ptr_session->flags & AIR_MIR_SESSION_FLAGS_ENABLE)? "enable": "disable");
+    AIR_PRINT("Tx tag: %s \n", (ptr_session->flags & AIR_MIR_SESSION_FLAGS_TX_TAG_OBEY_CFG)? "On": "Off");
+    AIR_PRINT("Dst port: %d \n", ptr_session->dst_port);
+    _mir_printSrcPortList(unit,session_id);
+}
+
+static AIR_ERROR_NO_T
+doMirrorGetSid(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T      rc = AIR_E_OK;
+    UI32_T              session_id = 0;
+    AIR_MIR_SESSION_T   session = {0};
+    I8_T i = 0;
+
+    session_id = _strtoul(argv[0], NULL, 0);
+    rc = air_mir_getSession(0, session_id, &session);
+    if (AIR_E_OK != rc)
+    {
+        AIR_PRINT("***Error***, get mirror session fail\n");
+        return rc;
+    }
+    /* print session information */
+    if(session.dst_port == AIR_PORT_INVALID)
+    {
+        AIR_PRINT("Session id %d not found\n", session_id);
+    }
+    else
+    {
+        _mir_printSession(0, session_id, &session);
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doMirrorDelSid(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T      rc = AIR_E_OK;
+    UI32_T              session_id = 0;
+    AIR_MIR_SESSION_T   session = {0};
+    UI8_T i = 0;
+
+    session_id = _strtoul(argv[0], NULL, 0);
+    rc = air_mir_delSession(0, session_id);
+    if (AIR_E_OK != rc)
+    {
+        AIR_PRINT("***Error***, del mirror session fail\n");
+        return rc;
+    }
+    for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)
+    {
+        session.src_port = i;
+        rc = air_mir_setMirrorPort(0, session_id, &session);
+        if (AIR_E_OK != rc)
+        {
+            AIR_PRINT("***Error***,port=%u error\n", i);
+            return rc;
+        }
+    }
+    if (rc != AIR_E_OK)
+    {
+        AIR_PRINT("***Error***, delete mirror session fail\n");
+    }
+    else
+        AIR_PRINT("***OK***, delete mirror session success\n");
+
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doMirrorAddRlist(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T      rc = AIR_E_OK;
+    UI32_T              session_id = 0;
+    AIR_MIR_SESSION_T   session = {0};
+    AIR_PORT_BITMAP_T rxPbm = {0};
+    UI8_T i = 0;
+
+    /*mirror add session-rlist <sid(0..3)> <list(UINTLIST)>*/
+    session_id = _strtoul(argv[0], NULL, 0);
+    rc = _portListStr2Ary(argv[1], rxPbm, 1);
+    if(rc != AIR_E_OK)
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return rc;
+    }
+    if(!rxPbm[0])
+    {
+        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)
+        {
+            memset(&session, 0, sizeof(AIR_MIR_SESSION_T));
+            session.src_port = i;
+            rc = air_mir_getMirrorPort(0, session_id, &session);
+            if (AIR_E_OK != rc)
+            {
+                AIR_PRINT("***Error***,get port=%u error\n", i);
+                return rc;
+            }
+
+            session.flags &= ~AIR_MIR_SESSION_FLAGS_DIR_RX;
+            session.src_port = i;
+            rc = air_mir_setMirrorPort(0, session_id, &session);
+            if (AIR_E_OK != rc)
+            {
+                AIR_PRINT("***Error***,set rx port=%u error\n", i);
+                return rc;
+            }
+        }
+    }
+    else
+    {
+        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)
+        {
+            if(rxPbm[0] & (1 << i))
+            {
+                memset(&session, 0, sizeof(AIR_MIR_SESSION_T));
+                session.src_port = i;
+                rc = air_mir_getMirrorPort(0, session_id, &session);
+                if (AIR_E_OK != rc)
+                {
+                    AIR_PRINT("***Error***,get port=%u error\n", i);
+                    return rc;
+                }
+
+                session.flags |= AIR_MIR_SESSION_FLAGS_DIR_RX;
+                session.src_port = i;
+                rc = air_mir_setMirrorPort(0, session_id, &session);
+                if (AIR_E_OK != rc)
+                {
+                    AIR_PRINT("***Error***,port=%u error\n", i);
+                    return rc;
+                }
+            }
+        }
+    }
+
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doMirrorAddTlist(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T      rc = AIR_E_OK;
+    UI32_T              session_id = 0;
+    AIR_MIR_SESSION_T   session = {0};
+    AIR_PORT_BITMAP_T txPbm = {0};
+    UI8_T i = 0;
+
+    /*mirror add session-tlist <sid(0..3)> <list(UINTLIST)>*/
+    session_id = _strtoul(argv[0], NULL, 0);
+    rc = _portListStr2Ary(argv[1], txPbm, 1);
+    if(rc != AIR_E_OK)
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return rc;
+    }
+    if(!txPbm[0])
+    {
+        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)
+        {
+            memset(&session, 0, sizeof(AIR_MIR_SESSION_T));
+            session.src_port = i;
+            rc = air_mir_getMirrorPort(0, session_id, &session);
+            if (AIR_E_OK != rc)
+            {
+                AIR_PRINT("***Error***,get port=%u error\n", i);
+                return rc;
+            }
+
+            session.flags &= ~AIR_MIR_SESSION_FLAGS_DIR_TX;
+            session.src_port = i;
+            rc = air_mir_setMirrorPort(0, session_id, &session);
+            if (AIR_E_OK != rc)
+            {
+                AIR_PRINT("***Error***,set rx port=%u error\n", i);
+                return rc;
+            }
+        }
+    }
+    else
+    {
+        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)
+        {
+            if(txPbm[0] & (1 << i))
+            {
+                memset(&session, 0, sizeof(AIR_MIR_SESSION_T));
+                session.src_port = i;
+                rc = air_mir_getMirrorPort(0, session_id, &session);
+                if (AIR_E_OK != rc)
+                {
+                    AIR_PRINT("***Error***,get port=%u error\n", i);
+                    return rc;
+                }
+
+                session.flags |= AIR_MIR_SESSION_FLAGS_DIR_TX;
+                session.src_port = i;
+                rc = air_mir_setMirrorPort(0, session_id, &session);
+                if (AIR_E_OK != rc)
+                {
+                    AIR_PRINT("***Error***,port=%u error\n", i);
+                    return rc;
+                }
+            }
+        }
+    }
+
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doMirrorSetSessionEnable(
+    UI32_T argc,
+    C8_T *argv[])
+
+{
+    AIR_ERROR_NO_T      rc = AIR_E_OK;
+    UI32_T              session_id = 0;
+    UI32_T              enable = 0;
+    BOOL_T              tmp_en = FALSE;
+
+    /*mirror set session-enable <sid(0..3)> <state(1:En,0:Dis)>*/
+    session_id = _strtoul(argv[0], NULL, 0);
+    enable = _strtoul(argv[1], NULL, 0);
+    if(enable)
+        tmp_en = TRUE;
+    /* set port mirror state */
+    rc = air_mir_setSessionAdminMode(0, session_id, tmp_en);
+    if(AIR_E_OK!=rc)
+    {
+        AIR_PRINT("***Error***\n");
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doMirrorSetSession(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T      rc = AIR_E_OK;
+    UI32_T              session_id = 0;
+    UI32_T              dst_port = 0;
+    UI8_T               enable = 0;
+    UI8_T               tag_en = 0;
+    UI8_T               dir = 0;
+    AIR_MIR_SESSION_T  session = {0};
+    AIR_PORT_BITMAP_T   rxPbm = {0};
+    I8_T               i = 0;
+
+    /*mirror set session <sid(0..3)> <dst_port(UINT)> <state(1:En,0:Dis)> <tag(1:on, 0:off)> <list(UINTLIST)> <dir(0:none,1:tx,2:rx,3:both)>*/
+    session_id = _strtoul(argv[0], NULL, 0);
+    dst_port = _strtoul(argv[1], NULL, 0);
+    AIR_PRINT("session id %d dst port %d.\n", session_id, dst_port);
+    session.dst_port = dst_port;
+    enable = _strtoul(argv[2], NULL, 0);
+    if(enable)
+    {
+        session.flags |= AIR_MIR_SESSION_FLAGS_ENABLE;
+    }
+    else
+    {
+        session.flags &= ~AIR_MIR_SESSION_FLAGS_ENABLE;
+    }
+    tag_en = _strtoul(argv[3], NULL, 0);
+    if(tag_en)
+    {
+        session.flags |= AIR_MIR_SESSION_FLAGS_TX_TAG_OBEY_CFG;
+    }
+    else
+    {
+        session.flags &= ~AIR_MIR_SESSION_FLAGS_TX_TAG_OBEY_CFG;
+    }
+    rc = _portListStr2Ary(argv[4], rxPbm, 1);
+    if(rc != AIR_E_OK)
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return rc;
+    }
+    AIR_PRINT("pbm %x.\n", rxPbm);
+    dir = _strtoul(argv[5], NULL, 0);
+    if(dir == 1)
+    {
+        session.flags |= AIR_MIR_SESSION_FLAGS_DIR_TX;
+    }
+    else if(dir == 2)
+    {
+        session.flags |= AIR_MIR_SESSION_FLAGS_DIR_RX;
+    }
+    else if(dir == 3)
+    {
+        session.flags |= AIR_MIR_SESSION_FLAGS_DIR_TX;
+        session.flags |= AIR_MIR_SESSION_FLAGS_DIR_RX;
+    }
+    else if (!dir)
+    {
+        session.flags &= ~AIR_MIR_SESSION_FLAGS_DIR_TX;
+        session.flags &= ~AIR_MIR_SESSION_FLAGS_DIR_RX;
+    }
+    else
+    {
+        return AIR_E_BAD_PARAMETER;
+    }
+    for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)
+    {
+        if(rxPbm[0] & (1 << i))
+        {
+            session.src_port = i;
+            /* set port mirror session */
+            rc = air_mir_addSession(0, session_id, &session);
+
+            if(AIR_E_OK!=rc)
+            {
+                AIR_PRINT("***Error***,dst-port=%u, src-port=%u error\n", session.dst_port, session.src_port);
+                return rc;
+            }
+            else
+                AIR_PRINT("add session %d,dst-port=%u, src-port=%u\n", session_id, session.dst_port, session.src_port);
+        }
+    }
+
+    return rc;
+}
+
+
+
+static AIR_ERROR_NO_T
+doMirrorSet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(mirrorSetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doMirrorAdd(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(mirrorAddCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doMirrorGet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(mirrorGetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doMirrorDel(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(mirrorDelCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doMirror(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(mirrorCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doMibClearPort(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0;
+
+    if(1 == argc)
+    {
+        /* mib clear port */
+        port = _strtoul(argv[0], NULL, 0);
+        ret = air_mib_clear_by_port(0,port);
+        AIR_PRINT("Clear port %d mib stats",port);
+        if(AIR_E_OK == ret)
+        {
+            AIR_PRINT("Done.\n");
+        }
+        else
+        {
+            AIR_PRINT("Fail.\n");
+        }
+    }
+    else if(0 == argc)
+    {
+        /*restart mib counter*/
+        air_mib_clear(0);
+        AIR_PRINT("Clear all mib stats",port);
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doMibClearAcl(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0;
+
+    if(0 == argc)
+    {
+        /* mib clear acl */
+        ret = air_mib_clearAclEvent(0);
+        AIR_PRINT("Clear ACL Event Counter ");
+        if(AIR_E_OK == ret)
+        {
+            AIR_PRINT("Done.\n");
+        }
+        else
+        {
+            AIR_PRINT("Fail.\n");
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doMibGetPort(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0;
+    UI32_T tmp32 = 0xffffffff;
+    AIR_MIB_CNT_RX_T rx_mib = {0};
+    AIR_MIB_CNT_TX_T tx_mib = {0};
+
+    port = _strtoul(argv[0], NULL, 0);
+    if(1 == argc)
+    {
+        /* mib get <port(0..6)> */
+        ret = air_mib_get(0, port, &rx_mib, &tx_mib);
+        AIR_PRINT("Get MIB Counter of Port %u ", port);
+        if(AIR_E_OK == ret)
+        {
+            AIR_PRINT("Done.\n");
+            AIR_PRINT("RX Drop Packet                    : %u\n", rx_mib.RDPC);
+            AIR_PRINT("RX filtering Packet               : %u\n", rx_mib.RFPC);
+            AIR_PRINT("RX Unicast Packet                 : %u\n", rx_mib.RUPC);
+            AIR_PRINT("RX Multicast Packet               : %u\n", rx_mib.RMPC);
+            AIR_PRINT("RX Broadcast Packet               : %u\n", rx_mib.RBPC);
+            AIR_PRINT("RX Alignment Error Packet         : %u\n", rx_mib.RAEPC);
+            AIR_PRINT("RX CRC Packet                     : %u\n", rx_mib.RCEPC);
+            AIR_PRINT("RX Undersize Packet               : %u\n", rx_mib.RUSPC);
+            AIR_PRINT("RX Fragment Error Packet          : %u\n", rx_mib.RFEPC);
+            AIR_PRINT("RX Oversize Packet                : %u\n", rx_mib.ROSPC);
+            AIR_PRINT("RX Jabber Error Packet            : %u\n", rx_mib.RJEPC);
+            AIR_PRINT("RX Pause Packet                   : %u\n", rx_mib.RPPC);
+            AIR_PRINT("RX Packet Length 64 bytes         : %u\n", rx_mib.RL64PC);
+            AIR_PRINT("RX Packet Length 65 ~ 127 bytes   : %u\n", rx_mib.RL65PC);
+            AIR_PRINT("RX Packet Length 128 ~ 255 bytes  : %u\n", rx_mib.RL128PC);
+            AIR_PRINT("RX Packet Length 256 ~ 511 bytes  : %u\n", rx_mib.RL256PC);
+            AIR_PRINT("RX Packet Length 512 ~ 1023 bytes : %u\n", rx_mib.RL512PC);
+            AIR_PRINT("RX Packet Length 1024 ~ 1518 bytes: %u\n", rx_mib.RL1024PC);
+            AIR_PRINT("RX Packet Length 1519 ~ max bytes : %u\n", rx_mib.RL1519PC);
+            AIR_PRINT("RX_CTRL Drop Packet               : %u\n", rx_mib.RCDPC);
+            AIR_PRINT("RX Ingress Drop Packet            : %u\n", rx_mib.RIDPC);
+            AIR_PRINT("RX ARL Drop Packet                : %u\n", rx_mib.RADPC);
+            AIR_PRINT("FLow Control Drop Packet          : %u\n", rx_mib.FCDPC);
+            AIR_PRINT("WRED Drop Packtet                 : %u\n", rx_mib.WRDPC);
+            AIR_PRINT("Mirror Drop Packet                : %u\n", rx_mib.MRDPC);
+            AIR_PRINT("RX  sFlow Sampling Packet         : %u\n", rx_mib.SFSPC);
+            AIR_PRINT("Rx sFlow Total Packet             : %u\n", rx_mib.SFTPC);
+            AIR_PRINT("Port Control Drop Packet          : %u\n", rx_mib.RXC_DPC);
+            AIR_PRINT("RX Octets good or bad packtes l32 : %u\n", (UI32_T)(rx_mib.ROC & tmp32));
+            AIR_PRINT("RX Octets good or bad packtes h32 : %u\n", (UI32_T)((rx_mib.ROC >> 32) & tmp32));
+            AIR_PRINT("RX Octets bad packets l32         : %u\n", (UI32_T)(rx_mib.ROC2 & tmp32));
+            AIR_PRINT("RX Octets bad packets h32         : %u\n", (UI32_T)((rx_mib.ROC2 >> 32) & tmp32));
+            AIR_PRINT("\n");
+            AIR_PRINT("TX Drop Packet                    : %u\n", tx_mib.TDPC);
+            AIR_PRINT("TX CRC Packet                     : %u\n", tx_mib.TCRC);
+            AIR_PRINT("TX Unicast Packet                 : %u\n", tx_mib.TUPC);
+            AIR_PRINT("TX Multicast Packet               : %u\n", tx_mib.TMPC);
+            AIR_PRINT("TX Broadcast Packet               : %u\n", tx_mib.TBPC);
+            AIR_PRINT("TX Collision Event Count          : %u\n", tx_mib.TCEC);
+            AIR_PRINT("TX Single Collision Event Count   : %u\n", tx_mib.TSCEC);
+            AIR_PRINT("TX Multiple Conllision Event Count: %u\n", tx_mib.TMCEC);
+            AIR_PRINT("TX Deferred Event Count           : %u\n", tx_mib.TDEC);
+            AIR_PRINT("TX Late Collision Event Count     : %u\n", tx_mib.TLCEC);
+            AIR_PRINT("TX Excessive Collision Event Count: %u\n", tx_mib.TXCEC);
+            AIR_PRINT("TX Pause Packet                   : %u\n", tx_mib.TPPC);
+            AIR_PRINT("TX Packet Length 64 bytes         : %u\n", tx_mib.TL64PC);
+            AIR_PRINT("TX Packet Length 65 ~ 127 bytes   : %u\n", tx_mib.TL65PC);
+            AIR_PRINT("TX Packet Length 128 ~ 255 bytes  : %u\n", tx_mib.TL128PC);
+            AIR_PRINT("TX Packet Length 256 ~ 511 bytes  : %u\n", tx_mib.TL256PC);
+            AIR_PRINT("TX Packet Length 512 ~ 1023 bytes : %u\n", tx_mib.TL512PC);
+            AIR_PRINT("TX Packet Length 1024 ~ 1518 bytes: %u\n", tx_mib.TL1024PC);
+            AIR_PRINT("TX Packet Length 1519 ~ max bytes : %u\n", tx_mib.TL1519PC);
+            AIR_PRINT("TX Oversize Drop Packet           : %u\n", tx_mib.TODPC);
+            AIR_PRINT("TX Octets good or bad packtes l32 : %u\n", (UI32_T)(tx_mib.TOC & tmp32));
+            AIR_PRINT("TX Octets good or bad packtes h32 : %u\n", (UI32_T)((tx_mib.TOC >> 32) & tmp32));
+            AIR_PRINT("TX Octets bad packets l32         : %u\n", (UI32_T)(tx_mib.TOC2 & tmp32));
+            AIR_PRINT("TX Octets bad packets h32         : %u\n", (UI32_T)((tx_mib.TOC2 >> 32) & tmp32));
+        }
+        else
+        {
+            AIR_PRINT("Fail.\n");
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doMibGetAcl(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T event = 0;
+    UI32_T cnt = 0;
+
+    if(1 == argc)
+    {
+        /* mib get acl <event(0..7)> */
+        event = _strtoul(argv[0], NULL, 0);
+        ret = air_mib_getAclEvent(0, event, &cnt);
+        AIR_PRINT("Get counter of ACL event %u ", event);
+        if(AIR_E_OK == ret)
+        {
+            AIR_PRINT("Done.\n");
+            AIR_PRINT("ACL Event Counter:%u\n", cnt);
+        }
+        else
+        {
+            AIR_PRINT("Fail.\n");
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doMibClear(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(mibClearCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doMibGet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(mibGetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doMib(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(mibCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doQosRateLimitExMngFrm(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T dir = 0;
+    BOOL_T enable = FALSE;
+
+    if(2 == argc)
+    {
+		dir = _strtoul(argv[0], NULL, 0);
+        if(dir == 0)
+            dir = AIR_QOS_RATE_DIR_EGRESS;
+        else if(dir == 1)
+            dir = AIR_QOS_RATE_DIR_INGRESS;
+        else
+        {
+            AIR_PRINT("Unrecognized command.\n");
+            ret = AIR_E_BAD_PARAMETER;
+            return ret;
+        }
+        enable = _strtoul(argv[1], NULL, 0);
+        ret = air_qos_setRateLimitExMngFrm(0, dir, enable);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Success.\n");
+            AIR_PRINT("Set %s Rate Limit Control %s management frame.\n",
+                    (AIR_QOS_RATE_DIR_INGRESS == dir)?"Ingress":"Egress",
+                    (TRUE == enable)?"exclude":"include");
+        }
+        else
+        {
+            AIR_PRINT("Fail.\n");
+            return ret;
+        }
+    }
+    else if(0 == argc)
+    {
+        dir = AIR_QOS_RATE_DIR_EGRESS;
+        ret = air_qos_getRateLimitExMngFrm(0, dir, &enable);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Success.\n");
+            AIR_PRINT("Get %s Rate Limit Control %s management frame.\n",
+                    (AIR_QOS_RATE_DIR_INGRESS == dir)?"Ingress":"Egress",
+                    (TRUE == enable)?"exclude":"include");
+        }
+        else
+        {
+            AIR_PRINT("Fail.\n");
+            return ret;
+        }
+        dir = AIR_QOS_RATE_DIR_INGRESS;
+        ret = air_qos_getRateLimitExMngFrm(0, dir, &enable);
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT("Success.\n");
+            AIR_PRINT("Get %s Rate Limit Control %s management frame.\n",
+                    (AIR_QOS_RATE_DIR_INGRESS == dir)?"Ingress":"Egress",
+                    (TRUE == enable)?"exclude":"include");
+        }
+        else
+        {
+            AIR_PRINT("Fail.\n");
+            return ret;
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+
+}
+
+static AIR_ERROR_NO_T
+doQosPortPriority(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    AIR_PORT_BITMAP_T portlist = {0};
+    UI32_T priority = 0;
+    UI8_T i = 0;
+
+    ret = _portListStr2Ary(argv[0], portlist, 1);
+    if(ret != AIR_E_OK)
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return ret;
+    }
+    if(2 == argc)
+    {
+        priority = _strtoul(argv[1], NULL, 0);
+        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)
+        {
+            if(portlist[0] & (1 << i))
+            {
+                ret = air_qos_setPortPriority(0, i, priority);
+                if(ret == AIR_E_OK)
+                {
+                    AIR_PRINT("Set Port%02d port based priority %d Success.\n", i, priority);
+                }
+                else
+                {
+                    AIR_PRINT("Set Port%02d port based priority %d Fail.\n", i, priority);
+                }
+            }
+        }
+    }
+    else if(1 == argc)
+    {
+        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)
+        {
+            if(portlist[0] & (1 << i))
+            {
+                ret = air_qos_getPortPriority(0, i, &priority);
+                if(ret == AIR_E_OK)
+                {
+                    AIR_PRINT("Get Port%d port based priority %d.\n", i, priority);
+                }
+                else
+                {
+                    AIR_PRINT("Get Port%d port based priority Fail.\n", i);
+                }
+            }
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doQosRateLimit(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    AIR_PORT_BITMAP_T portlist = {0};
+    AIR_QOS_RATE_LIMIT_CFG_T rl = {0};
+    UI8_T i = 0;
+
+    ret = _portListStr2Ary(argv[0], portlist, 1);
+    if(ret != AIR_E_OK)
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return ret;
+    }
+    if(5 == argc)
+    {
+        rl.ingress_cir = _strtoul(argv[1], NULL, 0);
+        rl.ingress_cbs = _strtoul(argv[2], NULL, 0);
+        rl.egress_cir = _strtoul(argv[3], NULL, 0);
+        rl.egress_cbs = _strtoul(argv[4], NULL, 0);
+        rl.flags |= AIR_QOS_RATE_LIMIT_CFG_FLAGS_ENABLE_INGRESS;
+        rl.flags |= AIR_QOS_RATE_LIMIT_CFG_FLAGS_ENABLE_EGRESS;
+        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)
+        {
+            if(portlist[0] & (1 << i))
+            {
+                ret = air_qos_setRateLimit(0, i, &rl);
+                if(ret == AIR_E_OK)
+                {
+                    AIR_PRINT("Set Port%02d Ingress CIR %d CBS %d Egress CIR %d CBS %d Success.\n", i, rl.ingress_cir, rl.ingress_cbs, rl.egress_cir, rl.egress_cbs);
+                }
+                else
+                {
+                    AIR_PRINT("Set Port%02d Ingress CIR %d CBS %d Egress CIR %d CBS %d Fail.\n", i, rl.ingress_cir, rl.ingress_cbs, rl.egress_cir, rl.egress_cbs);
+                }
+            }
+        }
+    }
+    else if(1 == argc)
+    {
+        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)
+        {
+            if(portlist[0] & (1 << i))
+            {
+                ret = air_qos_getRateLimit(0, i, &rl);
+                if(ret == AIR_E_OK)
+                {
+                    AIR_PRINT("Get Port%02d Ingress CIR %d CBS %d Egress CIR %d CBS %d\n", i, rl.ingress_cir, rl.ingress_cbs, rl.egress_cir, rl.egress_cbs);
+                }
+                else
+                {
+                    AIR_PRINT("Get Port%02d Rate Info Fail.\n", i);
+                }
+            }
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doQosRateLimitEnable(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    AIR_PORT_BITMAP_T portlist = {0};
+    C8_T sten[2][10] = {"Disable", "Enable"};
+    C8_T stdir[2][10] = {"Egress", "Ingress"};
+    UI32_T dir = 0, en = 0;
+    AIR_QOS_RATE_DIR_T tmp_dir = AIR_QOS_RATE_DIR_LAST;
+    BOOL_T state = FALSE;
+    UI8_T i = 0;
+
+    ret = _portListStr2Ary(argv[0], portlist, 1);
+    if(ret != AIR_E_OK)
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return ret;
+    }
+    if(3 == argc)
+    {
+        dir = _strtoul(argv[1], NULL, 0);
+        en = _strtoul(argv[2], NULL, 0);
+        if(dir == 0)
+            tmp_dir = AIR_QOS_RATE_DIR_EGRESS;
+        else if(dir == 1)
+            tmp_dir = AIR_QOS_RATE_DIR_INGRESS;
+        else
+        {
+            AIR_PRINT("Unrecognized command.\n");
+            return AIR_E_BAD_PARAMETER;
+        }
+        if(en)
+            state= TRUE;
+        else
+            state = FALSE;
+
+        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)
+        {
+            if(portlist[0] & (1 << i))
+            {
+                ret = air_qos_setRateLimitEnable(0, i, tmp_dir, state);
+                if(AIR_E_OK == ret)
+                {
+                    AIR_PRINT("Set Port%02d %s rate %s Success.\n", i, stdir[dir], sten[en]);
+                }
+                else
+                {
+                    AIR_PRINT("Set Port%02d %s rate %s Fail.\n", i, stdir[dir], sten[en]);
+                }
+            }
+        }
+    }
+    else if(1 == argc)
+    {
+        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)
+        {
+            if(portlist[0] & (1 << i))
+            {
+                tmp_dir = AIR_QOS_RATE_DIR_EGRESS;
+                dir = 0;
+                ret = air_qos_getRateLimitEnable(0, i, tmp_dir, &state);
+                if(AIR_E_OK == ret)
+                {
+                    AIR_PRINT("Get Port%02d %s rate %s Success.\n", i, stdir[dir], sten[state]);
+                }
+                else
+                {
+                    AIR_PRINT("Get Port%02d %s rate state Fail.\n", i, stdir[dir]);
+                }
+                tmp_dir = AIR_QOS_RATE_DIR_INGRESS;
+                dir = 1;
+                ret = air_qos_getRateLimitEnable(0, i, tmp_dir, &state);
+                if(AIR_E_OK == ret)
+                {
+                    AIR_PRINT("Get Port%02d %s rate %s Success.\n", i, stdir[dir], sten[state]);
+                }
+                else
+                {
+                    AIR_PRINT("Get Port%02d %s rate state Fail.\n", i, stdir[dir]);
+                }
+            }
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+
+}
+
+static AIR_ERROR_NO_T
+doQosDscp2Pri(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T dscp = 0, priority = 0;
+
+    dscp = _strtoul(argv[0], NULL, 0);
+    if(2 == argc)
+    {
+        priority = _strtoul(argv[1], NULL, 0);
+        ret = air_qos_setDscp2Pri(0, dscp, priority);
+        if(AIR_E_OK == ret)
+        {
+            AIR_PRINT("Set DSCP %d to priority %d Success.\n", dscp, priority);
+        }
+        else
+        {
+            AIR_PRINT("Set DSCP %d to priority %d Fail.\n", dscp, priority);
+        }
+    }
+    else if(1 == argc)
+    {
+        ret = air_qos_getDscp2Pri(0, dscp, &priority);
+        if(AIR_E_OK == ret)
+        {
+            AIR_PRINT("Get DSCP %d to priority %d\n", dscp, priority);
+        }
+        else
+        {
+            AIR_PRINT("Get DSCP %d to priority Fail.\n", dscp);
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doQosPri2Queue(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T priority = 0, queue = 0;
+
+    priority = _strtoul(argv[1], NULL, 0);
+
+    if(2 == argc)
+    {
+        priority = _strtoul(argv[0], NULL, 0);
+        queue = _strtoul(argv[1], NULL, 0);
+        ret = air_qos_setPri2Queue(0, priority, queue);
+        if(AIR_E_OK == ret)
+        {
+            AIR_PRINT("Set priority %d to queue %d Success.\n", priority, queue);
+        }
+        else
+        {
+            AIR_PRINT("Set priority %d to queue %d Fail.\n", priority, queue);
+        }
+    }
+    else
+    {
+        for(; priority < AIR_QOS_QUEUE_MAX_NUM; priority++)
+        {
+            ret = air_qos_getPri2Queue(0, priority, &queue);
+            if(AIR_E_OK == ret)
+            {
+                AIR_PRINT("Get priority %d to queue %d\n", priority, queue);
+            }
+            else
+            {
+                AIR_PRINT("Get priority %d to queue Fail.\n", priority);
+            }
+        }
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doQosTrustMode(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T mode = 0;
+    C8_T bs[4][13] = {"port", "1p_port", "dscp_port", "dscp_1p_port"};
+    AIR_QOS_TRUST_MODE_T mode_t = AIR_QOS_TRUST_MODE_LAST;
+    AIR_PORT_BITMAP_T portlist = {0};
+    UI8_T i = 0;
+
+    ret = _portListStr2Ary(argv[0], portlist, 1);
+    if(ret != AIR_E_OK)
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return ret;
+    }
+    if(2 == argc)
+    {
+        mode = _strtoul(argv[1], NULL, 0);
+        if(mode == 0)
+            mode_t = AIR_QOS_TRUST_MODE_PORT;
+        else if(mode == 1)
+            mode_t = AIR_QOS_TRUST_MODE_1P_PORT;
+        else if(mode == 2)
+            mode_t = AIR_QOS_TRUST_MODE_DSCP_PORT;
+        else if(mode == 3)
+            mode_t = AIR_QOS_TRUST_MODE_DSCP_1P_PORT;
+        else
+        {
+            AIR_PRINT("Unrecognized command.\n");
+            return AIR_E_BAD_PARAMETER;
+        }
+        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)
+        {
+            if(portlist[0] & (1 << i))
+            {
+                ret = air_qos_setTrustMode(0, i, mode_t);
+                if(AIR_E_OK == ret)
+                {
+                    AIR_PRINT("port %d Set Trust mode %s Success.\n", i, bs[mode]);
+                }
+                else
+                {
+                    AIR_PRINT("port %d Set Trust mode %s Fail.\n", i, bs[mode]);
+                }
+            }
+        }
+    }
+    else if(1 == argc)
+    {
+        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)
+        {
+            if(portlist[0] & (1 << i))
+            {
+                mode_t = AIR_QOS_TRUST_MODE_LAST;
+                ret = air_qos_getTrustMode(0, i, &mode_t);
+                if(AIR_E_OK == ret)
+                {
+                    if(mode_t == AIR_QOS_TRUST_MODE_PORT)
+                        mode = 0;
+                    else if(mode_t == AIR_QOS_TRUST_MODE_1P_PORT)
+                        mode = 1;
+                    else if(mode_t == AIR_QOS_TRUST_MODE_DSCP_PORT)
+                        mode = 2;
+                    else if(mode_t == AIR_QOS_TRUST_MODE_DSCP_1P_PORT)
+                        mode = 3;
+                    else
+                    {
+                        AIR_PRINT("port %d Get Trust mode Fail.\n", i);
+                        return AIR_E_OTHERS;
+                    }
+                    AIR_PRINT("port %d Get Trust mode %s\n", i, bs[mode]);
+                }
+                else
+                {
+                    AIR_PRINT("port %d Get Trust mode Fail.\n", i);
+                }
+            }
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doQosScheduleAlgo(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    AIR_PORT_BITMAP_T portlist = {0};
+    AIR_QOS_SCH_MODE_T sch_mode = AIR_QOS_SCH_MODE_LAST;
+    UI32_T scheduler = 0;
+    UI8_T queue = 0;
+    C8_T sche[3][5] = {"SP", "WRR", "WFQ"};
+    UI32_T weight = AIR_QOS_SHAPER_NOSETTING;
+    UI8_T i = 0;
+
+    ret = _portListStr2Ary(argv[0], portlist, 1);
+    if(ret != AIR_E_OK)
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return ret;
+    }
+    AIR_PRINT("port list is %d\n", portlist[0]);
+    if(4 == argc)
+    {
+        queue = _strtoul(argv[1], NULL, 0);
+        AIR_PRINT("queue is %d\n", queue);
+        scheduler = _strtoul(argv[2], NULL, 0);
+        AIR_PRINT("scheduler is %d\n", scheduler);
+        weight = _strtoul(argv[3], NULL, 0);
+        AIR_PRINT("weight is %d\n", weight);
+        if(scheduler == 0)
+        {
+            sch_mode = AIR_QOS_SCH_MODE_SP;
+            weight = AIR_QOS_SHAPER_NOSETTING;
+            if(weight != AIR_QOS_SHAPER_NOSETTING)
+                AIR_PRINT("[Warning] SP schedule mode no need weight\n");
+        }
+        else if(scheduler == 1)
+        {
+            sch_mode = AIR_QOS_SCH_MODE_WRR;
+            if(weight == AIR_QOS_SHAPER_NOSETTING)
+            {
+                AIR_PRINT("[Warning] No weight value input , plz check\n");
+                return AIR_E_BAD_PARAMETER;
+            }
+            AIR_PRINT("sch_mode is 1\n");
+        }
+        else if(scheduler == 2)
+        {
+            sch_mode = AIR_QOS_SCH_MODE_WFQ;
+            if(weight == AIR_QOS_SHAPER_NOSETTING)
+            {
+                AIR_PRINT("[Warning] No weight value input , plz check\n");
+                return AIR_E_BAD_PARAMETER;
+            }
+        }
+        else
+        {
+            AIR_PRINT("Unknown schedule mode, plz check again\n");
+            return AIR_E_BAD_PARAMETER;
+        }
+        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)
+        {
+            if(portlist[0] & (1 << i))
+            {
+                AIR_PRINT("port %d\n", i);
+                ret = air_qos_setScheduleAlgo(0, i, queue, sch_mode, weight);
+                if(AIR_E_OK == ret)
+                {
+                    AIR_PRINT("Set Port%02d Scheduler %s Success.\n", i, sche[scheduler]);
+                }
+                else
+                {
+                    AIR_PRINT("Set Port%02d Scheduler %s Fail.\n", i, sche[scheduler]);
+                }
+            }
+        }
+    }
+    else if(2 == argc)
+    {
+        queue = _strtoul(argv[1], NULL, 0);
+        for(i = 0; i < AIR_MAX_NUM_OF_PORTS; i++)
+        {
+            if(portlist[0] & (1 << i))
+            {
+                ret = air_qos_getScheduleAlgo(0, i, queue, &sch_mode, &weight);
+                if(AIR_E_OK == ret)
+                {
+                    if(sch_mode == AIR_QOS_SCH_MODE_SP)
+                        AIR_PRINT("Get Port%02d queue %d Scheduler %s\n", i, queue, sche[sch_mode]);
+                    else if((sch_mode == AIR_QOS_SCH_MODE_WRR) || (sch_mode == AIR_QOS_SCH_MODE_WFQ))
+                        AIR_PRINT("Get Port%02d queue %d Scheduler %s weight %d\n", i, queue, sche[sch_mode], weight);
+                    else
+                        AIR_PRINT("Get Port%02d queue %d Scheduler unknown\n", i, queue);
+                }
+                else
+                {
+                    AIR_PRINT("Get Port%02d queue %d Scheduler Fail.\n", i, queue);
+                }
+            }
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doQosGet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(qosGetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doQosSet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(qosSetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doQos(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(qosCmds, argc, argv);
+}
+static AIR_ERROR_NO_T
+doDiagTxComply(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T phy = 0;
+    UI32_T mode = 0;
+
+    phy = _strtoul(argv[0], NULL, 0);
+    if(2 == argc)
+    {
+        /* diag set txComply <phy(0~5)> <mode(0~8)> */
+        mode = _strtoul(argv[1], NULL, 0);
+        ret = air_diag_setTxComplyMode(0, phy, mode);
+        AIR_PRINT("Set diagnostic function: PHY %u Tx Compliance mode = %u ", phy, mode);
+    }
+    else if(1 == argc)
+    {
+        /* diag get txComply <phy(0~5)> */
+        ret = air_diag_getTxComplyMode(0, phy, &mode);
+        AIR_PRINT("Get diagnostic function: PHY %u Tx Compliance mode ", phy);
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    if(AIR_E_OK == ret)
+    {
+        AIR_PRINT("Done.\n\tMode=");
+        switch(mode)
+        {
+            case AIR_DIAG_TXCOMPLY_MODE_10M_NLP:
+                AIR_PRINT("%s\n", "10M_NLP");
+                break;
+            case AIR_DIAG_TXCOMPLY_MODE_10M_RANDOM:
+                AIR_PRINT("%s\n", "10M_Random");
+                break;
+            case AIR_DIAG_TXCOMPLY_MODE_10M_SINE:
+                AIR_PRINT("%s\n", "10M_Sine");
+                break;
+            case AIR_DIAG_TXCOMPLY_MODE_100M_PAIR_A:
+                AIR_PRINT("%s\n", "100M_Pair_a");
+                break;
+            case AIR_DIAG_TXCOMPLY_MODE_100M_PAIR_B:
+                AIR_PRINT("%s\n", "100M_Pair_b");
+                break;
+            case AIR_DIAG_TXCOMPLY_MODE_1000M_TM1:
+                AIR_PRINT("%s\n", "1000M_TM1");
+                break;
+            case AIR_DIAG_TXCOMPLY_MODE_1000M_TM2:
+                AIR_PRINT("%s\n", "1000M_TM2");
+                break;
+            case AIR_DIAG_TXCOMPLY_MODE_1000M_TM3:
+                AIR_PRINT("%s\n", "1000M_TM3");
+                break;
+            case AIR_DIAG_TXCOMPLY_MODE_1000M_TM4:
+                AIR_PRINT("%s\n", "1000M_TM4");
+                break;
+            default:
+                break;
+        }
+    }
+    else
+    if(AIR_E_OTHERS == ret)
+    {
+        AIR_PRINT("isn't setting.\n");
+    }
+    else
+    {
+        AIR_PRINT("Fail.\n");
+    }
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doDiagSet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(diagSetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doDiagGet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(diagGetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doDiag(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(diagCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doLedMode(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T mode = 0;
+
+    if(1 == argc)
+    {
+        /* led set mode <mode(0:disable, 1~3:2 LED, 4:User-Define)> */
+        mode = _strtoul(argv[0], NULL, 0);
+        ret = air_led_setMode(0, 0, mode);
+        AIR_PRINT("Set LED mode ");
+    }
+    else if(0 == argc)
+    {
+        /* led get mode */
+        ret = air_led_getMode(0, 0, &mode);
+        AIR_PRINT("Get LED mode ");
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    if(AIR_E_OK == ret)
+    {
+        switch(mode)
+        {
+            case AIR_LED_MODE_DISABLE:
+                AIR_PRINT(": Disabled.\n");
+                break;
+            case AIR_LED_MODE_2LED_MODE0:
+                AIR_PRINT(": LED 0:Link / LED 1:Activity.\n");
+                break;
+            case AIR_LED_MODE_2LED_MODE1:
+                AIR_PRINT(": LED 0:1000M Activity / LED 1:100M Activity.\n");
+                break;
+            case AIR_LED_MODE_2LED_MODE2:
+                AIR_PRINT(": LED 0:1000M Activity / LED 1:10&100M Activity.\n");
+                break;
+            case AIR_LED_MODE_USER_DEFINE:
+                AIR_PRINT(": User-Defined.\n");
+                break;
+            default:
+                AIR_PRINT(": Fail.\n");
+                break;
+        }
+    }
+    else
+    if(AIR_E_OTHERS == ret)
+    {
+        AIR_PRINT(": Unrecognized.\n");
+    }
+    else
+    {
+        AIR_PRINT("Fail.\n");
+    }
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doLedState(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI8_T entity = 0;
+    BOOL_T state = FALSE;
+
+    entity = _strtoul(argv[0], NULL, 0);
+    if(2 == argc)
+    {
+        /* led set state <led(0..1)> <state(1:En 0:Dis)> */
+        state = _strtoul(argv[1], NULL, 0);
+        ret = air_led_setState(0, 0, entity, state);
+        AIR_PRINT("Set LED %u state ", entity);
+    }
+    else if(1 == argc)
+    {
+        /* led get state <led(0..1)> */
+        ret = air_led_getState(0, 0, entity, &state);
+        AIR_PRINT("Get LED %u state ", entity );
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    if(AIR_E_OK == ret)
+    {
+        AIR_PRINT(": %s.\n", (state)?"Enable":"Disabled");
+    }
+    else
+    if(AIR_E_OTHERS == ret)
+    {
+        AIR_PRINT(": Unrecognized.\n");
+    }
+    else
+    {
+        AIR_PRINT("Fail.\n");
+    }
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doLedUsrDef(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T i = 0;
+    UI8_T entity = 0;
+    BOOL_T polarity = LOW;
+    UI32_T on_evt_map = 0;
+    UI32_T blk_evt_map = 0;
+    AIR_LED_ON_EVT_T on_evt;
+    AIR_LED_BLK_EVT_T blk_evt;
+
+    entity = _strtoul(argv[0], NULL, 0);
+    if(4 == argc)
+    {
+        /* led set usr <led(0..1)> <polarity(0:low, 1:high)> <on_evt(7'bin)> <blink_evt(10'bin)> */
+        polarity = _strtoul(argv[1], NULL, 0);
+        on_evt_map = _strtoul(argv[2], NULL, 2);
+        blk_evt_map = _strtoul(argv[3], NULL, 2);
+
+        memset(&on_evt, 0, sizeof(AIR_LED_ON_EVT_T));
+        if(on_evt_map & BIT(0))
+        {
+            on_evt.link_1000m = TRUE;
+        }
+        if(on_evt_map & BIT(1))
+        {
+            on_evt.link_100m = TRUE;
+        }
+        if(on_evt_map & BIT(2))
+        {
+            on_evt.link_10m = TRUE;
+        }
+        if(on_evt_map & BIT(3))
+        {
+            on_evt.link_dn = TRUE;
+        }
+        if(on_evt_map & BIT(4))
+        {
+            on_evt.fdx = TRUE;
+        }
+        if(on_evt_map & BIT(5))
+        {
+            on_evt.hdx = TRUE;
+        }
+        if(on_evt_map & BIT(6))
+        {
+            on_evt.force = TRUE;
+        }
+
+        memset(&blk_evt, 0, sizeof(AIR_LED_BLK_EVT_T));
+        if(blk_evt_map & BIT(0))
+        {
+            blk_evt.tx_act_1000m = TRUE;
+        }
+        if(blk_evt_map & BIT(1))
+        {
+            blk_evt.rx_act_1000m = TRUE;
+        }
+        if(blk_evt_map & BIT(2))
+        {
+            blk_evt.tx_act_100m = TRUE;
+        }
+        if(blk_evt_map & BIT(3))
+        {
+            blk_evt.rx_act_100m = TRUE;
+        }
+        if(blk_evt_map & BIT(4))
+        {
+            blk_evt.tx_act_10m = TRUE;
+        }
+        if(blk_evt_map & BIT(5))
+        {
+            blk_evt.rx_act_10m = TRUE;
+        }
+        if(blk_evt_map & BIT(6))
+        {
+            blk_evt.cls = TRUE;
+        }
+        if(blk_evt_map & BIT(7))
+        {
+            blk_evt.rx_crc = TRUE;
+        }
+        if(blk_evt_map & BIT(8))
+        {
+            blk_evt.rx_idle = TRUE;
+        }
+        if(blk_evt_map & BIT(9))
+        {
+            blk_evt.force = TRUE;
+        }
+        ret = air_led_setUsrDef(0, 0, entity, polarity, on_evt, blk_evt);
+        AIR_PRINT("Set LED %u User-define ", entity);
+    }
+    else if(1 == argc)
+    {
+        /* led get usr <led(0..1)> */
+        ret = air_led_getUsrDef(0, 0, entity, &polarity, &on_evt, &blk_evt);
+        AIR_PRINT("Get LED %u User-define ", entity );
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    if(AIR_E_OK == ret)
+    {
+        AIR_PRINT("Done.\n");
+        AIR_PRINT("Polarity:%u.\n", polarity);
+        AIR_PRINT("On Event:\n");
+        i = 6;
+        AIR_PRINT("\t(%u)Force on :%s\n", i--, (on_evt.force)?"On":"Off");
+        AIR_PRINT("\t(%u)Half Duplex :%s\n", i--, (on_evt.hdx)?"On":"Off");
+        AIR_PRINT("\t(%u)Full Duplex :%s\n", i--, (on_evt.fdx)?"On":"Off");
+        AIR_PRINT("\t(%u)Link Down :%s\n", i--, (on_evt.link_dn)?"On":"Off");
+        AIR_PRINT("\t(%u)Link 10M :%s\n", i--, (on_evt.link_10m)?"On":"Off");
+        AIR_PRINT("\t(%u)Link 100M :%s\n", i--, (on_evt.link_100m)?"On":"Off");
+        AIR_PRINT("\t(%u)Link 1000M :%s\n", i, (on_evt.link_1000m)?"On":"Off");
+
+        AIR_PRINT("Blinking Event:\n");
+        i = 9;
+        AIR_PRINT("\t(%u)Force blinks :%s\n", i--, (blk_evt.force)?"On":"Off");
+        AIR_PRINT("\t(%u)Rx Idle Error :%s\n", i--, (blk_evt.rx_idle)?"On":"Off");
+        AIR_PRINT("\t(%u)Rx CRC Error :%s\n", i--, (blk_evt.rx_crc)?"On":"Off");
+        AIR_PRINT("\t(%u)Collision :%s\n", i--, (blk_evt.cls)?"On":"Off");
+        AIR_PRINT("\t(%u)10Mbps RX Activity :%s\n", i--, (blk_evt.rx_act_10m)?"On":"Off");
+        AIR_PRINT("\t(%u)10Mbps TX Activity :%s\n", i--, (blk_evt.tx_act_10m)?"On":"Off");
+        AIR_PRINT("\t(%u)100Mbps RX Activity :%s\n", i--, (blk_evt.rx_act_100m)?"On":"Off");
+        AIR_PRINT("\t(%u)100Mbps TX Activity :%s\n", i--, (blk_evt.tx_act_100m)?"On":"Off");
+        AIR_PRINT("\t(%u)1000Mbps RX Activity :%s\n", i--, (blk_evt.rx_act_1000m)?"On":"Off");
+        AIR_PRINT("\t(%u)1000Mbps TX Activity :%s\n", i, (blk_evt.tx_act_1000m)?"On":"Off");
+    }
+    else
+    {
+        AIR_PRINT("Fail.\n");
+    }
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doLedBlkTime(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    AIR_LED_BLK_DUR_T time = 0;
+
+    if(1 == argc)
+    {
+        /* led set time <time(0~5)> */
+        time = _strtoul(argv[0], NULL, 0);
+        ret = air_led_setBlkTime(0, 0, time);
+        AIR_PRINT("Set Blinking Duration ");
+    }
+    else if(0 == argc)
+    {
+        /* led get time */
+        ret = air_led_getBlkTime(0, 0, &time);
+        AIR_PRINT("Get Blinking Duration ");
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    if(AIR_E_OK == ret)
+    {
+        AIR_PRINT("Done.\n");
+        AIR_PRINT("\tBlinking duration : %u (ms)\n", (32 << time) );
+    }
+    else
+    {
+        AIR_PRINT("Fail.\n");
+    }
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doLedSet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(ledSetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doLedGet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(ledGetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doLed(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(ledCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doShowVersion(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_PRINT("VERSION: %s\n", AIR_VER_SDK);
+
+    return AIR_E_OK;
+}
+
+static AIR_ERROR_NO_T
+doShow(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(showCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doStormRate(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0, type = 0;
+    UI32_T unit = 0, count = 0;
+    C8_T stype[3][5] = {"Bcst", "Mcst", "Ucst"};
+    UI32_T kb = 0;
+
+    port = _strtol(argv[0], NULL, 10);
+    type = _strtol(argv[1], NULL, 10);
+    if(4 == argc)
+    {
+        count = _strtol(argv[2], NULL, 10);
+        unit = _strtol(argv[3], NULL, 10);
+
+        if(0 == unit)
+            kb = 64;
+        else if(1 == unit)
+            kb = 256;
+        else if(2 == unit)
+            kb = 1024;
+        else if(3 == unit)
+            kb = 4096;
+        else
+            kb = 16384;
+        ret = air_sec_setStormRate(0, port, type, count, unit);
+        if(AIR_E_OK == ret)
+        {
+            AIR_PRINT("Set Port%02d %s storm rate (%d * %d) = %d Kbps\n", port, stype[type], count, kb, (count*kb));
+        }
+        else
+        {
+            AIR_PRINT("Set Port%02d %s storm rate Fail.\n", port, stype[type]);
+            AIR_PRINT("Note: Port(0..4) can only select unit(0..3), port(5..6) can only select unit(4)\n");
+        }
+    }
+    else if(2 == argc)
+    {
+        ret = air_sec_getStormRate(0, port, type, &count, &unit);
+        if(AIR_E_OK == ret)
+        {
+            if(0 == unit)
+                kb = 64;
+            else if(1 == unit)
+                kb = 256;
+            else if(2 == unit)
+                kb = 1024;
+            else if(3 == unit)
+                kb = 4096;
+            else
+                kb = 16384;
+            AIR_PRINT("Port%02d %s storm rate (%d * %d) = %d Kbps\n", port, stype[type], count, kb, (count*kb));
+        }
+        else
+        {
+            AIR_PRINT("Get Port%02d %s storm rate Fail\n", port, stype[type]);
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doFldMode(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0, type = 0;
+    BOOL_T fld_en = 0;
+    C8_T stype[4][5] = {"Bcst", "Mcst", "Ucst", "Qury"};
+    C8_T sen[2][10] = {"Disable", "Enable"};
+
+    port = _strtol(argv[0], NULL, 10);
+    type = _strtol(argv[1], NULL, 10);
+
+    if(2 == argc)
+    {
+        ret = air_sec_getFldMode(0, port, type, &fld_en);
+        if(AIR_E_OK == ret)
+        {
+            AIR_PRINT("Get Port%02d flooding %s frame %s\n", port, stype[type], sen[fld_en]);
+        }
+        else
+        {
+            AIR_PRINT("Get Port%02d flooding %s frame Fail\n", port, stype[type]);
+        }
+    }
+    else if(3 == argc)
+    {
+        fld_en = _strtol(argv[2], NULL, 10);
+        ret = air_sec_setFldMode(0, port, type, fld_en);
+        if(AIR_E_OK == ret)
+        {
+            AIR_PRINT("Set Port%02d flooding %s frame %s Success\n", port, stype[type], sen[fld_en]);
+        }
+        else
+        {
+            AIR_PRINT("Set Port%02d flooding %s frame %s Fail\n", port, stype[type], sen[fld_en]);
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doStormEnable(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T port = 0, type = 0;
+    BOOL_T en = 0;
+    C8_T sen[2][10] = {"Disable", "Enable"};
+    C8_T stype[3][5] = {"Bcst", "Mcst", "Ucst"};
+
+    port = _strtol(argv[0], NULL, 10);
+    type = _strtol(argv[1], NULL, 10);
+    if(3 == argc)
+    {
+        en = _strtol(argv[2], NULL, 10);
+        ret = air_sec_setStormEnable(0, port, type, en);
+        if(AIR_E_OK == ret)
+        {
+            AIR_PRINT("Set Port%02d %s storm %s Success.\n", port, stype[type], sen[en]);
+        }
+        else
+        {
+            AIR_PRINT("Set Port%02d %s storm %s Fail.\n", port, stype[type], sen[en]);
+        }
+    }
+    else if(2 == argc)
+    {
+        ret = air_sec_getStormEnable(0, port, type, &en);
+        if(AIR_E_OK == ret)
+        {
+            AIR_PRINT("Port%02d %s storm %s\n", port, stype[type], sen[en]);
+        }
+        else
+        {
+            AIR_PRINT("Get Port%02d %s storm Fail\n", port, stype[type]);
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doSaLearning(UI32_T argc, C8_T *argv[])
+{
+    UI32_T port = 0;
+    AIR_SEC_PORTSEC_PORT_CONFIG_T port_config;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port = _strtoul(argv[0], NULL, 0);
+    if(2 == argc)
+    {
+        memset(&port_config, 0, sizeof(AIR_SEC_PORTSEC_PORT_CONFIG_T));
+        rc = air_sec_getPortSecPortCfg(0, port, &port_config);
+        port_config.sa_lrn_en = _strtoul(argv[1], NULL, 0);
+        rc = air_sec_setPortSecPortCfg(0, port, port_config);
+        if(AIR_E_OK == rc)
+        {
+            AIR_PRINT("Set Port%02d sa learn %s Success.\n", port, port_config.sa_lrn_en?"Enable":"Disable");
+        }
+        else
+        {
+            AIR_PRINT("Set Port%02d sa learn %s Fail.\n", port, port_config.sa_lrn_en?"Enable":"Disable");
+        }
+    }
+    else if(1 == argc)
+    {
+        rc = air_sec_getPortSecPortCfg(0, port, &port_config);
+        if(AIR_E_OK == rc)
+        {
+            AIR_PRINT("Port%02d sa learn: %s\n", port, port_config.sa_lrn_en?"Enable":"Disable");
+        }
+        else
+        {
+            AIR_PRINT("Get Port%02d sa learn Fail\n", port);
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        rc = AIR_E_BAD_PARAMETER;
+    }
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doSaLimit(UI32_T argc, C8_T *argv[])
+{
+    UI32_T port = 0;
+    AIR_SEC_PORTSEC_PORT_CONFIG_T port_config;
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+
+    port = _strtoul(argv[0], NULL, 0);
+    if(3 == argc)
+    {
+        memset(&port_config, 0, sizeof(AIR_SEC_PORTSEC_PORT_CONFIG_T));
+        rc = air_sec_getPortSecPortCfg(0, port, &port_config);
+        port_config.sa_lmt_en = _strtoul(argv[1], NULL, 0);
+        port_config.sa_lmt_cnt = _strtoul(argv[2], NULL, 0);
+        rc = air_sec_setPortSecPortCfg(0, port, port_config);
+        if(AIR_E_OK == rc)
+        {
+            AIR_PRINT("Set Port%02d sa limit %s Success.\n", port, port_config.sa_lmt_en?"Enable":"Disable");
+        }
+        else
+        {
+            AIR_PRINT("Set Port%02d sa limit %s Fail.\n", port, port_config.sa_lmt_en?"Enable":"Disable");
+        }
+    }
+    else if(1 == argc)
+    {
+        rc = air_sec_getPortSecPortCfg(0, port, &port_config);
+        if(AIR_E_OK == rc)
+        {
+            AIR_PRINT("Port%02d ", port);
+            AIR_PRINT("sa limit: %s\n", port_config.sa_lmt_en?"Enable":"Disable");
+            if(TRUE == (port_config.sa_lmt_en && (AIR_MAX_NUM_OF_MAC ==  port_config.sa_lmt_cnt)))
+            {
+                AIR_PRINT("Sa learning without limitation\n");
+            }
+            else if(TRUE == (port_config.sa_lmt_en && (AIR_MAX_NUM_OF_MAC >  port_config.sa_lmt_cnt)))
+            {
+                AIR_PRINT("Rx sa allowable learning number: %d\n", port_config.sa_lmt_cnt);
+            }
+        }
+        else
+        {
+            AIR_PRINT("Get Port%02d sa limit Fail\n", port);
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        rc = AIR_E_BAD_PARAMETER;
+    }
+
+    return rc;
+}
+
+static AIR_ERROR_NO_T
+doSecGet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(secGetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doSecSet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(secSetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doSec(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(secCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doSwitchCpuPortEn(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    BOOL_T cpu_en = FALSE;
+
+    if(0 == argc)
+    {
+        /* switch get sysPhyEn */
+        ret = air_switch_getCpuPortEn(0, &cpu_en);
+        AIR_PRINT("Get Cpu Port State ");
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT(": %s\n", cpu_en?"Enable":"Disable");
+        }
+        else
+        {
+            AIR_PRINT("Fail!\n");
+        }
+    }
+    else if(1 == argc)
+    {
+        /* switch set sysPhyEn <phy_en> */
+        cpu_en = _strtol(argv[0], NULL, 0);
+        ret = air_switch_setCpuPortEn(0, cpu_en);
+        AIR_PRINT("Set CPU port State ");
+        if(ret == AIR_E_OK)
+        {
+            AIR_PRINT(": %s\n", cpu_en?"Enable":"Disable");
+        }
+        else
+        {
+            AIR_PRINT("Fail!\n");
+        }
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doSwitchCpuPort(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    BOOL_T cpu_en = FALSE;
+    UI32_T port = 0;
+    C8_T str_temp[AIR_MAX_NUM_OF_PORTS+1];
+
+    if(1 == argc)
+    {
+        /* switch set cpuPort <portnumber> */
+        port = _strtol(argv[0], NULL, 10);
+        ret = air_switch_setCpuPort(0, port);
+        AIR_PRINT("Set CPU Port ");
+    }
+    else if(0 == argc)
+    {
+        /* switch get cpuPort */
+        ret = air_switch_getCpuPort(0, &port);
+        AIR_PRINT("Get CPU Port ");
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    if(AIR_E_OK == ret)
+    {
+        AIR_PRINT(": %d\n", port);
+    }
+    else
+    {
+        AIR_PRINT("Fail!\n");
+    }
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doSwitchPhyLCIntrEn(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T phy = 0;
+    BOOL_T enable = FALSE;
+
+    if(2 == argc)
+    {
+        /* switch set phyLCIntrEn <phy(0..6)> <(1:En,0:Dis)> */
+        phy    = _strtol(argv[0], NULL, 10);
+        enable = _strtol(argv[1], NULL, 10);
+        ret    = air_switch_setSysIntrEn(0, (phy + AIR_SYS_INTR_TYPE_PHY0_LC), enable);
+    }
+    else if(1 == argc)
+    {
+        /* switch get phyLCIntrEn <phy(0..6)> */
+        phy = _strtol(argv[0], NULL, 10);
+        ret = air_switch_getSysIntrEn(0, (phy + AIR_SYS_INTR_TYPE_PHY0_LC), &enable);
+        AIR_PRINT("PHY(%d) LinkChange interrupt : %s\n", phy, (TRUE == enable) ? "enable" : "disable");
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doSwitchPhyLCIntrSts(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T phy = 0;
+    BOOL_T enable = FALSE;
+
+    if(2 == argc)
+    {
+        /* switch set phyLCIntrSts <phy(0..6)> <(1:En)> */
+        phy    = _strtol(argv[0], NULL, 10);
+        enable = _strtol(argv[1], NULL, 10);
+        ret    = air_switch_setSysIntrStatus(0, (phy + AIR_SYS_INTR_TYPE_PHY0_LC), enable);
+    }
+    else if(1 == argc)
+    {
+        /* switch get phyLCIntrSts <phy(0..6)> */
+        phy = _strtol(argv[0], NULL, 10);
+        ret = air_switch_getSysIntrStatus(0, (phy + AIR_SYS_INTR_TYPE_PHY0_LC), &enable);
+        AIR_PRINT("PHY(%d) LinkChange interrupt state : %s\n", phy, (TRUE == enable) ? "set" : "unset");
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        ret = AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+
+static AIR_ERROR_NO_T
+doSwitchSet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(switchSetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doSwitchGet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(switchGetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doSwitch(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(switchCmds, argc, argv);
+}
+
+static void _air_acl_printRuleMap(UI32_T *rule_map, UI32_T ary_num)
+{
+    UI32_T i;
+    BOOL_T first;
+
+    first = TRUE;
+    for(i=0; i<ary_num*32; i++)
+    {
+        if(rule_map[i/32] & BIT(i%32))
+        {
+            if(TRUE == first)
+            {
+                AIR_PRINT("%u", i);
+                first = FALSE;
+            }
+            else
+            {
+                AIR_PRINT(",%u", i);
+            }
+        }
+    }
+    AIR_PRINT("\n");
+}
+
+static AIR_ERROR_NO_T
+doAclEn(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T argi=0;
+    UI32_T port = 0;
+    BOOL_T en = FALSE;
+
+    if(1 == argc)
+    {
+        /* acl set en <en(1:En,0:Dis)> */
+        en = _strtoul(argv[argi++], NULL, 2);
+        ret = air_acl_setGlobalState(0, en);
+        AIR_PRINT("Set Global ACL function ");
+    }
+    else if(0 == argc)
+    {
+        /* acl get en */
+        ret = air_acl_getGlobalState(0, &en);
+        AIR_PRINT("Get Global ACL function ");
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    if(ret == AIR_E_OK)
+    {
+        AIR_PRINT(": %s\n", (TRUE == en)?"Enable":"Disable");
+    }
+    else
+    {
+        AIR_PRINT("Fail!\n");
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doAclRule(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T rule_idx = 0;
+    BOOL_T state = FALSE, reverse = FALSE, end = FALSE;
+    UI32_T argi = 0, ipv6 = 0, i = 0;
+    AIR_ACL_RULE_T rule;
+    UI8_T tmp_ip[16] = {0};
+    C8_T str_temp[AIR_MAX_NUM_OF_PORTS+1];
+    C8_T str[40];
+
+    memset(&rule, 0, sizeof(AIR_ACL_RULE_T));
+    if(argc >= 6)
+    {
+        /* acl set rule <idx(0..255)> <state(0:Dis,1:En)> <reverse(0:Dis,1:En)> <end(0:Dis,1:En)> <portmap(7'bin)>
+        <ipv6(0:Dis,1:En,2:Not care)>
+        [ dmac <dmac(12'hex)> <dmac_mask(12'hex)> ]
+        [ smac <smac(12'hex)> <smac_mask(12'hex)> ]
+        [ stag <stag(4'hex)> <stag_mask(4'hex)> ]
+        [ ctag <ctag(4'hex)> <ctag_mask(4'hex)> ]
+        [ etype <etype(4'hex)> <etype_mask(4'hex)> ]
+        [ dip <dip(IPADDR)> <dip_mask(IPADDR)> ]
+        [ sip <sip(IPADDR)> <sip_mask(IPADDR)> ]
+        [ dscp <dscp(2'hex)> <dscp_mask(2'hex)> ]
+        [ protocol <protocol(2'hex)> <protocol_mask(2'hex)> ]
+        [ dport <dport(4'hex)> <dport_mask(4'hex)> ]
+        [ sport <sport(4'hex)> <sport_mask(4'hex)> ]
+        [ flow_label <flow_label(4'hex)> <flow_label_mask(4'hex)> ]
+        [ udf <udf(4'hex)> <udf_mask(4'hex)> ] */
+
+        rule_idx = _strtoul(argv[argi++], NULL, 0);
+
+        rule.ctrl.rule_en = _strtoul(argv[argi++], NULL, 0);
+        rule.ctrl.reverse = _strtoul(argv[argi++], NULL, 0);
+        rule.ctrl.end = _strtoul(argv[argi++], NULL, 0);
+
+        rule.key.portmap = _strtoul(argv[argi++], NULL, 2);
+        rule.mask.portmap = (~rule.key.portmap) & AIR_ALL_PORT_BITMAP;
+
+        ipv6 = _strtoul(argv[argi++], NULL, 0);
+        if(0 == ipv6)
+        {
+            rule.key.isipv6 = FALSE;
+            rule.mask.isipv6 = TRUE;
+        }
+        else if(1 == ipv6)
+        {
+            rule.key.isipv6 = TRUE;
+            rule.mask.isipv6 = TRUE;
+        }
+        else
+        {
+            rule.mask.isipv6 = FALSE;
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "dmac")))
+        {
+            argi++;
+            _str2mac(argv[argi++], rule.key.dmac);
+            _str2mac(argv[argi++], rule.mask.dmac);
+            rule.key.fieldmap |= 1 << AIR_ACL_DMAC;
+        }
+        if((argi < argc) && (0 == _strcmp(argv[argi], "smac")))
+        {
+            argi++;
+            _str2mac(argv[argi++], rule.key.smac);
+            _str2mac(argv[argi++], rule.mask.smac);
+            rule.key.fieldmap |= 1 << AIR_ACL_SMAC;
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "stag")))
+        {
+            argi++;
+            rule.key.stag = _strtoul(argv[argi++], NULL, 16);
+            rule.mask.stag = _strtoul(argv[argi++], NULL, 16);
+            rule.key.fieldmap |= 1 << AIR_ACL_STAG;
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "ctag")))
+        {
+            argi++;
+            rule.key.ctag = _strtoul(argv[argi++], NULL, 16);
+            rule.mask.ctag = _strtoul(argv[argi++], NULL, 16);
+            rule.key.fieldmap |= 1 << AIR_ACL_CTAG;
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "etype")))
+        {
+            argi++;
+            rule.key.etype= _strtoul(argv[argi++], NULL, 16);
+            rule.mask.etype = _strtoul(argv[argi++], NULL, 16);
+            rule.key.fieldmap |= 1 << AIR_ACL_ETYPE;
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "dip")))
+        {
+            argi++;
+            if(0 == ipv6)
+            {
+                _str2ipv4(argv[argi++], rule.key.dip);
+                _str2ipv4(argv[argi++], rule.mask.dip);
+            }
+            else if(1 == ipv6)
+            {
+                _str2ipv6(argv[argi++], tmp_ip);
+                rule.key.dip[3] = (tmp_ip[0]<<24) | (tmp_ip[1]<<16) | (tmp_ip[2]<<8) | tmp_ip[3];
+                rule.key.dip[2] = (tmp_ip[4]<<24) | (tmp_ip[5]<<16) | (tmp_ip[6]<<8) | tmp_ip[7];
+                rule.key.dip[1] = (tmp_ip[8]<<24) | (tmp_ip[9]<<16) | (tmp_ip[10]<<8) | tmp_ip[11];
+                rule.key.dip[0] = (tmp_ip[12]<<24) | (tmp_ip[13]<<16) | (tmp_ip[14]<<8) | tmp_ip[15];
+                _str2ipv6(argv[argi++], tmp_ip);
+                rule.mask.dip[3] = (tmp_ip[0]<<24) | (tmp_ip[1]<<16) | (tmp_ip[2]<<8) | tmp_ip[3];
+                rule.mask.dip[2] = (tmp_ip[4]<<24) | (tmp_ip[5]<<16) | (tmp_ip[6]<<8) | tmp_ip[7];
+                rule.mask.dip[1] = (tmp_ip[8]<<24) | (tmp_ip[9]<<16) | (tmp_ip[10]<<8) | tmp_ip[11];
+                rule.mask.dip[0] = (tmp_ip[12]<<24) | (tmp_ip[13]<<16) | (tmp_ip[14]<<8) | tmp_ip[15];
+            }
+            rule.key.fieldmap |= 1 << AIR_ACL_DIP;
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "sip")))
+        {
+            argi++;
+            if(0 == ipv6)
+            {
+                _str2ipv4(argv[argi++], rule.key.sip);
+                _str2ipv4(argv[argi++], rule.mask.sip);
+            }
+            else if(1 == ipv6)
+            {
+                _str2ipv6(argv[argi++], tmp_ip);
+                rule.key.sip[3] = (tmp_ip[0]<<24) | (tmp_ip[1]<<16) | (tmp_ip[2]<<8) | tmp_ip[3];
+                rule.key.sip[2] = (tmp_ip[4]<<24) | (tmp_ip[5]<<16) | (tmp_ip[6]<<8) | tmp_ip[7];
+                rule.key.sip[1] = (tmp_ip[8]<<24) | (tmp_ip[9]<<16) | (tmp_ip[10]<<8) | tmp_ip[11];
+                rule.key.sip[0] = (tmp_ip[12]<<24) | (tmp_ip[13]<<16) | (tmp_ip[14]<<8) | tmp_ip[15];
+                _str2ipv6(argv[argi++], tmp_ip);
+                rule.mask.sip[3] = (tmp_ip[0]<<24) | (tmp_ip[1]<<16) | (tmp_ip[2]<<8) | tmp_ip[3];
+                rule.mask.sip[2] = (tmp_ip[4]<<24) | (tmp_ip[5]<<16) | (tmp_ip[6]<<8) | tmp_ip[7];
+                rule.mask.sip[1] = (tmp_ip[8]<<24) | (tmp_ip[9]<<16) | (tmp_ip[10]<<8) | tmp_ip[11];
+                rule.mask.sip[0] = (tmp_ip[12]<<24) | (tmp_ip[13]<<16) | (tmp_ip[14]<<8) | tmp_ip[15];
+            }
+            rule.key.fieldmap |= 1 << AIR_ACL_SIP;
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "dscp")))
+        {
+            argi++;
+            rule.key.dscp = _strtoul(argv[argi++], NULL, 16);
+            rule.mask.dscp = _strtoul(argv[argi++], NULL, 16);
+            rule.key.fieldmap |= 1 << AIR_ACL_DSCP;
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "protocol")))
+        {
+            argi++;
+            rule.key.protocol = _strtoul(argv[argi++], NULL, 16);
+            rule.mask.protocol = _strtoul(argv[argi++], NULL, 16);
+            rule.key.fieldmap |= 1 << AIR_ACL_PROTOCOL;
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "dport")))
+        {
+            argi++;
+            rule.key.dport = _strtoul(argv[argi++], NULL, 16);
+            rule.mask.dport = _strtoul(argv[argi++], NULL, 16);
+            rule.key.fieldmap |= 1 << AIR_ACL_DPORT;
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "sport")))
+        {
+            argi++;
+            rule.key.sport = _strtoul(argv[argi++], NULL, 16);
+            rule.mask.sport = _strtoul(argv[argi++], NULL, 16);
+            rule.key.fieldmap |= 1 << AIR_ACL_SPORT;
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "flow_label")))
+        {
+            argi++;
+            rule.key.flow_label= _strtoul(argv[argi++], NULL, 16);
+            rule.mask.flow_label= _strtoul(argv[argi++], NULL, 16);
+            rule.key.fieldmap |= 1 << AIR_ACL_FLOW_LABEL;
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "udf")))
+        {
+            argi++;
+            rule.key.udf = _strtoul(argv[argi++], NULL, 16);
+            rule.mask.udf = _strtoul(argv[argi++], NULL, 16);
+            rule.key.fieldmap |= 1 << AIR_ACL_UDF;
+        }
+        rule.mask.fieldmap = rule.key.fieldmap;
+        ret = air_acl_setRule(0, rule_idx, &rule);
+        if(ret < AIR_E_LAST)
+            AIR_PRINT("Set ACL Rule(%u): %s\n", rule_idx, air_error_getString(ret));
+    }
+    else if(1 == argc)
+    {
+        rule_idx = _strtoul(argv[0], NULL, 0);
+        ret = air_acl_getRule(0, rule_idx, &rule);
+        if(ret < AIR_E_LAST)
+            AIR_PRINT("Get ACL Rule(%u): %s\n", rule_idx, air_error_getString(ret));
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    if(AIR_E_OK == ret)
+    {
+        if(TRUE == rule.ctrl.rule_en)
+        {
+            AIR_PRINT("\t Rule end          : %s\n", (TRUE == rule.ctrl.end)?"Enable":"Disable");
+            AIR_PRINT("\t Rule reverse      : %s\n", (TRUE == rule.ctrl.reverse)?"Enable":"Disable");
+            _hex2bitstr((~rule.mask.portmap) & AIR_ALL_PORT_BITMAP, str_temp, AIR_MAX_NUM_OF_PORTS+1);
+            AIR_PRINT("\t Portmap[0:6]      : %s\n", str_temp);
+            for(i = AIR_ACL_DMAC; i < AIR_ACL_FIELD_TYPE_LAST; i++)
+            {
+                if((1 << i) & rule.mask.fieldmap)
+                {
+                    switch (i)
+                    {
+                        case AIR_ACL_DMAC:
+                            AIR_PRINT("\t dmac: ");
+                            AIR_PRINT("%02x-%02x-%02x-%02x-%02x-%02x",
+                            rule.key.dmac[0], rule.key.dmac[1], rule.key.dmac[2],
+                            rule.key.dmac[3], rule.key.dmac[4], rule.key.dmac[5]);
+                            AIR_PRINT(", dmac-mask: ");
+                            AIR_PRINT("%02x-%02x-%02x-%02x-%02x-%02x\n",
+                            rule.mask.dmac[0], rule.mask.dmac[1], rule.mask.dmac[2],
+                            rule.mask.dmac[3], rule.mask.dmac[4], rule.mask.dmac[5]);
+                            break;
+                        case AIR_ACL_SMAC:
+                            AIR_PRINT("\t smac: ");
+                            AIR_PRINT("%02x-%02x-%02x-%02x-%02x-%02x",
+                            rule.key.smac[0], rule.key.smac[1], rule.key.smac[2],
+                            rule.key.smac[3], rule.key.smac[4], rule.key.smac[5]);
+                            AIR_PRINT(", smac-mask: ");
+                            AIR_PRINT("%02x-%02x-%02x-%02x-%02x-%02x\n",
+                            rule.mask.smac[0], rule.mask.smac[1], rule.mask.smac[2],
+                            rule.mask.smac[3], rule.mask.smac[4], rule.mask.smac[5]);
+                            break;
+                        case AIR_ACL_ETYPE:
+                            AIR_PRINT("\t etype: 0x%x, etype-mask: 0x%x\n", rule.key.etype, rule.mask.etype);
+                            break;
+                        case AIR_ACL_STAG:
+                            AIR_PRINT("\t stag: 0x%x, stag-mask: 0x%x\n", rule.key.stag, rule.mask.stag);
+                            break;
+                        case AIR_ACL_CTAG:
+                            AIR_PRINT("\t ctag: 0x%x, ctag-mask: 0x%x\n", rule.key.ctag, rule.mask.ctag);
+                            break;
+                        case AIR_ACL_DPORT:
+                            AIR_PRINT("\t dport: 0x%x, dport-mask: 0x%x\n", rule.key.dport, rule.mask.dport);
+                            break;
+                        case AIR_ACL_SPORT:
+                            AIR_PRINT("\t sport: 0x%x, sport-mask: 0x%x\n", rule.key.sport, rule.mask.sport);
+                            break;
+                        case AIR_ACL_UDF:
+                            AIR_PRINT("\t udf: 0x%x, udf-mask: 0x%x\n", rule.key.udf, rule.mask.udf);
+                            break;
+                        case AIR_ACL_DIP:
+                            if (0 == rule.key.isipv6)
+                            {
+                                AIR_PRINT("\t dip: ");
+                                AIR_PRINT("%d.%d.%d.%d",
+                                ((rule.key.dip[0])&0xFF000000)>>24,((rule.key.dip[0])&0x00FF0000)>>16,
+                                ((rule.key.dip[0])&0x0000FF00)>>8, ((rule.key.dip[0])&0x000000FF));
+                                AIR_PRINT(", dip-mask: ");
+                                AIR_PRINT("%d.%d.%d.%d\n ",
+                                ((rule.mask.dip[0])&0xFF000000)>>24,((rule.mask.dip[0])&0x00FF0000)>>16,
+                                ((rule.mask.dip[0])&0x0000FF00)>>8, ((rule.mask.dip[0])&0x000000FF));
+                            }
+                            else
+                            {
+                                for(i=0; i<4; i++){
+                                    tmp_ip[i] = (rule.key.dip[3] >> (8*(3-i))) & 0xff;
+                                    AIR_PRINT("get tmp_ip[%d]=0x%x\n", i, tmp_ip[i]);
+                                }
+                                for(i=4; i<8; i++){
+                                    tmp_ip[i] = (rule.key.dip[2] >> (8*(7-i))) & 0xff;
+                                }
+                                for(i=8; i<12; i++){
+                                    tmp_ip[i] = (rule.key.dip[1] >> (8*(11-i))) & 0xff;
+                                }
+                                for(i=12; i<16; i++){
+                                    tmp_ip[i] = (rule.key.dip[0] >> (8*(15-i))) & 0xff;
+                                }
+
+                                AIR_PRINT("\t dip: ");
+                                AIR_PRINT("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+                                    tmp_ip[0], tmp_ip[1],tmp_ip[2], tmp_ip[3],tmp_ip[4], tmp_ip[5],tmp_ip[6], tmp_ip[7],
+                                    tmp_ip[8], tmp_ip[9],tmp_ip[10], tmp_ip[11],tmp_ip[12], tmp_ip[13],tmp_ip[14], tmp_ip[15]);
+                                for(i=0; i<4; i++){
+                                    tmp_ip[i] = (rule.mask.dip[3] >> (8*(3-i))) & 0xff;
+                                }
+                                for(i=4; i<8; i++){
+                                    tmp_ip[i] = (rule.mask.dip[2] >> (8*(7-i))) & 0xff;
+                                }
+                                for(i=8; i<12; i++){
+                                    tmp_ip[i] = (rule.mask.dip[1] >> (8*(11-i))) & 0xff;
+                                }
+                                for(i=12; i<16; i++){
+                                    tmp_ip[i] = (rule.mask.dip[0] >> (8*(15-i))) & 0xff;
+                                }
+                                AIR_PRINT(", dip-mask: ");
+                                AIR_PRINT("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n",
+                                    tmp_ip[0], tmp_ip[1],tmp_ip[2], tmp_ip[3],tmp_ip[4], tmp_ip[5],tmp_ip[6], tmp_ip[7],
+                                    tmp_ip[8], tmp_ip[9],tmp_ip[10], tmp_ip[11],tmp_ip[12], tmp_ip[13],tmp_ip[14], tmp_ip[15]);
+                            }
+                            break;
+                        case AIR_ACL_SIP:
+                            if (0 == rule.key.isipv6)
+                            {
+                                AIR_PRINT("\t sip: ");
+                                AIR_PRINT("%d.%d.%d.%d ",
+                                ((rule.key.sip[0])&0xFF000000)>>24,((rule.key.sip[0])&0x00FF0000)>>16,
+                                ((rule.key.sip[0])&0x0000FF00)>>8, ((rule.key.sip[0])&0x000000FF));
+                                AIR_PRINT(", sip-mask: ");
+                                AIR_PRINT("%d.%d.%d.%d\n ",
+                                ((rule.mask.sip[0])&0xFF000000)>>24,((rule.mask.sip[0])&0x00FF0000)>>16,
+                                ((rule.mask.sip[0])&0x0000FF00)>>8, ((rule.mask.sip[0])&0x000000FF));
+                            }
+                            else
+                            {
+                                for(i=0; i<4; i++){
+                                    tmp_ip[i] = (rule.key.sip[3] >> (8*(3-i))) & 0xff;
+                                }
+                                for(i=4; i<8; i++){
+                                    tmp_ip[i] = (rule.key.sip[2] >> (8*(7-i))) & 0xff;
+                                }
+                                for(i=8; i<12; i++){
+                                    tmp_ip[i] = (rule.key.sip[1] >> (8*(11-i))) & 0xff;
+                                }
+                                for(i=12; i<16; i++){
+                                    tmp_ip[i] = (rule.key.sip[0] >> (8*(15-i))) & 0xff;
+                                }
+                                AIR_PRINT("\t sip: ");
+                                AIR_PRINT("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+                                    tmp_ip[0], tmp_ip[1],tmp_ip[2], tmp_ip[3],tmp_ip[4], tmp_ip[5],tmp_ip[6], tmp_ip[7],
+                                    tmp_ip[8], tmp_ip[9],tmp_ip[10], tmp_ip[11],tmp_ip[12], tmp_ip[13],tmp_ip[14], tmp_ip[15]);
+                                for(i=0; i<4; i++){
+                                    tmp_ip[i] = (rule.mask.sip[3] >> (8*(3-i))) & 0xff;
+                                }
+                                for(i=4; i<8; i++){
+                                    tmp_ip[i] = (rule.mask.sip[2] >> (8*(7-i))) & 0xff;
+                                }
+                                for(i=8; i<12; i++){
+                                    tmp_ip[i] = (rule.mask.sip[1] >> (8*(11-i))) & 0xff;
+                                }
+                                for(i=12; i<16; i++){
+                                    tmp_ip[i] = (rule.mask.sip[0] >> (8*(15-i))) & 0xff;
+                                }
+                                AIR_PRINT(", sip-mask: ");
+                                AIR_PRINT("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n",
+                                    tmp_ip[0], tmp_ip[1],tmp_ip[2], tmp_ip[3],tmp_ip[4], tmp_ip[5],tmp_ip[6], tmp_ip[7],
+                                    tmp_ip[8], tmp_ip[9],tmp_ip[10], tmp_ip[11],tmp_ip[12], tmp_ip[13],tmp_ip[14], tmp_ip[15]);
+                            }
+                            break;
+                        case AIR_ACL_DSCP:
+                            AIR_PRINT("\t dscp: 0x%x, dscp-mask: 0x%x\n", rule.key.dscp, rule.mask.dscp);
+                            break;
+                        case AIR_ACL_PROTOCOL:
+                            AIR_PRINT("\t protocol: 0x%x, protocol-mask: 0x%x\n", rule.key.protocol, rule.mask.protocol);
+                            break;
+                        case AIR_ACL_FLOW_LABEL:
+                            AIR_PRINT("\t flow-label: 0x%x, flow-label-mask: 0x%x\n", rule.key.flow_label, rule.mask.flow_label);
+                            break;
+                        default:
+                            AIR_PRINT("error\n");
+                            break;
+                    }
+                }
+            }
+        }
+        else
+        {
+            AIR_PRINT("Entry is Invalid.\n");
+        }
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doAclRmvRule(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T rule_idx = 0;
+
+    if(1 == argc)
+    {
+        /* acl del rule <idx(0..127)> */
+        rule_idx = _strtoul(argv[0], NULL, 0);
+        ret = air_acl_delRule(0, rule_idx);
+        if(ret < AIR_E_LAST)
+            AIR_PRINT("Delete ACL Rule(%u): %s\n", rule_idx, air_error_getString(ret));
+    }
+    else if(0 == argc)
+    {
+        /* acl clear rule */
+        ret = air_acl_clearRule(0);
+        if(ret < AIR_E_LAST)
+            AIR_PRINT("Clear ACL Rule: %s\n", air_error_getString(ret));
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doAclUdfRule(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T rule_idx;
+    AIR_ACL_UDF_RULE_T rule;
+    C8_T start_addr[8][12]=
+    {
+        "MAC header",
+        "L2 payload",
+        "IPv4 header",
+        "IPv6 header",
+        "L3 payload",
+        "TCP header",
+        "UDP header",
+        "L4 payload"
+    };
+    C8_T str_temp[AIR_MAX_NUM_OF_PORTS+1];
+
+    memset(&rule, 0, sizeof(AIR_ACL_UDF_RULE_T));
+    if(7 == argc)
+    {
+        /* acl set rule <idx(0..255)> <mode(0:pattern, 1:threshold)> [ <pat(4'hex)> <mask(4'hex)> | <low(4'hex)> <high(4'hex)> ] <start(0:MAC,1:ether,2:IP,3:IP_data,4:TCP/UDP,5:TCP/UDP data,6:IPv6)> <offset(0..62,unit:2 bytes)> <portmap(7'bin)> */
+        rule_idx = _strtoul(argv[0], NULL, 0);
+        rule.cmp_sel = _strtoul(argv[1], NULL, 2);
+        if(AIR_ACL_RULE_CMP_SEL_PATTERN == rule.cmp_sel)
+        {
+            rule.pattern = _strtoul(argv[2], NULL, 16);
+            rule.mask = _strtoul(argv[3], NULL, 16);
+        }
+        else
+        {
+            rule.low_threshold = _strtoul(argv[2], NULL, 16);
+            rule.high_threshold = _strtoul(argv[3], NULL, 16);
+        }
+        rule.offset_format = _strtoul(argv[4], NULL, 0);
+        rule.offset = _strtoul(argv[5], NULL, 0);
+        rule.portmap = _strtoul(argv[6], NULL, 2);
+        rule.valid = TRUE;
+        ret = air_acl_setUdfRule(0, rule_idx, rule);
+        if(ret < AIR_E_LAST)
+            AIR_PRINT("Set ACL UDF Rule(%u): %s\n", rule_idx, air_error_getString(ret));
+    }
+    else if(1 == argc)
+    {
+        rule_idx = _strtoul(argv[0], NULL, 0);
+        ret = air_acl_getUdfRule(0, rule_idx, &rule);
+        if(ret < AIR_E_LAST)
+            AIR_PRINT("Get ACL UDF Rule(%u): %s\n", rule_idx, air_error_getString(ret));
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    if(AIR_E_OK == ret)
+    {
+        if(TRUE == rule.valid)
+        {
+            AIR_PRINT("\tMode          : %s\n", (AIR_ACL_RULE_CMP_SEL_PATTERN == rule.cmp_sel)?"Pattern":"Threshold");
+            if(AIR_ACL_RULE_CMP_SEL_PATTERN == rule.cmp_sel)
+            {
+                AIR_PRINT("\tPattern       : 0x%04X\n", rule.pattern);
+                AIR_PRINT("\tMask          : 0x%04X\n", rule.mask);
+            }
+            else
+            {
+                AIR_PRINT("\tLow Threshold : 0x%04X\n", rule.low_threshold);
+                AIR_PRINT("\tHigh Threshold: 0x%04X\n", rule.high_threshold);
+            }
+            AIR_PRINT("\tOffset Start  : %s\n", start_addr[rule.offset_format]);
+            AIR_PRINT("\tOffset        : %u %s\n", rule.offset*2, (0==rule.offset)?"Byte":"Bytes");
+            _hex2bitstr(rule.portmap, str_temp, AIR_MAX_NUM_OF_PORTS+1);
+            AIR_PRINT("\tPortmap[0:6]  : %s\n", str_temp);
+        }
+        else
+        {
+            AIR_PRINT("Entry is Invalid.\n");
+        }
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doAclRmvUdfRule(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T rule_idx;
+
+    if(1 == argc)
+    {
+        /* acl del udfRule <idx(0..15)> */
+        rule_idx = _strtoul(argv[0], NULL, 0);
+        ret = air_acl_delUdfRule(0, rule_idx);
+        if(ret < AIR_E_LAST)
+            AIR_PRINT("Delete ACL UDF Rule(%u): %s\n", rule_idx, air_error_getString(ret));
+    }
+    else if(0 == argc)
+    {
+        /* acl clear udfRule */
+        ret = air_acl_clearUdfRule(0);
+        if(ret < AIR_E_LAST)
+            AIR_PRINT("Clear ACL UDF Rule: %s\n", air_error_getString(ret));
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doAclAction(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T argi = 0;
+    UI32_T act_idx;
+    AIR_ACL_ACTION_T act;
+    UI32_T redirect, trtcm, fwd;
+    C8_T fwding[AIR_ACL_ACT_FWD_LAST][25] =
+    {
+        "Default",
+        "Default",
+        "Default",
+        "Default",
+        "Default & CPU excluded",
+        "Default & CPU included",
+        "CPU only",
+        "Drop"
+    };
+    C8_T trtcm_usr[AIR_ACL_ACT_USR_TCM_LAST][8] =
+    {
+        "Default",
+        "Green",
+        "Yellow",
+        "Red"
+    };
+    C8_T egtag[AIR_ACL_ACT_EGTAG_LAST][11] =
+    {
+        "Default",
+        "Consistent",
+        "",
+        "",
+        "Untag",
+        "Swap",
+        "Tag",
+        "Stack"
+    };
+    C8_T str_temp[20];
+
+    memset(&act, 0, sizeof(AIR_ACL_ACTION_T));
+    if(2 < argc)
+    {
+        /* acl set action <idx(0..127)>
+        [ forward <forward(0:Default,4:Exclude CPU,5:Include CPU,6:CPU only,7:Drop)> ]
+        [ egtag <egtag(0:Default,1:Consistent,4:Untag,5:Swap,6:Tag,7:Stack)> ]
+        [ mirrormap <mirrormap(2'bin)> ]
+        [ priority <priority(0..7)> ]
+        [ redirect <redirect(0:Dst,1:Vlan)> <portmap(7'bin)> ]
+        [ leaky_vlan <leaky_vlan(1:En,0:Dis)> ]
+        [ cnt_idx <cnt_idx(0..63)> ]
+        [ rate_idx <rate_idx(0..31)> ]
+        [ attack_idx <attack_idx(0..95)> ]
+        [ vid <vid(0..4095)> ]
+        [ manage <manage(1:En,0:Dis)> ]
+        [ bpdu <bpdu(1:En,0:Dis)> ]
+        [ class <class(0:Original,1:Defined)>[0..7] ]
+        [ drop_pcd <drop_pcd(0:Original,1:Defined)> [red <red(0..7)>][yellow <yellow(0..7)>][green <green(0..7)>] ]
+        [ color <color(0:Defined,1:Trtcm)> [ <defined_color(0:Dis,1:Green,2:Yellow,3:Red)> | <trtcm_idx(0..31)> ] ]*/
+
+        act_idx = _strtoul(argv[argi++], NULL, 0);
+        if((argi < argc) && (0 == _strcmp(argv[argi], "forward")))
+        {
+            argi++;
+            fwd = _strtoul(argv[argi++], NULL, 0);
+            act.fwd_en = TRUE;
+            act.fwd = fwd;
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "egtag")))
+        {
+            argi++;
+            act.egtag_en = TRUE;
+            act.egtag = _strtoul(argv[argi++], NULL, 0);
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "mirrormap")))
+        {
+            argi++;
+            act.mirrormap = _strtoul(argv[argi++], NULL, 2);
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "priority")))
+        {
+            argi++;
+            act.pri_user_en = TRUE;
+            act.pri_user= _strtoul(argv[argi++], NULL, 0);
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "redirect")))
+        {
+            argi++;
+            redirect = _strtoul(argv[argi++], NULL, 0);
+            if(0 ==  redirect)
+            {
+                act.port_en = TRUE;
+                act.dest_port_sel = TRUE;
+                act.portmap = _strtoul(argv[argi++], NULL, 2);
+            }
+            else
+            {
+                act.port_en = TRUE;
+                act.vlan_port_sel = TRUE;
+                act.portmap = _strtoul(argv[argi++], NULL, 2);
+            }
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "leaky_vlan")))
+        {
+            argi++;
+            act.lyvlan_en = TRUE;
+            act.lyvlan = _strtoul(argv[argi++], NULL, 0);
+        }
+
+        /* ACL event counter */
+        if((argi < argc) && (0 == _strcmp(argv[argi], "cnt_idx")))
+        {
+            argi++;
+            act.cnt_en = TRUE;
+            act.cnt_idx = _strtol(argv[argi++], NULL, 0);
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "rate_idx")))
+        {
+            argi++;
+            act.rate_en = TRUE;
+            act.rate_idx = _strtol(argv[argi++], NULL, 0);
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "attack_idx")))
+        {
+            argi++;
+            act.attack_en = TRUE;
+            act.attack_idx = _strtol(argv[argi++], NULL, 0);
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "vid")))
+        {
+            argi++;
+            act.vlan_en = TRUE;
+            act.vlan_idx = _strtol(argv[argi++], NULL, 0);
+        }
+
+        /* Management frame */
+        if((argi < argc) && (0 == _strcmp(argv[argi], "manage")))
+        {
+            argi++;
+            act.mang = _strtoul(argv[argi++], NULL, 2);
+        }
+
+        if((argi < argc) && (0 == _strcmp(argv[argi], "bpdu")))
+        {
+            argi++;
+            act.bpdu = _strtoul(argv[argi++], NULL, 2);
+        }
+
+        /* DSCP class remap */
+        if((argi < argc) && (0 == _strcmp(argv[argi], "class")))
+        {
+            argi++;
+            act.trtcm_en = TRUE;
+            act.trtcm.cls_slr_sel = _strtoul(argv[argi++], NULL, 2);
+            if(TRUE == act.trtcm.cls_slr_sel)
+            {
+                act.trtcm.cls_slr = _strtoul(argv[argi++], NULL, 0);
+            }
+        }
+        if((argi < argc) && (0 == _strcmp(argv[argi], "drop_pcd")))
+        {
+            argi++;
+            act.trtcm_en = TRUE;
+            act.trtcm.drop_pcd_sel = _strtoul(argv[argi++], NULL, 2);
+            if(TRUE == act.trtcm.drop_pcd_sel)
+            {
+                if(0 == _strcmp(argv[argi], "red"))
+                {
+                    argi++;
+                    act.trtcm.drop_pcd_r= _strtoul(argv[argi++], NULL, 0);
+                }
+                if(0 == _strcmp(argv[argi], "yellow"))
+                {
+                    argi++;
+                    act.trtcm.drop_pcd_y= _strtoul(argv[argi++], NULL, 0);
+                }
+                if(0 == _strcmp(argv[argi], "green"))
+                {
+                    argi++;
+                    act.trtcm.drop_pcd_g= _strtoul(argv[argi++], NULL, 0);
+                }
+            }
+        }
+
+        /* trTCM */
+        if((argi < argc) && (0 == _strcmp(argv[argi], "color")))
+        {
+            argi++;
+            act.trtcm_en = TRUE;
+            act.trtcm.tcm_sel = _strtoul(argv[argi++], NULL, 2);
+            trtcm = _strtoul(argv[argi++], NULL, 0);
+            if(FALSE == act.trtcm.tcm_sel)
+            {
+                act.trtcm.usr_tcm = trtcm;
+            }
+            else
+            {
+                act.trtcm.tcm_idx = trtcm;
+            }
+        }
+        ret = air_acl_setAction(0, act_idx, act);
+        AIR_PRINT("Set ACL Action(%u): %s\n", act_idx, air_error_getString(ret));
+    }
+    else if(1 == argc)
+    {
+        /* acl get action <idx(0..127)> */
+        act_idx = _strtoul(argv[argi++], NULL, 0);
+        ret = air_acl_getAction(0, act_idx, &act);
+        AIR_PRINT("Get ACL Action(%u): %s\n", act_idx, air_error_getString(ret));
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    if(AIR_E_OK == ret)
+    {
+        if(TRUE == act.fwd_en)
+        {
+            AIR_PRINT("\t Forwarding           : %s\n", fwding[act.fwd]);
+        }
+
+        if(TRUE == act.egtag_en)
+        {
+            AIR_PRINT("\t Egress tag           : %s\n", egtag[act.egtag]);
+        }
+
+        if(act.mirrormap)
+        {
+            AIR_PRINT("\t Mirror Session Map   : %u\n", act.mirrormap);
+        }
+
+        if(TRUE == act.pri_user_en)
+        {
+            AIR_PRINT("\t User Priority        : %u\n", act.pri_user);
+        }
+
+        if(TRUE == act.port_en)
+        {
+            _hex2bitstr(act.portmap, str_temp, AIR_MAX_NUM_OF_PORTS+1);
+            if(TRUE == act.dest_port_sel)
+            {
+                AIR_PRINT("\t Destination Port[0:6]: %s\n", str_temp);
+            }
+            else
+            {
+                AIR_PRINT("\t VLAN Port[0:6]       : %s\n", str_temp);
+            }
+        }
+
+        if(TRUE == act.lyvlan_en)
+        {
+            AIR_PRINT("\t Leaky VLAN           : %s\n", (TRUE == act.lyvlan)?"Enable":"Disable");
+        }
+        AIR_PRINT("\t Management Frame     : %s\n", (TRUE == act.mang)?"Enable":"Disable");
+        AIR_PRINT("\t BPDU Frame           : %s\n", (TRUE == act.bpdu)?"Enable":"Disable");
+
+        if(TRUE == act.cnt_en)
+        {
+            AIR_PRINT("\t Event Index          : %u\n", act.cnt_idx);
+        }
+
+        /* trTCM*/
+        if(TRUE == act.trtcm_en)
+        {
+            if(TRUE == act.trtcm.cls_slr_sel)
+            {
+                AIR_PRINT("\t Class Selector Remap : %u\n", act.trtcm.cls_slr);
+            }
+            else
+            {
+                AIR_PRINT("\t Class Selector Remap : %s\n", "Disable");
+            }
+            if(TRUE == act.trtcm.drop_pcd_sel)
+            {
+                AIR_PRINT("\t Drop Precedence Remap(Red): %u\n", act.trtcm.drop_pcd_r);
+                AIR_PRINT("\t Drop Precedence Remap(Yel): %u\n", act.trtcm.drop_pcd_y);
+                AIR_PRINT("\t Drop Precedence Remap(Gre): %u\n", act.trtcm.drop_pcd_g);
+            }
+            else
+            {
+                AIR_PRINT("\t Drop Precedence Remap: %s\n", "Disable");
+            }
+
+            if(TRUE == act.trtcm.tcm_sel)
+            {
+                AIR_PRINT("\t trTCM Meter Index    : %u\n", act.trtcm.tcm_idx);
+            }
+            else
+            {
+                AIR_PRINT("\t trTCM User Defined   : %s\n", trtcm_usr[act.trtcm.usr_tcm]);
+            }
+        }
+        /* rate control */
+        if(TRUE == act.rate_en)
+        {
+            AIR_PRINT("\t Rate Control Index   : %u\n", act.rate_idx);
+        }
+
+        if(TRUE == act.attack_en)
+        {
+            AIR_PRINT("\t Attack Rate Control Index: %u\n", act.attack_idx);
+        }
+
+        if(TRUE == act.vlan_en)
+        {
+            AIR_PRINT("\t ACL VLAN Index       : %u\n", act.vlan_idx);
+        }
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doAclRmvAction(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T act_idx;
+
+    if(1 == argc)
+    {
+        /* acl del action <idx(0..127)> */
+        act_idx = _strtoul(argv[0], NULL, 0);
+        ret = air_acl_delAction(0, act_idx);
+        if(ret < AIR_E_LAST)
+            AIR_PRINT("Delete ACL Action(%u): %s\n", act_idx, air_error_getString(ret));
+    }
+    else if(0 == argc)
+    {
+        /* acl clear action */
+        ret = air_acl_clearAction(0);
+        if(ret < AIR_E_LAST)
+            AIR_PRINT("Clear ACL Action: %s\n", air_error_getString(ret));
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doAclTrtcm(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T argi = 0;
+    UI32_T tcm_idx;
+    AIR_ACL_TRTCM_T tcm;
+
+    memset(&tcm, 0, sizeof(AIR_ACL_TRTCM_T));
+    if(5 == argc)
+    {
+        /* acl set trtcm <idx(0..31)> <cir(4'hex)> <pir(4'hex)> <cbs(4'hex)> <pbs(4'hex)> */
+        tcm_idx = _strtoul(argv[argi++], NULL, 0);
+        tcm.cir = _strtoul(argv[argi++], NULL, 16);
+        tcm.pir = _strtoul(argv[argi++], NULL, 16);
+        tcm.cbs = _strtoul(argv[argi++], NULL, 16);
+        tcm.pbs = _strtoul(argv[argi++], NULL, 16);
+
+        ret = air_acl_setTrtcm(0, tcm_idx, tcm);
+        if(ret < AIR_E_LAST)
+            AIR_PRINT("Set ACL trTCM(%u): %s\n", tcm_idx, air_error_getString(ret));
+    }
+    else if(1 == argc)
+    {
+        /* acl get trtcm <idx(1..31)> */
+        tcm_idx = _strtoul(argv[argi++], NULL, 0);
+        ret = air_acl_getTrtcm(0, tcm_idx, &tcm);
+        if(ret < AIR_E_LAST)
+            AIR_PRINT("Get ACL trTCM(%u): %s\n", tcm_idx, air_error_getString(ret));
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    if(AIR_E_OK == ret)
+    {
+        AIR_PRINT("\t CIR: 0x%04X(unit:64Kbps)\n", tcm.cir);
+        AIR_PRINT("\t PIR: 0x%04X(unit:64Kbps)\n", tcm.pir);
+        AIR_PRINT("\t CBS: 0x%04X(unit:Byte)\n", tcm.cbs);
+        AIR_PRINT("\t PBS: 0x%04X(unit:Byte)\n", tcm.pbs);
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doAclTrtcmEn(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    BOOL_T state = FALSE;
+
+    if (1 == argc)
+    {
+        /* acl set trtcmEn <en(1:En,0:Dis)> */
+        state = _strtol(argv[0], NULL, 10);
+        ret = air_acl_setTrtcmEnable(0, state);
+        AIR_PRINT("Set trTCM State ");
+    }
+    else if (0 == argc)
+    {
+        /* acl get trtcmEn */
+        ret = air_acl_getTrtcmEnable(0, &state);
+        AIR_PRINT("Get trTCM State ");
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    if (ret == AIR_E_OK)
+    {
+        AIR_PRINT(": %s\n", (TRUE == state) ? "Enable" : "Disable");
+    }
+    else
+    {
+        AIR_PRINT("Fail!\n");
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doAclRmvTrtcm(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T tcm_idx;
+
+    if(1 == argc)
+    {
+        /* acl del trtcm <idx(1..31)> */
+        tcm_idx = _strtoul(argv[0], NULL, 0);
+        ret = air_acl_delTrtcm(0, tcm_idx);
+        if(ret < AIR_E_LAST)
+            AIR_PRINT("Delete ACL TRTCM(%u): %s\n", tcm_idx, air_error_getString(ret));
+    }
+    else if(0 == argc)
+    {
+        /* acl clear trtcm */
+        ret = air_acl_clearTrtcm(0);
+        if(ret < AIR_E_LAST)
+            AIR_PRINT("Clear ACL TRTCM: %s\n", air_error_getString(ret));
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doAclPortEn(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T argi=0;
+    UI32_T port = 0;
+    BOOL_T en;
+
+    if(2 == argc)
+    {
+        /* acl set portEn <port(0..6)> <en(1:En,0:Dis)> */
+        port = _strtoul(argv[argi++], NULL, 0);
+        en = _strtoul(argv[argi++], NULL, 2);
+        ret = air_acl_setPortEnable(0, port, en);
+        AIR_PRINT("Set Port:%u ACL function ", port);
+    }
+    else if(1 == argc)
+    {
+        /* acl get portEn <port(0..6)> */
+        port = _strtoul(argv[argi++], NULL, 0);
+        ret = air_acl_getPortEnable(0, port, &en);
+        AIR_PRINT("Get Port:%u ACL function ", port);
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    if(ret == AIR_E_OK)
+    {
+        AIR_PRINT(": %s\n", (TRUE == en)?"Enable":"Disable");
+    }
+    else
+    {
+        AIR_PRINT("Fail!\n");
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doAclDropEn(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T argi=0;
+    UI32_T port = 0;
+    BOOL_T en;
+
+    if(2 == argc)
+    {
+        /* acl set dropEn <port(0..6)> <en(1:En,0:Dis)> */
+        port = _strtoul(argv[argi++], NULL, 0);
+        en = _strtoul(argv[argi++], NULL, 2);
+        ret = air_acl_setDropEnable(0, port, en);
+        AIR_PRINT("Set ACL Drop precedence ");
+    }
+    else if(1 == argc)
+    {
+        /* acl set dropEn <port(0..6)> */
+        port = _strtoul(argv[argi++], NULL, 0);
+        ret = air_acl_getDropEnable(0, port, &en);
+        AIR_PRINT("Get ACL Drop precedence ");
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    if(ret == AIR_E_OK)
+    {
+        AIR_PRINT("(Port %u):%s\n",
+                port,
+                (TRUE == en)?"Enable":"Disable");
+    }
+    else
+    {
+        AIR_PRINT("Fail!\n");
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doAclDropThrsh(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T argi=0;
+    UI32_T port = 0;
+    AIR_ACL_DP_COLOR_T color;
+    UI8_T queue;
+    UI32_T high, low;
+    C8_T dp_color[AIR_ACL_DP_COLOR_LAST][7] =
+    {
+        "Green",
+        "Yellow",
+        "Red"
+    };
+
+    if(5 == argc)
+    {
+        /* acl set dropThrsh <port(0..6)> <color(0:green,1:yellow,2:red)> <queue(0..7)> <high(0..2047)> <low(0..2047) */
+        port = _strtoul(argv[argi++], NULL, 0);
+        color = _strtoul(argv[argi++], NULL, 0);
+        queue = _strtoul(argv[argi++], NULL, 0);
+        high = _strtoul(argv[argi++], NULL, 0);
+        low = _strtoul(argv[argi++], NULL, 0);
+        ret = air_acl_setDropThreshold(0, port, color, queue, high, low);
+        AIR_PRINT("Set ACL Drop precedence ");
+    }
+    else if(3 == argc)
+    {
+        /* acl get dropThrsh <port(0..6)> <color(0:green,1:yellow,2:red)> <queue(0..7)> */
+        port = _strtoul(argv[argi++], NULL, 0);
+        color = _strtoul(argv[argi++], NULL, 0);
+        queue = _strtoul(argv[argi++], NULL, 0);
+        ret = air_acl_getDropThreshold(0, port, color, queue, &high, &low);
+        AIR_PRINT("Get ACL Drop precedence ");
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    if(ret == AIR_E_OK)
+    {
+        AIR_PRINT("(Port %u, color:%s, queue:%u):\n",
+                port,
+                dp_color[color],
+                queue);
+        AIR_PRINT("\tHigh Threshold :%u\n", high);
+        AIR_PRINT("\tLow Threshold  :%u\n", low);
+    }
+    else
+    {
+        AIR_PRINT("Fail!\n");
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doAclDropPbb(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T argi=0;
+    UI32_T port = 0;
+    AIR_ACL_DP_COLOR_T color;
+    UI8_T queue;
+    UI32_T pbb;
+    C8_T dp_color[AIR_ACL_DP_COLOR_LAST][7] =
+    {
+        "Green",
+        "Yellow",
+        "Red"
+    };
+
+    if(4 == argc)
+    {
+        /* acl set dropPbb <port(0..6)> <color(0:green,1:yellow,2:red)> <queue(0..7)> <probability(0..1023) */
+        port = _strtoul(argv[argi++], NULL, 0);
+        color = _strtoul(argv[argi++], NULL, 0);
+        queue = _strtoul(argv[argi++], NULL, 0);
+        pbb = _strtoul(argv[argi++], NULL, 0);
+        ret = air_acl_setDropProbability(0, port, color, queue, pbb);
+        AIR_PRINT("Set ACL Drop precedence ");
+    }
+    else if(3 == argc)
+    {
+        /* acl get dropPbb <port(0..6)> <color(0:green,1:yellow,2:red)> <queue(0..7)> */
+        port = _strtoul(argv[argi++], NULL, 0);
+        color = _strtoul(argv[argi++], NULL, 0);
+        queue = _strtoul(argv[argi++], NULL, 0);
+        ret = air_acl_getDropProbability(0, port, color, queue, &pbb);
+        AIR_PRINT("Get ACL Drop precedence ");
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    if(ret == AIR_E_OK)
+    {
+        AIR_PRINT("(Port %u, color:%s, queue %u):\n",
+                port,
+                dp_color[color],
+                queue);
+        AIR_PRINT("\tDrop probability:%u(unit=1/1023)\n", pbb);
+    }
+    else
+    {
+        AIR_PRINT("Fail!\n");
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doAclMeter(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T argi = 0;
+    UI32_T meter_idx, state, rate;
+
+    if(3 == argc)
+    {
+        /* acl set meter <idx(0..31)> <en(1:En,0:Dis)> <rate(0..65535)>
+           Note: Limit rate = rate * 64Kbps */
+        meter_idx = _strtoul(argv[argi++], NULL, 0);
+        state = _strtoul(argv[argi++], NULL, 2);
+        rate = _strtoul(argv[argi++], NULL, 0);
+
+        ret = air_acl_setMeterTable(0, meter_idx, state, rate);
+        if(ret < AIR_E_LAST)
+            AIR_PRINT("Set ACL Meter(%u): %s\n", meter_idx, air_error_getString(ret));
+    }
+    else if(1 == argc)
+    {
+        /* acl get meter <idx(0..31)> */
+        meter_idx = _strtoul(argv[argi++], NULL, 0);
+        ret = air_acl_getMeterTable(0, meter_idx, &state, &rate);
+        if(ret < AIR_E_LAST)
+            AIR_PRINT("Get ACL Meter(%u): %s\n", meter_idx, air_error_getString(ret));
+    }
+    else
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    if(AIR_E_OK == ret)
+    {
+        AIR_PRINT("\t State: %s\n", (TRUE == state)?"Enable":"Disable");
+        if(TRUE == state)
+        {
+            AIR_PRINT("\t Rate : %u(unit:64Kbps)\n", rate);
+        }
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doAclDump(
+    UI32_T argc,
+    C8_T *argv[])
+{
+    AIR_ERROR_NO_T ret = AIR_E_OK;
+    UI32_T i, cnt = 0;
+    AIR_ACL_CTRL_T ctrl;
+
+    if(0 != argc)
+    {
+        AIR_PRINT("Unrecognized command.\n");
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    for(i=0; i<ACL_MAX_RULE_NUM; i++)
+    {
+        memset(&ctrl, 0, sizeof(AIR_ACL_CTRL_T));
+        ret = air_acl_getRuleCtrl(0, i, &ctrl);
+        if(AIR_E_OK == ret)
+        {
+            if(TRUE == ctrl.rule_en)
+            {
+                cnt++;
+                AIR_PRINT("\t Entry-%d vaild\n", i);
+            }
+        }
+    }
+    if(0 == cnt)
+    {
+        AIR_PRINT("\t No entry vaild\n");
+    }
+
+    return ret;
+}
+
+static AIR_ERROR_NO_T
+doAclSet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(aclSetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doAclGet(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(aclGetCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doAclDel(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(aclDelCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doAclClear(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(aclClearCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+doAcl(
+        UI32_T argc,
+        C8_T *argv[])
+{
+    return subcmd(aclCmds, argc, argv);
+}
+
+static AIR_ERROR_NO_T
+subcmd(
+        const AIR_CMD_T tab[],
+        UI32_T argc,
+        C8_T *argv[])
+{
+    const AIR_CMD_T *cmdp;
+    I32_T found = 0;
+    I32_T i, len;
+
+    if (argc < 1)
+    {
+        goto print_out_cmds;
+    }
+
+    for (cmdp = tab; cmdp->name != NULL; cmdp++)
+    {
+        if (strlen(argv[0]) == strlen(cmdp->name))
+        {
+            if (strncmp(argv[0], cmdp->name, strlen(argv[0])) == 0)
+            {
+                found = 1;
+                break;
+            }
+        }
+    }
+
+    if(!found)
+    {
+        C8_T buf[66];
+
+print_out_cmds:
+        AIR_PRINT("valid subcommands:\n");
+        memset(buf, ' ', sizeof(buf));
+        buf[64] = '\n';
+        buf[65] = '\0';
+
+        for (i=0, cmdp = tab; cmdp->name != NULL; cmdp++)
+        {
+            len = strlen(cmdp->name);
+            strncpy(&buf[i*16], cmdp->name, (len > 16) ? 16 : len);
+            if(3 == i)
+            {
+                AIR_PRINT("%s\n", buf);
+                memset(buf, ' ', sizeof(buf));
+                buf[64] = '\n';
+                buf[65] = '\0';
+            }
+            i = (i + 1) % 4;
+        }
+
+        if (0 != i)
+            AIR_PRINT("%s\n", buf);
+
+        return AIR_E_BAD_PARAMETER;
+    }
+
+    if (CMD_NO_PARA == cmdp->argc_min)
+    {
+        if (argc != 1)
+        {
+            if (cmdp->argc_errmsg != NULL)
+            {
+                AIR_PRINT("Usage: %s\n", cmdp->argc_errmsg);
+            }
+
+            return AIR_E_BAD_PARAMETER;
+        }
+    }
+    else if (CMD_VARIABLE_PARA == cmdp->argc_min)
+    {
+        if (argc < 3)
+        {
+            if (cmdp->argc_errmsg != NULL)
+            {
+                AIR_PRINT("Usage: %s\n", cmdp->argc_errmsg);
+            }
+
+            return AIR_E_BAD_PARAMETER;
+        }
+    }
+    else
+    {
+        if ((argc <= cmdp->argc_min) || ((cmdp->argc_min != 0) && (argc != (cmdp->argc_min + 1))))
+        {
+            if (cmdp->argc_errmsg != NULL)
+            {
+                AIR_PRINT("Usage: %s\n", cmdp->argc_errmsg);
+            }
+
+            return AIR_E_BAD_PARAMETER;
+        }
+    }
+
+    if (cmdp->func)
+    {
+        argc--;
+        argv++;
+        return (*cmdp->func)(argc, argv);
+    }
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_parse_cmd
+ * PURPOSE:
+ *      This function is used process diagnostic cmd
+ * INPUT:
+ *      argc         -- parameter number
+ *      argv         -- parameter strings
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      NPS_E_OK     -- Successfully read the data.
+ *      NPS_E_OTHERS -- Failed to read the data.
+ * NOTES:
+ *
+ */
+AIR_ERROR_NO_T
+air_parse_cmd(
+        const UI32_T argc,
+        const C8_T **argv)
+{
+    return subcmd(Cmds, argc, (C8_T **)argv);
+}
+
diff --git a/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_error.c b/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_error.c
index 3323e54..abe27dd 100644
--- a/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_error.c
+++ b/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_error.c
@@ -1,73 +1,73 @@
-/* FILE NAME:   air_error.c

- * PURPOSE:

- *      Define the software modules in AIR SDK.

- * NOTES:

- */

-

-/* INCLUDE FILE DECLARATIONS

- */

-#include "air.h"

-

-/* NAMING CONSTANT DECLARATIONS

- */

-

-/* MACRO FUNCTION DECLARATIONS

- */

-

-/* DATA TYPE DECLARATIONS

- */

-

-/* GLOBAL VARIABLE DECLARATIONS

- */

-

-/* LOCAL SUBPROGRAM DECLARATIONS

- */

-

-/* STATIC VARIABLE DECLARATIONS

- */

-static C8_T *_air_error_cause[AIR_E_LAST] =

-{

-    "OK",

-    "NOT_OK",

-    "BAD_PARAMETER",

-    "TABLE_FULL",

-    "ENTRY_NOT_FOUND",

-    "ENTRY_EXISTS",

-    "NOT_SUPPORT",

-    "TIMEOUT",

-};

-

-/* EXPORTED SUBPROGRAM BODIES

- */

-

-/* LOCAL SUBPROGRAM BODIES

- */

-/* FUNCTION NAME:   air_error_getString

- * PURPOSE:

- *      To obtain the error string of the specified error code

- *

- * INPUT:

- *      The specified error code

- * OUTPUT:

- *      None

- * RETURN:

- *      Pointer to the target error string

- *

- * NOTES:

- *

- *

- */

-C8_T *

-air_error_getString(

-const AIR_ERROR_NO_T cause )

-{

-    if(cause < AIR_E_LAST)

-    {

-        return _air_error_cause[cause];

-    }

-    else

-    {

-        return "";

-    }

-}

-

+/* FILE NAME:   air_error.c
+ * PURPOSE:
+ *      Define the software modules in AIR SDK.
+ * NOTES:
+ */
+
+/* INCLUDE FILE DECLARATIONS
+ */
+#include "air.h"
+
+/* NAMING CONSTANT DECLARATIONS
+ */
+
+/* MACRO FUNCTION DECLARATIONS
+ */
+
+/* DATA TYPE DECLARATIONS
+ */
+
+/* GLOBAL VARIABLE DECLARATIONS
+ */
+
+/* LOCAL SUBPROGRAM DECLARATIONS
+ */
+
+/* STATIC VARIABLE DECLARATIONS
+ */
+static C8_T *_air_error_cause[AIR_E_LAST] =
+{
+    "OK",
+    "NOT_OK",
+    "BAD_PARAMETER",
+    "TABLE_FULL",
+    "ENTRY_NOT_FOUND",
+    "ENTRY_EXISTS",
+    "NOT_SUPPORT",
+    "TIMEOUT",
+};
+
+/* EXPORTED SUBPROGRAM BODIES
+ */
+
+/* LOCAL SUBPROGRAM BODIES
+ */
+/* FUNCTION NAME:   air_error_getString
+ * PURPOSE:
+ *      To obtain the error string of the specified error code
+ *
+ * INPUT:
+ *      The specified error code
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      Pointer to the target error string
+ *
+ * NOTES:
+ *
+ *
+ */
+C8_T *
+air_error_getString(
+const AIR_ERROR_NO_T cause )
+{
+    if(cause < AIR_E_LAST)
+    {
+        return _air_error_cause[cause];
+    }
+    else
+    {
+        return "";
+    }
+}
+
diff --git a/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_init.c b/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_init.c
index 8a4f96f..63664dc 100644
--- a/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_init.c
+++ b/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_init.c
@@ -1,147 +1,147 @@
-/* FILE NAME:   air_init.c

- * PURPOSE:

- *      Define the initialization function in AIR SDK.

- *

- * NOTES:

- *      None

- */

-

-/* INCLUDE FILE DECLARATIONS

- */

-#include "air.h"

-

-/* NAMING CONSTANT DECLARATIONS

- */

-

-/* MACRO FUNCTION DECLARATIONS

- */

-

-/* DATA TYPE DECLARATIONS

- */

-

-/* GLOBAL VARIABLE DECLARATIONS

- */

-AIR_PRINTF _ext_printf;

-AIR_UDELAY _ext_udelay;

-AIR_MALLOC _ext_malloc;

-AIR_FREE   _ext_free;

-

-/* LOCAL SUBPROGRAM DECLARATIONS

- */

-

-/* STATIC VARIABLE DECLARATIONS

- */

-

-/* EXPORTED SUBPROGRAM BODIES

- */

-

-/* LOCAL SUBPROGRAM BODIES

- */

-

-/* FUNCTION NAME:   air_init

- * PURPOSE:

- *      This API is used to initialize the SDK.

- *

- * INPUT:

- *      unit            --  The device unit

- *      ptr_init_param  --  The sdk callback functions.

- *

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_OTHERS

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_init(

-    const UI32_T unit,

-    AIR_INIT_PARAM_T *ptr_init_param)

-{

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-    UI32_T u32dat = 0;

-    UI8_T port = 0;

-    AIR_LED_ON_EVT_T on_evt;

-    AIR_LED_BLK_EVT_T blk_evt;

-

-    /* check point */

-    AIR_CHECK_PTR(ptr_init_param);

-

-    _ext_dev_access.read_callback = ptr_init_param->dev_access.read_callback;

-    _ext_dev_access.write_callback = ptr_init_param->dev_access.write_callback;

-    _ext_dev_access.phy_read_callback = ptr_init_param->dev_access.phy_read_callback;

-    _ext_dev_access.phy_write_callback = ptr_init_param->dev_access.phy_write_callback;

-    _ext_dev_access.phy_cl45_read_callback = ptr_init_param->dev_access.phy_cl45_read_callback;

-    _ext_dev_access.phy_cl45_write_callback = ptr_init_param->dev_access.phy_cl45_write_callback;

-    _ext_printf = ptr_init_param->printf;

-    _ext_udelay = ptr_init_param->udelay;

-    _ext_malloc = ptr_init_param->malloc;

-    _ext_free   = ptr_init_param->free;

-    

-    return rc;

-}

-

-/* FUNCTION NAME:   air_hw_reset

- * PURPOSE:

- *      This API is used to reset hardware.

- *

- * INPUT:

- *      unit            --  The device unit

- *

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_OTHERS

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_hw_reset(

-    const UI32_T unit)

-{

-    AIR_PRINT(">>>>> enct_hw_reset\n");

-    /* Set an8855 reset pin to 0 */

-

-    /* Delay 100ms */

-

-    /* Set an8855 reset pin to 1 */

-

-    /* Delay 600ms */

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_set_gpio_pin_mux

- * PURPOSE:

- *      This API is used to set gpio pin mux.

- *

- * INPUT:

- *      unit            --  The device unit

- *

- * OUTPUT:

- *      None

- *

- * RETURN:

- *      AIR_E_OK

- *      AIR_E_OTHERS

- *

- * NOTES:

- *      None

- */

-AIR_ERROR_NO_T

-air_set_gpio_pin_mux(

-    const UI32_T unit)

-{

-    AIR_PRINT(">>>>> enct_set_gpio_pin_mux\n");

-    /* Set GPIO_MODE0 */

-    /* Implementation for SLT HW */

-    aml_writeReg(unit, GPIO_MODE0, 0x11111111);

-

-    return AIR_E_OK;

-}

+/* FILE NAME:   air_init.c
+ * PURPOSE:
+ *      Define the initialization function in AIR SDK.
+ *
+ * NOTES:
+ *      None
+ */
+
+/* INCLUDE FILE DECLARATIONS
+ */
+#include "air.h"
+
+/* NAMING CONSTANT DECLARATIONS
+ */
+
+/* MACRO FUNCTION DECLARATIONS
+ */
+
+/* DATA TYPE DECLARATIONS
+ */
+
+/* GLOBAL VARIABLE DECLARATIONS
+ */
+AIR_PRINTF _ext_printf;
+AIR_UDELAY _ext_udelay;
+AIR_MALLOC _ext_malloc;
+AIR_FREE   _ext_free;
+
+/* LOCAL SUBPROGRAM DECLARATIONS
+ */
+
+/* STATIC VARIABLE DECLARATIONS
+ */
+
+/* EXPORTED SUBPROGRAM BODIES
+ */
+
+/* LOCAL SUBPROGRAM BODIES
+ */
+
+/* FUNCTION NAME:   air_init
+ * PURPOSE:
+ *      This API is used to initialize the SDK.
+ *
+ * INPUT:
+ *      unit            --  The device unit
+ *      ptr_init_param  --  The sdk callback functions.
+ *
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_OTHERS
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_init(
+    const UI32_T unit,
+    AIR_INIT_PARAM_T *ptr_init_param)
+{
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+    UI32_T u32dat = 0;
+    UI8_T port = 0;
+    AIR_LED_ON_EVT_T on_evt;
+    AIR_LED_BLK_EVT_T blk_evt;
+
+    /* check point */
+    AIR_CHECK_PTR(ptr_init_param);
+
+    _ext_dev_access.read_callback = ptr_init_param->dev_access.read_callback;
+    _ext_dev_access.write_callback = ptr_init_param->dev_access.write_callback;
+    _ext_dev_access.phy_read_callback = ptr_init_param->dev_access.phy_read_callback;
+    _ext_dev_access.phy_write_callback = ptr_init_param->dev_access.phy_write_callback;
+    _ext_dev_access.phy_cl45_read_callback = ptr_init_param->dev_access.phy_cl45_read_callback;
+    _ext_dev_access.phy_cl45_write_callback = ptr_init_param->dev_access.phy_cl45_write_callback;
+    _ext_printf = ptr_init_param->printf;
+    _ext_udelay = ptr_init_param->udelay;
+    _ext_malloc = ptr_init_param->malloc;
+    _ext_free   = ptr_init_param->free;
+    
+    return rc;
+}
+
+/* FUNCTION NAME:   air_hw_reset
+ * PURPOSE:
+ *      This API is used to reset hardware.
+ *
+ * INPUT:
+ *      unit            --  The device unit
+ *
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_OTHERS
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_hw_reset(
+    const UI32_T unit)
+{
+    AIR_PRINT(">>>>> enct_hw_reset\n");
+    /* Set an8855 reset pin to 0 */
+
+    /* Delay 100ms */
+
+    /* Set an8855 reset pin to 1 */
+
+    /* Delay 600ms */
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_set_gpio_pin_mux
+ * PURPOSE:
+ *      This API is used to set gpio pin mux.
+ *
+ * INPUT:
+ *      unit            --  The device unit
+ *
+ * OUTPUT:
+ *      None
+ *
+ * RETURN:
+ *      AIR_E_OK
+ *      AIR_E_OTHERS
+ *
+ * NOTES:
+ *      None
+ */
+AIR_ERROR_NO_T
+air_set_gpio_pin_mux(
+    const UI32_T unit)
+{
+    AIR_PRINT(">>>>> enct_set_gpio_pin_mux\n");
+    /* Set GPIO_MODE0 */
+    /* Implementation for SLT HW */
+    aml_writeReg(unit, GPIO_MODE0, 0x11111111);
+
+    return AIR_E_OK;
+}
diff --git a/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_port.c b/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_port.c
index 09dcef1..d33bf70 100644
--- a/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_port.c
+++ b/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_port.c
@@ -390,8 +390,8 @@
     AIR_CHECK_PTR(ptr_speed);
 
     /* Read data from register */
-    aml_readPhyReg(unit, port, 0x0, &u32dat);
-    (*ptr_speed) = (BITS_OFF_R(u32dat, 6, 1) << 1) | BITS_OFF_R(u32dat, 13, 1);
+	aml_readReg(unit, PMSR(port), &u32dat);
+	(*ptr_speed) = BITS_OFF_R(u32dat, 28, 3);
 
     return ret;
 }
@@ -491,8 +491,8 @@
     AIR_CHECK_PTR(ptr_duplex);
 
     /* Read data from register */
-    aml_readPhyReg(unit, port, 0x0, &u32dat);
-    (*ptr_duplex) = BITS_OFF_R(u32dat, 8, 1);
+	aml_readReg(unit, PMSR(port), &u32dat);
+	(*ptr_duplex) = BITS_OFF_R(u32dat, 25, 1);
 
     return AIR_E_OK;
 }
diff --git a/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_vlan.c b/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_vlan.c
index 6629165..94f296e 100644
--- a/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_vlan.c
+++ b/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_vlan.c
@@ -1,1448 +1,1449 @@
-/* FILE NAME:   air_vlan.c

- * PURPOSE:

- *      Define the VLAN function in AIR SDK.

- * NOTES:

- */

-

-/* INCLUDE FILE DECLARATIONS

- */

-#include "air.h"

-

-/* NAMING CONSTANT DECLARATIONS

- */

-

-/* MACRO FUNCTION DECLARATIONS

- */

-

-/* DATA TYPE DECLARATIONS

- */

-

-/* GLOBAL VARIABLE DECLARATIONS

- */

-

-/* LOCAL SUBPROGRAM DECLARATIONS

- */

-

-/* STATIC VARIABLE DECLARATIONS

- */

-

-/* EXPORTED SUBPROGRAM BODIES

- */

-

-/* LOCAL SUBPROGRAM BODIES

-*/

-void

-_air_vlan_readEntry(

-    const UI32_T unit,

-    const UI16_T vid,

-    AIR_VLAN_ENTRY_T* vlan_entry)

-{

-    UI32_T val = 0;

-    val = (0x80000000 + vid); //r_vid_cmd

-    aml_writeReg(unit, VTCR, val);

-

-    for (;;)

-    {

-        aml_readReg(unit, VTCR, &val);

-        if ((val & 0x80000000) == 0)

-            break;

-        AIR_UDELAY(10);

-    }

-

-    aml_readReg(unit, VLNRDATA0, &(vlan_entry->vlan_table.vlan_table0));

-    aml_readReg(unit, VLNRDATA1, &(vlan_entry->vlan_table.vlan_table1));

-}

-

-void

-_air_vlan_writeEntry(

-    const UI32_T unit,

-    const UI16_T vid,

-    AIR_VLAN_ENTRY_T* vlan_entry)

-{

-    UI32_T val = 0;

-

-    aml_writeReg(unit, VLNWDATA0, vlan_entry->vlan_table.vlan_table0);

-    aml_writeReg(unit, VLNWDATA1, vlan_entry->vlan_table.vlan_table1);

-    aml_writeReg(unit, VLNWDATA2, 0);

-    aml_writeReg(unit, VLNWDATA3, 0);

-    aml_writeReg(unit, VLNWDATA4, 0);

-

-    val = (0x80001000 + vid); //w_vid_cmd

-    aml_writeReg(unit, VTCR, val);

-

-    for (;;)

-    {

-        aml_readReg(unit, VTCR, &val);

-        if ((val & 0x80000000) == 0)

-            break;

-        AIR_UDELAY(10);

-    }

-}

-

-void

-_air_untagged_vlan_readEntry(

-    const UI32_T unit,

-    const UI16_T vid,

-    AIR_VLAN_ENTRY_ATTR_T* vlan_entry)

-{

-    UI32_T val = 0;

-    val = (0x80000000 + vid); //r_vid_cmd

-    aml_writeReg(unit, VTCR, val);

-

-    for (;;)

-    {

-        aml_readReg(unit, VTCR, &val);

-        if ((val & 0x80000000) == 0)

-            break;

-        AIR_UDELAY(10);

-    }

-

-    aml_readReg(unit, VLNRDATA0, &(vlan_entry->vlan_table.vlan_table0));

-    aml_readReg(unit, VLNRDATA1, &(vlan_entry->vlan_table.vlan_table1));

-    aml_readReg(unit, VLNRDATA2, &(vlan_entry->vlan_table.vlan_table2));

-    aml_readReg(unit, VLNRDATA3, &(vlan_entry->vlan_table.vlan_table3));

-    aml_readReg(unit, VLNRDATA4, &(vlan_entry->vlan_table.vlan_table4));

-}

-

-void

-_air_untagged_vlan_writeEntry(

-    const UI32_T unit,

-    const UI16_T vid,

-    AIR_VLAN_ENTRY_ATTR_T* vlan_entry)

-{

-    UI32_T val = 0;

-

-    aml_writeReg(unit, VLNWDATA0, vlan_entry->vlan_table.vlan_table0);

-    aml_writeReg(unit, VLNWDATA1, vlan_entry->vlan_table.vlan_table1);

-    aml_writeReg(unit, VLNWDATA2, vlan_entry->vlan_table.vlan_table2);

-    aml_writeReg(unit, VLNWDATA3, vlan_entry->vlan_table.vlan_table3);

-    aml_writeReg(unit, VLNWDATA4, vlan_entry->vlan_table.vlan_table4);

-

-    val = (0x80001000 + vid); //w_vid_cmd

-    aml_writeReg(unit, VTCR, val);

-

-    for (;;)

-    {

-        aml_readReg(unit, VTCR, &val);

-        if ((val & 0x80000000) == 0)

-            break;

-        AIR_UDELAY(10);

-    }

-}

-

-/* FUNCTION NAME:   air_vlan_create

- * PURPOSE:

- *      Create the vlan in the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      p_attr      -- vlan attr

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Vlan creation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_create(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    AIR_VLAN_ENTRY_ATTR_T *p_attr)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-

-    _air_vlan_readEntry(unit, vid, &vlan_entry);

-    if (vlan_entry.valid)

-        return AIR_E_ENTRY_EXISTS;

-

-    if (NULL != p_attr)

-    {

-        p_attr->valid = 1;

-        _air_untagged_vlan_writeEntry(unit, vid, p_attr);

-    }

-    else

-    {

-        memset(&vlan_entry, 0, sizeof(vlan_entry));

-        vlan_entry.valid = 1;

-        _air_untagged_vlan_writeEntry(unit, vid, &vlan_entry);

-    }

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_destroy

- * PURPOSE:

- *      Destroy the vlan in the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK     -- Successfully read the data.

- *      AIR_E_OTHERS -- Vlan destroy failed.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_destroy(

-    const UI32_T    unit,

-    const UI16_T    vid)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-

-    _air_vlan_writeEntry(unit, vid, &vlan_entry);

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_destroyAll

- * PURPOSE:

- *      Destroy the vlan in the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK     -- Successfully read the data.

- *      AIR_E_OTHERS -- Vlan destroy failed.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_destroyAll(

-    const UI32_T    unit,

-    const UI32_T    keep_and_restore_default_vlan)

-{

-    UI16_T vid = 0;

-

-    for (vid = AIR_VLAN_ID_MIN; vid <= AIR_VLAN_ID_MAX; vid++)

-    {

-        if (keep_and_restore_default_vlan)

-        {

-            air_vlan_reset(unit, vid);

-        }

-        else

-        {

-            air_vlan_destroy(unit, vid);

-        }

-    }

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_reset

- * PURPOSE:

- *      Destroy the vlan in the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK     -- Successfully reset the data.

- *      AIR_E_OTHERS -- Vlan reset failed.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_reset(

-    const UI32_T    unit,

-    const UI16_T    vid)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-

-    vlan_entry.vlan_entry_format.port_mem = AIR_ALL_PORT_BITMAP;

-    vlan_entry.valid = TRUE;

-

-    _air_vlan_writeEntry(unit, vid, &vlan_entry);

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_setFid

- * PURPOSE:

- *      Set the filter id of the vlan to the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      fid         -- filter id

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setFid(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const UI8_T     fid)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    /* VID check */

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-    AIR_PARAM_CHK((fid > AIR_FILTER_ID_MAX), AIR_E_BAD_PARAMETER);

-

-    _air_vlan_readEntry(unit, vid, &vlan_entry);

-    if (!vlan_entry.valid)

-        return AIR_E_ENTRY_NOT_FOUND;

-

-    vlan_entry.vlan_entry_format.fid = fid;

-    _air_vlan_writeEntry(unit, vid, &vlan_entry);

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_getFid

- * PURPOSE:

- *      Get the filter id of the vlan from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id to be created

- * OUTPUT:

- *      ptr_fid     -- filter id

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getFid(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    UI8_T           *ptr_fid)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-    AIR_CHECK_PTR(ptr_fid);

-

-    _air_vlan_readEntry(unit, vid, &vlan_entry);

-    if (!vlan_entry.valid)

-        return AIR_E_ENTRY_NOT_FOUND;

-

-    *ptr_fid = vlan_entry.vlan_entry_format.fid;

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_addMemberPort

- * PURPOSE:

- *      Add one vlan member to the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      port        -- port id

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_addMemberPort(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const UI32_T    port)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);

-

-    _air_vlan_readEntry(unit, vid, &vlan_entry);

-    if (!vlan_entry.valid)

-        return AIR_E_ENTRY_NOT_FOUND;

-

-    vlan_entry.vlan_entry_format.port_mem |= 1 << port;

-    _air_vlan_writeEntry(unit, vid, &vlan_entry);

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_delMemberPort

- * PURPOSE:

- *      Delete one vlan member from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      port        -- port id

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_delMemberPort(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const UI32_T    port)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);

-

-    _air_vlan_readEntry(unit, vid, &vlan_entry);

-    if (!vlan_entry.valid)

-        return AIR_E_ENTRY_NOT_FOUND;

-

-    vlan_entry.vlan_entry_format.port_mem &= ~(1 << port);

-    _air_vlan_writeEntry(unit, vid, &vlan_entry);

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_setMemberPort

- * PURPOSE:

- *      Replace the vlan members in the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      port_bitmap -- member port bitmap

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setMemberPort(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const UI32_T    port_bitmap)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-    AIR_PARAM_CHK((port_bitmap & (~AIR_ALL_PORT_BITMAP)), AIR_E_BAD_PARAMETER);

-

-    _air_vlan_readEntry(unit, vid, &vlan_entry);

-    if (!vlan_entry.valid)

-        return AIR_E_ENTRY_NOT_FOUND;

-

-    vlan_entry.vlan_entry_format.port_mem = port_bitmap;

-    _air_vlan_writeEntry(unit, vid, &vlan_entry);

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_getMemberPort

- * PURPOSE:

- *      Get the vlan members from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      port_bitmap -- member port bitmap

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getMemberPort(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    UI32_T          *ptr_port_bitmap)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-    AIR_CHECK_PTR(ptr_port_bitmap);

-

-    _air_vlan_readEntry(unit, vid, &vlan_entry);

-    if (!vlan_entry.valid)

-        return AIR_E_ENTRY_NOT_FOUND;

-

-    *ptr_port_bitmap = vlan_entry.vlan_entry_format.port_mem;

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_setIVL

- * PURPOSE:

- *      Set L2 lookup mode IVL/SVL for L2 traffic.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      enable      -- enable IVL

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setIVL(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const BOOL_T    enable)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-

-    _air_vlan_readEntry(unit, vid, &vlan_entry);

-    if (!vlan_entry.valid)

-        return AIR_E_ENTRY_NOT_FOUND;

-

-    vlan_entry.vlan_entry_format.ivl = enable ? 1 : 0;

-    _air_vlan_writeEntry(unit, vid, &vlan_entry);

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_getIVL

- * PURPOSE:

- *      Get L2 lookup mode IVL/SVL for L2 traffic.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      ptr_enable  -- enable IVL

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getIVL(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    BOOL_T          *ptr_enable)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-    AIR_CHECK_PTR(ptr_enable);

-

-    _air_vlan_readEntry(unit, vid, &vlan_entry);

-    if (!vlan_entry.valid)

-        return AIR_E_ENTRY_NOT_FOUND;

-

-    *ptr_enable = vlan_entry.vlan_entry_format.ivl;

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_setPortAcceptFrameType

- * PURPOSE:

- *      Set vlan accept frame type of the port from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- *      type        -- accept frame type

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setPortAcceptFrameType(

-    const UI32_T    unit,

-    const UI32_T    port,

-    const AIR_VLAN_ACCEPT_FRAME_TYPE_T type)

-{

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-    UI32_T val = 0;

-

-    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);

-    AIR_PARAM_CHK((type >= AIR_VLAN_ACCEPT_FRAME_TYPE_LAST), AIR_E_BAD_PARAMETER);

-

-    aml_readReg(unit, PVC(port), &val);

-    val &= ~PVC_ACC_FRM_MASK;

-    val |= (type & PVC_ACC_FRM_RELMASK) << PVC_ACC_FRM_OFFT;

-    aml_writeReg(unit, PVC(port), val);

-

-    return rc;

-}

-

-/* FUNCTION NAME:   air_vlan_getPortAcceptFrameType

- * PURPOSE:

- *      Get vlan accept frame type of the port from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- * OUTPUT:

- *      ptr_type    -- accept frame type

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getPortAcceptFrameType(

-    const UI32_T    unit,

-    const UI32_T    port,

-    AIR_VLAN_ACCEPT_FRAME_TYPE_T *ptr_type)

-{

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-    UI32_T val = 0;

-

-    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);

-    AIR_CHECK_PTR(ptr_type);

-

-    aml_readReg(unit, PVC(port), &val);

-    *ptr_type = (val >> PVC_ACC_FRM_OFFT) & PVC_ACC_FRM_RELMASK;

-

-    return rc;

-}

-

-/* FUNCTION NAME:   air_vlan_setPortLeakyVlanEnable

- * PURPOSE:

- *      Set leaky vlan enable of the port from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- *      pkt_type    -- packet type

- *      enable      -- enable leaky

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setPortLeakyVlanEnable(

-    const UI32_T    unit,

-    const UI32_T    port,

-    AIR_LEAKY_PKT_TYPE_T   pkt_type,

-    const BOOL_T    enable)

-{

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-    UI32_T val = 0;

-

-    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);

-    AIR_PARAM_CHK((pkt_type >= AIR_LEAKY_PKT_TYPE_LAST), AIR_E_BAD_PARAMETER);

-

-    aml_readReg(unit, PVC(port), &val);

-

-    if (pkt_type == AIR_LEAKY_PKT_TYPE_UNICAST)

-    {

-        if (enable)

-        {

-            val |= PVC_UC_LKYV_EN_MASK;

-        }

-        else

-        {

-            val &= ~PVC_UC_LKYV_EN_MASK;

-        }

-    }

-    else if (pkt_type == AIR_LEAKY_PKT_TYPE_MULTICAST)

-    {

-        if (enable)

-        {

-            val |= PVC_MC_LKYV_EN_MASK;

-        }

-        else

-        {

-            val &= ~PVC_MC_LKYV_EN_MASK;

-        }

-    }

-    else

-    {

-        if (enable)

-        {

-            val |= PVC_BC_LKYV_EN_MASK;

-        }

-        else

-        {

-            val &= ~PVC_BC_LKYV_EN_MASK;

-        }

-    }

-

-    aml_writeReg(unit, PVC(port), val);

-

-    return rc;

-}

-

-/* FUNCTION NAME:   air_vlan_getPortLeakyVlanEnable

- * PURPOSE:

- *      Get leaky vlan enable of the port from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- *      pkt_type    -- packet type

- * OUTPUT:

- *      ptr_enable  -- enable leaky

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getPortLeakyVlanEnable(

-    const UI32_T    unit,

-    const UI32_T    port,

-    AIR_LEAKY_PKT_TYPE_T   pkt_type,

-    BOOL_T          *ptr_enable)

-{

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-    UI32_T val = 0;

-

-    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);

-    AIR_PARAM_CHK((pkt_type >= AIR_LEAKY_PKT_TYPE_LAST), AIR_E_BAD_PARAMETER);

-    AIR_CHECK_PTR(ptr_enable);

-

-    aml_readReg(unit, PVC(port), &val);

-

-    if (pkt_type == AIR_LEAKY_PKT_TYPE_UNICAST)

-    {

-        *ptr_enable = val & PVC_UC_LKYV_EN_MASK ? TRUE : FALSE;

-    }

-    else if (pkt_type == AIR_LEAKY_PKT_TYPE_MULTICAST)

-    {

-        *ptr_enable = val & PVC_MC_LKYV_EN_MASK ? TRUE : FALSE;

-    }

-    else

-    {

-        *ptr_enable = val & PVC_BC_LKYV_EN_MASK ? TRUE : FALSE;

-    }

-

-    return rc;

-}

-

-/* FUNCTION NAME:   air_vlan_setPortAttr

- * PURPOSE:

- *      Set vlan port attribute from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- *      attr        -- vlan port attr

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setPortAttr(

-    const UI32_T    unit,

-    const UI32_T    port,

-    const AIR_VLAN_PORT_ATTR_T attr)

-{

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-    UI32_T val = 0;

-

-    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);

-    AIR_PARAM_CHK((attr >= AIR_VLAN_PORT_ATTR_LAST), AIR_E_BAD_PARAMETER);

-

-    aml_readReg(unit, PVC(port), &val);

-    val &= ~PVC_VLAN_ATTR_MASK;

-    val |= (attr & PVC_VLAN_ATTR_RELMASK) << PVC_VLAN_ATTR_OFFT;

-    aml_writeReg(unit, PVC(port), val);

-

-    return rc;

-}

-

-/* FUNCTION NAME:   air_vlan_getPortAttr

- * PURPOSE:

- *      Get vlan port attribute from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- * OUTPUT:

- *      ptr_attr    -- vlan port attr

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getPortAttr(

-    const UI32_T    unit,

-    const UI32_T    port,

-    AIR_VLAN_PORT_ATTR_T *ptr_attr)

-{

-    AIR_ERROR_NO_T rc = AIR_E_OK;

-    UI32_T val = 0;

-

-    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);

-    AIR_CHECK_PTR(ptr_attr);

-

-    aml_readReg(unit, PVC(port), &val);

-    *ptr_attr = (val >> PVC_VLAN_ATTR_OFFT) & PVC_VLAN_ATTR_RELMASK;

-

-    return rc;

-}

-

-/* FUNCTION NAME:   air_vlan_setIgrPortTagAttr

- * PURPOSE:

- *      Set vlan incoming port egress tag attribute from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- *      attr        -- egress tag attr

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setIgrPortTagAttr(

-    const UI32_T    unit,

-    const UI32_T    port,

-    const AIR_IGR_PORT_EG_TAG_ATTR_T attr)

-{

-    UI32_T val = 0;

-

-    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);

-    AIR_PARAM_CHK((attr >= AIR_IGR_PORT_EG_TAG_ATTR_LAST), AIR_E_BAD_PARAMETER);

-

-    aml_readReg(unit, PVC(port), &val);

-    val &= ~PVC_EG_TAG_MASK;

-    val |= (attr & PVC_EG_TAG_RELMASK) << PVC_EG_TAG_OFFT;

-    aml_writeReg(unit, PVC(port), val);

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_getIgrPortTagAttr

- * PURPOSE:

- *      Get vlan incoming port egress tag attribute from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- * OUTPUT:

- *      ptr_attr    -- egress tag attr

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getIgrPortTagAttr(

-    const UI32_T    unit,

-    const UI32_T    port,

-    AIR_IGR_PORT_EG_TAG_ATTR_T *ptr_attr)

-{

-    UI32_T val = 0;

-

-    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);

-    AIR_CHECK_PTR(ptr_attr);

-

-    aml_readReg(unit, PVC(port), &val);

-    *ptr_attr = (val >> PVC_EG_TAG_OFFT) & PVC_EG_TAG_RELMASK;

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_setPortEgsTagAttr

- * PURPOSE:

- *      Set vlan port egress tag attribute from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- *      attr        -- egress tag attr

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setPortEgsTagAttr(

-    const UI32_T    unit,

-    const UI32_T    port,

-    const AIR_PORT_EGS_TAG_ATTR_T attr)

-{

-    UI32_T val = 0;

-

-    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);

-    AIR_PARAM_CHK((attr >= AIR_PORT_EGS_TAG_ATTR_LAST), AIR_E_BAD_PARAMETER);

-

-    aml_readReg(unit, PCR(port), &val);

-    val &= ~PCR_EG_TAG_MASK;

-    val |= (attr & PCR_EG_TAG_RELMASK) << PCR_EG_TAG_OFFT;

-    aml_writeReg(unit, PCR(port), val);

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_getPortEgsTagAttr

- * PURPOSE:

- *      Get vlan port egress tag attribute from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- * OUTPUT:

- *      ptr_attr    -- egress tag attr

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getPortEgsTagAttr(

-    const UI32_T    unit,

-    const UI32_T    port,

-    AIR_PORT_EGS_TAG_ATTR_T *ptr_attr)

-{

-    UI32_T val = 0;

-

-    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);

-    AIR_CHECK_PTR(ptr_attr);

-

-    aml_readReg(unit, PCR(port), &val);

-    *ptr_attr = (val >> PCR_EG_TAG_OFFT) & PCR_EG_TAG_RELMASK;

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_setPortOuterTPID

- * PURPOSE:

- *      Set stack tag TPID of the port from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- *      tpid        -- TPID

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setPortOuterTPID(

-    const UI32_T    unit,

-    const UI32_T    port,

-    const UI16_T    tpid)

-{

-    UI32_T val = 0;

-

-    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);

-

-    aml_readReg(unit, PVC(port), &val);

-    val &= ~PVC_STAG_VPID_MASK;

-    val |= (tpid & PVC_STAG_VPID_RELMASK) << PVC_STAG_VPID_OFFT;

-    aml_writeReg(unit, PVC(port), val);

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_getPortOuterTPID

- * PURPOSE:

- *      Get stack tag TPID of the port from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- * OUTPUT:

- *      ptr_tpid    -- TPID

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getPortOuterTPID(

-    const UI32_T    unit,

-    const UI32_T    port,

-    UI16_T          *ptr_tpid)

-{

-    UI32_T val = 0;

-

-    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);

-    AIR_CHECK_PTR(ptr_tpid);

-

-    aml_readReg(unit, PVC(port), &val);

-    *ptr_tpid = (val >> PVC_STAG_VPID_OFFT) & PVC_STAG_VPID_RELMASK;

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_setPortPVID

- * PURPOSE:

- *      Set PVID of the port from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- *      pvid        -- native vlan id

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setPortPVID(

-    const UI32_T    unit,

-    const UI32_T    port,

-    const UI16_T    pvid)

-{

-    UI32_T val = 0;

-

-    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);

-    AIR_PARAM_CHK((pvid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-

-    aml_readReg(unit, PVID(port), &val);

-    val &= ~PVID_PCVID_MASK;

-    val |= (pvid & PVID_PCVID_RELMASK) << PVID_PCVID_OFFT;

-    aml_writeReg(unit, PVID(port), val);

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_getPortPVID

- * PURPOSE:

- *      Get PVID of the port from the specified device.

- * INPUT:

- *      unit        -- unit id

- *      port        -- port id

- * OUTPUT:

- *      ptr_pvid    -- native vlan id

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getPortPVID(

-    const UI32_T    unit,

-    const UI32_T    port,

-    UI16_T          *ptr_pvid)

-{

-    UI32_T val = 0;

-

-    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);

-    AIR_CHECK_PTR(ptr_pvid);

-

-    aml_readReg(unit, PVID(port), &val);

-    *ptr_pvid = (val >> PVID_PCVID_OFFT) & PVID_PCVID_RELMASK;

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_setServiceTag

- * PURPOSE:

- *      Set Vlan service tag.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      stag        -- service stag

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setServiceTag(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const UI16_T    stag)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-    AIR_PARAM_CHK((stag > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-

-    _air_vlan_readEntry(unit, vid, &vlan_entry);

-    if (!vlan_entry.valid)

-        return AIR_E_ENTRY_NOT_FOUND;

-

-    vlan_entry.vlan_entry_format.stag = stag;

-    _air_vlan_writeEntry(unit, vid, &vlan_entry);

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_getServiceTag

- * PURPOSE:

- *      Get Vlan service tag.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      ptr_stag    -- service stag

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getServiceTag(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    UI16_T          *ptr_stag)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-    AIR_CHECK_PTR(ptr_stag);

-

-    _air_vlan_readEntry(unit, vid, &vlan_entry);

-    if (!vlan_entry.valid)

-        return AIR_E_ENTRY_NOT_FOUND;

-

-    *ptr_stag = vlan_entry.vlan_entry_format.stag;

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_setEgsTagCtlEnable

- * PURPOSE:

- *      Set per vlan egress tag control.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      enable      -- enable vlan egress tag control

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setEgsTagCtlEnable(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const BOOL_T    enable)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-

-    _air_vlan_readEntry(unit, vid, &vlan_entry);

-    if (!vlan_entry.valid)

-        return AIR_E_ENTRY_NOT_FOUND;

-

-    vlan_entry.vlan_entry_format.eg_ctrl_en = enable ? 1 : 0;

-    _air_vlan_writeEntry(unit, vid, &vlan_entry);

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_getEgsTagCtlEnable

- * PURPOSE:

- *      Get per vlan egress tag control.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      ptr_enable  -- enable vlan egress tag control

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getEgsTagCtlEnable(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    BOOL_T          *ptr_enable)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-    AIR_CHECK_PTR(ptr_enable);

-

-    _air_vlan_readEntry(unit, vid, &vlan_entry);

-    if (!vlan_entry.valid)

-        return AIR_E_ENTRY_NOT_FOUND;

-

-    *ptr_enable = vlan_entry.vlan_entry_format.eg_ctrl_en;

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_setEgsTagConsistent

- * PURPOSE:

- *      Set per vlan egress tag consistent.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      enable      -- enable vlan egress tag consistent

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setEgsTagConsistent(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const BOOL_T    enable)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-

-    _air_vlan_readEntry(unit, vid, &vlan_entry);

-    if (!vlan_entry.valid)

-        return AIR_E_ENTRY_NOT_FOUND;

-

-    vlan_entry.vlan_entry_format.eg_con = enable;

-    _air_vlan_writeEntry(unit, vid, &vlan_entry);

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_getEgsTagConsistent

- * PURPOSE:

- *      Get per vlan egress tag consistent.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      ptr_enable  -- enable vlan egress tag consistent

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getEgsTagConsistent(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    BOOL_T          *ptr_enable)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-    AIR_CHECK_PTR(ptr_enable);

-

-    _air_vlan_readEntry(unit, vid, &vlan_entry);

-    if (!vlan_entry.valid)

-        return AIR_E_ENTRY_NOT_FOUND;

-

-    *ptr_enable = vlan_entry.vlan_entry_format.eg_con;

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_setPortBasedStag

- * PURPOSE:

- *      Set vlan port based stag enable.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      enable      -- vlan port based stag enable

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setPortBasedStag(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const BOOL_T    enable)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-

-    _air_vlan_readEntry(unit, vid, &vlan_entry);

-    if (!vlan_entry.valid)

-        return AIR_E_ENTRY_NOT_FOUND;

-

-    vlan_entry.vlan_entry_format.port_stag = enable;

-    _air_vlan_writeEntry(unit, vid, &vlan_entry);

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_getPortBasedStag

- * PURPOSE:

- *      Get vlan port based stag enable.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      ptr_enable  -- vlan port based stag enable

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getPortBasedStag(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    BOOL_T          *ptr_enable)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-    AIR_CHECK_PTR(ptr_enable);

-

-    _air_vlan_readEntry(unit, vid, &vlan_entry);

-    if (!vlan_entry.valid)

-        return AIR_E_ENTRY_NOT_FOUND;

-

-    *ptr_enable = vlan_entry.vlan_entry_format.port_stag;

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_setPortEgsTagCtl

- * PURPOSE:

- *      Set vlan port egress tag control.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- *      port        -- port id

- *      tag_ctl     -- egress tag control

- * OUTPUT:

- *      None

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_setPortEgsTagCtl(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const UI32_T    port,

-    const AIR_VLAN_PORT_EGS_TAG_CTL_TYPE_T    tag_ctl)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);

-    AIR_PARAM_CHK((tag_ctl >= AIR_PORT_EGS_TAG_ATTR_LAST), AIR_E_BAD_PARAMETER);

-

-    _air_vlan_readEntry(unit, vid, &vlan_entry);

-    if (!vlan_entry.valid)

-        return AIR_E_ENTRY_NOT_FOUND;

-

-    vlan_entry.vlan_entry_format.eg_ctrl &= ~(0x3 << (port * 2));

-    vlan_entry.vlan_entry_format.eg_ctrl |= (tag_ctl & 0x3) << (port * 2);

-    _air_vlan_writeEntry(unit, vid, &vlan_entry);

-

-    return AIR_E_OK;

-}

-

-/* FUNCTION NAME:   air_vlan_getPortEgsTagCtl

- * PURPOSE:

- *      Get vlan port egress tag control.

- * INPUT:

- *      unit        -- unit id

- *      vid         -- vlan id

- * OUTPUT:

- *      ptr_tag_ctl -- egress tag control

- * RETURN:

- *      AIR_E_OK                -- Successfully read the data.

- *      AIR_E_OTHERS            -- Operation failed.

- *      AIR_E_BAD_PARAMETER     -- Invalid parameter.

- * NOTES:

- *      none

- */

-AIR_ERROR_NO_T

-air_vlan_getPortEgsTagCtl(

-    const UI32_T    unit,

-    const UI16_T    vid,

-    const UI32_T    port,

-    AIR_VLAN_PORT_EGS_TAG_CTL_TYPE_T   *ptr_tag_ctl)

-{

-    AIR_VLAN_ENTRY_T vlan_entry = {0};

-

-    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);

-    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);

-    AIR_CHECK_PTR(ptr_tag_ctl);

-

-    _air_vlan_readEntry(unit, vid, &vlan_entry);

-    if (!vlan_entry.valid)

-        return AIR_E_ENTRY_NOT_FOUND;

-

-    *ptr_tag_ctl = (vlan_entry.vlan_entry_format.eg_ctrl >> (port * 2)) & 0x3;

-

-    return AIR_E_OK;

-}

+/* FILE NAME:   air_vlan.c
+ * PURPOSE:
+ *      Define the VLAN function in AIR SDK.
+ * NOTES:
+ */
+
+/* INCLUDE FILE DECLARATIONS
+ */
+#include "air.h"
+
+/* NAMING CONSTANT DECLARATIONS
+ */
+
+/* MACRO FUNCTION DECLARATIONS
+ */
+
+/* DATA TYPE DECLARATIONS
+ */
+
+/* GLOBAL VARIABLE DECLARATIONS
+ */
+
+/* LOCAL SUBPROGRAM DECLARATIONS
+ */
+
+/* STATIC VARIABLE DECLARATIONS
+ */
+
+/* EXPORTED SUBPROGRAM BODIES
+ */
+
+/* LOCAL SUBPROGRAM BODIES
+*/
+void
+_air_vlan_readEntry(
+    const UI32_T unit,
+    const UI16_T vid,
+    AIR_VLAN_ENTRY_T* vlan_entry)
+{
+    UI32_T val = 0;
+    val = (0x80000000 + vid); //r_vid_cmd
+    aml_writeReg(unit, VTCR, val);
+
+    for (;;)
+    {
+        aml_readReg(unit, VTCR, &val);
+        if ((val & 0x80000000) == 0)
+            break;
+        AIR_UDELAY(10);
+    }
+
+    aml_readReg(unit, VLNRDATA0, &(vlan_entry->vlan_table.vlan_table0));
+    aml_readReg(unit, VLNRDATA1, &(vlan_entry->vlan_table.vlan_table1));
+}
+
+void
+_air_vlan_writeEntry(
+    const UI32_T unit,
+    const UI16_T vid,
+    AIR_VLAN_ENTRY_T* vlan_entry)
+{
+    UI32_T val = 0;
+
+    aml_writeReg(unit, VLNWDATA0, vlan_entry->vlan_table.vlan_table0);
+    aml_writeReg(unit, VLNWDATA1, vlan_entry->vlan_table.vlan_table1);
+    aml_writeReg(unit, VLNWDATA2, 0);
+    aml_writeReg(unit, VLNWDATA3, 0);
+    aml_writeReg(unit, VLNWDATA4, 0);
+
+    val = (0x80001000 + vid); //w_vid_cmd
+    aml_writeReg(unit, VTCR, val);
+
+    for (;;)
+    {
+        aml_readReg(unit, VTCR, &val);
+        if ((val & 0x80000000) == 0)
+            break;
+        AIR_UDELAY(10);
+    }
+}
+
+void
+_air_untagged_vlan_readEntry(
+    const UI32_T unit,
+    const UI16_T vid,
+    AIR_VLAN_ENTRY_ATTR_T* vlan_entry)
+{
+    UI32_T val = 0;
+    val = (0x80000000 + vid); //r_vid_cmd
+    aml_writeReg(unit, VTCR, val);
+
+    for (;;)
+    {
+        aml_readReg(unit, VTCR, &val);
+        if ((val & 0x80000000) == 0)
+            break;
+        AIR_UDELAY(10);
+    }
+
+    aml_readReg(unit, VLNRDATA0, &(vlan_entry->vlan_table.vlan_table0));
+    aml_readReg(unit, VLNRDATA1, &(vlan_entry->vlan_table.vlan_table1));
+    aml_readReg(unit, VLNRDATA2, &(vlan_entry->vlan_table.vlan_table2));
+    aml_readReg(unit, VLNRDATA3, &(vlan_entry->vlan_table.vlan_table3));
+    aml_readReg(unit, VLNRDATA4, &(vlan_entry->vlan_table.vlan_table4));
+}
+
+void
+_air_untagged_vlan_writeEntry(
+    const UI32_T unit,
+    const UI16_T vid,
+    AIR_VLAN_ENTRY_ATTR_T* vlan_entry)
+{
+    UI32_T val = 0;
+
+    aml_writeReg(unit, VLNWDATA0, vlan_entry->vlan_table.vlan_table0);
+    aml_writeReg(unit, VLNWDATA1, vlan_entry->vlan_table.vlan_table1);
+    aml_writeReg(unit, VLNWDATA2, vlan_entry->vlan_table.vlan_table2);
+    aml_writeReg(unit, VLNWDATA3, vlan_entry->vlan_table.vlan_table3);
+    aml_writeReg(unit, VLNWDATA4, vlan_entry->vlan_table.vlan_table4);
+
+    val = (0x80001000 + vid); //w_vid_cmd
+    aml_writeReg(unit, VTCR, val);
+
+    for (;;)
+    {
+        aml_readReg(unit, VTCR, &val);
+        if ((val & 0x80000000) == 0)
+            break;
+        AIR_UDELAY(10);
+    }
+}
+
+/* FUNCTION NAME:   air_vlan_create
+ * PURPOSE:
+ *      Create the vlan in the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      p_attr      -- vlan attr
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Vlan creation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_create(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    AIR_VLAN_ENTRY_ATTR_T *p_attr)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+    AIR_VLAN_ENTRY_ATTR_T vlan_attr_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+
+    _air_vlan_readEntry(unit, vid, &vlan_entry);
+    if (vlan_entry.valid)
+        return AIR_E_ENTRY_EXISTS;
+
+    if (NULL != p_attr)
+    {
+        p_attr->valid = 1;
+        _air_untagged_vlan_writeEntry(unit, vid, p_attr);
+    }
+    else
+    {
+        memset(&vlan_attr_entry, 0, sizeof(vlan_attr_entry));
+        vlan_attr_entry.valid = 1;
+        _air_untagged_vlan_writeEntry(unit, vid, &vlan_attr_entry);
+    }
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_destroy
+ * PURPOSE:
+ *      Destroy the vlan in the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK     -- Successfully read the data.
+ *      AIR_E_OTHERS -- Vlan destroy failed.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_destroy(
+    const UI32_T    unit,
+    const UI16_T    vid)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+
+    _air_vlan_writeEntry(unit, vid, &vlan_entry);
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_destroyAll
+ * PURPOSE:
+ *      Destroy the vlan in the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK     -- Successfully read the data.
+ *      AIR_E_OTHERS -- Vlan destroy failed.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_destroyAll(
+    const UI32_T    unit,
+    const UI32_T    keep_and_restore_default_vlan)
+{
+    UI16_T vid = 0;
+
+    for (vid = AIR_VLAN_ID_MIN; vid <= AIR_VLAN_ID_MAX; vid++)
+    {
+        if (keep_and_restore_default_vlan)
+        {
+            air_vlan_reset(unit, vid);
+        }
+        else
+        {
+            air_vlan_destroy(unit, vid);
+        }
+    }
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_reset
+ * PURPOSE:
+ *      Destroy the vlan in the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK     -- Successfully reset the data.
+ *      AIR_E_OTHERS -- Vlan reset failed.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_reset(
+    const UI32_T    unit,
+    const UI16_T    vid)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+
+    vlan_entry.vlan_entry_format.port_mem = AIR_ALL_PORT_BITMAP;
+    vlan_entry.valid = TRUE;
+
+    _air_vlan_writeEntry(unit, vid, &vlan_entry);
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_setFid
+ * PURPOSE:
+ *      Set the filter id of the vlan to the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      fid         -- filter id
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setFid(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const UI8_T     fid)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    /* VID check */
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+    AIR_PARAM_CHK((fid > AIR_FILTER_ID_MAX), AIR_E_BAD_PARAMETER);
+
+    _air_vlan_readEntry(unit, vid, &vlan_entry);
+    if (!vlan_entry.valid)
+        return AIR_E_ENTRY_NOT_FOUND;
+
+    vlan_entry.vlan_entry_format.fid = fid;
+    _air_vlan_writeEntry(unit, vid, &vlan_entry);
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_getFid
+ * PURPOSE:
+ *      Get the filter id of the vlan from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id to be created
+ * OUTPUT:
+ *      ptr_fid     -- filter id
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getFid(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    UI8_T           *ptr_fid)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+    AIR_CHECK_PTR(ptr_fid);
+
+    _air_vlan_readEntry(unit, vid, &vlan_entry);
+    if (!vlan_entry.valid)
+        return AIR_E_ENTRY_NOT_FOUND;
+
+    *ptr_fid = vlan_entry.vlan_entry_format.fid;
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_addMemberPort
+ * PURPOSE:
+ *      Add one vlan member to the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      port        -- port id
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_addMemberPort(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const UI32_T    port)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);
+
+    _air_vlan_readEntry(unit, vid, &vlan_entry);
+    if (!vlan_entry.valid)
+        return AIR_E_ENTRY_NOT_FOUND;
+
+    vlan_entry.vlan_entry_format.port_mem |= 1 << port;
+    _air_vlan_writeEntry(unit, vid, &vlan_entry);
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_delMemberPort
+ * PURPOSE:
+ *      Delete one vlan member from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      port        -- port id
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_delMemberPort(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const UI32_T    port)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);
+
+    _air_vlan_readEntry(unit, vid, &vlan_entry);
+    if (!vlan_entry.valid)
+        return AIR_E_ENTRY_NOT_FOUND;
+
+    vlan_entry.vlan_entry_format.port_mem &= ~(1 << port);
+    _air_vlan_writeEntry(unit, vid, &vlan_entry);
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_setMemberPort
+ * PURPOSE:
+ *      Replace the vlan members in the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      port_bitmap -- member port bitmap
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setMemberPort(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const UI32_T    port_bitmap)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+    AIR_PARAM_CHK((port_bitmap & (~AIR_ALL_PORT_BITMAP)), AIR_E_BAD_PARAMETER);
+
+    _air_vlan_readEntry(unit, vid, &vlan_entry);
+    if (!vlan_entry.valid)
+        return AIR_E_ENTRY_NOT_FOUND;
+
+    vlan_entry.vlan_entry_format.port_mem = port_bitmap;
+    _air_vlan_writeEntry(unit, vid, &vlan_entry);
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_getMemberPort
+ * PURPOSE:
+ *      Get the vlan members from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      port_bitmap -- member port bitmap
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getMemberPort(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    UI32_T          *ptr_port_bitmap)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+    AIR_CHECK_PTR(ptr_port_bitmap);
+
+    _air_vlan_readEntry(unit, vid, &vlan_entry);
+    if (!vlan_entry.valid)
+        return AIR_E_ENTRY_NOT_FOUND;
+
+    *ptr_port_bitmap = vlan_entry.vlan_entry_format.port_mem;
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_setIVL
+ * PURPOSE:
+ *      Set L2 lookup mode IVL/SVL for L2 traffic.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      enable      -- enable IVL
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setIVL(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const BOOL_T    enable)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+
+    _air_vlan_readEntry(unit, vid, &vlan_entry);
+    if (!vlan_entry.valid)
+        return AIR_E_ENTRY_NOT_FOUND;
+
+    vlan_entry.vlan_entry_format.ivl = enable ? 1 : 0;
+    _air_vlan_writeEntry(unit, vid, &vlan_entry);
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_getIVL
+ * PURPOSE:
+ *      Get L2 lookup mode IVL/SVL for L2 traffic.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      ptr_enable  -- enable IVL
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getIVL(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    BOOL_T          *ptr_enable)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+    AIR_CHECK_PTR(ptr_enable);
+
+    _air_vlan_readEntry(unit, vid, &vlan_entry);
+    if (!vlan_entry.valid)
+        return AIR_E_ENTRY_NOT_FOUND;
+
+    *ptr_enable = vlan_entry.vlan_entry_format.ivl;
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_setPortAcceptFrameType
+ * PURPOSE:
+ *      Set vlan accept frame type of the port from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ *      type        -- accept frame type
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setPortAcceptFrameType(
+    const UI32_T    unit,
+    const UI32_T    port,
+    const AIR_VLAN_ACCEPT_FRAME_TYPE_T type)
+{
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+    UI32_T val = 0;
+
+    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);
+    AIR_PARAM_CHK((type >= AIR_VLAN_ACCEPT_FRAME_TYPE_LAST), AIR_E_BAD_PARAMETER);
+
+    aml_readReg(unit, PVC(port), &val);
+    val &= ~PVC_ACC_FRM_MASK;
+    val |= (type & PVC_ACC_FRM_RELMASK) << PVC_ACC_FRM_OFFT;
+    aml_writeReg(unit, PVC(port), val);
+
+    return rc;
+}
+
+/* FUNCTION NAME:   air_vlan_getPortAcceptFrameType
+ * PURPOSE:
+ *      Get vlan accept frame type of the port from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ * OUTPUT:
+ *      ptr_type    -- accept frame type
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getPortAcceptFrameType(
+    const UI32_T    unit,
+    const UI32_T    port,
+    AIR_VLAN_ACCEPT_FRAME_TYPE_T *ptr_type)
+{
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+    UI32_T val = 0;
+
+    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);
+    AIR_CHECK_PTR(ptr_type);
+
+    aml_readReg(unit, PVC(port), &val);
+    *ptr_type = (val >> PVC_ACC_FRM_OFFT) & PVC_ACC_FRM_RELMASK;
+
+    return rc;
+}
+
+/* FUNCTION NAME:   air_vlan_setPortLeakyVlanEnable
+ * PURPOSE:
+ *      Set leaky vlan enable of the port from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ *      pkt_type    -- packet type
+ *      enable      -- enable leaky
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setPortLeakyVlanEnable(
+    const UI32_T    unit,
+    const UI32_T    port,
+    AIR_LEAKY_PKT_TYPE_T   pkt_type,
+    const BOOL_T    enable)
+{
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+    UI32_T val = 0;
+
+    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);
+    AIR_PARAM_CHK((pkt_type >= AIR_LEAKY_PKT_TYPE_LAST), AIR_E_BAD_PARAMETER);
+
+    aml_readReg(unit, PVC(port), &val);
+
+    if (pkt_type == AIR_LEAKY_PKT_TYPE_UNICAST)
+    {
+        if (enable)
+        {
+            val |= PVC_UC_LKYV_EN_MASK;
+        }
+        else
+        {
+            val &= ~PVC_UC_LKYV_EN_MASK;
+        }
+    }
+    else if (pkt_type == AIR_LEAKY_PKT_TYPE_MULTICAST)
+    {
+        if (enable)
+        {
+            val |= PVC_MC_LKYV_EN_MASK;
+        }
+        else
+        {
+            val &= ~PVC_MC_LKYV_EN_MASK;
+        }
+    }
+    else
+    {
+        if (enable)
+        {
+            val |= PVC_BC_LKYV_EN_MASK;
+        }
+        else
+        {
+            val &= ~PVC_BC_LKYV_EN_MASK;
+        }
+    }
+
+    aml_writeReg(unit, PVC(port), val);
+
+    return rc;
+}
+
+/* FUNCTION NAME:   air_vlan_getPortLeakyVlanEnable
+ * PURPOSE:
+ *      Get leaky vlan enable of the port from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ *      pkt_type    -- packet type
+ * OUTPUT:
+ *      ptr_enable  -- enable leaky
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getPortLeakyVlanEnable(
+    const UI32_T    unit,
+    const UI32_T    port,
+    AIR_LEAKY_PKT_TYPE_T   pkt_type,
+    BOOL_T          *ptr_enable)
+{
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+    UI32_T val = 0;
+
+    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);
+    AIR_PARAM_CHK((pkt_type >= AIR_LEAKY_PKT_TYPE_LAST), AIR_E_BAD_PARAMETER);
+    AIR_CHECK_PTR(ptr_enable);
+
+    aml_readReg(unit, PVC(port), &val);
+
+    if (pkt_type == AIR_LEAKY_PKT_TYPE_UNICAST)
+    {
+        *ptr_enable = val & PVC_UC_LKYV_EN_MASK ? TRUE : FALSE;
+    }
+    else if (pkt_type == AIR_LEAKY_PKT_TYPE_MULTICAST)
+    {
+        *ptr_enable = val & PVC_MC_LKYV_EN_MASK ? TRUE : FALSE;
+    }
+    else
+    {
+        *ptr_enable = val & PVC_BC_LKYV_EN_MASK ? TRUE : FALSE;
+    }
+
+    return rc;
+}
+
+/* FUNCTION NAME:   air_vlan_setPortAttr
+ * PURPOSE:
+ *      Set vlan port attribute from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ *      attr        -- vlan port attr
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setPortAttr(
+    const UI32_T    unit,
+    const UI32_T    port,
+    const AIR_VLAN_PORT_ATTR_T attr)
+{
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+    UI32_T val = 0;
+
+    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);
+    AIR_PARAM_CHK((attr >= AIR_VLAN_PORT_ATTR_LAST), AIR_E_BAD_PARAMETER);
+
+    aml_readReg(unit, PVC(port), &val);
+    val &= ~PVC_VLAN_ATTR_MASK;
+    val |= (attr & PVC_VLAN_ATTR_RELMASK) << PVC_VLAN_ATTR_OFFT;
+    aml_writeReg(unit, PVC(port), val);
+
+    return rc;
+}
+
+/* FUNCTION NAME:   air_vlan_getPortAttr
+ * PURPOSE:
+ *      Get vlan port attribute from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ * OUTPUT:
+ *      ptr_attr    -- vlan port attr
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getPortAttr(
+    const UI32_T    unit,
+    const UI32_T    port,
+    AIR_VLAN_PORT_ATTR_T *ptr_attr)
+{
+    AIR_ERROR_NO_T rc = AIR_E_OK;
+    UI32_T val = 0;
+
+    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);
+    AIR_CHECK_PTR(ptr_attr);
+
+    aml_readReg(unit, PVC(port), &val);
+    *ptr_attr = (val >> PVC_VLAN_ATTR_OFFT) & PVC_VLAN_ATTR_RELMASK;
+
+    return rc;
+}
+
+/* FUNCTION NAME:   air_vlan_setIgrPortTagAttr
+ * PURPOSE:
+ *      Set vlan incoming port egress tag attribute from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ *      attr        -- egress tag attr
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setIgrPortTagAttr(
+    const UI32_T    unit,
+    const UI32_T    port,
+    const AIR_IGR_PORT_EG_TAG_ATTR_T attr)
+{
+    UI32_T val = 0;
+
+    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);
+    AIR_PARAM_CHK((attr >= AIR_IGR_PORT_EG_TAG_ATTR_LAST), AIR_E_BAD_PARAMETER);
+
+    aml_readReg(unit, PVC(port), &val);
+    val &= ~PVC_EG_TAG_MASK;
+    val |= (attr & PVC_EG_TAG_RELMASK) << PVC_EG_TAG_OFFT;
+    aml_writeReg(unit, PVC(port), val);
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_getIgrPortTagAttr
+ * PURPOSE:
+ *      Get vlan incoming port egress tag attribute from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ * OUTPUT:
+ *      ptr_attr    -- egress tag attr
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getIgrPortTagAttr(
+    const UI32_T    unit,
+    const UI32_T    port,
+    AIR_IGR_PORT_EG_TAG_ATTR_T *ptr_attr)
+{
+    UI32_T val = 0;
+
+    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);
+    AIR_CHECK_PTR(ptr_attr);
+
+    aml_readReg(unit, PVC(port), &val);
+    *ptr_attr = (val >> PVC_EG_TAG_OFFT) & PVC_EG_TAG_RELMASK;
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_setPortEgsTagAttr
+ * PURPOSE:
+ *      Set vlan port egress tag attribute from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ *      attr        -- egress tag attr
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setPortEgsTagAttr(
+    const UI32_T    unit,
+    const UI32_T    port,
+    const AIR_PORT_EGS_TAG_ATTR_T attr)
+{
+    UI32_T val = 0;
+
+    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);
+    AIR_PARAM_CHK((attr >= AIR_PORT_EGS_TAG_ATTR_LAST), AIR_E_BAD_PARAMETER);
+
+    aml_readReg(unit, PCR(port), &val);
+    val &= ~PCR_EG_TAG_MASK;
+    val |= (attr & PCR_EG_TAG_RELMASK) << PCR_EG_TAG_OFFT;
+    aml_writeReg(unit, PCR(port), val);
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_getPortEgsTagAttr
+ * PURPOSE:
+ *      Get vlan port egress tag attribute from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ * OUTPUT:
+ *      ptr_attr    -- egress tag attr
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getPortEgsTagAttr(
+    const UI32_T    unit,
+    const UI32_T    port,
+    AIR_PORT_EGS_TAG_ATTR_T *ptr_attr)
+{
+    UI32_T val = 0;
+
+    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);
+    AIR_CHECK_PTR(ptr_attr);
+
+    aml_readReg(unit, PCR(port), &val);
+    *ptr_attr = (val >> PCR_EG_TAG_OFFT) & PCR_EG_TAG_RELMASK;
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_setPortOuterTPID
+ * PURPOSE:
+ *      Set stack tag TPID of the port from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ *      tpid        -- TPID
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setPortOuterTPID(
+    const UI32_T    unit,
+    const UI32_T    port,
+    const UI16_T    tpid)
+{
+    UI32_T val = 0;
+
+    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);
+
+    aml_readReg(unit, PVC(port), &val);
+    val &= ~PVC_STAG_VPID_MASK;
+    val |= (tpid & PVC_STAG_VPID_RELMASK) << PVC_STAG_VPID_OFFT;
+    aml_writeReg(unit, PVC(port), val);
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_getPortOuterTPID
+ * PURPOSE:
+ *      Get stack tag TPID of the port from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ * OUTPUT:
+ *      ptr_tpid    -- TPID
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getPortOuterTPID(
+    const UI32_T    unit,
+    const UI32_T    port,
+    UI16_T          *ptr_tpid)
+{
+    UI32_T val = 0;
+
+    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);
+    AIR_CHECK_PTR(ptr_tpid);
+
+    aml_readReg(unit, PVC(port), &val);
+    *ptr_tpid = (val >> PVC_STAG_VPID_OFFT) & PVC_STAG_VPID_RELMASK;
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_setPortPVID
+ * PURPOSE:
+ *      Set PVID of the port from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ *      pvid        -- native vlan id
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setPortPVID(
+    const UI32_T    unit,
+    const UI32_T    port,
+    const UI16_T    pvid)
+{
+    UI32_T val = 0;
+
+    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);
+    AIR_PARAM_CHK((pvid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+
+    aml_readReg(unit, PVID(port), &val);
+    val &= ~PVID_PCVID_MASK;
+    val |= (pvid & PVID_PCVID_RELMASK) << PVID_PCVID_OFFT;
+    aml_writeReg(unit, PVID(port), val);
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_getPortPVID
+ * PURPOSE:
+ *      Get PVID of the port from the specified device.
+ * INPUT:
+ *      unit        -- unit id
+ *      port        -- port id
+ * OUTPUT:
+ *      ptr_pvid    -- native vlan id
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getPortPVID(
+    const UI32_T    unit,
+    const UI32_T    port,
+    UI16_T          *ptr_pvid)
+{
+    UI32_T val = 0;
+
+    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);
+    AIR_CHECK_PTR(ptr_pvid);
+
+    aml_readReg(unit, PVID(port), &val);
+    *ptr_pvid = (val >> PVID_PCVID_OFFT) & PVID_PCVID_RELMASK;
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_setServiceTag
+ * PURPOSE:
+ *      Set Vlan service tag.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      stag        -- service stag
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setServiceTag(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const UI16_T    stag)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+    AIR_PARAM_CHK((stag > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+
+    _air_vlan_readEntry(unit, vid, &vlan_entry);
+    if (!vlan_entry.valid)
+        return AIR_E_ENTRY_NOT_FOUND;
+
+    vlan_entry.vlan_entry_format.stag = stag;
+    _air_vlan_writeEntry(unit, vid, &vlan_entry);
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_getServiceTag
+ * PURPOSE:
+ *      Get Vlan service tag.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      ptr_stag    -- service stag
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getServiceTag(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    UI16_T          *ptr_stag)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+    AIR_CHECK_PTR(ptr_stag);
+
+    _air_vlan_readEntry(unit, vid, &vlan_entry);
+    if (!vlan_entry.valid)
+        return AIR_E_ENTRY_NOT_FOUND;
+
+    *ptr_stag = vlan_entry.vlan_entry_format.stag;
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_setEgsTagCtlEnable
+ * PURPOSE:
+ *      Set per vlan egress tag control.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      enable      -- enable vlan egress tag control
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setEgsTagCtlEnable(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const BOOL_T    enable)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+
+    _air_vlan_readEntry(unit, vid, &vlan_entry);
+    if (!vlan_entry.valid)
+        return AIR_E_ENTRY_NOT_FOUND;
+
+    vlan_entry.vlan_entry_format.eg_ctrl_en = enable ? 1 : 0;
+    _air_vlan_writeEntry(unit, vid, &vlan_entry);
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_getEgsTagCtlEnable
+ * PURPOSE:
+ *      Get per vlan egress tag control.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      ptr_enable  -- enable vlan egress tag control
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getEgsTagCtlEnable(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    BOOL_T          *ptr_enable)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+    AIR_CHECK_PTR(ptr_enable);
+
+    _air_vlan_readEntry(unit, vid, &vlan_entry);
+    if (!vlan_entry.valid)
+        return AIR_E_ENTRY_NOT_FOUND;
+
+    *ptr_enable = vlan_entry.vlan_entry_format.eg_ctrl_en;
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_setEgsTagConsistent
+ * PURPOSE:
+ *      Set per vlan egress tag consistent.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      enable      -- enable vlan egress tag consistent
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setEgsTagConsistent(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const BOOL_T    enable)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+
+    _air_vlan_readEntry(unit, vid, &vlan_entry);
+    if (!vlan_entry.valid)
+        return AIR_E_ENTRY_NOT_FOUND;
+
+    vlan_entry.vlan_entry_format.eg_con = enable;
+    _air_vlan_writeEntry(unit, vid, &vlan_entry);
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_getEgsTagConsistent
+ * PURPOSE:
+ *      Get per vlan egress tag consistent.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      ptr_enable  -- enable vlan egress tag consistent
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getEgsTagConsistent(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    BOOL_T          *ptr_enable)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+    AIR_CHECK_PTR(ptr_enable);
+
+    _air_vlan_readEntry(unit, vid, &vlan_entry);
+    if (!vlan_entry.valid)
+        return AIR_E_ENTRY_NOT_FOUND;
+
+    *ptr_enable = vlan_entry.vlan_entry_format.eg_con;
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_setPortBasedStag
+ * PURPOSE:
+ *      Set vlan port based stag enable.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      enable      -- vlan port based stag enable
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setPortBasedStag(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const BOOL_T    enable)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+
+    _air_vlan_readEntry(unit, vid, &vlan_entry);
+    if (!vlan_entry.valid)
+        return AIR_E_ENTRY_NOT_FOUND;
+
+    vlan_entry.vlan_entry_format.port_stag = enable;
+    _air_vlan_writeEntry(unit, vid, &vlan_entry);
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_getPortBasedStag
+ * PURPOSE:
+ *      Get vlan port based stag enable.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      ptr_enable  -- vlan port based stag enable
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getPortBasedStag(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    BOOL_T          *ptr_enable)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+    AIR_CHECK_PTR(ptr_enable);
+
+    _air_vlan_readEntry(unit, vid, &vlan_entry);
+    if (!vlan_entry.valid)
+        return AIR_E_ENTRY_NOT_FOUND;
+
+    *ptr_enable = vlan_entry.vlan_entry_format.port_stag;
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_setPortEgsTagCtl
+ * PURPOSE:
+ *      Set vlan port egress tag control.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ *      port        -- port id
+ *      tag_ctl     -- egress tag control
+ * OUTPUT:
+ *      None
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_setPortEgsTagCtl(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const UI32_T    port,
+    const AIR_VLAN_PORT_EGS_TAG_CTL_TYPE_T    tag_ctl)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);
+    AIR_PARAM_CHK((tag_ctl >= AIR_PORT_EGS_TAG_ATTR_LAST), AIR_E_BAD_PARAMETER);
+
+    _air_vlan_readEntry(unit, vid, &vlan_entry);
+    if (!vlan_entry.valid)
+        return AIR_E_ENTRY_NOT_FOUND;
+
+    vlan_entry.vlan_entry_format.eg_ctrl &= ~(0x3 << (port * 2));
+    vlan_entry.vlan_entry_format.eg_ctrl |= (tag_ctl & 0x3) << (port * 2);
+    _air_vlan_writeEntry(unit, vid, &vlan_entry);
+
+    return AIR_E_OK;
+}
+
+/* FUNCTION NAME:   air_vlan_getPortEgsTagCtl
+ * PURPOSE:
+ *      Get vlan port egress tag control.
+ * INPUT:
+ *      unit        -- unit id
+ *      vid         -- vlan id
+ * OUTPUT:
+ *      ptr_tag_ctl -- egress tag control
+ * RETURN:
+ *      AIR_E_OK                -- Successfully read the data.
+ *      AIR_E_OTHERS            -- Operation failed.
+ *      AIR_E_BAD_PARAMETER     -- Invalid parameter.
+ * NOTES:
+ *      none
+ */
+AIR_ERROR_NO_T
+air_vlan_getPortEgsTagCtl(
+    const UI32_T    unit,
+    const UI16_T    vid,
+    const UI32_T    port,
+    AIR_VLAN_PORT_EGS_TAG_CTL_TYPE_T   *ptr_tag_ctl)
+{
+    AIR_VLAN_ENTRY_T vlan_entry = {0};
+
+    AIR_PARAM_CHK((vid > AIR_VLAN_ID_MAX), AIR_E_BAD_PARAMETER);
+    AIR_PARAM_CHK((port >= AIR_MAX_NUM_OF_PORTS), AIR_E_BAD_PARAMETER);
+    AIR_CHECK_PTR(ptr_tag_ctl);
+
+    _air_vlan_readEntry(unit, vid, &vlan_entry);
+    if (!vlan_entry.valid)
+        return AIR_E_ENTRY_NOT_FOUND;
+
+    *ptr_tag_ctl = (vlan_entry.vlan_entry_format.eg_ctrl >> (port * 2)) & 0x3;
+
+    return AIR_E_OK;
+}
diff --git a/recipes-devtools/switch/files/src/an8855_sdk/core/an8855_mdio.h b/recipes-devtools/switch/files/src/an8855_sdk/core/an8855_mdio.h
index c2d4bd7..5322393 100644
--- a/recipes-devtools/switch/files/src/an8855_sdk/core/an8855_mdio.h
+++ b/recipes-devtools/switch/files/src/an8855_sdk/core/an8855_mdio.h
@@ -1,203 +1,203 @@
-/* FILE NAME:  an8855_mdio.h

- * PURPOSE:

- *      It provides AN8855 mdio access API.

- * NOTES:

- *

- */

-

-#ifndef AN8855_MDIO_H

-#define AN8855_MDIO_H

-

-/* INCLUDE FILE DECLARATIONS

- */

-//#include "CTP_type.h"

-//#include "CTP_shell.h"

-//#include "common.h"

-//#include "eth.h"

-

-/* NAMING CONSTANT DECLARATIONS

- */

-

-/* MACRO FUNCTION DECLARATIONS

- */

-/* Attention!! Customer should define udelay function */

-void delayUs(int usecond);

-#define an8855_udelay(us) delayUs(us)

-

-/* Attention!! Customer should define dbg_print to get dbg output */

-#ifndef dbg_print

-#define dbg_print(...)

-#endif

-

-#define AN8855_PHY_NUM                      5

-

-/* DATA TYPE DECLARATIONS

- */

-#ifndef NULL

-#define NULL 0L

-#endif

-

-#ifndef u32

-#define u32 unsigned int

-#endif

-

-#ifndef u16

-#define u16 unsigned short

-#endif

-

-#ifndef u8

-#define u8 unsigned char

-#endif

-

-typedef u32 (*AIR_MII_READ_FUNC_T) (u32 phy_addr, u32 reg, u32 *p_data);

-

-typedef u32 (*AIR_MII_WRITE_FUNC_T) (u32 phy_addr, u32 reg, u32 data);

-

-typedef u32 (*AIR_MII_C45_READ_FUNC_T) (u32 phy_addr, u32 dev, u32 reg, u32 *p_data);

-

-typedef u32 (*AIR_MII_C45_WRITE_FUNC_T) (u32 phy_addr, u32 dev, u32 reg, u32 data);

-

-extern u32 g_smi_addr;

-

-/* EXPORTED SUBPROGRAM SPECIFICATIONS

- */

-

-/* FUNCTION NAME:   an8855_set_smi_addr

- * PURPOSE:

- *      This API is used to set an8855 smi address.

- * INPUT:

- *      smi_addr -- AN8855 smi address

- * OUTPUT:

- * RETURN:

- * NOTES:

- *      None

- */

-void

-an8855_set_smi_addr(u32 smi_addr);

-

-/* FUNCTION NAME:   an8855_set_mii_callback

- * PURPOSE:

- *      This API is used to set an8855 mii access callbacks.

- * INPUT:

- *      mii_read -- mii read api function

- *      mii_write -- mii write api function

- * OUTPUT:

- * RETURN:

- *      0     -- Successfully set callback.

- *      -1    -- Setting callback failed.

- * NOTES:

- *      None

- */

-int

-an8855_set_mii_callback(

-    AIR_MII_READ_FUNC_T mii_read, 

-    AIR_MII_WRITE_FUNC_T mii_write,

-    AIR_MII_C45_READ_FUNC_T mii_c45_read, 

-    AIR_MII_C45_WRITE_FUNC_T mii_c45_write);

-

-/* FUNCTION NAME:   an8855_reg_read

- * PURPOSE:

- *      This API is used read an8855 registers.

- * INPUT:

- *      reg -- register offset

- * OUTPUT:

- * RETURN:

- *      Register value

- * NOTES:

- *      Attention!! Customer should implement mdio mutex

- *      lock in this func

- */

-u32

-an8855_reg_read(u32 reg);

-

-/* FUNCTION NAME:   an8855_reg_write

- * PURPOSE:

- *      This API is used write an8855 registers.

- * INPUT:

- *      reg -- register offset

- *      val -- register value

- * OUTPUT:

- * RETURN:

- * NOTES:

- *      Attention!! Customer should implement mdio mutex

- *      lock in this func

- */

-void

-an8855_reg_write(u32 reg, u32 val);

-

-/* FUNCTION NAME:   an8855_phy_read

- * PURPOSE:

- *      This API is used read an8855 phy registers.

- * INPUT:

- *      port_num -- port number, 0~4

- *      reg -- phy register offset

- * OUTPUT:

- *      p_val -- phy register value

- * RETURN:

- *      0 -- read success

- *      -1 -- read failure

- * NOTES:

- *      Attention!! Customer should implement mii mutex

- *      lock in this func

- */

-int

-an8855_phy_read(u32 port_num, u32 reg, u32 *p_val);

-

-/* FUNCTION NAME:   an8855_phy_write

- * PURPOSE:

- *      This API is used write an8855 phy registers.

- * INPUT:

- *      port_num -- port number, 0~4

- *      reg -- phy register offset

- *      val -- phy register value

- * OUTPUT:

- * RETURN:

- *      0 -- write success

- *      -1 -- write failure

- * NOTES:

- *      Attention!! Customer should implement mii mutex

- *      lock in this func

- */

-int

-an8855_phy_write(u32 port_num, u32 reg, u32 val);

-

-/* FUNCTION NAME:   an8855_phy_read_cl45

- * PURPOSE:

- *      This API is used read an8855 phy registers.

- * INPUT:

- *      port_num -- port number, 0~4

- *      dev_addr -- phy device type

- *      reg_addr -- phy register offset

- * OUTPUT:

- *      p_val -- phy register value

- * RETURN:

- *      0 -- read success

- *      -1 -- read failure

- * NOTES:

- *      Attention!! Customer should implement mii mutex

- *      lock in this func or before/after calling this func

- */

-u32

-an8855_phy_read_cl45(u32 port_num, u32 dev_addr, u32 reg_addr, u32 *p_val);

-

-/* FUNCTION NAME:   an8855_phy_write_cl45

- * PURPOSE:

- *      This API is used write an8855 phy registers.

- * INPUT:

- *      port_num -- port number, 0~4

- *      dev_addr -- phy device type

- *      reg_addr -- phy register offset

- *      val -- phy register value

- * OUTPUT:

- * RETURN:

- *      0 -- write success

- *      -1 -- write failure

- * NOTES:

- *      Attention!! Customer should implement mii mutex

- *      lock in this func or before/after calling this func

- */

-int

-an8855_phy_write_cl45(u32 port_num, u32 dev_addr, u32 reg_addr, u32 val);

-

-#endif  /* End of AN8855_MDIO_H */

-

+/* FILE NAME:  an8855_mdio.h
+ * PURPOSE:
+ *      It provides AN8855 mdio access API.
+ * NOTES:
+ *
+ */
+
+#ifndef AN8855_MDIO_H
+#define AN8855_MDIO_H
+
+/* INCLUDE FILE DECLARATIONS
+ */
+//#include "CTP_type.h"
+//#include "CTP_shell.h"
+//#include "common.h"
+//#include "eth.h"
+
+/* NAMING CONSTANT DECLARATIONS
+ */
+
+/* MACRO FUNCTION DECLARATIONS
+ */
+/* Attention!! Customer should define udelay function */
+void delayUs(int usecond);
+#define an8855_udelay(us) delayUs(us)
+
+/* Attention!! Customer should define dbg_print to get dbg output */
+#ifndef dbg_print
+#define dbg_print(...)
+#endif
+
+#define AN8855_PHY_NUM                      5
+
+/* DATA TYPE DECLARATIONS
+ */
+#ifndef NULL
+#define NULL 0L
+#endif
+
+#ifndef u32
+#define u32 unsigned int
+#endif
+
+#ifndef u16
+#define u16 unsigned short
+#endif
+
+#ifndef u8
+#define u8 unsigned char
+#endif
+
+typedef u32 (*AIR_MII_READ_FUNC_T) (u32 phy_addr, u32 reg, u32 *p_data);
+
+typedef u32 (*AIR_MII_WRITE_FUNC_T) (u32 phy_addr, u32 reg, u32 data);
+
+typedef u32 (*AIR_MII_C45_READ_FUNC_T) (u32 phy_addr, u32 dev, u32 reg, u32 *p_data);
+
+typedef u32 (*AIR_MII_C45_WRITE_FUNC_T) (u32 phy_addr, u32 dev, u32 reg, u32 data);
+
+extern u32 g_smi_addr;
+
+/* EXPORTED SUBPROGRAM SPECIFICATIONS
+ */
+
+/* FUNCTION NAME:   an8855_set_smi_addr
+ * PURPOSE:
+ *      This API is used to set an8855 smi address.
+ * INPUT:
+ *      smi_addr -- AN8855 smi address
+ * OUTPUT:
+ * RETURN:
+ * NOTES:
+ *      None
+ */
+void
+an8855_set_smi_addr(u32 smi_addr);
+
+/* FUNCTION NAME:   an8855_set_mii_callback
+ * PURPOSE:
+ *      This API is used to set an8855 mii access callbacks.
+ * INPUT:
+ *      mii_read -- mii read api function
+ *      mii_write -- mii write api function
+ * OUTPUT:
+ * RETURN:
+ *      0     -- Successfully set callback.
+ *      -1    -- Setting callback failed.
+ * NOTES:
+ *      None
+ */
+int
+an8855_set_mii_callback(
+    AIR_MII_READ_FUNC_T mii_read, 
+    AIR_MII_WRITE_FUNC_T mii_write,
+    AIR_MII_C45_READ_FUNC_T mii_c45_read, 
+    AIR_MII_C45_WRITE_FUNC_T mii_c45_write);
+
+/* FUNCTION NAME:   an8855_reg_read
+ * PURPOSE:
+ *      This API is used read an8855 registers.
+ * INPUT:
+ *      reg -- register offset
+ * OUTPUT:
+ * RETURN:
+ *      Register value
+ * NOTES:
+ *      Attention!! Customer should implement mdio mutex
+ *      lock in this func
+ */
+u32
+an8855_reg_read(u32 reg);
+
+/* FUNCTION NAME:   an8855_reg_write
+ * PURPOSE:
+ *      This API is used write an8855 registers.
+ * INPUT:
+ *      reg -- register offset
+ *      val -- register value
+ * OUTPUT:
+ * RETURN:
+ * NOTES:
+ *      Attention!! Customer should implement mdio mutex
+ *      lock in this func
+ */
+void
+an8855_reg_write(u32 reg, u32 val);
+
+/* FUNCTION NAME:   an8855_phy_read
+ * PURPOSE:
+ *      This API is used read an8855 phy registers.
+ * INPUT:
+ *      port_num -- port number, 0~4
+ *      reg -- phy register offset
+ * OUTPUT:
+ *      p_val -- phy register value
+ * RETURN:
+ *      0 -- read success
+ *      -1 -- read failure
+ * NOTES:
+ *      Attention!! Customer should implement mii mutex
+ *      lock in this func
+ */
+int
+an8855_phy_read(u32 port_num, u32 reg, u32 *p_val);
+
+/* FUNCTION NAME:   an8855_phy_write
+ * PURPOSE:
+ *      This API is used write an8855 phy registers.
+ * INPUT:
+ *      port_num -- port number, 0~4
+ *      reg -- phy register offset
+ *      val -- phy register value
+ * OUTPUT:
+ * RETURN:
+ *      0 -- write success
+ *      -1 -- write failure
+ * NOTES:
+ *      Attention!! Customer should implement mii mutex
+ *      lock in this func
+ */
+int
+an8855_phy_write(u32 port_num, u32 reg, u32 val);
+
+/* FUNCTION NAME:   an8855_phy_read_cl45
+ * PURPOSE:
+ *      This API is used read an8855 phy registers.
+ * INPUT:
+ *      port_num -- port number, 0~4
+ *      dev_addr -- phy device type
+ *      reg_addr -- phy register offset
+ * OUTPUT:
+ *      p_val -- phy register value
+ * RETURN:
+ *      0 -- read success
+ *      -1 -- read failure
+ * NOTES:
+ *      Attention!! Customer should implement mii mutex
+ *      lock in this func or before/after calling this func
+ */
+u32
+an8855_phy_read_cl45(u32 port_num, u32 dev_addr, u32 reg_addr, u32 *p_val);
+
+/* FUNCTION NAME:   an8855_phy_write_cl45
+ * PURPOSE:
+ *      This API is used write an8855 phy registers.
+ * INPUT:
+ *      port_num -- port number, 0~4
+ *      dev_addr -- phy device type
+ *      reg_addr -- phy register offset
+ *      val -- phy register value
+ * OUTPUT:
+ * RETURN:
+ *      0 -- write success
+ *      -1 -- write failure
+ * NOTES:
+ *      Attention!! Customer should implement mii mutex
+ *      lock in this func or before/after calling this func
+ */
+int
+an8855_phy_write_cl45(u32 port_num, u32 dev_addr, u32 reg_addr, u32 val);
+
+#endif  /* End of AN8855_MDIO_H */
+
diff --git a/recipes-devtools/switch/files/src/an8855_sdk/core/an8855_phy.h b/recipes-devtools/switch/files/src/an8855_sdk/core/an8855_phy.h
index 3c810e7..70836e8 100644
--- a/recipes-devtools/switch/files/src/an8855_sdk/core/an8855_phy.h
+++ b/recipes-devtools/switch/files/src/an8855_sdk/core/an8855_phy.h
@@ -1,409 +1,409 @@
-/* FILE NAME:  an8855_phy.h

- * PURPOSE:

- *      It provides AN8855 phy definition.

- * NOTES:

- *

- */

-

-#ifndef _AN8855_PHY_H_

-#define _AN8855_PHY_H_

-

-

-/* Type Definitions */

-#define int8_t char

-#define uint8_t unsigned char

-#define int16_t short

-#define uint16_t unsigned short

-#define int32_t int

-#define uint32_t unsigned int

-/* DATA TYPE DECLARATIONS

- */

-typedef int                 BOOL_T;

-typedef signed char         I8_T;

-typedef unsigned char       UI8_T;

-typedef signed short        I16_T;

-typedef unsigned short      UI16_T;

-typedef signed int          I32_T;

-typedef unsigned int        UI32_T;

-typedef char                C8_T;

-typedef unsigned long long  UI64_T;

-

-typedef UI8_T   AIR_MAC_T[6];

-

-/* Debug flags */

-//#define _MDIO_BOOTS_MODE     1 // Boots for preamble

-#define _DEBUG_PRINT         1 // Debug print for Arduino

-//#define _DEBUG_PRINT_eFuse     1

-#define _write_eFuse     1

-

-#define _DEBUG_SCAN_ALL      0 // Scan all Code for SerDes Calibration

-#define _WRITE_RG_DIR        1 // Write RG directly for Calibration

-#define _USER_DEFINE_MODE    0 // Replace to user-defined RG for Calibration

-#define _DEBUG_MANUAL        1 // dbg_20210604  // manual dbg_20210604

-/**************************************************************************/

-

-/* Phy Address */

-//#define phyadd_common 0x1d            // EN8801

-//#define PHY_NUM 1             // EN8801

-//#define phyadd_common 0x9         // EN7523

-//#define PHY_NUM 4             // EN7523

-//#define phyadd_common 0x0         // EN8850

-//#define PHY_NUM   5           // EN8850

-#define PHY_NUM   4             // EN8851

-#define CALIPOLARITY  1

-

-#define TXAMP_offset  0    // for 8851

-

-/* On/Off */

-//#define ENABLE  1

-//#define DISABLE 0

-#define Relay_ENABLE  1

-#define Relay_DISABLE 0

-

-/* FT Pattern */

-#define _MDIO     0x0

-#define _I2C      0x1

-#define FT_USB3_T101    0x0

-#define FT_PCIE0_T101   0x1

-#define FT_PCIE1_T101   0x2

-#define FT_PON_T101     0x3

-

-/********* Digital pin definition *************/

-#define Relay_Tx_PN         22   // relay 1

-#define Relay_R_R50         23   // relay 2

-#define Relay_Tx_Vol        24   // relay 3

-#define Relay_Rx_Vol        25   // relay 4

-#define Relay_DUT_GND       26   // relay 5

-#define Relay_I2C           27   // PIN for SCL&SDA , relay 6

-//#define Relay_I2C_SCL       27   // PIN for SCL&SDA , relay 6

-//#define Relay_I2C_SDA       28   // PIN for SCL&SDA , relay 6

-

-#define pin_MDIO           36   // PIN for MDIO

-#define pin_MDC            37   // PIN for MDC

-#define FT_PATTERN_bit0    49   // PIN for FT0

-#define FT_PATTERN_bit1    48   // PIN for FT1

-#define Relay_MDIO         35   // PIN for MDIO relay, relay 7

-

-/***********************************************/

-/* Use for I/O register PORTA control */

-#define POR_Relay_Tx_PN    D22   // use for PORTA control, relay 1

-#define POR_Relay_R_R50    D23   // use for PORTA control, relay 2

-#define POR_Relay_Tx_Vol   D24   // use for PORTA control, relay 3

-#define POR_Relay_Rx_Vol   D25   // use for PORTA control, relay 4

-#define POR_Relay_DUT_GND  D26   // use for PORTA control, relay 5

-//#define POR_Relay_I2C      D27   // use for PORTA control, relay 6

-#define POR_Relay_I2C_SCL  D27   // use for PORTA control, relay 6

-#define POR_Relay_I2C_SDA  D28   // use for PORTA control, relay 7

-

-/* Use for I/O register PORTC control */

-#define POR_MDIO               D36  // use for PORTC control

-#define POR_MDC                D37  // use for PORTC control

-#define POR_Relay_MDIO         D35  // use for PORTC control, relay 7

-

-/* Use for I/O register PORTL control */

-#define POR_FT_PATTERN_bit0    D49  // use for PORTL control

-#define POR_FT_PATTERN_bit1    D48  // use for PORTL control

-

-

-/* I/O register Port A */

-#define D22   0

-#define D23   1

-#define D24   2

-#define D25   3

-#define D26   4

-#define D27   5

-#define D28   6

-#define D29   7

-

-/* I/O register Port C */

-#define D37   0

-#define D36   1

-#define D35   2

-#define D34   3

-#define D33   4

-#define D32   5

-#define D31   6

-#define D30   7

-

-/* I/O register Port L */

-#define D49   0

-#define D48   1

-#define D47   2

-#define D46   3

-#define D45   4

-#define D44   5

-#define D43   6

-#define D42   7

-

-/* I/O register Port D */

-#define D21   0

-#define D20   1

-#define D19   2

-#define D18   3

-

-

-/***************************************************************************

-**************************************************************************

-* MDC/MDIO

-***************************************************************************

-***************************************************************************/

-#define SET_HIGH(data, nbit) ((data)|=(nbit))

-#define SET_LOW(data, nbit) ((data)&=~(nbit))

-

-#define MDIO_ONE  _BV(POR_MDIO)

-#define MDIO_ZERO 0x00

-#define MDC_ONE   _BV(POR_MDC)

-#define MDC_ZERO  0x00

-

-#define delay_us delayMicroseconds(0)

-

-#define ANACAL_INIT        0x01

-#define ANACAL_ERROR       0xFD

-#define ANACAL_SATURATION  0xFE

-#define ANACAL_FINISH      0xFF

-#define ANACAL_PAIR_A      0

-#define ANACAL_PAIR_B      1

-#define ANACAL_PAIR_C      2

-#define ANACAL_PAIR_D      3

-#define DAC_IN_0V          0x000

-#define DAC_IN_2V          0x0f0  // +/-1V

-

-#define ZCAL_MIDDLE        0x20

-#define TX_OFFSET_0mV_idx  31

-#define TX_AMP_MIDDLE      0x20

-

-#define TX_i2mpb_hbt_ofs  0x4   // 8851 fine tune 100M v1 (20220414)

-#define R50_OFFSET_VALUE  0x5

-

-//============== definition value for GbE ===================//

-#define BG_VOLTAGE_OUT     0xc0

-#define FORCE_MDI          2

-#define FORCE_MDIX         3

-#define LDO_1p15_VOSEL_1   1

-#define RX_CAL_VALUE_9       0x3

-#define RX_CAL_HVGA_BW_2     0x2

-#define RX_CAL_DCO_Normal    0x0

-#define RX_CAL_DCO_BYPASS_TX_RX  0x3

-#define RX_CAL_DCO_0xF    0xF

-

-#define TANA_MON_DCV_SEL__MASK         0xE0

-#define TANA_MON_DCV_SEL__MPX_TANA_A   0x20

-#define TANA_MON_DCV_SEL__MPX_TANA_B   0x40

-#define TANA_MON_DCV_SEL__MPX_TANA_C   0x60

-#define TANA_MON_DCV_SEL__MPX_TANA_D   0x80

-#define TANA_MON_DCV_SEL__MONVC__MASK  0x008000C8

-#define TANA_MON_DCV__TANA__VBG_MON    0x000000C0

-#define TANA_MON_DCV__TANA__MONVC      0x000000C8

-

-#define AN_disable_force_1000M 0x0140

-#define BG_voltage_output 0xc000

-#define Fix_mdi 0x1010

-#define Disable_tx_slew_control 0x0000

-#define LDO_control 0x0100

-#define Cal_control_BG 0x1110

-#define Cal_control_R50 0x1100

-#define Cal_control_TX_AMP 0x1100

-#define Cal_control_TX_OFST 0x0100

-#define Cal_control_R50_pairA_ENABLE 0x1101

-#define Disable_all 0x0

-#define Zcalen_A_ENABLE 0x0000

-#define Zcalen_B_ENABLE 0x1000

-#define Zcalen_C_ENABLE 0x0100

-#define Zcalen_D_ENABLE 0x0010

-#define MASK_MSB_8bit 0xff00

-#define MASK_LSB_8bit 0x00ff

-#define MASK_r50ohm_rsel_tx_a 0x7f00

-#define MASK_r50ohm_rsel_tx_b 0x007f

-#define MASK_r50ohm_rsel_tx_c 0x7f00

-#define MASK_r50ohm_rsel_tx_d 0x007f

-#define Rg_r50ohm_rsel_tx_a_en 0x8000

-#define Rg_r50ohm_rsel_tx_b_en 0x0080

-#define Rg_r50ohm_rsel_tx_c_en 0x8000

-#define Rg_r50ohm_rsel_tx_d_en 0x0080

-#define Rg_txvos_calen_ENABLE 0x0001

-#define Bypass_tx_offset_cal 0x8000

-#define Enable_Tx_VLD 0xf808

-#define Rg_txg_calen_a_ENABLE 0x1000

-#define Rg_txg_calen_b_ENABLE 0x0100

-#define Rg_txg_calen_c_ENABLE 0x0010

-#define Rg_txg_calen_d_ENABLE 0x0001

-#define Force_dasn_dac_in0_ENABLE 0x8000

-#define Force_dasn_dac_in1_ENABLE 0x8000

-#define MASK_cr_tx_amp_offset_MSB 0x3f00

-#define MASK_cr_tx_amp_offset_LSB 0x003f

-#define Rg_cal_refsel_ENABLE 0x0010

-#define MASK_da_tx_i2mpb_a_gbe 0xfc00

-#define MASK_da_tx_i2mpb_b_c_d_gbe 0x3f00

-

-#define LED_basic_control_en_active_low 0x800a

-#define LED_led0_en_active_high 0xc007

-#define LED_led0_force_blinking 0x0200

-

-

-

-/*phy calibration use*/

-//Type defines

-typedef unsigned char    UINT8;

-typedef unsigned short   UINT16;

-typedef unsigned long    UINT32;

-

-typedef struct

-{

-  UINT16 DATA_Lo;

-  UINT8  DATA_Hi;

-}TR_DATA_T;

-

-//CL22 Reg Support Page Select//

-#define RgAddr_Reg1Fh        0x1f

-#define CL22_Page_Reg        0x0000

-#define CL22_Page_ExtReg     0x0001

-#define CL22_Page_MiscReg    0x0002

-#define CL22_Page_LpiReg     0x0003

-#define CL22_Page_tReg       0x02A3

-#define CL22_Page_TrReg      0x52B5

-

-//CL45 Reg Support DEVID//

-#define DEVID_03             0x03

-#define DEVID_07             0x07

-#define DEVID_1E             0x1E

-#define DEVID_1F             0x1F

-

-//TokenRing Reg Access//

-#define TrReg_PKT_XMT_STA    0x8000

-#define TrReg_WR             0x8000

-#define TrReg_RD             0xA000

-

-/* ----------------- gephy_all Bit Field Definitions ------------------- */

-

-

-

-//-------------------------------------

-//0x0000

-#define RgAddr_Reg00h                               0x00

-

-//0x51e01200

-#define RgAddr_dev1Eh_reg120h                       0x0120

-//0x51e01220

-#define RgAddr_dev1Eh_reg122h                       0x0122

-//0x51e01440

-#define RgAddr_dev1Eh_reg144h                       0x0144

-//0x51e014a0

-#define RgAddr_dev1Eh_reg14Ah                       0x014a

-//0x51e019b0

-#define RgAddr_dev1Eh_reg19Bh                       0x019b

-//0x51e02340

-#define RgAddr_dev1Eh_reg234h                       0x0234

-//0x51e02380

-#define RgAddr_dev1Eh_reg238h                       0x0238

-//0x51e02390

-#define RgAddr_dev1Eh_reg239h                       0x0239

-//0x51f02680

-#define RgAddr_dev1Fh_reg268h                       0x0268

-//0x51e02d10

-#define RgAddr_dev1Eh_reg2D1h                       0x02d1

-//0x51e03230

-#define RgAddr_dev1Eh_reg323h                       0x0323

-//0x51e03240

-#define RgAddr_dev1Eh_reg324h                       0x0324

-//0x51e03260

-#define RgAddr_dev1Eh_reg326h                       0x0326

-

-//0x51f01000

-#define RgAddr_dev1Fh_reg100h                       0x0100

-//0x51e01450

-#define RgAddr_dev1Eh_reg145h                       0x0145

-//0x51f00ff0

-#define RgAddr_dev1Fh_reg0FFh                       0x00ff

-//0x51e00db0

-#define RgAddr_dev1Eh_reg0DBh                       0x00db

-//0x51e00dc0

-#define RgAddr_dev1Eh_reg0DCh                       0x00dc

-//0x51e00e00

-#define RgAddr_dev1Eh_reg0E0h                       0x00e0

-//0x51e00e10

-#define RgAddr_dev1Eh_reg0E1h                       0x00e1

-//0x51e00e00

-#define RgAddr_dev1Eh_reg0E0h                       0x00e0

-//0x51e017a0

-#define RgAddr_dev1Eh_reg17Ah                       0x017a

-//0x51f01150

-#define RgAddr_dev1Fh_reg115h                       0x0115

-//0x51f01000

-#define RgAddr_dev1Fh_reg100h                       0x0100

-//0x51e01450

-#define RgAddr_dev1Eh_reg145h                       0x0145

-//0x51e01450

-#define RgAddr_dev1Eh_reg145h                       0x0145

-//0x51e01850

-#define RgAddr_dev1Eh_reg185h                       0x0185

-//0x51e00fb0

-#define RgAddr_dev1Eh_reg0FBh                       0x00fb

-//0x51e01740

-#define RgAddr_dev1Eh_reg174h                       0x0174

-//0x51e01750

-#define RgAddr_dev1Eh_reg175h                       0x0175

-//0x51e01850

-#define RgAddr_dev1Eh_reg185h                       0x0185

-//0x51e00fb0

-#define RgAddr_dev1Eh_reg0FBh                       0x00fb

-//0x51e00960

-#define RgAddr_dev1Eh_reg096h                       0x0096

-//0x51e003e0

-#define RgAddr_dev1Eh_reg03Eh                       0x003e

-//0x51e00dd0

-#define RgAddr_dev1Eh_reg0DDh                       0x00dd

-//0x51e017d0

-#define RgAddr_dev1Eh_reg17Dh                       0x017d

-//0x51e01810

-#define RgAddr_dev1Eh_reg181h                       0x0181

-//0x51e00120

-#define RgAddr_dev1Eh_reg012h                       0x0012

-//0x51e017e0

-#define RgAddr_dev1Eh_reg17Eh                       0x017e

-//0x51e01820

-#define RgAddr_dev1Eh_reg182h                       0x0182

-//0x51e00170

-#define RgAddr_dev1Eh_reg017h                       0x0017

-//0x51e01830

-#define RgAddr_dev1Eh_reg183h                       0x0183

-//0x51e00190

-#define RgAddr_dev1Eh_reg019h                       0x0019

-//0x51e01800

-#define RgAddr_dev1Eh_reg180h                       0x0180

-//0x51e01840

-#define RgAddr_dev1Eh_reg184h                       0x0184

-//0x51e00210

-#define RgAddr_dev1Eh_reg021h                       0x0021

-//0x51e01720

-#define RgAddr_dev1Eh_reg172h                       0x0172

-//0x51e01730

-#define RgAddr_dev1Eh_reg173h                       0x0173

-//0x51e017c0

-#define RgAddr_dev1Eh_reg17Ch                       0x017c

-//0x51e017f0

-#define RgAddr_dev1Eh_reg17Fh                       0x017f

-

-//0x52b5100

-#define RgAddr_TrReg10h                             0x10

-//0x52b5110

-#define RgAddr_TrReg11h                             0x11

-//0x52b5120

-#define RgAddr_TrReg12h                             0x12

-

-//0x31c0

-#define RgAddr_LpiReg1Ch                            0x1c

-//0x31d0

-#define RgAddr_LpiReg1Dh                            0x1d

-uint8_t BG_Calibration(uint8_t phyadd, int8_t calipolarity);

-uint8_t R50_Calibration(uint8_t phyadd, uint8_t phyadd_common);

-uint8_t TX_OFS_Calibration(uint8_t phyadd, uint8_t phyadd_common);

-uint8_t TX_AMP_Calibration(uint8_t phyadd, uint8_t phyadd_common);

-//void config_gphy_port(UINT8, UINT8);

-

-void set_gphy_reg_cl22(uint8_t, uint8_t, uint16_t);

-uint16_t get_gphy_reg_cl45(uint8_t, uint8_t, uint16_t);

-void set_gphy_reg_cl45(uint8_t, uint8_t, uint16_t, uint16_t);

-void anacal_exe(uint8_t);

-

-#endif /* _AN8855_PHY_H_ */

-

+/* FILE NAME:  an8855_phy.h
+ * PURPOSE:
+ *      It provides AN8855 phy definition.
+ * NOTES:
+ *
+ */
+
+#ifndef _AN8855_PHY_H_
+#define _AN8855_PHY_H_
+
+
+/* Type Definitions */
+#define int8_t char
+#define uint8_t unsigned char
+#define int16_t short
+#define uint16_t unsigned short
+#define int32_t int
+#define uint32_t unsigned int
+/* DATA TYPE DECLARATIONS
+ */
+typedef int                 BOOL_T;
+typedef signed char         I8_T;
+typedef unsigned char       UI8_T;
+typedef signed short        I16_T;
+typedef unsigned short      UI16_T;
+typedef signed int          I32_T;
+typedef unsigned int        UI32_T;
+typedef char                C8_T;
+typedef unsigned long long  UI64_T;
+
+typedef UI8_T   AIR_MAC_T[6];
+
+/* Debug flags */
+//#define _MDIO_BOOTS_MODE     1 // Boots for preamble
+#define _DEBUG_PRINT         1 // Debug print for Arduino
+//#define _DEBUG_PRINT_eFuse     1
+#define _write_eFuse     1
+
+#define _DEBUG_SCAN_ALL      0 // Scan all Code for SerDes Calibration
+#define _WRITE_RG_DIR        1 // Write RG directly for Calibration
+#define _USER_DEFINE_MODE    0 // Replace to user-defined RG for Calibration
+#define _DEBUG_MANUAL        1 // dbg_20210604  // manual dbg_20210604
+/**************************************************************************/
+
+/* Phy Address */
+//#define phyadd_common 0x1d            // EN8801
+//#define PHY_NUM 1             // EN8801
+//#define phyadd_common 0x9         // EN7523
+//#define PHY_NUM 4             // EN7523
+//#define phyadd_common 0x0         // EN8850
+//#define PHY_NUM   5           // EN8850
+#define PHY_NUM   4             // EN8851
+#define CALIPOLARITY  1
+
+#define TXAMP_offset  0    // for 8851
+
+/* On/Off */
+//#define ENABLE  1
+//#define DISABLE 0
+#define Relay_ENABLE  1
+#define Relay_DISABLE 0
+
+/* FT Pattern */
+#define _MDIO     0x0
+#define _I2C      0x1
+#define FT_USB3_T101    0x0
+#define FT_PCIE0_T101   0x1
+#define FT_PCIE1_T101   0x2
+#define FT_PON_T101     0x3
+
+/********* Digital pin definition *************/
+#define Relay_Tx_PN         22   // relay 1
+#define Relay_R_R50         23   // relay 2
+#define Relay_Tx_Vol        24   // relay 3
+#define Relay_Rx_Vol        25   // relay 4
+#define Relay_DUT_GND       26   // relay 5
+#define Relay_I2C           27   // PIN for SCL&SDA , relay 6
+//#define Relay_I2C_SCL       27   // PIN for SCL&SDA , relay 6
+//#define Relay_I2C_SDA       28   // PIN for SCL&SDA , relay 6
+
+#define pin_MDIO           36   // PIN for MDIO
+#define pin_MDC            37   // PIN for MDC
+#define FT_PATTERN_bit0    49   // PIN for FT0
+#define FT_PATTERN_bit1    48   // PIN for FT1
+#define Relay_MDIO         35   // PIN for MDIO relay, relay 7
+
+/***********************************************/
+/* Use for I/O register PORTA control */
+#define POR_Relay_Tx_PN    D22   // use for PORTA control, relay 1
+#define POR_Relay_R_R50    D23   // use for PORTA control, relay 2
+#define POR_Relay_Tx_Vol   D24   // use for PORTA control, relay 3
+#define POR_Relay_Rx_Vol   D25   // use for PORTA control, relay 4
+#define POR_Relay_DUT_GND  D26   // use for PORTA control, relay 5
+//#define POR_Relay_I2C      D27   // use for PORTA control, relay 6
+#define POR_Relay_I2C_SCL  D27   // use for PORTA control, relay 6
+#define POR_Relay_I2C_SDA  D28   // use for PORTA control, relay 7
+
+/* Use for I/O register PORTC control */
+#define POR_MDIO               D36  // use for PORTC control
+#define POR_MDC                D37  // use for PORTC control
+#define POR_Relay_MDIO         D35  // use for PORTC control, relay 7
+
+/* Use for I/O register PORTL control */
+#define POR_FT_PATTERN_bit0    D49  // use for PORTL control
+#define POR_FT_PATTERN_bit1    D48  // use for PORTL control
+
+
+/* I/O register Port A */
+#define D22   0
+#define D23   1
+#define D24   2
+#define D25   3
+#define D26   4
+#define D27   5
+#define D28   6
+#define D29   7
+
+/* I/O register Port C */
+#define D37   0
+#define D36   1
+#define D35   2
+#define D34   3
+#define D33   4
+#define D32   5
+#define D31   6
+#define D30   7
+
+/* I/O register Port L */
+#define D49   0
+#define D48   1
+#define D47   2
+#define D46   3
+#define D45   4
+#define D44   5
+#define D43   6
+#define D42   7
+
+/* I/O register Port D */
+#define D21   0
+#define D20   1
+#define D19   2
+#define D18   3
+
+
+/***************************************************************************
+**************************************************************************
+* MDC/MDIO
+***************************************************************************
+***************************************************************************/
+#define SET_HIGH(data, nbit) ((data)|=(nbit))
+#define SET_LOW(data, nbit) ((data)&=~(nbit))
+
+#define MDIO_ONE  _BV(POR_MDIO)
+#define MDIO_ZERO 0x00
+#define MDC_ONE   _BV(POR_MDC)
+#define MDC_ZERO  0x00
+
+#define delay_us delayMicroseconds(0)
+
+#define ANACAL_INIT        0x01
+#define ANACAL_ERROR       0xFD
+#define ANACAL_SATURATION  0xFE
+#define ANACAL_FINISH      0xFF
+#define ANACAL_PAIR_A      0
+#define ANACAL_PAIR_B      1
+#define ANACAL_PAIR_C      2
+#define ANACAL_PAIR_D      3
+#define DAC_IN_0V          0x000
+#define DAC_IN_2V          0x0f0  // +/-1V
+
+#define ZCAL_MIDDLE        0x20
+#define TX_OFFSET_0mV_idx  31
+#define TX_AMP_MIDDLE      0x20
+
+#define TX_i2mpb_hbt_ofs  0x4   // 8851 fine tune 100M v1 (20220414)
+#define R50_OFFSET_VALUE  0x5
+
+//============== definition value for GbE ===================//
+#define BG_VOLTAGE_OUT     0xc0
+#define FORCE_MDI          2
+#define FORCE_MDIX         3
+#define LDO_1p15_VOSEL_1   1
+#define RX_CAL_VALUE_9       0x3
+#define RX_CAL_HVGA_BW_2     0x2
+#define RX_CAL_DCO_Normal    0x0
+#define RX_CAL_DCO_BYPASS_TX_RX  0x3
+#define RX_CAL_DCO_0xF    0xF
+
+#define TANA_MON_DCV_SEL__MASK         0xE0
+#define TANA_MON_DCV_SEL__MPX_TANA_A   0x20
+#define TANA_MON_DCV_SEL__MPX_TANA_B   0x40
+#define TANA_MON_DCV_SEL__MPX_TANA_C   0x60
+#define TANA_MON_DCV_SEL__MPX_TANA_D   0x80
+#define TANA_MON_DCV_SEL__MONVC__MASK  0x008000C8
+#define TANA_MON_DCV__TANA__VBG_MON    0x000000C0
+#define TANA_MON_DCV__TANA__MONVC      0x000000C8
+
+#define AN_disable_force_1000M 0x0140
+#define BG_voltage_output 0xc000
+#define Fix_mdi 0x1010
+#define Disable_tx_slew_control 0x0000
+#define LDO_control 0x0100
+#define Cal_control_BG 0x1110
+#define Cal_control_R50 0x1100
+#define Cal_control_TX_AMP 0x1100
+#define Cal_control_TX_OFST 0x0100
+#define Cal_control_R50_pairA_ENABLE 0x1101
+#define Disable_all 0x0
+#define Zcalen_A_ENABLE 0x0000
+#define Zcalen_B_ENABLE 0x1000
+#define Zcalen_C_ENABLE 0x0100
+#define Zcalen_D_ENABLE 0x0010
+#define MASK_MSB_8bit 0xff00
+#define MASK_LSB_8bit 0x00ff
+#define MASK_r50ohm_rsel_tx_a 0x7f00
+#define MASK_r50ohm_rsel_tx_b 0x007f
+#define MASK_r50ohm_rsel_tx_c 0x7f00
+#define MASK_r50ohm_rsel_tx_d 0x007f
+#define Rg_r50ohm_rsel_tx_a_en 0x8000
+#define Rg_r50ohm_rsel_tx_b_en 0x0080
+#define Rg_r50ohm_rsel_tx_c_en 0x8000
+#define Rg_r50ohm_rsel_tx_d_en 0x0080
+#define Rg_txvos_calen_ENABLE 0x0001
+#define Bypass_tx_offset_cal 0x8000
+#define Enable_Tx_VLD 0xf808
+#define Rg_txg_calen_a_ENABLE 0x1000
+#define Rg_txg_calen_b_ENABLE 0x0100
+#define Rg_txg_calen_c_ENABLE 0x0010
+#define Rg_txg_calen_d_ENABLE 0x0001
+#define Force_dasn_dac_in0_ENABLE 0x8000
+#define Force_dasn_dac_in1_ENABLE 0x8000
+#define MASK_cr_tx_amp_offset_MSB 0x3f00
+#define MASK_cr_tx_amp_offset_LSB 0x003f
+#define Rg_cal_refsel_ENABLE 0x0010
+#define MASK_da_tx_i2mpb_a_gbe 0xfc00
+#define MASK_da_tx_i2mpb_b_c_d_gbe 0x3f00
+
+#define LED_basic_control_en_active_low 0x800a
+#define LED_led0_en_active_high 0xc007
+#define LED_led0_force_blinking 0x0200
+
+
+
+/*phy calibration use*/
+//Type defines
+typedef unsigned char    UINT8;
+typedef unsigned short   UINT16;
+typedef unsigned long    UINT32;
+
+typedef struct
+{
+  UINT16 DATA_Lo;
+  UINT8  DATA_Hi;
+}TR_DATA_T;
+
+//CL22 Reg Support Page Select//
+#define RgAddr_Reg1Fh        0x1f
+#define CL22_Page_Reg        0x0000
+#define CL22_Page_ExtReg     0x0001
+#define CL22_Page_MiscReg    0x0002
+#define CL22_Page_LpiReg     0x0003
+#define CL22_Page_tReg       0x02A3
+#define CL22_Page_TrReg      0x52B5
+
+//CL45 Reg Support DEVID//
+#define DEVID_03             0x03
+#define DEVID_07             0x07
+#define DEVID_1E             0x1E
+#define DEVID_1F             0x1F
+
+//TokenRing Reg Access//
+#define TrReg_PKT_XMT_STA    0x8000
+#define TrReg_WR             0x8000
+#define TrReg_RD             0xA000
+
+/* ----------------- gephy_all Bit Field Definitions ------------------- */
+
+
+
+//-------------------------------------
+//0x0000
+#define RgAddr_Reg00h                               0x00
+
+//0x51e01200
+#define RgAddr_dev1Eh_reg120h                       0x0120
+//0x51e01220
+#define RgAddr_dev1Eh_reg122h                       0x0122
+//0x51e01440
+#define RgAddr_dev1Eh_reg144h                       0x0144
+//0x51e014a0
+#define RgAddr_dev1Eh_reg14Ah                       0x014a
+//0x51e019b0
+#define RgAddr_dev1Eh_reg19Bh                       0x019b
+//0x51e02340
+#define RgAddr_dev1Eh_reg234h                       0x0234
+//0x51e02380
+#define RgAddr_dev1Eh_reg238h                       0x0238
+//0x51e02390
+#define RgAddr_dev1Eh_reg239h                       0x0239
+//0x51f02680
+#define RgAddr_dev1Fh_reg268h                       0x0268
+//0x51e02d10
+#define RgAddr_dev1Eh_reg2D1h                       0x02d1
+//0x51e03230
+#define RgAddr_dev1Eh_reg323h                       0x0323
+//0x51e03240
+#define RgAddr_dev1Eh_reg324h                       0x0324
+//0x51e03260
+#define RgAddr_dev1Eh_reg326h                       0x0326
+
+//0x51f01000
+#define RgAddr_dev1Fh_reg100h                       0x0100
+//0x51e01450
+#define RgAddr_dev1Eh_reg145h                       0x0145
+//0x51f00ff0
+#define RgAddr_dev1Fh_reg0FFh                       0x00ff
+//0x51e00db0
+#define RgAddr_dev1Eh_reg0DBh                       0x00db
+//0x51e00dc0
+#define RgAddr_dev1Eh_reg0DCh                       0x00dc
+//0x51e00e00
+#define RgAddr_dev1Eh_reg0E0h                       0x00e0
+//0x51e00e10
+#define RgAddr_dev1Eh_reg0E1h                       0x00e1
+//0x51e00e00
+#define RgAddr_dev1Eh_reg0E0h                       0x00e0
+//0x51e017a0
+#define RgAddr_dev1Eh_reg17Ah                       0x017a
+//0x51f01150
+#define RgAddr_dev1Fh_reg115h                       0x0115
+//0x51f01000
+#define RgAddr_dev1Fh_reg100h                       0x0100
+//0x51e01450
+#define RgAddr_dev1Eh_reg145h                       0x0145
+//0x51e01450
+#define RgAddr_dev1Eh_reg145h                       0x0145
+//0x51e01850
+#define RgAddr_dev1Eh_reg185h                       0x0185
+//0x51e00fb0
+#define RgAddr_dev1Eh_reg0FBh                       0x00fb
+//0x51e01740
+#define RgAddr_dev1Eh_reg174h                       0x0174
+//0x51e01750
+#define RgAddr_dev1Eh_reg175h                       0x0175
+//0x51e01850
+#define RgAddr_dev1Eh_reg185h                       0x0185
+//0x51e00fb0
+#define RgAddr_dev1Eh_reg0FBh                       0x00fb
+//0x51e00960
+#define RgAddr_dev1Eh_reg096h                       0x0096
+//0x51e003e0
+#define RgAddr_dev1Eh_reg03Eh                       0x003e
+//0x51e00dd0
+#define RgAddr_dev1Eh_reg0DDh                       0x00dd
+//0x51e017d0
+#define RgAddr_dev1Eh_reg17Dh                       0x017d
+//0x51e01810
+#define RgAddr_dev1Eh_reg181h                       0x0181
+//0x51e00120
+#define RgAddr_dev1Eh_reg012h                       0x0012
+//0x51e017e0
+#define RgAddr_dev1Eh_reg17Eh                       0x017e
+//0x51e01820
+#define RgAddr_dev1Eh_reg182h                       0x0182
+//0x51e00170
+#define RgAddr_dev1Eh_reg017h                       0x0017
+//0x51e01830
+#define RgAddr_dev1Eh_reg183h                       0x0183
+//0x51e00190
+#define RgAddr_dev1Eh_reg019h                       0x0019
+//0x51e01800
+#define RgAddr_dev1Eh_reg180h                       0x0180
+//0x51e01840
+#define RgAddr_dev1Eh_reg184h                       0x0184
+//0x51e00210
+#define RgAddr_dev1Eh_reg021h                       0x0021
+//0x51e01720
+#define RgAddr_dev1Eh_reg172h                       0x0172
+//0x51e01730
+#define RgAddr_dev1Eh_reg173h                       0x0173
+//0x51e017c0
+#define RgAddr_dev1Eh_reg17Ch                       0x017c
+//0x51e017f0
+#define RgAddr_dev1Eh_reg17Fh                       0x017f
+
+//0x52b5100
+#define RgAddr_TrReg10h                             0x10
+//0x52b5110
+#define RgAddr_TrReg11h                             0x11
+//0x52b5120
+#define RgAddr_TrReg12h                             0x12
+
+//0x31c0
+#define RgAddr_LpiReg1Ch                            0x1c
+//0x31d0
+#define RgAddr_LpiReg1Dh                            0x1d
+uint8_t BG_Calibration(uint8_t phyadd, int8_t calipolarity);
+uint8_t R50_Calibration(uint8_t phyadd, uint8_t phyadd_common);
+uint8_t TX_OFS_Calibration(uint8_t phyadd, uint8_t phyadd_common);
+uint8_t TX_AMP_Calibration(uint8_t phyadd, uint8_t phyadd_common);
+//void config_gphy_port(UINT8, UINT8);
+
+void set_gphy_reg_cl22(uint8_t, uint8_t, uint16_t);
+uint16_t get_gphy_reg_cl45(uint8_t, uint8_t, uint16_t);
+void set_gphy_reg_cl45(uint8_t, uint8_t, uint16_t, uint16_t);
+void anacal_exe(uint8_t);
+
+#endif /* _AN8855_PHY_H_ */
+
diff --git a/recipes-devtools/switch/files/src/an8855_sdk/core/an8855_phy_cal.c b/recipes-devtools/switch/files/src/an8855_sdk/core/an8855_phy_cal.c
index c2e8e5c..9eb52e1 100644
--- a/recipes-devtools/switch/files/src/an8855_sdk/core/an8855_phy_cal.c
+++ b/recipes-devtools/switch/files/src/an8855_sdk/core/an8855_phy_cal.c
@@ -1,967 +1,967 @@
-/* FILE NAME:  an8855_phy_cal.c

-* PURPOSE:

-*    It provides an8855 switch phy calibration function.

-*

-* NOTES:

-*

-*/

-

-/* INCLUDE FILE DECLARATIONS

-*/

-#include "an8855_mdio.h"

-#include "an8855_phy.h"

-//#include "swk_gphy_reg.h"

-//#include "gphy_calibration.h"

-//#include "gsw_reg.h"

-

-/* NAMING CONSTANT DECLARATIONS

- */

-#define MII_BMCR                (0)

-#define BMCR_PDOWN              (0x0800)

-/* MACRO FUNCTION DECLARATIONS

- */

-

-#define FULL_BITS(_n_) ((1UL << (_n_)) - 1)

-

-/* DATA TYPE DECLARATIONS

- */

-

-/* GLOBAL VARIABLE DECLARATIONS

- */

-/* Zcal to R50 mapping table (20220404) */

-const uint8_t ZCAL_TO_R50ohm_TBL[64] =

-{

-    127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,

-    127, 127, 127, 127, 127, 123, 118, 114, 110, 106, 102, 98, 96, 92, 88, 85,

-    82, 80, 76, 72, 70, 67, 64, 62, 60, 56, 54, 52, 49, 48, 45, 43,

-    40, 39, 36, 34, 32, 32, 30, 28, 25, 24, 22, 20, 18, 16, 16, 14

-};

-

-/* Tx offset table, value is from small to big */

-const uint8_t  EN753x_TX_OFS_TBL[64] =

-{

-    0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30,

-    0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20,

-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,

-    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f

-};

-

-#define TOTAL_PATCH_C45_ITEMS   (15)

-#define TOTAL_PATCH_TR_ITEMS    (19)

-const uint16_t C45_PATCH_TABLE[TOTAL_PATCH_C45_ITEMS][3] =

-{

-    {0x1E, 0x120, 0x8014},

-    {0x1E, 0x122, 0xFFFF},

-    {0x1E, 0x122, 0xFFFF},

-    {0x1E, 0x144, 0x0200},

-    {0x1E, 0x14A, 0xEE20},

-    {0x1E, 0x189, 0x0110},

-    {0x1E, 0x19B, 0x0111},

-    {0x1E, 0x234, 0x0181},

-    {0x1E, 0x238, 0x0120},

-    {0x1E, 0x239, 0x0117},

-    {0x1F, 0x268, 0x07F4},

-    {0x1E, 0x2d1, 0x0733},

-    {0x1E, 0x323, 0x0011},

-    {0x1E, 0x324, 0x013F},

-    {0x1E, 0x326, 0x0037},

-};

-

-const uint32_t TR_PATCH_TABLE[TOTAL_PATCH_TR_ITEMS][2] =

-{

-    {0x83AA, 0x055a0 },

-    {0x83AE, 0x7FF3F },

-    {0x8F80, 0x0001e },

-    {0x8F82, 0x6FB90A},

-    {0x8FAE, 0x060671},

-    {0x8FB0, 0xE2F00 },

-    {0x8ECC, 0x444444},

-    {0x9686, 0x00000 },

-    {0x968C, 0x2EBAEF},

-    {0x9690, 0x00000b},

-    {0x9698, 0x0504D },

-    {0x969A, 0x2314f },

-    {0x969E, 0x03028 },

-    {0x96A0, 0x05010 },

-    {0x96A2, 0x40001 },

-    {0x96A6, 0x018670},

-    {0x96A8, 0x0024A },

-    {0x96B6, 0x00072 },

-    {0x96B8, 0x03210 },

-};

-

-#define TOTAL_NUMBER_OF_PATCH    (14)

-static uint16_t eee_patch_table[TOTAL_NUMBER_OF_PATCH][2] = {

-    {RgAddr_dev1Eh_reg120h, 0x8014},

-    {RgAddr_dev1Eh_reg122h, 0xFFFF},

-    {RgAddr_dev1Eh_reg122h, 0xFFFF},

-    {RgAddr_dev1Eh_reg144h, 0x0200},

-    {RgAddr_dev1Eh_reg14Ah, 0xEE20},

-    {RgAddr_dev1Eh_reg19Bh, 0x0111},

-    {RgAddr_dev1Eh_reg234h, 0x1181},

-    {RgAddr_dev1Eh_reg238h, 0x0120},

-    {RgAddr_dev1Eh_reg239h, 0x0117},

-    {RgAddr_dev1Fh_reg268h, 0x07F4},

-    {RgAddr_dev1Eh_reg2D1h, 0x0733},

-    {RgAddr_dev1Eh_reg323h, 0x0011},

-    {RgAddr_dev1Eh_reg324h, 0x013F},

-    {RgAddr_dev1Eh_reg326h, 0x0037}

-};

-

-#define TOTAL_NUMBER_OF_TR      (19)

-static uint16_t tr_reg_table[TOTAL_NUMBER_OF_TR][3] = {

-    {0x55A0, 0x0000, 0x83AA},

-    {0xFF3F, 0x0007, 0x83AE},

-    {0x001E, 0x0000, 0x8F80},

-    {0xB90A, 0x006F, 0x8F82},

-    {0x0671, 0x0006, 0x8FAE},

-    {0x2F00, 0x000E, 0x8FB0},

-    {0x4444, 0x0044, 0x8ECC},

-    {0x0004, 0x0000, 0x9686},

-    {0xBAEF, 0x002E, 0x968C},

-    {0x000B, 0x0000, 0x9690},

-    {0x504D, 0x0000, 0x9698},

-    {0x314F, 0x0002, 0x969A},

-    {0x3028, 0x0000, 0x969E},

-    {0x5010, 0x0000, 0x96A0},

-    {0x0001, 0x0004, 0x96A2},

-    {0x8670, 0x0001, 0x96A6},

-    {0x024A, 0x0000, 0x96A8},

-    {0x0072, 0x0000, 0x96B6},

-    {0x3210, 0x0000, 0x96B8}

-};

-

-void TR_RegWr(uint16_t phyadd, uint16_t tr_reg_addr, uint32_t tr_data);

-

-uint16_t get_gphy_reg_cl22(uint8_t phyad, uint8_t reg)

-{

-    uint32_t rdata = 0;

-

-    an8855_phy_read(phyad-g_smi_addr, reg, &rdata);

-

-    return ((uint16_t)rdata);

-    /*

-    gsw_top_reg_REG_PHY_IAC REG_PHY_IAC_val;

-    gsw_top_reg_REG_PHY_IAD REG_PHY_IAD_val;

-

-    // Wait until done

-    do

-    {

-      REG_PHY_IAC_val.Raw   = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAC);

-    }

-    while(REG_PHY_IAC_val.Bits.csr_phy_acs_st);

-

-    // Set address

-    REG_PHY_IAC_val.Bits.csr_mdio_st = 1;

-    REG_PHY_IAC_val.Bits.csr_mdio_cmd   = 2;

-    REG_PHY_IAC_val.Bits.csr_mdio_phy_addr = phyad;

-    REG_PHY_IAC_val.Bits.csr_mdio_reg_addr = reg;

-    REG_PHY_IAC_val.Bits.csr_phy_acs_st =   1;

-    io_write32(RgAddr_gsw_top_reg_REG_PHY_IAC, REG_PHY_IAC_val.Raw);

-    // Wait until done

-    do

-    {

-        REG_PHY_IAC_val.Raw = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAC);

-    }

-    while(REG_PHY_IAC_val.Bits.csr_phy_acs_st);

-

-    REG_PHY_IAD_val.Raw = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAD);

-

-    return REG_PHY_IAD_val.Raw;

-    */

-}

-

-/* EXPORTED SUBPROGRAM BODIES

- */

-void gphy_config(void)

-{

-    uint8_t port = 1;

-    uint8_t phy_base = 0, phys_in_chip = 8;

-

-    for (port = 1; port <= phys_in_chip; port++)

-    {

-        set_gphy_reg_cl45(phy_base + port, 0x7, 0x3c, 0x0006); // Enable EEE

-        set_gphy_reg_cl45(phy_base + port, 0x1e, 0x3e, 0xf000); // force on TXVLD

-    }

-}

-

-static void set_gphy_TrReg(uint8_t prtid, uint16_t parm_1, uint16_t parm_2, uint16_t parm_3)

-{

-    set_gphy_reg_cl22(prtid, RgAddr_TrReg11h, parm_1);

-    set_gphy_reg_cl22(prtid, RgAddr_TrReg12h, parm_2);

-    set_gphy_reg_cl22(prtid, RgAddr_TrReg10h, parm_3);

-}

-

-static void gphy_eee_patch(uint8_t phy_base)

-{

-    UI8_T   port = 1, index = 0, phy_addr = 1;

-    UI16_T  data = 0;

-

-    for (port = 1; port <=8; port++)

-    {

-        phy_addr = phy_base + port;

-        data = get_gphy_reg_cl22(phy_addr, MII_BMCR);

-        set_gphy_reg_cl22(phy_addr, MII_BMCR, data & ~(BMCR_PDOWN));    /* PHY power on */

-

-        /* Change EEE RG default value */

-        for (index = 0; index < TOTAL_NUMBER_OF_PATCH; index++)

-        {

-            set_gphy_reg_cl45(phy_addr, DEVID_1E, eee_patch_table[index][0], eee_patch_table[index][1]);

-        }

-

-        set_gphy_reg_cl22(phy_addr, RgAddr_Reg1Fh, CL22_Page_TrReg);   /* change CL22page to LpiReg(0x3) */

-        for (index = 0; index < TOTAL_NUMBER_OF_TR; index++)

-        {

-            set_gphy_TrReg(phy_addr, tr_reg_table[index][0], tr_reg_table[index][1], tr_reg_table[index][2]);

-        }

-

-        set_gphy_reg_cl22(phy_addr, RgAddr_Reg1Fh, CL22_Page_LpiReg);  /* change CL22page to LpiReg(0x3) */

-        set_gphy_reg_cl22(phy_addr, RgAddr_LpiReg1Ch, 0x0c92);         /* Fine turn SigDet for B2B LPI link down issue */

-        set_gphy_reg_cl22(phy_addr, RgAddr_LpiReg1Dh, 0x0001);         /* Enable "lpi_quit_waitafesigdet_en" for LPI link down issue */

-

-        set_gphy_reg_cl22(phy_addr, RgAddr_Reg1Fh, CL22_Page_Reg);     /* change CL22page to Reg(0x0) */

-    }

-}

-

-void gphy_calibration(uint8_t phy_base)

-{

-    uint8_t port = 1, phy_addr = 1 ,phy_group = 1, index = 0;

-    uint8_t phys_in_chip = 5;

-

-    BG_Calibration(phy_base, 0x1);

-    if (phys_in_chip > 4)

-    {

-        BG_Calibration(phy_base + 0x4, 0x1);

-    }

-

-    for (port = 0; port < phys_in_chip; port++)

-    {

-        if (port < 4)

-        {

-            phy_group = phy_base;     /* PHY group 1 */

-        }

-        else

-        {

-            phy_group = phy_base + 0x04;     /* PHY group 2 */

-        }

-        phy_addr = phy_base + port;

-        R50_Calibration(phy_addr, phy_group);

-        TX_OFS_Calibration(phy_addr, phy_group);

-        TX_AMP_Calibration(phy_addr, phy_group);

-    }

-

-    for (port = 0; port < phys_in_chip; port++)

-    {

-        phy_addr = phy_base + port;

-        set_gphy_reg_cl45(phy_addr, 0x1e, 0x017d, 0x0000);

-        set_gphy_reg_cl45(phy_addr, 0x1e, 0x017e, 0x0000);

-        set_gphy_reg_cl45(phy_addr, 0x1e, 0x017f, 0x0000);

-        set_gphy_reg_cl45(phy_addr, 0x1e, 0x0180, 0x0000);

-        set_gphy_reg_cl45(phy_addr, 0x1e, 0x0181, 0x0000);

-        set_gphy_reg_cl45(phy_addr, 0x1e, 0x0182, 0x0000);

-        set_gphy_reg_cl45(phy_addr, 0x1e, 0x0183, 0x0000);

-        set_gphy_reg_cl45(phy_addr, 0x1e, 0x0184, 0x0000);

-        set_gphy_reg_cl45(phy_addr, 0x1e, 0x00db, 0x0000);  // disable analog calibration circuit

-        set_gphy_reg_cl45(phy_addr, 0x1e, 0x00dc, 0x0000);  // disable Tx offset calibration circuit

-        set_gphy_reg_cl45(phy_addr, 0x1e, 0x003e, 0x0000);  // disable Tx VLD force mode

-        set_gphy_reg_cl45(phy_addr, 0x1e, 0x00dd, 0x0000);  // disable Tx offset/amplitude calibration circuit

-        set_gphy_reg_cl45(phy_addr, 0x1e, 0x0145, 0x1000);  // enable auto MDI/MDIX

-

-        set_gphy_reg_cl22(phy_addr, 0, 0x1200);

-        /* GPHY Rx low pass filter */

-        set_gphy_reg_cl45(phy_addr, 0x1e, 0xc7, 0xd000);

-        /* patch */

-        for (index = 0; index < TOTAL_PATCH_C45_ITEMS; index++)

-        {

-            set_gphy_reg_cl45(phy_addr, C45_PATCH_TABLE[index][0], C45_PATCH_TABLE[index][1], C45_PATCH_TABLE[index][2]);

-        }

-        for (index = 0; index < TOTAL_PATCH_TR_ITEMS; index++)

-        {

-            TR_RegWr(phy_addr, TR_PATCH_TABLE[index][0], TR_PATCH_TABLE[index][1]);

-        }

-        set_gphy_reg_cl22(phy_addr, 0x1f, 0x0  );

-        set_gphy_reg_cl22(phy_addr, 0x1f, 0x3  );

-        set_gphy_reg_cl22(phy_addr, 0x1c, 0xc92);

-        set_gphy_reg_cl22(phy_addr, 0x1d, 0x01 );

-        set_gphy_reg_cl22(phy_addr, 0x1f, 0x0  );

-    }

-    gphy_eee_patch(phy_base);

-}

-

-/* LOCAL SUBPROGRAM BODIES

- */

-void TR_RegWr(uint16_t phyadd, uint16_t tr_reg_addr, uint32_t tr_data)

-{

-    set_gphy_reg_cl22(phyadd, 0x1F, 0x52b5);       /* page select */

-    set_gphy_reg_cl22(phyadd, 0x11, (uint16_t)(tr_data & 0xffff));

-    set_gphy_reg_cl22(phyadd, 0x12, (uint16_t)(tr_data >> 16));

-    set_gphy_reg_cl22(phyadd, 0x10, (uint16_t)(tr_reg_addr | TrReg_WR));

-    set_gphy_reg_cl22(phyadd, 0x1F, 0x0);          /* page resetore */

-    return;

-}

-

-uint8_t BG_Calibration(uint8_t phyadd, int8_t calipolarity)

-{

-    int8_t rg_zcal_ctrl = 0, calibration_polarity = 0;

-    uint8_t all_ana_cal_status = 1;

-    uint16_t ad_cal_comp_out_init = 0;

-

-    /* setting */

-    set_gphy_reg_cl22(phyadd, RgAddr_Reg1Fh, CL22_Page_Reg);        // g0

-    set_gphy_reg_cl22(phyadd, RgAddr_Reg00h, AN_disable_force_1000M);  // AN disable, force 1000M

-    set_gphy_reg_cl45(phyadd, DEVID_1F, RgAddr_dev1Fh_reg100h, BG_voltage_output);// BG voltage output

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg145h, Fix_mdi);// fix mdi

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Fh_reg0FFh, 0x2);

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_BG);// 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Disable_all);// 1e_dc[0]:rg_txvos_calen

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0E1h, Disable_all);// 1e_e1[4]:rg_cal_refsel(0:1.2V) enable BG 1.2V to REXT PAD

-

-    /* calibrate */

-    rg_zcal_ctrl = ZCAL_MIDDLE;

-

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0E0h, (uint16_t)rg_zcal_ctrl);

-

-    anacal_exe(phyadd);

-    if (all_ana_cal_status == 0)

-    {

-        all_ana_cal_status = ANACAL_ERROR;

-    }

-    ad_cal_comp_out_init = (get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Ah) >> 8) & 0x1;

-

-

-    if (ad_cal_comp_out_init == 1)

-    {

-        calibration_polarity = -calipolarity;

-    }

-    else // ad_cal_comp_out_init == 0

-    {

-        calibration_polarity = calipolarity;

-    }

-

-    while (all_ana_cal_status < ANACAL_ERROR)

-    {

-        rg_zcal_ctrl += calibration_polarity;

-

-        set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0E0h, (uint16_t)rg_zcal_ctrl);

-

-

-        anacal_exe(phyadd);

-

-        if (all_ana_cal_status == 0)

-        {

-            all_ana_cal_status = ANACAL_ERROR;

-        }

-

-        else if (((get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Ah) >> 8) & 0x1) != ad_cal_comp_out_init)

-        {

-            all_ana_cal_status = ANACAL_FINISH;

-        }

-        else

-        {

-            if ((rg_zcal_ctrl == 0x3F) || (rg_zcal_ctrl == 0x00))

-            {

-                all_ana_cal_status = ANACAL_SATURATION;  // need to FT

-                rg_zcal_ctrl = ZCAL_MIDDLE;  // 0 dB

-            }

-        }

-    }

-

-    if (all_ana_cal_status == ANACAL_ERROR)

-    {

-        rg_zcal_ctrl = ZCAL_MIDDLE;  // 0 dB

-

-        set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0E0h, (uint16_t)rg_zcal_ctrl);

-    }

-    else

-    {

-        // rg_zcal_ctrl[5:0] rg_rext_trim[13:8]

-        set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0E0h, (uint16_t)((rg_zcal_ctrl << 8) | rg_zcal_ctrl));

-

-        // 1f_115[2:0](rg_bg_rasel) = rg_zcal_ctrl[5:3]

-        set_gphy_reg_cl45(phyadd, DEVID_1F, RgAddr_dev1Fh_reg115h, (uint16_t)((rg_zcal_ctrl & 0x3f) >> 3));

-    }

-

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Disable_all);

-    return all_ana_cal_status;

-}

-

-uint8_t R50_Calibration(uint8_t phyadd, uint8_t phyadd_common)

-{

-    int8_t rg_zcal_ctrl = 0, rg_r50ohm_rsel_tx = 0, calibration_polarity = 0;

-    uint8_t all_ana_cal_status = 1;

-    int16_t backup_dev1e_e0 = 0, ad_cal_comp_out_init = 0, calibration_pair = 0;

-

-    /* setting */

-    set_gphy_reg_cl22(phyadd, RgAddr_Reg1Fh, CL22_Page_Reg);        // g0

-    set_gphy_reg_cl22(phyadd, RgAddr_Reg00h, AN_disable_force_1000M);  // AN disable, force 1000M

-

-    set_gphy_reg_cl45(phyadd_common, DEVID_1F, RgAddr_dev1Fh_reg100h, BG_voltage_output); // BG voltage output

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg145h, Fix_mdi); // fix mdi

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg185h, Disable_tx_slew_control); // disable tx slew control

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0FBh, LDO_control); // ldo

-    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_R50); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen

-    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Disable_all); // 1e_dc[0]:rg_txvos_calen

-    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0E1h, Disable_all); // 1e_e1[4]:rg_cal_refsel(0:1.2V) enable BG 1.2V to REXT PAD

-

-    for (calibration_pair = ANACAL_PAIR_A; calibration_pair <= ANACAL_PAIR_D; calibration_pair++)

-    {

-        all_ana_cal_status = 1;

-

-        if (calibration_pair == ANACAL_PAIR_A)

-        {

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_R50_pairA_ENABLE); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Zcalen_A_ENABLE);

-        }

-        else if (calibration_pair == ANACAL_PAIR_B)

-        {

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_R50); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Zcalen_B_ENABLE); // 1e_dc[12]:rg_zcalen_b

-        }

-        else if (calibration_pair == ANACAL_PAIR_C)

-        {

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_R50); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Zcalen_C_ENABLE); // 1e_dc[8]:rg_zcalen_c

-        }

-        else // if(calibration_pair == ANACAL_PAIR_D)

-        {

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_R50); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Zcalen_D_ENABLE); // 1e_dc[4]:rg_zcalen_d

-        }

-

-        /* calibrate */

-        rg_zcal_ctrl = ZCAL_MIDDLE;             // start with 0 dB

-

-        backup_dev1e_e0 = (get_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0E0h)&(~0x003f));

-        set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0E0h, (backup_dev1e_e0 | rg_zcal_ctrl));

-

-        anacal_exe(phyadd_common);

-        if (all_ana_cal_status == 0)

-        {

-            all_ana_cal_status = ANACAL_ERROR;

-        }

-

-        ad_cal_comp_out_init = (get_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg17Ah) >> 8) & 0x1;  // 1e_17a[8]:ad_cal_comp_out

-

-        if (ad_cal_comp_out_init == 1)

-        {

-            calibration_polarity = -1;

-        }

-        else

-        {

-            calibration_polarity = 1;

-        }

-

-        while (all_ana_cal_status < ANACAL_ERROR)

-        {

-            rg_zcal_ctrl += calibration_polarity;

-

-            set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0E0h, (backup_dev1e_e0 | rg_zcal_ctrl));

-

-            anacal_exe(phyadd_common);

-

-            if (all_ana_cal_status == 0)

-            {

-                all_ana_cal_status = ANACAL_ERROR;

-            }

-            else if (((get_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg17Ah) >> 8) & 0x1) != ad_cal_comp_out_init)

-            {

-                all_ana_cal_status = ANACAL_FINISH;

-            }

-            else

-            {

-                if ((rg_zcal_ctrl == 0x3F) || (rg_zcal_ctrl == 0x00))

-                {

-                    all_ana_cal_status = ANACAL_SATURATION;  // need to FT

-                    rg_zcal_ctrl = ZCAL_MIDDLE;  // 0 dB

-                }

-            }

-        }

-

-        if (all_ana_cal_status == ANACAL_ERROR)

-        {

-            rg_r50ohm_rsel_tx = ZCAL_MIDDLE;  // 0 dB

-        }

-        else

-        {

-            if (rg_zcal_ctrl > (0x3F - R50_OFFSET_VALUE))

-            {

-                all_ana_cal_status = ANACAL_SATURATION;  // need to FT

-                rg_zcal_ctrl = ZCAL_MIDDLE;  // 0 dB

-            }

-            else

-            {

-                rg_zcal_ctrl += R50_OFFSET_VALUE;

-            }

-

-            rg_r50ohm_rsel_tx = ZCAL_TO_R50ohm_TBL[rg_zcal_ctrl];

-        }

-

-        if (calibration_pair == ANACAL_PAIR_A)

-        {

-            // cr_r50ohm_rsel_tx_a

-            ad_cal_comp_out_init = get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg174h)&(~MASK_r50ohm_rsel_tx_a);

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg174h, (ad_cal_comp_out_init | (((rg_r50ohm_rsel_tx << 8) & MASK_MSB_8bit) | Rg_r50ohm_rsel_tx_a_en))); // 1e_174[15:8]

-        }

-        else if (calibration_pair == ANACAL_PAIR_B)

-        {

-            // cr_r50ohm_rsel_tx_b

-            ad_cal_comp_out_init = get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg174h)&(~MASK_r50ohm_rsel_tx_b);

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg174h, (ad_cal_comp_out_init | (((rg_r50ohm_rsel_tx << 0) & MASK_LSB_8bit) | Rg_r50ohm_rsel_tx_b_en))); // 1e_174[7:0]

-        }

-        else if (calibration_pair == ANACAL_PAIR_C)

-        {

-            // cr_r50ohm_rsel_tx_c

-            ad_cal_comp_out_init = get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg175h)&(~MASK_r50ohm_rsel_tx_c);

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg175h, (ad_cal_comp_out_init | (((rg_r50ohm_rsel_tx << 8) & MASK_MSB_8bit) | Rg_r50ohm_rsel_tx_c_en))); // 1e_175[15:8]

-        }

-        else // if(calibration_pair == ANACAL_PAIR_D)

-        {

-            // cr_r50ohm_rsel_tx_d

-            ad_cal_comp_out_init = get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg175h)&(~MASK_r50ohm_rsel_tx_d);

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg175h, (ad_cal_comp_out_init | (((rg_r50ohm_rsel_tx << 0) & MASK_LSB_8bit) | Rg_r50ohm_rsel_tx_d_en))); // 1e_175[7:0]

-        }

-    }

-

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Disable_all);

-    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Disable_all);

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Disable_all);

-

-    return all_ana_cal_status;

-}

-

-uint8_t TX_OFS_Calibration(uint8_t phyadd, uint8_t phyadd_common)

-{

-    int8_t tx_offset_index = 0, calibration_polarity = 0;

-    uint8_t all_ana_cal_status = 1, tx_offset_reg_shift = 0, tbl_idx = 0;

-    int16_t ad_cal_comp_out_init = 0, calibration_pair = 0, tx_offset_reg = 0, reg_temp = 0;

-

-    /* setting */

-    set_gphy_reg_cl22(phyadd, RgAddr_Reg1Fh, CL22_Page_Reg);        // g0

-    set_gphy_reg_cl22(phyadd, RgAddr_Reg00h, AN_disable_force_1000M);  // AN disable, force 1000M

-

-    set_gphy_reg_cl45(phyadd, DEVID_1F, RgAddr_dev1Fh_reg100h, BG_voltage_output); // BG voltage output

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg145h, Fix_mdi); // fix mdi

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg185h, Disable_tx_slew_control); // disable tx slew control

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0FBh, LDO_control); // ldo

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_TX_OFST); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Rg_txvos_calen_ENABLE); // 1e_dc[0]:rg_txvos_calen

-    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_TX_OFST); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen

-    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Rg_txvos_calen_ENABLE); // 1e_dc[0]:rg_txvos_calen

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0E1h, Disable_all); // 1e_e1[4]:rg_cal_refsel(0:1.2V) enable BG 1.2V to REXT PAD

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg096h, Bypass_tx_offset_cal); // 1e_96[15]:bypass_tx_offset_cal, Hw bypass, Fw cal

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg03Eh, Enable_Tx_VLD); // 1e_3e:enable Tx VLD

-

-    for (calibration_pair = ANACAL_PAIR_A; calibration_pair <= ANACAL_PAIR_D; calibration_pair++)

-    {

-        all_ana_cal_status = 1;

-

-        tbl_idx = TX_OFFSET_0mV_idx;

-        tx_offset_index = EN753x_TX_OFS_TBL[tbl_idx];

-

-        if (calibration_pair == ANACAL_PAIR_A)

-        {

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Rg_txg_calen_a_ENABLE);       // 1e_dd[12]:rg_txg_calen_a

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Dh, (Force_dasn_dac_in0_ENABLE | DAC_IN_0V));  // 1e_17d:dac_in0_a

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg181h, (Force_dasn_dac_in1_ENABLE | DAC_IN_0V));  // 1e_181:dac_in1_a

-

-            reg_temp = (get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg172h)&(~MASK_cr_tx_amp_offset_MSB));

-            tx_offset_reg_shift = 8;  // 1e_172[13:8]

-            tx_offset_reg = RgAddr_dev1Eh_reg172h;

-        }

-        else if (calibration_pair == ANACAL_PAIR_B)

-        {

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Rg_txg_calen_b_ENABLE);       // 1e_dd[8]:rg_txg_calen_b

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Eh, (Force_dasn_dac_in0_ENABLE | DAC_IN_0V));  // 1e_17e:dac_in0_b

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg182h, (Force_dasn_dac_in1_ENABLE | DAC_IN_0V));  // 1e_182:dac_in1_b

-

-            reg_temp = (get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg172h)&(~MASK_cr_tx_amp_offset_LSB));

-            tx_offset_reg_shift = 0;  // 1e_172[5:0]

-            tx_offset_reg = RgAddr_dev1Eh_reg172h;

-        }

-        else if (calibration_pair == ANACAL_PAIR_C)

-        {

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Rg_txg_calen_c_ENABLE);       // 1e_dd[4]:rg_txg_calen_c

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Fh, (Force_dasn_dac_in0_ENABLE | DAC_IN_0V));  // 1e_17f:dac_in0_c

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg183h, (Force_dasn_dac_in1_ENABLE | DAC_IN_0V));  // 1e_183:dac_in1_c

-

-            reg_temp = (get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg173h)&(~MASK_cr_tx_amp_offset_MSB));

-            tx_offset_reg_shift = 8;  // 1e_173[13:8]

-            tx_offset_reg = RgAddr_dev1Eh_reg173h;

-        }

-        else // if(calibration_pair == ANACAL_PAIR_D)

-        {

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Rg_txg_calen_d_ENABLE);       // 1e_dd[0]:rg_txg_calen_d

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg180h, (Force_dasn_dac_in0_ENABLE | DAC_IN_0V));  // 1e_180:dac_in0_d

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg184h, (Force_dasn_dac_in1_ENABLE | DAC_IN_0V));  // 1e_184:dac_in1_d

-

-            reg_temp = (get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg173h)&(~MASK_cr_tx_amp_offset_LSB));

-            tx_offset_reg_shift = 0;  // 1e_173[5:0]

-            tx_offset_reg = RgAddr_dev1Eh_reg173h;

-        }

-

-        /* calibrate */

-        //tx_offset_index = TX_AMP_OFFSET_0mV;

-        tbl_idx = TX_OFFSET_0mV_idx;

-        tx_offset_index = EN753x_TX_OFS_TBL[tbl_idx];

-        set_gphy_reg_cl45(phyadd, DEVID_1E, tx_offset_reg, (reg_temp | (tx_offset_index << tx_offset_reg_shift)));  // 1e_172, 1e_173

-

-        anacal_exe(phyadd_common);

-        if (all_ana_cal_status == 0)

-        {

-            all_ana_cal_status = ANACAL_ERROR;

-        }

-

-        ad_cal_comp_out_init = (get_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg17Ah) >> 8) & 0x1;  // 1e_17a[8]:ad_cal_comp_out

-

-        if (ad_cal_comp_out_init == 1)

-        {

-            calibration_polarity = -1;

-        }

-        else

-        {

-            calibration_polarity = 1;

-        }

-

-        while (all_ana_cal_status < ANACAL_ERROR)

-        {

-            tbl_idx += calibration_polarity;

-            tx_offset_index = EN753x_TX_OFS_TBL[tbl_idx];

-

-            set_gphy_reg_cl45(phyadd, DEVID_1E, tx_offset_reg, (reg_temp | (tx_offset_index << tx_offset_reg_shift)));  // 1e_172, 1e_173

-

-            anacal_exe(phyadd_common);

-

-            if (all_ana_cal_status == 0)

-            {

-                all_ana_cal_status = ANACAL_ERROR;

-            }

-            else if (((get_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg17Ah) >> 8) & 0x1) != ad_cal_comp_out_init)

-            {

-                all_ana_cal_status = ANACAL_FINISH;

-            }

-            else

-            {

-                if ((tx_offset_index == 0x3f) || (tx_offset_index == 0x1f))

-                {

-                    all_ana_cal_status = ANACAL_SATURATION;  // need to FT

-                }

-            }

-        }

-

-        if (all_ana_cal_status == ANACAL_ERROR)

-        {

-            tbl_idx = TX_OFFSET_0mV_idx;

-            tx_offset_index = EN753x_TX_OFS_TBL[tbl_idx];

-

-            set_gphy_reg_cl45(phyadd, DEVID_1E, tx_offset_reg, (reg_temp | (tx_offset_index << tx_offset_reg_shift)));  // cr_tx_amp_offset_a/b/c/d, 1e_172, 1e_173

-        }

-    }

-

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Dh, Disable_all);

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Eh, Disable_all);

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Fh, Disable_all);

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg180h, Disable_all);

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg181h, Disable_all);

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg182h, Disable_all);

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg183h, Disable_all);

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg184h, Disable_all);

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Disable_all); // disable analog calibration circuit

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Disable_all); // disable Tx offset calibration circuit

-    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Disable_all); // disable analog calibration circuit

-    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Disable_all); // disable Tx offset calibration circuit

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg03Eh, Disable_all); // disable Tx VLD force mode

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Disable_all); // disable Tx offset/amplitude calibration circuit

-

-    return all_ana_cal_status;

-}

-

-uint8_t TX_AMP_Calibration(uint8_t phyadd, uint8_t phyadd_common)

-{

-    int8_t tx_amp_index = 0, calibration_polarity = 0;

-    uint8_t all_ana_cal_status = 1, tx_amp_reg_shift = 0;

-    uint8_t tx_amp_reg = 0, tx_amp_reg_100 = 0, tst_offset = 0, hbt_offset = 0, gbe_offset = 0, tbt_offset = 0;

-    uint16_t ad_cal_comp_out_init = 0, calibration_pair = 0, reg_temp = 0;

-

-  //phyadd_common = phyadd;

-

-    /* setting */

-    set_gphy_reg_cl22(phyadd, RgAddr_Reg1Fh, CL22_Page_Reg);        // g0

-    set_gphy_reg_cl22(phyadd, RgAddr_Reg00h, AN_disable_force_1000M);  // AN disable, force 1000M

-

-    set_gphy_reg_cl45(phyadd, DEVID_1F, RgAddr_dev1Fh_reg100h, BG_voltage_output); // BG voltage output

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg145h, Fix_mdi); // fix mdi

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg185h, Disable_tx_slew_control); // disable tx slew control

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0FBh, LDO_control); // ldo

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_TX_AMP); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Rg_txvos_calen_ENABLE); // 1e_dc[0]:rg_txvos_calen

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0E1h, Rg_cal_refsel_ENABLE); // 1e_e1[4]:rg_cal_refsel(0:1.2V) enable BG 1.2V to REXT PAD

-    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_TX_AMP); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen

-    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Rg_txvos_calen_ENABLE); // 1e_dc[0]:rg_txvos_calen

-    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0E1h, Rg_cal_refsel_ENABLE); // 1e_e1[4]:rg_cal_refsel(0:1.2V) enable BG 1.2V to REXT PAD

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg096h, Bypass_tx_offset_cal); // 1e_96[15]:bypass_tx_offset_cal, Hw bypass, Fw cal

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg03Eh, Enable_Tx_VLD); // 1e_3e:enable Tx VLD

-

-    for (calibration_pair = ANACAL_PAIR_A; calibration_pair <= ANACAL_PAIR_D; calibration_pair++)

-    //for (calibration_pair = ANACAL_PAIR_A; calibration_pair <= ANACAL_PAIR_B; calibration_pair++) // debugging

-    {

-        all_ana_cal_status = 1;

-

-        /* calibrate */

-        tx_amp_index = TX_AMP_MIDDLE;   // start with 0 dB

-        if (calibration_pair == ANACAL_PAIR_A)

-        {

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Rg_txg_calen_a_ENABLE);       // 1e_dd[12]:rg_txg_calen_a amp calibration enable

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Dh, (Force_dasn_dac_in0_ENABLE | DAC_IN_2V));  // 1e_17d:dac_in0_a

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg181h, (Force_dasn_dac_in1_ENABLE | DAC_IN_2V));  // 1e_181:dac_in1_a

-

-            reg_temp = (get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg012h)&(~MASK_da_tx_i2mpb_a_gbe));

-            tx_amp_reg_shift = 10;  // 1e_12[15:10]

-            tx_amp_reg = RgAddr_dev1Eh_reg012h;

-            tx_amp_reg_100 = 0x16;

-        }

-        else if (calibration_pair == ANACAL_PAIR_B)

-        {

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Rg_txg_calen_b_ENABLE);       // 1e_dd[8]:rg_txg_calen_b amp calibration enable

-            //Serial.println(Rg_txg_calen_b_ENABLE, HEX);

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Eh, (Force_dasn_dac_in0_ENABLE | DAC_IN_2V));  // 1e_17e:dac_in0_b

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg182h, (Force_dasn_dac_in1_ENABLE | DAC_IN_2V));  // 1e_182:dac_in1_b

-

-            reg_temp = (get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg017h)&(~MASK_da_tx_i2mpb_b_c_d_gbe));

-            tx_amp_reg_shift = 8; // 1e_17[13:8]

-            tx_amp_reg = RgAddr_dev1Eh_reg017h;

-            tx_amp_reg_100 = 0x18;

-        }

-        else if (calibration_pair == ANACAL_PAIR_C)

-        {

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Rg_txg_calen_c_ENABLE);       // 1e_dd[4]:rg_txg_calen_c amp calibration enable

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Fh, (Force_dasn_dac_in0_ENABLE | DAC_IN_2V));  // 1e_17f:dac_in0_c

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg183h, (Force_dasn_dac_in1_ENABLE | DAC_IN_2V));  // 1e_183:dac_in1_c

-

-            reg_temp = (get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg019h)&(~MASK_da_tx_i2mpb_b_c_d_gbe));

-            tx_amp_reg_shift = 8; // 1e_19[13:8]

-            tx_amp_reg = RgAddr_dev1Eh_reg019h;

-            tx_amp_reg_100 = 0x20;

-        }

-        else //if(calibration_pair == ANACAL_PAIR_D)

-        {

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Rg_txg_calen_d_ENABLE);       // 1e_dd[0]:rg_txg_calen_d amp calibration enable

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg180h, (Force_dasn_dac_in0_ENABLE | DAC_IN_2V));  // 1e_180:dac_in0_d

-            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg184h, (Force_dasn_dac_in1_ENABLE | DAC_IN_2V));  // 1e_184:dac_in1_d

-

-            reg_temp = (get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg021h)&(~MASK_da_tx_i2mpb_b_c_d_gbe));

-            tx_amp_reg_shift = 8; // 1e_21[13:8]

-            tx_amp_reg = RgAddr_dev1Eh_reg021h;

-            tx_amp_reg_100 = 0x22;

-        }

-

-        /* calibrate */

-        tx_amp_index = TX_AMP_MIDDLE; // start with 0 dB

-

-        set_gphy_reg_cl45(phyadd, DEVID_1E, tx_amp_reg, (reg_temp | (tx_amp_index << tx_amp_reg_shift))); // 1e_12/17/19/21

-

-        anacal_exe(phyadd_common);

-        if (all_ana_cal_status == 0)

-        {

-            all_ana_cal_status = ANACAL_ERROR;

-        }

-

-

-        ad_cal_comp_out_init = (get_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg17Ah) >> 8) & 0x1;    // 1e_17a[8]:ad_cal_comp_out

-        //Serial.println(ad_cal_comp_out_init, HEX);

-

-        if (ad_cal_comp_out_init == 1)

-        {

-            calibration_polarity = -1;

-        }

-        else

-        {

-            calibration_polarity = 1;

-        }

-        while (all_ana_cal_status < ANACAL_ERROR)

-        {

-            tx_amp_index += calibration_polarity;

-            //Serial.println(tx_amp_index, HEX);

-

-            set_gphy_reg_cl45(phyadd, DEVID_1E, tx_amp_reg, (reg_temp | (tx_amp_index << tx_amp_reg_shift)));

-

-            anacal_exe(phyadd_common);

-

-            if (all_ana_cal_status == 0)

-            {

-                all_ana_cal_status = ANACAL_ERROR;

-            }

-            else if (((get_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg17Ah) >> 8) & 0x1) != ad_cal_comp_out_init)

-            {

-                all_ana_cal_status = ANACAL_FINISH;

-                //Serial.print("    tx_amp_index: ");

-                //Serial.println(tx_amp_index, HEX);

-                //reg_temp = get_gphy_reg_cl45(phyadd, 0x1e, tx_amp_reg)&(~0xff00);

-                //set_gphy_reg_cl45(phyadd, 0x1e, tx_amp_reg, (reg_temp|((tx_amp_index + tst_offset)<<tx_amp_reg_shift)));  // for gbe(DAC)

-            }

-            else

-            {

-                if ((tx_amp_index == 0x3f) || (tx_amp_index == 0x00))

-                {

-                    all_ana_cal_status = ANACAL_SATURATION;  // need to FT

-                    tx_amp_index = TX_AMP_MIDDLE;

-                }

-            }

-        }

-

-        if (all_ana_cal_status == ANACAL_ERROR)

-        {

-            tx_amp_index = TX_AMP_MIDDLE;

-        }

-

-        // da_tx_i2mpb_a_gbe / b/c/d, only GBE for now

-        set_gphy_reg_cl45(phyadd, DEVID_1E, tx_amp_reg, ((tx_amp_index - TXAMP_offset) | ((tx_amp_index - TXAMP_offset) << tx_amp_reg_shift)));  // // temp modify

-        set_gphy_reg_cl45(phyadd, DEVID_1E, tx_amp_reg_100, ((tx_amp_index - TXAMP_offset) | ((tx_amp_index + TX_i2mpb_hbt_ofs) << tx_amp_reg_shift)));

-    }

-

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Dh, Disable_all);

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Eh, Disable_all);

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Fh, Disable_all);

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg180h, Disable_all);

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg181h, Disable_all);

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg182h, Disable_all);

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg183h, Disable_all);

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg184h, Disable_all);

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Disable_all); // disable analog calibration circuit

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Disable_all); // disable Tx offset calibration circuit

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg03Eh, Disable_all); // disable Tx VLD force mode

-    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Disable_all); // disable Tx offset/amplitude calibration circuit

-

-    return all_ana_cal_status;

-}

-

-void set_gphy_reg_cl22(uint8_t phyad, uint8_t reg, uint16_t value)

-{

-    an8855_phy_write(phyad-g_smi_addr, reg, value);

-    /*

-       gsw_top_reg_REG_PHY_IAC REG_PHY_IAC_val;

-       gsw_top_reg_REG_PHY_IAD REG_PHY_IAD_val;

-

-    // Wait until done

-    do

-    {

-    REG_PHY_IAC_val.Raw = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAC);

-    }

-    while(REG_PHY_IAC_val.Bits.csr_phy_acs_st);

-

-    // Set address

-    REG_PHY_IAC_val.Bits.csr_mdio_st = 1;

-    REG_PHY_IAC_val.Bits.csr_mdio_cmd = 1;

-    REG_PHY_IAC_val.Bits.csr_mdio_phy_addr = phyad;

-    REG_PHY_IAC_val.Bits.csr_mdio_reg_addr = reg;

-    REG_PHY_IAC_val.Bits.csr_mdio_wr_data = value;

-    REG_PHY_IAC_val.Bits.csr_phy_acs_st = 1;

-

-    io_write32(RgAddr_gsw_top_reg_REG_PHY_IAC, REG_PHY_IAC_val.Raw);

-    */

-}

-

-UINT16 get_gphy_reg_cl45(uint8_t prtid, uint8_t devid, uint16_t reg)

-{

-    UINT32 rdata = 0;

-

-    an8855_phy_read_cl45(prtid-g_smi_addr, devid, reg, &rdata);

-    return ((UINT16)rdata);

-    /*

-    gsw_top_reg_REG_PHY_IAC REG_PHY_IAC_val;

-    gsw_top_reg_REG_PHY_IAD REG_PHY_IAD_val;

-

-    // Wait until done

-    do

-    {

-    REG_PHY_IAC_val.Raw = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAC);

-    }

-    while(REG_PHY_IAC_val.Bits.csr_phy_acs_st);

-

-    // Set address

-    REG_PHY_IAC_val.Bits.csr_mdio_st = 0;

-    REG_PHY_IAC_val.Bits.csr_mdio_cmd = 0;

-    REG_PHY_IAC_val.Bits.csr_mdio_phy_addr = prtid;

-    REG_PHY_IAC_val.Bits.csr_mdio_reg_addr = devid;

-    REG_PHY_IAC_val.Bits.csr_mdio_wr_data = reg;

-    REG_PHY_IAC_val.Bits.csr_phy_acs_st = 1;

-

-    io_write32(RgAddr_gsw_top_reg_REG_PHY_IAC, REG_PHY_IAC_val.Raw);

-

-    // Wait until done

-    do

-    {

-    REG_PHY_IAC_val.Raw = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAC);

-    }

-    while(REG_PHY_IAC_val.Bits.csr_phy_acs_st);

-

-    // Read value

-    REG_PHY_IAC_val.Bits.csr_mdio_st = 0;

-    REG_PHY_IAC_val.Bits.csr_mdio_cmd = 3;

-    REG_PHY_IAC_val.Bits.csr_mdio_phy_addr = prtid;

-    REG_PHY_IAC_val.Bits.csr_mdio_reg_addr = devid;

-    REG_PHY_IAC_val.Bits.csr_mdio_wr_data = 0;

-    REG_PHY_IAC_val.Bits.csr_phy_acs_st = 1;

-    io_write32(RgAddr_gsw_top_reg_REG_PHY_IAC, REG_PHY_IAC_val.Raw);

-

-    // Wait until done

-    do

-    {

-    REG_PHY_IAC_val.Raw = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAC);

-    }

-    while(REG_PHY_IAC_val.Bits.csr_phy_acs_st);

-

-    REG_PHY_IAD_val.Raw = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAD);

-

-    return REG_PHY_IAD_val.Raw;

-    */

-}

-

-void set_gphy_reg_cl45(uint8_t prtid, uint8_t devid, uint16_t reg, uint16_t value)

-{

-    an8855_phy_write_cl45(prtid-g_smi_addr, devid, reg, value);

-    /*

-    gsw_top_reg_REG_PHY_IAC REG_PHY_IAC_val;

-

-    // Wait until done

-    do

-    {

-        REG_PHY_IAC_val.Raw = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAC);

-    }

-    while(REG_PHY_IAC_val.Bits.csr_phy_acs_st);

-

-    // Set address

-    REG_PHY_IAC_val.Bits.csr_mdio_st = 0;

-    REG_PHY_IAC_val.Bits.csr_mdio_cmd = 0;

-    REG_PHY_IAC_val.Bits.csr_mdio_phy_addr = prtid;

-    REG_PHY_IAC_val.Bits.csr_mdio_reg_addr = devid;

-    REG_PHY_IAC_val.Bits.csr_mdio_wr_data = reg;

-    REG_PHY_IAC_val.Bits.csr_phy_acs_st = 1;

-

-    io_write32(RgAddr_gsw_top_reg_REG_PHY_IAC, REG_PHY_IAC_val.Raw);

-

-    // Wait until done

-    do

-    {

-        REG_PHY_IAC_val.Raw = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAC);

-    }

-    while(REG_PHY_IAC_val.Bits.csr_phy_acs_st);

-

-    // Write value

-    REG_PHY_IAC_val.Bits.csr_mdio_st = 0;

-    REG_PHY_IAC_val.Bits.csr_mdio_cmd = 1;

-    REG_PHY_IAC_val.Bits.csr_mdio_phy_addr = prtid;

-    REG_PHY_IAC_val.Bits.csr_mdio_reg_addr = devid;

-    REG_PHY_IAC_val.Bits.csr_mdio_wr_data = value;

-    REG_PHY_IAC_val.Bits.csr_phy_acs_st = 1;

-

-    io_write32(RgAddr_gsw_top_reg_REG_PHY_IAC, REG_PHY_IAC_val.Raw);

-    */

-}

-

-void anacal_exe(uint8_t phyadd_common)

-{

-    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg17Ch, 1);// da_calin_flag pull high

-    an8855_udelay(1000);

-    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg17Ch, 0);// da_calin_flag pull low

-}

-

+/* FILE NAME:  an8855_phy_cal.c
+* PURPOSE:
+*    It provides an8855 switch phy calibration function.
+*
+* NOTES:
+*
+*/
+
+/* INCLUDE FILE DECLARATIONS
+*/
+#include "an8855_mdio.h"
+#include "an8855_phy.h"
+//#include "swk_gphy_reg.h"
+//#include "gphy_calibration.h"
+//#include "gsw_reg.h"
+
+/* NAMING CONSTANT DECLARATIONS
+ */
+#define MII_BMCR                (0)
+#define BMCR_PDOWN              (0x0800)
+/* MACRO FUNCTION DECLARATIONS
+ */
+
+#define FULL_BITS(_n_) ((1UL << (_n_)) - 1)
+
+/* DATA TYPE DECLARATIONS
+ */
+
+/* GLOBAL VARIABLE DECLARATIONS
+ */
+/* Zcal to R50 mapping table (20220404) */
+const uint8_t ZCAL_TO_R50ohm_TBL[64] =
+{
+    127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+    127, 127, 127, 127, 127, 123, 118, 114, 110, 106, 102, 98, 96, 92, 88, 85,
+    82, 80, 76, 72, 70, 67, 64, 62, 60, 56, 54, 52, 49, 48, 45, 43,
+    40, 39, 36, 34, 32, 32, 30, 28, 25, 24, 22, 20, 18, 16, 16, 14
+};
+
+/* Tx offset table, value is from small to big */
+const uint8_t  EN753x_TX_OFS_TBL[64] =
+{
+    0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30,
+    0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
+};
+
+#define TOTAL_PATCH_C45_ITEMS   (15)
+#define TOTAL_PATCH_TR_ITEMS    (19)
+const uint16_t C45_PATCH_TABLE[TOTAL_PATCH_C45_ITEMS][3] =
+{
+    {0x1E, 0x120, 0x8014},
+    {0x1E, 0x122, 0xFFFF},
+    {0x1E, 0x122, 0xFFFF},
+    {0x1E, 0x144, 0x0200},
+    {0x1E, 0x14A, 0xEE20},
+    {0x1E, 0x189, 0x0110},
+    {0x1E, 0x19B, 0x0111},
+    {0x1E, 0x234, 0x0181},
+    {0x1E, 0x238, 0x0120},
+    {0x1E, 0x239, 0x0117},
+    {0x1F, 0x268, 0x07F4},
+    {0x1E, 0x2d1, 0x0733},
+    {0x1E, 0x323, 0x0011},
+    {0x1E, 0x324, 0x013F},
+    {0x1E, 0x326, 0x0037},
+};
+
+const uint32_t TR_PATCH_TABLE[TOTAL_PATCH_TR_ITEMS][2] =
+{
+    {0x83AA, 0x055a0 },
+    {0x83AE, 0x7FF3F },
+    {0x8F80, 0x0001e },
+    {0x8F82, 0x6FB90A},
+    {0x8FAE, 0x060671},
+    {0x8FB0, 0xE2F00 },
+    {0x8ECC, 0x444444},
+    {0x9686, 0x00000 },
+    {0x968C, 0x2EBAEF},
+    {0x9690, 0x00000b},
+    {0x9698, 0x0504D },
+    {0x969A, 0x2314f },
+    {0x969E, 0x03028 },
+    {0x96A0, 0x05010 },
+    {0x96A2, 0x40001 },
+    {0x96A6, 0x018670},
+    {0x96A8, 0x0024A },
+    {0x96B6, 0x00072 },
+    {0x96B8, 0x03210 },
+};
+
+#define TOTAL_NUMBER_OF_PATCH    (14)
+static uint16_t eee_patch_table[TOTAL_NUMBER_OF_PATCH][2] = {
+    {RgAddr_dev1Eh_reg120h, 0x8014},
+    {RgAddr_dev1Eh_reg122h, 0xFFFF},
+    {RgAddr_dev1Eh_reg122h, 0xFFFF},
+    {RgAddr_dev1Eh_reg144h, 0x0200},
+    {RgAddr_dev1Eh_reg14Ah, 0xEE20},
+    {RgAddr_dev1Eh_reg19Bh, 0x0111},
+    {RgAddr_dev1Eh_reg234h, 0x1181},
+    {RgAddr_dev1Eh_reg238h, 0x0120},
+    {RgAddr_dev1Eh_reg239h, 0x0117},
+    {RgAddr_dev1Fh_reg268h, 0x07F4},
+    {RgAddr_dev1Eh_reg2D1h, 0x0733},
+    {RgAddr_dev1Eh_reg323h, 0x0011},
+    {RgAddr_dev1Eh_reg324h, 0x013F},
+    {RgAddr_dev1Eh_reg326h, 0x0037}
+};
+
+#define TOTAL_NUMBER_OF_TR      (19)
+static uint16_t tr_reg_table[TOTAL_NUMBER_OF_TR][3] = {
+    {0x55A0, 0x0000, 0x83AA},
+    {0xFF3F, 0x0007, 0x83AE},
+    {0x001E, 0x0000, 0x8F80},
+    {0xB90A, 0x006F, 0x8F82},
+    {0x0671, 0x0006, 0x8FAE},
+    {0x2F00, 0x000E, 0x8FB0},
+    {0x4444, 0x0044, 0x8ECC},
+    {0x0004, 0x0000, 0x9686},
+    {0xBAEF, 0x002E, 0x968C},
+    {0x000B, 0x0000, 0x9690},
+    {0x504D, 0x0000, 0x9698},
+    {0x314F, 0x0002, 0x969A},
+    {0x3028, 0x0000, 0x969E},
+    {0x5010, 0x0000, 0x96A0},
+    {0x0001, 0x0004, 0x96A2},
+    {0x8670, 0x0001, 0x96A6},
+    {0x024A, 0x0000, 0x96A8},
+    {0x0072, 0x0000, 0x96B6},
+    {0x3210, 0x0000, 0x96B8}
+};
+
+void TR_RegWr(uint16_t phyadd, uint16_t tr_reg_addr, uint32_t tr_data);
+
+uint16_t get_gphy_reg_cl22(uint8_t phyad, uint8_t reg)
+{
+    uint32_t rdata = 0;
+
+    an8855_phy_read(phyad-g_smi_addr, reg, &rdata);
+
+    return ((uint16_t)rdata);
+    /*
+    gsw_top_reg_REG_PHY_IAC REG_PHY_IAC_val;
+    gsw_top_reg_REG_PHY_IAD REG_PHY_IAD_val;
+
+    // Wait until done
+    do
+    {
+      REG_PHY_IAC_val.Raw   = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAC);
+    }
+    while(REG_PHY_IAC_val.Bits.csr_phy_acs_st);
+
+    // Set address
+    REG_PHY_IAC_val.Bits.csr_mdio_st = 1;
+    REG_PHY_IAC_val.Bits.csr_mdio_cmd   = 2;
+    REG_PHY_IAC_val.Bits.csr_mdio_phy_addr = phyad;
+    REG_PHY_IAC_val.Bits.csr_mdio_reg_addr = reg;
+    REG_PHY_IAC_val.Bits.csr_phy_acs_st =   1;
+    io_write32(RgAddr_gsw_top_reg_REG_PHY_IAC, REG_PHY_IAC_val.Raw);
+    // Wait until done
+    do
+    {
+        REG_PHY_IAC_val.Raw = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAC);
+    }
+    while(REG_PHY_IAC_val.Bits.csr_phy_acs_st);
+
+    REG_PHY_IAD_val.Raw = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAD);
+
+    return REG_PHY_IAD_val.Raw;
+    */
+}
+
+/* EXPORTED SUBPROGRAM BODIES
+ */
+void gphy_config(void)
+{
+    uint8_t port = 1;
+    uint8_t phy_base = 0, phys_in_chip = 8;
+
+    for (port = 1; port <= phys_in_chip; port++)
+    {
+        set_gphy_reg_cl45(phy_base + port, 0x7, 0x3c, 0x0006); // Enable EEE
+        set_gphy_reg_cl45(phy_base + port, 0x1e, 0x3e, 0xf000); // force on TXVLD
+    }
+}
+
+static void set_gphy_TrReg(uint8_t prtid, uint16_t parm_1, uint16_t parm_2, uint16_t parm_3)
+{
+    set_gphy_reg_cl22(prtid, RgAddr_TrReg11h, parm_1);
+    set_gphy_reg_cl22(prtid, RgAddr_TrReg12h, parm_2);
+    set_gphy_reg_cl22(prtid, RgAddr_TrReg10h, parm_3);
+}
+
+static void gphy_eee_patch(uint8_t phy_base)
+{
+    UI8_T   port = 1, index = 0, phy_addr = 1;
+    UI16_T  data = 0;
+
+    for (port = 1; port <=8; port++)
+    {
+        phy_addr = phy_base + port;
+        data = get_gphy_reg_cl22(phy_addr, MII_BMCR);
+        set_gphy_reg_cl22(phy_addr, MII_BMCR, data & ~(BMCR_PDOWN));    /* PHY power on */
+
+        /* Change EEE RG default value */
+        for (index = 0; index < TOTAL_NUMBER_OF_PATCH; index++)
+        {
+            set_gphy_reg_cl45(phy_addr, DEVID_1E, eee_patch_table[index][0], eee_patch_table[index][1]);
+        }
+
+        set_gphy_reg_cl22(phy_addr, RgAddr_Reg1Fh, CL22_Page_TrReg);   /* change CL22page to LpiReg(0x3) */
+        for (index = 0; index < TOTAL_NUMBER_OF_TR; index++)
+        {
+            set_gphy_TrReg(phy_addr, tr_reg_table[index][0], tr_reg_table[index][1], tr_reg_table[index][2]);
+        }
+
+        set_gphy_reg_cl22(phy_addr, RgAddr_Reg1Fh, CL22_Page_LpiReg);  /* change CL22page to LpiReg(0x3) */
+        set_gphy_reg_cl22(phy_addr, RgAddr_LpiReg1Ch, 0x0c92);         /* Fine turn SigDet for B2B LPI link down issue */
+        set_gphy_reg_cl22(phy_addr, RgAddr_LpiReg1Dh, 0x0001);         /* Enable "lpi_quit_waitafesigdet_en" for LPI link down issue */
+
+        set_gphy_reg_cl22(phy_addr, RgAddr_Reg1Fh, CL22_Page_Reg);     /* change CL22page to Reg(0x0) */
+    }
+}
+
+void gphy_calibration(uint8_t phy_base)
+{
+    uint8_t port = 1, phy_addr = 1 ,phy_group = 1, index = 0;
+    uint8_t phys_in_chip = 5;
+
+    BG_Calibration(phy_base, 0x1);
+    if (phys_in_chip > 4)
+    {
+        BG_Calibration(phy_base + 0x4, 0x1);
+    }
+
+    for (port = 0; port < phys_in_chip; port++)
+    {
+        if (port < 4)
+        {
+            phy_group = phy_base;     /* PHY group 1 */
+        }
+        else
+        {
+            phy_group = phy_base + 0x04;     /* PHY group 2 */
+        }
+        phy_addr = phy_base + port;
+        R50_Calibration(phy_addr, phy_group);
+        TX_OFS_Calibration(phy_addr, phy_group);
+        TX_AMP_Calibration(phy_addr, phy_group);
+    }
+
+    for (port = 0; port < phys_in_chip; port++)
+    {
+        phy_addr = phy_base + port;
+        set_gphy_reg_cl45(phy_addr, 0x1e, 0x017d, 0x0000);
+        set_gphy_reg_cl45(phy_addr, 0x1e, 0x017e, 0x0000);
+        set_gphy_reg_cl45(phy_addr, 0x1e, 0x017f, 0x0000);
+        set_gphy_reg_cl45(phy_addr, 0x1e, 0x0180, 0x0000);
+        set_gphy_reg_cl45(phy_addr, 0x1e, 0x0181, 0x0000);
+        set_gphy_reg_cl45(phy_addr, 0x1e, 0x0182, 0x0000);
+        set_gphy_reg_cl45(phy_addr, 0x1e, 0x0183, 0x0000);
+        set_gphy_reg_cl45(phy_addr, 0x1e, 0x0184, 0x0000);
+        set_gphy_reg_cl45(phy_addr, 0x1e, 0x00db, 0x0000);  // disable analog calibration circuit
+        set_gphy_reg_cl45(phy_addr, 0x1e, 0x00dc, 0x0000);  // disable Tx offset calibration circuit
+        set_gphy_reg_cl45(phy_addr, 0x1e, 0x003e, 0x0000);  // disable Tx VLD force mode
+        set_gphy_reg_cl45(phy_addr, 0x1e, 0x00dd, 0x0000);  // disable Tx offset/amplitude calibration circuit
+        set_gphy_reg_cl45(phy_addr, 0x1e, 0x0145, 0x1000);  // enable auto MDI/MDIX
+
+        set_gphy_reg_cl22(phy_addr, 0, 0x1200);
+        /* GPHY Rx low pass filter */
+        set_gphy_reg_cl45(phy_addr, 0x1e, 0xc7, 0xd000);
+        /* patch */
+        for (index = 0; index < TOTAL_PATCH_C45_ITEMS; index++)
+        {
+            set_gphy_reg_cl45(phy_addr, C45_PATCH_TABLE[index][0], C45_PATCH_TABLE[index][1], C45_PATCH_TABLE[index][2]);
+        }
+        for (index = 0; index < TOTAL_PATCH_TR_ITEMS; index++)
+        {
+            TR_RegWr(phy_addr, TR_PATCH_TABLE[index][0], TR_PATCH_TABLE[index][1]);
+        }
+        set_gphy_reg_cl22(phy_addr, 0x1f, 0x0  );
+        set_gphy_reg_cl22(phy_addr, 0x1f, 0x3  );
+        set_gphy_reg_cl22(phy_addr, 0x1c, 0xc92);
+        set_gphy_reg_cl22(phy_addr, 0x1d, 0x01 );
+        set_gphy_reg_cl22(phy_addr, 0x1f, 0x0  );
+    }
+    gphy_eee_patch(phy_base);
+}
+
+/* LOCAL SUBPROGRAM BODIES
+ */
+void TR_RegWr(uint16_t phyadd, uint16_t tr_reg_addr, uint32_t tr_data)
+{
+    set_gphy_reg_cl22(phyadd, 0x1F, 0x52b5);       /* page select */
+    set_gphy_reg_cl22(phyadd, 0x11, (uint16_t)(tr_data & 0xffff));
+    set_gphy_reg_cl22(phyadd, 0x12, (uint16_t)(tr_data >> 16));
+    set_gphy_reg_cl22(phyadd, 0x10, (uint16_t)(tr_reg_addr | TrReg_WR));
+    set_gphy_reg_cl22(phyadd, 0x1F, 0x0);          /* page resetore */
+    return;
+}
+
+uint8_t BG_Calibration(uint8_t phyadd, int8_t calipolarity)
+{
+    int8_t rg_zcal_ctrl = 0, calibration_polarity = 0;
+    uint8_t all_ana_cal_status = 1;
+    uint16_t ad_cal_comp_out_init = 0;
+
+    /* setting */
+    set_gphy_reg_cl22(phyadd, RgAddr_Reg1Fh, CL22_Page_Reg);        // g0
+    set_gphy_reg_cl22(phyadd, RgAddr_Reg00h, AN_disable_force_1000M);  // AN disable, force 1000M
+    set_gphy_reg_cl45(phyadd, DEVID_1F, RgAddr_dev1Fh_reg100h, BG_voltage_output);// BG voltage output
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg145h, Fix_mdi);// fix mdi
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Fh_reg0FFh, 0x2);
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_BG);// 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Disable_all);// 1e_dc[0]:rg_txvos_calen
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0E1h, Disable_all);// 1e_e1[4]:rg_cal_refsel(0:1.2V) enable BG 1.2V to REXT PAD
+
+    /* calibrate */
+    rg_zcal_ctrl = ZCAL_MIDDLE;
+
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0E0h, (uint16_t)rg_zcal_ctrl);
+
+    anacal_exe(phyadd);
+    if (all_ana_cal_status == 0)
+    {
+        all_ana_cal_status = ANACAL_ERROR;
+    }
+    ad_cal_comp_out_init = (get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Ah) >> 8) & 0x1;
+
+
+    if (ad_cal_comp_out_init == 1)
+    {
+        calibration_polarity = -calipolarity;
+    }
+    else // ad_cal_comp_out_init == 0
+    {
+        calibration_polarity = calipolarity;
+    }
+
+    while (all_ana_cal_status < ANACAL_ERROR)
+    {
+        rg_zcal_ctrl += calibration_polarity;
+
+        set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0E0h, (uint16_t)rg_zcal_ctrl);
+
+
+        anacal_exe(phyadd);
+
+        if (all_ana_cal_status == 0)
+        {
+            all_ana_cal_status = ANACAL_ERROR;
+        }
+
+        else if (((get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Ah) >> 8) & 0x1) != ad_cal_comp_out_init)
+        {
+            all_ana_cal_status = ANACAL_FINISH;
+        }
+        else
+        {
+            if ((rg_zcal_ctrl == 0x3F) || (rg_zcal_ctrl == 0x00))
+            {
+                all_ana_cal_status = ANACAL_SATURATION;  // need to FT
+                rg_zcal_ctrl = ZCAL_MIDDLE;  // 0 dB
+            }
+        }
+    }
+
+    if (all_ana_cal_status == ANACAL_ERROR)
+    {
+        rg_zcal_ctrl = ZCAL_MIDDLE;  // 0 dB
+
+        set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0E0h, (uint16_t)rg_zcal_ctrl);
+    }
+    else
+    {
+        // rg_zcal_ctrl[5:0] rg_rext_trim[13:8]
+        set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0E0h, (uint16_t)((rg_zcal_ctrl << 8) | rg_zcal_ctrl));
+
+        // 1f_115[2:0](rg_bg_rasel) = rg_zcal_ctrl[5:3]
+        set_gphy_reg_cl45(phyadd, DEVID_1F, RgAddr_dev1Fh_reg115h, (uint16_t)((rg_zcal_ctrl & 0x3f) >> 3));
+    }
+
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Disable_all);
+    return all_ana_cal_status;
+}
+
+uint8_t R50_Calibration(uint8_t phyadd, uint8_t phyadd_common)
+{
+    int8_t rg_zcal_ctrl = 0, rg_r50ohm_rsel_tx = 0, calibration_polarity = 0;
+    uint8_t all_ana_cal_status = 1;
+    int16_t backup_dev1e_e0 = 0, ad_cal_comp_out_init = 0, calibration_pair = 0;
+
+    /* setting */
+    set_gphy_reg_cl22(phyadd, RgAddr_Reg1Fh, CL22_Page_Reg);        // g0
+    set_gphy_reg_cl22(phyadd, RgAddr_Reg00h, AN_disable_force_1000M);  // AN disable, force 1000M
+
+    set_gphy_reg_cl45(phyadd_common, DEVID_1F, RgAddr_dev1Fh_reg100h, BG_voltage_output); // BG voltage output
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg145h, Fix_mdi); // fix mdi
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg185h, Disable_tx_slew_control); // disable tx slew control
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0FBh, LDO_control); // ldo
+    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_R50); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen
+    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Disable_all); // 1e_dc[0]:rg_txvos_calen
+    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0E1h, Disable_all); // 1e_e1[4]:rg_cal_refsel(0:1.2V) enable BG 1.2V to REXT PAD
+
+    for (calibration_pair = ANACAL_PAIR_A; calibration_pair <= ANACAL_PAIR_D; calibration_pair++)
+    {
+        all_ana_cal_status = 1;
+
+        if (calibration_pair == ANACAL_PAIR_A)
+        {
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_R50_pairA_ENABLE); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Zcalen_A_ENABLE);
+        }
+        else if (calibration_pair == ANACAL_PAIR_B)
+        {
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_R50); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Zcalen_B_ENABLE); // 1e_dc[12]:rg_zcalen_b
+        }
+        else if (calibration_pair == ANACAL_PAIR_C)
+        {
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_R50); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Zcalen_C_ENABLE); // 1e_dc[8]:rg_zcalen_c
+        }
+        else // if(calibration_pair == ANACAL_PAIR_D)
+        {
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_R50); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Zcalen_D_ENABLE); // 1e_dc[4]:rg_zcalen_d
+        }
+
+        /* calibrate */
+        rg_zcal_ctrl = ZCAL_MIDDLE;             // start with 0 dB
+
+        backup_dev1e_e0 = (get_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0E0h)&(~0x003f));
+        set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0E0h, (backup_dev1e_e0 | rg_zcal_ctrl));
+
+        anacal_exe(phyadd_common);
+        if (all_ana_cal_status == 0)
+        {
+            all_ana_cal_status = ANACAL_ERROR;
+        }
+
+        ad_cal_comp_out_init = (get_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg17Ah) >> 8) & 0x1;  // 1e_17a[8]:ad_cal_comp_out
+
+        if (ad_cal_comp_out_init == 1)
+        {
+            calibration_polarity = -1;
+        }
+        else
+        {
+            calibration_polarity = 1;
+        }
+
+        while (all_ana_cal_status < ANACAL_ERROR)
+        {
+            rg_zcal_ctrl += calibration_polarity;
+
+            set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0E0h, (backup_dev1e_e0 | rg_zcal_ctrl));
+
+            anacal_exe(phyadd_common);
+
+            if (all_ana_cal_status == 0)
+            {
+                all_ana_cal_status = ANACAL_ERROR;
+            }
+            else if (((get_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg17Ah) >> 8) & 0x1) != ad_cal_comp_out_init)
+            {
+                all_ana_cal_status = ANACAL_FINISH;
+            }
+            else
+            {
+                if ((rg_zcal_ctrl == 0x3F) || (rg_zcal_ctrl == 0x00))
+                {
+                    all_ana_cal_status = ANACAL_SATURATION;  // need to FT
+                    rg_zcal_ctrl = ZCAL_MIDDLE;  // 0 dB
+                }
+            }
+        }
+
+        if (all_ana_cal_status == ANACAL_ERROR)
+        {
+            rg_r50ohm_rsel_tx = ZCAL_MIDDLE;  // 0 dB
+        }
+        else
+        {
+            if (rg_zcal_ctrl > (0x3F - R50_OFFSET_VALUE))
+            {
+                all_ana_cal_status = ANACAL_SATURATION;  // need to FT
+                rg_zcal_ctrl = ZCAL_MIDDLE;  // 0 dB
+            }
+            else
+            {
+                rg_zcal_ctrl += R50_OFFSET_VALUE;
+            }
+
+            rg_r50ohm_rsel_tx = ZCAL_TO_R50ohm_TBL[rg_zcal_ctrl];
+        }
+
+        if (calibration_pair == ANACAL_PAIR_A)
+        {
+            // cr_r50ohm_rsel_tx_a
+            ad_cal_comp_out_init = get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg174h)&(~MASK_r50ohm_rsel_tx_a);
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg174h, (ad_cal_comp_out_init | (((rg_r50ohm_rsel_tx << 8) & MASK_MSB_8bit) | Rg_r50ohm_rsel_tx_a_en))); // 1e_174[15:8]
+        }
+        else if (calibration_pair == ANACAL_PAIR_B)
+        {
+            // cr_r50ohm_rsel_tx_b
+            ad_cal_comp_out_init = get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg174h)&(~MASK_r50ohm_rsel_tx_b);
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg174h, (ad_cal_comp_out_init | (((rg_r50ohm_rsel_tx << 0) & MASK_LSB_8bit) | Rg_r50ohm_rsel_tx_b_en))); // 1e_174[7:0]
+        }
+        else if (calibration_pair == ANACAL_PAIR_C)
+        {
+            // cr_r50ohm_rsel_tx_c
+            ad_cal_comp_out_init = get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg175h)&(~MASK_r50ohm_rsel_tx_c);
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg175h, (ad_cal_comp_out_init | (((rg_r50ohm_rsel_tx << 8) & MASK_MSB_8bit) | Rg_r50ohm_rsel_tx_c_en))); // 1e_175[15:8]
+        }
+        else // if(calibration_pair == ANACAL_PAIR_D)
+        {
+            // cr_r50ohm_rsel_tx_d
+            ad_cal_comp_out_init = get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg175h)&(~MASK_r50ohm_rsel_tx_d);
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg175h, (ad_cal_comp_out_init | (((rg_r50ohm_rsel_tx << 0) & MASK_LSB_8bit) | Rg_r50ohm_rsel_tx_d_en))); // 1e_175[7:0]
+        }
+    }
+
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Disable_all);
+    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Disable_all);
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Disable_all);
+
+    return all_ana_cal_status;
+}
+
+uint8_t TX_OFS_Calibration(uint8_t phyadd, uint8_t phyadd_common)
+{
+    int8_t tx_offset_index = 0, calibration_polarity = 0;
+    uint8_t all_ana_cal_status = 1, tx_offset_reg_shift = 0, tbl_idx = 0;
+    int16_t ad_cal_comp_out_init = 0, calibration_pair = 0, tx_offset_reg = 0, reg_temp = 0;
+
+    /* setting */
+    set_gphy_reg_cl22(phyadd, RgAddr_Reg1Fh, CL22_Page_Reg);        // g0
+    set_gphy_reg_cl22(phyadd, RgAddr_Reg00h, AN_disable_force_1000M);  // AN disable, force 1000M
+
+    set_gphy_reg_cl45(phyadd, DEVID_1F, RgAddr_dev1Fh_reg100h, BG_voltage_output); // BG voltage output
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg145h, Fix_mdi); // fix mdi
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg185h, Disable_tx_slew_control); // disable tx slew control
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0FBh, LDO_control); // ldo
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_TX_OFST); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Rg_txvos_calen_ENABLE); // 1e_dc[0]:rg_txvos_calen
+    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_TX_OFST); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen
+    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Rg_txvos_calen_ENABLE); // 1e_dc[0]:rg_txvos_calen
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0E1h, Disable_all); // 1e_e1[4]:rg_cal_refsel(0:1.2V) enable BG 1.2V to REXT PAD
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg096h, Bypass_tx_offset_cal); // 1e_96[15]:bypass_tx_offset_cal, Hw bypass, Fw cal
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg03Eh, Enable_Tx_VLD); // 1e_3e:enable Tx VLD
+
+    for (calibration_pair = ANACAL_PAIR_A; calibration_pair <= ANACAL_PAIR_D; calibration_pair++)
+    {
+        all_ana_cal_status = 1;
+
+        tbl_idx = TX_OFFSET_0mV_idx;
+        tx_offset_index = EN753x_TX_OFS_TBL[tbl_idx];
+
+        if (calibration_pair == ANACAL_PAIR_A)
+        {
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Rg_txg_calen_a_ENABLE);       // 1e_dd[12]:rg_txg_calen_a
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Dh, (Force_dasn_dac_in0_ENABLE | DAC_IN_0V));  // 1e_17d:dac_in0_a
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg181h, (Force_dasn_dac_in1_ENABLE | DAC_IN_0V));  // 1e_181:dac_in1_a
+
+            reg_temp = (get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg172h)&(~MASK_cr_tx_amp_offset_MSB));
+            tx_offset_reg_shift = 8;  // 1e_172[13:8]
+            tx_offset_reg = RgAddr_dev1Eh_reg172h;
+        }
+        else if (calibration_pair == ANACAL_PAIR_B)
+        {
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Rg_txg_calen_b_ENABLE);       // 1e_dd[8]:rg_txg_calen_b
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Eh, (Force_dasn_dac_in0_ENABLE | DAC_IN_0V));  // 1e_17e:dac_in0_b
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg182h, (Force_dasn_dac_in1_ENABLE | DAC_IN_0V));  // 1e_182:dac_in1_b
+
+            reg_temp = (get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg172h)&(~MASK_cr_tx_amp_offset_LSB));
+            tx_offset_reg_shift = 0;  // 1e_172[5:0]
+            tx_offset_reg = RgAddr_dev1Eh_reg172h;
+        }
+        else if (calibration_pair == ANACAL_PAIR_C)
+        {
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Rg_txg_calen_c_ENABLE);       // 1e_dd[4]:rg_txg_calen_c
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Fh, (Force_dasn_dac_in0_ENABLE | DAC_IN_0V));  // 1e_17f:dac_in0_c
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg183h, (Force_dasn_dac_in1_ENABLE | DAC_IN_0V));  // 1e_183:dac_in1_c
+
+            reg_temp = (get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg173h)&(~MASK_cr_tx_amp_offset_MSB));
+            tx_offset_reg_shift = 8;  // 1e_173[13:8]
+            tx_offset_reg = RgAddr_dev1Eh_reg173h;
+        }
+        else // if(calibration_pair == ANACAL_PAIR_D)
+        {
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Rg_txg_calen_d_ENABLE);       // 1e_dd[0]:rg_txg_calen_d
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg180h, (Force_dasn_dac_in0_ENABLE | DAC_IN_0V));  // 1e_180:dac_in0_d
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg184h, (Force_dasn_dac_in1_ENABLE | DAC_IN_0V));  // 1e_184:dac_in1_d
+
+            reg_temp = (get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg173h)&(~MASK_cr_tx_amp_offset_LSB));
+            tx_offset_reg_shift = 0;  // 1e_173[5:0]
+            tx_offset_reg = RgAddr_dev1Eh_reg173h;
+        }
+
+        /* calibrate */
+        //tx_offset_index = TX_AMP_OFFSET_0mV;
+        tbl_idx = TX_OFFSET_0mV_idx;
+        tx_offset_index = EN753x_TX_OFS_TBL[tbl_idx];
+        set_gphy_reg_cl45(phyadd, DEVID_1E, tx_offset_reg, (reg_temp | (tx_offset_index << tx_offset_reg_shift)));  // 1e_172, 1e_173
+
+        anacal_exe(phyadd_common);
+        if (all_ana_cal_status == 0)
+        {
+            all_ana_cal_status = ANACAL_ERROR;
+        }
+
+        ad_cal_comp_out_init = (get_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg17Ah) >> 8) & 0x1;  // 1e_17a[8]:ad_cal_comp_out
+
+        if (ad_cal_comp_out_init == 1)
+        {
+            calibration_polarity = -1;
+        }
+        else
+        {
+            calibration_polarity = 1;
+        }
+
+        while (all_ana_cal_status < ANACAL_ERROR)
+        {
+            tbl_idx += calibration_polarity;
+            tx_offset_index = EN753x_TX_OFS_TBL[tbl_idx];
+
+            set_gphy_reg_cl45(phyadd, DEVID_1E, tx_offset_reg, (reg_temp | (tx_offset_index << tx_offset_reg_shift)));  // 1e_172, 1e_173
+
+            anacal_exe(phyadd_common);
+
+            if (all_ana_cal_status == 0)
+            {
+                all_ana_cal_status = ANACAL_ERROR;
+            }
+            else if (((get_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg17Ah) >> 8) & 0x1) != ad_cal_comp_out_init)
+            {
+                all_ana_cal_status = ANACAL_FINISH;
+            }
+            else
+            {
+                if ((tx_offset_index == 0x3f) || (tx_offset_index == 0x1f))
+                {
+                    all_ana_cal_status = ANACAL_SATURATION;  // need to FT
+                }
+            }
+        }
+
+        if (all_ana_cal_status == ANACAL_ERROR)
+        {
+            tbl_idx = TX_OFFSET_0mV_idx;
+            tx_offset_index = EN753x_TX_OFS_TBL[tbl_idx];
+
+            set_gphy_reg_cl45(phyadd, DEVID_1E, tx_offset_reg, (reg_temp | (tx_offset_index << tx_offset_reg_shift)));  // cr_tx_amp_offset_a/b/c/d, 1e_172, 1e_173
+        }
+    }
+
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Dh, Disable_all);
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Eh, Disable_all);
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Fh, Disable_all);
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg180h, Disable_all);
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg181h, Disable_all);
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg182h, Disable_all);
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg183h, Disable_all);
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg184h, Disable_all);
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Disable_all); // disable analog calibration circuit
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Disable_all); // disable Tx offset calibration circuit
+    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Disable_all); // disable analog calibration circuit
+    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Disable_all); // disable Tx offset calibration circuit
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg03Eh, Disable_all); // disable Tx VLD force mode
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Disable_all); // disable Tx offset/amplitude calibration circuit
+
+    return all_ana_cal_status;
+}
+
+uint8_t TX_AMP_Calibration(uint8_t phyadd, uint8_t phyadd_common)
+{
+    int8_t tx_amp_index = 0, calibration_polarity = 0;
+    uint8_t all_ana_cal_status = 1, tx_amp_reg_shift = 0;
+    uint8_t tx_amp_reg = 0, tx_amp_reg_100 = 0, tst_offset = 0, hbt_offset = 0, gbe_offset = 0, tbt_offset = 0;
+    uint16_t ad_cal_comp_out_init = 0, calibration_pair = 0, reg_temp = 0;
+
+  //phyadd_common = phyadd;
+
+    /* setting */
+    set_gphy_reg_cl22(phyadd, RgAddr_Reg1Fh, CL22_Page_Reg);        // g0
+    set_gphy_reg_cl22(phyadd, RgAddr_Reg00h, AN_disable_force_1000M);  // AN disable, force 1000M
+
+    set_gphy_reg_cl45(phyadd, DEVID_1F, RgAddr_dev1Fh_reg100h, BG_voltage_output); // BG voltage output
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg145h, Fix_mdi); // fix mdi
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg185h, Disable_tx_slew_control); // disable tx slew control
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0FBh, LDO_control); // ldo
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_TX_AMP); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Rg_txvos_calen_ENABLE); // 1e_dc[0]:rg_txvos_calen
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0E1h, Rg_cal_refsel_ENABLE); // 1e_e1[4]:rg_cal_refsel(0:1.2V) enable BG 1.2V to REXT PAD
+    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Cal_control_TX_AMP); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen
+    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Rg_txvos_calen_ENABLE); // 1e_dc[0]:rg_txvos_calen
+    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg0E1h, Rg_cal_refsel_ENABLE); // 1e_e1[4]:rg_cal_refsel(0:1.2V) enable BG 1.2V to REXT PAD
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg096h, Bypass_tx_offset_cal); // 1e_96[15]:bypass_tx_offset_cal, Hw bypass, Fw cal
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg03Eh, Enable_Tx_VLD); // 1e_3e:enable Tx VLD
+
+    for (calibration_pair = ANACAL_PAIR_A; calibration_pair <= ANACAL_PAIR_D; calibration_pair++)
+    //for (calibration_pair = ANACAL_PAIR_A; calibration_pair <= ANACAL_PAIR_B; calibration_pair++) // debugging
+    {
+        all_ana_cal_status = 1;
+
+        /* calibrate */
+        tx_amp_index = TX_AMP_MIDDLE;   // start with 0 dB
+        if (calibration_pair == ANACAL_PAIR_A)
+        {
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Rg_txg_calen_a_ENABLE);       // 1e_dd[12]:rg_txg_calen_a amp calibration enable
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Dh, (Force_dasn_dac_in0_ENABLE | DAC_IN_2V));  // 1e_17d:dac_in0_a
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg181h, (Force_dasn_dac_in1_ENABLE | DAC_IN_2V));  // 1e_181:dac_in1_a
+
+            reg_temp = (get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg012h)&(~MASK_da_tx_i2mpb_a_gbe));
+            tx_amp_reg_shift = 10;  // 1e_12[15:10]
+            tx_amp_reg = RgAddr_dev1Eh_reg012h;
+            tx_amp_reg_100 = 0x16;
+        }
+        else if (calibration_pair == ANACAL_PAIR_B)
+        {
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Rg_txg_calen_b_ENABLE);       // 1e_dd[8]:rg_txg_calen_b amp calibration enable
+            //Serial.println(Rg_txg_calen_b_ENABLE, HEX);
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Eh, (Force_dasn_dac_in0_ENABLE | DAC_IN_2V));  // 1e_17e:dac_in0_b
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg182h, (Force_dasn_dac_in1_ENABLE | DAC_IN_2V));  // 1e_182:dac_in1_b
+
+            reg_temp = (get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg017h)&(~MASK_da_tx_i2mpb_b_c_d_gbe));
+            tx_amp_reg_shift = 8; // 1e_17[13:8]
+            tx_amp_reg = RgAddr_dev1Eh_reg017h;
+            tx_amp_reg_100 = 0x18;
+        }
+        else if (calibration_pair == ANACAL_PAIR_C)
+        {
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Rg_txg_calen_c_ENABLE);       // 1e_dd[4]:rg_txg_calen_c amp calibration enable
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Fh, (Force_dasn_dac_in0_ENABLE | DAC_IN_2V));  // 1e_17f:dac_in0_c
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg183h, (Force_dasn_dac_in1_ENABLE | DAC_IN_2V));  // 1e_183:dac_in1_c
+
+            reg_temp = (get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg019h)&(~MASK_da_tx_i2mpb_b_c_d_gbe));
+            tx_amp_reg_shift = 8; // 1e_19[13:8]
+            tx_amp_reg = RgAddr_dev1Eh_reg019h;
+            tx_amp_reg_100 = 0x20;
+        }
+        else //if(calibration_pair == ANACAL_PAIR_D)
+        {
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Rg_txg_calen_d_ENABLE);       // 1e_dd[0]:rg_txg_calen_d amp calibration enable
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg180h, (Force_dasn_dac_in0_ENABLE | DAC_IN_2V));  // 1e_180:dac_in0_d
+            set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg184h, (Force_dasn_dac_in1_ENABLE | DAC_IN_2V));  // 1e_184:dac_in1_d
+
+            reg_temp = (get_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg021h)&(~MASK_da_tx_i2mpb_b_c_d_gbe));
+            tx_amp_reg_shift = 8; // 1e_21[13:8]
+            tx_amp_reg = RgAddr_dev1Eh_reg021h;
+            tx_amp_reg_100 = 0x22;
+        }
+
+        /* calibrate */
+        tx_amp_index = TX_AMP_MIDDLE; // start with 0 dB
+
+        set_gphy_reg_cl45(phyadd, DEVID_1E, tx_amp_reg, (reg_temp | (tx_amp_index << tx_amp_reg_shift))); // 1e_12/17/19/21
+
+        anacal_exe(phyadd_common);
+        if (all_ana_cal_status == 0)
+        {
+            all_ana_cal_status = ANACAL_ERROR;
+        }
+
+
+        ad_cal_comp_out_init = (get_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg17Ah) >> 8) & 0x1;    // 1e_17a[8]:ad_cal_comp_out
+        //Serial.println(ad_cal_comp_out_init, HEX);
+
+        if (ad_cal_comp_out_init == 1)
+        {
+            calibration_polarity = -1;
+        }
+        else
+        {
+            calibration_polarity = 1;
+        }
+        while (all_ana_cal_status < ANACAL_ERROR)
+        {
+            tx_amp_index += calibration_polarity;
+            //Serial.println(tx_amp_index, HEX);
+
+            set_gphy_reg_cl45(phyadd, DEVID_1E, tx_amp_reg, (reg_temp | (tx_amp_index << tx_amp_reg_shift)));
+
+            anacal_exe(phyadd_common);
+
+            if (all_ana_cal_status == 0)
+            {
+                all_ana_cal_status = ANACAL_ERROR;
+            }
+            else if (((get_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg17Ah) >> 8) & 0x1) != ad_cal_comp_out_init)
+            {
+                all_ana_cal_status = ANACAL_FINISH;
+                //Serial.print("    tx_amp_index: ");
+                //Serial.println(tx_amp_index, HEX);
+                //reg_temp = get_gphy_reg_cl45(phyadd, 0x1e, tx_amp_reg)&(~0xff00);
+                //set_gphy_reg_cl45(phyadd, 0x1e, tx_amp_reg, (reg_temp|((tx_amp_index + tst_offset)<<tx_amp_reg_shift)));  // for gbe(DAC)
+            }
+            else
+            {
+                if ((tx_amp_index == 0x3f) || (tx_amp_index == 0x00))
+                {
+                    all_ana_cal_status = ANACAL_SATURATION;  // need to FT
+                    tx_amp_index = TX_AMP_MIDDLE;
+                }
+            }
+        }
+
+        if (all_ana_cal_status == ANACAL_ERROR)
+        {
+            tx_amp_index = TX_AMP_MIDDLE;
+        }
+
+        // da_tx_i2mpb_a_gbe / b/c/d, only GBE for now
+        set_gphy_reg_cl45(phyadd, DEVID_1E, tx_amp_reg, ((tx_amp_index - TXAMP_offset) | ((tx_amp_index - TXAMP_offset) << tx_amp_reg_shift)));  // // temp modify
+        set_gphy_reg_cl45(phyadd, DEVID_1E, tx_amp_reg_100, ((tx_amp_index - TXAMP_offset) | ((tx_amp_index + TX_i2mpb_hbt_ofs) << tx_amp_reg_shift)));
+    }
+
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Dh, Disable_all);
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Eh, Disable_all);
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg17Fh, Disable_all);
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg180h, Disable_all);
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg181h, Disable_all);
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg182h, Disable_all);
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg183h, Disable_all);
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg184h, Disable_all);
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DBh, Disable_all); // disable analog calibration circuit
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DCh, Disable_all); // disable Tx offset calibration circuit
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg03Eh, Disable_all); // disable Tx VLD force mode
+    set_gphy_reg_cl45(phyadd, DEVID_1E, RgAddr_dev1Eh_reg0DDh, Disable_all); // disable Tx offset/amplitude calibration circuit
+
+    return all_ana_cal_status;
+}
+
+void set_gphy_reg_cl22(uint8_t phyad, uint8_t reg, uint16_t value)
+{
+    an8855_phy_write(phyad-g_smi_addr, reg, value);
+    /*
+       gsw_top_reg_REG_PHY_IAC REG_PHY_IAC_val;
+       gsw_top_reg_REG_PHY_IAD REG_PHY_IAD_val;
+
+    // Wait until done
+    do
+    {
+    REG_PHY_IAC_val.Raw = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAC);
+    }
+    while(REG_PHY_IAC_val.Bits.csr_phy_acs_st);
+
+    // Set address
+    REG_PHY_IAC_val.Bits.csr_mdio_st = 1;
+    REG_PHY_IAC_val.Bits.csr_mdio_cmd = 1;
+    REG_PHY_IAC_val.Bits.csr_mdio_phy_addr = phyad;
+    REG_PHY_IAC_val.Bits.csr_mdio_reg_addr = reg;
+    REG_PHY_IAC_val.Bits.csr_mdio_wr_data = value;
+    REG_PHY_IAC_val.Bits.csr_phy_acs_st = 1;
+
+    io_write32(RgAddr_gsw_top_reg_REG_PHY_IAC, REG_PHY_IAC_val.Raw);
+    */
+}
+
+UINT16 get_gphy_reg_cl45(uint8_t prtid, uint8_t devid, uint16_t reg)
+{
+    UINT32 rdata = 0;
+
+    an8855_phy_read_cl45(prtid-g_smi_addr, devid, reg, &rdata);
+    return ((UINT16)rdata);
+    /*
+    gsw_top_reg_REG_PHY_IAC REG_PHY_IAC_val;
+    gsw_top_reg_REG_PHY_IAD REG_PHY_IAD_val;
+
+    // Wait until done
+    do
+    {
+    REG_PHY_IAC_val.Raw = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAC);
+    }
+    while(REG_PHY_IAC_val.Bits.csr_phy_acs_st);
+
+    // Set address
+    REG_PHY_IAC_val.Bits.csr_mdio_st = 0;
+    REG_PHY_IAC_val.Bits.csr_mdio_cmd = 0;
+    REG_PHY_IAC_val.Bits.csr_mdio_phy_addr = prtid;
+    REG_PHY_IAC_val.Bits.csr_mdio_reg_addr = devid;
+    REG_PHY_IAC_val.Bits.csr_mdio_wr_data = reg;
+    REG_PHY_IAC_val.Bits.csr_phy_acs_st = 1;
+
+    io_write32(RgAddr_gsw_top_reg_REG_PHY_IAC, REG_PHY_IAC_val.Raw);
+
+    // Wait until done
+    do
+    {
+    REG_PHY_IAC_val.Raw = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAC);
+    }
+    while(REG_PHY_IAC_val.Bits.csr_phy_acs_st);
+
+    // Read value
+    REG_PHY_IAC_val.Bits.csr_mdio_st = 0;
+    REG_PHY_IAC_val.Bits.csr_mdio_cmd = 3;
+    REG_PHY_IAC_val.Bits.csr_mdio_phy_addr = prtid;
+    REG_PHY_IAC_val.Bits.csr_mdio_reg_addr = devid;
+    REG_PHY_IAC_val.Bits.csr_mdio_wr_data = 0;
+    REG_PHY_IAC_val.Bits.csr_phy_acs_st = 1;
+    io_write32(RgAddr_gsw_top_reg_REG_PHY_IAC, REG_PHY_IAC_val.Raw);
+
+    // Wait until done
+    do
+    {
+    REG_PHY_IAC_val.Raw = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAC);
+    }
+    while(REG_PHY_IAC_val.Bits.csr_phy_acs_st);
+
+    REG_PHY_IAD_val.Raw = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAD);
+
+    return REG_PHY_IAD_val.Raw;
+    */
+}
+
+void set_gphy_reg_cl45(uint8_t prtid, uint8_t devid, uint16_t reg, uint16_t value)
+{
+    an8855_phy_write_cl45(prtid-g_smi_addr, devid, reg, value);
+    /*
+    gsw_top_reg_REG_PHY_IAC REG_PHY_IAC_val;
+
+    // Wait until done
+    do
+    {
+        REG_PHY_IAC_val.Raw = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAC);
+    }
+    while(REG_PHY_IAC_val.Bits.csr_phy_acs_st);
+
+    // Set address
+    REG_PHY_IAC_val.Bits.csr_mdio_st = 0;
+    REG_PHY_IAC_val.Bits.csr_mdio_cmd = 0;
+    REG_PHY_IAC_val.Bits.csr_mdio_phy_addr = prtid;
+    REG_PHY_IAC_val.Bits.csr_mdio_reg_addr = devid;
+    REG_PHY_IAC_val.Bits.csr_mdio_wr_data = reg;
+    REG_PHY_IAC_val.Bits.csr_phy_acs_st = 1;
+
+    io_write32(RgAddr_gsw_top_reg_REG_PHY_IAC, REG_PHY_IAC_val.Raw);
+
+    // Wait until done
+    do
+    {
+        REG_PHY_IAC_val.Raw = io_read32(RgAddr_gsw_top_reg_REG_PHY_IAC);
+    }
+    while(REG_PHY_IAC_val.Bits.csr_phy_acs_st);
+
+    // Write value
+    REG_PHY_IAC_val.Bits.csr_mdio_st = 0;
+    REG_PHY_IAC_val.Bits.csr_mdio_cmd = 1;
+    REG_PHY_IAC_val.Bits.csr_mdio_phy_addr = prtid;
+    REG_PHY_IAC_val.Bits.csr_mdio_reg_addr = devid;
+    REG_PHY_IAC_val.Bits.csr_mdio_wr_data = value;
+    REG_PHY_IAC_val.Bits.csr_phy_acs_st = 1;
+
+    io_write32(RgAddr_gsw_top_reg_REG_PHY_IAC, REG_PHY_IAC_val.Raw);
+    */
+}
+
+void anacal_exe(uint8_t phyadd_common)
+{
+    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg17Ch, 1);// da_calin_flag pull high
+    an8855_udelay(1000);
+    set_gphy_reg_cl45(phyadd_common, DEVID_1E, RgAddr_dev1Eh_reg17Ch, 0);// da_calin_flag pull low
+}
+
diff --git a/recipes-devtools/switch/files/src/an8855_sdk/core/an8855_reg.h b/recipes-devtools/switch/files/src/an8855_sdk/core/an8855_reg.h
index 3459cef..b444697 100644
--- a/recipes-devtools/switch/files/src/an8855_sdk/core/an8855_reg.h
+++ b/recipes-devtools/switch/files/src/an8855_sdk/core/an8855_reg.h
@@ -1,191 +1,191 @@
-    /* FILE NAME:  an8855_reg.h

- * PURPOSE:

- *      It provides AN8855 register definition.

- * NOTES:

- *

- */

-

-#ifndef AN8855_REG_H

-#define AN8855_REG_H

-

-#define PORT_CTRL_BASE                      0x10208000

-#define PORT_CTRL_PORT_OFFSET               0x200

-#define PORT_CTRL_REG(p, r)                 (PORT_CTRL_BASE + (p) * PORT_CTRL_PORT_OFFSET + (r))

-#define PCR(p)                              PORT_CTRL_REG(p, 0x04)

-

-#define PORT_MAC_CTRL_BASE                  0x10210000

-#define PORT_MAC_CTRL_PORT_OFFSET           0x200

-#define PORT_MAC_CTRL_REG(p, r)             (PORT_MAC_CTRL_BASE + (p) * PORT_MAC_CTRL_PORT_OFFSET + (r))

-#define PMCR(p)                             PORT_MAC_CTRL_REG(p, 0x00)

-

-/* Port debug count register */

-#define DBG_CNT_BASE                        0x3018

-#define DBG_CNT_PORT_BASE                   0x100

-#define DBG_CNT(p)                          (DBG_CNT_BASE + (p) * DBG_CNT_PORT_BASE)

-#define DIS_CLR                             (1 << 31)

-

-#define GMACCR                              (PORT_MAC_CTRL_BASE + 0x30e0)

-#define MTCC_LMT_S                          8

-#define MAX_RX_JUMBO_S                      4

-

-/* Values of MAX_RX_PKT_LEN */

-#define RX_PKT_LEN_1518                     0

-#define RX_PKT_LEN_1536                     1

-#define RX_PKT_LEN_1522                     2

-#define RX_PKT_LEN_MAX_JUMBO                3

-

-/* Fields of PMCR */

-#define FORCE_MODE                          (1 << 31)

-#define IPG_CFG_S                           20

-#define IPG_CFG_M                           0x300000

-#define EXT_PHY                             (1 << 19)

-#define MAC_MODE                            (1 << 18)

-#define MAC_TX_EN                           (1 << 16)

-#define MAC_RX_EN                           (1 << 15)

-#define MAC_PRE                             (1 << 14)

-#define BKOFF_EN                            (1 << 12)

-#define BACKPR_EN                           (1 << 11)

-#define FORCE_EEE1G                         (1 << 7)

-#define FORCE_EEE100                        (1 << 6)

-#define FORCE_RX_FC                         (1 << 5)

-#define FORCE_TX_FC                         (1 << 4)

-#define FORCE_SPD_S                         28

-#define FORCE_SPD_M                         0x70000000

-#define FORCE_DPX                           (1 << 25)

-#define FORCE_LINK                          (1 << 24)

-

-/* Fields of PMSR */

-#define EEE1G_STS                           (1 << 7)

-#define EEE100_STS                          (1 << 6)

-#define RX_FC_STS                           (1 << 5)

-#define TX_FC_STS                           (1 << 4)

-#define MAC_SPD_STS_S                       28

-#define MAC_SPD_STS_M                       0x70000000

-#define MAC_DPX_STS                         (1 << 25)

-#define MAC_LNK_STS                         (1 << 24)

-

-/* Values of MAC_SPD_STS */

-#define MAC_SPD_10                          0

-#define MAC_SPD_100                         1

-#define MAC_SPD_1000                        2

-#define MAC_SPD_2500                        3

-

-/* Values of IPG_CFG */

-#define IPG_96BIT                           0

-#define IPG_96BIT_WITH_SHORT_IPG            1

-#define IPG_64BIT                           2

-

-#define SGMII_REG_BASE                      0x5000

-#define SGMII_REG_PORT_BASE                 0x1000

-#define SGMII_REG(p, r)                     (SGMII_REG_BASE + (p) * SGMII_REG_PORT_BASE + (r))

-#define PCS_CONTROL_1(p)                    SGMII_REG(p, 0x00)

-#define SGMII_MODE(p)                       SGMII_REG(p, 0x20)

-#define QPHY_PWR_STATE_CTRL(p)              SGMII_REG(p, 0xe8)

-#define PHYA_CTRL_SIGNAL3(p)                SGMII_REG(p, 0x128)

-

-/* Fields of PCS_CONTROL_1 */

-#define SGMII_LINK_STATUS                   (1 << 18)

-#define SGMII_AN_ENABLE                     (1 << 12)

-#define SGMII_AN_RESTART                    (1 << 9)

-

-/* Fields of SGMII_MODE */

-#define SGMII_REMOTE_FAULT_DIS              (1 << 8)

-#define SGMII_IF_MODE_FORCE_DUPLEX          (1 << 4)

-#define SGMII_IF_MODE_FORCE_SPEED_S         0x2

-#define SGMII_IF_MODE_FORCE_SPEED_M         0x0c

-#define SGMII_IF_MODE_ADVERT_AN             (1 << 1)

-

-/* Values of SGMII_IF_MODE_FORCE_SPEED */

-#define SGMII_IF_MODE_FORCE_SPEED_10        0

-#define SGMII_IF_MODE_FORCE_SPEED_100       1

-#define SGMII_IF_MODE_FORCE_SPEED_1000      2

-

-/* Fields of QPHY_PWR_STATE_CTRL */

-#define PHYA_PWD                            (1 << 4)

-

-/* Fields of PHYA_CTRL_SIGNAL3 */

-#define RG_TPHY_SPEED_S                     2

-#define RG_TPHY_SPEED_M                     0x0c

-

-/* Values of RG_TPHY_SPEED */

-#define RG_TPHY_SPEED_1000                  0

-#define RG_TPHY_SPEED_2500                  1

-

-#define SYS_CTRL                            0x7000

-#define SW_PHY_RST                          (1 << 2)

-#define SW_SYS_RST                          (1 << 1)

-#define SW_REG_RST                          (1 << 0)

-

-#define PHY_IAC                             (0x1000e000)

-#define IAC_MAX_BUSY_TIME                   (1000)

-

-#define CLKGEN_CTRL                         0x7500

-#define CLK_SKEW_OUT_S                      8

-#define CLK_SKEW_OUT_M                      0x300

-#define CLK_SKEW_IN_S                       6

-#define CLK_SKEW_IN_M                       0xc0

-#define RXCLK_NO_DELAY                      (1 << 5)

-#define TXCLK_NO_REVERSE                    (1 << 4)

-#define GP_MODE_S                           1

-#define GP_MODE_M                           0x06

-#define GP_CLK_EN                           (1 << 0)

-

-/* Values of GP_MODE */

-#define GP_MODE_RGMII                       0

-#define GP_MODE_MII                         1

-#define GP_MODE_REV_MII                     2

-

-/* Values of CLK_SKEW_IN */

-#define CLK_SKEW_IN_NO_CHANGE               0

-#define CLK_SKEW_IN_DELAY_100PPS            1

-#define CLK_SKEW_IN_DELAY_200PPS            2

-#define CLK_SKEW_IN_REVERSE                 3

-

-/* Values of CLK_SKEW_OUT */

-#define CLK_SKEW_OUT_NO_CHANGE              0

-#define CLK_SKEW_OUT_DELAY_100PPS           1

-#define CLK_SKEW_OUT_DELAY_200PPS           2

-#define CLK_SKEW_OUT_REVERSE                3

-

-#define HWSTRAP                             0x7800

-#define XTAL_FSEL_S                         7

-#define XTAL_FSEL_M                         (1 << 7)

-

-#define XTAL_40MHZ                          0

-#define XTAL_25MHZ                          1

-

-#define PLLGP_EN                            0x7820

-#define EN_COREPLL                          (1 << 2)

-#define SW_CLKSW                            (1 << 1)

-#define SW_PLLGP                            (1 << 0)

-

-#define PLLGP_CR0                           0x78a8

-#define RG_COREPLL_EN                       (1 << 22)

-#define RG_COREPLL_POSDIV_S                 23

-#define RG_COREPLL_POSDIV_M                 0x3800000

-#define RG_COREPLL_SDM_PCW_S                1

-#define RG_COREPLL_SDM_PCW_M                0x3ffffe

-#define RG_COREPLL_SDM_PCW_CHG              (1 << 0)

-

-#define MHWSTRAP                            0x7804

-#define TOP_SIG_SR                          0x780c

-#define PAD_DUAL_SGMII_EN                   (1 << 1)

-

-/* RGMII and SGMII PLL clock */

-#define ANA_PLLGP_CR2                       0x78b0

-#define ANA_PLLGP_CR5                       0x78bc

-

-/* Efuse Register Define */

-#define GBE_EFUSE                           0x7bc8

-#define GBE_SEL_EFUSE_EN                    (1 << 0)

-

-/* GPIO_PAD_0 */

-#define GPIO_MODE0                          0x7c0c

-#define GPIO_MODE0_S                        0

-#define GPIO_MODE0_M                        0xf

-#define GPIO_0_INTERRUPT_MODE               0x1

-

-#define SMT0_IOLB                           0x7f04

-#define SMT_IOLB_5_SMI_MDC_EN               (1 << 5)

-

-#endif  /* End of AN8855_REG_H */

+    /* FILE NAME:  an8855_reg.h
+ * PURPOSE:
+ *      It provides AN8855 register definition.
+ * NOTES:
+ *
+ */
+
+#ifndef AN8855_REG_H
+#define AN8855_REG_H
+
+#define PORT_CTRL_BASE                      0x10208000
+#define PORT_CTRL_PORT_OFFSET               0x200
+#define PORT_CTRL_REG(p, r)                 (PORT_CTRL_BASE + (p) * PORT_CTRL_PORT_OFFSET + (r))
+#define PCR(p)                              PORT_CTRL_REG(p, 0x04)
+
+#define PORT_MAC_CTRL_BASE                  0x10210000
+#define PORT_MAC_CTRL_PORT_OFFSET           0x200
+#define PORT_MAC_CTRL_REG(p, r)             (PORT_MAC_CTRL_BASE + (p) * PORT_MAC_CTRL_PORT_OFFSET + (r))
+#define PMCR(p)                             PORT_MAC_CTRL_REG(p, 0x00)
+
+/* Port debug count register */
+#define DBG_CNT_BASE                        0x3018
+#define DBG_CNT_PORT_BASE                   0x100
+#define DBG_CNT(p)                          (DBG_CNT_BASE + (p) * DBG_CNT_PORT_BASE)
+#define DIS_CLR                             (1 << 31)
+
+#define GMACCR                              (PORT_MAC_CTRL_BASE + 0x30e0)
+#define MTCC_LMT_S                          8
+#define MAX_RX_JUMBO_S                      4
+
+/* Values of MAX_RX_PKT_LEN */
+#define RX_PKT_LEN_1518                     0
+#define RX_PKT_LEN_1536                     1
+#define RX_PKT_LEN_1522                     2
+#define RX_PKT_LEN_MAX_JUMBO                3
+
+/* Fields of PMCR */
+#define FORCE_MODE                          (1 << 31)
+#define IPG_CFG_S                           20
+#define IPG_CFG_M                           0x300000
+#define EXT_PHY                             (1 << 19)
+#define MAC_MODE                            (1 << 18)
+#define MAC_TX_EN                           (1 << 16)
+#define MAC_RX_EN                           (1 << 15)
+#define MAC_PRE                             (1 << 14)
+#define BKOFF_EN                            (1 << 12)
+#define BACKPR_EN                           (1 << 11)
+#define FORCE_EEE1G                         (1 << 7)
+#define FORCE_EEE100                        (1 << 6)
+#define FORCE_RX_FC                         (1 << 5)
+#define FORCE_TX_FC                         (1 << 4)
+#define FORCE_SPD_S                         28
+#define FORCE_SPD_M                         0x70000000
+#define FORCE_DPX                           (1 << 25)
+#define FORCE_LINK                          (1 << 24)
+
+/* Fields of PMSR */
+#define EEE1G_STS                           (1 << 7)
+#define EEE100_STS                          (1 << 6)
+#define RX_FC_STS                           (1 << 5)
+#define TX_FC_STS                           (1 << 4)
+#define MAC_SPD_STS_S                       28
+#define MAC_SPD_STS_M                       0x70000000
+#define MAC_DPX_STS                         (1 << 25)
+#define MAC_LNK_STS                         (1 << 24)
+
+/* Values of MAC_SPD_STS */
+#define MAC_SPD_10                          0
+#define MAC_SPD_100                         1
+#define MAC_SPD_1000                        2
+#define MAC_SPD_2500                        3
+
+/* Values of IPG_CFG */
+#define IPG_96BIT                           0
+#define IPG_96BIT_WITH_SHORT_IPG            1
+#define IPG_64BIT                           2
+
+#define SGMII_REG_BASE                      0x5000
+#define SGMII_REG_PORT_BASE                 0x1000
+#define SGMII_REG(p, r)                     (SGMII_REG_BASE + (p) * SGMII_REG_PORT_BASE + (r))
+#define PCS_CONTROL_1(p)                    SGMII_REG(p, 0x00)
+#define SGMII_MODE(p)                       SGMII_REG(p, 0x20)
+#define QPHY_PWR_STATE_CTRL(p)              SGMII_REG(p, 0xe8)
+#define PHYA_CTRL_SIGNAL3(p)                SGMII_REG(p, 0x128)
+
+/* Fields of PCS_CONTROL_1 */
+#define SGMII_LINK_STATUS                   (1 << 18)
+#define SGMII_AN_ENABLE                     (1 << 12)
+#define SGMII_AN_RESTART                    (1 << 9)
+
+/* Fields of SGMII_MODE */
+#define SGMII_REMOTE_FAULT_DIS              (1 << 8)
+#define SGMII_IF_MODE_FORCE_DUPLEX          (1 << 4)
+#define SGMII_IF_MODE_FORCE_SPEED_S         0x2
+#define SGMII_IF_MODE_FORCE_SPEED_M         0x0c
+#define SGMII_IF_MODE_ADVERT_AN             (1 << 1)
+
+/* Values of SGMII_IF_MODE_FORCE_SPEED */
+#define SGMII_IF_MODE_FORCE_SPEED_10        0
+#define SGMII_IF_MODE_FORCE_SPEED_100       1
+#define SGMII_IF_MODE_FORCE_SPEED_1000      2
+
+/* Fields of QPHY_PWR_STATE_CTRL */
+#define PHYA_PWD                            (1 << 4)
+
+/* Fields of PHYA_CTRL_SIGNAL3 */
+#define RG_TPHY_SPEED_S                     2
+#define RG_TPHY_SPEED_M                     0x0c
+
+/* Values of RG_TPHY_SPEED */
+#define RG_TPHY_SPEED_1000                  0
+#define RG_TPHY_SPEED_2500                  1
+
+#define SYS_CTRL                            0x7000
+#define SW_PHY_RST                          (1 << 2)
+#define SW_SYS_RST                          (1 << 1)
+#define SW_REG_RST                          (1 << 0)
+
+#define PHY_IAC                             (0x1000e000)
+#define IAC_MAX_BUSY_TIME                   (1000)
+
+#define CLKGEN_CTRL                         0x7500
+#define CLK_SKEW_OUT_S                      8
+#define CLK_SKEW_OUT_M                      0x300
+#define CLK_SKEW_IN_S                       6
+#define CLK_SKEW_IN_M                       0xc0
+#define RXCLK_NO_DELAY                      (1 << 5)
+#define TXCLK_NO_REVERSE                    (1 << 4)
+#define GP_MODE_S                           1
+#define GP_MODE_M                           0x06
+#define GP_CLK_EN                           (1 << 0)
+
+/* Values of GP_MODE */
+#define GP_MODE_RGMII                       0
+#define GP_MODE_MII                         1
+#define GP_MODE_REV_MII                     2
+
+/* Values of CLK_SKEW_IN */
+#define CLK_SKEW_IN_NO_CHANGE               0
+#define CLK_SKEW_IN_DELAY_100PPS            1
+#define CLK_SKEW_IN_DELAY_200PPS            2
+#define CLK_SKEW_IN_REVERSE                 3
+
+/* Values of CLK_SKEW_OUT */
+#define CLK_SKEW_OUT_NO_CHANGE              0
+#define CLK_SKEW_OUT_DELAY_100PPS           1
+#define CLK_SKEW_OUT_DELAY_200PPS           2
+#define CLK_SKEW_OUT_REVERSE                3
+
+#define HWSTRAP                             0x7800
+#define XTAL_FSEL_S                         7
+#define XTAL_FSEL_M                         (1 << 7)
+
+#define XTAL_40MHZ                          0
+#define XTAL_25MHZ                          1
+
+#define PLLGP_EN                            0x7820
+#define EN_COREPLL                          (1 << 2)
+#define SW_CLKSW                            (1 << 1)
+#define SW_PLLGP                            (1 << 0)
+
+#define PLLGP_CR0                           0x78a8
+#define RG_COREPLL_EN                       (1 << 22)
+#define RG_COREPLL_POSDIV_S                 23
+#define RG_COREPLL_POSDIV_M                 0x3800000
+#define RG_COREPLL_SDM_PCW_S                1
+#define RG_COREPLL_SDM_PCW_M                0x3ffffe
+#define RG_COREPLL_SDM_PCW_CHG              (1 << 0)
+
+#define MHWSTRAP                            0x7804
+#define TOP_SIG_SR                          0x780c
+#define PAD_DUAL_SGMII_EN                   (1 << 1)
+
+/* RGMII and SGMII PLL clock */
+#define ANA_PLLGP_CR2                       0x78b0
+#define ANA_PLLGP_CR5                       0x78bc
+
+/* Efuse Register Define */
+#define GBE_EFUSE                           0x7bc8
+#define GBE_SEL_EFUSE_EN                    (1 << 0)
+
+/* GPIO_PAD_0 */
+#define GPIO_MODE0                          0x7c0c
+#define GPIO_MODE0_S                        0
+#define GPIO_MODE0_M                        0xf
+#define GPIO_0_INTERRUPT_MODE               0x1
+
+#define SMT0_IOLB                           0x7f04
+#define SMT_IOLB_5_SMI_MDC_EN               (1 << 5)
+
+#endif  /* End of AN8855_REG_H */
diff --git a/recipes-devtools/switch/files/src/switch_753x.c b/recipes-devtools/switch/files/src/switch_753x.c
index 6936f6a..03e9778 100644
--- a/recipes-devtools/switch/files/src/switch_753x.c
+++ b/recipes-devtools/switch/files/src/switch_753x.c
@@ -8,17 +8,19 @@
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <linux/if.h>
+#include <errno.h>
 
 #include "switch_netlink.h"
 #include "switch_ioctl.h"
 #include "switch_fun.h"
 #include "switch_fun_an8855.h"
 
-#define SWITCH_APP_VERSION "1.0.1"
+#define SWITCH_APP_VERSION "1.0.7"
 
 struct mt753x_attr *attres;
 int chip_name;
 bool nl_init_flag;
+bool air_skip_check_flag;
 struct switch_func_s *p_switch_func;
 
 static void usage(char *cmd)
@@ -427,27 +429,39 @@
 {
 	unsigned int val;
 	unsigned int off;
+	char *endptr;
 	int i, j;
 
 	if (!strncmp(argv[len - 3], "reg", 4)) {
 		if (argv[len - 2][0] == 'r') {
-			off = strtoul(argv[len - 1], NULL, 16);
+			errno = 0;
+			off = strtoul(argv[len - 1], &endptr, 16);
+			if (errno != 0 || *endptr != '\0')
+				goto error;
 			reg_read(off, &val);
 			printf(" Read reg=%x, value=%x\n", off, val);
 		} else if (argv[len - 2][0] == 'w') {
-			off = strtoul(argv[len - 1], NULL, 16);
+			errno = 0;
+			off = strtoul(argv[len - 1], &endptr, 16);
+			if (errno != 0 || *endptr != '\0')
+				goto error;
 			if (argc != len + 1)
 				usage(argv[0]);
-			val = strtoul(argv[len], NULL, 16);
+			errno = 0;
+			val = strtoul(argv[len], &endptr, 16);
+			if (errno != 0 || *endptr != '\0')
+				goto error;
 			reg_write(off, val);
 			printf(" Write reg=%x, value=%x\n", off, val);
 		} else if (argv[len - 2][0] == 'd') {
-			off = strtoul(argv[len - 1], NULL, 16);
+			errno = 0;
+			off = strtoul(argv[len - 1], &endptr, 16);
+			if (errno != 0 || *endptr != '\0')
+				goto error;
 			for (i = 0; i < 16; i++) {
 				printf("0x%08x: ", off + 0x10 * i);
 				for (j = 0; j < 4; j++) {
-					reg_read(off + i * 0x10 + j * 0x4,
-						 &val);
+					reg_read(off + i * 0x10 + j * 0x4, &val);
 					printf(" 0x%08x", val);
 				}
 				printf("\n");
@@ -456,6 +470,9 @@
 			usage(argv[0]);
 	} else
 		usage(argv[0]);
+	return;
+error:
+	printf("Error: string converting\n");
 }
 
 static int get_chip_name()
@@ -464,6 +481,12 @@
 	FILE *fp = NULL;
 	char buff[255];
 
+	/*judge an8855, must be placed before reg_read*/
+	if (air_skip_check_flag) {
+		temp = 0x8855;
+		return temp;
+	}
+
 	/*judge jaguar embedded switch */
 	fp = fopen("/proc/device-tree/compatible", "r");
 	if (fp != NULL) {
@@ -488,11 +511,6 @@
 	if (temp == 0x7531)
 		return temp;
 
-	/*judge an8855 */
-	reg_read(0x10005000, &temp);
-	if (temp == 0x8855)
-		return temp;
-
 	return -1;
 }
 
@@ -622,7 +640,9 @@
 
 int main(int argc, char *argv[])
 {
-	int err;
+	int err = -EINVAL;
+	FILE *fp = NULL;
+	char buff[255];
 
 	attres = (struct mt753x_attr *)malloc(sizeof(struct mt753x_attr));
 	if (attres == NULL) {
@@ -633,28 +653,35 @@
 	attres->port_num = -1;
 	attres->phy_dev = -1;
 	nl_init_flag = true;
+	air_skip_check_flag = false;
 
-	/* dsa netlink family might not be enabled. Try gsw netlink family. */
-	err = mt753x_netlink_init(MT753X_DSA_GENL_NAME);
-	if (!err)
-		chip_name = get_chip_name();
+	fp = fopen("/proc/air_sw/device", "r");
+	if (fp != NULL) {
+		if (fgets(buff, 255, (FILE *) fp) && strstr(buff, "an8855"))
+			air_skip_check_flag = true;
 
-	if (err < 0) {
-		err = mt753x_netlink_init(MT753X_GENL_NAME);
-		if (!err)
-			chip_name = get_chip_name();
-	}
+		if ((fclose(fp) == 0) && air_skip_check_flag) {
+			err = mt753x_netlink_init(AN8855_DSA_GENL_NAME);
+			if (!err)
+				chip_name = get_chip_name();
 
-	if (err < 0) {
-		err = mt753x_netlink_init(AN8855_DSA_GENL_NAME);
+			if (err < 0) {
+				err = mt753x_netlink_init(AN8855_GENL_NAME);
+				if (!err)
+					chip_name = get_chip_name();
+			}
+		}
+	} else {
+		/* dsa netlink family might not be enabled. Try gsw netlink family. */
+		err = mt753x_netlink_init(MT753X_DSA_GENL_NAME);
 		if (!err)
 			chip_name = get_chip_name();
-	}
 
-	if (err < 0) {
-		err = mt753x_netlink_init(AN8855_GENL_NAME);
-		if (!err)
-			chip_name = get_chip_name();
+		if (err < 0) {
+			err = mt753x_netlink_init(MT753X_GENL_NAME);
+			if (!err)
+				chip_name = get_chip_name();
+		}
 	}
 
 	if (err < 0) {
diff --git a/recipes-devtools/switch/files/src/switch_extend.h b/recipes-devtools/switch/files/src/switch_extend.h
index c352767..a35316e 100644
--- a/recipes-devtools/switch/files/src/switch_extend.h
+++ b/recipes-devtools/switch/files/src/switch_extend.h
@@ -154,7 +154,7 @@
 #define REG_VAWD2_ADDR			(0x0098)
 #define REG_VLAN_ID_BASE		(0x0100)
 
-#define REG_CPGC_ADDR 			(0xB0)
+#define REG_CPGC_ADDR			(0xB0)
 #define REG_CPCG_COL_EN_OFFT		(0)
 #define REG_CPCG_COL_EN_RELMASK		(0x00000001)
 #define REG_CPCG_COL_EN_MASK		(REG_CPCG_COL_EN_RELMASK << REG_CPCG_COL_EN_OFFT)
@@ -336,7 +336,7 @@
 						 "         1: Blocking/Listening/Discarding\n" \
 						 "         2: Learning\n" \
 						 "         3: Forwarding\n"
-#define HELP_COLLISION_POOL_EN	"collision-pool enable [enable 0|1] \n"
-#define HELP_EEE_EN		"eee [enable|disable] ([port|portMap]) \n"
+#define HELP_COLLISION_POOL_EN	"collision-pool enable [enable 0|1]\n"
+#define HELP_EEE_EN		"eee [enable|disable] ([port|portMap])\n"
 
 //#endif //SQA_VERIFY
diff --git a/recipes-devtools/switch/files/src/switch_fun.c b/recipes-devtools/switch/files/src/switch_fun.c
index 4745cf5..4239e1a 100644
--- a/recipes-devtools/switch/files/src/switch_fun.c
+++ b/recipes-devtools/switch/files/src/switch_fun.c
@@ -1,6 +1,6 @@
 /*
-* switch_fun.c: switch function sets
-*/
+ * switch_fun.c: switch function sets
+ */
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -202,7 +202,7 @@
 	int ret;
 
 	if (port_num > 31) {
-		printf("Invalid Port or PHY addr \n");
+		printf("Invalid Port or PHY addr\n");
 		return -1;
 	}
 
@@ -225,7 +225,7 @@
 	int ret;
 
 	if (port_num > 31) {
-		printf("Invalid Port or PHY addr \n");
+		printf("Invalid Port or PHY addr\n");
 		return -1;
 	}
 
@@ -249,7 +249,7 @@
 	int ret;
 
 	if (port_num > 31) {
-		printf("Invalid Port or PHY addr \n");
+		printf("Invalid Port or PHY addr\n");
 		return -1;
 	}
 
@@ -273,7 +273,7 @@
 	int ret;
 
 	if (port_num > 31) {
-		printf("Invalid Port or PHY addr \n");
+		printf("Invalid Port or PHY addr\n");
 		return -1;
 	}
 
@@ -335,8 +335,8 @@
 	}
 
 	printf("mii_mgr_cl45:");
-	printf("Read:  port#=%d, device=0x%x, reg=0x%x, value=0x%x\n", port_num,
-	       0x1E, MT7530_T10_TEST_CONTROL, value);
+	printf("Read:  port#=%d, device=0x%x, reg=0x%x, value=0x%x\n",
+			port_num, 0x1E, MT7530_T10_TEST_CONTROL, value);
 
 	if (!strncmp(argv[3], "auto", 5)) {
 		value &= (~(0x3 << 3));
@@ -349,8 +349,8 @@
 		printf("invaild parameter\n");
 		return;
 	}
-	printf("Write: port#=%d, device=0x%x, reg=0x%x. value=0x%x\n", port_num,
-	       0x1E, MT7530_T10_TEST_CONTROL, value);
+	printf("Write: port#=%d, device=0x%x, reg=0x%x. value=0x%x\n",
+		port_num, 0x1E, MT7530_T10_TEST_CONTROL, value);
 
 	if (nl_init_flag == true)
 		ret =
@@ -408,16 +408,15 @@
 			goto error;
 
 		printf("port = %x, ch_addr = %x, node_addr=%x, data_addr=%x\n",
-		       port_num, ch_addr, node_addr, data_addr);
+				port_num, ch_addr, node_addr, data_addr);
 		tr_reg_control =
 		    (1 << 15) | (1 << 13) | (ch_addr << 11) | (node_addr << 7) |
 		    (data_addr << 1);
 		mii_mgr_write(port_num, 16, tr_reg_control);	// r16 = tr_reg_control
 		mii_mgr_read(port_num, 17, &val_l);
 		mii_mgr_read(port_num, 18, &val_h);
-		printf
-		    ("switch trreg read tr_reg_control=%x, value_H=%x, value_L=%x\n",
-		     tr_reg_control, val_h, val_l);
+		printf("switch trreg read tr_reg_control=%x, value_H=%x, value_L=%x\n",
+				tr_reg_control, val_h, val_l);
 	} else if (argv[2][0] == 'w') {
 		if (argc != 9)
 			return -1;
@@ -456,16 +455,15 @@
 			goto error;
 
 		printf("port = %x, ch_addr = %x, node_addr=%x, data_addr=%x\n",
-		       port_num, ch_addr, node_addr, data_addr);
+				port_num, ch_addr, node_addr, data_addr);
 		tr_reg_control =
 		    (1 << 15) | (0 << 13) | (ch_addr << 11) | (node_addr << 7) |
 		    (data_addr << 1);
 		mii_mgr_write(port_num, 17, val_l);
 		mii_mgr_write(port_num, 18, val_h);
 		mii_mgr_write(port_num, 16, tr_reg_control);	// r16 = tr_reg_control
-		printf
-		    ("switch trreg Write tr_reg_control=%x, value_H=%x, value_L=%x\n",
-		     tr_reg_control, val_h, val_l);
+		printf("switch trreg Write tr_reg_control=%x, value_H=%x, value_L=%x\n",
+				tr_reg_control, val_h, val_l);
 	} else
 		return -1;
 
@@ -497,9 +495,8 @@
 	reg = REG_VTCR_ADDR;
 	while (1) {		// wait until not busy
 		reg_read(reg, &value);
-		if ((value & REG_VTCR_BUSY_MASK) == 0) {
+		if ((value & REG_VTCR_BUSY_MASK) == 0)
 			break;
-		}
 	}
 	reg_write(REG_VAWD1_ADDR, vawd1);
 	printf("write reg: %x, value: %x\n", REG_VAWD1_ADDR, vawd1);
@@ -521,11 +518,17 @@
 {
 	unsigned int vawd1 = 0, vawd2 = 0;
 	unsigned char tbl_idx = 0;
+	unsigned int max_index = 0;
 	char *endptr;
 
+	if (chip_name == 0x7531 || chip_name == 0x7988)
+		max_index = 256;
+	else
+		max_index = 64;
+
 	errno = 0;
 	tbl_idx = strtoul(argv[3], &endptr, 10);
-	if (errno != 0 || *endptr != '\0') {
+	if (errno != 0 || *endptr != '\0' || tbl_idx >= max_index) {
 		printf("Error: wrong ACL rule table index\n");
 		return;
 	}
@@ -589,11 +592,17 @@
 {
 	unsigned int vawd1 = 0, vawd2 = 0;
 	unsigned char tbl_idx = 0;
+	unsigned int max_index = 0;
 	char *endptr;
 
+	if (chip_name == 0x7531 || chip_name == 0x7988)
+		max_index = 128;
+	else
+		max_index = 32;
+
 	errno = 0;
 	tbl_idx = strtoul(argv[3], &endptr, 10);
-	if (errno != 0 || *endptr != '\0') {
+	if (errno != 0 || *endptr != '\0' || tbl_idx >= max_index) {
 		printf("Error: wrong ACL mask table index\n");
 		return;
 	}
@@ -628,7 +637,8 @@
 
 	printf("Rule_control_tbl_idx:%d\n", tbl_idx);
 
-	if (tbl_idx >= max_index) {	/* Check the input parameters is right or not. */
+	if (tbl_idx >= max_index) {
+		/* Check the input parameters is right or not. */
 		printf(HELP_ACL_RULE_TBL_ADD);
 		return;
 	}
@@ -636,9 +646,8 @@
 
 	while (1) {		// wait until not busy
 		reg_read(reg, &value);
-		if ((value & REG_VTCR_BUSY_MASK) == 0) {
+		if ((value & REG_VTCR_BUSY_MASK) == 0)
 			break;
-		}
 	}
 	reg_write(REG_VAWD1_ADDR, vawd1);
 	printf("write reg: %x, value: %x\n", REG_VAWD1_ADDR, vawd1);
@@ -651,9 +660,8 @@
 
 	while (1) {		// wait until not busy
 		reg_read(reg, &value);
-		if ((value & REG_VTCR_BUSY_MASK) == 0) {
+		if ((value & REG_VTCR_BUSY_MASK) == 0)
 			break;
-		}
 	}
 }
 
@@ -661,11 +669,17 @@
 {
 	unsigned int vawd1 = 0, vawd2 = 0;
 	unsigned char tbl_idx = 0;
+	unsigned int max_index = 0;
 	char *endptr;
 
+	if (chip_name == 0x7531 || chip_name == 0x7988)
+		max_index = 128;
+	else
+		max_index = 32;
+
 	errno = 0;
 	tbl_idx = strtoul(argv[3], &endptr, 10);
-	if (errno != 0 || *endptr != '\0') {
+	if (errno != 0 || *endptr != '\0' || tbl_idx >= max_index) {
 		printf("Error: wrong ACL rule control table index\n");
 		return;
 	}
@@ -727,11 +741,12 @@
 {
 	unsigned int vawd1 = 0, vawd2 = 0;
 	unsigned char tbl_idx = 0;
+	unsigned int max_index = 32;
 	char *endptr;
 
 	errno = 0;
 	tbl_idx = strtoul(argv[3], &endptr, 10);
-	if (errno != 0 || *endptr != '\0') {
+	if (errno != 0 || *endptr != '\0' || tbl_idx >= max_index) {
 		printf("Error: wrong ACL rate control table index\n");
 		return;
 	}
@@ -802,9 +817,8 @@
 
 	if (len2 == 12) {
 		if (!argv[4] || strlen(argv[4]) != len2) {
-			printf
-			    ("The [%s] format error, should be of length %d\n",
-			     argv[4], len2);
+			printf("The [%s] format error, should be of length %d\n",
+					argv[4], len2);
 			return -1;
 		}
 	}
@@ -816,8 +830,7 @@
 
 	for (i = 0; i < 7; i++) {
 		if (argv[5][i] != '0' && argv[5][i] != '1') {
-			printf
-			    ("portmap format error, should be of combination of 0 or 1\n");
+			printf("portmap format error, should be of combination of 0 or 1\n");
 			return -1;
 		}
 		*port += (argv[5][i] - '0') * (1 << i);
@@ -890,7 +903,6 @@
 
 error:
 	printf("Error: string converting\n");
-	return;
 }
 
 void acl_dip_meter(int argc, char *argv[])
@@ -1214,8 +1226,7 @@
 		table_size = 0x40;
 		reg_write(REG_ATC_ADDR, 0x811c);	//dip search command
 	}
-	printf
-	    ("hash   port(0:6)   rsp_cnt  flag  timer    dip-address       ATRD\n");
+	printf("hash   port(0:6)   rsp_cnt  flag  timer    dip-address       ATRD\n");
 	for (i = 0; i < table_size; i++) {
 		while (1) {
 			reg_read(REG_ATC_ADDR, &value);
@@ -1292,10 +1303,6 @@
 	reg_write(REG_ATA1_ADDR, value);
 	printf("REG_ATA1_ADDR is 0x%x\n\r", value);
 
-#if 0
-	reg_write(REG_ATA2_ADDR, value);
-	printf("REG_ATA2_ADDR is 0x%x\n\r", value);
-#endif
 	if (!argv[4] || strlen(argv[4]) != 8) {
 		printf("portmap format error, should be of length 7\n");
 		return;
@@ -1303,8 +1310,7 @@
 	j = 0;
 	for (i = 0; i < 7; i++) {
 		if (argv[4][i] != '0' && argv[4][i] != '1') {
-			printf
-			    ("portmap format error, should be of combination of 0 or 1\n");
+			printf("portmap format error, should be of combination of 0 or 1\n");
 			return;
 		}
 		j += (argv[4][i] - '0') * (1 << i);
@@ -1368,7 +1374,6 @@
 
 void dip_clear(int argc, char *argv[])
 {
-
 	unsigned int value = 0;
 
 	reg_write(REG_ATC_ADDR, 0x8102);	//clear all dip
@@ -1481,8 +1486,7 @@
 	j = 0;
 	for (i = 0; i < 7; i++) {
 		if (argv[5][i] != '0' && argv[5][i] != '1') {
-			printf
-			    ("portmap format error, should be of combination of 0 or 1\n");
+			printf("portmap format error, should be of combination of 0 or 1\n");
 			return;
 		}
 		j += (argv[5][i] - '0') * (1 << i);
@@ -1571,8 +1575,7 @@
 		table_end = 0x3F;
 		reg_write(REG_ATC_ADDR, 0x800C);
 	}
-	printf
-	    ("hash  port(0:6)   fid   vid  age(s)   mac-address     filter my_mac\n");
+	printf("hash  port(0:6)   fid   vid  age(s)   mac-address     filter my_mac\n");
 	for (i = 0; i < table_size; i++) {
 		while (1) {
 			reg_read(REG_ATC_ADDR, &value);
@@ -1623,8 +1626,7 @@
 				}
 				break;
 			} else if ((value & 0x4000) && (((value >> 15) & 0x1) == 0) && (((value >> 16) & 0xfff) == table_end)) {	//at_table_end
-				printf("found the last entry %d (not ready)\n",
-				       i);
+				printf("found the last entry %d (not ready)\n", i);
 				return;
 			} else
 				usleep(5);
@@ -1702,8 +1704,7 @@
 	j = 0;
 	for (i = 0; i < 7; i++) {
 		if (argv[3][i] != '0' && argv[3][i] != '1') {
-			printf
-			    ("portmap format error, should be of combination of 0 or 1\n");
+			printf("portmap format error, should be of combination of 0 or 1\n");
 			return;
 		}
 		j += (argv[3][i] - '0') * (1 << i);
@@ -2154,8 +2155,7 @@
 	}
 
 	if (eg_tag)
-		printf
-		    ("  vid  fid  portmap    s-tag\teg_tag(0:untagged 2:tagged)\n");
+		printf("  vid  fid  portmap    s-tag\teg_tag(0:untagged 2:tagged)\n");
 	else
 		printf("  vid  fid  portmap    s-tag\n");
 
@@ -2305,8 +2305,7 @@
 	vlan_mem = 0;
 	for (i = 0; i < 8; i++) {
 		if (argv[5][i] != '0' && argv[5][i] != '1') {
-			printf
-			    ("portmap format error, should be of combination of 0 or 1\n");
+			printf("portmap format error, should be of combination of 0 or 1\n");
 			return;
 		}
 		vlan_mem += (argv[5][i] - '0') * (1 << i);
@@ -2316,8 +2315,7 @@
 	if (argc > 6) {
 		stag = strtoul(argv[6], NULL, 16);
 		if (stag < 0 || 0xfff < stag) {
-			printf
-			    ("wrong stag id range, should be within 0~4095\n");
+			printf("wrong stag id range, should be within 0~4095\n");
 			return;
 		}
 		//printf("STAG is 0x%x\n", stag);
@@ -2338,15 +2336,13 @@
 
 	if (argc > 8 && !eg_con) {
 		if (strlen(argv[8]) != 8) {
-			printf
-			    ("egtag portmap format error, should be of length 7\n");
+			printf("egtag portmap format error, should be of length 7\n");
 			return;
 		}
 
 		for (i = 0; i < 8; i++) {
 			if (argv[8][i] < '0' || argv[8][i] > '3') {
-				printf
-				    ("egtag portmap format error, should be of combination of 0 or 3\n");
+				printf("egtag portmap format error, should be of combination of 0 or 3\n");
 				return;
 			}
 			//eg_tag += (argv[8][i] - '0') * (1 << i * 2);
@@ -2592,13 +2588,13 @@
 	/*Software Register Reset  and Software System Reset */
 	reg_write(0x7000, 0x3);
 	reg_read(0x7000, &value);
-	printf("SYS_CTRL(0x7000) register value =0x%x  \n", value);
+	printf("SYS_CTRL(0x7000) register value =0x%x\n", value);
 	if (chip_name == 0x7531) {
 		reg_write(0x7c0c, 0x11111111);
 		reg_read(0x7c0c, &value);
-		printf("GPIO Mode (0x7c0c) select value =0x%x  \n", value);
+		printf("GPIO Mode (0x7c0c) select value =0x%x\n", value);
 	}
-	printf("Switch Software Reset !!! \n");
+	printf("Switch Software Reset !!!\n");
 }
 
 void phy_set_fc(int argc, char *argv[])
@@ -2633,8 +2629,7 @@
 	}
 	mii_mgr_write(port, 4, phy_value);
 	printf("write phy_value:0x%x\r\n", phy_value);
-	return;
-}				/*end phy_set_fc */
+}
 
 void phy_set_an(int argc, char *argv[])
 {
@@ -2664,7 +2659,7 @@
 	phy_value |= (auto_negotiation_en << 12);
 	mii_mgr_write(port, 0, phy_value);
 	printf("write phy_value:0x%x\r\n", phy_value);
-}				/*end phy_set_an */
+}
 
 void set_mac_pfc(int argc, char *argv[])
 {
@@ -2887,9 +2882,8 @@
 
 	port = atoi(argv[3]);
 
-	for (i = 0; i < 8; i++) {
+	for (i = 0; i < 8; i++)
 		weight[i] = atoi(argv[i + 4]);
-	}
 
 	/* MT7530 total 7 port */
 	if (port < 0 || port > 6) {
@@ -3087,7 +3081,7 @@
 	reg_read(reg, &value);
 
 	printf("SetVid: index:%d active:%d vid:%d portMap:%x tagPortMap:%x\r\n",
-	       index, active, vid, portMap, tagPortMap);
+			index, active, vid, portMap, tagPortMap);
 	return 0;
 
 }				/*end macMT753xVlanSetVid */
@@ -3140,8 +3134,8 @@
 	macMT753xVlanSetPvid(port, pvid);
 
 	printf("port:%d pvid:%d,vlancap: max_port:%d maxvid:%d\r\n",
-	       port, pvid, SWITCH_MAX_PORT, MAX_VID_VALUE);
-}				/*end doVlanSetPvid */
+			port, pvid, SWITCH_MAX_PORT, MAX_VID_VALUE);
+}
 
 void doVlanSetVid(int argc, char *argv[])
 {
@@ -3212,9 +3206,9 @@
 		}
 	}
 	macMT753xVlanSetVid(index, active, vid, portMap, tagPortMap,
-			    ivl_en, fid, stag);
+						ivl_en, fid, stag);
 	printf("index:%d active:%d vid:%d\r\n", index, active, vid);
-}				/*end doVlanSetVid */
+}
 
 void doVlanSetAccFrm(int argc, char *argv[])
 {
@@ -3247,7 +3241,7 @@
 
 	printf("write reg: %x, value: %x\n", reg, value);
 	reg_write(reg, value);
-}				/*end doVlanSetAccFrm */
+}
 
 void doVlanSetPortAttr(int argc, char *argv[])
 {
@@ -3407,9 +3401,8 @@
 	reg = 0xa0;
 	reg_read(reg, &value);
 	value &= (~(1 << 20));
-	if (!aging_en) {
+	if (!aging_en)
 		value |= (1 << 20);
-	}
 
 	aging_unit = (time / 0x100) + 1;
 	aging_cnt = (time / aging_unit);
@@ -3497,9 +3490,8 @@
 	if (errno != 0 || *endptr != '\0' || igmp_mir > 1)
 		goto error;
 
-	printf
-	    ("port:%d, port_tx_mir:%d, port_rx_mir:%d, acl_mir:%d, vlan_mis:%d, igmp_mir:%d\n",
-	     port, port_tx_mir, port_rx_mir, acl_mir, vlan_mis, igmp_mir);
+	printf("port:%d, port_tx_mir:%d, port_rx_mir:%d, acl_mir:%d, vlan_mis:%d, igmp_mir:%d\n",
+			port, port_tx_mir, port_rx_mir, acl_mir, vlan_mis, igmp_mir);
 
 	reg = REG_PCR_P0_ADDR + port * 0x100;
 	reg_read(reg, &value);
@@ -3581,37 +3573,26 @@
 	if (on_off == 1) {
 		if (chip_name == 0x7530) {
 			if (bw > 1000000) {
-				printf
-				    ("\n**Charge rate(%d) is larger than line rate(1000000kbps)**\n",
-				     bw);
+				printf("\n**Charge rate(%d) is larger than line rate(1000000kbps)**\n", bw);
 				return;
 			}
-			value =
-			    ((bw / 32) << 16) + (1 << 15) + (7 << 8) +
-			    (1 << 7) + 0x0f;
+			value = ((bw / 32) << 16) + (1 << 15) + (7 << 8) + (1 << 7) + 0x0f;
 		} else if (chip_name == 0x7531 || chip_name == 0x7988) {
 			if ((chip_name == 0x7531) && (bw > 2500000)) {
-				printf
-				    ("\n**Charge rate(%d) is larger than line rate(2500000kbps)**\n",
-				     bw);
+				printf("\n**Charge rate(%d) is larger than line rate(2500000kbps)**\n", bw);
 				return;
 			}
 
 			if ((chip_name == 0x7988) && (bw > 4000000)) {
-				printf
-				    ("\n**Charge rate(%d) is larger than line rate(4000000kbps)**\n",
-				     bw);
+				printf("\n**Charge rate(%d) is larger than line rate(4000000kbps)**\n", bw);
 				return;
 			}
 
 			if (bw / 32 >= 65536)	//supoort 2.5G case
-				value =
-				    ((bw / 32) << 16) + (1 << 15) + (1 << 14) +
-				    (1 << 12) + (7 << 8) + 0xf;
+				value = ((bw / 32) << 16) + (1 << 15) + (1 << 14) + (1 << 12) +
+					(7 << 8) + 0xf;
 			else
-				value =
-				    ((bw / 32) << 16) + (1 << 15) + (1 << 14) +
-				    (7 << 8) + 0xf;
+				value = ((bw / 32) << 16) + (1 << 15) + (1 << 14) + (7 << 8) + 0xf;
 		} else
 			printf("unknow chip\n");
 	}
@@ -3705,36 +3686,25 @@
 	if (on_off == 1) {
 		if (chip_name == 0x7530) {
 			if (bw < 0 || bw > 1000000) {
-				printf
-				    ("\n**Charge rate(%d) is larger than line rate(1000000kbps)**\n",
-				     bw);
+				printf("\n**Charge rate(%d) is larger than line rate(1000000kbps)**\n", bw);
 				return;
 			}
-			value =
-			    ((bw / 32) << 16) + (1 << 15) + (7 << 8) +
-			    (1 << 7) + 0xf;
+			value = ((bw / 32) << 16) + (1 << 15) + (7 << 8) + (1 << 7) + 0xf;
 		} else if (chip_name == 0x7531 || chip_name == 0x7988) {
 			if ((chip_name == 0x7531) && (bw < 0 || bw > 2500000)) {
-				printf
-				    ("\n**Charge rate(%d) is larger than line rate(2500000kbps)**\n",
-				     bw);
+				printf("\n**Charge rate(%d) is larger than line rate(2500000kbps)**\n", bw);
 				return;
 			}
 			if ((chip_name == 0x7988) && (bw < 0 || bw > 4000000)) {
-				printf
-				    ("\n**Charge rate(%d) is larger than line rate(4000000kbps)**\n",
-				     bw);
+				printf("\n**Charge rate(%d) is larger than line rate(4000000kbps)**\n", bw);
 				return;
 			}
 
 			if (bw / 32 >= 65536)	//support 2.5G cases
-				value =
-				    ((bw / 32) << 16) + (1 << 15) + (1 << 14) +
-				    (1 << 12) + (7 << 8) + 0xf;
+				value = ((bw / 32) << 16) + (1 << 15) + (1 << 14) + (1 << 12) +
+					(7 << 8) + 0xf;
 			else
-				value =
-				    ((bw / 32) << 16) + (1 << 15) + (1 << 14) +
-				    (7 << 8) + 0xf;
+				value = ((bw / 32) << 16) + (1 << 15) + (1 << 14) + (7 << 8) + 0xf;
 		} else
 			printf("unknow chip\n");
 	}
@@ -3834,14 +3804,13 @@
 
 	enable = atoi(argv[3]);
 
-	printf("collision pool enable: %d \n", enable);
-
 	/*Check the input parameters is right or not. */
 	if (enable > 1) {
 		printf(HELP_COLLISION_POOL_EN);
 		return;
 	}
 
+	printf("collision pool enable: %d\n", enable);
 	if (chip_name == 0x7531 || chip_name == 0x7988) {
 		reg = REG_CPGC_ADDR;
 		if (enable == 1) {
@@ -3912,8 +3881,7 @@
 		if (value & REG_CPCG_COL_EN_MASK)
 			table_dump_internal(COLLISION_TABLE);
 		else
-			printf
-			    ("\ncollision pool is disabled, please enable it before use this command.\n");
+			printf("\ncollision pool is disabled, please enable it before use this command.\n");
 	} else {
 		printf("\nCommand not support by this chip.\n");
 	}
@@ -3929,8 +3897,7 @@
 		if (value & REG_CPCG_COL_EN_MASK)
 			dip_dump_internal(COLLISION_TABLE);
 		else
-			printf
-			    ("\ncollision pool is disabled, please enable it before use this command.\n");
+			printf("\ncollision pool is disabled, please enable it before use this command.\n");
 	} else {
 		printf("\nCommand not support by this chip.\n");
 	}
@@ -3946,8 +3913,7 @@
 		if (value & REG_CPCG_COL_EN_MASK)
 			sip_dump_internal(COLLISION_TABLE);
 		else
-			printf
-			    ("\ncollision pool is disabled, please enable it before use this command.\n");
+			printf("\ncollision pool is disabled, please enable it before use this command.\n");
 	} else {
 		printf("\nCommand not support by this chip.\n");
 	}
@@ -4229,15 +4195,13 @@
 			port_map = 0;
 			for (p = 0; p < MAX_PHY_PORT; p++) {
 				if (argv[3][p] != '0' && argv[3][p] != '1') {
-					printf
-					    ("portmap format error, should be combination of 0 or 1\n");
+					printf("portmap format error, should be combination of 0 or 1\n");
 					goto error;
 				}
 				port_map |= ((argv[3][p] - '0') << p);
 			}
 		} else {
-			printf
-			    ("port_no or portmap format error, should be length of 1 or 5\n");
+			printf("port_no or portmap format error, should be length of 1 or 5\n");
 			goto error;
 		}
 	} else {
@@ -4284,7 +4248,6 @@
 	return;
 error:
 	printf(HELP_EEE_EN);
-	return;
 }
 
 void eee_dump(int argc, char *argv[])
@@ -4395,7 +4358,7 @@
 	reg_write(0x4fe0, 0x800000f0);
 }
 
-void exit_free()
+void exit_free(void)
 {
 	free(attres);
 	attres = NULL;
diff --git a/recipes-devtools/switch/files/src/switch_fun.h b/recipes-devtools/switch/files/src/switch_fun.h
index cc16c20..7ca1b0a 100644
--- a/recipes-devtools/switch/files/src/switch_fun.h
+++ b/recipes-devtools/switch/files/src/switch_fun.h
@@ -102,9 +102,9 @@
 int mii_mgr_read(unsigned int port_num, unsigned int reg, unsigned int *value);
 int mii_mgr_write(unsigned int port_num, unsigned int reg, unsigned int value);
 int mii_mgr_c45_read(unsigned int port_num, unsigned int dev, unsigned int reg,
-		     unsigned int *value);
+		unsigned int *value);
 int mii_mgr_c45_write(unsigned int port_num, unsigned int dev, unsigned int reg,
-		      unsigned int value);
+		unsigned int value);
 
 /*phy setting*/
 int phy_dump(int phy_addr);
@@ -222,5 +222,5 @@
 void read_free_page_counters(int argc, char *argv[]);
 
 void phy_crossover(int argc, char *argv[]);
-void exit_free();
+void exit_free(void);
 #endif
diff --git a/recipes-devtools/switch/files/src/switch_fun_an8855.c b/recipes-devtools/switch/files/src/switch_fun_an8855.c
index e5ec490..3f5d9f6 100644
--- a/recipes-devtools/switch/files/src/switch_fun_an8855.c
+++ b/recipes-devtools/switch/files/src/switch_fun_an8855.c
@@ -1,6 +1,6 @@
 /*
-* switch_fun.c: switch function sets
-*/
+ * switch_fun.c: switch function sets
+ */
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -18,9 +18,8 @@
 #include "switch_fun_an8855.h"
 
 #define MAC_STR         "%02X%02X%02X%02X%02X%02X"
-#define MAC2STR(m)      (m)[0],(m)[1],(m)[2],(m)[3],(m)[4],(m)[5]
 
-const static C8_T *mac_address_forward_control_string[] = {
+const static C8_T * mac_address_forward_control_string[] = {
 	"Default",
 	"CPU include",
 	"CPU exclude",
@@ -105,14 +104,13 @@
 };
 
 AIR_ERROR_NO_T
-an8855_reg_read(const UI32_T unit, const UI32_T addr_offset, UI32_T * ptr_data)
+an8855_reg_read(const UI32_T unit, const UI32_T addr_offset, UI32_T *ptr_data)
 {
 	int ret;
 
 	ret = reg_read(addr_offset, ptr_data);
-	if (ret < 0) {
+	if (ret < 0)
 		return AIR_E_OTHERS;
-	}
 
 	return AIR_E_OK;
 }
@@ -123,9 +121,8 @@
 	int ret;
 
 	ret = reg_write(addr_offset, data);
-	if (ret < 0) {
+	if (ret < 0)
 		return AIR_E_OTHERS;
-	}
 
 	return AIR_E_OK;
 }
@@ -133,14 +130,13 @@
 AIR_ERROR_NO_T
 an8855_phy_cl22_read(const UI32_T unit,
 		     const UI32_T port_id,
-		     const UI32_T addr_offset, UI32_T * ptr_data)
+		     const UI32_T addr_offset, UI32_T *ptr_data)
 {
 	int ret;
 
 	ret = mii_mgr_read(port_id, addr_offset, ptr_data);
-	if (ret < 0) {
+	if (ret < 0)
 		return AIR_E_OTHERS;
-	}
 
 	return AIR_E_OK;
 }
@@ -153,9 +149,9 @@
 	int ret;
 
 	ret = mii_mgr_write(port_id, addr_offset, data);
-	if (ret < 0) {
+	if (ret < 0)
 		return AIR_E_OTHERS;
-	}
+
 
 	return AIR_E_OK;
 }
@@ -164,14 +160,14 @@
 an8855_phy_cl45_read(const UI32_T unit,
 		     const UI32_T port_id,
 		     const UI32_T dev_type,
-		     const UI32_T addr_offset, UI32_T * ptr_data)
+		     const UI32_T addr_offset, UI32_T *ptr_data)
 {
 	int ret;
 
 	ret = mii_mgr_c45_read(port_id, dev_type, addr_offset, ptr_data);
-	if (ret < 0) {
+	if (ret < 0)
 		return AIR_E_OTHERS;
-	}
+
 
 	return AIR_E_OK;
 }
@@ -185,9 +181,9 @@
 	int ret;
 
 	ret = mii_mgr_c45_write(port_id, dev_type, addr_offset, data);
-	if (ret < 0) {
+	if (ret < 0)
 		return AIR_E_OTHERS;
-	}
+
 
 	return AIR_E_OK;
 }
@@ -198,12 +194,13 @@
 }
 
 static AIR_ERROR_NO_T
-_printMacEntry(AIR_MAC_ENTRY_T * mt, UI32_T age_unit, UI8_T count, UI8_T title)
+_printMacEntry(AIR_MAC_ENTRY_T *mt, UI32_T age_unit, UI8_T count, UI8_T title)
 {
 	AIR_ERROR_NO_T ret = AIR_E_OK;
 	I32_T i = 0, j = 0;
 	UI8_T first = 0;
 	UI8_T find = 0;
+
 	if (title) {
 		printf("%-6s%-15s%-5s%-5s%-5s%-10s%-10s%-6s\n",
 		       "unit",
@@ -213,7 +210,8 @@
 	}
 	for (i = 0; i < count; i++) {
 		printf("%-6d", age_unit);
-		printf(MAC_STR, MAC2STR(mt[i].mac));
+		printf(MAC_STR, mt[i].mac[0], mt[i].mac[1], mt[i].mac[2],
+			 mt[i].mac[3], mt[i].mac[4], mt[i].mac[5]);
 		printf("...");
 		if (mt[i].flags & AIR_L2_MAC_ENTRY_FLAGS_IVL) {
 			printf("%-3s..", "ivl");
@@ -224,11 +222,11 @@
 			printf("%-5s", ".....");
 			printf("%-5d", mt[i].fid);
 		}
-		if (mt[i].flags & AIR_L2_MAC_ENTRY_FLAGS_STATIC) {
+		if (mt[i].flags & AIR_L2_MAC_ENTRY_FLAGS_STATIC)
 			printf("%-7s.", "static");
-		} else {
+		else
 			printf("%d sec..", mt[i].timer);
-		}
+
 		printf("%-10s",
 		       mac_address_forward_control_string[mt[i].sa_fwd]);
 		first = 0;
@@ -256,7 +254,7 @@
 	return ret;
 }
 
-static AIR_ERROR_NO_T _str2mac(C8_T * str, C8_T * mac)
+static AIR_ERROR_NO_T _str2mac(C8_T *str, C8_T *mac)
 {
 	UI32_T i;
 	C8_T tmpstr[3];
@@ -307,9 +305,9 @@
 	while (1) {
 		memset(ptr_mt, 0, sizeof(AIR_MAC_ENTRY_T) * bucket_size);
 		ret = air_l2_getNextMacAddr(0, &count, ptr_mt);
-		if (AIR_E_OK != ret) {
+		if (ret != AIR_E_OK)
 			break;
-		}
+
 		total_count += count;
 		_printMacEntry(ptr_mt, 0, count, FALSE);
 	}
@@ -328,7 +326,6 @@
 
 DUMP_ERROR:
 	free(ptr_mt);
-	return;
 }
 
 void an8855_table_dump(int argc, char *argv[])
@@ -342,6 +339,7 @@
 	AIR_MAC_ENTRY_T mt;
 	unsigned int i = 0;
 	unsigned int age_time = 0;
+
 	memset(&mt, 0, sizeof(AIR_MAC_ENTRY_T));
 	if (!argv[2] || strlen(argv[2]) != 12) {
 		printf("MAC address format error, should be of length 12\n");
@@ -354,7 +352,7 @@
 	}
 	if (argc > 4) {
 		mt.cvid = strtoul(argv[4], NULL, 0);
-		if (4095 < mt.cvid) {
+		if (mt.cvid > 4095) {
 			printf("wrong vid range, should be within 0~4095\n");
 			return;
 		}
@@ -379,9 +377,9 @@
 			printf("wrong age range, should be within 1~1000000\n");
 			return;
 		}
-	} else {
+	} else
 		mt.flags |= AIR_L2_MAC_ENTRY_FLAGS_STATIC;
-	}
+
 	mt.sa_fwd = AIR_L2_FWD_CTRL_DEFAULT;
 	ret = air_l2_addMacAddr(0, &mt);
 	if (ret == AIR_E_OK) {
@@ -389,16 +387,14 @@
 		usleep(5000);
 		if (!(mt.flags & AIR_L2_MAC_ENTRY_FLAGS_STATIC)) {
 			ret = air_l2_setMacAddrAgeOut(0, age_time);
-			if (ret == AIR_E_OK) {
+			if (ret == AIR_E_OK)
 				printf("set age out time done.\n");
-			} else {
+			else
 				printf("set age out time fail.\n");
-			}
+
 		}
-	} else {
+	} else
 		printf("add mac address fail.\n");
-	}
-	return;
 }
 
 void an8855_table_search_mac_vid(int argc, char *argv[])
@@ -414,7 +410,7 @@
 	}
 
 	ptr_mt = malloc(sizeof(AIR_MAC_ENTRY_T));
-	if (NULL == ptr_mt) {
+	if (ptr_mt == NULL) {
 		printf("Error, malloc fail\n");
 		return;
 	}
@@ -443,7 +439,6 @@
 		printf("\n Not found!\n");
 	}
 	free(ptr_mt);
-	return;
 }
 
 void an8855_table_search_mac_fid(int argc, char *argv[])
@@ -459,7 +454,7 @@
 	}
 
 	ptr_mt = malloc(sizeof(AIR_MAC_ENTRY_T));
-	if (NULL == ptr_mt) {
+	if (ptr_mt == NULL) {
 		printf("Error, malloc fail\n");
 		return;
 	}
@@ -483,11 +478,10 @@
 	if (ret == AIR_E_OK) {
 		_printMacEntry(ptr_mt, 0, 1, TRUE);
 		_printMacEntry(ptr_mt, 0, 1, FALSE);
-	} else {
+	} else
 		printf("\n Not found!\n");
-	}
+
 	free(ptr_mt);
-	return;
 }
 
 void an8855_table_del_fid(int argc, char *argv[])
@@ -516,12 +510,10 @@
 	}
 
 	ret = air_l2_delMacAddr(0, &mt);
-	if (ret == AIR_E_OK) {
+	if (ret == AIR_E_OK)
 		printf("Done.\n");
-	} else {
+	else
 		printf("Fail.\n");
-	}
-	return;
 }
 
 void an8855_table_del_vid(int argc, char *argv[])
@@ -551,12 +543,11 @@
 	}
 
 	ret = air_l2_delMacAddr(0, &mt);
-	if (ret == AIR_E_OK) {
+	if (ret == AIR_E_OK)
 		printf("Done.\n");
-	} else {
+	else
 		printf("Fail.\n");
-	}
-	return;
+
 }
 
 void an8855_table_clear(int argc, char *argv[])
@@ -568,7 +559,7 @@
 		printf("Clear MAC Address Table Done.\n");
 	else
 		printf("Clear MAC Address Table Fail.\n");
-	return;
+
 }
 
 void an8855_set_mirror_to(int argc, char *argv[])
@@ -577,7 +568,7 @@
 	AIR_MIR_SESSION_T session = { 0 };
 
 	idx = strtoul(argv[3], NULL, 0);
-	if (idx < 0 || MAX_PORT < idx) {
+	if (idx < 0 || idx > MAX_PORT) {
 		printf("wrong port member, should be within 0~%d\n", MAX_PORT);
 		return;
 	}
@@ -598,12 +589,12 @@
 	idx = _strtoul(argv[3], NULL, 0);
 	mirror = _strtoul(argv[4], NULL, 0);
 
-	if (idx < 0 || MAX_PORT < idx) {
+	if (idx < 0 || idx > MAX_PORT) {
 		printf("wrong port member, should be within 0~%d\n", MAX_PORT);
 		return;
 	}
 
-	if (mirror < 0 || 3 < mirror) {
+	if (mirror < 0 || mirror > 3) {
 		printf("wrong mirror setting, should be within 0~3\n");
 		return;
 	}
@@ -642,9 +633,9 @@
 
 	if (eg_tag)
 		printf
-		    ("  vid  fid  portmap    s-tag\teg_tag(0:untagged 2:tagged)\n");
+		    ("  vid  fid  portmap   s-tag\teg_tag(0:untagged 2:tagged)\n");
 	else
-		printf("  vid  fid  portmap    s-tag\n");
+		printf("  vid  fid  portmap   s-tag\n");
 
 	for (i = 1; i < 4096; i++) {
 		_air_vlan_readEntry(0, i, &vlan_entry);
@@ -652,73 +643,43 @@
 		if (vlan_entry.valid) {
 			printf(" %4d  ", i);
 			printf(" %2d ", vlan_entry.vlan_entry_format.fid);
-			printf(" %c",
-			       (vlan_entry.
-				vlan_entry_format.port_mem & 0b0000001) ? '1' :
-			       '-');
-			printf("%c",
-			       (vlan_entry.
-				vlan_entry_format.port_mem & 0b0000010) ? '1' :
-			       '-');
-			printf("%c",
-			       (vlan_entry.
-				vlan_entry_format.port_mem & 0b0000100) ? '1' :
-			       '-');
-			printf("%c",
-			       (vlan_entry.
-				vlan_entry_format.port_mem & 0b0001000) ? '1' :
-			       '-');
-			printf("%c",
-			       (vlan_entry.
-				vlan_entry_format.port_mem & 0b0010000) ? '1' :
-			       '-');
-			printf("%c",
-			       (vlan_entry.
-				vlan_entry_format.port_mem & 0b0100000) ? '1' :
-			       '-');
-			printf("%c",
-			       (vlan_entry.
-				vlan_entry_format.port_mem & 0b1000000) ? '1' :
-			       '-');
+			printf(" %c", (vlan_entry.vlan_entry_format.port_mem & 0b0000001)
+				? '1' : '-');
+			printf("%c", (vlan_entry.vlan_entry_format.port_mem & 0b0000010)
+				? '1' : '-');
+			printf("%c", (vlan_entry.vlan_entry_format.port_mem & 0b0000100)
+				? '1' : '-');
+			printf("%c", (vlan_entry.vlan_entry_format.port_mem & 0b0001000)
+				? '1' : '-');
+			printf("%c", (vlan_entry.vlan_entry_format.port_mem & 0b0010000)
+				? '1' : '-');
+			printf("%c", (vlan_entry.vlan_entry_format.port_mem & 0b0100000)
+				? '1' : '-');
+			printf("%c", (vlan_entry.vlan_entry_format.port_mem & 0b1000000)
+				? '1' : '-');
 			printf("    %4d", vlan_entry.vlan_entry_format.eg_ctrl);
 			if (eg_tag) {
 				printf("\t");
 				if (vlan_entry.vlan_entry_format.eg_con
-				    && vlan_entry.
-				    vlan_entry_format.eg_ctrl_en) {
+				    && vlan_entry.vlan_entry_format.eg_ctrl_en) {
 					/* VTAG_EN=1 and EG_CON=1 */
 					printf("CONSISTENT");
-				} else if (vlan_entry.
-					   vlan_entry_format.eg_ctrl_en) {
+				} else if (vlan_entry.vlan_entry_format.eg_ctrl_en) {
 					/* VTAG_EN=1 */
 					printf("%d",
-					       (vlan_entry.
-						vlan_entry_format.eg_ctrl >> 0)
-					       & 0x3);
+						(vlan_entry.vlan_entry_format.eg_ctrl >> 0) & 0x3);
 					printf("%d",
-					       (vlan_entry.
-						vlan_entry_format.eg_ctrl >> 2)
-					       & 0x3);
+						(vlan_entry.vlan_entry_format.eg_ctrl >> 2) & 0x3);
 					printf("%d",
-					       (vlan_entry.
-						vlan_entry_format.eg_ctrl >> 4)
-					       & 0x3);
+						(vlan_entry.vlan_entry_format.eg_ctrl >> 4) & 0x3);
 					printf("%d",
-					       (vlan_entry.
-						vlan_entry_format.eg_ctrl >> 6)
-					       & 0x3);
+						(vlan_entry.vlan_entry_format.eg_ctrl >> 6) & 0x3);
 					printf("%d",
-					       (vlan_entry.
-						vlan_entry_format.eg_ctrl >> 8)
-					       & 0x3);
+						(vlan_entry.vlan_entry_format.eg_ctrl >> 8) & 0x3);
 					printf("%d",
-					       (vlan_entry.
-						vlan_entry_format.eg_ctrl >> 10)
-					       & 0x3);
+						(vlan_entry.vlan_entry_format.eg_ctrl >> 10) & 0x3);
 					printf("%d",
-					       (vlan_entry.
-						vlan_entry_format.eg_ctrl >> 12)
-					       & 0x3);
+						(vlan_entry.vlan_entry_format.eg_ctrl >> 12) & 0x3);
 				} else {
 					/* VTAG_EN=0 */
 					printf("DISABLED");
@@ -802,7 +763,7 @@
 
 	if (argc > 7) {
 		eg_con = strtoul(argv[7], NULL, 2);
-		eg_con = ! !eg_con;
+		eg_con = !!eg_con;
 		vlan_entry.vlan_entry_format.eg_con = eg_con;
 		vlan_entry.vlan_entry_format.eg_ctrl_en = 1;
 	}
@@ -817,8 +778,8 @@
 
 		for (i = 0; i < SWITCH_MAX_PORT; i++) {
 			if (argv[8][i] < '0' || argv[8][i] > '3') {
-				printf
-				    ("egtag portmap format error, should be of combination of 0 or 3\n");
+				printf("egtag portmap format error,");
+				printf("should be of combination of 0 or 3\n");
 				return;
 			}
 			eg_tag |= (argv[8][i] - '0') << (i * 2);
@@ -947,8 +908,8 @@
 		return;
 	}
 
-	printf("\r\nswitch qos base : %d. (port-based:0, tag-based:1,\
-		dscp-based:2, acl-based:3, arl-based:4, stag-based:5)\n", base);
+	printf("\r\nswitch qos base : %d. (port-based:0, tag-based:1,", base);
+	printf("dscp-based:2, acl-based:3, arl-based:4, stag-based:5)\n");
 	reg_read(0x10208030 + 0x200 * port, &value);
 	an8855_get_upw(&value, base);
 	reg_write(0x10208030 + 0x200 * port, value);
@@ -962,9 +923,8 @@
 
 	port = _strtoul(argv[3], NULL, 10);
 
-	for (i = 0; i < 8; i++) {
+	for (i = 0; i < 8; i++)
 		weight[i] = _strtoul(argv[i + 4], NULL, 10);
-	}
 
 	/* MT7530 total 7 port */
 	if (port < 0 || port > 6) {
@@ -978,13 +938,15 @@
 			return;
 		}
 	}
-	printf("port: %x, q0: %x, q1: %x, q2: %x, q3: %x, \
-		q4: %x, q5: %x, q6: %x, q7: %x\n", port, weight[0], weight[1], weight[2], weight[3], weight[4], weight[5], weight[6], weight[7]);
+	printf("port: %x, q0: %x, q1: %x, q2: %x, q3: %x,",
+		port, weight[0], weight[1], weight[2], weight[3]);
+	printf("q4: %x, q5: %x, q6: %x, q7: %x\n",
+		weight[4], weight[5], weight[6], weight[7]);
 
-	for (queue = 0; queue < 8; queue++) {
+	for (queue = 0; queue < 8; queue++)
 		air_qos_setScheduleAlgo(0, port, queue, AIR_QOS_SCH_MODE_WFQ,
 					weight[queue]);
-	}
+
 }
 
 void an8855_qos_set_portpri(int argc, char *argv[])
@@ -1096,17 +1058,19 @@
 		}
 	}
 
-	printf("index: %x, active: %x, vid: %x, portMap: %x, \
-		tagPortMap: %x, ivl_en: %x, fid: %x, stag: %x\n", index, active, vid, portMap, tagPortMap, ivl_en, fid, stag);
+	printf("index: %x, active: %x, vid: %x, portMap: %x,",
+		index, active, vid, portMap);
+	printf("tagPortMap: %x, ivl_en: %x, fid: %x, stag: %x\n",
+		tagPortMap, ivl_en, fid, stag);
 
-	vlan_entry.valid = ! !active;
+	vlan_entry.valid = !!active;
 	vlan_entry.vlan_entry_format.port_mem = portMap;
 	/* Total 6 ports */
 	for (i = 0; i < SWITCH_MAX_PORT; i++) {
 		if (tagPortMap & (1 << i))
 			vlan_entry.vlan_entry_format.eg_ctrl |= 0x2 << (i * 2);
 	}
-	vlan_entry.vlan_entry_format.ivl = ! !ivl_en;
+	vlan_entry.vlan_entry_format.ivl = !!ivl_en;
 	vlan_entry.vlan_entry_format.fid = fid;
 	vlan_entry.vlan_entry_format.stag = stag;
 
@@ -1234,9 +1198,8 @@
 		return;
 	}
 
-	for (port = 0; port < 6; port++) {
+	for (port = 0; port < 6; port++)
 		air_l2_setAgeEnable(0, port, aging_en);
-	}
 
 	air_l2_setMacAddrAgeOut(0, time);
 }
@@ -1264,9 +1227,8 @@
 		session.dst_port = mirror_port;
 		session.flags |= AIR_MIR_SESSION_FLAGS_ENABLE;
 		air_mir_addSession(0, 0, &session);
-	} else {
+	} else
 		air_mir_delSession(0, 0);
-	}
 
 	air_mir_setSessionAdminMode(0, 0, (int)mirror_en);
 }				/*end doMirrorEn */
@@ -1285,13 +1247,14 @@
 	vlan_mis = _strtoul(argv[7], NULL, 10);
 	igmp_mir = _strtoul(argv[8], NULL, 10);
 
-	printf
-	    ("port:%d, port_tx_mir:%d, port_rx_mir:%d, acl_mir:%d, vlan_mis:%d, igmp_mir:%d\n",
-	     port, port_tx_mir, port_rx_mir, acl_mir, vlan_mis, igmp_mir);
+	printf("port:%d, port_tx_mir:%d, port_rx_mir:%d, ",
+		port, port_tx_mir, port_rx_mir);
+	printf("acl_mir:%d, vlan_mis:%d, igmp_mir:%d\n",
+		acl_mir, vlan_mis, igmp_mir);
 
 	/*Check the input parameters is right or not. */
-	//if((port >= vlanCap->max_port_no) || (port_tx_mir > 1) || (port_rx_mir > 1) || (acl_mir > 1) || (vlan_mis > 1)){
-	if ((port >= SWITCH_MAX_PORT) || (port_tx_mir > 1) || (port_rx_mir > 1) || (acl_mir > 1) || (vlan_mis > 1)) {	// also allow CPU port (port6)
+	if ((port >= SWITCH_MAX_PORT) || (port_tx_mir > 1) || (port_rx_mir > 1)
+		|| (acl_mir > 1) || (vlan_mis > 1)) {	// also allow CPU port (port6)
 		printf(HELP_MIRROR_PORTBASED);
 		return;
 	}
@@ -1312,11 +1275,7 @@
 
 	air_mir_setMirrorPort(0, 0, &session);
 
-	/*
-
-	   not support acl/vlan/igmp mismatch
-
-	 */
+	/* not support acl/vlan/igmp mismatch */
 }				/*end doMirrorPortBased */
 
 void an8855_doStp(int argc, char *argv[])
@@ -1346,35 +1305,40 @@
 {
 	AIR_ERROR_NO_T ret = AIR_E_OK;
 	AIR_QOS_RATE_LIMIT_CFG_T rl = { 0 };
+
 	if (on_off) {
 		ret =
 		    air_qos_setRateLimitEnable(0, port,
 					       AIR_QOS_RATE_DIR_INGRESS, TRUE);
-		if (AIR_E_OK != ret) {
+		if (ret != AIR_E_OK) {
 			printf("an8855 set ingress ratelimit eanble fail\n");
 			return;
 		}
+		ret = air_qos_getRateLimit(0, port, &rl);
+		if (ret != AIR_E_OK) {
+			printf("an8855 get port %d ratelimit fail\n",
+				port);
+			return;
+		}
 		rl.ingress_cir = bw;
 		rl.flags |= AIR_QOS_RATE_LIMIT_CFG_FLAGS_ENABLE_INGRESS;
 		ret = air_qos_setRateLimit(0, port, &rl);
-		if (AIR_E_OK != ret) {
+		if (ret != AIR_E_OK) {
 			printf("an8855 set ingress ratelimit value %d fail\n",
 			       bw);
 			return;
-		} else {
-			printf("an8855 set ingress ratelimit value %d ok\n",
-			       bw);
 		}
+		printf("an8855 set ingress ratelimit value %d ok\n", bw);
 	} else {
 		ret =
 		    air_qos_setRateLimitEnable(0, port,
 					       AIR_QOS_RATE_DIR_INGRESS, FALSE);
-		if (AIR_E_OK != ret) {
+		if (ret != AIR_E_OK) {
 			printf("an8855 set ingress ratelimit disable fail\n");
 			return;
-		} else {
-			printf("an8855 set ingress ratelimit disable ok\n");
 		}
+		printf("an8855 set ingress ratelimit disable ok\n");
+
 	}
 }
 
@@ -1382,34 +1346,41 @@
 {
 	AIR_ERROR_NO_T ret = AIR_E_OK;
 	AIR_QOS_RATE_LIMIT_CFG_T rl = { 0 };
+
 	if (on_off) {
 		ret =
 		    air_qos_setRateLimitEnable(0, port, AIR_QOS_RATE_DIR_EGRESS,
 					       TRUE);
-		if (AIR_E_OK != ret) {
+		if (ret != AIR_E_OK) {
 			printf("an8855 set egress ratelimit eanble fail\n");
 			return;
 		}
+		ret = air_qos_getRateLimit(0, port, &rl);
+		if (ret != AIR_E_OK) {
+			printf("an8855 get port %d ratelimit fail\n",
+				port);
+			return;
+		}
 		rl.egress_cir = bw;
 		rl.flags |= AIR_QOS_RATE_LIMIT_CFG_FLAGS_ENABLE_EGRESS;
 		ret = air_qos_setRateLimit(0, port, &rl);
-		if (AIR_E_OK != ret) {
+		if (ret != AIR_E_OK) {
 			printf("an8855 set egress ratelimit value %d fail\n",
 			       bw);
 			return;
-		} else {
-			printf("an8855 set egress ratelimit value %d ok\n", bw);
 		}
+		printf("an8855 set egress ratelimit value %d ok\n", bw);
+
 	} else {
 		ret =
 		    air_qos_setRateLimitEnable(0, port, AIR_QOS_RATE_DIR_EGRESS,
 					       FALSE);
-		if (AIR_E_OK != ret) {
+		if (ret != AIR_E_OK) {
 			printf("an8855 set egress ratelimit disable fail\n");
 			return;
-		} else {
-			printf("an8855 set egress ratelimit disable ok\n");
 		}
+		printf("an8855 set egress ratelimit disable ok\n");
+
 	}
 }
 
@@ -1422,9 +1393,9 @@
 		bw = _strtoul(argv[4], NULL, 0);
 		on_off = 1;
 	} else if (argv[2][1] == 'f') {
-		if (argc != 4) {
+		if (argc != 4)
 			return;
-		}
+
 		on_off = 0;
 	}
 
@@ -1441,9 +1412,9 @@
 		bw = _strtoul(argv[4], NULL, 0);
 		on_off = 1;
 	} else if (argv[2][1] == 'f') {
-		if (argc != 4) {
+		if (argc != 4)
 			return;
-		}
+
 		on_off = 0;
 	}
 
@@ -1554,35 +1525,35 @@
 	queue[6] = (value & 0x3F0000) >> 16;
 	queue[7] = (value & 0x3F000000) >> 24;
 
-	printf("<===Free Page=======Current============Minimal=========> \n ");
-	printf("	                                                 \n ");
-	printf(" page counter      %u                %u               \n ",
+	printf("<===Free Page=======Current============Minimal=========>\n");
+	printf("\n");
+	printf(" page counter      %u                %u\n",
 	       free_page, free_page_min);
-	printf("                                                        \n ");
-	printf("========================================================= \n ");
-	printf("<===Type=======High threshold======Low threshold=========\n ");
-	printf("                                                        \n ");
-	printf("  system:         %u                 %u               \n",
+	printf("\n");
+	printf("=========================================================\n");
+	printf("<===Type=======High threshold======Low threshold=========\n");
+	printf("\n");
+	printf("  system:         %u                 %u\n",
 	       fc_free_blk_hithd * 2, fc_free_blk_lothd * 2);
-	printf("    port:         %u                 %u               \n",
+	printf("    port:         %u                 %u\n",
 	       fc_port_blk_hi_thd * 2, fc_port_blk_thd * 2);
-	printf(" queue 0:         %u                 NA                \n",
+	printf(" queue 0:         %u                 NA\n",
 	       queue[0]);
-	printf(" queue 1:         %u                 NA                \n",
+	printf(" queue 1:         %u                 NA\n",
 	       queue[1]);
-	printf(" queue 2:         %u                 NA                 \n",
+	printf(" queue 2:         %u                 NA\n",
 	       queue[2]);
-	printf(" queue 3:         %u                 NA                \n",
+	printf(" queue 3:         %u                 NA\n",
 	       queue[3]);
-	printf(" queue 4:         %u                 NA                \n",
+	printf(" queue 4:         %u                 NA\n",
 	       queue[4]);
-	printf(" queue 5:         %u                 NA                \n",
+	printf(" queue 5:         %u                 NA\n",
 	       queue[5]);
-	printf(" queue 6:         %u                 NA                \n",
+	printf(" queue 6:         %u                 NA\n",
 	       queue[6]);
-	printf(" queue 7:         %u                 NA                \n",
+	printf(" queue 7:         %u                 NA\n",
 	       queue[7]);
-	printf("=========================================================\n ");
+	printf("=========================================================\n");
 }
 
 void an8855_eee_enable(int argc, char *argv[])
@@ -1618,8 +1589,8 @@
 			port_map = 0;
 			for (p = 0; p < MAX_PHY_PORT; p++) {
 				if (argv[3][p] != '0' && argv[3][p] != '1') {
-					printf
-					    ("portmap format error, should be combination of 0 or 1\n");
+					printf("portmap format error, ");
+					printf("should be combination of 0 or 1\n");
 					goto error;
 				}
 				port_map |= ((argv[3][p] - '0') << p);
@@ -1636,11 +1607,11 @@
 	for (port_num = 0; port_num < MAX_PHY_PORT; port_num++) {
 		if (port_map & (1 << port_num)) {
 			air_port_getPsMode(0, port_num, &mode);
-			if (enable) {
+			if (enable)
 				mode |= AIR_PORT_PS_EEE;
-			} else {
+			else
 				mode &= ~AIR_PORT_PS_EEE;
-			}
+
 			air_port_setPsMode(0, port_num, mode);
 		}
 	}
@@ -1648,7 +1619,6 @@
 
 error:
 	printf(HELP_EEE_EN);
-	return;
 }
 
 void an8855_eee_dump(int argc, char *argv[])
@@ -1694,107 +1664,106 @@
 	AIR_MIB_CNT_RX_T rx_mib[7];
 	AIR_MIB_CNT_TX_T tx_mib[7];
 
-	printf("===================== %8s %8s %8s %8s %8s %8s %8s\n",
+	printf("======================== %8s %8s %8s %8s %8s %8s %8s\n",
 	       "Port0", "Port1", "Port2", "Port3", "Port4", "Port5", "Port6");
 
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		air_mib_get(0, port, &rx_mib[port], &tx_mib[port]);
-	}
 
 	printf("Tx Drop Packet      :");
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		printf("%8u ", tx_mib[port].TDPC);
-	}
+
 	printf("\n");
 	printf("Tx CRC Error        :");
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		printf("%8u ", tx_mib[port].TCRC);
-	}
+
 	printf("\n");
 	printf("Tx Unicast Packet   :");
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		printf("%8u ", tx_mib[port].TUPC);
-	}
+
 	printf("\n");
 	printf("Tx Multicast Packet :");
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		printf("%8u ", tx_mib[port].TMPC);
-	}
+
 	printf("\n");
 	printf("Tx Broadcast Packet :");
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		printf("%8u ", tx_mib[port].TBPC);
-	}
+
 	printf("\n");
 	printf("Tx Collision Event  :");
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		printf("%8u ", tx_mib[port].TCEC);
-	}
+
 	printf("\n");
 	printf("Tx Pause Packet     :");
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		printf("%8u ", tx_mib[port].TPPC);
-	}
+
 	printf("\n");
 	printf("Rx Drop Packet      :");
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		printf("%8u ", rx_mib[port].RDPC);
-	}
+
 	printf("\n");
 	printf("Rx Filtering Packet :");
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		printf("%8u ", rx_mib[port].RFPC);
-	}
+
 	printf("\n");
 	printf("Rx Unicast Packet   :");
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		printf("%8u ", rx_mib[port].RUPC);
-	}
+
 	printf("\n");
 	printf("Rx Multicast Packet :");
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		printf("%8u ", rx_mib[port].RMPC);
-	}
+
 	printf("\n");
 	printf("Rx Broadcast Packet :");
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		printf("%8u ", rx_mib[port].RBPC);
-	}
+
 	printf("\n");
 	printf("Rx Alignment Error  :");
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		printf("%8u ", rx_mib[port].RAEPC);
-	}
+
 	printf("\n");
 	printf("Rx CRC Error	    :");
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		printf("%8u ", rx_mib[port].RCEPC);
-	}
+
 	printf("\n");
 	printf("Rx Undersize Error  :");
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		printf("%8u ", rx_mib[port].RUSPC);
-	}
+
 	printf("\n");
 	printf("Rx Fragment Error   :");
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		printf("%8u ", rx_mib[port].RFEPC);
-	}
+
 	printf("\n");
 	printf("Rx Oversize Error   :");
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		printf("%8u ", rx_mib[port].ROSPC);
-	}
+
 	printf("\n");
 	printf("Rx Jabber Error     :");
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		printf("%8u ", rx_mib[port].RJEPC);
-	}
+
 	printf("\n");
 	printf("Rx Pause Packet     :");
-	for (port = 0; port < 7; port++) {
+	for (port = 0; port < 7; port++)
 		printf("%8u ", rx_mib[port].RPPC);
-	}
+
 	printf("\n");
 }
 
diff --git a/recipes-devtools/switch/files/src/switch_fun_an8855.h b/recipes-devtools/switch/files/src/switch_fun_an8855.h
index 1d67080..9be440f 100644
--- a/recipes-devtools/switch/files/src/switch_fun_an8855.h
+++ b/recipes-devtools/switch/files/src/switch_fun_an8855.h
@@ -1,6 +1,6 @@
 /*
-* switch_fun.h: switch function sets
-*/
+ * switch_fun.h: switch function sets
+ */
 #ifndef SWITCH_FUN_AN8855_H
 #define SWITCH_FUN_AN8855_H
 
@@ -16,7 +16,7 @@
 AIR_ERROR_NO_T
 an8855_phy_cl22_read(const UI32_T unit,
 		     const UI32_T port_id,
-		     const UI32_T addr_offset, UI32_T * ptr_data);
+		     const UI32_T addr_offset, UI32_T *ptr_data);
 
 AIR_ERROR_NO_T
 an8855_phy_cl22_write(const UI32_T unit,
@@ -27,7 +27,7 @@
 an8855_phy_cl45_read(const UI32_T unit,
 		     const UI32_T port_id,
 		     const UI32_T dev_type,
-		     const UI32_T addr_offset, UI32_T * ptr_data);
+		     const UI32_T addr_offset, UI32_T *ptr_data);
 
 AIR_ERROR_NO_T
 an8855_phy_cl45_write(const UI32_T unit,
@@ -40,38 +40,6 @@
 
 void an8855_not_supported(int argc, char *argv[]);
 
-#if 0
-/*acl setting*/
-void an8855_acl_mac_add(int argc, char *argv[]);
-void an8855_acl_dip_meter(int argc, char *argv[]);
-void an8855_acl_dip_trtcm(int argc, char *argv[]);
-void an8855_acl_ethertype(int argc, char *argv[]);
-void an8855_acl_ethertype(int argc, char *argv[]);
-void an8855_acl_dip_modify(int argc, char *argv[]);
-void an8855_acl_dip_pppoe(int argc, char *argv[]);
-void an8855_acl_dip_add(int argc, char *argv[]);
-void an8855_acl_l4_add(int argc, char *argv[]);
-void an8855_acl_sp_add(int argc, char *argv[]);
-
-void an8855_acl_port_enable(int argc, char *argv[]);
-void an8855_acl_table_add(int argc, char *argv[]);
-void an8855_acl_mask_table_add(int argc, char *argv[]);
-void an8855_acl_rule_table_add(int argc, char *argv[]);
-void an8855_acl_rate_table_add(int argc, char *argv[]);
-
-/*dip table*/
-void an8855_dip_dump(void);
-void an8855_dip_add(int argc, char *argv[]);
-void an8855_dip_del(int argc, char *argv[]);
-void an8855_dip_clear(void);
-
-/*sip table*/
-void an8855_sip_dump(void);
-void an8855_sip_add(int argc, char *argv[]);
-void an8855_sip_del(int argc, char *argv[]);
-void an8855_sip_clear(void);
-#endif
-
 /*stp*/
 void an8855_doStp(int argc, char *argv[]);
 
diff --git a/recipes-devtools/switch/files/src/switch_ioctl.c b/recipes-devtools/switch/files/src/switch_ioctl.c
index f6b97d4..dd9e621 100644
--- a/recipes-devtools/switch/files/src/switch_ioctl.c
+++ b/recipes-devtools/switch/files/src/switch_ioctl.c
@@ -164,7 +164,7 @@
 			break;
 		}
 	}
-	//printf(" PHY Indirect Access Control(0x701c) register read value =0x%x  \n", reg_value);
+	//printf(" PHY Indirect Access Control(0x701c) register read value =0x%x\n", reg_value);
 	*value = reg_value;
 
 	return 0;
@@ -205,7 +205,7 @@
 		}
 	}
 
-	//printf(" PHY Indirect Access Control(0x701c) register write value =0x%x  \n", reg_value);
+	//printf(" PHY Indirect Access Control(0x701c) register write value =0x%x\n", reg_value);
 
 	return 0;
 }
@@ -332,7 +332,7 @@
 		} else {
 			printf("MDIO cl45 set dev opeartion timeout\n");
 			reg_value = 0;
-			ret = -1; 
+			ret = -1;
 			goto out;
 		}
 	}
@@ -351,12 +351,12 @@
 		} else {
 			printf("MDIO cl45 read reg opeartion timeout\n");
 			reg_value = 0;
-			ret = -1; 
+			ret = -1;
 			break;
 		}
 	}
 out:
-	//printf(" PHY Indirect Access Control(0x701c) register read value =0x%x  \n", reg_value);
+	//printf(" PHY Indirect Access Control(0x701c) register read value =0x%x\n", reg_value);
 	*value = reg_value;
 
 	return ret;
@@ -384,7 +384,7 @@
 			loop_cnt++;
 		else {
 			printf("MDIO cl45 set dev opeartion timeout\n");
-			ret = -1; 
+			ret = -1;
 			goto out;
 		}
 	}
@@ -401,12 +401,12 @@
 			loop_cnt++;
 		else {
 			printf("MDIO cl45 write reg opeartion timeout\n");
-			ret = -1; 
+			ret = -1;
 			break;
 		}
 	}
 out:
-	//printf(" PHY Indirect Access Control(0x701c) register write value =0x%x  \n", reg_value);
+	//printf(" PHY Indirect Access Control(0x701c) register write value =0x%x\n", reg_value);
 	return ret;
 }
 
diff --git a/recipes-devtools/switch/files/src/switch_ioctl.h b/recipes-devtools/switch/files/src/switch_ioctl.h
index dffe9c7..3ee43d7 100644
--- a/recipes-devtools/switch/files/src/switch_ioctl.h
+++ b/recipes-devtools/switch/files/src/switch_ioctl.h
@@ -8,48 +8,43 @@
 #define ETH_DEVNAME "eth0"
 #define BR_DEVNAME "br-lan"
 
-#define RAETH_MII_READ                  0x89F3
-#define RAETH_MII_WRITE                 0x89F4
-#define RAETH_ESW_PHY_DUMP              0x89F7
+#define RAETH_MII_READ			0x89F3
+#define RAETH_MII_WRITE			0x89F4
+#define RAETH_ESW_PHY_DUMP		0x89F7
 
 struct esw_reg {
-        unsigned int off;
-        unsigned int val;
+	unsigned int off;
+	unsigned int val;
 };
 
 struct ra_mii_ioctl_data {
-        __u16 phy_id;
-        __u16 reg_num;
-        __u32 val_in;
-        __u32 val_out;
-/*
-        __u32 port_num;
-        __u32 dev_addr;
-        __u32 reg_addr;
-*/
+	__u16 phy_id;
+	__u16 reg_num;
+	__u32 val_in;
+	__u32 val_out;
 };
 
 struct ra_switch_ioctl_data {
-        unsigned int cmd;
-        unsigned int on_off;
-        unsigned int port;
-        unsigned int bw;
-        unsigned int vid;
-        unsigned int fid;
-        unsigned int port_map;
-        unsigned int rx_port_map;
-        unsigned int tx_port_map;
-        unsigned int igmp_query_interval;
-        unsigned int reg_addr;
-        unsigned int reg_val;
-        unsigned int mode;
-        unsigned int qos_queue_num;
-        unsigned int qos_type;
-        unsigned int qos_pri;
-        unsigned int qos_dscp;
-        unsigned int qos_table_idx;
-        unsigned int qos_weight;
-        unsigned char mac[6];
+	unsigned int cmd;
+	unsigned int on_off;
+	unsigned int port;
+	unsigned int bw;
+	unsigned int vid;
+	unsigned int fid;
+	unsigned int port_map;
+	unsigned int rx_port_map;
+	unsigned int tx_port_map;
+	unsigned int igmp_query_interval;
+	unsigned int reg_addr;
+	unsigned int reg_val;
+	unsigned int mode;
+	unsigned int qos_queue_num;
+	unsigned int qos_type;
+	unsigned int qos_pri;
+	unsigned int qos_dscp;
+	unsigned int qos_table_idx;
+	unsigned int qos_weight;
+	unsigned char mac[6];
 };
 
 extern int chip_name;
@@ -60,11 +55,11 @@
 int reg_write_ioctl(unsigned int offset, unsigned int value);
 int phy_dump_ioctl(unsigned int phy_addr);
 int mii_mgr_cl22_read_ioctl(unsigned int port_num, unsigned int reg,
-			    unsigned int *value);
+			unsigned int *value);
 int mii_mgr_cl22_write_ioctl(unsigned int port_num, unsigned int reg,
-			     unsigned int value);
+			unsigned int value);
 int mii_mgr_cl45_read_ioctl(unsigned int port_num, unsigned int dev,
-			    unsigned int reg, unsigned int *value);
+			unsigned int reg, unsigned int *value);
 int mii_mgr_cl45_write_ioctl(unsigned int port_num, unsigned int dev,
-			     unsigned int reg, unsigned int value);
+			unsigned int reg, unsigned int value);
 #endif
diff --git a/recipes-devtools/switch/files/src/switch_netlink.c b/recipes-devtools/switch/files/src/switch_netlink.c
index 397ebe5..f6f5fb0 100644
--- a/recipes-devtools/switch/files/src/switch_netlink.c
+++ b/recipes-devtools/switch/files/src/switch_netlink.c
@@ -44,7 +44,7 @@
 			    nla_get_string(attrs[MT753X_ATTR_TYPE_MESG]);
 			printf("register switch dev:\n%s", val->dev_info);
 		} else {
-			fprintf(stderr, "ERROR:No switch dev now\n");
+			printf("ERROR:No switch dev now\n");
 			goto done;
 		}
 	} else
@@ -132,7 +132,7 @@
 	/* Allocate an netllink message buffer */
 	msg = nlmsg_alloc();
 	if (!msg) {
-		fprintf(stderr, "Failed to allocate netlink message\n");
+		printf("Failed to allocate netlink message\n");
 		exit(1);
 	}
 	if (!construct) {
@@ -148,7 +148,7 @@
 	if (construct) {
 		err = construct(msg, arg);
 		if (err < 0) {
-			fprintf(stderr, "attributes error\n");
+			printf("attributes error\n");
 			goto nal_put_failure;
 		}
 	}
@@ -156,14 +156,14 @@
 	/* Allocate an new callback handler */
 	callback = nl_cb_alloc(NL_CB_CUSTOM);
 	if (!callback) {
-		fprintf(stderr, "Failed to allocate callback handler\n");
+		printf("Failed to allocate callback handler\n");
 		exit(1);
 	}
 
 	/* Send netlink message */
 	err = nl_send_auto_complete(user_sock, msg);
 	if (err < 0) {
-		fprintf(stderr, "nl_send_auto_complete failied:%d\n", err);
+		printf("nl_send_auto_complete failied:%d\n", err);
 		goto out;
 	}
 	finished = 0;
@@ -218,18 +218,18 @@
 	/* Allocate an new netlink socket */
 	user_sock = nl_socket_alloc();
 	if (!user_sock) {
-		fprintf(stderr, "Failed to create user socket\n");
+		printf("Failed to create user socket\n");
 		goto err;
 	}
 	/* Connetct the genl controller */
 	if (genl_connect(user_sock)) {
-		fprintf(stderr, "Failed to connetct to generic netlink\n");
+		printf("Failed to connetct to generic netlink\n");
 		goto err;
 	}
 	/* Allocate an new nl_cache */
 	ret = genl_ctrl_alloc_cache(user_sock, &cache);
 	if (ret < 0) {
-		fprintf(stderr, "Failed to allocate netlink cache\n");
+		printf("Failed to allocate netlink cache\n");
 		goto err;
 	}
 
@@ -254,7 +254,7 @@
 
 	err = mt753x_request_callback(cmd, list_swdevs, NULL, arg);
 	if (err < 0)
-		fprintf(stderr, "mt753x list dev error\n");
+		printf("mt753x list dev error\n");
 }
 
 static int mt753x_request(struct mt753x_attr *arg, int cmd)
@@ -263,7 +263,7 @@
 
 	err = mt753x_request_callback(cmd, spilt_attrs, construct_attrs, arg);
 	if (err < 0) {
-		fprintf(stderr, "mt753x deal request error\n");
+		printf("mt753x deal request error\n");
 		return err;
 	}
 	return 0;
@@ -279,7 +279,7 @@
 	attr->port_num = port_num;
 	attr->phy_dev = phy_dev;
 	attr->reg = offset;
-	attr->value = -1;
+	attr->value = 0;
 	attr->type = MT753X_ATTR_TYPE_REG;
 
 	switch (op) {
diff --git a/recipes-devtools/switch/files/src/switch_netlink.h b/recipes-devtools/switch/files/src/switch_netlink.h
index cb812d5..94d8389 100644
--- a/recipes-devtools/switch/files/src/switch_netlink.h
+++ b/recipes-devtools/switch/files/src/switch_netlink.h
@@ -1,6 +1,6 @@
 /*
  * switch_netlink.h: switch(netlink) set API
- * 
+ *
  * Author: Sirui Zhao <Sirui.Zhao@mediatek.com>
  */
 #ifndef MT753X_NETLINK_H
@@ -54,20 +54,16 @@
 int mt753x_netlink_init(const char *name);
 void mt753x_netlink_free(void);
 void mt753x_list_swdev(struct mt753x_attr *arg, int cmd);
-int reg_read_netlink(struct mt753x_attr *arg, unsigned int offset,
-		     unsigned int *value);
-int reg_write_netlink(struct mt753x_attr *arg, unsigned int offset,
-		      unsigned int value);
+int reg_read_netlink(struct mt753x_attr *arg, unsigned int offset, unsigned int *value);
+int reg_write_netlink(struct mt753x_attr *arg, unsigned int offset, unsigned int value);
 int phy_cl22_read_netlink(struct mt753x_attr *arg, unsigned int port_num,
-			  unsigned int phy_addr, unsigned int *value);
+			unsigned int phy_addr, unsigned int *value);
 int phy_cl22_write_netlink(struct mt753x_attr *arg, unsigned int port_num,
-			   unsigned int phy_addr, unsigned int value);
+			unsigned int phy_addr, unsigned int value);
 int phy_cl45_read_netlink(struct mt753x_attr *arg, unsigned int port_num,
-			  unsigned int phy_dev, unsigned int phy_addr,
-			  unsigned int *value);
+			unsigned int phy_dev, unsigned int phy_addr, unsigned int *value);
 int phy_cl45_write_netlink(struct mt753x_attr *arg, unsigned int port_num,
-			   unsigned int phy_dev, unsigned int phy_addr,
-			   unsigned int value);
+			unsigned int phy_dev, unsigned int phy_addr, unsigned int value);
 int phy_dump_netlink(struct mt753x_attr *arg, int phy_addr);
 
 #endif
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/generic/pending-5.4/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/recipes-kernel/linux/linux-mediatek-5.4/generic/pending-5.4/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
index bec6787..987a4f6 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/generic/pending-5.4/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/generic/pending-5.4/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
@@ -115,35 +115,3 @@
  	call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
  	add_device_randomness(dev->dev_addr, dev->addr_len);
  	return 0;
---- a/net/ethernet/eth.c
-+++ b/net/ethernet/eth.c
-@@ -143,6 +143,18 @@ u32 eth_get_headlen(const struct net_dev
- }
- EXPORT_SYMBOL(eth_get_headlen);
- 
-+static inline bool
-+eth_check_local_mask(const void *addr1, const void *addr2, const void *mask)
-+{
-+	const u16 *a1 = addr1;
-+	const u16 *a2 = addr2;
-+	const u16 *m = mask;
-+
-+	return (((a1[0] ^ a2[0]) & ~m[0]) |
-+		((a1[1] ^ a2[1]) & ~m[1]) |
-+		((a1[2] ^ a2[2]) & ~m[2]));
-+}
-+
- /**
-  * eth_type_trans - determine the packet's protocol ID.
-  * @skb: received socket data
-@@ -174,6 +186,10 @@ __be16 eth_type_trans(struct sk_buff *sk
- 		} else {
- 			skb->pkt_type = PACKET_OTHERHOST;
- 		}
-+
-+		if (eth_check_local_mask(eth->h_dest, dev->dev_addr,
-+					 dev->local_addr_mask))
-+			skb->gro_skip = 1;
- 	}
- 
- 	/*
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855.c
index b0ba4be..78cfbdc 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855.c
@@ -20,12 +20,72 @@
 #include <linux/gpio/consumer.h>
 #include <net/dsa.h>
 #include <linux/of_address.h>
+#include <linux/seq_file.h>
+#include <linux/proc_fs.h>
+#include <linux/version.h>
 
 #include "an8855.h"
 #include "an8855_nl.h"
+#include "an8855_phy.h"
 
 /* AN8855 driver version */
-#define ARHT_AN8855_DSA_DRIVER_VER	"1.0.1-L5.4"
+#define ARHT_AN8855_DSA_DRIVER_VER	"1.0.2"
+
+#define ARHT_CHIP_NAME                  "an8855"
+#define ARHT_PROC_DIR                   "air_sw"
+#define ARHT_PROC_NODE_DEVICE           "device"
+
+struct proc_dir_entry *proc_an8855_dsa_dir;
+
+/* T830 AN8855 Reference Board */
+static const struct an8855_led_cfg led_cfg[] = {
+/*************************************************************************
+ * Enable, LED idx, LED Polarity, LED ON event,  LED Blink event  LED Freq
+ *************************************************************************
+ */
+	/* GPIO0 */
+	{1, P4_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO1 */
+	{1, P4_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO2 */
+	{1, P0_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO3 */
+	{1, P0_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO4 */
+	{1, P1_LED0, LED_LOW,  LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO5 */
+	{1, P1_LED1, LED_LOW,  LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO6 */
+	{0, PHY_LED_MAX, LED_LOW,  LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO7 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO8 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO9 */
+	{1, P2_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO10 */
+	{1, P2_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO11 */
+	{1, P3_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO12 */
+	{1, P3_LED1, LED_HIGH,  LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO13 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO14 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO15 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO16 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO17 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO18 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO19 */
+	{0, PHY_LED_MAX, LED_LOW,  LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO20 */
+	{0, PHY_LED_MAX, LED_LOW,  LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+};
 
 /* String, offset, and register size in bytes if different from 4 bytes */
 static const struct an8855_mib_desc an8855_mib[] = {
@@ -99,7 +159,6 @@
 	ret = bus->write(bus, priv->phy_base, 0x14, (val & 0xFFFF));
 
 	ret = bus->write(bus, priv->phy_base, 0x1f, 0);
-	ret = bus->write(bus, priv->phy_base, 0x10, 0);
 
 	if (ret < 0) {
 		dev_err(&bus->dev, "failed to write an8855 register\n");
@@ -130,7 +189,6 @@
 	hi = bus->read(bus, priv->phy_base, 0x17);
 
 	ret = bus->write(bus, priv->phy_base, 0x1f, 0);
-	ret = bus->write(bus, priv->phy_base, 0x10, 0);
 	if (ret < 0) {
 		dev_err(&bus->dev, "failed to read an8855 register\n");
 		return ret;
@@ -327,16 +385,22 @@
 static int
 an8855_cl45_read(struct an8855_priv *priv, int port, int devad, int regnum)
 {
-	regnum = MII_ADDR_C45 | (devad << 16) | regnum;
-	return mdiobus_read_nested(priv->bus, port, regnum);
+	an8855_cl22_write(priv, port, 0x0d, devad);
+	an8855_cl22_write(priv, port, 0x0e, regnum);
+	an8855_cl22_write(priv, port, 0x0d, devad | (0x4000));
+	return an8855_cl22_read(priv, port, 0x0e);
 }
 
 static int
 an8855_cl45_write(struct an8855_priv *priv, int port, int devad, int regnum,
 		      u16 val)
 {
-	regnum = MII_ADDR_C45 | (devad << 16) | regnum;
-	return mdiobus_write_nested(priv->bus, port, regnum, val);
+	an8855_cl22_write(priv, port, 0x0d, devad);
+	an8855_cl22_write(priv, port, 0x0e, regnum);
+	an8855_cl22_write(priv, port, 0x0d, devad | (0x4000));
+	an8855_cl22_write(priv, port, 0x0e, val);
+
+	return 0;
 }
 
 int
@@ -457,7 +521,6 @@
 	priv->ports[port].pm |= PORTMATRIX_MATRIX(BIT(AN8855_CPU_PORT));
 	priv->ports[port].enable = true;
 	an8855_write(priv, AN8855_PORTMATRIX_P(port), priv->ports[port].pm);
-	an8855_clear(priv, AN8855_PMCR_P(port), PMCR_LINK_SETTINGS_MASK);
 
 	mutex_unlock(&priv->reg_mutex);
 
@@ -479,7 +542,6 @@
 	 */
 	priv->ports[port].enable = false;
 	an8855_write(priv, AN8855_PORTMATRIX_P(port), PORTMATRIX_CLR);
-	an8855_clear(priv, AN8855_PMCR_P(port), PMCR_LINK_SETTINGS_MASK);
 
 	mutex_unlock(&priv->reg_mutex);
 }
@@ -1085,6 +1147,216 @@
 		dev_dbg(ds->dev, "Add unused port%d to reserved VLAN%d group\n",
 			i, AN8855_RESERVED_VLAN);
 	}
+
+	return 0;
+}
+
+static int an8855_led_set_usr_def(struct dsa_switch *ds, u8 entity,
+		int polar, u16 on_evt, u16 blk_evt, u8 led_freq)
+{
+	struct an8855_priv *priv = ds->priv;
+	u32 cl45_data = 0;
+
+	if (polar == LED_HIGH)
+		on_evt |= LED_ON_POL;
+	else
+		on_evt &= ~LED_ON_POL;
+
+	/* LED on event */
+	an8855_phy_cl45_write(priv, (entity / 4), PHY_DEV1E,
+		PHY_SINGLE_LED_ON_CTRL(entity % 4), on_evt | LED_ON_EN);
+
+	/* LED blink event */
+	an8855_phy_cl45_write(priv, (entity / 4), PHY_DEV1E,
+		PHY_SINGLE_LED_BLK_CTRL(entity % 4), blk_evt);
+
+	/* LED freq */
+	switch (led_freq) {
+	case AIR_LED_BLK_DUR_32M:
+		cl45_data = 0x30e;
+		break;
+	case AIR_LED_BLK_DUR_64M:
+		cl45_data = 0x61a;
+		break;
+	case AIR_LED_BLK_DUR_128M:
+		cl45_data = 0xc35;
+		break;
+	case AIR_LED_BLK_DUR_256M:
+		cl45_data = 0x186a;
+		break;
+	case AIR_LED_BLK_DUR_512M:
+		cl45_data = 0x30d4;
+		break;
+	case AIR_LED_BLK_DUR_1024M:
+		cl45_data = 0x61a8;
+		break;
+	default:
+		break;
+	}
+	an8855_phy_cl45_write(priv, (entity / 4), PHY_DEV1E,
+		PHY_SINGLE_LED_BLK_DUR(entity % 4), cl45_data);
+
+	an8855_phy_cl45_write(priv, (entity / 4), PHY_DEV1E,
+		PHY_SINGLE_LED_ON_DUR(entity % 4), (cl45_data >> 1));
+
+	/* Disable DATA & BAD_SSD for port LED blink behavior */
+	cl45_data = an8855_phy_cl45_read(priv, (entity / 4), PHY_DEV1E,
+		PHY_PMA_CTRL);
+	cl45_data &= ~BIT(0);
+	cl45_data &= ~BIT(15);
+	an8855_phy_cl45_write(priv, (entity / 4), PHY_DEV1E,
+		PHY_PMA_CTRL, cl45_data);
+
+	return 0;
+}
+
+static int an8855_led_set_mode(struct dsa_switch *ds, u8 mode)
+{
+	struct an8855_priv *priv = ds->priv;
+	u16 cl45_data;
+
+	cl45_data = an8855_phy_cl45_read(priv, 0, PHY_DEV1F, PHY_LED_BCR);
+	switch (mode) {
+	case AN8855_LED_MODE_DISABLE:
+		cl45_data &= ~LED_BCR_EXT_CTRL;
+		cl45_data &= ~LED_BCR_MODE_MASK;
+		cl45_data |= LED_BCR_MODE_DISABLE;
+		break;
+	case AN8855_LED_MODE_USER_DEFINE:
+		cl45_data |= LED_BCR_EXT_CTRL;
+		cl45_data |= LED_BCR_CLK_EN;
+		break;
+	default:
+		dev_err(priv->dev, "LED mode%d is not supported!\n", mode);
+		return -EINVAL;
+	}
+	an8855_phy_cl45_write(priv, 0, PHY_DEV1F, PHY_LED_BCR, cl45_data);
+
+	return 0;
+}
+
+static int an8855_led_set_state(struct dsa_switch *ds, u8 entity, u8 state)
+{
+	struct an8855_priv *priv = ds->priv;
+	u16 cl45_data = 0;
+
+	/* Change to per port contorl */
+	cl45_data = an8855_phy_cl45_read(priv, (entity / 4), PHY_DEV1E,
+		PHY_LED_CTRL_SELECT);
+
+	if (state == 1)
+		cl45_data |= (1 << (entity % 4));
+	else
+		cl45_data &= ~(1 << (entity % 4));
+
+	an8855_phy_cl45_write(priv, (entity / 4), PHY_DEV1E,
+		PHY_LED_CTRL_SELECT, cl45_data);
+
+	/* LED enable setting */
+	cl45_data = an8855_phy_cl45_read(priv, (entity / 4),
+		PHY_DEV1E, PHY_SINGLE_LED_ON_CTRL(entity % 4));
+
+	if (state == 1)
+		cl45_data |= LED_ON_EN;
+	else
+		cl45_data &= ~LED_ON_EN;
+
+	an8855_phy_cl45_write(priv, (entity / 4), PHY_DEV1E,
+		PHY_SINGLE_LED_ON_CTRL(entity % 4), cl45_data);
+
+	return 0;
+}
+
+static int an8855_led_init(struct dsa_switch *ds)
+{
+	struct an8855_priv *priv = ds->priv;
+	u32 val, led_count = ARRAY_SIZE(led_cfg);
+	int ret = 0, id;
+	u32 tmp_val = 0;
+	u32 tmp_id = 0;
+
+	ret = an8855_led_set_mode(ds, AN8855_LED_MODE_USER_DEFINE);
+	if (ret != 0) {
+		dev_err(priv->dev, "led_set_mode fail(ret:%d)!\n", ret);
+		return ret;
+	}
+
+	for (id = 0; id < led_count; id++) {
+		ret = an8855_led_set_state(ds,
+			led_cfg[id].phy_led_idx, led_cfg[id].en);
+		if (ret != 0) {
+			dev_err(priv->dev, "led_set_state fail(ret:%d)!\n", ret);
+			return ret;
+		}
+		if (led_cfg[id].en == 1) {
+			ret = an8855_led_set_usr_def(ds,
+				led_cfg[id].phy_led_idx,
+				led_cfg[id].pol, led_cfg[id].on_cfg,
+				led_cfg[id].blk_cfg,
+				led_cfg[id].led_freq);
+			if (ret != 0) {
+				dev_err(priv->dev, "led_set_usr_def fail!\n");
+				return ret;
+			}
+		}
+	}
+
+	/* Setting for System LED & Loop LED */
+	an8855_write(priv, RG_GPIO_OE, 0x0);
+	an8855_write(priv, RG_GPIO_CTRL, 0x0);
+	val = 0;
+	an8855_write(priv, RG_GPIO_L_INV, val);
+
+	val = 0x1001;
+	an8855_write(priv, RG_GPIO_CTRL, val);
+	val = an8855_read(priv, RG_GPIO_DATA);
+	val |= BITS(1, 3);
+	val &= ~(BIT(0));
+	val &= ~(BIT(6));
+
+	an8855_write(priv, RG_GPIO_DATA, val);
+	val = an8855_read(priv, RG_GPIO_OE);
+	val |= 0x41;
+	an8855_write(priv, RG_GPIO_OE, val);
+
+	/* Mapping between GPIO & LED */
+	val = 0;
+	for (id = 0; id < led_count; id++) {
+		/* Skip GPIO6, due to GPIO6 does not support PORT LED */
+		if (id == 6)
+			continue;
+
+		if (led_cfg[id].en == 1) {
+			if (id < 7)
+				val |= led_cfg[id].phy_led_idx << ((id % 4) * 8);
+			else
+				val |= led_cfg[id].phy_led_idx << (((id - 1) % 4) * 8);
+		}
+
+		if (id < 7)
+			tmp_id = id;
+		else
+			tmp_id = id - 1;
+
+		if ((tmp_id % 4) == 0x3) {
+			an8855_write(priv, RG_GPIO_LED_SEL(tmp_id / 4), val);
+			tmp_val = an8855_read(priv, RG_GPIO_LED_SEL(tmp_id / 4));
+			val = 0;
+		}
+	}
+
+	/* Turn on LAN LED mode */
+	val = 0;
+	for (id = 0; id < led_count; id++) {
+		if (led_cfg[id].en == 1)
+			val |= 0x1 << id;
+	}
+	an8855_write(priv, RG_GPIO_LED_MODE, val);
+
+	/* Force clear blink pulse for per port LED */
+	an8855_phy_cl45_write(priv, 0, PHY_DEV1F, PHY_LED_BLINK_DUR_CTRL, 0x1f);
+	usleep_range(1000, 5000);
+	an8855_phy_cl45_write(priv, 0, PHY_DEV1F, PHY_LED_BLINK_DUR_CTRL, 0);
 
 	return 0;
 }
@@ -1095,7 +1367,7 @@
 	struct an8855_priv *priv = ds->priv;
 	struct an8855_dummy_poll p;
 	u32 unused_pm = 0;
-	u32 val, id;
+	u32 val, id, led_count = ARRAY_SIZE(led_cfg);
 	int ret, i;
 
 	/* Reset whole chip through gpio pin or memory-mapped registers for
@@ -1130,6 +1402,63 @@
 		priv->phy_base = priv->phy_base_new;
 	}
 
+	for (i = 0; i < AN8855_NUM_PHYS; i++) {
+		val = an8855_phy_read(ds, i, MII_BMCR);
+		val |= BMCR_ISOLATE;
+		an8855_phy_write(ds, i, MII_BMCR, val);
+	}
+
+	/* AN8855H need to setup before switch init */
+	val = an8855_read(priv, PKG_SEL);
+	if ((val & 0x7) == PAG_SEL_AN8855H) {
+		/* Invert for LED activity change */
+		val = an8855_read(priv, RG_GPIO_L_INV);
+		for (id = 0; id < led_count; id++) {
+			if ((led_cfg[id].pol == LED_HIGH) &&
+				(led_cfg[id].en == 1))
+				val |= 0x1 << id;
+		}
+		an8855_write(priv, RG_GPIO_L_INV, (val | 0x1));
+
+		/* MCU NOP CMD */
+		an8855_write(priv, RG_GDMP_RAM, 0x846);
+		an8855_write(priv, RG_GDMP_RAM + 4, 0x4a);
+
+		/* Enable MCU */
+		val = an8855_read(priv, RG_CLK_CPU_ICG);
+		an8855_write(priv, RG_CLK_CPU_ICG, val | MCU_ENABLE);
+		usleep_range(1000, 5000);
+
+		/* Disable MCU watchdog */
+		val = an8855_read(priv, RG_TIMER_CTL);
+		an8855_write(priv, RG_TIMER_CTL, (val & (~WDOG_ENABLE)));
+
+		/* Configure interrupt */
+		an8855_write(priv, RG_INTB_MODE, (0x1 << priv->intr_pin));
+
+		/* LED settings for T830 reference board */
+		ret = an8855_led_init(ds);
+		if (ret < 0) {
+			dev_err(priv->dev, "an8855_led_init fail. (ret=%d)\n", ret);
+			return ret;
+		}
+	}
+
+	/* Adjust to reduce noise */
+	for (i = 0; i < AN8855_NUM_PHYS; i++) {
+		an8855_phy_cl45_write(priv, i, PHY_DEV1E,
+			PHY_TX_PAIR_DLY_SEL_GBE, 0x4040);
+
+		an8855_phy_cl45_write(priv, i, PHY_DEV1E,
+			PHY_RXADC_CTRL, 0x1010);
+
+		an8855_phy_cl45_write(priv, i, PHY_DEV1E,
+			PHY_RXADC_REV_0, 0x100);
+
+		an8855_phy_cl45_write(priv, i, PHY_DEV1E,
+			PHY_RXADC_REV_1, 0x100);
+	}
+
 	/* Let phylink decide the interface later. */
 	priv->p5_interface = PHY_INTERFACE_MODE_NA;
 
@@ -1139,6 +1468,10 @@
 	an8855_rmw(priv, AN8855_BPC, AN8855_BPDU_PORT_FW_MASK,
 		   AN8855_BPDU_CPU_ONLY);
 
+	val = an8855_read(priv, AN8855_CKGCR);
+	val &= ~(CKG_LNKDN_GLB_STOP | CKG_LNKDN_PORT_STOP);
+	an8855_write(priv, AN8855_CKGCR, val);
+
 	/* Enable and reset MIB counters */
 	an8855_mib_reset(ds);
 
@@ -1157,8 +1490,18 @@
 			   PVC_EG_TAG(AN8855_VLAN_EG_CONSISTENT));
 	}
 
+	for (i = 0; i < AN8855_NUM_PHYS; i++) {
+		val = an8855_phy_read(ds, i, MII_BMCR);
+		val &= ~BMCR_ISOLATE;
+		an8855_phy_write(ds, i, MII_BMCR, val);
+	}
+
 	an8855_phy_setup(ds);
 
+	/* PHY restart AN*/
+	for (i = 0; i < AN8855_NUM_PHYS; i++)
+		an8855_phy_write(ds, i, MII_BMCR, 0x1240);
+
 	/* Group and enable unused ports as a standalone dumb switch. */
 	setup_unused_ports(ds, unused_pm);
 
@@ -1255,6 +1598,28 @@
 {
 	u32 val = 0;
 
+	/* TX FIR - improve TX EYE */
+	val = an8855_read(priv, INTF_CTRL_10);
+	val &= ~(0x3f << 16);
+	val |= BIT(21);
+	val &= ~(0x1f << 24);
+	val |= (0x4 << 24);
+	val |= BIT(29);
+	an8855_write(priv, INTF_CTRL_10, val);
+
+	val = an8855_read(priv, INTF_CTRL_11);
+	val &= ~(0x3f);
+	val |= BIT(6);
+	an8855_write(priv, INTF_CTRL_11, val);
+
+	/* RX CDR - improve RX Jitter Tolerance */
+	val = an8855_read(priv, RG_QP_CDR_LPF_BOT_LIM);
+	val &= ~(0x7 << 24);
+	val |= (0x5 << 24);
+	val &= ~(0x7 << 20);
+	val |= (0x5 << 20);
+	an8855_write(priv, RG_QP_CDR_LPF_BOT_LIM, val);
+
 	/* PLL */
 	val = an8855_read(priv, QP_DIG_MODE_CTRL_1);
 	val &= ~(0x3 << 2);
@@ -1384,7 +1749,7 @@
 	val &= ~(0xf << 25);
 	val |= (0x1 << 25);
 	val &= ~(0x7 << 29);
-	val |= (0x3 << 29);
+	val |= (0x6 << 29);
 	an8855_write(priv, RG_QP_CDR_LPF_SETVALUE, val);
 
 	val = an8855_read(priv, RG_QP_CDR_PR_CKREF_DIV1);
@@ -1415,12 +1780,9 @@
 	val |= (0x4 << 24);
 	an8855_write(priv, RG_QP_CDR_PR_CKREF_DIV1, val);
 
-	val = an8855_read(priv, PLL_CTRL_0);
-	val |= BIT(0);
-	an8855_write(priv, PLL_CTRL_0, val);
-
 	val = an8855_read(priv, RX_CTRL_26);
-	val &= ~BIT(23);
+	val |= BIT(23);
+	val &= ~BIT(24);
 	val |= BIT(26);
 	an8855_write(priv, RX_CTRL_26, val);
 
@@ -1448,8 +1810,8 @@
 	val = an8855_read(priv, RX_CTRL_8);
 	val &= ~(0xfff << 16);
 	val |= (0x200 << 16);
-	val &= ~(0x7fff << 14);
-	val |= (0xfff << 14);
+	val &= ~(0x7fff << 0);
+	val |= (0xfff << 0);
 	an8855_write(priv, RX_CTRL_8, val);
 
 	/* Frequency memter */
@@ -1468,6 +1830,10 @@
 	val |= (0x2710 << 0);
 	an8855_write(priv, RX_CTRL_7, val);
 
+	val = an8855_read(priv, PLL_CTRL_0);
+	val |= BIT(0);
+	an8855_write(priv, PLL_CTRL_0, val);
+
 	/* PCS Init */
 	val = an8855_read(priv, RG_HSGMII_PCS_CTROL_1);
 	val &= ~BIT(30);
@@ -1507,6 +1873,28 @@
 {
 	u32 val = 0;
 
+	/* TX FIR - improve TX EYE */
+	val = an8855_read(priv, INTF_CTRL_10);
+	val &= ~(0x3f << 16);
+	val |= BIT(21);
+	val &= ~(0x1f << 24);
+	val |= BIT(29);
+	an8855_write(priv, INTF_CTRL_10, val);
+
+	val = an8855_read(priv, INTF_CTRL_11);
+	val &= ~(0x3f);
+	val |= (0xd << 0);
+	val |= BIT(6);
+	an8855_write(priv, INTF_CTRL_11, val);
+
+	/* RX CDR - improve RX Jitter Tolerance */
+	val = an8855_read(priv, RG_QP_CDR_LPF_BOT_LIM);
+	val &= ~(0x7 << 24);
+	val |= (0x6 << 24);
+	val &= ~(0x7 << 20);
+	val |= (0x6 << 20);
+	an8855_write(priv, RG_QP_CDR_LPF_BOT_LIM, val);
+
 	/* PMA Init */
 	/* PLL */
 	val = an8855_read(priv, QP_DIG_MODE_CTRL_1);
@@ -1666,15 +2054,10 @@
 	val |= (0x4 << 24);
 	an8855_write(priv, RG_QP_CDR_PR_CKREF_DIV1, val);
 
-	val = an8855_read(priv, PLL_CTRL_0);
-	val |= BIT(0);
-	an8855_write(priv, PLL_CTRL_0, val);
-
 	val = an8855_read(priv, RX_CTRL_26);
-	val &= ~BIT(23);
-	if (mode == SGMII_MODE_AN)
+	val |= BIT(23);
+	val &= ~BIT(24);
 		val |= BIT(26);
-
 	an8855_write(priv, RX_CTRL_26, val);
 
 	val = an8855_read(priv, RX_DLY_0);
@@ -1721,6 +2104,10 @@
 	val |= (0x2710 << 0);
 	an8855_write(priv, RX_CTRL_7, val);
 
+	val = an8855_read(priv, PLL_CTRL_0);
+	val |= BIT(0);
+	an8855_write(priv, PLL_CTRL_0, val);
+
 	if (mode == SGMII_MODE_FORCE) {
 		/* PCS Init */
 		val = an8855_read(priv, QP_DIG_MODE_CTRL_0);
@@ -1805,7 +2192,6 @@
 		/* Restart AN */
 		val = an8855_read(priv, SGMII_REG_AN0);
 		val |= BIT(9);
-		val |= BIT(15);
 		an8855_write(priv, SGMII_REG_AN0, val);
 	}
 
@@ -1923,16 +2309,6 @@
 }
 
 static void
-an8855_phylink_mac_link_down(struct dsa_switch *ds, int port,
-					 unsigned int mode,
-					 phy_interface_t interface)
-{
-	struct an8855_priv *priv = ds->priv;
-
-	an8855_clear(priv, AN8855_PMCR_P(port), PMCR_LINK_SETTINGS_MASK);
-}
-
-static void
 an8855_phylink_mac_link_up(struct dsa_switch *ds, int port,
 				       unsigned int mode,
 				       phy_interface_t interface,
@@ -2194,6 +2570,51 @@
 	return priv->info->phy_write(ds, port, regnum, val);
 }
 
+static int an8855_proc_device_read(struct seq_file *seq, void *v)
+{
+	seq_printf(seq, "%s\n", ARHT_CHIP_NAME);
+
+	return 0;
+}
+
+static int an8855_proc_device_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, an8855_proc_device_read, 0);
+}
+
+#if (KERNEL_VERSION(5, 6, 0) <= LINUX_VERSION_CODE)
+static const struct proc_ops an8855_proc_device_fops = {
+	.proc_open	= an8855_proc_device_open,
+	.proc_read	= seq_read,
+	.proc_lseek	= seq_lseek,
+	.proc_release	= single_release,
+};
+#else
+static const struct file_operations an8855_proc_device_fops = {
+	.owner	= THIS_MODULE,
+	.open	= an8855_proc_device_open,
+	.read	= seq_read,
+	.llseek	= seq_lseek,
+	.release	= single_release,
+};
+#endif
+
+static int an8855_proc_device_init(void)
+{
+	if (!proc_an8855_dsa_dir)
+		proc_an8855_dsa_dir = proc_mkdir(ARHT_PROC_DIR, 0);
+
+	proc_create(ARHT_PROC_NODE_DEVICE, 0400, proc_an8855_dsa_dir,
+			&an8855_proc_device_fops);
+
+	return 0;
+}
+
+static void an8855_proc_device_exit(void)
+{
+	remove_proc_entry(ARHT_PROC_NODE_DEVICE, 0);
+}
+
 static const struct dsa_switch_ops an8855_switch_ops = {
 	.get_tag_protocol = air_get_tag_protocol,
 	.setup = an8855_sw_setup,
@@ -2217,10 +2638,6 @@
 	.port_mirror_add = an8855_port_mirror_add,
 	.port_mirror_del = an8855_port_mirror_del,
 	.phylink_validate = an8855_phylink_validate,
-	.phylink_mac_link_state = an8855_sw_phylink_mac_link_state,
-	.phylink_mac_config = an8855_phylink_mac_config,
-	.phylink_mac_link_down = an8855_phylink_mac_link_down,
-	.phylink_mac_link_up = an8855_phylink_mac_link_up,
 	.get_mac_eee = an8855_get_mac_eee,
 	.set_mac_eee = an8855_set_mac_eee,
 };
@@ -2308,6 +2725,13 @@
 	if ((ret < 0) || (priv->phy_base_new > 0x1f))
 		priv->phy_base_new = -1;
 
+	/* Assign AN8855 interrupt pin */
+	if (of_property_read_u32(dn, "airoha,intr", &priv->intr_pin))
+		priv->intr_pin = AN8855_DFL_INTR_ID;
+
+	if (of_property_read_u32(dn, "airoha,extSurge", &priv->extSurge))
+		priv->extSurge = AN8855_DFL_EXT_SURGE;
+
 	priv->bus = mdiodev->bus;
 	priv->dev = &mdiodev->dev;
 	priv->ds->priv = priv;
@@ -2324,6 +2748,7 @@
 	}
 	an8855_nl_init(&priv);
 
+	an8855_proc_device_init();
 	return 0;
 }
 
@@ -2338,6 +2763,8 @@
 	if (priv->base)
 		iounmap(priv->base);
 
+	an8855_proc_device_exit();
+
 	an8855_nl_exit();
 }
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855.h
index 531d9be..6c472c2 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855.h
@@ -10,10 +10,13 @@
 
 #define AN8855_NUM_PORTS				6
 #define AN8855_CPU_PORT					5
+#define AN8855_WORD_SIZE				4
 #define AN8855_NUM_FDB_RECORDS			2048
 #define AN8855_ALL_MEMBERS				0x3f
 #define AN8855_RESERVED_VLAN			2
 #define AN8855_GPHY_SMI_ADDR_DEFAULT	1
+#define AN8855_DFL_INTR_ID				0xd
+#define AN8855_DFL_EXT_SURGE			0x0
 
 enum an8855_id {
 	ID_AN8855 = 0,
@@ -286,6 +289,8 @@
 #define AN8855_CKGCR			(0x10213e1c)
 #define LPI_TXIDLE_THD			14
 #define LPI_TXIDLE_THD_MASK		BITS(14, 31)
+#define CKG_LNKDN_GLB_STOP	0x01
+#define CKG_LNKDN_PORT_STOP	0x02
 
 /* Register for MIB */
 #define AN8855_PORT_MIB_COUNTER(x)	(0x10214000 + (x) * 0x200)
@@ -303,18 +308,14 @@
 					 CCR_RX_OCT_CNT_BAD | \
 					 CCR_TX_OCT_CNT_GOOD | \
 					 CCR_TX_OCT_CNT_BAD | \
-					 CCR_RX_OCT_CNT_GOOD_2 | \
 					 CCR_RX_OCT_CNT_BAD_2 | \
-					 CCR_TX_OCT_CNT_GOOD_2 | \
 					 CCR_TX_OCT_CNT_BAD_2)
 #define	 CCR_MIB_ACTIVATE		(CCR_MIB_ENABLE | \
 					 CCR_RX_OCT_CNT_GOOD | \
 					 CCR_RX_OCT_CNT_BAD | \
 					 CCR_TX_OCT_CNT_GOOD | \
 					 CCR_TX_OCT_CNT_BAD | \
-					 CCR_RX_OCT_CNT_GOOD_2 | \
 					 CCR_RX_OCT_CNT_BAD_2 | \
-					 CCR_TX_OCT_CNT_GOOD_2 | \
 					 CCR_TX_OCT_CNT_BAD_2)
 
 /* AN8855 SGMII register group */
@@ -331,6 +332,22 @@
 #define AN8855_RST_CTRL			0x100050c0
 #define	 SYS_CTRL_SYS_RST		BIT(31)
 
+#define INT_MASK			0x100050F0
+#define INT_SYS_BIT			BIT(15)
+
+#define RG_CLK_CPU_ICG		0x10005034
+#define MCU_ENABLE			BIT(3)
+
+#define RG_TIMER_CTL		0x1000a100
+#define WDOG_ENABLE			BIT(25)
+
+#define CKGCR				0x10213E1C
+#define CKG_LNKDN_GLB_STOP	0x01
+#define CKG_LNKDN_PORT_STOP	0x02
+
+#define PKG_SEL				0x10000094
+#define PAG_SEL_AN8855H		0x2
+
 /* Register for hw trap status */
 #define AN8855_HWTRAP			0x1000009c
 
@@ -339,6 +356,15 @@
 
 #define SCU_BASE				0x10000000
 #define RG_RGMII_TXCK_C			(SCU_BASE + 0x1d0)
+#define RG_GPIO_LED_MODE		(SCU_BASE + 0x0054)
+#define RG_GPIO_LED_SEL(i)	(SCU_BASE + (0x0058 + ((i) * 4)))
+#define RG_INTB_MODE			(SCU_BASE + 0x0080)
+#define RG_GDMP_RAM				(SCU_BASE + 0x10000)
+
+#define RG_GPIO_L_INV			(SCU_BASE + 0x0010)
+#define RG_GPIO_CTRL			(SCU_BASE + 0xa300)
+#define RG_GPIO_DATA			(SCU_BASE + 0xa304)
+#define RG_GPIO_OE			(SCU_BASE + 0xa314)
 
 #define HSGMII_AN_CSR_BASE		0x10220000
 #define SGMII_REG_AN0			(HSGMII_AN_CSR_BASE + 0x000)
@@ -382,6 +408,8 @@
 #define SS_LCPLL_TDC_PCW_1			(QP_PMA_TOP_BASE + 0x248)
 #define INTF_CTRL_8			(QP_PMA_TOP_BASE + 0x320)
 #define INTF_CTRL_9			(QP_PMA_TOP_BASE + 0x324)
+#define INTF_CTRL_10		(QP_PMA_TOP_BASE + 0x328)
+#define INTF_CTRL_11		(QP_PMA_TOP_BASE + 0x32c)
 #define PLL_CTRL_0			(QP_PMA_TOP_BASE + 0x400)
 #define PLL_CTRL_2			(QP_PMA_TOP_BASE + 0x408)
 #define PLL_CTRL_3			(QP_PMA_TOP_BASE + 0x40c)
@@ -399,6 +427,7 @@
 #define QP_ANA_CSR_BASE				0x1022f000
 #define RG_QP_RX_DAC_EN				(QP_ANA_CSR_BASE + 0x00)
 #define RG_QP_RXAFE_RESERVE			(QP_ANA_CSR_BASE + 0x04)
+#define RG_QP_CDR_LPF_BOT_LIM		(QP_ANA_CSR_BASE + 0x08)
 #define RG_QP_CDR_LPF_MJV_LIM		(QP_ANA_CSR_BASE + 0x0c)
 #define RG_QP_CDR_LPF_SETVALUE		(QP_ANA_CSR_BASE + 0x14)
 #define RG_QP_CDR_PR_CKREF_DIV1		(QP_ANA_CSR_BASE + 0x18)
@@ -437,6 +466,74 @@
 	u8 ivl;
 };
 
+/* Definition of LED */
+#define LED_ON_EVENT	(LED_ON_EVT_LINK_1000M | \
+			LED_ON_EVT_LINK_100M | LED_ON_EVT_LINK_10M |\
+			LED_ON_EVT_LINK_HD | LED_ON_EVT_LINK_FD)
+
+#define LED_BLK_EVENT	(LED_BLK_EVT_1000M_TX_ACT | \
+			LED_BLK_EVT_1000M_RX_ACT | \
+			LED_BLK_EVT_100M_TX_ACT | \
+			LED_BLK_EVT_100M_RX_ACT | \
+			LED_BLK_EVT_10M_TX_ACT | \
+			LED_BLK_EVT_10M_RX_ACT)
+
+#define LED_FREQ	AIR_LED_BLK_DUR_64M
+
+enum phy_led_idx {
+	P0_LED0,
+	P0_LED1,
+	P0_LED2,
+	P0_LED3,
+	P1_LED0,
+	P1_LED1,
+	P1_LED2,
+	P1_LED3,
+	P2_LED0,
+	P2_LED1,
+	P2_LED2,
+	P2_LED3,
+	P3_LED0,
+	P3_LED1,
+	P3_LED2,
+	P3_LED3,
+	P4_LED0,
+	P4_LED1,
+	P4_LED2,
+	P4_LED3,
+	PHY_LED_MAX
+};
+
+/* TBD */
+enum an8855_led_blk_dur {
+	AIR_LED_BLK_DUR_32M,
+	AIR_LED_BLK_DUR_64M,
+	AIR_LED_BLK_DUR_128M,
+	AIR_LED_BLK_DUR_256M,
+	AIR_LED_BLK_DUR_512M,
+	AIR_LED_BLK_DUR_1024M,
+	AIR_LED_BLK_DUR_LAST
+};
+
+enum an8855_led_polarity {
+	LED_LOW,
+	LED_HIGH,
+};
+enum an8855_led_mode {
+	AN8855_LED_MODE_DISABLE,
+	AN8855_LED_MODE_USER_DEFINE,
+	AN8855_LED_MODE_LAST
+};
+
+struct an8855_led_cfg {
+	u16 en;
+	u8  phy_led_idx;
+	u16 pol;
+	u16 on_cfg;
+	u16 blk_cfg;
+	u8 led_freq;
+};
+
 /* struct an8855_port -	This is the main data structure for holding the state
  *			of the port.
  * @enable:	The status used for show port is enabled or not.
@@ -522,11 +619,13 @@
 	unsigned int phy_base;
 	int phy_base_new;
 	unsigned int id;
+	u32 intr_pin;
 	phy_interface_t p5_interface;
 	unsigned int p5_intf_sel;
 	u8 mirror_rx;
 	u8 mirror_tx;
 	u8 eee_enable;
+	u32 extSurge;
 
 	struct an8855_port ports[AN8855_NUM_PORTS];
 	/* protect among processes for registers access */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855_nl.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855_nl.c
index b80ce6f..45099d8 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855_nl.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855_nl.c
@@ -45,8 +45,8 @@
 static const struct nla_policy an8855_nl_cmd_policy[] = {
 	[AN8855_ATTR_TYPE_MESG] = {.type = NLA_STRING},
 	[AN8855_ATTR_TYPE_PHY] = {.type = NLA_S32},
-	[AN8855_ATTR_TYPE_REG] = {.type = NLA_S32},
-	[AN8855_ATTR_TYPE_VAL] = {.type = NLA_S32},
+	[AN8855_ATTR_TYPE_REG] = {.type = NLA_U32},
+	[AN8855_ATTR_TYPE_VAL] = {.type = NLA_U32},
 	[AN8855_ATTR_TYPE_DEV_NAME] = {.type = NLA_S32},
 	[AN8855_ATTR_TYPE_DEV_ID] = {.type = NLA_S32},
 	[AN8855_ATTR_TYPE_DEVAD] = {.type = NLA_S32},
@@ -150,15 +150,15 @@
 an8855_nl_reply_read(struct genl_info *info)
 {
 	struct sk_buff *rep_skb = NULL;
-	s32 phy, devad, reg;
-	int value;
+	s32 phy, devad;
+	u32 reg = 0;
+	int value = 0;
 	int ret = 0;
 
 	phy = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_PHY, -1);
 	devad = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_DEVAD, -1);
-	reg = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_REG, -1);
 
-	if (reg < 0)
+	if (an8855_nl_get_u32(info, AN8855_ATTR_TYPE_REG, &reg))
 		goto err;
 
 	ret = an8855_nl_prepare_reply(info, AN8855_CMD_READ, &rep_skb);
@@ -172,11 +172,11 @@
 						devad, reg);
 	} else
 		value = an8855_read(an8855_sw_priv, reg);
-	ret = nla_put_s32(rep_skb, AN8855_ATTR_TYPE_REG, reg);
+	ret = nla_put_u32(rep_skb, AN8855_ATTR_TYPE_REG, reg);
 	if (ret < 0)
 		goto err;
 
-	ret = nla_put_s32(rep_skb, AN8855_ATTR_TYPE_VAL, value);
+	ret = nla_put_u32(rep_skb, AN8855_ATTR_TYPE_VAL, value);
 	if (ret < 0)
 		goto err;
 
@@ -193,18 +193,16 @@
 an8855_nl_reply_write(struct genl_info *info)
 {
 	struct sk_buff *rep_skb = NULL;
-	s32 phy, devad, reg;
-	u32 value;
+	s32 phy, devad;
+	u32 value = 0, reg = 0;
 	int ret = 0;
 
 	phy = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_PHY, -1);
 	devad = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_DEVAD, -1);
-	reg = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_REG, -1);
-
-	if (an8855_nl_get_u32(info, AN8855_ATTR_TYPE_VAL, &value))
+	if (an8855_nl_get_u32(info, AN8855_ATTR_TYPE_REG, &reg))
 		goto err;
 
-	if (reg < 0)
+	if (an8855_nl_get_u32(info, AN8855_ATTR_TYPE_VAL, &value))
 		goto err;
 
 	ret = an8855_nl_prepare_reply(info, AN8855_CMD_WRITE, &rep_skb);
@@ -218,11 +216,11 @@
 					  value);
 	} else
 		an8855_write(an8855_sw_priv, reg, value);
-	ret = nla_put_s32(rep_skb, AN8855_ATTR_TYPE_REG, reg);
+	ret = nla_put_u32(rep_skb, AN8855_ATTR_TYPE_REG, reg);
 	if (ret < 0)
 		goto err;
 
-	ret = nla_put_s32(rep_skb, AN8855_ATTR_TYPE_VAL, value);
+	ret = nla_put_u32(rep_skb, AN8855_ATTR_TYPE_VAL, value);
 	if (ret < 0)
 		goto err;
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855_phy.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855_phy.c
index 5499312..9df0834 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855_phy.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855_phy.c
@@ -14,32 +14,36 @@
 #include "an8855.h"
 #include "an8855_phy.h"
 
-#define AN8855_NUM_PHYS 5
+#define AN8855_EFUSE_DATA0	0x1000a500
 
-static u32
-an8855_phy_read_dev_reg(struct dsa_switch *ds, u32 port_num,
-				   u32 dev_addr, u32 reg_addr)
+const u8 dsa_r50ohm_table[] = {
+	127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+	127, 127, 127, 127, 127, 127, 127, 126, 122, 117,
+	112, 109, 104, 101,  97,  94,  90,  88,  84,  80,
+	78,  74,  72,  68,  66,  64,  61,  58,  56,  53,
+	51,  48,  47,  44,  42,  40,  38,  36,  34,  32,
+	31,  28,  27,  24,  24,  22,  20,  18,  16,  16,
+	14,  12,  11,   9
+};
+
+static u8 shift_check(u8 base)
 {
-	struct an8855_priv *priv = ds->priv;
-	u32 phy_val;
-	u32 addr;
+	u8 i;
+	u32 sz = sizeof(dsa_r50ohm_table)/sizeof(u8);
 
-	addr = MII_ADDR_C45 | (dev_addr << 16) | (reg_addr & 0xffff);
-	phy_val = priv->info->phy_read(ds, port_num, addr);
+	for (i = 0; i < sz; ++i)
+		if (dsa_r50ohm_table[i] == base)
+			break;
 
-	return phy_val;
+	if (i < 8 || i >= sz)
+		return 25; /* index of 94 */
+
+	return (i - 8);
 }
 
-static void
-an8855_phy_write_dev_reg(struct dsa_switch *ds, u32 port_num,
-				     u32 dev_addr, u32 reg_addr, u32 write_data)
+static u8 get_shift_val(u8 idx)
 {
-	struct an8855_priv *priv = ds->priv;
-	u32 addr;
-
-	addr = MII_ADDR_C45 | (dev_addr << 16) | (reg_addr & 0xffff);
-
-	priv->info->phy_write(ds, port_num, addr, write_data);
+	return dsa_r50ohm_table[idx];
 }
 
 static void
@@ -64,7 +68,10 @@
 an8855_phy_setting(struct dsa_switch *ds)
 {
 	struct an8855_priv *priv = ds->priv;
-	int i;
+	int i, j;
+	u8 shift_sel = 0, rsel_tx_a = 0, rsel_tx_b = 0;
+	u8 rsel_tx_c = 0, rsel_tx_d = 0;
+	u16 cl45_data = 0;
 	u32 val;
 
 	/* Release power down */
@@ -82,48 +89,89 @@
 		val |= ADVERTISE_PAUSE_ASYM;
 		an8855_switch_phy_write(ds, i, MII_ADVERTISE, val);
 	}
+
+	if (priv->extSurge) {
+		for (i = 0; i < AN8855_NUM_PHYS; i++) {
+			/* Read data */
+			for (j = 0; j < AN8855_WORD_SIZE; j++) {
+				val = an8855_read(priv, AN8855_EFUSE_DATA0 +
+					(AN8855_WORD_SIZE * (3 + j + (4 * i))));
+
+				shift_sel = shift_check((val & 0x7f000000) >> 24);
+				switch (j) {
+				case 0:
+					rsel_tx_a = get_shift_val(shift_sel);
+					break;
+				case 1:
+					rsel_tx_b = get_shift_val(shift_sel);
+					break;
+				case 2:
+					rsel_tx_c = get_shift_val(shift_sel);
+					break;
+				case 3:
+					rsel_tx_d = get_shift_val(shift_sel);
+					break;
+				default:
+					continue;
+				}
+			}
+			cl45_data = an8855_phy_cl45_read(priv, i, PHY_DEV1E, 0x174);
+			cl45_data &= ~(0x7f7f);
+			cl45_data |= (rsel_tx_a << 8);
+			cl45_data |= rsel_tx_b;
+			an8855_phy_cl45_write(priv, i, PHY_DEV1E, 0x174, cl45_data);
+			cl45_data = an8855_phy_cl45_read(priv, i, PHY_DEV1E, 0x175);
+			cl45_data &= ~(0x7f7f);
+			cl45_data |= (rsel_tx_c << 8);
+			cl45_data |= rsel_tx_d;
+			an8855_phy_cl45_write(priv, i, PHY_DEV1E, 0x175, cl45_data);
+		}
+	}
 }
 
 static void
 an8855_low_power_setting(struct dsa_switch *ds)
 {
 	int port, addr;
+	struct an8855_priv *priv = ds->priv;
 
 	for (port = 0; port < AN8855_NUM_PHYS; port++) {
-		an8855_phy_write_dev_reg(ds, port, 0x1e, 0x11, 0x0f00);
-		an8855_phy_write_dev_reg(ds, port, 0x1e, 0x3c, 0x0000);
-		an8855_phy_write_dev_reg(ds, port, 0x1e, 0x3d, 0x0000);
-		an8855_phy_write_dev_reg(ds, port, 0x1e, 0x3e, 0x0000);
-		an8855_phy_write_dev_reg(ds, port, 0x1e, 0xc6, 0x53aa);
+		an8855_phy_cl45_write(priv, port, 0x1e, 0x11, 0x0f00);
+		an8855_phy_cl45_write(priv, port, 0x1e, 0x3c, 0x0000);
+		an8855_phy_cl45_write(priv, port, 0x1e, 0x3d, 0x0000);
+		an8855_phy_cl45_write(priv, port, 0x1e, 0x3e, 0x0000);
+		an8855_phy_cl45_write(priv, port, 0x1e, 0xc6, 0x53aa);
 	}
 
-	an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x268, 0x07f1);
-	an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x269, 0x2111);
-	an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x26a, 0x0000);
-	an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x26b, 0x0074);
-	an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x26e, 0x00f6);
-	an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x26f, 0x6666);
-	an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x271, 0x2c02);
-	an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x272, 0x0c22);
-	an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x700, 0x0001);
-	an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x701, 0x0803);
-	an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x702, 0x01b6);
-	an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x703, 0x2111);
+	an8855_phy_cl45_write(priv, 0, 0x1f, 0x268, 0x07f1);
+	an8855_phy_cl45_write(priv, 0, 0x1f, 0x269, 0x2111);
+	an8855_phy_cl45_write(priv, 0, 0x1f, 0x26a, 0x0000);
+	an8855_phy_cl45_write(priv, 0, 0x1f, 0x26b, 0x0074);
+	an8855_phy_cl45_write(priv, 0, 0x1f, 0x26e, 0x00f6);
+	an8855_phy_cl45_write(priv, 0, 0x1f, 0x26f, 0x6666);
+	an8855_phy_cl45_write(priv, 0, 0x1f, 0x271, 0x2c02);
+	an8855_phy_cl45_write(priv, 0, 0x1f, 0x272, 0x0c22);
+	an8855_phy_cl45_write(priv, 0, 0x1f, 0x700, 0x0001);
+	an8855_phy_cl45_write(priv, 0, 0x1f, 0x701, 0x0803);
+	an8855_phy_cl45_write(priv, 0, 0x1f, 0x702, 0x01b6);
+	an8855_phy_cl45_write(priv, 0, 0x1f, 0x703, 0x2111);
 
-	an8855_phy_write_dev_reg(ds, 1, 0x1f, 0x700, 0x0001);
+	an8855_phy_cl45_write(priv, 1, 0x1f, 0x700, 0x0001);
 
 	for (addr = 0x200; addr <= 0x230; addr += 2)
-		an8855_phy_write_dev_reg(ds, 0, 0x1f, addr, 0x2020);
+		an8855_phy_cl45_write(priv, 0, 0x1f, addr, 0x2020);
 
 	for (addr = 0x201; addr <= 0x231; addr += 2)
-		an8855_phy_write_dev_reg(ds, 0, 0x1f, addr, 0x0020);
+		an8855_phy_cl45_write(priv, 0, 0x1f, addr, 0x0020);
 }
 
 static void
 an8855_eee_setting(struct dsa_switch *ds, u32 port)
 {
+	struct an8855_priv *priv = ds->priv;
+
 	/* Disable EEE */
-	an8855_phy_write_dev_reg(ds, port, PHY_DEV07, PHY_DEV07_REG_03C, 0);
+	an8855_phy_cl45_write(priv, port, PHY_DEV07, PHY_DEV07_REG_03C, 0);
 }
 
 int
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855_phy.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855_phy.h
index 63a03bb..1193e67 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855_phy.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/airoha/an8855/an8855_phy.h
@@ -12,6 +12,8 @@
 
 #include <linux/bitops.h>
 
+#define AN8855_NUM_PHYS	5
+
 /*phy calibration use*/
 #define DEV_1E				0x1E
 /*global device 0x1f, always set P0*/
@@ -169,11 +171,69 @@
 /* PHY EEE Register bitmap of define */
 #define PHY_DEV07			0x07
 #define PHY_DEV07_REG_03C		0x3c
+#define ADV_EEE_100			0x2
+#define ADV_EEE_1000		0x4
 
 /* PHY DEV 0x1e Register bitmap of define */
 #define PHY_DEV1E			0x1e
+/* PHY TX PAIR DELAY SELECT Register */
+#define PHY_TX_PAIR_DLY_SEL_GBE		0x013
+/* PHY ADC Register */
+#define PHY_RXADC_CTRL				0x0d8
+#define PHY_RXADC_REV_0				0x0d9
+#define PHY_RXADC_REV_1				0x0da
+
+/* PHY LED Register bitmap of define */
+#define PHY_LED_CTRL_SELECT		0x3e8
+#define PHY_SINGLE_LED_ON_CTRL(i)	(0x3e0 + ((i) * 2))
+#define PHY_SINGLE_LED_BLK_CTRL(i)	(0x3e1 + ((i) * 2))
+#define PHY_SINGLE_LED_ON_DUR(i)	(0x3e9 + ((i) * 2))
+#define PHY_SINGLE_LED_BLK_DUR(i)	(0x3ea + ((i) * 2))
+
+#define PHY_PMA_CTRL	(0x340)
+
 #define PHY_DEV1F			0x1f
 
+#define PHY_LED_ON_CTRL(i)		(0x24 + ((i) * 2))
+#define LED_ON_EN				(1 << 15)
+#define LED_ON_POL				(1 << 14)
+#define LED_ON_EVT_MASK			(0x7f)
+
+/* LED ON Event */
+#define LED_ON_EVT_FORCE		(1 << 6)
+#define LED_ON_EVT_LINK_HD		(1 << 5)
+#define LED_ON_EVT_LINK_FD		(1 << 4)
+#define LED_ON_EVT_LINK_DOWN	(1 << 3)
+#define LED_ON_EVT_LINK_10M		(1 << 2)
+#define LED_ON_EVT_LINK_100M	(1 << 1)
+#define LED_ON_EVT_LINK_1000M	(1 << 0)
+
+#define PHY_LED_BLK_CTRL(i)		(0x25 + ((i) * 2))
+#define LED_BLK_EVT_MASK		(0x3ff)
+/* LED Blinking Event */
+#define LED_BLK_EVT_FORCE			(1 << 9)
+#define LED_BLK_EVT_10M_RX_ACT		(1 << 5)
+#define LED_BLK_EVT_10M_TX_ACT		(1 << 4)
+#define LED_BLK_EVT_100M_RX_ACT		(1 << 3)
+#define LED_BLK_EVT_100M_TX_ACT		(1 << 2)
+#define LED_BLK_EVT_1000M_RX_ACT	(1 << 1)
+#define LED_BLK_EVT_1000M_TX_ACT	(1 << 0)
+
+#define PHY_LED_BCR				(0x21)
+#define LED_BCR_EXT_CTRL		(1 << 15)
+#define LED_BCR_CLK_EN			(1 << 3)
+#define LED_BCR_TIME_TEST		(1 << 2)
+#define LED_BCR_MODE_MASK		(3)
+#define LED_BCR_MODE_DISABLE	(0)
+
+#define PHY_LED_ON_DUR			(0x22)
+#define LED_ON_DUR_MASK			(0xffff)
+
+#define PHY_LED_BLK_DUR			(0x23)
+#define LED_BLK_DUR_MASK		(0xffff)
+
+#define PHY_LED_BLINK_DUR_CTRL	(0x720)
+
 /* Proprietory Control Register of Internal Phy device 0x1e */
 #define PHY_TX_MLT3_BASE		0x0
 #define PHY_DEV1E_REG_13		0x13
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c
index fca5e3e..1916266 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c
@@ -609,7 +609,6 @@
 	case PSE_EIP197_PORT:
 		pse_info_get_cdm(eth, port, 6, options);
 		break;
-		break;
 	default:
 		pr_info("Not supported\n");
 		break;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c
index df23ae3..19cd4e3 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c
@@ -42,6 +42,41 @@
 	reset_event->count[id]++;
 }
 
+static void mtk_dump_reg(void *_eth, char *name, u32 offset, u32 range)
+{
+	struct mtk_eth *eth = _eth;
+	u32 cur = offset;
+
+	pr_info("\n============ %s ============\n", name);
+	while (cur < offset + range) {
+		pr_info("0x%x: %08x %08x %08x %08x\n",
+			cur, mtk_r32(eth, cur), mtk_r32(eth, cur + 0x4),
+			mtk_r32(eth, cur + 0x8), mtk_r32(eth, cur + 0xc));
+		cur += 0x10;
+	}
+}
+
+static void mtk_dump_regmap(struct regmap *pmap, char *name,
+			    u32 offset, u32 range)
+{
+	unsigned int cur = offset;
+	unsigned int val1 = 0, val2 = 0, val3 = 0, val4 = 0;
+
+	if (!pmap)
+		return;
+
+	pr_info("\n============ %s ============\n", name);
+	while (cur < offset + range) {
+		regmap_read(pmap, cur, &val1);
+		regmap_read(pmap, cur + 0x4, &val2);
+		regmap_read(pmap, cur + 0x8, &val3);
+		regmap_read(pmap, cur + 0xc, &val4);
+		pr_info("0x%x: %08x %08x %08x %08x\n",
+			cur, val1, val2, val3, val4);
+		cur += 0x10;
+	}
+}
+
 int mtk_eth_cold_reset(struct mtk_eth *eth)
 {
 	u32 reset_bits = 0;
@@ -55,16 +90,15 @@
 #if defined(CONFIG_MEDIATEK_NETSYS_V3)
 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE2))
 		reset_bits |= RSTCTRL_PPE2;
-	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3))
+	if (mtk_reset_flag == MTK_FE_START_RESET)
 		reset_bits |= RSTCTRL_WDMA0 | RSTCTRL_WDMA1 | RSTCTRL_WDMA2;
 #endif
 	ethsys_reset(eth, reset_bits);
 
-	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-		regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0x3ffffff);
-
 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3))
-		regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0x6F8FF);
+		regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0x6f8ff);
+	else if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
+		regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0x3ffffff);
 
 	return 0;
 }
@@ -89,6 +123,7 @@
 	}
 
 	if (i < 1000) {
+		done = 1;
 		reset_bits = RSTCTRL_ETH | RSTCTRL_PPE0;
 		if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
 			reset_bits |= RSTCTRL_PPE1;
@@ -96,8 +131,7 @@
 		if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE2))
 			reset_bits |= RSTCTRL_PPE2;
 		if (mtk_reset_flag == MTK_FE_START_RESET)
-			reset_bits |= RSTCTRL_WDMA0 |
-			RSTCTRL_WDMA1 | RSTCTRL_WDMA2;
+			reset_bits |= RSTCTRL_WDMA0 | RSTCTRL_WDMA1 | RSTCTRL_WDMA2;
 #endif
 
 		regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL,
@@ -105,27 +139,51 @@
 
 		udelay(1);
 		regmap_read(eth->ethsys, ETHSYS_RSTCTRL, &val2);
-		if (!(val2 & reset_bits))
+		if (!(val2 & reset_bits)) {
 			pr_info("[%s] error val2=0x%x reset_bits=0x%x !\n",
 				__func__, val2, reset_bits);
+			done = 0;
+		}
 		reset_bits |= RSTCTRL_FE;
 		regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL,
 				   reset_bits, ~reset_bits);
-
 		udelay(1);
 		regmap_read(eth->ethsys, ETHSYS_RSTCTRL, &val3);
-		if (val3 & reset_bits)
+		if (val3 & reset_bits) {
 			pr_info("[%s] error val3=0x%x reset_bits=0x%x !\n",
 				__func__, val3, reset_bits);
-		done = 1;
+			done = 0;
+		}
 		mtk_reset_event_update(eth, MTK_EVENT_WARM_CNT);
 	}
 
 	pr_info("[%s] reset record val1=0x%x, val2=0x%x, val3=0x%x i:%d done:%d\n",
 		__func__, val1, val2, val3, i, done);
 
-	if (!done)
+	if (!done) {
+		mtk_dump_reg(eth, "FE", 0x0, 0x300);
+		mtk_dump_reg(eth, "ADMA", PDMA_BASE + 0x200, 0x10);
+		mtk_dump_reg(eth, "QDMA", QDMA_BASE + 0x200, 0x10);
+		mtk_dump_reg(eth, "WDMA0", WDMA_BASE(0) + 0x200, 0x10);
+		mtk_dump_reg(eth, "WDMA1", WDMA_BASE(1) + 0x200, 0x10);
+		mtk_dump_reg(eth, "PPE0", PPE_BASE(0), 0x10);
+		mtk_dump_reg(eth, "PPE0", PPE_BASE(0) + 0x180, 0x20);
+		if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V1)) {
+			mtk_dump_reg(eth, "PPE1", PPE_BASE(1), 0x10);
+			mtk_dump_reg(eth, "PPE1", PPE_BASE(1) + 0x180, 0x20);
+		}
+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
+			mtk_dump_reg(eth, "PPE2", PPE_BASE(2), 0x10);
+			mtk_dump_reg(eth, "PPE2", PPE_BASE(2) + 0x180, 0x20);
+			regmap_write(eth->ethsys, ETHSYS_LP_NONE_IDLE_LAT0, 0xffffffff);
+			regmap_write(eth->ethsys, ETHSYS_LP_NONE_IDLE_LAT1, 0xffffffff);
+			regmap_read(eth->ethsys, ETHSYS_LP_NONE_IDLE_LAT0, &val1);
+			regmap_read(eth->ethsys, ETHSYS_LP_NONE_IDLE_LAT1, &val2);
+			pr_info("ETHSYS_LP_NONE_IDLE_LAT0:%x\n", val1);
+			pr_info("ETHSYS_LP_NONE_IDLE_LAT1:%x\n", val2);
+		}
 		mtk_eth_cold_reset(eth);
+	}
 
 	return 0;
 }
@@ -187,41 +245,6 @@
 	return IRQ_HANDLED;
 }
 
-static void mtk_dump_reg(void *_eth, char *name, u32 offset, u32 range)
-{
-	struct mtk_eth *eth = _eth;
-	u32 cur = offset;
-
-	pr_info("\n============ %s ============\n", name);
-	while(cur < offset + range) {
-		pr_info("0x%x: %08x %08x %08x %08x\n",
-			cur, mtk_r32(eth, cur), mtk_r32(eth, cur + 0x4),
-			mtk_r32(eth, cur + 0x8), mtk_r32(eth, cur + 0xc));
-		cur += 0x10;
-	}
-}
-
-static void mtk_dump_regmap(struct regmap *pmap, char *name,
-			    u32 offset, u32 range)
-{
-	unsigned int cur = offset;
-	unsigned int val1 = 0, val2 = 0, val3 = 0, val4 = 0;
-
-	if (!pmap)
-		return;
-
-	pr_info("\n============ %s ============\n", name);
-	while (cur < offset + range) {
-		regmap_read(pmap, cur, &val1);
-		regmap_read(pmap, cur + 0x4, &val2);
-		regmap_read(pmap, cur + 0x8, &val3);
-		regmap_read(pmap, cur + 0xc, &val4);
-		pr_info("0x%x: %08x %08x %08x %08x\n",
-			cur, val1, val2, val3, val4);
-		cur += 0x10;
-	}
-}
-
 void mtk_dump_netsys_info(void *_eth)
 {
 	struct mtk_eth *eth = _eth;
@@ -266,7 +289,7 @@
 
 	cur_opq = (mtk_r32(eth, MTK_PSE_OQ_STA(id)) & mask);
 	if ((cur_opq != 0) && (cur_opq == *pre_opq))
-		*err_opq++;
+		(*err_opq)++;
 	else
 		*err_opq = 0;
 
@@ -279,7 +302,7 @@
 	static u32 err_cnt[MTK_WDMA_CNT];
 	static u32 err_opq1, err_opq2, err_opq8;
 	static u32 err_opq9, err_opq13, err_opq15;
-	u32 opq1, opq2, opq8, opq9, opq13, opq15;
+	static u32 opq1, opq2, opq8, opq9, opq13, opq15;
 	u32 cur_dtx, tx_busy, fsm_ts;
 	u32 i, err_opq = 0, err_flag = 0;
 
@@ -342,26 +365,42 @@
 u32 mtk_monitor_wdma_rx(struct mtk_eth *eth)
 {
 	static u32 pre_drx[MTK_WDMA_CNT];
+	static u32 pre_crx[MTK_WDMA_CNT];
 	static u32 pre_opq[MTK_WDMA_CNT];
 	static u32 err_cnt[MTK_WDMA_CNT];
-	u32 cur_crx = 0, cur_drx = 0, cur_opq = 0, fsm_fs;
+	bool connsys_busy, netsys_busy;
+	u32 cur_crx = 0, cur_drx = 0, cur_opq = 0, fsm_fs, max_cnt;
 	u32 i, err_flag = 0;
+	int rx_cnt;
 
 	for (i = 0; i < MTK_WDMA_CNT; i++) {
+		connsys_busy = netsys_busy = false;
+		max_cnt = mtk_r32(eth, MTK_WDMA_RX_MAX_CNT(i));
 		cur_crx = mtk_r32(eth, MTK_WDMA_CRX_PTR(i));
 		cur_drx = mtk_r32(eth, MTK_WDMA_DRX_PTR(i));
+		rx_cnt = (cur_drx > cur_crx) ? (cur_drx - 1 - cur_crx) :
+					       (cur_drx - 1 - cur_crx + max_cnt);
 		cur_opq = MTK_FE_WDMA_OQ(i);
 		fsm_fs = mtk_r32(eth, MTK_FE_CDM_FSM(i)) &
 			(MTK_CDM_FS_FSM_MASK | MTK_CDM_FS_PARSER_FSM_MASK);
-		/*drx unchange && ring not full && output && fsm_fs*/
-		if (cur_drx == pre_drx[i] && (cur_crx != cur_drx) &&
-		    (cur_opq != 0 && cur_opq == pre_opq[i]) && fsm_fs) {
+		/* drx and crx remain unchanged && rx_cnt is not zero */
+		if ((cur_drx == pre_drx[i]) && (cur_crx == pre_crx[i]) && (rx_cnt > 0))
+			connsys_busy = true;
+		/* drx and crx remain unchanged && pse_opq is not empty */
+		else if ((cur_drx == pre_drx[i]) && (cur_crx == pre_crx[i]) &&
+			 (cur_opq != 0 && cur_opq == pre_opq[i]) && fsm_fs)
+			netsys_busy = true;
+		if (connsys_busy || netsys_busy) {
 			err_cnt[i]++;
 			if (err_cnt[i] >= 3) {
-				pr_info("WDMA %d Rx Info\n", i);
+				pr_info("WDMA %d Rx Info (%s)\n", i,
+					connsys_busy ? "CONNSYS busy" : "NETSYS busy");
 				pr_info("err_cnt = %d", err_cnt[i]);
 				pr_info("prev_drx = 0x%x	| cur_drx = 0x%x\n",
 					pre_drx[i], cur_drx);
+				pr_info("prev_crx = 0x%x	| cur_crx = 0x%x\n",
+					pre_crx[i], cur_crx);
+				pr_info("rx cnt = %d\n", rx_cnt);
 				pr_info("WDMA_CRX_PTR = 0x%x\n",
 					mtk_r32(eth, MTK_WDMA_CRX_PTR(i)));
 				pr_info("WDMA_DRX_PTR = 0x%x\n",
@@ -375,6 +414,7 @@
 			}
 		} else
 			err_cnt[i] = 0;
+		pre_crx[i] = cur_crx;
 		pre_drx[i] = cur_drx;
 		pre_opq[i] = cur_opq;
 	}
@@ -387,15 +427,21 @@
 
 u32 mtk_monitor_rx_fc(struct mtk_eth *eth)
 {
-	u32 i = 0, mib_base = 0, gdm_fc = 0;
+	struct mtk_hw_stats *hw_stats;
+	static u32 gdm_rx_fc[MTK_MAX_DEVS];
+	u32 i = 0, is_rx_fc = 0;
 
 	for (i = 0; i < MTK_MAC_COUNT; i++) {
-		mib_base = MTK_GDM1_TX_GBCNT + MTK_STAT_OFFSET*i + MTK_GDM_RX_FC;
-		gdm_fc =  mtk_r32(eth, mib_base);
-		if (gdm_fc < 1)
-			return 1;
+		if (!eth->mac[i] || !netif_running(eth->netdev[i]))
+			continue;
+
+		hw_stats = eth->mac[i]->hw_stats;
+		if (hw_stats->rx_flow_control_packets - gdm_rx_fc[i])
+			is_rx_fc = 1;
+
+		gdm_rx_fc[i] = hw_stats->rx_flow_control_packets;
 	}
-	return 0;
+	return is_rx_fc;
 }
 
 u32 mtk_monitor_qdma_tx(struct mtk_eth *eth)
@@ -408,7 +454,7 @@
 	u32 is_qfwd_hang = mtk_r32(eth, MTK_QDMA_FWD_CNT) == 0;
 
 	is_rx_fc = mtk_monitor_rx_fc(eth);
-	if (is_qfsm_hang && is_qfwd_hang && is_rx_fc) {
+	if (is_qfsm_hang && is_qfwd_hang && !is_rx_fc) {
 		err_cnt_qtx++;
 		if (err_cnt_qtx >= 3) {
 			pr_info("QDMA Tx Info\n");
@@ -604,6 +650,9 @@
 	u32 cur_fsm, pse_ipq, err_flag = 0, i;
 
 	for (i = 0; i < MTK_MAX_DEVS; i++) {
+		if (!eth->mac[i] || !netif_running(eth->netdev[i]))
+			continue;
+
 		struct mtk_hw_stats *hw_stats = eth->mac[i]->hw_stats;
 
 		is_gmac_rx[i] = (mtk_r32(eth, MTK_MAC_FSM(i)) & 0xFF0000) != 0x10000;
@@ -623,6 +672,9 @@
 
 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
 		for (i = 0; i < MTK_MAX_DEVS; i++) {
+			if (!eth->mac[i] || !netif_running(eth->netdev[i]))
+				continue;
+
 			if (i == 0) {
 				pse_ipq = (mtk_r32(eth, MTK_PSE_IQ_STA(0)) >> 16) & 0xFFF;
 				cur_fsm = mtk_r32(eth, MTK_FE_GDM1_FSM) & 0xFF;
@@ -672,6 +724,9 @@
 	u32 err_flag = 0, i, pse_opq;
 
 	for (i = 0; i < MTK_MAX_DEVS; i++) {
+		if (!eth->mac[i] || !netif_running(eth->netdev[i]))
+			continue;
+
 		struct mtk_hw_stats *hw_stats = eth->mac[i]->hw_stats;
 
 		if (i == 0)
@@ -697,6 +752,9 @@
 	}
 
 	for (i = 0; i < MTK_MAX_DEVS; i++) {
+		if (!eth->mac[i] || !netif_running(eth->netdev[i]))
+			continue;
+
 		gdm_fsm = mtk_r32(eth, MTK_FE_GDM_FSM(i)) & 0x1FFF0000;
 		pse_opq = MTK_FE_GDM_OQ(i);
 		if ((pre_gdm[i] == gdm_fsm) && (gdm_fsm == 0x10330000) &&
@@ -811,6 +869,9 @@
 	u32 mcr, sts, i;
 
 	for (i = 0; i < MTK_MAX_DEVS; i++) {
+		if (!eth->mac[i])
+			continue;
+
 		mac = eth->mac[i];
 		if (mac->type == MTK_GDM_TYPE) {
 			mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
@@ -829,18 +890,21 @@
 	}
 }
 
-void mtk_pse_port_linkdown(struct mtk_eth *eth, int port)
+void mtk_pse_set_port_link(struct mtk_eth *eth, u32 port, bool enable)
 {
-	u32 fe_glo_cfg;
+	u32 val;
 
-	fe_glo_cfg = mtk_r32(eth, MTK_FE_GLO_CFG(port));
-	fe_glo_cfg |= MTK_FE_LINK_DOWN_PORT(port);
-	mtk_w32(eth, fe_glo_cfg, MTK_FE_GLO_CFG(port));
+	val = mtk_r32(eth, MTK_FE_GLO_CFG(port));
+	if (enable)
+		val &= ~MTK_FE_LINK_DOWN_P(port);
+	else
+		val |= MTK_FE_LINK_DOWN_P(port);
+	mtk_w32(eth, val, MTK_FE_GLO_CFG(port));
 }
 
 void mtk_prepare_reset_fe(struct mtk_eth *eth)
 {
-	u32 i = 0, val = 0, mcr = 0;
+	u32 i = 0, val = 0;
 
 	/* Disable NETSYS Interrupt */
 	mtk_w32(eth, 0, MTK_FE_INT_ENABLE);
@@ -851,6 +915,9 @@
 	for (i = 0; i < MTK_MAC_COUNT; i++) {
 		if (!eth->netdev[i])
 			continue;
+
+		/* call carrier off first to avoid false dev_watchdog timeouts */
+		netif_carrier_off(eth->netdev[i]);
 		netif_tx_disable(eth->netdev[i]);
 	}
 
@@ -861,14 +928,14 @@
 	/* Force mac link down */
 	mtk_mac_linkdown(eth);
 
-	/* Force pse port link down */
-	mtk_pse_port_linkdown(eth, 0);
-	mtk_pse_port_linkdown(eth, 1);
-	mtk_pse_port_linkdown(eth, 2);
-	mtk_pse_port_linkdown(eth, 8);
-	mtk_pse_port_linkdown(eth, 9);
+	/* Force PSE port link down */
+	mtk_pse_set_port_link(eth, 0, false);
+	mtk_pse_set_port_link(eth, 1, false);
+	mtk_pse_set_port_link(eth, 2, false);
+	mtk_pse_set_port_link(eth, 8, false);
+	mtk_pse_set_port_link(eth, 9, false);
 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3))
-		mtk_pse_port_linkdown(eth, 15);
+		mtk_pse_set_port_link(eth, 15, false);
 
 	/* Enable GDM drop */
 	for (i = 0; i < MTK_MAC_COUNT; i++)
@@ -911,9 +978,9 @@
 	}
 
 	if (i >= poll_time) {
-		pr_info("[%s] PPE keeps busy !\n", __func__);
+		pr_warn("[%s] PPE%d keeps busy !\n", __func__, ppe_id);
 		mtk_dump_reg(eth, "FE", 0x0, 0x500);
-		mtk_dump_reg(eth, "PPE", 0x2200, 0x200);
+		mtk_dump_reg(eth, "PPE", PPE_BASE(ppe_id), 0x200);
 	}
 }
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.h
index dd8206a..facb645 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.h
@@ -92,4 +92,5 @@
 void mtk_prepare_reset_fe(struct mtk_eth *eth);
 void mtk_prepare_reset_ppe(struct mtk_eth *eth, u32 ppe_id);
 
+void mtk_pse_set_port_link(struct mtk_eth *eth, u32 port, bool enable);
 #endif		/* MTK_ETH_RESET_H */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 8971a7f..82f87b5 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -623,9 +623,11 @@
 {
 	struct mtk_mac *mac = container_of(config, struct mtk_mac,
 					   phylink_config);
+	struct mtk_eth *eth = mac->hw;
 	u32 val;
 
-	if (mac->type == MTK_XGDM_TYPE && mac->id != MTK_GMAC1_ID) {
+	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) &&
+	    mac->id != MTK_GMAC1_ID) {
 		val = mtk_r32(mac->hw, MTK_XMAC_MCR(mac->id));
 		val &= 0xfffffff0;
 		val |= XMAC_MCR_TRX_DISABLE;
@@ -970,10 +972,10 @@
 			__func__, gdm_fsm, mac_fsm);
 }
 
-static void mtk_pse_port_link_set(struct mtk_mac *mac, bool up,
-				  phy_interface_t interface)
+static void mtk_pse_set_mac_port_link(struct mtk_mac *mac, bool up,
+				      phy_interface_t interface)
 {
-	u32 fe_glo_cfg, val = 0, port = 0;
+	u32 port = 0;
 
 	if (!up && interface == PHY_INTERFACE_MODE_XGMII) {
 		void __iomem *base;
@@ -995,26 +997,18 @@
 	switch (mac->id) {
 	case MTK_GMAC1_ID:
 		port = PSE_GDM1_PORT;
-		val = MTK_FE_LINK_DOWN_PORT(port);
 		break;
 	case MTK_GMAC2_ID:
 		port = PSE_GDM2_PORT;
-		val = MTK_FE_LINK_DOWN_PORT(port);
 		break;
 	case MTK_GMAC3_ID:
 		port = PSE_GDM3_PORT;
-		val = MTK_FE_LINK_DOWN_PORT(port);
 		break;
+	default:
+		return;
 	}
 
-	fe_glo_cfg = mtk_r32(mac->hw, MTK_FE_GLO_CFG(port));
-
-	if (!up)
-		fe_glo_cfg |= val;
-	else
-		fe_glo_cfg &= ~val;
-
-	mtk_w32(mac->hw, fe_glo_cfg, MTK_FE_GLO_CFG(port));
+	mtk_pse_set_port_link(mac->hw, port, up);
 	mtk_gdm_fsm_poll(mac);
 }
 
@@ -1027,7 +1021,7 @@
 	unsigned int id;
 	u32 mcr, sts;
 
-	mtk_pse_port_link_set(mac, false, interface);
+	mtk_pse_set_mac_port_link(mac, false, interface);
 	if (mac->type == MTK_GDM_TYPE) {
 		mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
 		mcr &= ~(MAC_MCR_TX_EN | MAC_MCR_RX_EN | MAC_MCR_FORCE_LINK);
@@ -1209,7 +1203,7 @@
 		mcr &= ~(XMAC_MCR_TRX_DISABLE);
 		mtk_w32(mac->hw, mcr, MTK_XMAC_MCR(mac->id));
 	}
-	mtk_pse_port_link_set(mac, true, interface);
+	mtk_pse_set_mac_port_link(mac, true, interface);
 }
 
 static void mtk_validate(struct phylink_config *config,
@@ -2162,7 +2156,8 @@
 			}
 
 			memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info));
-			txd_info.size = min(frag_size, eth->soc->txrx.dma_max_len);
+			txd_info.size = min_t(unsigned int, frag_size,
+					      eth->soc->txrx.dma_max_len);
 			txd_info.qid = queue;
 			txd_info.last = i == skb_shinfo(skb)->nr_frags - 1 &&
 					!(frag_size - txd_info.size);
@@ -2322,7 +2317,6 @@
 	struct netdev_queue *txq;
 	bool gso = false;
 	int tx_num;
-	int i = 0;
 	int qid = skb_get_queue_mapping(skb);
 
 	/* normally we can rely on the stack not calling this more than once,
@@ -3254,8 +3248,11 @@
 	}
 
 	/* invalidate lro rings */
-	for (i = 0; i < MTK_HW_LRO_RING_NUM; i++)
-		mtk_w32(eth, 0, reg_map->pdma.lro_rx_ctrl_dw0 + 0x8 + (i * 0x40));
+	for (i = 0; i < MTK_HW_LRO_RING_NUM; i++) {
+		int idx = MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2) ? i : i + 1;
+
+		mtk_w32(eth, 0, reg_map->pdma.lro_rx_ctrl_dw0 + 0x8 + (idx * 0x40));
+	}
 
 	/* disable HW LRO */
 	mtk_w32(eth, 0, reg_map->pdma.lro_ctrl_dw0);
@@ -3763,11 +3760,21 @@
 static void mtk_dma_free(struct mtk_eth *eth)
 {
 	const struct mtk_soc_data *soc = eth->soc;
-	int i;
+	int i, j, txqs;
 
-	for (i = 0; i < MTK_MAC_COUNT; i++)
-		if (eth->netdev[i])
-			netdev_reset_queue(eth->netdev[i]);
+	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
+		txqs = MTK_QDMA_TX_NUM;
+	else
+		txqs = MTK_PDMA_TX_NUM;
+
+	for (i = 0; i < MTK_MAC_COUNT; i++) {
+		if (!eth->netdev[i])
+			continue;
+
+		for (j = 0; j < txqs; j++)
+			netdev_tx_reset_queue(netdev_get_tx_queue(eth->netdev[i], j));
+	}
+
 	if ( !eth->soc->has_sram && eth->scratch_ring) {
 		dma_free_coherent(eth->dma_dev,
 				  soc->txrx.fq_dma_size * soc->txrx.txd_size,
@@ -3859,8 +3866,8 @@
 	const struct mtk_reg_map *reg_map = eth->soc->reg_map;
 
 	if (tx_ring) {
-		if (unlikely(!(mtk_r32(eth, eth->soc->reg_map->pdma.irq_status) &
-			       mtk_r32(eth, eth->soc->reg_map->pdma.irq_mask) &
+		if (unlikely(!(mtk_r32(eth, reg_map->pdma.irq_status) &
+			       mtk_r32(eth, reg_map->pdma.irq_mask) &
 			       MTK_TX_DONE_INT(tx_ring->ring_no))))
 			return IRQ_NONE;
 
@@ -3869,8 +3876,8 @@
 			__napi_schedule(&txrx_napi->napi);
 		}
 	} else {
-		if (unlikely(!(mtk_r32(eth, eth->soc->reg_map->pdma.irq_status) &
-			       mtk_r32(eth, eth->soc->reg_map->pdma.irq_mask) &
+		if (unlikely(!(mtk_r32(eth, reg_map->pdma.irq_status) &
+			       mtk_r32(eth, reg_map->pdma.irq_mask) &
 			       MTK_RX_DONE_INT(rx_ring->ring_no))))
 			return IRQ_NONE;
 
@@ -4739,6 +4746,8 @@
 	mtk_prepare_reset_ppe(eth, 0);
 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
 		mtk_prepare_reset_ppe(eth, 1);
+	if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE2))
+		mtk_prepare_reset_ppe(eth, 2);
 
 	/* Adjust FE configurations to prepare for reset */
 	mtk_prepare_reset_fe(eth);
@@ -4757,7 +4766,7 @@
 				eth->netdev[i]);
 		}
 		rtnl_unlock();
-		if (wait_for_completion_timeout(&wait_ser_done, 3000)) {
+		if (wait_for_completion_timeout(&wait_ser_done, msecs_to_jiffies(3000))) {
 			if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) &&
 			    (mtk_stop_fail)) {
 				pr_info("send MTK_FE_START_RESET stop\n");
@@ -4766,8 +4775,9 @@
 							 eth->netdev[i]);
 				rtnl_unlock();
 				if (!wait_for_completion_timeout(&wait_ser_done,
-								 3000))
+								 msecs_to_jiffies(3000)))
 					pr_warn("wait for MTK_FE_START_RESET\n");
+				mtk_stop_fail = 0;
 			}
 			pr_warn("wait for MTK_FE_START_RESET\n");
 		}
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 198cd0e..08f5988 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -71,8 +71,10 @@
 #define MTK_RSS_MAX_INDIRECTION_TABLE	128
 
 /* Frame Engine Global Configuration */
-#define MTK_FE_GLO_CFG(port)	((port < 8) ? 0x0 : 0x24)
-#define MTK_FE_LINK_DOWN_PORT(x)	((x < 8) ? (1 << (8 + x)) : (1 << (x - 8)))
+#define MTK_FE_GLO_CFG(x)	((x < 8) ? 0x0 : 0x24)
+#define MTK_FE_LINK_DOWN_P(x)	((x < 8) ? FIELD_PREP(GENMASK(15, 8), BIT(x)) :	\
+					   FIELD_PREP(GENMASK(7, 0), BIT(x - 8)))
+
 /* Frame Engine Global Reset Register */
 #define MTK_RST_GL		0x04
 #define RST_GL_PSE		BIT(0)
@@ -539,6 +541,7 @@
 #define MTK_WDMA_GLO_CFG(x)	(WDMA_BASE(x) + 0x204)
 #define MTK_WDMA_TX_DBG_MON0(x)	(WDMA_BASE(x) + 0x230)
 #define MTK_WDMA_RX_DBG_MON1(x)	(WDMA_BASE(x) + 0x3c4)
+#define MTK_WDMA_RX_MAX_CNT(x)	(WDMA_BASE(x) + 0x104)
 #define MTK_WDMA_CRX_PTR(x)	(WDMA_BASE(x) + 0x108)
 #define MTK_WDMA_DRX_PTR(x)	(WDMA_BASE(x) + 0x10C)
 #define MTK_CDM_FS_PARSER_FSM_MASK	GENMASK(27, 24)
@@ -892,6 +895,10 @@
 /* ethernet reset check idle register */
 #define ETHSYS_FE_RST_CHK_IDLE_EN 	0x28
 
+/* ethernet non-idle check register */
+#define ETHSYS_LP_NONE_IDLE_LAT0 (0x144)
+#define ETHSYS_LP_NONE_IDLE_LAT1 (0x148)
+
 /* ethernet dma channel agent map */
 #define ETHSYS_DMA_AG_MAP	0x408
 #define ETHSYS_DMA_AG_MAP_PDMA	BIT(0)
@@ -1006,6 +1013,12 @@
 #define USXGMII_LPA_LINK	BIT(15)
 #define USXGMII_LPA_LATCH	BIT(31)
 
+/* Register to QPHY wrapper control */
+#define RG_PHY_TOP_CTRL0	0x82C
+#define USXGMII_PN_SWAP_MASK	GENMASK(1, 0)
+#define USXGMII_PN_SWAP_RX	BIT(1)
+#define USXGMII_PN_SWAP_TX	BIT(0)
+
 /* Register to read PCS Link status */
 #define RG_PCS_RX_STATUS0	0x904
 #define RG_PCS_RX_STATUS_UPDATE	BIT(16)
@@ -1775,16 +1788,18 @@
 	struct mtk_eth		*eth;
 	struct regmap		*regmap;
 	struct regmap		*regmap_pextp;
-	spinlock_t		regmap_lock;
+	struct mutex		regmap_lock;
+	struct mutex		reset_lock;
 	phy_interface_t		interface;
 	__ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
-	unsigned long		link_poll_inband;
+	unsigned long		link_poll_expire;
 	unsigned int		mode;
 	u32			flags;
 	u32			ana_rgc3;
 	u32			polarity;
 	u8			id;
-	struct timer_list	link_poll_outband;
+	struct phylink_link_state	state;
+	struct delayed_work	link_poll;
 	struct phylink_pcs	pcs;
 };
 
@@ -1812,12 +1827,15 @@
 	struct mtk_eth		*eth;
 	struct regmap		*regmap;
 	struct regmap		*regmap_pextp;
-	spinlock_t		regmap_lock;
+	struct mutex		regmap_lock;
+	struct mutex		reset_lock;
 	phy_interface_t		interface;
-	unsigned long		link_poll_inband;
+	unsigned long		link_poll_expire;
 	unsigned int		mode;
+	u32			polarity;
 	u8			id;
-	struct timer_list	link_poll_outband;
+	struct phylink_link_state	state;
+	struct delayed_work	link_poll;
 	struct phylink_pcs	pcs;
 };
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
index 011421c..d4f74c1 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
@@ -36,20 +36,24 @@
 void (*ra_sw_nat_clear_bind_entries)(void) = NULL;
 EXPORT_SYMBOL(ra_sw_nat_clear_bind_entries);
 
-int (*hnat_get_wdma_tx_port)(int wdma_idx) = NULL;
+int (*hnat_get_wdma_tx_port)(u32 wdma_idx) = NULL;
 EXPORT_SYMBOL(hnat_get_wdma_tx_port);
-int (*hnat_get_wdma_rx_port)(int wdma_idx) = NULL;
+int (*hnat_get_wdma_rx_port)(u32 wdma_idx) = NULL;
 EXPORT_SYMBOL(hnat_get_wdma_rx_port);
 
 int (*ppe_del_entry_by_mac)(unsigned char *mac) = NULL;
 EXPORT_SYMBOL(ppe_del_entry_by_mac);
+int (*ppe_del_entry_by_ip)(bool is_ipv4, void *addr) = NULL;
+EXPORT_SYMBOL(ppe_del_entry_by_ip);
+int (*ppe_del_entry_by_bssid_wcid)(u32 wdma_idx, u16 bssid, u16 wcid) = NULL;
+EXPORT_SYMBOL(ppe_del_entry_by_bssid_wcid);
 
 void (*ppe_dev_register_hook)(struct net_device *dev) = NULL;
 EXPORT_SYMBOL(ppe_dev_register_hook);
 void (*ppe_dev_unregister_hook)(struct net_device *dev) = NULL;
 EXPORT_SYMBOL(ppe_dev_unregister_hook);
 
-int (*hnat_set_wdma_pse_port_state)(int wdma_idx, int up) = NULL;
+int (*hnat_set_wdma_pse_port_state)(u32 wdma_idx, bool up) = NULL;
 EXPORT_SYMBOL(hnat_set_wdma_pse_port_state);
 
 static void hnat_sma_build_entry(struct timer_list *t)
@@ -144,7 +148,7 @@
 	}
 }
 
-static int mtk_get_wdma_tx_port(int wdma_idx)
+static int mtk_get_wdma_tx_port(u32 wdma_idx)
 {
 	if (wdma_idx == 0 || wdma_idx == 1 || wdma_idx == 2)
 		return NR_PPE0_PORT;
@@ -152,7 +156,7 @@
 	return -EINVAL;
 }
 
-static int mtk_get_wdma_rx_port(int wdma_idx)
+static int mtk_get_wdma_rx_port(u32 wdma_idx)
 {
 	if (wdma_idx == 2)
 		return NR_WDMA2_PORT;
@@ -164,20 +168,39 @@
 	return -EINVAL;
 }
 
-static int mtk_set_wdma_pse_port_state(int wdma_idx, int up)
+static int mtk_set_wdma_pse_port_state(u32 wdma_idx, bool up)
 {
-	u32 port = 0, link_dwn = 0;
+	int port;
 
 	port = mtk_get_wdma_rx_port(wdma_idx);
 	if (port < 0)
 		return -EINVAL;
 
-	link_dwn = 0x1 << (port - NR_WDMA0_PORT);
-	cr_set_field(hnat_priv->fe_base + MTK_FE_GLO_CFG(port), link_dwn, !up);
+	cr_set_field(hnat_priv->fe_base + MTK_FE_GLO_CFG(port),
+		     MTK_FE_LINK_DOWN_P((u32)port), !up);
 
 	return 0;
 }
 
+static int mtk_set_ppe_pse_port_state(u32 ppe_id, bool up)
+{
+	u32 port;
+
+	if (ppe_id == 0)
+		port = NR_PPE0_PORT;
+	else if (ppe_id == 1)
+		port = NR_PPE1_PORT;
+	else if (ppe_id == 2)
+		port = NR_PPE2_PORT;
+	else
+		return -EINVAL;
+
+	cr_set_field(hnat_priv->fe_base + MTK_FE_GLO_CFG(port),
+		     MTK_FE_LINK_DOWN_P(port), !up);
+
+	return 0;
+}
+
 void set_gmac_ppe_fwd(int id, int enable)
 {
 	void __iomem *reg;
@@ -241,6 +264,85 @@
 	return ret;
 }
 
+static int entry_ip_cmp(struct foe_entry *entry, bool is_ipv4, void *addr)
+{
+	struct in6_addr *tmp_ipv6;
+	struct in6_addr ipv6 = {0};
+	struct in6_addr foe_sipv6 = {0};
+	struct in6_addr foe_dipv6 = {0};
+	u32 *tmp_ipv4, ipv4;
+	u32 *sipv6_0 = NULL;
+	u32 *dipv6_0 = NULL;
+	int ret = 0;
+
+	if (is_ipv4) {
+		tmp_ipv4 = (u32 *)addr;
+		ipv4 = ntohl(*tmp_ipv4);
+
+		switch ((int)entry->bfib1.pkt_type) {
+		case IPV4_HNAPT:
+		case IPV4_HNAT:
+			if (entry->ipv4_hnapt.sip == ipv4 ||
+			    entry->ipv4_hnapt.new_dip == ipv4)
+				ret = 1;
+			break;
+		case IPV4_DSLITE:
+		case IPV4_MAP_E:
+			if (entry->ipv4_dslite.sip == ipv4 ||
+			    entry->ipv4_dslite.dip == ipv4)
+				ret = 1;
+			break;
+		default:
+			break;
+		}
+	} else {
+		memset(&foe_sipv6, 0, sizeof(struct in6_addr));
+		memset(&foe_dipv6, 0, sizeof(struct in6_addr));
+		memset(&ipv6, 0, sizeof(struct in6_addr));
+
+		tmp_ipv6 = (struct in6_addr *)addr;
+		ipv6.s6_addr32[0] = ntohl(tmp_ipv6->s6_addr32[0]);
+		ipv6.s6_addr32[1] = ntohl(tmp_ipv6->s6_addr32[1]);
+		ipv6.s6_addr32[2] = ntohl(tmp_ipv6->s6_addr32[2]);
+		ipv6.s6_addr32[3] = ntohl(tmp_ipv6->s6_addr32[3]);
+
+		switch ((int)entry->bfib1.pkt_type) {
+		case IPV6_3T_ROUTE:
+		case IPV6_5T_ROUTE:
+		case IPV6_6RD:
+			sipv6_0 = &(entry->ipv6_3t_route.ipv6_sip0);
+			dipv6_0 = &(entry->ipv6_3t_route.ipv6_dip0);
+			break;
+#if defined(CONFIG_MEDIATEK_NETSYS_V3)
+		case IPV6_HNAT:
+		case IPV6_HNAPT:
+			sipv6_0 = &(entry->ipv6_hnapt.ipv6_sip0);
+			dipv6_0 = &(entry->ipv6_hnapt.new_ipv6_ip0);
+			break;
+#endif
+		default:
+			break;
+		}
+
+		if (sipv6_0 && dipv6_0) {
+			memcpy(&foe_sipv6, sipv6_0, sizeof(struct in6_addr));
+			memcpy(&foe_dipv6, dipv6_0, sizeof(struct in6_addr));
+			if (!memcmp(&foe_sipv6, &ipv6, sizeof(struct in6_addr)) ||
+			    !memcmp(&foe_dipv6, &ipv6, sizeof(struct in6_addr)))
+				ret = 1;
+		}
+	}
+
+	if (ret && debug_level >= 2) {
+		if (is_ipv4)
+			pr_info("ipv4=%pI4\n", tmp_ipv4);
+		else
+			pr_info("ipv6=%pI6\n", tmp_ipv6);
+	}
+
+	return ret;
+}
+
 int entry_delete_by_mac(u8 *mac)
 {
 	struct foe_entry *entry = NULL;
@@ -265,6 +367,70 @@
 	return ret;
 }
 
+int entry_delete_by_ip(bool is_ipv4, void *addr)
+{
+	struct foe_entry *entry = NULL;
+	int index, i, ret = 0;
+
+	for (i = 0; i < CFG_PPE_NUM; i++) {
+		entry = hnat_priv->foe_table_cpu[i];
+		for (index = 0; index < DEF_ETRY_NUM; entry++, index++) {
+			if (entry->bfib1.state == BIND && entry_ip_cmp(entry, is_ipv4, addr)) {
+				memset(entry, 0, sizeof(*entry));
+				hnat_cache_ebl(1);
+				if (debug_level >= 2)
+					pr_info("delete entry idx = %d\n", index);
+				ret++;
+			}
+		}
+	}
+
+	if (!ret && debug_level >= 2)
+		pr_info("entry not found\n");
+
+	return ret;
+}
+
+static int entry_delete_by_bssid_wcid(u32 wdma_idx, u16 bssid, u16 wcid)
+{
+	struct foe_entry *entry = NULL;
+	int index, i;
+	int ret = 0;
+	int port;
+
+	port = mtk_get_wdma_rx_port(wdma_idx);
+
+	if (port < 0)
+		return -EINVAL;
+
+	for (i = 0; i < CFG_PPE_NUM; i++) {
+		entry = hnat_priv->foe_table_cpu[i];
+		for (index = 0; index < DEF_ETRY_NUM; entry++, index++) {
+			if (entry->bfib1.state != BIND)
+				continue;
+
+			if (IS_IPV4_GRP(entry)) {
+				if (entry->ipv4_hnapt.winfo.bssid != bssid ||
+				    entry->ipv4_hnapt.winfo.wcid != wcid ||
+				    entry->ipv4_hnapt.iblk2.dp != port)
+					continue;
+			} else {
+				if (entry->ipv6_5t_route.winfo.bssid != bssid ||
+				    entry->ipv6_5t_route.winfo.wcid != wcid ||
+				    entry->ipv6_5t_route.iblk2.dp != port)
+					continue;
+			}
+
+			memset(entry, 0, sizeof(*entry));
+			hnat_cache_ebl(1);
+			if (debug_level >= 2)
+				pr_info("delete entry idx = %d\n", index);
+			ret++;
+		}
+	}
+	return ret;
+}
+
 static void hnat_roam_handler(struct work_struct *work)
 {
 	struct kvec iov;
@@ -442,6 +608,8 @@
 	if (hnat_priv->data->version == MTK_HNAT_V3) {
 		cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_SB_FIFO_DBG,
 			     SB_MED_FULL_DRP_EN, 1);
+		cr_set_bits(hnat_priv->ppe_base[ppe_id] + PPE_GLO_CFG,
+			    NEW_IPV4_ID_INC_EN | TSID_EN);
 	}
 
 	/*enable ppe mib counter*/
@@ -668,6 +836,8 @@
 		return -1;
 
 	ppe_del_entry_by_mac = entry_delete_by_mac;
+	ppe_del_entry_by_ip = entry_delete_by_ip;
+	ppe_del_entry_by_bssid_wcid = entry_delete_by_bssid_wcid;
 	hook_toggle = 1;
 
 	return 0;
@@ -705,6 +875,8 @@
 
 	mod_timer(&hnat_priv->hnat_sma_build_entry_timer, jiffies + 3 * HZ);
 	ppe_del_entry_by_mac = NULL;
+	ppe_del_entry_by_ip = NULL;
+	ppe_del_entry_by_bssid_wcid = NULL;
 	hook_toggle = 0;
 
 	return 0;
@@ -713,6 +885,7 @@
 int hnat_warm_init(void)
 {
 	u32 foe_table_sz, foe_mib_tb_sz, ppe_id = 0;
+	int i;
 
 	unregister_netevent_notifier(&nf_hnat_netevent_nb);
 
@@ -738,6 +911,14 @@
 		hnat_hw_init(ppe_id);
 	}
 
+	/* The SER will enable all the PPE ports again,
+	 * so we have to manually disable the unused ports one more.
+	 */
+	for (i = 0; i < MAX_PPE_NUM; i++) {
+		if (i >= CFG_PPE_NUM)
+			mtk_set_ppe_pse_port_state(i, false);
+	}
+
 	set_gmac_ppe_fwd(NR_GMAC1_PORT, 1);
 	set_gmac_ppe_fwd(NR_GMAC2_PORT, 1);
 	set_gmac_ppe_fwd(NR_GMAC3_PORT, 1);
@@ -765,14 +946,18 @@
 	const struct of_device_id *match;
 
 	hnat_priv = devm_kzalloc(&pdev->dev, sizeof(struct mtk_hnat), GFP_KERNEL);
-	if (!hnat_priv)
-		return -ENOMEM;
+	if (!hnat_priv) {
+		err = -ENOMEM;
+		goto err_out2;
+	}
 
 	hnat_priv->foe_etry_num = DEF_ETRY_NUM;
 
 	match = of_match_device(of_hnat_match, &pdev->dev);
-	if (unlikely(!match))
-		return -EINVAL;
+	if (unlikely(!match)) {
+		err = -EINVAL;
+		goto err_out2;
+	}
 
 	hnat_priv->data = (struct mtk_hnat_data *)match->data;
 
@@ -780,8 +965,10 @@
 	np = hnat_priv->dev->of_node;
 
 	err = of_property_read_string(np, "mtketh-wan", &name);
-	if (err < 0)
-		return -EINVAL;
+	if (err < 0) {
+		err = -EINVAL;
+		goto err_out2;
+	}
 
 	strncpy(hnat_priv->wan, (char *)name, IFNAMSIZ - 1);
 	dev_info(&pdev->dev, "wan = %s\n", hnat_priv->wan);
@@ -810,8 +997,10 @@
 	/*get total gmac num in hnat*/
 	err = of_property_read_u32_index(np, "mtketh-max-gmac", 0, &val);
 
-	if (err < 0)
-		return -EINVAL;
+	if (err < 0) {
+		err = -EINVAL;
+		goto err_out2;
+	}
 
 	hnat_priv->gmac_num = val;
 
@@ -836,13 +1025,17 @@
 	dev_info(&pdev->dev, "ppe num = %d\n", hnat_priv->ppe_num);
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res)
-		return -ENOENT;
+	if (!res) {
+		err = -ENOENT;
+		goto err_out2;
+	}
 
 	hnat_priv->fe_base = devm_ioremap_nocache(&pdev->dev, res->start,
 					     res->end - res->start + 1);
-	if (!hnat_priv->fe_base)
-		return -EADDRNOTAVAIL;
+	if (!hnat_priv->fe_base) {
+		err = -EADDRNOTAVAIL;
+		goto err_out2;
+	}
 
 #if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3)
 	hnat_priv->ppe_base[0] = hnat_priv->fe_base + 0x2200;
@@ -858,7 +1051,7 @@
 
 	err = hnat_init_debugfs(hnat_priv);
 	if (err)
-		return err;
+		goto err_out2;
 
 	prop = of_find_property(np, "ext-devices", NULL);
 	for (name = of_prop_next_string(prop, NULL); name;
@@ -886,6 +1079,16 @@
 			goto err_out;
 	}
 
+	/* The PSE default enables all the PPE ports,
+	 * so we need to manually disable the unused ports.
+	 */
+	for (i = 0; i < MAX_PPE_NUM; i++) {
+		if (i >= CFG_PPE_NUM)
+			mtk_set_ppe_pse_port_state(i, false);
+		else
+			mtk_set_ppe_pse_port_state(i, true);
+	}
+
 	if (hnat_priv->data->whnat) {
 		err = whnat_adjust_nf_hooks();
 		if (err)
@@ -932,6 +1135,9 @@
 		ext_if_del(ext_entry);
 		kfree(ext_entry);
 	}
+err_out2:
+	for (i = 0; i < MAX_PPE_NUM; i++)
+		mtk_set_ppe_pse_port_state(i, false);
 	return err;
 }
 
@@ -950,6 +1156,9 @@
 	for (i = 0; i < CFG_PPE_NUM; i++)
 		hnat_stop(i);
 
+	for (i = 0; i < MAX_PPE_NUM; i++)
+		mtk_set_ppe_pse_port_state(i, false);
+
 	hnat_deinit_debugfs(hnat_priv);
 	hnat_release_netdev();
 	del_timer_sync(&hnat_priv->hnat_sma_build_entry_timer);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
index f7ca61c..c59e97e 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
@@ -17,6 +17,7 @@
 #include <linux/string.h>
 #include <linux/if.h>
 #include <linux/if_ether.h>
+#include <net/dsa.h>
 #include <net/netevent.h>
 #include <net/netfilter/nf_conntrack_zones.h>
 #include <linux/mod_devicetable.h>
@@ -192,6 +193,7 @@
 #define TTL0_DRP (0x1 << 4) /* RW */
 #define MCAST_TB_EN (0x1 << 7) /* RW */
 #define MCAST_HASH (0x3 << 12) /* RW */
+#define NEW_IPV4_ID_INC_EN (0x1 << 20) /* RW */
 #define SP_CMP_EN (0x1 << 25) /* RW */
 
 #define MC_P3_PPSE (0xf << 12) /* RW */
@@ -1286,30 +1288,19 @@
 		hnat_set_entry_lock(entry, false);
 }
 
-#if defined(CONFIG_NET_DSA_MT7530)
-u32 hnat_dsa_fill_stag(const struct net_device *netdev,
+int hnat_dsa_fill_stag(const struct net_device *netdev,
 		       struct foe_entry *entry,
 		       struct flow_offload_hw_path *hw_path,
 		       u16 eth_proto, int mape);
-
+int hnat_dsa_get_port(struct net_device **dev);
 static inline bool hnat_dsa_is_enable(struct mtk_hnat *priv)
 {
+#if defined(CONFIG_NET_DSA)
 	return (priv->wan_dsa_port != NONE_DSA_PORT);
-}
 #else
-static inline u32 hnat_dsa_fill_stag(const struct net_device *netdev,
-				     struct foe_entry *entry,
-				     struct flow_offload_hw_path *hw_path,
-				     u16 eth_proto, int mape)
-{
-	return 0;
-}
-
-static inline bool hnat_dsa_is_enable(struct mtk_hnat *priv)
-{
 	return false;
-}
 #endif
+}
 
 void hnat_deinit_debugfs(struct mtk_hnat *h);
 int hnat_init_debugfs(struct mtk_hnat *h);
@@ -1350,6 +1341,7 @@
 void set_gmac_ppe_fwd(int gmac_no, int enable);
 int entry_detail(u32 ppe_id, int index);
 int entry_delete_by_mac(u8 *mac);
+int entry_delete_by_ip(bool is_ipv4, void *addr);
 int entry_delete(u32 ppe_id, int index);
 int hnat_warm_init(void);
 u32 hnat_get_ppe_hash(struct foe_entry *entry);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
index 3e0d540..53f4d10 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
@@ -324,6 +324,9 @@
 	pr_info("              6   <entry_idx>  Show PPE2 specific foe entry info. of assigned <entry_idx>\n");
 	pr_info("              7   <entry_idx>  Delete PPE2 specific foe entry of assigned <entry_idx>\n");
 	pr_info("                               When entry_idx is -1, clear all entries\n");
+	pr_info("              8   <mac>        Delete all PPEs foe entry of assinged smac and dmac\n");
+	pr_info("              9   <ip>         Delete all PPEs foe entry of assinged IP(DL and UL)\n");
+	pr_info("              10  <ipV6>       Delete all PPEs foe entry of assinged IPv6(DL and UL)\n");
 
 	return 0;
 }
@@ -743,6 +746,66 @@
 }
 EXPORT_SYMBOL(entry_delete);
 
+static u32 hnat_char2hex(const char c)
+{
+	switch (c) {
+	case '0'...'9':
+		return 0x0 + (c - '0');
+	case 'a'...'f':
+		return 0xa + (c - 'a');
+	case 'A'...'F':
+		return 0xa + (c - 'A');
+	default:
+		pr_info("MAC format error\n");
+		return 0;
+	}
+}
+
+static void hnat_parse_mac(char *str, char *mac)
+{
+	int i;
+
+	for (i = 0; i < ETH_ALEN; i++) {
+		mac[i] = (hnat_char2hex(str[i * 3]) << 4) +
+			 (hnat_char2hex(str[i * 3 + 1]));
+	}
+}
+
+int delete_entry_by_mac(char *mac_str)
+{
+	char mac[6];
+
+	hnat_parse_mac(mac_str, mac);
+	entry_delete_by_mac(mac);
+
+	return 0;
+}
+
+int delete_entry_by_ip(bool is_ipv4, char *str)
+{
+	struct in6_addr ipv6;
+	void *addr = NULL;
+	u32 ipv4;
+
+	if (is_ipv4) {
+		if (!in4_pton(str, -1, (u8 *)&ipv4, -1, NULL)) {
+			pr_info("Invalid IPv4 address.\n");
+			return -EINVAL;
+		}
+		addr = (void *)(&ipv4);
+	} else {
+		if (!in6_pton(str, -1, (u8 *)&ipv6, -1, NULL)) {
+			pr_info("Invalid IPv6 address.\n");
+			return -EINVAL;
+		}
+		addr = (void *)(&ipv6);
+	}
+
+	entry_delete_by_ip(is_ipv4, addr);
+
+	return 0;
+}
+
 int cr_set_usage(int level)
 {
 	debug_level = level;
@@ -1893,6 +1956,7 @@
 	char *p_token = NULL;
 	char *p_delimiter = " \t";
 	int ret;
+	bool is_ipv4 = true;
 
 	if (len >= sizeof(buf)) {
 		pr_info("input handling fail!\n");
@@ -1925,16 +1989,32 @@
 			arg1 = 0;
 		else
 			ret = kstrtol(p_token, 10, &arg1);
+
+		(*entry_set_func[arg0])(arg1);
+		break;
+	case 8:
+		p_token = strsep(&p_buf, p_delimiter);
+		if (!p_token)
+			break;
+
+		delete_entry_by_mac(p_token);
+		break;
+	case 9:
+	case 10:
+		p_token = strsep(&p_buf, p_delimiter);
+		if (!p_token)
+			break;
+
+		if (arg0 == 10)
+			is_ipv4 = false;
+		delete_entry_by_ip(is_ipv4, p_token);
 		break;
 	default:
 		pr_info("no handler defined for command id(0x%08lx)\n\r", arg0);
-		arg0 = 0;
-		arg1 = 0;
+		entry_set_usage(debug_level);
 		break;
 	}
 
-	(*entry_set_func[arg0])(arg1);
-
 	return len;
 }
 
@@ -3049,31 +3129,6 @@
 	return hash;
 }
 
-static u32 hnat_char2hex(const char c)
-{
-	switch (c) {
-	case '0'...'9':
-		return 0x0 + (c - '0');
-	case 'a'...'f':
-		return 0xa + (c - 'a');
-	case 'A'...'F':
-		return 0xa + (c - 'A');
-	default:
-		pr_info("MAC format error\n");
-		return 0;
-	}
-}
-
-static void hnat_parse_mac(char *str, char *mac)
-{
-	int i;
-
-	for (i = 0; i < ETH_ALEN; i++) {
-		mac[i] = (hnat_char2hex(str[i * 3]) << 4) +
-			 (hnat_char2hex(str[i * 3 + 1]));
-	}
-}
-
 static void hnat_static_entry_help(void)
 {
 	pr_info("-------------------- Usage --------------------\n");
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
index cbebcd4..6960462 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
@@ -843,7 +843,7 @@
 mtk_hnat_ipv4_nf_pre_routing(void *priv, struct sk_buff *skb,
 			     const struct nf_hook_state *state)
 {
-	struct flow_offload_hw_path hw_path;
+	struct flow_offload_hw_path hw_path = { 0 };
 
 	if (!skb)
 		goto drop;
@@ -998,7 +998,7 @@
 	return NF_DROP;
 }
 
-static unsigned int hnat_ipv6_get_nexthop(struct sk_buff *skb,
+static int hnat_ipv6_get_nexthop(struct sk_buff *skb,
 					  const struct net_device *out,
 					  struct flow_offload_hw_path *hw_path)
 {
@@ -1056,7 +1056,7 @@
 	return 0;
 }
 
-static unsigned int hnat_ipv4_get_nexthop(struct sk_buff *skb,
+static int hnat_ipv4_get_nexthop(struct sk_buff *skb,
 					  const struct net_device *out,
 					  struct flow_offload_hw_path *hw_path)
 {
@@ -1065,10 +1065,13 @@
 	struct dst_entry *dst = skb_dst(skb);
 	struct rtable *rt = (struct rtable *)dst;
 	struct net_device *dev = (__force struct net_device *)out;
+	struct ethhdr *eth;
 
 	if (hw_path->flags & FLOW_OFFLOAD_PATH_PPPOE) {
+		rcu_read_lock_bh();
 		memcpy(eth_hdr(skb)->h_source, hw_path->eth_src, ETH_ALEN);
 		memcpy(eth_hdr(skb)->h_dest, hw_path->eth_dest, ETH_ALEN);
+		rcu_read_unlock_bh();
 		return 0;
 	}
 
@@ -1088,8 +1091,15 @@
 		return -1;
 	}
 
+	if (ip_hdr(skb)->protocol == IPPROTO_IPV6)
+		/* 6RD LAN->WAN(6to4) */
+		eth = (struct ethhdr *)(skb->data - ETH_HLEN);
+	else
+		eth = eth_hdr(skb);
+
-	memcpy(eth_hdr(skb)->h_dest, neigh->ha, ETH_ALEN);
-	memcpy(eth_hdr(skb)->h_source, out->dev_addr, ETH_ALEN);
+	memcpy(eth->h_dest, neigh->ha, ETH_ALEN);
+	memcpy(eth->h_source, out->dev_addr, ETH_ALEN);
+	eth->h_proto = htons(ETH_P_IP);
 
 	rcu_read_unlock_bh();
 
@@ -1224,8 +1234,10 @@
 				     struct foe_entry *foe,
 				     struct flow_offload_hw_path *hw_path)
 {
+	struct net_device *master_dev = (struct net_device *)dev;
 	struct foe_entry entry = { 0 };
 	int whnat = IS_WHNAT(dev);
+	struct mtk_mac *mac;
 	struct ethhdr *eth;
 	struct iphdr *iph;
 	struct ipv6hdr *ip6h;
@@ -1235,15 +1247,17 @@
 	enum ip_conntrack_info ctinfo;
 	int gmac = NR_DISCARD;
 	int udp = 0;
+	int port_id = 0;
 	u32 qid = 0;
-	u32 port_id = 0;
 	u32 payload_len = 0;
 	int mape = 0;
-	struct mtk_mac *mac = netdev_priv(dev);
 
 	if (ipv6_hdr(skb)->nexthdr == NEXTHDR_IPIP)
 		/* point to ethernet header for DS-Lite and MapE */
 		eth = get_ipv6_ipip_ethhdr(skb, hw_path);
+	else if (ip_hdr(skb)->protocol == IPPROTO_IPV6)
+		/* 6RD LAN->WAN(6to4) */
+		eth = (struct ethhdr *)(skb->data - ETH_HLEN);
 	else
 		eth = eth_hdr(skb);
 
@@ -1381,6 +1395,44 @@
 			entry.ipv4_hnapt.bfib1.udp = udp;
 			break;
 
+		case IPPROTO_IPV6:
+			/* 6RD LAN->WAN(6to4) */
+			if (entry.bfib1.pkt_type == IPV6_6RD) {
+				entry.ipv6_6rd.ipv6_sip0 = foe->ipv6_6rd.ipv6_sip0;
+				entry.ipv6_6rd.ipv6_sip1 = foe->ipv6_6rd.ipv6_sip1;
+				entry.ipv6_6rd.ipv6_sip2 = foe->ipv6_6rd.ipv6_sip2;
+				entry.ipv6_6rd.ipv6_sip3 = foe->ipv6_6rd.ipv6_sip3;
+
+				entry.ipv6_6rd.ipv6_dip0 = foe->ipv6_6rd.ipv6_dip0;
+				entry.ipv6_6rd.ipv6_dip1 = foe->ipv6_6rd.ipv6_dip1;
+				entry.ipv6_6rd.ipv6_dip2 = foe->ipv6_6rd.ipv6_dip2;
+				entry.ipv6_6rd.ipv6_dip3 = foe->ipv6_6rd.ipv6_dip3;
+
+				entry.ipv6_6rd.sport = foe->ipv6_6rd.sport;
+				entry.ipv6_6rd.dport = foe->ipv6_6rd.dport;
+				entry.ipv6_6rd.tunnel_sipv4 = ntohl(iph->saddr);
+				entry.ipv6_6rd.tunnel_dipv4 = ntohl(iph->daddr);
+				entry.ipv6_6rd.hdr_chksum = ppe_get_chkbase(iph);
+				entry.ipv6_6rd.flag = (ntohs(iph->frag_off) >> 13);
+				entry.ipv6_6rd.ttl = iph->ttl;
+				entry.ipv6_6rd.dscp = iph->tos;
+				entry.ipv6_6rd.per_flow_6rd_id = 1;
+				entry.ipv6_6rd.vlan1 = hw_path->vlan_id;
+				if (hnat_priv->data->per_flow_accounting)
+					entry.ipv6_6rd.iblk2.mibf = 1;
+
+				ip6h = (struct ipv6hdr *)skb_inner_network_header(skb);
+				if (ip6h->nexthdr == NEXTHDR_UDP)
+					entry.ipv6_6rd.bfib1.udp = 1;
+
+#if defined(CONFIG_MEDIATEK_NETSYS_V3)
+				entry.ipv6_6rd.eg_keep_ecn = 1;
+				entry.ipv6_6rd.eg_keep_cls = 1;
+#endif
+				break;
+			}
+
+			return -1;
 		default:
 			return -1;
 		}
@@ -1640,41 +1692,7 @@
 		break;
 
 	default:
-		iph = ip_hdr(skb);
-		switch (entry.bfib1.pkt_type) {
-		case IPV6_6RD: /* 6RD LAN->WAN */
-			entry.ipv6_6rd.ipv6_sip0 = foe->ipv6_6rd.ipv6_sip0;
-			entry.ipv6_6rd.ipv6_sip1 = foe->ipv6_6rd.ipv6_sip1;
-			entry.ipv6_6rd.ipv6_sip2 = foe->ipv6_6rd.ipv6_sip2;
-			entry.ipv6_6rd.ipv6_sip3 = foe->ipv6_6rd.ipv6_sip3;
-
-			entry.ipv6_6rd.ipv6_dip0 = foe->ipv6_6rd.ipv6_dip0;
-			entry.ipv6_6rd.ipv6_dip1 = foe->ipv6_6rd.ipv6_dip1;
-			entry.ipv6_6rd.ipv6_dip2 = foe->ipv6_6rd.ipv6_dip2;
-			entry.ipv6_6rd.ipv6_dip3 = foe->ipv6_6rd.ipv6_dip3;
-
-			entry.ipv6_6rd.sport = foe->ipv6_6rd.sport;
-			entry.ipv6_6rd.dport = foe->ipv6_6rd.dport;
-			entry.ipv6_6rd.tunnel_sipv4 = ntohl(iph->saddr);
-			entry.ipv6_6rd.tunnel_dipv4 = ntohl(iph->daddr);
-			entry.ipv6_6rd.hdr_chksum = ppe_get_chkbase(iph);
-			entry.ipv6_6rd.flag = (ntohs(iph->frag_off) >> 13);
-			entry.ipv6_6rd.ttl = iph->ttl;
-			entry.ipv6_6rd.dscp = iph->tos;
-			entry.ipv6_6rd.per_flow_6rd_id = 1;
-			entry.ipv6_6rd.vlan1 = hw_path->vlan_id;
-			if (hnat_priv->data->per_flow_accounting)
-				entry.ipv6_6rd.iblk2.mibf = 1;
-
-#if defined(CONFIG_MEDIATEK_NETSYS_V3)
-			entry.ipv6_6rd.eg_keep_ecn = 1;
-			entry.ipv6_6rd.eg_keep_cls = 1;
-#endif
-			break;
-
-		default:
-			return -1;
-		}
+		return -1;
 	}
 
 	/* Fill Layer2 Info.*/
@@ -1683,33 +1701,25 @@
 	/* Fill Info Blk*/
 	entry = ppe_fill_info_blk(eth, entry, hw_path);
 
-	if (IS_LAN(dev)) {
-		if (IS_BOND_MODE) {
-			gmac = ((skb_hnat_entry(skb) >> 1) % hnat_priv->gmac_num) ?
-				 NR_GMAC2_PORT : NR_GMAC1_PORT;
-		} else if (IS_DSA_LAN(dev)) {
-			port_id = hnat_dsa_fill_stag(dev, &entry, hw_path,
-						     ntohs(eth->h_proto),
-						     mape);
-			gmac = NR_GMAC1_PORT;
-		} else {
-			gmac = HNAT_GMAC_FP(mac->id);
+	if (IS_LAN_GRP(dev) || IS_WAN(dev)) { /* Forward to GMAC Ports */
+		port_id = hnat_dsa_get_port(&master_dev);
+		if (port_id >= 0) {
+			if (hnat_dsa_fill_stag(dev, &entry, hw_path,
+					       ntohs(eth->h_proto), mape) < 0)
+				return 0;
 		}
-	} else if (IS_LAN2(dev)) {
+
+		mac = netdev_priv(master_dev);
 		gmac = HNAT_GMAC_FP(mac->id);
-	} else if (IS_WAN(dev)) {
-		if (mape_toggle && mape == 1) {
+
+		if (IS_LAN(dev) && IS_BOND_MODE) {
+			gmac = ((skb_hnat_entry(skb) >> 1) % hnat_priv->gmac_num) ?
+				 NR_GMAC2_PORT : NR_GMAC1_PORT;
+		} else if (IS_WAN(dev) && mape_toggle && mape == 1) {
 			gmac = NR_PDMA_PORT;
 			/* Set act_dp = wan_dev */
 			entry.ipv4_hnapt.act_dp &= ~UDF_PINGPONG_IFIDX;
 			entry.ipv4_hnapt.act_dp |= dev->ifindex & UDF_PINGPONG_IFIDX;
-		} else if (IS_DSA_WAN(dev)) {
-			port_id = hnat_dsa_fill_stag(dev, &entry, hw_path,
-						     ntohs(eth->h_proto),
-						     mape);
-			gmac = NR_GMAC1_PORT;
-		} else {
-			gmac = HNAT_GMAC_FP(mac->id);
 		}
 	} else if (IS_EXT(dev) && (FROM_GE_PPD(skb) || FROM_GE_LAN_GRP(skb) ||
 		   FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb) || FROM_WED(skb))) {
@@ -1931,7 +1941,7 @@
 		/* We must ensure all info has been updated before set to hw */
 		wmb();
 		/* After other fields have been written, write info1 to BIND the entry */
-		foe->bfib1 = entry.bfib1;
+		memcpy(&foe->bfib1, &entry.bfib1, sizeof(entry.bfib1));
 
 		/* reset statistic for this entry */
 		if (hnat_priv->data->per_flow_accounting &&
@@ -2240,7 +2250,7 @@
 	/* We must ensure all info has been updated before set to hw */
 	wmb();
 	/* After other fields have been written, write info1 to BIND the entry */
-	hw_entry->bfib1 = entry.bfib1;
+	memcpy(&hw_entry->bfib1, &entry.bfib1, sizeof(entry.bfib1));
 
 	hnat_set_entry_lock(hw_entry, false);
 
@@ -2748,8 +2758,8 @@
 
 static unsigned int mtk_hnat_nf_post_routing(
 	struct sk_buff *skb, const struct net_device *out,
-	unsigned int (*fn)(struct sk_buff *, const struct net_device *,
-			   struct flow_offload_hw_path *),
+	int (*fn)(struct sk_buff *, const struct net_device *,
+		  struct flow_offload_hw_path *),
 	const char *func)
 {
 	struct foe_entry *entry;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
index 75c3a75..d8e5709 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
@@ -6,58 +6,77 @@
 
 #include <linux/of_device.h>
 #include <net/netfilter/nf_flow_table.h>
+
 #include "hnat.h"
 
+int hnat_dsa_get_port(struct net_device **dev)
+{
+#if defined(CONFIG_NET_DSA)
+	struct dsa_port *dp;
+
+	dp = dsa_port_from_netdev(*dev);
+	if (IS_ERR(dp))
+		return -ENODEV;
+
+	*dev = dp->cpu_dp->master;
+
+	return dp->index;
+#else
+	return -ENODEV;
+#endif
+}
+
-u32 hnat_dsa_fill_stag(const struct net_device *netdev,
+int hnat_dsa_fill_stag(const struct net_device *netdev,
 		       struct foe_entry *entry,
 		       struct flow_offload_hw_path *hw_path,
 		       u16 eth_proto,
 		       int mape)
 {
-	const struct net_device *ndev;
+#if defined(CONFIG_NET_DSA)
 	const unsigned int *port_reg;
+	const struct dsa_port *dp;
+	struct net_device *ndev;
 	int port_index;
-	u16 sp_tag;
+	u16 dsa_tag;
 
 	if (hw_path->flags & FLOW_OFFLOAD_PATH_VLAN)
 		ndev = hw_path->dev;
 	else
-		ndev = netdev;
+		ndev = (struct net_device *)netdev;
 
 	port_reg = of_get_property(ndev->dev.of_node, "reg", NULL);
 	if (unlikely(!port_reg))
 		return -EINVAL;
 
 	port_index = be32_to_cpup(port_reg);
-	sp_tag = BIT(port_index);
+
+	/* In the case MAPE LAN --> WAN, binding entry is to CPU.
+	 * Do not add special tag.
+	 */
+	if (IS_WAN(ndev) && mape)
+		return port_index;
+
+	dp = dsa_port_from_netdev(ndev);
+	if (IS_ERR(dp))
+		return -ENODEV;
+
+	dsa_tag = BIT(port_index);
 
 	if (!entry->bfib1.vlan_layer)
 		entry->bfib1.vlan_layer = 1;
 	else
 		/* VLAN existence indicator */
-		sp_tag |= BIT(8);
-	entry->bfib1.vpm = 0;
+		dsa_tag |= BIT(8);
 
-	switch (eth_proto) {
-	case ETH_P_IP:
-		if (entry->ipv4_hnapt.bfib1.pkt_type == IPV4_DSLITE
-			|| (entry->ipv4_hnapt.bfib1.pkt_type == IPV4_MAP_E))
-			entry->ipv4_dslite.etype = sp_tag;
-		else
-			entry->ipv4_hnapt.etype = sp_tag;
-		break;
-	case ETH_P_IPV6:
-		/* In the case MAPE LAN --> WAN, binding entry is to CPU.
-		 * Do not add special tag.
-		 */
-		if (!mape)
-			/* etype offset of ipv6 entries are the same. */
-			entry->ipv6_5t_route.etype = sp_tag;
+	if (IS_IPV4_GRP(entry))
+		entry->ipv4_hnapt.etype = dsa_tag;
+	else
+		entry->ipv6_5t_route.etype = dsa_tag;
 
-		break;
-	default:
-		pr_info("DSA + HNAT unsupport protocol\n");
-	}
+	entry->bfib1.vpm = 0;
 
 	return port_index;
+#else
+	return -EINVAL;
+#endif
 }
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c
index 8ae7608..51a9b1f 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c
@@ -77,18 +77,70 @@
 {
 	unsigned int val;
 
+	mutex_lock(&mpcs->reset_lock);
+
 	regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
 
+	mutex_unlock(&mpcs->reset_lock);
+
 	return FIELD_GET(SGMII_LINK_STATYS, val);
 }
 
+static void mtk_sgmii_get_state(struct mtk_sgmii_pcs *mpcs)
+{
+	struct phylink_link_state *state = &mpcs->state;
+	unsigned int bm, adv, rgc3, sgm_mode;
+
+	mutex_lock(&mpcs->reset_lock);
+
+	state->interface = mpcs->interface;
+
+	regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm);
+	if (bm & SGMII_AN_ENABLE) {
+		regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
+
+		phylink_mii_c22_pcs_decode_state(state,
+						 FIELD_GET(SGMII_BMSR, bm),
+						 FIELD_GET(SGMII_LPA, adv));
+	} else {
+		state->link = !!(bm & SGMII_LINK_STATYS);
+
+		regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &sgm_mode);
+
+		switch (sgm_mode & SGMII_SPEED_MASK) {
+		case SGMII_SPEED_10:
+			state->speed = SPEED_10;
+			break;
+		case SGMII_SPEED_100:
+			state->speed = SPEED_100;
+			break;
+		case SGMII_SPEED_1000:
+			regmap_read(mpcs->regmap, mpcs->ana_rgc3, &rgc3);
+			rgc3 = FIELD_GET(RG_PHY_SPEED_3_125G, rgc3);
+			state->speed = rgc3 ? SPEED_2500 : SPEED_1000;
+			break;
+		}
+
+		if (sgm_mode & SGMII_DUPLEX_HALF)
+			state->duplex = DUPLEX_HALF;
+		else
+			state->duplex = DUPLEX_FULL;
+	}
+
+	mutex_unlock(&mpcs->reset_lock);
+}
+
-void mtk_sgmii_reset(struct mtk_eth *eth, int id)
+void mtk_sgmii_reset(struct mtk_sgmii_pcs *mpcs)
 {
+	struct mtk_eth *eth = mpcs->eth;
+	int id = mpcs->id;
 	u32 val = 0;
 
-	if (!eth->toprgu)
+	if (id >= MTK_MAX_DEVS || !eth->toprgu)
 		return;
 
+	mutex_lock(&mpcs->reset_lock);
+
 	switch (id) {
 	case 0:
 		/* Enable software reset */
@@ -104,7 +156,7 @@
 		       SWSYSRST_SGMII0_GRST;
 		regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
 
-		udelay(100);
+		usleep_range(100, 500);
 
 		/* De-assert SGMII reset */
 		regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
@@ -133,7 +185,7 @@
 		       SWSYSRST_SGMII1_GRST;
 		regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
 
-		udelay(100);
+		usleep_range(100, 500);
 
 		/* De-assert SGMII reset */
 		regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
@@ -150,7 +202,9 @@
 		break;
 	}
 
+	mutex_unlock(&mpcs->reset_lock);
+
-	mdelay(1);
+	usleep_range(10000, 11000);
 }
 
 void mtk_sgmii_setup_phya_gen1(struct mtk_sgmii_pcs *mpcs)
@@ -210,7 +264,7 @@
 	/* Force AEQ on */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x02002800);
-	ndelay(1020);
+	usleep_range(1, 5);
 	regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
 			   0x20000000);
 	/* Setup DA default value */
@@ -241,28 +295,28 @@
 	/* Release reset */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0200E800);
-	udelay(150);
+	usleep_range(150, 500);
 	/* Switch to P0 */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0200C111);
-	ndelay(1020);
+	usleep_range(1, 5);
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0200C101);
-	udelay(15);
+	usleep_range(15, 50);
 	/* Switch to Gen2 */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0201C111);
-	ndelay(1020);
+	usleep_range(1, 5);
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0201C101);
-	udelay(100);
+	usleep_range(100, 500);
 	regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
 			   0x00000030);
 	regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
 			   0x80201F01);
 	regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
 			   0x30000000);
-	udelay(400);
+	usleep_range(400, 1000);
 }
 
 void mtk_sgmii_setup_phya_gen2(struct mtk_sgmii_pcs *mpcs)
@@ -322,7 +376,7 @@
 	/* Force AEQ on */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x02002800);
-	ndelay(1020);
+	usleep_range(1, 5);
 	regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
 			   0x20000000);
 	/* Setup DA default value */
@@ -351,28 +405,28 @@
 	/* Release reset */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0200E800);
-	udelay(150);
+	usleep_range(150, 500);
 	/* Switch to P0 */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0200C111);
-	ndelay(1020);
+	usleep_range(1, 5);
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0200C101);
-	udelay(15);
+	usleep_range(15, 50);
 	/* Switch to Gen2 */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0201C111);
-	ndelay(1020);
+	usleep_range(1, 5);
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0201C101);
-	udelay(100);
+	usleep_range(100, 500);
 	regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
 			   0x00000030);
 	regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
 			   0x80201F01);
 	regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
 			   0x30000000);
-	udelay(400);
+	usleep_range(400, 1000);
 }
 
 static int mtk_sgmii_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
@@ -391,8 +445,6 @@
 	if (advertise < 0)
 		return advertise;
 
-	spin_lock(&mpcs->regmap_lock);
-
 	/* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and
 	 * we assume that fixes it's speed at bitrate = line rate (in
 	 * other words, 1000Mbps or 2500Mbps).
@@ -416,22 +468,22 @@
 			speed = SGMII_SPEED_1000;
 	}
 
-	if (mpcs->interface != interface) {
-		mpcs->interface = interface;
-		mpcs->mode = mode;
-		linkmode_copy(mpcs->advertising, advertising);
-		mode_changed = true;
-	}
-
 	link_timer = phylink_get_link_timer_ns(interface);
-	if (link_timer < 0) {
-		spin_unlock(&mpcs->regmap_lock);
+	if (link_timer < 0)
 		return link_timer;
-	}
 
 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
 		mtk_sgmii_xfi_pll_enable(eth->sgmii);
-		mtk_sgmii_reset(eth, mpcs->id);
+		mtk_sgmii_reset(mpcs);
+	}
+
+	mutex_lock(&mpcs->regmap_lock);
+
+	if (mode >= 0 && mpcs->interface != interface) {
+		mpcs->interface = interface;
+		mpcs->mode = mode;
+		linkmode_copy(mpcs->advertising, advertising);
+		mode_changed = true;
 	}
 
 	/* PHYA power down */
@@ -474,7 +526,7 @@
 			   SGMII_AN_ENABLE, bmcr);
 
 	/* Release PHYA power down state */
-	udelay(100);
+	usleep_range(50, 100);
 	regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
 
 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
@@ -484,32 +536,38 @@
 			mtk_sgmii_setup_phya_gen1(mpcs);
 	}
 
-	spin_unlock(&mpcs->regmap_lock);
+	mutex_unlock(&mpcs->regmap_lock);
 
 	return changed || mode_changed;
 }
 
-static void mtk_sgmii_pcs_link_poll(struct timer_list *t)
+static void mtk_sgmii_pcs_link_poll(struct work_struct *work)
 {
-	struct mtk_sgmii_pcs *mpcs = from_timer(mpcs, t, link_poll_outband);
+	struct mtk_sgmii_pcs *mpcs = container_of(work, struct mtk_sgmii_pcs,
+						  link_poll.work);
 
-	if (mpcs->interface == PHY_INTERFACE_MODE_NA)
+	if (mpcs->interface != PHY_INTERFACE_MODE_SGMII &&
+	    mpcs->interface != PHY_INTERFACE_MODE_1000BASEX &&
+	    mpcs->interface != PHY_INTERFACE_MODE_2500BASEX)
 		goto exit;
 
 	if (!mtk_sgmii_link_status(mpcs))
-		mtk_sgmii_pcs_config(&mpcs->pcs, mpcs->mode, mpcs->interface,
+		mtk_sgmii_pcs_config(&mpcs->pcs, -1, mpcs->interface,
 				     mpcs->advertising, false);
 
 exit:
-	if (mpcs->mode != MLO_AN_INBAND)
-		mod_timer(&mpcs->link_poll_outband, jiffies + HZ);
+	if (mpcs->mode == MLO_AN_INBAND)
+		mtk_sgmii_get_state(mpcs);
+	else if (!delayed_work_pending(&mpcs->link_poll))
+		schedule_delayed_work(&mpcs->link_poll, msecs_to_jiffies(1000));
 }
 
 static int mtk_sgmii_pcs_enable(struct phylink_pcs *pcs)
 {
 	struct mtk_sgmii_pcs *mpcs = pcs_to_mtk_sgmii_pcs(pcs);
 
-	mod_timer(&mpcs->link_poll_outband, jiffies + HZ);
+	if (!delayed_work_pending(&mpcs->link_poll))
+		schedule_delayed_work(&mpcs->link_poll, msecs_to_jiffies(1000));
 
 	return 0;
 }
@@ -518,58 +576,34 @@
 {
 	struct mtk_sgmii_pcs *mpcs = pcs_to_mtk_sgmii_pcs(pcs);
 
-	del_timer_sync(&mpcs->link_poll_outband);
+	cancel_delayed_work_sync(&mpcs->link_poll);
 
-	mpcs->interface = PHY_INTERFACE_MODE_NA;
+	if (mpcs->mode == MLO_AN_INBAND)
+		mtk_sgmii_get_state(mpcs);
 }
 
 static void mtk_sgmii_pcs_get_state(struct phylink_pcs *pcs,
 				    struct phylink_link_state *state)
 {
 	struct mtk_sgmii_pcs *mpcs = pcs_to_mtk_sgmii_pcs(pcs);
-	unsigned int bm, adv, rgc3, sgm_mode;
-
-	state->interface = mpcs->interface;
-
-	regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm);
-	if (bm & SGMII_AN_ENABLE) {
-		regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
-
-		phylink_mii_c22_pcs_decode_state(state,
-						 FIELD_GET(SGMII_BMSR, bm),
-						 FIELD_GET(SGMII_LPA, adv));
-	} else {
-		state->link = !!(bm & SGMII_LINK_STATYS);
-
-		regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &sgm_mode);
-
-		switch (sgm_mode & SGMII_SPEED_MASK) {
-		case SGMII_SPEED_10:
-			state->speed = SPEED_10;
-			break;
-		case SGMII_SPEED_100:
-			state->speed = SPEED_100;
-			break;
-		case SGMII_SPEED_1000:
-			regmap_read(mpcs->regmap, mpcs->ana_rgc3, &rgc3);
-			rgc3 = FIELD_GET(RG_PHY_SPEED_3_125G, rgc3);
-			state->speed = rgc3 ? SPEED_2500 : SPEED_1000;
-			break;
-		}
 
-		if (sgm_mode & SGMII_DUPLEX_HALF)
-			state->duplex = DUPLEX_HALF;
-		else
-			state->duplex = DUPLEX_FULL;
-	}
+	/* When the interface of the mpcs is not initialized,
+	 * we should avoid overriding the state interface.
+	 */
+	if (mpcs->state.interface != PHY_INTERFACE_MODE_NA)
+		state->interface = mpcs->state.interface;
+	state->speed = mpcs->state.speed;
+	state->duplex = mpcs->state.duplex;
+	state->link = mpcs->state.link;
 
 	/* Reconfiguring SGMII every second to ensure that PCS can
 	 * link up with the Link Partner when a module is inserted.
 	 */
-	if (state->link == 0 && time_after(jiffies, mpcs->link_poll_inband + HZ)) {
-		mpcs->link_poll_inband = jiffies;
-		mtk_sgmii_pcs_config(pcs, MLO_AN_INBAND,
-				     state->interface, mpcs->advertising, false);
+	if (time_after(jiffies, mpcs->link_poll_expire) &&
+	    !delayed_work_pending(&mpcs->link_poll)) {
+		mpcs->link_poll_expire = jiffies + HZ;
+		mpcs->state.an_enabled = state->an_enabled;
+		schedule_delayed_work(&mpcs->link_poll, 0);
 	}
 }
 
@@ -591,6 +625,7 @@
 				  int speed, int duplex)
 {
 	struct mtk_sgmii_pcs *mpcs = pcs_to_mtk_sgmii_pcs(pcs);
+	struct device *dev = mpcs->eth->dev;
 	unsigned int sgm_mode, val;
 	unsigned long t_start = jiffies;
 
@@ -602,7 +637,7 @@
 
 	} while (time_before(jiffies, t_start + msecs_to_jiffies(3000)));
 
-	pr_warn("%s wait link up timeout!\n", __func__);
+	dev_warn(dev, "sgmii%d: wait link up timeout!\n", mpcs->id);
 	return;
 
 exit:
@@ -669,9 +704,14 @@
 		ss->pcs[i].pcs.poll = true;
 		ss->pcs[i].interface = PHY_INTERFACE_MODE_NA;
 
-		timer_setup(&ss->pcs[i].link_poll_outband, mtk_sgmii_pcs_link_poll, 0);
+		ss->pcs[i].state.link = 0;
+		ss->pcs[i].state.duplex = DUPLEX_FULL;
+		ss->pcs[i].state.speed = SPEED_1000;
+
+		INIT_DELAYED_WORK(&ss->pcs[i].link_poll, mtk_sgmii_pcs_link_poll);
 
-		spin_lock_init(&ss->pcs[i].regmap_lock);
+		mutex_init(&ss->pcs[i].regmap_lock);
+		mutex_init(&ss->pcs[i].reset_lock);
 
 		of_node_put(np);
 	}
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c
index 16ae838..85aae88 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c
@@ -186,7 +186,7 @@
 	/* Force AEQ on */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x02002800);
-	ndelay(1020);
+	usleep_range(1, 5);
 	regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
 			   0x20000000);
 	/* Setup DA default value */
@@ -219,28 +219,28 @@
 	/* Release reset */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0200E800);
-	udelay(150);
+	usleep_range(150, 500);
 	/* Switch to P0 */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0200C111);
-	ndelay(1020);
+	usleep_range(1, 5);
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0200C101);
-	udelay(15);
+	usleep_range(15, 50);
 	/* Switch to Gen3 */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0202C111);
-	ndelay(1020);
+	usleep_range(1, 5);
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0202C101);
-	udelay(100);
+	usleep_range(100, 500);
 	regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
 			   0x00000030);
 	regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
 			   0x80201F00);
 	regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
 			   0x30000000);
-	udelay(400);
+	usleep_range(400, 1000);
 }
 
 void mtk_usxgmii_setup_phya_5gbaser(struct mtk_usxgmii_pcs *mpcs)
@@ -300,7 +300,7 @@
 	/* Force AEQ on */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x02002800);
-	ndelay(1020);
+	usleep_range(1, 5);
 	regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
 			   0x20000000);
 	/* Setup DA default value */
@@ -333,28 +333,28 @@
 	/* Release reset */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0200E800);
-	udelay(150);
+	usleep_range(150, 500);
 	/* Switch to P0 */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0200C111);
-	ndelay(1020);
+	usleep_range(1, 5);
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0200C101);
-	udelay(15);
+	usleep_range(15, 50);
 	/* Switch to Gen3 */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0202C111);
-	ndelay(1020);
+	usleep_range(1, 5);
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0202C101);
-	udelay(100);
+	usleep_range(100, 500);
 	regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
 			   0x00000030);
 	regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
 			   0x80201F00);
 	regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
 			   0x30000000);
-	udelay(400);
+	usleep_range(400, 1000);
 }
 
 void mtk_usxgmii_setup_phya_10gbaser(struct mtk_usxgmii_pcs *mpcs)
@@ -414,7 +414,7 @@
 	/* Force AEQ on */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x02002800);
-	ndelay(1020);
+	usleep_range(1, 5);
 	regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
 			   0x20000000);
 	/* Setup DA default value */
@@ -450,34 +450,36 @@
 	/* Release reset */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0200E800);
-	udelay(150);
+	usleep_range(150, 500);
 	/* Switch to P0 */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0200C111);
-	ndelay(1020);
+	usleep_range(1, 5);
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0200C101);
-	udelay(15);
+	usleep_range(15, 50);
 	/* Switch to Gen3 */
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0202C111);
-	ndelay(1020);
+	usleep_range(1, 5);
 	regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
 			   0x0202C101);
-	udelay(100);
+	usleep_range(100, 500);
 	regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
 			   0x00000030);
 	regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
 			   0x80201F00);
 	regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
 			   0x30000000);
-	udelay(400);
+	usleep_range(400, 1000);
 }
 
 int mtk_usxgmii_link_status(struct mtk_usxgmii_pcs *mpcs)
 {
 	unsigned int val;
 
+	mutex_lock(&mpcs->reset_lock);
+
 	/* Refresh USXGMII link status by toggling RG_PCS_RX_STATUS_UPDATE */
 	regmap_read(mpcs->regmap, RG_PCS_RX_STATUS0, &val);
 	val |= RG_PCS_RX_STATUS_UPDATE;
@@ -490,16 +492,113 @@
 	/* Read USXGMII link status */
 	regmap_read(mpcs->regmap, RG_PCS_RX_STATUS0, &val);
 
+	mutex_unlock(&mpcs->reset_lock);
+
 	return FIELD_GET(RG_PCS_RX_LINK_STATUS, val);
 }
 
+bool mtk_usxgmii_is_valid_ctle(struct mtk_usxgmii_pcs *mpcs)
+{
+	unsigned int val, ctle;
+
+	mutex_lock(&mpcs->reset_lock);
+
+	regmap_write(mpcs->regmap_pextp, 0x00, 0x00000404);
+	regmap_write(mpcs->regmap_pextp, 0x10, 0x00d600d5);
+	regmap_read(mpcs->regmap_pextp, 0xd0, &val);
+
+	mutex_unlock(&mpcs->reset_lock);
+
+	ctle = FIELD_GET(GENMASK(12, 8), val);
+	if (ctle > 10)
+		return false;
+
+	return true;
+}
+
+static void mtk_usxgmii_get_state(struct mtk_usxgmii_pcs *mpcs)
+{
+	struct phylink_link_state *state = &mpcs->state;
+	struct mtk_eth *eth = mpcs->eth;
+	struct mtk_mac *mac = eth->mac[mtk_xgmii2mac_id(eth, mpcs->id)];
+	u32 val = 0;
+
+	mutex_lock(&mpcs->reset_lock);
+
+	regmap_read(mpcs->regmap, RG_PCS_AN_CTRL0, &val);
+	if (FIELD_GET(USXGMII_AN_ENABLE, val)) {
+		/* Refresh LPA by inverting LPA_LATCH */
+		regmap_read(mpcs->regmap, RG_PCS_AN_STS0, &val);
+		regmap_update_bits(mpcs->regmap, RG_PCS_AN_STS0,
+				   USXGMII_LPA_LATCH,
+				   !(val & USXGMII_LPA_LATCH));
+
+		regmap_read(mpcs->regmap, RG_PCS_AN_STS0, &val);
+
+		state->interface = mpcs->interface;
+		state->link = FIELD_GET(USXGMII_LPA_LINK, val);
+		state->duplex = FIELD_GET(USXGMII_LPA_DUPLEX, val);
+
+		switch (FIELD_GET(USXGMII_LPA_SPEED_MASK, val)) {
+		case USXGMII_LPA_SPEED_10:
+			state->speed = SPEED_10;
+			break;
+		case USXGMII_LPA_SPEED_100:
+			state->speed = SPEED_100;
+			break;
+		case USXGMII_LPA_SPEED_1000:
+			state->speed = SPEED_1000;
+			break;
+		case USXGMII_LPA_SPEED_2500:
+			state->speed = SPEED_2500;
+			break;
+		case USXGMII_LPA_SPEED_5000:
+			state->speed = SPEED_5000;
+			break;
+		case USXGMII_LPA_SPEED_10000:
+			state->speed = SPEED_10000;
+			break;
+		}
+	} else {
+		val = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id));
+
+		if (mac->id == MTK_GMAC2_ID)
+			val = val >> 16;
+
+		switch (FIELD_GET(MTK_USXGMII_PCS_MODE, val)) {
+		case 0:
+			state->speed = SPEED_10000;
+			break;
+		case 1:
+			state->speed = SPEED_5000;
+			break;
+		case 2:
+			state->speed = SPEED_2500;
+			break;
+		case 3:
+			state->speed = SPEED_1000;
+			break;
+		}
+
+		state->interface = mpcs->interface;
+		state->link = FIELD_GET(MTK_USXGMII_PCS_LINK, val);
+		state->duplex = DUPLEX_FULL;
+	}
+
+	mutex_unlock(&mpcs->reset_lock);
+}
+
-void mtk_usxgmii_reset(struct mtk_eth *eth, int id)
+void mtk_usxgmii_reset(struct mtk_usxgmii_pcs *mpcs)
 {
+	struct mtk_eth *eth = mpcs->eth;
+	int id = mpcs->id;
 	u32 val = 0;
 
 	if (id >= MTK_MAX_DEVS || !eth->toprgu)
 		return;
 
+	mutex_lock(&mpcs->reset_lock);
+
 	switch (id) {
 	case 0:
 		/* Enable software reset */
@@ -515,7 +614,7 @@
 		       SWSYSRST_XFI0_GRST;
 		regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
 
-		udelay(100);
+		usleep_range(100, 500);
 
 		/* De-assert USXGMII reset */
 		regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
@@ -544,7 +643,7 @@
 		       SWSYSRST_XFI1_GRST;
 		regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
 
-		udelay(100);
+		usleep_range(100, 500);
 
 		/* De-assert USXGMII reset */
 		regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
@@ -561,7 +660,9 @@
 		break;
 	}
 
+	mutex_unlock(&mpcs->reset_lock);
+
-	mdelay(10);
+	usleep_range(10000, 11000);
 }
 
 static int mtk_usxgmii_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
@@ -571,12 +672,9 @@
 {
 	struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
 	struct mtk_eth *eth = mpcs->eth;
-	unsigned long flags;
 	unsigned int an_ctrl = 0, link_timer = 0, xfi_mode = 0, adapt_mode = 0;
 	bool mode_changed = false;
 
-	spin_lock_irqsave(&mpcs->regmap_lock, flags);
-
 	if (interface == PHY_INTERFACE_MODE_USXGMII) {
 		an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF) |
 			  USXGMII_AN_ENABLE;
@@ -602,20 +700,25 @@
 			   FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_TX_MODE_5G);
 		adapt_mode = USXGMII_RATE_UPDATE_MODE;
 	} else {
-		spin_unlock_irqrestore(&mpcs->regmap_lock, flags);
 		return -EINVAL;
 	}
 
 	adapt_mode |= FIELD_PREP(USXGMII_RATE_ADAPT_MODE, USXGMII_RATE_ADAPT_MODE_X1);
 
+	mtk_usxgmii_xfi_pll_enable(eth->usxgmii);
+	mtk_usxgmii_reset(mpcs);
+
-	if (mpcs->interface != interface) {
+	mutex_lock(&mpcs->regmap_lock);
+
+	if (mode >= 0 && mpcs->interface != interface) {
 		mpcs->interface = interface;
 		mpcs->mode = mode;
 		mode_changed = true;
 	}
 
-	mtk_usxgmii_xfi_pll_enable(eth->usxgmii);
-	mtk_usxgmii_reset(eth, mpcs->id);
+	/* Configure the interface polarity */
+	regmap_update_bits(mpcs->regmap, RG_PHY_TOP_CTRL0,
+			   USXGMII_PN_SWAP_MASK, mpcs->polarity);
 
 	/* Setup USXGMII AN ctrl */
 	regmap_update_bits(mpcs->regmap, RG_PCS_AN_CTRL0,
@@ -646,13 +749,13 @@
 			   USXGMII_XFI_RX_MODE | USXGMII_XFI_TX_MODE,
 			   xfi_mode);
 
-	udelay(1);
+	usleep_range(1, 5);
 
 	/* Un-gated MAC CK */
 	regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1,
 			   USXGMII_MAC_CK_GATED, 0);
 
-	udelay(1);
+	usleep_range(1, 5);
 
 	/* Disable interface force mode for the AN mode */
 	if (an_ctrl & USXGMII_AN_ENABLE)
@@ -667,32 +770,38 @@
 	else if (interface == PHY_INTERFACE_MODE_5GBASER)
 		mtk_usxgmii_setup_phya_5gbaser(mpcs);
 
-	spin_unlock_irqrestore(&mpcs->regmap_lock, flags);
+	mutex_unlock(&mpcs->regmap_lock);
 
 	return mode_changed;
 }
 
-static void mtk_usxgmii_pcs_link_poll(struct timer_list *t)
+static void mtk_usxgmii_pcs_link_poll(struct work_struct *work)
 {
-	struct mtk_usxgmii_pcs *mpcs = from_timer(mpcs, t, link_poll_outband);
+	struct mtk_usxgmii_pcs *mpcs = container_of(work, struct mtk_usxgmii_pcs,
+						    link_poll.work);
 
-	if (mpcs->interface == PHY_INTERFACE_MODE_NA)
+	if (mpcs->interface != PHY_INTERFACE_MODE_5GBASER &&
+	    mpcs->interface != PHY_INTERFACE_MODE_10GKR &&
+	    mpcs->interface != PHY_INTERFACE_MODE_USXGMII)
 		goto exit;
 
-	if (!mtk_usxgmii_link_status(mpcs))
-		mtk_usxgmii_pcs_config(&mpcs->pcs, mpcs->mode,
+	if (!mtk_usxgmii_link_status(mpcs) || !mtk_usxgmii_is_valid_ctle(mpcs))
+		mtk_usxgmii_pcs_config(&mpcs->pcs, -1,
 				       mpcs->interface, NULL, false);
 
 exit:
-	if (mpcs->mode != MLO_AN_INBAND)
-		mod_timer(&mpcs->link_poll_outband, jiffies + HZ);
+	if (mpcs->mode == MLO_AN_INBAND)
+		mtk_usxgmii_get_state(mpcs);
+	else if (!delayed_work_pending(&mpcs->link_poll))
+		schedule_delayed_work(&mpcs->link_poll, msecs_to_jiffies(1000));
 }
 
 static int mtk_usxgmii_pcs_enable(struct phylink_pcs *pcs)
 {
 	struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
 
-	mod_timer(&mpcs->link_poll_outband, jiffies + HZ);
+	if (!delayed_work_pending(&mpcs->link_poll))
+		schedule_delayed_work(&mpcs->link_poll, msecs_to_jiffies(1000));
 
 	return 0;
 }
@@ -701,86 +810,31 @@
 {
 	struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
 
-	del_timer_sync(&mpcs->link_poll_outband);
+	cancel_delayed_work_sync(&mpcs->link_poll);
 
-	mpcs->interface = PHY_INTERFACE_MODE_NA;
+	if (mpcs->mode == MLO_AN_INBAND)
+		mtk_usxgmii_get_state(mpcs);
 }
 
 static void mtk_usxgmii_pcs_get_state(struct phylink_pcs *pcs,
 				    struct phylink_link_state *state)
 {
 	struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
-	struct mtk_eth *eth = mpcs->eth;
-	struct mtk_mac *mac = eth->mac[mtk_xgmii2mac_id(eth, mpcs->id)];
-	u32 val = 0;
-
-	regmap_read(mpcs->regmap, RG_PCS_AN_CTRL0, &val);
-	if (FIELD_GET(USXGMII_AN_ENABLE, val)) {
-		/* Refresh LPA by inverting LPA_LATCH */
-		regmap_read(mpcs->regmap, RG_PCS_AN_STS0, &val);
-		regmap_update_bits(mpcs->regmap, RG_PCS_AN_STS0,
-				   USXGMII_LPA_LATCH,
-				   !(val & USXGMII_LPA_LATCH));
-
-		regmap_read(mpcs->regmap, RG_PCS_AN_STS0, &val);
-
-		state->interface = mpcs->interface;
-		state->link = FIELD_GET(USXGMII_LPA_LINK, val);
-		state->duplex = FIELD_GET(USXGMII_LPA_DUPLEX, val);
-
-		switch (FIELD_GET(USXGMII_LPA_SPEED_MASK, val)) {
-		case USXGMII_LPA_SPEED_10:
-			state->speed = SPEED_10;
-			break;
-		case USXGMII_LPA_SPEED_100:
-			state->speed = SPEED_100;
-			break;
-		case USXGMII_LPA_SPEED_1000:
-			state->speed = SPEED_1000;
-			break;
-		case USXGMII_LPA_SPEED_2500:
-			state->speed = SPEED_2500;
-			break;
-		case USXGMII_LPA_SPEED_5000:
-			state->speed = SPEED_5000;
-			break;
-		case USXGMII_LPA_SPEED_10000:
-			state->speed = SPEED_10000;
-			break;
-		}
-	} else {
-		val = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id));
-
-		if (mac->id == MTK_GMAC2_ID)
-			val = val >> 16;
-
-		switch (FIELD_GET(MTK_USXGMII_PCS_MODE, val)) {
-		case 0:
-			state->speed = SPEED_10000;
-			break;
-		case 1:
-			state->speed = SPEED_5000;
-			break;
-		case 2:
-			state->speed = SPEED_2500;
-			break;
-		case 3:
-			state->speed = SPEED_1000;
-			break;
-		}
 
-		state->interface = mpcs->interface;
-		state->link = FIELD_GET(MTK_USXGMII_PCS_LINK, val);
-		state->duplex = DUPLEX_FULL;
-	}
+	if (mpcs->state.interface != PHY_INTERFACE_MODE_NA)
+		state->interface = mpcs->state.interface;
+	state->speed = mpcs->state.speed;
+	state->duplex = mpcs->state.duplex;
+	state->link = mpcs->state.link;
 
 	/* Reconfiguring USXGMII every second to ensure that PCS can
 	 * link up with the Link Partner when a module is inserted.
 	 */
-	if (state->link == 0 && time_after(jiffies, mpcs->link_poll_inband + HZ)) {
-		mpcs->link_poll_inband = jiffies;
-		mtk_usxgmii_pcs_config(pcs, MLO_AN_INBAND,
-				       state->interface, NULL, false);
+	if (time_after(jiffies, mpcs->link_poll_expire) &&
+	    !delayed_work_pending(&mpcs->link_poll)) {
+		mpcs->link_poll_expire = jiffies + HZ;
+		mpcs->state.an_enabled = state->an_enabled;
+		schedule_delayed_work(&mpcs->link_poll, 0);
 	}
 }
 
@@ -802,14 +856,9 @@
 				    int speed, int duplex)
 {
 	struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
+	struct device *dev = mpcs->eth->dev;
 	unsigned long t_start = jiffies;
 
-	/* Reconfiguring USXGMII to ensure the quality of the RX signal
-	 * after the line side link up.
-	 */
-	mtk_usxgmii_pcs_config(pcs, mode,
-			       interface, NULL, false);
-
 	do {
 		msleep(100);
 
@@ -818,7 +867,7 @@
 
 	} while (time_before(jiffies, t_start + msecs_to_jiffies(3000)));
 
-	pr_warn("%s wait link up timeout!\n", __func__);
+	dev_warn(dev, "usxgmii%d: wait link up timeout!\n", mpcs->id);
 }
 
 static const struct phylink_pcs_ops mtk_usxgmii_pcs_ops = {
@@ -848,13 +897,26 @@
 		if (IS_ERR(ss->pcs[i].regmap))
 			return PTR_ERR(ss->pcs[i].regmap);
 
+		ss->pcs[i].polarity = 0;
+		if (of_property_read_bool(np, "pn_swap"))
+			ss->pcs[i].polarity |= USXGMII_PN_SWAP_TX | USXGMII_PN_SWAP_RX;
+		else if (of_property_read_bool(np, "pn_swap_tx"))
+			ss->pcs[i].polarity |= USXGMII_PN_SWAP_TX;
+		else if (of_property_read_bool(np, "pn_swap_rx"))
+			ss->pcs[i].polarity |= USXGMII_PN_SWAP_RX;
+
 		ss->pcs[i].pcs.ops = &mtk_usxgmii_pcs_ops;
 		ss->pcs[i].pcs.poll = true;
 		ss->pcs[i].interface = PHY_INTERFACE_MODE_NA;
 
-		timer_setup(&ss->pcs[i].link_poll_outband, mtk_usxgmii_pcs_link_poll, 0);
+		ss->pcs[i].state.link = 0;
+		ss->pcs[i].state.duplex = DUPLEX_FULL;
+		ss->pcs[i].state.speed = SPEED_10000;
+
+		INIT_DELAYED_WORK(&ss->pcs[i].link_poll, mtk_usxgmii_pcs_link_poll);
 
-		spin_lock_init(&ss->pcs[i].regmap_lock);
+		mutex_init(&ss->pcs[i].regmap_lock);
+		mutex_init(&ss->pcs[i].reset_lock);
 
 		of_node_put(np);
 	}
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855.c
index f3db8b8..79d6d1a 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855.c
@@ -18,6 +18,16 @@
 /* AN8855 registers */
 #define SCU_BASE					0x10000000
 #define RG_RGMII_TXCK_C				(SCU_BASE + 0x1d0)
+#define RG_GPIO_LED_MODE			(SCU_BASE + 0x0054)
+#define RG_GPIO_LED_SEL(i)	(SCU_BASE + (0x0058 + ((i) * 4)))
+#define RG_INTB_MODE				(SCU_BASE + 0x0080)
+#define RG_GDMP_RAM				(SCU_BASE + 0x10000)
+
+#define RG_GPIO_L_INV			(SCU_BASE + 0x0010)
+#define RG_GPIO_CTRL			(SCU_BASE + 0xa300)
+#define RG_GPIO_DATA			(SCU_BASE + 0xa304)
+#define RG_GPIO_OE			(SCU_BASE + 0xa314)
+
 
 #define HSGMII_AN_CSR_BASE			0x10220000
 #define SGMII_REG_AN0				(HSGMII_AN_CSR_BASE + 0x000)
@@ -61,6 +71,8 @@
 #define SS_LCPLL_TDC_PCW_1			(QP_PMA_TOP_BASE + 0x248)
 #define INTF_CTRL_8		(QP_PMA_TOP_BASE + 0x320)
 #define INTF_CTRL_9		(QP_PMA_TOP_BASE + 0x324)
+#define INTF_CTRL_10	(QP_PMA_TOP_BASE + 0x328)
+#define INTF_CTRL_11	(QP_PMA_TOP_BASE + 0x32c)
 #define PLL_CTRL_0		(QP_PMA_TOP_BASE + 0x400)
 #define PLL_CTRL_2		(QP_PMA_TOP_BASE + 0x408)
 #define PLL_CTRL_3		(QP_PMA_TOP_BASE + 0x40c)
@@ -78,6 +90,7 @@
 #define QP_ANA_CSR_BASE				0x1022f000
 #define RG_QP_RX_DAC_EN				(QP_ANA_CSR_BASE + 0x00)
 #define RG_QP_RXAFE_RESERVE			(QP_ANA_CSR_BASE + 0x04)
+#define RG_QP_CDR_LPF_BOT_LIM		(QP_ANA_CSR_BASE + 0x08)
 #define RG_QP_CDR_LPF_MJV_LIM		(QP_ANA_CSR_BASE + 0x0c)
 #define RG_QP_CDR_LPF_SETVALUE		(QP_ANA_CSR_BASE + 0x14)
 #define RG_QP_CDR_PR_CKREF_DIV1		(QP_ANA_CSR_BASE + 0x18)
@@ -106,6 +119,66 @@
 /* Fields of PHY_EXT_REG_14 */
 #define PHY_EN_DOWN_SHFIT		BIT(4)
 
+/* PHY dev address 0x1E */
+#define PHY_DEV1E				0x1e
+
+/* PHY TX PAIR DELAY SELECT Register */
+#define PHY_TX_PAIR_DLY_SEL_GBE		0x013
+/* PHY ADC Register */
+#define PHY_RXADC_CTRL				0x0d8
+#define PHY_RXADC_REV_0				0x0d9
+#define PHY_RXADC_REV_1				0x0da
+
+/* PHY LED Register bitmap of define */
+#define PHY_LED_CTRL_SELECT		0x3e8
+#define PHY_SINGLE_LED_ON_CTRL(i)	(0x3e0 + ((i) * 2))
+#define PHY_SINGLE_LED_BLK_CTRL(i)	(0x3e1 + ((i) * 2))
+#define PHY_SINGLE_LED_ON_DUR(i)	(0x3e9 + ((i) * 2))
+#define PHY_SINGLE_LED_BLK_DUR(i)	(0x3ea + ((i) * 2))
+
+#define PHY_PMA_CTRL	(0x340)
+
+/* PHY dev address 0x1F */
+#define PHY_DEV1F				0x1f
+#define PHY_LED_ON_CTRL(i)		(0x24 + ((i) * 2))
+#define LED_ON_EN				(1 << 15)
+#define LED_ON_POL				(1 << 14)
+#define LED_ON_EVT_MASK			(0x7f)
+/* LED ON Event */
+#define LED_ON_EVT_FORCE		(1 << 6)
+#define LED_ON_EVT_LINK_HD		(1 << 5)
+#define LED_ON_EVT_LINK_FD		(1 << 4)
+#define LED_ON_EVT_LINK_DOWN	(1 << 3)
+#define LED_ON_EVT_LINK_10M		(1 << 2)
+#define LED_ON_EVT_LINK_100M	(1 << 1)
+#define LED_ON_EVT_LINK_1000M	(1 << 0)
+
+#define PHY_LED_BLK_CTRL(i)		(0x25 + ((i) * 2))
+#define LED_BLK_EVT_MASK		(0x3ff)
+/* LED Blinking Event */
+#define LED_BLK_EVT_FORCE			(1 << 9)
+#define LED_BLK_EVT_10M_RX_ACT		(1 << 5)
+#define LED_BLK_EVT_10M_TX_ACT		(1 << 4)
+#define LED_BLK_EVT_100M_RX_ACT		(1 << 3)
+#define LED_BLK_EVT_100M_TX_ACT		(1 << 2)
+#define LED_BLK_EVT_1000M_RX_ACT	(1 << 1)
+#define LED_BLK_EVT_1000M_TX_ACT	(1 << 0)
+
+#define PHY_LED_BCR				(0x21)
+#define LED_BCR_EXT_CTRL		(1 << 15)
+#define LED_BCR_CLK_EN			(1 << 3)
+#define LED_BCR_TIME_TEST		(1 << 2)
+#define LED_BCR_MODE_MASK		(3)
+#define LED_BCR_MODE_DISABLE	(0)
+
+#define PHY_LED_ON_DUR			(0x22)
+#define LED_ON_DUR_MASK			(0xffff)
+
+#define PHY_LED_BLK_DUR			(0x23)
+#define LED_BLK_DUR_MASK		(0xffff)
+
+#define PHY_LED_BLINK_DUR_CTRL	(0x720)
+
 /* Unique fields of PMCR for AN8855 */
 #define FORCE_TX_FC		BIT(4)
 #define FORCE_RX_FC		BIT(5)
@@ -117,10 +190,114 @@
 #define CHIP_ID			0x10005000
 #define CHIP_REV		0x10005004
 
+#define AN8855_EFUSE_DATA0	0x1000a500
+
+const u8 r50ohm_table[] = {
+	127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+	127, 127, 127, 127, 127, 127, 127, 126, 122, 117,
+	112, 109, 104, 101,  97,  94,  90,  88,  84,  80,
+	78,  74,  72,  68,  66,  64,  61,  58,  56,  53,
+	51,  48,  47,  44,  42,  40,  38,  36,  34,  32,
+	31,  28,  27,  24,  24,  22,  20,  18,  16,  16,
+	14,  12,  11,   9
+};
+
+static u8 shift_check(u8 base)
+{
+	u8 i;
+	u32 sz = sizeof(r50ohm_table)/sizeof(u8);
+
+	for (i = 0; i < sz; ++i)
+		if (r50ohm_table[i] == base)
+			break;
+
+	if (i < 8 || i >= sz)
+		return 25; /* index of 94 */
+
+	return (i - 8);
+}
+
+static u8 get_shift_val(u8 idx)
+{
+	return r50ohm_table[idx];
+}
+
+/* T830 AN8855 Reference Board */
+static const struct an8855_led_cfg led_cfg[] = {
+/*************************************************************************
+ * Enable, LED idx, LED Polarity, LED ON event,  LED Blink event  LED Freq
+ *************************************************************************
+ */
+	/* GPIO0 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO1 */
+	{1, P0_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO2 */
+	{1, P1_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO3 */
+	{1, P2_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO4 */
+	{1, P3_LED1, LED_LOW,  LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO5 */
+	{1, P4_LED1, LED_LOW,  LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO6 */
+	{0, PHY_LED_MAX, LED_LOW,  LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO7 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO8 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO9 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO10 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO11 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO12 */
+	{0, PHY_LED_MAX, LED_LOW,  LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO13 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO14 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO15 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO16 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO17 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO18 */
+	{0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO19 */
+	{0, PHY_LED_MAX, LED_LOW,  LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+	/* GPIO20 */
+	{0, PHY_LED_MAX, LED_LOW,  LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+};
+
 static int an8855_set_hsgmii_mode(struct gsw_an8855 *gsw)
 {
 	u32 val = 0;
 
+	/* TX FIR - improve TX EYE */
+	val = an8855_reg_read(gsw, INTF_CTRL_10);
+	val &= ~(0x3f << 16);
+	val |= BIT(21);
+	val &= ~(0x1f << 24);
+	val |= (0x4 << 24);
+	val |= BIT(29);
+	an8855_reg_write(gsw, INTF_CTRL_10, val);
+
+	val = an8855_reg_read(gsw, INTF_CTRL_11);
+	val &= ~(0x3f);
+	val |= BIT(6);
+	an8855_reg_write(gsw, INTF_CTRL_11, val);
+
+	/* RX CDR - improve RX Jitter Tolerance */
+	val = an8855_reg_read(gsw, RG_QP_CDR_LPF_BOT_LIM);
+	val &= ~(0x7 << 24);
+	val |= (0x5 << 24);
+	val &= ~(0x7 << 20);
+	val |= (0x5 << 20);
+	an8855_reg_write(gsw, RG_QP_CDR_LPF_BOT_LIM, val);
+
 	/* PLL */
 	val = an8855_reg_read(gsw, QP_DIG_MODE_CTRL_1);
 	val &= ~(0x3 << 2);
@@ -250,7 +427,7 @@
 	val &= ~(0xf << 25);
 	val |= (0x1 << 25);
 	val &= ~(0x7 << 29);
-	val |= (0x3 << 29);
+	val |= (0x6 << 29);
 	an8855_reg_write(gsw, RG_QP_CDR_LPF_SETVALUE, val);
 
 	val = an8855_reg_read(gsw, RG_QP_CDR_PR_CKREF_DIV1);
@@ -281,12 +458,10 @@
 	val |= (0x4 << 24);
 	an8855_reg_write(gsw, RG_QP_CDR_PR_CKREF_DIV1, val);
 
-	val = an8855_reg_read(gsw, PLL_CTRL_0);
-	val |= BIT(0);
-	an8855_reg_write(gsw, PLL_CTRL_0, val);
-
+	/* PMA (For HW Mode) */
 	val = an8855_reg_read(gsw, RX_CTRL_26);
-	val &= ~BIT(23);
+	val |= BIT(23);
+	val &= ~BIT(24);
 	val |= BIT(26);
 	an8855_reg_write(gsw, RX_CTRL_26, val);
 
@@ -314,8 +489,8 @@
 	val = an8855_reg_read(gsw, RX_CTRL_8);
 	val &= ~(0xfff << 16);
 	val |= (0x200 << 16);
-	val &= ~(0x7fff << 14);
-	val |= (0xfff << 14);
+	val &= ~(0x7fff << 0);
+	val |= (0xfff << 0);
 	an8855_reg_write(gsw, RX_CTRL_8, val);
 
 	/* Frequency memter */
@@ -334,6 +509,10 @@
 	val |= (0x2710 << 0);
 	an8855_reg_write(gsw, RX_CTRL_7, val);
 
+	val = an8855_reg_read(gsw, PLL_CTRL_0);
+	val |= BIT(0);
+	an8855_reg_write(gsw, PLL_CTRL_0, val);
+
 	/* PCS Init */
 	val = an8855_reg_read(gsw, RG_HSGMII_PCS_CTROL_1);
 	val &= ~BIT(30);
@@ -372,6 +551,28 @@
 {
 	u32 val = 0;
 
+	/* TX FIR - improve TX EYE */
+	val = an8855_reg_read(gsw, INTF_CTRL_10);
+	val &= ~(0x3f << 16);
+	val |= BIT(21);
+	val &= ~(0x1f << 24);
+	val |= BIT(29);
+	an8855_reg_write(gsw, INTF_CTRL_10, val);
+
+	val = an8855_reg_read(gsw, INTF_CTRL_11);
+	val &= ~(0x3f);
+	val |= (0xd << 0);
+	val |= BIT(6);
+	an8855_reg_write(gsw, INTF_CTRL_11, val);
+
+	/* RX CDR - improve RX Jitter Tolerance */
+	val = an8855_reg_read(gsw, RG_QP_CDR_LPF_BOT_LIM);
+	val &= ~(0x7 << 24);
+	val |= (0x6 << 24);
+	val &= ~(0x7 << 20);
+	val |= (0x6 << 20);
+	an8855_reg_write(gsw, RG_QP_CDR_LPF_BOT_LIM, val);
+
 	/* PMA Init */
 	/* PLL */
 	val = an8855_reg_read(gsw, QP_DIG_MODE_CTRL_1);
@@ -531,13 +732,10 @@
 	val |= (0x4 << 24);
 	an8855_reg_write(gsw, RG_QP_CDR_PR_CKREF_DIV1, val);
 
-	val = an8855_reg_read(gsw, PLL_CTRL_0);
-	val |= BIT(0);
-	an8855_reg_write(gsw, PLL_CTRL_0, val);
-
+	/* PMA (For HW Mode) */
 	val = an8855_reg_read(gsw, RX_CTRL_26);
-	val &= ~BIT(23);
-	if (mode == SGMII_MODE_AN)
+	val |= BIT(23);
+	val &= ~BIT(24);
 		val |= BIT(26);
 
 	an8855_reg_write(gsw, RX_CTRL_26, val);
@@ -586,6 +784,10 @@
 	val |= (0x2710 << 0);
 	an8855_reg_write(gsw, RX_CTRL_7, val);
 
+	val = an8855_reg_read(gsw, PLL_CTRL_0);
+	val |= BIT(0);
+	an8855_reg_write(gsw, PLL_CTRL_0, val);
+
 	if (mode == SGMII_MODE_FORCE) {
 		/* PCS Init */
 		val = an8855_reg_read(gsw, QP_DIG_MODE_CTRL_0);
@@ -670,7 +872,6 @@
 		/* Restart AN */
 		val = an8855_reg_read(gsw, SGMII_REG_AN0);
 		val |= BIT(9);
-		val |= BIT(15);
 		an8855_reg_write(gsw, SGMII_REG_AN0, val);
 	}
 
@@ -772,7 +973,10 @@
 
 static void an8855_phy_setting(struct gsw_an8855 *gsw)
 {
-	int i;
+	int i, j;
+	u8 shift_sel = 0, rsel_tx_a = 0, rsel_tx_b = 0;
+	u8 rsel_tx_c = 0, rsel_tx_d = 0;
+	u16 cl45_data = 0;
 	u32 val;
 
 	/* Release power down */
@@ -791,52 +995,262 @@
 		val |= ADVERTISE_PAUSE_ASYM;
 		gsw->mii_write(gsw, i, MII_ADVERTISE, val);
 	}
+
+	if (gsw->extSurge) {
+		for (i = 0; i < AN8855_NUM_PHYS; i++) {
+			/* Read data */
+			for (j = 0; j < AN8855_WORD_SIZE; j++) {
+				val = an8855_reg_read(gsw, AN8855_EFUSE_DATA0 +
+					(AN8855_WORD_SIZE * (3 + j + (4 * i))));
+
+				shift_sel = shift_check((val & 0x7f000000) >> 24);
+				switch (j) {
+				case 0:
+					rsel_tx_a = get_shift_val(shift_sel);
+					break;
+				case 1:
+					rsel_tx_b = get_shift_val(shift_sel);
+					break;
+				case 2:
+					rsel_tx_c = get_shift_val(shift_sel);
+					break;
+				case 3:
+					rsel_tx_d = get_shift_val(shift_sel);
+					break;
+				default:
+					continue;
+				}
+			}
+			cl45_data = gsw->mmd_read(gsw, i, PHY_DEV1E, 0x174);
+			cl45_data &= ~(0x7f7f);
+			cl45_data |= (rsel_tx_a << 8);
+			cl45_data |= rsel_tx_b;
+			gsw->mmd_write(gsw, i, PHY_DEV1E, 0x174, cl45_data);
+			cl45_data = gsw->mmd_read(gsw, i, PHY_DEV1E, 0x175);
+			cl45_data &= ~(0x7f7f);
+			cl45_data |= (rsel_tx_c << 8);
+			cl45_data |= rsel_tx_d;
+			gsw->mmd_write(gsw, i, PHY_DEV1E, 0x175, cl45_data);
+		}
+	}
+}
+
+static void an8855_eee_setting(struct gsw_an8855 *gsw, u32 port)
+{
+	/* Disable EEE */
+	gsw->mmd_write(gsw, port, PHY_DEV07, PHY_DEV07_REG_03C, 0);
+}
+
+static int an8855_led_set_usr_def(struct gsw_an8855 *gsw, u8 entity,
+		int polar, u16 on_evt, u16 blk_evt, u8 led_freq)
+{
+	u32 cl45_data = 0;
+
+	if (polar == LED_HIGH)
+		on_evt |= LED_ON_POL;
+	else
+		on_evt &= ~LED_ON_POL;
+
+	/* LED on event */
+	gsw->mmd_write(gsw, (entity / 4), PHY_DEV1E,
+		PHY_SINGLE_LED_ON_CTRL(entity % 4), on_evt | LED_ON_EN);
+
+	/* LED blink event */
+	gsw->mmd_write(gsw, (entity / 4), PHY_DEV1E,
+		PHY_SINGLE_LED_BLK_CTRL(entity % 4), blk_evt);
+
+	/* LED freq */
+	switch (led_freq) {
+	case AIR_LED_BLK_DUR_32M:
+		cl45_data = 0x30e;
+		break;
+	case AIR_LED_BLK_DUR_64M:
+		cl45_data = 0x61a;
+		break;
+	case AIR_LED_BLK_DUR_128M:
+		cl45_data = 0xc35;
+		break;
+	case AIR_LED_BLK_DUR_256M:
+		cl45_data = 0x186a;
+		break;
+	case AIR_LED_BLK_DUR_512M:
+		cl45_data = 0x30d4;
+		break;
+	case AIR_LED_BLK_DUR_1024M:
+		cl45_data = 0x61a8;
+		break;
+	default:
+		break;
+	}
+	gsw->mmd_write(gsw, (entity / 4), PHY_DEV1E,
+		PHY_SINGLE_LED_BLK_DUR(entity % 4), cl45_data);
+
+	gsw->mmd_write(gsw, (entity / 4), PHY_DEV1E,
+		PHY_SINGLE_LED_ON_DUR(entity % 4), (cl45_data >> 1));
+
+	/* Disable DATA & BAD_SSD for port LED blink behavior */
+	cl45_data = gsw->mmd_read(gsw, (entity / 4), PHY_DEV1E,
+		PHY_PMA_CTRL);
+	cl45_data &= ~BIT(0);
+	cl45_data &= ~BIT(15);
+	gsw->mmd_write(gsw, (entity / 4), PHY_DEV1E,
+		PHY_PMA_CTRL, cl45_data);
+
+	return 0;
 }
 
-static void an8855_low_power_setting(struct gsw_an8855 *gsw)
+static int an8855_led_set_mode(struct gsw_an8855 *gsw, u8 mode)
 {
-	int port, addr;
+	u16 cl45_data;
 
-	for (port = 0; port < AN8855_NUM_PHYS; port++) {
-		gsw->mmd_write(gsw, port, 0x1e, 0x11, 0x0f00);
-		gsw->mmd_write(gsw, port, 0x1e, 0x3c, 0x0000);
-		gsw->mmd_write(gsw, port, 0x1e, 0x3d, 0x0000);
-		gsw->mmd_write(gsw, port, 0x1e, 0x3e, 0x0000);
-		gsw->mmd_write(gsw, port, 0x1e, 0xc6, 0x53aa);
+	cl45_data = gsw->mmd_read(gsw, 0, PHY_DEV1F, PHY_LED_BCR);
+	switch (mode) {
+	case AN8855_LED_MODE_DISABLE:
+		cl45_data &= ~LED_BCR_EXT_CTRL;
+		cl45_data &= ~LED_BCR_MODE_MASK;
+		cl45_data |= LED_BCR_MODE_DISABLE;
+		break;
+	case AN8855_LED_MODE_USER_DEFINE:
+		cl45_data |= LED_BCR_EXT_CTRL;
+		cl45_data |= LED_BCR_CLK_EN;
+		break;
+	default:
+		dev_info(gsw->dev, "LED mode%d is not supported!\n", mode);
+		return -EINVAL;
 	}
+	gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_LED_BCR, cl45_data);
 
-	gsw->mmd_write(gsw, 0, 0x1f, 0x268, 0x07f1);
-	gsw->mmd_write(gsw, 0, 0x1f, 0x269, 0x2111);
-	gsw->mmd_write(gsw, 0, 0x1f, 0x26a, 0x0000);
-	gsw->mmd_write(gsw, 0, 0x1f, 0x26b, 0x0074);
-	gsw->mmd_write(gsw, 0, 0x1f, 0x26e, 0x00f6);
-	gsw->mmd_write(gsw, 0, 0x1f, 0x26f, 0x6666);
-	gsw->mmd_write(gsw, 0, 0x1f, 0x271, 0x2c02);
-	gsw->mmd_write(gsw, 0, 0x1f, 0x272, 0x0c22);
-	gsw->mmd_write(gsw, 0, 0x1f, 0x700, 0x0001);
-	gsw->mmd_write(gsw, 0, 0x1f, 0x701, 0x0803);
-	gsw->mmd_write(gsw, 0, 0x1f, 0x702, 0x01b6);
-	gsw->mmd_write(gsw, 0, 0x1f, 0x703, 0x2111);
+	return 0;
+}
 
-	gsw->mmd_write(gsw, 1, 0x1f, 0x700, 0x0001);
+static int an8855_led_set_state(struct gsw_an8855 *gsw, u8 entity, u8 state)
+{
+	u16 cl45_data = 0;
+
+	/* Change to per port contorl */
+	cl45_data = gsw->mmd_read(gsw, (entity / 4), PHY_DEV1E,
+		PHY_LED_CTRL_SELECT);
+
+	if (state == 1)
+		cl45_data |= (1 << (entity % 4));
+	else
+		cl45_data &= ~(1 << (entity % 4));
+	gsw->mmd_write(gsw, (entity / 4), PHY_DEV1E,
+		PHY_LED_CTRL_SELECT, cl45_data);
+
+	/* LED enable setting */
+	cl45_data = gsw->mmd_read(gsw, (entity / 4),
+		PHY_DEV1E, PHY_SINGLE_LED_ON_CTRL(entity % 4));
 
-	for (addr = 0x200; addr <= 0x230; addr += 2)
-		gsw->mmd_write(gsw, 0, 0x1f, addr, 0x2020);
+	if (state == 1)
+		cl45_data |= LED_ON_EN;
+	else
+		cl45_data &= ~LED_ON_EN;
 
-	for (addr = 0x201; addr <= 0x231; addr += 2)
-		gsw->mmd_write(gsw, 0, 0x1f, addr, 0x0020);
+	gsw->mmd_write(gsw, (entity / 4), PHY_DEV1E,
+		PHY_SINGLE_LED_ON_CTRL(entity % 4), cl45_data);
+
+	return 0;
 }
 
-static void an8855_eee_setting(struct gsw_an8855 *gsw, u32 port)
+static int an8855_led_init(struct gsw_an8855 *gsw)
 {
-	/* Disable EEE */
-	gsw->mmd_write(gsw, port, PHY_DEV07, PHY_DEV07_REG_03C, 0);
+	u32 val, led_count = ARRAY_SIZE(led_cfg);
+	int ret = 0, id;
+	u32 tmp_val = 0;
+	u32 tmp_id = 0;
+
+	ret = an8855_led_set_mode(gsw, AN8855_LED_MODE_USER_DEFINE);
+	if (ret != 0) {
+		dev_info(gsw->dev, "led_set_mode fail(ret:%d)!\n", ret);
+		return ret;
+	}
+
+	for (id = 0; id < led_count; id++) {
+		ret = an8855_led_set_state(gsw,
+			led_cfg[id].phy_led_idx, led_cfg[id].en);
+		if (ret != 0) {
+			dev_info(gsw->dev, "led_set_state fail(ret:%d)!\n", ret);
+			return ret;
+		}
+		if (led_cfg[id].en == 1) {
+			ret = an8855_led_set_usr_def(gsw,
+				led_cfg[id].phy_led_idx,
+				led_cfg[id].pol, led_cfg[id].on_cfg,
+				led_cfg[id].blk_cfg,
+				led_cfg[id].led_freq);
+			if (ret != 0) {
+				dev_info(gsw->dev, "led_set_usr_def fail!\n");
+				return ret;
+			}
+		}
+	}
+
+	/* Setting for System LED & Loop LED */
+	an8855_reg_write(gsw, RG_GPIO_OE, 0x0);
+	an8855_reg_write(gsw, RG_GPIO_CTRL, 0x0);
+	val = 0;
+	an8855_reg_write(gsw, RG_GPIO_L_INV, val);
+
+	val = 0x1001;
+	an8855_reg_write(gsw, RG_GPIO_CTRL, val);
+	val = an8855_reg_read(gsw, RG_GPIO_DATA);
+	val |= BITS(1, 3);
+	val &= ~(BIT(0));
+	val &= ~(BIT(6));
+
+	an8855_reg_write(gsw, RG_GPIO_DATA, val);
+	val = an8855_reg_read(gsw, RG_GPIO_OE);
+	val |= 0x41;
+	an8855_reg_write(gsw, RG_GPIO_OE, val);
+
+	/* Mapping between GPIO & LED */
+	val = 0;
+	for (id = 0; id < led_count; id++) {
+		/* Skip GPIO6, due to GPIO6 does not support PORT LED */
+		if (id == 6)
+			continue;
+
+		if (led_cfg[id].en == 1) {
+			if (id < 7)
+				val |= led_cfg[id].phy_led_idx << ((id % 4) * 8);
+			else
+				val |= led_cfg[id].phy_led_idx << (((id - 1) % 4) * 8);
+		}
+
+		if (id < 7)
+			tmp_id = id;
+		else
+			tmp_id = id - 1;
+
+		if ((tmp_id % 4) == 0x3) {
+			an8855_reg_write(gsw, RG_GPIO_LED_SEL(tmp_id / 4), val);
+			tmp_val = an8855_reg_read(gsw, RG_GPIO_LED_SEL(tmp_id / 4));
+			val = 0;
+		}
+	}
+
+	/* Turn on LAN LED mode */
+	val = 0;
+	for (id = 0; id < led_count; id++) {
+		if (led_cfg[id].en == 1)
+			val |= 0x1 << id;
+	}
+	an8855_reg_write(gsw, RG_GPIO_LED_MODE, val);
+
+	/* Force clear blink pulse for per port LED */
+	gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_LED_BLINK_DUR_CTRL, 0x1f);
+	usleep_range(1000, 5000);
+	gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_LED_BLINK_DUR_CTRL, 0);
+
+	return 0;
 }
 
 static int an8855_sw_init(struct gsw_an8855 *gsw)
 {
-	int i;
-	u32 val;
+	int i, ret = 0;
+	u32 val, led_count = ARRAY_SIZE(led_cfg);
+	int id;
 
 	gsw->phy_base = gsw->smi_addr & AN8855_SMI_ADDR_MASK;
 
@@ -852,7 +1266,7 @@
 	an8855_reg_write(gsw, SYS_CTRL, SW_SYS_RST);
 	usleep_range(100000, 110000);
 
-	/* change gphy smi address */
+	/* Change gphy smi address */
 	if (gsw->new_smi_addr != gsw->smi_addr) {
 		an8855_reg_write(gsw, RG_GPHY_SMI_ADDR, gsw->new_smi_addr);
 		gsw->smi_addr = gsw->new_smi_addr;
@@ -865,6 +1279,59 @@
 		gsw->mii_write(gsw, i, MII_BMCR, val);
 	}
 
+	/* AN8855H need to setup before switch init */
+	val = an8855_reg_read(gsw, PKG_SEL);
+	if ((val & 0x7) == PAG_SEL_AN8855H) {
+
+		/* Invert for LED activity change */
+		val = an8855_reg_read(gsw, RG_GPIO_L_INV);
+		for (id = 0; id < led_count; id++) {
+			if ((led_cfg[id].pol == LED_HIGH) &&
+				(led_cfg[id].en == 1))
+				val |= 0x1 << id;
+		}
+		an8855_reg_write(gsw, RG_GPIO_L_INV, (val | 0x1));
+
+		/* MCU NOP CMD */
+		an8855_reg_write(gsw, RG_GDMP_RAM, 0x846);
+		an8855_reg_write(gsw, RG_GDMP_RAM + 4, 0x4a);
+
+		/* Enable MCU */
+		val = an8855_reg_read(gsw, RG_CLK_CPU_ICG);
+		an8855_reg_write(gsw, RG_CLK_CPU_ICG, val | MCU_ENABLE);
+		usleep_range(1000, 5000);
+
+		/* Disable MCU watchdog */
+		val = an8855_reg_read(gsw, RG_TIMER_CTL);
+		an8855_reg_write(gsw, RG_TIMER_CTL, (val & (~WDOG_ENABLE)));
+
+		/* Configure interrupt */
+		an8855_reg_write(gsw, RG_INTB_MODE, (0x1 << gsw->intr_pin));
+
+		/* LED settings for T830 reference board */
+		ret = an8855_led_init(gsw);
+		if (ret < 0) {
+			dev_info(gsw->dev, "an8855_led_init fail. (ret=%d)\n", ret);
+			return ret;
+		}
+	}
+
+	/* Adjust to reduce noise */
+	for (i = 0; i < AN8855_NUM_PHYS; i++) {
+		gsw->mmd_write(gsw, i, PHY_DEV1E,
+			PHY_TX_PAIR_DLY_SEL_GBE, 0x4040);
+
+		gsw->mmd_write(gsw, i, PHY_DEV1E,
+			PHY_RXADC_CTRL, 0x1010);
+
+		gsw->mmd_write(gsw, i, PHY_DEV1E,
+			PHY_RXADC_REV_0, 0x100);
+
+		gsw->mmd_write(gsw, i, PHY_DEV1E,
+			PHY_RXADC_REV_1, 0x100);
+	}
+
+	/* Setup SERDES port 5 */
 	an8855_mac_port_setup(gsw, 5, &gsw->port5_cfg);
 
 	/* Global mac control settings */
@@ -875,6 +1342,7 @@
 	val = an8855_reg_read(gsw, CKGCR);
 	val &= ~(CKG_LNKDN_GLB_STOP | CKG_LNKDN_PORT_STOP);
 	an8855_reg_write(gsw, CKGCR, val);
+
 	return 0;
 }
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855.h
index 883ba6e..5f84ee5 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855.h
@@ -23,8 +23,24 @@
 
 #define AN8855_DFL_CPU_PORT		5
 #define AN8855_NUM_PHYS			5
+#define AN8855_WORD_SIZE		4
 #define AN8855_DFL_SMI_ADDR		0x1
 #define AN8855_SMI_ADDR_MASK	0x1f
+#define AN8855_DFL_INTR_ID		0xd
+#define AN8855_DFL_EXT_SURGE	0x0
+
+#define LED_ON_EVENT	(LED_ON_EVT_LINK_1000M | \
+			LED_ON_EVT_LINK_100M | LED_ON_EVT_LINK_10M |\
+			LED_ON_EVT_LINK_HD | LED_ON_EVT_LINK_FD)
+
+#define LED_BLK_EVENT	(LED_BLK_EVT_1000M_TX_ACT | \
+			LED_BLK_EVT_1000M_RX_ACT | \
+			LED_BLK_EVT_100M_TX_ACT | \
+			LED_BLK_EVT_100M_RX_ACT | \
+			LED_BLK_EVT_10M_TX_ACT | \
+			LED_BLK_EVT_10M_RX_ACT)
+
+#define LED_FREQ	AIR_LED_BLK_DUR_64M
 
 struct gsw_an8855;
 
@@ -37,6 +53,60 @@
 	SGMII_MODE_FORCE,
 };
 
+enum phy_led_idx {
+	P0_LED0,
+	P0_LED1,
+	P0_LED2,
+	P0_LED3,
+	P1_LED0,
+	P1_LED1,
+	P1_LED2,
+	P1_LED3,
+	P2_LED0,
+	P2_LED1,
+	P2_LED2,
+	P2_LED3,
+	P3_LED0,
+	P3_LED1,
+	P3_LED2,
+	P3_LED3,
+	P4_LED0,
+	P4_LED1,
+	P4_LED2,
+	P4_LED3,
+	PHY_LED_MAX
+};
+
+/* TBD */
+enum an8855_led_blk_dur {
+	AIR_LED_BLK_DUR_32M,
+	AIR_LED_BLK_DUR_64M,
+	AIR_LED_BLK_DUR_128M,
+	AIR_LED_BLK_DUR_256M,
+	AIR_LED_BLK_DUR_512M,
+	AIR_LED_BLK_DUR_1024M,
+	AIR_LED_BLK_DUR_LAST
+};
+
+enum an8855_led_polarity {
+	LED_LOW,
+	LED_HIGH,
+};
+enum an8855_led_mode {
+	AN8855_LED_MODE_DISABLE,
+	AN8855_LED_MODE_USER_DEFINE,
+	AN8855_LED_MODE_LAST
+};
+
+struct an8855_led_cfg {
+	u16 en;
+	u8  phy_led_idx;
+	u16 pol;
+	u16 on_cfg;
+	u16 blk_cfg;
+	u8 led_freq;
+};
+
 struct an8855_port_cfg {
 	struct device_node *np;
 	phy_interface_t phy_mode;
@@ -55,6 +125,8 @@
 	u32 smi_addr;
 	u32 new_smi_addr;
 	u32 phy_base;
+	u32 intr_pin;
+	u32 extSurge;
 
 	enum an8855_model model;
 	const char *name;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_mdio.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_mdio.c
index 920240d..a4eb389 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_mdio.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_mdio.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifian8855_gsw_ider: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2023 Airoha Inc.
  * Author: Min Yao <min.yao@airoha.com>
@@ -18,6 +18,9 @@
 #include <linux/of_net.h>
 #include <linux/of_irq.h>
 #include <linux/phy.h>
+#include <linux/version.h>
+#include <linux/seq_file.h>
+#include <linux/proc_fs.h>
 
 #include "an8855.h"
 #include "an8855_swconfig.h"
@@ -25,11 +28,15 @@
 #include "an8855_nl.h"
 
 /* AN8855 driver version */
-#define ARHT_AN8855_SWCFG_DRIVER_VER	"1.0.1-L5.4"
+#define ARHT_AN8855_SWCFG_DRIVER_VER	"1.0.6"
+#define ARHT_CHIP_NAME                  "an8855"
+#define ARHT_PROC_DIR                   "air_sw"
+#define ARHT_PROC_NODE_DEVICE           "device"
 
 static u32 an8855_gsw_id;
 struct list_head an8855_devs;
 static DEFINE_MUTEX(an8855_devs_lock);
+struct proc_dir_entry *proc_an8855_gsw_dir;
 
 static struct an8855_sw_id *an8855_sw_ids[] = {
 	&an8855_id,
@@ -53,7 +60,6 @@
 	high = gsw->host_bus->read(gsw->host_bus, gsw->smi_addr, 0x17);
 
 	gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x1f, 0x0);
-	gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x10, 0x0);
 
 	mutex_unlock(&gsw->host_bus->mdio_lock);
 
@@ -78,7 +84,6 @@
 			     (val & 0xFFFF));
 
 	gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x1f, 0x0);
-	gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x10, 0x0);
 
 	mutex_unlock(&gsw->host_bus->mdio_lock);
 }
@@ -110,13 +115,17 @@
 int an8855_mmd_read(struct gsw_an8855 *gsw, int addr, int devad, u16 reg)
 {
 	int val;
-	u32 regnum = MII_ADDR_C45 | (devad << 16) | reg;
 
 	if (addr < AN8855_NUM_PHYS)
 		addr = (gsw->phy_base + addr) & AN8855_SMI_ADDR_MASK;
 
 	mutex_lock(&gsw->host_bus->mdio_lock);
-	val = gsw->host_bus->read(gsw->host_bus, addr, regnum);
+
+	gsw->host_bus->write(gsw->host_bus, addr, 0x0d, devad);
+	gsw->host_bus->write(gsw->host_bus, addr, 0x0e, reg);
+	gsw->host_bus->write(gsw->host_bus, addr, 0x0d, devad | (0x4000));
+	val = gsw->host_bus->read(gsw->host_bus, addr, 0xe);
+
 	mutex_unlock(&gsw->host_bus->mdio_lock);
 
 	return val;
@@ -125,13 +134,16 @@
 void an8855_mmd_write(struct gsw_an8855 *gsw, int addr, int devad, u16 reg,
 		      u16 val)
 {
-	u32 regnum = MII_ADDR_C45 | (devad << 16) | reg;
-
 	if (addr < AN8855_NUM_PHYS)
 		addr = (gsw->phy_base + addr) & AN8855_SMI_ADDR_MASK;
 
 	mutex_lock(&gsw->host_bus->mdio_lock);
-	gsw->host_bus->write(gsw->host_bus, addr, regnum, val);
+
+	gsw->host_bus->write(gsw->host_bus, addr, 0x0d, devad);
+	gsw->host_bus->write(gsw->host_bus, addr, 0x0e, reg);
+	gsw->host_bus->write(gsw->host_bus, addr, 0x0d, devad | (0x4000));
+	gsw->host_bus->write(gsw->host_bus, addr, 0x0e, val);
+
 	mutex_unlock(&gsw->host_bus->mdio_lock);
 }
 
@@ -146,6 +158,10 @@
 	struct device_node *fixed_link_node;
 	struct an8855_port_cfg *port_cfg;
 	u32 port;
+#if (KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE)
+	int ret;
+
+#endif
 
 	for_each_child_of_node(gsw->dev->of_node, port_np) {
 		if (!of_device_is_compatible(port_np, "airoha,an8855-port"))
@@ -172,9 +188,13 @@
 		}
 
 		port_cfg->np = port_np;
-
+#if (KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE)
+		ret = of_get_phy_mode(port_np, &port_cfg->phy_mode);
+		if (ret < 0) {
+#else
 		port_cfg->phy_mode = of_get_phy_mode(port_np);
 		if (port_cfg->phy_mode < 0) {
+#endif
 			dev_info(gsw->dev, "incorrect phy-mode %d\n", port);
 			continue;
 		}
@@ -296,6 +316,7 @@
 	}
 
 	gpio_direction_output(gsw->reset_pin, 0);
+	gpio_set_value(gsw->reset_pin, 0);
 	usleep_range(100000, 150000);
 	gpio_set_value(gsw->reset_pin, 1);
 	usleep_range(100000, 150000);
@@ -314,6 +335,51 @@
 	return IRQ_HANDLED;
 }
 
+static int an8855_proc_device_read(struct seq_file *seq, void *v)
+{
+	seq_printf(seq, "%s\n", ARHT_CHIP_NAME);
+
+	return 0;
+}
+
+static int an8855_proc_device_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, an8855_proc_device_read, 0);
+}
+
+#if (KERNEL_VERSION(5, 6, 0) <= LINUX_VERSION_CODE)
+static const struct proc_ops an8855_proc_device_fops = {
+	.proc_open	= an8855_proc_device_open,
+	.proc_read	= seq_read,
+	.proc_lseek	= seq_lseek,
+	.proc_release	= single_release,
+};
+#else
+static const struct file_operations an8855_proc_device_fops = {
+	.owner	= THIS_MODULE,
+	.open	= an8855_proc_device_open,
+	.read	= seq_read,
+	.llseek	= seq_lseek,
+	.release	= single_release,
+};
+#endif
+
+static int an8855_proc_device_init(struct gsw_an8855 *gsw)
+{
+	if (!proc_an8855_gsw_dir)
+		proc_an8855_gsw_dir = proc_mkdir(ARHT_PROC_DIR, 0);
+
+	proc_create(ARHT_PROC_NODE_DEVICE, 0400, proc_an8855_gsw_dir,
+			&an8855_proc_device_fops);
+
+	return 0;
+}
+
+static void an8855_proc_device_exit(void)
+{
+	remove_proc_entry(ARHT_PROC_NODE_DEVICE, 0);
+}
+
 static int an8855_probe(struct platform_device *pdev)
 {
 	struct gsw_an8855 *gsw;
@@ -355,6 +421,14 @@
 	if (of_property_read_u32(np, "airoha,smi-addr", &gsw->new_smi_addr))
 		gsw->new_smi_addr = AN8855_DFL_SMI_ADDR;
 
+	/* Assign AN8855 interrupt pin */
+	if (of_property_read_u32(np, "airoha,intr", &gsw->intr_pin))
+		gsw->intr_pin = AN8855_DFL_INTR_ID;
+
+	/* AN8855 surge enhancement */
+	if (of_property_read_u32(np, "airoha,extSurge", &gsw->extSurge))
+		gsw->extSurge = AN8855_DFL_EXT_SURGE;
+
 	/* Get LAN/WAN port mapping */
 	map = an8855_find_mapping(np);
 	if (map) {
@@ -411,6 +485,8 @@
 
 	an8855_gsw_nl_init();
 
+	an8855_proc_device_init(gsw);
+
 	an8855_swconfig_init(gsw);
 
 	if (sw->post_init)
@@ -441,6 +517,8 @@
 	an8855_swconfig_destroy(gsw);
 #endif
 
+	an8855_proc_device_exit();
+
 	an8855_gsw_nl_exit();
 
 	an8855_remove_gsw(gsw);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_nl.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_nl.c
index 37e3dad..f5af6d3 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_nl.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_nl.c
@@ -26,8 +26,8 @@
 static const struct nla_policy an8855_nl_cmd_policy[] = {
 	[AN8855_ATTR_TYPE_MESG] = { .type = NLA_STRING },
 	[AN8855_ATTR_TYPE_PHY] = { .type = NLA_S32 },
-	[AN8855_ATTR_TYPE_REG] = { .type = NLA_S32 },
-	[AN8855_ATTR_TYPE_VAL] = { .type = NLA_S32 },
+	[AN8855_ATTR_TYPE_REG] = { .type = NLA_U32 },
+	[AN8855_ATTR_TYPE_VAL] = { .type = NLA_U32 },
 	[AN8855_ATTR_TYPE_DEV_NAME] = { .type = NLA_S32 },
 	[AN8855_ATTR_TYPE_DEV_ID] = { .type = NLA_S32 },
 	[AN8855_ATTR_TYPE_DEVAD] = { .type = NLA_S32 },
@@ -202,15 +202,15 @@
 static int an8855_nl_reply_read(struct genl_info *info, struct gsw_an8855 *gsw)
 {
 	struct sk_buff *rep_skb = NULL;
-	s32 phy, devad, reg;
-	int value;
+	s32 phy, devad;
+	u32 reg = 0;
+	int value = 0;
 	int ret = 0;
 
 	phy = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_PHY, -1);
 	devad = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_DEVAD, -1);
-	reg = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_REG, -1);
 
-	if (reg < 0)
+	if (an8855_nl_get_u32(info, AN8855_ATTR_TYPE_REG, &reg))
 		goto err;
 
 	ret = an8855_nl_prepare_reply(info, AN8855_CMD_READ, &rep_skb);
@@ -226,11 +226,11 @@
 		value = an8855_reg_read(gsw, reg);
 	}
 
-	ret = nla_put_s32(rep_skb, AN8855_ATTR_TYPE_REG, reg);
+	ret = nla_put_u32(rep_skb, AN8855_ATTR_TYPE_REG, reg);
 	if (ret < 0)
 		goto err;
 
-	ret = nla_put_s32(rep_skb, AN8855_ATTR_TYPE_VAL, value);
+	ret = nla_put_u32(rep_skb, AN8855_ATTR_TYPE_VAL, value);
 	if (ret < 0)
 		goto err;
 
@@ -246,18 +246,16 @@
 static int an8855_nl_reply_write(struct genl_info *info, struct gsw_an8855 *gsw)
 {
 	struct sk_buff *rep_skb = NULL;
-	s32 phy, devad, reg;
-	u32 value;
+	s32 phy, devad;
+	u32 value = 0, reg = 0;
 	int ret = 0;
 
 	phy = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_PHY, -1);
 	devad = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_DEVAD, -1);
-	reg = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_REG, -1);
-
-	if (an8855_nl_get_u32(info, AN8855_ATTR_TYPE_VAL, &value))
+	if (an8855_nl_get_u32(info, AN8855_ATTR_TYPE_REG, &reg))
 		goto err;
 
-	if (reg < 0)
+	if (an8855_nl_get_u32(info, AN8855_ATTR_TYPE_VAL, &value))
 		goto err;
 
 	ret = an8855_nl_prepare_reply(info, AN8855_CMD_WRITE, &rep_skb);
@@ -273,11 +271,11 @@
 		an8855_reg_write(gsw, reg, value);
 	}
 
-	ret = nla_put_s32(rep_skb, AN8855_ATTR_TYPE_REG, reg);
+	ret = nla_put_u32(rep_skb, AN8855_ATTR_TYPE_REG, reg);
 	if (ret < 0)
 		goto err;
 
-	ret = nla_put_s32(rep_skb, AN8855_ATTR_TYPE_VAL, value);
+	ret = nla_put_u32(rep_skb, AN8855_ATTR_TYPE_VAL, value);
 	if (ret < 0)
 		goto err;
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_regs.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_regs.h
index 9dbfaeb..32790a1 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_regs.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_regs.h
@@ -85,10 +85,10 @@
 #define PMCR(p)					PORT_MAC_CTRL_REG(p, 0x00)
 #define PMSR(p)					PORT_MAC_CTRL_REG(p, 0x10)
 
-#define GMACCR					(PORT_MAC_CTRL_BASE + 0x30e0)
+#define GMACCR					(PORT_MAC_CTRL_BASE + 0x3e00)
 
-#define MAX_RX_JUMBO_S			2
-#define MAX_RX_JUMBO_M			0x3c
+#define MAX_RX_JUMBO_S			4
+#define MAX_RX_JUMBO_M			0xf0
 #define MAX_RX_PKT_LEN_S		0
 #define MAX_RX_PKT_LEN_M		0x3
 
@@ -172,6 +172,12 @@
 #define STATS_RSFTPC		0xF8
 #define STATS_RXCDPC		0xFC
 
+#define RG_CLK_CPU_ICG		0x10005034
+#define MCU_ENABLE			BIT(3)
+
+#define RG_TIMER_CTL		0x1000a100
+#define WDOG_ENABLE			BIT(25)
+
 #define SYS_CTRL			0x100050C0
 #define SW_SYS_RST			BIT(31)
 
@@ -182,7 +188,10 @@
 #define SYS_INT_STS			0x1021C014
 #define PHY_LC_INT(p)		BIT(p)
 
-#define CKGCR				(0x10213E1C)
-#define CKG_LNKDN_GLB_STOP	(0x01)
-#define CKG_LNKDN_PORT_STOP	(0x02)
+#define CKGCR				0x10213E1C
+#define CKG_LNKDN_GLB_STOP	0x01
+#define CKG_LNKDN_PORT_STOP	0x02
+
+#define PKG_SEL				0x10000094
+#define PAG_SEL_AN8855H		0x2
 #endif /* _AN8855_REGS_H_ */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_swconfig.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_swconfig.c
index e796582..2dd2ffd 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_swconfig.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_swconfig.c
@@ -125,7 +125,7 @@
 {
 	struct gsw_an8855 *gsw = container_of(dev, struct gsw_an8855, swdev);
 
-	if (port >= AN8855_NUM_PORTS)
+	if (port < 0 || port >= AN8855_NUM_PORTS)
 		return -EINVAL;
 
 	*val = an8855_reg_read(gsw, PVID(port));
@@ -138,7 +138,7 @@
 {
 	struct gsw_an8855 *gsw = container_of(dev, struct gsw_an8855, swdev);
 
-	if (port >= AN8855_NUM_PORTS)
+	if (port < 0 || port >= AN8855_NUM_PORTS)
 		return -EINVAL;
 
 	if (pvid < AN8855_MIN_VID || pvid > AN8855_MAX_VID)
@@ -300,18 +300,20 @@
 static u64 get_mib_counter(struct gsw_an8855 *gsw, int i, int port)
 {
 	unsigned int offset;
-	u64 lo, hi, hi2;
+	u64 lo = 0, hi = 0, hi2 = 0;
 
-	offset = an8855_mibs[i].offset;
+	if (i >= 0) {
+		offset = an8855_mibs[i].offset;
 
-	if (an8855_mibs[i].size == 1)
-		return an8855_reg_read(gsw, MIB_COUNTER_REG(port, offset));
+		if (an8855_mibs[i].size == 1)
+			return an8855_reg_read(gsw, MIB_COUNTER_REG(port, offset));
 
-	do {
-		hi = an8855_reg_read(gsw, MIB_COUNTER_REG(port, offset + 4));
-		lo = an8855_reg_read(gsw, MIB_COUNTER_REG(port, offset));
-		hi2 = an8855_reg_read(gsw, MIB_COUNTER_REG(port, offset + 4));
-	} while (hi2 != hi);
+		do {
+			hi = an8855_reg_read(gsw, MIB_COUNTER_REG(port, offset + 4));
+			lo = an8855_reg_read(gsw, MIB_COUNTER_REG(port, offset));
+			hi2 = an8855_reg_read(gsw, MIB_COUNTER_REG(port, offset + 4));
+		} while (hi2 != hi);
+	}
 
 	return (hi << 32) | lo;
 }
@@ -370,7 +372,7 @@
 	an8855_reg_write(gsw, PORTMATRIX(gsw->cpu_port), PORT_MATRIX_M);
 
 	for (i = 0; i < AN8855_NUM_PORTS; i++) {
-		u32 pvc_mode = 0x8100 << STAG_VPID_S;
+		u32 pvc_mode = 0x9100 << STAG_VPID_S;
 
 		if (gsw->port5_cfg.stag_on && i == 5)
 			pvc_mode |= PVC_PORT_STAG | PVC_STAG_REPLACE;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_vlan.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_vlan.c
index 6ef8c99..1a1d751 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_vlan.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/airoha/an8855/an8855_vlan.c
@@ -11,19 +11,19 @@
 		.name = "llllw",
 		.pvids = { 1, 1, 1, 1, 2, 1 },
 		.members = { 0, 0x2f, 0x30 },
-		.etags = { 0, 0, 0x20 },
+		.etags = { 0, 0, 0 },
 		.vids = { 0, 1, 2 },
 	}, {
 		.name = "wllll",
 		.pvids = { 2, 1, 1, 1, 1, 1 },
 		.members = { 0, 0x3e, 0x21 },
-		.etags = { 0, 0, 0x20 },
+		.etags = { 0, 0, 0 },
 		.vids = { 0, 1, 2 },
 	}, {
 		.name = "lwlll",
 		.pvids = { 1, 2, 1, 1, 1, 1 },
 		.members = { 0, 0x3d, 0x22 },
-		.etags = { 0, 0, 0x20 },
+		.etags = { 0, 0, 0 },
 		.vids = { 0, 1, 2 },
 	}, {
 		.name = "lllll",
@@ -124,14 +124,14 @@
 
 	/* set all untag-only ports as transparent and the rest as user port */
 	for (i = 0; i < AN8855_NUM_PORTS; i++) {
-		u32 pvc_mode = 0x8100 << STAG_VPID_S;
+		u32 pvc_mode = 0x9100 << STAG_VPID_S;
 
 		if (untag_ports & BIT(i) && !(tag_ports & BIT(i)))
-			pvc_mode = (0x8100 << STAG_VPID_S) |
+			pvc_mode = (0x9100 << STAG_VPID_S) |
 				(VA_TRANSPARENT_PORT << VLAN_ATTR_S);
 
 		if (gsw->port5_cfg.stag_on && i == 5)
-			pvc_mode = (u32)((0x8100 << STAG_VPID_S) | PVC_PORT_STAG
+			pvc_mode = (u32)((0x9100 << STAG_VPID_S) | PVC_PORT_STAG
 						| PVC_STAG_REPLACE);
 
 		an8855_reg_write(gsw, PVC(i), pvc_mode);
@@ -159,7 +159,8 @@
 		u16 pvid = 0;
 		u32 val;
 
-		if (vlan < AN8855_NUM_VLANS && gsw->vlan_entries[vlan].member)
+		if ((vlan >= 0) && (vlan < AN8855_NUM_VLANS)
+			&& (gsw->vlan_entries[vlan].member))
 			pvid = gsw->vlan_entries[vlan].vid;
 
 		val = an8855_reg_read(gsw, PVID(i));
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-mt7622-backport-nf-hw-offload-framework-and-upstream.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-mt7622-backport-nf-hw-offload-framework-and-upstream.patch
index b0eb98b..ec39c96 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-mt7622-backport-nf-hw-offload-framework-and-upstream.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-mt7622-backport-nf-hw-offload-framework-and-upstream.patch
@@ -3101,10 +3101,10 @@
  static struct dst_entry	*ip6_dst_check(struct dst_entry *dst, u32 cookie);
  static unsigned int	 ip6_default_advmss(const struct dst_entry *dst);
 -static unsigned int	 ip6_mtu(const struct dst_entry *dst);
-+static unsigned int	ip6_mtu(const struct dst_entry *dst);
- static struct dst_entry *ip6_negative_advice(struct dst_entry *);
++static unsigned int    ip6_mtu(const struct dst_entry *dst);
+ static void		ip6_negative_advice(struct sock *sk,
+ 					    struct dst_entry *dst);
  static void		ip6_dst_destroy(struct dst_entry *);
- static void		ip6_dst_ifdown(struct dst_entry *,
 @@ -3125,25 +3125,7 @@ static unsigned int ip6_default_advmss(const struct dst_entry *dst)
  
  static unsigned int ip6_mtu(const struct dst_entry *dst)
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/0005-dts-mt7622-add-gsw.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/0005-dts-mt7622-add-gsw.patch
index fb959ce..73aba88 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/0005-dts-mt7622-add-gsw.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/0005-dts-mt7622-add-gsw.patch
@@ -1,3 +1,5 @@
+diff --git a/arch/arm64/boot/dts/mediatek/mt7622-bananapi-bpi-r64.dts b/arch/arm64/boot/dts/mediatek/mt7622-bananapi-bpi-r64.dts
+index eec9ec1..84df1ca 100644
 --- a/arch/arm64/boot/dts/mediatek/mt7622-bananapi-bpi-r64.dts
 +++ b/arch/arm64/boot/dts/mediatek/mt7622-bananapi-bpi-r64.dts
 @@ -53,6 +53,13 @@
@@ -14,7 +16,7 @@
  	leds {
  		compatible = "gpio-leds";
  
-@@ -146,6 +153,36 @@
+@@ -147,6 +154,36 @@
  	};
  };
  
@@ -51,6 +53,8 @@
  &i2c1 {
  	pinctrl-names = "default";
  	pinctrl-0 = <&i2c1_pins>;
+diff --git a/arch/arm64/boot/dts/mediatek/mt7622-rfb1.dts b/arch/arm64/boot/dts/mediatek/mt7622-rfb1.dts
+index ee57fcc..0fc2dcb 100644
 --- a/arch/arm64/boot/dts/mediatek/mt7622-rfb1.dts
 +++ b/arch/arm64/boot/dts/mediatek/mt7622-rfb1.dts
 @@ -1,7 +1,6 @@
@@ -81,7 +85,7 @@
  	};
  
  	cpus {
-@@ -39,23 +39,36 @@
+@@ -40,23 +39,36 @@
  
  	gpio-keys {
  		compatible = "gpio-keys";
@@ -115,14 +119,14 @@
 +			gpios = <&pio 88 GPIO_ACTIVE_HIGH>;
 + 		};
 + 	};
-+	
++
  	memory@40000000 {
 -		reg = <0 0x40000000 0 0x20000000>;
 +		reg = <0 0x40000000 0 0x40000000>;
+ 		device_type = "memory";
  	};
  
- 	reg_1p8v: regulator-1p8v {
-@@ -101,23 +113,82 @@
+@@ -102,24 +114,83 @@
  };
  
  &eth {
@@ -162,6 +166,7 @@
 -		phy5: ethernet-phy@5 {
 -			reg = <5>;
 -			phy-mode = "sgmii";
+-		};
 +		switch@0 {
 +			compatible = "mediatek,mt7531";
 +			reg = <0>;
@@ -209,24 +214,11 @@
 +					};
 +				};
 +			};
- 		};
++ 		};
  	};
  };
-@@ -185,15 +256,28 @@
  
- &pcie {
- 	pinctrl-names = "default";
--	pinctrl-0 = <&pcie0_pins>;
-+	pinctrl-0 = <&pcie0_pins>, <&pcie1_pins>;
- 	status = "okay";
- 
- 	pcie@0,0 {
- 		status = "okay";
- 	};
-+
-+	pcie@1,0 {
-+		status = "okay";
-+	};
+@@ -195,6 +266,15 @@
  };
  
  &pio {
@@ -242,7 +234,7 @@
  	/* eMMC is shared pin with parallel NAND */
  	emmc_pins_default: emmc-pins-default {
  		mux {
-@@ -460,11 +544,11 @@
+@@ -461,11 +541,11 @@
  };
  
  &sata {
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/0993-arm64-dts-mediatek-Split-PCIe-node-for-MT2712-MT7622.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/0993-arm64-dts-mediatek-Split-PCIe-node-for-MT2712-MT7622.patch
index 4fe0e0f..14999a5 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/0993-arm64-dts-mediatek-Split-PCIe-node-for-MT2712-MT7622.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/0993-arm64-dts-mediatek-Split-PCIe-node-for-MT2712-MT7622.patch
@@ -389,25 +389,20 @@
  					<0 0 0 2 &pcie_intc1 1>,
 --- a/arch/arm64/boot/dts/mediatek/mt7622-rfb1.dts
 +++ b/arch/arm64/boot/dts/mediatek/mt7622-rfb1.dts
-@@ -254,18 +254,16 @@
+@@ -184,14 +184,16 @@
  	};
  };
  
 -&pcie {
 +&pcie0 {
  	pinctrl-names = "default";
--	pinctrl-0 = <&pcie0_pins>, <&pcie1_pins>;
-+	pinctrl-0 = <&pcie0_pins>;
+ 	pinctrl-0 = <&pcie0_pins>;
  	status = "okay";
 +};
  
 -	pcie@0,0 {
 -		status = "okay";
 -	};
--
--	pcie@1,0 {
--		status = "okay";
--	};
 +&pcie1 {
 +	pinctrl-names = "default";
 +	pinctrl-0 = <&pcie1_pins>;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1402-v6.4-mtd-spinand-backport-series-flash.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1402-v6.4-mtd-spinand-backport-series-flash.patch
index d3434eb..e0c59d5 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1402-v6.4-mtd-spinand-backport-series-flash.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1402-v6.4-mtd-spinand-backport-series-flash.patch
@@ -442,7 +442,7 @@
  		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1),
  		     NAND_ECCREQ(4, 512),
  		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-@@ -116,51 +118,194 @@ static const struct spinand_info macroni
+@@ -116,23 +118,76 @@ static const struct spinand_info macroni
  					      &update_cache_variants),
  		     SPINAND_HAS_QE_BIT,
  		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
@@ -455,16 +455,18 @@
  					      &write_cache_variants,
  					      &update_cache_variants),
  		     SPINAND_HAS_QE_BIT,
+-		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
+-	SPINAND_INFO("MX35LF4GE4AD", 0x37,
 +		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
 +				     mx35lf1ge4ab_ecc_get_status)),
 +	SPINAND_INFO("MX35LF4GE4AD",
 +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x37),
-+		     NAND_MEMORG(1, 4096, 128, 64, 2048, 40, 1, 1, 1),
-+		     NAND_ECCREQ(8, 512),
-+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+					      &write_cache_variants,
-+					      &update_cache_variants),
-+		     SPINAND_HAS_QE_BIT,
+ 		     NAND_MEMORG(1, 4096, 128, 64, 2048, 40, 1, 1, 1),
+ 		     NAND_ECCREQ(8, 512),
+ 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+ 					      &write_cache_variants,
+ 					      &update_cache_variants),
+ 		     SPINAND_HAS_QE_BIT,
 +		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
 +				     mx35lf1ge4ab_ecc_get_status)),
 +	SPINAND_INFO("MX35LF1G24AD",
@@ -476,30 +478,16 @@
 +					      &update_cache_variants),
 +		     SPINAND_HAS_QE_BIT,
  		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
--	SPINAND_INFO("MX35LF4GE4AD", 0x37,
--		     NAND_MEMORG(1, 4096, 128, 64, 2048, 40, 1, 1, 1),
+-	SPINAND_INFO("MX35LF2G14AC", 0x20,
 +	SPINAND_INFO("MX35LF2G24AD",
 +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24),
 +		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1),
- 		     NAND_ECCREQ(8, 512),
- 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
- 					      &write_cache_variants,
- 					      &update_cache_variants),
- 		     SPINAND_HAS_QE_BIT,
- 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
--};
--
--static int macronix_spinand_detect(struct spinand_device *spinand)
--{
--	u8 *id = spinand->id.data;
--	int ret;
--
--	/*
--	 * Macronix SPI NAND read ID needs a dummy byte, so the first byte in
--	 * raw_id is garbage.
--	 */
--	if (id[1] != SPINAND_MFR_MACRONIX)
--		return 0;
++		     NAND_ECCREQ(8, 512),
++		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++					      &write_cache_variants,
++					      &update_cache_variants),
++		     SPINAND_HAS_QE_BIT,
++		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
 +	SPINAND_INFO("MX35LF4G24AD",
 +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35),
 +		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 2, 1, 1),
@@ -529,131 +517,133 @@
 +		     SPINAND_HAS_QE_BIT,
 +		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
 +				     mx35lf1ge4ab_ecc_get_status)),
- 
--	ret = spinand_match_and_init(spinand, macronix_spinand_table,
--				     ARRAY_SIZE(macronix_spinand_table),
--				     id[2]);
--	if (ret)
--		return ret;
++
 +	SPINAND_INFO("MX35LF2G14AC",
 +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x20),
-+		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1),
-+		     NAND_ECCREQ(4, 512),
-+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+					      &write_cache_variants,
-+					      &update_cache_variants),
-+		     SPINAND_HAS_QE_BIT,
-+		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-+				     mx35lf1ge4ab_ecc_get_status)),
+ 		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1),
+ 		     NAND_ECCREQ(4, 512),
+ 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -141,7 +196,8 @@ static const struct spinand_info macroni
+ 		     SPINAND_HAS_QE_BIT,
+ 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+ 				     mx35lf1ge4ab_ecc_get_status)),
+-	SPINAND_INFO("MX35UF4G24AD", 0xb5,
 +	SPINAND_INFO("MX35UF4G24AD",
 +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb5),
-+		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 2, 1, 1),
-+		     NAND_ECCREQ(8, 512),
-+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+					      &write_cache_variants,
-+					      &update_cache_variants),
-+		     SPINAND_HAS_QE_BIT,
-+		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-+				     mx35lf1ge4ab_ecc_get_status)),
+ 		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 2, 1, 1),
+ 		     NAND_ECCREQ(8, 512),
+ 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -150,7 +206,8 @@ static const struct spinand_info macroni
+ 		     SPINAND_HAS_QE_BIT,
+ 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+ 				     mx35lf1ge4ab_ecc_get_status)),
+-	SPINAND_INFO("MX35UF4GE4AD", 0xb7,
 +	SPINAND_INFO("MX35UF4GE4AD",
 +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb7),
-+		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
-+		     NAND_ECCREQ(8, 512),
-+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+					      &write_cache_variants,
-+					      &update_cache_variants),
-+		     SPINAND_HAS_QE_BIT,
-+		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-+				     mx35lf1ge4ab_ecc_get_status)),
+ 		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
+ 		     NAND_ECCREQ(8, 512),
+ 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -159,7 +216,8 @@ static const struct spinand_info macroni
+ 		     SPINAND_HAS_QE_BIT,
+ 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+ 				     mx35lf1ge4ab_ecc_get_status)),
+-	SPINAND_INFO("MX35UF2G14AC", 0xa0,
 +	SPINAND_INFO("MX35UF2G14AC",
 +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa0),
-+		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1),
-+		     NAND_ECCREQ(4, 512),
-+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+					      &write_cache_variants,
-+					      &update_cache_variants),
-+		     SPINAND_HAS_QE_BIT,
-+		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-+				     mx35lf1ge4ab_ecc_get_status)),
+ 		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1),
+ 		     NAND_ECCREQ(4, 512),
+ 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -168,7 +226,8 @@ static const struct spinand_info macroni
+ 		     SPINAND_HAS_QE_BIT,
+ 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+ 				     mx35lf1ge4ab_ecc_get_status)),
+-	SPINAND_INFO("MX35UF2G24AD", 0xa4,
 +	SPINAND_INFO("MX35UF2G24AD",
 +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa4),
-+		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1),
-+		     NAND_ECCREQ(8, 512),
-+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+					      &write_cache_variants,
-+					      &update_cache_variants),
-+		     SPINAND_HAS_QE_BIT,
-+		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-+				     mx35lf1ge4ab_ecc_get_status)),
+ 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1),
+ 		     NAND_ECCREQ(8, 512),
+ 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -177,7 +236,8 @@ static const struct spinand_info macroni
+ 		     SPINAND_HAS_QE_BIT,
+ 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+ 				     mx35lf1ge4ab_ecc_get_status)),
+-	SPINAND_INFO("MX35UF2GE4AD", 0xa6,
 +	SPINAND_INFO("MX35UF2GE4AD",
 +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa6),
-+		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
-+		     NAND_ECCREQ(8, 512),
-+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+					      &write_cache_variants,
-+					      &update_cache_variants),
-+		     SPINAND_HAS_QE_BIT,
-+		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-+				     mx35lf1ge4ab_ecc_get_status)),
+ 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
+ 		     NAND_ECCREQ(8, 512),
+ 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -186,7 +246,8 @@ static const struct spinand_info macroni
+ 		     SPINAND_HAS_QE_BIT,
+ 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+ 				     mx35lf1ge4ab_ecc_get_status)),
+-	SPINAND_INFO("MX35UF2GE4AC", 0xa2,
 +	SPINAND_INFO("MX35UF2GE4AC",
 +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa2),
-+		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
-+		     NAND_ECCREQ(4, 512),
-+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+					      &write_cache_variants,
-+					      &update_cache_variants),
-+		     SPINAND_HAS_QE_BIT,
-+		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-+				     mx35lf1ge4ab_ecc_get_status)),
+ 		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
+ 		     NAND_ECCREQ(4, 512),
+ 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -195,7 +256,8 @@ static const struct spinand_info macroni
+ 		     SPINAND_HAS_QE_BIT,
+ 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+ 				     mx35lf1ge4ab_ecc_get_status)),
+-	SPINAND_INFO("MX35UF1G14AC", 0x90,
 +	SPINAND_INFO("MX35UF1G14AC",
 +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x90),
-+		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
-+		     NAND_ECCREQ(4, 512),
-+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+					      &write_cache_variants,
-+					      &update_cache_variants),
-+		     SPINAND_HAS_QE_BIT,
-+		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-+				     mx35lf1ge4ab_ecc_get_status)),
+ 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
+ 		     NAND_ECCREQ(4, 512),
+ 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -204,7 +266,8 @@ static const struct spinand_info macroni
+ 		     SPINAND_HAS_QE_BIT,
+ 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+ 				     mx35lf1ge4ab_ecc_get_status)),
+-	SPINAND_INFO("MX35UF1G24AD", 0x94,
 +	SPINAND_INFO("MX35UF1G24AD",
 +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x94),
-+		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
-+		     NAND_ECCREQ(8, 512),
-+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+					      &write_cache_variants,
-+					      &update_cache_variants),
-+		     SPINAND_HAS_QE_BIT,
-+		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-+				     mx35lf1ge4ab_ecc_get_status)),
+ 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
+ 		     NAND_ECCREQ(8, 512),
+ 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -213,7 +276,8 @@ static const struct spinand_info macroni
+ 		     SPINAND_HAS_QE_BIT,
+ 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+ 				     mx35lf1ge4ab_ecc_get_status)),
+-	SPINAND_INFO("MX35UF1GE4AD", 0x96,
 +	SPINAND_INFO("MX35UF1GE4AD",
 +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x96),
-+		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
-+		     NAND_ECCREQ(8, 512),
-+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+					      &write_cache_variants,
-+					      &update_cache_variants),
-+		     SPINAND_HAS_QE_BIT,
-+		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-+				     mx35lf1ge4ab_ecc_get_status)),
+ 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
+ 		     NAND_ECCREQ(8, 512),
+ 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -222,7 +286,8 @@ static const struct spinand_info macroni
+ 		     SPINAND_HAS_QE_BIT,
+ 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+ 				     mx35lf1ge4ab_ecc_get_status)),
+-	SPINAND_INFO("MX35UF1GE4AC", 0x92,
 +	SPINAND_INFO("MX35UF1GE4AC",
 +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92),
-+		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
-+		     NAND_ECCREQ(4, 512),
-+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+					      &write_cache_variants,
-+					      &update_cache_variants),
-+		     SPINAND_HAS_QE_BIT,
-+		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-+				     mx35lf1ge4ab_ecc_get_status)),
+ 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
+ 		     NAND_ECCREQ(4, 512),
+ 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -231,6 +296,7 @@ static const struct spinand_info macroni
+ 		     SPINAND_HAS_QE_BIT,
+ 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+ 				     mx35lf1ge4ab_ecc_get_status)),
++
+ };
  
--	return 1;
--}
-+};
+ static int macronix_spinand_detect(struct spinand_device *spinand)
+--- a/drivers/mtd/nand/spi/macronix.c
++++ b/drivers/mtd/nand/spi/macronix.c
+@@ -255,7 +255,6 @@ static int macronix_spinand_detect(struc
+ }
  
  static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = {
 -	.detect = macronix_spinand_detect,
  };
+ const struct spinand_manufacturer macronix_spinand_manufacturer = {
  
+--- a/drivers/mtd/nand/spi/macronix.c
++++ b/drivers/mtd/nand/spi/macronix.c
+@@ -326,5 +326,7 @@ static const struct spinand_manufacturer
  const struct spinand_manufacturer macronix_spinand_manufacturer = {
  	.id = SPINAND_MFR_MACRONIX,
  	.name = "Macronix",
@@ -661,6 +651,7 @@
 +	.nchips = ARRAY_SIZE(macronix_spinand_table),
  	.ops = &macronix_spinand_manuf_ops,
  };
+ 
 Index: linux-5.4.260/drivers/mtd/nand/spi/micron.c
 ===================================================================
 --- linux-5.4.260.orig/drivers/mtd/nand/spi/micron.c
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1716-v6.6-net-phy-add-phylink-pcs_enable-and-pcs_disable.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1716-v6.6-net-phy-add-phylink-pcs_enable-and-pcs_disable.patch
index 29306fd..a3f7134 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1716-v6.6-net-phy-add-phylink-pcs_enable-and-pcs_disable.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1716-v6.6-net-phy-add-phylink-pcs_enable-and-pcs_disable.patch
@@ -38,7 +38,7 @@
 -	if (pcs_changed)
 +	if (pcs_changed) {
 +		if (pl->pcs && pl->pcs->ops->pcs_disable)
-+			pcs->ops->pcs_disable(pl->pcs);
++			pl->pcs->ops->pcs_disable(pl->pcs);
 +
  		pl->pcs = pcs;
 +	}
@@ -47,7 +47,7 @@
  
 +	if (pl->pcs_state == PCS_STATE_STARTING || pcs_changed) {
 +		if (pl->pcs && pl->pcs->ops->pcs_enable)
-+			err = pcs->ops->pcs_enable(pl->pcs);
++			err = pl->pcs->ops->pcs_enable(pl->pcs);
 +	}
 +
  	if (pl->pcs) {
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1802-v6.4-backport-pinctrl-pinconf-setting-combo.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1802-v6.4-backport-pinctrl-pinconf-setting-combo.patch
index 43ca0c5..cea4fe8 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1802-v6.4-backport-pinctrl-pinconf-setting-combo.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1802-v6.4-backport-pinctrl-pinconf-setting-combo.patch
@@ -109,109 +109,82 @@
  					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;
+@@ -6,7 +6,6 @@
+  *
+  */
+ 
+-#include <dt-bindings/pinctrl/mt65xx.h>
+ #include <linux/device.h>
+ #include <linux/err.h>
+ #include <linux/gpio/driver.h>
+@@ -67,44 +66,34 @@ static int mtk_hw_pin_field_lookup(struc
+ 				   const struct mtk_pin_desc *desc,
+ 				   int field, struct mtk_pin_field *pfd)
+ {
+-	const struct mtk_pin_field_calc *c;
++	const struct mtk_pin_field_calc *c, *e;
+ 	const struct mtk_pin_reg_calc *rc;
+-	int start = 0, end, check;
+-	bool found = false;
+ 	u32 bits;
+ 
+ 	if (hw->soc->reg_cal && hw->soc->reg_cal[field].range) {
+ 		rc = &hw->soc->reg_cal[field];
+ 	} else {
+ 		dev_dbg(hw->dev,
+-			"Not support field %d for this soc\n", field);
++			"Not support field %d for pin %d (%s)\n",
++			field, desc->number, desc->name);
+ 		return -ENOTSUPP;
+ 	}
+ 
+-	end = rc->nranges - 1;
++	c = rc->range;
++	e = c + rc->nranges;
+ 
+-	while (start <= end) {
+-		check = (start + end) >> 1;
+-		if (desc->number >= rc->range[check].s_pin
+-		 && desc->number <= rc->range[check].e_pin) {
+-			found = true;
+-			break;
+-		} else if (start == end)
++	while (c < e) {
++		if (desc->number >= c->s_pin && desc->number <= c->e_pin)
+ 			break;
+-		else if (desc->number < rc->range[check].s_pin)
+-			end = check - 1;
+-		else
+-			start = check + 1;
++		c++;
+ 	}
+ 
+-	if (!found) {
++	if (c >= e) {
+ 		dev_dbg(hw->dev, "Not support field %d for pin = %d (%s)\n",
+ 			field, desc->number, desc->name);
+ 		return -ENOTSUPP;
+ 	}
+ 
+-	c = rc->range + check;
+-
+ 	if (c->i_base > hw->nbase - 1) {
+ 		dev_err(hw->dev,
+ 			"Invalid base for field %d for pin = %d (%s)\n",
+@@ -193,9 +182,6 @@ int mtk_hw_set_value(struct mtk_pinctrl
+ 	if (err)
+ 		return err;
+ 
+-	if (value < 0 || value > pf.mask)
+-		return -EINVAL;
+-
+ 	if (!pf.next)
+ 		mtk_rmw(hw, pf.index, pf.offset, pf.mask << pf.bitpos,
+ 			(value & pf.mask) << pf.bitpos);
+@@ -619,6 +605,186 @@ out:
+ 	return err;
  }
  
-+/* 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)
@@ -392,105 +365,59 @@
 +	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)
-+{
+ static int mtk_pinconf_bias_get_pu_pd(struct mtk_pinctrl *hw,
+ 				const struct mtk_pin_desc *desc,
+ 				u32 *pullup, u32 *enable)
+@@ -700,45 +866,43 @@ 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;
+-
+-	err = mtk_pinconf_bias_set_pu_pd(hw, desc, pullup, arg);
+-	if (!err)
+-		goto out;
+-
+-	err = mtk_pinconf_bias_set_pullsel_pullen(hw, desc, pullup, arg);
+-	if (!err)
+-		goto out;
+-
+-	err = mtk_pinconf_bias_set_pupd_r1_r0(hw, desc, pullup, arg);
+-
+-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;
 +	int err = -ENOTSUPP;
 +	u32 try_all_type;
-+
+ 
+-	err = mtk_pinconf_bias_get_pu_pd(hw, desc, pullup, enable);
+-	if (!err)
+-		goto out;
 +	if (hw->soc->pull_type)
 +		try_all_type = hw->soc->pull_type[desc->number];
 +	else
 +		try_all_type = MTK_PULL_TYPE_MASK;
-+
+ 
+-	err = mtk_pinconf_bias_get_pullsel_pullen(hw, desc, pullup, enable);
+-	if (!err)
+-		goto out;
 +	if (try_all_type & MTK_PULL_RSEL_TYPE) {
 +		err = mtk_pinconf_bias_get_rsel(hw, desc, pullup, enable);
 +		if (!err)
 +			return err;
 +	}
-+
+ 
+-	err = mtk_pinconf_bias_get_pupd_r1_r0(hw, desc, pullup, enable);
 +	if (try_all_type & MTK_PULL_PU_PD_TYPE) {
 +		err = mtk_pinconf_bias_get_pu_pd(hw, desc, pullup, enable);
 +		if (!err)
@@ -506,14 +433,44 @@
 +
 +	if (try_all_type & MTK_PULL_PUPD_R1R0_TYPE)
 +		err = mtk_pinconf_bias_get_pupd_r1_r0(hw, desc, pullup, enable);
-+
-+	return err;
-+}
+ 
+-out:
+ 	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)
+@@ -831,18 +995,6 @@ int mtk_pinconf_drive_get_rev1(struct mt
+ 	return 0;
+ }
+ 
+-int mtk_pinconf_drive_set_raw(struct mtk_pinctrl *hw,
+-			       const struct mtk_pin_desc *desc, u32 arg)
+-{
+-	return mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_DRV, arg);
+-}
+-
+-int mtk_pinconf_drive_get_raw(struct mtk_pinctrl *hw,
+-			       const struct mtk_pin_desc *desc, int *val)
+-{
+-	return mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_DRV, val);
+-}
+-
+ int mtk_pinconf_adv_pull_set(struct mtk_pinctrl *hw,
+ 			     const struct mtk_pin_desc *desc, bool pullup,
+ 			     u32 arg)
+@@ -876,9 +1028,7 @@ int mtk_pinconf_adv_pull_set(struct mtk_
+ 			if (err)
+ 				return err;
+ 		} else {
+-			err = mtk_pinconf_bias_set_rev1(hw, desc, pullup);
+-			if (err)
+-				err = mtk_pinconf_bias_set(hw, desc, pullup);
++			return -ENOTSUPP;
+ 		}
+ 	}
+ 
 --- a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.h
 +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.h
 @@ -17,6 +17,34 @@
@@ -593,19 +550,22 @@
  
  	/* Specific pinconfig operations */
  	int (*bias_disable_set)(struct mtk_pinctrl *hw,
-@@ -215,7 +264,10 @@ struct mtk_pin_soc {
+@@ -215,12 +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,
+ 	int (*bias_set_combo)(struct mtk_pinctrl *hw,
+-			const struct mtk_pin_desc *desc, u32 pullup, u32 arg);
 +			      const struct mtk_pin_desc *desc, u32 pullup, u32 arg);
-+	int (*bias_get_combo)(struct mtk_pinctrl *hw,
+ 	int (*bias_get_combo)(struct mtk_pinctrl *hw,
+-			const struct mtk_pin_desc *desc, u32 *pullup, u32 *arg);
+-
 +			      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 {
+@@ -251,6 +298,10 @@ struct mtk_pinctrl {
  	struct mtk_eint			*eint;
  	struct mtk_pinctrl_group	*groups;
  	const char          **grp_names;
@@ -616,7 +576,18 @@
  };
  
  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
+@@ -282,28 +333,22 @@ int mtk_pinconf_bias_set_rev1(struct mtk
+ int mtk_pinconf_bias_get_rev1(struct mtk_pinctrl *hw,
+ 			      const struct mtk_pin_desc *desc, bool pullup,
+ 			      int *res);
+-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(struct mtk_pinctrl *hw,
  			  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);
@@ -630,3 +601,13 @@
  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,
+ 			       const struct mtk_pin_desc *desc, int *val);
+ 
+-int mtk_pinconf_drive_set_raw(struct mtk_pinctrl *hw,
+-			       const struct mtk_pin_desc *desc, u32 arg);
+-int mtk_pinconf_drive_get_raw(struct mtk_pinctrl *hw,
+-			       const struct mtk_pin_desc *desc, int *val);
+-
+ int mtk_pinconf_adv_pull_set(struct mtk_pinctrl *hw,
+ 			     const struct mtk_pin_desc *desc, bool pullup,
+ 			     u32 arg);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2377-integrate-ONFI-and-CASN-manipulation.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2377-integrate-ONFI-and-CASN-manipulation.patch
new file mode 100644
index 0000000..c2b76f9
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2377-integrate-ONFI-and-CASN-manipulation.patch
@@ -0,0 +1,162 @@
+--- a/drivers/mtd/nand/raw/nand_onfi.c
++++ b/drivers/mtd/nand/raw/nand_onfi.c
+@@ -12,20 +12,14 @@
+  * This file contains all ONFI helpers.
+  */
+ 
++#include <linux/mtd/param.h>
+ #include <linux/slab.h>
+ 
+ #include "internals.h"
+ 
+ u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
+ {
+-	int i;
+-	while (len--) {
+-		crc ^= *p++ << 8;
+-		for (i = 0; i < 8; i++)
+-			crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
+-	}
+-
+-	return crc;
++	return nanddev_crc16(crc, p, len);
+ }
+ 
+ /* Parse the Extended Parameter Page. */
+@@ -104,37 +98,6 @@ ext_out:
+ }
+ 
+ /*
+- * Recover data with bit-wise majority
+- */
+-static void nand_bit_wise_majority(const void **srcbufs,
+-				   unsigned int nsrcbufs,
+-				   void *dstbuf,
+-				   unsigned int bufsize)
+-{
+-	int i, j, k;
+-
+-	for (i = 0; i < bufsize; i++) {
+-		u8 val = 0;
+-
+-		for (j = 0; j < 8; j++) {
+-			unsigned int cnt = 0;
+-
+-			for (k = 0; k < nsrcbufs; k++) {
+-				const u8 *srcbuf = srcbufs[k];
+-
+-				if (srcbuf[i] & BIT(j))
+-					cnt++;
+-			}
+-
+-			if (cnt > nsrcbufs / 2)
+-				val |= BIT(j);
+-		}
+-
+-		((u8 *)dstbuf)[i] = val;
+-	}
+-}
+-
+-/*
+  * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
+  */
+ int nand_onfi_detect(struct nand_chip *chip)
+@@ -184,7 +147,7 @@ int nand_onfi_detect(struct nand_chip *c
+ 		const void *srcbufs[3] = {p, p + 1, p + 2};
+ 
+ 		pr_warn("Could not find a valid ONFI parameter page, trying bit-wise majority to recover it\n");
+-		nand_bit_wise_majority(srcbufs, ARRAY_SIZE(srcbufs), p,
++		nanddev_bit_wise_majority(srcbufs, ARRAY_SIZE(srcbufs), p,
+ 				       sizeof(*p));
+ 
+ 		if (onfi_crc16(ONFI_CRC_BASE, (u8 *)p, 254) !=
+--- /dev/null
++++ b/drivers/mtd/nand/param.c
+@@ -0,0 +1,52 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) 2023 - Mediatek
++ *
++ * Author: SkyLake <SkyLake.Huang@mediatek.com>
++ */
++
++#include <linux/mtd/param.h>
++
++u16 nanddev_crc16(u16 crc, u8 const *p, size_t len)
++{
++	int i;
++
++	while (len--) {
++		crc ^= *p++ << 8;
++		for (i = 0; i < 8; i++)
++			crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
++	}
++
++	return crc;
++}
++
++/*
++ * Recover data with bit-wise majority
++ */
++void nanddev_bit_wise_majority(const void **srcbufs,
++				   unsigned int nsrcbufs,
++				   void *dstbuf,
++				   unsigned int bufsize)
++{
++	int i, j, k;
++
++	for (i = 0; i < bufsize; i++) {
++		u8 val = 0;
++
++		for (j = 0; j < 8; j++) {
++			unsigned int cnt = 0;
++
++			for (k = 0; k < nsrcbufs; k++) {
++				const u8 *srcbuf = srcbufs[k];
++
++				if (srcbuf[i] & BIT(j))
++					cnt++;
++			}
++
++			if (cnt > nsrcbufs / 2)
++				val |= BIT(j);
++		}
++
++		((u8 *)dstbuf)[i] = val;
++	}
++}
+--- /dev/null
++++ b/include/linux/mtd/param.h
+@@ -0,0 +1,22 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) 2023 - Mediatek
++ *
++ * Author: SkyLake <SkyLake.Huang@mediatek.com>
++ */
++
++#ifndef __LINUX_NAND_PARAM
++#define __LINUX_NAND_PARAM
++
++#include <linux/bitops.h>
++#include <linux/types.h>
++#include <stddef.h>
++
++u16 nanddev_crc16(u16 crc, u8 const *p, size_t len);
++void nanddev_bit_wise_majority(const void **srcbufs,
++				   unsigned int nsrcbufs,
++				   void *dstbuf,
++				   unsigned int bufsize);
++
++#endif /* __LINUX_NAND_PARAM */
++
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -1,6 +1,6 @@
+ # SPDX-License-Identifier: GPL-2.0
+ 
+-nandcore-objs := core.o bbt.o
++nandcore-objs := core.o bbt.o param.o
+ obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
+ 
+ obj-y	+= onenand/
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2378-Add-CASN-support-for-SPI-NAND.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2378-Add-CASN-support-for-SPI-NAND.patch
new file mode 100644
index 0000000..fbf328f
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2378-Add-CASN-support-for-SPI-NAND.patch
@@ -0,0 +1,1196 @@
+From f588443bf5be9b8fda24fbdc80187ce8f015dcf1 Mon Sep 17 00:00:00 2001
+From: "SkyLake.Huang" <skylake.huang@mediatek.com>
+Date: Mon, 31 Jul 2023 10:40:09 +0800
+Subject: [PATCH] Add CASN support for SPI-NAND
+
+Signed-off-by: SkyLake.Huang <skylake.huang@mediatek.com>
+---
+ drivers/mtd/nand/spi/core.c | 569 +++++++++++++++++++++++++++++++++++-
+ include/linux/mtd/onfi.h    | 153 +++++++++-
+ include/linux/mtd/spinand.h |  59 +++-
+ 3 files changed, 772 insertions(+), 9 deletions(-)
+
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -9,10 +9,13 @@
+ 
+ #define pr_fmt(fmt)	"spi-nand: " fmt
+ 
++#include <linux/bitfield.h>
+ #include <linux/device.h>
+ #include <linux/jiffies.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
++#include <linux/mtd/casn.h>
++#include <linux/mtd/param.h>
+ #include <linux/mtd/spinand.h>
+ #include <linux/of.h>
+ #include <linux/slab.h>
+@@ -20,12 +23,15 @@
+ #include <linux/spi/spi.h>
+ #include <linux/spi/spi-mem.h>
+ 
++extern void sanitize_string(uint8_t *s, size_t len);
++
+ static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)
+ {
+-	struct spi_mem_op op = SPINAND_GET_FEATURE_OP(reg,
+-						      spinand->scratchbuf);
++	struct spi_mem_op op;
+ 	int ret;
+ 
++	op = (struct spi_mem_op) SPINAND_GET_FEATURE_OP(reg,
++							spinand->scratchbuf);
+ 	ret = spi_mem_exec_op(spinand->spimem, &op);
+ 	if (ret)
+ 		return ret;
+@@ -36,8 +42,10 @@ static int spinand_read_reg_op(struct sp
+ 
+ static int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val)
+ {
+-	struct spi_mem_op op = SPINAND_SET_FEATURE_OP(reg,
+-						      spinand->scratchbuf);
++	struct spi_mem_op op;
++
++	op = (struct spi_mem_op) SPINAND_SET_FEATURE_OP(reg,
++							spinand->scratchbuf);
+ 
+ 	*spinand->scratchbuf = val;
+ 	return spi_mem_exec_op(spinand->spimem, &op);
+@@ -402,6 +410,62 @@ static int spinand_lock_block(struct spi
+ 	return spinand_write_reg_op(spinand, REG_BLOCK_LOCK, lock);
+ }
+ 
++static size_t eccsr_none_op(size_t val, size_t mask) { return val; }
++static size_t eccsr_and_op(size_t val, size_t mask) { return val & mask; }
++static size_t eccsr_add_op(size_t val, size_t mask) { return val + mask; }
++static size_t eccsr_minus_op(size_t val, size_t mask) { return val - mask; }
++static size_t eccsr_mul_op(size_t val, size_t mask) { return val * mask; }
++
++static void spinand_read_adv_ecc(struct spinand_device *spinand,
++				 struct spi_mem_op *ops, u16 *eccsr,
++				 u16 mask, u8 shift,
++				 u8 pre_op, u8 pre_mask)
++{
++	u8 *p = spinand->scratchbuf;
++
++	spi_mem_exec_op(spinand->spimem, ops);
++
++	if (likely(mask <= 0xff))
++		*eccsr += (*p & mask) >> shift;
++	else
++		*eccsr += (((*p << 8) | (*p+1)) & mask) >> shift;
++
++	*eccsr = spinand->eccsr_math_op[pre_op](*eccsr, pre_mask);
++}
++
++static int spinand_casn_get_ecc_status(struct spinand_device *spinand, u8 status)
++{
++	struct mtd_info *mtd = spinand_to_mtd(spinand);
++	struct CASN_ADVECC *ah = spinand->advecc_high;
++	struct CASN_ADVECC *al = spinand->advecc_low;
++	u16 eccsr_high = 0;
++	u16 eccsr_low = 0;
++	u32 eccsr = 0;
++
++	if (al->cmd) {
++		spinand_read_adv_ecc(spinand,
++				     spinand->advecc_low_ops, &eccsr_low,
++				     al->mask, al->shift,
++				     al->pre_op, al->pre_mask);
++		eccsr += eccsr_low;
++	}
++	if (ah->cmd) {
++		spinand_read_adv_ecc(spinand,
++				     spinand->advecc_high_ops, &eccsr_high,
++				     ah->mask, ah->shift,
++				     ah->pre_op, ah->pre_mask);
++		eccsr += eccsr_high << spinand->advecc_low_bitcnt;
++	}
++
++	if (eccsr == spinand->advecc_noerr_status)
++		return 0;
++	else if (eccsr == spinand->advecc_uncor_status)
++		return -EBADMSG;
++	eccsr = spinand->eccsr_math_op[spinand->advecc_post_op](eccsr, spinand->advecc_post_mask);
++
++	return eccsr > mtd->ecc_strength ? mtd->ecc_strength : eccsr;
++}
++
+ static int spinand_check_ecc_status(struct spinand_device *spinand, u8 status)
+ {
+ 	struct nand_device *nand = spinand_to_nand(spinand);
+@@ -792,10 +856,11 @@ static int spinand_manufacturer_match(st
+ }
+ 
+ int spinand_cal_read(void *priv, u32 *addr, int addrlen, u8 *buf, int readlen) {
+-	int ret;
+-	u8 status;
+ 	struct spinand_device *spinand = (struct spinand_device *)priv;
+ 	struct device *dev = &spinand->spimem->spi->dev;
++	struct spi_mem_op op;
++	u8 status;
++	int ret;
+ 
+ 	typedef struct nand_pos my_pos;
+ 	my_pos pos;
+@@ -838,13 +903,246 @@ int spinand_cal_read(void *priv, u32 *ad
+ 	if (ret < 0)
+ 		return ret;
+ 
+-	struct spi_mem_op op = SPINAND_PAGE_READ_FROM_CACHE_OP(
++	op = (struct spi_mem_op) SPINAND_PAGE_READ_FROM_CACHE_OP(
+ 		false, 0, 1, buf, readlen);
+ 	ret = spi_mem_exec_op(spinand->spimem, &op);
+ 
+ 	return 0;
+ }
+ 
++static int spinand_check_casn_validity(struct spinand_device *spinand,
++				       struct nand_casn *casn)
++{
++	struct device *dev = &spinand->spimem->spi->dev;
++
++	if (be32_to_cpu(casn->bits_per_cell) != 1) {
++		dev_err(dev, "[CASN] bits-per-cell must be 1\n");
++		return -EINVAL;
++	}
++
++	switch (be32_to_cpu(casn->bytes_per_page)) {
++	case 2048:
++	case 4096:
++		break;
++	default:
++		dev_err(dev, "[CASN] page size must be 2048/4096\n");
++		return -EINVAL;
++	}
++
++	switch (be32_to_cpu(casn->spare_bytes_per_page)) {
++	case 64:
++	case 96:
++	case 128:
++	case 256:
++		break;
++	default:
++		dev_err(dev, "[CASN] spare size must be 64/128/256\n");
++		return -EINVAL;
++	}
++
++	switch (be32_to_cpu(casn->pages_per_block)) {
++	case 64:
++	case 128:
++		break;
++	default:
++		dev_err(dev, "[CASN] pages_per_block must be 64/128\n");
++		return -EINVAL;
++	}
++
++	switch (be32_to_cpu(casn->blocks_per_lun)) {
++	case 1024:
++		if (be32_to_cpu(casn->max_bb_per_lun) != 20) {
++			dev_err(dev, "[CASN] max_bb_per_lun must be 20 when blocks_per_lun is 1024\n");
++			return -EINVAL;
++		}
++		break;
++	case 2048:
++		if (be32_to_cpu(casn->max_bb_per_lun) != 40) {
++			dev_err(dev, "[CASN] max_bb_per_lun must be 40 when blocks_per_lun is 2048\n");
++			return -EINVAL;
++		}
++		break;
++	case 4096:
++		if (be32_to_cpu(casn->max_bb_per_lun) != 80) {
++			dev_err(dev, "[CASN] max_bb_per_lun must be 80 when blocks_per_lun is 4096\n");
++			return -EINVAL;
++		}
++		break;
++	default:
++		dev_err(dev, "[CASN] blocks_per_lun must be 1024/2048/4096\n");
++		return -EINVAL;
++	}
++
++	switch (be32_to_cpu(casn->planes_per_lun)) {
++	case 1:
++	case 2:
++		break;
++	default:
++		dev_err(dev, "[CASN] planes_per_lun must be 1/2\n");
++		return -EINVAL;
++	}
++
++	switch (be32_to_cpu(casn->luns_per_target)) {
++	case 1:
++	case 2:
++		break;
++	default:
++		dev_err(dev, "[CASN] luns_per_target must be 1/2\n");
++		return -EINVAL;
++	}
++
++	switch (be32_to_cpu(casn->total_target)) {
++	case 1:
++	case 2:
++		break;
++	default:
++		dev_err(dev, "[CASN] ntargets must be 1/2\n");
++		return -EINVAL;
++	}
++
++	if (casn->casn_oob.layout_type != OOB_CONTINUOUS &&
++	    casn->casn_oob.layout_type != OOB_DISCRETE) {
++		dev_err(dev, "[CASN] OOB layout type isn't correct.\n");
++		return -EINVAL;
++	}
++
++	if (casn->ecc_status_high.status_nbytes > 2 ||
++	    casn->ecc_status_low.status_nbytes > 2) {
++		dev_err(dev, "[CASN] ADVECC status nbytes must be no more than 2\n");
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++static int spinand_check_casn(struct spinand_device *spinand,
++			struct nand_casn *casn, unsigned int *sel)
++{
++	struct device *dev = &spinand->spimem->spi->dev;
++	uint16_t crc = be16_to_cpu(casn->crc);
++	uint16_t crc_compute;
++	int ret = 0;
++	int i;
++
++	/* There are 3 copies of CASN Pages V1. Choose one avabilable copy
++	 * first. If none of the copies is available, try to recover.
++	 */
++	for (i = 0; i < CASN_PAGE_V1_COPIES; i++) {
++		if (be32_to_cpu(casn[i].signature) != CASN_SIGNATURE) {
++			ret = -EINVAL;
++			continue;
++		}
++		crc_compute = nanddev_crc16(CASN_CRC_BASE, (u8 *)(casn + i),
++					    SPINAND_CASN_V1_CRC_OFS);
++		dev_dbg(dev, "CASN COPY %d CRC read: 0x%x, compute: 0x%x\n",
++			i, crc, crc_compute);
++		if (crc != crc_compute) {
++			ret = -EBADMSG;
++			continue;
++		}
++		ret = spinand_check_casn_validity(spinand, casn + i);
++		if (ret < 0)
++			continue;
++		*sel = i;
++		break;
++	}
++
++	if (i == CASN_PAGE_V1_COPIES && ret == -EBADMSG) {
++		const void *srcbufs[CASN_PAGE_V1_COPIES];
++		int j;
++
++		for (j = 0; j < CASN_PAGE_V1_COPIES; j++)
++			srcbufs[j] = casn + j;
++		dev_info(dev, "Couldn't find a valid CASN page, try bitwise majority to recover it\n");
++		nanddev_bit_wise_majority(srcbufs, CASN_PAGE_V1_COPIES, casn,
++					  sizeof(*casn));
++		crc_compute = nanddev_crc16(CASN_CRC_BASE, (uint8_t *)casn,
++					    SPINAND_CASN_V1_CRC_OFS);
++		if (crc_compute != crc) {
++			dev_err(dev, "CASN page recovery failed, aborting\n");
++			return -EBADMSG;
++		}
++		ret = spinand_check_casn_validity(spinand, casn + i);
++		if (ret < 0)
++			return ret;
++		dev_info(dev, "CASN page recovery succeeded\n");
++		*sel = 0;
++	}
++
++	return ret;
++}
++
++static int spinand_casn_detect(struct spinand_device *spinand,
++			       struct nand_casn *casn, unsigned int *sel)
++{
++	struct device *dev = &spinand->spimem->spi->dev;
++	uint8_t casn_offset[3] = {0x0, 0x1, 0x4};
++	struct nand_page_io_req req;
++	struct spi_mem_op op;
++	struct nand_pos pos;
++	int check_ret = 0;
++	uint8_t status;
++	int final_ret;
++	int ret = 0;
++	u8 cfg_reg;
++	int i;
++
++	ret = spinand_read_reg_op(spinand, REG_CFG, &cfg_reg);
++	if (ret)
++		return ret;
++
++	ret = spinand_write_reg_op(spinand, REG_CFG, cfg_reg | BIT(6));
++	if (ret)
++		return ret;
++
++	memset(&pos, 0, sizeof(pos));
++
++	req = (struct nand_page_io_req){
++		.pos = pos,
++		.dataoffs = 0,
++		.datalen = 256 * CASN_PAGE_V1_COPIES,
++		.databuf.in = (u8 *)casn,
++		.mode = MTD_OPS_AUTO_OOB,
++	};
++
++	for (i = 0; i < sizeof(casn_offset)/sizeof(uint8_t); i++) {
++		req.pos.page = casn_offset[i];
++		ret = spinand_load_page_op(spinand, &req);
++		if (ret)
++			goto finish;
++
++		ret = spinand_wait(spinand, &status);
++		if (ret < 0)
++			goto finish;
++
++		op = (struct spi_mem_op) SPINAND_PAGE_READ_FROM_CACHE_OP(
++			false, 768, 1, (u8 *)casn, 256 * CASN_PAGE_V1_COPIES);
++		ret = spi_mem_exec_op(spinand->spimem, &op);
++		if (ret < 0)
++			goto finish;
++
++		check_ret = spinand_check_casn(spinand, casn, sel);
++		if (!check_ret)
++			break;
++	}
++
++finish:
++	/* We need to restore configuration register. */
++	final_ret = spinand_write_reg_op(spinand, REG_CFG, cfg_reg);
++	if (final_ret)
++		return final_ret;
++
++	if (check_ret) {
++		dev_err(dev, "CASN page check failed\n");
++		return check_ret;
++	}
++
++	if (ret)
++		dev_err(dev, "CASN page read failed\n");
++
++	return ret;
++}
++
+ static int spinand_id_detect(struct spinand_device *spinand)
+ {
+ 	u8 *id = spinand->id.data;
+@@ -876,7 +1174,7 @@ static int spinand_id_detect(struct spin
+ 
+ static int spinand_manufacturer_init(struct spinand_device *spinand)
+ {
+-	if (spinand->manufacturer->ops->init)
++	if (!spinand->use_casn && spinand->manufacturer->ops->init)
+ 		return spinand->manufacturer->ops->init(spinand);
+ 
+ 	return 0;
+@@ -885,7 +1183,7 @@ static int spinand_manufacturer_init(str
+ static void spinand_manufacturer_cleanup(struct spinand_device *spinand)
+ {
+ 	/* Release manufacturer private data */
+-	if (spinand->manufacturer->ops->cleanup)
++	if (!spinand->use_casn && spinand->manufacturer->ops->cleanup)
+ 		return spinand->manufacturer->ops->cleanup(spinand);
+ }
+ 
+@@ -988,37 +1286,456 @@ int spinand_match_and_init(struct spinan
+ 	return -ENOTSUPP;
+ }
+ 
++static int spinand_casn_ooblayout_ecc(struct mtd_info *mtd, int section,
++				       struct mtd_oob_region *region)
++{
++	struct spinand_device *spinand = mtd_to_spinand(mtd);
++	int sectionp;
++	struct CASN_OOB *co = spinand->casn_oob;
++
++	sectionp = spinand->base.memorg.pagesize/mtd->ecc_step_size;
++	if (section >= sectionp)
++		return -ERANGE;
++
++	if (co->layout_type == OOB_DISCRETE) {
++		region->offset = co->ecc_parity_start +
++				 (co->free_length + co->ecc_parity_space)
++				 * section;
++	} else if (co->layout_type == OOB_CONTINUOUS) {
++		region->offset = co->ecc_parity_start + co->ecc_parity_space * section;
++	}
++	region->length = co->ecc_parity_real_length;
++
++	return 0;
++}
++
++static int spinand_casn_ooblayout_free(struct mtd_info *mtd, int section,
++					struct mtd_oob_region *region)
++{
++	struct spinand_device *spinand = mtd_to_spinand(mtd);
++	int sectionp;
++	struct CASN_OOB *co = spinand->casn_oob;
++
++	sectionp = spinand->base.memorg.pagesize/mtd->ecc_step_size;
++	if (section >= sectionp)
++		return -ERANGE;
++
++	if (!section) {
++		region->offset = co->free_start + co->bbm_length;
++		region->length = co->free_length - co->bbm_length;
++	} else {
++		if (co->layout_type == OOB_DISCRETE) {
++			region->offset = co->free_start +
++					 (co->free_length +
++					  co->ecc_parity_space) * section;
++		} else if (co->layout_type == OOB_CONTINUOUS) {
++			region->offset = co->free_start +
++					 co->free_length * section;
++		}
++		region->length = co->free_length;
++	}
++
++	return 0;
++}
++
++static const struct mtd_ooblayout_ops spinand_casn_ooblayout = {
++	.ecc = spinand_casn_ooblayout_ecc,
++	.free = spinand_casn_ooblayout_free,
++};
++
++static int spinand_set_read_op_variants(struct spinand_device *spinand,
++					struct nand_casn *casn)
++{
++	struct spinand_op_variants casn_read_cache_variants;
++	u16 sdr_read_cap = be16_to_cpu(casn->sdr_read_cap);
++	struct spi_mem_op *read_ops;
++	const struct spi_mem_op *op;
++	int i = 0;
++
++	read_ops = devm_kzalloc(&spinand->spimem->spi->dev,
++				sizeof(struct spi_mem_op) *
++				hweight16(sdr_read_cap),
++				GFP_KERNEL);
++	if (!read_ops)
++		return -ENOMEM;
++
++	if (FIELD_GET(SDR_READ_1_4_4, sdr_read_cap)) {
++		read_ops[i] = (struct spi_mem_op)
++			SPINAND_CASN_PAGE_READ_FROM_CACHE_QUADIO_OP(
++				casn->sdr_read_1_4_4.addr_nbytes, 0,
++				casn->sdr_read_1_4_4.dummy_nbytes, NULL, 0
++			);
++		i++;
++	}
++	if (FIELD_GET(SDR_READ_1_1_4, sdr_read_cap)) {
++		read_ops[i] = (struct spi_mem_op)
++			SPINAND_CASN_PAGE_READ_FROM_CACHE_X4_OP(
++				casn->sdr_read_1_1_4.addr_nbytes, 0,
++				casn->sdr_read_1_1_4.dummy_nbytes, NULL, 0
++			);
++		i++;
++	}
++	if (FIELD_GET(SDR_READ_1_2_2, sdr_read_cap)) {
++		read_ops[i] = (struct spi_mem_op)
++			SPINAND_CASN_PAGE_READ_FROM_CACHE_DUALIO_OP(
++				casn->sdr_read_1_2_2.addr_nbytes, 0,
++				casn->sdr_read_1_2_2.dummy_nbytes, NULL, 0
++			);
++		i++;
++	}
++	if (FIELD_GET(SDR_READ_1_1_2, sdr_read_cap)) {
++		read_ops[i] = (struct spi_mem_op)
++			SPINAND_CASN_PAGE_READ_FROM_CACHE_X2_OP(
++				casn->sdr_read_1_1_2.addr_nbytes, 0,
++				casn->sdr_read_1_1_2.dummy_nbytes, NULL, 0
++			);
++		i++;
++	}
++	if (FIELD_GET(SDR_READ_1_1_1_FAST, sdr_read_cap)) {
++		read_ops[i] = (struct spi_mem_op)
++			SPINAND_CASN_PAGE_READ_FROM_CACHE_OP(
++				true, casn->sdr_read_1_1_1_fast.addr_nbytes, 0,
++				casn->sdr_read_1_1_1_fast.dummy_nbytes, NULL, 0
++			);
++		i++;
++	}
++	if (FIELD_GET(SDR_READ_1_1_1, sdr_read_cap)) {
++		read_ops[i] = (struct spi_mem_op)
++			SPINAND_CASN_PAGE_READ_FROM_CACHE_OP(
++				false, casn->sdr_read_1_1_1.addr_nbytes, 0,
++				casn->sdr_read_1_1_1.dummy_nbytes, NULL, 0
++			);
++		i++;
++	}
++
++	casn_read_cache_variants = (struct spinand_op_variants){
++		.ops = read_ops,
++		.nops = hweight16(sdr_read_cap),
++	};
++
++	op = spinand_select_op_variant(spinand, &casn_read_cache_variants);
++	if (!op) {
++		devm_kfree(&spinand->spimem->spi->dev, read_ops);
++		return -ENOTSUPP;
++	}
++	spinand->op_templates.read_cache = op;
++
++	return 0;
++}
++
++static int spinand_set_write_op_variants(struct spinand_device *spinand,
++					 struct nand_casn *casn)
++{
++	struct spinand_op_variants casn_write_cache_variants;
++	struct spi_mem_op *write_ops;
++	const struct spi_mem_op *op;
++	int i = 0;
++
++	write_ops = devm_kzalloc(&spinand->spimem->spi->dev,
++				 sizeof(struct spi_mem_op) *
++				 hweight8(casn->sdr_write_cap),
++				 GFP_KERNEL);
++	if (!write_ops)
++		return -ENOMEM;
++
++	if (FIELD_GET(SDR_WRITE_1_1_4, casn->sdr_write_cap)) {
++		write_ops[i] = (struct spi_mem_op)
++			SPINAND_CASN_PROG_LOAD_X4(
++				true, casn->sdr_write_1_1_4.addr_nbytes, 0,
++				NULL, 0);
++		i++;
++	}
++	if (FIELD_GET(SDR_WRITE_1_1_1, casn->sdr_write_cap)) {
++		write_ops[i] = (struct spi_mem_op)
++			SPINAND_CASN_PROG_LOAD(
++				true, casn->sdr_write_1_1_1.addr_nbytes, 0,
++				NULL, 0);
++		i++;
++	}
++
++	casn_write_cache_variants = (struct spinand_op_variants){
++		.ops = write_ops,
++		.nops = hweight8(casn->sdr_write_cap),
++	};
++
++	op = spinand_select_op_variant(spinand, &casn_write_cache_variants);
++	if (!op) {
++		devm_kfree(&spinand->spimem->spi->dev, write_ops);
++		return -ENOTSUPP;
++	}
++	spinand->op_templates.write_cache = op;
++
++	return 0;
++}
++
++static int spinand_set_update_op_variants(struct spinand_device *spinand,
++					  struct nand_casn *casn)
++{
++	struct spinand_op_variants casn_update_cache_variants;
++	struct spi_mem_op *update_ops;
++	const struct spi_mem_op *op;
++	int i = 0;
++
++	update_ops = devm_kzalloc(&spinand->spimem->spi->dev,
++				  sizeof(struct spi_mem_op) *
++				  hweight8(casn->sdr_update_cap),
++				  GFP_KERNEL);
++	if (!update_ops)
++		return -ENOMEM;
++
++	if (FIELD_GET(SDR_UPDATE_1_1_4, casn->sdr_update_cap)) {
++		update_ops[i] = (struct spi_mem_op)
++			SPINAND_CASN_PROG_LOAD_X4(
++				false, casn->sdr_update_1_1_4.addr_nbytes, 0,
++				NULL, 0);
++		i++;
++	}
++	if (FIELD_GET(SDR_UPDATE_1_1_1, casn->sdr_update_cap)) {
++		update_ops[i] = (struct spi_mem_op)
++			SPINAND_CASN_PROG_LOAD(
++				false, casn->sdr_update_1_1_1.addr_nbytes, 0,
++				NULL, 0);
++		i++;
++	}
++
++	casn_update_cache_variants = (struct spinand_op_variants){
++		.ops = update_ops,
++		.nops = hweight8(casn->sdr_update_cap),
++	};
++
++	op = spinand_select_op_variant(spinand, &casn_update_cache_variants);
++	if (!op) {
++		devm_kfree(&spinand->spimem->spi->dev, update_ops);
++		return -ENOTSUPP;
++	}
++	spinand->op_templates.update_cache = op;
++
++	return 0;
++}
++
++static int spinand_init_via_casn(struct spinand_device *spinand,
++				 struct nand_casn *casn)
++{
++	struct nand_device *nand = spinand_to_nand(spinand);
++	u32 val;
++	int ret;
++	int i;
++
++	/* Set members of nand->memorg via CASN. */
++	for (i = 0; i < 9; i++) {
++		val = be32_to_cpu(*(&casn->bits_per_cell + i));
++		memcpy((u32 *)&nand->memorg.bits_per_cell + i, &val, sizeof(u32));
++	}
++	nand->eccreq.strength = be32_to_cpu(casn->ecc_strength);
++	nand->eccreq.step_size = be32_to_cpu(casn->ecc_step_size);
++	spinand->flags = casn->flags;
++
++	if (spinand->flags & SPINAND_SUP_ADV_ECC_STATUS) {
++		spinand->eccinfo = (struct spinand_ecc_info) {
++			&spinand_casn_get_ecc_status, &spinand_casn_ooblayout};
++	} else {
++		spinand->eccinfo = (struct spinand_ecc_info) {
++			NULL, &spinand_casn_ooblayout };
++	}
++
++	spinand->advecc_high_ops = devm_kzalloc(&spinand->spimem->spi->dev,
++						sizeof(struct spi_mem_op),
++						GFP_KERNEL);
++	if (!spinand->advecc_high_ops)
++		return -ENOMEM;
++	spinand->advecc_low_ops = devm_kzalloc(&spinand->spimem->spi->dev,
++					       sizeof(struct spi_mem_op),
++					       GFP_KERNEL);
++	if (!spinand->advecc_low_ops)
++		return -ENOMEM;
++	spinand->casn_oob = devm_kzalloc(&spinand->spimem->spi->dev,
++					 sizeof(struct CASN_OOB),
++					 GFP_KERNEL);
++	if (!spinand->casn_oob)
++		return -ENOMEM;
++	spinand->advecc_high = devm_kzalloc(&spinand->spimem->spi->dev,
++					    sizeof(struct CASN_ADVECC),
++					    GFP_KERNEL);
++	if (!spinand->advecc_high)
++		return -ENOMEM;
++	spinand->advecc_low = devm_kzalloc(&spinand->spimem->spi->dev,
++					   sizeof(struct CASN_ADVECC),
++					   GFP_KERNEL);
++	if (!spinand->advecc_low)
++		return -ENOMEM;
++
++	*spinand->advecc_high_ops = (struct spi_mem_op)
++		SPINAND_CASN_ADVECC_OP(casn->ecc_status_high, spinand->scratchbuf);
++	*spinand->advecc_low_ops = (struct spi_mem_op)
++		SPINAND_CASN_ADVECC_OP(casn->ecc_status_low, spinand->scratchbuf);
++
++	memcpy(spinand->casn_oob, &casn->casn_oob, sizeof(struct CASN_OOB));
++
++	spinand->advecc_high->cmd = casn->ecc_status_high.cmd;
++	spinand->advecc_high->mask = be16_to_cpu(casn->ecc_status_high.status_mask);
++	spinand->advecc_high->shift = spinand->advecc_high->mask ?
++				      ffs(spinand->advecc_high->mask)-1 : 0;
++	spinand->advecc_high->pre_op = casn->ecc_status_high.pre_op;
++	spinand->advecc_high->pre_mask = casn->ecc_status_high.pre_mask;
++
++	spinand->advecc_low->cmd = casn->ecc_status_low.cmd;
++	spinand->advecc_low->mask = be16_to_cpu(casn->ecc_status_low.status_mask);
++	spinand->advecc_low->shift = spinand->advecc_low->mask ?
++				     ffs(spinand->advecc_low->mask)-1 : 0;
++	spinand->advecc_low->pre_op = casn->ecc_status_low.pre_op;
++	spinand->advecc_low->pre_mask = casn->ecc_status_low.pre_mask;
++
++	spinand->advecc_low_bitcnt = hweight16(spinand->advecc_low->mask);
++
++	spinand->advecc_noerr_status = casn->advecc_noerr_status;
++	spinand->advecc_uncor_status = casn->advecc_uncor_status;
++	spinand->advecc_post_op = casn->advecc_post_op;
++	spinand->advecc_post_mask = casn->advecc_post_mask;
++	spinand->eccsr_math_op[0] = eccsr_none_op;
++	spinand->eccsr_math_op[1] = eccsr_and_op;
++	spinand->eccsr_math_op[2] = eccsr_add_op;
++	spinand->eccsr_math_op[3] = eccsr_minus_op;
++	spinand->eccsr_math_op[4] = eccsr_mul_op;
++
++	ret = spinand_set_read_op_variants(spinand, casn);
++	if (ret < 0)
++		return ret;
++	ret = spinand_set_write_op_variants(spinand, casn);
++	if (ret < 0)
++		return ret;
++	ret = spinand_set_update_op_variants(spinand, casn);
++	if (ret < 0)
++		return ret;
++
++	return 0;
++}
++
++static void spinand_dump_casn(struct spinand_device *spinand, struct nand_casn *casn)
++{
++	int i;
++
++	dev_dbg(&spinand->spimem->spi->dev,
++		"---Start dumping full CASN page---\n");
++	for (i = 0; i < 64; i++)
++		pr_debug("0x%08x", *((u32 *)casn + i));
++
++	pr_debug("** Dump critical fields **\n");
++	pr_debug("signature: 0x%04x\n", be32_to_cpu(casn->signature));
++	pr_debug("version: v%u.%u\n", casn->version >> 4, casn->version & 0xf);
++	pr_debug("[Memory Organization]\n");
++	pr_debug("  bits_per_cell: %d\n", be32_to_cpu(casn->bits_per_cell));
++	pr_debug("  bytes_per_page: %d\n", be32_to_cpu(casn->bytes_per_page));
++	pr_debug("  spare_bytes_per_page: %d\n",
++		 be32_to_cpu(casn->spare_bytes_per_page));
++	pr_debug("  pages_per_block: %d\n",
++		 be32_to_cpu(casn->pages_per_block));
++	pr_debug("  blocks_per_lun: %d\n", be32_to_cpu(casn->blocks_per_lun));
++	pr_debug("  max_bb_per_lun: %d\n", be32_to_cpu(casn->max_bb_per_lun));
++	pr_debug("  planes_per_lun: %d\n", be32_to_cpu(casn->planes_per_lun));
++	pr_debug("  luns_per_target: %d\n",
++		 be32_to_cpu(casn->luns_per_target));
++	pr_debug("  total_target: %d\n", be32_to_cpu(casn->total_target));
++	pr_debug("[flags]\n");
++	pr_debug("  0. Have QE bit? %s\n",
++		casn->flags & SPINAND_HAS_QE_BIT ? "Yes" : "No");
++	pr_debug("  1. Have continuous read feature bit? %s\n",
++		casn->flags & SPINAND_HAS_CR_FEAT_BIT ? "Yes" : "No");
++	pr_debug("  2. Support continuous read? %s\n",
++		casn->flags & SPINAND_SUP_CR ? "Yes" : "No");
++	pr_debug("  3. Support on-die ECC? %s\n",
++		casn->flags & SPINAND_SUP_ON_DIE_ECC ? "Yes" : "No");
++	pr_debug("  4. Support legacy ECC status? %s\n",
++		casn->flags & SPINAND_SUP_LEGACY_ECC_STATUS ? "Yes" : "No");
++	pr_debug("  5. Support advanced ECC status? %s\n",
++		casn->flags & SPINAND_SUP_ADV_ECC_STATUS ? "Yes" : "No");
++	pr_debug("  6. ECC parity readable? %s\n",
++		casn->flags & SPINAND_ECC_PARITY_READABLE ? "Yes" : "No");
++	pr_debug("[R/W ability]\n");
++	pr_debug("  read ability: %x\n", be16_to_cpu(casn->sdr_read_cap));
++	pr_debug("  write ability: %x\n", casn->sdr_write_cap);
++	pr_debug("  update ability: %x\n", casn->sdr_update_cap);
++	pr_debug("advanced ECC no error state: %x\n",
++		 casn->advecc_noerr_status);
++	pr_debug("advecced ECC uncorrectable state: %x\n",
++		 casn->advecc_uncor_status);
++	pr_debug("CRC: 0x%04x\n", be16_to_cpu(casn->crc));
++
++	dev_dbg(&spinand->spimem->spi->dev,
++		"---Dumping full CASN page ends here.---\n");
++}
++
+ static int spinand_detect(struct spinand_device *spinand)
+ {
+-	struct device *dev = &spinand->spimem->spi->dev;
+ 	struct nand_device *nand = spinand_to_nand(spinand);
++	struct device *dev = &spinand->spimem->spi->dev;
++	struct nand_casn *casn;
++	char manufacturer[14];
++	unsigned int sel = 0;
++	char model[17];
+ 	int ret;
+ 
+ 	ret = spinand_reset_op(spinand);
+ 	if (ret)
+ 		return ret;
+ 
+-	ret = spinand_id_detect(spinand);
+-	if (ret) {
+-		dev_err(dev, "unknown raw ID %*phN\n", SPINAND_MAX_ID_LEN,
+-			spinand->id.data);
+-		return ret;
++	spinand->use_casn = false;
++	casn = kzalloc((sizeof(struct nand_casn) * CASN_PAGE_V1_COPIES), GFP_KERNEL);
++	if (!casn)
++		return -ENOMEM;
++
++	ret = spinand_casn_detect(spinand, casn, &sel);
++	if (!ret) {
++		spinand->use_casn = true;
++		strncpy(manufacturer, casn[sel].manufacturer, sizeof(manufacturer)-1);
++		sanitize_string(manufacturer, sizeof(manufacturer));
++		strncpy(model, casn[sel].model, sizeof(model)-1);
++		sanitize_string(model, sizeof(model));
++
++		spinand_dump_casn(spinand, casn + sel);
++
++		ret = spinand_init_via_casn(spinand, casn + sel);
++		if (ret)
++			dev_err(dev, "Initilize spinand via CASN failed: %d\n", ret);
++	}
++
++	if (ret < 0) {
++		dev_warn(dev, "Fallback to read ID\n");
++
++		ret = spinand_reset_op(spinand);
++		if (ret)
++			goto free_casn;
++		ret = spinand_id_detect(spinand);
++		if (ret) {
++			dev_err(dev, "unknown raw ID %*phN\n", SPINAND_MAX_ID_LEN,
++				spinand->id.data);
++			goto free_casn;
++		}
+ 	}
+ 
+ 	if (nand->memorg.ntargets > 1 && !spinand->select_target) {
+ 		dev_err(dev,
+ 			"SPI NANDs with more than one die must implement ->select_target()\n");
+-		return -EINVAL;
++		ret = -EINVAL;
++		goto free_casn;
++	}
++
++	if (spinand->use_casn) {
++		dev_info(&spinand->spimem->spi->dev,
++			 "%s %s SPI NAND was found.\n", manufacturer, model);
++	} else {
++		dev_info(&spinand->spimem->spi->dev,
++			 "%s SPI NAND was found.\n", spinand->manufacturer->name);
+ 	}
+ 
+-	dev_info(&spinand->spimem->spi->dev,
+-		 "%s SPI NAND was found.\n", spinand->manufacturer->name);
+ 	dev_info(&spinand->spimem->spi->dev,
+ 		 "%llu MiB, block size: %zu KiB, page size: %zu, OOB size: %u\n",
+ 		 nanddev_size(nand) >> 20, nanddev_eraseblock_size(nand) >> 10,
+ 		 nanddev_page_size(nand), nanddev_per_page_oobsize(nand));
+ 
+-	return 0;
++free_casn:
++	kfree(casn);
++
++	return ret;
+ }
+ 
+ static int spinand_noecc_ooblayout_ecc(struct mtd_info *mtd, int section,
+--- a/include/linux/mtd/spinand.h
++++ b/include/linux/mtd/spinand.h
+@@ -62,6 +62,59 @@
+ 		   SPI_MEM_OP_NO_DUMMY,					\
+ 		   SPI_MEM_OP_NO_DATA)
+ 
++/* Macros for CASN */
++#define SPINAND_CASN_PAGE_READ_FROM_CACHE_OP(fast, naddr, addr, ndummy, buf, len) \
++	SPI_MEM_OP(SPI_MEM_OP_CMD(fast ? 0x0b : 0x03, 1),		\
++		   SPI_MEM_OP_ADDR(naddr, addr, 1),			\
++		   SPI_MEM_OP_DUMMY(ndummy, 1),			\
++		   SPI_MEM_OP_DATA_IN(len, buf, 1))
++
++#define SPINAND_CASN_PAGE_READ_FROM_CACHE_X2_OP(naddr, addr, ndummy, buf, len)	\
++	SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1),			\
++		   SPI_MEM_OP_ADDR(naddr, addr, 1),			\
++		   SPI_MEM_OP_DUMMY(ndummy, 1),			\
++		   SPI_MEM_OP_DATA_IN(len, buf, 2))
++
++#define SPINAND_CASN_PAGE_READ_FROM_CACHE_DUALIO_OP(naddr, addr, ndummy, buf, len)	\
++		SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1),			\
++			   SPI_MEM_OP_ADDR(naddr, addr, 2),			\
++			   SPI_MEM_OP_DUMMY(ndummy, 2),			\
++			   SPI_MEM_OP_DATA_IN(len, buf, 2))
++
++#define SPINAND_CASN_PAGE_READ_FROM_CACHE_X4_OP(naddr, addr, ndummy, buf, len)	\
++	SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1),				\
++		   SPI_MEM_OP_ADDR(naddr, addr, 1),				\
++		   SPI_MEM_OP_DUMMY(ndummy, 1),				\
++		   SPI_MEM_OP_DATA_IN(len, buf, 4))
++
++#define SPINAND_CASN_PAGE_READ_FROM_CACHE_QUADIO_OP(naddr, addr, ndummy, buf, len)	\
++	SPI_MEM_OP(SPI_MEM_OP_CMD(0xeb, 1),				\
++		   SPI_MEM_OP_ADDR(naddr, addr, 4),				\
++		   SPI_MEM_OP_DUMMY(ndummy, 4),				\
++		   SPI_MEM_OP_DATA_IN(len, buf, 4))
++
++#define SPINAND_CASN_PROG_LOAD(reset, naddr, addr, buf, len)			\
++	SPI_MEM_OP(SPI_MEM_OP_CMD(reset ? 0x02 : 0x84, 1),		\
++		   SPI_MEM_OP_ADDR(naddr, addr, 1),				\
++		   SPI_MEM_OP_NO_DUMMY,					\
++		   SPI_MEM_OP_DATA_OUT(len, buf, 1))
++
++#define SPINAND_CASN_PROG_LOAD_X4(reset, naddr, addr, buf, len)			\
++	SPI_MEM_OP(SPI_MEM_OP_CMD(reset ? 0x32 : 0x34, 1),		\
++		   SPI_MEM_OP_ADDR(naddr, addr, 1),				\
++		   SPI_MEM_OP_NO_DUMMY,					\
++		   SPI_MEM_OP_DATA_OUT(len, buf, 4))
++
++#define SPINAND_CASN_ADVECC_OP(casn_adv_ecc_status, buf)			\
++	SPI_MEM_OP(SPI_MEM_OP_CMD(casn_adv_ecc_status.cmd, 1),			\
++		   SPI_MEM_OP_ADDR(casn_adv_ecc_status.addr_nbytes,		\
++				   casn_adv_ecc_status.addr,			\
++				   casn_adv_ecc_status.addr_buswidth),		\
++		   SPI_MEM_OP_DUMMY(casn_adv_ecc_status.dummy_nbytes,		\
++				    casn_adv_ecc_status.dummy_buswidth),	\
++		   SPI_MEM_OP_DATA_IN(casn_adv_ecc_status.status_nbytes, buf, 1))
++/* Macros for CASN end */
++
+ #define SPINAND_PAGE_READ_FROM_CACHE_OP(fast, addr, ndummy, buf, len)	\
+ 	SPI_MEM_OP(SPI_MEM_OP_CMD(fast ? 0x0b : 0x03, 1),		\
+ 		   SPI_MEM_OP_ADDR(2, addr, 1),				\
+@@ -287,6 +340,12 @@ struct spinand_ecc_info {
+ 
+ #define SPINAND_HAS_QE_BIT		BIT(0)
+ #define SPINAND_HAS_CR_FEAT_BIT		BIT(1)
++#define SPINAND_SUP_CR			BIT(2)
++#define SPINAND_SUP_ON_DIE_ECC		BIT(3)
++#define SPINAND_SUP_LEGACY_ECC_STATUS	BIT(4)
++#define SPINAND_SUP_ADV_ECC_STATUS	BIT(5)
++#define SPINAND_ECC_PARITY_READABLE	BIT(6)
++
+ 
+ /**
+  * struct spinand_info - Structure used to describe SPI NAND chips
+@@ -363,6 +422,28 @@ struct spinand_dirmap {
+ };
+ 
+ /**
++ * struct CASN_ADVECC - CASN's advanced ECC description
++ * @cmd: Command to access SPI-NAND on-chip ECC status registers
++ * @mask: Mask to access SPI-NAND on-chip ECC status registers.
++ *	  ADV_ECC_STATUS->status_nbytes | CASN_ADVECC->mask
++ *			1		|      0 to 0xff
++ *			2		|     0 to 0xffff
++ * @shift: How many bits to shift to get on-chip ECC status
++ * @pre_op: This comes from CASN page's ADV_ECC_STATUS's pre_op.
++ *	    After reading on-chip ECC status, we need to do some math
++ *	    operations if this is specified.
++ * @pre_mask: This comes from CASN page's ADV_ECC_STATUS's pre_mask.
++ *	      This is used in companion with pre_op above.
++ */
++struct CASN_ADVECC {
++	u8 cmd;
++	u16 mask;
++	u8 shift;
++	u8 pre_op;
++	u8 pre_mask;
++};
++
++/**
+  * struct spinand_device - SPI NAND device instance
+  * @base: NAND device instance
+  * @spimem: pointer to the SPI mem object
+@@ -414,6 +495,23 @@ struct spinand_device {
+ 	u8 *oobbuf;
+ 	u8 *scratchbuf;
+ 	const struct spinand_manufacturer *manufacturer;
++
++	bool use_casn;
++	struct nand_casn *casn;
++	struct spi_mem_op *advecc_high_ops; /* ops to read higher part of advanced ECC status*/
++	struct spi_mem_op *advecc_low_ops;
++	struct CASN_OOB *casn_oob;
++	struct CASN_ADVECC *advecc_high;
++	struct CASN_ADVECC *advecc_low;
++
++	u8 advecc_low_bitcnt;
++	u8 advecc_noerr_status;
++	u8 advecc_uncor_status;
++	u8 advecc_post_op;
++	u8 advecc_post_mask;
++
++	size_t (*eccsr_math_op[4])(size_t, size_t);
++
+ 	void *priv;
+ };
+ 
+@@ -484,3 +582,4 @@ int spinand_upd_cfg(struct spinand_devic
+ int spinand_select_target(struct spinand_device *spinand, unsigned int target);
+ 
+ #endif /* __LINUX_MTD_SPINAND_H */
++
+--- /dev/null
++++ b/include/linux/mtd/casn.h
+@@ -0,0 +1,191 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) 2023 - Mediatek
++ *
++ * Author: SkyLake <SkyLake.Huang@mediatek.com>
++ */
++
++#ifndef __LINUX_MTD_CASN_H
++#define __LINUX_MTD_CASN_H
++
++#define CASN_CRC_BASE	0x4341
++#define CASN_SIGNATURE	0x4341534EU
++#define SPINAND_CASN_V1_CRC_OFS (254)
++#define CASN_PAGE_V1_COPIES     (3)
++
++#define SDR_READ_1_1_1		BIT(0)
++#define SDR_READ_1_1_1_FAST	BIT(1)
++#define SDR_READ_1_1_2		BIT(2)
++#define SDR_READ_1_2_2		BIT(3)
++#define SDR_READ_1_1_4		BIT(4)
++#define SDR_READ_1_4_4		BIT(5)
++#define SDR_READ_1_1_8		BIT(6)
++#define SDR_READ_1_8_8		BIT(7)
++
++#define SDR_WRITE_1_1_1		BIT(0)
++#define SDR_WRITE_1_1_4		BIT(1)
++
++#define SDR_UPDATE_1_1_1	BIT(0)
++#define SDR_UPDATE_1_1_4	BIT(1)
++
++struct op_slice {
++	u8 cmd_opcode;
++#if defined(__LITTLE_ENDIAN_BITFIELD)
++	u8 dummy_nbytes : 4;
++	u8 addr_nbytes : 4;
++#elif defined(__BIG_ENDIAN_BITFIELD)
++	u8 addr_nbytes : 4;
++	u8 dummy_nbytes : 4;
++#endif
++};
++
++struct SPINAND_FLAGS {
++#if defined(__LITTLE_ENDIAN_BITFIELD)
++	u8 has_qe_bit : 1;
++	u8 has_cr_feat_bit : 1;
++	u8 conti_read_cap : 1;
++	u8 on_die_ecc : 1;
++	u8 legacy_ecc_status : 1;
++	u8 adv_ecc_status : 1;
++	u8 ecc_parity_readable : 1;
++	u8 ecc_alg : 1; /* ECC algorithm */
++#elif defined(__BIG_ENDIAN_BITFIELD)
++	u8 ecc_alg : 1; /* ECC algorithm */
++	u8 ecc_parity_readable : 1;
++	u8 adv_ecc_status : 1;
++	u8 legacy_ecc_status : 1;
++	u8 on_die_ecc : 1;
++	u8 conti_read_cap : 1;
++	u8 has_cr_feat_bit : 1;
++	u8 has_qe_bit : 1;
++#endif
++};
++
++struct ADV_ECC_STATUS {
++	u8 cmd;
++	u8 addr;
++	u8 addr_nbytes;
++	u8 addr_buswidth;
++	u8 dummy_nbytes;
++	u8 dummy_buswidth;
++	u8 status_nbytes;
++	u16 status_mask;
++	u8 pre_op; /* pre-process operator */
++	u8 pre_mask; /* pre-process mask */
++} __packed;
++
++struct CASN_OOB {
++	u8 layout_type;
++
++	/* OOB free layout */
++	u8 free_start;
++	u8 free_length;
++	u8 bbm_length;
++
++	/* ECC parity layout */
++	u8 ecc_parity_start;
++	u8 ecc_parity_space;
++	u8 ecc_parity_real_length;
++};
++
++enum oob_overall {
++	OOB_DISCRETE = 0,
++	OOB_CONTINUOUS,
++};
++
++struct nand_casn {
++	/* CASN signature must be 4 chars: 'C','A','S','N'  */
++	union {
++		u8 sig[4];
++		u32 signature;
++	};
++
++	u8 version;
++	char manufacturer[13];
++	char model[16];
++
++	__be32 bits_per_cell;
++	__be32 bytes_per_page;
++	__be32 spare_bytes_per_page;
++	__be32 pages_per_block;
++	__be32 blocks_per_lun;
++	__be32 max_bb_per_lun;
++	__be32 planes_per_lun;
++	__be32 luns_per_target;
++	__be32 total_target;
++
++	__be32 ecc_strength;
++	__be32 ecc_step_size;
++
++	u8 flags;
++	u8 reserved1;
++
++	__be16 sdr_read_cap;
++	struct op_slice sdr_read_1_1_1;
++	struct op_slice sdr_read_1_1_1_fast;
++	struct op_slice sdr_read_1_1_2;
++	struct op_slice sdr_read_1_2_2;
++	struct op_slice sdr_read_1_1_4;
++	struct op_slice sdr_read_1_4_4;
++	struct op_slice sdr_read_1_1_8;
++	struct op_slice sdr_read_1_8_8;
++
++	struct op_slice sdr_cont_read_1_1_1;
++	struct op_slice sdr_cont_read_1_1_1_fast;
++	struct op_slice sdr_cont_read_1_1_2;
++	struct op_slice sdr_cont_read_1_2_2;
++	struct op_slice sdr_cont_read_1_1_4;
++	struct op_slice sdr_cont_read_1_4_4;
++	struct op_slice sdr_cont_read_1_1_8;
++	struct op_slice sdr_cont_read_1_8_8;
++
++	__be16 ddr_read_cap;
++	struct op_slice ddr_read_1_1_1;
++	struct op_slice ddr_read_1_1_1_fast;
++	struct op_slice ddr_read_1_1_2;
++	struct op_slice ddr_read_1_2_2;
++	struct op_slice ddr_read_1_1_4;
++	struct op_slice ddr_read_1_4_4;
++	struct op_slice ddr_read_1_1_8;
++	struct op_slice ddr_read_1_8_8;
++
++	struct op_slice ddr_cont_read_1_1_1;
++	struct op_slice ddr_cont_read_1_1_1_fast;
++	struct op_slice ddr_cont_read_1_1_2;
++	struct op_slice ddr_cont_read_1_2_2;
++	struct op_slice ddr_cont_read_1_1_4;
++	struct op_slice ddr_cont_read_1_4_4;
++	struct op_slice ddr_cont_read_1_1_8;
++	struct op_slice ddr_cont_read_1_8_8;
++
++	u8 sdr_write_cap;
++	struct op_slice sdr_write_1_1_1;
++	struct op_slice sdr_write_1_1_4;
++	struct op_slice reserved2[6];
++	u8 ddr_write_cap;
++	struct op_slice reserved3[8];
++
++	u8 sdr_update_cap;
++	struct op_slice sdr_update_1_1_1;
++	struct op_slice sdr_update_1_1_4;
++	struct op_slice reserved4[6];
++	u8 ddr_update_cap;
++	struct op_slice reserved5[8];
++
++	struct CASN_OOB casn_oob;
++
++	/* Advanced ECC status CMD0 (higher bits) */
++	struct ADV_ECC_STATUS ecc_status_high;
++	/* Advanced ECC status CMD1 (lower bits) */
++	struct ADV_ECC_STATUS ecc_status_low;
++
++	u8 advecc_noerr_status;
++	u8 advecc_uncor_status;
++	u8 advecc_post_op;
++	u8 advecc_post_mask;
++
++	u8 reserved6[5];
++	__be16 crc;
++} __packed;
++
++#endif /* __LINUX_MTD_CASN_H */
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 81a4054..6ab5265 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
@@ -162,6 +162,8 @@
     file://999-2374-drivers-spi-mt65xx-Add-controller-calibration-parameter.patch \
     file://999-2375-spi-update-driver.patch \
     file://999-2376-drivers-spi-mt65xx-add-dts-buswidth-flow.patch \
+    file://999-2377-integrate-ONFI-and-CASN-manipulation.patch \
+    file://999-2378-Add-CASN-support-for-SPI-NAND.patch \
     file://999-2380-fix-dirty-race-between-do_tmpfile.patch \
     file://999-2500-cpufreq-add-the-missing-platform-driver-unregister.patch \
     file://999-2501-cpufreq-Enable-clocks-and-regulators.patch \
diff --git a/recipes-kernel/linux/linux-mediatek_5.4.bb b/recipes-kernel/linux/linux-mediatek_5.4.bb
index fcfe567..643bab7 100644
--- a/recipes-kernel/linux/linux-mediatek_5.4.bb
+++ b/recipes-kernel/linux/linux-mediatek_5.4.bb
@@ -9,8 +9,8 @@
 
 KBRANCH ?= "linux-5.4.y"
 
-LINUX_VERSION ?= "5.4.271"
-SRCREV_machine ?= "3fec063b052e395f4920fbc59a8d0bb3c9666d76"
+LINUX_VERSION ?= "5.4.281"
+SRCREV_machine ?= "84d75fd864979b0228cfe7170a359c0a60f04a98"
 KMETA = "kernel-meta"
 SRCREV_meta ?= "feeb59687bc0f054af837a5061f8d413ec7c93e9"
 
diff --git a/recipes-wifi/atenl/files/iwpriv.sh b/recipes-wifi/atenl/files/iwpriv.sh
index b636bf8..a4257f1 100644
--- a/recipes-wifi/atenl/files/iwpriv.sh
+++ b/recipes-wifi/atenl/files/iwpriv.sh
@@ -369,15 +369,73 @@
     do_cmd "mt76-test ${interface} set tx_rate_sgi=${sgi} tx_ltf=${he_ltf}"
 }
 
+function convert_tm_cbw_to_nl {
+    local cbw=$1
+    local bw="NOHT"
+
+    case ${cbw} in
+        # TM_CBW_20MHZ
+        "0")
+            bw="20"
+            ;;
+        # TM_CBW_40MHZ
+        "1")
+            bw="40"
+            ;;
+        # TM_CBW_80MHZ
+        "2")
+            bw="80"
+            ;;
+        # TM_CBW_10MHZ
+        "3")
+            bw="10"
+            ;;
+        # TM_CBW_5MHZ
+        "4")
+            bw="5"
+            ;;
+        # TM_CBW_160MHZ
+        "5")
+            bw="160"
+            ;;
+        # TM_CBW_8080MHZ
+        "6")
+            bw="80p80"
+            ;;
+        # TM_CBW_320MHZ
+        "12")
+            bw="320"
+            ;;
+    esac
+
+    echo ${bw}
+}
+
+function convert_bw {
+    local system_bw=$(echo $1 | sed s/:/' '/g | cut -d " " -f 1)
+    local data_bw=$(echo $1 | sed s/:/' '/g | cut -d " " -f 2)
+    # Convert TM_CBW to NL80211_CHAN_WIDTH
+    local tx_pkt_bw=$(convert_tm_cbw_to_nl ${data_bw})
+
+    record_config "ATETXBW" ${system_bw} ${iwpriv_file}
+
+    # apply per-packet bw
+    if [[ $1 == *":"* ]]; then
+        do_cmd "mt76-test phy${phy_idx} set tx_pkt_bw=${tx_pkt_bw}"
+    fi
+}
+
 function convert_channel {
-    local ctrl_band_idx=$(get_config "ATECTRLBANDIDX" ${iwpriv_file})
     local ch=$(echo $1 | sed s/:/' '/g | cut -d " " -f 1)
+    local control_ch=$(echo $1 | sed s/:/' '/g | cut -d " " -f 1)
+    local band=$(echo $1 | sed s/:/' '/g | cut -d " " -f 2)
+    local pri_sel=$(echo $1 | sed s/:/' '/g | cut -d " " -f 3)
+    local ctrl_band_idx=$(get_config "ATECTRLBANDIDX" ${iwpriv_file})
     local bw=$(get_config "ATETXBW" ${iwpriv_file} | cut -d ":" -f 1)
-    local bw_str="HT20"
+    local bw_str="20"
     local base_chan=1
     local control_freq=0
     local base_freq=0
-    local band=$(echo $1 | sed s/:/' '/g | cut -d " " -f 2)
     local temp=$((phy_idx+1))
 
     # Handle ATECTRLBANDIDX
@@ -403,290 +461,60 @@
         fi
     fi
 
+    if [[ $1 == *":"* ]] && [ -n "${pri_sel}" ]; then
+        do_cmd "mt76-test phy${phy_idx} set tx_pri_sel=${pri_sel}"
+    fi
+
     if [[ $1 != *":"* ]] || [ "${band}" = "0" ]; then
         case ${bw} in
             "1")
                 if [ "${ch}" -lt "3" ] || [ "${ch}" -gt "12" ]; then
-                    local bw_str="HT20"
+                    bw_str="20"
                 else
-                    local bw_str="HT40+"
-                    ch=$(expr ${ch} - "2")
-                fi
-                ;;
-        esac
-        local base_freq=2412
-    elif [ "${band}" = "1" ]; then
-        case ${bw} in
-            "5")
-                bw_str="160MHz"
-                if [ ${ch} -lt "68" ]; then
-                    ch="36"
-                elif [ ${ch} -lt "100" ]; then
-                    ch="68"
-                elif [ ${ch} -lt "132" ]; then
-                    ch="100"
-                elif [ ${ch} -lt "181" ]; then
-                    ch="149"
-                fi
-                ;;
-            "2")
-                bw_str="80MHz"
-                if [ ${ch} -lt "52" ]; then
-                    ch="36"
-                elif [ ${ch} -lt "68" ]; then
-                    ch="52"
-                elif [ ${ch} -lt "84" ]; then
-                    ch="68"
-                elif [ ${ch} -lt "100" ]; then
-                    ch="84"
-                elif [ ${ch} -lt "116" ]; then
-                    ch="100"
-                elif [ ${ch} -lt "132" ]; then
-                    ch="116"
-                elif [ ${ch} -lt "149" ]; then
-                    ch="132"
-                elif [ ${ch} -lt "165" ]; then
-                    ch="149"
-                elif [ ${ch} -lt "181" ]; then
-                    ch="165"
-                fi
-                ;;
-            "1")
-                if [ ${ch} -lt "44" ]; then
-                    ch=$([ "${ch}" -lt "40" ] && echo "36" || echo "40")
-                    bw_str=$([ "${ch}" -le "38" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "52" ]; then
-                    ch=$([ "${ch}" -lt "48" ] && echo "44" || echo "48")
-                    bw_str=$([ "${ch}" -le "46" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "60" ]; then
-                    ch=$([ "${ch}" -lt "56" ] && echo "52" || echo "56")
-                    bw_str=$([ "${ch}" -le "54" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "68" ]; then
-                    ch=$([ "${ch}" -lt "64" ] && echo "60" || echo "64")
-                    bw_str=$([ "${ch}" -le "62" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "76" ]; then
-                    ch=$([ "${ch}" -lt "72" ] && echo "68" || echo "72")
-                    bw_str=$([ "${ch}" -le "70" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "84" ]; then
-                    ch=$([ "${ch}" -lt "80" ] && echo "76" || echo "80")
-                    bw_str=$([ "${ch}" -le "78" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "92" ]; then
-                    ch=$([ "${ch}" -lt "88" ] && echo "84" || echo "88")
-                    bw_str=$([ "${ch}" -le "86" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "100" ]; then
-                    ch=$([ "${ch}" -lt "96" ] && echo "92" || echo "96")
-                    bw_str=$([ "${ch}" -le "94" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "108" ]; then
-                    ch=$([ "${ch}" -lt "104" ] && echo "100" || echo "104")
-                    bw_str=$([ "${ch}" -le "102" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "116" ]; then
-                    ch=$([ "${ch}" -lt "112" ] && echo "108" || echo "112")
-                    bw_str=$([ "${ch}" -le "110" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "124" ]; then
-                    ch=$([ "${ch}" -lt "120" ] && echo "116" || echo "120")
-                    bw_str=$([ "${ch}" -le "118" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "132" ]; then
-                    ch=$([ "${ch}" -lt "128" ] && echo "124" || echo "128")
-                    bw_str=$([ "${ch}" -le "126" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "140" ]; then
-                    ch=$([ "${ch}" -lt "136" ] && echo "132" || echo "136")
-                    bw_str=$([ "${ch}" -le "134" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "149" ]; then
-                    ch=$([ "${ch}" -lt "144" ] && echo "140" || echo "144")
-                    bw_str=$([ "${ch}" -le "142" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "157" ]; then
-                    ch=$([ "${ch}" -lt "153" ] && echo "149" || echo "153")
-                    bw_str=$([ "${ch}" -le "151" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "165" ]; then
-                    ch=$([ "${ch}" -lt "161" ] && echo "157" || echo "161")
-                    bw_str=$([ "${ch}" -le "159" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "173" ]; then
-                    ch=$([ "${ch}" -lt "169" ] && echo "165" || echo "169")
-                    bw_str=$([ "${ch}" -le "167" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "181" ]; then
-                    ch=$([ "${ch}" -lt "177" ] && echo "173" || echo "177")
-                    bw_str=$([ "${ch}" -le "175" ] && echo "HT40+" || echo "HT40-")
+                    bw_str="40"
+                    control_ch=$(expr ${ch} - "2")
                 fi
                 ;;
-            "0")
-                local bw_str="HT20"
-                ;;
         esac
-        local base_freq=5180
-        local base_chan=36
+        base_freq=2412
     else
-        local base_freq=5955
         case ${bw} in
             "12")
-                local bw_str="320"
-                if [ ${ch} == "31" ]; then
-                    local control_freq="5955"
-                elif [ ${ch} == "63" ]; then
-                    local control_freq="6115"
-                elif [ ${ch} == "95" ]; then
-                    local control_freq="6275"
-                elif [ ${ch} == "127" ]; then
-                    local control_freq="6435"
-                elif [ ${ch} == "159" ]; then
-                    local control_freq="6595"
-                elif [ ${ch} == "191" ]; then
-                    local control_freq="6755"
-                fi
-                local center_freq=$(((ch - base_chan) * 5 + base_freq))
-                do_cmd "iw dev mon${phy_idx} set freq ${control_freq} ${bw_str} ${center_freq}"
-                return
+                bw_str="320"
+                control_ch=$(expr ${ch} - "30")
                 ;;
             "5")
-                bw_str="160MHz"
-                if [ ${ch} -lt "33" ]; then
-                    ch="1"
-                elif [ ${ch} -lt "65" ]; then
-                    ch="33"
-                elif [ ${ch} -lt "97" ]; then
-                    ch="65"
-                elif [ ${ch} -lt "129" ]; then
-                    ch="97"
-                elif [ ${ch} -lt "161" ]; then
-                    ch="129"
-                elif [ ${ch} -lt "193" ]; then
-                    ch="161"
-                elif [ ${ch} -lt "225" ]; then
-                    ch="193"
-                fi
+                bw_str="160"
+                control_ch=$(expr ${ch} - "14")
                 ;;
             "2")
-                bw_str="80MHz"
-                if [ ${ch} -lt "17" ]; then
-                    ch="1"
-                elif [ ${ch} -lt "33" ]; then
-                    ch="17"
-                elif [ ${ch} -lt "49" ]; then
-                    ch="33"
-                elif [ ${ch} -lt "65" ]; then
-                    ch="49"
-                elif [ ${ch} -lt "81" ]; then
-                    ch="65"
-                elif [ ${ch} -lt "97" ]; then
-                    ch="81"
-                elif [ ${ch} -lt "113" ]; then
-                    ch="97"
-                elif [ ${ch} -lt "129" ]; then
-                    ch="113"
-                elif [ ${ch} -lt "145" ]; then
-                    ch="129"
-                elif [ ${ch} -lt "161" ]; then
-                    ch="145"
-                elif [ ${ch} -lt "177" ]; then
-                    ch="161"
-                elif [ ${ch} -lt "193" ]; then
-                    ch="177"
-                elif [ ${ch} -lt "209" ]; then
-                    ch="193"
-                elif [ ${ch} -lt "225" ]; then
-                    ch="209"
-                fi
+                bw_str="80"
+                control_ch=$(expr ${ch} - "6")
                 ;;
             "1")
-                if [ ${ch} -lt "9" ]; then
-                    ch=$([ "${ch}" -lt "5" ] && echo "1" || echo "5")
-                    bw_str=$([ "${ch}" -le "3" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "17" ]; then
-                    ch=$([ "${ch}" -lt "13" ] && echo "9" || echo "13")
-                    bw_str=$([ "${ch}" -le "11" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "25" ]; then
-                    ch=$([ "${ch}" -lt "21" ] && echo "17" || echo "21")
-                    bw_str=$([ "${ch}" -le "19" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "33" ]; then
-                    ch=$([ "${ch}" -lt "29" ] && echo "25" || echo "29")
-                    bw_str=$([ "${ch}" -le "27" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "33" ]; then
-                    ch=$([ "${ch}" -lt "29" ] && echo "25" || echo "29")
-                    bw_str=$([ "${ch}" -le "27" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "41" ]; then
-                    ch=$([ "${ch}" -lt "37" ] && echo "33" || echo "37")
-                    bw_str=$([ "${ch}" -le "35" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "49" ]; then
-                    ch=$([ "${ch}" -lt "45" ] && echo "41" || echo "45")
-                    bw_str=$([ "${ch}" -le "43" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "57" ]; then
-                    ch=$([ "${ch}" -lt "53" ] && echo "49" || echo "53")
-                    bw_str=$([ "${ch}" -le "51" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "65" ]; then
-                    ch=$([ "${ch}" -lt "61" ] && echo "57" || echo "61")
-                    bw_str=$([ "${ch}" -le "59" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "73" ]; then
-                    ch=$([ "${ch}" -lt "69" ] && echo "65" || echo "69")
-                    bw_str=$([ "${ch}" -le "67" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "81" ]; then
-                    ch=$([ "${ch}" -lt "77" ] && echo "73" || echo "77")
-                    bw_str=$([ "${ch}" -le "75" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "89" ]; then
-                    ch=$([ "${ch}" -lt "85" ] && echo "81" || echo "85")
-                    bw_str=$([ "${ch}" -le "83" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "97" ]; then
-                    ch=$([ "${ch}" -lt "93" ] && echo "89" || echo "93")
-                    bw_str=$([ "${ch}" -le "91" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "105" ]; then
-                    ch=$([ "${ch}" -lt "101" ] && echo "97" || echo "101")
-                    bw_str=$([ "${ch}" -le "99" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "113" ]; then
-                    ch=$([ "${ch}" -lt "109" ] && echo "105" || echo "109")
-                    bw_str=$([ "${ch}" -le "107" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "121" ]; then
-                    ch=$([ "${ch}" -lt "117" ] && echo "113" || echo "117")
-                    bw_str=$([ "${ch}" -le "115" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "129" ]; then
-                    ch=$([ "${ch}" -lt "125" ] && echo "121" || echo "125")
-                    bw_str=$([ "${ch}" -le "123" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "137" ]; then
-                    ch=$([ "${ch}" -lt "133" ] && echo "129" || echo "133")
-                    bw_str=$([ "${ch}" -le "131" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "145" ]; then
-                    ch=$([ "${ch}" -lt "141" ] && echo "137" || echo "141")
-                    bw_str=$([ "${ch}" -le "139" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "153" ]; then
-                    ch=$([ "${ch}" -lt "149" ] && echo "145" || echo "149")
-                    bw_str=$([ "${ch}" -le "147" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "161" ]; then
-                    ch=$([ "${ch}" -lt "157" ] && echo "153" || echo "157")
-                    bw_str=$([ "${ch}" -le "155" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "169" ]; then
-                    ch=$([ "${ch}" -lt "165" ] && echo "161" || echo "165")
-                    bw_str=$([ "${ch}" -le "163" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "177" ]; then
-                    ch=$([ "${ch}" -lt "173" ] && echo "169" || echo "173")
-                    bw_str=$([ "${ch}" -le "171" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "185" ]; then
-                    ch=$([ "${ch}" -lt "181" ] && echo "177" || echo "181")
-                    bw_str=$([ "${ch}" -le "179" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "193" ]; then
-                    ch=$([ "${ch}" -lt "189" ] && echo "185" || echo "189")
-                    bw_str=$([ "${ch}" -le "187" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "201" ]; then
-                    ch=$([ "${ch}" -lt "197" ] && echo "193" || echo "197")
-                    bw_str=$([ "${ch}" -le "195" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "209" ]; then
-                    ch=$([ "${ch}" -lt "205" ] && echo "201" || echo "205")
-                    bw_str=$([ "${ch}" -le "203" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "217" ]; then
-                    ch=$([ "${ch}" -lt "213" ] && echo "209" || echo "213")
-                    bw_str=$([ "${ch}" -le "211" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "225" ]; then
-                    ch=$([ "${ch}" -lt "221" ] && echo "217" || echo "221")
-                    bw_str=$([ "${ch}" -le "219" ] && echo "HT40+" || echo "HT40-")
-                elif [ ${ch} -lt "233" ]; then
-                    ch=$([ "${ch}" -lt "229" ] && echo "225" || echo "229")
-                    bw_str=$([ "${ch}" -le "227" ] && echo "HT40+" || echo "HT40-")
-                fi
+                bw_str="40"
+                control_ch=$(expr ${ch} - "2")
                 ;;
             "0")
-                local bw_str="HT20"
+                bw_str="20"
+                control_ch=${ch}
                 ;;
         esac
+        if [ "${band}" = "1" ]; then
+            base_freq=5180
+            base_chan=36
+        else
+            base_freq=5955
+        fi
     fi
 
-    local control_freq=$(((ch - base_chan) * 5 + base_freq))
-    do_cmd "iw dev mon${phy_idx} set freq ${control_freq} ${bw_str}"
+    local center_freq=$(((ch - base_chan) * 5 + base_freq))
+    local control_freq=$(((control_ch - base_chan) * 5 + base_freq))
+    if [ "${center_freq}" == "${control_freq}" ]; then
+        do_cmd "iw dev mon${phy_idx} set freq ${control_freq} ${bw_str}"
+    else
+        do_cmd "iw dev mon${phy_idx} set freq ${control_freq} ${bw_str} ${center_freq}"
+    fi
 }
 
 function convert_rxstat {
@@ -1327,7 +1155,7 @@
             param_new=${param}
             ;;
         "ATETXBW")
-            record_config ${cmd} ${param} ${iwpriv_file}
+            convert_bw ${param}
             skip=1
             ;;
         "ATECHANNEL")
diff --git a/recipes-wifi/atenl/files/src/nl.h b/recipes-wifi/atenl/files/src/nl.h
index 7d9b20d..b580a01 100644
--- a/recipes-wifi/atenl/files/src/nl.h
+++ b/recipes-wifi/atenl/files/src/nl.h
@@ -33,6 +33,9 @@
  * @MT76_TM_ATTR_TX_POWER_CONTROL: enable tx power control (u8)
  * @MT76_TM_ATTR_TX_POWER: per-antenna tx power array (nested, u8 attrs)
  *
+ * @MT76_TM_ATTR_TX_PKT_BW: per-packet data bandwidth (u8)
+ * @MT76_TM_ATTR_TX_PRI_SEL: primary channel selection index (u8)
+ *
  * @MT76_TM_ATTR_FREQ_OFFSET: RF frequency offset (u32)
  *
  * @MT76_TM_ATTR_STATS: statistics (nested, see &enum mt76_testmode_stats_attr)
@@ -104,6 +107,9 @@
 	MT76_TM_ATTR_TX_POWER_CONTROL,
 	MT76_TM_ATTR_TX_POWER,
 
+	MT76_TM_ATTR_TX_PKT_BW,
+	MT76_TM_ATTR_TX_PRI_SEL,
+
 	MT76_TM_ATTR_FREQ_OFFSET,
 
 	MT76_TM_ATTR_STATS,
diff --git a/recipes-wifi/hostapd/files/hostapd-init-EHT.sh b/recipes-wifi/hostapd/files/hostapd-init-EHT.sh
index 7651e04..340e16c 100644
--- a/recipes-wifi/hostapd/files/hostapd-init-EHT.sh
+++ b/recipes-wifi/hostapd/files/hostapd-init-EHT.sh
@@ -126,6 +126,15 @@
     echo vht_capab=$vht_capab >> /etc/hostapd-5G.conf
 }
 
+gen_he_6ghz_reg_pwr_type() { 
+    local config_file="$1"
+
+    if grep -q "^country_code=US" "$config_file"; then
+	sed -i "/^he_6ghz_reg_pwr_type=.*/c\he_6ghz_reg_pwr_type=0" "$config_file"
+	grep -q "^he_6ghz_reg_pwr_type" "$config_file" || echo "he_6ghz_reg_pwr_type=0" >> "$config_file"
+    fi
+}
+
 create_hostapdConf() {
 	devidx=0
 	phyidx=0
@@ -202,6 +211,7 @@
         fi
 
         if [ "$band" == "6g" ]; then
+	    gen_he_6ghz_reg_pwr_type /etc/hostapd-6G.conf
             cp -f /etc/hostapd-6G.conf /nvram/hostapd"$devidx".conf
         fi
 
diff --git a/recipes-wifi/hostapd/files/hostapd.uc b/recipes-wifi/hostapd/files/hostapd.uc
index 48f12c5..19b1490 100644
--- a/recipes-wifi/hostapd/files/hostapd.uc
+++ b/recipes-wifi/hostapd/files/hostapd.uc
@@ -64,6 +64,7 @@
 	let freq = params.frequency;
 	let bw320_offset = params.bw320_offset;
 	let band_idx = params.band_idx;
+	let punct_bitmap = params.punct_bitmap;
 	if (!freq)
 		return null;
 
@@ -94,7 +95,7 @@
 	if (freq < 4000)
 		width = 0;
 
-	return hostapd.freq_info(freq, sec_offset, width, bw320_offset, band_idx);
+	return hostapd.freq_info(freq, sec_offset, width, bw320_offset, band_idx, punct_bitmap);
 }
 
 function iface_add(phy, config, phy_status)
@@ -785,6 +786,7 @@
 			band_idx: 0,
 			csa: true,
 			csa_count: 0,
+			punct_bitmap: 0,
 		},
 		call: ex_wrap(function(req) {
 			if (req.args.up == null || !req.args.phy)
@@ -799,6 +801,7 @@
 			hostapd.printf(`    * bw320_offset: ${req.args.bw320_offset}`);
 			hostapd.printf(`    * band_idx: ${req.args.band_idx}`);
 			hostapd.printf(`    * csa: ${req.args.csa}`);
+			hostapd.printf(`    * punct_bitmap: ${req.args.punct_bitmap}`);
 
 			let phy = req.args.phy;
 			let config = hostapd.data.config[phy];
diff --git a/recipes-wifi/hostapd/files/openwrt_script/hostapd.sh b/recipes-wifi/hostapd/files/openwrt_script/hostapd.sh
index 763702e..40266db 100644
--- a/recipes-wifi/hostapd/files/openwrt_script/hostapd.sh
+++ b/recipes-wifi/hostapd/files/openwrt_script/hostapd.sh
@@ -383,6 +383,9 @@
 	config_add_string fils_dhcp
 
 	config_add_int ocv
+
+	config_add_boolean apup
+	config_add_string apup_peer_ifname_prefix
 }
 
 hostapd_set_vlan_file() {
@@ -569,7 +572,7 @@
 		ppsk airtime_bss_weight airtime_bss_limit airtime_sta_weight \
 		multicast_to_unicast_all proxy_arp per_sta_vif \
 		eap_server eap_user_file ca_cert server_cert private_key private_key_passwd server_id \
-		vendor_elements fils ocv
+		vendor_elements fils ocv apup
 
 	set_default fils 0
 	set_default isolate 0
@@ -593,6 +596,7 @@
 	set_default airtime_bss_weight 0
 	set_default airtime_bss_limit 0
 	set_default eap_server 0
+	set_default apup 0
 
 	/usr/sbin/hostapd -vfils || fils=0
 
@@ -1163,6 +1167,16 @@
 		append bss_conf "per_sta_vif=$per_sta_vif" "$N"
 	fi
 
+	if [ "$apup" -gt 0 ]; then
+		append bss_conf "apup=$apup" "$N"
+
+		local apup_peer_ifname_prefix
+		json_get_vars apup_peer_ifname_prefix
+		if [ -n "$apup_peer_ifname_prefix" ] ; then
+			append bss_conf "apup_peer_ifname_prefix=$apup_peer_ifname_prefix" "$N"
+		fi
+	fi
+
 	json_get_values opts hostapd_bss_options
 	for val in $opts; do
 		append bss_conf "$val" "$N"
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0001-ctrl_iface-create-link-based-hapd-control-sockets.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0001-ctrl_iface-create-link-based-hapd-control-sockets.patch
new file mode 100644
index 0000000..580179a
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0001-ctrl_iface-create-link-based-hapd-control-sockets.patch
@@ -0,0 +1,228 @@
+From 166ae43a8066cbc70d5d990cfd29cb6c4c2afc67 Mon Sep 17 00:00:00 2001
+From: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
+Date: Tue, 23 Apr 2024 11:01:23 +0530
+Subject: [PATCH 001/126] ctrl_iface: create link based hapd control sockets
+
+Create link based control sockets to access the link based commands
+through hostapd_cli. This will create the link interfaces in the name of
+wlan<X>_link<X>
+
+Example:
+To fetch link 0 status from wlan0, below command can be used -
+    $ hostapd_cli -i wlan0 -l 0 status
+
+On failure of link/interface selection, below error will be observed
+    $ hostapd_cli -i wlan0 -l 2 status
+    Failed to connect to hostapd - wpa_ctrl_open: No such file or directory
+
+Signed-off-by: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
+Co-developed-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ hostapd/ctrl_iface.c  | 16 ++++++++++++++--
+ hostapd/hostapd_cli.c | 30 ++++++++++++++++++++++++++++--
+ src/ap/hostapd.c      | 28 ++++++++++++++++++++++++++++
+ src/ap/hostapd.h      |  1 +
+ src/common/wpa_ctrl.h |  4 ++++
+ 5 files changed, 75 insertions(+), 4 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 39b9ef59d..3fa33be7a 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4687,18 +4687,26 @@ static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
+ {
+ 	char *buf;
+ 	size_t len;
++	char *ctrl_sock_iface;
++
++#ifdef CONFIG_IEEE80211BE
++	ctrl_sock_iface = hapd->ctrl_sock_iface;
++#else
++	ctrl_sock_iface = hapd->conf->iface;
++#endif /* CONFIG_IEEE80211BE */
+ 
+ 	if (hapd->conf->ctrl_interface == NULL)
+ 		return NULL;
+ 
+ 	len = os_strlen(hapd->conf->ctrl_interface) +
+-		os_strlen(hapd->conf->iface) + 2;
++		os_strlen(ctrl_sock_iface) + 2;
++
+ 	buf = os_malloc(len);
+ 	if (buf == NULL)
+ 		return NULL;
+ 
+ 	os_snprintf(buf, len, "%s/%s",
+-		    hapd->conf->ctrl_interface, hapd->conf->iface);
++		    hapd->conf->ctrl_interface, ctrl_sock_iface);
+ 	buf[len - 1] = '\0';
+ 	return buf;
+ }
+@@ -4869,7 +4877,11 @@ fail:
+ #endif /* ANDROID */
+ 
+ 	if (os_strlen(hapd->conf->ctrl_interface) + 1 +
++#ifdef CONFIG_IEEE80211BE
++	    os_strlen(hapd->ctrl_sock_iface) >= sizeof(addr.sun_path))
++#else
+ 	    os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path))
++#endif /* CONFIG_IEEE80211BE */
+ 		goto fail;
+ 
+ 	s = socket(PF_UNIX, SOCK_DGRAM, 0);
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index eb8a38350..f05a734fe 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -54,7 +54,11 @@ static void usage(void)
+ 	fprintf(stderr, "%s\n", hostapd_cli_version);
+ 	fprintf(stderr,
+ 		"\n"
++#ifdef CONFIG_IEEE80211BE
++		"usage: hostapd_cli [-p<path>] [-i<ifname>] [-l<link_id>] [-hvBr] "
++#else
+ 		"usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvBr] "
++#endif /* CONFIG_IEEE80211BE */
+ 		"[-a<path>] \\\n"
+ 		"                   [-P<pid file>] [-G<ping interval>] [command..]\n"
+ 		"\n"
+@@ -74,7 +78,12 @@ static void usage(void)
+ 		"   -B           run a daemon in the background\n"
+ 		"   -i<ifname>   Interface to listen on (default: first "
+ 		"interface found in the\n"
+-		"                socket path)\n\n");
++		"                socket path)\n"
++#ifdef CONFIG_IEEE80211BE
++		"   -l<link_id>  Link ID of the interface in case of Multi-Link\n"
++		"                Operation\n"
++#endif /* CONFIG_IEEE80211BE */
++		"\n");
+ 	print_help(stderr, NULL);
+ }
+ 
+@@ -2205,19 +2214,26 @@ static void hostapd_cli_action(struct wpa_ctrl *ctrl)
+ 	eloop_unregister_read_sock(fd);
+ }
+ 
+-
+ int main(int argc, char *argv[])
+ {
+ 	int warning_displayed = 0;
+ 	int c;
+ 	int daemonize = 0;
+ 	int reconnect = 0;
++#ifdef CONFIG_IEEE80211BE
++	int link_id = -1;
++	char buf[300];
++#endif /* CONFIG_IEEE80211BE */
+ 
+ 	if (os_program_init())
+ 		return -1;
+ 
+ 	for (;;) {
++#ifdef CONFIG_IEEE80211BE
++		c = getopt(argc, argv, "a:BhG:i:l:p:P:rs:v");
++#else
+ 		c = getopt(argc, argv, "a:BhG:i:p:P:rs:v");
++#endif /* CONFIG_IEEE80211BE */
+ 		if (c < 0)
+ 			break;
+ 		switch (c) {
+@@ -2252,6 +2268,16 @@ int main(int argc, char *argv[])
+ 		case 's':
+ 			client_socket_dir = optarg;
+ 			break;
++#ifdef CONFIG_IEEE80211BE
++		case 'l':
++			link_id = atoi(optarg);
++			os_memset(buf, '\0', sizeof(buf));
++			os_snprintf(buf, sizeof(buf), "%s_%s%d",
++				    ctrl_ifname, WPA_CTRL_IFACE_LINK_NAME, link_id);
++			os_free(ctrl_ifname);
++			ctrl_ifname = os_strdup(buf);
++			break;
++#endif /* CONFIG_IEEE80211BE */
+ 		default:
+ 			usage();
+ 			return -1;
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index a05de030d..c819c30cf 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -1808,12 +1808,37 @@ int hostapd_set_acl(struct hostapd_data *hapd)
+ }
+ 
+ 
++static void hostapd_set_ctrl_sock_iface(struct hostapd_data *hapd)
++{
++#ifdef CONFIG_IEEE80211BE
++	os_memset(hapd->ctrl_sock_iface, '\0',
++		  sizeof(hapd->ctrl_sock_iface));
++	os_strlcpy(hapd->ctrl_sock_iface, hapd->conf->iface,
++		   sizeof(hapd->ctrl_sock_iface));
++
++	if (hapd->conf->mld_ap) {
++		char buf[128];
++
++		os_memset(buf, '\0', sizeof(buf));
++		os_snprintf(buf, sizeof(buf), "%s_%s%d",
++			    hapd->conf->iface, WPA_CTRL_IFACE_LINK_NAME,
++			    hapd->mld_link_id);
++		os_memset(hapd->ctrl_sock_iface, '\0',
++			  sizeof(hapd->ctrl_sock_iface));
++		os_strlcpy(hapd->ctrl_sock_iface, buf, sizeof(buf));
++	}
++#endif /* CONFIG_IEEE80211BE */
++}
++
++
+ static int start_ctrl_iface_bss(struct hostapd_data *hapd)
+ {
+ 	if (!hapd->iface->interfaces ||
+ 	    !hapd->iface->interfaces->ctrl_iface_init)
+ 		return 0;
+ 
++	hostapd_set_ctrl_sock_iface(hapd);
++
+ 	if (hapd->iface->interfaces->ctrl_iface_init(hapd)) {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Failed to setup control interface for %s",
+@@ -1834,6 +1859,9 @@ static int start_ctrl_iface(struct hostapd_iface *iface)
+ 
+ 	for (i = 0; i < iface->num_bss; i++) {
+ 		struct hostapd_data *hapd = iface->bss[i];
++
++		hostapd_set_ctrl_sock_iface(hapd);
++
+ 		if (iface->interfaces->ctrl_iface_init(hapd)) {
+ 			wpa_printf(MSG_ERROR,
+ 				   "Failed to setup control interface for %s",
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index dcf395ca5..34a665562 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -476,6 +476,7 @@ struct hostapd_data {
+ 	struct hostapd_mld *mld;
+ 	struct dl_list link;
+ 	u8 mld_link_id;
++	char ctrl_sock_iface[IFNAMSIZ + 1];
+ #ifdef CONFIG_TESTING_OPTIONS
+ 	u8 eht_mld_link_removal_count;
+ #endif /* CONFIG_TESTING_OPTIONS */
+diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
+index f6142501e..865ac6d91 100644
+--- a/src/common/wpa_ctrl.h
++++ b/src/common/wpa_ctrl.h
+@@ -674,4 +674,8 @@ char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl);
+ }
+ #endif
+ 
++#ifdef CONFIG_IEEE80211BE
++#define WPA_CTRL_IFACE_LINK_NAME	"link"
++#endif /* CONFIG_IEEE80211BE */
++
+ #endif /* WPA_CTRL_H */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0001-hostapd-MLO-fix-for_each_mld_link-macro.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0001-hostapd-MLO-fix-for_each_mld_link-macro.patch
deleted file mode 100644
index c8e7f92..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0001-hostapd-MLO-fix-for_each_mld_link-macro.patch
+++ /dev/null
@@ -1,101 +0,0 @@
-From f5102b209870065c3e3719dd113892eafd4bb59e Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:31 +0530
-Subject: [PATCH 001/104] hostapd: MLO: fix for_each_mld_link macro
-
-Currently for_each_mld_link macro uses 3 nested for loops. Since now the
-affliated links are linked together via linked list, the logic can
-be improvised by using dl_list_for_each macro instead which uses one for
-loop.
-
-Modify for_each_mld_link macro to use dl_list_for_each instead.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/beacon.c   | 10 +---------
- src/ap/hostapd.h  | 17 +++--------------
- src/ap/sta_info.c |  4 +---
- 3 files changed, 5 insertions(+), 26 deletions(-)
-
-diff --git a/src/ap/beacon.c b/src/ap/beacon.c
-index 32865f667..195c7bbd9 100644
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -945,7 +945,6 @@ static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd,
- {
- 	struct probe_resp_params sta_info_params;
- 	struct hostapd_data *link;
--	unsigned int probed_mld_id, i, j;
- 
- 	params->mld_ap = NULL;
- 	params->mld_info = os_zalloc(sizeof(*params->mld_info));
-@@ -956,14 +955,7 @@ static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd,
- 		   "MLD: Got ML probe request with AP MLD ID %d for links %04x",
- 		   mld_id, links);
- 
--	/*
--	 * We want to include the AP MLD ID in the response if it was
--	 * included in the request.
--	 */
--	probed_mld_id = mld_id != -1 ? mld_id : hostapd_get_mld_id(hapd);
--
--	for_each_mld_link(link, i, j, hapd->iface->interfaces,
--			  probed_mld_id) {
-+	for_each_mld_link(link, hapd) {
- 		struct mld_link_info *link_info;
- 		size_t buflen;
- 		u8 mld_link_id = link->mld_link_id;
-diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
-index affe4f604..d12efb104 100644
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -817,19 +817,8 @@ struct hostapd_data * hostapd_mld_get_first_bss(struct hostapd_data *hapd);
- 
- bool hostapd_mld_is_first_bss(struct hostapd_data *hapd);
- 
--#define for_each_mld_link(_link, _bss_idx, _iface_idx, _ifaces, _mld_id) \
--	for (_iface_idx = 0;						\
--	     _iface_idx < (_ifaces)->count;				\
--	     _iface_idx++)						\
--		for (_bss_idx = 0;					\
--		     _bss_idx <						\
--			(_ifaces)->iface[_iface_idx]->num_bss;		\
--		     _bss_idx++)					\
--			for (_link =					\
--			     (_ifaces)->iface[_iface_idx]->bss[_bss_idx]; \
--			    _link && _link->conf->mld_ap &&		\
--				hostapd_get_mld_id(_link) == _mld_id;	\
--			    _link = NULL)
-+#define for_each_mld_link(partner, self) \
-+	dl_list_for_each(partner, &self->mld->links, struct hostapd_data, link)
- 
- #else /* CONFIG_IEEE80211BE */
- 
-@@ -838,7 +827,7 @@ static inline bool hostapd_mld_is_first_bss(struct hostapd_data *hapd)
- 	return true;
- }
- 
--#define for_each_mld_link(_link, _bss_idx, _iface_idx, _ifaces, _mld_id) \
-+#define for_each_mld_link(partner, self) \
- 	if (false)
- 
- #endif /* CONFIG_IEEE80211BE */
-diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
-index 122880a3d..2423ff189 100644
---- a/src/ap/sta_info.c
-+++ b/src/ap/sta_info.c
-@@ -1761,10 +1761,8 @@ static void ap_sta_remove_link_sta(struct hostapd_data *hapd,
- 				   struct sta_info *sta)
- {
- 	struct hostapd_data *tmp_hapd;
--	unsigned int i, j;
- 
--	for_each_mld_link(tmp_hapd, i, j, hapd->iface->interfaces,
--			  hostapd_get_mld_id(hapd)) {
-+	for_each_mld_link(tmp_hapd, hapd) {
- 		struct sta_info *tmp_sta;
- 
- 		if (hapd == tmp_hapd)
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0002-ctrl_iface-MLO-introduce-MLD-level-socket.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0002-ctrl_iface-MLO-introduce-MLD-level-socket.patch
new file mode 100644
index 0000000..448534e
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0002-ctrl_iface-MLO-introduce-MLD-level-socket.patch
@@ -0,0 +1,479 @@
+From 631a48c1a241bf9515d296eeea5d6060bef96cff Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Tue, 23 Apr 2024 11:01:24 +0530
+Subject: [PATCH 002/126] ctrl_iface: MLO: introduce MLD level socket
+
+With MLO, each link have socket created with "<ifname>_link<link id>" under
+the control interface directory.
+
+Introduce a MLD level socket - "<ifname>" as well under the same control
+interface directory. This socket can be used to pass the command to its
+partner links directly instead of using the link level socket. Link ID
+needs to be passed with the command.
+
+The structure of the command is -
+ "<COMMAND APPLICABALE FOR THE LINK> LINKID <link id>"
+
+Directory looks something like this -
+  $ ls /var/run/hostapd/
+    wlan0
+    wlan0_link0
+    wlan0_link1
+
+wlan0 here is the MLD level socket. Rest are each link level.
+
+This would also help to maintain backwards compatibility with applications
+which looks for <ifname> under the control interface directory.`
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ hostapd/ctrl_iface.c | 335 +++++++++++++++++++++++++++++++++++++++++++
+ hostapd/ctrl_iface.h |   4 +
+ hostapd/main.c       |   5 +
+ src/ap/hostapd.c     |  11 ++
+ src/ap/hostapd.h     |   6 +
+ 5 files changed, 361 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 3fa33be7a..5fe29147f 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4682,6 +4682,341 @@ done:
+ }
+ 
+ 
++#ifdef CONFIG_IEEE80211BE
++#ifndef CONFIG_CTRL_IFACE_UDP
++static int hostapd_mld_ctrl_iface_attach(struct hostapd_mld *mld,
++					 struct sockaddr_storage *from,
++					 socklen_t fromlen, const char *input)
++{
++	return ctrl_iface_attach(&mld->ctrl_dst, from, fromlen, input);
++}
++
++
++static int hostapd_mld_ctrl_iface_detach(struct hostapd_mld *mld,
++					 struct sockaddr_storage *from,
++					 socklen_t fromlen)
++{
++	return ctrl_iface_detach(&mld->ctrl_dst, from, fromlen);
++}
++
++
++static int hostapd_mld_ctrl_iface_receive_process(struct hostapd_mld *mld,
++						  char *buf, char *reply,
++						  int reply_size,
++						  struct sockaddr_storage *from,
++						  socklen_t fromlen)
++{
++	struct hostapd_data *link_hapd, *link_itr;
++	int reply_len, link_id = -1;
++	char *link_cmd;
++	bool found = false;
++
++	os_memcpy(reply, "OK\n", 3);
++	reply_len = 3;
++
++	/* Check if link id is provided in the command or not */
++	link_cmd = os_strstr(buf, "LINKID");
++	if (link_cmd) {
++		/* Trim the link id part now */
++		*(link_cmd - 1) = '\0';
++
++		link_cmd += 7;
++		link_id = atoi(link_cmd);
++
++		if (link_id < 0 || link_id >= 15) {
++			os_memcpy(reply, "INVALID LINK ID\n", 16);
++			reply_len = 16;
++			return reply_len;
++		}
++
++		link_hapd = mld->fbss;
++		if (!link_hapd) {
++			os_memcpy(reply, "NO LINKS ACTIVE\n", 16);
++			reply_len = 16;
++			return reply_len;
++		}
++
++		for_each_mld_link(link_itr, link_hapd) {
++			if (link_itr->mld_link_id == link_id) {
++				found = true;
++				break;
++			}
++		}
++
++		if (!found) {
++			os_memcpy(reply, "FAIL\n", 5);
++			reply_len = 5;
++			return reply_len;
++		}
++
++		link_hapd = link_itr;
++	} else {
++		link_hapd = mld->fbss;
++	}
++
++	if (os_strcmp(buf, "PING") == 0) {
++		os_memcpy(reply, "PONG\n", 5);
++		reply_len = 5;
++	} else if (os_strcmp(buf, "ATTACH") == 0) {
++		if (hostapd_mld_ctrl_iface_attach(mld, from, fromlen, NULL))
++			reply_len = -1;
++	} else if (os_strncmp(buf, "ATTACH ", 7) == 0) {
++		if (hostapd_mld_ctrl_iface_attach(mld, from, fromlen, buf + 7))
++			reply_len = -1;
++	} else if (os_strcmp(buf, "DETACH") == 0) {
++		if (hostapd_mld_ctrl_iface_detach(mld, from, fromlen))
++			reply_len = -1;
++	} else {
++		if (link_id == -1)
++			wpa_printf(MSG_DEBUG, "Link ID not provided, using first link BSS (if available)");
++
++		if (!link_hapd)
++			reply_len = -1;
++		else
++			reply_len =
++				hostapd_ctrl_iface_receive_process(link_hapd, buf,
++								   reply, reply_size,
++								   from, fromlen);
++	}
++
++	if (reply_len < 0) {
++		os_memcpy(reply, "FAIL\n", 5);
++		reply_len = 5;
++	}
++
++	return reply_len;
++}
++
++
++static void hostapd_mld_ctrl_iface_receive(int sock, void *eloop_ctx,
++					   void *sock_ctx)
++{
++	struct hostapd_mld *mld = eloop_ctx;
++	char buf[4096];
++	int res;
++	struct sockaddr_storage from;
++	socklen_t fromlen = sizeof(from);
++	char *reply, *pos = buf;
++	const int reply_size = 4096;
++	int reply_len;
++	int level = MSG_DEBUG;
++
++	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
++		       (struct sockaddr *) &from, &fromlen);
++	if (res < 0) {
++		wpa_printf(MSG_ERROR, "recvfrom(mld ctrl_iface): %s",
++			   strerror(errno));
++		return;
++	}
++	buf[res] = '\0';
++
++	reply = os_malloc(reply_size);
++	if (reply == NULL) {
++		if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
++			   fromlen) < 0) {
++			wpa_printf(MSG_DEBUG, "MLD CTRL: sendto failed: %s",
++				   strerror(errno));
++		}
++		return;
++	}
++
++	if (os_strcmp(pos, "PING") == 0)
++		level = MSG_EXCESSIVE;
++
++	wpa_hexdump_ascii(level, "RX MLD ctrl_iface", pos, res);
++
++	reply_len = hostapd_mld_ctrl_iface_receive_process(mld, pos,
++							   reply, reply_size,
++							   &from, fromlen);
++
++	if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
++		   fromlen) < 0) {
++		wpa_printf(MSG_DEBUG, "MLD CTRL: sendto failed: %s",
++			   strerror(errno));
++	}
++	os_free(reply);
++}
++
++
++static char * hostapd_mld_ctrl_iface_path(struct hostapd_mld *mld)
++{
++	char *buf;
++	size_t len;
++
++	if (!mld->ctrl_interface)
++		return NULL;
++
++	len = os_strlen(mld->ctrl_interface) + os_strlen(mld->name) + 2;
++
++	buf = os_malloc(len);
++	if (buf == NULL)
++		return NULL;
++
++	os_snprintf(buf, len, "%s/%s", mld->ctrl_interface, mld->name);
++	buf[len - 1] = '\0';
++	return buf;
++}
++#endif /* !CONFIG_CTRL_IFACE_UDP */
++
++
++int hostapd_mld_ctrl_iface_init(struct hostapd_mld *mld)
++{
++#ifndef CONFIG_CTRL_IFACE_UDP
++	struct sockaddr_un addr;
++	int s = -1;
++	char *fname = NULL;
++
++	if (!mld)
++		return -1;
++
++	if (mld->ctrl_sock > -1) {
++		wpa_printf(MSG_DEBUG, "MLD %s ctrl_iface already exists!",
++			   mld->name);
++		return 0;
++	}
++
++	dl_list_init(&mld->ctrl_dst);
++
++	if (mld->ctrl_interface == NULL)
++		return 0;
++
++	if (mkdir(mld->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
++		if (errno == EEXIST) {
++			wpa_printf(MSG_DEBUG, "Using existing control "
++				   "interface directory.");
++		} else {
++			wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s",
++				   strerror(errno));
++			goto fail;
++		}
++	}
++
++	if (os_strlen(mld->ctrl_interface) + 1 +
++	    os_strlen(mld->name) >= sizeof(addr.sun_path))
++		goto fail;
++
++	s = socket(PF_UNIX, SOCK_DGRAM, 0);
++	if (s < 0) {
++		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
++		goto fail;
++	}
++
++	os_memset(&addr, 0, sizeof(addr));
++#ifdef __FreeBSD__
++	addr.sun_len = sizeof(addr);
++#endif /* __FreeBSD__ */
++	addr.sun_family = AF_UNIX;
++
++	fname = hostapd_mld_ctrl_iface_path(mld);
++	if (fname == NULL)
++		goto fail;
++
++	os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
++
++	wpa_printf(MSG_DEBUG, "Setting up MLD %s ctrl_iface", mld->name);
++
++	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
++		wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
++			   strerror(errno));
++		if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
++			wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
++				   " allow connections - assuming it was left"
++				   "over from forced program termination");
++			if (unlink(fname) < 0) {
++				wpa_printf(MSG_ERROR,
++					   "Could not unlink existing ctrl_iface socket '%s': %s",
++					   fname, strerror(errno));
++				goto fail;
++			}
++			if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
++			    0) {
++				wpa_printf(MSG_ERROR,
++					   "hostapd-ctrl-iface: bind(PF_UNIX): %s",
++					   strerror(errno));
++				goto fail;
++			}
++			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
++				   "ctrl_iface socket '%s'", fname);
++		} else {
++			wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
++				   "be in use - cannot override it");
++			wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
++				   "not used anymore", fname);
++			os_free(fname);
++			fname = NULL;
++			goto fail;
++		}
++	}
++
++	if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
++		wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s",
++			   strerror(errno));
++		goto fail;
++	}
++	os_free(fname);
++
++	mld->ctrl_sock = s;
++
++	if (eloop_register_read_sock(s, hostapd_mld_ctrl_iface_receive, mld,
++				     NULL) < 0)
++		return -1;
++
++	return 0;
++
++fail:
++	if (s >= 0)
++		close(s);
++	if (fname) {
++		unlink(fname);
++		os_free(fname);
++	}
++	return -1;
++#endif /* !CONFIG_CTRL_IFACE_UDP */
++	return 0;
++}
++
++
++void hostapd_mld_ctrl_iface_deinit(struct hostapd_mld *mld)
++{
++#ifndef CONFIG_CTRL_IFACE_UDP
++	struct wpa_ctrl_dst *dst, *prev;
++
++	if (mld->ctrl_sock > -1) {
++		char *fname;
++		eloop_unregister_read_sock(mld->ctrl_sock);
++		close(mld->ctrl_sock);
++		mld->ctrl_sock = -1;
++
++		fname = hostapd_mld_ctrl_iface_path(mld);
++		if (fname)
++			unlink(fname);
++		os_free(fname);
++
++		if (mld->ctrl_interface &&
++		    rmdir(mld->ctrl_interface) < 0) {
++			if (errno == ENOTEMPTY) {
++				wpa_printf(MSG_DEBUG, "MLD Control interface "
++					   "directory not empty - leaving it "
++					   "behind");
++			} else {
++				wpa_printf(MSG_ERROR,
++					   "rmdir[ctrl_interface=%s]: %s",
++					   mld->ctrl_interface,
++					   strerror(errno));
++			}
++		}
++	}
++
++	dl_list_for_each_safe(dst, prev, &mld->ctrl_dst, struct wpa_ctrl_dst,
++			      list)
++		os_free(dst);
++#endif /* !CONFIG_CTRL_IFACE_UDP */
++
++	os_free(mld->ctrl_interface);
++}
++#endif /* CONFIG_IEEE80211BE */
++
++
+ #ifndef CONFIG_CTRL_IFACE_UDP
+ static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
+ {
+diff --git a/hostapd/ctrl_iface.h b/hostapd/ctrl_iface.h
+index 3341a66bd..ec5a95be7 100644
+--- a/hostapd/ctrl_iface.h
++++ b/hostapd/ctrl_iface.h
+@@ -14,6 +14,10 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd);
+ void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd);
+ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface);
+ void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface);
++#ifdef CONFIG_IEEE80211BE
++int hostapd_mld_ctrl_iface_init(struct hostapd_mld *mld);
++void hostapd_mld_ctrl_iface_deinit(struct hostapd_mld *mld);
++#endif /* CONFIG_IEEE80211BE */
+ #else /* CONFIG_NO_CTRL_IFACE */
+ static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
+ {
+diff --git a/hostapd/main.c b/hostapd/main.c
+index 00e02bb03..aa1f69812 100644
+--- a/hostapd/main.c
++++ b/hostapd/main.c
+@@ -748,6 +748,7 @@ static void hostapd_global_cleanup_mld(struct hapd_interfaces *interfaces)
+ 		if (!interfaces->mld[i])
+ 			continue;
+ 
++		interfaces->mld_ctrl_iface_deinit(interfaces->mld[i]);
+ 		os_free(interfaces->mld[i]);
+ 		interfaces->mld[i] = NULL;
+ 	}
+@@ -793,6 +794,10 @@ int main(int argc, char *argv[])
+ 	interfaces.global_iface_path = NULL;
+ 	interfaces.global_iface_name = NULL;
+ 	interfaces.global_ctrl_sock = -1;
++#ifdef CONFIG_IEEE80211BE
++	interfaces.mld_ctrl_iface_init = hostapd_mld_ctrl_iface_init;
++	interfaces.mld_ctrl_iface_deinit = hostapd_mld_ctrl_iface_deinit;
++#endif /* CONFIG_IEEE80211BE */
+ 	dl_list_init(&interfaces.global_ctrl_dst);
+ #ifdef CONFIG_ETH_P_OUI
+ 	dl_list_init(&interfaces.eth_p_oui);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index c819c30cf..36d48ae09 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -3093,9 +3093,18 @@ static void hostapd_bss_setup_multi_link(struct hostapd_data *hapd,
+ 
+ 	os_strlcpy(mld->name, conf->iface, sizeof(conf->iface));
+ 	dl_list_init(&mld->links);
++	mld->ctrl_sock = -1;
++	mld->ctrl_interface = os_strdup(hapd->conf->ctrl_interface);
+ 
+ 	wpa_printf(MSG_DEBUG, "AP MLD %s created", mld->name);
+ 
++	/*
++	 * Initialize MLD control interfaces early to allow external monitoring of
++	 * link setup operations.
++	 */
++	if (interfaces->mld_ctrl_iface_init(mld))
++		goto fail;
++
+ 	hapd->mld = mld;
+ 	hostapd_mld_ref_inc(mld);
+ 	hostapd_bss_alloc_link_id(hapd);
+@@ -3155,6 +3164,8 @@ static void hostapd_cleanup_unused_mlds(struct hapd_interfaces *interfaces)
+ 		if (!remove && !forced_remove)
+ 			continue;
+ 
++		interfaces->mld_ctrl_iface_deinit(mld);
++
+ 		wpa_printf(MSG_DEBUG, "AP MLD %s: Freed%s", mld->name,
+ 			   forced_remove ? " (forced)" : "");
+ 		os_free(mld);
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 34a665562..2ef63e5f2 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -97,6 +97,8 @@ struct hapd_interfaces {
+ #ifdef CONFIG_IEEE80211BE
+ 	struct hostapd_mld **mld;
+ 	size_t mld_count;
++	int (*mld_ctrl_iface_init)(struct hostapd_mld *mld);
++	void (*mld_ctrl_iface_deinit)(struct hostapd_mld *mld);
+ #endif /* CONFIG_IEEE80211BE */
+ };
+ 
+@@ -519,6 +521,10 @@ struct hostapd_mld {
+ 
+ 	struct hostapd_data *fbss;
+ 	struct dl_list links; /* List head of all affiliated links */
++
++	int ctrl_sock;
++	struct dl_list ctrl_dst;
++	char *ctrl_interface; /* directory for UNIX domain sockets */
+ };
+ 
+ #define HOSTAPD_MLD_MAX_REF_COUNT      0xFF
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0002-hostapd-MLO-frame-link-add-command-on-per-BSS-basis.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0002-hostapd-MLO-frame-link-add-command-on-per-BSS-basis.patch
deleted file mode 100644
index 86c2263..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0002-hostapd-MLO-frame-link-add-command-on-per-BSS-basis.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From 3ca32441ecd9d1a52f736d4a4fffdc24de629e90 Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:32 +0530
-Subject: [PATCH 002/104] hostapd: MLO: frame link add command on per BSS basis
-
-Currently function nl80211_link_add() creates the link add NL message on
-drv basis which in turn uses drv's first BSS always. In order to support
-link add for various other interfaces, use BSS handler to create the NL
-message.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/drivers/driver_nl80211.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 4949de577..042bc97a8 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13876,7 +13876,7 @@ static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr,
- 		}
- 	}
- 
--	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ADD_LINK);
-+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_ADD_LINK);
- 	if (!msg ||
- 	    nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
- 	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0003-hostapd_cli-MLO-pass-LINKID-in-the-command.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0003-hostapd_cli-MLO-pass-LINKID-in-the-command.patch
new file mode 100644
index 0000000..9b45d05
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0003-hostapd_cli-MLO-pass-LINKID-in-the-command.patch
@@ -0,0 +1,194 @@
+From 702fc9f42fc30acd7f956994f887d02eef3c3ade Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Tue, 23 Apr 2024 11:01:25 +0530
+Subject: [PATCH 003/126] hostapd_cli: MLO: pass 'LINKID' in the command
+
+MLD level socket can take 'LINKID <link id>'. Add changes to pass this via
+hostapd_cli. User needs to give "link_id=<link_id>" in post fix fashion in
+the command in order to pass this link_id from cli.
+
+For example -
+$ hostapd_cli -i wlan0 status link_id=0 | grep freq=
+freq=2437
+
+$ hostapd_cli -i wlan0
+...
+Interactive mode
+
+> ping
+PONG
+>
+> status link_id=0
+Command for 'LINKID 0'
+state=ENABLED
+phy=phy0
+freq=2437
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ hostapd/hostapd_cli.c | 39 +++++++++++++++++++++++++++++++
+ src/common/wpa_ctrl.c | 54 +++++++++++++++++++++++++++++++++++++++++++
+ src/common/wpa_ctrl.h |  3 +++
+ 3 files changed, 96 insertions(+)
+
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index f05a734fe..d69525502 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1962,6 +1962,45 @@ static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ 	} else if (count == 0) {
+ 		printf("Unknown command '%s'\n", argv[0]);
+ 	} else {
++#ifdef CONFIG_IEEE80211BE
++		char *pos, *end;
++		int i, j, link_id;
++		bool link_found = false;
++
++		wpa_ctrl_reset_mld_link(ctrl);
++		i = 0;
++
++		while (i < argc) {
++			pos = os_strstr(argv[i], "link_id=");
++			if (!pos) {
++				i++;
++				continue;
++			}
++
++			pos = pos + 8;
++			link_id = strtol(pos, &end, 10);
++
++			if (link_id < 0 || link_id >= 15) {
++				printf("Invalid link ID '%d'\n", link_id);
++				return;
++			}
++
++			link_found = true;
++
++			/* remove this link_id= from the arguements */
++			for (j = i + 1; j < argc; j++)
++				argv[j - 1] = argv[j];
++
++			argc--;
++			i = 0;
++		}
++
++		if (link_found) {
++			wpa_ctrl_set_mld_link(ctrl, link_id);
++			printf("Command for '%s'\n",
++			       wpa_ctrl_get_mld_link(ctrl));
++		}
++#endif /* CONFIG_IEEE80211BE */
+ 		match->handler(ctrl, argc - 1, &argv[1]);
+ 	}
+ }
+diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c
+index 7e197f094..d0c174c05 100644
+--- a/src/common/wpa_ctrl.c
++++ b/src/common/wpa_ctrl.c
+@@ -72,6 +72,13 @@ struct wpa_ctrl {
+ #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+ 	HANDLE pipe;
+ #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
++#ifdef CONFIG_IEEE80211BE
++	/* 'LINKID ' - 7 chars including space
++	 * 'XX' - Two chars max for link id
++	 * Total required 10 chars at least
++	 */
++	char link_id_str[10];
++#endif /* CONFIG_IEEE80211BE */
+ };
+ 
+ 
+@@ -488,6 +495,7 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
+ 	fd_set rfds;
+ 	const char *_cmd;
+ 	char *cmd_buf = NULL;
++	char *link_cmd_buf = NULL;
+ 	size_t _cmd_len;
+ 
+ #ifdef CONFIG_CTRL_IFACE_UDP
+@@ -510,6 +518,28 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
+ 		_cmd_len = cmd_len;
+ 	}
+ 
++#ifdef CONFIG_IEEE80211BE
++	if (os_strlen(ctrl->link_id_str)) {
++		char *pos;
++
++		_cmd_len = _cmd_len + 1 + os_strlen(ctrl->link_id_str);
++		link_cmd_buf = os_malloc(_cmd_len);
++		if (link_cmd_buf == NULL) {
++			if (cmd_buf)
++				os_free(cmd_buf);
++			return -1;
++		}
++
++		pos = link_cmd_buf;
++		os_strlcpy(pos, _cmd, _cmd_len);
++		pos += os_strlen(_cmd);
++		*pos++ = ' ';
++		os_memcpy(pos, ctrl->link_id_str, os_strlen(ctrl->link_id_str));
++		_cmd = link_cmd_buf;
++		wpa_ctrl_reset_mld_link(ctrl);
++	}
++#endif /* CONFIG_IEEE80211BE */
++
+ 	errno = 0;
+ 	started_at.sec = 0;
+ 	started_at.usec = 0;
+@@ -535,9 +565,11 @@ retry_send:
+ 		}
+ 	send_err:
+ 		os_free(cmd_buf);
++		os_free(link_cmd_buf);
+ 		return -1;
+ 	}
+ 	os_free(cmd_buf);
++	os_free(link_cmd_buf);
+ 
+ 	os_get_reltime(&ending_at);
+ 	ending_at.sec += 10;
+@@ -773,4 +805,26 @@ int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl)
+ 
+ #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+ 
++
++#ifdef CONFIG_IEEE80211BE
++void wpa_ctrl_reset_mld_link(struct wpa_ctrl *ctrl)
++{
++	os_memset(ctrl->link_id_str, '\0', sizeof(ctrl->link_id_str));
++}
++
++
++void wpa_ctrl_set_mld_link(struct wpa_ctrl *ctrl, int link_id)
++{
++	os_snprintf(ctrl->link_id_str, sizeof(ctrl->link_id_str),
++		    "LINKID %d", link_id);
++}
++
++
++char *wpa_ctrl_get_mld_link(struct wpa_ctrl *ctrl)
++{
++	return ctrl->link_id_str;
++}
++#endif /* CONFIG_IEEE80211BE */
++
++
+ #endif /* CONFIG_CTRL_IFACE */
+diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
+index 865ac6d91..d1ce1dd29 100644
+--- a/src/common/wpa_ctrl.h
++++ b/src/common/wpa_ctrl.h
+@@ -676,6 +676,9 @@ char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl);
+ 
+ #ifdef CONFIG_IEEE80211BE
+ #define WPA_CTRL_IFACE_LINK_NAME	"link"
++void wpa_ctrl_reset_mld_link(struct wpa_ctrl *ctrl);
++void wpa_ctrl_set_mld_link(struct wpa_ctrl *ctrl, int link_id);
++char *wpa_ctrl_get_mld_link(struct wpa_ctrl *ctrl);
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ #endif /* WPA_CTRL_H */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0003-nl80211-Print-the-interface-name-in-debug-during-lin.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0003-nl80211-Print-the-interface-name-in-debug-during-lin.patch
deleted file mode 100644
index 8dfcc5c..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0003-nl80211-Print-the-interface-name-in-debug-during-lin.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From 695a2dbff28bb259c2b4f8bfdfb9040f81ab7e90 Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:33 +0530
-Subject: [PATCH 003/104] nl80211: Print the interface name in debug during
- link add
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/drivers/driver_nl80211.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 042bc97a8..98948bfb1 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13900,8 +13900,8 @@ static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr,
- 	bss->valid_links |= BIT(link_id);
- 	bss->links[link_id].ctx = bss_ctx;
- 
--	wpa_printf(MSG_DEBUG, "nl80211: MLD: valid_links=0x%04x",
--		   bss->valid_links);
-+	wpa_printf(MSG_DEBUG, "nl80211: MLD: valid_links=0x%04x on %s",
-+		   bss->valid_links, bss->ifname);
- 	return 0;
- }
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0004-hostapd-MLO-send-link_id-on-sta_deauth.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0004-hostapd-MLO-send-link_id-on-sta_deauth.patch
deleted file mode 100644
index 5f56b72..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0004-hostapd-MLO-send-link_id-on-sta_deauth.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From 49a31ee63f482c930e001e2b6a13bf9261fcf5de Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:34 +0530
-Subject: [PATCH 004/104] hostapd: MLO: send link_id on sta_deauth()
-
-Function i802_sta_deauth() already has the link_id passed to it in its
-arguments. Use that to pass it down to send mlme handler.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/drivers/driver_nl80211.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 98948bfb1..e5fa22b59 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -8254,7 +8254,7 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
- 	return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
- 					    IEEE80211_HDRLEN +
- 					    sizeof(mgmt.u.deauth), 0, 0, 0, 0,
--					    0, NULL, 0, 0, -1);
-+					    0, NULL, 0, 0, link_id);
- }
- 
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0004-hostapd_cli-MLO-add-status-command-for-MLD-socket.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0004-hostapd_cli-MLO-add-status-command-for-MLD-socket.patch
new file mode 100644
index 0000000..08a7a11
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0004-hostapd_cli-MLO-add-status-command-for-MLD-socket.patch
@@ -0,0 +1,99 @@
+From 8e2802b52f5469e12dcb0bc38929f2721a2f0a83 Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Tue, 23 Apr 2024 11:01:26 +0530
+Subject: [PATCH 004/126] hostapd_cli: MLO: add status command for MLD socket
+
+Add MLD level 'status' command. Currently each link level socket has got
+'status' command. When the same is passed on MLD level socket without any
+link id, it routes it to first BSS of the MLD if available. Handle this
+now properly.
+
+If link id is not passed then it will be treated as MLD level status
+command.
+
+$ hostapd_cli -i wlan0
+....
+Interactive mode
+
+> status
+name=wlan0
+mld_address=AA:BB:CC:DD:EE:FF
+num_links=2
+LINK INFORMATION
+link_id=0
+link_addr=AA:BB:CC:DD:EE:EE
+link_id=1
+link_addr=AA:BB:CC:DD:FF:FF
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ hostapd/ctrl_iface.c | 46 ++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 46 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 5fe29147f..a584d370e 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4700,6 +4700,49 @@ static int hostapd_mld_ctrl_iface_detach(struct hostapd_mld *mld,
+ }
+ 
+ 
++int hostapd_ctrl_mld_iface_status(struct hostapd_mld *mld, char *buf,
++				  size_t buflen)
++{
++	struct hostapd_data *link_hapd;
++	int len = 0, ret;
++
++	ret = os_snprintf(buf + len, buflen - len,
++			  "name=%s\n"
++			  "mld_address=" MACSTR "\n"
++			  "num_links=%d\n",
++			  mld->name, MAC2STR(mld->mld_addr), mld->num_links);
++	if (os_snprintf_error(buflen - len, ret))
++		return len;
++	len += ret;
++
++	if (!mld->fbss) {
++		ret = os_snprintf(buf + len, buflen - len,
++				  "\n No Link information present\n");
++		if (os_snprintf_error(buflen - len, ret))
++			return len;
++		len += ret;
++	}
++
++	ret = os_snprintf(buf + len, buflen - len,
++			 "LINK INFORMATION\n");
++	if (os_snprintf_error(buflen - len, ret))
++		return len;
++	len += ret;
++
++	dl_list_for_each(link_hapd, &mld->links, struct hostapd_data, link) {
++		ret = os_snprintf(buf + len, buflen - len,
++				 "link_id=%d\n"
++				 "link_addr=" MACSTR "\n",
++				 link_hapd->mld_link_id, MAC2STR(link_hapd->own_addr));
++		if (os_snprintf_error(buflen - len, ret))
++			return len;
++		len += ret;
++	}
++
++	return len;
++}
++
++
+ static int hostapd_mld_ctrl_iface_receive_process(struct hostapd_mld *mld,
+ 						  char *buf, char *reply,
+ 						  int reply_size,
+@@ -4766,6 +4809,9 @@ static int hostapd_mld_ctrl_iface_receive_process(struct hostapd_mld *mld,
+ 	} else if (os_strcmp(buf, "DETACH") == 0) {
+ 		if (hostapd_mld_ctrl_iface_detach(mld, from, fromlen))
+ 			reply_len = -1;
++	} else if (os_strcmp(buf, "STATUS") == 0 && link_id == -1){
++		reply_len = hostapd_ctrl_mld_iface_status(mld, reply,
++							  reply_size);
+ 	} else {
+ 		if (link_id == -1)
+ 			wpa_printf(MSG_DEBUG, "Link ID not provided, using first link BSS (if available)");
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0005-hostapd-MLO-handle-auth-assoc-on-link-address.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0005-hostapd-MLO-handle-auth-assoc-on-link-address.patch
deleted file mode 100644
index e8adbc0..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0005-hostapd-MLO-handle-auth-assoc-on-link-address.patch
+++ /dev/null
@@ -1,77 +0,0 @@
-From ec1fdb73853632e9a9003f8c59620d1e12f6d2d0 Mon Sep 17 00:00:00 2001
-From: Sriram R <quic_srirrama@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:35 +0530
-Subject: [PATCH 005/104] hostapd: MLO: handle auth/assoc on link address
-
-Modify authentication and association frames to be always sent with link
-address as A1 and A3 for ease of Tx status handling.
-
-Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/ieee802_11.c | 25 ++-----------------------
- 1 file changed, 2 insertions(+), 23 deletions(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 5a3132de4..b20300bab 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -416,14 +416,7 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
- 	struct wpabuf *ml_resp = NULL;
- 
- #ifdef CONFIG_IEEE80211BE
--	/*
--	 * Once a non-AP MLD is added to the driver, the addressing should use
--	 * the MLD MAC address. Thus, use the MLD address instead of translating
--	 * the addresses.
--	 */
- 	if (ap_sta_is_mld(hapd, sta)) {
--		sa = hapd->mld->mld_addr;
--
- 		ml_resp = hostapd_ml_auth_resp(hapd);
- 		if (!ml_resp)
- 			return -1;
-@@ -444,7 +437,7 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
- 					    WLAN_FC_STYPE_AUTH);
- 	os_memcpy(reply->da, dst, ETH_ALEN);
- 	os_memcpy(reply->sa, sa, ETH_ALEN);
--	os_memcpy(reply->bssid, bssid, ETH_ALEN);
-+	os_memcpy(reply->bssid, sa, ETH_ALEN);
- 
- 	reply->u.auth.auth_alg = host_to_le16(auth_alg);
- 	reply->u.auth.auth_transaction = host_to_le16(auth_transaction);
-@@ -3265,14 +3258,9 @@ static void handle_auth(struct hostapd_data *hapd,
- 	bssid = mgmt->bssid;
- 
- #ifdef CONFIG_IEEE80211BE
--	 /*
--	  * Once a non-AP MLD is added to the driver, the addressing should use
--	  * the MLD MAC address. It is the responsibility of the driver to
--	  * handle the translations.
--	  */
- 	if (ap_sta_is_mld(hapd, sta)) {
- 		dst = sta->addr;
--		bssid = hapd->mld->mld_addr;
-+		bssid = hapd->own_addr;
- 	}
- #endif /* CONFIG_IEEE80211BE */
- 
-@@ -4823,15 +4811,6 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
- 			     (reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
- 			      WLAN_FC_STYPE_ASSOC_RESP));
- 
--#ifdef CONFIG_IEEE80211BE
--	/*
--	 * Once a non-AP MLD is added to the driver, the addressing should use
--	 * MLD MAC address.
--	 */
--	if (ap_sta_is_mld(hapd, sta) && allow_mld_addr_trans)
--		sa = hapd->mld->mld_addr;
--#endif /* CONFIG_IEEE80211BE */
--
- 	os_memcpy(reply->da, addr, ETH_ALEN);
- 	os_memcpy(reply->sa, sa, ETH_ALEN);
- 	os_memcpy(reply->bssid, sa, ETH_ALEN);
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0005-hostapd-afcd-add-AFC-daemon-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0005-hostapd-afcd-add-AFC-daemon-support.patch
new file mode 100644
index 0000000..2955527
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0005-hostapd-afcd-add-AFC-daemon-support.patch
@@ -0,0 +1,615 @@
+From f25cbdd28ff46a7beedd07f575614da7c7d269d7 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 17 May 2024 11:50:25 +0200
+Subject: [PATCH 005/126] hostapd: afcd: add AFC daemon support
+
+Introduce Automated Frequency Coordination Daemon (AFCD) support
+for UNII-5 and UNII-7 6GHz bands.
+AFCD will be used by hostapd AFC client in order to forward the AFC
+request to the AFC coordinator and decouple AFC connection management
+from hostapd.
+AFC is required for Standard Power Devices (SPDs) to determine a lists
+of channels and EIRP/PSD powers that are available in the 6GHz spectrum.
+AFCD is tested with AFC DUT Test Harness [0].
+Add afc-reply.json as reference for replies from the AFC coordinator.
+
+[0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main
+
+Tested-by: Felix Fietkau <nbd@nbd.name>
+Tested-by: Allen Ye <allen.ye@mediatek.com>
+Tested-by: Krishna Chaitanya <chaitanya.mgit@gmail.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ afc/.gitignore    |   1 +
+ afc/Makefile      |  31 +++++
+ afc/afc-reply.txt | 219 +++++++++++++++++++++++++++++++++
+ afc/afcd.c        | 305 ++++++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 556 insertions(+)
+ create mode 100644 afc/.gitignore
+ create mode 100644 afc/Makefile
+ create mode 100644 afc/afc-reply.txt
+ create mode 100644 afc/afcd.c
+
+diff --git a/afc/.gitignore b/afc/.gitignore
+new file mode 100644
+index 000000000..8d8cca905
+--- /dev/null
++++ b/afc/.gitignore
+@@ -0,0 +1 @@
++afcd
+diff --git a/afc/Makefile b/afc/Makefile
+new file mode 100644
+index 000000000..a83bd01db
+--- /dev/null
++++ b/afc/Makefile
+@@ -0,0 +1,31 @@
++ALL=afcd
++
++include ../src/build.rules
++
++CFLAGS += -I../src/utils
++CFLAGS += -I../src
++
++OBJS=afcd.o
++OBJS += ../src/utils/common.o
++OBJS += ../src/utils/wpa_debug.o
++OBJS += ../src/utils/wpabuf.o
++
++ifndef CONFIG_OS
++ifdef CONFIG_NATIVE_WINDOWS
++CONFIG_OS=win32
++else
++CONFIG_OS=unix
++endif
++endif
++OBJS += ../src/utils/os_$(CONFIG_OS).o
++
++LIBS += -lcurl
++
++_OBJS_VAR := OBJS
++include ../src/objs.mk
++afcd: $(OBJS)
++	$(Q)$(LDO) $(LDFLAGS) -o afcd $(OBJS) $(LIBS)
++	@$(E) "  LD " $@
++
++clean: common-clean
++	rm -f core *~
+diff --git a/afc/afc-reply.txt b/afc/afc-reply.txt
+new file mode 100644
+index 000000000..aaa4f8956
+--- /dev/null
++++ b/afc/afc-reply.txt
+@@ -0,0 +1,219 @@
++HTTP/1.1 200 OK
++Content-Type: application/json
++Content-Length: 4843
++
++{
++   "availableSpectrumInquiryResponses":[
++      {
++         "availabilityExpireTime":"2023-02-23T12:53:18Z",
++         "availableChannelInfo":[
++            {
++               "channelCfi":[
++                  1,
++                  5,
++                  9,
++                  13,
++                  17,
++                  21,
++                  25,
++                  29,
++                  33,
++                  37,
++                  41,
++                  45,
++                  49,
++                  53,
++                  57,
++                  61,
++                  65,
++                  69,
++                  73,
++                  77,
++                  81,
++                  85,
++                  89,
++                  93,
++                  117,
++                  121,
++                  125,
++                  129,
++                  133,
++                  137,
++                  141,
++                  145,
++                  149,
++                  153,
++                  157,
++                  161,
++                  165,
++                  169,
++                  173,
++                  177,
++                  181
++               ],
++               "globalOperatingClass":131,
++               "maxEirp":[
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5
++               ]
++            },
++            {
++               "channelCfi":[
++                  3,
++                  11,
++                  19,
++                  27,
++                  35,
++                  43,
++                  51,
++                  59,
++                  67,
++                  75,
++                  83,
++                  91,
++                  123,
++                  131,
++                  139,
++                  147,
++                  155,
++                  163,
++                  171,
++                  179
++               ],
++               "globalOperatingClass":132,
++               "maxEirp":[
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5
++               ]
++            },
++            {
++               "channelCfi":[
++                  7,
++                  23,
++                  39,
++                  55,
++                  71,
++                  87,
++                  135,
++                  151,
++                  167
++               ],
++               "globalOperatingClass":133,
++               "maxEirp":[
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5
++               ]
++            },
++            {
++               "channelCfi":[
++                  15,
++                  47,
++                  79,
++                  143
++               ],
++               "globalOperatingClass":134,
++               "maxEirp":[
++                  5,
++                  5,
++                  5,
++                  5
++               ]
++            },
++            {
++               "channelCfi":[
++               ],
++               "globalOperatingClass":135,
++               "maxEirp":[
++               ]
++            }
++         ],
++         "availableFrequencyInfo":[
++            {
++               "frequencyRange":{
++                  "highFrequency":6425,
++                  "lowFrequency":5925
++               },
++               "maxPSD":3.98970004336019
++            },
++            {
++               "frequencyRange":{
++                  "highFrequency":6865,
++                  "lowFrequency":6525
++               },
++               "maxPSD":3.98970004336019
++            }
++         ],
++         "requestId":"11235813",
++         "response":{
++            "responseCode":0,
++            "shortDescription":"Success"
++         },
++         "rulesetId":"US_47_CFR_PART_15_SUBPART_E"
++      }
++   ],
++   "version":"1.1"
++}
+diff --git a/afc/afcd.c b/afc/afcd.c
+new file mode 100644
+index 000000000..2b99940ae
+--- /dev/null
++++ b/afc/afcd.c
+@@ -0,0 +1,305 @@
++/*
++ * Automated Frequency Coordination Daemon
++ * Copyright (c) 2024, Lorenzo Bianconi <lorenzo@kernel.org>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include <curl/curl.h>
++#include <sys/un.h>
++#include <sys/stat.h>
++
++#include "utils/includes.h"
++#include "utils/common.h"
++
++#define CURL_TIMEOUT	60
++#define AFCD_SOCK	"afcd.sock"
++
++struct curl_ctx {
++	char *buf;
++	size_t buf_len;
++};
++
++static volatile bool exiting;
++
++static char *path = "/var/run";
++static char *bearer_token;
++static char *url;
++static int port = 443;
++
++
++static size_t afcd_curl_cb_write(void *ptr, size_t size, size_t nmemb,
++				 void *userdata)
++{
++	struct curl_ctx *ctx = userdata;
++	char *buf;
++
++	buf = os_realloc(ctx->buf, ctx->buf_len + size * nmemb + 1);
++	if (!buf)
++		return 0;
++
++	ctx->buf = buf;
++	os_memcpy(buf + ctx->buf_len, ptr, size * nmemb);
++	buf[ctx->buf_len + size * nmemb] = '\0';
++	ctx->buf_len += size * nmemb;
++
++	return size * nmemb;
++}
++
++
++static int afcd_send_request(struct curl_ctx *ctx, unsigned char *request)
++{
++	struct curl_slist *headers = NULL, *tmp;
++	int ret = CURLE_FAILED_INIT;
++	CURL *curl;
++
++	wpa_printf(MSG_DEBUG, "Sending AFC request to %s", url);
++
++	curl_global_init(CURL_GLOBAL_ALL);
++	curl = curl_easy_init();
++	if (!curl)
++		goto out_global_cleanup;
++
++	headers  = curl_slist_append(headers, "Accept: application/json");
++	if (!headers)
++		goto out_easy_cleanup;
++
++	tmp = curl_slist_append(headers, "Content-Type: application/json");
++	if (!tmp)
++		goto out_slist_free_all;
++	headers = tmp;
++
++	tmp = curl_slist_append(headers, "charset: utf-8");
++	if (!tmp)
++		goto out_slist_free_all;
++	headers = tmp;
++
++	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
++	curl_easy_setopt(curl, CURLOPT_URL, url);
++	curl_easy_setopt(curl, CURLOPT_PORT, port);
++	curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
++	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
++			 afcd_curl_cb_write);
++	curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx);
++	curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcrp/0.1");
++	curl_easy_setopt(curl, CURLOPT_TIMEOUT, CURL_TIMEOUT);
++	curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
++	if (bearer_token)
++		curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, bearer_token);
++	curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BEARER);
++	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
++	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L);
++	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request);
++	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L);
++
++	ret = curl_easy_perform(curl);
++	if (ret != CURLE_OK)
++		wpa_printf(MSG_ERROR, "curl_easy_perform failed: %s",
++			   curl_easy_strerror(ret));
++
++out_slist_free_all:
++	curl_slist_free_all(headers);
++out_easy_cleanup:
++	curl_easy_cleanup(curl);
++out_global_cleanup:
++	curl_global_cleanup();
++
++	return ret == CURLE_OK ? 0 : -EINVAL;
++}
++
++
++static void handle_term(int sig)
++{
++	wpa_printf(MSG_ERROR, "Received signal %d", sig);
++	exiting = true;
++}
++
++
++static void usage(void)
++{
++	wpa_printf(MSG_ERROR,
++		   "%s:\n"
++		   "afcd -u<url> [-p<port>][-t<token>][-D<unix-sock dir>][-P<PID file>][-dB]",
++		   __func__);
++}
++
++
++#define BUFSIZE		8192
++static int afcd_server_run(void)
++{
++	size_t len = os_strlen(path) + 1 + os_strlen(AFCD_SOCK);
++	struct sockaddr_un addr = {
++		.sun_family = AF_UNIX,
++#ifdef __FreeBSD__
++		.sun_len = sizeof(addr),
++#endif /* __FreeBSD__ */
++	};
++	int sockfd, ret = 0;
++	char *fname = NULL;
++	unsigned char *buf;
++	fd_set read_set;
++
++	if (len >= sizeof(addr.sun_path))
++		return -EINVAL;
++
++	if (mkdir(path, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST)
++		return -EINVAL;
++
++	buf = os_malloc(BUFSIZE);
++	if (!buf)
++		return -ENOMEM;
++
++	fname = os_malloc(len + 1);
++	if (!fname) {
++		ret = -ENOMEM;
++		goto free_buf;
++	}
++
++	os_snprintf(fname, len + 1, "%s/%s", path, AFCD_SOCK);
++	fname[len] = '\0';
++	os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
++
++	sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
++	if (sockfd < 0) {
++		wpa_printf(MSG_ERROR, "Failed creating socket");
++		ret = -errno;
++		goto unlink;
++	}
++
++	if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
++		wpa_printf(MSG_ERROR, "Failed to bind socket");
++		ret = -errno;
++		goto close;
++	}
++
++	if (listen(sockfd, 10) < 0) {
++		wpa_printf(MSG_ERROR, "Failed to listen on socket");
++		ret = -errno;
++		goto close;
++	}
++
++	FD_ZERO(&read_set);
++	while (!exiting) {
++		socklen_t addr_len = sizeof(addr);
++		struct sockaddr_in6 c_addr;
++		struct timeval timeout = {
++			.tv_sec = 1,
++		};
++		struct curl_ctx ctx = {};
++		int fd;
++
++		FD_SET(sockfd, &read_set);
++		if (select(sockfd + 1, &read_set, NULL, NULL, &timeout) < 0) {
++			if (errno != EINTR) {
++				wpa_printf(MSG_ERROR,
++					   "Select failed on socket");
++				ret = -errno;
++				break;
++			}
++			continue;
++		}
++
++		if (!FD_ISSET(sockfd, &read_set))
++			continue;
++
++		fd = accept(sockfd, (struct sockaddr *)&c_addr,
++			    &addr_len);
++		if (fd < 0) {
++			if (errno != EINTR) {
++				wpa_printf(MSG_ERROR,
++					   "Failed accepting connections");
++				ret = -errno;
++				break;
++			}
++			continue;
++		}
++
++		os_memset(buf, 0, BUFSIZE);
++		if (recv(fd, buf, BUFSIZE - 1, 0) <= 0) {
++			close(fd);
++			continue;
++		}
++
++		wpa_printf(MSG_DEBUG, "Received request: %s", buf);
++		if (!afcd_send_request(&ctx, buf)) {
++			wpa_printf(MSG_DEBUG, "Received reply: %s", ctx.buf);
++			send(fd, ctx.buf, ctx.buf_len, MSG_NOSIGNAL);
++			free(ctx.buf);
++		}
++		close(fd);
++	}
++close:
++	close(sockfd);
++unlink:
++	unlink(fname);
++	os_free(fname);
++free_buf:
++	os_free(buf);
++
++	return ret;
++}
++
++
++int main(int argc, char **argv)
++{
++	bool daemonize = false;
++	char *pid_file = NULL;
++
++	if (os_program_init())
++		return -1;
++
++	for (;;) {
++		int c = getopt(argc, argv, "u:p:t:D:P:hdB");
++
++		if (c < 0)
++			break;
++
++		switch (c) {
++		case 'h':
++			usage();
++			return 0;
++		case 'B':
++			daemonize = true;
++			break;
++		case 'D':
++			path = optarg;
++			break;
++		case 'P':
++			os_free(pid_file);
++			pid_file = os_rel2abs_path(optarg);
++			break;
++		case 'u':
++			url = optarg;
++			break;
++		case 'p':
++			port = atoi(optarg);
++			break;
++		case 'd':
++			if (wpa_debug_level > 0)
++				wpa_debug_level--;
++			break;
++		case 't':
++			bearer_token = optarg;
++			break;
++		default:
++			usage();
++			return -EINVAL;
++		}
++	}
++
++	if (!url) {
++		usage();
++		return -EINVAL;
++	}
++
++	if (daemonize && os_daemonize(pid_file)) {
++		wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
++		return -EINVAL;
++	}
++
++	signal(SIGTERM, handle_term);
++	signal(SIGINT, handle_term);
++
++	return afcd_server_run();
++}
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0006-hostapd-MLO-reset-auth-state-machine-s-ML-info.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0006-hostapd-MLO-reset-auth-state-machine-s-ML-info.patch
deleted file mode 100644
index 2a7bacd..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0006-hostapd-MLO-reset-auth-state-machine-s-ML-info.patch
+++ /dev/null
@@ -1,93 +0,0 @@
-From 4be7c245a946016c41a69c7469e00d22aaa32a46 Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:36 +0530
-Subject: [PATCH 006/104] hostapd: MLO: reset auth state machine's ML info
-
-Currently auth state machine ML info is set only when the it is created
-newly. However, if the association is tried again, the state machine will
-exist already and hence the ML info will not be refreshed. This leads to
-an issue where if in the subsequent association request, the MLD info is
-different than old info then validation of it will fail.
-
-Fix this issue by refreshing the auth state machine's ML info every time
-association request is handled.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/ieee802_11.c | 32 ++++++++++++++++++--------------
- src/ap/wpa_auth.c   |  1 +
- 2 files changed, 19 insertions(+), 14 deletions(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index b20300bab..98398ccdd 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -4032,15 +4032,15 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
- 
- 	if (hapd->conf->wpa && wpa_ie) {
- 		enum wpa_validate_result res;
-+#ifdef CONFIG_IEEE80211BE
-+		struct mld_info *info = &sta->mld_info;
-+		bool init = false;
-+#endif /* CONFIG_IEEE80211BE */
- 
- 		wpa_ie -= 2;
- 		wpa_ie_len += 2;
- 
- 		if (!sta->wpa_sm) {
--#ifdef CONFIG_IEEE80211BE
--			struct mld_info *info = &sta->mld_info;
--#endif /* CONFIG_IEEE80211BE */
--
- 			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
- 							sta->addr,
- 							p2p_dev_addr);
-@@ -4050,19 +4050,23 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
- 					   "Failed to initialize RSN state machine");
- 				return WLAN_STATUS_UNSPECIFIED_FAILURE;
- 			}
--
- #ifdef CONFIG_IEEE80211BE
--			if (ap_sta_is_mld(hapd, sta)) {
--				wpa_printf(MSG_DEBUG,
--					   "MLD: Set ML info in RSN Authenticator");
--				wpa_auth_set_ml_info(sta->wpa_sm,
--						     hapd->mld->mld_addr,
--						     sta->mld_assoc_link_id,
--						     info);
--			}
--#endif /* CONFIG_IEEE80211BE */
-+			init = true;
- 		}
- 
-+		if (ap_sta_is_mld(hapd, sta)) {
-+			wpa_printf(MSG_DEBUG,
-+				   "MLD: %s ML info in RSN Authenticator",
-+				   init ? "Set" : "Reset");
-+			wpa_auth_set_ml_info(sta->wpa_sm,
-+					     hapd->mld->mld_addr,
-+					     sta->mld_assoc_link_id,
-+					     info);
-+		}
-+#else
-+		}
-+#endif /* CONFIG_IEEE80211BE */
-+
- 		wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
- 		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
- 					  hapd->iface->freq,
-diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
-index 01a10b23c..0d15c4209 100644
---- a/src/ap/wpa_auth.c
-+++ b/src/ap/wpa_auth.c
-@@ -6820,6 +6820,7 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
- 		return;
- 
- 	os_memset(sm->mld_links, 0, sizeof(sm->mld_links));
-+	sm->n_mld_affiliated_links = 0;
- 
- 	wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
- 			"MLD: Initialization");
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0006-hostapd-export-hostapd_is_usable_chans-utility-routi.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0006-hostapd-export-hostapd_is_usable_chans-utility-routi.patch
new file mode 100644
index 0000000..7dc2584
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0006-hostapd-export-hostapd_is_usable_chans-utility-routi.patch
@@ -0,0 +1,57 @@
+From ce1dd0c7ab931cc448bed621a12033fbc94f36f1 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 17 May 2024 11:50:26 +0200
+Subject: [PATCH 006/126] hostapd: export hostapd_is_usable_chans utility
+ routine
+
+This is a preliminary patch to introduce AFC support.
+
+Tested-by: Felix Fietkau <nbd@nbd.name>
+Tested-by: Allen Ye <allen.ye@mediatek.com>
+Tested-by: Krishna Chaitanya <chaitanya.mgit@gmail.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ src/ap/hw_features.c | 2 +-
+ src/ap/hw_features.h | 6 ++++++
+ 2 files changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index c4556603d..85e67080d 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -1008,7 +1008,7 @@ static bool hostapd_is_usable_punct_bitmap(struct hostapd_iface *iface)
+  * 0 = not usable
+  * -1 = not currently usable due to 6 GHz NO-IR
+  */
+-static int hostapd_is_usable_chans(struct hostapd_iface *iface)
++int hostapd_is_usable_chans(struct hostapd_iface *iface)
+ {
+ 	int secondary_freq;
+ 	struct hostapd_channel_data *pri_chan;
+diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
+index c682c6d20..eeffb1abd 100644
+--- a/src/ap/hw_features.h
++++ b/src/ap/hw_features.h
+@@ -30,6 +30,7 @@ void hostapd_stop_setup_timers(struct hostapd_iface *iface);
+ int hostapd_hw_skip_mode(struct hostapd_iface *iface,
+ 			 struct hostapd_hw_modes *mode);
+ int hostapd_determine_mode(struct hostapd_iface *iface);
++int hostapd_is_usable_chans(struct hostapd_iface *iface);
+ #else /* NEED_AP_MLME */
+ static inline void
+ hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
+@@ -103,6 +104,11 @@ static inline int hostapd_determine_mode(struct hostapd_iface *iface)
+ 	return 0;
+ }
+ 
++static inline int hostapd_is_usable_chans(struct hostapd_iface *iface)
++{
++	return 1;
++}
++
+ #endif /* NEED_AP_MLME */
+ 
+ #endif /* HW_FEATURES_H */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0007-hostapd-MLO-add-support-for-cohosted-ML-BSS.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0007-hostapd-MLO-add-support-for-cohosted-ML-BSS.patch
deleted file mode 100644
index 9afd8f4..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0007-hostapd-MLO-add-support-for-cohosted-ML-BSS.patch
+++ /dev/null
@@ -1,201 +0,0 @@
-From d56daa4ebdf544a30f30986097edd6d5f9b8674f Mon Sep 17 00:00:00 2001
-From: Sriram R <quic_srirrama@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:37 +0530
-Subject: [PATCH 007/104] hostapd: MLO: add support for cohosted ML BSS
-
-Currently MLO is being supported with an assumption of only single BSS per
-link in the hostapd conf file. This needs to be extended when cohosted ML
-BSS exist in the same config file.
-
-Extend the support for cohosted BSSes. This is required for MBSSID MLO
-support as well.
-
-Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- hostapd/main.c   | 38 +++++++-------------------
- src/ap/hostapd.c | 70 +++++++++++++++++++++++++++++++++++++++++++-----
- 2 files changed, 73 insertions(+), 35 deletions(-)
-
-diff --git a/hostapd/main.c b/hostapd/main.c
-index a43d3a5be..524a10274 100644
---- a/hostapd/main.c
-+++ b/hostapd/main.c
-@@ -158,6 +158,9 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
- 	struct hostapd_bss_config *conf = hapd->conf;
- 	u8 *b = conf->bssid;
- 	struct wpa_driver_capa capa;
-+#ifdef CONFIG_IEEE80211BE
-+	struct hostapd_data *h_hapd = NULL;
-+#endif /* CONFIG_IEEE80211BE */
- 
- 	if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) {
- 		wpa_printf(MSG_ERROR, "No hostapd driver wrapper available");
-@@ -165,35 +168,10 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
- 	}
- 
- #ifdef CONFIG_IEEE80211BE
--	for (i = 0; conf->mld_ap && i < iface->interfaces->count; i++) {
--		struct hostapd_iface *h = iface->interfaces->iface[i];
--		struct hostapd_data *h_hapd = h->bss[0];
--		struct hostapd_bss_config *hconf = h_hapd->conf;
--
--		if (h == iface) {
--			wpa_printf(MSG_DEBUG, "MLD: Skip own interface");
--			continue;
--		}
--
--		if (!hconf->mld_ap) {
--			wpa_printf(MSG_DEBUG,
--				   "MLD: Skip non-MLD");
--			continue;
--		}
--
--		if (!hostapd_is_ml_partner(hapd, h_hapd)) {
--			wpa_printf(MSG_DEBUG,
--				   "MLD: Skip non matching MLD vif name");
--			continue;
--		}
--
--		wpa_printf(MSG_DEBUG, "MLD: Found matching MLD interface");
--		if (!h_hapd->drv_priv) {
--			wpa_printf(MSG_DEBUG,
--				   "MLD: Matching MLD BSS not initialized yet");
--			continue;
--		}
-+	if (conf->mld_ap)
-+		h_hapd = hostapd_mld_get_first_bss(hapd);
- 
-+	if (h_hapd) {
- 		hapd->drv_priv = h_hapd->drv_priv;
- 		hapd->interface_added = h_hapd->interface_added;
- 
-@@ -214,6 +192,8 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
- 		}
- 
- 		hostapd_mld_add_link(hapd);
-+		wpa_printf(MSG_DEBUG, "Setup of non first link (%d) BSS of MLD %s",
-+			   hapd->mld_link_id, hapd->conf->iface);
- 
- 		goto setup_mld;
- 	}
-@@ -298,6 +278,8 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
- 			os_memcpy(hapd->own_addr, b, ETH_ALEN);
- 
- 		hostapd_mld_add_link(hapd);
-+		wpa_printf(MSG_DEBUG, "Setup of first link (%d) BSS of MLD %s",
-+			   hapd->mld_link_id, hapd->conf->iface);
- 	}
- 
- setup_mld:
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index f8cb6432d..ff1d8f9d0 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -1333,6 +1333,9 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
- 	char force_ifname[IFNAMSIZ];
- 	u8 if_addr[ETH_ALEN];
- 	int flush_old_stations = 1;
-+#ifdef CONFIG_IEEE80211BE
-+	struct hostapd_data *h_hapd = NULL;
-+#endif /* CONFIG_IEEE80211BE */
- 
- 	if (!hostapd_mld_is_first_bss(hapd))
- 		wpa_printf(MSG_DEBUG,
-@@ -1379,6 +1382,21 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
- 			} while (mac_in_conf(hapd->iconf, hapd->own_addr));
- 		}
- 
-+#ifdef CONFIG_IEEE80211BE
-+		if (conf->mld_ap) {
-+			h_hapd = hostapd_mld_get_first_bss(hapd);
-+
-+			if (h_hapd) {
-+				hapd->drv_priv = h_hapd->drv_priv;
-+				hapd->interface_added = h_hapd->interface_added;
-+				hostapd_mld_add_link(hapd);
-+				wpa_printf(MSG_DEBUG, "Setup of non first link (%d) BSS of MLD %s",
-+					   hapd->mld_link_id, hapd->conf->iface);
-+				goto setup_mld;
-+			}
-+		}
-+#endif /* CONFIG_IEEE80211BE */
-+
- 		hapd->interface_added = 1;
- 		if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
- 				   conf->iface, addr, hapd,
-@@ -1393,8 +1411,33 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
- 
- 		if (!addr)
- 			os_memcpy(hapd->own_addr, if_addr, ETH_ALEN);
-+
-+#ifdef CONFIG_IEEE80211BE
-+		if (hapd->conf->mld_ap) {
-+			wpa_printf(MSG_DEBUG, "Setup of first link (%d) BSS of MLD %s",
-+				   hapd->mld_link_id, hapd->conf->iface);
-+			os_memcpy(hapd->mld->mld_addr, hapd->own_addr, ETH_ALEN);
-+			hostapd_mld_add_link(hapd);
-+		}
- 	}
- 
-+setup_mld:
-+
-+	if (hapd->conf->mld_ap && !first) {
-+		wpa_printf(MSG_DEBUG,
-+			   "MLD: Set link_id=%u, mld_addr=" MACSTR
-+			   ", own_addr=" MACSTR,
-+			   hapd->mld_link_id, MAC2STR(hapd->mld->mld_addr),
-+			   MAC2STR(hapd->own_addr));
-+
-+		if (hostapd_drv_link_add(hapd, hapd->mld_link_id,
-+					 hapd->own_addr))
-+			return -1;
-+	}
-+#else
-+	}
-+#endif /* CONFIG_IEEE80211BE */
-+
- 	if (conf->wmm_enabled < 0)
- 		conf->wmm_enabled = hapd->iconf->ieee80211n |
- 			hapd->iconf->ieee80211ax;
-@@ -4679,17 +4722,30 @@ void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx)
- struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
- 					       u8 link_id)
- {
--	unsigned int i;
-+	struct hostapd_iface *iface;
-+	struct hostapd_data *bss;
-+	struct hostapd_bss_config *conf;
-+	unsigned int i, j;
- 
- 	for (i = 0; i < hapd->iface->interfaces->count; i++) {
--		struct hostapd_iface *h = hapd->iface->interfaces->iface[i];
--		struct hostapd_data *h_hapd = h->bss[0];
--
--		if (!hostapd_is_ml_partner(hapd, h_hapd))
-+		iface = hapd->iface->interfaces->iface[i];
-+		if (!iface)
- 			continue;
- 
--		if (h_hapd->mld_link_id == link_id)
--			return h_hapd;
-+		for (j = 0; j < iface->num_bss; j++) {
-+			bss = iface->bss[j];
-+			conf = bss->conf;
-+
-+			if (!conf->mld_ap ||
-+			    !hostapd_is_ml_partner(hapd, bss))
-+				continue;
-+
-+			if (!bss->drv_priv)
-+				continue;
-+
-+			if (bss->mld_link_id == link_id)
-+				return bss;
-+		}
- 	}
- 
- 	return NULL;
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0007-hostapd-ap-add-AFC-client-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0007-hostapd-ap-add-AFC-client-support.patch
new file mode 100644
index 0000000..0dce399
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0007-hostapd-ap-add-AFC-client-support.patch
@@ -0,0 +1,1650 @@
+From 3bd1d33a94f25337fd70df60ee5a42a60f95cba9 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 17 May 2024 11:50:27 +0200
+Subject: [PATCH 007/126] hostapd: ap: add AFC client support
+
+Introduce Automated Frequency Coordination (AFC) support for UNII-5 and
+UNII-7 6GHz bands.
+AFC client will connect to AFCD providing AP related parameter for AFC
+coordinator (e.g. geolocation, supported frequencies, ..).
+AFC is required for Standard Power Devices (SPDs) to determine a lists
+of channels and EIRP/PSD powers that are available in the 6GHz spectrum.
+AFC hostapd client is tested with AFC DUT Test Harness [0].
+
+[0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main
+
+Tested-by: Felix Fietkau <nbd@nbd.name>
+Tested-by: Allen Ye <allen.ye@mediatek.com>
+Tested-by: Krishna Chaitanya <chaitanya.mgit@gmail.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ hostapd/Makefile      |    8 +
+ hostapd/config_file.c |  261 +++++++++++
+ hostapd/defconfig     |    3 +
+ hostapd/hostapd.conf  |   42 ++
+ src/ap/afc.c          | 1041 +++++++++++++++++++++++++++++++++++++++++
+ src/ap/ap_config.c    |   16 +
+ src/ap/ap_config.h    |   47 ++
+ src/ap/hostapd.c      |   16 +
+ src/ap/hostapd.h      |   45 ++
+ src/ap/hw_features.c  |    2 +
+ 10 files changed, 1481 insertions(+)
+ create mode 100644 src/ap/afc.c
+
+diff --git a/hostapd/Makefile b/hostapd/Makefile
+index ca4439234..78171025e 100644
+--- a/hostapd/Makefile
++++ b/hostapd/Makefile
+@@ -104,6 +104,14 @@ CFLAGS += -DCONFIG_TAXONOMY
+ OBJS += ../src/ap/taxonomy.o
+ endif
+ 
++ifdef CONFIG_IEEE80211AX
++ifdef CONFIG_AFC
++CFLAGS += -DCONFIG_AFC
++OBJS += ../src/ap/afc.o
++LIBS += -ljson-c
++endif
++endif
++
+ ifdef CONFIG_MODULE_TESTS
+ CFLAGS += -DCONFIG_MODULE_TESTS
+ OBJS += hapd_module_tests.o
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 96f1b1749..a86621ed9 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -1281,6 +1281,190 @@ static int hostapd_parse_he_srg_bitmap(u8 *bitmap, char *val)
+ 	return 0;
+ }
+ 
++
++#ifdef CONFIG_AFC
++static int hostapd_afc_parse_cert_ids(struct hostapd_config *conf, char *pos)
++{
++	struct cert_id *c = NULL;
++	int i, count = 0;
++
++	while (pos && pos[0]) {
++		char *p;
++
++		c = os_realloc_array(c, count + 1, sizeof(*c));
++		if (!c)
++			return -ENOMEM;
++
++		i = count;
++		count++;
++
++		p = os_strchr(pos, ':');
++		if (!p)
++			goto error;
++
++		*p++ = '\0';
++		if (!p || !p[0])
++			goto error;
++
++		c[i].rulset = os_malloc(os_strlen(pos) + 1);
++		if (!c[i].rulset)
++			goto error;
++
++		os_strlcpy(c[i].rulset, pos, os_strlen(pos) + 1);
++		pos = p;
++		p = os_strchr(pos, ',');
++		if (p)
++			*p++ = '\0';
++
++		c[i].id = os_malloc(os_strlen(pos) + 1);
++		if (!c[i].id)
++			goto error;
++
++		os_strlcpy(c[i].id, pos, os_strlen(pos) + 1);
++		pos = p;
++	}
++
++	conf->afc.n_cert_ids = count;
++	conf->afc.cert_ids = c;
++
++	return 0;
++
++error:
++	for (i = 0; i < count; i++) {
++		os_free(c[i].rulset);
++		os_free(c[i].id);
++	}
++	os_free(c);
++
++	return -ENOMEM;
++}
++
++
++static int hostapd_afc_parse_position_data(struct afc_linear_polygon **data,
++					   unsigned int *n_linear_polygon_data,
++					   char *pos)
++{
++	struct afc_linear_polygon *d = NULL;
++	int i, count = 0;
++
++	while (pos && pos[0]) {
++		char *p, *end;
++
++		d = os_realloc_array(d, count + 1, sizeof(*d));
++		if (!d)
++			return -ENOMEM;
++
++		i = count;
++		count++;
++
++		p = os_strchr(pos, ':');
++		if (!p)
++			goto error;
++
++		*p++ = '\0';
++		if (!p || !p[0])
++			goto error;
++
++		d[i].longitude = strtod(pos, &end);
++		if (*end)
++			goto error;
++
++		pos = p;
++		p = os_strchr(pos, ',');
++		if (p)
++			*p++ = '\0';
++
++		d[i].latitude = strtod(pos, &end);
++		if (*end)
++			goto error;
++
++		pos = p;
++	}
++
++	*n_linear_polygon_data = count;
++	*data = d;
++
++	return 0;
++
++error:
++	os_free(d);
++	return -ENOMEM;
++}
++
++
++static int hostapd_afc_parse_freq_range(struct hostapd_config *conf, char *pos)
++{
++	struct afc_freq_range *f = NULL;
++	int i, count = 0;
++
++	while (pos && pos[0]) {
++		char *p;
++
++		f = os_realloc_array(f, count + 1, sizeof(*f));
++		if (!f)
++			return -ENOMEM;
++
++		i = count;
++		count++;
++
++		p = os_strchr(pos, ':');
++		if (!p)
++			goto error;
++
++		*p++ = '\0';
++		if (!p || !p[0])
++			goto error;
++
++		f[i].low_freq = atoi(pos);
++		pos = p;
++		p = os_strchr(pos, ',');
++		if (p)
++			*p++ = '\0';
++
++		f[i].high_freq = atoi(pos);
++		pos = p;
++	}
++
++	conf->afc.n_freq_range = count;
++	conf->afc.freq_range = f;
++
++	return 0;
++
++error:
++	os_free(f);
++	return -ENOMEM;
++}
++
++
++static int hostapd_afc_parse_op_class(struct hostapd_config *conf, char *pos)
++{
++	unsigned int *oc = NULL;
++	int i, count = 0;
++
++	while (pos && pos[0]) {
++		char *p;
++
++		oc = os_realloc_array(oc, count + 1, sizeof(*oc));
++		if (!oc)
++			return -ENOMEM;
++
++		i = count;
++		count++;
++
++		p = os_strchr(pos, ',');
++		if (p)
++			*p++ = '\0';
++
++		oc[i] = atoi(pos);
++		pos = p;
++	}
++
++	conf->afc.n_op_class = count;
++	conf->afc.op_class = oc;
++
++	return 0;
++}
++#endif /* CONFIG_AFC */
+ #endif /* CONFIG_IEEE80211AX */
+ 
+ 
+@@ -3955,6 +4139,83 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 			return 1;
+ 		}
+ 		bss->unsol_bcast_probe_resp_interval = val;
++#ifdef CONFIG_AFC
++	} else if (os_strcmp(buf, "afcd_sock") == 0) {
++		conf->afc.socket = os_malloc(os_strlen(pos) + 1);
++		if (!conf->afc.socket)
++			return 1;
++
++		os_strlcpy(conf->afc.socket, pos, os_strlen(pos) + 1);
++	} else if (os_strcmp(buf, "afc_request_version") == 0) {
++		conf->afc.request.version = os_malloc(os_strlen(pos) + 1);
++		if (!conf->afc.request.version)
++			return 1;
++
++		os_strlcpy(conf->afc.request.version, pos, os_strlen(pos) + 1);
++	} else if (os_strcmp(buf, "afc_request_id") == 0) {
++		conf->afc.request.id = os_malloc(os_strlen(pos) + 1);
++		if (!conf->afc.request.id)
++			return 1;
++
++		os_strlcpy(conf->afc.request.id, pos, os_strlen(pos) + 1);
++	} else if (os_strcmp(buf, "afc_serial_number") == 0) {
++		conf->afc.request.sn = os_malloc(os_strlen(pos) + 1);
++		if (!conf->afc.request.sn)
++			return 1;
++
++		os_strlcpy(conf->afc.request.sn, pos, os_strlen(pos) + 1);
++	} else if (os_strcmp(buf, "afc_cert_ids") == 0) {
++		if (hostapd_afc_parse_cert_ids(conf, pos))
++			return 1;
++	} else if (os_strcmp(buf, "afc_location_type") == 0) {
++		conf->afc.location.type = atoi(pos);
++		if (conf->afc.location.type != ELLIPSE &&
++		    conf->afc.location.type != LINEAR_POLYGON &&
++		    conf->afc.location.type != RADIAL_POLYGON)
++			return 1;
++	} else if (os_strcmp(buf, "afc_linear_polygon") == 0) {
++		if (hostapd_afc_parse_position_data(
++			&conf->afc.location.linear_polygon_data,
++			&conf->afc.location.n_linear_polygon_data,
++			pos))
++			return 1;
++	} else if (os_strcmp(buf, "afc_radial_polygon") == 0) {
++		if (hostapd_afc_parse_position_data(
++			(struct afc_linear_polygon **)
++			&conf->afc.location.radial_polygon_data,
++			&conf->afc.location.n_radial_polygon_data,
++			pos))
++			return 1;
++	} else if (os_strcmp(buf, "afc_major_axis") == 0) {
++		conf->afc.location.major_axis = atoi(pos);
++	} else if (os_strcmp(buf, "afc_minor_axis") == 0) {
++		conf->afc.location.minor_axis = atoi(pos);
++	} else if (os_strcmp(buf, "afc_orientation") == 0) {
++		conf->afc.location.orientation = atoi(pos);
++	} else if (os_strcmp(buf, "afc_height") == 0) {
++		char *end;
++
++		conf->afc.location.height = strtod(pos, &end);
++		if (*end)
++			return 1;
++	} else if (os_strcmp(buf, "afc_height_type") == 0) {
++		conf->afc.location.height_type = os_malloc(os_strlen(pos) + 1);
++		if (!conf->afc.location.height_type)
++			return 1;
++
++		os_strlcpy(conf->afc.location.height_type, pos,
++			   os_strlen(pos) + 1);
++	} else if (os_strcmp(buf, "afc_vertical_tolerance") == 0) {
++		conf->afc.location.vertical_tolerance = atoi(pos);
++	} else if (os_strcmp(buf, "afc_min_power") == 0) {
++		conf->afc.min_power = atoi(pos);
++	} else if (os_strcmp(buf, "afc_freq_range") == 0) {
++		if (hostapd_afc_parse_freq_range(conf, pos))
++			return 1;
++	} else if (os_strcmp(buf, "afc_op_class") == 0) {
++		if (hostapd_afc_parse_op_class(conf, pos))
++			return 1;
++#endif /* CONFIG_AFC */
+ 	} else if (os_strcmp(buf, "mbssid") == 0) {
+ 		int mbssid = atoi(pos);
+ 		if (mbssid < 0 || mbssid > ENHANCED_MBSSID_ENABLED) {
+diff --git a/hostapd/defconfig b/hostapd/defconfig
+index 550db697b..66bf894eb 100644
+--- a/hostapd/defconfig
++++ b/hostapd/defconfig
+@@ -425,3 +425,6 @@ CONFIG_DPP2=y
+ 
+ # Wi-Fi Aware unsynchronized service discovery (NAN USD)
+ #CONFIG_NAN_USD=y
++
++# Enable Automated Frequency Coordination for 6GHz outdoor
++#CONFIG_AFC=y
+diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
+index 93524cf5d..56442c69d 100644
+--- a/hostapd/hostapd.conf
++++ b/hostapd/hostapd.conf
+@@ -1030,6 +1030,48 @@ wmm_ac_vo_acm=0
+ # Valid range: 0..20 TUs; default is 0 (disabled)
+ #unsol_bcast_probe_resp_interval=0
+ 
++##### Automated Frequency Coordination for 6GHz UNII-5 and UNII-7 bands #######
++
++# AFC daemon connection socket
++#afcd_sock=/var/run/afcd.sock
++
++# AFC request identification parameters
++#afc_request_version=1.1
++#afc_request_id=11235813
++#afc_serial_number=abcdefg
++#afc_cert_ids=US_47_CFR_PART_15_SUBPART_E:CID000
++#
++# AFC location type:
++# 0 = ellipse
++# 1 = linear polygon
++# 2 = radial polygon
++#afc_location_type=0
++#
++# AFC ellipse or linear polygon coordinations
++#afc_linear_polygon=-122.984157:37.425056
++#
++# AFC radial polygon coordinations
++#afc_radial_polygon=118.8:92.76,76.44:87.456,98.56:123.33
++#
++# AFC ellipse major/minor axis and orientation
++#afc_major_axis=100
++#afc_minor_axis=50
++#afc_orientation=70
++#
++# AFC device elevation parameters
++#afc_height=3.0
++#afc_height_type=AGL
++#afc_vertical_tolerance=7
++#
++# AFC minimum desired TX power (dbm)
++#afc_min_power=24
++#
++# AFC request frequency ranges
++#afc_freq_range=5925:6425,6525:6875
++#
++# AFC request operation classes
++#afc_op_class=131,132,133,134,136
++
+ ##### IEEE 802.11be related configuration #####################################
+ 
+ #ieee80211be: Whether IEEE 802.11be (EHT) is enabled
+diff --git a/src/ap/afc.c b/src/ap/afc.c
+new file mode 100644
+index 000000000..cfee83fe7
+--- /dev/null
++++ b/src/ap/afc.c
+@@ -0,0 +1,1041 @@
++/*
++ * Automated Frequency Coordination
++ * Copyright (c) 2024, Lorenzo Bianconi <lorenzo@kernel.org>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include <json-c/json.h>
++#include <sys/un.h>
++#include <time.h>
++
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/eloop.h"
++#include "hostapd.h"
++#include "acs.h"
++#include "hw_features.h"
++
++#define HOSTAPD_AFC_RETRY_TIMEOUT	180
++#define HOSTAPD_AFC_TIMEOUT		86400 /* 24h */
++#define HOSTAPD_AFC_BUFSIZE		8192
++
++static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx);
++
++
++static struct json_object *
++hostapd_afc_build_location_request(struct hostapd_iface *iface)
++{
++	struct json_object *location_obj, *center_obj, *ellipse_obj;
++	struct json_object *elevation_obj, *str_obj;
++	struct hostapd_config *iconf = iface->conf;
++	bool is_ap_indoor = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type);
++
++	location_obj = json_object_new_object();
++	if (!location_obj)
++		return NULL;
++
++	if (iconf->afc.location.type != LINEAR_POLYGON) {
++		struct afc_linear_polygon *lp =
++			&iconf->afc.location.linear_polygon_data[0];
++
++		if (!lp)
++			goto error;
++
++		ellipse_obj = json_object_new_object();
++		if (!ellipse_obj)
++			goto error;
++
++		center_obj = json_object_new_object();
++		if (!center_obj)
++			goto error;
++
++		json_object_object_add(ellipse_obj, "center", center_obj);
++
++		str_obj = json_object_new_double(lp->longitude);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(center_obj, "longitude", str_obj);
++		str_obj = json_object_new_double(lp->latitude);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(center_obj, "latitude", str_obj);
++	}
++
++	switch (iconf->afc.location.type) {
++	case LINEAR_POLYGON: {
++		struct json_object *outer_boundary_obj;
++		int i;
++
++		outer_boundary_obj = json_object_new_object();
++		if (!outer_boundary_obj)
++			goto error;
++
++		json_object_object_add(location_obj, "linearPolygon",
++				       outer_boundary_obj);
++		ellipse_obj = json_object_new_array();
++		if (!ellipse_obj)
++			goto error;
++
++		json_object_object_add(outer_boundary_obj, "outerBoundary",
++				       ellipse_obj);
++		for (i = 0;
++		     i < iconf->afc.location.n_linear_polygon_data; i++) {
++			struct afc_linear_polygon *lp =
++				&iconf->afc.location.linear_polygon_data[i];
++
++			center_obj = json_object_new_object();
++			if (!center_obj)
++				goto error;
++
++			json_object_array_add(ellipse_obj, center_obj);
++			str_obj = json_object_new_double(lp->longitude);
++			if (!str_obj)
++				goto error;
++
++			json_object_object_add(center_obj, "longitude",
++					       str_obj);
++			str_obj = json_object_new_double(lp->latitude);
++			if (!str_obj)
++				goto error;
++
++			json_object_object_add(center_obj, "latitude",
++					       str_obj);
++		}
++		break;
++	}
++	case RADIAL_POLYGON: {
++		struct json_object *outer_boundary_obj;
++		int i;
++
++		json_object_object_add(location_obj, "radialPolygon",
++				       ellipse_obj);
++
++		outer_boundary_obj = json_object_new_array();
++		if (!outer_boundary_obj)
++			goto error;
++
++		json_object_object_add(ellipse_obj, "outerBoundary",
++				       outer_boundary_obj);
++		for (i = 0;
++		     i < iconf->afc.location.n_radial_polygon_data; i++) {
++			struct afc_radial_polygon *rp =
++				&iconf->afc.location.radial_polygon_data[i];
++			struct json_object *angle_obj;
++
++			angle_obj = json_object_new_object();
++			if (!angle_obj)
++				goto error;
++
++			json_object_array_add(outer_boundary_obj, angle_obj);
++
++			str_obj = json_object_new_double(rp->angle);
++			if (!str_obj)
++				goto error;
++
++			json_object_object_add(angle_obj, "angle", str_obj);
++			str_obj = json_object_new_double(rp->length);
++			if (!str_obj)
++				goto error;
++
++			json_object_object_add(angle_obj, "length", str_obj);
++		}
++		break;
++	}
++	case ELLIPSE:
++	default:
++		json_object_object_add(location_obj, "ellipse", ellipse_obj);
++
++		str_obj = json_object_new_int(iconf->afc.location.major_axis);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(ellipse_obj, "majorAxis", str_obj);
++		str_obj = json_object_new_int(iconf->afc.location.minor_axis);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(ellipse_obj, "minorAxis", str_obj);
++		str_obj = json_object_new_int(iconf->afc.location.orientation);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(ellipse_obj, "orientation", str_obj);
++		break;
++	}
++
++	elevation_obj = json_object_new_object();
++	if (!elevation_obj)
++		goto error;
++
++	json_object_object_add(location_obj, "elevation",
++			       elevation_obj);
++	str_obj = json_object_new_double(iconf->afc.location.height);
++	if (!str_obj)
++		goto error;
++
++	json_object_object_add(elevation_obj, "height", str_obj);
++	if (iconf->afc.location.height_type) {
++		str_obj = json_object_new_string(iconf->afc.location.height_type);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(elevation_obj, "heightType", str_obj);
++	}
++
++	str_obj = json_object_new_int(iconf->afc.location.vertical_tolerance);
++	if (!str_obj)
++		goto error;
++
++	json_object_object_add(elevation_obj, "verticalUncertainty",
++			       str_obj);
++	str_obj = json_object_new_int(is_ap_indoor);
++	if (!str_obj)
++		goto error;
++
++	json_object_object_add(location_obj, "indoorDeployment", str_obj);
++
++	return location_obj;
++
++error:
++	json_object_put(location_obj);
++	return NULL;
++}
++
++
++static struct json_object * hostapd_afc_get_opclass_chan_list(u8 op_class)
++{
++	struct json_object *chan_list_obj, *str_obj;
++	const struct oper_class_map *oper_class;
++	int chan_offset, chan;
++
++	oper_class = get_oper_class(NULL, op_class);
++	if (!oper_class)
++		return NULL;
++
++	chan_list_obj = json_object_new_array();
++	if (!chan_list_obj)
++		return NULL;
++
++	switch (op_class) {
++	case 132: /*  40MHz */
++		chan_offset = 2;
++		break;
++	case 133: /*  80MHz */
++		chan_offset = 6;
++		break;
++	case 134: /* 160MHz */
++		chan_offset = 14;
++		break;
++	default:
++		chan_offset = 0;
++		break;
++	}
++
++	for (chan = oper_class->min_chan; chan <= oper_class->max_chan;
++	     chan += oper_class->inc) {
++		str_obj = json_object_new_int(chan + chan_offset);
++		if (!str_obj) {
++			json_object_put(chan_list_obj);
++			return NULL;
++		}
++		json_object_array_add(chan_list_obj, str_obj);
++	}
++
++	return chan_list_obj;
++}
++
++
++static struct json_object *
++hostapd_afc_build_req_chan_list(struct hostapd_iface *iface)
++{
++	struct json_object *op_class_list_obj, *str_obj;
++	struct hostapd_config *iconf = iface->conf;
++	int i;
++
++	op_class_list_obj = json_object_new_array();
++	if (!op_class_list_obj)
++		return NULL;
++
++	for (i = 0; i < iconf->afc.n_op_class; i++) {
++		struct json_object *op_class_obj, *chan_list_obj;
++		u8 op_class = iconf->afc.op_class[i];
++
++		if (!is_6ghz_op_class(op_class))
++			continue;
++
++		op_class_obj = json_object_new_object();
++		if (!op_class_obj)
++			goto error;
++
++		json_object_array_add(op_class_list_obj, op_class_obj);
++		str_obj = json_object_new_int(op_class);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(op_class_obj, "globalOperatingClass",
++				       str_obj);
++
++		chan_list_obj = hostapd_afc_get_opclass_chan_list(op_class);
++		if (!chan_list_obj)
++			goto error;
++
++		json_object_object_add(op_class_obj, "channelCfi",
++				       chan_list_obj);
++	}
++
++	return op_class_list_obj;
++
++error:
++	json_object_put(op_class_list_obj);
++	return NULL;
++}
++
++
++static struct json_object *
++hostapd_afc_build_request(struct hostapd_iface *iface)
++{
++	struct json_object *l1_obj, *l2_obj, *la1_obj, *la2_obj;
++	struct json_object *s2_obj, *str_obj, *location_obj;
++	struct hostapd_config *iconf = iface->conf;
++	struct json_object *op_class_list_obj;
++	int i;
++
++	l1_obj = json_object_new_object();
++	if (!l1_obj)
++		return NULL;
++
++	if (iconf->afc.request.version) {
++		str_obj = json_object_new_string(iconf->afc.request.version);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(l1_obj, "version", str_obj);
++	}
++
++	la1_obj = json_object_new_array();
++	if (!la1_obj)
++		goto error;
++
++	json_object_object_add(l1_obj, "availableSpectrumInquiryRequests",
++			       la1_obj);
++	l2_obj = json_object_new_object();
++	if (!l2_obj)
++		goto error;
++
++	json_object_array_add(la1_obj, l2_obj);
++	if (iconf->afc.request.id) {
++		str_obj = json_object_new_string(iconf->afc.request.id);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(l2_obj, "requestId", str_obj);
++	}
++
++	s2_obj = json_object_new_object();
++	if (!s2_obj)
++		goto error;
++
++	json_object_object_add(l2_obj, "deviceDescriptor", s2_obj);
++	if (iconf->afc.request.sn) {
++		str_obj = json_object_new_string(iconf->afc.request.sn);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(s2_obj, "serialNumber", str_obj);
++	}
++
++	la2_obj = json_object_new_array();
++	if (!la2_obj)
++		goto error;
++
++	json_object_object_add(s2_obj, "certificationId", la2_obj);
++	for (i = 0; i < iconf->afc.n_cert_ids; i++) {
++		struct json_object *obj;
++
++		obj = json_object_new_object();
++		if (!obj)
++			goto error;
++
++		json_object_array_add(la2_obj, obj);
++		str_obj =
++			json_object_new_string(iconf->afc.cert_ids[i].rulset);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(obj, "rulesetId", str_obj);
++		str_obj = json_object_new_string(iconf->afc.cert_ids[i].id);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(obj, "id", str_obj);
++	}
++
++	location_obj = hostapd_afc_build_location_request(iface);
++	if (!location_obj)
++		goto error;
++
++	json_object_object_add(l2_obj, "location", location_obj);
++	str_obj = json_object_new_int(iconf->afc.min_power);
++	if (!str_obj)
++		goto error;
++
++	json_object_object_add(l2_obj, "minDesiredPower", str_obj);
++
++	if (iconf->afc.n_freq_range) {
++		struct json_object *freq_obj;
++
++		freq_obj = json_object_new_array();
++		if (!freq_obj)
++			goto error;
++
++		json_object_object_add(l2_obj, "inquiredFrequencyRange",
++				       freq_obj);
++		for (i = 0; i < iconf->afc.n_freq_range; i++) {
++			struct afc_freq_range *fr = &iconf->afc.freq_range[i];
++			struct json_object *obj;
++
++			obj = json_object_new_object();
++			if (!obj)
++				goto error;
++
++			json_object_array_add(freq_obj, obj);
++			str_obj = json_object_new_int(fr->low_freq);
++			if (!str_obj)
++				goto error;
++
++			json_object_object_add(obj, "lowFrequency", str_obj);
++			str_obj = json_object_new_int(fr->high_freq);
++			if (!str_obj)
++				goto error;
++
++			json_object_object_add(obj, "highFrequency", str_obj);
++		}
++	}
++
++	op_class_list_obj = hostapd_afc_build_req_chan_list(iface);
++	if (!op_class_list_obj)
++		goto error;
++
++	json_object_object_add(l2_obj, "inquiredChannels", op_class_list_obj);
++
++	wpa_printf(MSG_DEBUG, "Pending AFC request: %s",
++		   json_object_get_string(l1_obj));
++
++	return l1_obj;
++
++error:
++	json_object_put(l1_obj);
++
++	return NULL;
++}
++
++
++static int
++hostad_afc_parse_available_freq_info(struct hostapd_iface *iface,
++				     struct json_object *reply_elem_obj)
++{
++	struct afc_freq_range_elem *f = NULL;
++	struct json_object *obj;
++	int i, count = 0;
++
++	if (!json_object_object_get_ex(reply_elem_obj,
++				       "availableFrequencyInfo", &obj))
++		return 0;
++
++	for (i = 0; i < json_object_array_length(obj); i++) {
++		struct json_object *range_elem_obj, *freq_range_obj;
++		struct json_object *high_freq_obj, *low_freq_obj;
++		struct json_object *max_psd_obj;
++
++		range_elem_obj = json_object_array_get_idx(obj, i);
++		if (!range_elem_obj)
++			continue;
++
++		if (!json_object_object_get_ex(range_elem_obj,
++					       "frequencyRange",
++					       &freq_range_obj))
++			continue;
++
++		if (!json_object_object_get_ex(freq_range_obj,
++					       "lowFrequency",
++					       &low_freq_obj))
++			continue;
++
++		if (!json_object_object_get_ex(freq_range_obj,
++					       "highFrequency",
++					       &high_freq_obj))
++			continue;
++
++		if (!json_object_object_get_ex(range_elem_obj, "maxPsd",
++					       &max_psd_obj) &&
++		    !json_object_object_get_ex(range_elem_obj, "maxPSD",
++					       &max_psd_obj))
++			continue;
++
++		f = os_realloc_array(f, count + 1, sizeof(*f));
++		if (!f)
++			return -ENOMEM;
++
++		f[count].low_freq = json_object_get_int(low_freq_obj);
++		f[count].high_freq = json_object_get_int(high_freq_obj);
++		f[count++].max_psd = json_object_get_int(max_psd_obj);
++	}
++	iface->afc.freq_range = f;
++	iface->afc.num_freq_range = count;
++
++	return 0;
++}
++
++
++static int hostad_afc_update_chan_info(struct afc_chan_info_elem **chan_list,
++				       int *chan_list_size, u8 op_class,
++				       int center_chan, int power)
++{
++	int num_low_subchan, ch, count = *chan_list_size;
++	struct afc_chan_info_elem *c = *chan_list;
++
++	switch (op_class) {
++	case 132: /*  40MHz */
++		num_low_subchan = 2;
++		break;
++	case 133: /*  80MHz */
++		num_low_subchan = 6;
++		break;
++	case 134: /* 160MHz */
++		num_low_subchan = 14;
++		break;
++	default:
++		num_low_subchan = 0;
++		break;
++	}
++
++	for (ch = center_chan - num_low_subchan;
++	     ch <= center_chan + num_low_subchan; ch += 4) {
++		int i;
++
++		for (i = 0; i < count; i++) {
++			if (c[i].chan == ch)
++				break;
++		}
++
++		if (i == count) {
++			c = os_realloc_array(c, count + 1, sizeof(*c));
++			if (!c)
++				return -ENOMEM;
++
++			c[count].chan = ch;
++			c[count++].power = power;
++		}
++	}
++
++	*chan_list_size = count;
++	*chan_list = c;
++
++	return 0;
++}
++
++
++static int
++hostad_afc_parse_available_chan_info(struct hostapd_iface *iface,
++				     struct json_object *reply_elem_obj)
++{
++	struct afc_chan_info_elem *c = NULL;
++	struct json_object *obj;
++	int i, count = 0;
++
++	if (!json_object_object_get_ex(reply_elem_obj,
++				       "availableChannelInfo", &obj))
++		return 0;
++
++	for (i = 0; i < json_object_array_length(obj); i++) {
++		struct json_object *range_elem_obj, *op_class_obj;
++		struct json_object *chan_cfi_obj, *max_eirp_obj;
++		int ch, op_class;
++
++		range_elem_obj = json_object_array_get_idx(obj, i);
++		if (!range_elem_obj)
++			continue;
++
++		if (!json_object_object_get_ex(range_elem_obj,
++					       "globalOperatingClass",
++					       &op_class_obj))
++			continue;
++
++		if (!json_object_object_get_ex(range_elem_obj, "maxEirp",
++					       &max_eirp_obj))
++			continue;
++
++		if (!json_object_object_get_ex(range_elem_obj, "channelCfi",
++					       &chan_cfi_obj))
++			continue;
++
++		op_class = json_object_get_int(op_class_obj);
++		for (ch = 0;
++		     ch < json_object_array_length(chan_cfi_obj); ch++) {
++			struct json_object *pwr_obj;
++			struct json_object *ch_obj;
++			int channel, power;
++
++			ch_obj = json_object_array_get_idx(chan_cfi_obj, ch);
++			if (!ch_obj)
++				continue;
++
++			pwr_obj = json_object_array_get_idx(max_eirp_obj, ch);
++			if (!pwr_obj)
++				continue;
++
++			channel = json_object_get_int(ch_obj);
++			power = json_object_get_int(pwr_obj);
++
++			hostad_afc_update_chan_info(&c, &count, op_class,
++						    channel, power);
++		}
++		iface->afc.chan_info_list = c;
++		iface->afc.num_chan_info = count;
++	}
++
++	return 0;
++}
++
++
++static int hostad_afc_get_timeout(struct json_object *obj)
++{
++	time_t t, now;
++	struct tm tm;
++
++	if (sscanf(json_object_get_string(obj), "%d-%d-%dT%d:%d:%dZ",
++		   &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour,
++		   &tm.tm_min, &tm.tm_sec) <= 0)
++		return HOSTAPD_AFC_TIMEOUT;
++
++	tm.tm_year -= 1900;
++	tm.tm_mon -= 1;
++	tm.tm_isdst = -1;
++	t = mktime(&tm);
++	time(&now);
++
++	return now > t ? HOSTAPD_AFC_RETRY_TIMEOUT : (t - now) * 80 / 100;
++}
++
++
++static int hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply)
++{
++	struct json_object *payload_obj, *reply_obj, *version_obj;
++	struct hostapd_config *iconf = iface->conf;
++	int i, request_timeout = -1, ret = -EINVAL;
++
++	wpa_printf(MSG_DEBUG, "Received AFC reply: %s", reply);
++	payload_obj = json_tokener_parse(reply);
++	if (!payload_obj) {
++		wpa_printf(MSG_ERROR, "Failed to parse AFC reply payload");
++		return -EINVAL;
++	}
++
++	if (!json_object_object_get_ex(payload_obj, "version", &version_obj)) {
++		wpa_printf(MSG_ERROR, "Missing version in AFC reply");
++		return -EINVAL;
++	}
++
++	if (iconf->afc.request.version &&
++	    os_strcmp(iconf->afc.request.version,
++		      json_object_get_string(version_obj))) {
++		wpa_printf(MSG_ERROR, "Mismatch in AFC reply version");
++		return -EINVAL;
++	}
++
++	if (!json_object_object_get_ex(payload_obj,
++				       "availableSpectrumInquiryResponses",
++				       &reply_obj)) {
++		wpa_printf(MSG_ERROR,
++			   "Missing availableSpectrumInquiry in AFC reply");
++		return -EINVAL;
++	}
++
++	for (i = 0; i < json_object_array_length(reply_obj); i++) {
++		struct json_object *reply_elem_obj, *obj, *status_obj;
++		int j, status = -EINVAL;
++
++		reply_elem_obj = json_object_array_get_idx(reply_obj, i);
++		if (!reply_elem_obj) {
++			wpa_printf(MSG_DEBUG,
++				   "Failed to get reply element at index %d",
++				   i);
++			continue;
++		}
++
++		if (!json_object_object_get_ex(reply_elem_obj, "requestId",
++					       &obj)) {
++			wpa_printf(MSG_DEBUG,
++				   "Missing requestId in reply element %d", i);
++			continue;
++		}
++
++		if (iconf->afc.request.id &&
++		    os_strcmp(iconf->afc.request.id,
++			      json_object_get_string(obj))) {
++			wpa_printf(MSG_DEBUG,
++				   "RequestId mismatch in reply element %d",
++				   i);
++			continue;
++		}
++
++		if (!json_object_object_get_ex(reply_elem_obj, "rulesetId",
++					       &obj)) {
++			wpa_printf(MSG_DEBUG,
++				   "Missing rulesetId in reply element %d", i);
++			continue;
++		}
++
++		for (j = 0; j < iconf->afc.n_cert_ids; j++) {
++			if (!os_strcmp(iconf->afc.cert_ids[j].rulset,
++				       json_object_get_string(obj)))
++				break;
++		}
++
++		if (j == iconf->afc.n_cert_ids) {
++			wpa_printf(MSG_DEBUG,
++				   "RulesetId mismatch in reply element %d",
++				   i);
++			continue;
++		}
++
++		if (!json_object_object_get_ex(reply_elem_obj, "response",
++					       &obj)) {
++			wpa_printf(MSG_DEBUG,
++				   "Missing response field in reply element %d",
++				   i);
++			continue;
++		}
++
++		if (json_object_object_get_ex(obj, "shortDescription",
++					      &status_obj))
++			wpa_printf(MSG_DEBUG, "AFC reply element %d: %s",
++				   i, json_object_get_string(status_obj));
++
++		if (json_object_object_get_ex(obj, "responseCode",
++					      &status_obj))
++			status = json_object_get_int(status_obj);
++
++		if (status < 0) {
++			wpa_printf(MSG_DEBUG,
++				   "Reply element %d invalid responseCode: %d",
++				   i, status);
++			continue;
++		}
++
++		if (hostad_afc_parse_available_freq_info(iface,
++							 reply_elem_obj) ||
++		    hostad_afc_parse_available_chan_info(iface,
++							 reply_elem_obj))
++			continue;
++
++		if (json_object_object_get_ex(reply_elem_obj,
++					      "availabilityExpireTime",
++					      &obj)) {
++			int timeout = hostad_afc_get_timeout(obj);
++
++			if (request_timeout < 0 || timeout < request_timeout)
++				request_timeout = timeout;
++		}
++
++		ret = status;
++	}
++
++	iface->afc.data_valid = true;
++	iface->afc.timeout = request_timeout;
++	if (iface->afc.timeout < 0)
++		iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
++
++	return ret;
++}
++
++
++static int hostapd_afc_send_receive(struct hostapd_iface *iface)
++{
++	struct hostapd_config *iconf = iface->conf;
++	json_object *request_obj = NULL;
++	struct timeval sock_timeout = {
++		.tv_sec = 5,
++	};
++	struct sockaddr_un addr = {
++		.sun_family = AF_UNIX,
++#ifdef __FreeBSD__
++		.sun_len = sizeof(addr),
++#endif /* __FreeBSD__ */
++	};
++	const char *request;
++	char *buf = NULL;
++	int sockfd, ret;
++	fd_set read_set;
++
++	if (iface->afc.data_valid) {
++		/* AFC data already downloaded from the server */
++		return 0;
++	}
++
++	if (!iconf->afc.socket) {
++		wpa_printf(MSG_ERROR, "Missing AFC socket string");
++		return -EINVAL;
++	}
++
++	iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
++	if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) {
++		wpa_printf(MSG_ERROR, "Malformed AFC socket string %s",
++			   iconf->afc.socket);
++		return -EINVAL;
++	}
++
++	os_strlcpy(addr.sun_path, iconf->afc.socket, sizeof(addr.sun_path));
++	sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
++	if (sockfd < 0) {
++		wpa_printf(MSG_ERROR, "Failed creating AFC socket");
++		return sockfd;
++	}
++
++	if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
++		wpa_printf(MSG_ERROR, "Failed connecting AFC socket");
++		ret = -EIO;
++		goto close_sock;
++	}
++
++	request_obj = hostapd_afc_build_request(iface);
++	if (!request_obj) {
++		ret = -ENOMEM;
++		goto close_sock;
++	}
++
++	request = json_object_to_json_string(request_obj);
++	if (send(sockfd, request, strlen(request), 0) < 0) {
++		wpa_printf(MSG_ERROR, "Failed sending AFC request");
++		ret = -EIO;
++		goto close_sock;
++	}
++
++	FD_ZERO(&read_set);
++	FD_SET(sockfd, &read_set);
++	if (select(sockfd + 1, &read_set, NULL, NULL, &sock_timeout) < 0) {
++		wpa_printf(MSG_ERROR, "Select failed on AFC socket");
++		ret = -errno;
++		goto close_sock;
++	}
++
++	if (!FD_ISSET(sockfd, &read_set)) {
++		ret = -EIO;
++		goto close_sock;
++	}
++
++	buf = os_zalloc(HOSTAPD_AFC_BUFSIZE);
++	if (!buf) {
++		ret = -ENOMEM;
++		goto close_sock;
++	}
++
++	ret = recv(sockfd, buf, HOSTAPD_AFC_BUFSIZE - 1, 0);
++	if (ret <= 0)
++		goto close_sock;
++
++	ret = hostapd_afc_parse_reply(iface, buf);
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed parsing AFC reply: %d", ret);
++close_sock:
++	os_free(buf);
++	json_object_put(request_obj);
++	close(sockfd);
++
++	return ret;
++}
++
++
++static bool hostapd_afc_has_usable_chans(struct hostapd_iface *iface)
++{
++	const struct oper_class_map *oper_class;
++	int ch;
++
++	oper_class = get_oper_class(NULL, iface->conf->op_class);
++	if (!oper_class)
++		return false;
++
++	for (ch = oper_class->min_chan; ch <= oper_class->max_chan;
++	     ch += oper_class->inc) {
++		struct hostapd_hw_modes *mode = iface->current_mode;
++		int i;
++
++		for (i = 0; i < mode->num_channels; i++) {
++			struct hostapd_channel_data *chan = &mode->channels[i];
++
++			if (chan->chan == ch &&
++			    !(chan->flag & HOSTAPD_CHAN_DISABLED))
++				return true;
++		}
++	}
++
++	return false;
++}
++
++
++int hostapd_afc_handle_request(struct hostapd_iface *iface)
++{
++	struct hostapd_config *iconf = iface->conf;
++	int ret;
++
++	/* AFC is required just for standard power AP */
++	if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
++		return 1;
++
++	if (!is_6ghz_op_class(iconf->op_class) || !is_6ghz_freq(iface->freq))
++		return 1;
++
++	if (iface->state == HAPD_IFACE_ACS)
++		return 1;
++
++	ret = hostapd_afc_send_receive(iface);
++	if (ret < 0) {
++		/*
++		 * If the connection to the AFCD failed, resched for a
++		 * future attempt.
++		 */
++		wpa_printf(MSG_ERROR, "AFC connection failed: %d", ret);
++		if (ret == -EIO)
++			ret = 0;
++		goto resched;
++	}
++
++	hostap_afc_disable_channels(iface);
++	if (!hostapd_afc_has_usable_chans(iface))
++		goto resched;
++
++	if (!hostapd_is_usable_chans(iface)) {
++		/* Trigger an ACS freq scan */
++		iconf->channel = 0;
++		iface->freq = 0;
++
++		if (acs_init(iface) != HOSTAPD_CHAN_ACS) {
++			wpa_printf(MSG_ERROR, "Could not start ACS");
++			ret = -EINVAL;
++		}
++	} else {
++		ret = 1;
++	}
++
++resched:
++	eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
++	eloop_register_timeout(iface->afc.timeout, 0,
++			       hostapd_afc_timeout_handler, iface, NULL);
++
++	return ret;
++}
++
++
++static void hostapd_afc_delete_data_from_server(struct hostapd_iface *iface)
++{
++	os_free(iface->afc.chan_info_list);
++	os_free(iface->afc.freq_range);
++
++	iface->afc.num_freq_range = 0;
++	iface->afc.num_chan_info = 0;
++
++	iface->afc.chan_info_list = NULL;
++	iface->afc.freq_range = NULL;
++
++	iface->afc.data_valid = false;
++}
++
++
++static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx)
++{
++	struct hostapd_iface *iface = eloop_ctx;
++	bool restart_iface = true;
++
++	hostapd_afc_delete_data_from_server(iface);
++	if (iface->state != HAPD_IFACE_ENABLED) {
++		/* Hostapd is not fully enabled yet, toggle the interface */
++		goto restart_interface;
++	}
++
++	if (hostapd_afc_send_receive(iface) < 0 ||
++	    hostapd_get_hw_features(iface)) {
++		restart_iface = false;
++		goto restart_interface;
++	}
++
++	if (hostapd_is_usable_chans(iface))
++		goto resched;
++
++	restart_iface = hostapd_afc_has_usable_chans(iface);
++	if (restart_iface) {
++		/* Trigger an ACS freq scan */
++		iface->conf->channel = 0;
++		iface->freq = 0;
++	}
++
++restart_interface:
++	hostapd_disable_iface(iface);
++	if (restart_iface)
++		hostapd_enable_iface(iface);
++resched:
++	eloop_register_timeout(iface->afc.timeout, 0,
++			       hostapd_afc_timeout_handler, iface, NULL);
++}
++
++
++void hostapd_afc_stop(struct hostapd_iface *iface)
++{
++	eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
++}
++
++
++void hostap_afc_disable_channels(struct hostapd_iface *iface)
++{
++	struct hostapd_hw_modes *mode = NULL;
++	int i;
++
++	for (i = 0; i < iface->num_hw_features; i++) {
++		mode = &iface->hw_features[i];
++		if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
++		    mode->is_6ghz)
++			break;
++	}
++
++	if (i == iface->num_hw_features)
++		return;
++
++	if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type))
++		return;
++
++	if (!iface->afc.data_valid)
++		return;
++
++	for (i = 0; i < mode->num_channels; i++) {
++		struct hostapd_channel_data *chan = &mode->channels[i];
++		int j;
++
++		if (!is_6ghz_freq(chan->freq))
++			continue;
++
++		for (j = 0; j < iface->afc.num_freq_range; j++) {
++			if (chan->freq >= iface->afc.freq_range[j].low_freq &&
++			    chan->freq <= iface->afc.freq_range[j].high_freq)
++				break;
++		}
++
++		if (j != iface->afc.num_freq_range)
++			continue;
++
++		for (j = 0; j < iface->afc.num_chan_info; j++) {
++			if (chan->chan == iface->afc.chan_info_list[j].chan)
++				break;
++		}
++
++		if (j != iface->afc.num_chan_info)
++			continue;
++
++		chan->flag |= HOSTAPD_CHAN_DISABLED;
++		wpa_printf(MSG_MSGDUMP,
++			   "Disabling freq=%d MHz (not allowed by AFC)",
++			   chan->freq);
++	}
++}
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index c6aa49610..9e34e029a 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -1047,6 +1047,22 @@ void hostapd_config_free(struct hostapd_config *conf)
+ #endif /* CONFIG_ACS */
+ 	wpabuf_free(conf->lci);
+ 	wpabuf_free(conf->civic);
++#ifdef CONFIG_AFC
++	os_free(conf->afc.socket);
++	os_free(conf->afc.request.version);
++	os_free(conf->afc.request.id);
++	os_free(conf->afc.request.sn);
++	for (i = 0; i < conf->afc.n_cert_ids; i++) {
++		os_free(conf->afc.cert_ids[i].rulset);
++		os_free(conf->afc.cert_ids[i].id);
++	}
++	os_free(conf->afc.cert_ids);
++	os_free(conf->afc.location.height_type);
++	os_free(conf->afc.location.linear_polygon_data);
++	os_free(conf->afc.location.radial_polygon_data);
++	os_free(conf->afc.freq_range);
++	os_free(conf->afc.op_class);
++#endif /* CONFIG_AFC */
+ 
+ 	os_free(conf);
+ }
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index d42076785..e6669e6a3 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1249,6 +1249,53 @@ struct hostapd_config {
+ 
+ 	/* Whether to enable TWT responder in HT and VHT modes */
+ 	bool ht_vht_twt_responder;
++
++#ifdef CONFIG_AFC
++	struct {
++		char *socket;
++		struct {
++			char *version;
++			char *id;
++			char *sn;
++		} request;
++		unsigned int n_cert_ids;
++		struct cert_id {
++			char *rulset;
++			char *id;
++		} *cert_ids;
++		struct {
++			enum afc_location_type {
++				ELLIPSE,
++				LINEAR_POLYGON,
++				RADIAL_POLYGON,
++			} type;
++			unsigned int n_linear_polygon_data;
++			struct afc_linear_polygon {
++				double longitude;
++				double latitude;
++			} *linear_polygon_data;
++			unsigned int n_radial_polygon_data;
++			struct afc_radial_polygon {
++				double length;
++				double angle;
++			} *radial_polygon_data;
++			int major_axis;
++			int minor_axis;
++			int orientation;
++			double height;
++			char *height_type;
++			int vertical_tolerance;
++		} location;
++		unsigned int n_freq_range;
++		struct afc_freq_range {
++			int low_freq;
++			int high_freq;
++		} *freq_range;
++		unsigned int n_op_class;
++		unsigned int *op_class;
++		int min_power;
++	} afc;
++#endif /* CONFIG_AFC */
+ };
+ 
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 36d48ae09..5a8cdc90e 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -715,6 +715,7 @@ void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
+ static void hostapd_cleanup_iface(struct hostapd_iface *iface)
+ {
+ 	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
++	hostapd_afc_stop(iface);
+ 	eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
+ 			     NULL);
+ 
+@@ -2559,6 +2560,16 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
+ 		}
+ #endif /* CONFIG_MESH */
+ 
++#ifdef CONFIG_IEEE80211AX
++		/* check AFC for 6GHz channels. */
++		res = hostapd_afc_handle_request(iface);
++		if (res <= 0) {
++			if (res < 0)
++				goto fail;
++			return res;
++		}
++#endif /* CONFIG_IEEE80211AX */
++
+ 		if (!delay_apply_cfg &&
+ 		    hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
+ 				     hapd->iconf->channel,
+@@ -2957,6 +2968,7 @@ void hostapd_interface_deinit(struct hostapd_iface *iface)
+ 
+ 	hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+ 
++	hostapd_afc_stop(iface);
+ 	eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+ 	iface->wait_channel_update = 0;
+ 	iface->is_no_ir = false;
+@@ -3030,6 +3042,10 @@ void hostapd_interface_free(struct hostapd_iface *iface)
+ 			   __func__, iface->bss[j]);
+ 		os_free(iface->bss[j]);
+ 	}
++#ifdef CONFIG_AFC
++	os_free(iface->afc.chan_info_list);
++	os_free(iface->afc.freq_range);
++#endif
+ 	hostapd_cleanup_iface(iface);
+ }
+ 
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 2ef63e5f2..d67a0afa0 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -718,9 +718,54 @@ struct hostapd_iface {
+ 	bool is_no_ir;
+ 
+ 	bool is_ch_switch_dfs; /* Channel switch from ACS to DFS */
++
++#ifdef CONFIG_AFC
++	struct {
++		int timeout;
++		unsigned int num_freq_range;
++		struct afc_freq_range_elem {
++			int low_freq;
++			int high_freq;
++			/**
++			 * max eirp power spectral density received from
++			 * the AFC coordinator for this band
++			 */
++			int max_psd;
++		} *freq_range;
++		unsigned int num_chan_info;
++		struct afc_chan_info_elem {
++			int chan;
++			/**
++			 * max eirp power received from the AFC coordinator
++			 * for this channel
++			 */
++			int power;
++		} *chan_info_list;
++		bool data_valid;
++	} afc;
++#endif /* CONFIG_AFC */
+ };
+ 
+ /* hostapd.c */
++#ifdef CONFIG_AFC
++int hostapd_afc_handle_request(struct hostapd_iface *iface);
++void hostapd_afc_stop(struct hostapd_iface *iface);
++void hostap_afc_disable_channels(struct hostapd_iface *iface);
++#else
++static inline int hostapd_afc_handle_request(struct hostapd_iface *iface)
++{
++	return 1;
++}
++
++static inline void hostapd_afc_stop(struct hostapd_iface *iface)
++{
++}
++
++static inline void hostap_afc_disable_channels(struct hostapd_iface *iface)
++{
++}
++#endif /* CONFIG_AFC */
++
+ int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+ 			       int (*cb)(struct hostapd_iface *iface,
+ 					 void *ctx), void *ctx);
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index 85e67080d..8aa0b3ab5 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -114,6 +114,8 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
+ 	iface->hw_features = modes;
+ 	iface->num_hw_features = num_modes;
+ 
++	hostap_afc_disable_channels(iface);
++
+ 	for (i = 0; i < num_modes; i++) {
+ 		struct hostapd_hw_modes *feature = &modes[i];
+ 		int dfs_enabled = hapd->iconf->ieee80211h &&
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0008-hostapd-MLO-extend-support-for-cohosted-ML-BSS.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0008-hostapd-MLO-extend-support-for-cohosted-ML-BSS.patch
deleted file mode 100644
index 42c4b67..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0008-hostapd-MLO-extend-support-for-cohosted-ML-BSS.patch
+++ /dev/null
@@ -1,368 +0,0 @@
-From 27dbd9d9796d656c8cf78d51d48162208080a987 Mon Sep 17 00:00:00 2001
-From: Sriram R <quic_srirrama@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:38 +0530
-Subject: [PATCH 008/104] hostapd: MLO: extend support for cohosted ML BSS
-
-Modify necessary helper apis to support multiple BSS support for MLO to
-make the changes scalable.
-
-Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/ieee802_11.c     | 116 ++++++++++++++++------------------------
- src/ap/ieee802_11_eht.c |  27 +++-------
- src/ap/wpa_auth_glue.c  |  52 +++++++++++-------
- 3 files changed, 89 insertions(+), 106 deletions(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 98398ccdd..26e3d8356 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -4567,7 +4567,7 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
- 				  bool offload)
- {
- #ifdef CONFIG_IEEE80211BE
--	unsigned int i, j;
-+	unsigned int i;
- 
- 	if (!hostapd_is_mld_ap(hapd))
- 		return 0;
-@@ -4582,25 +4582,25 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
- 		hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
- 
- 	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
--		struct hostapd_iface *iface = NULL;
-+		struct hostapd_data *bss = NULL;
- 		struct mld_link_info *link = &sta->mld_info.links[i];
-+		bool link_bss_found = false;
- 
- 		if (!link->valid)
- 			continue;
- 
--		for (j = 0; j < hapd->iface->interfaces->count; j++) {
--			iface = hapd->iface->interfaces->iface[j];
-+		for_each_mld_link(bss, hapd) {
-+			if (bss == hapd)
-+				continue;
- 
--			if (hapd->iface == iface)
-+			if (bss->mld_link_id != i)
- 				continue;
- 
--			if (hostapd_is_ml_partner(hapd, iface->bss[0]) &&
--			    i == iface->bss[0]->mld_link_id)
--				break;
-+			link_bss_found = true;
-+			break;
- 		}
- 
--		if (!iface || j == hapd->iface->interfaces->count ||
--		    TEST_FAIL()) {
-+		if (!link_bss_found || TEST_FAIL()) {
- 			wpa_printf(MSG_DEBUG,
- 				   "MLD: No link match for link_id=%u", i);
- 
-@@ -4613,7 +4613,7 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
- 			if (!offload)
- 				ieee80211_ml_build_assoc_resp(hapd, link);
- 		} else {
--			if (ieee80211_ml_process_link(iface->bss[0], sta, link,
-+			if (ieee80211_ml_process_link(bss, sta, link,
- 						      ies, ies_len, reassoc,
- 						      offload))
- 				return -1;
-@@ -5777,7 +5777,7 @@ static bool hostapd_ml_handle_disconnect(struct hostapd_data *hapd,
- #ifdef CONFIG_IEEE80211BE
- 	struct hostapd_data *assoc_hapd, *tmp_hapd;
- 	struct sta_info *assoc_sta;
--	unsigned int i, link_id;
-+	struct sta_info *tmp_sta;
- 
- 	if (!hostapd_is_mld_ap(hapd))
- 		return false;
-@@ -5790,45 +5790,27 @@ static bool hostapd_ml_handle_disconnect(struct hostapd_data *hapd,
- 	if (!assoc_sta)
- 		return false;
- 
--	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
--		for (i = 0; i < assoc_hapd->iface->interfaces->count; i++) {
--			struct sta_info *tmp_sta;
--
--			if (!assoc_sta->mld_info.links[link_id].valid)
--				continue;
-+	for_each_mld_link(tmp_hapd, assoc_hapd) {
-+		if (tmp_hapd == assoc_hapd)
-+			continue;
- 
--			tmp_hapd =
--				assoc_hapd->iface->interfaces->iface[i]->bss[0];
-+		if (!assoc_sta->mld_info.links[tmp_hapd->mld_link_id].valid)
-+			continue;
- 
--			if (!hostapd_is_ml_partner(assoc_hapd, tmp_hapd))
-+		for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
-+		     tmp_sta = tmp_sta->next) {
-+			if (tmp_sta->mld_assoc_link_id !=
-+			    assoc_sta->mld_assoc_link_id ||
-+			    tmp_sta->aid != assoc_sta->aid)
- 				continue;
- 
--			for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
--			     tmp_sta = tmp_sta->next) {
--				/*
--				 * Remove the station on which the association
--				 * was done only after all other link stations
--				 * are removed. Since there is only a single
--				 * station per struct hostapd_hapd with the
--				 * same association link simply break out from
--				 * the loop.
--				 */
--				if (tmp_sta == assoc_sta)
--					break;
--
--				if (tmp_sta->mld_assoc_link_id !=
--				    assoc_sta->mld_assoc_link_id ||
--				    tmp_sta->aid != assoc_sta->aid)
--					continue;
--
--				if (!disassoc)
--					hostapd_deauth_sta(tmp_hapd, tmp_sta,
--							   mgmt);
--				else
--					hostapd_disassoc_sta(tmp_hapd, tmp_sta,
--							     mgmt);
--				break;
--			}
-+			if (!disassoc)
-+				hostapd_deauth_sta(tmp_hapd, tmp_sta,
-+						   mgmt);
-+			else
-+				hostapd_disassoc_sta(tmp_hapd, tmp_sta,
-+						     mgmt);
-+			break;
- 		}
- 	}
- 
-@@ -6451,38 +6433,34 @@ static void hostapd_ml_handle_assoc_cb(struct hostapd_data *hapd,
- 				       struct sta_info *sta, bool ok)
- {
- #ifdef CONFIG_IEEE80211BE
--	unsigned int i, link_id;
-+	struct hostapd_data *tmp_hapd;
- 
- 	if (!hostapd_is_mld_ap(hapd))
- 		return;
- 
--	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
--		struct mld_link_info *link = &sta->mld_info.links[link_id];
-+	for_each_mld_link(tmp_hapd, hapd) {
-+		struct mld_link_info *link =
-+				&sta->mld_info.links[tmp_hapd->mld_link_id];
-+		struct sta_info *tmp_sta;
- 
--		if (!link->valid)
-+		if (tmp_hapd == hapd)
- 			continue;
- 
--		for (i = 0; i < hapd->iface->interfaces->count; i++) {
--			struct sta_info *tmp_sta;
--			struct hostapd_data *tmp_hapd =
--				hapd->iface->interfaces->iface[i]->bss[0];
-+		if (!link->valid)
-+			continue;
- 
--			if (!hostapd_is_ml_partner(tmp_hapd, hapd))
-+		for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
-+		     tmp_sta = tmp_sta->next) {
-+			if (tmp_sta == sta ||
-+			    tmp_sta->mld_assoc_link_id !=
-+			    sta->mld_assoc_link_id ||
-+			    tmp_sta->aid != sta->aid)
- 				continue;
- 
--			for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
--			     tmp_sta = tmp_sta->next) {
--				if (tmp_sta == sta ||
--				    tmp_sta->mld_assoc_link_id !=
--				    sta->mld_assoc_link_id ||
--				    tmp_sta->aid != sta->aid)
--					continue;
--
--				ieee80211_ml_link_sta_assoc_cb(tmp_hapd,
--							       tmp_sta, link,
--							       ok);
--				break;
--			}
-+			ieee80211_ml_link_sta_assoc_cb(tmp_hapd,
-+						       tmp_sta, link,
-+						       ok);
-+			break;
- 		}
- 	}
- #endif /* CONFIG_IEEE80211BE */
-diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
-index 7365057ad..353a4116e 100644
---- a/src/ap/ieee802_11_eht.c
-+++ b/src/ap/ieee802_11_eht.c
-@@ -1029,7 +1029,7 @@ const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
- static int hostapd_mld_validate_assoc_info(struct hostapd_data *hapd,
- 					   struct sta_info *sta)
- {
--	u8 i, link_id;
-+	u8 link_id;
- 	struct mld_info *info = &sta->mld_info;
- 
- 	if (!ap_sta_is_mld(hapd, sta)) {
-@@ -1049,31 +1049,20 @@ static int hostapd_mld_validate_assoc_info(struct hostapd_data *hapd,
- 	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- 		struct hostapd_data *other_hapd;
- 
--		if (!info->links[link_id].valid)
-+		if (!info->links[link_id].valid || link_id == hapd->mld_link_id)
- 			continue;
- 
--		for (i = 0; i < hapd->iface->interfaces->count; i++) {
--			other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
--
--			if (hapd == other_hapd)
--				continue;
--
--			if (hostapd_is_ml_partner(hapd, other_hapd) &&
--			    link_id == other_hapd->mld_link_id)
--				break;
--		}
--
--		if (i == hapd->iface->interfaces->count &&
--		    link_id != hapd->mld_link_id) {
-+		other_hapd = hostapd_mld_get_link_bss(hapd, link_id);
-+		if (!other_hapd) {
- 			wpa_printf(MSG_DEBUG, "MLD: Invalid link ID=%u",
- 				   link_id);
- 			return -1;
- 		}
- 
--		if (i < hapd->iface->interfaces->count)
--			os_memcpy(info->links[link_id].local_addr,
--				  other_hapd->own_addr,
--				  ETH_ALEN);
-+		os_memcpy(info->links[link_id].local_addr,
-+			  other_hapd->own_addr,
-+			  ETH_ALEN);
-+
- 	}
- 
- 	return 0;
-diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
-index 012f2b803..d3cd44695 100644
---- a/src/ap/wpa_auth_glue.c
-+++ b/src/ap/wpa_auth_glue.c
-@@ -1537,7 +1537,7 @@ static int hostapd_wpa_auth_get_ml_rsn_info(void *ctx,
- 					    struct wpa_auth_ml_rsn_info *info)
- {
- 	struct hostapd_data *hapd = ctx;
--	unsigned int i, j;
-+	unsigned int i;
- 
- 	wpa_printf(MSG_DEBUG, "WPA_AUTH: MLD: Get RSN info CB: n_mld_links=%u",
- 		   info->n_mld_links);
-@@ -1547,26 +1547,33 @@ static int hostapd_wpa_auth_get_ml_rsn_info(void *ctx,
- 
- 	for (i = 0; i < info->n_mld_links; i++) {
- 		unsigned int link_id = info->links[i].link_id;
-+		struct hostapd_data *bss = NULL;
-+		bool link_bss_found = false;
- 
- 		wpa_printf(MSG_DEBUG,
- 			   "WPA_AUTH: MLD: Get link RSN CB: link_id=%u",
- 			   link_id);
- 
--		for (j = 0; j < hapd->iface->interfaces->count; j++) {
--			struct hostapd_iface *iface =
--				hapd->iface->interfaces->iface[j];
-+		if (hapd->mld_link_id == link_id) {
-+			wpa_auth_ml_get_rsn_info(hapd->wpa_auth,
-+						 &info->links[i]);
-+			continue;
-+		}
- 
--			if (!hostapd_is_ml_partner(hapd, iface->bss[0]) ||
--			    link_id != iface->bss[0]->mld_link_id ||
--			    !iface->bss[0]->wpa_auth)
-+		for_each_mld_link(bss, hapd) {
-+			if (bss == hapd)
- 				continue;
- 
--			wpa_auth_ml_get_rsn_info(iface->bss[0]->wpa_auth,
-+			if (bss->mld_link_id != link_id)
-+				continue;
-+
-+			wpa_auth_ml_get_rsn_info(bss->wpa_auth,
- 						 &info->links[i]);
-+			link_bss_found = true;
- 			break;
- 		}
- 
--		if (j == hapd->iface->interfaces->count)
-+		if (!link_bss_found)
- 			wpa_printf(MSG_DEBUG,
- 				   "WPA_AUTH: MLD: link=%u not found", link_id);
- 	}
-@@ -1579,7 +1586,7 @@ static int hostapd_wpa_auth_get_ml_key_info(void *ctx,
- 					    struct wpa_auth_ml_key_info *info)
- {
- 	struct hostapd_data *hapd = ctx;
--	unsigned int i, j;
-+	unsigned int i;
- 
- 	wpa_printf(MSG_DEBUG, "WPA_AUTH: MLD: Get key info CB: n_mld_links=%u",
- 		   info->n_mld_links);
-@@ -1588,29 +1595,38 @@ static int hostapd_wpa_auth_get_ml_key_info(void *ctx,
- 		return -1;
- 
- 	for (i = 0; i < info->n_mld_links; i++) {
-+		struct hostapd_data *bss = NULL;
- 		u8 link_id = info->links[i].link_id;
-+		bool link_bss_found = false;
- 
- 		wpa_printf(MSG_DEBUG,
- 			   "WPA_AUTH: MLD: Get link info CB: link_id=%u",
- 			   link_id);
- 
--		for (j = 0; j < hapd->iface->interfaces->count; j++) {
--			struct hostapd_iface *iface =
--				hapd->iface->interfaces->iface[j];
-+		if (hapd->mld_link_id == link_id) {
-+			wpa_auth_ml_get_key_info(hapd->wpa_auth,
-+						 &info->links[i],
-+						 info->mgmt_frame_prot,
-+						 info->beacon_prot);
-+			continue;
-+		}
-+
-+		for_each_mld_link(bss, hapd) {
-+			if (bss == hapd)
-+				continue;
- 
--			if (!hostapd_is_ml_partner(hapd, iface->bss[0]) ||
--			    link_id != iface->bss[0]->mld_link_id ||
--			    !iface->bss[0]->wpa_auth)
-+			if (bss->mld_link_id != link_id)
- 				continue;
- 
--			wpa_auth_ml_get_key_info(iface->bss[0]->wpa_auth,
-+			wpa_auth_ml_get_key_info(bss->wpa_auth,
- 						 &info->links[i],
- 						 info->mgmt_frame_prot,
- 						 info->beacon_prot);
-+			link_bss_found = true;
- 			break;
- 		}
- 
--		if (j == hapd->iface->interfaces->count)
-+		if (!link_bss_found)
- 			wpa_printf(MSG_DEBUG,
- 				   "WPA_AUTH: MLD: link=%u not found", link_id);
- 	}
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0008-hostapd-update-TPE-IE-according-to-AFC.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0008-hostapd-update-TPE-IE-according-to-AFC.patch
new file mode 100644
index 0000000..c6752f7
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0008-hostapd-update-TPE-IE-according-to-AFC.patch
@@ -0,0 +1,166 @@
+From 4e29b0943aeb681e09dcd0e59ae3532246f44c3d Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 17 May 2024 11:50:28 +0200
+Subject: [PATCH 008/126] hostapd: update TPE IE according to AFC
+
+Update Transmit Power Envelope (TPE) IE according to the reply from AFC
+coordinator on UNII-5 or UNII-7 6GHz bands.
+
+Tested-by: Felix Fietkau <nbd@nbd.name>
+Tested-by: Allen Ye <allen.ye@mediatek.com>
+Tested-by: Krishna Chaitanya <chaitanya.mgit@gmail.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ src/ap/afc.c        | 37 +++++++++++++++++++++++++++++++++++
+ src/ap/hostapd.h    |  9 +++++++++
+ src/ap/ieee802_11.c | 47 ++++++++++++++++++++++++++++-----------------
+ 3 files changed, 75 insertions(+), 18 deletions(-)
+
+diff --git a/src/ap/afc.c b/src/ap/afc.c
+index cfee83fe7..361ecb575 100644
+--- a/src/ap/afc.c
++++ b/src/ap/afc.c
+@@ -1039,3 +1039,40 @@ void hostap_afc_disable_channels(struct hostapd_iface *iface)
+ 			   chan->freq);
+ 	}
+ }
++
++
++int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
++				       int *power)
++{
++	int i;
++
++	if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type))
++		return -EINVAL;
++
++	if (!iface->afc.data_valid)
++		return -EINVAL;
++
++	if (psd) {
++		for (i = 0; i < iface->afc.num_freq_range; i++) {
++			struct afc_freq_range_elem *f;
++
++			f = &iface->afc.freq_range[i];
++			if (iface->freq >= f->low_freq &&
++			    iface->freq <= f->high_freq) {
++				*power = 2 * f->max_psd;
++				return 0;
++			}
++		}
++	} else {
++		for (i = 0; i < iface->afc.num_chan_info; i++) {
++			struct afc_chan_info_elem *c;
++
++			c = &iface->afc.chan_info_list[i];
++			if (c->chan == iface->conf->channel) {
++				*power = 2 * c->power;
++				return 0;
++			}
++		}
++	}
++	return -EINVAL;
++}
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index d67a0afa0..996977fdf 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -748,10 +748,19 @@ struct hostapd_iface {
+ 
+ /* hostapd.c */
+ #ifdef CONFIG_AFC
++int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
++				       int *power);
+ int hostapd_afc_handle_request(struct hostapd_iface *iface);
+ void hostapd_afc_stop(struct hostapd_iface *iface);
+ void hostap_afc_disable_channels(struct hostapd_iface *iface);
+ #else
++static inline int
++hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
++				   int *power)
++{
++	return -EINVAL;
++}
++
+ static inline int hostapd_afc_handle_request(struct hostapd_iface *iface)
+ {
+ 	return 1;
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index fd1de5ebc..d8d82d737 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7110,42 +7110,53 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
+ 	 */
+ 	if (is_6ghz_op_class(iconf->op_class)) {
+ 		enum max_tx_pwr_interpretation tx_pwr_intrpn;
++		int err, max_eirp_psd, max_eirp_power;
+ 
+ 		/* Same Maximum Transmit Power for all 20 MHz bands */
+ 		tx_pwr_count = 0;
+ 		tx_pwr_intrpn = REGULATORY_CLIENT_EIRP_PSD;
+ 
+ 		/* Default Transmit Power Envelope for Global Operating Class */
+-		if (hapd->iconf->reg_def_cli_eirp_psd != -1)
+-			tx_pwr = hapd->iconf->reg_def_cli_eirp_psd;
+-		else
+-			tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
++		err = hostap_afc_get_chan_max_eirp_power(iface, true,
++							 &max_eirp_psd);
++		if (err < 0) {
++			if (hapd->iconf->reg_def_cli_eirp_psd != -1)
++				max_eirp_psd = hapd->iconf->reg_def_cli_eirp_psd;
++			else
++				max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
++		}
+ 
+ 		eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn,
+-					   REG_DEFAULT_CLIENT, tx_pwr);
++					   REG_DEFAULT_CLIENT, max_eirp_psd);
+ 
+ 		/* Indoor Access Point must include an additional TPE for
+ 		 * subordinate devices */
+ 		if (he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type)) {
+-			/* TODO: Extract PSD limits from channel data */
+-			if (hapd->iconf->reg_sub_cli_eirp_psd != -1)
+-				tx_pwr = hapd->iconf->reg_sub_cli_eirp_psd;
+-			else
+-				tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
++			if (err < 0) {
++				/* non-AFC connection */
++				if (hapd->iconf->reg_sub_cli_eirp_psd != -1)
++					max_eirp_psd = hapd->iconf->reg_sub_cli_eirp_psd;
++				else
++					max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
++			}
+ 			eid = hostapd_add_tpe_info(eid, tx_pwr_count,
+ 						   tx_pwr_intrpn,
+ 						   REG_SUBORDINATE_CLIENT,
+-						   tx_pwr);
++						   max_eirp_psd);
+ 		}
+ 
+-		if (iconf->reg_def_cli_eirp != -1 &&
+-		    he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
+-			eid = hostapd_add_tpe_info(
+-				eid, tx_pwr_count, REGULATORY_CLIENT_EIRP,
+-				REG_DEFAULT_CLIENT,
+-				hapd->iconf->reg_def_cli_eirp);
++		if (hostap_afc_get_chan_max_eirp_power(iface, false,
++						       &max_eirp_power)) {
++			max_eirp_power = iconf->reg_def_cli_eirp;
++			if (max_eirp_power == -1 ||
++			    !he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
++				return eid;
++		}
+ 
+-		return eid;
++		return hostapd_add_tpe_info(eid, tx_pwr_count,
++					    REGULATORY_CLIENT_EIRP,
++					    REG_DEFAULT_CLIENT,
++					    max_eirp_power);
+ 	}
+ #endif /* CONFIG_IEEE80211AX */
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0009-hostapd-MLO-pass-link_id-in-get_hapd_bssid-helper-fu.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0009-hostapd-MLO-pass-link_id-in-get_hapd_bssid-helper-fu.patch
deleted file mode 100644
index 6b9fc79..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0009-hostapd-MLO-pass-link_id-in-get_hapd_bssid-helper-fu.patch
+++ /dev/null
@@ -1,120 +0,0 @@
-From 30cd94f678f5f85703854812f0deb6467b37df5f Mon Sep 17 00:00:00 2001
-From: Sriram R <quic_srirrama@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:39 +0530
-Subject: [PATCH 009/104] hostapd: MLO: pass link_id in get_hapd_bssid helper
- function
-
-Currently get_hapd_bssid() function matches the given bssid in all bsses
-of its own iface. However with MLO, there is requirement to check its
-own partner BSS at least.
-
-Make changes to compare its link partners as well and if link id passed
-matches with the link id of the partner then return it.
-
-Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/drv_callbacks.c | 47 +++++++++++++++++++++++++-----------------
- 1 file changed, 28 insertions(+), 19 deletions(-)
-
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index 2d3206909..adac2d478 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -1750,7 +1750,7 @@ switch_link_hapd(struct hostapd_data *hapd, int link_id)
- #define HAPD_BROADCAST ((struct hostapd_data *) -1)
- 
- static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
--					    const u8 *bssid)
-+					    const u8 *bssid, int link_id)
- {
- 	size_t i;
- 
-@@ -1761,8 +1761,30 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
- 		return HAPD_BROADCAST;
- 
- 	for (i = 0; i < iface->num_bss; i++) {
-+#ifdef CONFIG_IEEE80211BE
-+		struct hostapd_data *hapd, *p_hapd;
-+
-+		hapd = iface->bss[i];
-+		if (ether_addr_equal(bssid, hapd->own_addr) ||
-+		    (hapd->conf->mld_ap &&
-+		     ether_addr_equal(bssid, hapd->mld->mld_addr) &&
-+		     link_id == hapd->mld_link_id)) {
-+			return hapd;
-+		} else if (hapd->conf->mld_ap) {
-+			for_each_mld_link(p_hapd, hapd) {
-+				if (p_hapd == hapd)
-+					continue;
-+
-+				if (ether_addr_equal(bssid, p_hapd->own_addr) ||
-+				    (ether_addr_equal(bssid, p_hapd->mld->mld_addr) &&
-+				     link_id == p_hapd->mld_link_id))
-+					return p_hapd;
-+			}
-+		}
-+#else
- 		if (ether_addr_equal(bssid, iface->bss[i]->own_addr))
- 			return iface->bss[i];
-+#endif /*CONFIG_IEEE80211BE */
- 	}
- 
- 	return NULL;
-@@ -1773,7 +1795,7 @@ static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd,
- 					const u8 *bssid, const u8 *addr,
- 					int wds)
- {
--	hapd = get_hapd_bssid(hapd->iface, bssid);
-+	hapd = get_hapd_bssid(hapd->iface, bssid, -1);
- 	if (hapd == NULL || hapd == HAPD_BROADCAST)
- 		return;
- 
-@@ -1813,14 +1835,7 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
- 	if (bssid == NULL)
- 		return 0;
- 
--#ifdef CONFIG_IEEE80211BE
--	if (hapd->conf->mld_ap &&
--	    ether_addr_equal(hapd->mld->mld_addr, bssid))
--		is_mld = true;
--#endif /* CONFIG_IEEE80211BE */
--
--	if (!is_mld)
--		hapd = get_hapd_bssid(iface, bssid);
-+	hapd = get_hapd_bssid(iface, bssid, rx_mgmt->link_id);
- 
- 	if (!hapd) {
- 		u16 fc = le_to_host16(hdr->frame_control);
-@@ -1872,17 +1887,11 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
- 	struct ieee80211_hdr *hdr;
- 	struct hostapd_data *orig_hapd, *tmp_hapd;
- 
--#ifdef CONFIG_IEEE80211BE
--	if (hapd->conf->mld_ap && link_id != -1) {
--		tmp_hapd = hostapd_mld_get_link_bss(hapd, link_id);
--		if (tmp_hapd)
--			hapd = tmp_hapd;
--	}
--#endif /* CONFIG_IEEE80211BE */
- 	orig_hapd = hapd;
- 
- 	hdr = (struct ieee80211_hdr *) buf;
--	tmp_hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
-+	hapd = switch_link_hapd(hapd, link_id);
-+	tmp_hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len), link_id);
- 	if (tmp_hapd) {
- 		hapd = tmp_hapd;
- #ifdef CONFIG_IEEE80211BE
-@@ -1899,7 +1908,7 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
- 		if (stype != WLAN_FC_STYPE_ACTION || len <= 25 ||
- 		    buf[24] != WLAN_ACTION_PUBLIC)
- 			return;
--		hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2);
-+		hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2, link_id);
- 		if (!hapd || hapd == HAPD_BROADCAST)
- 			return;
- 		/*
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0009-sync-2024-06-21-openwrt-trunk-src-folder.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0009-sync-2024-06-21-openwrt-trunk-src-folder.patch
new file mode 100644
index 0000000..66a6d8c
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0009-sync-2024-06-21-openwrt-trunk-src-folder.patch
@@ -0,0 +1,5140 @@
+From 2acddad81c876fda04bfd94e7d45dca4bc72a843 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 19 Jul 2024 15:23:02 +0800
+Subject: [PATCH 009/126] sync 2024-06-21 openwrt/trunk src folder
+
+Sync to 032d3fcf7a861b140435b6507b2b0b66361c92f8
+"hostapd: use strdup on string passed to hostapd_add_iface"
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ hostapd/radius.c           |  715 +++++++++++++
+ src/ap/ubus.c              | 2006 ++++++++++++++++++++++++++++++++++++
+ src/ap/ubus.h              |  157 +++
+ src/ap/ucode.c             |  817 +++++++++++++++
+ src/ap/ucode.h             |   54 +
+ src/utils/build_features.h |   65 ++
+ src/utils/ucode.c          |  502 +++++++++
+ src/utils/ucode.h          |   30 +
+ wpa_supplicant/ubus.c      |  280 +++++
+ wpa_supplicant/ubus.h      |   55 +
+ wpa_supplicant/ucode.c     |  299 ++++++
+ wpa_supplicant/ucode.h     |   49 +
+ 12 files changed, 5029 insertions(+)
+ create mode 100644 hostapd/radius.c
+ create mode 100644 src/ap/ubus.c
+ create mode 100644 src/ap/ubus.h
+ create mode 100644 src/ap/ucode.c
+ create mode 100644 src/ap/ucode.h
+ create mode 100644 src/utils/build_features.h
+ create mode 100644 src/utils/ucode.c
+ create mode 100644 src/utils/ucode.h
+ create mode 100644 wpa_supplicant/ubus.c
+ create mode 100644 wpa_supplicant/ubus.h
+ create mode 100644 wpa_supplicant/ucode.c
+ create mode 100644 wpa_supplicant/ucode.h
+
+diff --git a/hostapd/radius.c b/hostapd/radius.c
+new file mode 100644
+index 000000000..362a22c27
+--- /dev/null
++++ b/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/src/ap/ubus.c b/src/ap/ubus.c
+new file mode 100644
+index 000000000..8689494bc
+--- /dev/null
++++ b/src/ap/ubus.c
+@@ -0,0 +1,2006 @@
++/*
++ * 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);
++}
++
++
++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,
++				hostapd_get_punct_bitmap(hapd));
++
++	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;
++
++	if (!ctx)
++		return;
++
++	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/src/ap/ubus.h b/src/ap/ubus.h
+new file mode 100644
+index 000000000..22767d67e
+--- /dev/null
++++ b/src/ap/ubus.h
+@@ -0,0 +1,157 @@
++/*
++ * 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
++
++#include "sta_info.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;
++struct sta_info;
++
++#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/src/ap/ucode.c b/src/ap/ucode.c
+new file mode 100644
+index 000000000..68fb45088
+--- /dev/null
++++ b/src/ap/ucode.c
+@@ -0,0 +1,817 @@
++#include <sys/un.h>
++
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/ucode.h"
++#include "hostapd.h"
++#include "beacon.h"
++#include "hw_features.h"
++#include "ap_drv_ops.h"
++#include "dfs.h"
++#include "acs.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);
++	hapd->ucode.idx = wpa_ucode_registry_add(bss_registry, val);
++
++	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);
++	hapd->ucode.idx = wpa_ucode_registry_add(iface_registry, val);
++
++	return val;
++}
++
++static void
++hostapd_ucode_update_bss_list(struct hostapd_iface *iface, uc_value_t *if_bss, uc_value_t *bss)
++{
++	uc_value_t *list;
++	int i;
++
++	list = ucv_array_new(vm);
++	for (i = 0; iface->bss && i < iface->num_bss; i++) {
++		struct hostapd_data *hapd = iface->bss[i];
++		uc_value_t *val = hostapd_ucode_bss_get_uval(hapd);
++
++		ucv_array_set(list, i, ucv_get(ucv_string_new(hapd->conf->iface)));
++		ucv_object_add(bss, hapd->conf->iface, ucv_get(val));
++	}
++	ucv_object_add(if_bss, iface->phy, ucv_get(list));
++}
++
++static void
++hostapd_ucode_update_interfaces(void)
++{
++	uc_value_t *ifs = ucv_object_new(vm);
++	uc_value_t *if_bss = ucv_array_new(vm);
++	uc_value_t *bss = 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, if_bss, bss);
++	}
++
++	ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
++	ucv_object_add(ucv_prototype_get(global), "interface_bss", ucv_get(if_bss));
++	ucv_object_add(ucv_prototype_get(global), "bss", ucv_get(bss));
++	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);
++	char *data;
++	int ret;
++
++	if (ucv_type(iface) != UC_STRING)
++		return ucv_int64_new(-1);
++
++	data = strdup(ucv_string_get(iface));
++	ret = hostapd_add_iface(interfaces, data);
++	free(data);
++
++	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 struct hostapd_vlan *
++bss_conf_find_vlan(struct hostapd_bss_config *bss, int id)
++{
++	struct hostapd_vlan *vlan;
++
++	for (vlan = bss->vlan; vlan; vlan = vlan->next)
++		if (vlan->vlan_id == id)
++			return vlan;
++
++	return NULL;
++}
++
++static int
++bss_conf_rename_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
++		     const char *ifname)
++{
++	if (!strcmp(ifname, vlan->ifname))
++		return 0;
++
++	hostapd_drv_if_rename(hapd, WPA_IF_AP_VLAN, vlan->ifname, ifname);
++	os_strlcpy(vlan->ifname, ifname, sizeof(vlan->ifname));
++
++	return 0;
++}
++
++static int
++bss_reload_vlans(struct hostapd_data *hapd, struct hostapd_bss_config *bss)
++{
++	struct hostapd_bss_config *old_bss = hapd->conf;
++	struct hostapd_vlan *vlan, *vlan_new, *wildcard;
++	char ifname[IFNAMSIZ + 1], vlan_ifname[IFNAMSIZ + 1], *pos;
++	int ret;
++
++	vlan = bss_conf_find_vlan(old_bss, VLAN_ID_WILDCARD);
++	wildcard = bss_conf_find_vlan(bss, VLAN_ID_WILDCARD);
++	if (!!vlan != !!wildcard)
++		return -1;
++
++	if (vlan && wildcard && strcmp(vlan->ifname, wildcard->ifname) != 0)
++		strcpy(vlan->ifname, wildcard->ifname);
++	else
++		wildcard = NULL;
++
++	for (vlan = bss->vlan; vlan; vlan = vlan->next) {
++		if (vlan->vlan_id == VLAN_ID_WILDCARD ||
++		    vlan->dynamic_vlan > 0)
++			continue;
++
++		if (!bss_conf_find_vlan(old_bss, vlan->vlan_id))
++			return -1;
++	}
++
++	for (vlan = old_bss->vlan; vlan; vlan = vlan->next) {
++		if (vlan->vlan_id == VLAN_ID_WILDCARD)
++			continue;
++
++		if (vlan->dynamic_vlan == 0) {
++			vlan_new = bss_conf_find_vlan(bss, vlan->vlan_id);
++			if (!vlan_new)
++				return -1;
++
++			if (bss_conf_rename_vlan(hapd, vlan, vlan_new->ifname))
++				return -1;
++
++			continue;
++		}
++
++		if (!wildcard)
++			continue;
++
++		os_strlcpy(ifname, wildcard->ifname, sizeof(ifname));
++		pos = os_strchr(ifname, '#');
++		if (!pos)
++			return -1;
++
++		*pos++ = '\0';
++		ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s%d%s",
++				  ifname, vlan->vlan_id, pos);
++	        if (os_snprintf_error(sizeof(vlan_ifname), ret))
++			return -1;
++
++		if (bss_conf_rename_vlan(hapd, vlan, vlan_ifname))
++			return -1;
++	}
++
++	return 0;
++}
++
++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);
++	uc_value_t *files_only = uc_fn_arg(2);
++	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)
++		goto out;
++
++	if (idx > conf->num_bss || !conf->bss[idx])
++		goto free;
++
++	if (ucv_boolean_get(files_only)) {
++		struct hostapd_bss_config *bss = conf->bss[idx];
++		struct hostapd_bss_config *old_bss = hapd->conf;
++
++#define swap_field(name)				\
++	do {								\
++		void *ptr = old_bss->name;		\
++		old_bss->name = bss->name;		\
++		bss->name = ptr;				\
++	} while (0)
++
++		swap_field(ssid.wpa_psk_file);
++		ret = bss_reload_vlans(hapd, bss);
++		goto done;
++	}
++
++	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_setup_bss(hapd, hapd == iface->bss[0], true);
++	hostapd_ucode_update_interfaces();
++
++done:
++	ret = 0;
++free:
++	hostapd_config_free(conf);
++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)
++		return NULL;
++
++	iface = hapd->iface;
++	if (iface->num_bss == 1) {
++		wpa_printf(MSG_ERROR, "trying to delete last bss of an iface: %s\n", hapd->conf->iface);
++		return NULL;
++	}
++
++	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--;
++
++	iface->bss[0]->interface_added = 0;
++	hostapd_drv_set_first_bss(iface->bss[0]);
++	hapd->interface_added = 1;
++
++	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_interfaces();
++	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_interfaces();
++	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_iface_set_bss_order(uc_vm_t *vm, size_t nargs)
++{
++	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++	uc_value_t *bss_list = uc_fn_arg(0);
++	struct hostapd_data **new_bss;
++	struct hostapd_bss_config **new_conf;
++
++	if (!iface)
++		return NULL;
++
++	if (ucv_type(bss_list) != UC_ARRAY ||
++	    ucv_array_length(bss_list) != iface->num_bss)
++		return NULL;
++
++	new_bss = calloc(iface->num_bss, sizeof(*new_bss));
++	new_conf = calloc(iface->num_bss, sizeof(*new_conf));
++	for (size_t i = 0; i < iface->num_bss; i++) {
++		struct hostapd_data *bss;
++
++		bss = ucv_resource_data(ucv_array_get(bss_list, i), "hostapd.bss");
++		if (bss->iface != iface)
++			goto free;
++
++		for (size_t k = 0; k < i; k++)
++			if (new_bss[k] == bss)
++				goto free;
++
++		new_bss[i] = bss;
++		new_conf[i] = bss->conf;
++	}
++
++	new_bss[0]->interface_added = 0;
++	for (size_t i = 1; i < iface->num_bss; i++)
++		new_bss[i]->interface_added = 1;
++
++	free(iface->bss);
++	iface->bss = new_bss;
++
++	free(iface->conf->bss);
++	iface->conf->bss = new_conf;
++	iface->conf->num_bss = iface->num_bss;
++	hostapd_drv_set_first_bss(iface->bss[0]);
++
++	return ucv_boolean_new(true);
++
++free:
++	free(new_bss);
++	free(new_conf);
++	return NULL;
++}
++
++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);
++}
++
++static void
++uc_hostapd_disable_iface(struct hostapd_iface *iface)
++{
++	switch (iface->state) {
++	case HAPD_IFACE_DISABLED:
++		break;
++#ifdef CONFIG_ACS
++	case HAPD_IFACE_ACS:
++		acs_cleanup(iface);
++		iface->scan_cb = NULL;
++		/* fallthrough */
++#endif
++	default:
++		hostapd_disable_iface(iface);
++		break;
++	}
++}
++
++static uc_value_t *
++uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
++{
++	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++	int i;
++
++	if (!iface)
++		return NULL;
++
++	if (iface->state != HAPD_IFACE_ENABLED)
++		uc_hostapd_disable_iface(iface);
++
++	for (i = 0; i < iface->num_bss; i++) {
++		struct hostapd_data *hapd = iface->bss[i];
++
++		hostapd_drv_stop_ap(hapd);
++		hapd->beacon_set_done = 0;
++	}
++
++	return NULL;
++}
++
++static uc_value_t *
++uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
++{
++	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++	uc_value_t *info = uc_fn_arg(0);
++	struct hostapd_config *conf;
++	bool changed = false;
++	uint64_t intval;
++	int i;
++
++	if (!iface)
++		return NULL;
++
++	if (!info) {
++		iface->freq = 0;
++		goto out;
++	}
++
++	if (ucv_type(info) != UC_OBJECT)
++		return NULL;
++
++#define UPDATE_VAL(field, name)							\
++	if ((intval = ucv_int64_get(ucv_object_get(info, name, NULL))) &&	\
++		!errno && intval != conf->field) do {				\
++		conf->field = intval;						\
++		changed = true;							\
++	} while(0)
++
++	conf = iface->conf;
++	UPDATE_VAL(op_class, "op_class");
++	UPDATE_VAL(hw_mode, "hw_mode");
++	UPDATE_VAL(channel, "channel");
++	UPDATE_VAL(secondary_channel, "sec_channel");
++	if (!changed &&
++	    (iface->bss[0]->beacon_set_done ||
++	     iface->state == HAPD_IFACE_DFS))
++		return ucv_boolean_new(true);
++
++	intval = ucv_int64_get(ucv_object_get(info, "center_seg0_idx", NULL));
++	if (!errno)
++		hostapd_set_oper_centr_freq_seg0_idx(conf, intval);
++
++	intval = ucv_int64_get(ucv_object_get(info, "center_seg1_idx", NULL));
++	if (!errno)
++		hostapd_set_oper_centr_freq_seg1_idx(conf, intval);
++
++	intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
++	if (!errno)
++		hostapd_set_oper_chwidth(conf, intval);
++
++	intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL));
++	if (!errno)
++		iface->freq = intval;
++	else
++		iface->freq = 0;
++	conf->acs = 0;
++
++out:
++	switch (iface->state) {
++	case HAPD_IFACE_ENABLED:
++		if (!hostapd_is_dfs_required(iface) ||
++			hostapd_is_dfs_chan_available(iface))
++			break;
++		wpa_printf(MSG_INFO, "DFS CAC required on new channel, restart interface");
++		/* fallthrough */
++	default:
++		uc_hostapd_disable_iface(iface);
++		break;
++	}
++
++	if (conf->channel && !iface->freq)
++		iface->freq = hostapd_hw_get_freq(iface->bss[0], conf->channel);
++
++	if (iface->state != HAPD_IFACE_ENABLED) {
++		hostapd_enable_iface(iface);
++		return ucv_boolean_new(true);
++	}
++
++	for (i = 0; i < iface->num_bss; i++) {
++		struct hostapd_data *hapd = iface->bss[i];
++		int ret;
++
++		hapd->conf->start_disabled = 0;
++		hostapd_set_freq(hapd, conf->hw_mode, iface->freq,
++				 conf->channel,
++				 conf->enable_edmg,
++				 conf->edmg_channel,
++				 conf->ieee80211n,
++				 conf->ieee80211ac,
++				 conf->ieee80211ax,
++				 conf->ieee80211be,
++				 conf->secondary_channel,
++				 hostapd_get_oper_chwidth(conf),
++				 hostapd_get_oper_centr_freq_seg0_idx(conf),
++				 hostapd_get_oper_centr_freq_seg1_idx(conf));
++
++		ieee802_11_set_beacon(hapd);
++	}
++
++	return ucv_boolean_new(true);
++}
++
++static uc_value_t *
++uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
++{
++	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++	uc_value_t *info = uc_fn_arg(0);
++	struct hostapd_config *conf;
++	struct csa_settings csa = {};
++	uint64_t intval;
++	int i, ret = 0;
++
++	if (!iface || ucv_type(info) != UC_OBJECT)
++		return NULL;
++
++	conf = iface->conf;
++	if ((intval = ucv_int64_get(ucv_object_get(info, "csa_count", NULL))) && !errno)
++		csa.cs_count = intval;
++	if ((intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL))) && !errno)
++		csa.freq_params.sec_channel_offset = intval;
++
++	csa.freq_params.ht_enabled = conf->ieee80211n;
++	csa.freq_params.vht_enabled = conf->ieee80211ac;
++	csa.freq_params.he_enabled = conf->ieee80211ax;
++#ifdef CONFIG_IEEE80211BE
++	csa.freq_params.eht_enabled = conf->ieee80211be;
++#endif
++	intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
++	if (errno)
++		intval = hostapd_get_oper_chwidth(conf);
++	if (intval)
++		csa.freq_params.bandwidth = 40 << intval;
++	else
++		csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20;
++
++	if ((intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL))) && !errno)
++		csa.freq_params.freq = intval;
++	if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq1", NULL))) && !errno)
++		csa.freq_params.center_freq1 = intval;
++	if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
++		csa.freq_params.center_freq2 = intval;
++
++	for (i = 0; i < iface->num_bss; i++)
++		ret = hostapd_switch_channel(iface->bss[i], &csa);
++
++	return ucv_boolean_new(!ret);
++}
++
++static uc_value_t *
++uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
++{
++	struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
++	uc_value_t *ifname_arg = uc_fn_arg(0);
++	char prev_ifname[IFNAMSIZ + 1];
++	struct sta_info *sta;
++	const char *ifname;
++	int ret;
++
++	if (!hapd || ucv_type(ifname_arg) != UC_STRING)
++		return NULL;
++
++	os_strlcpy(prev_ifname, hapd->conf->iface, sizeof(prev_ifname));
++	ifname = ucv_string_get(ifname_arg);
++
++	hostapd_ubus_free_bss(hapd);
++	if (interfaces->ctrl_iface_deinit)
++		interfaces->ctrl_iface_deinit(hapd);
++
++	ret = hostapd_drv_if_rename(hapd, WPA_IF_AP_BSS, NULL, ifname);
++	if (ret)
++		goto out;
++
++	for (sta = hapd->sta_list; sta; sta = sta->next) {
++		char cur_name[IFNAMSIZ + 1], new_name[IFNAMSIZ + 1];
++
++		if (!(sta->flags & WLAN_STA_WDS) || sta->pending_wds_enable)
++			continue;
++
++		snprintf(cur_name, sizeof(cur_name), "%s.sta%d", prev_ifname, sta->aid);
++		snprintf(new_name, sizeof(new_name), "%s.sta%d", ifname, sta->aid);
++		hostapd_drv_if_rename(hapd, WPA_IF_AP_VLAN, cur_name, new_name);
++	}
++
++	if (!strncmp(hapd->conf->ssid.vlan, hapd->conf->iface, sizeof(hapd->conf->ssid.vlan)))
++		os_strlcpy(hapd->conf->ssid.vlan, ifname, sizeof(hapd->conf->ssid.vlan));
++	os_strlcpy(hapd->conf->iface, ifname, sizeof(hapd->conf->iface));
++	hostapd_ubus_add_bss(hapd);
++
++	hostapd_ucode_update_interfaces();
++out:
++	if (interfaces->ctrl_iface_init)
++		interfaces->ctrl_iface_init(hapd);
++
++	return ret ? NULL : ucv_boolean_new(true);
++}
++
++
++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 },
++		{ "freq_info", uc_wpa_freq_info },
++		{ "add_iface", uc_hostapd_add_iface },
++		{ "remove_iface", uc_hostapd_remove_iface },
++		{ "udebug_set", uc_wpa_udebug_set },
++	};
++	static const uc_function_list_t bss_fns[] = {
++		{ "ctrl", uc_hostapd_bss_ctrl },
++		{ "set_config", uc_hostapd_bss_set_config },
++		{ "rename", uc_hostapd_bss_rename },
++		{ "delete", uc_hostapd_bss_delete },
++	};
++	static const uc_function_list_t iface_fns[] = {
++		{ "set_bss_order", uc_hostapd_iface_set_bss_order },
++		{ "add_bss", uc_hostapd_iface_add_bss },
++		{ "stop", uc_hostapd_iface_stop },
++		{ "start", uc_hostapd_iface_start },
++		{ "switch_channel", uc_hostapd_iface_switch_channel },
++	};
++	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)
++{
++	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));
++	ucv_put(wpa_ucode_call(2));
++	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/src/ap/ucode.h b/src/ap/ucode.h
+new file mode 100644
+index 000000000..d00b78716
+--- /dev/null
++++ b/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);
++
++#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)
++{
++}
++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/src/utils/build_features.h b/src/utils/build_features.h
+new file mode 100644
+index 000000000..553769ece
+--- /dev/null
++++ b/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/src/utils/ucode.c b/src/utils/ucode.c
+new file mode 100644
+index 000000000..29c753c32
+--- /dev/null
++++ b/src/utils/ucode.c
+@@ -0,0 +1,502 @@
++#include <unistd.h>
++#include "ucode.h"
++#include "utils/eloop.h"
++#include "crypto/crypto.h"
++#include "crypto/sha1.h"
++#include "common/ieee802_11_common.h"
++#include <linux/netlink.h>
++#include <linux/genetlink.h>
++#include <linux/nl80211.h>
++#include <libubox/uloop.h>
++#include <ucode/compiler.h>
++#include <udebug.h>
++
++static uc_value_t *registry;
++static uc_vm_t vm;
++static struct uloop_timeout gc_timer;
++static struct udebug ud;
++static struct udebug_buf ud_log, ud_nl[3];
++static const struct udebug_buf_meta meta_log = {
++	.name = "wpa_log",
++	.format = UDEBUG_FORMAT_STRING,
++};
++static const struct udebug_buf_meta meta_nl_ll = {
++	.name = "wpa_nl_ctrl",
++	.format = UDEBUG_FORMAT_PACKET,
++	.sub_format = UDEBUG_DLT_NETLINK,
++};
++static const struct udebug_buf_meta meta_nl_tx = {
++	.name = "wpa_nl_tx",
++	.format = UDEBUG_FORMAT_PACKET,
++	.sub_format = UDEBUG_DLT_NETLINK,
++};
++#define UDEBUG_FLAG_RX_FRAME	(1ULL << 0)
++static const struct udebug_buf_flag rx_flags[] = {
++	{  "rx_frame", UDEBUG_FLAG_RX_FRAME },
++};
++static const struct udebug_buf_meta meta_nl_rx = {
++	.name = "wpa_nl_rx",
++	.format = UDEBUG_FORMAT_PACKET,
++	.sub_format = UDEBUG_DLT_NETLINK,
++	.flags = rx_flags,
++	.n_flags = ARRAY_SIZE(rx_flags),
++};
++static struct udebug_ubus_ring udebug_rings[] = {
++	{
++		.buf = &ud_log,
++		.meta = &meta_log,
++		.default_entries = 1024,
++		.default_size = 64 * 1024
++	},
++	{
++		.buf = &ud_nl[0],
++		.meta = &meta_nl_rx,
++		.default_entries = 1024,
++		.default_size = 256 * 1024,
++	},
++	{
++		.buf = &ud_nl[1],
++		.meta = &meta_nl_tx,
++		.default_entries = 1024,
++		.default_size = 64 * 1024,
++	},
++	{
++		.buf = &ud_nl[2],
++		.meta = &meta_nl_ll,
++		.default_entries = 1024,
++		.default_size = 32 * 1024,
++	}
++};
++char *udebug_service;
++struct udebug_ubus ud_ubus;
++
++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_freq_info(uc_vm_t *vm, size_t nargs)
++{
++	uc_value_t *freq = uc_fn_arg(0);
++	uc_value_t *sec = uc_fn_arg(1);
++	int width = ucv_uint64_get(uc_fn_arg(2));
++	int freq_val, center_idx, center_ofs;
++	enum oper_chan_width chanwidth;
++	enum hostapd_hw_mode hw_mode;
++	u8 op_class, channel, tmp_channel;
++	const char *modestr;
++	int sec_channel = 0;
++	uc_value_t *ret;
++
++	if (ucv_type(freq) != UC_INTEGER)
++		return NULL;
++
++	freq_val = ucv_int64_get(freq);
++	if (ucv_type(sec) == UC_INTEGER)
++		sec_channel = ucv_int64_get(sec);
++	else if (sec)
++		return NULL;
++	else if (freq_val > 4000)
++		sec_channel = (freq_val / 20) & 1 ? 1 : -1;
++	else
++		sec_channel = freq_val < 2442 ? 1 : -1;
++
++	if (sec_channel != -1 && sec_channel != 1 && sec_channel != 0)
++		return NULL;
++
++	switch (width) {
++	case 0:
++		chanwidth = CONF_OPER_CHWIDTH_USE_HT;
++		break;
++	case 1:
++		chanwidth = CONF_OPER_CHWIDTH_80MHZ;
++		break;
++	case 2:
++		chanwidth = CONF_OPER_CHWIDTH_160MHZ;
++		break;
++	default:
++		return NULL;
++	}
++
++	hw_mode = ieee80211_freq_to_channel_ext(freq_val, sec_channel,
++						chanwidth, &op_class, &channel);
++	switch (hw_mode) {
++	case HOSTAPD_MODE_IEEE80211B:
++		modestr = "b";
++		break;
++	case HOSTAPD_MODE_IEEE80211G:
++		modestr = "g";
++		break;
++	case HOSTAPD_MODE_IEEE80211A:
++		modestr = "a";
++		break;
++	case HOSTAPD_MODE_IEEE80211AD:
++		modestr = "ad";
++		break;
++	default:
++		return NULL;
++	}
++
++	ret = ucv_object_new(vm);
++	ucv_object_add(ret, "op_class", ucv_int64_new(op_class));
++	ucv_object_add(ret, "channel", ucv_int64_new(channel));
++	ucv_object_add(ret, "hw_mode", ucv_int64_new(hw_mode));
++	ucv_object_add(ret, "hw_mode_str", ucv_get(ucv_string_new(modestr)));
++	ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
++	ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
++
++	if (!sec_channel)
++		return ret;
++
++	if (freq_val >= 5900)
++		center_ofs = 0;
++	else if (freq_val >= 5745)
++		center_ofs = 20;
++	else
++		center_ofs = 35;
++	tmp_channel = channel - center_ofs;
++	tmp_channel &= ~((8 << width) - 1);
++	center_idx = tmp_channel + center_ofs + (4 << width) - 1;
++
++	if (freq_val < 3000)
++		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(0));
++	else
++		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(center_idx));
++	center_idx = (center_idx - channel) * 5 + freq_val;
++	ucv_object_add(ret, "center_freq1", ucv_int64_new(center_idx));
++
++out:
++	return ret;
++}
++
++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;
++}
++
++static void udebug_printf_hook(int level, const char *fmt, va_list ap)
++{
++	udebug_entry_init(&ud_log);
++	udebug_entry_vprintf(&ud_log, fmt, ap);
++	udebug_entry_add(&ud_log);
++}
++
++static void udebug_hexdump_hook(int level, const char *title,
++                const void *data, size_t len)
++{
++	char *buf;
++
++	udebug_entry_init(&ud_log);
++	udebug_entry_printf(&ud_log, "%s - hexdump:", title);
++	buf = udebug_entry_append(&ud_log, NULL, 3 * len);
++	for (size_t i = 0; i < len; i++)
++		buf += sprintf(buf, " %02x", *(uint8_t *)(data + i));
++	udebug_entry_add(&ud_log);
++}
++
++static void udebug_netlink_hook(int tx, const void *data, size_t len)
++{
++	struct {
++		uint16_t pkttype;
++		uint16_t arphdr;
++		uint16_t _pad[5];
++		uint16_t proto;
++	} hdr = {
++		.pkttype = host_to_be16(tx ? 7 : 6),
++		.arphdr = host_to_be16(824),
++		.proto = host_to_be16(16),
++	};
++	const struct nlmsghdr *nlh = data;
++	const struct genlmsghdr *gnlh = data + NLMSG_HDRLEN;
++	struct udebug_buf *buf = &ud_nl[!!tx];
++
++	if (nlh->nlmsg_type == 0x10)
++		buf = &ud_nl[2];
++	else if (!tx && gnlh->cmd == NL80211_CMD_FRAME &&
++	         !(udebug_buf_flags(buf) & UDEBUG_FLAG_RX_FRAME))
++		return;
++
++	if (!udebug_buf_valid(buf))
++		return;
++
++	udebug_entry_init(buf);
++	udebug_entry_append(buf, &hdr, sizeof(hdr));
++	udebug_entry_append(buf, data, len);
++	udebug_entry_add(buf);
++}
++
++static void
++wpa_udebug_config(struct udebug_ubus *ctx, struct blob_attr *data,
++		  bool enabled)
++{
++	udebug_ubus_apply_config(&ud, udebug_rings, ARRAY_SIZE(udebug_rings),
++				 data, enabled);
++
++	if (udebug_buf_valid(&ud_log)) {
++		wpa_printf_hook = udebug_printf_hook;
++		wpa_hexdump_hook = udebug_hexdump_hook;
++	} else {
++		wpa_printf_hook = NULL;
++		wpa_hexdump_hook = NULL;
++	}
++
++	if (udebug_buf_valid(&ud_nl[0]) ||
++	    udebug_buf_valid(&ud_nl[1]) ||
++	    udebug_buf_valid(&ud_nl[2]))
++		wpa_netlink_hook = udebug_netlink_hook;
++	else
++		wpa_netlink_hook = NULL;
++}
++
++uc_value_t *uc_wpa_udebug_set(uc_vm_t *vm, size_t nargs)
++{
++	uc_value_t *name = uc_fn_arg(0);
++	uc_value_t *ubus = uc_fn_arg(1);
++	static bool enabled = false;
++	struct ubus_context *ctx;
++	bool cur_en;
++
++	cur_en = ucv_type(name) == UC_STRING;
++	ctx = ucv_resource_data(ubus, "ubus.connection");
++	if (!ctx)
++		cur_en = false;
++
++	if (enabled == cur_en)
++		return ucv_boolean_new(true);
++
++	enabled = cur_en;
++	if (enabled) {
++		udebug_service = strdup(ucv_string_get(name));
++		udebug_init(&ud);
++		udebug_auto_connect(&ud, NULL);
++		udebug_ubus_init(&ud_ubus, ctx, udebug_service, wpa_udebug_config);
++	} else {
++		udebug_ubus_free(&ud_ubus);
++		for (size_t i = 0; i < ARRAY_SIZE(udebug_rings); i++)
++			if (udebug_buf_valid(udebug_rings[i].buf))
++				udebug_buf_free(udebug_rings[i].buf);
++		udebug_free(&ud);
++		free(udebug_service);
++	}
++
++	return ucv_boolean_new(true);
++}
++
++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;
++}
++
++int wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val)
++{
++	uc_value_t *data;
++	int i = 0;
++
++	while (ucv_array_get(reg, i))
++		i++;
++
++	ucv_array_set(reg, i, ucv_get(val));
++
++	return 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);
++	void **dataptr;
++
++	if (!val)
++		return NULL;
++
++	ucv_array_set(reg, idx - 1, NULL);
++	dataptr = ucv_resource_dataptr(val, NULL);
++	if (dataptr)
++		*dataptr = 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/src/utils/ucode.h b/src/utils/ucode.h
+new file mode 100644
+index 000000000..c083241e0
+--- /dev/null
++++ b/src/utils/ucode.h
+@@ -0,0 +1,30 @@
++#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);
++
++int wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val);
++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_udebug_set(uc_vm_t *vm, size_t nargs);
++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);
++uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs);
++
++#endif
+diff --git a/wpa_supplicant/ubus.c b/wpa_supplicant/ubus.c
+new file mode 100644
+index 000000000..1c477f0c0
+--- /dev/null
++++ b/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/wpa_supplicant/ubus.h b/wpa_supplicant/ubus.h
+new file mode 100644
+index 000000000..f6681cb26
+--- /dev/null
++++ b/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/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+new file mode 100644
+index 000000000..397f85bde
+--- /dev/null
++++ b/wpa_supplicant/ucode.c
+@@ -0,0 +1,299 @@
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/ucode.h"
++#include "drivers/driver.h"
++#include "ap/hostapd.h"
++#include "wpa_supplicant_i.h"
++#include "wps_supplicant.h"
++#include "bss.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_s->ucode.idx = wpa_ucode_registry_add(iface_registry, val);
++
++	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);
++}
++
++void wpas_ucode_update_state(struct wpa_supplicant *wpa_s)
++{
++	const char *state;
++	uc_value_t *val;
++
++	val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
++	if (!val)
++		return;
++
++	if (wpa_ucode_call_prepare("state"))
++		return;
++
++	state = wpa_supplicant_state_txt(wpa_s->wpa_state);
++	uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
++	uc_value_push(ucv_get(val));
++	uc_value_push(ucv_get(ucv_string_new(state)));
++	ucv_put(wpa_ucode_call(3));
++	ucv_gc(vm);
++}
++
++void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data)
++{
++	const char *state;
++	uc_value_t *val;
++
++	if (event != EVENT_CH_SWITCH_STARTED)
++		return;
++
++	val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
++	if (!val)
++		return;
++
++	if (wpa_ucode_call_prepare("event"))
++		return;
++
++	uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
++	uc_value_push(ucv_get(val));
++	uc_value_push(ucv_get(ucv_string_new(event_to_string(event))));
++	val = ucv_object_new(vm);
++	uc_value_push(ucv_get(val));
++
++	if (event == EVENT_CH_SWITCH_STARTED) {
++		ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count));
++		ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq));
++		ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
++		ucv_object_add(val, "center_freq1", ucv_int64_new(data->ch_switch.cf1));
++		ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
++	}
++
++	ucv_put(wpa_ucode_call(4));
++	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 *driver = ucv_object_get(info, "driver", NULL);
++	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);
++	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),
++	};
++
++	if (driver) {
++		const char *drvname;
++		if (ucv_type(driver) != UC_STRING)
++			goto out;
++
++		iface.driver = NULL;
++		drvname = ucv_string_get(driver);
++		for (int i = 0; wpa_drivers[i]; i++) {
++			if (!strcmp(drvname, wpa_drivers[i]->name))
++				iface.driver = wpa_drivers[i]->name;
++		}
++
++		if (!iface.driver)
++			goto out;
++	}
++
++	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);
++}
++
++static uc_value_t *
++uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
++{
++	struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
++	struct wpa_bss *bss;
++	uc_value_t *ret, *val;
++
++	if (!wpa_s)
++		return NULL;
++
++	ret = ucv_object_new(vm);
++
++	val = ucv_string_new(wpa_supplicant_state_txt(wpa_s->wpa_state));
++	ucv_object_add(ret, "state", ucv_get(val));
++
++	bss = wpa_s->current_bss;
++	if (bss) {
++		int sec_chan = 0;
++		const u8 *ie;
++
++		ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
++		if (ie && ie[1] >= 2) {
++			const struct ieee80211_ht_operation *ht_oper;
++			int sec;
++
++			ht_oper = (const void *) (ie + 2);
++			sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
++			if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
++				sec_chan = 1;
++			else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
++				sec_chan = -1;
++		}
++
++		ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
++		ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
++	}
++
++#ifdef CONFIG_MESH
++	if (wpa_s->ifmsh) {
++		struct hostapd_iface *ifmsh = wpa_s->ifmsh;
++
++		ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(ifmsh->conf->secondary_channel));
++		ucv_object_add(ret, "frequency", ucv_int64_new(ifmsh->freq));
++	}
++#endif
++
++	return 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 },
++		{ "udebug_set", uc_wpa_udebug_set },
++	};
++	static const uc_function_list_t iface_fns[] = {
++		{ "status", uc_wpas_iface_status },
++	};
++	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, "wpas.iface", iface_fns, NULL);
++
++	iface_registry = ucv_array_new(vm);
++	uc_vm_registry_set(vm, "wpas.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/wpa_supplicant/ucode.h b/wpa_supplicant/ucode.h
+new file mode 100644
+index 000000000..a429a0ed8
+--- /dev/null
++++ b/wpa_supplicant/ucode.h
+@@ -0,0 +1,49 @@
++#ifndef __WPAS_UCODE_H
++#define __WPAS_UCODE_H
++
++#include "utils/ucode.h"
++
++struct wpa_global;
++union wpa_event_data;
++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);
++void wpas_ucode_update_state(struct wpa_supplicant *wpa_s);
++void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data);
++#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)
++{
++}
++
++static inline void wpas_ucode_update_state(struct wpa_supplicant *wpa_s)
++{
++}
++
++static inline void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data)
++{
++}
++
++#endif
++
++#endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0010-hostapd-MLO-pass-ctx-in-mlme_event_mgmt.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0010-hostapd-MLO-pass-ctx-in-mlme_event_mgmt.patch
deleted file mode 100644
index de6b62e..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0010-hostapd-MLO-pass-ctx-in-mlme_event_mgmt.patch
+++ /dev/null
@@ -1,64 +0,0 @@
-From 33c9e4624040e1e0f331260c239fccdccbe52528 Mon Sep 17 00:00:00 2001
-From: Sriram R <quic_srirrama@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:40 +0530
-Subject: [PATCH 010/104] hostapd: MLO: pass ctx in mlme_event_mgmt()
-
-Add support to pass ctx in mlme_event_mgmt(). This will help in to route
-the event properly to link BSS.
-
-Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/drv_callbacks.c             | 2 +-
- src/drivers/driver.h               | 8 ++++++++
- src/drivers/driver_nl80211_event.c | 1 +
- 3 files changed, 10 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index adac2d478..3b24aa4f4 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -1810,8 +1810,8 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
- 	const u8 *bssid;
- 	struct hostapd_frame_info fi;
- 	int ret;
--	bool is_mld = false;
- 
-+	hapd = rx_mgmt->ctx ? rx_mgmt->ctx : hapd;
- 	hapd = switch_link_hapd(hapd, rx_mgmt->link_id);
- 	iface = hapd->iface;
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index d67c949b6..a7455ef6e 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -6345,6 +6345,14 @@ union wpa_event_data {
- 		 */
- 		void *drv_priv;
- 
-+		/**
-+		 * ctx - Pointer to store ctx of private BSS information
-+		 *
-+		 * If not set to NULL, this is used for forwarding the packet
-+		 * to right link BSS of ML BSS.
-+		 */
-+		void *ctx;
-+
- 		/**
- 		 * freq - Frequency (in MHz) on which the frame was received
- 		 */
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 51b27bd5e..1ca8b5bce 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -1367,6 +1367,7 @@ static void mlme_event_mgmt(struct i802_bss *bss,
- 	event.rx_mgmt.frame_len = len;
- 	event.rx_mgmt.ssi_signal = ssi_signal;
- 	event.rx_mgmt.drv_priv = bss;
-+	event.rx_mgmt.ctx = bss->ctx;
- 	event.rx_mgmt.link_id = link_id;
- 
- 	wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0010-sync-2024-08-13-openwrt-trunk-patches-folder.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0010-sync-2024-08-13-openwrt-trunk-patches-folder.patch
new file mode 100644
index 0000000..6fe601b
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0010-sync-2024-08-13-openwrt-trunk-patches-folder.patch
@@ -0,0 +1,14492 @@
+From 1a9b003e08a15df0e52604b991c1dd851a9bd265 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 19 Jul 2024 15:26:42 +0800
+Subject: [PATCH 010/126] sync 2024-08-13 openwrt/trunk patches folder
+
+Sync to e80520197c9ca7bced50d3605d6baba6dead6e35
+"hostapd: Add support for APuP"
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ hostapd/Makefile                          |  153 +-
+ hostapd/config_file.c                     |   42 +-
+ hostapd/ctrl_iface.c                      |    4 +
+ hostapd/defconfig                         |   15 +-
+ hostapd/hostapd_cli.c                     |   10 +-
+ hostapd/main.c                            |   21 +-
+ src/ap/acs.c                              |    5 +-
+ src/ap/airtime_policy.c                   |   15 +-
+ src/ap/ap_config.h                        |   37 +
+ src/ap/ap_drv_ops.c                       |   28 +-
+ src/ap/ap_drv_ops.h                       |   24 +-
+ src/ap/apup.c                             |  168 +
+ src/ap/apup.h                             |   24 +
+ src/ap/beacon.c                           |   16 +-
+ src/ap/ctrl_iface_ap.c                    |   45 +-
+ src/ap/dfs.c                              |   22 +-
+ src/ap/drv_callbacks.c                    |   16 +-
+ src/ap/hostapd.c                          |   59 +-
+ src/ap/hostapd.h                          |   41 +
+ src/ap/hw_features.c                      |    3 +-
+ src/ap/ieee802_11.c                       |   55 +-
+ src/ap/ieee802_11.h                       |    2 +
+ src/ap/ieee802_11_ht.c                    |   15 +
+ src/ap/ieee802_11_vht.c                   |   17 +
+ src/ap/ieee802_1x.c                       |    6 +
+ src/ap/rrm.c                              |   10 +
+ src/ap/sta_info.c                         |   35 +-
+ src/ap/sta_info.h                         |   16 +-
+ src/ap/ubus.c                             |   15 +
+ src/ap/ubus.h                             |    5 +
+ src/ap/ucode.c                            |   17 +
+ src/ap/ucode.h                            |    4 +
+ src/ap/vlan_full.c                        |    4 +
+ src/ap/vlan_init.c                        |    8 +-
+ src/ap/wnm_ap.c                           |   16 +-
+ src/ap/wpa_auth.c                         |    3 +-
+ src/ap/wpa_auth_glue.c                    |    9 +-
+ src/ap/wps_hostapd.c                      |    6 +-
+ src/ap/x_snoop.c                          |   22 +-
+ src/common/defs.h                         |    4 +
+ src/common/dpp_crypto.c                   |   10 +-
+ src/common/hw_features_common.c           |    1 +
+ src/common/ieee802_11_defs.h              |    2 +
+ src/common/sae.c                          |    7 +
+ src/common/wpa_ctrl.c                     |   10 +-
+ src/crypto/Makefile                       |  129 +-
+ src/crypto/crypto_mbedtls.c               | 4228 +++++++++++++++++++++
+ src/crypto/crypto_module_tests.c          |  134 +
+ src/crypto/tls_mbedtls.c                  | 3313 ++++++++++++++++
+ src/drivers/driver.h                      |   38 +-
+ src/drivers/driver_nl80211.c              |  174 +-
+ src/drivers/driver_nl80211_capa.c         |    4 +
+ src/drivers/driver_nl80211_event.c        |    5 +
+ src/drivers/driver_nl80211_scan.c         |    2 +-
+ src/drivers/drivers.c                     |    4 +
+ src/drivers/drivers.mak                   |    4 +-
+ src/drivers/rfkill.h                      |   16 +
+ src/radius/radius_client.c                |   34 +
+ src/radius/radius_client.h                |    2 +
+ src/radius/radius_das.c                   |  201 +-
+ src/radius/radius_das.h                   |    1 +
+ src/radius/radius_server.c                |   61 +-
+ src/rsn_supp/wpa.c                        |    3 +
+ src/tls/Makefile                          |   11 +
+ src/utils/eloop.c                         |   20 +-
+ src/utils/eloop.h                         |    8 +
+ src/utils/uloop.c                         |   64 +
+ src/utils/wpa_debug.c                     |   49 +-
+ src/utils/wpa_debug.h                     |   73 +-
+ tests/Makefile                            |   76 +-
+ tests/hwsim/example-hostapd.config        |    8 +-
+ tests/hwsim/example-wpa_supplicant.config |    9 +-
+ tests/hwsim/test_ap_eap.py                |  114 +-
+ tests/hwsim/test_ap_ft.py                 |    4 +-
+ tests/hwsim/test_authsrv.py               |    9 +-
+ tests/hwsim/test_dpp.py                   |   19 +-
+ tests/hwsim/test_erp.py                   |   16 +-
+ tests/hwsim/test_fils.py                  |    4 +
+ tests/hwsim/test_pmksa_cache.py           |    4 +-
+ tests/hwsim/test_sae.py                   |    7 +
+ tests/hwsim/test_suite_b.py               |    3 +
+ tests/hwsim/test_wpas_ctrl.py             |    2 +-
+ tests/hwsim/utils.py                      |    8 +-
+ tests/test-crypto_module.c                |   16 +
+ tests/test-https.c                        |   12 +-
+ tests/test-https_server.c                 |   12 +-
+ wpa_supplicant/Makefile                   |  143 +-
+ wpa_supplicant/ap.c                       |   28 +-
+ wpa_supplicant/config.c                   |   95 +
+ wpa_supplicant/config_file.c              |    8 +-
+ wpa_supplicant/config_ssid.h              |    5 +
+ wpa_supplicant/ctrl_iface.c               |   12 +-
+ wpa_supplicant/defconfig                  |    6 +-
+ wpa_supplicant/eapol_test.c               |   11 +
+ wpa_supplicant/events.c                   |    7 +-
+ wpa_supplicant/main.c                     |   14 +-
+ wpa_supplicant/mesh.c                     |    3 +
+ wpa_supplicant/wpa_cli.c                  |    9 +
+ wpa_supplicant/wpa_priv.c                 |    8 +-
+ wpa_supplicant/wpa_supplicant.c           |   76 +-
+ wpa_supplicant/wpa_supplicant_i.h         |    6 +
+ wpa_supplicant/wps_supplicant.c           |    3 +
+ wpa_supplicant/wps_supplicant.h           |    3 +-
+ 103 files changed, 9984 insertions(+), 401 deletions(-)
+ create mode 100644 src/ap/apup.c
+ create mode 100644 src/ap/apup.h
+ create mode 100644 src/crypto/crypto_mbedtls.c
+ create mode 100644 src/crypto/tls_mbedtls.c
+ create mode 100644 src/utils/uloop.c
+ create mode 100644 tests/test-crypto_module.c
+
+diff --git a/hostapd/Makefile b/hostapd/Makefile
+index 78171025e..8dc6e6216 100644
+--- a/hostapd/Makefile
++++ b/hostapd/Makefile
+@@ -1,6 +1,7 @@
+ ALL=hostapd hostapd_cli
+ CONFIG_FILE = .config
+ 
++-include $(if $(MULTICALL), ../wpa_supplicant/.config)
+ include ../src/build.rules
+ 
+ ifdef LIBS
+@@ -62,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
+@@ -174,6 +179,24 @@ OBJS += ../src/common/hw_features_common.o
+ 
+ OBJS += ../src/eapol_auth/eapol_auth_sm.o
+ 
++ifdef CONFIG_UBUS
++CFLAGS += -DUBUS_SUPPORT
++OBJS += ../src/ap/ubus.o
++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
+ CFLAGS += -O0 -fprofile-arcs -ftest-coverage -U_FORTIFY_SOURCE
+@@ -208,7 +231,8 @@ endif
+ 
+ ifdef CONFIG_NO_VLAN
+ CFLAGS += -DCONFIG_NO_VLAN
+-else
++endif
++ifneq ($(findstring CONFIG_NO_VLAN,$(CFLAGS)), CONFIG_NO_VLAN)
+ OBJS += ../src/ap/vlan_init.o
+ OBJS += ../src/ap/vlan_ifconfig.o
+ OBJS += ../src/ap/vlan.o
+@@ -228,6 +252,9 @@ endif
+ ifdef CONFIG_NO_CTRL_IFACE
+ CFLAGS += -DCONFIG_NO_CTRL_IFACE
+ else
++ifdef CONFIG_CTRL_IFACE_MIB
++CFLAGS += -DCONFIG_CTRL_IFACE_MIB
++endif
+ ifeq ($(CONFIG_CTRL_IFACE), udp)
+ CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+ else
+@@ -367,10 +394,14 @@ CFLAGS += -DCONFIG_MBO
+ OBJS += ../src/ap/mbo_ap.o
+ endif
+ 
++ifndef MULTICALL
++CFLAGS += -DNO_SUPPLICANT
++endif
++
+ include ../src/drivers/drivers.mak
+-OBJS += $(DRV_AP_OBJS)
+-CFLAGS += $(DRV_AP_CFLAGS)
+-LDFLAGS += $(DRV_AP_LDFLAGS)
++OBJS += $(sort $(DRV_AP_OBJS) $(if $(MULTICALL),$(DRV_WPA_OBJS)))
++CFLAGS += $(DRV_AP_CFLAGS) $(if $(MULTICALL),$(DRV_WPA_CFLAGS))
++LDFLAGS += $(DRV_AP_LDFLAGS) $(if $(MULTICALL),$(DRV_WPA_LDFLAGS))
+ LIBS += $(DRV_AP_LIBS)
+ 
+ ifdef CONFIG_L2_PACKET
+@@ -716,6 +747,7 @@ CFLAGS += -DCONFIG_TLSV12
+ endif
+ 
+ ifeq ($(CONFIG_TLS), wolfssl)
++CFLAGS += -DCONFIG_TLS_WOLFSSL
+ CONFIG_CRYPTO=wolfssl
+ ifdef TLS_FUNCS
+ OBJS += ../src/crypto/tls_wolfssl.o
+@@ -736,6 +768,7 @@ endif
+ endif
+ 
+ ifeq ($(CONFIG_TLS), openssl)
++CFLAGS += -DCONFIG_TLS_OPENSSL
+ CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
+ CONFIG_CRYPTO=openssl
+ ifdef TLS_FUNCS
+@@ -765,7 +798,39 @@ endif
+ CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
+ endif
+ 
++ifeq ($(CONFIG_TLS), mbedtls)
++CFLAGS += -DCONFIG_TLS_MBEDTLS
++ifndef CONFIG_CRYPTO
++CONFIG_CRYPTO=mbedtls
++endif
++ifdef TLS_FUNCS
++OBJS += ../src/crypto/tls_mbedtls.o
++LIBS += -lmbedtls
++ifndef CONFIG_DPP
++LIBS += -lmbedx509
++endif
++endif
++OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++HOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++SOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++ifeq ($(CONFIG_CRYPTO), mbedtls)
++ifdef CONFIG_DPP
++LIBS += -lmbedx509
++LIBS_h += -lmbedx509
++LIBS_n += -lmbedx509
++LIBS_s += -lmbedx509
++endif
++LIBS += -lmbedcrypto
++LIBS_h += -lmbedcrypto
++LIBS_n += -lmbedcrypto
++LIBS_s += -lmbedcrypto
++# XXX: create a config option?
++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
++endif
++endif
++
+ ifeq ($(CONFIG_TLS), gnutls)
++CFLAGS += -DCONFIG_TLS_GNUTLS
+ ifndef CONFIG_CRYPTO
+ # default to libgcrypt
+ CONFIG_CRYPTO=gnutls
+@@ -796,6 +861,7 @@ endif
+ endif
+ 
+ ifeq ($(CONFIG_TLS), internal)
++CFLAGS += -DCONFIG_TLS_INTERNAL
+ ifndef CONFIG_CRYPTO
+ CONFIG_CRYPTO=internal
+ endif
+@@ -874,6 +940,7 @@ endif
+ endif
+ 
+ ifeq ($(CONFIG_TLS), linux)
++CFLAGS += -DCONFIG_TLS_INTERNAL
+ OBJS += ../src/crypto/crypto_linux.o
+ ifdef TLS_FUNCS
+ OBJS += ../src/crypto/crypto_internal-rsa.o
+@@ -944,9 +1011,11 @@ endif
+ 
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-wrap.o
+ endif
+ endif
++endif
+ ifdef NEED_AES_EAX
+ AESOBJS += ../src/crypto/aes-eax.o
+ NEED_AES_CTR=y
+@@ -956,38 +1025,48 @@ AESOBJS += ../src/crypto/aes-siv.o
+ NEED_AES_CTR=y
+ endif
+ ifdef NEED_AES_CTR
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-ctr.o
+ endif
++endif
+ ifdef NEED_AES_ENCBLOCK
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-encblock.o
+ endif
++endif
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-omac1.o
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_UNWRAP
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ NEED_AES_DEC=y
+ AESOBJS += ../src/crypto/aes-unwrap.o
+ endif
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_CBC
+ NEED_AES_DEC=y
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-cbc.o
+ endif
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_DEC
+ ifdef CONFIG_INTERNAL_AES
+ AESOBJS += ../src/crypto/aes-internal-dec.o
+@@ -1002,12 +1081,16 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-prf.o
++endif
+ ifdef CONFIG_INTERNAL_SHA1
+ SHA1OBJS += ../src/crypto/sha1-internal.o
+ ifdef NEED_FIPS186_2_PRF
+@@ -1016,16 +1099,22 @@ endif
+ endif
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
+ endif
+ endif
++endif
+ ifdef NEED_T_PRF
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-tprf.o
+ endif
++endif
+ ifdef NEED_TLS_PRF
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-tlsprf.o
+ endif
+ endif
++endif
+ 
+ ifdef NEED_SHA1
+ OBJS += $(SHA1OBJS)
+@@ -1035,11 +1124,13 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/md5.o
+ endif
+ endif
+ endif
+ endif
++endif
+ 
+ ifdef NEED_MD5
+ ifdef CONFIG_INTERNAL_MD5
+@@ -1078,56 +1169,81 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256-prf.o
++endif
+ ifdef CONFIG_INTERNAL_SHA256
+ OBJS += ../src/crypto/sha256-internal.o
+ endif
+ ifdef NEED_TLS_PRF_SHA256
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256-tlsprf.o
+ endif
++endif
+ ifdef NEED_TLS_PRF_SHA384
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-tlsprf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA256_KDF
++CFLAGS += -DCONFIG_HMAC_SHA256_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256-kdf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA384_KDF
++CFLAGS += -DCONFIG_HMAC_SHA384_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-kdf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA512_KDF
++CFLAGS += -DCONFIG_HMAC_SHA512_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512-kdf.o
+ endif
++endif
+ ifdef NEED_SHA384
+ CFLAGS += -DCONFIG_SHA384
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-prf.o
+ endif
++endif
+ ifdef NEED_SHA512
+ CFLAGS += -DCONFIG_SHA512
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512-prf.o
+ endif
++endif
+ 
+ ifdef CONFIG_INTERNAL_SHA384
+ CFLAGS += -DCONFIG_INTERNAL_SHA384
+@@ -1172,11 +1288,13 @@ HOBJS += $(SHA1OBJS)
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ HOBJS += ../src/crypto/md5.o
+ endif
+ endif
+ endif
+ endif
++endif
+ 
+ ifdef CONFIG_RADIUS_SERVER
+ CFLAGS += -DRADIUS_SERVER
+@@ -1306,6 +1424,11 @@ ifdef CONFIG_NO_TKIP
+ CFLAGS += -DCONFIG_NO_TKIP
+ endif
+ 
++ifdef CONFIG_APUP
++CFLAGS += -DCONFIG_APUP
++OBJS += ../src/ap/apup.o
++endif
++
+ $(DESTDIR)$(BINDIR)/%: %
+ 	install -D $(<) $(@)
+ 
+@@ -1314,8 +1437,14 @@ install: $(addprefix $(DESTDIR)$(BINDIR)/,$(ALL))
+ _OBJS_VAR := OBJS
+ include ../src/objs.mk
+ 
++hostapd_multi.a: $(BCHECK) $(OBJS)
++	$(Q)$(CC) -c -o hostapd_multi.o -Dmain=hostapd_main $(CFLAGS) main.c
++	@$(E) "  CC " $<
++	@rm -f $@
++	@$(AR) cr $@ hostapd_multi.o $(OBJS)
++
+ hostapd: $(OBJS)
+-	$(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
++	+$(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
+ 	@$(E) "  LD " $@
+ 
+ ifdef CONFIG_WPA_TRACE
+@@ -1326,7 +1455,7 @@ _OBJS_VAR := OBJS_c
+ include ../src/objs.mk
+ 
+ hostapd_cli: $(OBJS_c)
+-	$(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
++	+$(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
+ 	@$(E) "  LD " $@
+ 
+ NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS)
+@@ -1350,7 +1479,9 @@ NOBJS += ../src/utils/trace.o
+ endif
+ 
+ HOBJS += hlr_auc_gw.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).o ../src/utils/wpabuf.o ../src/crypto/milenage.o
++ifneq ($(CONFIG_TLS), mbedtls)
+ HOBJS += ../src/crypto/aes-encblock.o
++endif
+ ifdef CONFIG_INTERNAL_AES
+ HOBJS += ../src/crypto/aes-internal.o
+ HOBJS += ../src/crypto/aes-internal-enc.o
+@@ -1373,13 +1504,17 @@ SOBJS += ../src/common/sae.o
+ SOBJS += ../src/common/sae_pk.o
+ SOBJS += ../src/common/dragonfly.o
+ SOBJS += $(AESOBJS)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SOBJS += ../src/crypto/sha256-prf.o
+ SOBJS += ../src/crypto/sha384-prf.o
+ SOBJS += ../src/crypto/sha512-prf.o
++endif
+ SOBJS += ../src/crypto/dh_groups.o
++ifneq ($(CONFIG_TLS), mbedtls)
+ SOBJS += ../src/crypto/sha256-kdf.o
+ SOBJS += ../src/crypto/sha384-kdf.o
+ SOBJS += ../src/crypto/sha512-kdf.o
++endif
+ 
+ _OBJS_VAR := NOBJS
+ include ../src/objs.mk
+@@ -1388,6 +1523,12 @@ include ../src/objs.mk
+ _OBJS_VAR := SOBJS
+ include ../src/objs.mk
+ 
++dump_cflags:
++	@printf "%s " "$(CFLAGS)"
++
++dump_ldflags:
++	@printf "%s " "$(LDFLAGS) $(LIBS) $(EXTRALIBS)"
++
+ nt_password_hash: $(NOBJS)
+ 	$(Q)$(CC) $(LDFLAGS) -o nt_password_hash $(NOBJS) $(LIBS_n)
+ 	@$(E) "  LD " $@
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index a86621ed9..f3968ec95 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -1229,6 +1229,8 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf,
+ 		conf->vht_capab |= VHT_CAP_RX_ANTENNA_PATTERN;
+ 	if (os_strstr(capab, "[TX-ANTENNA-PATTERN]"))
+ 		conf->vht_capab |= VHT_CAP_TX_ANTENNA_PATTERN;
++	if (os_strstr(capab, "[EXT-NSS-BW-SUPP]"))
++		conf->vht_capab |= VHT_CAP_EXTENDED_NSS_BW_SUPPORT;
+ 	return 0;
+ }
+ #endif /* CONFIG_IEEE80211AC */
+@@ -2654,8 +2656,12 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 			   sizeof(conf->bss[0]->iface));
+ 	} else if (os_strcmp(buf, "bridge") == 0) {
+ 		os_strlcpy(bss->bridge, pos, sizeof(bss->bridge));
++		if (!bss->wds_bridge[0])
++			os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge));
+ 	} else if (os_strcmp(buf, "bridge_hairpin") == 0) {
+ 		bss->bridge_hairpin = atoi(pos);
++	} else if (os_strcmp(buf, "snoop_iface") == 0) {
++		os_strlcpy(bss->snoop_iface, pos, sizeof(bss->snoop_iface));
+ 	} else if (os_strcmp(buf, "vlan_bridge") == 0) {
+ 		os_strlcpy(bss->vlan_bridge, pos, sizeof(bss->vlan_bridge));
+ 	} else if (os_strcmp(buf, "wds_bridge") == 0) {
+@@ -3043,6 +3049,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 	} else if (os_strcmp(buf, "iapp_interface") == 0) {
+ 		wpa_printf(MSG_INFO, "DEPRECATED: iapp_interface not used");
+ #endif /* CONFIG_IAPP */
++	} else if (os_strcmp(buf, "dynamic_own_ip_addr") == 0) {
++		bss->dynamic_own_ip_addr = atoi(pos);
+ 	} else if (os_strcmp(buf, "own_ip_addr") == 0) {
+ 		if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) {
+ 			wpa_printf(MSG_ERROR,
+@@ -3270,6 +3278,14 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 				   line, bss->max_num_sta, MAX_STA_COUNT);
+ 			return 1;
+ 		}
++	} else if (os_strcmp(buf, "iface_max_num_sta") == 0) {
++		conf->max_num_sta = atoi(pos);
++		if (conf->max_num_sta < 0 ||
++		    conf->max_num_sta > MAX_STA_COUNT) {
++			wpa_printf(MSG_ERROR, "Line %d: Invalid max_num_sta=%d; allowed range 0..%d",
++				   line, conf->max_num_sta, MAX_STA_COUNT);
++			return 1;
++		}
+ 	} else if (os_strcmp(buf, "wpa") == 0) {
+ 		bss->wpa = atoi(pos);
+ 	} else if (os_strcmp(buf, "extended_key_id") == 0) {
+@@ -3459,6 +3475,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		wpa_printf(MSG_INFO,
+ 			   "Line %d: Obsolete peerkey parameter ignored", line);
+ #ifdef CONFIG_IEEE80211R_AP
++	} else if (os_strcmp(buf, "ft_iface") == 0) {
++		os_strlcpy(bss->ft_iface, pos, sizeof(bss->ft_iface));
+ 	} else if (os_strcmp(buf, "mobility_domain") == 0) {
+ 		if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
+ 		    hexstr2bin(pos, bss->mobility_domain,
+@@ -3828,6 +3846,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ #ifndef CONFIG_NO_VLAN
+ 	} else if (os_strcmp(buf, "dynamic_vlan") == 0) {
+ 		bss->ssid.dynamic_vlan = atoi(pos);
++	} else if (os_strcmp(buf, "vlan_no_bridge") == 0) {
++		bss->ssid.vlan_no_bridge = atoi(pos);
+ 	} else if (os_strcmp(buf, "per_sta_vif") == 0) {
+ 		bss->ssid.per_sta_vif = atoi(pos);
+ 	} else if (os_strcmp(buf, "vlan_file") == 0) {
+@@ -3929,6 +3949,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		if (bss->ocv && !bss->ieee80211w)
+ 			bss->ieee80211w = 1;
+ #endif /* CONFIG_OCV */
++	} else if (os_strcmp(buf, "noscan") == 0) {
++		conf->noscan = atoi(pos);
++	} else if (os_strcmp(buf, "ht_coex") == 0) {
++		conf->no_ht_coex = !atoi(pos);
+ 	} else if (os_strcmp(buf, "ieee80211n") == 0) {
+ 		conf->ieee80211n = atoi(pos);
+ 	} else if (os_strcmp(buf, "ht_capab") == 0) {
+@@ -3979,6 +4003,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 	} else if (os_strcmp(buf, "he_bss_color") == 0) {
+ 		conf->he_op.he_bss_color = atoi(pos) & 0x3f;
+ 		conf->he_op.he_bss_color_disabled = 0;
++		if (atoi(pos) > 63)
++			conf->he_op.he_bss_color = os_random() % 63 + 1;
+ 	} else if (os_strcmp(buf, "he_bss_color_partial") == 0) {
+ 		conf->he_op.he_bss_color_partial = atoi(pos);
+ 	} else if (os_strcmp(buf, "he_default_pe_duration") == 0) {
+@@ -5435,6 +5461,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		bss->mld_indicate_disabled = atoi(pos);
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
++#ifdef CONFIG_APUP
++	} else if (os_strcmp(buf, "apup") == 0) {
++		bss->apup = !!atoi(pos);
++		if (bss->apup)
++			bss->wds_sta = 1;
++	} else if (os_strcmp(buf, "apup_peer_ifname_prefix") == 0) {
++		os_strlcpy(bss->apup_peer_ifname_prefix,
++		           pos, sizeof(bss->apup_peer_ifname_prefix));
++#endif // def CONFIG_APUP
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+@@ -5460,7 +5495,12 @@ struct hostapd_config * hostapd_config_read(const char *fname)
+ 	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);
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index a584d370e..1d5922fab 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4005,6 +4005,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 						      reply_size);
+ 	} else if (os_strcmp(buf, "STATUS-DRIVER") == 0) {
+ 		reply_len = hostapd_drv_status(hapd, reply, reply_size);
++#ifdef CONFIG_CTRL_IFACE_MIB
+ 	} else if (os_strcmp(buf, "MIB") == 0) {
+ 		reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
+ 		if (reply_len >= 0) {
+@@ -4046,6 +4047,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 	} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
+ 		reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
+ 							reply_size);
++#endif
+ 	} else if (os_strcmp(buf, "ATTACH") == 0) {
+ 		if (hostapd_ctrl_iface_attach(hapd, from, fromlen, NULL))
+ 			reply_len = -1;
+@@ -5994,6 +5996,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;
+@@ -6095,6 +6098,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/hostapd/defconfig b/hostapd/defconfig
+index 66bf894eb..f716553bb 100644
+--- a/hostapd/defconfig
++++ b/hostapd/defconfig
+@@ -6,9 +6,21 @@
+ # just setting VARIABLE=n is not disabling that variable.
+ #
+ # This file is included in Makefile, so variables like CFLAGS and LIBS can also
+-# be modified from here. In most cass, these lines should use += in order not
++# be modified from here. In most cases, these lines should use += in order not
+ # to override previous values of the variables.
+ 
++
++# Uncomment following two lines and fix the paths if you have installed TLS
++# libraries in a non-default location
++#CFLAGS += -I/usr/local/openssl/include
++#LIBS += -L/usr/local/openssl/lib
++
++# Some Red Hat versions seem to include kerberos header files from OpenSSL, but
++# the kerberos files are not in the default include path. Following line can be
++# used to fix build issues on such systems (krb5.h not found).
++#CFLAGS += -I/usr/include/kerberos
++
++
+ # Driver interface for Host AP driver
+ CONFIG_DRIVER_HOSTAP=y
+ 
+@@ -281,6 +293,7 @@ CONFIG_IPV6=y
+ # openssl = OpenSSL (default)
+ # gnutls = GnuTLS
+ # internal = Internal TLSv1 implementation (experimental)
++# mbedtls = mbed TLS
+ # linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
+ # none = Empty template
+ #CONFIG_TLS=openssl
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index d69525502..ebf8addc1 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -410,7 +410,6 @@ static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
+ }
+ 
+ 
+-#ifdef CONFIG_TAXONOMY
+ static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
+ 				     char *argv[])
+ {
+@@ -423,7 +422,6 @@ static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
+ 	os_snprintf(buf, sizeof(buf), "SIGNATURE %s", argv[0]);
+ 	return wpa_ctrl_command(ctrl, buf);
+ }
+-#endif /* CONFIG_TAXONOMY */
+ 
+ 
+ static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
+@@ -440,7 +438,6 @@ static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
+ }
+ 
+ 
+-#ifdef CONFIG_WPS
+ static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
+ 				   char *argv[])
+ {
+@@ -666,7 +663,6 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
+ 			 ssid_hex, argv[1]);
+ 	return wpa_ctrl_command(ctrl, buf);
+ }
+-#endif /* CONFIG_WPS */
+ 
+ 
+ static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
+@@ -766,7 +762,7 @@ static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd,
+ 	}
+ 
+ 	buf[len] = '\0';
+-	if (memcmp(buf, "FAIL", 4) == 0)
++	if (memcmp(buf, "FAIL", 4) == 0 || memcmp(buf, "UNKNOWN COMMAND", 15) == 0)
+ 		return -1;
+ 	if (print)
+ 		printf("%s", buf);
+@@ -1695,13 +1691,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 	{ "disassociate", hostapd_cli_cmd_disassociate,
+ 	  hostapd_complete_stations,
+ 	  "<addr> = disassociate a station" },
+-#ifdef CONFIG_TAXONOMY
+ 	{ "signature", hostapd_cli_cmd_signature, hostapd_complete_stations,
+ 	  "<addr> = get taxonomy signature for a station" },
+-#endif /* CONFIG_TAXONOMY */
+ 	{ "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations,
+ 	  "<addr> = send SA Query to a station" },
+-#ifdef CONFIG_WPS
+ 	{ "wps_pin", hostapd_cli_cmd_wps_pin, NULL,
+ 	  "<uuid> <pin> [timeout] [addr] = add WPS Enrollee PIN" },
+ 	{ "wps_check_pin", hostapd_cli_cmd_wps_check_pin, NULL,
+@@ -1726,7 +1719,6 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 	  "<SSID> <auth> <encr> <key> = configure AP" },
+ 	{ "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL,
+ 	  "= show current WPS status" },
+-#endif /* CONFIG_WPS */
+ 	{ "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL,
+ 	  "= send Disassociation Imminent notification" },
+ 	{ "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL,
+diff --git a/hostapd/main.c b/hostapd/main.c
+index aa1f69812..e790f18ce 100644
+--- a/hostapd/main.c
++++ b/hostapd/main.c
+@@ -31,7 +31,7 @@
+ #include "config_file.h"
+ #include "eap_register.h"
+ #include "ctrl_iface.h"
+-
++#include "build_features.h"
+ 
+ struct hapd_global {
+ 	void **drv_priv;
+@@ -40,6 +40,7 @@ struct hapd_global {
+ 
+ static struct hapd_global global;
+ 
++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,
+@@ -692,6 +693,11 @@ fail:
+ 	return -1;
+ }
+ 
++void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
++                       union wpa_event_data *data);
++
++void hostapd_wpa_event_global(void *ctx, enum wpa_event_type event,
++ 				 union wpa_event_data *data);
+ 
+ #ifdef CONFIG_WPS
+ static int gen_uuid(const char *txt_addr)
+@@ -784,6 +790,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;
+@@ -813,8 +824,10 @@ int main(int argc, char *argv[])
+ 		return -1;
+ #endif /* CONFIG_DPP */
+ 
++	wpa_supplicant_event = hostapd_wpa_event;
++	wpa_supplicant_event_global = hostapd_wpa_event_global;
+ 	for (;;) {
+-		c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:q");
++		c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:g:G:qv::");
+ 		if (c < 0)
+ 			break;
+ 		switch (c) {
+@@ -851,6 +864,8 @@ int main(int argc, char *argv[])
+ 			break;
+ #endif /* CONFIG_DEBUG_LINUX_TRACING */
+ 		case 'v':
++			if (optarg)
++				exit(!has_feature(optarg));
+ 			show_version();
+ 			exit(1);
+ 		case 'g':
+@@ -1020,6 +1035,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");
+@@ -1029,6 +1045,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++) {
+diff --git a/src/ap/acs.c b/src/ap/acs.c
+index f5b36d327..25fec499a 100644
+--- a/src/ap/acs.c
++++ b/src/ap/acs.c
+@@ -471,17 +471,17 @@ static int acs_get_bw_center_chan(int freq, enum bw_type bw)
+ static int acs_survey_is_sufficient(struct freq_survey *survey)
+ {
+ 	if (!(survey->filled & SURVEY_HAS_NF)) {
++		survey->nf = -95;
+ 		wpa_printf(MSG_INFO,
+ 			   "ACS: Survey for freq %d is missing noise floor",
+ 			   survey->freq);
+-		return 0;
+ 	}
+ 
+ 	if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
++		survey->channel_time = 0;
+ 		wpa_printf(MSG_INFO,
+ 			   "ACS: Survey for freq %d is missing channel time",
+ 			   survey->freq);
+-		return 0;
+ 	}
+ 
+ 	if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
+@@ -489,7 +489,6 @@ static int acs_survey_is_sufficient(struct freq_survey *survey)
+ 		wpa_printf(MSG_INFO,
+ 			   "ACS: Survey for freq %d is missing RX and busy time (at least one is required)",
+ 			   survey->freq);
+-		return 0;
+ 	}
+ 
+ 	return 1;
+diff --git a/src/ap/airtime_policy.c b/src/ap/airtime_policy.c
+index 68443115f..26f11ad98 100644
+--- a/src/ap/airtime_policy.c
++++ b/src/ap/airtime_policy.c
+@@ -112,8 +112,14 @@ static void set_sta_weights(struct hostapd_data *hapd, unsigned int weight)
+ {
+ 	struct sta_info *sta;
+ 
+-	for (sta = hapd->sta_list; sta; sta = sta->next)
+-		sta_set_airtime_weight(hapd, sta, weight);
++	for (sta = hapd->sta_list; sta; sta = sta->next) {
++		unsigned int sta_weight = weight;
++
++		if (sta->dyn_airtime_weight)
++			sta_weight = (weight * sta->dyn_airtime_weight) / 256;
++
++		sta_set_airtime_weight(hapd, sta, sta_weight);
++	}
+ }
+ 
+ 
+@@ -244,7 +250,10 @@ int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta)
+ 	unsigned int weight;
+ 
+ 	if (hapd->iconf->airtime_mode == AIRTIME_MODE_STATIC) {
+-		weight = get_weight_for_sta(hapd, sta->addr);
++		if (sta->dyn_airtime_weight)
++			weight = sta->dyn_airtime_weight;
++		else
++			weight = get_weight_for_sta(hapd, sta->addr);
+ 		if (weight)
+ 			return sta_set_airtime_weight(hapd, sta, weight);
+ 	}
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index e6669e6a3..29a9ae7db 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -121,6 +121,7 @@ struct hostapd_ssid {
+ #define DYNAMIC_VLAN_OPTIONAL 1
+ #define DYNAMIC_VLAN_REQUIRED 2
+ 	int dynamic_vlan;
++	int vlan_no_bridge;
+ #define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0
+ #define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
+ #define DYNAMIC_VLAN_NAMING_END 2
+@@ -282,6 +283,8 @@ struct airtime_sta_weight {
+ struct hostapd_bss_config {
+ 	char iface[IFNAMSIZ + 1];
+ 	char bridge[IFNAMSIZ + 1];
++	char ft_iface[IFNAMSIZ + 1];
++	char snoop_iface[IFNAMSIZ + 1];
+ 	char vlan_bridge[IFNAMSIZ + 1];
+ 	char wds_bridge[IFNAMSIZ + 1];
+ 	int bridge_hairpin; /* hairpin_mode on bridge members */
+@@ -307,6 +310,7 @@ struct hostapd_bss_config {
+ 	unsigned int eap_sim_db_timeout;
+ 	int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
+ 	struct hostapd_ip_addr own_ip_addr;
++	int dynamic_own_ip_addr;
+ 	char *nas_identifier;
+ 	struct hostapd_radius_servers *radius;
+ 	int radius_require_message_authenticator;
+@@ -995,6 +999,35 @@ struct hostapd_bss_config {
+ 	bool mld_indicate_disabled;
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
++
++#ifdef CONFIG_APUP
++	/**
++	 * Access Point Micro Peering
++	 * A simpler and more useful successor to Ad Hoc,
++	 * Wireless Distribution System, 802.11s mesh mode, Multi-AP and EasyMesh.
++	 *
++	 * Almost plain APs communicate between them via 4-address mode, like in WDS
++	 * but all of them are AP, so they can eventually communicate also with
++	 * plain stations and more AP nodes in sight.
++	 * Low hardware requirements, just AP mode support + 4-address mode, and no
++	 * more unnecessary complications, like hardcoded bridging or routing
++	 * algorithm in WiFi stack.
++	 * For each AP in sight an interface is created, and then it can be used as
++	 * convenient in each case, bridging, routing etc.
++	 */
++	bool apup;
++
++	/**
++	 * In 4-address mode each peer AP in sight is associated to its own
++	 * interface so we have more flexibility in "user-space".
++	 * Those interfaces could be simply bridged in a trivial topology (which
++	 * happens automatically if wds_bridge is not an empty string), or feeded to
++	 * a routing daemon.
++	 *
++	 * If not defined interface names are generated following the WDS convention.
++	 */
++	char apup_peer_ifname_prefix[IFNAMSIZ + 1];
++#endif /* CONFIG_APUP */
+ };
+ 
+ /**
+@@ -1085,6 +1118,8 @@ struct hostapd_config {
+ 	unsigned int track_sta_max_num;
+ 	unsigned int track_sta_max_age;
+ 
++	int max_num_sta;
++
+ 	char country[3]; /* first two octets: country code as described in
+ 			  * ISO/IEC 3166-1. Third octet:
+ 			  * ' ' (ascii 32): all environments
+@@ -1122,6 +1157,8 @@ struct hostapd_config {
+ 
+ 	int ht_op_mode_fixed;
+ 	u16 ht_capab;
++	int noscan;
++	int no_ht_coex;
+ 	int ieee80211n;
+ 	int secondary_channel;
+ 	int no_pri_sec_switch;
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index c47349110..7c9527cd3 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -385,13 +385,37 @@ int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
+ 			const u8 *addr, int aid, int val)
+ {
+ 	const char *bridge = NULL;
++	char ifName[IFNAMSIZ + 1];
++
++	int mRet = 0;
+ 
+ 	if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
+ 		return -1;
++
++#ifdef CONFIG_APUP
++	if (hapd->conf->apup && hapd->conf->apup_peer_ifname_prefix[0]) {
++		mRet = os_snprintf(
++		            ifName, sizeof(ifName), "%s%d",
++		            hapd->conf->apup_peer_ifname_prefix, aid);
++	}
++	else
++#endif // def CONFIG_APUP
++		mRet = os_snprintf(
++		            ifName, sizeof(ifName), "%s.sta%d",
++		            hapd->conf->iface, aid);
++
++	if (mRet >= (int) sizeof(ifName))
++		wpa_printf(MSG_WARNING,
++		           "nl80211: WDS interface name was truncated");
++	else if (mRet < 0)
++		return mRet;
++
++	// Pass back to the caller the resulting interface name
++	if (ifname_wds)
++		os_strlcpy(ifname_wds, ifName, IFNAMSIZ + 1);
++
+ 	if (hapd->conf->wds_bridge[0])
+ 		bridge = hapd->conf->wds_bridge;
+-	else if (hapd->conf->bridge[0])
+-		bridge = hapd->conf->bridge;
+ 	return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
+ 					 bridge, ifname_wds);
+ }
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index d7e79c840..58ca046c6 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -35,6 +35,9 @@ int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
+ 			      int enabled);
+ int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname);
+ int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname);
++
++/** @param val as per nl80211 driver implementation, 1 means add 0 means remove
++ */
+ int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
+ 			const u8 *addr, int aid, int val);
+ int hostapd_sta_add(struct hostapd_data *hapd,
+@@ -371,12 +374,12 @@ static inline int hostapd_drv_br_port_set_attr(struct hostapd_data *hapd,
+ 
+ static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
+ 					       enum drv_br_net_param param,
+-					       unsigned int val)
++					       const char *ifname, unsigned int val)
+ {
+ 	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ 	    hapd->driver->br_set_net_param == NULL)
+ 		return -1;
+-	return hapd->driver->br_set_net_param(hapd->drv_priv, param, val);
++	return hapd->driver->br_set_net_param(hapd->drv_priv, param, ifname, val);
+ }
+ 
+ static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
+@@ -404,6 +407,23 @@ static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
+ 	return hapd->driver->stop_ap(hapd->drv_priv, link_id);
+ }
+ 
++static inline int hostapd_drv_if_rename(struct hostapd_data *hapd,
++					enum wpa_driver_if_type type,
++					const char *ifname,
++					const char *new_name)
++{
++	if (!hapd->driver || !hapd->driver->if_rename || !hapd->drv_priv)
++		return -1;
++	return hapd->driver->if_rename(hapd->drv_priv, type, ifname, new_name);
++}
++
++static inline int hostapd_drv_set_first_bss(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->driver->set_first_bss || !hapd->drv_priv)
++		return 0;
++	return hapd->driver->set_first_bss(hapd->drv_priv);
++}
++
+ static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
+ 					   struct wpa_channel_info *ci)
+ {
+diff --git a/src/ap/apup.c b/src/ap/apup.c
+new file mode 100644
+index 000000000..f736ddc8e
+--- /dev/null
++++ b/src/ap/apup.c
+@@ -0,0 +1,168 @@
++/*
++ * hostapd / APuP Access Point Micro Peering
++ *
++ * Copyright (C) 2023-2024  Gioacchino Mazzurco <gio@polymathes.cc>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++/* Be extremely careful altering include order, move just one in the wrong place
++ * and you will start getting a bunch of error of undefined bool, size_t etc. */
++
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/os.h"
++
++#include "apup.h"
++
++#include "drivers/driver.h"
++#include "wpa_auth.h"
++#include "ap_mlme.h"
++#include "ieee802_11.h"
++#include "ap_drv_ops.h"
++#include "sta_info.h"
++
++#ifdef UBUS_SUPPORT
++#	include "ubus.h"
++#endif
++
++#ifdef UCODE_SUPPORT
++#	include "ucode.h"
++#endif
++
++void apup_process_beacon(struct hostapd_data *hapd,
++              const struct ieee80211_mgmt *mgmt, size_t len,
++              const struct ieee802_11_elems *elems )
++{
++	if (!os_memcmp(hapd->own_addr, mgmt->bssid, ETH_ALEN))
++	{
++		wpa_printf(MSG_WARNING,
++		           "apup_process_beacon(...) own beacon elems.ssid %.*s",
++		           (int) elems->ssid_len, elems->ssid);
++		return;
++	}
++
++	if (elems->ssid_len != hapd->conf->ssid.ssid_len ||
++	        os_memcmp(elems->ssid, hapd->conf->ssid.ssid, elems->ssid_len))
++		return;
++
++	struct sta_info* sta_ret = ap_get_sta(hapd, mgmt->bssid);
++	if (sta_ret)
++		return;
++
++	sta_ret = ap_sta_add(hapd, mgmt->bssid);
++
++	/* TODO: this has been added just to making compiler happy after breaking
++	 * changes introduced in 11a607d121df512e010148bedcb4263a03329dc7 to support
++	 * IEEE80211BE Multi Link Operation. Look at that commit with more time and
++	 * understand what could be a proper implementation in this context too
++	 */
++	const u8 *mld_link_addr = NULL;
++	bool mld_link_sta = false;
++
++	/* First add the station without more information */
++	int aRet = hostapd_sta_add(
++	            hapd, mgmt->bssid, sta_ret->aid, 0,
++	            NULL, 0, 0, NULL, NULL, NULL, 0, NULL, 0, NULL,
++	            sta_ret->flags, 0, 0, 0,
++	            0, // 0 add, 1 set
++	            mld_link_addr, mld_link_sta);
++
++	sta_ret->flags |= WLAN_STA_AUTH;
++	wpa_auth_sm_event(sta_ret->wpa_sm, WPA_AUTH);
++
++	/* TODO: Investigate if supporting WPA or other encryption method is
++	 * possible */
++	sta_ret->auth_alg = WLAN_AUTH_OPEN;
++	mlme_authenticate_indication(hapd, sta_ret);
++
++	sta_ret->capability = le_to_host16(mgmt->u.beacon.capab_info);
++
++	if (sta_ret->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
++		sta_ret->flags |= WLAN_STA_SHORT_PREAMBLE;
++	else
++		sta_ret->flags &= ~WLAN_STA_SHORT_PREAMBLE;
++
++	hostapd_copy_supp_rates(hapd, sta_ret, elems);
++
++	/* Whithout this flag copy_sta_[v]ht_capab will disable [V]HT
++	 * capabilities even if available */
++	if (elems->ht_capabilities || elems->vht_capabilities)
++		sta_ret->flags |= WLAN_STA_WMM;
++
++	copy_sta_ht_capab(hapd, sta_ret, elems->ht_capabilities);
++#ifdef CONFIG_IEEE80211AC
++	copy_sta_vht_capab(hapd, sta_ret, elems->vht_capabilities);
++	copy_sta_vht_oper(hapd, sta_ret, elems->vht_operation);
++	copy_sta_vendor_vht(hapd, sta_ret, elems->vendor_vht, elems->vendor_vht_len);
++#endif // def CONFIG_IEEE80211AC
++#ifdef CONFIG_IEEE80211AX
++	copy_sta_he_capab(hapd, sta_ret, IEEE80211_MODE_AP,
++	                  elems->he_capabilities, elems->he_capabilities_len);
++	copy_sta_he_6ghz_capab(hapd, sta_ret,  elems->he_6ghz_band_cap);
++#endif // def CONFIG_IEEE80211AX
++#ifdef CONFIG_IEEE80211BE
++	copy_sta_eht_capab(hapd, sta_ret,
++	                   IEEE80211_MODE_AP, // TODO: Make sure is the right value
++	                   elems->he_capabilities, elems->he_capabilities_len,
++	                   elems->eht_capabilities, elems->eht_capabilities_len);
++#endif //def CONFIG_IEEE80211BE
++
++	update_ht_state(hapd, sta_ret);
++
++	if (hostapd_get_aid(hapd, sta_ret) < 0)
++	{
++		wpa_printf(MSG_INFO, "apup_process_beacon(...) No room for more AIDs");
++		return;
++	}
++
++	sta_ret->flags |= WLAN_STA_ASSOC_REQ_OK;
++
++	/* Make sure that the previously registered inactivity timer will not
++	 * remove the STA immediately. */
++	sta_ret->timeout_next = STA_NULLFUNC;
++
++	sta_ret->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
++
++	/* Then set the paramethers */
++	int sRet = hostapd_sta_add(
++	            hapd, mgmt->bssid, sta_ret->aid,
++	            sta_ret->capability,
++	            sta_ret->supported_rates, sta_ret->supported_rates_len,
++	            0, // u16 listen_interval TODO ?
++	            sta_ret->ht_capabilities,
++	            sta_ret->vht_capabilities,
++	            sta_ret->he_capab, sta_ret->he_capab_len,
++	            sta_ret->eht_capab, sta_ret->eht_capab_len,
++	            sta_ret->he_6ghz_capab,
++	            sta_ret->flags,
++	            0, // u8 qosinfo
++	            sta_ret->vht_opmode,
++	            0, // int supp_p2p_ps
++	            1, // 0 add, 1 set
++	            mld_link_addr, mld_link_sta);
++
++	ap_sta_set_authorized(hapd, sta_ret, 1);
++	hostapd_set_sta_flags(hapd, sta_ret);
++
++	char mIfname[IFNAMSIZ + 1];
++	os_memset(mIfname, 0, IFNAMSIZ + 1);
++
++	// last param 1 means add 0 means remove
++	int mRet = hostapd_set_wds_sta(
++	            hapd, mIfname, mgmt->bssid, sta_ret->aid, 1);
++
++	wpa_printf(MSG_INFO,
++	           "apup_process_beacon(...) Added APuP peer at %s with flags: %d,"
++	           " capabilities %d",
++	           mIfname, sta_ret->flags, sta_ret->capability);
++
++#ifdef UBUS_SUPPORT
++	hostapd_ubus_notify_apup_newpeer(hapd, mgmt->bssid, mIfname);
++#endif
++
++#ifdef UCODE_SUPPORT
++	hostapd_ucode_apup_newpeer(hapd, mIfname);
++#endif
++}
+diff --git a/src/ap/apup.h b/src/ap/apup.h
+new file mode 100644
+index 000000000..a14a283bb
+--- /dev/null
++++ b/src/ap/apup.h
+@@ -0,0 +1,24 @@
++/*
++ * hostapd / APuP Access Point Micro Peering
++ *
++ * Copyright (C) 2023-2024  Gioacchino Mazzurco <gio@polymathes.cc>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++/* Be extremely careful altering include order, move just one in the wrong place
++ * and you will start getting a bunch of error of undefined bool, size_t etc. */
++
++#include "utils/includes.h"
++#include "utils/common.h"
++
++#include "hostapd.h"
++#include "common/ieee802_11_defs.h"
++
++/** When beacons from other Access Point are received, if the SSID is matching
++ * add them as APuP peers (aka WDS STA to our own AP) the same happens on the
++ * peer when receiving our beacons */
++void apup_process_beacon(struct hostapd_data *hapd,
++              const struct ieee80211_mgmt *mgmt, size_t len,
++              const struct ieee802_11_elems *elems );
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index ddb99ca22..58b561661 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -1439,6 +1439,12 @@ void handle_probe_req(struct hostapd_data *hapd,
+ 	int mld_id;
+ 	u16 links;
+ #endif /* CONFIG_IEEE80211BE */
++	struct hostapd_ubus_request req = {
++		.type = HOSTAPD_UBUS_PROBE_REQ,
++		.mgmt_frame = mgmt,
++		.ssi_signal = ssi_signal,
++		.elems = &elems,
++	};
+ 
+ 	if (hapd->iconf->rssi_ignore_probe_request && ssi_signal &&
+ 	    ssi_signal < hapd->iconf->rssi_ignore_probe_request)
+@@ -1492,7 +1498,7 @@ void handle_probe_req(struct hostapd_data *hapd,
+ 	 * is less likely to see them (Probe Request frame sent on a
+ 	 * neighboring, but partially overlapping, channel).
+ 	 */
+-	if (elems.ds_params &&
++	if (elems.ds_params && 0 &&
+ 	    hapd->iface->current_mode &&
+ 	    (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G ||
+ 	     hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B) &&
+@@ -1625,6 +1631,12 @@ void handle_probe_req(struct hostapd_data *hapd,
+ 	}
+ #endif /* CONFIG_P2P */
+ 
++	if (hostapd_ubus_handle_event(hapd, &req)) {
++		wpa_printf(MSG_DEBUG, "Probe request for " MACSTR " rejected by ubus handler.\n",
++		       MAC2STR(mgmt->sa));
++		return;
++	}
++
+ 	/* TODO: verify that supp_rates contains at least one matching rate
+ 	 * with AP configuration */
+ 
+@@ -1643,7 +1655,7 @@ void handle_probe_req(struct hostapd_data *hapd,
+ 	if (hapd->conf->no_probe_resp_if_max_sta &&
+ 	    is_multicast_ether_addr(mgmt->da) &&
+ 	    is_multicast_ether_addr(mgmt->bssid) &&
+-	    hapd->num_sta >= hapd->conf->max_num_sta &&
++	    hostapd_check_max_sta(hapd) &&
+ 	    !ap_get_sta(hapd, mgmt->sa)) {
+ 		wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
+ 			   " since no room for additional STA",
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index d4d73de19..a1ddbda9f 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -26,6 +26,26 @@
+ #include "taxonomy.h"
+ #include "wnm_ap.h"
+ 
++static const char * hw_mode_str(enum hostapd_hw_mode mode)
++{
++	switch (mode) {
++	case HOSTAPD_MODE_IEEE80211B:
++		return "b";
++	case HOSTAPD_MODE_IEEE80211G:
++		return "g";
++	case HOSTAPD_MODE_IEEE80211A:
++		return "a";
++	case HOSTAPD_MODE_IEEE80211AD:
++		return "ad";
++	case HOSTAPD_MODE_IEEE80211ANY:
++		return "any";
++	case NUM_HOSTAPD_MODES:
++		return "invalid";
++	}
++	return "unknown";
++}
++
++#ifdef CONFIG_CTRL_IFACE_MIB
+ 
+ static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen,
+ 					   size_t curr_len, const u8 *mcs_set)
+@@ -212,26 +232,6 @@ static const char * timeout_next_str(int val)
+ }
+ 
+ 
+-static const char * hw_mode_str(enum hostapd_hw_mode mode)
+-{
+-	switch (mode) {
+-	case HOSTAPD_MODE_IEEE80211B:
+-		return "b";
+-	case HOSTAPD_MODE_IEEE80211G:
+-		return "g";
+-	case HOSTAPD_MODE_IEEE80211A:
+-		return "a";
+-	case HOSTAPD_MODE_IEEE80211AD:
+-		return "ad";
+-	case HOSTAPD_MODE_IEEE80211ANY:
+-		return "any";
+-	case NUM_HOSTAPD_MODES:
+-		return "invalid";
+-	}
+-	return "unknown";
+-}
+-
+-
+ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
+ 				      struct sta_info *sta,
+ 				      char *buf, size_t buflen)
+@@ -562,6 +562,7 @@ int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
+ 	return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
+ }
+ 
++#endif
+ 
+ #ifdef CONFIG_P2P_MANAGER
+ static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
+@@ -1010,12 +1011,12 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
+ 			return len;
+ 		len += ret;
+ 	}
+-
++#ifdef CONFIG_CTRL_IFACE_MIB
+ 	if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) {
+ 		len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
+ 						   mode->mcs_set);
+ 	}
+-
++#endif /* CONFIG_CTRL_IFACE_MIB */
+ 	if (iface->current_rates && iface->num_rates) {
+ 		ret = os_snprintf(buf + len, buflen - len, "supported_rates=");
+ 		if (os_snprintf_error(buflen - len, ret))
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index af9dc16f5..fe044297b 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -18,6 +18,7 @@
+ #include "ap_drv_ops.h"
+ #include "drivers/driver.h"
+ #include "dfs.h"
++#include "crypto/crypto.h"
+ 
+ 
+ enum dfs_channel_type {
+@@ -527,9 +528,14 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+ 	int num_available_chandefs;
+ 	int chan_idx, chan_idx2;
+ 	int sec_chan_idx_80p80 = -1;
++	bool is_mesh = false;
+ 	int i;
+ 	u32 _rand;
+ 
++#ifdef CONFIG_MESH
++	is_mesh = iface->mconf;
++#endif
++
+ 	wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
+ 	*secondary_channel = 0;
+ 	*oper_centr_freq_seg0_idx = 0;
+@@ -549,8 +555,20 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+ 	if (num_available_chandefs == 0)
+ 		return NULL;
+ 
+-	if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
++	/* try to use deterministic channel in mesh, so that both sides
++	 * have a chance to switch to the same channel */
++	if (is_mesh) {
++#ifdef CONFIG_MESH
++		u64 hash[4];
++		const u8 *meshid[1] = { &iface->mconf->meshid[0] };
++		const size_t meshid_len = iface->mconf->meshid_len;
++
++		sha256_vector(1, meshid, &meshid_len, (u8 *)&hash[0]);
++		_rand = hash[0] + hash[1] + hash[2] + hash[3];
++#endif
++	} else if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
+ 		return NULL;
++
+ 	chan_idx = _rand % num_available_chandefs;
+ 	wpa_printf(MSG_DEBUG, "DFS: Picked random entry from the list: %d/%d",
+ 		   chan_idx, num_available_chandefs);
+@@ -1218,6 +1236,8 @@ int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
+ 		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+ 		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+ 
++	hostapd_ubus_notify_radar_detected(iface, freq, chan_width, cf1, cf2);
++
+ 	/* Proceed only if DFS is not offloaded to the driver */
+ 	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+ 		return 0;
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index c74e551c5..6e76f697a 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -270,6 +270,10 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+ 	struct hostapd_iface *iface = hapd->iface;
+ #endif /* CONFIG_OWE */
+ 	bool updated = false;
++	struct hostapd_ubus_request req = {
++		.type = HOSTAPD_UBUS_ASSOC_REQ,
++		.addr = addr,
++	};
+ 
+ 	if (addr == NULL) {
+ 		/*
+@@ -414,6 +418,12 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+ 		goto fail;
+ 	}
+ 
++	if (hostapd_ubus_handle_event(hapd, &req)) {
++		wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
++			   MAC2STR(req.addr));
++		goto fail;
++	}
++
+ #ifdef CONFIG_P2P
+ 	if (elems.p2p) {
+ 		wpabuf_free(sta->p2p_ie);
+@@ -2416,8 +2426,8 @@ static void hostapd_event_color_change(struct hostapd_data *hapd, bool success)
+ #endif  /* CONFIG_IEEE80211AX */
+ 
+ 
+-void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+-			  union wpa_event_data *data)
++void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
++		       union wpa_event_data *data)
+ {
+ 	struct hostapd_data *hapd = ctx;
+ 	struct sta_info *sta;
+@@ -2776,7 +2786,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ }
+ 
+ 
+-void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
++void hostapd_wpa_event_global(void *ctx, enum wpa_event_type event,
+ 				 union wpa_event_data *data)
+ {
+ 	struct hapd_interfaces *interfaces = ctx;
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 5a8cdc90e..8159194e1 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -247,6 +247,29 @@ static int hostapd_iface_conf_changed(struct hostapd_config *newconf,
+ 	return 0;
+ }
+ 
++static inline int hostapd_iface_num_sta(struct hostapd_iface *iface)
++{
++	int num_sta = 0;
++	int i;
++
++	for (i = 0; i < iface->num_bss; i++)
++		num_sta += iface->bss[i]->num_sta;
++
++	return num_sta;
++}
++
++
++int hostapd_check_max_sta(struct hostapd_data *hapd)
++{
++	if (hapd->num_sta >= hapd->conf->max_num_sta)
++		return 1;
++
++	if (hapd->iconf->max_num_sta &&
++	    hostapd_iface_num_sta(hapd->iface) >= hapd->iconf->max_num_sta)
++		return 1;
++
++	return 0;
++}
+ 
+ int hostapd_reload_config(struct hostapd_iface *iface)
+ {
+@@ -255,6 +278,8 @@ int hostapd_reload_config(struct hostapd_iface *iface)
+ 	struct hostapd_config *newconf, *oldconf;
+ 	size_t j;
+ 
++	hostapd_ucode_reload_bss(hapd);
++
+ 	if (iface->config_fname == NULL) {
+ 		/* Only in-memory config in use - assume it has been updated */
+ 		hostapd_clear_old(iface);
+@@ -475,6 +500,8 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
+ 	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);
+ 	vlan_deinit(hapd);
+@@ -485,7 +512,7 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
+ 		struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+ 		size_t i;
+ 
+-		for (i = 0; i < ifaces->count; i++) {
++		for (i = 0; ifaces && i < ifaces->count; i++) {
+ 			struct hostapd_iface *iface = ifaces->iface[i];
+ 			size_t j;
+ 
+@@ -685,6 +712,7 @@ static void sta_track_deinit(struct hostapd_iface *iface)
+ void hostapd_cleanup_iface_partial(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);
+ #ifdef NEED_AP_MLME
+ 	hostapd_stop_setup_timers(iface);
+@@ -1304,6 +1332,9 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
+ 	if (hapd->driver && hapd->driver->set_operstate)
+ 		hapd->driver->set_operstate(hapd->drv_priv, 1);
+ 
++	hostapd_ubus_add_bss(hapd);
++	hostapd_ucode_add_bss(hapd);
++
+ 	return 0;
+ }
+ 
+@@ -1336,6 +1367,7 @@ static int hostapd_bss_radius_init(struct hostapd_data *hapd)
+ 
+ 		os_memset(&das_conf, 0, sizeof(das_conf));
+ 		das_conf.port = conf->radius_das_port;
++		das_conf.nas_identifier = conf->nas_identifier;
+ 		das_conf.shared_secret = conf->radius_das_shared_secret;
+ 		das_conf.shared_secret_len =
+ 			conf->radius_das_shared_secret_len;
+@@ -1378,8 +1410,7 @@ static int hostapd_bss_radius_init(struct hostapd_data *hapd)
+  * 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];
+@@ -2510,6 +2541,7 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
+ 	if (err)
+ 		goto fail;
+ 
++	hostapd_ubus_add_iface(iface);
+ 	wpa_printf(MSG_DEBUG, "Completing interface initialization");
+ 	if (iface->freq) {
+ #ifdef NEED_AP_MLME
+@@ -2739,6 +2771,7 @@ dfs_offload:
+ 
+ fail:
+ 	wpa_printf(MSG_ERROR, "Interface initialization failed");
++	hostapd_ubus_free_iface(iface);
+ 
+ 	if (iface->is_no_ir) {
+ 		hostapd_set_state(iface, HAPD_IFACE_NO_IR);
+@@ -2938,7 +2971,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+ }
+ 
+ 
+-static void hostapd_bss_deinit(struct hostapd_data *hapd)
++void hostapd_bss_deinit(struct hostapd_data *hapd)
+ {
+ 	if (!hapd)
+ 		return;
+@@ -3471,6 +3504,7 @@ void hostapd_interface_deinit_free(struct hostapd_iface *iface)
+ 		   (unsigned int) iface->conf->num_bss);
+ 	driver = iface->bss[0]->driver;
+ 	drv_priv = iface->bss[0]->drv_priv;
++	hostapd_ubus_free_iface(iface);
+ 	hostapd_interface_deinit(iface);
+ 	wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
+ 		   __func__, driver, drv_priv);
+@@ -4002,7 +4036,8 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
+ 		hapd_iface = interfaces->iface[i];
+ 		if (hapd_iface == NULL)
+ 			return -1;
+-		if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
++		if (!os_strcmp(hapd_iface->phy, buf) ||
++		    !os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
+ 			wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
+ 			hapd_iface->driver_ap_teardown =
+ 				!!(hapd_iface->drv_flags &
+@@ -4048,6 +4083,8 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
+ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ 			   int reassoc)
+ {
++	int mld_assoc_link_id = -1;
++
+ 	if (hapd->tkip_countermeasures) {
+ 		hostapd_drv_sta_deauth(hapd, sta->addr,
+ 				       WLAN_REASON_MICHAEL_MIC_FAILURE);
+@@ -4055,10 +4092,16 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ 	}
+ 
+ #ifdef CONFIG_IEEE80211BE
+-	if (ap_sta_is_mld(hapd, sta) &&
+-	    sta->mld_assoc_link_id != hapd->mld_link_id)
+-		return;
++	if (ap_sta_is_mld(hapd, sta)) {
++		if (sta->mld_assoc_link_id == hapd->mld_link_id) {
++			mld_assoc_link_id = sta->mld_assoc_link_id;
++		} else {
++			return;
++		}
++	}
+ #endif /* CONFIG_IEEE80211BE */
++        if (mld_assoc_link_id != -2)
++		hostapd_prune_associations(hapd, sta->addr, mld_assoc_link_id);
+ 
+ 	ap_sta_clear_disconnect_timeouts(hapd, sta);
+ 	sta->post_csa_sa_query = 0;
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 996977fdf..994e4681e 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -18,6 +18,8 @@
+ #include "utils/list.h"
+ #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 +53,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);
+@@ -169,6 +175,21 @@ struct hostapd_sae_commit_queue {
+ 	u8 msg[];
+ };
+ 
++/**
++ * struct hostapd_openwrt_stats - OpenWrt custom STA/AP statistics
++ */
++struct hostapd_openwrt_stats {
++	struct {
++		u64 neighbor_report_tx;
++	} rrm;
++
++	struct {
++		u64 bss_transition_query_rx;
++		u64 bss_transition_request_tx;
++		u64 bss_transition_response_rx;
++	} wnm;
++};
++
+ /**
+  * struct hostapd_data - hostapd per-BSS data structure
+  */
+@@ -176,6 +197,8 @@ struct hostapd_data {
+ 	struct hostapd_iface *iface;
+ 	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;
+@@ -183,6 +206,9 @@ struct hostapd_data {
+ 
+ 	u8 own_addr[ETH_ALEN];
+ 
++	/* OpenWrt specific statistics */
++	struct hostapd_openwrt_stats openwrt_stats;
++
+ 	int num_sta; /* number of entries in sta_list */
+ 	struct sta_info *sta_list; /* STA info list head */
+ #define STA_HASH_SIZE 256
+@@ -535,6 +561,7 @@ struct hostapd_mld {
+  */
+ struct hostapd_iface {
+ 	struct hapd_interfaces *interfaces;
++	struct hostapd_ucode_iface ucode;
+ 	void *owner;
+ 	char *config_fname;
+ 	struct hostapd_config *conf;
+@@ -786,6 +813,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+ 		       struct hostapd_bss_config *bss);
+ int hostapd_setup_interface(struct hostapd_iface *iface);
+ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
++void hostapd_set_own_neighbor_report(struct hostapd_data *hapd);
+ void hostapd_interface_deinit(struct hostapd_iface *iface);
+ void hostapd_interface_free(struct hostapd_iface *iface);
+ struct hostapd_iface * hostapd_alloc_iface(void);
+@@ -794,6 +822,8 @@ struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
+ 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_bss_deinit(struct hostapd_data *hapd);
+ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ 			   int reassoc);
+ void hostapd_interface_deinit_free(struct hostapd_iface *iface);
+@@ -821,6 +851,7 @@ void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
+ 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);
++int hostapd_check_max_sta(struct hostapd_data *hapd);
+ 
+ void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap);
+ void hostapd_cleanup_cca_params(struct hostapd_data *hapd);
+@@ -908,6 +939,16 @@ static inline bool hostapd_mld_is_first_bss(struct hostapd_data *hapd)
+ 
+ #endif /* CONFIG_IEEE80211BE */
+ 
++static inline bool ap_sta_is_mld(struct hostapd_data *hapd,
++				 struct sta_info *sta)
++{
++#ifdef CONFIG_IEEE80211BE
++	return hapd->conf->mld_ap && sta && sta->mld_info.mld_sta;
++#else /* CONFIG_IEEE80211BE */
++	return false;
++#endif /* CONFIG_IEEE80211BE */
++}
++
+ u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd);
+ 
+ #endif /* HOSTAPD_H */
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index 8aa0b3ab5..400c50988 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -553,7 +553,8 @@ static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
+ 	int ret;
+ 
+ 	/* Check that HT40 is used and PRI / SEC switch is allowed */
+-	if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)
++	if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch ||
++		iface->conf->noscan)
+ 		return 0;
+ 
+ 	hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index d8d82d737..39b1bb4c7 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -59,6 +59,9 @@
+ #include "nan_usd_ap.h"
+ #include "pasn/pasn_common.h"
+ 
++#ifdef CONFIG_APUP
++#	include "apup.h"
++#endif // def CONFIG_APUP
+ 
+ #ifdef CONFIG_FILS
+ static struct wpabuf *
+@@ -2876,7 +2879,7 @@ static void handle_auth(struct hostapd_data *hapd,
+ 	u16 auth_alg, auth_transaction, status_code;
+ 	u16 resp = WLAN_STATUS_SUCCESS;
+ 	struct sta_info *sta = NULL;
+-	int res, reply_res;
++	int res, reply_res, ubus_resp;
+ 	u16 fc;
+ 	const u8 *challenge = NULL;
+ 	u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
+@@ -2887,6 +2890,11 @@ static void handle_auth(struct hostapd_data *hapd,
+ #ifdef CONFIG_IEEE80211BE
+ 	bool mld_sta = false;
+ #endif /* CONFIG_IEEE80211BE */
++	struct hostapd_ubus_request req = {
++		.type = HOSTAPD_UBUS_AUTH_REQ,
++		.mgmt_frame = mgmt,
++		.ssi_signal = rssi,
++	};
+ 
+ 	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
+ 		wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
+@@ -3083,6 +3091,13 @@ static void handle_auth(struct hostapd_data *hapd,
+ 		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ 		goto fail;
+ 	}
++	ubus_resp = hostapd_ubus_handle_event(hapd, &req);
++	if (ubus_resp) {
++		wpa_printf(MSG_DEBUG, "Station " MACSTR " rejected by ubus handler.\n",
++			MAC2STR(mgmt->sa));
++		resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
++		goto fail;
++	}
+ 	if (res == HOSTAPD_ACL_PENDING)
+ 		return;
+ 
+@@ -3555,8 +3570,8 @@ static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta,
+ }
+ 
+ 
+-static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
+-			   struct ieee802_11_elems *elems)
++u16 hostapd_copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
++			   const struct ieee802_11_elems *elems)
+ {
+ 	/* Supported rates not used in IEEE 802.11ad/DMG */
+ 	if (hapd->iface->current_mode &&
+@@ -3943,7 +3958,7 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+ 			       elems->ext_capab_len);
+ 	if (resp != WLAN_STATUS_SUCCESS)
+ 		return resp;
+-	resp = copy_supp_rates(hapd, sta, elems);
++	resp = hostapd_copy_supp_rates(hapd, sta, elems);
+ 	if (resp != WLAN_STATUS_SUCCESS)
+ 		return resp;
+ 
+@@ -4763,6 +4778,13 @@ static int add_associated_sta(struct hostapd_data *hapd,
+ 	 * drivers to accept the STA parameter configuration. Since this is
+ 	 * after a new FT-over-DS exchange, a new TK has been derived, so key
+ 	 * reinstallation is not a concern for this case.
++	 *
++	 * If the STA was associated and authorized earlier, but came for a new
++	 * connection (!added_unassoc + !reassoc), remove the existing STA entry
++	 * so that it can be re-added. This case is rarely seen when the AP could
++	 * not receive the deauth/disassoc frame from the STA. And the STA comes
++	 * back with new connection within a short period or before the inactive
++	 * STA entry is removed from the list.
+ 	 */
+ 	wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR
+ 		   " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)",
+@@ -4776,7 +4798,8 @@ static int add_associated_sta(struct hostapd_data *hapd,
+ 	    (!(sta->flags & WLAN_STA_AUTHORIZED) ||
+ 	     (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) ||
+ 	     (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
+-	      !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) {
++	      !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)) ||
++	     (!reassoc && (sta->flags & WLAN_STA_AUTHORIZED)))) {
+ 		hostapd_drv_sta_remove(hapd, sta->addr);
+ 		wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
+ 		set = 0;
+@@ -5337,7 +5360,7 @@ static void handle_assoc(struct hostapd_data *hapd,
+ 	int resp = WLAN_STATUS_SUCCESS;
+ 	u16 reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ 	const u8 *pos;
+-	int left, i;
++	int left, i, ubus_resp;
+ 	struct sta_info *sta;
+ 	u8 *tmp = NULL;
+ #ifdef CONFIG_FILS
+@@ -5579,6 +5602,11 @@ static void handle_assoc(struct hostapd_data *hapd,
+ 		left = res;
+ 	}
+ #endif /* CONFIG_FILS */
++	struct hostapd_ubus_request req = {
++		.type = HOSTAPD_UBUS_ASSOC_REQ,
++		.mgmt_frame = mgmt,
++		.ssi_signal = rssi,
++	};
+ 
+ 	/* followed by SSID and Supported rates; and HT capabilities if 802.11n
+ 	 * is used */
+@@ -5681,6 +5709,13 @@ static void handle_assoc(struct hostapd_data *hapd,
+ 	if (set_beacon)
+ 		ieee802_11_update_beacons(hapd->iface);
+ 
++	ubus_resp = hostapd_ubus_handle_event(hapd, &req);
++	if (ubus_resp) {
++		wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
++		       MAC2STR(mgmt->sa));
++		resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
++		goto fail;
++	}
+  fail:
+ 
+ 	/*
+@@ -5910,6 +5945,7 @@ static void handle_disassoc(struct hostapd_data *hapd,
+ 			   (unsigned long) len);
+ 		return;
+ 	}
++	hostapd_ubus_notify(hapd, "disassoc", mgmt->sa);
+ 
+ 	sta = ap_get_sta(hapd, mgmt->sa);
+ 	if (!sta) {
+@@ -5941,6 +5977,8 @@ static void handle_deauth(struct hostapd_data *hapd,
+ 	/* Clear the PTKSA cache entries for PASN */
+ 	ptksa_cache_flush(hapd->ptksa, mgmt->sa, WPA_CIPHER_NONE);
+ 
++	hostapd_ubus_notify(hapd, "deauth", mgmt->sa);
++
+ 	sta = ap_get_sta(hapd, mgmt->sa);
+ 	if (!sta) {
+ 		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
+@@ -5974,6 +6012,11 @@ static void handle_beacon(struct hostapd_data *hapd,
+ 				      0);
+ 
+ 	ap_list_process_beacon(hapd->iface, mgmt, &elems, fi);
++
++#ifdef CONFIG_APUP
++	if (hapd->conf->apup)
++		apup_process_beacon(hapd, mgmt, len, &elems);
++#endif // def CONFIG_APUP
+ }
+ 
+ 
+diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
+index dd4995f3f..0e13d2940 100644
+--- a/src/ap/ieee802_11.h
++++ b/src/ap/ieee802_11.h
+@@ -108,6 +108,8 @@ int hostapd_process_ml_assoc_req_addr(struct hostapd_data *hapd,
+ 				      const u8 *basic_mle, size_t basic_mle_len,
+ 				      u8 *mld_addr);
+ int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
++u16 hostapd_copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
++		      const struct ieee802_11_elems *elems);
+ u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ 		      const u8 *ht_capab);
+ u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
+diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
+index f90f1254e..7f0a00f95 100644
+--- a/src/ap/ieee802_11_ht.c
++++ b/src/ap/ieee802_11_ht.c
+@@ -82,7 +82,9 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
+ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
+ {
+ 	struct ieee80211_ht_operation *oper;
++	le32 vht_capabilities_info;
+ 	u8 *pos = eid;
++	u8 chwidth;
+ 
+ 	if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n ||
+ 	    is_6ghz_op_class(hapd->iconf->op_class))
+@@ -103,6 +105,13 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
+ 		oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
+ 			HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
+ 
++	vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
++	chwidth = hostapd_get_oper_chwidth(hapd->iconf);
++	if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT
++		&& ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
++		oper->operation_mode = host_to_le16(hapd->iconf->vht_oper_centr_freq_seg0_idx << 5);
++	}
++
+ 	pos += sizeof(*oper);
+ 
+ 	return pos;
+@@ -230,6 +239,9 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
+ 		return;
+ 	}
+ 
++	if (iface->conf->noscan || iface->conf->no_ht_coex)
++		return;
++
+ 	if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) {
+ 		wpa_printf(MSG_DEBUG,
+ 			   "Ignore too short 20/40 BSS Coexistence Management frame");
+@@ -390,6 +402,9 @@ void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta)
+ 	if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+ 		return;
+ 
++	if (iface->conf->noscan || iface->conf->no_ht_coex)
++		return;
++
+ 	wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR
+ 		   " in Association Request", MAC2STR(sta->addr));
+ 
+diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
+index 4dc325ce8..68880ab64 100644
+--- a/src/ap/ieee802_11_vht.c
++++ b/src/ap/ieee802_11_vht.c
+@@ -26,6 +26,7 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
+ 	struct ieee80211_vht_capabilities *cap;
+ 	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+ 	u8 *pos = eid;
++	u8 chwidth;
+ 
+ 	if (!mode || is_6ghz_op_class(hapd->iconf->op_class))
+ 		return eid;
+@@ -63,6 +64,17 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
+ 			host_to_le32(nsts << VHT_CAP_BEAMFORMEE_STS_OFFSET);
+ 	}
+ 
++	chwidth = hostapd_get_oper_chwidth(hapd->iconf);
++	if (((host_to_le32(mode->vht_capab)) & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
++		&& ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
++		cap->vht_capabilities_info |= VHT_CAP_EXTENDED_NSS_BW_SUPPORT;
++		cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ));
++		cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ));
++		cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_MASK));
++	} else {
++		cap->vht_capabilities_info &= ~VHT_CAP_EXTENDED_NSS_BW_SUPPORT_MASK;
++	}
++
+ 	/* Supported MCS set comes from hw */
+ 	os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
+ 
+@@ -75,6 +87,7 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
+ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
+ {
+ 	struct ieee80211_vht_operation *oper;
++	le32 vht_capabilities_info;
+ 	u8 *pos = eid;
+ 	enum oper_chan_width oper_chwidth =
+ 		hostapd_get_oper_chwidth(hapd->iconf);
+@@ -110,6 +123,7 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
+ 	oper->vht_op_info_chan_center_freq_seg1_idx = seg1;
+ 
+ 	oper->vht_op_info_chwidth = oper_chwidth;
++	vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
+ 	if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ) {
+ 		/*
+ 		 * Convert 160 MHz channel width to new style as interop
+@@ -123,6 +137,9 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
+ 			oper->vht_op_info_chan_center_freq_seg0_idx -= 8;
+ 		else
+ 			oper->vht_op_info_chan_center_freq_seg0_idx += 8;
++
++		if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
++			oper->vht_op_info_chan_center_freq_seg1_idx = 0;
+ 	} else if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ) {
+ 		/*
+ 		 * Convert 80+80 MHz channel width to new style as interop
+diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
+index f4103ac9a..7b5b45a2b 100644
+--- a/src/ap/ieee802_1x.c
++++ b/src/ap/ieee802_1x.c
+@@ -600,6 +600,10 @@ int add_common_radius_attr(struct hostapd_data *hapd,
+ 	struct hostapd_radius_attr *attr;
+ 	int len;
+ 
++	if (hapd->conf->dynamic_own_ip_addr)
++		radius_client_get_local_addr(hapd->radius,
++					     &hapd->conf->own_ip_addr);
++
+ 	if (!hostapd_config_get_radius_attr(req_attr,
+ 					    RADIUS_ATTR_NAS_IP_ADDRESS) &&
+ 	    hapd->conf->own_ip_addr.af == AF_INET &&
+@@ -2848,6 +2852,7 @@ static const char * bool_txt(bool val)
+ 	return val ? "TRUE" : "FALSE";
+ }
+ 
++#ifdef CONFIG_CTRL_IFACE_MIB
+ 
+ int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
+ {
+@@ -3034,6 +3039,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ 	return len;
+ }
+ 
++#endif
+ 
+ #ifdef CONFIG_HS20
+ static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx)
+diff --git a/src/ap/rrm.c b/src/ap/rrm.c
+index fbcddf3f9..b024499ac 100644
+--- a/src/ap/rrm.c
++++ b/src/ap/rrm.c
+@@ -89,6 +89,9 @@ static void hostapd_handle_beacon_report(struct hostapd_data *hapd,
+ 		return;
+ 	wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s",
+ 		MAC2STR(addr), token, rep_mode, report);
++	if (len < sizeof(struct rrm_measurement_beacon_report))
++		return;
++	hostapd_ubus_notify_beacon_report(hapd, addr, token, rep_mode, (struct rrm_measurement_beacon_report*) pos, len);
+ }
+ 
+ 
+@@ -269,6 +272,8 @@ static void hostapd_send_nei_report_resp(struct hostapd_data *hapd,
+ 		}
+ 	}
+ 
++	hapd->openwrt_stats.rrm.neighbor_report_tx++;
++
+ 	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ 				wpabuf_head(buf), wpabuf_len(buf));
+ 	wpabuf_free(buf);
+@@ -397,15 +402,20 @@ void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
+ 		   mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa));
+ 
+ 	switch (mgmt->u.action.u.rrm.action) {
++	case WLAN_RRM_LINK_MEASUREMENT_REPORT:
++		hostapd_ubus_handle_link_measurement(hapd, buf, len);
++		break;
+ 	case WLAN_RRM_RADIO_MEASUREMENT_REPORT:
+ 		hostapd_handle_radio_msmt_report(hapd, buf, len);
+ 		break;
+ 	case WLAN_RRM_NEIGHBOR_REPORT_REQUEST:
+ 		hostapd_handle_nei_report_req(hapd, buf, len);
+ 		break;
++	/*
+ 	case WLAN_RRM_LINK_MEASUREMENT_REPORT:
+ 		hostapd_handle_link_mesr_report(hapd, buf, len);
+ 		break;
++	*/
+ 	default:
+ 		wpa_printf(MSG_DEBUG, "RRM action %u is not supported",
+ 			   mgmt->u.action.u.rrm.action);
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index 13613dbab..51978f45f 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -542,6 +542,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
+ 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ 			       HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+ 			       "local deauth request");
++		hostapd_ubus_notify(hapd, "local-deauth", sta->addr);
+ 		ap_free_sta(hapd, sta);
+ 		return;
+ 	}
+@@ -699,6 +700,7 @@ skip_poll:
+ 		mlme_deauthenticate_indication(
+ 			hapd, sta,
+ 			WLAN_REASON_PREV_AUTH_NOT_VALID);
++		hostapd_ubus_notify(hapd, "inactive-deauth", sta->addr);
+ 		ap_free_sta(hapd, sta);
+ 		break;
+ 	}
+@@ -1485,9 +1487,6 @@ bool ap_sta_set_authorized_flag(struct hostapd_data *hapd, struct sta_info *sta,
+ 				mld_assoc_link_id = -2;
+ 		}
+ #endif /* CONFIG_IEEE80211BE */
+-		if (mld_assoc_link_id != -2)
+-			hostapd_prune_associations(hapd, sta->addr,
+-						   mld_assoc_link_id);
+ 		sta->flags |= WLAN_STA_AUTHORIZED;
+ 	} else {
+ 		sta->flags &= ~WLAN_STA_AUTHORIZED;
+@@ -1524,15 +1523,28 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
+ 		os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr));
+ 
+ 	if (authorized) {
++		static const char * const auth_algs[] = {
++			[WLAN_AUTH_OPEN] = "open",
++			[WLAN_AUTH_SHARED_KEY] = "shared",
++			[WLAN_AUTH_FT] = "ft",
++			[WLAN_AUTH_SAE] = "sae",
++			[WLAN_AUTH_FILS_SK] = "fils-sk",
++			[WLAN_AUTH_FILS_SK_PFS] = "fils-sk-pfs",
++			[WLAN_AUTH_FILS_PK] = "fils-pk",
++			[WLAN_AUTH_PASN] = "pasn",
++		};
++		const char *auth_alg = NULL;
+ 		const u8 *dpp_pkhash;
+ 		const char *keyid;
+ 		char dpp_pkhash_buf[100];
+ 		char keyid_buf[100];
+ 		char ip_addr[100];
++		char alg_buf[100];
+ 
+ 		dpp_pkhash_buf[0] = '\0';
+ 		keyid_buf[0] = '\0';
+ 		ip_addr[0] = '\0';
++		alg_buf[0] = '\0';
+ #ifdef CONFIG_P2P
+ 		if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
+ 			os_snprintf(ip_addr, sizeof(ip_addr),
+@@ -1543,6 +1555,13 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
+ 		}
+ #endif /* CONFIG_P2P */
+ 
++		if (sta->auth_alg < ARRAY_SIZE(auth_algs))
++			auth_alg = auth_algs[sta->auth_alg];
++
++		if (auth_alg)
++			os_snprintf(alg_buf, sizeof(alg_buf),
++				" auth_alg=%s", auth_alg);
++
+ 		keyid = ap_sta_wpa_get_keyid(hapd, sta);
+ 		if (keyid) {
+ 			os_snprintf(keyid_buf, sizeof(keyid_buf),
+@@ -1561,17 +1580,19 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
+ 					 dpp_pkhash, SHA256_MAC_LEN);
+ 		}
+ 
+-		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s",
+-			buf, ip_addr, keyid_buf, dpp_pkhash_buf);
++		hostapd_ubus_notify_authorized(hapd, sta, auth_alg);
++		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s%s",
++			buf, ip_addr, keyid_buf, dpp_pkhash_buf, alg_buf);
+ 
+ 		if (hapd->msg_ctx_parent &&
+ 		    hapd->msg_ctx_parent != hapd->msg_ctx)
+ 			wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
+-					  AP_STA_CONNECTED "%s%s%s%s",
++					  AP_STA_CONNECTED "%s%s%s%s%s",
+ 					  buf, ip_addr, keyid_buf,
+-					  dpp_pkhash_buf);
++					  dpp_pkhash_buf, alg_buf);
+ 	} else {
+ 		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
++		hostapd_ubus_notify(hapd, "disassoc", sta->addr);
+ 
+ 		if (hapd->msg_ctx_parent &&
+ 		    hapd->msg_ctx_parent != hapd->msg_ctx)
+diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
+index 84629358c..d03d18b48 100644
+--- a/src/ap/sta_info.h
++++ b/src/ap/sta_info.h
+@@ -17,7 +17,6 @@
+ #include "common/sae.h"
+ #include "crypto/sha384.h"
+ #include "pasn/pasn_common.h"
+-#include "hostapd.h"
+ 
+ /* STA flags */
+ #define WLAN_STA_AUTH BIT(0)
+@@ -49,10 +48,6 @@
+ #define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
+ #define WLAN_STA_NONERP BIT(31)
+ 
+-/* Maximum number of supported rates (from both Supported Rates and Extended
+- * Supported Rates IEs). */
+-#define WLAN_SUPP_RATES_MAX 32
+-
+ struct hostapd_data;
+ 
+ struct mbo_non_pref_chan_info {
+@@ -321,6 +316,7 @@ struct sta_info {
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #ifdef CONFIG_AIRTIME_POLICY
+ 	unsigned int airtime_weight;
++	unsigned int dyn_airtime_weight;
+ 	struct os_reltime backlogged_until;
+ #endif /* CONFIG_AIRTIME_POLICY */
+ 
+@@ -421,16 +417,6 @@ int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta);
+ 
+ void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta);
+ 
+-static inline bool ap_sta_is_mld(struct hostapd_data *hapd,
+-				 struct sta_info *sta)
+-{
+-#ifdef CONFIG_IEEE80211BE
+-	return hapd->conf->mld_ap && sta && sta->mld_info.mld_sta;
+-#else /* CONFIG_IEEE80211BE */
+-	return false;
+-#endif /* CONFIG_IEEE80211BE */
+-}
+-
+ static inline void ap_sta_set_mld(struct sta_info *sta, bool mld)
+ {
+ #ifdef CONFIG_IEEE80211BE
+diff --git a/src/ap/ubus.c b/src/ap/ubus.c
+index 8689494bc..f21516fc3 100644
+--- a/src/ap/ubus.c
++++ b/src/ap/ubus.c
+@@ -2004,3 +2004,18 @@ int hostapd_ubus_notify_bss_transition_query(
+ 	return ureq.resp;
+ #endif
+ }
++
++#ifdef CONFIG_APUP
++void hostapd_ubus_notify_apup_newpeer(
++	struct hostapd_data *hapd, const u8 *addr, const char *ifname)
++{
++	if (!hapd->ubus.obj.has_subscribers)
++		return;
++
++	blob_buf_init(&b, 0);
++	blobmsg_add_macaddr(&b, "address", addr);
++	blobmsg_add_string(&b, "ifname", ifname);
++
++	ubus_notify(ctx, &hapd->ubus.obj, "apup-newpeer", b.head, -1);
++}
++#endif // def CONFIG_APUP
+diff --git a/src/ap/ubus.h b/src/ap/ubus.h
+index 22767d67e..1c65e4dcb 100644
+--- a/src/ap/ubus.h
++++ b/src/ap/ubus.h
+@@ -71,6 +71,11 @@ int hostapd_ubus_notify_bss_transition_query(
+ void hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+ 				    const char *auth_alg);
+ 
++#ifdef CONFIG_APUP
++void hostapd_ubus_notify_apup_newpeer(
++	struct hostapd_data *hapd, const u8 *addr, const char *ifname);
++#endif // def CONFIG_APUP
++
+ #else
+ 
+ struct hostapd_ubus_bss {};
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index 68fb45088..3468615fd 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -815,3 +815,20 @@ void hostapd_ucode_free_bss(struct hostapd_data *hapd)
+ 	ucv_put(wpa_ucode_call(2));
+ 	ucv_gc(vm);
+ }
++
++#ifdef CONFIG_APUP
++void hostapd_ucode_apup_newpeer(struct hostapd_data *hapd, const char *ifname)
++{
++	uc_value_t *val;
++
++	if (wpa_ucode_call_prepare("apup_newpeer"))
++		return;
++
++	val = hostapd_ucode_bss_get_uval(hapd);
++	uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface))); // BSS ifname
++	uc_value_push(ucv_get(val));
++	uc_value_push(ucv_get(ucv_string_new(ifname))); // APuP peer ifname
++	ucv_put(wpa_ucode_call(2));
++	ucv_gc(vm);
++}
++#endif // def CONFIG_APUP
+diff --git a/src/ap/ucode.h b/src/ap/ucode.h
+index d00b78716..c9bdde651 100644
+--- a/src/ap/ucode.h
++++ b/src/ap/ucode.h
+@@ -27,6 +27,10 @@ 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);
+ 
++#ifdef CONFIG_APUP
++void hostapd_ucode_apup_newpeer(struct hostapd_data *hapd, const char *ifname);
++#endif // def CONFIG_APUP
++
+ #else
+ 
+ static inline int hostapd_ucode_init(struct hapd_interfaces *ifaces)
+diff --git a/src/ap/vlan_full.c b/src/ap/vlan_full.c
+index 19aa3c649..053d6338e 100644
+--- a/src/ap/vlan_full.c
++++ b/src/ap/vlan_full.c
+@@ -475,6 +475,9 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
+ 	if (!vlan)
+ 		return;
+ 
++	if (hapd->conf->ssid.vlan_no_bridge)
++		goto out;
++
+ 	vlan->configured = 1;
+ 
+ 	notempty = vlan->vlan_desc.notempty;
+@@ -506,6 +509,7 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
+ 				    ifname, br_name, tagged[i], hapd);
+ 	}
+ 
++out:
+ 	ifconfig_up(ifname);
+ }
+ 
+diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
+index 53eacfb45..b69f3de41 100644
+--- a/src/ap/vlan_init.c
++++ b/src/ap/vlan_init.c
+@@ -22,6 +22,7 @@
+ static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+ 		       int existsok)
+ {
++	bool vlan_exists = iface_exists(vlan->ifname);
+ 	int ret;
+ #ifdef CONFIG_WEP
+ 	int i;
+@@ -36,7 +37,7 @@ static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+ 	}
+ #endif /* CONFIG_WEP */
+ 
+-	if (!iface_exists(vlan->ifname))
++	if (!vlan_exists)
+ 		ret = hostapd_vlan_if_add(hapd, vlan->ifname);
+ 	else if (!existsok)
+ 		return -1;
+@@ -51,6 +52,9 @@ static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+ 	if (hapd->wpa_auth)
+ 		ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id);
+ 
++	if (!ret && !vlan_exists)
++		hostapd_ubus_add_vlan(hapd, vlan);
++
+ 	if (ret == 0)
+ 		return ret;
+ 
+@@ -77,6 +81,8 @@ int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+ 			   "WPA deinitialization for VLAN %d failed (%d)",
+ 			   vlan->vlan_id, ret);
+ 
++	hostapd_ubus_remove_vlan(hapd, vlan);
++
+ 	return hostapd_vlan_if_remove(hapd, vlan->ifname);
+ }
+ 
+diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
+index af8cccaef..d259200c9 100644
+--- a/src/ap/wnm_ap.c
++++ b/src/ap/wnm_ap.c
+@@ -410,6 +410,7 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
+ 	mgmt->u.action.u.bss_tm_req.validity_interval = 1;
+ 	pos = mgmt->u.action.u.bss_tm_req.variable;
+ 
++	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 "
+ 		   "validity_interval=%u",
+@@ -478,7 +479,8 @@ static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
+ 		MAC2STR(addr), reason, hex ? " neighbor=" : "", hex);
+ 	os_free(hex);
+ 
+-	ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
++	if (!hostapd_ubus_notify_bss_transition_query(hapd, addr, dialog_token, reason, pos, end - pos))
++		ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
+ }
+ 
+ 
+@@ -500,7 +502,7 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
+ 					      size_t len)
+ {
+ 	u8 dialog_token, status_code, bss_termination_delay;
+-	const u8 *pos, *end;
++	const u8 *pos, *end, *target_bssid = NULL;
+ 	int enabled = hapd->conf->bss_transition;
+ 	struct sta_info *sta;
+ 
+@@ -547,6 +549,7 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
+ 			wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
+ 			return;
+ 		}
++		target_bssid = pos;
+ 		sta->agreed_to_steer = 1;
+ 		eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
+ 		eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
+@@ -566,6 +569,10 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
+ 			MAC2STR(addr), status_code, bss_termination_delay);
+ 	}
+ 
++	hostapd_ubus_notify_bss_transition_response(hapd, sta->addr, dialog_token,
++						    status_code, bss_termination_delay,
++						    target_bssid, pos, end - pos);
++
+ 	wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
+ 		    pos, end - pos);
+ }
+@@ -814,10 +821,12 @@ int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
+ 					       plen);
+ 		return 0;
+ 	case WNM_BSS_TRANS_MGMT_QUERY:
++		hapd->openwrt_stats.wnm.bss_transition_query_rx++;
+ 		ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
+ 						   plen);
+ 		return 0;
+ 	case WNM_BSS_TRANS_MGMT_RESP:
++		hapd->openwrt_stats.wnm.bss_transition_response_rx++;
+ 		ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload,
+ 						  plen);
+ 		return 0;
+@@ -865,6 +874,7 @@ int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
+ 
+ 	pos = mgmt->u.action.u.bss_tm_req.variable;
+ 
++	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+ 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to "
+ 		   MACSTR, disassoc_timer, MAC2STR(sta->addr));
+ 	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
+@@ -947,6 +957,7 @@ int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
+ 		return -1;
+ 	}
+ 
++	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+ 	if (disassoc_timer) {
+ 		/* send disassociation frame after time-out */
+ 		set_disassoc_timer(hapd, sta, disassoc_timer);
+@@ -1028,6 +1039,7 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+ 	}
+ 	os_free(buf);
+ 
++	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+ 	if (disassoc_timer) {
+ #ifdef CONFIG_IEEE80211BE
+ 		if (ap_sta_is_mld(hapd, sta)) {
+diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
+index 93f157d62..3a1d288dd 100644
+--- a/src/ap/wpa_auth.c
++++ b/src/ap/wpa_auth.c
+@@ -6095,6 +6095,7 @@ static const char * wpa_bool_txt(int val)
+ 	return val ? "TRUE" : "FALSE";
+ }
+ 
++#ifdef CONFIG_CTRL_IFACE_MIB
+ 
+ #define RSN_SUITE "%02x-%02x-%02x-%d"
+ #define RSN_SUITE_ARG(s) \
+@@ -6247,7 +6248,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
+ 
+ 	return len;
+ }
+-
++#endif
+ 
+ void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth)
+ {
+diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
+index 13685b7c2..eaded9434 100644
+--- a/src/ap/wpa_auth_glue.c
++++ b/src/ap/wpa_auth_glue.c
+@@ -328,6 +328,7 @@ static void hostapd_wpa_auth_psk_failure_report(void *ctx, const u8 *addr)
+ 	struct hostapd_data *hapd = ctx;
+ 	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POSSIBLE_PSK_MISMATCH MACSTR,
+ 		MAC2STR(addr));
++	hostapd_ubus_notify(hapd, "key-mismatch", addr);
+ }
+ 
+ 
+@@ -1811,8 +1812,12 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
+ 	    wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
+ 		const char *ft_iface;
+ 
+-		ft_iface = hapd->conf->bridge[0] ? hapd->conf->bridge :
+-			   hapd->conf->iface;
++		if (hapd->conf->ft_iface[0])
++			ft_iface = hapd->conf->ft_iface;
++		else if (hapd->conf->bridge[0])
++			ft_iface = hapd->conf->bridge;
++		else
++			ft_iface = hapd->conf->iface;
+ 		hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB,
+ 					  hostapd_rrb_receive, hapd, 1);
+ 		if (!hapd->l2) {
+diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
+index 82d4d5fdd..dfc5c3ecb 100644
+--- a/src/ap/wps_hostapd.c
++++ b/src/ap/wps_hostapd.c
+@@ -394,9 +394,8 @@ static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd,
+ 				bss->wpa_pairwise |= WPA_CIPHER_GCMP;
+ 			else
+ 				bss->wpa_pairwise |= WPA_CIPHER_CCMP;
+-		}
+ #ifndef CONFIG_NO_TKIP
+-		if (cred->encr_type & WPS_ENCR_TKIP)
++		} else if (cred->encr_type & WPS_ENCR_TKIP)
+ 			bss->wpa_pairwise |= WPA_CIPHER_TKIP;
+ #endif /* CONFIG_NO_TKIP */
+ 		bss->rsn_pairwise = bss->wpa_pairwise;
+@@ -1181,8 +1180,7 @@ int hostapd_init_wps(struct hostapd_data *hapd,
+ 					  WPA_CIPHER_GCMP_256)) {
+ 			wps->encr_types |= WPS_ENCR_AES;
+ 			wps->encr_types_rsn |= WPS_ENCR_AES;
+-		}
+-		if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
++		} else if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
+ #ifdef CONFIG_NO_TKIP
+ 			wpa_printf(MSG_INFO, "WPS: TKIP not supported");
+ 			goto fail;
+diff --git a/src/ap/x_snoop.c b/src/ap/x_snoop.c
+index 029f4de23..4c20f137f 100644
+--- a/src/ap/x_snoop.c
++++ b/src/ap/x_snoop.c
+@@ -33,28 +33,31 @@ int x_snoop_init(struct hostapd_data *hapd)
+ 
+ 	hapd->x_snoop_initialized = true;
+ 
+-	if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
++	if (!conf->snoop_iface[0] &&
++	    hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
+ 					 1)) {
+ 		wpa_printf(MSG_DEBUG,
+ 			   "x_snoop: Failed to enable hairpin_mode on the bridge port");
+ 		return -1;
+ 	}
+ 
+-	if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
++	if (!conf->snoop_iface[0] &&
++	    hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
+ 		wpa_printf(MSG_DEBUG,
+ 			   "x_snoop: Failed to enable proxyarp on the bridge port");
+ 		return -1;
+ 	}
+ 
+ 	if (hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
+-					 1)) {
++					 conf->snoop_iface[0] ? conf->snoop_iface : NULL, 1)) {
+ 		wpa_printf(MSG_DEBUG,
+ 			   "x_snoop: Failed to enable accepting gratuitous ARP on the bridge");
+ 		return -1;
+ 	}
+ 
+ #ifdef CONFIG_IPV6
+-	if (hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, 1)) {
++	if (!conf->snoop_iface[0] &&
++	    hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, NULL, 1)) {
+ 		wpa_printf(MSG_DEBUG,
+ 			   "x_snoop: Failed to enable multicast snooping on the bridge");
+ 		return -1;
+@@ -73,8 +76,12 @@ x_snoop_get_l2_packet(struct hostapd_data *hapd,
+ {
+ 	struct hostapd_bss_config *conf = hapd->conf;
+ 	struct l2_packet_data *l2;
++	const char *ifname = conf->bridge;
++
++	if (conf->snoop_iface[0])
++		ifname = conf->snoop_iface;
+ 
+-	l2 = l2_packet_init(conf->bridge, NULL, ETH_P_ALL, handler, hapd, 1);
++	l2 = l2_packet_init(ifname, NULL, ETH_P_ALL, handler, hapd, 1);
+ 	if (l2 == NULL) {
+ 		wpa_printf(MSG_DEBUG,
+ 			   "x_snoop: Failed to initialize L2 packet processing %s",
+@@ -127,9 +134,12 @@ void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+ 
+ void x_snoop_deinit(struct hostapd_data *hapd)
+ {
++	struct hostapd_bss_config *conf = hapd->conf;
++
+ 	if (!hapd->x_snoop_initialized)
+ 		return;
+-	hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0);
++	hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
++				     conf->snoop_iface[0] ? conf->snoop_iface : NULL, 0);
+ 	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0);
+ 	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0);
+ 	hapd->x_snoop_initialized = false;
+diff --git a/src/common/defs.h b/src/common/defs.h
+index 8cca094e8..151b69170 100644
+--- a/src/common/defs.h
++++ b/src/common/defs.h
+@@ -63,6 +63,10 @@
+ 			 WPA_KEY_MGMT_FT_FILS_SHA256 | \
+ 			 WPA_KEY_MGMT_FT_FILS_SHA384)
+ 
++/* Maximum number of supported rates (from both Supported Rates and Extended
++ * Supported Rates IEs). */
++#define WLAN_SUPP_RATES_MAX 32
++
+ static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
+ {
+ 	return !!(akm & (WPA_KEY_MGMT_IEEE8021X |
+diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c
+index f17f95a2c..39d39f429 100644
+--- a/src/common/dpp_crypto.c
++++ b/src/common/dpp_crypto.c
+@@ -269,6 +269,12 @@ int dpp_get_pubkey_hash(struct crypto_ec_key *key, u8 *hash)
+ 
+ struct crypto_ec_key * dpp_gen_keypair(const struct dpp_curve_params *curve)
+ {
++	if (curve == NULL) {
++		wpa_printf(MSG_DEBUG,
++		           "DPP: %s curve must be initialized", __func__);
++		return NULL;
++	}
++
+ 	struct crypto_ec_key *key;
+ 
+ 	wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
+@@ -1582,7 +1588,9 @@ dpp_pkex_derive_Qr(const struct dpp_curve_params *curve, const u8 *mac_resp,
+ 	Pr = crypto_ec_key_get_public_key(Pr_key);
+ 	Qr = crypto_ec_point_init(ec);
+ 	hash_bn = crypto_bignum_init_set(hash, curve->hash_len);
+-	if (!Pr || !Qr || !hash_bn || crypto_ec_point_mul(ec, Pr, hash_bn, Qr))
++	if (!Pr || !Qr || !hash_bn ||
++	    crypto_bignum_mod(hash_bn, crypto_ec_get_prime(ec), hash_bn) ||
++	    crypto_ec_point_mul(ec, Pr, hash_bn, Qr))
+ 		goto fail;
+ 
+ 	if (crypto_ec_point_is_at_infinity(ec, Qr)) {
+diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
+index 2c47bf812..8bd6e994d 100644
+--- a/src/common/hw_features_common.c
++++ b/src/common/hw_features_common.c
+@@ -898,6 +898,7 @@ int ieee80211ac_cap_check(u32 hw, u32 conf)
+ 	VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
+ 	VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
+ 	VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
++	VHT_CAP_CHECK(VHT_CAP_EXTENDED_NSS_BW_SUPPORT);
+ 
+ #undef VHT_CAP_CHECK
+ #undef VHT_CAP_CHECK_MAX
+diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
+index db9e90355..269b1cf97 100644
+--- a/src/common/ieee802_11_defs.h
++++ b/src/common/ieee802_11_defs.h
+@@ -1398,6 +1398,8 @@ struct ieee80211_ampe_ie {
+ #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB     ((u32) BIT(26) | BIT(27))
+ #define VHT_CAP_RX_ANTENNA_PATTERN                  ((u32) BIT(28))
+ #define VHT_CAP_TX_ANTENNA_PATTERN                  ((u32) BIT(29))
++#define VHT_CAP_EXTENDED_NSS_BW_SUPPORT		    ((u32) BIT(30))
++#define VHT_CAP_EXTENDED_NSS_BW_SUPPORT_MASK	    ((u32) BIT(30) | BIT(31))
+ 
+ #define VHT_OPMODE_CHANNEL_WIDTH_MASK		    ((u8) BIT(0) | BIT(1))
+ #define VHT_OPMODE_CHANNEL_RxNSS_MASK		    ((u8) BIT(4) | BIT(5) | \
+diff --git a/src/common/sae.c b/src/common/sae.c
+index a65da6134..36f09e31b 100644
+--- a/src/common/sae.c
++++ b/src/common/sae.c
+@@ -1278,6 +1278,13 @@ void sae_deinit_pt(struct sae_pt *pt)
+ static int sae_derive_commit_element_ecc(struct sae_data *sae,
+ 					 struct crypto_bignum *mask)
+ {
++	if (sae->tmp->pwe_ecc == NULL) {
++		wpa_printf(MSG_DEBUG,
++		           "SAE: %s sae->tmp->pwe_ecc must be initialized",
++		           __func__);
++		return -1;
++	}
++
+ 	/* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
+ 	if (!sae->tmp->own_commit_element_ecc) {
+ 		sae->tmp->own_commit_element_ecc =
+diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c
+index d0c174c05..ba659fe0f 100644
+--- a/src/common/wpa_ctrl.c
++++ b/src/common/wpa_ctrl.c
+@@ -142,7 +142,7 @@ try_again:
+ 		return NULL;
+ 	}
+ 	tries++;
+-#ifdef ANDROID
++
+ 	/* Set client socket file permissions so that bind() creates the client
+ 	 * socket with these permissions and there is no need to try to change
+ 	 * them with chmod() after bind() which would have potential issues with
+@@ -154,7 +154,7 @@ try_again:
+ 	 * operations to allow the response to go through. Those are using the
+ 	 * no-deference-symlinks version to avoid races. */
+ 	fchmod(ctrl->s, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+-#endif /* ANDROID */
++
+ 	if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
+ 		    sizeof(ctrl->local)) < 0) {
+ 		if (errno == EADDRINUSE && tries < 2) {
+@@ -172,7 +172,11 @@ try_again:
+ 		return NULL;
+ 	}
+ 
+-#ifdef ANDROID
++#ifndef ANDROID
++	/* Set group even if we do not have privileges to change owner */
++	lchown(ctrl->local.sun_path, -1, 101);
++	lchown(ctrl->local.sun_path, 101, 101);
++#else
+ 	/* Set group even if we do not have privileges to change owner */
+ 	lchown(ctrl->local.sun_path, -1, AID_WIFI);
+ 	lchown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
+diff --git a/src/crypto/Makefile b/src/crypto/Makefile
+index ce0997091..96bac9476 100644
+--- a/src/crypto/Makefile
++++ b/src/crypto/Makefile
+@@ -1,10 +1,121 @@
+-CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+-CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
+-CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
+ #CFLAGS += -DALL_DH_GROUPS
+ CFLAGS += -DCONFIG_SHA256
+ CFLAGS += -DCONFIG_SHA384
++CFLAGS += -DCONFIG_HMAC_SHA256_KDF
+ CFLAGS += -DCONFIG_HMAC_SHA384_KDF
++
++# crypto_module_tests.c
++CFLAGS += -DCONFIG_MODULE_TESTS
++CFLAGS += -DCONFIG_DPP
++#CFLAGS += -DCONFIG_DPP2
++#CFLAGS += -DCONFIG_DPP3
++CFLAGS += -DCONFIG_ECC
++CFLAGS += -DCONFIG_MESH
++CFLAGS += -DEAP_PSK
++CFLAGS += -DEAP_FAST
++
++ifeq ($(CONFIG_TLS),mbedtls)
++
++# (enable features for 'cd tests; make run-tests CONFIG_TLS=mbedtls')
++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
++CFLAGS += -DCONFIG_DES
++CFLAGS += -DEAP_IKEV2
++CFLAGS += -DEAP_MSCHAPv2
++CFLAGS += -DEAP_SIM
++
++LIB_OBJS = tls_mbedtls.o crypto_mbedtls.o
++LIB_OBJS+= \
++	aes-eax.o \
++	aes-siv.o \
++	dh_groups.o \
++	milenage.o \
++	ms_funcs.o
++
++else
++ifeq ($(CONFIG_TLS),openssl)
++
++# (enable features for 'cd tests; make run-tests CONFIG_TLS=openssl')
++ifndef CONFIG_TLS_DEFAULT_CIPHERS
++CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
++endif
++CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
++CFLAGS += -DEAP_TLS_OPENSSL
++
++LIB_OBJS = tls_openssl.o fips_prf_openssl.o crypto_openssl.o
++LIB_OBJS+= \
++	aes-ctr.o \
++	aes-eax.o \
++	aes-encblock.o \
++	aes-siv.o \
++	dh_groups.o \
++	milenage.o \
++	ms_funcs.o \
++	sha1-prf.o \
++	sha1-tlsprf.o \
++	sha1-tprf.o \
++	sha256-kdf.o \
++	sha256-prf.o \
++	sha256-tlsprf.o
++
++else
++ifeq ($(CONFIG_TLS),wolfssl)
++
++# (wolfssl libraries must be built with ./configure --enable-wpas)
++# (enable features for 'cd tests; make run-tests CONFIG_TLS=wolfssl')
++CFLAGS += -DWOLFSSL_DER_LOAD
++CFLAGS += -DCONFIG_DES
++
++LIB_OBJS = tls_wolfssl.o fips_prf_wolfssl.o crypto_wolfssl.o
++LIB_OBJS+= \
++	aes-ctr.o \
++	aes-eax.o \
++	aes-encblock.o \
++	aes-siv.o \
++	dh_groups.o \
++	milenage.o \
++	ms_funcs.o \
++	sha1-prf.o \
++	sha1-tlsprf.o \
++	sha1-tprf.o \
++	sha256-kdf.o \
++	sha256-prf.o \
++	sha256-tlsprf.o
++
++else
++ifeq ($(CONFIG_TLS),gnutls)
++
++# (enable features for 'cd tests; make run-tests CONFIG_TLS=gnutls')
++LIB_OBJS = tls_gnutls.o crypto_gnutls.o
++LIB_OBJS+= \
++	aes-cbc.o \
++	aes-ctr.o \
++	aes-eax.o \
++	aes-encblock.o \
++	aes-omac1.o \
++	aes-siv.o \
++	aes-unwrap.o \
++	aes-wrap.o \
++	dh_group5.o \
++	dh_groups.o \
++	milenage.o \
++	ms_funcs.o \
++	rc4.o \
++	sha1-pbkdf2.o \
++	sha1-prf.o \
++	fips_prf_internal.o \
++	sha1-internal.o \
++	sha1-tlsprf.o \
++	sha1-tprf.o \
++	sha256-kdf.o \
++	sha256-prf.o \
++	sha256-tlsprf.o
++
++else
++
++CFLAGS += -DCONFIG_CRYPTO_INTERNAL
++CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
++CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
+ CFLAGS += -DCONFIG_INTERNAL_SHA384
+ 
+ LIB_OBJS= \
+@@ -13,7 +124,6 @@ LIB_OBJS= \
+ 	aes-ctr.o \
+ 	aes-eax.o \
+ 	aes-encblock.o \
+-	aes-gcm.o \
+ 	aes-internal.o \
+ 	aes-internal-dec.o \
+ 	aes-internal-enc.o \
+@@ -37,6 +147,7 @@ LIB_OBJS= \
+ 	sha1-tlsprf.o \
+ 	sha1-tprf.o \
+ 	sha256.o \
++	sha256-kdf.o \
+ 	sha256-prf.o \
+ 	sha256-tlsprf.o \
+ 	sha256-internal.o \
+@@ -53,6 +164,16 @@ LIB_OBJS += crypto_internal-modexp.o
+ LIB_OBJS += crypto_internal-rsa.o
+ LIB_OBJS += tls_internal.o
+ LIB_OBJS += fips_prf_internal.o
++
++endif
++endif
++endif
++endif
++
++
++# (used by wlantest/{bip,gcmp,rx_mgmt}.c and tests/test-aes.c)
++LIB_OBJS += aes-gcm.o
++
+ ifndef TEST_FUZZ
+ LIB_OBJS += random.o
+ endif
+diff --git a/src/crypto/crypto_mbedtls.c b/src/crypto/crypto_mbedtls.c
+new file mode 100644
+index 000000000..7a91c965f
+--- /dev/null
++++ b/src/crypto/crypto_mbedtls.c
+@@ -0,0 +1,4228 @@
++/*
++ * crypto wrapper functions for mbed TLS
++ *
++ * SPDX-FileCopyrightText: 2022 Glenn Strauss <gstrauss@gluelogic.com>
++ * SPDX-License-Identifier: BSD-3-Clause
++ */
++
++#include "utils/includes.h"
++#include "utils/common.h"
++
++#include <mbedtls/version.h>
++#include <mbedtls/entropy.h>
++#include <mbedtls/ctr_drbg.h>
++#include <mbedtls/platform_util.h> /* mbedtls_platform_zeroize() */
++#include <mbedtls/asn1.h>
++#include <mbedtls/asn1write.h>
++#include <mbedtls/aes.h>
++#include <mbedtls/md.h>
++#include <mbedtls/md5.h>
++#include <mbedtls/sha1.h>
++#include <mbedtls/sha256.h>
++#include <mbedtls/sha512.h>
++
++#ifndef MBEDTLS_PRIVATE
++#define MBEDTLS_PRIVATE(x) x
++#endif
++
++/* hostapd/wpa_supplicant provides forced_memzero(),
++ * but prefer mbedtls_platform_zeroize() */
++#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz)
++
++#ifndef __has_attribute
++#define __has_attribute(x) 0
++#endif
++
++#ifndef __GNUC_PREREQ
++#define __GNUC_PREREQ(maj,min) 0
++#endif
++
++#ifndef __attribute_cold__
++#if __has_attribute(cold) \
++ || __GNUC_PREREQ(4,3)
++#define __attribute_cold__  __attribute__((__cold__))
++#else
++#define __attribute_cold__
++#endif
++#endif
++
++#ifndef __attribute_noinline__
++#if __has_attribute(noinline) \
++ || __GNUC_PREREQ(3,1)
++#define __attribute_noinline__  __attribute__((__noinline__))
++#else
++#define __attribute_noinline__
++#endif
++#endif
++
++#include "crypto.h"
++#include "aes_wrap.h"
++#include "aes.h"
++#include "md5.h"
++#include "sha1.h"
++#include "sha256.h"
++#include "sha384.h"
++#include "sha512.h"
++
++
++/*
++ * selective code inclusion based on preprocessor defines
++ *
++ * future: additional code could be wrapped with preprocessor checks if
++ * wpa_supplicant/Makefile and hostap/Makefile were more consistent with
++ * setting preprocessor defines for named groups of functionality
++ */
++
++#if defined(CONFIG_FIPS)
++#undef MBEDTLS_MD4_C     /* omit md4_vector() */
++#undef MBEDTLS_MD5_C     /* omit md5_vector() hmac_md5_vector() hmac_md5() */
++#undef MBEDTLS_DES_C     /* omit des_encrypt() */
++#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */
++#define CRYPTO_MBEDTLS_CONFIG_FIPS
++#endif
++
++#if !defined(CONFIG_FIPS)
++#if defined(EAP_PWD) \
++ || defined(EAP_LEAP) || defined(EAP_LEAP_DYNAMIC) \
++ || defined(EAP_TTLS) || defined(EAP_TTLS_DYNAMIC) \
++ || defined(EAP_MSCHAPv2) || defined(EAP_MSCHAPv2_DYNAMIC) \
++ || defined(EAP_SERVER_MSCHAPV2)
++#ifndef MBEDTLS_MD4_C    /* (MD4 not in mbedtls 3.x) */
++#include "md4-internal.c"/* pull in hostap local implementation */
++#endif /* md4_vector() */
++#else
++#undef MBEDTLS_MD4_C     /* omit md4_vector() */
++#endif
++#endif
++
++#if !defined(CONFIG_NO_RC4) && !defined(CONFIG_NO_WPA)
++#ifndef MBEDTLS_ARC4_C   /* (RC4 not in mbedtls 3.x) */
++#include "rc4.c"         /* pull in hostap local implementation */
++#endif /* rc4_skip() */
++#else
++#undef MBEDTLS_ARC4_C    /* omit rc4_skip() */
++#endif
++
++#if defined(CONFIG_MACSEC)     \
++ || defined(CONFIG_NO_RADIUS)  \
++ || defined(CONFIG_IEEE80211R) \
++ || defined(EAP_SERVER_FAST)   \
++ || defined(EAP_SERVER_TEAP)   \
++ || !defined(CONFIG_NO_WPA)
++       /* aes_wrap() aes_unwrap() */
++#else
++#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */
++#endif
++
++#if !defined(CONFIG_SHA256)
++#undef MBEDTLS_SHA256_C
++#endif
++
++#if !defined(CONFIG_SHA384) && !defined(CONFIG_SHA512)
++#undef MBEDTLS_SHA512_C
++#endif
++
++#if defined(CONFIG_HMAC_SHA256_KDF)
++#define CRYPTO_MBEDTLS_HMAC_KDF_SHA256
++#endif
++#if defined(CONFIG_HMAC_SHA384_KDF)
++#define CRYPTO_MBEDTLS_HMAC_KDF_SHA384
++#endif
++#if defined(CONFIG_HMAC_SHA512_KDF)
++#define CRYPTO_MBEDTLS_HMAC_KDF_SHA512
++#endif
++
++#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \
++ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA)
++/* EAP_SIM=y EAP_AKA=y */
++#define CRYPTO_MBEDTLS_FIPS186_2_PRF
++#endif
++
++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
++ || defined(EAP_TEAP) || defined(EAP_TEAP_DYNAMIC) || defined(EAP_SERVER_FAST)
++#define CRYPTO_MBEDTLS_SHA1_T_PRF
++#endif
++
++#if defined(CONFIG_DES)
++#define CRYPTO_MBEDTLS_DES_ENCRYPT
++#endif /* des_encrypt() */
++
++#if !defined(CONFIG_NO_PBKDF2)
++#define CRYPTO_MBEDTLS_PBKDF2_SHA1
++#endif /* pbkdf2_sha1() */
++
++#if defined(EAP_IKEV2) \
++ || defined(EAP_IKEV2_DYNAMIC) \
++ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */
++#define CRYPTO_MBEDTLS_CRYPTO_CIPHER
++#endif /* crypto_cipher_*() */
++
++#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */
++#define CRYPTO_MBEDTLS_CRYPTO_HASH
++#endif /* crypto_hash_*() */
++
++#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */ \
++ || defined(CONFIG_SAE) /* CONFIG_SAE=y */
++#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++#endif /* crypto_bignum_*() */
++
++#if defined(EAP_PWD)          /* CONFIG_EAP_PWD=y */    \
++ || defined(EAP_EKE)          /* CONFIG_EAP_EKE=y */    \
++ || defined(EAP_EKE_DYNAMIC)  /* CONFIG_EAP_EKE=y */    \
++ || defined(EAP_SERVER_EKE)   /* CONFIG_EAP_EKE=y */    \
++ || defined(EAP_IKEV2)        /* CONFIG_EAP_IKEV2y */   \
++ || defined(EAP_IKEV2_DYNAMIC)/* CONFIG_EAP_IKEV2=y */  \
++ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */  \
++ || defined(CONFIG_SAE)       /* CONFIG_SAE=y */        \
++ || defined(CONFIG_WPS)       /* CONFIG_WPS=y */
++#define CRYPTO_MBEDTLS_CRYPTO_DH
++#if defined(CONFIG_WPS_NFC)
++#define CRYPTO_MBEDTLS_DH5_INIT_FIXED
++#endif /* dh5_init_fixed() */
++#endif /* crypto_dh_*() */
++
++#if !defined(CONFIG_NO_WPA) /* CONFIG_NO_WPA= */
++#define CRYPTO_MBEDTLS_CRYPTO_ECDH
++#endif /* crypto_ecdh_*() */
++
++#if defined(CONFIG_ECC)
++#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++#define CRYPTO_MBEDTLS_CRYPTO_EC
++#endif /* crypto_ec_*() crypto_ec_key_*() */
++
++#if defined(CONFIG_DPP) /* CONFIG_DPP=y */
++#define CRYPTO_MBEDTLS_CRYPTO_EC_DPP /* extra for DPP */
++#define CRYPTO_MBEDTLS_CRYPTO_CSR
++#endif /* crypto_csr_*() */
++
++#if defined(CONFIG_DPP3) /* CONFIG_DPP3=y */
++#define CRYPTO_MBEDTLS_CRYPTO_HPKE
++#endif
++
++#if defined(CONFIG_DPP2) /* CONFIG_DPP2=y */
++#define CRYPTO_MBEDTLS_CRYPTO_PKCS7
++#endif /* crypto_pkcs7_*() */
++
++#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \
++ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA) \
++ || defined(CONFIG_AP) || defined(HOSTAPD)
++/* CONFIG_EAP_SIM=y CONFIG_EAP_AKA=y CONFIG_AP=y HOSTAPD */
++#if defined(CRYPTO_RSA_OAEP_SHA256)
++#define CRYPTO_MBEDTLS_CRYPTO_RSA
++#endif
++#endif /* crypto_rsa_*() */
++
++
++static int ctr_drbg_init_state;
++static mbedtls_ctr_drbg_context ctr_drbg;
++static mbedtls_entropy_context entropy;
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++#include <mbedtls/bignum.h>
++static mbedtls_mpi mpi_sw_A;
++#endif
++
++__attribute_cold__
++__attribute_noinline__
++static mbedtls_ctr_drbg_context * ctr_drbg_init(void)
++{
++	mbedtls_ctr_drbg_init(&ctr_drbg);
++	mbedtls_entropy_init(&entropy);
++	if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
++	                          NULL, 0)) {
++		wpa_printf(MSG_ERROR, "Init of random number generator failed");
++		/* XXX: abort? */
++	}
++	else
++		ctr_drbg_init_state = 1;
++
++	return &ctr_drbg;
++}
++
++__attribute_cold__
++void crypto_unload(void)
++{
++	if (ctr_drbg_init_state) {
++		mbedtls_ctr_drbg_free(&ctr_drbg);
++		mbedtls_entropy_free(&entropy);
++	  #ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++		mbedtls_mpi_free(&mpi_sw_A);
++	  #endif
++		ctr_drbg_init_state = 0;
++	}
++}
++
++/* init ctr_drbg on first use
++ * crypto_global_init() and crypto_global_deinit() are not available here
++ * (available only when CONFIG_TLS=internal, which is not CONFIG_TLS=mbedtls) */
++mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/
++inline
++mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void)
++{
++	return ctr_drbg_init_state ? &ctr_drbg : ctr_drbg_init();
++}
++
++#ifdef CRYPTO_MBEDTLS_CONFIG_FIPS
++int crypto_get_random(void *buf, size_t len)
++{
++	return mbedtls_ctr_drbg_random(crypto_mbedtls_ctr_drbg(),buf,len) ? -1 : 0;
++}
++#endif
++
++
++#if 1
++
++/* tradeoff: slightly smaller code size here at cost of slight increase
++ * in instructions and function calls at runtime versus the expanded
++ * per-message-digest code that follows in #else (~0.5 kib .text larger) */
++
++__attribute_noinline__
++static int md_vector(size_t num_elem, const u8 *addr[], const size_t *len,
++                     u8 *mac, mbedtls_md_type_t md_type)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	mbedtls_md_context_t ctx;
++	mbedtls_md_init(&ctx);
++	if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0) != 0){
++		mbedtls_md_free(&ctx);
++		return -1;
++	}
++	mbedtls_md_starts(&ctx);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_md_update(&ctx, addr[i], len[i]);
++	mbedtls_md_finish(&ctx, mac);
++	mbedtls_md_free(&ctx);
++	return 0;
++}
++
++#ifdef MBEDTLS_SHA512_C
++int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA512);
++}
++
++int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA384);
++}
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA256);
++}
++#endif
++
++#ifdef MBEDTLS_SHA1_C
++int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA1);
++}
++#endif
++
++#ifdef MBEDTLS_MD5_C
++int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD5);
++}
++#endif
++
++#ifdef MBEDTLS_MD4_C
++#include <mbedtls/md4.h>
++int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD4);
++}
++#endif
++
++#else  /* expanded per-message-digest functions */
++
++#ifdef MBEDTLS_SHA512_C
++#include <mbedtls/sha512.h>
++__attribute_noinline__
++static int sha384_512_vector(size_t num_elem, const u8 *addr[],
++                             const size_t *len, u8 *mac, int is384)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	struct mbedtls_sha512_context ctx;
++	mbedtls_sha512_init(&ctx);
++  #if MBEDTLS_VERSION_MAJOR >= 3
++	mbedtls_sha512_starts(&ctx, is384);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_sha512_update(&ctx, addr[i], len[i]);
++	mbedtls_sha512_finish(&ctx, mac);
++  #else
++	mbedtls_sha512_starts_ret(&ctx, is384);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_sha512_update_ret(&ctx, addr[i], len[i]);
++	mbedtls_sha512_finish_ret(&ctx, mac);
++  #endif
++	mbedtls_sha512_free(&ctx);
++	return 0;
++}
++
++int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return sha384_512_vector(num_elem, addr, len, mac, 0);
++}
++
++int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return sha384_512_vector(num_elem, addr, len, mac, 1);
++}
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++#include <mbedtls/sha256.h>
++int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	struct mbedtls_sha256_context ctx;
++	mbedtls_sha256_init(&ctx);
++  #if MBEDTLS_VERSION_MAJOR >= 3
++	mbedtls_sha256_starts(&ctx, 0);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_sha256_update(&ctx, addr[i], len[i]);
++	mbedtls_sha256_finish(&ctx, mac);
++  #else
++	mbedtls_sha256_starts_ret(&ctx, 0);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_sha256_update_ret(&ctx, addr[i], len[i]);
++	mbedtls_sha256_finish_ret(&ctx, mac);
++  #endif
++	mbedtls_sha256_free(&ctx);
++	return 0;
++}
++#endif
++
++#ifdef MBEDTLS_SHA1_C
++#include <mbedtls/sha1.h>
++int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	struct mbedtls_sha1_context ctx;
++	mbedtls_sha1_init(&ctx);
++  #if MBEDTLS_VERSION_MAJOR >= 3
++	mbedtls_sha1_starts(&ctx);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_sha1_update(&ctx, addr[i], len[i]);
++	mbedtls_sha1_finish(&ctx, mac);
++  #else
++	mbedtls_sha1_starts_ret(&ctx);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_sha1_update_ret(&ctx, addr[i], len[i]);
++	mbedtls_sha1_finish_ret(&ctx, mac);
++  #endif
++	mbedtls_sha1_free(&ctx);
++	return 0;
++}
++#endif
++
++#ifdef MBEDTLS_MD5_C
++#include <mbedtls/md5.h>
++int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	struct mbedtls_md5_context ctx;
++	mbedtls_md5_init(&ctx);
++  #if MBEDTLS_VERSION_MAJOR >= 3
++	mbedtls_md5_starts(&ctx);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_md5_update(&ctx, addr[i], len[i]);
++	mbedtls_md5_finish(&ctx, mac);
++  #else
++	mbedtls_md5_starts_ret(&ctx);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_md5_update_ret(&ctx, addr[i], len[i]);
++	mbedtls_md5_finish_ret(&ctx, mac);
++  #endif
++	mbedtls_md5_free(&ctx);
++	return 0;
++}
++#endif
++
++#ifdef MBEDTLS_MD4_C
++#include <mbedtls/md4.h>
++int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	struct mbedtls_md4_context ctx;
++	mbedtls_md4_init(&ctx);
++	mbedtls_md4_starts_ret(&ctx);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_md4_update_ret(&ctx, addr[i], len[i]);
++	mbedtls_md4_finish_ret(&ctx, mac);
++	mbedtls_md4_free(&ctx);
++	return 0;
++}
++#endif
++
++#endif /* expanded per-message-digest functions */
++
++
++__attribute_noinline__
++static int hmac_vector(const u8 *key, size_t key_len, size_t num_elem,
++                       const u8 *addr[], const size_t *len, u8 *mac,
++                       mbedtls_md_type_t md_type)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	mbedtls_md_context_t ctx;
++	mbedtls_md_init(&ctx);
++	if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1) != 0){
++		mbedtls_md_free(&ctx);
++		return -1;
++	}
++	mbedtls_md_hmac_starts(&ctx, key, key_len);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++	mbedtls_md_hmac_finish(&ctx, mac);
++	mbedtls_md_free(&ctx);
++	return 0;
++}
++
++#ifdef MBEDTLS_SHA512_C
++int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
++                       const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return hmac_vector(key, key_len, num_elem, addr, len, mac,
++			   MBEDTLS_MD_SHA512);
++}
++
++int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++                u8 *mac)
++{
++	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++			   MBEDTLS_MD_SHA512);
++}
++
++int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
++                       const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return hmac_vector(key, key_len, num_elem, addr, len, mac,
++			   MBEDTLS_MD_SHA384);
++}
++
++int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++                u8 *mac)
++{
++	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++			   MBEDTLS_MD_SHA384);
++}
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
++                       const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return hmac_vector(key, key_len, num_elem, addr, len, mac,
++			   MBEDTLS_MD_SHA256);
++}
++
++int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++                u8 *mac)
++{
++	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++			   MBEDTLS_MD_SHA256);
++}
++#endif
++
++#ifdef MBEDTLS_SHA1_C
++int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
++                     const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return hmac_vector(key, key_len, num_elem, addr, len, mac,
++			   MBEDTLS_MD_SHA1);
++}
++
++int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++              u8 *mac)
++{
++	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++			   MBEDTLS_MD_SHA1);
++}
++#endif
++
++#ifdef MBEDTLS_MD5_C
++int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
++                    const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return hmac_vector(key, key_len, num_elem, addr, len, mac,
++			   MBEDTLS_MD_MD5);
++}
++
++int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++             u8 *mac)
++{
++	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++			   MBEDTLS_MD_MD5);
++}
++#endif
++
++
++#if defined(MBEDTLS_SHA256_C) || defined(MBEDTLS_SHA512_C)
++
++#if defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA256) \
++ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA384) \
++ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA512)
++
++#include <mbedtls/hkdf.h>
++
++/* sha256-kdf.c sha384-kdf.c sha512-kdf.c */
++
++/* HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869) */
++/* HMAC-SHA384 KDF (RFC 5295) and HKDF-Expand(SHA384) (RFC 5869) */
++/* HMAC-SHA512 KDF (RFC 5295) and HKDF-Expand(SHA512) (RFC 5869) */
++__attribute_noinline__
++static int hmac_kdf_expand(const u8 *prk, size_t prk_len,
++                           const char *label, const u8 *info, size_t info_len,
++                           u8 *okm, size_t okm_len, mbedtls_md_type_t md_type)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
++  #ifdef MBEDTLS_HKDF_C
++	if (label == NULL)  /* RFC 5869 HKDF-Expand when (label == NULL) */
++		return mbedtls_hkdf_expand(md_info, prk, prk_len, info,
++		                           info_len, okm, okm_len) ? -1 : 0;
++  #endif
++
++	const size_t mac_len = mbedtls_md_get_size(md_info);
++	/* okm_len must not exceed 255 times hash len (RFC 5869 Section 2.3) */
++	if (okm_len > ((mac_len << 8) - mac_len))
++		return -1;
++
++	mbedtls_md_context_t ctx;
++	mbedtls_md_init(&ctx);
++	if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
++		mbedtls_md_free(&ctx);
++		return -1;
++	}
++	mbedtls_md_hmac_starts(&ctx, prk, prk_len);
++
++	u8 iter = 1;
++	const u8 *addr[4] = { okm, (const u8 *)label, info, &iter };
++	size_t len[4] = { 0, label ? os_strlen(label)+1 : 0, info_len, 1 };
++
++	for (; okm_len >= mac_len; okm_len -= mac_len, ++iter) {
++		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
++			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++		mbedtls_md_hmac_finish(&ctx, okm);
++		mbedtls_md_hmac_reset(&ctx);
++		addr[0] = okm;
++		okm += mac_len;
++		len[0] = mac_len; /*(include digest in subsequent rounds)*/
++	}
++
++	if (okm_len) {
++		u8 hash[MBEDTLS_MD_MAX_SIZE];
++		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
++			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++		mbedtls_md_hmac_finish(&ctx, hash);
++		os_memcpy(okm, hash, okm_len);
++		forced_memzero(hash, mac_len);
++	}
++
++	mbedtls_md_free(&ctx);
++	return 0;
++}
++
++#ifdef MBEDTLS_SHA512_C
++#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA512
++int hmac_sha512_kdf(const u8 *secret, size_t secret_len,
++		    const char *label, const u8 *seed, size_t seed_len,
++		    u8 *out, size_t outlen)
++{
++	return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
++	                       out, outlen, MBEDTLS_MD_SHA512);
++}
++#endif
++
++#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA384
++int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
++		    const char *label, const u8 *seed, size_t seed_len,
++		    u8 *out, size_t outlen)
++{
++	return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
++	                       out, outlen, MBEDTLS_MD_SHA384);
++}
++#endif
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA256
++int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
++		    const char *label, const u8 *seed, size_t seed_len,
++		    u8 *out, size_t outlen)
++{
++	return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
++	                       out, outlen, MBEDTLS_MD_SHA256);
++}
++#endif
++#endif
++
++#endif /* CRYPTO_MBEDTLS_HMAC_KDF_* */
++
++
++/* sha256-prf.c sha384-prf.c sha512-prf.c */
++
++/* hmac_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function */
++__attribute_noinline__
++static int hmac_prf_bits(const u8 *key, size_t key_len, const char *label,
++                         const u8 *data, size_t data_len, u8 *buf,
++                         size_t buf_len_bits, mbedtls_md_type_t md_type)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	mbedtls_md_context_t ctx;
++	mbedtls_md_init(&ctx);
++	const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
++	if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
++		mbedtls_md_free(&ctx);
++		return -1;
++	}
++	mbedtls_md_hmac_starts(&ctx, key, key_len);
++
++	u16 ctr, n_le = host_to_le16(buf_len_bits);
++	const u8 * const addr[] = { (u8 *)&ctr,(u8 *)label,data,(u8 *)&n_le };
++	const size_t len[] =      { 2, os_strlen(label), data_len, 2 };
++	const size_t mac_len = mbedtls_md_get_size(md_info);
++	size_t buf_len = (buf_len_bits + 7) / 8;
++	for (ctr = 1; buf_len >= mac_len; buf_len -= mac_len, ++ctr) {
++	  #if __BYTE_ORDER == __BIG_ENDIAN
++		ctr = host_to_le16(ctr);
++	  #endif
++		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
++			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++		mbedtls_md_hmac_finish(&ctx, buf);
++		mbedtls_md_hmac_reset(&ctx);
++		buf += mac_len;
++	  #if __BYTE_ORDER == __BIG_ENDIAN
++		ctr = le_to_host16(ctr);
++	  #endif
++	}
++
++	if (buf_len) {
++		u8 hash[MBEDTLS_MD_MAX_SIZE];
++	  #if __BYTE_ORDER == __BIG_ENDIAN
++		ctr = host_to_le16(ctr);
++	  #endif
++		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
++			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++		mbedtls_md_hmac_finish(&ctx, hash);
++		os_memcpy(buf, hash, buf_len);
++		buf += buf_len;
++		forced_memzero(hash, mac_len);
++	}
++
++	/* Mask out unused bits in last octet if it does not use all the bits */
++	if ((buf_len_bits &= 0x7))
++		buf[-1] &= (u8)(0xff << (8 - buf_len_bits));
++
++	mbedtls_md_free(&ctx);
++	return 0;
++}
++
++#ifdef MBEDTLS_SHA512_C
++int sha512_prf(const u8 *key, size_t key_len, const char *label,
++               const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
++{
++	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
++	                     buf_len * 8, MBEDTLS_MD_SHA512);
++}
++
++int sha384_prf(const u8 *key, size_t key_len, const char *label,
++               const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
++{
++	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
++	                     buf_len * 8, MBEDTLS_MD_SHA384);
++}
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++int sha256_prf(const u8 *key, size_t key_len, const char *label,
++               const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
++{
++	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
++	                     buf_len * 8, MBEDTLS_MD_SHA256);
++}
++
++int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
++                    const u8 *data, size_t data_len, u8 *buf,
++                    size_t buf_len_bits)
++{
++	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
++	                     buf_len_bits, MBEDTLS_MD_SHA256);
++}
++#endif
++
++#endif /* MBEDTLS_SHA256_C || MBEDTLS_SHA512_C */
++
++
++#ifdef MBEDTLS_SHA1_C
++
++/* sha1-prf.c */
++
++/* sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) */
++
++int sha1_prf(const u8 *key, size_t key_len, const char *label,
++	     const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
++{
++	/*(note: algorithm differs from hmac_prf_bits() */
++	/*(note: smaller code size instead of expanding hmac_sha1_vector()
++	 * as is done in hmac_prf_bits(); not expecting large num of loops) */
++	u8 counter = 0;
++	const u8 *addr[] = { (u8 *)label, data, &counter };
++	const size_t len[] = { os_strlen(label)+1, data_len, 1 };
++
++	for (; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++counter) {
++		if (hmac_sha1_vector(key, key_len, 3, addr, len, buf))
++			return -1;
++		buf += SHA1_MAC_LEN;
++	}
++
++	if (buf_len) {
++		u8 hash[SHA1_MAC_LEN];
++		if (hmac_sha1_vector(key, key_len, 3, addr, len, hash))
++			return -1;
++		os_memcpy(buf, hash, buf_len);
++		forced_memzero(hash, sizeof(hash));
++	}
++
++	return 0;
++}
++
++#ifdef CRYPTO_MBEDTLS_SHA1_T_PRF
++
++/* sha1-tprf.c */
++
++/* sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF) (RFC 4851,Section 5.5)*/
++
++int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
++	       const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len)
++{
++	/*(note: algorithm differs from hmac_prf_bits() and hmac_kdf() above)*/
++	/*(note: smaller code size instead of expanding hmac_sha1_vector()
++	 * as is done in hmac_prf_bits(); not expecting large num of loops) */
++	u8 ctr;
++	u16 olen = host_to_be16(buf_len);
++	const u8 *addr[] = { buf, (u8 *)label, seed, (u8 *)&olen, &ctr };
++	size_t len[] = { 0, os_strlen(label)+1, seed_len, 2, 1 };
++
++	for (ctr = 1; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++ctr) {
++		if (hmac_sha1_vector(key, key_len, 5, addr, len, buf))
++			return -1;
++		addr[0] = buf;
++		buf += SHA1_MAC_LEN;
++		len[0] = SHA1_MAC_LEN; /*(include digest in subsequent rounds)*/
++	}
++
++	if (buf_len) {
++		u8 hash[SHA1_MAC_LEN];
++		if (hmac_sha1_vector(key, key_len, 5, addr, len, hash))
++			return -1;
++		os_memcpy(buf, hash, buf_len);
++		forced_memzero(hash, sizeof(hash));
++	}
++
++	return 0;
++}
++
++#endif /* CRYPTO_MBEDTLS_SHA1_T_PRF */
++
++#ifdef CRYPTO_MBEDTLS_FIPS186_2_PRF
++
++/* fips_prf_internal.c sha1-internal.c */
++
++/* used only by src/eap_common/eap_sim_common.c:eap_sim_prf()
++ * for eap_sim_derive_keys() and eap_sim_derive_keys_reauth()
++ * where xlen is 160 */
++
++int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
++{
++	/* FIPS 186-2 + change notice 1 */
++
++	mbedtls_sha1_context ctx;
++	u8 * const xkey = ctx.MBEDTLS_PRIVATE(buffer);
++	u32 * const xstate = ctx.MBEDTLS_PRIVATE(state);
++	const u32 xstate_init[] =
++	  { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 };
++
++	mbedtls_sha1_init(&ctx);
++	os_memcpy(xkey, seed, seed_len < 64 ? seed_len : 64);
++
++	/* note: does not fill extra bytes if (xlen % 20) (SHA1_MAC_LEN) */
++	for (; xlen >= 20; xlen -= 20) {
++		/* XSEED_j = 0 */
++		/* XVAL = (XKEY + XSEED_j) mod 2^b */
++
++		/* w_i = G(t, XVAL) */
++		os_memcpy(xstate, xstate_init, sizeof(xstate_init));
++		mbedtls_internal_sha1_process(&ctx, xkey);
++
++	  #if __BYTE_ORDER == __LITTLE_ENDIAN
++		xstate[0] = host_to_be32(xstate[0]);
++		xstate[1] = host_to_be32(xstate[1]);
++		xstate[2] = host_to_be32(xstate[2]);
++		xstate[3] = host_to_be32(xstate[3]);
++		xstate[4] = host_to_be32(xstate[4]);
++	  #endif
++		os_memcpy(x, xstate, 20);
++		if (xlen == 20) /*(done; skip prep for next loop)*/
++			break;
++
++		/* XKEY = (1 + XKEY + w_i) mod 2^b */
++		for (u32 carry = 1, k = 20; k-- > 0; carry >>= 8)
++			xkey[k] = (carry += xkey[k] + x[k]) & 0xff;
++		x += 20;
++		/* x_j = w_0|w_1 (each pair of iterations through loop)*/
++	}
++
++	mbedtls_sha1_free(&ctx);
++	return 0;
++}
++
++#endif /* CRYPTO_MBEDTLS_FIPS186_2_PRF */
++
++#endif /* MBEDTLS_SHA1_C */
++
++
++#ifdef CRYPTO_MBEDTLS_DES_ENCRYPT
++#ifdef MBEDTLS_DES_C
++#include <mbedtls/des.h>
++int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
++{
++	u8 pkey[8], next, tmp;
++	int i;
++
++	/* Add parity bits to the key */
++	next = 0;
++	for (i = 0; i < 7; i++) {
++		tmp = key[i];
++		pkey[i] = (tmp >> i) | next | 1;
++		next = tmp << (7 - i);
++	}
++	pkey[i] = next | 1;
++
++	mbedtls_des_context des;
++	mbedtls_des_init(&des);
++	int ret = mbedtls_des_setkey_enc(&des, pkey)
++	       || mbedtls_des_crypt_ecb(&des, clear, cypher) ? -1 : 0;
++	mbedtls_des_free(&des);
++	return ret;
++}
++#else
++#include "des-internal.c"/* pull in hostap local implementation */
++#endif
++#endif
++
++
++#ifdef CRYPTO_MBEDTLS_PBKDF2_SHA1
++/* sha1-pbkdf2.c */
++#include <mbedtls/pkcs5.h>
++int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
++                int iterations, u8 *buf, size_t buflen)
++{
++  #if MBEDTLS_VERSION_NUMBER >= 0x03020200 /* mbedtls 3.2.2 */
++	return mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA1,
++			(const u8 *)passphrase, os_strlen(passphrase),
++			ssid, ssid_len, iterations, 32, buf) ? -1 : 0;
++  #else
++	const mbedtls_md_info_t *md_info;
++	md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
++	if (md_info == NULL)
++		return -1;
++	mbedtls_md_context_t ctx;
++	mbedtls_md_init(&ctx);
++	int ret = mbedtls_md_setup(&ctx, md_info, 1)
++	       || mbedtls_pkcs5_pbkdf2_hmac(&ctx,
++			(const u8 *)passphrase, os_strlen(passphrase),
++			ssid, ssid_len, iterations, 32, buf) ? -1 : 0;
++	mbedtls_md_free(&ctx);
++	return ret;
++  #endif
++}
++#endif
++
++
++/*#include "aes.h"*/ /* prototypes also included in "crypto.h" */
++
++static void *aes_crypt_init_mode(const u8 *key, size_t len, int mode)
++{
++	if (TEST_FAIL())
++		return NULL;
++
++	mbedtls_aes_context *aes = os_malloc(sizeof(*aes));
++	if (!aes)
++		return NULL;
++
++	mbedtls_aes_init(aes);
++	if ((mode == MBEDTLS_AES_ENCRYPT
++	    ? mbedtls_aes_setkey_enc(aes, key, len * 8)
++	    : mbedtls_aes_setkey_dec(aes, key, len * 8)) == 0)
++		return aes;
++
++	mbedtls_aes_free(aes);
++	os_free(aes);
++	return NULL;
++}
++
++void *aes_encrypt_init(const u8 *key, size_t len)
++{
++	return aes_crypt_init_mode(key, len, MBEDTLS_AES_ENCRYPT);
++}
++
++int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
++{
++	return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, plain, crypt);
++}
++
++void aes_encrypt_deinit(void *ctx)
++{
++	mbedtls_aes_free(ctx);
++	os_free(ctx);
++}
++
++void *aes_decrypt_init(const u8 *key, size_t len)
++{
++	return aes_crypt_init_mode(key, len, MBEDTLS_AES_DECRYPT);
++}
++
++int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
++{
++	return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_DECRYPT, crypt, plain);
++}
++
++void aes_decrypt_deinit(void *ctx)
++{
++	mbedtls_aes_free(ctx);
++	os_free(ctx);
++}
++
++
++#include "aes_wrap.h"
++
++
++#ifdef MBEDTLS_NIST_KW_C
++
++#include <mbedtls/nist_kw.h>
++
++/* aes-wrap.c */
++int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	mbedtls_nist_kw_context ctx;
++	mbedtls_nist_kw_init(&ctx);
++	size_t olen;
++	int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
++	                                 kek, kek_len*8, 1)
++	       || mbedtls_nist_kw_wrap(&ctx, MBEDTLS_KW_MODE_KW, plain, n*8,
++	                               cipher, &olen, (n+1)*8) ? -1 : 0;
++	mbedtls_nist_kw_free(&ctx);
++	return ret;
++}
++
++/* aes-unwrap.c */
++int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, u8 *plain)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	mbedtls_nist_kw_context ctx;
++	mbedtls_nist_kw_init(&ctx);
++	size_t olen;
++	int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
++	                                 kek, kek_len*8, 0)
++	       || mbedtls_nist_kw_unwrap(&ctx, MBEDTLS_KW_MODE_KW, cipher,
++	                                 (n+1)*8, plain, &olen, n*8) ? -1 : 0;
++	mbedtls_nist_kw_free(&ctx);
++	return ret;
++}
++
++#else
++
++#ifndef CRYPTO_MBEDTLS_CONFIG_FIPS
++#include "aes-wrap.c"    /* pull in hostap local implementation */
++#include "aes-unwrap.c"  /* pull in hostap local implementation */
++#endif
++
++#endif /* MBEDTLS_NIST_KW_C */
++
++
++#ifdef MBEDTLS_CMAC_C
++
++/* aes-omac1.c */
++
++#include <mbedtls/cmac.h>
++
++int omac1_aes_vector(
++    const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[],
++    const size_t *len, u8 *mac)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	mbedtls_cipher_type_t cipher_type;
++	switch (key_len) {
++	case 16: cipher_type = MBEDTLS_CIPHER_AES_128_ECB; break;
++	case 24: cipher_type = MBEDTLS_CIPHER_AES_192_ECB; break;
++	case 32: cipher_type = MBEDTLS_CIPHER_AES_256_ECB; break;
++	default: return -1;
++	}
++	const mbedtls_cipher_info_t *cipher_info;
++	cipher_info = mbedtls_cipher_info_from_type(cipher_type);
++	if (cipher_info == NULL)
++		return -1;
++
++	mbedtls_cipher_context_t ctx;
++	mbedtls_cipher_init(&ctx);
++	int ret = -1;
++	if (mbedtls_cipher_setup(&ctx, cipher_info) == 0
++	    && mbedtls_cipher_cmac_starts(&ctx, key, key_len*8) == 0) {
++		ret = 0;
++		for (size_t i = 0; i < num_elem && ret == 0; ++i)
++			ret = mbedtls_cipher_cmac_update(&ctx, addr[i], len[i]);
++	}
++	if (ret == 0)
++		ret = mbedtls_cipher_cmac_finish(&ctx, mac);
++	mbedtls_cipher_free(&ctx);
++	return ret ? -1 : 0;
++}
++
++int omac1_aes_128_vector(const u8 *key, size_t num_elem,
++			 const u8 *addr[], const size_t *len,
++			 u8 *mac)
++{
++	return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
++}
++
++int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
++{
++	return omac1_aes_vector(key, 16, 1, &data, &data_len, mac);
++}
++
++int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
++{
++	return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
++}
++
++#else
++
++#include "aes-omac1.c"  /* pull in hostap local implementation */
++
++#ifndef MBEDTLS_AES_BLOCK_SIZE
++#define MBEDTLS_AES_BLOCK_SIZE 16
++#endif
++
++#endif /* MBEDTLS_CMAC_C */
++
++
++/* These interfaces can be inefficient when used in loops, as the overhead of
++ * initialization each call is large for each block input (e.g. 16 bytes) */
++
++
++/* aes-encblock.c */
++int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	mbedtls_aes_context aes;
++	mbedtls_aes_init(&aes);
++	int ret = mbedtls_aes_setkey_enc(&aes, key, 128)
++	       || mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_ENCRYPT, in, out)
++	  ? -1
++	  : 0;
++	mbedtls_aes_free(&aes);
++	return ret;
++}
++
++
++/* aes-ctr.c */
++int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
++		    u8 *data, size_t data_len)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	unsigned char counter[MBEDTLS_AES_BLOCK_SIZE];
++	unsigned char stream_block[MBEDTLS_AES_BLOCK_SIZE];
++	os_memcpy(counter, nonce, MBEDTLS_AES_BLOCK_SIZE);/*(must be writable)*/
++
++	mbedtls_aes_context ctx;
++	mbedtls_aes_init(&ctx);
++	size_t nc_off = 0;
++	int ret = mbedtls_aes_setkey_enc(&ctx, key, key_len*8)
++	       || mbedtls_aes_crypt_ctr(&ctx, data_len, &nc_off,
++	                                counter, stream_block,
++	                                data, data) ? -1 : 0;
++	forced_memzero(stream_block, sizeof(stream_block));
++	mbedtls_aes_free(&ctx);
++	return ret;
++}
++
++int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
++			u8 *data, size_t data_len)
++{
++	return aes_ctr_encrypt(key, 16, nonce, data, data_len);
++}
++
++
++/* aes-cbc.c */
++static int aes_128_cbc_oper(const u8 *key, const u8 *iv,
++                            u8 *data, size_t data_len, int mode)
++{
++	unsigned char ivec[MBEDTLS_AES_BLOCK_SIZE];
++	os_memcpy(ivec, iv, MBEDTLS_AES_BLOCK_SIZE); /*(must be writable)*/
++
++	mbedtls_aes_context ctx;
++	mbedtls_aes_init(&ctx);
++	int ret = (mode == MBEDTLS_AES_ENCRYPT
++	           ? mbedtls_aes_setkey_enc(&ctx, key, 128)
++	           : mbedtls_aes_setkey_dec(&ctx, key, 128))
++	       || mbedtls_aes_crypt_cbc(&ctx, mode, data_len, ivec, data, data);
++	mbedtls_aes_free(&ctx);
++	return ret ? -1 : 0;
++}
++
++int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_ENCRYPT);
++}
++
++int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_DECRYPT);
++}
++
++
++/*
++ * Much of the following is documented in crypto.h as for CONFIG_TLS=internal
++ * but such comments are not accurate:
++ *
++ * "This function is only used with internal TLSv1 implementation
++ *  (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
++ *  to implement this."
++ */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_CIPHER
++
++#include <mbedtls/cipher.h>
++
++struct crypto_cipher
++{
++	mbedtls_cipher_context_t ctx_enc;
++	mbedtls_cipher_context_t ctx_dec;
++};
++
++struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
++					  const u8 *iv, const u8 *key,
++					  size_t key_len)
++{
++	/* IKEv2 src/eap_common/ikev2_common.c:ikev2_{encr,decr}_encrypt()
++	 * uses one of CRYPTO_CIPHER_ALG_AES or CRYPTO_CIPHER_ALG_3DES */
++
++	mbedtls_cipher_type_t cipher_type;
++	size_t iv_len;
++	switch (alg) {
++  #ifdef MBEDTLS_ARC4_C
++  #if 0
++	case CRYPTO_CIPHER_ALG_RC4:
++		cipher_type = MBEDTLS_CIPHER_ARC4_128;
++		iv_len = 0;
++		break;
++  #endif
++  #endif
++  #ifdef MBEDTLS_AES_C
++	case CRYPTO_CIPHER_ALG_AES:
++		if (key_len == 16) cipher_type = MBEDTLS_CIPHER_AES_128_CTR;
++		if (key_len == 24) cipher_type = MBEDTLS_CIPHER_AES_192_CTR;
++		if (key_len == 32) cipher_type = MBEDTLS_CIPHER_AES_256_CTR;
++		iv_len = 16;
++		break;
++  #endif
++  #ifdef MBEDTLS_DES_C
++	case CRYPTO_CIPHER_ALG_3DES:
++		cipher_type = MBEDTLS_CIPHER_DES_EDE3_CBC;
++		iv_len = 8;
++		break;
++  #if 0
++	case CRYPTO_CIPHER_ALG_DES:
++		cipher_type = MBEDTLS_CIPHER_DES_CBC;
++		iv_len = 8;
++		break;
++  #endif
++  #endif
++	default:
++		return NULL;
++	}
++
++	const mbedtls_cipher_info_t *cipher_info;
++	cipher_info = mbedtls_cipher_info_from_type(cipher_type);
++	if (cipher_info == NULL)
++		return NULL;
++
++	key_len *= 8; /* key_bitlen */
++  #if 0 /*(were key_bitlen not already available)*/
++  #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */
++	key_len = mbedtls_cipher_info_get_key_bitlen(cipher_info);
++  #else
++	key_len = cipher_info->MBEDTLS_PRIVATE(key_bitlen);
++  #endif
++  #endif
++
++  #if 0 /*(were iv_len not known above, would need MBEDTLS_PRIVATE(iv_size))*/
++	iv_len = cipher_info->MBEDTLS_PRIVATE(iv_size);
++  #endif
++
++	struct crypto_cipher *ctx = os_malloc(sizeof(*ctx));
++	if (!ctx)
++		return NULL;
++
++	mbedtls_cipher_init(&ctx->ctx_enc);
++	mbedtls_cipher_init(&ctx->ctx_dec);
++	if (   mbedtls_cipher_setup(&ctx->ctx_enc,cipher_info) == 0
++	    && mbedtls_cipher_setup(&ctx->ctx_dec,cipher_info) == 0
++	    && mbedtls_cipher_setkey(&ctx->ctx_enc,key,key_len,MBEDTLS_ENCRYPT) == 0
++	    && mbedtls_cipher_setkey(&ctx->ctx_dec,key,key_len,MBEDTLS_DECRYPT) == 0
++	    && mbedtls_cipher_set_iv(&ctx->ctx_enc,iv,iv_len) == 0
++	    && mbedtls_cipher_set_iv(&ctx->ctx_dec,iv,iv_len) == 0
++	    && mbedtls_cipher_reset(&ctx->ctx_enc) == 0
++	    && mbedtls_cipher_reset(&ctx->ctx_dec) == 0) {
++		return ctx;
++	}
++
++	mbedtls_cipher_free(&ctx->ctx_enc);
++	mbedtls_cipher_free(&ctx->ctx_dec);
++	os_free(ctx);
++	return NULL;
++}
++
++int crypto_cipher_encrypt(struct crypto_cipher *ctx,
++			  const u8 *plain, u8 *crypt, size_t len)
++{
++	size_t olen = 0; /*(poor interface above; unknown size of u8 *crypt)*/
++	return (mbedtls_cipher_update(&ctx->ctx_enc, plain, len, crypt, &olen)
++	        || mbedtls_cipher_finish(&ctx->ctx_enc, crypt + olen, &olen)) ? -1 : 0;
++}
++
++int crypto_cipher_decrypt(struct crypto_cipher *ctx,
++			  const u8 *crypt, u8 *plain, size_t len)
++{
++	size_t olen = 0; /*(poor interface above; unknown size of u8 *plain)*/
++	return (mbedtls_cipher_update(&ctx->ctx_dec, crypt, len, plain, &olen)
++	        || mbedtls_cipher_finish(&ctx->ctx_dec, plain + olen, &olen)) ? -1 : 0;
++}
++
++void crypto_cipher_deinit(struct crypto_cipher *ctx)
++{
++	mbedtls_cipher_free(&ctx->ctx_enc);
++	mbedtls_cipher_free(&ctx->ctx_dec);
++	os_free(ctx);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_CIPHER */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_HASH
++
++struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
++				      size_t key_len)
++{
++	mbedtls_md_type_t md_type;
++	int is_hmac = 0;
++
++	switch (alg) {
++  #ifdef MBEDTLS_MD5_C
++	case CRYPTO_HASH_ALG_MD5:
++		md_type = MBEDTLS_MD_MD5;
++		break;
++  #endif
++  #ifdef MBEDTLS_SHA1_C
++	case CRYPTO_HASH_ALG_SHA1:
++		md_type = MBEDTLS_MD_SHA1;
++		break;
++  #endif
++  #ifdef MBEDTLS_MD5_C
++	case CRYPTO_HASH_ALG_HMAC_MD5:
++		md_type = MBEDTLS_MD_MD5;
++		is_hmac = 1;
++		break;
++  #endif
++  #ifdef MBEDTLS_SHA1_C
++	case CRYPTO_HASH_ALG_HMAC_SHA1:
++		md_type = MBEDTLS_MD_SHA1;
++		is_hmac = 1;
++		break;
++  #endif
++  #ifdef MBEDTLS_SHA256_C
++	case CRYPTO_HASH_ALG_SHA256:
++		md_type = MBEDTLS_MD_SHA256;
++		break;
++	case CRYPTO_HASH_ALG_HMAC_SHA256:
++		md_type = MBEDTLS_MD_SHA256;
++		is_hmac = 1;
++		break;
++  #endif
++  #ifdef MBEDTLS_SHA512_C
++	case CRYPTO_HASH_ALG_SHA384:
++		md_type = MBEDTLS_MD_SHA384;
++		break;
++	case CRYPTO_HASH_ALG_SHA512:
++		md_type = MBEDTLS_MD_SHA512;
++		break;
++  #endif
++	default:
++		return NULL;
++	}
++
++	const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
++	if (!md_info)
++		return NULL;
++
++	mbedtls_md_context_t *mctx = os_malloc(sizeof(*mctx));
++	if (mctx == NULL)
++		return NULL;
++
++	mbedtls_md_init(mctx);
++	if (mbedtls_md_setup(mctx, md_info, is_hmac) != 0) {
++		os_free(mctx);
++		return NULL;
++	}
++
++	if (is_hmac)
++		mbedtls_md_hmac_starts(mctx, key, key_len);
++	else
++		mbedtls_md_starts(mctx);
++	return (struct crypto_hash *)((uintptr_t)mctx | is_hmac);
++}
++
++void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
++{
++	mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL);
++  #if 0
++	/*(mbedtls_md_hmac_update() and mbedtls_md_update()
++	 * make same modifications under the hood in mbedtls)*/
++	if ((uintptr_t)ctx & 1uL)
++		mbedtls_md_hmac_update(mctx, data, len);
++	else
++  #endif
++		mbedtls_md_update(mctx, data, len);
++}
++
++int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
++{
++	mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL);
++	if (mac != NULL && len != NULL) { /*(NULL if caller just freeing context)*/
++	  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
++		const mbedtls_md_info_t *md_info = mbedtls_md_info_from_ctx(mctx);
++	  #else
++		const mbedtls_md_info_t *md_info = mctx->MBEDTLS_PRIVATE(md_info);
++	  #endif
++		size_t maclen = mbedtls_md_get_size(md_info);
++		if (*len < maclen) {
++			*len = maclen;
++			/*(note: ctx not freed; can call again with larger *len)*/
++			return -1;
++		}
++		*len = maclen;
++		if ((uintptr_t)ctx & 1uL)
++			mbedtls_md_hmac_finish(mctx, mac);
++		else
++			mbedtls_md_finish(mctx, mac);
++	}
++	mbedtls_md_free(mctx);
++	os_free(mctx);
++
++	if (TEST_FAIL())
++		return -1;
++
++	return 0;
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_HASH */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++
++#include <mbedtls/bignum.h>
++
++/* crypto.h bignum interfaces */
++
++struct crypto_bignum *crypto_bignum_init(void)
++{
++	if (TEST_FAIL())
++		return NULL;
++
++	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
++	if (bn)
++		mbedtls_mpi_init(bn);
++	return (struct crypto_bignum *)bn;
++}
++
++struct crypto_bignum *crypto_bignum_init_set(const u8 *buf, size_t len)
++{
++	if (TEST_FAIL())
++		return NULL;
++
++	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
++	if (bn) {
++		mbedtls_mpi_init(bn);
++		if (mbedtls_mpi_read_binary(bn, buf, len) == 0)
++			return (struct crypto_bignum *)bn;
++	}
++
++	os_free(bn);
++	return NULL;
++}
++
++struct crypto_bignum *crypto_bignum_init_uint(unsigned int val)
++{
++	if (TEST_FAIL())
++		return NULL;
++
++  #if 0 /*(hostap use of this interface passes int, not uint)*/
++	val = host_to_be32(val);
++	return crypto_bignum_init_set((const u8 *)&val, sizeof(val));
++  #else
++	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
++	if (bn) {
++		mbedtls_mpi_init(bn);
++		if (mbedtls_mpi_lset(bn, (int)val) == 0)
++			return (struct crypto_bignum *)bn;
++	}
++
++	os_free(bn);
++	return NULL;
++  #endif
++}
++
++void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
++{
++	mbedtls_mpi_free((mbedtls_mpi *)n);
++	os_free(n);
++}
++
++int crypto_bignum_to_bin(const struct crypto_bignum *a,
++			 u8 *buf, size_t buflen, size_t padlen)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	size_t n = mbedtls_mpi_size((mbedtls_mpi *)a);
++	if (n < padlen)
++		n = padlen;
++	return n > buflen || mbedtls_mpi_write_binary((mbedtls_mpi *)a, buf, n)
++	  ? -1
++	  : (int)(n);
++}
++
++int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	/*assert(r != m);*//* r must not be same as m for mbedtls_mpi_random()*/
++  #if MBEDTLS_VERSION_NUMBER >= 0x021B0000 /* mbedtls 2.27.0 */
++	return mbedtls_mpi_random((mbedtls_mpi *)r, 0, (mbedtls_mpi *)m,
++				  mbedtls_ctr_drbg_random,
++				  crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++  #else
++	/* (needed by EAP_PWD, SAE, DPP) */
++	wpa_printf(MSG_ERROR,
++	           "mbedtls 2.27.0 or later required for mbedtls_mpi_random()");
++	return -1;
++  #endif
++}
++
++int crypto_bignum_add(const struct crypto_bignum *a,
++		      const struct crypto_bignum *b,
++		      struct crypto_bignum *c)
++{
++	return mbedtls_mpi_add_mpi((mbedtls_mpi *)c,
++				   (const mbedtls_mpi *)a,
++				   (const mbedtls_mpi *)b) ? -1 : 0;
++}
++
++int crypto_bignum_mod(const struct crypto_bignum *a,
++		      const struct crypto_bignum *b,
++		      struct crypto_bignum *c)
++{
++	return mbedtls_mpi_mod_mpi((mbedtls_mpi *)c,
++				   (const mbedtls_mpi *)a,
++				   (const mbedtls_mpi *)b) ? -1 : 0;
++}
++
++int crypto_bignum_exptmod(const struct crypto_bignum *a,
++			  const struct crypto_bignum *b,
++			  const struct crypto_bignum *c,
++			  struct crypto_bignum *d)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	/* (check if input params match d; d is the result) */
++	/* (a == d) is ok in current mbedtls implementation */
++	if (b == d || c == d) { /*(not ok; store result in intermediate)*/
++		mbedtls_mpi R;
++		mbedtls_mpi_init(&R);
++		int rc = mbedtls_mpi_exp_mod(&R,
++		                             (const mbedtls_mpi *)a,
++		                             (const mbedtls_mpi *)b,
++		                             (const mbedtls_mpi *)c,
++		                             NULL)
++		      || mbedtls_mpi_copy((mbedtls_mpi *)d, &R) ? -1 : 0;
++		mbedtls_mpi_free(&R);
++		return rc;
++	}
++	else {
++		return mbedtls_mpi_exp_mod((mbedtls_mpi *)d,
++		                           (const mbedtls_mpi *)a,
++		                           (const mbedtls_mpi *)b,
++		                           (const mbedtls_mpi *)c,
++		                           NULL) ? -1 : 0;
++	}
++}
++
++int crypto_bignum_inverse(const struct crypto_bignum *a,
++			  const struct crypto_bignum *b,
++			  struct crypto_bignum *c)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	return mbedtls_mpi_inv_mod((mbedtls_mpi *)c,
++				   (const mbedtls_mpi *)a,
++				   (const mbedtls_mpi *)b) ? -1 : 0;
++}
++
++int crypto_bignum_sub(const struct crypto_bignum *a,
++		      const struct crypto_bignum *b,
++		      struct crypto_bignum *c)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	return mbedtls_mpi_sub_mpi((mbedtls_mpi *)c,
++				   (const mbedtls_mpi *)a,
++				   (const mbedtls_mpi *)b) ? -1 : 0;
++}
++
++int crypto_bignum_div(const struct crypto_bignum *a,
++		      const struct crypto_bignum *b,
++		      struct crypto_bignum *c)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	/*(most current use of this crypto.h interface has a == c (result),
++	 * so store result in an intermediate to avoid overwritten input)*/
++	mbedtls_mpi R;
++	mbedtls_mpi_init(&R);
++	int rc = mbedtls_mpi_div_mpi(&R, NULL,
++				     (const mbedtls_mpi *)a,
++				     (const mbedtls_mpi *)b)
++	      || mbedtls_mpi_copy((mbedtls_mpi *)c, &R) ? -1 : 0;
++	mbedtls_mpi_free(&R);
++	return rc;
++}
++
++int crypto_bignum_addmod(const struct crypto_bignum *a,
++			 const struct crypto_bignum *b,
++			 const struct crypto_bignum *c,
++			 struct crypto_bignum *d)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	return mbedtls_mpi_add_mpi((mbedtls_mpi *)d,
++				   (const mbedtls_mpi *)a,
++				   (const mbedtls_mpi *)b)
++	    || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d,
++				   (mbedtls_mpi *)d,
++				   (const mbedtls_mpi *)c) ? -1 : 0;
++}
++
++int crypto_bignum_mulmod(const struct crypto_bignum *a,
++			 const struct crypto_bignum *b,
++			 const struct crypto_bignum *c,
++			 struct crypto_bignum *d)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	return mbedtls_mpi_mul_mpi((mbedtls_mpi *)d,
++				   (const mbedtls_mpi *)a,
++				   (const mbedtls_mpi *)b)
++	    || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d,
++				   (mbedtls_mpi *)d,
++				   (const mbedtls_mpi *)c) ? -1 : 0;
++}
++
++int crypto_bignum_sqrmod(const struct crypto_bignum *a,
++			 const struct crypto_bignum *b,
++			 struct crypto_bignum *c)
++{
++	if (TEST_FAIL())
++		return -1;
++
++  #if 1
++	return crypto_bignum_mulmod(a, a, b, c);
++  #else
++	mbedtls_mpi bn;
++	mbedtls_mpi_init(&bn);
++	if (mbedtls_mpi_lset(&bn, 2)) /* alt?: mbedtls_mpi_set_bit(&bn, 1) */
++		return -1;
++	int ret = mbedtls_mpi_exp_mod((mbedtls_mpi *)c,
++				      (const mbedtls_mpi *)a, &bn,
++				      (const mbedtls_mpi *)b, NULL) ? -1 : 0;
++	mbedtls_mpi_free(&bn);
++	return ret;
++  #endif
++}
++
++int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
++			 struct crypto_bignum *r)
++{
++	return mbedtls_mpi_copy((mbedtls_mpi *)r, (const mbedtls_mpi *)a)
++	    || mbedtls_mpi_shift_r((mbedtls_mpi *)r, n) ? -1 : 0;
++}
++
++int crypto_bignum_cmp(const struct crypto_bignum *a,
++		      const struct crypto_bignum *b)
++{
++	return mbedtls_mpi_cmp_mpi((const mbedtls_mpi *)a, (const mbedtls_mpi *)b);
++}
++
++int crypto_bignum_is_zero(const struct crypto_bignum *a)
++{
++	/* XXX: src/common/sae.c:sswu() contains comment:
++	 * "TODO: Make sure crypto_bignum_is_zero() is constant time"
++	 * Note: mbedtls_mpi_cmp_int() *is not* constant time */
++	return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 0) == 0);
++}
++
++int crypto_bignum_is_one(const struct crypto_bignum *a)
++{
++	return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 1) == 0);
++}
++
++int crypto_bignum_is_odd(const struct crypto_bignum *a)
++{
++	return mbedtls_mpi_get_bit((const mbedtls_mpi *)a, 0);
++}
++
++#include "utils/const_time.h"
++int crypto_bignum_legendre(const struct crypto_bignum *a,
++			   const struct crypto_bignum *p)
++{
++	if (TEST_FAIL())
++		return -2;
++
++	/* Security Note:
++	 * mbedtls_mpi_exp_mod() is not documented to run in constant time,
++	 * though mbedtls/library/bignum.c uses constant_time_internal.h funcs.
++	 * Compare to crypto_openssl.c:crypto_bignum_legendre()
++	 * which uses openssl BN_mod_exp_mont_consttime()
++	 * mbedtls/library/ecp.c has further countermeasures to timing attacks,
++	 * (but ecp.c funcs are not used here) */
++
++	mbedtls_mpi exp, tmp;
++	mbedtls_mpi_init(&exp);
++	mbedtls_mpi_init(&tmp);
++
++	/* exp = (p-1) / 2 */
++	int res;
++	if (mbedtls_mpi_sub_int(&exp, (const mbedtls_mpi *)p, 1) == 0
++	    && mbedtls_mpi_shift_r(&exp, 1) == 0
++	    && mbedtls_mpi_exp_mod(&tmp, (const mbedtls_mpi *)a, &exp,
++	                           (const mbedtls_mpi *)p, NULL) == 0) {
++		/*(modified from crypto_openssl.c:crypto_bignum_legendre())*/
++		/* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need
++		 * to use constant time selection to avoid branches here. */
++		unsigned int mask;
++		res = -1;
++		mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 1) == 0), 1);
++		res = const_time_select_int(mask, 1, res);
++		mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 0) == 0), 1);
++		res = const_time_select_int(mask, 0, res);
++	} else {
++		res = -2;
++	}
++
++	mbedtls_mpi_free(&tmp);
++	mbedtls_mpi_free(&exp);
++	return res;
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_BIGNUM */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_DH
++
++/* crypto_internal-modexp.c */
++
++#include <mbedtls/bignum.h>
++#include <mbedtls/dhm.h>
++
++#if 0 /* crypto_dh_init() and crypto_dh_derive_secret() prefer to use mbedtls */
++int crypto_mod_exp(const u8 *base, size_t base_len,
++		   const u8 *power, size_t power_len,
++		   const u8 *modulus, size_t modulus_len,
++		   u8 *result, size_t *result_len)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	mbedtls_mpi bn_base, bn_exp, bn_modulus, bn_result;
++	mbedtls_mpi_init(&bn_base);
++	mbedtls_mpi_init(&bn_exp);
++	mbedtls_mpi_init(&bn_modulus);
++	mbedtls_mpi_init(&bn_result);
++
++	size_t len;
++	int ret =  mbedtls_mpi_read_binary(&bn_base, base, base_len)
++	        || mbedtls_mpi_read_binary(&bn_exp, power, power_len)
++	        || mbedtls_mpi_read_binary(&bn_modulus, modulus, modulus_len)
++	        || mbedtls_mpi_exp_mod(&bn_result,&bn_base,&bn_exp,&bn_modulus,NULL)
++	        || (len = mbedtls_mpi_size(&bn_result)) > *result_len
++	        || mbedtls_mpi_write_binary(&bn_result, result, (*result_len = len))
++	  ? -1
++	  : 0;
++
++	mbedtls_mpi_free(&bn_base);
++	mbedtls_mpi_free(&bn_exp);
++	mbedtls_mpi_free(&bn_modulus);
++	mbedtls_mpi_free(&bn_result);
++	return ret;
++}
++#endif
++
++static int crypto_mbedtls_dh_set_bin_pg(mbedtls_dhm_context *ctx, u8 generator,
++                                        const u8 *prime, size_t prime_len)
++{
++	/*(could set these directly in MBEDTLS_PRIVATE members)*/
++	mbedtls_mpi P, G;
++	mbedtls_mpi_init(&P);
++	mbedtls_mpi_init(&G);
++	int ret = mbedtls_mpi_lset(&G, generator)
++	       || mbedtls_mpi_read_binary(&P, prime, prime_len)
++	       || mbedtls_dhm_set_group(ctx, &P, &G);
++	mbedtls_mpi_free(&P);
++	mbedtls_mpi_free(&G);
++	return ret;
++}
++
++__attribute_noinline__
++static int crypto_mbedtls_dh_init_public(mbedtls_dhm_context *ctx, u8 generator,
++                                         const u8 *prime, size_t prime_len,
++                                         u8 *privkey, u8 *pubkey)
++{
++	if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len)
++	    || mbedtls_dhm_make_public(ctx, (int)prime_len, pubkey, prime_len,
++	                               mbedtls_ctr_drbg_random,
++	                               crypto_mbedtls_ctr_drbg()))
++		return -1;
++
++  /*(enable later when upstream mbedtls interface changes require)*/
++  #if 0 && MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++	mbedtls_mpi X;
++	mbedtls_mpi_init(&X);
++	int ret = mbedtls_dhm_get_value(ctx, MBEDTLS_DHM_PARAM_X, &X)
++	       || mbedtls_mpi_write_binary(&X, privkey, prime_len) ? -1 : 0;
++	mbedtls_mpi_free(&X);
++	return ret;
++  #else
++	return mbedtls_mpi_write_binary(&ctx->MBEDTLS_PRIVATE(X),
++	                                privkey, prime_len) ? -1 : 0;
++  #endif
++}
++
++int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
++		   u8 *pubkey)
++{
++	if (TEST_FAIL())
++		return -1;
++
++  #if 0 /*(crypto_dh_init() duplicated (and identical) in crypto_*.c modules)*/
++	size_t pubkey_len, pad;
++
++	if (os_get_random(privkey, prime_len) < 0)
++		return -1;
++	if (os_memcmp(privkey, prime, prime_len) > 0) {
++		/* Make sure private value is smaller than prime */
++		privkey[0] = 0;
++	}
++
++	pubkey_len = prime_len;
++	if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
++			   pubkey, &pubkey_len) < 0)
++		return -1;
++	if (pubkey_len < prime_len) {
++		pad = prime_len - pubkey_len;
++		os_memmove(pubkey + pad, pubkey, pubkey_len);
++		os_memset(pubkey, 0, pad);
++	}
++
++	return 0;
++  #else
++	/* Prefer to use mbedtls to derive our public/private key, as doing so
++	 * leverages mbedtls to properly format output and to perform blinding*/
++	mbedtls_dhm_context ctx;
++	mbedtls_dhm_init(&ctx);
++	int ret = crypto_mbedtls_dh_init_public(&ctx, generator, prime,
++	                                        prime_len, privkey, pubkey);
++	mbedtls_dhm_free(&ctx);
++	return ret;
++  #endif
++}
++
++/*(crypto_dh_derive_secret() could be implemented using crypto.h APIs
++ * instead of being reimplemented in each crypto_*.c)*/
++int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
++			    const u8 *order, size_t order_len,
++			    const u8 *privkey, size_t privkey_len,
++			    const u8 *pubkey, size_t pubkey_len,
++			    u8 *secret, size_t *len)
++{
++	if (TEST_FAIL())
++		return -1;
++
++  #if 0
++	if (pubkey_len > prime_len ||
++	    (pubkey_len == prime_len &&
++	     os_memcmp(pubkey, prime, prime_len) >= 0))
++		return -1;
++
++	int res = 0;
++	mbedtls_mpi pub;
++	mbedtls_mpi_init(&pub);
++	if (mbedtls_mpi_read_binary(&pub, pubkey, pubkey_len)
++	    || mbedtls_mpi_cmp_int(&pub, 1) <= 0) {
++		res = -1;
++	} else if (order) {
++		mbedtls_mpi p, q, tmp;
++		mbedtls_mpi_init(&p);
++		mbedtls_mpi_init(&q);
++		mbedtls_mpi_init(&tmp);
++
++		/* verify: pubkey^q == 1 mod p */
++		res = (mbedtls_mpi_read_binary(&p, prime, prime_len)
++		    || mbedtls_mpi_read_binary(&q, order, order_len)
++		    || mbedtls_mpi_exp_mod(&tmp, &pub, &q, &p, NULL)
++		    || mbedtls_mpi_cmp_int(&tmp, 1) != 0);
++
++		mbedtls_mpi_free(&p);
++		mbedtls_mpi_free(&q);
++		mbedtls_mpi_free(&tmp);
++	}
++	mbedtls_mpi_free(&pub);
++
++	return (res == 0)
++	  ? crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
++			   prime, prime_len, secret, len)
++	  : -1;
++  #else
++	/* Prefer to use mbedtls to derive DH shared secret, as doing so
++	 * leverages mbedtls to validate params and to perform blinding.
++	 *
++	 * Attempt to reconstitute DH context to derive shared secret
++	 * (due to limitations of the interface, which ought to pass context).
++	 * Force provided G (our private key) into context without validation.
++	 * Regenerating GX (our public key) not needed to derive shared secret.
++	 */
++	/*(older compilers might not support VLAs)*/
++	/*unsigned char buf[2+prime_len+2+1+2+pubkey_len];*/
++	unsigned char buf[2+MBEDTLS_MPI_MAX_SIZE+2+1+2+MBEDTLS_MPI_MAX_SIZE];
++	unsigned char *p = buf + 2 + prime_len;
++	if (2+prime_len+2+1+2+pubkey_len > sizeof(buf))
++		return -1;
++	WPA_PUT_BE16(buf, prime_len);  /*(2-byte big-endian size of prime)*/
++	p[0] = 0;                      /*(2-byte big-endian size of generator)*/
++	p[1] = 1;
++	p[2] = generator;
++	WPA_PUT_BE16(p+3, pubkey_len); /*(2-byte big-endian size of pubkey)*/
++	os_memcpy(p+5, pubkey, pubkey_len);
++	os_memcpy(buf+2, prime, prime_len);
++
++	mbedtls_dhm_context ctx;
++	mbedtls_dhm_init(&ctx);
++	p = buf;
++	int ret = mbedtls_dhm_read_params(&ctx, &p, p+2+prime_len+5+pubkey_len)
++	       || mbedtls_mpi_read_binary(&ctx.MBEDTLS_PRIVATE(X),
++	                                  privkey, privkey_len)
++	       || mbedtls_dhm_calc_secret(&ctx, secret, *len, len,
++	                                  mbedtls_ctr_drbg_random,
++	                                  crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++	mbedtls_dhm_free(&ctx);
++	return ret;
++  #endif
++}
++
++/* dh_group5.c */
++
++#include "dh_group5.h"
++
++/* RFC3526_PRIME_1536[] and RFC3526_GENERATOR_1536[] from crypto_wolfssl.c */
++
++static const unsigned char RFC3526_PRIME_1536[] = {
++	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
++	0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
++	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
++	0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
++	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
++	0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
++	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
++	0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
++	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
++	0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
++	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
++	0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
++	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
++	0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
++	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
++	0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
++};
++
++static const unsigned char RFC3526_GENERATOR_1536[] = {
++	0x02
++};
++
++void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
++{
++	const unsigned char * const prime = RFC3526_PRIME_1536;
++	const size_t prime_len = sizeof(RFC3526_PRIME_1536);
++	const u8 generator = *RFC3526_GENERATOR_1536;
++	struct wpabuf *wpubl = NULL, *wpriv = NULL;
++
++	mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx));
++	if (ctx == NULL)
++		return NULL;
++	mbedtls_dhm_init(ctx);
++
++	if (   (wpubl = wpabuf_alloc(prime_len))
++	    && (wpriv = wpabuf_alloc(prime_len))
++	    && crypto_mbedtls_dh_init_public(ctx, generator, prime, prime_len,
++	                                     wpabuf_put(wpriv, prime_len),
++	                                     wpabuf_put(wpubl, prime_len))==0) {
++		wpabuf_free(*publ);
++		wpabuf_clear_free(*priv);
++		*publ = wpubl;
++		*priv = wpriv;
++		return ctx;
++	}
++
++	wpabuf_clear_free(wpriv);
++	wpabuf_free(wpubl);
++	mbedtls_dhm_free(ctx);
++	os_free(ctx);
++	return NULL;
++}
++
++#ifdef CRYPTO_MBEDTLS_DH5_INIT_FIXED
++void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
++{
++	const unsigned char * const prime = RFC3526_PRIME_1536;
++	const size_t prime_len = sizeof(RFC3526_PRIME_1536);
++	const u8 generator = *RFC3526_GENERATOR_1536;
++
++	mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx));
++	if (ctx == NULL)
++		return NULL;
++	mbedtls_dhm_init(ctx);
++
++	if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len)==0
++	   #if 0 /*(ignore; not required to derive shared secret)*/
++	    && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(GX),
++				       wpabuf_head(publ),wpabuf_len(publ))==0
++	   #endif
++	    && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(X),
++				       wpabuf_head(priv),wpabuf_len(priv))==0) {
++		return ctx;
++	}
++
++	mbedtls_dhm_free(ctx);
++	os_free(ctx);
++	return NULL;
++}
++#endif
++
++struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
++				  const struct wpabuf *own_private)
++{
++	/*((mbedtls_dhm_context *)ctx must already contain own_private)*/
++	/* mbedtls 2.x: prime_len = ctx->len; */
++	/* mbedtls 3.x: prime_len = mbedtls_dhm_get_len(ctx); */
++	size_t olen = sizeof(RFC3526_PRIME_1536); /*(sizeof(); prime known)*/
++	struct wpabuf *buf = wpabuf_alloc(olen);
++	if (buf == NULL)
++		return NULL;
++	if (mbedtls_dhm_read_public((mbedtls_dhm_context *)ctx,
++	                            wpabuf_head(peer_public),
++	                            wpabuf_len(peer_public)) == 0
++	    && mbedtls_dhm_calc_secret(ctx, wpabuf_mhead(buf), olen, &olen,
++	                               mbedtls_ctr_drbg_random,
++	                               crypto_mbedtls_ctr_drbg()) == 0) {
++		wpabuf_put(buf, olen);
++		return buf;
++	}
++
++	wpabuf_free(buf);
++	return NULL;
++}
++
++void dh5_free(void *ctx)
++{
++	mbedtls_dhm_free(ctx);
++	os_free(ctx);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_DH */
++
++
++#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC)
++
++#include <mbedtls/ecp.h>
++
++#define CRYPTO_EC_pbits(e) (((mbedtls_ecp_group *)(e))->pbits)
++#define CRYPTO_EC_plen(e) ((((mbedtls_ecp_group *)(e))->pbits+7)>>3)
++#define CRYPTO_EC_P(e)    (&((mbedtls_ecp_group *)(e))->P)
++#define CRYPTO_EC_N(e)    (&((mbedtls_ecp_group *)(e))->N)
++#define CRYPTO_EC_A(e)    (&((mbedtls_ecp_group *)(e))->A)
++#define CRYPTO_EC_B(e)    (&((mbedtls_ecp_group *)(e))->B)
++#define CRYPTO_EC_G(e)    (&((mbedtls_ecp_group *)(e))->G)
++
++static mbedtls_ecp_group_id crypto_mbedtls_ecp_group_id_from_ike_id(int group)
++{
++	/* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */
++	switch (group) {
++  #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
++	case 19: return MBEDTLS_ECP_DP_SECP256R1;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
++	case 20: return MBEDTLS_ECP_DP_SECP384R1;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
++	case 21: return MBEDTLS_ECP_DP_SECP521R1;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
++	case 25: return MBEDTLS_ECP_DP_SECP192R1;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
++	case 26: return MBEDTLS_ECP_DP_SECP224R1;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
++	case 28: return MBEDTLS_ECP_DP_BP256R1;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
++	case 29: return MBEDTLS_ECP_DP_BP384R1;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
++	case 30: return MBEDTLS_ECP_DP_BP512R1;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
++	case 31: return MBEDTLS_ECP_DP_CURVE25519;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
++	case 32: return MBEDTLS_ECP_DP_CURVE448;
++  #endif
++	default: return MBEDTLS_ECP_DP_NONE;
++	}
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC
++static int crypto_mbedtls_ike_id_from_ecp_group_id(mbedtls_ecp_group_id grp_id)
++{
++	/* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */
++	/*(for crypto_ec_key_group())*/
++	switch (grp_id) {
++  #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP256R1:  return 19;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP384R1:  return 20;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP521R1:  return 21;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP192R1:  return 25;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP224R1:  return 26;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
++	case MBEDTLS_ECP_DP_BP256R1:    return 28;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
++	case MBEDTLS_ECP_DP_BP384R1:    return 29;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
++	case MBEDTLS_ECP_DP_BP512R1:    return 30;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
++	case MBEDTLS_ECP_DP_CURVE25519: return 31;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
++	case MBEDTLS_ECP_DP_CURVE448:   return 32;
++  #endif
++	default: return -1;
++	}
++}
++#endif
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH || CRYPTO_MBEDTLS_CRYPTO_EC */
++
++
++#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC_DPP)
++
++#include <mbedtls/ecp.h>
++#include <mbedtls/pk.h>
++
++static int crypto_mbedtls_keypair_gen(int group, mbedtls_pk_context *pk)
++{
++	mbedtls_ecp_group_id grp_id =
++	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
++	if (grp_id == MBEDTLS_ECP_DP_NONE)
++		return -1;
++	const mbedtls_pk_info_t *pk_info =
++	  mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
++	if (pk_info == NULL)
++		return -1;
++	return mbedtls_pk_setup(pk, pk_info)
++	    || mbedtls_ecp_gen_key(grp_id, mbedtls_pk_ec(*pk),
++	                           mbedtls_ctr_drbg_random,
++	                           crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++}
++
++#endif
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_ECDH
++
++#include <mbedtls/ecdh.h>
++#include <mbedtls/ecdsa.h>
++#include <mbedtls/ecp.h>
++#include <mbedtls/pk.h>
++
++/* wrap mbedtls_ecdh_context for more future-proof direct access to components
++ * (mbedtls_ecdh_context internal implementation may change between releases)
++ *
++ * If mbedtls_pk_context -- specifically underlying mbedtls_ecp_keypair --
++ * lifetime were guaranteed to be longer than that of mbedtls_ecdh_context,
++ * then mbedtls_pk_context or mbedtls_ecp_keypair could be stored in crypto_ecdh
++ * (or crypto_ec_key could be stored in crypto_ecdh, and crypto_ec_key could
++ *  wrap mbedtls_ecp_keypair and components, to avoid MBEDTLS_PRIVATE access) */
++struct crypto_ecdh {
++	mbedtls_ecdh_context ctx;
++	mbedtls_ecp_group grp;
++	mbedtls_ecp_point Q;
++};
++
++struct crypto_ecdh * crypto_ecdh_init(int group)
++{
++	mbedtls_pk_context pk;
++	mbedtls_pk_init(&pk);
++	struct crypto_ecdh *ecdh = crypto_mbedtls_keypair_gen(group, &pk) == 0
++	  ? crypto_ecdh_init2(group, (struct crypto_ec_key *)&pk)
++	  : NULL;
++	mbedtls_pk_free(&pk);
++	return ecdh;
++}
++
++struct crypto_ecdh * crypto_ecdh_init2(int group,
++				       struct crypto_ec_key *own_key)
++{
++	mbedtls_ecp_group_id grp_id =
++	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
++	if (grp_id == MBEDTLS_ECP_DP_NONE)
++		return NULL;
++	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)own_key);
++	struct crypto_ecdh *ecdh = os_malloc(sizeof(*ecdh));
++	if (ecdh == NULL)
++		return NULL;
++	mbedtls_ecdh_init(&ecdh->ctx);
++	mbedtls_ecp_group_init(&ecdh->grp);
++	mbedtls_ecp_point_init(&ecdh->Q);
++	if (mbedtls_ecdh_setup(&ecdh->ctx, grp_id) == 0
++	    && mbedtls_ecdh_get_params(&ecdh->ctx,ecp_kp,MBEDTLS_ECDH_OURS) == 0) {
++		/* copy grp and Q for later use
++		 * (retrieving this info later is more convoluted
++		 *  even if mbedtls_ecdh_make_public() is considered)*/
++	  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
++		mbedtls_mpi d;
++		mbedtls_mpi_init(&d);
++		if (mbedtls_ecp_export(ecp_kp, &ecdh->grp, &d, &ecdh->Q) == 0) {
++			mbedtls_mpi_free(&d);
++			return ecdh;
++		}
++		mbedtls_mpi_free(&d);
++	  #else
++		if (mbedtls_ecp_group_load(&ecdh->grp, grp_id) == 0
++		    && mbedtls_ecp_copy(&ecdh->Q, &ecp_kp->MBEDTLS_PRIVATE(Q)) == 0)
++			return ecdh;
++	  #endif
++	}
++
++	mbedtls_ecp_point_free(&ecdh->Q);
++	mbedtls_ecp_group_free(&ecdh->grp);
++	mbedtls_ecdh_free(&ecdh->ctx);
++	os_free(ecdh);
++	return NULL;
++}
++
++struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
++{
++	mbedtls_ecp_group *grp = &ecdh->grp;
++	size_t prime_len = CRYPTO_EC_plen(grp);
++	size_t output_len = prime_len;
++	u8 output_offset = 0;
++	u8 buf[256];
++
++  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++	/* len */
++  #endif
++  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
++		output_len = inc_y ? prime_len * 2 + 1 : prime_len + 1;
++		output_offset = 1;
++	}
++  #endif
++
++	if (output_len > sizeof(buf))
++		return NULL;
++
++	inc_y = inc_y ? MBEDTLS_ECP_PF_UNCOMPRESSED : MBEDTLS_ECP_PF_COMPRESSED;
++	if (mbedtls_ecp_point_write_binary(grp, &ecdh->Q, inc_y, &output_len,
++	                                   buf, output_len) == 0) {
++		return wpabuf_alloc_copy(buf + output_offset, output_len - output_offset);
++	}
++
++	return NULL;
++}
++
++#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
++static int crypto_mbedtls_short_weierstrass_derive_y(mbedtls_ecp_group *grp,
++                                                     mbedtls_mpi *bn,
++                                                     int parity_bit)
++{
++	/* y^2 = x^3 + ax + b
++	 * sqrt(w) = w^((p+1)/4) mod p   (for prime p where p = 3 mod 4) */
++	mbedtls_mpi *cy2 = (mbedtls_mpi *)
++	  crypto_ec_point_compute_y_sqr((struct crypto_ec *)grp,
++	                                (const struct crypto_bignum *)bn); /*x*/
++	if (cy2 == NULL)
++		return -1;
++
++	/*mbedtls_mpi_free(bn);*/
++	/*(reuse bn to store result (y))*/
++
++	mbedtls_mpi exp;
++	mbedtls_mpi_init(&exp);
++	int ret = mbedtls_mpi_get_bit(&grp->P, 0) != 1 /*(p = 3 mod 4)*/
++	       || mbedtls_mpi_get_bit(&grp->P, 1) != 1 /*(p = 3 mod 4)*/
++	       || mbedtls_mpi_add_int(&exp, &grp->P, 1)
++	       || mbedtls_mpi_shift_r(&exp, 2)
++	       || mbedtls_mpi_exp_mod(bn, cy2, &exp, &grp->P, NULL)
++	       || (mbedtls_mpi_get_bit(bn, 0) != parity_bit
++	           && mbedtls_mpi_sub_mpi(bn, &grp->P, bn));
++	mbedtls_mpi_free(&exp);
++	mbedtls_mpi_free(cy2);
++	os_free(cy2);
++	return ret;
++}
++#endif
++
++struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
++					const u8 *key, size_t len)
++{
++	if (len == 0) /*(invalid peer key)*/
++		return NULL;
++
++	mbedtls_ecp_group *grp = &ecdh->grp;
++
++  #if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
++	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
++		/* add header for mbedtls_ecdh_read_public() */
++		u8 buf[256];
++		if (sizeof(buf)-1 < len)
++			return NULL;
++		buf[0] = (u8)(len);
++		os_memcpy(buf+1, key, len);
++
++		if (inc_y) {
++			if (!(len & 1)) { /*(dpp code/tests does not include tag?!?)*/
++				if (sizeof(buf)-2 < len)
++					return NULL;
++				buf[0] = (u8)(1+len);
++				buf[1] = 0x04;
++				os_memcpy(buf+2, key, len);
++			}
++			len >>= 1; /*(repurpose len to prime_len)*/
++		} else { /* (inc_y == 0) */
++			/* mbedtls_ecp_point_read_binary() does not currently support
++			 * MBEDTLS_ECP_PF_COMPRESSED format (buf[1] = 0x02 or 0x03)
++			 * (returns MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) */
++
++			/* derive y, amend buf[] with y for UNCOMPRESSED format */
++			if (sizeof(buf)-2 < len*2 || len == 0)
++				return NULL;
++
++			buf[0] = (u8)(1+len*2);
++			buf[1] = 0x04;
++			os_memcpy(buf+2, key, len);
++
++			mbedtls_mpi bn;
++			mbedtls_mpi_init(&bn);
++			int ret = mbedtls_mpi_read_binary(&bn, key, len)
++			       || crypto_mbedtls_short_weierstrass_derive_y(grp, &bn, 0)
++			       || mbedtls_mpi_write_binary(&bn, buf+2+len, len);
++			mbedtls_mpi_free(&bn);
++			if (ret != 0)
++				return NULL;
++		}
++
++		if (mbedtls_ecdh_read_public(&ecdh->ctx, buf, buf[0]+1))
++			return NULL;
++	}
++  #endif
++  #if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED)
++	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) {
++		if (mbedtls_ecdh_read_public(&ecdh->ctx, key, len))
++			return NULL;
++	}
++  #endif
++
++	struct wpabuf *buf = wpabuf_alloc(len);
++	if (buf == NULL)
++		return NULL;
++
++	if (mbedtls_ecdh_calc_secret(&ecdh->ctx, &len,
++	                             wpabuf_mhead(buf), len,
++	                             mbedtls_ctr_drbg_random,
++	                             crypto_mbedtls_ctr_drbg()) == 0) {
++		wpabuf_put(buf, len);
++		return buf;
++	}
++
++	wpabuf_clear_free(buf);
++	return NULL;
++}
++
++void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
++{
++	if (ecdh == NULL)
++		return;
++	mbedtls_ecp_point_free(&ecdh->Q);
++	mbedtls_ecp_group_free(&ecdh->grp);
++	mbedtls_ecdh_free(&ecdh->ctx);
++	os_free(ecdh);
++}
++
++size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh)
++{
++	return CRYPTO_EC_plen(&ecdh->grp);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC
++
++#include <mbedtls/ecp.h>
++
++struct crypto_ec *crypto_ec_init(int group)
++{
++	mbedtls_ecp_group_id grp_id =
++	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
++	if (grp_id == MBEDTLS_ECP_DP_NONE)
++		return NULL;
++	mbedtls_ecp_group *e = os_malloc(sizeof(*e));
++	if (e == NULL)
++		return NULL;
++	mbedtls_ecp_group_init(e);
++	if (mbedtls_ecp_group_load(e, grp_id) == 0)
++		return (struct crypto_ec *)e;
++
++	mbedtls_ecp_group_free(e);
++	os_free(e);
++	return NULL;
++}
++
++void crypto_ec_deinit(struct crypto_ec *e)
++{
++	mbedtls_ecp_group_free((mbedtls_ecp_group *)e);
++	os_free(e);
++}
++
++size_t crypto_ec_prime_len(struct crypto_ec *e)
++{
++	return CRYPTO_EC_plen(e);
++}
++
++size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
++{
++	return CRYPTO_EC_pbits(e);
++}
++
++size_t crypto_ec_order_len(struct crypto_ec *e)
++{
++	return (mbedtls_mpi_bitlen(CRYPTO_EC_N(e)) + 7) / 8;
++}
++
++const struct crypto_bignum *crypto_ec_get_prime(struct crypto_ec *e)
++{
++	return (const struct crypto_bignum *)CRYPTO_EC_P(e);
++}
++
++const struct crypto_bignum *crypto_ec_get_order(struct crypto_ec *e)
++{
++	return (const struct crypto_bignum *)CRYPTO_EC_N(e);
++}
++
++const struct crypto_bignum *crypto_ec_get_a(struct crypto_ec *e)
++{
++	static const uint8_t secp256r1_a[] =
++	  {0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x01,
++	   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++	   0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc};
++	static const uint8_t secp384r1_a[] =
++	  {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
++	   0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
++	   0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xfc};
++	static const uint8_t secp521r1_a[] =
++	  {0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xfc};
++	static const uint8_t secp192r1_a[] =
++	  {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc};
++	static const uint8_t secp224r1_a[] =
++	  {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xfe};
++
++	const uint8_t *bin = NULL;
++	size_t len = 0;
++
++	/* (mbedtls groups matching supported sswu_curve_param() IKE groups) */
++	switch (((mbedtls_ecp_group *)e)->id) {
++  #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP256R1:
++		bin = secp256r1_a;
++		len = sizeof(secp256r1_a);
++		break;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP384R1:
++		bin = secp384r1_a;
++		len = sizeof(secp384r1_a);
++		break;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP521R1:
++		bin = secp521r1_a;
++		len = sizeof(secp521r1_a);
++		break;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP192R1:
++		bin = secp192r1_a;
++		len = sizeof(secp192r1_a);
++		break;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP224R1:
++		bin = secp224r1_a;
++		len = sizeof(secp224r1_a);
++		break;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
++	case MBEDTLS_ECP_DP_BP256R1:
++		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++  #endif
++  #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
++	case MBEDTLS_ECP_DP_BP384R1:
++		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++  #endif
++  #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
++	case MBEDTLS_ECP_DP_BP512R1:
++		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++  #endif
++  #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
++	case MBEDTLS_ECP_DP_CURVE25519:
++		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++  #endif
++  #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
++	case MBEDTLS_ECP_DP_CURVE448:
++		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++  #endif
++	default:
++		return NULL;
++	}
++
++	/*(note: not thread-safe; returns file-scoped static storage)*/
++	if (mbedtls_mpi_read_binary(&mpi_sw_A, bin, len) == 0)
++		return (const struct crypto_bignum *)&mpi_sw_A;
++	return NULL;
++}
++
++const struct crypto_bignum *crypto_ec_get_b(struct crypto_ec *e)
++{
++	return (const struct crypto_bignum *)CRYPTO_EC_B(e);
++}
++
++const struct crypto_ec_point * crypto_ec_get_generator(struct crypto_ec *e)
++{
++	return (const struct crypto_ec_point *)CRYPTO_EC_G(e);
++}
++
++struct crypto_ec_point *crypto_ec_point_init(struct crypto_ec *e)
++{
++	if (TEST_FAIL())
++		return NULL;
++
++	mbedtls_ecp_point *p = os_malloc(sizeof(*p));
++	if (p != NULL)
++		mbedtls_ecp_point_init(p);
++	return (struct crypto_ec_point *)p;
++}
++
++void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
++{
++	mbedtls_ecp_point_free((mbedtls_ecp_point *)p);
++	os_free(p);
++}
++
++int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
++		      struct crypto_bignum *x)
++{
++	mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
++	return mbedtls_mpi_copy((mbedtls_mpi *)x, px)
++	  ? -1
++	  : 0;
++}
++
++int crypto_ec_point_to_bin(struct crypto_ec *e,
++			   const struct crypto_ec_point *point, u8 *x, u8 *y)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	/* crypto.h documents crypto_ec_point_to_bin() output is big-endian */
++	size_t len = CRYPTO_EC_plen(e);
++	if (x) {
++		mbedtls_mpi *px = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(X);
++		if (mbedtls_mpi_write_binary(px, x, len))
++			return -1;
++	}
++	if (y) {
++	  #if 0 /*(should not be necessary; py mpi should be in initial state)*/
++	  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++		if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++		    == MBEDTLS_ECP_TYPE_MONTGOMERY) {
++			os_memset(y, 0, len);
++			return 0;
++		}
++	  #endif
++	  #endif
++		mbedtls_mpi *py = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(Y);
++		if (mbedtls_mpi_write_binary(py, y, len))
++			return -1;
++	}
++	return 0;
++}
++
++struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
++						  const u8 *val)
++{
++	if (TEST_FAIL())
++		return NULL;
++
++	size_t len = CRYPTO_EC_plen(e);
++	mbedtls_ecp_point *p = os_malloc(sizeof(*p));
++	u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
++	if (p == NULL)
++		return NULL;
++	mbedtls_ecp_point_init(p);
++
++  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++	    == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
++	  #if 0 /* prefer alternative to MBEDTLS_PRIVATE() access */
++		mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
++		mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
++		mbedtls_mpi *pz = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Z);
++
++		if (mbedtls_mpi_read_binary(px, val, len) == 0
++		    && mbedtls_mpi_read_binary(py, val + len, len) == 0
++		    && mbedtls_mpi_lset(pz, 1) == 0)
++			return (struct crypto_ec_point *)p;
++	  #else
++		buf[0] = 0x04;
++		os_memcpy(buf+1, val, len*2);
++		if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p,
++		                                  buf, 1+len*2) == 0)
++			return (struct crypto_ec_point *)p;
++	  #endif
++	}
++  #endif
++  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++	    == MBEDTLS_ECP_TYPE_MONTGOMERY) {
++		/* crypto.h interface documents crypto_ec_point_from_bin()
++		 * val is length: prime_len * 2 and is big-endian
++		 * (Short Weierstrass is assumed by hostap)
++		 * Reverse to little-endian format for Montgomery */
++		for (unsigned int i = 0; i < len; ++i)
++			buf[i] = val[len-1-i];
++		if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p,
++		                                  buf, len) == 0)
++			return (struct crypto_ec_point *)p;
++	}
++  #endif
++
++	mbedtls_ecp_point_free(p);
++	os_free(p);
++	return NULL;
++}
++
++int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
++			const struct crypto_ec_point *b,
++			struct crypto_ec_point *c)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	/* mbedtls does not provide an mbedtls_ecp_point add function */
++	mbedtls_mpi one;
++	mbedtls_mpi_init(&one);
++	int ret = mbedtls_mpi_lset(&one, 1)
++	       || mbedtls_ecp_muladd(
++			(mbedtls_ecp_group *)e, (mbedtls_ecp_point *)c,
++			&one, (const mbedtls_ecp_point *)a,
++			&one, (const mbedtls_ecp_point *)b) ? -1 : 0;
++	mbedtls_mpi_free(&one);
++	return ret;
++}
++
++int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
++			const struct crypto_bignum *b,
++			struct crypto_ec_point *res)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	return mbedtls_ecp_mul(
++		(mbedtls_ecp_group *)e, (mbedtls_ecp_point *)res,
++		(const mbedtls_mpi *)b, (const mbedtls_ecp_point *)p,
++		mbedtls_ctr_drbg_random, crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++}
++
++int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++	    == MBEDTLS_ECP_TYPE_MONTGOMERY) {
++		/* e.g. MBEDTLS_ECP_DP_CURVE25519 and MBEDTLS_ECP_DP_CURVE448 */
++		wpa_printf(MSG_ERROR,
++		           "%s not implemented for Montgomery curves",__func__);
++		return -1;
++	}
++
++	/* mbedtls does not provide an mbedtls_ecp_point invert function */
++	/* below works for Short Weierstrass; incorrect for Montgomery curves */
++	mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
++	return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p) /*point at infinity*/
++	    || mbedtls_mpi_cmp_int(py, 0) == 0      /*point is its own inverse*/
++	    || mbedtls_mpi_sub_abs(py, CRYPTO_EC_P(e), py) == 0 ? 0 : -1;
++}
++
++#ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++static int
++crypto_ec_point_y_sqr_weierstrass(mbedtls_ecp_group *e, const mbedtls_mpi *x,
++                                  mbedtls_mpi *y2)
++{
++	/* MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS  y^2 = x^3 + a x + b    */
++
++	/* Short Weierstrass elliptic curve group w/o A set treated as A = -3 */
++	/* Attempt to match mbedtls/library/ecp.c:ecp_check_pubkey_sw() behavior
++	 * and elsewhere in mbedtls/library/ecp.c where if A is not set, it is
++	 * treated as if A = -3. */
++
++  #if 0
++	/* y^2 = x^3 + ax + b */
++	mbedtls_mpi *A = &e->A;
++	mbedtls_mpi t, A_neg3;
++	if (&e->A.p == NULL) {
++		mbedtls_mpi_init(&A_neg3);
++		if (mbedtls_mpi_lset(&A_neg3, -3) != 0) {
++			mbedtls_mpi_free(&A_neg3);
++			return -1;
++		}
++		A = &A_neg3;
++	}
++	mbedtls_mpi_init(&t);
++	int ret = /* x^3 */
++	          mbedtls_mpi_lset(&t, 3)
++	       || mbedtls_mpi_exp_mod(y2,  x, &t, &e->P, NULL)
++		  /* ax */
++	       || mbedtls_mpi_mul_mpi(y2, y2, A)
++	       || mbedtls_mpi_mod_mpi(&t, &t, &e->P)
++		  /* ax + b */
++	       || mbedtls_mpi_add_mpi(&t, &t, &e->B)
++	       || mbedtls_mpi_mod_mpi(&t, &t, &e->P)
++		  /* x^3 + ax + b */
++	       || mbedtls_mpi_add_mpi(&t, &t, y2) /* ax + b + x^3 */
++	       || mbedtls_mpi_mod_mpi(y2, &t, &e->P);
++	mbedtls_mpi_free(&t);
++	if (A == &A_neg3)
++		mbedtls_mpi_free(&A_neg3);
++	return ret; /* 0: success, non-zero: failure */
++  #else
++	/* y^2 = x^3 + ax + b = (x^2 + a)x + b */
++	return    /* x^2 */
++	          mbedtls_mpi_mul_mpi(y2,  x, x)
++	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++		  /* x^2 + a */
++	       || (e->A.MBEDTLS_PRIVATE(p)
++	           ? mbedtls_mpi_add_mpi(y2, y2, &e->A)
++	           : mbedtls_mpi_sub_int(y2, y2, 3))
++	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++		  /* (x^2 + a)x */
++	       || mbedtls_mpi_mul_mpi(y2, y2, x)
++	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++		  /* (x^2 + a)x + b */
++	       || mbedtls_mpi_add_mpi(y2, y2, &e->B)
++	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P);
++  #endif
++}
++#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */
++
++#if 0 /* not used by hostap */
++#ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++static int
++crypto_ec_point_y_sqr_montgomery(mbedtls_ecp_group *e, const mbedtls_mpi *x,
++                                 mbedtls_mpi *y2)
++{
++	/* XXX: !!! must be reviewed and audited for correctness !!! */
++
++	/* MBEDTLS_ECP_TYPE_MONTGOMERY         y^2 = x^3 + a x^2 + x  */
++
++	/* y^2 = x^3 + a x^2 + x = (x + a)x^2 + x */
++	mbedtls_mpi x2;
++	mbedtls_mpi_init(&x2);
++	int ret = /* x^2 */
++	          mbedtls_mpi_mul_mpi(&x2, x, x)
++	       || mbedtls_mpi_mod_mpi(&x2, &x2, &e->P)
++		  /* x + a */
++	       || mbedtls_mpi_add_mpi(y2,  x, &e->A)
++	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++		  /* (x + a)x^2 */
++	       || mbedtls_mpi_mul_mpi(y2, y2, &x2)
++	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++		  /* (x + a)x^2 + x */
++	       || mbedtls_mpi_add_mpi(y2, y2, x)
++	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P);
++	mbedtls_mpi_free(&x2);
++	return ret; /* 0: success, non-zero: failure */
++}
++#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */
++#endif
++
++struct crypto_bignum *
++crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
++			      const struct crypto_bignum *x)
++{
++	if (TEST_FAIL())
++		return NULL;
++
++	mbedtls_mpi *y2 = os_malloc(sizeof(*y2));
++	if (y2 == NULL)
++		return NULL;
++	mbedtls_mpi_init(y2);
++
++  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++	      == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS
++	    && crypto_ec_point_y_sqr_weierstrass((mbedtls_ecp_group *)e,
++	                                         (const mbedtls_mpi *)x,
++	                                         y2) == 0)
++		return (struct crypto_bignum *)y2;
++  #endif
++  #if 0 /* not used by hostap */
++  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++	      == MBEDTLS_ECP_TYPE_MONTGOMERY
++	    && crypto_ec_point_y_sqr_montgomery((mbedtls_ecp_group *)e,
++	                                        (const mbedtls_mpi *)x,
++	                                        y2) == 0)
++		return (struct crypto_bignum *)y2;
++  #endif
++  #endif
++
++	mbedtls_mpi_free(y2);
++	os_free(y2);
++	return NULL;
++}
++
++int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
++				   const struct crypto_ec_point *p)
++{
++	return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p);
++}
++
++int crypto_ec_point_is_on_curve(struct crypto_ec *e,
++				const struct crypto_ec_point *p)
++{
++  #if 1
++	return mbedtls_ecp_check_pubkey((const mbedtls_ecp_group *)e,
++	                                (const mbedtls_ecp_point *)p) == 0;
++  #else
++	/* compute y^2 mod P and compare to y^2 mod P */
++	/*(ref: src/eap_common/eap_pwd_common.c:compute_password_element())*/
++	const mbedtls_mpi *px = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
++	mbedtls_mpi *cy2 = (mbedtls_mpi *)
++	  crypto_ec_point_compute_y_sqr(e, (const struct crypto_bignum *)px);
++	if (cy2 == NULL)
++		return 0;
++
++	mbedtls_mpi y2;
++	mbedtls_mpi_init(&y2);
++	const mbedtls_mpi *py = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
++	int is_on_curve = mbedtls_mpi_mul_mpi(&y2, py, py) /* y^2 mod P */
++	               || mbedtls_mpi_mod_mpi(&y2, &y2, CRYPTO_EC_P(e))
++	               || mbedtls_mpi_cmp_mpi(&y2, cy2) != 0 ? 0 : 1;
++
++	mbedtls_mpi_free(&y2);
++	mbedtls_mpi_free(cy2);
++	os_free(cy2);
++	return is_on_curve;
++  #endif
++}
++
++int crypto_ec_point_cmp(const struct crypto_ec *e,
++			const struct crypto_ec_point *a,
++			const struct crypto_ec_point *b)
++{
++	return mbedtls_ecp_point_cmp((const mbedtls_ecp_point *)a,
++	                             (const mbedtls_ecp_point *)b);
++}
++
++#if !defined(CONFIG_NO_STDOUT_DEBUG)
++void crypto_ec_point_debug_print(const struct crypto_ec *e,
++				 const struct crypto_ec_point *p,
++				 const char *title)
++{
++	u8 x[MBEDTLS_MPI_MAX_SIZE];
++	u8 y[MBEDTLS_MPI_MAX_SIZE];
++	size_t len = CRYPTO_EC_plen(e);
++	/* crypto_ec_point_to_bin ought to take (const struct crypto_ec *e) */
++	struct crypto_ec *ee;
++	*(const struct crypto_ec **)&ee = e; /*(cast away const)*/
++	if (crypto_ec_point_to_bin(ee, p, x, y) == 0) {
++		if (title)
++			wpa_printf(MSG_DEBUG, "%s", title);
++		wpa_hexdump(MSG_DEBUG, "x:", x, len);
++		wpa_hexdump(MSG_DEBUG, "y:", y, len);
++	}
++}
++#endif
++
++
++struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len)
++{
++	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++	if (ctx == NULL)
++		return NULL;
++	mbedtls_pk_init(ctx);
++  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++	if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0) == 0)
++  #else
++	if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0,
++	                         mbedtls_ctr_drbg_random,
++	                         crypto_mbedtls_ctr_drbg()) == 0)
++  #endif
++		return (struct crypto_ec_key *)ctx;
++
++	mbedtls_pk_free(ctx);
++	os_free(ctx);
++	return NULL;
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE
++#ifdef CONFIG_MODULE_TESTS
++/*(for crypto_module_tests.c)*/
++struct crypto_ec_key * crypto_ec_key_set_priv(int group,
++					      const u8 *raw, size_t raw_len)
++{
++	mbedtls_ecp_group_id grp_id =
++	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
++	if (grp_id == MBEDTLS_ECP_DP_NONE)
++		return NULL;
++	const mbedtls_pk_info_t *pk_info =
++	  mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
++	if (pk_info == NULL)
++		return NULL;
++	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++	if (ctx == NULL)
++		return NULL;
++	mbedtls_pk_init(ctx);
++	if (mbedtls_pk_setup(ctx, pk_info) == 0
++	    && mbedtls_ecp_read_key(grp_id,mbedtls_pk_ec(*ctx),raw,raw_len) == 0) {
++		return (struct crypto_ec_key *)ctx;
++	}
++
++	mbedtls_pk_free(ctx);
++	os_free(ctx);
++	return NULL;
++}
++#endif
++#endif
++
++#include <mbedtls/error.h>
++#include <mbedtls/oid.h>
++static int crypto_mbedtls_pk_parse_subpubkey_compressed(mbedtls_pk_context *ctx, const u8 *der, size_t der_len)
++{
++    /* The following is modified from:
++     *   mbedtls/library/pkparse.c:mbedtls_pk_parse_subpubkey()
++     *   mbedtls/library/pkparse.c:pk_get_pk_alg()
++     *   mbedtls/library/pkparse.c:pk_use_ecparams()
++     */
++    mbedtls_pk_type_t pk_alg = MBEDTLS_PK_NONE;
++    const mbedtls_pk_info_t *pk_info;
++    int ret;
++    size_t len;
++    const unsigned char *end = der+der_len;
++    unsigned char *p;
++    *(const unsigned char **)&p = der;
++
++    if( ( ret = mbedtls_asn1_get_tag( &p, end, &len,
++                    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
++    {
++        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret ) );
++    }
++
++    end = p + len;
++
++    /*
++    if( ( ret = pk_get_pk_alg( &p, end, &pk_alg, &alg_params ) ) != 0 )
++        return( ret );
++    */
++    mbedtls_asn1_buf alg_oid, params;
++    memset( &params, 0, sizeof(mbedtls_asn1_buf) );
++    if( ( ret = mbedtls_asn1_get_alg( &p, end, &alg_oid, &params ) ) != 0 )
++        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_ALG, ret ) );
++    if( mbedtls_oid_get_pk_alg( &alg_oid, &pk_alg ) != 0 )
++        return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
++
++    if( ( ret = mbedtls_asn1_get_bitstring_null( &p, end, &len ) ) != 0 )
++        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY, ret ) );
++
++    if( p + len != end )
++        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY,
++                MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ) );
++
++    if( ( pk_info = mbedtls_pk_info_from_type( pk_alg ) ) == NULL )
++        return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
++
++    if( ( ret = mbedtls_pk_setup( ctx, pk_info ) ) != 0 )
++        return( ret );
++
++    /* assume mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx)
++     * has already run with ctx initialized up to pk_get_ecpubkey(),
++     * and pk_get_ecpubkey() has returned MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
++     *
++     * mbedtls mbedtls_ecp_point_read_binary()
++     * does not handle point in COMPRESSED format
++     *
++     * (validate assumption that algorithm is EC) */
++    mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx);
++    if (ecp_kp == NULL)
++        return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
++    mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++    mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++    mbedtls_ecp_group_id grp_id;
++
++
++    /* mbedtls/library/pkparse.c:pk_use_ecparams() */
++
++    if( params.tag == MBEDTLS_ASN1_OID )
++    {
++        if( mbedtls_oid_get_ec_grp( &params, &grp_id ) != 0 )
++            return( MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE );
++    }
++    else
++    {
++#if defined(MBEDTLS_PK_PARSE_EC_EXTENDED)
++        /*(large code block not copied from mbedtls; unsupported)*/
++      #if 0
++        if( ( ret = pk_group_id_from_specified( &params, &grp_id ) ) != 0 )
++            return( ret );
++      #endif
++#endif
++        return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
++    }
++
++    /*
++     * grp may already be initialized; if so, make sure IDs match
++     */
++    if( ecp_kp_grp->id != MBEDTLS_ECP_DP_NONE && ecp_kp_grp->id != grp_id )
++        return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
++
++    if( ( ret = mbedtls_ecp_group_load( ecp_kp_grp, grp_id ) ) != 0 )
++        return( ret );
++
++
++    /* (validate assumption that EC point is in COMPRESSED format) */
++    len = CRYPTO_EC_plen(ecp_kp_grp);
++    if( mbedtls_ecp_get_type(ecp_kp_grp) != MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS
++        || (end - p) != 1+len
++        || (*p != 0x02 && *p != 0x03) )
++        return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
++
++    /* Instead of calling mbedtls/library/pkparse.c:pk_get_ecpubkey() to call
++     * mbedtls_ecp_point_read_binary(), manually parse point into ecp_kp_Q */
++    mbedtls_mpi *X = &ecp_kp_Q->MBEDTLS_PRIVATE(X);
++    mbedtls_mpi *Y = &ecp_kp_Q->MBEDTLS_PRIVATE(Y);
++    mbedtls_mpi *Z = &ecp_kp_Q->MBEDTLS_PRIVATE(Z);
++    ret = mbedtls_mpi_lset(Z, 1);
++    if (ret != 0)
++        return( ret );
++    ret = mbedtls_mpi_read_binary(X, p+1, len);
++    if (ret != 0)
++        return( ret );
++    /* derive Y
++     * (similar derivation of Y in crypto_mbedtls.c:crypto_ecdh_set_peerkey())*/
++    ret = mbedtls_mpi_copy(Y, X) /*(Y is used as input and output obj below)*/
++       || crypto_mbedtls_short_weierstrass_derive_y(ecp_kp_grp, Y, (*p & 1));
++    if (ret != 0)
++        return( ret );
++
++    return mbedtls_ecp_check_pubkey( ecp_kp_grp, ecp_kp_Q );
++}
++
++struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len)
++{
++	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++	if (ctx == NULL)
++		return NULL;
++	mbedtls_pk_init(ctx);
++	/*int rc = mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx);*/
++	int rc = mbedtls_pk_parse_public_key(ctx, der, der_len);
++	if (rc == 0)
++		return (struct crypto_ec_key *)ctx;
++	else if (rc == MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) {
++		/* mbedtls mbedtls_ecp_point_read_binary()
++		 * does not handle point in COMPRESSED format; parse internally */
++		rc = crypto_mbedtls_pk_parse_subpubkey_compressed(ctx,der,der_len);
++		if (rc == 0)
++			return (struct crypto_ec_key *)ctx;
++	}
++
++	mbedtls_pk_free(ctx);
++	os_free(ctx);
++	return NULL;
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++
++static struct crypto_ec_key *
++crypto_ec_key_set_pub_point_for_group(mbedtls_ecp_group_id grp_id,
++                                      const mbedtls_ecp_point *pub,
++                                      const u8 *buf, size_t len)
++{
++	const mbedtls_pk_info_t *pk_info =
++	  mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
++	if (pk_info == NULL)
++		return NULL;
++	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++	if (ctx == NULL)
++		return NULL;
++	mbedtls_pk_init(ctx);
++	if (mbedtls_pk_setup(ctx, pk_info) == 0) {
++		/* (Is private key generation necessary for callers?)
++		 * alt: gen key then overwrite Q
++		 *   mbedtls_ecp_gen_key(grp_id, ecp_kp,
++	         *                       mbedtls_ctr_drbg_random,
++	         *                       crypto_mbedtls_ctr_drbg()) == 0
++	         */
++		mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx);
++		mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++		mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++		mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d);
++		if (mbedtls_ecp_group_load(ecp_kp_grp, grp_id) == 0
++		    && (pub
++		         ? mbedtls_ecp_copy(ecp_kp_Q, pub) == 0
++		         : mbedtls_ecp_point_read_binary(ecp_kp_grp, ecp_kp_Q,
++		                                         buf, len) == 0)
++		    && mbedtls_ecp_gen_privkey(ecp_kp_grp, ecp_kp_d,
++		                               mbedtls_ctr_drbg_random,
++		                               crypto_mbedtls_ctr_drbg()) == 0){
++			return (struct crypto_ec_key *)ctx;
++		}
++	}
++
++	mbedtls_pk_free(ctx);
++	os_free(ctx);
++	return NULL;
++}
++
++struct crypto_ec_key * crypto_ec_key_set_pub(int group, const u8 *x,
++					     const u8 *y, size_t len)
++{
++	mbedtls_ecp_group_id grp_id =
++	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
++	if (grp_id == MBEDTLS_ECP_DP_NONE)
++		return NULL;
++	if (len > MBEDTLS_MPI_MAX_SIZE)
++		return NULL;
++	u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
++	buf[0] = 0x04; /* assume x,y for Short Weierstrass */
++	os_memcpy(buf+1, x, len);
++	os_memcpy(buf+1+len, y, len);
++
++	return crypto_ec_key_set_pub_point_for_group(grp_id,NULL,buf,1+len*2);
++}
++
++struct crypto_ec_key *
++crypto_ec_key_set_pub_point(struct crypto_ec *e,
++			    const struct crypto_ec_point *pub)
++{
++	mbedtls_ecp_group_id grp_id = ((mbedtls_ecp_group *)e)->id;
++	mbedtls_ecp_point *p = (mbedtls_ecp_point *)pub;
++	return crypto_ec_key_set_pub_point_for_group(grp_id, p, NULL, 0);
++}
++
++
++struct crypto_ec_key * crypto_ec_key_gen(int group)
++{
++	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++	if (ctx == NULL)
++		return NULL;
++	mbedtls_pk_init(ctx);
++	if (crypto_mbedtls_keypair_gen(group, ctx) == 0)
++		return (struct crypto_ec_key *)ctx;
++	mbedtls_pk_free(ctx);
++	os_free(ctx);
++	return NULL;
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++void crypto_ec_key_deinit(struct crypto_ec_key *key)
++{
++	mbedtls_pk_free((mbedtls_pk_context *)key);
++	os_free(key);
++}
++
++struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key)
++{
++	/* (similar to crypto_ec_key_get_pubkey_point(),
++	 *  but compressed point format and ASN.1 DER wrapping)*/
++#ifndef MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/
++#define MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES    ( 30 + 2 * MBEDTLS_ECP_MAX_BYTES )
++#endif
++	unsigned char buf[MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES];
++	int len = mbedtls_pk_write_pubkey_der((mbedtls_pk_context *)key,
++	                                      buf, sizeof(buf));
++	if (len < 0)
++		return NULL;
++	/*  Note: data is written at the end of the buffer! Use the
++	 *        return value to determine where you should start
++	 *        using the buffer */
++	unsigned char *p = buf+sizeof(buf)-len;
++
++  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++	if (ecp_kp == NULL)
++		return NULL;
++	mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++	/*  Note: sae_pk.c expects pubkey point in compressed format,
++	 *        but mbedtls_pk_write_pubkey_der() writes uncompressed format.
++	 *        Manually translate format and update lengths in DER format */
++	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
++		unsigned char *end = buf+sizeof(buf);
++		size_t n;
++		/* SubjectPublicKeyInfo SEQUENCE */
++		mbedtls_asn1_get_tag(&p, end, &n,
++		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++		/* algorithm AlgorithmIdentifier */
++		unsigned char *a = p;
++		size_t alen;
++		mbedtls_asn1_get_tag(&p, end, &alen,
++		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++		p += alen;
++		alen = (size_t)(p - a);
++		/* subjectPublicKey BIT STRING */
++		mbedtls_asn1_get_tag(&p, end, &n, MBEDTLS_ASN1_BIT_STRING);
++		/* rewrite into compressed point format and rebuild ASN.1 */
++		p[1] = (buf[sizeof(buf)-1] & 1) ? 0x03 : 0x02;
++		n = 1 + 1 + (n-2)/2;
++		len = mbedtls_asn1_write_len(&p, buf, n) + (int)n;
++		len += mbedtls_asn1_write_tag(&p, buf, MBEDTLS_ASN1_BIT_STRING);
++		os_memmove(p-alen, a, alen);
++		len += alen;
++		p -= alen;
++		len += mbedtls_asn1_write_len(&p, buf, (size_t)len);
++		len += mbedtls_asn1_write_tag(&p, buf,
++		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++	}
++  #endif
++	return wpabuf_alloc_copy(p, (size_t)len);
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++
++struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key,
++						bool include_pub)
++{
++#ifndef MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/
++#define MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES    ( 29 + 3 * MBEDTLS_ECP_MAX_BYTES )
++#endif
++	unsigned char priv[MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES];
++	int privlen = mbedtls_pk_write_key_der((mbedtls_pk_context *)key,
++	                                       priv, sizeof(priv));
++	if (privlen < 0)
++		return NULL;
++
++	struct wpabuf *wbuf;
++
++	/*  Note: data is written at the end of the buffer! Use the
++	 *        return value to determine where you should start
++	 *        using the buffer */
++	/* mbedtls_pk_write_key_der() includes publicKey in DER */
++	if (include_pub)
++		wbuf = wpabuf_alloc_copy(priv+sizeof(priv)-privlen, privlen);
++	else {
++		/* calculate publicKey offset and skip from end of buffer */
++		unsigned char *p = priv+sizeof(priv)-privlen;
++		unsigned char *end = priv+sizeof(priv);
++		size_t len;
++		/* ECPrivateKey SEQUENCE */
++		mbedtls_asn1_get_tag(&p, end, &len,
++		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++		/* version INTEGER */
++		unsigned char *v = p;
++		mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_INTEGER);
++		p += len;
++		/* privateKey OCTET STRING */
++		mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING);
++		p += len;
++		/* parameters ECParameters */
++		mbedtls_asn1_get_tag(&p, end, &len,
++		    MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED);
++		p += len;
++
++		/* write new SEQUENCE header (we know that it fits in priv[]) */
++		len = (size_t)(p - v);
++		p = v;
++		len += mbedtls_asn1_write_len(&p, priv, len);
++		len += mbedtls_asn1_write_tag(&p, priv,
++		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++		wbuf = wpabuf_alloc_copy(p, len);
++	}
++
++	forced_memzero(priv, sizeof(priv));
++	return wbuf;
++}
++
++struct wpabuf * crypto_ec_key_get_pubkey_point(struct crypto_ec_key *key,
++					       int prefix)
++{
++	/*(similarities to crypto_ecdh_get_pubkey(), but different struct)*/
++	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++	if (ecp_kp == NULL)
++		return NULL;
++	mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++	size_t len = CRYPTO_EC_plen(grp);
++  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++	/* len */
++  #endif
++  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS)
++		len = len*2+1;
++  #endif
++	struct wpabuf *buf = wpabuf_alloc(len);
++	if (buf == NULL)
++		return NULL;
++	mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++	if (mbedtls_ecp_point_write_binary(grp, ecp_kp_Q,
++	                                   MBEDTLS_ECP_PF_UNCOMPRESSED, &len,
++	                                   wpabuf_mhead_u8(buf), len) == 0) {
++		if (!prefix) /* Remove 0x04 prefix if requested */
++			os_memmove(wpabuf_mhead(buf),wpabuf_mhead(buf)+1,--len);
++		wpabuf_put(buf, len);
++		return buf;
++	}
++
++	wpabuf_free(buf);
++	return NULL;
++}
++
++struct crypto_ec_point *
++crypto_ec_key_get_public_key(struct crypto_ec_key *key)
++{
++	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++	if (ecp_kp == NULL)
++		return NULL;
++	mbedtls_ecp_point *p = os_malloc(sizeof(*p));
++	if (p != NULL) {
++		/*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/
++		mbedtls_ecp_point_init(p);
++		mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++		if (mbedtls_ecp_copy(p, ecp_kp_Q)) {
++			mbedtls_ecp_point_free(p);
++			os_free(p);
++			p = NULL;
++		}
++	}
++	return (struct crypto_ec_point *)p;
++}
++
++struct crypto_bignum *
++crypto_ec_key_get_private_key(struct crypto_ec_key *key)
++{
++	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++	if (ecp_kp == NULL)
++		return NULL;
++	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
++	if (bn) {
++		/*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/
++		mbedtls_mpi_init(bn);
++		mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d);
++		if (mbedtls_mpi_copy(bn, ecp_kp_d)) {
++			mbedtls_mpi_free(bn);
++			os_free(bn);
++			bn = NULL;
++		}
++	}
++	return (struct crypto_bignum *)bn;
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++static mbedtls_md_type_t crypto_ec_key_sign_md(size_t len)
++{
++	/* get mbedtls_md_type_t from length of hash data to be signed */
++	switch (len) {
++	case 64: return MBEDTLS_MD_SHA512;
++	case 48: return MBEDTLS_MD_SHA384;
++	case 32: return MBEDTLS_MD_SHA256;
++	case 20: return MBEDTLS_MD_SHA1;
++	case 16: return MBEDTLS_MD_MD5;
++	default: return MBEDTLS_MD_NONE;
++	}
++}
++
++struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
++				   size_t len)
++{
++  #ifndef MBEDTLS_PK_SIGNATURE_MAX_SIZE /*(defined since mbedtls 2.20.0)*/
++  #if MBEDTLS_ECDSA_MAX_LEN > MBEDTLS_MPI_MAX_SIZE
++  #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_ECDSA_MAX_LEN
++  #else
++  #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_MPI_MAX_SIZE
++  #endif
++  #endif
++	size_t sig_len = MBEDTLS_PK_SIGNATURE_MAX_SIZE;
++	struct wpabuf *buf = wpabuf_alloc(sig_len);
++	if (buf == NULL)
++		return NULL;
++	if (mbedtls_pk_sign((mbedtls_pk_context *)key,
++	                    crypto_ec_key_sign_md(len), data, len,
++	                    wpabuf_mhead_u8(buf),
++  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++	                    sig_len,
++  #endif
++	                    &sig_len,
++	                    mbedtls_ctr_drbg_random,
++	                    crypto_mbedtls_ctr_drbg()) == 0) {
++		wpabuf_put(buf, sig_len);
++		return buf;
++	}
++
++	wpabuf_free(buf);
++	return NULL;
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++struct wpabuf * crypto_ec_key_sign_r_s(struct crypto_ec_key *key,
++				       const u8 *data, size_t len)
++{
++	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++	if (ecp_kp == NULL)
++		return NULL;
++
++	size_t sig_len = MBEDTLS_ECDSA_MAX_LEN;
++	u8 buf[MBEDTLS_ECDSA_MAX_LEN];
++	if (mbedtls_ecdsa_write_signature(ecp_kp, crypto_ec_key_sign_md(len),
++	                                  data, len, buf,
++  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++	                                  sig_len,
++  #endif
++	                                  &sig_len,
++	                                  mbedtls_ctr_drbg_random,
++	                                  crypto_mbedtls_ctr_drbg())) {
++		return NULL;
++	}
++
++	/*(mbedtls_ecdsa_write_signature() writes signature in ASN.1)*/
++	/* parse ASN.1 to get r and s and lengths */
++	u8 *p = buf, *r, *s;
++	u8 *end = p + sig_len;
++	size_t rlen, slen;
++	mbedtls_asn1_get_tag(&p, end, &rlen,
++	  MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++	mbedtls_asn1_get_tag(&p, end, &rlen, MBEDTLS_ASN1_INTEGER);
++	r = p;
++	p += rlen;
++	mbedtls_asn1_get_tag(&p, end, &slen, MBEDTLS_ASN1_INTEGER);
++	s = p;
++
++	/* write raw r and s into out
++	 * (including removal of leading 0 if added for ASN.1 integer)
++	 * note: DPP caller expects raw r, s each padded to prime len */
++	mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++	size_t plen = CRYPTO_EC_plen(ecp_kp_grp);
++	if (rlen > plen) {
++		r += (rlen - plen);
++		rlen = plen;
++	}
++	if (slen > plen) {
++		s += (slen - plen);
++		slen = plen;
++	}
++	struct wpabuf *out = wpabuf_alloc(plen*2);
++	if (out) {
++		wpabuf_put(out, plen*2);
++		p = wpabuf_mhead_u8(out);
++		os_memset(p, 0, plen*2);
++		os_memcpy(p+plen*1-rlen, r, rlen);
++		os_memcpy(p+plen*2-slen, s, slen);
++	}
++	return out;
++}
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
++				   size_t len, const u8 *sig, size_t sig_len)
++{
++	switch (mbedtls_pk_verify((mbedtls_pk_context *)key,
++	                          crypto_ec_key_sign_md(len), data, len,
++	                          sig, sig_len)) {
++	case 0:
++	/*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */
++		return 1;
++	case MBEDTLS_ERR_ECP_VERIFY_FAILED:
++		return 0;
++	default:
++		return -1;
++	}
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++int crypto_ec_key_verify_signature_r_s(struct crypto_ec_key *key,
++				       const u8 *data, size_t len,
++				       const u8 *r, size_t r_len,
++				       const u8 *s, size_t s_len)
++{
++	/* reimplement mbedtls_ecdsa_read_signature() without encoding r and s
++	 * into ASN.1 just for mbedtls_ecdsa_read_signature() to decode ASN.1 */
++	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++	if (ecp_kp == NULL)
++		return -1;
++	mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++	mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++
++	mbedtls_mpi mpi_r;
++	mbedtls_mpi mpi_s;
++	mbedtls_mpi_init(&mpi_r);
++	mbedtls_mpi_init(&mpi_s);
++	int ret = mbedtls_mpi_read_binary(&mpi_r, r, r_len)
++	       || mbedtls_mpi_read_binary(&mpi_s, s, s_len) ? -1 : 0;
++	if (ret == 0) {
++		ret = mbedtls_ecdsa_verify(ecp_kp_grp, data, len,
++		                           ecp_kp_Q, &mpi_r, &mpi_s);
++		ret = ret ? ret == MBEDTLS_ERR_ECP_BAD_INPUT_DATA ? 0 : -1 : 1;
++	}
++	mbedtls_mpi_free(&mpi_r);
++	mbedtls_mpi_free(&mpi_s);
++	return ret;
++}
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++int crypto_ec_key_group(struct crypto_ec_key *key)
++{
++	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++	if (ecp_kp == NULL)
++		return -1;
++	mbedtls_ecp_group *ecp_group = &ecp_kp->MBEDTLS_PRIVATE(grp);
++	return crypto_mbedtls_ike_id_from_ecp_group_id(ecp_group->id);
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++
++int crypto_ec_key_cmp(struct crypto_ec_key *key1, struct crypto_ec_key *key2)
++{
++#if 0 /*(DPP is passing two public keys; unable to use pk_check_pair())*/
++  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++	return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1,
++	                             (const mbedtls_pk_context *)key2) ? -1 : 0;
++  #else
++	return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1,
++	                             (const mbedtls_pk_context *)key2,
++	                             mbedtls_ctr_drbg_random,
++	                             crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++  #endif
++#else
++	mbedtls_ecp_keypair *ecp_kp1=mbedtls_pk_ec(*(mbedtls_pk_context *)key1);
++	mbedtls_ecp_keypair *ecp_kp2=mbedtls_pk_ec(*(mbedtls_pk_context *)key2);
++	if (ecp_kp1 == NULL || ecp_kp2 == NULL)
++		return -1;
++	mbedtls_ecp_group *ecp_kp1_grp = &ecp_kp1->MBEDTLS_PRIVATE(grp);
++	mbedtls_ecp_group *ecp_kp2_grp = &ecp_kp2->MBEDTLS_PRIVATE(grp);
++	mbedtls_ecp_point *ecp_kp1_Q = &ecp_kp1->MBEDTLS_PRIVATE(Q);
++	mbedtls_ecp_point *ecp_kp2_Q = &ecp_kp2->MBEDTLS_PRIVATE(Q);
++	return ecp_kp1_grp->id != ecp_kp2_grp->id
++	    || mbedtls_ecp_point_cmp(ecp_kp1_Q, ecp_kp2_Q) ? -1 : 0;
++#endif
++}
++
++void crypto_ec_key_debug_print(const struct crypto_ec_key *key,
++			       const char *title)
++{
++	/* TBD: what info is desirable here and in what human readable format?*/
++	/*(crypto_openssl.c prints a human-readably public key and attributes)*/
++  #if 0
++	struct mbedtls_pk_debug_item debug_item;
++	if (mbedtls_pk_debug((const mbedtls_pk_context *)key, &debug_item))
++		return;
++	/* ... */
++  #endif
++	wpa_printf(MSG_DEBUG, "%s: %s not implemented", title, __func__);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_CSR
++
++#include <mbedtls/x509_csr.h>
++#include <mbedtls/oid.h>
++
++struct crypto_csr * crypto_csr_init(void)
++{
++	mbedtls_x509write_csr *csr = os_malloc(sizeof(*csr));
++	if (csr != NULL)
++		mbedtls_x509write_csr_init(csr);
++	return (struct crypto_csr *)csr;
++}
++
++struct crypto_csr * crypto_csr_verify(const struct wpabuf *req)
++{
++	/* future: look for alternatives to MBEDTLS_PRIVATE() access */
++
++	/* sole caller src/common/dpp_crypto.c:dpp_validate_csr()
++	 * uses (mbedtls_x509_csr *) to obtain CSR_ATTR_CHALLENGE_PASSWORD
++	 * so allocate different object (mbedtls_x509_csr *) and special-case
++	 * object when used in crypto_csr_get_attribute() and when free()d in
++	 * crypto_csr_deinit(). */
++
++	mbedtls_x509_csr *csr = os_malloc(sizeof(*csr));
++	if (csr == NULL)
++		return NULL;
++	mbedtls_x509_csr_init(csr);
++	const mbedtls_md_info_t *md_info;
++	unsigned char digest[MBEDTLS_MD_MAX_SIZE];
++	if (mbedtls_x509_csr_parse_der(csr,wpabuf_head(req),wpabuf_len(req))==0
++	    && (md_info=mbedtls_md_info_from_type(csr->MBEDTLS_PRIVATE(sig_md)))
++	       != NULL
++	    && mbedtls_md(md_info, csr->cri.p, csr->cri.len, digest) == 0) {
++		switch (mbedtls_pk_verify(&csr->pk,csr->MBEDTLS_PRIVATE(sig_md),
++		                          digest, mbedtls_md_get_size(md_info),
++		                          csr->MBEDTLS_PRIVATE(sig).p,
++		                          csr->MBEDTLS_PRIVATE(sig).len)) {
++		case 0:
++		/*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */
++			return (struct crypto_csr *)((uintptr_t)csr | 1uL);
++		default:
++			break;
++		}
++	}
++
++	mbedtls_x509_csr_free(csr);
++	os_free(csr);
++	return NULL;
++}
++
++void crypto_csr_deinit(struct crypto_csr *csr)
++{
++	if ((uintptr_t)csr & 1uL) {
++		csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL);
++		mbedtls_x509_csr_free((mbedtls_x509_csr *)csr);
++	}
++	else
++		mbedtls_x509write_csr_free((mbedtls_x509write_csr *)csr);
++	os_free(csr);
++}
++
++int crypto_csr_set_ec_public_key(struct crypto_csr *csr,
++				 struct crypto_ec_key *key)
++{
++	mbedtls_x509write_csr_set_key((mbedtls_x509write_csr *)csr,
++	                              (mbedtls_pk_context *)key);
++	return 0;
++}
++
++int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type,
++			const char *name)
++{
++	/* specialized for src/common/dpp_crypto.c */
++
++	/* sole caller src/common/dpp_crypto.c:dpp_build_csr()
++	 * calls this function only once, using type == CSR_NAME_CN
++	 * (If called more than once, this code would need to append
++	 *  components to the subject name, which we could do by
++	 *  appending to (mbedtls_x509write_csr *) private member
++	 *  mbedtls_asn1_named_data *MBEDTLS_PRIVATE(subject)) */
++
++	const char *label;
++	switch (type) {
++	case CSR_NAME_CN: label = "CN="; break;
++	case CSR_NAME_SN: label = "SN="; break;
++	case CSR_NAME_C:  label = "C=";  break;
++	case CSR_NAME_O:  label = "O=";  break;
++	case CSR_NAME_OU: label = "OU="; break;
++	default: return -1;
++	}
++
++	size_t len = strlen(name);
++	struct wpabuf *buf = wpabuf_alloc(3+len+1);
++	if (buf == NULL)
++		return -1;
++	wpabuf_put_data(buf, label, strlen(label));
++	wpabuf_put_data(buf, name, len+1); /*(include trailing '\0')*/
++	/* Note: 'name' provided is set as given and should be backslash-escaped
++	 * by caller when necessary, e.g. literal ',' which are not separating
++	 * components should be backslash-escaped */
++
++	int ret =
++	  mbedtls_x509write_csr_set_subject_name((mbedtls_x509write_csr *)csr,
++	                                         wpabuf_head(buf)) ? -1 : 0;
++	wpabuf_free(buf);
++	return ret;
++}
++
++/* OBJ_pkcs9_challengePassword  1 2 840 113549 1 9 7 */
++static const char OBJ_pkcs9_challengePassword[] = MBEDTLS_OID_PKCS9 "\x07";
++
++int crypto_csr_set_attribute(struct crypto_csr *csr, enum crypto_csr_attr attr,
++			     int attr_type, const u8 *value, size_t len)
++{
++	/* specialized for src/common/dpp_crypto.c */
++	/* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes
++	 *   attr      == CSR_ATTR_CHALLENGE_PASSWORD
++	 *   attr_type == ASN1_TAG_UTF8STRING */
++
++	const char *oid;
++	size_t oid_len;
++	switch (attr) {
++	case CSR_ATTR_CHALLENGE_PASSWORD:
++		oid = OBJ_pkcs9_challengePassword;
++		oid_len = sizeof(OBJ_pkcs9_challengePassword)-1;
++		break;
++	default:
++		return -1;
++	}
++
++  #if 0 /*(incorrect; sets an extension, not an attribute)*/
++	return mbedtls_x509write_csr_set_extension((mbedtls_x509write_csr *)csr,
++	                                           oid, oid_len,
++	  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++	                                           0, /*(critical flag)*/
++	  #endif
++	                                           value, len) ? -1 : 0;
++  #else
++	(void)oid;
++	(void)oid_len;
++  #endif
++
++	/* mbedtls does not currently provide way to set an attribute in a CSR:
++	 *   https://github.com/Mbed-TLS/mbedtls/issues/4886 */
++	wpa_printf(MSG_ERROR,
++	  "mbedtls does not currently support setting challengePassword "
++	  "attribute in CSR");
++	return -1;
++}
++
++const u8 * mbedtls_x509_csr_attr_oid_value(mbedtls_x509_csr *csr,
++                                           const char *oid, size_t oid_len,
++                                           size_t *vlen, int *vtype)
++{
++	/* Note: mbedtls_x509_csr_parse_der() has parsed and validated CSR,
++	 *	   so validation checks are not repeated here
++	 *
++	 * It would be nicer if (mbedtls_x509_csr *) had an mbedtls_x509_buf of
++	 * Attributes (or at least a pointer) since mbedtls_x509_csr_parse_der()
++	 * already parsed the rest of CertificationRequestInfo, some of which is
++	 * repeated here to step to Attributes.  Since csr->subject_raw.p points
++	 * into csr->cri.p, which points into csr->raw.p, step over version and
++	 * subject of CertificationRequestInfo (SEQUENCE) */
++	unsigned char *p = csr->subject_raw.p + csr->subject_raw.len;
++	unsigned char *end = csr->cri.p + csr->cri.len, *ext;
++	size_t len;
++
++	/* step over SubjectPublicKeyInfo */
++	mbedtls_asn1_get_tag(&p, end, &len,
++	    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++	p += len;
++
++	/* Attributes
++	 *   { ATTRIBUTE:IOSet } ::= SET OF { SEQUENCE { OID, value } }
++	 */
++	if (mbedtls_asn1_get_tag(&p, end, &len,
++	      MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) != 0) {
++		return NULL;
++	}
++	while (p < end) {
++		if (mbedtls_asn1_get_tag(&p, end, &len,
++		      MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
++			return NULL;
++		}
++		ext = p;
++		p += len;
++
++		if (mbedtls_asn1_get_tag(&ext,end,&len,MBEDTLS_ASN1_OID) != 0)
++			return NULL;
++		if (oid_len != len || 0 != memcmp(ext, oid, oid_len))
++			continue;
++
++		/* found oid; return value */
++		*vtype = *ext++; /* tag */
++		return (mbedtls_asn1_get_len(&ext,end,vlen) == 0) ? ext : NULL;
++	}
++
++	return NULL;
++}
++
++const u8 * crypto_csr_get_attribute(struct crypto_csr *csr,
++				    enum crypto_csr_attr attr,
++				    size_t *len, int *type)
++{
++	/* specialized for src/common/dpp_crypto.c */
++	/* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes
++	 *   attr == CSR_ATTR_CHALLENGE_PASSWORD */
++
++	const char *oid;
++	size_t oid_len;
++	switch (attr) {
++	case CSR_ATTR_CHALLENGE_PASSWORD:
++		oid = OBJ_pkcs9_challengePassword;
++		oid_len = sizeof(OBJ_pkcs9_challengePassword)-1;
++		break;
++	default:
++		return NULL;
++	}
++
++	/* see crypto_csr_verify(); expecting (mbedtls_x509_csr *) tagged |=1 */
++	if (!((uintptr_t)csr & 1uL))
++		return NULL;
++	csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL);
++
++	return mbedtls_x509_csr_attr_oid_value((mbedtls_x509_csr *)csr,
++	                                       oid, oid_len, len, type);
++}
++
++struct wpabuf * crypto_csr_sign(struct crypto_csr *csr,
++				struct crypto_ec_key *key,
++				enum crypto_hash_alg algo)
++{
++	mbedtls_md_type_t sig_md;
++	switch (algo) {
++  #ifdef MBEDTLS_SHA256_C
++	case CRYPTO_HASH_ALG_SHA256: sig_md = MBEDTLS_MD_SHA256; break;
++  #endif
++  #ifdef MBEDTLS_SHA512_C
++	case CRYPTO_HASH_ALG_SHA384: sig_md = MBEDTLS_MD_SHA384; break;
++	case CRYPTO_HASH_ALG_SHA512: sig_md = MBEDTLS_MD_SHA512; break;
++  #endif
++	default:
++		return NULL;
++	}
++	mbedtls_x509write_csr_set_md_alg((mbedtls_x509write_csr *)csr, sig_md);
++
++  #if 0
++	unsigned char key_usage = MBEDTLS_X509_KU_DIGITAL_SIGNATURE
++	                        | MBEDTLS_X509_KU_KEY_CERT_SIGN;
++	if (mbedtls_x509write_csr_set_key_usage((mbedtls_x509write_csr *)csr,
++	                                        key_usage))
++		return NULL;
++  #endif
++
++  #if 0
++	unsigned char ns_cert_type = MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT
++	                           | MBEDTLS_X509_NS_CERT_TYPE_EMAIL;
++	if (mbedtls_x509write_csr_set_ns_cert_type((mbedtls_x509write_csr *)csr,
++	                                           ns_cert_type))
++		return NULL;
++  #endif
++
++  #if 0
++	/* mbedtls does not currently provide way to set an attribute in a CSR:
++	 *   https://github.com/Mbed-TLS/mbedtls/issues/4886
++	 * XXX: hwsim dpp_enterprise test fails due to this limitation.
++	 *
++	 * Current usage of this function is solely by dpp_build_csr(),
++	 * so as a kludge, might consider custom (struct crypto_csr *)
++	 * containing (mbedtls_x509write_csr *) and a list of attributes
++	 * (i.e. challengePassword).  Might have to totally reimplement
++	 * mbedtls_x509write_csr_der(); underlying x509write_csr_der_internal()
++	 * handles signing the CSR.  (This is more work that appending an
++	 * Attributes section to end of CSR and adjusting ASN.1 length of CSR.)
++	 */
++  #endif
++
++	unsigned char buf[4096]; /* XXX: large enough?  too large? */
++	int len = mbedtls_x509write_csr_der((mbedtls_x509write_csr *)csr,
++	                                    buf, sizeof(buf),
++	                                    mbedtls_ctr_drbg_random,
++	                                    crypto_mbedtls_ctr_drbg());
++	if (len < 0)
++		return NULL;
++	/*  Note: data is written at the end of the buffer! Use the
++	 *        return value to determine where you should start
++	 *        using the buffer */
++	return wpabuf_alloc_copy(buf+sizeof(buf)-len, (size_t)len);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_CSR */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_PKCS7
++
++#if 0
++#include <mbedtls/pkcs7.h> /* PKCS7 is not currently supported in mbedtls */
++#include <mbedtls/pem.h>
++#endif
++
++struct wpabuf * crypto_pkcs7_get_certificates(const struct wpabuf *pkcs7)
++{
++	/* PKCS7 is not currently supported in mbedtls */
++	return NULL;
++
++#if 0
++	/* https://github.com/naynajain/mbedtls-1 branch: development-pkcs7
++	 * (??? potential future contribution to mbedtls ???) */
++
++	/* Note: PKCS7 signature *is not* verified by this function.
++	 * The function interface does not provide for passing a certificate */
++
++	mbedtls_pkcs7 mpkcs7;
++	mbedtls_pkcs7_init(&mpkcs7);
++	int pkcs7_type = mbedtls_pkcs7_parse_der(wpabuf_head(pkcs7),
++	                                         wpabuf_len(pkcs7),
++	                                         &mpkcs7);
++	wpabuf *buf = NULL;
++	do {
++		if (pkcs7_type < 0)
++			break;
++
++		/* src/common/dpp.c:dpp_parse_cred_dot1x() interested in certs
++		 * for wpa_supplicant/dpp_supplicant.c:wpas_dpp_add_network()
++		 * (? are adding certificate headers and footers desired ?) */
++
++		/* development-pkcs7 branch does not currently provide
++		 * additional interfaces to retrieve the parsed data */
++
++		mbedtls_x509_crt *certs =
++		  &mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(certs);
++		int ncerts =
++		  mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(no_of_certs);
++
++		/* allocate buffer for PEM (base64-encoded DER)
++		 * plus header, footer, newlines, and some extra */
++		buf = wpabuf_alloc((wpabuf_len(pkcs7)+2)/3*4 + ncerts*64);
++		if (buf == NULL)
++			break;
++
++		#define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n"
++		#define PEM_END_CRT   "-----END CERTIFICATE-----\n"
++		size_t olen;
++		for (int i = 0; i < ncerts; ++i) {
++			int ret = mbedtls_pem_write_buffer(
++			            PEM_BEGIN_CRT, PEM_END_CRT,
++			            certs[i].raw.p, certs[i].raw.len,
++			            wpabuf_mhead(buf, 0), wpabuf_tailroom(buf),
++			            &olen));
++			if (ret == 0)
++				wpabuf_put(buf, olen);
++			} else {
++				if (ret == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL)
++					ret = wpabuf_resize(
++					        &buf,olen-wpabuf_tailroom(buf));
++				if (ret == 0) {
++					--i;/*(adjust loop iterator for retry)*/
++					continue;
++				}
++				wpabuf_free(buf);
++				buf = NULL;
++				break;
++			}
++		}
++	} while (0);
++
++	mbedtls_pkcs7_free(&mpkcs7);
++	return buf;
++#endif
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_PKCS7 */
++
++
++#ifdef MBEDTLS_ARC4_C
++#include <mbedtls/arc4.h>
++int rc4_skip(const u8 *key, size_t keylen, size_t skip,
++	     u8 *data, size_t data_len)
++{
++	mbedtls_arc4_context ctx;
++	mbedtls_arc4_init(&ctx);
++	mbedtls_arc4_setup(&ctx, key, keylen);
++
++	if (skip) {
++		/*(prefer [16] on ancient hardware with smaller cache lines)*/
++		unsigned char skip_buf[64]; /*('skip' is generally small)*/
++		/*os_memset(skip_buf, 0, sizeof(skip_buf));*/ /*(necessary?)*/
++		size_t len;
++		do {
++			len = skip > sizeof(skip_buf) ? sizeof(skip_buf) : skip;
++			mbedtls_arc4_crypt(&ctx, len, skip_buf, skip_buf);
++		} while ((skip -= len));
++	}
++
++	int ret = mbedtls_arc4_crypt(&ctx, data_len, data, data);
++	mbedtls_arc4_free(&ctx);
++	return ret;
++}
++#endif
++
++
++/* duplicated in tls_mbedtls.c:tls_mbedtls_readfile()*/
++__attribute_noinline__
++static int crypto_mbedtls_readfile(const char *path, u8 **buf, size_t *n)
++{
++  #if 0 /* #ifdef MBEDTLS_FS_IO */
++	/*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/
++	if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) {
++		wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path);
++		return -1;
++	}
++  #else
++	/*(use os_readfile() so that we can use os_free()
++	 *(if we use mbedtls_pk_load_file() above, macros prevent calling free()
++	 * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free()
++	 * on buf aborts in tests if buf not allocated via os_malloc())*/
++	*buf = (u8 *)os_readfile(path, n);
++	if (!*buf) {
++		wpa_printf(MSG_ERROR, "error: os_readfile %s", path);
++		return -1;
++	}
++	u8 *buf0 = os_realloc(*buf, *n+1);
++	if (!buf0) {
++		bin_clear_free(*buf, *n);
++		*buf = NULL;
++		return -1;
++	}
++	buf0[(*n)++] = '\0';
++	*buf = buf0;
++  #endif
++	return 0;
++}
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_RSA
++#ifdef MBEDTLS_RSA_C
++
++#include <mbedtls/pk.h>
++#include <mbedtls/rsa.h>
++
++struct crypto_rsa_key * crypto_rsa_key_read(const char *file, bool private_key)
++{
++	/* mbedtls_pk_parse_keyfile() and mbedtls_pk_parse_public_keyfile()
++	 * require #ifdef MBEDTLS_FS_IO in mbedtls library.  Prefer to use
++	 * crypto_mbedtls_readfile(), which wraps os_readfile() */
++	u8 *data;
++	size_t len;
++	if (crypto_mbedtls_readfile(file, &data, &len) != 0)
++		return NULL;
++
++	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++	if (ctx == NULL) {
++		bin_clear_free(data, len);
++		return NULL;
++	}
++	mbedtls_pk_init(ctx);
++
++	int rc;
++	rc = (private_key
++	      ? mbedtls_pk_parse_key(ctx, data, len, NULL, 0
++	  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++	                            ,mbedtls_ctr_drbg_random,
++	                             crypto_mbedtls_ctr_drbg()
++	  #endif
++	                            )
++	      : mbedtls_pk_parse_public_key(ctx, data, len)) == 0
++	    && mbedtls_pk_can_do(ctx, MBEDTLS_PK_RSA);
++
++	bin_clear_free(data, len);
++
++	if (rc) {
++		/* use MBEDTLS_RSA_PKCS_V21 padding for RSAES-OAEP */
++		/* use MBEDTLS_MD_SHA256 for these hostap interfaces */
++	  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++		/*(no return value in mbedtls 2.x)*/
++		mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx),
++		                        MBEDTLS_RSA_PKCS_V21,
++		                        MBEDTLS_MD_SHA256);
++	  #else
++		if (mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx),
++		                            MBEDTLS_RSA_PKCS_V21,
++		                            MBEDTLS_MD_SHA256) == 0)
++	  #endif
++			return (struct crypto_rsa_key *)ctx;
++	}
++
++	mbedtls_pk_free(ctx);
++	os_free(ctx);
++	return NULL;
++}
++
++struct wpabuf * crypto_rsa_oaep_sha256_encrypt(struct crypto_rsa_key *key,
++					       const struct wpabuf *in)
++{
++	mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key);
++	size_t olen = mbedtls_rsa_get_len(pk_rsa);
++	struct wpabuf *buf = wpabuf_alloc(olen);
++	if (buf == NULL)
++		return NULL;
++
++	/* mbedtls_pk_encrypt() takes a few more hops to get to same func */
++	if (mbedtls_rsa_rsaes_oaep_encrypt(pk_rsa,
++	                                   mbedtls_ctr_drbg_random,
++	                                   crypto_mbedtls_ctr_drbg(),
++	  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++	                                   MBEDTLS_RSA_PRIVATE,
++	  #endif
++	                                   NULL, 0,
++	                                   wpabuf_len(in), wpabuf_head(in),
++	                                   wpabuf_put(buf, olen)) == 0) {
++		return buf;
++	}
++
++	wpabuf_clear_free(buf);
++	return NULL;
++}
++
++struct wpabuf * crypto_rsa_oaep_sha256_decrypt(struct crypto_rsa_key *key,
++					       const struct wpabuf *in)
++{
++	mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key);
++	size_t olen = mbedtls_rsa_get_len(pk_rsa);
++	struct wpabuf *buf = wpabuf_alloc(olen);
++	if (buf == NULL)
++		return NULL;
++
++	/* mbedtls_pk_decrypt() takes a few more hops to get to same func */
++	if (mbedtls_rsa_rsaes_oaep_decrypt(pk_rsa,
++	                                   mbedtls_ctr_drbg_random,
++	                                   crypto_mbedtls_ctr_drbg(),
++	  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++	                                   MBEDTLS_RSA_PUBLIC,
++	  #endif
++	                                   NULL, 0, &olen, wpabuf_head(in),
++	                                   wpabuf_mhead(buf), olen) == 0) {
++		wpabuf_put(buf, olen);
++		return buf;
++	}
++
++	wpabuf_clear_free(buf);
++	return NULL;
++}
++
++void crypto_rsa_key_free(struct crypto_rsa_key *key)
++{
++	mbedtls_pk_free((mbedtls_pk_context *)key);
++	os_free(key);
++}
++
++#endif /* MBEDTLS_RSA_C */
++#endif /* CRYPTO_MBEDTLS_CRYPTO_RSA */
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE
++
++struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id,
++			       enum hpke_kdf_id kdf_id,
++			       enum hpke_aead_id aead_id,
++			       struct crypto_ec_key *peer_pub,
++			       const u8 *info, size_t info_len,
++			       const u8 *aad, size_t aad_len,
++			       const u8 *pt, size_t pt_len)
++{
++	/* not yet implemented */
++	return NULL;
++}
++
++struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
++			       enum hpke_kdf_id kdf_id,
++			       enum hpke_aead_id aead_id,
++			       struct crypto_ec_key *own_priv,
++			       const u8 *info, size_t info_len,
++			       const u8 *aad, size_t aad_len,
++			       const u8 *enc_ct, size_t enc_ct_len)
++{
++	/* not yet implemented */
++	return NULL;
++}
++
++#endif
+diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
+index ffeddbadd..07c36d850 100644
+--- a/src/crypto/crypto_module_tests.c
++++ b/src/crypto/crypto_module_tests.c
+@@ -2470,6 +2470,139 @@ static int test_hpke(void)
+ }
+ 
+ 
++static int test_ecc(void)
++{
++#ifdef CONFIG_ECC
++#ifndef CONFIG_TLS_INTERNAL
++#ifndef CONFIG_TLS_GNUTLS
++#if defined(CONFIG_TLS_MBEDTLS) \
++ || defined(CONFIG_TLS_OPENSSL) \
++ || defined(CONFIG_TLS_WOLFSSL)
++	wpa_printf(MSG_INFO, "Testing ECC");
++	/* Note: some tests below are valid on supported Short Weierstrass
++	 * curves, but not on Montgomery curves (e.g. IKE groups 31 and 32)
++	 * (e.g. deriving and comparing y^2 test below not valid on Montgomery)
++	 */
++#ifdef CONFIG_TLS_MBEDTLS
++	const int grps[] = {19, 20, 21, 25, 26, 28};
++#endif
++#ifdef CONFIG_TLS_OPENSSL
++	const int grps[] = {19, 20, 21, 26};
++#endif
++#ifdef CONFIG_TLS_WOLFSSL
++	const int grps[] = {19, 20, 21, 26};
++#endif
++	uint32_t i;
++	struct crypto_ec *e = NULL;
++	struct crypto_ec_point *p = NULL, *q = NULL;
++	struct crypto_bignum *x = NULL, *y = NULL;
++#ifdef CONFIG_DPP
++	u8 bin[4096];
++#endif
++	for (i = 0; i < ARRAY_SIZE(grps); ++i) {
++		e = crypto_ec_init(grps[i]);
++		if (e == NULL
++		    || crypto_ec_prime_len(e) == 0
++		    || crypto_ec_prime_len_bits(e) == 0
++		    || crypto_ec_order_len(e) == 0
++		    || crypto_ec_get_prime(e) == NULL
++		    || crypto_ec_get_order(e) == NULL
++		    || crypto_ec_get_a(e) == NULL
++		    || crypto_ec_get_b(e) == NULL
++		    || crypto_ec_get_generator(e) == NULL) {
++			break;
++		}
++#ifdef CONFIG_DPP
++		struct crypto_ec_key *key = crypto_ec_key_gen(grps[i]);
++		if (key == NULL)
++			break;
++		p = crypto_ec_key_get_public_key(key);
++		q = crypto_ec_key_get_public_key(key);
++		crypto_ec_key_deinit(key);
++		if (p == NULL || q == NULL)
++			break;
++		if (!crypto_ec_point_is_on_curve(e, p))
++			break;
++
++		/* inverted point should not match original;
++		 * double-invert should match */
++		if (crypto_ec_point_invert(e, q) != 0
++		    || crypto_ec_point_cmp(e, p, q) == 0
++		    || crypto_ec_point_invert(e, q) != 0
++		    || crypto_ec_point_cmp(e, p, q) != 0) {
++			break;
++		}
++
++		/* crypto_ec_point_to_bin() and crypto_ec_point_from_bin()
++		 * imbalanced interfaces? */
++		size_t prime_len = crypto_ec_prime_len(e);
++		if (prime_len * 2 > sizeof(bin))
++			break;
++		if (crypto_ec_point_to_bin(e, p, bin, bin+prime_len) != 0)
++			break;
++		struct crypto_ec_point *tmp = crypto_ec_point_from_bin(e, bin);
++		if (tmp == NULL)
++			break;
++		if (crypto_ec_point_cmp(e, p, tmp) != 0) {
++			crypto_ec_point_deinit(tmp, 0);
++			break;
++		}
++		crypto_ec_point_deinit(tmp, 0);
++
++		x = crypto_bignum_init();
++		y = crypto_bignum_init_set(bin+prime_len, prime_len);
++		if (x == NULL || y == NULL || crypto_ec_point_x(e, p, x) != 0)
++			break;
++		struct crypto_bignum *y2 = crypto_ec_point_compute_y_sqr(e, x);
++		if (y2 == NULL)
++			break;
++		if (crypto_bignum_sqrmod(y, crypto_ec_get_prime(e), y) != 0
++		    || crypto_bignum_cmp(y, y2) != 0) {
++			crypto_bignum_deinit(y2, 0);
++			break;
++		}
++		crypto_bignum_deinit(y2, 0);
++		crypto_bignum_deinit(x, 0);
++		crypto_bignum_deinit(y, 0);
++		x = NULL;
++		y = NULL;
++
++		x = crypto_bignum_init();
++		if (x == NULL)
++			break;
++		if (crypto_bignum_rand(x, crypto_ec_get_prime(e)) != 0)
++			break;
++		crypto_bignum_deinit(x, 0);
++		x = NULL;
++
++		crypto_ec_point_deinit(p, 0);
++		p = NULL;
++		crypto_ec_point_deinit(q, 0);
++		q = NULL;
++#endif /* CONFIG_DPP */
++		crypto_ec_deinit(e);
++		e = NULL;
++	}
++	if (i != ARRAY_SIZE(grps)) {
++		crypto_bignum_deinit(x, 0);
++		crypto_bignum_deinit(y, 0);
++		crypto_ec_point_deinit(p, 0);
++		crypto_ec_point_deinit(q, 0);
++		crypto_ec_deinit(e);
++		wpa_printf(MSG_INFO,
++		           "ECC test case failed tls_id:%d", grps[i]);
++		return -1;
++	}
++
++	wpa_printf(MSG_INFO, "ECC test cases passed");
++#endif
++#endif /* !CONFIG_TLS_GNUTLS */
++#endif /* !CONFIG_TLS_INTERNAL */
++#endif /* CONFIG_ECC */
++	return 0;
++}
++
++
+ static int test_ms_funcs(void)
+ {
+ #ifndef CONFIG_FIPS
+@@ -2591,6 +2724,7 @@ int crypto_module_tests(void)
+ 	    test_fips186_2_prf() ||
+ 	    test_extract_expand_hkdf() ||
+ 	    test_hpke() ||
++	    test_ecc() ||
+ 	    test_ms_funcs())
+ 		ret = -1;
+ 
+diff --git a/src/crypto/tls_mbedtls.c b/src/crypto/tls_mbedtls.c
+new file mode 100644
+index 000000000..d83a3db73
+--- /dev/null
++++ b/src/crypto/tls_mbedtls.c
+@@ -0,0 +1,3313 @@
++/*
++ * SSL/TLS interface functions for mbed TLS
++ *
++ * SPDX-FileCopyrightText: 2022 Glenn Strauss <gstrauss@gluelogic.com>
++ * SPDX-License-Identifier: BSD-3-Clause
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ *
++ * template:  src/crypto/tls_none.c
++ * reference: src/crypto/tls_*.c
++ *
++ * Known Limitations:
++ * - no TLSv1.3 (not available in mbedtls 2.x; experimental in mbedtls 3.x)
++ * - no OCSP (not yet available in mbedtls)
++ * - mbedtls does not support all certificate encodings used by hwsim tests
++ *   PCKS#5 v1.5
++ *   PCKS#12
++ *   DH DSA
++ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
++ * - mbedtls does not currently provide way to set an attribute in a CSR
++ *     https://github.com/Mbed-TLS/mbedtls/issues/4886
++ *   so tests/hwsim dpp_enterprise tests fail
++ * - DPP2 not supported
++ *   PKCS#7 parsing is not supported in mbedtls
++ *   See crypto_mbedtls.c:crypto_pkcs7_get_certificates() comments
++ * - DPP3 not supported
++ *   hpke_base_seal() and hpke_base_seal() not implemented in crypto_mbedtls.c
++ *
++ * Status:
++ * - code written to be compatible with mbedtls 2.x and mbedtls 3.x
++ *   (currently requires mbedtls >= 2.27.0 for mbedtls_mpi_random())
++ *   (currently requires mbedtls >= 2.18.0 for mbedtls_ssl_tls_prf())
++ * - builds with tests/build/build-wpa_supplicant-mbedtls.config
++ * - passes all tests/ crypto module tests (incomplete coverage)
++ *   ($ cd tests; make clean; make -j 4 run-tests CONFIG_TLS=mbedtls)
++ * - passes almost all tests/hwsim tests
++ *   (hwsim tests skipped for missing features)
++ *
++ * RFE:
++ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
++ * - client/server session resumption, and/or save client session ticket
++ */
++
++#include "includes.h"
++#include "common.h"
++
++#include <mbedtls/version.h>
++#include <mbedtls/ctr_drbg.h>
++#include <mbedtls/error.h>
++#include <mbedtls/oid.h>
++#include <mbedtls/pem.h>
++#include <mbedtls/platform.h> /* mbedtls_calloc() mbedtls_free() */
++#include <mbedtls/platform_util.h> /* mbedtls_platform_zeroize() */
++#include <mbedtls/ssl.h>
++#include <mbedtls/ssl_ticket.h>
++#include <mbedtls/x509.h>
++#include <mbedtls/x509_crt.h>
++
++#if MBEDTLS_VERSION_NUMBER >= 0x02040000 /* mbedtls 2.4.0 */
++#include <mbedtls/net_sockets.h>
++#else
++#include <mbedtls/net.h>
++#endif
++
++#ifndef MBEDTLS_PRIVATE
++#define MBEDTLS_PRIVATE(x) x
++#endif
++
++#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
++#define mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl) \
++        ((ssl)->MBEDTLS_PRIVATE(session) \
++        ?(ssl)->MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(ciphersuite) \
++        : 0)
++#define mbedtls_ssl_ciphersuite_get_name(info) \
++        (info)->MBEDTLS_PRIVATE(name)
++#endif
++
++#include "crypto.h"     /* sha256_vector() */
++#include "tls.h"
++
++#ifndef SHA256_DIGEST_LENGTH
++#define SHA256_DIGEST_LENGTH 32
++#endif
++
++#ifndef MBEDTLS_EXPKEY_FIXED_SECRET_LEN
++#define MBEDTLS_EXPKEY_FIXED_SECRET_LEN 48
++#endif
++
++#ifndef MBEDTLS_EXPKEY_RAND_LEN
++#define MBEDTLS_EXPKEY_RAND_LEN 32
++#endif
++
++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++static mbedtls_ssl_export_keys_t tls_connection_export_keys_cb;
++#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++static mbedtls_ssl_export_keys_ext_t tls_connection_export_keys_cb;
++#else /*(not implemented; return error)*/
++#define mbedtls_ssl_tls_prf(a,b,c,d,e,f,g,h) (-1)
++typedef mbedtls_tls_prf_types int;
++#endif
++
++
++/* hostapd/wpa_supplicant provides forced_memzero(),
++ * but prefer mbedtls_platform_zeroize() */
++#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz)
++
++
++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
++ || defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
++#ifdef MBEDTLS_SSL_SESSION_TICKETS
++#ifdef MBEDTLS_SSL_TICKET_C
++#define TLS_MBEDTLS_SESSION_TICKETS
++#if defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
++#define TLS_MBEDTLS_EAP_TEAP
++#endif
++#if !defined(CONFIG_FIPS) /* EAP-FAST keys cannot be exported in FIPS mode */
++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
++#define TLS_MBEDTLS_EAP_FAST
++#endif
++#endif
++#endif
++#endif
++#endif
++
++
++struct tls_conf {
++	mbedtls_ssl_config conf;
++
++	unsigned int verify_peer:1;
++	unsigned int verify_depth0_only:1;
++	unsigned int check_crl:2;           /*(needs :2 bits for 0, 1, 2)*/
++	unsigned int check_crl_strict:1;    /*(needs :1 bit  for 0, 1)*/
++	unsigned int ca_cert_probe:1;
++	unsigned int has_ca_cert:1;
++	unsigned int has_client_cert:1;
++	unsigned int has_private_key:1;
++	unsigned int suiteb128:1;
++	unsigned int suiteb192:1;
++	mbedtls_x509_crl *crl;
++	mbedtls_x509_crt ca_cert;
++	mbedtls_x509_crt client_cert;
++	mbedtls_pk_context private_key;
++
++	uint32_t refcnt;
++
++	unsigned int flags;
++	char *subject_match;
++	char *altsubject_match;
++	char *suffix_match;
++	char *domain_match;
++	char *check_cert_subject;
++	u8 ca_cert_hash[SHA256_DIGEST_LENGTH];
++
++	int *ciphersuites;  /* list of ciphersuite ids for mbedtls_ssl_config */
++#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
++	mbedtls_ecp_group_id *curves;
++#else
++	uint16_t *curves;   /* list of curve ids for mbedtls_ssl_config */
++#endif
++};
++
++
++struct tls_global {
++	struct tls_conf *tls_conf;
++	char *ocsp_stapling_response;
++	mbedtls_ctr_drbg_context *ctr_drbg; /*(see crypto_mbedtls.c)*/
++  #ifdef MBEDTLS_SSL_SESSION_TICKETS
++	mbedtls_ssl_ticket_context ticket_ctx;
++  #endif
++	char *ca_cert_file;
++	struct os_reltime crl_reload_previous;
++	unsigned int crl_reload_interval;
++	uint32_t refcnt;
++	struct tls_config init_conf;
++};
++
++static struct tls_global tls_ctx_global;
++
++
++struct tls_connection {
++	struct tls_conf *tls_conf;
++	struct wpabuf *push_buf;
++	struct wpabuf *pull_buf;
++	size_t pull_buf_offset;
++
++	unsigned int established:1;
++	unsigned int resumed:1;
++	unsigned int verify_peer:1;
++	unsigned int is_server:1;
++
++	mbedtls_ssl_context ssl;
++
++	mbedtls_tls_prf_types tls_prf_type;
++	size_t expkey_keyblock_size;
++	size_t expkey_secret_len;
++  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++	unsigned char expkey_secret[MBEDTLS_EXPKEY_FIXED_SECRET_LEN];
++  #else
++	unsigned char expkey_secret[MBEDTLS_MD_MAX_SIZE];
++  #endif
++	unsigned char expkey_randbytes[MBEDTLS_EXPKEY_RAND_LEN*2];
++
++	int read_alerts, write_alerts, failed;
++
++  #ifdef TLS_MBEDTLS_SESSION_TICKETS
++	tls_session_ticket_cb session_ticket_cb;
++	void *session_ticket_cb_ctx;
++	unsigned char *clienthello_session_ticket;
++	size_t clienthello_session_ticket_len;
++  #endif
++	char *peer_subject; /* peer subject info for authenticated peer */
++	struct wpabuf *success_data;
++};
++
++
++#ifndef __has_attribute
++#define __has_attribute(x) 0
++#endif
++
++#ifndef __GNUC_PREREQ
++#define __GNUC_PREREQ(maj,min) 0
++#endif
++
++#ifndef __attribute_cold__
++#if __has_attribute(cold) \
++ || __GNUC_PREREQ(4,3)
++#define __attribute_cold__  __attribute__((__cold__))
++#else
++#define __attribute_cold__
++#endif
++#endif
++
++#ifndef __attribute_noinline__
++#if __has_attribute(noinline) \
++ || __GNUC_PREREQ(3,1)
++#define __attribute_noinline__  __attribute__((__noinline__))
++#else
++#define __attribute_noinline__
++#endif
++#endif
++
++
++__attribute_cold__
++__attribute_noinline__
++static void emsg(int level, const char * const msg)
++{
++	wpa_printf(level, "MTLS: %s", msg);
++}
++
++
++__attribute_cold__
++__attribute_noinline__
++static void emsgrc(int level, const char * const msg, int rc)
++{
++  #ifdef MBEDTLS_ERROR_C
++	/* error logging convenience function that decodes mbedtls result codes */
++	char buf[256];
++	mbedtls_strerror(rc, buf, sizeof(buf));
++	wpa_printf(level, "MTLS: %s: %s (-0x%04x)", msg, buf, -rc);
++  #else
++	wpa_printf(level, "MTLS: %s: (-0x%04x)", msg, -rc);
++  #endif
++}
++
++
++#define elog(rc, msg) emsgrc(MSG_ERROR, (msg), (rc))
++#define ilog(rc, msg) emsgrc(MSG_INFO,  (msg), (rc))
++
++
++struct tls_conf * tls_conf_init(void *tls_ctx)
++{
++	struct tls_conf *tls_conf = os_zalloc(sizeof(*tls_conf));
++	if (tls_conf == NULL)
++		return NULL;
++	tls_conf->refcnt = 1;
++
++	mbedtls_ssl_config_init(&tls_conf->conf);
++	mbedtls_ssl_conf_rng(&tls_conf->conf,
++			     mbedtls_ctr_drbg_random, tls_ctx_global.ctr_drbg);
++	mbedtls_x509_crt_init(&tls_conf->ca_cert);
++	mbedtls_x509_crt_init(&tls_conf->client_cert);
++	mbedtls_pk_init(&tls_conf->private_key);
++
++	return tls_conf;
++}
++
++
++void tls_conf_deinit(struct tls_conf *tls_conf)
++{
++	if (tls_conf == NULL || --tls_conf->refcnt != 0)
++		return;
++
++	mbedtls_x509_crt_free(&tls_conf->ca_cert);
++	mbedtls_x509_crt_free(&tls_conf->client_cert);
++	if (tls_conf->crl) {
++		mbedtls_x509_crl_free(tls_conf->crl);
++		os_free(tls_conf->crl);
++	}
++	mbedtls_pk_free(&tls_conf->private_key);
++	mbedtls_ssl_config_free(&tls_conf->conf);
++	os_free(tls_conf->curves);
++	os_free(tls_conf->ciphersuites);
++	os_free(tls_conf->subject_match);
++	os_free(tls_conf->altsubject_match);
++	os_free(tls_conf->suffix_match);
++	os_free(tls_conf->domain_match);
++	os_free(tls_conf->check_cert_subject);
++	os_free(tls_conf);
++}
++
++
++mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/
++
++__attribute_cold__
++void * tls_init(const struct tls_config *conf)
++{
++	/* RFE: review struct tls_config *conf (different from tls_conf) */
++
++	if (++tls_ctx_global.refcnt > 1)
++		return &tls_ctx_global;
++
++	tls_ctx_global.ctr_drbg = crypto_mbedtls_ctr_drbg();
++  #ifdef MBEDTLS_SSL_SESSION_TICKETS
++	mbedtls_ssl_ticket_init(&tls_ctx_global.ticket_ctx);
++	mbedtls_ssl_ticket_setup(&tls_ctx_global.ticket_ctx,
++	                         mbedtls_ctr_drbg_random,
++	                         tls_ctx_global.ctr_drbg,
++	                         MBEDTLS_CIPHER_AES_256_GCM,
++	                         43200); /* ticket timeout: 12 hours */
++  #endif
++	/* copy struct for future use */
++	tls_ctx_global.init_conf = *conf;
++	if (conf->openssl_ciphers)
++		tls_ctx_global.init_conf.openssl_ciphers =
++		  os_strdup(conf->openssl_ciphers);
++
++	tls_ctx_global.crl_reload_interval = conf->crl_reload_interval;
++	os_get_reltime(&tls_ctx_global.crl_reload_previous);
++
++	return &tls_ctx_global;
++}
++
++
++__attribute_cold__
++void tls_deinit(void *tls_ctx)
++{
++	if (tls_ctx == NULL || --tls_ctx_global.refcnt != 0)
++		return;
++
++	tls_conf_deinit(tls_ctx_global.tls_conf);
++	os_free(tls_ctx_global.ca_cert_file);
++	os_free(tls_ctx_global.ocsp_stapling_response);
++	char *openssl_ciphers; /*(allocated in tls_init())*/
++	*(const char **)&openssl_ciphers =
++	  tls_ctx_global.init_conf.openssl_ciphers;
++	os_free(openssl_ciphers);
++  #ifdef MBEDTLS_SSL_SESSION_TICKETS
++	mbedtls_ssl_ticket_free(&tls_ctx_global.ticket_ctx);
++  #endif
++	os_memset(&tls_ctx_global, 0, sizeof(tls_ctx_global));
++}
++
++
++int tls_get_errors(void *tls_ctx)
++{
++	return 0;
++}
++
++
++static void tls_connection_deinit_expkey(struct tls_connection *conn)
++{
++	conn->tls_prf_type = 0; /* MBEDTLS_SSL_TLS_PRF_NONE; */
++	conn->expkey_keyblock_size = 0;
++	conn->expkey_secret_len = 0;
++	forced_memzero(conn->expkey_secret, sizeof(conn->expkey_secret));
++	forced_memzero(conn->expkey_randbytes, sizeof(conn->expkey_randbytes));
++}
++
++
++#ifdef TLS_MBEDTLS_SESSION_TICKETS
++void tls_connection_deinit_clienthello_session_ticket(struct tls_connection *conn)
++{
++	if (conn->clienthello_session_ticket) {
++		mbedtls_platform_zeroize(conn->clienthello_session_ticket,
++		                         conn->clienthello_session_ticket_len);
++		mbedtls_free(conn->clienthello_session_ticket);
++		conn->clienthello_session_ticket = NULL;
++		conn->clienthello_session_ticket_len = 0;
++	}
++}
++#endif
++
++
++void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
++{
++	if (conn == NULL)
++		return;
++
++  #if 0 /*(good intention, but never sent since we destroy self below)*/
++	if (conn->established)
++		mbedtls_ssl_close_notify(&conn->ssl);
++  #endif
++
++	if (conn->tls_prf_type)
++		tls_connection_deinit_expkey(conn);
++
++  #ifdef TLS_MBEDTLS_SESSION_TICKETS
++	if (conn->clienthello_session_ticket)
++		tls_connection_deinit_clienthello_session_ticket(conn);
++  #endif
++
++	os_free(conn->peer_subject);
++	wpabuf_free(conn->success_data);
++	wpabuf_free(conn->push_buf);
++	wpabuf_free(conn->pull_buf);
++	mbedtls_ssl_free(&conn->ssl);
++	tls_conf_deinit(conn->tls_conf);
++	os_free(conn);
++}
++
++
++static void tls_mbedtls_refresh_crl(void);
++static int tls_mbedtls_ssl_setup(struct tls_connection *conn);
++
++struct tls_connection * tls_connection_init(void *tls_ctx)
++{
++	struct tls_connection *conn = os_zalloc(sizeof(*conn));
++	if (conn == NULL)
++		return NULL;
++
++	mbedtls_ssl_init(&conn->ssl);
++
++	conn->tls_conf = tls_ctx_global.tls_conf; /*(inherit global conf, if set)*/
++	if (conn->tls_conf) {
++		++conn->tls_conf->refcnt;
++		/* check for CRL refresh if inheriting from global config */
++		tls_mbedtls_refresh_crl();
++
++		conn->verify_peer = conn->tls_conf->verify_peer;
++		if (tls_mbedtls_ssl_setup(conn) != 0) {
++			tls_connection_deinit(&tls_ctx_global, conn);
++			return NULL;
++		}
++	}
++
++	return conn;
++}
++
++
++int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
++{
++	return conn ? conn->established : 0;
++}
++
++
++__attribute_noinline__
++char * tls_mbedtls_peer_serial_num(const mbedtls_x509_crt *crt, char *serial_num, size_t len)
++{
++	/* mbedtls_x509_serial_gets() inefficiently formats to hex separated by
++	 * colons, so generate the hex serial number here.  The func
++	 * wpa_snprintf_hex_uppercase() is similarly inefficient. */
++	size_t i = 0; /* skip leading 0's per Distinguished Encoding Rules (DER) */
++	while (i < crt->serial.len && crt->serial.p[i] == 0) ++i;
++	if (i == crt->serial.len) --i;
++
++	const unsigned char *s = crt->serial.p + i;
++	const size_t e = (crt->serial.len - i) * 2;
++	if (e >= len)
++		return NULL;
++  #if 0
++	wpa_snprintf_hex_uppercase(serial_num, len, s, crt->serial.len-i);
++  #else
++	for (i = 0; i < e; i+=2, ++s) {
++		serial_num[i+0] = "0123456789ABCDEF"[(*s >>  4)];
++		serial_num[i+1] = "0123456789ABCDEF"[(*s & 0xF)];
++	}
++	serial_num[e] = '\0';
++  #endif
++	return serial_num;
++}
++
++
++char * tls_connection_peer_serial_num(void *tls_ctx,
++				      struct tls_connection *conn)
++{
++	const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&conn->ssl);
++	if (crt == NULL)
++		return NULL;
++	size_t len = crt->serial.len * 2 + 1;
++	char *serial_num = os_malloc(len);
++	if (!serial_num)
++		return NULL;
++	return tls_mbedtls_peer_serial_num(crt, serial_num, len);
++}
++
++
++static void tls_pull_buf_reset(struct tls_connection *conn);
++
++int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
++{
++	/* Note: this function called from eap_peer_tls_reauth_init()
++	 * for session resumption, not for connection shutdown */
++
++	if (conn == NULL)
++		return -1;
++
++	tls_pull_buf_reset(conn);
++	wpabuf_free(conn->push_buf);
++	conn->push_buf = NULL;
++	conn->established = 0;
++	conn->resumed = 0;
++	if (conn->tls_prf_type)
++		tls_connection_deinit_expkey(conn);
++
++	/* RFE: prepare for session resumption? (see doc in crypto/tls.h) */
++
++	return mbedtls_ssl_session_reset(&conn->ssl);
++}
++
++
++static int tls_wpabuf_resize_put_data(struct wpabuf **buf,
++                                      const unsigned char *data, size_t dlen)
++{
++	if (wpabuf_resize(buf, dlen) < 0)
++		return 0;
++	wpabuf_put_data(*buf, data, dlen);
++	return 1;
++}
++
++
++static int tls_pull_buf_append(struct tls_connection *conn,
++                               const struct wpabuf *in_data)
++{
++	/*(interface does not lend itself to move semantics)*/
++	return tls_wpabuf_resize_put_data(&conn->pull_buf,
++	                                  wpabuf_head(in_data),
++	                                  wpabuf_len(in_data));
++}
++
++
++static void tls_pull_buf_reset(struct tls_connection *conn)
++{
++	/*(future: might consider reusing conn->pull_buf)*/
++	wpabuf_free(conn->pull_buf);
++	conn->pull_buf = NULL;
++	conn->pull_buf_offset = 0;
++}
++
++
++__attribute_cold__
++static void tls_pull_buf_discard(struct tls_connection *conn, const char *func)
++{
++	size_t discard = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
++	if (discard)
++		wpa_printf(MSG_DEBUG,
++			   "%s - %zu bytes remaining in pull_buf; discarding",
++			   func, discard);
++	tls_pull_buf_reset(conn);
++}
++
++
++static int tls_pull_func(void *ptr, unsigned char *buf, size_t len)
++{
++	struct tls_connection *conn = (struct tls_connection *) ptr;
++	if (conn->pull_buf == NULL)
++		return MBEDTLS_ERR_SSL_WANT_READ;
++	const size_t dlen = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
++	if (dlen == 0)
++		return MBEDTLS_ERR_SSL_WANT_READ;
++
++	if (len > dlen)
++		len = dlen;
++	os_memcpy(buf, wpabuf_head(conn->pull_buf)+conn->pull_buf_offset, len);
++
++	if (len == dlen) {
++		tls_pull_buf_reset(conn);
++		/*wpa_printf(MSG_DEBUG, "%s - emptied pull_buf", __func__);*/
++	}
++	else {
++		conn->pull_buf_offset += len;
++		/*wpa_printf(MSG_DEBUG, "%s - %zu bytes remaining in pull_buf",
++			   __func__, dlen - len);*/
++	}
++	return (int)len;
++}
++
++
++static int tls_push_func(void *ptr, const unsigned char *buf, size_t len)
++{
++	struct tls_connection *conn = (struct tls_connection *) ptr;
++	return tls_wpabuf_resize_put_data(&conn->push_buf, buf, len)
++	  ? (int)len
++	  : MBEDTLS_ERR_SSL_ALLOC_FAILED;
++}
++
++
++static int
++tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags);
++
++
++static int tls_mbedtls_ssl_setup(struct tls_connection *conn)
++{
++  #if 0
++	/* mbedtls_ssl_setup() must be called only once */
++	/* If this func might be called multiple times (e.g. via set_params),
++	 * then we should set a flag in conn that ssl was initialized */
++	if (conn->ssl_is_init) {
++		mbedtls_ssl_free(&conn->ssl);
++		mbedtls_ssl_init(&conn->ssl);
++	}
++  #endif
++
++	int ret = mbedtls_ssl_setup(&conn->ssl, &conn->tls_conf->conf);
++	if (ret != 0) {
++		elog(ret, "mbedtls_ssl_setup");
++		return -1;
++	}
++
++	mbedtls_ssl_set_bio(&conn->ssl, conn, tls_push_func, tls_pull_func, NULL);
++  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++	mbedtls_ssl_set_export_keys_cb(
++	    &conn->ssl, tls_connection_export_keys_cb, conn);
++  #elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++	mbedtls_ssl_conf_export_keys_ext_cb(
++	    &conn->tls_conf->conf, tls_connection_export_keys_cb, conn);
++  #endif
++	if (conn->verify_peer)
++		mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
++
++	return 0;
++}
++
++
++static int tls_mbedtls_data_is_pem(const u8 *data)
++{
++    return (NULL != os_strstr((char *)data, "-----"));
++}
++
++
++static void tls_mbedtls_set_allowed_tls_vers(struct tls_conf *tls_conf,
++                                             mbedtls_ssl_config *conf)
++{
++  #if !defined(MBEDTLS_SSL_PROTO_TLS1_3)
++	tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_3;
++  #endif
++
++	/* unconditionally require TLSv1.2+ for TLS_CONN_SUITEB */
++	if (tls_conf->flags & TLS_CONN_SUITEB) {
++		tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_0;
++		tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_1;
++	}
++
++	const unsigned int flags = tls_conf->flags;
++
++	/* attempt to map flags to min and max TLS protocol version */
++
++	int min = (flags & TLS_CONN_DISABLE_TLSv1_0)
++		? (flags & TLS_CONN_DISABLE_TLSv1_1)
++		? (flags & TLS_CONN_DISABLE_TLSv1_2)
++		? (flags & TLS_CONN_DISABLE_TLSv1_3)
++		? 4
++		: 3
++		: 2
++		: 1
++		: 0;
++
++	int max = (flags & TLS_CONN_DISABLE_TLSv1_3)
++		? (flags & TLS_CONN_DISABLE_TLSv1_2)
++		? (flags & TLS_CONN_DISABLE_TLSv1_1)
++		? (flags & TLS_CONN_DISABLE_TLSv1_0)
++		? -1
++		: 0
++		: 1
++		: 2
++		: 3;
++
++	if ((flags & TLS_CONN_ENABLE_TLSv1_2) && min > 2) min = 2;
++	if ((flags & TLS_CONN_ENABLE_TLSv1_1) && min > 1) min = 1;
++	if ((flags & TLS_CONN_ENABLE_TLSv1_0) && min > 0) min = 0;
++	if (max < min) {
++		emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
++		return;
++	}
++  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++	/* mbed TLS 3.0.0 removes support for protocols < TLSv1.2 */
++	if (min < 2 || max < 2) {
++		emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
++		if (min < 2) min = 2;
++		if (max < 2) max = 2;
++	}
++  #endif
++
++  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
++	/* MBEDTLS_SSL_VERSION_TLS1_2 = 0x0303 *//*!< (D)TLS 1.2 */
++	/* MBEDTLS_SSL_VERSION_TLS1_3 = 0x0304 *//*!< (D)TLS 1.3 */
++	min = (min == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
++	max = (max == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
++	mbedtls_ssl_conf_min_tls_version(conf, min);
++	mbedtls_ssl_conf_max_tls_version(conf, max);
++  #else
++   #ifndef MBEDTLS_SSL_MINOR_VERSION_4
++	if (min == 3) min = 2;
++	if (max == 3) max = 2;
++   #endif
++	/* MBEDTLS_SSL_MINOR_VERSION_0  0 *//*!< SSL v3.0 */
++	/* MBEDTLS_SSL_MINOR_VERSION_1  1 *//*!< TLS v1.0 */
++	/* MBEDTLS_SSL_MINOR_VERSION_2  2 *//*!< TLS v1.1 */
++	/* MBEDTLS_SSL_MINOR_VERSION_3  3 *//*!< TLS v1.2 */
++	/* MBEDTLS_SSL_MINOR_VERSION_4  4 *//*!< TLS v1.3 */
++	mbedtls_ssl_conf_min_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, min+1);
++	mbedtls_ssl_conf_max_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, max+1);
++  #endif
++}
++
++
++__attribute_noinline__
++static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n);
++
++
++static int
++tls_mbedtls_set_dhparams(struct tls_conf *tls_conf, const char *dh_file)
++{
++    size_t len;
++    u8 *data;
++    if (tls_mbedtls_readfile(dh_file, &data, &len))
++        return 0;
++
++    /* parse only if DH parameters if in PEM format */
++    if (tls_mbedtls_data_is_pem(data)
++        && NULL == os_strstr((char *)data, "-----BEGIN DH PARAMETERS-----")) {
++        if (os_strstr((char *)data, "-----BEGIN DSA PARAMETERS-----"))
++            wpa_printf(MSG_WARNING, "DSA parameters not handled (%s)", dh_file);
++        else
++            wpa_printf(MSG_WARNING, "unexpected DH param content (%s)",dh_file);
++        forced_memzero(data, len);
++        os_free(data);
++        return 0;
++    }
++
++    /* mbedtls_dhm_parse_dhm() expects "-----BEGIN DH PARAMETERS-----" if PEM */
++    mbedtls_dhm_context dhm;
++    mbedtls_dhm_init(&dhm);
++    int rc = mbedtls_dhm_parse_dhm(&dhm, data, len);
++    if (0 == rc)
++        rc = mbedtls_ssl_conf_dh_param_ctx(&tls_conf->conf, &dhm);
++    if (0 != rc)
++        elog(rc, dh_file);
++    mbedtls_dhm_free(&dhm);
++
++    forced_memzero(data, len);
++    os_free(data);
++    return (0 == rc);
++}
++
++
++/* reference: lighttpd src/mod_mbedtls.c:mod_mbedtls_ssl_append_curve()
++ * (same author: gstrauss@gluelogic.com; same license: BSD-3-Clause) */
++#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
++static int
++tls_mbedtls_append_curve (mbedtls_ecp_group_id *ids, int nids, int idsz, const mbedtls_ecp_group_id id)
++{
++    if (1 >= idsz - (nids + 1)) {
++        emsg(MSG_ERROR, "error: too many curves during list expand");
++        return -1;
++    }
++    ids[++nids] = id;
++    return nids;
++}
++
++
++static int
++tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
++{
++    mbedtls_ecp_group_id ids[512];
++    int nids = -1;
++    const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
++    const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
++
++    for (const char *e = curvelist-1; e; ) {
++        const char * const n = e+1;
++        e = os_strchr(n, ':');
++        size_t len = e ? (size_t)(e - n) : os_strlen(n);
++        mbedtls_ecp_group_id grp_id = MBEDTLS_ECP_DP_NONE;
++        switch (len) {
++          case 5:
++            if (0 == os_memcmp("P-521", n, 5))
++                grp_id = MBEDTLS_ECP_DP_SECP521R1;
++            else if (0 == os_memcmp("P-384", n, 5))
++                grp_id = MBEDTLS_ECP_DP_SECP384R1;
++            else if (0 == os_memcmp("P-256", n, 5))
++                grp_id = MBEDTLS_ECP_DP_SECP256R1;
++            break;
++          case 6:
++            if (0 == os_memcmp("BP-521", n, 6))
++                grp_id = MBEDTLS_ECP_DP_BP512R1;
++            else if (0 == os_memcmp("BP-384", n, 6))
++                grp_id = MBEDTLS_ECP_DP_BP384R1;
++            else if (0 == os_memcmp("BP-256", n, 6))
++                grp_id = MBEDTLS_ECP_DP_BP256R1;
++            break;
++          default:
++            break;
++        }
++        if (grp_id != MBEDTLS_ECP_DP_NONE) {
++            nids = tls_mbedtls_append_curve(ids, nids, idsz, grp_id);
++            if (-1 == nids) return 0;
++            continue;
++        }
++        /* similar to mbedtls_ecp_curve_info_from_name() */
++        const mbedtls_ecp_curve_info *info;
++        for (info = curve_info; info->grp_id != MBEDTLS_ECP_DP_NONE; ++info) {
++            if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
++                break;
++        }
++        if (info->grp_id == MBEDTLS_ECP_DP_NONE) {
++            wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
++            return 0;
++        }
++
++        nids = tls_mbedtls_append_curve(ids, nids, idsz, info->grp_id);
++        if (-1 == nids) return 0;
++    }
++
++    /* mod_openssl configures "prime256v1" if curve list not specified,
++     * but mbedtls provides a list of supported curves if not explicitly set */
++    if (-1 == nids) return 1; /* empty list; no-op */
++
++    ids[++nids] = MBEDTLS_ECP_DP_NONE; /* terminate list */
++    ++nids;
++
++    /* curves list must be persistent for lifetime of mbedtls_ssl_config */
++    tls_conf->curves = os_malloc(nids * sizeof(mbedtls_ecp_group_id));
++    if (tls_conf->curves == NULL)
++        return 0;
++    os_memcpy(tls_conf->curves, ids, nids * sizeof(mbedtls_ecp_group_id));
++
++    mbedtls_ssl_conf_curves(&tls_conf->conf, tls_conf->curves);
++    return 1;
++}
++#else
++static int
++tls_mbedtls_append_curve (uint16_t *ids, int nids, int idsz, const uint16_t id)
++{
++    if (1 >= idsz - (nids + 1)) {
++        emsg(MSG_ERROR, "error: too many curves during list expand");
++        return -1;
++    }
++    ids[++nids] = id;
++    return nids;
++}
++
++
++static int
++tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
++{
++    /* TLS Supported Groups (renamed from "EC Named Curve Registry")
++     * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
++     */
++    uint16_t ids[512];
++    int nids = -1;
++    const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
++    const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
++
++    for (const char *e = curvelist-1; e; ) {
++        const char * const n = e+1;
++        e = os_strchr(n, ':');
++        size_t len = e ? (size_t)(e - n) : os_strlen(n);
++        uint16_t tls_id = 0;
++        switch (len) {
++          case 5:
++            if (0 == os_memcmp("P-521", n, 5))
++                tls_id = 25; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP521R1 */
++            else if (0 == os_memcmp("P-384", n, 5))
++                tls_id = 24; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP384R1 */
++            else if (0 == os_memcmp("P-256", n, 5))
++                tls_id = 23; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP256R1 */
++            break;
++          case 6:
++            if (0 == os_memcmp("BP-521", n, 6))
++                tls_id = 28; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP512R1 */
++            else if (0 == os_memcmp("BP-384", n, 6))
++                tls_id = 27; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP384R1 */
++            else if (0 == os_memcmp("BP-256", n, 6))
++                tls_id = 26; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP256R1 */
++            break;
++          default:
++            break;
++        }
++        if (tls_id != 0) {
++            nids = tls_mbedtls_append_curve(ids, nids, idsz, tls_id);
++            if (-1 == nids) return 0;
++            continue;
++        }
++        /* similar to mbedtls_ecp_curve_info_from_name() */
++        const mbedtls_ecp_curve_info *info;
++        for (info = curve_info; info->tls_id != 0; ++info) {
++            if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
++                break;
++        }
++        if (info->tls_id == 0) {
++            wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
++            return 0;
++        }
++
++        nids = tls_mbedtls_append_curve(ids, nids, idsz, info->tls_id);
++        if (-1 == nids) return 0;
++    }
++
++    /* mod_openssl configures "prime256v1" if curve list not specified,
++     * but mbedtls provides a list of supported curves if not explicitly set */
++    if (-1 == nids) return 1; /* empty list; no-op */
++
++    ids[++nids] = 0; /* terminate list */
++    ++nids;
++
++    /* curves list must be persistent for lifetime of mbedtls_ssl_config */
++    tls_conf->curves = os_malloc(nids * sizeof(uint16_t));
++    if (tls_conf->curves == NULL)
++        return 0;
++    os_memcpy(tls_conf->curves, ids, nids * sizeof(uint16_t));
++
++    mbedtls_ssl_conf_groups(&tls_conf->conf, tls_conf->curves);
++    return 1;
++}
++#endif /* MBEDTLS_VERSION_NUMBER >= 0x03010000 */ /* mbedtls 3.1.0 */
++
++
++/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
++static const int suite_AES_256_ephemeral[] = {
++    /* All AES-256 ephemeral suites */
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8
++};
++
++/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
++static const int suite_AES_128_ephemeral[] = {
++    /* All AES-128 ephemeral suites */
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8
++};
++
++/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
++/* HIGH cipher list (mapped from openssl list to mbedtls) */
++static const int suite_HIGH[] = {
++    MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
++    MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384,
++    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256,
++    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384,
++    MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
++    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
++    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256,
++    MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256,
++    MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
++    MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM,
++    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
++    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
++    MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM_8,
++    MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM,
++    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
++    MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM_8,
++    MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256,
++    MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_RSA_WITH_AES_256_CCM,
++    MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256,
++    MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_RSA_WITH_AES_256_CCM_8,
++    MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256,
++    MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
++    MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384,
++    MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_RSA_WITH_AES_128_CCM,
++    MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_RSA_WITH_AES_128_CCM_8,
++    MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256,
++    MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
++    MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256,
++    MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256,
++    MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
++    MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384,
++    MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384,
++    MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256,
++    MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256,
++    MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256,
++    MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_PSK_WITH_AES_256_CCM,
++    MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384,
++    MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384,
++    MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8,
++    MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384,
++    MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_PSK_WITH_AES_128_CCM,
++    MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256,
++    MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8,
++    MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256
++};
++
++
++__attribute_noinline__
++static int
++tls_mbedtls_append_ciphersuite (int *ids, int nids, int idsz, const int *x, int xsz)
++{
++    if (xsz >= idsz - (nids + 1)) {
++        emsg(MSG_ERROR, "error: too many ciphers during list expand");
++        return -1;
++    }
++
++    for (int i = 0; i < xsz; ++i)
++        ids[++nids] = x[i];
++
++    return nids;
++}
++
++
++static int
++tls_mbedtls_translate_ciphername(int id, char *buf, size_t buflen)
++{
++    const mbedtls_ssl_ciphersuite_t *info =
++      mbedtls_ssl_ciphersuite_from_id(id);
++    if (info == NULL)
++        return 0;
++    const char *name = mbedtls_ssl_ciphersuite_get_name(info);
++    const size_t len = os_strlen(name);
++    if (len == 7 && 0 == os_memcmp(name, "unknown", 7))
++        return 0;
++    if (len >= buflen)
++        return 0;
++    os_strlcpy(buf, name, buflen);
++
++    /* attempt to translate mbedtls string to openssl string
++     * (some heuristics; incomplete) */
++    size_t i = 0, j = 0;
++    if (buf[0] == 'T') {
++        if (os_strncmp(buf, "TLS1-3-", 7) == 0) {
++            buf[3] = '-';
++            j = 4; /* remove "1-3" from "TLS1-3-" prefix */
++            i = 7;
++        }
++        else if (os_strncmp(buf, "TLS-", 4) == 0)
++            i = 4; /* remove "TLS-" prefix */
++    }
++    for (; buf[i]; ++i) {
++        if (buf[i] == '-') {
++            if (i >= 3) {
++                if (0 == os_memcmp(buf+i-3, "AES", 3))
++                    continue; /* "AES-" -> "AES" */
++            }
++            if (i >= 4) {
++                if (0 == os_memcmp(buf+i-4, "WITH", 4)) {
++                    j -= 4;   /* remove "WITH-" */
++                    continue;
++                }
++            }
++        }
++        buf[j++] = buf[i];
++    }
++    buf[j] = '\0';
++
++    return j;
++}
++
++
++__attribute_noinline__
++static int
++tls_mbedtls_set_ciphersuites(struct tls_conf *tls_conf, int *ids, int nids)
++{
++    /* ciphersuites list must be persistent for lifetime of mbedtls_ssl_config*/
++    os_free(tls_conf->ciphersuites);
++    tls_conf->ciphersuites = os_malloc(nids * sizeof(int));
++    if (tls_conf->ciphersuites == NULL)
++        return 0;
++    os_memcpy(tls_conf->ciphersuites, ids, nids * sizeof(int));
++    mbedtls_ssl_conf_ciphersuites(&tls_conf->conf, tls_conf->ciphersuites);
++    return 1;
++}
++
++
++static int
++tls_mbedtls_set_ciphers(struct tls_conf *tls_conf, const char *ciphers)
++{
++    char buf[64];
++    int ids[512];
++    int nids = -1;
++    const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
++    const char *next;
++    size_t blen, clen;
++    do {
++        next = os_strchr(ciphers, ':');
++        clen = next ? (size_t)(next - ciphers) : os_strlen(ciphers);
++        if (!clen)
++            continue;
++
++        /* special-case a select set of openssl group names for hwsim tests */
++	/* (review; remove excess code if tests are not run for non-OpenSSL?) */
++        if (clen == 9 && os_memcmp(ciphers, "SUITEB192", 9) == 0) {
++            static int ssl_preset_suiteb192_ciphersuites[] = {
++                MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
++                0
++            };
++            return tls_mbedtls_set_ciphersuites(tls_conf,
++                                                ssl_preset_suiteb192_ciphersuites,
++                                                2);
++        }
++        if (clen == 9 && os_memcmp(ciphers, "SUITEB128", 9) == 0) {
++            static int ssl_preset_suiteb128_ciphersuites[] = {
++                MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
++                0
++            };
++            return tls_mbedtls_set_ciphersuites(tls_conf,
++                                                ssl_preset_suiteb128_ciphersuites,
++                                                2);
++        }
++        if (clen == 7 && os_memcmp(ciphers, "DEFAULT", 7) == 0)
++            continue;
++        if (clen == 6 && os_memcmp(ciphers, "AES128", 6) == 0) {
++            nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
++                     suite_AES_128_ephemeral,
++                     (int)ARRAY_SIZE(suite_AES_128_ephemeral));
++            if (nids == -1)
++                return 0;
++            continue;
++        }
++        if (clen == 6 && os_memcmp(ciphers, "AES256", 6) == 0) {
++            nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
++                     suite_AES_256_ephemeral,
++                     (int)ARRAY_SIZE(suite_AES_256_ephemeral));
++            if (nids == -1)
++                return 0;
++            continue;
++        }
++        if (clen == 4 && os_memcmp(ciphers, "HIGH", 4) == 0) {
++            nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz, suite_HIGH,
++                                                  (int)ARRAY_SIZE(suite_HIGH));
++            if (nids == -1)
++                return 0;
++            continue;
++        }
++        /* ignore anonymous cipher group names (?not supported by mbedtls?) */
++        if (clen == 4 && os_memcmp(ciphers, "!ADH", 4) == 0)
++            continue;
++        if (clen == 6 && os_memcmp(ciphers, "-aECDH", 6) == 0)
++            continue;
++        if (clen == 7 && os_memcmp(ciphers, "-aECDSA", 7) == 0)
++            continue;
++
++        /* attempt to match mbedtls cipher names
++         * nb: does not support openssl group names or list manipulation syntax
++         *   (alt: could copy almost 1200 lines (!!!) of lighttpd mod_mbedtls.c
++         *    mod_mbedtls_ssl_conf_ciphersuites() to translate strings)
++         * note: not efficient to rewrite list for each ciphers entry,
++         *       but this code is expected to run only at startup
++         */
++        const int *list = mbedtls_ssl_list_ciphersuites();
++        for (; *list; ++list) {
++            blen = tls_mbedtls_translate_ciphername(*list,buf,sizeof(buf));
++            if (!blen)
++                continue;
++
++            /* matching heuristics additional to translate_ciphername above */
++            if (blen == clen+4) {
++                char *cbc = os_strstr(buf, "CBC-");
++                if (cbc) {
++                    os_memmove(cbc, cbc+4, blen-(cbc+4-buf)+1); /*(w/ '\0')*/
++                    blen -= 4;
++                }
++            }
++            if (blen >= clen && os_memcmp(ciphers, buf, clen) == 0
++                && (blen == clen
++                    || (blen == clen+7 && os_memcmp(buf+clen, "-SHA256", 7)))) {
++                if (1 >= idsz - (nids + 1)) {
++                    emsg(MSG_ERROR,
++                         "error: too many ciphers during list expand");
++                    return 0;
++                }
++                ids[++nids] = *list;
++                break;
++            }
++        }
++        if (*list == 0) {
++            wpa_printf(MSG_ERROR,
++                       "MTLS: unrecognized cipher: %.*s", (int)clen, ciphers);
++            return 0;
++        }
++    } while ((ciphers = next ? next+1 : NULL));
++
++    if (-1 == nids) return 1; /* empty list; no-op */
++
++    ids[++nids] = 0; /* terminate list */
++    ++nids;
++
++    return tls_mbedtls_set_ciphersuites(tls_conf, ids, nids);
++}
++
++
++__attribute_noinline__
++static int tls_mbedtls_set_item(char **config_item, const char *item)
++{
++	os_free(*config_item);
++	*config_item = NULL;
++	return item ? (*config_item = os_strdup(item)) != NULL : 1;
++}
++
++
++static int tls_connection_set_subject_match(struct tls_conf *tls_conf,
++                                            const struct tls_connection_params *params)
++{
++	int rc = 1;
++	rc &= tls_mbedtls_set_item(&tls_conf->subject_match,
++	                              params->subject_match);
++	rc &= tls_mbedtls_set_item(&tls_conf->altsubject_match,
++	                              params->altsubject_match);
++	rc &= tls_mbedtls_set_item(&tls_conf->suffix_match,
++	                              params->suffix_match);
++	rc &= tls_mbedtls_set_item(&tls_conf->domain_match,
++	                              params->domain_match);
++	rc &= tls_mbedtls_set_item(&tls_conf->check_cert_subject,
++	                              params->check_cert_subject);
++	return rc;
++}
++
++
++/* duplicated in crypto_mbedtls.c:crypto_mbedtls_readfile()*/
++__attribute_noinline__
++static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n)
++{
++  #if 0 /* #ifdef MBEDTLS_FS_IO */
++	/*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/
++	if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) {
++		wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path);
++		return -1;
++	}
++  #else
++	/*(use os_readfile() so that we can use os_free()
++	 *(if we use mbedtls_pk_load_file() above, macros prevent calling free()
++	 * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free()
++	 * on buf aborts in tests if buf not allocated via os_malloc())*/
++	*buf = (u8 *)os_readfile(path, n);
++	if (!*buf) {
++		wpa_printf(MSG_ERROR, "error: os_readfile %s", path);
++		return -1;
++	}
++	u8 *buf0 = os_realloc(*buf, *n+1);
++	if (!buf0) {
++		bin_clear_free(*buf, *n);
++		*buf = NULL;
++		return -1;
++	}
++	buf0[(*n)++] = '\0';
++	*buf = buf0;
++  #endif
++	return 0;
++}
++
++
++static int tls_mbedtls_set_crl(struct tls_conf *tls_conf, const u8 *data, size_t len)
++{
++	/* do not use mbedtls_x509_crl_parse() on PEM unless it contains CRL */
++	if (len && data[len-1] == '\0'
++	    && NULL == os_strstr((const char *)data,"-----BEGIN X509 CRL-----")
++	    && tls_mbedtls_data_is_pem(data))
++		return 0;
++
++	mbedtls_x509_crl crl;
++	mbedtls_x509_crl_init(&crl);
++	int rc = mbedtls_x509_crl_parse(&crl, data, len);
++	if (rc < 0) {
++		mbedtls_x509_crl_free(&crl);
++		return rc == MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT ? 0 : rc;
++	}
++
++	mbedtls_x509_crl *crl_new = os_malloc(sizeof(crl));
++	if (crl_new == NULL) {
++		mbedtls_x509_crl_free(&crl);
++		return MBEDTLS_ERR_X509_ALLOC_FAILED;
++	}
++	os_memcpy(crl_new, &crl, sizeof(crl));
++
++	mbedtls_x509_crl *crl_old = tls_conf->crl;
++	tls_conf->crl = crl_new;
++	if (crl_old) {
++		mbedtls_x509_crl_free(crl_old);
++		os_free(crl_old);
++	}
++	return 0;
++}
++
++
++static int tls_mbedtls_set_ca(struct tls_conf *tls_conf, u8 *data, size_t len)
++{
++	/* load crt struct onto stack and then copy into tls_conf in
++	 * order to preserve existing tls_conf value if error occurs
++	 *
++	 * hostapd is not threaded, or else should allocate memory and swap in
++	 * pointer reduce race condition.  (If threaded, would also need to
++	 * keep reference count of use to avoid freeing while still in use.) */
++
++	mbedtls_x509_crt crt;
++	mbedtls_x509_crt_init(&crt);
++	int rc = mbedtls_x509_crt_parse(&crt, data, len);
++	if (rc < 0) {
++		mbedtls_x509_crt_free(&crt);
++		return rc;
++	}
++
++	mbedtls_x509_crt_free(&tls_conf->ca_cert);
++	os_memcpy(&tls_conf->ca_cert, &crt, sizeof(crt));
++	return 0;
++}
++
++
++static int tls_mbedtls_set_ca_and_crl(struct tls_conf *tls_conf, const char *ca_cert_file)
++{
++	size_t len;
++	u8 *data;
++	if (tls_mbedtls_readfile(ca_cert_file, &data, &len))
++		return -1;
++
++	int rc;
++	if (0 == (rc = tls_mbedtls_set_ca(tls_conf, data, len))
++	    && (!tls_mbedtls_data_is_pem(data) /*skip parse for CRL if not PEM*/
++	        || 0 == (rc = tls_mbedtls_set_crl(tls_conf, data, len)))) {
++		mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
++		                          &tls_conf->ca_cert,
++		                          tls_conf->crl);
++	}
++	else {
++		elog(rc, __func__);
++		emsg(MSG_ERROR, ca_cert_file);
++	}
++
++	forced_memzero(data, len);
++	os_free(data);
++	return rc;
++}
++
++
++static void tls_mbedtls_refresh_crl(void)
++{
++	/* check for CRL refresh
++	 * continue even if error occurs; continue with previous cert, CRL */
++	unsigned int crl_reload_interval = tls_ctx_global.crl_reload_interval;
++	const char *ca_cert_file = tls_ctx_global.ca_cert_file;
++	if (!crl_reload_interval || !ca_cert_file)
++		return;
++
++	struct os_reltime *previous = &tls_ctx_global.crl_reload_previous;
++	struct os_reltime now;
++	if (os_get_reltime(&now) != 0
++	    || !os_reltime_expired(&now, previous, crl_reload_interval))
++		return;
++
++	/* Note: modifying global state is not thread-safe
++	 *       if in use by existing connections
++	 *
++	 * src/utils/os.h does not provide a portable stat()
++	 * or else it would be a good idea to check mtime and size,
++	 * and avoid reloading if file has not changed */
++
++	if (tls_mbedtls_set_ca_and_crl(tls_ctx_global.tls_conf, ca_cert_file) == 0)
++		*previous = now;
++}
++
++
++static int tls_mbedtls_set_ca_cert(struct tls_conf *tls_conf,
++				   const struct tls_connection_params *params)
++{
++	if (params->ca_cert) {
++		if (os_strncmp(params->ca_cert, "probe://", 8) == 0) {
++			tls_conf->ca_cert_probe = 1;
++			tls_conf->has_ca_cert = 1;
++			return 0;
++		}
++
++		if (os_strncmp(params->ca_cert, "hash://", 7) == 0) {
++			const char *pos = params->ca_cert + 7;
++			if (os_strncmp(pos, "server/sha256/", 14) != 0) {
++				emsg(MSG_ERROR, "unsupported ca_cert hash value");
++				return -1;
++			}
++			pos += 14;
++			if (os_strlen(pos) != SHA256_DIGEST_LENGTH*2) {
++				emsg(MSG_ERROR, "unexpected ca_cert hash length");
++				return -1;
++			}
++			if (hexstr2bin(pos, tls_conf->ca_cert_hash,
++			               SHA256_DIGEST_LENGTH) < 0) {
++				emsg(MSG_ERROR, "invalid ca_cert hash value");
++				return -1;
++			}
++			emsg(MSG_DEBUG, "checking only server certificate match");
++			tls_conf->verify_depth0_only = 1;
++			tls_conf->has_ca_cert = 1;
++			return 0;
++		}
++
++		if (tls_mbedtls_set_ca_and_crl(tls_conf, params->ca_cert) != 0)
++			return -1;
++	}
++	if (params->ca_cert_blob) {
++		size_t len = params->ca_cert_blob_len;
++		int is_pem = tls_mbedtls_data_is_pem(params->ca_cert_blob);
++		if (len && params->ca_cert_blob[len-1] != '\0' && is_pem)
++			++len; /*(include '\0' in len for PEM)*/
++		int ret = mbedtls_x509_crt_parse(&tls_conf->ca_cert,
++		                                 params->ca_cert_blob, len);
++		if (ret != 0) {
++			elog(ret, "mbedtls_x509_crt_parse");
++			return -1;
++		}
++		if (is_pem) { /*(ca_cert_blob in DER format contains ca cert only)*/
++			ret = tls_mbedtls_set_crl(tls_conf, params->ca_cert_blob, len);
++			if (ret != 0) {
++				elog(ret, "mbedtls_x509_crl_parse");
++				return -1;
++			}
++		}
++	}
++
++	if (mbedtls_x509_time_is_future(&tls_conf->ca_cert.valid_from)
++	    || mbedtls_x509_time_is_past(&tls_conf->ca_cert.valid_to)) {
++		emsg(MSG_WARNING, "ca_cert expired or not yet valid");
++		if (params->ca_cert)
++			emsg(MSG_WARNING, params->ca_cert);
++	}
++
++	tls_conf->has_ca_cert = 1;
++	return 0;
++}
++
++
++static int tls_mbedtls_set_certs(struct tls_conf *tls_conf,
++				 const struct tls_connection_params *params)
++{
++	int ret;
++
++	if (params->ca_cert || params->ca_cert_blob) {
++		if (tls_mbedtls_set_ca_cert(tls_conf, params) != 0)
++			return -1;
++	}
++	else if (params->ca_path) {
++		emsg(MSG_INFO, "ca_path support not implemented");
++		return -1;
++	}
++
++	if (!tls_conf->has_ca_cert)
++		mbedtls_ssl_conf_authmode(&tls_conf->conf, MBEDTLS_SSL_VERIFY_NONE);
++	else {
++		/* Initial setting: REQUIRED for client, OPTIONAL for server
++		 *   (see also tls_connection_set_verify()) */
++		tls_conf->verify_peer = (tls_ctx_global.tls_conf == NULL);
++		int authmode = tls_conf->verify_peer
++		  ? MBEDTLS_SSL_VERIFY_REQUIRED
++		  : MBEDTLS_SSL_VERIFY_OPTIONAL;
++		mbedtls_ssl_conf_authmode(&tls_conf->conf, authmode);
++		mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
++		                          &tls_conf->ca_cert,
++		                          tls_conf->crl);
++
++		if (!tls_connection_set_subject_match(tls_conf, params))
++			return -1;
++	}
++
++	if (params->client_cert2) /*(yes, server_cert2 in msg below)*/
++		emsg(MSG_INFO, "server_cert2 support not implemented");
++
++	if (params->client_cert) {
++		size_t len;
++		u8 *data;
++		if (tls_mbedtls_readfile(params->client_cert, &data, &len))
++			return -1;
++		ret = mbedtls_x509_crt_parse(&tls_conf->client_cert, data, len);
++		forced_memzero(data, len);
++		os_free(data);
++	}
++	if (params->client_cert_blob) {
++		size_t len = params->client_cert_blob_len;
++		if (len && params->client_cert_blob[len-1] != '\0'
++		    && tls_mbedtls_data_is_pem(params->client_cert_blob))
++			++len; /*(include '\0' in len for PEM)*/
++		ret = mbedtls_x509_crt_parse(&tls_conf->client_cert,
++		                             params->client_cert_blob, len);
++	}
++	if (params->client_cert || params->client_cert_blob) {
++		if (ret < 0) {
++			elog(ret, "mbedtls_x509_crt_parse");
++			if (params->client_cert)
++				emsg(MSG_ERROR, params->client_cert);
++			return -1;
++		}
++		if (mbedtls_x509_time_is_future(&tls_conf->client_cert.valid_from)
++		    || mbedtls_x509_time_is_past(&tls_conf->client_cert.valid_to)) {
++			emsg(MSG_WARNING, "cert expired or not yet valid");
++			if (params->client_cert)
++				emsg(MSG_WARNING, params->client_cert);
++		}
++		tls_conf->has_client_cert = 1;
++	}
++
++	if (params->private_key || params->private_key_blob) {
++		size_t len = params->private_key_blob_len;
++		u8 *data;
++		*(const u8 **)&data = params->private_key_blob;
++		if (len && data[len-1] != '\0' && tls_mbedtls_data_is_pem(data))
++			++len; /*(include '\0' in len for PEM)*/
++		if (params->private_key
++		    && tls_mbedtls_readfile(params->private_key, &data, &len)) {
++			return -1;
++		}
++		const char *pwd = params->private_key_passwd;
++	  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++		ret = mbedtls_pk_parse_key(&tls_conf->private_key,
++			data, len,
++			(const unsigned char *)pwd,
++			pwd ? os_strlen(pwd) : 0,
++			mbedtls_ctr_drbg_random,
++			tls_ctx_global.ctr_drbg);
++	  #else
++		ret = mbedtls_pk_parse_key(&tls_conf->private_key,
++			data, len,
++			(const unsigned char *)pwd,
++			pwd ? os_strlen(pwd) : 0);
++	  #endif
++		if (params->private_key) {
++			forced_memzero(data, len);
++			os_free(data);
++		}
++		if (ret < 0) {
++			elog(ret, "mbedtls_pk_parse_key");
++			return -1;
++		}
++		tls_conf->has_private_key = 1;
++	}
++
++	if (tls_conf->has_client_cert && tls_conf->has_private_key) {
++		ret = mbedtls_ssl_conf_own_cert(
++		    &tls_conf->conf, &tls_conf->client_cert, &tls_conf->private_key);
++		if (ret < 0) {
++			elog(ret, "mbedtls_ssl_conf_own_cert");
++			return -1;
++		}
++	}
++
++	return 0;
++}
++
++
++/* mbedtls_x509_crt_profile_suiteb plus rsa_min_bitlen 2048 */
++/* (reference: see also mbedtls_x509_crt_profile_next) */
++/* ??? should permit SHA-512, too, and additional curves ??? */
++static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb128 =
++{
++    /* Only SHA-256 and 384 */
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) |
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
++    /* Only ECDSA */
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
++#if defined(MBEDTLS_ECP_C)
++    /* Only NIST P-256 and P-384 */
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP256R1 ) |
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
++#else
++    0,
++#endif
++    2048,
++};
++
++
++/* stricter than mbedtls_x509_crt_profile_suiteb */
++/* (reference: see also mbedtls_x509_crt_profile_next) */
++/* ??? should permit SHA-512, too, and additional curves ??? */
++static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192 =
++{
++    /* Only SHA-384 */
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
++    /* Only ECDSA */
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
++#if defined(MBEDTLS_ECP_C)
++    /* Only NIST P-384 */
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
++#else
++    0,
++#endif
++    3072,
++};
++
++
++/* stricter than mbedtls_x509_crt_profile_suiteb except allow any PK alg */
++/* (reference: see also mbedtls_x509_crt_profile_next) */
++/* ??? should permit SHA-512, too, and additional curves ??? */
++static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192_anypk =
++{
++    /* Only SHA-384 */
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
++    0xFFFFFFF, /* Any PK alg    */
++#if defined(MBEDTLS_ECP_C)
++    /* Only NIST P-384 */
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
++#else
++    0,
++#endif
++    3072,
++};
++
++
++static int tls_mbedtls_set_params(struct tls_conf *tls_conf,
++				  const struct tls_connection_params *params)
++{
++	tls_conf->flags = params->flags;
++
++	if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
++		emsg(MSG_INFO, "ocsp=3 not supported");
++		return -1;
++	}
++
++	if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP) {
++		emsg(MSG_INFO, "ocsp not supported");
++		return -1;
++	}
++
++	int suiteb128 = 0;
++	int suiteb192 = 0;
++	if (params->openssl_ciphers) {
++		if (os_strcmp(params->openssl_ciphers, "SUITEB192") == 0) {
++			suiteb192 = 1;
++			tls_conf->flags |= TLS_CONN_SUITEB;
++		}
++		if (os_strcmp(params->openssl_ciphers, "SUITEB128") == 0) {
++			suiteb128 = 1;
++			tls_conf->flags |= TLS_CONN_SUITEB;
++		}
++	}
++
++	int ret = mbedtls_ssl_config_defaults(
++	    &tls_conf->conf, tls_ctx_global.tls_conf ? MBEDTLS_SSL_IS_SERVER
++	                                             : MBEDTLS_SSL_IS_CLIENT,
++	    MBEDTLS_SSL_TRANSPORT_STREAM,
++	    (tls_conf->flags & TLS_CONN_SUITEB) ? MBEDTLS_SSL_PRESET_SUITEB
++	                                        : MBEDTLS_SSL_PRESET_DEFAULT);
++	if (ret != 0) {
++		elog(ret, "mbedtls_ssl_config_defaults");
++		return -1;
++	}
++
++	if (suiteb128) {
++		mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
++		                              &tls_mbedtls_crt_profile_suiteb128);
++		mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 2048);
++	}
++	else if (suiteb192) {
++		mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
++		                              &tls_mbedtls_crt_profile_suiteb192);
++		mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
++	}
++	else if (tls_conf->flags & TLS_CONN_SUITEB) {
++		/* treat as suiteb192 while allowing any PK algorithm */
++		mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
++		                              &tls_mbedtls_crt_profile_suiteb192_anypk);
++		mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
++	}
++
++	tls_mbedtls_set_allowed_tls_vers(tls_conf, &tls_conf->conf);
++	ret = tls_mbedtls_set_certs(tls_conf, params);
++	if (ret != 0)
++		return -1;
++
++	if (params->dh_file
++	    && !tls_mbedtls_set_dhparams(tls_conf, params->dh_file)) {
++		return -1;
++	}
++
++	if (params->openssl_ecdh_curves
++	    && !tls_mbedtls_set_curves(tls_conf, params->openssl_ecdh_curves)) {
++		return -1;
++	}
++
++	if (params->openssl_ciphers) {
++		if (!tls_mbedtls_set_ciphers(tls_conf, params->openssl_ciphers))
++			return -1;
++	}
++	else if (tls_conf->flags & TLS_CONN_SUITEB) {
++		/* special-case a select set of ciphers for hwsim tests */
++		if (!tls_mbedtls_set_ciphers(tls_conf,
++		        (tls_conf->flags & TLS_CONN_SUITEB_NO_ECDH)
++		          ? "DHE-RSA-AES256-GCM-SHA384"
++		          : "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384"))
++			return -1;
++	}
++
++	return 0;
++}
++
++
++int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
++			      const struct tls_connection_params *params)
++{
++	if (conn == NULL || params == NULL)
++		return -1;
++
++	tls_conf_deinit(conn->tls_conf);
++	struct tls_conf *tls_conf = conn->tls_conf = tls_conf_init(tls_ctx);
++	if (tls_conf == NULL)
++		return -1;
++
++	if (tls_ctx_global.tls_conf) {
++		tls_conf->check_crl = tls_ctx_global.tls_conf->check_crl;
++		tls_conf->check_crl_strict = tls_ctx_global.tls_conf->check_crl_strict;
++		/*(tls_openssl.c inherits check_cert_subject from global conf)*/
++		if (tls_ctx_global.tls_conf->check_cert_subject) {
++			tls_conf->check_cert_subject =
++			  os_strdup(tls_ctx_global.tls_conf->check_cert_subject);
++			if (tls_conf->check_cert_subject == NULL)
++				return -1;
++		}
++	}
++
++	if (tls_mbedtls_set_params(tls_conf, params) != 0)
++		return -1;
++	conn->verify_peer = tls_conf->verify_peer;
++
++	return tls_mbedtls_ssl_setup(conn);
++}
++
++
++#ifdef TLS_MBEDTLS_SESSION_TICKETS
++
++static int tls_mbedtls_clienthello_session_ticket_prep (struct tls_connection *conn,
++                                                        const u8 *data, size_t len)
++{
++	if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
++		return -1;
++	if (conn->clienthello_session_ticket)
++		tls_connection_deinit_clienthello_session_ticket(conn);
++	if (len) {
++		conn->clienthello_session_ticket = mbedtls_calloc(1, len);
++		if (conn->clienthello_session_ticket == NULL)
++			return -1;
++		conn->clienthello_session_ticket_len = len;
++		os_memcpy(conn->clienthello_session_ticket, data, len);
++	}
++	return 0;
++}
++
++
++static void tls_mbedtls_clienthello_session_ticket_set (struct tls_connection *conn)
++{
++	mbedtls_ssl_session *sess = conn->ssl.MBEDTLS_PRIVATE(session_negotiate);
++	if (sess->MBEDTLS_PRIVATE(ticket)) {
++		mbedtls_platform_zeroize(sess->MBEDTLS_PRIVATE(ticket),
++		                         sess->MBEDTLS_PRIVATE(ticket_len));
++		mbedtls_free(sess->MBEDTLS_PRIVATE(ticket));
++	}
++	sess->MBEDTLS_PRIVATE(ticket) = conn->clienthello_session_ticket;
++	sess->MBEDTLS_PRIVATE(ticket_len) = conn->clienthello_session_ticket_len;
++	sess->MBEDTLS_PRIVATE(ticket_lifetime) = 86400;/* XXX: can hint be 0? */
++
++	conn->clienthello_session_ticket = NULL;
++	conn->clienthello_session_ticket_len = 0;
++}
++
++
++static int tls_mbedtls_ssl_ticket_write(void *p_ticket,
++                                        const mbedtls_ssl_session *session,
++                                        unsigned char *start,
++                                        const unsigned char *end,
++                                        size_t *tlen,
++                                        uint32_t *lifetime)
++{
++	struct tls_connection *conn = p_ticket;
++	if (conn && conn->session_ticket_cb) {
++		/* see tls_mbedtls_clienthello_session_ticket_prep() */
++		/* see tls_mbedtls_clienthello_session_ticket_set() */
++		return 0;
++	}
++
++	return mbedtls_ssl_ticket_write(&tls_ctx_global.ticket_ctx,
++	                                session, start, end, tlen, lifetime);
++}
++
++
++static int tls_mbedtls_ssl_ticket_parse(void *p_ticket,
++                                        mbedtls_ssl_session *session,
++                                        unsigned char *buf,
++                                        size_t len)
++{
++	/* XXX: TODO: not implemented in client;
++	 * mbedtls_ssl_conf_session_tickets_cb() callbacks only for TLS server*/
++
++	if (len == 0)
++		return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
++
++	struct tls_connection *conn = p_ticket;
++	if (conn && conn->session_ticket_cb) {
++		/* XXX: have random and secret been initialized yet?
++		 *      or must keys first be exported?
++		 *      EAP-FAST uses all args, EAP-TEAP only uses secret */
++		struct tls_random data;
++		if (tls_connection_get_random(NULL, conn, &data) != 0)
++			return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
++		int ret =
++		  conn->session_ticket_cb(conn->session_ticket_cb_ctx,
++		                          buf, len,
++		                          data.client_random,
++		                          data.server_random,
++		                          conn->expkey_secret);
++		if (ret == 1) {
++			conn->resumed = 1;
++			return 0;
++		}
++		emsg(MSG_ERROR, "EAP session ticket ext not implemented");
++		return MBEDTLS_ERR_SSL_INVALID_MAC;
++		/*(non-zero return used for mbedtls debug logging)*/
++	}
++
++	/* XXX: TODO always use tls_mbedtls_ssl_ticket_parse() for callback? */
++	int rc = mbedtls_ssl_ticket_parse(&tls_ctx_global.ticket_ctx,
++	                                  session, buf, len);
++	if (conn)
++		conn->resumed = (rc == 0);
++	return rc;
++}
++
++#endif /* TLS_MBEDTLS_SESSION_TICKETS */
++
++
++__attribute_cold__
++int tls_global_set_params(void *tls_ctx,
++			  const struct tls_connection_params *params)
++{
++	/* XXX: why might global_set_params be called more than once? */
++	if (tls_ctx_global.tls_conf)
++		tls_conf_deinit(tls_ctx_global.tls_conf);
++	tls_ctx_global.tls_conf = tls_conf_init(tls_ctx);
++	if (tls_ctx_global.tls_conf == NULL)
++		return -1;
++
++  #ifdef MBEDTLS_SSL_SESSION_TICKETS
++  #ifdef MBEDTLS_SSL_TICKET_C
++	if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET))
++	  #ifdef TLS_MBEDTLS_SESSION_TICKETS
++		mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
++		                                    tls_mbedtls_ssl_ticket_write,
++		                                    tls_mbedtls_ssl_ticket_parse,
++		                                    NULL);
++	  #else
++		mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
++		                                    mbedtls_ssl_ticket_write,
++		                                    mbedtls_ssl_ticket_parse,
++		                                    &tls_ctx_global.ticket_ctx);
++	  #endif
++  #endif
++  #endif
++
++	os_free(tls_ctx_global.ocsp_stapling_response);
++	tls_ctx_global.ocsp_stapling_response = NULL;
++	if (params->ocsp_stapling_response)
++		tls_ctx_global.ocsp_stapling_response =
++			os_strdup(params->ocsp_stapling_response);
++
++	os_free(tls_ctx_global.ca_cert_file);
++	tls_ctx_global.ca_cert_file = NULL;
++	if (params->ca_cert)
++		tls_ctx_global.ca_cert_file = os_strdup(params->ca_cert);
++	return tls_mbedtls_set_params(tls_ctx_global.tls_conf, params);
++}
++
++
++int tls_global_set_verify(void *tls_ctx, int check_crl, int strict)
++{
++	tls_ctx_global.tls_conf->check_crl = check_crl;
++	tls_ctx_global.tls_conf->check_crl_strict = strict; /*(time checks)*/
++	return 0;
++}
++
++
++int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
++			      int verify_peer, unsigned int flags,
++			      const u8 *session_ctx, size_t session_ctx_len)
++{
++	/*(EAP server-side calls this from eap_server_tls_ssl_init())*/
++	if (conn == NULL)
++		return -1;
++
++	conn->tls_conf->flags |= flags;/* TODO: reprocess flags, if necessary */
++
++	int authmode;
++	switch (verify_peer) {
++	case 2:  authmode = MBEDTLS_SSL_VERIFY_OPTIONAL; break;/*(eap_teap_init())*/
++	case 1:  authmode = MBEDTLS_SSL_VERIFY_REQUIRED; break;
++	default: authmode = MBEDTLS_SSL_VERIFY_NONE;     break;
++	}
++	mbedtls_ssl_set_hs_authmode(&conn->ssl, authmode);
++
++	if ((conn->verify_peer = (authmode != MBEDTLS_SSL_VERIFY_NONE)))
++		mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
++	else
++		mbedtls_ssl_set_verify(&conn->ssl, NULL, NULL);
++
++	return 0;
++}
++
++
++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++static void tls_connection_export_keys_cb(
++    void *p_expkey, mbedtls_ssl_key_export_type secret_type,
++    const unsigned char *secret, size_t secret_len,
++    const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
++    const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
++    mbedtls_tls_prf_types tls_prf_type)
++{
++	struct tls_connection *conn = p_expkey;
++	conn->tls_prf_type = tls_prf_type;
++	if (!tls_prf_type)
++		return;
++	if (secret_len > sizeof(conn->expkey_secret)) {
++		emsg(MSG_ERROR, "tls_connection_export_keys_cb secret too long");
++		conn->tls_prf_type = MBEDTLS_SSL_TLS_PRF_NONE; /* 0 */
++		return;
++	}
++	conn->expkey_secret_len = secret_len;
++	os_memcpy(conn->expkey_secret, secret, secret_len);
++	os_memcpy(conn->expkey_randbytes,
++	          client_random, MBEDTLS_EXPKEY_RAND_LEN);
++	os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
++	          server_random, MBEDTLS_EXPKEY_RAND_LEN);
++}
++#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++static int tls_connection_export_keys_cb(
++    void *p_expkey,
++    const unsigned char *ms,
++    const unsigned char *kb,
++    size_t maclen,
++    size_t keylen,
++    size_t ivlen,
++    const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
++    const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
++    mbedtls_tls_prf_types tls_prf_type )
++{
++	struct tls_connection *conn = p_expkey;
++	conn->tls_prf_type = tls_prf_type;
++	if (!tls_prf_type)
++		return -1; /*(return value ignored by mbedtls)*/
++	conn->expkey_keyblock_size = maclen + keylen + ivlen;
++	conn->expkey_secret_len = MBEDTLS_EXPKEY_FIXED_SECRET_LEN;
++	os_memcpy(conn->expkey_secret, ms, MBEDTLS_EXPKEY_FIXED_SECRET_LEN);
++	os_memcpy(conn->expkey_randbytes,
++	          client_random, MBEDTLS_EXPKEY_RAND_LEN);
++	os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
++	          server_random, MBEDTLS_EXPKEY_RAND_LEN);
++	return 0;
++}
++#endif
++
++
++int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
++			      struct tls_random *data)
++{
++	if (!conn || !conn->tls_prf_type)
++		return -1;
++	data->client_random = conn->expkey_randbytes;
++	data->client_random_len = MBEDTLS_EXPKEY_RAND_LEN;
++	data->server_random = conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN;
++	data->server_random_len = MBEDTLS_EXPKEY_RAND_LEN;
++	return 0;
++}
++
++
++int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
++			      const char *label, const u8 *context,
++			      size_t context_len, u8 *out, size_t out_len)
++{
++	/* (EAP-PEAP EAP-TLS EAP-TTLS) */
++  #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++	return (conn && conn->established && conn->tls_prf_type)
++	  ? mbedtls_ssl_tls_prf(conn->tls_prf_type,
++				conn->expkey_secret, conn->expkey_secret_len, label,
++				conn->expkey_randbytes,
++				sizeof(conn->expkey_randbytes), out, out_len)
++	  : -1;
++  #else
++	/* not implemented here for mbedtls < 2.18.0 */
++	return -1;
++  #endif
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_FAST
++
++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++/* keyblock size info is not exposed in mbed TLS 3.0.0 */
++/* extracted from mbedtls library/ssl_tls.c:ssl_tls12_populate_transform() */
++#include <mbedtls/ssl_ciphersuites.h>
++#include <mbedtls/cipher.h>
++static size_t tls_mbedtls_ssl_keyblock_size (mbedtls_ssl_context *ssl)
++{
++  #if !defined(MBEDTLS_USE_PSA_CRYPTO) /* XXX: (not extracted for PSA crypto) */
++  #if defined(MBEDTLS_SSL_PROTO_TLS1_3)
++    if (mbedtls_ssl_get_version_number(ssl) == MBEDTLS_SSL_VERSION_TLS1_3)
++        return 0; /* (calculation not extracted) */
++  #endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
++
++    int ciphersuite = mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl);
++    const mbedtls_ssl_ciphersuite_t *ciphersuite_info =
++      mbedtls_ssl_ciphersuite_from_id(ciphersuite);
++    if (ciphersuite_info == NULL)
++        return 0;
++
++    const mbedtls_cipher_info_t *cipher_info =
++      mbedtls_cipher_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(cipher));
++    if (cipher_info == NULL)
++        return 0;
++
++  #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */
++    size_t keylen = mbedtls_cipher_info_get_key_bitlen(cipher_info) / 8;
++    mbedtls_cipher_mode_t mode = mbedtls_cipher_info_get_mode(cipher_info);
++  #else
++    size_t keylen = cipher_info->MBEDTLS_PRIVATE(key_bitlen) / 8;
++    mbedtls_cipher_mode_t mode = cipher_info->MBEDTLS_PRIVATE(mode);
++  #endif
++  #if defined(MBEDTLS_GCM_C) || \
++      defined(MBEDTLS_CCM_C) || \
++      defined(MBEDTLS_CHACHAPOLY_C)
++    if (mode == MBEDTLS_MODE_GCM || mode == MBEDTLS_MODE_CCM)
++        return keylen + 4;
++    else if (mode == MBEDTLS_MODE_CHACHAPOLY)
++        return keylen + 12;
++    else
++  #endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C || MBEDTLS_CHACHAPOLY_C */
++  #if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
++    {
++        const mbedtls_md_info_t *md_info =
++          mbedtls_md_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(mac));
++        if (md_info == NULL)
++            return 0;
++        size_t mac_key_len = mbedtls_md_get_size(md_info);
++        size_t ivlen = mbedtls_cipher_info_get_iv_size(cipher_info);
++        return keylen + mac_key_len + ivlen;
++    }
++  #endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */
++  #endif /* !MBEDTLS_USE_PSA_CRYPTO *//* (not extracted for PSA crypto) */
++    return 0;
++}
++#endif /* MBEDTLS_VERSION_NUMBER >= 0x03000000 *//* mbedtls 3.0.0 */
++
++
++int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
++				    u8 *out, size_t out_len)
++{
++	/* XXX: has export keys callback been run? */
++	if (!conn || !conn->tls_prf_type)
++		return -1;
++
++  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++	conn->expkey_keyblock_size = tls_mbedtls_ssl_keyblock_size(&conn->ssl);
++	if (conn->expkey_keyblock_size == 0)
++		return -1;
++  #endif
++	size_t skip = conn->expkey_keyblock_size * 2;
++	unsigned char *tmp_out = os_malloc(skip + out_len);
++	if (!tmp_out)
++		return -1;
++
++	/* server_random and then client_random */
++	unsigned char seed[MBEDTLS_EXPKEY_RAND_LEN*2];
++	os_memcpy(seed, conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
++	          MBEDTLS_EXPKEY_RAND_LEN);
++	os_memcpy(seed + MBEDTLS_EXPKEY_RAND_LEN, conn->expkey_randbytes,
++	          MBEDTLS_EXPKEY_RAND_LEN);
++
++  #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++	int ret = mbedtls_ssl_tls_prf(conn->tls_prf_type,
++				      conn->expkey_secret, conn->expkey_secret_len,
++				      "key expansion", seed, sizeof(seed),
++				      tmp_out, skip + out_len);
++	if (ret == 0)
++		os_memcpy(out, tmp_out + skip, out_len);
++  #else
++	int ret = -1; /*(not reached if not impl; return -1 at top of func)*/
++  #endif
++
++	bin_clear_free(tmp_out, skip + out_len);
++	forced_memzero(seed, sizeof(seed));
++	return ret;
++}
++
++#endif /* TLS_MBEDTLS_EAP_FAST */
++
++
++__attribute_cold__
++static void tls_mbedtls_suiteb_handshake_alert (struct tls_connection *conn)
++{
++	/* tests/hwsim/test_suite_b.py test_suite_b_192_rsa_insufficient_dh */
++	if (!(conn->tls_conf->flags & TLS_CONN_SUITEB))
++		return;
++	if (tls_ctx_global.tls_conf) /*(is server; want issue event on client)*/
++		return;
++  #if 0
++	/*(info not available on client;
++         * mbed TLS library enforces dhm min bitlen in ServerKeyExchange)*/
++	if (MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ==
++	  #if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
++	          mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl)
++	  #else
++	          mbedtls_ssl_get_ciphersuite_id(
++	            mbedtls_ssl_get_ciphersuite(&conn->ssl))
++	  #endif
++	    && mbedtls_mpi_size(&conn->tls_conf->conf.MBEDTLS_PRIVATE(dhm_P))
++	         < 384 /*(3072/8)*/)
++  #endif
++	{
++		struct tls_config *init_conf = &tls_ctx_global.init_conf;
++		if (init_conf->event_cb) {
++			union tls_event_data ev;
++			os_memset(&ev, 0, sizeof(ev));
++			ev.alert.is_local = 1;
++			ev.alert.type = "fatal";
++			/*"internal error" string for tests/hwsim/test_suiteb.py */
++			ev.alert.description = "internal error: handshake failure";
++			/*ev.alert.description = "insufficient security";*/
++			init_conf->event_cb(init_conf->cb_ctx, TLS_ALERT, &ev);
++		}
++	}
++}
++
++
++struct wpabuf * tls_connection_handshake(void *tls_ctx,
++					 struct tls_connection *conn,
++					 const struct wpabuf *in_data,
++					 struct wpabuf **appl_data)
++{
++	if (appl_data)
++		*appl_data = NULL;
++
++	if (in_data && wpabuf_len(in_data)) {
++		/*(unsure why tls_gnutls.c discards buffer contents; skip here)*/
++		if (conn->pull_buf && 0) /* disable; appears unwise */
++			tls_pull_buf_discard(conn, __func__);
++		if (!tls_pull_buf_append(conn, in_data))
++			return NULL;
++	}
++
++	if (conn->tls_conf == NULL) {
++		struct tls_connection_params params;
++		os_memset(&params, 0, sizeof(params));
++		params.openssl_ciphers =
++		  tls_ctx_global.init_conf.openssl_ciphers;
++		params.flags = tls_ctx_global.tls_conf->flags;
++		if (tls_connection_set_params(tls_ctx, conn, &params) != 0)
++			return NULL;
++	}
++
++	if (conn->verify_peer) /*(call here might be redundant; nbd)*/
++		mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
++
++  #ifdef TLS_MBEDTLS_SESSION_TICKETS
++	if (conn->clienthello_session_ticket)
++		/*(starting handshake for EAP-FAST and EAP-TEAP)*/
++		tls_mbedtls_clienthello_session_ticket_set(conn);
++
++	/* (not thread-safe due to need to set userdata 'conn' for callback) */
++	/* (unable to use mbedtls_ssl_set_user_data_p() with mbedtls 3.2.0+
++	 *  since ticket write and parse callbacks take (mbedtls_ssl_session *)
++	 *  param instead of (mbedtls_ssl_context *) param) */
++	if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
++		mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
++		                                    NULL, NULL, NULL);
++	else
++		mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
++		                                    tls_mbedtls_ssl_ticket_write,
++		                                    tls_mbedtls_ssl_ticket_parse,
++		                                    conn);
++  #endif
++
++  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
++	int ret = mbedtls_ssl_handshake(&conn->ssl);
++  #else
++	int ret = 0;
++	while (conn->ssl.MBEDTLS_PRIVATE(state) != MBEDTLS_SSL_HANDSHAKE_OVER) {
++		ret = mbedtls_ssl_handshake_step(&conn->ssl);
++		if (ret != 0)
++			break;
++	}
++  #endif
++
++  #ifdef TLS_MBEDTLS_SESSION_TICKETS
++	mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
++	                                    tls_mbedtls_ssl_ticket_write,
++	                                    tls_mbedtls_ssl_ticket_parse,
++	                                    NULL);
++  #endif
++
++	switch (ret) {
++	case 0:
++		conn->established = 1;
++		if (conn->push_buf == NULL)
++			/* Need to return something to get final TLS ACK. */
++			conn->push_buf = wpabuf_alloc(0);
++
++		if (appl_data /*&& conn->pull_buf && wpabuf_len(conn->pull_buf)*/)
++			*appl_data = NULL; /* RFE: check for application data */
++		break;
++	case MBEDTLS_ERR_SSL_WANT_WRITE:
++	case MBEDTLS_ERR_SSL_WANT_READ:
++	case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS:
++	case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS:
++		if (tls_ctx_global.tls_conf /*(is server)*/
++		    && conn->established && conn->push_buf == NULL)
++			/* Need to return something to trigger completion of EAP-TLS. */
++			conn->push_buf = wpabuf_alloc(0);
++		break;
++	default:
++		++conn->failed;
++		switch (ret) {
++		case MBEDTLS_ERR_SSL_CLIENT_RECONNECT:
++		case MBEDTLS_ERR_NET_CONN_RESET:
++		case MBEDTLS_ERR_NET_SEND_FAILED:
++			++conn->write_alerts;
++			break;
++	      #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++		case MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE:
++	      #else
++		case MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE:
++	      #endif
++			tls_mbedtls_suiteb_handshake_alert(conn);
++			/* fall through */
++		case MBEDTLS_ERR_NET_RECV_FAILED:
++		case MBEDTLS_ERR_SSL_CONN_EOF:
++		case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
++		case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE:
++			++conn->read_alerts;
++			break;
++		default:
++			break;
++		}
++
++		ilog(ret, "mbedtls_ssl_handshake");
++		break;
++	}
++
++	struct wpabuf *out_data = conn->push_buf;
++	conn->push_buf = NULL;
++	return out_data;
++}
++
++
++struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
++						struct tls_connection *conn,
++						const struct wpabuf *in_data,
++						struct wpabuf **appl_data)
++{
++	conn->is_server = 1;
++	return tls_connection_handshake(tls_ctx, conn, in_data, appl_data);
++}
++
++
++struct wpabuf * tls_connection_encrypt(void *tls_ctx,
++				       struct tls_connection *conn,
++				       const struct wpabuf *in_data)
++{
++	int res = mbedtls_ssl_write(&conn->ssl,
++	                            wpabuf_head_u8(in_data), wpabuf_len(in_data));
++	if (res < 0) {
++		elog(res, "mbedtls_ssl_write");
++		return NULL;
++	}
++
++	struct wpabuf *buf = conn->push_buf;
++	conn->push_buf = NULL;
++	return buf;
++}
++
++
++struct wpabuf * tls_connection_decrypt(void *tls_ctx,
++				       struct tls_connection *conn,
++				       const struct wpabuf *in_data)
++{
++	int res;
++	struct wpabuf *out;
++
++	/*assert(in_data != NULL);*/
++	if (!tls_pull_buf_append(conn, in_data))
++		return NULL;
++
++  #if defined(MBEDTLS_ZLIB_SUPPORT) /* removed in mbedtls 3.x */
++	/* Add extra buffer space to handle the possibility of decrypted
++	 * data being longer than input data due to TLS compression. */
++	out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
++  #else /* TLS compression is disabled in mbedtls 3.x */
++	out = wpabuf_alloc(wpabuf_len(in_data));
++  #endif
++	if (out == NULL)
++		return NULL;
++
++	res = mbedtls_ssl_read(&conn->ssl, wpabuf_mhead(out), wpabuf_size(out));
++	if (res < 0) {
++	  #if 1 /*(seems like a different error if wpabuf_len(in_data) == 0)*/
++		if (res == MBEDTLS_ERR_SSL_WANT_READ)
++			return out;
++	  #endif
++		elog(res, "mbedtls_ssl_read");
++		wpabuf_free(out);
++		return NULL;
++	}
++	wpabuf_put(out, res);
++
++	return out;
++}
++
++
++int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
++{
++	/* XXX: might need to detect if session resumed from TLS session ticket
++	 * even if not special session ticket handling for EAP-FAST, EAP-TEAP */
++	/* (?ssl->handshake->resume during session ticket validation?) */
++	return conn && conn->resumed;
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_FAST
++int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
++				   u8 *ciphers)
++{
++	/* ciphers is list of TLS_CIPHER_* from hostap/src/crypto/tls.h */
++	int ids[7];
++	const int idsz = (int)sizeof(ids);
++	int nids = -1, id;
++	for ( ; *ciphers != TLS_CIPHER_NONE; ++ciphers) {
++		switch (*ciphers) {
++		case TLS_CIPHER_RC4_SHA:
++		  #ifdef MBEDTLS_TLS_RSA_WITH_RC4_128_SHA
++			id = MBEDTLS_TLS_RSA_WITH_RC4_128_SHA;
++			break;
++		  #else
++			continue; /*(not supported in mbedtls 3.x; ignore)*/
++		  #endif
++		case TLS_CIPHER_AES128_SHA:
++			id = MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA;
++			break;
++		case TLS_CIPHER_RSA_DHE_AES128_SHA:
++			id = MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
++			break;
++		case TLS_CIPHER_ANON_DH_AES128_SHA:
++			continue; /*(not supported in mbedtls; ignore)*/
++		case TLS_CIPHER_RSA_DHE_AES256_SHA:
++			id = MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
++			break;
++		case TLS_CIPHER_AES256_SHA:
++			id = MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA;
++			break;
++		default:
++			return -1; /* should not happen */
++		}
++		if (++nids == idsz)
++			return -1; /* should not happen */
++		ids[nids] = id;
++	}
++	if (nids < 0)
++		return 0; /* nothing to do */
++	if (++nids == idsz)
++		return -1; /* should not happen */
++	ids[nids] = 0; /* terminate list */
++	++nids;
++
++	return tls_mbedtls_set_ciphersuites(conn->tls_conf, ids, nids) ? 0 : -1;
++}
++#endif
++
++
++int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
++		    char *buf, size_t buflen)
++{
++	if (conn == NULL)
++		return -1;
++	os_strlcpy(buf, mbedtls_ssl_get_version(&conn->ssl), buflen);
++	return buf[0] != 'u' ? 0 : -1; /*(-1 if "unknown")*/
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_TEAP
++u16 tls_connection_get_cipher_suite(struct tls_connection *conn)
++{
++	if (conn == NULL)
++		return 0;
++	return (u16)mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
++}
++#endif
++
++
++int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
++		   char *buf, size_t buflen)
++{
++	if (conn == NULL)
++		return -1;
++	const int id = mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
++	return tls_mbedtls_translate_ciphername(id, buf, buflen) ? 0 : -1;
++}
++
++
++#ifdef TLS_MBEDTLS_SESSION_TICKETS
++
++int tls_connection_enable_workaround(void *tls_ctx,
++				     struct tls_connection *conn)
++{
++	/* (see comment in src/eap_peer/eap_fast.c:eap_fast_init()) */
++	/* XXX: is there a relevant setting for this in mbed TLS? */
++	/* (do we even care that much about older CBC ciphers?) */
++	return 0;
++}
++
++
++int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
++				    int ext_type, const u8 *data,
++				    size_t data_len)
++{
++	/* (EAP-FAST and EAP-TEAP) */
++	if (ext_type == MBEDTLS_TLS_EXT_SESSION_TICKET) /*(ext_type == 35)*/
++		return tls_mbedtls_clienthello_session_ticket_prep(conn, data,
++		                                                   data_len);
++
++	return -1;
++}
++
++#endif /* TLS_MBEDTLS_SESSION_TICKETS */
++
++
++int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
++{
++	return conn ? conn->failed : -1;
++}
++
++
++int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
++{
++	return conn ? conn->read_alerts : -1;
++}
++
++
++int tls_connection_get_write_alerts(void *tls_ctx,
++				    struct tls_connection *conn)
++{
++	return conn ? conn->write_alerts : -1;
++}
++
++
++#ifdef TLS_MBEDTLS_SESSION_TICKETS
++int tls_connection_set_session_ticket_cb(
++	void *tls_ctx, struct tls_connection *conn,
++	tls_session_ticket_cb cb, void *ctx)
++{
++	if (!(conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)) {
++		/* (EAP-FAST and EAP-TEAP) */
++		conn->session_ticket_cb = cb;
++		conn->session_ticket_cb_ctx = ctx;
++		return 0;
++	}
++	return -1;
++}
++#endif
++
++
++int tls_get_library_version(char *buf, size_t buf_len)
++{
++  #ifndef MBEDTLS_VERSION_C
++	const char * const ver = "n/a";
++  #else
++	char ver[9];
++	mbedtls_version_get_string(ver);
++  #endif
++	return os_snprintf(buf, buf_len,
++	                   "mbed TLS build=" MBEDTLS_VERSION_STRING " run=%s", ver);
++}
++
++
++void tls_connection_set_success_data(struct tls_connection *conn,
++				     struct wpabuf *data)
++{
++	wpabuf_free(conn->success_data);
++	conn->success_data = data;
++}
++
++
++void tls_connection_set_success_data_resumed(struct tls_connection *conn)
++{
++}
++
++
++const struct wpabuf *
++tls_connection_get_success_data(struct tls_connection *conn)
++{
++	return conn->success_data;
++}
++
++
++void tls_connection_remove_session(struct tls_connection *conn)
++{
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_TEAP
++int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len)
++{
++  #if defined(MBEDTLS_SSL_RENEGOTIATION) /* XXX: renegotiation or resumption? */
++	/* data from TLS handshake Finished message */
++	size_t verify_len = conn->ssl.MBEDTLS_PRIVATE(verify_data_len);
++	char *verify_data = (conn->is_server ^ conn->resumed)
++	  ? conn->ssl.MBEDTLS_PRIVATE(peer_verify_data)
++	  : conn->ssl.MBEDTLS_PRIVATE(own_verify_data);
++	if (verify_len && verify_len <= max_len) {
++		os_memcpy(buf, verify_data, verify_len);
++		return (int)verify_len;
++	}
++  #endif
++	return -1;
++}
++#endif
++
++
++__attribute_noinline__
++static void tls_mbedtls_set_peer_subject(struct tls_connection *conn, const mbedtls_x509_crt *crt)
++{
++	if (conn->peer_subject)
++		return;
++	char buf[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
++	int buflen = mbedtls_x509_dn_gets(buf, sizeof(buf), &crt->subject);
++	if (buflen >= 0 && (conn->peer_subject = os_malloc((size_t)buflen+1)))
++		os_memcpy(conn->peer_subject, buf, (size_t)buflen+1);
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_TEAP
++const char * tls_connection_get_peer_subject(struct tls_connection *conn)
++{
++	if (!conn)
++		return NULL;
++	if (!conn->peer_subject) { /*(if not set during cert verify)*/
++		const mbedtls_x509_crt *peer_cert =
++		  mbedtls_ssl_get_peer_cert(&conn->ssl);
++		if (peer_cert)
++			tls_mbedtls_set_peer_subject(conn, peer_cert);
++	}
++	return conn->peer_subject;
++}
++#endif
++
++
++#ifdef TLS_MBEDTLS_EAP_TEAP
++bool tls_connection_get_own_cert_used(struct tls_connection *conn)
++{
++	/* XXX: availability of cert does not necessary mean that client
++	 * received certificate request from server and then sent cert.
++	 * ? step handshake in tls_connection_handshake() looking for
++	 *   MBEDTLS_SSL_CERTIFICATE_REQUEST ? */
++	const struct tls_conf * const tls_conf = conn->tls_conf;
++	return (tls_conf->has_client_cert && tls_conf->has_private_key);
++}
++#endif
++
++
++#if defined(CONFIG_FIPS)
++#define TLS_MBEDTLS_CONFIG_FIPS
++#endif
++
++#if defined(CONFIG_SHA256)
++#define TLS_MBEDTLS_TLS_PRF_SHA256
++#endif
++
++#if defined(CONFIG_SHA384)
++#define TLS_MBEDTLS_TLS_PRF_SHA384
++#endif
++
++
++#ifndef TLS_MBEDTLS_CONFIG_FIPS
++#if defined(CONFIG_MODULE_TESTS)
++/* unused with CONFIG_TLS=mbedtls except in crypto_module_tests.c */
++#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */ \
++ && MBEDTLS_VERSION_NUMBER <  0x03000000 /* mbedtls 3.0.0 */
++/* sha1-tlsprf.c */
++#include "sha1.h"
++int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
++		     const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
++{
++	return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_TLS1,
++				   secret, secret_len, label,
++				   seed, seed_len, out, outlen) ? -1 : 0;
++}
++#else
++#include "sha1-tlsprf.c" /* pull in hostap local implementation */
++#endif
++#endif
++#endif
++
++#ifdef TLS_MBEDTLS_TLS_PRF_SHA256
++/* sha256-tlsprf.c */
++#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++#include "sha256.h"
++int tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label,
++		   const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
++{
++	return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA256,
++				   secret, secret_len, label,
++				   seed, seed_len, out, outlen) ? -1 : 0;
++}
++#else
++#include "sha256-tlsprf.c" /* pull in hostap local implementation */
++#endif
++#endif
++
++#ifdef TLS_MBEDTLS_TLS_PRF_SHA384
++/* sha384-tlsprf.c */
++#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++#include "sha384.h"
++int tls_prf_sha384(const u8 *secret, size_t secret_len, const char *label,
++		   const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
++{
++	return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA384,
++				   secret, secret_len, label,
++				   seed, seed_len, out, outlen) ? -1 : 0;
++}
++#else
++#include "sha384-tlsprf.c" /* pull in hostap local implementation */
++#endif
++#endif
++
++
++#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
++#define mbedtls_x509_crt_has_ext_type(crt, ext_type) \
++        ((crt)->MBEDTLS_PRIVATE(ext_types) & (ext_type))
++#endif
++
++struct mlist { const char *p; size_t n; };
++
++
++static int
++tls_mbedtls_match_altsubject(mbedtls_x509_crt *crt, const char *match)
++{
++	/* RFE: this could be pre-parsed into structured data at config time */
++	struct mlist list[256]; /*(much larger than expected)*/
++	int nlist = 0;
++	if (   os_strncmp(match, "EMAIL:", 6) != 0
++	    && os_strncmp(match, "DNS:",   4) != 0
++	    && os_strncmp(match, "URI:",   4) != 0   ) {
++		wpa_printf(MSG_INFO, "MTLS: Invalid altSubjectName match '%s'", match);
++		return 0;
++	}
++	for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
++		do { } while ((tok = os_strchr(s, ';'))
++		              && os_strncmp(tok+1, "EMAIL:", 6) != 0
++		              && os_strncmp(tok+1, "DNS:",   4) != 0
++		              && os_strncmp(tok+1, "URI:",   4) != 0);
++		list[nlist].p = s;
++		list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
++		if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
++			wpa_printf(MSG_INFO, "MTLS: excessive altSubjectName match '%s'",
++			           match);
++			break; /* truncate huge list and continue */
++		}
++	}
++
++	if (!mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
++		return 0;
++
++	const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
++	for (; cur != NULL; cur = cur->next) {
++		const unsigned char san_type = (unsigned char)cur->buf.tag
++		                             & MBEDTLS_ASN1_TAG_VALUE_MASK;
++		char t;
++		size_t step = 4;
++		switch (san_type) {             /* "EMAIL:" or "DNS:" or "URI:" */
++		case MBEDTLS_X509_SAN_RFC822_NAME:       step = 6; t = 'E'; break;
++		case MBEDTLS_X509_SAN_DNS_NAME:                    t = 'D'; break;
++		case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: t = 'U'; break;
++		default: continue;
++		}
++
++		for (int i = 0; i < nlist; ++i) {
++			/* step over "EMAIL:" or "DNS:" or "URI:" in list[i].p */
++			/* Note: v is not '\0'-terminated, but is a known length vlen,
++			 * so okay to pass to os_strncasecmp() even though not z-string */
++			if (cur->buf.len == list[i].n - step && t == *list[i].p
++			    && 0 == os_strncasecmp((char *)cur->buf.p,
++			                           list[i].p+step, cur->buf.len)) {
++				return 1; /* match */
++			}
++		}
++	}
++	return 0; /* no match */
++}
++
++
++static int
++tls_mbedtls_match_suffix(const char *v, size_t vlen,
++                         const struct mlist *list, int nlist, int full)
++{
++	/* Note: v is not '\0'-terminated, but is a known length vlen,
++	 * so okay to pass to os_strncasecmp() even though not z-string */
++	for (int i = 0; i < nlist; ++i) {
++		size_t n = list[i].n;
++		if ((n == vlen || (n < vlen && v[vlen-n-1] == '.' && !full))
++		    && 0 == os_strncasecmp(v+vlen-n, list[i].p, n))
++			return 1; /* match */
++	}
++	return 0; /* no match */
++}
++
++
++static int
++tls_mbedtls_match_suffixes(mbedtls_x509_crt *crt, const char *match, int full)
++{
++	/* RFE: this could be pre-parsed into structured data at config time */
++	struct mlist list[256]; /*(much larger than expected)*/
++	int nlist = 0;
++	for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
++		tok = os_strchr(s, ';');
++		list[nlist].p = s;
++		list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
++		if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
++			wpa_printf(MSG_INFO, "MTLS: excessive suffix match '%s'", match);
++			break; /* truncate huge list and continue */
++		}
++	}
++
++	/* check subjectAltNames */
++	if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME)) {
++		const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
++		for (; cur != NULL; cur = cur->next) {
++			const unsigned char san_type = (unsigned char)cur->buf.tag
++			                             & MBEDTLS_ASN1_TAG_VALUE_MASK;
++			if (san_type == MBEDTLS_X509_SAN_DNS_NAME
++			    && tls_mbedtls_match_suffix((char *)cur->buf.p,
++			                                cur->buf.len,
++			                                list, nlist, full)) {
++				return 1; /* match */
++			}
++		}
++	}
++
++	/* check subject CN */
++	const mbedtls_x509_name *name = &crt->subject;
++	for (; name != NULL; name = name->next) {
++		if (name->oid.p && MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid) == 0)
++			break;
++	}
++	if (name && tls_mbedtls_match_suffix((char *)name->val.p, name->val.len,
++	                                     list, nlist, full)) {
++		return 1; /* match */
++	}
++
++	return 0; /* no match */
++}
++
++
++static int
++tls_mbedtls_match_dn_field(mbedtls_x509_crt *crt, const char *match)
++{
++	/* RFE: this could be pre-parsed into structured data at config time */
++	struct mlistoid { const char *p; size_t n;
++	                  const char *oid; size_t olen;
++	                  int prefix; };
++	struct mlistoid list[32]; /*(much larger than expected)*/
++	int nlist = 0;
++	for (const char *s = match, *tok, *e; *s; s = tok ? tok+1 : "") {
++		tok = os_strchr(s, '/');
++		list[nlist].oid = NULL;
++		list[nlist].olen = 0;
++		list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
++		e = memchr(s, '=', list[nlist].n);
++		if (e == NULL) {
++			if (list[nlist].n == 0)
++				continue; /* skip consecutive, repeated '/' */
++			if (list[nlist].n == 1 && *s == '*') {
++				/* special-case "*" to match any OID and value */
++				s = e = "=*";
++				list[nlist].n = 2;
++				list[nlist].oid = "";
++			}
++			else {
++				wpa_printf(MSG_INFO,
++				           "MTLS: invalid check_cert_subject '%s' missing '='",
++				           match);
++				return 0;
++			}
++		}
++		switch (e - s) {
++		case 1:
++			if (*s == 'C') {
++				list[nlist].oid  = MBEDTLS_OID_AT_COUNTRY;
++				list[nlist].olen = sizeof(MBEDTLS_OID_AT_COUNTRY)-1;
++			}
++			else if (*s == 'L') {
++				list[nlist].oid  = MBEDTLS_OID_AT_LOCALITY;
++				list[nlist].olen = sizeof(MBEDTLS_OID_AT_LOCALITY)-1;
++			}
++			else if (*s == 'O') {
++				list[nlist].oid  = MBEDTLS_OID_AT_ORGANIZATION;
++				list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORGANIZATION)-1;
++			}
++			break;
++		case 2:
++			if (s[0] == 'C' && s[1] == 'N') {
++				list[nlist].oid  = MBEDTLS_OID_AT_CN;
++				list[nlist].olen = sizeof(MBEDTLS_OID_AT_CN)-1;
++			}
++			else if (s[0] == 'S' && s[1] == 'T') {
++				list[nlist].oid  = MBEDTLS_OID_AT_STATE;
++				list[nlist].olen = sizeof(MBEDTLS_OID_AT_STATE)-1;
++			}
++			else if (s[0] == 'O' && s[1] == 'U') {
++				list[nlist].oid  = MBEDTLS_OID_AT_ORG_UNIT;
++				list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORG_UNIT)-1;
++			}
++			break;
++		case 12:
++			if (os_memcmp(s, "emailAddress", 12) == 0) {
++				list[nlist].oid  = MBEDTLS_OID_PKCS9_EMAIL;
++				list[nlist].olen = sizeof(MBEDTLS_OID_PKCS9_EMAIL)-1;
++			}
++			break;
++		default:
++			break;
++		}
++		if (list[nlist].oid == NULL) {
++			wpa_printf(MSG_INFO,
++			           "MTLS: Unknown field in check_cert_subject '%s'",
++			           match);
++			return 0;
++		}
++		list[nlist].n -= (size_t)(++e - s);
++		list[nlist].p = e;
++		if (list[nlist].n && e[list[nlist].n-1] == '*') {
++			--list[nlist].n;
++			list[nlist].prefix = 1;
++		}
++		/*(could easily add support for suffix matches if value begins with '*',
++		 * but suffix match is not currently supported by other TLS modules)*/
++
++		if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
++			wpa_printf(MSG_INFO,
++			           "MTLS: excessive check_cert_subject match '%s'",
++			           match);
++			break; /* truncate huge list and continue */
++		}
++	}
++
++	/* each component in match string must match cert Subject in order listed
++	 * The behavior below preserves ordering but is slightly different than
++	 * the grossly inefficient contortions implemented in tls_openssl.c */
++	const mbedtls_x509_name *name = &crt->subject;
++	for (int i = 0; i < nlist; ++i) {
++		int found = 0;
++		for (; name != NULL && !found; name = name->next) {
++			if (!name->oid.p)
++				continue;
++			/* special-case "*" to match any OID and value */
++			if (list[i].olen == 0) {
++				found = 1;
++				continue;
++			}
++			/* perform equalent of !MBEDTLS_OID_CMP() with oid ptr and len */
++			if (list[i].olen != name->oid.len
++			    || os_memcmp(list[i].oid, name->oid.p, name->oid.len) != 0)
++				continue;
++			/* Note: v is not '\0'-terminated, but is a known length vlen,
++			 * so okay to pass to os_strncasecmp() even though not z-string */
++			if ((list[i].prefix
++			      ? list[i].n <= name->val.len  /* prefix match */
++			      : list[i].n == name->val.len) /* full match */
++			    && 0 == os_strncasecmp((char *)name->val.p,
++			                           list[i].p, list[i].n)) {
++				found = 1;
++				continue;
++			}
++		}
++		if (!found)
++			return 0; /* no match */
++	}
++	return 1; /* match */
++}
++
++
++__attribute_cold__
++static void
++tls_mbedtls_verify_fail_event (mbedtls_x509_crt *crt, int depth,
++                               const char *errmsg, enum tls_fail_reason reason)
++{
++	struct tls_config *init_conf = &tls_ctx_global.init_conf;
++	if (init_conf->event_cb == NULL)
++		return;
++
++	struct wpabuf *certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
++	char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
++	if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
++		subject[0] = '\0';
++	union tls_event_data ev;
++	os_memset(&ev, 0, sizeof(ev));
++	ev.cert_fail.reason = reason;
++	ev.cert_fail.depth = depth;
++	ev.cert_fail.subject = subject;
++	ev.cert_fail.reason_txt = errmsg;
++	ev.cert_fail.cert = certbuf;
++
++	init_conf->event_cb(init_conf->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
++
++	wpabuf_free(certbuf);
++}
++
++
++__attribute_noinline__
++static void
++tls_mbedtls_verify_cert_event (struct tls_connection *conn,
++                               mbedtls_x509_crt *crt, int depth)
++{
++	struct tls_config *init_conf = &tls_ctx_global.init_conf;
++	if (init_conf->event_cb == NULL)
++		return;
++
++	struct wpabuf *certbuf = NULL;
++	union tls_event_data ev;
++	os_memset(&ev, 0, sizeof(ev));
++
++  #ifdef MBEDTLS_SHA256_C
++	u8 hash[SHA256_DIGEST_LENGTH];
++	const u8 *addr[] = { (u8 *)crt->raw.p };
++	if (sha256_vector(1, addr, &crt->raw.len, hash) == 0) {
++		ev.peer_cert.hash = hash;
++		ev.peer_cert.hash_len = sizeof(hash);
++	}
++  #endif
++	ev.peer_cert.depth = depth;
++	char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
++	if (depth == 0)
++		ev.peer_cert.subject = conn->peer_subject;
++	if (ev.peer_cert.subject == NULL) {
++		ev.peer_cert.subject = subject;
++		if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
++			subject[0] = '\0';
++	}
++
++	char serial_num[128+1];
++	ev.peer_cert.serial_num =
++	  tls_mbedtls_peer_serial_num(crt, serial_num, sizeof(serial_num));
++
++	const mbedtls_x509_sequence *cur;
++
++	cur = NULL;
++	if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
++		cur = &crt->subject_alt_names;
++	for (; cur != NULL; cur = cur->next) {
++		const unsigned char san_type = (unsigned char)cur->buf.tag
++		                             & MBEDTLS_ASN1_TAG_VALUE_MASK;
++		size_t prelen = 4;
++		const char *pre;
++		switch (san_type) {
++		case MBEDTLS_X509_SAN_RFC822_NAME:     prelen = 6; pre = "EMAIL:";break;
++		case MBEDTLS_X509_SAN_DNS_NAME:                    pre = "DNS:";  break;
++		case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: pre = "URI:";  break;
++		default: continue;
++		}
++
++		char *pos = os_malloc(prelen + cur->buf.len + 1);
++		if (pos == NULL)
++			break;
++		ev.peer_cert.altsubject[ev.peer_cert.num_altsubject] = pos;
++		os_memcpy(pos, pre, prelen);
++		/* data should be properly backslash-escaped if needed,
++		 * so code below does not re-escape, but does replace CTLs */
++		/*os_memcpy(pos+prelen, cur->buf.p, cur->buf.len);*/
++		/*pos[prelen+cur->buf.len] = '\0';*/
++		pos += prelen;
++		for (size_t i = 0; i < cur->buf.len; ++i) {
++			unsigned char c = cur->buf.p[i];
++			*pos++ = (c >= 32 && c != 127) ? c : '?';
++		}
++		*pos = '\0';
++
++		if (++ev.peer_cert.num_altsubject == TLS_MAX_ALT_SUBJECT)
++			break;
++	}
++
++	cur = NULL;
++	if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_CERTIFICATE_POLICIES))
++		cur = &crt->certificate_policies;
++	for (; cur != NULL; cur = cur->next) {
++		if (cur->buf.len != 11) /* len of OID_TOD_STRICT or OID_TOD_TOFU */
++			continue;
++		/* TOD-STRICT "1.3.6.1.4.1.40808.1.3.1" */
++		/* TOD-TOFU   "1.3.6.1.4.1.40808.1.3.2" */
++		#define OID_TOD_STRICT "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x01"
++		#define OID_TOD_TOFU   "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x02"
++		if (os_memcmp(cur->buf.p,
++		              OID_TOD_STRICT, sizeof(OID_TOD_STRICT)-1) == 0) {
++			ev.peer_cert.tod = 1; /* TOD-STRICT */
++			break;
++		}
++		if (os_memcmp(cur->buf.p,
++		              OID_TOD_TOFU, sizeof(OID_TOD_TOFU)-1) == 0) {
++			ev.peer_cert.tod = 2; /* TOD-TOFU */
++			break;
++		}
++	}
++
++	struct tls_conf *tls_conf = conn->tls_conf;
++	if (tls_conf->ca_cert_probe || (tls_conf->flags & TLS_CONN_EXT_CERT_CHECK)
++	    || init_conf->cert_in_cb) {
++		certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
++		ev.peer_cert.cert = certbuf;
++	}
++
++	init_conf->event_cb(init_conf->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
++
++	wpabuf_free(certbuf);
++	char **altsubject;
++	*(const char ***)&altsubject = ev.peer_cert.altsubject;
++	for (size_t i = 0; i < ev.peer_cert.num_altsubject; ++i)
++		os_free(altsubject[i]);
++}
++
++
++static int
++tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
++{
++	/* XXX: N.B. verify code not carefully tested besides hwsim tests
++	 *
++	 * RFE: mbedtls_x509_crt_verify_info() and enhance log trace messages
++	 * RFE: review and add support for additional TLS_CONN_* flags
++	 * not handling OCSP (not available in mbedtls)
++	 * ... */
++
++	struct tls_connection *conn = (struct tls_connection *)arg;
++	struct tls_conf *tls_conf = conn->tls_conf;
++	uint32_t flags_in = *flags;
++
++	if (depth > 8) { /*(depth 8 picked as arbitrary limit)*/
++		emsg(MSG_WARNING, "client cert chain too long");
++		*flags |= MBEDTLS_X509_BADCERT_OTHER; /* cert chain too long */
++		tls_mbedtls_verify_fail_event(crt, depth,
++			                      "client cert chain too long",
++		                              TLS_FAIL_BAD_CERTIFICATE);
++	}
++	else if (tls_conf->verify_depth0_only) {
++		if (depth > 0)
++			*flags = 0;
++		else {
++		  #ifdef MBEDTLS_SHA256_C
++			u8 hash[SHA256_DIGEST_LENGTH];
++			const u8 *addr[] = { (u8 *)crt->raw.p };
++			if (sha256_vector(1, addr, &crt->raw.len, hash) < 0
++			    || os_memcmp(tls_conf->ca_cert_hash, hash, sizeof(hash)) != 0) {
++				*flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED;
++				tls_mbedtls_verify_fail_event(crt, depth,
++			                                      "cert hash mismatch",
++				                              TLS_FAIL_UNTRUSTED);
++			}
++			else /* hash matches; ignore other issues *except* if revoked)*/
++				*flags &= MBEDTLS_X509_BADCERT_REVOKED;
++		  #endif
++		}
++	}
++	else if (depth == 0) {
++		if (!conn->peer_subject)
++			tls_mbedtls_set_peer_subject(conn, crt);
++		/*(use same labels to tls_mbedtls_verify_fail_event() as used in
++		 * other TLS modules so that hwsim tests find exact string match)*/
++		if (!conn->peer_subject) { /* error copying subject string */
++			*flags |= MBEDTLS_X509_BADCERT_OTHER;
++			tls_mbedtls_verify_fail_event(crt, depth,
++			                              "internal error",
++			                              TLS_FAIL_UNSPECIFIED);
++		}
++		/*(use os_strstr() for subject match as is done in tls_mbedtls.c
++		 * to follow the same behavior, even though a suffix match would
++		 * make more sense.  Also, note that strstr match does not
++		 * normalize whitespace (between components) for comparison)*/
++		else if (tls_conf->subject_match
++		         && os_strstr(conn->peer_subject,
++		                      tls_conf->subject_match) == NULL) {
++			wpa_printf(MSG_WARNING,
++			           "MTLS: Subject '%s' did not match with '%s'",
++			           conn->peer_subject, tls_conf->subject_match);
++			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++			tls_mbedtls_verify_fail_event(crt, depth,
++			                              "Subject mismatch",
++			                              TLS_FAIL_SUBJECT_MISMATCH);
++		}
++		if (tls_conf->altsubject_match
++		    && !tls_mbedtls_match_altsubject(crt, tls_conf->altsubject_match)) {
++			wpa_printf(MSG_WARNING,
++				   "MTLS: altSubjectName match '%s' not found",
++			           tls_conf->altsubject_match);
++			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++			tls_mbedtls_verify_fail_event(crt, depth,
++			                              "AltSubject mismatch",
++			                              TLS_FAIL_ALTSUBJECT_MISMATCH);
++		}
++		if (tls_conf->suffix_match
++		    && !tls_mbedtls_match_suffixes(crt, tls_conf->suffix_match, 0)) {
++			wpa_printf(MSG_WARNING,
++			           "MTLS: Domain suffix match '%s' not found",
++				   tls_conf->suffix_match);
++			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++			tls_mbedtls_verify_fail_event(crt, depth,
++			                              "Domain suffix mismatch",
++			                              TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
++		}
++		if (tls_conf->domain_match
++		    && !tls_mbedtls_match_suffixes(crt, tls_conf->domain_match, 1)) {
++			wpa_printf(MSG_WARNING,
++			           "MTLS: Domain match '%s' not found",
++				   tls_conf->domain_match);
++			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++			tls_mbedtls_verify_fail_event(crt, depth,
++			                              "Domain mismatch",
++			                              TLS_FAIL_DOMAIN_MISMATCH);
++		}
++		if (tls_conf->check_cert_subject
++		    && !tls_mbedtls_match_dn_field(crt, tls_conf->check_cert_subject)) {
++			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++			tls_mbedtls_verify_fail_event(crt, depth,
++			                              "Distinguished Name",
++			                              TLS_FAIL_DN_MISMATCH);
++		}
++		if (tls_conf->flags & TLS_CONN_SUITEB) {
++			/* check RSA modulus size (public key bitlen) */
++			const mbedtls_pk_type_t pk_alg = mbedtls_pk_get_type(&crt->pk);
++			if ((pk_alg == MBEDTLS_PK_RSA || pk_alg == MBEDTLS_PK_RSASSA_PSS)
++			    && mbedtls_pk_get_bitlen(&crt->pk) < 3072) {
++				/* hwsim suite_b RSA tests expect 3072
++				 *   suite_b_192_rsa_ecdhe_radius_rsa2048_client
++				 *   suite_b_192_rsa_dhe_radius_rsa2048_client */
++				*flags |= MBEDTLS_X509_BADCERT_BAD_KEY;
++				tls_mbedtls_verify_fail_event(crt, depth,
++				                              "Insufficient RSA modulus size",
++				                              TLS_FAIL_INSUFFICIENT_KEY_LEN);
++			}
++		}
++		if (tls_conf->check_crl && tls_conf->crl == NULL) {
++			/* see tests/hwsim test_ap_eap.py ap_wpa2_eap_tls_check_crl */
++			emsg(MSG_WARNING, "check_crl set but no CRL loaded; reject all?");
++			*flags |= MBEDTLS_X509_BADCERT_OTHER;
++			tls_mbedtls_verify_fail_event(crt, depth,
++				                      "check_crl set but no CRL loaded; "
++			                              "reject all?",
++			                              TLS_FAIL_BAD_CERTIFICATE);
++		}
++	}
++	else {
++		if (tls_conf->check_crl != 2) /* 2 == verify CRLs for all certs */
++			*flags &= ~MBEDTLS_X509_BADCERT_REVOKED;
++	}
++
++	if (!tls_conf->check_crl_strict) {
++		*flags &= ~MBEDTLS_X509_BADCRL_EXPIRED;
++		*flags &= ~MBEDTLS_X509_BADCRL_FUTURE;
++	}
++
++	if (tls_conf->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
++		*flags &= ~MBEDTLS_X509_BADCERT_EXPIRED;
++		*flags &= ~MBEDTLS_X509_BADCERT_FUTURE;
++	}
++
++	tls_mbedtls_verify_cert_event(conn, crt, depth);
++
++	if (*flags) {
++		if (*flags & (MBEDTLS_X509_BADCERT_NOT_TRUSTED
++		             |MBEDTLS_X509_BADCERT_CN_MISMATCH
++		             |MBEDTLS_X509_BADCERT_REVOKED)) {
++			emsg(MSG_WARNING, "client cert not trusted");
++		}
++		/* report event if flags set but no additional flags set above */
++		/* (could translate flags to more detailed TLS_FAIL_* if needed) */
++		if (!(*flags & ~flags_in)) {
++			enum tls_fail_reason reason = TLS_FAIL_UNSPECIFIED;
++			const char *errmsg = "cert verify fail unspecified";
++			if (*flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) {
++				reason = TLS_FAIL_UNTRUSTED;
++				errmsg = "certificate not trusted";
++			}
++			if (*flags & MBEDTLS_X509_BADCERT_REVOKED) {
++				reason = TLS_FAIL_REVOKED;
++				errmsg = "certificate has been revoked";
++			}
++			if (*flags & MBEDTLS_X509_BADCERT_FUTURE) {
++				reason = TLS_FAIL_NOT_YET_VALID;
++				errmsg = "certificate not yet valid";
++			}
++			if (*flags & MBEDTLS_X509_BADCERT_EXPIRED) {
++				reason = TLS_FAIL_EXPIRED;
++				errmsg = "certificate has expired";
++			}
++			if (*flags & MBEDTLS_X509_BADCERT_BAD_MD) {
++				reason = TLS_FAIL_BAD_CERTIFICATE;
++				errmsg = "certificate uses insecure algorithm";
++			}
++			tls_mbedtls_verify_fail_event(crt, depth, errmsg, reason);
++		}
++	  #if 0
++		/* ??? send (again) cert events for all certs in chain ???
++		 * (should already have been called for greater depths) */
++		/* tls_openssl.c:tls_verify_cb() sends cert events for all certs
++		 * in chain if certificate validation fails, but sends all events
++		 * with depth set to 0 (might be a bug) */
++		if (depth > 0) {
++			int pdepth = depth + 1;
++			for (mbedtls_x509_crt *pcrt; (pcrt = crt->next); ++pdepth) {
++				tls_mbedtls_verify_cert_event(conn, pcrt, pdepth);
++			}
++		}
++	  #endif
++		/*(do not preserve subject if verification failed but was optional)*/
++		if (depth == 0 && conn->peer_subject) {
++			os_free(conn->peer_subject);
++			conn->peer_subject = NULL;
++		}
++	}
++	else if (depth == 0) {
++		struct tls_config *init_conf = &tls_ctx_global.init_conf;
++		if (tls_conf->ca_cert_probe) {
++			/* reject server certificate on probe-only run */
++			*flags |= MBEDTLS_X509_BADCERT_OTHER;
++			tls_mbedtls_verify_fail_event(crt, depth,
++			                              "server chain probe",
++			                              TLS_FAIL_SERVER_CHAIN_PROBE);
++		}
++		else if (init_conf->event_cb) {
++			/* ??? send event as soon as depth == 0 is verified ???
++			 * What about rest of chain?
++			 * Follows tls_mbedtls.c behavior: */
++			init_conf->event_cb(init_conf->cb_ctx,
++			                    TLS_CERT_CHAIN_SUCCESS, NULL);
++		}
++	}
++
++	return 0;
++}
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 4331782d8..e1a447333 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -979,6 +979,9 @@ struct wpa_driver_associate_params {
+ 	 * responsible for selecting with which BSS to associate. */
+ 	const u8 *bssid;
+ 
++	unsigned char rates[WLAN_SUPP_RATES_MAX];
++	int mcast_rate;
++
+ 	/**
+ 	 * bssid_hint - BSSID of a proposed AP
+ 	 *
+@@ -1886,6 +1889,7 @@ struct wpa_driver_mesh_join_params {
+ #define WPA_DRIVER_MESH_FLAG_AMPE	0x00000008
+ 	unsigned int flags;
+ 	bool handle_dfs;
++	int mcast_rate;
+ };
+ 
+ struct wpa_driver_set_key_params {
+@@ -2357,6 +2361,9 @@ struct wpa_driver_capa {
+ 	/** Maximum number of iterations in a single scan plan */
+ 	u32 max_sched_scan_plan_iterations;
+ 
++	/** Maximum number of extra IE bytes for scans */
++	u16 max_scan_ie_len;
++
+ 	/** Whether sched_scan (offloaded scanning) is supported */
+ 	int sched_scan_supported;
+ 
+@@ -3887,6 +3894,25 @@ struct wpa_driver_ops {
+ 	int (*if_remove)(void *priv, enum wpa_driver_if_type type,
+ 			 const char *ifname);
+ 
++	/**
++	 * if_rename - Rename a virtual interface
++	 * @priv: Private driver interface data
++	 * @type: Interface type
++	 * @ifname: Interface name of the virtual interface to be renamed
++	 *	    (NULL when renaming the AP BSS interface)
++	 * @new_name: New interface name of the virtual interface
++	 * Returns: 0 on success, -1 on failure
++	 */
++	int (*if_rename)(void *priv, enum wpa_driver_if_type type,
++			 const char *ifname, const char *new_name);
++
++	/**
++	 * set_first_bss - Make a virtual interface the first (primary) bss
++	 * @priv: Private driver interface data
++	 * Returns: 0 on success, -1 on failure
++	 */
++	int (*set_first_bss)(void *priv);
++
+ 	/**
+ 	 * set_sta_vlan - Bind a station into a specific interface (AP only)
+ 	 * @priv: Private driver interface data
+@@ -3989,7 +4015,7 @@ struct wpa_driver_ops {
+ 	 * Returns: 0 on success, -1 on failure
+ 	 */
+ 	int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val,
+-			   const char *bridge_ifname, char *ifname_wds);
++			   const char *bridge_ifname, const char *ifname_wds);
+ 
+ 	/**
+ 	 * send_action - Transmit an Action frame
+@@ -4291,7 +4317,7 @@ struct wpa_driver_ops {
+ 	 * Returns: 0 on success, negative (<0) on failure
+ 	 */
+ 	int (*br_set_net_param)(void *priv, enum drv_br_net_param param,
+-				unsigned int val);
++				const char *ifname, unsigned int val);
+ 
+ 	/**
+ 	 * get_wowlan - Get wake-on-wireless status
+@@ -6588,6 +6614,7 @@ union wpa_event_data {
+ 
+ 	/**
+ 	 * struct ch_switch
++	 * @count: Count until channel switch activates
+ 	 * @freq: Frequency of new channel in MHz
+ 	 * @ht_enabled: Whether this is an HT channel
+ 	 * @ch_offset: Secondary channel offset
+@@ -6598,6 +6625,7 @@ union wpa_event_data {
+ 	 * @punct_bitmap: Puncturing bitmap
+ 	 */
+ 	struct ch_switch {
++		int count;
+ 		int freq;
+ 		int ht_enabled;
+ 		int ch_offset;
+@@ -6846,8 +6874,8 @@ union wpa_event_data {
+  * Driver wrapper code should call this function whenever an event is received
+  * from the driver.
+  */
+-void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+-			  union wpa_event_data *data);
++extern void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
++				    union wpa_event_data *data);
+ 
+ /**
+  * wpa_supplicant_event_global - Report a driver event for wpa_supplicant
+@@ -6859,7 +6887,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+  * Same as wpa_supplicant_event(), but we search for the interface in
+  * wpa_global.
+  */
+-void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
++extern void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
+ 				 union wpa_event_data *data);
+ 
+ /*
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 39f58ff83..a70eaae38 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -75,6 +75,16 @@ enum nlmsgerr_attrs {
+ 
+ #endif /* ANDROID */
+ 
++static void handle_nl_debug_hook(struct nl_msg *msg, int tx)
++{
++	const struct nlmsghdr *nlh;
++
++	if (!wpa_netlink_hook)
++		return;
++
++	nlh = nlmsg_hdr(msg);
++	wpa_netlink_hook(tx, nlh, nlh->nlmsg_len);
++}
+ 
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+@@ -429,6 +439,11 @@ static int no_seq_check(struct nl_msg *msg, void *arg)
+ 	return NL_OK;
+ }
+ 
++static int debug_handler(struct nl_msg *msg, void *arg)
++{
++	handle_nl_debug_hook(msg, 0);
++	return NL_OK;
++}
+ 
+ static void nl80211_nlmsg_clear(struct nl_msg *msg)
+ {
+@@ -502,6 +517,8 @@ int send_and_recv(struct nl80211_global *global,
+ 	if (!msg)
+ 		return -ENOMEM;
+ 
++	handle_nl_debug_hook(msg, 1);
++
+ 	err.err = -ENOMEM;
+ 
+ 	s_nl_cb = nl_socket_get_cb(nl_handle);
+@@ -536,6 +553,7 @@ int send_and_recv(struct nl80211_global *global,
+ 	err.orig_msg = msg;
+ 	err.err_info = err_info;
+ 
++	nl_cb_set(cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
+ 	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
+ 	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err.err);
+ 	if (ack_handler_custom) {
+@@ -939,6 +957,7 @@ nl80211_get_wiphy_data_ap(struct i802_bss *bss)
+ 			os_free(w);
+ 			return NULL;
+ 		}
++		nl_cb_set(w->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
+ 		nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ 			  no_seq_check, NULL);
+ 		nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+@@ -1353,7 +1372,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
+ 		}
+ 		wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
+ 			   namebuf, ifname);
+-		if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
++		if (drv->first_bss->ifindex != ifi->ifi_index) {
+ 			wpa_printf(MSG_DEBUG,
+ 				   "nl80211: Not the main interface (%s) - do not indicate interface down",
+ 				   drv->first_bss->ifname);
+@@ -1389,7 +1408,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
+ 		}
+ 		wpa_printf(MSG_DEBUG, "nl80211: Interface up (%s/%s)",
+ 			   namebuf, ifname);
+-		if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
++		if (drv->first_bss->ifindex != ifi->ifi_index) {
+ 			wpa_printf(MSG_DEBUG,
+ 				   "nl80211: Not the main interface (%s) - do not indicate interface up",
+ 				   drv->first_bss->ifname);
+@@ -2035,6 +2054,7 @@ static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
+ 	genl_family_put(family);
+ 	nl_cache_free(cache);
+ 
++	nl_cb_set(global->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
+ 	nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ 		  no_seq_check, NULL);
+ 	nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+@@ -2205,6 +2225,7 @@ static int nl80211_init_bss(struct i802_bss *bss)
+ 	if (!bss->nl_cb)
+ 		return -1;
+ 
++	nl_cb_set(bss->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
+ 	nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ 		  no_seq_check, NULL);
+ 	nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+@@ -3083,7 +3104,7 @@ static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss,
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+ 	struct i802_link *link = nl80211_get_link(bss, link_id);
+ 
+-	if (!link->beacon_set)
++	if (!link || !link->beacon_set)
+ 		return 0;
+ 
+ 	wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
+@@ -5494,7 +5515,7 @@ static int nl80211_set_channel(struct i802_bss *bss,
+ 		   freq->he_enabled, freq->eht_enabled, freq->bandwidth,
+ 		   freq->center_freq1, freq->center_freq2);
+ 
+-	msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
++	msg = nl80211_bss_msg(bss, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
+ 			      NL80211_CMD_SET_WIPHY);
+ 	if (!msg || nl80211_put_freq_params(msg, freq) < 0) {
+ 		nlmsg_free(msg);
+@@ -6183,8 +6204,7 @@ static void nl80211_teardown_ap(struct i802_bss *bss)
+ 		nl80211_mgmt_unsubscribe(bss, "AP teardown");
+ 
+ 	nl80211_put_wiphy_data_ap(bss);
+-	if (bss->flink)
+-		bss->flink->beacon_set = 0;
++	wpa_driver_nl80211_del_beacon_all(bss);
+ }
+ 
+ 
+@@ -8414,24 +8434,14 @@ static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+ 
+ 
+ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
+-			    const char *bridge_ifname, char *ifname_wds)
++			    const char *bridge_ifname, const char *ifname_wds)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+-	char name[IFNAMSIZ + 1];
++	const char *name = ifname_wds; // Kept to reduce changes to the minimum
+ 	union wpa_event_data event;
+ 	int ret;
+ 
+-	ret = os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
+-	if (ret >= (int) sizeof(name))
+-		wpa_printf(MSG_WARNING,
+-			   "nl80211: WDS interface name was truncated");
+-	else if (ret < 0)
+-		return ret;
+-
+-	if (ifname_wds)
+-		os_strlcpy(ifname_wds, name, IFNAMSIZ + 1);
+-
+ 	wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
+ 		   " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
+ 	if (val) {
+@@ -8574,6 +8584,7 @@ static void *i802_init(struct hostapd_data *hapd,
+ 	char master_ifname[IFNAMSIZ];
+ 	int ifindex, br_ifindex = 0;
+ 	int br_added = 0;
++	int err;
+ 
+ 	bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
+ 					  params->global_priv, 1,
+@@ -8633,21 +8644,17 @@ static void *i802_init(struct hostapd_data *hapd,
+ 	    (params->num_bridge == 0 || !params->bridge[0]))
+ 		add_ifidx(drv, br_ifindex, drv->ifindex);
+ 
+-	if (bss->added_if_into_bridge || bss->already_in_bridge) {
+-		int err;
+-
+-		drv->rtnl_sk = nl_socket_alloc();
+-		if (drv->rtnl_sk == NULL) {
+-			wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
+-			goto failed;
+-		}
++	drv->rtnl_sk = nl_socket_alloc();
++	if (drv->rtnl_sk == NULL) {
++		wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
++		goto failed;
++	}
+ 
+-		err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
+-		if (err) {
+-			wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
+-				   nl_geterror(err));
+-			goto failed;
+-		}
++	err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
++	if (err) {
++		wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
++			   nl_geterror(err));
++		goto failed;
+ 	}
+ 
+ 	if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
+@@ -8998,8 +9005,6 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
+ 		wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context");
+ 		nl80211_teardown_ap(bss);
+ 		nl80211_remove_links(bss);
+-		if (!bss->added_if && !drv->first_bss->next)
+-			wpa_driver_nl80211_del_beacon_all(bss);
+ 		nl80211_destroy_bss(bss);
+ 		if (!bss->added_if)
+ 			i802_set_iface_flags(bss, 0);
+@@ -9016,6 +9021,50 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
+ 	return 0;
+ }
+ 
++static int wpa_driver_nl80211_if_rename(struct i802_bss *bss,
++					enum wpa_driver_if_type type,
++					const char *ifname, const char *new_name)
++{
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct ifinfomsg ifi = {
++		.ifi_family = AF_UNSPEC,
++		.ifi_index = bss->ifindex,
++	};
++	struct nl_msg *msg;
++	int res = -ENOMEM;
++
++	if (ifname)
++		ifi.ifi_index = if_nametoindex(ifname);
++
++	msg = nlmsg_alloc_simple(RTM_SETLINK, 0);
++	if (!msg)
++		return res;
++
++	if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
++		goto out;
++
++	if (nla_put_string(msg, IFLA_IFNAME, new_name))
++		goto out;
++
++	res = nl_send_auto_complete(drv->rtnl_sk, msg);
++	if (res < 0)
++		goto out;
++
++	res = nl_wait_for_ack(drv->rtnl_sk);
++	if (res) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Renaming device %s to %s failed: %s",
++			   ifname ? ifname : bss->ifname, new_name, nl_geterror(res));
++		goto out;
++	}
++
++	if (type == WPA_IF_AP_BSS && !ifname)
++		os_strlcpy(bss->ifname, new_name, sizeof(bss->ifname));
++
++out:
++	nlmsg_free(msg);
++	return res;
++}
+ 
+ static int cookie_handler(struct nl_msg *msg, void *arg)
+ {
+@@ -10807,6 +10856,37 @@ static bool nl80211_is_drv_shared(void *priv, void *bss_ctx)
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ 
++static int driver_nl80211_if_rename(void *priv, enum wpa_driver_if_type type,
++				    const char *ifname, const char *new_name)
++{
++	struct i802_bss *bss = priv;
++	return wpa_driver_nl80211_if_rename(bss, type, ifname, new_name);
++}
++
++
++static int driver_nl80211_set_first_bss(void *priv)
++{
++	struct i802_bss *bss = priv, *tbss;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++
++	if (drv->first_bss == bss)
++		return 0;
++
++	for (tbss = drv->first_bss; tbss; tbss = tbss->next) {
++		if (tbss->next != bss)
++			continue;
++
++		tbss->next = bss->next;
++		bss->next = drv->first_bss;
++		drv->first_bss = bss;
++		drv->ctx = bss->ctx;
++		return 0;
++	}
++
++	return -1;
++}
++
++
+ static int driver_nl80211_send_mlme(void *priv, const u8 *data,
+ 				    size_t data_len, int noack,
+ 				    unsigned int freq,
+@@ -11309,6 +11389,10 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ 	if (ret)
+ 		goto error;
+ 
++	if (drv->nlmode == NL80211_IFTYPE_MESH_POINT) {
++		nla_put_flag(msg, NL80211_ATTR_HANDLE_DFS);
++	}
++
+ 	/* beacon_csa params */
+ 	beacon_csa = nla_nest_start(msg, NL80211_ATTR_CSA_IES);
+ 	if (!beacon_csa)
+@@ -11983,6 +12067,18 @@ static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id,
+ }
+ 
+ 
++static int nl80211_put_mcast_rate(struct nl_msg *msg, int mcast_rate)
++{
++	if (mcast_rate > 0) {
++		wpa_printf(MSG_DEBUG, "  * mcast_rate=%.1f",
++			   (double)mcast_rate / 10);
++		return nla_put_u32(msg, NL80211_ATTR_MCAST_RATE, mcast_rate);
++	}
++
++	return 0;
++}
++
++
+ static int nl80211_put_mesh_config(struct nl_msg *msg,
+ 				   struct wpa_driver_mesh_bss_params *params)
+ {
+@@ -12044,6 +12140,7 @@ static int nl80211_join_mesh(struct i802_bss *bss,
+ 	    nl80211_put_basic_rates(msg, params->basic_rates) ||
+ 	    nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
+ 	    nl80211_put_beacon_int(msg, params->beacon_int) ||
++	    nl80211_put_mcast_rate(msg, params->mcast_rate) ||
+ 	    nl80211_put_dtim_period(msg, params->dtim_period))
+ 		goto fail;
+ 
+@@ -12397,7 +12494,7 @@ static const char * drv_br_net_param_str(enum drv_br_net_param param)
+ 
+ 
+ static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
+-				       unsigned int val)
++				       const char *ifname, unsigned int val)
+ {
+ 	struct i802_bss *bss = priv;
+ 	char path[128];
+@@ -12423,8 +12520,11 @@ static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
+ 			return -EINVAL;
+ 	}
+ 
++	if (!ifname)
++		ifname = bss->brname;
++
+ 	os_snprintf(path, sizeof(path), "/proc/sys/net/ipv%d/conf/%s/%s",
+-		    ip_version, bss->brname, param_txt);
++		    ip_version, ifname, param_txt);
+ 
+ set_val:
+ 	if (linux_write_system_file(path, val))
+@@ -14027,6 +14127,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.set_acl = wpa_driver_nl80211_set_acl,
+ 	.if_add = wpa_driver_nl80211_if_add,
+ 	.if_remove = driver_nl80211_if_remove,
++	.if_rename = driver_nl80211_if_rename,
++	.set_first_bss = driver_nl80211_set_first_bss,
+ 	.send_mlme = driver_nl80211_send_mlme,
+ 	.get_hw_feature_data = nl80211_get_hw_feature_data,
+ 	.sta_add = wpa_driver_nl80211_sta_add,
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 26c1f4140..d5ba66b10 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -976,6 +976,10 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 			nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]);
+ 	}
+ 
++	if (tb[NL80211_ATTR_MAX_SCAN_IE_LEN])
++		capa->max_scan_ie_len =
++			nla_get_u16(tb[NL80211_ATTR_MAX_SCAN_IE_LEN]);
++
+ 	if (tb[NL80211_ATTR_MAX_MATCH_SETS])
+ 		capa->max_match_sets =
+ 			nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index aee815e97..768c72905 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -1196,6 +1196,7 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
+ 				 struct nlattr *bw, struct nlattr *cf1,
+ 				 struct nlattr *cf2,
+ 				 struct nlattr *punct_bitmap,
++				 struct nlattr *count,
+ 				 int finished)
+ {
+ 	struct i802_bss *bss;
+@@ -1259,6 +1260,8 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
+ 		data.ch_switch.cf1 = nla_get_u32(cf1);
+ 	if (cf2)
+ 		data.ch_switch.cf2 = nla_get_u32(cf2);
++	if (count)
++		data.ch_switch.count = nla_get_u32(count);
+ 
+ 	if (link)
+ 		data.ch_switch.link_id = nla_get_u8(link);
+@@ -3999,6 +4002,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
+ 				     tb[NL80211_ATTR_CENTER_FREQ1],
+ 				     tb[NL80211_ATTR_CENTER_FREQ2],
+ 				     tb[NL80211_ATTR_PUNCT_BITMAP],
++				     tb[NL80211_ATTR_CH_SWITCH_COUNT],
+ 				     0);
+ 		break;
+ 	case NL80211_CMD_CH_SWITCH_NOTIFY:
+@@ -4011,6 +4015,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
+ 				     tb[NL80211_ATTR_CENTER_FREQ1],
+ 				     tb[NL80211_ATTR_CENTER_FREQ2],
+ 				     tb[NL80211_ATTR_PUNCT_BITMAP],
++				     NULL,
+ 				     1);
+ 		break;
+ 	case NL80211_CMD_DISCONNECT:
+diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
+index b055e684a..a8ea8f2cf 100644
+--- a/src/drivers/driver_nl80211_scan.c
++++ b/src/drivers/driver_nl80211_scan.c
+@@ -221,7 +221,7 @@ nl80211_scan_common(struct i802_bss *bss, u8 cmd,
+ 		wpa_printf(MSG_DEBUG, "nl80211: Passive scan requested");
+ 	}
+ 
+-	if (params->extra_ies) {
++	if (params->extra_ies && drv->capa.max_scan_ie_len >= params->extra_ies_len) {
+ 		wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
+ 			    params->extra_ies, params->extra_ies_len);
+ 		if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
+diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c
+index e95df6ddb..9071da3cf 100644
+--- a/src/drivers/drivers.c
++++ b/src/drivers/drivers.c
+@@ -10,6 +10,10 @@
+ #include "utils/common.h"
+ #include "driver.h"
+ 
++void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
++			     union wpa_event_data *data);
++void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
++			     union wpa_event_data *data);
+ 
+ const struct wpa_driver_ops *const wpa_drivers[] =
+ {
+diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
+index a03d4a034..8da44d9f5 100644
+--- a/src/drivers/drivers.mak
++++ b/src/drivers/drivers.mak
+@@ -54,7 +54,6 @@ NEED_SME=y
+ NEED_AP_MLME=y
+ NEED_NETLINK=y
+ NEED_LINUX_IOCTL=y
+-NEED_RFKILL=y
+ NEED_RADIOTAP=y
+ NEED_LIBNL=y
+ endif
+@@ -111,7 +110,6 @@ DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
+ CONFIG_WIRELESS_EXTENSION=y
+ NEED_NETLINK=y
+ NEED_LINUX_IOCTL=y
+-NEED_RFKILL=y
+ endif
+ 
+ ifdef CONFIG_DRIVER_NDIS
+@@ -137,7 +135,6 @@ endif
+ ifdef CONFIG_WIRELESS_EXTENSION
+ DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
+ DRV_WPA_OBJS += ../src/drivers/driver_wext.o
+-NEED_RFKILL=y
+ endif
+ 
+ ifdef NEED_NETLINK
+@@ -146,6 +143,7 @@ endif
+ 
+ ifdef NEED_RFKILL
+ DRV_OBJS += ../src/drivers/rfkill.o
++DRV_WPA_CFLAGS += -DCONFIG_RFKILL
+ endif
+ 
+ ifdef NEED_RADIOTAP
+diff --git a/src/drivers/rfkill.h b/src/drivers/rfkill.h
+index 0412ac330..e27565375 100644
+--- a/src/drivers/rfkill.h
++++ b/src/drivers/rfkill.h
+@@ -18,8 +18,24 @@ struct rfkill_config {
+ 	void (*unblocked_cb)(void *ctx);
+ };
+ 
++#ifdef CONFIG_RFKILL
+ struct rfkill_data * rfkill_init(struct rfkill_config *cfg);
+ void rfkill_deinit(struct rfkill_data *rfkill);
+ int rfkill_is_blocked(struct rfkill_data *rfkill);
++#else
++static inline struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
++{
++	return (void *) 1;
++}
++
++static inline void rfkill_deinit(struct rfkill_data *rfkill)
++{
++}
++
++static inline int rfkill_is_blocked(struct rfkill_data *rfkill)
++{
++	return 0;
++}
++#endif
+ 
+ #endif /* RFKILL_H */
+diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
+index 2a7f36170..8e8903051 100644
+--- a/src/radius/radius_client.c
++++ b/src/radius/radius_client.c
+@@ -165,6 +165,8 @@ struct radius_client_data {
+ 	 */
+ 	void *ctx;
+ 
++	struct hostapd_ip_addr local_ip;
++
+ 	/**
+ 	 * conf - RADIUS client configuration (list of RADIUS servers to use)
+ 	 */
+@@ -818,6 +820,30 @@ static void radius_close_acct_socket(struct radius_client_data *radius)
+ }
+ 
+ 
++/**
++ * radius_client_send - Get local address for the RADIUS auth socket
++ * @radius: RADIUS client context from radius_client_init()
++ * @addr: pointer to store the address
++ *
++ * This function returns the local address for the connection to the RADIUS
++ * auth server. It also opens the socket if it's not available yet.
++ */
++int radius_client_get_local_addr(struct radius_client_data *radius,
++				 struct hostapd_ip_addr *addr)
++{
++	struct hostapd_radius_servers *conf = radius->conf;
++
++	if (conf->auth_server && radius->auth_sock < 0)
++		radius_client_init_auth(radius);
++
++	if (radius->auth_sock < 0)
++		return -1;
++
++	memcpy(addr, &radius->local_ip, sizeof(*addr));
++
++	return 0;
++}
++
+ /**
+  * radius_client_send - Send a RADIUS request
+  * @radius: RADIUS client context from radius_client_init()
+@@ -1711,6 +1737,10 @@ radius_change_server(struct radius_client_data *radius,
+ 			wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
+ 				   inet_ntoa(claddr.sin_addr),
+ 				   ntohs(claddr.sin_port));
++			if (auth) {
++				radius->local_ip.af = AF_INET;
++				radius->local_ip.u.v4 = claddr.sin_addr;
++			}
+ 		}
+ 		break;
+ #ifdef CONFIG_IPV6
+@@ -1722,6 +1752,10 @@ radius_change_server(struct radius_client_data *radius,
+ 				   inet_ntop(AF_INET6, &claddr6.sin6_addr,
+ 					     abuf, sizeof(abuf)),
+ 				   ntohs(claddr6.sin6_port));
++			if (auth) {
++				radius->local_ip.af = AF_INET6;
++				radius->local_ip.u.v6 = claddr6.sin6_addr;
++			}
+ 		}
+ 		break;
+ 	}
+diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h
+index db40637ea..9a89b0382 100644
+--- a/src/radius/radius_client.h
++++ b/src/radius/radius_client.h
+@@ -274,6 +274,8 @@ int radius_client_register(struct radius_client_data *radius,
+ void radius_client_set_interim_error_cb(struct radius_client_data *radius,
+ 					void (*cb)(const u8 *addr, void *ctx),
+ 					void *ctx);
++int radius_client_get_local_addr(struct radius_client_data *radius,
++				 struct hostapd_ip_addr * addr);
+ int radius_client_send(struct radius_client_data *radius,
+ 		       struct radius_msg *msg,
+ 		       RadiusType msg_type, const u8 *addr);
+diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
+index 8d7c9b4c4..01913b08f 100644
+--- a/src/radius/radius_das.c
++++ b/src/radius/radius_das.c
+@@ -12,13 +12,26 @@
+ #include "utils/common.h"
+ #include "utils/eloop.h"
+ #include "utils/ip_addr.h"
++#include "utils/list.h"
+ #include "radius.h"
+ #include "radius_das.h"
+ 
+ 
+-struct radius_das_data {
++static struct dl_list das_ports = DL_LIST_HEAD_INIT(das_ports);
++
++struct radius_das_port {
++	struct dl_list list;
++	struct dl_list das_data;
++
++	int port;
+ 	int sock;
++};
++
++struct radius_das_data {
++	struct dl_list list;
++	struct radius_das_port *port;
+ 	u8 *shared_secret;
++	u8 *nas_identifier;
+ 	size_t shared_secret_len;
+ 	struct hostapd_ip_addr client_addr;
+ 	unsigned int time_window;
+@@ -388,56 +401,17 @@ fail:
+ }
+ 
+ 
+-static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
++static void
++radius_das_receive_msg(struct radius_das_data *das, struct radius_msg *msg,
++		       struct sockaddr *from, socklen_t fromlen,
++		       char *abuf, int from_port)
+ {
+-	struct radius_das_data *das = eloop_ctx;
+-	u8 buf[1500];
+-	union {
+-		struct sockaddr_storage ss;
+-		struct sockaddr_in sin;
+-#ifdef CONFIG_IPV6
+-		struct sockaddr_in6 sin6;
+-#endif /* CONFIG_IPV6 */
+-	} from;
+-	char abuf[50];
+-	int from_port = 0;
+-	socklen_t fromlen;
+-	int len;
+-	struct radius_msg *msg, *reply = NULL;
++	struct radius_msg *reply = NULL;
+ 	struct radius_hdr *hdr;
+ 	struct wpabuf *rbuf;
++	struct os_time now;
+ 	u32 val;
+ 	int res;
+-	struct os_time now;
+-
+-	fromlen = sizeof(from);
+-	len = recvfrom(sock, buf, sizeof(buf), 0,
+-		       (struct sockaddr *) &from.ss, &fromlen);
+-	if (len < 0) {
+-		wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
+-		return;
+-	}
+-
+-	os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
+-	from_port = ntohs(from.sin.sin_port);
+-
+-	wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
+-		   len, abuf, from_port);
+-	if (das->client_addr.u.v4.s_addr &&
+-	    das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
+-		wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
+-		return;
+-	}
+-
+-	msg = radius_msg_parse(buf, len);
+-	if (msg == NULL) {
+-		wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
+-			   "from %s:%d failed", abuf, from_port);
+-		return;
+-	}
+-
+-	if (wpa_debug_level <= MSG_MSGDUMP)
+-		radius_msg_dump(msg);
+ 
+ 	if (radius_msg_verify_das_req(msg, das->shared_secret,
+ 				       das->shared_secret_len,
+@@ -504,9 +478,8 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
+ 			radius_msg_dump(reply);
+ 
+ 		rbuf = radius_msg_get_buf(reply);
+-		res = sendto(das->sock, wpabuf_head(rbuf),
+-			     wpabuf_len(rbuf), 0,
+-			     (struct sockaddr *) &from.ss, fromlen);
++		res = sendto(das->port->sock, wpabuf_head(rbuf),
++			     wpabuf_len(rbuf), 0, from, fromlen);
+ 		if (res < 0) {
+ 			wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
+ 				   abuf, from_port, strerror(errno));
+@@ -518,6 +491,72 @@ fail:
+ 	radius_msg_free(reply);
+ }
+ 
++static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
++{
++	struct radius_das_port *p = eloop_ctx;
++	struct radius_das_data *das;
++	u8 buf[1500];
++	union {
++		struct sockaddr_storage ss;
++		struct sockaddr_in sin;
++#ifdef CONFIG_IPV6
++		struct sockaddr_in6 sin6;
++#endif /* CONFIG_IPV6 */
++	} from;
++	struct radius_msg *msg;
++	size_t nasid_len = 0;
++	u8 *nasid_buf = NULL;
++	char abuf[50];
++	int from_port = 0;
++	socklen_t fromlen;
++	int found = 0;
++	int len;
++
++	fromlen = sizeof(from);
++	len = recvfrom(sock, buf, sizeof(buf), 0,
++		       (struct sockaddr *) &from.ss, &fromlen);
++	if (len < 0) {
++		wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
++		return;
++	}
++
++	os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
++	from_port = ntohs(from.sin.sin_port);
++
++	msg = radius_msg_parse(buf, len);
++	if (msg == NULL) {
++		wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
++			   "from %s:%d failed", abuf, from_port);
++		return;
++	}
++
++	wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
++		   len, abuf, from_port);
++
++	if (wpa_debug_level <= MSG_MSGDUMP)
++		radius_msg_dump(msg);
++
++	radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
++				&nasid_buf, &nasid_len, NULL);
++	dl_list_for_each(das, &p->das_data, struct radius_das_data, list) {
++		if (das->client_addr.u.v4.s_addr &&
++		    das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr)
++			continue;
++
++		if (das->nas_identifier && nasid_buf &&
++		    (nasid_len != os_strlen(das->nas_identifier) ||
++		     os_memcmp(das->nas_identifier, nasid_buf, nasid_len) != 0))
++			continue;
++
++		found = 1;
++		radius_das_receive_msg(das, msg, (struct sockaddr *)&from.ss,
++				       fromlen, abuf, from_port);
++	}
++
++	if (!found)
++		wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
++}
++
+ 
+ static int radius_das_open_socket(int port)
+ {
+@@ -543,6 +582,49 @@ static int radius_das_open_socket(int port)
+ }
+ 
+ 
++static struct radius_das_port *
++radius_das_open_port(int port)
++{
++	struct radius_das_port *p;
++
++	dl_list_for_each(p, &das_ports, struct radius_das_port, list) {
++		if (p->port == port)
++			return p;
++	}
++
++	p = os_zalloc(sizeof(*p));
++	if (p == NULL)
++		return NULL;
++
++	dl_list_init(&p->das_data);
++	p->port = port;
++	p->sock = radius_das_open_socket(port);
++	if (p->sock < 0)
++		goto free_port;
++
++	if (eloop_register_read_sock(p->sock, radius_das_receive, p, NULL))
++		goto close_port;
++
++	dl_list_add(&das_ports, &p->list);
++
++	return p;
++
++close_port:
++	close(p->sock);
++free_port:
++	os_free(p);
++
++	return NULL;
++}
++
++static void radius_das_close_port(struct radius_das_port *p)
++{
++	dl_list_del(&p->list);
++	eloop_unregister_read_sock(p->sock);
++	close(p->sock);
++	free(p);
++}
++
+ struct radius_das_data *
+ radius_das_init(struct radius_das_conf *conf)
+ {
+@@ -563,6 +645,8 @@ radius_das_init(struct radius_das_conf *conf)
+ 	das->ctx = conf->ctx;
+ 	das->disconnect = conf->disconnect;
+ 	das->coa = conf->coa;
++	if (conf->nas_identifier)
++		das->nas_identifier = os_strdup(conf->nas_identifier);
+ 
+ 	os_memcpy(&das->client_addr, conf->client_addr,
+ 		  sizeof(das->client_addr));
+@@ -575,19 +659,15 @@ radius_das_init(struct radius_das_conf *conf)
+ 	}
+ 	das->shared_secret_len = conf->shared_secret_len;
+ 
+-	das->sock = radius_das_open_socket(conf->port);
+-	if (das->sock < 0) {
++	das->port = radius_das_open_port(conf->port);
++	if (!das->port) {
+ 		wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
+ 			   "DAS");
+ 		radius_das_deinit(das);
+ 		return NULL;
+ 	}
+ 
+-	if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
+-	{
+-		radius_das_deinit(das);
+-		return NULL;
+-	}
++	dl_list_add(&das->port->das_data, &das->list);
+ 
+ 	return das;
+ }
+@@ -598,11 +678,14 @@ void radius_das_deinit(struct radius_das_data *das)
+ 	if (das == NULL)
+ 		return;
+ 
+-	if (das->sock >= 0) {
+-		eloop_unregister_read_sock(das->sock);
+-		close(das->sock);
++	if (das->port) {
++		dl_list_del(&das->list);
++
++		if (dl_list_empty(&das->port->das_data))
++			radius_das_close_port(das->port);
+ 	}
+ 
++	os_free(das->nas_identifier);
+ 	os_free(das->shared_secret);
+ 	os_free(das);
+ }
+diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h
+index 233d662f6..80dc13fc8 100644
+--- a/src/radius/radius_das.h
++++ b/src/radius/radius_das.h
+@@ -44,6 +44,7 @@ struct radius_das_attrs {
+ struct radius_das_conf {
+ 	int port;
+ 	const u8 *shared_secret;
++	const u8 *nas_identifier;
+ 	size_t shared_secret_len;
+ 	const struct hostapd_ip_addr *client_addr;
+ 	unsigned int time_window;
+diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
+index fa3691548..95a1cb994 100644
+--- 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(struct radius_server_data *data,
+ 	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_server_data *data, const char *keyname)
+ }
+ #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 radius_server_data *data,
+ 		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);
+ 
+@@ -1123,11 +1160,10 @@ radius_server_encapsulate_eap(struct radius_server_data *data,
+ 	}
+ 
+ 	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;
+@@ -1221,11 +1257,10 @@ radius_server_macacl(struct radius_server_data *data,
+ 	}
+ 
+ 	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;
+@@ -2527,7 +2562,7 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity,
+ 	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/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
+index 52a4c7442..ce1aa60a9 100644
+--- a/src/rsn_supp/wpa.c
++++ b/src/rsn_supp/wpa.c
+@@ -4153,6 +4153,8 @@ static u32 wpa_key_mgmt_suite(struct wpa_sm *sm)
+ }
+ 
+ 
++#ifdef CONFIG_CTRL_IFACE_MIB
++
+ #define RSN_SUITE "%02x-%02x-%02x-%d"
+ #define RSN_SUITE_ARG(s) \
+ ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
+@@ -4234,6 +4236,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
+ 
+ 	return (int) len;
+ }
++#endif
+ #endif /* CONFIG_CTRL_IFACE */
+ 
+ 
+diff --git a/src/tls/Makefile b/src/tls/Makefile
+index c84fbe859..e974a41f0 100644
+--- a/src/tls/Makefile
++++ b/src/tls/Makefile
+@@ -1,3 +1,10 @@
++LIB_OBJS= asn1.o
++
++ifneq ($(CONFIG_TLS),gnutls)
++ifneq ($(CONFIG_TLS),mbedtls)
++ifneq ($(CONFIG_TLS),openssl)
++ifneq ($(CONFIG_TLS),wolfssl)
++
+ CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+ CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+ CFLAGS += -DCONFIG_TLSV11
+@@ -21,5 +28,9 @@ LIB_OBJS= \
+ 	tlsv1_server_read.o \
+ 	tlsv1_server_write.o \
+ 	x509v3.o
++endif
++endif
++endif
++endif
+ 
+ include ../lib.rules
+diff --git a/src/utils/eloop.c b/src/utils/eloop.c
+index 00b0beff0..50dd1beda 100644
+--- 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)
+ {
+diff --git a/src/utils/eloop.h b/src/utils/eloop.h
+index 04ee6d183..5452ea589 100644
+--- a/src/utils/eloop.h
++++ b/src/utils/eloop.h
+@@ -65,6 +65,9 @@ typedef void (*eloop_timeout_handler)(void *eloop_ctx, void *user_ctx);
+  */
+ 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 sig, void *signal_ctx);
+  */
+ 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_signal_handler handler,
+  */
+ int eloop_sock_requeue(void);
+ 
++void eloop_add_uloop(void);
++
+ /**
+  * eloop_run - Start the event loop
+  *
+diff --git a/src/utils/uloop.c b/src/utils/uloop.c
+new file mode 100644
+index 000000000..c0d26db93
+--- /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/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
+index 7f3dd185f..627575e39 100644
+--- a/src/utils/wpa_debug.c
++++ b/src/utils/wpa_debug.c
+@@ -26,6 +26,10 @@ static FILE *wpa_debug_tracing_file = NULL;
+ #define WPAS_TRACE_PFX "wpas <%d>: "
+ #endif /* CONFIG_DEBUG_LINUX_TRACING */
+ 
++void (*wpa_printf_hook)(int level, const char *fmt, va_list ap);
++void (*wpa_hexdump_hook)(int level, const char *title, const void *buf,
++			 size_t len);
++void (*wpa_netlink_hook)(int tx, const void *data, size_t len);
+ 
+ int wpa_debug_level = MSG_INFO;
+ int wpa_debug_show_keys = 0;
+@@ -206,10 +210,16 @@ void wpa_debug_close_linux_tracing(void)
+  *
+  * Note: New line '\n' is added to the end of the text when printing to stdout.
+  */
+-void wpa_printf(int level, const char *fmt, ...)
++void _wpa_printf(int level, const char *fmt, ...)
+ {
+ 	va_list ap;
+ 
++	if (wpa_printf_hook) {
++		va_start(ap, fmt);
++		wpa_printf_hook(level, fmt, ap);
++		va_end(ap);
++	}
++
+ 	if (level >= wpa_debug_level) {
+ #ifdef CONFIG_ANDROID_LOG
+ 		va_start(ap, fmt);
+@@ -255,11 +265,14 @@ void wpa_printf(int level, const char *fmt, ...)
+ }
+ 
+ 
+-static void _wpa_hexdump(int level, const char *title, const u8 *buf,
++void _wpa_hexdump(int level, const char *title, const u8 *buf,
+ 			 size_t len, int show, int only_syslog)
+ {
+ 	size_t i;
+ 
++	if (wpa_hexdump_hook)
++		wpa_hexdump_hook(level, title, buf, len);
++
+ #ifdef CONFIG_DEBUG_LINUX_TRACING
+ 	if (wpa_debug_tracing_file != NULL) {
+ 		fprintf(wpa_debug_tracing_file,
+@@ -382,19 +395,7 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf,
+ #endif /* CONFIG_ANDROID_LOG */
+ }
+ 
+-void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
+-{
+-	_wpa_hexdump(level, title, buf, len, 1, 0);
+-}
+-
+-
+-void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len)
+-{
+-	_wpa_hexdump(level, title, buf, len, wpa_debug_show_keys, 0);
+-}
+-
+-
+-static void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
++void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
+ 			       size_t len, int show)
+ {
+ 	size_t i, llen;
+@@ -507,20 +508,6 @@ file_done:
+ }
+ 
+ 
+-void wpa_hexdump_ascii(int level, const char *title, const void *buf,
+-		       size_t len)
+-{
+-	_wpa_hexdump_ascii(level, title, buf, len, 1);
+-}
+-
+-
+-void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
+-			   size_t len)
+-{
+-	_wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
+-}
+-
+-
+ #ifdef CONFIG_DEBUG_FILE
+ static char *last_path = NULL;
+ #endif /* CONFIG_DEBUG_FILE */
+@@ -644,7 +631,7 @@ void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func)
+ }
+ 
+ 
+-void wpa_msg(void *ctx, int level, const char *fmt, ...)
++void _wpa_msg(void *ctx, int level, const char *fmt, ...)
+ {
+ 	va_list ap;
+ 	char *buf;
+@@ -682,7 +669,7 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...)
+ }
+ 
+ 
+-void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
++void _wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
+ {
+ 	va_list ap;
+ 	char *buf;
+diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
+index 4c02ad3c7..854520bfe 100644
+--- a/src/utils/wpa_debug.h
++++ b/src/utils/wpa_debug.h
+@@ -11,6 +11,10 @@
+ 
+ #include "wpabuf.h"
+ 
++extern void (*wpa_printf_hook)(int level, const char *fmt, va_list ap);
++extern void (*wpa_hexdump_hook)(int level, const char *title,
++				const void *buf, size_t len);
++extern void (*wpa_netlink_hook)(int tx, const void *data, size_t len);
+ extern int wpa_debug_level;
+ extern int wpa_debug_show_keys;
+ extern int wpa_debug_timestamp;
+@@ -51,6 +55,17 @@ void wpa_debug_close_file(void);
+ void wpa_debug_setup_stdout(void);
+ void wpa_debug_stop_log(void);
+ 
++/* internal */
++void _wpa_hexdump(int level, const char *title, const u8 *buf,
++		  size_t len, int show, int only_syslog);
++void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
++			size_t len, int show);
++extern int wpa_debug_show_keys;
++
++#ifndef CONFIG_MSG_MIN_PRIORITY
++#define CONFIG_MSG_MIN_PRIORITY 0
++#endif
++
+ /**
+  * wpa_debug_printf_timestamp - Print timestamp for debug output
+  *
+@@ -71,9 +86,15 @@ void wpa_debug_print_timestamp(void);
+  *
+  * Note: New line '\n' is added to the end of the text when printing to stdout.
+  */
+-void wpa_printf(int level, const char *fmt, ...)
++void _wpa_printf(int level, const char *fmt, ...)
+ PRINTF_FORMAT(2, 3);
+ 
++#define wpa_printf(level, ...)						\
++	do {								\
++		if (level >= CONFIG_MSG_MIN_PRIORITY)			\
++			_wpa_printf(level, __VA_ARGS__);		\
++	} while(0)
++
+ /**
+  * wpa_hexdump - conditional hex dump
+  * @level: priority level (MSG_*) of the message
+@@ -85,7 +106,13 @@ PRINTF_FORMAT(2, 3);
+  * output may be directed to stdout, stderr, and/or syslog based on
+  * configuration. The contents of buf is printed out has hex dump.
+  */
+-void wpa_hexdump(int level, const char *title, const void *buf, size_t len);
++static inline void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
++{
++	if (level < CONFIG_MSG_MIN_PRIORITY)
++		return;
++
++	_wpa_hexdump(level, title, buf, len, 1, 1);
++}
+ 
+ static inline void wpa_hexdump_buf(int level, const char *title,
+ 				   const struct wpabuf *buf)
+@@ -107,7 +134,13 @@ static inline void wpa_hexdump_buf(int level, const char *title,
+  * like wpa_hexdump(), but by default, does not include secret keys (passwords,
+  * etc.) in debug output.
+  */
+-void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len);
++static inline void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len)
++{
++	if (level < CONFIG_MSG_MIN_PRIORITY)
++		return;
++
++	_wpa_hexdump(level, title, buf, len, wpa_debug_show_keys, 1);
++}
+ 
+ static inline void wpa_hexdump_buf_key(int level, const char *title,
+ 				       const struct wpabuf *buf)
+@@ -129,8 +162,14 @@ static inline void wpa_hexdump_buf_key(int level, const char *title,
+  * the hex numbers and ASCII characters (for printable range) are shown. 16
+  * bytes per line will be shown.
+  */
+-void wpa_hexdump_ascii(int level, const char *title, const void *buf,
+-		       size_t len);
++static inline void wpa_hexdump_ascii(int level, const char *title,
++				     const u8 *buf, size_t len)
++{
++	if (level < CONFIG_MSG_MIN_PRIORITY)
++		return;
++
++	_wpa_hexdump_ascii(level, title, buf, len, 1);
++}
+ 
+ /**
+  * wpa_hexdump_ascii_key - conditional hex dump, hide keys
+@@ -146,8 +185,14 @@ void wpa_hexdump_ascii(int level, const char *title, const void *buf,
+  * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by
+  * default, does not include secret keys (passwords, etc.) in debug output.
+  */
+-void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
+-			   size_t len);
++static inline void wpa_hexdump_ascii_key(int level, const char *title,
++					 const u8 *buf, size_t len)
++{
++	if (level < CONFIG_MSG_MIN_PRIORITY)
++		return;
++
++	_wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
++}
+ 
+ /*
+  * wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce
+@@ -184,7 +229,12 @@ void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
+  *
+  * Note: New line '\n' is added to the end of the text when printing to stdout.
+  */
+-void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
++void _wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
++#define wpa_msg(ctx, level, ...)					\
++	do {								\
++		if (level >= CONFIG_MSG_MIN_PRIORITY)			\
++			_wpa_msg(ctx, level, __VA_ARGS__);		\
++	} while(0)
+ 
+ /**
+  * wpa_msg_ctrl - Conditional printf for ctrl_iface monitors
+@@ -198,8 +248,13 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
+  * attached ctrl_iface monitors. In other words, it can be used for frequent
+  * events that do not need to be sent to syslog.
+  */
+-void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
++void _wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
+ PRINTF_FORMAT(3, 4);
++#define wpa_msg_ctrl(ctx, level, ...)					\
++	do {								\
++		if (level >= CONFIG_MSG_MIN_PRIORITY)			\
++			_wpa_msg_ctrl(ctx, level, __VA_ARGS__);		\
++	} while(0)
+ 
+ /**
+  * wpa_msg_global - Global printf for ctrl_iface monitors
+diff --git a/tests/Makefile b/tests/Makefile
+index 8ec154bb3..25fdf9e00 100644
+--- a/tests/Makefile
++++ b/tests/Makefile
+@@ -1,10 +1,12 @@
+-ALL=test-base64 test-md4 test-milenage \
+-	test-rsa-sig-ver \
+-	test-sha1 \
+-	test-https test-https_server \
+-	test-sha256 test-aes test-x509v3 test-list test-rc4 \
++RUN_TESTS= \
++	test-list \
++	test-md4 test-rc4 test-sha1 test-sha256 \
++	test-milenage test-aes \
++	test-crypto_module \
+ 	test-bss
+ 
++ALL=$(RUN_TESTS) test-base64 test-https test-https_server
++
+ include ../src/build.rules
+ 
+ ifdef LIBFUZZER
+@@ -25,13 +27,27 @@ CFLAGS += -DCONFIG_IEEE80211R_AP
+ CFLAGS += -DCONFIG_IEEE80211R
+ CFLAGS += -DCONFIG_TDLS
+ 
++# test-crypto_module
++CFLAGS += -DCONFIG_MODULE_TESTS
++CFLAGS += -DCONFIG_DPP
++#CFLAGS += -DCONFIG_DPP2
++#CFLAGS += -DCONFIG_DPP3
++CFLAGS += -DCONFIG_ECC
++CFLAGS += -DCONFIG_HMAC_SHA256_KDF
++CFLAGS += -DCONFIG_HMAC_SHA384_KDF
++CFLAGS += -DCONFIG_MESH
++CFLAGS += -DCONFIG_SHA256
++CFLAGS += -DCONFIG_SHA384
++CFLAGS += -DEAP_PSK
++CFLAGS += -DEAP_FAST
++
+ CFLAGS += -I../src
+ CFLAGS += -I../src/utils
+ 
+ SLIBS = ../src/utils/libutils.a
+ 
+-DLIBS = ../src/crypto/libcrypto.a \
+-	../src/tls/libtls.a
++DLIBS = ../src/tls/libtls.a \
++	../src/crypto/libcrypto.a
+ 
+ _OBJS_VAR := LLIBS
+ include ../src/objs.mk
+@@ -43,12 +59,43 @@ include ../src/objs.mk
+ LIBS = $(SLIBS) $(DLIBS)
+ LLIBS = -Wl,--start-group $(DLIBS) -Wl,--end-group $(SLIBS)
+ 
++ifeq ($(CONFIG_TLS),mbedtls)
++CFLAGS += -DCONFIG_TLS_MBEDTLS
++LLIBS += -lmbedtls -lmbedx509 -lmbedcrypto
++else
++ifeq ($(CONFIG_TLS),openssl)
++CFLAGS += -DCONFIG_TLS_OPENSSL
++LLIBS += -lssl -lcrypto
++else
++ifeq ($(CONFIG_TLS),gnutls)
++CFLAGS += -DCONFIG_TLS_GNUTLS
++LLIBS += -lgnutls -lgpg-error -lgcrypt
++else
++ifeq ($(CONFIG_TLS),wolfssl)
++CFLAGS += -DCONFIG_TLS_WOLFSSL
++LLIBS += -lwolfssl -lm
++else
++CFLAGS += -DCONFIG_TLS_INTERNAL
++CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
++ALL += test-rsa-sig-ver
++ALL += test-x509v3
++clean-config_tls_internal:
++	rm -f test_x509v3_nist.out.*
++	rm -f test_x509v3_nist2.out.*
++endif
++endif
++endif
++endif
++
+ # glibc < 2.17 needs -lrt for clock_gettime()
+ LLIBS += -lrt
+ 
+ test-aes: $(call BUILDOBJ,test-aes.o) $(LIBS)
+ 	$(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+ 
++test-crypto_module: $(call BUILDOBJ,test-crypto_module.o) $(LIBS)
++	$(LDO) $(LDFLAGS) -o $@ $< $(LLIBS)
++
+ test-base64: $(call BUILDOBJ,test-base64.o) $(LIBS)
+ 	$(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+ 
+@@ -141,18 +188,11 @@ test-bss: $(call BUILDOBJ,test-bss.o) $(WPA_OBJS) $(LIBS)
+ 	$(LDO) $(LDFLAGS) -o $@ $< $(LLIBS) $(WPA_CFLAGS) $(WPA_OBJS) $(LIBS)
+ 
+ run-tests: $(ALL)
+-	./test-aes
+-	./test-list
+-	./test-md4
+-	./test-milenage
+-	./test-rsa-sig-ver
+-	./test-sha1
+-	./test-sha256
+-	./test-bss
++	@set -ex; for i in $(RUN_TESTS); do ./$$i; done
+ 	@echo
+ 	@echo All tests completed successfully.
+ 
+-clean: common-clean
++clean: common-clean clean-config_tls_internal
+ 	rm -f *~
+-	rm -f test_x509v3_nist.out.*
+-	rm -f test_x509v3_nist2.out.*
++
++.PHONY: run-tests clean-config_tls_internal
+diff --git a/tests/hwsim/example-hostapd.config b/tests/hwsim/example-hostapd.config
+index 210b7fb86..5f326da07 100644
+--- a/tests/hwsim/example-hostapd.config
++++ b/tests/hwsim/example-hostapd.config
+@@ -4,6 +4,7 @@ CONFIG_DRIVER_NONE=y
+ CONFIG_DRIVER_NL80211=y
+ CONFIG_RSN_PREAUTH=y
+ 
++#CONFIG_TLS=mbedtls
+ #CONFIG_TLS=internal
+ #CONFIG_INTERNAL_LIBTOMMATH=y
+ #CONFIG_INTERNAL_LIBTOMMATH_FAST=y
+@@ -33,12 +34,7 @@ CONFIG_EAP_TNC=y
+ CFLAGS += -DTNC_CONFIG_FILE=\"tnc/tnc_config\"
+ LIBS += -rdynamic
+ CONFIG_EAP_UNAUTH_TLS=y
+-ifeq ($(CONFIG_TLS), openssl)
+-CONFIG_EAP_PWD=y
+-endif
+-ifeq ($(CONFIG_TLS), wolfssl)
+-CONFIG_EAP_PWD=y
+-endif
++CONFIG_EAP_PWD=$(if $(filter openssl wolfssl mbedtls,$(CONFIG_TLS)),y,)
+ CONFIG_EAP_EKE=y
+ CONFIG_PKCS12=y
+ CONFIG_RADIUS_SERVER=y
+diff --git a/tests/hwsim/example-wpa_supplicant.config b/tests/hwsim/example-wpa_supplicant.config
+index 123f397e3..c69b1f9cd 100644
+--- a/tests/hwsim/example-wpa_supplicant.config
++++ b/tests/hwsim/example-wpa_supplicant.config
+@@ -2,6 +2,7 @@
+ 
+ CONFIG_TLS=openssl
+ #CONFIG_TLS=wolfssl
++#CONFIG_TLS=mbedtls
+ #CONFIG_TLS=internal
+ #CONFIG_INTERNAL_LIBTOMMATH=y
+ #CONFIG_INTERNAL_LIBTOMMATH_FAST=y
+@@ -34,13 +35,7 @@ LIBS += -rdynamic
+ CONFIG_EAP_FAST=y
+ CONFIG_EAP_TEAP=y
+ CONFIG_EAP_IKEV2=y
+-
+-ifeq ($(CONFIG_TLS), openssl)
+-CONFIG_EAP_PWD=y
+-endif
+-ifeq ($(CONFIG_TLS), wolfssl)
+-CONFIG_EAP_PWD=y
+-endif
++CONFIG_EAP_PWD=$(if $(filter openssl wolfssl mbedtls,$(CONFIG_TLS)),y,)
+ 
+ CONFIG_USIM_SIMULATOR=y
+ CONFIG_SIM_SIMULATOR=y
+diff --git a/tests/hwsim/test_ap_eap.py b/tests/hwsim/test_ap_eap.py
+index f8e75b5fb..48e4dedcc 100644
+--- a/tests/hwsim/test_ap_eap.py
++++ b/tests/hwsim/test_ap_eap.py
+@@ -42,20 +42,42 @@ def check_eap_capa(dev, method):
+     res = dev.get_capability("eap")
+     if method not in res:
+         raise HwsimSkip("EAP method %s not supported in the build" % method)
++    if method == "FAST" or method == "TEAP":
++        tls = dev.request("GET tls_library")
++        if tls.startswith("mbed TLS"):
++            raise HwsimSkip("EAP-%s not supported with this TLS library: " % method + tls)
+ 
+ def check_subject_match_support(dev):
+     tls = dev.request("GET tls_library")
+-    if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
++    if tls.startswith("OpenSSL"):
++        return
++    elif tls.startswith("wolfSSL"):
++        return
++    elif tls.startswith("mbed TLS"):
++        return
++    else:
+         raise HwsimSkip("subject_match not supported with this TLS library: " + tls)
+ 
+ def check_check_cert_subject_support(dev):
+     tls = dev.request("GET tls_library")
+-    if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
++    if tls.startswith("OpenSSL"):
++        return
++    elif tls.startswith("wolfSSL"):
++        return
++    elif tls.startswith("mbed TLS"):
++        return
++    else:
+         raise HwsimSkip("check_cert_subject not supported with this TLS library: " + tls)
+ 
+ def check_altsubject_match_support(dev):
+     tls = dev.request("GET tls_library")
+-    if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
++    if tls.startswith("OpenSSL"):
++        return
++    elif tls.startswith("wolfSSL"):
++        return
++    elif tls.startswith("mbed TLS"):
++        return
++    else:
+         raise HwsimSkip("altsubject_match not supported with this TLS library: " + tls)
+ 
+ def check_domain_match(dev):
+@@ -70,7 +92,13 @@ def check_domain_suffix_match(dev):
+ 
+ def check_domain_match_full(dev):
+     tls = dev.request("GET tls_library")
+-    if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
++    if tls.startswith("OpenSSL"):
++        return
++    elif tls.startswith("wolfSSL"):
++        return
++    elif tls.startswith("mbed TLS"):
++        return
++    else:
+         raise HwsimSkip("domain_suffix_match requires full match with this TLS library: " + tls)
+ 
+ def check_cert_probe_support(dev):
+@@ -79,8 +107,15 @@ def check_cert_probe_support(dev):
+         raise HwsimSkip("Certificate probing not supported with this TLS library: " + tls)
+ 
+ def check_ext_cert_check_support(dev):
++    if not openssl_imported:
++        raise HwsimSkip("OpenSSL python method not available")
++
+     tls = dev.request("GET tls_library")
+-    if not tls.startswith("OpenSSL"):
++    if tls.startswith("OpenSSL"):
++        return
++    elif tls.startswith("mbed TLS"):
++        return
++    else:
+         raise HwsimSkip("ext_cert_check not supported with this TLS library: " + tls)
+ 
+ def check_ocsp_support(dev):
+@@ -91,14 +126,18 @@ def check_ocsp_support(dev):
+     #    raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
+     #if tls.startswith("wolfSSL"):
+     #    raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
++    if tls.startswith("mbed TLS"):
++        raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
+ 
+ def check_pkcs5_v15_support(dev):
+     tls = dev.request("GET tls_library")
+-    if "BoringSSL" in tls or "GnuTLS" in tls:
++    if "BoringSSL" in tls or "GnuTLS" in tls or "mbed TLS" in tls:
+         raise HwsimSkip("PKCS#5 v1.5 not supported with this TLS library: " + tls)
+ 
+ def check_tls13_support(dev):
+     tls = dev.request("GET tls_library")
++    if tls.startswith("mbed TLS"):
++        raise HwsimSkip("TLS v1.3 not supported")
+     ok = ['run=OpenSSL 1.1.1', 'run=OpenSSL 3.0', 'run=OpenSSL 3.1',
+           'run=OpenSSL 3.2', 'run=OpenSSL 3.3', 'wolfSSL']
+     for s in ok:
+@@ -122,11 +161,15 @@ def check_pkcs12_support(dev):
+     #    raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
+     if tls.startswith("wolfSSL"):
+         raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
++    if tls.startswith("mbed TLS"):
++        raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
+ 
+ def check_dh_dsa_support(dev):
+     tls = dev.request("GET tls_library")
+     if tls.startswith("internal"):
+         raise HwsimSkip("DH DSA not supported with this TLS library: " + tls)
++    if tls.startswith("mbed TLS"):
++        raise HwsimSkip("DH DSA not supported with this TLS library: " + tls)
+ 
+ def check_ec_support(dev):
+     tls = dev.request("GET tls_library")
+@@ -1741,7 +1784,7 @@ def test_ap_wpa2_eap_ttls_pap_subject_match(dev, apdev):
+     eap_connect(dev[0], hapd, "TTLS", "pap user",
+                 anonymous_identity="ttls", password="password",
+                 ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+-                subject_match="/C=FI/O=w1.fi/CN=server.w1.fi",
++                check_cert_subject="/C=FI/O=w1.fi/CN=server.w1.fi",
+                 altsubject_match="EMAIL:noone@example.com;DNS:server.w1.fi;URI:http://example.com/")
+     eap_reauth(dev[0], "TTLS")
+ 
+@@ -2976,6 +3019,7 @@ def test_ap_wpa2_eap_tls_neg_domain_match(dev, apdev):
+ 
+ def test_ap_wpa2_eap_tls_neg_subject_match(dev, apdev):
+     """WPA2-Enterprise negative test - subject mismatch"""
++    check_subject_match_support(dev[0])
+     params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+     hostapd.add_ap(apdev[0], params)
+     dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+@@ -3036,6 +3080,7 @@ def test_ap_wpa2_eap_tls_neg_subject_match(dev, apdev):
+ 
+ def test_ap_wpa2_eap_tls_neg_altsubject_match(dev, apdev):
+     """WPA2-Enterprise negative test - altsubject mismatch"""
++    check_altsubject_match_support(dev[0])
+     params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+     hostapd.add_ap(apdev[0], params)
+ 
+@@ -3582,7 +3627,7 @@ def test_ap_wpa2_eap_ikev2_oom(dev, apdev):
+             dev[0].request("REMOVE_NETWORK all")
+ 
+     tls = dev[0].request("GET tls_library")
+-    if not tls.startswith("wolfSSL"):
++    if not tls.startswith("wolfSSL") and not tls.startswith("mbed TLS"):
+         tests = [(1, "os_get_random;dh_init")]
+     else:
+         tests = [(1, "crypto_dh_init;dh_init")]
+@@ -4896,7 +4941,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca(dev, apdev, params):
+     params["private_key"] = "auth_serv/iCA-server/server.key"
+     hostapd.add_ap(apdev[0], params)
+     tls = dev[0].request("GET tls_library")
+-    if "GnuTLS" in tls or "wolfSSL" in tls:
++    if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+         ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+         client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+     else:
+@@ -4962,6 +5007,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_sha1(dev, apdev, params):
+     run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, "-sha1")
+ 
+ def run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, md):
++    check_ocsp_support(dev[0])
+     params = int_eap_server_params()
+     params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem"
+     params["server_cert"] = "auth_serv/iCA-server/server.pem"
+@@ -4971,7 +5017,7 @@ def run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, md):
+     try:
+         hostapd.add_ap(apdev[0], params)
+         tls = dev[0].request("GET tls_library")
+-        if "GnuTLS" in tls or "wolfSSL" in tls:
++        if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+             ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+             client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+         else:
+@@ -5007,7 +5053,7 @@ def run_ap_wpa2_eap_tls_intermediate_ca_ocsp_revoked(dev, apdev, params, md):
+     try:
+         hostapd.add_ap(apdev[0], params)
+         tls = dev[0].request("GET tls_library")
+-        if "GnuTLS" in tls or "wolfSSL" in tls:
++        if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+             ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+             client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+         else:
+@@ -5057,7 +5103,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_multi_missing_resp(dev, apdev, par
+     try:
+         hostapd.add_ap(apdev[0], params)
+         tls = dev[0].request("GET tls_library")
+-        if "GnuTLS" in tls or "wolfSSL" in tls:
++        if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+             ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+             client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+         else:
+@@ -5124,7 +5170,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_multi(dev, apdev, params):
+ 
+         hostapd.add_ap(apdev[0], params)
+         tls = dev[0].request("GET tls_library")
+-        if "GnuTLS" in tls or "wolfSSL" in tls:
++        if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+             ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+             client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+         else:
+@@ -5382,6 +5428,7 @@ def test_ap_wpa2_eap_ttls_server_cert_eku_client_server(dev, apdev):
+ 
+ def test_ap_wpa2_eap_ttls_server_pkcs12(dev, apdev):
+     """WPA2-Enterprise using EAP-TTLS and server PKCS#12 file"""
++    check_pkcs12_support(dev[0])
+     skip_with_fips(dev[0])
+     params = int_eap_server_params()
+     del params["server_cert"]
+@@ -5394,6 +5441,7 @@ def test_ap_wpa2_eap_ttls_server_pkcs12(dev, apdev):
+ 
+ def test_ap_wpa2_eap_ttls_server_pkcs12_extra(dev, apdev):
+     """EAP-TTLS and server PKCS#12 file with extra certs"""
++    check_pkcs12_support(dev[0])
+     skip_with_fips(dev[0])
+     params = int_eap_server_params()
+     del params["server_cert"]
+@@ -5416,6 +5464,7 @@ def test_ap_wpa2_eap_ttls_dh_params_server(dev, apdev):
+ 
+ def test_ap_wpa2_eap_ttls_dh_params_dsa_server(dev, apdev):
+     """WPA2-Enterprise using EAP-TTLS and alternative server dhparams (DSA)"""
++    check_dh_dsa_support(dev[0])
+     params = int_eap_server_params()
+     params["dh_file"] = "auth_serv/dsaparam.pem"
+     hapd = hostapd.add_ap(apdev[0], params)
+@@ -5727,8 +5776,8 @@ def test_ap_wpa2_eap_non_ascii_identity2(dev, apdev):
+ def test_openssl_cipher_suite_config_wpas(dev, apdev):
+     """OpenSSL cipher suite configuration on wpa_supplicant"""
+     tls = dev[0].request("GET tls_library")
+-    if not tls.startswith("OpenSSL"):
+-        raise HwsimSkip("TLS library is not OpenSSL: " + tls)
++    if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
++        raise HwsimSkip("TLS library is not OpenSSL or mbed TLS: " + tls)
+     params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+     hapd = hostapd.add_ap(apdev[0], params)
+     eap_connect(dev[0], hapd, "TTLS", "pap user",
+@@ -5754,14 +5803,14 @@ def test_openssl_cipher_suite_config_wpas(dev, apdev):
+ def test_openssl_cipher_suite_config_hapd(dev, apdev):
+     """OpenSSL cipher suite configuration on hostapd"""
+     tls = dev[0].request("GET tls_library")
+-    if not tls.startswith("OpenSSL"):
+-        raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL: " + tls)
++    if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
++        raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL or mbed TLS: " + tls)
+     params = int_eap_server_params()
+     params['openssl_ciphers'] = "AES256"
+     hapd = hostapd.add_ap(apdev[0], params)
+     tls = hapd.request("GET tls_library")
+-    if not tls.startswith("OpenSSL"):
+-        raise HwsimSkip("hostapd TLS library is not OpenSSL: " + tls)
++    if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
++        raise HwsimSkip("hostapd TLS library is not OpenSSL or mbed TLS: " + tls)
+     eap_connect(dev[0], hapd, "TTLS", "pap user",
+                 anonymous_identity="ttls", password="password",
+                 ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+@@ -6207,13 +6256,17 @@ def test_ap_wpa2_eap_tls_versions(dev, apdev):
+             check_tls_ver(dev[0], hapd,
+                           "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1",
+                           "TLSv1.2")
+-    elif tls.startswith("internal"):
++    elif tls.startswith("internal") or tls.startswith("mbed TLS"):
+         check_tls_ver(dev[0], hapd,
+                       "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1", "TLSv1.2")
+-    check_tls_ver(dev[1], hapd,
+-                  "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1")
+-    check_tls_ver(dev[2], hapd,
+-                  "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1")
++    if tls.startswith("mbed TLS"):
++        check_tls_ver(dev[2], hapd,
++                      "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1.0")
++    else:
++        check_tls_ver(dev[1], hapd,
++                      "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1")
++        check_tls_ver(dev[2], hapd,
++                      "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1")
+     if "run=OpenSSL 1.1.1" in tls or "run=OpenSSL 3." in tls:
+         check_tls_ver(dev[0], hapd,
+                       "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0", "TLSv1.3")
+@@ -6235,6 +6288,11 @@ def test_ap_wpa2_eap_tls_versions_server(dev, apdev):
+     tests = [("TLSv1", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
+              ("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
+              ("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")]
++    tls = dev[0].request("GET tls_library")
++    if tls.startswith("mbed TLS"):
++        tests = [#("TLSv1.0", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
++                 #("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
++                 ("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")]
+     for exp, flags in tests:
+         hapd.disable()
+         hapd.set("tls_flags", flags)
+@@ -7305,6 +7363,7 @@ def test_ap_wpa2_eap_assoc_rsn(dev, apdev):
+ def test_eap_tls_ext_cert_check(dev, apdev):
+     """EAP-TLS and external server certification validation"""
+     # With internal server certificate chain validation
++    check_ext_cert_check_support(dev[0])
+     id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+                         identity="tls user",
+                         ca_cert="auth_serv/ca.pem",
+@@ -7317,6 +7376,7 @@ def test_eap_tls_ext_cert_check(dev, apdev):
+ def test_eap_ttls_ext_cert_check(dev, apdev):
+     """EAP-TTLS and external server certification validation"""
+     # Without internal server certificate chain validation
++    check_ext_cert_check_support(dev[0])
+     id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                         identity="pap user", anonymous_identity="ttls",
+                         password="password", phase2="auth=PAP",
+@@ -7327,6 +7387,7 @@ def test_eap_ttls_ext_cert_check(dev, apdev):
+ def test_eap_peap_ext_cert_check(dev, apdev):
+     """EAP-PEAP and external server certification validation"""
+     # With internal server certificate chain validation
++    check_ext_cert_check_support(dev[0])
+     id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
+                         identity="user", anonymous_identity="peap",
+                         ca_cert="auth_serv/ca.pem",
+@@ -7337,6 +7398,7 @@ def test_eap_peap_ext_cert_check(dev, apdev):
+ 
+ def test_eap_fast_ext_cert_check(dev, apdev):
+     """EAP-FAST and external server certification validation"""
++    check_ext_cert_check_support(dev[0])
+     check_eap_capa(dev[0], "FAST")
+     # With internal server certificate chain validation
+     dev[0].request("SET blob fast_pac_auth_ext ")
+@@ -7351,10 +7413,6 @@ def test_eap_fast_ext_cert_check(dev, apdev):
+     run_ext_cert_check(dev, apdev, id)
+ 
+ def run_ext_cert_check(dev, apdev, net_id):
+-    check_ext_cert_check_support(dev[0])
+-    if not openssl_imported:
+-        raise HwsimSkip("OpenSSL python method not available")
+-
+     params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+     hapd = hostapd.add_ap(apdev[0], params)
+ 
+diff --git a/tests/hwsim/test_ap_ft.py b/tests/hwsim/test_ap_ft.py
+index 13461f014..8ffb5042c 100644
+--- a/tests/hwsim/test_ap_ft.py
++++ b/tests/hwsim/test_ap_ft.py
+@@ -2494,11 +2494,11 @@ def test_ap_ft_ap_oom5(dev, apdev):
+         # This will fail to roam
+         dev[0].roam(bssid1, check_bssid=False)
+ 
+-    with fail_test(hapd1, 1, "sha256_prf_bits;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
++    with fail_test(hapd1, 1, "sha256_prf;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
+         # This will fail to roam
+         dev[0].roam(bssid1, check_bssid=False)
+ 
+-    with fail_test(hapd1, 3, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
++    with fail_test(hapd1, 2, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
+         # This will fail to roam
+         dev[0].roam(bssid1, check_bssid=False)
+ 
+diff --git a/tests/hwsim/test_authsrv.py b/tests/hwsim/test_authsrv.py
+index e0665bcb2..02ec301e5 100644
+--- a/tests/hwsim/test_authsrv.py
++++ b/tests/hwsim/test_authsrv.py
+@@ -156,9 +156,12 @@ def test_authsrv_oom(dev, apdev):
+         if "FAIL" not in authsrv.request("ENABLE"):
+             raise Exception("ENABLE succeeded during OOM")
+ 
+-    with alloc_fail(authsrv, 1, "tls_init;authsrv_init"):
+-        if "FAIL" not in authsrv.request("ENABLE"):
+-            raise Exception("ENABLE succeeded during OOM")
++    # tls_mbedtls.c:tls_init() does not alloc memory (no alloc fail trigger)
++    tls = dev[0].request("GET tls_library")
++    if not tls.startswith("mbed TLS"):
++        with alloc_fail(authsrv, 1, "tls_init;authsrv_init"):
++            if "FAIL" not in authsrv.request("ENABLE"):
++                raise Exception("ENABLE succeeded during OOM")
+ 
+     for count in range(1, 3):
+         with alloc_fail(authsrv, count, "eap_sim_db_init;authsrv_init"):
+diff --git a/tests/hwsim/test_dpp.py b/tests/hwsim/test_dpp.py
+index 518983bd0..077de58c9 100644
+--- a/tests/hwsim/test_dpp.py
++++ b/tests/hwsim/test_dpp.py
+@@ -39,7 +39,8 @@ def check_dpp_capab(dev, brainpool=False, min_ver=1):
+         raise HwsimSkip("DPP not supported")
+     if brainpool:
+         tls = dev.request("GET tls_library")
+-        if (not tls.startswith("OpenSSL") or "run=BoringSSL" in tls) and not tls.startswith("wolfSSL"):
++        if (not tls.startswith("OpenSSL") or "run=BoringSSL" in tls) and not tls.startswith("wolfSSL") \
++                                                                     and not tls.startswith("mbed TLS"):
+             raise HwsimSkip("Crypto library does not support Brainpool curves: " + tls)
+     capa = dev.request("GET_CAPABILITY dpp")
+     ver = 1
+@@ -3902,6 +3903,9 @@ def test_dpp_proto_auth_req_no_i_proto_key(dev, apdev):
+ 
+ def test_dpp_proto_auth_req_invalid_i_proto_key(dev, apdev):
+     """DPP protocol testing - invalid I-proto key in Auth Req"""
++    tls = dev[0].request("GET tls_library")
++    if tls.startswith("mbed TLS"):
++        raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
+     run_dpp_proto_auth_req_missing(dev, 66, "Invalid Initiator Protocol Key")
+ 
+ def test_dpp_proto_auth_req_no_i_nonce(dev, apdev):
+@@ -3997,7 +4001,12 @@ def test_dpp_proto_auth_resp_no_r_proto_key(dev, apdev):
+ 
+ def test_dpp_proto_auth_resp_invalid_r_proto_key(dev, apdev):
+     """DPP protocol testing - invalid R-Proto Key in Auth Resp"""
+-    run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key")
++    tls = dev[0].request("GET tls_library")
++    if tls.startswith("mbed TLS"):
++        # mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key
++        run_dpp_proto_auth_resp_missing(dev, 67, "Failed to derive ECDH shared secret")
++    else:
++        run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key")
+ 
+ def test_dpp_proto_auth_resp_no_r_nonce(dev, apdev):
+     """DPP protocol testing - no R-nonce in Auth Resp"""
+@@ -4359,11 +4368,17 @@ def test_dpp_proto_pkex_exchange_resp_invalid_status(dev, apdev):
+ 
+ def test_dpp_proto_pkex_cr_req_invalid_bootstrap_key(dev, apdev):
+     """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Request"""
++    tls = dev[0].request("GET tls_library")
++    if tls.startswith("mbed TLS"):
++        raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
+     run_dpp_proto_pkex_req_missing(dev, 47,
+                                    "Peer bootstrapping key is invalid")
+ 
+ def test_dpp_proto_pkex_cr_resp_invalid_bootstrap_key(dev, apdev):
+     """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Response"""
++    tls = dev[0].request("GET tls_library")
++    if tls.startswith("mbed TLS"):
++        raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
+     run_dpp_proto_pkex_resp_missing(dev, 48,
+                                     "Peer bootstrapping key is invalid")
+ 
+diff --git a/tests/hwsim/test_erp.py b/tests/hwsim/test_erp.py
+index d083993e8..262e9f095 100644
+--- a/tests/hwsim/test_erp.py
++++ b/tests/hwsim/test_erp.py
+@@ -12,7 +12,7 @@ import time
+ 
+ import hostapd
+ from utils import *
+-from test_ap_eap import int_eap_server_params, check_tls13_support
++from test_ap_eap import int_eap_server_params, check_tls13_support, check_eap_capa
+ from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations
+ 
+ def test_erp_initiate_reauth_start(dev, apdev):
+@@ -276,6 +276,7 @@ def test_erp_radius_eap_methods(dev, apdev):
+     params['erp_domain'] = 'example.com'
+     params['disable_pmksa_caching'] = '1'
+     hapd = hostapd.add_ap(apdev[0], params)
++    tls = dev[0].request("GET tls_library")
+ 
+     erp_test(dev[0], hapd, eap="AKA", identity="0232010000000000@example.com",
+              password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
+@@ -289,7 +290,7 @@ def test_erp_radius_eap_methods(dev, apdev):
+              password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
+     erp_test(dev[0], hapd, eap="EKE", identity="erp-eke@example.com",
+              password="hello")
+-    if "FAST" in eap_methods:
++    if "FAST" in eap_methods and check_eap_capa(dev[0], "FAST"):
+         erp_test(dev[0], hapd, eap="FAST", identity="erp-fast@example.com",
+                  password="password", ca_cert="auth_serv/ca.pem",
+                  phase2="auth=GTC",
+@@ -301,13 +302,14 @@ def test_erp_radius_eap_methods(dev, apdev):
+              password="password")
+     erp_test(dev[0], hapd, eap="PAX", identity="erp-pax@example.com",
+              password_hex="0123456789abcdef0123456789abcdef")
+-    if "MSCHAPV2" in eap_methods:
++    if "MSCHAPV2" in eap_methods and check_eap_capa(dev[0], "MSCHAPV2"):
+         erp_test(dev[0], hapd, eap="PEAP", identity="erp-peap@example.com",
+                  password="password", ca_cert="auth_serv/ca.pem",
+                  phase2="auth=MSCHAPV2")
+-        erp_test(dev[0], hapd, eap="TEAP", identity="erp-teap@example.com",
+-                 password="password", ca_cert="auth_serv/ca.pem",
+-                 phase2="auth=MSCHAPV2", pac_file="blob://teap_pac")
++        if check_eap_capa(dev[0], "TEAP"):
++            erp_test(dev[0], hapd, eap="TEAP", identity="erp-teap@example.com",
++                     password="password", ca_cert="auth_serv/ca.pem",
++                     phase2="auth=MSCHAPV2", pac_file="blob://teap_pac")
+     erp_test(dev[0], hapd, eap="PSK", identity="erp-psk@example.com",
+              password_hex="0123456789abcdef0123456789abcdef")
+     if "PWD" in eap_methods:
+@@ -640,7 +642,7 @@ def test_erp_local_errors(dev, apdev):
+         dev[0].request("REMOVE_NETWORK all")
+         dev[0].wait_disconnected()
+ 
+-    for count in range(1, 6):
++    for count in range(1, 4):
+         dev[0].request("ERP_FLUSH")
+         with fail_test(dev[0], count, "hmac_sha256_kdf;eap_peer_erp_init"):
+             dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+diff --git a/tests/hwsim/test_fils.py b/tests/hwsim/test_fils.py
+index 6f857243a..de65c57a7 100644
+--- a/tests/hwsim/test_fils.py
++++ b/tests/hwsim/test_fils.py
+@@ -1477,6 +1477,10 @@ def check_ec_group(dev, group):
+     tls = dev.request("GET tls_library")
+     if tls.startswith("wolfSSL"):
+         return
++    elif tls.startswith("mbed TLS"):
++        if int(group) == 27:
++            raise HwsimSkip("Brainpool EC group 27 not supported by mbed TLS")
++        return
+     if int(group) in [25]:
+         if not (tls.startswith("OpenSSL") and ("build=OpenSSL 1.0.2" in tls or "build=OpenSSL 1.1" in tls or "build=OpenSSL 3." in tls) and ("run=OpenSSL 1.0.2" in tls or "run=OpenSSL 1.1" in tls or "run=OpenSSL 3." in tls)):
+             raise HwsimSkip("EC group not supported")
+diff --git a/tests/hwsim/test_pmksa_cache.py b/tests/hwsim/test_pmksa_cache.py
+index 4a3b444ff..4f7f7f760 100644
+--- a/tests/hwsim/test_pmksa_cache.py
++++ b/tests/hwsim/test_pmksa_cache.py
+@@ -958,7 +958,7 @@ def test_pmksa_cache_preauth_wpas_oom(dev, apdev):
+     eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
+                 password_hex="0123456789abcdef0123456789abcdef",
+                 bssid=apdev[0]['bssid'])
+-    for i in range(1, 11):
++    for i in range(1, 10):
+         with alloc_fail(dev[0], i, "rsn_preauth_init"):
+             res = dev[0].request("PREAUTH f2:11:22:33:44:55").strip()
+             logger.info("Iteration %d - PREAUTH command results: %s" % (i, res))
+@@ -966,7 +966,7 @@ def test_pmksa_cache_preauth_wpas_oom(dev, apdev):
+                 state = dev[0].request('GET_ALLOC_FAIL')
+                 if state.startswith('0:'):
+                     break
+-                time.sleep(0.05)
++                time.sleep(0.10)
+ 
+ def test_pmksa_cache_ctrl(dev, apdev):
+     """PMKSA cache control interface operations"""
+diff --git a/tests/hwsim/test_sae.py b/tests/hwsim/test_sae.py
+index 679db0e2d..4ea9bd6a4 100644
+--- a/tests/hwsim/test_sae.py
++++ b/tests/hwsim/test_sae.py
+@@ -178,6 +178,11 @@ def test_sae_groups(dev, apdev):
+     if tls.startswith("OpenSSL") and "run=OpenSSL 1." in tls:
+         logger.info("Add Brainpool EC groups since OpenSSL is new enough")
+         sae_groups += [27, 28, 29, 30]
++    if tls.startswith("mbed TLS"):
++        # secp224k1 and secp224r1 (26) have prime p = 1 mod 4, and mbedtls
++        # does not have code to derive y from compressed format for those curves
++        sae_groups = [19, 25, 20, 21, 1, 2, 5, 14, 15, 16, 22, 23, 24]
++        sae_groups += [27, 28, 29, 30]
+     heavy_groups = [14, 15, 16]
+     suitable_groups = [15, 16, 17, 18, 19, 20, 21]
+     groups = [str(g) for g in sae_groups]
+@@ -2232,6 +2237,8 @@ def run_sae_pwe_group(dev, apdev, group):
+             logger.info("Add Brainpool EC groups since OpenSSL is new enough")
+         elif tls.startswith("wolfSSL"):
+             logger.info("Make sure Brainpool EC groups were enabled when compiling wolfSSL")
++        elif tls.startswith("mbed TLS"):
++            logger.info("Make sure Brainpool EC groups were enabled when compiling mbed TLS")
+         else:
+             raise HwsimSkip("Brainpool curve not supported")
+     start_sae_pwe_ap(apdev[0], group, 2)
+diff --git a/tests/hwsim/test_suite_b.py b/tests/hwsim/test_suite_b.py
+index ddd1c2ee7..a44f32955 100644
+--- a/tests/hwsim/test_suite_b.py
++++ b/tests/hwsim/test_suite_b.py
+@@ -27,6 +27,8 @@ def check_suite_b_tls_lib(dev, dhe=False, level128=False):
+         return
+     if tls.startswith("wolfSSL"):
+         return
++    if tls.startswith("mbed TLS"):
++        return
+     if not tls.startswith("OpenSSL"):
+         raise HwsimSkip("TLS library not supported for Suite B: " + tls)
+     supported = False
+@@ -520,6 +522,7 @@ def test_suite_b_192_rsa_insufficient_dh(dev, apdev):
+ 
+     dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
+                    ieee80211w="2",
++                   openssl_ciphers="DHE-RSA-AES256-GCM-SHA384",
+                    phase1="tls_suiteb=1",
+                    eap="TLS", identity="tls user",
+                    ca_cert="auth_serv/rsa3072-ca.pem",
+diff --git a/tests/hwsim/test_wpas_ctrl.py b/tests/hwsim/test_wpas_ctrl.py
+index cf6d3211e..cbf136eaf 100644
+--- a/tests/hwsim/test_wpas_ctrl.py
++++ b/tests/hwsim/test_wpas_ctrl.py
+@@ -1856,7 +1856,7 @@ def _test_wpas_ctrl_oom(dev):
+     tls = dev[0].request("GET tls_library")
+     if not tls.startswith("internal"):
+         tests.append(('NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG', 'FAIL',
+-                      4, 'wpas_ctrl_nfc_get_handover_sel_p2p'))
++                      3, 'wpas_ctrl_nfc_get_handover_sel_p2p'))
+     for cmd, exp, count, func in tests:
+         with alloc_fail(dev[0], count, func):
+             res = dev[0].request(cmd)
+diff --git a/tests/hwsim/utils.py b/tests/hwsim/utils.py
+index 7e3608284..b23c1ee0b 100644
+--- a/tests/hwsim/utils.py
++++ b/tests/hwsim/utils.py
+@@ -145,7 +145,13 @@ def check_imsi_privacy_support(dev):
+ 
+ def check_tls_tod(dev):
+     tls = dev.request("GET tls_library")
+-    if not tls.startswith("OpenSSL") and not tls.startswith("internal"):
++    if tls.startswith("OpenSSL"):
++        return
++    elif tls.startswith("internal"):
++        return
++    elif tls.startswith("mbed TLS"):
++        return
++    else:
+         raise HwsimSkip("TLS TOD-TOFU/STRICT not supported with this TLS library: " + tls)
+ 
+ def vht_supported():
+diff --git a/tests/test-crypto_module.c b/tests/test-crypto_module.c
+new file mode 100644
+index 000000000..0f1156142
+--- /dev/null
++++ b/tests/test-crypto_module.c
+@@ -0,0 +1,16 @@
++/*
++ * crypto module tests - test program
++ * Copyright (c) 2022, Glenn Strauss <gstrauss@gluelogic.com>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++#include "utils/module_tests.h"
++#include "crypto/crypto_module_tests.c"
++
++int main(int argc, char *argv[])
++{
++	return crypto_module_tests();
++}
+diff --git a/tests/test-https.c b/tests/test-https.c
+index a72e56f9d..e9df82f1d 100644
+--- a/tests/test-https.c
++++ b/tests/test-https.c
+@@ -75,7 +75,7 @@ static int https_client(int s, const char *path)
+ 	struct tls_connection *conn;
+ 	struct wpabuf *in, *out, *appl;
+ 	int res = -1;
+-	int need_more_data;
++	int need_more_data = 0;
+ 
+ 	os_memset(&conf, 0, sizeof(conf));
+ 	conf.event_cb = https_tls_event_cb;
+@@ -93,8 +93,12 @@ static int https_client(int s, const char *path)
+ 
+ 	for (;;) {
+ 		appl = NULL;
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ 		out = tls_connection_handshake2(tls, conn, in, &appl,
+ 						&need_more_data);
++#else
++		out = tls_connection_handshake(tls, conn, in, &appl);
++#endif
+ 		wpabuf_free(in);
+ 		in = NULL;
+ 		if (out == NULL) {
+@@ -152,11 +156,15 @@ static int https_client(int s, const char *path)
+ 
+ 	wpa_printf(MSG_INFO, "Reading HTTP response");
+ 	for (;;) {
+-		int need_more_data;
++		int need_more_data = 0;
+ 		in = https_recv(s);
+ 		if (in == NULL)
+ 			goto done;
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ 		out = tls_connection_decrypt2(tls, conn, in, &need_more_data);
++#else
++		out = tls_connection_decrypt(tls, conn, in);
++#endif
+ 		if (need_more_data)
+ 			wpa_printf(MSG_DEBUG, "HTTP: Need more data");
+ 		wpabuf_free(in);
+diff --git a/tests/test-https_server.c b/tests/test-https_server.c
+index 33b448682..9dcca5596 100644
+--- a/tests/test-https_server.c
++++ b/tests/test-https_server.c
+@@ -67,10 +67,12 @@ static struct wpabuf * https_recv(int s, int timeout_ms)
+ }
+ 
+ 
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ static void https_tls_log_cb(void *ctx, const char *msg)
+ {
+ 	wpa_printf(MSG_DEBUG, "TLS: %s", msg);
+ }
++#endif
+ 
+ 
+ static int https_server(int s)
+@@ -79,7 +81,7 @@ static int https_server(int s)
+ 	void *tls;
+ 	struct tls_connection_params params;
+ 	struct tls_connection *conn;
+-	struct wpabuf *in, *out, *appl;
++	struct wpabuf *in = NULL, *out = NULL, *appl = NULL;
+ 	int res = -1;
+ 
+ 	os_memset(&conf, 0, sizeof(conf));
+@@ -106,7 +108,9 @@ static int https_server(int s)
+ 		return -1;
+ 	}
+ 
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ 	tls_connection_set_log_cb(conn, https_tls_log_cb, NULL);
++#endif
+ 
+ 	for (;;) {
+ 		in = https_recv(s, 5000);
+@@ -147,12 +151,16 @@ static int https_server(int s)
+ 
+ 	wpa_printf(MSG_INFO, "Reading HTTP request");
+ 	for (;;) {
+-		int need_more_data;
++		int need_more_data = 0;
+ 
+ 		in = https_recv(s, 5000);
+ 		if (!in)
+ 			goto done;
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ 		out = tls_connection_decrypt2(tls, conn, in, &need_more_data);
++#else
++		out = tls_connection_decrypt(tls, conn, in);
++#endif
+ 		wpabuf_free(in);
+ 		in = NULL;
+ 		if (need_more_data) {
+diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
+index 743c8acd6..c40e8d70d 100644
+--- a/wpa_supplicant/Makefile
++++ b/wpa_supplicant/Makefile
+@@ -10,6 +10,7 @@ ALL += dbus/fi.w1.wpa_supplicant1.service
+ EXTRA_TARGETS=dynamic_eap_methods
+ 
+ CONFIG_FILE=.config
++-include $(if $(MULTICALL),../hostapd/.config)
+ include ../src/build.rules
+ 
+ ifdef CONFIG_BUILD_PASN_SO
+@@ -190,6 +191,25 @@ ifdef CONFIG_EAPOL_TEST
+ CFLAGS += -Werror -DEAPOL_TEST
+ 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
++endif
++
+ ifdef CONFIG_CODE_COVERAGE
+ CFLAGS += -O0 -fprofile-arcs -ftest-coverage -U_FORTIFY_SOURCE
+ LIBS += -lgcov
+@@ -389,7 +409,9 @@ endif
+ ifdef CONFIG_IBSS_RSN
+ NEED_RSN_AUTHENTICATOR=y
+ CFLAGS += -DCONFIG_IBSS_RSN
++ifndef MULTICALL
+ CFLAGS += -DCONFIG_NO_VLAN
++endif
+ OBJS += ibss_rsn.o
+ endif
+ 
+@@ -981,6 +1003,10 @@ ifdef CONFIG_DYNAMIC_EAP_METHODS
+ CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS
+ LIBS += -ldl -rdynamic
+ endif
++else
++  ifdef MULTICALL
++    OBJS += ../src/eap_common/eap_common.o
++  endif
+ endif
+ 
+ ifdef CONFIG_AP
+@@ -988,9 +1014,11 @@ NEED_EAP_COMMON=y
+ NEED_RSN_AUTHENTICATOR=y
+ CFLAGS += -DCONFIG_AP
+ OBJS += ap.o
++ifndef MULTICALL
+ CFLAGS += -DCONFIG_NO_RADIUS
+ CFLAGS += -DCONFIG_NO_ACCOUNTING
+ CFLAGS += -DCONFIG_NO_VLAN
++endif
+ OBJS += ../src/ap/hostapd.o
+ OBJS += ../src/ap/wpa_auth_glue.o
+ OBJS += ../src/ap/utils.o
+@@ -1030,7 +1058,16 @@ ifdef CONFIG_FILS
+ OBJS += ../src/ap/fils_hlp.o
+ endif
+ ifdef CONFIG_CTRL_IFACE
++ifdef CONFIG_CTRL_IFACE_MIB
++CFLAGS += -DCONFIG_CTRL_IFACE_MIB
++endif
+ 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
+@@ -1081,6 +1118,12 @@ endif
+ ifdef CONFIG_HS20
+ OBJS += ../src/ap/hs20.o
+ endif
++else
++  ifdef MULTICALL
++    OBJS += ../src/eap_server/eap_server.o
++    OBJS += ../src/eap_server/eap_server_identity.o
++    OBJS += ../src/eap_server/eap_server_methods.o
++  endif
+ endif
+ 
+ ifdef CONFIG_MBO
+@@ -1090,7 +1133,9 @@ NEED_GAS=y
+ endif
+ 
+ ifdef NEED_RSN_AUTHENTICATOR
++ifndef MULTICALL
+ CFLAGS += -DCONFIG_NO_RADIUS
++endif
+ NEED_AES_WRAP=y
+ OBJS += ../src/ap/wpa_auth.o
+ OBJS += ../src/ap/wpa_auth_ie.o
+@@ -1189,6 +1234,7 @@ TLS_FUNCS=y
+ endif
+ 
+ ifeq ($(CONFIG_TLS), wolfssl)
++CFLAGS += -DCONFIG_TLS_WOLFSSL
+ ifdef TLS_FUNCS
+ CFLAGS += -DWOLFSSL_DER_LOAD
+ OBJS += ../src/crypto/tls_wolfssl.o
+@@ -1204,6 +1250,7 @@ LIBS_p += -lwolfssl -lm
+ endif
+ 
+ ifeq ($(CONFIG_TLS), openssl)
++CFLAGS += -DCONFIG_TLS_OPENSSL
+ CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
+ ifdef TLS_FUNCS
+ CFLAGS += -DEAP_TLS_OPENSSL
+@@ -1230,7 +1277,28 @@ endif
+ CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
+ endif
+ 
++ifeq ($(CONFIG_TLS), mbedtls)
++CFLAGS += -DCONFIG_TLS_MBEDTLS
++ifndef CONFIG_CRYPTO
++CONFIG_CRYPTO=mbedtls
++endif
++ifdef TLS_FUNCS
++OBJS += ../src/crypto/tls_mbedtls.o
++LIBS += -lmbedtls -lmbedx509
++endif
++OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++OBJS_p += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++OBJS_priv += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++ifeq ($(CONFIG_CRYPTO), mbedtls)
++LIBS += -lmbedcrypto
++LIBS_p += -lmbedcrypto
++# XXX: create a config option?
++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
++endif
++endif
++
+ ifeq ($(CONFIG_TLS), gnutls)
++CFLAGS += -DCONFIG_TLS_GNUTLS
+ ifndef CONFIG_CRYPTO
+ # default to libgcrypt
+ CONFIG_CRYPTO=gnutls
+@@ -1261,6 +1329,7 @@ endif
+ endif
+ 
+ ifeq ($(CONFIG_TLS), internal)
++CFLAGS += -DCONFIG_TLS_INTERNAL
+ ifndef CONFIG_CRYPTO
+ CONFIG_CRYPTO=internal
+ endif
+@@ -1341,6 +1410,7 @@ endif
+ endif
+ 
+ ifeq ($(CONFIG_TLS), linux)
++CFLAGS += -DCONFIG_TLS_INTERNAL
+ OBJS += ../src/crypto/crypto_linux.o
+ OBJS_p += ../src/crypto/crypto_linux.o
+ ifdef TLS_FUNCS
+@@ -1422,9 +1492,11 @@ endif
+ 
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ NEED_INTERNAL_AES_WRAP=y
+ endif
+ endif
++endif
+ ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP
+ # Seems to be needed at least with BoringSSL
+ NEED_INTERNAL_AES_WRAP=y
+@@ -1438,9 +1510,11 @@ endif
+ 
+ ifdef NEED_INTERNAL_AES_WRAP
+ ifneq ($(CONFIG_TLS), linux)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-unwrap.o
+ endif
+ endif
++endif
+ ifdef NEED_AES_EAX
+ AESOBJS += ../src/crypto/aes-eax.o
+ NEED_AES_CTR=y
+@@ -1450,35 +1524,45 @@ AESOBJS += ../src/crypto/aes-siv.o
+ NEED_AES_CTR=y
+ endif
+ ifdef NEED_AES_CTR
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-ctr.o
+ endif
++endif
+ ifdef NEED_AES_ENCBLOCK
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-encblock.o
+ endif
++endif
+ NEED_AES_ENC=y
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-omac1.o
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_WRAP
+ NEED_AES_ENC=y
+ ifdef NEED_INTERNAL_AES_WRAP
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-wrap.o
+ endif
+ endif
++endif
+ ifdef NEED_AES_CBC
+ NEED_AES_ENC=y
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-cbc.o
+ endif
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_ENC
+ ifdef CONFIG_INTERNAL_AES
+ AESOBJS += ../src/crypto/aes-internal-enc.o
+@@ -1493,12 +1577,16 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-prf.o
++endif
+ ifdef CONFIG_INTERNAL_SHA1
+ SHA1OBJS += ../src/crypto/sha1-internal.o
+ ifdef NEED_FIPS186_2_PRF
+@@ -1510,29 +1598,37 @@ CFLAGS += -DCONFIG_NO_PBKDF2
+ else
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_T_PRF
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-tprf.o
+ endif
++endif
+ ifdef NEED_TLS_PRF
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-tlsprf.o
+ endif
+ endif
++endif
+ 
+ ifndef CONFIG_FIPS
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ MD5OBJS += ../src/crypto/md5.o
+ endif
+ endif
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_MD5
+ ifdef CONFIG_INTERNAL_MD5
+ MD5OBJS += ../src/crypto/md5-internal.o
+@@ -1587,12 +1683,17 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA256OBJS += ../src/crypto/sha256.o
+ endif
+ endif
+ endif
+ endif
++endif
++
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA256OBJS += ../src/crypto/sha256-prf.o
++endif
+ ifdef CONFIG_INTERNAL_SHA256
+ SHA256OBJS += ../src/crypto/sha256-internal.o
+ endif
+@@ -1605,50 +1706,68 @@ CFLAGS += -DCONFIG_INTERNAL_SHA512
+ SHA256OBJS += ../src/crypto/sha512-internal.o
+ endif
+ ifdef NEED_TLS_PRF_SHA256
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA256OBJS += ../src/crypto/sha256-tlsprf.o
+ endif
++endif
+ ifdef NEED_TLS_PRF_SHA384
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA256OBJS += ../src/crypto/sha384-tlsprf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA256_KDF
+ CFLAGS += -DCONFIG_HMAC_SHA256_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256-kdf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA384_KDF
+ CFLAGS += -DCONFIG_HMAC_SHA384_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-kdf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA512_KDF
+ CFLAGS += -DCONFIG_HMAC_SHA512_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512-kdf.o
+ endif
++endif
+ OBJS += $(SHA256OBJS)
+ ifdef NEED_SHA384
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384.o
+ endif
+ endif
+ endif
+ endif
++endif
+ CFLAGS += -DCONFIG_SHA384
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-prf.o
+ endif
++endif
+ ifdef NEED_SHA512
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512.o
+ endif
+ endif
+ endif
+ endif
++endif
+ CFLAGS += -DCONFIG_SHA512
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512-prf.o
+ endif
++endif
+ 
+ ifdef NEED_ASN1
+ OBJS += ../src/tls/asn1.o
+@@ -1823,10 +1942,12 @@ ifdef CONFIG_FIPS
+ CFLAGS += -DCONFIG_FIPS
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ $(error CONFIG_FIPS=y requires CONFIG_TLS=openssl)
+ endif
+ endif
+ endif
++endif
+ 
+ OBJS += $(SHA1OBJS) $(DESOBJS)
+ 
+@@ -2004,32 +2125,38 @@ wpa_priv: $(BCHECK) $(OBJS_priv)
+ 
+ _OBJS_VAR := OBJS
+ include ../src/objs.mk
++wpa_supplicant_multi.a: .config $(BCHECK) $(OBJS) $(EXTRA_progs)
++	$(Q)$(CC) -c -o wpa_supplicant_multi.o -Dmain=wpa_supplicant_main $(CFLAGS) main.c
++	@$(E) "  CC " $<
++	@rm -f $@
++	@$(AR) cr $@ wpa_supplicant_multi.o $(OBJS)
++
+ wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs)
+-	$(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
++	+$(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
+ 	@$(E) "  LD " $@
+ 
+ _OBJS_VAR := OBJS_t
+ include ../src/objs.mk
+ eapol_test: $(OBJS_t)
+-	$(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
++	+$(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
+ 	@$(E) "  LD " $@
+ 
+ _OBJS_VAR := OBJS_t2
+ include ../src/objs.mk
+ preauth_test: $(OBJS_t2)
+-	$(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
++	+$(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
+ 	@$(E) "  LD " $@
+ 
+ _OBJS_VAR := OBJS_p
+ include ../src/objs.mk
+ wpa_passphrase: $(OBJS_p)
+-	$(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
++	+$(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
+ 	@$(E) "  LD " $@
+ 
+ _OBJS_VAR := OBJS_c
+ include ../src/objs.mk
+ wpa_cli: $(OBJS_c)
+-	$(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
++	+$(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
+ 	@$(E) "  LD " $@
+ 
+ LIBCTRL += ../src/common/wpa_ctrl.o
+@@ -2136,6 +2263,12 @@ eap_gpsk.so: $(SRC_EAP_GPSK)
+ 	$(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
+ 	@$(E) "  sed" $<
+ 
++dump_cflags:
++	@printf "%s " "$(CFLAGS)"
++
++dump_ldflags:
++	@printf "%s " "$(LDFLAGS) $(LIBS) $(EXTRALIBS)"
++
+ wpa_supplicant.exe: wpa_supplicant
+ 	mv -f $< $@
+ wpa_cli.exe: wpa_cli
+diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
+index 69a0e5ee1..7e8c97c38 100644
+--- a/wpa_supplicant/ap.c
++++ b/wpa_supplicant/ap.c
+@@ -1520,7 +1520,7 @@ int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+ #endif /* CONFIG_WPS */
+ 
+ 
+-#ifdef CONFIG_CTRL_IFACE
++#if defined(CONFIG_CTRL_IFACE) && defined(CONFIG_CTRL_IFACE_MIB)
+ 
+ int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
+ 			    char *buf, size_t buflen)
+@@ -1846,17 +1846,37 @@ int ap_switch_channel(struct wpa_supplicant *wpa_s,
+ 
+ 
+ #ifdef CONFIG_CTRL_IFACE
++
++static int __ap_ctrl_iface_chanswitch(struct hostapd_iface *iface,
++				      struct csa_settings *settings)
++{
++#ifdef NEED_AP_MLME
++	if (!iface || !iface->bss[0])
++		return 0;
++
++	return hostapd_switch_channel(iface->bss[0], settings);
++#else
++	return -1;
++#endif
++}
++
++
+ int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos)
+ {
+ 	struct csa_settings settings;
+ 	int ret = hostapd_parse_csa_settings(pos, &settings);
+ 
+-	if (ret)
+-		return ret;
++	if (!(wpa_s->ap_iface && wpa_s->ap_iface->bss[0]) &&
++	    !(wpa_s->ifmsh && wpa_s->ifmsh->bss[0]))
++		return -1;
+ 
+ 	settings.link_id = -1;
+ 
+-	return ap_switch_channel(wpa_s, &settings);
++	ret = __ap_ctrl_iface_chanswitch(wpa_s->ap_iface, &settings);
++	if (ret)
++		return ret;
++
++	return __ap_ctrl_iface_chanswitch(wpa_s->ifmsh, &settings);
+ }
+ #endif /* CONFIG_CTRL_IFACE */
+ 
+diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
+index b02b694a3..dc4b0636a 100644
+--- a/wpa_supplicant/config.c
++++ b/wpa_supplicant/config.c
+@@ -18,6 +18,7 @@
+ #include "eap_peer/eap.h"
+ #include "p2p/p2p.h"
+ #include "fst/fst.h"
++#include "ap/sta_info.h"
+ #include "config.h"
+ 
+ 
+@@ -2421,6 +2422,97 @@ static char * wpa_config_write_mac_value(const struct parse_data *data,
+ #endif /* NO_CONFIG_WRITE */
+ 
+ 
++static int wpa_config_parse_mcast_rate(const struct parse_data *data,
++				       struct wpa_ssid *ssid, int line,
++				       const char *value)
++{
++	ssid->mcast_rate = (int)(strtod(value, NULL) * 10);
++
++	return 0;
++}
++
++#ifndef NO_CONFIG_WRITE
++static char * wpa_config_write_mcast_rate(const struct parse_data *data,
++					  struct wpa_ssid *ssid)
++{
++	char *value;
++	int res;
++
++	if (!ssid->mcast_rate == 0)
++		return NULL;
++
++	value = os_malloc(6); /* longest: 300.0 */
++	if (value == NULL)
++		return NULL;
++	res = os_snprintf(value, 5, "%.1f", (double)ssid->mcast_rate / 10);
++	if (res < 0) {
++		os_free(value);
++		return NULL;
++	}
++	return value;
++}
++#endif /* NO_CONFIG_WRITE */
++
++static int wpa_config_parse_rates(const struct parse_data *data,
++				  struct wpa_ssid *ssid, int line,
++				  const char *value)
++{
++	int i;
++	char *pos, *r, *sptr, *end;
++	double rate;
++
++	pos = (char *)value;
++	r = strtok_r(pos, ",", &sptr);
++	i = 0;
++	while (pos && i < WLAN_SUPP_RATES_MAX) {
++		rate = 0.0;
++		if (r)
++			rate = strtod(r, &end);
++		ssid->rates[i] = rate * 2;
++		if (*end != '\0' || rate * 2 != ssid->rates[i])
++			return 1;
++
++		i++;
++		r = strtok_r(NULL, ",", &sptr);
++	}
++
++	return 0;
++}
++
++#ifndef NO_CONFIG_WRITE
++static char * wpa_config_write_rates(const struct parse_data *data,
++				     struct wpa_ssid *ssid)
++{
++	char *value, *pos;
++	int res, i;
++
++	if (ssid->rates[0] <= 0)
++		return NULL;
++
++	value = os_malloc(6 * WLAN_SUPP_RATES_MAX + 1);
++	if (value == NULL)
++		return NULL;
++	pos = value;
++	for (i = 0; i < WLAN_SUPP_RATES_MAX - 1; i++) {
++		res = os_snprintf(pos, 6, "%.1f,", (double)ssid->rates[i] / 2);
++		if (res < 0) {
++			os_free(value);
++			return NULL;
++		}
++		pos += res;
++	}
++	res = os_snprintf(pos, 6, "%.1f",
++			  (double)ssid->rates[WLAN_SUPP_RATES_MAX - 1] / 2);
++	if (res < 0) {
++		os_free(value);
++		return NULL;
++	}
++
++	value[6 * WLAN_SUPP_RATES_MAX] = '\0';
++	return value;
++}
++#endif /* NO_CONFIG_WRITE */
++
+ /* Helper macros for network block parser */
+ 
+ #ifdef OFFSET
+@@ -2639,6 +2731,7 @@ static const struct parse_data ssid_fields[] = {
+ #else /* CONFIG_MESH */
+ 	{ INT_RANGE(mode, 0, 4) },
+ #endif /* CONFIG_MESH */
++	{ INT_RANGE(noscan, 0, 1) },
+ 	{ INT_RANGE(proactive_key_caching, 0, 1) },
+ 	{ INT_RANGE(disabled, 0, 2) },
+ 	{ STR(id_str) },
+@@ -2712,6 +2805,8 @@ static const struct parse_data ssid_fields[] = {
+ 	{ INT(ap_max_inactivity) },
+ 	{ INT(dtim_period) },
+ 	{ INT(beacon_int) },
++	{ FUNC(rates) },
++	{ FUNC(mcast_rate) },
+ #ifdef CONFIG_MACSEC
+ 	{ INT_RANGE(macsec_policy, 0, 1) },
+ 	{ INT_RANGE(macsec_integ_only, 0, 1) },
+diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
+index fd8eafe2b..5ce616129 100644
+--- a/wpa_supplicant/config_file.c
++++ b/wpa_supplicant/config_file.c
+@@ -326,8 +326,13 @@ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
+ 	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));
+@@ -775,6 +780,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
+ #endif /* IEEE8021X_EAPOL */
+ 	INT(mode);
+ 	INT(no_auto_peer);
++	INT(noscan);
+ 	INT(mesh_fwding);
+ 	INT(frequency);
+ 	INT(enable_edmg);
+diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
+index d64c30508..872bcc270 100644
+--- a/wpa_supplicant/config_ssid.h
++++ b/wpa_supplicant/config_ssid.h
+@@ -879,6 +879,9 @@ struct wpa_ssid {
+ 	 */
+ 	void *parent_cred;
+ 
++	unsigned char rates[WLAN_SUPP_RATES_MAX];
++	double mcast_rate;
++
+ #ifdef CONFIG_MACSEC
+ 	/**
+ 	 * macsec_policy - Determines the policy for MACsec secure session
+@@ -1035,6 +1038,8 @@ struct wpa_ssid {
+ 	 */
+ 	int no_auto_peer;
+ 
++	int noscan;
++
+ 	/**
+ 	 * mesh_rssi_threshold - Set mesh parameter mesh_rssi_threshold (dBm)
+ 	 *
+diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
+index d245531cd..4777c3abe 100644
+--- a/wpa_supplicant/ctrl_iface.c
++++ b/wpa_supplicant/ctrl_iface.c
+@@ -2355,7 +2355,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
+ 			pos += ret;
+ 		}
+ 
+-#ifdef CONFIG_AP
++#if defined(CONFIG_AP) && defined(CONFIG_CTRL_IFACE_MIB)
+ 		if (wpa_s->ap_iface) {
+ 			pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos,
+ 							    end - pos,
+@@ -12561,6 +12561,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ 			reply_len = -1;
+ 	} else if (os_strncmp(buf, "NOTE ", 5) == 0) {
+ 		wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
++#ifdef CONFIG_CTRL_IFACE_MIB
+ 	} else if (os_strcmp(buf, "MIB") == 0) {
+ 		reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
+ 		if (reply_len >= 0) {
+@@ -12573,6 +12574,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ 				reply_size - reply_len);
+ #endif /* CONFIG_MACSEC */
+ 		}
++#endif
+ 	} else if (os_strncmp(buf, "STATUS", 6) == 0) {
+ 		reply_len = wpa_supplicant_ctrl_iface_status(
+ 			wpa_s, buf + 6, reply, reply_size);
+@@ -13061,6 +13063,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ 		reply_len = wpa_supplicant_ctrl_iface_bss(
+ 			wpa_s, buf + 4, reply, reply_size);
+ #ifdef CONFIG_AP
++#ifdef CONFIG_CTRL_IFACE_MIB
+ 	} else if (os_strcmp(buf, "STA-FIRST") == 0) {
+ 		reply_len = ap_ctrl_iface_sta_first(wpa_s, reply, reply_size);
+ 	} else if (os_strncmp(buf, "STA ", 4) == 0) {
+@@ -13069,12 +13072,15 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ 	} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
+ 		reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply,
+ 						   reply_size);
++#endif
++#ifdef CONFIG_CTRL_IFACE_MIB
+ 	} else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
+ 		if (ap_ctrl_iface_sta_deauthenticate(wpa_s, buf + 15))
+ 			reply_len = -1;
+ 	} else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
+ 		if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13))
+ 			reply_len = -1;
++#endif
+ 	} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
+ 		if (ap_ctrl_iface_chanswitch(wpa_s, buf + 12))
+ 			reply_len = -1;
+@@ -13233,7 +13239,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ 		if (wpas_ctrl_iface_coloc_intf_report(wpa_s, buf + 18))
+ 			reply_len = -1;
+ #endif /* CONFIG_WNM */
+-#ifdef CONFIG_WNM_AP
++#if defined(CONFIG_AP) && defined(CONFIG_WNM_AP)
+ 	} else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
+ 		if (ap_ctrl_iface_disassoc_imminent(wpa_s, buf + 18))
+ 			reply_len = -1;
+@@ -13243,7 +13249,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ 	} else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
+ 		if (ap_ctrl_iface_bss_tm_req(wpa_s, buf + 11))
+ 			reply_len = -1;
+-#endif /* CONFIG_WNM_AP */
++#endif /* CONFIG_AP && CONFIG_WNM_AP */
+ 	} else if (os_strcmp(buf, "FLUSH") == 0) {
+ 		wpa_supplicant_ctrl_iface_flush(wpa_s);
+ 	} else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) {
+diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
+index 52befd8f1..ace6c5530 100644
+--- a/wpa_supplicant/defconfig
++++ b/wpa_supplicant/defconfig
+@@ -10,8 +10,8 @@
+ # to override previous values of the variables.
+ 
+ 
+-# Uncomment following two lines and fix the paths if you have installed OpenSSL
+-# or GnuTLS in non-default location
++# Uncomment following two lines and fix the paths if you have installed TLS
++# libraries in a non-default location
+ #CFLAGS += -I/usr/local/openssl/include
+ #LIBS += -L/usr/local/openssl/lib
+ 
+@@ -20,6 +20,7 @@
+ # used to fix build issues on such systems (krb5.h not found).
+ #CFLAGS += -I/usr/include/kerberos
+ 
++
+ # Driver interface for generic Linux wireless extensions
+ # Note: WEXT is deprecated in the current Linux kernel version and no new
+ # functionality is added to it. nl80211-based interface is the new
+@@ -329,6 +330,7 @@ CONFIG_BACKEND=file
+ # openssl = OpenSSL (default)
+ # gnutls = GnuTLS
+ # internal = Internal TLSv1 implementation (experimental)
++# mbedtls = mbed TLS
+ # linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
+ # none = Empty template
+ #CONFIG_TLS=openssl
+diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
+index 0c17aaea4..3d35757bf 100644
+--- a/wpa_supplicant/eapol_test.c
++++ b/wpa_supplicant/eapol_test.c
+@@ -31,7 +31,12 @@
+ #include "ctrl_iface.h"
+ #include "pcsc_funcs.h"
+ #include "wpas_glue.h"
++#include "drivers/driver.h"
+ 
++void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
++			     union wpa_event_data *data);
++void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
++			     union wpa_event_data *data);
+ 
+ const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
+ 
+@@ -1328,6 +1333,10 @@ static void usage(void)
+ 	       "option several times.\n");
+ }
+ 
++extern void supplicant_event(void *ctx, enum wpa_event_type event,
++			     union wpa_event_data *data);
++extern void supplicant_event_global(void *ctx, enum wpa_event_type event,
++			     union wpa_event_data *data);
+ 
+ int main(int argc, char *argv[])
+ {
+@@ -1351,6 +1360,8 @@ int main(int argc, char *argv[])
+ 	if (os_program_init())
+ 		return -1;
+ 
++	wpa_supplicant_event = supplicant_event;
++	wpa_supplicant_event_global = supplicant_event_global;
+ 	hostapd_logger_register_cb(hostapd_logger_cb);
+ 
+ 	os_memset(&eapol_test, 0, sizeof(eapol_test));
+diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
+index bb0e95ba4..5c08d4a19 100644
+--- a/wpa_supplicant/events.c
++++ b/wpa_supplicant/events.c
+@@ -6038,8 +6038,8 @@ static void wpas_link_reconfig(struct wpa_supplicant *wpa_s)
+ }
+ 
+ 
+-void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+-			  union wpa_event_data *data)
++void supplicant_event(void *ctx, enum wpa_event_type event,
++		      union wpa_event_data *data)
+ {
+ 	struct wpa_supplicant *wpa_s = ctx;
+ 	int resched;
+@@ -6074,6 +6074,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ 		event_to_string(event), event);
+ #endif /* CONFIG_NO_STDOUT_DEBUG */
+ 
++	wpas_ucode_event(wpa_s, event, data);
+ 	switch (event) {
+ 	case EVENT_AUTH:
+ #ifdef CONFIG_FST
+@@ -6991,7 +6992,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ }
+ 
+ 
+-void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
++void supplicant_event_global(void *ctx, enum wpa_event_type event,
+ 				 union wpa_event_data *data)
+ {
+ 	struct wpa_supplicant *wpa_s;
+diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c
+index 9229eb51f..ee152c5b9 100644
+--- a/wpa_supplicant/main.c
++++ b/wpa_supplicant/main.c
+@@ -12,6 +12,7 @@
+ #endif /* __linux__ */
+ 
+ #include "common.h"
++#include "build_features.h"
+ #include "crypto/crypto.h"
+ #include "fst/fst.h"
+ #include "wpa_supplicant_i.h"
+@@ -202,7 +203,7 @@ int main(int argc, char *argv[])
+ 
+ 	for (;;) {
+ 		c = getopt(argc, argv,
+-			   "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvW");
++			   "b:Bc:C:D:de:f:g:G:hi:I:KLMm:nNo:O:p:P:qsTtuv::W");
+ 		if (c < 0)
+ 			break;
+ 		switch (c) {
+@@ -267,6 +268,9 @@ int main(int argc, char *argv[])
+ 			params.conf_p2p_dev = optarg;
+ 			break;
+ #endif /* CONFIG_P2P */
++		case 'n':
++			iface_count = 0;
++			break;
+ 		case 'o':
+ 			params.override_driver = optarg;
+ 			break;
+@@ -302,8 +306,12 @@ int main(int argc, char *argv[])
+ 			break;
+ #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
+ 		case 'v':
+-			printf("%s\n", wpa_supplicant_version);
+-			exitcode = 0;
++			if (optarg) {
++				exitcode = !has_feature(optarg);
++			} else {
++				printf("%s\n", wpa_supplicant_version);
++				exitcode = 0;
++			}
+ 			goto out;
+ 		case 'W':
+ 			params.wait_for_monitor++;
+diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
+index 85c1ea8ba..dabbb0334 100644
+--- a/wpa_supplicant/mesh.c
++++ b/wpa_supplicant/mesh.c
+@@ -506,6 +506,8 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
+ 			   frequency);
+ 		goto out_free;
+ 	}
++	if (conf->noscan)
++		ssid->noscan = 1;
+ 
+ 	if (ssid->mesh_basic_rates == NULL) {
+ 		/*
+@@ -630,6 +632,7 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s,
+ 
+ 	params->meshid = ssid->ssid;
+ 	params->meshid_len = ssid->ssid_len;
++	params->mcast_rate = ssid->mcast_rate;
+ 	ibss_mesh_setup_freq(wpa_s, ssid, &params->freq);
+ 	wpa_s->mesh_ht_enabled = !!params->freq.ht_enabled;
+ 	wpa_s->mesh_vht_enabled = !!params->freq.vht_enabled;
+diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
+index af00e7910..b239410e8 100644
+--- a/wpa_supplicant/wpa_cli.c
++++ b/wpa_supplicant/wpa_cli.c
+@@ -26,6 +26,15 @@
+ #include <cutils/properties.h>
+ #endif /* ANDROID */
+ 
++#ifndef CONFIG_P2P
++#define CONFIG_P2P
++#endif
++#ifndef CONFIG_AP
++#define CONFIG_AP
++#endif
++#ifndef CONFIG_MESH
++#define CONFIG_MESH
++#endif
+ 
+ static const char *const wpa_cli_version =
+ "wpa_cli v" VERSION_STR "\n"
+diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
+index 88f3f2a52..92efe5629 100644
+--- a/wpa_supplicant/wpa_priv.c
++++ b/wpa_supplicant/wpa_priv.c
+@@ -1042,8 +1042,8 @@ static void wpa_priv_send_ft_response(struct wpa_priv_interface *iface,
+ }
+ 
+ 
+-void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+-			  union wpa_event_data *data)
++static void supplicant_event(void *ctx, enum wpa_event_type event,
++			     union wpa_event_data *data)
+ {
+ 	struct wpa_priv_interface *iface = ctx;
+ 
+@@ -1106,7 +1106,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ }
+ 
+ 
+-void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
++void supplicant_event_global(void *ctx, enum wpa_event_type event,
+ 				 union wpa_event_data *data)
+ {
+ 	struct wpa_priv_global *global = ctx;
+@@ -1220,6 +1220,8 @@ int main(int argc, char *argv[])
+ 	if (os_program_init())
+ 		return -1;
+ 
++	wpa_supplicant_event = supplicant_event;
++	wpa_supplicant_event_global = supplicant_event_global;
+ 	wpa_priv_fd_workaround();
+ 
+ 	os_memset(&global, 0, sizeof(global));
+diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
+index 1e77493ef..32b178560 100644
+--- a/wpa_supplicant/wpa_supplicant.c
++++ b/wpa_supplicant/wpa_supplicant.c
+@@ -1151,6 +1151,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
+ 		sme_sched_obss_scan(wpa_s, 0);
+ 	}
+ 	wpa_s->wpa_state = state;
++	wpas_ucode_update_state(wpa_s);
+ 
+ #ifdef CONFIG_BGSCAN
+ 	if (state == WPA_COMPLETED && wpa_s->current_ssid != wpa_s->bgscan_ssid)
+@@ -2831,7 +2832,7 @@ static int drv_supports_vht(struct wpa_supplicant *wpa_s,
+ }
+ 
+ 
+-static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode)
++static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode, bool dfs_enabled)
+ {
+ 	int i;
+ 
+@@ -2840,7 +2841,10 @@ static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode)
+ 
+ 		chan = hw_get_channel_chan(mode, i, NULL);
+ 		if (!chan ||
+-		    chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
++		    chan->flag & HOSTAPD_CHAN_DISABLED)
++			return false;
++		
++		if (!dfs_enabled && chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
+ 			return false;
+ 	}
+ 
+@@ -2900,7 +2904,7 @@ static bool ibss_mesh_can_use_vht(struct wpa_supplicant *wpa_s,
+ 				  const struct wpa_ssid *ssid,
+ 				  struct hostapd_hw_modes *mode)
+ {
+-	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
++	if (mode->mode != HOSTAPD_MODE_IEEE80211A && !(ssid->noscan))
+ 		return false;
+ 
+ 	if (!drv_supports_vht(wpa_s, ssid))
+@@ -2967,13 +2971,13 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s,
+ 				   const struct wpa_ssid *ssid,
+ 				   struct hostapd_hw_modes *mode,
+ 				   struct hostapd_freq_params *freq,
+-				   int obss_scan) {
++				   int obss_scan, bool dfs_enabled) {
+ 	int chan_idx;
+ 	struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
+ 	int i, res;
+ 	unsigned int j;
+ 	static const int ht40plus[] = {
+-		36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
++		1, 2, 3, 4, 5, 6, 7, 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
+ 		149, 157, 165, 173, 184, 192
+ 	};
+ 	int ht40 = -1;
+@@ -2991,8 +2995,11 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s,
+ 		return;
+ 
+ 	/* Check primary channel flags */
+-	if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
++	if (pri_chan->flag & HOSTAPD_CHAN_DISABLED)
+ 		return;
++	if (pri_chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
++		if (!dfs_enabled)
++			return;
+ 
+ #ifdef CONFIG_HT_OVERRIDES
+ 	if (ssid->disable_ht40)
+@@ -3018,8 +3025,11 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s,
+ 		return;
+ 
+ 	/* Check secondary channel flags */
+-	if (sec_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
++	if (sec_chan->flag & HOSTAPD_CHAN_DISABLED)
+ 		return;
++	if (sec_chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
++		if (!dfs_enabled)
++			return;
+ 
+ 	if (ht40 == -1) {
+ 		if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS))
+@@ -3074,7 +3084,7 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
+ 				       const struct wpa_ssid *ssid,
+ 				       struct hostapd_hw_modes *mode,
+ 				       struct hostapd_freq_params *freq,
+-				       int ieee80211_mode, bool is_6ghz) {
++				       int ieee80211_mode, bool is_6ghz, bool dfs_enabled) {
+ 	static const int bw80[] = {
+ 		5180, 5260, 5500, 5580, 5660, 5745, 5825,
+ 		5955, 6035, 6115, 6195, 6275, 6355, 6435,
+@@ -3119,7 +3129,7 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
+ 		goto skip_80mhz;
+ 
+ 	/* Use 40 MHz if channel not usable */
+-	if (!ibss_mesh_is_80mhz_avail(channel, mode))
++	if (!ibss_mesh_is_80mhz_avail(channel, mode, dfs_enabled))
+ 		goto skip_80mhz;
+ 
+ 	chwidth = CONF_OPER_CHWIDTH_80MHZ;
+@@ -3133,7 +3143,7 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
+ 	if ((mode->he_capab[ieee80211_mode].phy_cap[
+ 		     HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+ 	     HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G) && is_6ghz &&
+-	    ibss_mesh_is_80mhz_avail(channel + 16, mode)) {
++	    ibss_mesh_is_80mhz_avail(channel + 16, mode, dfs_enabled)) {
+ 		for (j = 0; j < ARRAY_SIZE(bw160); j++) {
+ 			if (freq->freq == bw160[j]) {
+ 				chwidth = CONF_OPER_CHWIDTH_160MHZ;
+@@ -3161,10 +3171,12 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
+ 				if (!chan)
+ 					continue;
+ 
+-				if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+-						  HOSTAPD_CHAN_NO_IR |
+-						  HOSTAPD_CHAN_RADAR))
++				if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ 					continue;
++				if (chan->flag & (HOSTAPD_CHAN_RADAR |
++						  HOSTAPD_CHAN_NO_IR))
++					if (!dfs_enabled)
++						continue;
+ 
+ 				/* Found a suitable second segment for 80+80 */
+ 				chwidth = CONF_OPER_CHWIDTH_80P80MHZ;
+@@ -3216,12 +3228,17 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
+ 	int ieee80211_mode = wpas_mode_to_ieee80211_mode(ssid->mode);
+ 	enum hostapd_hw_mode hw_mode;
+ 	struct hostapd_hw_modes *mode = NULL;
+-	int obss_scan = 1;
++	int obss_scan = !(ssid->noscan);
+ 	u8 channel;
+ 	bool is_6ghz, is_24ghz;
++	bool dfs_enabled = wpa_s->conf->country[0] && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_RADAR);
+ 
+ 	freq->freq = ssid->frequency;
+ 
++	if (ssid->fixed_freq) {
++		obss_scan = 0;
++	}
++
+ 	if (ssid->mode == WPAS_MODE_IBSS && !ssid->fixed_freq) {
+ 		struct wpa_bss *bss = ibss_find_existing_bss(wpa_s, ssid);
+ 
+@@ -3259,11 +3276,13 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
+ 		freq->he_enabled = ibss_mesh_can_use_he(wpa_s, ssid, mode,
+ 							ieee80211_mode);
+ 	freq->channel = channel;
++	if (mode->mode == HOSTAPD_MODE_IEEE80211G && ssid->noscan)
++		ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan, dfs_enabled);
+ 	/* Setup higher BW only for 5 GHz */
+ 	if (mode->mode == HOSTAPD_MODE_IEEE80211A) {
+-		ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan);
++		ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan, dfs_enabled);
+ 		if (!ibss_mesh_select_80_160mhz(wpa_s, ssid, mode, freq,
+-						ieee80211_mode, is_6ghz))
++						ieee80211_mode, is_6ghz, dfs_enabled))
+ 			freq->he_enabled = freq->vht_enabled = false;
+ 	}
+ 
+@@ -4449,6 +4468,12 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
+ 			params.beacon_int = ssid->beacon_int;
+ 		else
+ 			params.beacon_int = wpa_s->conf->beacon_int;
++		int i = 0;
++		while (i < WLAN_SUPP_RATES_MAX) {
++			params.rates[i] = ssid->rates[i];
++			i++;
++		}
++		params.mcast_rate = ssid->mcast_rate;
+ 	}
+ 
+ 	if (bss && ssid->enable_edmg)
+@@ -6093,7 +6118,7 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent)
+ 	if (wpa_s == NULL)
+ 		return NULL;
+ 	wpa_s->scan_req = INITIAL_SCAN_REQ;
+-	wpa_s->scan_interval = 5;
++	wpa_s->scan_interval = 1;
+ 	wpa_s->new_connection = 1;
+ 	wpa_s->parent = parent ? parent : wpa_s;
+ 	wpa_s->p2pdev = wpa_s->parent;
+@@ -7809,7 +7834,6 @@ struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global,
+ 	return NULL;
+ }
+ 
+-
+ /**
+  * wpa_supplicant_match_existing - Match existing interfaces
+  * @global: Pointer to global data from wpa_supplicant_init()
+@@ -7844,6 +7868,11 @@ static int wpa_supplicant_match_existing(struct wpa_global *global)
+ 
+ #endif /* CONFIG_MATCH_IFACE */
+ 
++extern void supplicant_event(void *ctx, enum wpa_event_type event,
++			     union wpa_event_data *data);
++
++extern void supplicant_event_global(void *ctx, enum wpa_event_type event,
++ 				 union wpa_event_data *data);
+ 
+ /**
+  * wpa_supplicant_add_iface - Add a new network interface
+@@ -7926,6 +7955,9 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
+ 	}
+ #endif /* CONFIG_P2P */
+ 
++	wpas_ubus_add_bss(wpa_s);
++	wpas_ucode_add_bss(wpa_s);
++
+ 	return wpa_s;
+ }
+ 
+@@ -7952,6 +7984,9 @@ int wpa_supplicant_remove_iface(struct wpa_global *global,
+ 	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 */
+ 	prev = global->ifaces;
+ 	if (prev == wpa_s) {
+@@ -8100,6 +8135,8 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
+ #ifndef CONFIG_NO_WPA_MSG
+ 	wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
+ #endif /* CONFIG_NO_WPA_MSG */
++	wpa_supplicant_event = supplicant_event;
++	wpa_supplicant_event_global = supplicant_event_global;
+ 
+ 	if (params->wpa_debug_file_path)
+ 		wpa_debug_open_file(params->wpa_debug_file_path);
+@@ -8258,6 +8295,7 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
+ 
+ 	eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+ 			       wpas_periodic, global, NULL);
++	wpas_ucode_init(global);
+ 
+ 	return global;
+ }
+@@ -8330,6 +8368,8 @@ void wpa_supplicant_deinit(struct wpa_global *global)
+ 
+ 	wpas_notify_supplicant_deinitialized(global);
+ 
++	wpas_ucode_free();
++
+ 	eap_peer_unregister_methods();
+ #ifdef CONFIG_AP
+ 	eap_server_unregister_methods();
+diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
+index 48ec95fa5..952a3bd5a 100644
+--- a/wpa_supplicant/wpa_supplicant_i.h
++++ b/wpa_supplicant/wpa_supplicant_i.h
+@@ -21,6 +21,8 @@
+ #include "config_ssid.h"
+ #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;
+@@ -319,6 +321,8 @@ struct wpa_global {
+ #endif /* CONFIG_WIFI_DISPLAY */
+ 
+ 	struct psk_list_entry *add_psk; /* From group formation */
++
++	struct ubus_object ubus_global;
+ };
+ 
+ 
+@@ -693,6 +697,8 @@ struct wpa_supplicant {
+ 	unsigned char own_addr[ETH_ALEN];
+ 	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 */
+diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
+index 7b9cf7f9e..03748f4bb 100644
+--- a/wpa_supplicant/wps_supplicant.c
++++ b/wpa_supplicant/wps_supplicant.c
+@@ -33,6 +33,7 @@
+ #include "p2p/p2p.h"
+ #include "p2p_supplicant.h"
+ #include "wps_supplicant.h"
++#include "ubus.h"
+ 
+ 
+ #ifndef WPS_PIN_SCAN_IGNORE_SEL_REG
+@@ -401,6 +402,8 @@ static int wpa_supplicant_wps_cred(void *ctx,
+ 	wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
+ 			cred->cred_attr, cred->cred_attr_len);
+ 
++	wpas_ubus_notify(wpa_s, cred);
++
+ 	if (wpa_s->conf->wps_cred_processing == 1)
+ 		return 0;
+ 
+diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
+index aae3f7cb5..30b4e9105 100644
+--- a/wpa_supplicant/wps_supplicant.h
++++ b/wpa_supplicant/wps_supplicant.h
+@@ -9,6 +9,7 @@
+ #ifndef WPS_SUPPLICANT_H
+ #define WPS_SUPPLICANT_H
+ 
++struct wpa_bss;
+ struct wpa_scan_results;
+ 
+ #ifdef CONFIG_WPS
+@@ -16,8 +17,6 @@ struct wpa_scan_results;
+ #include "wps/wps.h"
+ #include "wps/wps_defs.h"
+ 
+-struct wpa_bss;
+-
+ struct wps_new_ap_settings {
+ 	const char *ssid_hex;
+ 	const char *auth;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0011-hostapd-MLO-move-mgmt-and-control-port-Tx-status-to-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0011-hostapd-MLO-move-mgmt-and-control-port-Tx-status-to-.patch
deleted file mode 100644
index 1d9c2fd..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0011-hostapd-MLO-move-mgmt-and-control-port-Tx-status-to-.patch
+++ /dev/null
@@ -1,136 +0,0 @@
-From 0b72d2a8002e79886433ee85fd23661ec4d3d731 Mon Sep 17 00:00:00 2001
-From: Sriram R <quic_srirrama@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:41 +0530
-Subject: [PATCH 011/104] hostapd: MLO: move mgmt and control port Tx status to
- per BSS handling
-
-Currently management and control port transmit status is handled on drv's
-first BSS only. However to support multiple MLDs there is requirement to
-handle it in on a given BSS.
-
-Add changes to use the passed BSS instead of always going with drv's first
-BSS.
-
-Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/drivers/driver_nl80211_event.c | 25 +++++++++++++------------
- 1 file changed, 13 insertions(+), 12 deletions(-)
-
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 1ca8b5bce..f5778cdaf 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -22,7 +22,7 @@
- 
- 
- static void
--nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
-+nl80211_control_port_frame_tx_status(struct i802_bss *bss,
- 				     const u8 *frame, size_t len,
- 				     struct nlattr *ack, struct nlattr *cookie);
- 
-@@ -1374,12 +1374,13 @@ static void mlme_event_mgmt(struct i802_bss *bss,
- }
- 
- 
--static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
-+static void mlme_event_mgmt_tx_status(struct i802_bss *bss,
- 				      struct nlattr *cookie, const u8 *frame,
- 				      size_t len, struct nlattr *ack)
- {
- 	union wpa_event_data event;
- 	const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) frame;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
- 	u16 fc = le_to_host16(hdr->frame_control);
- 	u64 cookie_val = 0;
- 
-@@ -1398,7 +1399,7 @@ static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
- 	    WPA_GET_BE16(frame + 2 * ETH_ALEN) == ETH_P_PAE) {
- 		wpa_printf(MSG_DEBUG,
- 			   "nl80211: Work around misdelivered control port TX status for EAPOL");
--		nl80211_control_port_frame_tx_status(drv, frame, len, ack,
-+		nl80211_control_port_frame_tx_status(bss, frame, len, ack,
- 						     cookie);
- 		return;
- 	}
-@@ -1434,7 +1435,7 @@ static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
- 	event.tx_status.ack = ack != NULL;
- 	event.tx_status.link_id = cookie_val == drv->send_frame_cookie ?
- 		drv->send_frame_link_id : NL80211_DRV_LINK_ID_NA;
--	wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
-+	wpa_supplicant_event(bss->ctx, EVENT_TX_STATUS, &event);
- }
- 
- 
-@@ -1742,7 +1743,7 @@ static void mlme_event(struct i802_bss *bss,
- 				nla_len(frame), link_id);
- 		break;
- 	case NL80211_CMD_FRAME_TX_STATUS:
--		mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame),
-+		mlme_event_mgmt_tx_status(bss, cookie, nla_data(frame),
- 					  nla_len(frame), ack);
- 		break;
- 	case NL80211_CMD_UNPROT_DEAUTHENTICATE:
-@@ -3652,8 +3653,7 @@ static void nl80211_sta_opmode_change_event(struct wpa_driver_nl80211_data *drv,
- }
- 
- 
--static void nl80211_control_port_frame(struct wpa_driver_nl80211_data *drv,
--				       struct nlattr **tb)
-+static void nl80211_control_port_frame(struct i802_bss *bss, struct nlattr **tb)
- {
- 	u8 *src_addr;
- 	u16 ethertype;
-@@ -3682,7 +3682,7 @@ static void nl80211_control_port_frame(struct wpa_driver_nl80211_data *drv,
- 			   MAC2STR(src_addr));
- 		break;
- 	case ETH_P_PAE:
--		drv_event_eapol_rx2(drv->ctx, src_addr,
-+		drv_event_eapol_rx2(bss->ctx, src_addr,
- 				    nla_data(tb[NL80211_ATTR_FRAME]),
- 				    nla_len(tb[NL80211_ATTR_FRAME]),
- 				    encrypted, link_id);
-@@ -3698,10 +3698,11 @@ static void nl80211_control_port_frame(struct wpa_driver_nl80211_data *drv,
- 
- 
- static void
--nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
-+nl80211_control_port_frame_tx_status(struct i802_bss *bss,
- 				     const u8 *frame, size_t len,
- 				     struct nlattr *ack, struct nlattr *cookie)
- {
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
- 	union wpa_event_data event;
- 
- 	if (!cookie || len < ETH_HLEN)
-@@ -3720,7 +3721,7 @@ nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
- 		nla_get_u64(cookie) == drv->eapol_tx_cookie ?
- 		drv->eapol_tx_link_id : NL80211_DRV_LINK_ID_NA;
- 
--	wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
-+	wpa_supplicant_event(bss->ctx, EVENT_EAPOL_TX_STATUS, &event);
- }
- 
- 
-@@ -4065,7 +4066,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
- 	case NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS:
- 		if (!frame)
- 			break;
--		nl80211_control_port_frame_tx_status(drv,
-+		nl80211_control_port_frame_tx_status(bss,
- 						     nla_data(frame),
- 						     nla_len(frame),
- 						     tb[NL80211_ATTR_ACK],
-@@ -4238,7 +4239,7 @@ int process_bss_event(struct nl_msg *msg, void *arg)
- 		nl80211_external_auth(bss->drv, tb);
- 		break;
- 	case NL80211_CMD_CONTROL_PORT_FRAME:
--		nl80211_control_port_frame(bss->drv, tb);
-+		nl80211_control_port_frame(bss, tb);
- 		break;
- 	default:
- 		wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0011-mtk-hostapd-sync-with-wireless-next.git-include-uapi.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0011-mtk-hostapd-sync-with-wireless-next.git-include-uapi.patch
new file mode 100644
index 0000000..ffd8333
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0011-mtk-hostapd-sync-with-wireless-next.git-include-uapi.patch
@@ -0,0 +1,1274 @@
+From 02fb89a020d0bab2eac528f3d49379b535ebeda2 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 31 Jul 2024 10:42:08 +0800
+Subject: [PATCH 011/126] mtk: hostapd: sync with wireless-next.git
+ include/uapi/linux/nl80211.h
+
+This brings in nl80211 definitions as of 2024-07-11.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ src/drivers/driver_nl80211_event.c |   1 +
+ src/drivers/nl80211_copy.h         | 537 ++++++++++++++++++++---------
+ 2 files changed, 370 insertions(+), 168 deletions(-)
+
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 768c72905..1787dbd99 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -186,6 +186,7 @@ static const char * nl80211_command_to_string(enum nl80211_commands cmd)
+ 	C2S(NL80211_CMD_REMOVE_LINK_STA)
+ 	C2S(NL80211_CMD_SET_HW_TIMESTAMP)
+ 	C2S(NL80211_CMD_LINKS_REMOVED)
++	C2S(NL80211_CMD_SET_TID_TO_LINK_MAPPING)
+ 	C2S(__NL80211_CMD_AFTER_LAST)
+ 	}
+ #undef C2S
+diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
+index dced2c49d..f97f5adc8 100644
+--- a/src/drivers/nl80211_copy.h
++++ b/src/drivers/nl80211_copy.h
+@@ -11,7 +11,7 @@
+  * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
+  * Copyright 2008 Colin McCabe <colin@cozybit.com>
+  * Copyright 2015-2017	Intel Deutschland GmbH
+- * Copyright (C) 2018-2023 Intel Corporation
++ * Copyright (C) 2018-2024 Intel Corporation
+  *
+  * Permission to use, copy, modify, and/or distribute this software for any
+  * purpose with or without fee is hereby granted, provided that the above
+@@ -72,7 +72,7 @@
+  * For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS
+  * and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows:
+  *  - a setup station entry is added, not yet authorized, without any rate
+- *    or capability information, this just exists to avoid race conditions
++ *    or capability information; this just exists to avoid race conditions
+  *  - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid
+  *    to add rate and capability information to the station and at the same
+  *    time mark it authorized.
+@@ -87,7 +87,7 @@
+  * DOC: Frame transmission/registration support
+  *
+  * Frame transmission and registration support exists to allow userspace
+- * management entities such as wpa_supplicant react to management frames
++ * management entities such as wpa_supplicant to react to management frames
+  * that are not being handled by the kernel. This includes, for example,
+  * certain classes of action frames that cannot be handled in the kernel
+  * for various reasons.
+@@ -113,7 +113,7 @@
+  *
+  * Frame transmission allows userspace to send for example the required
+  * responses to action frames. It is subject to some sanity checking,
+- * but many frames can be transmitted. When a frame was transmitted, its
++ * but many frames can be transmitted. When a frame is transmitted, its
+  * status is indicated to the sending socket.
+  *
+  * For more technical details, see the corresponding command descriptions
+@@ -123,7 +123,7 @@
+ /**
+  * DOC: Virtual interface / concurrency capabilities
+  *
+- * Some devices are able to operate with virtual MACs, they can have
++ * Some devices are able to operate with virtual MACs; they can have
+  * more than one virtual interface. The capability handling for this
+  * is a bit complex though, as there may be a number of restrictions
+  * on the types of concurrency that are supported.
+@@ -135,7 +135,7 @@
+  * Once concurrency is desired, more attributes must be observed:
+  * To start with, since some interface types are purely managed in
+  * software, like the AP-VLAN type in mac80211 for example, there's
+- * an additional list of these, they can be added at any time and
++ * an additional list of these; they can be added at any time and
+  * are only restricted by some semantic restrictions (e.g. AP-VLAN
+  * cannot be added without a corresponding AP interface). This list
+  * is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute.
+@@ -164,7 +164,7 @@
+  * Packet coalesce feature helps to reduce number of received interrupts
+  * to host by buffering these packets in firmware/hardware for some
+  * predefined time. Received interrupt will be generated when one of the
+- * following events occur.
++ * following events occurs.
+  * a) Expiration of hardware timer whose expiration time is set to maximum
+  * coalescing delay of matching coalesce rule.
+  * b) Coalescing buffer in hardware reaches its limit.
+@@ -174,7 +174,7 @@
+  * rule.
+  * a) Maximum coalescing delay
+  * b) List of packet patterns which needs to be matched
+- * c) Condition for coalescence. pattern 'match' or 'no match'
++ * c) Condition for coalescence: pattern 'match' or 'no match'
+  * Multiple such rules can be created.
+  */
+ 
+@@ -213,7 +213,7 @@
+ /**
+  * DOC: FILS shared key authentication offload
+  *
+- * FILS shared key authentication offload can be advertized by drivers by
++ * FILS shared key authentication offload can be advertised by drivers by
+  * setting @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD flag. The drivers that support
+  * FILS shared key authentication offload should be able to construct the
+  * authentication and association frames for FILS shared key authentication and
+@@ -239,7 +239,7 @@
+  * The PMKSA can be maintained in userspace persistently so that it can be used
+  * later after reboots or wifi turn off/on also.
+  *
+- * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertized by a FILS
++ * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertised by a FILS
+  * capable AP supporting PMK caching. It specifies the scope within which the
+  * PMKSAs are cached in an ESS. %NL80211_CMD_SET_PMKSA and
+  * %NL80211_CMD_DEL_PMKSA are enhanced to allow support for PMKSA caching based
+@@ -290,12 +290,12 @@
+  * If the configuration needs to be applied for specific peer then the MAC
+  * address of the peer needs to be passed in %NL80211_ATTR_MAC, otherwise the
+  * configuration will be applied for all the connected peers in the vif except
+- * any peers that have peer specific configuration for the TID by default; if
+- * the %NL80211_TID_CONFIG_ATTR_OVERRIDE flag is set, peer specific values
++ * any peers that have peer-specific configuration for the TID by default; if
++ * the %NL80211_TID_CONFIG_ATTR_OVERRIDE flag is set, peer-specific values
+  * will be overwritten.
+  *
+- * All this configuration is valid only for STA's current connection
+- * i.e. the configuration will be reset to default when the STA connects back
++ * All this configuration is valid only for STA's current connection,
++ * i.e., the configuration will be reset to default when the STA connects back
+  * after disconnection/roaming, and this configuration will be cleared when
+  * the interface goes down.
+  */
+@@ -413,8 +413,8 @@
+  *	are like for %NL80211_CMD_SET_BEACON, and additionally parameters that
+  *	do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL,
+  *	%NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID,
+- *	%NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE,
+- *	%NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS,
++ *	%NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
++ *	%NL80211_ATTR_CIPHER_SUITE_GROUP, %NL80211_ATTR_WPA_VERSIONS,
+  *	%NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY,
+  *	%NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT,
+  *	%NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS.
+@@ -438,23 +438,19 @@
+  *	%NL80211_ATTR_REASON_CODE can optionally be used to specify which type
+  *	of disconnection indication should be sent to the station
+  *	(Deauthentication or Disassociation frame and reason code for that
+- *	frame).
++ *	frame). %NL80211_ATTR_MLO_LINK_ID can be used optionally to remove
++ *	stations connected and using at least that link as one of its links.
+  *
+  * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
+- * 	destination %NL80211_ATTR_MAC on the interface identified by
+- * 	%NL80211_ATTR_IFINDEX.
++ *	destination %NL80211_ATTR_MAC on the interface identified by
++ *	%NL80211_ATTR_IFINDEX.
+  * @NL80211_CMD_SET_MPATH:  Set mesh path attributes for mesh path to
+- * 	destination %NL80211_ATTR_MAC on the interface identified by
+- * 	%NL80211_ATTR_IFINDEX.
++ *	destination %NL80211_ATTR_MAC on the interface identified by
++ *	%NL80211_ATTR_IFINDEX.
+  * @NL80211_CMD_NEW_MPATH: Create a new mesh path for the destination given by
+  *	%NL80211_ATTR_MAC via %NL80211_ATTR_MPATH_NEXT_HOP.
+  * @NL80211_CMD_DEL_MPATH: Delete a mesh path to the destination given by
+  *	%NL80211_ATTR_MAC.
+- * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the
+- *	interface identified by %NL80211_ATTR_IFINDEX.
+- * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC
+- *	or, if no MAC address given, all mesh paths, on the interface identified
+- *	by %NL80211_ATTR_IFINDEX.
+  * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by
+  *	%NL80211_ATTR_IFINDEX.
+  *
+@@ -475,15 +471,15 @@
+  *	after being queried by the kernel. CRDA replies by sending a regulatory
+  *	domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our
+  *	current alpha2 if it found a match. It also provides
+- * 	NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each
+- * 	regulatory rule is a nested set of attributes  given by
+- * 	%NL80211_ATTR_REG_RULE_FREQ_[START|END] and
+- * 	%NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by
+- * 	%NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and
+- * 	%NL80211_ATTR_REG_RULE_POWER_MAX_EIRP.
++ *	NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each
++ *	regulatory rule is a nested set of attributes  given by
++ *	%NL80211_ATTR_REG_RULE_FREQ_[START|END] and
++ *	%NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by
++ *	%NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and
++ *	%NL80211_ATTR_REG_RULE_POWER_MAX_EIRP.
+  * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain
+- * 	to the specified ISO/IEC 3166-1 alpha2 country code. The core will
+- * 	store this as a valid request and then query userspace for it.
++ *	to the specified ISO/IEC 3166-1 alpha2 country code. The core will
++ *	store this as a valid request and then query userspace for it.
+  *
+  * @NL80211_CMD_GET_MESH_CONFIG: Get mesh networking properties for the
+  *	interface identified by %NL80211_ATTR_IFINDEX
+@@ -521,7 +517,7 @@
+  *	%NL80211_ATTR_SCHED_SCAN_PLANS. If %NL80211_ATTR_SCHED_SCAN_PLANS is
+  *	not specified and only %NL80211_ATTR_SCHED_SCAN_INTERVAL is specified,
+  *	scheduled scan will run in an infinite loop with the specified interval.
+- *	These attributes are mutually exculsive,
++ *	These attributes are mutually exclusive,
+  *	i.e. NL80211_ATTR_SCHED_SCAN_INTERVAL must not be passed if
+  *	NL80211_ATTR_SCHED_SCAN_PLANS is defined.
+  *	If for some reason scheduled scan is aborted by the driver, all scan
+@@ -552,7 +548,7 @@
+  *	%NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface
+  *	is brought down while a scheduled scan was running.
+  *
+- * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation
++ * @NL80211_CMD_GET_SURVEY: get survey results, e.g. channel occupation
+  *      or noise level
+  * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to
+  *	NL80211_CMD_GET_SURVEY and on the "scan" multicast group)
+@@ -563,40 +559,41 @@
+  *	using %NL80211_ATTR_SSID, %NL80211_ATTR_FILS_CACHE_ID,
+  *	%NL80211_ATTR_PMKID, and %NL80211_ATTR_PMK in case of FILS
+  *	authentication where %NL80211_ATTR_FILS_CACHE_ID is the identifier
+- *	advertized by a FILS capable AP identifying the scope of PMKSA in an
++ *	advertised by a FILS capable AP identifying the scope of PMKSA in an
+  *	ESS.
+  * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC
+  *	(for the BSSID) and %NL80211_ATTR_PMKID or using %NL80211_ATTR_SSID,
+  *	%NL80211_ATTR_FILS_CACHE_ID, and %NL80211_ATTR_PMKID in case of FILS
+- *	authentication.
++ *	authentication. Additionally in case of SAE offload and OWE offloads
++ *	PMKSA entry can be deleted using %NL80211_ATTR_SSID.
+  * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries.
+  *
+  * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain
+- * 	has been changed and provides details of the request information
+- * 	that caused the change such as who initiated the regulatory request
+- * 	(%NL80211_ATTR_REG_INITIATOR), the wiphy_idx
+- * 	(%NL80211_ATTR_REG_ALPHA2) on which the request was made from if
+- * 	the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or
+- * 	%NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain
+- * 	set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is
+- * 	%NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on
+- * 	to (%NL80211_ATTR_REG_ALPHA2).
++ *	has been changed and provides details of the request information
++ *	that caused the change such as who initiated the regulatory request
++ *	(%NL80211_ATTR_REG_INITIATOR), the wiphy_idx
++ *	(%NL80211_ATTR_REG_ALPHA2) on which the request was made from if
++ *	the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or
++ *	%NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain
++ *	set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is
++ *	%NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on
++ *	to (%NL80211_ATTR_REG_ALPHA2).
+  * @NL80211_CMD_REG_BEACON_HINT: indicates to userspace that an AP beacon
+- * 	has been found while world roaming thus enabling active scan or
+- * 	any mode of operation that initiates TX (beacons) on a channel
+- * 	where we would not have been able to do either before. As an example
+- * 	if you are world roaming (regulatory domain set to world or if your
+- * 	driver is using a custom world roaming regulatory domain) and while
+- * 	doing a passive scan on the 5 GHz band you find an AP there (if not
+- * 	on a DFS channel) you will now be able to actively scan for that AP
+- * 	or use AP mode on your card on that same channel. Note that this will
+- * 	never be used for channels 1-11 on the 2 GHz band as they are always
+- * 	enabled world wide. This beacon hint is only sent if your device had
+- * 	either disabled active scanning or beaconing on a channel. We send to
+- * 	userspace the wiphy on which we removed a restriction from
+- * 	(%NL80211_ATTR_WIPHY) and the channel on which this occurred
+- * 	before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER)
+- * 	the beacon hint was processed.
++ *	has been found while world roaming thus enabling active scan or
++ *	any mode of operation that initiates TX (beacons) on a channel
++ *	where we would not have been able to do either before. As an example
++ *	if you are world roaming (regulatory domain set to world or if your
++ *	driver is using a custom world roaming regulatory domain) and while
++ *	doing a passive scan on the 5 GHz band you find an AP there (if not
++ *	on a DFS channel) you will now be able to actively scan for that AP
++ *	or use AP mode on your card on that same channel. Note that this will
++ *	never be used for channels 1-11 on the 2 GHz band as they are always
++ *	enabled world wide. This beacon hint is only sent if your device had
++ *	either disabled active scanning or beaconing on a channel. We send to
++ *	userspace the wiphy on which we removed a restriction from
++ *	(%NL80211_ATTR_WIPHY) and the channel on which this occurred
++ *	before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER)
++ *	the beacon hint was processed.
+  *
+  * @NL80211_CMD_AUTHENTICATE: authentication request and notification.
+  *	This command is used both as a command (request to authenticate) and
+@@ -607,7 +604,7 @@
+  *	BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify
+  *	the SSID (mainly for association, but is included in authentication
+  *	request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ +
+- *	%NL80211_ATTR_WIPHY_FREQ_OFFSET is used to specify the frequence of the
++ *	%NL80211_ATTR_WIPHY_FREQ_OFFSET is used to specify the frequency of the
+  *	channel in MHz. %NL80211_ATTR_AUTH_TYPE is used to specify the
+  *	authentication type. %NL80211_ATTR_IE is used to define IEs
+  *	(VendorSpecificInfo, but also including RSN IE and FT IEs) to be added
+@@ -816,7 +813,7 @@
+  *	reached.
+  * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ
+  *	and the attributes determining channel width) the given interface
+- *	(identifed by %NL80211_ATTR_IFINDEX) shall operate on.
++ *	(identified by %NL80211_ATTR_IFINDEX) shall operate on.
+  *	In case multiple channels are supported by the device, the mechanism
+  *	with which it switches channels is implementation-defined.
+  *	When a monitor interface is given, it can only switch channel while
+@@ -888,7 +885,7 @@
+  *	inform userspace of the new replay counter.
+  *
+  * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace
+- *	of PMKSA caching dandidates.
++ *	of PMKSA caching candidates.
+  *
+  * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup).
+  *	In addition, this can be used as an event to request userspace to take
+@@ -924,7 +921,7 @@
+  *
+  * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface
+  *	by sending a null data frame to it and reporting when the frame is
+- *	acknowleged. This is used to allow timing out inactive clients. Uses
++ *	acknowledged. This is used to allow timing out inactive clients. Uses
+  *	%NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a
+  *	direct reply with an %NL80211_ATTR_COOKIE that is later used to match
+  *	up the event with the request. The event includes the same data and
+@@ -1118,7 +1115,7 @@
+  *	current configuration is not changed.  If it is present but
+  *	set to zero, the configuration is changed to don't-care
+  *	(i.e. the device can decide what to do).
+- * @NL80211_CMD_NAN_FUNC_MATCH: Notification sent when a match is reported.
++ * @NL80211_CMD_NAN_MATCH: Notification sent when a match is reported.
+  *	This will contain a %NL80211_ATTR_NAN_MATCH nested attribute and
+  *	%NL80211_ATTR_COOKIE.
+  *
+@@ -1135,11 +1132,15 @@
+  * @NL80211_CMD_DEL_PMK: For offloaded 4-Way handshake, delete the previously
+  *	configured PMK for the authenticator address identified by
+  *	%NL80211_ATTR_MAC.
+- * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates an 802.1X FT roam was
+- *	completed successfully. Drivers that support 4 way handshake offload
+- *	should send this event after indicating 802.1X FT assocation with
+- *	%NL80211_CMD_ROAM. If the 4 way handshake failed %NL80211_CMD_DISCONNECT
+- *	should be indicated instead.
++ * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates port is authorized and
++ *	open for regular data traffic. For STA/P2P-client, this event is sent
++ *	with AP MAC address and for AP/P2P-GO, the event carries the STA/P2P-
++ *	client MAC address.
++ *	Drivers that support 4 way handshake offload should send this event for
++ *	STA/P2P-client after successful 4-way HS or after 802.1X FT following
++ *	NL80211_CMD_CONNECT or NL80211_CMD_ROAM. Drivers using AP/P2P-GO 4-way
++ *	handshake offload should send this event on successful completion of
++ *	4-way handshake with the peer (STA/P2P-client).
+  * @NL80211_CMD_CONTROL_PORT_FRAME: Control Port (e.g. PAE) frame TX request
+  *	and RX notification.  This command is used both as a request to transmit
+  *	a control port frame and as a notification that a control port frame
+@@ -1323,6 +1324,11 @@
+  *	Multi-Link reconfiguration. %NL80211_ATTR_MLO_LINKS is used to provide
+  *	information about the removed STA MLD setup links.
+  *
++ * @NL80211_CMD_SET_TID_TO_LINK_MAPPING: Set the TID to Link Mapping for a
++ *      non-AP MLD station. The %NL80211_ATTR_MLO_TTLM_DLINK and
++ *      %NL80211_ATTR_MLO_TTLM_ULINK attributes are used to specify the
++ *      TID to Link mapping for downlink/uplink traffic.
++ *
+  * @NL80211_CMD_MAX: highest used command number
+  * @__NL80211_CMD_AFTER_LAST: internal use
+  */
+@@ -1578,6 +1584,8 @@ enum nl80211_commands {
+ 
+ 	NL80211_CMD_LINKS_REMOVED,
+ 
++	NL80211_CMD_SET_TID_TO_LINK_MAPPING,
++
+ 	/* add new commands above here */
+ 
+ 	/* used to define NL80211_CMD_MAX below */
+@@ -1702,21 +1710,21 @@ enum nl80211_commands {
+  *	(see &enum nl80211_plink_action).
+  * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path.
+  * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path
+- * 	info given for %NL80211_CMD_GET_MPATH, nested attribute described at
++ *	info given for %NL80211_CMD_GET_MPATH, nested attribute described at
+  *	&enum nl80211_mpath_info.
+  *
+  * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of
+  *      &enum nl80211_mntr_flags.
+  *
+  * @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the
+- * 	current regulatory domain should be set to or is already set to.
+- * 	For example, 'CR', for Costa Rica. This attribute is used by the kernel
+- * 	to query the CRDA to retrieve one regulatory domain. This attribute can
+- * 	also be used by userspace to query the kernel for the currently set
+- * 	regulatory domain. We chose an alpha2 as that is also used by the
+- * 	IEEE-802.11 country information element to identify a country.
+- * 	Users can also simply ask the wireless core to set regulatory domain
+- * 	to a specific alpha2.
++ *	current regulatory domain should be set to or is already set to.
++ *	For example, 'CR', for Costa Rica. This attribute is used by the kernel
++ *	to query the CRDA to retrieve one regulatory domain. This attribute can
++ *	also be used by userspace to query the kernel for the currently set
++ *	regulatory domain. We chose an alpha2 as that is also used by the
++ *	IEEE-802.11 country information element to identify a country.
++ *	Users can also simply ask the wireless core to set regulatory domain
++ *	to a specific alpha2.
+  * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory
+  *	rules.
+  *
+@@ -1759,9 +1767,9 @@ enum nl80211_commands {
+  * @NL80211_ATTR_BSS: scan result BSS
+  *
+  * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain
+- * 	currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_*
++ *	currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_*
+  * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently
+- * 	set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*)
++ *	set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*)
+  *
+  * @NL80211_ATTR_SUPPORTED_COMMANDS: wiphy attribute that specifies
+  *	an array of command numbers (i.e. a mapping index to command number)
+@@ -1780,15 +1788,15 @@ enum nl80211_commands {
+  *	a u32
+  *
+  * @NL80211_ATTR_FREQ_BEFORE: A channel which has suffered a regulatory change
+- * 	due to considerations from a beacon hint. This attribute reflects
+- * 	the state of the channel _before_ the beacon hint processing. This
+- * 	attributes consists of a nested attribute containing
+- * 	NL80211_FREQUENCY_ATTR_*
++ *	due to considerations from a beacon hint. This attribute reflects
++ *	the state of the channel _before_ the beacon hint processing. This
++ *	attributes consists of a nested attribute containing
++ *	NL80211_FREQUENCY_ATTR_*
+  * @NL80211_ATTR_FREQ_AFTER: A channel which has suffered a regulatory change
+- * 	due to considerations from a beacon hint. This attribute reflects
+- * 	the state of the channel _after_ the beacon hint processing. This
+- * 	attributes consists of a nested attribute containing
+- * 	NL80211_FREQUENCY_ATTR_*
++ *	due to considerations from a beacon hint. This attribute reflects
++ *	the state of the channel _after_ the beacon hint processing. This
++ *	attributes consists of a nested attribute containing
++ *	NL80211_FREQUENCY_ATTR_*
+  *
+  * @NL80211_ATTR_CIPHER_SUITES: a set of u32 values indicating the supported
+  *	cipher suites
+@@ -1835,7 +1843,7 @@ enum nl80211_commands {
+  *	using %CMD_CONTROL_PORT_FRAME.  If control port routing over NL80211 is
+  *	to be used then userspace must also use the %NL80211_ATTR_SOCKET_OWNER
+  *	flag. When used with %NL80211_ATTR_CONTROL_PORT_NO_PREAUTH, pre-auth
+- *	frames are not forwared over the control port.
++ *	frames are not forwarded over the control port.
+  *
+  * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver.
+  *	We recommend using nested, driver-specific attributes within this.
+@@ -1849,12 +1857,6 @@ enum nl80211_commands {
+  *	that protected APs should be used. This is also used with NEW_BEACON to
+  *	indicate that the BSS is to use protection.
+  *
+- * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT, ASSOCIATE, and NEW_BEACON
+- *	to indicate which unicast key ciphers will be used with the connection
+- *	(an array of u32).
+- * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+- *	indicate which group key cipher will be used with the connection (a
+- *	u32).
+  * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+  *	indicate which WPA version(s) the AP we want to associate with is using
+  *	(a u32 with flags from &enum nl80211_wpa_versions).
+@@ -1885,6 +1887,7 @@ enum nl80211_commands {
+  *	with %NL80211_KEY_* sub-attributes
+  *
+  * @NL80211_ATTR_PID: Process ID of a network namespace.
++ * @NL80211_ATTR_NETNS_FD: File descriptor of a network namespace.
+  *
+  * @NL80211_ATTR_GENERATION: Used to indicate consistent snapshots for
+  *	dumps. This number increases whenever the object list being
+@@ -1939,6 +1942,7 @@ enum nl80211_commands {
+  *
+  * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was
+  *	acknowledged by the recipient.
++ * @NL80211_ATTR_ACK_SIGNAL: Station's ack signal strength (s32)
+  *
+  * @NL80211_ATTR_PS_STATE: powersave state, using &enum nl80211_ps_state values.
+  *
+@@ -1972,10 +1976,10 @@ enum nl80211_commands {
+  *	bit. Depending on which antennas are selected in the bitmap, 802.11n
+  *	drivers can derive which chainmasks to use (if all antennas belonging to
+  *	a particular chain are disabled this chain should be disabled) and if
+- *	a chain has diversity antennas wether diversity should be used or not.
++ *	a chain has diversity antennas whether diversity should be used or not.
+  *	HT capabilities (STBC, TX Beamforming, Antenna selection) can be
+  *	derived from the available chains after applying the antenna mask.
+- *	Non-802.11n drivers can derive wether to use diversity or not.
++ *	Non-802.11n drivers can derive whether to use diversity or not.
+  *	Drivers may reject configurations or RX/TX mask combinations they cannot
+  *	support by returning -EINVAL.
+  *
+@@ -2048,6 +2052,10 @@ enum nl80211_commands {
+  * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported
+  *	interface combinations. In each nested item, it contains attributes
+  *	defined in &enum nl80211_if_combination_attrs.
++ *	If the wiphy uses multiple radios (@NL80211_ATTR_WIPHY_RADIOS is set),
++ *	this attribute contains the interface combinations of the first radio.
++ *	See @NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS for the global wiphy
++ *	combinations for the sum of all radios.
+  * @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like
+  *	%NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that
+  *	are managed in software: interfaces of these types aren't subject to
+@@ -2136,6 +2144,9 @@ enum nl80211_commands {
+  * @NL80211_ATTR_DISABLE_HE: Force HE capable interfaces to disable
+  *      this feature during association. This is a flag attribute.
+  *	Currently only supported in mac80211 drivers.
++ * @NL80211_ATTR_DISABLE_EHT: Force EHT capable interfaces to disable
++ *      this feature during association. This is a flag attribute.
++ *	Currently only supported in mac80211 drivers.
+  * @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the
+  *      ATTR_HT_CAPABILITY to which attention should be paid.
+  *      Currently, only mac80211 NICs support this feature.
+@@ -2145,6 +2156,12 @@ enum nl80211_commands {
+  *      All values are treated as suggestions and may be ignored
+  *      by the driver as required.  The actual values may be seen in
+  *      the station debugfs ht_caps file.
++ * @NL80211_ATTR_VHT_CAPABILITY_MASK: Specify which bits of the
++ *      ATTR_VHT_CAPABILITY to which attention should be paid.
++ *      Currently, only mac80211 NICs support this feature.
++ *      All values are treated as suggestions and may be ignored
++ *      by the driver as required.  The actual values may be seen in
++ *      the station debugfs vht_caps file.
+  *
+  * @NL80211_ATTR_DFS_REGION: region for regulatory rules which this country
+  *    abides to when initiating radiation on DFS channels. A country maps
+@@ -2403,7 +2420,7 @@ enum nl80211_commands {
+  *	scheduled scan is started.  Or the delay before a WoWLAN
+  *	net-detect scan is started, counting from the moment the
+  *	system is suspended.  This value is a u32, in seconds.
+-
++ *
+  * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
+  *      is operating in an indoor environment.
+  *
+@@ -2545,7 +2562,7 @@ enum nl80211_commands {
+  *	from successful FILS authentication and is used with
+  *	%NL80211_CMD_CONNECT.
+  *
+- * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertized by a FILS AP
++ * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertised by a FILS AP
+  *	identifying the scope of PMKSAs. This is used with
+  *	@NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA.
+  *
+@@ -2826,6 +2843,31 @@ enum nl80211_commands {
+  * @NL80211_ATTR_MLO_LINK_DISABLED: Flag attribute indicating that the link is
+  *	disabled.
+  *
++ * @NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA: Include BSS usage data, i.e.
++ *	include BSSes that can only be used in restricted scenarios and/or
++ *	cannot be used at all.
++ *
++ * @NL80211_ATTR_MLO_TTLM_DLINK: Binary attribute specifying the downlink TID to
++ *      link mapping. The length is 8 * sizeof(u16). For each TID the link
++ *      mapping is as defined in section 9.4.2.314 (TID-To-Link Mapping element)
++ *      in Draft P802.11be_D4.0.
++ * @NL80211_ATTR_MLO_TTLM_ULINK: Binary attribute specifying the uplink TID to
++ *      link mapping. The length is 8 * sizeof(u16). For each TID the link
++ *      mapping is as defined in section 9.4.2.314 (TID-To-Link Mapping element)
++ *      in Draft P802.11be_D4.0.
++ *
++ * @NL80211_ATTR_ASSOC_SPP_AMSDU: flag attribute used with
++ *	%NL80211_CMD_ASSOCIATE indicating the SPP A-MSDUs
++ *	are used on this connection
++ *
++ * @NL80211_ATTR_WIPHY_RADIOS: Nested attribute describing physical radios
++ *	belonging to this wiphy. See &enum nl80211_wiphy_radio_attrs.
++ *
++ * @NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS: Nested attribute listing the
++ *	supported interface combinations for all radios combined. In each
++ *	nested item, it contains attributes defined in
++ *	&enum nl80211_if_combination_attrs.
++ *
+  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
+  * @NL80211_ATTR_MAX: highest attribute number currently defined
+  * @__NL80211_ATTR_AFTER_LAST: internal use
+@@ -3364,6 +3406,16 @@ enum nl80211_attrs {
+ 
+ 	NL80211_ATTR_MLO_LINK_DISABLED,
+ 
++	NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA,
++
++	NL80211_ATTR_MLO_TTLM_DLINK,
++	NL80211_ATTR_MLO_TTLM_ULINK,
++
++	NL80211_ATTR_ASSOC_SPP_AMSDU,
++
++	NL80211_ATTR_WIPHY_RADIOS,
++	NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS,
++
+ 	/* add attributes here, update the policy in nl80211.c */
+ 
+ 	__NL80211_ATTR_AFTER_LAST,
+@@ -3504,6 +3556,7 @@ enum nl80211_iftype {
+  * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers
+  *	that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a
+  *	previously added station into associated state
++ * @NL80211_STA_FLAG_SPP_AMSDU: station supports SPP A-MSDUs
+  * @NL80211_STA_FLAG_MAX: highest station flag number currently defined
+  * @__NL80211_STA_FLAG_AFTER_LAST: internal use
+  */
+@@ -3516,6 +3569,7 @@ enum nl80211_sta_flags {
+ 	NL80211_STA_FLAG_AUTHENTICATED,
+ 	NL80211_STA_FLAG_TDLS_PEER,
+ 	NL80211_STA_FLAG_ASSOCIATED,
++	NL80211_STA_FLAG_SPP_AMSDU,
+ 
+ 	/* keep last */
+ 	__NL80211_STA_FLAG_AFTER_LAST,
+@@ -3526,7 +3580,7 @@ enum nl80211_sta_flags {
+  * enum nl80211_sta_p2p_ps_status - station support of P2P PS
+  *
+  * @NL80211_P2P_PS_UNSUPPORTED: station doesn't support P2P PS mechanism
+- * @@NL80211_P2P_PS_SUPPORTED: station supports P2P PS mechanism
++ * @NL80211_P2P_PS_SUPPORTED: station supports P2P PS mechanism
+  * @NUM_NL80211_P2P_PS_STATUS: number of values
+  */
+ enum nl80211_sta_p2p_ps_status {
+@@ -3564,9 +3618,9 @@ enum nl80211_he_gi {
+ 
+ /**
+  * enum nl80211_he_ltf - HE long training field
+- * @NL80211_RATE_INFO_HE_1xLTF: 3.2 usec
+- * @NL80211_RATE_INFO_HE_2xLTF: 6.4 usec
+- * @NL80211_RATE_INFO_HE_4xLTF: 12.8 usec
++ * @NL80211_RATE_INFO_HE_1XLTF: 3.2 usec
++ * @NL80211_RATE_INFO_HE_2XLTF: 6.4 usec
++ * @NL80211_RATE_INFO_HE_4XLTF: 12.8 usec
+  */
+ enum nl80211_he_ltf {
+ 	NL80211_RATE_INFO_HE_1XLTF,
+@@ -3681,7 +3735,7 @@ enum nl80211_eht_ru_alloc {
+  * @NL80211_RATE_INFO_HE_GI: HE guard interval identifier
+  *	(u8, see &enum nl80211_he_gi)
+  * @NL80211_RATE_INFO_HE_DCM: HE DCM value (u8, 0/1)
+- * @NL80211_RATE_INFO_RU_ALLOC: HE RU allocation, if not present then
++ * @NL80211_RATE_INFO_HE_RU_ALLOC: HE RU allocation, if not present then
+  *	non-OFDMA was used (u8, see &enum nl80211_he_ru_alloc)
+  * @NL80211_RATE_INFO_320_MHZ_WIDTH: 320 MHz bitrate
+  * @NL80211_RATE_INFO_EHT_MCS: EHT MCS index (u8, 0-15)
+@@ -3784,7 +3838,7 @@ enum nl80211_sta_bss_param {
+  *	(u64, to this station)
+  * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm)
+  * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute
+- * 	containing info as possible, see &enum nl80211_rate_info
++ *	containing info as possible, see &enum nl80211_rate_info
+  * @NL80211_STA_INFO_RX_PACKETS: total received packet (MSDUs and MMPDUs)
+  *	(u32, from this station)
+  * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (MSDUs and MMPDUs)
+@@ -3813,8 +3867,8 @@ enum nl80211_sta_bss_param {
+  *	Contains a nested array of signal strength attributes (u8, dBm)
+  * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average
+  *	Same format as NL80211_STA_INFO_CHAIN_SIGNAL.
+- * @NL80211_STA_EXPECTED_THROUGHPUT: expected throughput considering also the
+- *	802.11 header (u32, kbps)
++ * @NL80211_STA_INFO_EXPECTED_THROUGHPUT: expected throughput considering also
++ *	the 802.11 header (u32, kbps)
+  * @NL80211_STA_INFO_RX_DROP_MISC: RX packets dropped for unspecified reasons
+  *	(u64)
+  * @NL80211_STA_INFO_BEACON_RX: number of beacons received from this peer (u64)
+@@ -4000,7 +4054,7 @@ enum nl80211_mpath_flags {
+  * @NL80211_MPATH_INFO_METRIC: metric (cost) of this mesh path
+  * @NL80211_MPATH_INFO_EXPTIME: expiration time for the path, in msec from now
+  * @NL80211_MPATH_INFO_FLAGS: mesh path flags, enumerated in
+- * 	&enum nl80211_mpath_flags;
++ *	&enum nl80211_mpath_flags;
+  * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec
+  * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries
+  * @NL80211_MPATH_INFO_HOP_COUNT: hop count to destination
+@@ -4140,7 +4194,7 @@ enum nl80211_band_attr {
+  * @NL80211_WMMR_CW_MAX: Maximum contention window slot.
+  * @NL80211_WMMR_AIFSN: Arbitration Inter Frame Space.
+  * @NL80211_WMMR_TXOP: Maximum allowed tx operation time.
+- * @nl80211_WMMR_MAX: highest possible wmm rule.
++ * @NL80211_WMMR_MAX: highest possible wmm rule.
+  * @__NL80211_WMMR_LAST: Internal use.
+  */
+ enum nl80211_wmm_rule {
+@@ -4162,15 +4216,16 @@ enum nl80211_wmm_rule {
+  * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current
+  *	regulatory domain.
+  * @NL80211_FREQUENCY_ATTR_NO_IR: no mechanisms that initiate radiation
+- * 	are permitted on this channel, this includes sending probe
+- * 	requests, or modes of operation that require beaconing.
++ *	are permitted on this channel, this includes sending probe
++ *	requests, or modes of operation that require beaconing.
++ * @__NL80211_FREQUENCY_ATTR_NO_IBSS: obsolete, same as _NO_IR
+  * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory
+  *	on this channel in current regulatory domain.
+  * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm
+  *	(100 * dBm).
+  * @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS
+  *	(enum nl80211_dfs_state)
+- * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in miliseconds for how long
++ * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in milliseconds for how long
+  *	this channel is in this DFS state.
+  * @NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: HT40- isn't possible with this
+  *	channel as the control channel
+@@ -4226,6 +4281,19 @@ enum nl80211_wmm_rule {
+  *	in current regulatory domain.
+  * @NL80211_FREQUENCY_ATTR_PSD: Power spectral density (in dBm) that
+  *	is allowed on this channel in current regulatory domain.
++ * @NL80211_FREQUENCY_ATTR_DFS_CONCURRENT: Operation on this channel is
++ *	allowed for peer-to-peer or adhoc communication under the control
++ *	of a DFS master which operates on the same channel (FCC-594280 D01
++ *	Section B.3). Should be used together with %NL80211_RRF_DFS only.
++ * @NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT: Client connection to VLP AP
++ *	not allowed using this channel
++ * @NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP
++ *	not allowed using this channel
++ * @NL80211_FREQUENCY_ATTR_CAN_MONITOR: This channel can be used in monitor
++ *	mode despite other (regulatory) restrictions, even if the channel is
++ *	otherwise completely disabled.
++ * @NL80211_FREQUENCY_ATTR_ALLOW_6GHZ_VLP_AP: This channel can be used for a
++ *	very low power (VLP) AP, despite being NO_IR.
+  * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
+  *	currently defined
+  * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
+@@ -4265,6 +4333,11 @@ enum nl80211_frequency_attr {
+ 	NL80211_FREQUENCY_ATTR_NO_320MHZ,
+ 	NL80211_FREQUENCY_ATTR_NO_EHT,
+ 	NL80211_FREQUENCY_ATTR_PSD,
++	NL80211_FREQUENCY_ATTR_DFS_CONCURRENT,
++	NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT,
++	NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT,
++	NL80211_FREQUENCY_ATTR_CAN_MONITOR,
++	NL80211_FREQUENCY_ATTR_ALLOW_6GHZ_VLP_AP,
+ 
+ 	/* keep last */
+ 	__NL80211_FREQUENCY_ATTR_AFTER_LAST,
+@@ -4277,6 +4350,10 @@ enum nl80211_frequency_attr {
+ #define NL80211_FREQUENCY_ATTR_NO_IR		NL80211_FREQUENCY_ATTR_NO_IR
+ #define NL80211_FREQUENCY_ATTR_GO_CONCURRENT \
+ 					NL80211_FREQUENCY_ATTR_IR_CONCURRENT
++#define NL80211_FREQUENCY_ATTR_NO_UHB_VLP_CLIENT \
++	NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT
++#define NL80211_FREQUENCY_ATTR_NO_UHB_AFC_CLIENT \
++	NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT
+ 
+ /**
+  * enum nl80211_bitrate_attr - bitrate attributes
+@@ -4299,16 +4376,16 @@ enum nl80211_bitrate_attr {
+ };
+ 
+ /**
+- * enum nl80211_initiator - Indicates the initiator of a reg domain request
++ * enum nl80211_reg_initiator - Indicates the initiator of a reg domain request
+  * @NL80211_REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world
+- * 	regulatory domain.
++ *	regulatory domain.
+  * @NL80211_REGDOM_SET_BY_USER: User asked the wireless core to set the
+- * 	regulatory domain.
++ *	regulatory domain.
+  * @NL80211_REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the
+- * 	wireless core it thinks its knows the regulatory domain we should be in.
++ *	wireless core it thinks its knows the regulatory domain we should be in.
+  * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an
+- * 	802.11 country information element with regulatory information it
+- * 	thinks we should consider. cfg80211 only processes the country
++ *	802.11 country information element with regulatory information it
++ *	thinks we should consider. cfg80211 only processes the country
+  *	code from the IE, and relies on the regulatory domain information
+  *	structure passed by userspace (CRDA) from our wireless-regdb.
+  *	If a channel is enabled but the country code indicates it should
+@@ -4327,11 +4404,11 @@ enum nl80211_reg_initiator {
+  *	to a specific country. When this is set you can count on the
+  *	ISO / IEC 3166 alpha2 country code being valid.
+  * @NL80211_REGDOM_TYPE_WORLD: the regulatory set domain is the world regulatory
+- * 	domain.
++ *	domain.
+  * @NL80211_REGDOM_TYPE_CUSTOM_WORLD: the regulatory domain set is a custom
+- * 	driver specific world regulatory domain. These do not apply system-wide
+- * 	and are only applicable to the individual devices which have requested
+- * 	them to be applied.
++ *	driver specific world regulatory domain. These do not apply system-wide
++ *	and are only applicable to the individual devices which have requested
++ *	them to be applied.
+  * @NL80211_REGDOM_TYPE_INTERSECTION: the regulatory domain set is the product
+  *	of an intersection between two regulatory domains -- the previously
+  *	set regulatory domain on the system and the last accepted regulatory
+@@ -4348,21 +4425,21 @@ enum nl80211_reg_type {
+  * enum nl80211_reg_rule_attr - regulatory rule attributes
+  * @__NL80211_REG_RULE_ATTR_INVALID: attribute number 0 is reserved
+  * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional
+- * 	considerations for a given frequency range. These are the
+- * 	&enum nl80211_reg_rule_flags.
++ *	considerations for a given frequency range. These are the
++ *	&enum nl80211_reg_rule_flags.
+  * @NL80211_ATTR_FREQ_RANGE_START: starting frequencry for the regulatory
+- * 	rule in KHz. This is not a center of frequency but an actual regulatory
+- * 	band edge.
++ *	rule in KHz. This is not a center of frequency but an actual regulatory
++ *	band edge.
+  * @NL80211_ATTR_FREQ_RANGE_END: ending frequency for the regulatory rule
+- * 	in KHz. This is not a center a frequency but an actual regulatory
+- * 	band edge.
++ *	in KHz. This is not a center a frequency but an actual regulatory
++ *	band edge.
+  * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this
+  *	frequency range, in KHz.
+  * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain
+- * 	for a given frequency range. The value is in mBi (100 * dBi).
+- * 	If you don't have one then don't send this.
++ *	for a given frequency range. The value is in mBi (100 * dBi).
++ *	If you don't have one then don't send this.
+  * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for
+- * 	a given frequency range. The value is in mBm (100 * dBm).
++ *	a given frequency range. The value is in mBm (100 * dBm).
+  * @NL80211_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+  *	If not present or 0 default CAC time will be used.
+  * @NL80211_ATTR_POWER_RULE_PSD: power spectral density (in dBm).
+@@ -4414,14 +4491,7 @@ enum nl80211_reg_rule_attr {
+  *	value as specified by &struct nl80211_bss_select_rssi_adjust.
+  * @NL80211_SCHED_SCAN_MATCH_ATTR_BSSID: BSSID to be used for matching
+  *	(this cannot be used together with SSID).
+- * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Nested attribute that carries the
+- *	band specific minimum rssi thresholds for the bands defined in
+- *	enum nl80211_band. The minimum rssi threshold value(s32) specific to a
+- *	band shall be encapsulated in attribute with type value equals to one
+- *	of the NL80211_BAND_* defined in enum nl80211_band. For example, the
+- *	minimum rssi threshold value for 2.4GHZ band shall be encapsulated
+- *	within an attribute of type NL80211_BAND_2GHZ. And one or more of such
+- *	attributes will be nested within this attribute.
++ * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Obsolete
+  * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
+  *	attribute number currently defined
+  * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
+@@ -4434,7 +4504,7 @@ enum nl80211_sched_scan_match_attr {
+ 	NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI,
+ 	NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST,
+ 	NL80211_SCHED_SCAN_MATCH_ATTR_BSSID,
+-	NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI,
++	NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI, /* obsolete */
+ 
+ 	/* keep last */
+ 	__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
+@@ -4456,8 +4526,9 @@ enum nl80211_sched_scan_match_attr {
+  * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links
+  * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links
+  * @NL80211_RRF_NO_IR: no mechanisms that initiate radiation are allowed,
+- * 	this includes probe requests or modes of operation that require
+- * 	beaconing.
++ *	this includes probe requests or modes of operation that require
++ *	beaconing.
++ * @__NL80211_RRF_NO_IBSS: obsolete, same as NO_IR
+  * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated
+  *	base on contiguous rules and wider channels will be allowed to cross
+  *	multiple contiguous/overlapping frequency ranges.
+@@ -4470,6 +4541,14 @@ enum nl80211_sched_scan_match_attr {
+  * @NL80211_RRF_NO_320MHZ: 320MHz operation not allowed
+  * @NL80211_RRF_NO_EHT: EHT operation not allowed
+  * @NL80211_RRF_PSD: Ruleset has power spectral density value
++ * @NL80211_RRF_DFS_CONCURRENT: Operation on this channel is allowed for
++ *	peer-to-peer or adhoc communication under the control of a DFS master
++ *	which operates on the same channel (FCC-594280 D01 Section B.3).
++ *	Should be used together with %NL80211_RRF_DFS only.
++ * @NL80211_RRF_NO_6GHZ_VLP_CLIENT: Client connection to VLP AP not allowed
++ * @NL80211_RRF_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP not allowed
++ * @NL80211_RRF_ALLOW_6GHZ_VLP_AP: Very low power (VLP) AP can be permitted
++ *	despite NO_IR configuration.
+  */
+ enum nl80211_reg_rule_flags {
+ 	NL80211_RRF_NO_OFDM		= 1<<0,
+@@ -4491,6 +4570,10 @@ enum nl80211_reg_rule_flags {
+ 	NL80211_RRF_NO_320MHZ		= 1<<18,
+ 	NL80211_RRF_NO_EHT		= 1<<19,
+ 	NL80211_RRF_PSD			= 1<<20,
++	NL80211_RRF_DFS_CONCURRENT	= 1<<21,
++	NL80211_RRF_NO_6GHZ_VLP_CLIENT	= 1<<22,
++	NL80211_RRF_NO_6GHZ_AFC_CLIENT	= 1<<23,
++	NL80211_RRF_ALLOW_6GHZ_VLP_AP	= 1<<24,
+ };
+ 
+ #define NL80211_RRF_PASSIVE_SCAN	NL80211_RRF_NO_IR
+@@ -4499,6 +4582,8 @@ enum nl80211_reg_rule_flags {
+ #define NL80211_RRF_NO_HT40		(NL80211_RRF_NO_HT40MINUS |\
+ 					 NL80211_RRF_NO_HT40PLUS)
+ #define NL80211_RRF_GO_CONCURRENT	NL80211_RRF_IR_CONCURRENT
++#define NL80211_RRF_NO_UHB_VLP_CLIENT	NL80211_RRF_NO_6GHZ_VLP_CLIENT
++#define NL80211_RRF_NO_UHB_AFC_CLIENT	NL80211_RRF_NO_6GHZ_AFC_CLIENT
+ 
+ /* For backport compatibility with older userspace */
+ #define NL80211_RRF_NO_IR_ALL		(NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS)
+@@ -4645,8 +4730,8 @@ enum nl80211_mntr_flags {
+  *	alternate between Active and Doze states, but may not wake up
+  *	for neighbor's beacons.
+  *
+- * @__NL80211_MESH_POWER_AFTER_LAST - internal use
+- * @NL80211_MESH_POWER_MAX - highest possible power save level
++ * @__NL80211_MESH_POWER_AFTER_LAST: internal use
++ * @NL80211_MESH_POWER_MAX: highest possible power save level
+  */
+ 
+ enum nl80211_mesh_power_mode {
+@@ -5027,6 +5112,36 @@ enum nl80211_bss_scan_width {
+ 	NL80211_BSS_CHAN_WIDTH_2,
+ };
+ 
++/**
++ * enum nl80211_bss_use_for - bitmap indicating possible BSS use
++ * @NL80211_BSS_USE_FOR_NORMAL: Use this BSS for normal "connection",
++ *	including IBSS/MBSS depending on the type.
++ * @NL80211_BSS_USE_FOR_MLD_LINK: This BSS can be used as a link in an
++ *	MLO connection. Note that for an MLO connection, all links including
++ *	the assoc link must have this flag set, and the assoc link must
++ *	additionally have %NL80211_BSS_USE_FOR_NORMAL set.
++ */
++enum nl80211_bss_use_for {
++	NL80211_BSS_USE_FOR_NORMAL = 1 << 0,
++	NL80211_BSS_USE_FOR_MLD_LINK = 1 << 1,
++};
++
++/**
++ * enum nl80211_bss_cannot_use_reasons - reason(s) connection to a
++ *	BSS isn't possible
++ * @NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY: NSTR nonprimary links aren't
++ *	supported by the device, and this BSS entry represents one.
++ * @NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH: STA is not supporting
++ *	the AP power type (SP, VLP, AP) that the AP uses.
++ */
++enum nl80211_bss_cannot_use_reasons {
++	NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY	= 1 << 0,
++	NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH	= 1 << 1,
++};
++
++#define NL80211_BSS_CANNOT_USE_UHB_PWR_MISMATCH \
++	NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH
++
+ /**
+  * enum nl80211_bss - netlink attributes for a BSS
+  *
+@@ -5079,6 +5194,14 @@ enum nl80211_bss_scan_width {
+  * @NL80211_BSS_FREQUENCY_OFFSET: frequency offset in KHz
+  * @NL80211_BSS_MLO_LINK_ID: MLO link ID of the BSS (u8).
+  * @NL80211_BSS_MLD_ADDR: MLD address of this BSS if connected to it.
++ * @NL80211_BSS_USE_FOR: u32 bitmap attribute indicating what the BSS can be
++ *	used for, see &enum nl80211_bss_use_for.
++ * @NL80211_BSS_CANNOT_USE_REASONS: Indicates the reason that this BSS cannot
++ *	be used for all or some of the possible uses by the device reporting it,
++ *	even though its presence was detected.
++ *	This is a u64 attribute containing a bitmap of values from
++ *	&enum nl80211_cannot_use_reasons, note that the attribute may be missing
++ *	if no reasons are specified.
+  * @__NL80211_BSS_AFTER_LAST: internal
+  * @NL80211_BSS_MAX: highest BSS attribute
+  */
+@@ -5106,6 +5229,8 @@ enum nl80211_bss {
+ 	NL80211_BSS_FREQUENCY_OFFSET,
+ 	NL80211_BSS_MLO_LINK_ID,
+ 	NL80211_BSS_MLD_ADDR,
++	NL80211_BSS_USE_FOR,
++	NL80211_BSS_CANNOT_USE_REASONS,
+ 
+ 	/* keep last */
+ 	__NL80211_BSS_AFTER_LAST,
+@@ -5454,7 +5579,7 @@ enum nl80211_tx_rate_setting {
+  *	(%NL80211_TID_CONFIG_ATTR_TIDS, %NL80211_TID_CONFIG_ATTR_OVERRIDE).
+  * @NL80211_TID_CONFIG_ATTR_PEER_SUPP: same as the previous per-vif one, but
+  *	per peer instead.
+- * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribue, if set indicates
++ * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribute, if set indicates
+  *	that the new configuration overrides all previous peer
+  *	configurations, otherwise previous peer specific configurations
+  *	should be left untouched.
+@@ -5626,7 +5751,7 @@ struct nl80211_pattern_support {
+  *	"TCP connection wakeup" for more details. This is a nested attribute
+  *	containing the exact information for establishing and keeping alive
+  *	the TCP connection.
+- * @NL80211_WOWLAN_TRIG_TCP_WAKEUP_MATCH: For wakeup reporting only, the
++ * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH: For wakeup reporting only, the
+  *	wakeup packet was received on the TCP connection
+  * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST: For wakeup reporting only, the
+  *	TCP connection was lost or failed to be established
+@@ -5655,6 +5780,8 @@ struct nl80211_pattern_support {
+  *	%NL80211_ATTR_SCAN_FREQUENCIES contains more than one
+  *	frequency, it means that the match occurred in more than one
+  *	channel.
++ * @NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC: For wakeup reporting only.
++ *	Wake up happened due to unprotected deauth or disassoc frame in MFP.
+  * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers
+  * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number
+  *
+@@ -5682,6 +5809,7 @@ enum nl80211_wowlan_triggers {
+ 	NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS,
+ 	NL80211_WOWLAN_TRIG_NET_DETECT,
+ 	NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS,
++	NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC,
+ 
+ 	/* keep last */
+ 	NUM_NL80211_WOWLAN_TRIG,
+@@ -5837,7 +5965,7 @@ enum nl80211_attr_coalesce_rule {
+ 
+ /**
+  * enum nl80211_coalesce_condition - coalesce rule conditions
+- * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns
++ * @NL80211_COALESCE_CONDITION_MATCH: coalesce Rx packets when patterns
+  *	in a rule are matched.
+  * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns
+  *	in a rule are not matched.
+@@ -5936,7 +6064,7 @@ enum nl80211_if_combination_attrs {
+  * enum nl80211_plink_state - state of a mesh peer link finite state machine
+  *
+  * @NL80211_PLINK_LISTEN: initial state, considered the implicit
+- *	state of non existent mesh peer links
++ *	state of non-existent mesh peer links
+  * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to
+  *	this mesh peer
+  * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received
+@@ -5972,7 +6100,7 @@ enum nl80211_plink_state {
+  * @NL80211_PLINK_ACTION_BLOCK: block traffic from this mesh peer
+  * @NUM_NL80211_PLINK_ACTIONS: number of possible actions
+  */
+-enum plink_actions {
++enum nl80211_plink_action {
+ 	NL80211_PLINK_ACTION_NO_ACTION,
+ 	NL80211_PLINK_ACTION_OPEN,
+ 	NL80211_PLINK_ACTION_BLOCK,
+@@ -6229,7 +6357,7 @@ enum nl80211_feature_flags {
+  *	request to use RRM (see %NL80211_ATTR_USE_RRM) with
+  *	%NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests, which will set
+  *	the ASSOC_REQ_USE_RRM flag in the association request even if
+- *	NL80211_FEATURE_QUIET is not advertized.
++ *	NL80211_FEATURE_QUIET is not advertised.
+  * @NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER: This device supports MU-MIMO air
+  *	sniffer which means that it can be configured to hear packets from
+  *	certain groups which can be configured by the
+@@ -6241,13 +6369,15 @@ enum nl80211_feature_flags {
+  *	the BSS that the interface that requested the scan is connected to
+  *	(if available).
+  * @NL80211_EXT_FEATURE_BSS_PARENT_TSF: Per BSS, this driver reports the
+- *	time the last beacon/probe was received. The time is the TSF of the
+- *	BSS that the interface that requested the scan is connected to
+- *	(if available).
++ *	time the last beacon/probe was received. For a non-MLO connection, the
++ *	time is the TSF of the BSS that the interface that requested the scan is
++ *	connected to (if available). For an MLO connection, the time is the TSF
++ *	of the BSS corresponding with link ID specified in the scan request (if
++ *	specified).
+  * @NL80211_EXT_FEATURE_SET_SCAN_DWELL: This driver supports configuration of
+  *	channel dwell time.
+  * @NL80211_EXT_FEATURE_BEACON_RATE_LEGACY: Driver supports beacon rate
+- *	configuration (AP/mesh), supporting a legacy (non HT/VHT) rate.
++ *	configuration (AP/mesh), supporting a legacy (non-HT/VHT) rate.
+  * @NL80211_EXT_FEATURE_BEACON_RATE_HT: Driver supports beacon rate
+  *	configuration (AP/mesh) with HT rates.
+  * @NL80211_EXT_FEATURE_BEACON_RATE_VHT: Driver supports beacon rate
+@@ -6297,6 +6427,7 @@ enum nl80211_feature_flags {
+  *	receiving control port frames over nl80211 instead of the netdevice.
+  * @NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT: This driver/device supports
+  *	(average) ACK signal strength reporting.
++ * @NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT: Backward-compatible ID
+  * @NL80211_EXT_FEATURE_TXQS: Driver supports FQ-CoDel-enabled intermediate
+  *      TXQs.
+  * @NL80211_EXT_FEATURE_SCAN_RANDOM_SN: Driver/device supports randomizing the
+@@ -6321,8 +6452,7 @@ enum nl80211_feature_flags {
+  * @NL80211_EXT_FEATURE_AP_PMKSA_CACHING: Driver/device supports PMKSA caching
+  *	(set/del PMKSA operations) in AP mode.
+  *
+- * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Driver supports
+- *	filtering of sched scan results using band specific RSSI thresholds.
++ * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Obsolete
+  *
+  * @NL80211_EXT_FEATURE_STA_TX_PWR: This driver supports controlling tx power
+  *	to a station.
+@@ -6426,6 +6556,16 @@ enum nl80211_feature_flags {
+  * @NL80211_EXT_FEATURE_OWE_OFFLOAD_AP: Driver/Device wants to do OWE DH IE
+  *	handling in AP mode.
+  *
++ * @NL80211_EXT_FEATURE_DFS_CONCURRENT: The device supports peer-to-peer or
++ *	ad hoc operation on DFS channels under the control of a concurrent
++ *	DFS master on the same channel as described in FCC-594280 D01
++ *	(Section B.3). This, for example, allows P2P GO and P2P clients to
++ *	operate on DFS channels as long as there's a concurrent BSS connection.
++ *
++ * @NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT: The driver has support for SPP
++ *	(signaling and payload protected) A-MSDUs and this shall be advertised
++ *	in the RSNXE.
++ *
+  * @NUM_NL80211_EXT_FEATURES: number of extended features.
+  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
+  */
+@@ -6467,7 +6607,7 @@ enum nl80211_ext_feature_index {
+ 	NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER,
+ 	NL80211_EXT_FEATURE_AIRTIME_FAIRNESS,
+ 	NL80211_EXT_FEATURE_AP_PMKSA_CACHING,
+-	NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD,
++	NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD, /* obsolete */
+ 	NL80211_EXT_FEATURE_EXT_KEY_ID,
+ 	NL80211_EXT_FEATURE_STA_TX_PWR,
+ 	NL80211_EXT_FEATURE_SAE_OFFLOAD,
+@@ -6499,6 +6639,8 @@ enum nl80211_ext_feature_index {
+ 	NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA,
+ 	NL80211_EXT_FEATURE_OWE_OFFLOAD,
+ 	NL80211_EXT_FEATURE_OWE_OFFLOAD_AP,
++	NL80211_EXT_FEATURE_DFS_CONCURRENT,
++	NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT,
+ 
+ 	/* add new features before the definition below */
+ 	NUM_NL80211_EXT_FEATURES,
+@@ -6583,7 +6725,7 @@ enum nl80211_timeout_reason {
+  *	request parameters IE in the probe request
+  * @NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe responses
+  * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE: send probe request frames at
+- *	rate of at least 5.5M. In case non OCE AP is discovered in the channel,
++ *	rate of at least 5.5M. In case non-OCE AP is discovered in the channel,
+  *	only the first probe req in the channel will be sent in high rate.
+  * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: allow probe request
+  *	tx deferral (dot11FILSProbeDelay shall be set to 15ms)
+@@ -6619,7 +6761,7 @@ enum nl80211_timeout_reason {
+  *	received on the 2.4/5 GHz channels to actively scan only the 6GHz
+  *	channels on which APs are expected to be found. Note that when not set,
+  *	the scan logic would scan all 6GHz channels, but since transmission of
+- *	probe requests on non PSC channels is limited, it is highly likely that
++ *	probe requests on non-PSC channels is limited, it is highly likely that
+  *	these channels would passively be scanned. Also note that when the flag
+  *	is set, in addition to the colocated APs, PSC channels would also be
+  *	scanned if the user space has asked for it.
+@@ -6669,6 +6811,8 @@ enum nl80211_acl_policy {
+  * @NL80211_SMPS_STATIC: static SMPS (use a single antenna)
+  * @NL80211_SMPS_DYNAMIC: dynamic smps (start with a single antenna and
+  *	turn on other antennas after CTS/RTS).
++ * @__NL80211_SMPS_AFTER_LAST: internal
++ * @NL80211_SMPS_MAX: highest used enumeration
+  */
+ enum nl80211_smps_mode {
+ 	NL80211_SMPS_OFF,
+@@ -6890,6 +7034,8 @@ enum nl80211_bss_select_attr {
+  * @NL80211_NAN_FUNC_PUBLISH: function is publish
+  * @NL80211_NAN_FUNC_SUBSCRIBE: function is subscribe
+  * @NL80211_NAN_FUNC_FOLLOW_UP: function is follow-up
++ * @__NL80211_NAN_FUNC_TYPE_AFTER_LAST: internal use
++ * @NL80211_NAN_FUNC_MAX_TYPE: internal use
+  */
+ enum nl80211_nan_function_type {
+ 	NL80211_NAN_FUNC_PUBLISH,
+@@ -6951,7 +7097,7 @@ enum nl80211_nan_func_term_reason {
+  *	The instance ID for the follow up Service Discovery Frame. This is u8.
+  * @NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID: relevant if the function's type
+  *	is follow up. This is a u8.
+- *	The requestor instance ID for the follow up Service Discovery Frame.
++ *	The requester instance ID for the follow up Service Discovery Frame.
+  * @NL80211_NAN_FUNC_FOLLOW_UP_DEST: the MAC address of the recipient of the
+  *	follow up Service Discovery Frame. This is a binary attribute.
+  * @NL80211_NAN_FUNC_CLOSE_RANGE: is this function limited for devices in a
+@@ -7050,7 +7196,7 @@ enum nl80211_nan_match_attributes {
+ };
+ 
+ /**
+- * nl80211_external_auth_action - Action to perform with external
++ * enum nl80211_external_auth_action - Action to perform with external
+  *     authentication request. Used by NL80211_ATTR_EXTERNAL_AUTH_ACTION.
+  * @NL80211_EXTERNAL_AUTH_START: Start the authentication.
+  * @NL80211_EXTERNAL_AUTH_ABORT: Abort the ongoing authentication.
+@@ -7068,7 +7214,7 @@ enum nl80211_external_auth_action {
+  * @NL80211_FTM_RESP_ATTR_LCI: The content of Measurement Report Element
+  *	(9.4.2.22 in 802.11-2016) with type 8 - LCI (9.4.2.22.10),
+  *	i.e. starting with the measurement token
+- * @NL80211_FTM_RESP_ATTR_CIVIC: The content of Measurement Report Element
++ * @NL80211_FTM_RESP_ATTR_CIVICLOC: The content of Measurement Report Element
+  *	(9.4.2.22 in 802.11-2016) with type 11 - Civic (Section 9.4.2.22.13),
+  *	i.e. starting with the measurement token
+  * @__NL80211_FTM_RESP_ATTR_LAST: Internal
+@@ -7341,7 +7487,7 @@ enum nl80211_peer_measurement_attrs {
+  * @NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED: flag attribute indicating if
+  *	trigger based ranging measurement is supported
+  * @NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED: flag attribute indicating
+- *	if non trigger based ranging measurement is supported
++ *	if non-trigger-based ranging measurement is supported
+  *
+  * @NUM_NL80211_PMSR_FTM_CAPA_ATTR: internal
+  * @NL80211_PMSR_FTM_CAPA_ATTR_MAX: highest attribute number
+@@ -7395,7 +7541,7 @@ enum nl80211_peer_measurement_ftm_capa {
+  *      if neither %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED nor
+  *	%NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set, EDCA based
+  *	ranging will be used.
+- * @NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED: request non trigger based
++ * @NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED: request non-trigger-based
+  *	ranging measurement (flag)
+  *	This attribute and %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED are
+  *	mutually exclusive.
+@@ -7473,7 +7619,7 @@ enum nl80211_peer_measurement_ftm_failure_reasons {
+  * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS: number of FTM Request frames
+  *	transmitted (u32, optional)
+  * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES: number of FTM Request frames
+- *	that were acknowleged (u32, optional)
++ *	that were acknowledged (u32, optional)
+  * @NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME: retry time received from the
+  *	busy peer (u32, seconds)
+  * @NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP: actual number of bursts exponent
+@@ -7711,6 +7857,7 @@ enum nl80211_sae_pwe_mechanism {
+  *
+  * @NL80211_SAR_TYPE_POWER: power limitation specified in 0.25dBm unit
+  *
++ * @NUM_NL80211_SAR_TYPE: internal
+  */
+ enum nl80211_sar_type {
+ 	NL80211_SAR_TYPE_POWER,
+@@ -7724,6 +7871,8 @@ enum nl80211_sar_type {
+ /**
+  * enum nl80211_sar_attrs - Attributes for SAR spec
+  *
++ * @__NL80211_SAR_ATTR_INVALID: Invalid
++ *
+  * @NL80211_SAR_ATTR_TYPE: the SAR type as defined in &enum nl80211_sar_type.
+  *
+  * @NL80211_SAR_ATTR_SPECS: Nested array of SAR power
+@@ -7755,6 +7904,8 @@ enum nl80211_sar_attrs {
+ /**
+  * enum nl80211_sar_specs_attrs - Attributes for SAR power limit specs
+  *
++ * @__NL80211_SAR_ATTR_SPECS_INVALID: Invalid
++ *
+  * @NL80211_SAR_ATTR_SPECS_POWER: Required (s32)value to specify the actual
+  *	power limit value in units of 0.25 dBm if type is
+  *	NL80211_SAR_TYPE_POWER. (i.e., a value of 44 represents 11 dBm).
+@@ -7869,4 +8020,54 @@ enum nl80211_ap_settings_flags {
+ 	NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT	= 1 << 1,
+ };
+ 
++/**
++ * enum nl80211_wiphy_radio_attrs - wiphy radio attributes
++ *
++ * @__NL80211_WIPHY_RADIO_ATTR_INVALID: Invalid
++ *
++ * @NL80211_WIPHY_RADIO_ATTR_INDEX: Index of this radio (u32)
++ * @NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE: Frequency range supported by this
++ *	radio. Attribute may be present multiple times.
++ * @NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION: Supported interface
++ *	combination for this radio. Attribute may be present multiple times
++ *	and contains attributes defined in &enum nl80211_if_combination_attrs.
++ *
++ * @__NL80211_WIPHY_RADIO_ATTR_LAST: Internal
++ * @NL80211_WIPHY_RADIO_ATTR_MAX: Highest attribute
++ */
++enum nl80211_wiphy_radio_attrs {
++	__NL80211_WIPHY_RADIO_ATTR_INVALID,
++
++	NL80211_WIPHY_RADIO_ATTR_INDEX,
++	NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE,
++	NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION,
++
++	/* keep last */
++	__NL80211_WIPHY_RADIO_ATTR_LAST,
++	NL80211_WIPHY_RADIO_ATTR_MAX = __NL80211_WIPHY_RADIO_ATTR_LAST - 1,
++};
++
++/**
++ * enum nl80211_wiphy_radio_freq_range - wiphy radio frequency range
++ *
++ * @__NL80211_WIPHY_RADIO_FREQ_ATTR_INVALID: Invalid
++ *
++ * @NL80211_WIPHY_RADIO_FREQ_ATTR_START: Frequency range start (u32).
++ *	The unit is kHz.
++ * @NL80211_WIPHY_RADIO_FREQ_ATTR_END: Frequency range end (u32).
++ *	The unit is kHz.
++ *
++ * @__NL80211_WIPHY_RADIO_FREQ_ATTR_LAST: Internal
++ * @NL80211_WIPHY_RADIO_FREQ_ATTR_MAX: Highest attribute
++ */
++enum nl80211_wiphy_radio_freq_range {
++	__NL80211_WIPHY_RADIO_FREQ_ATTR_INVALID,
++
++	NL80211_WIPHY_RADIO_FREQ_ATTR_START,
++	NL80211_WIPHY_RADIO_FREQ_ATTR_END,
++
++	__NL80211_WIPHY_RADIO_FREQ_ATTR_LAST,
++	NL80211_WIPHY_RADIO_FREQ_ATTR_MAX = __NL80211_WIPHY_RADIO_FREQ_ATTR_LAST - 1,
++};
++
+ #endif /* __LINUX_NL80211_H */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0012-hostapd-make-hostapd_eapol_tx_status-function-static.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0012-hostapd-make-hostapd_eapol_tx_status-function-static.patch
deleted file mode 100644
index 960569b..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0012-hostapd-make-hostapd_eapol_tx_status-function-static.patch
+++ /dev/null
@@ -1,113 +0,0 @@
-From ac474b8dc6eb9d6a7562a714c0bbdcda47a3d858 Mon Sep 17 00:00:00 2001
-From: Sriram R <quic_srirrama@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:42 +0530
-Subject: [PATCH 012/104] hostapd: make hostapd_eapol_tx_status() function
- static
-
-hostapd_eapol_tx_status() function is being used only at one place in
-drv_callbacks. However, it is defined in ieee802_11.c which does not
-suit there.
-
-Hence, being the function definition in drv_callbacks.c and make it static.
-
-No functionality changes.
-
-Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/drv_callbacks.c | 25 +++++++++++++++++++++++++
- src/ap/ieee802_11.c    | 28 ----------------------------
- src/ap/ieee802_11.h    |  2 --
- 3 files changed, 25 insertions(+), 30 deletions(-)
-
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index 3b24aa4f4..12e6b3f36 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -2348,6 +2348,31 @@ err:
- }
- #endif /* CONFIG_OWE */
- 
-+static void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
-+				    const u8 *data, size_t len, int ack)
-+{
-+	struct sta_info *sta;
-+	struct hostapd_iface *iface = hapd->iface;
-+
-+	sta = ap_get_sta(hapd, dst);
-+	if (sta == NULL && iface->num_bss > 1) {
-+		size_t j;
-+		for (j = 0; j < iface->num_bss; j++) {
-+			hapd = iface->bss[j];
-+			sta = ap_get_sta(hapd, dst);
-+			if (sta)
-+				break;
-+		}
-+	}
-+	if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
-+		wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
-+			   MACSTR " that is not currently associated",
-+			   MAC2STR(dst));
-+		return;
-+	}
-+
-+	ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
-+}
- 
- void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
- 			  union wpa_event_data *data)
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 26e3d8356..9f7e9afdd 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -6874,34 +6874,6 @@ void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
- 	ieee802_1x_tx_status(hapd, sta, buf, len, ack);
- }
- 
--
--void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
--			     const u8 *data, size_t len, int ack)
--{
--	struct sta_info *sta;
--	struct hostapd_iface *iface = hapd->iface;
--
--	sta = ap_get_sta(hapd, dst);
--	if (sta == NULL && iface->num_bss > 1) {
--		size_t j;
--		for (j = 0; j < iface->num_bss; j++) {
--			hapd = iface->bss[j];
--			sta = ap_get_sta(hapd, dst);
--			if (sta)
--				break;
--		}
--	}
--	if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
--		wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
--			   MACSTR " that is not currently associated",
--			   MAC2STR(dst));
--		return;
--	}
--
--	ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
--}
--
--
- void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr)
- {
- 	struct sta_info *sta;
-diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
-index a35486d46..262e0ce14 100644
---- a/src/ap/ieee802_11.h
-+++ b/src/ap/ieee802_11.h
-@@ -132,8 +132,6 @@ int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
- u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid);
- void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
- 		       const u8 *buf, size_t len, int ack);
--void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
--			     const u8 *data, size_t len, int ack);
- void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
- 				int wds);
- u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0012-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0012-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch
new file mode 100644
index 0000000..800f08e
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0012-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch
@@ -0,0 +1,442 @@
+From 91b94ac0deb7a19c0d2288f264e0d9b1c4f3c2e1 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 012/126] mtk: hostapd: 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
+database. The second can iterate neighbor report database to build up neighbor
+report data.
+
+2. Support including neighbor report elements in ANQP response
+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. 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 +
+ src/ap/ap_config.h     |   1 +
+ src/ap/ctrl_iface_ap.c |  18 ++++++-
+ src/ap/gas_serv.c      |  29 ++++++++++
+ src/ap/gas_serv.h      |   2 +
+ src/ap/neighbor_db.c   | 118 +++++++++++++++++++++++++++++++++++++++++
+ src/ap/neighbor_db.h   |   9 ++++
+ src/ap/wnm_ap.c        |  43 +++++++++++++--
+ 9 files changed, 221 insertions(+), 5 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 1d5922fab..f2733687c 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -1300,6 +1300,11 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
+ #endif /* CONFIG_DPP */
+ 	} else if (os_strcasecmp(cmd, "setband") == 0) {
+ 		ret = hostapd_ctrl_iface_set_band(hapd, value);
++	} else if (os_strcasecmp(cmd, "bss_termination_tsf") == 0) {
++		int termination_sec = atoi(value);
++		hapd->conf->bss_termination_tsf = termination_sec;
++		wpa_printf(MSG_DEBUG, "BSS Termination TSF: value = %d",
++                termination_sec);
+ 	} else {
+ 		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 9e34e029a..67661dfe0 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -177,6 +177,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
+ 	bss->pasn_comeback_after = 10;
+ 	bss->pasn_noauth = 1;
+ #endif /* CONFIG_PASN */
++	bss->bss_termination_tsf = 0;
+ }
+ 
+ 
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 29a9ae7db..25ac22e31 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -572,6 +572,7 @@ struct hostapd_bss_config {
+ 	int wnm_sleep_mode;
+ 	int wnm_sleep_mode_no_keys;
+ 	int bss_transition;
++	unsigned int bss_termination_tsf;
+ 
+ 	/* IEEE 802.11u - Interworking */
+ 	int interworking;
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index a1ddbda9f..50f993253 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -1400,6 +1400,10 @@ int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
+ 			wpa_printf(MSG_DEBUG, "Invalid bss_term data");
+ 			return -1;
+ 		}
++		if (hapd->conf->bss_termination_tsf) {
++			WPA_PUT_LE64(&bss_term_dur[2], hapd->conf->bss_termination_tsf);
++		}
++
+ 		end++;
+ 		WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
+ 	}
+@@ -1426,16 +1430,26 @@ int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
+ 		req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
+ 	}
+ 
+-	if (os_strstr(cmd, " pref=1"))
++	if (os_strstr(cmd, " pref=1")) {
+ 		req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
++		if (nei_len == 0) {
++			// Add neigibor report from neighbor report db to nei_rep buffer
++			nei_len = hostapd_neighbor_insert_buffer (hapd, nei_rep, 1000);
++		}
++	}
+ 	if (os_strstr(cmd, " abridged=1"))
+ 		req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
+-	if (os_strstr(cmd, " disassoc_imminent=1"))
++	if (os_strstr(cmd, " disassoc_imminent=1")) {
+ 		req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
++		/* Set own BSS neighbor report preference value as 0 */
++		hostapd_neighbor_set_own_report_pref(hapd, nei_rep, nei_len, 0);
++	}
+ 	if (os_strstr(cmd, " link_removal_imminent=1"))
+ 		req_mode |= WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT;
+ 
+ #ifdef CONFIG_MBO
++	hostapd_neighbor_set_pref_by_non_pref_chan(hapd, sta, nei_rep, nei_len);
++
+ 	pos = os_strstr(cmd, "mbo=");
+ 	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
+--- a/src/ap/gas_serv.c
++++ b/src/ap/gas_serv.c
+@@ -19,6 +19,7 @@
+ #include "dpp_hostapd.h"
+ #include "sta_info.h"
+ #include "gas_serv.h"
++#include "neighbor_db.h"
+ 
+ 
+ #ifdef CONFIG_DPP
+@@ -369,6 +370,24 @@ static void anqp_add_network_auth_type(struct hostapd_data *hapd,
+ 	}
+ }
+ 
++static void anqp_add_neighbor_report(struct hostapd_data *hapd,
++				       struct wpabuf *buf)
++{
++	struct hostapd_neighbor_entry *nr;
++	u8 *len_pos = gas_anqp_add_element(buf, ANQP_NEIGHBOR_REPORT);
++	if (dl_list_empty(&hapd->nr_db)) {
++		wpabuf_put_le16(buf, 0);
++	}
++	else {
++		dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list ) {
++			wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
++			wpabuf_put_u8(buf, wpabuf_len(nr->nr));
++			wpabuf_put_buf(buf, nr->nr);
++		}
++	}
++	gas_anqp_set_element_len(buf, len_pos);
++}
++
+ 
+ static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
+ 					struct wpabuf *buf)
+@@ -986,6 +1005,9 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
+ 		len += 1000;
+ 	if (request & ANQP_REQ_ICON_REQUEST)
+ 		len += 65536;
++    if (request & ANQP_REQ_NEIGHBOR_REPORT) {
++        len += (40 * hostapd_neighbor_count(hapd));
++    }
+ #ifdef CONFIG_FILS
+ 	if (request & ANQP_FILS_REALM_INFO)
+ 		len += 2 * dl_list_len(&hapd->conf->fils_realms);
+@@ -1028,6 +1050,8 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
+ 		anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
+ 	if (request & ANQP_REQ_EMERGENCY_NAI)
+ 		anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
++	if (request & ANQP_REQ_NEIGHBOR_REPORT)
++		anqp_add_neighbor_report(hapd, buf);
+ 
+ 	for (i = 0; i < num_extra_req; i++) {
+ #ifdef CONFIG_FILS
+@@ -1172,6 +1196,11 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
+ 			     "Emergency NAI",
+ 			     get_anqp_elem(hapd, info_id) != NULL, qi);
+ 		break;
++	case ANQP_NEIGHBOR_REPORT:
++		set_anqp_req(ANQP_REQ_NEIGHBOR_REPORT,
++			     "Neighbor Report",
++			     get_anqp_elem(hapd, info_id) != NULL, qi);
++		break;
+ 	default:
+ #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
+--- a/src/ap/gas_serv.h
++++ b/src/ap/gas_serv.h
+@@ -40,6 +40,8 @@
+ 	(1 << (ANQP_TDLS_CAPABILITY - ANQP_QUERY_LIST))
+ #define ANQP_REQ_EMERGENCY_NAI \
+ 	(1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST))
++#define ANQP_REQ_NEIGHBOR_REPORT \
++	(1 << (ANQP_NEIGHBOR_REPORT - ANQP_QUERY_LIST))
+ /*
+  * 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 f7a7d83d4..d9216a5ae 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)
+ }
+ 
+ 
++int hostapd_neighbor_count(struct hostapd_data *hapd)
++{
++	struct hostapd_neighbor_entry *nr;
++	int count = 0;
++
++	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
++			 list) {
++		count++;
++	}
++	return count;
++}
++
++
++int hostapd_neighbor_insert_buffer(struct hostapd_data *hapd, char *buf,
++        size_t buflen)
++{
++	struct hostapd_neighbor_entry *nr;
++	char *pos = buf;
++
++	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
++			 list) {
++		/* For neighbor report IE, we only need bssid and nr*/
++		*pos++ = WLAN_EID_NEIGHBOR_REPORT;
++		*pos++ = wpabuf_len(nr->nr);
++		os_memcpy(pos, wpabuf_head(nr->nr), wpabuf_len(nr->nr));
++		pos += wpabuf_len(nr->nr);
++	}
++
++	return pos - buf;
++}
++
++
+ static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
+ {
+ 	wpabuf_free(nr->nr);
+@@ -364,3 +396,89 @@ int hostapd_neighbor_sync_own_report(struct hostapd_data *hapd)
+ 
+ 	return 0;
+ }
++
++void hostapd_neighbor_set_own_report_pref(struct hostapd_data *hapd, char *nei_buf,
++			 size_t buflen, const int pref)
++{
++	struct hostapd_neighbor_entry *nr;
++	char *pos, *next_nr;
++
++	pos = nei_buf;
++	next_nr = nei_buf;
++
++	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
++			 list) {
++		pos = next_nr;
++		next_nr = pos + 2 + wpabuf_len(nr->nr);
++		/* Shift 2 bytes for Element ID and Neighbor report length */
++		pos = pos + 2;
++		if(os_memcmp(pos, hapd->own_addr, ETH_ALEN) == 0) {
++			/* Shift for BSSID + BSSID info + Op_class + channel num + PHY type */
++			pos = pos + 6 + 4 + 1 + 1 + 1;
++
++			/* Iterate Subelement */
++			while (next_nr - pos > 0) {
++				if (*pos == 3) {
++					pos = pos + 2;
++					*pos = pref;
++					return;
++				} else {
++					pos++;
++					int shift_len = *pos++;
++					pos = pos + shift_len;
++				}
++			}
++		}
++	}
++}
++
++#ifdef CONFIG_MBO
++void hostapd_neighbor_set_pref_by_non_pref_chan(struct hostapd_data *hapd,
++			 struct sta_info* sta, char *nei_buf, size_t buflen)
++{
++	struct hostapd_neighbor_entry *nr;
++	struct mbo_non_pref_chan_info *info;
++	u8 i;
++
++	for(info = sta->non_pref_chan; info; info = info->next) {
++		/* Check OP_Class and Channel num */
++		for(i = 0; i < info->num_channels; i++) {
++			char *pos, *next_nr;
++
++			pos = nei_buf;
++			next_nr = nei_buf;
++
++			/* Iterate Neighbor report database */
++			dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
++					 list) {
++				pos = next_nr;
++				next_nr = pos + 2 + wpabuf_len(nr->nr);
++				/**
++				 * Shift 12 bytes for Element ID, Neighbor report length,
++				 * BSSID and BSSID info.
++				 */
++				pos = pos + 12;
++				int nr_op_class = *pos++;
++				int nr_channel = *pos;
++				if(info->op_class == nr_op_class && info->channels[i] == nr_channel) {
++					/* Shift for Channel Num + PHY type */
++					pos = pos + 1 + 1;
++
++					// Iterate Subelement
++					while(next_nr - pos > 0) {
++						if(*pos == 3) {
++							pos = pos + 2;
++							*pos = info->pref;
++							break;
++						}else {
++							pos++;
++							int shift_len = *pos++;
++							pos = pos + shift_len;
++						}
++					}
++				}
++			}
++		}
++	}
++}
++#endif
+diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
+index 53f714203..cf1400256 100644
+--- a/src/ap/neighbor_db.h
++++ b/src/ap/neighbor_db.h
+@@ -25,4 +25,13 @@ int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
+ 			    const struct wpa_ssid_value *ssid);
+ void hostapd_free_neighbor_db(struct hostapd_data *hapd);
+ 
++int hostapd_neighbor_count(struct hostapd_data *hapd);
++int hostapd_neighbor_insert_buffer(struct hostapd_data *hapd, char *buf,
++        size_t buflen);
++void hostapd_neighbor_set_own_report_pref(struct hostapd_data *hapd, char *nei_buf,
++			 size_t buflen, const int pref);
++#ifdef CONFIG_MBO
++void hostapd_neighbor_set_pref_by_non_pref_chan(struct hostapd_data *hapd,
++			 struct sta_info* sta, char *nei_buf, size_t buflen);
++#endif
+ #endif /* NEIGHBOR_DB_H */
+diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
+index d259200c9..4ac96b1be 100644
+--- a/src/ap/wnm_ap.c
++++ b/src/ap/wnm_ap.c
+@@ -20,6 +20,7 @@
+ #include "ap/wpa_auth.h"
+ #include "mbo_ap.h"
+ #include "wnm_ap.h"
++#include "ap/neighbor_db.h"
+ 
+ #define MAX_TFS_IE_LEN  1024
+ 
+@@ -390,13 +391,24 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
+ 	u8 *pos;
+ 	int res;
+ 
+-	mgmt = os_zalloc(sizeof(*mgmt));
+-	if (mgmt == NULL)
++	int nr_num = hostapd_neighbor_count(hapd);
++	int nr_size = ETH_ALEN + 4 + 1 + 1 + 1 + 5;
++	int total_nr_size = nr_num * nr_size;
++	u8 *nr_data = os_malloc(total_nr_size);
++	int nr_data_len = 0;
++	if(nr_data == NULL) {
++		wpa_printf (MSG_ERROR, "Failed to allocate memory");
++	} else {
++	    nr_data_len = hostapd_neighbor_insert_buffer(hapd, nr_data, total_nr_size);
++	}
++	mgmt = os_zalloc(sizeof(*mgmt) + nr_data_len);
++	if (mgmt == NULL) {
++		wpa_printf (MSG_ERROR, "Failed to allocate memory for mgmt frame");
+ 		return -1;
++	}
+ 
+ 	sta = ap_get_sta(hapd, addr);
+ 	own_addr = wnm_ap_get_own_addr(hapd, sta);
+-
+ 	os_memcpy(mgmt->da, addr, ETH_ALEN);
+ 	os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ 	os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
+@@ -406,10 +418,18 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
+ 	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+ 	mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
+ 	mgmt->u.action.u.bss_tm_req.req_mode = 0;
++	if(nr_num) {
++		mgmt->u.action.u.bss_tm_req.req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
++	}
+ 	mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
+ 	mgmt->u.action.u.bss_tm_req.validity_interval = 1;
+ 	pos = mgmt->u.action.u.bss_tm_req.variable;
+ 
++	if(nr_num) {
++		os_memcpy(pos, nr_data, nr_data_len);
++		pos += nr_data_len;
++	}
++
+ 	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 "
+@@ -915,6 +935,22 @@ static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
+ }
+ 
+ 
++void bss_termination_disable_iface(void *eloop_ctx, void *timeout_ctx)
++{
++	struct hostapd_data *hapd = eloop_ctx;
++	hostapd_disable_iface(hapd->iface);
++}
++
++
++static void set_disable_iface_timer(struct hostapd_data *hapd, struct sta_info *sta,
++			       int disable_iface_timer)
++{
++	wpa_printf(MSG_DEBUG, "Disable interface timer set to %d secs", disable_iface_timer);
++	eloop_register_timeout(disable_iface_timer, 0,
++			       bss_termination_disable_iface, hapd, NULL);
++}
++
++
+ int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
+ 				   struct sta_info *sta, const char *url,
+ 				   int disassoc_timer)
+@@ -1006,6 +1042,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;
++		set_disable_iface_timer(hapd, sta, hapd->conf->bss_termination_tsf);
+ 	}
+ 
+ 	if (url) {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0013-hostapd-MLO-handle-link_id-in-EAPOL-Tx-status-handle.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0013-hostapd-MLO-handle-link_id-in-EAPOL-Tx-status-handle.patch
deleted file mode 100644
index ab69545..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0013-hostapd-MLO-handle-link_id-in-EAPOL-Tx-status-handle.patch
+++ /dev/null
@@ -1,181 +0,0 @@
-From aa339ee77d60fe9314182cf0e60fa2da4da72b44 Mon Sep 17 00:00:00 2001
-From: Sriram R <quic_srirrama@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:43 +0530
-Subject: [PATCH 013/104] hostapd: MLO: handle link_id in EAPOL Tx status
- handler
-
-Add link id support in EAPOL Tx status handler so that event can be
-routed to appropriate link BSS.
-
-In order to support this, modify hostapd_find_by_sta() function to check
-each BSS's other parnter link BSS sta list as well.
-
-Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/drv_callbacks.c | 108 +++++++++++++++--------------------------
- 1 file changed, 38 insertions(+), 70 deletions(-)
-
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index 12e6b3f36..064c7abae 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -1945,53 +1945,46 @@ static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr)
- 
- 
- static struct hostapd_data * hostapd_find_by_sta(struct hostapd_iface *iface,
--						 const u8 *src, bool rsn)
-+						 const u8 *src, bool rsn,
-+						 struct sta_info **sta_ret)
- {
-+	struct hostapd_data *hapd;
- 	struct sta_info *sta;
- 	unsigned int j;
- 
-+	if (sta_ret)
-+		*sta_ret = NULL;
-+
- 	for (j = 0; j < iface->num_bss; j++) {
--		sta = ap_get_sta(iface->bss[j], src);
-+		hapd = iface->bss[j];
-+		sta = ap_get_sta(hapd, src);
- 		if (sta && (sta->flags & WLAN_STA_ASSOC) &&
--		    (!rsn || sta->wpa_sm))
--			return iface->bss[j];
--	}
--
--	return NULL;
--}
--
--
-+		    (!rsn || sta->wpa_sm)) {
-+			if (sta_ret)
-+				*sta_ret = sta;
-+			return hapd;
- #ifdef CONFIG_IEEE80211BE
--static bool search_mld_sta(struct hostapd_data **p_hapd, const u8 *src)
--{
--	struct hostapd_data *hapd = *p_hapd;
--	unsigned int i;
--
--	/* Search for STA on other MLO BSSs */
--	for (i = 0; i < hapd->iface->interfaces->count; i++) {
--		struct hostapd_iface *h =
--			hapd->iface->interfaces->iface[i];
--		struct hostapd_data *h_hapd = h->bss[0];
--
--		if (!hostapd_is_ml_partner(h_hapd, hapd))
--			continue;
-+		} else if (hapd->conf->mld_ap) {
-+			struct hostapd_data *p_hapd;
- 
--		h_hapd = hostapd_find_by_sta(h, src, false);
--		if (h_hapd) {
--			struct sta_info *sta = ap_get_sta(h_hapd, src);
-+			for_each_mld_link(p_hapd, hapd) {
-+				if (p_hapd == hapd)
-+					continue;
- 
--			if (sta && sta->mld_info.mld_sta &&
--			    sta->mld_assoc_link_id != h_hapd->mld_link_id)
--				continue;
--			*p_hapd = h_hapd;
--			return true;
-+				sta = ap_get_sta(p_hapd, src);
-+				if (sta && (sta->flags & WLAN_STA_ASSOC) &&
-+				    (!rsn || sta->wpa_sm)) {
-+					if (sta_ret)
-+						*sta_ret = sta;
-+					return p_hapd;
-+				}
-+			}
-+#endif /* CONFIG_IEEE80211BE */
- 		}
- 	}
- 
--	return false;
-+	return NULL;
- }
--#endif /* CONFIG_IEEE80211BE */
--
- 
- static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
- 				   const u8 *data, size_t data_len,
-@@ -2001,28 +1994,10 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
- 	struct hostapd_data *orig_hapd = hapd;
- 
- #ifdef CONFIG_IEEE80211BE
--	if (link_id != -1) {
--		struct hostapd_data *h_hapd;
--
--		hapd = switch_link_hapd(hapd, link_id);
--		h_hapd = hostapd_find_by_sta(hapd->iface, src, true);
--		if (!h_hapd)
--			h_hapd = hostapd_find_by_sta(orig_hapd->iface, src,
--						     true);
--		if (!h_hapd)
--			h_hapd = hostapd_find_by_sta(hapd->iface, src, false);
--		if (!h_hapd)
--			h_hapd = hostapd_find_by_sta(orig_hapd->iface, src,
--						     false);
--		if (h_hapd)
--			hapd = h_hapd;
--	} else if (hapd->conf->mld_ap) {
--		search_mld_sta(&hapd, src);
--	} else {
--		hapd = hostapd_find_by_sta(hapd->iface, src, false);
--	}
-+	hapd = switch_link_hapd(hapd, link_id);
-+	hapd = hostapd_find_by_sta(hapd->iface, src, true, NULL);
- #else /* CONFIG_IEEE80211BE */
--	hapd = hostapd_find_by_sta(hapd->iface, src, false);
-+	hapd = hostapd_find_by_sta(hapd->iface, src, false, NULL);
- #endif /* CONFIG_IEEE80211BE */
- 
- 	if (!hapd) {
-@@ -2349,22 +2324,15 @@ err:
- #endif /* CONFIG_OWE */
- 
- static void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
--				    const u8 *data, size_t len, int ack)
-+				    const u8 *data, size_t len, int ack,
-+				    int link_id)
- {
- 	struct sta_info *sta;
--	struct hostapd_iface *iface = hapd->iface;
- 
--	sta = ap_get_sta(hapd, dst);
--	if (sta == NULL && iface->num_bss > 1) {
--		size_t j;
--		for (j = 0; j < iface->num_bss; j++) {
--			hapd = iface->bss[j];
--			sta = ap_get_sta(hapd, dst);
--			if (sta)
--				break;
--		}
--	}
--	if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
-+	hapd = switch_link_hapd(hapd, link_id);
-+	hapd = hostapd_find_by_sta(hapd->iface, dst, false, &sta);
-+
-+	if (sta == NULL) {
- 		wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
- 			   MACSTR " that is not currently associated",
- 			   MAC2STR(dst));
-@@ -2431,11 +2399,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
- 		}
- 		break;
- 	case EVENT_EAPOL_TX_STATUS:
--		hapd = switch_link_hapd(hapd, data->eapol_tx_status.link_id);
- 		hostapd_eapol_tx_status(hapd, data->eapol_tx_status.dst,
- 					data->eapol_tx_status.data,
- 					data->eapol_tx_status.data_len,
--					data->eapol_tx_status.ack);
-+					data->eapol_tx_status.ack,
-+					data->eapol_tx_status.link_id);
- 		break;
- 	case EVENT_DRIVER_CLIENT_POLL_OK:
- 		hostapd_client_poll_ok(hapd, data->client_poll.addr);
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0013-mtk-hostapd-print-some-sae-info-by-hostapd-ctrl.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0013-mtk-hostapd-print-some-sae-info-by-hostapd-ctrl.patch
new file mode 100644
index 0000000..fdc78e1
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0013-mtk-hostapd-print-some-sae-info-by-hostapd-ctrl.patch
@@ -0,0 +1,112 @@
+From e415e499c9ea0939f0c286c5c0422acbc3491756 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 20 Sep 2022 19:33:45 +0800
+Subject: [PATCH 013/126] mtk: hostapd: print some sae info by hostapd ctrl
+
+root@OpenWrt:~# hostapd_cli -i phy0-ap0 pmksa
+Setup at link 0:
+Index / SPA / PMKID / PMK / expiration (in seconds) / opportunistic
+Setup at link 1:
+Index / SPA / PMKID / PMK / expiration (in seconds) / opportunistic
+0 02:0c:43:b2:01:1c b5706f5f53117e00fa46fcf94c225009 7505eb11319fa94add14d0fd091caadf9be2c642aa7363a96e1efa575e794420 43180 0
+Setup at link 2:
+Index / SPA / PMKID / PMK / expiration (in seconds) / opportunistic
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ hostapd/ctrl_iface.c      | 14 ++++++++++++++
+ src/ap/ctrl_iface_ap.c    | 22 +++++++++++++++++++++-
+ src/ap/pmksa_cache_auth.c |  5 ++++-
+ 3 files changed, 39 insertions(+), 2 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index f2733687c..2d90d77c6 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -70,6 +70,7 @@
+ #include "fst/fst_ctrl_iface.h"
+ #include "config_file.h"
+ #include "ctrl_iface.h"
++#include "crypto/dh_groups.h"
+ 
+ 
+ #define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
+@@ -1376,6 +1377,19 @@ static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd,
+ 		if (os_snprintf_error(buflen, res))
+ 			return -1;
+ 		return res;
++	} else if (os_strcmp(cmd, "sae_group_capability") == 0) {
++#ifdef CONFIG_SAE
++		/* see sae_set_group() */
++		res = os_snprintf(buf, buflen, "%s%s%s%s19 20 21",
++				  dh_groups_get(15) ? "15 ": "",
++				  dh_groups_get(16) ? "16 ": "",
++				  dh_groups_get(17) ? "17 ": "",
++				  dh_groups_get(18) ? "18 ": "");
++
++		if (os_snprintf_error(buflen, res))
++			return -1;
++		return res;
++#endif /* CONFIG_SAE */
+ 	}
+ 
+ 	return -1;
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index 50f993253..4d3c7e529 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -1157,7 +1157,27 @@ int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd)
+ int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
+ 				  size_t len)
+ {
+-	return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len);
++	char *pos = buf;
++	int ret, link_id;
++
++	if (!hapd->conf->mld_ap)
++		return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len);
++
++	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
++		struct hostapd_data *h = hostapd_mld_get_link_bss(hapd, link_id);
++
++		if (!h)
++			continue;
++
++		ret = os_snprintf(pos, len - (pos - buf), "Setup at link %u:\n", h->mld_link_id);
++		if (os_snprintf_error(len - (pos - buf), ret))
++			return pos - buf;
++		pos += ret;
++
++		pos += wpa_auth_pmksa_list(h->wpa_auth, pos, len - (pos - buf));
++	}
++
++	return pos - buf;
+ }
+ 
+ 
+diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
+index 2fce8383d..8a63cb682 100644
+--- a/src/ap/pmksa_cache_auth.c
++++ b/src/ap/pmksa_cache_auth.c
+@@ -658,7 +658,7 @@ int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
+ 
+ 	os_get_reltime(&now);
+ 	ret = os_snprintf(pos, buf + len - pos,
+-			  "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n");
++			  "Index / SPA / PMKID / PMK / expiration (in seconds) / opportunistic\n");
+ 	if (os_snprintf_error(buf + len - pos, ret))
+ 		return pos - buf;
+ 	pos += ret;
+@@ -672,6 +672,9 @@ int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
+ 		pos += ret;
+ 		pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
+ 					PMKID_LEN);
++		*pos++ = ' ';
++		pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmk,
++					entry->pmk_len);
+ 		ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
+ 				  (int) (entry->expiration - now.sec),
+ 				  entry->opportunistic);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0014-hostapd-MLO-update-all-partner-s-link-beacon.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0014-hostapd-MLO-update-all-partner-s-link-beacon.patch
deleted file mode 100644
index 0cec211..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0014-hostapd-MLO-update-all-partner-s-link-beacon.patch
+++ /dev/null
@@ -1,77 +0,0 @@
-From 4be307ebbc6b94b6a334855a9efe633d77ca98fe Mon Sep 17 00:00:00 2001
-From: Sriram R <quic_srirrama@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:44 +0530
-Subject: [PATCH 014/104] hostapd: MLO: update all partner's link beacon
-
-Whenever there is a beacon update of any one of the link, all its other
-partner's link beacon should be refreshed.
-
-Add changes to update all partner's link beacon.
-
-Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/beacon.c | 27 +++++++++++++++++++--------
- 1 file changed, 19 insertions(+), 8 deletions(-)
-
-diff --git a/src/ap/beacon.c b/src/ap/beacon.c
-index 195c7bbd9..b780d98e4 100644
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -2648,7 +2648,7 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
- 	struct hostapd_iface *iface = hapd->iface;
- 	int ret;
- 	size_t i, j;
--	bool is_6g;
-+	bool is_6g, hapd_mld = false;
- 
- 	ret = __ieee802_11_set_beacon(hapd);
- 	if (ret != 0)
-@@ -2657,26 +2657,37 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
- 	if (!iface->interfaces || iface->interfaces->count <= 1)
- 		return 0;
- 
-+#ifdef CONFIG_IEEE80211BE
-+	hapd_mld = hapd->conf->mld_ap;
-+#endif /* CONFIG_IEEE80211BE */
-+
- 	/* Update Beacon frames in case of 6 GHz colocation or AP MLD */
- 	is_6g = is_6ghz_op_class(iface->conf->op_class);
- 	for (j = 0; j < iface->interfaces->count; j++) {
- 		struct hostapd_iface *other;
--		bool mld_ap = false;
-+		bool other_iface_6g;
- 
- 		other = iface->interfaces->iface[j];
- 		if (other == iface || !other || !other->conf)
- 			continue;
- 
--#ifdef CONFIG_IEEE80211BE
--		if (hostapd_is_ml_partner(hapd, other->bss[0]))
--			mld_ap = true;
--#endif /* CONFIG_IEEE80211BE */
-+		other_iface_6g = is_6ghz_op_class(other->conf->op_class);
- 
--		if (is_6g == is_6ghz_op_class(other->conf->op_class) &&
--		    !mld_ap)
-+		if (is_6g == other_iface_6g && !hapd_mld)
- 			continue;
- 
- 		for (i = 0; i < other->num_bss; i++) {
-+#ifdef CONFIG_IEEE80211BE
-+			bool mld_ap = false;
-+
-+			if (hapd_mld && other->bss[i]->conf->mld_ap &&
-+			    hostapd_is_ml_partner(hapd, other->bss[i]))
-+				mld_ap = true;
-+
-+			if (is_6g == other_iface_6g && !mld_ap)
-+				continue;
-+#endif /* CONFIG_IEEE80211BE */
-+
- 			if (other->bss[i] && other->bss[i]->started)
- 				__ieee802_11_set_beacon(other->bss[i]);
- 		}
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0014-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0014-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch
new file mode 100644
index 0000000..97cf42b
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0014-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch
@@ -0,0 +1,196 @@
+From 05ca0e1db230ecd668db4597ac1ef1d937459ea3 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Tue, 31 May 2022 21:15:54 +0800
+Subject: [PATCH 014/126] mtk: hostapd: add support for runtime set in-band
+ discovery
+
+Usage:
+hostapd_cli unsolic_probe_resp [tx_type] [interval]
+
+0: disable all in-band discovery
+1: enable unsolicited probe response
+2: enable FILS discovery
+
+The mac80211 layer already has a new variable update,
+so the redundant variable disable has been removed.
+
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ hostapd/ctrl_iface.c         | 66 ++++++++++++++++++++++++++++++++++++
+ hostapd/hostapd_cli.c        | 20 +++++++++++
+ src/ap/beacon.c              |  5 ++-
+ src/drivers/driver_nl80211.c |  6 ++--
+ 4 files changed, 94 insertions(+), 3 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 2d90d77c6..40244c5e7 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -773,6 +773,69 @@ static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd,
+ 
+ #endif /* CONFIG_INTERWORKING */
+ 
++static int hostapd_ctrl_iface_inband_discovery(struct hostapd_data *hapd,
++					       const char *cmd)
++{
++	struct hostapd_bss_config *conf = hapd->conf;
++	const char *pos = cmd;
++	int tx_type, interval, ret;
++
++	tx_type = atoi(pos);
++	if (tx_type < 0 || tx_type > 2) {
++		wpa_printf(MSG_ERROR, "Invalid tx type\n");
++		return -1;
++	}
++
++	pos = os_strchr(pos, ' ');
++	if(!pos)
++		return -1;
++	pos++;
++	interval = atoi(pos);
++	if (interval < 0 || interval > 20) {
++		wpa_printf(MSG_ERROR, "Invalid interval value\n");
++		return -1;
++	}
++
++	wpa_printf(MSG_ERROR, "Set inband discovery type:%d, interval:%d\n",
++			      tx_type, interval);
++
++#define DISABLE_INBAND_DISC 0
++#define UNSOL_PROBE_RESP 1
++#define FILS_DISCOVERY 2
++
++#ifdef CONFIG_FILS
++	conf->fils_discovery_max_int = 0;
++	conf->fils_discovery_min_int = 0;
++#endif /* CONFIG_FILS */
++	conf->unsol_bcast_probe_resp_interval = 0;
++
++	switch (tx_type) {
++	case DISABLE_INBAND_DISC:
++	default:
++		/* Disable both Unsolicited probe response and FILS discovery*/
++		break;
++	case UNSOL_PROBE_RESP:
++		/* Enable Unsolicited probe response */
++		conf->unsol_bcast_probe_resp_interval = interval;
++		break;
++#ifdef CONFIG_FILS
++	case FILS_DISCOVERY:
++		/* Enable FILS discovery */
++		conf->fils_discovery_min_int = interval;
++		conf->fils_discovery_max_int = interval;
++		break;
++#endif /* CONFIG_FILS */
++	}
++
++	ret = ieee802_11_update_beacons(hapd->iface);
++	if(ret) {
++		wpa_printf(MSG_DEBUG,
++			"Failed to update with inband discovery parameters\n");
++		return -1;
++	}
++
++	return 0;
++}
+ 
+ #ifdef CONFIG_WNM_AP
+ 
+@@ -4175,6 +4238,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		if (hostapd_ctrl_iface_coloc_intf_req(hapd, buf + 15))
+ 			reply_len = -1;
+ #endif /* CONFIG_WNM_AP */
++	} else if (os_strncmp(buf, "INBAND_DISCOVERY ", 17) == 0) {
++		if (hostapd_ctrl_iface_inband_discovery(hapd, buf + 17))
++			reply_len = -1;
+ 	} else if (os_strcmp(buf, "GET_CONFIG") == 0) {
+ 		reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
+ 							  reply_size);
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index ebf8addc1..bb15bc751 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -664,6 +664,24 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
+ 	return wpa_ctrl_command(ctrl, buf);
+ }
+ 
++static int hostapd_cli_cmd_inband_discovery(struct wpa_ctrl *ctrl, int argc,
++					    char *argv[])
++{
++	char buf[300];
++	int res;
++
++	if (argc < 2) {
++		printf("Invalid 'inband_discovery' command - two arguments"
++		       "tx_type interval\n");
++		return -1;
++	}
++
++	res = os_snprintf(buf, sizeof(buf), "INBAND_DISCOVERY %s %s",
++			  argv[0], argv[1]);
++	if (os_snprintf_error(sizeof(buf), res))
++		return -1;
++	return wpa_ctrl_command(ctrl, buf);
++}
+ 
+ static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
+ 					     char *argv[])
+@@ -1883,6 +1901,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 	{ "driver", hostapd_cli_cmd_driver, NULL,
+ 	  "<driver sub command> [<hex formatted data>] = send driver command data" },
+ #endif /* ANDROID */
++	{ "inband_discovery", hostapd_cli_cmd_inband_discovery, NULL,
++          "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 58b561661..9bf73747f 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -2164,6 +2164,8 @@ static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
+ 				   struct wpa_driver_ap_params *params)
+ {
+ 	params->fd_max_int = hapd->conf->fils_discovery_max_int;
++	params->ubpr.unsol_bcast_probe_resp_interval =
++		hapd->conf->unsol_bcast_probe_resp_interval;
+ 	if (is_6ghz_op_class(hapd->iconf->op_class) &&
+ 	    params->fd_max_int > FD_MAX_INTERVAL_6GHZ)
+ 		params->fd_max_int = FD_MAX_INTERVAL_6GHZ;
+@@ -2172,7 +2174,8 @@ static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
+ 	if (params->fd_min_int > params->fd_max_int)
+ 		params->fd_min_int = params->fd_max_int;
+ 
+-	if (params->fd_max_int)
++	if (params->fd_max_int || (is_6ghz_op_class(hapd->iconf->op_class) &&
++	    !params->ubpr.unsol_bcast_probe_resp_interval))
+ 		return hostapd_gen_fils_discovery(hapd,
+ 						  &params->fd_frame_tmpl_len);
+ 
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index a70eaae38..2c68bf997 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -4790,7 +4790,6 @@ static int nl80211_fils_discovery(struct i802_bss *bss, struct nl_msg *msg,
+ 	     nla_put(msg, NL80211_FILS_DISCOVERY_ATTR_TMPL,
+ 		     params->fd_frame_tmpl_len, params->fd_frame_tmpl)))
+ 		return -1;
+-
+ 	nla_nest_end(msg, attr);
+ 	return 0;
+ }
+@@ -5422,7 +5421,10 @@ static int wpa_driver_nl80211_set_ap(void *priv,
+ #endif /* CONFIG_SAE */
+ 
+ #ifdef CONFIG_FILS
+-	if (params->fd_max_int && nl80211_fils_discovery(bss, msg, params) < 0)
++	if ((params->fd_max_int ||
++	    ((params->freq->freq > 5950 && params->freq->freq <= 7115) &&
++	      !(params->ubpr.unsol_bcast_probe_resp_interval))) &&
++	     nl80211_fils_discovery(bss, msg, params) < 0)
+ 		goto fail;
+ #endif /* CONFIG_FILS */
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0015-hostapd-MLO-skip-assoc-link-processing-in-ML-info.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0015-hostapd-MLO-skip-assoc-link-processing-in-ML-info.patch
deleted file mode 100644
index 3dd5690..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0015-hostapd-MLO-skip-assoc-link-processing-in-ML-info.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 6eeca68d65795783243d3634627b4ac8f79e3d15 Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:45 +0530
-Subject: [PATCH 015/104] hostapd: MLO: skip assoc link processing in ML info
-
-Currently during processing ML info in association request, all links are
-iterated over. However, the assoc link info will not be present in the
-ML info hence following print is observed during ML association (assoc link
-is 1) -
-
-MLD: No link match for link_id=1
-
-Add changes to skip processing for the assoc link. No functionality
-changes.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/ieee802_11.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 9f7e9afdd..39c63f29b 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -4586,7 +4586,7 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
- 		struct mld_link_info *link = &sta->mld_info.links[i];
- 		bool link_bss_found = false;
- 
--		if (!link->valid)
-+		if (!link->valid || i == sta->mld_assoc_link_id)
- 			continue;
- 
- 		for_each_mld_link(bss, hapd) {
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0015-mtk-hostapd-Add-mtk_vendor.h.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0015-mtk-hostapd-Add-mtk_vendor.h.patch
new file mode 100644
index 0000000..ef346f2
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0015-mtk-hostapd-Add-mtk_vendor.h.patch
@@ -0,0 +1,216 @@
+From e37c0f7888fefa1d8367209dd89dc18ab63c4711 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 30 May 2022 15:04:57 +0800
+Subject: [PATCH 015/126] mtk: hostapd: Add mtk_vendor.h
+
+---
+ src/common/mtk_vendor.h | 197 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 197 insertions(+)
+ create mode 100644 src/common/mtk_vendor.h
+
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+new file mode 100644
+index 000000000..4a19d2fc9
+--- /dev/null
++++ b/src/common/mtk_vendor.h
+@@ -0,0 +1,197 @@
++// SPDX-License-Identifier: ISC
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++#ifndef MTK_VENDOR_H
++#define MTK_VENDOR_H
++
++#define OUI_MTK    0x000ce7
++
++enum mtk_nl80211_vendor_subcmds {
++	MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
++	MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
++	MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
++	MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
++	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
++};
++
++enum mtk_vendor_attr_edcca_ctrl {
++	MTK_VENDOR_ATTR_EDCCA_THRESHOLD_INVALID = 0,
++
++	MTK_VENDOR_ATTR_EDCCA_CTRL_MODE,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
++};
++
++enum mtk_vendor_attr_edcca_ctrl_mode {
++	EDCCA_CTRL_SET_EN = 0,
++	EDCCA_CTRL_SET_THERS,
++	EDCCA_CTRL_GET_EN,
++	EDCCA_CTRL_GET_THERS,
++	EDCCA_CTRL_NUM,
++};
++
++static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
++};
++
++enum mtk_vendor_attr_csi_ctrl {
++	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
++	MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
++	MTK_VENDOR_ATTR_CSI_CTRL_INTERVAL,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_DATA,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_CSI_CTRL,
++	MTK_VENDOR_ATTR_CSI_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_CSI_CTRL - 1
++};
++
++enum mtk_vendor_attr_csi_data {
++	MTK_VENDOR_ATTR_CSI_DATA_UNSPEC,
++	MTK_VENDOR_ATTR_CSI_DATA_PAD,
++
++	MTK_VENDOR_ATTR_CSI_DATA_VER,
++	MTK_VENDOR_ATTR_CSI_DATA_TS,
++	MTK_VENDOR_ATTR_CSI_DATA_RSSI,
++	MTK_VENDOR_ATTR_CSI_DATA_SNR,
++	MTK_VENDOR_ATTR_CSI_DATA_BW,
++	MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
++	MTK_VENDOR_ATTR_CSI_DATA_TA,
++	MTK_VENDOR_ATTR_CSI_DATA_I,
++	MTK_VENDOR_ATTR_CSI_DATA_Q,
++	MTK_VENDOR_ATTR_CSI_DATA_INFO,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD1,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD2,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD3,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD4,
++	MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
++	MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
++	MTK_VENDOR_ATTR_CSI_DATA_MODE,
++	MTK_VENDOR_ATTR_CSI_DATA_H_IDX,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_CSI_DATA,
++	MTK_VENDOR_ATTR_CSI_DATA_MAX =
++		NUM_MTK_VENDOR_ATTRS_CSI_DATA - 1
++};
++
++enum mtk_vendor_attr_mnt_ctrl {
++	MTK_VENDOR_ATTR_AMNT_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_AMNT_CTRL_SET,
++	MTK_VENDOR_ATTR_AMNT_CTRL_DUMP,
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_AMNT_CTRL,
++	MTK_VENDOR_ATTR_AMNT_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_AMNT_CTRL - 1
++};
++
++enum mtk_vendor_attr_mnt_set {
++	MTK_VENDOR_ATTR_AMNT_SET_UNSPEC,
++
++	MTK_VENDOR_ATTR_AMNT_SET_INDEX,
++	MTK_VENDOR_ATTR_AMNT_SET_MACADDR,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_AMNT_SET,
++	MTK_VENDOR_ATTR_AMNT_SET_MAX =
++		NUM_MTK_VENDOR_ATTRS_AMNT_SET - 1
++};
++
++enum mtk_vendor_attr_mnt_dump {
++	MTK_VENDOR_ATTR_AMNT_DUMP_UNSPEC,
++
++	MTK_VENDOR_ATTR_AMNT_DUMP_INDEX,
++	MTK_VENDOR_ATTR_AMNT_DUMP_LEN,
++	MTK_VENDOR_ATTR_AMNT_DUMP_RESULT,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
++	MTK_VENDOR_ATTR_AMNT_DUMP_MAX =
++		NUM_MTK_VENDOR_ATTRS_AMNT_DUMP - 1
++};
++
++enum mtk_vendor_attr_wireless_ctrl {
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
++};
++
++enum mtk_vendor_attr_rfeature_ctrl {
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
++};
++
++#define CSI_MAX_COUNT 256
++#define ETH_ALEN 6
++
++struct csi_data {
++	s16 data_i[CSI_MAX_COUNT];
++	s16 data_q[CSI_MAX_COUNT];
++	s8 rssi;
++	u8 snr;
++	u32 ts;
++	u8 data_bw;
++	u8 pri_ch_idx;
++	u8 ta[ETH_ALEN];
++	u32 info;
++	u8 rx_mode;
++	u32 h_idx;
++	u16 tx_idx;
++	u16 rx_idx;
++};
++
++struct amnt_data {
++	u8 idx;
++	u8 addr[ETH_ALEN];
++	s8 rssi[4];
++	u32 last_seen;
++};
++#endif /* MTK_VENDOR_H */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0016-hostapd-MLO-Enhance-wpa-state-machine.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0016-hostapd-MLO-Enhance-wpa-state-machine.patch
deleted file mode 100644
index 0c17b68..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0016-hostapd-MLO-Enhance-wpa-state-machine.patch
+++ /dev/null
@@ -1,335 +0,0 @@
-From 11cfbaf42eaadf0fd7b50d13f0b7664c1675dc11 Mon Sep 17 00:00:00 2001
-From: Rameshkumar Sundaram <quic_ramess@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:46 +0530
-Subject: [PATCH 016/104] hostapd: MLO: Enhance wpa state machine
-
-Add required ML Specific members in wpa_authenticator and struct
-wpa_state_machine to maintain self and partner link information.
-
-Maintain state machine object in all associated link stations and
-destroy/remove references from the same whenever link stations are getting
-removed.
-
-Increase the wpa_group object reference count for all links in which ML
-station is getting associated and release the same whenever link stations
-are getting removed.
-
-Signed-off-by: Rameshkumar Sundaram <quic_ramess@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/ieee802_11.c |   9 ++--
- src/ap/sta_info.c   |  35 ++++++++++++++-
- src/ap/wpa_auth.c   | 101 +++++++++++++++++++++++++++++++++++++++++---
- src/ap/wpa_auth.h   |  16 +++++++
- src/ap/wpa_auth_i.h |   8 ++++
- 5 files changed, 159 insertions(+), 10 deletions(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 39c63f29b..9d04bdf43 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -4467,6 +4467,8 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
- 	}
- 
- 	sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
-+	sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
-+
- 	status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
- 	if (status != WLAN_STATUS_SUCCESS) {
- 		wpa_printf(MSG_DEBUG, "MLD: link: Element check failed");
-@@ -4474,7 +4476,6 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
- 	}
- 
- 	ap_sta_set_mld(sta, true);
--	sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
- 
- 	os_memcpy(&sta->mld_info, &origin_sta->mld_info, sizeof(sta->mld_info));
- 	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-@@ -4501,9 +4502,11 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
- 			ieee802_11_update_beacons(hapd->iface);
- 	}
- 
--	/* RSN Authenticator should always be the one on the original station */
-+	/* Maintain state machine reference on all link STAs, this is needed
-+	 * during Group rekey handling.
-+	 */
- 	wpa_auth_sta_deinit(sta->wpa_sm);
--	sta->wpa_sm = NULL;
-+	sta->wpa_sm = origin_sta->wpa_sm;
- 
- 	/*
- 	 * Do not initialize the EAPOL state machine.
-diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
-index 2423ff189..d483aa9d3 100644
---- a/src/ap/sta_info.c
-+++ b/src/ap/sta_info.c
-@@ -199,6 +199,26 @@ static void __ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
- 	hostapd_drv_sta_remove(hapd, sta->addr);
- }
- 
-+#ifdef CONFIG_IEEE80211BE
-+static void set_for_each_partner_link_sta(struct hostapd_data *hapd,
-+					  struct sta_info *psta, void *data)
-+{
-+	struct sta_info *lsta;
-+	struct hostapd_data *lhapd;
-+
-+	if (!ap_sta_is_mld(hapd, psta))
-+		return;
-+
-+	for_each_mld_link(lhapd, hapd) {
-+		if (lhapd == hapd)
-+			continue;
-+
-+		lsta = ap_get_sta(lhapd, psta->addr);
-+		if (lsta)
-+			lsta->wpa_sm = data;
-+	}
-+}
-+#endif /* CONFIG_IEEE80211BE */
- 
- void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
- {
-@@ -317,8 +337,17 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
- 
- #ifdef CONFIG_IEEE80211BE
- 	if (!ap_sta_is_mld(hapd, sta) ||
--	    hapd->mld_link_id == sta->mld_assoc_link_id)
-+	    hapd->mld_link_id == sta->mld_assoc_link_id) {
- 		wpa_auth_sta_deinit(sta->wpa_sm);
-+		/* Remove refrences from partner links. */
-+		set_for_each_partner_link_sta(hapd, sta, NULL);
-+	}
-+
-+	/* Release group references in case non assoc link STA is removed
-+	 * before assoc link STA
-+	 */
-+	if (hostapd_sta_is_link_sta(hapd, sta))
-+		wpa_release_link_auth_ref(sta->wpa_sm, hapd->mld_link_id);
- #else
- 	wpa_auth_sta_deinit(sta->wpa_sm);
- #endif /* CONFIG_IEEE80211BE */
-@@ -903,8 +932,10 @@ static void ap_sta_disconnect_common(struct hostapd_data *hapd,
- 	ieee802_1x_free_station(hapd, sta);
- #ifdef CONFIG_IEEE80211BE
- 	if (!hapd->conf->mld_ap ||
--	    hapd->mld_link_id == sta->mld_assoc_link_id)
-+	    hapd->mld_link_id == sta->mld_assoc_link_id) {
- 		wpa_auth_sta_deinit(sta->wpa_sm);
-+		set_for_each_partner_link_sta(hapd, sta, NULL);
-+	}
- #else
- 	wpa_auth_sta_deinit(sta->wpa_sm);
- #endif /* CONFIG_IEEE80211BE */
-diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
-index 0d15c4209..8c1052c25 100644
---- a/src/ap/wpa_auth.c
-+++ b/src/ap/wpa_auth.c
-@@ -102,6 +102,59 @@ static const u8 * wpa_auth_get_spa(const struct wpa_state_machine *sm)
- 	return sm->addr;
- }
- 
-+#ifdef CONFIG_IEEE80211BE
-+void wpa_release_link_auth_ref(struct wpa_state_machine *sm, int release_link_id)
-+{
-+	int link_id;
-+
-+	if (!sm || release_link_id >= MAX_NUM_MLD_LINKS)
-+		return;
-+
-+	for_each_sm_auth(sm, link_id)
-+		if (link_id == release_link_id) {
-+			wpa_group_put(sm->mld_links[link_id].wpa_auth,
-+				      sm->mld_links[link_id].wpa_auth->group);
-+			sm->mld_links[link_id].wpa_auth = NULL;
-+		}
-+}
-+
-+static int wpa_get_link_sta_auth(struct wpa_authenticator *wpa_auth, void *data)
-+{
-+	struct wpa_get_link_auth_ctx *ctx = data;
-+
-+	if (os_memcmp(wpa_auth->addr, ctx->addr, ETH_ALEN) != 0)
-+		return 0;
-+	ctx->wpa_auth = wpa_auth;
-+	return 1;
-+}
-+
-+static int wpa_get_primary_wpa_auth_cb(struct wpa_authenticator *wpa_auth, void *data)
-+{
-+	struct wpa_get_link_auth_ctx *ctx = data;
-+
-+	if (!wpa_auth->is_ml || os_memcmp(wpa_auth->mld_addr, ctx->addr, ETH_ALEN) != 0 ||
-+	    !wpa_auth->primary_auth)
-+		return 0;
-+
-+	ctx->wpa_auth = wpa_auth;
-+	return 1;
-+}
-+
-+static struct wpa_authenticator *
-+wpa_get_primary_wpa_auth(struct wpa_authenticator *wpa_auth)
-+{
-+	struct wpa_get_link_auth_ctx ctx;
-+
-+	if (!wpa_auth || !wpa_auth->is_ml || wpa_auth->primary_auth)
-+		return wpa_auth;
-+
-+	ctx.addr = wpa_auth->mld_addr;
-+	ctx.wpa_auth = NULL;
-+	wpa_auth_for_each_auth(wpa_auth, wpa_get_primary_wpa_auth_cb, &ctx);
-+
-+	return ctx.wpa_auth;
-+}
-+#endif /* CONFIG_IEEE80211BE */
- 
- static inline int wpa_auth_mic_failure_report(
- 	struct wpa_authenticator *wpa_auth, const u8 *addr)
-@@ -798,6 +851,10 @@ void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm)
- 
- static void wpa_free_sta_sm(struct wpa_state_machine *sm)
- {
-+#ifdef CONFIG_IEEE80211BE
-+	int link_id;
-+#endif /* CONFIG_IEEE80211BE */
-+
- #ifdef CONFIG_P2P
- 	if (WPA_GET_BE32(sm->ip_addr)) {
- 		wpa_printf(MSG_DEBUG,
-@@ -821,6 +878,13 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
- 	os_free(sm->last_rx_eapol_key);
- 	os_free(sm->wpa_ie);
- 	os_free(sm->rsnxe);
-+#ifdef CONFIG_IEEE80211BE
-+	for_each_sm_auth(sm, link_id) {
-+		wpa_group_put(sm->mld_links[link_id].wpa_auth,
-+			      sm->mld_links[link_id].wpa_auth->group);
-+		sm->mld_links[link_id].wpa_auth = NULL;
-+	}
-+#endif /* CONFIG_IEEE80211BE */
- 	wpa_group_put(sm->wpa_auth, sm->group);
- #ifdef CONFIG_DPP2
- 	wpabuf_clear_free(sm->dpp_z);
-@@ -831,7 +895,7 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
- 
- void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
- {
--	struct wpa_authenticator *wpa_auth;
-+	struct wpa_authenticator *wpa_auth, *primary_wpa_auth;
- 
- 	if (!sm)
- 		return;
-@@ -840,10 +904,18 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
- 	if (wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) {
- 		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
- 				"strict rekeying - force GTK rekey since STA is leaving");
-+
-+#ifdef CONFIG_IEEE80211BE
-+		if (wpa_auth->is_ml && !wpa_auth->primary_auth)
-+			primary_wpa_auth = wpa_get_primary_wpa_auth(wpa_auth);
-+		else
-+#endif /* CONFIG_IEEE80211BE */
-+			primary_wpa_auth = wpa_auth;
-+
- 		if (eloop_deplete_timeout(0, 500000, wpa_rekey_gtk,
--					  wpa_auth, NULL) == -1)
-+					  primary_wpa_auth, NULL) == -1)
- 			eloop_register_timeout(0, 500000, wpa_rekey_gtk,
--					       wpa_auth, NULL);
-+					       primary_wpa_auth, NULL);
- 	}
- 
- 	eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
-@@ -6835,6 +6907,7 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
- 	for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- 		struct mld_link_info *link = &info->links[link_id];
- 		struct mld_link *sm_link = &sm->mld_links[link_id];
-+		struct wpa_get_link_auth_ctx ctx;
- 
- 		sm_link->valid = link->valid;
- 		if (!link->valid)
-@@ -6849,10 +6922,28 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
- 			   MAC2STR(sm_link->own_addr),
- 			   MAC2STR(sm_link->peer_addr));
- 
--		if (link_id != mld_assoc_link_id)
-+		ml_rsn_info.links[i++].link_id = link_id;
-+
-+		if (link_id != mld_assoc_link_id) {
- 			sm->n_mld_affiliated_links++;
-+			ctx.addr = link->local_addr;
-+			ctx.wpa_auth = NULL;
-+			wpa_auth_for_each_auth(sm->wpa_auth, wpa_get_link_sta_auth, &ctx);
-+
-+			if (ctx.wpa_auth) {
-+				sm_link->wpa_auth = ctx.wpa_auth;
-+				wpa_group_get(sm_link->wpa_auth,
-+					      sm_link->wpa_auth->group);
-+			}
-+		} else {
-+			sm_link->wpa_auth = sm->wpa_auth;
-+		}
- 
--		ml_rsn_info.links[i++].link_id = link_id;
-+		if (!sm_link->wpa_auth)
-+			wpa_printf(MSG_ERROR, "Unable to find authenticator object for "
-+				    "ML STA " MACSTR " on link " MACSTR " link id %d",
-+				    MAC2STR(sm->own_mld_addr), MAC2STR(sm_link->own_addr),
-+				    link_id);
- 	}
- 
- 	ml_rsn_info.n_mld_links = i;
-diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
-index c74862307..1446872f3 100644
---- a/src/ap/wpa_auth.h
-+++ b/src/ap/wpa_auth.h
-@@ -647,4 +647,20 @@ void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
- 			      struct wpa_auth_ml_link_key_info *info,
- 			      bool mgmt_frame_prot, bool beacon_prot);
- 
-+#ifdef CONFIG_IEEE80211BE
-+void wpa_release_link_auth_ref(struct wpa_state_machine *sm,
-+			       int release_link_id);
-+
-+#define for_each_sm_auth(sm, link_id) \
-+	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) \
-+		if (sm->mld_links[link_id].valid && \
-+		    sm->mld_links[link_id].wpa_auth && \
-+		    sm->wpa_auth != sm->mld_links[link_id].wpa_auth) \
-+
-+struct wpa_get_link_auth_ctx {
-+	u8 *addr;
-+	struct wpa_authenticator *wpa_auth;
-+};
-+#endif /* CONFIG_IEEE80211BE */
-+
- #endif /* WPA_AUTH_H */
-diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
-index 9ba830415..9ba90749d 100644
---- a/src/ap/wpa_auth_i.h
-+++ b/src/ap/wpa_auth_i.h
-@@ -186,6 +186,7 @@ struct wpa_state_machine {
- 		size_t rsne_len;
- 		const u8 *rsnxe;
- 		size_t rsnxe_len;
-+		struct wpa_authenticator *wpa_auth;
- 	} mld_links[MAX_NUM_MLD_LINKS];
- #endif /* CONFIG_IEEE80211BE */
- };
-@@ -262,6 +263,13 @@ struct wpa_authenticator {
- #ifdef CONFIG_P2P
- 	struct bitfield *ip_pool;
- #endif /* CONFIG_P2P */
-+
-+#ifdef CONFIG_IEEE80211BE
-+	bool is_ml;
-+	u8 mld_addr[ETH_ALEN];
-+	u8 link_id;
-+	bool primary_auth;
-+#endif /* CONFIG_IEEE80211BE */
- };
- 
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0016-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0016-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch
new file mode 100644
index 0000000..9fb8962
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0016-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch
@@ -0,0 +1,631 @@
+From 75020e1c62dd8d16c872614861146d8cfc12f6df Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 30 May 2022 16:31:34 +0800
+Subject: [PATCH 016/126] mtk: hostapd: Support EDCCA hostapd configuration
+
+edcca_enable and edcca_compensation and implement edcca related handlers.
+
+---
+ hostapd/config_file.c             |  34 ++++++
+ hostapd/ctrl_iface.c              | 124 +++++++++++++++++++++
+ src/ap/ap_config.c                |   4 +
+ src/ap/ap_config.h                |  30 ++++++
+ src/ap/ap_drv_ops.c               |  24 +++++
+ src/ap/ap_drv_ops.h               |   4 +
+ src/ap/hostapd.c                  |   7 ++
+ src/common/mtk_vendor.h           |  20 ++--
+ src/drivers/driver.h              |   4 +
+ src/drivers/driver_nl80211.c      | 174 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   7 ++
+ 12 files changed, 427 insertions(+), 6 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index f3968ec95..9f5ee48bf 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5470,6 +5470,40 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		os_strlcpy(bss->apup_peer_ifname_prefix,
+ 		           pos, sizeof(bss->apup_peer_ifname_prefix));
+ #endif // def CONFIG_APUP
++	} else if (os_strcmp(buf, "edcca_threshold") == 0) {
++		if (hostapd_parse_intlist(&conf->edcca_threshold, pos) ||
++		    conf->edcca_threshold[0] < EDCCA_MIN_CONFIG_THRES ||
++		    conf->edcca_threshold[0] > EDCCA_MAX_CONFIG_THRES ||
++		    conf->edcca_threshold[1] < EDCCA_MIN_CONFIG_THRES ||
++		    conf->edcca_threshold[1] > EDCCA_MAX_CONFIG_THRES ||
++		    conf->edcca_threshold[2] < EDCCA_MIN_CONFIG_THRES ||
++		    conf->edcca_threshold[2] > EDCCA_MAX_CONFIG_THRES ||
++		    conf->edcca_threshold[3] < EDCCA_MIN_CONFIG_THRES ||
++		    conf->edcca_threshold[3] > EDCCA_MAX_CONFIG_THRES) {
++			wpa_printf(MSG_ERROR, "Line %d: invalid edcca threshold",
++				   line);
++			return 1;
++		}
++	} else if (os_strcmp(buf, "edcca_enable") == 0) {
++		int mode = atoi(pos);
++		if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
++			wpa_printf(MSG_ERROR, "Line %d: Invalid edcca_enable %d;"
++				  " allowed value 0 (Force Disable) or 1(Auto) ",
++				   line, mode);
++			return 1;
++		}
++		conf->edcca_enable = (u8) mode;
++	} else if (os_strcmp(buf, "edcca_compensation") == 0) {
++		int val = atoi(pos);
++		if (val < EDCCA_MIN_COMPENSATION ||
++		    val > EDCCA_MAX_COMPENSATION) {
++			wpa_printf(MSG_ERROR, "Line %d: Invalid compensation"
++				   " value %d; allowed value %d ~ %d.",
++				   line, val, EDCCA_MIN_COMPENSATION,
++				   EDCCA_MAX_COMPENSATION);
++			return 1;
++		}
++		conf->edcca_compensation = (s8) val;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 40244c5e7..8a9fa3ec9 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -545,6 +545,19 @@ static const char * pbc_status_str(enum pbc_status status)
+ }
+ 
+ 
++static const char *edcca_mode_str(enum edcca_mode status)
++{
++	switch (status) {
++		case EDCCA_MODE_FORCE_DISABLE:
++			return "Force Disable";
++		case EDCCA_MODE_AUTO:
++			return "Auto";
++		default:
++			return "Unknown";
++	}
++}
++
++
+ static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd,
+ 					     char *buf, size_t buflen)
+ {
+@@ -3774,6 +3787,111 @@ static int hostapd_ctrl_iface_link_remove(struct hostapd_data *hapd, char *cmd,
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
+ 
++static int
++hostapd_ctrl_iface_set_edcca(struct hostapd_data *hapd, char *cmd,
++					 char *buf, size_t buflen)
++{
++	char *pos, *config, *value;
++	config = cmd;
++	pos = os_strchr(config, ' ');
++	if (pos == NULL)
++		return -1;
++	*pos++ = '\0';
++
++	if (pos == NULL)
++		return -1;
++	value = pos;
++
++	if (os_strcmp(config, "enable") == 0) {
++		int mode = atoi(value);
++		if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
++			wpa_printf(MSG_ERROR, "Invalid value for edcca enable");
++			return -1;
++		}
++		hapd->iconf->edcca_enable = (u8) mode;
++		if (hostapd_drv_configure_edcca_enable(hapd) != 0)
++			return -1;
++	} else if (os_strcmp(config, "compensation") == 0) {
++		int compensation = atoi(value);
++		if (compensation < EDCCA_MIN_COMPENSATION ||
++		    compensation > EDCCA_MAX_COMPENSATION) {
++			wpa_printf(MSG_ERROR, "Invalid value for edcca compensation");
++			return -1;
++		}
++		hapd->iconf->edcca_compensation = (s8) compensation;
++		if (hostapd_drv_configure_edcca_enable(hapd) != 0)
++			return -1;
++	} else if (os_strcmp(config, "threshold") == 0) {
++		char *thres_value;
++		thres_value = os_strchr(value, ':');
++		if (thres_value == NULL)
++			return -1;
++		*thres_value++ = '\0';
++
++		if (thres_value == NULL)
++			return -1;
++		int bw_idx = atoi(value);
++		int threshold = atoi(thres_value);
++
++		if (bw_idx < EDCCA_BW_20 || bw_idx > EDCCA_BW_160) {
++			wpa_printf(MSG_ERROR,
++				   "Unsupported Bandwidth idx %d for SET_EDCCA",
++				   bw_idx);
++			return -1;
++		}
++		if (threshold < EDCCA_MIN_CONFIG_THRES ||
++		    threshold > EDCCA_MAX_CONFIG_THRES) {
++			wpa_printf(MSG_ERROR,
++				   "Unsupported threshold %d for SET_EDCCA",
++				   threshold);
++			return -1;
++		}
++
++		int threshold_arr[EDCCA_MAX_BW_NUM];
++		/* 0x7f means keep the origival value in firmware */
++		os_memset(threshold_arr, 0x7f, sizeof(threshold_arr));
++		threshold_arr[bw_idx] = threshold;
++
++		if (hostapd_drv_configure_edcca_threshold(hapd, threshold_arr) != 0)
++			return -1;
++	} else {
++		wpa_printf(MSG_ERROR,
++			"Unsupported parameter %s for SET_EDCCA", config);
++		return -1;
++	}
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
++
++static int
++hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *cmd, char *buf,
++			     size_t buflen)
++{
++	char *pos, *end;
++
++	pos = buf;
++	end = buf + buflen;
++	u8 value[EDCCA_MAX_BW_NUM] = {0};
++
++	if (os_strcmp(cmd, "enable") == 0) {
++		return os_snprintf(pos, end - pos, "Enable: %s\n",
++				   edcca_mode_str(hapd->iconf->edcca_enable));
++	} else if (os_strcmp(cmd, "compensation") == 0) {
++		return os_snprintf(pos, end - pos, "Compensation: %d\n",
++				  hapd->iconf->edcca_compensation);
++	} else if (os_strcmp(cmd, "threshold") == 0) {
++		if (hostapd_drv_get_edcca(hapd, EDCCA_CTRL_GET_THRES, &value) != 0)
++			return -1;
++		return os_snprintf(pos, end - pos,
++				   "Threshold BW20: 0x%x, BW40: 0x%x, BW80: 0x%x, BW160: 0x%x\n",
++				   value[0], value[1], value[2], value[3]);
++	} else {
++		wpa_printf(MSG_ERROR,
++			"Unsupported parameter %s for GET_EDCCA", cmd);
++		return -1;
++	}
++}
++
+ 
+ #ifdef CONFIG_NAN_USD
+ 
+@@ -4669,6 +4787,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 			reply_len = -1;
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
++	} else if (os_strncmp(buf, "SET_EDCCA ", 10) == 0) {
++		reply_len = hostapd_ctrl_iface_set_edcca(hapd, buf+10, reply,
++							  reply_size);
++	} else if (os_strncmp(buf, "GET_EDCCA ", 10) == 0) {
++		reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
++							  reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 67661dfe0..112954a89 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -305,6 +305,9 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 	conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL;
+ #endif /* CONFIG_AIRTIME_POLICY */
+ 
++	conf->edcca_enable = EDCCA_MODE_AUTO;
++	conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
++
+ 	hostapd_set_and_check_bw320_offset(conf, 0);
+ 
+ 	return conf;
+@@ -1046,6 +1049,7 @@ void hostapd_config_free(struct hostapd_config *conf)
+ #ifdef CONFIG_ACS
+ 	os_free(conf->acs_chan_bias);
+ #endif /* CONFIG_ACS */
++	os_free(conf->edcca_threshold);
+ 	wpabuf_free(conf->lci);
+ 	wpabuf_free(conf->civic);
+ #ifdef CONFIG_AFC
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 25ac22e31..b9afe1f47 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1334,8 +1334,38 @@ struct hostapd_config {
+ 		int min_power;
+ 	} afc;
+ #endif /* CONFIG_AFC */
++
++	u8 edcca_enable;
++	s8 edcca_compensation;
++	int *edcca_threshold;
++};
++
++enum edcca_mode {
++	EDCCA_MODE_FORCE_DISABLE = 0,
++	EDCCA_MODE_AUTO = 1,
++};
++
++enum edcca_bw_id {
++	EDCCA_BW_20 = 0,
++	EDCCA_BW_40,
++	EDCCA_BW_80,
++	EDCCA_BW_160,
++	EDCCA_MAX_BW_NUM,
++};
++
++enum mtk_vendor_attr_edcca_ctrl_mode {
++	EDCCA_CTRL_SET_EN = 0,
++	EDCCA_CTRL_SET_THRES,
++	EDCCA_CTRL_GET_EN,
++	EDCCA_CTRL_GET_THRES,
++	EDCCA_CTRL_NUM,
+ };
+ 
++#define EDCCA_DEFAULT_COMPENSATION -6
++#define EDCCA_MIN_COMPENSATION -126
++#define EDCCA_MAX_COMPENSATION 126
++#define EDCCA_MIN_CONFIG_THRES -126
++#define EDCCA_MAX_CONFIG_THRES 0
+ 
+ static inline enum oper_chan_width
+ hostapd_get_oper_chwidth(struct hostapd_config *conf)
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 7c9527cd3..c7cacbd81 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1274,3 +1274,27 @@ int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
+ 	return hapd->driver->set_secure_ranging_ctx(hapd->drv_priv, &params);
+ }
+ #endif /* CONFIG_PASN */
++
++int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->driver->configure_edcca_enable)
++		return 0;
++	return hapd->driver->configure_edcca_enable(hapd->drv_priv,
++			hapd->iconf->edcca_enable,
++				hapd->iconf->edcca_compensation);
++}
++
++int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
++					  const int *threshold)
++{
++	if (!hapd->driver || !hapd->driver->configure_edcca_threshold)
++		return 0;
++	return hapd->driver->configure_edcca_threshold(hapd->drv_priv, threshold);
++}
++
++int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
++{
++	if (!hapd->driver || !hapd->driver->get_edcca)
++		return 0;
++	return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 58ca046c6..23a331914 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -152,6 +152,10 @@ int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
+ 				       u8 ltf_keyseed_len,
+ 				       const u8 *ltf_keyseed, u32 action);
+ 
++int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
++int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
++					  const int *threshold);
++int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 8159194e1..972bb1763 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2756,6 +2756,13 @@ dfs_offload:
+ 	}
+ #endif /* CONFIG_MESH */
+ 
++	if (hostapd_drv_configure_edcca_enable(hapd) < 0)
++		goto fail;
++
++	if (hostapd_drv_configure_edcca_threshold(hapd,
++						  hapd->iconf->edcca_threshold) < 0)
++		goto fail;
++
+ 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ 		   iface->bss[0]->conf->iface);
+ 	if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 4a19d2fc9..6121857dd 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -30,14 +30,22 @@ enum mtk_vendor_attr_edcca_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
+ };
+ 
+-enum mtk_vendor_attr_edcca_ctrl_mode {
+-	EDCCA_CTRL_SET_EN = 0,
+-	EDCCA_CTRL_SET_THERS,
+-	EDCCA_CTRL_GET_EN,
+-	EDCCA_CTRL_GET_THERS,
+-	EDCCA_CTRL_NUM,
++enum mtk_vendor_attr_edcca_dump {
++	MTK_VENDOR_ATTR_EDCCA_DUMP_UNSPEC = 0,
++
++	MTK_VENDOR_ATTR_EDCCA_DUMP_MODE,
++	MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL,
++	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL,
++	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL,
++	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP,
++	MTK_VENDOR_ATTR_EDCCA_DUMP_MAX =
++		NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
+ };
+ 
++
+ static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
+ 	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index e1a447333..bf103516b 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5246,6 +5246,10 @@ struct wpa_driver_ops {
+ 			      const u8 *match, size_t match_len,
+ 			      bool multicast);
+ #endif /* CONFIG_TESTING_OPTIONS */
++	int (*configure_edcca_enable)(void *priv, const u8 edcca_enable,
++				  const s8 edcca_compensation);
++	int (*configure_edcca_threshold)(void *priv, const int *threshold);
++	int (*get_edcca)(void *priv, const u8 mode, u8 *value);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 2c68bf997..606632aad 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -39,6 +39,8 @@
+ #include "radiotap_iter.h"
+ #include "rfkill.h"
+ #include "driver_nl80211.h"
++#include "common/mtk_vendor.h"
++#include "ap/ap_config.h"
+ 
+ 
+ #ifndef NETLINK_CAP_ACK
+@@ -14101,6 +14103,174 @@ static int testing_nl80211_radio_disable(void *priv, int disabled)
+ 
+ #endif /* CONFIG_TESTING_OPTIONS */
+ 
++static int nl80211_configure_edcca_enable(void *priv,
++					  const u8 edcca_enable,
++					  const s8 edcca_compensation)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_edcca_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting EDCCA enable");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
++	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_EN) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, edcca_enable) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
++		edcca_compensation)) {
++		wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to configure EDCCA enable. ret=%d (%s) ",
++			   ret, strerror(-ret));
++	}
++	return ret;
++}
++
++static int nl80211_configure_edcca_threshold(void *priv, const int *threshold)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_edcca_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting EDCCA threshold");
++		return 0;
++	}
++
++	if (!threshold) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Input EDCCA threshold is empty!");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
++	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_THRES) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, threshold[0] & 0xff) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL, threshold[1] & 0xff) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL, threshold[2] & 0xff) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL, threshold[3] & 0xff)) {
++		wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to configure EDCCA threshold. ret=%d (%s) ",
++			   ret, strerror(-ret));
++	}
++	return ret;
++}
++
++
++static int edcca_info_handler(struct nl_msg *msg, void *arg)
++{
++	u8 *info = (u8 *) arg;
++	struct nlattr *tb[NL80211_ATTR_MAX + 1];
++	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_MAX + 1];
++	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++	struct nlattr *nl_vend, *attr;
++
++	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++		  genlmsg_attrlen(gnlh, 0), NULL);
++
++	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++	if (!nl_vend)
++		return NL_SKIP;
++
++	nla_parse(tb_vendor, MTK_VENDOR_ATTR_EDCCA_DUMP_MAX,
++		  nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++	attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL];
++	if (!attr) {
++		wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL");
++		return NL_SKIP;
++	}
++
++	*info++ = nla_get_u8(attr);
++
++	attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL];
++	if (!attr) {
++		wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL");
++		return NL_SKIP;
++	}
++
++	*info++ = nla_get_u8(attr);
++
++	attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL];
++	if (!attr) {
++		wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL");
++		return NL_SKIP;
++	}
++
++	*info++ = nla_get_u8(attr);
++
++	attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL];
++	if (!attr) {
++		wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL");
++		return NL_SKIP;
++	}
++
++	*info = nla_get_u8(attr);
++	return NL_SKIP;
++}
++
++
++static int nl80211_get_edcca(void *priv, const u8 mode, u8 *value)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_edcca_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting EDCCA threshold");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
++	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED)) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, mode)) {
++		wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++	nla_nest_end(msg, data);
++	ret = send_and_recv_resp(drv, msg, edcca_info_handler, value);
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to get EDCCA configuration. ret=%d (%s)",
++			   ret, strerror(-ret));
++	}
++	return ret;
++}
++
+ 
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+@@ -14262,4 +14432,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.register_frame = testing_nl80211_register_frame,
+ 	.radio_disable = testing_nl80211_radio_disable,
+ #endif /* CONFIG_TESTING_OPTIONS */
++/* Need ifdef CONFIG_DRIVER_NL80211_MTK */
++	.configure_edcca_enable = nl80211_configure_edcca_enable,
++	.configure_edcca_threshold = nl80211_configure_edcca_threshold,
++	.get_edcca = nl80211_get_edcca,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 618746e67..62c47efbd 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -200,6 +200,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int secure_ranging_ctx_vendor_cmd_avail:1;
+ 	unsigned int puncturing:1;
+ 	unsigned int qca_ap_allowed_freqs:1;
++	unsigned int mtk_edcca_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index d5ba66b10..465fd318d 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -18,6 +18,7 @@
+ #include "common/qca-vendor-attr.h"
+ #include "common/brcm_vendor.h"
+ #include "driver_nl80211.h"
++#include "common/mtk_vendor.h"
+ 
+ 
+ static int protocol_feature_handler(struct nl_msg *msg, void *arg)
+@@ -1138,6 +1139,12 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 					break;
+ 				}
+ #endif /* CONFIG_DRIVER_NL80211_BRCM */
++			} else if (vinfo->vendor_id == OUI_MTK) {
++				switch (vinfo->subcmd) {
++				case MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL:
++					drv->mtk_edcca_vendor_cmd_avail = 1;
++					break;
++				}
+ 			}
+ 
+ 			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0017-hostapd-MLO-add-support-for-MLO-rekey.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0017-hostapd-MLO-add-support-for-MLO-rekey.patch
deleted file mode 100644
index 5895635..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0017-hostapd-MLO-add-support-for-MLO-rekey.patch
+++ /dev/null
@@ -1,799 +0,0 @@
-From 5e6164cb6143d55409c08ae9bfd859efa188e383 Mon Sep 17 00:00:00 2001
-From: Rameshkumar Sundaram <quic_ramess@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:47 +0530
-Subject: [PATCH 017/104] hostapd: MLO: add support for MLO rekey
-
-Currently wpa group rekey is not supported for ML Stations when non-assoc
-link initiates a group rekey, to support the same following changes have
-been made-
-  * Calculate links specific MLO GTK/IGTK and BIGTK KDE lengths based on
-    corresponding cipher and key instead of taking length of one link and
-    multiplying it by no of associated links.
-  * For MLD, Arm group key rekey timer on one of the links and whenever it
-    fires do group key rekey for all links.
-
-Signed-off-by: Rameshkumar Sundaram <quic_ramess@quicinc.com>
-Co-developed-by: Adil Saeed Musthafa <quic_adilm@quicinc.com>
-Signed-off-by: Adil Saeed Musthafa <quic_adilm@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/drv_callbacks.c                        |   2 +-
- src/ap/ieee802_11.c                           |  13 +-
- src/ap/wpa_auth.c                             | 310 +++++++++++++++---
- src/ap/wpa_auth.h                             |   9 +-
- src/ap/wpa_auth_glue.c                        |  22 ++
- src/ap/wpa_auth_i.h                           |   1 +
- src/ap/wpa_auth_ie.c                          |  12 +-
- src/common/wpa_common.h                       |   1 +
- tests/fuzzing/eapol-key-auth/eapol-key-auth.c |   2 +-
- wpa_supplicant/ibss_rsn.c                     |   2 +-
- 10 files changed, 317 insertions(+), 57 deletions(-)
-
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index 064c7abae..dc21977ff 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -528,7 +528,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
- 					  elems.rsnxe ? elems.rsnxe - 2 : NULL,
- 					  elems.rsnxe ? elems.rsnxe_len + 2 : 0,
- 					  elems.mdie, elems.mdie_len,
--					  elems.owe_dh, elems.owe_dh_len);
-+					  elems.owe_dh, elems.owe_dh_len, NULL);
- 		reason = WLAN_REASON_INVALID_IE;
- 		status = WLAN_STATUS_INVALID_IE;
- 		switch (res) {
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 9d04bdf43..7ee18f4ae 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -1887,7 +1887,7 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
- 				  elems.rsn_ie - 2, elems.rsn_ie_len + 2,
- 				  elems.rsnxe ? elems.rsnxe - 2 : NULL,
- 				  elems.rsnxe ? elems.rsnxe_len + 2 : 0,
--				  elems.mdie, elems.mdie_len, NULL, 0);
-+				  elems.mdie, elems.mdie_len, NULL, 0, NULL);
- 	resp = wpa_res_to_status_code(res);
- 	if (resp != WLAN_STATUS_SUCCESS)
- 		goto fail;
-@@ -3778,7 +3778,7 @@ u16 owe_process_rsn_ie(struct hostapd_data *hapd,
- 	rsn_ie_len += 2;
- 	res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
- 				  hapd->iface->freq, rsn_ie, rsn_ie_len,
--				  NULL, 0, NULL, 0, owe_dh, owe_dh_len);
-+				  NULL, 0, NULL, 0, owe_dh, owe_dh_len, NULL);
- 	status = wpa_res_to_status_code(res);
- 	if (status != WLAN_STATUS_SUCCESS)
- 		goto end;
-@@ -3867,6 +3867,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
- 	const u8 *wpa_ie;
- 	size_t wpa_ie_len;
- 	const u8 *p2p_dev_addr = NULL;
-+	struct hostapd_data *assoc_hapd;
-+	struct sta_info *assoc_sta = NULL;
- 
- 	resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len);
- 	if (resp != WLAN_STATUS_SUCCESS)
-@@ -4041,6 +4043,10 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
- 		wpa_ie_len += 2;
- 
- 		if (!sta->wpa_sm) {
-+			if (!link)
-+				assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta,
-+								     &assoc_hapd);
-+
- 			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
- 							sta->addr,
- 							p2p_dev_addr);
-@@ -4076,7 +4082,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
- 					  elems->rsnxe ? elems->rsnxe_len + 2 :
- 					  0,
- 					  elems->mdie, elems->mdie_len,
--					  elems->owe_dh, elems->owe_dh_len);
-+					  elems->owe_dh, elems->owe_dh_len,
-+					  assoc_sta ? assoc_sta->wpa_sm : NULL);
- 		resp = wpa_res_to_status_code(res);
- 		if (resp != WLAN_STATUS_SUCCESS)
- 			return resp;
-diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
-index 8c1052c25..7a07dcc4c 100644
---- a/src/ap/wpa_auth.c
-+++ b/src/ap/wpa_auth.c
-@@ -71,6 +71,9 @@ static void wpa_group_put(struct wpa_authenticator *wpa_auth,
- 			  struct wpa_group *group);
- static int ieee80211w_kde_len(struct wpa_state_machine *sm);
- static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
-+static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
-+				 struct wpa_group *group);
-+
- 
- static const u32 eapol_key_timeout_first = 100; /* ms */
- static const u32 eapol_key_timeout_subseq = 1000; /* ms */
-@@ -102,6 +105,22 @@ static const u8 * wpa_auth_get_spa(const struct wpa_state_machine *sm)
- 	return sm->addr;
- }
- 
-+static void wpa_update_gkeydone(struct wpa_state_machine *sm, int update)
-+{
-+#ifdef CONFIG_IEEE80211BE
-+	int link_id;
-+#endif /* CONFIG_IEEE80211BE */
-+	if (!sm || !sm->wpa_auth)
-+		return;
-+
-+	sm->wpa_auth->group->GKeyDoneStations += update;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	for_each_sm_auth(sm, link_id)
-+		sm->mld_links[link_id].wpa_auth->group->GKeyDoneStations += update;
-+#endif /* CONFIG_IEEE80211BE */
-+}
-+
- #ifdef CONFIG_IEEE80211BE
- void wpa_release_link_auth_ref(struct wpa_state_machine *sm, int release_link_id)
- {
-@@ -139,10 +158,12 @@ static int wpa_get_primary_wpa_auth_cb(struct wpa_authenticator *wpa_auth, void
- 	ctx->wpa_auth = wpa_auth;
- 	return 1;
- }
-+#endif /* CONFIG_IEEE80211BE */
- 
- static struct wpa_authenticator *
- wpa_get_primary_wpa_auth(struct wpa_authenticator *wpa_auth)
- {
-+#ifdef CONFIG_IEEE80211BE
- 	struct wpa_get_link_auth_ctx ctx;
- 
- 	if (!wpa_auth || !wpa_auth->is_ml || wpa_auth->primary_auth)
-@@ -153,8 +174,10 @@ wpa_get_primary_wpa_auth(struct wpa_authenticator *wpa_auth)
- 	wpa_auth_for_each_auth(wpa_auth, wpa_get_primary_wpa_auth_cb, &ctx);
- 
- 	return ctx.wpa_auth;
--}
-+#else
-+	return wpa_auth;
- #endif /* CONFIG_IEEE80211BE */
-+}
- 
- static inline int wpa_auth_mic_failure_report(
- 	struct wpa_authenticator *wpa_auth, const u8 *addr)
-@@ -420,15 +443,16 @@ static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
- 	}
- }
- 
--
--static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
-+static void wpa_rekey_all_groups(struct wpa_authenticator *wpa_auth)
- {
--	struct wpa_authenticator *wpa_auth = eloop_ctx;
- 	struct wpa_group *group, *next;
- 
- 	wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK");
- 	group = wpa_auth->group;
- 	while (group) {
-+		wpa_printf(MSG_DEBUG, "GTK rekey start for authenticator("
-+			   MACSTR "), group vlan %d",
-+			   MAC2STR(wpa_auth->addr), group->vlan_id);
- 		wpa_group_get(wpa_auth, group);
- 
- 		group->GTKReKey = true;
-@@ -441,6 +465,80 @@ static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
- 		wpa_group_put(wpa_auth, group);
- 		group = next;
- 	}
-+}
-+
-+#ifdef CONFIG_IEEE80211BE
-+static void wpa_update_all_gtks(struct wpa_authenticator *wpa_auth)
-+{
-+	struct wpa_group *group, *next;
-+
-+	group = wpa_auth->group;
-+	while (group) {
-+		wpa_group_get(wpa_auth, group);
-+
-+		wpa_group_update_gtk(wpa_auth, group);
-+		next = group->next;
-+		wpa_group_put(wpa_auth, group);
-+		group = next;
-+	}
-+}
-+
-+static int wpa_update_all_gtks_cb(struct wpa_authenticator *wpa_auth, void *ctx)
-+{
-+	u8 *mld_addr = ctx;
-+
-+	if (os_memcmp(wpa_auth->mld_addr, mld_addr, ETH_ALEN) != 0)
-+		return 0;
-+
-+	wpa_update_all_gtks(wpa_auth);
-+	return 0;
-+}
-+
-+static int wpa_rekey_all_groups_cb(struct wpa_authenticator *wpa_auth,
-+				   void *ctx)
-+{
-+	u8 *mld_addr = ctx;
-+
-+	if (os_memcmp(wpa_auth->mld_addr, mld_addr, ETH_ALEN) != 0)
-+		return 0;
-+
-+	wpa_rekey_all_groups(wpa_auth);
-+	return 0;
-+}
-+#endif /* CONFIG_IEEE80211BE */
-+
-+static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
-+{
-+	struct wpa_authenticator *wpa_auth = eloop_ctx;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	if (wpa_auth->is_ml) {
-+		/* Non Primary ML authenticator eloop timer for group rekey is never
-+		 * started and shouldn't fire too check and warn just in case
-+		 */
-+		if (!wpa_auth->primary_auth) {
-+			wpa_printf(MSG_DEBUG,
-+				   "WPA: Can't start GTK rekey on non-primary ML authenticator");
-+			return;
-+		}
-+		/*
-+		 * Generate all the new I/BIG/GTKs
-+		 */
-+		wpa_auth_for_each_auth(wpa_auth, wpa_update_all_gtks_cb,
-+				       wpa_auth->mld_addr);
-+
-+		/*
-+		 * Send all the generated I/BIG/GTKs to the respective
-+		 * stations via G1 messages
-+		 */
-+		wpa_auth_for_each_auth(wpa_auth, wpa_rekey_all_groups_cb,
-+				       wpa_auth->mld_addr);
-+	} else {
-+		wpa_rekey_all_groups(wpa_auth);
-+	}
-+#else
-+	wpa_rekey_all_groups(wpa_auth);
-+#endif /* CONFIG_IEEE80211BE */
- 
- 	if (wpa_auth->conf.wpa_group_rekey) {
- 		eloop_register_timeout(wpa_auth->conf.wpa_group_rekey,
-@@ -590,8 +688,19 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
- 	wpa_auth = os_zalloc(sizeof(struct wpa_authenticator));
- 	if (!wpa_auth)
- 		return NULL;
-+
- 	os_memcpy(wpa_auth->addr, addr, ETH_ALEN);
- 	os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
-+
-+#ifdef CONFIG_IEEE80211BE
-+	if (conf->mld_addr) {
-+		wpa_auth->is_ml = true;
-+		wpa_auth->link_id = conf->link_id;
-+		wpa_auth->primary_auth = !conf->first_link_auth;
-+		os_memcpy(wpa_auth->mld_addr, conf->mld_addr, ETH_ALEN);
-+	}
-+#endif /* CONFIG_IEEE80211BE */
-+
- 	wpa_auth->cb = cb;
- 	wpa_auth->cb_ctx = cb_ctx;
- 
-@@ -635,7 +744,15 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
- 				       wpa_rekey_gmk, wpa_auth, NULL);
- 	}
- 
-+#ifdef CONFIG_IEEE80211BE
-+	/* For ML AP, run Group rekey timer only on one link(first) and whenever
-+	 * it fires do rekey on all associated ML links at one shot.
-+	 */
-+	if ((!wpa_auth->is_ml || !conf->first_link_auth) &&
-+	    wpa_auth->conf.wpa_group_rekey) {
-+#else
- 	if (wpa_auth->conf.wpa_group_rekey) {
-+#endif /* CONFIG_IEEE80211BE */
- 		eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0,
- 				       wpa_rekey_gtk, wpa_auth, NULL);
- 	}
-@@ -699,6 +816,10 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth)
- 	struct wpa_group *group, *prev;
- 
- 	eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
-+
-+	/* TODO: assign ML Primary authenticator to next link auth and
-+	 * start rekey timer.
-+	 */
- 	eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
- 
- 	pmksa_cache_auth_deinit(wpa_auth->pmksa);
-@@ -868,7 +989,7 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
- 	}
- #endif /* CONFIG_P2P */
- 	if (sm->GUpdateStationKeys) {
--		sm->group->GKeyDoneStations--;
-+		wpa_update_gkeydone(sm, -1);
- 		sm->GUpdateStationKeys = false;
- 	}
- #ifdef CONFIG_IEEE80211R_AP
-@@ -1669,12 +1790,14 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
- 			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
- 					LOGGER_INFO,
- 					"received EAPOL-Key Request for GTK rekeying");
--			eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
-+
-+			eloop_cancel_timeout(wpa_rekey_gtk,
-+					     wpa_get_primary_wpa_auth(wpa_auth), NULL);
- 			if (wpa_auth_gtk_rekey_in_process(wpa_auth))
- 				wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG,
- 						"skip new GTK rekey - already in process");
- 			else
--				wpa_rekey_gtk(wpa_auth, NULL);
-+				wpa_rekey_gtk(wpa_get_primary_wpa_auth(wpa_auth), NULL);
- 		}
- 	} else {
- 		/* Do not allow the same key replay counter to be reused. */
-@@ -2207,7 +2330,7 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
- 			 * Reauthentication cancels the pending group key
- 			 * update for this STA.
- 			 */
--			sm->group->GKeyDoneStations--;
-+			wpa_update_gkeydone(sm, -1);
- 			sm->GUpdateStationKeys = false;
- 			sm->PtkGroupInit = true;
- 		}
-@@ -2284,7 +2407,7 @@ SM_STATE(WPA_PTK, INITIALIZE)
- 
- 	sm->keycount = 0;
- 	if (sm->GUpdateStationKeys)
--		sm->group->GKeyDoneStations--;
-+		wpa_update_gkeydone(sm, -1);
- 	sm->GUpdateStationKeys = false;
- 	if (sm->wpa == WPA_VERSION_WPA)
- 		sm->PInitAKeys = false;
-@@ -4058,41 +4181,54 @@ static void wpa_auth_get_ml_key_info(struct wpa_authenticator *wpa_auth,
- 	wpa_auth->cb->get_ml_key_info(wpa_auth->cb_ctx, info);
- }
- 
-+#define KDE_HDR_LEN (1 + 1 + RSN_SELECTOR_LEN)
- 
- static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm)
- {
--	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
--	struct wpa_group *gsm = sm->group;
--	size_t gtk_len = gsm->GTK_len;
--	size_t igtk_len;
--	size_t kde_len;
--	unsigned int n_links;
-+	struct wpa_authenticator *wpa_auth;
-+	size_t kde_len = 0;
-+	int link_id;
- 
- 	if (sm->mld_assoc_link_id < 0)
- 		return 0;
- 
--	n_links = sm->n_mld_affiliated_links + 1;
-+	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
-+		if (!sm->mld_links[link_id].valid)
-+			continue;
-+
-+		wpa_auth = sm->mld_links[link_id].wpa_auth;
-+		if (!wpa_auth || !wpa_auth->group)
-+			continue;
- 
--	/* MLO GTK KDE for each link */
--	kde_len = n_links * (2 + RSN_SELECTOR_LEN + 1 + 6 + gtk_len);
-+		/* MLO GTK KDE
-+		 * Header + Key-idx and Link-id + PN
-+		 */
-+		kde_len += (KDE_HDR_LEN  + 1 + WPA_MLO_GTK_KDE_PN_LEN);
-+		kde_len += wpa_auth->group->GTK_len;
- 
--	if (!sm->mgmt_frame_prot)
--		return kde_len;
-+		if (!sm->mgmt_frame_prot)
-+			continue;
- 
--	/* MLO IGTK KDE for each link */
--	igtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
--	kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
-+		if (wpa_auth->conf.tx_bss_auth)
-+			wpa_auth = wpa_auth->conf.tx_bss_auth;
- 
--	if (wpa_auth->conf.tx_bss_auth) {
--		wpa_auth = wpa_auth->conf.tx_bss_auth;
--		igtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
--	}
-+		/* MLO IGTK KDE
-+		 * Header + Key-idx & IPN + Link-id
-+		 */
-+		kde_len += (KDE_HDR_LEN + WPA_IGTK_KDE_PREFIX_LEN + 1);
-+		kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
- 
--	if (!wpa_auth->conf.beacon_prot)
--		return kde_len;
-+		if (!wpa_auth->conf.beacon_prot)
-+			continue;
-+
-+		/* MLO BIGTK KDE
-+		 * Header + Key-idx & IPN + Link-id
-+		 */
-+		kde_len += (KDE_HDR_LEN + WPA_BIGTK_KDE_PREFIX_LEN + 1);
-+		kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
-+	}
- 
--	/* MLO BIGTK KDE for each link */
--	kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
-+	wpa_printf(MSG_DEBUG, "MLO Group kdes len = %zu", kde_len);
- 
- 	return kde_len;
- }
-@@ -4102,6 +4238,7 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
- {
- 	struct wpa_auth_ml_key_info ml_key_info;
- 	unsigned int i, link_id;
-+	u8 *start = pos;
- 
- 	/* First fetch the key information from all the authenticators */
- 	os_memset(&ml_key_info, 0, sizeof(ml_key_info));
-@@ -4153,8 +4290,10 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
- 		i++;
- 	}
- 
--	if (!sm->mgmt_frame_prot)
-+	if (!sm->mgmt_frame_prot) {
-+		wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start);
- 		return pos;
-+	}
- 
- 	/* Add MLO IGTK KDEs */
- 	for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
-@@ -4193,8 +4332,10 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
- 		i++;
- 	}
- 
--	if (!sm->wpa_auth->conf.beacon_prot)
-+	if (!sm->wpa_auth->conf.beacon_prot) {
-+		wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start);
- 		return pos;
-+	}
- 
- 	/* Add MLO BIGTK KDEs */
- 	for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
-@@ -4234,6 +4375,7 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
- 		i++;
- 	}
- 
-+	wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start);
- 	return pos;
- }
- 
-@@ -4274,6 +4416,7 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
- {
- #ifdef CONFIG_IEEE80211BE
- 	u8 link_id;
-+	u8 *start = pos;
- 
- 	if (sm->mld_assoc_link_id < 0)
- 		return pos;
-@@ -4324,6 +4467,7 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
- 		}
- 	}
- 
-+	wpa_printf(MSG_DEBUG, "RSN: MLO Link kde len = %ld", pos - start);
- 	pos = wpa_auth_ml_group_kdes(sm, pos);
- #endif /* CONFIG_IEEE80211BE */
- 
-@@ -5106,7 +5250,7 @@ SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED)
- #endif /* CONFIG_OCV */
- 
- 	if (sm->GUpdateStationKeys)
--		sm->group->GKeyDoneStations--;
-+		wpa_update_gkeydone(sm, -1);
- 	sm->GUpdateStationKeys = false;
- 	sm->GTimeoutCtr = 0;
- 	/* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */
-@@ -5121,7 +5265,7 @@ SM_STATE(WPA_PTK_GROUP, KEYERROR)
- {
- 	SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group);
- 	if (sm->GUpdateStationKeys)
--		sm->group->GKeyDoneStations--;
-+		wpa_update_gkeydone(sm, -1);
- 	sm->GUpdateStationKeys = false;
- 	sm->Disconnect = true;
- 	sm->disconnect_reason = WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT;
-@@ -5415,18 +5559,11 @@ int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos)
- 
- #endif /* CONFIG_WNM_AP */
- 
--
--static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
--			      struct wpa_group *group)
-+static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
-+				 struct wpa_group *group)
- {
- 	int tmp;
- 
--	wpa_printf(MSG_DEBUG,
--		   "WPA: group state machine entering state SETKEYS (VLAN-ID %d)",
--		   group->vlan_id);
--	group->changed = true;
--	group->wpa_group_state = WPA_GROUP_SETKEYS;
--	group->GTKReKey = false;
- 	tmp = group->GM;
- 	group->GM = group->GN;
- 	group->GN = tmp;
-@@ -5440,6 +5577,24 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
- 	 * counting the STAs that are marked with GUpdateStationKeys instead of
- 	 * including all STAs that could be in not-yet-completed state. */
- 	wpa_gtk_update(wpa_auth, group);
-+}
-+
-+static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
-+			      struct wpa_group *group)
-+{
-+	wpa_printf(MSG_DEBUG,
-+		   "WPA: group state machine entering state SETKEYS (VLAN-ID %d)",
-+		   group->vlan_id);
-+	group->changed = true;
-+	group->wpa_group_state = WPA_GROUP_SETKEYS;
-+	group->GTKReKey = false;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	if (wpa_auth->is_ml)
-+		goto skip_update;
-+#endif /* CONFIG_IEEE80211BE */
-+
-+	wpa_group_update_gtk(wpa_auth, group);
- 
- 	if (group->GKeyDoneStations) {
- 		wpa_printf(MSG_DEBUG,
-@@ -5447,6 +5602,10 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
- 			   group->GKeyDoneStations);
- 		group->GKeyDoneStations = 0;
- 	}
-+
-+#ifdef CONFIG_IEEE80211BE
-+skip_update:
-+#endif /* CONFIG_IEEE80211BE */
- 	wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group);
- 	wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d",
- 		   group->GKeyDoneStations);
-@@ -5564,6 +5723,57 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
- 	}
- }
- 
-+static void wpa_mark_group_change(struct wpa_state_machine *sm, bool change)
-+{
-+#ifdef CONFIG_IEEE80211BE
-+	int link_id;
-+#endif /* CONFIG_IEEE80211BE */
-+
-+	if (!sm || !sm->wpa_auth)
-+		return;
-+	sm->wpa_auth->group->changed = change;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	for_each_sm_auth(sm, link_id)
-+		sm->mld_links[link_id].wpa_auth->group->changed = change;
-+#endif /* CONFIG_IEEE80211BE */
-+}
-+
-+static void wpa_group_sm_step_links(struct wpa_state_machine *sm)
-+{
-+#ifdef CONFIG_IEEE80211BE
-+	int link_id;
-+#endif /* CONFIG_IEEE80211BE */
-+
-+	if (!sm || !sm->wpa_auth)
-+		return;
-+	wpa_group_sm_step(sm->wpa_auth, sm->wpa_auth->group);
-+
-+#ifdef CONFIG_IEEE80211BE
-+	for_each_sm_auth(sm, link_id)
-+		wpa_group_sm_step(sm->mld_links[link_id].wpa_auth,
-+				  sm->mld_links[link_id].wpa_auth->group);
-+#endif /* CONFIG_IEEE80211BE */
-+}
-+
-+static bool wpa_group_sm_changed(struct wpa_state_machine *sm)
-+{
-+#ifdef CONFIG_IEEE80211BE
-+	int link_id;
-+#endif /* CONFIG_IEEE80211BE */
-+	bool changed;
-+
-+	if (!sm || !sm->wpa_auth)
-+		return false;
-+	changed = sm->wpa_auth->group->changed;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	for_each_sm_auth(sm, link_id)
-+		changed |= sm->mld_links[link_id].wpa_auth->group->changed;
-+#endif /* CONFIG_IEEE80211BE */
-+
-+	return changed;
-+}
- 
- static int wpa_sm_step(struct wpa_state_machine *sm)
- {
-@@ -5584,7 +5794,7 @@ static int wpa_sm_step(struct wpa_state_machine *sm)
- 			break;
- 
- 		sm->changed = false;
--		sm->wpa_auth->group->changed = false;
-+		wpa_mark_group_change(sm, false);
- 
- 		SM_STEP_RUN(WPA_PTK);
- 		if (sm->pending_deinit)
-@@ -5592,8 +5802,8 @@ static int wpa_sm_step(struct wpa_state_machine *sm)
- 		SM_STEP_RUN(WPA_PTK_GROUP);
- 		if (sm->pending_deinit)
- 			break;
--		wpa_group_sm_step(sm->wpa_auth, sm->group);
--	} while (sm->changed || sm->wpa_auth->group->changed);
-+		wpa_group_sm_step_links(sm);
-+	} while (sm->changed || wpa_group_sm_changed(sm));
- 	sm->in_step_loop = 0;
- 
- 	if (sm->pending_deinit) {
-@@ -6807,8 +7017,10 @@ int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth)
- {
- 	if (!wpa_auth)
- 		return -1;
--	eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
--	return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL);
-+	eloop_cancel_timeout(wpa_rekey_gtk,
-+			     wpa_get_primary_wpa_auth(wpa_auth), NULL);
-+	return eloop_register_timeout(0, 0, wpa_rekey_gtk,
-+				      wpa_get_primary_wpa_auth(wpa_auth), NULL);
- }
- 
- 
-diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
-index 1446872f3..331d217b5 100644
---- a/src/ap/wpa_auth.h
-+++ b/src/ap/wpa_auth.h
-@@ -285,6 +285,12 @@ struct wpa_auth_config {
- 	 * Set only in nontransmitted BSSs, i.e., is NULL for transmitted BSS
- 	 * and in BSSs that are not part of a Multi-BSSID set. */
- 	struct wpa_authenticator *tx_bss_auth;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	u8 *mld_addr;
-+	int link_id;
-+	struct wpa_authenticator *first_link_auth;
-+#endif /* CONFIG_IEEE80211BE */
- };
- 
- typedef enum {
-@@ -429,7 +435,8 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
- 		    const u8 *wpa_ie, size_t wpa_ie_len,
- 		    const u8 *rsnxe, size_t rsnxe_len,
- 		    const u8 *mdie, size_t mdie_len,
--		    const u8 *owe_dh, size_t owe_dh_len);
-+		    const u8 *owe_dh, size_t owe_dh_len,
-+		    struct wpa_state_machine *assoc_sm);
- int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
- 		      struct wpa_state_machine *sm,
- 		      const u8 *osen_ie, size_t osen_ie_len);
-diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
-index d3cd44695..1726c7201 100644
---- a/src/ap/wpa_auth_glue.c
-+++ b/src/ap/wpa_auth_glue.c
-@@ -1713,6 +1713,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
- 
- 	hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf);
- 	_conf.msg_ctx = hapd->msg_ctx;
-+
- 	tx_bss = hostapd_mbssid_get_tx_bss(hapd);
- 	if (tx_bss != hapd)
- 		_conf.tx_bss_auth = tx_bss->wpa_auth;
-@@ -1753,6 +1754,27 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
- 		!!(hapd->iface->drv_flags2 &
- 		   WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP);
- 
-+#ifdef CONFIG_IEEE80211BE
-+	_conf.mld_addr = NULL;
-+	_conf.link_id = -1;
-+	_conf.first_link_auth = NULL;
-+
-+	if (hapd->conf->mld_ap) {
-+		struct hostapd_data *lhapd;
-+
-+		_conf.mld_addr = hapd->mld->mld_addr;
-+		_conf.link_id = hapd->mld_link_id;
-+
-+		for_each_mld_link(lhapd, hapd) {
-+			if (lhapd == hapd)
-+				continue;
-+
-+			if (lhapd->wpa_auth)
-+				_conf.first_link_auth = lhapd->wpa_auth;
-+		}
-+	}
-+#endif /* CONFIG_IEEE80211BE */
-+
- 	hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
- 	if (hapd->wpa_auth == NULL) {
- 		wpa_printf(MSG_ERROR, "WPA initialization failed.");
-diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
-index 9ba90749d..29bb66733 100644
---- a/src/ap/wpa_auth_i.h
-+++ b/src/ap/wpa_auth_i.h
-@@ -176,6 +176,7 @@ struct wpa_state_machine {
- 	u8 peer_mld_addr[ETH_ALEN];
- 	s8 mld_assoc_link_id;
- 	u8 n_mld_affiliated_links;
-+	u16 valid_links;
- 
- 	struct mld_link {
- 		bool valid;
-diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
-index a5f2861c9..bf2303e4f 100644
---- a/src/ap/wpa_auth_ie.c
-+++ b/src/ap/wpa_auth_ie.c
-@@ -608,7 +608,8 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
- 		    const u8 *wpa_ie, size_t wpa_ie_len,
- 		    const u8 *rsnxe, size_t rsnxe_len,
- 		    const u8 *mdie, size_t mdie_len,
--		    const u8 *owe_dh, size_t owe_dh_len)
-+		    const u8 *owe_dh, size_t owe_dh_len,
-+		    struct wpa_state_machine *assoc_sm)
- {
- 	struct wpa_auth_config *conf = &wpa_auth->conf;
- 	struct wpa_ie_data data;
-@@ -956,6 +957,15 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
- 	else
- 		sm->wpa = WPA_VERSION_WPA;
- 
-+	if (assoc_sm) {
-+		/* For ML Association Link STA cannot choose a different
-+		 * akm or pairwise cipher from assoc STA
-+		 */
-+		if (sm->wpa_key_mgmt != assoc_sm->wpa_key_mgmt)
-+			return WPA_INVALID_AKMP;
-+		if (sm->pairwise != assoc_sm->pairwise)
-+			return WPA_INVALID_PAIRWISE;
-+	}
- #if defined(CONFIG_IEEE80211R_AP) && defined(CONFIG_FILS)
- 	if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256 ||
- 	     sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) &&
-diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
-index 01efeea3a..24ceed600 100644
---- a/src/common/wpa_common.h
-+++ b/src/common/wpa_common.h
-@@ -24,6 +24,7 @@
- #define WPA_PASN_PMK_LEN 32
- #define WPA_PASN_MAX_MIC_LEN 24
- #define WPA_MAX_RSNXE_LEN 4
-+#define WPA_MLO_GTK_KDE_PN_LEN 6
- 
- #define OWE_DH_GROUP 19
- 
-diff --git a/tests/fuzzing/eapol-key-auth/eapol-key-auth.c b/tests/fuzzing/eapol-key-auth/eapol-key-auth.c
-index bb46422c6..17f69fd76 100644
---- a/tests/fuzzing/eapol-key-auth/eapol-key-auth.c
-+++ b/tests/fuzzing/eapol-key-auth/eapol-key-auth.c
-@@ -262,7 +262,7 @@ static int auth_init(struct wpa *wpa)
- 	}
- 
- 	if (wpa_validate_wpa_ie(wpa->auth_group, wpa->auth, 2412, supp_ie,
--				supp_ie_len, NULL, 0, NULL, 0, NULL, 0) !=
-+				supp_ie_len, NULL, 0, NULL, 0, NULL, 0, NULL) !=
- 	    WPA_IE_OK) {
- 		wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
- 		return -1;
-diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
-index 554268a47..2d06f1a6a 100644
---- a/wpa_supplicant/ibss_rsn.c
-+++ b/wpa_supplicant/ibss_rsn.c
-@@ -484,7 +484,7 @@ static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn,
- 				"\x00\x0f\xac\x04"
- 				"\x01\x00\x00\x0f\xac\x04"
- 				"\x01\x00\x00\x0f\xac\x02"
--				"\x00\x00", 22, NULL, 0, NULL, 0, NULL, 0) !=
-+				"\x00\x00", 22, NULL, 0, NULL, 0, NULL, 0, NULL) !=
- 	    WPA_IE_OK) {
- 		wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
- 		return -1;
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0017-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0017-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch
new file mode 100644
index 0000000..34dcd0f
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0017-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch
@@ -0,0 +1,454 @@
+From e45dc5872482e9db6cac8263a82832584095ecf3 Mon Sep 17 00:00:00 2001
+From: TomLiu <tomml.liu@mediatek.com>
+Date: Tue, 9 Aug 2022 10:23:44 -0700
+Subject: [PATCH 017/126] mtk: hostapd: Add hostapd MU SET/GET control
+
+---
+ hostapd/config_file.c             |   9 +++
+ hostapd/ctrl_iface.c              |  66 ++++++++++++++++++
+ hostapd/hostapd_cli.c             |  18 +++++
+ src/ap/ap_config.c                |   1 +
+ src/ap/ap_config.h                |   1 +
+ src/ap/ap_drv_ops.c               |  14 ++++
+ src/ap/ap_drv_ops.h               |   2 +
+ src/ap/hostapd.c                  |   2 +
+ src/common/mtk_vendor.h           |  15 ++++
+ src/drivers/driver.h              |  13 ++++
+ src/drivers/driver_nl80211.c      | 110 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   3 +
+ 13 files changed, 255 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 9f5ee48bf..22da72c07 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4251,6 +4251,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 			return 1;
+ 		}
+ 		conf->mbssid = mbssid;
++	} else if (os_strcmp(buf, "mu_onoff") == 0) {
++		int val = atoi(pos);
++		if (val < 0 || val > 15) {
++			wpa_printf(MSG_ERROR,
++				   "Line %d: invalid mu_onoff value",
++				   line);
++			return 1;
++		}
++		conf->mu_onoff = val;
+ #endif /* CONFIG_IEEE80211AX */
+ 	} else if (os_strcmp(buf, "max_listen_interval") == 0) {
+ 		bss->max_listen_interval = atoi(pos);
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 8a9fa3ec9..d168c2fb5 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4179,6 +4179,67 @@ fail:
+ #endif /* CONFIG_NAN_USD */
+ 
+ 
++static int
++hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
++					 char *buf, size_t buflen)
++{
++	char *pos, *config, *value;
++	config = cmd;
++	pos = os_strchr(config, ' ');
++	if (pos == NULL)
++		return -1;
++	*pos++ = '\0';
++
++	if(pos == NULL)
++		return -1;
++	value = pos;
++
++	if (os_strcmp(config, "onoff") == 0) {
++		int mu = atoi(value);
++		if (mu < 0 || mu > 15) {
++			wpa_printf(MSG_ERROR, "Invalid value for mu");
++			return -1;
++		}
++		hapd->iconf->mu_onoff = (u8) mu;
++	} else {
++		wpa_printf(MSG_ERROR,
++			"Unsupported parameter %s for SET_MU", config);
++		return -1;
++	}
++
++	if(hostapd_drv_mu_ctrl(hapd) == 0) {
++		return os_snprintf(buf, buflen, "OK\n");
++	} else {
++		return -1;
++	}
++}
++
++
++static int
++hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
++					 size_t buflen)
++{
++	u8 mu_onoff;
++	char *pos, *end;
++
++	pos = buf;
++	end = buf + buflen;
++
++	if (hapd->iface->state != HAPD_IFACE_ENABLED)
++		return os_snprintf(pos, end - pos, "Not allowed to get_mu when current state is %s\n",
++				   hostapd_state_text(hapd->iface->state));
++
++	if (hostapd_drv_mu_dump(hapd, &mu_onoff) == 0) {
++		hapd->iconf->mu_onoff = mu_onoff;
++		return os_snprintf(pos, end - pos, "[hostapd_cli] = UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
++			!!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
++	} else {
++		wpa_printf(MSG_INFO, "ctrl iface failed to call");
++		return -1;
++	}
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -4793,6 +4854,11 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 	} else if (os_strncmp(buf, "GET_EDCCA ", 10) == 0) {
+ 		reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
+ 							  reply_size);
++	} else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
++		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply,
++							  reply_size);
++	} else if (os_strncmp(buf, "GET_MU", 6) == 0) {
++		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index bb15bc751..f4dc1517e 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1460,6 +1460,20 @@ static int hostapd_cli_cmd_driver_flags2(struct wpa_ctrl *ctrl, int argc,
+ }
+ 
+ 
++static int hostapd_cli_cmd_set_mu(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "SET_MU", 1, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
++}
++
++
+ #ifdef CONFIG_DPP
+ 
+ static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
+@@ -1831,6 +1845,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 	  " = show supported driver flags"},
+ 	{ "driver_flags2", hostapd_cli_cmd_driver_flags2, NULL,
+ 	  " = show supported driver flags2"},
++	{ "set_mu", hostapd_cli_cmd_set_mu, NULL,
++		"<value> [0-15] bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0)"},
++	{ "get_mu", hostapd_cli_cmd_get_mu, NULL,
++		" = show mu onoff value in 0-15 bitmap"},
+ #ifdef CONFIG_DPP
+ 	{ "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 112954a89..45c391a65 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -291,6 +291,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 	conf->reg_def_cli_eirp_psd = -1;
+ 	conf->reg_sub_cli_eirp_psd = -1;
+ 	conf->reg_def_cli_eirp = -1;
++	conf->mu_onoff = 15;
+ #endif /* CONFIG_IEEE80211AX */
+ 
+ 	/* The third octet of the country string uses an ASCII space character
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index b9afe1f47..baf3b40f8 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1235,6 +1235,7 @@ struct hostapd_config {
+ 	int reg_def_cli_eirp;
+ 
+ 	bool require_he;
++	u8 mu_onoff;
+ #endif /* CONFIG_IEEE80211AX */
+ 
+ 	/* VHT enable/disable config from CHAN_SWITCH */
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index c7cacbd81..56d923462 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1298,3 +1298,17 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
+ 		return 0;
+ 	return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
+ }
++
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->driver->mu_ctrl)
++		return 0;
++	return hapd->driver->mu_ctrl(hapd->drv_priv, hapd->iconf->mu_onoff);
++}
++
++int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
++{
++	if (!hapd->driver || !hapd->driver->mu_dump)
++		return 0;
++	return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 23a331914..47b23513f 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -156,6 +156,8 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
+ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
+ 					  const int *threshold);
+ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 972bb1763..227474db5 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2762,6 +2762,8 @@ dfs_offload:
+ 	if (hostapd_drv_configure_edcca_threshold(hapd,
+ 						  hapd->iconf->edcca_threshold) < 0)
+ 		goto fail;
++	if (hostapd_drv_mu_ctrl(hapd) < 0)
++		goto fail;
+ 
+ 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ 		   iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 6121857dd..60bc4cd4c 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -10,6 +10,8 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
+ 	MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
+ 	MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
++	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
++	MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
+ 	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
+ };
+ 
+@@ -177,6 +179,19 @@ enum mtk_vendor_attr_rfeature_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_mu_ctrl {
++	MTK_VENDOR_ATTR_MU_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
++	MTK_VENDOR_ATTR_MU_CTRL_DUMP,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
++	MTK_VENDOR_ATTR_MU_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
++};
++
++
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+ 
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index bf103516b..2217809db 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -176,6 +176,11 @@ struct hostapd_channel_data {
+ 	 * punct_bitmap - RU puncturing bitmap
+ 	 */
+ 	u16 punct_bitmap;
++
++	/**
++	 * mu onoff=<val> (bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0))
++	 */
++	u8 mu_onoff;
+ };
+ 
+ #define HE_MAC_CAPAB_0		0
+@@ -5250,6 +5255,14 @@ struct wpa_driver_ops {
+ 				  const s8 edcca_compensation);
+ 	int (*configure_edcca_threshold)(void *priv, const int *threshold);
+ 	int (*get_edcca)(void *priv, const u8 mode, u8 *value);
++
++	/**
++	 * mu_ctrl - ctrl on off for UL/DL MURU
++	 * @priv: Private driver interface data
++	 *
++	 */
++	 int (*mu_ctrl)(void *priv, u8 mu_onoff);
++	 int (*mu_dump)(void *priv, u8 *mu_onoff);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 606632aad..2e011e3df 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -13939,6 +13939,114 @@ fail:
+ }
+ 
+ 
++#ifdef CONFIG_IEEE80211AX
++static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_mu_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting mu control");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
++		!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++		nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, mu_onoff)) {
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if(ret){
++		wpa_printf(MSG_ERROR, "Failed to set mu_onoff. ret=%d (%s)", ret, strerror(-ret));
++	}
++	return ret;
++}
++
++
++static int mu_dump_handler(struct nl_msg *msg, void *arg)
++{
++	u8 *mu_onoff = (u8 *) arg;
++	struct nlattr *tb[NL80211_ATTR_MAX + 1];
++	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_MU_CTRL_MAX + 1];
++	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++	struct nlattr *nl_vend, *attr;
++
++	static const struct nla_policy
++	mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL + 1] = {
++		[MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
++		[MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
++	};
++
++	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++			genlmsg_attrlen(gnlh, 0), NULL);
++
++	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++	if (!nl_vend)
++		return NL_SKIP;
++
++	nla_parse(tb_vendor, MTK_VENDOR_ATTR_MU_CTRL_MAX,
++		  nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++	attr = tb_vendor[MTK_VENDOR_ATTR_MU_CTRL_DUMP];
++	if (!attr) {
++		wpa_printf(MSG_ERROR, "nl80211: cannot find MTK_VENDOR_ATTR_MU_CTRL_DUMP");
++		return NL_SKIP;
++	}
++
++	*mu_onoff = nla_get_u8(attr);
++	wpa_printf(MSG_DEBUG, "nla_get mu_onoff: %d\n", *mu_onoff);
++
++	return 0;
++}
++
++static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *attr;
++	int ret;
++
++	if (!drv->mtk_mu_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting mu control");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL)) {
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++
++  attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!attr) {
++		nlmsg_free(msg);
++		return -1;
++	}
++
++	nla_nest_end(msg, attr);
++
++	ret = send_and_recv_resp(drv, msg, mu_dump_handler, mu_onoff);
++
++	if(ret){
++		wpa_printf(MSG_ERROR, "Failed to get mu_onoff. ret=%d (%s)", ret, strerror(-ret));
++	}
++
++	return ret;
++}
++#endif /* CONFIG_IEEE80211AX */
++
++
+ #ifdef CONFIG_DPP
+ static int nl80211_dpp_listen(void *priv, bool enable)
+ {
+@@ -14418,6 +14526,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.update_connect_params = nl80211_update_connection_params,
+ 	.send_external_auth_status = nl80211_send_external_auth_status,
+ 	.set_4addr_mode = nl80211_set_4addr_mode,
++	.mu_ctrl = nl80211_mu_onoff,
++	.mu_dump = nl80211_mu_dump,
+ #ifdef CONFIG_DPP
+ 	.dpp_listen = nl80211_dpp_listen,
+ #endif /* CONFIG_DPP */
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 62c47efbd..f99bba9e1 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -201,6 +201,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int puncturing:1;
+ 	unsigned int qca_ap_allowed_freqs:1;
+ 	unsigned int mtk_edcca_vendor_cmd_avail:1;
++	unsigned int mtk_mu_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 465fd318d..0eda91d0f 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1144,6 +1144,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL:
+ 					drv->mtk_edcca_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_MU_CTRL :
++					drv->mtk_mu_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0018-hostapd-MLO-send-link-id-during-flushing-stations.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0018-hostapd-MLO-send-link-id-during-flushing-stations.patch
deleted file mode 100644
index ce4a844..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0018-hostapd-MLO-send-link-id-during-flushing-stations.patch
+++ /dev/null
@@ -1,156 +0,0 @@
-From 8ac142806112477fa012414a2bdea22239e474a4 Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:48 +0530
-Subject: [PATCH 018/104] hostapd: MLO: send link id during flushing stations
-
-Currently, whenever a BSS is set up, it sends flush all stations via
-command - NL80211_CMD_DEL_STATION on its interface. However, in case
-of MLO, station could have been connected to other links by the time
-this link is coming up. Since there is no link id currently being
-passed, all those stations entries are also removed in the driver which is
-wrong.
-
-Hence add change to send link id along with the command during MLO so that
-the driver can use this link id and flush only those stations which are
-using the passed link id as one of its links.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/ap_drv_ops.c          | 10 +++++++++-
- src/drivers/driver.h         |  4 +++-
- src/drivers/driver_atheros.c |  2 +-
- src/drivers/driver_bsd.c     |  2 +-
- src/drivers/driver_hostap.c  |  2 +-
- src/drivers/driver_nl80211.c | 17 ++++++++++++++---
- 6 files changed, 29 insertions(+), 8 deletions(-)
-
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 0d493b837..32722084d 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -624,9 +624,17 @@ int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
- 
- int hostapd_flush(struct hostapd_data *hapd)
- {
-+	int link_id = -1;
-+
- 	if (hapd->driver == NULL || hapd->driver->flush == NULL)
- 		return 0;
--	return hapd->driver->flush(hapd->drv_priv);
-+
-+#ifdef CONFIG_IEEE80211BE
-+	if (hapd->conf && hapd->conf->mld_ap)
-+		link_id = hapd->mld_link_id;
-+#endif /* CONFIG_IEEE80211BE */
-+
-+	return hapd->driver->flush(hapd->drv_priv, link_id);
- }
- 
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index a7455ef6e..e672a1787 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -3578,13 +3578,15 @@ struct wpa_driver_ops {
- 	/**
- 	 * flush - Flush all association stations (AP only)
- 	 * @priv: Private driver interface data
-+	 * @link_id: In case of MLO, valid link_id on which all associated stations
-+	 *	     will be flushed. -1 otherwise.
- 	 * Returns: 0 on success, -1 on failure
- 	 *
- 	 * This function requests the driver to disassociate all associated
- 	 * stations. This function does not need to be implemented if the
- 	 * driver does not process association frames internally.
- 	 */
--	int (*flush)(void *priv);
-+	int (*flush)(void *priv, int link_id);
- 
- 	/**
- 	 * set_generic_elem - Add IEs into Beacon/Probe Response frames (AP)
-diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
-index ae7f0e535..71863306a 100644
---- a/src/drivers/driver_atheros.c
-+++ b/src/drivers/driver_atheros.c
-@@ -632,7 +632,7 @@ atheros_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
- 
- 
- static int
--atheros_flush(void *priv)
-+atheros_flush(void *priv, int link_id)
- {
- 	u8 allsta[IEEE80211_ADDR_LEN];
- 	os_memset(allsta, 0xff, IEEE80211_ADDR_LEN);
-diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
-index 850637f0d..82d8a0186 100644
---- a/src/drivers/driver_bsd.c
-+++ b/src/drivers/driver_bsd.c
-@@ -946,7 +946,7 @@ bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
- 
- 
- static int
--bsd_flush(void *priv)
-+bsd_flush(void *priv, int link_id)
- {
- 	u8 allsta[IEEE80211_ADDR_LEN];
- 
-diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
-index d3520aacc..3aa5860bc 100644
---- a/src/drivers/driver_hostap.c
-+++ b/src/drivers/driver_hostap.c
-@@ -572,7 +572,7 @@ static int hostap_set_ssid(void *priv, const u8 *buf, int len)
- }
- 
- 
--static int hostap_flush(void *priv)
-+static int hostap_flush(void *priv, int link_id)
- {
- 	struct hostap_driver_data *drv = priv;
- 	struct prism2_hostapd_param param;
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index e5fa22b59..9ac621ae6 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -7729,25 +7729,36 @@ static int i802_set_frag(void *priv, int frag)
- }
- 
- 
--static int i802_flush(void *priv)
-+static int i802_flush(void *priv, int link_id)
- {
- 	struct i802_bss *bss = priv;
- 	struct nl_msg *msg;
- 	int res;
- 
--	wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
--		   bss->ifname);
-+	if (link_id == NL80211_DRV_LINK_ID_NA)
-+		wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
-+			   bss->ifname);
-+	else
-+		wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (with link %d)",
-+			   bss->ifname, link_id);
- 
- 	/*
- 	 * XXX: FIX! this needs to flush all VLANs too
- 	 */
- 	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION);
-+	if (link_id >= 0 && (bss->valid_links & BIT(link_id)) &&
-+	    nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
-+		goto fail;
-+
- 	res = send_and_recv_cmd(bss->drv, msg);
- 	if (res) {
- 		wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d "
- 			   "(%s)", res, strerror(-res));
- 	}
- 	return res;
-+fail:
-+	nlmsg_free(msg);
-+	return -1;
- }
- 
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0018-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0018-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch
new file mode 100644
index 0000000..5033c63
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0018-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch
@@ -0,0 +1,247 @@
+From d6a05d4839676a55e440ea03420000dd2499b4c9 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 2 Sep 2022 01:03:23 +0800
+Subject: [PATCH 018/126] mtk: hostapd: Add three wire PTA ctrl hostapd vendor
+ command
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/config_file.c             |  4 ++++
+ src/ap/ap_config.c                |  1 +
+ src/ap/ap_config.h                | 13 ++++++++++++
+ src/ap/ap_drv_ops.c               | 11 +++++++++++
+ src/ap/ap_drv_ops.h               |  1 +
+ src/ap/hostapd.c                  |  2 ++
+ src/common/mtk_vendor.h           | 16 +++++++++++++++
+ src/drivers/driver.h              |  8 ++++++++
+ src/drivers/driver_nl80211.c      | 33 +++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |  1 +
+ src/drivers/driver_nl80211_capa.c |  3 +++
+ 11 files changed, 93 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 22da72c07..ebbd56734 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5513,6 +5513,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 			return 1;
+ 		}
+ 		conf->edcca_compensation = (s8) val;
++	} else if (os_strcmp(buf, "three_wire_enable") == 0) {
++		u8 en = atoi(pos);
++
++		conf->three_wire_enable = en;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 45c391a65..cd520ebdc 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -308,6 +308,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 
+ 	conf->edcca_enable = EDCCA_MODE_AUTO;
+ 	conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
++	conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
+ 
+ 	hostapd_set_and_check_bw320_offset(conf, 0);
+ 
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index baf3b40f8..3ab6cae2c 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1339,6 +1339,19 @@ struct hostapd_config {
+ 	u8 edcca_enable;
+ 	s8 edcca_compensation;
+ 	int *edcca_threshold;
++	u8 three_wire_enable;
++};
++
++enum three_wire_mode {
++	THREE_WIRE_MODE_DISABLE,
++	THREE_WIRE_MODE_EXT0_ENABLE,
++	THREE_WIRE_MODE_EXT1_ENABLE,
++	THREE_WIRE_MODE_ALL_ENABLE,
++
++	/* keep last */
++	NUM_THREE_WIRE_MODE,
++	THREE_WIRE_MODE_MAX =
++		NUM_THREE_WIRE_MODE - 1
+ };
+ 
+ enum edcca_mode {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 56d923462..fa6aa7085 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1312,3 +1312,14 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
+ 		return 0;
+ 	return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
+ }
++
++int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->driver->three_wire_ctrl)
++		return 0;
++	if (hapd->iconf->three_wire_enable > THREE_WIRE_MODE_MAX) {
++		wpa_printf(MSG_INFO, "Invalid value for three wire enable\n");
++		return 0;
++	}
++	return hapd->driver->three_wire_ctrl(hapd->drv_priv, hapd->iconf->three_wire_enable);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 47b23513f..008f56e76 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -158,6 +158,7 @@ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
+ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+ int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
++int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 227474db5..56a024bbf 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2764,6 +2764,8 @@ dfs_offload:
+ 		goto fail;
+ 	if (hostapd_drv_mu_ctrl(hapd) < 0)
+ 		goto fail;
++	if (hostapd_drv_three_wire_ctrl(hapd) < 0)
++		goto fail;
+ 
+ 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ 		   iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 60bc4cd4c..99ecbaf71 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -13,6 +13,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
+ 	MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
+ 	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
++	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8
+ };
+ 
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -58,6 +59,21 @@ static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
+ 	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
+ };
+ 
++enum mtk_vendor_attr_3wire_ctrl {
++	MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_3WIRE_CTRL_MODE,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL,
++	MTK_VENDOR_ATTR_3WIRE_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
++};
++
++static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
++	[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
++};
++
+ enum mtk_vendor_attr_csi_ctrl {
+ 	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
+ 
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 2217809db..13e91d21d 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5263,6 +5263,14 @@ struct wpa_driver_ops {
+ 	 */
+ 	 int (*mu_ctrl)(void *priv, u8 mu_onoff);
+ 	 int (*mu_dump)(void *priv, u8 *mu_onoff);
++
++	/**
++	 * three_wire_ctrl - set three_wire_ctrl mode
++	 * @priv: Private driver interface data
++	 * @three_wire_enable: three_wire_ctrl mode
++	 *
++	 */
++	 int (*three_wire_ctrl)(void *priv, u8 three_wire_enable);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 2e011e3df..f94fa19b0 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14379,6 +14379,38 @@ static int nl80211_get_edcca(void *priv, const u8 mode, u8 *value)
+ 	return ret;
+ }
+ 
++static int nl80211_enable_three_wire(void *priv, const u8 three_wire_enable)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	/* Prepare nl80211 cmd */
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_3wire_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting three wire control");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL) ||
++	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_3WIRE_CTRL_MODE, three_wire_enable)) {
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to enable three wire. ret=%d (%s) ",
++			   ret, strerror(-ret));
++	}
++	return ret;
++}
+ 
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+@@ -14546,4 +14578,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.configure_edcca_enable = nl80211_configure_edcca_enable,
+ 	.configure_edcca_threshold = nl80211_configure_edcca_threshold,
+ 	.get_edcca = nl80211_get_edcca,
++	.three_wire_ctrl = nl80211_enable_three_wire,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index f99bba9e1..5de6ca6f0 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -202,6 +202,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int qca_ap_allowed_freqs:1;
+ 	unsigned int mtk_edcca_vendor_cmd_avail:1;
+ 	unsigned int mtk_mu_vendor_cmd_avail:1;
++	unsigned int mtk_3wire_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 0eda91d0f..433eecf56 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1147,6 +1147,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_MU_CTRL :
+ 					drv->mtk_mu_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL :
++					drv->mtk_3wire_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0019-hostapd-MLO-display-link-details-in-status-command.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0019-hostapd-MLO-display-link-details-in-status-command.patch
deleted file mode 100644
index 77f3ddd..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0019-hostapd-MLO-display-link-details-in-status-command.patch
+++ /dev/null
@@ -1,84 +0,0 @@
-From 3d12a39b10565a10bec40b53cf6e69b60115a35f Mon Sep 17 00:00:00 2001
-From: Harshitha Prem <quic_hprem@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:49 +0530
-Subject: [PATCH 019/104] hostapd: MLO: display link details in status command
-
-Currently, link id and number of link details of a MLD AP interface is not
-displayed in status command of hostapd_cli.
-
-Add changes to display the link id and number of link details.
-
-The details would be seen as below for a MLD AP interface:
-
-$ hostapd_cli -i wlan0 status | grep link
-num_links=1
-link_id=0
-link_addr=AA:BB:CC:DD:EE:FF
-
-$ hostapd_cli -i wlan1 status | grep link
-num_links=2
-link_id=0
-link_addr=AA:BB:CC:DD:EE:FF
-partner_link_id=1
-partner_link_addr=AA:BB:CC:DD:EE:AA
-
-The above details would not be displayed for non-MLD AP interfaces.
-
-Signed-off-by: Harshitha Prem <quic_hprem@quicinc.com>
-Co-developed-by: Manish Dharanenthiran <quic_mdharane@quicinc.com>
-Signed-off-by: Manish Dharanenthiran <quic_mdharane@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/ctrl_iface_ap.c | 36 ++++++++++++++++++++++++++++++++++++
- 1 file changed, 36 insertions(+)
-
-diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index 272317774..2cfef4bd4 100644
---- a/src/ap/ctrl_iface_ap.c
-+++ b/src/ap/ctrl_iface_ap.c
-@@ -887,6 +887,42 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
- 				return len;
- 			len += ret;
- 		}
-+
-+		if (hapd->conf->mld_ap) {
-+			struct hostapd_data *link_bss;
-+
-+			ret = os_snprintf(buf + len, buflen - len,
-+					  "num_links=%d\n",
-+					  hapd->mld->num_links);
-+			if (os_snprintf_error(buflen - len, ret))
-+				return len;
-+			len += ret;
-+
-+			/* self bss */
-+			ret = os_snprintf(buf + len, buflen - len,
-+					  "link_id=%d\n"
-+					  "link_addr=" MACSTR "\n",
-+					  hapd->mld_link_id,
-+					  MAC2STR(hapd->own_addr));
-+			if (os_snprintf_error(buflen - len, ret))
-+				return len;
-+			len += ret;
-+
-+			/* partner bss */
-+			for_each_mld_link(link_bss, hapd) {
-+				if (link_bss == hapd)
-+					continue;
-+
-+				ret = os_snprintf(buf + len, buflen - len,
-+						  "partner_link_id=%d\n"
-+						  "partner_link_addr=" MACSTR "\n",
-+						  link_bss->mld_link_id,
-+						  MAC2STR(link_bss->own_addr));
-+				if (os_snprintf_error(buflen - len, ret))
-+					return len;
-+				len += ret;
-+			}
-+		}
- 	}
- #endif /* CONFIG_IEEE80211BE */
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0019-mtk-hostapd-Add-hostapd-iBF-control.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0019-mtk-hostapd-Add-hostapd-iBF-control.patch
new file mode 100644
index 0000000..161d154
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0019-mtk-hostapd-Add-hostapd-iBF-control.patch
@@ -0,0 +1,431 @@
+From 63d5ec844ead699159dd047efc91717861efba41 Mon Sep 17 00:00:00 2001
+From: mtk27835 <shurong.wen@mediatek.com>
+Date: Wed, 7 Sep 2022 14:41:51 -0700
+Subject: [PATCH 019/126] mtk: hostapd: Add hostapd iBF control
+
+Signed-off-by: mtk27835 <shurong.wen@mediatek.com>
+---
+ hostapd/config_file.c             |   3 +
+ hostapd/ctrl_iface.c              |  26 +++++++
+ hostapd/hostapd_cli.c             |   9 +++
+ src/ap/ap_config.c                |   1 +
+ src/ap/ap_config.h                |   2 +
+ src/ap/ap_drv_ops.c               |  14 ++++
+ src/ap/ap_drv_ops.h               |   2 +
+ src/ap/hostapd.c                  |   2 +
+ src/common/mtk_vendor.h           |  35 +++++++++-
+ src/drivers/driver.h              |  19 ++++++
+ src/drivers/driver_nl80211.c      | 108 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   3 +
+ 13 files changed, 224 insertions(+), 1 deletion(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index ebbd56734..e437aa981 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5517,6 +5517,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		u8 en = atoi(pos);
+ 
+ 		conf->three_wire_enable = en;
++	} else if (os_strcmp(buf, "ibf_enable") == 0) { /*ibf setting is per device*/
++		int val = atoi(pos);
++		conf->ibf_enable = !!val;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index d168c2fb5..3221c0fb1 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4240,6 +4240,30 @@ hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
+ }
+ 
+ 
++static int
++hostapd_ctrl_iface_get_ibf(struct hostapd_data *hapd, char *buf,
++					 size_t buflen)
++{
++	u8 ibf_enable;
++	int ret;
++	char *pos, *end;
++
++	pos = buf;
++	end = buf + buflen;
++
++	if (hostapd_drv_ibf_dump(hapd, &ibf_enable) == 0) {
++		hapd->iconf->ibf_enable = ibf_enable;
++		ret = os_snprintf(pos, end - pos, "ibf_enable: %u\n",
++			  ibf_enable);
++	}
++
++	if (os_snprintf_error(end - pos, ret))
++		return 0;
++
++	return ret;
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -4859,6 +4883,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 							  reply_size);
+ 	} else if (os_strncmp(buf, "GET_MU", 6) == 0) {
+ 		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
++	} else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
++		reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index f4dc1517e..ccbf863f6 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1691,6 +1691,13 @@ static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ #endif /* ANDROID */
+ 
+ 
++static int hostapd_cli_cmd_get_ibf(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "GET_IBF", 0, NULL, NULL);
++}
++
++
+ struct hostapd_cli_cmd {
+ 	const char *cmd;
+ 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -1921,6 +1928,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ #endif /* ANDROID */
+ 	{ "inband_discovery", hostapd_cli_cmd_inband_discovery, NULL,
+           "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
++	{ "get_ibf", hostapd_cli_cmd_get_ibf, NULL,
++	  " = show iBF state (enabled/disabled)"},
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index cd520ebdc..08b55d0eb 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -309,6 +309,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 	conf->edcca_enable = EDCCA_MODE_AUTO;
+ 	conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
+ 	conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
++	conf->ibf_enable = IBF_DEFAULT_ENABLE;
+ 
+ 	hostapd_set_and_check_bw320_offset(conf, 0);
+ 
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 3ab6cae2c..3f07147db 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1340,6 +1340,7 @@ struct hostapd_config {
+ 	s8 edcca_compensation;
+ 	int *edcca_threshold;
+ 	u8 three_wire_enable;
++	u8 ibf_enable;
+ };
+ 
+ enum three_wire_mode {
+@@ -1503,6 +1504,7 @@ hostapd_set_and_check_bw320_offset(struct hostapd_config *conf,
+ #endif /* CONFIG_IEEE80211BE */
+ }
+ 
++#define IBF_DEFAULT_ENABLE 0
+ 
+ int hostapd_mac_comp(const void *a, const void *b);
+ struct hostapd_config * hostapd_config_defaults(void);
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index fa6aa7085..5172f06b9 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1323,3 +1323,17 @@ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
+ 	}
+ 	return hapd->driver->three_wire_ctrl(hapd->drv_priv, hapd->iconf->three_wire_enable);
+ }
++
++int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->driver->ibf_ctrl)
++		return 0;
++	return hapd->driver->ibf_ctrl(hapd->drv_priv, hapd->iconf->ibf_enable);
++}
++
++int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable)
++{
++	if (!hapd->driver || !hapd->driver->ibf_dump)
++		return 0;
++	return hapd->driver->ibf_dump(hapd->drv_priv, ibf_enable);
++}
+\ No newline at end of file
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 008f56e76..3633e2ed5 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -159,6 +159,8 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+ int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 56a024bbf..7503718c8 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2766,6 +2766,8 @@ dfs_offload:
+ 		goto fail;
+ 	if (hostapd_drv_three_wire_ctrl(hapd) < 0)
+ 		goto fail;
++	if (hostapd_drv_ibf_ctrl(hapd) < 0)
++		goto fail;
+ 
+ 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ 		   iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 99ecbaf71..9811f266e 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -13,7 +13,8 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
+ 	MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
+ 	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
+-	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8
++	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
++	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
+ };
+ 
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -207,6 +208,38 @@ enum mtk_vendor_attr_mu_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_ibf_ctrl {
++	MTK_VENDOR_ATTR_IBF_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_IBF_CTRL_ENABLE,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_IBF_CTRL,
++	MTK_VENDOR_ATTR_IBF_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_IBF_CTRL - 1
++};
++
++enum mtk_vendor_attr_ibf_dump {
++	MTK_VENDOR_ATTR_IBF_DUMP_UNSPEC,
++
++	MTK_VENDOR_ATTR_IBF_DUMP_ENABLE,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_IBF_DUMP,
++	MTK_VENDOR_ATTR_IBF_DUMP_MAX =
++		NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
++};
++
++static struct nla_policy
++ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
++	[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
++};
++
++static struct nla_policy
++ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
++	[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
++};
++
+ 
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 13e91d21d..97be9e8fb 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -181,6 +181,11 @@ struct hostapd_channel_data {
+ 	 * mu onoff=<val> (bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0))
+ 	 */
+ 	u8 mu_onoff;
++
++	/**
++	 * ibf_enable=<val>
++	 */
++	u8 ibf_enable;
+ };
+ 
+ #define HE_MAC_CAPAB_0		0
+@@ -5271,6 +5276,20 @@ struct wpa_driver_ops {
+ 	 *
+ 	 */
+ 	 int (*three_wire_ctrl)(void *priv, u8 three_wire_enable);
++
++	/**
++	 * ibf_ctrl - ctrl disable/enable for ibf
++	 * @priv: Private driver interface data
++	 *
++	 */
++	int (*ibf_ctrl)(void *priv, u8 ibf_enable);
++
++	/**
++	 * ibf_dump - dump ibf
++	 * @priv: Private driver interface data
++	 *
++	 */
++	int (*ibf_dump)(void *priv, u8 *ibf_enable);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index f94fa19b0..c5c0d1b49 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14412,6 +14412,112 @@ static int nl80211_enable_three_wire(void *priv, const u8 three_wire_enable)
+ 	return ret;
+ }
+ 
++static int nl80211_ibf_enable(void *priv, u8 ibf_enable)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_ibf_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting ibf control");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_IBF_CTRL_ENABLE, ibf_enable);
++
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to set ibf_enable. ret=%d (%s)", ret, strerror(-ret));
++	}
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
++static int ibf_dump_handler(struct nl_msg *msg, void *arg)
++{
++	u8 *ibf_enable = (u8 *) arg;
++	struct nlattr *tb[NL80211_ATTR_MAX + 1];
++	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_IBF_DUMP_MAX + 1];
++	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++	struct nlattr *nl_vend, *attr;
++
++	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++			genlmsg_attrlen(gnlh, 0), NULL);
++
++	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++	if (!nl_vend)
++		return NL_SKIP;
++
++	nla_parse(tb_vendor, MTK_VENDOR_ATTR_IBF_DUMP_MAX,
++			nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++	attr = tb_vendor[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE];
++	if (!attr) {
++		wpa_printf(MSG_ERROR, "nl80211: cannot find MTK_VENDOR_ATTR_IBF_DUMP_ENABLE");
++		return NL_SKIP;
++	}
++
++	*ibf_enable = nla_get_u8(attr);
++
++	return NL_SKIP;
++}
++
++static int
++nl80211_ibf_dump(void *priv, u8 *ibf_enable)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++	if (!data)
++		goto fail;
++
++	nla_nest_end(msg, data);
++
++	ret = send_and_recv_resp(drv, msg, ibf_dump_handler, ibf_enable);
++
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to dump ibf_enable. ret=%d (%s)", ret, strerror(-ret));
++	}
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+ 	.desc = "Linux nl80211/cfg80211",
+@@ -14579,4 +14685,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.configure_edcca_threshold = nl80211_configure_edcca_threshold,
+ 	.get_edcca = nl80211_get_edcca,
+ 	.three_wire_ctrl = nl80211_enable_three_wire,
++	.ibf_ctrl = nl80211_ibf_enable,
++	.ibf_dump = nl80211_ibf_dump,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 5de6ca6f0..1432eeda8 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -203,6 +203,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_edcca_vendor_cmd_avail:1;
+ 	unsigned int mtk_mu_vendor_cmd_avail:1;
+ 	unsigned int mtk_3wire_vendor_cmd_avail:1;
++	unsigned int mtk_ibf_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 433eecf56..670588dd2 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1150,6 +1150,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL :
+ 					drv->mtk_3wire_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL:
++					drv->mtk_ibf_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0020-hostapd-fix-RNR-building-for-co-location-and-MLO.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0020-hostapd-fix-RNR-building-for-co-location-and-MLO.patch
deleted file mode 100644
index 879adf1..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0020-hostapd-fix-RNR-building-for-co-location-and-MLO.patch
+++ /dev/null
@@ -1,748 +0,0 @@
-From 8affcd80f5143fa23d3f21427b6b9f11af35ef5d Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:50 +0530
-Subject: [PATCH 020/104] hostapd: fix RNR building for co-location and MLO
-
-Currently with MLO changes, RNR formation for co-location or MLO
-was not working as expected. Hence make it work as per the
-expectation.
-
-For example, during co-location, if the BSS is also its ML partner
-then there is no need to include a separate TBTT for it.
-
-Also, during co-location, if the BSS is not its partner but it is
-ML capable, then the TBTT length should be 16 bytes and it should
-include the MLD Parameters for it in the RNR.
-
-During co-location, for a given Neighbor AP (operating on a given
-channel and op-class) if it has BSSes which are ML capable as well
-as BSSes which are not, then there should be two Neighbor AP Info
-present. One indicating TBTT length as 13 bytes and one indicating
-TBTT info length as 16 bytes.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/beacon.c     |  12 +-
- src/ap/ieee802_11.c | 387 ++++++++++++++++++++++++++++++++------------
- src/ap/ieee802_11.h |   5 +-
- 3 files changed, 290 insertions(+), 114 deletions(-)
-
-diff --git a/src/ap/beacon.c b/src/ap/beacon.c
-index b780d98e4..4354dfae3 100644
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -677,7 +677,7 @@ static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd,
- 					 params->known_bss,
- 					 params->known_bss_len, NULL);
- 	if (!params->is_ml_sta_info)
--		buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
-+		buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP, true);
- 	buflen += hostapd_mbo_ie_len(hapd);
- 	buflen += hostapd_eid_owe_trans_len(hapd);
- 	buflen += hostapd_eid_dpp_cc_len(hapd);
-@@ -797,7 +797,7 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
- 	pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
- 
- 	if (!params->is_ml_sta_info)
--		pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP);
-+		pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP, true);
- 	pos = hostapd_eid_fils_indic(hapd, pos, 0);
- 	pos = hostapd_get_rsnxe(hapd, pos, epos - pos);
- 
-@@ -1946,7 +1946,7 @@ static u8 * hostapd_gen_fils_discovery(struct hostapd_data *hapd, size_t *len)
- 		total_len += 3;
- 	}
- 
--	total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION);
-+	total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION, true);
- 
- 	pos = hostapd_eid_fils_indic(hapd, buf, 0);
- 	buf_len = pos - buf;
-@@ -2020,7 +2020,7 @@ static u8 * hostapd_gen_fils_discovery(struct hostapd_data *hapd, size_t *len)
- 	/* Fill in the Length field value */
- 	*length_pos = pos - (length_pos + 1);
- 
--	pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION);
-+	pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION, true);
- 
- 	/* FILS Indication element */
- 	if (buf_len) {
-@@ -2126,7 +2126,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
- 	if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
- 	    hapd == hostapd_mbssid_get_tx_bss(hapd))
- 		tail_len += 5; /* Multiple BSSID Configuration element */
--	tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON);
-+	tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON, true);
- 	tail_len += hostapd_mbo_ie_len(hapd);
- 	tail_len += hostapd_eid_owe_trans_len(hapd);
- 	tail_len += hostapd_eid_dpp_cc_len(hapd);
-@@ -2262,7 +2262,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
- 
- 	tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
- 
--	tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON);
-+	tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON, true);
- 	tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
- 	tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos);
- 	tailpos = hostapd_eid_mbssid_config(hapd, tailpos,
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 7ee18f4ae..9a23c7240 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -7273,20 +7273,21 @@ static size_t
- hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
- 			  struct hostapd_data *reporting_hapd,
- 			  size_t *current_len,
--			  struct mbssid_ie_profiles *skip_profiles)
-+			  struct mbssid_ie_profiles *skip_profiles,
-+			  bool mld_update)
- {
- 	size_t total_len = 0, len = *current_len;
--	int tbtt_count = 0;
--	size_t i, start = 0;
--	bool ap_mld = false;
-+	int tbtt_count, total_tbtt_count = 0;
-+	size_t i, start;
-+	u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
- 
--#ifdef CONFIG_IEEE80211BE
--	ap_mld = !!hapd->conf->mld_ap;
--#endif /* CONFIG_IEEE80211BE */
-+repeat_rnr_len:
-+	start = 0;
-+	tbtt_count = 0;
- 
- 	while (start < hapd->iface->num_bss) {
- 		if (!len ||
--		    len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255 ||
-+		    len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
- 		    tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
- 			len = RNR_HEADER_LEN;
- 			total_len += RNR_HEADER_LEN;
-@@ -7298,10 +7299,15 @@ hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
- 
- 		for (i = start; i < hapd->iface->num_bss; i++) {
- 			struct hostapd_data *bss = hapd->iface->bss[i];
-+			bool ap_mld = false;
- 
- 			if (!bss || !bss->conf || !bss->started)
- 				continue;
- 
-+#ifdef CONFIG_IEEE80211BE
-+			ap_mld = !!bss->conf->mld_ap;
-+#endif /* CONFIG_IEEE80211BE */
-+
- 			if (bss == reporting_hapd ||
- 			    bss->conf->ignore_broadcast_ssid)
- 				continue;
-@@ -7310,23 +7316,71 @@ hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
- 			    i >= skip_profiles->start && i < skip_profiles->end)
- 				continue;
- 
--			if (len + RNR_TBTT_INFO_LEN > 255 ||
-+			/* No need to report if length is for normal TBTT and the BSS
-+			 * is a MLD. MLD TBTT will include this.
-+			 */
-+			if (tbtt_info_len == RNR_TBTT_INFO_LEN && ap_mld)
-+				continue;
-+
-+			/* No need to report if length is for MLD TBTT and the BSS
-+			 * is not MLD. Normal TBTT will include this.
-+			 */
-+			if (tbtt_info_len == RNR_TBTT_INFO_MLD_LEN && !ap_mld)
-+				continue;
-+
-+#ifdef CONFIG_IEEE80211BE
-+			/* If building for co-location and they are ML partners,
-+			 * no need to include since the ML RNR will carry this.
-+			 */
-+			if (!mld_update && hostapd_is_ml_partner(reporting_hapd, bss))
-+				continue;
-+
-+			/* If building for ML RNR and they are not ML parnters,
-+			 * don't include.
-+			 */
-+			if (mld_update && !hostapd_is_ml_partner(reporting_hapd, bss))
-+				continue;
-+#endif /* CONFIG_IEEE80211BE */
-+
-+			if (len + tbtt_info_len > 255 ||
- 			    tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
- 				break;
- 
--			if (!ap_mld) {
--				len += RNR_TBTT_INFO_LEN;
--				total_len += RNR_TBTT_INFO_LEN;
--			} else {
--				len += RNR_TBTT_INFO_MLD_LEN;
--				total_len += RNR_TBTT_INFO_MLD_LEN;
--			}
-+			len += tbtt_info_len;
-+			total_len += tbtt_info_len;
- 			tbtt_count++;
- 		}
- 		start = i;
- 	}
- 
--	if (!tbtt_count)
-+	total_tbtt_count += tbtt_count;
-+
-+	/* If building for co-location, re-build again but this time include
-+	 * ML TBTTs.
-+	 */
-+	if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) {
-+		tbtt_info_len = RNR_TBTT_INFO_MLD_LEN;
-+
-+		/* If no TBTT was found, then adjust the len and total_len since
-+		 * it would have incremented before we checked all bss.
-+		 */
-+		if (!tbtt_count) {
-+			len -= RNR_TBTT_HEADER_LEN;
-+			total_len -= RNR_TBTT_HEADER_LEN;
-+		}
-+
-+		goto repeat_rnr_len;
-+	}
-+
-+	/* this is possible when it re-built and in that no suitable TBTT was
-+	 * found. Adjust the length accordingly.
-+	 */
-+	if (!tbtt_count && total_tbtt_count) {
-+		len -= RNR_TBTT_HEADER_LEN;
-+		total_len -= RNR_TBTT_HEADER_LEN;
-+	}
-+
-+	if (!total_tbtt_count)
- 		total_len = 0;
- 	else
- 		*current_len = len;
-@@ -7375,8 +7429,8 @@ static enum colocation_mode get_colocation_mode(struct hostapd_data *hapd)
- }
- 
- 
--static size_t hostapd_eid_rnr_multi_iface_len(struct hostapd_data *hapd,
--					      size_t *current_len)
-+static size_t hostapd_eid_rnr_colocation_len(struct hostapd_data *hapd,
-+					     size_t *current_len)
- {
- 	struct hostapd_iface *iface;
- 	size_t len = 0;
-@@ -7387,34 +7441,57 @@ static size_t hostapd_eid_rnr_multi_iface_len(struct hostapd_data *hapd,
- 
- 	for (i = 0; i < hapd->iface->interfaces->count; i++) {
- 		iface = hapd->iface->interfaces->iface[i];
--		bool ap_mld = false;
--
--#ifdef CONFIG_IEEE80211BE
--		if (hostapd_is_ml_partner(hapd, iface->bss[0]))
--			ap_mld = true;
--#endif /* CONFIG_IEEE80211BE */
- 
--		if (iface == hapd->iface ||
--		    !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
-+		if (!iface || iface == hapd->iface ||
-+		    !is_6ghz_op_class(iface->conf->op_class))
- 			continue;
- 
- 		len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
--						 current_len, NULL);
-+						 current_len, NULL, false);
- 	}
- 
- 	return len;
- }
- 
--
--size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type)
-+static size_t hostapd_eid_rnr_mlo_len(struct hostapd_data *hapd, u32 type,
-+				      size_t *current_len)
- {
--	size_t total_len = 0, current_len = 0;
--	enum colocation_mode mode = get_colocation_mode(hapd);
--	bool ap_mld = false;
-+	size_t len = 0;
- 
- #ifdef CONFIG_IEEE80211BE
--	ap_mld = !!hapd->conf->mld_ap;
-+	struct hostapd_iface *iface;
-+	size_t i;
-+
-+	if (!hapd->iface || !hapd->iface->interfaces)
-+		return 0;
-+
-+	if (!hapd->conf->mld_ap)
-+		return 0;
-+
-+	/* TODO allow for FILS/Action as well */
-+	if (type != WLAN_FC_STYPE_BEACON && type != WLAN_FC_STYPE_PROBE_RESP)
-+		return 0;
-+
-+	for (i = 0; i < hapd->iface->interfaces->count; i++) {
-+		iface = hapd->iface->interfaces->iface[i];
-+
-+		if (!iface || iface == hapd->iface)
-+			continue;
-+
-+		if (hapd->iface->freq == iface->freq)
-+			continue;
-+
-+		len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
-+						 current_len, NULL, true);
-+	}
- #endif /* CONFIG_IEEE80211BE */
-+	return len;
-+}
-+
-+size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type, bool include_mld_params)
-+{
-+	size_t total_len = 0, current_len = 0;
-+	enum colocation_mode mode = get_colocation_mode(hapd);
- 
- 	switch (type) {
- 	case WLAN_FC_STYPE_BEACON:
-@@ -7423,29 +7500,35 @@ size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type)
- 		/* fallthrough */
- 
- 	case WLAN_FC_STYPE_PROBE_RESP:
--		if (mode == COLOCATED_LOWER_BAND || ap_mld)
-+		if (mode == COLOCATED_LOWER_BAND)
- 			total_len +=
--				hostapd_eid_rnr_multi_iface_len(hapd,
--								&current_len);
-+				hostapd_eid_rnr_colocation_len(hapd,
-+							       &current_len);
- 
- 		if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
- 		    !hapd->iconf->mbssid)
- 			total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
- 							       &current_len,
--							       NULL);
-+							       NULL, false);
- 		break;
- 
- 	case WLAN_FC_STYPE_ACTION:
- 		if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
- 			total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
- 							       &current_len,
--							       NULL);
-+							       NULL, false);
- 		break;
- 
- 	default:
- 		break;
- 	}
- 
-+	/* For EMA Beacons, MLD neighbor repoting is added as part of mbssid rnr */
-+	if (include_mld_params &&
-+	    (type != WLAN_FC_STYPE_BEACON ||
-+	     hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED))
-+		total_len += hostapd_eid_rnr_mlo_len(hapd, type, &current_len);
-+
- 	return total_len;
- }
- 
-@@ -7509,13 +7592,14 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
- 				struct hostapd_data *reporting_hapd,
- 				struct mbssid_ie_profiles *skip_profiles,
- 				size_t i, u8 *tbtt_count, size_t *len,
--				u8 **pos)
-+				u8 **pos, u8 **tbtt_count_pos, u8 tbtt_info_len,
-+				u8 op_class, bool mld_update)
- {
- 	struct hostapd_iface *iface = hapd->iface;
- 	struct hostapd_data *bss = iface->bss[i];
- 	u8 bss_param = 0;
--	bool ap_mld = false;
- 	u8 *eid = *pos;
-+	bool ap_mld = false;
- 
- #ifdef CONFIG_IEEE80211BE
- 	ap_mld = !!hapd->conf->mld_ap;
-@@ -7529,10 +7613,47 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
- 	    && i >= skip_profiles->start && i < skip_profiles->end)
- 		return false;
- 
-+	/* No need to report if length is for normal TBTT and the BSS
-+	 * is a MLD. MLD TBTT will include this.
-+	 */
-+	if (tbtt_info_len == RNR_TBTT_INFO_LEN && ap_mld)
-+		return false;
-+
-+	/* No need to report if length is for MLD TBTT and the BSS
-+	 * is not MLD. Normal TBTT will include this.
-+	 */
-+	if (tbtt_info_len == RNR_TBTT_INFO_MLD_LEN && !ap_mld)
-+		return false;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	/* If building for co-location and they are ML partners,
-+	 * no need to include since the ML RNR will carry this.
-+	 */
-+	if (!mld_update && hostapd_is_ml_partner(reporting_hapd, bss))
-+		return false;
-+
-+	/* If building for ML RNR and they are not ML parnters,
-+	 * don't include.
-+	 */
-+	if (mld_update && !hostapd_is_ml_partner(reporting_hapd, bss))
-+		return false;
-+#endif /* CONFIG_IEEE80211BE */
-+
- 	if (*len + RNR_TBTT_INFO_LEN > 255 ||
- 	    *tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
- 		return true;
- 
-+	if (!(*tbtt_count)) {
-+		/* Add Neighbor report header info only if there is at least
-+		 * one tbtt info available
-+		 */
-+		*tbtt_count_pos = eid++;
-+		*eid++ = tbtt_info_len;
-+		*eid++ = op_class;
-+		*eid++ = bss->iconf->channel;
-+		*len += RNR_TBTT_HEADER_LEN;
-+	}
-+
- 	*eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
- 	os_memcpy(eid, bss->own_addr, ETH_ALEN);
- 	eid += ETH_ALEN;
-@@ -7556,29 +7677,36 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
- 	*eid++ = bss_param;
- 	*eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER;
- 
--	if (!ap_mld) {
--		*len += RNR_TBTT_INFO_LEN;
--	} else {
- #ifdef CONFIG_IEEE80211BE
--		u8 param_ch = hapd->eht_mld_bss_param_change;
--
--		if (hostapd_is_ml_partner(bss, reporting_hapd))
--			*eid++ = 0;
--		else
--			*eid++ = hostapd_get_mld_id(hapd);
--
--		*eid++ = hapd->mld_link_id | ((param_ch & 0xF) << 4);
--		*eid = (param_ch >> 4) & 0xF;
-+	if (ap_mld) {
-+		u8 param_ch = bss->eht_mld_bss_param_change;
-+		bool is_partner;
-+
-+		/* If bss is not partner of the reporting_hapd then
-+		 *  a) MLD ID advertised shall be 255.
-+		 *  b) Link ID advertised shall be 15.
-+		 *  c) BPCC advertised shall be 255
-+		 */
-+		is_partner = hostapd_is_ml_partner(bss, reporting_hapd);
-+		/* MLD ID */
-+		*eid++ = is_partner ? hostapd_get_mld_id(bss) : 0xFF;
-+		/* Link ID (Bit 3 to Bit 0)
-+		 * BPCC (Bit 4 to Bit 7)
-+		 */
-+		*eid++ = is_partner ?
-+			 bss->mld_link_id | ((param_ch & 0xF) << 4) :
-+			 (MAX_NUM_MLD_LINKS | 0xF0);
-+		/* BPCC (Bit 3 to Bit 0) */
-+		*eid = is_partner ? ((param_ch & 0xF0) >> 4) : 0x0F;
- #ifdef CONFIG_TESTING_OPTIONS
--		if (hapd->conf->mld_indicate_disabled)
-+		if (bss->conf->mld_indicate_disabled)
- 			*eid |= RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
- #endif /* CONFIG_TESTING_OPTIONS */
- 		eid++;
--
--		*len += RNR_TBTT_INFO_MLD_LEN;
--#endif /* CONFIG_IEEE80211BE */
- 	}
-+#endif /* CONFIG_IEEE80211BE */
- 
-+	*len += tbtt_info_len;
- 	(*tbtt_count)++;
- 	*pos = eid;
- 
-@@ -7589,18 +7717,16 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
- static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
- 				  struct hostapd_data *reporting_hapd,
- 				  u8 *eid, size_t *current_len,
--				  struct mbssid_ie_profiles *skip_profiles)
-+				  struct mbssid_ie_profiles *skip_profiles,
-+				  bool mld_update)
- {
- 	struct hostapd_iface *iface = hapd->iface;
--	size_t i, start = 0;
-+	size_t i, start;
- 	size_t len = *current_len;
--	u8 *tbtt_count_pos, *eid_start = eid, *size_offset = (eid - len) + 1;
--	u8 tbtt_count = 0, op_class, channel;
--	bool ap_mld = false;
--
--#ifdef CONFIG_IEEE80211BE
--	ap_mld = !!hapd->conf->mld_ap;
--#endif /* CONFIG_IEEE80211BE */
-+	u8 *eid_start = eid, *size_offset = (eid - len) + 1;
-+	u8 *tbtt_count_pos = size_offset + 1;
-+	u8 tbtt_count, total_tbtt_count = 0, op_class, channel;
-+	u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
- 
- 	if (!(iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || !iface->freq)
- 		return eid;
-@@ -7612,9 +7738,12 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
- 	    NUM_HOSTAPD_MODES)
- 		return eid;
- 
-+repeat_rnr:
-+	start = 0;
-+	tbtt_count = 0;
- 	while (start < iface->num_bss) {
- 		if (!len ||
--		    len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255 ||
-+		    len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
- 		    tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
- 			eid_start = eid;
- 			*eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT;
-@@ -7623,34 +7752,42 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
- 			tbtt_count = 0;
- 		}
- 
--		tbtt_count_pos = eid++;
--		*eid++ = ap_mld ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
--		*eid++ = op_class;
--		*eid++ = hapd->iconf->channel;
--		len += RNR_TBTT_HEADER_LEN;
--
- 		for (i = start; i < iface->num_bss; i++) {
- 			if (hostapd_eid_rnr_bss(hapd, reporting_hapd,
- 						skip_profiles, i,
--						&tbtt_count, &len, &eid))
-+						&tbtt_count, &len, &eid,
-+						&tbtt_count_pos, tbtt_info_len,
-+						op_class, mld_update))
- 				break;
- 		}
- 
- 		start = i;
--		*tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
--		*size_offset = (eid - size_offset) - 1;
-+
-+		if (tbtt_count) {
-+			*tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
-+			*size_offset = (eid - size_offset) - 1;
-+		}
-+	}
-+
-+	total_tbtt_count += tbtt_count;
-+
-+	/* If building for co-location, re-build again but this time include
-+	 * ML TBTTs.
-+	 */
-+	if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) {
-+		tbtt_info_len = RNR_TBTT_INFO_MLD_LEN;
-+		goto repeat_rnr;
- 	}
- 
--	if (tbtt_count == 0)
-+	if (!total_tbtt_count)
- 		return eid_start;
- 
- 	*current_len = len;
- 	return eid;
- }
- 
--
--static u8 * hostapd_eid_rnr_multi_iface(struct hostapd_data *hapd, u8 *eid,
--					size_t *current_len)
-+u8 *hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
-+			       size_t *current_len)
- {
- 	struct hostapd_iface *iface;
- 	size_t i;
-@@ -7660,35 +7797,56 @@ static u8 * hostapd_eid_rnr_multi_iface(struct hostapd_data *hapd, u8 *eid,
- 
- 	for (i = 0; i < hapd->iface->interfaces->count; i++) {
- 		iface = hapd->iface->interfaces->iface[i];
--		bool ap_mld = false;
- 
--#ifdef CONFIG_IEEE80211BE
--		if (hostapd_is_ml_partner(hapd, iface->bss[0]))
--			ap_mld = true;
--#endif /* CONFIG_IEEE80211BE */
--
--		if (iface == hapd->iface ||
--		    !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
-+		if (!iface || iface == hapd->iface ||
-+		    !is_6ghz_op_class(iface->conf->op_class))
- 			continue;
- 
- 		eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
--					    current_len, NULL);
-+					    current_len, NULL, false);
- 	}
- 
- 	return eid;
- }
- 
-+u8 *hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
-+			u8 *eid, size_t *current_len)
-+{
-+#ifdef CONFIG_IEEE80211BE
-+	struct hostapd_iface *iface;
-+	size_t i;
-+
-+	if (!hapd->iface || !hapd->iface->interfaces)
-+		return eid;
-+
-+	if (!hapd->conf->mld_ap)
-+		return eid;
-+
-+	/* TODO allow for FILS/Action as well */
-+	if (type != WLAN_FC_STYPE_BEACON && type != WLAN_FC_STYPE_PROBE_RESP)
-+		return eid;
-+
-+	for (i = 0; i < hapd->iface->interfaces->count; i++) {
-+		iface = hapd->iface->interfaces->iface[i];
-+
-+		if (!iface || iface == hapd->iface)
-+			continue;
-+
-+		if (hapd->iface->freq == iface->freq)
-+			continue;
- 
--u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
-+		eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
-+					    current_len, NULL, true);
-+	}
-+#endif /* CONFIG_IEEE80211BE */
-+	return eid;
-+}
-+
-+u8 *hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type, bool include_mld_params)
- {
- 	u8 *eid_start = eid;
- 	size_t current_len = 0;
- 	enum colocation_mode mode = get_colocation_mode(hapd);
--	bool ap_mld = false;
--
--#ifdef CONFIG_IEEE80211BE
--	ap_mld = !!hapd->conf->mld_ap;
--#endif /* CONFIG_IEEE80211BE */
- 
- 	switch (type) {
- 	case WLAN_FC_STYPE_BEACON:
-@@ -7697,26 +7855,34 @@ u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
- 		/* fallthrough */
- 
- 	case WLAN_FC_STYPE_PROBE_RESP:
--		if (mode == COLOCATED_LOWER_BAND || ap_mld)
--			eid = hostapd_eid_rnr_multi_iface(hapd, eid,
--							  &current_len);
-+		if (mode == COLOCATED_LOWER_BAND)
-+			eid = hostapd_eid_rnr_colocation(hapd, eid,
-+							 &current_len);
- 
- 		if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
- 		    !hapd->iconf->mbssid)
- 			eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
--						    &current_len, NULL);
-+						    &current_len, NULL,
-+						    false);
- 		break;
- 
- 	case WLAN_FC_STYPE_ACTION:
- 		if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
- 			eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
--						    &current_len, NULL);
-+						    &current_len, NULL,
-+						    false);
- 		break;
- 
- 	default:
- 		return eid_start;
- 	}
- 
-+	/* For EMA Beacons, MLD neighbor repoting is added as part of mbssid rnr */
-+	if (include_mld_params &&
-+	    (type != WLAN_FC_STYPE_BEACON ||
-+	     hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED))
-+		eid = hostapd_eid_rnr_mlo(hapd, type, eid, &current_len);
-+
- 	if (eid == eid_start + 2)
- 		return eid_start;
- 
-@@ -7815,6 +7981,11 @@ size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
- 			      size_t known_bss_len, size_t *rnr_len)
- {
- 	size_t len = 0, bss_index = 1;
-+	bool ap_mld = false;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	ap_mld = !!hapd->conf->mld_ap;
-+#endif /* CONFIG_IEEE80211BE */
- 
- 	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
- 	    (frame_type != WLAN_FC_STYPE_BEACON &&
-@@ -7847,12 +8018,12 @@ size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
- 
- 			*rnr_len += hostapd_eid_rnr_iface_len(
- 				hapd, hostapd_mbssid_get_tx_bss(hapd),
--				&rnr_cur_len, &skip_profiles);
-+				&rnr_cur_len, &skip_profiles, ap_mld);
- 		}
- 	}
- 
- 	if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && rnr_len)
--		*rnr_len += hostapd_eid_rnr_len(hapd, frame_type);
-+		*rnr_len += hostapd_eid_rnr_len(hapd, frame_type, false);
- 
- 	return len;
- }
-@@ -7978,7 +8149,11 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
- {
- 	size_t bss_index = 1, cur_len = 0;
- 	u8 elem_index = 0, *rnr_start_eid = rnr_eid;
--	bool add_rnr;
-+	bool add_rnr, ap_mld = false;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	ap_mld = !!hapd->conf->mld_ap;
-+#endif /* CONFIG_IEEE80211BE */
- 
- 	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
- 	    (frame_stype != WLAN_FC_STYPE_BEACON &&
-@@ -8023,7 +8198,7 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
- 			cur_len = 0;
- 			rnr_eid = hostapd_eid_rnr_iface(
- 				hapd, hostapd_mbssid_get_tx_bss(hapd),
--				rnr_eid, &cur_len, &skip_profiles);
-+				rnr_eid, &cur_len, &skip_profiles, ap_mld);
- 		}
- 	}
- 
-@@ -8035,8 +8210,8 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
- 		if (hapd->conf->rnr)
- 			rnr_eid = hostapd_eid_nr_db(hapd, rnr_eid, &cur_len);
- 		if (get_colocation_mode(hapd) == COLOCATED_LOWER_BAND)
--			rnr_eid = hostapd_eid_rnr_multi_iface(hapd, rnr_eid,
--							      &cur_len);
-+			rnr_eid = hostapd_eid_rnr_colocation(hapd, rnr_eid,
-+							     &cur_len);
- 	}
- 
- 	return eid;
-diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
-index 262e0ce14..078f4baf9 100644
---- a/src/ap/ieee802_11.h
-+++ b/src/ap/ieee802_11.h
-@@ -225,8 +225,9 @@ void auth_sae_process_commit(void *eloop_ctx, void *user_ctx);
- u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len);
- u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
- 		    const u8 *ext_capab_ie, size_t ext_capab_ie_len);
--size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type);
--u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type);
-+size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type, bool include_mld_params);
-+u8 *hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type,
-+		    bool include_mld_params);
- int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
- 			       int res, struct radius_sta *info);
- size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0020-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0020-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch
new file mode 100644
index 0000000..dbfad2e
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0020-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch
@@ -0,0 +1,32 @@
+From 761a5a50507b7af032ba35a08d8b3fa0a3b8991a Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 22 Sep 2022 16:08:09 +0800
+Subject: [PATCH 020/126] mtk: hostapd: Do not include HE capab IE if
+ associated sta's HE capab IE is invalid
+
+The parameter 'sta' passed to send_assoc_resp() might be NULL, so an
+NULL check is necessary before access the 'sta'.
+Only one such check was missed in this function, and this patch fixs it.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ieee802_11.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 39b1bb4c7..18d5b8f79 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -5007,7 +5007,8 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
+ #endif /* CONFIG_IEEE80211AC */
+ 
+ #ifdef CONFIG_IEEE80211AX
+-	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
++	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax && sta &&
++			sta->flags & WLAN_STA_HE) {
+ 		p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
+ 		p = hostapd_eid_he_operation(hapd, p);
+ 		p = hostapd_eid_cca(hapd, p);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0021-mtk-hostapd-Add-DFS-detection-mode.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0021-mtk-hostapd-Add-DFS-detection-mode.patch
new file mode 100644
index 0000000..b520988
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0021-mtk-hostapd-Add-DFS-detection-mode.patch
@@ -0,0 +1,136 @@
+From 83fcf8b56d7886992d6dc474ca09400177304705 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 14:55:49 +0800
+Subject: [PATCH 021/126] mtk: hostapd: Add DFS detection mode
+
+Add DFS detection mode for testing radar detection rate.
+If DFS detection mode is on, AP will not switch channels when receiving
+a radar signal.
+This detection mode also supports background chain.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/config_file.c |  4 ++++
+ hostapd/ctrl_iface.c  | 23 +++++++++++++++++++++++
+ src/ap/ap_config.h    | 13 +++++++++++++
+ src/ap/dfs.c          | 10 ++++++++++
+ 4 files changed, 50 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index e437aa981..a5fa97a35 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5520,6 +5520,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 	} else if (os_strcmp(buf, "ibf_enable") == 0) { /*ibf setting is per device*/
+ 		int val = atoi(pos);
+ 		conf->ibf_enable = !!val;
++	} else if (os_strcmp(buf, "dfs_detect_mode") == 0) { /*bypass channel switch*/
++		u8 en = strtol(pos, NULL, 10);
++
++		conf->dfs_detect_mode = en;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 3221c0fb1..ef0ade6c9 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4264,6 +4264,26 @@ hostapd_ctrl_iface_get_ibf(struct hostapd_data *hapd, char *buf,
+ }
+ 
+ 
++static int
++hostapd_ctrl_iface_set_dfs_detect_mode(struct hostapd_data *hapd, char *value,
++				       char *buf, size_t buflen)
++{
++	u8 dfs_detect_mode;
++
++	if (!value)
++		return -1;
++
++	dfs_detect_mode = strtol(value, NULL, 10);
++	if (dfs_detect_mode > DFS_DETECT_MODE_MAX) {
++		wpa_printf(MSG_ERROR, "Invalid value for dfs detect mode");
++		return -1;
++	}
++	hapd->iconf->dfs_detect_mode = dfs_detect_mode;
++
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -4885,6 +4905,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
+ 	} else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
+ 		reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
++	} else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
++		reply_len = hostapd_ctrl_iface_set_dfs_detect_mode(hapd, buf + 16,
++								   reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 3f07147db..f7027473e 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1341,6 +1341,7 @@ struct hostapd_config {
+ 	int *edcca_threshold;
+ 	u8 three_wire_enable;
+ 	u8 ibf_enable;
++	u8 dfs_detect_mode;
+ };
+ 
+ enum three_wire_mode {
+@@ -1355,6 +1356,18 @@ enum three_wire_mode {
+ 		NUM_THREE_WIRE_MODE - 1
+ };
+ 
++enum dfs_mode {
++	DFS_DETECT_MODE_DISABLE,
++	DFS_DETECT_MODE_AP_ENABLE,
++	DFS_DETECT_MODE_BACKGROUND_ENABLE,
++	DFS_DETECT_MODE_ALL_ENABLE,
++
++	/* keep last */
++	NUM_DFS_DETECT_MODE,
++	DFS_DETECT_MODE_MAX =
++		NUM_DFS_DETECT_MODE - 1
++};
++
+ enum edcca_mode {
+ 	EDCCA_MODE_FORCE_DISABLE = 0,
+ 	EDCCA_MODE_AUTO = 1,
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index fe044297b..49dc4d424 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1347,6 +1347,11 @@ hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
+ 		   __func__, iface->radar_background.cac_started ? "yes" : "no",
+ 		   hostapd_csa_in_progress(iface) ? "yes" : "no");
+ 
++	/* Skip channel switch when background dfs detect mode is on */
++	if (iface->conf->dfs_detect_mode == DFS_DETECT_MODE_BACKGROUND_ENABLE ||
++	    iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
++		return 0;
++
+ 	/* Check if CSA in progress */
+ 	if (hostapd_csa_in_progress(iface))
+ 		return 0;
+@@ -1395,6 +1400,11 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
+ 		   __func__, iface->cac_started ? "yes" : "no",
+ 		   hostapd_csa_in_progress(iface) ? "yes" : "no");
+ 
++	/* Skip channel switch when dfs detect mode is on */
++	if (iface->conf->dfs_detect_mode == DFS_DETECT_MODE_AP_ENABLE ||
++	    iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
++		return 0;
++
+ 	/* Check if CSA in progress */
+ 	if (hostapd_csa_in_progress(iface))
+ 		return 0;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0021-tests-MLO-add-basic-cohosted-MLDs-functionality-test.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0021-tests-MLO-add-basic-cohosted-MLDs-functionality-test.patch
deleted file mode 100644
index a4eb061..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0021-tests-MLO-add-basic-cohosted-MLDs-functionality-test.patch
+++ /dev/null
@@ -1,271 +0,0 @@
-From c43241d046e8a6ae75549c23d470b94f16c74ca7 Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:51 +0530
-Subject: [PATCH 021/104] tests: MLO: add basic cohosted MLDs functionality
- testing
-
-Add test case to test basic cohosted MLDs functionality. Add helper
-functions to create the configuration file, start hostapd instance.
-
-Client connectivty test case will be added via a subsequent change.
-
-eht_mld_cohosted_discovery: 2 co-hosted MLDs without non-MLD RNR. Basic
-bring up and beacon, MLD RNR, scan validation.
-
-eht_mld_cohosted_discovery_with_rnr: Same like eht_mld_cohosted_discovery
-but additionally non-MLD RNR (rnr=1) is also enabled. Validate the non-MLD
-RNR as well.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- tests/hwsim/test_eht.py | 230 ++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 230 insertions(+)
-
-diff --git a/tests/hwsim/test_eht.py b/tests/hwsim/test_eht.py
-index a012fe4e7..732406219 100644
---- a/tests/hwsim/test_eht.py
-+++ b/tests/hwsim/test_eht.py
-@@ -15,6 +15,7 @@ from tshark import run_tshark
- from test_gas import hs20_ap_params
- from test_dpp import check_dpp_capab, wait_auth_success
- from test_rrm import build_beacon_request, run_req_beacon, BeaconReport
-+import os, subprocess, time, tempfile
- 
- def eht_verify_wifi_version(dev):
-     status = dev.get_status()
-@@ -1823,3 +1824,232 @@ def test_eht_mlo_csa(dev, apdev):
-             traffic_test(wpas, hapd0)
- 
-             #TODO: CSA on non-first link
-+
-+def create_base_conf_file(iface, channel, prefix='hostapd-', hw_mode='g',
-+                          op_class=None):
-+    # Create configuration file and add phy characteristics
-+    fd, fname = tempfile.mkstemp(dir='/tmp',
-+                                 prefix=prefix + iface + "-chan-" + str(channel) + "-")
-+    f = os.fdopen(fd, 'w')
-+
-+    f.write("driver=nl80211\n")
-+    f.write("hw_mode=" + str(hw_mode) + "\n")
-+    f.write("ieee80211n=1\n")
-+    if hw_mode == 'a' and \
-+       (op_class is None or \
-+        op_class not in [131, 132, 133, 134, 135, 136, 137]):
-+        f.write("ieee80211ac=1\n")
-+    f.write("ieee80211ax=1\n")
-+    f.write("ieee80211be=1\n")
-+    f.write("channel=" + str(channel) + "\n")
-+
-+    return f, fname
-+
-+def append_bss_conf_to_file(f, ifname, params, first=False):
-+    # Add BSS specific characteristics
-+    config = "bss"
-+
-+    if first:
-+        config = "interface"
-+
-+    f.write("\n" + config + "=%s\n" % ifname)
-+
-+    for k, v in list(params.items()):
-+        f.write("{}={}\n".format(k,v))
-+
-+    f.write("mld_ap=1\n")
-+
-+def dump_config(fname):
-+    with open(fname, 'r') as f:
-+        cfg = f.read()
-+        logger.debug("hostapd config: " + str(fname) + "\n" + cfg)
-+
-+def get_config(iface, count, ssid, passphrase, channel, bssid_regex,
-+               rnr=False, debug=False):
-+    f, fname = create_base_conf_file(iface, channel=channel)
-+    hapds = []
-+
-+    for i in range(count):
-+        if i == 0:
-+            ifname = iface
-+        else:
-+            ifname = iface + "-" + str(i)
-+
-+        set_ssid = ssid + str(i)
-+        set_passphrase = passphrase + str(i)
-+        params = hostapd.wpa2_params(ssid=set_ssid, passphrase=set_passphrase,
-+                                     wpa_key_mgmt="SAE", ieee80211w="2")
-+        params['sae_pwe'] = "2"
-+        params['group_mgmt_cipher'] = "AES-128-CMAC"
-+        params['beacon_prot'] = "1"
-+        params["ctrl_interface"] = "/var/run/hostapd/chan_" + str(channel)
-+        params["bssid"] = bssid_regex % (i + 1)
-+
-+        if rnr:
-+            params["rnr"]="1"
-+
-+        append_bss_conf_to_file(f, ifname, params, first=(i == 0))
-+
-+        hapds.append([ifname, params["ctrl_interface"], i])
-+
-+    f.close()
-+
-+    if debug:
-+        dump_config(fname)
-+
-+    return fname, hapds
-+
-+def start_ap(prefix, configs):
-+    pid = prefix + ".hostapd.pid"
-+    configs = configs.split()
-+
-+    cmd = ['../../hostapd/hostapd', '-ddKtB', '-P', pid, '-f',
-+           prefix + ".hostapd-log"]
-+
-+    cmd = cmd + configs
-+
-+    logger.info("Starting APs")
-+    res = subprocess.check_call(cmd)
-+    if res != 0:
-+        raise Exception("Could not start hostapd: %s" % str(res))
-+
-+    # Wait for hostapd to complete initialization and daemonize.
-+    time.sleep(2)
-+
-+    if not os.path.exists(pid):
-+        raise Exception("hostapd did not create PID file.")
-+
-+def get_mld_devs(hapd_iface, count, prefix, rnr=False):
-+    fname1, hapds1 = get_config(hapd_iface, count=count, ssid="mld-",
-+                                passphrase="qwertyuiop-", channel=1,
-+                                bssid_regex="02:00:00:00:07:%02x",
-+                                rnr=rnr, debug=True)
-+    fname2, hapds2 = get_config(hapd_iface, count=count, ssid="mld-",
-+                                passphrase="qwertyuiop-", channel=6,
-+                                bssid_regex="02:00:00:00:08:%02x",
-+                                rnr=rnr, debug=True)
-+
-+    start_ap(prefix, fname1 + " " + fname2)
-+
-+    hapd_mld1_link0 = hostapd.Hostapd(ifname=hapds1[0][0], ctrl=hapds1[0][1],
-+                                      bssidx=hapds1[0][2])
-+    hapd_mld1_link1 = hostapd.Hostapd(ifname=hapds2[0][0], ctrl=hapds2[0][1],
-+                                      bssidx=hapds2[0][2])
-+
-+    hapd_mld2_link0 = hostapd.Hostapd(ifname=hapds1[1][0], ctrl=hapds1[1][1],
-+                                      bssidx=hapds1[1][2])
-+    hapd_mld2_link1 = hostapd.Hostapd(ifname=hapds2[1][0], ctrl=hapds2[1][1],
-+                                      bssidx=hapds2[1][2])
-+
-+    if not hapd_mld1_link0.ping():
-+        raise Exception("Could not ping hostapd")
-+
-+    if not hapd_mld1_link1.ping():
-+        raise Exception("Could not ping hostapd")
-+
-+    if not hapd_mld2_link0.ping():
-+        raise Exception("Could not ping hostapd")
-+
-+    if not hapd_mld2_link1.ping():
-+        raise Exception("Could not ping hostapd")
-+
-+    os.remove(fname1)
-+    os.remove(fname2)
-+
-+    return [hapd_mld1_link0, hapd_mld1_link1, hapd_mld2_link0, hapd_mld2_link1]
-+
-+def stop_mld_devs(hapds, pid):
-+    pid = pid + ".hostapd.pid"
-+
-+    if "OK" not in hapds[0].request("TERMINATE"):
-+        raise Exception("Failed to terminate hostapd process")
-+
-+    ev = hapds[0].wait_event(["CTRL-EVENT-TERMINATING"], timeout=15)
-+    if ev is None:
-+        raise Exception("CTRL-EVENT-TERMINATING not seen")
-+
-+    time.sleep(0.5)
-+
-+    if os.path.exists(pid):
-+        raise Exception("PID file exits after process termination")
-+
-+def eht_parse_rnr(bss, rnr=False, exp_bssid=None):
-+        partner_rnr_pattern = re.compile(".*ap_info.*, mld ID=0, link ID=",
-+                                         re.MULTILINE)
-+        ml_pattern = re.compile(".*multi-link:.*, MLD addr=.*", re.MULTILINE)
-+
-+        if partner_rnr_pattern.search(bss) is None:
-+            raise Exception("RNR element not found for first link of first MLD")
-+
-+        if ml_pattern.search(bss) is None:
-+            raise Exception("ML element not found for first link of first MLD")
-+
-+        if not rnr:
-+            return
-+
-+        coloc_rnr_pattern = re.compile(".*ap_info.*, mld ID=255, link ID=..",
-+                                       re.MULTILINE)
-+
-+        if coloc_rnr_pattern.search(bss) is None:
-+            raise Exception("RNR element not found for co-located BSS")
-+
-+        line = coloc_rnr_pattern.search(bss).group()
-+        if line.count('bssid') > 1:
-+            raise Exception("More than one BSS found for co-located RNR")
-+
-+        # Get the BSSID carried in the RNR
-+        index = line.rindex('bssid')
-+        bssid = line[index+len('bssid')+1:].split(',')[0]
-+
-+        # Get the MLD ID carried in the RNR
-+        index = line.rindex('link ID')
-+        link_id = line[index+len('link ID')+1:].split(',')[0]
-+
-+        if link_id != "15":
-+            raise Exception("Unexpected link ID for co-located BSS which is not own partner")
-+
-+        if bssid != exp_bssid:
-+            raise Exception("Unexpected BSSID for co-located BSS")
-+
-+def eht_mld_cohosted_discovery(dev, apdev, params, rnr=False):
-+    with HWSimRadio(use_mlo=True, n_channels=2) as (hapd_radio, hapd_iface), \
-+        HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface):
-+
-+        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
-+        wpas.interface_add(wpas_iface)
-+
-+        hapds = get_mld_devs(hapd_iface=hapd_iface, count=2, prefix=params['prefix'],
-+                             rnr=rnr)
-+
-+        # Only scan link 0
-+        res = wpas.request("SCAN freq=2412")
-+        if "FAIL" in res:
-+            raise Exception("Failed to start scan")
-+
-+        ev = wpas.wait_event(["CTRL-EVENT-SCAN-STARTED"])
-+        if ev is None:
-+            raise Exception("Scan did not start")
-+
-+        ev = wpas.wait_event(["CTRL-EVENT-SCAN-RESULTS"])
-+        if ev is None:
-+            raise Exception("Scan did not complete")
-+
-+        logger.info("Scan done")
-+
-+        bss = wpas.request("BSS " + hapds[0].own_addr())
-+        logger.info("BSS 0_0: " + str(bss))
-+        eht_parse_rnr(bss, rnr, hapds[2].own_addr())
-+
-+        bss = wpas.request("BSS " + hapds[2].own_addr())
-+        logger.info("BSS 1_0: " + str(bss))
-+        eht_parse_rnr(bss, rnr, hapds[0].own_addr())
-+
-+        stop_mld_devs(hapds, params['prefix'])
-+
-+def test_eht_mld_cohosted_discovery(dev, apdev, params):
-+    """EHT 2 AP MLDs discovery"""
-+    eht_mld_cohosted_discovery(dev, apdev, params)
-+
-+def test_eht_mld_cohosted_discovery_with_rnr(dev, apdev, params):
-+    """EHT 2 AP MLDs discovery (with co-location RNR)"""
-+    eht_mld_cohosted_discovery(dev, apdev, params, rnr=True)
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0022-mtk-hostapd-Add-DFS-offchan-channel-switch.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0022-mtk-hostapd-Add-DFS-offchan-channel-switch.patch
new file mode 100644
index 0000000..6e13b02
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0022-mtk-hostapd-Add-DFS-offchan-channel-switch.patch
@@ -0,0 +1,192 @@
+From b74aa64e3cbacf537ac3e7a96f3295ebe0979c0c Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 14:56:55 +0800
+Subject: [PATCH 022/126] mtk: hostapd: Add DFS offchan channel switch
+
+Add DFS background chain channel switch command for testing purpose.
+This feature is implemented via hostapd_cli command.
+Command format:
+hostapd_cli -i <interface> raw SET_OFFCHAN_CTRL chan=<dfs_channel>
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 72 ++++++++++++++++++++++++++++++++++++++++++++
+ src/ap/dfs.c         | 25 ++++++---------
+ src/ap/dfs.h         | 15 +++++++++
+ 3 files changed, 96 insertions(+), 16 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index ef0ade6c9..32d4997b2 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4284,6 +4284,76 @@ hostapd_ctrl_iface_set_dfs_detect_mode(struct hostapd_data *hapd, char *value,
+ }
+ 
+ 
++static int
++hostapd_ctrl_iface_set_offchan_ctrl(struct hostapd_data *hapd, char *cmd,
++				    char *buf, size_t buflen)
++{
++	struct hostapd_iface *iface = hapd->iface;
++	char *pos, *param;
++	enum hostapd_hw_mode hw_mode;
++	bool chan_found = false;
++	int i, num_available_chandefs, channel, chan_width, sec = 0;
++	int sec_chan_idx_80p80 = -1;
++	u8 oper_centr_freq_seg0_idx, oper_centr_freq_seg1_idx;
++	struct hostapd_channel_data *chan;
++	enum dfs_channel_type type = DFS_NO_CAC_YET;
++
++	param = os_strchr(cmd, ' ');
++	if (!param)
++		return -1;
++	*param++ = '\0';
++
++	pos = os_strstr(param, "chan=");
++	if (pos)
++		channel = strtol(pos + 5, NULL, 10);
++	else
++		return -1;
++
++	num_available_chandefs = dfs_find_channel(iface, NULL, 0, 0, type);
++	for (i = 0; i < num_available_chandefs; i++) {
++		dfs_find_channel(iface, &chan, 0, i, type);
++		if (chan->chan == channel) {
++			chan_found = true;
++			break;
++		}
++	}
++
++	if (!chan_found)
++		return -1;
++
++	if (iface->conf->secondary_channel)
++		sec = 1;
++
++	dfs_adjust_center_freq(iface, chan,
++			       sec,
++			       sec_chan_idx_80p80,
++			       &oper_centr_freq_seg0_idx,
++			       &oper_centr_freq_seg1_idx);
++
++	if (hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
++				  chan->freq, chan->chan,
++				  iface->conf->ieee80211n,
++				  iface->conf->ieee80211ac,
++				  iface->conf->ieee80211ax,
++				  iface->conf->ieee80211be,
++				  sec, hostapd_get_oper_chwidth(iface->conf),
++				  oper_centr_freq_seg0_idx,
++				  oper_centr_freq_seg1_idx, true)) {
++		wpa_printf(MSG_ERROR, "DFS failed to start CAC offchannel");
++		iface->radar_background.channel = -1;
++		return -1;
++	}
++
++	iface->radar_background.channel = chan->chan;
++	iface->radar_background.freq = chan->freq;
++	iface->radar_background.secondary_channel = sec;
++	iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
++	iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
++
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -4908,6 +4978,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 	} else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
+ 		reply_len = hostapd_ctrl_iface_set_dfs_detect_mode(hapd, buf + 16,
+ 								   reply, reply_size);
++	} else if (os_strncmp(buf, "SET_OFFCHAN_CTRL", 16) == 0) {
++		reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 49dc4d424..42cce2dce 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -20,13 +20,6 @@
+ #include "dfs.h"
+ #include "crypto/crypto.h"
+ 
+-
+-enum dfs_channel_type {
+-	DFS_ANY_CHANNEL,
+-	DFS_AVAILABLE, /* non-radar or radar-available */
+-	DFS_NO_CAC_YET, /* radar-not-yet-available */
+-};
+-
+ static struct hostapd_channel_data *
+ dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
+ 			u8 *oper_centr_freq_seg0_idx,
+@@ -239,9 +232,9 @@ static int is_in_chanlist(struct hostapd_iface *iface,
+  *  - hapd->vht/he_oper_centr_freq_seg0_idx
+  *  - hapd->vht/he_oper_centr_freq_seg1_idx
+  */
+-static int dfs_find_channel(struct hostapd_iface *iface,
+-			    struct hostapd_channel_data **ret_chan,
+-			    int idx, enum dfs_channel_type type)
++int dfs_find_channel(struct hostapd_iface *iface,
++		     struct hostapd_channel_data **ret_chan,
++		     int idx, enum dfs_channel_type type)
+ {
+ 	struct hostapd_hw_modes *mode;
+ 	struct hostapd_channel_data *chan;
+@@ -301,12 +294,12 @@ static int dfs_find_channel(struct hostapd_iface *iface,
+ }
+ 
+ 
+-static void dfs_adjust_center_freq(struct hostapd_iface *iface,
+-				   struct hostapd_channel_data *chan,
+-				   int secondary_channel,
+-				   int sec_chan_idx_80p80,
+-				   u8 *oper_centr_freq_seg0_idx,
+-				   u8 *oper_centr_freq_seg1_idx)
++void dfs_adjust_center_freq(struct hostapd_iface *iface,
++			    struct hostapd_channel_data *chan,
++			    int secondary_channel,
++			    int sec_chan_idx_80p80,
++			    u8 *oper_centr_freq_seg0_idx,
++			    u8 *oper_centr_freq_seg1_idx)
+ {
+ 	if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax)
+ 		return;
+diff --git a/src/ap/dfs.h b/src/ap/dfs.h
+index 606c1b393..c2556d2d9 100644
+--- a/src/ap/dfs.h
++++ b/src/ap/dfs.h
+@@ -9,6 +9,12 @@
+ #ifndef DFS_H
+ #define DFS_H
+ 
++enum dfs_channel_type {
++	DFS_ANY_CHANNEL,
++	DFS_AVAILABLE, /* non-radar or radar-available */
++	DFS_NO_CAC_YET, /* radar-not-yet-available */
++};
++
+ int hostapd_handle_dfs(struct hostapd_iface *iface);
+ 
+ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+@@ -32,5 +38,14 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
+ int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
+ 			   int center_freq);
++int dfs_find_channel(struct hostapd_iface *iface,
++		     struct hostapd_channel_data **ret_chan,
++		     int idx, enum dfs_channel_type type);
++void dfs_adjust_center_freq(struct hostapd_iface *iface,
++			    struct hostapd_channel_data *chan,
++			    int secondary_channel,
++			    int sec_chan_idx_80p80,
++			    u8 *oper_centr_freq_seg0_idx,
++			    u8 *oper_centr_freq_seg1_idx);
+ 
+ #endif /* DFS_H */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0022-tests-MLO-add-cohosted-MLDs-connectivity-testing.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0022-tests-MLO-add-cohosted-MLDs-connectivity-testing.patch
deleted file mode 100644
index af3c390..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0022-tests-MLO-add-cohosted-MLDs-connectivity-testing.patch
+++ /dev/null
@@ -1,67 +0,0 @@
-From 29a075f5ea644abdfb9bd93f79b05c72bb9fb78c Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:52 +0530
-Subject: [PATCH 022/104] tests: MLO: add cohosted MLDs connectivity testing
-
-Add test case 'eht_mld_cohosted_connectivity' which creates two 2 link AP
-MLDs and  connect 2 links MLD client to each one of them and test data
-traffic.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- tests/hwsim/test_eht.py | 42 +++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 42 insertions(+)
-
-diff --git a/tests/hwsim/test_eht.py b/tests/hwsim/test_eht.py
-index 732406219..f09d31878 100644
---- a/tests/hwsim/test_eht.py
-+++ b/tests/hwsim/test_eht.py
-@@ -2053,3 +2053,45 @@ def test_eht_mld_cohosted_discovery(dev, apdev, params):
- def test_eht_mld_cohosted_discovery_with_rnr(dev, apdev, params):
-     """EHT 2 AP MLDs discovery (with co-location RNR)"""
-     eht_mld_cohosted_discovery(dev, apdev, params, rnr=True)
-+
-+def test_eht_mld_cohosted_connectivity(dev, apdev, params):
-+    """EHT 2 AP MLDs with 2 MLD clients connection"""
-+    with HWSimRadio(use_mlo=True, n_channels=2) as (hapd_radio, hapd_iface), \
-+        HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface), \
-+        HWSimRadio(use_mlo=True) as (wpas_radio1, wpas_iface1):
-+
-+        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
-+        wpas.interface_add(wpas_iface)
-+
-+        wpas1 = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
-+        wpas1.interface_add(wpas_iface1)
-+
-+        hapds = get_mld_devs(hapd_iface=hapd_iface, count=2, prefix=params['prefix'],
-+                             rnr=False)
-+
-+        passphrase = "qwertyuiop-"
-+        ssid = "mld-"
-+
-+        # Connect one client to first AP MLD and verify traffic on both links
-+        wpas.set("sae_pwe", "1")
-+        wpas.connect(ssid+"0", sae_password=passphrase+"0", scan_freq="2412",
-+                     key_mgmt="SAE", ieee80211w="2")
-+
-+        eht_verify_status(wpas, hapds[0], 2412, 20, is_ht=True, mld=True,
-+                          valid_links=3, active_links=3)
-+        eht_verify_wifi_version(wpas)
-+
-+        traffic_test(wpas, hapds[0])
-+        traffic_test(wpas, hapds[1])
-+
-+        # Connect another client to second AP MLD and verify traffic on both links
-+        wpas1.set("sae_pwe", "1")
-+        wpas1.connect(ssid+"1", sae_password=passphrase+"1", scan_freq="2437",
-+                      key_mgmt="SAE", ieee80211w="2")
-+
-+        eht_verify_status(wpas1, hapds[3], 2437, 20, is_ht=True, mld=True,
-+                          valid_links=3, active_links=3)
-+        eht_verify_wifi_version(wpas1)
-+
-+        traffic_test(wpas1, hapds[3])
-+        traffic_test(wpas1, hapds[2])
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0023-backport-hostapd-afcd-add-AFC-daemon-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0023-backport-hostapd-afcd-add-AFC-daemon-support.patch
deleted file mode 100644
index 9c4f7a4..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0023-backport-hostapd-afcd-add-AFC-daemon-support.patch
+++ /dev/null
@@ -1,372 +0,0 @@
-From 84123bd3df810acd8d463a31d519005cfd0cc8d0 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Wed, 20 Mar 2024 07:13:01 +0800
-Subject: [PATCH 023/104] backport: hostapd: afcd: add AFC daemon support
-
-Introduce Automated Frequency Coordination Daemon (AFCD) support
-for UNII-5 and UNII-7 6GHz bands.
-AFCD will be used by hostapd AFC client in order to forward the AFC
-request to the AFC coordinator and decouple AFC connection management
-from hostapd.
-AFC is required for Standard Power Devices (SPDs) to determine a lists
-of channels and EIRP/PSD powers that are available in the 6GHz spectrum.
-AFCD is tested with AFC DUT Test Harness [0].
-
-[0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main
-
-Tested-by: Allen Ye <allen.ye@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
----
- afc/.gitignore |   1 +
- afc/Makefile   |  31 ++++++
- afc/afcd.c     | 292 +++++++++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 324 insertions(+)
- create mode 100644 afc/.gitignore
- create mode 100644 afc/Makefile
- create mode 100644 afc/afcd.c
-
-diff --git a/afc/.gitignore b/afc/.gitignore
-new file mode 100644
-index 000000000..8d8cca905
---- /dev/null
-+++ b/afc/.gitignore
-@@ -0,0 +1 @@
-+afcd
-diff --git a/afc/Makefile b/afc/Makefile
-new file mode 100644
-index 000000000..a83bd01db
---- /dev/null
-+++ b/afc/Makefile
-@@ -0,0 +1,31 @@
-+ALL=afcd
-+
-+include ../src/build.rules
-+
-+CFLAGS += -I../src/utils
-+CFLAGS += -I../src
-+
-+OBJS=afcd.o
-+OBJS += ../src/utils/common.o
-+OBJS += ../src/utils/wpa_debug.o
-+OBJS += ../src/utils/wpabuf.o
-+
-+ifndef CONFIG_OS
-+ifdef CONFIG_NATIVE_WINDOWS
-+CONFIG_OS=win32
-+else
-+CONFIG_OS=unix
-+endif
-+endif
-+OBJS += ../src/utils/os_$(CONFIG_OS).o
-+
-+LIBS += -lcurl
-+
-+_OBJS_VAR := OBJS
-+include ../src/objs.mk
-+afcd: $(OBJS)
-+	$(Q)$(LDO) $(LDFLAGS) -o afcd $(OBJS) $(LIBS)
-+	@$(E) "  LD " $@
-+
-+clean: common-clean
-+	rm -f core *~
-diff --git a/afc/afcd.c b/afc/afcd.c
-new file mode 100644
-index 000000000..f502846c5
---- /dev/null
-+++ b/afc/afcd.c
-@@ -0,0 +1,292 @@
-+/*
-+ * Automated Frequency Coordination Daemon
-+ * Copyright (c) 2024, Lorenzo Bianconi <lorenzo@kernel.org>
-+ *
-+ * This software may be distributed under the terms of the BSD license.
-+ * See README for more details.
-+ */
-+
-+#include <curl/curl.h>
-+#include <sys/un.h>
-+#include <sys/stat.h>
-+
-+#include "utils/includes.h"
-+#include "utils/common.h"
-+
-+#define CURL_TIMEOUT	60
-+#define AFCD_SOCK	"afcd.sock"
-+
-+struct curl_ctx {
-+	char *buf;
-+	size_t buf_len;
-+};
-+
-+static volatile bool exiting;
-+
-+static char *path = "/var/run";
-+static char *bearer_token;
-+static char *url;
-+static int port = 443;
-+
-+
-+static size_t afcd_curl_cb_write(void *ptr, size_t size, size_t nmemb,
-+				 void *userdata)
-+{
-+	struct curl_ctx *ctx = userdata;
-+	char *buf;
-+
-+	buf = os_realloc(ctx->buf, ctx->buf_len + size * nmemb + 1);
-+	if (!buf)
-+		return 0;
-+
-+	ctx->buf = buf;
-+	os_memcpy(buf + ctx->buf_len, ptr, size * nmemb);
-+	buf[ctx->buf_len + size * nmemb] = '\0';
-+	ctx->buf_len += size * nmemb;
-+
-+	return size * nmemb;
-+}
-+
-+
-+static int afcd_send_request(struct curl_ctx *ctx, unsigned char *request)
-+{
-+	struct curl_slist *headers = NULL;
-+	CURL *curl;
-+	int ret;
-+
-+	wpa_printf(MSG_DEBUG, "Sending AFC request to %s", url);
-+
-+	curl_global_init(CURL_GLOBAL_ALL);
-+	curl = curl_easy_init();
-+	if (!curl)
-+		return -ENOMEM;
-+
-+	headers  = curl_slist_append(headers, "Accept: application/json");
-+	headers  = curl_slist_append(headers,
-+				     "Content-Type: application/json");
-+	headers  = curl_slist_append(headers, "charset: utf-8");
-+
-+	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
-+	curl_easy_setopt(curl, CURLOPT_URL, url);
-+	curl_easy_setopt(curl, CURLOPT_PORT, port);
-+	curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
-+	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
-+			 afcd_curl_cb_write);
-+	curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx);
-+	curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcrp/0.1");
-+	curl_easy_setopt(curl, CURLOPT_TIMEOUT, CURL_TIMEOUT);
-+	curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
-+	if (bearer_token)
-+		curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, bearer_token);
-+	curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BEARER);
-+	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
-+	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L);
-+	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request);
-+	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L);
-+
-+	ret = curl_easy_perform(curl);
-+	if (ret != CURLE_OK)
-+		wpa_printf(MSG_ERROR, "curl_easy_perform failed: %s",
-+			   curl_easy_strerror(ret));
-+
-+	curl_easy_cleanup(curl);
-+	curl_global_cleanup();
-+
-+	return ret == CURLE_OK ? 0 : -EINVAL;
-+}
-+
-+
-+static void handle_term(int sig)
-+{
-+	wpa_printf(MSG_ERROR, "Received signal %d", sig);
-+	exiting = true;
-+}
-+
-+
-+static void usage(void)
-+{
-+	wpa_printf(MSG_ERROR,
-+		   "%s:\n"
-+		   "afcd -u<url> [-p<port>][-t<token>][-D<unix-sock dir>][-P<PID file>][-dB]",
-+		   __func__);
-+}
-+
-+
-+#define BUFSIZE		8192
-+static int afcd_server_run(void)
-+{
-+	size_t len = os_strlen(path) + 1 + os_strlen(AFCD_SOCK);
-+	struct sockaddr_un addr = {
-+		.sun_family = AF_UNIX,
-+#ifdef __FreeBSD__
-+		.sun_len = sizeof(addr),
-+#endif /* __FreeBSD__ */
-+	};
-+	int sockfd, ret = 0;
-+	char *fname = NULL;
-+	unsigned char *buf;
-+	fd_set read_set;
-+
-+	if (len >= sizeof(addr.sun_path))
-+		return -EINVAL;
-+
-+	if (mkdir(path, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST)
-+		return -EINVAL;
-+
-+	buf = os_malloc(BUFSIZE);
-+	if (!buf)
-+		return -ENOMEM;
-+
-+	fname = os_malloc(len + 1);
-+	if (!fname) {
-+		ret = -ENOMEM;
-+		goto free_buf;
-+	}
-+
-+	os_snprintf(fname, len + 1, "%s/%s", path, AFCD_SOCK);
-+	fname[len] = '\0';
-+	os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
-+
-+	sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
-+	if (sockfd < 0) {
-+		wpa_printf(MSG_ERROR, "Failed creating socket");
-+		ret = -errno;
-+		goto unlink;
-+	}
-+
-+	if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-+		wpa_printf(MSG_ERROR, "Failed to bind socket");
-+		ret = -errno;
-+		goto close;
-+	}
-+
-+	if (listen(sockfd, 10) < 0) {
-+		wpa_printf(MSG_ERROR, "Failed to listen on socket");
-+		ret = -errno;
-+		goto close;
-+	}
-+
-+	FD_ZERO(&read_set);
-+	while (!exiting) {
-+		socklen_t addr_len = sizeof(addr);
-+		struct sockaddr_in6 c_addr;
-+		struct timeval timeout = {
-+			.tv_sec = 1,
-+		};
-+		struct curl_ctx ctx = {};
-+		int fd;
-+
-+		FD_SET(sockfd, &read_set);
-+		if (select(sockfd + 1, &read_set, NULL, NULL, &timeout) < 0) {
-+			if (errno != EINTR) {
-+				wpa_printf(MSG_ERROR,
-+					   "Select failed on socket");
-+				ret = -errno;
-+				break;
-+			}
-+			continue;
-+		}
-+
-+		if (!FD_ISSET(sockfd, &read_set))
-+			continue;
-+
-+		fd = accept(sockfd, (struct sockaddr *)&c_addr,
-+			    &addr_len);
-+		if (fd < 0) {
-+			if (errno != EINTR) {
-+				wpa_printf(MSG_ERROR,
-+					   "Failed accepting connections");
-+				ret = -errno;
-+				break;
-+			}
-+			continue;
-+		}
-+
-+		os_memset(buf, 0, BUFSIZE);
-+		if (recv(fd, buf, BUFSIZE, 0) <= 0) {
-+			close(fd);
-+			continue;
-+		}
-+
-+		wpa_printf(MSG_DEBUG, "Received request: %s", buf);
-+		if (!afcd_send_request(&ctx, buf)) {
-+			wpa_printf(MSG_DEBUG, "Received reply: %s", ctx.buf);
-+			send(fd, ctx.buf, ctx.buf_len, MSG_NOSIGNAL);
-+			free(ctx.buf);
-+		}
-+		close(fd);
-+	}
-+close:
-+	close(sockfd);
-+unlink:
-+	unlink(fname);
-+	os_free(fname);
-+free_buf:
-+	os_free(buf);
-+
-+	return ret;
-+}
-+
-+
-+int main(int argc, char **argv)
-+{
-+	bool daemonize = false;
-+	char *pid_file = NULL;
-+
-+	if (os_program_init())
-+		return -1;
-+
-+	for (;;) {
-+		int c = getopt(argc, argv, "u:p:t:D:P:hdB");
-+
-+		if (c < 0)
-+			break;
-+
-+		switch (c) {
-+		case 'h':
-+			usage();
-+			return 0;
-+		case 'B':
-+			daemonize = true;
-+			break;
-+		case 'D':
-+			path = optarg;
-+			break;
-+		case 'P':
-+			os_free(pid_file);
-+			pid_file = os_rel2abs_path(optarg);
-+			break;
-+		case 'u':
-+			url = optarg;
-+			break;
-+		case 'p':
-+			port = atoi(optarg);
-+			break;
-+		case 'd':
-+			if (wpa_debug_level > 0)
-+				wpa_debug_level--;
-+			break;
-+		case 't':
-+			bearer_token = optarg;
-+			break;
-+		default:
-+			usage();
-+			return -EINVAL;
-+		}
-+	}
-+
-+	if (!url) {
-+		usage();
-+		return -EINVAL;
-+	}
-+
-+	if (daemonize && os_daemonize(pid_file)) {
-+		wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
-+		return -EINVAL;
-+	}
-+
-+	signal(SIGTERM, handle_term);
-+	signal(SIGINT, handle_term);
-+
-+	return afcd_server_run();
-+}
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0023-mtk-hostapd-Add-amsdu-set-get-ctrl.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0023-mtk-hostapd-Add-amsdu-set-get-ctrl.patch
new file mode 100644
index 0000000..46e779e
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0023-mtk-hostapd-Add-amsdu-set-get-ctrl.patch
@@ -0,0 +1,400 @@
+From 7759dfbe3c937a1945c6df05a109853125dc6653 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Fri, 16 Dec 2022 03:57:11 +0800
+Subject: [PATCH 023/126] mtk: hostapd: Add amsdu set get ctrl
+
+---
+ hostapd/config_file.c             |   9 +++
+ hostapd/ctrl_iface.c              |  26 +++++++
+ hostapd/hostapd_cli.c             |   9 +++
+ src/ap/ap_config.c                |   1 +
+ src/ap/ap_config.h                |   1 +
+ src/ap/ap_drv_ops.c               |  14 ++++
+ src/ap/ap_drv_ops.h               |   2 +
+ src/ap/hostapd.c                  |   2 +
+ src/common/mtk_vendor.h           |  17 ++++-
+ src/drivers/driver.h              |   9 +++
+ src/drivers/driver_nl80211.c      | 114 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   3 +
+ 13 files changed, 207 insertions(+), 1 deletion(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index a5fa97a35..1f57b882f 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5524,6 +5524,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		u8 en = strtol(pos, NULL, 10);
+ 
+ 		conf->dfs_detect_mode = en;
++	} else if (os_strcmp(buf, "amsdu") == 0) {
++		int val = atoi(pos);
++		if (val < 0 || val > 1) {
++			wpa_printf(MSG_ERROR,
++					 "Line %d: invalid amsdu value",
++					 line);
++			return 1;
++		}
++		conf->amsdu = val;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 32d4997b2..7e5733278 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4354,6 +4354,30 @@ hostapd_ctrl_iface_set_offchan_ctrl(struct hostapd_data *hapd, char *cmd,
+ }
+ 
+ 
++static int
++hostapd_ctrl_iface_get_amsdu(struct hostapd_data *hapd, char *buf,
++					 size_t buflen)
++{
++	u8 amsdu;
++	int ret;
++	char *pos, *end;
++
++	pos = buf;
++	end = buf + buflen;
++
++	if (hostapd_drv_amsdu_dump(hapd, &amsdu) == 0) {
++		hapd->iconf->amsdu = amsdu;
++		ret = os_snprintf(pos, end - pos, "[hostapd_cli] AMSDU: %u\n",
++					hapd->iconf->amsdu);
++	}
++
++	if (os_snprintf_error(end - pos, ret))
++		return 0;
++
++	return ret;
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -4980,6 +5004,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 								   reply, reply_size);
+ 	} else if (os_strncmp(buf, "SET_OFFCHAN_CTRL", 16) == 0) {
+ 		reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
++	} else if (os_strncmp(buf, "GET_AMSDU", 9) == 0) {
++		reply_len = hostapd_ctrl_iface_get_amsdu(hapd, reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index ccbf863f6..d934bb0d4 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1698,6 +1698,13 @@ static int hostapd_cli_cmd_get_ibf(struct wpa_ctrl *ctrl, int argc,
+ }
+ 
+ 
++static int hostapd_cli_cmd_get_amsdu(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "GET_AMSDU", 0, NULL, NULL);
++}
++
++
+ struct hostapd_cli_cmd {
+ 	const char *cmd;
+ 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -1930,6 +1937,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+           "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
+ 	{ "get_ibf", hostapd_cli_cmd_get_ibf, NULL,
+ 	  " = show iBF state (enabled/disabled)"},
++	{ "get_amsdu", hostapd_cli_cmd_get_amsdu, NULL,
++		" = show AMSDU state"},
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 08b55d0eb..5dd7f7b2b 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -310,6 +310,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 	conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
+ 	conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
+ 	conf->ibf_enable = IBF_DEFAULT_ENABLE;
++	conf->amsdu = 1;
+ 
+ 	hostapd_set_and_check_bw320_offset(conf, 0);
+ 
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index f7027473e..d0a692750 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1342,6 +1342,7 @@ struct hostapd_config {
+ 	u8 three_wire_enable;
+ 	u8 ibf_enable;
+ 	u8 dfs_detect_mode;
++	u8 amsdu;
+ };
+ 
+ enum three_wire_mode {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 5172f06b9..2685c6f8a 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1336,4 +1336,18 @@ int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable)
+ 	if (!hapd->driver || !hapd->driver->ibf_dump)
+ 		return 0;
+ 	return hapd->driver->ibf_dump(hapd->drv_priv, ibf_enable);
++}
++
++int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->driver->amsdu_ctrl)
++		return 0;
++	return hapd->driver->amsdu_ctrl(hapd->drv_priv, hapd->iconf->amsdu);
++}
++
++int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu)
++{
++	if (!hapd->driver || !hapd->driver->amsdu_dump)
++		return 0;
++	return hapd->driver->amsdu_dump(hapd->drv_priv, amsdu);
+ }
+\ No newline at end of file
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 3633e2ed5..68b26595f 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -161,6 +161,8 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
++int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 7503718c8..6ab3e6043 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2768,6 +2768,8 @@ dfs_offload:
+ 		goto fail;
+ 	if (hostapd_drv_ibf_ctrl(hapd) < 0)
+ 		goto fail;
++	if (hostapd_drv_amsdu_ctrl(hapd) < 0)
++		goto fail;
+ 
+ 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ 		   iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 9811f266e..7b4d7c11a 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -170,7 +170,6 @@ enum mtk_vendor_attr_wireless_ctrl {
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
+-	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
+ 
+@@ -180,6 +179,22 @@ enum mtk_vendor_attr_wireless_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_wireless_dump {
++	MTK_VENDOR_ATTR_WIRELESS_DUMP_UNSPEC,
++
++	MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP,
++	MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX =
++		NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
++};
++
++static const struct nla_policy
++wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
++	[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
++};
++
+ enum mtk_vendor_attr_rfeature_ctrl {
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
+ 
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 97be9e8fb..348cf9c88 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5290,6 +5290,15 @@ struct wpa_driver_ops {
+ 	 *
+ 	 */
+ 	int (*ibf_dump)(void *priv, u8 *ibf_enable);
++
++	/**
++	 * amsdu_ctrl - enable/disable amsdu
++	 * amsdu_dump - get current amsdu status
++	 * @priv: Private driver interface data
++	 *
++	 */
++	int (*amsdu_ctrl)(void *priv, u8 amsdu);
++	int (*amsdu_dump)(void *priv, u8 *amsdu);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index c5c0d1b49..c2a0f1adb 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14518,6 +14518,118 @@ fail:
+ 	return -ENOBUFS;
+ }
+ 
++static int nl80211_enable_amsdu(void *priv, u8 amsdu)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_wireless_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting ap wireless control");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU, amsdu);
++
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to set amsdu. ret=%d (%s)", ret, strerror(-ret));
++	}
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
++static int dump_amsdu_handler(struct nl_msg *msg, void *arg)
++{
++	u8 *amsdu = (u8 *) arg;
++	struct nlattr *tb[NL80211_ATTR_MAX + 1];
++	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX + 1];
++	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++	struct nlattr *nl_vend, *attr_amsdu;
++
++	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++			genlmsg_attrlen(gnlh, 0), NULL);
++
++	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++	if (!nl_vend)
++		return NL_SKIP;
++
++	nla_parse(tb_vendor, MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX,
++			nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++	attr_amsdu = tb_vendor[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU];
++	if (!attr_amsdu ){
++		wpa_printf(MSG_ERROR, "nl80211: cannot find vendor attributes");
++		return NL_SKIP;
++	}
++
++	*amsdu = nla_get_u8(attr_amsdu);
++
++	return NL_SKIP;
++}
++
++static int
++nl80211_dump_amsdu(void *priv, u8 *amsdu)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_wireless_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++				 "nl80211: Driver does not support ap_wireless control");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	nla_nest_end(msg, data);
++
++	ret = send_and_recv_resp(drv, msg, dump_amsdu_handler, amsdu);
++
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to dump amsdu. ret=%d (%s)", ret, strerror(-ret));
++	}
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+ 	.desc = "Linux nl80211/cfg80211",
+@@ -14687,4 +14799,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.three_wire_ctrl = nl80211_enable_three_wire,
+ 	.ibf_ctrl = nl80211_ibf_enable,
+ 	.ibf_dump = nl80211_ibf_dump,
++	.amsdu_ctrl = nl80211_enable_amsdu,
++	.amsdu_dump = nl80211_dump_amsdu,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 1432eeda8..5aa813e26 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -204,6 +204,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_mu_vendor_cmd_avail:1;
+ 	unsigned int mtk_3wire_vendor_cmd_avail:1;
+ 	unsigned int mtk_ibf_vendor_cmd_avail:1;
++	unsigned int mtk_wireless_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 670588dd2..4e2afb7f2 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1153,6 +1153,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL:
+ 					drv->mtk_ibf_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL:
++					drv->mtk_wireless_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0024-backport-hostapd-export-hostapd_is_usable_chans-util.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0024-backport-hostapd-export-hostapd_is_usable_chans-util.patch
deleted file mode 100644
index aac2145..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0024-backport-hostapd-export-hostapd_is_usable_chans-util.patch
+++ /dev/null
@@ -1,55 +0,0 @@
-From 524c84524695034b8d531d70b546d5479d59641f Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Wed, 20 Mar 2024 07:17:31 +0800
-Subject: [PATCH 024/104] backport: hostapd: export hostapd_is_usable_chans
- utility routine
-
-This is a preliminary patch to introduce AFC support.
-
-Tested-by: Allen Ye <allen.ye@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
----
- src/ap/hw_features.c | 2 +-
- src/ap/hw_features.h | 6 ++++++
- 2 files changed, 7 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
-index fd401d78a..e652d7504 100644
---- a/src/ap/hw_features.c
-+++ b/src/ap/hw_features.c
-@@ -995,7 +995,7 @@ static bool hostapd_is_usable_punct_bitmap(struct hostapd_iface *iface)
-  * 0 = not usable
-  * -1 = not currently usable due to 6 GHz NO-IR
-  */
--static int hostapd_is_usable_chans(struct hostapd_iface *iface)
-+int hostapd_is_usable_chans(struct hostapd_iface *iface)
- {
- 	int secondary_freq;
- 	struct hostapd_channel_data *pri_chan;
-diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
-index c682c6d20..eeffb1abd 100644
---- a/src/ap/hw_features.h
-+++ b/src/ap/hw_features.h
-@@ -30,6 +30,7 @@ void hostapd_stop_setup_timers(struct hostapd_iface *iface);
- int hostapd_hw_skip_mode(struct hostapd_iface *iface,
- 			 struct hostapd_hw_modes *mode);
- int hostapd_determine_mode(struct hostapd_iface *iface);
-+int hostapd_is_usable_chans(struct hostapd_iface *iface);
- #else /* NEED_AP_MLME */
- static inline void
- hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
-@@ -103,6 +104,11 @@ static inline int hostapd_determine_mode(struct hostapd_iface *iface)
- 	return 0;
- }
- 
-+static inline int hostapd_is_usable_chans(struct hostapd_iface *iface)
-+{
-+	return 1;
-+}
-+
- #endif /* NEED_AP_MLME */
- 
- #endif /* HW_FEATURES_H */
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0024-mtk-hostapd-Add-he_ldpc-configuration.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0024-mtk-hostapd-Add-he_ldpc-configuration.patch
new file mode 100644
index 0000000..43ba4db
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0024-mtk-hostapd-Add-he_ldpc-configuration.patch
@@ -0,0 +1,102 @@
+From 305f9748549189ce802544c0923446ca6a53ae1b Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Thu, 12 Jan 2023 15:18:19 +0800
+Subject: [PATCH 024/126] mtk: hostapd: Add he_ldpc configuration
+
+---
+ hostapd/config_file.c        | 2 ++
+ hostapd/hostapd.conf         | 5 +++++
+ src/ap/ap_config.c           | 1 +
+ src/ap/ap_config.h           | 1 +
+ src/ap/ieee802_11_he.c       | 7 +++++++
+ src/common/ieee802_11_defs.h | 3 +++
+ 6 files changed, 19 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 1f57b882f..dc05738db 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4000,6 +4000,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		conf->he_phy_capab.he_su_beamformee = atoi(pos);
+ 	} else if (os_strcmp(buf, "he_mu_beamformer") == 0) {
+ 		conf->he_phy_capab.he_mu_beamformer = atoi(pos);
++	} else if (os_strcmp(buf, "he_ldpc") == 0) {
++		conf->he_phy_capab.he_ldpc = atoi(pos);
+ 	} else if (os_strcmp(buf, "he_bss_color") == 0) {
+ 		conf->he_op.he_bss_color = atoi(pos) & 0x3f;
+ 		conf->he_op.he_bss_color_disabled = 0;
+diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
+index 56442c69d..118754800 100644
+--- a/hostapd/hostapd.conf
++++ b/hostapd/hostapd.conf
+@@ -858,6 +858,11 @@ wmm_ac_vo_acm=0
+ # 1 = supported
+ #he_mu_beamformer=1
+ 
++#he_ldpc: HE LDPC support
++# 0 = not supported
++# 1 = supported (default)
++#he_ldpc=1
++
+ # he_bss_color: BSS color (1-63)
+ #he_bss_color=1
+ 
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 5dd7f7b2b..3a12de3cd 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -275,6 +275,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ #endif /* CONFIG_ACS */
+ 
+ #ifdef CONFIG_IEEE80211AX
++	conf->he_phy_capab.he_ldpc = 1;
+ 	conf->he_op.he_rts_threshold = HE_OPERATION_RTS_THRESHOLD_MASK >>
+ 		HE_OPERATION_RTS_THRESHOLD_OFFSET;
+ 	/* Set default basic MCS/NSS set to single stream MCS 0-7 */
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index d0a692750..f90f4d554 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1035,6 +1035,7 @@ struct hostapd_bss_config {
+  * struct he_phy_capabilities_info - HE PHY capabilities
+  */
+ struct he_phy_capabilities_info {
++	bool he_ldpc;
+ 	bool he_su_beamformer;
+ 	bool he_su_beamformee;
+ 	bool he_mu_beamformer;
+diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
+index a2deda6c4..3c6ee72fe 100644
+--- a/src/ap/ieee802_11_he.c
++++ b/src/ap/ieee802_11_he.c
+@@ -139,6 +139,13 @@ u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
+ 		os_memcpy(&cap->optional[mcs_nss_size],
+ 			  mode->he_capab[opmode].ppet,  ppet_size);
+ 
++	if (hapd->iface->conf->he_phy_capab.he_ldpc)
++		cap->he_phy_capab_info[HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX] |=
++			HE_PHYCAP_LDPC_CODING_IN_PAYLOAD;
++	else
++		cap->he_phy_capab_info[HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX] &=
++			~HE_PHYCAP_LDPC_CODING_IN_PAYLOAD;
++
+ 	if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
+ 		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
+ 			HE_PHYCAP_SU_BEAMFORMER_CAPAB;
+diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
+index 269b1cf97..c380d0c7e 100644
+--- a/src/common/ieee802_11_defs.h
++++ b/src/common/ieee802_11_defs.h
+@@ -2461,6 +2461,9 @@ struct ieee80211_spatial_reuse {
+ #define HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G	((u8) BIT(3))
+ #define HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G	((u8) BIT(4))
+ 
++#define HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX	1
++#define HE_PHYCAP_LDPC_CODING_IN_PAYLOAD	((u8) BIT(5))
++
+ #define HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX	3
+ #define HE_PHYCAP_SU_BEAMFORMER_CAPAB		((u8) BIT(7))
+ #define HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX	4
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0025-backport-hostapd-ap-add-AFC-client-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0025-backport-hostapd-ap-add-AFC-client-support.patch
deleted file mode 100644
index 357ca69..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0025-backport-hostapd-ap-add-AFC-client-support.patch
+++ /dev/null
@@ -1,1542 +0,0 @@
-From c635af2f526c7dc7a862e5c6fed5f2015d8e85b6 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Wed, 20 Mar 2024 07:18:37 +0800
-Subject: [PATCH 025/104] backport: hostapd: ap: add AFC client support
-
-Introduce Automated Frequency Coordination (AFC) support for UNII-5 and
-UNII-7 6GHz bands.
-AFC client will connect to AFCD providing AP related parameter for AFC
-coordinator (e.g. geolocation, supported frequencies, ..).
-AFC is required for Standard Power Devices (SPDs) to determine a lists
-of channels and EIRP/PSD powers that are available in the 6GHz spectrum.
-AFC hostapd client is tested with AFC DUT Test Harness [0].
-
-[0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main
-
-Tested-by: Allen Ye <allen.ye@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
----
- hostapd/Makefile      |   6 +
- hostapd/config_file.c | 262 ++++++++++++
- hostapd/defconfig     |   3 +
- hostapd/hostapd.conf  |  42 ++
- src/ap/afc.c          | 918 ++++++++++++++++++++++++++++++++++++++++++
- src/ap/ap_config.c    |  16 +
- src/ap/ap_config.h    |  47 +++
- src/ap/hostapd.c      |  60 +++
- src/ap/hostapd.h      |  29 ++
- src/ap/hw_features.c  |   2 +
- 10 files changed, 1385 insertions(+)
- create mode 100644 src/ap/afc.c
-
-diff --git a/hostapd/Makefile b/hostapd/Makefile
-index b3cb68673..405e05e5f 100644
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -103,6 +103,12 @@ CFLAGS += -DCONFIG_TAXONOMY
- OBJS += ../src/ap/taxonomy.o
- endif
- 
-+ifdef CONFIG_AFC
-+CFLAGS += -DCONFIG_AFC
-+OBJS += ../src/ap/afc.o
-+LIBS += -ljson-c
-+endif
-+
- ifdef CONFIG_MODULE_TESTS
- CFLAGS += -DCONFIG_MODULE_TESTS
- OBJS += hapd_module_tests.o
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 56b2df3ae..261905368 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -2436,6 +2436,191 @@ static int get_u16(const char *pos, int line, u16 *ret_val)
- #endif /* CONFIG_IEEE80211BE */
- 
- 
-+#ifdef CONFIG_AFC
-+static int hostapd_afc_parse_cert_ids(struct hostapd_config *conf, char *pos)
-+{
-+	struct cert_id *c = NULL;
-+	int i, count = 0;
-+
-+	while (pos && pos[0]) {
-+		char *p;
-+
-+		c = os_realloc_array(c, count + 1, sizeof(*c));
-+		if (!c)
-+			return -ENOMEM;
-+
-+		i = count;
-+		count++;
-+
-+		p = os_strchr(pos, ':');
-+		if (!p)
-+			goto error;
-+
-+		*p++ = '\0';
-+		if (!p || !p[0])
-+			goto error;
-+
-+		c[i].rulset = os_malloc(os_strlen(pos) + 1);
-+		if (!c[i].rulset)
-+			goto error;
-+
-+		os_strlcpy(c[i].rulset, pos, os_strlen(pos) + 1);
-+		pos = p;
-+		p = os_strchr(pos, ',');
-+		if (p)
-+			*p++ = '\0';
-+
-+		c[i].id = os_malloc(os_strlen(pos) + 1);
-+		if (!c[i].id)
-+			goto error;
-+
-+		os_strlcpy(c[i].id, pos, os_strlen(pos) + 1);
-+		pos = p;
-+	}
-+
-+	conf->afc.n_cert_ids = count;
-+	conf->afc.cert_ids = c;
-+
-+	return 0;
-+
-+error:
-+	for (i = 0; i < count; i++) {
-+		os_free(c[i].rulset);
-+		os_free(c[i].id);
-+	}
-+	os_free(c);
-+
-+	return -ENOMEM;
-+}
-+
-+
-+static int hostapd_afc_parse_position_data(struct afc_linear_polygon **data,
-+					   unsigned int *n_linear_polygon_data,
-+					   char *pos)
-+{
-+	struct afc_linear_polygon *d = NULL;
-+	int i, count = 0;
-+
-+	while (pos && pos[0]) {
-+		char *p, *end;
-+
-+		d = os_realloc_array(d, count + 1, sizeof(*d));
-+		if (!d)
-+			return -ENOMEM;
-+
-+		i = count;
-+		count++;
-+
-+		p = os_strchr(pos, ':');
-+		if (!p)
-+			goto error;
-+
-+		*p++ = '\0';
-+		if (!p || !p[0])
-+			goto error;
-+
-+		d[i].longitude = strtod(pos, &end);
-+		if (*end)
-+			goto error;
-+
-+		pos = p;
-+		p = os_strchr(pos, ',');
-+		if (p)
-+			*p++ = '\0';
-+
-+		d[i].latitude = strtod(pos, &end);
-+		if (*end)
-+			goto error;
-+
-+		pos = p;
-+	}
-+
-+	*n_linear_polygon_data = count;
-+	*data = d;
-+
-+	return 0;
-+
-+error:
-+	os_free(d);
-+	return -ENOMEM;
-+}
-+
-+
-+static int hostapd_afc_parse_freq_range(struct hostapd_config *conf, char *pos)
-+{
-+	struct afc_freq_range *f = NULL;
-+	int i, count = 0;
-+
-+	while (pos && pos[0]) {
-+		char *p;
-+
-+		f = os_realloc_array(f, count + 1, sizeof(*f));
-+		if (!f)
-+			return -ENOMEM;
-+
-+		i = count;
-+		count++;
-+
-+		p = os_strchr(pos, ':');
-+		if (!p)
-+			goto error;
-+
-+		*p++ = '\0';
-+		if (!p || !p[0])
-+			goto error;
-+
-+		f[i].low_freq = atoi(pos);
-+		pos = p;
-+		p = os_strchr(pos, ',');
-+		if (p)
-+			*p++ = '\0';
-+
-+		f[i].high_freq = atoi(pos);
-+		pos = p;
-+	}
-+
-+	conf->afc.n_freq_range = count;
-+	conf->afc.freq_range = f;
-+
-+	return 0;
-+
-+error:
-+	os_free(f);
-+	return -ENOMEM;
-+}
-+
-+
-+static int hostapd_afc_parse_op_class(struct hostapd_config *conf, char *pos)
-+{
-+	unsigned int *oc = NULL;
-+	int i, count = 0;
-+
-+	while (pos && pos[0]) {
-+		char *p;
-+
-+		oc = os_realloc_array(oc, count + 1, sizeof(*oc));
-+		if (!oc)
-+			return -ENOMEM;
-+
-+		i = count;
-+		count++;
-+
-+		p = os_strchr(pos, ',');
-+		if (p)
-+			*p++ = '\0';
-+
-+		oc[i] = atoi(pos);
-+		pos = p;
-+	}
-+
-+	conf->afc.n_op_class = count;
-+	conf->afc.op_class = oc;
-+
-+	return 0;
-+}
-+#endif /* CONFIG_AFC */
-+
-+
- static int hostapd_config_fill(struct hostapd_config *conf,
- 			       struct hostapd_bss_config *bss,
- 			       const char *buf, char *pos, int line)
-@@ -3862,6 +4047,83 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 			return 1;
- 		}
- 		bss->unsol_bcast_probe_resp_interval = val;
-+#ifdef CONFIG_AFC
-+	} else if (os_strcmp(buf, "afcd_sock") == 0) {
-+		conf->afc.socket = os_malloc(os_strlen(pos) + 1);
-+		if (!conf->afc.socket)
-+			return 1;
-+
-+		os_strlcpy(conf->afc.socket, pos, os_strlen(pos) + 1);
-+	} else if (os_strcmp(buf, "afc_request_version") == 0) {
-+		conf->afc.request.version = os_malloc(os_strlen(pos) + 1);
-+		if (!conf->afc.request.version)
-+			return 1;
-+
-+		os_strlcpy(conf->afc.request.version, pos, os_strlen(pos) + 1);
-+	} else if (os_strcmp(buf, "afc_request_id") == 0) {
-+		conf->afc.request.id = os_malloc(os_strlen(pos) + 1);
-+		if (!conf->afc.request.id)
-+			return 1;
-+
-+		os_strlcpy(conf->afc.request.id, pos, os_strlen(pos) + 1);
-+	} else if (os_strcmp(buf, "afc_serial_number") == 0) {
-+		conf->afc.request.sn = os_malloc(os_strlen(pos) + 1);
-+		if (!conf->afc.request.sn)
-+			return 1;
-+
-+		os_strlcpy(conf->afc.request.sn, pos, os_strlen(pos) + 1);
-+	} else if (os_strcmp(buf, "afc_cert_ids") == 0) {
-+		if (hostapd_afc_parse_cert_ids(conf, pos))
-+			return 1;
-+	} else if (os_strcmp(buf, "afc_location_type") == 0) {
-+		conf->afc.location.type = atoi(pos);
-+		if (conf->afc.location.type != ELLIPSE &&
-+		    conf->afc.location.type != LINEAR_POLYGON &&
-+		    conf->afc.location.type != RADIAL_POLYGON)
-+			return 1;
-+	} else if (os_strcmp(buf, "afc_linear_polygon") == 0) {
-+		if (hostapd_afc_parse_position_data(
-+			&conf->afc.location.linear_polygon_data,
-+			&conf->afc.location.n_linear_polygon_data,
-+			pos))
-+			return 1;
-+	} else if (os_strcmp(buf, "afc_radial_polygon") == 0) {
-+		if (hostapd_afc_parse_position_data(
-+			(struct afc_linear_polygon **)
-+			&conf->afc.location.radial_polygon_data,
-+			&conf->afc.location.n_radial_polygon_data,
-+			pos))
-+			return 1;
-+	} else if (os_strcmp(buf, "afc_major_axis") == 0) {
-+		conf->afc.location.major_axis = atoi(pos);
-+	} else if (os_strcmp(buf, "afc_minor_axis") == 0) {
-+		conf->afc.location.minor_axis = atoi(pos);
-+	} else if (os_strcmp(buf, "afc_orientation") == 0) {
-+		conf->afc.location.orientation = atoi(pos);
-+	} else if (os_strcmp(buf, "afc_height") == 0) {
-+		char *end;
-+
-+		conf->afc.location.height = strtod(pos, &end);
-+		if (*end)
-+			return 1;
-+	} else if (os_strcmp(buf, "afc_height_type") == 0) {
-+		conf->afc.location.height_type = os_malloc(os_strlen(pos) + 1);
-+		if (!conf->afc.location.height_type)
-+			return 1;
-+
-+		os_strlcpy(conf->afc.location.height_type, pos,
-+			   os_strlen(pos) + 1);
-+	} else if (os_strcmp(buf, "afc_vertical_tolerance") == 0) {
-+		conf->afc.location.vertical_tolerance = atoi(pos);
-+	} else if (os_strcmp(buf, "afc_min_power") == 0) {
-+		conf->afc.min_power = atoi(pos);
-+	} else if (os_strcmp(buf, "afc_freq_range") == 0) {
-+		if (hostapd_afc_parse_freq_range(conf, pos))
-+			return 1;
-+	} else if (os_strcmp(buf, "afc_op_class") == 0) {
-+		if (hostapd_afc_parse_op_class(conf, pos))
-+			return 1;
-+#endif /* CONFIG_AFC */
- 	} else if (os_strcmp(buf, "mbssid") == 0) {
- 		int mbssid = atoi(pos);
- 		if (mbssid < 0 || mbssid > ENHANCED_MBSSID_ENABLED) {
-diff --git a/hostapd/defconfig b/hostapd/defconfig
-index 550db697b..66bf894eb 100644
---- a/hostapd/defconfig
-+++ b/hostapd/defconfig
-@@ -425,3 +425,6 @@ CONFIG_DPP2=y
- 
- # Wi-Fi Aware unsynchronized service discovery (NAN USD)
- #CONFIG_NAN_USD=y
-+
-+# Enable Automated Frequency Coordination for 6GHz outdoor
-+#CONFIG_AFC=y
-diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
-index d80abcac0..0d10998af 100644
---- a/hostapd/hostapd.conf
-+++ b/hostapd/hostapd.conf
-@@ -1005,6 +1005,48 @@ wmm_ac_vo_acm=0
- # Valid range: 0..20 TUs; default is 0 (disabled)
- #unsol_bcast_probe_resp_interval=0
- 
-+##### Automated Frequency Coordination for 6GHz UNII-5 and UNII-7 bands #######
-+
-+# AFC daemon connection socket
-+#afcd_sock=/var/run/afcd.sock
-+
-+# AFC request identification parameters
-+#afc_request_version=1.1
-+#afc_request_id=11235813
-+#afc_serial_number=abcdefg
-+#afc_cert_ids=US_47_CFR_PART_15_SUBPART_E:CID000
-+#
-+# AFC location type:
-+# 0 = ellipse
-+# 1 = linear polygon
-+# 2 = radial polygon
-+#afc_location_type=0
-+#
-+# AFC ellipse or linear polygon coordinations
-+#afc_linear_polygon=-122.984157:37.425056
-+#
-+# AFC radial polygon coordinations
-+#afc_radial_polygon=118.8:92.76,76.44:87.456,98.56:123.33
-+#
-+# AFC ellipse major/minor axis and orientation
-+#afc_major_axis=100
-+#afc_minor_axis=50
-+#afc_orientation=70
-+#
-+# AFC device elevation parameters
-+#afc_height=3.0
-+#afc_height_type=AGL
-+#afc_vertical_tolerance=7
-+#
-+# AFC minimum desired TX power (dbm)
-+#afc_min_power=24
-+#
-+# AFC request frequency ranges
-+#afc_freq_range=5925:6425,6525:6875
-+#
-+# AFC request operation classes
-+#afc_op_class=131,132,133,134,136
-+
- ##### IEEE 802.11be related configuration #####################################
- 
- #ieee80211be: Whether IEEE 802.11be (EHT) is enabled
-diff --git a/src/ap/afc.c b/src/ap/afc.c
-new file mode 100644
-index 000000000..c75d5d582
---- /dev/null
-+++ b/src/ap/afc.c
-@@ -0,0 +1,918 @@
-+/*
-+ * Automated Frequency Coordination
-+ * Copyright (c) 2024, Lorenzo Bianconi <lorenzo@kernel.org>
-+ *
-+ * This software may be distributed under the terms of the BSD license.
-+ * See README for more details.
-+ */
-+
-+#include <json-c/json.h>
-+#include <sys/un.h>
-+#include <time.h>
-+
-+#include "utils/includes.h"
-+#include "utils/common.h"
-+#include "utils/eloop.h"
-+#include "hostapd.h"
-+#include "acs.h"
-+#include "hw_features.h"
-+
-+#define HOSTAPD_AFC_RETRY_TIMEOUT	180
-+#define HOSTAPD_AFC_TIMEOUT		86400 /* 24h */
-+#define HOSTAPD_AFC_BUFSIZE		4096
-+
-+static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx);
-+
-+
-+static struct json_object *
-+hostapd_afc_build_location_request(struct hostapd_iface *iface)
-+{
-+	struct json_object *location_obj, *center_obj, *ellipse_obj;
-+	struct json_object *elevation_obj, *str_obj;
-+	struct hostapd_config *iconf = iface->conf;
-+	bool is_ap_indoor = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type);
-+
-+	location_obj = json_object_new_object();
-+	if (!location_obj)
-+		return NULL;
-+
-+	if (iconf->afc.location.type != LINEAR_POLYGON) {
-+		struct afc_linear_polygon *lp =
-+			&iconf->afc.location.linear_polygon_data[0];
-+
-+		ellipse_obj = json_object_new_object();
-+		if (!ellipse_obj)
-+			goto error;
-+
-+		center_obj = json_object_new_object();
-+		if (!center_obj)
-+			goto error;
-+
-+		json_object_object_add(ellipse_obj, "center", center_obj);
-+
-+		str_obj = json_object_new_double(lp->longitude);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(center_obj, "longitude", str_obj);
-+		str_obj = json_object_new_double(lp->latitude);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(center_obj, "latitude", str_obj);
-+
-+	}
-+
-+	switch (iconf->afc.location.type) {
-+	case LINEAR_POLYGON: {
-+		struct json_object *outer_boundary_obj;
-+		int i;
-+
-+		outer_boundary_obj = json_object_new_object();
-+		if (!outer_boundary_obj)
-+			goto error;
-+
-+		json_object_object_add(location_obj, "linearPolygon",
-+				       outer_boundary_obj);
-+		ellipse_obj = json_object_new_array();
-+		if (!ellipse_obj)
-+			goto error;
-+
-+		json_object_object_add(outer_boundary_obj, "outerBoundary",
-+				       ellipse_obj);
-+		for (i = 0;
-+		     i < iconf->afc.location.n_linear_polygon_data; i++) {
-+			struct afc_linear_polygon *lp =
-+				&iconf->afc.location.linear_polygon_data[i];
-+
-+			center_obj = json_object_new_object();
-+			if (!center_obj)
-+				goto error;
-+
-+			json_object_array_add(ellipse_obj, center_obj);
-+			str_obj = json_object_new_double(lp->longitude);
-+			if (!str_obj)
-+				goto error;
-+
-+			json_object_object_add(center_obj, "longitude",
-+					       str_obj);
-+			str_obj = json_object_new_double(lp->latitude);
-+			if (!str_obj)
-+				goto error;
-+
-+			json_object_object_add(center_obj, "latitude",
-+					       str_obj);
-+		}
-+		break;
-+	}
-+	case RADIAL_POLYGON: {
-+		struct json_object *outer_boundary_obj;
-+		int i;
-+
-+		json_object_object_add(location_obj, "radialPolygon",
-+				       ellipse_obj);
-+
-+		outer_boundary_obj = json_object_new_array();
-+		if (!outer_boundary_obj)
-+			goto error;
-+
-+		json_object_object_add(ellipse_obj, "outerBoundary",
-+				       outer_boundary_obj);
-+		for (i = 0;
-+		     i < iconf->afc.location.n_radial_polygon_data; i++) {
-+			struct afc_radial_polygon *rp =
-+				&iconf->afc.location.radial_polygon_data[i];
-+			struct json_object *angle_obj;
-+
-+			angle_obj = json_object_new_object();
-+			if (!angle_obj)
-+				goto error;
-+
-+			json_object_array_add(outer_boundary_obj, angle_obj);
-+
-+			str_obj = json_object_new_double(rp->angle);
-+			if (!str_obj)
-+				goto error;
-+
-+			json_object_object_add(angle_obj, "angle", str_obj);
-+			str_obj = json_object_new_double(rp->length);
-+			if (!str_obj)
-+				goto error;
-+
-+			json_object_object_add(angle_obj, "length", str_obj);
-+		}
-+		break;
-+	}
-+	case ELLIPSE:
-+	default:
-+		json_object_object_add(location_obj, "ellipse", ellipse_obj);
-+
-+		str_obj = json_object_new_int(iconf->afc.location.major_axis);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(ellipse_obj, "majorAxis", str_obj);
-+		str_obj = json_object_new_int(iconf->afc.location.minor_axis);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(ellipse_obj, "minorAxis", str_obj);
-+		str_obj = json_object_new_int(iconf->afc.location.orientation);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(ellipse_obj, "orientation", str_obj);
-+		break;
-+	}
-+
-+	elevation_obj = json_object_new_object();
-+	if (!elevation_obj)
-+		goto error;
-+
-+	json_object_object_add(location_obj, "elevation",
-+			       elevation_obj);
-+	str_obj = json_object_new_double(iconf->afc.location.height);
-+	if (!str_obj)
-+		goto error;
-+
-+	json_object_object_add(elevation_obj, "height", str_obj);
-+	str_obj = json_object_new_string(iconf->afc.location.height_type);
-+	if (!str_obj)
-+		goto error;
-+
-+	json_object_object_add(elevation_obj, "heightType", str_obj);
-+	str_obj = json_object_new_int(iconf->afc.location.vertical_tolerance);
-+	if (!str_obj)
-+		goto error;
-+
-+	json_object_object_add(elevation_obj, "verticalUncertainty",
-+			       str_obj);
-+	str_obj = json_object_new_int(is_ap_indoor);
-+	if (!str_obj)
-+		goto error;
-+
-+	json_object_object_add(location_obj, "indoorDeployment", str_obj);
-+
-+	return location_obj;
-+
-+error:
-+	json_object_put(location_obj);
-+	return NULL;
-+}
-+
-+
-+static struct json_object * hostapd_afc_get_opclass_chan_list(u8 op_class)
-+{
-+	struct json_object *chan_list_obj, *str_obj;
-+	const struct oper_class_map *oper_class;
-+	int chan_offset, chan;
-+
-+	oper_class = get_oper_class(NULL, op_class);
-+	if (!oper_class)
-+		return NULL;
-+
-+	chan_list_obj = json_object_new_array();
-+	if (!chan_list_obj)
-+		return NULL;
-+
-+	switch (op_class) {
-+	case 132: /*  40MHz */
-+		chan_offset = 2;
-+		break;
-+	case 133: /*  80MHz */
-+		chan_offset = 6;
-+		break;
-+	case 134: /* 160MHz */
-+		chan_offset = 14;
-+		break;
-+	default:
-+		chan_offset = 0;
-+		break;
-+	}
-+
-+	for (chan = oper_class->min_chan; chan <= oper_class->max_chan;
-+	     chan += oper_class->inc) {
-+		str_obj = json_object_new_int(chan + chan_offset);
-+		if (!str_obj) {
-+			json_object_put(chan_list_obj);
-+			return NULL;
-+		}
-+		json_object_array_add(chan_list_obj, str_obj);
-+	}
-+
-+	return chan_list_obj;
-+}
-+
-+
-+static struct json_object *
-+hostapd_afc_build_req_chan_list(struct hostapd_iface *iface)
-+{
-+	struct json_object *op_class_list_obj, *str_obj;
-+	struct hostapd_config *iconf = iface->conf;
-+	int i;
-+
-+	op_class_list_obj = json_object_new_array();
-+	if (!op_class_list_obj)
-+		return NULL;
-+
-+	for (i = 0; i < iconf->afc.n_op_class; i++) {
-+		struct json_object *op_class_obj, *chan_list_obj;
-+		u8 op_class = iconf->afc.op_class[i];
-+
-+		if (!is_6ghz_op_class(op_class))
-+			continue;
-+
-+		op_class_obj = json_object_new_object();
-+		if (!op_class_obj)
-+			goto error;
-+
-+		json_object_array_add(op_class_list_obj, op_class_obj);
-+		str_obj = json_object_new_int(op_class);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(op_class_obj, "globalOperatingClass",
-+				       str_obj);
-+
-+		chan_list_obj = hostapd_afc_get_opclass_chan_list(op_class);
-+		if (!chan_list_obj)
-+			goto error;
-+
-+		json_object_object_add(op_class_obj, "channelCfi",
-+				       chan_list_obj);
-+	}
-+
-+	return op_class_list_obj;
-+
-+error:
-+	json_object_put(op_class_list_obj);
-+	return NULL;
-+}
-+
-+
-+static struct json_object *
-+hostapd_afc_build_request(struct hostapd_iface *iface)
-+{
-+	struct json_object *l1_obj, *l2_obj, *la1_obj, *la2_obj;
-+	struct json_object *s2_obj, *str_obj, *location_obj;
-+	struct hostapd_config *iconf = iface->conf;
-+	struct json_object *op_class_list_obj;
-+	int i;
-+
-+	l1_obj = json_object_new_object();
-+	if (!l1_obj)
-+		return NULL;
-+
-+	if (iconf->afc.request.version) {
-+		str_obj = json_object_new_string(iconf->afc.request.version);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(l1_obj, "version", str_obj);
-+	}
-+
-+	la1_obj = json_object_new_array();
-+	if (!la1_obj)
-+		goto error;
-+
-+	json_object_object_add(l1_obj, "availableSpectrumInquiryRequests",
-+			       la1_obj);
-+	l2_obj = json_object_new_object();
-+	if (!l2_obj)
-+		goto error;
-+
-+	json_object_array_add(la1_obj, l2_obj);
-+	if (iconf->afc.request.id) {
-+		str_obj = json_object_new_string(iconf->afc.request.id);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(l2_obj, "requestId", str_obj);
-+	}
-+
-+	s2_obj = json_object_new_object();
-+	if (!s2_obj)
-+		goto error;
-+
-+	json_object_object_add(l2_obj, "deviceDescriptor", s2_obj);
-+	if (iconf->afc.request.sn) {
-+		str_obj = json_object_new_string(iconf->afc.request.sn);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(s2_obj, "serialNumber", str_obj);
-+	}
-+
-+	la2_obj = json_object_new_array();
-+	if (!la2_obj)
-+		goto error;
-+
-+	json_object_object_add(s2_obj, "certificationId", la2_obj);
-+	for (i = 0; i < iconf->afc.n_cert_ids; i++) {
-+		struct json_object *obj;
-+
-+		obj = json_object_new_object();
-+		if (!obj)
-+			goto error;
-+
-+		json_object_array_add(la2_obj, obj);
-+		str_obj =
-+			json_object_new_string(iconf->afc.cert_ids[i].rulset);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(obj, "rulesetId", str_obj);
-+		str_obj = json_object_new_string(iconf->afc.cert_ids[i].id);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(obj, "id", str_obj);
-+	}
-+
-+	location_obj = hostapd_afc_build_location_request(iface);
-+	if (!location_obj)
-+		goto error;
-+
-+	json_object_object_add(l2_obj, "location", location_obj);
-+	str_obj = json_object_new_int(iconf->afc.min_power);
-+	if (!str_obj)
-+		goto error;
-+
-+	json_object_object_add(l2_obj, "minDesiredPower", str_obj);
-+
-+	if (iconf->afc.n_freq_range) {
-+		struct json_object *freq_obj;
-+
-+		freq_obj = json_object_new_array();
-+		if (!freq_obj)
-+			goto error;
-+
-+		json_object_object_add(l2_obj, "inquiredFrequencyRange",
-+				       freq_obj);
-+		for (i = 0; i < iconf->afc.n_freq_range; i++) {
-+			struct afc_freq_range *fr = &iconf->afc.freq_range[i];
-+			struct json_object *obj;
-+
-+			obj = json_object_new_object();
-+			if (!obj)
-+				goto error;
-+
-+			json_object_array_add(freq_obj, obj);
-+			str_obj = json_object_new_int(fr->low_freq);
-+			if (!str_obj)
-+				goto error;
-+
-+			json_object_object_add(obj, "lowFrequency", str_obj);
-+			str_obj = json_object_new_int(fr->high_freq);
-+			if (!str_obj)
-+				goto error;
-+
-+			json_object_object_add(obj, "highFrequency", str_obj);
-+		}
-+	}
-+
-+	op_class_list_obj = hostapd_afc_build_req_chan_list(iface);
-+	if (!op_class_list_obj)
-+		goto error;
-+
-+	json_object_object_add(l2_obj, "inquiredChannels", op_class_list_obj);
-+
-+	wpa_printf(MSG_DEBUG, "Pending AFC request: %s",
-+		   json_object_get_string(l1_obj));
-+
-+	return l1_obj;
-+
-+error:
-+	json_object_put(l1_obj);
-+
-+	return NULL;
-+}
-+
-+
-+static int
-+hostad_afc_parse_available_freq_info(struct hostapd_iface *iface,
-+				     struct json_object *reply_elem_obj)
-+{
-+	struct afc_freq_range_elem *f = NULL;
-+	struct json_object *obj;
-+	int i, count = 0;
-+
-+	if (!json_object_object_get_ex(reply_elem_obj,
-+				       "availableFrequencyInfo", &obj))
-+		return 0;
-+
-+	for (i = 0; i < json_object_array_length(obj); i++) {
-+		struct json_object *range_elem_obj, *freq_range_obj;
-+		struct json_object *high_freq_obj, *low_freq_obj;
-+		struct json_object *max_psd_obj;
-+
-+		range_elem_obj = json_object_array_get_idx(obj, i);
-+		if (!json_object_object_get_ex(range_elem_obj,
-+					       "frequencyRange",
-+					       &freq_range_obj))
-+			continue;
-+
-+		if (!json_object_object_get_ex(freq_range_obj,
-+					       "lowFrequency",
-+					       &low_freq_obj))
-+			continue;
-+
-+		if (!json_object_object_get_ex(freq_range_obj,
-+					       "highFrequency",
-+					       &high_freq_obj))
-+			continue;
-+
-+		if (!json_object_object_get_ex(range_elem_obj, "maxPsd",
-+					       &max_psd_obj) &&
-+		    !json_object_object_get_ex(range_elem_obj, "maxPSD",
-+					       &max_psd_obj))
-+			continue;
-+
-+		f = os_realloc_array(f, count + 1, sizeof(*f));
-+		if (!f)
-+			return -ENOMEM;
-+
-+		f[count].low_freq = json_object_get_int(low_freq_obj);
-+		f[count].high_freq = json_object_get_int(high_freq_obj);
-+		f[count++].max_psd = json_object_get_int(max_psd_obj);
-+	}
-+	iface->afc.freq_range = f;
-+	iface->afc.num_freq_range = count;
-+
-+	return 0;
-+}
-+
-+
-+static int hostad_afc_update_chan_info(struct afc_chan_info_elem **chan_list,
-+				       int *chan_list_size, u8 op_class,
-+				       int center_chan, int power)
-+{
-+	int num_low_subchan, ch, count = *chan_list_size;
-+	struct afc_chan_info_elem *c = *chan_list;
-+
-+	switch (op_class) {
-+	case 132: /*  40MHz */
-+		num_low_subchan = 2;
-+		break;
-+	case 133: /*  80MHz */
-+		num_low_subchan = 6;
-+		break;
-+	case 134: /* 160MHz */
-+		num_low_subchan = 14;
-+		break;
-+	default:
-+		num_low_subchan = 0;
-+		break;
-+	}
-+
-+	for (ch = center_chan - num_low_subchan;
-+	     ch <= center_chan + num_low_subchan; ch += 4) {
-+		int i;
-+
-+		for (i = 0; i < count; i++) {
-+			if (c[i].chan == ch)
-+				break;
-+		}
-+
-+		if (i == count) {
-+			c = os_realloc_array(c, count + 1, sizeof(*c));
-+			if (!c)
-+				return -ENOMEM;
-+
-+			c[count].chan = ch;
-+			c[count++].power = power;
-+		}
-+	}
-+
-+	*chan_list_size = count;
-+	*chan_list = c;
-+
-+	return 0;
-+}
-+
-+
-+static int
-+hostad_afc_parse_available_chan_info(struct hostapd_iface *iface,
-+				     struct json_object *reply_elem_obj)
-+{
-+	struct afc_chan_info_elem *c = NULL;
-+	struct json_object *obj;
-+	int i, count = 0;
-+
-+	if (!json_object_object_get_ex(reply_elem_obj,
-+				       "availableChannelInfo", &obj))
-+		return 0;
-+
-+	for (i = 0; i < json_object_array_length(obj); i++) {
-+		struct json_object *range_elem_obj, *op_class_obj;
-+		struct json_object *chan_cfi_obj, *max_eirp_obj;
-+		int ch, op_class;
-+
-+		range_elem_obj = json_object_array_get_idx(obj, i);
-+		if (!json_object_object_get_ex(range_elem_obj,
-+					       "globalOperatingClass",
-+					       &op_class_obj))
-+			continue;
-+
-+		if (!json_object_object_get_ex(range_elem_obj, "maxEirp",
-+					       &max_eirp_obj))
-+			continue;
-+
-+		if (!json_object_object_get_ex(range_elem_obj, "channelCfi",
-+					       &chan_cfi_obj))
-+			continue;
-+
-+		op_class = json_object_get_int(op_class_obj);
-+		for (ch = 0;
-+		     ch < json_object_array_length(chan_cfi_obj); ch++) {
-+			struct json_object *pwr_obj;
-+			struct json_object *ch_obj;
-+			int channel, power;
-+
-+			ch_obj = json_object_array_get_idx(chan_cfi_obj, ch);
-+			if (!ch_obj)
-+				continue;
-+
-+			pwr_obj = json_object_array_get_idx(max_eirp_obj, ch);
-+			if (!pwr_obj)
-+				continue;
-+
-+			channel = json_object_get_int(ch_obj);
-+			power = json_object_get_int(pwr_obj);
-+
-+			hostad_afc_update_chan_info(&c, &count, op_class,
-+						    channel, power);
-+		}
-+		iface->afc.chan_info_list = c;
-+		iface->afc.num_chan_info = count;
-+	}
-+
-+	return 0;
-+}
-+
-+
-+static int hostad_afc_get_timeout(struct json_object *obj)
-+{
-+	time_t t, now;
-+	struct tm tm;
-+
-+	if (sscanf(json_object_get_string(obj), "%d-%d-%dT%d:%d:%dZ",
-+		   &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour,
-+		   &tm.tm_min, &tm.tm_sec) <= 0)
-+		return HOSTAPD_AFC_TIMEOUT;
-+
-+	tm.tm_year -= 1900;
-+	tm.tm_mon -= 1;
-+	tm.tm_isdst = -1;
-+	t = mktime(&tm);
-+	time(&now);
-+
-+	return now > t ? HOSTAPD_AFC_RETRY_TIMEOUT : (t - now) * 80 / 100;
-+}
-+
-+
-+static int hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply)
-+{
-+	struct json_object *payload_obj, *reply_obj, *version_obj;
-+	struct hostapd_config *iconf = iface->conf;
-+	int i, request_timeout = -1, ret = -EINVAL;
-+
-+	wpa_printf(MSG_DEBUG, "Received AFC reply: %s", reply);
-+	payload_obj = json_tokener_parse(reply);
-+	if (!payload_obj)
-+		return -EINVAL;
-+
-+	if (!json_object_object_get_ex(payload_obj, "version", &version_obj))
-+		return -EINVAL;
-+
-+	if (iconf->afc.request.version &&
-+	    os_strcmp(iconf->afc.request.version,
-+		      json_object_get_string(version_obj)))
-+		return -EINVAL;
-+
-+	if (!json_object_object_get_ex(payload_obj,
-+				       "availableSpectrumInquiryResponses",
-+				       &reply_obj))
-+		return -EINVAL;
-+
-+	for (i = 0; i < json_object_array_length(reply_obj); i++) {
-+		struct json_object *reply_elem_obj, *obj, *status_obj;
-+		int j, status = -EINVAL;
-+
-+		reply_elem_obj = json_object_array_get_idx(reply_obj, i);
-+		if (!reply_elem_obj)
-+			continue;
-+
-+		if (!json_object_object_get_ex(reply_elem_obj, "requestId",
-+					       &obj))
-+			continue;
-+
-+		if (iconf->afc.request.id &&
-+		    os_strcmp(iconf->afc.request.id,
-+			      json_object_get_string(obj)))
-+			continue;
-+
-+		if (!json_object_object_get_ex(reply_elem_obj, "rulesetId",
-+					       &obj))
-+			continue;
-+
-+		for (j = 0; j < iconf->afc.n_cert_ids; j++) {
-+			if (!os_strcmp(iconf->afc.cert_ids[j].rulset,
-+				       json_object_get_string(obj)))
-+				break;
-+		}
-+
-+		if (j == iconf->afc.n_cert_ids)
-+			continue;
-+
-+		if (!json_object_object_get_ex(reply_elem_obj, "response",
-+					       &obj))
-+			continue;
-+
-+		if (json_object_object_get_ex(obj, "shortDescription",
-+					      &status_obj))
-+			wpa_printf(MSG_DEBUG, "AFC reply element %d: %s",
-+				   i, json_object_get_string(status_obj));
-+
-+		if (json_object_object_get_ex(obj, "responseCode",
-+					      &status_obj))
-+			status = json_object_get_int(status_obj);
-+
-+		if (status < 0)
-+			continue;
-+
-+		if (hostad_afc_parse_available_freq_info(iface,
-+							 reply_elem_obj) ||
-+		    hostad_afc_parse_available_chan_info(iface,
-+							 reply_elem_obj))
-+			continue;
-+
-+		if (json_object_object_get_ex(reply_elem_obj,
-+					      "availabilityExpireTime",
-+					      &obj)) {
-+			int timeout = hostad_afc_get_timeout(obj);
-+
-+			if (request_timeout < 0 || timeout < request_timeout)
-+				request_timeout = timeout;
-+		}
-+
-+		ret = status;
-+	}
-+
-+	iface->afc.data_valid = true;
-+	iface->afc.timeout = request_timeout;
-+	if (iface->afc.timeout < 0)
-+		iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
-+
-+	return ret;
-+}
-+
-+
-+static int hostapd_afc_send_receive(struct hostapd_iface *iface)
-+{
-+	struct hostapd_config *iconf = iface->conf;
-+	json_object *request_obj = NULL;
-+	struct timeval sock_timeout = {
-+		.tv_sec = 5,
-+	};
-+	struct sockaddr_un addr = {
-+		.sun_family = AF_UNIX,
-+#ifdef __FreeBSD__
-+		.sun_len = sizeof(addr),
-+#endif /* __FreeBSD__ */
-+	};
-+	char buf[HOSTAPD_AFC_BUFSIZE] = {};
-+	const char *request;
-+	int sockfd, ret;
-+	fd_set read_set;
-+
-+	if (iface->afc.data_valid) {
-+		/* AFC data already downloaded from the server */
-+		return 0;
-+	}
-+
-+	iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
-+	if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) {
-+		wpa_printf(MSG_ERROR, "Malformed AFC socket string %s",
-+			   iconf->afc.socket);
-+		return -EINVAL;
-+	}
-+
-+	os_strlcpy(addr.sun_path, iconf->afc.socket, sizeof(addr.sun_path));
-+	sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
-+	if (sockfd < 0) {
-+		wpa_printf(MSG_ERROR, "Failed creating AFC socket");
-+		return sockfd;
-+	}
-+
-+	if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-+		wpa_printf(MSG_ERROR, "Failed connecting AFC socket");
-+		ret = -EIO;
-+		goto close_sock;
-+	}
-+
-+	request_obj = hostapd_afc_build_request(iface);
-+	if (!request_obj) {
-+		ret = -ENOMEM;
-+		goto close_sock;
-+	}
-+
-+	request = json_object_to_json_string(request_obj);
-+	if (send(sockfd, request, strlen(request), 0) < 0) {
-+		wpa_printf(MSG_ERROR, "Failed sending AFC request");
-+		ret = -EIO;
-+		goto close_sock;
-+	}
-+
-+	FD_ZERO(&read_set);
-+	FD_SET(sockfd, &read_set);
-+	if (select(sockfd + 1, &read_set, NULL, NULL, &sock_timeout) < 0) {
-+		wpa_printf(MSG_ERROR, "Select failed on AFC socket");
-+		ret = -errno;
-+		goto close_sock;
-+	}
-+
-+	if (!FD_ISSET(sockfd, &read_set)) {
-+		ret = -EIO;
-+		goto close_sock;
-+	}
-+
-+	ret = recv(sockfd, buf, sizeof(buf), 0);
-+	if (ret <= 0)
-+		goto close_sock;
-+
-+	ret = hostapd_afc_parse_reply(iface, buf);
-+close_sock:
-+	json_object_put(request_obj);
-+	close(sockfd);
-+
-+	return ret;
-+}
-+
-+
-+static bool hostapd_afc_has_usable_chans(struct hostapd_iface *iface)
-+{
-+	const struct oper_class_map *oper_class;
-+	int ch;
-+
-+	oper_class = get_oper_class(NULL, iface->conf->op_class);
-+	if (!oper_class)
-+		return false;
-+
-+	for (ch = oper_class->min_chan; ch <= oper_class->max_chan;
-+	     ch += oper_class->inc) {
-+		struct hostapd_hw_modes *mode = iface->current_mode;
-+		int i;
-+
-+		for (i = 0; i < mode->num_channels; i++) {
-+			struct hostapd_channel_data *chan = &mode->channels[i];
-+
-+			if (chan->chan == ch &&
-+			    !(chan->flag & HOSTAPD_CHAN_DISABLED))
-+			    return true;
-+		}
-+	}
-+
-+	return false;
-+}
-+
-+
-+int hostapd_afc_handle_request(struct hostapd_iface *iface)
-+{
-+	struct hostapd_config *iconf = iface->conf;
-+	int ret;
-+
-+	/* AFC is required just for standard power AP */
-+	if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
-+		return 1;
-+
-+	if (!is_6ghz_op_class(iconf->op_class) || !is_6ghz_freq(iface->freq))
-+		return 1;
-+
-+	if (iface->state == HAPD_IFACE_ACS)
-+		return 1;
-+
-+	ret = hostapd_afc_send_receive(iface);
-+	if (ret < 0) {
-+		/*
-+		 * If the connection to the AFCD failed, resched for a
-+		 * future attempt.
-+		 */
-+		wpa_printf(MSG_ERROR, "AFC connection failed: %d", ret);
-+		if (ret == -EIO)
-+			ret = 0;
-+		goto resched;
-+	}
-+
-+	hostap_afc_disable_channels(iface);
-+	if (!hostapd_afc_has_usable_chans(iface))
-+		goto resched;
-+
-+	/* Trigger an ACS freq scan */
-+	iconf->channel = 0;
-+	iface->freq = 0;
-+
-+	if (acs_init(iface) != HOSTAPD_CHAN_ACS) {
-+		wpa_printf(MSG_ERROR, "Could not start ACS");
-+		ret = -EINVAL;
-+	}
-+
-+resched:
-+	eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
-+	eloop_register_timeout(iface->afc.timeout, 0,
-+			       hostapd_afc_timeout_handler, iface, NULL);
-+
-+	return ret;
-+}
-+
-+
-+static void hostapd_afc_delete_data_from_server(struct hostapd_iface *iface)
-+{
-+	os_free(iface->afc.chan_info_list);
-+	os_free(iface->afc.freq_range);
-+
-+	iface->afc.num_freq_range = 0;
-+	iface->afc.num_chan_info = 0;
-+
-+	iface->afc.chan_info_list = NULL;
-+	iface->afc.freq_range = NULL;
-+
-+	iface->afc.data_valid = false;
-+}
-+
-+
-+static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx)
-+{
-+	struct hostapd_iface *iface = eloop_ctx;
-+	bool restart_iface = true;
-+
-+	hostapd_afc_delete_data_from_server(iface);
-+	if (iface->state != HAPD_IFACE_ENABLED) {
-+		/* Hostapd is not fully enabled yet, toogle the interface */
-+		goto restart_interface;
-+	}
-+
-+	if (hostapd_afc_send_receive(iface) < 0 ||
-+	    hostapd_get_hw_features(iface)) {
-+		restart_iface = false;
-+		goto restart_interface;
-+	}
-+
-+	if (hostapd_is_usable_chans(iface))
-+		goto resched;
-+
-+	restart_iface = hostapd_afc_has_usable_chans(iface);
-+	if (restart_iface) {
-+		/* Trigger an ACS freq scan */
-+		iface->conf->channel = 0;
-+		iface->freq = 0;
-+	}
-+
-+restart_interface:
-+	hostapd_disable_iface(iface);
-+	if (restart_iface)
-+		hostapd_enable_iface(iface);
-+resched:
-+	eloop_register_timeout(iface->afc.timeout, 0,
-+			       hostapd_afc_timeout_handler, iface, NULL);
-+}
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 1a18df617..ca67aeb41 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -1035,6 +1035,22 @@ void hostapd_config_free(struct hostapd_config *conf)
- #endif /* CONFIG_ACS */
- 	wpabuf_free(conf->lci);
- 	wpabuf_free(conf->civic);
-+#ifdef CONFIG_AFC
-+	os_free(conf->afc.socket);
-+	os_free(conf->afc.request.version);
-+	os_free(conf->afc.request.id);
-+	os_free(conf->afc.request.sn);
-+	for (i = 0; i < conf->afc.n_cert_ids; i++) {
-+		os_free(conf->afc.cert_ids[i].rulset);
-+		os_free(conf->afc.cert_ids[i].id);
-+	}
-+	os_free(conf->afc.cert_ids);
-+	os_free(conf->afc.location.height_type);
-+	os_free(conf->afc.location.linear_polygon_data);
-+	os_free(conf->afc.location.radial_polygon_data);
-+	os_free(conf->afc.freq_range);
-+	os_free(conf->afc.op_class);
-+#endif /* CONFIG_AFC */
- 
- 	os_free(conf);
- }
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 754d55331..2330163c4 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1225,6 +1225,53 @@ struct hostapd_config {
- 		MBSSID_ENABLED = 1,
- 		ENHANCED_MBSSID_ENABLED = 2,
- 	} mbssid;
-+
-+#ifdef CONFIG_AFC
-+	struct {
-+		char *socket;
-+		struct {
-+			char *version;
-+			char *id;
-+			char *sn;
-+		} request;
-+		unsigned int n_cert_ids;
-+		struct cert_id {
-+			char *rulset;
-+			char *id;
-+		} *cert_ids;
-+		struct {
-+			enum afc_location_type {
-+				ELLIPSE,
-+				LINEAR_POLYGON,
-+				RADIAL_POLYGON,
-+			} type;
-+			unsigned int n_linear_polygon_data;
-+			struct afc_linear_polygon {
-+				double longitude;
-+				double latitude;
-+			} *linear_polygon_data;
-+			unsigned int n_radial_polygon_data;
-+			struct afc_radial_polygon {
-+				double length;
-+				double angle;
-+			} *radial_polygon_data;
-+			int major_axis;
-+			int minor_axis;
-+			int orientation;
-+			double height;
-+			char *height_type;
-+			int vertical_tolerance;
-+		} location;
-+		unsigned int n_freq_range;
-+		struct afc_freq_range {
-+			int low_freq;
-+			int high_freq;
-+		} *freq_range;
-+		unsigned int n_op_class;
-+		unsigned int *op_class;
-+		int min_power;
-+	} afc;
-+#endif /* CONFIG_AFC */
- };
- 
- 
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index ff1d8f9d0..916ac00c4 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2497,6 +2497,16 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
- 		}
- #endif /* CONFIG_MESH */
- 
-+#ifdef CONFIG_AFC
-+		/* check AFC for 6GHz channels. */
-+		res = hostapd_afc_handle_request(iface);
-+		if (res <= 0) {
-+			if (res < 0)
-+				goto fail;
-+			return res;
-+		}
-+#endif /* CONFIG_AFC */
-+
- 		if (!delay_apply_cfg &&
- 		    hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
- 				     hapd->iconf->channel,
-@@ -2968,6 +2978,10 @@ void hostapd_interface_free(struct hostapd_iface *iface)
- 			   __func__, iface->bss[j]);
- 		os_free(iface->bss[j]);
- 	}
-+#ifdef CONFIG_AFC
-+	os_free(iface->afc.chan_info_list);
-+	os_free(iface->afc.freq_range);
-+#endif
- 	hostapd_cleanup_iface(iface);
- }
- 
-@@ -4888,3 +4902,49 @@ u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd)
- 
- 	return punct_bitmap;
- }
-+
-+
-+void hostap_afc_disable_channels(struct hostapd_iface *iface)
-+{
-+#ifdef CONFIG_AFC
-+	struct hostapd_hw_modes *mode;
-+	int i;
-+
-+	if (iface->num_hw_features < HOSTAPD_MODE_IEEE80211A)
-+		return;
-+
-+	if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type))
-+		return;
-+
-+	if (!iface->afc.data_valid)
-+		return;
-+
-+	mode = &iface->hw_features[HOSTAPD_MODE_IEEE80211A];
-+	for (i = 0; i < mode->num_channels; i++) {
-+		struct hostapd_channel_data *chan = &mode->channels[i];
-+		int j;
-+
-+		if (!is_6ghz_freq(chan->freq))
-+			continue;
-+
-+		for (j = 0; j < iface->afc.num_freq_range; j++) {
-+			if (chan->freq >= iface->afc.freq_range[j].low_freq &&
-+			    chan->freq <= iface->afc.freq_range[j].high_freq)
-+				break;
-+		}
-+
-+		if (j != iface->afc.num_freq_range)
-+			continue;
-+
-+		for (j = 0; j < iface->afc.num_chan_info; j++) {
-+			if (chan->chan == iface->afc.chan_info_list[j].chan)
-+				break;
-+		}
-+
-+		if (j != iface->afc.num_chan_info)
-+			continue;
-+
-+		chan->flag |= HOSTAPD_CHAN_DISABLED;
-+	}
-+#endif /* CONFIG_AFC */
-+}
-diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
-index d12efb104..18bcb82d9 100644
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -700,9 +700,38 @@ struct hostapd_iface {
- 
- 	/* Configured freq of interface is NO_IR */
- 	bool is_no_ir;
-+
-+#ifdef CONFIG_AFC
-+	struct {
-+		int timeout;
-+		unsigned int num_freq_range;
-+		struct afc_freq_range_elem {
-+			int low_freq;
-+			int high_freq;
-+			/**
-+			 * max eirp power spectral density received from
-+			 * the AFC coordinator for this band
-+			 */
-+			int max_psd;
-+		} *freq_range;
-+		unsigned int num_chan_info;
-+		struct afc_chan_info_elem {
-+			int chan;
-+			/**
-+			 * max eirp power received from the AFC coordinator
-+			 * for this channel
-+			 */
-+			int power;
-+		} *chan_info_list;
-+		bool data_valid;
-+	} afc;
-+#endif /* CONFIG_AFC */
- };
- 
- /* hostapd.c */
-+void hostap_afc_disable_channels(struct hostapd_iface *iface);
-+int hostapd_afc_handle_request(struct hostapd_iface *iface);
-+
- int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
- 			       int (*cb)(struct hostapd_iface *iface,
- 					 void *ctx), void *ctx);
-diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
-index e652d7504..222f3dc05 100644
---- a/src/ap/hw_features.c
-+++ b/src/ap/hw_features.c
-@@ -114,6 +114,8 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
- 	iface->hw_features = modes;
- 	iface->num_hw_features = num_modes;
- 
-+	hostap_afc_disable_channels(iface);
-+
- 	for (i = 0; i < num_modes; i++) {
- 		struct hostapd_hw_modes *feature = &modes[i];
- 		int dfs_enabled = hapd->iconf->ieee80211h &&
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0025-mtk-hostapd-6G-band-does-not-require-DFS.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0025-mtk-hostapd-6G-band-does-not-require-DFS.patch
new file mode 100644
index 0000000..2ba3b97
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0025-mtk-hostapd-6G-band-does-not-require-DFS.patch
@@ -0,0 +1,24 @@
+From ba80aca90cdfbc98b8a21489f0fc89114c94f2d1 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 13 Feb 2023 11:03:53 +0800
+Subject: [PATCH 025/126] mtk: hostapd: 6G band does not require DFS
+
+---
+ src/ap/dfs.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 42cce2dce..86598a18a 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1538,6 +1538,7 @@ int hostapd_is_dfs_required(struct hostapd_iface *iface)
+ 	if ((!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+ 	     !iface->conf->ieee80211h) ||
+ 	    !iface->current_mode ||
++	    is_6ghz_freq(iface->freq) ||
+ 	    iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+ 		return 0;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0026-backport-hostapd-update-TPE-IE-according-to-AFC.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0026-backport-hostapd-update-TPE-IE-according-to-AFC.patch
deleted file mode 100644
index 42ff1d2..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0026-backport-hostapd-update-TPE-IE-according-to-AFC.patch
+++ /dev/null
@@ -1,158 +0,0 @@
-From b2078261e779c949218974a054dc52f3dc5493c7 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Wed, 20 Mar 2024 07:20:01 +0800
-Subject: [PATCH 026/104] backport: hostapd: update TPE IE according to AFC
-
-Update Transmit Power Envelope (TPE) IE according to the reply from AFC
-coordinator on UNII-5 or UNII-7 6GHz bands.
-
-Tested-by: Allen Ye <allen.ye@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
----
- src/ap/hostapd.c    | 39 +++++++++++++++++++++++++++++++++++++
- src/ap/hostapd.h    |  2 ++
- src/ap/ieee802_11.c | 47 ++++++++++++++++++++++++++++-----------------
- 3 files changed, 70 insertions(+), 18 deletions(-)
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 916ac00c4..b899c9831 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -4904,6 +4904,45 @@ u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd)
- }
- 
- 
-+int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
-+				       int *power)
-+{
-+#ifdef CONFIG_AFC
-+	int i;
-+
-+	if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type))
-+		return -EINVAL;
-+
-+	if (!iface->afc.data_valid)
-+		return -EINVAL;
-+
-+	if (psd) {
-+		for (i = 0; i < iface->afc.num_freq_range; i++) {
-+			struct afc_freq_range_elem *f;
-+
-+			f = &iface->afc.freq_range[i];
-+			if (iface->freq >= f->low_freq &&
-+			    iface->freq <= f->high_freq) {
-+				*power = 2 * f->max_psd;
-+				return 0;
-+			}
-+		}
-+	} else {
-+		for (i = 0; i < iface->afc.num_chan_info; i++) {
-+			struct afc_chan_info_elem *c;
-+
-+			c = &iface->afc.chan_info_list[i];
-+			if (c->chan == iface->conf->channel) {
-+				*power = 2 * c->power;
-+				return 0;
-+			}
-+		}
-+	}
-+#endif /* CONFIG_AFC */
-+	return -EINVAL;
-+}
-+
-+
- void hostap_afc_disable_channels(struct hostapd_iface *iface)
- {
- #ifdef CONFIG_AFC
-diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
-index 18bcb82d9..594866fbb 100644
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -729,6 +729,8 @@ struct hostapd_iface {
- };
- 
- /* hostapd.c */
-+int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
-+				       int *power);
- void hostap_afc_disable_channels(struct hostapd_iface *iface);
- int hostapd_afc_handle_request(struct hostapd_iface *iface);
- 
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 9a23c7240..179af5e28 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -7047,42 +7047,53 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
- 	 */
- 	if (is_6ghz_op_class(iconf->op_class)) {
- 		enum max_tx_pwr_interpretation tx_pwr_intrpn;
-+		int err, max_eirp_psd, max_eirp_power;
- 
- 		/* Same Maximum Transmit Power for all 20 MHz bands */
- 		tx_pwr_count = 0;
- 		tx_pwr_intrpn = REGULATORY_CLIENT_EIRP_PSD;
- 
- 		/* Default Transmit Power Envelope for Global Operating Class */
--		if (hapd->iconf->reg_def_cli_eirp_psd != -1)
--			tx_pwr = hapd->iconf->reg_def_cli_eirp_psd;
--		else
--			tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
-+		err = hostap_afc_get_chan_max_eirp_power(iface, true,
-+							 &max_eirp_psd);
-+		if (err < 0) {
-+			if (hapd->iconf->reg_def_cli_eirp_psd != -1)
-+				max_eirp_psd = hapd->iconf->reg_def_cli_eirp_psd;
-+			else
-+				max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
-+		}
- 
- 		eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn,
--					   REG_DEFAULT_CLIENT, tx_pwr);
-+					   REG_DEFAULT_CLIENT, max_eirp_psd);
- 
- 		/* Indoor Access Point must include an additional TPE for
- 		 * subordinate devices */
- 		if (he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type)) {
--			/* TODO: Extract PSD limits from channel data */
--			if (hapd->iconf->reg_sub_cli_eirp_psd != -1)
--				tx_pwr = hapd->iconf->reg_sub_cli_eirp_psd;
--			else
--				tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
-+			if (err < 0) {
-+				/* non-AFC connection */
-+				if (hapd->iconf->reg_sub_cli_eirp_psd != -1)
-+					max_eirp_psd = hapd->iconf->reg_sub_cli_eirp_psd;
-+				else
-+					max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
-+			}
- 			eid = hostapd_add_tpe_info(eid, tx_pwr_count,
- 						   tx_pwr_intrpn,
- 						   REG_SUBORDINATE_CLIENT,
--						   tx_pwr);
-+						   max_eirp_psd);
- 		}
- 
--		if (iconf->reg_def_cli_eirp != -1 &&
--		    he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
--			eid = hostapd_add_tpe_info(
--				eid, tx_pwr_count, REGULATORY_CLIENT_EIRP,
--				REG_DEFAULT_CLIENT,
--				hapd->iconf->reg_def_cli_eirp);
-+		if (hostap_afc_get_chan_max_eirp_power(iface, false,
-+						       &max_eirp_power)) {
-+			max_eirp_power = iconf->reg_def_cli_eirp;
-+			if (max_eirp_power == -1 ||
-+			    !he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
-+				return eid;
-+		}
- 
--		return eid;
-+		return hostapd_add_tpe_info(eid, tx_pwr_count,
-+					    REGULATORY_CLIENT_EIRP,
-+					    REG_DEFAULT_CLIENT,
-+					    max_eirp_power);
- 	}
- #endif /* CONFIG_IEEE80211AX */
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0026-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0026-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch
new file mode 100644
index 0000000..914e7ad
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0026-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch
@@ -0,0 +1,46 @@
+From 4fc496233d06ceb3c162fa4a474c9bf8bf2ae909 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 11:01:18 +0800
+Subject: [PATCH 026/126] mtk: hostapd: Fix sending wrong VHT operation IE in
+ CSA while using ZWDFS
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 86598a18a..aaaea0edc 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1130,6 +1130,14 @@ static int
+ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+ {
+ 	u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
++	int ret;
++
++	ret = hostapd_dfs_request_channel_switch(iface, iface->radar_background.channel,
++						 iface->radar_background.freq,
++						 iface->radar_background.secondary_channel,
++						 current_vht_oper_chwidth,
++						 iface->radar_background.centr_freq_seg0_idx,
++						 iface->radar_background.centr_freq_seg1_idx);
+ 
+ 	iface->conf->channel = iface->radar_background.channel;
+ 	iface->freq = iface->radar_background.freq;
+@@ -1142,11 +1150,7 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+ 
+ 	hostapd_dfs_update_background_chain(iface);
+ 
+-	return hostapd_dfs_request_channel_switch(
+-		iface, iface->conf->channel, iface->freq,
+-		iface->conf->secondary_channel, current_vht_oper_chwidth,
+-		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+-		hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
++	return ret;
+ }
+ 
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0027-hostapd-sync-2024-01-18-openwrt-trunk-src-folder.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0027-hostapd-sync-2024-01-18-openwrt-trunk-src-folder.patch
deleted file mode 100644
index cbe2769..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0027-hostapd-sync-2024-01-18-openwrt-trunk-src-folder.patch
+++ /dev/null
@@ -1,5128 +0,0 @@
-From ccb5628a9c5eae2b56cb88f43f850146863cba30 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Tue, 23 Jan 2024 16:46:44 +0800
-Subject: [PATCH 027/104] hostapd: sync 2024-01-18 openwrt/trunk src folder
-
----
- hostapd/radius.c           |  715 +++++++++++++
- src/ap/ubus.c              | 2005 ++++++++++++++++++++++++++++++++++++
- src/ap/ubus.h              |  154 +++
- src/ap/ucode.c             |  813 +++++++++++++++
- src/ap/ucode.h             |   54 +
- src/utils/build_features.h |   65 ++
- src/utils/ucode.c          |  502 +++++++++
- src/utils/ucode.h          |   30 +
- wpa_supplicant/ubus.c      |  280 +++++
- wpa_supplicant/ubus.h      |   55 +
- wpa_supplicant/ucode.c     |  299 ++++++
- wpa_supplicant/ucode.h     |   49 +
- 12 files changed, 5021 insertions(+)
- create mode 100644 hostapd/radius.c
- create mode 100644 src/ap/ubus.c
- create mode 100644 src/ap/ubus.h
- create mode 100644 src/ap/ucode.c
- create mode 100644 src/ap/ucode.h
- create mode 100644 src/utils/build_features.h
- create mode 100644 src/utils/ucode.c
- create mode 100644 src/utils/ucode.h
- create mode 100644 wpa_supplicant/ubus.c
- create mode 100644 wpa_supplicant/ubus.h
- create mode 100644 wpa_supplicant/ucode.c
- create mode 100644 wpa_supplicant/ucode.h
-
-diff --git a/hostapd/radius.c b/hostapd/radius.c
-new file mode 100644
-index 000000000..362a22c27
---- /dev/null
-+++ b/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/src/ap/ubus.c b/src/ap/ubus.c
-new file mode 100644
-index 000000000..f2041a0c9
---- /dev/null
-+++ b/src/ap/ubus.c
-@@ -0,0 +1,2005 @@
-+/*
-+ * 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);
-+}
-+
-+
-+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, hostapd_get_punct_bitmap(hapd));
-+
-+	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;
-+
-+	if (!ctx)
-+		return;
-+
-+	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/src/ap/ubus.h b/src/ap/ubus.h
-new file mode 100644
-index 000000000..b0f7c44ab
---- /dev/null
-+++ b/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/src/ap/ucode.c b/src/ap/ucode.c
-new file mode 100644
-index 000000000..16d1b5153
---- /dev/null
-+++ b/src/ap/ucode.c
-@@ -0,0 +1,813 @@
-+#include <sys/un.h>
-+
-+#include "utils/includes.h"
-+#include "utils/common.h"
-+#include "utils/ucode.h"
-+#include "hostapd.h"
-+#include "beacon.h"
-+#include "hw_features.h"
-+#include "ap_drv_ops.h"
-+#include "dfs.h"
-+#include "acs.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);
-+	hapd->ucode.idx = wpa_ucode_registry_add(bss_registry, val);
-+
-+	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);
-+	hapd->ucode.idx = wpa_ucode_registry_add(iface_registry, val);
-+
-+	return val;
-+}
-+
-+static void
-+hostapd_ucode_update_bss_list(struct hostapd_iface *iface, uc_value_t *if_bss, uc_value_t *bss)
-+{
-+	uc_value_t *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);
-+
-+		ucv_array_set(list, i, ucv_get(ucv_string_new(hapd->conf->iface)));
-+		ucv_object_add(bss, hapd->conf->iface, ucv_get(val));
-+	}
-+	ucv_object_add(if_bss, iface->phy, ucv_get(list));
-+}
-+
-+static void
-+hostapd_ucode_update_interfaces(void)
-+{
-+	uc_value_t *ifs = ucv_object_new(vm);
-+	uc_value_t *if_bss = ucv_array_new(vm);
-+	uc_value_t *bss = 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, if_bss, bss);
-+	}
-+
-+	ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
-+	ucv_object_add(ucv_prototype_get(global), "interface_bss", ucv_get(if_bss));
-+	ucv_object_add(ucv_prototype_get(global), "bss", ucv_get(bss));
-+	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 struct hostapd_vlan *
-+bss_conf_find_vlan(struct hostapd_bss_config *bss, int id)
-+{
-+	struct hostapd_vlan *vlan;
-+
-+	for (vlan = bss->vlan; vlan; vlan = vlan->next)
-+		if (vlan->vlan_id == id)
-+			return vlan;
-+
-+	return NULL;
-+}
-+
-+static int
-+bss_conf_rename_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
-+		     const char *ifname)
-+{
-+	if (!strcmp(ifname, vlan->ifname))
-+		return 0;
-+
-+	hostapd_drv_if_rename(hapd, WPA_IF_AP_VLAN, vlan->ifname, ifname);
-+	os_strlcpy(vlan->ifname, ifname, sizeof(vlan->ifname));
-+
-+	return 0;
-+}
-+
-+static int
-+bss_reload_vlans(struct hostapd_data *hapd, struct hostapd_bss_config *bss)
-+{
-+	struct hostapd_bss_config *old_bss = hapd->conf;
-+	struct hostapd_vlan *vlan, *vlan_new, *wildcard;
-+	char ifname[IFNAMSIZ + 1], vlan_ifname[IFNAMSIZ + 1], *pos;
-+	int ret;
-+
-+	vlan = bss_conf_find_vlan(old_bss, VLAN_ID_WILDCARD);
-+	wildcard = bss_conf_find_vlan(bss, VLAN_ID_WILDCARD);
-+	if (!!vlan != !!wildcard)
-+		return -1;
-+
-+	if (vlan && wildcard && strcmp(vlan->ifname, wildcard->ifname) != 0)
-+		strcpy(vlan->ifname, wildcard->ifname);
-+	else
-+		wildcard = NULL;
-+
-+	for (vlan = bss->vlan; vlan; vlan = vlan->next) {
-+		if (vlan->vlan_id == VLAN_ID_WILDCARD ||
-+		    vlan->dynamic_vlan > 0)
-+			continue;
-+
-+		if (!bss_conf_find_vlan(old_bss, vlan->vlan_id))
-+			return -1;
-+	}
-+
-+	for (vlan = old_bss->vlan; vlan; vlan = vlan->next) {
-+		if (vlan->vlan_id == VLAN_ID_WILDCARD)
-+			continue;
-+
-+		if (vlan->dynamic_vlan == 0) {
-+			vlan_new = bss_conf_find_vlan(bss, vlan->vlan_id);
-+			if (!vlan_new)
-+				return -1;
-+
-+			if (bss_conf_rename_vlan(hapd, vlan, vlan_new->ifname))
-+				return -1;
-+
-+			continue;
-+		}
-+
-+		if (!wildcard)
-+			continue;
-+
-+		os_strlcpy(ifname, wildcard->ifname, sizeof(ifname));
-+		pos = os_strchr(ifname, '#');
-+		if (!pos)
-+			return -1;
-+
-+		*pos++ = '\0';
-+		ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s%d%s",
-+				  ifname, vlan->vlan_id, pos);
-+	        if (os_snprintf_error(sizeof(vlan_ifname), ret))
-+			return -1;
-+
-+		if (bss_conf_rename_vlan(hapd, vlan, vlan_ifname))
-+			return -1;
-+	}
-+
-+	return 0;
-+}
-+
-+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);
-+	uc_value_t *files_only = uc_fn_arg(2);
-+	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)
-+		goto out;
-+
-+	if (idx > conf->num_bss || !conf->bss[idx])
-+		goto free;
-+
-+	if (ucv_boolean_get(files_only)) {
-+		struct hostapd_bss_config *bss = conf->bss[idx];
-+		struct hostapd_bss_config *old_bss = hapd->conf;
-+
-+#define swap_field(name)				\
-+	do {								\
-+		void *ptr = old_bss->name;		\
-+		old_bss->name = bss->name;		\
-+		bss->name = ptr;				\
-+	} while (0)
-+
-+		swap_field(ssid.wpa_psk_file);
-+		ret = bss_reload_vlans(hapd, bss);
-+		goto done;
-+	}
-+
-+	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_setup_bss(hapd, hapd == iface->bss[0], true);
-+	hostapd_ucode_update_interfaces();
-+
-+done:
-+	ret = 0;
-+free:
-+	hostapd_config_free(conf);
-+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)
-+		return NULL;
-+
-+	iface = hapd->iface;
-+	if (iface->num_bss == 1) {
-+		wpa_printf(MSG_ERROR, "trying to delete last bss of an iface: %s\n", hapd->conf->iface);
-+		return NULL;
-+	}
-+
-+	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--;
-+
-+	iface->bss[0]->interface_added = 0;
-+	hostapd_drv_set_first_bss(iface->bss[0]);
-+	hapd->interface_added = 1;
-+
-+	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_interfaces();
-+	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_interfaces();
-+	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_iface_set_bss_order(uc_vm_t *vm, size_t nargs)
-+{
-+	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
-+	uc_value_t *bss_list = uc_fn_arg(0);
-+	struct hostapd_data **new_bss;
-+	struct hostapd_bss_config **new_conf;
-+
-+	if (!iface)
-+		return NULL;
-+
-+	if (ucv_type(bss_list) != UC_ARRAY ||
-+	    ucv_array_length(bss_list) != iface->num_bss)
-+		return NULL;
-+
-+	new_bss = calloc(iface->num_bss, sizeof(*new_bss));
-+	new_conf = calloc(iface->num_bss, sizeof(*new_conf));
-+	for (size_t i = 0; i < iface->num_bss; i++) {
-+		struct hostapd_data *bss;
-+
-+		bss = ucv_resource_data(ucv_array_get(bss_list, i), "hostapd.bss");
-+		if (bss->iface != iface)
-+			goto free;
-+
-+		for (size_t k = 0; k < i; k++)
-+			if (new_bss[k] == bss)
-+				goto free;
-+
-+		new_bss[i] = bss;
-+		new_conf[i] = bss->conf;
-+	}
-+
-+	new_bss[0]->interface_added = 0;
-+	for (size_t i = 1; i < iface->num_bss; i++)
-+		new_bss[i]->interface_added = 1;
-+
-+	free(iface->bss);
-+	iface->bss = new_bss;
-+
-+	free(iface->conf->bss);
-+	iface->conf->bss = new_conf;
-+	iface->conf->num_bss = iface->num_bss;
-+	hostapd_drv_set_first_bss(iface->bss[0]);
-+
-+	return ucv_boolean_new(true);
-+
-+free:
-+	free(new_bss);
-+	free(new_conf);
-+	return NULL;
-+}
-+
-+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);
-+}
-+
-+static void
-+uc_hostapd_disable_iface(struct hostapd_iface *iface)
-+{
-+	switch (iface->state) {
-+	case HAPD_IFACE_DISABLED:
-+		break;
-+#ifdef CONFIG_ACS
-+	case HAPD_IFACE_ACS:
-+		acs_cleanup(iface);
-+		iface->scan_cb = NULL;
-+		/* fallthrough */
-+#endif
-+	default:
-+		hostapd_disable_iface(iface);
-+		break;
-+	}
-+}
-+
-+static uc_value_t *
-+uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
-+{
-+	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
-+	int i;
-+
-+	if (!iface)
-+		return NULL;
-+
-+	if (iface->state != HAPD_IFACE_ENABLED)
-+		uc_hostapd_disable_iface(iface);
-+
-+	for (i = 0; i < iface->num_bss; i++) {
-+		struct hostapd_data *hapd = iface->bss[i];
-+
-+		hostapd_drv_stop_ap(hapd);
-+		hapd->beacon_set_done = 0;
-+	}
-+
-+	return NULL;
-+}
-+
-+static uc_value_t *
-+uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
-+{
-+	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
-+	uc_value_t *info = uc_fn_arg(0);
-+	struct hostapd_config *conf;
-+	bool changed = false;
-+	uint64_t intval;
-+	int i;
-+
-+	if (!iface)
-+		return NULL;
-+
-+	if (!info) {
-+		iface->freq = 0;
-+		goto out;
-+	}
-+
-+	if (ucv_type(info) != UC_OBJECT)
-+		return NULL;
-+
-+#define UPDATE_VAL(field, name)							\
-+	if ((intval = ucv_int64_get(ucv_object_get(info, name, NULL))) &&	\
-+		!errno && intval != conf->field) do {				\
-+		conf->field = intval;						\
-+		changed = true;							\
-+	} while(0)
-+
-+	conf = iface->conf;
-+	UPDATE_VAL(op_class, "op_class");
-+	UPDATE_VAL(hw_mode, "hw_mode");
-+	UPDATE_VAL(channel, "channel");
-+	UPDATE_VAL(secondary_channel, "sec_channel");
-+	if (!changed &&
-+	    (iface->bss[0]->beacon_set_done ||
-+	     iface->state == HAPD_IFACE_DFS))
-+		return ucv_boolean_new(true);
-+
-+	intval = ucv_int64_get(ucv_object_get(info, "center_seg0_idx", NULL));
-+	if (!errno)
-+		hostapd_set_oper_centr_freq_seg0_idx(conf, intval);
-+
-+	intval = ucv_int64_get(ucv_object_get(info, "center_seg1_idx", NULL));
-+	if (!errno)
-+		hostapd_set_oper_centr_freq_seg1_idx(conf, intval);
-+
-+	intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
-+	if (!errno)
-+		hostapd_set_oper_chwidth(conf, intval);
-+
-+	intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL));
-+	if (!errno)
-+		iface->freq = intval;
-+	else
-+		iface->freq = 0;
-+	conf->acs = 0;
-+
-+out:
-+	switch (iface->state) {
-+	case HAPD_IFACE_ENABLED:
-+		if (!hostapd_is_dfs_required(iface) ||
-+			hostapd_is_dfs_chan_available(iface))
-+			break;
-+		wpa_printf(MSG_INFO, "DFS CAC required on new channel, restart interface");
-+		/* fallthrough */
-+	default:
-+		uc_hostapd_disable_iface(iface);
-+		break;
-+	}
-+
-+	if (conf->channel && !iface->freq)
-+		iface->freq = hostapd_hw_get_freq(iface->bss[0], conf->channel);
-+
-+	if (iface->state != HAPD_IFACE_ENABLED) {
-+		hostapd_enable_iface(iface);
-+		return ucv_boolean_new(true);
-+	}
-+
-+	for (i = 0; i < iface->num_bss; i++) {
-+		struct hostapd_data *hapd = iface->bss[i];
-+		int ret;
-+
-+		hapd->conf->start_disabled = 0;
-+		hostapd_set_freq(hapd, conf->hw_mode, iface->freq,
-+				 conf->channel,
-+				 conf->enable_edmg,
-+				 conf->edmg_channel,
-+				 conf->ieee80211n,
-+				 conf->ieee80211ac,
-+				 conf->ieee80211ax,
-+				 conf->ieee80211be,
-+				 conf->secondary_channel,
-+				 hostapd_get_oper_chwidth(conf),
-+				 hostapd_get_oper_centr_freq_seg0_idx(conf),
-+				 hostapd_get_oper_centr_freq_seg1_idx(conf));
-+
-+		ieee802_11_set_beacon(hapd);
-+	}
-+
-+	return ucv_boolean_new(true);
-+}
-+
-+static uc_value_t *
-+uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
-+{
-+	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
-+	uc_value_t *info = uc_fn_arg(0);
-+	struct hostapd_config *conf;
-+	struct csa_settings csa = {};
-+	uint64_t intval;
-+	int i, ret = 0;
-+
-+	if (!iface || ucv_type(info) != UC_OBJECT)
-+		return NULL;
-+
-+	conf = iface->conf;
-+	if ((intval = ucv_int64_get(ucv_object_get(info, "csa_count", NULL))) && !errno)
-+		csa.cs_count = intval;
-+	if ((intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL))) && !errno)
-+		csa.freq_params.sec_channel_offset = intval;
-+
-+	csa.freq_params.ht_enabled = conf->ieee80211n;
-+	csa.freq_params.vht_enabled = conf->ieee80211ac;
-+	csa.freq_params.he_enabled = conf->ieee80211ax;
-+#ifdef CONFIG_IEEE80211BE
-+	csa.freq_params.eht_enabled = conf->ieee80211be;
-+#endif
-+	intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
-+	if (errno)
-+		intval = hostapd_get_oper_chwidth(conf);
-+	if (intval)
-+		csa.freq_params.bandwidth = 40 << intval;
-+	else
-+		csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20;
-+
-+	if ((intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL))) && !errno)
-+		csa.freq_params.freq = intval;
-+	if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq1", NULL))) && !errno)
-+		csa.freq_params.center_freq1 = intval;
-+	if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
-+		csa.freq_params.center_freq2 = intval;
-+
-+	for (i = 0; i < iface->num_bss; i++)
-+		ret = hostapd_switch_channel(iface->bss[i], &csa);
-+
-+	return ucv_boolean_new(!ret);
-+}
-+
-+static uc_value_t *
-+uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
-+{
-+	struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
-+	uc_value_t *ifname_arg = uc_fn_arg(0);
-+	char prev_ifname[IFNAMSIZ + 1];
-+	struct sta_info *sta;
-+	const char *ifname;
-+	int ret;
-+
-+	if (!hapd || ucv_type(ifname_arg) != UC_STRING)
-+		return NULL;
-+
-+	os_strlcpy(prev_ifname, hapd->conf->iface, sizeof(prev_ifname));
-+	ifname = ucv_string_get(ifname_arg);
-+
-+	hostapd_ubus_free_bss(hapd);
-+	if (interfaces->ctrl_iface_deinit)
-+		interfaces->ctrl_iface_deinit(hapd);
-+
-+	ret = hostapd_drv_if_rename(hapd, WPA_IF_AP_BSS, NULL, ifname);
-+	if (ret)
-+		goto out;
-+
-+	for (sta = hapd->sta_list; sta; sta = sta->next) {
-+		char cur_name[IFNAMSIZ + 1], new_name[IFNAMSIZ + 1];
-+
-+		if (!(sta->flags & WLAN_STA_WDS) || sta->pending_wds_enable)
-+			continue;
-+
-+		snprintf(cur_name, sizeof(cur_name), "%s.sta%d", prev_ifname, sta->aid);
-+		snprintf(new_name, sizeof(new_name), "%s.sta%d", ifname, sta->aid);
-+		hostapd_drv_if_rename(hapd, WPA_IF_AP_VLAN, cur_name, new_name);
-+	}
-+
-+	if (!strncmp(hapd->conf->ssid.vlan, hapd->conf->iface, sizeof(hapd->conf->ssid.vlan)))
-+		os_strlcpy(hapd->conf->ssid.vlan, ifname, sizeof(hapd->conf->ssid.vlan));
-+	os_strlcpy(hapd->conf->iface, ifname, sizeof(hapd->conf->iface));
-+	hostapd_ubus_add_bss(hapd);
-+
-+	hostapd_ucode_update_interfaces();
-+out:
-+	if (interfaces->ctrl_iface_init)
-+		interfaces->ctrl_iface_init(hapd);
-+
-+	return ret ? NULL : ucv_boolean_new(true);
-+}
-+
-+
-+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 },
-+		{ "freq_info", uc_wpa_freq_info },
-+		{ "add_iface", uc_hostapd_add_iface },
-+		{ "remove_iface", uc_hostapd_remove_iface },
-+		{ "udebug_set", uc_wpa_udebug_set },
-+	};
-+	static const uc_function_list_t bss_fns[] = {
-+		{ "ctrl", uc_hostapd_bss_ctrl },
-+		{ "set_config", uc_hostapd_bss_set_config },
-+		{ "rename", uc_hostapd_bss_rename },
-+		{ "delete", uc_hostapd_bss_delete },
-+	};
-+	static const uc_function_list_t iface_fns[] = {
-+		{ "set_bss_order", uc_hostapd_iface_set_bss_order },
-+		{ "add_bss", uc_hostapd_iface_add_bss },
-+		{ "stop", uc_hostapd_iface_stop },
-+		{ "start", uc_hostapd_iface_start },
-+		{ "switch_channel", uc_hostapd_iface_switch_channel },
-+	};
-+	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)
-+{
-+	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));
-+	ucv_put(wpa_ucode_call(2));
-+	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/src/ap/ucode.h b/src/ap/ucode.h
-new file mode 100644
-index 000000000..d00b78716
---- /dev/null
-+++ b/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);
-+
-+#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)
-+{
-+}
-+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/src/utils/build_features.h b/src/utils/build_features.h
-new file mode 100644
-index 000000000..553769ece
---- /dev/null
-+++ b/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/src/utils/ucode.c b/src/utils/ucode.c
-new file mode 100644
-index 000000000..29c753c32
---- /dev/null
-+++ b/src/utils/ucode.c
-@@ -0,0 +1,502 @@
-+#include <unistd.h>
-+#include "ucode.h"
-+#include "utils/eloop.h"
-+#include "crypto/crypto.h"
-+#include "crypto/sha1.h"
-+#include "common/ieee802_11_common.h"
-+#include <linux/netlink.h>
-+#include <linux/genetlink.h>
-+#include <linux/nl80211.h>
-+#include <libubox/uloop.h>
-+#include <ucode/compiler.h>
-+#include <udebug.h>
-+
-+static uc_value_t *registry;
-+static uc_vm_t vm;
-+static struct uloop_timeout gc_timer;
-+static struct udebug ud;
-+static struct udebug_buf ud_log, ud_nl[3];
-+static const struct udebug_buf_meta meta_log = {
-+	.name = "wpa_log",
-+	.format = UDEBUG_FORMAT_STRING,
-+};
-+static const struct udebug_buf_meta meta_nl_ll = {
-+	.name = "wpa_nl_ctrl",
-+	.format = UDEBUG_FORMAT_PACKET,
-+	.sub_format = UDEBUG_DLT_NETLINK,
-+};
-+static const struct udebug_buf_meta meta_nl_tx = {
-+	.name = "wpa_nl_tx",
-+	.format = UDEBUG_FORMAT_PACKET,
-+	.sub_format = UDEBUG_DLT_NETLINK,
-+};
-+#define UDEBUG_FLAG_RX_FRAME	(1ULL << 0)
-+static const struct udebug_buf_flag rx_flags[] = {
-+	{  "rx_frame", UDEBUG_FLAG_RX_FRAME },
-+};
-+static const struct udebug_buf_meta meta_nl_rx = {
-+	.name = "wpa_nl_rx",
-+	.format = UDEBUG_FORMAT_PACKET,
-+	.sub_format = UDEBUG_DLT_NETLINK,
-+	.flags = rx_flags,
-+	.n_flags = ARRAY_SIZE(rx_flags),
-+};
-+static struct udebug_ubus_ring udebug_rings[] = {
-+	{
-+		.buf = &ud_log,
-+		.meta = &meta_log,
-+		.default_entries = 1024,
-+		.default_size = 64 * 1024
-+	},
-+	{
-+		.buf = &ud_nl[0],
-+		.meta = &meta_nl_rx,
-+		.default_entries = 1024,
-+		.default_size = 256 * 1024,
-+	},
-+	{
-+		.buf = &ud_nl[1],
-+		.meta = &meta_nl_tx,
-+		.default_entries = 1024,
-+		.default_size = 64 * 1024,
-+	},
-+	{
-+		.buf = &ud_nl[2],
-+		.meta = &meta_nl_ll,
-+		.default_entries = 1024,
-+		.default_size = 32 * 1024,
-+	}
-+};
-+char *udebug_service;
-+struct udebug_ubus ud_ubus;
-+
-+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_freq_info(uc_vm_t *vm, size_t nargs)
-+{
-+	uc_value_t *freq = uc_fn_arg(0);
-+	uc_value_t *sec = uc_fn_arg(1);
-+	int width = ucv_uint64_get(uc_fn_arg(2));
-+	int freq_val, center_idx, center_ofs;
-+	enum oper_chan_width chanwidth;
-+	enum hostapd_hw_mode hw_mode;
-+	u8 op_class, channel, tmp_channel;
-+	const char *modestr;
-+	int sec_channel = 0;
-+	uc_value_t *ret;
-+
-+	if (ucv_type(freq) != UC_INTEGER)
-+		return NULL;
-+
-+	freq_val = ucv_int64_get(freq);
-+	if (ucv_type(sec) == UC_INTEGER)
-+		sec_channel = ucv_int64_get(sec);
-+	else if (sec)
-+		return NULL;
-+	else if (freq_val > 4000)
-+		sec_channel = (freq_val / 20) & 1 ? 1 : -1;
-+	else
-+		sec_channel = freq_val < 2442 ? 1 : -1;
-+
-+	if (sec_channel != -1 && sec_channel != 1 && sec_channel != 0)
-+		return NULL;
-+
-+	switch (width) {
-+	case 0:
-+		chanwidth = CONF_OPER_CHWIDTH_USE_HT;
-+		break;
-+	case 1:
-+		chanwidth = CONF_OPER_CHWIDTH_80MHZ;
-+		break;
-+	case 2:
-+		chanwidth = CONF_OPER_CHWIDTH_160MHZ;
-+		break;
-+	default:
-+		return NULL;
-+	}
-+
-+	hw_mode = ieee80211_freq_to_channel_ext(freq_val, sec_channel,
-+						chanwidth, &op_class, &channel);
-+	switch (hw_mode) {
-+	case HOSTAPD_MODE_IEEE80211B:
-+		modestr = "b";
-+		break;
-+	case HOSTAPD_MODE_IEEE80211G:
-+		modestr = "g";
-+		break;
-+	case HOSTAPD_MODE_IEEE80211A:
-+		modestr = "a";
-+		break;
-+	case HOSTAPD_MODE_IEEE80211AD:
-+		modestr = "ad";
-+		break;
-+	default:
-+		return NULL;
-+	}
-+
-+	ret = ucv_object_new(vm);
-+	ucv_object_add(ret, "op_class", ucv_int64_new(op_class));
-+	ucv_object_add(ret, "channel", ucv_int64_new(channel));
-+	ucv_object_add(ret, "hw_mode", ucv_int64_new(hw_mode));
-+	ucv_object_add(ret, "hw_mode_str", ucv_get(ucv_string_new(modestr)));
-+	ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
-+	ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
-+
-+	if (!sec_channel)
-+		return ret;
-+
-+	if (freq_val >= 5900)
-+		center_ofs = 0;
-+	else if (freq_val >= 5745)
-+		center_ofs = 20;
-+	else
-+		center_ofs = 35;
-+	tmp_channel = channel - center_ofs;
-+	tmp_channel &= ~((8 << width) - 1);
-+	center_idx = tmp_channel + center_ofs + (4 << width) - 1;
-+
-+	if (freq_val < 3000)
-+		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(0));
-+	else
-+		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(center_idx));
-+	center_idx = (center_idx - channel) * 5 + freq_val;
-+	ucv_object_add(ret, "center_freq1", ucv_int64_new(center_idx));
-+
-+out:
-+	return ret;
-+}
-+
-+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;
-+}
-+
-+static void udebug_printf_hook(int level, const char *fmt, va_list ap)
-+{
-+	udebug_entry_init(&ud_log);
-+	udebug_entry_vprintf(&ud_log, fmt, ap);
-+	udebug_entry_add(&ud_log);
-+}
-+
-+static void udebug_hexdump_hook(int level, const char *title,
-+                const void *data, size_t len)
-+{
-+	char *buf;
-+
-+	udebug_entry_init(&ud_log);
-+	udebug_entry_printf(&ud_log, "%s - hexdump:", title);
-+	buf = udebug_entry_append(&ud_log, NULL, 3 * len);
-+	for (size_t i = 0; i < len; i++)
-+		buf += sprintf(buf, " %02x", *(uint8_t *)(data + i));
-+	udebug_entry_add(&ud_log);
-+}
-+
-+static void udebug_netlink_hook(int tx, const void *data, size_t len)
-+{
-+	struct {
-+		uint16_t pkttype;
-+		uint16_t arphdr;
-+		uint16_t _pad[5];
-+		uint16_t proto;
-+	} hdr = {
-+		.pkttype = host_to_be16(tx ? 7 : 6),
-+		.arphdr = host_to_be16(824),
-+		.proto = host_to_be16(16),
-+	};
-+	const struct nlmsghdr *nlh = data;
-+	const struct genlmsghdr *gnlh = data + NLMSG_HDRLEN;
-+	struct udebug_buf *buf = &ud_nl[!!tx];
-+
-+	if (nlh->nlmsg_type == 0x10)
-+		buf = &ud_nl[2];
-+	else if (!tx && gnlh->cmd == NL80211_CMD_FRAME &&
-+	         !(udebug_buf_flags(buf) & UDEBUG_FLAG_RX_FRAME))
-+		return;
-+
-+	if (!udebug_buf_valid(buf))
-+		return;
-+
-+	udebug_entry_init(buf);
-+	udebug_entry_append(buf, &hdr, sizeof(hdr));
-+	udebug_entry_append(buf, data, len);
-+	udebug_entry_add(buf);
-+}
-+
-+static void
-+wpa_udebug_config(struct udebug_ubus *ctx, struct blob_attr *data,
-+		  bool enabled)
-+{
-+	udebug_ubus_apply_config(&ud, udebug_rings, ARRAY_SIZE(udebug_rings),
-+				 data, enabled);
-+
-+	if (udebug_buf_valid(&ud_log)) {
-+		wpa_printf_hook = udebug_printf_hook;
-+		wpa_hexdump_hook = udebug_hexdump_hook;
-+	} else {
-+		wpa_printf_hook = NULL;
-+		wpa_hexdump_hook = NULL;
-+	}
-+
-+	if (udebug_buf_valid(&ud_nl[0]) ||
-+	    udebug_buf_valid(&ud_nl[1]) ||
-+	    udebug_buf_valid(&ud_nl[2]))
-+		wpa_netlink_hook = udebug_netlink_hook;
-+	else
-+		wpa_netlink_hook = NULL;
-+}
-+
-+uc_value_t *uc_wpa_udebug_set(uc_vm_t *vm, size_t nargs)
-+{
-+	uc_value_t *name = uc_fn_arg(0);
-+	uc_value_t *ubus = uc_fn_arg(1);
-+	static bool enabled = false;
-+	struct ubus_context *ctx;
-+	bool cur_en;
-+
-+	cur_en = ucv_type(name) == UC_STRING;
-+	ctx = ucv_resource_data(ubus, "ubus.connection");
-+	if (!ctx)
-+		cur_en = false;
-+
-+	if (enabled == cur_en)
-+		return ucv_boolean_new(true);
-+
-+	enabled = cur_en;
-+	if (enabled) {
-+		udebug_service = strdup(ucv_string_get(name));
-+		udebug_init(&ud);
-+		udebug_auto_connect(&ud, NULL);
-+		udebug_ubus_init(&ud_ubus, ctx, udebug_service, wpa_udebug_config);
-+	} else {
-+		udebug_ubus_free(&ud_ubus);
-+		for (size_t i = 0; i < ARRAY_SIZE(udebug_rings); i++)
-+			if (udebug_buf_valid(udebug_rings[i].buf))
-+				udebug_buf_free(udebug_rings[i].buf);
-+		udebug_free(&ud);
-+		free(udebug_service);
-+	}
-+
-+	return ucv_boolean_new(true);
-+}
-+
-+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;
-+}
-+
-+int wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val)
-+{
-+	uc_value_t *data;
-+	int i = 0;
-+
-+	while (ucv_array_get(reg, i))
-+		i++;
-+
-+	ucv_array_set(reg, i, ucv_get(val));
-+
-+	return 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);
-+	void **dataptr;
-+
-+	if (!val)
-+		return NULL;
-+
-+	ucv_array_set(reg, idx - 1, NULL);
-+	dataptr = ucv_resource_dataptr(val, NULL);
-+	if (dataptr)
-+		*dataptr = 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/src/utils/ucode.h b/src/utils/ucode.h
-new file mode 100644
-index 000000000..c083241e0
---- /dev/null
-+++ b/src/utils/ucode.h
-@@ -0,0 +1,30 @@
-+#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);
-+
-+int wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val);
-+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_udebug_set(uc_vm_t *vm, size_t nargs);
-+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);
-+uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs);
-+
-+#endif
-diff --git a/wpa_supplicant/ubus.c b/wpa_supplicant/ubus.c
-new file mode 100644
-index 000000000..1c477f0c0
---- /dev/null
-+++ b/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/wpa_supplicant/ubus.h b/wpa_supplicant/ubus.h
-new file mode 100644
-index 000000000..f6681cb26
---- /dev/null
-+++ b/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/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
-new file mode 100644
-index 000000000..397f85bde
---- /dev/null
-+++ b/wpa_supplicant/ucode.c
-@@ -0,0 +1,299 @@
-+#include "utils/includes.h"
-+#include "utils/common.h"
-+#include "utils/ucode.h"
-+#include "drivers/driver.h"
-+#include "ap/hostapd.h"
-+#include "wpa_supplicant_i.h"
-+#include "wps_supplicant.h"
-+#include "bss.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_s->ucode.idx = wpa_ucode_registry_add(iface_registry, val);
-+
-+	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);
-+}
-+
-+void wpas_ucode_update_state(struct wpa_supplicant *wpa_s)
-+{
-+	const char *state;
-+	uc_value_t *val;
-+
-+	val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
-+	if (!val)
-+		return;
-+
-+	if (wpa_ucode_call_prepare("state"))
-+		return;
-+
-+	state = wpa_supplicant_state_txt(wpa_s->wpa_state);
-+	uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
-+	uc_value_push(ucv_get(val));
-+	uc_value_push(ucv_get(ucv_string_new(state)));
-+	ucv_put(wpa_ucode_call(3));
-+	ucv_gc(vm);
-+}
-+
-+void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data)
-+{
-+	const char *state;
-+	uc_value_t *val;
-+
-+	if (event != EVENT_CH_SWITCH_STARTED)
-+		return;
-+
-+	val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
-+	if (!val)
-+		return;
-+
-+	if (wpa_ucode_call_prepare("event"))
-+		return;
-+
-+	uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
-+	uc_value_push(ucv_get(val));
-+	uc_value_push(ucv_get(ucv_string_new(event_to_string(event))));
-+	val = ucv_object_new(vm);
-+	uc_value_push(ucv_get(val));
-+
-+	if (event == EVENT_CH_SWITCH_STARTED) {
-+		ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count));
-+		ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq));
-+		ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
-+		ucv_object_add(val, "center_freq1", ucv_int64_new(data->ch_switch.cf1));
-+		ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
-+	}
-+
-+	ucv_put(wpa_ucode_call(4));
-+	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 *driver = ucv_object_get(info, "driver", NULL);
-+	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);
-+	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),
-+	};
-+
-+	if (driver) {
-+		const char *drvname;
-+		if (ucv_type(driver) != UC_STRING)
-+			goto out;
-+
-+		iface.driver = NULL;
-+		drvname = ucv_string_get(driver);
-+		for (int i = 0; wpa_drivers[i]; i++) {
-+			if (!strcmp(drvname, wpa_drivers[i]->name))
-+				iface.driver = wpa_drivers[i]->name;
-+		}
-+
-+		if (!iface.driver)
-+			goto out;
-+	}
-+
-+	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);
-+}
-+
-+static uc_value_t *
-+uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
-+{
-+	struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
-+	struct wpa_bss *bss;
-+	uc_value_t *ret, *val;
-+
-+	if (!wpa_s)
-+		return NULL;
-+
-+	ret = ucv_object_new(vm);
-+
-+	val = ucv_string_new(wpa_supplicant_state_txt(wpa_s->wpa_state));
-+	ucv_object_add(ret, "state", ucv_get(val));
-+
-+	bss = wpa_s->current_bss;
-+	if (bss) {
-+		int sec_chan = 0;
-+		const u8 *ie;
-+
-+		ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
-+		if (ie && ie[1] >= 2) {
-+			const struct ieee80211_ht_operation *ht_oper;
-+			int sec;
-+
-+			ht_oper = (const void *) (ie + 2);
-+			sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
-+			if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
-+				sec_chan = 1;
-+			else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
-+				sec_chan = -1;
-+		}
-+
-+		ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
-+		ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
-+	}
-+
-+#ifdef CONFIG_MESH
-+	if (wpa_s->ifmsh) {
-+		struct hostapd_iface *ifmsh = wpa_s->ifmsh;
-+
-+		ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(ifmsh->conf->secondary_channel));
-+		ucv_object_add(ret, "frequency", ucv_int64_new(ifmsh->freq));
-+	}
-+#endif
-+
-+	return 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 },
-+		{ "udebug_set", uc_wpa_udebug_set },
-+	};
-+	static const uc_function_list_t iface_fns[] = {
-+		{ "status", uc_wpas_iface_status },
-+	};
-+	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, "wpas.iface", iface_fns, NULL);
-+
-+	iface_registry = ucv_array_new(vm);
-+	uc_vm_registry_set(vm, "wpas.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/wpa_supplicant/ucode.h b/wpa_supplicant/ucode.h
-new file mode 100644
-index 000000000..a429a0ed8
---- /dev/null
-+++ b/wpa_supplicant/ucode.h
-@@ -0,0 +1,49 @@
-+#ifndef __WPAS_UCODE_H
-+#define __WPAS_UCODE_H
-+
-+#include "utils/ucode.h"
-+
-+struct wpa_global;
-+union wpa_event_data;
-+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);
-+void wpas_ucode_update_state(struct wpa_supplicant *wpa_s);
-+void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data);
-+#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)
-+{
-+}
-+
-+static inline void wpas_ucode_update_state(struct wpa_supplicant *wpa_s)
-+{
-+}
-+
-+static inline void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data)
-+{
-+}
-+
-+#endif
-+
-+#endif
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0027-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0027-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch
new file mode 100644
index 0000000..09ed283
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0027-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch
@@ -0,0 +1,189 @@
+From a6e614017f8f6c007fa3ee2cf1543e6460097f10 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 10:51:47 +0800
+Subject: [PATCH 027/126] mtk: hostapd: Add sta-assisted DFS state update
+ mechanism
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c                       | 20 ++++++++++++++++++++
+ src/ap/dfs.h                       |  3 +++
+ src/ap/drv_callbacks.c             | 28 ++++++++++++++++++++++++++++
+ src/common/wpa_ctrl.h              |  1 +
+ src/drivers/driver.h               | 14 ++++++++++++++
+ src/drivers/driver_nl80211_event.c |  6 ++++++
+ src/drivers/nl80211_copy.h         |  6 ++++++
+ 7 files changed, 78 insertions(+)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index aaaea0edc..4f8ef8111 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1535,6 +1535,26 @@ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+ }
+ 
+ 
++int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
++				 int ht_enabled, int chan_offset, int chan_width,
++				 int cf1, int cf2, u32 state)
++{
++	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_STA_UPDATE
++		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d state=%s",
++		freq, ht_enabled, chan_offset, chan_width, cf1, cf2,
++		(state == HOSTAPD_CHAN_DFS_AVAILABLE) ? "available" : "usable");
++
++	/* Proceed only if DFS is not offloaded to the driver */
++	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
++		return 0;
++
++	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
++		      cf1, cf2, state);
++
++	return 0;
++}
++
++
+ int hostapd_is_dfs_required(struct hostapd_iface *iface)
+ {
+ 	int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res;
+diff --git a/src/ap/dfs.h b/src/ap/dfs.h
+index c2556d2d9..25ba29ca1 100644
+--- a/src/ap/dfs.h
++++ b/src/ap/dfs.h
+@@ -30,6 +30,9 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+ 			     int ht_enabled,
+ 			     int chan_offset, int chan_width, int cf1, int cf2);
++int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
++				 int ht_enabled, int chan_offset, int chan_width,
++				 int cf1, int cf2, u32 state);
+ int hostapd_is_dfs_required(struct hostapd_iface *iface);
+ int hostapd_is_dfs_chan_available(struct hostapd_iface *iface);
+ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 6e76f697a..1d40943d6 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2270,6 +2270,24 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
+ 			      radar->cf1, radar->cf2);
+ }
+ 
++static void hostapd_event_dfs_sta_cac_skipped(struct hostapd_data *hapd,
++					      struct dfs_event *radar)
++{
++	wpa_printf(MSG_DEBUG, "DFS CAC skipped (by STA) on %d MHz", radar->freq);
++	hostapd_dfs_sta_update_state(hapd->iface, radar->freq, radar->ht_enabled,
++				     radar->chan_offset, radar->chan_width,
++				     radar->cf1, radar->cf2, HOSTAPD_CHAN_DFS_AVAILABLE);
++}
++
++static void hostapd_event_dfs_sta_cac_expired(struct hostapd_data *hapd,
++					      struct dfs_event *radar)
++{
++	wpa_printf(MSG_DEBUG, "DFS CAC expired (by STA) on %d MHz", radar->freq);
++	hostapd_dfs_sta_update_state(hapd->iface, radar->freq, radar->ht_enabled,
++				     radar->chan_offset, radar->chan_width,
++				     radar->cf1, radar->cf2, HOSTAPD_CHAN_DFS_USABLE);
++}
++
+ #endif /* NEED_AP_MLME */
+ 
+ 
+@@ -2687,6 +2705,16 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ 		hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
+ 		hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
+ 		break;
++	case EVENT_DFS_STA_CAC_SKIPPED:
++		if (!data)
++			break;
++		hostapd_event_dfs_sta_cac_skipped(hapd, &data->dfs_event);
++		break;
++	case EVENT_DFS_STA_CAC_EXPIRED:
++		if (!data)
++			break;
++		hostapd_event_dfs_sta_cac_expired(hapd, &data->dfs_event);
++		break;
+ 	case EVENT_CHANNEL_LIST_CHANGED:
+ 		/* channel list changed (regulatory?), update channel list */
+ 		/* TODO: check this. hostapd_get_hw_features() initializes
+diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
+index d1ce1dd29..4700c5487 100644
+--- a/src/common/wpa_ctrl.h
++++ b/src/common/wpa_ctrl.h
+@@ -383,6 +383,7 @@ extern "C" {
+ #define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED "
+ #define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED "
+ #define DFS_EVENT_PRE_CAC_EXPIRED "DFS-PRE-CAC-EXPIRED "
++#define DFS_EVENT_STA_UPDATE "DFS-STA-UPDATE "
+ 
+ #define AP_CSA_FINISHED "AP-CSA-FINISHED "
+ 
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 348cf9c88..cccc44147 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5926,6 +5926,20 @@ enum wpa_event_type {
+ 	 * EVENT_LINK_RECONFIG - Notification that AP links removed
+ 	 */
+ 	EVENT_LINK_RECONFIG,
++
++	/**
++	 * EVENT_DFS_STA_CAC_SKIPPED - Notification that CAC has been skipped
++	 *
++	 * The channel in the notification is now marked as available.
++	 */
++	EVENT_DFS_STA_CAC_SKIPPED,
++
++	/**
++	 * EVENT_DFS_STA_CAC_EXPIRED - Notification that CAC has expired
++	 *
++	 * The channel in the notification is now marked as usable.
++	 */
++	EVENT_DFS_STA_CAC_EXPIRED,
+ };
+ 
+ 
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 1787dbd99..8ba479861 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -2539,6 +2539,12 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
+ 	case NL80211_RADAR_CAC_STARTED:
+ 		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
+ 		break;
++	case NL80211_RADAR_STA_CAC_SKIPPED:
++		wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
++		break;
++	case NL80211_RADAR_STA_CAC_EXPIRED:
++		wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_EXPIRED, &data);
++		break;
+ 	default:
+ 		wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
+ 			   "received", event_type);
+diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
+index f97f5adc8..622fa71fd 100644
+--- a/src/drivers/nl80211_copy.h
++++ b/src/drivers/nl80211_copy.h
+@@ -6843,6 +6843,10 @@ enum nl80211_smps_mode {
+  *	applicable for ETSI dfs domain where pre-CAC is valid for ever.
+  * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
+  *	should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
++ * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
++ *	when receiving CSA/assoc resp
++ * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
++ *	when STA is disconnected or leaving the channel
+  */
+ enum nl80211_radar_event {
+ 	NL80211_RADAR_DETECTED,
+@@ -6851,6 +6855,8 @@ enum nl80211_radar_event {
+ 	NL80211_RADAR_NOP_FINISHED,
+ 	NL80211_RADAR_PRE_CAC_EXPIRED,
+ 	NL80211_RADAR_CAC_STARTED,
++	NL80211_RADAR_STA_CAC_SKIPPED,
++	NL80211_RADAR_STA_CAC_EXPIRED,
+ };
+ 
+ /**
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0028-hostapd-sync-2024-01-18-openwrt-trunk-patch-folder.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0028-hostapd-sync-2024-01-18-openwrt-trunk-patch-folder.patch
deleted file mode 100644
index 1dff1f3..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0028-hostapd-sync-2024-01-18-openwrt-trunk-patch-folder.patch
+++ /dev/null
@@ -1,14442 +0,0 @@
-From 2eff271199b22ce44432d531e8b44809368ac5d2 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Tue, 23 Jan 2024 17:07:17 +0800
-Subject: [PATCH 028/104] hostapd: sync 2024-01-18 openwrt/trunk patch folder
-
----
- hostapd/Makefile                          |  149 +-
- hostapd/config_file.c                     |   39 +-
- hostapd/ctrl_iface.c                      |    4 +
- hostapd/defconfig                         |   15 +-
- hostapd/hostapd_cli.c                     |   10 +-
- hostapd/main.c                            |   21 +-
- src/ap/acs.c                              |    5 +-
- src/ap/airtime_policy.c                   |   15 +-
- src/ap/ap_config.h                        |    8 +
- src/ap/ap_drv_ops.c                       |    5 +-
- src/ap/ap_drv_ops.h                       |   21 +-
- src/ap/beacon.c                           |   14 +-
- src/ap/ctrl_iface_ap.c                    |   45 +-
- src/ap/dfs.c                              |   22 +-
- src/ap/drv_callbacks.c                    |   16 +-
- src/ap/hostapd.c                          |   51 +-
- src/ap/hostapd.h                          |   42 +
- src/ap/hw_features.c                      |    3 +-
- src/ap/ieee802_11.c                       |   50 +-
- src/ap/ieee802_11_ht.c                    |   15 +
- src/ap/ieee802_11_shared.c                |    2 -
- src/ap/ieee802_11_vht.c                   |   17 +
- src/ap/ieee802_1x.c                       |    6 +
- src/ap/ndisc_snoop.c                      |    1 +
- src/ap/rrm.c                              |    8 +
- src/ap/sta_info.c                         |   35 +-
- src/ap/sta_info.h                         |   12 +-
- src/ap/vlan_full.c                        |    4 +
- src/ap/vlan_init.c                        |    8 +-
- src/ap/wnm_ap.c                           |   16 +-
- src/ap/wpa_auth.c                         |    3 +-
- src/ap/wpa_auth_glue.c                    |    9 +-
- src/ap/wps_hostapd.c                      |    6 +-
- src/ap/x_snoop.c                          |   22 +-
- src/common/dpp_crypto.c                   |   10 +-
- src/common/hw_features_common.c           |    1 +
- src/common/ieee802_11_defs.h              |    2 +
- src/common/sae.c                          |    7 +
- src/common/wpa_common.c                   |   40 +-
- src/common/wpa_ctrl.c                     |   10 +-
- src/crypto/Makefile                       |  129 +-
- src/crypto/crypto_mbedtls.c               | 4228 +++++++++++++++++++++
- src/crypto/crypto_module_tests.c          |  134 +
- src/crypto/crypto_wolfssl.c               |   18 +
- src/crypto/tls_mbedtls.c                  | 3313 ++++++++++++++++
- src/drivers/driver.h                      |   36 +-
- src/drivers/driver_nl80211.c              |  300 +-
- src/drivers/driver_nl80211_capa.c         |    4 +
- src/drivers/driver_nl80211_event.c        |    5 +
- src/drivers/driver_nl80211_scan.c         |    2 +-
- src/drivers/drivers.c                     |    4 +
- src/drivers/drivers.mak                   |    4 +-
- src/drivers/rfkill.h                      |   16 +
- src/radius/radius_client.c                |   34 +
- src/radius/radius_client.h                |    2 +
- src/radius/radius_das.c                   |  201 +-
- src/radius/radius_das.h                   |    1 +
- src/radius/radius_server.c                |   61 +-
- src/rsn_supp/wpa.c                        |    3 +
- src/tls/Makefile                          |   11 +
- src/utils/eloop.c                         |   20 +-
- src/utils/eloop.h                         |    8 +
- src/utils/uloop.c                         |   64 +
- src/utils/wpa_debug.c                     |   49 +-
- src/utils/wpa_debug.h                     |   73 +-
- tests/Makefile                            |   65 +-
- tests/hwsim/example-hostapd.config        |   10 +-
- tests/hwsim/example-wpa_supplicant.config |   11 +-
- tests/hwsim/test_ap_eap.py                |  112 +-
- tests/hwsim/test_ap_ft.py                 |    4 +-
- tests/hwsim/test_authsrv.py               |    9 +-
- tests/hwsim/test_dpp.py                   |   19 +-
- tests/hwsim/test_erp.py                   |   16 +-
- tests/hwsim/test_fils.py                  |   12 +
- tests/hwsim/test_pmksa_cache.py           |    4 +-
- tests/hwsim/test_sae.py                   |    7 +
- tests/hwsim/test_suite_b.py               |    3 +
- tests/hwsim/test_wpas_ctrl.py             |    2 +-
- tests/hwsim/utils.py                      |    8 +-
- tests/test-crypto_module.c                |   16 +
- tests/test-https.c                        |   12 +-
- tests/test-https_server.c                 |   12 +-
- wpa_supplicant/Makefile                   |  144 +-
- wpa_supplicant/ap.c                       |   22 +-
- wpa_supplicant/config.c                   |   95 +
- wpa_supplicant/config_file.c              |    8 +-
- wpa_supplicant/config_ssid.h              |    7 +
- wpa_supplicant/ctrl_iface.c               |   12 +-
- wpa_supplicant/defconfig                  |    6 +-
- wpa_supplicant/eapol_test.c               |   11 +
- wpa_supplicant/events.c                   |   13 +-
- wpa_supplicant/main.c                     |   14 +-
- wpa_supplicant/mesh.c                     |    3 +
- wpa_supplicant/wpa_cli.c                  |    9 +
- wpa_supplicant/wpa_priv.c                 |    8 +-
- wpa_supplicant/wpa_supplicant.c           |   75 +-
- wpa_supplicant/wpa_supplicant_i.h         |    6 +
- wpa_supplicant/wps_supplicant.c           |    3 +
- wpa_supplicant/wps_supplicant.h           |    3 +-
- 99 files changed, 9786 insertions(+), 464 deletions(-)
- create mode 100644 src/crypto/crypto_mbedtls.c
- create mode 100644 src/crypto/tls_mbedtls.c
- create mode 100644 src/utils/uloop.c
- create mode 100644 tests/test-crypto_module.c
-
-diff --git a/hostapd/Makefile b/hostapd/Makefile
-index 405e05e5f..f5c1dc029 100644
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -1,6 +1,7 @@
- ALL=hostapd hostapd_cli
- CONFIG_FILE = .config
- 
-+-include $(if $(MULTICALL), ../wpa_supplicant/.config)
- include ../src/build.rules
- 
- ifdef LIBS
-@@ -62,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
-@@ -171,6 +176,24 @@ OBJS += ../src/common/hw_features_common.o
- 
- OBJS += ../src/eapol_auth/eapol_auth_sm.o
- 
-+ifdef CONFIG_UBUS
-+CFLAGS += -DUBUS_SUPPORT
-+OBJS += ../src/ap/ubus.o
-+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
- CFLAGS += -O0 -fprofile-arcs -ftest-coverage -U_FORTIFY_SOURCE
-@@ -205,7 +228,8 @@ endif
- 
- ifdef CONFIG_NO_VLAN
- CFLAGS += -DCONFIG_NO_VLAN
--else
-+endif
-+ifneq ($(findstring CONFIG_NO_VLAN,$(CFLAGS)), CONFIG_NO_VLAN)
- OBJS += ../src/ap/vlan_init.o
- OBJS += ../src/ap/vlan_ifconfig.o
- OBJS += ../src/ap/vlan.o
-@@ -225,6 +249,9 @@ endif
- ifdef CONFIG_NO_CTRL_IFACE
- CFLAGS += -DCONFIG_NO_CTRL_IFACE
- else
-+ifdef CONFIG_CTRL_IFACE_MIB
-+CFLAGS += -DCONFIG_CTRL_IFACE_MIB
-+endif
- ifeq ($(CONFIG_CTRL_IFACE), udp)
- CFLAGS += -DCONFIG_CTRL_IFACE_UDP
- else
-@@ -332,6 +359,7 @@ ifdef CONFIG_FILS
- CFLAGS += -DCONFIG_FILS
- OBJS += ../src/ap/fils_hlp.o
- NEED_SHA384=y
-+NEED_HMAC_SHA384_KDF=y
- NEED_AES_SIV=y
- ifdef CONFIG_FILS_SK_PFS
- CFLAGS += -DCONFIG_FILS_SK_PFS
-@@ -364,10 +392,14 @@ CFLAGS += -DCONFIG_MBO
- OBJS += ../src/ap/mbo_ap.o
- endif
- 
-+ifndef MULTICALL
-+CFLAGS += -DNO_SUPPLICANT
-+endif
-+
- include ../src/drivers/drivers.mak
--OBJS += $(DRV_AP_OBJS)
--CFLAGS += $(DRV_AP_CFLAGS)
--LDFLAGS += $(DRV_AP_LDFLAGS)
-+OBJS += $(sort $(DRV_AP_OBJS) $(if $(MULTICALL),$(DRV_WPA_OBJS)))
-+CFLAGS += $(DRV_AP_CFLAGS) $(if $(MULTICALL),$(DRV_WPA_CFLAGS))
-+LDFLAGS += $(DRV_AP_LDFLAGS) $(if $(MULTICALL),$(DRV_WPA_LDFLAGS))
- LIBS += $(DRV_AP_LIBS)
- 
- ifdef CONFIG_L2_PACKET
-@@ -714,6 +746,7 @@ CFLAGS += -DCONFIG_TLSV12
- endif
- 
- ifeq ($(CONFIG_TLS), wolfssl)
-+CFLAGS += -DCONFIG_TLS_WOLFSSL
- CONFIG_CRYPTO=wolfssl
- ifdef TLS_FUNCS
- OBJS += ../src/crypto/tls_wolfssl.o
-@@ -734,6 +767,7 @@ endif
- endif
- 
- ifeq ($(CONFIG_TLS), openssl)
-+CFLAGS += -DCONFIG_TLS_OPENSSL
- CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
- CONFIG_CRYPTO=openssl
- ifdef TLS_FUNCS
-@@ -763,7 +797,39 @@ endif
- CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
- endif
- 
-+ifeq ($(CONFIG_TLS), mbedtls)
-+CFLAGS += -DCONFIG_TLS_MBEDTLS
-+ifndef CONFIG_CRYPTO
-+CONFIG_CRYPTO=mbedtls
-+endif
-+ifdef TLS_FUNCS
-+OBJS += ../src/crypto/tls_mbedtls.o
-+LIBS += -lmbedtls
-+ifndef CONFIG_DPP
-+LIBS += -lmbedx509
-+endif
-+endif
-+OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+HOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+SOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+ifeq ($(CONFIG_CRYPTO), mbedtls)
-+ifdef CONFIG_DPP
-+LIBS += -lmbedx509
-+LIBS_h += -lmbedx509
-+LIBS_n += -lmbedx509
-+LIBS_s += -lmbedx509
-+endif
-+LIBS += -lmbedcrypto
-+LIBS_h += -lmbedcrypto
-+LIBS_n += -lmbedcrypto
-+LIBS_s += -lmbedcrypto
-+# XXX: create a config option?
-+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
-+endif
-+endif
-+
- ifeq ($(CONFIG_TLS), gnutls)
-+CFLAGS += -DCONFIG_TLS_GNUTLS
- ifndef CONFIG_CRYPTO
- # default to libgcrypt
- CONFIG_CRYPTO=gnutls
-@@ -794,6 +860,7 @@ endif
- endif
- 
- ifeq ($(CONFIG_TLS), internal)
-+CFLAGS += -DCONFIG_TLS_INTERNAL
- ifndef CONFIG_CRYPTO
- CONFIG_CRYPTO=internal
- endif
-@@ -872,6 +939,7 @@ endif
- endif
- 
- ifeq ($(CONFIG_TLS), linux)
-+CFLAGS += -DCONFIG_TLS_INTERNAL
- OBJS += ../src/crypto/crypto_linux.o
- ifdef TLS_FUNCS
- OBJS += ../src/crypto/crypto_internal-rsa.o
-@@ -942,9 +1010,11 @@ endif
- 
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-wrap.o
- endif
- endif
-+endif
- ifdef NEED_AES_EAX
- AESOBJS += ../src/crypto/aes-eax.o
- NEED_AES_CTR=y
-@@ -954,38 +1024,48 @@ AESOBJS += ../src/crypto/aes-siv.o
- NEED_AES_CTR=y
- endif
- ifdef NEED_AES_CTR
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-ctr.o
- endif
-+endif
- ifdef NEED_AES_ENCBLOCK
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-encblock.o
- endif
-+endif
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-omac1.o
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_UNWRAP
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- NEED_AES_DEC=y
- AESOBJS += ../src/crypto/aes-unwrap.o
- endif
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_CBC
- NEED_AES_DEC=y
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-cbc.o
- endif
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_DEC
- ifdef CONFIG_INTERNAL_AES
- AESOBJS += ../src/crypto/aes-internal-dec.o
-@@ -1000,12 +1080,16 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-prf.o
-+endif
- ifdef CONFIG_INTERNAL_SHA1
- SHA1OBJS += ../src/crypto/sha1-internal.o
- ifdef NEED_FIPS186_2_PRF
-@@ -1014,16 +1098,22 @@ endif
- endif
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
- endif
- endif
-+endif
- ifdef NEED_T_PRF
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-tprf.o
- endif
-+endif
- ifdef NEED_TLS_PRF
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-tlsprf.o
- endif
- endif
-+endif
- 
- ifdef NEED_SHA1
- OBJS += $(SHA1OBJS)
-@@ -1033,11 +1123,13 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/md5.o
- endif
- endif
- endif
- endif
-+endif
- 
- ifdef NEED_MD5
- ifdef CONFIG_INTERNAL_MD5
-@@ -1076,56 +1168,81 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256-prf.o
-+endif
- ifdef CONFIG_INTERNAL_SHA256
- OBJS += ../src/crypto/sha256-internal.o
- endif
- ifdef NEED_TLS_PRF_SHA256
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256-tlsprf.o
- endif
-+endif
- ifdef NEED_TLS_PRF_SHA384
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-tlsprf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA256_KDF
-+CFLAGS += -DCONFIG_HMAC_SHA256_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256-kdf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA384_KDF
-+CFLAGS += -DCONFIG_HMAC_SHA384_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-kdf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA512_KDF
-+CFLAGS += -DCONFIG_HMAC_SHA512_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512-kdf.o
- endif
-+endif
- ifdef NEED_SHA384
- CFLAGS += -DCONFIG_SHA384
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-prf.o
- endif
-+endif
- ifdef NEED_SHA512
- CFLAGS += -DCONFIG_SHA512
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512-prf.o
- endif
-+endif
- 
- ifdef CONFIG_INTERNAL_SHA384
- CFLAGS += -DCONFIG_INTERNAL_SHA384
-@@ -1170,11 +1287,13 @@ HOBJS += $(SHA1OBJS)
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- HOBJS += ../src/crypto/md5.o
- endif
- endif
- endif
- endif
-+endif
- 
- ifdef CONFIG_RADIUS_SERVER
- CFLAGS += -DRADIUS_SERVER
-@@ -1311,8 +1430,14 @@ install: $(addprefix $(DESTDIR)$(BINDIR)/,$(ALL))
- _OBJS_VAR := OBJS
- include ../src/objs.mk
- 
-+hostapd_multi.a: $(BCHECK) $(OBJS)
-+	$(Q)$(CC) -c -o hostapd_multi.o -Dmain=hostapd_main $(CFLAGS) main.c
-+	@$(E) "  CC " $<
-+	@rm -f $@
-+	@$(AR) cr $@ hostapd_multi.o $(OBJS)
-+
- hostapd: $(OBJS)
--	$(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
-+	+$(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
- 	@$(E) "  LD " $@
- 
- ifdef CONFIG_WPA_TRACE
-@@ -1323,7 +1448,7 @@ _OBJS_VAR := OBJS_c
- include ../src/objs.mk
- 
- hostapd_cli: $(OBJS_c)
--	$(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
-+	+$(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
- 	@$(E) "  LD " $@
- 
- NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS)
-@@ -1347,7 +1472,9 @@ NOBJS += ../src/utils/trace.o
- endif
- 
- HOBJS += hlr_auc_gw.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).o ../src/utils/wpabuf.o ../src/crypto/milenage.o
-+ifneq ($(CONFIG_TLS), mbedtls)
- HOBJS += ../src/crypto/aes-encblock.o
-+endif
- ifdef CONFIG_INTERNAL_AES
- HOBJS += ../src/crypto/aes-internal.o
- HOBJS += ../src/crypto/aes-internal-enc.o
-@@ -1370,13 +1497,17 @@ SOBJS += ../src/common/sae.o
- SOBJS += ../src/common/sae_pk.o
- SOBJS += ../src/common/dragonfly.o
- SOBJS += $(AESOBJS)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SOBJS += ../src/crypto/sha256-prf.o
- SOBJS += ../src/crypto/sha384-prf.o
- SOBJS += ../src/crypto/sha512-prf.o
-+endif
- SOBJS += ../src/crypto/dh_groups.o
-+ifneq ($(CONFIG_TLS), mbedtls)
- SOBJS += ../src/crypto/sha256-kdf.o
- SOBJS += ../src/crypto/sha384-kdf.o
- SOBJS += ../src/crypto/sha512-kdf.o
-+endif
- 
- _OBJS_VAR := NOBJS
- include ../src/objs.mk
-@@ -1385,6 +1516,12 @@ include ../src/objs.mk
- _OBJS_VAR := SOBJS
- include ../src/objs.mk
- 
-+dump_cflags:
-+	@printf "%s " "$(CFLAGS)"
-+
-+dump_ldflags:
-+	@printf "%s " "$(LDFLAGS) $(LIBS) $(EXTRALIBS)"
-+
- nt_password_hash: $(NOBJS)
- 	$(Q)$(CC) $(LDFLAGS) -o nt_password_hash $(NOBJS) $(LIBS_n)
- 	@$(E) "  LD " $@
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 261905368..0094db279 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -1229,6 +1229,8 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf,
- 		conf->vht_capab |= VHT_CAP_RX_ANTENNA_PATTERN;
- 	if (os_strstr(capab, "[TX-ANTENNA-PATTERN]"))
- 		conf->vht_capab |= VHT_CAP_TX_ANTENNA_PATTERN;
-+	if (os_strstr(capab, "[EXT-NSS-BW-SUPP]"))
-+		conf->vht_capab |= VHT_CAP_EXTENDED_NSS_BW_SUPPORT;
- 	return 0;
- }
- #endif /* CONFIG_IEEE80211AC */
-@@ -1678,6 +1680,8 @@ static int parse_anqp_elem(struct hostapd_bss_config *bss, char *buf, int line)
- 	return 0;
- }
- 
-+#endif /* CONFIG_INTERWORKING */
-+
- 
- static int parse_qos_map_set(struct hostapd_bss_config *bss,
- 			     char *buf, int line)
-@@ -1719,8 +1723,6 @@ static int parse_qos_map_set(struct hostapd_bss_config *bss,
- 	return 0;
- }
- 
--#endif /* CONFIG_INTERWORKING */
--
- 
- #ifdef CONFIG_HS20
- static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf,
-@@ -2630,8 +2632,12 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 			   sizeof(conf->bss[0]->iface));
- 	} else if (os_strcmp(buf, "bridge") == 0) {
- 		os_strlcpy(bss->bridge, pos, sizeof(bss->bridge));
-+		if (!bss->wds_bridge[0])
-+			os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge));
- 	} else if (os_strcmp(buf, "bridge_hairpin") == 0) {
- 		bss->bridge_hairpin = atoi(pos);
-+	} else if (os_strcmp(buf, "snoop_iface") == 0) {
-+		os_strlcpy(bss->snoop_iface, pos, sizeof(bss->snoop_iface));
- 	} else if (os_strcmp(buf, "vlan_bridge") == 0) {
- 		os_strlcpy(bss->vlan_bridge, pos, sizeof(bss->vlan_bridge));
- 	} else if (os_strcmp(buf, "wds_bridge") == 0) {
-@@ -2998,6 +3004,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 	} else if (os_strcmp(buf, "iapp_interface") == 0) {
- 		wpa_printf(MSG_INFO, "DEPRECATED: iapp_interface not used");
- #endif /* CONFIG_IAPP */
-+	} else if (os_strcmp(buf, "dynamic_own_ip_addr") == 0) {
-+		bss->dynamic_own_ip_addr = atoi(pos);
- 	} else if (os_strcmp(buf, "own_ip_addr") == 0) {
- 		if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) {
- 			wpa_printf(MSG_ERROR,
-@@ -3222,6 +3230,14 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 				   line, bss->max_num_sta, MAX_STA_COUNT);
- 			return 1;
- 		}
-+	} else if (os_strcmp(buf, "iface_max_num_sta") == 0) {
-+		conf->max_num_sta = atoi(pos);
-+		if (conf->max_num_sta < 0 ||
-+		    conf->max_num_sta > MAX_STA_COUNT) {
-+			wpa_printf(MSG_ERROR, "Line %d: Invalid max_num_sta=%d; allowed range 0..%d",
-+				   line, conf->max_num_sta, MAX_STA_COUNT);
-+			return 1;
-+		}
- 	} else if (os_strcmp(buf, "wpa") == 0) {
- 		bss->wpa = atoi(pos);
- 	} else if (os_strcmp(buf, "extended_key_id") == 0) {
-@@ -3373,6 +3389,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		wpa_printf(MSG_INFO,
- 			   "Line %d: Obsolete peerkey parameter ignored", line);
- #ifdef CONFIG_IEEE80211R_AP
-+	} else if (os_strcmp(buf, "ft_iface") == 0) {
-+		os_strlcpy(bss->ft_iface, pos, sizeof(bss->ft_iface));
- 	} else if (os_strcmp(buf, "mobility_domain") == 0) {
- 		if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
- 		    hexstr2bin(pos, bss->mobility_domain,
-@@ -3742,6 +3760,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- #ifndef CONFIG_NO_VLAN
- 	} else if (os_strcmp(buf, "dynamic_vlan") == 0) {
- 		bss->ssid.dynamic_vlan = atoi(pos);
-+	} else if (os_strcmp(buf, "vlan_no_bridge") == 0) {
-+		bss->ssid.vlan_no_bridge = atoi(pos);
- 	} else if (os_strcmp(buf, "per_sta_vif") == 0) {
- 		bss->ssid.per_sta_vif = atoi(pos);
- 	} else if (os_strcmp(buf, "vlan_file") == 0) {
-@@ -3839,6 +3859,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		if (bss->ocv && !bss->ieee80211w)
- 			bss->ieee80211w = 1;
- #endif /* CONFIG_OCV */
-+	} else if (os_strcmp(buf, "noscan") == 0) {
-+		conf->noscan = atoi(pos);
-+	} else if (os_strcmp(buf, "ht_coex") == 0) {
-+		conf->no_ht_coex = !atoi(pos);
- 	} else if (os_strcmp(buf, "ieee80211n") == 0) {
- 		conf->ieee80211n = atoi(pos);
- 	} else if (os_strcmp(buf, "ht_capab") == 0) {
-@@ -3887,6 +3911,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 	} else if (os_strcmp(buf, "he_bss_color") == 0) {
- 		conf->he_op.he_bss_color = atoi(pos) & 0x3f;
- 		conf->he_op.he_bss_color_disabled = 0;
-+		if (atoi(pos) > 63)
-+			conf->he_op.he_bss_color = os_random() % 63 + 1;
- 	} else if (os_strcmp(buf, "he_bss_color_partial") == 0) {
- 		conf->he_op.he_bss_color_partial = atoi(pos);
- 	} else if (os_strcmp(buf, "he_default_pe_duration") == 0) {
-@@ -4520,10 +4546,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		bss->gas_frag_limit = val;
- 	} else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
- 		bss->gas_comeback_delay = atoi(pos);
-+#endif /* CONFIG_INTERWORKING */
- 	} else if (os_strcmp(buf, "qos_map_set") == 0) {
- 		if (parse_qos_map_set(bss, pos, line) < 0)
- 			return 1;
--#endif /* CONFIG_INTERWORKING */
- #ifdef CONFIG_RADIUS_TEST
- 	} else if (os_strcmp(buf, "dump_msk_file") == 0) {
- 		os_free(bss->dump_msk_file);
-@@ -5347,7 +5373,12 @@ struct hostapd_config * hostapd_config_read(const char *fname)
- 	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);
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 10cb186f1..f76226cf4 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3876,6 +3876,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 						      reply_size);
- 	} else if (os_strcmp(buf, "STATUS-DRIVER") == 0) {
- 		reply_len = hostapd_drv_status(hapd, reply, reply_size);
-+#ifdef CONFIG_CTRL_IFACE_MIB
- 	} else if (os_strcmp(buf, "MIB") == 0) {
- 		reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
- 		if (reply_len >= 0) {
-@@ -3917,6 +3918,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 	} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
- 		reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
- 							reply_size);
-+#endif
- 	} else if (os_strcmp(buf, "ATTACH") == 0) {
- 		if (hostapd_ctrl_iface_attach(hapd, from, fromlen, NULL))
- 			reply_len = -1;
-@@ -5464,6 +5466,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;
-@@ -5565,6 +5568,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/hostapd/defconfig b/hostapd/defconfig
-index 66bf894eb..f716553bb 100644
---- a/hostapd/defconfig
-+++ b/hostapd/defconfig
-@@ -6,9 +6,21 @@
- # just setting VARIABLE=n is not disabling that variable.
- #
- # This file is included in Makefile, so variables like CFLAGS and LIBS can also
--# be modified from here. In most cass, these lines should use += in order not
-+# be modified from here. In most cases, these lines should use += in order not
- # to override previous values of the variables.
- 
-+
-+# Uncomment following two lines and fix the paths if you have installed TLS
-+# libraries in a non-default location
-+#CFLAGS += -I/usr/local/openssl/include
-+#LIBS += -L/usr/local/openssl/lib
-+
-+# Some Red Hat versions seem to include kerberos header files from OpenSSL, but
-+# the kerberos files are not in the default include path. Following line can be
-+# used to fix build issues on such systems (krb5.h not found).
-+#CFLAGS += -I/usr/include/kerberos
-+
-+
- # Driver interface for Host AP driver
- CONFIG_DRIVER_HOSTAP=y
- 
-@@ -281,6 +293,7 @@ CONFIG_IPV6=y
- # openssl = OpenSSL (default)
- # gnutls = GnuTLS
- # internal = Internal TLSv1 implementation (experimental)
-+# mbedtls = mbed TLS
- # linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
- # none = Empty template
- #CONFIG_TLS=openssl
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index a9d326de8..a469b1f4d 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -401,7 +401,6 @@ static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
- }
- 
- 
--#ifdef CONFIG_TAXONOMY
- static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
- 				     char *argv[])
- {
-@@ -414,7 +413,6 @@ static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
- 	os_snprintf(buf, sizeof(buf), "SIGNATURE %s", argv[0]);
- 	return wpa_ctrl_command(ctrl, buf);
- }
--#endif /* CONFIG_TAXONOMY */
- 
- 
- static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
-@@ -431,7 +429,6 @@ static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
- }
- 
- 
--#ifdef CONFIG_WPS
- static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
- 				   char *argv[])
- {
-@@ -657,7 +654,6 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
- 			 ssid_hex, argv[1]);
- 	return wpa_ctrl_command(ctrl, buf);
- }
--#endif /* CONFIG_WPS */
- 
- 
- static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
-@@ -757,7 +753,7 @@ static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd,
- 	}
- 
- 	buf[len] = '\0';
--	if (memcmp(buf, "FAIL", 4) == 0)
-+	if (memcmp(buf, "FAIL", 4) == 0 || memcmp(buf, "UNKNOWN COMMAND", 15) == 0)
- 		return -1;
- 	if (print)
- 		printf("%s", buf);
-@@ -1670,13 +1666,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 	{ "disassociate", hostapd_cli_cmd_disassociate,
- 	  hostapd_complete_stations,
- 	  "<addr> = disassociate a station" },
--#ifdef CONFIG_TAXONOMY
- 	{ "signature", hostapd_cli_cmd_signature, hostapd_complete_stations,
- 	  "<addr> = get taxonomy signature for a station" },
--#endif /* CONFIG_TAXONOMY */
- 	{ "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations,
- 	  "<addr> = send SA Query to a station" },
--#ifdef CONFIG_WPS
- 	{ "wps_pin", hostapd_cli_cmd_wps_pin, NULL,
- 	  "<uuid> <pin> [timeout] [addr] = add WPS Enrollee PIN" },
- 	{ "wps_check_pin", hostapd_cli_cmd_wps_check_pin, NULL,
-@@ -1701,7 +1694,6 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 	  "<SSID> <auth> <encr> <key> = configure AP" },
- 	{ "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL,
- 	  "= show current WPS status" },
--#endif /* CONFIG_WPS */
- 	{ "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL,
- 	  "= send Disassociation Imminent notification" },
- 	{ "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL,
-diff --git a/hostapd/main.c b/hostapd/main.c
-index 524a10274..0ccd4a5d7 100644
---- a/hostapd/main.c
-+++ b/hostapd/main.c
-@@ -31,7 +31,7 @@
- #include "config_file.h"
- #include "eap_register.h"
- #include "ctrl_iface.h"
--
-+#include "build_features.h"
- 
- struct hapd_global {
- 	void **drv_priv;
-@@ -40,6 +40,7 @@ struct hapd_global {
- 
- static struct hapd_global global;
- 
-+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,
-@@ -690,6 +691,11 @@ fail:
- 	return -1;
- }
- 
-+void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
-+                       union wpa_event_data *data);
-+
-+void hostapd_wpa_event_global(void *ctx, enum wpa_event_type event,
-+ 				 union wpa_event_data *data);
- 
- #ifdef CONFIG_WPS
- static int gen_uuid(const char *txt_addr)
-@@ -781,6 +787,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;
-@@ -806,8 +817,10 @@ int main(int argc, char *argv[])
- 		return -1;
- #endif /* CONFIG_DPP */
- 
-+	wpa_supplicant_event = hostapd_wpa_event;
-+	wpa_supplicant_event_global = hostapd_wpa_event_global;
- 	for (;;) {
--		c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:q");
-+		c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:g:G:qv::");
- 		if (c < 0)
- 			break;
- 		switch (c) {
-@@ -844,6 +857,8 @@ int main(int argc, char *argv[])
- 			break;
- #endif /* CONFIG_DEBUG_LINUX_TRACING */
- 		case 'v':
-+			if (optarg)
-+				exit(!has_feature(optarg));
- 			show_version();
- 			exit(1);
- 		case 'g':
-@@ -1013,6 +1028,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");
-@@ -1022,6 +1038,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++) {
-diff --git a/src/ap/acs.c b/src/ap/acs.c
-index 28b0ba71c..4c4c750ab 100644
---- a/src/ap/acs.c
-+++ b/src/ap/acs.c
-@@ -467,17 +467,17 @@ static int acs_get_bw_center_chan(int freq, enum bw_type bw)
- static int acs_survey_is_sufficient(struct freq_survey *survey)
- {
- 	if (!(survey->filled & SURVEY_HAS_NF)) {
-+		survey->nf = -95;
- 		wpa_printf(MSG_INFO,
- 			   "ACS: Survey for freq %d is missing noise floor",
- 			   survey->freq);
--		return 0;
- 	}
- 
- 	if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
-+		survey->channel_time = 0;
- 		wpa_printf(MSG_INFO,
- 			   "ACS: Survey for freq %d is missing channel time",
- 			   survey->freq);
--		return 0;
- 	}
- 
- 	if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
-@@ -485,7 +485,6 @@ static int acs_survey_is_sufficient(struct freq_survey *survey)
- 		wpa_printf(MSG_INFO,
- 			   "ACS: Survey for freq %d is missing RX and busy time (at least one is required)",
- 			   survey->freq);
--		return 0;
- 	}
- 
- 	return 1;
-diff --git a/src/ap/airtime_policy.c b/src/ap/airtime_policy.c
-index 68443115f..26f11ad98 100644
---- a/src/ap/airtime_policy.c
-+++ b/src/ap/airtime_policy.c
-@@ -112,8 +112,14 @@ static void set_sta_weights(struct hostapd_data *hapd, unsigned int weight)
- {
- 	struct sta_info *sta;
- 
--	for (sta = hapd->sta_list; sta; sta = sta->next)
--		sta_set_airtime_weight(hapd, sta, weight);
-+	for (sta = hapd->sta_list; sta; sta = sta->next) {
-+		unsigned int sta_weight = weight;
-+
-+		if (sta->dyn_airtime_weight)
-+			sta_weight = (weight * sta->dyn_airtime_weight) / 256;
-+
-+		sta_set_airtime_weight(hapd, sta, sta_weight);
-+	}
- }
- 
- 
-@@ -244,7 +250,10 @@ int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta)
- 	unsigned int weight;
- 
- 	if (hapd->iconf->airtime_mode == AIRTIME_MODE_STATIC) {
--		weight = get_weight_for_sta(hapd, sta->addr);
-+		if (sta->dyn_airtime_weight)
-+			weight = sta->dyn_airtime_weight;
-+		else
-+			weight = get_weight_for_sta(hapd, sta->addr);
- 		if (weight)
- 			return sta_set_airtime_weight(hapd, sta, weight);
- 	}
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 2330163c4..d10b00be9 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -121,6 +121,7 @@ struct hostapd_ssid {
- #define DYNAMIC_VLAN_OPTIONAL 1
- #define DYNAMIC_VLAN_REQUIRED 2
- 	int dynamic_vlan;
-+	int vlan_no_bridge;
- #define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0
- #define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
- #define DYNAMIC_VLAN_NAMING_END 2
-@@ -282,6 +283,8 @@ struct airtime_sta_weight {
- struct hostapd_bss_config {
- 	char iface[IFNAMSIZ + 1];
- 	char bridge[IFNAMSIZ + 1];
-+	char ft_iface[IFNAMSIZ + 1];
-+	char snoop_iface[IFNAMSIZ + 1];
- 	char vlan_bridge[IFNAMSIZ + 1];
- 	char wds_bridge[IFNAMSIZ + 1];
- 	int bridge_hairpin; /* hairpin_mode on bridge members */
-@@ -307,6 +310,7 @@ struct hostapd_bss_config {
- 	unsigned int eap_sim_db_timeout;
- 	int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
- 	struct hostapd_ip_addr own_ip_addr;
-+	int dynamic_own_ip_addr;
- 	char *nas_identifier;
- 	struct hostapd_radius_servers *radius;
- 	int acct_interim_interval;
-@@ -1064,6 +1068,8 @@ struct hostapd_config {
- 	unsigned int track_sta_max_num;
- 	unsigned int track_sta_max_age;
- 
-+	int max_num_sta;
-+
- 	char country[3]; /* first two octets: country code as described in
- 			  * ISO/IEC 3166-1. Third octet:
- 			  * ' ' (ascii 32): all environments
-@@ -1101,6 +1107,8 @@ struct hostapd_config {
- 
- 	int ht_op_mode_fixed;
- 	u16 ht_capab;
-+	int noscan;
-+	int no_ht_coex;
- 	int ieee80211n;
- 	int secondary_channel;
- 	int no_pri_sec_switch;
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 32722084d..527b2c984 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -387,8 +387,6 @@ int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
- 		return -1;
- 	if (hapd->conf->wds_bridge[0])
- 		bridge = hapd->conf->wds_bridge;
--	else if (hapd->conf->bridge[0])
--		bridge = hapd->conf->bridge;
- 	return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
- 					 bridge, ifname_wds);
- }
-@@ -1031,7 +1029,8 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
- int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
- 			    const u8 *qos_map_set, u8 qos_map_set_len)
- {
--	if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv)
-+	if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv ||
-+	    !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_QOS_MAPPING))
- 		return 0;
- 	return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
- 					 qos_map_set_len);
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index d7e79c840..f8a8725be 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -371,12 +371,12 @@ static inline int hostapd_drv_br_port_set_attr(struct hostapd_data *hapd,
- 
- static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
- 					       enum drv_br_net_param param,
--					       unsigned int val)
-+					       const char *ifname, unsigned int val)
- {
- 	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
- 	    hapd->driver->br_set_net_param == NULL)
- 		return -1;
--	return hapd->driver->br_set_net_param(hapd->drv_priv, param, val);
-+	return hapd->driver->br_set_net_param(hapd->drv_priv, param, ifname, val);
- }
- 
- static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
-@@ -404,6 +404,23 @@ static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
- 	return hapd->driver->stop_ap(hapd->drv_priv, link_id);
- }
- 
-+static inline int hostapd_drv_if_rename(struct hostapd_data *hapd,
-+					enum wpa_driver_if_type type,
-+					const char *ifname,
-+					const char *new_name)
-+{
-+	if (!hapd->driver || !hapd->driver->if_rename || !hapd->drv_priv)
-+		return -1;
-+	return hapd->driver->if_rename(hapd->drv_priv, type, ifname, new_name);
-+}
-+
-+static inline int hostapd_drv_set_first_bss(struct hostapd_data *hapd)
-+{
-+	if (!hapd->driver || !hapd->driver->set_first_bss || !hapd->drv_priv)
-+		return 0;
-+	return hapd->driver->set_first_bss(hapd->drv_priv);
-+}
-+
- static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
- 					   struct wpa_channel_info *ci)
- {
-diff --git a/src/ap/beacon.c b/src/ap/beacon.c
-index 4354dfae3..26453cb2c 100644
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -1343,6 +1343,12 @@ void handle_probe_req(struct hostapd_data *hapd,
- 	int mld_id;
- 	u16 links;
- #endif /* CONFIG_IEEE80211BE */
-+	struct hostapd_ubus_request req = {
-+		.type = HOSTAPD_UBUS_PROBE_REQ,
-+		.mgmt_frame = mgmt,
-+		.ssi_signal = ssi_signal,
-+		.elems = &elems,
-+	};
- 
- 	if (hapd->iconf->rssi_ignore_probe_request && ssi_signal &&
- 	    ssi_signal < hapd->iconf->rssi_ignore_probe_request)
-@@ -1529,6 +1535,12 @@ void handle_probe_req(struct hostapd_data *hapd,
- 	}
- #endif /* CONFIG_P2P */
- 
-+	if (hostapd_ubus_handle_event(hapd, &req)) {
-+		wpa_printf(MSG_DEBUG, "Probe request for " MACSTR " rejected by ubus handler.\n",
-+		       MAC2STR(mgmt->sa));
-+		return;
-+	}
-+
- 	/* TODO: verify that supp_rates contains at least one matching rate
- 	 * with AP configuration */
- 
-@@ -1547,7 +1559,7 @@ void handle_probe_req(struct hostapd_data *hapd,
- 	if (hapd->conf->no_probe_resp_if_max_sta &&
- 	    is_multicast_ether_addr(mgmt->da) &&
- 	    is_multicast_ether_addr(mgmt->bssid) &&
--	    hapd->num_sta >= hapd->conf->max_num_sta &&
-+	    hostapd_check_max_sta(hapd) &&
- 	    !ap_get_sta(hapd, mgmt->sa)) {
- 		wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
- 			   " since no room for additional STA",
-diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index 2cfef4bd4..cd7db4fc6 100644
---- a/src/ap/ctrl_iface_ap.c
-+++ b/src/ap/ctrl_iface_ap.c
-@@ -26,6 +26,26 @@
- #include "taxonomy.h"
- #include "wnm_ap.h"
- 
-+static const char * hw_mode_str(enum hostapd_hw_mode mode)
-+{
-+	switch (mode) {
-+	case HOSTAPD_MODE_IEEE80211B:
-+		return "b";
-+	case HOSTAPD_MODE_IEEE80211G:
-+		return "g";
-+	case HOSTAPD_MODE_IEEE80211A:
-+		return "a";
-+	case HOSTAPD_MODE_IEEE80211AD:
-+		return "ad";
-+	case HOSTAPD_MODE_IEEE80211ANY:
-+		return "any";
-+	case NUM_HOSTAPD_MODES:
-+		return "invalid";
-+	}
-+	return "unknown";
-+}
-+
-+#ifdef CONFIG_CTRL_IFACE_MIB
- 
- static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen,
- 					   size_t curr_len, const u8 *mcs_set)
-@@ -212,26 +232,6 @@ static const char * timeout_next_str(int val)
- }
- 
- 
--static const char * hw_mode_str(enum hostapd_hw_mode mode)
--{
--	switch (mode) {
--	case HOSTAPD_MODE_IEEE80211B:
--		return "b";
--	case HOSTAPD_MODE_IEEE80211G:
--		return "g";
--	case HOSTAPD_MODE_IEEE80211A:
--		return "a";
--	case HOSTAPD_MODE_IEEE80211AD:
--		return "ad";
--	case HOSTAPD_MODE_IEEE80211ANY:
--		return "any";
--	case NUM_HOSTAPD_MODES:
--		return "invalid";
--	}
--	return "unknown";
--}
--
--
- static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
- 				      struct sta_info *sta,
- 				      char *buf, size_t buflen)
-@@ -539,6 +539,7 @@ int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
- 	return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
- }
- 
-+#endif
- 
- #ifdef CONFIG_P2P_MANAGER
- static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
-@@ -987,12 +988,12 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
- 			return len;
- 		len += ret;
- 	}
--
-+#ifdef CONFIG_CTRL_IFACE_MIB
- 	if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) {
- 		len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
- 						   mode->mcs_set);
- 	}
--
-+#endif /* CONFIG_CTRL_IFACE_MIB */
- 	if (iface->current_rates && iface->num_rates) {
- 		ret = os_snprintf(buf + len, buflen - len, "supported_rates=");
- 		if (os_snprintf_error(buflen - len, ret))
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index fc2e8d83c..d14fad136 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -17,6 +17,7 @@
- #include "ap_drv_ops.h"
- #include "drivers/driver.h"
- #include "dfs.h"
-+#include "crypto/crypto.h"
- 
- 
- enum dfs_channel_type {
-@@ -526,9 +527,14 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
- 	int num_available_chandefs;
- 	int chan_idx, chan_idx2;
- 	int sec_chan_idx_80p80 = -1;
-+	bool is_mesh = false;
- 	int i;
- 	u32 _rand;
- 
-+#ifdef CONFIG_MESH
-+	is_mesh = iface->mconf;
-+#endif
-+
- 	wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
- 	*secondary_channel = 0;
- 	*oper_centr_freq_seg0_idx = 0;
-@@ -548,8 +554,20 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
- 	if (num_available_chandefs == 0)
- 		return NULL;
- 
--	if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
-+	/* try to use deterministic channel in mesh, so that both sides
-+	 * have a chance to switch to the same channel */
-+	if (is_mesh) {
-+#ifdef CONFIG_MESH
-+		u64 hash[4];
-+		const u8 *meshid[1] = { &iface->mconf->meshid[0] };
-+		const size_t meshid_len = iface->mconf->meshid_len;
-+
-+		sha256_vector(1, meshid, &meshid_len, (u8 *)&hash[0]);
-+		_rand = hash[0] + hash[1] + hash[2] + hash[3];
-+#endif
-+	} else if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
- 		return NULL;
-+
- 	chan_idx = _rand % num_available_chandefs;
- 	wpa_printf(MSG_DEBUG, "DFS: Picked random entry from the list: %d/%d",
- 		   chan_idx, num_available_chandefs);
-@@ -1207,6 +1225,8 @@ int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
- 		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
- 		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
- 
-+	hostapd_ubus_notify_radar_detected(iface, freq, chan_width, cf1, cf2);
-+
- 	/* Proceed only if DFS is not offloaded to the driver */
- 	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
- 		return 0;
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index dc21977ff..e8796f709 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -268,6 +268,10 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
- 	struct hostapd_iface *iface = hapd->iface;
- #endif /* CONFIG_OWE */
- 	bool updated = false;
-+	struct hostapd_ubus_request req = {
-+		.type = HOSTAPD_UBUS_ASSOC_REQ,
-+		.addr = addr,
-+	};
- 
- 	if (addr == NULL) {
- 		/*
-@@ -412,6 +416,12 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
- 		goto fail;
- 	}
- 
-+	if (hostapd_ubus_handle_event(hapd, &req)) {
-+		wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
-+			   MAC2STR(req.addr));
-+		goto fail;
-+	}
-+
- #ifdef CONFIG_P2P
- 	if (elems.p2p) {
- 		wpabuf_free(sta->p2p_ie);
-@@ -2342,8 +2352,8 @@ static void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
- 	ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
- }
- 
--void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
--			  union wpa_event_data *data)
-+void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
-+		       union wpa_event_data *data)
- {
- 	struct hostapd_data *hapd = ctx;
- 	struct sta_info *sta;
-@@ -2675,7 +2685,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
- }
- 
- 
--void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
-+void hostapd_wpa_event_global(void *ctx, enum wpa_event_type event,
- 				 union wpa_event_data *data)
- {
- 	struct hapd_interfaces *interfaces = ctx;
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index b899c9831..7959859b0 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -247,6 +247,29 @@ static int hostapd_iface_conf_changed(struct hostapd_config *newconf,
- 	return 0;
- }
- 
-+static inline int hostapd_iface_num_sta(struct hostapd_iface *iface)
-+{
-+	int num_sta = 0;
-+	int i;
-+
-+	for (i = 0; i < iface->num_bss; i++)
-+		num_sta += iface->bss[i]->num_sta;
-+
-+	return num_sta;
-+}
-+
-+
-+int hostapd_check_max_sta(struct hostapd_data *hapd)
-+{
-+	if (hapd->num_sta >= hapd->conf->max_num_sta)
-+		return 1;
-+
-+	if (hapd->iconf->max_num_sta &&
-+	    hostapd_iface_num_sta(hapd->iface) >= hapd->iconf->max_num_sta)
-+		return 1;
-+
-+	return 0;
-+}
- 
- int hostapd_reload_config(struct hostapd_iface *iface)
- {
-@@ -255,6 +278,8 @@ int hostapd_reload_config(struct hostapd_iface *iface)
- 	struct hostapd_config *newconf, *oldconf;
- 	size_t j;
- 
-+	hostapd_ucode_reload_bss(hapd);
-+
- 	if (iface->config_fname == NULL) {
- 		/* Only in-memory config in use - assume it has been updated */
- 		hostapd_clear_old(iface);
-@@ -475,6 +500,8 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
- 	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);
- 	vlan_deinit(hapd);
-@@ -685,6 +712,7 @@ static void sta_track_deinit(struct hostapd_iface *iface)
- void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
- {
- 	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
-+	eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
- #ifdef NEED_AP_MLME
- 	hostapd_stop_setup_timers(iface);
- #endif /* NEED_AP_MLME */
-@@ -714,7 +742,7 @@ void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
- static void hostapd_cleanup_iface(struct hostapd_iface *iface)
- {
- 	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
--	eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
-+	hostapd_ucode_free_iface(iface);
- 	eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
- 			     NULL);
- 
-@@ -1303,6 +1331,9 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
- 	if (hapd->driver && hapd->driver->set_operstate)
- 		hapd->driver->set_operstate(hapd->drv_priv, 1);
- 
-+	hostapd_ubus_add_bss(hapd);
-+	hostapd_ucode_add_bss(hapd);
-+
- 	return 0;
- }
- 
-@@ -1324,8 +1355,7 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
-  * 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];
-@@ -1552,6 +1582,7 @@ setup_mld:
- 
- 			os_memset(&das_conf, 0, sizeof(das_conf));
- 			das_conf.port = conf->radius_das_port;
-+			das_conf.nas_identifier = conf->nas_identifier;
- 			das_conf.shared_secret = conf->radius_das_shared_secret;
- 			das_conf.shared_secret_len =
- 				conf->radius_das_shared_secret_len;
-@@ -1627,6 +1658,7 @@ setup_mld:
- 		wpa_printf(MSG_ERROR, "GAS server initialization failed");
- 		return -1;
- 	}
-+#endif /* CONFIG_INTERWORKING */
- 
- 	if (conf->qos_map_set_len &&
- 	    hostapd_drv_set_qos_map(hapd, conf->qos_map_set,
-@@ -1634,7 +1666,6 @@ setup_mld:
- 		wpa_printf(MSG_ERROR, "Failed to initialize QoS Map");
- 		return -1;
- 	}
--#endif /* CONFIG_INTERWORKING */
- 
- 	if (conf->bss_load_update_period && bss_load_update_init(hapd)) {
- 		wpa_printf(MSG_ERROR, "BSS Load initialization failed");
-@@ -2447,6 +2478,7 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
- 	if (err)
- 		goto fail;
- 
-+	hostapd_ubus_add_iface(iface);
- 	wpa_printf(MSG_DEBUG, "Completing interface initialization");
- 	if (iface->freq) {
- #ifdef NEED_AP_MLME
-@@ -2676,6 +2708,7 @@ dfs_offload:
- 
- fail:
- 	wpa_printf(MSG_ERROR, "Interface initialization failed");
-+	hostapd_ubus_free_iface(iface);
- 
- 	if (iface->is_no_ir) {
- 		hostapd_set_state(iface, HAPD_IFACE_NO_IR);
-@@ -2875,7 +2908,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
- }
- 
- 
--static void hostapd_bss_deinit(struct hostapd_data *hapd)
-+void hostapd_bss_deinit(struct hostapd_data *hapd)
- {
- 	if (!hapd)
- 		return;
-@@ -3395,6 +3428,7 @@ void hostapd_interface_deinit_free(struct hostapd_iface *iface)
- 		   (unsigned int) iface->conf->num_bss);
- 	driver = iface->bss[0]->driver;
- 	drv_priv = iface->bss[0]->drv_priv;
-+	hostapd_ubus_free_iface(iface);
- 	hostapd_interface_deinit(iface);
- 	wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
- 		   __func__, driver, drv_priv);
-@@ -3926,7 +3960,8 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
- 		hapd_iface = interfaces->iface[i];
- 		if (hapd_iface == NULL)
- 			return -1;
--		if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
-+		if (!os_strcmp(hapd_iface->phy, buf) ||
-+		    !os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
- 			wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
- 			hapd_iface->driver_ap_teardown =
- 				!!(hapd_iface->drv_flags &
-@@ -3972,6 +4007,8 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
- void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
- 			   int reassoc)
- {
-+	int mld_assoc_link_id = -1;
-+
- 	if (hapd->tkip_countermeasures) {
- 		hostapd_drv_sta_deauth(hapd, sta->addr,
- 				       WLAN_REASON_MICHAEL_MIC_FAILURE);
-@@ -3983,6 +4020,8 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
- 	    sta->mld_assoc_link_id != hapd->mld_link_id)
- 		return;
- #endif /* CONFIG_IEEE80211BE */
-+        if (mld_assoc_link_id != -2)
-+		hostapd_prune_associations(hapd, sta->addr, mld_assoc_link_id);
- 
- 	ap_sta_clear_disconnect_timeouts(hapd, sta);
- 	sta->post_csa_sa_query = 0;
-diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
-index 594866fbb..1e4113459 100644
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -13,11 +13,14 @@
- #include <sqlite3.h>
- #endif /* CONFIG_SQLITE */
- 
-+#include "ap/sta_info.h"
- #include "common/defs.h"
- #include "common/dpp.h"
- #include "utils/list.h"
- #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 +54,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);
-@@ -167,6 +174,21 @@ struct hostapd_sae_commit_queue {
- 	u8 msg[];
- };
- 
-+/**
-+ * struct hostapd_openwrt_stats - OpenWrt custom STA/AP statistics
-+ */
-+struct hostapd_openwrt_stats {
-+	struct {
-+		u64 neighbor_report_tx;
-+	} rrm;
-+
-+	struct {
-+		u64 bss_transition_query_rx;
-+		u64 bss_transition_request_tx;
-+		u64 bss_transition_response_rx;
-+	} wnm;
-+};
-+
- /**
-  * struct hostapd_data - hostapd per-BSS data structure
-  */
-@@ -174,6 +196,8 @@ struct hostapd_data {
- 	struct hostapd_iface *iface;
- 	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;
-@@ -181,6 +205,9 @@ struct hostapd_data {
- 
- 	u8 own_addr[ETH_ALEN];
- 
-+	/* OpenWrt specific statistics */
-+	struct hostapd_openwrt_stats openwrt_stats;
-+
- 	int num_sta; /* number of entries in sta_list */
- 	struct sta_info *sta_list; /* STA info list head */
- #define STA_HASH_SIZE 256
-@@ -523,6 +550,7 @@ struct hostapd_mld {
-  */
- struct hostapd_iface {
- 	struct hapd_interfaces *interfaces;
-+	struct hostapd_ucode_iface ucode;
- 	void *owner;
- 	char *config_fname;
- 	struct hostapd_config *conf;
-@@ -745,6 +773,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
- 		       struct hostapd_bss_config *bss);
- int hostapd_setup_interface(struct hostapd_iface *iface);
- int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
-+void hostapd_set_own_neighbor_report(struct hostapd_data *hapd);
- void hostapd_interface_deinit(struct hostapd_iface *iface);
- void hostapd_interface_free(struct hostapd_iface *iface);
- struct hostapd_iface * hostapd_alloc_iface(void);
-@@ -753,6 +782,8 @@ struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
- 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_bss_deinit(struct hostapd_data *hapd);
- void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
- 			   int reassoc);
- void hostapd_interface_deinit_free(struct hostapd_iface *iface);
-@@ -780,6 +811,7 @@ void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
- 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);
-+int hostapd_check_max_sta(struct hostapd_data *hapd);
- 
- void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap);
- void hostapd_cleanup_cca_params(struct hostapd_data *hapd);
-@@ -865,4 +897,14 @@ static inline bool hostapd_mld_is_first_bss(struct hostapd_data *hapd)
- 
- u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd);
- 
-+static inline bool ap_sta_is_mld(struct hostapd_data *hapd,
-+				 struct sta_info *sta)
-+{
-+#ifdef CONFIG_IEEE80211BE
-+	return hapd->conf->mld_ap && sta && sta->mld_info.mld_sta;
-+#else /* CONFIG_IEEE80211BE */
-+	return false;
-+#endif /* CONFIG_IEEE80211BE */
-+}
-+
- #endif /* HOSTAPD_H */
-diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
-index 222f3dc05..672e43a10 100644
---- a/src/ap/hw_features.c
-+++ b/src/ap/hw_features.c
-@@ -546,7 +546,8 @@ static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
- 	int ret;
- 
- 	/* Check that HT40 is used and PRI / SEC switch is allowed */
--	if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)
-+	if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch ||
-+		iface->conf->noscan)
- 		return 0;
- 
- 	hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 179af5e28..bda61b998 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -2804,7 +2804,7 @@ static void handle_auth(struct hostapd_data *hapd,
- 	u16 auth_alg, auth_transaction, status_code;
- 	u16 resp = WLAN_STATUS_SUCCESS;
- 	struct sta_info *sta = NULL;
--	int res, reply_res;
-+	int res, reply_res, ubus_resp;
- 	u16 fc;
- 	const u8 *challenge = NULL;
- 	u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
-@@ -2815,6 +2815,11 @@ static void handle_auth(struct hostapd_data *hapd,
- #ifdef CONFIG_IEEE80211BE
- 	bool mld_sta = false;
- #endif /* CONFIG_IEEE80211BE */
-+	struct hostapd_ubus_request req = {
-+		.type = HOSTAPD_UBUS_AUTH_REQ,
-+		.mgmt_frame = mgmt,
-+		.ssi_signal = rssi,
-+	};
- 
- 	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
- 		wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
-@@ -3008,6 +3013,13 @@ static void handle_auth(struct hostapd_data *hapd,
- 		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
- 		goto fail;
- 	}
-+	ubus_resp = hostapd_ubus_handle_event(hapd, &req);
-+	if (ubus_resp) {
-+		wpa_printf(MSG_DEBUG, "Station " MACSTR " rejected by ubus handler.\n",
-+			MAC2STR(mgmt->sa));
-+		resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
-+		goto fail;
-+	}
- 	if (res == HOSTAPD_ACL_PENDING)
- 		return;
- 
-@@ -3042,15 +3054,6 @@ static void handle_auth(struct hostapd_data *hapd,
- 				       seq_ctrl);
- 			return;
- 		}
--#ifdef CONFIG_MESH
--		if ((hapd->conf->mesh & MESH_ENABLED) &&
--		    sta->plink_state == PLINK_BLOCKED) {
--			wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
--				   " is blocked - drop Authentication frame",
--				   MAC2STR(sa));
--			return;
--		}
--#endif /* CONFIG_MESH */
- #ifdef CONFIG_PASN
- 		if (auth_alg == WLAN_AUTH_PASN &&
- 		    (sta->flags & WLAN_STA_ASSOC)) {
-@@ -4698,6 +4701,13 @@ static int add_associated_sta(struct hostapd_data *hapd,
- 	 * drivers to accept the STA parameter configuration. Since this is
- 	 * after a new FT-over-DS exchange, a new TK has been derived, so key
- 	 * reinstallation is not a concern for this case.
-+	 *
-+	 * If the STA was associated and authorized earlier, but came for a new
-+	 * connection (!added_unassoc + !reassoc), remove the existing STA entry
-+	 * so that it can be re-added. This case is rarely seen when the AP could
-+	 * not receive the deauth/disassoc frame from the STA. And the STA comes
-+	 * back with new connection within a short period or before the inactive
-+	 * STA entry is removed from the list.
- 	 */
- 	wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR
- 		   " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)",
-@@ -4711,7 +4721,8 @@ static int add_associated_sta(struct hostapd_data *hapd,
- 	    (!(sta->flags & WLAN_STA_AUTHORIZED) ||
- 	     (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) ||
- 	     (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
--	      !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) {
-+	      !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)) ||
-+	     (!reassoc && (sta->flags & WLAN_STA_AUTHORIZED)))) {
- 		hostapd_drv_sta_remove(hapd, sta->addr);
- 		wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
- 		set = 0;
-@@ -5273,7 +5284,7 @@ static void handle_assoc(struct hostapd_data *hapd,
- 	int resp = WLAN_STATUS_SUCCESS;
- 	u16 reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
- 	const u8 *pos;
--	int left, i;
-+	int left, i, ubus_resp;
- 	struct sta_info *sta;
- 	u8 *tmp = NULL;
- #ifdef CONFIG_FILS
-@@ -5515,6 +5526,11 @@ static void handle_assoc(struct hostapd_data *hapd,
- 		left = res;
- 	}
- #endif /* CONFIG_FILS */
-+	struct hostapd_ubus_request req = {
-+		.type = HOSTAPD_UBUS_ASSOC_REQ,
-+		.mgmt_frame = mgmt,
-+		.ssi_signal = rssi,
-+	};
- 
- 	/* followed by SSID and Supported rates; and HT capabilities if 802.11n
- 	 * is used */
-@@ -5617,6 +5633,13 @@ static void handle_assoc(struct hostapd_data *hapd,
- 	if (set_beacon)
- 		ieee802_11_set_beacons(hapd->iface);
- 
-+	ubus_resp = hostapd_ubus_handle_event(hapd, &req);
-+	if (ubus_resp) {
-+		wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
-+		       MAC2STR(mgmt->sa));
-+		resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
-+		goto fail;
-+	}
-  fail:
- 
- 	/*
-@@ -5848,6 +5871,7 @@ static void handle_disassoc(struct hostapd_data *hapd,
- 			   (unsigned long) len);
- 		return;
- 	}
-+	hostapd_ubus_notify(hapd, "disassoc", mgmt->sa);
- 
- 	sta = ap_get_sta(hapd, mgmt->sa);
- 	if (!sta) {
-@@ -5879,6 +5903,8 @@ static void handle_deauth(struct hostapd_data *hapd,
- 	/* Clear the PTKSA cache entries for PASN */
- 	ptksa_cache_flush(hapd->ptksa, mgmt->sa, WPA_CIPHER_NONE);
- 
-+	hostapd_ubus_notify(hapd, "deauth", mgmt->sa);
-+
- 	sta = ap_get_sta(hapd, mgmt->sa);
- 	if (!sta) {
- 		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
-diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
-index f90f1254e..7f0a00f95 100644
---- a/src/ap/ieee802_11_ht.c
-+++ b/src/ap/ieee802_11_ht.c
-@@ -82,7 +82,9 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
- u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
- {
- 	struct ieee80211_ht_operation *oper;
-+	le32 vht_capabilities_info;
- 	u8 *pos = eid;
-+	u8 chwidth;
- 
- 	if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n ||
- 	    is_6ghz_op_class(hapd->iconf->op_class))
-@@ -103,6 +105,13 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
- 		oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
- 			HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
- 
-+	vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
-+	chwidth = hostapd_get_oper_chwidth(hapd->iconf);
-+	if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT
-+		&& ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
-+		oper->operation_mode = host_to_le16(hapd->iconf->vht_oper_centr_freq_seg0_idx << 5);
-+	}
-+
- 	pos += sizeof(*oper);
- 
- 	return pos;
-@@ -230,6 +239,9 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
- 		return;
- 	}
- 
-+	if (iface->conf->noscan || iface->conf->no_ht_coex)
-+		return;
-+
- 	if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) {
- 		wpa_printf(MSG_DEBUG,
- 			   "Ignore too short 20/40 BSS Coexistence Management frame");
-@@ -390,6 +402,9 @@ void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta)
- 	if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
- 		return;
- 
-+	if (iface->conf->noscan || iface->conf->no_ht_coex)
-+		return;
-+
- 	wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR
- 		   " in Association Request", MAC2STR(sta->addr));
- 
-diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
-index a5716f037..85790c7ed 100644
---- a/src/ap/ieee802_11_shared.c
-+++ b/src/ap/ieee802_11_shared.c
-@@ -1138,13 +1138,11 @@ u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len)
- u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
- 		    const u8 *ext_capab_ie, size_t ext_capab_ie_len)
- {
--#ifdef CONFIG_INTERWORKING
- 	/* check for QoS Map support */
- 	if (ext_capab_ie_len >= 5) {
- 		if (ext_capab_ie[4] & 0x01)
- 			sta->qos_map_enabled = 1;
- 	}
--#endif /* CONFIG_INTERWORKING */
- 
- 	if (ext_capab_ie_len > 0) {
- 		sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
-diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
-index 4dc325ce8..68880ab64 100644
---- a/src/ap/ieee802_11_vht.c
-+++ b/src/ap/ieee802_11_vht.c
-@@ -26,6 +26,7 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
- 	struct ieee80211_vht_capabilities *cap;
- 	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
- 	u8 *pos = eid;
-+	u8 chwidth;
- 
- 	if (!mode || is_6ghz_op_class(hapd->iconf->op_class))
- 		return eid;
-@@ -63,6 +64,17 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
- 			host_to_le32(nsts << VHT_CAP_BEAMFORMEE_STS_OFFSET);
- 	}
- 
-+	chwidth = hostapd_get_oper_chwidth(hapd->iconf);
-+	if (((host_to_le32(mode->vht_capab)) & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
-+		&& ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
-+		cap->vht_capabilities_info |= VHT_CAP_EXTENDED_NSS_BW_SUPPORT;
-+		cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ));
-+		cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ));
-+		cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_MASK));
-+	} else {
-+		cap->vht_capabilities_info &= ~VHT_CAP_EXTENDED_NSS_BW_SUPPORT_MASK;
-+	}
-+
- 	/* Supported MCS set comes from hw */
- 	os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
- 
-@@ -75,6 +87,7 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
- u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
- {
- 	struct ieee80211_vht_operation *oper;
-+	le32 vht_capabilities_info;
- 	u8 *pos = eid;
- 	enum oper_chan_width oper_chwidth =
- 		hostapd_get_oper_chwidth(hapd->iconf);
-@@ -110,6 +123,7 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
- 	oper->vht_op_info_chan_center_freq_seg1_idx = seg1;
- 
- 	oper->vht_op_info_chwidth = oper_chwidth;
-+	vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
- 	if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ) {
- 		/*
- 		 * Convert 160 MHz channel width to new style as interop
-@@ -123,6 +137,9 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
- 			oper->vht_op_info_chan_center_freq_seg0_idx -= 8;
- 		else
- 			oper->vht_op_info_chan_center_freq_seg0_idx += 8;
-+
-+		if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
-+			oper->vht_op_info_chan_center_freq_seg1_idx = 0;
- 	} else if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ) {
- 		/*
- 		 * Convert 80+80 MHz channel width to new style as interop
-diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
-index 8e98b6521..8abebbf34 100644
---- a/src/ap/ieee802_1x.c
-+++ b/src/ap/ieee802_1x.c
-@@ -600,6 +600,10 @@ int add_common_radius_attr(struct hostapd_data *hapd,
- 	struct hostapd_radius_attr *attr;
- 	int len;
- 
-+	if (hapd->conf->dynamic_own_ip_addr)
-+		radius_client_get_local_addr(hapd->radius,
-+					     &hapd->conf->own_ip_addr);
-+
- 	if (!hostapd_config_get_radius_attr(req_attr,
- 					    RADIUS_ATTR_NAS_IP_ADDRESS) &&
- 	    hapd->conf->own_ip_addr.af == AF_INET &&
-@@ -2845,6 +2849,7 @@ static const char * bool_txt(bool val)
- 	return val ? "TRUE" : "FALSE";
- }
- 
-+#ifdef CONFIG_CTRL_IFACE_MIB
- 
- int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
- {
-@@ -3031,6 +3036,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
- 	return len;
- }
- 
-+#endif
- 
- #ifdef CONFIG_HS20
- static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx)
-diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c
-index 788c12fdc..bc1eb6251 100644
---- a/src/ap/ndisc_snoop.c
-+++ b/src/ap/ndisc_snoop.c
-@@ -61,6 +61,7 @@ void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
- 	dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr,
- 			      list) {
- 		hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr);
-+		dl_list_del(&ip6addr->list);
- 		os_free(ip6addr);
- 	}
- }
-diff --git a/src/ap/rrm.c b/src/ap/rrm.c
-index f2d5cd16e..8220590a0 100644
---- a/src/ap/rrm.c
-+++ b/src/ap/rrm.c
-@@ -89,6 +89,9 @@ static void hostapd_handle_beacon_report(struct hostapd_data *hapd,
- 		return;
- 	wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s",
- 		MAC2STR(addr), token, rep_mode, report);
-+	if (len < sizeof(struct rrm_measurement_beacon_report))
-+		return;
-+	hostapd_ubus_notify_beacon_report(hapd, addr, token, rep_mode, (struct rrm_measurement_beacon_report*) pos, len);
- }
- 
- 
-@@ -269,6 +272,8 @@ static void hostapd_send_nei_report_resp(struct hostapd_data *hapd,
- 		}
- 	}
- 
-+	hapd->openwrt_stats.rrm.neighbor_report_tx++;
-+
- 	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
- 				wpabuf_head(buf), wpabuf_len(buf));
- 	wpabuf_free(buf);
-@@ -350,6 +355,9 @@ void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
- 		   mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa));
- 
- 	switch (mgmt->u.action.u.rrm.action) {
-+	case WLAN_RRM_LINK_MEASUREMENT_REPORT:
-+		hostapd_ubus_handle_link_measurement(hapd, buf, len);
-+		break;
- 	case WLAN_RRM_RADIO_MEASUREMENT_REPORT:
- 		hostapd_handle_radio_msmt_report(hapd, buf, len);
- 		break;
-diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
-index d483aa9d3..ee6e20538 100644
---- a/src/ap/sta_info.c
-+++ b/src/ap/sta_info.c
-@@ -539,6 +539,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
- 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
- 			       HOSTAPD_LEVEL_INFO, "deauthenticated due to "
- 			       "local deauth request");
-+		hostapd_ubus_notify(hapd, "local-deauth", sta->addr);
- 		ap_free_sta(hapd, sta);
- 		return;
- 	}
-@@ -694,6 +695,7 @@ skip_poll:
- 		mlme_deauthenticate_indication(
- 			hapd, sta,
- 			WLAN_REASON_PREV_AUTH_NOT_VALID);
-+		hostapd_ubus_notify(hapd, "inactive-deauth", sta->addr);
- 		ap_free_sta(hapd, sta);
- 		break;
- 	}
-@@ -1476,9 +1478,6 @@ bool ap_sta_set_authorized_flag(struct hostapd_data *hapd, struct sta_info *sta,
- 				mld_assoc_link_id = -2;
- 		}
- #endif /* CONFIG_IEEE80211BE */
--		if (mld_assoc_link_id != -2)
--			hostapd_prune_associations(hapd, sta->addr,
--						   mld_assoc_link_id);
- 		sta->flags |= WLAN_STA_AUTHORIZED;
- 	} else {
- 		sta->flags &= ~WLAN_STA_AUTHORIZED;
-@@ -1515,15 +1514,28 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
- 		os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr));
- 
- 	if (authorized) {
-+		static const char * const auth_algs[] = {
-+			[WLAN_AUTH_OPEN] = "open",
-+			[WLAN_AUTH_SHARED_KEY] = "shared",
-+			[WLAN_AUTH_FT] = "ft",
-+			[WLAN_AUTH_SAE] = "sae",
-+			[WLAN_AUTH_FILS_SK] = "fils-sk",
-+			[WLAN_AUTH_FILS_SK_PFS] = "fils-sk-pfs",
-+			[WLAN_AUTH_FILS_PK] = "fils-pk",
-+			[WLAN_AUTH_PASN] = "pasn",
-+		};
-+		const char *auth_alg = NULL;
- 		const u8 *dpp_pkhash;
- 		const char *keyid;
- 		char dpp_pkhash_buf[100];
- 		char keyid_buf[100];
- 		char ip_addr[100];
-+		char alg_buf[100];
- 
- 		dpp_pkhash_buf[0] = '\0';
- 		keyid_buf[0] = '\0';
- 		ip_addr[0] = '\0';
-+		alg_buf[0] = '\0';
- #ifdef CONFIG_P2P
- 		if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
- 			os_snprintf(ip_addr, sizeof(ip_addr),
-@@ -1534,6 +1546,13 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
- 		}
- #endif /* CONFIG_P2P */
- 
-+		if (sta->auth_alg < ARRAY_SIZE(auth_algs))
-+			auth_alg = auth_algs[sta->auth_alg];
-+
-+		if (auth_alg)
-+			os_snprintf(alg_buf, sizeof(alg_buf),
-+				" auth_alg=%s", auth_alg);
-+
- 		keyid = ap_sta_wpa_get_keyid(hapd, sta);
- 		if (keyid) {
- 			os_snprintf(keyid_buf, sizeof(keyid_buf),
-@@ -1552,17 +1571,19 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
- 					 dpp_pkhash, SHA256_MAC_LEN);
- 		}
- 
--		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s",
--			buf, ip_addr, keyid_buf, dpp_pkhash_buf);
-+		hostapd_ubus_notify_authorized(hapd, sta, auth_alg);
-+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s%s",
-+			buf, ip_addr, keyid_buf, dpp_pkhash_buf, alg_buf);
- 
- 		if (hapd->msg_ctx_parent &&
- 		    hapd->msg_ctx_parent != hapd->msg_ctx)
- 			wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
--					  AP_STA_CONNECTED "%s%s%s%s",
-+					  AP_STA_CONNECTED "%s%s%s%s%s",
- 					  buf, ip_addr, keyid_buf,
--					  dpp_pkhash_buf);
-+					  dpp_pkhash_buf, alg_buf);
- 	} else {
- 		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
-+		hostapd_ubus_notify(hapd, "disassoc", sta->addr);
- 
- 		if (hapd->msg_ctx_parent &&
- 		    hapd->msg_ctx_parent != hapd->msg_ctx)
-diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
-index 153e4a000..38b80903d 100644
---- a/src/ap/sta_info.h
-+++ b/src/ap/sta_info.h
-@@ -17,7 +17,6 @@
- #include "common/sae.h"
- #include "crypto/sha384.h"
- #include "pasn/pasn_common.h"
--#include "hostapd.h"
- 
- /* STA flags */
- #define WLAN_STA_AUTH BIT(0)
-@@ -323,6 +322,7 @@ struct sta_info {
- #endif /* CONFIG_TESTING_OPTIONS */
- #ifdef CONFIG_AIRTIME_POLICY
- 	unsigned int airtime_weight;
-+	unsigned int dyn_airtime_weight;
- 	struct os_reltime backlogged_until;
- #endif /* CONFIG_AIRTIME_POLICY */
- 
-@@ -420,16 +420,6 @@ int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta);
- 
- void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta);
- 
--static inline bool ap_sta_is_mld(struct hostapd_data *hapd,
--				 struct sta_info *sta)
--{
--#ifdef CONFIG_IEEE80211BE
--	return hapd->conf->mld_ap && sta && sta->mld_info.mld_sta;
--#else /* CONFIG_IEEE80211BE */
--	return false;
--#endif /* CONFIG_IEEE80211BE */
--}
--
- static inline void ap_sta_set_mld(struct sta_info *sta, bool mld)
- {
- #ifdef CONFIG_IEEE80211BE
-diff --git a/src/ap/vlan_full.c b/src/ap/vlan_full.c
-index 19aa3c649..053d6338e 100644
---- a/src/ap/vlan_full.c
-+++ b/src/ap/vlan_full.c
-@@ -475,6 +475,9 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
- 	if (!vlan)
- 		return;
- 
-+	if (hapd->conf->ssid.vlan_no_bridge)
-+		goto out;
-+
- 	vlan->configured = 1;
- 
- 	notempty = vlan->vlan_desc.notempty;
-@@ -506,6 +509,7 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
- 				    ifname, br_name, tagged[i], hapd);
- 	}
- 
-+out:
- 	ifconfig_up(ifname);
- }
- 
-diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
-index 53eacfb45..b69f3de41 100644
---- a/src/ap/vlan_init.c
-+++ b/src/ap/vlan_init.c
-@@ -22,6 +22,7 @@
- static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
- 		       int existsok)
- {
-+	bool vlan_exists = iface_exists(vlan->ifname);
- 	int ret;
- #ifdef CONFIG_WEP
- 	int i;
-@@ -36,7 +37,7 @@ static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
- 	}
- #endif /* CONFIG_WEP */
- 
--	if (!iface_exists(vlan->ifname))
-+	if (!vlan_exists)
- 		ret = hostapd_vlan_if_add(hapd, vlan->ifname);
- 	else if (!existsok)
- 		return -1;
-@@ -51,6 +52,9 @@ static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
- 	if (hapd->wpa_auth)
- 		ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id);
- 
-+	if (!ret && !vlan_exists)
-+		hostapd_ubus_add_vlan(hapd, vlan);
-+
- 	if (ret == 0)
- 		return ret;
- 
-@@ -77,6 +81,8 @@ int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
- 			   "WPA deinitialization for VLAN %d failed (%d)",
- 			   vlan->vlan_id, ret);
- 
-+	hostapd_ubus_remove_vlan(hapd, vlan);
-+
- 	return hostapd_vlan_if_remove(hapd, vlan->ifname);
- }
- 
-diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
-index af8cccaef..d259200c9 100644
---- a/src/ap/wnm_ap.c
-+++ b/src/ap/wnm_ap.c
-@@ -410,6 +410,7 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
- 	mgmt->u.action.u.bss_tm_req.validity_interval = 1;
- 	pos = mgmt->u.action.u.bss_tm_req.variable;
- 
-+	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 "
- 		   "validity_interval=%u",
-@@ -478,7 +479,8 @@ static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
- 		MAC2STR(addr), reason, hex ? " neighbor=" : "", hex);
- 	os_free(hex);
- 
--	ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
-+	if (!hostapd_ubus_notify_bss_transition_query(hapd, addr, dialog_token, reason, pos, end - pos))
-+		ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
- }
- 
- 
-@@ -500,7 +502,7 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
- 					      size_t len)
- {
- 	u8 dialog_token, status_code, bss_termination_delay;
--	const u8 *pos, *end;
-+	const u8 *pos, *end, *target_bssid = NULL;
- 	int enabled = hapd->conf->bss_transition;
- 	struct sta_info *sta;
- 
-@@ -547,6 +549,7 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
- 			wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
- 			return;
- 		}
-+		target_bssid = pos;
- 		sta->agreed_to_steer = 1;
- 		eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
- 		eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
-@@ -566,6 +569,10 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
- 			MAC2STR(addr), status_code, bss_termination_delay);
- 	}
- 
-+	hostapd_ubus_notify_bss_transition_response(hapd, sta->addr, dialog_token,
-+						    status_code, bss_termination_delay,
-+						    target_bssid, pos, end - pos);
-+
- 	wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
- 		    pos, end - pos);
- }
-@@ -814,10 +821,12 @@ int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
- 					       plen);
- 		return 0;
- 	case WNM_BSS_TRANS_MGMT_QUERY:
-+		hapd->openwrt_stats.wnm.bss_transition_query_rx++;
- 		ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
- 						   plen);
- 		return 0;
- 	case WNM_BSS_TRANS_MGMT_RESP:
-+		hapd->openwrt_stats.wnm.bss_transition_response_rx++;
- 		ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload,
- 						  plen);
- 		return 0;
-@@ -865,6 +874,7 @@ int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
- 
- 	pos = mgmt->u.action.u.bss_tm_req.variable;
- 
-+	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
- 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to "
- 		   MACSTR, disassoc_timer, MAC2STR(sta->addr));
- 	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
-@@ -947,6 +957,7 @@ int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
- 		return -1;
- 	}
- 
-+	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
- 	if (disassoc_timer) {
- 		/* send disassociation frame after time-out */
- 		set_disassoc_timer(hapd, sta, disassoc_timer);
-@@ -1028,6 +1039,7 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
- 	}
- 	os_free(buf);
- 
-+	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
- 	if (disassoc_timer) {
- #ifdef CONFIG_IEEE80211BE
- 		if (ap_sta_is_mld(hapd, sta)) {
-diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
-index 7a07dcc4c..b23d75444 100644
---- a/src/ap/wpa_auth.c
-+++ b/src/ap/wpa_auth.c
-@@ -5865,6 +5865,7 @@ static const char * wpa_bool_txt(int val)
- 	return val ? "TRUE" : "FALSE";
- }
- 
-+#ifdef CONFIG_CTRL_IFACE_MIB
- 
- #define RSN_SUITE "%02x-%02x-%02x-%d"
- #define RSN_SUITE_ARG(s) \
-@@ -6017,7 +6018,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
- 
- 	return len;
- }
--
-+#endif
- 
- void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth)
- {
-diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
-index 1726c7201..5a9ec6975 100644
---- a/src/ap/wpa_auth_glue.c
-+++ b/src/ap/wpa_auth_glue.c
-@@ -275,6 +275,7 @@ static void hostapd_wpa_auth_psk_failure_report(void *ctx, const u8 *addr)
- 	struct hostapd_data *hapd = ctx;
- 	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POSSIBLE_PSK_MISMATCH MACSTR,
- 		MAC2STR(addr));
-+	hostapd_ubus_notify(hapd, "key-mismatch", addr);
- }
- 
- 
-@@ -1812,8 +1813,12 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
- 	    wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
- 		const char *ft_iface;
- 
--		ft_iface = hapd->conf->bridge[0] ? hapd->conf->bridge :
--			   hapd->conf->iface;
-+		if (hapd->conf->ft_iface[0])
-+			ft_iface = hapd->conf->ft_iface;
-+		else if (hapd->conf->bridge[0])
-+			ft_iface = hapd->conf->bridge;
-+		else
-+			ft_iface = hapd->conf->iface;
- 		hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB,
- 					  hostapd_rrb_receive, hapd, 1);
- 		if (!hapd->l2) {
-diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
-index 82d4d5fdd..dfc5c3ecb 100644
---- a/src/ap/wps_hostapd.c
-+++ b/src/ap/wps_hostapd.c
-@@ -394,9 +394,8 @@ static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd,
- 				bss->wpa_pairwise |= WPA_CIPHER_GCMP;
- 			else
- 				bss->wpa_pairwise |= WPA_CIPHER_CCMP;
--		}
- #ifndef CONFIG_NO_TKIP
--		if (cred->encr_type & WPS_ENCR_TKIP)
-+		} else if (cred->encr_type & WPS_ENCR_TKIP)
- 			bss->wpa_pairwise |= WPA_CIPHER_TKIP;
- #endif /* CONFIG_NO_TKIP */
- 		bss->rsn_pairwise = bss->wpa_pairwise;
-@@ -1181,8 +1180,7 @@ int hostapd_init_wps(struct hostapd_data *hapd,
- 					  WPA_CIPHER_GCMP_256)) {
- 			wps->encr_types |= WPS_ENCR_AES;
- 			wps->encr_types_rsn |= WPS_ENCR_AES;
--		}
--		if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
-+		} else if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
- #ifdef CONFIG_NO_TKIP
- 			wpa_printf(MSG_INFO, "WPS: TKIP not supported");
- 			goto fail;
-diff --git a/src/ap/x_snoop.c b/src/ap/x_snoop.c
-index 029f4de23..4c20f137f 100644
---- a/src/ap/x_snoop.c
-+++ b/src/ap/x_snoop.c
-@@ -33,28 +33,31 @@ int x_snoop_init(struct hostapd_data *hapd)
- 
- 	hapd->x_snoop_initialized = true;
- 
--	if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
-+	if (!conf->snoop_iface[0] &&
-+	    hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
- 					 1)) {
- 		wpa_printf(MSG_DEBUG,
- 			   "x_snoop: Failed to enable hairpin_mode on the bridge port");
- 		return -1;
- 	}
- 
--	if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
-+	if (!conf->snoop_iface[0] &&
-+	    hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
- 		wpa_printf(MSG_DEBUG,
- 			   "x_snoop: Failed to enable proxyarp on the bridge port");
- 		return -1;
- 	}
- 
- 	if (hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
--					 1)) {
-+					 conf->snoop_iface[0] ? conf->snoop_iface : NULL, 1)) {
- 		wpa_printf(MSG_DEBUG,
- 			   "x_snoop: Failed to enable accepting gratuitous ARP on the bridge");
- 		return -1;
- 	}
- 
- #ifdef CONFIG_IPV6
--	if (hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, 1)) {
-+	if (!conf->snoop_iface[0] &&
-+	    hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, NULL, 1)) {
- 		wpa_printf(MSG_DEBUG,
- 			   "x_snoop: Failed to enable multicast snooping on the bridge");
- 		return -1;
-@@ -73,8 +76,12 @@ x_snoop_get_l2_packet(struct hostapd_data *hapd,
- {
- 	struct hostapd_bss_config *conf = hapd->conf;
- 	struct l2_packet_data *l2;
-+	const char *ifname = conf->bridge;
-+
-+	if (conf->snoop_iface[0])
-+		ifname = conf->snoop_iface;
- 
--	l2 = l2_packet_init(conf->bridge, NULL, ETH_P_ALL, handler, hapd, 1);
-+	l2 = l2_packet_init(ifname, NULL, ETH_P_ALL, handler, hapd, 1);
- 	if (l2 == NULL) {
- 		wpa_printf(MSG_DEBUG,
- 			   "x_snoop: Failed to initialize L2 packet processing %s",
-@@ -127,9 +134,12 @@ void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
- 
- void x_snoop_deinit(struct hostapd_data *hapd)
- {
-+	struct hostapd_bss_config *conf = hapd->conf;
-+
- 	if (!hapd->x_snoop_initialized)
- 		return;
--	hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0);
-+	hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
-+				     conf->snoop_iface[0] ? conf->snoop_iface : NULL, 0);
- 	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0);
- 	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0);
- 	hapd->x_snoop_initialized = false;
-diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c
-index f17f95a2c..39d39f429 100644
---- a/src/common/dpp_crypto.c
-+++ b/src/common/dpp_crypto.c
-@@ -269,6 +269,12 @@ int dpp_get_pubkey_hash(struct crypto_ec_key *key, u8 *hash)
- 
- struct crypto_ec_key * dpp_gen_keypair(const struct dpp_curve_params *curve)
- {
-+	if (curve == NULL) {
-+		wpa_printf(MSG_DEBUG,
-+		           "DPP: %s curve must be initialized", __func__);
-+		return NULL;
-+	}
-+
- 	struct crypto_ec_key *key;
- 
- 	wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
-@@ -1582,7 +1588,9 @@ dpp_pkex_derive_Qr(const struct dpp_curve_params *curve, const u8 *mac_resp,
- 	Pr = crypto_ec_key_get_public_key(Pr_key);
- 	Qr = crypto_ec_point_init(ec);
- 	hash_bn = crypto_bignum_init_set(hash, curve->hash_len);
--	if (!Pr || !Qr || !hash_bn || crypto_ec_point_mul(ec, Pr, hash_bn, Qr))
-+	if (!Pr || !Qr || !hash_bn ||
-+	    crypto_bignum_mod(hash_bn, crypto_ec_get_prime(ec), hash_bn) ||
-+	    crypto_ec_point_mul(ec, Pr, hash_bn, Qr))
- 		goto fail;
- 
- 	if (crypto_ec_point_is_at_infinity(ec, Qr)) {
-diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
-index 2c47bf812..8bd6e994d 100644
---- a/src/common/hw_features_common.c
-+++ b/src/common/hw_features_common.c
-@@ -898,6 +898,7 @@ int ieee80211ac_cap_check(u32 hw, u32 conf)
- 	VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
- 	VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
- 	VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
-+	VHT_CAP_CHECK(VHT_CAP_EXTENDED_NSS_BW_SUPPORT);
- 
- #undef VHT_CAP_CHECK
- #undef VHT_CAP_CHECK_MAX
-diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
-index 5b39a61e1..7a1da3252 100644
---- a/src/common/ieee802_11_defs.h
-+++ b/src/common/ieee802_11_defs.h
-@@ -1397,6 +1397,8 @@ struct ieee80211_ampe_ie {
- #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB     ((u32) BIT(26) | BIT(27))
- #define VHT_CAP_RX_ANTENNA_PATTERN                  ((u32) BIT(28))
- #define VHT_CAP_TX_ANTENNA_PATTERN                  ((u32) BIT(29))
-+#define VHT_CAP_EXTENDED_NSS_BW_SUPPORT		    ((u32) BIT(30))
-+#define VHT_CAP_EXTENDED_NSS_BW_SUPPORT_MASK	    ((u32) BIT(30) | BIT(31))
- 
- #define VHT_OPMODE_CHANNEL_WIDTH_MASK		    ((u8) BIT(0) | BIT(1))
- #define VHT_OPMODE_CHANNEL_RxNSS_MASK		    ((u8) BIT(4) | BIT(5) | \
-diff --git a/src/common/sae.c b/src/common/sae.c
-index f1c164e13..05de737e5 100644
---- a/src/common/sae.c
-+++ b/src/common/sae.c
-@@ -1278,6 +1278,13 @@ void sae_deinit_pt(struct sae_pt *pt)
- static int sae_derive_commit_element_ecc(struct sae_data *sae,
- 					 struct crypto_bignum *mask)
- {
-+	if (sae->tmp->pwe_ecc == NULL) {
-+		wpa_printf(MSG_DEBUG,
-+		           "SAE: %s sae->tmp->pwe_ecc must be initialized",
-+		           __func__);
-+		return -1;
-+	}
-+
- 	/* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
- 	if (!sae->tmp->own_commit_element_ecc) {
- 		sae->tmp->own_commit_element_ecc =
-diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
-index 6ea3311ce..7a608c30e 100644
---- a/src/common/wpa_common.c
-+++ b/src/common/wpa_common.c
-@@ -2856,6 +2856,31 @@ u32 wpa_akm_to_suite(int akm)
- }
- 
- 
-+static void wpa_fixup_wpa_ie_rsn(u8 *assoc_ie, const u8 *wpa_msg_ie,
-+				 size_t rsn_ie_len)
-+{
-+	int pos, count;
-+
-+	pos = sizeof(struct rsn_ie_hdr) + RSN_SELECTOR_LEN;
-+	if (rsn_ie_len < pos + 2)
-+		return;
-+
-+	count = WPA_GET_LE16(wpa_msg_ie + pos);
-+	pos += 2 + count * RSN_SELECTOR_LEN;
-+	if (rsn_ie_len < pos + 2)
-+		return;
-+
-+	count = WPA_GET_LE16(wpa_msg_ie + pos);
-+	pos += 2 + count * RSN_SELECTOR_LEN;
-+	if (rsn_ie_len < pos + 2)
-+		return;
-+
-+	if (!assoc_ie[pos] && !assoc_ie[pos + 1] &&
-+	    (wpa_msg_ie[pos] || wpa_msg_ie[pos + 1]))
-+		memcpy(&assoc_ie[pos], &wpa_msg_ie[pos], 2);
-+}
-+
-+
- int wpa_compare_rsn_ie(int ft_initial_assoc,
- 		       const u8 *ie1, size_t ie1len,
- 		       const u8 *ie2, size_t ie2len)
-@@ -2863,8 +2888,19 @@ int wpa_compare_rsn_ie(int ft_initial_assoc,
- 	if (ie1 == NULL || ie2 == NULL)
- 		return -1;
- 
--	if (ie1len == ie2len && os_memcmp(ie1, ie2, ie1len) == 0)
--		return 0; /* identical IEs */
-+	if (ie1len == ie2len) {
-+		u8 *ie_tmp;
-+
-+		if (os_memcmp(ie1, ie2, ie1len) == 0)
-+			return 0; /* identical IEs */
-+
-+		ie_tmp = alloca(ie1len);
-+		memcpy(ie_tmp, ie1, ie1len);
-+		wpa_fixup_wpa_ie_rsn(ie_tmp, ie2, ie1len);
-+
-+		if (os_memcmp(ie_tmp, ie2, ie1len) == 0)
-+			return 0; /* only mismatch in RSN capabilties */
-+	}
- 
- #ifdef CONFIG_IEEE80211R
- 	if (ft_initial_assoc) {
-diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c
-index 7e197f094..791fdbf93 100644
---- a/src/common/wpa_ctrl.c
-+++ b/src/common/wpa_ctrl.c
-@@ -135,7 +135,7 @@ try_again:
- 		return NULL;
- 	}
- 	tries++;
--#ifdef ANDROID
-+
- 	/* Set client socket file permissions so that bind() creates the client
- 	 * socket with these permissions and there is no need to try to change
- 	 * them with chmod() after bind() which would have potential issues with
-@@ -147,7 +147,7 @@ try_again:
- 	 * operations to allow the response to go through. Those are using the
- 	 * no-deference-symlinks version to avoid races. */
- 	fchmod(ctrl->s, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
--#endif /* ANDROID */
-+
- 	if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
- 		    sizeof(ctrl->local)) < 0) {
- 		if (errno == EADDRINUSE && tries < 2) {
-@@ -165,7 +165,11 @@ try_again:
- 		return NULL;
- 	}
- 
--#ifdef ANDROID
-+#ifndef ANDROID
-+	/* Set group even if we do not have privileges to change owner */
-+	lchown(ctrl->local.sun_path, -1, 101);
-+	lchown(ctrl->local.sun_path, 101, 101);
-+#else
- 	/* Set group even if we do not have privileges to change owner */
- 	lchown(ctrl->local.sun_path, -1, AID_WIFI);
- 	lchown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
-diff --git a/src/crypto/Makefile b/src/crypto/Makefile
-index ce0997091..96bac9476 100644
---- a/src/crypto/Makefile
-+++ b/src/crypto/Makefile
-@@ -1,10 +1,121 @@
--CFLAGS += -DCONFIG_CRYPTO_INTERNAL
--CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
--CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
- #CFLAGS += -DALL_DH_GROUPS
- CFLAGS += -DCONFIG_SHA256
- CFLAGS += -DCONFIG_SHA384
-+CFLAGS += -DCONFIG_HMAC_SHA256_KDF
- CFLAGS += -DCONFIG_HMAC_SHA384_KDF
-+
-+# crypto_module_tests.c
-+CFLAGS += -DCONFIG_MODULE_TESTS
-+CFLAGS += -DCONFIG_DPP
-+#CFLAGS += -DCONFIG_DPP2
-+#CFLAGS += -DCONFIG_DPP3
-+CFLAGS += -DCONFIG_ECC
-+CFLAGS += -DCONFIG_MESH
-+CFLAGS += -DEAP_PSK
-+CFLAGS += -DEAP_FAST
-+
-+ifeq ($(CONFIG_TLS),mbedtls)
-+
-+# (enable features for 'cd tests; make run-tests CONFIG_TLS=mbedtls')
-+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
-+CFLAGS += -DCONFIG_DES
-+CFLAGS += -DEAP_IKEV2
-+CFLAGS += -DEAP_MSCHAPv2
-+CFLAGS += -DEAP_SIM
-+
-+LIB_OBJS = tls_mbedtls.o crypto_mbedtls.o
-+LIB_OBJS+= \
-+	aes-eax.o \
-+	aes-siv.o \
-+	dh_groups.o \
-+	milenage.o \
-+	ms_funcs.o
-+
-+else
-+ifeq ($(CONFIG_TLS),openssl)
-+
-+# (enable features for 'cd tests; make run-tests CONFIG_TLS=openssl')
-+ifndef CONFIG_TLS_DEFAULT_CIPHERS
-+CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
-+endif
-+CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
-+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
-+CFLAGS += -DEAP_TLS_OPENSSL
-+
-+LIB_OBJS = tls_openssl.o fips_prf_openssl.o crypto_openssl.o
-+LIB_OBJS+= \
-+	aes-ctr.o \
-+	aes-eax.o \
-+	aes-encblock.o \
-+	aes-siv.o \
-+	dh_groups.o \
-+	milenage.o \
-+	ms_funcs.o \
-+	sha1-prf.o \
-+	sha1-tlsprf.o \
-+	sha1-tprf.o \
-+	sha256-kdf.o \
-+	sha256-prf.o \
-+	sha256-tlsprf.o
-+
-+else
-+ifeq ($(CONFIG_TLS),wolfssl)
-+
-+# (wolfssl libraries must be built with ./configure --enable-wpas)
-+# (enable features for 'cd tests; make run-tests CONFIG_TLS=wolfssl')
-+CFLAGS += -DWOLFSSL_DER_LOAD
-+CFLAGS += -DCONFIG_DES
-+
-+LIB_OBJS = tls_wolfssl.o fips_prf_wolfssl.o crypto_wolfssl.o
-+LIB_OBJS+= \
-+	aes-ctr.o \
-+	aes-eax.o \
-+	aes-encblock.o \
-+	aes-siv.o \
-+	dh_groups.o \
-+	milenage.o \
-+	ms_funcs.o \
-+	sha1-prf.o \
-+	sha1-tlsprf.o \
-+	sha1-tprf.o \
-+	sha256-kdf.o \
-+	sha256-prf.o \
-+	sha256-tlsprf.o
-+
-+else
-+ifeq ($(CONFIG_TLS),gnutls)
-+
-+# (enable features for 'cd tests; make run-tests CONFIG_TLS=gnutls')
-+LIB_OBJS = tls_gnutls.o crypto_gnutls.o
-+LIB_OBJS+= \
-+	aes-cbc.o \
-+	aes-ctr.o \
-+	aes-eax.o \
-+	aes-encblock.o \
-+	aes-omac1.o \
-+	aes-siv.o \
-+	aes-unwrap.o \
-+	aes-wrap.o \
-+	dh_group5.o \
-+	dh_groups.o \
-+	milenage.o \
-+	ms_funcs.o \
-+	rc4.o \
-+	sha1-pbkdf2.o \
-+	sha1-prf.o \
-+	fips_prf_internal.o \
-+	sha1-internal.o \
-+	sha1-tlsprf.o \
-+	sha1-tprf.o \
-+	sha256-kdf.o \
-+	sha256-prf.o \
-+	sha256-tlsprf.o
-+
-+else
-+
-+CFLAGS += -DCONFIG_CRYPTO_INTERNAL
-+CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
-+CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
- CFLAGS += -DCONFIG_INTERNAL_SHA384
- 
- LIB_OBJS= \
-@@ -13,7 +124,6 @@ LIB_OBJS= \
- 	aes-ctr.o \
- 	aes-eax.o \
- 	aes-encblock.o \
--	aes-gcm.o \
- 	aes-internal.o \
- 	aes-internal-dec.o \
- 	aes-internal-enc.o \
-@@ -37,6 +147,7 @@ LIB_OBJS= \
- 	sha1-tlsprf.o \
- 	sha1-tprf.o \
- 	sha256.o \
-+	sha256-kdf.o \
- 	sha256-prf.o \
- 	sha256-tlsprf.o \
- 	sha256-internal.o \
-@@ -53,6 +164,16 @@ LIB_OBJS += crypto_internal-modexp.o
- LIB_OBJS += crypto_internal-rsa.o
- LIB_OBJS += tls_internal.o
- LIB_OBJS += fips_prf_internal.o
-+
-+endif
-+endif
-+endif
-+endif
-+
-+
-+# (used by wlantest/{bip,gcmp,rx_mgmt}.c and tests/test-aes.c)
-+LIB_OBJS += aes-gcm.o
-+
- ifndef TEST_FUZZ
- LIB_OBJS += random.o
- endif
-diff --git a/src/crypto/crypto_mbedtls.c b/src/crypto/crypto_mbedtls.c
-new file mode 100644
-index 000000000..7a91c965f
---- /dev/null
-+++ b/src/crypto/crypto_mbedtls.c
-@@ -0,0 +1,4228 @@
-+/*
-+ * crypto wrapper functions for mbed TLS
-+ *
-+ * SPDX-FileCopyrightText: 2022 Glenn Strauss <gstrauss@gluelogic.com>
-+ * SPDX-License-Identifier: BSD-3-Clause
-+ */
-+
-+#include "utils/includes.h"
-+#include "utils/common.h"
-+
-+#include <mbedtls/version.h>
-+#include <mbedtls/entropy.h>
-+#include <mbedtls/ctr_drbg.h>
-+#include <mbedtls/platform_util.h> /* mbedtls_platform_zeroize() */
-+#include <mbedtls/asn1.h>
-+#include <mbedtls/asn1write.h>
-+#include <mbedtls/aes.h>
-+#include <mbedtls/md.h>
-+#include <mbedtls/md5.h>
-+#include <mbedtls/sha1.h>
-+#include <mbedtls/sha256.h>
-+#include <mbedtls/sha512.h>
-+
-+#ifndef MBEDTLS_PRIVATE
-+#define MBEDTLS_PRIVATE(x) x
-+#endif
-+
-+/* hostapd/wpa_supplicant provides forced_memzero(),
-+ * but prefer mbedtls_platform_zeroize() */
-+#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz)
-+
-+#ifndef __has_attribute
-+#define __has_attribute(x) 0
-+#endif
-+
-+#ifndef __GNUC_PREREQ
-+#define __GNUC_PREREQ(maj,min) 0
-+#endif
-+
-+#ifndef __attribute_cold__
-+#if __has_attribute(cold) \
-+ || __GNUC_PREREQ(4,3)
-+#define __attribute_cold__  __attribute__((__cold__))
-+#else
-+#define __attribute_cold__
-+#endif
-+#endif
-+
-+#ifndef __attribute_noinline__
-+#if __has_attribute(noinline) \
-+ || __GNUC_PREREQ(3,1)
-+#define __attribute_noinline__  __attribute__((__noinline__))
-+#else
-+#define __attribute_noinline__
-+#endif
-+#endif
-+
-+#include "crypto.h"
-+#include "aes_wrap.h"
-+#include "aes.h"
-+#include "md5.h"
-+#include "sha1.h"
-+#include "sha256.h"
-+#include "sha384.h"
-+#include "sha512.h"
-+
-+
-+/*
-+ * selective code inclusion based on preprocessor defines
-+ *
-+ * future: additional code could be wrapped with preprocessor checks if
-+ * wpa_supplicant/Makefile and hostap/Makefile were more consistent with
-+ * setting preprocessor defines for named groups of functionality
-+ */
-+
-+#if defined(CONFIG_FIPS)
-+#undef MBEDTLS_MD4_C     /* omit md4_vector() */
-+#undef MBEDTLS_MD5_C     /* omit md5_vector() hmac_md5_vector() hmac_md5() */
-+#undef MBEDTLS_DES_C     /* omit des_encrypt() */
-+#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */
-+#define CRYPTO_MBEDTLS_CONFIG_FIPS
-+#endif
-+
-+#if !defined(CONFIG_FIPS)
-+#if defined(EAP_PWD) \
-+ || defined(EAP_LEAP) || defined(EAP_LEAP_DYNAMIC) \
-+ || defined(EAP_TTLS) || defined(EAP_TTLS_DYNAMIC) \
-+ || defined(EAP_MSCHAPv2) || defined(EAP_MSCHAPv2_DYNAMIC) \
-+ || defined(EAP_SERVER_MSCHAPV2)
-+#ifndef MBEDTLS_MD4_C    /* (MD4 not in mbedtls 3.x) */
-+#include "md4-internal.c"/* pull in hostap local implementation */
-+#endif /* md4_vector() */
-+#else
-+#undef MBEDTLS_MD4_C     /* omit md4_vector() */
-+#endif
-+#endif
-+
-+#if !defined(CONFIG_NO_RC4) && !defined(CONFIG_NO_WPA)
-+#ifndef MBEDTLS_ARC4_C   /* (RC4 not in mbedtls 3.x) */
-+#include "rc4.c"         /* pull in hostap local implementation */
-+#endif /* rc4_skip() */
-+#else
-+#undef MBEDTLS_ARC4_C    /* omit rc4_skip() */
-+#endif
-+
-+#if defined(CONFIG_MACSEC)     \
-+ || defined(CONFIG_NO_RADIUS)  \
-+ || defined(CONFIG_IEEE80211R) \
-+ || defined(EAP_SERVER_FAST)   \
-+ || defined(EAP_SERVER_TEAP)   \
-+ || !defined(CONFIG_NO_WPA)
-+       /* aes_wrap() aes_unwrap() */
-+#else
-+#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */
-+#endif
-+
-+#if !defined(CONFIG_SHA256)
-+#undef MBEDTLS_SHA256_C
-+#endif
-+
-+#if !defined(CONFIG_SHA384) && !defined(CONFIG_SHA512)
-+#undef MBEDTLS_SHA512_C
-+#endif
-+
-+#if defined(CONFIG_HMAC_SHA256_KDF)
-+#define CRYPTO_MBEDTLS_HMAC_KDF_SHA256
-+#endif
-+#if defined(CONFIG_HMAC_SHA384_KDF)
-+#define CRYPTO_MBEDTLS_HMAC_KDF_SHA384
-+#endif
-+#if defined(CONFIG_HMAC_SHA512_KDF)
-+#define CRYPTO_MBEDTLS_HMAC_KDF_SHA512
-+#endif
-+
-+#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \
-+ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA)
-+/* EAP_SIM=y EAP_AKA=y */
-+#define CRYPTO_MBEDTLS_FIPS186_2_PRF
-+#endif
-+
-+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
-+ || defined(EAP_TEAP) || defined(EAP_TEAP_DYNAMIC) || defined(EAP_SERVER_FAST)
-+#define CRYPTO_MBEDTLS_SHA1_T_PRF
-+#endif
-+
-+#if defined(CONFIG_DES)
-+#define CRYPTO_MBEDTLS_DES_ENCRYPT
-+#endif /* des_encrypt() */
-+
-+#if !defined(CONFIG_NO_PBKDF2)
-+#define CRYPTO_MBEDTLS_PBKDF2_SHA1
-+#endif /* pbkdf2_sha1() */
-+
-+#if defined(EAP_IKEV2) \
-+ || defined(EAP_IKEV2_DYNAMIC) \
-+ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_CIPHER
-+#endif /* crypto_cipher_*() */
-+
-+#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_HASH
-+#endif /* crypto_hash_*() */
-+
-+#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */ \
-+ || defined(CONFIG_SAE) /* CONFIG_SAE=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+#endif /* crypto_bignum_*() */
-+
-+#if defined(EAP_PWD)          /* CONFIG_EAP_PWD=y */    \
-+ || defined(EAP_EKE)          /* CONFIG_EAP_EKE=y */    \
-+ || defined(EAP_EKE_DYNAMIC)  /* CONFIG_EAP_EKE=y */    \
-+ || defined(EAP_SERVER_EKE)   /* CONFIG_EAP_EKE=y */    \
-+ || defined(EAP_IKEV2)        /* CONFIG_EAP_IKEV2y */   \
-+ || defined(EAP_IKEV2_DYNAMIC)/* CONFIG_EAP_IKEV2=y */  \
-+ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */  \
-+ || defined(CONFIG_SAE)       /* CONFIG_SAE=y */        \
-+ || defined(CONFIG_WPS)       /* CONFIG_WPS=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_DH
-+#if defined(CONFIG_WPS_NFC)
-+#define CRYPTO_MBEDTLS_DH5_INIT_FIXED
-+#endif /* dh5_init_fixed() */
-+#endif /* crypto_dh_*() */
-+
-+#if !defined(CONFIG_NO_WPA) /* CONFIG_NO_WPA= */
-+#define CRYPTO_MBEDTLS_CRYPTO_ECDH
-+#endif /* crypto_ecdh_*() */
-+
-+#if defined(CONFIG_ECC)
-+#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+#define CRYPTO_MBEDTLS_CRYPTO_EC
-+#endif /* crypto_ec_*() crypto_ec_key_*() */
-+
-+#if defined(CONFIG_DPP) /* CONFIG_DPP=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_EC_DPP /* extra for DPP */
-+#define CRYPTO_MBEDTLS_CRYPTO_CSR
-+#endif /* crypto_csr_*() */
-+
-+#if defined(CONFIG_DPP3) /* CONFIG_DPP3=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_HPKE
-+#endif
-+
-+#if defined(CONFIG_DPP2) /* CONFIG_DPP2=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_PKCS7
-+#endif /* crypto_pkcs7_*() */
-+
-+#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \
-+ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA) \
-+ || defined(CONFIG_AP) || defined(HOSTAPD)
-+/* CONFIG_EAP_SIM=y CONFIG_EAP_AKA=y CONFIG_AP=y HOSTAPD */
-+#if defined(CRYPTO_RSA_OAEP_SHA256)
-+#define CRYPTO_MBEDTLS_CRYPTO_RSA
-+#endif
-+#endif /* crypto_rsa_*() */
-+
-+
-+static int ctr_drbg_init_state;
-+static mbedtls_ctr_drbg_context ctr_drbg;
-+static mbedtls_entropy_context entropy;
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+#include <mbedtls/bignum.h>
-+static mbedtls_mpi mpi_sw_A;
-+#endif
-+
-+__attribute_cold__
-+__attribute_noinline__
-+static mbedtls_ctr_drbg_context * ctr_drbg_init(void)
-+{
-+	mbedtls_ctr_drbg_init(&ctr_drbg);
-+	mbedtls_entropy_init(&entropy);
-+	if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
-+	                          NULL, 0)) {
-+		wpa_printf(MSG_ERROR, "Init of random number generator failed");
-+		/* XXX: abort? */
-+	}
-+	else
-+		ctr_drbg_init_state = 1;
-+
-+	return &ctr_drbg;
-+}
-+
-+__attribute_cold__
-+void crypto_unload(void)
-+{
-+	if (ctr_drbg_init_state) {
-+		mbedtls_ctr_drbg_free(&ctr_drbg);
-+		mbedtls_entropy_free(&entropy);
-+	  #ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+		mbedtls_mpi_free(&mpi_sw_A);
-+	  #endif
-+		ctr_drbg_init_state = 0;
-+	}
-+}
-+
-+/* init ctr_drbg on first use
-+ * crypto_global_init() and crypto_global_deinit() are not available here
-+ * (available only when CONFIG_TLS=internal, which is not CONFIG_TLS=mbedtls) */
-+mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/
-+inline
-+mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void)
-+{
-+	return ctr_drbg_init_state ? &ctr_drbg : ctr_drbg_init();
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CONFIG_FIPS
-+int crypto_get_random(void *buf, size_t len)
-+{
-+	return mbedtls_ctr_drbg_random(crypto_mbedtls_ctr_drbg(),buf,len) ? -1 : 0;
-+}
-+#endif
-+
-+
-+#if 1
-+
-+/* tradeoff: slightly smaller code size here at cost of slight increase
-+ * in instructions and function calls at runtime versus the expanded
-+ * per-message-digest code that follows in #else (~0.5 kib .text larger) */
-+
-+__attribute_noinline__
-+static int md_vector(size_t num_elem, const u8 *addr[], const size_t *len,
-+                     u8 *mac, mbedtls_md_type_t md_type)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	mbedtls_md_context_t ctx;
-+	mbedtls_md_init(&ctx);
-+	if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0) != 0){
-+		mbedtls_md_free(&ctx);
-+		return -1;
-+	}
-+	mbedtls_md_starts(&ctx);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_md_update(&ctx, addr[i], len[i]);
-+	mbedtls_md_finish(&ctx, mac);
-+	mbedtls_md_free(&ctx);
-+	return 0;
-+}
-+
-+#ifdef MBEDTLS_SHA512_C
-+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA512);
-+}
-+
-+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA384);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA256);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA1_C
-+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA1);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD5_C
-+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD5);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD4_C
-+#include <mbedtls/md4.h>
-+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD4);
-+}
-+#endif
-+
-+#else  /* expanded per-message-digest functions */
-+
-+#ifdef MBEDTLS_SHA512_C
-+#include <mbedtls/sha512.h>
-+__attribute_noinline__
-+static int sha384_512_vector(size_t num_elem, const u8 *addr[],
-+                             const size_t *len, u8 *mac, int is384)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	struct mbedtls_sha512_context ctx;
-+	mbedtls_sha512_init(&ctx);
-+  #if MBEDTLS_VERSION_MAJOR >= 3
-+	mbedtls_sha512_starts(&ctx, is384);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_sha512_update(&ctx, addr[i], len[i]);
-+	mbedtls_sha512_finish(&ctx, mac);
-+  #else
-+	mbedtls_sha512_starts_ret(&ctx, is384);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_sha512_update_ret(&ctx, addr[i], len[i]);
-+	mbedtls_sha512_finish_ret(&ctx, mac);
-+  #endif
-+	mbedtls_sha512_free(&ctx);
-+	return 0;
-+}
-+
-+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return sha384_512_vector(num_elem, addr, len, mac, 0);
-+}
-+
-+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return sha384_512_vector(num_elem, addr, len, mac, 1);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+#include <mbedtls/sha256.h>
-+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	struct mbedtls_sha256_context ctx;
-+	mbedtls_sha256_init(&ctx);
-+  #if MBEDTLS_VERSION_MAJOR >= 3
-+	mbedtls_sha256_starts(&ctx, 0);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_sha256_update(&ctx, addr[i], len[i]);
-+	mbedtls_sha256_finish(&ctx, mac);
-+  #else
-+	mbedtls_sha256_starts_ret(&ctx, 0);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_sha256_update_ret(&ctx, addr[i], len[i]);
-+	mbedtls_sha256_finish_ret(&ctx, mac);
-+  #endif
-+	mbedtls_sha256_free(&ctx);
-+	return 0;
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA1_C
-+#include <mbedtls/sha1.h>
-+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	struct mbedtls_sha1_context ctx;
-+	mbedtls_sha1_init(&ctx);
-+  #if MBEDTLS_VERSION_MAJOR >= 3
-+	mbedtls_sha1_starts(&ctx);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_sha1_update(&ctx, addr[i], len[i]);
-+	mbedtls_sha1_finish(&ctx, mac);
-+  #else
-+	mbedtls_sha1_starts_ret(&ctx);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_sha1_update_ret(&ctx, addr[i], len[i]);
-+	mbedtls_sha1_finish_ret(&ctx, mac);
-+  #endif
-+	mbedtls_sha1_free(&ctx);
-+	return 0;
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD5_C
-+#include <mbedtls/md5.h>
-+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	struct mbedtls_md5_context ctx;
-+	mbedtls_md5_init(&ctx);
-+  #if MBEDTLS_VERSION_MAJOR >= 3
-+	mbedtls_md5_starts(&ctx);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_md5_update(&ctx, addr[i], len[i]);
-+	mbedtls_md5_finish(&ctx, mac);
-+  #else
-+	mbedtls_md5_starts_ret(&ctx);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_md5_update_ret(&ctx, addr[i], len[i]);
-+	mbedtls_md5_finish_ret(&ctx, mac);
-+  #endif
-+	mbedtls_md5_free(&ctx);
-+	return 0;
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD4_C
-+#include <mbedtls/md4.h>
-+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	struct mbedtls_md4_context ctx;
-+	mbedtls_md4_init(&ctx);
-+	mbedtls_md4_starts_ret(&ctx);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_md4_update_ret(&ctx, addr[i], len[i]);
-+	mbedtls_md4_finish_ret(&ctx, mac);
-+	mbedtls_md4_free(&ctx);
-+	return 0;
-+}
-+#endif
-+
-+#endif /* expanded per-message-digest functions */
-+
-+
-+__attribute_noinline__
-+static int hmac_vector(const u8 *key, size_t key_len, size_t num_elem,
-+                       const u8 *addr[], const size_t *len, u8 *mac,
-+                       mbedtls_md_type_t md_type)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	mbedtls_md_context_t ctx;
-+	mbedtls_md_init(&ctx);
-+	if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1) != 0){
-+		mbedtls_md_free(&ctx);
-+		return -1;
-+	}
-+	mbedtls_md_hmac_starts(&ctx, key, key_len);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+	mbedtls_md_hmac_finish(&ctx, mac);
-+	mbedtls_md_free(&ctx);
-+	return 0;
-+}
-+
-+#ifdef MBEDTLS_SHA512_C
-+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
-+                       const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+			   MBEDTLS_MD_SHA512);
-+}
-+
-+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+                u8 *mac)
-+{
-+	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+			   MBEDTLS_MD_SHA512);
-+}
-+
-+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
-+                       const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+			   MBEDTLS_MD_SHA384);
-+}
-+
-+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+                u8 *mac)
-+{
-+	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+			   MBEDTLS_MD_SHA384);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
-+                       const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+			   MBEDTLS_MD_SHA256);
-+}
-+
-+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+                u8 *mac)
-+{
-+	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+			   MBEDTLS_MD_SHA256);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA1_C
-+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
-+                     const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+			   MBEDTLS_MD_SHA1);
-+}
-+
-+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+              u8 *mac)
-+{
-+	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+			   MBEDTLS_MD_SHA1);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD5_C
-+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
-+                    const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+			   MBEDTLS_MD_MD5);
-+}
-+
-+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+             u8 *mac)
-+{
-+	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+			   MBEDTLS_MD_MD5);
-+}
-+#endif
-+
-+
-+#if defined(MBEDTLS_SHA256_C) || defined(MBEDTLS_SHA512_C)
-+
-+#if defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA256) \
-+ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA384) \
-+ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA512)
-+
-+#include <mbedtls/hkdf.h>
-+
-+/* sha256-kdf.c sha384-kdf.c sha512-kdf.c */
-+
-+/* HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869) */
-+/* HMAC-SHA384 KDF (RFC 5295) and HKDF-Expand(SHA384) (RFC 5869) */
-+/* HMAC-SHA512 KDF (RFC 5295) and HKDF-Expand(SHA512) (RFC 5869) */
-+__attribute_noinline__
-+static int hmac_kdf_expand(const u8 *prk, size_t prk_len,
-+                           const char *label, const u8 *info, size_t info_len,
-+                           u8 *okm, size_t okm_len, mbedtls_md_type_t md_type)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
-+  #ifdef MBEDTLS_HKDF_C
-+	if (label == NULL)  /* RFC 5869 HKDF-Expand when (label == NULL) */
-+		return mbedtls_hkdf_expand(md_info, prk, prk_len, info,
-+		                           info_len, okm, okm_len) ? -1 : 0;
-+  #endif
-+
-+	const size_t mac_len = mbedtls_md_get_size(md_info);
-+	/* okm_len must not exceed 255 times hash len (RFC 5869 Section 2.3) */
-+	if (okm_len > ((mac_len << 8) - mac_len))
-+		return -1;
-+
-+	mbedtls_md_context_t ctx;
-+	mbedtls_md_init(&ctx);
-+	if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
-+		mbedtls_md_free(&ctx);
-+		return -1;
-+	}
-+	mbedtls_md_hmac_starts(&ctx, prk, prk_len);
-+
-+	u8 iter = 1;
-+	const u8 *addr[4] = { okm, (const u8 *)label, info, &iter };
-+	size_t len[4] = { 0, label ? os_strlen(label)+1 : 0, info_len, 1 };
-+
-+	for (; okm_len >= mac_len; okm_len -= mac_len, ++iter) {
-+		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
-+			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+		mbedtls_md_hmac_finish(&ctx, okm);
-+		mbedtls_md_hmac_reset(&ctx);
-+		addr[0] = okm;
-+		okm += mac_len;
-+		len[0] = mac_len; /*(include digest in subsequent rounds)*/
-+	}
-+
-+	if (okm_len) {
-+		u8 hash[MBEDTLS_MD_MAX_SIZE];
-+		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
-+			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+		mbedtls_md_hmac_finish(&ctx, hash);
-+		os_memcpy(okm, hash, okm_len);
-+		forced_memzero(hash, mac_len);
-+	}
-+
-+	mbedtls_md_free(&ctx);
-+	return 0;
-+}
-+
-+#ifdef MBEDTLS_SHA512_C
-+#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA512
-+int hmac_sha512_kdf(const u8 *secret, size_t secret_len,
-+		    const char *label, const u8 *seed, size_t seed_len,
-+		    u8 *out, size_t outlen)
-+{
-+	return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
-+	                       out, outlen, MBEDTLS_MD_SHA512);
-+}
-+#endif
-+
-+#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA384
-+int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
-+		    const char *label, const u8 *seed, size_t seed_len,
-+		    u8 *out, size_t outlen)
-+{
-+	return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
-+	                       out, outlen, MBEDTLS_MD_SHA384);
-+}
-+#endif
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA256
-+int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
-+		    const char *label, const u8 *seed, size_t seed_len,
-+		    u8 *out, size_t outlen)
-+{
-+	return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
-+	                       out, outlen, MBEDTLS_MD_SHA256);
-+}
-+#endif
-+#endif
-+
-+#endif /* CRYPTO_MBEDTLS_HMAC_KDF_* */
-+
-+
-+/* sha256-prf.c sha384-prf.c sha512-prf.c */
-+
-+/* hmac_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function */
-+__attribute_noinline__
-+static int hmac_prf_bits(const u8 *key, size_t key_len, const char *label,
-+                         const u8 *data, size_t data_len, u8 *buf,
-+                         size_t buf_len_bits, mbedtls_md_type_t md_type)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	mbedtls_md_context_t ctx;
-+	mbedtls_md_init(&ctx);
-+	const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
-+	if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
-+		mbedtls_md_free(&ctx);
-+		return -1;
-+	}
-+	mbedtls_md_hmac_starts(&ctx, key, key_len);
-+
-+	u16 ctr, n_le = host_to_le16(buf_len_bits);
-+	const u8 * const addr[] = { (u8 *)&ctr,(u8 *)label,data,(u8 *)&n_le };
-+	const size_t len[] =      { 2, os_strlen(label), data_len, 2 };
-+	const size_t mac_len = mbedtls_md_get_size(md_info);
-+	size_t buf_len = (buf_len_bits + 7) / 8;
-+	for (ctr = 1; buf_len >= mac_len; buf_len -= mac_len, ++ctr) {
-+	  #if __BYTE_ORDER == __BIG_ENDIAN
-+		ctr = host_to_le16(ctr);
-+	  #endif
-+		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
-+			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+		mbedtls_md_hmac_finish(&ctx, buf);
-+		mbedtls_md_hmac_reset(&ctx);
-+		buf += mac_len;
-+	  #if __BYTE_ORDER == __BIG_ENDIAN
-+		ctr = le_to_host16(ctr);
-+	  #endif
-+	}
-+
-+	if (buf_len) {
-+		u8 hash[MBEDTLS_MD_MAX_SIZE];
-+	  #if __BYTE_ORDER == __BIG_ENDIAN
-+		ctr = host_to_le16(ctr);
-+	  #endif
-+		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
-+			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+		mbedtls_md_hmac_finish(&ctx, hash);
-+		os_memcpy(buf, hash, buf_len);
-+		buf += buf_len;
-+		forced_memzero(hash, mac_len);
-+	}
-+
-+	/* Mask out unused bits in last octet if it does not use all the bits */
-+	if ((buf_len_bits &= 0x7))
-+		buf[-1] &= (u8)(0xff << (8 - buf_len_bits));
-+
-+	mbedtls_md_free(&ctx);
-+	return 0;
-+}
-+
-+#ifdef MBEDTLS_SHA512_C
-+int sha512_prf(const u8 *key, size_t key_len, const char *label,
-+               const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
-+{
-+	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
-+	                     buf_len * 8, MBEDTLS_MD_SHA512);
-+}
-+
-+int sha384_prf(const u8 *key, size_t key_len, const char *label,
-+               const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
-+{
-+	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
-+	                     buf_len * 8, MBEDTLS_MD_SHA384);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+int sha256_prf(const u8 *key, size_t key_len, const char *label,
-+               const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
-+{
-+	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
-+	                     buf_len * 8, MBEDTLS_MD_SHA256);
-+}
-+
-+int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
-+                    const u8 *data, size_t data_len, u8 *buf,
-+                    size_t buf_len_bits)
-+{
-+	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
-+	                     buf_len_bits, MBEDTLS_MD_SHA256);
-+}
-+#endif
-+
-+#endif /* MBEDTLS_SHA256_C || MBEDTLS_SHA512_C */
-+
-+
-+#ifdef MBEDTLS_SHA1_C
-+
-+/* sha1-prf.c */
-+
-+/* sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) */
-+
-+int sha1_prf(const u8 *key, size_t key_len, const char *label,
-+	     const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
-+{
-+	/*(note: algorithm differs from hmac_prf_bits() */
-+	/*(note: smaller code size instead of expanding hmac_sha1_vector()
-+	 * as is done in hmac_prf_bits(); not expecting large num of loops) */
-+	u8 counter = 0;
-+	const u8 *addr[] = { (u8 *)label, data, &counter };
-+	const size_t len[] = { os_strlen(label)+1, data_len, 1 };
-+
-+	for (; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++counter) {
-+		if (hmac_sha1_vector(key, key_len, 3, addr, len, buf))
-+			return -1;
-+		buf += SHA1_MAC_LEN;
-+	}
-+
-+	if (buf_len) {
-+		u8 hash[SHA1_MAC_LEN];
-+		if (hmac_sha1_vector(key, key_len, 3, addr, len, hash))
-+			return -1;
-+		os_memcpy(buf, hash, buf_len);
-+		forced_memzero(hash, sizeof(hash));
-+	}
-+
-+	return 0;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_SHA1_T_PRF
-+
-+/* sha1-tprf.c */
-+
-+/* sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF) (RFC 4851,Section 5.5)*/
-+
-+int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
-+	       const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len)
-+{
-+	/*(note: algorithm differs from hmac_prf_bits() and hmac_kdf() above)*/
-+	/*(note: smaller code size instead of expanding hmac_sha1_vector()
-+	 * as is done in hmac_prf_bits(); not expecting large num of loops) */
-+	u8 ctr;
-+	u16 olen = host_to_be16(buf_len);
-+	const u8 *addr[] = { buf, (u8 *)label, seed, (u8 *)&olen, &ctr };
-+	size_t len[] = { 0, os_strlen(label)+1, seed_len, 2, 1 };
-+
-+	for (ctr = 1; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++ctr) {
-+		if (hmac_sha1_vector(key, key_len, 5, addr, len, buf))
-+			return -1;
-+		addr[0] = buf;
-+		buf += SHA1_MAC_LEN;
-+		len[0] = SHA1_MAC_LEN; /*(include digest in subsequent rounds)*/
-+	}
-+
-+	if (buf_len) {
-+		u8 hash[SHA1_MAC_LEN];
-+		if (hmac_sha1_vector(key, key_len, 5, addr, len, hash))
-+			return -1;
-+		os_memcpy(buf, hash, buf_len);
-+		forced_memzero(hash, sizeof(hash));
-+	}
-+
-+	return 0;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_SHA1_T_PRF */
-+
-+#ifdef CRYPTO_MBEDTLS_FIPS186_2_PRF
-+
-+/* fips_prf_internal.c sha1-internal.c */
-+
-+/* used only by src/eap_common/eap_sim_common.c:eap_sim_prf()
-+ * for eap_sim_derive_keys() and eap_sim_derive_keys_reauth()
-+ * where xlen is 160 */
-+
-+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
-+{
-+	/* FIPS 186-2 + change notice 1 */
-+
-+	mbedtls_sha1_context ctx;
-+	u8 * const xkey = ctx.MBEDTLS_PRIVATE(buffer);
-+	u32 * const xstate = ctx.MBEDTLS_PRIVATE(state);
-+	const u32 xstate_init[] =
-+	  { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 };
-+
-+	mbedtls_sha1_init(&ctx);
-+	os_memcpy(xkey, seed, seed_len < 64 ? seed_len : 64);
-+
-+	/* note: does not fill extra bytes if (xlen % 20) (SHA1_MAC_LEN) */
-+	for (; xlen >= 20; xlen -= 20) {
-+		/* XSEED_j = 0 */
-+		/* XVAL = (XKEY + XSEED_j) mod 2^b */
-+
-+		/* w_i = G(t, XVAL) */
-+		os_memcpy(xstate, xstate_init, sizeof(xstate_init));
-+		mbedtls_internal_sha1_process(&ctx, xkey);
-+
-+	  #if __BYTE_ORDER == __LITTLE_ENDIAN
-+		xstate[0] = host_to_be32(xstate[0]);
-+		xstate[1] = host_to_be32(xstate[1]);
-+		xstate[2] = host_to_be32(xstate[2]);
-+		xstate[3] = host_to_be32(xstate[3]);
-+		xstate[4] = host_to_be32(xstate[4]);
-+	  #endif
-+		os_memcpy(x, xstate, 20);
-+		if (xlen == 20) /*(done; skip prep for next loop)*/
-+			break;
-+
-+		/* XKEY = (1 + XKEY + w_i) mod 2^b */
-+		for (u32 carry = 1, k = 20; k-- > 0; carry >>= 8)
-+			xkey[k] = (carry += xkey[k] + x[k]) & 0xff;
-+		x += 20;
-+		/* x_j = w_0|w_1 (each pair of iterations through loop)*/
-+	}
-+
-+	mbedtls_sha1_free(&ctx);
-+	return 0;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_FIPS186_2_PRF */
-+
-+#endif /* MBEDTLS_SHA1_C */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_DES_ENCRYPT
-+#ifdef MBEDTLS_DES_C
-+#include <mbedtls/des.h>
-+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
-+{
-+	u8 pkey[8], next, tmp;
-+	int i;
-+
-+	/* Add parity bits to the key */
-+	next = 0;
-+	for (i = 0; i < 7; i++) {
-+		tmp = key[i];
-+		pkey[i] = (tmp >> i) | next | 1;
-+		next = tmp << (7 - i);
-+	}
-+	pkey[i] = next | 1;
-+
-+	mbedtls_des_context des;
-+	mbedtls_des_init(&des);
-+	int ret = mbedtls_des_setkey_enc(&des, pkey)
-+	       || mbedtls_des_crypt_ecb(&des, clear, cypher) ? -1 : 0;
-+	mbedtls_des_free(&des);
-+	return ret;
-+}
-+#else
-+#include "des-internal.c"/* pull in hostap local implementation */
-+#endif
-+#endif
-+
-+
-+#ifdef CRYPTO_MBEDTLS_PBKDF2_SHA1
-+/* sha1-pbkdf2.c */
-+#include <mbedtls/pkcs5.h>
-+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
-+                int iterations, u8 *buf, size_t buflen)
-+{
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03020200 /* mbedtls 3.2.2 */
-+	return mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA1,
-+			(const u8 *)passphrase, os_strlen(passphrase),
-+			ssid, ssid_len, iterations, 32, buf) ? -1 : 0;
-+  #else
-+	const mbedtls_md_info_t *md_info;
-+	md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
-+	if (md_info == NULL)
-+		return -1;
-+	mbedtls_md_context_t ctx;
-+	mbedtls_md_init(&ctx);
-+	int ret = mbedtls_md_setup(&ctx, md_info, 1)
-+	       || mbedtls_pkcs5_pbkdf2_hmac(&ctx,
-+			(const u8 *)passphrase, os_strlen(passphrase),
-+			ssid, ssid_len, iterations, 32, buf) ? -1 : 0;
-+	mbedtls_md_free(&ctx);
-+	return ret;
-+  #endif
-+}
-+#endif
-+
-+
-+/*#include "aes.h"*/ /* prototypes also included in "crypto.h" */
-+
-+static void *aes_crypt_init_mode(const u8 *key, size_t len, int mode)
-+{
-+	if (TEST_FAIL())
-+		return NULL;
-+
-+	mbedtls_aes_context *aes = os_malloc(sizeof(*aes));
-+	if (!aes)
-+		return NULL;
-+
-+	mbedtls_aes_init(aes);
-+	if ((mode == MBEDTLS_AES_ENCRYPT
-+	    ? mbedtls_aes_setkey_enc(aes, key, len * 8)
-+	    : mbedtls_aes_setkey_dec(aes, key, len * 8)) == 0)
-+		return aes;
-+
-+	mbedtls_aes_free(aes);
-+	os_free(aes);
-+	return NULL;
-+}
-+
-+void *aes_encrypt_init(const u8 *key, size_t len)
-+{
-+	return aes_crypt_init_mode(key, len, MBEDTLS_AES_ENCRYPT);
-+}
-+
-+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
-+{
-+	return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, plain, crypt);
-+}
-+
-+void aes_encrypt_deinit(void *ctx)
-+{
-+	mbedtls_aes_free(ctx);
-+	os_free(ctx);
-+}
-+
-+void *aes_decrypt_init(const u8 *key, size_t len)
-+{
-+	return aes_crypt_init_mode(key, len, MBEDTLS_AES_DECRYPT);
-+}
-+
-+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
-+{
-+	return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_DECRYPT, crypt, plain);
-+}
-+
-+void aes_decrypt_deinit(void *ctx)
-+{
-+	mbedtls_aes_free(ctx);
-+	os_free(ctx);
-+}
-+
-+
-+#include "aes_wrap.h"
-+
-+
-+#ifdef MBEDTLS_NIST_KW_C
-+
-+#include <mbedtls/nist_kw.h>
-+
-+/* aes-wrap.c */
-+int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	mbedtls_nist_kw_context ctx;
-+	mbedtls_nist_kw_init(&ctx);
-+	size_t olen;
-+	int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
-+	                                 kek, kek_len*8, 1)
-+	       || mbedtls_nist_kw_wrap(&ctx, MBEDTLS_KW_MODE_KW, plain, n*8,
-+	                               cipher, &olen, (n+1)*8) ? -1 : 0;
-+	mbedtls_nist_kw_free(&ctx);
-+	return ret;
-+}
-+
-+/* aes-unwrap.c */
-+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, u8 *plain)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	mbedtls_nist_kw_context ctx;
-+	mbedtls_nist_kw_init(&ctx);
-+	size_t olen;
-+	int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
-+	                                 kek, kek_len*8, 0)
-+	       || mbedtls_nist_kw_unwrap(&ctx, MBEDTLS_KW_MODE_KW, cipher,
-+	                                 (n+1)*8, plain, &olen, n*8) ? -1 : 0;
-+	mbedtls_nist_kw_free(&ctx);
-+	return ret;
-+}
-+
-+#else
-+
-+#ifndef CRYPTO_MBEDTLS_CONFIG_FIPS
-+#include "aes-wrap.c"    /* pull in hostap local implementation */
-+#include "aes-unwrap.c"  /* pull in hostap local implementation */
-+#endif
-+
-+#endif /* MBEDTLS_NIST_KW_C */
-+
-+
-+#ifdef MBEDTLS_CMAC_C
-+
-+/* aes-omac1.c */
-+
-+#include <mbedtls/cmac.h>
-+
-+int omac1_aes_vector(
-+    const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[],
-+    const size_t *len, u8 *mac)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	mbedtls_cipher_type_t cipher_type;
-+	switch (key_len) {
-+	case 16: cipher_type = MBEDTLS_CIPHER_AES_128_ECB; break;
-+	case 24: cipher_type = MBEDTLS_CIPHER_AES_192_ECB; break;
-+	case 32: cipher_type = MBEDTLS_CIPHER_AES_256_ECB; break;
-+	default: return -1;
-+	}
-+	const mbedtls_cipher_info_t *cipher_info;
-+	cipher_info = mbedtls_cipher_info_from_type(cipher_type);
-+	if (cipher_info == NULL)
-+		return -1;
-+
-+	mbedtls_cipher_context_t ctx;
-+	mbedtls_cipher_init(&ctx);
-+	int ret = -1;
-+	if (mbedtls_cipher_setup(&ctx, cipher_info) == 0
-+	    && mbedtls_cipher_cmac_starts(&ctx, key, key_len*8) == 0) {
-+		ret = 0;
-+		for (size_t i = 0; i < num_elem && ret == 0; ++i)
-+			ret = mbedtls_cipher_cmac_update(&ctx, addr[i], len[i]);
-+	}
-+	if (ret == 0)
-+		ret = mbedtls_cipher_cmac_finish(&ctx, mac);
-+	mbedtls_cipher_free(&ctx);
-+	return ret ? -1 : 0;
-+}
-+
-+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
-+			 const u8 *addr[], const size_t *len,
-+			 u8 *mac)
-+{
-+	return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
-+}
-+
-+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
-+{
-+	return omac1_aes_vector(key, 16, 1, &data, &data_len, mac);
-+}
-+
-+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
-+{
-+	return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
-+}
-+
-+#else
-+
-+#include "aes-omac1.c"  /* pull in hostap local implementation */
-+
-+#ifndef MBEDTLS_AES_BLOCK_SIZE
-+#define MBEDTLS_AES_BLOCK_SIZE 16
-+#endif
-+
-+#endif /* MBEDTLS_CMAC_C */
-+
-+
-+/* These interfaces can be inefficient when used in loops, as the overhead of
-+ * initialization each call is large for each block input (e.g. 16 bytes) */
-+
-+
-+/* aes-encblock.c */
-+int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	mbedtls_aes_context aes;
-+	mbedtls_aes_init(&aes);
-+	int ret = mbedtls_aes_setkey_enc(&aes, key, 128)
-+	       || mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_ENCRYPT, in, out)
-+	  ? -1
-+	  : 0;
-+	mbedtls_aes_free(&aes);
-+	return ret;
-+}
-+
-+
-+/* aes-ctr.c */
-+int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
-+		    u8 *data, size_t data_len)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	unsigned char counter[MBEDTLS_AES_BLOCK_SIZE];
-+	unsigned char stream_block[MBEDTLS_AES_BLOCK_SIZE];
-+	os_memcpy(counter, nonce, MBEDTLS_AES_BLOCK_SIZE);/*(must be writable)*/
-+
-+	mbedtls_aes_context ctx;
-+	mbedtls_aes_init(&ctx);
-+	size_t nc_off = 0;
-+	int ret = mbedtls_aes_setkey_enc(&ctx, key, key_len*8)
-+	       || mbedtls_aes_crypt_ctr(&ctx, data_len, &nc_off,
-+	                                counter, stream_block,
-+	                                data, data) ? -1 : 0;
-+	forced_memzero(stream_block, sizeof(stream_block));
-+	mbedtls_aes_free(&ctx);
-+	return ret;
-+}
-+
-+int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
-+			u8 *data, size_t data_len)
-+{
-+	return aes_ctr_encrypt(key, 16, nonce, data, data_len);
-+}
-+
-+
-+/* aes-cbc.c */
-+static int aes_128_cbc_oper(const u8 *key, const u8 *iv,
-+                            u8 *data, size_t data_len, int mode)
-+{
-+	unsigned char ivec[MBEDTLS_AES_BLOCK_SIZE];
-+	os_memcpy(ivec, iv, MBEDTLS_AES_BLOCK_SIZE); /*(must be writable)*/
-+
-+	mbedtls_aes_context ctx;
-+	mbedtls_aes_init(&ctx);
-+	int ret = (mode == MBEDTLS_AES_ENCRYPT
-+	           ? mbedtls_aes_setkey_enc(&ctx, key, 128)
-+	           : mbedtls_aes_setkey_dec(&ctx, key, 128))
-+	       || mbedtls_aes_crypt_cbc(&ctx, mode, data_len, ivec, data, data);
-+	mbedtls_aes_free(&ctx);
-+	return ret ? -1 : 0;
-+}
-+
-+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_ENCRYPT);
-+}
-+
-+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_DECRYPT);
-+}
-+
-+
-+/*
-+ * Much of the following is documented in crypto.h as for CONFIG_TLS=internal
-+ * but such comments are not accurate:
-+ *
-+ * "This function is only used with internal TLSv1 implementation
-+ *  (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
-+ *  to implement this."
-+ */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_CIPHER
-+
-+#include <mbedtls/cipher.h>
-+
-+struct crypto_cipher
-+{
-+	mbedtls_cipher_context_t ctx_enc;
-+	mbedtls_cipher_context_t ctx_dec;
-+};
-+
-+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
-+					  const u8 *iv, const u8 *key,
-+					  size_t key_len)
-+{
-+	/* IKEv2 src/eap_common/ikev2_common.c:ikev2_{encr,decr}_encrypt()
-+	 * uses one of CRYPTO_CIPHER_ALG_AES or CRYPTO_CIPHER_ALG_3DES */
-+
-+	mbedtls_cipher_type_t cipher_type;
-+	size_t iv_len;
-+	switch (alg) {
-+  #ifdef MBEDTLS_ARC4_C
-+  #if 0
-+	case CRYPTO_CIPHER_ALG_RC4:
-+		cipher_type = MBEDTLS_CIPHER_ARC4_128;
-+		iv_len = 0;
-+		break;
-+  #endif
-+  #endif
-+  #ifdef MBEDTLS_AES_C
-+	case CRYPTO_CIPHER_ALG_AES:
-+		if (key_len == 16) cipher_type = MBEDTLS_CIPHER_AES_128_CTR;
-+		if (key_len == 24) cipher_type = MBEDTLS_CIPHER_AES_192_CTR;
-+		if (key_len == 32) cipher_type = MBEDTLS_CIPHER_AES_256_CTR;
-+		iv_len = 16;
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_DES_C
-+	case CRYPTO_CIPHER_ALG_3DES:
-+		cipher_type = MBEDTLS_CIPHER_DES_EDE3_CBC;
-+		iv_len = 8;
-+		break;
-+  #if 0
-+	case CRYPTO_CIPHER_ALG_DES:
-+		cipher_type = MBEDTLS_CIPHER_DES_CBC;
-+		iv_len = 8;
-+		break;
-+  #endif
-+  #endif
-+	default:
-+		return NULL;
-+	}
-+
-+	const mbedtls_cipher_info_t *cipher_info;
-+	cipher_info = mbedtls_cipher_info_from_type(cipher_type);
-+	if (cipher_info == NULL)
-+		return NULL;
-+
-+	key_len *= 8; /* key_bitlen */
-+  #if 0 /*(were key_bitlen not already available)*/
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */
-+	key_len = mbedtls_cipher_info_get_key_bitlen(cipher_info);
-+  #else
-+	key_len = cipher_info->MBEDTLS_PRIVATE(key_bitlen);
-+  #endif
-+  #endif
-+
-+  #if 0 /*(were iv_len not known above, would need MBEDTLS_PRIVATE(iv_size))*/
-+	iv_len = cipher_info->MBEDTLS_PRIVATE(iv_size);
-+  #endif
-+
-+	struct crypto_cipher *ctx = os_malloc(sizeof(*ctx));
-+	if (!ctx)
-+		return NULL;
-+
-+	mbedtls_cipher_init(&ctx->ctx_enc);
-+	mbedtls_cipher_init(&ctx->ctx_dec);
-+	if (   mbedtls_cipher_setup(&ctx->ctx_enc,cipher_info) == 0
-+	    && mbedtls_cipher_setup(&ctx->ctx_dec,cipher_info) == 0
-+	    && mbedtls_cipher_setkey(&ctx->ctx_enc,key,key_len,MBEDTLS_ENCRYPT) == 0
-+	    && mbedtls_cipher_setkey(&ctx->ctx_dec,key,key_len,MBEDTLS_DECRYPT) == 0
-+	    && mbedtls_cipher_set_iv(&ctx->ctx_enc,iv,iv_len) == 0
-+	    && mbedtls_cipher_set_iv(&ctx->ctx_dec,iv,iv_len) == 0
-+	    && mbedtls_cipher_reset(&ctx->ctx_enc) == 0
-+	    && mbedtls_cipher_reset(&ctx->ctx_dec) == 0) {
-+		return ctx;
-+	}
-+
-+	mbedtls_cipher_free(&ctx->ctx_enc);
-+	mbedtls_cipher_free(&ctx->ctx_dec);
-+	os_free(ctx);
-+	return NULL;
-+}
-+
-+int crypto_cipher_encrypt(struct crypto_cipher *ctx,
-+			  const u8 *plain, u8 *crypt, size_t len)
-+{
-+	size_t olen = 0; /*(poor interface above; unknown size of u8 *crypt)*/
-+	return (mbedtls_cipher_update(&ctx->ctx_enc, plain, len, crypt, &olen)
-+	        || mbedtls_cipher_finish(&ctx->ctx_enc, crypt + olen, &olen)) ? -1 : 0;
-+}
-+
-+int crypto_cipher_decrypt(struct crypto_cipher *ctx,
-+			  const u8 *crypt, u8 *plain, size_t len)
-+{
-+	size_t olen = 0; /*(poor interface above; unknown size of u8 *plain)*/
-+	return (mbedtls_cipher_update(&ctx->ctx_dec, crypt, len, plain, &olen)
-+	        || mbedtls_cipher_finish(&ctx->ctx_dec, plain + olen, &olen)) ? -1 : 0;
-+}
-+
-+void crypto_cipher_deinit(struct crypto_cipher *ctx)
-+{
-+	mbedtls_cipher_free(&ctx->ctx_enc);
-+	mbedtls_cipher_free(&ctx->ctx_dec);
-+	os_free(ctx);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_CIPHER */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_HASH
-+
-+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
-+				      size_t key_len)
-+{
-+	mbedtls_md_type_t md_type;
-+	int is_hmac = 0;
-+
-+	switch (alg) {
-+  #ifdef MBEDTLS_MD5_C
-+	case CRYPTO_HASH_ALG_MD5:
-+		md_type = MBEDTLS_MD_MD5;
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_SHA1_C
-+	case CRYPTO_HASH_ALG_SHA1:
-+		md_type = MBEDTLS_MD_SHA1;
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_MD5_C
-+	case CRYPTO_HASH_ALG_HMAC_MD5:
-+		md_type = MBEDTLS_MD_MD5;
-+		is_hmac = 1;
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_SHA1_C
-+	case CRYPTO_HASH_ALG_HMAC_SHA1:
-+		md_type = MBEDTLS_MD_SHA1;
-+		is_hmac = 1;
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_SHA256_C
-+	case CRYPTO_HASH_ALG_SHA256:
-+		md_type = MBEDTLS_MD_SHA256;
-+		break;
-+	case CRYPTO_HASH_ALG_HMAC_SHA256:
-+		md_type = MBEDTLS_MD_SHA256;
-+		is_hmac = 1;
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_SHA512_C
-+	case CRYPTO_HASH_ALG_SHA384:
-+		md_type = MBEDTLS_MD_SHA384;
-+		break;
-+	case CRYPTO_HASH_ALG_SHA512:
-+		md_type = MBEDTLS_MD_SHA512;
-+		break;
-+  #endif
-+	default:
-+		return NULL;
-+	}
-+
-+	const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
-+	if (!md_info)
-+		return NULL;
-+
-+	mbedtls_md_context_t *mctx = os_malloc(sizeof(*mctx));
-+	if (mctx == NULL)
-+		return NULL;
-+
-+	mbedtls_md_init(mctx);
-+	if (mbedtls_md_setup(mctx, md_info, is_hmac) != 0) {
-+		os_free(mctx);
-+		return NULL;
-+	}
-+
-+	if (is_hmac)
-+		mbedtls_md_hmac_starts(mctx, key, key_len);
-+	else
-+		mbedtls_md_starts(mctx);
-+	return (struct crypto_hash *)((uintptr_t)mctx | is_hmac);
-+}
-+
-+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
-+{
-+	mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL);
-+  #if 0
-+	/*(mbedtls_md_hmac_update() and mbedtls_md_update()
-+	 * make same modifications under the hood in mbedtls)*/
-+	if ((uintptr_t)ctx & 1uL)
-+		mbedtls_md_hmac_update(mctx, data, len);
-+	else
-+  #endif
-+		mbedtls_md_update(mctx, data, len);
-+}
-+
-+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
-+{
-+	mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL);
-+	if (mac != NULL && len != NULL) { /*(NULL if caller just freeing context)*/
-+	  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
-+		const mbedtls_md_info_t *md_info = mbedtls_md_info_from_ctx(mctx);
-+	  #else
-+		const mbedtls_md_info_t *md_info = mctx->MBEDTLS_PRIVATE(md_info);
-+	  #endif
-+		size_t maclen = mbedtls_md_get_size(md_info);
-+		if (*len < maclen) {
-+			*len = maclen;
-+			/*(note: ctx not freed; can call again with larger *len)*/
-+			return -1;
-+		}
-+		*len = maclen;
-+		if ((uintptr_t)ctx & 1uL)
-+			mbedtls_md_hmac_finish(mctx, mac);
-+		else
-+			mbedtls_md_finish(mctx, mac);
-+	}
-+	mbedtls_md_free(mctx);
-+	os_free(mctx);
-+
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	return 0;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_HASH */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+
-+#include <mbedtls/bignum.h>
-+
-+/* crypto.h bignum interfaces */
-+
-+struct crypto_bignum *crypto_bignum_init(void)
-+{
-+	if (TEST_FAIL())
-+		return NULL;
-+
-+	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
-+	if (bn)
-+		mbedtls_mpi_init(bn);
-+	return (struct crypto_bignum *)bn;
-+}
-+
-+struct crypto_bignum *crypto_bignum_init_set(const u8 *buf, size_t len)
-+{
-+	if (TEST_FAIL())
-+		return NULL;
-+
-+	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
-+	if (bn) {
-+		mbedtls_mpi_init(bn);
-+		if (mbedtls_mpi_read_binary(bn, buf, len) == 0)
-+			return (struct crypto_bignum *)bn;
-+	}
-+
-+	os_free(bn);
-+	return NULL;
-+}
-+
-+struct crypto_bignum *crypto_bignum_init_uint(unsigned int val)
-+{
-+	if (TEST_FAIL())
-+		return NULL;
-+
-+  #if 0 /*(hostap use of this interface passes int, not uint)*/
-+	val = host_to_be32(val);
-+	return crypto_bignum_init_set((const u8 *)&val, sizeof(val));
-+  #else
-+	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
-+	if (bn) {
-+		mbedtls_mpi_init(bn);
-+		if (mbedtls_mpi_lset(bn, (int)val) == 0)
-+			return (struct crypto_bignum *)bn;
-+	}
-+
-+	os_free(bn);
-+	return NULL;
-+  #endif
-+}
-+
-+void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
-+{
-+	mbedtls_mpi_free((mbedtls_mpi *)n);
-+	os_free(n);
-+}
-+
-+int crypto_bignum_to_bin(const struct crypto_bignum *a,
-+			 u8 *buf, size_t buflen, size_t padlen)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	size_t n = mbedtls_mpi_size((mbedtls_mpi *)a);
-+	if (n < padlen)
-+		n = padlen;
-+	return n > buflen || mbedtls_mpi_write_binary((mbedtls_mpi *)a, buf, n)
-+	  ? -1
-+	  : (int)(n);
-+}
-+
-+int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	/*assert(r != m);*//* r must not be same as m for mbedtls_mpi_random()*/
-+  #if MBEDTLS_VERSION_NUMBER >= 0x021B0000 /* mbedtls 2.27.0 */
-+	return mbedtls_mpi_random((mbedtls_mpi *)r, 0, (mbedtls_mpi *)m,
-+				  mbedtls_ctr_drbg_random,
-+				  crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+  #else
-+	/* (needed by EAP_PWD, SAE, DPP) */
-+	wpa_printf(MSG_ERROR,
-+	           "mbedtls 2.27.0 or later required for mbedtls_mpi_random()");
-+	return -1;
-+  #endif
-+}
-+
-+int crypto_bignum_add(const struct crypto_bignum *a,
-+		      const struct crypto_bignum *b,
-+		      struct crypto_bignum *c)
-+{
-+	return mbedtls_mpi_add_mpi((mbedtls_mpi *)c,
-+				   (const mbedtls_mpi *)a,
-+				   (const mbedtls_mpi *)b) ? -1 : 0;
-+}
-+
-+int crypto_bignum_mod(const struct crypto_bignum *a,
-+		      const struct crypto_bignum *b,
-+		      struct crypto_bignum *c)
-+{
-+	return mbedtls_mpi_mod_mpi((mbedtls_mpi *)c,
-+				   (const mbedtls_mpi *)a,
-+				   (const mbedtls_mpi *)b) ? -1 : 0;
-+}
-+
-+int crypto_bignum_exptmod(const struct crypto_bignum *a,
-+			  const struct crypto_bignum *b,
-+			  const struct crypto_bignum *c,
-+			  struct crypto_bignum *d)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	/* (check if input params match d; d is the result) */
-+	/* (a == d) is ok in current mbedtls implementation */
-+	if (b == d || c == d) { /*(not ok; store result in intermediate)*/
-+		mbedtls_mpi R;
-+		mbedtls_mpi_init(&R);
-+		int rc = mbedtls_mpi_exp_mod(&R,
-+		                             (const mbedtls_mpi *)a,
-+		                             (const mbedtls_mpi *)b,
-+		                             (const mbedtls_mpi *)c,
-+		                             NULL)
-+		      || mbedtls_mpi_copy((mbedtls_mpi *)d, &R) ? -1 : 0;
-+		mbedtls_mpi_free(&R);
-+		return rc;
-+	}
-+	else {
-+		return mbedtls_mpi_exp_mod((mbedtls_mpi *)d,
-+		                           (const mbedtls_mpi *)a,
-+		                           (const mbedtls_mpi *)b,
-+		                           (const mbedtls_mpi *)c,
-+		                           NULL) ? -1 : 0;
-+	}
-+}
-+
-+int crypto_bignum_inverse(const struct crypto_bignum *a,
-+			  const struct crypto_bignum *b,
-+			  struct crypto_bignum *c)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	return mbedtls_mpi_inv_mod((mbedtls_mpi *)c,
-+				   (const mbedtls_mpi *)a,
-+				   (const mbedtls_mpi *)b) ? -1 : 0;
-+}
-+
-+int crypto_bignum_sub(const struct crypto_bignum *a,
-+		      const struct crypto_bignum *b,
-+		      struct crypto_bignum *c)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	return mbedtls_mpi_sub_mpi((mbedtls_mpi *)c,
-+				   (const mbedtls_mpi *)a,
-+				   (const mbedtls_mpi *)b) ? -1 : 0;
-+}
-+
-+int crypto_bignum_div(const struct crypto_bignum *a,
-+		      const struct crypto_bignum *b,
-+		      struct crypto_bignum *c)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	/*(most current use of this crypto.h interface has a == c (result),
-+	 * so store result in an intermediate to avoid overwritten input)*/
-+	mbedtls_mpi R;
-+	mbedtls_mpi_init(&R);
-+	int rc = mbedtls_mpi_div_mpi(&R, NULL,
-+				     (const mbedtls_mpi *)a,
-+				     (const mbedtls_mpi *)b)
-+	      || mbedtls_mpi_copy((mbedtls_mpi *)c, &R) ? -1 : 0;
-+	mbedtls_mpi_free(&R);
-+	return rc;
-+}
-+
-+int crypto_bignum_addmod(const struct crypto_bignum *a,
-+			 const struct crypto_bignum *b,
-+			 const struct crypto_bignum *c,
-+			 struct crypto_bignum *d)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	return mbedtls_mpi_add_mpi((mbedtls_mpi *)d,
-+				   (const mbedtls_mpi *)a,
-+				   (const mbedtls_mpi *)b)
-+	    || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d,
-+				   (mbedtls_mpi *)d,
-+				   (const mbedtls_mpi *)c) ? -1 : 0;
-+}
-+
-+int crypto_bignum_mulmod(const struct crypto_bignum *a,
-+			 const struct crypto_bignum *b,
-+			 const struct crypto_bignum *c,
-+			 struct crypto_bignum *d)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	return mbedtls_mpi_mul_mpi((mbedtls_mpi *)d,
-+				   (const mbedtls_mpi *)a,
-+				   (const mbedtls_mpi *)b)
-+	    || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d,
-+				   (mbedtls_mpi *)d,
-+				   (const mbedtls_mpi *)c) ? -1 : 0;
-+}
-+
-+int crypto_bignum_sqrmod(const struct crypto_bignum *a,
-+			 const struct crypto_bignum *b,
-+			 struct crypto_bignum *c)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+  #if 1
-+	return crypto_bignum_mulmod(a, a, b, c);
-+  #else
-+	mbedtls_mpi bn;
-+	mbedtls_mpi_init(&bn);
-+	if (mbedtls_mpi_lset(&bn, 2)) /* alt?: mbedtls_mpi_set_bit(&bn, 1) */
-+		return -1;
-+	int ret = mbedtls_mpi_exp_mod((mbedtls_mpi *)c,
-+				      (const mbedtls_mpi *)a, &bn,
-+				      (const mbedtls_mpi *)b, NULL) ? -1 : 0;
-+	mbedtls_mpi_free(&bn);
-+	return ret;
-+  #endif
-+}
-+
-+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
-+			 struct crypto_bignum *r)
-+{
-+	return mbedtls_mpi_copy((mbedtls_mpi *)r, (const mbedtls_mpi *)a)
-+	    || mbedtls_mpi_shift_r((mbedtls_mpi *)r, n) ? -1 : 0;
-+}
-+
-+int crypto_bignum_cmp(const struct crypto_bignum *a,
-+		      const struct crypto_bignum *b)
-+{
-+	return mbedtls_mpi_cmp_mpi((const mbedtls_mpi *)a, (const mbedtls_mpi *)b);
-+}
-+
-+int crypto_bignum_is_zero(const struct crypto_bignum *a)
-+{
-+	/* XXX: src/common/sae.c:sswu() contains comment:
-+	 * "TODO: Make sure crypto_bignum_is_zero() is constant time"
-+	 * Note: mbedtls_mpi_cmp_int() *is not* constant time */
-+	return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 0) == 0);
-+}
-+
-+int crypto_bignum_is_one(const struct crypto_bignum *a)
-+{
-+	return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 1) == 0);
-+}
-+
-+int crypto_bignum_is_odd(const struct crypto_bignum *a)
-+{
-+	return mbedtls_mpi_get_bit((const mbedtls_mpi *)a, 0);
-+}
-+
-+#include "utils/const_time.h"
-+int crypto_bignum_legendre(const struct crypto_bignum *a,
-+			   const struct crypto_bignum *p)
-+{
-+	if (TEST_FAIL())
-+		return -2;
-+
-+	/* Security Note:
-+	 * mbedtls_mpi_exp_mod() is not documented to run in constant time,
-+	 * though mbedtls/library/bignum.c uses constant_time_internal.h funcs.
-+	 * Compare to crypto_openssl.c:crypto_bignum_legendre()
-+	 * which uses openssl BN_mod_exp_mont_consttime()
-+	 * mbedtls/library/ecp.c has further countermeasures to timing attacks,
-+	 * (but ecp.c funcs are not used here) */
-+
-+	mbedtls_mpi exp, tmp;
-+	mbedtls_mpi_init(&exp);
-+	mbedtls_mpi_init(&tmp);
-+
-+	/* exp = (p-1) / 2 */
-+	int res;
-+	if (mbedtls_mpi_sub_int(&exp, (const mbedtls_mpi *)p, 1) == 0
-+	    && mbedtls_mpi_shift_r(&exp, 1) == 0
-+	    && mbedtls_mpi_exp_mod(&tmp, (const mbedtls_mpi *)a, &exp,
-+	                           (const mbedtls_mpi *)p, NULL) == 0) {
-+		/*(modified from crypto_openssl.c:crypto_bignum_legendre())*/
-+		/* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need
-+		 * to use constant time selection to avoid branches here. */
-+		unsigned int mask;
-+		res = -1;
-+		mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 1) == 0), 1);
-+		res = const_time_select_int(mask, 1, res);
-+		mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 0) == 0), 1);
-+		res = const_time_select_int(mask, 0, res);
-+	} else {
-+		res = -2;
-+	}
-+
-+	mbedtls_mpi_free(&tmp);
-+	mbedtls_mpi_free(&exp);
-+	return res;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_BIGNUM */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_DH
-+
-+/* crypto_internal-modexp.c */
-+
-+#include <mbedtls/bignum.h>
-+#include <mbedtls/dhm.h>
-+
-+#if 0 /* crypto_dh_init() and crypto_dh_derive_secret() prefer to use mbedtls */
-+int crypto_mod_exp(const u8 *base, size_t base_len,
-+		   const u8 *power, size_t power_len,
-+		   const u8 *modulus, size_t modulus_len,
-+		   u8 *result, size_t *result_len)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	mbedtls_mpi bn_base, bn_exp, bn_modulus, bn_result;
-+	mbedtls_mpi_init(&bn_base);
-+	mbedtls_mpi_init(&bn_exp);
-+	mbedtls_mpi_init(&bn_modulus);
-+	mbedtls_mpi_init(&bn_result);
-+
-+	size_t len;
-+	int ret =  mbedtls_mpi_read_binary(&bn_base, base, base_len)
-+	        || mbedtls_mpi_read_binary(&bn_exp, power, power_len)
-+	        || mbedtls_mpi_read_binary(&bn_modulus, modulus, modulus_len)
-+	        || mbedtls_mpi_exp_mod(&bn_result,&bn_base,&bn_exp,&bn_modulus,NULL)
-+	        || (len = mbedtls_mpi_size(&bn_result)) > *result_len
-+	        || mbedtls_mpi_write_binary(&bn_result, result, (*result_len = len))
-+	  ? -1
-+	  : 0;
-+
-+	mbedtls_mpi_free(&bn_base);
-+	mbedtls_mpi_free(&bn_exp);
-+	mbedtls_mpi_free(&bn_modulus);
-+	mbedtls_mpi_free(&bn_result);
-+	return ret;
-+}
-+#endif
-+
-+static int crypto_mbedtls_dh_set_bin_pg(mbedtls_dhm_context *ctx, u8 generator,
-+                                        const u8 *prime, size_t prime_len)
-+{
-+	/*(could set these directly in MBEDTLS_PRIVATE members)*/
-+	mbedtls_mpi P, G;
-+	mbedtls_mpi_init(&P);
-+	mbedtls_mpi_init(&G);
-+	int ret = mbedtls_mpi_lset(&G, generator)
-+	       || mbedtls_mpi_read_binary(&P, prime, prime_len)
-+	       || mbedtls_dhm_set_group(ctx, &P, &G);
-+	mbedtls_mpi_free(&P);
-+	mbedtls_mpi_free(&G);
-+	return ret;
-+}
-+
-+__attribute_noinline__
-+static int crypto_mbedtls_dh_init_public(mbedtls_dhm_context *ctx, u8 generator,
-+                                         const u8 *prime, size_t prime_len,
-+                                         u8 *privkey, u8 *pubkey)
-+{
-+	if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len)
-+	    || mbedtls_dhm_make_public(ctx, (int)prime_len, pubkey, prime_len,
-+	                               mbedtls_ctr_drbg_random,
-+	                               crypto_mbedtls_ctr_drbg()))
-+		return -1;
-+
-+  /*(enable later when upstream mbedtls interface changes require)*/
-+  #if 0 && MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+	mbedtls_mpi X;
-+	mbedtls_mpi_init(&X);
-+	int ret = mbedtls_dhm_get_value(ctx, MBEDTLS_DHM_PARAM_X, &X)
-+	       || mbedtls_mpi_write_binary(&X, privkey, prime_len) ? -1 : 0;
-+	mbedtls_mpi_free(&X);
-+	return ret;
-+  #else
-+	return mbedtls_mpi_write_binary(&ctx->MBEDTLS_PRIVATE(X),
-+	                                privkey, prime_len) ? -1 : 0;
-+  #endif
-+}
-+
-+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
-+		   u8 *pubkey)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+  #if 0 /*(crypto_dh_init() duplicated (and identical) in crypto_*.c modules)*/
-+	size_t pubkey_len, pad;
-+
-+	if (os_get_random(privkey, prime_len) < 0)
-+		return -1;
-+	if (os_memcmp(privkey, prime, prime_len) > 0) {
-+		/* Make sure private value is smaller than prime */
-+		privkey[0] = 0;
-+	}
-+
-+	pubkey_len = prime_len;
-+	if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
-+			   pubkey, &pubkey_len) < 0)
-+		return -1;
-+	if (pubkey_len < prime_len) {
-+		pad = prime_len - pubkey_len;
-+		os_memmove(pubkey + pad, pubkey, pubkey_len);
-+		os_memset(pubkey, 0, pad);
-+	}
-+
-+	return 0;
-+  #else
-+	/* Prefer to use mbedtls to derive our public/private key, as doing so
-+	 * leverages mbedtls to properly format output and to perform blinding*/
-+	mbedtls_dhm_context ctx;
-+	mbedtls_dhm_init(&ctx);
-+	int ret = crypto_mbedtls_dh_init_public(&ctx, generator, prime,
-+	                                        prime_len, privkey, pubkey);
-+	mbedtls_dhm_free(&ctx);
-+	return ret;
-+  #endif
-+}
-+
-+/*(crypto_dh_derive_secret() could be implemented using crypto.h APIs
-+ * instead of being reimplemented in each crypto_*.c)*/
-+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
-+			    const u8 *order, size_t order_len,
-+			    const u8 *privkey, size_t privkey_len,
-+			    const u8 *pubkey, size_t pubkey_len,
-+			    u8 *secret, size_t *len)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+  #if 0
-+	if (pubkey_len > prime_len ||
-+	    (pubkey_len == prime_len &&
-+	     os_memcmp(pubkey, prime, prime_len) >= 0))
-+		return -1;
-+
-+	int res = 0;
-+	mbedtls_mpi pub;
-+	mbedtls_mpi_init(&pub);
-+	if (mbedtls_mpi_read_binary(&pub, pubkey, pubkey_len)
-+	    || mbedtls_mpi_cmp_int(&pub, 1) <= 0) {
-+		res = -1;
-+	} else if (order) {
-+		mbedtls_mpi p, q, tmp;
-+		mbedtls_mpi_init(&p);
-+		mbedtls_mpi_init(&q);
-+		mbedtls_mpi_init(&tmp);
-+
-+		/* verify: pubkey^q == 1 mod p */
-+		res = (mbedtls_mpi_read_binary(&p, prime, prime_len)
-+		    || mbedtls_mpi_read_binary(&q, order, order_len)
-+		    || mbedtls_mpi_exp_mod(&tmp, &pub, &q, &p, NULL)
-+		    || mbedtls_mpi_cmp_int(&tmp, 1) != 0);
-+
-+		mbedtls_mpi_free(&p);
-+		mbedtls_mpi_free(&q);
-+		mbedtls_mpi_free(&tmp);
-+	}
-+	mbedtls_mpi_free(&pub);
-+
-+	return (res == 0)
-+	  ? crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
-+			   prime, prime_len, secret, len)
-+	  : -1;
-+  #else
-+	/* Prefer to use mbedtls to derive DH shared secret, as doing so
-+	 * leverages mbedtls to validate params and to perform blinding.
-+	 *
-+	 * Attempt to reconstitute DH context to derive shared secret
-+	 * (due to limitations of the interface, which ought to pass context).
-+	 * Force provided G (our private key) into context without validation.
-+	 * Regenerating GX (our public key) not needed to derive shared secret.
-+	 */
-+	/*(older compilers might not support VLAs)*/
-+	/*unsigned char buf[2+prime_len+2+1+2+pubkey_len];*/
-+	unsigned char buf[2+MBEDTLS_MPI_MAX_SIZE+2+1+2+MBEDTLS_MPI_MAX_SIZE];
-+	unsigned char *p = buf + 2 + prime_len;
-+	if (2+prime_len+2+1+2+pubkey_len > sizeof(buf))
-+		return -1;
-+	WPA_PUT_BE16(buf, prime_len);  /*(2-byte big-endian size of prime)*/
-+	p[0] = 0;                      /*(2-byte big-endian size of generator)*/
-+	p[1] = 1;
-+	p[2] = generator;
-+	WPA_PUT_BE16(p+3, pubkey_len); /*(2-byte big-endian size of pubkey)*/
-+	os_memcpy(p+5, pubkey, pubkey_len);
-+	os_memcpy(buf+2, prime, prime_len);
-+
-+	mbedtls_dhm_context ctx;
-+	mbedtls_dhm_init(&ctx);
-+	p = buf;
-+	int ret = mbedtls_dhm_read_params(&ctx, &p, p+2+prime_len+5+pubkey_len)
-+	       || mbedtls_mpi_read_binary(&ctx.MBEDTLS_PRIVATE(X),
-+	                                  privkey, privkey_len)
-+	       || mbedtls_dhm_calc_secret(&ctx, secret, *len, len,
-+	                                  mbedtls_ctr_drbg_random,
-+	                                  crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+	mbedtls_dhm_free(&ctx);
-+	return ret;
-+  #endif
-+}
-+
-+/* dh_group5.c */
-+
-+#include "dh_group5.h"
-+
-+/* RFC3526_PRIME_1536[] and RFC3526_GENERATOR_1536[] from crypto_wolfssl.c */
-+
-+static const unsigned char RFC3526_PRIME_1536[] = {
-+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
-+	0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
-+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
-+	0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
-+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
-+	0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
-+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
-+	0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
-+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
-+	0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
-+	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
-+	0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
-+	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
-+	0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
-+	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
-+	0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
-+};
-+
-+static const unsigned char RFC3526_GENERATOR_1536[] = {
-+	0x02
-+};
-+
-+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
-+{
-+	const unsigned char * const prime = RFC3526_PRIME_1536;
-+	const size_t prime_len = sizeof(RFC3526_PRIME_1536);
-+	const u8 generator = *RFC3526_GENERATOR_1536;
-+	struct wpabuf *wpubl = NULL, *wpriv = NULL;
-+
-+	mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx));
-+	if (ctx == NULL)
-+		return NULL;
-+	mbedtls_dhm_init(ctx);
-+
-+	if (   (wpubl = wpabuf_alloc(prime_len))
-+	    && (wpriv = wpabuf_alloc(prime_len))
-+	    && crypto_mbedtls_dh_init_public(ctx, generator, prime, prime_len,
-+	                                     wpabuf_put(wpriv, prime_len),
-+	                                     wpabuf_put(wpubl, prime_len))==0) {
-+		wpabuf_free(*publ);
-+		wpabuf_clear_free(*priv);
-+		*publ = wpubl;
-+		*priv = wpriv;
-+		return ctx;
-+	}
-+
-+	wpabuf_clear_free(wpriv);
-+	wpabuf_free(wpubl);
-+	mbedtls_dhm_free(ctx);
-+	os_free(ctx);
-+	return NULL;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_DH5_INIT_FIXED
-+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
-+{
-+	const unsigned char * const prime = RFC3526_PRIME_1536;
-+	const size_t prime_len = sizeof(RFC3526_PRIME_1536);
-+	const u8 generator = *RFC3526_GENERATOR_1536;
-+
-+	mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx));
-+	if (ctx == NULL)
-+		return NULL;
-+	mbedtls_dhm_init(ctx);
-+
-+	if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len)==0
-+	   #if 0 /*(ignore; not required to derive shared secret)*/
-+	    && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(GX),
-+				       wpabuf_head(publ),wpabuf_len(publ))==0
-+	   #endif
-+	    && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(X),
-+				       wpabuf_head(priv),wpabuf_len(priv))==0) {
-+		return ctx;
-+	}
-+
-+	mbedtls_dhm_free(ctx);
-+	os_free(ctx);
-+	return NULL;
-+}
-+#endif
-+
-+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
-+				  const struct wpabuf *own_private)
-+{
-+	/*((mbedtls_dhm_context *)ctx must already contain own_private)*/
-+	/* mbedtls 2.x: prime_len = ctx->len; */
-+	/* mbedtls 3.x: prime_len = mbedtls_dhm_get_len(ctx); */
-+	size_t olen = sizeof(RFC3526_PRIME_1536); /*(sizeof(); prime known)*/
-+	struct wpabuf *buf = wpabuf_alloc(olen);
-+	if (buf == NULL)
-+		return NULL;
-+	if (mbedtls_dhm_read_public((mbedtls_dhm_context *)ctx,
-+	                            wpabuf_head(peer_public),
-+	                            wpabuf_len(peer_public)) == 0
-+	    && mbedtls_dhm_calc_secret(ctx, wpabuf_mhead(buf), olen, &olen,
-+	                               mbedtls_ctr_drbg_random,
-+	                               crypto_mbedtls_ctr_drbg()) == 0) {
-+		wpabuf_put(buf, olen);
-+		return buf;
-+	}
-+
-+	wpabuf_free(buf);
-+	return NULL;
-+}
-+
-+void dh5_free(void *ctx)
-+{
-+	mbedtls_dhm_free(ctx);
-+	os_free(ctx);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_DH */
-+
-+
-+#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC)
-+
-+#include <mbedtls/ecp.h>
-+
-+#define CRYPTO_EC_pbits(e) (((mbedtls_ecp_group *)(e))->pbits)
-+#define CRYPTO_EC_plen(e) ((((mbedtls_ecp_group *)(e))->pbits+7)>>3)
-+#define CRYPTO_EC_P(e)    (&((mbedtls_ecp_group *)(e))->P)
-+#define CRYPTO_EC_N(e)    (&((mbedtls_ecp_group *)(e))->N)
-+#define CRYPTO_EC_A(e)    (&((mbedtls_ecp_group *)(e))->A)
-+#define CRYPTO_EC_B(e)    (&((mbedtls_ecp_group *)(e))->B)
-+#define CRYPTO_EC_G(e)    (&((mbedtls_ecp_group *)(e))->G)
-+
-+static mbedtls_ecp_group_id crypto_mbedtls_ecp_group_id_from_ike_id(int group)
-+{
-+	/* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */
-+	switch (group) {
-+  #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
-+	case 19: return MBEDTLS_ECP_DP_SECP256R1;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
-+	case 20: return MBEDTLS_ECP_DP_SECP384R1;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
-+	case 21: return MBEDTLS_ECP_DP_SECP521R1;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
-+	case 25: return MBEDTLS_ECP_DP_SECP192R1;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
-+	case 26: return MBEDTLS_ECP_DP_SECP224R1;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
-+	case 28: return MBEDTLS_ECP_DP_BP256R1;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
-+	case 29: return MBEDTLS_ECP_DP_BP384R1;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
-+	case 30: return MBEDTLS_ECP_DP_BP512R1;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
-+	case 31: return MBEDTLS_ECP_DP_CURVE25519;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
-+	case 32: return MBEDTLS_ECP_DP_CURVE448;
-+  #endif
-+	default: return MBEDTLS_ECP_DP_NONE;
-+	}
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC
-+static int crypto_mbedtls_ike_id_from_ecp_group_id(mbedtls_ecp_group_id grp_id)
-+{
-+	/* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */
-+	/*(for crypto_ec_key_group())*/
-+	switch (grp_id) {
-+  #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP256R1:  return 19;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP384R1:  return 20;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP521R1:  return 21;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP192R1:  return 25;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP224R1:  return 26;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
-+	case MBEDTLS_ECP_DP_BP256R1:    return 28;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
-+	case MBEDTLS_ECP_DP_BP384R1:    return 29;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
-+	case MBEDTLS_ECP_DP_BP512R1:    return 30;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
-+	case MBEDTLS_ECP_DP_CURVE25519: return 31;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
-+	case MBEDTLS_ECP_DP_CURVE448:   return 32;
-+  #endif
-+	default: return -1;
-+	}
-+}
-+#endif
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH || CRYPTO_MBEDTLS_CRYPTO_EC */
-+
-+
-+#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC_DPP)
-+
-+#include <mbedtls/ecp.h>
-+#include <mbedtls/pk.h>
-+
-+static int crypto_mbedtls_keypair_gen(int group, mbedtls_pk_context *pk)
-+{
-+	mbedtls_ecp_group_id grp_id =
-+	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+	if (grp_id == MBEDTLS_ECP_DP_NONE)
-+		return -1;
-+	const mbedtls_pk_info_t *pk_info =
-+	  mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
-+	if (pk_info == NULL)
-+		return -1;
-+	return mbedtls_pk_setup(pk, pk_info)
-+	    || mbedtls_ecp_gen_key(grp_id, mbedtls_pk_ec(*pk),
-+	                           mbedtls_ctr_drbg_random,
-+	                           crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+}
-+
-+#endif
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_ECDH
-+
-+#include <mbedtls/ecdh.h>
-+#include <mbedtls/ecdsa.h>
-+#include <mbedtls/ecp.h>
-+#include <mbedtls/pk.h>
-+
-+/* wrap mbedtls_ecdh_context for more future-proof direct access to components
-+ * (mbedtls_ecdh_context internal implementation may change between releases)
-+ *
-+ * If mbedtls_pk_context -- specifically underlying mbedtls_ecp_keypair --
-+ * lifetime were guaranteed to be longer than that of mbedtls_ecdh_context,
-+ * then mbedtls_pk_context or mbedtls_ecp_keypair could be stored in crypto_ecdh
-+ * (or crypto_ec_key could be stored in crypto_ecdh, and crypto_ec_key could
-+ *  wrap mbedtls_ecp_keypair and components, to avoid MBEDTLS_PRIVATE access) */
-+struct crypto_ecdh {
-+	mbedtls_ecdh_context ctx;
-+	mbedtls_ecp_group grp;
-+	mbedtls_ecp_point Q;
-+};
-+
-+struct crypto_ecdh * crypto_ecdh_init(int group)
-+{
-+	mbedtls_pk_context pk;
-+	mbedtls_pk_init(&pk);
-+	struct crypto_ecdh *ecdh = crypto_mbedtls_keypair_gen(group, &pk) == 0
-+	  ? crypto_ecdh_init2(group, (struct crypto_ec_key *)&pk)
-+	  : NULL;
-+	mbedtls_pk_free(&pk);
-+	return ecdh;
-+}
-+
-+struct crypto_ecdh * crypto_ecdh_init2(int group,
-+				       struct crypto_ec_key *own_key)
-+{
-+	mbedtls_ecp_group_id grp_id =
-+	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+	if (grp_id == MBEDTLS_ECP_DP_NONE)
-+		return NULL;
-+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)own_key);
-+	struct crypto_ecdh *ecdh = os_malloc(sizeof(*ecdh));
-+	if (ecdh == NULL)
-+		return NULL;
-+	mbedtls_ecdh_init(&ecdh->ctx);
-+	mbedtls_ecp_group_init(&ecdh->grp);
-+	mbedtls_ecp_point_init(&ecdh->Q);
-+	if (mbedtls_ecdh_setup(&ecdh->ctx, grp_id) == 0
-+	    && mbedtls_ecdh_get_params(&ecdh->ctx,ecp_kp,MBEDTLS_ECDH_OURS) == 0) {
-+		/* copy grp and Q for later use
-+		 * (retrieving this info later is more convoluted
-+		 *  even if mbedtls_ecdh_make_public() is considered)*/
-+	  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
-+		mbedtls_mpi d;
-+		mbedtls_mpi_init(&d);
-+		if (mbedtls_ecp_export(ecp_kp, &ecdh->grp, &d, &ecdh->Q) == 0) {
-+			mbedtls_mpi_free(&d);
-+			return ecdh;
-+		}
-+		mbedtls_mpi_free(&d);
-+	  #else
-+		if (mbedtls_ecp_group_load(&ecdh->grp, grp_id) == 0
-+		    && mbedtls_ecp_copy(&ecdh->Q, &ecp_kp->MBEDTLS_PRIVATE(Q)) == 0)
-+			return ecdh;
-+	  #endif
-+	}
-+
-+	mbedtls_ecp_point_free(&ecdh->Q);
-+	mbedtls_ecp_group_free(&ecdh->grp);
-+	mbedtls_ecdh_free(&ecdh->ctx);
-+	os_free(ecdh);
-+	return NULL;
-+}
-+
-+struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
-+{
-+	mbedtls_ecp_group *grp = &ecdh->grp;
-+	size_t prime_len = CRYPTO_EC_plen(grp);
-+	size_t output_len = prime_len;
-+	u8 output_offset = 0;
-+	u8 buf[256];
-+
-+  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+	/* len */
-+  #endif
-+  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
-+		output_len = inc_y ? prime_len * 2 + 1 : prime_len + 1;
-+		output_offset = 1;
-+	}
-+  #endif
-+
-+	if (output_len > sizeof(buf))
-+		return NULL;
-+
-+	inc_y = inc_y ? MBEDTLS_ECP_PF_UNCOMPRESSED : MBEDTLS_ECP_PF_COMPRESSED;
-+	if (mbedtls_ecp_point_write_binary(grp, &ecdh->Q, inc_y, &output_len,
-+	                                   buf, output_len) == 0) {
-+		return wpabuf_alloc_copy(buf + output_offset, output_len - output_offset);
-+	}
-+
-+	return NULL;
-+}
-+
-+#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
-+static int crypto_mbedtls_short_weierstrass_derive_y(mbedtls_ecp_group *grp,
-+                                                     mbedtls_mpi *bn,
-+                                                     int parity_bit)
-+{
-+	/* y^2 = x^3 + ax + b
-+	 * sqrt(w) = w^((p+1)/4) mod p   (for prime p where p = 3 mod 4) */
-+	mbedtls_mpi *cy2 = (mbedtls_mpi *)
-+	  crypto_ec_point_compute_y_sqr((struct crypto_ec *)grp,
-+	                                (const struct crypto_bignum *)bn); /*x*/
-+	if (cy2 == NULL)
-+		return -1;
-+
-+	/*mbedtls_mpi_free(bn);*/
-+	/*(reuse bn to store result (y))*/
-+
-+	mbedtls_mpi exp;
-+	mbedtls_mpi_init(&exp);
-+	int ret = mbedtls_mpi_get_bit(&grp->P, 0) != 1 /*(p = 3 mod 4)*/
-+	       || mbedtls_mpi_get_bit(&grp->P, 1) != 1 /*(p = 3 mod 4)*/
-+	       || mbedtls_mpi_add_int(&exp, &grp->P, 1)
-+	       || mbedtls_mpi_shift_r(&exp, 2)
-+	       || mbedtls_mpi_exp_mod(bn, cy2, &exp, &grp->P, NULL)
-+	       || (mbedtls_mpi_get_bit(bn, 0) != parity_bit
-+	           && mbedtls_mpi_sub_mpi(bn, &grp->P, bn));
-+	mbedtls_mpi_free(&exp);
-+	mbedtls_mpi_free(cy2);
-+	os_free(cy2);
-+	return ret;
-+}
-+#endif
-+
-+struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
-+					const u8 *key, size_t len)
-+{
-+	if (len == 0) /*(invalid peer key)*/
-+		return NULL;
-+
-+	mbedtls_ecp_group *grp = &ecdh->grp;
-+
-+  #if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
-+	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
-+		/* add header for mbedtls_ecdh_read_public() */
-+		u8 buf[256];
-+		if (sizeof(buf)-1 < len)
-+			return NULL;
-+		buf[0] = (u8)(len);
-+		os_memcpy(buf+1, key, len);
-+
-+		if (inc_y) {
-+			if (!(len & 1)) { /*(dpp code/tests does not include tag?!?)*/
-+				if (sizeof(buf)-2 < len)
-+					return NULL;
-+				buf[0] = (u8)(1+len);
-+				buf[1] = 0x04;
-+				os_memcpy(buf+2, key, len);
-+			}
-+			len >>= 1; /*(repurpose len to prime_len)*/
-+		} else { /* (inc_y == 0) */
-+			/* mbedtls_ecp_point_read_binary() does not currently support
-+			 * MBEDTLS_ECP_PF_COMPRESSED format (buf[1] = 0x02 or 0x03)
-+			 * (returns MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) */
-+
-+			/* derive y, amend buf[] with y for UNCOMPRESSED format */
-+			if (sizeof(buf)-2 < len*2 || len == 0)
-+				return NULL;
-+
-+			buf[0] = (u8)(1+len*2);
-+			buf[1] = 0x04;
-+			os_memcpy(buf+2, key, len);
-+
-+			mbedtls_mpi bn;
-+			mbedtls_mpi_init(&bn);
-+			int ret = mbedtls_mpi_read_binary(&bn, key, len)
-+			       || crypto_mbedtls_short_weierstrass_derive_y(grp, &bn, 0)
-+			       || mbedtls_mpi_write_binary(&bn, buf+2+len, len);
-+			mbedtls_mpi_free(&bn);
-+			if (ret != 0)
-+				return NULL;
-+		}
-+
-+		if (mbedtls_ecdh_read_public(&ecdh->ctx, buf, buf[0]+1))
-+			return NULL;
-+	}
-+  #endif
-+  #if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED)
-+	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) {
-+		if (mbedtls_ecdh_read_public(&ecdh->ctx, key, len))
-+			return NULL;
-+	}
-+  #endif
-+
-+	struct wpabuf *buf = wpabuf_alloc(len);
-+	if (buf == NULL)
-+		return NULL;
-+
-+	if (mbedtls_ecdh_calc_secret(&ecdh->ctx, &len,
-+	                             wpabuf_mhead(buf), len,
-+	                             mbedtls_ctr_drbg_random,
-+	                             crypto_mbedtls_ctr_drbg()) == 0) {
-+		wpabuf_put(buf, len);
-+		return buf;
-+	}
-+
-+	wpabuf_clear_free(buf);
-+	return NULL;
-+}
-+
-+void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
-+{
-+	if (ecdh == NULL)
-+		return;
-+	mbedtls_ecp_point_free(&ecdh->Q);
-+	mbedtls_ecp_group_free(&ecdh->grp);
-+	mbedtls_ecdh_free(&ecdh->ctx);
-+	os_free(ecdh);
-+}
-+
-+size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh)
-+{
-+	return CRYPTO_EC_plen(&ecdh->grp);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC
-+
-+#include <mbedtls/ecp.h>
-+
-+struct crypto_ec *crypto_ec_init(int group)
-+{
-+	mbedtls_ecp_group_id grp_id =
-+	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+	if (grp_id == MBEDTLS_ECP_DP_NONE)
-+		return NULL;
-+	mbedtls_ecp_group *e = os_malloc(sizeof(*e));
-+	if (e == NULL)
-+		return NULL;
-+	mbedtls_ecp_group_init(e);
-+	if (mbedtls_ecp_group_load(e, grp_id) == 0)
-+		return (struct crypto_ec *)e;
-+
-+	mbedtls_ecp_group_free(e);
-+	os_free(e);
-+	return NULL;
-+}
-+
-+void crypto_ec_deinit(struct crypto_ec *e)
-+{
-+	mbedtls_ecp_group_free((mbedtls_ecp_group *)e);
-+	os_free(e);
-+}
-+
-+size_t crypto_ec_prime_len(struct crypto_ec *e)
-+{
-+	return CRYPTO_EC_plen(e);
-+}
-+
-+size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
-+{
-+	return CRYPTO_EC_pbits(e);
-+}
-+
-+size_t crypto_ec_order_len(struct crypto_ec *e)
-+{
-+	return (mbedtls_mpi_bitlen(CRYPTO_EC_N(e)) + 7) / 8;
-+}
-+
-+const struct crypto_bignum *crypto_ec_get_prime(struct crypto_ec *e)
-+{
-+	return (const struct crypto_bignum *)CRYPTO_EC_P(e);
-+}
-+
-+const struct crypto_bignum *crypto_ec_get_order(struct crypto_ec *e)
-+{
-+	return (const struct crypto_bignum *)CRYPTO_EC_N(e);
-+}
-+
-+const struct crypto_bignum *crypto_ec_get_a(struct crypto_ec *e)
-+{
-+	static const uint8_t secp256r1_a[] =
-+	  {0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x01,
-+	   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+	   0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc};
-+	static const uint8_t secp384r1_a[] =
-+	  {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
-+	   0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
-+	   0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xfc};
-+	static const uint8_t secp521r1_a[] =
-+	  {0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xfc};
-+	static const uint8_t secp192r1_a[] =
-+	  {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc};
-+	static const uint8_t secp224r1_a[] =
-+	  {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xfe};
-+
-+	const uint8_t *bin = NULL;
-+	size_t len = 0;
-+
-+	/* (mbedtls groups matching supported sswu_curve_param() IKE groups) */
-+	switch (((mbedtls_ecp_group *)e)->id) {
-+  #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP256R1:
-+		bin = secp256r1_a;
-+		len = sizeof(secp256r1_a);
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP384R1:
-+		bin = secp384r1_a;
-+		len = sizeof(secp384r1_a);
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP521R1:
-+		bin = secp521r1_a;
-+		len = sizeof(secp521r1_a);
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP192R1:
-+		bin = secp192r1_a;
-+		len = sizeof(secp192r1_a);
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP224R1:
-+		bin = secp224r1_a;
-+		len = sizeof(secp224r1_a);
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
-+	case MBEDTLS_ECP_DP_BP256R1:
-+		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
-+	case MBEDTLS_ECP_DP_BP384R1:
-+		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
-+	case MBEDTLS_ECP_DP_BP512R1:
-+		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
-+	case MBEDTLS_ECP_DP_CURVE25519:
-+		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
-+	case MBEDTLS_ECP_DP_CURVE448:
-+		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+  #endif
-+	default:
-+		return NULL;
-+	}
-+
-+	/*(note: not thread-safe; returns file-scoped static storage)*/
-+	if (mbedtls_mpi_read_binary(&mpi_sw_A, bin, len) == 0)
-+		return (const struct crypto_bignum *)&mpi_sw_A;
-+	return NULL;
-+}
-+
-+const struct crypto_bignum *crypto_ec_get_b(struct crypto_ec *e)
-+{
-+	return (const struct crypto_bignum *)CRYPTO_EC_B(e);
-+}
-+
-+const struct crypto_ec_point * crypto_ec_get_generator(struct crypto_ec *e)
-+{
-+	return (const struct crypto_ec_point *)CRYPTO_EC_G(e);
-+}
-+
-+struct crypto_ec_point *crypto_ec_point_init(struct crypto_ec *e)
-+{
-+	if (TEST_FAIL())
-+		return NULL;
-+
-+	mbedtls_ecp_point *p = os_malloc(sizeof(*p));
-+	if (p != NULL)
-+		mbedtls_ecp_point_init(p);
-+	return (struct crypto_ec_point *)p;
-+}
-+
-+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
-+{
-+	mbedtls_ecp_point_free((mbedtls_ecp_point *)p);
-+	os_free(p);
-+}
-+
-+int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
-+		      struct crypto_bignum *x)
-+{
-+	mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
-+	return mbedtls_mpi_copy((mbedtls_mpi *)x, px)
-+	  ? -1
-+	  : 0;
-+}
-+
-+int crypto_ec_point_to_bin(struct crypto_ec *e,
-+			   const struct crypto_ec_point *point, u8 *x, u8 *y)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	/* crypto.h documents crypto_ec_point_to_bin() output is big-endian */
-+	size_t len = CRYPTO_EC_plen(e);
-+	if (x) {
-+		mbedtls_mpi *px = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(X);
-+		if (mbedtls_mpi_write_binary(px, x, len))
-+			return -1;
-+	}
-+	if (y) {
-+	  #if 0 /*(should not be necessary; py mpi should be in initial state)*/
-+	  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+		if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+		    == MBEDTLS_ECP_TYPE_MONTGOMERY) {
-+			os_memset(y, 0, len);
-+			return 0;
-+		}
-+	  #endif
-+	  #endif
-+		mbedtls_mpi *py = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(Y);
-+		if (mbedtls_mpi_write_binary(py, y, len))
-+			return -1;
-+	}
-+	return 0;
-+}
-+
-+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
-+						  const u8 *val)
-+{
-+	if (TEST_FAIL())
-+		return NULL;
-+
-+	size_t len = CRYPTO_EC_plen(e);
-+	mbedtls_ecp_point *p = os_malloc(sizeof(*p));
-+	u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
-+	if (p == NULL)
-+		return NULL;
-+	mbedtls_ecp_point_init(p);
-+
-+  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+	    == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
-+	  #if 0 /* prefer alternative to MBEDTLS_PRIVATE() access */
-+		mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
-+		mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
-+		mbedtls_mpi *pz = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Z);
-+
-+		if (mbedtls_mpi_read_binary(px, val, len) == 0
-+		    && mbedtls_mpi_read_binary(py, val + len, len) == 0
-+		    && mbedtls_mpi_lset(pz, 1) == 0)
-+			return (struct crypto_ec_point *)p;
-+	  #else
-+		buf[0] = 0x04;
-+		os_memcpy(buf+1, val, len*2);
-+		if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p,
-+		                                  buf, 1+len*2) == 0)
-+			return (struct crypto_ec_point *)p;
-+	  #endif
-+	}
-+  #endif
-+  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+	    == MBEDTLS_ECP_TYPE_MONTGOMERY) {
-+		/* crypto.h interface documents crypto_ec_point_from_bin()
-+		 * val is length: prime_len * 2 and is big-endian
-+		 * (Short Weierstrass is assumed by hostap)
-+		 * Reverse to little-endian format for Montgomery */
-+		for (unsigned int i = 0; i < len; ++i)
-+			buf[i] = val[len-1-i];
-+		if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p,
-+		                                  buf, len) == 0)
-+			return (struct crypto_ec_point *)p;
-+	}
-+  #endif
-+
-+	mbedtls_ecp_point_free(p);
-+	os_free(p);
-+	return NULL;
-+}
-+
-+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
-+			const struct crypto_ec_point *b,
-+			struct crypto_ec_point *c)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	/* mbedtls does not provide an mbedtls_ecp_point add function */
-+	mbedtls_mpi one;
-+	mbedtls_mpi_init(&one);
-+	int ret = mbedtls_mpi_lset(&one, 1)
-+	       || mbedtls_ecp_muladd(
-+			(mbedtls_ecp_group *)e, (mbedtls_ecp_point *)c,
-+			&one, (const mbedtls_ecp_point *)a,
-+			&one, (const mbedtls_ecp_point *)b) ? -1 : 0;
-+	mbedtls_mpi_free(&one);
-+	return ret;
-+}
-+
-+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
-+			const struct crypto_bignum *b,
-+			struct crypto_ec_point *res)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	return mbedtls_ecp_mul(
-+		(mbedtls_ecp_group *)e, (mbedtls_ecp_point *)res,
-+		(const mbedtls_mpi *)b, (const mbedtls_ecp_point *)p,
-+		mbedtls_ctr_drbg_random, crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+}
-+
-+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+	    == MBEDTLS_ECP_TYPE_MONTGOMERY) {
-+		/* e.g. MBEDTLS_ECP_DP_CURVE25519 and MBEDTLS_ECP_DP_CURVE448 */
-+		wpa_printf(MSG_ERROR,
-+		           "%s not implemented for Montgomery curves",__func__);
-+		return -1;
-+	}
-+
-+	/* mbedtls does not provide an mbedtls_ecp_point invert function */
-+	/* below works for Short Weierstrass; incorrect for Montgomery curves */
-+	mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
-+	return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p) /*point at infinity*/
-+	    || mbedtls_mpi_cmp_int(py, 0) == 0      /*point is its own inverse*/
-+	    || mbedtls_mpi_sub_abs(py, CRYPTO_EC_P(e), py) == 0 ? 0 : -1;
-+}
-+
-+#ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+static int
-+crypto_ec_point_y_sqr_weierstrass(mbedtls_ecp_group *e, const mbedtls_mpi *x,
-+                                  mbedtls_mpi *y2)
-+{
-+	/* MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS  y^2 = x^3 + a x + b    */
-+
-+	/* Short Weierstrass elliptic curve group w/o A set treated as A = -3 */
-+	/* Attempt to match mbedtls/library/ecp.c:ecp_check_pubkey_sw() behavior
-+	 * and elsewhere in mbedtls/library/ecp.c where if A is not set, it is
-+	 * treated as if A = -3. */
-+
-+  #if 0
-+	/* y^2 = x^3 + ax + b */
-+	mbedtls_mpi *A = &e->A;
-+	mbedtls_mpi t, A_neg3;
-+	if (&e->A.p == NULL) {
-+		mbedtls_mpi_init(&A_neg3);
-+		if (mbedtls_mpi_lset(&A_neg3, -3) != 0) {
-+			mbedtls_mpi_free(&A_neg3);
-+			return -1;
-+		}
-+		A = &A_neg3;
-+	}
-+	mbedtls_mpi_init(&t);
-+	int ret = /* x^3 */
-+	          mbedtls_mpi_lset(&t, 3)
-+	       || mbedtls_mpi_exp_mod(y2,  x, &t, &e->P, NULL)
-+		  /* ax */
-+	       || mbedtls_mpi_mul_mpi(y2, y2, A)
-+	       || mbedtls_mpi_mod_mpi(&t, &t, &e->P)
-+		  /* ax + b */
-+	       || mbedtls_mpi_add_mpi(&t, &t, &e->B)
-+	       || mbedtls_mpi_mod_mpi(&t, &t, &e->P)
-+		  /* x^3 + ax + b */
-+	       || mbedtls_mpi_add_mpi(&t, &t, y2) /* ax + b + x^3 */
-+	       || mbedtls_mpi_mod_mpi(y2, &t, &e->P);
-+	mbedtls_mpi_free(&t);
-+	if (A == &A_neg3)
-+		mbedtls_mpi_free(&A_neg3);
-+	return ret; /* 0: success, non-zero: failure */
-+  #else
-+	/* y^2 = x^3 + ax + b = (x^2 + a)x + b */
-+	return    /* x^2 */
-+	          mbedtls_mpi_mul_mpi(y2,  x, x)
-+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+		  /* x^2 + a */
-+	       || (e->A.MBEDTLS_PRIVATE(p)
-+	           ? mbedtls_mpi_add_mpi(y2, y2, &e->A)
-+	           : mbedtls_mpi_sub_int(y2, y2, 3))
-+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+		  /* (x^2 + a)x */
-+	       || mbedtls_mpi_mul_mpi(y2, y2, x)
-+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+		  /* (x^2 + a)x + b */
-+	       || mbedtls_mpi_add_mpi(y2, y2, &e->B)
-+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P);
-+  #endif
-+}
-+#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */
-+
-+#if 0 /* not used by hostap */
-+#ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+static int
-+crypto_ec_point_y_sqr_montgomery(mbedtls_ecp_group *e, const mbedtls_mpi *x,
-+                                 mbedtls_mpi *y2)
-+{
-+	/* XXX: !!! must be reviewed and audited for correctness !!! */
-+
-+	/* MBEDTLS_ECP_TYPE_MONTGOMERY         y^2 = x^3 + a x^2 + x  */
-+
-+	/* y^2 = x^3 + a x^2 + x = (x + a)x^2 + x */
-+	mbedtls_mpi x2;
-+	mbedtls_mpi_init(&x2);
-+	int ret = /* x^2 */
-+	          mbedtls_mpi_mul_mpi(&x2, x, x)
-+	       || mbedtls_mpi_mod_mpi(&x2, &x2, &e->P)
-+		  /* x + a */
-+	       || mbedtls_mpi_add_mpi(y2,  x, &e->A)
-+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+		  /* (x + a)x^2 */
-+	       || mbedtls_mpi_mul_mpi(y2, y2, &x2)
-+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+		  /* (x + a)x^2 + x */
-+	       || mbedtls_mpi_add_mpi(y2, y2, x)
-+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P);
-+	mbedtls_mpi_free(&x2);
-+	return ret; /* 0: success, non-zero: failure */
-+}
-+#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */
-+#endif
-+
-+struct crypto_bignum *
-+crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
-+			      const struct crypto_bignum *x)
-+{
-+	if (TEST_FAIL())
-+		return NULL;
-+
-+	mbedtls_mpi *y2 = os_malloc(sizeof(*y2));
-+	if (y2 == NULL)
-+		return NULL;
-+	mbedtls_mpi_init(y2);
-+
-+  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+	      == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS
-+	    && crypto_ec_point_y_sqr_weierstrass((mbedtls_ecp_group *)e,
-+	                                         (const mbedtls_mpi *)x,
-+	                                         y2) == 0)
-+		return (struct crypto_bignum *)y2;
-+  #endif
-+  #if 0 /* not used by hostap */
-+  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+	      == MBEDTLS_ECP_TYPE_MONTGOMERY
-+	    && crypto_ec_point_y_sqr_montgomery((mbedtls_ecp_group *)e,
-+	                                        (const mbedtls_mpi *)x,
-+	                                        y2) == 0)
-+		return (struct crypto_bignum *)y2;
-+  #endif
-+  #endif
-+
-+	mbedtls_mpi_free(y2);
-+	os_free(y2);
-+	return NULL;
-+}
-+
-+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
-+				   const struct crypto_ec_point *p)
-+{
-+	return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p);
-+}
-+
-+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
-+				const struct crypto_ec_point *p)
-+{
-+  #if 1
-+	return mbedtls_ecp_check_pubkey((const mbedtls_ecp_group *)e,
-+	                                (const mbedtls_ecp_point *)p) == 0;
-+  #else
-+	/* compute y^2 mod P and compare to y^2 mod P */
-+	/*(ref: src/eap_common/eap_pwd_common.c:compute_password_element())*/
-+	const mbedtls_mpi *px = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
-+	mbedtls_mpi *cy2 = (mbedtls_mpi *)
-+	  crypto_ec_point_compute_y_sqr(e, (const struct crypto_bignum *)px);
-+	if (cy2 == NULL)
-+		return 0;
-+
-+	mbedtls_mpi y2;
-+	mbedtls_mpi_init(&y2);
-+	const mbedtls_mpi *py = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
-+	int is_on_curve = mbedtls_mpi_mul_mpi(&y2, py, py) /* y^2 mod P */
-+	               || mbedtls_mpi_mod_mpi(&y2, &y2, CRYPTO_EC_P(e))
-+	               || mbedtls_mpi_cmp_mpi(&y2, cy2) != 0 ? 0 : 1;
-+
-+	mbedtls_mpi_free(&y2);
-+	mbedtls_mpi_free(cy2);
-+	os_free(cy2);
-+	return is_on_curve;
-+  #endif
-+}
-+
-+int crypto_ec_point_cmp(const struct crypto_ec *e,
-+			const struct crypto_ec_point *a,
-+			const struct crypto_ec_point *b)
-+{
-+	return mbedtls_ecp_point_cmp((const mbedtls_ecp_point *)a,
-+	                             (const mbedtls_ecp_point *)b);
-+}
-+
-+#if !defined(CONFIG_NO_STDOUT_DEBUG)
-+void crypto_ec_point_debug_print(const struct crypto_ec *e,
-+				 const struct crypto_ec_point *p,
-+				 const char *title)
-+{
-+	u8 x[MBEDTLS_MPI_MAX_SIZE];
-+	u8 y[MBEDTLS_MPI_MAX_SIZE];
-+	size_t len = CRYPTO_EC_plen(e);
-+	/* crypto_ec_point_to_bin ought to take (const struct crypto_ec *e) */
-+	struct crypto_ec *ee;
-+	*(const struct crypto_ec **)&ee = e; /*(cast away const)*/
-+	if (crypto_ec_point_to_bin(ee, p, x, y) == 0) {
-+		if (title)
-+			wpa_printf(MSG_DEBUG, "%s", title);
-+		wpa_hexdump(MSG_DEBUG, "x:", x, len);
-+		wpa_hexdump(MSG_DEBUG, "y:", y, len);
-+	}
-+}
-+#endif
-+
-+
-+struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len)
-+{
-+	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+	if (ctx == NULL)
-+		return NULL;
-+	mbedtls_pk_init(ctx);
-+  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+	if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0) == 0)
-+  #else
-+	if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0,
-+	                         mbedtls_ctr_drbg_random,
-+	                         crypto_mbedtls_ctr_drbg()) == 0)
-+  #endif
-+		return (struct crypto_ec_key *)ctx;
-+
-+	mbedtls_pk_free(ctx);
-+	os_free(ctx);
-+	return NULL;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE
-+#ifdef CONFIG_MODULE_TESTS
-+/*(for crypto_module_tests.c)*/
-+struct crypto_ec_key * crypto_ec_key_set_priv(int group,
-+					      const u8 *raw, size_t raw_len)
-+{
-+	mbedtls_ecp_group_id grp_id =
-+	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+	if (grp_id == MBEDTLS_ECP_DP_NONE)
-+		return NULL;
-+	const mbedtls_pk_info_t *pk_info =
-+	  mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
-+	if (pk_info == NULL)
-+		return NULL;
-+	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+	if (ctx == NULL)
-+		return NULL;
-+	mbedtls_pk_init(ctx);
-+	if (mbedtls_pk_setup(ctx, pk_info) == 0
-+	    && mbedtls_ecp_read_key(grp_id,mbedtls_pk_ec(*ctx),raw,raw_len) == 0) {
-+		return (struct crypto_ec_key *)ctx;
-+	}
-+
-+	mbedtls_pk_free(ctx);
-+	os_free(ctx);
-+	return NULL;
-+}
-+#endif
-+#endif
-+
-+#include <mbedtls/error.h>
-+#include <mbedtls/oid.h>
-+static int crypto_mbedtls_pk_parse_subpubkey_compressed(mbedtls_pk_context *ctx, const u8 *der, size_t der_len)
-+{
-+    /* The following is modified from:
-+     *   mbedtls/library/pkparse.c:mbedtls_pk_parse_subpubkey()
-+     *   mbedtls/library/pkparse.c:pk_get_pk_alg()
-+     *   mbedtls/library/pkparse.c:pk_use_ecparams()
-+     */
-+    mbedtls_pk_type_t pk_alg = MBEDTLS_PK_NONE;
-+    const mbedtls_pk_info_t *pk_info;
-+    int ret;
-+    size_t len;
-+    const unsigned char *end = der+der_len;
-+    unsigned char *p;
-+    *(const unsigned char **)&p = der;
-+
-+    if( ( ret = mbedtls_asn1_get_tag( &p, end, &len,
-+                    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
-+    {
-+        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret ) );
-+    }
-+
-+    end = p + len;
-+
-+    /*
-+    if( ( ret = pk_get_pk_alg( &p, end, &pk_alg, &alg_params ) ) != 0 )
-+        return( ret );
-+    */
-+    mbedtls_asn1_buf alg_oid, params;
-+    memset( &params, 0, sizeof(mbedtls_asn1_buf) );
-+    if( ( ret = mbedtls_asn1_get_alg( &p, end, &alg_oid, &params ) ) != 0 )
-+        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_ALG, ret ) );
-+    if( mbedtls_oid_get_pk_alg( &alg_oid, &pk_alg ) != 0 )
-+        return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
-+
-+    if( ( ret = mbedtls_asn1_get_bitstring_null( &p, end, &len ) ) != 0 )
-+        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY, ret ) );
-+
-+    if( p + len != end )
-+        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY,
-+                MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ) );
-+
-+    if( ( pk_info = mbedtls_pk_info_from_type( pk_alg ) ) == NULL )
-+        return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
-+
-+    if( ( ret = mbedtls_pk_setup( ctx, pk_info ) ) != 0 )
-+        return( ret );
-+
-+    /* assume mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx)
-+     * has already run with ctx initialized up to pk_get_ecpubkey(),
-+     * and pk_get_ecpubkey() has returned MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
-+     *
-+     * mbedtls mbedtls_ecp_point_read_binary()
-+     * does not handle point in COMPRESSED format
-+     *
-+     * (validate assumption that algorithm is EC) */
-+    mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx);
-+    if (ecp_kp == NULL)
-+        return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
-+    mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+    mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+    mbedtls_ecp_group_id grp_id;
-+
-+
-+    /* mbedtls/library/pkparse.c:pk_use_ecparams() */
-+
-+    if( params.tag == MBEDTLS_ASN1_OID )
-+    {
-+        if( mbedtls_oid_get_ec_grp( &params, &grp_id ) != 0 )
-+            return( MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE );
-+    }
-+    else
-+    {
-+#if defined(MBEDTLS_PK_PARSE_EC_EXTENDED)
-+        /*(large code block not copied from mbedtls; unsupported)*/
-+      #if 0
-+        if( ( ret = pk_group_id_from_specified( &params, &grp_id ) ) != 0 )
-+            return( ret );
-+      #endif
-+#endif
-+        return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
-+    }
-+
-+    /*
-+     * grp may already be initialized; if so, make sure IDs match
-+     */
-+    if( ecp_kp_grp->id != MBEDTLS_ECP_DP_NONE && ecp_kp_grp->id != grp_id )
-+        return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
-+
-+    if( ( ret = mbedtls_ecp_group_load( ecp_kp_grp, grp_id ) ) != 0 )
-+        return( ret );
-+
-+
-+    /* (validate assumption that EC point is in COMPRESSED format) */
-+    len = CRYPTO_EC_plen(ecp_kp_grp);
-+    if( mbedtls_ecp_get_type(ecp_kp_grp) != MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS
-+        || (end - p) != 1+len
-+        || (*p != 0x02 && *p != 0x03) )
-+        return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
-+
-+    /* Instead of calling mbedtls/library/pkparse.c:pk_get_ecpubkey() to call
-+     * mbedtls_ecp_point_read_binary(), manually parse point into ecp_kp_Q */
-+    mbedtls_mpi *X = &ecp_kp_Q->MBEDTLS_PRIVATE(X);
-+    mbedtls_mpi *Y = &ecp_kp_Q->MBEDTLS_PRIVATE(Y);
-+    mbedtls_mpi *Z = &ecp_kp_Q->MBEDTLS_PRIVATE(Z);
-+    ret = mbedtls_mpi_lset(Z, 1);
-+    if (ret != 0)
-+        return( ret );
-+    ret = mbedtls_mpi_read_binary(X, p+1, len);
-+    if (ret != 0)
-+        return( ret );
-+    /* derive Y
-+     * (similar derivation of Y in crypto_mbedtls.c:crypto_ecdh_set_peerkey())*/
-+    ret = mbedtls_mpi_copy(Y, X) /*(Y is used as input and output obj below)*/
-+       || crypto_mbedtls_short_weierstrass_derive_y(ecp_kp_grp, Y, (*p & 1));
-+    if (ret != 0)
-+        return( ret );
-+
-+    return mbedtls_ecp_check_pubkey( ecp_kp_grp, ecp_kp_Q );
-+}
-+
-+struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len)
-+{
-+	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+	if (ctx == NULL)
-+		return NULL;
-+	mbedtls_pk_init(ctx);
-+	/*int rc = mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx);*/
-+	int rc = mbedtls_pk_parse_public_key(ctx, der, der_len);
-+	if (rc == 0)
-+		return (struct crypto_ec_key *)ctx;
-+	else if (rc == MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) {
-+		/* mbedtls mbedtls_ecp_point_read_binary()
-+		 * does not handle point in COMPRESSED format; parse internally */
-+		rc = crypto_mbedtls_pk_parse_subpubkey_compressed(ctx,der,der_len);
-+		if (rc == 0)
-+			return (struct crypto_ec_key *)ctx;
-+	}
-+
-+	mbedtls_pk_free(ctx);
-+	os_free(ctx);
-+	return NULL;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+
-+static struct crypto_ec_key *
-+crypto_ec_key_set_pub_point_for_group(mbedtls_ecp_group_id grp_id,
-+                                      const mbedtls_ecp_point *pub,
-+                                      const u8 *buf, size_t len)
-+{
-+	const mbedtls_pk_info_t *pk_info =
-+	  mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
-+	if (pk_info == NULL)
-+		return NULL;
-+	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+	if (ctx == NULL)
-+		return NULL;
-+	mbedtls_pk_init(ctx);
-+	if (mbedtls_pk_setup(ctx, pk_info) == 0) {
-+		/* (Is private key generation necessary for callers?)
-+		 * alt: gen key then overwrite Q
-+		 *   mbedtls_ecp_gen_key(grp_id, ecp_kp,
-+	         *                       mbedtls_ctr_drbg_random,
-+	         *                       crypto_mbedtls_ctr_drbg()) == 0
-+	         */
-+		mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx);
-+		mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+		mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+		mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d);
-+		if (mbedtls_ecp_group_load(ecp_kp_grp, grp_id) == 0
-+		    && (pub
-+		         ? mbedtls_ecp_copy(ecp_kp_Q, pub) == 0
-+		         : mbedtls_ecp_point_read_binary(ecp_kp_grp, ecp_kp_Q,
-+		                                         buf, len) == 0)
-+		    && mbedtls_ecp_gen_privkey(ecp_kp_grp, ecp_kp_d,
-+		                               mbedtls_ctr_drbg_random,
-+		                               crypto_mbedtls_ctr_drbg()) == 0){
-+			return (struct crypto_ec_key *)ctx;
-+		}
-+	}
-+
-+	mbedtls_pk_free(ctx);
-+	os_free(ctx);
-+	return NULL;
-+}
-+
-+struct crypto_ec_key * crypto_ec_key_set_pub(int group, const u8 *x,
-+					     const u8 *y, size_t len)
-+{
-+	mbedtls_ecp_group_id grp_id =
-+	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+	if (grp_id == MBEDTLS_ECP_DP_NONE)
-+		return NULL;
-+	if (len > MBEDTLS_MPI_MAX_SIZE)
-+		return NULL;
-+	u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
-+	buf[0] = 0x04; /* assume x,y for Short Weierstrass */
-+	os_memcpy(buf+1, x, len);
-+	os_memcpy(buf+1+len, y, len);
-+
-+	return crypto_ec_key_set_pub_point_for_group(grp_id,NULL,buf,1+len*2);
-+}
-+
-+struct crypto_ec_key *
-+crypto_ec_key_set_pub_point(struct crypto_ec *e,
-+			    const struct crypto_ec_point *pub)
-+{
-+	mbedtls_ecp_group_id grp_id = ((mbedtls_ecp_group *)e)->id;
-+	mbedtls_ecp_point *p = (mbedtls_ecp_point *)pub;
-+	return crypto_ec_key_set_pub_point_for_group(grp_id, p, NULL, 0);
-+}
-+
-+
-+struct crypto_ec_key * crypto_ec_key_gen(int group)
-+{
-+	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+	if (ctx == NULL)
-+		return NULL;
-+	mbedtls_pk_init(ctx);
-+	if (crypto_mbedtls_keypair_gen(group, ctx) == 0)
-+		return (struct crypto_ec_key *)ctx;
-+	mbedtls_pk_free(ctx);
-+	os_free(ctx);
-+	return NULL;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+void crypto_ec_key_deinit(struct crypto_ec_key *key)
-+{
-+	mbedtls_pk_free((mbedtls_pk_context *)key);
-+	os_free(key);
-+}
-+
-+struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key)
-+{
-+	/* (similar to crypto_ec_key_get_pubkey_point(),
-+	 *  but compressed point format and ASN.1 DER wrapping)*/
-+#ifndef MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/
-+#define MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES    ( 30 + 2 * MBEDTLS_ECP_MAX_BYTES )
-+#endif
-+	unsigned char buf[MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES];
-+	int len = mbedtls_pk_write_pubkey_der((mbedtls_pk_context *)key,
-+	                                      buf, sizeof(buf));
-+	if (len < 0)
-+		return NULL;
-+	/*  Note: data is written at the end of the buffer! Use the
-+	 *        return value to determine where you should start
-+	 *        using the buffer */
-+	unsigned char *p = buf+sizeof(buf)-len;
-+
-+  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+	if (ecp_kp == NULL)
-+		return NULL;
-+	mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+	/*  Note: sae_pk.c expects pubkey point in compressed format,
-+	 *        but mbedtls_pk_write_pubkey_der() writes uncompressed format.
-+	 *        Manually translate format and update lengths in DER format */
-+	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
-+		unsigned char *end = buf+sizeof(buf);
-+		size_t n;
-+		/* SubjectPublicKeyInfo SEQUENCE */
-+		mbedtls_asn1_get_tag(&p, end, &n,
-+		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+		/* algorithm AlgorithmIdentifier */
-+		unsigned char *a = p;
-+		size_t alen;
-+		mbedtls_asn1_get_tag(&p, end, &alen,
-+		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+		p += alen;
-+		alen = (size_t)(p - a);
-+		/* subjectPublicKey BIT STRING */
-+		mbedtls_asn1_get_tag(&p, end, &n, MBEDTLS_ASN1_BIT_STRING);
-+		/* rewrite into compressed point format and rebuild ASN.1 */
-+		p[1] = (buf[sizeof(buf)-1] & 1) ? 0x03 : 0x02;
-+		n = 1 + 1 + (n-2)/2;
-+		len = mbedtls_asn1_write_len(&p, buf, n) + (int)n;
-+		len += mbedtls_asn1_write_tag(&p, buf, MBEDTLS_ASN1_BIT_STRING);
-+		os_memmove(p-alen, a, alen);
-+		len += alen;
-+		p -= alen;
-+		len += mbedtls_asn1_write_len(&p, buf, (size_t)len);
-+		len += mbedtls_asn1_write_tag(&p, buf,
-+		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+	}
-+  #endif
-+	return wpabuf_alloc_copy(p, (size_t)len);
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+
-+struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key,
-+						bool include_pub)
-+{
-+#ifndef MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/
-+#define MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES    ( 29 + 3 * MBEDTLS_ECP_MAX_BYTES )
-+#endif
-+	unsigned char priv[MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES];
-+	int privlen = mbedtls_pk_write_key_der((mbedtls_pk_context *)key,
-+	                                       priv, sizeof(priv));
-+	if (privlen < 0)
-+		return NULL;
-+
-+	struct wpabuf *wbuf;
-+
-+	/*  Note: data is written at the end of the buffer! Use the
-+	 *        return value to determine where you should start
-+	 *        using the buffer */
-+	/* mbedtls_pk_write_key_der() includes publicKey in DER */
-+	if (include_pub)
-+		wbuf = wpabuf_alloc_copy(priv+sizeof(priv)-privlen, privlen);
-+	else {
-+		/* calculate publicKey offset and skip from end of buffer */
-+		unsigned char *p = priv+sizeof(priv)-privlen;
-+		unsigned char *end = priv+sizeof(priv);
-+		size_t len;
-+		/* ECPrivateKey SEQUENCE */
-+		mbedtls_asn1_get_tag(&p, end, &len,
-+		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+		/* version INTEGER */
-+		unsigned char *v = p;
-+		mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_INTEGER);
-+		p += len;
-+		/* privateKey OCTET STRING */
-+		mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING);
-+		p += len;
-+		/* parameters ECParameters */
-+		mbedtls_asn1_get_tag(&p, end, &len,
-+		    MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED);
-+		p += len;
-+
-+		/* write new SEQUENCE header (we know that it fits in priv[]) */
-+		len = (size_t)(p - v);
-+		p = v;
-+		len += mbedtls_asn1_write_len(&p, priv, len);
-+		len += mbedtls_asn1_write_tag(&p, priv,
-+		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+		wbuf = wpabuf_alloc_copy(p, len);
-+	}
-+
-+	forced_memzero(priv, sizeof(priv));
-+	return wbuf;
-+}
-+
-+struct wpabuf * crypto_ec_key_get_pubkey_point(struct crypto_ec_key *key,
-+					       int prefix)
-+{
-+	/*(similarities to crypto_ecdh_get_pubkey(), but different struct)*/
-+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+	if (ecp_kp == NULL)
-+		return NULL;
-+	mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+	size_t len = CRYPTO_EC_plen(grp);
-+  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+	/* len */
-+  #endif
-+  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS)
-+		len = len*2+1;
-+  #endif
-+	struct wpabuf *buf = wpabuf_alloc(len);
-+	if (buf == NULL)
-+		return NULL;
-+	mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+	if (mbedtls_ecp_point_write_binary(grp, ecp_kp_Q,
-+	                                   MBEDTLS_ECP_PF_UNCOMPRESSED, &len,
-+	                                   wpabuf_mhead_u8(buf), len) == 0) {
-+		if (!prefix) /* Remove 0x04 prefix if requested */
-+			os_memmove(wpabuf_mhead(buf),wpabuf_mhead(buf)+1,--len);
-+		wpabuf_put(buf, len);
-+		return buf;
-+	}
-+
-+	wpabuf_free(buf);
-+	return NULL;
-+}
-+
-+struct crypto_ec_point *
-+crypto_ec_key_get_public_key(struct crypto_ec_key *key)
-+{
-+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+	if (ecp_kp == NULL)
-+		return NULL;
-+	mbedtls_ecp_point *p = os_malloc(sizeof(*p));
-+	if (p != NULL) {
-+		/*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/
-+		mbedtls_ecp_point_init(p);
-+		mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+		if (mbedtls_ecp_copy(p, ecp_kp_Q)) {
-+			mbedtls_ecp_point_free(p);
-+			os_free(p);
-+			p = NULL;
-+		}
-+	}
-+	return (struct crypto_ec_point *)p;
-+}
-+
-+struct crypto_bignum *
-+crypto_ec_key_get_private_key(struct crypto_ec_key *key)
-+{
-+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+	if (ecp_kp == NULL)
-+		return NULL;
-+	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
-+	if (bn) {
-+		/*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/
-+		mbedtls_mpi_init(bn);
-+		mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d);
-+		if (mbedtls_mpi_copy(bn, ecp_kp_d)) {
-+			mbedtls_mpi_free(bn);
-+			os_free(bn);
-+			bn = NULL;
-+		}
-+	}
-+	return (struct crypto_bignum *)bn;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+static mbedtls_md_type_t crypto_ec_key_sign_md(size_t len)
-+{
-+	/* get mbedtls_md_type_t from length of hash data to be signed */
-+	switch (len) {
-+	case 64: return MBEDTLS_MD_SHA512;
-+	case 48: return MBEDTLS_MD_SHA384;
-+	case 32: return MBEDTLS_MD_SHA256;
-+	case 20: return MBEDTLS_MD_SHA1;
-+	case 16: return MBEDTLS_MD_MD5;
-+	default: return MBEDTLS_MD_NONE;
-+	}
-+}
-+
-+struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
-+				   size_t len)
-+{
-+  #ifndef MBEDTLS_PK_SIGNATURE_MAX_SIZE /*(defined since mbedtls 2.20.0)*/
-+  #if MBEDTLS_ECDSA_MAX_LEN > MBEDTLS_MPI_MAX_SIZE
-+  #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_ECDSA_MAX_LEN
-+  #else
-+  #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_MPI_MAX_SIZE
-+  #endif
-+  #endif
-+	size_t sig_len = MBEDTLS_PK_SIGNATURE_MAX_SIZE;
-+	struct wpabuf *buf = wpabuf_alloc(sig_len);
-+	if (buf == NULL)
-+		return NULL;
-+	if (mbedtls_pk_sign((mbedtls_pk_context *)key,
-+	                    crypto_ec_key_sign_md(len), data, len,
-+	                    wpabuf_mhead_u8(buf),
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+	                    sig_len,
-+  #endif
-+	                    &sig_len,
-+	                    mbedtls_ctr_drbg_random,
-+	                    crypto_mbedtls_ctr_drbg()) == 0) {
-+		wpabuf_put(buf, sig_len);
-+		return buf;
-+	}
-+
-+	wpabuf_free(buf);
-+	return NULL;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+struct wpabuf * crypto_ec_key_sign_r_s(struct crypto_ec_key *key,
-+				       const u8 *data, size_t len)
-+{
-+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+	if (ecp_kp == NULL)
-+		return NULL;
-+
-+	size_t sig_len = MBEDTLS_ECDSA_MAX_LEN;
-+	u8 buf[MBEDTLS_ECDSA_MAX_LEN];
-+	if (mbedtls_ecdsa_write_signature(ecp_kp, crypto_ec_key_sign_md(len),
-+	                                  data, len, buf,
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+	                                  sig_len,
-+  #endif
-+	                                  &sig_len,
-+	                                  mbedtls_ctr_drbg_random,
-+	                                  crypto_mbedtls_ctr_drbg())) {
-+		return NULL;
-+	}
-+
-+	/*(mbedtls_ecdsa_write_signature() writes signature in ASN.1)*/
-+	/* parse ASN.1 to get r and s and lengths */
-+	u8 *p = buf, *r, *s;
-+	u8 *end = p + sig_len;
-+	size_t rlen, slen;
-+	mbedtls_asn1_get_tag(&p, end, &rlen,
-+	  MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+	mbedtls_asn1_get_tag(&p, end, &rlen, MBEDTLS_ASN1_INTEGER);
-+	r = p;
-+	p += rlen;
-+	mbedtls_asn1_get_tag(&p, end, &slen, MBEDTLS_ASN1_INTEGER);
-+	s = p;
-+
-+	/* write raw r and s into out
-+	 * (including removal of leading 0 if added for ASN.1 integer)
-+	 * note: DPP caller expects raw r, s each padded to prime len */
-+	mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+	size_t plen = CRYPTO_EC_plen(ecp_kp_grp);
-+	if (rlen > plen) {
-+		r += (rlen - plen);
-+		rlen = plen;
-+	}
-+	if (slen > plen) {
-+		s += (slen - plen);
-+		slen = plen;
-+	}
-+	struct wpabuf *out = wpabuf_alloc(plen*2);
-+	if (out) {
-+		wpabuf_put(out, plen*2);
-+		p = wpabuf_mhead_u8(out);
-+		os_memset(p, 0, plen*2);
-+		os_memcpy(p+plen*1-rlen, r, rlen);
-+		os_memcpy(p+plen*2-slen, s, slen);
-+	}
-+	return out;
-+}
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
-+				   size_t len, const u8 *sig, size_t sig_len)
-+{
-+	switch (mbedtls_pk_verify((mbedtls_pk_context *)key,
-+	                          crypto_ec_key_sign_md(len), data, len,
-+	                          sig, sig_len)) {
-+	case 0:
-+	/*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */
-+		return 1;
-+	case MBEDTLS_ERR_ECP_VERIFY_FAILED:
-+		return 0;
-+	default:
-+		return -1;
-+	}
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+int crypto_ec_key_verify_signature_r_s(struct crypto_ec_key *key,
-+				       const u8 *data, size_t len,
-+				       const u8 *r, size_t r_len,
-+				       const u8 *s, size_t s_len)
-+{
-+	/* reimplement mbedtls_ecdsa_read_signature() without encoding r and s
-+	 * into ASN.1 just for mbedtls_ecdsa_read_signature() to decode ASN.1 */
-+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+	if (ecp_kp == NULL)
-+		return -1;
-+	mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+	mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+
-+	mbedtls_mpi mpi_r;
-+	mbedtls_mpi mpi_s;
-+	mbedtls_mpi_init(&mpi_r);
-+	mbedtls_mpi_init(&mpi_s);
-+	int ret = mbedtls_mpi_read_binary(&mpi_r, r, r_len)
-+	       || mbedtls_mpi_read_binary(&mpi_s, s, s_len) ? -1 : 0;
-+	if (ret == 0) {
-+		ret = mbedtls_ecdsa_verify(ecp_kp_grp, data, len,
-+		                           ecp_kp_Q, &mpi_r, &mpi_s);
-+		ret = ret ? ret == MBEDTLS_ERR_ECP_BAD_INPUT_DATA ? 0 : -1 : 1;
-+	}
-+	mbedtls_mpi_free(&mpi_r);
-+	mbedtls_mpi_free(&mpi_s);
-+	return ret;
-+}
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+int crypto_ec_key_group(struct crypto_ec_key *key)
-+{
-+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+	if (ecp_kp == NULL)
-+		return -1;
-+	mbedtls_ecp_group *ecp_group = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+	return crypto_mbedtls_ike_id_from_ecp_group_id(ecp_group->id);
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+
-+int crypto_ec_key_cmp(struct crypto_ec_key *key1, struct crypto_ec_key *key2)
-+{
-+#if 0 /*(DPP is passing two public keys; unable to use pk_check_pair())*/
-+  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+	return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1,
-+	                             (const mbedtls_pk_context *)key2) ? -1 : 0;
-+  #else
-+	return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1,
-+	                             (const mbedtls_pk_context *)key2,
-+	                             mbedtls_ctr_drbg_random,
-+	                             crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+  #endif
-+#else
-+	mbedtls_ecp_keypair *ecp_kp1=mbedtls_pk_ec(*(mbedtls_pk_context *)key1);
-+	mbedtls_ecp_keypair *ecp_kp2=mbedtls_pk_ec(*(mbedtls_pk_context *)key2);
-+	if (ecp_kp1 == NULL || ecp_kp2 == NULL)
-+		return -1;
-+	mbedtls_ecp_group *ecp_kp1_grp = &ecp_kp1->MBEDTLS_PRIVATE(grp);
-+	mbedtls_ecp_group *ecp_kp2_grp = &ecp_kp2->MBEDTLS_PRIVATE(grp);
-+	mbedtls_ecp_point *ecp_kp1_Q = &ecp_kp1->MBEDTLS_PRIVATE(Q);
-+	mbedtls_ecp_point *ecp_kp2_Q = &ecp_kp2->MBEDTLS_PRIVATE(Q);
-+	return ecp_kp1_grp->id != ecp_kp2_grp->id
-+	    || mbedtls_ecp_point_cmp(ecp_kp1_Q, ecp_kp2_Q) ? -1 : 0;
-+#endif
-+}
-+
-+void crypto_ec_key_debug_print(const struct crypto_ec_key *key,
-+			       const char *title)
-+{
-+	/* TBD: what info is desirable here and in what human readable format?*/
-+	/*(crypto_openssl.c prints a human-readably public key and attributes)*/
-+  #if 0
-+	struct mbedtls_pk_debug_item debug_item;
-+	if (mbedtls_pk_debug((const mbedtls_pk_context *)key, &debug_item))
-+		return;
-+	/* ... */
-+  #endif
-+	wpa_printf(MSG_DEBUG, "%s: %s not implemented", title, __func__);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_CSR
-+
-+#include <mbedtls/x509_csr.h>
-+#include <mbedtls/oid.h>
-+
-+struct crypto_csr * crypto_csr_init(void)
-+{
-+	mbedtls_x509write_csr *csr = os_malloc(sizeof(*csr));
-+	if (csr != NULL)
-+		mbedtls_x509write_csr_init(csr);
-+	return (struct crypto_csr *)csr;
-+}
-+
-+struct crypto_csr * crypto_csr_verify(const struct wpabuf *req)
-+{
-+	/* future: look for alternatives to MBEDTLS_PRIVATE() access */
-+
-+	/* sole caller src/common/dpp_crypto.c:dpp_validate_csr()
-+	 * uses (mbedtls_x509_csr *) to obtain CSR_ATTR_CHALLENGE_PASSWORD
-+	 * so allocate different object (mbedtls_x509_csr *) and special-case
-+	 * object when used in crypto_csr_get_attribute() and when free()d in
-+	 * crypto_csr_deinit(). */
-+
-+	mbedtls_x509_csr *csr = os_malloc(sizeof(*csr));
-+	if (csr == NULL)
-+		return NULL;
-+	mbedtls_x509_csr_init(csr);
-+	const mbedtls_md_info_t *md_info;
-+	unsigned char digest[MBEDTLS_MD_MAX_SIZE];
-+	if (mbedtls_x509_csr_parse_der(csr,wpabuf_head(req),wpabuf_len(req))==0
-+	    && (md_info=mbedtls_md_info_from_type(csr->MBEDTLS_PRIVATE(sig_md)))
-+	       != NULL
-+	    && mbedtls_md(md_info, csr->cri.p, csr->cri.len, digest) == 0) {
-+		switch (mbedtls_pk_verify(&csr->pk,csr->MBEDTLS_PRIVATE(sig_md),
-+		                          digest, mbedtls_md_get_size(md_info),
-+		                          csr->MBEDTLS_PRIVATE(sig).p,
-+		                          csr->MBEDTLS_PRIVATE(sig).len)) {
-+		case 0:
-+		/*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */
-+			return (struct crypto_csr *)((uintptr_t)csr | 1uL);
-+		default:
-+			break;
-+		}
-+	}
-+
-+	mbedtls_x509_csr_free(csr);
-+	os_free(csr);
-+	return NULL;
-+}
-+
-+void crypto_csr_deinit(struct crypto_csr *csr)
-+{
-+	if ((uintptr_t)csr & 1uL) {
-+		csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL);
-+		mbedtls_x509_csr_free((mbedtls_x509_csr *)csr);
-+	}
-+	else
-+		mbedtls_x509write_csr_free((mbedtls_x509write_csr *)csr);
-+	os_free(csr);
-+}
-+
-+int crypto_csr_set_ec_public_key(struct crypto_csr *csr,
-+				 struct crypto_ec_key *key)
-+{
-+	mbedtls_x509write_csr_set_key((mbedtls_x509write_csr *)csr,
-+	                              (mbedtls_pk_context *)key);
-+	return 0;
-+}
-+
-+int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type,
-+			const char *name)
-+{
-+	/* specialized for src/common/dpp_crypto.c */
-+
-+	/* sole caller src/common/dpp_crypto.c:dpp_build_csr()
-+	 * calls this function only once, using type == CSR_NAME_CN
-+	 * (If called more than once, this code would need to append
-+	 *  components to the subject name, which we could do by
-+	 *  appending to (mbedtls_x509write_csr *) private member
-+	 *  mbedtls_asn1_named_data *MBEDTLS_PRIVATE(subject)) */
-+
-+	const char *label;
-+	switch (type) {
-+	case CSR_NAME_CN: label = "CN="; break;
-+	case CSR_NAME_SN: label = "SN="; break;
-+	case CSR_NAME_C:  label = "C=";  break;
-+	case CSR_NAME_O:  label = "O=";  break;
-+	case CSR_NAME_OU: label = "OU="; break;
-+	default: return -1;
-+	}
-+
-+	size_t len = strlen(name);
-+	struct wpabuf *buf = wpabuf_alloc(3+len+1);
-+	if (buf == NULL)
-+		return -1;
-+	wpabuf_put_data(buf, label, strlen(label));
-+	wpabuf_put_data(buf, name, len+1); /*(include trailing '\0')*/
-+	/* Note: 'name' provided is set as given and should be backslash-escaped
-+	 * by caller when necessary, e.g. literal ',' which are not separating
-+	 * components should be backslash-escaped */
-+
-+	int ret =
-+	  mbedtls_x509write_csr_set_subject_name((mbedtls_x509write_csr *)csr,
-+	                                         wpabuf_head(buf)) ? -1 : 0;
-+	wpabuf_free(buf);
-+	return ret;
-+}
-+
-+/* OBJ_pkcs9_challengePassword  1 2 840 113549 1 9 7 */
-+static const char OBJ_pkcs9_challengePassword[] = MBEDTLS_OID_PKCS9 "\x07";
-+
-+int crypto_csr_set_attribute(struct crypto_csr *csr, enum crypto_csr_attr attr,
-+			     int attr_type, const u8 *value, size_t len)
-+{
-+	/* specialized for src/common/dpp_crypto.c */
-+	/* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes
-+	 *   attr      == CSR_ATTR_CHALLENGE_PASSWORD
-+	 *   attr_type == ASN1_TAG_UTF8STRING */
-+
-+	const char *oid;
-+	size_t oid_len;
-+	switch (attr) {
-+	case CSR_ATTR_CHALLENGE_PASSWORD:
-+		oid = OBJ_pkcs9_challengePassword;
-+		oid_len = sizeof(OBJ_pkcs9_challengePassword)-1;
-+		break;
-+	default:
-+		return -1;
-+	}
-+
-+  #if 0 /*(incorrect; sets an extension, not an attribute)*/
-+	return mbedtls_x509write_csr_set_extension((mbedtls_x509write_csr *)csr,
-+	                                           oid, oid_len,
-+	  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+	                                           0, /*(critical flag)*/
-+	  #endif
-+	                                           value, len) ? -1 : 0;
-+  #else
-+	(void)oid;
-+	(void)oid_len;
-+  #endif
-+
-+	/* mbedtls does not currently provide way to set an attribute in a CSR:
-+	 *   https://github.com/Mbed-TLS/mbedtls/issues/4886 */
-+	wpa_printf(MSG_ERROR,
-+	  "mbedtls does not currently support setting challengePassword "
-+	  "attribute in CSR");
-+	return -1;
-+}
-+
-+const u8 * mbedtls_x509_csr_attr_oid_value(mbedtls_x509_csr *csr,
-+                                           const char *oid, size_t oid_len,
-+                                           size_t *vlen, int *vtype)
-+{
-+	/* Note: mbedtls_x509_csr_parse_der() has parsed and validated CSR,
-+	 *	   so validation checks are not repeated here
-+	 *
-+	 * It would be nicer if (mbedtls_x509_csr *) had an mbedtls_x509_buf of
-+	 * Attributes (or at least a pointer) since mbedtls_x509_csr_parse_der()
-+	 * already parsed the rest of CertificationRequestInfo, some of which is
-+	 * repeated here to step to Attributes.  Since csr->subject_raw.p points
-+	 * into csr->cri.p, which points into csr->raw.p, step over version and
-+	 * subject of CertificationRequestInfo (SEQUENCE) */
-+	unsigned char *p = csr->subject_raw.p + csr->subject_raw.len;
-+	unsigned char *end = csr->cri.p + csr->cri.len, *ext;
-+	size_t len;
-+
-+	/* step over SubjectPublicKeyInfo */
-+	mbedtls_asn1_get_tag(&p, end, &len,
-+	    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+	p += len;
-+
-+	/* Attributes
-+	 *   { ATTRIBUTE:IOSet } ::= SET OF { SEQUENCE { OID, value } }
-+	 */
-+	if (mbedtls_asn1_get_tag(&p, end, &len,
-+	      MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) != 0) {
-+		return NULL;
-+	}
-+	while (p < end) {
-+		if (mbedtls_asn1_get_tag(&p, end, &len,
-+		      MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
-+			return NULL;
-+		}
-+		ext = p;
-+		p += len;
-+
-+		if (mbedtls_asn1_get_tag(&ext,end,&len,MBEDTLS_ASN1_OID) != 0)
-+			return NULL;
-+		if (oid_len != len || 0 != memcmp(ext, oid, oid_len))
-+			continue;
-+
-+		/* found oid; return value */
-+		*vtype = *ext++; /* tag */
-+		return (mbedtls_asn1_get_len(&ext,end,vlen) == 0) ? ext : NULL;
-+	}
-+
-+	return NULL;
-+}
-+
-+const u8 * crypto_csr_get_attribute(struct crypto_csr *csr,
-+				    enum crypto_csr_attr attr,
-+				    size_t *len, int *type)
-+{
-+	/* specialized for src/common/dpp_crypto.c */
-+	/* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes
-+	 *   attr == CSR_ATTR_CHALLENGE_PASSWORD */
-+
-+	const char *oid;
-+	size_t oid_len;
-+	switch (attr) {
-+	case CSR_ATTR_CHALLENGE_PASSWORD:
-+		oid = OBJ_pkcs9_challengePassword;
-+		oid_len = sizeof(OBJ_pkcs9_challengePassword)-1;
-+		break;
-+	default:
-+		return NULL;
-+	}
-+
-+	/* see crypto_csr_verify(); expecting (mbedtls_x509_csr *) tagged |=1 */
-+	if (!((uintptr_t)csr & 1uL))
-+		return NULL;
-+	csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL);
-+
-+	return mbedtls_x509_csr_attr_oid_value((mbedtls_x509_csr *)csr,
-+	                                       oid, oid_len, len, type);
-+}
-+
-+struct wpabuf * crypto_csr_sign(struct crypto_csr *csr,
-+				struct crypto_ec_key *key,
-+				enum crypto_hash_alg algo)
-+{
-+	mbedtls_md_type_t sig_md;
-+	switch (algo) {
-+  #ifdef MBEDTLS_SHA256_C
-+	case CRYPTO_HASH_ALG_SHA256: sig_md = MBEDTLS_MD_SHA256; break;
-+  #endif
-+  #ifdef MBEDTLS_SHA512_C
-+	case CRYPTO_HASH_ALG_SHA384: sig_md = MBEDTLS_MD_SHA384; break;
-+	case CRYPTO_HASH_ALG_SHA512: sig_md = MBEDTLS_MD_SHA512; break;
-+  #endif
-+	default:
-+		return NULL;
-+	}
-+	mbedtls_x509write_csr_set_md_alg((mbedtls_x509write_csr *)csr, sig_md);
-+
-+  #if 0
-+	unsigned char key_usage = MBEDTLS_X509_KU_DIGITAL_SIGNATURE
-+	                        | MBEDTLS_X509_KU_KEY_CERT_SIGN;
-+	if (mbedtls_x509write_csr_set_key_usage((mbedtls_x509write_csr *)csr,
-+	                                        key_usage))
-+		return NULL;
-+  #endif
-+
-+  #if 0
-+	unsigned char ns_cert_type = MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT
-+	                           | MBEDTLS_X509_NS_CERT_TYPE_EMAIL;
-+	if (mbedtls_x509write_csr_set_ns_cert_type((mbedtls_x509write_csr *)csr,
-+	                                           ns_cert_type))
-+		return NULL;
-+  #endif
-+
-+  #if 0
-+	/* mbedtls does not currently provide way to set an attribute in a CSR:
-+	 *   https://github.com/Mbed-TLS/mbedtls/issues/4886
-+	 * XXX: hwsim dpp_enterprise test fails due to this limitation.
-+	 *
-+	 * Current usage of this function is solely by dpp_build_csr(),
-+	 * so as a kludge, might consider custom (struct crypto_csr *)
-+	 * containing (mbedtls_x509write_csr *) and a list of attributes
-+	 * (i.e. challengePassword).  Might have to totally reimplement
-+	 * mbedtls_x509write_csr_der(); underlying x509write_csr_der_internal()
-+	 * handles signing the CSR.  (This is more work that appending an
-+	 * Attributes section to end of CSR and adjusting ASN.1 length of CSR.)
-+	 */
-+  #endif
-+
-+	unsigned char buf[4096]; /* XXX: large enough?  too large? */
-+	int len = mbedtls_x509write_csr_der((mbedtls_x509write_csr *)csr,
-+	                                    buf, sizeof(buf),
-+	                                    mbedtls_ctr_drbg_random,
-+	                                    crypto_mbedtls_ctr_drbg());
-+	if (len < 0)
-+		return NULL;
-+	/*  Note: data is written at the end of the buffer! Use the
-+	 *        return value to determine where you should start
-+	 *        using the buffer */
-+	return wpabuf_alloc_copy(buf+sizeof(buf)-len, (size_t)len);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_CSR */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_PKCS7
-+
-+#if 0
-+#include <mbedtls/pkcs7.h> /* PKCS7 is not currently supported in mbedtls */
-+#include <mbedtls/pem.h>
-+#endif
-+
-+struct wpabuf * crypto_pkcs7_get_certificates(const struct wpabuf *pkcs7)
-+{
-+	/* PKCS7 is not currently supported in mbedtls */
-+	return NULL;
-+
-+#if 0
-+	/* https://github.com/naynajain/mbedtls-1 branch: development-pkcs7
-+	 * (??? potential future contribution to mbedtls ???) */
-+
-+	/* Note: PKCS7 signature *is not* verified by this function.
-+	 * The function interface does not provide for passing a certificate */
-+
-+	mbedtls_pkcs7 mpkcs7;
-+	mbedtls_pkcs7_init(&mpkcs7);
-+	int pkcs7_type = mbedtls_pkcs7_parse_der(wpabuf_head(pkcs7),
-+	                                         wpabuf_len(pkcs7),
-+	                                         &mpkcs7);
-+	wpabuf *buf = NULL;
-+	do {
-+		if (pkcs7_type < 0)
-+			break;
-+
-+		/* src/common/dpp.c:dpp_parse_cred_dot1x() interested in certs
-+		 * for wpa_supplicant/dpp_supplicant.c:wpas_dpp_add_network()
-+		 * (? are adding certificate headers and footers desired ?) */
-+
-+		/* development-pkcs7 branch does not currently provide
-+		 * additional interfaces to retrieve the parsed data */
-+
-+		mbedtls_x509_crt *certs =
-+		  &mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(certs);
-+		int ncerts =
-+		  mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(no_of_certs);
-+
-+		/* allocate buffer for PEM (base64-encoded DER)
-+		 * plus header, footer, newlines, and some extra */
-+		buf = wpabuf_alloc((wpabuf_len(pkcs7)+2)/3*4 + ncerts*64);
-+		if (buf == NULL)
-+			break;
-+
-+		#define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n"
-+		#define PEM_END_CRT   "-----END CERTIFICATE-----\n"
-+		size_t olen;
-+		for (int i = 0; i < ncerts; ++i) {
-+			int ret = mbedtls_pem_write_buffer(
-+			            PEM_BEGIN_CRT, PEM_END_CRT,
-+			            certs[i].raw.p, certs[i].raw.len,
-+			            wpabuf_mhead(buf, 0), wpabuf_tailroom(buf),
-+			            &olen));
-+			if (ret == 0)
-+				wpabuf_put(buf, olen);
-+			} else {
-+				if (ret == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL)
-+					ret = wpabuf_resize(
-+					        &buf,olen-wpabuf_tailroom(buf));
-+				if (ret == 0) {
-+					--i;/*(adjust loop iterator for retry)*/
-+					continue;
-+				}
-+				wpabuf_free(buf);
-+				buf = NULL;
-+				break;
-+			}
-+		}
-+	} while (0);
-+
-+	mbedtls_pkcs7_free(&mpkcs7);
-+	return buf;
-+#endif
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_PKCS7 */
-+
-+
-+#ifdef MBEDTLS_ARC4_C
-+#include <mbedtls/arc4.h>
-+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
-+	     u8 *data, size_t data_len)
-+{
-+	mbedtls_arc4_context ctx;
-+	mbedtls_arc4_init(&ctx);
-+	mbedtls_arc4_setup(&ctx, key, keylen);
-+
-+	if (skip) {
-+		/*(prefer [16] on ancient hardware with smaller cache lines)*/
-+		unsigned char skip_buf[64]; /*('skip' is generally small)*/
-+		/*os_memset(skip_buf, 0, sizeof(skip_buf));*/ /*(necessary?)*/
-+		size_t len;
-+		do {
-+			len = skip > sizeof(skip_buf) ? sizeof(skip_buf) : skip;
-+			mbedtls_arc4_crypt(&ctx, len, skip_buf, skip_buf);
-+		} while ((skip -= len));
-+	}
-+
-+	int ret = mbedtls_arc4_crypt(&ctx, data_len, data, data);
-+	mbedtls_arc4_free(&ctx);
-+	return ret;
-+}
-+#endif
-+
-+
-+/* duplicated in tls_mbedtls.c:tls_mbedtls_readfile()*/
-+__attribute_noinline__
-+static int crypto_mbedtls_readfile(const char *path, u8 **buf, size_t *n)
-+{
-+  #if 0 /* #ifdef MBEDTLS_FS_IO */
-+	/*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/
-+	if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) {
-+		wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path);
-+		return -1;
-+	}
-+  #else
-+	/*(use os_readfile() so that we can use os_free()
-+	 *(if we use mbedtls_pk_load_file() above, macros prevent calling free()
-+	 * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free()
-+	 * on buf aborts in tests if buf not allocated via os_malloc())*/
-+	*buf = (u8 *)os_readfile(path, n);
-+	if (!*buf) {
-+		wpa_printf(MSG_ERROR, "error: os_readfile %s", path);
-+		return -1;
-+	}
-+	u8 *buf0 = os_realloc(*buf, *n+1);
-+	if (!buf0) {
-+		bin_clear_free(*buf, *n);
-+		*buf = NULL;
-+		return -1;
-+	}
-+	buf0[(*n)++] = '\0';
-+	*buf = buf0;
-+  #endif
-+	return 0;
-+}
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_RSA
-+#ifdef MBEDTLS_RSA_C
-+
-+#include <mbedtls/pk.h>
-+#include <mbedtls/rsa.h>
-+
-+struct crypto_rsa_key * crypto_rsa_key_read(const char *file, bool private_key)
-+{
-+	/* mbedtls_pk_parse_keyfile() and mbedtls_pk_parse_public_keyfile()
-+	 * require #ifdef MBEDTLS_FS_IO in mbedtls library.  Prefer to use
-+	 * crypto_mbedtls_readfile(), which wraps os_readfile() */
-+	u8 *data;
-+	size_t len;
-+	if (crypto_mbedtls_readfile(file, &data, &len) != 0)
-+		return NULL;
-+
-+	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+	if (ctx == NULL) {
-+		bin_clear_free(data, len);
-+		return NULL;
-+	}
-+	mbedtls_pk_init(ctx);
-+
-+	int rc;
-+	rc = (private_key
-+	      ? mbedtls_pk_parse_key(ctx, data, len, NULL, 0
-+	  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+	                            ,mbedtls_ctr_drbg_random,
-+	                             crypto_mbedtls_ctr_drbg()
-+	  #endif
-+	                            )
-+	      : mbedtls_pk_parse_public_key(ctx, data, len)) == 0
-+	    && mbedtls_pk_can_do(ctx, MBEDTLS_PK_RSA);
-+
-+	bin_clear_free(data, len);
-+
-+	if (rc) {
-+		/* use MBEDTLS_RSA_PKCS_V21 padding for RSAES-OAEP */
-+		/* use MBEDTLS_MD_SHA256 for these hostap interfaces */
-+	  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+		/*(no return value in mbedtls 2.x)*/
-+		mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx),
-+		                        MBEDTLS_RSA_PKCS_V21,
-+		                        MBEDTLS_MD_SHA256);
-+	  #else
-+		if (mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx),
-+		                            MBEDTLS_RSA_PKCS_V21,
-+		                            MBEDTLS_MD_SHA256) == 0)
-+	  #endif
-+			return (struct crypto_rsa_key *)ctx;
-+	}
-+
-+	mbedtls_pk_free(ctx);
-+	os_free(ctx);
-+	return NULL;
-+}
-+
-+struct wpabuf * crypto_rsa_oaep_sha256_encrypt(struct crypto_rsa_key *key,
-+					       const struct wpabuf *in)
-+{
-+	mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key);
-+	size_t olen = mbedtls_rsa_get_len(pk_rsa);
-+	struct wpabuf *buf = wpabuf_alloc(olen);
-+	if (buf == NULL)
-+		return NULL;
-+
-+	/* mbedtls_pk_encrypt() takes a few more hops to get to same func */
-+	if (mbedtls_rsa_rsaes_oaep_encrypt(pk_rsa,
-+	                                   mbedtls_ctr_drbg_random,
-+	                                   crypto_mbedtls_ctr_drbg(),
-+	  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+	                                   MBEDTLS_RSA_PRIVATE,
-+	  #endif
-+	                                   NULL, 0,
-+	                                   wpabuf_len(in), wpabuf_head(in),
-+	                                   wpabuf_put(buf, olen)) == 0) {
-+		return buf;
-+	}
-+
-+	wpabuf_clear_free(buf);
-+	return NULL;
-+}
-+
-+struct wpabuf * crypto_rsa_oaep_sha256_decrypt(struct crypto_rsa_key *key,
-+					       const struct wpabuf *in)
-+{
-+	mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key);
-+	size_t olen = mbedtls_rsa_get_len(pk_rsa);
-+	struct wpabuf *buf = wpabuf_alloc(olen);
-+	if (buf == NULL)
-+		return NULL;
-+
-+	/* mbedtls_pk_decrypt() takes a few more hops to get to same func */
-+	if (mbedtls_rsa_rsaes_oaep_decrypt(pk_rsa,
-+	                                   mbedtls_ctr_drbg_random,
-+	                                   crypto_mbedtls_ctr_drbg(),
-+	  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+	                                   MBEDTLS_RSA_PUBLIC,
-+	  #endif
-+	                                   NULL, 0, &olen, wpabuf_head(in),
-+	                                   wpabuf_mhead(buf), olen) == 0) {
-+		wpabuf_put(buf, olen);
-+		return buf;
-+	}
-+
-+	wpabuf_clear_free(buf);
-+	return NULL;
-+}
-+
-+void crypto_rsa_key_free(struct crypto_rsa_key *key)
-+{
-+	mbedtls_pk_free((mbedtls_pk_context *)key);
-+	os_free(key);
-+}
-+
-+#endif /* MBEDTLS_RSA_C */
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_RSA */
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE
-+
-+struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id,
-+			       enum hpke_kdf_id kdf_id,
-+			       enum hpke_aead_id aead_id,
-+			       struct crypto_ec_key *peer_pub,
-+			       const u8 *info, size_t info_len,
-+			       const u8 *aad, size_t aad_len,
-+			       const u8 *pt, size_t pt_len)
-+{
-+	/* not yet implemented */
-+	return NULL;
-+}
-+
-+struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
-+			       enum hpke_kdf_id kdf_id,
-+			       enum hpke_aead_id aead_id,
-+			       struct crypto_ec_key *own_priv,
-+			       const u8 *info, size_t info_len,
-+			       const u8 *aad, size_t aad_len,
-+			       const u8 *enc_ct, size_t enc_ct_len)
-+{
-+	/* not yet implemented */
-+	return NULL;
-+}
-+
-+#endif
-diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
-index ffeddbadd..07c36d850 100644
---- a/src/crypto/crypto_module_tests.c
-+++ b/src/crypto/crypto_module_tests.c
-@@ -2470,6 +2470,139 @@ static int test_hpke(void)
- }
- 
- 
-+static int test_ecc(void)
-+{
-+#ifdef CONFIG_ECC
-+#ifndef CONFIG_TLS_INTERNAL
-+#ifndef CONFIG_TLS_GNUTLS
-+#if defined(CONFIG_TLS_MBEDTLS) \
-+ || defined(CONFIG_TLS_OPENSSL) \
-+ || defined(CONFIG_TLS_WOLFSSL)
-+	wpa_printf(MSG_INFO, "Testing ECC");
-+	/* Note: some tests below are valid on supported Short Weierstrass
-+	 * curves, but not on Montgomery curves (e.g. IKE groups 31 and 32)
-+	 * (e.g. deriving and comparing y^2 test below not valid on Montgomery)
-+	 */
-+#ifdef CONFIG_TLS_MBEDTLS
-+	const int grps[] = {19, 20, 21, 25, 26, 28};
-+#endif
-+#ifdef CONFIG_TLS_OPENSSL
-+	const int grps[] = {19, 20, 21, 26};
-+#endif
-+#ifdef CONFIG_TLS_WOLFSSL
-+	const int grps[] = {19, 20, 21, 26};
-+#endif
-+	uint32_t i;
-+	struct crypto_ec *e = NULL;
-+	struct crypto_ec_point *p = NULL, *q = NULL;
-+	struct crypto_bignum *x = NULL, *y = NULL;
-+#ifdef CONFIG_DPP
-+	u8 bin[4096];
-+#endif
-+	for (i = 0; i < ARRAY_SIZE(grps); ++i) {
-+		e = crypto_ec_init(grps[i]);
-+		if (e == NULL
-+		    || crypto_ec_prime_len(e) == 0
-+		    || crypto_ec_prime_len_bits(e) == 0
-+		    || crypto_ec_order_len(e) == 0
-+		    || crypto_ec_get_prime(e) == NULL
-+		    || crypto_ec_get_order(e) == NULL
-+		    || crypto_ec_get_a(e) == NULL
-+		    || crypto_ec_get_b(e) == NULL
-+		    || crypto_ec_get_generator(e) == NULL) {
-+			break;
-+		}
-+#ifdef CONFIG_DPP
-+		struct crypto_ec_key *key = crypto_ec_key_gen(grps[i]);
-+		if (key == NULL)
-+			break;
-+		p = crypto_ec_key_get_public_key(key);
-+		q = crypto_ec_key_get_public_key(key);
-+		crypto_ec_key_deinit(key);
-+		if (p == NULL || q == NULL)
-+			break;
-+		if (!crypto_ec_point_is_on_curve(e, p))
-+			break;
-+
-+		/* inverted point should not match original;
-+		 * double-invert should match */
-+		if (crypto_ec_point_invert(e, q) != 0
-+		    || crypto_ec_point_cmp(e, p, q) == 0
-+		    || crypto_ec_point_invert(e, q) != 0
-+		    || crypto_ec_point_cmp(e, p, q) != 0) {
-+			break;
-+		}
-+
-+		/* crypto_ec_point_to_bin() and crypto_ec_point_from_bin()
-+		 * imbalanced interfaces? */
-+		size_t prime_len = crypto_ec_prime_len(e);
-+		if (prime_len * 2 > sizeof(bin))
-+			break;
-+		if (crypto_ec_point_to_bin(e, p, bin, bin+prime_len) != 0)
-+			break;
-+		struct crypto_ec_point *tmp = crypto_ec_point_from_bin(e, bin);
-+		if (tmp == NULL)
-+			break;
-+		if (crypto_ec_point_cmp(e, p, tmp) != 0) {
-+			crypto_ec_point_deinit(tmp, 0);
-+			break;
-+		}
-+		crypto_ec_point_deinit(tmp, 0);
-+
-+		x = crypto_bignum_init();
-+		y = crypto_bignum_init_set(bin+prime_len, prime_len);
-+		if (x == NULL || y == NULL || crypto_ec_point_x(e, p, x) != 0)
-+			break;
-+		struct crypto_bignum *y2 = crypto_ec_point_compute_y_sqr(e, x);
-+		if (y2 == NULL)
-+			break;
-+		if (crypto_bignum_sqrmod(y, crypto_ec_get_prime(e), y) != 0
-+		    || crypto_bignum_cmp(y, y2) != 0) {
-+			crypto_bignum_deinit(y2, 0);
-+			break;
-+		}
-+		crypto_bignum_deinit(y2, 0);
-+		crypto_bignum_deinit(x, 0);
-+		crypto_bignum_deinit(y, 0);
-+		x = NULL;
-+		y = NULL;
-+
-+		x = crypto_bignum_init();
-+		if (x == NULL)
-+			break;
-+		if (crypto_bignum_rand(x, crypto_ec_get_prime(e)) != 0)
-+			break;
-+		crypto_bignum_deinit(x, 0);
-+		x = NULL;
-+
-+		crypto_ec_point_deinit(p, 0);
-+		p = NULL;
-+		crypto_ec_point_deinit(q, 0);
-+		q = NULL;
-+#endif /* CONFIG_DPP */
-+		crypto_ec_deinit(e);
-+		e = NULL;
-+	}
-+	if (i != ARRAY_SIZE(grps)) {
-+		crypto_bignum_deinit(x, 0);
-+		crypto_bignum_deinit(y, 0);
-+		crypto_ec_point_deinit(p, 0);
-+		crypto_ec_point_deinit(q, 0);
-+		crypto_ec_deinit(e);
-+		wpa_printf(MSG_INFO,
-+		           "ECC test case failed tls_id:%d", grps[i]);
-+		return -1;
-+	}
-+
-+	wpa_printf(MSG_INFO, "ECC test cases passed");
-+#endif
-+#endif /* !CONFIG_TLS_GNUTLS */
-+#endif /* !CONFIG_TLS_INTERNAL */
-+#endif /* CONFIG_ECC */
-+	return 0;
-+}
-+
-+
- static int test_ms_funcs(void)
- {
- #ifndef CONFIG_FIPS
-@@ -2591,6 +2724,7 @@ int crypto_module_tests(void)
- 	    test_fips186_2_prf() ||
- 	    test_extract_expand_hkdf() ||
- 	    test_hpke() ||
-+	    test_ecc() ||
- 	    test_ms_funcs())
- 		ret = -1;
- 
-diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c
-index 269174321..a554fd8a8 100644
---- a/src/crypto/crypto_wolfssl.c
-+++ b/src/crypto/crypto_wolfssl.c
-@@ -1633,6 +1633,7 @@ struct crypto_ec {
- #ifdef CONFIG_DPP
- 	ecc_point *g; /* Only used in DPP for now */
- #endif /* CONFIG_DPP */
-+	WC_RNG rng;
- 	mp_int a;
- 	mp_int prime;
- 	mp_int order;
-@@ -1666,6 +1667,20 @@ struct crypto_ec * crypto_ec_init(int group)
- 	e->key = ecc_key_init();
- 	if (!e->key) {
- 		LOG_WOLF_ERROR_FUNC_NULL(ecc_key_init);
-+
-+	if (wc_ecc_init(e->key) != 0 ||
-+	    wc_InitRng(e->rng) != 0 ||
-+	    wc_ecc_set_rng(e->key, e->rng) != 0 ||
-+	    wc_ecc_set_curve(e->key, 0, curve_id) != 0 ||
-+	    mp_init(e->a) != MP_OKAY ||
-+	    mp_init(e->prime) != MP_OKAY ||
-+	    mp_init(e->order) != MP_OKAY ||
-+	    mp_init(e->b) != MP_OKAY ||
-+	    mp_read_radix(e->a, e->key.dp->Af, 16) != MP_OKAY ||
-+	    mp_read_radix(e->b, e->key.dp->Bf, 16) != MP_OKAY ||
-+	    mp_read_radix(e->prime, e->key.dp->prime, 16) != MP_OKAY ||
-+	    mp_read_radix(e->order, e->key.dp->order, 16) != MP_OKAY ||
-+	    mp_montgomery_setup(e->prime, e->mont_b) != MP_OKAY)
- 		goto done;
- 	}
- 
-@@ -1764,6 +1779,9 @@ void crypto_ec_deinit(struct crypto_ec* e)
- #endif /* CONFIG_DPP */
- 	if (e->own_key)
- 		ecc_key_deinit(e->key);
-+
-+	wc_FreeRng(e->rng);
-+	wc_ecc_free(e->key);
- 	os_free(e);
- }
- 
-diff --git a/src/crypto/tls_mbedtls.c b/src/crypto/tls_mbedtls.c
-new file mode 100644
-index 000000000..2580a3a27
---- /dev/null
-+++ b/src/crypto/tls_mbedtls.c
-@@ -0,0 +1,3313 @@
-+/*
-+ * SSL/TLS interface functions for mbed TLS
-+ *
-+ * SPDX-FileCopyrightText: 2022 Glenn Strauss <gstrauss@gluelogic.com>
-+ * SPDX-License-Identifier: BSD-3-Clause
-+ *
-+ * This software may be distributed under the terms of the BSD license.
-+ * See README for more details.
-+ *
-+ * template:  src/crypto/tls_none.c
-+ * reference: src/crypto/tls_*.c
-+ *
-+ * Known Limitations:
-+ * - no TLSv1.3 (not available in mbedtls 2.x; experimental in mbedtls 3.x)
-+ * - no OCSP (not yet available in mbedtls)
-+ * - mbedtls does not support all certificate encodings used by hwsim tests
-+ *   PCKS#5 v1.5
-+ *   PCKS#12
-+ *   DH DSA
-+ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
-+ * - mbedtls does not currently provide way to set an attribute in a CSR
-+ *     https://github.com/Mbed-TLS/mbedtls/issues/4886
-+ *   so tests/hwsim dpp_enterprise tests fail
-+ * - DPP2 not supported
-+ *   PKCS#7 parsing is not supported in mbedtls
-+ *   See crypto_mbedtls.c:crypto_pkcs7_get_certificates() comments
-+ * - DPP3 not supported
-+ *   hpke_base_seal() and hpke_base_seal() not implemented in crypto_mbedtls.c
-+ *
-+ * Status:
-+ * - code written to be compatible with mbedtls 2.x and mbedtls 3.x
-+ *   (currently requires mbedtls >= 2.27.0 for mbedtls_mpi_random())
-+ *   (currently requires mbedtls >= 2.18.0 for mbedtls_ssl_tls_prf())
-+ * - builds with tests/build/build-wpa_supplicant-mbedtls.config
-+ * - passes all tests/ crypto module tests (incomplete coverage)
-+ *   ($ cd tests; make clean; make -j 4 run-tests CONFIG_TLS=mbedtls)
-+ * - passes almost all tests/hwsim tests
-+ *   (hwsim tests skipped for missing features)
-+ *
-+ * RFE:
-+ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
-+ * - client/server session resumption, and/or save client session ticket
-+ */
-+
-+#include "includes.h"
-+#include "common.h"
-+
-+#include <mbedtls/version.h>
-+#include <mbedtls/ctr_drbg.h>
-+#include <mbedtls/error.h>
-+#include <mbedtls/oid.h>
-+#include <mbedtls/pem.h>
-+#include <mbedtls/platform.h> /* mbedtls_calloc() mbedtls_free() */
-+#include <mbedtls/platform_util.h> /* mbedtls_platform_zeroize() */
-+#include <mbedtls/ssl.h>
-+#include <mbedtls/ssl_ticket.h>
-+#include <mbedtls/x509.h>
-+#include <mbedtls/x509_crt.h>
-+
-+#if MBEDTLS_VERSION_NUMBER >= 0x02040000 /* mbedtls 2.4.0 */
-+#include <mbedtls/net_sockets.h>
-+#else
-+#include <mbedtls/net.h>
-+#endif
-+
-+#ifndef MBEDTLS_PRIVATE
-+#define MBEDTLS_PRIVATE(x) x
-+#endif
-+
-+#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
-+#define mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl) \
-+        ((ssl)->MBEDTLS_PRIVATE(session) \
-+        ?(ssl)->MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(ciphersuite) \
-+        : 0)
-+#define mbedtls_ssl_ciphersuite_get_name(info) \
-+        (info)->MBEDTLS_PRIVATE(name)
-+#endif
-+
-+#include "crypto.h"     /* sha256_vector() */
-+#include "tls.h"
-+
-+#ifndef SHA256_DIGEST_LENGTH
-+#define SHA256_DIGEST_LENGTH 32
-+#endif
-+
-+#ifndef MBEDTLS_EXPKEY_FIXED_SECRET_LEN
-+#define MBEDTLS_EXPKEY_FIXED_SECRET_LEN 48
-+#endif
-+
-+#ifndef MBEDTLS_EXPKEY_RAND_LEN
-+#define MBEDTLS_EXPKEY_RAND_LEN 32
-+#endif
-+
-+#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+static mbedtls_ssl_export_keys_t tls_connection_export_keys_cb;
-+#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+static mbedtls_ssl_export_keys_ext_t tls_connection_export_keys_cb;
-+#else /*(not implemented; return error)*/
-+#define mbedtls_ssl_tls_prf(a,b,c,d,e,f,g,h) (-1)
-+typedef mbedtls_tls_prf_types int;
-+#endif
-+
-+
-+/* hostapd/wpa_supplicant provides forced_memzero(),
-+ * but prefer mbedtls_platform_zeroize() */
-+#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz)
-+
-+
-+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
-+ || defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
-+#ifdef MBEDTLS_SSL_SESSION_TICKETS
-+#ifdef MBEDTLS_SSL_TICKET_C
-+#define TLS_MBEDTLS_SESSION_TICKETS
-+#if defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
-+#define TLS_MBEDTLS_EAP_TEAP
-+#endif
-+#if !defined(CONFIG_FIPS) /* EAP-FAST keys cannot be exported in FIPS mode */
-+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
-+#define TLS_MBEDTLS_EAP_FAST
-+#endif
-+#endif
-+#endif
-+#endif
-+#endif
-+
-+
-+struct tls_conf {
-+	mbedtls_ssl_config conf;
-+
-+	unsigned int verify_peer:1;
-+	unsigned int verify_depth0_only:1;
-+	unsigned int check_crl:2;           /*(needs :2 bits for 0, 1, 2)*/
-+	unsigned int check_crl_strict:1;    /*(needs :1 bit  for 0, 1)*/
-+	unsigned int ca_cert_probe:1;
-+	unsigned int has_ca_cert:1;
-+	unsigned int has_client_cert:1;
-+	unsigned int has_private_key:1;
-+	unsigned int suiteb128:1;
-+	unsigned int suiteb192:1;
-+	mbedtls_x509_crl *crl;
-+	mbedtls_x509_crt ca_cert;
-+	mbedtls_x509_crt client_cert;
-+	mbedtls_pk_context private_key;
-+
-+	uint32_t refcnt;
-+
-+	unsigned int flags;
-+	char *subject_match;
-+	char *altsubject_match;
-+	char *suffix_match;
-+	char *domain_match;
-+	char *check_cert_subject;
-+	u8 ca_cert_hash[SHA256_DIGEST_LENGTH];
-+
-+	int *ciphersuites;  /* list of ciphersuite ids for mbedtls_ssl_config */
-+#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
-+	mbedtls_ecp_group_id *curves;
-+#else
-+	uint16_t *curves;   /* list of curve ids for mbedtls_ssl_config */
-+#endif
-+};
-+
-+
-+struct tls_global {
-+	struct tls_conf *tls_conf;
-+	char *ocsp_stapling_response;
-+	mbedtls_ctr_drbg_context *ctr_drbg; /*(see crypto_mbedtls.c)*/
-+  #ifdef MBEDTLS_SSL_SESSION_TICKETS
-+	mbedtls_ssl_ticket_context ticket_ctx;
-+  #endif
-+	char *ca_cert_file;
-+	struct os_reltime crl_reload_previous;
-+	unsigned int crl_reload_interval;
-+	uint32_t refcnt;
-+	struct tls_config init_conf;
-+};
-+
-+static struct tls_global tls_ctx_global;
-+
-+
-+struct tls_connection {
-+	struct tls_conf *tls_conf;
-+	struct wpabuf *push_buf;
-+	struct wpabuf *pull_buf;
-+	size_t pull_buf_offset;
-+
-+	unsigned int established:1;
-+	unsigned int resumed:1;
-+	unsigned int verify_peer:1;
-+	unsigned int is_server:1;
-+
-+	mbedtls_ssl_context ssl;
-+
-+	mbedtls_tls_prf_types tls_prf_type;
-+	size_t expkey_keyblock_size;
-+	size_t expkey_secret_len;
-+  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+	unsigned char expkey_secret[MBEDTLS_EXPKEY_FIXED_SECRET_LEN];
-+  #else
-+	unsigned char expkey_secret[MBEDTLS_MD_MAX_SIZE];
-+  #endif
-+	unsigned char expkey_randbytes[MBEDTLS_EXPKEY_RAND_LEN*2];
-+
-+	int read_alerts, write_alerts, failed;
-+
-+  #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+	tls_session_ticket_cb session_ticket_cb;
-+	void *session_ticket_cb_ctx;
-+	unsigned char *clienthello_session_ticket;
-+	size_t clienthello_session_ticket_len;
-+  #endif
-+	char *peer_subject; /* peer subject info for authenticated peer */
-+	struct wpabuf *success_data;
-+};
-+
-+
-+#ifndef __has_attribute
-+#define __has_attribute(x) 0
-+#endif
-+
-+#ifndef __GNUC_PREREQ
-+#define __GNUC_PREREQ(maj,min) 0
-+#endif
-+
-+#ifndef __attribute_cold__
-+#if __has_attribute(cold) \
-+ || __GNUC_PREREQ(4,3)
-+#define __attribute_cold__  __attribute__((__cold__))
-+#else
-+#define __attribute_cold__
-+#endif
-+#endif
-+
-+#ifndef __attribute_noinline__
-+#if __has_attribute(noinline) \
-+ || __GNUC_PREREQ(3,1)
-+#define __attribute_noinline__  __attribute__((__noinline__))
-+#else
-+#define __attribute_noinline__
-+#endif
-+#endif
-+
-+
-+__attribute_cold__
-+__attribute_noinline__
-+static void emsg(int level, const char * const msg)
-+{
-+	wpa_printf(level, "MTLS: %s", msg);
-+}
-+
-+
-+__attribute_cold__
-+__attribute_noinline__
-+static void emsgrc(int level, const char * const msg, int rc)
-+{
-+  #ifdef MBEDTLS_ERROR_C
-+	/* error logging convenience function that decodes mbedtls result codes */
-+	char buf[256];
-+	mbedtls_strerror(rc, buf, sizeof(buf));
-+	wpa_printf(level, "MTLS: %s: %s (-0x%04x)", msg, buf, -rc);
-+  #else
-+	wpa_printf(level, "MTLS: %s: (-0x%04x)", msg, -rc);
-+  #endif
-+}
-+
-+
-+#define elog(rc, msg) emsgrc(MSG_ERROR, (msg), (rc))
-+#define ilog(rc, msg) emsgrc(MSG_INFO,  (msg), (rc))
-+
-+
-+struct tls_conf * tls_conf_init(void *tls_ctx)
-+{
-+	struct tls_conf *tls_conf = os_zalloc(sizeof(*tls_conf));
-+	if (tls_conf == NULL)
-+		return NULL;
-+	tls_conf->refcnt = 1;
-+
-+	mbedtls_ssl_config_init(&tls_conf->conf);
-+	mbedtls_ssl_conf_rng(&tls_conf->conf,
-+			     mbedtls_ctr_drbg_random, tls_ctx_global.ctr_drbg);
-+	mbedtls_x509_crt_init(&tls_conf->ca_cert);
-+	mbedtls_x509_crt_init(&tls_conf->client_cert);
-+	mbedtls_pk_init(&tls_conf->private_key);
-+
-+	return tls_conf;
-+}
-+
-+
-+void tls_conf_deinit(struct tls_conf *tls_conf)
-+{
-+	if (tls_conf == NULL || --tls_conf->refcnt != 0)
-+		return;
-+
-+	mbedtls_x509_crt_free(&tls_conf->ca_cert);
-+	mbedtls_x509_crt_free(&tls_conf->client_cert);
-+	if (tls_conf->crl) {
-+		mbedtls_x509_crl_free(tls_conf->crl);
-+		os_free(tls_conf->crl);
-+	}
-+	mbedtls_pk_free(&tls_conf->private_key);
-+	mbedtls_ssl_config_free(&tls_conf->conf);
-+	os_free(tls_conf->curves);
-+	os_free(tls_conf->ciphersuites);
-+	os_free(tls_conf->subject_match);
-+	os_free(tls_conf->altsubject_match);
-+	os_free(tls_conf->suffix_match);
-+	os_free(tls_conf->domain_match);
-+	os_free(tls_conf->check_cert_subject);
-+	os_free(tls_conf);
-+}
-+
-+
-+mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/
-+
-+__attribute_cold__
-+void * tls_init(const struct tls_config *conf)
-+{
-+	/* RFE: review struct tls_config *conf (different from tls_conf) */
-+
-+	if (++tls_ctx_global.refcnt > 1)
-+		return &tls_ctx_global;
-+
-+	tls_ctx_global.ctr_drbg = crypto_mbedtls_ctr_drbg();
-+  #ifdef MBEDTLS_SSL_SESSION_TICKETS
-+	mbedtls_ssl_ticket_init(&tls_ctx_global.ticket_ctx);
-+	mbedtls_ssl_ticket_setup(&tls_ctx_global.ticket_ctx,
-+	                         mbedtls_ctr_drbg_random,
-+	                         tls_ctx_global.ctr_drbg,
-+	                         MBEDTLS_CIPHER_AES_256_GCM,
-+	                         43200); /* ticket timeout: 12 hours */
-+  #endif
-+	/* copy struct for future use */
-+	tls_ctx_global.init_conf = *conf;
-+	if (conf->openssl_ciphers)
-+		tls_ctx_global.init_conf.openssl_ciphers =
-+		  os_strdup(conf->openssl_ciphers);
-+
-+	tls_ctx_global.crl_reload_interval = conf->crl_reload_interval;
-+	os_get_reltime(&tls_ctx_global.crl_reload_previous);
-+
-+	return &tls_ctx_global;
-+}
-+
-+
-+__attribute_cold__
-+void tls_deinit(void *tls_ctx)
-+{
-+	if (tls_ctx == NULL || --tls_ctx_global.refcnt != 0)
-+		return;
-+
-+	tls_conf_deinit(tls_ctx_global.tls_conf);
-+	os_free(tls_ctx_global.ca_cert_file);
-+	os_free(tls_ctx_global.ocsp_stapling_response);
-+	char *openssl_ciphers; /*(allocated in tls_init())*/
-+	*(const char **)&openssl_ciphers =
-+	  tls_ctx_global.init_conf.openssl_ciphers;
-+	os_free(openssl_ciphers);
-+  #ifdef MBEDTLS_SSL_SESSION_TICKETS
-+	mbedtls_ssl_ticket_free(&tls_ctx_global.ticket_ctx);
-+  #endif
-+	os_memset(&tls_ctx_global, 0, sizeof(tls_ctx_global));
-+}
-+
-+
-+int tls_get_errors(void *tls_ctx)
-+{
-+	return 0;
-+}
-+
-+
-+static void tls_connection_deinit_expkey(struct tls_connection *conn)
-+{
-+	conn->tls_prf_type = 0; /* MBEDTLS_SSL_TLS_PRF_NONE; */
-+	conn->expkey_keyblock_size = 0;
-+	conn->expkey_secret_len = 0;
-+	forced_memzero(conn->expkey_secret, sizeof(conn->expkey_secret));
-+	forced_memzero(conn->expkey_randbytes, sizeof(conn->expkey_randbytes));
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_SESSION_TICKETS
-+void tls_connection_deinit_clienthello_session_ticket(struct tls_connection *conn)
-+{
-+	if (conn->clienthello_session_ticket) {
-+		mbedtls_platform_zeroize(conn->clienthello_session_ticket,
-+		                         conn->clienthello_session_ticket_len);
-+		mbedtls_free(conn->clienthello_session_ticket);
-+		conn->clienthello_session_ticket = NULL;
-+		conn->clienthello_session_ticket_len = 0;
-+	}
-+}
-+#endif
-+
-+
-+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
-+{
-+	if (conn == NULL)
-+		return;
-+
-+  #if 0 /*(good intention, but never sent since we destroy self below)*/
-+	if (conn->established)
-+		mbedtls_ssl_close_notify(&conn->ssl);
-+  #endif
-+
-+	if (conn->tls_prf_type)
-+		tls_connection_deinit_expkey(conn);
-+
-+  #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+	if (conn->clienthello_session_ticket)
-+		tls_connection_deinit_clienthello_session_ticket(conn);
-+  #endif
-+
-+	os_free(conn->peer_subject);
-+	wpabuf_free(conn->success_data);
-+	wpabuf_free(conn->push_buf);
-+	wpabuf_free(conn->pull_buf);
-+	mbedtls_ssl_free(&conn->ssl);
-+	tls_conf_deinit(conn->tls_conf);
-+	os_free(conn);
-+}
-+
-+
-+static void tls_mbedtls_refresh_crl(void);
-+static int tls_mbedtls_ssl_setup(struct tls_connection *conn);
-+
-+struct tls_connection * tls_connection_init(void *tls_ctx)
-+{
-+	struct tls_connection *conn = os_zalloc(sizeof(*conn));
-+	if (conn == NULL)
-+		return NULL;
-+
-+	mbedtls_ssl_init(&conn->ssl);
-+
-+	conn->tls_conf = tls_ctx_global.tls_conf; /*(inherit global conf, if set)*/
-+	if (conn->tls_conf) {
-+		++conn->tls_conf->refcnt;
-+		/* check for CRL refresh if inheriting from global config */
-+		tls_mbedtls_refresh_crl();
-+
-+		conn->verify_peer = conn->tls_conf->verify_peer;
-+		if (tls_mbedtls_ssl_setup(conn) != 0) {
-+			tls_connection_deinit(&tls_ctx_global, conn);
-+			return NULL;
-+		}
-+	}
-+
-+	return conn;
-+}
-+
-+
-+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
-+{
-+	return conn ? conn->established : 0;
-+}
-+
-+
-+__attribute_noinline__
-+char * tls_mbedtls_peer_serial_num(const mbedtls_x509_crt *crt, char *serial_num, size_t len)
-+{
-+	/* mbedtls_x509_serial_gets() inefficiently formats to hex separated by
-+	 * colons, so generate the hex serial number here.  The func
-+	 * wpa_snprintf_hex_uppercase() is similarly inefficient. */
-+	size_t i = 0; /* skip leading 0's per Distinguished Encoding Rules (DER) */
-+	while (i < crt->serial.len && crt->serial.p[i] == 0) ++i;
-+	if (i == crt->serial.len) --i;
-+
-+	const unsigned char *s = crt->serial.p + i;
-+	const size_t e = (crt->serial.len - i) * 2;
-+	if (e >= len)
-+		return NULL;
-+  #if 0
-+	wpa_snprintf_hex_uppercase(serial_num, len, s, crt->serial.len-i);
-+  #else
-+	for (i = 0; i < e; i+=2, ++s) {
-+		serial_num[i+0] = "0123456789ABCDEF"[(*s >>  4)];
-+		serial_num[i+1] = "0123456789ABCDEF"[(*s & 0xF)];
-+	}
-+	serial_num[e] = '\0';
-+  #endif
-+	return serial_num;
-+}
-+
-+
-+char * tls_connection_peer_serial_num(void *tls_ctx,
-+				      struct tls_connection *conn)
-+{
-+	const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&conn->ssl);
-+	if (crt == NULL)
-+		return NULL;
-+	size_t len = crt->serial.len * 2 + 1;
-+	char *serial_num = os_malloc(len);
-+	if (!serial_num)
-+		return NULL;
-+	return tls_mbedtls_peer_serial_num(crt, serial_num, len);
-+}
-+
-+
-+static void tls_pull_buf_reset(struct tls_connection *conn);
-+
-+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
-+{
-+	/* Note: this function called from eap_peer_tls_reauth_init()
-+	 * for session resumption, not for connection shutdown */
-+
-+	if (conn == NULL)
-+		return -1;
-+
-+	tls_pull_buf_reset(conn);
-+	wpabuf_free(conn->push_buf);
-+	conn->push_buf = NULL;
-+	conn->established = 0;
-+	conn->resumed = 0;
-+	if (conn->tls_prf_type)
-+		tls_connection_deinit_expkey(conn);
-+
-+	/* RFE: prepare for session resumption? (see doc in crypto/tls.h) */
-+
-+	return mbedtls_ssl_session_reset(&conn->ssl);
-+}
-+
-+
-+static int tls_wpabuf_resize_put_data(struct wpabuf **buf,
-+                                      const unsigned char *data, size_t dlen)
-+{
-+	if (wpabuf_resize(buf, dlen) < 0)
-+		return 0;
-+	wpabuf_put_data(*buf, data, dlen);
-+	return 1;
-+}
-+
-+
-+static int tls_pull_buf_append(struct tls_connection *conn,
-+                               const struct wpabuf *in_data)
-+{
-+	/*(interface does not lend itself to move semantics)*/
-+	return tls_wpabuf_resize_put_data(&conn->pull_buf,
-+	                                  wpabuf_head(in_data),
-+	                                  wpabuf_len(in_data));
-+}
-+
-+
-+static void tls_pull_buf_reset(struct tls_connection *conn)
-+{
-+	/*(future: might consider reusing conn->pull_buf)*/
-+	wpabuf_free(conn->pull_buf);
-+	conn->pull_buf = NULL;
-+	conn->pull_buf_offset = 0;
-+}
-+
-+
-+__attribute_cold__
-+static void tls_pull_buf_discard(struct tls_connection *conn, const char *func)
-+{
-+	size_t discard = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
-+	if (discard)
-+		wpa_printf(MSG_DEBUG,
-+			   "%s - %zu bytes remaining in pull_buf; discarding",
-+			   func, discard);
-+	tls_pull_buf_reset(conn);
-+}
-+
-+
-+static int tls_pull_func(void *ptr, unsigned char *buf, size_t len)
-+{
-+	struct tls_connection *conn = (struct tls_connection *) ptr;
-+	if (conn->pull_buf == NULL)
-+		return MBEDTLS_ERR_SSL_WANT_READ;
-+	const size_t dlen = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
-+	if (dlen == 0)
-+		return MBEDTLS_ERR_SSL_WANT_READ;
-+
-+	if (len > dlen)
-+		len = dlen;
-+	os_memcpy(buf, wpabuf_head(conn->pull_buf)+conn->pull_buf_offset, len);
-+
-+	if (len == dlen) {
-+		tls_pull_buf_reset(conn);
-+		/*wpa_printf(MSG_DEBUG, "%s - emptied pull_buf", __func__);*/
-+	}
-+	else {
-+		conn->pull_buf_offset += len;
-+		/*wpa_printf(MSG_DEBUG, "%s - %zu bytes remaining in pull_buf",
-+			   __func__, dlen - len);*/
-+	}
-+	return (int)len;
-+}
-+
-+
-+static int tls_push_func(void *ptr, const unsigned char *buf, size_t len)
-+{
-+	struct tls_connection *conn = (struct tls_connection *) ptr;
-+	return tls_wpabuf_resize_put_data(&conn->push_buf, buf, len)
-+	  ? (int)len
-+	  : MBEDTLS_ERR_SSL_ALLOC_FAILED;
-+}
-+
-+
-+static int
-+tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags);
-+
-+
-+static int tls_mbedtls_ssl_setup(struct tls_connection *conn)
-+{
-+  #if 0
-+	/* mbedtls_ssl_setup() must be called only once */
-+	/* If this func might be called multiple times (e.g. via set_params),
-+	 * then we should set a flag in conn that ssl was initialized */
-+	if (conn->ssl_is_init) {
-+		mbedtls_ssl_free(&conn->ssl);
-+		mbedtls_ssl_init(&conn->ssl);
-+	}
-+  #endif
-+
-+	int ret = mbedtls_ssl_setup(&conn->ssl, &conn->tls_conf->conf);
-+	if (ret != 0) {
-+		elog(ret, "mbedtls_ssl_setup");
-+		return -1;
-+	}
-+
-+	mbedtls_ssl_set_bio(&conn->ssl, conn, tls_push_func, tls_pull_func, NULL);
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+	mbedtls_ssl_set_export_keys_cb(
-+	    &conn->ssl, tls_connection_export_keys_cb, conn);
-+  #elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+	mbedtls_ssl_conf_export_keys_ext_cb(
-+	    &conn->tls_conf->conf, tls_connection_export_keys_cb, conn);
-+  #endif
-+	if (conn->verify_peer)
-+		mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
-+
-+	return 0;
-+}
-+
-+
-+static int tls_mbedtls_data_is_pem(const u8 *data)
-+{
-+    return (NULL != os_strstr((char *)data, "-----"));
-+}
-+
-+
-+static void tls_mbedtls_set_allowed_tls_vers(struct tls_conf *tls_conf,
-+                                             mbedtls_ssl_config *conf)
-+{
-+  #if !defined(MBEDTLS_SSL_PROTO_TLS1_3)
-+	tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_3;
-+  #endif
-+
-+	/* unconditionally require TLSv1.2+ for TLS_CONN_SUITEB */
-+	if (tls_conf->flags & TLS_CONN_SUITEB) {
-+		tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_0;
-+		tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_1;
-+	}
-+
-+	const unsigned int flags = tls_conf->flags;
-+
-+	/* attempt to map flags to min and max TLS protocol version */
-+
-+	int min = (flags & TLS_CONN_DISABLE_TLSv1_0)
-+		? (flags & TLS_CONN_DISABLE_TLSv1_1)
-+		? (flags & TLS_CONN_DISABLE_TLSv1_2)
-+		? (flags & TLS_CONN_DISABLE_TLSv1_3)
-+		? 4
-+		: 3
-+		: 2
-+		: 1
-+		: 0;
-+
-+	int max = (flags & TLS_CONN_DISABLE_TLSv1_3)
-+		? (flags & TLS_CONN_DISABLE_TLSv1_2)
-+		? (flags & TLS_CONN_DISABLE_TLSv1_1)
-+		? (flags & TLS_CONN_DISABLE_TLSv1_0)
-+		? -1
-+		: 0
-+		: 1
-+		: 2
-+		: 3;
-+
-+	if ((flags & TLS_CONN_ENABLE_TLSv1_2) && min > 2) min = 2;
-+	if ((flags & TLS_CONN_ENABLE_TLSv1_1) && min > 1) min = 1;
-+	if ((flags & TLS_CONN_ENABLE_TLSv1_0) && min > 0) min = 0;
-+	if (max < min) {
-+		emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
-+		return;
-+	}
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+	/* mbed TLS 3.0.0 removes support for protocols < TLSv1.2 */
-+	if (min < 2 || max < 2) {
-+		emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
-+		if (min < 2) min = 2;
-+		if (max < 2) max = 2;
-+	}
-+  #endif
-+
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
-+	/* MBEDTLS_SSL_VERSION_TLS1_2 = 0x0303 *//*!< (D)TLS 1.2 */
-+	/* MBEDTLS_SSL_VERSION_TLS1_3 = 0x0304 *//*!< (D)TLS 1.3 */
-+	min = (min == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
-+	max = (max == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
-+	mbedtls_ssl_conf_min_tls_version(conf, min);
-+	mbedtls_ssl_conf_max_tls_version(conf, max);
-+  #else
-+   #ifndef MBEDTLS_SSL_MINOR_VERSION_4
-+	if (min == 3) min = 2;
-+	if (max == 3) max = 2;
-+   #endif
-+	/* MBEDTLS_SSL_MINOR_VERSION_0  0 *//*!< SSL v3.0 */
-+	/* MBEDTLS_SSL_MINOR_VERSION_1  1 *//*!< TLS v1.0 */
-+	/* MBEDTLS_SSL_MINOR_VERSION_2  2 *//*!< TLS v1.1 */
-+	/* MBEDTLS_SSL_MINOR_VERSION_3  3 *//*!< TLS v1.2 */
-+	/* MBEDTLS_SSL_MINOR_VERSION_4  4 *//*!< TLS v1.3 */
-+	mbedtls_ssl_conf_min_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, min+1);
-+	mbedtls_ssl_conf_max_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, max+1);
-+  #endif
-+}
-+
-+
-+__attribute_noinline__
-+static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n);
-+
-+
-+static int
-+tls_mbedtls_set_dhparams(struct tls_conf *tls_conf, const char *dh_file)
-+{
-+    size_t len;
-+    u8 *data;
-+    if (tls_mbedtls_readfile(dh_file, &data, &len))
-+        return 0;
-+
-+    /* parse only if DH parameters if in PEM format */
-+    if (tls_mbedtls_data_is_pem(data)
-+        && NULL == os_strstr((char *)data, "-----BEGIN DH PARAMETERS-----")) {
-+        if (os_strstr((char *)data, "-----BEGIN DSA PARAMETERS-----"))
-+            wpa_printf(MSG_WARNING, "DSA parameters not handled (%s)", dh_file);
-+        else
-+            wpa_printf(MSG_WARNING, "unexpected DH param content (%s)",dh_file);
-+        forced_memzero(data, len);
-+        os_free(data);
-+        return 0;
-+    }
-+
-+    /* mbedtls_dhm_parse_dhm() expects "-----BEGIN DH PARAMETERS-----" if PEM */
-+    mbedtls_dhm_context dhm;
-+    mbedtls_dhm_init(&dhm);
-+    int rc = mbedtls_dhm_parse_dhm(&dhm, data, len);
-+    if (0 == rc)
-+        rc = mbedtls_ssl_conf_dh_param_ctx(&tls_conf->conf, &dhm);
-+    if (0 != rc)
-+        elog(rc, dh_file);
-+    mbedtls_dhm_free(&dhm);
-+
-+    forced_memzero(data, len);
-+    os_free(data);
-+    return (0 == rc);
-+}
-+
-+
-+/* reference: lighttpd src/mod_mbedtls.c:mod_mbedtls_ssl_append_curve()
-+ * (same author: gstrauss@gluelogic.com; same license: BSD-3-Clause) */
-+#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
-+static int
-+tls_mbedtls_append_curve (mbedtls_ecp_group_id *ids, int nids, int idsz, const mbedtls_ecp_group_id id)
-+{
-+    if (1 >= idsz - (nids + 1)) {
-+        emsg(MSG_ERROR, "error: too many curves during list expand");
-+        return -1;
-+    }
-+    ids[++nids] = id;
-+    return nids;
-+}
-+
-+
-+static int
-+tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
-+{
-+    mbedtls_ecp_group_id ids[512];
-+    int nids = -1;
-+    const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
-+    const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
-+
-+    for (const char *e = curvelist-1; e; ) {
-+        const char * const n = e+1;
-+        e = os_strchr(n, ':');
-+        size_t len = e ? (size_t)(e - n) : os_strlen(n);
-+        mbedtls_ecp_group_id grp_id = MBEDTLS_ECP_DP_NONE;
-+        switch (len) {
-+          case 5:
-+            if (0 == os_memcmp("P-521", n, 5))
-+                grp_id = MBEDTLS_ECP_DP_SECP521R1;
-+            else if (0 == os_memcmp("P-384", n, 5))
-+                grp_id = MBEDTLS_ECP_DP_SECP384R1;
-+            else if (0 == os_memcmp("P-256", n, 5))
-+                grp_id = MBEDTLS_ECP_DP_SECP256R1;
-+            break;
-+          case 6:
-+            if (0 == os_memcmp("BP-521", n, 6))
-+                grp_id = MBEDTLS_ECP_DP_BP512R1;
-+            else if (0 == os_memcmp("BP-384", n, 6))
-+                grp_id = MBEDTLS_ECP_DP_BP384R1;
-+            else if (0 == os_memcmp("BP-256", n, 6))
-+                grp_id = MBEDTLS_ECP_DP_BP256R1;
-+            break;
-+          default:
-+            break;
-+        }
-+        if (grp_id != MBEDTLS_ECP_DP_NONE) {
-+            nids = tls_mbedtls_append_curve(ids, nids, idsz, grp_id);
-+            if (-1 == nids) return 0;
-+            continue;
-+        }
-+        /* similar to mbedtls_ecp_curve_info_from_name() */
-+        const mbedtls_ecp_curve_info *info;
-+        for (info = curve_info; info->grp_id != MBEDTLS_ECP_DP_NONE; ++info) {
-+            if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
-+                break;
-+        }
-+        if (info->grp_id == MBEDTLS_ECP_DP_NONE) {
-+            wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
-+            return 0;
-+        }
-+
-+        nids = tls_mbedtls_append_curve(ids, nids, idsz, info->grp_id);
-+        if (-1 == nids) return 0;
-+    }
-+
-+    /* mod_openssl configures "prime256v1" if curve list not specified,
-+     * but mbedtls provides a list of supported curves if not explicitly set */
-+    if (-1 == nids) return 1; /* empty list; no-op */
-+
-+    ids[++nids] = MBEDTLS_ECP_DP_NONE; /* terminate list */
-+    ++nids;
-+
-+    /* curves list must be persistent for lifetime of mbedtls_ssl_config */
-+    tls_conf->curves = os_malloc(nids * sizeof(mbedtls_ecp_group_id));
-+    if (tls_conf->curves == NULL)
-+        return 0;
-+    os_memcpy(tls_conf->curves, ids, nids * sizeof(mbedtls_ecp_group_id));
-+
-+    mbedtls_ssl_conf_curves(&tls_conf->conf, tls_conf->curves);
-+    return 1;
-+}
-+#else
-+static int
-+tls_mbedtls_append_curve (uint16_t *ids, int nids, int idsz, const uint16_t id)
-+{
-+    if (1 >= idsz - (nids + 1)) {
-+        emsg(MSG_ERROR, "error: too many curves during list expand");
-+        return -1;
-+    }
-+    ids[++nids] = id;
-+    return nids;
-+}
-+
-+
-+static int
-+tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
-+{
-+    /* TLS Supported Groups (renamed from "EC Named Curve Registry")
-+     * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
-+     */
-+    uint16_t ids[512];
-+    int nids = -1;
-+    const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
-+    const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
-+
-+    for (const char *e = curvelist-1; e; ) {
-+        const char * const n = e+1;
-+        e = os_strchr(n, ':');
-+        size_t len = e ? (size_t)(e - n) : os_strlen(n);
-+        uint16_t tls_id = 0;
-+        switch (len) {
-+          case 5:
-+            if (0 == os_memcmp("P-521", n, 5))
-+                tls_id = 25; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP521R1 */
-+            else if (0 == os_memcmp("P-384", n, 5))
-+                tls_id = 24; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP384R1 */
-+            else if (0 == os_memcmp("P-256", n, 5))
-+                tls_id = 23; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP256R1 */
-+            break;
-+          case 6:
-+            if (0 == os_memcmp("BP-521", n, 6))
-+                tls_id = 28; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP512R1 */
-+            else if (0 == os_memcmp("BP-384", n, 6))
-+                tls_id = 27; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP384R1 */
-+            else if (0 == os_memcmp("BP-256", n, 6))
-+                tls_id = 26; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP256R1 */
-+            break;
-+          default:
-+            break;
-+        }
-+        if (tls_id != 0) {
-+            nids = tls_mbedtls_append_curve(ids, nids, idsz, tls_id);
-+            if (-1 == nids) return 0;
-+            continue;
-+        }
-+        /* similar to mbedtls_ecp_curve_info_from_name() */
-+        const mbedtls_ecp_curve_info *info;
-+        for (info = curve_info; info->tls_id != 0; ++info) {
-+            if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
-+                break;
-+        }
-+        if (info->tls_id == 0) {
-+            wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
-+            return 0;
-+        }
-+
-+        nids = tls_mbedtls_append_curve(ids, nids, idsz, info->tls_id);
-+        if (-1 == nids) return 0;
-+    }
-+
-+    /* mod_openssl configures "prime256v1" if curve list not specified,
-+     * but mbedtls provides a list of supported curves if not explicitly set */
-+    if (-1 == nids) return 1; /* empty list; no-op */
-+
-+    ids[++nids] = 0; /* terminate list */
-+    ++nids;
-+
-+    /* curves list must be persistent for lifetime of mbedtls_ssl_config */
-+    tls_conf->curves = os_malloc(nids * sizeof(uint16_t));
-+    if (tls_conf->curves == NULL)
-+        return 0;
-+    os_memcpy(tls_conf->curves, ids, nids * sizeof(uint16_t));
-+
-+    mbedtls_ssl_conf_groups(&tls_conf->conf, tls_conf->curves);
-+    return 1;
-+}
-+#endif /* MBEDTLS_VERSION_NUMBER >= 0x03010000 */ /* mbedtls 3.1.0 */
-+
-+
-+/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
-+static const int suite_AES_256_ephemeral[] = {
-+    /* All AES-256 ephemeral suites */
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8
-+};
-+
-+/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
-+static const int suite_AES_128_ephemeral[] = {
-+    /* All AES-128 ephemeral suites */
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8
-+};
-+
-+/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
-+/* HIGH cipher list (mapped from openssl list to mbedtls) */
-+static const int suite_HIGH[] = {
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
-+    MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384,
-+    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256,
-+    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384,
-+    MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
-+    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
-+    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256,
-+    MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256,
-+    MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
-+    MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM,
-+    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
-+    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
-+    MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM_8,
-+    MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM,
-+    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
-+    MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM_8,
-+    MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256,
-+    MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_RSA_WITH_AES_256_CCM,
-+    MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256,
-+    MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_RSA_WITH_AES_256_CCM_8,
-+    MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256,
-+    MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
-+    MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384,
-+    MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_RSA_WITH_AES_128_CCM,
-+    MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_RSA_WITH_AES_128_CCM_8,
-+    MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256,
-+    MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
-+    MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256,
-+    MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256,
-+    MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
-+    MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384,
-+    MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384,
-+    MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256,
-+    MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256,
-+    MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256,
-+    MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_PSK_WITH_AES_256_CCM,
-+    MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384,
-+    MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384,
-+    MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8,
-+    MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384,
-+    MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_PSK_WITH_AES_128_CCM,
-+    MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256,
-+    MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8,
-+    MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256
-+};
-+
-+
-+__attribute_noinline__
-+static int
-+tls_mbedtls_append_ciphersuite (int *ids, int nids, int idsz, const int *x, int xsz)
-+{
-+    if (xsz >= idsz - (nids + 1)) {
-+        emsg(MSG_ERROR, "error: too many ciphers during list expand");
-+        return -1;
-+    }
-+
-+    for (int i = 0; i < xsz; ++i)
-+        ids[++nids] = x[i];
-+
-+    return nids;
-+}
-+
-+
-+static int
-+tls_mbedtls_translate_ciphername(int id, char *buf, size_t buflen)
-+{
-+    const mbedtls_ssl_ciphersuite_t *info =
-+      mbedtls_ssl_ciphersuite_from_id(id);
-+    if (info == NULL)
-+        return 0;
-+    const char *name = mbedtls_ssl_ciphersuite_get_name(info);
-+    const size_t len = os_strlen(name);
-+    if (len == 7 && 0 == os_memcmp(name, "unknown", 7))
-+        return 0;
-+    if (len >= buflen)
-+        return 0;
-+    os_strlcpy(buf, name, buflen);
-+
-+    /* attempt to translate mbedtls string to openssl string
-+     * (some heuristics; incomplete) */
-+    size_t i = 0, j = 0;
-+    if (buf[0] == 'T') {
-+        if (os_strncmp(buf, "TLS1-3-", 7) == 0) {
-+            buf[3] = '-';
-+            j = 4; /* remove "1-3" from "TLS1-3-" prefix */
-+            i = 7;
-+        }
-+        else if (os_strncmp(buf, "TLS-", 4) == 0)
-+            i = 4; /* remove "TLS-" prefix */
-+    }
-+    for (; buf[i]; ++i) {
-+        if (buf[i] == '-') {
-+            if (i >= 3) {
-+                if (0 == os_memcmp(buf+i-3, "AES", 3))
-+                    continue; /* "AES-" -> "AES" */
-+            }
-+            if (i >= 4) {
-+                if (0 == os_memcmp(buf+i-4, "WITH", 4)) {
-+                    j -= 4;   /* remove "WITH-" */
-+                    continue;
-+                }
-+            }
-+        }
-+        buf[j++] = buf[i];
-+    }
-+    buf[j] = '\0';
-+
-+    return j;
-+}
-+
-+
-+__attribute_noinline__
-+static int
-+tls_mbedtls_set_ciphersuites(struct tls_conf *tls_conf, int *ids, int nids)
-+{
-+    /* ciphersuites list must be persistent for lifetime of mbedtls_ssl_config*/
-+    os_free(tls_conf->ciphersuites);
-+    tls_conf->ciphersuites = os_malloc(nids * sizeof(int));
-+    if (tls_conf->ciphersuites == NULL)
-+        return 0;
-+    os_memcpy(tls_conf->ciphersuites, ids, nids * sizeof(int));
-+    mbedtls_ssl_conf_ciphersuites(&tls_conf->conf, tls_conf->ciphersuites);
-+    return 1;
-+}
-+
-+
-+static int
-+tls_mbedtls_set_ciphers(struct tls_conf *tls_conf, const char *ciphers)
-+{
-+    char buf[64];
-+    int ids[512];
-+    int nids = -1;
-+    const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
-+    const char *next;
-+    size_t blen, clen;
-+    do {
-+        next = os_strchr(ciphers, ':');
-+        clen = next ? (size_t)(next - ciphers) : os_strlen(ciphers);
-+        if (!clen)
-+            continue;
-+
-+        /* special-case a select set of openssl group names for hwsim tests */
-+	/* (review; remove excess code if tests are not run for non-OpenSSL?) */
-+        if (clen == 9 && os_memcmp(ciphers, "SUITEB192", 9) == 0) {
-+            static int ssl_preset_suiteb192_ciphersuites[] = {
-+                MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
-+                0
-+            };
-+            return tls_mbedtls_set_ciphersuites(tls_conf,
-+                                                ssl_preset_suiteb192_ciphersuites,
-+                                                2);
-+        }
-+        if (clen == 9 && os_memcmp(ciphers, "SUITEB128", 9) == 0) {
-+            static int ssl_preset_suiteb128_ciphersuites[] = {
-+                MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-+                0
-+            };
-+            return tls_mbedtls_set_ciphersuites(tls_conf,
-+                                                ssl_preset_suiteb128_ciphersuites,
-+                                                2);
-+        }
-+        if (clen == 7 && os_memcmp(ciphers, "DEFAULT", 7) == 0)
-+            continue;
-+        if (clen == 6 && os_memcmp(ciphers, "AES128", 6) == 0) {
-+            nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
-+                     suite_AES_128_ephemeral,
-+                     (int)ARRAY_SIZE(suite_AES_128_ephemeral));
-+            if (nids == -1)
-+                return 0;
-+            continue;
-+        }
-+        if (clen == 6 && os_memcmp(ciphers, "AES256", 6) == 0) {
-+            nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
-+                     suite_AES_256_ephemeral,
-+                     (int)ARRAY_SIZE(suite_AES_256_ephemeral));
-+            if (nids == -1)
-+                return 0;
-+            continue;
-+        }
-+        if (clen == 4 && os_memcmp(ciphers, "HIGH", 4) == 0) {
-+            nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz, suite_HIGH,
-+                                                  (int)ARRAY_SIZE(suite_HIGH));
-+            if (nids == -1)
-+                return 0;
-+            continue;
-+        }
-+        /* ignore anonymous cipher group names (?not supported by mbedtls?) */
-+        if (clen == 4 && os_memcmp(ciphers, "!ADH", 4) == 0)
-+            continue;
-+        if (clen == 6 && os_memcmp(ciphers, "-aECDH", 6) == 0)
-+            continue;
-+        if (clen == 7 && os_memcmp(ciphers, "-aECDSA", 7) == 0)
-+            continue;
-+
-+        /* attempt to match mbedtls cipher names
-+         * nb: does not support openssl group names or list manipulation syntax
-+         *   (alt: could copy almost 1200 lines (!!!) of lighttpd mod_mbedtls.c
-+         *    mod_mbedtls_ssl_conf_ciphersuites() to translate strings)
-+         * note: not efficient to rewrite list for each ciphers entry,
-+         *       but this code is expected to run only at startup
-+         */
-+        const int *list = mbedtls_ssl_list_ciphersuites();
-+        for (; *list; ++list) {
-+            blen = tls_mbedtls_translate_ciphername(*list,buf,sizeof(buf));
-+            if (!blen)
-+                continue;
-+
-+            /* matching heuristics additional to translate_ciphername above */
-+            if (blen == clen+4) {
-+                char *cbc = os_strstr(buf, "CBC-");
-+                if (cbc) {
-+                    os_memmove(cbc, cbc+4, blen-(cbc+4-buf)+1); /*(w/ '\0')*/
-+                    blen -= 4;
-+                }
-+            }
-+            if (blen >= clen && os_memcmp(ciphers, buf, clen) == 0
-+                && (blen == clen
-+                    || (blen == clen+7 && os_memcmp(buf+clen, "-SHA256", 7)))) {
-+                if (1 >= idsz - (nids + 1)) {
-+                    emsg(MSG_ERROR,
-+                         "error: too many ciphers during list expand");
-+                    return 0;
-+                }
-+                ids[++nids] = *list;
-+                break;
-+            }
-+        }
-+        if (*list == 0) {
-+            wpa_printf(MSG_ERROR,
-+                       "MTLS: unrecognized cipher: %.*s", (int)clen, ciphers);
-+            return 0;
-+        }
-+    } while ((ciphers = next ? next+1 : NULL));
-+
-+    if (-1 == nids) return 1; /* empty list; no-op */
-+
-+    ids[++nids] = 0; /* terminate list */
-+    ++nids;
-+
-+    return tls_mbedtls_set_ciphersuites(tls_conf, ids, nids);
-+}
-+
-+
-+__attribute_noinline__
-+static int tls_mbedtls_set_item(char **config_item, const char *item)
-+{
-+	os_free(*config_item);
-+	*config_item = NULL;
-+	return item ? (*config_item = os_strdup(item)) != NULL : 1;
-+}
-+
-+
-+static int tls_connection_set_subject_match(struct tls_conf *tls_conf,
-+                                            const struct tls_connection_params *params)
-+{
-+	int rc = 1;
-+	rc &= tls_mbedtls_set_item(&tls_conf->subject_match,
-+	                              params->subject_match);
-+	rc &= tls_mbedtls_set_item(&tls_conf->altsubject_match,
-+	                              params->altsubject_match);
-+	rc &= tls_mbedtls_set_item(&tls_conf->suffix_match,
-+	                              params->suffix_match);
-+	rc &= tls_mbedtls_set_item(&tls_conf->domain_match,
-+	                              params->domain_match);
-+	rc &= tls_mbedtls_set_item(&tls_conf->check_cert_subject,
-+	                              params->check_cert_subject);
-+	return rc;
-+}
-+
-+
-+/* duplicated in crypto_mbedtls.c:crypto_mbedtls_readfile()*/
-+__attribute_noinline__
-+static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n)
-+{
-+  #if 0 /* #ifdef MBEDTLS_FS_IO */
-+	/*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/
-+	if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) {
-+		wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path);
-+		return -1;
-+	}
-+  #else
-+	/*(use os_readfile() so that we can use os_free()
-+	 *(if we use mbedtls_pk_load_file() above, macros prevent calling free()
-+	 * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free()
-+	 * on buf aborts in tests if buf not allocated via os_malloc())*/
-+	*buf = (u8 *)os_readfile(path, n);
-+	if (!*buf) {
-+		wpa_printf(MSG_ERROR, "error: os_readfile %s", path);
-+		return -1;
-+	}
-+	u8 *buf0 = os_realloc(*buf, *n+1);
-+	if (!buf0) {
-+		bin_clear_free(*buf, *n);
-+		*buf = NULL;
-+		return -1;
-+	}
-+	buf0[(*n)++] = '\0';
-+	*buf = buf0;
-+  #endif
-+	return 0;
-+}
-+
-+
-+static int tls_mbedtls_set_crl(struct tls_conf *tls_conf, const u8 *data, size_t len)
-+{
-+	/* do not use mbedtls_x509_crl_parse() on PEM unless it contains CRL */
-+	if (len && data[len-1] == '\0'
-+	    && NULL == os_strstr((const char *)data,"-----BEGIN X509 CRL-----")
-+	    && tls_mbedtls_data_is_pem(data))
-+		return 0;
-+
-+	mbedtls_x509_crl crl;
-+	mbedtls_x509_crl_init(&crl);
-+	int rc = mbedtls_x509_crl_parse(&crl, data, len);
-+	if (rc < 0) {
-+		mbedtls_x509_crl_free(&crl);
-+		return rc == MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT ? 0 : rc;
-+	}
-+
-+	mbedtls_x509_crl *crl_new = os_malloc(sizeof(crl));
-+	if (crl_new == NULL) {
-+		mbedtls_x509_crl_free(&crl);
-+		return MBEDTLS_ERR_X509_ALLOC_FAILED;
-+	}
-+	os_memcpy(crl_new, &crl, sizeof(crl));
-+
-+	mbedtls_x509_crl *crl_old = tls_conf->crl;
-+	tls_conf->crl = crl_new;
-+	if (crl_old) {
-+		mbedtls_x509_crl_free(crl_old);
-+		os_free(crl_old);
-+	}
-+	return 0;
-+}
-+
-+
-+static int tls_mbedtls_set_ca(struct tls_conf *tls_conf, u8 *data, size_t len)
-+{
-+	/* load crt struct onto stack and then copy into tls_conf in
-+	 * order to preserve existing tls_conf value if error occurs
-+	 *
-+	 * hostapd is not threaded, or else should allocate memory and swap in
-+	 * pointer reduce race condition.  (If threaded, would also need to
-+	 * keep reference count of use to avoid freeing while still in use.) */
-+
-+	mbedtls_x509_crt crt;
-+	mbedtls_x509_crt_init(&crt);
-+	int rc = mbedtls_x509_crt_parse(&crt, data, len);
-+	if (rc < 0) {
-+		mbedtls_x509_crt_free(&crt);
-+		return rc;
-+	}
-+
-+	mbedtls_x509_crt_free(&tls_conf->ca_cert);
-+	os_memcpy(&tls_conf->ca_cert, &crt, sizeof(crt));
-+	return 0;
-+}
-+
-+
-+static int tls_mbedtls_set_ca_and_crl(struct tls_conf *tls_conf, const char *ca_cert_file)
-+{
-+	size_t len;
-+	u8 *data;
-+	if (tls_mbedtls_readfile(ca_cert_file, &data, &len))
-+		return -1;
-+
-+	int rc;
-+	if (0 == (rc = tls_mbedtls_set_ca(tls_conf, data, len))
-+	    && (!tls_mbedtls_data_is_pem(data) /*skip parse for CRL if not PEM*/
-+	        || 0 == (rc = tls_mbedtls_set_crl(tls_conf, data, len)))) {
-+		mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
-+		                          &tls_conf->ca_cert,
-+		                          tls_conf->crl);
-+	}
-+	else {
-+		elog(rc, __func__);
-+		emsg(MSG_ERROR, ca_cert_file);
-+	}
-+
-+	forced_memzero(data, len);
-+	os_free(data);
-+	return rc;
-+}
-+
-+
-+static void tls_mbedtls_refresh_crl(void)
-+{
-+	/* check for CRL refresh
-+	 * continue even if error occurs; continue with previous cert, CRL */
-+	unsigned int crl_reload_interval = tls_ctx_global.crl_reload_interval;
-+	const char *ca_cert_file = tls_ctx_global.ca_cert_file;
-+	if (!crl_reload_interval || !ca_cert_file)
-+		return;
-+
-+	struct os_reltime *previous = &tls_ctx_global.crl_reload_previous;
-+	struct os_reltime now;
-+	if (os_get_reltime(&now) != 0
-+	    || !os_reltime_expired(&now, previous, crl_reload_interval))
-+		return;
-+
-+	/* Note: modifying global state is not thread-safe
-+	 *       if in use by existing connections
-+	 *
-+	 * src/utils/os.h does not provide a portable stat()
-+	 * or else it would be a good idea to check mtime and size,
-+	 * and avoid reloading if file has not changed */
-+
-+	if (tls_mbedtls_set_ca_and_crl(tls_ctx_global.tls_conf, ca_cert_file) == 0)
-+		*previous = now;
-+}
-+
-+
-+static int tls_mbedtls_set_ca_cert(struct tls_conf *tls_conf,
-+				   const struct tls_connection_params *params)
-+{
-+	if (params->ca_cert) {
-+		if (os_strncmp(params->ca_cert, "probe://", 8) == 0) {
-+			tls_conf->ca_cert_probe = 1;
-+			tls_conf->has_ca_cert = 1;
-+			return 0;
-+		}
-+
-+		if (os_strncmp(params->ca_cert, "hash://", 7) == 0) {
-+			const char *pos = params->ca_cert + 7;
-+			if (os_strncmp(pos, "server/sha256/", 14) != 0) {
-+				emsg(MSG_ERROR, "unsupported ca_cert hash value");
-+				return -1;
-+			}
-+			pos += 14;
-+			if (os_strlen(pos) != SHA256_DIGEST_LENGTH*2) {
-+				emsg(MSG_ERROR, "unexpected ca_cert hash length");
-+				return -1;
-+			}
-+			if (hexstr2bin(pos, tls_conf->ca_cert_hash,
-+			               SHA256_DIGEST_LENGTH) < 0) {
-+				emsg(MSG_ERROR, "invalid ca_cert hash value");
-+				return -1;
-+			}
-+			emsg(MSG_DEBUG, "checking only server certificate match");
-+			tls_conf->verify_depth0_only = 1;
-+			tls_conf->has_ca_cert = 1;
-+			return 0;
-+		}
-+
-+		if (tls_mbedtls_set_ca_and_crl(tls_conf, params->ca_cert) != 0)
-+			return -1;
-+	}
-+	if (params->ca_cert_blob) {
-+		size_t len = params->ca_cert_blob_len;
-+		int is_pem = tls_mbedtls_data_is_pem(params->ca_cert_blob);
-+		if (len && params->ca_cert_blob[len-1] != '\0' && is_pem)
-+			++len; /*(include '\0' in len for PEM)*/
-+		int ret = mbedtls_x509_crt_parse(&tls_conf->ca_cert,
-+		                                 params->ca_cert_blob, len);
-+		if (ret != 0) {
-+			elog(ret, "mbedtls_x509_crt_parse");
-+			return -1;
-+		}
-+		if (is_pem) { /*(ca_cert_blob in DER format contains ca cert only)*/
-+			ret = tls_mbedtls_set_crl(tls_conf, params->ca_cert_blob, len);
-+			if (ret != 0) {
-+				elog(ret, "mbedtls_x509_crl_parse");
-+				return -1;
-+			}
-+		}
-+	}
-+
-+	if (mbedtls_x509_time_is_future(&tls_conf->ca_cert.valid_from)
-+	    || mbedtls_x509_time_is_past(&tls_conf->ca_cert.valid_to)) {
-+		emsg(MSG_WARNING, "ca_cert expired or not yet valid");
-+		if (params->ca_cert)
-+			emsg(MSG_WARNING, params->ca_cert);
-+	}
-+
-+	tls_conf->has_ca_cert = 1;
-+	return 0;
-+}
-+
-+
-+static int tls_mbedtls_set_certs(struct tls_conf *tls_conf,
-+				 const struct tls_connection_params *params)
-+{
-+	int ret;
-+
-+	if (params->ca_cert || params->ca_cert_blob) {
-+		if (tls_mbedtls_set_ca_cert(tls_conf, params) != 0)
-+			return -1;
-+	}
-+	else if (params->ca_path) {
-+		emsg(MSG_INFO, "ca_path support not implemented");
-+		return -1;
-+	}
-+
-+	if (!tls_conf->has_ca_cert)
-+		mbedtls_ssl_conf_authmode(&tls_conf->conf, MBEDTLS_SSL_VERIFY_NONE);
-+	else {
-+		/* Initial setting: REQUIRED for client, OPTIONAL for server
-+		 *   (see also tls_connection_set_verify()) */
-+		tls_conf->verify_peer = (tls_ctx_global.tls_conf == NULL);
-+		int authmode = tls_conf->verify_peer
-+		  ? MBEDTLS_SSL_VERIFY_REQUIRED
-+		  : MBEDTLS_SSL_VERIFY_OPTIONAL;
-+		mbedtls_ssl_conf_authmode(&tls_conf->conf, authmode);
-+		mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
-+		                          &tls_conf->ca_cert,
-+		                          tls_conf->crl);
-+
-+		if (!tls_connection_set_subject_match(tls_conf, params))
-+			return -1;
-+	}
-+
-+	if (params->client_cert2) /*(yes, server_cert2 in msg below)*/
-+		emsg(MSG_INFO, "server_cert2 support not implemented");
-+
-+	if (params->client_cert) {
-+		size_t len;
-+		u8 *data;
-+		if (tls_mbedtls_readfile(params->client_cert, &data, &len))
-+			return -1;
-+		ret = mbedtls_x509_crt_parse(&tls_conf->client_cert, data, len);
-+		forced_memzero(data, len);
-+		os_free(data);
-+	}
-+	if (params->client_cert_blob) {
-+		size_t len = params->client_cert_blob_len;
-+		if (len && params->client_cert_blob[len-1] != '\0'
-+		    && tls_mbedtls_data_is_pem(params->client_cert_blob))
-+			++len; /*(include '\0' in len for PEM)*/
-+		ret = mbedtls_x509_crt_parse(&tls_conf->client_cert,
-+		                             params->client_cert_blob, len);
-+	}
-+	if (params->client_cert || params->client_cert_blob) {
-+		if (ret < 0) {
-+			elog(ret, "mbedtls_x509_crt_parse");
-+			if (params->client_cert)
-+				emsg(MSG_ERROR, params->client_cert);
-+			return -1;
-+		}
-+		if (mbedtls_x509_time_is_future(&tls_conf->client_cert.valid_from)
-+		    || mbedtls_x509_time_is_past(&tls_conf->client_cert.valid_to)) {
-+			emsg(MSG_WARNING, "cert expired or not yet valid");
-+			if (params->client_cert)
-+				emsg(MSG_WARNING, params->client_cert);
-+		}
-+		tls_conf->has_client_cert = 1;
-+	}
-+
-+	if (params->private_key || params->private_key_blob) {
-+		size_t len = params->private_key_blob_len;
-+		u8 *data;
-+		*(const u8 **)&data = params->private_key_blob;
-+		if (len && data[len-1] != '\0' && tls_mbedtls_data_is_pem(data))
-+			++len; /*(include '\0' in len for PEM)*/
-+		if (params->private_key
-+		    && tls_mbedtls_readfile(params->private_key, &data, &len)) {
-+			return -1;
-+		}
-+		const char *pwd = params->private_key_passwd;
-+	  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+		ret = mbedtls_pk_parse_key(&tls_conf->private_key,
-+			data, len,
-+			(const unsigned char *)pwd,
-+			pwd ? os_strlen(pwd) : 0,
-+			mbedtls_ctr_drbg_random,
-+			tls_ctx_global.ctr_drbg);
-+	  #else
-+		ret = mbedtls_pk_parse_key(&tls_conf->private_key,
-+			data, len,
-+			(const unsigned char *)pwd,
-+			pwd ? os_strlen(pwd) : 0);
-+	  #endif
-+		if (params->private_key) {
-+			forced_memzero(data, len);
-+			os_free(data);
-+		}
-+		if (ret < 0) {
-+			elog(ret, "mbedtls_pk_parse_key");
-+			return -1;
-+		}
-+		tls_conf->has_private_key = 1;
-+	}
-+
-+	if (tls_conf->has_client_cert && tls_conf->has_private_key) {
-+		ret = mbedtls_ssl_conf_own_cert(
-+		    &tls_conf->conf, &tls_conf->client_cert, &tls_conf->private_key);
-+		if (ret < 0) {
-+			elog(ret, "mbedtls_ssl_conf_own_cert");
-+			return -1;
-+		}
-+	}
-+
-+	return 0;
-+}
-+
-+
-+/* mbedtls_x509_crt_profile_suiteb plus rsa_min_bitlen 2048 */
-+/* (reference: see also mbedtls_x509_crt_profile_next) */
-+/* ??? should permit SHA-512, too, and additional curves ??? */
-+static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb128 =
-+{
-+    /* Only SHA-256 and 384 */
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) |
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
-+    /* Only ECDSA */
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
-+#if defined(MBEDTLS_ECP_C)
-+    /* Only NIST P-256 and P-384 */
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP256R1 ) |
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
-+#else
-+    0,
-+#endif
-+    2048,
-+};
-+
-+
-+/* stricter than mbedtls_x509_crt_profile_suiteb */
-+/* (reference: see also mbedtls_x509_crt_profile_next) */
-+/* ??? should permit SHA-512, too, and additional curves ??? */
-+static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192 =
-+{
-+    /* Only SHA-384 */
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
-+    /* Only ECDSA */
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
-+#if defined(MBEDTLS_ECP_C)
-+    /* Only NIST P-384 */
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
-+#else
-+    0,
-+#endif
-+    3072,
-+};
-+
-+
-+/* stricter than mbedtls_x509_crt_profile_suiteb except allow any PK alg */
-+/* (reference: see also mbedtls_x509_crt_profile_next) */
-+/* ??? should permit SHA-512, too, and additional curves ??? */
-+static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192_anypk =
-+{
-+    /* Only SHA-384 */
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
-+    0xFFFFFFF, /* Any PK alg    */
-+#if defined(MBEDTLS_ECP_C)
-+    /* Only NIST P-384 */
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
-+#else
-+    0,
-+#endif
-+    3072,
-+};
-+
-+
-+static int tls_mbedtls_set_params(struct tls_conf *tls_conf,
-+				  const struct tls_connection_params *params)
-+{
-+	tls_conf->flags = params->flags;
-+
-+	if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
-+		emsg(MSG_INFO, "ocsp=3 not supported");
-+		return -1;
-+	}
-+
-+	if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP) {
-+		emsg(MSG_INFO, "ocsp not supported");
-+		return -1;
-+	}
-+
-+	int suiteb128 = 0;
-+	int suiteb192 = 0;
-+	if (params->openssl_ciphers) {
-+		if (os_strcmp(params->openssl_ciphers, "SUITEB192") == 0) {
-+			suiteb192 = 1;
-+			tls_conf->flags |= TLS_CONN_SUITEB;
-+		}
-+		if (os_strcmp(params->openssl_ciphers, "SUITEB128") == 0) {
-+			suiteb128 = 1;
-+			tls_conf->flags |= TLS_CONN_SUITEB;
-+		}
-+	}
-+
-+	int ret = mbedtls_ssl_config_defaults(
-+	    &tls_conf->conf, tls_ctx_global.tls_conf ? MBEDTLS_SSL_IS_SERVER
-+	                                             : MBEDTLS_SSL_IS_CLIENT,
-+	    MBEDTLS_SSL_TRANSPORT_STREAM,
-+	    (tls_conf->flags & TLS_CONN_SUITEB) ? MBEDTLS_SSL_PRESET_SUITEB
-+	                                        : MBEDTLS_SSL_PRESET_DEFAULT);
-+	if (ret != 0) {
-+		elog(ret, "mbedtls_ssl_config_defaults");
-+		return -1;
-+	}
-+
-+	if (suiteb128) {
-+		mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
-+		                              &tls_mbedtls_crt_profile_suiteb128);
-+		mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 2048);
-+	}
-+	else if (suiteb192) {
-+		mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
-+		                              &tls_mbedtls_crt_profile_suiteb192);
-+		mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
-+	}
-+	else if (tls_conf->flags & TLS_CONN_SUITEB) {
-+		/* treat as suiteb192 while allowing any PK algorithm */
-+		mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
-+		                              &tls_mbedtls_crt_profile_suiteb192_anypk);
-+		mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
-+	}
-+
-+	tls_mbedtls_set_allowed_tls_vers(tls_conf, &tls_conf->conf);
-+	ret = tls_mbedtls_set_certs(tls_conf, params);
-+	if (ret != 0)
-+		return -1;
-+
-+	if (params->dh_file
-+	    && !tls_mbedtls_set_dhparams(tls_conf, params->dh_file)) {
-+		return -1;
-+	}
-+
-+	if (params->openssl_ecdh_curves
-+	    && !tls_mbedtls_set_curves(tls_conf, params->openssl_ecdh_curves)) {
-+		return -1;
-+	}
-+
-+	if (params->openssl_ciphers) {
-+		if (!tls_mbedtls_set_ciphers(tls_conf, params->openssl_ciphers))
-+			return -1;
-+	}
-+	else if (tls_conf->flags & TLS_CONN_SUITEB) {
-+		/* special-case a select set of ciphers for hwsim tests */
-+		if (!tls_mbedtls_set_ciphers(tls_conf,
-+		        (tls_conf->flags & TLS_CONN_SUITEB_NO_ECDH)
-+		          ? "DHE-RSA-AES256-GCM-SHA384"
-+		          : "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384"))
-+			return -1;
-+	}
-+
-+	return 0;
-+}
-+
-+
-+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
-+			      const struct tls_connection_params *params)
-+{
-+	if (conn == NULL || params == NULL)
-+		return -1;
-+
-+	tls_conf_deinit(conn->tls_conf);
-+	struct tls_conf *tls_conf = conn->tls_conf = tls_conf_init(tls_ctx);
-+	if (tls_conf == NULL)
-+		return -1;
-+
-+	if (tls_ctx_global.tls_conf) {
-+		tls_conf->check_crl = tls_ctx_global.tls_conf->check_crl;
-+		tls_conf->check_crl_strict = tls_ctx_global.tls_conf->check_crl_strict;
-+		/*(tls_openssl.c inherits check_cert_subject from global conf)*/
-+		if (tls_ctx_global.tls_conf->check_cert_subject) {
-+			tls_conf->check_cert_subject =
-+			  os_strdup(tls_ctx_global.tls_conf->check_cert_subject);
-+			if (tls_conf->check_cert_subject == NULL)
-+				return -1;
-+		}
-+	}
-+
-+	if (tls_mbedtls_set_params(tls_conf, params) != 0)
-+		return -1;
-+	conn->verify_peer = tls_conf->verify_peer;
-+
-+	return tls_mbedtls_ssl_setup(conn);
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_SESSION_TICKETS
-+
-+static int tls_mbedtls_clienthello_session_ticket_prep (struct tls_connection *conn,
-+                                                        const u8 *data, size_t len)
-+{
-+	if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
-+		return -1;
-+	if (conn->clienthello_session_ticket)
-+		tls_connection_deinit_clienthello_session_ticket(conn);
-+	if (len) {
-+		conn->clienthello_session_ticket = mbedtls_calloc(1, len);
-+		if (conn->clienthello_session_ticket == NULL)
-+			return -1;
-+		conn->clienthello_session_ticket_len = len;
-+		os_memcpy(conn->clienthello_session_ticket, data, len);
-+	}
-+	return 0;
-+}
-+
-+
-+static void tls_mbedtls_clienthello_session_ticket_set (struct tls_connection *conn)
-+{
-+	mbedtls_ssl_session *sess = conn->ssl.MBEDTLS_PRIVATE(session_negotiate);
-+	if (sess->MBEDTLS_PRIVATE(ticket)) {
-+		mbedtls_platform_zeroize(sess->MBEDTLS_PRIVATE(ticket),
-+		                         sess->MBEDTLS_PRIVATE(ticket_len));
-+		mbedtls_free(sess->MBEDTLS_PRIVATE(ticket));
-+	}
-+	sess->MBEDTLS_PRIVATE(ticket) = conn->clienthello_session_ticket;
-+	sess->MBEDTLS_PRIVATE(ticket_len) = conn->clienthello_session_ticket_len;
-+	sess->MBEDTLS_PRIVATE(ticket_lifetime) = 86400;/* XXX: can hint be 0? */
-+
-+	conn->clienthello_session_ticket = NULL;
-+	conn->clienthello_session_ticket_len = 0;
-+}
-+
-+
-+static int tls_mbedtls_ssl_ticket_write(void *p_ticket,
-+                                        const mbedtls_ssl_session *session,
-+                                        unsigned char *start,
-+                                        const unsigned char *end,
-+                                        size_t *tlen,
-+                                        uint32_t *lifetime)
-+{
-+	struct tls_connection *conn = p_ticket;
-+	if (conn && conn->session_ticket_cb) {
-+		/* see tls_mbedtls_clienthello_session_ticket_prep() */
-+		/* see tls_mbedtls_clienthello_session_ticket_set() */
-+		return 0;
-+	}
-+
-+	return mbedtls_ssl_ticket_write(&tls_ctx_global.ticket_ctx,
-+	                                session, start, end, tlen, lifetime);
-+}
-+
-+
-+static int tls_mbedtls_ssl_ticket_parse(void *p_ticket,
-+                                        mbedtls_ssl_session *session,
-+                                        unsigned char *buf,
-+                                        size_t len)
-+{
-+	/* XXX: TODO: not implemented in client;
-+	 * mbedtls_ssl_conf_session_tickets_cb() callbacks only for TLS server*/
-+
-+	if (len == 0)
-+		return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-+
-+	struct tls_connection *conn = p_ticket;
-+	if (conn && conn->session_ticket_cb) {
-+		/* XXX: have random and secret been initialized yet?
-+		 *      or must keys first be exported?
-+		 *      EAP-FAST uses all args, EAP-TEAP only uses secret */
-+		struct tls_random data;
-+		if (tls_connection_get_random(NULL, conn, &data) != 0)
-+			return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
-+		int ret =
-+		  conn->session_ticket_cb(conn->session_ticket_cb_ctx,
-+		                          buf, len,
-+		                          data.client_random,
-+		                          data.server_random,
-+		                          conn->expkey_secret);
-+		if (ret == 1) {
-+			conn->resumed = 1;
-+			return 0;
-+		}
-+		emsg(MSG_ERROR, "EAP session ticket ext not implemented");
-+		return MBEDTLS_ERR_SSL_INVALID_MAC;
-+		/*(non-zero return used for mbedtls debug logging)*/
-+	}
-+
-+	/* XXX: TODO always use tls_mbedtls_ssl_ticket_parse() for callback? */
-+	int rc = mbedtls_ssl_ticket_parse(&tls_ctx_global.ticket_ctx,
-+	                                  session, buf, len);
-+	if (conn)
-+		conn->resumed = (rc == 0);
-+	return rc;
-+}
-+
-+#endif /* TLS_MBEDTLS_SESSION_TICKETS */
-+
-+
-+__attribute_cold__
-+int tls_global_set_params(void *tls_ctx,
-+			  const struct tls_connection_params *params)
-+{
-+	/* XXX: why might global_set_params be called more than once? */
-+	if (tls_ctx_global.tls_conf)
-+		tls_conf_deinit(tls_ctx_global.tls_conf);
-+	tls_ctx_global.tls_conf = tls_conf_init(tls_ctx);
-+	if (tls_ctx_global.tls_conf == NULL)
-+		return -1;
-+
-+  #ifdef MBEDTLS_SSL_SESSION_TICKETS
-+  #ifdef MBEDTLS_SSL_TICKET_C
-+	if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET))
-+	  #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+		mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
-+		                                    tls_mbedtls_ssl_ticket_write,
-+		                                    tls_mbedtls_ssl_ticket_parse,
-+		                                    NULL);
-+	  #else
-+		mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
-+		                                    mbedtls_ssl_ticket_write,
-+		                                    mbedtls_ssl_ticket_parse,
-+		                                    &tls_ctx_global.ticket_ctx);
-+	  #endif
-+  #endif
-+  #endif
-+
-+	os_free(tls_ctx_global.ocsp_stapling_response);
-+	tls_ctx_global.ocsp_stapling_response = NULL;
-+	if (params->ocsp_stapling_response)
-+		tls_ctx_global.ocsp_stapling_response =
-+			os_strdup(params->ocsp_stapling_response);
-+
-+	os_free(tls_ctx_global.ca_cert_file);
-+	tls_ctx_global.ca_cert_file = NULL;
-+	if (params->ca_cert)
-+		tls_ctx_global.ca_cert_file = os_strdup(params->ca_cert);
-+	return tls_mbedtls_set_params(tls_ctx_global.tls_conf, params);
-+}
-+
-+
-+int tls_global_set_verify(void *tls_ctx, int check_crl, int strict)
-+{
-+	tls_ctx_global.tls_conf->check_crl = check_crl;
-+	tls_ctx_global.tls_conf->check_crl_strict = strict; /*(time checks)*/
-+	return 0;
-+}
-+
-+
-+int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
-+			      int verify_peer, unsigned int flags,
-+			      const u8 *session_ctx, size_t session_ctx_len)
-+{
-+	/*(EAP server-side calls this from eap_server_tls_ssl_init())*/
-+	if (conn == NULL)
-+		return -1;
-+
-+	conn->tls_conf->flags |= flags;/* TODO: reprocess flags, if necessary */
-+
-+	int authmode;
-+	switch (verify_peer) {
-+	case 2:  authmode = MBEDTLS_SSL_VERIFY_OPTIONAL; break;/*(eap_teap_init())*/
-+	case 1:  authmode = MBEDTLS_SSL_VERIFY_REQUIRED; break;
-+	default: authmode = MBEDTLS_SSL_VERIFY_NONE;     break;
-+	}
-+	mbedtls_ssl_set_hs_authmode(&conn->ssl, authmode);
-+
-+	if ((conn->verify_peer = (authmode != MBEDTLS_SSL_VERIFY_NONE)))
-+		mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
-+	else
-+		mbedtls_ssl_set_verify(&conn->ssl, NULL, NULL);
-+
-+	return 0;
-+}
-+
-+
-+#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+static void tls_connection_export_keys_cb(
-+    void *p_expkey, mbedtls_ssl_key_export_type secret_type,
-+    const unsigned char *secret, size_t secret_len,
-+    const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
-+    const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
-+    mbedtls_tls_prf_types tls_prf_type)
-+{
-+	struct tls_connection *conn = p_expkey;
-+	conn->tls_prf_type = tls_prf_type;
-+	if (!tls_prf_type)
-+		return;
-+	if (secret_len > sizeof(conn->expkey_secret)) {
-+		emsg(MSG_ERROR, "tls_connection_export_keys_cb secret too long");
-+		conn->tls_prf_type = MBEDTLS_SSL_TLS_PRF_NONE; /* 0 */
-+		return;
-+	}
-+	conn->expkey_secret_len = secret_len;
-+	os_memcpy(conn->expkey_secret, secret, secret_len);
-+	os_memcpy(conn->expkey_randbytes,
-+	          client_random, MBEDTLS_EXPKEY_RAND_LEN);
-+	os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
-+	          server_random, MBEDTLS_EXPKEY_RAND_LEN);
-+}
-+#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+static int tls_connection_export_keys_cb(
-+    void *p_expkey,
-+    const unsigned char *ms,
-+    const unsigned char *kb,
-+    size_t maclen,
-+    size_t keylen,
-+    size_t ivlen,
-+    const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
-+    const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
-+    mbedtls_tls_prf_types tls_prf_type )
-+{
-+	struct tls_connection *conn = p_expkey;
-+	conn->tls_prf_type = tls_prf_type;
-+	if (!tls_prf_type)
-+		return -1; /*(return value ignored by mbedtls)*/
-+	conn->expkey_keyblock_size = maclen + keylen + ivlen;
-+	conn->expkey_secret_len = MBEDTLS_EXPKEY_FIXED_SECRET_LEN;
-+	os_memcpy(conn->expkey_secret, ms, MBEDTLS_EXPKEY_FIXED_SECRET_LEN);
-+	os_memcpy(conn->expkey_randbytes,
-+	          client_random, MBEDTLS_EXPKEY_RAND_LEN);
-+	os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
-+	          server_random, MBEDTLS_EXPKEY_RAND_LEN);
-+	return 0;
-+}
-+#endif
-+
-+
-+int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
-+			      struct tls_random *data)
-+{
-+	if (!conn || !conn->tls_prf_type)
-+		return -1;
-+	data->client_random = conn->expkey_randbytes;
-+	data->client_random_len = MBEDTLS_EXPKEY_RAND_LEN;
-+	data->server_random = conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN;
-+	data->server_random_len = MBEDTLS_EXPKEY_RAND_LEN;
-+	return 0;
-+}
-+
-+
-+int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
-+			      const char *label, const u8 *context,
-+			      size_t context_len, u8 *out, size_t out_len)
-+{
-+	/* (EAP-PEAP EAP-TLS EAP-TTLS) */
-+  #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+	return (conn && conn->established && conn->tls_prf_type)
-+	  ? mbedtls_ssl_tls_prf(conn->tls_prf_type,
-+				conn->expkey_secret, conn->expkey_secret_len, label,
-+				conn->expkey_randbytes,
-+				sizeof(conn->expkey_randbytes), out, out_len)
-+	  : -1;
-+  #else
-+	/* not implemented here for mbedtls < 2.18.0 */
-+	return -1;
-+  #endif
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_FAST
-+
-+#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+/* keyblock size info is not exposed in mbed TLS 3.0.0 */
-+/* extracted from mbedtls library/ssl_tls.c:ssl_tls12_populate_transform() */
-+#include <mbedtls/ssl_ciphersuites.h>
-+#include <mbedtls/cipher.h>
-+static size_t tls_mbedtls_ssl_keyblock_size (mbedtls_ssl_context *ssl)
-+{
-+  #if !defined(MBEDTLS_USE_PSA_CRYPTO) /* XXX: (not extracted for PSA crypto) */
-+  #if defined(MBEDTLS_SSL_PROTO_TLS1_3)
-+    if (tls_version == MBEDTLS_SSL_VERSION_TLS1_3)
-+        return 0; /* (calculation not extracted) */
-+  #endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
-+
-+    int ciphersuite = mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl);
-+    const mbedtls_ssl_ciphersuite_t *ciphersuite_info =
-+      mbedtls_ssl_ciphersuite_from_id(ciphersuite);
-+    if (ciphersuite_info == NULL)
-+        return 0;
-+
-+    const mbedtls_cipher_info_t *cipher_info =
-+      mbedtls_cipher_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(cipher));
-+    if (cipher_info == NULL)
-+        return 0;
-+
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */
-+    size_t keylen = mbedtls_cipher_info_get_key_bitlen(cipher_info) / 8;
-+    mbedtls_cipher_mode_t mode = mbedtls_cipher_info_get_mode(cipher_info);
-+  #else
-+    size_t keylen = cipher_info->MBEDTLS_PRIVATE(key_bitlen) / 8;
-+    mbedtls_cipher_mode_t mode = cipher_info->MBEDTLS_PRIVATE(mode);
-+  #endif
-+  #if defined(MBEDTLS_GCM_C) || \
-+      defined(MBEDTLS_CCM_C) || \
-+      defined(MBEDTLS_CHACHAPOLY_C)
-+    if (mode == MBEDTLS_MODE_GCM || mode == MBEDTLS_MODE_CCM)
-+        return keylen + 4;
-+    else if (mode == MBEDTLS_MODE_CHACHAPOLY)
-+        return keylen + 12;
-+    else
-+  #endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C || MBEDTLS_CHACHAPOLY_C */
-+  #if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
-+    {
-+        const mbedtls_md_info_t *md_info =
-+          mbedtls_md_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(mac));
-+        if (md_info == NULL)
-+            return 0;
-+        size_t mac_key_len = mbedtls_md_get_size(md_info);
-+        size_t ivlen = mbedtls_cipher_info_get_iv_size(cipher_info);
-+        return keylen + mac_key_len + ivlen;
-+    }
-+  #endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */
-+  #endif /* !MBEDTLS_USE_PSA_CRYPTO *//* (not extracted for PSA crypto) */
-+    return 0;
-+}
-+#endif /* MBEDTLS_VERSION_NUMBER >= 0x03000000 *//* mbedtls 3.0.0 */
-+
-+
-+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
-+				    u8 *out, size_t out_len)
-+{
-+	/* XXX: has export keys callback been run? */
-+	if (!conn || !conn->tls_prf_type)
-+		return -1;
-+
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+	conn->expkey_keyblock_size = tls_mbedtls_ssl_keyblock_size(&conn->ssl);
-+	if (conn->expkey_keyblock_size == 0)
-+		return -1;
-+  #endif
-+	size_t skip = conn->expkey_keyblock_size * 2;
-+	unsigned char *tmp_out = os_malloc(skip + out_len);
-+	if (!tmp_out)
-+		return -1;
-+
-+	/* server_random and then client_random */
-+	unsigned char seed[MBEDTLS_EXPKEY_RAND_LEN*2];
-+	os_memcpy(seed, conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
-+	          MBEDTLS_EXPKEY_RAND_LEN);
-+	os_memcpy(seed + MBEDTLS_EXPKEY_RAND_LEN, conn->expkey_randbytes,
-+	          MBEDTLS_EXPKEY_RAND_LEN);
-+
-+  #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+	int ret = mbedtls_ssl_tls_prf(conn->tls_prf_type,
-+				      conn->expkey_secret, conn->expkey_secret_len,
-+				      "key expansion", seed, sizeof(seed),
-+				      tmp_out, skip + out_len);
-+	if (ret == 0)
-+		os_memcpy(out, tmp_out + skip, out_len);
-+  #else
-+	int ret = -1; /*(not reached if not impl; return -1 at top of func)*/
-+  #endif
-+
-+	bin_clear_free(tmp_out, skip + out_len);
-+	forced_memzero(seed, sizeof(seed));
-+	return ret;
-+}
-+
-+#endif /* TLS_MBEDTLS_EAP_FAST */
-+
-+
-+__attribute_cold__
-+static void tls_mbedtls_suiteb_handshake_alert (struct tls_connection *conn)
-+{
-+	/* tests/hwsim/test_suite_b.py test_suite_b_192_rsa_insufficient_dh */
-+	if (!(conn->tls_conf->flags & TLS_CONN_SUITEB))
-+		return;
-+	if (tls_ctx_global.tls_conf) /*(is server; want issue event on client)*/
-+		return;
-+  #if 0
-+	/*(info not available on client;
-+         * mbed TLS library enforces dhm min bitlen in ServerKeyExchange)*/
-+	if (MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ==
-+	  #if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
-+	          mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl)
-+	  #else
-+	          mbedtls_ssl_get_ciphersuite_id(
-+	            mbedtls_ssl_get_ciphersuite(&conn->ssl))
-+	  #endif
-+	    && mbedtls_mpi_size(&conn->tls_conf->conf.MBEDTLS_PRIVATE(dhm_P))
-+	         < 384 /*(3072/8)*/)
-+  #endif
-+	{
-+		struct tls_config *init_conf = &tls_ctx_global.init_conf;
-+		if (init_conf->event_cb) {
-+			union tls_event_data ev;
-+			os_memset(&ev, 0, sizeof(ev));
-+			ev.alert.is_local = 1;
-+			ev.alert.type = "fatal";
-+			/*"internal error" string for tests/hwsim/test_suiteb.py */
-+			ev.alert.description = "internal error: handshake failure";
-+			/*ev.alert.description = "insufficient security";*/
-+			init_conf->event_cb(init_conf->cb_ctx, TLS_ALERT, &ev);
-+		}
-+	}
-+}
-+
-+
-+struct wpabuf * tls_connection_handshake(void *tls_ctx,
-+					 struct tls_connection *conn,
-+					 const struct wpabuf *in_data,
-+					 struct wpabuf **appl_data)
-+{
-+	if (appl_data)
-+		*appl_data = NULL;
-+
-+	if (in_data && wpabuf_len(in_data)) {
-+		/*(unsure why tls_gnutls.c discards buffer contents; skip here)*/
-+		if (conn->pull_buf && 0) /* disable; appears unwise */
-+			tls_pull_buf_discard(conn, __func__);
-+		if (!tls_pull_buf_append(conn, in_data))
-+			return NULL;
-+	}
-+
-+	if (conn->tls_conf == NULL) {
-+		struct tls_connection_params params;
-+		os_memset(&params, 0, sizeof(params));
-+		params.openssl_ciphers =
-+		  tls_ctx_global.init_conf.openssl_ciphers;
-+		params.flags = tls_ctx_global.tls_conf->flags;
-+		if (tls_connection_set_params(tls_ctx, conn, &params) != 0)
-+			return NULL;
-+	}
-+
-+	if (conn->verify_peer) /*(call here might be redundant; nbd)*/
-+		mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
-+
-+  #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+	if (conn->clienthello_session_ticket)
-+		/*(starting handshake for EAP-FAST and EAP-TEAP)*/
-+		tls_mbedtls_clienthello_session_ticket_set(conn);
-+
-+	/* (not thread-safe due to need to set userdata 'conn' for callback) */
-+	/* (unable to use mbedtls_ssl_set_user_data_p() with mbedtls 3.2.0+
-+	 *  since ticket write and parse callbacks take (mbedtls_ssl_session *)
-+	 *  param instead of (mbedtls_ssl_context *) param) */
-+	if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
-+		mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
-+		                                    NULL, NULL, NULL);
-+	else
-+		mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
-+		                                    tls_mbedtls_ssl_ticket_write,
-+		                                    tls_mbedtls_ssl_ticket_parse,
-+		                                    conn);
-+  #endif
-+
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
-+	int ret = mbedtls_ssl_handshake(&conn->ssl);
-+  #else
-+	int ret = 0;
-+	while (conn->ssl.MBEDTLS_PRIVATE(state) != MBEDTLS_SSL_HANDSHAKE_OVER) {
-+		ret = mbedtls_ssl_handshake_step(&conn->ssl);
-+		if (ret != 0)
-+			break;
-+	}
-+  #endif
-+
-+  #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+	mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
-+	                                    tls_mbedtls_ssl_ticket_write,
-+	                                    tls_mbedtls_ssl_ticket_parse,
-+	                                    NULL);
-+  #endif
-+
-+	switch (ret) {
-+	case 0:
-+		conn->established = 1;
-+		if (conn->push_buf == NULL)
-+			/* Need to return something to get final TLS ACK. */
-+			conn->push_buf = wpabuf_alloc(0);
-+
-+		if (appl_data /*&& conn->pull_buf && wpabuf_len(conn->pull_buf)*/)
-+			*appl_data = NULL; /* RFE: check for application data */
-+		break;
-+	case MBEDTLS_ERR_SSL_WANT_WRITE:
-+	case MBEDTLS_ERR_SSL_WANT_READ:
-+	case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS:
-+	case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS:
-+		if (tls_ctx_global.tls_conf /*(is server)*/
-+		    && conn->established && conn->push_buf == NULL)
-+			/* Need to return something to trigger completion of EAP-TLS. */
-+			conn->push_buf = wpabuf_alloc(0);
-+		break;
-+	default:
-+		++conn->failed;
-+		switch (ret) {
-+		case MBEDTLS_ERR_SSL_CLIENT_RECONNECT:
-+		case MBEDTLS_ERR_NET_CONN_RESET:
-+		case MBEDTLS_ERR_NET_SEND_FAILED:
-+			++conn->write_alerts;
-+			break;
-+	      #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+		case MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE:
-+	      #else
-+		case MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE:
-+	      #endif
-+			tls_mbedtls_suiteb_handshake_alert(conn);
-+			/* fall through */
-+		case MBEDTLS_ERR_NET_RECV_FAILED:
-+		case MBEDTLS_ERR_SSL_CONN_EOF:
-+		case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
-+		case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE:
-+			++conn->read_alerts;
-+			break;
-+		default:
-+			break;
-+		}
-+
-+		ilog(ret, "mbedtls_ssl_handshake");
-+		break;
-+	}
-+
-+	struct wpabuf *out_data = conn->push_buf;
-+	conn->push_buf = NULL;
-+	return out_data;
-+}
-+
-+
-+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
-+						struct tls_connection *conn,
-+						const struct wpabuf *in_data,
-+						struct wpabuf **appl_data)
-+{
-+	conn->is_server = 1;
-+	return tls_connection_handshake(tls_ctx, conn, in_data, appl_data);
-+}
-+
-+
-+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
-+				       struct tls_connection *conn,
-+				       const struct wpabuf *in_data)
-+{
-+	int res = mbedtls_ssl_write(&conn->ssl,
-+	                            wpabuf_head_u8(in_data), wpabuf_len(in_data));
-+	if (res < 0) {
-+		elog(res, "mbedtls_ssl_write");
-+		return NULL;
-+	}
-+
-+	struct wpabuf *buf = conn->push_buf;
-+	conn->push_buf = NULL;
-+	return buf;
-+}
-+
-+
-+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
-+				       struct tls_connection *conn,
-+				       const struct wpabuf *in_data)
-+{
-+	int res;
-+	struct wpabuf *out;
-+
-+	/*assert(in_data != NULL);*/
-+	if (!tls_pull_buf_append(conn, in_data))
-+		return NULL;
-+
-+  #if defined(MBEDTLS_ZLIB_SUPPORT) /* removed in mbedtls 3.x */
-+	/* Add extra buffer space to handle the possibility of decrypted
-+	 * data being longer than input data due to TLS compression. */
-+	out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
-+  #else /* TLS compression is disabled in mbedtls 3.x */
-+	out = wpabuf_alloc(wpabuf_len(in_data));
-+  #endif
-+	if (out == NULL)
-+		return NULL;
-+
-+	res = mbedtls_ssl_read(&conn->ssl, wpabuf_mhead(out), wpabuf_size(out));
-+	if (res < 0) {
-+	  #if 1 /*(seems like a different error if wpabuf_len(in_data) == 0)*/
-+		if (res == MBEDTLS_ERR_SSL_WANT_READ)
-+			return out;
-+	  #endif
-+		elog(res, "mbedtls_ssl_read");
-+		wpabuf_free(out);
-+		return NULL;
-+	}
-+	wpabuf_put(out, res);
-+
-+	return out;
-+}
-+
-+
-+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
-+{
-+	/* XXX: might need to detect if session resumed from TLS session ticket
-+	 * even if not special session ticket handling for EAP-FAST, EAP-TEAP */
-+	/* (?ssl->handshake->resume during session ticket validation?) */
-+	return conn && conn->resumed;
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_FAST
-+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
-+				   u8 *ciphers)
-+{
-+	/* ciphers is list of TLS_CIPHER_* from hostap/src/crypto/tls.h */
-+	int ids[7];
-+	const int idsz = (int)sizeof(ids);
-+	int nids = -1, id;
-+	for ( ; *ciphers != TLS_CIPHER_NONE; ++ciphers) {
-+		switch (*ciphers) {
-+		case TLS_CIPHER_RC4_SHA:
-+		  #ifdef MBEDTLS_TLS_RSA_WITH_RC4_128_SHA
-+			id = MBEDTLS_TLS_RSA_WITH_RC4_128_SHA;
-+			break;
-+		  #else
-+			continue; /*(not supported in mbedtls 3.x; ignore)*/
-+		  #endif
-+		case TLS_CIPHER_AES128_SHA:
-+			id = MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA;
-+			break;
-+		case TLS_CIPHER_RSA_DHE_AES128_SHA:
-+			id = MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
-+			break;
-+		case TLS_CIPHER_ANON_DH_AES128_SHA:
-+			continue; /*(not supported in mbedtls; ignore)*/
-+		case TLS_CIPHER_RSA_DHE_AES256_SHA:
-+			id = MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
-+			break;
-+		case TLS_CIPHER_AES256_SHA:
-+			id = MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA;
-+			break;
-+		default:
-+			return -1; /* should not happen */
-+		}
-+		if (++nids == idsz)
-+			return -1; /* should not happen */
-+		ids[nids] = id;
-+	}
-+	if (nids < 0)
-+		return 0; /* nothing to do */
-+	if (++nids == idsz)
-+		return -1; /* should not happen */
-+	ids[nids] = 0; /* terminate list */
-+	++nids;
-+
-+	return tls_mbedtls_set_ciphersuites(conn->tls_conf, ids, nids) ? 0 : -1;
-+}
-+#endif
-+
-+
-+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
-+		    char *buf, size_t buflen)
-+{
-+	if (conn == NULL)
-+		return -1;
-+	os_strlcpy(buf, mbedtls_ssl_get_version(&conn->ssl), buflen);
-+	return buf[0] != 'u' ? 0 : -1; /*(-1 if "unknown")*/
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_TEAP
-+u16 tls_connection_get_cipher_suite(struct tls_connection *conn)
-+{
-+	if (conn == NULL)
-+		return 0;
-+	return (u16)mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
-+}
-+#endif
-+
-+
-+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
-+		   char *buf, size_t buflen)
-+{
-+	if (conn == NULL)
-+		return -1;
-+	const int id = mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
-+	return tls_mbedtls_translate_ciphername(id, buf, buflen) ? 0 : -1;
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_SESSION_TICKETS
-+
-+int tls_connection_enable_workaround(void *tls_ctx,
-+				     struct tls_connection *conn)
-+{
-+	/* (see comment in src/eap_peer/eap_fast.c:eap_fast_init()) */
-+	/* XXX: is there a relevant setting for this in mbed TLS? */
-+	/* (do we even care that much about older CBC ciphers?) */
-+	return 0;
-+}
-+
-+
-+int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
-+				    int ext_type, const u8 *data,
-+				    size_t data_len)
-+{
-+	/* (EAP-FAST and EAP-TEAP) */
-+	if (ext_type == MBEDTLS_TLS_EXT_SESSION_TICKET) /*(ext_type == 35)*/
-+		return tls_mbedtls_clienthello_session_ticket_prep(conn, data,
-+		                                                   data_len);
-+
-+	return -1;
-+}
-+
-+#endif /* TLS_MBEDTLS_SESSION_TICKETS */
-+
-+
-+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
-+{
-+	return conn ? conn->failed : -1;
-+}
-+
-+
-+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
-+{
-+	return conn ? conn->read_alerts : -1;
-+}
-+
-+
-+int tls_connection_get_write_alerts(void *tls_ctx,
-+				    struct tls_connection *conn)
-+{
-+	return conn ? conn->write_alerts : -1;
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_SESSION_TICKETS
-+int tls_connection_set_session_ticket_cb(
-+	void *tls_ctx, struct tls_connection *conn,
-+	tls_session_ticket_cb cb, void *ctx)
-+{
-+	if (!(conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)) {
-+		/* (EAP-FAST and EAP-TEAP) */
-+		conn->session_ticket_cb = cb;
-+		conn->session_ticket_cb_ctx = ctx;
-+		return 0;
-+	}
-+	return -1;
-+}
-+#endif
-+
-+
-+int tls_get_library_version(char *buf, size_t buf_len)
-+{
-+  #ifndef MBEDTLS_VERSION_C
-+	const char * const ver = "n/a";
-+  #else
-+	char ver[9];
-+	mbedtls_version_get_string(ver);
-+  #endif
-+	return os_snprintf(buf, buf_len,
-+	                   "mbed TLS build=" MBEDTLS_VERSION_STRING " run=%s", ver);
-+}
-+
-+
-+void tls_connection_set_success_data(struct tls_connection *conn,
-+				     struct wpabuf *data)
-+{
-+	wpabuf_free(conn->success_data);
-+	conn->success_data = data;
-+}
-+
-+
-+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
-+{
-+}
-+
-+
-+const struct wpabuf *
-+tls_connection_get_success_data(struct tls_connection *conn)
-+{
-+	return conn->success_data;
-+}
-+
-+
-+void tls_connection_remove_session(struct tls_connection *conn)
-+{
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_TEAP
-+int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len)
-+{
-+  #if defined(MBEDTLS_SSL_RENEGOTIATION) /* XXX: renegotiation or resumption? */
-+	/* data from TLS handshake Finished message */
-+	size_t verify_len = conn->ssl.MBEDTLS_PRIVATE(verify_data_len);
-+	char *verify_data = (conn->is_server ^ conn->resumed)
-+	  ? conn->ssl.MBEDTLS_PRIVATE(peer_verify_data)
-+	  : conn->ssl.MBEDTLS_PRIVATE(own_verify_data);
-+	if (verify_len && verify_len <= max_len) {
-+		os_memcpy(buf, verify_data, verify_len);
-+		return (int)verify_len;
-+	}
-+  #endif
-+	return -1;
-+}
-+#endif
-+
-+
-+__attribute_noinline__
-+static void tls_mbedtls_set_peer_subject(struct tls_connection *conn, const mbedtls_x509_crt *crt)
-+{
-+	if (conn->peer_subject)
-+		return;
-+	char buf[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
-+	int buflen = mbedtls_x509_dn_gets(buf, sizeof(buf), &crt->subject);
-+	if (buflen >= 0 && (conn->peer_subject = os_malloc((size_t)buflen+1)))
-+		os_memcpy(conn->peer_subject, buf, (size_t)buflen+1);
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_TEAP
-+const char * tls_connection_get_peer_subject(struct tls_connection *conn)
-+{
-+	if (!conn)
-+		return NULL;
-+	if (!conn->peer_subject) { /*(if not set during cert verify)*/
-+		const mbedtls_x509_crt *peer_cert =
-+		  mbedtls_ssl_get_peer_cert(&conn->ssl);
-+		if (peer_cert)
-+			tls_mbedtls_set_peer_subject(conn, peer_cert);
-+	}
-+	return conn->peer_subject;
-+}
-+#endif
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_TEAP
-+bool tls_connection_get_own_cert_used(struct tls_connection *conn)
-+{
-+	/* XXX: availability of cert does not necessary mean that client
-+	 * received certificate request from server and then sent cert.
-+	 * ? step handshake in tls_connection_handshake() looking for
-+	 *   MBEDTLS_SSL_CERTIFICATE_REQUEST ? */
-+	const struct tls_conf * const tls_conf = conn->tls_conf;
-+	return (tls_conf->has_client_cert && tls_conf->has_private_key);
-+}
-+#endif
-+
-+
-+#if defined(CONFIG_FIPS)
-+#define TLS_MBEDTLS_CONFIG_FIPS
-+#endif
-+
-+#if defined(CONFIG_SHA256)
-+#define TLS_MBEDTLS_TLS_PRF_SHA256
-+#endif
-+
-+#if defined(CONFIG_SHA384)
-+#define TLS_MBEDTLS_TLS_PRF_SHA384
-+#endif
-+
-+
-+#ifndef TLS_MBEDTLS_CONFIG_FIPS
-+#if defined(CONFIG_MODULE_TESTS)
-+/* unused with CONFIG_TLS=mbedtls except in crypto_module_tests.c */
-+#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */ \
-+ && MBEDTLS_VERSION_NUMBER <  0x03000000 /* mbedtls 3.0.0 */
-+/* sha1-tlsprf.c */
-+#include "sha1.h"
-+int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
-+		     const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
-+{
-+	return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_TLS1,
-+				   secret, secret_len, label,
-+				   seed, seed_len, out, outlen) ? -1 : 0;
-+}
-+#else
-+#include "sha1-tlsprf.c" /* pull in hostap local implementation */
-+#endif
-+#endif
-+#endif
-+
-+#ifdef TLS_MBEDTLS_TLS_PRF_SHA256
-+/* sha256-tlsprf.c */
-+#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+#include "sha256.h"
-+int tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label,
-+		   const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
-+{
-+	return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA256,
-+				   secret, secret_len, label,
-+				   seed, seed_len, out, outlen) ? -1 : 0;
-+}
-+#else
-+#include "sha256-tlsprf.c" /* pull in hostap local implementation */
-+#endif
-+#endif
-+
-+#ifdef TLS_MBEDTLS_TLS_PRF_SHA384
-+/* sha384-tlsprf.c */
-+#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+#include "sha384.h"
-+int tls_prf_sha384(const u8 *secret, size_t secret_len, const char *label,
-+		   const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
-+{
-+	return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA384,
-+				   secret, secret_len, label,
-+				   seed, seed_len, out, outlen) ? -1 : 0;
-+}
-+#else
-+#include "sha384-tlsprf.c" /* pull in hostap local implementation */
-+#endif
-+#endif
-+
-+
-+#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
-+#define mbedtls_x509_crt_has_ext_type(crt, ext_type) \
-+        ((crt)->MBEDTLS_PRIVATE(ext_types) & (ext_type))
-+#endif
-+
-+struct mlist { const char *p; size_t n; };
-+
-+
-+static int
-+tls_mbedtls_match_altsubject(mbedtls_x509_crt *crt, const char *match)
-+{
-+	/* RFE: this could be pre-parsed into structured data at config time */
-+	struct mlist list[256]; /*(much larger than expected)*/
-+	int nlist = 0;
-+	if (   os_strncmp(match, "EMAIL:", 6) != 0
-+	    && os_strncmp(match, "DNS:",   4) != 0
-+	    && os_strncmp(match, "URI:",   4) != 0   ) {
-+		wpa_printf(MSG_INFO, "MTLS: Invalid altSubjectName match '%s'", match);
-+		return 0;
-+	}
-+	for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
-+		do { } while ((tok = os_strchr(s, ';'))
-+		              && os_strncmp(tok+1, "EMAIL:", 6) != 0
-+		              && os_strncmp(tok+1, "DNS:",   4) != 0
-+		              && os_strncmp(tok+1, "URI:",   4) != 0);
-+		list[nlist].p = s;
-+		list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
-+		if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
-+			wpa_printf(MSG_INFO, "MTLS: excessive altSubjectName match '%s'",
-+			           match);
-+			break; /* truncate huge list and continue */
-+		}
-+	}
-+
-+	if (!mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
-+		return 0;
-+
-+	const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
-+	for (; cur != NULL; cur = cur->next) {
-+		const unsigned char san_type = (unsigned char)cur->buf.tag
-+		                             & MBEDTLS_ASN1_TAG_VALUE_MASK;
-+		char t;
-+		size_t step = 4;
-+		switch (san_type) {             /* "EMAIL:" or "DNS:" or "URI:" */
-+		case MBEDTLS_X509_SAN_RFC822_NAME:       step = 6; t = 'E'; break;
-+		case MBEDTLS_X509_SAN_DNS_NAME:                    t = 'D'; break;
-+		case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: t = 'U'; break;
-+		default: continue;
-+		}
-+
-+		for (int i = 0; i < nlist; ++i) {
-+			/* step over "EMAIL:" or "DNS:" or "URI:" in list[i].p */
-+			/* Note: v is not '\0'-terminated, but is a known length vlen,
-+			 * so okay to pass to os_strncasecmp() even though not z-string */
-+			if (cur->buf.len == list[i].n - step && t == *list[i].p
-+			    && 0 == os_strncasecmp((char *)cur->buf.p,
-+			                           list[i].p+step, cur->buf.len)) {
-+				return 1; /* match */
-+			}
-+		}
-+	}
-+	return 0; /* no match */
-+}
-+
-+
-+static int
-+tls_mbedtls_match_suffix(const char *v, size_t vlen,
-+                         const struct mlist *list, int nlist, int full)
-+{
-+	/* Note: v is not '\0'-terminated, but is a known length vlen,
-+	 * so okay to pass to os_strncasecmp() even though not z-string */
-+	for (int i = 0; i < nlist; ++i) {
-+		size_t n = list[i].n;
-+		if ((n == vlen || (n < vlen && v[vlen-n-1] == '.' && !full))
-+		    && 0 == os_strncasecmp(v+vlen-n, list[i].p, n))
-+			return 1; /* match */
-+	}
-+	return 0; /* no match */
-+}
-+
-+
-+static int
-+tls_mbedtls_match_suffixes(mbedtls_x509_crt *crt, const char *match, int full)
-+{
-+	/* RFE: this could be pre-parsed into structured data at config time */
-+	struct mlist list[256]; /*(much larger than expected)*/
-+	int nlist = 0;
-+	for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
-+		tok = os_strchr(s, ';');
-+		list[nlist].p = s;
-+		list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
-+		if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
-+			wpa_printf(MSG_INFO, "MTLS: excessive suffix match '%s'", match);
-+			break; /* truncate huge list and continue */
-+		}
-+	}
-+
-+	/* check subjectAltNames */
-+	if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME)) {
-+		const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
-+		for (; cur != NULL; cur = cur->next) {
-+			const unsigned char san_type = (unsigned char)cur->buf.tag
-+			                             & MBEDTLS_ASN1_TAG_VALUE_MASK;
-+			if (san_type == MBEDTLS_X509_SAN_DNS_NAME
-+			    && tls_mbedtls_match_suffix((char *)cur->buf.p,
-+			                                cur->buf.len,
-+			                                list, nlist, full)) {
-+				return 1; /* match */
-+			}
-+		}
-+	}
-+
-+	/* check subject CN */
-+	const mbedtls_x509_name *name = &crt->subject;
-+	for (; name != NULL; name = name->next) {
-+		if (name->oid.p && MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid) == 0)
-+			break;
-+	}
-+	if (name && tls_mbedtls_match_suffix((char *)name->val.p, name->val.len,
-+	                                     list, nlist, full)) {
-+		return 1; /* match */
-+	}
-+
-+	return 0; /* no match */
-+}
-+
-+
-+static int
-+tls_mbedtls_match_dn_field(mbedtls_x509_crt *crt, const char *match)
-+{
-+	/* RFE: this could be pre-parsed into structured data at config time */
-+	struct mlistoid { const char *p; size_t n;
-+	                  const char *oid; size_t olen;
-+	                  int prefix; };
-+	struct mlistoid list[32]; /*(much larger than expected)*/
-+	int nlist = 0;
-+	for (const char *s = match, *tok, *e; *s; s = tok ? tok+1 : "") {
-+		tok = os_strchr(s, '/');
-+		list[nlist].oid = NULL;
-+		list[nlist].olen = 0;
-+		list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
-+		e = memchr(s, '=', list[nlist].n);
-+		if (e == NULL) {
-+			if (list[nlist].n == 0)
-+				continue; /* skip consecutive, repeated '/' */
-+			if (list[nlist].n == 1 && *s == '*') {
-+				/* special-case "*" to match any OID and value */
-+				s = e = "=*";
-+				list[nlist].n = 2;
-+				list[nlist].oid = "";
-+			}
-+			else {
-+				wpa_printf(MSG_INFO,
-+				           "MTLS: invalid check_cert_subject '%s' missing '='",
-+				           match);
-+				return 0;
-+			}
-+		}
-+		switch (e - s) {
-+		case 1:
-+			if (*s == 'C') {
-+				list[nlist].oid  = MBEDTLS_OID_AT_COUNTRY;
-+				list[nlist].olen = sizeof(MBEDTLS_OID_AT_COUNTRY)-1;
-+			}
-+			else if (*s == 'L') {
-+				list[nlist].oid  = MBEDTLS_OID_AT_LOCALITY;
-+				list[nlist].olen = sizeof(MBEDTLS_OID_AT_LOCALITY)-1;
-+			}
-+			else if (*s == 'O') {
-+				list[nlist].oid  = MBEDTLS_OID_AT_ORGANIZATION;
-+				list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORGANIZATION)-1;
-+			}
-+			break;
-+		case 2:
-+			if (s[0] == 'C' && s[1] == 'N') {
-+				list[nlist].oid  = MBEDTLS_OID_AT_CN;
-+				list[nlist].olen = sizeof(MBEDTLS_OID_AT_CN)-1;
-+			}
-+			else if (s[0] == 'S' && s[1] == 'T') {
-+				list[nlist].oid  = MBEDTLS_OID_AT_STATE;
-+				list[nlist].olen = sizeof(MBEDTLS_OID_AT_STATE)-1;
-+			}
-+			else if (s[0] == 'O' && s[1] == 'U') {
-+				list[nlist].oid  = MBEDTLS_OID_AT_ORG_UNIT;
-+				list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORG_UNIT)-1;
-+			}
-+			break;
-+		case 12:
-+			if (os_memcmp(s, "emailAddress", 12) == 0) {
-+				list[nlist].oid  = MBEDTLS_OID_PKCS9_EMAIL;
-+				list[nlist].olen = sizeof(MBEDTLS_OID_PKCS9_EMAIL)-1;
-+			}
-+			break;
-+		default:
-+			break;
-+		}
-+		if (list[nlist].oid == NULL) {
-+			wpa_printf(MSG_INFO,
-+			           "MTLS: Unknown field in check_cert_subject '%s'",
-+			           match);
-+			return 0;
-+		}
-+		list[nlist].n -= (size_t)(++e - s);
-+		list[nlist].p = e;
-+		if (list[nlist].n && e[list[nlist].n-1] == '*') {
-+			--list[nlist].n;
-+			list[nlist].prefix = 1;
-+		}
-+		/*(could easily add support for suffix matches if value begins with '*',
-+		 * but suffix match is not currently supported by other TLS modules)*/
-+
-+		if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
-+			wpa_printf(MSG_INFO,
-+			           "MTLS: excessive check_cert_subject match '%s'",
-+			           match);
-+			break; /* truncate huge list and continue */
-+		}
-+	}
-+
-+	/* each component in match string must match cert Subject in order listed
-+	 * The behavior below preserves ordering but is slightly different than
-+	 * the grossly inefficient contortions implemented in tls_openssl.c */
-+	const mbedtls_x509_name *name = &crt->subject;
-+	for (int i = 0; i < nlist; ++i) {
-+		int found = 0;
-+		for (; name != NULL && !found; name = name->next) {
-+			if (!name->oid.p)
-+				continue;
-+			/* special-case "*" to match any OID and value */
-+			if (list[i].olen == 0) {
-+				found = 1;
-+				continue;
-+			}
-+			/* perform equalent of !MBEDTLS_OID_CMP() with oid ptr and len */
-+			if (list[i].olen != name->oid.len
-+			    || os_memcmp(list[i].oid, name->oid.p, name->oid.len) != 0)
-+				continue;
-+			/* Note: v is not '\0'-terminated, but is a known length vlen,
-+			 * so okay to pass to os_strncasecmp() even though not z-string */
-+			if ((list[i].prefix
-+			      ? list[i].n <= name->val.len  /* prefix match */
-+			      : list[i].n == name->val.len) /* full match */
-+			    && 0 == os_strncasecmp((char *)name->val.p,
-+			                           list[i].p, list[i].n)) {
-+				found = 1;
-+				continue;
-+			}
-+		}
-+		if (!found)
-+			return 0; /* no match */
-+	}
-+	return 1; /* match */
-+}
-+
-+
-+__attribute_cold__
-+static void
-+tls_mbedtls_verify_fail_event (mbedtls_x509_crt *crt, int depth,
-+                               const char *errmsg, enum tls_fail_reason reason)
-+{
-+	struct tls_config *init_conf = &tls_ctx_global.init_conf;
-+	if (init_conf->event_cb == NULL)
-+		return;
-+
-+	struct wpabuf *certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
-+	char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
-+	if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
-+		subject[0] = '\0';
-+	union tls_event_data ev;
-+	os_memset(&ev, 0, sizeof(ev));
-+	ev.cert_fail.reason = reason;
-+	ev.cert_fail.depth = depth;
-+	ev.cert_fail.subject = subject;
-+	ev.cert_fail.reason_txt = errmsg;
-+	ev.cert_fail.cert = certbuf;
-+
-+	init_conf->event_cb(init_conf->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
-+
-+	wpabuf_free(certbuf);
-+}
-+
-+
-+__attribute_noinline__
-+static void
-+tls_mbedtls_verify_cert_event (struct tls_connection *conn,
-+                               mbedtls_x509_crt *crt, int depth)
-+{
-+	struct tls_config *init_conf = &tls_ctx_global.init_conf;
-+	if (init_conf->event_cb == NULL)
-+		return;
-+
-+	struct wpabuf *certbuf = NULL;
-+	union tls_event_data ev;
-+	os_memset(&ev, 0, sizeof(ev));
-+
-+  #ifdef MBEDTLS_SHA256_C
-+	u8 hash[SHA256_DIGEST_LENGTH];
-+	const u8 *addr[] = { (u8 *)crt->raw.p };
-+	if (sha256_vector(1, addr, &crt->raw.len, hash) == 0) {
-+		ev.peer_cert.hash = hash;
-+		ev.peer_cert.hash_len = sizeof(hash);
-+	}
-+  #endif
-+	ev.peer_cert.depth = depth;
-+	char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
-+	if (depth == 0)
-+		ev.peer_cert.subject = conn->peer_subject;
-+	if (ev.peer_cert.subject == NULL) {
-+		ev.peer_cert.subject = subject;
-+		if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
-+			subject[0] = '\0';
-+	}
-+
-+	char serial_num[128+1];
-+	ev.peer_cert.serial_num =
-+	  tls_mbedtls_peer_serial_num(crt, serial_num, sizeof(serial_num));
-+
-+	const mbedtls_x509_sequence *cur;
-+
-+	cur = NULL;
-+	if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
-+		cur = &crt->subject_alt_names;
-+	for (; cur != NULL; cur = cur->next) {
-+		const unsigned char san_type = (unsigned char)cur->buf.tag
-+		                             & MBEDTLS_ASN1_TAG_VALUE_MASK;
-+		size_t prelen = 4;
-+		const char *pre;
-+		switch (san_type) {
-+		case MBEDTLS_X509_SAN_RFC822_NAME:     prelen = 6; pre = "EMAIL:";break;
-+		case MBEDTLS_X509_SAN_DNS_NAME:                    pre = "DNS:";  break;
-+		case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: pre = "URI:";  break;
-+		default: continue;
-+		}
-+
-+		char *pos = os_malloc(prelen + cur->buf.len + 1);
-+		if (pos == NULL)
-+			break;
-+		ev.peer_cert.altsubject[ev.peer_cert.num_altsubject] = pos;
-+		os_memcpy(pos, pre, prelen);
-+		/* data should be properly backslash-escaped if needed,
-+		 * so code below does not re-escape, but does replace CTLs */
-+		/*os_memcpy(pos+prelen, cur->buf.p, cur->buf.len);*/
-+		/*pos[prelen+cur->buf.len] = '\0';*/
-+		pos += prelen;
-+		for (size_t i = 0; i < cur->buf.len; ++i) {
-+			unsigned char c = cur->buf.p[i];
-+			*pos++ = (c >= 32 && c != 127) ? c : '?';
-+		}
-+		*pos = '\0';
-+
-+		if (++ev.peer_cert.num_altsubject == TLS_MAX_ALT_SUBJECT)
-+			break;
-+	}
-+
-+	cur = NULL;
-+	if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_CERTIFICATE_POLICIES))
-+		cur = &crt->certificate_policies;
-+	for (; cur != NULL; cur = cur->next) {
-+		if (cur->buf.len != 11) /* len of OID_TOD_STRICT or OID_TOD_TOFU */
-+			continue;
-+		/* TOD-STRICT "1.3.6.1.4.1.40808.1.3.1" */
-+		/* TOD-TOFU   "1.3.6.1.4.1.40808.1.3.2" */
-+		#define OID_TOD_STRICT "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x01"
-+		#define OID_TOD_TOFU   "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x02"
-+		if (os_memcmp(cur->buf.p,
-+		              OID_TOD_STRICT, sizeof(OID_TOD_STRICT)-1) == 0) {
-+			ev.peer_cert.tod = 1; /* TOD-STRICT */
-+			break;
-+		}
-+		if (os_memcmp(cur->buf.p,
-+		              OID_TOD_TOFU, sizeof(OID_TOD_TOFU)-1) == 0) {
-+			ev.peer_cert.tod = 2; /* TOD-TOFU */
-+			break;
-+		}
-+	}
-+
-+	struct tls_conf *tls_conf = conn->tls_conf;
-+	if (tls_conf->ca_cert_probe || (tls_conf->flags & TLS_CONN_EXT_CERT_CHECK)
-+	    || init_conf->cert_in_cb) {
-+		certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
-+		ev.peer_cert.cert = certbuf;
-+	}
-+
-+	init_conf->event_cb(init_conf->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
-+
-+	wpabuf_free(certbuf);
-+	char **altsubject;
-+	*(const char ***)&altsubject = ev.peer_cert.altsubject;
-+	for (size_t i = 0; i < ev.peer_cert.num_altsubject; ++i)
-+		os_free(altsubject[i]);
-+}
-+
-+
-+static int
-+tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
-+{
-+	/* XXX: N.B. verify code not carefully tested besides hwsim tests
-+	 *
-+	 * RFE: mbedtls_x509_crt_verify_info() and enhance log trace messages
-+	 * RFE: review and add support for additional TLS_CONN_* flags
-+	 * not handling OCSP (not available in mbedtls)
-+	 * ... */
-+
-+	struct tls_connection *conn = (struct tls_connection *)arg;
-+	struct tls_conf *tls_conf = conn->tls_conf;
-+	uint32_t flags_in = *flags;
-+
-+	if (depth > 8) { /*(depth 8 picked as arbitrary limit)*/
-+		emsg(MSG_WARNING, "client cert chain too long");
-+		*flags |= MBEDTLS_X509_BADCERT_OTHER; /* cert chain too long */
-+		tls_mbedtls_verify_fail_event(crt, depth,
-+			                      "client cert chain too long",
-+		                              TLS_FAIL_BAD_CERTIFICATE);
-+	}
-+	else if (tls_conf->verify_depth0_only) {
-+		if (depth > 0)
-+			*flags = 0;
-+		else {
-+		  #ifdef MBEDTLS_SHA256_C
-+			u8 hash[SHA256_DIGEST_LENGTH];
-+			const u8 *addr[] = { (u8 *)crt->raw.p };
-+			if (sha256_vector(1, addr, &crt->raw.len, hash) < 0
-+			    || os_memcmp(tls_conf->ca_cert_hash, hash, sizeof(hash)) != 0) {
-+				*flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED;
-+				tls_mbedtls_verify_fail_event(crt, depth,
-+			                                      "cert hash mismatch",
-+				                              TLS_FAIL_UNTRUSTED);
-+			}
-+			else /* hash matches; ignore other issues *except* if revoked)*/
-+				*flags &= MBEDTLS_X509_BADCERT_REVOKED;
-+		  #endif
-+		}
-+	}
-+	else if (depth == 0) {
-+		if (!conn->peer_subject)
-+			tls_mbedtls_set_peer_subject(conn, crt);
-+		/*(use same labels to tls_mbedtls_verify_fail_event() as used in
-+		 * other TLS modules so that hwsim tests find exact string match)*/
-+		if (!conn->peer_subject) { /* error copying subject string */
-+			*flags |= MBEDTLS_X509_BADCERT_OTHER;
-+			tls_mbedtls_verify_fail_event(crt, depth,
-+			                              "internal error",
-+			                              TLS_FAIL_UNSPECIFIED);
-+		}
-+		/*(use os_strstr() for subject match as is done in tls_mbedtls.c
-+		 * to follow the same behavior, even though a suffix match would
-+		 * make more sense.  Also, note that strstr match does not
-+		 * normalize whitespace (between components) for comparison)*/
-+		else if (tls_conf->subject_match
-+		         && os_strstr(conn->peer_subject,
-+		                      tls_conf->subject_match) == NULL) {
-+			wpa_printf(MSG_WARNING,
-+			           "MTLS: Subject '%s' did not match with '%s'",
-+			           conn->peer_subject, tls_conf->subject_match);
-+			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+			tls_mbedtls_verify_fail_event(crt, depth,
-+			                              "Subject mismatch",
-+			                              TLS_FAIL_SUBJECT_MISMATCH);
-+		}
-+		if (tls_conf->altsubject_match
-+		    && !tls_mbedtls_match_altsubject(crt, tls_conf->altsubject_match)) {
-+			wpa_printf(MSG_WARNING,
-+				   "MTLS: altSubjectName match '%s' not found",
-+			           tls_conf->altsubject_match);
-+			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+			tls_mbedtls_verify_fail_event(crt, depth,
-+			                              "AltSubject mismatch",
-+			                              TLS_FAIL_ALTSUBJECT_MISMATCH);
-+		}
-+		if (tls_conf->suffix_match
-+		    && !tls_mbedtls_match_suffixes(crt, tls_conf->suffix_match, 0)) {
-+			wpa_printf(MSG_WARNING,
-+			           "MTLS: Domain suffix match '%s' not found",
-+				   tls_conf->suffix_match);
-+			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+			tls_mbedtls_verify_fail_event(crt, depth,
-+			                              "Domain suffix mismatch",
-+			                              TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
-+		}
-+		if (tls_conf->domain_match
-+		    && !tls_mbedtls_match_suffixes(crt, tls_conf->domain_match, 1)) {
-+			wpa_printf(MSG_WARNING,
-+			           "MTLS: Domain match '%s' not found",
-+				   tls_conf->domain_match);
-+			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+			tls_mbedtls_verify_fail_event(crt, depth,
-+			                              "Domain mismatch",
-+			                              TLS_FAIL_DOMAIN_MISMATCH);
-+		}
-+		if (tls_conf->check_cert_subject
-+		    && !tls_mbedtls_match_dn_field(crt, tls_conf->check_cert_subject)) {
-+			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+			tls_mbedtls_verify_fail_event(crt, depth,
-+			                              "Distinguished Name",
-+			                              TLS_FAIL_DN_MISMATCH);
-+		}
-+		if (tls_conf->flags & TLS_CONN_SUITEB) {
-+			/* check RSA modulus size (public key bitlen) */
-+			const mbedtls_pk_type_t pk_alg = mbedtls_pk_get_type(&crt->pk);
-+			if ((pk_alg == MBEDTLS_PK_RSA || pk_alg == MBEDTLS_PK_RSASSA_PSS)
-+			    && mbedtls_pk_get_bitlen(&crt->pk) < 3072) {
-+				/* hwsim suite_b RSA tests expect 3072
-+				 *   suite_b_192_rsa_ecdhe_radius_rsa2048_client
-+				 *   suite_b_192_rsa_dhe_radius_rsa2048_client */
-+				*flags |= MBEDTLS_X509_BADCERT_BAD_KEY;
-+				tls_mbedtls_verify_fail_event(crt, depth,
-+				                              "Insufficient RSA modulus size",
-+				                              TLS_FAIL_INSUFFICIENT_KEY_LEN);
-+			}
-+		}
-+		if (tls_conf->check_crl && tls_conf->crl == NULL) {
-+			/* see tests/hwsim test_ap_eap.py ap_wpa2_eap_tls_check_crl */
-+			emsg(MSG_WARNING, "check_crl set but no CRL loaded; reject all?");
-+			*flags |= MBEDTLS_X509_BADCERT_OTHER;
-+			tls_mbedtls_verify_fail_event(crt, depth,
-+				                      "check_crl set but no CRL loaded; "
-+			                              "reject all?",
-+			                              TLS_FAIL_BAD_CERTIFICATE);
-+		}
-+	}
-+	else {
-+		if (tls_conf->check_crl != 2) /* 2 == verify CRLs for all certs */
-+			*flags &= ~MBEDTLS_X509_BADCERT_REVOKED;
-+	}
-+
-+	if (!tls_conf->check_crl_strict) {
-+		*flags &= ~MBEDTLS_X509_BADCRL_EXPIRED;
-+		*flags &= ~MBEDTLS_X509_BADCRL_FUTURE;
-+	}
-+
-+	if (tls_conf->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
-+		*flags &= ~MBEDTLS_X509_BADCERT_EXPIRED;
-+		*flags &= ~MBEDTLS_X509_BADCERT_FUTURE;
-+	}
-+
-+	tls_mbedtls_verify_cert_event(conn, crt, depth);
-+
-+	if (*flags) {
-+		if (*flags & (MBEDTLS_X509_BADCERT_NOT_TRUSTED
-+		             |MBEDTLS_X509_BADCERT_CN_MISMATCH
-+		             |MBEDTLS_X509_BADCERT_REVOKED)) {
-+			emsg(MSG_WARNING, "client cert not trusted");
-+		}
-+		/* report event if flags set but no additional flags set above */
-+		/* (could translate flags to more detailed TLS_FAIL_* if needed) */
-+		if (!(*flags & ~flags_in)) {
-+			enum tls_fail_reason reason = TLS_FAIL_UNSPECIFIED;
-+			const char *errmsg = "cert verify fail unspecified";
-+			if (*flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) {
-+				reason = TLS_FAIL_UNTRUSTED;
-+				errmsg = "certificate not trusted";
-+			}
-+			if (*flags & MBEDTLS_X509_BADCERT_REVOKED) {
-+				reason = TLS_FAIL_REVOKED;
-+				errmsg = "certificate has been revoked";
-+			}
-+			if (*flags & MBEDTLS_X509_BADCERT_FUTURE) {
-+				reason = TLS_FAIL_NOT_YET_VALID;
-+				errmsg = "certificate not yet valid";
-+			}
-+			if (*flags & MBEDTLS_X509_BADCERT_EXPIRED) {
-+				reason = TLS_FAIL_EXPIRED;
-+				errmsg = "certificate has expired";
-+			}
-+			if (*flags & MBEDTLS_X509_BADCERT_BAD_MD) {
-+				reason = TLS_FAIL_BAD_CERTIFICATE;
-+				errmsg = "certificate uses insecure algorithm";
-+			}
-+			tls_mbedtls_verify_fail_event(crt, depth, errmsg, reason);
-+		}
-+	  #if 0
-+		/* ??? send (again) cert events for all certs in chain ???
-+		 * (should already have been called for greater depths) */
-+		/* tls_openssl.c:tls_verify_cb() sends cert events for all certs
-+		 * in chain if certificate validation fails, but sends all events
-+		 * with depth set to 0 (might be a bug) */
-+		if (depth > 0) {
-+			int pdepth = depth + 1;
-+			for (mbedtls_x509_crt *pcrt; (pcrt = crt->next); ++pdepth) {
-+				tls_mbedtls_verify_cert_event(conn, pcrt, pdepth);
-+			}
-+		}
-+	  #endif
-+		/*(do not preserve subject if verification failed but was optional)*/
-+		if (depth == 0 && conn->peer_subject) {
-+			os_free(conn->peer_subject);
-+			conn->peer_subject = NULL;
-+		}
-+	}
-+	else if (depth == 0) {
-+		struct tls_config *init_conf = &tls_ctx_global.init_conf;
-+		if (tls_conf->ca_cert_probe) {
-+			/* reject server certificate on probe-only run */
-+			*flags |= MBEDTLS_X509_BADCERT_OTHER;
-+			tls_mbedtls_verify_fail_event(crt, depth,
-+			                              "server chain probe",
-+			                              TLS_FAIL_SERVER_CHAIN_PROBE);
-+		}
-+		else if (init_conf->event_cb) {
-+			/* ??? send event as soon as depth == 0 is verified ???
-+			 * What about rest of chain?
-+			 * Follows tls_mbedtls.c behavior: */
-+			init_conf->event_cb(init_conf->cb_ctx,
-+			                    TLS_CERT_CHAIN_SUCCESS, NULL);
-+		}
-+	}
-+
-+	return 0;
-+}
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index e672a1787..3e3e309f4 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -971,6 +971,9 @@ struct wpa_driver_associate_params {
- 	 * responsible for selecting with which BSS to associate. */
- 	const u8 *bssid;
- 
-+	unsigned char rates[32];
-+	int mcast_rate;
-+
- 	/**
- 	 * bssid_hint - BSSID of a proposed AP
- 	 *
-@@ -1873,6 +1876,7 @@ struct wpa_driver_mesh_join_params {
- #define WPA_DRIVER_MESH_FLAG_AMPE	0x00000008
- 	unsigned int flags;
- 	bool handle_dfs;
-+	int mcast_rate;
- };
- 
- struct wpa_driver_set_key_params {
-@@ -2340,6 +2344,9 @@ struct wpa_driver_capa {
- 	/** Maximum number of iterations in a single scan plan */
- 	u32 max_sched_scan_plan_iterations;
- 
-+	/** Maximum number of extra IE bytes for scans */
-+	u16 max_scan_ie_len;
-+
- 	/** Whether sched_scan (offloaded scanning) is supported */
- 	int sched_scan_supported;
- 
-@@ -3861,6 +3868,25 @@ struct wpa_driver_ops {
- 	int (*if_remove)(void *priv, enum wpa_driver_if_type type,
- 			 const char *ifname);
- 
-+	/**
-+	 * if_rename - Rename a virtual interface
-+	 * @priv: Private driver interface data
-+	 * @type: Interface type
-+	 * @ifname: Interface name of the virtual interface to be renamed
-+	 *	    (NULL when renaming the AP BSS interface)
-+	 * @new_name: New interface name of the virtual interface
-+	 * Returns: 0 on success, -1 on failure
-+	 */
-+	int (*if_rename)(void *priv, enum wpa_driver_if_type type,
-+			 const char *ifname, const char *new_name);
-+
-+	/**
-+	 * set_first_bss - Make a virtual interface the first (primary) bss
-+	 * @priv: Private driver interface data
-+	 * Returns: 0 on success, -1 on failure
-+	 */
-+	int (*set_first_bss)(void *priv);
-+
- 	/**
- 	 * set_sta_vlan - Bind a station into a specific interface (AP only)
- 	 * @priv: Private driver interface data
-@@ -4265,7 +4291,7 @@ struct wpa_driver_ops {
- 	 * Returns: 0 on success, negative (<0) on failure
- 	 */
- 	int (*br_set_net_param)(void *priv, enum drv_br_net_param param,
--				unsigned int val);
-+				const char *ifname, unsigned int val);
- 
- 	/**
- 	 * get_wowlan - Get wake-on-wireless status
-@@ -6559,6 +6585,7 @@ union wpa_event_data {
- 
- 	/**
- 	 * struct ch_switch
-+	 * @count: Count until channel switch activates
- 	 * @freq: Frequency of new channel in MHz
- 	 * @ht_enabled: Whether this is an HT channel
- 	 * @ch_offset: Secondary channel offset
-@@ -6569,6 +6596,7 @@ union wpa_event_data {
- 	 * @punct_bitmap: Puncturing bitmap
- 	 */
- 	struct ch_switch {
-+		int count;
- 		int freq;
- 		int ht_enabled;
- 		int ch_offset;
-@@ -6816,8 +6844,8 @@ union wpa_event_data {
-  * Driver wrapper code should call this function whenever an event is received
-  * from the driver.
-  */
--void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
--			  union wpa_event_data *data);
-+extern void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
-+				    union wpa_event_data *data);
- 
- /**
-  * wpa_supplicant_event_global - Report a driver event for wpa_supplicant
-@@ -6829,7 +6857,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
-  * Same as wpa_supplicant_event(), but we search for the interface in
-  * wpa_global.
-  */
--void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
-+extern void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
- 				 union wpa_event_data *data);
- 
- /*
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 9ac621ae6..6778ad369 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -78,6 +78,16 @@ enum nlmsgerr_attrs {
- 
- #endif /* ANDROID */
- 
-+static void handle_nl_debug_hook(struct nl_msg *msg, int tx)
-+{
-+	const struct nlmsghdr *nlh;
-+
-+	if (!wpa_netlink_hook)
-+		return;
-+
-+	nlh = nlmsg_hdr(msg);
-+	wpa_netlink_hook(tx, nlh, nlh->nlmsg_len);
-+}
- 
- static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
- {
-@@ -432,6 +442,11 @@ static int no_seq_check(struct nl_msg *msg, void *arg)
- 	return NL_OK;
- }
- 
-+static int debug_handler(struct nl_msg *msg, void *arg)
-+{
-+	handle_nl_debug_hook(msg, 0);
-+	return NL_OK;
-+}
- 
- static void nl80211_nlmsg_clear(struct nl_msg *msg)
- {
-@@ -505,6 +520,7 @@ int send_and_recv(struct nl80211_global *global,
- 	if (!msg)
- 		return -ENOMEM;
- 
-+	handle_nl_debug_hook(msg, 1);
- 	err.err = -ENOMEM;
- 
- 	s_nl_cb = nl_socket_get_cb(nl_handle);
-@@ -539,6 +555,7 @@ int send_and_recv(struct nl80211_global *global,
- 	err.orig_msg = msg;
- 	err.err_info = err_info;
- 
-+	nl_cb_set(cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
- 	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
- 	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err.err);
- 	if (ack_handler_custom) {
-@@ -942,6 +959,7 @@ nl80211_get_wiphy_data_ap(struct i802_bss *bss)
- 			os_free(w);
- 			return NULL;
- 		}
-+		nl_cb_set(w->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
- 		nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
- 			  no_seq_check, NULL);
- 		nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
-@@ -1356,7 +1374,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
- 		}
- 		wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
- 			   namebuf, ifname);
--		if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
-+		if (drv->first_bss->ifindex != ifi->ifi_index) {
- 			wpa_printf(MSG_DEBUG,
- 				   "nl80211: Not the main interface (%s) - do not indicate interface down",
- 				   drv->first_bss->ifname);
-@@ -1392,7 +1410,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
- 		}
- 		wpa_printf(MSG_DEBUG, "nl80211: Interface up (%s/%s)",
- 			   namebuf, ifname);
--		if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
-+		if (drv->first_bss->ifindex != ifi->ifi_index) {
- 			wpa_printf(MSG_DEBUG,
- 				   "nl80211: Not the main interface (%s) - do not indicate interface up",
- 				   drv->first_bss->ifname);
-@@ -2038,6 +2056,7 @@ static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
- 	genl_family_put(family);
- 	nl_cache_free(cache);
- 
-+	nl_cb_set(global->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
- 	nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
- 		  no_seq_check, NULL);
- 	nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
-@@ -2208,6 +2227,7 @@ static int nl80211_init_bss(struct i802_bss *bss)
- 	if (!bss->nl_cb)
- 		return -1;
- 
-+	nl_cb_set(bss->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
- 	nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
- 		  no_seq_check, NULL);
- 	nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
-@@ -5485,7 +5505,7 @@ static int nl80211_set_channel(struct i802_bss *bss,
- 		   freq->he_enabled, freq->eht_enabled, freq->bandwidth,
- 		   freq->center_freq1, freq->center_freq2);
- 
--	msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
-+	msg = nl80211_bss_msg(bss, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
- 			      NL80211_CMD_SET_WIPHY);
- 	if (!msg || nl80211_put_freq_params(msg, freq) < 0) {
- 		nlmsg_free(msg);
-@@ -5858,26 +5878,29 @@ fail:
- 
- static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
- {
--#ifdef CONFIG_LIBNL3_ROUTE
- 	struct wpa_driver_nl80211_data *drv = bss->drv;
--	struct rtnl_neigh *rn;
--	struct nl_addr *nl_addr;
-+	struct ndmsg nhdr = {
-+		.ndm_state = NUD_PERMANENT,
-+		.ndm_ifindex = bss->ifindex,
-+		.ndm_family = AF_BRIDGE,
-+	};
-+	struct nl_msg *msg;
- 	int err;
- 
--	rn = rtnl_neigh_alloc();
--	if (!rn)
-+	msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
-+	if (!msg)
- 		return;
- 
--	rtnl_neigh_set_family(rn, AF_BRIDGE);
--	rtnl_neigh_set_ifindex(rn, bss->ifindex);
--	nl_addr = nl_addr_build(AF_BRIDGE, (void *) addr, ETH_ALEN);
--	if (!nl_addr) {
--		rtnl_neigh_put(rn);
--		return;
--	}
--	rtnl_neigh_set_lladdr(rn, nl_addr);
-+	if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
-+		goto errout;
- 
--	err = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
-+	if (nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *)addr))
-+		goto errout;
-+
-+	if (nl_send_auto_complete(drv->rtnl_sk, msg) < 0)
-+		goto errout;
-+
-+	err = nl_wait_for_ack(drv->rtnl_sk);
- 	if (err < 0) {
- 		wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry delete for "
- 			   MACSTR " ifindex=%d failed: %s", MAC2STR(addr),
-@@ -5887,9 +5910,8 @@ static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
- 			   MACSTR, MAC2STR(addr));
- 	}
- 
--	nl_addr_put(nl_addr);
--	rtnl_neigh_put(rn);
--#endif /* CONFIG_LIBNL3_ROUTE */
-+errout:
-+	nlmsg_free(msg);
- }
- 
- 
-@@ -6178,6 +6200,8 @@ static void nl80211_teardown_ap(struct i802_bss *bss)
- 	nl80211_put_wiphy_data_ap(bss);
- 	if (bss->flink)
- 		bss->flink->beacon_set = 0;
-+
-+	wpa_driver_nl80211_del_beacon_all(bss);
- }
- 
- 
-@@ -8566,6 +8590,7 @@ static void *i802_init(struct hostapd_data *hapd,
- 	char master_ifname[IFNAMSIZ];
- 	int ifindex, br_ifindex = 0;
- 	int br_added = 0;
-+	int err;
- 
- 	bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
- 					  params->global_priv, 1,
-@@ -8625,24 +8650,18 @@ static void *i802_init(struct hostapd_data *hapd,
- 	    (params->num_bridge == 0 || !params->bridge[0]))
- 		add_ifidx(drv, br_ifindex, drv->ifindex);
- 
--#ifdef CONFIG_LIBNL3_ROUTE
--	if (bss->added_if_into_bridge || bss->already_in_bridge) {
--		int err;
--
--		drv->rtnl_sk = nl_socket_alloc();
--		if (drv->rtnl_sk == NULL) {
--			wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
--			goto failed;
--		}
-+	drv->rtnl_sk = nl_socket_alloc();
-+	if (drv->rtnl_sk == NULL) {
-+		wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
-+		goto failed;
-+	}
- 
--		err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
--		if (err) {
--			wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
--				   nl_geterror(err));
--			goto failed;
--		}
-+	err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
-+	if (err) {
-+		wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
-+			   nl_geterror(err));
-+		goto failed;
- 	}
--#endif /* CONFIG_LIBNL3_ROUTE */
- 
- 	if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
- 		wpa_printf(MSG_DEBUG,
-@@ -9000,6 +9019,7 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
- 		if (drv->first_bss->next) {
- 			drv->first_bss = drv->first_bss->next;
- 			drv->ctx = drv->first_bss->ctx;
-+			drv->ifindex = drv->first_bss->ifindex;
- 			os_free(bss);
- 		} else {
- 			wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to");
-@@ -9009,6 +9029,50 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
- 	return 0;
- }
- 
-+static int wpa_driver_nl80211_if_rename(struct i802_bss *bss,
-+					enum wpa_driver_if_type type,
-+					const char *ifname, const char *new_name)
-+{
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct ifinfomsg ifi = {
-+		.ifi_family = AF_UNSPEC,
-+		.ifi_index = bss->ifindex,
-+	};
-+	struct nl_msg *msg;
-+	int res = -ENOMEM;
-+
-+	if (ifname)
-+		ifi.ifi_index = if_nametoindex(ifname);
-+
-+	msg = nlmsg_alloc_simple(RTM_SETLINK, 0);
-+	if (!msg)
-+		return res;
-+
-+	if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
-+		goto out;
-+
-+	if (nla_put_string(msg, IFLA_IFNAME, new_name))
-+		goto out;
-+
-+	res = nl_send_auto_complete(drv->rtnl_sk, msg);
-+	if (res < 0)
-+		goto out;
-+
-+	res = nl_wait_for_ack(drv->rtnl_sk);
-+	if (res) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Renaming device %s to %s failed: %s",
-+			   ifname ? ifname : bss->ifname, new_name, nl_geterror(res));
-+		goto out;
-+	}
-+
-+	if (type == WPA_IF_AP_BSS && !ifname)
-+		os_strlcpy(bss->ifname, new_name, sizeof(bss->ifname));
-+
-+out:
-+	nlmsg_free(msg);
-+	return res;
-+}
- 
- static int cookie_handler(struct nl_msg *msg, void *arg)
- {
-@@ -10792,6 +10856,37 @@ static bool nl80211_is_drv_shared(void *priv, void *bss_ctx)
- #endif /* CONFIG_IEEE80211BE */
- 
- 
-+static int driver_nl80211_if_rename(void *priv, enum wpa_driver_if_type type,
-+				    const char *ifname, const char *new_name)
-+{
-+	struct i802_bss *bss = priv;
-+	return wpa_driver_nl80211_if_rename(bss, type, ifname, new_name);
-+}
-+
-+
-+static int driver_nl80211_set_first_bss(void *priv)
-+{
-+	struct i802_bss *bss = priv, *tbss;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+
-+	if (drv->first_bss == bss)
-+		return 0;
-+
-+	for (tbss = drv->first_bss; tbss; tbss = tbss->next) {
-+		if (tbss->next != bss)
-+			continue;
-+
-+		tbss->next = bss->next;
-+		bss->next = drv->first_bss;
-+		drv->first_bss = bss;
-+		drv->ctx = bss->ctx;
-+		return 0;
-+	}
-+
-+	return -1;
-+}
-+
-+
- static int driver_nl80211_send_mlme(void *priv, const u8 *data,
- 				    size_t data_len, int noack,
- 				    unsigned int freq,
-@@ -11294,6 +11389,10 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
- 	if (ret)
- 		goto error;
- 
-+	if (drv->nlmode == NL80211_IFTYPE_MESH_POINT) {
-+		nla_put_flag(msg, NL80211_ATTR_HANDLE_DFS);
-+	}
-+
- 	/* beacon_csa params */
- 	beacon_csa = nla_nest_start(msg, NL80211_ATTR_CSA_IES);
- 	if (!beacon_csa)
-@@ -11940,6 +12039,18 @@ static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id,
- }
- 
- 
-+static int nl80211_put_mcast_rate(struct nl_msg *msg, int mcast_rate)
-+{
-+	if (mcast_rate > 0) {
-+		wpa_printf(MSG_DEBUG, "  * mcast_rate=%.1f",
-+			   (double)mcast_rate / 10);
-+		return nla_put_u32(msg, NL80211_ATTR_MCAST_RATE, mcast_rate);
-+	}
-+
-+	return 0;
-+}
-+
-+
- static int nl80211_put_mesh_config(struct nl_msg *msg,
- 				   struct wpa_driver_mesh_bss_params *params)
- {
-@@ -12001,6 +12112,7 @@ static int nl80211_join_mesh(struct i802_bss *bss,
- 	    nl80211_put_basic_rates(msg, params->basic_rates) ||
- 	    nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
- 	    nl80211_put_beacon_int(msg, params->beacon_int) ||
-+	    nl80211_put_mcast_rate(msg, params->mcast_rate) ||
- 	    nl80211_put_dtim_period(msg, params->dtim_period))
- 		goto fail;
- 
-@@ -12156,13 +12268,14 @@ static int wpa_driver_br_add_ip_neigh(void *priv, u8 version,
- 				      const u8 *ipaddr, int prefixlen,
- 				      const u8 *addr)
- {
--#ifdef CONFIG_LIBNL3_ROUTE
- 	struct i802_bss *bss = priv;
- 	struct wpa_driver_nl80211_data *drv = bss->drv;
--	struct rtnl_neigh *rn;
--	struct nl_addr *nl_ipaddr = NULL;
--	struct nl_addr *nl_lladdr = NULL;
--	int family, addrsize;
-+	struct ndmsg nhdr = {
-+		.ndm_state = NUD_PERMANENT,
-+		.ndm_ifindex = bss->br_ifindex,
-+	};
-+	struct nl_msg *msg;
-+	int addrsize;
- 	int res;
- 
- 	if (!ipaddr || prefixlen == 0 || !addr)
-@@ -12181,85 +12294,66 @@ static int wpa_driver_br_add_ip_neigh(void *priv, u8 version,
- 	}
- 
- 	if (version == 4) {
--		family = AF_INET;
-+		nhdr.ndm_family = AF_INET;
- 		addrsize = 4;
- 	} else if (version == 6) {
--		family = AF_INET6;
-+		nhdr.ndm_family = AF_INET6;
- 		addrsize = 16;
- 	} else {
- 		return -EINVAL;
- 	}
- 
--	rn = rtnl_neigh_alloc();
--	if (rn == NULL)
-+	msg = nlmsg_alloc_simple(RTM_NEWNEIGH, NLM_F_CREATE);
-+	if (!msg)
- 		return -ENOMEM;
- 
--	/* set the destination ip address for neigh */
--	nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
--	if (nl_ipaddr == NULL) {
--		wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
--		res = -ENOMEM;
-+	res = -ENOMEM;
-+	if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
- 		goto errout;
--	}
--	nl_addr_set_prefixlen(nl_ipaddr, prefixlen);
--	res = rtnl_neigh_set_dst(rn, nl_ipaddr);
--	if (res) {
--		wpa_printf(MSG_DEBUG,
--			   "nl80211: neigh set destination addr failed");
-+
-+	if (nla_put(msg, NDA_DST, addrsize, (void *)ipaddr))
- 		goto errout;
--	}
- 
--	/* set the corresponding lladdr for neigh */
--	nl_lladdr = nl_addr_build(AF_BRIDGE, (u8 *) addr, ETH_ALEN);
--	if (nl_lladdr == NULL) {
--		wpa_printf(MSG_DEBUG, "nl80211: neigh set lladdr failed");
--		res = -ENOMEM;
-+	if (nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *)addr))
- 		goto errout;
--	}
--	rtnl_neigh_set_lladdr(rn, nl_lladdr);
- 
--	rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
--	rtnl_neigh_set_state(rn, NUD_PERMANENT);
-+	res = nl_send_auto_complete(drv->rtnl_sk, msg);
-+	if (res < 0)
-+		goto errout;
- 
--	res = rtnl_neigh_add(drv->rtnl_sk, rn, NLM_F_CREATE);
-+	res = nl_wait_for_ack(drv->rtnl_sk);
- 	if (res) {
- 		wpa_printf(MSG_DEBUG,
- 			   "nl80211: Adding bridge ip neigh failed: %s",
- 			   nl_geterror(res));
- 	}
- errout:
--	if (nl_lladdr)
--		nl_addr_put(nl_lladdr);
--	if (nl_ipaddr)
--		nl_addr_put(nl_ipaddr);
--	if (rn)
--		rtnl_neigh_put(rn);
-+	nlmsg_free(msg);
- 	return res;
--#else /* CONFIG_LIBNL3_ROUTE */
--	return -1;
--#endif /* CONFIG_LIBNL3_ROUTE */
- }
- 
- 
- static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version,
- 					 const u8 *ipaddr)
- {
--#ifdef CONFIG_LIBNL3_ROUTE
- 	struct i802_bss *bss = priv;
- 	struct wpa_driver_nl80211_data *drv = bss->drv;
--	struct rtnl_neigh *rn;
--	struct nl_addr *nl_ipaddr;
--	int family, addrsize;
-+	struct ndmsg nhdr = {
-+		.ndm_state = NUD_PERMANENT,
-+		.ndm_ifindex = bss->br_ifindex,
-+	};
-+	struct nl_msg *msg;
-+	int addrsize;
- 	int res;
- 
- 	if (!ipaddr)
- 		return -EINVAL;
- 
- 	if (version == 4) {
--		family = AF_INET;
-+		nhdr.ndm_family = AF_INET;
- 		addrsize = 4;
- 	} else if (version == 6) {
--		family = AF_INET6;
-+		nhdr.ndm_family = AF_INET6;
- 		addrsize = 16;
- 	} else {
- 		return -EINVAL;
-@@ -12277,41 +12371,30 @@ static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version,
- 		return -1;
- 	}
- 
--	rn = rtnl_neigh_alloc();
--	if (rn == NULL)
-+	msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
-+	if (!msg)
- 		return -ENOMEM;
- 
--	/* set the destination ip address for neigh */
--	nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
--	if (nl_ipaddr == NULL) {
--		wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
--		res = -ENOMEM;
-+	res = -ENOMEM;
-+	if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
- 		goto errout;
--	}
--	res = rtnl_neigh_set_dst(rn, nl_ipaddr);
--	if (res) {
--		wpa_printf(MSG_DEBUG,
--			   "nl80211: neigh set destination addr failed");
-+
-+	if (nla_put(msg, NDA_DST, addrsize, (void *)ipaddr))
- 		goto errout;
--	}
- 
--	rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
-+	res = nl_send_auto_complete(drv->rtnl_sk, msg);
-+	if (res < 0)
-+		goto errout;
- 
--	res = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
-+	res = nl_wait_for_ack(drv->rtnl_sk);
- 	if (res) {
- 		wpa_printf(MSG_DEBUG,
- 			   "nl80211: Deleting bridge ip neigh failed: %s",
- 			   nl_geterror(res));
- 	}
- errout:
--	if (nl_ipaddr)
--		nl_addr_put(nl_ipaddr);
--	if (rn)
--		rtnl_neigh_put(rn);
-+	nlmsg_free(msg);
- 	return res;
--#else /* CONFIG_LIBNL3_ROUTE */
--	return -1;
--#endif /* CONFIG_LIBNL3_ROUTE */
- }
- 
- 
-@@ -12389,7 +12472,7 @@ static const char * drv_br_net_param_str(enum drv_br_net_param param)
- 
- 
- static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
--				       unsigned int val)
-+				       const char *ifname, unsigned int val)
- {
- 	struct i802_bss *bss = priv;
- 	char path[128];
-@@ -12415,8 +12498,11 @@ static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
- 			return -EINVAL;
- 	}
- 
-+	if (!ifname)
-+		ifname = bss->brname;
-+
- 	os_snprintf(path, sizeof(path), "/proc/sys/net/ipv%d/conf/%s/%s",
--		    ip_version, bss->brname, param_txt);
-+		    ip_version, ifname, param_txt);
- 
- set_val:
- 	if (linux_write_system_file(path, val))
-@@ -14019,6 +14105,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.set_acl = wpa_driver_nl80211_set_acl,
- 	.if_add = wpa_driver_nl80211_if_add,
- 	.if_remove = driver_nl80211_if_remove,
-+	.if_rename = driver_nl80211_if_rename,
-+	.set_first_bss = driver_nl80211_set_first_bss,
- 	.send_mlme = driver_nl80211_send_mlme,
- 	.get_hw_feature_data = nl80211_get_hw_feature_data,
- 	.sta_add = wpa_driver_nl80211_sta_add,
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 65389d206..d6a887cef 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -976,6 +976,10 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 			nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]);
- 	}
- 
-+	if (tb[NL80211_ATTR_MAX_SCAN_IE_LEN])
-+		capa->max_scan_ie_len =
-+			nla_get_u16(tb[NL80211_ATTR_MAX_SCAN_IE_LEN]);
-+
- 	if (tb[NL80211_ATTR_MAX_MATCH_SETS])
- 		capa->max_match_sets =
- 			nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index f5778cdaf..4a12d749c 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -1196,6 +1196,7 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
- 				 struct nlattr *bw, struct nlattr *cf1,
- 				 struct nlattr *cf2,
- 				 struct nlattr *punct_bitmap,
-+				 struct nlattr *count,
- 				 int finished)
- {
- 	struct i802_bss *bss;
-@@ -1259,6 +1260,8 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
- 		data.ch_switch.cf1 = nla_get_u32(cf1);
- 	if (cf2)
- 		data.ch_switch.cf2 = nla_get_u32(cf2);
-+	if (count)
-+		data.ch_switch.count = nla_get_u32(count);
- 
- 	if (link)
- 		data.ch_switch.link_id = nla_get_u8(link);
-@@ -3972,6 +3975,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
- 				     tb[NL80211_ATTR_CENTER_FREQ1],
- 				     tb[NL80211_ATTR_CENTER_FREQ2],
- 				     tb[NL80211_ATTR_PUNCT_BITMAP],
-+				     tb[NL80211_ATTR_CH_SWITCH_COUNT],
- 				     0);
- 		break;
- 	case NL80211_CMD_CH_SWITCH_NOTIFY:
-@@ -3984,6 +3988,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
- 				     tb[NL80211_ATTR_CENTER_FREQ1],
- 				     tb[NL80211_ATTR_CENTER_FREQ2],
- 				     tb[NL80211_ATTR_PUNCT_BITMAP],
-+				     NULL,
- 				     1);
- 		break;
- 	case NL80211_CMD_DISCONNECT:
-diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
-index 577f84fef..c352a88bc 100644
---- a/src/drivers/driver_nl80211_scan.c
-+++ b/src/drivers/driver_nl80211_scan.c
-@@ -221,7 +221,7 @@ nl80211_scan_common(struct i802_bss *bss, u8 cmd,
- 		wpa_printf(MSG_DEBUG, "nl80211: Passive scan requested");
- 	}
- 
--	if (params->extra_ies) {
-+	if (params->extra_ies && drv->capa.max_scan_ie_len >= params->extra_ies_len) {
- 		wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
- 			    params->extra_ies, params->extra_ies_len);
- 		if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
-diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c
-index e95df6ddb..9071da3cf 100644
---- a/src/drivers/drivers.c
-+++ b/src/drivers/drivers.c
-@@ -10,6 +10,10 @@
- #include "utils/common.h"
- #include "driver.h"
- 
-+void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
-+			     union wpa_event_data *data);
-+void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
-+			     union wpa_event_data *data);
- 
- const struct wpa_driver_ops *const wpa_drivers[] =
- {
-diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
-index a03d4a034..8da44d9f5 100644
---- a/src/drivers/drivers.mak
-+++ b/src/drivers/drivers.mak
-@@ -54,7 +54,6 @@ NEED_SME=y
- NEED_AP_MLME=y
- NEED_NETLINK=y
- NEED_LINUX_IOCTL=y
--NEED_RFKILL=y
- NEED_RADIOTAP=y
- NEED_LIBNL=y
- endif
-@@ -111,7 +110,6 @@ DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
- CONFIG_WIRELESS_EXTENSION=y
- NEED_NETLINK=y
- NEED_LINUX_IOCTL=y
--NEED_RFKILL=y
- endif
- 
- ifdef CONFIG_DRIVER_NDIS
-@@ -137,7 +135,6 @@ endif
- ifdef CONFIG_WIRELESS_EXTENSION
- DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
- DRV_WPA_OBJS += ../src/drivers/driver_wext.o
--NEED_RFKILL=y
- endif
- 
- ifdef NEED_NETLINK
-@@ -146,6 +143,7 @@ endif
- 
- ifdef NEED_RFKILL
- DRV_OBJS += ../src/drivers/rfkill.o
-+DRV_WPA_CFLAGS += -DCONFIG_RFKILL
- endif
- 
- ifdef NEED_RADIOTAP
-diff --git a/src/drivers/rfkill.h b/src/drivers/rfkill.h
-index 0412ac330..e27565375 100644
---- a/src/drivers/rfkill.h
-+++ b/src/drivers/rfkill.h
-@@ -18,8 +18,24 @@ struct rfkill_config {
- 	void (*unblocked_cb)(void *ctx);
- };
- 
-+#ifdef CONFIG_RFKILL
- struct rfkill_data * rfkill_init(struct rfkill_config *cfg);
- void rfkill_deinit(struct rfkill_data *rfkill);
- int rfkill_is_blocked(struct rfkill_data *rfkill);
-+#else
-+static inline struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
-+{
-+	return (void *) 1;
-+}
-+
-+static inline void rfkill_deinit(struct rfkill_data *rfkill)
-+{
-+}
-+
-+static inline int rfkill_is_blocked(struct rfkill_data *rfkill)
-+{
-+	return 0;
-+}
-+#endif
- 
- #endif /* RFKILL_H */
-diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
-index 2a7f36170..8e8903051 100644
---- a/src/radius/radius_client.c
-+++ b/src/radius/radius_client.c
-@@ -165,6 +165,8 @@ struct radius_client_data {
- 	 */
- 	void *ctx;
- 
-+	struct hostapd_ip_addr local_ip;
-+
- 	/**
- 	 * conf - RADIUS client configuration (list of RADIUS servers to use)
- 	 */
-@@ -818,6 +820,30 @@ static void radius_close_acct_socket(struct radius_client_data *radius)
- }
- 
- 
-+/**
-+ * radius_client_send - Get local address for the RADIUS auth socket
-+ * @radius: RADIUS client context from radius_client_init()
-+ * @addr: pointer to store the address
-+ *
-+ * This function returns the local address for the connection to the RADIUS
-+ * auth server. It also opens the socket if it's not available yet.
-+ */
-+int radius_client_get_local_addr(struct radius_client_data *radius,
-+				 struct hostapd_ip_addr *addr)
-+{
-+	struct hostapd_radius_servers *conf = radius->conf;
-+
-+	if (conf->auth_server && radius->auth_sock < 0)
-+		radius_client_init_auth(radius);
-+
-+	if (radius->auth_sock < 0)
-+		return -1;
-+
-+	memcpy(addr, &radius->local_ip, sizeof(*addr));
-+
-+	return 0;
-+}
-+
- /**
-  * radius_client_send - Send a RADIUS request
-  * @radius: RADIUS client context from radius_client_init()
-@@ -1711,6 +1737,10 @@ radius_change_server(struct radius_client_data *radius,
- 			wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
- 				   inet_ntoa(claddr.sin_addr),
- 				   ntohs(claddr.sin_port));
-+			if (auth) {
-+				radius->local_ip.af = AF_INET;
-+				radius->local_ip.u.v4 = claddr.sin_addr;
-+			}
- 		}
- 		break;
- #ifdef CONFIG_IPV6
-@@ -1722,6 +1752,10 @@ radius_change_server(struct radius_client_data *radius,
- 				   inet_ntop(AF_INET6, &claddr6.sin6_addr,
- 					     abuf, sizeof(abuf)),
- 				   ntohs(claddr6.sin6_port));
-+			if (auth) {
-+				radius->local_ip.af = AF_INET6;
-+				radius->local_ip.u.v6 = claddr6.sin6_addr;
-+			}
- 		}
- 		break;
- 	}
-diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h
-index db40637ea..9a89b0382 100644
---- a/src/radius/radius_client.h
-+++ b/src/radius/radius_client.h
-@@ -274,6 +274,8 @@ int radius_client_register(struct radius_client_data *radius,
- void radius_client_set_interim_error_cb(struct radius_client_data *radius,
- 					void (*cb)(const u8 *addr, void *ctx),
- 					void *ctx);
-+int radius_client_get_local_addr(struct radius_client_data *radius,
-+				 struct hostapd_ip_addr * addr);
- int radius_client_send(struct radius_client_data *radius,
- 		       struct radius_msg *msg,
- 		       RadiusType msg_type, const u8 *addr);
-diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
-index aaa3fc267..327782f62 100644
---- a/src/radius/radius_das.c
-+++ b/src/radius/radius_das.c
-@@ -12,13 +12,26 @@
- #include "utils/common.h"
- #include "utils/eloop.h"
- #include "utils/ip_addr.h"
-+#include "utils/list.h"
- #include "radius.h"
- #include "radius_das.h"
- 
- 
--struct radius_das_data {
-+static struct dl_list das_ports = DL_LIST_HEAD_INIT(das_ports);
-+
-+struct radius_das_port {
-+	struct dl_list list;
-+	struct dl_list das_data;
-+
-+	int port;
- 	int sock;
-+};
-+
-+struct radius_das_data {
-+	struct dl_list list;
-+	struct radius_das_port *port;
- 	u8 *shared_secret;
-+	u8 *nas_identifier;
- 	size_t shared_secret_len;
- 	struct hostapd_ip_addr client_addr;
- 	unsigned int time_window;
-@@ -378,56 +391,17 @@ fail:
- }
- 
- 
--static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
-+static void
-+radius_das_receive_msg(struct radius_das_data *das, struct radius_msg *msg,
-+		       struct sockaddr *from, socklen_t fromlen,
-+		       char *abuf, int from_port)
- {
--	struct radius_das_data *das = eloop_ctx;
--	u8 buf[1500];
--	union {
--		struct sockaddr_storage ss;
--		struct sockaddr_in sin;
--#ifdef CONFIG_IPV6
--		struct sockaddr_in6 sin6;
--#endif /* CONFIG_IPV6 */
--	} from;
--	char abuf[50];
--	int from_port = 0;
--	socklen_t fromlen;
--	int len;
--	struct radius_msg *msg, *reply = NULL;
-+	struct radius_msg *reply = NULL;
- 	struct radius_hdr *hdr;
- 	struct wpabuf *rbuf;
-+	struct os_time now;
- 	u32 val;
- 	int res;
--	struct os_time now;
--
--	fromlen = sizeof(from);
--	len = recvfrom(sock, buf, sizeof(buf), 0,
--		       (struct sockaddr *) &from.ss, &fromlen);
--	if (len < 0) {
--		wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
--		return;
--	}
--
--	os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
--	from_port = ntohs(from.sin.sin_port);
--
--	wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
--		   len, abuf, from_port);
--	if (das->client_addr.u.v4.s_addr &&
--	    das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
--		wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
--		return;
--	}
--
--	msg = radius_msg_parse(buf, len);
--	if (msg == NULL) {
--		wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
--			   "from %s:%d failed", abuf, from_port);
--		return;
--	}
--
--	if (wpa_debug_level <= MSG_MSGDUMP)
--		radius_msg_dump(msg);
- 
- 	if (radius_msg_verify_das_req(msg, das->shared_secret,
- 				       das->shared_secret_len,
-@@ -494,9 +468,8 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
- 			radius_msg_dump(reply);
- 
- 		rbuf = radius_msg_get_buf(reply);
--		res = sendto(das->sock, wpabuf_head(rbuf),
--			     wpabuf_len(rbuf), 0,
--			     (struct sockaddr *) &from.ss, fromlen);
-+		res = sendto(das->port->sock, wpabuf_head(rbuf),
-+			     wpabuf_len(rbuf), 0, from, fromlen);
- 		if (res < 0) {
- 			wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
- 				   abuf, from_port, strerror(errno));
-@@ -508,6 +481,72 @@ fail:
- 	radius_msg_free(reply);
- }
- 
-+static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
-+{
-+	struct radius_das_port *p = eloop_ctx;
-+	struct radius_das_data *das;
-+	u8 buf[1500];
-+	union {
-+		struct sockaddr_storage ss;
-+		struct sockaddr_in sin;
-+#ifdef CONFIG_IPV6
-+		struct sockaddr_in6 sin6;
-+#endif /* CONFIG_IPV6 */
-+	} from;
-+	struct radius_msg *msg;
-+	size_t nasid_len = 0;
-+	u8 *nasid_buf = NULL;
-+	char abuf[50];
-+	int from_port = 0;
-+	socklen_t fromlen;
-+	int found = 0;
-+	int len;
-+
-+	fromlen = sizeof(from);
-+	len = recvfrom(sock, buf, sizeof(buf), 0,
-+		       (struct sockaddr *) &from.ss, &fromlen);
-+	if (len < 0) {
-+		wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
-+		return;
-+	}
-+
-+	os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
-+	from_port = ntohs(from.sin.sin_port);
-+
-+	msg = radius_msg_parse(buf, len);
-+	if (msg == NULL) {
-+		wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
-+			   "from %s:%d failed", abuf, from_port);
-+		return;
-+	}
-+
-+	wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
-+		   len, abuf, from_port);
-+
-+	if (wpa_debug_level <= MSG_MSGDUMP)
-+		radius_msg_dump(msg);
-+
-+	radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
-+				&nasid_buf, &nasid_len, NULL);
-+	dl_list_for_each(das, &p->das_data, struct radius_das_data, list) {
-+		if (das->client_addr.u.v4.s_addr &&
-+		    das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr)
-+			continue;
-+
-+		if (das->nas_identifier && nasid_buf &&
-+		    (nasid_len != os_strlen(das->nas_identifier) ||
-+		     os_memcmp(das->nas_identifier, nasid_buf, nasid_len) != 0))
-+			continue;
-+
-+		found = 1;
-+		radius_das_receive_msg(das, msg, (struct sockaddr *)&from.ss,
-+				       fromlen, abuf, from_port);
-+	}
-+
-+	if (!found)
-+		wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
-+}
-+
- 
- static int radius_das_open_socket(int port)
- {
-@@ -533,6 +572,49 @@ static int radius_das_open_socket(int port)
- }
- 
- 
-+static struct radius_das_port *
-+radius_das_open_port(int port)
-+{
-+	struct radius_das_port *p;
-+
-+	dl_list_for_each(p, &das_ports, struct radius_das_port, list) {
-+		if (p->port == port)
-+			return p;
-+	}
-+
-+	p = os_zalloc(sizeof(*p));
-+	if (p == NULL)
-+		return NULL;
-+
-+	dl_list_init(&p->das_data);
-+	p->port = port;
-+	p->sock = radius_das_open_socket(port);
-+	if (p->sock < 0)
-+		goto free_port;
-+
-+	if (eloop_register_read_sock(p->sock, radius_das_receive, p, NULL))
-+		goto close_port;
-+
-+	dl_list_add(&das_ports, &p->list);
-+
-+	return p;
-+
-+close_port:
-+	close(p->sock);
-+free_port:
-+	os_free(p);
-+
-+	return NULL;
-+}
-+
-+static void radius_das_close_port(struct radius_das_port *p)
-+{
-+	dl_list_del(&p->list);
-+	eloop_unregister_read_sock(p->sock);
-+	close(p->sock);
-+	free(p);
-+}
-+
- struct radius_das_data *
- radius_das_init(struct radius_das_conf *conf)
- {
-@@ -553,6 +635,8 @@ radius_das_init(struct radius_das_conf *conf)
- 	das->ctx = conf->ctx;
- 	das->disconnect = conf->disconnect;
- 	das->coa = conf->coa;
-+	if (conf->nas_identifier)
-+		das->nas_identifier = os_strdup(conf->nas_identifier);
- 
- 	os_memcpy(&das->client_addr, conf->client_addr,
- 		  sizeof(das->client_addr));
-@@ -565,19 +649,15 @@ radius_das_init(struct radius_das_conf *conf)
- 	}
- 	das->shared_secret_len = conf->shared_secret_len;
- 
--	das->sock = radius_das_open_socket(conf->port);
--	if (das->sock < 0) {
-+	das->port = radius_das_open_port(conf->port);
-+	if (!das->port) {
- 		wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
- 			   "DAS");
- 		radius_das_deinit(das);
- 		return NULL;
- 	}
- 
--	if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
--	{
--		radius_das_deinit(das);
--		return NULL;
--	}
-+	dl_list_add(&das->port->das_data, &das->list);
- 
- 	return das;
- }
-@@ -588,11 +668,14 @@ void radius_das_deinit(struct radius_das_data *das)
- 	if (das == NULL)
- 		return;
- 
--	if (das->sock >= 0) {
--		eloop_unregister_read_sock(das->sock);
--		close(das->sock);
-+	if (das->port) {
-+		dl_list_del(&das->list);
-+
-+		if (dl_list_empty(&das->port->das_data))
-+			radius_das_close_port(das->port);
- 	}
- 
-+	os_free(das->nas_identifier);
- 	os_free(das->shared_secret);
- 	os_free(das);
- }
-diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h
-index 233d662f6..80dc13fc8 100644
---- a/src/radius/radius_das.h
-+++ b/src/radius/radius_das.h
-@@ -44,6 +44,7 @@ struct radius_das_attrs {
- struct radius_das_conf {
- 	int port;
- 	const u8 *shared_secret;
-+	const u8 *nas_identifier;
- 	size_t shared_secret_len;
- 	const struct hostapd_ip_addr *client_addr;
- 	unsigned int time_window;
-diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
-index e02c21540..57a47263e 100644
---- 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(struct radius_server_data *data,
- 	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_server_data *data, const char *keyname)
- }
- #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 radius_server_data *data,
- 		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 radius_server_data *data,
- 	}
- 
- 	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_server_data *data,
- 	}
- 
- 	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(void *ctx, const u8 *identity,
- 	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/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
-index 8956c4072..e669858d8 100644
---- a/src/rsn_supp/wpa.c
-+++ b/src/rsn_supp/wpa.c
-@@ -3943,6 +3943,8 @@ static u32 wpa_key_mgmt_suite(struct wpa_sm *sm)
- }
- 
- 
-+#ifdef CONFIG_CTRL_IFACE_MIB
-+
- #define RSN_SUITE "%02x-%02x-%02x-%d"
- #define RSN_SUITE_ARG(s) \
- ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
-@@ -4024,6 +4026,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
- 
- 	return (int) len;
- }
-+#endif
- #endif /* CONFIG_CTRL_IFACE */
- 
- 
-diff --git a/src/tls/Makefile b/src/tls/Makefile
-index c84fbe859..e974a41f0 100644
---- a/src/tls/Makefile
-+++ b/src/tls/Makefile
-@@ -1,3 +1,10 @@
-+LIB_OBJS= asn1.o
-+
-+ifneq ($(CONFIG_TLS),gnutls)
-+ifneq ($(CONFIG_TLS),mbedtls)
-+ifneq ($(CONFIG_TLS),openssl)
-+ifneq ($(CONFIG_TLS),wolfssl)
-+
- CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
- CFLAGS += -DCONFIG_CRYPTO_INTERNAL
- CFLAGS += -DCONFIG_TLSV11
-@@ -21,5 +28,9 @@ LIB_OBJS= \
- 	tlsv1_server_read.o \
- 	tlsv1_server_write.o \
- 	x509v3.o
-+endif
-+endif
-+endif
-+endif
- 
- include ../lib.rules
-diff --git a/src/utils/eloop.c b/src/utils/eloop.c
-index 00b0beff0..50dd1beda 100644
---- 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)
- {
-diff --git a/src/utils/eloop.h b/src/utils/eloop.h
-index 04ee6d183..5452ea589 100644
---- a/src/utils/eloop.h
-+++ b/src/utils/eloop.h
-@@ -65,6 +65,9 @@ typedef void (*eloop_timeout_handler)(void *eloop_ctx, void *user_ctx);
-  */
- 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 sig, void *signal_ctx);
-  */
- 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_signal_handler handler,
-  */
- int eloop_sock_requeue(void);
- 
-+void eloop_add_uloop(void);
-+
- /**
-  * eloop_run - Start the event loop
-  *
-diff --git a/src/utils/uloop.c b/src/utils/uloop.c
-new file mode 100644
-index 000000000..c0d26db93
---- /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/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
-index 7f3dd185f..627575e39 100644
---- a/src/utils/wpa_debug.c
-+++ b/src/utils/wpa_debug.c
-@@ -26,6 +26,10 @@ static FILE *wpa_debug_tracing_file = NULL;
- #define WPAS_TRACE_PFX "wpas <%d>: "
- #endif /* CONFIG_DEBUG_LINUX_TRACING */
- 
-+void (*wpa_printf_hook)(int level, const char *fmt, va_list ap);
-+void (*wpa_hexdump_hook)(int level, const char *title, const void *buf,
-+			 size_t len);
-+void (*wpa_netlink_hook)(int tx, const void *data, size_t len);
- 
- int wpa_debug_level = MSG_INFO;
- int wpa_debug_show_keys = 0;
-@@ -206,10 +210,16 @@ void wpa_debug_close_linux_tracing(void)
-  *
-  * Note: New line '\n' is added to the end of the text when printing to stdout.
-  */
--void wpa_printf(int level, const char *fmt, ...)
-+void _wpa_printf(int level, const char *fmt, ...)
- {
- 	va_list ap;
- 
-+	if (wpa_printf_hook) {
-+		va_start(ap, fmt);
-+		wpa_printf_hook(level, fmt, ap);
-+		va_end(ap);
-+	}
-+
- 	if (level >= wpa_debug_level) {
- #ifdef CONFIG_ANDROID_LOG
- 		va_start(ap, fmt);
-@@ -255,11 +265,14 @@ void wpa_printf(int level, const char *fmt, ...)
- }
- 
- 
--static void _wpa_hexdump(int level, const char *title, const u8 *buf,
-+void _wpa_hexdump(int level, const char *title, const u8 *buf,
- 			 size_t len, int show, int only_syslog)
- {
- 	size_t i;
- 
-+	if (wpa_hexdump_hook)
-+		wpa_hexdump_hook(level, title, buf, len);
-+
- #ifdef CONFIG_DEBUG_LINUX_TRACING
- 	if (wpa_debug_tracing_file != NULL) {
- 		fprintf(wpa_debug_tracing_file,
-@@ -382,19 +395,7 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf,
- #endif /* CONFIG_ANDROID_LOG */
- }
- 
--void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
--{
--	_wpa_hexdump(level, title, buf, len, 1, 0);
--}
--
--
--void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len)
--{
--	_wpa_hexdump(level, title, buf, len, wpa_debug_show_keys, 0);
--}
--
--
--static void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
-+void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
- 			       size_t len, int show)
- {
- 	size_t i, llen;
-@@ -507,20 +508,6 @@ file_done:
- }
- 
- 
--void wpa_hexdump_ascii(int level, const char *title, const void *buf,
--		       size_t len)
--{
--	_wpa_hexdump_ascii(level, title, buf, len, 1);
--}
--
--
--void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
--			   size_t len)
--{
--	_wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
--}
--
--
- #ifdef CONFIG_DEBUG_FILE
- static char *last_path = NULL;
- #endif /* CONFIG_DEBUG_FILE */
-@@ -644,7 +631,7 @@ void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func)
- }
- 
- 
--void wpa_msg(void *ctx, int level, const char *fmt, ...)
-+void _wpa_msg(void *ctx, int level, const char *fmt, ...)
- {
- 	va_list ap;
- 	char *buf;
-@@ -682,7 +669,7 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...)
- }
- 
- 
--void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
-+void _wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
- {
- 	va_list ap;
- 	char *buf;
-diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
-index 4c02ad3c7..854520bfe 100644
---- a/src/utils/wpa_debug.h
-+++ b/src/utils/wpa_debug.h
-@@ -11,6 +11,10 @@
- 
- #include "wpabuf.h"
- 
-+extern void (*wpa_printf_hook)(int level, const char *fmt, va_list ap);
-+extern void (*wpa_hexdump_hook)(int level, const char *title,
-+				const void *buf, size_t len);
-+extern void (*wpa_netlink_hook)(int tx, const void *data, size_t len);
- extern int wpa_debug_level;
- extern int wpa_debug_show_keys;
- extern int wpa_debug_timestamp;
-@@ -51,6 +55,17 @@ void wpa_debug_close_file(void);
- void wpa_debug_setup_stdout(void);
- void wpa_debug_stop_log(void);
- 
-+/* internal */
-+void _wpa_hexdump(int level, const char *title, const u8 *buf,
-+		  size_t len, int show, int only_syslog);
-+void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
-+			size_t len, int show);
-+extern int wpa_debug_show_keys;
-+
-+#ifndef CONFIG_MSG_MIN_PRIORITY
-+#define CONFIG_MSG_MIN_PRIORITY 0
-+#endif
-+
- /**
-  * wpa_debug_printf_timestamp - Print timestamp for debug output
-  *
-@@ -71,9 +86,15 @@ void wpa_debug_print_timestamp(void);
-  *
-  * Note: New line '\n' is added to the end of the text when printing to stdout.
-  */
--void wpa_printf(int level, const char *fmt, ...)
-+void _wpa_printf(int level, const char *fmt, ...)
- PRINTF_FORMAT(2, 3);
- 
-+#define wpa_printf(level, ...)						\
-+	do {								\
-+		if (level >= CONFIG_MSG_MIN_PRIORITY)			\
-+			_wpa_printf(level, __VA_ARGS__);		\
-+	} while(0)
-+
- /**
-  * wpa_hexdump - conditional hex dump
-  * @level: priority level (MSG_*) of the message
-@@ -85,7 +106,13 @@ PRINTF_FORMAT(2, 3);
-  * output may be directed to stdout, stderr, and/or syslog based on
-  * configuration. The contents of buf is printed out has hex dump.
-  */
--void wpa_hexdump(int level, const char *title, const void *buf, size_t len);
-+static inline void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
-+{
-+	if (level < CONFIG_MSG_MIN_PRIORITY)
-+		return;
-+
-+	_wpa_hexdump(level, title, buf, len, 1, 1);
-+}
- 
- static inline void wpa_hexdump_buf(int level, const char *title,
- 				   const struct wpabuf *buf)
-@@ -107,7 +134,13 @@ static inline void wpa_hexdump_buf(int level, const char *title,
-  * like wpa_hexdump(), but by default, does not include secret keys (passwords,
-  * etc.) in debug output.
-  */
--void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len);
-+static inline void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len)
-+{
-+	if (level < CONFIG_MSG_MIN_PRIORITY)
-+		return;
-+
-+	_wpa_hexdump(level, title, buf, len, wpa_debug_show_keys, 1);
-+}
- 
- static inline void wpa_hexdump_buf_key(int level, const char *title,
- 				       const struct wpabuf *buf)
-@@ -129,8 +162,14 @@ static inline void wpa_hexdump_buf_key(int level, const char *title,
-  * the hex numbers and ASCII characters (for printable range) are shown. 16
-  * bytes per line will be shown.
-  */
--void wpa_hexdump_ascii(int level, const char *title, const void *buf,
--		       size_t len);
-+static inline void wpa_hexdump_ascii(int level, const char *title,
-+				     const u8 *buf, size_t len)
-+{
-+	if (level < CONFIG_MSG_MIN_PRIORITY)
-+		return;
-+
-+	_wpa_hexdump_ascii(level, title, buf, len, 1);
-+}
- 
- /**
-  * wpa_hexdump_ascii_key - conditional hex dump, hide keys
-@@ -146,8 +185,14 @@ void wpa_hexdump_ascii(int level, const char *title, const void *buf,
-  * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by
-  * default, does not include secret keys (passwords, etc.) in debug output.
-  */
--void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
--			   size_t len);
-+static inline void wpa_hexdump_ascii_key(int level, const char *title,
-+					 const u8 *buf, size_t len)
-+{
-+	if (level < CONFIG_MSG_MIN_PRIORITY)
-+		return;
-+
-+	_wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
-+}
- 
- /*
-  * wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce
-@@ -184,7 +229,12 @@ void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
-  *
-  * Note: New line '\n' is added to the end of the text when printing to stdout.
-  */
--void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
-+void _wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
-+#define wpa_msg(ctx, level, ...)					\
-+	do {								\
-+		if (level >= CONFIG_MSG_MIN_PRIORITY)			\
-+			_wpa_msg(ctx, level, __VA_ARGS__);		\
-+	} while(0)
- 
- /**
-  * wpa_msg_ctrl - Conditional printf for ctrl_iface monitors
-@@ -198,8 +248,13 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
-  * attached ctrl_iface monitors. In other words, it can be used for frequent
-  * events that do not need to be sent to syslog.
-  */
--void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
-+void _wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
- PRINTF_FORMAT(3, 4);
-+#define wpa_msg_ctrl(ctx, level, ...)					\
-+	do {								\
-+		if (level >= CONFIG_MSG_MIN_PRIORITY)			\
-+			_wpa_msg_ctrl(ctx, level, __VA_ARGS__);		\
-+	} while(0)
- 
- /**
-  * wpa_msg_global - Global printf for ctrl_iface monitors
-diff --git a/tests/Makefile b/tests/Makefile
-index 8ec154bb3..58287f56f 100644
---- a/tests/Makefile
-+++ b/tests/Makefile
-@@ -5,6 +5,14 @@ ALL=test-base64 test-md4 test-milenage \
- 	test-sha256 test-aes test-x509v3 test-list test-rc4 \
- 	test-bss
- 
-+RUN_TESTS= \
-+	test-list \
-+	test-md4 test-rc4 test-sha1 test-sha256 \
-+	test-milenage test-aes \
-+	test-crypto_module
-+
-+ALL=$(RUN_TESTS) test-base64 test-https test-https_server
-+
- include ../src/build.rules
- 
- ifdef LIBFUZZER
-@@ -25,13 +33,27 @@ CFLAGS += -DCONFIG_IEEE80211R_AP
- CFLAGS += -DCONFIG_IEEE80211R
- CFLAGS += -DCONFIG_TDLS
- 
-+# test-crypto_module
-+CFLAGS += -DCONFIG_MODULE_TESTS
-+CFLAGS += -DCONFIG_DPP
-+#CFLAGS += -DCONFIG_DPP2
-+#CFLAGS += -DCONFIG_DPP3
-+CFLAGS += -DCONFIG_ECC
-+CFLAGS += -DCONFIG_HMAC_SHA256_KDF
-+CFLAGS += -DCONFIG_HMAC_SHA384_KDF
-+CFLAGS += -DCONFIG_MESH
-+CFLAGS += -DCONFIG_SHA256
-+CFLAGS += -DCONFIG_SHA384
-+CFLAGS += -DEAP_PSK
-+CFLAGS += -DEAP_FAST
-+
- CFLAGS += -I../src
- CFLAGS += -I../src/utils
- 
- SLIBS = ../src/utils/libutils.a
- 
--DLIBS = ../src/crypto/libcrypto.a \
--	../src/tls/libtls.a
-+DLIBS = ../src/tls/libtls.a \
-+	../src/crypto/libcrypto.a
- 
- _OBJS_VAR := LLIBS
- include ../src/objs.mk
-@@ -43,12 +65,43 @@ include ../src/objs.mk
- LIBS = $(SLIBS) $(DLIBS)
- LLIBS = -Wl,--start-group $(DLIBS) -Wl,--end-group $(SLIBS)
- 
-+ifeq ($(CONFIG_TLS),mbedtls)
-+CFLAGS += -DCONFIG_TLS_MBEDTLS
-+LLIBS += -lmbedtls -lmbedx509 -lmbedcrypto
-+else
-+ifeq ($(CONFIG_TLS),openssl)
-+CFLAGS += -DCONFIG_TLS_OPENSSL
-+LLIBS += -lssl -lcrypto
-+else
-+ifeq ($(CONFIG_TLS),gnutls)
-+CFLAGS += -DCONFIG_TLS_GNUTLS
-+LLIBS += -lgnutls -lgpg-error -lgcrypt
-+else
-+ifeq ($(CONFIG_TLS),wolfssl)
-+CFLAGS += -DCONFIG_TLS_WOLFSSL
-+LLIBS += -lwolfssl -lm
-+else
-+CFLAGS += -DCONFIG_TLS_INTERNAL
-+CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
-+ALL += test-rsa-sig-ver
-+ALL += test-x509v3
-+clean-config_tls_internal:
-+	rm -f test_x509v3_nist.out.*
-+	rm -f test_x509v3_nist2.out.*
-+endif
-+endif
-+endif
-+endif
-+
- # glibc < 2.17 needs -lrt for clock_gettime()
- LLIBS += -lrt
- 
- test-aes: $(call BUILDOBJ,test-aes.o) $(LIBS)
- 	$(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
- 
-+test-crypto_module: $(call BUILDOBJ,test-crypto_module.o) $(LIBS)
-+	$(LDO) $(LDFLAGS) -o $@ $< $(LLIBS)
-+
- test-base64: $(call BUILDOBJ,test-base64.o) $(LIBS)
- 	$(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
- 
-@@ -149,10 +202,12 @@ run-tests: $(ALL)
- 	./test-sha1
- 	./test-sha256
- 	./test-bss
-+
-+	@set -ex; for i in $(RUN_TESTS); do ./$$i; done
- 	@echo
- 	@echo All tests completed successfully.
- 
--clean: common-clean
-+clean: common-clean clean-config_tls_internal
- 	rm -f *~
--	rm -f test_x509v3_nist.out.*
--	rm -f test_x509v3_nist2.out.*
-+
-+.PHONY: run-tests clean-config_tls_internal
-diff --git a/tests/hwsim/example-hostapd.config b/tests/hwsim/example-hostapd.config
-index 210b7fb86..608e20eed 100644
---- a/tests/hwsim/example-hostapd.config
-+++ b/tests/hwsim/example-hostapd.config
-@@ -4,6 +4,7 @@ CONFIG_DRIVER_NONE=y
- CONFIG_DRIVER_NL80211=y
- CONFIG_RSN_PREAUTH=y
- 
-+#CONFIG_TLS=mbedtls
- #CONFIG_TLS=internal
- #CONFIG_INTERNAL_LIBTOMMATH=y
- #CONFIG_INTERNAL_LIBTOMMATH_FAST=y
-@@ -33,12 +34,7 @@ CONFIG_EAP_TNC=y
- CFLAGS += -DTNC_CONFIG_FILE=\"tnc/tnc_config\"
- LIBS += -rdynamic
- CONFIG_EAP_UNAUTH_TLS=y
--ifeq ($(CONFIG_TLS), openssl)
--CONFIG_EAP_PWD=y
--endif
--ifeq ($(CONFIG_TLS), wolfssl)
--CONFIG_EAP_PWD=y
--endif
-+CONFIG_EAP_PWD=$(if $(filter openssl wolfssl mbedtls,$(CONFIG_TLS)),y,)
- CONFIG_EAP_EKE=y
- CONFIG_PKCS12=y
- CONFIG_RADIUS_SERVER=y
-@@ -88,7 +84,7 @@ CFLAGS += -DCONFIG_RADIUS_TEST
- CONFIG_MODULE_TESTS=y
- 
- CONFIG_SUITEB=y
--CONFIG_SUITEB192=y
-+CONFIG_SUITEB192=$(if $(filter openssl mbedtls,$(CONFIG_TLS)),y,)
- 
- # AddressSanitizer (ASan) can be enabled by uncommenting the following lines.
- # This can be used as a more efficient memory error detector than valgrind
-diff --git a/tests/hwsim/example-wpa_supplicant.config b/tests/hwsim/example-wpa_supplicant.config
-index 123f397e3..da0dde659 100644
---- a/tests/hwsim/example-wpa_supplicant.config
-+++ b/tests/hwsim/example-wpa_supplicant.config
-@@ -2,6 +2,7 @@
- 
- CONFIG_TLS=openssl
- #CONFIG_TLS=wolfssl
-+#CONFIG_TLS=mbedtls
- #CONFIG_TLS=internal
- #CONFIG_INTERNAL_LIBTOMMATH=y
- #CONFIG_INTERNAL_LIBTOMMATH_FAST=y
-@@ -34,13 +35,7 @@ LIBS += -rdynamic
- CONFIG_EAP_FAST=y
- CONFIG_EAP_TEAP=y
- CONFIG_EAP_IKEV2=y
--
--ifeq ($(CONFIG_TLS), openssl)
--CONFIG_EAP_PWD=y
--endif
--ifeq ($(CONFIG_TLS), wolfssl)
--CONFIG_EAP_PWD=y
--endif
-+CONFIG_EAP_PWD=$(if $(filter openssl wolfssl mbedtls,$(CONFIG_TLS)),y,)
- 
- CONFIG_USIM_SIMULATOR=y
- CONFIG_SIM_SIMULATOR=y
-@@ -136,7 +131,7 @@ CONFIG_TESTING_OPTIONS=y
- CONFIG_MODULE_TESTS=y
- 
- CONFIG_SUITEB=y
--CONFIG_SUITEB192=y
-+CONFIG_SUITEB192=$(if $(filter openssl mbedtls,$(CONFIG_TLS)),y,)
- 
- # AddressSanitizer (ASan) can be enabled by uncommenting the following lines.
- # This can be used as a more efficient memory error detector than valgrind
-diff --git a/tests/hwsim/test_ap_eap.py b/tests/hwsim/test_ap_eap.py
-index a20140316..027a60b25 100644
---- a/tests/hwsim/test_ap_eap.py
-+++ b/tests/hwsim/test_ap_eap.py
-@@ -42,20 +42,42 @@ def check_eap_capa(dev, method):
-     res = dev.get_capability("eap")
-     if method not in res:
-         raise HwsimSkip("EAP method %s not supported in the build" % method)
-+    if method == "FAST" or method == "TEAP":
-+        tls = dev.request("GET tls_library")
-+        if tls.startswith("mbed TLS"):
-+            raise HwsimSkip("EAP-%s not supported with this TLS library: " % method + tls)
- 
- def check_subject_match_support(dev):
-     tls = dev.request("GET tls_library")
--    if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
-+    if tls.startswith("OpenSSL"):
-+        return
-+    elif tls.startswith("wolfSSL"):
-+        return
-+    elif tls.startswith("mbed TLS"):
-+        return
-+    else:
-         raise HwsimSkip("subject_match not supported with this TLS library: " + tls)
- 
- def check_check_cert_subject_support(dev):
-     tls = dev.request("GET tls_library")
--    if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
-+    if tls.startswith("OpenSSL"):
-+        return
-+    elif tls.startswith("wolfSSL"):
-+        return
-+    elif tls.startswith("mbed TLS"):
-+        return
-+    else:
-         raise HwsimSkip("check_cert_subject not supported with this TLS library: " + tls)
- 
- def check_altsubject_match_support(dev):
-     tls = dev.request("GET tls_library")
--    if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
-+    if tls.startswith("OpenSSL"):
-+        return
-+    elif tls.startswith("wolfSSL"):
-+        return
-+    elif tls.startswith("mbed TLS"):
-+        return
-+    else:
-         raise HwsimSkip("altsubject_match not supported with this TLS library: " + tls)
- 
- def check_domain_match(dev):
-@@ -70,7 +92,13 @@ def check_domain_suffix_match(dev):
- 
- def check_domain_match_full(dev):
-     tls = dev.request("GET tls_library")
--    if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
-+    if tls.startswith("OpenSSL"):
-+        return
-+    elif tls.startswith("wolfSSL"):
-+        return
-+    elif tls.startswith("mbed TLS"):
-+        return
-+    else:
-         raise HwsimSkip("domain_suffix_match requires full match with this TLS library: " + tls)
- 
- def check_cert_probe_support(dev):
-@@ -79,8 +107,15 @@ def check_cert_probe_support(dev):
-         raise HwsimSkip("Certificate probing not supported with this TLS library: " + tls)
- 
- def check_ext_cert_check_support(dev):
-+    if not openssl_imported:
-+        raise HwsimSkip("OpenSSL python method not available")
-+
-     tls = dev.request("GET tls_library")
--    if not tls.startswith("OpenSSL"):
-+    if tls.startswith("OpenSSL"):
-+        return
-+    elif tls.startswith("mbed TLS"):
-+        return
-+    else:
-         raise HwsimSkip("ext_cert_check not supported with this TLS library: " + tls)
- 
- def check_ocsp_support(dev):
-@@ -91,10 +126,12 @@ def check_ocsp_support(dev):
-     #    raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
-     #if tls.startswith("wolfSSL"):
-     #    raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
-+    if tls.startswith("mbed TLS"):
-+        raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
- 
- def check_pkcs5_v15_support(dev):
-     tls = dev.request("GET tls_library")
--    if "BoringSSL" in tls or "GnuTLS" in tls:
-+    if "BoringSSL" in tls or "GnuTLS" in tls or "mbed TLS" in tls:
-         raise HwsimSkip("PKCS#5 v1.5 not supported with this TLS library: " + tls)
- 
- def check_tls13_support(dev):
-@@ -122,11 +159,15 @@ def check_pkcs12_support(dev):
-     #    raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
-     if tls.startswith("wolfSSL"):
-         raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
-+    if tls.startswith("mbed TLS"):
-+        raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
- 
- def check_dh_dsa_support(dev):
-     tls = dev.request("GET tls_library")
-     if tls.startswith("internal"):
-         raise HwsimSkip("DH DSA not supported with this TLS library: " + tls)
-+    if tls.startswith("mbed TLS"):
-+        raise HwsimSkip("DH DSA not supported with this TLS library: " + tls)
- 
- def check_ec_support(dev):
-     tls = dev.request("GET tls_library")
-@@ -1741,7 +1782,7 @@ def test_ap_wpa2_eap_ttls_pap_subject_match(dev, apdev):
-     eap_connect(dev[0], hapd, "TTLS", "pap user",
-                 anonymous_identity="ttls", password="password",
-                 ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
--                subject_match="/C=FI/O=w1.fi/CN=server.w1.fi",
-+                check_cert_subject="/C=FI/O=w1.fi/CN=server.w1.fi",
-                 altsubject_match="EMAIL:noone@example.com;DNS:server.w1.fi;URI:http://example.com/")
-     eap_reauth(dev[0], "TTLS")
- 
-@@ -2976,6 +3017,7 @@ def test_ap_wpa2_eap_tls_neg_domain_match(dev, apdev):
- 
- def test_ap_wpa2_eap_tls_neg_subject_match(dev, apdev):
-     """WPA2-Enterprise negative test - subject mismatch"""
-+    check_subject_match_support(dev[0])
-     params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
-     hostapd.add_ap(apdev[0], params)
-     dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
-@@ -3036,6 +3078,7 @@ def test_ap_wpa2_eap_tls_neg_subject_match(dev, apdev):
- 
- def test_ap_wpa2_eap_tls_neg_altsubject_match(dev, apdev):
-     """WPA2-Enterprise negative test - altsubject mismatch"""
-+    check_altsubject_match_support(dev[0])
-     params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
-     hostapd.add_ap(apdev[0], params)
- 
-@@ -3582,7 +3625,7 @@ def test_ap_wpa2_eap_ikev2_oom(dev, apdev):
-             dev[0].request("REMOVE_NETWORK all")
- 
-     tls = dev[0].request("GET tls_library")
--    if not tls.startswith("wolfSSL"):
-+    if not tls.startswith("wolfSSL") and not tls.startswith("mbed TLS"):
-         tests = [(1, "os_get_random;dh_init")]
-     else:
-         tests = [(1, "crypto_dh_init;dh_init")]
-@@ -4896,7 +4939,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca(dev, apdev, params):
-     params["private_key"] = "auth_serv/iCA-server/server.key"
-     hostapd.add_ap(apdev[0], params)
-     tls = dev[0].request("GET tls_library")
--    if "GnuTLS" in tls or "wolfSSL" in tls:
-+    if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
-         ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
-         client_cert = "auth_serv/iCA-user/user_and_ica.pem"
-     else:
-@@ -4962,6 +5005,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_sha1(dev, apdev, params):
-     run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, "-sha1")
- 
- def run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, md):
-+    check_ocsp_support(dev[0])
-     params = int_eap_server_params()
-     params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem"
-     params["server_cert"] = "auth_serv/iCA-server/server.pem"
-@@ -4971,7 +5015,7 @@ def run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, md):
-     try:
-         hostapd.add_ap(apdev[0], params)
-         tls = dev[0].request("GET tls_library")
--        if "GnuTLS" in tls or "wolfSSL" in tls:
-+        if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
-             ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
-             client_cert = "auth_serv/iCA-user/user_and_ica.pem"
-         else:
-@@ -5007,7 +5051,7 @@ def run_ap_wpa2_eap_tls_intermediate_ca_ocsp_revoked(dev, apdev, params, md):
-     try:
-         hostapd.add_ap(apdev[0], params)
-         tls = dev[0].request("GET tls_library")
--        if "GnuTLS" in tls or "wolfSSL" in tls:
-+        if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
-             ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
-             client_cert = "auth_serv/iCA-user/user_and_ica.pem"
-         else:
-@@ -5057,7 +5101,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_multi_missing_resp(dev, apdev, par
-     try:
-         hostapd.add_ap(apdev[0], params)
-         tls = dev[0].request("GET tls_library")
--        if "GnuTLS" in tls or "wolfSSL" in tls:
-+        if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
-             ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
-             client_cert = "auth_serv/iCA-user/user_and_ica.pem"
-         else:
-@@ -5124,7 +5168,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_multi(dev, apdev, params):
- 
-         hostapd.add_ap(apdev[0], params)
-         tls = dev[0].request("GET tls_library")
--        if "GnuTLS" in tls or "wolfSSL" in tls:
-+        if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
-             ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
-             client_cert = "auth_serv/iCA-user/user_and_ica.pem"
-         else:
-@@ -5382,6 +5426,7 @@ def test_ap_wpa2_eap_ttls_server_cert_eku_client_server(dev, apdev):
- 
- def test_ap_wpa2_eap_ttls_server_pkcs12(dev, apdev):
-     """WPA2-Enterprise using EAP-TTLS and server PKCS#12 file"""
-+    check_pkcs12_support(dev[0])
-     skip_with_fips(dev[0])
-     params = int_eap_server_params()
-     del params["server_cert"]
-@@ -5394,6 +5439,7 @@ def test_ap_wpa2_eap_ttls_server_pkcs12(dev, apdev):
- 
- def test_ap_wpa2_eap_ttls_server_pkcs12_extra(dev, apdev):
-     """EAP-TTLS and server PKCS#12 file with extra certs"""
-+    check_pkcs12_support(dev[0])
-     skip_with_fips(dev[0])
-     params = int_eap_server_params()
-     del params["server_cert"]
-@@ -5416,6 +5462,7 @@ def test_ap_wpa2_eap_ttls_dh_params_server(dev, apdev):
- 
- def test_ap_wpa2_eap_ttls_dh_params_dsa_server(dev, apdev):
-     """WPA2-Enterprise using EAP-TTLS and alternative server dhparams (DSA)"""
-+    check_dh_dsa_support(dev[0])
-     params = int_eap_server_params()
-     params["dh_file"] = "auth_serv/dsaparam.pem"
-     hapd = hostapd.add_ap(apdev[0], params)
-@@ -5727,8 +5774,8 @@ def test_ap_wpa2_eap_non_ascii_identity2(dev, apdev):
- def test_openssl_cipher_suite_config_wpas(dev, apdev):
-     """OpenSSL cipher suite configuration on wpa_supplicant"""
-     tls = dev[0].request("GET tls_library")
--    if not tls.startswith("OpenSSL"):
--        raise HwsimSkip("TLS library is not OpenSSL: " + tls)
-+    if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
-+        raise HwsimSkip("TLS library is not OpenSSL or mbed TLS: " + tls)
-     params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
-     hapd = hostapd.add_ap(apdev[0], params)
-     eap_connect(dev[0], hapd, "TTLS", "pap user",
-@@ -5754,14 +5801,14 @@ def test_openssl_cipher_suite_config_wpas(dev, apdev):
- def test_openssl_cipher_suite_config_hapd(dev, apdev):
-     """OpenSSL cipher suite configuration on hostapd"""
-     tls = dev[0].request("GET tls_library")
--    if not tls.startswith("OpenSSL"):
--        raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL: " + tls)
-+    if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
-+        raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL or mbed TLS: " + tls)
-     params = int_eap_server_params()
-     params['openssl_ciphers'] = "AES256"
-     hapd = hostapd.add_ap(apdev[0], params)
-     tls = hapd.request("GET tls_library")
--    if not tls.startswith("OpenSSL"):
--        raise HwsimSkip("hostapd TLS library is not OpenSSL: " + tls)
-+    if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
-+        raise HwsimSkip("hostapd TLS library is not OpenSSL or mbed TLS: " + tls)
-     eap_connect(dev[0], hapd, "TTLS", "pap user",
-                 anonymous_identity="ttls", password="password",
-                 ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
-@@ -6207,14 +6254,26 @@ def test_ap_wpa2_eap_tls_versions(dev, apdev):
-             check_tls_ver(dev[0], hapd,
-                           "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1",
-                           "TLSv1.2")
--    elif tls.startswith("internal"):
-+    elif tls.startswith("internal") or tls.startswith("mbed TLS"):
-         check_tls_ver(dev[0], hapd,
-                       "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1", "TLSv1.2")
-+<<<<<<< HEAD
-     check_tls_ver(dev[1], hapd,
-                   "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1")
-     check_tls_ver(dev[2], hapd,
-                   "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1")
-     if "run=OpenSSL 1.1.1" in tls or "run=OpenSSL 3." in tls:
-+=======
-+    if tls.startswith("mbed TLS"):
-+        check_tls_ver(dev[2], hapd,
-+                      "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1.0")
-+    else:
-+        check_tls_ver(dev[1], hapd,
-+                      "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1")
-+        check_tls_ver(dev[2], hapd,
-+                      "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1")
-+    if "run=OpenSSL 1.1.1" in tls or "run=OpenSSL 3.0" in tls:
-+>>>>>>> 585bc9ada (hostapd: sync 2024-01-18 openwrt/trunk patch folder)
-         check_tls_ver(dev[0], hapd,
-                       "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0", "TLSv1.3")
- 
-@@ -6235,6 +6294,11 @@ def test_ap_wpa2_eap_tls_versions_server(dev, apdev):
-     tests = [("TLSv1", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
-              ("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
-              ("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")]
-+    tls = dev[0].request("GET tls_library")
-+    if tls.startswith("mbed TLS"):
-+        tests = [#("TLSv1.0", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
-+                 #("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
-+                 ("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")]
-     for exp, flags in tests:
-         hapd.disable()
-         hapd.set("tls_flags", flags)
-@@ -7305,6 +7369,7 @@ def test_ap_wpa2_eap_assoc_rsn(dev, apdev):
- def test_eap_tls_ext_cert_check(dev, apdev):
-     """EAP-TLS and external server certification validation"""
-     # With internal server certificate chain validation
-+    check_ext_cert_check_support(dev[0])
-     id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
-                         identity="tls user",
-                         ca_cert="auth_serv/ca.pem",
-@@ -7317,6 +7382,7 @@ def test_eap_tls_ext_cert_check(dev, apdev):
- def test_eap_ttls_ext_cert_check(dev, apdev):
-     """EAP-TTLS and external server certification validation"""
-     # Without internal server certificate chain validation
-+    check_ext_cert_check_support(dev[0])
-     id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
-                         identity="pap user", anonymous_identity="ttls",
-                         password="password", phase2="auth=PAP",
-@@ -7327,6 +7393,7 @@ def test_eap_ttls_ext_cert_check(dev, apdev):
- def test_eap_peap_ext_cert_check(dev, apdev):
-     """EAP-PEAP and external server certification validation"""
-     # With internal server certificate chain validation
-+    check_ext_cert_check_support(dev[0])
-     id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
-                         identity="user", anonymous_identity="peap",
-                         ca_cert="auth_serv/ca.pem",
-@@ -7337,6 +7404,7 @@ def test_eap_peap_ext_cert_check(dev, apdev):
- 
- def test_eap_fast_ext_cert_check(dev, apdev):
-     """EAP-FAST and external server certification validation"""
-+    check_ext_cert_check_support(dev[0])
-     check_eap_capa(dev[0], "FAST")
-     # With internal server certificate chain validation
-     dev[0].request("SET blob fast_pac_auth_ext ")
-@@ -7351,10 +7419,6 @@ def test_eap_fast_ext_cert_check(dev, apdev):
-     run_ext_cert_check(dev, apdev, id)
- 
- def run_ext_cert_check(dev, apdev, net_id):
--    check_ext_cert_check_support(dev[0])
--    if not openssl_imported:
--        raise HwsimSkip("OpenSSL python method not available")
--
-     params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
-     hapd = hostapd.add_ap(apdev[0], params)
- 
-diff --git a/tests/hwsim/test_ap_ft.py b/tests/hwsim/test_ap_ft.py
-index 3d07d21f7..a708412de 100644
---- a/tests/hwsim/test_ap_ft.py
-+++ b/tests/hwsim/test_ap_ft.py
-@@ -2486,11 +2486,11 @@ def test_ap_ft_ap_oom5(dev, apdev):
-         # This will fail to roam
-         dev[0].roam(bssid1, check_bssid=False)
- 
--    with fail_test(hapd1, 1, "sha256_prf_bits;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
-+    with fail_test(hapd1, 1, "sha256_prf;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
-         # This will fail to roam
-         dev[0].roam(bssid1, check_bssid=False)
- 
--    with fail_test(hapd1, 3, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
-+    with fail_test(hapd1, 2, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
-         # This will fail to roam
-         dev[0].roam(bssid1, check_bssid=False)
- 
-diff --git a/tests/hwsim/test_authsrv.py b/tests/hwsim/test_authsrv.py
-index e0665bcb2..02ec301e5 100644
---- a/tests/hwsim/test_authsrv.py
-+++ b/tests/hwsim/test_authsrv.py
-@@ -156,9 +156,12 @@ def test_authsrv_oom(dev, apdev):
-         if "FAIL" not in authsrv.request("ENABLE"):
-             raise Exception("ENABLE succeeded during OOM")
- 
--    with alloc_fail(authsrv, 1, "tls_init;authsrv_init"):
--        if "FAIL" not in authsrv.request("ENABLE"):
--            raise Exception("ENABLE succeeded during OOM")
-+    # tls_mbedtls.c:tls_init() does not alloc memory (no alloc fail trigger)
-+    tls = dev[0].request("GET tls_library")
-+    if not tls.startswith("mbed TLS"):
-+        with alloc_fail(authsrv, 1, "tls_init;authsrv_init"):
-+            if "FAIL" not in authsrv.request("ENABLE"):
-+                raise Exception("ENABLE succeeded during OOM")
- 
-     for count in range(1, 3):
-         with alloc_fail(authsrv, count, "eap_sim_db_init;authsrv_init"):
-diff --git a/tests/hwsim/test_dpp.py b/tests/hwsim/test_dpp.py
-index 518983bd0..077de58c9 100644
---- a/tests/hwsim/test_dpp.py
-+++ b/tests/hwsim/test_dpp.py
-@@ -39,7 +39,8 @@ def check_dpp_capab(dev, brainpool=False, min_ver=1):
-         raise HwsimSkip("DPP not supported")
-     if brainpool:
-         tls = dev.request("GET tls_library")
--        if (not tls.startswith("OpenSSL") or "run=BoringSSL" in tls) and not tls.startswith("wolfSSL"):
-+        if (not tls.startswith("OpenSSL") or "run=BoringSSL" in tls) and not tls.startswith("wolfSSL") \
-+                                                                     and not tls.startswith("mbed TLS"):
-             raise HwsimSkip("Crypto library does not support Brainpool curves: " + tls)
-     capa = dev.request("GET_CAPABILITY dpp")
-     ver = 1
-@@ -3902,6 +3903,9 @@ def test_dpp_proto_auth_req_no_i_proto_key(dev, apdev):
- 
- def test_dpp_proto_auth_req_invalid_i_proto_key(dev, apdev):
-     """DPP protocol testing - invalid I-proto key in Auth Req"""
-+    tls = dev[0].request("GET tls_library")
-+    if tls.startswith("mbed TLS"):
-+        raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
-     run_dpp_proto_auth_req_missing(dev, 66, "Invalid Initiator Protocol Key")
- 
- def test_dpp_proto_auth_req_no_i_nonce(dev, apdev):
-@@ -3997,7 +4001,12 @@ def test_dpp_proto_auth_resp_no_r_proto_key(dev, apdev):
- 
- def test_dpp_proto_auth_resp_invalid_r_proto_key(dev, apdev):
-     """DPP protocol testing - invalid R-Proto Key in Auth Resp"""
--    run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key")
-+    tls = dev[0].request("GET tls_library")
-+    if tls.startswith("mbed TLS"):
-+        # mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key
-+        run_dpp_proto_auth_resp_missing(dev, 67, "Failed to derive ECDH shared secret")
-+    else:
-+        run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key")
- 
- def test_dpp_proto_auth_resp_no_r_nonce(dev, apdev):
-     """DPP protocol testing - no R-nonce in Auth Resp"""
-@@ -4359,11 +4368,17 @@ def test_dpp_proto_pkex_exchange_resp_invalid_status(dev, apdev):
- 
- def test_dpp_proto_pkex_cr_req_invalid_bootstrap_key(dev, apdev):
-     """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Request"""
-+    tls = dev[0].request("GET tls_library")
-+    if tls.startswith("mbed TLS"):
-+        raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
-     run_dpp_proto_pkex_req_missing(dev, 47,
-                                    "Peer bootstrapping key is invalid")
- 
- def test_dpp_proto_pkex_cr_resp_invalid_bootstrap_key(dev, apdev):
-     """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Response"""
-+    tls = dev[0].request("GET tls_library")
-+    if tls.startswith("mbed TLS"):
-+        raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
-     run_dpp_proto_pkex_resp_missing(dev, 48,
-                                     "Peer bootstrapping key is invalid")
- 
-diff --git a/tests/hwsim/test_erp.py b/tests/hwsim/test_erp.py
-index d083993e8..262e9f095 100644
---- a/tests/hwsim/test_erp.py
-+++ b/tests/hwsim/test_erp.py
-@@ -12,7 +12,7 @@ import time
- 
- import hostapd
- from utils import *
--from test_ap_eap import int_eap_server_params, check_tls13_support
-+from test_ap_eap import int_eap_server_params, check_tls13_support, check_eap_capa
- from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations
- 
- def test_erp_initiate_reauth_start(dev, apdev):
-@@ -276,6 +276,7 @@ def test_erp_radius_eap_methods(dev, apdev):
-     params['erp_domain'] = 'example.com'
-     params['disable_pmksa_caching'] = '1'
-     hapd = hostapd.add_ap(apdev[0], params)
-+    tls = dev[0].request("GET tls_library")
- 
-     erp_test(dev[0], hapd, eap="AKA", identity="0232010000000000@example.com",
-              password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
-@@ -289,7 +290,7 @@ def test_erp_radius_eap_methods(dev, apdev):
-              password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
-     erp_test(dev[0], hapd, eap="EKE", identity="erp-eke@example.com",
-              password="hello")
--    if "FAST" in eap_methods:
-+    if "FAST" in eap_methods and check_eap_capa(dev[0], "FAST"):
-         erp_test(dev[0], hapd, eap="FAST", identity="erp-fast@example.com",
-                  password="password", ca_cert="auth_serv/ca.pem",
-                  phase2="auth=GTC",
-@@ -301,13 +302,14 @@ def test_erp_radius_eap_methods(dev, apdev):
-              password="password")
-     erp_test(dev[0], hapd, eap="PAX", identity="erp-pax@example.com",
-              password_hex="0123456789abcdef0123456789abcdef")
--    if "MSCHAPV2" in eap_methods:
-+    if "MSCHAPV2" in eap_methods and check_eap_capa(dev[0], "MSCHAPV2"):
-         erp_test(dev[0], hapd, eap="PEAP", identity="erp-peap@example.com",
-                  password="password", ca_cert="auth_serv/ca.pem",
-                  phase2="auth=MSCHAPV2")
--        erp_test(dev[0], hapd, eap="TEAP", identity="erp-teap@example.com",
--                 password="password", ca_cert="auth_serv/ca.pem",
--                 phase2="auth=MSCHAPV2", pac_file="blob://teap_pac")
-+        if check_eap_capa(dev[0], "TEAP"):
-+            erp_test(dev[0], hapd, eap="TEAP", identity="erp-teap@example.com",
-+                     password="password", ca_cert="auth_serv/ca.pem",
-+                     phase2="auth=MSCHAPV2", pac_file="blob://teap_pac")
-     erp_test(dev[0], hapd, eap="PSK", identity="erp-psk@example.com",
-              password_hex="0123456789abcdef0123456789abcdef")
-     if "PWD" in eap_methods:
-@@ -640,7 +642,7 @@ def test_erp_local_errors(dev, apdev):
-         dev[0].request("REMOVE_NETWORK all")
-         dev[0].wait_disconnected()
- 
--    for count in range(1, 6):
-+    for count in range(1, 4):
-         dev[0].request("ERP_FLUSH")
-         with fail_test(dev[0], count, "hmac_sha256_kdf;eap_peer_erp_init"):
-             dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
-diff --git a/tests/hwsim/test_fils.py b/tests/hwsim/test_fils.py
-index 5cdc28734..17110c5c2 100644
---- a/tests/hwsim/test_fils.py
-+++ b/tests/hwsim/test_fils.py
-@@ -1484,6 +1484,18 @@ def run_fils_sk_pfs(dev, apdev, group, params):
-     check_erp_capa(dev[0])
-     check_ec_group(dev[0], group)
- 
-+    tls = dev[0].request("GET tls_library")
-+    if tls.startswith("mbed TLS"):
-+        if int(group) == 27:
-+            raise HwsimSkip("Brainpool EC group 27 not supported by mbed TLS")
-+    elif not tls.startswith("wolfSSL"):
-+        if int(group) in [25]:
-+            if not (tls.startswith("OpenSSL") and ("build=OpenSSL 1.0.2" in tls or "build=OpenSSL 1.1" in tls or "build=OpenSSL 3.0" in tls) and ("run=OpenSSL 1.0.2" in tls or "run=OpenSSL 1.1" in tls or "run=OpenSSL 3.0" in tls)):
-+                raise HwsimSkip("EC group not supported")
-+        if int(group) in [27, 28, 29, 30]:
-+            if not (tls.startswith("OpenSSL") and ("build=OpenSSL 1.0.2" in tls or "build=OpenSSL 1.1" in tls or "build=OpenSSL 3.0" in tls) and ("run=OpenSSL 1.0.2" in tls or "run=OpenSSL 1.1" in tls or "run=OpenSSL 3.0" in tls)):
-+                raise HwsimSkip("Brainpool EC group not supported")
-+
-     start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
- 
-     bssid = apdev[0]['bssid']
-diff --git a/tests/hwsim/test_pmksa_cache.py b/tests/hwsim/test_pmksa_cache.py
-index 4a3b444ff..4f7f7f760 100644
---- a/tests/hwsim/test_pmksa_cache.py
-+++ b/tests/hwsim/test_pmksa_cache.py
-@@ -958,7 +958,7 @@ def test_pmksa_cache_preauth_wpas_oom(dev, apdev):
-     eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
-                 password_hex="0123456789abcdef0123456789abcdef",
-                 bssid=apdev[0]['bssid'])
--    for i in range(1, 11):
-+    for i in range(1, 10):
-         with alloc_fail(dev[0], i, "rsn_preauth_init"):
-             res = dev[0].request("PREAUTH f2:11:22:33:44:55").strip()
-             logger.info("Iteration %d - PREAUTH command results: %s" % (i, res))
-@@ -966,7 +966,7 @@ def test_pmksa_cache_preauth_wpas_oom(dev, apdev):
-                 state = dev[0].request('GET_ALLOC_FAIL')
-                 if state.startswith('0:'):
-                     break
--                time.sleep(0.05)
-+                time.sleep(0.10)
- 
- def test_pmksa_cache_ctrl(dev, apdev):
-     """PMKSA cache control interface operations"""
-diff --git a/tests/hwsim/test_sae.py b/tests/hwsim/test_sae.py
-index aceb92751..6f9ee5669 100644
---- a/tests/hwsim/test_sae.py
-+++ b/tests/hwsim/test_sae.py
-@@ -178,6 +178,11 @@ def test_sae_groups(dev, apdev):
-     if tls.startswith("OpenSSL") and "run=OpenSSL 1." in tls:
-         logger.info("Add Brainpool EC groups since OpenSSL is new enough")
-         sae_groups += [27, 28, 29, 30]
-+    if tls.startswith("mbed TLS"):
-+        # secp224k1 and secp224r1 (26) have prime p = 1 mod 4, and mbedtls
-+        # does not have code to derive y from compressed format for those curves
-+        sae_groups = [19, 25, 20, 21, 1, 2, 5, 14, 15, 16, 22, 23, 24]
-+        sae_groups += [27, 28, 29, 30]
-     heavy_groups = [14, 15, 16]
-     suitable_groups = [15, 16, 17, 18, 19, 20, 21]
-     groups = [str(g) for g in sae_groups]
-@@ -2194,6 +2199,8 @@ def run_sae_pwe_group(dev, apdev, group):
-             logger.info("Add Brainpool EC groups since OpenSSL is new enough")
-         elif tls.startswith("wolfSSL"):
-             logger.info("Make sure Brainpool EC groups were enabled when compiling wolfSSL")
-+        elif tls.startswith("mbed TLS"):
-+            logger.info("Make sure Brainpool EC groups were enabled when compiling mbed TLS")
-         else:
-             raise HwsimSkip("Brainpool curve not supported")
-     start_sae_pwe_ap(apdev[0], group, 2)
-diff --git a/tests/hwsim/test_suite_b.py b/tests/hwsim/test_suite_b.py
-index d03a39dee..d703dee95 100644
---- a/tests/hwsim/test_suite_b.py
-+++ b/tests/hwsim/test_suite_b.py
-@@ -27,6 +27,8 @@ def check_suite_b_tls_lib(dev, dhe=False, level128=False):
-         return
-     if tls.startswith("wolfSSL"):
-         return
-+    if tls.startswith("mbed TLS"):
-+        return
-     if not tls.startswith("OpenSSL"):
-         raise HwsimSkip("TLS library not supported for Suite B: " + tls)
-     supported = False
-@@ -520,6 +522,7 @@ def test_suite_b_192_rsa_insufficient_dh(dev, apdev):
- 
-     dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
-                    ieee80211w="2",
-+                   openssl_ciphers="DHE-RSA-AES256-GCM-SHA384",
-                    phase1="tls_suiteb=1",
-                    eap="TLS", identity="tls user",
-                    ca_cert="auth_serv/rsa3072-ca.pem",
-diff --git a/tests/hwsim/test_wpas_ctrl.py b/tests/hwsim/test_wpas_ctrl.py
-index 44eb00444..fbe0fb794 100644
---- a/tests/hwsim/test_wpas_ctrl.py
-+++ b/tests/hwsim/test_wpas_ctrl.py
-@@ -1856,7 +1856,7 @@ def _test_wpas_ctrl_oom(dev):
-     tls = dev[0].request("GET tls_library")
-     if not tls.startswith("internal"):
-         tests.append(('NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG', 'FAIL',
--                      4, 'wpas_ctrl_nfc_get_handover_sel_p2p'))
-+                      3, 'wpas_ctrl_nfc_get_handover_sel_p2p'))
-     for cmd, exp, count, func in tests:
-         with alloc_fail(dev[0], count, func):
-             res = dev[0].request(cmd)
-diff --git a/tests/hwsim/utils.py b/tests/hwsim/utils.py
-index 7e3608284..b23c1ee0b 100644
---- a/tests/hwsim/utils.py
-+++ b/tests/hwsim/utils.py
-@@ -145,7 +145,13 @@ def check_imsi_privacy_support(dev):
- 
- def check_tls_tod(dev):
-     tls = dev.request("GET tls_library")
--    if not tls.startswith("OpenSSL") and not tls.startswith("internal"):
-+    if tls.startswith("OpenSSL"):
-+        return
-+    elif tls.startswith("internal"):
-+        return
-+    elif tls.startswith("mbed TLS"):
-+        return
-+    else:
-         raise HwsimSkip("TLS TOD-TOFU/STRICT not supported with this TLS library: " + tls)
- 
- def vht_supported():
-diff --git a/tests/test-crypto_module.c b/tests/test-crypto_module.c
-new file mode 100644
-index 000000000..0f1156142
---- /dev/null
-+++ b/tests/test-crypto_module.c
-@@ -0,0 +1,16 @@
-+/*
-+ * crypto module tests - test program
-+ * Copyright (c) 2022, Glenn Strauss <gstrauss@gluelogic.com>
-+ *
-+ * This software may be distributed under the terms of the BSD license.
-+ * See README for more details.
-+ */
-+
-+#include "utils/includes.h"
-+#include "utils/module_tests.h"
-+#include "crypto/crypto_module_tests.c"
-+
-+int main(int argc, char *argv[])
-+{
-+	return crypto_module_tests();
-+}
-diff --git a/tests/test-https.c b/tests/test-https.c
-index a72e56f9d..e9df82f1d 100644
---- a/tests/test-https.c
-+++ b/tests/test-https.c
-@@ -75,7 +75,7 @@ static int https_client(int s, const char *path)
- 	struct tls_connection *conn;
- 	struct wpabuf *in, *out, *appl;
- 	int res = -1;
--	int need_more_data;
-+	int need_more_data = 0;
- 
- 	os_memset(&conf, 0, sizeof(conf));
- 	conf.event_cb = https_tls_event_cb;
-@@ -93,8 +93,12 @@ static int https_client(int s, const char *path)
- 
- 	for (;;) {
- 		appl = NULL;
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- 		out = tls_connection_handshake2(tls, conn, in, &appl,
- 						&need_more_data);
-+#else
-+		out = tls_connection_handshake(tls, conn, in, &appl);
-+#endif
- 		wpabuf_free(in);
- 		in = NULL;
- 		if (out == NULL) {
-@@ -152,11 +156,15 @@ static int https_client(int s, const char *path)
- 
- 	wpa_printf(MSG_INFO, "Reading HTTP response");
- 	for (;;) {
--		int need_more_data;
-+		int need_more_data = 0;
- 		in = https_recv(s);
- 		if (in == NULL)
- 			goto done;
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- 		out = tls_connection_decrypt2(tls, conn, in, &need_more_data);
-+#else
-+		out = tls_connection_decrypt(tls, conn, in);
-+#endif
- 		if (need_more_data)
- 			wpa_printf(MSG_DEBUG, "HTTP: Need more data");
- 		wpabuf_free(in);
-diff --git a/tests/test-https_server.c b/tests/test-https_server.c
-index 33b448682..9dcca5596 100644
---- a/tests/test-https_server.c
-+++ b/tests/test-https_server.c
-@@ -67,10 +67,12 @@ static struct wpabuf * https_recv(int s, int timeout_ms)
- }
- 
- 
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- static void https_tls_log_cb(void *ctx, const char *msg)
- {
- 	wpa_printf(MSG_DEBUG, "TLS: %s", msg);
- }
-+#endif
- 
- 
- static int https_server(int s)
-@@ -79,7 +81,7 @@ static int https_server(int s)
- 	void *tls;
- 	struct tls_connection_params params;
- 	struct tls_connection *conn;
--	struct wpabuf *in, *out, *appl;
-+	struct wpabuf *in = NULL, *out = NULL, *appl = NULL;
- 	int res = -1;
- 
- 	os_memset(&conf, 0, sizeof(conf));
-@@ -106,7 +108,9 @@ static int https_server(int s)
- 		return -1;
- 	}
- 
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- 	tls_connection_set_log_cb(conn, https_tls_log_cb, NULL);
-+#endif
- 
- 	for (;;) {
- 		in = https_recv(s, 5000);
-@@ -147,12 +151,16 @@ static int https_server(int s)
- 
- 	wpa_printf(MSG_INFO, "Reading HTTP request");
- 	for (;;) {
--		int need_more_data;
-+		int need_more_data = 0;
- 
- 		in = https_recv(s, 5000);
- 		if (!in)
- 			goto done;
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- 		out = tls_connection_decrypt2(tls, conn, in, &need_more_data);
-+#else
-+		out = tls_connection_decrypt(tls, conn, in);
-+#endif
- 		wpabuf_free(in);
- 		in = NULL;
- 		if (need_more_data) {
-diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
-index dd13308f7..c65acab94 100644
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -10,6 +10,7 @@ ALL += dbus/fi.w1.wpa_supplicant1.service
- EXTRA_TARGETS=dynamic_eap_methods
- 
- CONFIG_FILE=.config
-+-include $(if $(MULTICALL),../hostapd/.config)
- include ../src/build.rules
- 
- ifdef CONFIG_BUILD_PASN_SO
-@@ -188,6 +189,25 @@ ifdef CONFIG_EAPOL_TEST
- CFLAGS += -Werror -DEAPOL_TEST
- 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
-+endif
-+
- ifdef CONFIG_CODE_COVERAGE
- CFLAGS += -O0 -fprofile-arcs -ftest-coverage -U_FORTIFY_SOURCE
- LIBS += -lgcov
-@@ -334,6 +354,7 @@ endif
- ifdef CONFIG_FILS
- CFLAGS += -DCONFIG_FILS
- NEED_SHA384=y
-+NEED_HMAC_SHA384_KDF=y
- NEED_AES_SIV=y
- ifdef CONFIG_FILS_SK_PFS
- CFLAGS += -DCONFIG_FILS_SK_PFS
-@@ -388,7 +409,9 @@ endif
- ifdef CONFIG_IBSS_RSN
- NEED_RSN_AUTHENTICATOR=y
- CFLAGS += -DCONFIG_IBSS_RSN
-+ifndef MULTICALL
- CFLAGS += -DCONFIG_NO_VLAN
-+endif
- OBJS += ibss_rsn.o
- endif
- 
-@@ -980,6 +1003,10 @@ ifdef CONFIG_DYNAMIC_EAP_METHODS
- CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS
- LIBS += -ldl -rdynamic
- endif
-+else
-+  ifdef MULTICALL
-+    OBJS += ../src/eap_common/eap_common.o
-+  endif
- endif
- 
- ifdef CONFIG_AP
-@@ -987,9 +1014,11 @@ NEED_EAP_COMMON=y
- NEED_RSN_AUTHENTICATOR=y
- CFLAGS += -DCONFIG_AP
- OBJS += ap.o
-+ifndef MULTICALL
- CFLAGS += -DCONFIG_NO_RADIUS
- CFLAGS += -DCONFIG_NO_ACCOUNTING
- CFLAGS += -DCONFIG_NO_VLAN
-+endif
- OBJS += ../src/ap/hostapd.o
- OBJS += ../src/ap/wpa_auth_glue.o
- OBJS += ../src/ap/utils.o
-@@ -1029,7 +1058,16 @@ ifdef CONFIG_FILS
- OBJS += ../src/ap/fils_hlp.o
- endif
- ifdef CONFIG_CTRL_IFACE
-+ifdef CONFIG_CTRL_IFACE_MIB
-+CFLAGS += -DCONFIG_CTRL_IFACE_MIB
-+endif
- 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
-@@ -1080,6 +1118,12 @@ endif
- ifdef CONFIG_HS20
- OBJS += ../src/ap/hs20.o
- endif
-+else
-+  ifdef MULTICALL
-+    OBJS += ../src/eap_server/eap_server.o
-+    OBJS += ../src/eap_server/eap_server_identity.o
-+    OBJS += ../src/eap_server/eap_server_methods.o
-+  endif
- endif
- 
- ifdef CONFIG_MBO
-@@ -1089,7 +1133,9 @@ NEED_GAS=y
- endif
- 
- ifdef NEED_RSN_AUTHENTICATOR
-+ifndef MULTICALL
- CFLAGS += -DCONFIG_NO_RADIUS
-+endif
- NEED_AES_WRAP=y
- OBJS += ../src/ap/wpa_auth.o
- OBJS += ../src/ap/wpa_auth_ie.o
-@@ -1188,6 +1234,7 @@ TLS_FUNCS=y
- endif
- 
- ifeq ($(CONFIG_TLS), wolfssl)
-+CFLAGS += -DCONFIG_TLS_WOLFSSL
- ifdef TLS_FUNCS
- CFLAGS += -DWOLFSSL_DER_LOAD
- OBJS += ../src/crypto/tls_wolfssl.o
-@@ -1203,6 +1250,7 @@ LIBS_p += -lwolfssl -lm
- endif
- 
- ifeq ($(CONFIG_TLS), openssl)
-+CFLAGS += -DCONFIG_TLS_OPENSSL
- CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
- ifdef TLS_FUNCS
- CFLAGS += -DEAP_TLS_OPENSSL
-@@ -1229,7 +1277,28 @@ endif
- CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
- endif
- 
-+ifeq ($(CONFIG_TLS), mbedtls)
-+CFLAGS += -DCONFIG_TLS_MBEDTLS
-+ifndef CONFIG_CRYPTO
-+CONFIG_CRYPTO=mbedtls
-+endif
-+ifdef TLS_FUNCS
-+OBJS += ../src/crypto/tls_mbedtls.o
-+LIBS += -lmbedtls -lmbedx509
-+endif
-+OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+OBJS_p += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+OBJS_priv += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+ifeq ($(CONFIG_CRYPTO), mbedtls)
-+LIBS += -lmbedcrypto
-+LIBS_p += -lmbedcrypto
-+# XXX: create a config option?
-+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
-+endif
-+endif
-+
- ifeq ($(CONFIG_TLS), gnutls)
-+CFLAGS += -DCONFIG_TLS_GNUTLS
- ifndef CONFIG_CRYPTO
- # default to libgcrypt
- CONFIG_CRYPTO=gnutls
-@@ -1260,6 +1329,7 @@ endif
- endif
- 
- ifeq ($(CONFIG_TLS), internal)
-+CFLAGS += -DCONFIG_TLS_INTERNAL
- ifndef CONFIG_CRYPTO
- CONFIG_CRYPTO=internal
- endif
-@@ -1340,6 +1410,7 @@ endif
- endif
- 
- ifeq ($(CONFIG_TLS), linux)
-+CFLAGS += -DCONFIG_TLS_INTERNAL
- OBJS += ../src/crypto/crypto_linux.o
- OBJS_p += ../src/crypto/crypto_linux.o
- ifdef TLS_FUNCS
-@@ -1421,9 +1492,11 @@ endif
- 
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- NEED_INTERNAL_AES_WRAP=y
- endif
- endif
-+endif
- ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP
- # Seems to be needed at least with BoringSSL
- NEED_INTERNAL_AES_WRAP=y
-@@ -1437,9 +1510,11 @@ endif
- 
- ifdef NEED_INTERNAL_AES_WRAP
- ifneq ($(CONFIG_TLS), linux)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-unwrap.o
- endif
- endif
-+endif
- ifdef NEED_AES_EAX
- AESOBJS += ../src/crypto/aes-eax.o
- NEED_AES_CTR=y
-@@ -1449,35 +1524,45 @@ AESOBJS += ../src/crypto/aes-siv.o
- NEED_AES_CTR=y
- endif
- ifdef NEED_AES_CTR
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-ctr.o
- endif
-+endif
- ifdef NEED_AES_ENCBLOCK
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-encblock.o
- endif
-+endif
- NEED_AES_ENC=y
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-omac1.o
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_WRAP
- NEED_AES_ENC=y
- ifdef NEED_INTERNAL_AES_WRAP
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-wrap.o
- endif
- endif
-+endif
- ifdef NEED_AES_CBC
- NEED_AES_ENC=y
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-cbc.o
- endif
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_ENC
- ifdef CONFIG_INTERNAL_AES
- AESOBJS += ../src/crypto/aes-internal-enc.o
-@@ -1492,12 +1577,16 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-prf.o
-+endif
- ifdef CONFIG_INTERNAL_SHA1
- SHA1OBJS += ../src/crypto/sha1-internal.o
- ifdef NEED_FIPS186_2_PRF
-@@ -1509,29 +1598,37 @@ CFLAGS += -DCONFIG_NO_PBKDF2
- else
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
- endif
- endif
- endif
-+endif
- ifdef NEED_T_PRF
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-tprf.o
- endif
-+endif
- ifdef NEED_TLS_PRF
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-tlsprf.o
- endif
- endif
-+endif
- 
- ifndef CONFIG_FIPS
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- MD5OBJS += ../src/crypto/md5.o
- endif
- endif
- endif
- endif
- endif
-+endif
- ifdef NEED_MD5
- ifdef CONFIG_INTERNAL_MD5
- MD5OBJS += ../src/crypto/md5-internal.o
-@@ -1586,12 +1683,17 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA256OBJS += ../src/crypto/sha256.o
- endif
- endif
- endif
- endif
-+endif
-+
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA256OBJS += ../src/crypto/sha256-prf.o
-+endif
- ifdef CONFIG_INTERNAL_SHA256
- SHA256OBJS += ../src/crypto/sha256-internal.o
- endif
-@@ -1604,50 +1706,68 @@ CFLAGS += -DCONFIG_INTERNAL_SHA512
- SHA256OBJS += ../src/crypto/sha512-internal.o
- endif
- ifdef NEED_TLS_PRF_SHA256
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA256OBJS += ../src/crypto/sha256-tlsprf.o
- endif
-+endif
- ifdef NEED_TLS_PRF_SHA384
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA256OBJS += ../src/crypto/sha384-tlsprf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA256_KDF
- CFLAGS += -DCONFIG_HMAC_SHA256_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256-kdf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA384_KDF
- CFLAGS += -DCONFIG_HMAC_SHA384_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-kdf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA512_KDF
- CFLAGS += -DCONFIG_HMAC_SHA512_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512-kdf.o
- endif
-+endif
- OBJS += $(SHA256OBJS)
- ifdef NEED_SHA384
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384.o
- endif
- endif
- endif
- endif
-+endif
- CFLAGS += -DCONFIG_SHA384
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-prf.o
- endif
-+endif
- ifdef NEED_SHA512
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512.o
- endif
- endif
- endif
- endif
-+endif
- CFLAGS += -DCONFIG_SHA512
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512-prf.o
- endif
-+endif
- 
- ifdef NEED_ASN1
- OBJS += ../src/tls/asn1.o
-@@ -1822,10 +1942,12 @@ ifdef CONFIG_FIPS
- CFLAGS += -DCONFIG_FIPS
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- $(error CONFIG_FIPS=y requires CONFIG_TLS=openssl)
- endif
- endif
- endif
-+endif
- 
- OBJS += $(SHA1OBJS) $(DESOBJS)
- 
-@@ -2003,32 +2125,38 @@ wpa_priv: $(BCHECK) $(OBJS_priv)
- 
- _OBJS_VAR := OBJS
- include ../src/objs.mk
-+wpa_supplicant_multi.a: .config $(BCHECK) $(OBJS) $(EXTRA_progs)
-+	$(Q)$(CC) -c -o wpa_supplicant_multi.o -Dmain=wpa_supplicant_main $(CFLAGS) main.c
-+	@$(E) "  CC " $<
-+	@rm -f $@
-+	@$(AR) cr $@ wpa_supplicant_multi.o $(OBJS)
-+
- wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs)
--	$(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
-+	+$(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
- 	@$(E) "  LD " $@
- 
- _OBJS_VAR := OBJS_t
- include ../src/objs.mk
- eapol_test: $(OBJS_t)
--	$(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
-+	+$(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
- 	@$(E) "  LD " $@
- 
- _OBJS_VAR := OBJS_t2
- include ../src/objs.mk
- preauth_test: $(OBJS_t2)
--	$(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
-+	+$(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
- 	@$(E) "  LD " $@
- 
- _OBJS_VAR := OBJS_p
- include ../src/objs.mk
- wpa_passphrase: $(OBJS_p)
--	$(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
-+	+$(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
- 	@$(E) "  LD " $@
- 
- _OBJS_VAR := OBJS_c
- include ../src/objs.mk
- wpa_cli: $(OBJS_c)
--	$(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
-+	+$(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
- 	@$(E) "  LD " $@
- 
- LIBCTRL += ../src/common/wpa_ctrl.o
-@@ -2135,6 +2263,12 @@ eap_gpsk.so: $(SRC_EAP_GPSK)
- 	$(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
- 	@$(E) "  sed" $<
- 
-+dump_cflags:
-+	@printf "%s " "$(CFLAGS)"
-+
-+dump_ldflags:
-+	@printf "%s " "$(LDFLAGS) $(LIBS) $(EXTRALIBS)"
-+
- wpa_supplicant.exe: wpa_supplicant
- 	mv -f $< $@
- wpa_cli.exe: wpa_cli
-diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
-index 69a0e5ee1..43c39d7ce 100644
---- a/wpa_supplicant/ap.c
-+++ b/wpa_supplicant/ap.c
-@@ -1520,7 +1520,7 @@ int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
- #endif /* CONFIG_WPS */
- 
- 
--#ifdef CONFIG_CTRL_IFACE
-+#if defined(CONFIG_CTRL_IFACE) && defined(CONFIG_CTRL_IFACE_MIB)
- 
- int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
- 			    char *buf, size_t buflen)
-@@ -1846,11 +1846,31 @@ int ap_switch_channel(struct wpa_supplicant *wpa_s,
- 
- 
- #ifdef CONFIG_CTRL_IFACE
-+
-+static int __ap_ctrl_iface_chanswitch(struct hostapd_iface *iface,
-+				      struct csa_settings *settings)
-+{
-+#ifdef NEED_AP_MLME
-+	if (!iface || !iface->bss[0])
-+		return 0;
-+
-+	return hostapd_switch_channel(iface->bss[0], settings);
-+#else
-+	return -1;
-+#endif
-+}
-+
-+
- int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos)
- {
- 	struct csa_settings settings;
- 	int ret = hostapd_parse_csa_settings(pos, &settings);
- 
-+	if (!(wpa_s->ap_iface && wpa_s->ap_iface->bss[0]) &&
-+	    !(wpa_s->ifmsh && wpa_s->ifmsh->bss[0]))
-+		return -1;
-+
-+	ret = __ap_ctrl_iface_chanswitch(wpa_s->ap_iface, &settings);
- 	if (ret)
- 		return ret;
- 
-diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
-index 2c756136c..c3943355d 100644
---- a/wpa_supplicant/config.c
-+++ b/wpa_supplicant/config.c
-@@ -18,6 +18,7 @@
- #include "eap_peer/eap.h"
- #include "p2p/p2p.h"
- #include "fst/fst.h"
-+#include "ap/sta_info.h"
- #include "config.h"
- 
- 
-@@ -2421,6 +2422,97 @@ static char * wpa_config_write_mac_value(const struct parse_data *data,
- #endif /* NO_CONFIG_WRITE */
- 
- 
-+static int wpa_config_parse_mcast_rate(const struct parse_data *data,
-+				       struct wpa_ssid *ssid, int line,
-+				       const char *value)
-+{
-+	ssid->mcast_rate = (int)(strtod(value, NULL) * 10);
-+
-+	return 0;
-+}
-+
-+#ifndef NO_CONFIG_WRITE
-+static char * wpa_config_write_mcast_rate(const struct parse_data *data,
-+					  struct wpa_ssid *ssid)
-+{
-+	char *value;
-+	int res;
-+
-+	if (!ssid->mcast_rate == 0)
-+		return NULL;
-+
-+	value = os_malloc(6); /* longest: 300.0 */
-+	if (value == NULL)
-+		return NULL;
-+	res = os_snprintf(value, 5, "%.1f", (double)ssid->mcast_rate / 10);
-+	if (res < 0) {
-+		os_free(value);
-+		return NULL;
-+	}
-+	return value;
-+}
-+#endif /* NO_CONFIG_WRITE */
-+
-+static int wpa_config_parse_rates(const struct parse_data *data,
-+				  struct wpa_ssid *ssid, int line,
-+				  const char *value)
-+{
-+	int i;
-+	char *pos, *r, *sptr, *end;
-+	double rate;
-+
-+	pos = (char *)value;
-+	r = strtok_r(pos, ",", &sptr);
-+	i = 0;
-+	while (pos && i < WLAN_SUPP_RATES_MAX) {
-+		rate = 0.0;
-+		if (r)
-+			rate = strtod(r, &end);
-+		ssid->rates[i] = rate * 2;
-+		if (*end != '\0' || rate * 2 != ssid->rates[i])
-+			return 1;
-+
-+		i++;
-+		r = strtok_r(NULL, ",", &sptr);
-+	}
-+
-+	return 0;
-+}
-+
-+#ifndef NO_CONFIG_WRITE
-+static char * wpa_config_write_rates(const struct parse_data *data,
-+				     struct wpa_ssid *ssid)
-+{
-+	char *value, *pos;
-+	int res, i;
-+
-+	if (ssid->rates[0] <= 0)
-+		return NULL;
-+
-+	value = os_malloc(6 * WLAN_SUPP_RATES_MAX + 1);
-+	if (value == NULL)
-+		return NULL;
-+	pos = value;
-+	for (i = 0; i < WLAN_SUPP_RATES_MAX - 1; i++) {
-+		res = os_snprintf(pos, 6, "%.1f,", (double)ssid->rates[i] / 2);
-+		if (res < 0) {
-+			os_free(value);
-+			return NULL;
-+		}
-+		pos += res;
-+	}
-+	res = os_snprintf(pos, 6, "%.1f",
-+			  (double)ssid->rates[WLAN_SUPP_RATES_MAX - 1] / 2);
-+	if (res < 0) {
-+		os_free(value);
-+		return NULL;
-+	}
-+
-+	value[6 * WLAN_SUPP_RATES_MAX] = '\0';
-+	return value;
-+}
-+#endif /* NO_CONFIG_WRITE */
-+
- /* Helper macros for network block parser */
- 
- #ifdef OFFSET
-@@ -2639,6 +2731,7 @@ static const struct parse_data ssid_fields[] = {
- #else /* CONFIG_MESH */
- 	{ INT_RANGE(mode, 0, 4) },
- #endif /* CONFIG_MESH */
-+	{ INT_RANGE(noscan, 0, 1) },
- 	{ INT_RANGE(proactive_key_caching, 0, 1) },
- 	{ INT_RANGE(disabled, 0, 2) },
- 	{ STR(id_str) },
-@@ -2712,6 +2805,8 @@ static const struct parse_data ssid_fields[] = {
- 	{ INT(ap_max_inactivity) },
- 	{ INT(dtim_period) },
- 	{ INT(beacon_int) },
-+	{ FUNC(rates) },
-+	{ FUNC(mcast_rate) },
- #ifdef CONFIG_MACSEC
- 	{ INT_RANGE(macsec_policy, 0, 1) },
- 	{ INT_RANGE(macsec_integ_only, 0, 1) },
-diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
-index 1a2c0c9be..7a3ed6373 100644
---- a/wpa_supplicant/config_file.c
-+++ b/wpa_supplicant/config_file.c
-@@ -326,8 +326,13 @@ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
- 	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));
-@@ -775,6 +780,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
- #endif /* IEEE8021X_EAPOL */
- 	INT(mode);
- 	INT(no_auto_peer);
-+	INT(noscan);
- 	INT(mesh_fwding);
- 	INT(frequency);
- 	INT(enable_edmg);
-diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
-index e40650c27..de79972b6 100644
---- a/wpa_supplicant/config_ssid.h
-+++ b/wpa_supplicant/config_ssid.h
-@@ -10,8 +10,10 @@
- #define CONFIG_SSID_H
- 
- #include "common/defs.h"
-+#include "ap/sta_info.h"
- #include "utils/list.h"
- #include "eap_peer/eap_config.h"
-+#include "drivers/nl80211_copy.h"
- 
- 
- #define DEFAULT_EAP_WORKAROUND ((unsigned int) -1)
-@@ -879,6 +881,9 @@ struct wpa_ssid {
- 	 */
- 	void *parent_cred;
- 
-+	unsigned char rates[WLAN_SUPP_RATES_MAX];
-+	double mcast_rate;
-+
- #ifdef CONFIG_MACSEC
- 	/**
- 	 * macsec_policy - Determines the policy for MACsec secure session
-@@ -1035,6 +1040,8 @@ struct wpa_ssid {
- 	 */
- 	int no_auto_peer;
- 
-+	int noscan;
-+
- 	/**
- 	 * mesh_rssi_threshold - Set mesh parameter mesh_rssi_threshold (dBm)
- 	 *
-diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
-index d0fda4cd9..ec45f29bb 100644
---- a/wpa_supplicant/ctrl_iface.c
-+++ b/wpa_supplicant/ctrl_iface.c
-@@ -2355,7 +2355,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
- 			pos += ret;
- 		}
- 
--#ifdef CONFIG_AP
-+#if defined(CONFIG_AP) && defined(CONFIG_CTRL_IFACE_MIB)
- 		if (wpa_s->ap_iface) {
- 			pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos,
- 							    end - pos,
-@@ -12542,6 +12542,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
- 			reply_len = -1;
- 	} else if (os_strncmp(buf, "NOTE ", 5) == 0) {
- 		wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
-+#ifdef CONFIG_CTRL_IFACE_MIB
- 	} else if (os_strcmp(buf, "MIB") == 0) {
- 		reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
- 		if (reply_len >= 0) {
-@@ -12554,6 +12555,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
- 				reply_size - reply_len);
- #endif /* CONFIG_MACSEC */
- 		}
-+#endif
- 	} else if (os_strncmp(buf, "STATUS", 6) == 0) {
- 		reply_len = wpa_supplicant_ctrl_iface_status(
- 			wpa_s, buf + 6, reply, reply_size);
-@@ -13042,6 +13044,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
- 		reply_len = wpa_supplicant_ctrl_iface_bss(
- 			wpa_s, buf + 4, reply, reply_size);
- #ifdef CONFIG_AP
-+#ifdef CONFIG_CTRL_IFACE_MIB
- 	} else if (os_strcmp(buf, "STA-FIRST") == 0) {
- 		reply_len = ap_ctrl_iface_sta_first(wpa_s, reply, reply_size);
- 	} else if (os_strncmp(buf, "STA ", 4) == 0) {
-@@ -13050,12 +13053,15 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
- 	} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
- 		reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply,
- 						   reply_size);
-+#endif
-+#ifdef CONFIG_CTRL_IFACE_MIB
- 	} else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
- 		if (ap_ctrl_iface_sta_deauthenticate(wpa_s, buf + 15))
- 			reply_len = -1;
- 	} else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
- 		if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13))
- 			reply_len = -1;
-+#endif
- 	} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
- 		if (ap_ctrl_iface_chanswitch(wpa_s, buf + 12))
- 			reply_len = -1;
-@@ -13214,7 +13220,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
- 		if (wpas_ctrl_iface_coloc_intf_report(wpa_s, buf + 18))
- 			reply_len = -1;
- #endif /* CONFIG_WNM */
--#ifdef CONFIG_WNM_AP
-+#if defined(CONFIG_AP) && defined(CONFIG_WNM_AP)
- 	} else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
- 		if (ap_ctrl_iface_disassoc_imminent(wpa_s, buf + 18))
- 			reply_len = -1;
-@@ -13224,7 +13230,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
- 	} else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
- 		if (ap_ctrl_iface_bss_tm_req(wpa_s, buf + 11))
- 			reply_len = -1;
--#endif /* CONFIG_WNM_AP */
-+#endif /* CONFIG_AP && CONFIG_WNM_AP */
- 	} else if (os_strcmp(buf, "FLUSH") == 0) {
- 		wpa_supplicant_ctrl_iface_flush(wpa_s);
- 	} else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) {
-diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
-index 52befd8f1..ace6c5530 100644
---- a/wpa_supplicant/defconfig
-+++ b/wpa_supplicant/defconfig
-@@ -10,8 +10,8 @@
- # to override previous values of the variables.
- 
- 
--# Uncomment following two lines and fix the paths if you have installed OpenSSL
--# or GnuTLS in non-default location
-+# Uncomment following two lines and fix the paths if you have installed TLS
-+# libraries in a non-default location
- #CFLAGS += -I/usr/local/openssl/include
- #LIBS += -L/usr/local/openssl/lib
- 
-@@ -20,6 +20,7 @@
- # used to fix build issues on such systems (krb5.h not found).
- #CFLAGS += -I/usr/include/kerberos
- 
-+
- # Driver interface for generic Linux wireless extensions
- # Note: WEXT is deprecated in the current Linux kernel version and no new
- # functionality is added to it. nl80211-based interface is the new
-@@ -329,6 +330,7 @@ CONFIG_BACKEND=file
- # openssl = OpenSSL (default)
- # gnutls = GnuTLS
- # internal = Internal TLSv1 implementation (experimental)
-+# mbedtls = mbed TLS
- # linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
- # none = Empty template
- #CONFIG_TLS=openssl
-diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
-index 95953de92..673c3cc11 100644
---- a/wpa_supplicant/eapol_test.c
-+++ b/wpa_supplicant/eapol_test.c
-@@ -31,7 +31,12 @@
- #include "ctrl_iface.h"
- #include "pcsc_funcs.h"
- #include "wpas_glue.h"
-+#include "drivers/driver.h"
- 
-+void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
-+			     union wpa_event_data *data);
-+void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
-+			     union wpa_event_data *data);
- 
- const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
- 
-@@ -1325,6 +1330,10 @@ static void usage(void)
- 	       "option several times.\n");
- }
- 
-+extern void supplicant_event(void *ctx, enum wpa_event_type event,
-+			     union wpa_event_data *data);
-+extern void supplicant_event_global(void *ctx, enum wpa_event_type event,
-+			     union wpa_event_data *data);
- 
- int main(int argc, char *argv[])
- {
-@@ -1348,6 +1357,8 @@ int main(int argc, char *argv[])
- 	if (os_program_init())
- 		return -1;
- 
-+	wpa_supplicant_event = supplicant_event;
-+	wpa_supplicant_event_global = supplicant_event_global;
- 	hostapd_logger_register_cb(hostapd_logger_cb);
- 
- 	os_memset(&eapol_test, 0, sizeof(eapol_test));
-diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
-index ca2794638..2a9342318 100644
---- a/wpa_supplicant/events.c
-+++ b/wpa_supplicant/events.c
-@@ -2935,8 +2935,6 @@ void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s)
- }
- 
- 
--#ifdef CONFIG_INTERWORKING
--
- static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map,
- 			    size_t len)
- {
-@@ -2969,8 +2967,6 @@ static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s,
- 	}
- }
- 
--#endif /* CONFIG_INTERWORKING */
--
- 
- static void wpa_supplicant_set_4addr_mode(struct wpa_supplicant *wpa_s)
- {
-@@ -3349,10 +3345,8 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
- 		wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
- 				       data->assoc_info.resp_ies_len);
- #endif /* CONFIG_WNM */
--#ifdef CONFIG_INTERWORKING
- 		interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
- 						data->assoc_info.resp_ies_len);
--#endif /* CONFIG_INTERWORKING */
- 		if (wpa_s->hw_capab == CAPAB_VHT &&
- 		    get_ie(data->assoc_info.resp_ies,
- 			   data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP))
-@@ -5928,8 +5922,8 @@ static void wpas_link_reconfig(struct wpa_supplicant *wpa_s)
- }
- 
- 
--void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
--			  union wpa_event_data *data)
-+void supplicant_event(void *ctx, enum wpa_event_type event,
-+		      union wpa_event_data *data)
- {
- 	struct wpa_supplicant *wpa_s = ctx;
- 	int resched;
-@@ -5964,6 +5958,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
- 		event_to_string(event), event);
- #endif /* CONFIG_NO_STDOUT_DEBUG */
- 
-+	wpas_ucode_event(wpa_s, event, data);
- 	switch (event) {
- 	case EVENT_AUTH:
- #ifdef CONFIG_FST
-@@ -6881,7 +6876,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
- }
- 
- 
--void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
-+void supplicant_event_global(void *ctx, enum wpa_event_type event,
- 				 union wpa_event_data *data)
- {
- 	struct wpa_supplicant *wpa_s;
-diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c
-index 9229eb51f..ee152c5b9 100644
---- a/wpa_supplicant/main.c
-+++ b/wpa_supplicant/main.c
-@@ -12,6 +12,7 @@
- #endif /* __linux__ */
- 
- #include "common.h"
-+#include "build_features.h"
- #include "crypto/crypto.h"
- #include "fst/fst.h"
- #include "wpa_supplicant_i.h"
-@@ -202,7 +203,7 @@ int main(int argc, char *argv[])
- 
- 	for (;;) {
- 		c = getopt(argc, argv,
--			   "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvW");
-+			   "b:Bc:C:D:de:f:g:G:hi:I:KLMm:nNo:O:p:P:qsTtuv::W");
- 		if (c < 0)
- 			break;
- 		switch (c) {
-@@ -267,6 +268,9 @@ int main(int argc, char *argv[])
- 			params.conf_p2p_dev = optarg;
- 			break;
- #endif /* CONFIG_P2P */
-+		case 'n':
-+			iface_count = 0;
-+			break;
- 		case 'o':
- 			params.override_driver = optarg;
- 			break;
-@@ -302,8 +306,12 @@ int main(int argc, char *argv[])
- 			break;
- #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
- 		case 'v':
--			printf("%s\n", wpa_supplicant_version);
--			exitcode = 0;
-+			if (optarg) {
-+				exitcode = !has_feature(optarg);
-+			} else {
-+				printf("%s\n", wpa_supplicant_version);
-+				exitcode = 0;
-+			}
- 			goto out;
- 		case 'W':
- 			params.wait_for_monitor++;
-diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
-index 85c1ea8ba..dabbb0334 100644
---- a/wpa_supplicant/mesh.c
-+++ b/wpa_supplicant/mesh.c
-@@ -506,6 +506,8 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
- 			   frequency);
- 		goto out_free;
- 	}
-+	if (conf->noscan)
-+		ssid->noscan = 1;
- 
- 	if (ssid->mesh_basic_rates == NULL) {
- 		/*
-@@ -630,6 +632,7 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s,
- 
- 	params->meshid = ssid->ssid;
- 	params->meshid_len = ssid->ssid_len;
-+	params->mcast_rate = ssid->mcast_rate;
- 	ibss_mesh_setup_freq(wpa_s, ssid, &params->freq);
- 	wpa_s->mesh_ht_enabled = !!params->freq.ht_enabled;
- 	wpa_s->mesh_vht_enabled = !!params->freq.vht_enabled;
-diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
-index 60f85624f..67352a121 100644
---- a/wpa_supplicant/wpa_cli.c
-+++ b/wpa_supplicant/wpa_cli.c
-@@ -26,6 +26,15 @@
- #include <cutils/properties.h>
- #endif /* ANDROID */
- 
-+#ifndef CONFIG_P2P
-+#define CONFIG_P2P
-+#endif
-+#ifndef CONFIG_AP
-+#define CONFIG_AP
-+#endif
-+#ifndef CONFIG_MESH
-+#define CONFIG_MESH
-+#endif
- 
- static const char *const wpa_cli_version =
- "wpa_cli v" VERSION_STR "\n"
-diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
-index 88f3f2a52..92efe5629 100644
---- a/wpa_supplicant/wpa_priv.c
-+++ b/wpa_supplicant/wpa_priv.c
-@@ -1042,8 +1042,8 @@ static void wpa_priv_send_ft_response(struct wpa_priv_interface *iface,
- }
- 
- 
--void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
--			  union wpa_event_data *data)
-+static void supplicant_event(void *ctx, enum wpa_event_type event,
-+			     union wpa_event_data *data)
- {
- 	struct wpa_priv_interface *iface = ctx;
- 
-@@ -1106,7 +1106,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
- }
- 
- 
--void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
-+void supplicant_event_global(void *ctx, enum wpa_event_type event,
- 				 union wpa_event_data *data)
- {
- 	struct wpa_priv_global *global = ctx;
-@@ -1220,6 +1220,8 @@ int main(int argc, char *argv[])
- 	if (os_program_init())
- 		return -1;
- 
-+	wpa_supplicant_event = supplicant_event;
-+	wpa_supplicant_event_global = supplicant_event_global;
- 	wpa_priv_fd_workaround();
- 
- 	os_memset(&global, 0, sizeof(global));
-diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
-index ab71e2f27..fea84fe49 100644
---- a/wpa_supplicant/wpa_supplicant.c
-+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -1060,6 +1060,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
- 		sme_sched_obss_scan(wpa_s, 0);
- 	}
- 	wpa_s->wpa_state = state;
-+	wpas_ucode_update_state(wpa_s);
- 
- #ifdef CONFIG_BGSCAN
- 	if (state == WPA_COMPLETED && wpa_s->current_ssid != wpa_s->bgscan_ssid)
-@@ -2698,7 +2699,7 @@ static int drv_supports_vht(struct wpa_supplicant *wpa_s,
- }
- 
- 
--static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode)
-+static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode, bool dfs_enabled)
- {
- 	int i;
- 
-@@ -2707,7 +2708,10 @@ static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode)
- 
- 		chan = hw_get_channel_chan(mode, i, NULL);
- 		if (!chan ||
--		    chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
-+		    chan->flag & HOSTAPD_CHAN_DISABLED)
-+			return false;
-+		
-+		if (!dfs_enabled && chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
- 			return false;
- 	}
- 
-@@ -2767,7 +2771,7 @@ static bool ibss_mesh_can_use_vht(struct wpa_supplicant *wpa_s,
- 				  const struct wpa_ssid *ssid,
- 				  struct hostapd_hw_modes *mode)
- {
--	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
-+	if (mode->mode != HOSTAPD_MODE_IEEE80211A && !(ssid->noscan))
- 		return false;
- 
- 	if (!drv_supports_vht(wpa_s, ssid))
-@@ -2834,12 +2838,13 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s,
- 				   const struct wpa_ssid *ssid,
- 				   struct hostapd_hw_modes *mode,
- 				   struct hostapd_freq_params *freq,
--				   int obss_scan) {
-+				   int obss_scan, bool dfs_enabled) {
- 	int chan_idx;
- 	struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
- 	int i, res;
- 	unsigned int j;
- 	static const int ht40plus[] = {
-+		1, 2, 3, 4, 5, 6, 7,
- 		36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
- 		149, 157, 165, 173, 184, 192
- 	};
-@@ -2858,8 +2863,11 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s,
- 		return;
- 
- 	/* Check primary channel flags */
--	if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
-+	if (pri_chan->flag & HOSTAPD_CHAN_DISABLED)
- 		return;
-+	if (pri_chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
-+		if (!dfs_enabled)
-+			return;
- 
- #ifdef CONFIG_HT_OVERRIDES
- 	if (ssid->disable_ht40)
-@@ -2885,8 +2893,11 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s,
- 		return;
- 
- 	/* Check secondary channel flags */
--	if (sec_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
-+	if (sec_chan->flag & HOSTAPD_CHAN_DISABLED)
- 		return;
-+	if (sec_chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
-+		if (!dfs_enabled)
-+			return;
- 
- 	if (ht40 == -1) {
- 		if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS))
-@@ -2941,7 +2952,7 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
- 				       const struct wpa_ssid *ssid,
- 				       struct hostapd_hw_modes *mode,
- 				       struct hostapd_freq_params *freq,
--				       int ieee80211_mode, bool is_6ghz) {
-+				       int ieee80211_mode, bool is_6ghz, bool dfs_enabled) {
- 	static const int bw80[] = {
- 		5180, 5260, 5500, 5580, 5660, 5745, 5825,
- 		5955, 6035, 6115, 6195, 6275, 6355, 6435,
-@@ -2986,7 +2997,7 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
- 		goto skip_80mhz;
- 
- 	/* Use 40 MHz if channel not usable */
--	if (!ibss_mesh_is_80mhz_avail(channel, mode))
-+	if (!ibss_mesh_is_80mhz_avail(channel, mode, dfs_enabled))
- 		goto skip_80mhz;
- 
- 	chwidth = CONF_OPER_CHWIDTH_80MHZ;
-@@ -3000,7 +3011,7 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
- 	if ((mode->he_capab[ieee80211_mode].phy_cap[
- 		     HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
- 	     HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G) && is_6ghz &&
--	    ibss_mesh_is_80mhz_avail(channel + 16, mode)) {
-+	    ibss_mesh_is_80mhz_avail(channel + 16, mode, dfs_enabled)) {
- 		for (j = 0; j < ARRAY_SIZE(bw160); j++) {
- 			if (freq->freq == bw160[j]) {
- 				chwidth = CONF_OPER_CHWIDTH_160MHZ;
-@@ -3028,10 +3039,12 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
- 				if (!chan)
- 					continue;
- 
--				if (chan->flag & (HOSTAPD_CHAN_DISABLED |
--						  HOSTAPD_CHAN_NO_IR |
--						  HOSTAPD_CHAN_RADAR))
-+				if (chan->flag & HOSTAPD_CHAN_DISABLED)
- 					continue;
-+				if (chan->flag & (HOSTAPD_CHAN_RADAR |
-+						  HOSTAPD_CHAN_NO_IR))
-+					if (!dfs_enabled)
-+						continue;
- 
- 				/* Found a suitable second segment for 80+80 */
- 				chwidth = CONF_OPER_CHWIDTH_80P80MHZ;
-@@ -3083,12 +3096,17 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
- 	int ieee80211_mode = wpas_mode_to_ieee80211_mode(ssid->mode);
- 	enum hostapd_hw_mode hw_mode;
- 	struct hostapd_hw_modes *mode = NULL;
--	int i, obss_scan = 1;
-+	int i, obss_scan = !(ssid->noscan);
- 	u8 channel;
- 	bool is_6ghz, is_24ghz;
-+	bool dfs_enabled = wpa_s->conf->country[0] && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_RADAR);
- 
- 	freq->freq = ssid->frequency;
- 
-+	if (ssid->fixed_freq) {
-+		obss_scan = 0;
-+	}
-+
- 	if (ssid->mode == WPAS_MODE_IBSS && !ssid->fixed_freq) {
- 		struct wpa_bss *bss = ibss_find_existing_bss(wpa_s, ssid);
- 
-@@ -3132,11 +3150,13 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
- 		freq->he_enabled = ibss_mesh_can_use_he(wpa_s, ssid, mode,
- 							ieee80211_mode);
- 	freq->channel = channel;
-+	if (mode->mode == HOSTAPD_MODE_IEEE80211G && ssid->noscan)
-+		ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan, dfs_enabled);
- 	/* Setup higher BW only for 5 GHz */
- 	if (mode->mode == HOSTAPD_MODE_IEEE80211A) {
--		ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan);
-+		ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan, dfs_enabled);
- 		if (!ibss_mesh_select_80_160mhz(wpa_s, ssid, mode, freq,
--						ieee80211_mode, is_6ghz))
-+						ieee80211_mode, is_6ghz, dfs_enabled))
- 			freq->he_enabled = freq->vht_enabled = false;
- 	}
- 
-@@ -4240,6 +4260,12 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
- 			params.beacon_int = ssid->beacon_int;
- 		else
- 			params.beacon_int = wpa_s->conf->beacon_int;
-+		int i = 0;
-+		while (i < WLAN_SUPP_RATES_MAX) {
-+			params.rates[i] = ssid->rates[i];
-+			i++;
-+		}
-+		params.mcast_rate = ssid->mcast_rate;
- 	}
- 
- 	if (bss && ssid->enable_edmg)
-@@ -5861,7 +5887,7 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent)
- 	if (wpa_s == NULL)
- 		return NULL;
- 	wpa_s->scan_req = INITIAL_SCAN_REQ;
--	wpa_s->scan_interval = 5;
-+	wpa_s->scan_interval = 1;
- 	wpa_s->new_connection = 1;
- 	wpa_s->parent = parent ? parent : wpa_s;
- 	wpa_s->p2pdev = wpa_s->parent;
-@@ -7576,7 +7602,6 @@ struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global,
- 	return NULL;
- }
- 
--
- /**
-  * wpa_supplicant_match_existing - Match existing interfaces
-  * @global: Pointer to global data from wpa_supplicant_init()
-@@ -7611,6 +7636,11 @@ static int wpa_supplicant_match_existing(struct wpa_global *global)
- 
- #endif /* CONFIG_MATCH_IFACE */
- 
-+extern void supplicant_event(void *ctx, enum wpa_event_type event,
-+			     union wpa_event_data *data);
-+
-+extern void supplicant_event_global(void *ctx, enum wpa_event_type event,
-+ 				 union wpa_event_data *data);
- 
- /**
-  * wpa_supplicant_add_iface - Add a new network interface
-@@ -7693,6 +7723,9 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
- 	}
- #endif /* CONFIG_P2P */
- 
-+	wpas_ubus_add_bss(wpa_s);
-+	wpas_ucode_add_bss(wpa_s);
-+
- 	return wpa_s;
- }
- 
-@@ -7719,6 +7752,9 @@ int wpa_supplicant_remove_iface(struct wpa_global *global,
- 	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 */
- 	prev = global->ifaces;
- 	if (prev == wpa_s) {
-@@ -7867,6 +7903,8 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
- #ifndef CONFIG_NO_WPA_MSG
- 	wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
- #endif /* CONFIG_NO_WPA_MSG */
-+	wpa_supplicant_event = supplicant_event;
-+	wpa_supplicant_event_global = supplicant_event_global;
- 
- 	if (params->wpa_debug_file_path)
- 		wpa_debug_open_file(params->wpa_debug_file_path);
-@@ -8025,6 +8063,7 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
- 
- 	eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
- 			       wpas_periodic, global, NULL);
-+	wpas_ucode_init(global);
- 
- 	return global;
- }
-@@ -8097,6 +8136,8 @@ void wpa_supplicant_deinit(struct wpa_global *global)
- 
- 	wpas_notify_supplicant_deinitialized(global);
- 
-+	wpas_ucode_free();
-+
- 	eap_peer_unregister_methods();
- #ifdef CONFIG_AP
- 	eap_server_unregister_methods();
-diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
-index 426d077d2..e0c0e5b0c 100644
---- a/wpa_supplicant/wpa_supplicant_i.h
-+++ b/wpa_supplicant/wpa_supplicant_i.h
-@@ -21,6 +21,8 @@
- #include "config_ssid.h"
- #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;
-@@ -319,6 +321,8 @@ struct wpa_global {
- #endif /* CONFIG_WIFI_DISPLAY */
- 
- 	struct psk_list_entry *add_psk; /* From group formation */
-+
-+	struct ubus_object ubus_global;
- };
- 
- 
-@@ -693,6 +697,8 @@ struct wpa_supplicant {
- 	unsigned char own_addr[ETH_ALEN];
- 	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 */
-diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
-index 8cd355f6b..136b06583 100644
---- a/wpa_supplicant/wps_supplicant.c
-+++ b/wpa_supplicant/wps_supplicant.c
-@@ -33,6 +33,7 @@
- #include "p2p/p2p.h"
- #include "p2p_supplicant.h"
- #include "wps_supplicant.h"
-+#include "ubus.h"
- 
- 
- #ifndef WPS_PIN_SCAN_IGNORE_SEL_REG
-@@ -401,6 +402,8 @@ static int wpa_supplicant_wps_cred(void *ctx,
- 	wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
- 			cred->cred_attr, cred->cred_attr_len);
- 
-+	wpas_ubus_notify(wpa_s, cred);
-+
- 	if (wpa_s->conf->wps_cred_processing == 1)
- 		return 0;
- 
-diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
-index aae3f7cb5..30b4e9105 100644
---- a/wpa_supplicant/wps_supplicant.h
-+++ b/wpa_supplicant/wps_supplicant.h
-@@ -9,6 +9,7 @@
- #ifndef WPS_SUPPLICANT_H
- #define WPS_SUPPLICANT_H
- 
-+struct wpa_bss;
- struct wpa_scan_results;
- 
- #ifdef CONFIG_WPS
-@@ -16,8 +17,6 @@ struct wpa_scan_results;
- #include "wps/wps.h"
- #include "wps/wps_defs.h"
- 
--struct wpa_bss;
--
- struct wps_new_ap_settings {
- 	const char *ssid_hex;
- 	const char *auth;
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0028-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0028-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch
new file mode 100644
index 0000000..ed43435
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0028-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch
@@ -0,0 +1,60 @@
+From 5fc587076c71f7a410dd59b467f156a0ed804b13 Mon Sep 17 00:00:00 2001
+From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
+Date: Fri, 3 Mar 2023 12:45:42 +0800
+Subject: [PATCH 028/126] mtk: hostapd: Mark DFS channel as available for CSA.
+
+---
+ hostapd/ctrl_iface.c   | 10 ++++++++++
+ hostapd/hostapd_cli.c  |  2 +-
+ src/ap/ctrl_iface_ap.c |  1 +
+ 3 files changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 7e5733278..815633c67 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -2792,6 +2792,16 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ 		break;
+ 	}
+ 
++	if (settings.freq_params.radar_background) {
++		hostapd_dfs_sta_update_state(iface,
++			settings.freq_params.freq,
++			settings.freq_params.ht_enabled,
++			settings.freq_params.sec_channel_offset,
++			bandwidth, settings.freq_params.center_freq1,
++			settings.freq_params.center_freq2,
++			HOSTAPD_CHAN_DFS_AVAILABLE);
++	}
++
+ 	if (settings.freq_params.center_freq1)
+ 		dfs_range += hostapd_is_dfs_overlap(
+ 			iface, bandwidth, settings.freq_params.center_freq1);
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index d934bb0d4..7ab980990 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1800,7 +1800,7 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 	  "<addr> = send QoS Map Configure frame" },
+ 	{ "chan_switch", hostapd_cli_cmd_chan_switch, NULL,
+ 	  "<cs_count> <freq> [sec_channel_offset=] [center_freq1=]\n"
+-	  "  [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n"
++	  "  [center_freq2=] [bandwidth=] [blocktx] [ht|vht] [skip_cac]\n"
+ 	  "  = initiate channel switch announcement" },
+ #ifdef CONFIG_IEEE80211AX
+ 	{ "color_change", hostapd_cli_cmd_color_change, NULL,
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index 4d3c7e529..20d426560 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -1140,6 +1140,7 @@ int hostapd_parse_csa_settings(const char *pos,
+ 	settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
+ 	settings->freq_params.he_enabled = !!os_strstr(pos, " he");
+ 	settings->freq_params.eht_enabled = !!os_strstr(pos, " eht");
++	settings->freq_params.radar_background = !!os_strstr(pos, " skip_cac");
+ 	settings->block_tx = !!os_strstr(pos, " blocktx");
+ #undef SET_CSA_SETTING
+ #undef SET_CSA_SETTING_EXT
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0029-mtk-hostapd-Add-available-color-bitmap.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0029-mtk-hostapd-Add-available-color-bitmap.patch
new file mode 100644
index 0000000..a8adfdc
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0029-mtk-hostapd-Add-available-color-bitmap.patch
@@ -0,0 +1,476 @@
+From 75c1aefed4cda9d8bcb5e8e7f6f086af4a6ff58a Mon Sep 17 00:00:00 2001
+From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Date: Thu, 26 Jan 2023 09:16:00 +0800
+Subject: [PATCH 029/126] mtk: hostapd: Add available color bitmap
+
+Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+---
+ hostapd/ctrl_iface.c              |  74 +++++++++++
+ hostapd/hostapd_cli.c             |  18 +++
+ src/ap/ap_drv_ops.c               |  10 +-
+ src/ap/ap_drv_ops.h               |   2 +
+ src/common/mtk_vendor.h           |  11 ++
+ src/drivers/driver.h              |   8 ++
+ src/drivers/driver_nl80211.c      | 198 +++++++++++++++++++++++++++++-
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   3 +
+ 9 files changed, 323 insertions(+), 2 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 815633c67..b8a957468 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4387,6 +4387,76 @@ hostapd_ctrl_iface_get_amsdu(struct hostapd_data *hapd, char *buf,
+ 	return ret;
+ }
+ 
++static int
++hostapd_ctrl_iface_get_bss_color(struct hostapd_data *hapd, char *buf,
++		size_t buflen)
++{
++	int ret;
++	char *pos, *end;
++	int i;
++
++	pos = buf;
++	end = buf + buflen;
++
++	if (hapd->iface->conf->he_op.he_bss_color_disabled)
++		ret = os_snprintf(buf, buflen, "BSS Color disabled\n");
++	else
++		ret = os_snprintf(buf, buflen, "BSS Color=%u\n",
++				  hapd->iface->conf->he_op.he_bss_color);
++
++	pos += ret;
++
++	return pos - buf;
++}
++
++
++static int
++hostapd_ctrl_iface_get_aval_color_bmp(struct hostapd_data *hapd, char *buf,
++		size_t buflen)
++{
++	int ret;
++	char *pos, *end;
++	int i;
++	u64 aval_color_bmp = 0;
++
++	hostapd_drv_get_aval_bss_color_bmp(hapd, &aval_color_bmp);
++	hapd->color_collision_bitmap = ~aval_color_bmp;
++
++	pos = buf;
++	end = buf + buflen;
++
++	ret = os_snprintf(buf, buflen,
++			"available color bitmap=0x%llx\n",
++			aval_color_bmp);
++	if (os_snprintf_error(end - pos, ret))
++		return pos - buf;
++	pos += ret;
++
++	for (i = 0; i < HE_OPERATION_BSS_COLOR_MAX; i++) {
++		int bit = !!((aval_color_bmp >> i) & 1LLU);
++
++		if (i % 8 == 0) {
++			ret = os_snprintf(pos, end - pos, "%2d: ", i);
++			if (os_snprintf_error(end - pos, ret))
++				return pos - buf;
++			pos += ret;
++		}
++
++		ret = os_snprintf(pos, end - pos, "%d ", bit);
++		if (os_snprintf_error(end - pos, ret))
++			return pos - buf;
++		pos += ret;
++
++		if (i % 8 == 7) {
++			ret = os_snprintf(pos, end - pos, "\n");
++			if (os_snprintf_error(end - pos, ret))
++				return pos - buf;
++			pos += ret;
++		}
++	}
++	return pos - buf;
++}
++
+ 
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+@@ -5016,6 +5086,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
+ 	} else if (os_strncmp(buf, "GET_AMSDU", 9) == 0) {
+ 		reply_len = hostapd_ctrl_iface_get_amsdu(hapd, reply, reply_size);
++	} else if (os_strncmp(buf, "GET_BSS_COLOR", 13) == 0) {
++		reply_len = hostapd_ctrl_iface_get_bss_color(hapd, reply, reply_size);
++	} else if (os_strncmp(buf, "AVAL_COLOR_BMP", 14) == 0) {
++		reply_len = hostapd_ctrl_iface_get_aval_color_bmp(hapd, reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 7ab980990..27d61b06d 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1683,6 +1683,20 @@ static int hostapd_cli_cmd_reload_rxkhs(struct wpa_ctrl *ctrl, int argc,
+ #endif /* CONFIG_IEEE80211R_AP */
+ 
+ 
++static int hostapd_cli_cmd_get_bss_color(struct wpa_ctrl *ctrl, int argc,
++					  char *argv[])
++{
++	return wpa_ctrl_command(ctrl, "GET_BSS_COLOR");
++}
++
++
++static int hostapd_cli_cmd_get_aval_color_bmp(struct wpa_ctrl *ctrl, int argc,
++					  char *argv[])
++{
++	return wpa_ctrl_command(ctrl, "AVAL_COLOR_BMP");
++}
++
++
+ #ifdef ANDROID
+ static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ {
+@@ -1929,6 +1943,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 	{ "get_rxkhs", hostapd_cli_cmd_get_rxkhs, NULL,
+ 	  "= get R0KHs and R1KHs" },
+ #endif /* CONFIG_IEEE80211R_AP */
++	{ "get_bss_color", hostapd_cli_cmd_get_bss_color, NULL,
++	  "= get current BSS color" },
++	{ "get_color_bmp", hostapd_cli_cmd_get_aval_color_bmp, NULL,
++	  "= get available BSS color bitmap" },
+ #ifdef ANDROID
+ 	{ "driver", hostapd_cli_cmd_driver, NULL,
+ 	  "<driver sub command> [<hex formatted data>] = send driver command data" },
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 2685c6f8a..efa4e2b4d 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1350,4 +1350,12 @@ int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu)
+ 	if (!hapd->driver || !hapd->driver->amsdu_dump)
+ 		return 0;
+ 	return hapd->driver->amsdu_dump(hapd->drv_priv, amsdu);
+-}
+\ No newline at end of file
++}
++
++int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_color_bmp)
++{
++	if (!hapd->driver || !hapd->driver->get_aval_color_bmp ||
++	    hapd->iface->conf->he_op.he_bss_color_disabled)
++		return 0;
++	return hapd->driver->get_aval_color_bmp(hapd->drv_priv, aval_color_bmp);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 68b26595f..f57dff4aa 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -163,6 +163,8 @@ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
+ int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
++int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd,
++				       u64 *aval_color_bmp);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 7b4d7c11a..03daeb72a 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -15,6 +15,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
+ 	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
+ 	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
++	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
+ };
+ 
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -255,6 +256,16 @@ ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
+ 	[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
+ };
+ 
++enum mtk_vendor_attr_bss_color_ctrl {
++	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL,
++	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
++};
+ 
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index cccc44147..5d7eb5b4e 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5299,6 +5299,14 @@ struct wpa_driver_ops {
+ 	 */
+ 	int (*amsdu_ctrl)(void *priv, u8 amsdu);
+ 	int (*amsdu_dump)(void *priv, u8 *amsdu);
++
++	/**
++	 * get_aval_color_bmp - get available BSS color bitmap
++	 * @priv: Private driver interface data
++	 * @aval_color_bmp: available bss color bitmap
++	 *
++	 */
++	int (*get_aval_color_bmp)(void *priv, u64 *aval_color_bmp);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index c2a0f1adb..73bf082f2 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -13175,7 +13175,6 @@ static void nl80211_parse_btm_candidate_info(struct candidate_list *candidate,
+ 		   num, MAC2STR(candidate->bssid), buf);
+ }
+ 
+-
+ static int
+ nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg)
+ {
+@@ -14630,6 +14629,202 @@ fail:
+ 	return -ENOBUFS;
+ }
+ 
++static int nl80211_get_aval_color_bmp_handler(struct nl_msg *msg, void *arg)
++{
++	u64 *aval_color_bmp = arg;
++	struct nlattr *tb[NL80211_ATTR_MAX + 1];
++	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX + 1];
++	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++	struct nlattr *nl_vend, *attr;
++
++	static const struct nla_policy
++	bss_color_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL + 1] = {
++		[MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP] = { .type = NLA_U64 },
++	};
++
++	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++			genlmsg_attrlen(gnlh, 0), NULL);
++
++	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++	if (!nl_vend)
++		return NL_SKIP;
++
++	nla_parse(tb_vendor, MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX,
++			nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++	*aval_color_bmp = nla_get_u64(tb_vendor[MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP]);
++
++	return 0;
++}
++
++static int nl80211_get_aval_color_bmp(void *priv, u64 *aval_color_bmp)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *attr;
++	int ret;
++
++	if (!drv->mtk_bss_color_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support BSS COLOR vendor cmd");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL))
++		return -ENOBUFS;
++
++	attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!attr) {
++		nlmsg_free(msg);
++		return -1;
++	}
++
++	nla_nest_end(msg, attr);
++
++	ret = send_and_recv_resp(drv, msg, nl80211_get_aval_color_bmp_handler, aval_color_bmp);
++
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to send BSS COLOR vendor cmd. ret=%d (%s) ",
++			   ret, strerror(-ret));
++	}
++	return ret;
++}
++
++static int nl80211_ap_wireless(void *priv, u8 sub_vendor_id, int value)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_wireless_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting ap wireless control");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	if (sub_vendor_id == MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE)
++		nla_put_u16(msg, sub_vendor_id, (u16) value);
++	else
++		nla_put_u8(msg, sub_vendor_id, (u8) value);
++
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set ap_wireless. ret=%d (%s)", ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
++static int nl80211_ap_rfeatures(void *priv, u8 sub_vendor_id, int value)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_rfeatures_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting ap rfeatures control");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	nla_put_u8(msg, sub_vendor_id, (u8) value);
++
++	nla_nest_end(msg, data);
++
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set rf_features. ret=%d (%s)", ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
++static int nl80211_ap_trigtype(void *priv, u8 enable, u8 type)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data, *data2;
++	int ret;
++
++	if (!drv->mtk_rfeatures_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting ap rfeatures control");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	data2 = nla_nest_start(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG);
++	if (!data2)
++		goto fail;
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN, enable);
++	nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE, type);
++
++	nla_nest_end(msg, data2);
++	nla_nest_end(msg, data);
++
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set trig_type. ret=%d (%s)", ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+ 	.desc = "Linux nl80211/cfg80211",
+@@ -14801,4 +14996,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.ibf_dump = nl80211_ibf_dump,
+ 	.amsdu_ctrl = nl80211_enable_amsdu,
+ 	.amsdu_dump = nl80211_dump_amsdu,
++	.get_aval_color_bmp = nl80211_get_aval_color_bmp,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 5aa813e26..5b4d45567 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -205,6 +205,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_3wire_vendor_cmd_avail:1;
+ 	unsigned int mtk_ibf_vendor_cmd_avail:1;
+ 	unsigned int mtk_wireless_vendor_cmd_avail:1;
++	unsigned int mtk_bss_color_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 4e2afb7f2..919f5bdf6 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1156,6 +1156,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL:
+ 					drv->mtk_wireless_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
++					drv->mtk_bss_color_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0029-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0029-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch
deleted file mode 100644
index 2d4b438..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0029-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch
+++ /dev/null
@@ -1,441 +0,0 @@
-From 63eb73fe7fe0cb9e088b95cffe4b123885bf9ede 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 029/104] mtk: hostapd: 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
-database. The second can iterate neighbor report database to build up neighbor
-report data.
-
-2. Support including neighbor report elements in ANQP response
-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. 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 +
- src/ap/ap_config.h     |   1 +
- src/ap/ctrl_iface_ap.c |  18 ++++++-
- src/ap/gas_serv.c      |  29 ++++++++++
- src/ap/gas_serv.h      |   2 +
- src/ap/neighbor_db.c   | 118 +++++++++++++++++++++++++++++++++++++++++
- src/ap/neighbor_db.h   |   9 ++++
- src/ap/wnm_ap.c        |  43 +++++++++++++--
- 9 files changed, 221 insertions(+), 5 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index f76226cf4..4240319b7 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -1300,6 +1300,11 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
- #endif /* CONFIG_DPP */
- 	} else if (os_strcasecmp(cmd, "setband") == 0) {
- 		ret = hostapd_ctrl_iface_set_band(hapd, value);
-+	} else if (os_strcasecmp(cmd, "bss_termination_tsf") == 0) {
-+		int termination_sec = atoi(value);
-+		hapd->conf->bss_termination_tsf = termination_sec;
-+		wpa_printf(MSG_DEBUG, "BSS Termination TSF: value = %d",
-+                termination_sec);
- 	} else {
- 		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 ca67aeb41..6c8b10291 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -175,6 +175,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
- 	bss->pasn_comeback_after = 10;
- 	bss->pasn_noauth = 1;
- #endif /* CONFIG_PASN */
-+	bss->bss_termination_tsf = 0;
- }
- 
- 
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index d10b00be9..379dc22cf 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -560,6 +560,7 @@ struct hostapd_bss_config {
- 	int wnm_sleep_mode;
- 	int wnm_sleep_mode_no_keys;
- 	int bss_transition;
-+	unsigned int bss_termination_tsf;
- 
- 	/* IEEE 802.11u - Interworking */
- 	int interworking;
-diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index cd7db4fc6..a2f89260c 100644
---- a/src/ap/ctrl_iface_ap.c
-+++ b/src/ap/ctrl_iface_ap.c
-@@ -1377,6 +1377,10 @@ int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
- 			wpa_printf(MSG_DEBUG, "Invalid bss_term data");
- 			return -1;
- 		}
-+		if (hapd->conf->bss_termination_tsf) {
-+			WPA_PUT_LE64(&bss_term_dur[2], hapd->conf->bss_termination_tsf);
-+		}
-+
- 		end++;
- 		WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
- 	}
-@@ -1403,16 +1407,26 @@ int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
- 		req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
- 	}
- 
--	if (os_strstr(cmd, " pref=1"))
-+	if (os_strstr(cmd, " pref=1")) {
- 		req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
-+		if (nei_len == 0) {
-+			// Add neigibor report from neighbor report db to nei_rep buffer
-+			nei_len = hostapd_neighbor_insert_buffer (hapd, nei_rep, 1000);
-+		}
-+	}
- 	if (os_strstr(cmd, " abridged=1"))
- 		req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
--	if (os_strstr(cmd, " disassoc_imminent=1"))
-+	if (os_strstr(cmd, " disassoc_imminent=1")) {
- 		req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
-+		/* Set own BSS neighbor report preference value as 0 */
-+		hostapd_neighbor_set_own_report_pref(hapd, nei_rep, nei_len, 0);
-+	}
- 	if (os_strstr(cmd, " link_removal_imminent=1"))
- 		req_mode |= WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT;
- 
- #ifdef CONFIG_MBO
-+	hostapd_neighbor_set_pref_by_non_pref_chan(hapd, sta, nei_rep, nei_len);
-+
- 	pos = os_strstr(cmd, "mbo=");
- 	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
---- a/src/ap/gas_serv.c
-+++ b/src/ap/gas_serv.c
-@@ -19,6 +19,7 @@
- #include "dpp_hostapd.h"
- #include "sta_info.h"
- #include "gas_serv.h"
-+#include "neighbor_db.h"
- 
- 
- #ifdef CONFIG_DPP
-@@ -369,6 +370,24 @@ static void anqp_add_network_auth_type(struct hostapd_data *hapd,
- 	}
- }
- 
-+static void anqp_add_neighbor_report(struct hostapd_data *hapd,
-+				       struct wpabuf *buf)
-+{
-+	struct hostapd_neighbor_entry *nr;
-+	u8 *len_pos = gas_anqp_add_element(buf, ANQP_NEIGHBOR_REPORT);
-+	if (dl_list_empty(&hapd->nr_db)) {
-+		wpabuf_put_le16(buf, 0);
-+	}
-+	else {
-+		dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list ) {
-+			wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
-+			wpabuf_put_u8(buf, wpabuf_len(nr->nr));
-+			wpabuf_put_buf(buf, nr->nr);
-+		}
-+	}
-+	gas_anqp_set_element_len(buf, len_pos);
-+}
-+
- 
- static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
- 					struct wpabuf *buf)
-@@ -986,6 +1005,9 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
- 		len += 1000;
- 	if (request & ANQP_REQ_ICON_REQUEST)
- 		len += 65536;
-+    if (request & ANQP_REQ_NEIGHBOR_REPORT) {
-+        len += (40 * hostapd_neighbor_count(hapd));
-+    }
- #ifdef CONFIG_FILS
- 	if (request & ANQP_FILS_REALM_INFO)
- 		len += 2 * dl_list_len(&hapd->conf->fils_realms);
-@@ -1028,6 +1050,8 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
- 		anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
- 	if (request & ANQP_REQ_EMERGENCY_NAI)
- 		anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
-+	if (request & ANQP_REQ_NEIGHBOR_REPORT)
-+		anqp_add_neighbor_report(hapd, buf);
- 
- 	for (i = 0; i < num_extra_req; i++) {
- #ifdef CONFIG_FILS
-@@ -1172,6 +1196,11 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
- 			     "Emergency NAI",
- 			     get_anqp_elem(hapd, info_id) != NULL, qi);
- 		break;
-+	case ANQP_NEIGHBOR_REPORT:
-+		set_anqp_req(ANQP_REQ_NEIGHBOR_REPORT,
-+			     "Neighbor Report",
-+			     get_anqp_elem(hapd, info_id) != NULL, qi);
-+		break;
- 	default:
- #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
---- a/src/ap/gas_serv.h
-+++ b/src/ap/gas_serv.h
-@@ -40,6 +40,8 @@
- 	(1 << (ANQP_TDLS_CAPABILITY - ANQP_QUERY_LIST))
- #define ANQP_REQ_EMERGENCY_NAI \
- 	(1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST))
-+#define ANQP_REQ_NEIGHBOR_REPORT \
-+	(1 << (ANQP_NEIGHBOR_REPORT - ANQP_QUERY_LIST))
- /*
-  * 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 f7a7d83d4..d9216a5ae 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)
- }
- 
- 
-+int hostapd_neighbor_count(struct hostapd_data *hapd)
-+{
-+	struct hostapd_neighbor_entry *nr;
-+	int count = 0;
-+
-+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
-+			 list) {
-+		count++;
-+	}
-+	return count;
-+}
-+
-+
-+int hostapd_neighbor_insert_buffer(struct hostapd_data *hapd, char *buf,
-+        size_t buflen)
-+{
-+	struct hostapd_neighbor_entry *nr;
-+	char *pos = buf;
-+
-+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
-+			 list) {
-+		/* For neighbor report IE, we only need bssid and nr*/
-+		*pos++ = WLAN_EID_NEIGHBOR_REPORT;
-+		*pos++ = wpabuf_len(nr->nr);
-+		os_memcpy(pos, wpabuf_head(nr->nr), wpabuf_len(nr->nr));
-+		pos += wpabuf_len(nr->nr);
-+	}
-+
-+	return pos - buf;
-+}
-+
-+
- static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
- {
- 	wpabuf_free(nr->nr);
-@@ -364,3 +396,89 @@ int hostapd_neighbor_sync_own_report(struct hostapd_data *hapd)
- 
- 	return 0;
- }
-+
-+void hostapd_neighbor_set_own_report_pref(struct hostapd_data *hapd, char *nei_buf,
-+			 size_t buflen, const int pref)
-+{
-+	struct hostapd_neighbor_entry *nr;
-+	char *pos, *next_nr;
-+
-+	pos = nei_buf;
-+	next_nr = nei_buf;
-+
-+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
-+			 list) {
-+		pos = next_nr;
-+		next_nr = pos + 2 + wpabuf_len(nr->nr);
-+		/* Shift 2 bytes for Element ID and Neighbor report length */
-+		pos = pos + 2;
-+		if(os_memcmp(pos, hapd->own_addr, ETH_ALEN) == 0) {
-+			/* Shift for BSSID + BSSID info + Op_class + channel num + PHY type */
-+			pos = pos + 6 + 4 + 1 + 1 + 1;
-+
-+			/* Iterate Subelement */
-+			while (next_nr - pos > 0) {
-+				if (*pos == 3) {
-+					pos = pos + 2;
-+					*pos = pref;
-+					return;
-+				} else {
-+					pos++;
-+					int shift_len = *pos++;
-+					pos = pos + shift_len;
-+				}
-+			}
-+		}
-+	}
-+}
-+
-+#ifdef CONFIG_MBO
-+void hostapd_neighbor_set_pref_by_non_pref_chan(struct hostapd_data *hapd,
-+			 struct sta_info* sta, char *nei_buf, size_t buflen)
-+{
-+	struct hostapd_neighbor_entry *nr;
-+	struct mbo_non_pref_chan_info *info;
-+	u8 i;
-+
-+	for(info = sta->non_pref_chan; info; info = info->next) {
-+		/* Check OP_Class and Channel num */
-+		for(i = 0; i < info->num_channels; i++) {
-+			char *pos, *next_nr;
-+
-+			pos = nei_buf;
-+			next_nr = nei_buf;
-+
-+			/* Iterate Neighbor report database */
-+			dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
-+					 list) {
-+				pos = next_nr;
-+				next_nr = pos + 2 + wpabuf_len(nr->nr);
-+				/**
-+				 * Shift 12 bytes for Element ID, Neighbor report length,
-+				 * BSSID and BSSID info.
-+				 */
-+				pos = pos + 12;
-+				int nr_op_class = *pos++;
-+				int nr_channel = *pos;
-+				if(info->op_class == nr_op_class && info->channels[i] == nr_channel) {
-+					/* Shift for Channel Num + PHY type */
-+					pos = pos + 1 + 1;
-+
-+					// Iterate Subelement
-+					while(next_nr - pos > 0) {
-+						if(*pos == 3) {
-+							pos = pos + 2;
-+							*pos = info->pref;
-+							break;
-+						}else {
-+							pos++;
-+							int shift_len = *pos++;
-+							pos = pos + shift_len;
-+						}
-+					}
-+				}
-+			}
-+		}
-+	}
-+}
-+#endif
-diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
-index 53f714203..cf1400256 100644
---- a/src/ap/neighbor_db.h
-+++ b/src/ap/neighbor_db.h
-@@ -25,4 +25,13 @@ int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
- 			    const struct wpa_ssid_value *ssid);
- void hostapd_free_neighbor_db(struct hostapd_data *hapd);
- 
-+int hostapd_neighbor_count(struct hostapd_data *hapd);
-+int hostapd_neighbor_insert_buffer(struct hostapd_data *hapd, char *buf,
-+        size_t buflen);
-+void hostapd_neighbor_set_own_report_pref(struct hostapd_data *hapd, char *nei_buf,
-+			 size_t buflen, const int pref);
-+#ifdef CONFIG_MBO
-+void hostapd_neighbor_set_pref_by_non_pref_chan(struct hostapd_data *hapd,
-+			 struct sta_info* sta, char *nei_buf, size_t buflen);
-+#endif
- #endif /* NEIGHBOR_DB_H */
-diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
-index d259200c9..4ac96b1be 100644
---- a/src/ap/wnm_ap.c
-+++ b/src/ap/wnm_ap.c
-@@ -20,6 +20,7 @@
- #include "ap/wpa_auth.h"
- #include "mbo_ap.h"
- #include "wnm_ap.h"
-+#include "ap/neighbor_db.h"
- 
- #define MAX_TFS_IE_LEN  1024
- 
-@@ -390,13 +391,24 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
- 	u8 *pos;
- 	int res;
- 
--	mgmt = os_zalloc(sizeof(*mgmt));
--	if (mgmt == NULL)
-+	int nr_num = hostapd_neighbor_count(hapd);
-+	int nr_size = ETH_ALEN + 4 + 1 + 1 + 1 + 5;
-+	int total_nr_size = nr_num * nr_size;
-+	u8 *nr_data = os_malloc(total_nr_size);
-+	int nr_data_len = 0;
-+	if(nr_data == NULL) {
-+		wpa_printf (MSG_ERROR, "Failed to allocate memory");
-+	} else {
-+	    nr_data_len = hostapd_neighbor_insert_buffer(hapd, nr_data, total_nr_size);
-+	}
-+	mgmt = os_zalloc(sizeof(*mgmt) + nr_data_len);
-+	if (mgmt == NULL) {
-+		wpa_printf (MSG_ERROR, "Failed to allocate memory for mgmt frame");
- 		return -1;
-+	}
- 
- 	sta = ap_get_sta(hapd, addr);
- 	own_addr = wnm_ap_get_own_addr(hapd, sta);
--
- 	os_memcpy(mgmt->da, addr, ETH_ALEN);
- 	os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
- 	os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
-@@ -406,10 +418,18 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
- 	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
- 	mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
- 	mgmt->u.action.u.bss_tm_req.req_mode = 0;
-+	if(nr_num) {
-+		mgmt->u.action.u.bss_tm_req.req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
-+	}
- 	mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
- 	mgmt->u.action.u.bss_tm_req.validity_interval = 1;
- 	pos = mgmt->u.action.u.bss_tm_req.variable;
- 
-+	if(nr_num) {
-+		os_memcpy(pos, nr_data, nr_data_len);
-+		pos += nr_data_len;
-+	}
-+
- 	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 "
-@@ -915,6 +935,22 @@ static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
- }
- 
- 
-+void bss_termination_disable_iface(void *eloop_ctx, void *timeout_ctx)
-+{
-+	struct hostapd_data *hapd = eloop_ctx;
-+	hostapd_disable_iface(hapd->iface);
-+}
-+
-+
-+static void set_disable_iface_timer(struct hostapd_data *hapd, struct sta_info *sta,
-+			       int disable_iface_timer)
-+{
-+	wpa_printf(MSG_DEBUG, "Disable interface timer set to %d secs", disable_iface_timer);
-+	eloop_register_timeout(disable_iface_timer, 0,
-+			       bss_termination_disable_iface, hapd, NULL);
-+}
-+
-+
- int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
- 				   struct sta_info *sta, const char *url,
- 				   int disassoc_timer)
-@@ -1006,6 +1042,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;
-+		set_disable_iface_timer(hapd, sta, hapd->conf->bss_termination_tsf);
- 	}
- 
- 	if (url) {
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0030-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0030-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch
new file mode 100644
index 0000000..f78e796
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0030-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch
@@ -0,0 +1,210 @@
+From e0914bbc141ee65c28697d891b718d3c6f6dbb78 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Mar 2023 16:08:30 +0800
+Subject: [PATCH 030/126] mtk: hostapd: Fix ZWDFS issue in BW 160
+
+When background radar is enabled and bandwidth is set to 160, AP will
+fail to startup due to the lack of non-DFS channel.
+Under this circumstance, AP should perform CAC itself, and the background
+chain could also perform CAC simultaneously.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 98 ++++++++++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 79 insertions(+), 19 deletions(-)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 4f8ef8111..9677b26ea 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -70,15 +70,22 @@ static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
+ static int dfs_channel_available(struct hostapd_channel_data *chan,
+ 				 enum dfs_channel_type type)
+ {
++	int dfs_status = chan->flag & HOSTAPD_CHAN_DFS_MASK;
++
++	if (chan->flag & HOSTAPD_CHAN_DISABLED)
++		return -1;
++
+ 	if (type == DFS_NO_CAC_YET) {
+ 		/* Select only radar channel where CAC has not been
+ 		 * performed yet
+ 		 */
+-		if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+-		    (chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+-		     HOSTAPD_CHAN_DFS_USABLE)
++		if (!(chan->flag & HOSTAPD_CHAN_RADAR))
++			return 0;
++
++		if (dfs_status == HOSTAPD_CHAN_DFS_USABLE)
+ 			return 1;
+-		return 0;
++
++		return -1;
+ 	}
+ 
+ 	/*
+@@ -87,16 +94,14 @@ static int dfs_channel_available(struct hostapd_channel_data *chan,
+ 	 * channel for CSA, unless they are available for immediate use.
+ 	 */
+ 	if (type == DFS_AVAILABLE && (chan->flag & HOSTAPD_CHAN_RADAR) &&
+-	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
+-	     HOSTAPD_CHAN_DFS_AVAILABLE))
+-		return 0;
++	    (dfs_status != HOSTAPD_CHAN_DFS_AVAILABLE))
++		return -1;
+ 
+-	if (chan->flag & HOSTAPD_CHAN_DISABLED)
+-		return 0;
+ 	if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+-	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+-	     HOSTAPD_CHAN_DFS_UNAVAILABLE))
+-		return 0;
++	    ((dfs_status == HOSTAPD_CHAN_DFS_UNAVAILABLE) ||
++	    (dfs_status == HOSTAPD_CHAN_DFS_UNKNOWN)))
++		return -1;
++
+ 	return 1;
+ }
+ 
+@@ -168,7 +173,7 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
+ 				    enum dfs_channel_type type)
+ {
+ 	struct hostapd_channel_data *first_chan, *chan;
+-	int i;
++	int i, available = 0, ret = 0;
+ 	u32 bw = num_chan_to_bw(num_chans);
+ 
+ 	if (first_chan_idx + num_chans > mode->num_channels) {
+@@ -204,14 +209,17 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
+ 			return 0;
+ 		}
+ 
+-		if (!dfs_channel_available(chan, type)) {
++		ret = dfs_channel_available(chan, type);
++		if (ret < 0) {
+ 			wpa_printf(MSG_DEBUG, "DFS: channel not available %d",
+ 				   first_chan->freq + i * 20);
+ 			return 0;
+ 		}
++
++		available |= ret;
+ 	}
+ 
+-	return 1;
++	return available;
+ }
+ 
+ 
+@@ -839,8 +847,12 @@ static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
+  */
+ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ {
++	struct hostapd_channel_data *channel;
+ 	int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
+-	int skip_radar = 0;
++	int sec = 0, skip_radar = 0;
++	u8 cf1 = 0, cf2 = 0;
++	bool use_radar_background = dfs_use_radar_background(iface);
++	enum dfs_channel_type channel_type = DFS_NO_CAC_YET;
+ 
+ 	if (is_6ghz_freq(iface->freq))
+ 		return 1;
+@@ -903,7 +915,7 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ 	/* Finally start CAC */
+ 	hostapd_set_state(iface, HAPD_IFACE_DFS);
+ 	wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz%s", iface->freq,
+-		   dfs_use_radar_background(iface) ? " (background)" : "");
++		   use_radar_background ? " (background)" : "");
+ 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
+ 		"freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
+ 		iface->freq,
+@@ -913,6 +925,16 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ 		hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
+ 		iface->dfs_cac_ms / 1000);
+ 
++	if (use_radar_background) {
++		channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, DFS_AVAILABLE);
++		/*
++		 * AP cannot get any random available channel.
++		 * Let AP and dedicated radar chain both perform CAC.
++		 */
++		if (!channel)
++			use_radar_background = false;
++	}
++
+ 	res = hostapd_start_dfs_cac(
+ 		iface, iface->conf->hw_mode, iface->freq, iface->conf->channel,
+ 		iface->conf->ieee80211n, iface->conf->ieee80211ac,
+@@ -921,14 +943,14 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ 		hostapd_get_oper_chwidth(iface->conf),
+ 		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+ 		hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
+-		dfs_use_radar_background(iface));
++		use_radar_background);
+ 
+ 	if (res) {
+ 		wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
+ 		return -1;
+ 	}
+ 
+-	if (dfs_use_radar_background(iface)) {
++	if (use_radar_background) {
+ 		/* Cache background radar parameters. */
+ 		iface->radar_background.channel = iface->conf->channel;
+ 		iface->radar_background.secondary_channel =
+@@ -949,6 +971,35 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ 
+ 		iface->radar_background.temp_ch = 1;
+ 		return 1;
++	} else if (dfs_use_radar_background(iface)) {
++		if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
++			channel_type = DFS_ANY_CHANNEL;
++
++		channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, channel_type);
++
++		if (!channel ||
++		    (channel->chan == iface->conf->channel &&
++		    cf1 == hostapd_get_oper_centr_freq_seg0_idx(iface->conf) &&
++		    cf2 == hostapd_get_oper_centr_freq_seg1_idx(iface->conf))) {
++			wpa_printf(MSG_ERROR, "Background radar could not get valid channel\n");
++			iface->radar_background.channel = -1;
++			return 0;
++		}
++
++		hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
++				      channel->freq, channel->chan,
++				      iface->conf->ieee80211n,
++				      iface->conf->ieee80211ac,
++				      iface->conf->ieee80211ax,
++				      iface->conf->ieee80211be,
++				      sec, hostapd_get_oper_chwidth(iface->conf),
++				      cf1, cf2, true);
++
++		iface->radar_background.channel = channel->chan;
++		iface->radar_background.freq = channel->freq;
++		iface->radar_background.secondary_channel = sec;
++		iface->radar_background.centr_freq_seg0_idx = cf1;
++		iface->radar_background.centr_freq_seg1_idx = cf2;
+ 	}
+ 
+ 	return 0;
+@@ -1214,6 +1265,15 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ 				hostapd_setup_interface_complete(iface, 0);
+ 				iface->cac_started = 0;
+ 			}
++
++			/*
++			 * When background radar is enabled but the CAC completion
++			 * is not received from the background chain.
++			 * Then, reset radar background chain.
++			 */
++			if (dfs_use_radar_background(iface) &&
++			    iface->radar_background.channel == -1)
++				hostapd_dfs_update_background_chain(iface);
+ 		}
+ 	} else if (hostapd_dfs_is_background_event(iface, freq)) {
+ 		iface->radar_background.cac_started = 0;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0030-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0030-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch
deleted file mode 100644
index 56988b8..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0030-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From d87f115b26430a4edd465d21be00ec61599c332e Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Tue, 20 Sep 2022 19:33:45 +0800
-Subject: [PATCH 030/104] mtk: hostapd: print sae groups by hostapd ctrl
-
----
- hostapd/ctrl_iface.c | 13 +++++++++++++
- 1 file changed, 13 insertions(+)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 4240319b7..1f950bc46 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -1376,6 +1376,19 @@ static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd,
- 		if (os_snprintf_error(buflen, res))
- 			return -1;
- 		return res;
-+	} else if (os_strcmp(cmd, "sae_group_capability") == 0) {
-+#ifdef CONFIG_SAE
-+		/* see sae_set_group() */
-+		res = os_snprintf(buf, buflen, "%s%s%s%s19 20 21",
-+				  dh_groups_get(15) ? "15 ": "",
-+				  dh_groups_get(16) ? "16 ": "",
-+				  dh_groups_get(17) ? "17 ": "",
-+				  dh_groups_get(18) ? "18 ": "");
-+
-+		if (os_snprintf_error(buflen, res))
-+			return -1;
-+		return res;
-+#endif /* CONFIG_SAE */
- 	}
- 
- 	return -1;
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0031-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0031-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch
new file mode 100644
index 0000000..9587c45
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0031-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch
@@ -0,0 +1,449 @@
+From 502598dedaa7d8fa86a06cf2d03d17d06798c224 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Tue, 24 Jan 2023 19:06:44 +0800
+Subject: [PATCH 031/126] mtk: hostapd: Add vendor for CAPI certification
+ commands
+
+Support new hostapd_cli command as below:
+$ hostapd_cli -i <intf> raw ap_rfeatures trig_variant=<type>
+
+This will prepare nl80211 msg
+MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_VARIANT_TYPE and then send to driver
+via nl80211.
+
+Support new hostapd_cli command as below:
+$ hostapd_cli -i <intf> -l <link_id> raw ap_rfeatures coding_type=<type>
+
+This will prepare nl80211 msg
+MTK_VENDOR_ATTR_RFEATURE_CTRL_CODING_TYPE and then send it to driver
+via nl80211.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ hostapd/ctrl_iface.c              | 103 ++++++++++++++++++++++++++++++
+ src/ap/ap_drv_ops.c               |  28 ++++++++
+ src/ap/ap_drv_ops.h               |   3 +
+ src/common/mtk_vendor.h           |  37 ++---------
+ src/drivers/driver.h              |  23 +++++++
+ src/drivers/driver_nl80211.c      |  63 +++++++++++++++++-
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   3 +
+ 8 files changed, 229 insertions(+), 32 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index b8a957468..6e7c03f81 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -72,6 +72,7 @@
+ #include "ctrl_iface.h"
+ #include "crypto/dh_groups.h"
+ 
++#include "common/mtk_vendor.h"
+ 
+ #define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
+ 
+@@ -4457,6 +4458,104 @@ hostapd_ctrl_iface_get_aval_color_bmp(struct hostapd_data *hapd, char *buf,
+ 	return pos - buf;
+ }
+ 
++static int
++hostapd_ctrl_iface_ap_wireless(struct hostapd_data *hapd, char *cmd,
++					 char *buf, size_t buflen)
++{
++	char *pos, *value, *config = cmd;
++	enum mtk_vendor_attr_wireless_ctrl sub_cmd;
++
++	pos = os_strchr(config, '=');
++	if (pos == NULL)
++		return -1;
++	*pos++ = '\0';
++
++	if(pos == NULL)
++		return -1;
++	value = pos;
++
++	if (os_strncmp(config, "fixed_mcs", 9) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS;
++	else if (os_strncmp(config, "ofdma", 5) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA;
++	else if (os_strncmp(config, "ppdu_type", 9) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE;
++	else if (os_strncmp(config, "nusers_ofdma", 12) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA;
++	else if (os_strncmp(config, "add_ba_req_bufsize", 18) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE;
++	else if (os_strncmp(config, "mimo", 4) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO;
++	else if (os_strncmp(config, "cert", 4) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT ;
++	else if (os_strncmp(config, "amsdu", 5) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU;
++	else if (os_strncmp(config, "rts_sigta", 9) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA;
++	else {
++		wpa_printf(MSG_ERROR,
++			"Unsupported parameter %s for ap_wireless", config);
++		return -1;
++	}
++
++	if (hostapd_drv_ap_wireless(hapd, (u8) sub_cmd, atoi(value)) != 0)
++		return -1;
++
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
++static int
++hostapd_ctrl_iface_ap_rfeatures(struct hostapd_data *hapd, char *cmd,
++					 char *buf, size_t buflen)
++{
++	char *pos, *value, *type, *config = cmd;
++	enum mtk_vendor_attr_rfeature_ctrl sub_cmd;
++
++	pos = os_strchr(config, '=');
++	if (pos == NULL)
++		return -1;
++	*pos++ = '\0';
++
++	if(pos == NULL)
++		return -1;
++	value = pos;
++
++	if (os_strncmp(config, "he_gi", 5) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI;
++	else if (os_strncmp(config, "he_ltf", 6) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF;
++	else if (os_strncmp(config, "trig_type", 9) == 0) {
++		pos = os_strchr(value, ',');
++		if (pos == NULL)
++			return -1;
++		*pos++ = '\0';
++		if(pos == NULL)
++			return -1;
++		type = pos;
++		goto trigtype;
++	} else if (os_strcmp(config, "ack_policy") == 0)
++		sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY;
++	else if (os_strcmp(config, "trig_variant") == 0)
++		sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_VARIANT_TYPE;
++	else if (os_strcmp(config, "coding_type") == 0)
++		sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_CODING_TYPE;
++	else {
++		wpa_printf(MSG_ERROR,
++			"Unsupported parameter %s for ap_rfeatures", config);
++		return -1;
++	}
++
++	if (hostapd_drv_ap_rfeatures(hapd, (u8) sub_cmd, atoi(value)) != 0)
++		return -1;
++	goto exit;
++
++trigtype:
++	if (hostapd_drv_ap_trig_type(hapd, atoi(value), atoi(type)) != 0)
++		return -1;
++
++exit:
++	return os_snprintf(buf, buflen, "OK\n");
++}
+ 
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+@@ -5090,6 +5189,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		reply_len = hostapd_ctrl_iface_get_bss_color(hapd, reply, reply_size);
+ 	} else if (os_strncmp(buf, "AVAL_COLOR_BMP", 14) == 0) {
+ 		reply_len = hostapd_ctrl_iface_get_aval_color_bmp(hapd, reply, reply_size);
++	} else if (os_strncmp(buf, "ap_wireless ", 12) == 0) {
++		reply_len = hostapd_ctrl_iface_ap_wireless(hapd, buf + 12, reply, reply_size);
++	} else if (os_strncmp(buf, "ap_rfeatures ", 13) == 0) {
++		reply_len = hostapd_ctrl_iface_ap_rfeatures(hapd, buf + 13, reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index efa4e2b4d..999cef7ad 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1359,3 +1359,31 @@ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_colo
+ 		return 0;
+ 	return hapd->driver->get_aval_color_bmp(hapd->drv_priv, aval_color_bmp);
+ }
++
++int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
++{
++	if (!hapd->driver || !hapd->driver->ap_wireless)
++		return 0;
++	return hapd->driver->ap_wireless(hapd->drv_priv, sub_vendor_id, value);
++}
++
++int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
++{
++	s8 link_id = -1;
++
++	if (!hapd->driver || !hapd->driver->ap_rfeatures)
++		return 0;
++
++	if (hapd->conf->mld_ap)
++		link_id = hapd->mld_link_id;
++
++	return hapd->driver->ap_rfeatures(hapd->drv_priv, sub_vendor_id, value,
++					  link_id);
++}
++
++int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type)
++{
++	if (!hapd->driver || !hapd->driver->ap_trigtype)
++		return 0;
++	return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index f57dff4aa..95b8dabbe 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -165,6 +165,9 @@ int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
+ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd,
+ 				       u64 *aval_color_bmp);
++int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
++int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
++int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 03daeb72a..aa9df4fc4 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -50,17 +50,6 @@ enum mtk_vendor_attr_edcca_dump {
+ 		NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
+ };
+ 
+-
+-static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
+-	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
+-	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
+-	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
+-	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
+-	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
+-	[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
+-	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_3wire_ctrl {
+ 	MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
+ 
+@@ -72,10 +61,6 @@ enum mtk_vendor_attr_3wire_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
+ };
+ 
+-static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
+-	[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_csi_ctrl {
+ 	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
+ 
+@@ -172,7 +157,8 @@ enum mtk_vendor_attr_wireless_ctrl {
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
+-	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT = 9,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
+@@ -191,11 +177,6 @@ enum mtk_vendor_attr_wireless_dump {
+ 		NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
+ };
+ 
+-static const struct nla_policy
+-wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
+-	[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_rfeature_ctrl {
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
+ 
+@@ -205,6 +186,10 @@ enum mtk_vendor_attr_rfeature_ctrl {
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_VARIANT_TYPE,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_CODING_TYPE,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_LINK_ID,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
+@@ -246,16 +231,6 @@ enum mtk_vendor_attr_ibf_dump {
+ 		NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
+ };
+ 
+-static struct nla_policy
+-ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
+-	[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
+-};
+-
+-static struct nla_policy
+-ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
+-	[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_bss_color_ctrl {
+ 	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
+ 
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 5d7eb5b4e..a0ce0aedd 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5307,6 +5307,29 @@ struct wpa_driver_ops {
+ 	 *
+ 	 */
+ 	int (*get_aval_color_bmp)(void *priv, u64 *aval_color_bmp);
++
++	/**
++	* ap_wireless - set wireless command
++	* @priv: Private driver interface data
++	* @value: value
++	*/
++	int (*ap_wireless)(void *priv, u8 mode, int value);
++
++	/**
++	* ap_rfeatures - set ap rf features command
++	* @priv: Private driver interface data
++	* @value: value
++	* @link_id: MLD link id. -1 if this is an non-MLD AP.
++	*/
++	int (*ap_rfeatures)(void *priv, u8 mode, int value, s8 link_id);
++
++	/**
++	* ap_trigtype - set trigger type
++	* @priv: Private driver interface data
++	* @enable: enable or disable
++	* @type: trigger type
++	*/
++	int (*ap_trigtype)(void *priv, u8 enable, u8 type);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 73bf082f2..7b743a1b8 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -88,6 +88,61 @@ static void handle_nl_debug_hook(struct nl_msg *msg, int tx)
+ 	wpa_netlink_hook(tx, nlh, nlh->nlmsg_len);
+ }
+ 
++static struct nla_policy
++ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
++	[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
++};
++
++static struct nla_policy
++ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
++	[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
++};
++
++static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
++	[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
++};
++
++static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
++};
++
++static const struct nla_policy
++wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
++	[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
++};
++
++static const struct nla_policy
++rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG] = { .type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_VARIANT_TYPE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_CODING_TYPE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_LINK_ID] = { .type = NLA_U8 },
++};
++
++static const struct nla_policy
++wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE] = {.type = NLA_U16 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
++};
++
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+ 	struct nl_sock *handle;
+@@ -14737,7 +14792,7 @@ fail:
+ 	return -ENOBUFS;
+ }
+ 
+-static int nl80211_ap_rfeatures(void *priv, u8 sub_vendor_id, int value)
++static int nl80211_ap_rfeatures(void *priv, u8 sub_vendor_id, int value, s8 link_id)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+@@ -14765,6 +14820,9 @@ static int nl80211_ap_rfeatures(void *priv, u8 sub_vendor_id, int value)
+ 
+ 	nla_put_u8(msg, sub_vendor_id, (u8) value);
+ 
++	if (link_id > -1)
++		nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_LINK_ID, link_id);
++
+ 	nla_nest_end(msg, data);
+ 
+ 	ret = send_and_recv_cmd(drv, msg);
+@@ -14997,4 +15055,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.amsdu_ctrl = nl80211_enable_amsdu,
+ 	.amsdu_dump = nl80211_dump_amsdu,
+ 	.get_aval_color_bmp = nl80211_get_aval_color_bmp,
++	.ap_wireless = nl80211_ap_wireless,
++	.ap_rfeatures = nl80211_ap_rfeatures,
++	.ap_trigtype = nl80211_ap_trigtype,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 5b4d45567..046991a3d 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -206,6 +206,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_ibf_vendor_cmd_avail:1;
+ 	unsigned int mtk_wireless_vendor_cmd_avail:1;
+ 	unsigned int mtk_bss_color_vendor_cmd_avail:1;
++	unsigned int mtk_rfeatures_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 919f5bdf6..ad7f5003a 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1159,6 +1159,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
+ 					drv->mtk_bss_color_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
++					drv->mtk_rfeatures_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0031-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0031-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch
deleted file mode 100644
index b149bda..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0031-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch
+++ /dev/null
@@ -1,198 +0,0 @@
-From 5453bd56aa134865ecaab20bde482b6c389831cb Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Tue, 31 May 2022 21:15:54 +0800
-Subject: [PATCH 031/104] mtk: hostapd: add support for runtime set in-band
- discovery
-
-Usage:
-hostapd_cli unsolic_probe_resp [tx_type] [interval]
-
-0: disable all in-band discovery
-1: enable unsolicited probe response
-2: enable FILS discovery
-
-Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
-
-The mac80211 layer already has a new variable "update",
-so the redundant variable "disable" has been removed.
-
-Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
----
- hostapd/ctrl_iface.c         | 66 ++++++++++++++++++++++++++++++++++++
- hostapd/hostapd_cli.c        | 20 +++++++++++
- src/ap/beacon.c              |  5 ++-
- src/drivers/driver_nl80211.c |  6 ++--
- 4 files changed, 94 insertions(+), 3 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 1f950bc46..fb9f09bb1 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -772,6 +772,69 @@ static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd,
- 
- #endif /* CONFIG_INTERWORKING */
- 
-+static int hostapd_ctrl_iface_inband_discovery(struct hostapd_data *hapd,
-+					       const char *cmd)
-+{
-+	struct hostapd_bss_config *conf = hapd->conf;
-+	const char *pos = cmd;
-+	int tx_type, interval, ret;
-+
-+	tx_type = atoi(pos);
-+	if (tx_type < 0 || tx_type > 2) {
-+		wpa_printf(MSG_ERROR, "Invalid tx type\n");
-+		return -1;
-+	}
-+
-+	pos = os_strchr(pos, ' ');
-+	if(!pos)
-+		return -1;
-+	pos++;
-+	interval = atoi(pos);
-+	if (interval < 0 || interval > 20) {
-+		wpa_printf(MSG_ERROR, "Invalid interval value\n");
-+		return -1;
-+	}
-+
-+	wpa_printf(MSG_ERROR, "Set inband discovery type:%d, interval:%d\n",
-+			      tx_type, interval);
-+
-+#define DISABLE_INBAND_DISC 0
-+#define UNSOL_PROBE_RESP 1
-+#define FILS_DISCOVERY 2
-+
-+#ifdef CONFIG_FILS
-+	conf->fils_discovery_max_int = 0;
-+	conf->fils_discovery_min_int = 0;
-+#endif /* CONFIG_FILS */
-+	conf->unsol_bcast_probe_resp_interval = 0;
-+
-+	switch (tx_type) {
-+	case DISABLE_INBAND_DISC:
-+	default:
-+		/* Disable both Unsolicited probe response and FILS discovery*/
-+		break;
-+	case UNSOL_PROBE_RESP:
-+		/* Enable Unsolicited probe response */
-+		conf->unsol_bcast_probe_resp_interval = interval;
-+		break;
-+#ifdef CONFIG_FILS
-+	case FILS_DISCOVERY:
-+		/* Enable FILS discovery */
-+		conf->fils_discovery_min_int = interval;
-+		conf->fils_discovery_max_int = interval;
-+		break;
-+#endif /* CONFIG_FILS */
-+	}
-+
-+	ret = ieee802_11_update_beacons(hapd->iface);
-+	if(ret) {
-+		wpa_printf(MSG_DEBUG,
-+			"Failed to update with inband discovery parameters\n");
-+		return -1;
-+	}
-+
-+	return 0;
-+}
- 
- #ifdef CONFIG_WNM_AP
- 
-@@ -4045,6 +4108,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 		if (hostapd_ctrl_iface_coloc_intf_req(hapd, buf + 15))
- 			reply_len = -1;
- #endif /* CONFIG_WNM_AP */
-+	} else if (os_strncmp(buf, "INBAND_DISCOVERY ", 17) == 0) {
-+		if (hostapd_ctrl_iface_inband_discovery(hapd, buf + 17))
-+			reply_len = -1;
- 	} else if (os_strcmp(buf, "GET_CONFIG") == 0) {
- 		reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
- 							  reply_size);
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index a469b1f4d..1fb6d999e 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -655,6 +655,24 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
- 	return wpa_ctrl_command(ctrl, buf);
- }
- 
-+static int hostapd_cli_cmd_inband_discovery(struct wpa_ctrl *ctrl, int argc,
-+					    char *argv[])
-+{
-+	char buf[300];
-+	int res;
-+
-+	if (argc < 2) {
-+		printf("Invalid 'inband_discovery' command - two arguments"
-+		       "tx_type interval\n");
-+		return -1;
-+	}
-+
-+	res = os_snprintf(buf, sizeof(buf), "INBAND_DISCOVERY %s %s",
-+			  argv[0], argv[1]);
-+	if (os_snprintf_error(sizeof(buf), res))
-+		return -1;
-+	return wpa_ctrl_command(ctrl, buf);
-+}
- 
- static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
- 					     char *argv[])
-@@ -1851,6 +1869,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 	{ "driver", hostapd_cli_cmd_driver, NULL,
- 	  "<driver sub command> [<hex formatted data>] = send driver command data" },
- #endif /* ANDROID */
-+	{ "inband_discovery", hostapd_cli_cmd_inband_discovery, NULL,
-+          "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
- 	{ NULL, NULL, NULL, NULL }
- };
- 
-diff --git a/src/ap/beacon.c b/src/ap/beacon.c
-index 26453cb2c..a5c46b067 100644
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -2055,6 +2055,8 @@ static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
- 				   struct wpa_driver_ap_params *params)
- {
- 	params->fd_max_int = hapd->conf->fils_discovery_max_int;
-+	params->unsol_bcast_probe_resp_interval =
-+		hapd->conf->unsol_bcast_probe_resp_interval;
- 	if (is_6ghz_op_class(hapd->iconf->op_class) &&
- 	    params->fd_max_int > FD_MAX_INTERVAL_6GHZ)
- 		params->fd_max_int = FD_MAX_INTERVAL_6GHZ;
-@@ -2063,7 +2065,8 @@ static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
- 	if (params->fd_min_int > params->fd_max_int)
- 		params->fd_min_int = params->fd_max_int;
- 
--	if (params->fd_max_int)
-+	if (params->fd_max_int || (is_6ghz_op_class(hapd->iconf->op_class) &&
-+	    !params->unsol_bcast_probe_resp_interval))
- 		return hostapd_gen_fils_discovery(hapd,
- 						  &params->fd_frame_tmpl_len);
- 
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 6778ad369..501d0e42e 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -4780,7 +4780,6 @@ static int nl80211_fils_discovery(struct i802_bss *bss, struct nl_msg *msg,
- 	     nla_put(msg, NL80211_FILS_DISCOVERY_ATTR_TMPL,
- 		     params->fd_frame_tmpl_len, params->fd_frame_tmpl)))
- 		return -1;
--
- 	nla_nest_end(msg, attr);
- 	return 0;
- }
-@@ -5412,7 +5411,10 @@ static int wpa_driver_nl80211_set_ap(void *priv,
- #endif /* CONFIG_SAE */
- 
- #ifdef CONFIG_FILS
--	if (params->fd_max_int && nl80211_fils_discovery(bss, msg, params) < 0)
-+	if ((params->fd_max_int ||
-+	    ((params->freq->freq > 5950 && params->freq->freq <= 7115) &&
-+	      !(params->unsol_bcast_probe_resp_interval))) &&
-+	     nl80211_fils_discovery(bss, msg, params) < 0)
- 		goto fail;
- #endif /* CONFIG_FILS */
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0032-mtk-hostapd-Add-mtk_vendor.h.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0032-mtk-hostapd-Add-mtk_vendor.h.patch
deleted file mode 100644
index e93390d..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0032-mtk-hostapd-Add-mtk_vendor.h.patch
+++ /dev/null
@@ -1,216 +0,0 @@
-From 5be34a5e9682f4448e41322dc4f96d5d9a58240e Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Mon, 30 May 2022 15:04:57 +0800
-Subject: [PATCH 032/104] mtk: hostapd: Add mtk_vendor.h
-
----
- src/common/mtk_vendor.h | 197 ++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 197 insertions(+)
- create mode 100644 src/common/mtk_vendor.h
-
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-new file mode 100644
-index 000000000..4a19d2fc9
---- /dev/null
-+++ b/src/common/mtk_vendor.h
-@@ -0,0 +1,197 @@
-+// SPDX-License-Identifier: ISC
-+/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
-+#ifndef MTK_VENDOR_H
-+#define MTK_VENDOR_H
-+
-+#define OUI_MTK    0x000ce7
-+
-+enum mtk_nl80211_vendor_subcmds {
-+	MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
-+	MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
-+	MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
-+	MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
-+	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
-+};
-+
-+enum mtk_vendor_attr_edcca_ctrl {
-+	MTK_VENDOR_ATTR_EDCCA_THRESHOLD_INVALID = 0,
-+
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_MODE,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_edcca_ctrl_mode {
-+	EDCCA_CTRL_SET_EN = 0,
-+	EDCCA_CTRL_SET_THERS,
-+	EDCCA_CTRL_GET_EN,
-+	EDCCA_CTRL_GET_THERS,
-+	EDCCA_CTRL_NUM,
-+};
-+
-+static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
-+};
-+
-+enum mtk_vendor_attr_csi_ctrl {
-+	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_CSI_CTRL_CFG,
-+	MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE,
-+	MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE,
-+	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
-+	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
-+	MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
-+	MTK_VENDOR_ATTR_CSI_CTRL_INTERVAL,
-+
-+	MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
-+
-+	MTK_VENDOR_ATTR_CSI_CTRL_DATA,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_CSI_CTRL,
-+	MTK_VENDOR_ATTR_CSI_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_CSI_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_csi_data {
-+	MTK_VENDOR_ATTR_CSI_DATA_UNSPEC,
-+	MTK_VENDOR_ATTR_CSI_DATA_PAD,
-+
-+	MTK_VENDOR_ATTR_CSI_DATA_VER,
-+	MTK_VENDOR_ATTR_CSI_DATA_TS,
-+	MTK_VENDOR_ATTR_CSI_DATA_RSSI,
-+	MTK_VENDOR_ATTR_CSI_DATA_SNR,
-+	MTK_VENDOR_ATTR_CSI_DATA_BW,
-+	MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
-+	MTK_VENDOR_ATTR_CSI_DATA_TA,
-+	MTK_VENDOR_ATTR_CSI_DATA_I,
-+	MTK_VENDOR_ATTR_CSI_DATA_Q,
-+	MTK_VENDOR_ATTR_CSI_DATA_INFO,
-+	MTK_VENDOR_ATTR_CSI_DATA_RSVD1,
-+	MTK_VENDOR_ATTR_CSI_DATA_RSVD2,
-+	MTK_VENDOR_ATTR_CSI_DATA_RSVD3,
-+	MTK_VENDOR_ATTR_CSI_DATA_RSVD4,
-+	MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
-+	MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
-+	MTK_VENDOR_ATTR_CSI_DATA_MODE,
-+	MTK_VENDOR_ATTR_CSI_DATA_H_IDX,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_CSI_DATA,
-+	MTK_VENDOR_ATTR_CSI_DATA_MAX =
-+		NUM_MTK_VENDOR_ATTRS_CSI_DATA - 1
-+};
-+
-+enum mtk_vendor_attr_mnt_ctrl {
-+	MTK_VENDOR_ATTR_AMNT_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_AMNT_CTRL_SET,
-+	MTK_VENDOR_ATTR_AMNT_CTRL_DUMP,
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_AMNT_CTRL,
-+	MTK_VENDOR_ATTR_AMNT_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_AMNT_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_mnt_set {
-+	MTK_VENDOR_ATTR_AMNT_SET_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_AMNT_SET_INDEX,
-+	MTK_VENDOR_ATTR_AMNT_SET_MACADDR,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_AMNT_SET,
-+	MTK_VENDOR_ATTR_AMNT_SET_MAX =
-+		NUM_MTK_VENDOR_ATTRS_AMNT_SET - 1
-+};
-+
-+enum mtk_vendor_attr_mnt_dump {
-+	MTK_VENDOR_ATTR_AMNT_DUMP_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_AMNT_DUMP_INDEX,
-+	MTK_VENDOR_ATTR_AMNT_DUMP_LEN,
-+	MTK_VENDOR_ATTR_AMNT_DUMP_RESULT,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
-+	MTK_VENDOR_ATTR_AMNT_DUMP_MAX =
-+		NUM_MTK_VENDOR_ATTRS_AMNT_DUMP - 1
-+};
-+
-+enum mtk_vendor_attr_wireless_ctrl {
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_rfeature_ctrl {
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
-+};
-+
-+#define CSI_MAX_COUNT 256
-+#define ETH_ALEN 6
-+
-+struct csi_data {
-+	s16 data_i[CSI_MAX_COUNT];
-+	s16 data_q[CSI_MAX_COUNT];
-+	s8 rssi;
-+	u8 snr;
-+	u32 ts;
-+	u8 data_bw;
-+	u8 pri_ch_idx;
-+	u8 ta[ETH_ALEN];
-+	u32 info;
-+	u8 rx_mode;
-+	u32 h_idx;
-+	u16 tx_idx;
-+	u16 rx_idx;
-+};
-+
-+struct amnt_data {
-+	u8 idx;
-+	u8 addr[ETH_ALEN];
-+	s8 rssi[4];
-+	u32 last_seen;
-+};
-+#endif /* MTK_VENDOR_H */
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0032-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0032-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch
new file mode 100644
index 0000000..40542c0
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0032-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch
@@ -0,0 +1,506 @@
+From 8d8d23a68c8e0b47457497d29a3834feb0748682 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Fri, 12 May 2023 05:18:48 +0800
+Subject: [PATCH 032/126] mtk: hostapd: Air Monitor support in hostapd by
+ vendor
+
+Signed-off-by: mtk23888 <dipanshu.mittal@mediatek.com>
+---
+ hostapd/ctrl_iface.c              | 113 +++++++++++++++++++
+ hostapd/hostapd_cli.c             |  15 +++
+ src/ap/ap_drv_ops.c               |  14 +++
+ src/ap/ap_drv_ops.h               |   3 +
+ src/common/mtk_vendor.h           |   8 ++
+ src/drivers/driver.h              |  16 +++
+ src/drivers/driver_nl80211.c      | 179 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   2 +
+ 9 files changed, 351 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 6e7c03f81..03831a908 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4500,6 +4500,44 @@ hostapd_ctrl_iface_ap_wireless(struct hostapd_data *hapd, char *cmd,
+ 
+ 	if (hostapd_drv_ap_wireless(hapd, (u8) sub_cmd, atoi(value)) != 0)
+ 		return -1;
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
++static int
++hostapd_ctrl_iface_set_amnt(struct hostapd_data *hapd, char *cmd,
++					char *buf, size_t buflen)
++{
++	char *tmp, sta_mac[ETH_ALEN] = {0};
++	int amnt_idx = 0;
++
++	tmp = strtok_r(cmd, " ", &cmd);
++
++	if (!tmp) {
++		wpa_printf(MSG_ERROR, "Error in command format\n");
++		return -1;
++	}
++
++	amnt_idx = strtol(tmp, &tmp, 10);
++
++	if (amnt_idx < 0 || amnt_idx > 15) {
++		wpa_printf(MSG_ERROR, "Wrong AMNT index %d\n", amnt_idx);
++		return -1;
++	}
++
++	if (!cmd) {
++		wpa_printf(MSG_ERROR, "Error in command format\n");
++		return -1;
++	}
++
++	if (hwaddr_aton(cmd, sta_mac) < 0) {
++		wpa_printf(MSG_ERROR, "station mac is not right.\n");
++		return -1;
++	}
++
++	if (hostapd_drv_amnt_set(hapd, amnt_idx, sta_mac)) {
++		wpa_printf(MSG_ERROR, "Not able to set amnt index\n");
++		return -1;
++	}
+ 
+ 	return os_snprintf(buf, buflen, "OK\n");
+ }
+@@ -4557,6 +4595,75 @@ exit:
+ 	return os_snprintf(buf, buflen, "OK\n");
+ }
+ 
++static int
++hostapd_ctrl_iface_dump_amnt(struct hostapd_data *hapd, char *cmd,
++				char *buf, size_t buflen)
++{
++	char *tmp;
++	int amnt_idx = 0, ret = 0;
++	struct amnt_resp_data *resp_buf;
++	char *pos, *end;
++	struct amnt_data *res;
++
++	pos = buf;
++	end = buf + buflen;
++
++	tmp = strtok_r(cmd, " ", &cmd);
++
++	if (!tmp) {
++		wpa_printf(MSG_ERROR, "Error in command format\n");
++		return -1;
++	}
++
++	amnt_idx = strtoul(tmp, &tmp, 0);
++
++	if ((amnt_idx < 0 || amnt_idx > 15) && amnt_idx != 0xff) {
++		wpa_printf(MSG_ERROR, "Wrong AMNT index\n");
++		return -1;
++	}
++
++	if (amnt_idx == 0xff)
++		resp_buf = (struct amnt_resp_data *) os_zalloc(AIR_MONITOR_MAX_ENTRY
++							* sizeof(struct amnt_data) + 1);
++	else
++		resp_buf = (struct amnt_resp_data *) os_zalloc(sizeof(struct amnt_data) + 1);
++
++	if (resp_buf == NULL) {
++		wpa_printf(MSG_ERROR, "Error in memory allocation\n");
++		return -1;
++	}
++
++	if (hostapd_drv_amnt_dump(hapd, amnt_idx, (u8 *)resp_buf)) {
++		wpa_printf(MSG_ERROR, "Not able to set amnt index\n");
++		os_free(resp_buf);
++		return -1;
++	}
++
++	for (int i = 0; i < resp_buf->sta_num && i < AIR_MONITOR_MAX_ENTRY; i++) {
++		res = &resp_buf->resp_data[i];
++		ret = os_snprintf(pos, end - pos,
++				"[hostapd_cli] amnt_idx: %d, addr="MACSTR
++				", rssi=%d/%d/%d/%d, last_seen=%u\n",
++				res->idx,
++				MAC2STR(res->addr), res->rssi[0],
++				res->rssi[1], res->rssi[2],
++				res->rssi[3], res->last_seen);
++		if (os_snprintf_error(end - pos, ret)) {
++			os_free(resp_buf);
++			return 0;
++		}
++		pos = pos + ret;
++	}
++
++	os_free(resp_buf);
++
++	if (pos == buf)
++		return os_snprintf(buf, buflen, "Index %d is not monitored\n",
++				amnt_idx);
++	else
++		return pos - buf;
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -5193,6 +5300,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		reply_len = hostapd_ctrl_iface_ap_wireless(hapd, buf + 12, reply, reply_size);
+ 	} else if (os_strncmp(buf, "ap_rfeatures ", 13) == 0) {
+ 		reply_len = hostapd_ctrl_iface_ap_rfeatures(hapd, buf + 13, reply, reply_size);
++	} else if (os_strncmp(buf, "SET_AMNT", 8) == 0) {
++		reply_len = hostapd_ctrl_iface_set_amnt(hapd, buf+9,
++							reply, reply_size);
++	} else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
++		reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
++							reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 27d61b06d..dba805e8a 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1718,6 +1718,17 @@ static int hostapd_cli_cmd_get_amsdu(struct wpa_ctrl *ctrl, int argc,
+ 	return hostapd_cli_cmd(ctrl, "GET_AMSDU", 0, NULL, NULL);
+ }
+ 
++static int hostapd_cli_cmd_set_amnt(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "SET_AMNT", 2, argc, argv);
++}
++
++static int hostapd_cli_cmd_dump_amnt(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "DUMP_AMNT", 1, argc, argv);
++}
+ 
+ struct hostapd_cli_cmd {
+ 	const char *cmd;
+@@ -1957,6 +1968,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 	  " = show iBF state (enabled/disabled)"},
+ 	{ "get_amsdu", hostapd_cli_cmd_get_amsdu, NULL,
+ 		" = show AMSDU state"},
++	{ "set_amnt", hostapd_cli_cmd_set_amnt, NULL,
++		" = Set Station index and mac to monitor"},
++	{ "dump_amnt", hostapd_cli_cmd_dump_amnt, NULL,
++		" = Dump RSSI of monitoring Station"},
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 999cef7ad..5988bd11c 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1387,3 +1387,17 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type)
+ 		return 0;
+ 	return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type);
+ }
++
++int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac)
++{
++	if (!hapd->driver || !hapd->driver->amnt_set)
++		return 0;
++	return hapd->driver->amnt_set(hapd->drv_priv, amnt_idx, amnt_sta_mac);
++}
++
++int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf)
++{
++	if (!hapd->driver || !hapd->driver->amnt_dump)
++		return 0;
++	return hapd->driver->amnt_dump(hapd->drv_priv, amnt_idx, amnt_dump_buf);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 95b8dabbe..d914fd967 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -169,6 +169,9 @@ int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int val
+ int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
+ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
+ 
++int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
++int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
++
+ #include "drivers/driver.h"
+ 
+ int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index aa9df4fc4..ee0c15eb3 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -261,10 +261,18 @@ struct csi_data {
+ 	u16 rx_idx;
+ };
+ 
++#define AIR_MONITOR_MAX_ENTRY 16
++
+ struct amnt_data {
+ 	u8 idx;
+ 	u8 addr[ETH_ALEN];
+ 	s8 rssi[4];
+ 	u32 last_seen;
+ };
++
++struct amnt_resp_data {
++	u8 sta_num;
++	struct amnt_data resp_data[0];
++};
++
+ #endif /* MTK_VENDOR_H */
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index a0ce0aedd..4bc0bbeae 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5330,6 +5330,22 @@ struct wpa_driver_ops {
+ 	* @type: trigger type
+ 	*/
+ 	int (*ap_trigtype)(void *priv, u8 enable, u8 type);
++
++	/**
++	* amnt_set - add/delete station from monitoring
++	* @priv: Private driver interface data
++	* @amnt_idx: Monitor Index
++	* @amnt_sta_mac: station mac address
++	*/
++	int (*amnt_set)(void *priv, u8 amnt_idx, u8 *amnt_sta_mac);
++
++	/**
++	* amnt_dump - Dump particular/ all station
++	* @priv: Private driver interface data
++	* @amnt_idx: Monitor Index
++	* @amnt_dump_buf: Buffer to print
++	*/
++	int (*amnt_dump)(void *priv, u8 amnt_idx, u8 *amnt_dump_buf);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 7b743a1b8..db8a6f8e4 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -143,6 +143,19 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
+ 	[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
+ };
+ 
++static struct nla_policy
++amnt_ctrl_policy[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL] = {
++	[MTK_VENDOR_ATTR_AMNT_CTRL_SET] = {.type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP] = { .type = NLA_NESTED },
++};
++
++static struct nla_policy
++amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
++	[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_AMNT_DUMP_LEN] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
++};
++
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+ 	struct nl_sock *handle;
+@@ -14883,6 +14896,170 @@ fail:
+ 	return -ENOBUFS;
+ }
+ 
++static int
++nl80211_amnt_set(void *priv, u8 amnt_idx, u8 *amnt_sta_mac)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	void *tb1;
++	int ret;
++
++	if (!drv->mtk_amnt_vendor_cmd_avail) {
++		wpa_printf(MSG_ERROR,
++			"nl80211: Driver does not support air monitor");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++			nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++	if (!data)
++		goto fail;
++
++	tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_AMNT_CTRL_SET);
++	if (!tb1)
++		goto fail;
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_AMNT_SET_INDEX, amnt_idx);
++
++	nla_put(msg, MTK_VENDOR_ATTR_AMNT_SET_MACADDR, ETH_ALEN, amnt_sta_mac);
++
++	nla_nest_end(msg, tb1);
++	nla_nest_end(msg, data);
++
++	ret = send_and_recv_cmd(drv, msg);
++
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set air monitor. ret=%d (%s)",
++			ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++
++}
++
++static int
++mt76_amnt_dump_cb(struct nl_msg *msg, void *arg)
++{
++	struct nlattr *tb[NL80211_ATTR_MAX + 1];
++	struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
++	struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP];
++	struct nlattr *attr, *cur, *data;
++	struct amnt_data *res;
++	int len = 0, rem;
++	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++	struct amnt_resp_data *amnt_dump = (struct amnt_resp_data *)arg;
++
++	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++		genlmsg_attrlen(gnlh, 0), NULL);
++
++	attr = tb[NL80211_ATTR_VENDOR_DATA];
++	if (!attr)
++		return NL_SKIP;
++
++	nla_parse_nested(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX,
++			attr, amnt_ctrl_policy);
++
++	if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP])
++		return NL_SKIP;
++
++	nla_parse_nested(tb2, NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
++			tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP], amnt_dump_policy);
++
++	if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_LEN])
++		return NL_SKIP;
++
++	len = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_DUMP_LEN]);
++	if (!len)
++		return 0;
++
++	if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT])
++		return NL_SKIP;
++
++	data = tb2[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT];
++
++	nla_for_each_nested(cur, data, rem) {
++		if (amnt_dump->sta_num >= AIR_MONITOR_MAX_ENTRY)
++			return NL_SKIP;
++		res = (struct amnt_data *) nla_data(cur);
++		wpa_printf(MSG_ERROR, "[vendor] amnt_idx: %d, "
++			"addr="MACSTR", "
++			"rssi=%d/%d/%d/%d, last_seen=%u\n",
++			res->idx,
++			MAC2STR(res->addr),
++			res->rssi[0], res->rssi[1], res->rssi[2],
++			res->rssi[3], res->last_seen);
++		os_memcpy(&amnt_dump->resp_data[amnt_dump->sta_num], res,
++			sizeof(struct amnt_data));
++		amnt_dump->sta_num++;
++	}
++	return 0;
++}
++
++static int
++nl80211_amnt_dump(void *priv, u8 amnt_idx, u8 *dump_buf)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	void *tb1;
++	int ret;
++
++	if (!drv->mtk_amnt_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			"nl80211: Driver does not support air monitor");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++			nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++	if (!data)
++		goto fail;
++
++	tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_AMNT_CTRL_DUMP
++			| NLA_F_NESTED);
++	if (!tb1)
++		goto fail;
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_AMNT_DUMP_INDEX, amnt_idx);
++
++	nla_nest_end(msg, tb1);
++	nla_nest_end(msg, data);
++
++	ret = send_and_recv_resp(drv, msg, mt76_amnt_dump_cb, dump_buf);
++
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to Dump air monitor. ret=%d (%s)"
++			, ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+ 	.desc = "Linux nl80211/cfg80211",
+@@ -15058,4 +15235,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.ap_wireless = nl80211_ap_wireless,
+ 	.ap_rfeatures = nl80211_ap_rfeatures,
+ 	.ap_trigtype = nl80211_ap_trigtype,
++	.amnt_set = nl80211_amnt_set,
++	.amnt_dump = nl80211_amnt_dump,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 046991a3d..adc1b9bf7 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -207,6 +207,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_wireless_vendor_cmd_avail:1;
+ 	unsigned int mtk_bss_color_vendor_cmd_avail:1;
+ 	unsigned int mtk_rfeatures_vendor_cmd_avail:1;
++	unsigned int mtk_amnt_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index ad7f5003a..4a6ff0f28 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1158,6 +1158,8 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 					break;
+ 				case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
+ 					drv->mtk_bss_color_vendor_cmd_avail = 1;
++				case MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL:
++					drv->mtk_amnt_vendor_cmd_avail = 1;
+ 					break;
+ 				case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
+ 					drv->mtk_rfeatures_vendor_cmd_avail = 1;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0033-mtk-hostapd-Add-muru-user-number-debug-command.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0033-mtk-hostapd-Add-muru-user-number-debug-command.patch
new file mode 100644
index 0000000..0f5d6ef
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0033-mtk-hostapd-Add-muru-user-number-debug-command.patch
@@ -0,0 +1,228 @@
+From f54048fe93acc757c0cb144077d4e4e3fb2ac9fc Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Fri, 12 May 2023 05:24:19 +0800
+Subject: [PATCH 033/126] mtk: hostapd: Add muru user number debug command
+
+---
+ hostapd/ctrl_iface.c         | 13 ++++++++++++-
+ src/ap/ap_drv_ops.c          |  4 ++--
+ src/ap/ap_drv_ops.h          |  2 +-
+ src/ap/hostapd.c             |  3 ++-
+ src/common/mtk_vendor.h      |  7 +++++++
+ src/drivers/driver.h         |  4 ++--
+ src/drivers/driver_nl80211.c | 37 ++++++++++++++++++++++++++++--------
+ 7 files changed, 55 insertions(+), 15 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 03831a908..b78ee24c0 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -3803,6 +3803,8 @@ hostapd_ctrl_iface_set_edcca(struct hostapd_data *hapd, char *cmd,
+ 					 char *buf, size_t buflen)
+ {
+ 	char *pos, *config, *value;
++	u8 mode;
++
+ 	config = cmd;
+ 	pos = os_strchr(config, ' ');
+ 	if (pos == NULL)
+@@ -4195,6 +4197,8 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ 					 char *buf, size_t buflen)
+ {
+ 	char *pos, *config, *value;
++	u8 mode;
++
+ 	config = cmd;
+ 	pos = os_strchr(config, ' ');
+ 	if (pos == NULL)
+@@ -4212,13 +4216,20 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ 			return -1;
+ 		}
+ 		hapd->iconf->mu_onoff = (u8) mu;
++		mode = MU_CTRL_ONOFF;
++	} else if (os_strcmp(config, "ul_user_cnt") == 0) {
++		mode = MU_CTRL_UL_USER_CNT;
++		wpa_printf(MSG_ERROR, "ul_user_cnt:%d\n", (u8)atoi(value));
++	} else if (os_strcmp(config, "dl_user_cnt") == 0) {
++		mode = MU_CTRL_DL_USER_CNT;
++		wpa_printf(MSG_ERROR, "dl_user_cnt:%d\n", (u8)atoi(value));
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			"Unsupported parameter %s for SET_MU", config);
+ 		return -1;
+ 	}
+ 
+-	if(hostapd_drv_mu_ctrl(hapd) == 0) {
++	if(hostapd_drv_mu_ctrl(hapd, mode, (u8)atoi(value)) == 0) {
+ 		return os_snprintf(buf, buflen, "OK\n");
+ 	} else {
+ 		return -1;
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 5988bd11c..496fd4263 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1299,11 +1299,11 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
+ 	return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
+ }
+ 
+-int hostapd_drv_mu_ctrl(struct hostapd_data *hapd)
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val)
+ {
+ 	if (!hapd->driver || !hapd->driver->mu_ctrl)
+ 		return 0;
+-	return hapd->driver->mu_ctrl(hapd->drv_priv, hapd->iconf->mu_onoff);
++	return hapd->driver->mu_ctrl(hapd->drv_priv, mode, val);
+ }
+ 
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index d914fd967..a949e168d 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -156,7 +156,7 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
+ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
+ 					  const int *threshold);
+ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+-int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val);
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 6ab3e6043..9e1ba6a21 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -58,6 +58,7 @@
+ #include "wpa_auth_kay.h"
+ #include "hw_features.h"
+ 
++#include "common/mtk_vendor.h"
+ 
+ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
+ #ifdef CONFIG_WEP
+@@ -2762,7 +2763,7 @@ dfs_offload:
+ 	if (hostapd_drv_configure_edcca_threshold(hapd,
+ 						  hapd->iconf->edcca_threshold) < 0)
+ 		goto fail;
+-	if (hostapd_drv_mu_ctrl(hapd) < 0)
++	if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF, hapd->iconf->mu_onoff) < 0)
+ 		goto fail;
+ 	if (hostapd_drv_three_wire_ctrl(hapd) < 0)
+ 		goto fail;
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index ee0c15eb3..34238f098 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -202,6 +202,8 @@ enum mtk_vendor_attr_mu_ctrl {
+ 
+ 	MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
+ 	MTK_VENDOR_ATTR_MU_CTRL_DUMP,
++	MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE,
++	MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
+@@ -275,4 +277,9 @@ struct amnt_resp_data {
+ 	struct amnt_data resp_data[0];
+ };
+ 
++enum {
++	MU_CTRL_ONOFF,
++	MU_CTRL_DL_USER_CNT,
++	MU_CTRL_UL_USER_CNT,
++};
+ #endif /* MTK_VENDOR_H */
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 4bc0bbeae..c8910739e 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5262,11 +5262,11 @@ struct wpa_driver_ops {
+ 	int (*get_edcca)(void *priv, const u8 mode, u8 *value);
+ 
+ 	/**
+-	 * mu_ctrl - ctrl on off for UL/DL MURU
++	 * mu_ctrl - ctrl for UL/DL MURU
+ 	 * @priv: Private driver interface data
+ 	 *
+ 	 */
+-	 int (*mu_ctrl)(void *priv, u8 mu_onoff);
++	 int (*mu_ctrl)(void *priv, u8 mode, u8 val);
+ 	 int (*mu_dump)(void *priv, u8 *mu_onoff);
+ 
+ 	/**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index db8a6f8e4..da1e6a8ed 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14007,13 +14007,13 @@ fail:
+ 
+ 
+ #ifdef CONFIG_IEEE80211AX
+-static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
++static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+ 	struct nl_msg *msg;
+ 	struct nlattr *data;
+-	int ret;
++	int ret = -ENOBUFS;
+ 
+ 	if (!drv->mtk_mu_vendor_cmd_avail) {
+ 		wpa_printf(MSG_INFO,
+@@ -14024,17 +14024,38 @@ static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
+ 	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ 		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
+ 		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
+-		!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+-		nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, mu_onoff)) {
+-		nlmsg_free(msg);
+-		return -ENOBUFS;
++		!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)))
++		goto fail;
++
++	switch (mode) {
++	case MU_CTRL_ONOFF:
++			if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, val))
++				goto fail;
++		break;
++	case MU_CTRL_UL_USER_CNT:
++	case MU_CTRL_DL_USER_CNT:
++			if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE, mode) ||
++			    nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL, val))
++				goto fail;
++		break;
++	default:
++		wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode !");
++		ret = -EINVAL;
++		goto fail;
+ 	}
++
+ 	nla_nest_end(msg, data);
++
+ 	ret = send_and_recv_cmd(drv, msg);
+ 	if(ret){
+-		wpa_printf(MSG_ERROR, "Failed to set mu_onoff. ret=%d (%s)", ret, strerror(-ret));
++		wpa_printf(MSG_ERROR, "Failed to set mu_ctrl. ret=%d (%s)", ret, strerror(-ret));
+ 	}
+ 	return ret;
++
++fail:
++	nl80211_nlmsg_clear(msg);
++	nlmsg_free(msg);
++	return ret;
+ }
+ 
+ 
+@@ -15206,7 +15227,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.update_connect_params = nl80211_update_connection_params,
+ 	.send_external_auth_status = nl80211_send_external_auth_status,
+ 	.set_4addr_mode = nl80211_set_4addr_mode,
+-	.mu_ctrl = nl80211_mu_onoff,
++	.mu_ctrl = nl80211_mu_ctrl,
+ 	.mu_dump = nl80211_mu_dump,
+ #ifdef CONFIG_DPP
+ 	.dpp_listen = nl80211_dpp_listen,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0033-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0033-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch
deleted file mode 100644
index d7544bc..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0033-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch
+++ /dev/null
@@ -1,630 +0,0 @@
-From 7b735e95647fe46cc5dcf7d859c8f9abbed5ae0d Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Mon, 30 May 2022 16:31:34 +0800
-Subject: [PATCH 033/104] mtk: hostapd: Support EDCCA hostapd configuration
-
-edcca_enable and edcca_compensation and implement edcca related handlers.
----
- hostapd/config_file.c             |  34 ++++++
- hostapd/ctrl_iface.c              | 124 +++++++++++++++++++++
- src/ap/ap_config.c                |   4 +
- src/ap/ap_config.h                |  30 ++++++
- src/ap/ap_drv_ops.c               |  24 +++++
- src/ap/ap_drv_ops.h               |   4 +
- src/ap/hostapd.c                  |   7 ++
- src/common/mtk_vendor.h           |  20 ++--
- src/drivers/driver.h              |   4 +
- src/drivers/driver_nl80211.c      | 174 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |   1 +
- src/drivers/driver_nl80211_capa.c |   7 ++
- 12 files changed, 427 insertions(+), 6 deletions(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 0094db279..f8c1eec0a 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5348,6 +5348,40 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		bss->mld_indicate_disabled = atoi(pos);
- #endif /* CONFIG_TESTING_OPTIONS */
- #endif /* CONFIG_IEEE80211BE */
-+	} else if (os_strcmp(buf, "edcca_threshold") == 0) {
-+		if (hostapd_parse_intlist(&conf->edcca_threshold, pos) ||
-+		    conf->edcca_threshold[0] < EDCCA_MIN_CONFIG_THRES ||
-+		    conf->edcca_threshold[0] > EDCCA_MAX_CONFIG_THRES ||
-+		    conf->edcca_threshold[1] < EDCCA_MIN_CONFIG_THRES ||
-+		    conf->edcca_threshold[1] > EDCCA_MAX_CONFIG_THRES ||
-+		    conf->edcca_threshold[2] < EDCCA_MIN_CONFIG_THRES ||
-+		    conf->edcca_threshold[2] > EDCCA_MAX_CONFIG_THRES ||
-+		    conf->edcca_threshold[3] < EDCCA_MIN_CONFIG_THRES ||
-+		    conf->edcca_threshold[3] > EDCCA_MAX_CONFIG_THRES) {
-+			wpa_printf(MSG_ERROR, "Line %d: invalid edcca threshold",
-+				   line);
-+			return 1;
-+		}
-+	} else if (os_strcmp(buf, "edcca_enable") == 0) {
-+		int mode = atoi(pos);
-+		if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
-+			wpa_printf(MSG_ERROR, "Line %d: Invalid edcca_enable %d;"
-+				  " allowed value 0 (Force Disable) or 1(Auto) ",
-+				   line, mode);
-+			return 1;
-+		}
-+		conf->edcca_enable = (u8) mode;
-+	} else if (os_strcmp(buf, "edcca_compensation") == 0) {
-+		int val = atoi(pos);
-+		if (val < EDCCA_MIN_COMPENSATION ||
-+		    val > EDCCA_MAX_COMPENSATION) {
-+			wpa_printf(MSG_ERROR, "Line %d: Invalid compensation"
-+				   " value %d; allowed value %d ~ %d.",
-+				   line, val, EDCCA_MIN_COMPENSATION,
-+				   EDCCA_MAX_COMPENSATION);
-+			return 1;
-+		}
-+		conf->edcca_compensation = (s8) val;
- 	} else {
- 		wpa_printf(MSG_ERROR,
- 			   "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index fb9f09bb1..78a3380f2 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -544,6 +544,19 @@ static const char * pbc_status_str(enum pbc_status status)
- }
- 
- 
-+static const char *edcca_mode_str(enum edcca_mode status)
-+{
-+	switch (status) {
-+		case EDCCA_MODE_FORCE_DISABLE:
-+			return "Force Disable";
-+		case EDCCA_MODE_AUTO:
-+			return "Auto";
-+		default:
-+			return "Unknown";
-+	}
-+}
-+
-+
- static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd,
- 					     char *buf, size_t buflen)
- {
-@@ -3644,6 +3657,111 @@ static int hostapd_ctrl_iface_link_remove(struct hostapd_data *hapd, char *cmd,
- #endif /* CONFIG_TESTING_OPTIONS */
- #endif /* CONFIG_IEEE80211BE */
- 
-+static int
-+hostapd_ctrl_iface_set_edcca(struct hostapd_data *hapd, char *cmd,
-+					 char *buf, size_t buflen)
-+{
-+	char *pos, *config, *value;
-+	config = cmd;
-+	pos = os_strchr(config, ' ');
-+	if (pos == NULL)
-+		return -1;
-+	*pos++ = '\0';
-+
-+	if (pos == NULL)
-+		return -1;
-+	value = pos;
-+
-+	if (os_strcmp(config, "enable") == 0) {
-+		int mode = atoi(value);
-+		if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
-+			wpa_printf(MSG_ERROR, "Invalid value for edcca enable");
-+			return -1;
-+		}
-+		hapd->iconf->edcca_enable = (u8) mode;
-+		if (hostapd_drv_configure_edcca_enable(hapd) != 0)
-+			return -1;
-+	} else if (os_strcmp(config, "compensation") == 0) {
-+		int compensation = atoi(value);
-+		if (compensation < EDCCA_MIN_COMPENSATION ||
-+		    compensation > EDCCA_MAX_COMPENSATION) {
-+			wpa_printf(MSG_ERROR, "Invalid value for edcca compensation");
-+			return -1;
-+		}
-+		hapd->iconf->edcca_compensation = (s8) compensation;
-+		if (hostapd_drv_configure_edcca_enable(hapd) != 0)
-+			return -1;
-+	} else if (os_strcmp(config, "threshold") == 0) {
-+		char *thres_value;
-+		thres_value = os_strchr(value, ':');
-+		if (thres_value == NULL)
-+			return -1;
-+		*thres_value++ = '\0';
-+
-+		if (thres_value == NULL)
-+			return -1;
-+		int bw_idx = atoi(value);
-+		int threshold = atoi(thres_value);
-+
-+		if (bw_idx < EDCCA_BW_20 || bw_idx > EDCCA_BW_160) {
-+			wpa_printf(MSG_ERROR,
-+				   "Unsupported Bandwidth idx %d for SET_EDCCA",
-+				   bw_idx);
-+			return -1;
-+		}
-+		if (threshold < EDCCA_MIN_CONFIG_THRES ||
-+		    threshold > EDCCA_MAX_CONFIG_THRES) {
-+			wpa_printf(MSG_ERROR,
-+				   "Unsupported threshold %d for SET_EDCCA",
-+				   threshold);
-+			return -1;
-+		}
-+
-+		int threshold_arr[EDCCA_MAX_BW_NUM];
-+		/* 0x7f means keep the origival value in firmware */
-+		os_memset(threshold_arr, 0x7f, sizeof(threshold_arr));
-+		threshold_arr[bw_idx] = threshold;
-+
-+		if (hostapd_drv_configure_edcca_threshold(hapd, threshold_arr) != 0)
-+			return -1;
-+	} else {
-+		wpa_printf(MSG_ERROR,
-+			"Unsupported parameter %s for SET_EDCCA", config);
-+		return -1;
-+	}
-+	return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+
-+static int
-+hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *cmd, char *buf,
-+			     size_t buflen)
-+{
-+	char *pos, *end;
-+
-+	pos = buf;
-+	end = buf + buflen;
-+	u8 value[EDCCA_MAX_BW_NUM] = {0};
-+
-+	if (os_strcmp(cmd, "enable") == 0) {
-+		return os_snprintf(pos, end - pos, "Enable: %s\n",
-+				   edcca_mode_str(hapd->iconf->edcca_enable));
-+	} else if (os_strcmp(cmd, "compensation") == 0) {
-+		return os_snprintf(pos, end - pos, "Compensation: %d\n",
-+				  hapd->iconf->edcca_compensation);
-+	} else if (os_strcmp(cmd, "threshold") == 0) {
-+		if (hostapd_drv_get_edcca(hapd, EDCCA_CTRL_GET_THRES, &value) != 0)
-+			return -1;
-+		return os_snprintf(pos, end - pos,
-+				   "Threshold BW20: 0x%x, BW40: 0x%x, BW80: 0x%x, BW160: 0x%x\n",
-+				   value[0], value[1], value[2], value[3]);
-+	} else {
-+		wpa_printf(MSG_ERROR,
-+			"Unsupported parameter %s for GET_EDCCA", cmd);
-+		return -1;
-+	}
-+}
-+
- 
- #ifdef CONFIG_NAN_USD
- 
-@@ -4531,6 +4649,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 			reply_len = -1;
- #endif /* CONFIG_TESTING_OPTIONS */
- #endif /* CONFIG_IEEE80211BE */
-+	} else if (os_strncmp(buf, "SET_EDCCA ", 10) == 0) {
-+		reply_len = hostapd_ctrl_iface_set_edcca(hapd, buf+10, reply,
-+							  reply_size);
-+	} else if (os_strncmp(buf, "GET_EDCCA ", 10) == 0) {
-+		reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
-+							  reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 6c8b10291..965600577 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -303,6 +303,9 @@ struct hostapd_config * hostapd_config_defaults(void)
- 	conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL;
- #endif /* CONFIG_AIRTIME_POLICY */
- 
-+	conf->edcca_enable = EDCCA_MODE_AUTO;
-+	conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
-+
- 	hostapd_set_and_check_bw320_offset(conf, 0);
- 
- 	return conf;
-@@ -1034,6 +1037,7 @@ void hostapd_config_free(struct hostapd_config *conf)
- #ifdef CONFIG_ACS
- 	os_free(conf->acs_chan_bias);
- #endif /* CONFIG_ACS */
-+	os_free(conf->edcca_threshold);
- 	wpabuf_free(conf->lci);
- 	wpabuf_free(conf->civic);
- #ifdef CONFIG_AFC
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 379dc22cf..09718fada 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1281,8 +1281,38 @@ struct hostapd_config {
- 		int min_power;
- 	} afc;
- #endif /* CONFIG_AFC */
-+
-+	u8 edcca_enable;
-+	s8 edcca_compensation;
-+	int *edcca_threshold;
-+};
-+
-+enum edcca_mode {
-+	EDCCA_MODE_FORCE_DISABLE = 0,
-+	EDCCA_MODE_AUTO = 1,
-+};
-+
-+enum edcca_bw_id {
-+	EDCCA_BW_20 = 0,
-+	EDCCA_BW_40,
-+	EDCCA_BW_80,
-+	EDCCA_BW_160,
-+	EDCCA_MAX_BW_NUM,
-+};
-+
-+enum mtk_vendor_attr_edcca_ctrl_mode {
-+	EDCCA_CTRL_SET_EN = 0,
-+	EDCCA_CTRL_SET_THRES,
-+	EDCCA_CTRL_GET_EN,
-+	EDCCA_CTRL_GET_THRES,
-+	EDCCA_CTRL_NUM,
- };
- 
-+#define EDCCA_DEFAULT_COMPENSATION -6
-+#define EDCCA_MIN_COMPENSATION -126
-+#define EDCCA_MAX_COMPENSATION 126
-+#define EDCCA_MIN_CONFIG_THRES -126
-+#define EDCCA_MAX_CONFIG_THRES 0
- 
- static inline enum oper_chan_width
- hostapd_get_oper_chwidth(struct hostapd_config *conf)
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 527b2c984..a6caf6a73 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1245,3 +1245,27 @@ int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
- 	return hapd->driver->set_secure_ranging_ctx(hapd->drv_priv, &params);
- }
- #endif /* CONFIG_PASN */
-+
-+int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd)
-+{
-+	if (!hapd->driver || !hapd->driver->configure_edcca_enable)
-+		return 0;
-+	return hapd->driver->configure_edcca_enable(hapd->drv_priv,
-+			hapd->iconf->edcca_enable,
-+				hapd->iconf->edcca_compensation);
-+}
-+
-+int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
-+					  const int *threshold)
-+{
-+	if (!hapd->driver || !hapd->driver->configure_edcca_threshold)
-+		return 0;
-+	return hapd->driver->configure_edcca_threshold(hapd->drv_priv, threshold);
-+}
-+
-+int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
-+{
-+	if (!hapd->driver || !hapd->driver->get_edcca)
-+		return 0;
-+	return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index f8a8725be..98836153f 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -149,6 +149,10 @@ int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
- 				       u8 ltf_keyseed_len,
- 				       const u8 *ltf_keyseed, u32 action);
- 
-+int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
-+int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
-+					  const int *threshold);
-+int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 7959859b0..6af31179e 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2693,6 +2693,13 @@ dfs_offload:
- 	}
- #endif /* CONFIG_MESH */
- 
-+	if (hostapd_drv_configure_edcca_enable(hapd) < 0)
-+		goto fail;
-+
-+	if (hostapd_drv_configure_edcca_threshold(hapd,
-+						  hapd->iconf->edcca_threshold) < 0)
-+		goto fail;
-+
- 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- 		   iface->bss[0]->conf->iface);
- 	if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 4a19d2fc9..6121857dd 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -30,14 +30,22 @@ enum mtk_vendor_attr_edcca_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
- };
- 
--enum mtk_vendor_attr_edcca_ctrl_mode {
--	EDCCA_CTRL_SET_EN = 0,
--	EDCCA_CTRL_SET_THERS,
--	EDCCA_CTRL_GET_EN,
--	EDCCA_CTRL_GET_THERS,
--	EDCCA_CTRL_NUM,
-+enum mtk_vendor_attr_edcca_dump {
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_UNSPEC = 0,
-+
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_MODE,
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP,
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_MAX =
-+		NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
- };
- 
-+
- static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
- 	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
- 	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 3e3e309f4..ed5f5c013 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5220,6 +5220,10 @@ struct wpa_driver_ops {
- 			      const u8 *match, size_t match_len,
- 			      bool multicast);
- #endif /* CONFIG_TESTING_OPTIONS */
-+	int (*configure_edcca_enable)(void *priv, const u8 edcca_enable,
-+				  const s8 edcca_compensation);
-+	int (*configure_edcca_threshold)(void *priv, const int *threshold);
-+	int (*get_edcca)(void *priv, const u8 mode, u8 *value);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 501d0e42e..d59efe8b6 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -42,6 +42,8 @@
- #include "radiotap_iter.h"
- #include "rfkill.h"
- #include "driver_nl80211.h"
-+#include "common/mtk_vendor.h"
-+#include "ap/ap_config.h"
- 
- 
- #ifndef NETLINK_CAP_ACK
-@@ -14079,6 +14081,174 @@ static int testing_nl80211_radio_disable(void *priv, int disabled)
- 
- #endif /* CONFIG_TESTING_OPTIONS */
- 
-+static int nl80211_configure_edcca_enable(void *priv,
-+					  const u8 edcca_enable,
-+					  const s8 edcca_compensation)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_edcca_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting EDCCA enable");
-+		return 0;
-+	}
-+
-+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
-+	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_EN) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, edcca_enable) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
-+		edcca_compensation)) {
-+		wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
-+		nlmsg_free(msg);
-+		return -ENOBUFS;
-+	}
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_cmd(drv, msg);
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to configure EDCCA enable. ret=%d (%s) ",
-+			   ret, strerror(-ret));
-+	}
-+	return ret;
-+}
-+
-+static int nl80211_configure_edcca_threshold(void *priv, const int *threshold)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_edcca_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting EDCCA threshold");
-+		return 0;
-+	}
-+
-+	if (!threshold) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Input EDCCA threshold is empty!");
-+		return 0;
-+	}
-+
-+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
-+	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_THRES) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, threshold[0] & 0xff) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL, threshold[1] & 0xff) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL, threshold[2] & 0xff) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL, threshold[3] & 0xff)) {
-+		wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
-+		nlmsg_free(msg);
-+		return -ENOBUFS;
-+	}
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_cmd(drv, msg);
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to configure EDCCA threshold. ret=%d (%s) ",
-+			   ret, strerror(-ret));
-+	}
-+	return ret;
-+}
-+
-+
-+static int edcca_info_handler(struct nl_msg *msg, void *arg)
-+{
-+	u8 *info = (u8 *) arg;
-+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_MAX + 1];
-+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+	struct nlattr *nl_vend, *attr;
-+
-+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+		  genlmsg_attrlen(gnlh, 0), NULL);
-+
-+	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+	if (!nl_vend)
-+		return NL_SKIP;
-+
-+	nla_parse(tb_vendor, MTK_VENDOR_ATTR_EDCCA_DUMP_MAX,
-+		  nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+	attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL];
-+	if (!attr) {
-+		wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL");
-+		return NL_SKIP;
-+	}
-+
-+	*info++ = nla_get_u8(attr);
-+
-+	attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL];
-+	if (!attr) {
-+		wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL");
-+		return NL_SKIP;
-+	}
-+
-+	*info++ = nla_get_u8(attr);
-+
-+	attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL];
-+	if (!attr) {
-+		wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL");
-+		return NL_SKIP;
-+	}
-+
-+	*info++ = nla_get_u8(attr);
-+
-+	attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL];
-+	if (!attr) {
-+		wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL");
-+		return NL_SKIP;
-+	}
-+
-+	*info = nla_get_u8(attr);
-+	return NL_SKIP;
-+}
-+
-+
-+static int nl80211_get_edcca(void *priv, const u8 mode, u8 *value)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_edcca_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting EDCCA threshold");
-+		return 0;
-+	}
-+
-+	if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
-+	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED)) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, mode)) {
-+		wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
-+		nlmsg_free(msg);
-+		return -ENOBUFS;
-+	}
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_resp(drv, msg, edcca_info_handler, value);
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to get EDCCA configuration. ret=%d (%s)",
-+			   ret, strerror(-ret));
-+	}
-+	return ret;
-+}
-+
- 
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
-@@ -14240,4 +14410,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.register_frame = testing_nl80211_register_frame,
- 	.radio_disable = testing_nl80211_radio_disable,
- #endif /* CONFIG_TESTING_OPTIONS */
-+/* Need ifdef CONFIG_DRIVER_NL80211_MTK */
-+	.configure_edcca_enable = nl80211_configure_edcca_enable,
-+	.configure_edcca_threshold = nl80211_configure_edcca_threshold,
-+	.get_edcca = nl80211_get_edcca,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 618746e67..62c47efbd 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -200,6 +200,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int secure_ranging_ctx_vendor_cmd_avail:1;
- 	unsigned int puncturing:1;
- 	unsigned int qca_ap_allowed_freqs:1;
-+	unsigned int mtk_edcca_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index d6a887cef..cd4d799a1 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -18,6 +18,7 @@
- #include "common/qca-vendor-attr.h"
- #include "common/brcm_vendor.h"
- #include "driver_nl80211.h"
-+#include "common/mtk_vendor.h"
- 
- 
- static int protocol_feature_handler(struct nl_msg *msg, void *arg)
-@@ -1138,6 +1139,12 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 					break;
- 				}
- #endif /* CONFIG_DRIVER_NL80211_BRCM */
-+			} else if (vinfo->vendor_id == OUI_MTK) {
-+				switch (vinfo->subcmd) {
-+				case MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL:
-+					drv->mtk_edcca_vendor_cmd_avail = 1;
-+					break;
-+				}
- 			}
- 
- 			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0034-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0034-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch
deleted file mode 100644
index 3849fa4..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0034-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch
+++ /dev/null
@@ -1,454 +0,0 @@
-From fa905ce61f3cfecf94012fc2a11680e2615fc05d Mon Sep 17 00:00:00 2001
-From: TomLiu <tomml.liu@mediatek.com>
-Date: Tue, 9 Aug 2022 10:23:44 -0700
-Subject: [PATCH 034/104] mtk: hostapd: Add hostapd MU SET/GET control
-
----
- hostapd/config_file.c             |   9 +++
- hostapd/ctrl_iface.c              |  66 ++++++++++++++++++
- hostapd/hostapd_cli.c             |  18 +++++
- src/ap/ap_config.c                |   1 +
- src/ap/ap_config.h                |   1 +
- src/ap/ap_drv_ops.c               |  14 ++++
- src/ap/ap_drv_ops.h               |   2 +
- src/ap/hostapd.c                  |   2 +
- src/common/mtk_vendor.h           |  15 ++++
- src/drivers/driver.h              |  13 ++++
- src/drivers/driver_nl80211.c      | 110 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |   1 +
- src/drivers/driver_nl80211_capa.c |   3 +
- 13 files changed, 255 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index f8c1eec0a..637c2df9f 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4159,6 +4159,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 			return 1;
- 		}
- 		conf->mbssid = mbssid;
-+	} else if (os_strcmp(buf, "mu_onoff") == 0) {
-+		int val = atoi(pos);
-+		if (val < 0 || val > 15) {
-+			wpa_printf(MSG_ERROR,
-+				   "Line %d: invalid mu_onoff value",
-+				   line);
-+			return 1;
-+		}
-+		conf->mu_onoff = val;
- #endif /* CONFIG_IEEE80211AX */
- 	} else if (os_strcmp(buf, "max_listen_interval") == 0) {
- 		bss->max_listen_interval = atoi(pos);
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 78a3380f2..3a79a1284 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4049,6 +4049,67 @@ fail:
- #endif /* CONFIG_NAN_USD */
- 
- 
-+static int
-+hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
-+					 char *buf, size_t buflen)
-+{
-+	char *pos, *config, *value;
-+	config = cmd;
-+	pos = os_strchr(config, ' ');
-+	if (pos == NULL)
-+		return -1;
-+	*pos++ = '\0';
-+
-+	if(pos == NULL)
-+		return -1;
-+	value = pos;
-+
-+	if (os_strcmp(config, "onoff") == 0) {
-+		int mu = atoi(value);
-+		if (mu < 0 || mu > 15) {
-+			wpa_printf(MSG_ERROR, "Invalid value for mu");
-+			return -1;
-+		}
-+		hapd->iconf->mu_onoff = (u8) mu;
-+	} else {
-+		wpa_printf(MSG_ERROR,
-+			"Unsupported parameter %s for SET_MU", config);
-+		return -1;
-+	}
-+
-+	if(hostapd_drv_mu_ctrl(hapd) == 0) {
-+		return os_snprintf(buf, buflen, "OK\n");
-+	} else {
-+		return -1;
-+	}
-+}
-+
-+
-+static int
-+hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
-+					 size_t buflen)
-+{
-+	u8 mu_onoff;
-+	char *pos, *end;
-+
-+	pos = buf;
-+	end = buf + buflen;
-+
-+	if (hapd->iface->state != HAPD_IFACE_ENABLED)
-+		return os_snprintf(pos, end - pos, "Not allowed to get_mu when current state is %s\n",
-+				   hostapd_state_text(hapd->iface->state));
-+
-+	if (hostapd_drv_mu_dump(hapd, &mu_onoff) == 0) {
-+		hapd->iconf->mu_onoff = mu_onoff;
-+		return os_snprintf(pos, end - pos, "[hostapd_cli] = UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
-+			!!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
-+	} else {
-+		wpa_printf(MSG_INFO, "ctrl iface failed to call");
-+		return -1;
-+	}
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -4655,6 +4716,11 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 	} else if (os_strncmp(buf, "GET_EDCCA ", 10) == 0) {
- 		reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
- 							  reply_size);
-+	} else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
-+		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply,
-+							  reply_size);
-+	} else if (os_strncmp(buf, "GET_MU", 6) == 0) {
-+		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 1fb6d999e..da9dabd6f 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1442,6 +1442,20 @@ static int hostapd_cli_cmd_driver_flags2(struct wpa_ctrl *ctrl, int argc,
- }
- 
- 
-+static int hostapd_cli_cmd_set_mu(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "SET_MU", 1, argc, argv);
-+}
-+
-+
-+static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
-+}
-+
-+
- #ifdef CONFIG_DPP
- 
- static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
-@@ -1801,6 +1815,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 	  " = show supported driver flags"},
- 	{ "driver_flags2", hostapd_cli_cmd_driver_flags2, NULL,
- 	  " = show supported driver flags2"},
-+	{ "set_mu", hostapd_cli_cmd_set_mu, NULL,
-+		"<value> [0-15] bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0)"},
-+	{ "get_mu", hostapd_cli_cmd_get_mu, NULL,
-+		" = show mu onoff value in 0-15 bitmap"},
- #ifdef CONFIG_DPP
- 	{ "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 965600577..9b3ef0b5b 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -289,6 +289,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- 	conf->reg_def_cli_eirp_psd = -1;
- 	conf->reg_sub_cli_eirp_psd = -1;
- 	conf->reg_def_cli_eirp = -1;
-+	conf->mu_onoff = 15;
- #endif /* CONFIG_IEEE80211AX */
- 
- 	/* The third octet of the country string uses an ASCII space character
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 09718fada..f7dbbbec3 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1185,6 +1185,7 @@ struct hostapd_config {
- 	int reg_def_cli_eirp;
- 
- 	bool require_he;
-+	u8 mu_onoff;
- #endif /* CONFIG_IEEE80211AX */
- 
- 	/* VHT enable/disable config from CHAN_SWITCH */
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index a6caf6a73..897ed6af8 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1269,3 +1269,17 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
- 		return 0;
- 	return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
- }
-+
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd)
-+{
-+	if (!hapd->driver || !hapd->driver->mu_ctrl)
-+		return 0;
-+	return hapd->driver->mu_ctrl(hapd->drv_priv, hapd->iconf->mu_onoff);
-+}
-+
-+int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
-+{
-+	if (!hapd->driver || !hapd->driver->mu_dump)
-+		return 0;
-+	return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 98836153f..5ab20cc41 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -153,6 +153,8 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
- int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
- 					  const int *threshold);
- int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 6af31179e..d29b51fc5 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2699,6 +2699,8 @@ dfs_offload:
- 	if (hostapd_drv_configure_edcca_threshold(hapd,
- 						  hapd->iconf->edcca_threshold) < 0)
- 		goto fail;
-+	if (hostapd_drv_mu_ctrl(hapd) < 0)
-+		goto fail;
- 
- 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- 		   iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 6121857dd..60bc4cd4c 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -10,6 +10,8 @@ enum mtk_nl80211_vendor_subcmds {
- 	MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
- 	MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
- 	MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
-+	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
-+	MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
- 	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
- };
- 
-@@ -177,6 +179,19 @@ enum mtk_vendor_attr_rfeature_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
- };
- 
-+enum mtk_vendor_attr_mu_ctrl {
-+	MTK_VENDOR_ATTR_MU_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
-+	MTK_VENDOR_ATTR_MU_CTRL_DUMP,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
-+	MTK_VENDOR_ATTR_MU_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
-+};
-+
-+
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index ed5f5c013..df7ce5ab9 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -176,6 +176,11 @@ struct hostapd_channel_data {
- 	 * punct_bitmap - RU puncturing bitmap
- 	 */
- 	u16 punct_bitmap;
-+
-+	/**
-+	 * mu onoff=<val> (bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0))
-+	 */
-+	u8 mu_onoff;
- };
- 
- #define HE_MAC_CAPAB_0		0
-@@ -5224,6 +5229,14 @@ struct wpa_driver_ops {
- 				  const s8 edcca_compensation);
- 	int (*configure_edcca_threshold)(void *priv, const int *threshold);
- 	int (*get_edcca)(void *priv, const u8 mode, u8 *value);
-+
-+	/**
-+	 * mu_ctrl - ctrl on off for UL/DL MURU
-+	 * @priv: Private driver interface data
-+	 *
-+	 */
-+	 int (*mu_ctrl)(void *priv, u8 mu_onoff);
-+	 int (*mu_dump)(void *priv, u8 *mu_onoff);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index d59efe8b6..c234eb029 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13917,6 +13917,114 @@ fail:
- }
- 
- 
-+#ifdef CONFIG_IEEE80211AX
-+static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_mu_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting mu control");
-+		return 0;
-+	}
-+
-+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
-+		!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+		nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, mu_onoff)) {
-+		nlmsg_free(msg);
-+		return -ENOBUFS;
-+	}
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_cmd(drv, msg);
-+	if(ret){
-+		wpa_printf(MSG_ERROR, "Failed to set mu_onoff. ret=%d (%s)", ret, strerror(-ret));
-+	}
-+	return ret;
-+}
-+
-+
-+static int mu_dump_handler(struct nl_msg *msg, void *arg)
-+{
-+	u8 *mu_onoff = (u8 *) arg;
-+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_MU_CTRL_MAX + 1];
-+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+	struct nlattr *nl_vend, *attr;
-+
-+	static const struct nla_policy
-+	mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL + 1] = {
-+		[MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
-+		[MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
-+	};
-+
-+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+			genlmsg_attrlen(gnlh, 0), NULL);
-+
-+	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+	if (!nl_vend)
-+		return NL_SKIP;
-+
-+	nla_parse(tb_vendor, MTK_VENDOR_ATTR_MU_CTRL_MAX,
-+		  nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+	attr = tb_vendor[MTK_VENDOR_ATTR_MU_CTRL_DUMP];
-+	if (!attr) {
-+		wpa_printf(MSG_ERROR, "nl80211: cannot find MTK_VENDOR_ATTR_MU_CTRL_DUMP");
-+		return NL_SKIP;
-+	}
-+
-+	*mu_onoff = nla_get_u8(attr);
-+	wpa_printf(MSG_DEBUG, "nla_get mu_onoff: %d\n", *mu_onoff);
-+
-+	return 0;
-+}
-+
-+static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *attr;
-+	int ret;
-+
-+	if (!drv->mtk_mu_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting mu control");
-+		return 0;
-+	}
-+
-+	if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL)) {
-+		nlmsg_free(msg);
-+		return -ENOBUFS;
-+	}
-+
-+  attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+	if (!attr) {
-+		nlmsg_free(msg);
-+		return -1;
-+	}
-+
-+	nla_nest_end(msg, attr);
-+
-+	ret = send_and_recv_resp(drv, msg, mu_dump_handler, mu_onoff);
-+
-+	if(ret){
-+		wpa_printf(MSG_ERROR, "Failed to get mu_onoff. ret=%d (%s)", ret, strerror(-ret));
-+	}
-+
-+	return ret;
-+}
-+#endif /* CONFIG_IEEE80211AX */
-+
-+
- #ifdef CONFIG_DPP
- static int nl80211_dpp_listen(void *priv, bool enable)
- {
-@@ -14396,6 +14504,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.update_connect_params = nl80211_update_connection_params,
- 	.send_external_auth_status = nl80211_send_external_auth_status,
- 	.set_4addr_mode = nl80211_set_4addr_mode,
-+	.mu_ctrl = nl80211_mu_onoff,
-+	.mu_dump = nl80211_mu_dump,
- #ifdef CONFIG_DPP
- 	.dpp_listen = nl80211_dpp_listen,
- #endif /* CONFIG_DPP */
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 62c47efbd..f99bba9e1 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -201,6 +201,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int puncturing:1;
- 	unsigned int qca_ap_allowed_freqs:1;
- 	unsigned int mtk_edcca_vendor_cmd_avail:1;
-+	unsigned int mtk_mu_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index cd4d799a1..9c0a47971 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1144,6 +1144,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL:
- 					drv->mtk_edcca_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_MU_CTRL :
-+					drv->mtk_mu_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0034-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0034-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch
new file mode 100644
index 0000000..69615ec
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0034-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch
@@ -0,0 +1,656 @@
+From f7ffccc3487fbc51786d4063b03e0e4a090253b0 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Sat, 3 Jun 2023 17:12:15 +0800
+Subject: [PATCH 034/126] mtk: hostapd: add connac3 PHY MURU manual mode config
+ support
+
+This commit supports read the following two formats to set MU/RU manual
+mode:
+1. hostapd_cli -i <intf> raw set_muru_manual_config=<field>:<value>
+2. hostapd_cli -i <intf> set_mu <field> <value>
+
+For the <field>, we support the following field:
+1. ul_comm_user_cnt/dl_comm_user_cnt: set the number of user
+2. ul_comm_bw/dl_comm_bw: set the bandwith
+3. ul_user_ru_alloc/dl_user_ru_alloc: set the RU band idx and RU
+allocate idx
+4. ul_user_mcs/dl_user_mcs: set the mcs for each user
+5. ul_user_ssAlloc_raru: set the number of ss for each user
+6. ul_comm_gi_ltf: set the combinations of gi and ltf for UL only.
+7. dl_comm_toneplan: fix ru toneplan allocation
+8. dl_comm_ack_policy: fix station ack policy
+9. update : trigger driver to send mcu command to set muru manual mode.
+
+For the value of each field, please check wiki to learn the details:
+https://wiki.mediatek.inc/display/GWKB/muru_mancfg_user_guide
+
+For the fields that mt76 support to use, we will update in this wiki:
+https://wiki.mediatek.inc/pages/viewpage.action?pageId=1271741116
+
+Please noted that this commit is only for connac 3 gen chips. If this
+feature is to be used in other generations, the following actions must
+be taken:
+1. Different data structue needs to be defined for different
+generations, e.g. connac4_muru_comm, connac4_muru_dl.
+2. hostapd_ctrl_iface_set_mu() shall be modified.
+3. A new code level configuration shall be defined to differentiate the
+code flow that different generations will go through.
+
+Add support new argument global_comm_band for muru manual config
+command. This argument can be used to specify the band to apply manual
+config.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ hostapd/ctrl_iface.c         | 240 +++++++++++++++++++++++++++++++----
+ src/ap/ap_config.h           |   1 +
+ src/ap/ap_drv_ops.c          |   4 +-
+ src/ap/ap_drv_ops.h          |   2 +-
+ src/ap/hostapd.c             |   2 +-
+ src/common/mtk_vendor.h      | 166 +++++++++++++++++++++++-
+ src/drivers/driver.h         |   2 +-
+ src/drivers/driver_nl80211.c |  21 ++-
+ 8 files changed, 394 insertions(+), 44 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index b78ee24c0..68b83bf6e 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -3905,7 +3905,6 @@ hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *cmd, char *buf,
+ 	}
+ }
+ 
+-
+ #ifdef CONFIG_NAN_USD
+ 
+ static int hostapd_ctrl_nan_publish(struct hostapd_data *hapd, char *cmd,
+@@ -4192,21 +4191,61 @@ fail:
+ #endif /* CONFIG_NAN_USD */
+ 
+ 
++static int
++hostapd_parse_argument_helper(char *value, u16 **ptr_input)
++{
++#define MAX_MU_CTRL_NUM 17
++	u16 *input;
++	char *endptr;
++	int cnt = 0;
++
++	input = os_zalloc(MAX_MU_CTRL_NUM * sizeof(u16));
++	if (input == NULL) {
++		wpa_printf(MSG_ERROR, "Failed to allocate memory.\n");
++		return -1;
++	}
++	while (value) {
++		u8 val = strtol(value, &endptr, 10);
++
++		if (value != endptr) {
++			input[cnt++] = val;
++			value = os_strchr(endptr, ':');
++			if (value)
++				value++;
++		} else {
++			break;
++		}
++	}
++
++	*ptr_input = input;
++	return cnt;
++}
++
++#define MURU_CFG_DEPENDENCE_CHECK(_val, _mask) do {				\
++		if ((le_to_host32(_val) & (_mask)) != _mask) {			\
++			wpa_printf(MSG_ERROR, "Set %s first\n", #_mask);	\
++			goto fail;						\
++		}								\
++	} while(0)
++
+ static int
+ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+-					 char *buf, size_t buflen)
++			  char *buf, size_t buflen)
+ {
+ 	char *pos, *config, *value;
+-	u8 mode;
++	u8 i;
++	int cnt = 0, ret;
++	u16 *val;
++	struct connac3_muru *muru;
++	struct connac3_muru_dl *dl;
++	struct connac3_muru_ul *ul;
++	struct connac3_muru_comm *comm;
+ 
+ 	config = cmd;
+ 	pos = os_strchr(config, ' ');
+-	if (pos == NULL)
+-		return -1;
+-	*pos++ = '\0';
++	if (pos != NULL)
++		*pos++ = '\0';
+ 
+-	if(pos == NULL)
+-		return -1;
+ 	value = pos;
+ 
+ 	if (os_strcmp(config, "onoff") == 0) {
+@@ -4216,24 +4255,170 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ 			return -1;
+ 		}
+ 		hapd->iconf->mu_onoff = (u8) mu;
+-		mode = MU_CTRL_ONOFF;
+-	} else if (os_strcmp(config, "ul_user_cnt") == 0) {
+-		mode = MU_CTRL_UL_USER_CNT;
+-		wpa_printf(MSG_ERROR, "ul_user_cnt:%d\n", (u8)atoi(value));
+-	} else if (os_strcmp(config, "dl_user_cnt") == 0) {
+-		mode = MU_CTRL_DL_USER_CNT;
+-		wpa_printf(MSG_ERROR, "dl_user_cnt:%d\n", (u8)atoi(value));
+-	} else {
+-		wpa_printf(MSG_ERROR,
+-			"Unsupported parameter %s for SET_MU", config);
+-		return -1;
++
++		if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) == 0)
++			return os_snprintf(buf, buflen, "OK\n");
++		else
++			goto fail;
+ 	}
+ 
+-	if(hostapd_drv_mu_ctrl(hapd, mode, (u8)atoi(value)) == 0) {
+-		return os_snprintf(buf, buflen, "OK\n");
++	if (hapd->iconf->muru_config == NULL)
++		hapd->iconf->muru_config = os_zalloc(sizeof(struct connac3_muru));
++
++	muru = hapd->iconf->muru_config;
++	dl = &muru->dl;
++	ul = &muru->ul;
++	comm = &muru->comm;
++
++	if (os_strncmp(config, "update", 6) == 0) {
++		ret = hostapd_drv_mu_ctrl(hapd, MU_CTRL_UPDATE);
++
++		os_free(hapd->iconf->muru_config);
++		hapd->iconf->muru_config = NULL;
++
++		if (ret)
++			goto fail;
++	} else if (os_strcmp(config, "ul_comm_user_cnt") == 0) {
++		ul->user_num = (u8)atoi(value);
++		comm->ppdu_format |= MURU_PPDU_HE_TRIG;
++		comm->sch_type |= MURU_OFDMA_SCH_TYPE_UL;
++		muru->cfg_comm |= host_to_le32(MURU_COMM_SET);
++		muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_TOTAL_USER_CNT);
++	} else if (os_strcmp(config, "dl_comm_user_cnt") == 0) {
++		dl->user_num = (u8)atoi(value);
++		comm->ppdu_format |= MURU_PPDU_HE_MU;
++		comm->sch_type |= MURU_OFDMA_SCH_TYPE_DL;
++		muru->cfg_comm |= host_to_le32(MURU_COMM_SET);
++		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_TOTAL_USER_CNT);
++	} else if (os_strcmp(config, "dl_comm_bw") == 0) {
++		dl->bw = (u8)atoi(value);
++		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_BW);
++	} else if (os_strcmp(config, "ul_comm_bw") == 0) {
++		ul->bw = (u8)atoi(value);
++		muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_BW);
++	} else if (os_strcmp(config, "dl_user_ru_alloc") == 0) {
++		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
++		cnt = hostapd_parse_argument_helper(value, &val);
++		if (cnt == -1)
++			goto fail;
++		if (cnt != (dl->user_num * 2))
++			goto para_fail;
++		for (i = 0; i < dl->user_num; i++) {
++			dl->usr[i].ru_alloc_seg = (val[2 * i] & 0x1);
++			dl->usr[i].ru_allo_ps160 = ((val[2 * i] & 0x2) >> 1);
++			dl->usr[i].ru_idx = val[(2 * i) + 1];
++		}
++		os_free(val);
++		muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_RU_ALLOC);
++	} else if (os_strcmp(config, "ul_user_ru_alloc") == 0) {
++		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
++		cnt = hostapd_parse_argument_helper(value, &val);
++		if (cnt == -1)
++			goto fail;
++		if (cnt != (ul->user_num * 2))
++			goto para_fail;
++		for (i = 0; i < ul->user_num; i++) {
++			ul->usr[i].ru_alloc_seg = (val[2 * i] & 0x1);
++			ul->usr[i].ru_allo_ps160 = ((val[2 * i] & 0x2) >> 1);
++			ul->usr[i].ru_idx = val[(2 * i) + 1];
++		}
++		os_free(val);
++		muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_RU_ALLOC);
++	} else if (os_strcmp(config, "dl_user_mcs") == 0) {
++		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
++		cnt = hostapd_parse_argument_helper(value, &val);
++		if (cnt == -1)
++			goto fail;
++		if (cnt != dl->user_num)
++			goto para_fail;
++		for (i = 0; i < cnt; i++)
++			dl->usr[i].mcs = (u8) val[i];
++		os_free(val);
++		muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_MCS);
++	} else if (os_strcmp(config, "ul_user_mcs") == 0) {
++		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
++		cnt = hostapd_parse_argument_helper(value, &val);
++		if (cnt == -1)
++			goto fail;
++		if (cnt != ul->user_num)
++			goto para_fail;
++		for (i = 0; i < cnt; i++)
++			ul->usr[i].mcs = (u8) val[i];
++		os_free(val);
++		muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_MCS);
++	} else if (os_strcmp(config, "dl_user_cod") == 0) {
++		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
++		cnt = hostapd_parse_argument_helper(value, &val);
++		if (cnt == -1)
++			goto fail;
++		if (cnt != dl->user_num)
++			goto para_fail;
++		for (i = 0; i < cnt; i++)
++			dl->usr[i].ldpc = (u8) val[i];
++		os_free(val);
++		muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_COD);
++	} else if (os_strcmp(config, "ul_user_cod") == 0) {
++		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
++		cnt = hostapd_parse_argument_helper(value, &val);
++		if (cnt == -1)
++			goto fail;
++		if (cnt != ul->user_num)
++			goto para_fail;
++		for (i = 0; i < cnt; i++)
++			ul->usr[i].ldpc = (u8) val[i];
++		os_free(val);
++		muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_COD);
++	} else if (os_strcmp(config, "ul_user_ssAlloc_raru") == 0) {
++		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
++		cnt = hostapd_parse_argument_helper(value, &val);
++		if (cnt == -1)
++			goto fail;
++		if (cnt != ul->user_num)
++			goto para_fail;
++		for (i = 0; i < cnt; i++)
++			ul->usr[i].nss = (u8) val[i];
++		os_free(val);
++		muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_NSS);
++	} else if (os_strcmp(config, "dl_comm_gi") == 0) {
++		dl->gi = (u8)atoi(value);
++		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_GI);
++	} else if (os_strcmp(config, "dl_comm_ltf") == 0) {
++		dl->ltf = (u8)atoi(value);
++		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_LTF);
++	} else if (os_strcmp(config, "ul_comm_gi_ltf") == 0) {
++		ul->gi_ltf = (u8)atoi(value);
++		muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_GILTF);
++	} else if (os_strcmp(config, "dl_comm_ack_policy") == 0) {
++		dl->ack_policy = (u8)atoi(value);
++		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_ACK_PLY);
++	} else if (os_strcmp(config, "dl_comm_toneplan") == 0) {
++		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_BW);
++		cnt = hostapd_parse_argument_helper(value, &val);
++		if (cnt == -1)
++			goto fail;
++		i = pow(2, dl->bw);
++		if (cnt != i)
++			goto para_fail;
++		for (i = 0; i < cnt; i++)
++			dl->ru[i] = host_to_le16(val[i]);
++		os_free(val);
++		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_TONE_PLAN);
++	} else if (os_strcmp(config, "global_comm_band") == 0) {
++		comm->band = (u8)atoi(value);
++		muru->cfg_comm |= host_to_le32(MURU_COMM_BAND);
+ 	} else {
+-		return -1;
++		wpa_printf(MSG_ERROR,
++			   "Unsupported parameter %s for SET_MU", config);
++		goto fail;
+ 	}
++
++	return os_snprintf(buf, buflen, "OK\n");
++
++para_fail:
++	os_free(val);
++	wpa_printf(MSG_ERROR, "Incorrect input number\n");
++fail:
++	return os_snprintf(buf, buflen, "FAIL\n");
+ }
+ 
+ 
+@@ -5290,8 +5475,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
+ 							  reply_size);
+ 	} else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
+-		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply,
+-							  reply_size);
++		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply, reply_size);
+ 	} else if (os_strncmp(buf, "GET_MU", 6) == 0) {
+ 		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
+ 	} else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
+@@ -5317,6 +5501,14 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 	} else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
+ 		reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
+ 							reply, reply_size);
++	} else if (os_strncmp(buf, "set_muru_manual_config=", 23) == 0) {
++		// Replace first ':' with a single space ' '
++		char *pos = buf + 23;
++
++		pos = os_strchr(pos, ':');
++		if (pos)
++			*pos = ' ';
++		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 23, reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index f90f4d554..0c51d2ab9 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1344,6 +1344,7 @@ struct hostapd_config {
+ 	u8 ibf_enable;
+ 	u8 dfs_detect_mode;
+ 	u8 amsdu;
++	void *muru_config;
+ };
+ 
+ enum three_wire_mode {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 496fd4263..ccd0cb939 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1299,11 +1299,11 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
+ 	return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
+ }
+ 
+-int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val)
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode)
+ {
+ 	if (!hapd->driver || !hapd->driver->mu_ctrl)
+ 		return 0;
+-	return hapd->driver->mu_ctrl(hapd->drv_priv, mode, val);
++	return hapd->driver->mu_ctrl(hapd->drv_priv, mode, hapd->iconf);
+ }
+ 
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index a949e168d..659154f56 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -156,7 +156,7 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
+ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
+ 					  const int *threshold);
+ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+-int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val);
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode);
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 9e1ba6a21..f06687064 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2763,7 +2763,7 @@ dfs_offload:
+ 	if (hostapd_drv_configure_edcca_threshold(hapd,
+ 						  hapd->iconf->edcca_threshold) < 0)
+ 		goto fail;
+-	if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF, hapd->iconf->mu_onoff) < 0)
++	if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) < 0)
+ 		goto fail;
+ 	if (hostapd_drv_three_wire_ctrl(hapd) < 0)
+ 		goto fail;
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 34238f098..f0abcb6b1 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -202,8 +202,11 @@ enum mtk_vendor_attr_mu_ctrl {
+ 
+ 	MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
+ 	MTK_VENDOR_ATTR_MU_CTRL_DUMP,
+-	MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE,
+-	MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL,
++	/**
++	 * The above attrs are also used by connac 2. It is best not to modify the
++	 * above data structure.
++	 */
++	MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
+@@ -278,8 +281,163 @@ struct amnt_resp_data {
+ };
+ 
+ enum {
++	MU_CTRL_UPDATE,
+ 	MU_CTRL_ONOFF,
+-	MU_CTRL_DL_USER_CNT,
+-	MU_CTRL_UL_USER_CNT,
+ };
++
++struct connac3_muru_comm {
++	u8 pda_pol;
++	u8 band;
++	u8 spe_idx;
++	u8 proc_type;
++
++	le16 mlo_ctrl;
++	u8 sch_type;
++	u8 ppdu_format;
++	u8 ac;
++	u8 _rsv[3];
++};
++
++struct connac3_muru_dl {
++	u8 user_num;
++	u8 tx_mode;
++	u8 bw;
++	u8 gi;
++
++	u8 ltf;
++	u8 mcs;
++	u8 dcm;
++	u8 cmprs;
++
++	le16 ru[16];
++
++	u8 c26[2];
++	u8 ack_policy;
++	u8 tx_power;
++
++	le16 mu_ppdu_duration;
++	u8 agc_disp_order;
++	u8 _rsv1;
++
++	u8 agc_disp_pol;
++	u8 agc_disp_ratio;
++	le16 agc_disp_linkMFG;
++
++	le16 prmbl_punc_bmp;
++	u8 _rsv2[2];
++
++	struct {
++		le16 wlan_idx;
++		u8 ru_alloc_seg;
++		u8 ru_idx;
++		u8 ldpc;
++		u8 nss;
++		u8 mcs;
++		u8 mu_group_idx;
++		u8 vht_groud_id;
++		u8 vht_up;
++		u8 he_start_stream;
++		u8 he_mu_spatial;
++		le16 tx_power_alpha;
++		u8 ack_policy;
++		u8 ru_allo_ps160;
++	} usr[16];
++};
++
++struct connac3_muru_ul {
++	u8 user_num;
++	u8 tx_mode;
++
++	u8 ba_type;
++	u8 _rsv;
++
++	u8 bw;
++	u8 gi_ltf;
++	le16 ul_len;
++
++	le16 trig_cnt;
++	u8 pad;
++	u8 trig_type;
++
++	le16 trig_intv;
++	u8 trig_ta[ETH_ALEN];
++	le16 ul_ru[16];
++
++	u8 c26[2];
++	le16 agc_disp_linkMFG;
++
++	u8 agc_disp_mu_len;
++	u8 agc_disp_pol;
++	u8 agc_disp_ratio;
++	u8 agc_disp_pu_idx;
++
++	struct {
++		le16 wlan_idx;
++		u8 ru_alloc_seg;
++		u8 ru_idx;
++		u8 ldpc;
++		u8 nss;
++		u8 mcs;
++		u8 target_rssi;
++		le32 trig_pkt_size;
++		u8 ru_allo_ps160;
++		u8 _rsv2[3];
++	} usr[16];
++};
++
++struct connac3_muru_dbg {
++	/* HE TB RX Debug */
++	le32 rx_hetb_nonsf_en_bitmap;
++	le32 rx_hetb_cfg[2];
++};
++
++struct connac3_muru {
++	le32 cfg_comm;
++	le32 cfg_dl;
++	le32 cfg_ul;
++	le32 cfg_dbg;
++
++	struct connac3_muru_comm comm;
++	struct connac3_muru_dl dl;
++	struct connac3_muru_ul ul;
++	struct connac3_muru_dbg dbg;
++};
++
++#define MURU_OFDMA_SCH_TYPE_DL	BIT(0)
++#define MURU_OFDMA_SCH_TYPE_UL	BIT(1)
++#define MURU_PPDU_HE_TRIG	BIT(2)
++#define MURU_PPDU_HE_MU		BIT(3)
++
++/* Common Config */
++#define MURU_COMM_PPDU_FMT	BIT(0)
++#define MURU_COMM_BAND		BIT(2)
++#define MURU_COMM_WMM		BIT(3)
++#define MURU_COMM_SPE_IDX	BIT(4)
++#define MURU_COMM_SET		(MURU_COMM_PPDU_FMT | MURU_COMM_BAND | \
++				 MURU_COMM_WMM | MURU_COMM_SPE_IDX)
++
++/* DL Common config */
++#define MURU_FIXED_DL_BW		BIT(0)
++#define MURU_FIXED_DL_GI		BIT(1)
++#define MURU_FIXED_DL_TONE_PLAN		BIT(3)
++#define MURU_FIXED_DL_TOTAL_USER_CNT	BIT(4)
++#define MURU_FIXED_DL_LTF		BIT(5)
++#define MURU_FIXED_DL_ACK_PLY		BIT(9)
++
++/* DL Per User Config */
++#define MURU_FIXED_USER_DL_COD		BIT(17)
++#define MURU_FIXED_USER_DL_MCS		BIT(18)
++#define MURU_FIXED_USER_DL_RU_ALLOC	BIT(20)
++
++/* UL Common Config */
++#define MURU_FIXED_UL_TOTAL_USER_CNT	BIT(4)
++#define MURU_FIXED_UL_BW		BIT(5)
++#define MURU_FIXED_UL_GILTF		BIT(6)
++
++/* UL Per User Config */
++#define MURU_FIXED_USER_UL_COD		BIT(18)
++#define MURU_FIXED_USER_UL_MCS		BIT(19)
++#define MURU_FIXED_USER_UL_NSS		BIT(20)
++#define MURU_FIXED_USER_UL_RU_ALLOC	BIT(21)
++
+ #endif /* MTK_VENDOR_H */
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index c8910739e..6fc79f659 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5266,7 +5266,7 @@ struct wpa_driver_ops {
+ 	 * @priv: Private driver interface data
+ 	 *
+ 	 */
+-	 int (*mu_ctrl)(void *priv, u8 mode, u8 val);
++	 int (*mu_ctrl)(void *priv, u8 mode, void *config);
+ 	 int (*mu_dump)(void *priv, u8 *mu_onoff);
+ 
+ 	/**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index da1e6a8ed..8a8f8abe8 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14007,12 +14007,13 @@ fail:
+ 
+ 
+ #ifdef CONFIG_IEEE80211AX
+-static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
++static int nl80211_mu_ctrl(void *priv, u8 mode, void *config)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+ 	struct nl_msg *msg;
+ 	struct nlattr *data;
++	struct hostapd_config *cfg = config;
+ 	int ret = -ENOBUFS;
+ 
+ 	if (!drv->mtk_mu_vendor_cmd_avail) {
+@@ -14029,17 +14030,16 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
+ 
+ 	switch (mode) {
+ 	case MU_CTRL_ONOFF:
+-			if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, val))
+-				goto fail;
++		if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff))
++			goto fail;
+ 		break;
+-	case MU_CTRL_UL_USER_CNT:
+-	case MU_CTRL_DL_USER_CNT:
+-			if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE, mode) ||
+-			    nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL, val))
+-				goto fail;
++	case MU_CTRL_UPDATE:
++		if (nla_put(msg, MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
++			    sizeof(struct connac3_muru), cfg->muru_config))
++			goto fail;
+ 		break;
+ 	default:
+-		wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode !");
++		wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode %u!", mode);
+ 		ret = -EINVAL;
+ 		goto fail;
+ 	}
+@@ -14047,9 +14047,8 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
+ 	nla_nest_end(msg, data);
+ 
+ 	ret = send_and_recv_cmd(drv, msg);
+-	if(ret){
++	if (ret)
+ 		wpa_printf(MSG_ERROR, "Failed to set mu_ctrl. ret=%d (%s)", ret, strerror(-ret));
+-	}
+ 	return ret;
+ 
+ fail:
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0035-mtk-hostapd-Add-HE-capabilities-check.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0035-mtk-hostapd-Add-HE-capabilities-check.patch
new file mode 100644
index 0000000..780cce4
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0035-mtk-hostapd-Add-HE-capabilities-check.patch
@@ -0,0 +1,49 @@
+From 4e8197027a7b86f6cf5f1d2d9095e30281693319 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Fri, 9 Jun 2023 09:03:05 +0800
+Subject: [PATCH 035/126] mtk: hostapd: Add HE capabilities check
+
+---
+ src/ap/hw_features.c | 26 ++++++++++++++++++++++++++
+ 1 file changed, 26 insertions(+)
+
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index 400c50988..2a2832cd4 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -722,6 +722,32 @@ static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
+ #ifdef CONFIG_IEEE80211AX
+ static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface)
+ {
++	struct hostapd_hw_modes *mode = iface->current_mode;
++	struct he_capabilities *he_cap = &mode->he_capab[IEEE80211_MODE_AP];
++	struct hostapd_config *conf = iface->conf;
++
++#define HE_CAP_CHECK(hw_cap, field, phy_idx, cfg_cap)					\
++	do {									\
++		if (cfg_cap && !(hw_cap[phy_idx] & field)) {	\
++			wpa_printf(MSG_ERROR, "Driver does not support configured" \
++				     " HE capability [%s]", #field);		\
++			return 0;						\
++		}								\
++	} while (0)
++
++	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_LDPC_CODING_IN_PAYLOAD,
++		     HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX,
++		     conf->he_phy_capab.he_ldpc);
++	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMER_CAPAB,
++		     HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX,
++		     conf->he_phy_capab.he_su_beamformer);
++	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMEE_CAPAB,
++		     HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX,
++		     conf->he_phy_capab.he_su_beamformee);
++	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_MU_BEAMFORMER_CAPAB,
++		     HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX,
++		     conf->he_phy_capab.he_mu_beamformer);
++
+ 	return 1;
+ }
+ #endif /* CONFIG_IEEE80211AX */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0035-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0035-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch
deleted file mode 100644
index 596fdd3..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0035-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch
+++ /dev/null
@@ -1,247 +0,0 @@
-From 588292b2fac44452523a27ece07b85fbd0f41c5d Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 2 Sep 2022 01:03:23 +0800
-Subject: [PATCH 035/104] mtk: hostapd: Add three wire PTA ctrl hostapd vendor
- command
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- hostapd/config_file.c             |  4 ++++
- src/ap/ap_config.c                |  1 +
- src/ap/ap_config.h                | 13 ++++++++++++
- src/ap/ap_drv_ops.c               | 11 +++++++++++
- src/ap/ap_drv_ops.h               |  1 +
- src/ap/hostapd.c                  |  2 ++
- src/common/mtk_vendor.h           | 16 +++++++++++++++
- src/drivers/driver.h              |  8 ++++++++
- src/drivers/driver_nl80211.c      | 33 +++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |  1 +
- src/drivers/driver_nl80211_capa.c |  3 +++
- 11 files changed, 93 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 637c2df9f..3d9923692 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5391,6 +5391,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 			return 1;
- 		}
- 		conf->edcca_compensation = (s8) val;
-+	} else if (os_strcmp(buf, "three_wire_enable") == 0) {
-+		u8 en = atoi(pos);
-+
-+		conf->three_wire_enable = en;
- 	} else {
- 		wpa_printf(MSG_ERROR,
- 			   "Line %d: unknown configuration item '%s'",
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 9b3ef0b5b..79fd3a24b 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -306,6 +306,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- 
- 	conf->edcca_enable = EDCCA_MODE_AUTO;
- 	conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
-+	conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
- 
- 	hostapd_set_and_check_bw320_offset(conf, 0);
- 
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index f7dbbbec3..d1bbd238c 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1286,6 +1286,19 @@ struct hostapd_config {
- 	u8 edcca_enable;
- 	s8 edcca_compensation;
- 	int *edcca_threshold;
-+	u8 three_wire_enable;
-+};
-+
-+enum three_wire_mode {
-+	THREE_WIRE_MODE_DISABLE,
-+	THREE_WIRE_MODE_EXT0_ENABLE,
-+	THREE_WIRE_MODE_EXT1_ENABLE,
-+	THREE_WIRE_MODE_ALL_ENABLE,
-+
-+	/* keep last */
-+	NUM_THREE_WIRE_MODE,
-+	THREE_WIRE_MODE_MAX =
-+		NUM_THREE_WIRE_MODE - 1
- };
- 
- enum edcca_mode {
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 897ed6af8..587b8f37f 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1283,3 +1283,14 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
- 		return 0;
- 	return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
- }
-+
-+int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
-+{
-+	if (!hapd->driver || !hapd->driver->three_wire_ctrl)
-+		return 0;
-+	if (hapd->iconf->three_wire_enable > THREE_WIRE_MODE_MAX) {
-+		wpa_printf(MSG_INFO, "Invalid value for three wire enable\n");
-+		return 0;
-+	}
-+	return hapd->driver->three_wire_ctrl(hapd->drv_priv, hapd->iconf->three_wire_enable);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 5ab20cc41..7448e7954 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -155,6 +155,7 @@ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
- int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
- int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
-+int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index d29b51fc5..5ceb49962 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2701,6 +2701,8 @@ dfs_offload:
- 		goto fail;
- 	if (hostapd_drv_mu_ctrl(hapd) < 0)
- 		goto fail;
-+	if (hostapd_drv_three_wire_ctrl(hapd) < 0)
-+		goto fail;
- 
- 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- 		   iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 60bc4cd4c..99ecbaf71 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -13,6 +13,7 @@ enum mtk_nl80211_vendor_subcmds {
- 	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
- 	MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
- 	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
-+	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8
- };
- 
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -58,6 +59,21 @@ static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
- 	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
- };
- 
-+enum mtk_vendor_attr_3wire_ctrl {
-+	MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_3WIRE_CTRL_MODE,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL,
-+	MTK_VENDOR_ATTR_3WIRE_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
-+};
-+
-+static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
-+	[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
-+};
-+
- enum mtk_vendor_attr_csi_ctrl {
- 	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index df7ce5ab9..dec4336a4 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5237,6 +5237,14 @@ struct wpa_driver_ops {
- 	 */
- 	 int (*mu_ctrl)(void *priv, u8 mu_onoff);
- 	 int (*mu_dump)(void *priv, u8 *mu_onoff);
-+
-+	/**
-+	 * three_wire_ctrl - set three_wire_ctrl mode
-+	 * @priv: Private driver interface data
-+	 * @three_wire_enable: three_wire_ctrl mode
-+	 *
-+	 */
-+	 int (*three_wire_ctrl)(void *priv, u8 three_wire_enable);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index c234eb029..c9899e492 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -14357,6 +14357,38 @@ static int nl80211_get_edcca(void *priv, const u8 mode, u8 *value)
- 	return ret;
- }
- 
-+static int nl80211_enable_three_wire(void *priv, const u8 three_wire_enable)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	/* Prepare nl80211 cmd */
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_3wire_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting three wire control");
-+		return 0;
-+	}
-+
-+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL) ||
-+	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_3WIRE_CTRL_MODE, three_wire_enable)) {
-+		nlmsg_free(msg);
-+		return -ENOBUFS;
-+	}
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_cmd(drv, msg);
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to enable three wire. ret=%d (%s) ",
-+			   ret, strerror(-ret));
-+	}
-+	return ret;
-+}
- 
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
-@@ -14524,4 +14556,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.configure_edcca_enable = nl80211_configure_edcca_enable,
- 	.configure_edcca_threshold = nl80211_configure_edcca_threshold,
- 	.get_edcca = nl80211_get_edcca,
-+	.three_wire_ctrl = nl80211_enable_three_wire,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index f99bba9e1..5de6ca6f0 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -202,6 +202,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int qca_ap_allowed_freqs:1;
- 	unsigned int mtk_edcca_vendor_cmd_avail:1;
- 	unsigned int mtk_mu_vendor_cmd_avail:1;
-+	unsigned int mtk_3wire_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 9c0a47971..fddcf8349 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1147,6 +1147,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_MU_CTRL :
- 					drv->mtk_mu_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL :
-+					drv->mtk_3wire_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0036-mtk-hostapd-Add-hostapd-iBF-control.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0036-mtk-hostapd-Add-hostapd-iBF-control.patch
deleted file mode 100644
index 56149a4..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0036-mtk-hostapd-Add-hostapd-iBF-control.patch
+++ /dev/null
@@ -1,431 +0,0 @@
-From 235a6041bde6ce30da6e631b901260dec5fadcda Mon Sep 17 00:00:00 2001
-From: mtk27835 <shurong.wen@mediatek.com>
-Date: Wed, 7 Sep 2022 14:41:51 -0700
-Subject: [PATCH 036/104] mtk: hostapd: Add hostapd iBF control
-
-Signed-off-by: mtk27835 <shurong.wen@mediatek.com>
----
- hostapd/config_file.c             |   3 +
- hostapd/ctrl_iface.c              |  26 +++++++
- hostapd/hostapd_cli.c             |   9 +++
- src/ap/ap_config.c                |   1 +
- src/ap/ap_config.h                |   2 +
- src/ap/ap_drv_ops.c               |  14 ++++
- src/ap/ap_drv_ops.h               |   2 +
- src/ap/hostapd.c                  |   2 +
- src/common/mtk_vendor.h           |  35 +++++++++-
- src/drivers/driver.h              |  19 ++++++
- src/drivers/driver_nl80211.c      | 108 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |   1 +
- src/drivers/driver_nl80211_capa.c |   3 +
- 13 files changed, 224 insertions(+), 1 deletion(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 3d9923692..247d68811 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5395,6 +5395,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		u8 en = atoi(pos);
- 
- 		conf->three_wire_enable = en;
-+	} else if (os_strcmp(buf, "ibf_enable") == 0) { /*ibf setting is per device*/
-+		int val = atoi(pos);
-+		conf->ibf_enable = !!val;
- 	} else {
- 		wpa_printf(MSG_ERROR,
- 			   "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 3a79a1284..10bbce341 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4110,6 +4110,30 @@ hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
- }
- 
- 
-+static int
-+hostapd_ctrl_iface_get_ibf(struct hostapd_data *hapd, char *buf,
-+					 size_t buflen)
-+{
-+	u8 ibf_enable;
-+	int ret;
-+	char *pos, *end;
-+
-+	pos = buf;
-+	end = buf + buflen;
-+
-+	if (hostapd_drv_ibf_dump(hapd, &ibf_enable) == 0) {
-+		hapd->iconf->ibf_enable = ibf_enable;
-+		ret = os_snprintf(pos, end - pos, "ibf_enable: %u\n",
-+			  ibf_enable);
-+	}
-+
-+	if (os_snprintf_error(end - pos, ret))
-+		return 0;
-+
-+	return ret;
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -4721,6 +4745,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 							  reply_size);
- 	} else if (os_strncmp(buf, "GET_MU", 6) == 0) {
- 		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
-+	} else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
-+		reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index da9dabd6f..276ca578c 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1666,6 +1666,13 @@ static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
- #endif /* ANDROID */
- 
- 
-+static int hostapd_cli_cmd_get_ibf(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "GET_IBF", 0, NULL, NULL);
-+}
-+
-+
- struct hostapd_cli_cmd {
- 	const char *cmd;
- 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
-@@ -1889,6 +1896,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- #endif /* ANDROID */
- 	{ "inband_discovery", hostapd_cli_cmd_inband_discovery, NULL,
-           "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
-+	{ "get_ibf", hostapd_cli_cmd_get_ibf, NULL,
-+	  " = show iBF state (enabled/disabled)"},
- 	{ NULL, NULL, NULL, NULL }
- };
- 
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 79fd3a24b..04e263167 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -307,6 +307,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- 	conf->edcca_enable = EDCCA_MODE_AUTO;
- 	conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
- 	conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
-+	conf->ibf_enable = IBF_DEFAULT_ENABLE;
- 
- 	hostapd_set_and_check_bw320_offset(conf, 0);
- 
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index d1bbd238c..5f084796d 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1287,6 +1287,7 @@ struct hostapd_config {
- 	s8 edcca_compensation;
- 	int *edcca_threshold;
- 	u8 three_wire_enable;
-+	u8 ibf_enable;
- };
- 
- enum three_wire_mode {
-@@ -1450,6 +1451,7 @@ hostapd_set_and_check_bw320_offset(struct hostapd_config *conf,
- #endif /* CONFIG_IEEE80211BE */
- }
- 
-+#define IBF_DEFAULT_ENABLE 0
- 
- int hostapd_mac_comp(const void *a, const void *b);
- struct hostapd_config * hostapd_config_defaults(void);
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 587b8f37f..3cace58e5 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1294,3 +1294,17 @@ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
- 	}
- 	return hapd->driver->three_wire_ctrl(hapd->drv_priv, hapd->iconf->three_wire_enable);
- }
-+
-+int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd)
-+{
-+	if (!hapd->driver || !hapd->driver->ibf_ctrl)
-+		return 0;
-+	return hapd->driver->ibf_ctrl(hapd->drv_priv, hapd->iconf->ibf_enable);
-+}
-+
-+int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable)
-+{
-+	if (!hapd->driver || !hapd->driver->ibf_dump)
-+		return 0;
-+	return hapd->driver->ibf_dump(hapd->drv_priv, ibf_enable);
-+}
-\ No newline at end of file
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 7448e7954..0886acb2d 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -156,6 +156,8 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
- int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
- int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 5ceb49962..1d941683f 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2703,6 +2703,8 @@ dfs_offload:
- 		goto fail;
- 	if (hostapd_drv_three_wire_ctrl(hapd) < 0)
- 		goto fail;
-+	if (hostapd_drv_ibf_ctrl(hapd) < 0)
-+		goto fail;
- 
- 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- 		   iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 99ecbaf71..9811f266e 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -13,7 +13,8 @@ enum mtk_nl80211_vendor_subcmds {
- 	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
- 	MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
- 	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
--	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8
-+	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
-+	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
- };
- 
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -207,6 +208,38 @@ enum mtk_vendor_attr_mu_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
- };
- 
-+enum mtk_vendor_attr_ibf_ctrl {
-+	MTK_VENDOR_ATTR_IBF_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_IBF_CTRL_ENABLE,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_IBF_CTRL,
-+	MTK_VENDOR_ATTR_IBF_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_IBF_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_ibf_dump {
-+	MTK_VENDOR_ATTR_IBF_DUMP_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_IBF_DUMP_ENABLE,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_IBF_DUMP,
-+	MTK_VENDOR_ATTR_IBF_DUMP_MAX =
-+		NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
-+};
-+
-+static struct nla_policy
-+ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
-+	[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
-+};
-+
-+static struct nla_policy
-+ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
-+	[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
-+};
-+
- 
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index dec4336a4..f5cff646e 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -181,6 +181,11 @@ struct hostapd_channel_data {
- 	 * mu onoff=<val> (bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0))
- 	 */
- 	u8 mu_onoff;
-+
-+	/**
-+	 * ibf_enable=<val>
-+	 */
-+	u8 ibf_enable;
- };
- 
- #define HE_MAC_CAPAB_0		0
-@@ -5245,6 +5250,20 @@ struct wpa_driver_ops {
- 	 *
- 	 */
- 	 int (*three_wire_ctrl)(void *priv, u8 three_wire_enable);
-+
-+	/**
-+	 * ibf_ctrl - ctrl disable/enable for ibf
-+	 * @priv: Private driver interface data
-+	 *
-+	 */
-+	int (*ibf_ctrl)(void *priv, u8 ibf_enable);
-+
-+	/**
-+	 * ibf_dump - dump ibf
-+	 * @priv: Private driver interface data
-+	 *
-+	 */
-+	int (*ibf_dump)(void *priv, u8 *ibf_enable);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index c9899e492..c17052f22 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -14390,6 +14390,112 @@ static int nl80211_enable_three_wire(void *priv, const u8 three_wire_enable)
- 	return ret;
- }
- 
-+static int nl80211_ibf_enable(void *priv, u8 ibf_enable)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_ibf_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting ibf control");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+	if (!data)
-+		goto fail;
-+
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_IBF_CTRL_ENABLE, ibf_enable);
-+
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_cmd(drv, msg);
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to set ibf_enable. ret=%d (%s)", ret, strerror(-ret));
-+	}
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
-+static int ibf_dump_handler(struct nl_msg *msg, void *arg)
-+{
-+	u8 *ibf_enable = (u8 *) arg;
-+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_IBF_DUMP_MAX + 1];
-+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+	struct nlattr *nl_vend, *attr;
-+
-+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+			genlmsg_attrlen(gnlh, 0), NULL);
-+
-+	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+	if (!nl_vend)
-+		return NL_SKIP;
-+
-+	nla_parse(tb_vendor, MTK_VENDOR_ATTR_IBF_DUMP_MAX,
-+			nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+	attr = tb_vendor[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE];
-+	if (!attr) {
-+		wpa_printf(MSG_ERROR, "nl80211: cannot find MTK_VENDOR_ATTR_IBF_DUMP_ENABLE");
-+		return NL_SKIP;
-+	}
-+
-+	*ibf_enable = nla_get_u8(attr);
-+
-+	return NL_SKIP;
-+}
-+
-+static int
-+nl80211_ibf_dump(void *priv, u8 *ibf_enable)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
-+	if (!data)
-+		goto fail;
-+
-+	nla_nest_end(msg, data);
-+
-+	ret = send_and_recv_resp(drv, msg, ibf_dump_handler, ibf_enable);
-+
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to dump ibf_enable. ret=%d (%s)", ret, strerror(-ret));
-+	}
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
- 	.desc = "Linux nl80211/cfg80211",
-@@ -14557,4 +14663,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.configure_edcca_threshold = nl80211_configure_edcca_threshold,
- 	.get_edcca = nl80211_get_edcca,
- 	.three_wire_ctrl = nl80211_enable_three_wire,
-+	.ibf_ctrl = nl80211_ibf_enable,
-+	.ibf_dump = nl80211_ibf_dump,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 5de6ca6f0..1432eeda8 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -203,6 +203,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int mtk_edcca_vendor_cmd_avail:1;
- 	unsigned int mtk_mu_vendor_cmd_avail:1;
- 	unsigned int mtk_3wire_vendor_cmd_avail:1;
-+	unsigned int mtk_ibf_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index fddcf8349..615af2eb2 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1150,6 +1150,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL :
- 					drv->mtk_3wire_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL:
-+					drv->mtk_ibf_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0036-mtk-hostapd-Fix-background-channel-overlapping-opera.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0036-mtk-hostapd-Fix-background-channel-overlapping-opera.patch
new file mode 100644
index 0000000..a25e243
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0036-mtk-hostapd-Fix-background-channel-overlapping-opera.patch
@@ -0,0 +1,64 @@
+From ce1043355ec94008f97f8ee1401a9a7a2050fb88 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 036/126] mtk: hostapd: 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 9677b26ea..754c471f5 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -815,6 +815,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)
+ {
+@@ -1142,6 +1156,8 @@ static void hostapd_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,
+@@ -1386,6 +1402,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;
+@@ -1513,6 +1530,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/0037-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0037-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch
deleted file mode 100644
index 03513eb..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0037-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-From dce3106e1c43076ab410af89f817e15cee7959a3 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Thu, 22 Sep 2022 16:08:09 +0800
-Subject: [PATCH 037/104] mtk: hostapd: Do not include HE capab IE if
- associated sta's HE capab IE is invalid
-
-The parameter 'sta' passed to send_assoc_resp() might be NULL, so an
-NULL check is necessary before access the 'sta'.
-Only one such check was missed in this function, and this patch fixs it.
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/ieee802_11.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index bda61b998..d972a25f1 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -4931,7 +4931,8 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
- #endif /* CONFIG_IEEE80211AC */
- 
- #ifdef CONFIG_IEEE80211AX
--	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
-+	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax && sta &&
-+			sta->flags & WLAN_STA_HE) {
- 		p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
- 		p = hostapd_eid_he_operation(hapd, p);
- 		p = hostapd_eid_cca(hapd, p);
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0037-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0037-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch
new file mode 100644
index 0000000..050e465
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0037-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch
@@ -0,0 +1,31 @@
+From 2059027bdf239e4e5ce3382f1f286c3830059341 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 5 Jul 2023 10:47:20 +0800
+Subject: [PATCH 037/126] mtk: hostapd: Fix hostapd_dfs_start_cac log
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 754c471f5..66ccb0057 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1685,9 +1685,11 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ 	/* TODO: How to check CAC time for ETSI weather channels? */
+ 	iface->dfs_cac_ms = 60000;
+ 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
+-		"freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
++		"freq=%d chan=%d chan_offset=%d width=%s seg0=%d "
+ 		"seg1=%d cac_time=%ds%s",
+-		freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
++		freq, (freq - 5000) / 5, chan_offset,
++		channel_width_to_string(chan_width),
++		(cf1 - 5000) / 5, cf2 ? (cf2 - 5000) / 5 : 0,
+ 		iface->dfs_cac_ms / 1000,
+ 		hostapd_dfs_is_background_event(iface, freq) ?
+ 		" (background)" : "");
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0038-mtk-hostapd-Add-DFS-detection-mode.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0038-mtk-hostapd-Add-DFS-detection-mode.patch
deleted file mode 100644
index c540b5e..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0038-mtk-hostapd-Add-DFS-detection-mode.patch
+++ /dev/null
@@ -1,136 +0,0 @@
-From 9d180d46527a6a53824227c2b7c6e4e37bf3acfb Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Feb 2023 14:55:49 +0800
-Subject: [PATCH 038/104] mtk: hostapd: Add DFS detection mode
-
-Add DFS detection mode for testing radar detection rate.
-If DFS detection mode is on, AP will not switch channels when receiving
-a radar signal.
-This detection mode also supports background chain.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- hostapd/config_file.c |  4 ++++
- hostapd/ctrl_iface.c  | 23 +++++++++++++++++++++++
- src/ap/ap_config.h    | 13 +++++++++++++
- src/ap/dfs.c          | 10 ++++++++++
- 4 files changed, 50 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 247d68811..40ade89c0 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5398,6 +5398,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 	} else if (os_strcmp(buf, "ibf_enable") == 0) { /*ibf setting is per device*/
- 		int val = atoi(pos);
- 		conf->ibf_enable = !!val;
-+	} else if (os_strcmp(buf, "dfs_detect_mode") == 0) { /*bypass channel switch*/
-+		u8 en = strtol(pos, NULL, 10);
-+
-+		conf->dfs_detect_mode = en;
- 	} else {
- 		wpa_printf(MSG_ERROR,
- 			   "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 10bbce341..71a87459c 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4134,6 +4134,26 @@ hostapd_ctrl_iface_get_ibf(struct hostapd_data *hapd, char *buf,
- }
- 
- 
-+static int
-+hostapd_ctrl_iface_set_dfs_detect_mode(struct hostapd_data *hapd, char *value,
-+				       char *buf, size_t buflen)
-+{
-+	u8 dfs_detect_mode;
-+
-+	if (!value)
-+		return -1;
-+
-+	dfs_detect_mode = strtol(value, NULL, 10);
-+	if (dfs_detect_mode > DFS_DETECT_MODE_MAX) {
-+		wpa_printf(MSG_ERROR, "Invalid value for dfs detect mode");
-+		return -1;
-+	}
-+	hapd->iconf->dfs_detect_mode = dfs_detect_mode;
-+
-+	return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -4747,6 +4767,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
- 	} else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
- 		reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
-+	} else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
-+		reply_len = hostapd_ctrl_iface_set_dfs_detect_mode(hapd, buf + 16,
-+								   reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 5f084796d..7607c63e1 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1288,6 +1288,7 @@ struct hostapd_config {
- 	int *edcca_threshold;
- 	u8 three_wire_enable;
- 	u8 ibf_enable;
-+	u8 dfs_detect_mode;
- };
- 
- enum three_wire_mode {
-@@ -1302,6 +1303,18 @@ enum three_wire_mode {
- 		NUM_THREE_WIRE_MODE - 1
- };
- 
-+enum dfs_mode {
-+	DFS_DETECT_MODE_DISABLE,
-+	DFS_DETECT_MODE_AP_ENABLE,
-+	DFS_DETECT_MODE_BACKGROUND_ENABLE,
-+	DFS_DETECT_MODE_ALL_ENABLE,
-+
-+	/* keep last */
-+	NUM_DFS_DETECT_MODE,
-+	DFS_DETECT_MODE_MAX =
-+		NUM_DFS_DETECT_MODE - 1
-+};
-+
- enum edcca_mode {
- 	EDCCA_MODE_FORCE_DISABLE = 0,
- 	EDCCA_MODE_AUTO = 1,
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index d14fad136..1df4de6b8 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1336,6 +1336,11 @@ hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
- 		   __func__, iface->radar_background.cac_started ? "yes" : "no",
- 		   hostapd_csa_in_progress(iface) ? "yes" : "no");
- 
-+	/* Skip channel switch when background dfs detect mode is on */
-+	if (iface->conf->dfs_detect_mode == DFS_DETECT_MODE_BACKGROUND_ENABLE ||
-+	    iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
-+		return 0;
-+
- 	/* Check if CSA in progress */
- 	if (hostapd_csa_in_progress(iface))
- 		return 0;
-@@ -1384,6 +1389,11 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
- 		   __func__, iface->cac_started ? "yes" : "no",
- 		   hostapd_csa_in_progress(iface) ? "yes" : "no");
- 
-+	/* Skip channel switch when dfs detect mode is on */
-+	if (iface->conf->dfs_detect_mode == DFS_DETECT_MODE_AP_ENABLE ||
-+	    iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
-+		return 0;
-+
- 	/* Check if CSA in progress */
- 	if (hostapd_csa_in_progress(iface))
- 		return 0;
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0038-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0038-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch
new file mode 100644
index 0000000..af4f9ac
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0038-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch
@@ -0,0 +1,62 @@
+From 2f22f947b2719f96d44f3aca158f19228c4a7dee Mon Sep 17 00:00:00 2001
+From: Michael Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 13 Jul 2023 13:14:26 +0800
+Subject: [PATCH 038/126] mtk: hostapd: Check the bridge after ioctl
+ SIOCBRADDIF failed
+
+If ioctl returns EBUSY on command SIOCBRADDIF, the interface might
+already be bridged by others, and linux_br_add_if should not indicate an
+error in the case.
+
+This patch checks whether the interface is correctly brigded when ioctl
+returns EBUSY.
+
+Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+---
+ src/drivers/linux_ioctl.c | 16 +++++++++++++++-
+ 1 file changed, 15 insertions(+), 1 deletion(-)
+
+diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c
+index 29abc0c59..73d27825d 100644
+--- a/src/drivers/linux_ioctl.c
++++ b/src/drivers/linux_ioctl.c
+@@ -150,7 +150,8 @@ int linux_br_del(int sock, const char *brname)
+ int linux_br_add_if(int sock, const char *brname, const char *ifname)
+ {
+ 	struct ifreq ifr;
+-	int ifindex;
++	int ifindex, ret;
++	char in_br[IFNAMSIZ];
+ 
+ 	ifindex = if_nametoindex(ifname);
+ 	if (ifindex == 0)
+@@ -165,6 +166,17 @@ int linux_br_add_if(int sock, const char *brname, const char *ifname)
+ 
+ 		wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge "
+ 			   "%s: %s", ifname, brname, strerror(errno));
++
++		/* If ioctl returns -EBUSY when adding interface into bridge,
++		 * the interface might already be added by netifd, so here we
++		 * check whether the interface is currently on the right
++		 * bridge. */
++		if(errno == EBUSY && linux_br_get(in_br, ifname) == 0 &&
++	           os_strcmp(in_br, brname) == 0)
++			ret = 0;
++		else
++			ret = -1;
++
+ 		errno = saved_errno;
+ 
+ 		/* If ioctl() returns EBUSY when adding an interface into the
+@@ -175,6 +187,8 @@ int linux_br_add_if(int sock, const char *brname, const char *ifname)
+ 		if (errno != EBUSY || linux_br_get(in_br, ifname) != 0 ||
+ 		    os_strcmp(in_br, brname) != 0)
+ 			return -1;
++
++		return ret;
+ 	}
+ 
+ 	return 0;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0039-mtk-hostapd-Add-DFS-offchan-channel-switch.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0039-mtk-hostapd-Add-DFS-offchan-channel-switch.patch
deleted file mode 100644
index a37e541..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0039-mtk-hostapd-Add-DFS-offchan-channel-switch.patch
+++ /dev/null
@@ -1,192 +0,0 @@
-From 7f0f977e36718a7a827f7d78e6ab578d53aa630b Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Feb 2023 14:56:55 +0800
-Subject: [PATCH 039/104] mtk: hostapd: Add DFS offchan channel switch
-
-Add DFS background chain channel switch command for testing purpose.
-This feature is implemented via hostapd_cli command.
-Command format:
-hostapd_cli -i <interface> raw SET_OFFCHAN_CTRL chan=<dfs_channel>
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- hostapd/ctrl_iface.c | 72 ++++++++++++++++++++++++++++++++++++++++++++
- src/ap/dfs.c         | 25 ++++++---------
- src/ap/dfs.h         | 15 +++++++++
- 3 files changed, 96 insertions(+), 16 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 71a87459c..68dcc7982 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4154,6 +4154,76 @@ hostapd_ctrl_iface_set_dfs_detect_mode(struct hostapd_data *hapd, char *value,
- }
- 
- 
-+static int
-+hostapd_ctrl_iface_set_offchan_ctrl(struct hostapd_data *hapd, char *cmd,
-+				    char *buf, size_t buflen)
-+{
-+	struct hostapd_iface *iface = hapd->iface;
-+	char *pos, *param;
-+	enum hostapd_hw_mode hw_mode;
-+	bool chan_found = false;
-+	int i, num_available_chandefs, channel, chan_width, sec = 0;
-+	int sec_chan_idx_80p80 = -1;
-+	u8 oper_centr_freq_seg0_idx, oper_centr_freq_seg1_idx;
-+	struct hostapd_channel_data *chan;
-+	enum dfs_channel_type type = DFS_NO_CAC_YET;
-+
-+	param = os_strchr(cmd, ' ');
-+	if (!param)
-+		return -1;
-+	*param++ = '\0';
-+
-+	pos = os_strstr(param, "chan=");
-+	if (pos)
-+		channel = strtol(pos + 5, NULL, 10);
-+	else
-+		return -1;
-+
-+	num_available_chandefs = dfs_find_channel(iface, NULL, 0, type);
-+	for (i = 0; i < num_available_chandefs; i++) {
-+		dfs_find_channel(iface, &chan, i, type);
-+		if (chan->chan == channel) {
-+			chan_found = true;
-+			break;
-+		}
-+	}
-+
-+	if (!chan_found)
-+		return -1;
-+
-+	if (iface->conf->secondary_channel)
-+		sec = 1;
-+
-+	dfs_adjust_center_freq(iface, chan,
-+			       sec,
-+			       sec_chan_idx_80p80,
-+			       &oper_centr_freq_seg0_idx,
-+			       &oper_centr_freq_seg1_idx);
-+
-+	if (hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
-+				  chan->freq, chan->chan,
-+				  iface->conf->ieee80211n,
-+				  iface->conf->ieee80211ac,
-+				  iface->conf->ieee80211ax,
-+				  iface->conf->ieee80211be,
-+				  sec, hostapd_get_oper_chwidth(iface->conf),
-+				  oper_centr_freq_seg0_idx,
-+				  oper_centr_freq_seg1_idx, true)) {
-+		wpa_printf(MSG_ERROR, "DFS failed to start CAC offchannel");
-+		iface->radar_background.channel = -1;
-+		return -1;
-+	}
-+
-+	iface->radar_background.channel = chan->chan;
-+	iface->radar_background.freq = chan->freq;
-+	iface->radar_background.secondary_channel = sec;
-+	iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
-+	iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
-+
-+	return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -4770,6 +4840,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 	} else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
- 		reply_len = hostapd_ctrl_iface_set_dfs_detect_mode(hapd, buf + 16,
- 								   reply, reply_size);
-+	} else if (os_strncmp(buf, "SET_OFFCHAN_CTRL", 16) == 0) {
-+		reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 1df4de6b8..ece27d070 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -19,13 +19,6 @@
- #include "dfs.h"
- #include "crypto/crypto.h"
- 
--
--enum dfs_channel_type {
--	DFS_ANY_CHANNEL,
--	DFS_AVAILABLE, /* non-radar or radar-available */
--	DFS_NO_CAC_YET, /* radar-not-yet-available */
--};
--
- static struct hostapd_channel_data *
- dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
- 			u8 *oper_centr_freq_seg0_idx,
-@@ -238,9 +231,9 @@ static int is_in_chanlist(struct hostapd_iface *iface,
-  *  - hapd->vht/he_oper_centr_freq_seg0_idx
-  *  - hapd->vht/he_oper_centr_freq_seg1_idx
-  */
--static int dfs_find_channel(struct hostapd_iface *iface,
--			    struct hostapd_channel_data **ret_chan,
--			    int idx, enum dfs_channel_type type)
-+int dfs_find_channel(struct hostapd_iface *iface,
-+		     struct hostapd_channel_data **ret_chan,
-+		     int idx, enum dfs_channel_type type)
- {
- 	struct hostapd_hw_modes *mode;
- 	struct hostapd_channel_data *chan;
-@@ -300,12 +293,12 @@ static int dfs_find_channel(struct hostapd_iface *iface,
- }
- 
- 
--static void dfs_adjust_center_freq(struct hostapd_iface *iface,
--				   struct hostapd_channel_data *chan,
--				   int secondary_channel,
--				   int sec_chan_idx_80p80,
--				   u8 *oper_centr_freq_seg0_idx,
--				   u8 *oper_centr_freq_seg1_idx)
-+void dfs_adjust_center_freq(struct hostapd_iface *iface,
-+			    struct hostapd_channel_data *chan,
-+			    int secondary_channel,
-+			    int sec_chan_idx_80p80,
-+			    u8 *oper_centr_freq_seg0_idx,
-+			    u8 *oper_centr_freq_seg1_idx)
- {
- 	if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax)
- 		return;
-diff --git a/src/ap/dfs.h b/src/ap/dfs.h
-index 606c1b393..c2556d2d9 100644
---- a/src/ap/dfs.h
-+++ b/src/ap/dfs.h
-@@ -9,6 +9,12 @@
- #ifndef DFS_H
- #define DFS_H
- 
-+enum dfs_channel_type {
-+	DFS_ANY_CHANNEL,
-+	DFS_AVAILABLE, /* non-radar or radar-available */
-+	DFS_NO_CAC_YET, /* radar-not-yet-available */
-+};
-+
- int hostapd_handle_dfs(struct hostapd_iface *iface);
- 
- int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
-@@ -32,5 +38,14 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
- int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
- int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
- 			   int center_freq);
-+int dfs_find_channel(struct hostapd_iface *iface,
-+		     struct hostapd_channel_data **ret_chan,
-+		     int idx, enum dfs_channel_type type);
-+void dfs_adjust_center_freq(struct hostapd_iface *iface,
-+			    struct hostapd_channel_data *chan,
-+			    int secondary_channel,
-+			    int sec_chan_idx_80p80,
-+			    u8 *oper_centr_freq_seg0_idx,
-+			    u8 *oper_centr_freq_seg1_idx);
- 
- #endif /* DFS_H */
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0039-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0039-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch
new file mode 100644
index 0000000..42c9a65
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0039-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch
@@ -0,0 +1,30 @@
+From c8bd35df569715a82cddb0df4a5f894fe535be84 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Fri, 14 Jul 2023 17:19:13 +0800
+Subject: [PATCH 039/126] mtk: hostapd: Update parameter_set_count in MU EDCA
+ IE
+
+without this patch, MU EDCA Parameter update count not equal to
+WMM Parameter set count.
+
+---
+ src/ap/ieee802_11_he.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
+index 3c6ee72fe..3b6b2041c 100644
+--- a/src/ap/ieee802_11_he.c
++++ b/src/ap/ieee802_11_he.c
+@@ -317,6 +317,9 @@ u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
+ 	edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
+ 	os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
+ 
++	if (hapd->conf->wmm_enabled)
++		edca->he_qos_info = hapd->parameter_set_count % 0xf;
++
+ 	wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
+ 		    pos, sizeof(*edca));
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0040-mtk-hostapd-Add-amsdu-set-get-ctrl.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0040-mtk-hostapd-Add-amsdu-set-get-ctrl.patch
deleted file mode 100644
index ead86d5..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0040-mtk-hostapd-Add-amsdu-set-get-ctrl.patch
+++ /dev/null
@@ -1,400 +0,0 @@
-From 8291551130127914cbe3b5346fbd2edc89437df8 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 16 Dec 2022 03:57:11 +0800
-Subject: [PATCH 040/104] mtk: hostapd: Add amsdu set get ctrl
-
----
- hostapd/config_file.c             |   9 +++
- hostapd/ctrl_iface.c              |  26 +++++++
- hostapd/hostapd_cli.c             |   9 +++
- src/ap/ap_config.c                |   1 +
- src/ap/ap_config.h                |   1 +
- src/ap/ap_drv_ops.c               |  14 ++++
- src/ap/ap_drv_ops.h               |   2 +
- src/ap/hostapd.c                  |   2 +
- src/common/mtk_vendor.h           |  17 ++++-
- src/drivers/driver.h              |   9 +++
- src/drivers/driver_nl80211.c      | 114 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |   1 +
- src/drivers/driver_nl80211_capa.c |   3 +
- 13 files changed, 207 insertions(+), 1 deletion(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 40ade89c0..7695ab196 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5402,6 +5402,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		u8 en = strtol(pos, NULL, 10);
- 
- 		conf->dfs_detect_mode = en;
-+	} else if (os_strcmp(buf, "amsdu") == 0) {
-+		int val = atoi(pos);
-+		if (val < 0 || val > 1) {
-+			wpa_printf(MSG_ERROR,
-+					 "Line %d: invalid amsdu value",
-+					 line);
-+			return 1;
-+		}
-+		conf->amsdu = val;
- 	} else {
- 		wpa_printf(MSG_ERROR,
- 			   "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 68dcc7982..5f6278ecd 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4224,6 +4224,30 @@ hostapd_ctrl_iface_set_offchan_ctrl(struct hostapd_data *hapd, char *cmd,
- }
- 
- 
-+static int
-+hostapd_ctrl_iface_get_amsdu(struct hostapd_data *hapd, char *buf,
-+					 size_t buflen)
-+{
-+	u8 amsdu;
-+	int ret;
-+	char *pos, *end;
-+
-+	pos = buf;
-+	end = buf + buflen;
-+
-+	if (hostapd_drv_amsdu_dump(hapd, &amsdu) == 0) {
-+		hapd->iconf->amsdu = amsdu;
-+		ret = os_snprintf(pos, end - pos, "[hostapd_cli] AMSDU: %u\n",
-+					hapd->iconf->amsdu);
-+	}
-+
-+	if (os_snprintf_error(end - pos, ret))
-+		return 0;
-+
-+	return ret;
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -4842,6 +4866,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 								   reply, reply_size);
- 	} else if (os_strncmp(buf, "SET_OFFCHAN_CTRL", 16) == 0) {
- 		reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
-+	} else if (os_strncmp(buf, "GET_AMSDU", 9) == 0) {
-+		reply_len = hostapd_ctrl_iface_get_amsdu(hapd, reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 276ca578c..847f867ab 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1673,6 +1673,13 @@ static int hostapd_cli_cmd_get_ibf(struct wpa_ctrl *ctrl, int argc,
- }
- 
- 
-+static int hostapd_cli_cmd_get_amsdu(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "GET_AMSDU", 0, NULL, NULL);
-+}
-+
-+
- struct hostapd_cli_cmd {
- 	const char *cmd;
- 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
-@@ -1898,6 +1905,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
-           "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
- 	{ "get_ibf", hostapd_cli_cmd_get_ibf, NULL,
- 	  " = show iBF state (enabled/disabled)"},
-+	{ "get_amsdu", hostapd_cli_cmd_get_amsdu, NULL,
-+		" = show AMSDU state"},
- 	{ NULL, NULL, NULL, NULL }
- };
- 
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 04e263167..2420a251e 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -308,6 +308,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- 	conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
- 	conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
- 	conf->ibf_enable = IBF_DEFAULT_ENABLE;
-+	conf->amsdu = 1;
- 
- 	hostapd_set_and_check_bw320_offset(conf, 0);
- 
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 7607c63e1..123e12c8f 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1289,6 +1289,7 @@ struct hostapd_config {
- 	u8 three_wire_enable;
- 	u8 ibf_enable;
- 	u8 dfs_detect_mode;
-+	u8 amsdu;
- };
- 
- enum three_wire_mode {
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 3cace58e5..23228a8d2 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1307,4 +1307,18 @@ int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable)
- 	if (!hapd->driver || !hapd->driver->ibf_dump)
- 		return 0;
- 	return hapd->driver->ibf_dump(hapd->drv_priv, ibf_enable);
-+}
-+
-+int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd)
-+{
-+	if (!hapd->driver || !hapd->driver->amsdu_ctrl)
-+		return 0;
-+	return hapd->driver->amsdu_ctrl(hapd->drv_priv, hapd->iconf->amsdu);
-+}
-+
-+int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu)
-+{
-+	if (!hapd->driver || !hapd->driver->amsdu_dump)
-+		return 0;
-+	return hapd->driver->amsdu_dump(hapd->drv_priv, amsdu);
- }
-\ No newline at end of file
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 0886acb2d..f3a044557 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -158,6 +158,8 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
- int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
-+int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 1d941683f..a5b683676 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2705,6 +2705,8 @@ dfs_offload:
- 		goto fail;
- 	if (hostapd_drv_ibf_ctrl(hapd) < 0)
- 		goto fail;
-+	if (hostapd_drv_amsdu_ctrl(hapd) < 0)
-+		goto fail;
- 
- 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- 		   iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 9811f266e..7b4d7c11a 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -170,7 +170,6 @@ enum mtk_vendor_attr_wireless_ctrl {
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
--	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
- 
-@@ -180,6 +179,22 @@ enum mtk_vendor_attr_wireless_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
- };
- 
-+enum mtk_vendor_attr_wireless_dump {
-+	MTK_VENDOR_ATTR_WIRELESS_DUMP_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP,
-+	MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX =
-+		NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
-+};
-+
-+static const struct nla_policy
-+wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
-+	[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
-+};
-+
- enum mtk_vendor_attr_rfeature_ctrl {
- 	MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index f5cff646e..6eeb9c22e 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5264,6 +5264,15 @@ struct wpa_driver_ops {
- 	 *
- 	 */
- 	int (*ibf_dump)(void *priv, u8 *ibf_enable);
-+
-+	/**
-+	 * amsdu_ctrl - enable/disable amsdu
-+	 * amsdu_dump - get current amsdu status
-+	 * @priv: Private driver interface data
-+	 *
-+	 */
-+	int (*amsdu_ctrl)(void *priv, u8 amsdu);
-+	int (*amsdu_dump)(void *priv, u8 *amsdu);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index c17052f22..eca2ff077 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -14496,6 +14496,118 @@ fail:
- 	return -ENOBUFS;
- }
- 
-+static int nl80211_enable_amsdu(void *priv, u8 amsdu)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_wireless_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting ap wireless control");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+	if (!data)
-+		goto fail;
-+
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU, amsdu);
-+
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_cmd(drv, msg);
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to set amsdu. ret=%d (%s)", ret, strerror(-ret));
-+	}
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
-+static int dump_amsdu_handler(struct nl_msg *msg, void *arg)
-+{
-+	u8 *amsdu = (u8 *) arg;
-+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX + 1];
-+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+	struct nlattr *nl_vend, *attr_amsdu;
-+
-+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+			genlmsg_attrlen(gnlh, 0), NULL);
-+
-+	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+	if (!nl_vend)
-+		return NL_SKIP;
-+
-+	nla_parse(tb_vendor, MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX,
-+			nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+	attr_amsdu = tb_vendor[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU];
-+	if (!attr_amsdu ){
-+		wpa_printf(MSG_ERROR, "nl80211: cannot find vendor attributes");
-+		return NL_SKIP;
-+	}
-+
-+	*amsdu = nla_get_u8(attr_amsdu);
-+
-+	return NL_SKIP;
-+}
-+
-+static int
-+nl80211_dump_amsdu(void *priv, u8 *amsdu)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_wireless_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+				 "nl80211: Driver does not support ap_wireless control");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+	if (!data)
-+		goto fail;
-+
-+	nla_nest_end(msg, data);
-+
-+	ret = send_and_recv_resp(drv, msg, dump_amsdu_handler, amsdu);
-+
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to dump amsdu. ret=%d (%s)", ret, strerror(-ret));
-+	}
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
- 	.desc = "Linux nl80211/cfg80211",
-@@ -14665,4 +14777,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.three_wire_ctrl = nl80211_enable_three_wire,
- 	.ibf_ctrl = nl80211_ibf_enable,
- 	.ibf_dump = nl80211_ibf_dump,
-+	.amsdu_ctrl = nl80211_enable_amsdu,
-+	.amsdu_dump = nl80211_dump_amsdu,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 1432eeda8..5aa813e26 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -204,6 +204,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int mtk_mu_vendor_cmd_avail:1;
- 	unsigned int mtk_3wire_vendor_cmd_avail:1;
- 	unsigned int mtk_ibf_vendor_cmd_avail:1;
-+	unsigned int mtk_wireless_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 615af2eb2..474d4e273 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1153,6 +1153,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL:
- 					drv->mtk_ibf_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL:
-+					drv->mtk_wireless_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0040-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0040-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch
new file mode 100644
index 0000000..86c4b44
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0040-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch
@@ -0,0 +1,36 @@
+From 30e4604148273074fee5daeff21a6a7029c19832 Mon Sep 17 00:00:00 2001
+From: mtk20656 <chank.chen@mediatek.com>
+Date: Mon, 24 Jul 2023 11:30:27 +0800
+Subject: [PATCH 040/126] mtk: hostapd: add extension IE list for non-inherit
+ IE in mbssid
+
+Certain clients do not scan all non tx profiles due to absence of
+element ID extension list which is mandatory field in non inheritance
+IE. Non inheritance Element ID is followed by extension element ID.
+Length is expected to be mentioned. Currently we do not support any
+extension element and hence filling length as 0.
+
+Signed-off-by: mtk20656 <chank.chen@mediatek.com>
+---
+ src/ap/ieee802_11.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+ mode change 100644 => 100755 src/ap/ieee802_11.c
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+old mode 100644
+new mode 100755
+index 18d5b8f79..258ab7943
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -8078,7 +8078,7 @@ static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
+ 		else if (hapd->conf->xrates_supported)
+ 			ie_count++;
+ 		if (ie_count)
+-			nontx_profile_len += 4 + ie_count;
++			nontx_profile_len += 5 + ie_count;
+ 
+ 		if (len + nontx_profile_len > 255)
+ 			break;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0041-mtk-hostapd-Add-he_ldpc-configuration.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0041-mtk-hostapd-Add-he_ldpc-configuration.patch
deleted file mode 100644
index 5dac957..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0041-mtk-hostapd-Add-he_ldpc-configuration.patch
+++ /dev/null
@@ -1,102 +0,0 @@
-From 0fa801d52f2e29c87aef757efc690aa5b2474f1b Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Thu, 12 Jan 2023 15:18:19 +0800
-Subject: [PATCH 041/104] mtk: hostapd: Add he_ldpc configuration
-
----
- hostapd/config_file.c        | 2 ++
- hostapd/hostapd.conf         | 5 +++++
- src/ap/ap_config.c           | 1 +
- src/ap/ap_config.h           | 1 +
- src/ap/ieee802_11_he.c       | 7 +++++++
- src/common/ieee802_11_defs.h | 3 +++
- 6 files changed, 19 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 7695ab196..dadc8f108 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3908,6 +3908,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		conf->he_phy_capab.he_su_beamformee = atoi(pos);
- 	} else if (os_strcmp(buf, "he_mu_beamformer") == 0) {
- 		conf->he_phy_capab.he_mu_beamformer = atoi(pos);
-+	} else if (os_strcmp(buf, "he_ldpc") == 0) {
-+		conf->he_phy_capab.he_ldpc = atoi(pos);
- 	} else if (os_strcmp(buf, "he_bss_color") == 0) {
- 		conf->he_op.he_bss_color = atoi(pos) & 0x3f;
- 		conf->he_op.he_bss_color_disabled = 0;
-diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
-index 0d10998af..f988b17b2 100644
---- a/hostapd/hostapd.conf
-+++ b/hostapd/hostapd.conf
-@@ -833,6 +833,11 @@ wmm_ac_vo_acm=0
- # 1 = supported
- #he_mu_beamformer=1
- 
-+#he_ldpc: HE LDPC support
-+# 0 = not supported
-+# 1 = supported (default)
-+#he_ldpc=1
-+
- # he_bss_color: BSS color (1-63)
- #he_bss_color=1
- 
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 2420a251e..ba1b2a7a3 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -273,6 +273,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- #endif /* CONFIG_ACS */
- 
- #ifdef CONFIG_IEEE80211AX
-+	conf->he_phy_capab.he_ldpc = 1;
- 	conf->he_op.he_rts_threshold = HE_OPERATION_RTS_THRESHOLD_MASK >>
- 		HE_OPERATION_RTS_THRESHOLD_OFFSET;
- 	/* Set default basic MCS/NSS set to single stream MCS 0-7 */
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 123e12c8f..d995b8d9c 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -985,6 +985,7 @@ struct hostapd_bss_config {
-  * struct he_phy_capabilities_info - HE PHY capabilities
-  */
- struct he_phy_capabilities_info {
-+	bool he_ldpc;
- 	bool he_su_beamformer;
- 	bool he_su_beamformee;
- 	bool he_mu_beamformer;
-diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
-index a2deda6c4..3c6ee72fe 100644
---- a/src/ap/ieee802_11_he.c
-+++ b/src/ap/ieee802_11_he.c
-@@ -139,6 +139,13 @@ u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
- 		os_memcpy(&cap->optional[mcs_nss_size],
- 			  mode->he_capab[opmode].ppet,  ppet_size);
- 
-+	if (hapd->iface->conf->he_phy_capab.he_ldpc)
-+		cap->he_phy_capab_info[HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX] |=
-+			HE_PHYCAP_LDPC_CODING_IN_PAYLOAD;
-+	else
-+		cap->he_phy_capab_info[HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX] &=
-+			~HE_PHYCAP_LDPC_CODING_IN_PAYLOAD;
-+
- 	if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
- 		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
- 			HE_PHYCAP_SU_BEAMFORMER_CAPAB;
-diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
-index 7a1da3252..a289c2d87 100644
---- a/src/common/ieee802_11_defs.h
-+++ b/src/common/ieee802_11_defs.h
-@@ -2452,6 +2452,9 @@ struct ieee80211_spatial_reuse {
- #define HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G	((u8) BIT(3))
- #define HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G	((u8) BIT(4))
- 
-+#define HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX	1
-+#define HE_PHYCAP_LDPC_CODING_IN_PAYLOAD	((u8) BIT(5))
-+
- #define HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX	3
- #define HE_PHYCAP_SU_BEAMFORMER_CAPAB		((u8) BIT(7))
- #define HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX	4
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0041-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0041-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch
new file mode 100644
index 0000000..d3daf72
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0041-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch
@@ -0,0 +1,44 @@
+From f8f314f2ce3c4e8e82cc98bbd404c18424b13dd7 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 041/126] mtk: hostapd: 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 f06687064..4549aecb2 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4658,6 +4658,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/0042-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0042-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch
new file mode 100644
index 0000000..1a28908
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0042-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch
@@ -0,0 +1,45 @@
+From d29370b7ec78ba410d0877441206849e197b1609 Mon Sep 17 00:00:00 2001
+From: mtk23510 <rudra.shahi@mediatek.com>
+Date: Fri, 26 May 2023 14:52:35 +0800
+Subject: [PATCH 042/126] mtk: hostapd: Add support for gtk rekeying in hostapd
+ cli
+
+Signed-off-by: mtk23510 <rudra.shahi@mediatek.com>
+---
+ hostapd/hostapd_cli.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index dba805e8a..e43d515d5 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1309,6 +1309,15 @@ static int hostapd_cli_cmd_stop_ap(struct wpa_ctrl *ctrl, int argc,
+ }
+ 
+ 
++#ifdef CONFIG_TESTING_OPTIONS
++static int hostapd_cli_cmd_rekey_gtk(struct wpa_ctrl *ctrl, int argc,
++				      char *argv[])
++{
++	return wpa_ctrl_command(ctrl, "REKEY_GTK");
++}
++#endif
++
++
+ static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ {
+ 	char cmd[256];
+@@ -1861,6 +1870,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 	  "= update Beacon frame contents\n"},
+ 	{ "stop_ap", hostapd_cli_cmd_stop_ap, NULL,
+ 	  "= stop AP\n"},
++#ifdef CONFIG_TESTING_OPTIONS
++	{ "rekey_gtk", hostapd_cli_cmd_rekey_gtk, NULL,
++	  "= rekey gtk\n"},
++#endif
+ 	{ "erp_flush", hostapd_cli_cmd_erp_flush, NULL,
+ 	  "= drop all ERP keys"},
+ 	{ "log_level", hostapd_cli_cmd_log_level, NULL,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0042-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0042-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch
deleted file mode 100644
index 8676424..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0042-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From d342772023b344ce09a22eaeff4307e5bfe52d7c Mon Sep 17 00:00:00 2001
-From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
-Date: Tue, 24 Jan 2023 19:06:44 +0800
-Subject: [PATCH 042/104] mtk: hostapd: Add vendor command attribute for RTS BW
- signaling.
-
-Signed-off-by: himanshu.goyal <himanshu.goyal@mediatek.com>
----
- src/common/mtk_vendor.h | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 7b4d7c11a..ace993bc8 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -172,6 +172,7 @@ enum mtk_vendor_attr_wireless_ctrl {
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
- 
- 	/* keep last */
- 	NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0043-mtk-hostapd-6G-band-does-not-require-DFS.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0043-mtk-hostapd-6G-band-does-not-require-DFS.patch
deleted file mode 100644
index 304e444..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0043-mtk-hostapd-6G-band-does-not-require-DFS.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-From 931c93d5d19249a0b4e4efbc5957f537578dfd81 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Mon, 13 Feb 2023 11:03:53 +0800
-Subject: [PATCH 043/104] mtk: hostapd: 6G band does not require DFS
-
----
- src/ap/dfs.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index ece27d070..44f3a2cb1 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1525,6 +1525,7 @@ int hostapd_is_dfs_required(struct hostapd_iface *iface)
- 	if ((!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
- 	     !iface->conf->ieee80211h) ||
- 	    !iface->current_mode ||
-+	    is_6ghz_freq(iface->freq) ||
- 	    iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
- 		return 0;
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0043-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0043-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch
new file mode 100644
index 0000000..2a3c271
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0043-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch
@@ -0,0 +1,48 @@
+From 8d8843872efa3b2a694e85b3125435fc6a1abc09 Mon Sep 17 00:00:00 2001
+From: Michael Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 11 Jul 2023 14:17:43 +0800
+Subject: [PATCH 043/126] mtk: hostapd: Set WMM and TX queue parameters for
+ wpa_supplicant
+
+Since most of the time, wpa_supplicant will be used to setup an STA
+interface, it's default WMM and TX queue parameters should be set for
+STA.
+
+Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/config.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
+index dc4b0636a..d2c05e352 100644
+--- a/wpa_supplicant/config.c
++++ b/wpa_supplicant/config.c
+@@ -4722,19 +4722,19 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
+ 	const struct hostapd_wmm_ac_params ac_bk =
+ 		{ aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
+ 	const struct hostapd_wmm_ac_params ac_be =
+-		{ aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
++		{ aCWmin, aCWmin + 2, 3, 0, 0 }; /* best effort traffic */
+ 	const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
+-		{ aCWmin - 1, aCWmin, 2, 3008 / 32, 0 };
++		{ aCWmin - 1, aCWmin, 1, 3008 / 32, 0 };
+ 	const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
+-		{ aCWmin - 2, aCWmin - 1, 2, 1504 / 32, 0 };
++		{ aCWmin - 2, aCWmin - 1, 1, 1504 / 32, 0 };
+ 	const struct hostapd_tx_queue_params txq_bk =
+ 		{ 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
+ 	const struct hostapd_tx_queue_params txq_be =
+-		{ 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0 };
++		{ 3, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
+ 	const struct hostapd_tx_queue_params txq_vi =
+-		{ 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30 };
++		{ 2, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30 };
+ 	const struct hostapd_tx_queue_params txq_vo =
+-		{ 1, (ecw2cw(aCWmin) + 1) / 4 - 1,
++		{ 2, (ecw2cw(aCWmin) + 1) / 4 - 1,
+ 		  (ecw2cw(aCWmin) + 1) / 2 - 1, 15 };
+ 
+ #undef ecw2cw
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0044-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0044-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch
deleted file mode 100644
index 9a8d8ae..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0044-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch
+++ /dev/null
@@ -1,46 +0,0 @@
-From 79d4323faf5c14f9a4a8e0cf3219582210463206 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Feb 2023 11:01:18 +0800
-Subject: [PATCH 044/104] mtk: hostapd: Fix sending wrong VHT operation IE in
- CSA while using ZWDFS
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c | 14 +++++++++-----
- 1 file changed, 9 insertions(+), 5 deletions(-)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 44f3a2cb1..c703d2fb8 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1129,6 +1129,14 @@ static int
- hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
- {
- 	u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
-+	int ret;
-+
-+	ret = hostapd_dfs_request_channel_switch(iface, iface->radar_background.channel,
-+						 iface->radar_background.freq,
-+						 iface->radar_background.secondary_channel,
-+						 current_vht_oper_chwidth,
-+						 iface->radar_background.centr_freq_seg0_idx,
-+						 iface->radar_background.centr_freq_seg1_idx);
- 
- 	iface->conf->channel = iface->radar_background.channel;
- 	iface->freq = iface->radar_background.freq;
-@@ -1141,11 +1149,7 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
- 
- 	hostapd_dfs_update_background_chain(iface);
- 
--	return hostapd_dfs_request_channel_switch(
--		iface, iface->conf->channel, iface->freq,
--		iface->conf->secondary_channel, current_vht_oper_chwidth,
--		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
--		hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
-+	return ret;
- }
- 
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0044-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0044-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch
new file mode 100644
index 0000000..4d071a4
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0044-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch
@@ -0,0 +1,78 @@
+From 9aa3ee6838af657f932ed1e7e70420ecfae8452e Mon Sep 17 00:00:00 2001
+From: Michael Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 7 Jul 2023 17:16:11 +0800
+Subject: [PATCH 044/126] mtk: hostapd: Set STA TX queue parameters
+ configuration after association
+
+This patch adds the way for wpa_supplicant to set driver's TX queue
+parameters.
+Since STA parses and apply TX queue parameters from AP beacon's WMM IE
+during association, wpa_supplicant set driver's TX queue parameters
+after the association.
+
+Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/driver_i.h | 12 ++++++++++++
+ wpa_supplicant/events.c   | 16 ++++++++++++++++
+ 2 files changed, 28 insertions(+)
+
+diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
+index d01b52bb1..663e16053 100644
+--- a/wpa_supplicant/driver_i.h
++++ b/wpa_supplicant/driver_i.h
+@@ -321,6 +321,18 @@ static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s,
+ 	return 0;
+ }
+ 
++static inline int wpa_drv_set_tx_queue_params(struct wpa_supplicant *wpa_s,
++					      int q, int aifs, int cw_min,
++					      int cw_max, int burst_time)
++{
++	int link_id = -1;
++	if (wpa_s->driver->set_tx_queue_params)
++		return wpa_s->driver->set_tx_queue_params(wpa_s->drv_priv, q,
++							  aifs, cw_min, cw_max,
++							  burst_time, link_id);
++	return 0;
++}
++
+ static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s,
+ 				    const u8 *data, size_t data_len, int noack,
+ 				    unsigned int freq, unsigned int wait)
+diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
+index 5c08d4a19..68f4d2dbe 100644
+--- a/wpa_supplicant/events.c
++++ b/wpa_supplicant/events.c
+@@ -4187,6 +4187,20 @@ out:
+ 	return wpa_sm_set_mlo_params(wpa_s->wpa, &wpa_mlo);
+ }
+ 
++static void wpa_supplicant_tx_queue_params(struct wpa_supplicant *wpa_s){
++	struct hostapd_tx_queue_params *p;
++
++	for (int i = 0; i < NUM_TX_QUEUES; i++){
++		p = &wpa_s->conf->tx_queue[i];
++		if(wpa_drv_set_tx_queue_params(wpa_s, i, p->aifs,
++						      p->cwmin, p->cwmax,
++						      p->burst)) {
++			wpa_printf(MSG_DEBUG, "Failed to set TX queue "
++				   "parameters for queue %d.", i);
++			/* Continue anyway */
++		}
++	}
++}
+ 
+ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
+ 				       union wpa_event_data *data)
+@@ -4516,6 +4530,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
+ 
+ 	if (wpa_s->current_ssid && wpa_s->current_ssid->enable_4addr_mode)
+ 		wpa_supplicant_set_4addr_mode(wpa_s);
++
++	wpa_supplicant_tx_queue_params(wpa_s);
+ }
+ 
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0045-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0045-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch
deleted file mode 100644
index 5ef55c0..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0045-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch
+++ /dev/null
@@ -1,189 +0,0 @@
-From cc4ace547c0b0ebef084e9634f729267b6c89f08 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Feb 2023 10:51:47 +0800
-Subject: [PATCH 045/104] mtk: hostapd: Add sta-assisted DFS state update
- mechanism
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c                       | 20 ++++++++++++++++++++
- src/ap/dfs.h                       |  3 +++
- src/ap/drv_callbacks.c             | 28 ++++++++++++++++++++++++++++
- src/common/wpa_ctrl.h              |  1 +
- src/drivers/driver.h               | 14 ++++++++++++++
- src/drivers/driver_nl80211_event.c |  6 ++++++
- src/drivers/nl80211_copy.h         |  6 ++++++
- 7 files changed, 78 insertions(+)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index c703d2fb8..3e036441b 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1522,6 +1522,26 @@ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
- }
- 
- 
-+int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
-+				 int ht_enabled, int chan_offset, int chan_width,
-+				 int cf1, int cf2, u32 state)
-+{
-+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_STA_UPDATE
-+		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d state=%s",
-+		freq, ht_enabled, chan_offset, chan_width, cf1, cf2,
-+		(state == HOSTAPD_CHAN_DFS_AVAILABLE) ? "available" : "usable");
-+
-+	/* Proceed only if DFS is not offloaded to the driver */
-+	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
-+		return 0;
-+
-+	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
-+		      cf1, cf2, state);
-+
-+	return 0;
-+}
-+
-+
- int hostapd_is_dfs_required(struct hostapd_iface *iface)
- {
- 	int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res;
-diff --git a/src/ap/dfs.h b/src/ap/dfs.h
-index c2556d2d9..25ba29ca1 100644
---- a/src/ap/dfs.h
-+++ b/src/ap/dfs.h
-@@ -30,6 +30,9 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
- int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
- 			     int ht_enabled,
- 			     int chan_offset, int chan_width, int cf1, int cf2);
-+int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
-+				 int ht_enabled, int chan_offset, int chan_width,
-+				 int cf1, int cf2, u32 state);
- int hostapd_is_dfs_required(struct hostapd_iface *iface);
- int hostapd_is_dfs_chan_available(struct hostapd_iface *iface);
- int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index e8796f709..caa171474 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -2226,6 +2226,24 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
- 			      radar->cf1, radar->cf2);
- }
- 
-+static void hostapd_event_dfs_sta_cac_skipped(struct hostapd_data *hapd,
-+					      struct dfs_event *radar)
-+{
-+	wpa_printf(MSG_DEBUG, "DFS CAC skipped (by STA) on %d MHz", radar->freq);
-+	hostapd_dfs_sta_update_state(hapd->iface, radar->freq, radar->ht_enabled,
-+				     radar->chan_offset, radar->chan_width,
-+				     radar->cf1, radar->cf2, HOSTAPD_CHAN_DFS_AVAILABLE);
-+}
-+
-+static void hostapd_event_dfs_sta_cac_expired(struct hostapd_data *hapd,
-+					      struct dfs_event *radar)
-+{
-+	wpa_printf(MSG_DEBUG, "DFS CAC expired (by STA) on %d MHz", radar->freq);
-+	hostapd_dfs_sta_update_state(hapd->iface, radar->freq, radar->ht_enabled,
-+				     radar->chan_offset, radar->chan_width,
-+				     radar->cf1, radar->cf2, HOSTAPD_CHAN_DFS_USABLE);
-+}
-+
- #endif /* NEED_AP_MLME */
- 
- 
-@@ -2592,6 +2610,16 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
- 		hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
- 		hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
- 		break;
-+	case EVENT_DFS_STA_CAC_SKIPPED:
-+		if (!data)
-+			break;
-+		hostapd_event_dfs_sta_cac_skipped(hapd, &data->dfs_event);
-+		break;
-+	case EVENT_DFS_STA_CAC_EXPIRED:
-+		if (!data)
-+			break;
-+		hostapd_event_dfs_sta_cac_expired(hapd, &data->dfs_event);
-+		break;
- 	case EVENT_CHANNEL_LIST_CHANGED:
- 		/* channel list changed (regulatory?), update channel list */
- 		/* TODO: check this. hostapd_get_hw_features() initializes
-diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
-index c5bb9abd7..88ad54d6f 100644
---- a/src/common/wpa_ctrl.h
-+++ b/src/common/wpa_ctrl.h
-@@ -383,6 +383,7 @@ extern "C" {
- #define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED "
- #define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED "
- #define DFS_EVENT_PRE_CAC_EXPIRED "DFS-PRE-CAC-EXPIRED "
-+#define DFS_EVENT_STA_UPDATE "DFS-STA-UPDATE "
- 
- #define AP_CSA_FINISHED "AP-CSA-FINISHED "
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 6eeb9c22e..dbd0137ac 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5900,6 +5900,20 @@ enum wpa_event_type {
- 	 * EVENT_LINK_RECONFIG - Notification that AP links removed
- 	 */
- 	EVENT_LINK_RECONFIG,
-+
-+	/**
-+	 * EVENT_DFS_STA_CAC_SKIPPED - Notification that CAC has been skipped
-+	 *
-+	 * The channel in the notification is now marked as available.
-+	 */
-+	EVENT_DFS_STA_CAC_SKIPPED,
-+
-+	/**
-+	 * EVENT_DFS_STA_CAC_EXPIRED - Notification that CAC has expired
-+	 *
-+	 * The channel in the notification is now marked as usable.
-+	 */
-+	EVENT_DFS_STA_CAC_EXPIRED,
- };
- 
- 
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 4a12d749c..7889930a0 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -2529,6 +2529,12 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
- 	case NL80211_RADAR_CAC_STARTED:
- 		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
- 		break;
-+	case NL80211_RADAR_STA_CAC_SKIPPED:
-+		wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
-+		break;
-+	case NL80211_RADAR_STA_CAC_EXPIRED:
-+		wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_EXPIRED, &data);
-+		break;
- 	default:
- 		wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
- 			   "received", event_type);
-diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
-index dced2c49d..8917d565b 100644
---- a/src/drivers/nl80211_copy.h
-+++ b/src/drivers/nl80211_copy.h
-@@ -6699,6 +6699,10 @@ enum nl80211_smps_mode {
-  *	applicable for ETSI dfs domain where pre-CAC is valid for ever.
-  * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
-  *	should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
-+ * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
-+ *	when receiving CSA/assoc resp
-+ * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
-+ *	when STA is disconnected or leaving the channel
-  */
- enum nl80211_radar_event {
- 	NL80211_RADAR_DETECTED,
-@@ -6707,6 +6711,8 @@ enum nl80211_radar_event {
- 	NL80211_RADAR_NOP_FINISHED,
- 	NL80211_RADAR_PRE_CAC_EXPIRED,
- 	NL80211_RADAR_CAC_STARTED,
-+	NL80211_RADAR_STA_CAC_SKIPPED,
-+	NL80211_RADAR_STA_CAC_EXPIRED,
- };
- 
- /**
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0045-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0045-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch
new file mode 100644
index 0000000..41a4af7
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0045-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch
@@ -0,0 +1,27 @@
+From 7d38f0de94d325b00fecb4cd2888d0a0695446c2 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 1 Sep 2023 15:31:24 +0800
+Subject: [PATCH 045/126] mtk: hostapd: avoid color switch when beacon is not
+ set
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/hostapd.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 4549aecb2..7da5c3afa 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4815,7 +4815,7 @@ void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap)
+ {
+ 	struct os_reltime now;
+ 
+-	if (hapd->cca_in_progress)
++	if (hapd->cca_in_progress || !hapd->beacon_set_done)
+ 		return;
+ 
+ 	if (os_get_reltime(&now))
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0046-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0046-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch
new file mode 100644
index 0000000..c2a8853
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0046-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch
@@ -0,0 +1,30 @@
+From 1d5789bf2fb508516d7a650bf282a12f39eccda3 Mon Sep 17 00:00:00 2001
+From: mtk20656 <chank.chen@mediatek.com>
+Date: Wed, 13 Sep 2023 19:29:51 +0800
+Subject: [PATCH 046/126] mtk: hostapd: 6g bss connect do not consider ht
+ operation
+
+Signed-off-by: mtk20656 <chank.chen@mediatek.com>
+---
+ src/ap/ieee802_11.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+ mode change 100755 => 100644 src/ap/ieee802_11.c
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+old mode 100755
+new mode 100644
+index 258ab7943..ee698fa4a
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -5667,7 +5667,7 @@ static void handle_assoc(struct hostapd_data *hapd,
+ 			set_beacon = true;
+ 	}
+ 
+-	if (update_ht_state(hapd, sta) > 0)
++	if (!is_6ghz_op_class(hapd->iconf->op_class) && update_ht_state(hapd, sta) > 0)
+ 		set_beacon = true;
+ 
+ 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0046-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0046-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch
deleted file mode 100644
index 7d905c5..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0046-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch
+++ /dev/null
@@ -1,60 +0,0 @@
-From 21476005ab6b517c7765fec7c3d6c3383c5f44f4 Mon Sep 17 00:00:00 2001
-From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
-Date: Fri, 3 Mar 2023 12:45:42 +0800
-Subject: [PATCH 046/104] mtk: hostapd: Mark DFS channel as available for CSA.
-
----
- hostapd/ctrl_iface.c   | 10 ++++++++++
- hostapd/hostapd_cli.c  |  2 +-
- src/ap/ctrl_iface_ap.c |  1 +
- 3 files changed, 12 insertions(+), 1 deletion(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 5f6278ecd..052588da4 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -2776,6 +2776,16 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
- 		break;
- 	}
- 
-+	if (settings.freq_params.radar_background) {
-+		hostapd_dfs_sta_update_state(iface,
-+			settings.freq_params.freq,
-+			settings.freq_params.ht_enabled,
-+			settings.freq_params.sec_channel_offset,
-+			bandwidth, settings.freq_params.center_freq1,
-+			settings.freq_params.center_freq2,
-+			HOSTAPD_CHAN_DFS_AVAILABLE);
-+	}
-+
- 	if (settings.freq_params.center_freq1)
- 		dfs_range += hostapd_is_dfs_overlap(
- 			iface, bandwidth, settings.freq_params.center_freq1);
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 847f867ab..da9c0f931 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1775,7 +1775,7 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 	  "<addr> = send QoS Map Configure frame" },
- 	{ "chan_switch", hostapd_cli_cmd_chan_switch, NULL,
- 	  "<cs_count> <freq> [sec_channel_offset=] [center_freq1=]\n"
--	  "  [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n"
-+	  "  [center_freq2=] [bandwidth=] [blocktx] [ht|vht] [skip_cac]\n"
- 	  "  = initiate channel switch announcement" },
- 	{ "notify_cw_change", hostapd_cli_cmd_notify_cw_change, NULL,
- 	  "<channel_width> = 0 - 20 MHz, 1 - 40 MHz, 2 - 80 MHz, 3 - 160 MHz" },
-diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index a2f89260c..b92311e32 100644
---- a/src/ap/ctrl_iface_ap.c
-+++ b/src/ap/ctrl_iface_ap.c
-@@ -1117,6 +1117,7 @@ int hostapd_parse_csa_settings(const char *pos,
- 	settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
- 	settings->freq_params.he_enabled = !!os_strstr(pos, " he");
- 	settings->freq_params.eht_enabled = !!os_strstr(pos, " eht");
-+	settings->freq_params.radar_background = !!os_strstr(pos, " skip_cac");
- 	settings->block_tx = !!os_strstr(pos, " blocktx");
- #undef SET_CSA_SETTING
- #undef SET_CSA_SETTING_EXT
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0047-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0047-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch
new file mode 100644
index 0000000..510b699
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0047-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch
@@ -0,0 +1,96 @@
+From df41420a192985b4a8c535968dbbf0ee07cc8c73 Mon Sep 17 00:00:00 2001
+From: "fancy.liu" <fancy.liu@mediatek.com>
+Date: Sun, 8 Oct 2023 11:50:06 +0800
+Subject: [PATCH 047/126] mtk: hostapd: Add ACS chanlist info in get_config
+
+This patch is used to add ACS chanlist info displaying
+for upper layer application obtaining.
+
+Command format:
+hostapd_cli -i phy0-ap0 get_config
+
+Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 59 ++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 59 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 68b83bf6e..53924b265 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -1059,6 +1059,7 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
+ {
+ 	int ret;
+ 	char *pos, *end;
++	int i;
+ 
+ 	pos = buf;
+ 	end = buf + buflen;
+@@ -1238,6 +1239,64 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
+ 		pos += ret;
+ 	}
+ 
++	/* dump chanlist */
++	if (hapd->iface->conf->acs_ch_list.num > 0) {
++		ret = os_snprintf(pos, end - pos, "chanlist=");
++		if (os_snprintf_error(end - pos, ret))
++			return pos - buf;
++		pos += ret;
++
++		for (i = 0; i < hapd->iface->conf->acs_ch_list.num; i++) {
++			if (i > 0) {
++				ret = os_snprintf(pos, end - pos, ", ");
++				if (os_snprintf_error(end - pos, ret))
++					return pos - buf;
++				pos += ret;
++			}
++
++			ret = os_snprintf(pos, end - pos, "%d-%d",
++				hapd->iface->conf->acs_ch_list.range[i].min,
++				hapd->iface->conf->acs_ch_list.range[i].max);
++			if (os_snprintf_error(end - pos, ret))
++				return pos - buf;
++			pos += ret;
++		}
++
++		ret = os_snprintf(pos, end - pos, "\n");
++		if (os_snprintf_error(end - pos, ret))
++			return pos - buf;
++		pos += ret;
++	}
++
++	/* dump freqlist */
++	if (hapd->iface->conf->acs_freq_list.num > 0) {
++		ret = os_snprintf(pos, end - pos, "freqlist=");
++		if (os_snprintf_error(end - pos, ret))
++			return pos - buf;
++		pos += ret;
++
++		for (i = 0; i < hapd->iface->conf->acs_freq_list.num; i++) {
++			if (i > 0) {
++				ret = os_snprintf(pos, end - pos, ", ");
++				if (os_snprintf_error(end - pos, ret))
++					return pos - buf;
++				pos += ret;
++			}
++
++			ret = os_snprintf(pos, end - pos, "%d-%d",
++				hapd->iface->conf->acs_freq_list.range[i].min,
++				hapd->iface->conf->acs_freq_list.range[i].max);
++			if (os_snprintf_error(end - pos, ret))
++				return pos - buf;
++			pos += ret;
++		}
++
++		ret = os_snprintf(pos, end - pos, "\n");
++		if (os_snprintf_error(end - pos, ret))
++			return pos - buf;
++		pos += ret;
++	}
++
+ 	return pos - buf;
+ }
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0047-mtk-hostapd-Add-available-color-bitmap.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0047-mtk-hostapd-Add-available-color-bitmap.patch
deleted file mode 100644
index 9f3c55b..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0047-mtk-hostapd-Add-available-color-bitmap.patch
+++ /dev/null
@@ -1,476 +0,0 @@
-From 50ceb0f2eb9b542ab115ed79fd2d68d46e9e03a0 Mon Sep 17 00:00:00 2001
-From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Date: Thu, 26 Jan 2023 09:16:00 +0800
-Subject: [PATCH 047/104] mtk: hostapd: Add available color bitmap
-
-Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
----
- hostapd/ctrl_iface.c              |  74 +++++++++++
- hostapd/hostapd_cli.c             |  18 +++
- src/ap/ap_drv_ops.c               |  10 +-
- src/ap/ap_drv_ops.h               |   2 +
- src/common/mtk_vendor.h           |  11 ++
- src/drivers/driver.h              |   8 ++
- src/drivers/driver_nl80211.c      | 198 +++++++++++++++++++++++++++++-
- src/drivers/driver_nl80211.h      |   1 +
- src/drivers/driver_nl80211_capa.c |   3 +
- 9 files changed, 323 insertions(+), 2 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 052588da4..7b83bdd4f 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4257,6 +4257,76 @@ hostapd_ctrl_iface_get_amsdu(struct hostapd_data *hapd, char *buf,
- 	return ret;
- }
- 
-+static int
-+hostapd_ctrl_iface_get_bss_color(struct hostapd_data *hapd, char *buf,
-+		size_t buflen)
-+{
-+	int ret;
-+	char *pos, *end;
-+	int i;
-+
-+	pos = buf;
-+	end = buf + buflen;
-+
-+	if (hapd->iface->conf->he_op.he_bss_color_disabled)
-+		ret = os_snprintf(buf, buflen, "BSS Color disabled\n");
-+	else
-+		ret = os_snprintf(buf, buflen, "BSS Color=%u\n",
-+				  hapd->iface->conf->he_op.he_bss_color);
-+
-+	pos += ret;
-+
-+	return pos - buf;
-+}
-+
-+
-+static int
-+hostapd_ctrl_iface_get_aval_color_bmp(struct hostapd_data *hapd, char *buf,
-+		size_t buflen)
-+{
-+	int ret;
-+	char *pos, *end;
-+	int i;
-+	u64 aval_color_bmp = 0;
-+
-+	hostapd_drv_get_aval_bss_color_bmp(hapd, &aval_color_bmp);
-+	hapd->color_collision_bitmap = ~aval_color_bmp;
-+
-+	pos = buf;
-+	end = buf + buflen;
-+
-+	ret = os_snprintf(buf, buflen,
-+			"available color bitmap=0x%llx\n",
-+			aval_color_bmp);
-+	if (os_snprintf_error(end - pos, ret))
-+		return pos - buf;
-+	pos += ret;
-+
-+	for (i = 0; i < HE_OPERATION_BSS_COLOR_MAX; i++) {
-+		int bit = !!((aval_color_bmp >> i) & 1LLU);
-+
-+		if (i % 8 == 0) {
-+			ret = os_snprintf(pos, end - pos, "%2d: ", i);
-+			if (os_snprintf_error(end - pos, ret))
-+				return pos - buf;
-+			pos += ret;
-+		}
-+
-+		ret = os_snprintf(pos, end - pos, "%d ", bit);
-+		if (os_snprintf_error(end - pos, ret))
-+			return pos - buf;
-+		pos += ret;
-+
-+		if (i % 8 == 7) {
-+			ret = os_snprintf(pos, end - pos, "\n");
-+			if (os_snprintf_error(end - pos, ret))
-+				return pos - buf;
-+			pos += ret;
-+		}
-+	}
-+	return pos - buf;
-+}
-+
- 
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
-@@ -4878,6 +4948,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 		reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
- 	} else if (os_strncmp(buf, "GET_AMSDU", 9) == 0) {
- 		reply_len = hostapd_ctrl_iface_get_amsdu(hapd, reply, reply_size);
-+	} else if (os_strncmp(buf, "GET_BSS_COLOR", 13) == 0) {
-+		reply_len = hostapd_ctrl_iface_get_bss_color(hapd, reply, reply_size);
-+	} else if (os_strncmp(buf, "AVAL_COLOR_BMP", 14) == 0) {
-+		reply_len = hostapd_ctrl_iface_get_aval_color_bmp(hapd, reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index da9c0f931..865c11432 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1658,6 +1658,20 @@ static int hostapd_cli_cmd_reload_rxkhs(struct wpa_ctrl *ctrl, int argc,
- #endif /* CONFIG_IEEE80211R_AP */
- 
- 
-+static int hostapd_cli_cmd_get_bss_color(struct wpa_ctrl *ctrl, int argc,
-+					  char *argv[])
-+{
-+	return wpa_ctrl_command(ctrl, "GET_BSS_COLOR");
-+}
-+
-+
-+static int hostapd_cli_cmd_get_aval_color_bmp(struct wpa_ctrl *ctrl, int argc,
-+					  char *argv[])
-+{
-+	return wpa_ctrl_command(ctrl, "AVAL_COLOR_BMP");
-+}
-+
-+
- #ifdef ANDROID
- static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
- {
-@@ -1897,6 +1911,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 	{ "get_rxkhs", hostapd_cli_cmd_get_rxkhs, NULL,
- 	  "= get R0KHs and R1KHs" },
- #endif /* CONFIG_IEEE80211R_AP */
-+	{ "get_bss_color", hostapd_cli_cmd_get_bss_color, NULL,
-+	  "= get current BSS color" },
-+	{ "get_color_bmp", hostapd_cli_cmd_get_aval_color_bmp, NULL,
-+	  "= get available BSS color bitmap" },
- #ifdef ANDROID
- 	{ "driver", hostapd_cli_cmd_driver, NULL,
- 	  "<driver sub command> [<hex formatted data>] = send driver command data" },
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 23228a8d2..cabcd47af 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1321,4 +1321,12 @@ int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu)
- 	if (!hapd->driver || !hapd->driver->amsdu_dump)
- 		return 0;
- 	return hapd->driver->amsdu_dump(hapd->drv_priv, amsdu);
--}
-\ No newline at end of file
-+}
-+
-+int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_color_bmp)
-+{
-+	if (!hapd->driver || !hapd->driver->get_aval_color_bmp ||
-+	    hapd->iface->conf->he_op.he_bss_color_disabled)
-+		return 0;
-+	return hapd->driver->get_aval_color_bmp(hapd->drv_priv, aval_color_bmp);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index f3a044557..9da2b0049 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -160,6 +160,8 @@ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
- int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
-+int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd,
-+				       u64 *aval_color_bmp);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index ace993bc8..e27fe69b3 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -15,6 +15,7 @@ enum mtk_nl80211_vendor_subcmds {
- 	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
- 	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
- 	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
-+	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
- };
- 
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -256,6 +257,16 @@ ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
- 	[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
- };
- 
-+enum mtk_vendor_attr_bss_color_ctrl {
-+	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL,
-+	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
-+};
- 
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index dbd0137ac..6b6317bfa 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5273,6 +5273,14 @@ struct wpa_driver_ops {
- 	 */
- 	int (*amsdu_ctrl)(void *priv, u8 amsdu);
- 	int (*amsdu_dump)(void *priv, u8 *amsdu);
-+
-+	/**
-+	 * get_aval_color_bmp - get available BSS color bitmap
-+	 * @priv: Private driver interface data
-+	 * @aval_color_bmp: available bss color bitmap
-+	 *
-+	 */
-+	int (*get_aval_color_bmp)(void *priv, u64 *aval_color_bmp);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index eca2ff077..4c98e8ab3 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13153,7 +13153,6 @@ static void nl80211_parse_btm_candidate_info(struct candidate_list *candidate,
- 		   num, MAC2STR(candidate->bssid), buf);
- }
- 
--
- static int
- nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg)
- {
-@@ -14608,6 +14607,202 @@ fail:
- 	return -ENOBUFS;
- }
- 
-+static int nl80211_get_aval_color_bmp_handler(struct nl_msg *msg, void *arg)
-+{
-+	u64 *aval_color_bmp = arg;
-+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX + 1];
-+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+	struct nlattr *nl_vend, *attr;
-+
-+	static const struct nla_policy
-+	bss_color_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL + 1] = {
-+		[MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP] = { .type = NLA_U64 },
-+	};
-+
-+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+			genlmsg_attrlen(gnlh, 0), NULL);
-+
-+	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+	if (!nl_vend)
-+		return NL_SKIP;
-+
-+	nla_parse(tb_vendor, MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX,
-+			nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+	*aval_color_bmp = nla_get_u64(tb_vendor[MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP]);
-+
-+	return 0;
-+}
-+
-+static int nl80211_get_aval_color_bmp(void *priv, u64 *aval_color_bmp)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *attr;
-+	int ret;
-+
-+	if (!drv->mtk_bss_color_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support BSS COLOR vendor cmd");
-+		return 0;
-+	}
-+
-+	if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL))
-+		return -ENOBUFS;
-+
-+	attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+	if (!attr) {
-+		nlmsg_free(msg);
-+		return -1;
-+	}
-+
-+	nla_nest_end(msg, attr);
-+
-+	ret = send_and_recv_resp(drv, msg, nl80211_get_aval_color_bmp_handler, aval_color_bmp);
-+
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to send BSS COLOR vendor cmd. ret=%d (%s) ",
-+			   ret, strerror(-ret));
-+	}
-+	return ret;
-+}
-+
-+static int nl80211_ap_wireless(void *priv, u8 sub_vendor_id, int value)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_wireless_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting ap wireless control");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+	if (!data)
-+		goto fail;
-+
-+	if (sub_vendor_id == MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE)
-+		nla_put_u16(msg, sub_vendor_id, (u16) value);
-+	else
-+		nla_put_u8(msg, sub_vendor_id, (u8) value);
-+
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_cmd(drv, msg);
-+	if (ret)
-+		wpa_printf(MSG_ERROR, "Failed to set ap_wireless. ret=%d (%s)", ret, strerror(-ret));
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
-+static int nl80211_ap_rfeatures(void *priv, u8 sub_vendor_id, int value)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_rfeatures_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting ap rfeatures control");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+	if (!data)
-+		goto fail;
-+
-+	nla_put_u8(msg, sub_vendor_id, (u8) value);
-+
-+	nla_nest_end(msg, data);
-+
-+	ret = send_and_recv_cmd(drv, msg);
-+	if (ret)
-+		wpa_printf(MSG_ERROR, "Failed to set rf_features. ret=%d (%s)", ret, strerror(-ret));
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
-+static int nl80211_ap_trigtype(void *priv, u8 enable, u8 type)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data, *data2;
-+	int ret;
-+
-+	if (!drv->mtk_rfeatures_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting ap rfeatures control");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+	if (!data)
-+		goto fail;
-+
-+	data2 = nla_nest_start(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG);
-+	if (!data2)
-+		goto fail;
-+
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN, enable);
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE, type);
-+
-+	nla_nest_end(msg, data2);
-+	nla_nest_end(msg, data);
-+
-+	ret = send_and_recv_cmd(drv, msg);
-+	if (ret)
-+		wpa_printf(MSG_ERROR, "Failed to set trig_type. ret=%d (%s)", ret, strerror(-ret));
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
- 	.desc = "Linux nl80211/cfg80211",
-@@ -14779,4 +14974,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.ibf_dump = nl80211_ibf_dump,
- 	.amsdu_ctrl = nl80211_enable_amsdu,
- 	.amsdu_dump = nl80211_dump_amsdu,
-+	.get_aval_color_bmp = nl80211_get_aval_color_bmp,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 5aa813e26..5b4d45567 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -205,6 +205,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int mtk_3wire_vendor_cmd_avail:1;
- 	unsigned int mtk_ibf_vendor_cmd_avail:1;
- 	unsigned int mtk_wireless_vendor_cmd_avail:1;
-+	unsigned int mtk_bss_color_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 474d4e273..a7df2d172 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1156,6 +1156,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL:
- 					drv->mtk_wireless_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
-+					drv->mtk_bss_color_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0048-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0048-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch
new file mode 100644
index 0000000..b7753e8
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0048-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch
@@ -0,0 +1,43 @@
+From 1d4221d2dd26e3324617c390ccb117f59b894dde Mon Sep 17 00:00:00 2001
+From: mtk25255 <rohit.kamat@mediatek.com>
+Date: Thu, 12 Oct 2023 14:29:23 +0800
+Subject: [PATCH 048/126] mtk: hostapd: Fix RSNXE Interop issue with STA
+
+---
+ src/ap/ieee802_11.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index ee698fa4a..4581ae1d4 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -5370,6 +5370,7 @@ static void handle_assoc(struct hostapd_data *hapd,
+ 	int omit_rsnxe = 0;
+ 	bool set_beacon = false;
+ 	bool mld_addrs_not_translated = false;
++	bool sae_pk = false;
+ 
+ 	if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
+ 				      sizeof(mgmt->u.assoc_req))) {
+@@ -5615,7 +5616,17 @@ static void handle_assoc(struct hostapd_data *hapd,
+ 	if (resp != WLAN_STATUS_SUCCESS)
+ 		goto fail;
+ 	omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
+-
++#ifdef CONFIG_SAE_PK
++	sae_pk = hostapd_sae_pk_in_use(hapd->conf);
++#endif /* CONFIG_SAE_PK */
++	if (omit_rsnxe) {
++		if (!reassoc && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
++				(hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
++				 hapd->conf->sae_pwe == SAE_PWE_BOTH || sae_pk ||
++				 wpa_key_mgmt_sae_ext_key(hapd->conf->wpa_key_mgmt))) {
++			omit_rsnxe = 0;
++		}
++	}
+ 	if (hostapd_get_aid(hapd, sta) < 0) {
+ 		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ 			       HOSTAPD_LEVEL_INFO, "No room for more AIDs");
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0048-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0048-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch
deleted file mode 100644
index e6f30ed..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0048-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch
+++ /dev/null
@@ -1,210 +0,0 @@
-From 1b8fc72bfd653ce3ef422e86617b1821948f4805 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Mar 2023 16:08:30 +0800
-Subject: [PATCH 048/104] mtk: hostapd: Fix ZWDFS issue in BW 160
-
-When background radar is enabled and bandwidth is set to 160, AP will
-fail to startup due to the lack of non-DFS channel.
-Under this circumstance, AP should perform CAC itself, and the background
-chain could also perform CAC simultaneously.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c | 98 ++++++++++++++++++++++++++++++++++++++++++----------
- 1 file changed, 79 insertions(+), 19 deletions(-)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 3e036441b..f5794753e 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -69,15 +69,22 @@ static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
- static int dfs_channel_available(struct hostapd_channel_data *chan,
- 				 enum dfs_channel_type type)
- {
-+	int dfs_status = chan->flag & HOSTAPD_CHAN_DFS_MASK;
-+
-+	if (chan->flag & HOSTAPD_CHAN_DISABLED)
-+		return -1;
-+
- 	if (type == DFS_NO_CAC_YET) {
- 		/* Select only radar channel where CAC has not been
- 		 * performed yet
- 		 */
--		if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
--		    (chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
--		     HOSTAPD_CHAN_DFS_USABLE)
-+		if (!(chan->flag & HOSTAPD_CHAN_RADAR))
-+			return 0;
-+
-+		if (dfs_status == HOSTAPD_CHAN_DFS_USABLE)
- 			return 1;
--		return 0;
-+
-+		return -1;
- 	}
- 
- 	/*
-@@ -86,16 +93,14 @@ static int dfs_channel_available(struct hostapd_channel_data *chan,
- 	 * channel for CSA, unless they are available for immediate use.
- 	 */
- 	if (type == DFS_AVAILABLE && (chan->flag & HOSTAPD_CHAN_RADAR) &&
--	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
--	     HOSTAPD_CHAN_DFS_AVAILABLE))
--		return 0;
-+	    (dfs_status != HOSTAPD_CHAN_DFS_AVAILABLE))
-+		return -1;
- 
--	if (chan->flag & HOSTAPD_CHAN_DISABLED)
--		return 0;
- 	if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
--	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
--	     HOSTAPD_CHAN_DFS_UNAVAILABLE))
--		return 0;
-+	    ((dfs_status == HOSTAPD_CHAN_DFS_UNAVAILABLE) ||
-+	    (dfs_status == HOSTAPD_CHAN_DFS_UNKNOWN)))
-+		return -1;
-+
- 	return 1;
- }
- 
-@@ -167,7 +172,7 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
- 				    enum dfs_channel_type type)
- {
- 	struct hostapd_channel_data *first_chan, *chan;
--	int i;
-+	int i, available = 0, ret = 0;
- 	u32 bw = num_chan_to_bw(num_chans);
- 
- 	if (first_chan_idx + num_chans > mode->num_channels) {
-@@ -203,14 +208,17 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
- 			return 0;
- 		}
- 
--		if (!dfs_channel_available(chan, type)) {
-+		ret = dfs_channel_available(chan, type);
-+		if (ret < 0) {
- 			wpa_printf(MSG_DEBUG, "DFS: channel not available %d",
- 				   first_chan->freq + i * 20);
- 			return 0;
- 		}
-+
-+		available |= ret;
- 	}
- 
--	return 1;
-+	return available;
- }
- 
- 
-@@ -838,8 +846,12 @@ static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
-  */
- int hostapd_handle_dfs(struct hostapd_iface *iface)
- {
-+	struct hostapd_channel_data *channel;
- 	int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
--	int skip_radar = 0;
-+	int sec = 0, skip_radar = 0;
-+	u8 cf1 = 0, cf2 = 0;
-+	bool use_radar_background = dfs_use_radar_background(iface);
-+	enum dfs_channel_type channel_type = DFS_NO_CAC_YET;
- 
- 	if (is_6ghz_freq(iface->freq))
- 		return 1;
-@@ -902,7 +914,7 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- 	/* Finally start CAC */
- 	hostapd_set_state(iface, HAPD_IFACE_DFS);
- 	wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz%s", iface->freq,
--		   dfs_use_radar_background(iface) ? " (background)" : "");
-+		   use_radar_background ? " (background)" : "");
- 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
- 		"freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
- 		iface->freq,
-@@ -912,6 +924,16 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- 		hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
- 		iface->dfs_cac_ms / 1000);
- 
-+	if (use_radar_background) {
-+		channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, DFS_AVAILABLE);
-+		/*
-+		 * AP cannot get any random available channel.
-+		 * Let AP and dedicated radar chain both perform CAC.
-+		 */
-+		if (!channel)
-+			use_radar_background = false;
-+	}
-+
- 	res = hostapd_start_dfs_cac(
- 		iface, iface->conf->hw_mode, iface->freq, iface->conf->channel,
- 		iface->conf->ieee80211n, iface->conf->ieee80211ac,
-@@ -920,14 +942,14 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- 		hostapd_get_oper_chwidth(iface->conf),
- 		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
- 		hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
--		dfs_use_radar_background(iface));
-+		use_radar_background);
- 
- 	if (res) {
- 		wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
- 		return -1;
- 	}
- 
--	if (dfs_use_radar_background(iface)) {
-+	if (use_radar_background) {
- 		/* Cache background radar parameters. */
- 		iface->radar_background.channel = iface->conf->channel;
- 		iface->radar_background.secondary_channel =
-@@ -948,6 +970,35 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- 
- 		iface->radar_background.temp_ch = 1;
- 		return 1;
-+	} else if (dfs_use_radar_background(iface)) {
-+		if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
-+			channel_type = DFS_ANY_CHANNEL;
-+
-+		channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, channel_type);
-+
-+		if (!channel ||
-+		    (channel->chan == iface->conf->channel &&
-+		    cf1 == hostapd_get_oper_centr_freq_seg0_idx(iface->conf) &&
-+		    cf2 == hostapd_get_oper_centr_freq_seg1_idx(iface->conf))) {
-+			wpa_printf(MSG_ERROR, "Background radar could not get valid channel\n");
-+			iface->radar_background.channel = -1;
-+			return 0;
-+		}
-+
-+		hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
-+				      channel->freq, channel->chan,
-+				      iface->conf->ieee80211n,
-+				      iface->conf->ieee80211ac,
-+				      iface->conf->ieee80211ax,
-+				      iface->conf->ieee80211be,
-+				      sec, hostapd_get_oper_chwidth(iface->conf),
-+				      cf1, cf2, true);
-+
-+		iface->radar_background.channel = channel->chan;
-+		iface->radar_background.freq = channel->freq;
-+		iface->radar_background.secondary_channel = sec;
-+		iface->radar_background.centr_freq_seg0_idx = cf1;
-+		iface->radar_background.centr_freq_seg1_idx = cf2;
- 	}
- 
- 	return 0;
-@@ -1204,6 +1255,15 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
- 				hostapd_setup_interface_complete(iface, 0);
- 				iface->cac_started = 0;
- 			}
-+
-+			/*
-+			 * When background radar is enabled but the CAC completion
-+			 * is not received from the background chain.
-+			 * Then, reset radar background chain.
-+			 */
-+			if (dfs_use_radar_background(iface) &&
-+			    iface->radar_background.channel == -1)
-+				hostapd_dfs_update_background_chain(iface);
- 		}
- 	} else if (hostapd_dfs_is_background_event(iface, freq)) {
- 		iface->radar_background.cac_started = 0;
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0049-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0049-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch
deleted file mode 100644
index 8b21535..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0049-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch
+++ /dev/null
@@ -1,396 +0,0 @@
-From b9b137827e9c0584682606bd5fe1cd9f50635819 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 17 Mar 2023 16:17:14 +0800
-Subject: [PATCH 049/104] mtk: hostapd: Add vendor for CAPI certification
- commands
-
----
- hostapd/ctrl_iface.c              | 99 +++++++++++++++++++++++++++++++
- src/ap/ap_drv_ops.c               | 21 +++++++
- src/ap/ap_drv_ops.h               |  3 +
- src/common/mtk_vendor.h           | 33 +----------
- src/drivers/driver.h              | 22 +++++++
- src/drivers/driver_nl80211.c      | 55 +++++++++++++++++
- src/drivers/driver_nl80211.h      |  1 +
- src/drivers/driver_nl80211_capa.c |  3 +
- 8 files changed, 206 insertions(+), 31 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 7b83bdd4f..1154a2394 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -71,6 +71,7 @@
- #include "config_file.h"
- #include "ctrl_iface.h"
- 
-+#include "common/mtk_vendor.h"
- 
- #define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
- 
-@@ -4327,6 +4328,100 @@ hostapd_ctrl_iface_get_aval_color_bmp(struct hostapd_data *hapd, char *buf,
- 	return pos - buf;
- }
- 
-+static int
-+hostapd_ctrl_iface_ap_wireless(struct hostapd_data *hapd, char *cmd,
-+					 char *buf, size_t buflen)
-+{
-+	char *pos, *value, *config = cmd;
-+	enum mtk_vendor_attr_wireless_ctrl sub_cmd;
-+
-+	pos = os_strchr(config, '=');
-+	if (pos == NULL)
-+		return -1;
-+	*pos++ = '\0';
-+
-+	if(pos == NULL)
-+		return -1;
-+	value = pos;
-+
-+	if (os_strncmp(config, "fixed_mcs", 9) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS;
-+	else if (os_strncmp(config, "ofdma", 5) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA;
-+	else if (os_strncmp(config, "ppdu_type", 9) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE;
-+	else if (os_strncmp(config, "nusers_ofdma", 12) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA;
-+	else if (os_strncmp(config, "add_ba_req_bufsize", 18) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE;
-+	else if (os_strncmp(config, "mimo", 4) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO;
-+	else if (os_strncmp(config, "cert", 4) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT ;
-+	else if (os_strncmp(config, "amsdu", 5) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU;
-+	else if (os_strncmp(config, "rts_sigta", 9) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA;
-+	else {
-+		wpa_printf(MSG_ERROR,
-+			"Unsupported parameter %s for ap_wireless", config);
-+		return -1;
-+	}
-+
-+	if (hostapd_drv_ap_wireless(hapd, (u8) sub_cmd, atoi(value)) != 0)
-+		return -1;
-+
-+	return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+static int
-+hostapd_ctrl_iface_ap_rfeatures(struct hostapd_data *hapd, char *cmd,
-+					 char *buf, size_t buflen)
-+{
-+	char *pos, *value, *type, *config = cmd;
-+	enum mtk_vendor_attr_rfeature_ctrl sub_cmd;
-+
-+	pos = os_strchr(config, '=');
-+	if (pos == NULL)
-+		return -1;
-+	*pos++ = '\0';
-+
-+	if(pos == NULL)
-+		return -1;
-+	value = pos;
-+
-+	if (os_strncmp(config, "he_gi", 5) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI;
-+	else if (os_strncmp(config, "he_ltf", 6) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF;
-+	else if (os_strncmp(config, "trig_type", 9) == 0) {
-+		pos = os_strchr(value, ',');
-+		if (pos == NULL)
-+			return -1;
-+		*pos++ = '\0';
-+		if(pos == NULL)
-+			return -1;
-+		type = pos;
-+		goto trigtype;
-+	} else if (os_strcmp(config, "ack_policy") == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY;
-+	else {
-+		wpa_printf(MSG_ERROR,
-+			"Unsupported parameter %s for ap_rfeatures", config);
-+		return -1;
-+	}
-+
-+	if (hostapd_drv_ap_rfeatures(hapd, (u8) sub_cmd, atoi(value)) != 0)
-+		return -1;
-+	goto exit;
-+
-+trigtype:
-+	if (hostapd_drv_ap_trig_type(hapd, atoi(value), atoi(type)) != 0)
-+		return -1;
-+
-+exit:
-+	return os_snprintf(buf, buflen, "OK\n");
-+}
- 
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
-@@ -4952,6 +5047,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 		reply_len = hostapd_ctrl_iface_get_bss_color(hapd, reply, reply_size);
- 	} else if (os_strncmp(buf, "AVAL_COLOR_BMP", 14) == 0) {
- 		reply_len = hostapd_ctrl_iface_get_aval_color_bmp(hapd, reply, reply_size);
-+	} else if (os_strncmp(buf, "ap_wireless ", 12) == 0) {
-+		reply_len = hostapd_ctrl_iface_ap_wireless(hapd, buf + 12, reply, reply_size);
-+	} else if (os_strncmp(buf, "ap_rfeatures ", 13) == 0) {
-+		reply_len = hostapd_ctrl_iface_ap_rfeatures(hapd, buf + 13, reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index cabcd47af..06d71f309 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1330,3 +1330,24 @@ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_colo
- 		return 0;
- 	return hapd->driver->get_aval_color_bmp(hapd->drv_priv, aval_color_bmp);
- }
-+
-+int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
-+{
-+	if (!hapd->driver || !hapd->driver->ap_wireless)
-+		return 0;
-+	return hapd->driver->ap_wireless(hapd->drv_priv, sub_vendor_id, value);
-+}
-+
-+int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
-+{
-+	if (!hapd->driver || !hapd->driver->ap_rfeatures)
-+		return 0;
-+	return hapd->driver->ap_rfeatures(hapd->drv_priv, sub_vendor_id, value);
-+}
-+
-+int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type)
-+{
-+	if (!hapd->driver || !hapd->driver->ap_trigtype)
-+		return 0;
-+	return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 9da2b0049..c58930217 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -162,6 +162,9 @@ int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
- int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd,
- 				       u64 *aval_color_bmp);
-+int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
-+int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
-+int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index e27fe69b3..0b23c76ad 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -50,17 +50,6 @@ enum mtk_vendor_attr_edcca_dump {
- 		NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
- };
- 
--
--static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
--	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
--	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
--	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
--	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
--	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
--	[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
--	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
--};
--
- enum mtk_vendor_attr_3wire_ctrl {
- 	MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
- 
-@@ -72,10 +61,6 @@ enum mtk_vendor_attr_3wire_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
- };
- 
--static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
--	[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
--};
--
- enum mtk_vendor_attr_csi_ctrl {
- 	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
- 
-@@ -172,7 +157,7 @@ enum mtk_vendor_attr_wireless_ctrl {
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
--	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT = 9,
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
- 
- 	/* keep last */
-@@ -192,11 +177,6 @@ enum mtk_vendor_attr_wireless_dump {
- 		NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
- };
- 
--static const struct nla_policy
--wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
--	[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
--};
--
- enum mtk_vendor_attr_rfeature_ctrl {
- 	MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
- 
-@@ -206,6 +186,7 @@ enum mtk_vendor_attr_rfeature_ctrl {
- 	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
- 	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
- 	MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF,
- 
- 	/* keep last */
- 	NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
-@@ -247,16 +228,6 @@ enum mtk_vendor_attr_ibf_dump {
- 		NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
- };
- 
--static struct nla_policy
--ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
--	[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
--};
--
--static struct nla_policy
--ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
--	[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
--};
--
- enum mtk_vendor_attr_bss_color_ctrl {
- 	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 6b6317bfa..a25601c91 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5281,6 +5281,28 @@ struct wpa_driver_ops {
- 	 *
- 	 */
- 	int (*get_aval_color_bmp)(void *priv, u64 *aval_color_bmp);
-+
-+	/**
-+	* ap_wireless - set wireless command
-+	* @priv: Private driver interface data
-+	* @value: value
-+	*/
-+	int (*ap_wireless)(void *priv, u8 mode, int value);
-+
-+	/**
-+	* ap_rfeatures - set ap rf features command
-+	* @priv: Private driver interface data
-+	* @value: value
-+	*/
-+	int (*ap_rfeatures)(void *priv, u8 mode, int value);
-+
-+	/**
-+	* ap_trigtype - set trigger type
-+	* @priv: Private driver interface data
-+	* @enable: enable or disable
-+	* @type: trigger type
-+	*/
-+	int (*ap_trigtype)(void *priv, u8 enable, u8 type);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 4c98e8ab3..86e5844cd 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -91,6 +91,58 @@ static void handle_nl_debug_hook(struct nl_msg *msg, int tx)
- 	wpa_netlink_hook(tx, nlh, nlh->nlmsg_len);
- }
- 
-+static struct nla_policy
-+ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
-+	[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
-+};
-+
-+static struct nla_policy
-+ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
-+	[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
-+};
-+
-+static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
-+	[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
-+};
-+
-+static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
-+};
-+
-+static const struct nla_policy
-+wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
-+	[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
-+};
-+
-+static const struct nla_policy
-+rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG] = { .type = NLA_NESTED },
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
-+};
-+
-+static const struct nla_policy
-+wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE] = {.type = NLA_U16 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
-+};
-+
- static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
- {
- 	struct nl_sock *handle;
-@@ -14975,4 +15027,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.amsdu_ctrl = nl80211_enable_amsdu,
- 	.amsdu_dump = nl80211_dump_amsdu,
- 	.get_aval_color_bmp = nl80211_get_aval_color_bmp,
-+	.ap_wireless = nl80211_ap_wireless,
-+	.ap_rfeatures = nl80211_ap_rfeatures,
-+	.ap_trigtype = nl80211_ap_trigtype,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 5b4d45567..046991a3d 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -206,6 +206,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int mtk_ibf_vendor_cmd_avail:1;
- 	unsigned int mtk_wireless_vendor_cmd_avail:1;
- 	unsigned int mtk_bss_color_vendor_cmd_avail:1;
-+	unsigned int mtk_rfeatures_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index a7df2d172..6498eba6d 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1159,6 +1159,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
- 					drv->mtk_bss_color_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
-+					drv->mtk_rfeatures_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0049-mtk-hostapd-update-eht-operation-element.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0049-mtk-hostapd-update-eht-operation-element.patch
new file mode 100644
index 0000000..d99e601
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0049-mtk-hostapd-update-eht-operation-element.patch
@@ -0,0 +1,29 @@
+From 97f988afe8673579444a9ae90c2db48a6d7527e7 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Wed, 10 May 2023 13:11:34 +0800
+Subject: [PATCH 049/126] mtk: hostapd: update eht operation element
+
+---
+ src/ap/ieee802_11_eht.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index b935ee889..a04eb23e0 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -237,9 +237,9 @@ u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
+ 
+ 	/* TODO: Fill in appropriate EHT-MCS max Nss information */
+ 	oper->basic_eht_mcs_nss_set[0] = 0x11;
+-	oper->basic_eht_mcs_nss_set[1] = 0x00;
+-	oper->basic_eht_mcs_nss_set[2] = 0x00;
+-	oper->basic_eht_mcs_nss_set[3] = 0x00;
++	oper->basic_eht_mcs_nss_set[1] = 0x11;
++	oper->basic_eht_mcs_nss_set[2] = 0x11;
++	oper->basic_eht_mcs_nss_set[3] = 0x11;
+ 
+ 	if (!eht_oper_info_present)
+ 		return pos + elen;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0050-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0050-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch
deleted file mode 100644
index aecb14a..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0050-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch
+++ /dev/null
@@ -1,506 +0,0 @@
-From 994774c363a07fa90a7a21974b7b4a371b235673 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 12 May 2023 05:18:48 +0800
-Subject: [PATCH 050/104] mtk: hostapd: Air Monitor support in hostapd by
- vendor
-
-Signed-off-by: mtk23888 <dipanshu.mittal@mediatek.com>
----
- hostapd/ctrl_iface.c              | 113 +++++++++++++++++++
- hostapd/hostapd_cli.c             |  15 +++
- src/ap/ap_drv_ops.c               |  14 +++
- src/ap/ap_drv_ops.h               |   3 +
- src/common/mtk_vendor.h           |   8 ++
- src/drivers/driver.h              |  16 +++
- src/drivers/driver_nl80211.c      | 179 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |   1 +
- src/drivers/driver_nl80211_capa.c |   2 +
- 9 files changed, 351 insertions(+)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 1154a2394..56722384b 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4370,6 +4370,44 @@ hostapd_ctrl_iface_ap_wireless(struct hostapd_data *hapd, char *cmd,
- 
- 	if (hostapd_drv_ap_wireless(hapd, (u8) sub_cmd, atoi(value)) != 0)
- 		return -1;
-+	return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+static int
-+hostapd_ctrl_iface_set_amnt(struct hostapd_data *hapd, char *cmd,
-+					char *buf, size_t buflen)
-+{
-+	char *tmp, sta_mac[ETH_ALEN] = {0};
-+	int amnt_idx = 0;
-+
-+	tmp = strtok_r(cmd, " ", &cmd);
-+
-+	if (!tmp) {
-+		wpa_printf(MSG_ERROR, "Error in command format\n");
-+		return -1;
-+	}
-+
-+	amnt_idx = strtol(tmp, &tmp, 10);
-+
-+	if (amnt_idx < 0 || amnt_idx > 15) {
-+		wpa_printf(MSG_ERROR, "Wrong AMNT index %d\n", amnt_idx);
-+		return -1;
-+	}
-+
-+	if (!cmd) {
-+		wpa_printf(MSG_ERROR, "Error in command format\n");
-+		return -1;
-+	}
-+
-+	if (hwaddr_aton(cmd, sta_mac) < 0) {
-+		wpa_printf(MSG_ERROR, "station mac is not right.\n");
-+		return -1;
-+	}
-+
-+	if (hostapd_drv_amnt_set(hapd, amnt_idx, sta_mac)) {
-+		wpa_printf(MSG_ERROR, "Not able to set amnt index\n");
-+		return -1;
-+	}
- 
- 	return os_snprintf(buf, buflen, "OK\n");
- }
-@@ -4423,6 +4461,75 @@ exit:
- 	return os_snprintf(buf, buflen, "OK\n");
- }
- 
-+static int
-+hostapd_ctrl_iface_dump_amnt(struct hostapd_data *hapd, char *cmd,
-+				char *buf, size_t buflen)
-+{
-+	char *tmp;
-+	int amnt_idx = 0, ret = 0;
-+	struct amnt_resp_data *resp_buf;
-+	char *pos, *end;
-+	struct amnt_data *res;
-+
-+	pos = buf;
-+	end = buf + buflen;
-+
-+	tmp = strtok_r(cmd, " ", &cmd);
-+
-+	if (!tmp) {
-+		wpa_printf(MSG_ERROR, "Error in command format\n");
-+		return -1;
-+	}
-+
-+	amnt_idx = strtoul(tmp, &tmp, 0);
-+
-+	if ((amnt_idx < 0 || amnt_idx > 15) && amnt_idx != 0xff) {
-+		wpa_printf(MSG_ERROR, "Wrong AMNT index\n");
-+		return -1;
-+	}
-+
-+	if (amnt_idx == 0xff)
-+		resp_buf = (struct amnt_resp_data *) os_zalloc(AIR_MONITOR_MAX_ENTRY
-+							* sizeof(struct amnt_data) + 1);
-+	else
-+		resp_buf = (struct amnt_resp_data *) os_zalloc(sizeof(struct amnt_data) + 1);
-+
-+	if (resp_buf == NULL) {
-+		wpa_printf(MSG_ERROR, "Error in memory allocation\n");
-+		return -1;
-+	}
-+
-+	if (hostapd_drv_amnt_dump(hapd, amnt_idx, (u8 *)resp_buf)) {
-+		wpa_printf(MSG_ERROR, "Not able to set amnt index\n");
-+		os_free(resp_buf);
-+		return -1;
-+	}
-+
-+	for (int i = 0; i < resp_buf->sta_num && i < AIR_MONITOR_MAX_ENTRY; i++) {
-+		res = &resp_buf->resp_data[i];
-+		ret = os_snprintf(pos, end - pos,
-+				"[hostapd_cli] amnt_idx: %d, addr="MACSTR
-+				", rssi=%d/%d/%d/%d, last_seen=%u\n",
-+				res->idx,
-+				MAC2STR(res->addr), res->rssi[0],
-+				res->rssi[1], res->rssi[2],
-+				res->rssi[3], res->last_seen);
-+		if (os_snprintf_error(end - pos, ret)) {
-+			os_free(resp_buf);
-+			return 0;
-+		}
-+		pos = pos + ret;
-+	}
-+
-+	os_free(resp_buf);
-+
-+	if (pos == buf)
-+		return os_snprintf(buf, buflen, "Index %d is not monitored\n",
-+				amnt_idx);
-+	else
-+		return pos - buf;
-+}
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -5051,6 +5158,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 		reply_len = hostapd_ctrl_iface_ap_wireless(hapd, buf + 12, reply, reply_size);
- 	} else if (os_strncmp(buf, "ap_rfeatures ", 13) == 0) {
- 		reply_len = hostapd_ctrl_iface_ap_rfeatures(hapd, buf + 13, reply, reply_size);
-+	} else if (os_strncmp(buf, "SET_AMNT", 8) == 0) {
-+		reply_len = hostapd_ctrl_iface_set_amnt(hapd, buf+9,
-+							reply, reply_size);
-+	} else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
-+		reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
-+							reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 865c11432..12c580455 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1693,6 +1693,17 @@ static int hostapd_cli_cmd_get_amsdu(struct wpa_ctrl *ctrl, int argc,
- 	return hostapd_cli_cmd(ctrl, "GET_AMSDU", 0, NULL, NULL);
- }
- 
-+static int hostapd_cli_cmd_set_amnt(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "SET_AMNT", 2, argc, argv);
-+}
-+
-+static int hostapd_cli_cmd_dump_amnt(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "DUMP_AMNT", 1, argc, argv);
-+}
- 
- struct hostapd_cli_cmd {
- 	const char *cmd;
-@@ -1925,6 +1936,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 	  " = show iBF state (enabled/disabled)"},
- 	{ "get_amsdu", hostapd_cli_cmd_get_amsdu, NULL,
- 		" = show AMSDU state"},
-+	{ "set_amnt", hostapd_cli_cmd_set_amnt, NULL,
-+		" = Set Station index and mac to monitor"},
-+	{ "dump_amnt", hostapd_cli_cmd_dump_amnt, NULL,
-+		" = Dump RSSI of monitoring Station"},
- 	{ NULL, NULL, NULL, NULL }
- };
- 
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 06d71f309..df652b12f 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1351,3 +1351,17 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type)
- 		return 0;
- 	return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type);
- }
-+
-+int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac)
-+{
-+	if (!hapd->driver || !hapd->driver->amnt_set)
-+		return 0;
-+	return hapd->driver->amnt_set(hapd->drv_priv, amnt_idx, amnt_sta_mac);
-+}
-+
-+int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf)
-+{
-+	if (!hapd->driver || !hapd->driver->amnt_dump)
-+		return 0;
-+	return hapd->driver->amnt_dump(hapd->drv_priv, amnt_idx, amnt_dump_buf);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index c58930217..4805a2e84 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -166,6 +166,9 @@ int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int val
- int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
- int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
- 
-+int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
-+int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
-+
- #include "drivers/driver.h"
- 
- int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 0b23c76ad..dd1ca2164 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -258,10 +258,18 @@ struct csi_data {
- 	u16 rx_idx;
- };
- 
-+#define AIR_MONITOR_MAX_ENTRY 16
-+
- struct amnt_data {
- 	u8 idx;
- 	u8 addr[ETH_ALEN];
- 	s8 rssi[4];
- 	u32 last_seen;
- };
-+
-+struct amnt_resp_data {
-+	u8 sta_num;
-+	struct amnt_data resp_data[0];
-+};
-+
- #endif /* MTK_VENDOR_H */
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index a25601c91..dd9c33201 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5303,6 +5303,22 @@ struct wpa_driver_ops {
- 	* @type: trigger type
- 	*/
- 	int (*ap_trigtype)(void *priv, u8 enable, u8 type);
-+
-+	/**
-+	* amnt_set - add/delete station from monitoring
-+	* @priv: Private driver interface data
-+	* @amnt_idx: Monitor Index
-+	* @amnt_sta_mac: station mac address
-+	*/
-+	int (*amnt_set)(void *priv, u8 amnt_idx, u8 *amnt_sta_mac);
-+
-+	/**
-+	* amnt_dump - Dump particular/ all station
-+	* @priv: Private driver interface data
-+	* @amnt_idx: Monitor Index
-+	* @amnt_dump_buf: Buffer to print
-+	*/
-+	int (*amnt_dump)(void *priv, u8 amnt_idx, u8 *amnt_dump_buf);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 86e5844cd..a2a6807f4 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -143,6 +143,19 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
- 	[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
- };
- 
-+static struct nla_policy
-+amnt_ctrl_policy[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL] = {
-+	[MTK_VENDOR_ATTR_AMNT_CTRL_SET] = {.type = NLA_NESTED },
-+	[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP] = { .type = NLA_NESTED },
-+};
-+
-+static struct nla_policy
-+amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
-+	[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_AMNT_DUMP_LEN] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
-+};
-+
- static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
- {
- 	struct nl_sock *handle;
-@@ -14855,6 +14868,170 @@ fail:
- 	return -ENOBUFS;
- }
- 
-+static int
-+nl80211_amnt_set(void *priv, u8 amnt_idx, u8 *amnt_sta_mac)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	void *tb1;
-+	int ret;
-+
-+	if (!drv->mtk_amnt_vendor_cmd_avail) {
-+		wpa_printf(MSG_ERROR,
-+			"nl80211: Driver does not support air monitor");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+			nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
-+	if (!data)
-+		goto fail;
-+
-+	tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_AMNT_CTRL_SET);
-+	if (!tb1)
-+		goto fail;
-+
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_AMNT_SET_INDEX, amnt_idx);
-+
-+	nla_put(msg, MTK_VENDOR_ATTR_AMNT_SET_MACADDR, ETH_ALEN, amnt_sta_mac);
-+
-+	nla_nest_end(msg, tb1);
-+	nla_nest_end(msg, data);
-+
-+	ret = send_and_recv_cmd(drv, msg);
-+
-+	if (ret)
-+		wpa_printf(MSG_ERROR, "Failed to set air monitor. ret=%d (%s)",
-+			ret, strerror(-ret));
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+
-+}
-+
-+static int
-+mt76_amnt_dump_cb(struct nl_msg *msg, void *arg)
-+{
-+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+	struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
-+	struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP];
-+	struct nlattr *attr, *cur, *data;
-+	struct amnt_data *res;
-+	int len = 0, rem;
-+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+	struct amnt_resp_data *amnt_dump = (struct amnt_resp_data *)arg;
-+
-+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+		genlmsg_attrlen(gnlh, 0), NULL);
-+
-+	attr = tb[NL80211_ATTR_VENDOR_DATA];
-+	if (!attr)
-+		return NL_SKIP;
-+
-+	nla_parse_nested(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX,
-+			attr, amnt_ctrl_policy);
-+
-+	if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP])
-+		return NL_SKIP;
-+
-+	nla_parse_nested(tb2, NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
-+			tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP], amnt_dump_policy);
-+
-+	if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_LEN])
-+		return NL_SKIP;
-+
-+	len = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_DUMP_LEN]);
-+	if (!len)
-+		return 0;
-+
-+	if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT])
-+		return NL_SKIP;
-+
-+	data = tb2[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT];
-+
-+	nla_for_each_nested(cur, data, rem) {
-+		if (amnt_dump->sta_num >= AIR_MONITOR_MAX_ENTRY)
-+			return NL_SKIP;
-+		res = (struct amnt_data *) nla_data(cur);
-+		wpa_printf(MSG_ERROR, "[vendor] amnt_idx: %d, "
-+			"addr="MACSTR", "
-+			"rssi=%d/%d/%d/%d, last_seen=%u\n",
-+			res->idx,
-+			MAC2STR(res->addr),
-+			res->rssi[0], res->rssi[1], res->rssi[2],
-+			res->rssi[3], res->last_seen);
-+		os_memcpy(&amnt_dump->resp_data[amnt_dump->sta_num], res,
-+			sizeof(struct amnt_data));
-+		amnt_dump->sta_num++;
-+	}
-+	return 0;
-+}
-+
-+static int
-+nl80211_amnt_dump(void *priv, u8 amnt_idx, u8 *dump_buf)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	void *tb1;
-+	int ret;
-+
-+	if (!drv->mtk_amnt_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			"nl80211: Driver does not support air monitor");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+			nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
-+	if (!data)
-+		goto fail;
-+
-+	tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_AMNT_CTRL_DUMP
-+			| NLA_F_NESTED);
-+	if (!tb1)
-+		goto fail;
-+
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_AMNT_DUMP_INDEX, amnt_idx);
-+
-+	nla_nest_end(msg, tb1);
-+	nla_nest_end(msg, data);
-+
-+	ret = send_and_recv_resp(drv, msg, mt76_amnt_dump_cb, dump_buf);
-+
-+	if (ret)
-+		wpa_printf(MSG_ERROR, "Failed to Dump air monitor. ret=%d (%s)"
-+			, ret, strerror(-ret));
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
- 	.desc = "Linux nl80211/cfg80211",
-@@ -15030,4 +15207,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.ap_wireless = nl80211_ap_wireless,
- 	.ap_rfeatures = nl80211_ap_rfeatures,
- 	.ap_trigtype = nl80211_ap_trigtype,
-+	.amnt_set = nl80211_amnt_set,
-+	.amnt_dump = nl80211_amnt_dump,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 046991a3d..adc1b9bf7 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -207,6 +207,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int mtk_wireless_vendor_cmd_avail:1;
- 	unsigned int mtk_bss_color_vendor_cmd_avail:1;
- 	unsigned int mtk_rfeatures_vendor_cmd_avail:1;
-+	unsigned int mtk_amnt_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 6498eba6d..38e83e42b 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1158,6 +1158,8 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 					break;
- 				case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
- 					drv->mtk_bss_color_vendor_cmd_avail = 1;
-+				case MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL:
-+					drv->mtk_amnt_vendor_cmd_avail = 1;
- 					break;
- 				case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
- 					drv->mtk_rfeatures_vendor_cmd_avail = 1;
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0050-mtk-hostapd-add-support-for-ucode-to-parse-BW320MHz-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0050-mtk-hostapd-add-support-for-ucode-to-parse-BW320MHz-.patch
new file mode 100644
index 0000000..89795f5
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0050-mtk-hostapd-add-support-for-ucode-to-parse-BW320MHz-.patch
@@ -0,0 +1,28 @@
+From 4ee7e5918d767afcef2797f4bffe000c5c811229 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Wed, 30 Aug 2023 04:23:37 +0800
+Subject: [PATCH 050/126] mtk: hostapd: add support for ucode to parse BW320MHz
+ info
+
+---
+ src/utils/ucode.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/utils/ucode.c b/src/utils/ucode.c
+index 29c753c32..4b6ed3a94 100644
+--- a/src/utils/ucode.c
++++ b/src/utils/ucode.c
+@@ -144,6 +144,10 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 	case 2:
+ 		chanwidth = CONF_OPER_CHWIDTH_160MHZ;
+ 		break;
++	case 9:
++		width = 3;
++		chanwidth = CONF_OPER_CHWIDTH_320MHZ;
++		break;
+ 	default:
+ 		return NULL;
+ 	}
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0051-mtk-hostapd-Add-muru-user-number-debug-command.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0051-mtk-hostapd-Add-muru-user-number-debug-command.patch
deleted file mode 100644
index 85e1f7a..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0051-mtk-hostapd-Add-muru-user-number-debug-command.patch
+++ /dev/null
@@ -1,228 +0,0 @@
-From ccef6202191f2a17f84f021e6e2ade206b8c9cc1 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 12 May 2023 05:24:19 +0800
-Subject: [PATCH 051/104] mtk: hostapd: Add muru user number debug command
-
----
- hostapd/ctrl_iface.c         | 13 ++++++++++++-
- src/ap/ap_drv_ops.c          |  4 ++--
- src/ap/ap_drv_ops.h          |  2 +-
- src/ap/hostapd.c             |  3 ++-
- src/common/mtk_vendor.h      |  7 +++++++
- src/drivers/driver.h         |  4 ++--
- src/drivers/driver_nl80211.c | 37 ++++++++++++++++++++++++++++--------
- 7 files changed, 55 insertions(+), 15 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 56722384b..88475b321 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3673,6 +3673,8 @@ hostapd_ctrl_iface_set_edcca(struct hostapd_data *hapd, char *cmd,
- 					 char *buf, size_t buflen)
- {
- 	char *pos, *config, *value;
-+	u8 mode;
-+
- 	config = cmd;
- 	pos = os_strchr(config, ' ');
- 	if (pos == NULL)
-@@ -4065,6 +4067,8 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
- 					 char *buf, size_t buflen)
- {
- 	char *pos, *config, *value;
-+	u8 mode;
-+
- 	config = cmd;
- 	pos = os_strchr(config, ' ');
- 	if (pos == NULL)
-@@ -4082,13 +4086,20 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
- 			return -1;
- 		}
- 		hapd->iconf->mu_onoff = (u8) mu;
-+		mode = MU_CTRL_ONOFF;
-+	} else if (os_strcmp(config, "ul_user_cnt") == 0) {
-+		mode = MU_CTRL_UL_USER_CNT;
-+		wpa_printf(MSG_ERROR, "ul_user_cnt:%d\n", (u8)atoi(value));
-+	} else if (os_strcmp(config, "dl_user_cnt") == 0) {
-+		mode = MU_CTRL_DL_USER_CNT;
-+		wpa_printf(MSG_ERROR, "dl_user_cnt:%d\n", (u8)atoi(value));
- 	} else {
- 		wpa_printf(MSG_ERROR,
- 			"Unsupported parameter %s for SET_MU", config);
- 		return -1;
- 	}
- 
--	if(hostapd_drv_mu_ctrl(hapd) == 0) {
-+	if(hostapd_drv_mu_ctrl(hapd, mode, (u8)atoi(value)) == 0) {
- 		return os_snprintf(buf, buflen, "OK\n");
- 	} else {
- 		return -1;
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index df652b12f..8878db380 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1270,11 +1270,11 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
- 	return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
- }
- 
--int hostapd_drv_mu_ctrl(struct hostapd_data *hapd)
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val)
- {
- 	if (!hapd->driver || !hapd->driver->mu_ctrl)
- 		return 0;
--	return hapd->driver->mu_ctrl(hapd->drv_priv, hapd->iconf->mu_onoff);
-+	return hapd->driver->mu_ctrl(hapd->drv_priv, mode, val);
- }
- 
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 4805a2e84..f77d07da0 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -153,7 +153,7 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
- int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
- 					  const int *threshold);
- int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
--int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val);
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
- int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index a5b683676..5fd46d53d 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -58,6 +58,7 @@
- #include "wpa_auth_kay.h"
- #include "hw_features.h"
- 
-+#include "common/mtk_vendor.h"
- 
- static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
- #ifdef CONFIG_WEP
-@@ -2699,7 +2700,7 @@ dfs_offload:
- 	if (hostapd_drv_configure_edcca_threshold(hapd,
- 						  hapd->iconf->edcca_threshold) < 0)
- 		goto fail;
--	if (hostapd_drv_mu_ctrl(hapd) < 0)
-+	if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF, hapd->iconf->mu_onoff) < 0)
- 		goto fail;
- 	if (hostapd_drv_three_wire_ctrl(hapd) < 0)
- 		goto fail;
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index dd1ca2164..99371bf73 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -199,6 +199,8 @@ enum mtk_vendor_attr_mu_ctrl {
- 
- 	MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
- 	MTK_VENDOR_ATTR_MU_CTRL_DUMP,
-+	MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE,
-+	MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL,
- 
- 	/* keep last */
- 	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
-@@ -272,4 +274,9 @@ struct amnt_resp_data {
- 	struct amnt_data resp_data[0];
- };
- 
-+enum {
-+	MU_CTRL_ONOFF,
-+	MU_CTRL_DL_USER_CNT,
-+	MU_CTRL_UL_USER_CNT,
-+};
- #endif /* MTK_VENDOR_H */
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index dd9c33201..3be4562e7 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5236,11 +5236,11 @@ struct wpa_driver_ops {
- 	int (*get_edcca)(void *priv, const u8 mode, u8 *value);
- 
- 	/**
--	 * mu_ctrl - ctrl on off for UL/DL MURU
-+	 * mu_ctrl - ctrl for UL/DL MURU
- 	 * @priv: Private driver interface data
- 	 *
- 	 */
--	 int (*mu_ctrl)(void *priv, u8 mu_onoff);
-+	 int (*mu_ctrl)(void *priv, u8 mode, u8 val);
- 	 int (*mu_dump)(void *priv, u8 *mu_onoff);
- 
- 	/**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index a2a6807f4..035a477e2 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13982,13 +13982,13 @@ fail:
- 
- 
- #ifdef CONFIG_IEEE80211AX
--static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
-+static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
- {
- 	struct i802_bss *bss = priv;
- 	struct wpa_driver_nl80211_data *drv = bss->drv;
- 	struct nl_msg *msg;
- 	struct nlattr *data;
--	int ret;
-+	int ret = -ENOBUFS;
- 
- 	if (!drv->mtk_mu_vendor_cmd_avail) {
- 		wpa_printf(MSG_INFO,
-@@ -13999,17 +13999,38 @@ static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
- 	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
- 		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
- 		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
--		!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
--		nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, mu_onoff)) {
--		nlmsg_free(msg);
--		return -ENOBUFS;
-+		!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)))
-+		goto fail;
-+
-+	switch (mode) {
-+	case MU_CTRL_ONOFF:
-+			if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, val))
-+				goto fail;
-+		break;
-+	case MU_CTRL_UL_USER_CNT:
-+	case MU_CTRL_DL_USER_CNT:
-+			if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE, mode) ||
-+			    nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL, val))
-+				goto fail;
-+		break;
-+	default:
-+		wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode !");
-+		ret = -EINVAL;
-+		goto fail;
- 	}
-+
- 	nla_nest_end(msg, data);
-+
- 	ret = send_and_recv_cmd(drv, msg);
- 	if(ret){
--		wpa_printf(MSG_ERROR, "Failed to set mu_onoff. ret=%d (%s)", ret, strerror(-ret));
-+		wpa_printf(MSG_ERROR, "Failed to set mu_ctrl. ret=%d (%s)", ret, strerror(-ret));
- 	}
- 	return ret;
-+
-+fail:
-+	nl80211_nlmsg_clear(msg);
-+	nlmsg_free(msg);
-+	return ret;
- }
- 
- 
-@@ -15178,7 +15199,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.update_connect_params = nl80211_update_connection_params,
- 	.send_external_auth_status = nl80211_send_external_auth_status,
- 	.set_4addr_mode = nl80211_set_4addr_mode,
--	.mu_ctrl = nl80211_mu_onoff,
-+	.mu_ctrl = nl80211_mu_ctrl,
- 	.mu_dump = nl80211_mu_dump,
- #ifdef CONFIG_DPP
- 	.dpp_listen = nl80211_dpp_listen,
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0051-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0051-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch
new file mode 100644
index 0000000..09e28f0
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0051-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch
@@ -0,0 +1,279 @@
+From 5070c33b7eb059d1c78ff0c02bb56b16e01431f8 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 11 Sep 2023 10:16:35 +0800
+Subject: [PATCH 051/126] mtk: hostapd: synchronize bandwidth in AP/STA support
+
+Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ucode.c         | 41 +++++++++++++++++++--
+ src/utils/ucode.c      | 12 +++++--
+ wpa_supplicant/ucode.c | 82 ++++++++++++++++++++++++++++++++++--------
+ 3 files changed, 117 insertions(+), 18 deletions(-)
+
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index 3468615fd..8c3212f6f 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -493,6 +493,9 @@ uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
+ 	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
+ 	int i;
+ 
++	wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
++			iface->phy, hostapd_state_text(iface->state));
++
+ 	if (!iface)
+ 		return NULL;
+ 
+@@ -519,6 +522,9 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
+ 	uint64_t intval;
+ 	int i;
+ 
++	wpa_printf(MSG_INFO, "ucode: mtk: start iface for %s in state %s\n",
++			iface->phy, hostapd_state_text(iface->state));
++
+ 	if (!iface)
+ 		return NULL;
+ 
+@@ -541,7 +547,13 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
+ 	UPDATE_VAL(op_class, "op_class");
+ 	UPDATE_VAL(hw_mode, "hw_mode");
+ 	UPDATE_VAL(channel, "channel");
+-	UPDATE_VAL(secondary_channel, "sec_channel");
++
++	intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL));
++	if (!errno) {
++		conf->secondary_channel = intval;
++		changed = true;
++	}
++
+ 	if (!changed &&
+ 	    (iface->bss[0]->beacon_set_done ||
+ 	     iface->state == HAPD_IFACE_DFS))
+@@ -587,6 +599,18 @@ out:
+ 		return ucv_boolean_new(true);
+ 	}
+ 
++	wpa_printf(MSG_INFO, "ucode: mtk: updated channel information:\n");
++	wpa_printf(MSG_INFO, "    * channel: %d\n", conf->channel);
++	wpa_printf(MSG_INFO, "    * op_class: %d\n", conf->op_class);
++	wpa_printf(MSG_INFO, "    * secondary channel: %d\n",
++			conf->secondary_channel);
++	wpa_printf(MSG_INFO, "    * seg0: %d\n",
++			hostapd_get_oper_centr_freq_seg0_idx(conf));
++	wpa_printf(MSG_INFO, "    * seg1: %d\n",
++			hostapd_get_oper_centr_freq_seg0_idx(conf));
++	wpa_printf(MSG_INFO, "    * oper_chwidth: %d\n",
++			hostapd_get_oper_chwidth(conf));
++
+ 	for (i = 0; i < iface->num_bss; i++) {
+ 		struct hostapd_data *hapd = iface->bss[i];
+ 		int ret;
+@@ -621,6 +645,7 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 	uint64_t intval;
+ 	int i, ret = 0;
+ 
++	wpa_printf(MSG_INFO, "ucode: mtk: channel switch for %s\n", iface->phy);
+ 	if (!iface || ucv_type(info) != UC_OBJECT)
+ 		return NULL;
+ 
+@@ -640,7 +665,8 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 	if (errno)
+ 		intval = hostapd_get_oper_chwidth(conf);
+ 	if (intval)
+-		csa.freq_params.bandwidth = 40 << intval;
++		csa.freq_params.bandwidth = 40 <<
++			(intval == CONF_OPER_CHWIDTH_320MHZ ? 3 : intval);
+ 	else
+ 		csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20;
+ 
+@@ -651,6 +677,17 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 	if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
+ 		csa.freq_params.center_freq2 = intval;
+ 
++	wpa_printf(MSG_INFO, "ucode: mtk: switch channel information:\n");
++	wpa_printf(MSG_INFO, "    * freq is %d\n", csa.freq_params.freq);
++	wpa_printf(MSG_INFO, "    * bandwidth is %d\n",
++			csa.freq_params.bandwidth);
++	wpa_printf(MSG_INFO, "    * sec_chan_offset is %d\n",
++			csa.freq_params.sec_channel_offset);
++	wpa_printf(MSG_INFO, "    * center_freq1 is %d\n",
++			csa.freq_params.center_freq1);
++	wpa_printf(MSG_INFO, "    * center_freq2 is %d\n",
++			csa.freq_params.center_freq2);
++
+ 	for (i = 0; i < iface->num_bss; i++)
+ 		ret = hostapd_switch_channel(iface->bss[i], &csa);
+ 
+diff --git a/src/utils/ucode.c b/src/utils/ucode.c
+index 4b6ed3a94..6f82382f3 100644
+--- a/src/utils/ucode.c
++++ b/src/utils/ucode.c
+@@ -110,6 +110,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 	uc_value_t *freq = uc_fn_arg(0);
+ 	uc_value_t *sec = uc_fn_arg(1);
+ 	int width = ucv_uint64_get(uc_fn_arg(2));
++	int bw320_offset = 1;
+ 	int freq_val, center_idx, center_ofs;
+ 	enum oper_chan_width chanwidth;
+ 	enum hostapd_hw_mode hw_mode;
+@@ -147,6 +148,9 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 	case 9:
+ 		width = 3;
+ 		chanwidth = CONF_OPER_CHWIDTH_320MHZ;
++
++		/* bw320_offset is 1 for 320 MHz-1, and 2 for 320 MHz-2 */
++		bw320_offset = ucv_uint64_get(uc_fn_arg(3));
+ 		break;
+ 	default:
+ 		return NULL;
+@@ -178,12 +182,16 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 	ucv_object_add(ret, "hw_mode_str", ucv_get(ucv_string_new(modestr)));
+ 	ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
+ 	ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
++	ucv_object_add(ret, "oper_chwidth", ucv_int64_new(chanwidth));
+ 
+-	if (!sec_channel)
++	if (chanwidth == CONF_OPER_CHWIDTH_USE_HT && !sec_channel) {
++		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(channel));
++		ucv_object_add(ret, "center_freq1", ucv_int64_new(freq_val));
+ 		return ret;
++	}
+ 
+ 	if (freq_val >= 5900)
+-		center_ofs = 0;
++		center_ofs = 32 * (1 - bw320_offset);
+ 	else if (freq_val >= 5745)
+ 		center_ofs = 20;
+ 	else
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+index 397f85bde..542ca25c9 100644
+--- a/wpa_supplicant/ucode.c
++++ b/wpa_supplicant/ucode.c
+@@ -7,6 +7,8 @@
+ #include "wps_supplicant.h"
+ #include "bss.h"
+ #include "ucode.h"
++#include "driver_i.h"
++#include "common/ieee802_11_common.h"
+ 
+ static struct wpa_global *wpa_global;
+ static uc_resource_type_t *global_type, *iface_type;
+@@ -96,6 +98,8 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ {
+ 	const char *state;
+ 	uc_value_t *val;
++	enum oper_chan_width ch_width;
++	int center_freq1, bw320_offset = 1;
+ 
+ 	if (event != EVENT_CH_SWITCH_STARTED)
+ 		return;
+@@ -114,11 +118,42 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ 	uc_value_push(ucv_get(val));
+ 
+ 	if (event == EVENT_CH_SWITCH_STARTED) {
++		center_freq1 = data->ch_switch.cf1;
++
++		switch (data->ch_switch.ch_width) {
++		case CHAN_WIDTH_80:
++			ch_width = CONF_OPER_CHWIDTH_80MHZ;
++			break;
++		case CHAN_WIDTH_80P80:
++			ch_width = CONF_OPER_CHWIDTH_80P80MHZ;
++			break;
++		case CHAN_WIDTH_160:
++			ch_width = CONF_OPER_CHWIDTH_160MHZ;
++			break;
++		case CHAN_WIDTH_320:
++			ch_width = CONF_OPER_CHWIDTH_320MHZ;
++			break;
++		case CHAN_WIDTH_20_NOHT:
++		case CHAN_WIDTH_20:
++		case CHAN_WIDTH_40:
++		default:
++			ch_width = CONF_OPER_CHWIDTH_USE_HT;
++			break;
++		}
++
++		/* Check bandwidth 320 MHz-2 */
++		if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
++		    (center_freq1 == 6265) || center_freq1 == 6585 ||
++		     center_freq1 == 6905)
++			bw320_offset = 2;
++
+ 		ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count));
+ 		ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq));
+ 		ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
+-		ucv_object_add(val, "center_freq1", ucv_int64_new(data->ch_switch.cf1));
++		ucv_object_add(val, "center_freq1", ucv_int64_new(center_freq1));
+ 		ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
++		ucv_object_add(val, "ch_width", ucv_int64_new(ch_width));
++		ucv_object_add(val, "bw320_offset", ucv_int64_new(bw320_offset));
+ 	}
+ 
+ 	ucv_put(wpa_ucode_call(4));
+@@ -212,6 +247,11 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 	struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
+ 	struct wpa_bss *bss;
+ 	uc_value_t *ret, *val;
++	struct wpa_channel_info ci;
++	u8 op_class, channel;
++	enum oper_chan_width ch_width;
++	int center_freq1, bw320_offset = 1, is_24ghz;
++	enum hostapd_hw_mode hw_mode;
+ 
+ 	if (!wpa_s)
+ 		return NULL;
+@@ -224,23 +264,37 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 	bss = wpa_s->current_bss;
+ 	if (bss) {
+ 		int sec_chan = 0;
+-		const u8 *ie;
+-
+-		ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
+-		if (ie && ie[1] >= 2) {
+-			const struct ieee80211_ht_operation *ht_oper;
+-			int sec;
+-
+-			ht_oper = (const void *) (ie + 2);
+-			sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
+-			if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+-				sec_chan = 1;
+-			else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+-				sec_chan = -1;
++
++		hw_mode = ieee80211_freq_to_chan(bss->freq, &channel);
++		is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
++			hw_mode == HOSTAPD_MODE_IEEE80211B;
++
++		wpa_drv_channel_info(wpa_s, &ci);
++		center_freq1 = ci.center_frq1;
++
++		if (bss->freq != center_freq1) {
++			if (is_24ghz)
++				sec_chan = (bss->freq < center_freq1) ? 1 : -1;
++			else
++				sec_chan = (bss->freq / 20) & 1 ? 1 : -1;
++		}
++
++		if (ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
++						  sec_chan, &op_class, &channel))
++			return NULL;
++
++		ch_width = op_class_to_ch_width(op_class);
++		if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
++		    (center_freq1 == 6265) || center_freq1 == 6585 ||
++		     center_freq1 == 6905) {
++			/* Bandwidth 320 MHz-2 */
++			bw320_offset = 2;
+ 		}
+ 
+ 		ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
+ 		ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
++		ucv_object_add(ret, "ch_width", ucv_int64_new(ch_width));
++		ucv_object_add(ret, "bw320_offset", ucv_int64_new(bw320_offset));
+ 	}
+ 
+ #ifdef CONFIG_MESH
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0052-mtk-hostapd-Add-support-for-updating-background-chan.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0052-mtk-hostapd-Add-support-for-updating-background-chan.patch
new file mode 100644
index 0000000..84d8057
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0052-mtk-hostapd-Add-support-for-updating-background-chan.patch
@@ -0,0 +1,341 @@
+From 4bce62337a31d599b1102e782de689552af5549c Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 5 Jul 2023 10:25:01 +0800
+Subject: [PATCH 052/126] mtk: hostapd: Add support for updating background
+ channel by driver
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c                       | 107 ++++++++++++++++++++++++++++-
+ src/ap/dfs.h                       |   3 +
+ src/ap/drv_callbacks.c             |  24 +++++++
+ src/ap/hostapd.h                   |   5 ++
+ src/drivers/driver.h               |  12 ++++
+ src/drivers/driver_nl80211_event.c |   6 ++
+ src/drivers/nl80211_copy.h         |   6 ++
+ 7 files changed, 162 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 66ccb0057..72bb469a6 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -817,11 +817,14 @@ 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);
++	int width = iface->radar_background.new_chwidth;
+ 
+ 	if (!dfs_use_radar_background(iface))
+ 		return;
+ 
++	if (!width)
++		width = hostapd_get_oper_chwidth(iface->conf);
++
+ 	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))
+@@ -986,6 +989,15 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ 		iface->radar_background.temp_ch = 1;
+ 		return 1;
+ 	} else if (dfs_use_radar_background(iface)) {
++		/*
++		 * AP is going to perform CAC, so reset temp_ch to 0,
++		 * when dedicated rx has already started CAC.
++		 */
++		if (iface->radar_background.cac_started) {
++			iface->radar_background.temp_ch = 0;
++			return 0;
++		}
++
+ 		if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
+ 			channel_type = DFS_ANY_CHANNEL;
+ 
+@@ -1126,6 +1138,8 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
+ 	 * ch_switch_notify event is received */
+ 	wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
+ 
++	hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
++
+ 	return 0;
+ }
+ 
+@@ -1177,6 +1191,9 @@ static void hostapd_dfs_update_background_chain(struct hostapd_iface *iface)
+ 	iface->radar_background.secondary_channel = sec;
+ 	iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+ 	iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
++	/* if main channel do not require dfs, then set temp_ch = 1 */
++	if (!hostapd_is_dfs_required(iface))
++		iface->radar_background.temp_ch = 1;
+ 
+ 	wpa_printf(MSG_DEBUG,
+ 		   "%s: setting background chain to chan %d (%d MHz)",
+@@ -1199,6 +1216,10 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+ 	u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+ 	int ret;
+ 
++	if (iface->radar_background.new_chwidth) {
++		hostapd_set_oper_chwidth(iface->conf, iface->radar_background.new_chwidth);
++		iface->radar_background.new_chwidth = 0;
++	}
+ 	ret = hostapd_dfs_request_channel_switch(iface, iface->radar_background.channel,
+ 						 iface->radar_background.freq,
+ 						 iface->radar_background.secondary_channel,
+@@ -1221,6 +1242,52 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+ }
+ 
+ 
++static void
++hostapd_dfs_background_expand(struct hostapd_iface *iface, int chan_width)
++{
++	struct hostapd_hw_modes *mode = iface->current_mode;
++	struct hostapd_channel_data *chan;
++	int i, channel, width = channel_width_to_int(chan_width);
++
++	if (iface->conf->channel - iface->radar_background.channel == width / 5)
++		channel = iface->radar_background.channel;
++	else if (iface->radar_background.channel - iface->conf->channel == width / 5)
++		channel = iface->conf->channel;
++	else
++		return;
++
++	for (i = 0; i < mode->num_channels; i++) {
++		chan = &mode->channels[i];
++		if (chan->chan == channel)
++			break;
++	}
++
++	if (i == mode->num_channels || !dfs_is_chan_allowed(chan, width / 10))
++		return;
++
++	switch (chan_width) {
++	case CHAN_WIDTH_20_NOHT:
++	case CHAN_WIDTH_20:
++		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_USE_HT;
++		break;
++	case CHAN_WIDTH_40:
++		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_80MHZ;
++		break;
++	case CHAN_WIDTH_80:
++		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_160MHZ;
++		break;
++	default:
++		return;
++	}
++
++	iface->radar_background.freq = channel * 5 + 5000;
++	iface->radar_background.channel = channel;
++	iface->radar_background.centr_freq_seg0_idx = channel + width / 5 - 2;
++	iface->radar_background.secondary_channel = 1;
++	iface->radar_background.expand_ch = 0;
++}
++
++
+ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ 			     int ht_enabled, int chan_offset, int chan_width,
+ 			     int cf1, int cf2)
+@@ -1263,6 +1330,10 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ 					return 0;
+ 
+ 				iface->radar_background.temp_ch = 0;
++
++				if (iface->radar_background.expand_ch)
++					hostapd_dfs_background_expand(iface, chan_width);
++
+ 				return hostapd_dfs_start_channel_switch_background(iface);
+ 			}
+ 
+@@ -1293,6 +1364,8 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ 		}
+ 	} else if (hostapd_dfs_is_background_event(iface, freq)) {
+ 		iface->radar_background.cac_started = 0;
++		iface->radar_background.temp_ch = 0;
++		iface->radar_background.expand_ch = 0;
+ 		hostapd_dfs_update_background_chain(iface);
+ 	}
+ 
+@@ -1426,6 +1499,9 @@ hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
+ 	    iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
+ 		return 0;
+ 
++	iface->radar_background.temp_ch = 0;
++	iface->radar_background.expand_ch = 0;
++
+ 	/* Check if CSA in progress */
+ 	if (hostapd_csa_in_progress(iface))
+ 		return 0;
+@@ -1662,6 +1738,35 @@ int hostapd_is_dfs_required(struct hostapd_iface *iface)
+ }
+ 
+ 
++int hostapd_dfs_background_chan_update(struct hostapd_iface *iface, int freq,
++				       int ht_enabled, int chan_offset, int chan_width,
++				       int cf1, int cf2, bool expand)
++{
++	switch (chan_width) {
++	case CHAN_WIDTH_80:
++		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_80MHZ;
++		break;
++	case CHAN_WIDTH_160:
++		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_160MHZ;
++		break;
++	default:
++		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_USE_HT;
++		break;
++	};
++
++	iface->radar_background.freq = freq;
++	iface->radar_background.channel = (freq - 5000) / 5;
++	iface->radar_background.centr_freq_seg0_idx = (cf1 - 5000) / 5;
++	iface->radar_background.centr_freq_seg1_idx = cf2 ? (cf2 - 5000) / 5 : 0;
++	if (expand) {
++		iface->radar_background.temp_ch = 1;
++		iface->radar_background.expand_ch = 1;
++	}
++
++	return 0;
++}
++
++
+ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ 			  int ht_enabled, int chan_offset, int chan_width,
+ 			  int cf1, int cf2)
+diff --git a/src/ap/dfs.h b/src/ap/dfs.h
+index 25ba29ca1..a1a2be5ec 100644
+--- a/src/ap/dfs.h
++++ b/src/ap/dfs.h
+@@ -30,6 +30,9 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+ 			     int ht_enabled,
+ 			     int chan_offset, int chan_width, int cf1, int cf2);
++int hostapd_dfs_background_chan_update(struct hostapd_iface *iface, int freq,
++				       int ht_enabled, int chan_offset, int chan_width,
++				       int cf1, int cf2, bool expand);
+ int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
+ 				 int ht_enabled, int chan_offset, int chan_width,
+ 				 int cf1, int cf2, u32 state);
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 1d40943d6..1fa27bf80 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2270,6 +2270,18 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
+ 			      radar->cf1, radar->cf2);
+ }
+ 
++
++static void hostapd_event_dfs_background_chan_update(struct hostapd_data *hapd,
++						     struct dfs_event *radar, bool expand)
++{
++	wpa_printf(MSG_DEBUG, "DFS background channel %s to %d MHz",
++		   expand ? "expand" : "update", radar->freq);
++	hostapd_dfs_background_chan_update(hapd->iface, radar->freq, radar->ht_enabled,
++					   radar->chan_offset, radar->chan_width,
++					   radar->cf1, radar->cf2, expand);
++}
++
++
+ static void hostapd_event_dfs_sta_cac_skipped(struct hostapd_data *hapd,
+ 					      struct dfs_event *radar)
+ {
+@@ -2705,6 +2717,18 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ 		hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
+ 		hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
+ 		break;
++	case EVENT_DFS_BACKGROUND_CHAN_UPDATE:
++		if (!data)
++			break;
++		hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
++		hostapd_event_dfs_background_chan_update(hapd, &data->dfs_event, false);
++		break;
++	case EVENT_DFS_BACKGROUND_CHAN_EXPAND:
++		if (!data)
++			break;
++		hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
++		hostapd_event_dfs_background_chan_update(hapd, &data->dfs_event, true);
++		break;
+ 	case EVENT_DFS_STA_CAC_SKIPPED:
+ 		if (!data)
+ 			break;
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 994e4681e..574a5ba1d 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -654,6 +654,11 @@ struct hostapd_iface {
+ 		unsigned int temp_ch:1;
+ 		/* CAC started on radar offchain */
+ 		unsigned int cac_started:1;
++		/* Main chain should expand its width according to the
++		 * current offchain channel after CAC detection on radar offchain.
++		 */
++		unsigned int expand_ch:1;
++		int new_chwidth;
+ 	} radar_background;
+ 
+ 	u16 hw_flags;
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 6fc79f659..8457f4703 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5987,6 +5987,18 @@ enum wpa_event_type {
+ 	 * The channel in the notification is now marked as usable.
+ 	 */
+ 	EVENT_DFS_STA_CAC_EXPIRED,
++
++	/**
++	 * EVENT_DFS_BACKGROUND_CHAN_UPDATE - Notification that background
++	 * channel has been updated.
++	 */
++	EVENT_DFS_BACKGROUND_CHAN_UPDATE,
++
++	/**
++	 * EVENT_DFS_BACKGROUND_CHAN_EXPAND - Notification that background
++	 * channel has been updated and operating channel should expand its width.
++	 */
++	EVENT_DFS_BACKGROUND_CHAN_EXPAND,
+ };
+ 
+ 
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 8ba479861..35ee939b7 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -2539,6 +2539,12 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
+ 	case NL80211_RADAR_CAC_STARTED:
+ 		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
+ 		break;
++	case NL80211_RADAR_BACKGROUND_CHAN_UPDATE:
++		wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_UPDATE, &data);
++		break;
++	case NL80211_RADAR_BACKGROUND_CHAN_EXPAND:
++		wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_EXPAND, &data);
++		break;
+ 	case NL80211_RADAR_STA_CAC_SKIPPED:
+ 		wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
+ 		break;
+diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
+index 622fa71fd..d425c797c 100644
+--- a/src/drivers/nl80211_copy.h
++++ b/src/drivers/nl80211_copy.h
+@@ -6843,6 +6843,10 @@ enum nl80211_smps_mode {
+  *	applicable for ETSI dfs domain where pre-CAC is valid for ever.
+  * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
+  *	should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
++ * @NL80211_RADAR_BACKGROUND_CHAN_UPDATE: background channel is updated by the
++ *	driver.
++ * @NL80211_RADAR_BACKGROUND_CHAN_EXPAND: background channel is updated by the
++ *	driver and required to expand main operating channel.
+  * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
+  *	when receiving CSA/assoc resp
+  * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
+@@ -6855,6 +6859,8 @@ enum nl80211_radar_event {
+ 	NL80211_RADAR_NOP_FINISHED,
+ 	NL80211_RADAR_PRE_CAC_EXPIRED,
+ 	NL80211_RADAR_CAC_STARTED,
++	NL80211_RADAR_BACKGROUND_CHAN_UPDATE,
++	NL80211_RADAR_BACKGROUND_CHAN_EXPAND,
+ 	NL80211_RADAR_STA_CAC_SKIPPED,
+ 	NL80211_RADAR_STA_CAC_EXPIRED,
+ };
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0052-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0052-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch
deleted file mode 100644
index 757c28b..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0052-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch
+++ /dev/null
@@ -1,647 +0,0 @@
-From e64468ed944634f41f7f305e7460b6a696b00127 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Sat, 3 Jun 2023 17:12:15 +0800
-Subject: [PATCH 052/104] mtk: hostapd: add connac3 PHY MURU manual mode config
- support
-
-This commit supports read the following two formats to set MU/RU manual
-mode:
-1. hostapd_cli -i <intf> raw set_muru_manual_config=<field>:<value>
-2. hostapd_cli -i <intf> set_mu <field> <value>
-
-For the <field>, we support the following field:
-1. ul_comm_user_cnt/dl_comm_user_cnt: set the number of user
-2. ul_comm_bw/dl_comm_bw: set the bandwith
-3. ul_user_ru_alloc/dl_user_ru_alloc: set the RU band idx and RU
-allocate idx
-4. ul_user_mcs/dl_user_mcs: set the mcs for each user
-5. ul_user_ssAlloc_raru: set the number of ss for each user
-6. ul_comm_gi_ltf: set the combinations of gi and ltf for UL only.
-7. dl_comm_toneplan: fix ru toneplan allocation
-8. dl_comm_ack_policy: fix station ack policy
-9. update : trigger driver to send mcu command to set muru manual mode.
-
-For the value of each field, please check wiki to learn the details:
-https://wiki.mediatek.inc/display/GWKB/muru_mancfg_user_guide
-
-For the fields that mt76 support to use, we will update in this wiki:
-https://wiki.mediatek.inc/pages/viewpage.action?pageId=1271741116
-
-Please noted that this commit is only for connac 3 gen chips. If this
-feature is to be used in other generations, the following actions must
-be taken:
-1. Different data structue needs to be defined for different
-generations, e.g. connac4_muru_comm, connac4_muru_dl.
-2. hostapd_ctrl_iface_set_mu() shall be modified.
-3. A new code level configuration shall be defined to differentiate the
-code flow that different generations will go through.
----
- hostapd/ctrl_iface.c         | 237 +++++++++++++++++++++++++++++++----
- src/ap/ap_config.h           |   1 +
- src/ap/ap_drv_ops.c          |   4 +-
- src/ap/ap_drv_ops.h          |   2 +-
- src/ap/hostapd.c             |   2 +-
- src/common/mtk_vendor.h      | 166 +++++++++++++++++++++++-
- src/drivers/driver.h         |   2 +-
- src/drivers/driver_nl80211.c |  21 ++--
- 8 files changed, 391 insertions(+), 44 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 88475b321..ed383df7d 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3775,7 +3775,6 @@ hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *cmd, char *buf,
- 	}
- }
- 
--
- #ifdef CONFIG_NAN_USD
- 
- static int hostapd_ctrl_nan_publish(struct hostapd_data *hapd, char *cmd,
-@@ -4062,21 +4061,61 @@ fail:
- #endif /* CONFIG_NAN_USD */
- 
- 
-+static int
-+hostapd_parse_argument_helper(char *value, u16 **ptr_input)
-+{
-+#define MAX_MU_CTRL_NUM 17
-+	u16 *input;
-+	char *endptr;
-+	int cnt = 0;
-+
-+	input = os_zalloc(MAX_MU_CTRL_NUM * sizeof(u16));
-+	if (input == NULL) {
-+		wpa_printf(MSG_ERROR, "Failed to allocate memory.\n");
-+		return -1;
-+	}
-+	while (value) {
-+		u8 val = strtol(value, &endptr, 10);
-+
-+		if (value != endptr) {
-+			input[cnt++] = val;
-+			value = os_strchr(endptr, ':');
-+			if (value)
-+				value++;
-+		} else {
-+			break;
-+		}
-+	}
-+
-+	*ptr_input = input;
-+	return cnt;
-+}
-+
-+#define MURU_CFG_DEPENDENCE_CHECK(_val, _mask) do {				\
-+		if ((le_to_host32(_val) & (_mask)) != _mask) {			\
-+			wpa_printf(MSG_ERROR, "Set %s first\n", #_mask);	\
-+			goto fail;						\
-+		}								\
-+	} while(0)
-+
- static int
- hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
--					 char *buf, size_t buflen)
-+			  char *buf, size_t buflen)
- {
- 	char *pos, *config, *value;
--	u8 mode;
-+	u8 i;
-+	int cnt = 0, ret;
-+	u16 *val;
-+	struct connac3_muru *muru;
-+	struct connac3_muru_dl *dl;
-+	struct connac3_muru_ul *ul;
-+	struct connac3_muru_comm *comm;
- 
- 	config = cmd;
- 	pos = os_strchr(config, ' ');
--	if (pos == NULL)
--		return -1;
--	*pos++ = '\0';
-+	if (pos != NULL)
-+		*pos++ = '\0';
- 
--	if(pos == NULL)
--		return -1;
- 	value = pos;
- 
- 	if (os_strcmp(config, "onoff") == 0) {
-@@ -4086,24 +4125,167 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
- 			return -1;
- 		}
- 		hapd->iconf->mu_onoff = (u8) mu;
--		mode = MU_CTRL_ONOFF;
--	} else if (os_strcmp(config, "ul_user_cnt") == 0) {
--		mode = MU_CTRL_UL_USER_CNT;
--		wpa_printf(MSG_ERROR, "ul_user_cnt:%d\n", (u8)atoi(value));
--	} else if (os_strcmp(config, "dl_user_cnt") == 0) {
--		mode = MU_CTRL_DL_USER_CNT;
--		wpa_printf(MSG_ERROR, "dl_user_cnt:%d\n", (u8)atoi(value));
--	} else {
--		wpa_printf(MSG_ERROR,
--			"Unsupported parameter %s for SET_MU", config);
--		return -1;
-+
-+		if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) == 0)
-+			return os_snprintf(buf, buflen, "OK\n");
-+		else
-+			goto fail;
- 	}
- 
--	if(hostapd_drv_mu_ctrl(hapd, mode, (u8)atoi(value)) == 0) {
--		return os_snprintf(buf, buflen, "OK\n");
-+	if (hapd->iconf->muru_config == NULL)
-+		hapd->iconf->muru_config = os_zalloc(sizeof(struct connac3_muru));
-+
-+	muru = hapd->iconf->muru_config;
-+	dl = &muru->dl;
-+	ul = &muru->ul;
-+	comm = &muru->comm;
-+
-+	if (os_strncmp(config, "update", 6) == 0) {
-+		ret = hostapd_drv_mu_ctrl(hapd, MU_CTRL_UPDATE);
-+
-+		os_free(hapd->iconf->muru_config);
-+		hapd->iconf->muru_config = NULL;
-+
-+		if (ret)
-+			goto fail;
-+	} else if (os_strcmp(config, "ul_comm_user_cnt") == 0) {
-+		ul->user_num = (u8)atoi(value);
-+		comm->ppdu_format |= MURU_PPDU_HE_TRIG;
-+		comm->sch_type |= MURU_OFDMA_SCH_TYPE_UL;
-+		muru->cfg_comm |= host_to_le32(MURU_COMM_SET);
-+		muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_TOTAL_USER_CNT);
-+	} else if (os_strcmp(config, "dl_comm_user_cnt") == 0) {
-+		dl->user_num = (u8)atoi(value);
-+		comm->ppdu_format |= MURU_PPDU_HE_MU;
-+		comm->sch_type |= MURU_OFDMA_SCH_TYPE_DL;
-+		muru->cfg_comm |= host_to_le32(MURU_COMM_SET);
-+		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_TOTAL_USER_CNT);
-+	} else if (os_strcmp(config, "dl_comm_bw") == 0) {
-+		dl->bw = (u8)atoi(value);
-+		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_BW);
-+	} else if (os_strcmp(config, "ul_comm_bw") == 0) {
-+		ul->bw = (u8)atoi(value);
-+		muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_BW);
-+	} else if (os_strcmp(config, "dl_user_ru_alloc") == 0) {
-+		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
-+		cnt = hostapd_parse_argument_helper(value, &val);
-+		if (cnt == -1)
-+			goto fail;
-+		if (cnt != (dl->user_num * 2))
-+			goto para_fail;
-+		for (i = 0; i < dl->user_num; i++) {
-+			dl->usr[i].ru_alloc_seg = (val[2 * i] & 0x1);
-+			dl->usr[i].ru_allo_ps160 = ((val[2 * i] & 0x2) >> 1);
-+			dl->usr[i].ru_idx = val[(2 * i) + 1];
-+		}
-+		os_free(val);
-+		muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_RU_ALLOC);
-+	} else if (os_strcmp(config, "ul_user_ru_alloc") == 0) {
-+		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
-+		cnt = hostapd_parse_argument_helper(value, &val);
-+		if (cnt == -1)
-+			goto fail;
-+		if (cnt != (ul->user_num * 2))
-+			goto para_fail;
-+		for (i = 0; i < ul->user_num; i++) {
-+			ul->usr[i].ru_alloc_seg = (val[2 * i] & 0x1);
-+			ul->usr[i].ru_allo_ps160 = ((val[2 * i] & 0x2) >> 1);
-+			ul->usr[i].ru_idx = val[(2 * i) + 1];
-+		}
-+		os_free(val);
-+		muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_RU_ALLOC);
-+	} else if (os_strcmp(config, "dl_user_mcs") == 0) {
-+		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
-+		cnt = hostapd_parse_argument_helper(value, &val);
-+		if (cnt == -1)
-+			goto fail;
-+		if (cnt != dl->user_num)
-+			goto para_fail;
-+		for (i = 0; i < cnt; i++)
-+			dl->usr[i].mcs = (u8) val[i];
-+		os_free(val);
-+		muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_MCS);
-+	} else if (os_strcmp(config, "ul_user_mcs") == 0) {
-+		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
-+		cnt = hostapd_parse_argument_helper(value, &val);
-+		if (cnt == -1)
-+			goto fail;
-+		if (cnt != ul->user_num)
-+			goto para_fail;
-+		for (i = 0; i < cnt; i++)
-+			ul->usr[i].mcs = (u8) val[i];
-+		os_free(val);
-+		muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_MCS);
-+	} else if (os_strcmp(config, "dl_user_cod") == 0) {
-+		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
-+		cnt = hostapd_parse_argument_helper(value, &val);
-+		if (cnt == -1)
-+			goto fail;
-+		if (cnt != dl->user_num)
-+			goto para_fail;
-+		for (i = 0; i < cnt; i++)
-+			dl->usr[i].ldpc = (u8) val[i];
-+		os_free(val);
-+		muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_COD);
-+	} else if (os_strcmp(config, "ul_user_cod") == 0) {
-+		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
-+		cnt = hostapd_parse_argument_helper(value, &val);
-+		if (cnt == -1)
-+			goto fail;
-+		if (cnt != ul->user_num)
-+			goto para_fail;
-+		for (i = 0; i < cnt; i++)
-+			ul->usr[i].ldpc = (u8) val[i];
-+		os_free(val);
-+		muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_COD);
-+	} else if (os_strcmp(config, "ul_user_ssAlloc_raru") == 0) {
-+		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
-+		cnt = hostapd_parse_argument_helper(value, &val);
-+		if (cnt == -1)
-+			goto fail;
-+		if (cnt != ul->user_num)
-+			goto para_fail;
-+		for (i = 0; i < cnt; i++)
-+			ul->usr[i].nss = (u8) val[i];
-+		os_free(val);
-+		muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_NSS);
-+	} else if (os_strcmp(config, "dl_comm_gi") == 0) {
-+		dl->gi = (u8)atoi(value);
-+		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_GI);
-+	} else if (os_strcmp(config, "dl_comm_ltf") == 0) {
-+		dl->ltf = (u8)atoi(value);
-+		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_LTF);
-+	} else if (os_strcmp(config, "ul_comm_gi_ltf") == 0) {
-+		ul->gi_ltf = (u8)atoi(value);
-+		muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_GILTF);
-+	} else if (os_strcmp(config, "dl_comm_ack_policy") == 0) {
-+		dl->ack_policy = (u8)atoi(value);
-+		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_ACK_PLY);
-+	} else if (os_strcmp(config, "dl_comm_toneplan") == 0) {
-+		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_BW);
-+		cnt = hostapd_parse_argument_helper(value, &val);
-+		if (cnt == -1)
-+			goto fail;
-+		i = pow(2, dl->bw);
-+		if (cnt != i)
-+			goto para_fail;
-+		for (i = 0; i < cnt; i++)
-+			dl->ru[i] = host_to_le16(val[i]);
-+		os_free(val);
-+		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_TONE_PLAN);
- 	} else {
--		return -1;
-+		wpa_printf(MSG_ERROR,
-+			   "Unsupported parameter %s for SET_MU", config);
-+		goto fail;
- 	}
-+
-+	return os_snprintf(buf, buflen, "OK\n");
-+
-+para_fail:
-+	os_free(val);
-+	wpa_printf(MSG_ERROR, "Incorrect input number\n");
-+fail:
-+	return os_snprintf(buf, buflen, "FAIL\n");
- }
- 
- 
-@@ -5148,8 +5330,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 		reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
- 							  reply_size);
- 	} else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
--		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply,
--							  reply_size);
-+		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply, reply_size);
- 	} else if (os_strncmp(buf, "GET_MU", 6) == 0) {
- 		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
- 	} else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
-@@ -5175,6 +5356,14 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 	} else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
- 		reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
- 							reply, reply_size);
-+	} else if (os_strncmp(buf, "set_muru_manual_config=", 23) == 0) {
-+		// Replace first ':' with a single space ' '
-+		char *pos = buf + 23;
-+
-+		pos = os_strchr(pos, ':');
-+		if (pos)
-+			*pos = ' ';
-+		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 23, reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index d995b8d9c..3827a8fc8 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1291,6 +1291,7 @@ struct hostapd_config {
- 	u8 ibf_enable;
- 	u8 dfs_detect_mode;
- 	u8 amsdu;
-+	void *muru_config;
- };
- 
- enum three_wire_mode {
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 8878db380..116bc4ceb 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1270,11 +1270,11 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
- 	return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
- }
- 
--int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val)
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode)
- {
- 	if (!hapd->driver || !hapd->driver->mu_ctrl)
- 		return 0;
--	return hapd->driver->mu_ctrl(hapd->drv_priv, mode, val);
-+	return hapd->driver->mu_ctrl(hapd->drv_priv, mode, hapd->iconf);
- }
- 
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index f77d07da0..84b41881a 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -153,7 +153,7 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
- int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
- 					  const int *threshold);
- int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
--int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val);
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode);
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
- int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 5fd46d53d..d1ee0764b 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2700,7 +2700,7 @@ dfs_offload:
- 	if (hostapd_drv_configure_edcca_threshold(hapd,
- 						  hapd->iconf->edcca_threshold) < 0)
- 		goto fail;
--	if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF, hapd->iconf->mu_onoff) < 0)
-+	if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) < 0)
- 		goto fail;
- 	if (hostapd_drv_three_wire_ctrl(hapd) < 0)
- 		goto fail;
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 99371bf73..e140de60b 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -199,8 +199,11 @@ enum mtk_vendor_attr_mu_ctrl {
- 
- 	MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
- 	MTK_VENDOR_ATTR_MU_CTRL_DUMP,
--	MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE,
--	MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL,
-+	/**
-+	 * The above attrs are also used by connac 2. It is best not to modify the
-+	 * above data structure.
-+	 */
-+	MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
- 
- 	/* keep last */
- 	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
-@@ -275,8 +278,163 @@ struct amnt_resp_data {
- };
- 
- enum {
-+	MU_CTRL_UPDATE,
- 	MU_CTRL_ONOFF,
--	MU_CTRL_DL_USER_CNT,
--	MU_CTRL_UL_USER_CNT,
- };
-+
-+struct connac3_muru_comm {
-+	u8 pda_pol;
-+	u8 band;
-+	u8 spe_idx;
-+	u8 proc_type;
-+
-+	le16 mlo_ctrl;
-+	u8 sch_type;
-+	u8 ppdu_format;
-+	u8 ac;
-+	u8 _rsv[3];
-+};
-+
-+struct connac3_muru_dl {
-+	u8 user_num;
-+	u8 tx_mode;
-+	u8 bw;
-+	u8 gi;
-+
-+	u8 ltf;
-+	u8 mcs;
-+	u8 dcm;
-+	u8 cmprs;
-+
-+	le16 ru[16];
-+
-+	u8 c26[2];
-+	u8 ack_policy;
-+	u8 tx_power;
-+
-+	le16 mu_ppdu_duration;
-+	u8 agc_disp_order;
-+	u8 _rsv1;
-+
-+	u8 agc_disp_pol;
-+	u8 agc_disp_ratio;
-+	le16 agc_disp_linkMFG;
-+
-+	le16 prmbl_punc_bmp;
-+	u8 _rsv2[2];
-+
-+	struct {
-+		le16 wlan_idx;
-+		u8 ru_alloc_seg;
-+		u8 ru_idx;
-+		u8 ldpc;
-+		u8 nss;
-+		u8 mcs;
-+		u8 mu_group_idx;
-+		u8 vht_groud_id;
-+		u8 vht_up;
-+		u8 he_start_stream;
-+		u8 he_mu_spatial;
-+		le16 tx_power_alpha;
-+		u8 ack_policy;
-+		u8 ru_allo_ps160;
-+	} usr[16];
-+};
-+
-+struct connac3_muru_ul {
-+	u8 user_num;
-+	u8 tx_mode;
-+
-+	u8 ba_type;
-+	u8 _rsv;
-+
-+	u8 bw;
-+	u8 gi_ltf;
-+	le16 ul_len;
-+
-+	le16 trig_cnt;
-+	u8 pad;
-+	u8 trig_type;
-+
-+	le16 trig_intv;
-+	u8 trig_ta[ETH_ALEN];
-+	le16 ul_ru[16];
-+
-+	u8 c26[2];
-+	le16 agc_disp_linkMFG;
-+
-+	u8 agc_disp_mu_len;
-+	u8 agc_disp_pol;
-+	u8 agc_disp_ratio;
-+	u8 agc_disp_pu_idx;
-+
-+	struct {
-+		le16 wlan_idx;
-+		u8 ru_alloc_seg;
-+		u8 ru_idx;
-+		u8 ldpc;
-+		u8 nss;
-+		u8 mcs;
-+		u8 target_rssi;
-+		le32 trig_pkt_size;
-+		u8 ru_allo_ps160;
-+		u8 _rsv2[3];
-+	} usr[16];
-+};
-+
-+struct connac3_muru_dbg {
-+	/* HE TB RX Debug */
-+	le32 rx_hetb_nonsf_en_bitmap;
-+	le32 rx_hetb_cfg[2];
-+};
-+
-+struct connac3_muru {
-+	le32 cfg_comm;
-+	le32 cfg_dl;
-+	le32 cfg_ul;
-+	le32 cfg_dbg;
-+
-+	struct connac3_muru_comm comm;
-+	struct connac3_muru_dl dl;
-+	struct connac3_muru_ul ul;
-+	struct connac3_muru_dbg dbg;
-+};
-+
-+#define MURU_OFDMA_SCH_TYPE_DL	BIT(0)
-+#define MURU_OFDMA_SCH_TYPE_UL	BIT(1)
-+#define MURU_PPDU_HE_TRIG	BIT(2)
-+#define MURU_PPDU_HE_MU		BIT(3)
-+
-+/* Common Config */
-+#define MURU_COMM_PPDU_FMT	BIT(0)
-+#define MURU_COMM_BAND		BIT(2)
-+#define MURU_COMM_WMM		BIT(3)
-+#define MURU_COMM_SPE_IDX	BIT(4)
-+#define MURU_COMM_SET		(MURU_COMM_PPDU_FMT | MURU_COMM_BAND | \
-+				 MURU_COMM_WMM | MURU_COMM_SPE_IDX)
-+
-+/* DL Common config */
-+#define MURU_FIXED_DL_BW		BIT(0)
-+#define MURU_FIXED_DL_GI		BIT(1)
-+#define MURU_FIXED_DL_TONE_PLAN		BIT(3)
-+#define MURU_FIXED_DL_TOTAL_USER_CNT	BIT(4)
-+#define MURU_FIXED_DL_LTF		BIT(5)
-+#define MURU_FIXED_DL_ACK_PLY		BIT(9)
-+
-+/* DL Per User Config */
-+#define MURU_FIXED_USER_DL_COD		BIT(17)
-+#define MURU_FIXED_USER_DL_MCS		BIT(18)
-+#define MURU_FIXED_USER_DL_RU_ALLOC	BIT(20)
-+
-+/* UL Common Config */
-+#define MURU_FIXED_UL_TOTAL_USER_CNT	BIT(4)
-+#define MURU_FIXED_UL_BW		BIT(5)
-+#define MURU_FIXED_UL_GILTF		BIT(6)
-+
-+/* UL Per User Config */
-+#define MURU_FIXED_USER_UL_COD		BIT(18)
-+#define MURU_FIXED_USER_UL_MCS		BIT(19)
-+#define MURU_FIXED_USER_UL_NSS		BIT(20)
-+#define MURU_FIXED_USER_UL_RU_ALLOC	BIT(21)
-+
- #endif /* MTK_VENDOR_H */
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 3be4562e7..1c0c38e24 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5240,7 +5240,7 @@ struct wpa_driver_ops {
- 	 * @priv: Private driver interface data
- 	 *
- 	 */
--	 int (*mu_ctrl)(void *priv, u8 mode, u8 val);
-+	 int (*mu_ctrl)(void *priv, u8 mode, void *config);
- 	 int (*mu_dump)(void *priv, u8 *mu_onoff);
- 
- 	/**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 035a477e2..aeb755b11 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13982,12 +13982,13 @@ fail:
- 
- 
- #ifdef CONFIG_IEEE80211AX
--static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
-+static int nl80211_mu_ctrl(void *priv, u8 mode, void *config)
- {
- 	struct i802_bss *bss = priv;
- 	struct wpa_driver_nl80211_data *drv = bss->drv;
- 	struct nl_msg *msg;
- 	struct nlattr *data;
-+	struct hostapd_config *cfg = config;
- 	int ret = -ENOBUFS;
- 
- 	if (!drv->mtk_mu_vendor_cmd_avail) {
-@@ -14004,17 +14005,16 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
- 
- 	switch (mode) {
- 	case MU_CTRL_ONOFF:
--			if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, val))
--				goto fail;
-+		if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff))
-+			goto fail;
- 		break;
--	case MU_CTRL_UL_USER_CNT:
--	case MU_CTRL_DL_USER_CNT:
--			if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE, mode) ||
--			    nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL, val))
--				goto fail;
-+	case MU_CTRL_UPDATE:
-+		if (nla_put(msg, MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
-+			    sizeof(struct connac3_muru), cfg->muru_config))
-+			goto fail;
- 		break;
- 	default:
--		wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode !");
-+		wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode %u!", mode);
- 		ret = -EINVAL;
- 		goto fail;
- 	}
-@@ -14022,9 +14022,8 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
- 	nla_nest_end(msg, data);
- 
- 	ret = send_and_recv_cmd(drv, msg);
--	if(ret){
-+	if (ret)
- 		wpa_printf(MSG_ERROR, "Failed to set mu_ctrl. ret=%d (%s)", ret, strerror(-ret));
--	}
- 	return ret;
- 
- fail:
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0053-mtk-hostapd-Add-HE-capabilities-check.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0053-mtk-hostapd-Add-HE-capabilities-check.patch
deleted file mode 100644
index c9fc8b7..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0053-mtk-hostapd-Add-HE-capabilities-check.patch
+++ /dev/null
@@ -1,52 +0,0 @@
-From 2dd00ec0a8231bd8c6893f9517875ad94022f9b2 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Fri, 9 Jun 2023 09:03:05 +0800
-Subject: [PATCH 053/104] mtk: hostapd: Add HE capabilities check
-
-Add HE capabilities check.
-Since "HE capabilities" check has been removed by driver,
-add the support for "HE capabilities" check in hostapd.
----
- src/ap/hw_features.c | 26 ++++++++++++++++++++++++++
- 1 file changed, 26 insertions(+)
-
-diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
-index 672e43a10..a35c5974a 100644
---- a/src/ap/hw_features.c
-+++ b/src/ap/hw_features.c
-@@ -709,6 +709,32 @@ static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
- #ifdef CONFIG_IEEE80211AX
- static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface)
- {
-+	struct hostapd_hw_modes *mode = iface->current_mode;
-+	struct he_capabilities *he_cap = &mode->he_capab[IEEE80211_MODE_AP];
-+	struct hostapd_config *conf = iface->conf;
-+
-+#define HE_CAP_CHECK(hw_cap, field, phy_idx, cfg_cap)					\
-+	do {									\
-+		if (cfg_cap && !(hw_cap[phy_idx] & field)) {	\
-+			wpa_printf(MSG_ERROR, "Driver does not support configured" \
-+				     " HE capability [%s]", #field);		\
-+			return 0;						\
-+		}								\
-+	} while (0)
-+
-+	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_LDPC_CODING_IN_PAYLOAD,
-+		     HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX,
-+		     conf->he_phy_capab.he_ldpc);
-+	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMER_CAPAB,
-+		     HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX,
-+		     conf->he_phy_capab.he_su_beamformer);
-+	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMEE_CAPAB,
-+		     HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX,
-+		     conf->he_phy_capab.he_su_beamformee);
-+	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_MU_BEAMFORMER_CAPAB,
-+		     HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX,
-+		     conf->he_phy_capab.he_mu_beamformer);
-+
- 	return 1;
- }
- #endif /* CONFIG_IEEE80211AX */
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0053-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0053-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem.patch
new file mode 100644
index 0000000..5c5547a
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0053-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem.patch
@@ -0,0 +1,290 @@
+From fe8560c4ccea682bb471816a1e5200ef7f3cde60 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 2 Aug 2023 19:00:34 +0800
+Subject: [PATCH 053/126] mtk: hostapd: add zwdfs mode ctrl for eagle efem
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/config_file.c             |  2 ++
+ hostapd/ctrl_iface.c              | 30 +++++++++++++++++++++++++++
+ src/ap/ap_config.h                |  6 ++++++
+ src/ap/ap_drv_ops.c               | 14 +++++++++++++
+ src/ap/ap_drv_ops.h               |  1 +
+ src/ap/dfs.c                      |  6 ++++++
+ src/common/mtk_vendor.h           | 12 +++++++++++
+ src/drivers/driver.h              |  7 +++++++
+ src/drivers/driver_nl80211.c      | 34 +++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |  1 +
+ src/drivers/driver_nl80211_capa.c |  3 +++
+ 11 files changed, 116 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index dc05738db..b941ec5fa 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -3662,6 +3662,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		conf->acs_exclude_6ghz_non_psc = atoi(pos);
+ 	} else if (os_strcmp(buf, "enable_background_radar") == 0) {
+ 		conf->enable_background_radar = atoi(pos);
++	} else if (os_strcmp(buf, "background_radar_mode") == 0) {
++		conf->background_radar_mode = atoi(pos);
+ 	} else if (os_strcmp(buf, "min_tx_power") == 0) {
+ 		int val = atoi(pos);
+ 
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 53924b265..61d69902d 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4919,6 +4919,33 @@ hostapd_ctrl_iface_dump_amnt(struct hostapd_data *hapd, char *cmd,
+ 		return pos - buf;
+ }
+ 
++static int
++hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cmd,
++					     char *buf, size_t buflen)
++{
++	struct hostapd_iface *iface = hapd->iface;
++	char *pos, *param;
++
++	param = os_strchr(cmd, ' ');
++	if (!param)
++		return -1;
++	*param++ = '\0';
++
++	pos = os_strstr(param, "mode=");
++	if (!pos)
++		return -1;
++
++	if (os_strncmp(pos + 5, "cert", 4) == 0)
++		iface->conf->background_radar_mode = BACKGROUND_RADAR_CERT_MODE;
++	else if (os_strncmp(pos + 5, "normal", 6) == 0)
++		iface->conf->background_radar_mode = BACKGROUND_RADAR_NORMAL_MODE;
++
++	if (hostapd_drv_background_radar_mode(hapd) < 0)
++		return -1;
++
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -5568,6 +5595,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		if (pos)
+ 			*pos = ' ';
+ 		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 23, reply, reply_size);
++	} else if (os_strncmp(buf, "SET_BACKGROUND_RADAR_MODE", 25) == 0) {
++		reply_len = hostapd_ctrl_iface_set_background_radar_mode(hapd, buf + 25,
++									 reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 0c51d2ab9..cffde344f 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1101,6 +1101,7 @@ struct hostapd_config {
+ 	bool hw_mode_set;
+ 	int acs_exclude_6ghz_non_psc;
+ 	int enable_background_radar;
++	int background_radar_mode;
+ 	enum {
+ 		LONG_PREAMBLE = 0,
+ 		SHORT_PREAMBLE = 1
+@@ -1359,6 +1360,11 @@ enum three_wire_mode {
+ 		NUM_THREE_WIRE_MODE - 1
+ };
+ 
++enum background_radar_mode {
++	BACKGROUND_RADAR_NORMAL_MODE,
++	BACKGROUND_RADAR_CERT_MODE,
++};
++
+ enum dfs_mode {
+ 	DFS_DETECT_MODE_DISABLE,
+ 	DFS_DETECT_MODE_AP_ENABLE,
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index ccd0cb939..6f8f329b0 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1401,3 +1401,17 @@ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_
+ 		return 0;
+ 	return hapd->driver->amnt_dump(hapd->drv_priv, amnt_idx, amnt_dump_buf);
+ }
++
++int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->driver->background_radar_mode ||
++	    !(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_RADAR_BACKGROUND) ||
++	    !hapd->iface->conf->enable_background_radar)
++		return 0;
++	if (hapd->iconf->background_radar_mode > BACKGROUND_RADAR_CERT_MODE) {
++		wpa_printf(MSG_INFO, "Invalid value for background radar mode\n");
++		return 0;
++	}
++	return hapd->driver->background_radar_mode(hapd->drv_priv,
++						   hapd->iconf->background_radar_mode);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 659154f56..53075ea94 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -171,6 +171,7 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
+ 
+ int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
+ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
++int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 72bb469a6..db26f6536 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -986,6 +986,9 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ 		if (res < 0)
+ 			return res;
+ 
++		if (hostapd_drv_background_radar_mode(iface->bss[0]) < 0)
++			return -1;
++
+ 		iface->radar_background.temp_ch = 1;
+ 		return 1;
+ 	} else if (dfs_use_radar_background(iface)) {
+@@ -1026,6 +1029,9 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ 		iface->radar_background.secondary_channel = sec;
+ 		iface->radar_background.centr_freq_seg0_idx = cf1;
+ 		iface->radar_background.centr_freq_seg1_idx = cf2;
++
++		if (hostapd_drv_background_radar_mode(iface->bss[0]) < 0)
++			return -1;
+ 	}
+ 
+ 	return 0;
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index f0abcb6b1..3fb4c38f3 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -16,6 +16,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
+ 	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
+ 	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
++	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
+ };
+ 
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -247,6 +248,17 @@ enum mtk_vendor_attr_bss_color_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_background_radar_ctrl {
++	MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL,
++	MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
++};
++
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+ 
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 8457f4703..35c02937d 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5346,6 +5346,13 @@ struct wpa_driver_ops {
+ 	* @amnt_dump_buf: Buffer to print
+ 	*/
+ 	int (*amnt_dump)(void *priv, u8 amnt_idx, u8 *amnt_dump_buf);
++
++	/**
++	 * background_radar_mode - set background radar mode
++	 * @priv: Private driver interface data
++	 * @background_radar_mode: background radar mode
++	 */
++	int (*background_radar_mode)(void *priv, u8 background_radar_mode);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 8a8f8abe8..2d2b47456 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -15080,6 +15080,39 @@ fail:
+ 	return -ENOBUFS;
+ }
+ 
++static int nl80211_background_radar_mode(void *priv, const u8 background_radar_mode)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	/* Prepare nl80211 cmd */
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_background_radar_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting background radar mode");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL) ||
++	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE, background_radar_mode)) {
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to set background radar mode. ret=%d (%s) ",
++			   ret, strerror(-ret));
++	}
++	return ret;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+ 	.desc = "Linux nl80211/cfg80211",
+@@ -15257,4 +15290,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.ap_trigtype = nl80211_ap_trigtype,
+ 	.amnt_set = nl80211_amnt_set,
+ 	.amnt_dump = nl80211_amnt_dump,
++	.background_radar_mode = nl80211_background_radar_mode,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index adc1b9bf7..e9aae8d14 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -208,6 +208,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_bss_color_vendor_cmd_avail:1;
+ 	unsigned int mtk_rfeatures_vendor_cmd_avail:1;
+ 	unsigned int mtk_amnt_vendor_cmd_avail:1;
++	unsigned int mtk_background_radar_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 4a6ff0f28..fd8c7b0ad 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1164,6 +1164,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
+ 					drv->mtk_rfeatures_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL:
++					drv->mtk_background_radar_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0054-mtk-hostapd-Fix-background-channel-overlapping-opera.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0054-mtk-hostapd-Fix-background-channel-overlapping-opera.patch
deleted file mode 100644
index e14b996..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0054-mtk-hostapd-Fix-background-channel-overlapping-opera.patch
+++ /dev/null
@@ -1,64 +0,0 @@
-From cc4db7ad22853f72f43128f96e5d4edcb7c245a1 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 054/104] mtk: hostapd: 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 f5794753e..8be953287 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -814,6 +814,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)
- {
-@@ -1141,6 +1155,8 @@ static void hostapd_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,
-@@ -1375,6 +1391,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;
-@@ -1502,6 +1519,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.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0054-mtk-hostapd-add-support-enable-disable-preamble-punc.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0054-mtk-hostapd-add-support-enable-disable-preamble-punc.patch
new file mode 100644
index 0000000..a26dcf8
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0054-mtk-hostapd-add-support-enable-disable-preamble-punc.patch
@@ -0,0 +1,375 @@
+From d57c8c29e6c88c609ff3c3770f5d6f0eed151761 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 21 Sep 2023 10:29:46 +0800
+Subject: [PATCH 054/126] mtk: hostapd: add support enable/disable preamble
+ puncture from mtk vendor command
+
+This commit supports two ways to enable/disable preamble puncture
+feature.
+
+1. Add new hostapd configuration pp_mode. The possible value could be
+1 to 3. When the value is 0, it means that the firmware will turn off
+the pp algorithm. When the value is 1, it means that the firmware will
+enable the pp algorithm, allowing the algorithm to determine whether pp
+could be applied on each txcmd. When the value is 2, it means that pp
+feature is manually configured by the user. Please noted that for
+current implementation, the default configuration is 0.
+
+2. $ hostapd_cli -i <intf_name> raw set_pp mode val
+The argument val could be 0 for PP feature disabled or 1 to configure
+PP feature as auto mode.
+
+This commit also let user check whether pp feature is enabled by
+hostapd_cli command. The usage shows as below:
+$ hostapd_cli -i <intf_name> raw get_pp mode
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ hostapd/config_file.c             | 12 +++++++
+ hostapd/ctrl_iface.c              | 59 +++++++++++++++++++++++++++++++
+ src/ap/ap_config.c                |  1 +
+ src/ap/ap_config.h                |  7 ++++
+ src/ap/ap_drv_ops.c               |  9 +++++
+ src/ap/ap_drv_ops.h               |  1 +
+ src/ap/hostapd.c                  |  2 ++
+ src/common/mtk_vendor.h           | 12 +++++++
+ src/drivers/driver.h              |  6 ++++
+ src/drivers/driver_nl80211.c      | 49 +++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |  1 +
+ src/drivers/driver_nl80211_capa.c |  3 ++
+ 12 files changed, 162 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index b941ec5fa..fe92540c4 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5446,6 +5446,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 	} else if (os_strcmp(buf, "punct_bitmap") == 0) {
+ 		if (get_u16(pos, line, &conf->punct_bitmap))
+ 			return 1;
++		conf->punct_bitmap = atoi(pos);
++		conf->pp_mode = PP_MANUAL_MODE;
+ 	} else if (os_strcmp(buf, "punct_acs_threshold") == 0) {
+ 		int val = atoi(pos);
+ 
+@@ -5537,6 +5539,16 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 			return 1;
+ 		}
+ 		conf->amsdu = val;
++	} else if (os_strcmp(buf, "pp_mode") == 0) {
++		int val = atoi(pos);
++
++		if ((val != PP_MANUAL_MODE && conf->punct_bitmap) ||
++		    val < PP_DISABLE || val > PP_MANUAL_MODE) {
++			wpa_printf(MSG_ERROR, "Line %d: invalid pp_mode value",
++				   line);
++			return 1;
++		}
++		conf->pp_mode = (u8) val;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 61d69902d..be47bd492 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4946,6 +4946,59 @@ hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cm
+ 	return os_snprintf(buf, buflen, "OK\n");
+ }
+ 
++static int
++hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
++			  size_t buflen)
++{
++	char *pos, *config, *value;
++
++	config = cmd;
++	pos = os_strchr(config, ' ');
++	if (pos == NULL)
++		return -1;
++	*pos++ = '\0';
++
++	if (pos == NULL)
++		return -1;
++	value = pos;
++
++	if (os_strcmp(config, "mode") == 0) {
++		int val = atoi(value);
++
++		if (val < PP_DISABLE || val > PP_AUTO_MODE) {
++			wpa_printf(MSG_ERROR, "Invalid value for set_pp");
++			return -1;
++		}
++		hapd->iconf->pp_mode = (u8) val;
++		if (hostapd_drv_pp_mode_set(hapd) != 0)
++			return -1;
++	} else {
++		wpa_printf(MSG_ERROR,
++			   "Unsupported parameter %s for set_pp", config);
++		return -1;
++	}
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
++static int
++hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
++			  size_t buflen)
++{
++	char *pos, *end;
++
++	pos = buf;
++	end = buf + buflen;
++
++	if (os_strcmp(cmd, "mode") == 0) {
++		return os_snprintf(pos, end - pos, "pp_mode: %d\n",
++				   hapd->iconf->pp_mode);
++	} else {
++		wpa_printf(MSG_ERROR,
++			   "Unsupported parameter %s for get_pp", cmd);
++		return -1;
++	}
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -5587,6 +5640,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 	} else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
+ 		reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
+ 							reply, reply_size);
++	} else if (os_strncmp(buf, "set_pp", 6) == 0) {
++		reply_len = hostapd_ctrl_iface_set_pp(hapd, buf + 7, reply,
++						      reply_size);
++	} else if (os_strncmp(buf, "get_pp", 6) == 0) {
++		reply_len = hostapd_ctrl_iface_get_pp(hapd, buf + 7, reply,
++						      reply_size);
+ 	} else if (os_strncmp(buf, "set_muru_manual_config=", 23) == 0) {
+ 		// Replace first ':' with a single space ' '
+ 		char *pos = buf + 23;
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 3a12de3cd..8b98d6170 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -312,6 +312,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 	conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
+ 	conf->ibf_enable = IBF_DEFAULT_ENABLE;
+ 	conf->amsdu = 1;
++	conf->pp_mode = PP_DISABLE;
+ 
+ 	hostapd_set_and_check_bw320_offset(conf, 0);
+ 
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index cffde344f..a8002fd1a 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1346,6 +1346,7 @@ struct hostapd_config {
+ 	u8 dfs_detect_mode;
+ 	u8 amsdu;
+ 	void *muru_config;
++	u8 pp_mode;
+ };
+ 
+ enum three_wire_mode {
+@@ -1398,6 +1399,12 @@ enum mtk_vendor_attr_edcca_ctrl_mode {
+ 	EDCCA_CTRL_NUM,
+ };
+ 
++enum pp_mode {
++	PP_DISABLE = 0,
++	PP_AUTO_MODE,
++	PP_MANUAL_MODE,
++};
++
+ #define EDCCA_DEFAULT_COMPENSATION -6
+ #define EDCCA_MIN_COMPENSATION -126
+ #define EDCCA_MAX_COMPENSATION 126
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 6f8f329b0..dc772ea31 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1415,3 +1415,12 @@ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
+ 	return hapd->driver->background_radar_mode(hapd->drv_priv,
+ 						   hapd->iconf->background_radar_mode);
+ }
++
++int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->driver->pp_mode_set ||
++	    hapd->iconf->pp_mode > PP_AUTO_MODE)
++		return 0;
++	return hapd->driver->pp_mode_set(hapd->drv_priv,
++					 hapd->iconf->pp_mode);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 53075ea94..9a477a40e 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -172,6 +172,7 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
+ int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
+ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
+ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
++int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 7da5c3afa..7ebdf4eb0 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2771,6 +2771,8 @@ dfs_offload:
+ 		goto fail;
+ 	if (hostapd_drv_amsdu_ctrl(hapd) < 0)
+ 		goto fail;
++	if (hostapd_drv_pp_mode_set(hapd) < 0)
++		goto fail;
+ 
+ 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ 		   iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 3fb4c38f3..0a96c842e 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -17,6 +17,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
+ 	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
+ 	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
++	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
+ };
+ 
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -259,6 +260,17 @@ enum mtk_vendor_attr_background_radar_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_pp_ctrl {
++	MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_PP_MODE,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_PP_CTRL,
++	MTK_VENDOR_ATTR_PP_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
++};
++
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+ 
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 35c02937d..ab93405fb 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5353,6 +5353,12 @@ struct wpa_driver_ops {
+ 	 * @background_radar_mode: background radar mode
+ 	 */
+ 	int (*background_radar_mode)(void *priv, u8 background_radar_mode);
++	/**
++	 * pp_mode_set - Set preamble puncture operation mode
++	 * @priv: Private driver interface data
++	 * @pp_mode: Value is defined in enum pp_mode
++	 */
++	int (*pp_mode_set)(void *priv, const u8 pp_mode);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 2d2b47456..a1e2ee091 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -156,6 +156,11 @@ amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
+ 	[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
+ };
+ 
++static struct nla_policy
++pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
++	[MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
++};
++
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+ 	struct nl_sock *handle;
+@@ -15113,6 +15118,49 @@ static int nl80211_background_radar_mode(void *priv, const u8 background_radar_m
+ 	return ret;
+ }
+ 
++static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_pp_vendor_cmd_avail) {
++		wpa_printf(MSG_DEBUG,
++			   "nl80211: Driver does not support setting preamble puncture");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_PP_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_PP_MODE, pp_mode);
++
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set pp_enable. ret=%d (%s)",
++			   ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+ 	.desc = "Linux nl80211/cfg80211",
+@@ -15291,4 +15339,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.amnt_set = nl80211_amnt_set,
+ 	.amnt_dump = nl80211_amnt_dump,
+ 	.background_radar_mode = nl80211_background_radar_mode,
++	.pp_mode_set = nl80211_pp_mode_set,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index e9aae8d14..707bb7fe4 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -209,6 +209,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_rfeatures_vendor_cmd_avail:1;
+ 	unsigned int mtk_amnt_vendor_cmd_avail:1;
+ 	unsigned int mtk_background_radar_vendor_cmd_avail:1;
++	unsigned int mtk_pp_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index fd8c7b0ad..d082b83d5 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1167,6 +1167,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL:
+ 					drv->mtk_background_radar_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_PP_CTRL:
++					drv->mtk_pp_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0055-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0055-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch
deleted file mode 100644
index 4f42bfa..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0055-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From 0232fabaf99094f319d03ab818cb0c847b6727c2 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 5 Jul 2023 10:47:20 +0800
-Subject: [PATCH 055/104] mtk: hostapd: Fix hostapd_dfs_start_cac log
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c | 6 ++++--
- 1 file changed, 4 insertions(+), 2 deletions(-)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 8be953287..7adaf81ac 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1664,9 +1664,11 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
- 	/* TODO: How to check CAC time for ETSI weather channels? */
- 	iface->dfs_cac_ms = 60000;
- 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
--		"freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
-+		"freq=%d chan=%d chan_offset=%d width=%s seg0=%d "
- 		"seg1=%d cac_time=%ds%s",
--		freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
-+		freq, (freq - 5000) / 5, chan_offset,
-+		channel_width_to_string(chan_width),
-+		(cf1 - 5000) / 5, cf2 ? (cf2 - 5000) / 5 : 0,
- 		iface->dfs_cac_ms / 1000,
- 		hostapd_dfs_is_background_event(iface, freq) ?
- 		" (background)" : "");
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0055-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0055-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch
new file mode 100644
index 0000000..09e54c8
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0055-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch
@@ -0,0 +1,247 @@
+From fb2f823022007f3b72042a38a35a3829fecf9d9f Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Wed, 22 Nov 2023 21:41:34 +0800
+Subject: [PATCH 055/126] mtk: hostapd: add no_beacon vendor command for cert
+
+Add the vendor command to disable/enable beacon
+
+[Usage]
+hostapd_cli -i <interface> no_beacon <value>
+ <value>
+ 0: enable beacon
+ 1: disable beacon
+
+Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
+---
+ hostapd/ctrl_iface.c              | 21 +++++++++++++++++++
+ hostapd/hostapd_cli.c             |  7 +++++++
+ src/ap/ap_drv_ops.c               |  8 ++++++++
+ src/ap/ap_drv_ops.h               |  1 +
+ src/common/mtk_vendor.h           | 12 +++++++++++
+ src/drivers/driver.h              |  7 +++++++
+ src/drivers/driver_nl80211.c      | 34 +++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |  1 +
+ src/drivers/driver_nl80211_capa.c |  3 +++
+ 9 files changed, 94 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index be47bd492..8187cfb3c 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4999,6 +4999,24 @@ hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ 	}
+ }
+ 
++static int
++hostapd_ctrl_iface_disable_beacon(struct hostapd_data *hapd, char *value,
++				  char *buf, size_t buflen)
++{
++	int disable_beacon = atoi(value);
++
++	if (disable_beacon < 0) {
++		wpa_printf(MSG_ERROR, "Invalid value for beacon ctrl");
++		return -1;
++	}
++
++	if (hostapd_drv_beacon_ctrl(hapd, !disable_beacon) == 0)
++		return os_snprintf(buf, buflen, "OK\n");
++	else
++		return -1;
++
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -5657,6 +5675,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 	} else if (os_strncmp(buf, "SET_BACKGROUND_RADAR_MODE", 25) == 0) {
+ 		reply_len = hostapd_ctrl_iface_set_background_radar_mode(hapd, buf + 25,
+ 									 reply, reply_size);
++	} else if (os_strncmp(buf, "NO_BEACON ", 10) == 0) {
++		reply_len = hostapd_ctrl_iface_disable_beacon(hapd, buf + 10, reply,
++							      reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index e43d515d5..9a6a742c1 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1482,6 +1482,11 @@ static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
+ 	return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
+ }
+ 
++static int hostapd_cli_cmd_disable_beacon(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "NO_BEACON", 1, argc, argv);
++}
+ 
+ #ifdef CONFIG_DPP
+ 
+@@ -1901,6 +1906,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 		"<value> [0-15] bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0)"},
+ 	{ "get_mu", hostapd_cli_cmd_get_mu, NULL,
+ 		" = show mu onoff value in 0-15 bitmap"},
++	{ "no_beacon", hostapd_cli_cmd_disable_beacon, NULL,
++		"<value> 0: Enable beacon, 1: Disable beacon"},
+ #ifdef CONFIG_DPP
+ 	{ "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_drv_ops.c b/src/ap/ap_drv_ops.c
+index dc772ea31..d4be02b77 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1424,3 +1424,11 @@ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
+ 	return hapd->driver->pp_mode_set(hapd->drv_priv,
+ 					 hapd->iconf->pp_mode);
+ }
++
++int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
++{
++	if (!hapd->driver || !hapd->driver->beacon_ctrl)
++		return 0;
++	return hapd->driver->beacon_ctrl(hapd->drv_priv, beacon_mode);
++}
++
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 9a477a40e..2ee4ebd04 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -173,6 +173,7 @@ int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_ma
+ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
+ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
+ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
++int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 0a96c842e..261994b8a 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -18,6 +18,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
+ 	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
+ 	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
++	MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
+ };
+ 
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -271,6 +272,17 @@ enum mtk_vendor_attr_pp_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_beacon_ctrl {
++	MTK_VENDOR_ATTR_BEACON_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_BEACON_CTRL_MODE,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_BEACON_CTRL,
++	MTK_VENDOR_ATTR_BEACON_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
++};
++
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+ 
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index ab93405fb..b24caae8a 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5269,6 +5269,13 @@ struct wpa_driver_ops {
+ 	 int (*mu_ctrl)(void *priv, u8 mode, void *config);
+ 	 int (*mu_dump)(void *priv, u8 *mu_onoff);
+ 
++	/**
++	 * beacon_ctrl - ctrl on off for beacon
++	 * @priv: Private driver interface data
++	 *
++	 */
++	int (*beacon_ctrl)(void *priv, u8 beacon_mode);
++
+ 	/**
+ 	 * three_wire_ctrl - set three_wire_ctrl mode
+ 	 * @priv: Private driver interface data
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index a1e2ee091..10e5c837b 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14136,6 +14136,39 @@ static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
+ 
+ 	return ret;
+ }
++static int nl80211_beacon_ctrl(void *priv, u8 beacon_mode)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_beacon_ctrl_vendor_cmd_avail) {
++		wpa_printf(MSG_ERROR,
++			   "nl80211: Driver does not support setting beacon control");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL) ||
++		!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++		nla_put_u8(msg, MTK_VENDOR_ATTR_BEACON_CTRL_MODE, beacon_mode)) {
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++
++	nla_nest_end(msg, data);
++
++	ret = send_and_recv_cmd(drv, msg);
++
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set beacon_ctrl. ret=%d (%s)", ret, strerror(-ret));
++
++	return ret;
++}
++
+ #endif /* CONFIG_IEEE80211AX */
+ 
+ 
+@@ -15309,6 +15342,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.set_4addr_mode = nl80211_set_4addr_mode,
+ 	.mu_ctrl = nl80211_mu_ctrl,
+ 	.mu_dump = nl80211_mu_dump,
++	.beacon_ctrl = nl80211_beacon_ctrl,
+ #ifdef CONFIG_DPP
+ 	.dpp_listen = nl80211_dpp_listen,
+ #endif /* CONFIG_DPP */
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 707bb7fe4..9866c221c 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -210,6 +210,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_amnt_vendor_cmd_avail:1;
+ 	unsigned int mtk_background_radar_vendor_cmd_avail:1;
+ 	unsigned int mtk_pp_vendor_cmd_avail:1;
++	unsigned int mtk_beacon_ctrl_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index d082b83d5..30f89b687 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1170,6 +1170,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_PP_CTRL:
+ 					drv->mtk_pp_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL :
++					drv->mtk_beacon_ctrl_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0056-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0056-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch
deleted file mode 100644
index 6943e83..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0056-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch
+++ /dev/null
@@ -1,62 +0,0 @@
-From 942808028d207776f1a4dbe678166282fb272b37 Mon Sep 17 00:00:00 2001
-From: Michael Lee <michael-cy.lee@mediatek.com>
-Date: Thu, 13 Jul 2023 13:14:26 +0800
-Subject: [PATCH 056/104] mtk: hostapd: Check the bridge after ioctl
- SIOCBRADDIF failed
-
-If ioctl returns EBUSY on command SIOCBRADDIF, the interface might
-already be bridged by others, and linux_br_add_if should not indicate an
-error in the case.
-
-This patch checks whether the interface is correctly brigded when ioctl
-returns EBUSY.
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- src/drivers/linux_ioctl.c | 16 +++++++++++++++-
- 1 file changed, 15 insertions(+), 1 deletion(-)
-
-diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c
-index 29abc0c59..73d27825d 100644
---- a/src/drivers/linux_ioctl.c
-+++ b/src/drivers/linux_ioctl.c
-@@ -150,7 +150,8 @@ int linux_br_del(int sock, const char *brname)
- int linux_br_add_if(int sock, const char *brname, const char *ifname)
- {
- 	struct ifreq ifr;
--	int ifindex;
-+	int ifindex, ret;
-+	char in_br[IFNAMSIZ];
- 
- 	ifindex = if_nametoindex(ifname);
- 	if (ifindex == 0)
-@@ -165,6 +166,17 @@ int linux_br_add_if(int sock, const char *brname, const char *ifname)
- 
- 		wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge "
- 			   "%s: %s", ifname, brname, strerror(errno));
-+
-+		/* If ioctl returns -EBUSY when adding interface into bridge,
-+		 * the interface might already be added by netifd, so here we
-+		 * check whether the interface is currently on the right
-+		 * bridge. */
-+		if(errno == EBUSY && linux_br_get(in_br, ifname) == 0 &&
-+	           os_strcmp(in_br, brname) == 0)
-+			ret = 0;
-+		else
-+			ret = -1;
-+
- 		errno = saved_errno;
- 
- 		/* If ioctl() returns EBUSY when adding an interface into the
-@@ -175,6 +187,8 @@ int linux_br_add_if(int sock, const char *brname, const char *ifname)
- 		if (errno != EBUSY || linux_br_get(in_br, ifname) != 0 ||
- 		    os_strcmp(in_br, brname) != 0)
- 			return -1;
-+
-+		return ret;
- 	}
- 
- 	return 0;
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0056-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0056-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch
new file mode 100644
index 0000000..b3dc7f1
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0056-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch
@@ -0,0 +1,64 @@
+From c384a286a5d163d324b1901b1c446df1c6dfdbe2 Mon Sep 17 00:00:00 2001
+From: "amlendu.mishra" <amlendu.mishra@mediatek.com>
+Date: Wed, 13 Dec 2023 18:13:01 +0530
+Subject: [PATCH 056/126] mtk: hostapd: WPS added change to configure AP PIN
+ lock timout
+
+added config paramter ap_pin_lockout_time to configure
+AP PIN timeout from hosatpd.conf
+
+Signed-off-by: amlendu.mishra <amlendu.mishra@mediatek.com>
+---
+ hostapd/config_file.c | 2 ++
+ src/ap/ap_config.h    | 1 +
+ src/ap/wps_hostapd.c  | 9 ++++++---
+ 3 files changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index fe92540c4..7ab2c6827 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4283,6 +4283,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		bss->wps_independent = atoi(pos);
+ 	} else if (os_strcmp(buf, "ap_setup_locked") == 0) {
+ 		bss->ap_setup_locked = atoi(pos);
++	} else if (os_strcmp(buf, "ap_pin_lockout_time") == 0) {
++		bss->ap_pin_lockout_time = atoi(pos);
+ 	} else if (os_strcmp(buf, "uuid") == 0) {
+ 		if (uuid_str2bin(pos, bss->uuid)) {
+ 			wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index a8002fd1a..b66f79d31 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -505,6 +505,7 @@ struct hostapd_bss_config {
+ #ifdef CONFIG_WPS
+ 	int wps_independent;
+ 	int ap_setup_locked;
++	unsigned int ap_pin_lockout_time;
+ 	u8 uuid[16];
+ 	char *wps_pin_requests;
+ 	char *device_name;
+diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
+index dfc5c3ecb..8a6fc42b2 100644
+--- a/src/ap/wps_hostapd.c
++++ b/src/ap/wps_hostapd.c
+@@ -768,9 +768,12 @@ static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
+ 		eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+ 		wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely");
+ 	} else if (!hapd->conf->ap_setup_locked) {
+-		if (hapd->ap_pin_lockout_time == 0)
+-			hapd->ap_pin_lockout_time = 60;
+-		else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
++		if (hapd->ap_pin_lockout_time == 0) {
++			if (hapd->conf->ap_pin_lockout_time)
++				hapd->ap_pin_lockout_time = hapd->conf->ap_pin_lockout_time;
++			else
++				hapd->ap_pin_lockout_time = 60;
++		} else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
+ 			 (hapd->ap_pin_failures % 3) == 0)
+ 			hapd->ap_pin_lockout_time *= 2;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0057-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0057-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch
deleted file mode 100644
index 9a8f33f..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0057-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From cd4001cf3751979177cefa215f438888397f5bcb Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Fri, 14 Jul 2023 17:19:13 +0800
-Subject: [PATCH 057/104] mtk: hostapd: Update parameter_set_count in MU EDCA
- IE
-
-without this patch, MU EDCA Parameter update count not equal to
-WMM Parameter set count.
----
- src/ap/ieee802_11_he.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
-index 3c6ee72fe..3b6b2041c 100644
---- a/src/ap/ieee802_11_he.c
-+++ b/src/ap/ieee802_11_he.c
-@@ -317,6 +317,9 @@ u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
- 	edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
- 	os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
- 
-+	if (hapd->conf->wmm_enabled)
-+		edca->he_qos_info = hapd->parameter_set_count % 0xf;
-+
- 	wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
- 		    pos, sizeof(*edca));
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0057-mtk-hostapd-remove-chan-freq-list-check-when-scan-re.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0057-mtk-hostapd-remove-chan-freq-list-check-when-scan-re.patch
new file mode 100644
index 0000000..f66edfe
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0057-mtk-hostapd-remove-chan-freq-list-check-when-scan-re.patch
@@ -0,0 +1,44 @@
+From 43f030f950401ccd573e7bc07080107f74f214bd Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 23 Jan 2024 10:52:57 +0800
+Subject: [PATCH 057/126] mtk: hostapd: remove chan/freq list check when scan
+ request and factor calculation
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/acs.c | 12 ------------
+ 1 file changed, 12 deletions(-)
+
+diff --git a/src/ap/acs.c b/src/ap/acs.c
+index 25fec499a..f7e7f15d2 100644
+--- a/src/ap/acs.c
++++ b/src/ap/acs.c
+@@ -599,12 +599,6 @@ static void acs_survey_mode_interference_factor(
+ 		    iface->conf->acs_exclude_dfs)
+ 			continue;
+ 
+-		if (!is_in_chanlist(iface, chan))
+-			continue;
+-
+-		if (!is_in_freqlist(iface, chan))
+-			continue;
+-
+ 		if (chan->max_tx_power < iface->conf->min_tx_power)
+ 			continue;
+ 
+@@ -1370,12 +1364,6 @@ static int * acs_request_scan_add_freqs(struct hostapd_iface *iface,
+ 		     iface->conf->acs_exclude_dfs))
+ 			continue;
+ 
+-		if (!is_in_chanlist(iface, chan))
+-			continue;
+-
+-		if (!is_in_freqlist(iface, chan))
+-			continue;
+-
+ 		if (chan->max_tx_power < iface->conf->min_tx_power)
+ 			continue;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0058-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0058-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch
new file mode 100644
index 0000000..22e91c7
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0058-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch
@@ -0,0 +1,44 @@
+From 03ace053c9dfcbfafec764a894292180f5587c42 Mon Sep 17 00:00:00 2001
+From: "fancy.liu" <fancy.liu@mediatek.com>
+Date: Wed, 1 Nov 2023 19:58:05 +0800
+Subject: [PATCH 058/126] mtk: hostapd: Fix chan_switch to usable DFS channel
+ fail due to ACS
+
+Step and issue:
+1. Enable ACS in hostapd config;
+2. Bootup and then use hostapd_cli cmd switch channel to a DFS channel;
+3. Will do ACS again, and no work on channel specified in step 2.
+
+Root cause:
+When need do DFS-CAC, hostapd will do intf disable, then set the new
+channel into running config settings, and finally enable intf;
+In the test case, new DFS channel is set to runnint config settings, but
+another param acs is still 1 (enable), caused the ACS running when
+intf enabled.
+
+Solution:
+In the hostapd_switch_channel_fallback, need to disable acs if channel
+is valid.
+
+Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
+---
+ src/ap/hostapd.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 7ebdf4eb0..97bc4808e 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4669,6 +4669,9 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+ 
+ 	iface->freq = freq_params->freq;
+ 	iface->conf->channel = freq_params->channel;
++	if (iface->conf->channel != 0) /* If channel not zero, will disable acs. */
++		iface->conf->acs = 0;
++
+ 	iface->conf->secondary_channel = freq_params->sec_channel_offset;
+ 	if (ieee80211_freq_to_channel_ext(freq_params->freq,
+ 					  freq_params->sec_channel_offset, bw,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0058-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0058-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch
deleted file mode 100644
index 07e0656..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0058-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 43840874fe5c56d76612c953d0e31b771818c0d3 Mon Sep 17 00:00:00 2001
-From: mtk20656 <chank.chen@mediatek.com>
-Date: Mon, 24 Jul 2023 11:30:27 +0800
-Subject: [PATCH 058/104] mtk: hostapd: add extension IE list for non-inherit
- IE in mbssid
-
-Certain clients do not scan all non tx profiles due to absence of
-element ID extension list which is mandatory field in non inheritance
-IE. Non inheritance Element ID is followed by extension element ID.
-Length is expected to be mentioned. Currently we do not support any
-extension element and hence filling length as 0.
-
-Signed-off-by: mtk20656 <chank.chen@mediatek.com>
----
- src/ap/ieee802_11.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
- mode change 100644 => 100755 src/ap/ieee802_11.c
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-old mode 100644
-new mode 100755
-index d972a25f1..e42d4e1cc
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -7999,7 +7999,7 @@ static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
- 		else if (hapd->conf->xrates_supported)
- 			ie_count++;
- 		if (ie_count)
--			nontx_profile_len += 4 + ie_count;
-+			nontx_profile_len += 5 + ie_count;
- 
- 		if (len + nontx_profile_len > 255)
- 			break;
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0059-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0059-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch
deleted file mode 100644
index 690dcc0..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0059-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From ab91679c1eeee2c48b871335756601df995e1a19 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 059/104] mtk: hostapd: 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 d1ee0764b..db451387b 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -4569,6 +4569,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.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0059-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0059-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch
new file mode 100644
index 0000000..0be5c0e
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0059-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch
@@ -0,0 +1,122 @@
+From c3f080cfa523b13726b223411cc1bf574000883b Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Thu, 19 Oct 2023 13:38:11 +0800
+Subject: [PATCH 059/126] mtk: hostapd: initialize i802_bss's flink->freq with
+ iface freq.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ src/ap/ap_drv_ops.c          | 6 +++---
+ src/ap/ap_drv_ops.h          | 2 +-
+ src/ap/hostapd.c             | 2 +-
+ src/drivers/driver.h         | 2 +-
+ src/drivers/driver_nl80211.c | 4 ++--
+ wpa_supplicant/driver_i.h    | 2 +-
+ 6 files changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index d4be02b77..011bb37f8 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -371,7 +371,7 @@ int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
+ 	char force_ifname[IFNAMSIZ];
+ 	u8 if_addr[ETH_ALEN];
+ 	return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
+-			      NULL, NULL, force_ifname, if_addr, NULL, 0);
++			      NULL, NULL, force_ifname, if_addr, NULL, 0, hapd->iface->freq);
+ }
+ 
+ 
+@@ -589,13 +589,13 @@ int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
+ int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ 		   const char *ifname, const u8 *addr, void *bss_ctx,
+ 		   void **drv_priv, char *force_ifname, u8 *if_addr,
+-		   const char *bridge, int use_existing)
++		   const char *bridge, int use_existing, int freq)
+ {
+ 	if (hapd->driver == NULL || hapd->driver->if_add == NULL)
+ 		return -1;
+ 	return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
+ 				    bss_ctx, drv_priv, force_ifname, if_addr,
+-				    bridge, use_existing, 1);
++				    bridge, use_existing, 1, freq);
+ }
+ 
+ 
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 2ee4ebd04..8c783610c 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -61,7 +61,7 @@ int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
+ int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ 		   const char *ifname, const u8 *addr, void *bss_ctx,
+ 		   void **drv_priv, char *force_ifname, u8 *if_addr,
+-		   const char *bridge, int use_existing);
++		   const char *bridge, int use_existing, int freq);
+ int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ 		      const char *ifname);
+ int hostapd_if_link_remove(struct hostapd_data *hapd,
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 97bc4808e..f64f17ee6 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -1487,7 +1487,7 @@ int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
+ 				   conf->iface, addr, hapd,
+ 				   &hapd->drv_priv, force_ifname, if_addr,
+ 				   conf->bridge[0] ? conf->bridge : NULL,
+-				   first == -1)) {
++				   first == -1, hapd->iface->freq)) {
+ 			wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
+ 				   MACSTR ")", MAC2STR(hapd->own_addr));
+ 			hapd->interface_added = 0;
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index b24caae8a..8f5d260aa 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -3892,7 +3892,7 @@ struct wpa_driver_ops {
+ 	int (*if_add)(void *priv, enum wpa_driver_if_type type,
+ 		      const char *ifname, const u8 *addr, void *bss_ctx,
+ 		      void **drv_priv, char *force_ifname, u8 *if_addr,
+-		      const char *bridge, int use_existing, int setup_ap);
++		      const char *bridge, int use_existing, int setup_ap, int freq);
+ 
+ 	/**
+ 	 * if_remove - Remove a virtual interface
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 10e5c837b..7cb65a2bc 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -8869,7 +8869,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
+ 				     void *bss_ctx, void **drv_priv,
+ 				     char *force_ifname, u8 *if_addr,
+ 				     const char *bridge, int use_existing,
+-				     int setup_ap)
++				     int setup_ap, int freq)
+ {
+ 	enum nl80211_iftype nlmode;
+ 	struct i802_bss *bss = priv;
+@@ -8989,7 +8989,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
+ 		new_bss->valid_links = 0;
+ 		os_memcpy(new_bss->flink->addr, new_bss->addr, ETH_ALEN);
+ 
+-		new_bss->flink->freq = drv->first_bss->flink->freq;
++		new_bss->flink->freq = (freq == -1) ? drv->first_bss->flink->freq : freq;
+ 		new_bss->ctx = bss_ctx;
+ 		new_bss->added_if = added;
+ 		drv->first_bss->next = new_bss;
+diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
+index 663e16053..624192ebd 100644
+--- a/wpa_supplicant/driver_i.h
++++ b/wpa_supplicant/driver_i.h
+@@ -446,7 +446,7 @@ static inline int wpa_drv_if_add(struct wpa_supplicant *wpa_s,
+ 	if (wpa_s->driver->if_add)
+ 		return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname,
+ 					     addr, bss_ctx, NULL, force_ifname,
+-					     if_addr, bridge, 0, 0);
++					     if_addr, bridge, 0, 0, -1);
+ 	return -1;
+ }
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0060-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0060-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch
deleted file mode 100644
index ccbe766..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0060-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch
+++ /dev/null
@@ -1,45 +0,0 @@
-From 00c2dff4bf8d15c1c84321cef1892009aa32e9ed Mon Sep 17 00:00:00 2001
-From: mtk23510 <rudra.shahi@mediatek.com>
-Date: Fri, 26 May 2023 14:52:35 +0800
-Subject: [PATCH 060/104] mtk: hostapd: Add support for gtk rekeying in hostapd
- cli
-
-Signed-off-by: mtk23510 <rudra.shahi@mediatek.com>
----
- hostapd/hostapd_cli.c | 13 +++++++++++++
- 1 file changed, 13 insertions(+)
-
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 12c580455..e0b175386 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1291,6 +1291,15 @@ static int hostapd_cli_cmd_stop_ap(struct wpa_ctrl *ctrl, int argc,
- }
- 
- 
-+#ifdef CONFIG_TESTING_OPTIONS
-+static int hostapd_cli_cmd_rekey_gtk(struct wpa_ctrl *ctrl, int argc,
-+				      char *argv[])
-+{
-+	return wpa_ctrl_command(ctrl, "REKEY_GTK");
-+}
-+#endif
-+
-+
- static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
- {
- 	char cmd[256];
-@@ -1831,6 +1840,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 	  "= update Beacon frame contents\n"},
- 	{ "stop_ap", hostapd_cli_cmd_stop_ap, NULL,
- 	  "= stop AP\n"},
-+#ifdef CONFIG_TESTING_OPTIONS
-+	{ "rekey_gtk", hostapd_cli_cmd_rekey_gtk, NULL,
-+	  "= rekey gtk\n"},
-+#endif
- 	{ "erp_flush", hostapd_cli_cmd_erp_flush, NULL,
- 	  "= drop all ERP keys"},
- 	{ "log_level", hostapd_cli_cmd_log_level, NULL,
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0060-mtk-hostapd-fix-mld_assoc_link_id.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0060-mtk-hostapd-fix-mld_assoc_link_id.patch
new file mode 100644
index 0000000..79883e2
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0060-mtk-hostapd-fix-mld_assoc_link_id.patch
@@ -0,0 +1,31 @@
+From 69f830f234d58e4bcbdafe70d28c366e87a65c66 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 29 Jan 2024 11:24:28 +0800
+Subject: [PATCH 060/126] mtk: hostapd: fix mld_assoc_link_id
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ src/ap/hostapd.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index f64f17ee6..eefd4c5d3 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4111,11 +4111,9 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ 
+ #ifdef CONFIG_IEEE80211BE
+ 	if (ap_sta_is_mld(hapd, sta)) {
+-		if (sta->mld_assoc_link_id == hapd->mld_link_id) {
+-			mld_assoc_link_id = sta->mld_assoc_link_id;
+-		} else {
++		if (sta->mld_assoc_link_id != hapd->mld_link_id)
+ 			return;
+-		}
++		mld_assoc_link_id = sta->mld_assoc_link_id;
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+         if (mld_assoc_link_id != -2)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0061-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0061-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch
deleted file mode 100644
index 65ba1f0..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0061-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From 2dc6e435eef405ae0cfb69a89c1c8ec7d2852635 Mon Sep 17 00:00:00 2001
-From: Michael Lee <michael-cy.lee@mediatek.com>
-Date: Tue, 11 Jul 2023 14:17:43 +0800
-Subject: [PATCH 061/104] mtk: hostapd: Set WMM and TX queue parameters for
- wpa_supplicant
-
-Since most of the time, wpa_supplicant will be used to setup an STA
-interface, it's default WMM and TX queue parameters should be set for
-STA.
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- wpa_supplicant/config.c | 12 ++++++------
- 1 file changed, 6 insertions(+), 6 deletions(-)
-
-diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
-index c3943355d..7bb57e2ab 100644
---- a/wpa_supplicant/config.c
-+++ b/wpa_supplicant/config.c
-@@ -4720,19 +4720,19 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
- 	const struct hostapd_wmm_ac_params ac_bk =
- 		{ aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
- 	const struct hostapd_wmm_ac_params ac_be =
--		{ aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
-+		{ aCWmin, aCWmin + 2, 3, 0, 0 }; /* best effort traffic */
- 	const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
--		{ aCWmin - 1, aCWmin, 2, 3008 / 32, 0 };
-+		{ aCWmin - 1, aCWmin, 1, 3008 / 32, 0 };
- 	const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
--		{ aCWmin - 2, aCWmin - 1, 2, 1504 / 32, 0 };
-+		{ aCWmin - 2, aCWmin - 1, 1, 1504 / 32, 0 };
- 	const struct hostapd_tx_queue_params txq_bk =
- 		{ 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
- 	const struct hostapd_tx_queue_params txq_be =
--		{ 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0 };
-+		{ 3, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
- 	const struct hostapd_tx_queue_params txq_vi =
--		{ 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30 };
-+		{ 2, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30 };
- 	const struct hostapd_tx_queue_params txq_vo =
--		{ 1, (ecw2cw(aCWmin) + 1) / 4 - 1,
-+		{ 2, (ecw2cw(aCWmin) + 1) / 4 - 1,
- 		  (ecw2cw(aCWmin) + 1) / 2 - 1, 15 };
- 
- #undef ecw2cw
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0061-mtk-wpa_supplicant-correctly-get-assoc-frequency.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0061-mtk-wpa_supplicant-correctly-get-assoc-frequency.patch
new file mode 100644
index 0000000..8877145
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0061-mtk-wpa_supplicant-correctly-get-assoc-frequency.patch
@@ -0,0 +1,25 @@
+From 98c59e85209339abc85968b1e1d1de66d48bc88a Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 19 Oct 2023 10:48:11 +0800
+Subject: [PATCH 061/126] mtk: wpa_supplicant: correctly get assoc frequency
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ src/drivers/driver_nl80211_event.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 35ee939b7..73582aeb0 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -329,6 +329,7 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
+ 			   wpa_ssid_txt(drv->ssid, drv->ssid_len));
+ 	}
+ 
++	drv->assoc_freq = nl80211_get_assoc_freq(drv);
+ 	event.assoc_info.freq = drv->assoc_freq;
+ 	drv->first_bss->flink->freq = drv->assoc_freq;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0062-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0062-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch
deleted file mode 100644
index 502da4c..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0062-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch
+++ /dev/null
@@ -1,78 +0,0 @@
-From eaf45cd7a14dac2d5d601653792d2bc101118585 Mon Sep 17 00:00:00 2001
-From: Michael Lee <michael-cy.lee@mediatek.com>
-Date: Fri, 7 Jul 2023 17:16:11 +0800
-Subject: [PATCH 062/104] mtk: hostapd: Set STA TX queue parameters
- configuration after association
-
-This patch adds the way for wpa_supplicant to set driver's TX queue
-parameters.
-Since STA parses and apply TX queue parameters from AP beacon's WMM IE
-during association, wpa_supplicant set driver's TX queue parameters
-after the association.
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- wpa_supplicant/driver_i.h | 12 ++++++++++++
- wpa_supplicant/events.c   | 16 ++++++++++++++++
- 2 files changed, 28 insertions(+)
-
-diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
-index d01b52bb1..663e16053 100644
---- a/wpa_supplicant/driver_i.h
-+++ b/wpa_supplicant/driver_i.h
-@@ -321,6 +321,18 @@ static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s,
- 	return 0;
- }
- 
-+static inline int wpa_drv_set_tx_queue_params(struct wpa_supplicant *wpa_s,
-+					      int q, int aifs, int cw_min,
-+					      int cw_max, int burst_time)
-+{
-+	int link_id = -1;
-+	if (wpa_s->driver->set_tx_queue_params)
-+		return wpa_s->driver->set_tx_queue_params(wpa_s->drv_priv, q,
-+							  aifs, cw_min, cw_max,
-+							  burst_time, link_id);
-+	return 0;
-+}
-+
- static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s,
- 				    const u8 *data, size_t data_len, int noack,
- 				    unsigned int freq, unsigned int wait)
-diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
-index 2a9342318..8fd2f2049 100644
---- a/wpa_supplicant/events.c
-+++ b/wpa_supplicant/events.c
-@@ -4070,6 +4070,20 @@ out:
- 	return wpa_sm_set_mlo_params(wpa_s->wpa, &wpa_mlo);
- }
- 
-+static void wpa_supplicant_tx_queue_params(struct wpa_supplicant *wpa_s){
-+	struct hostapd_tx_queue_params *p;
-+
-+	for (int i = 0; i < NUM_TX_QUEUES; i++){
-+		p = &wpa_s->conf->tx_queue[i];
-+		if(wpa_drv_set_tx_queue_params(wpa_s, i, p->aifs,
-+						      p->cwmin, p->cwmax,
-+						      p->burst)) {
-+			wpa_printf(MSG_DEBUG, "Failed to set TX queue "
-+				   "parameters for queue %d.", i);
-+			/* Continue anyway */
-+		}
-+	}
-+}
- 
- static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
- 				       union wpa_event_data *data)
-@@ -4399,6 +4413,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
- 
- 	if (wpa_s->current_ssid && wpa_s->current_ssid->enable_4addr_mode)
- 		wpa_supplicant_set_4addr_mode(wpa_s);
-+
-+	wpa_supplicant_tx_queue_params(wpa_s);
- }
- 
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0062-mtk-wpa_supplicant-force-MLD-STA-to-use-SAE-H2E-duri.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0062-mtk-wpa_supplicant-force-MLD-STA-to-use-SAE-H2E-duri.patch
new file mode 100644
index 0000000..4f6a065
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0062-mtk-wpa_supplicant-force-MLD-STA-to-use-SAE-H2E-duri.patch
@@ -0,0 +1,29 @@
+From 63810009871c9dc527fc3a40c3f873bfbf560f7d Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 19 Oct 2023 10:51:55 +0800
+Subject: [PATCH 062/126] mtk: wpa_supplicant: force MLD STA to use SAE H2E
+ during authentication
+
+Otherwise the MLD STA setup will fail with hostapd MLD AP.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ wpa_supplicant/sme.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
+index 443b0b667..afdccf54f 100644
+--- a/wpa_supplicant/sme.c
++++ b/wpa_supplicant/sme.c
+@@ -204,7 +204,7 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
+ 	if (wpa_key_mgmt_sae_ext_key(key_mgmt) &&
+ 	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+ 		use_pt = 1;
+-	if (bss && is_6ghz_freq(bss->freq) &&
++	if (bss && (is_6ghz_freq(bss->freq) || !is_zero_ether_addr(bss->mld_addr)) &&
+ 	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+ 		use_pt = 1;
+ #ifdef CONFIG_SAE_PK
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0063-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0063-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch
deleted file mode 100644
index e1384d9..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0063-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From 7449e88f54fb5e16296399c43c9f758535123cde Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Fri, 1 Sep 2023 15:31:24 +0800
-Subject: [PATCH 063/104] mtk: hostapd: avoid color switch when beacon is not
- set
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/hostapd.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index db451387b..0d4b79b48 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -4707,7 +4707,7 @@ void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap)
- {
- 	struct os_reltime now;
- 
--	if (hapd->cca_in_progress)
-+	if (hapd->cca_in_progress || !hapd->beacon_set_done)
- 		return;
- 
- 	if (os_get_reltime(&now))
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0063-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0063-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch
new file mode 100644
index 0000000..93aafb3
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0063-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch
@@ -0,0 +1,71 @@
+From 5378549cd55834d56c7364268f7849f30e780756 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 11 Dec 2023 17:02:05 +0800
+Subject: [PATCH 063/126] mtk: hostapd: extend ap_get_sta() to find the correct
+ sta
+
+There're still some mld address tranlation issues that need to be dealt
+with on driver side (e.g. RX eapol frames). So add the code that find
+station also with link address and across hapds at the moment.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ src/ap/ieee802_11.c |  1 +
+ src/ap/sta_info.c   | 16 ++++++++++++++++
+ src/ap/sta_info.h   |  1 +
+ 3 files changed, 18 insertions(+)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 4581ae1d4..b87fae929 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -3194,6 +3194,7 @@ static void handle_auth(struct hostapd_data *hapd,
+ 				  mgmt->sa, ETH_ALEN);
+ 			os_memcpy(sta->mld_info.links[link_id].local_addr,
+ 				  hapd->own_addr, ETH_ALEN);
++			os_memcpy(sta->setup_link_addr, mgmt->sa, ETH_ALEN);
+ 		}
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index 51978f45f..d4f4eb913 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -73,6 +73,22 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+ 	s = hapd->sta_hash[STA_HASH(sta)];
+ 	while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
+ 		s = s->hnext;
++
++	if (hapd->conf->mld_ap && !s) {
++		u8 link_id;
++
++		for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
++			struct hostapd_data *h = hostapd_mld_get_link_bss(hapd, link_id);
++
++			if (!h)
++				continue;
++
++			for (s = h->sta_list; s; s = s->next)
++				if (!os_memcmp(s->setup_link_addr, sta, 6))
++					return s;
++		}
++	}
++
+ 	return s;
+ }
+ 
+diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
+index d03d18b48..6b88799e2 100644
+--- a/src/ap/sta_info.h
++++ b/src/ap/sta_info.h
+@@ -96,6 +96,7 @@ struct sta_info {
+ 	struct sta_info *next; /* next entry in sta list */
+ 	struct sta_info *hnext; /* next entry in hash table list */
+ 	u8 addr[6];
++	u8 setup_link_addr[6];
+ 	be32 ipaddr;
+ 	struct dl_list ip6addr; /* list head for struct ip6addr */
+ 	u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0064-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0064-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch
deleted file mode 100644
index 7d6be8a..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0064-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From 8e01f276c2d7be41f3521026c92d8f1bd833865f Mon Sep 17 00:00:00 2001
-From: mtk20656 <chank.chen@mediatek.com>
-Date: Wed, 13 Sep 2023 19:29:51 +0800
-Subject: [PATCH 064/104] mtk: hostapd: 6g bss connect do not consider ht
- operation
-
-Signed-off-by: mtk20656 <chank.chen@mediatek.com>
----
- src/ap/ieee802_11.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
- mode change 100755 => 100644 src/ap/ieee802_11.c
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-old mode 100755
-new mode 100644
-index e42d4e1cc..923cbebcc
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -5591,7 +5591,7 @@ static void handle_assoc(struct hostapd_data *hapd,
- 			set_beacon = true;
- 	}
- 
--	if (update_ht_state(hapd, sta) > 0)
-+	if (!is_6ghz_op_class(hapd->iconf->op_class) && update_ht_state(hapd, sta) > 0)
- 		set_beacon = true;
- 
- 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0064-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0064-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch
new file mode 100644
index 0000000..60e5325
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0064-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch
@@ -0,0 +1,39 @@
+From 016e41e3031fe6c2d5c12f29f47616f7a1b7dabf Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 18 Dec 2023 18:53:35 +0800
+Subject: [PATCH 064/126] mtk: hostapd: update cookie only when noack is unset
+
+This can prevent cookie unmatched problems during setup.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ src/drivers/driver_nl80211.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 7cb65a2bc..4b404f0bb 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -4485,7 +4485,7 @@ send_frame_cmd:
+ 	res = nl80211_send_frame_cmd(bss, freq, wait_time, data, data_len,
+ 				     use_cookie, no_cck, noack, offchanok,
+ 				     csa_offs, csa_offs_len, link_id);
+-	if (!res)
++	if (!res && !noack)
+ 		drv->send_frame_link_id = link_id;
+ 
+ 	return res;
+@@ -9200,8 +9200,8 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss,
+ 			   "cookie 0x%llx", no_ack ? " (no ACK)" : "",
+ 			   (long long unsigned int) cookie);
+ 
+-		if (save_cookie)
+-			drv->send_frame_cookie = no_ack ? (u64) -1 : cookie;
++		if (save_cookie && !no_ack)
++			drv->send_frame_cookie = cookie;
+ 
+ 		if (!wait) {
+ 			 /* There is no need to store this cookie since there
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0065-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0065-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch
deleted file mode 100644
index d87fef0..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0065-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch
+++ /dev/null
@@ -1,96 +0,0 @@
-From 9639b495a347cdd2aadbe2bc2d336b4398518d29 Mon Sep 17 00:00:00 2001
-From: "fancy.liu" <fancy.liu@mediatek.com>
-Date: Sun, 8 Oct 2023 11:50:06 +0800
-Subject: [PATCH 065/104] mtk: hostapd: Add ACS chanlist info in get_config
-
-This patch is used to add ACS chanlist info displaying
-for upper layer application obtaining.
-
-Command format:
-hostapd_cli -i phy0-ap0 get_config
-
-Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
----
- hostapd/ctrl_iface.c | 59 ++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 59 insertions(+)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index ed383df7d..581acc260 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -1058,6 +1058,7 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
- {
- 	int ret;
- 	char *pos, *end;
-+	int i;
- 
- 	pos = buf;
- 	end = buf + buflen;
-@@ -1237,6 +1238,64 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
- 		pos += ret;
- 	}
- 
-+	/* dump chanlist */
-+	if (hapd->iface->conf->acs_ch_list.num > 0) {
-+		ret = os_snprintf(pos, end - pos, "chanlist=");
-+		if (os_snprintf_error(end - pos, ret))
-+			return pos - buf;
-+		pos += ret;
-+
-+		for (i = 0; i < hapd->iface->conf->acs_ch_list.num; i++) {
-+			if (i > 0) {
-+				ret = os_snprintf(pos, end - pos, ", ");
-+				if (os_snprintf_error(end - pos, ret))
-+					return pos - buf;
-+				pos += ret;
-+			}
-+
-+			ret = os_snprintf(pos, end - pos, "%d-%d",
-+				hapd->iface->conf->acs_ch_list.range[i].min,
-+				hapd->iface->conf->acs_ch_list.range[i].max);
-+			if (os_snprintf_error(end - pos, ret))
-+				return pos - buf;
-+			pos += ret;
-+		}
-+
-+		ret = os_snprintf(pos, end - pos, "\n");
-+		if (os_snprintf_error(end - pos, ret))
-+			return pos - buf;
-+		pos += ret;
-+	}
-+
-+	/* dump freqlist */
-+	if (hapd->iface->conf->acs_freq_list.num > 0) {
-+		ret = os_snprintf(pos, end - pos, "freqlist=");
-+		if (os_snprintf_error(end - pos, ret))
-+			return pos - buf;
-+		pos += ret;
-+
-+		for (i = 0; i < hapd->iface->conf->acs_freq_list.num; i++) {
-+			if (i > 0) {
-+				ret = os_snprintf(pos, end - pos, ", ");
-+				if (os_snprintf_error(end - pos, ret))
-+					return pos - buf;
-+				pos += ret;
-+			}
-+
-+			ret = os_snprintf(pos, end - pos, "%d-%d",
-+				hapd->iface->conf->acs_freq_list.range[i].min,
-+				hapd->iface->conf->acs_freq_list.range[i].max);
-+			if (os_snprintf_error(end - pos, ret))
-+				return pos - buf;
-+			pos += ret;
-+		}
-+
-+		ret = os_snprintf(pos, end - pos, "\n");
-+		if (os_snprintf_error(end - pos, ret))
-+			return pos - buf;
-+		pos += ret;
-+	}
-+
- 	return pos - buf;
- }
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0065-mtk-wpa_supplicant-fix-bss-selection-when-setting-ml.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0065-mtk-wpa_supplicant-fix-bss-selection-when-setting-ml.patch
new file mode 100644
index 0000000..1a77564
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0065-mtk-wpa_supplicant-fix-bss-selection-when-setting-ml.patch
@@ -0,0 +1,34 @@
+From a52f67542afdb1c61bd26ea4a8368620114e4747 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 29 Dec 2023 15:04:27 +0800
+Subject: [PATCH 065/126] mtk: wpa_supplicant: fix bss selection when setting
+ mld_connect_band_pref
+
+Without this patch, when setting mld_connect_band_pref as 5g, wrong bss
+will be selected.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ wpa_supplicant/sme.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
+index afdccf54f..abef26f16 100644
+--- a/wpa_supplicant/sme.c
++++ b/wpa_supplicant/sme.c
+@@ -443,8 +443,11 @@ static struct wpa_bss * wpas_ml_connect_pref(struct wpa_supplicant *wpa_s,
+ 	}
+ 
+ 	for_each_link(wpa_s->valid_links, i) {
+-		if (wpa_s->mlo_assoc_link_id == i)
++		if (wpa_s->mlo_assoc_link_id == i) {
++			if (bss->freq >= low && bss->freq <= high)
++				return bss;
+ 			continue;
++		}
+ 
+ 		if (wpa_s->links[i].freq >= low && wpa_s->links[i].freq <= high)
+ 			goto found;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0066-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0066-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch
deleted file mode 100644
index 3f59e13..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0066-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From b463e82d0eec8674e430a7e837c569be4c9fe2c2 Mon Sep 17 00:00:00 2001
-From: mtk25255 <rohit.kamat@mediatek.com>
-Date: Thu, 12 Oct 2023 14:29:23 +0800
-Subject: [PATCH 066/104] mtk: hostapd: Fix RSNXE Interop issue with STA
-
----
- src/ap/ieee802_11.c | 13 ++++++++++++-
- 1 file changed, 12 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 923cbebcc..ce3874901 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -5294,6 +5294,7 @@ static void handle_assoc(struct hostapd_data *hapd,
- 	int omit_rsnxe = 0;
- 	bool set_beacon = false;
- 	bool mld_addrs_not_translated = false;
-+	bool sae_pk = false;
- 
- 	if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
- 				      sizeof(mgmt->u.assoc_req))) {
-@@ -5539,7 +5540,17 @@ static void handle_assoc(struct hostapd_data *hapd,
- 	if (resp != WLAN_STATUS_SUCCESS)
- 		goto fail;
- 	omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
--
-+#ifdef CONFIG_SAE_PK
-+	sae_pk = hostapd_sae_pk_in_use(hapd->conf);
-+#endif /* CONFIG_SAE_PK */
-+	if (omit_rsnxe) {
-+		if (!reassoc && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
-+				(hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
-+				 hapd->conf->sae_pwe == SAE_PWE_BOTH || sae_pk ||
-+				 wpa_key_mgmt_sae_ext_key(hapd->conf->wpa_key_mgmt))) {
-+			omit_rsnxe = 0;
-+		}
-+	}
- 	if (hostapd_get_aid(hapd, sta) < 0) {
- 		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
- 			       HOSTAPD_LEVEL_INFO, "No room for more AIDs");
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0066-mtk-hostapd-add-mld_primary-option.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0066-mtk-hostapd-add-mld_primary-option.patch
new file mode 100644
index 0000000..87fe7ac
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0066-mtk-hostapd-add-mld_primary-option.patch
@@ -0,0 +1,41 @@
+From cba099045780e4befb6d08db29e12549f5d8a83b Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 26 Dec 2023 08:05:41 +0800
+Subject: [PATCH 066/126] mtk: hostapd: add mld_primary option
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ hostapd/config_file.c | 2 ++
+ src/ap/ap_config.h    | 3 +++
+ 2 files changed, 5 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 7ab2c6827..014ca4d3c 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5462,6 +5462,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		conf->punct_acs_threshold = val;
+ 	} else if (os_strcmp(buf, "mld_ap") == 0) {
+ 		bss->mld_ap = !!atoi(pos);
++	} else if (os_strcmp(buf, "mld_primary") == 0) {
++		bss->mld_primary = !!atoi(pos);
+ 	} else if (os_strcmp(buf, "mld_addr") == 0) {
+ 		if (hwaddr_aton(pos, bss->mld_addr)) {
+ 			wpa_printf(MSG_ERROR, "Line %d: Invalid mld_addr",
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index b66f79d31..413505c59 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -987,6 +987,9 @@ struct hostapd_bss_config {
+ 	/* The AP is part of an AP MLD */
+ 	u8 mld_ap;
+ 
++	/* The AP is the primary AP of an AP MLD */
++	u8 mld_primary;
++
+ 	/* The MLD ID to which the AP MLD is affiliated with */
+ 	u8 mld_id;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0067-mtk-hostapd-update-eht-operation-element.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0067-mtk-hostapd-update-eht-operation-element.patch
deleted file mode 100644
index 8764aa7..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0067-mtk-hostapd-update-eht-operation-element.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From a6db9becf71712107500adf239b89f4f8523d3f3 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Wed, 10 May 2023 13:11:34 +0800
-Subject: [PATCH 067/104] mtk: hostapd: update eht operation element
-
----
- src/ap/ieee802_11_eht.c | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
-index 353a4116e..e13662a59 100644
---- a/src/ap/ieee802_11_eht.c
-+++ b/src/ap/ieee802_11_eht.c
-@@ -237,9 +237,9 @@ u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
- 
- 	/* TODO: Fill in appropriate EHT-MCS max Nss information */
- 	oper->basic_eht_mcs_nss_set[0] = 0x11;
--	oper->basic_eht_mcs_nss_set[1] = 0x00;
--	oper->basic_eht_mcs_nss_set[2] = 0x00;
--	oper->basic_eht_mcs_nss_set[3] = 0x00;
-+	oper->basic_eht_mcs_nss_set[1] = 0x11;
-+	oper->basic_eht_mcs_nss_set[2] = 0x11;
-+	oper->basic_eht_mcs_nss_set[3] = 0x11;
- 
- 	if (!eht_oper_info_present)
- 		return pos + elen;
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0067-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0067-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch
new file mode 100644
index 0000000..64611ae
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0067-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch
@@ -0,0 +1,102 @@
+From 68d893f8fd726074299c7040ae69d6ce8b4aca99 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Wed, 6 Mar 2024 15:01:33 +0800
+Subject: [PATCH 067/126] mtk: wpa_supplicant: add 'mld_allowed_phy'
+ configuration option for MLD STA
+
+A new configuration option named 'mld_allowed_phy' is added for MLD STA.
+This option indicates the bitmap of allowed phy for MLO connection.
+Note that setting 'mld_allowed_phy' to 0 makes no phy allowed for MLO.
+In other word, the STA becomes a legacy STA.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/config.c      |  1 +
+ wpa_supplicant/config.h      |  1 +
+ wpa_supplicant/config_file.c |  2 ++
+ wpa_supplicant/sme.c         | 18 ++++++++++++++++++
+ 4 files changed, 22 insertions(+)
+
+diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
+index d2c05e352..d989afdd4 100644
+--- a/wpa_supplicant/config.c
++++ b/wpa_supplicant/config.c
+@@ -5683,6 +5683,7 @@ static const struct global_parse_data global_fields[] = {
+ #endif /* CONFIG_PASN */
+ #ifdef CONFIG_TESTING_OPTIONS
+ 	{ INT_RANGE(mld_force_single_link, 0, 1), 0 },
++	{ INT_RANGE(mld_allowed_phy, 0, 7), 0 },
+ 	{ INT_RANGE(mld_connect_band_pref, 0, MLD_CONNECT_BAND_PREF_MAX), 0 },
+ 	{ FUNC(mld_connect_bssid_pref), 0 },
+ #endif /* CONFIG_TESTING_OPTIONS */
+diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
+index d74b5c455..27ffa3034 100644
+--- a/wpa_supplicant/config.h
++++ b/wpa_supplicant/config.h
+@@ -1813,6 +1813,7 @@ struct wpa_config {
+ 	u8 mld_connect_bssid_pref[ETH_ALEN];
+ 
+ 	int mld_force_single_link;
++	u8 mld_allowed_phy; /* bitmap of allowed phy for MLO connection */
+ #endif /* CONFIG_TESTING_OPTIONS */
+ };
+ 
+diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
+index 5ce616129..591766a85 100644
+--- a/wpa_supplicant/config_file.c
++++ b/wpa_supplicant/config_file.c
+@@ -1626,6 +1626,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
+ #ifdef CONFIG_TESTING_OPTIONS
+ 	if (config->mld_force_single_link)
+ 		fprintf(f, "mld_force_single_link=1\n");
++	if (config->mld_allowed_phy)
++		fprintf(f, "mld_allowed_phy=%u\n", config->mld_allowed_phy);
+ 	if (config->mld_connect_band_pref != MLD_CONNECT_BAND_PREF_AUTO)
+ 		fprintf(f, "mld_connect_band_pref=%d\n",
+ 			config->mld_connect_band_pref);
+diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
+index abef26f16..e8a6a30e8 100644
+--- a/wpa_supplicant/sme.c
++++ b/wpa_supplicant/sme.c
+@@ -523,6 +523,16 @@ static int wpas_sme_ml_auth(struct wpa_supplicant *wpa_s,
+ }
+ 
+ 
++#ifdef CONFIG_TESTING_OPTIONS
++static bool check_mld_allowed_phy(struct wpa_supplicant *wpa_s, int freq)
++{
++	return ((wpa_s->conf->mld_allowed_phy & BIT(0)) && IS_2P4GHZ(freq)) ||
++	       ((wpa_s->conf->mld_allowed_phy & BIT(1)) && IS_5GHZ(freq)) ||
++	       ((wpa_s->conf->mld_allowed_phy & BIT(2)) && is_6ghz_freq(freq));
++}
++#endif /* CONFIG_TESTING_OPTIONS */
++
++
+ static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
+ 				   struct wpa_bss *bss, struct wpa_ssid *ssid)
+ {
+@@ -534,6 +544,11 @@ static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
+ 	for_each_link(bss->valid_links, i) {
+ 		const u8 *bssid = bss->mld_links[i].bssid;
+ 
++#ifdef CONFIG_TESTING_OPTIONS
++		if (!check_mld_allowed_phy(wpa_s, bss->mld_links[i].freq))
++			continue;
++#endif /* CONFIG_TESTING_OPTIONS */
++
+ 		wpa_s->valid_links |= BIT(i);
+ 		os_memcpy(wpa_s->links[i].bssid, bssid, ETH_ALEN);
+ 		wpa_s->links[i].freq = bss->mld_links[i].freq;
+@@ -587,6 +602,9 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
+ 	if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) &&
+ 	    !wpa_bss_parse_basic_ml_element(wpa_s, bss, wpa_s->ap_mld_addr,
+ 					    NULL, ssid, NULL) &&
++#ifdef CONFIG_TESTING_OPTIONS
++	    wpa_s->conf->mld_allowed_phy &&
++#endif /* CONFIG_TESTING_OPTIONS */
+ 	    bss->valid_links) {
+ 		wpa_printf(MSG_DEBUG, "MLD: In authentication");
+ 		wpas_sme_set_mlo_links(wpa_s, bss, ssid);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0068-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0068-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch
new file mode 100644
index 0000000..055820b
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0068-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch
@@ -0,0 +1,312 @@
+From fcb02b75b95acf9efafa46fd3766f48e6799aeb5 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 29 Feb 2024 19:55:34 +0800
+Subject: [PATCH 068/126] mtk: hostapd: support band_idx option for
+ set_mu/get_mu vendor command
+
+Support band_idx for set_mu and get_mu vendor command. The usage shows
+as below:
+1. get_mu: $ hostapd_cli -i <intf> get_mu <band_idx>
+2. set_mu: $ hostapd_cli -i <intf> set_mu <mu_onff>:<band_idx>
+
+Also, make 'band_idx' a mandatory configuration option.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ hostapd/config_file.c        |  9 +++++
+ hostapd/ctrl_iface.c         | 78 ++++++++++++++++++++++++++++--------
+ hostapd/hostapd_cli.c        |  2 +-
+ src/ap/ap_config.c           |  6 +++
+ src/ap/ap_config.h           |  1 +
+ src/ap/ap_drv_ops.c          |  2 +-
+ src/common/mtk_vendor.h      |  1 +
+ src/drivers/driver.h         |  2 +-
+ src/drivers/driver_nl80211.c | 15 +++----
+ 9 files changed, 88 insertions(+), 28 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 014ca4d3c..50d63e259 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5553,6 +5553,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 			return 1;
+ 		}
+ 		conf->pp_mode = (u8) val;
++	} else if (os_strcmp(buf, "band_idx") == 0) {
++		int val = atoi(pos);
++
++		if (val < 0) {
++			wpa_printf(MSG_ERROR, "Line %d: invalid band_idx value",
++				   line);
++			return 1;
++		}
++		conf->band_idx = (u8) val;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 8187cfb3c..76fd25d8b 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4308,17 +4308,42 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ 	value = pos;
+ 
+ 	if (os_strcmp(config, "onoff") == 0) {
+-		int mu = atoi(value);
+-		if (mu < 0 || mu > 15) {
+-			wpa_printf(MSG_ERROR, "Invalid value for mu");
+-			return -1;
++		cnt = hostapd_parse_argument_helper(value, &val);
++		if (cnt == -1)
++			goto fail;
++		if (cnt < 1 || val[0] > 15)
++			goto para_fail;
++
++		if (hostapd_is_mld_ap(hapd)) {
++			u8 band_idx;
++
++			if (cnt != 2)
++				goto para_fail;
++
++			band_idx = val[1];
++
++			for (i = 0; i < hapd->iface->interfaces->count; i++) {
++				struct hostapd_iface *iface;
++
++				iface = hapd->iface->interfaces->iface[i];
++				if (!iface || !iface->conf)
++					continue;
++
++				if (iface->conf->band_idx == band_idx) {
++					hapd = iface->bss[0];
++					break;
++				}
++			}
++			if (hapd->iface->conf->band_idx != band_idx)
++				goto para_fail;
+ 		}
+-		hapd->iconf->mu_onoff = (u8) mu;
+ 
+-		if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) == 0)
+-			return os_snprintf(buf, buflen, "OK\n");
+-		else
++		hapd->iconf->mu_onoff = val[0];
++		os_free(val);
++		if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) != 0)
+ 			goto fail;
++
++		return os_snprintf(buf, buflen, "OK\n");
+ 	}
+ 
+ 	if (hapd->iconf->muru_config == NULL)
+@@ -4330,6 +4355,7 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ 	comm = &muru->comm;
+ 
+ 	if (os_strncmp(config, "update", 6) == 0) {
++		// [ToDo] "update" needs to support band_idx argument
+ 		ret = hostapd_drv_mu_ctrl(hapd, MU_CTRL_UPDATE);
+ 
+ 		os_free(hapd->iconf->muru_config);
+@@ -4475,15 +4501,14 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ 
+ para_fail:
+ 	os_free(val);
+-	wpa_printf(MSG_ERROR, "Incorrect input number\n");
++	wpa_printf(MSG_ERROR, "Input number or value is incorrect\n");
+ fail:
+ 	return os_snprintf(buf, buflen, "FAIL\n");
+ }
+ 
+-
+ static int
+-hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
+-					 size_t buflen)
++hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *input, char *buf,
++			  size_t buflen)
+ {
+ 	u8 mu_onoff;
+ 	char *pos, *end;
+@@ -4491,14 +4516,35 @@ hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
+ 	pos = buf;
+ 	end = buf + buflen;
+ 
++	if (hostapd_is_mld_ap(hapd)) {
++		u8 band_idx, i;
++
++		band_idx = (u8)atoi(input);
++
++		for (i = 0; i < hapd->iface->interfaces->count; i++) {
++			struct hostapd_iface *iface;
++
++			iface = hapd->iface->interfaces->iface[i];
++			if (!iface || !iface->conf)
++				continue;
++
++			if (iface->conf->band_idx == band_idx) {
++				hapd = iface->bss[0];
++				break;
++			}
++		}
++		if (hapd->iface->conf->band_idx != band_idx)
++			return os_snprintf(pos, end - pos, "Invalid band idx to get_mu\n");
++	}
++
+ 	if (hapd->iface->state != HAPD_IFACE_ENABLED)
+ 		return os_snprintf(pos, end - pos, "Not allowed to get_mu when current state is %s\n",
+ 				   hostapd_state_text(hapd->iface->state));
+ 
+ 	if (hostapd_drv_mu_dump(hapd, &mu_onoff) == 0) {
+ 		hapd->iconf->mu_onoff = mu_onoff;
+-		return os_snprintf(pos, end - pos, "[hostapd_cli] = UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
+-			!!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
++		return os_snprintf(pos, end - pos, "Band idx %u: UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
++			hapd->iconf->band_idx, !!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
+ 	} else {
+ 		wpa_printf(MSG_INFO, "ctrl iface failed to call");
+ 		return -1;
+@@ -5633,8 +5679,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 							  reply_size);
+ 	} else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
+ 		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply, reply_size);
+-	} else if (os_strncmp(buf, "GET_MU", 6) == 0) {
+-		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
++	} else if (os_strncmp(buf, "GET_MU ", 7) == 0) {
++		reply_len = hostapd_ctrl_iface_get_mu(hapd, buf + 7, reply, reply_size);
+ 	} else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
+ 		reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
+ 	} else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 9a6a742c1..a98f76eee 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1479,7 +1479,7 @@ static int hostapd_cli_cmd_set_mu(struct wpa_ctrl *ctrl, int argc,
+ static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
+ 					   char *argv[])
+ {
+-	return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
++	return hostapd_cli_cmd(ctrl, "GET_MU", 0, argc, argv);
+ }
+ 
+ static int hostapd_cli_cmd_disable_beacon(struct wpa_ctrl *ctrl, int argc,
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 8b98d6170..95100b25c 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -313,6 +313,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 	conf->ibf_enable = IBF_DEFAULT_ENABLE;
+ 	conf->amsdu = 1;
+ 	conf->pp_mode = PP_DISABLE;
++	conf->band_idx = 255;
+ 
+ 	hostapd_set_and_check_bw320_offset(conf, 0);
+ 
+@@ -1631,6 +1632,11 @@ int hostapd_config_check(struct hostapd_config *conf, int full_config)
+ 		return -1;
+ 	}
+ 
++	if (full_config && conf->band_idx == 255) {
++		wpa_printf(MSG_ERROR, "band_idx is required");
++		return -1;
++	}
++
+ 	for (i = 0; i < conf->num_bss; i++) {
+ 		if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
+ 			return -1;
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 413505c59..c61e67403 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1351,6 +1351,7 @@ struct hostapd_config {
+ 	u8 amsdu;
+ 	void *muru_config;
+ 	u8 pp_mode;
++	u8 band_idx;
+ };
+ 
+ enum three_wire_mode {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 011bb37f8..48c2801da 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1310,7 +1310,7 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
+ {
+ 	if (!hapd->driver || !hapd->driver->mu_dump)
+ 		return 0;
+-	return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
++	return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff, hapd->iconf->band_idx);
+ }
+ 
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 261994b8a..9b054ef43 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -210,6 +210,7 @@ enum mtk_vendor_attr_mu_ctrl {
+ 	 * above data structure.
+ 	 */
+ 	MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
++	MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 8f5d260aa..47f4f56c0 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5267,7 +5267,7 @@ struct wpa_driver_ops {
+ 	 *
+ 	 */
+ 	 int (*mu_ctrl)(void *priv, u8 mode, void *config);
+-	 int (*mu_dump)(void *priv, u8 *mu_onoff);
++	 int (*mu_dump)(void *priv, u8 *mu_onoff, u8 band_idx);
+ 
+ 	/**
+ 	 * beacon_ctrl - ctrl on off for beacon
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 4b404f0bb..f6080f160 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14035,7 +14035,8 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, void *config)
+ 
+ 	switch (mode) {
+ 	case MU_CTRL_ONOFF:
+-		if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff))
++		if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff) ||
++		    nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX, cfg->band_idx))
+ 			goto fail;
+ 		break;
+ 	case MU_CTRL_UPDATE:
+@@ -14099,7 +14100,7 @@ static int mu_dump_handler(struct nl_msg *msg, void *arg)
+ 	return 0;
+ }
+ 
+-static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
++static int nl80211_mu_dump(void *priv, u8 *mu_onoff, u8 band_idx)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+@@ -14115,17 +14116,13 @@ static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
+ 
+ 	if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
+ 		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
+-		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL)) {
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
++		!(attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++		nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX, band_idx)) {
+ 		nlmsg_free(msg);
+ 		return -ENOBUFS;
+ 	}
+ 
+-  attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+-	if (!attr) {
+-		nlmsg_free(msg);
+-		return -1;
+-	}
+-
+ 	nla_nest_end(msg, attr);
+ 
+ 	ret = send_and_recv_resp(drv, msg, mu_dump_handler, mu_onoff);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0068-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0068-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch
deleted file mode 100644
index f383d62..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0068-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From f5256a36cd00b54955decd53961ece85dd5f11f9 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Wed, 30 Aug 2023 04:23:37 +0800
-Subject: [PATCH 068/104] mtk: hostapd: ucode: add support for ucode to parse
- BW320MHz info
-
----
- src/utils/ucode.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/src/utils/ucode.c b/src/utils/ucode.c
-index 29c753c32..4b6ed3a94 100644
---- a/src/utils/ucode.c
-+++ b/src/utils/ucode.c
-@@ -144,6 +144,10 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- 	case 2:
- 		chanwidth = CONF_OPER_CHWIDTH_160MHZ;
- 		break;
-+	case 9:
-+		width = 3;
-+		chanwidth = CONF_OPER_CHWIDTH_320MHZ;
-+		break;
- 	default:
- 		return NULL;
- 	}
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0069-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0069-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch
new file mode 100644
index 0000000..8cdcaf5
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0069-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch
@@ -0,0 +1,145 @@
+From 8df808e1c82be7ce9b00937bb14a2536824f16d2 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 19 Jan 2024 14:11:05 +0800
+Subject: [PATCH 069/126] mtk: hostapd: Handle DFS radar detection in MLO
+
+To handle DFS CAC in MLO, we add the following changes:
+1. Add link id info to radar detect cmd for the kernel to use the correct link.
+2. Block RNR IE for disabled iface. (the EID len would be wrong without it)
+3. Only flush the old stations for the first BSS; otherwise, after DFS CAC
+stations would be flushed again.
+
+Add background radar handling
+
+The logic has changed here, so rebase it.
+Avoid flushing old stations for non-first BSS so that the stations
+can remain connected when non-first BSS is added via link add or it
+completes CAC.
+Also, handle the case when the first BSS requires CAC.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/ap_drv_ops.c                |  9 +++++++++
+ src/ap/hostapd.c                   | 11 ++++++++++-
+ src/ap/ieee802_11.c                |  3 +++
+ src/drivers/driver_nl80211.c       | 18 ++++++++++++++++++
+ src/drivers/driver_nl80211.h       |  1 +
+ src/drivers/driver_nl80211_event.c |  3 ++-
+ 6 files changed, 43 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 48c2801da..f51d5be8e 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1041,6 +1041,15 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
+ 		return -1;
+ 	}
+ 	data.radar_background = radar_background;
++	data.link_id = -1;
++
++#ifdef CONFIG_IEEE80211BE
++	if (hapd->conf->mld_ap) {
++		data.link_id = hapd->mld_link_id;
++		wpa_printf(MSG_DEBUG,
++			   "hostapd_start_dfs_cac: link_id=%d", data.link_id);
++	}
++#endif /* CONFIG_IEEE80211BE */
+ 
+ 	res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
+ 	if (!res) {
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index eefd4c5d3..93c164177 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -1420,9 +1420,18 @@ int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
+ 	u8 if_addr[ETH_ALEN];
+ 	int flush_old_stations = 1;
+ 
+-	if (!hostapd_mld_is_first_bss(hapd))
++	if (!hostapd_mld_is_first_bss(hapd)) {
++		/* Only flush old stations when setting up the first BSS for the MLD. */
++		flush_old_stations = 0;
+ 		wpa_printf(MSG_DEBUG,
+ 			   "MLD: %s: Setting non-first BSS", __func__);
++	} else if (hapd->conf->mld_ap &&
++		   hapd->iface->state == HAPD_IFACE_DFS) {
++		/* Also, avoid flushing old STA when the first BSS of the MLD requires CAC. */
++		flush_old_stations = 0;
++		wpa_printf(MSG_DEBUG,
++			   "MLD: %s: Setting first BSS after CAC complete", __func__);
++	}
+ 
+ 	wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)",
+ 		   __func__, hapd, conf->iface, first);
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index b87fae929..c6676b754 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7910,6 +7910,9 @@ u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
+ 		    !is_6ghz_op_class(iface->conf->op_class))
+ 			continue;
+ 
++		if (!iface->bss[0]->started)
++			continue;
++
+ 		eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
+ 					    current_len, NULL, false);
+ 	}
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index f6080f160..dec358fba 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -10587,6 +10587,24 @@ static int nl80211_start_radar_detection(void *priv,
+ 		return -1;
+ 	}
+ 
++	if (freq->link_id != NL80211_DRV_LINK_ID_NA) {
++		wpa_printf(MSG_DEBUG, "nl80211: Set link_id=%u for radar detect",
++			   freq->link_id);
++
++		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, freq->link_id)) {
++			nlmsg_free(msg);
++			return -ENOBUFS;
++		}
++
++		if (freq->radar_background) {
++			struct i802_link *link = nl80211_get_link(bss, freq->link_id);
++
++			link->background_freq = freq->freq;
++		} else {
++			nl80211_link_set_freq(bss, freq->link_id, freq->freq);
++		}
++	}
++
+ 	ret = send_and_recv_cmd(drv, msg);
+ 	if (ret == 0)
+ 		return 0;
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 9866c221c..a0a62e536 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -56,6 +56,7 @@ struct i802_link {
+ 	unsigned int beacon_set:1;
+ 
+ 	int freq;
++	int background_freq;
+ 	int bandwidth;
+ 	u8 addr[ETH_ALEN];
+ 	void *ctx;
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 73582aeb0..7f5a3d892 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -1638,7 +1638,8 @@ nl80211_get_link_id_by_freq(struct i802_bss *bss, unsigned int freq)
+ 	unsigned int i;
+ 
+ 	for_each_link(bss->valid_links, i) {
+-		if ((unsigned int) bss->links[i].freq == freq)
++		if ((unsigned int) bss->links[i].freq == freq ||
++		    (unsigned int) bss->links[i].background_freq == freq)
+ 			return i;
+ 	}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0069-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0069-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch
deleted file mode 100644
index 4ca6761..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0069-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch
+++ /dev/null
@@ -1,279 +0,0 @@
-From d7a803942f27759fe0e27c4550d70e44fb83c897 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Mon, 11 Sep 2023 10:16:35 +0800
-Subject: [PATCH 069/104] mtk: hostapd: synchronize bandwidth in AP/STA support
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- src/ap/ucode.c         | 41 +++++++++++++++++++--
- src/utils/ucode.c      | 12 +++++--
- wpa_supplicant/ucode.c | 82 ++++++++++++++++++++++++++++++++++--------
- 3 files changed, 117 insertions(+), 18 deletions(-)
-
-diff --git a/src/ap/ucode.c b/src/ap/ucode.c
-index 16d1b5153..98b2a3bf2 100644
---- a/src/ap/ucode.c
-+++ b/src/ap/ucode.c
-@@ -489,6 +489,9 @@ uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
- 	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
- 	int i;
- 
-+	wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
-+			iface->phy, hostapd_state_text(iface->state));
-+
- 	if (!iface)
- 		return NULL;
- 
-@@ -515,6 +518,9 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
- 	uint64_t intval;
- 	int i;
- 
-+	wpa_printf(MSG_INFO, "ucode: mtk: start iface for %s in state %s\n",
-+			iface->phy, hostapd_state_text(iface->state));
-+
- 	if (!iface)
- 		return NULL;
- 
-@@ -537,7 +543,13 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
- 	UPDATE_VAL(op_class, "op_class");
- 	UPDATE_VAL(hw_mode, "hw_mode");
- 	UPDATE_VAL(channel, "channel");
--	UPDATE_VAL(secondary_channel, "sec_channel");
-+
-+	intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL));
-+	if (!errno) {
-+		conf->secondary_channel = intval;
-+		changed = true;
-+	}
-+
- 	if (!changed &&
- 	    (iface->bss[0]->beacon_set_done ||
- 	     iface->state == HAPD_IFACE_DFS))
-@@ -583,6 +595,18 @@ out:
- 		return ucv_boolean_new(true);
- 	}
- 
-+	wpa_printf(MSG_INFO, "ucode: mtk: updated channel information:\n");
-+	wpa_printf(MSG_INFO, "    * channel: %d\n", conf->channel);
-+	wpa_printf(MSG_INFO, "    * op_class: %d\n", conf->op_class);
-+	wpa_printf(MSG_INFO, "    * secondary channel: %d\n",
-+			conf->secondary_channel);
-+	wpa_printf(MSG_INFO, "    * seg0: %d\n",
-+			hostapd_get_oper_centr_freq_seg0_idx(conf));
-+	wpa_printf(MSG_INFO, "    * seg1: %d\n",
-+			hostapd_get_oper_centr_freq_seg0_idx(conf));
-+	wpa_printf(MSG_INFO, "    * oper_chwidth: %d\n",
-+			hostapd_get_oper_chwidth(conf));
-+
- 	for (i = 0; i < iface->num_bss; i++) {
- 		struct hostapd_data *hapd = iface->bss[i];
- 		int ret;
-@@ -617,6 +641,7 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
- 	uint64_t intval;
- 	int i, ret = 0;
- 
-+	wpa_printf(MSG_INFO, "ucode: mtk: channel switch for %s\n", iface->phy);
- 	if (!iface || ucv_type(info) != UC_OBJECT)
- 		return NULL;
- 
-@@ -636,7 +661,8 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
- 	if (errno)
- 		intval = hostapd_get_oper_chwidth(conf);
- 	if (intval)
--		csa.freq_params.bandwidth = 40 << intval;
-+		csa.freq_params.bandwidth = 40 <<
-+			(intval == CONF_OPER_CHWIDTH_320MHZ ? 3 : intval);
- 	else
- 		csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20;
- 
-@@ -647,6 +673,17 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
- 	if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
- 		csa.freq_params.center_freq2 = intval;
- 
-+	wpa_printf(MSG_INFO, "ucode: mtk: switch channel information:\n");
-+	wpa_printf(MSG_INFO, "    * freq is %d\n", csa.freq_params.freq);
-+	wpa_printf(MSG_INFO, "    * bandwidth is %d\n",
-+			csa.freq_params.bandwidth);
-+	wpa_printf(MSG_INFO, "    * sec_chan_offset is %d\n",
-+			csa.freq_params.sec_channel_offset);
-+	wpa_printf(MSG_INFO, "    * center_freq1 is %d\n",
-+			csa.freq_params.center_freq1);
-+	wpa_printf(MSG_INFO, "    * center_freq2 is %d\n",
-+			csa.freq_params.center_freq2);
-+
- 	for (i = 0; i < iface->num_bss; i++)
- 		ret = hostapd_switch_channel(iface->bss[i], &csa);
- 
-diff --git a/src/utils/ucode.c b/src/utils/ucode.c
-index 4b6ed3a94..6f82382f3 100644
---- a/src/utils/ucode.c
-+++ b/src/utils/ucode.c
-@@ -110,6 +110,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- 	uc_value_t *freq = uc_fn_arg(0);
- 	uc_value_t *sec = uc_fn_arg(1);
- 	int width = ucv_uint64_get(uc_fn_arg(2));
-+	int bw320_offset = 1;
- 	int freq_val, center_idx, center_ofs;
- 	enum oper_chan_width chanwidth;
- 	enum hostapd_hw_mode hw_mode;
-@@ -147,6 +148,9 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- 	case 9:
- 		width = 3;
- 		chanwidth = CONF_OPER_CHWIDTH_320MHZ;
-+
-+		/* bw320_offset is 1 for 320 MHz-1, and 2 for 320 MHz-2 */
-+		bw320_offset = ucv_uint64_get(uc_fn_arg(3));
- 		break;
- 	default:
- 		return NULL;
-@@ -178,12 +182,16 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- 	ucv_object_add(ret, "hw_mode_str", ucv_get(ucv_string_new(modestr)));
- 	ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
- 	ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
-+	ucv_object_add(ret, "oper_chwidth", ucv_int64_new(chanwidth));
- 
--	if (!sec_channel)
-+	if (chanwidth == CONF_OPER_CHWIDTH_USE_HT && !sec_channel) {
-+		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(channel));
-+		ucv_object_add(ret, "center_freq1", ucv_int64_new(freq_val));
- 		return ret;
-+	}
- 
- 	if (freq_val >= 5900)
--		center_ofs = 0;
-+		center_ofs = 32 * (1 - bw320_offset);
- 	else if (freq_val >= 5745)
- 		center_ofs = 20;
- 	else
-diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
-index 397f85bde..542ca25c9 100644
---- a/wpa_supplicant/ucode.c
-+++ b/wpa_supplicant/ucode.c
-@@ -7,6 +7,8 @@
- #include "wps_supplicant.h"
- #include "bss.h"
- #include "ucode.h"
-+#include "driver_i.h"
-+#include "common/ieee802_11_common.h"
- 
- static struct wpa_global *wpa_global;
- static uc_resource_type_t *global_type, *iface_type;
-@@ -96,6 +98,8 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
- {
- 	const char *state;
- 	uc_value_t *val;
-+	enum oper_chan_width ch_width;
-+	int center_freq1, bw320_offset = 1;
- 
- 	if (event != EVENT_CH_SWITCH_STARTED)
- 		return;
-@@ -114,11 +118,42 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
- 	uc_value_push(ucv_get(val));
- 
- 	if (event == EVENT_CH_SWITCH_STARTED) {
-+		center_freq1 = data->ch_switch.cf1;
-+
-+		switch (data->ch_switch.ch_width) {
-+		case CHAN_WIDTH_80:
-+			ch_width = CONF_OPER_CHWIDTH_80MHZ;
-+			break;
-+		case CHAN_WIDTH_80P80:
-+			ch_width = CONF_OPER_CHWIDTH_80P80MHZ;
-+			break;
-+		case CHAN_WIDTH_160:
-+			ch_width = CONF_OPER_CHWIDTH_160MHZ;
-+			break;
-+		case CHAN_WIDTH_320:
-+			ch_width = CONF_OPER_CHWIDTH_320MHZ;
-+			break;
-+		case CHAN_WIDTH_20_NOHT:
-+		case CHAN_WIDTH_20:
-+		case CHAN_WIDTH_40:
-+		default:
-+			ch_width = CONF_OPER_CHWIDTH_USE_HT;
-+			break;
-+		}
-+
-+		/* Check bandwidth 320 MHz-2 */
-+		if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
-+		    (center_freq1 == 6265) || center_freq1 == 6585 ||
-+		     center_freq1 == 6905)
-+			bw320_offset = 2;
-+
- 		ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count));
- 		ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq));
- 		ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
--		ucv_object_add(val, "center_freq1", ucv_int64_new(data->ch_switch.cf1));
-+		ucv_object_add(val, "center_freq1", ucv_int64_new(center_freq1));
- 		ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
-+		ucv_object_add(val, "ch_width", ucv_int64_new(ch_width));
-+		ucv_object_add(val, "bw320_offset", ucv_int64_new(bw320_offset));
- 	}
- 
- 	ucv_put(wpa_ucode_call(4));
-@@ -212,6 +247,11 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
- 	struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
- 	struct wpa_bss *bss;
- 	uc_value_t *ret, *val;
-+	struct wpa_channel_info ci;
-+	u8 op_class, channel;
-+	enum oper_chan_width ch_width;
-+	int center_freq1, bw320_offset = 1, is_24ghz;
-+	enum hostapd_hw_mode hw_mode;
- 
- 	if (!wpa_s)
- 		return NULL;
-@@ -224,23 +264,37 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
- 	bss = wpa_s->current_bss;
- 	if (bss) {
- 		int sec_chan = 0;
--		const u8 *ie;
--
--		ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
--		if (ie && ie[1] >= 2) {
--			const struct ieee80211_ht_operation *ht_oper;
--			int sec;
--
--			ht_oper = (const void *) (ie + 2);
--			sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
--			if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
--				sec_chan = 1;
--			else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
--				sec_chan = -1;
-+
-+		hw_mode = ieee80211_freq_to_chan(bss->freq, &channel);
-+		is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
-+			hw_mode == HOSTAPD_MODE_IEEE80211B;
-+
-+		wpa_drv_channel_info(wpa_s, &ci);
-+		center_freq1 = ci.center_frq1;
-+
-+		if (bss->freq != center_freq1) {
-+			if (is_24ghz)
-+				sec_chan = (bss->freq < center_freq1) ? 1 : -1;
-+			else
-+				sec_chan = (bss->freq / 20) & 1 ? 1 : -1;
-+		}
-+
-+		if (ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
-+						  sec_chan, &op_class, &channel))
-+			return NULL;
-+
-+		ch_width = op_class_to_ch_width(op_class);
-+		if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
-+		    (center_freq1 == 6265) || center_freq1 == 6585 ||
-+		     center_freq1 == 6905) {
-+			/* Bandwidth 320 MHz-2 */
-+			bw320_offset = 2;
- 		}
- 
- 		ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
- 		ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
-+		ucv_object_add(ret, "ch_width", ucv_int64_new(ch_width));
-+		ucv_object_add(ret, "bw320_offset", ucv_int64_new(bw320_offset));
- 	}
- 
- #ifdef CONFIG_MESH
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0070-mtk-hostapd-Add-support-for-updating-background-chan.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0070-mtk-hostapd-Add-support-for-updating-background-chan.patch
deleted file mode 100644
index d1800fe..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0070-mtk-hostapd-Add-support-for-updating-background-chan.patch
+++ /dev/null
@@ -1,339 +0,0 @@
-From 0455150c89b046a3ebd81134527ff4cae5025f3d Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 5 Jul 2023 10:25:01 +0800
-Subject: [PATCH 070/104] mtk: hostapd: Add support for updating background
- channel by driver
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c                       | 107 ++++++++++++++++++++++++++++-
- src/ap/dfs.h                       |   3 +
- src/ap/drv_callbacks.c             |  22 ++++++
- src/ap/hostapd.h                   |   5 ++
- src/drivers/driver.h               |  12 ++++
- src/drivers/driver_nl80211_event.c |   6 ++
- src/drivers/nl80211_copy.h         |   6 ++
- 7 files changed, 160 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 7adaf81ac..e39f3c180 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -816,11 +816,14 @@ 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);
-+	int width = iface->radar_background.new_chwidth;
- 
- 	if (!dfs_use_radar_background(iface))
- 		return;
- 
-+	if (!width)
-+		width = hostapd_get_oper_chwidth(iface->conf);
-+
- 	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))
-@@ -985,6 +988,15 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- 		iface->radar_background.temp_ch = 1;
- 		return 1;
- 	} else if (dfs_use_radar_background(iface)) {
-+		/*
-+		 * AP is going to perform CAC, so reset temp_ch to 0,
-+		 * when dedicated rx has already started CAC.
-+		 */
-+		if (iface->radar_background.cac_started) {
-+			iface->radar_background.temp_ch = 0;
-+			return 0;
-+		}
-+
- 		if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
- 			channel_type = DFS_ANY_CHANNEL;
- 
-@@ -1125,6 +1137,8 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
- 	 * ch_switch_notify event is received */
- 	wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
- 
-+	hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
-+
- 	return 0;
- }
- 
-@@ -1176,6 +1190,9 @@ static void hostapd_dfs_update_background_chain(struct hostapd_iface *iface)
- 	iface->radar_background.secondary_channel = sec;
- 	iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
- 	iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
-+	/* if main channel do not require dfs, then set temp_ch = 1 */
-+	if (!hostapd_is_dfs_required(iface))
-+		iface->radar_background.temp_ch = 1;
- 
- 	wpa_printf(MSG_DEBUG,
- 		   "%s: setting background chain to chan %d (%d MHz)",
-@@ -1198,6 +1215,10 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
- 	u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
- 	int ret;
- 
-+	if (iface->radar_background.new_chwidth) {
-+		hostapd_set_oper_chwidth(iface->conf, iface->radar_background.new_chwidth);
-+		iface->radar_background.new_chwidth = 0;
-+	}
- 	ret = hostapd_dfs_request_channel_switch(iface, iface->radar_background.channel,
- 						 iface->radar_background.freq,
- 						 iface->radar_background.secondary_channel,
-@@ -1220,6 +1241,52 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
- }
- 
- 
-+static void
-+hostapd_dfs_background_expand(struct hostapd_iface *iface, int chan_width)
-+{
-+	struct hostapd_hw_modes *mode = iface->current_mode;
-+	struct hostapd_channel_data *chan;
-+	int i, channel, width = channel_width_to_int(chan_width);
-+
-+	if (iface->conf->channel - iface->radar_background.channel == width / 5)
-+		channel = iface->radar_background.channel;
-+	else if (iface->radar_background.channel - iface->conf->channel == width / 5)
-+		channel = iface->conf->channel;
-+	else
-+		return;
-+
-+	for (i = 0; i < mode->num_channels; i++) {
-+		chan = &mode->channels[i];
-+		if (chan->chan == channel)
-+			break;
-+	}
-+
-+	if (i == mode->num_channels || !dfs_is_chan_allowed(chan, width / 10))
-+		return;
-+
-+	switch (chan_width) {
-+	case CHAN_WIDTH_20_NOHT:
-+	case CHAN_WIDTH_20:
-+		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_USE_HT;
-+		break;
-+	case CHAN_WIDTH_40:
-+		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_80MHZ;
-+		break;
-+	case CHAN_WIDTH_80:
-+		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_160MHZ;
-+		break;
-+	default:
-+		return;
-+	}
-+
-+	iface->radar_background.freq = channel * 5 + 5000;
-+	iface->radar_background.channel = channel;
-+	iface->radar_background.centr_freq_seg0_idx = channel + width / 5 - 2;
-+	iface->radar_background.secondary_channel = 1;
-+	iface->radar_background.expand_ch = 0;
-+}
-+
-+
- int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
- 			     int ht_enabled, int chan_offset, int chan_width,
- 			     int cf1, int cf2)
-@@ -1253,6 +1320,10 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
- 					return 0;
- 
- 				iface->radar_background.temp_ch = 0;
-+
-+				if (iface->radar_background.expand_ch)
-+					hostapd_dfs_background_expand(iface, chan_width);
-+
- 				return hostapd_dfs_start_channel_switch_background(iface);
- 			}
- 
-@@ -1283,6 +1354,8 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
- 		}
- 	} else if (hostapd_dfs_is_background_event(iface, freq)) {
- 		iface->radar_background.cac_started = 0;
-+		iface->radar_background.temp_ch = 0;
-+		iface->radar_background.expand_ch = 0;
- 		hostapd_dfs_update_background_chain(iface);
- 	}
- 
-@@ -1415,6 +1488,9 @@ hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
- 	    iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
- 		return 0;
- 
-+	iface->radar_background.temp_ch = 0;
-+	iface->radar_background.expand_ch = 0;
-+
- 	/* Check if CSA in progress */
- 	if (hostapd_csa_in_progress(iface))
- 		return 0;
-@@ -1649,6 +1725,35 @@ int hostapd_is_dfs_required(struct hostapd_iface *iface)
- }
- 
- 
-+int hostapd_dfs_background_chan_update(struct hostapd_iface *iface, int freq,
-+				       int ht_enabled, int chan_offset, int chan_width,
-+				       int cf1, int cf2, bool expand)
-+{
-+	switch (chan_width) {
-+	case CHAN_WIDTH_80:
-+		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_80MHZ;
-+		break;
-+	case CHAN_WIDTH_160:
-+		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_160MHZ;
-+		break;
-+	default:
-+		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_USE_HT;
-+		break;
-+	};
-+
-+	iface->radar_background.freq = freq;
-+	iface->radar_background.channel = (freq - 5000) / 5;
-+	iface->radar_background.centr_freq_seg0_idx = (cf1 - 5000) / 5;
-+	iface->radar_background.centr_freq_seg1_idx = cf2 ? (cf2 - 5000) / 5 : 0;
-+	if (expand) {
-+		iface->radar_background.temp_ch = 1;
-+		iface->radar_background.expand_ch = 1;
-+	}
-+
-+	return 0;
-+}
-+
-+
- int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
- 			  int ht_enabled, int chan_offset, int chan_width,
- 			  int cf1, int cf2)
-diff --git a/src/ap/dfs.h b/src/ap/dfs.h
-index 25ba29ca1..a1a2be5ec 100644
---- a/src/ap/dfs.h
-+++ b/src/ap/dfs.h
-@@ -30,6 +30,9 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
- int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
- 			     int ht_enabled,
- 			     int chan_offset, int chan_width, int cf1, int cf2);
-+int hostapd_dfs_background_chan_update(struct hostapd_iface *iface, int freq,
-+				       int ht_enabled, int chan_offset, int chan_width,
-+				       int cf1, int cf2, bool expand);
- int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
- 				 int ht_enabled, int chan_offset, int chan_width,
- 				 int cf1, int cf2, u32 state);
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index caa171474..2d946afd6 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -2226,6 +2226,18 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
- 			      radar->cf1, radar->cf2);
- }
- 
-+
-+static void hostapd_event_dfs_background_chan_update(struct hostapd_data *hapd,
-+						     struct dfs_event *radar, bool expand)
-+{
-+	wpa_printf(MSG_DEBUG, "DFS background channel %s to %d MHz",
-+		   expand ? "expand" : "update", radar->freq);
-+	hostapd_dfs_background_chan_update(hapd->iface, radar->freq, radar->ht_enabled,
-+					   radar->chan_offset, radar->chan_width,
-+					   radar->cf1, radar->cf2, expand);
-+}
-+
-+
- static void hostapd_event_dfs_sta_cac_skipped(struct hostapd_data *hapd,
- 					      struct dfs_event *radar)
- {
-@@ -2610,6 +2622,16 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
- 		hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
- 		hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
- 		break;
-+	case EVENT_DFS_BACKGROUND_CHAN_UPDATE:
-+		if (!data)
-+			break;
-+		hostapd_event_dfs_background_chan_update(hapd, &data->dfs_event, false);
-+		break;
-+	case EVENT_DFS_BACKGROUND_CHAN_EXPAND:
-+		if (!data)
-+			break;
-+		hostapd_event_dfs_background_chan_update(hapd, &data->dfs_event, true);
-+		break;
- 	case EVENT_DFS_STA_CAC_SKIPPED:
- 		if (!data)
- 			break;
-diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
-index 1e4113459..5b37be87b 100644
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -640,6 +640,11 @@ struct hostapd_iface {
- 		unsigned int temp_ch:1;
- 		/* CAC started on radar offchain */
- 		unsigned int cac_started:1;
-+		/* Main chain should expand its width according to the
-+		 * current offchain channel after CAC detection on radar offchain.
-+		 */
-+		unsigned int expand_ch:1;
-+		int new_chwidth;
- 	} radar_background;
- 
- 	u16 hw_flags;
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 1c0c38e24..4e3dc9bdb 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5960,6 +5960,18 @@ enum wpa_event_type {
- 	 * The channel in the notification is now marked as usable.
- 	 */
- 	EVENT_DFS_STA_CAC_EXPIRED,
-+
-+	/**
-+	 * EVENT_DFS_BACKGROUND_CHAN_UPDATE - Notification that background
-+	 * channel has been updated.
-+	 */
-+	EVENT_DFS_BACKGROUND_CHAN_UPDATE,
-+
-+	/**
-+	 * EVENT_DFS_BACKGROUND_CHAN_EXPAND - Notification that background
-+	 * channel has been updated and operating channel should expand its width.
-+	 */
-+	EVENT_DFS_BACKGROUND_CHAN_EXPAND,
- };
- 
- 
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 7889930a0..6631285bf 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -2529,6 +2529,12 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
- 	case NL80211_RADAR_CAC_STARTED:
- 		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
- 		break;
-+	case NL80211_RADAR_BACKGROUND_CHAN_UPDATE:
-+		wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_UPDATE, &data);
-+		break;
-+	case NL80211_RADAR_BACKGROUND_CHAN_EXPAND:
-+		wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_EXPAND, &data);
-+		break;
- 	case NL80211_RADAR_STA_CAC_SKIPPED:
- 		wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
- 		break;
-diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
-index 8917d565b..c56954306 100644
---- a/src/drivers/nl80211_copy.h
-+++ b/src/drivers/nl80211_copy.h
-@@ -6699,6 +6699,10 @@ enum nl80211_smps_mode {
-  *	applicable for ETSI dfs domain where pre-CAC is valid for ever.
-  * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
-  *	should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
-+ * @NL80211_RADAR_BACKGROUND_CHAN_UPDATE: background channel is updated by the
-+ *	driver.
-+ * @NL80211_RADAR_BACKGROUND_CHAN_EXPAND: background channel is updated by the
-+ *	driver and required to expand main operating channel.
-  * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
-  *	when receiving CSA/assoc resp
-  * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
-@@ -6711,6 +6715,8 @@ enum nl80211_radar_event {
- 	NL80211_RADAR_NOP_FINISHED,
- 	NL80211_RADAR_PRE_CAC_EXPIRED,
- 	NL80211_RADAR_CAC_STARTED,
-+	NL80211_RADAR_BACKGROUND_CHAN_UPDATE,
-+	NL80211_RADAR_BACKGROUND_CHAN_EXPAND,
- 	NL80211_RADAR_STA_CAC_SKIPPED,
- 	NL80211_RADAR_STA_CAC_EXPIRED,
- };
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0070-mtk-hostapd-add-wds-mlo-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0070-mtk-hostapd-add-wds-mlo-support.patch
new file mode 100644
index 0000000..293384f
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0070-mtk-hostapd-add-wds-mlo-support.patch
@@ -0,0 +1,203 @@
+From 89cea0c904dfce8fca45a3d721f2871c32402e05 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Tue, 16 Jan 2024 16:22:17 +0800
+Subject: [PATCH 070/126] mtk: hostapd: add wds mlo support
+
+1. Add mld_assoc_sta to get the primary sta_info.
+2. Find hapd according to mld address.
+
+The latest get_hapd_bssid return hapd only if link id is matched.
+However,the hostapd_rx_from_unknown_sta does not have link
+information so it cannot get hapd.
+
+Modify get_hapd_bssid to ignore link id when link id is -1.
+
+Without this patch, wds mode cannot work and the AP would not be
+aware that station is using 4 address.
+
+Transmit correct hapd by i802_bss->ctx to EVENT_RX_FROM_UNKNOWN handler.
+Without this patch, AP cannot setup AP_VLAN interface in mlo + legacy case.
+In mlo + legacy case, if wds null is sent to legacy AP, it cannot get correct
+hapd according drv->ctx because drv->ctx and legacy AP's hapd may not under
+the same hostapd_iface.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ src/ap/drv_callbacks.c             |  7 ++++---
+ src/ap/ieee802_11.c                |  8 ++++++++
+ src/ap/sta_info.c                  |  6 +++++-
+ src/ap/sta_info.h                  |  1 +
+ src/drivers/driver.h               |  3 +++
+ src/drivers/driver_nl80211.c       | 14 ++++++++++++++
+ src/drivers/driver_nl80211_event.c |  2 +-
+ 7 files changed, 36 insertions(+), 5 deletions(-)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 1fa27bf80..1107fd70e 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -1798,6 +1798,7 @@ switch_link_scan(struct hostapd_data *hapd, u64 scan_cookie)
+ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+ 					    const u8 *bssid, int link_id)
+ {
++	struct hostapd_data *ret = NULL;
+ 	size_t i;
+ 
+ 	if (bssid == NULL)
+@@ -1820,7 +1821,7 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+ 		if (ether_addr_equal(bssid, hapd->own_addr) ||
+ 		    (hapd->conf->mld_ap &&
+ 		     ether_addr_equal(bssid, hapd->mld->mld_addr) &&
+-		     link_id == hapd->mld_link_id))
++		     (link_id == hapd->mld_link_id || link_id == -1)))
+ 			return hapd;
+ 
+ 		if (!hapd->conf->mld_ap)
+@@ -1832,13 +1833,13 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+ 
+ 			if (ether_addr_equal(bssid, p_hapd->own_addr) ||
+ 			    (ether_addr_equal(bssid, p_hapd->mld->mld_addr) &&
+-			     link_id == p_hapd->mld_link_id))
++			     (link_id == p_hapd->mld_link_id || link_id == -1)))
+ 				return p_hapd;
+ 		}
+ #endif /* CONFIG_IEEE80211BE */
+ 	}
+ 
+-	return NULL;
++	return ret;
+ }
+ 
+ 
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index c6676b754..d26b50031 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -3183,6 +3183,7 @@ static void handle_auth(struct hostapd_data *hapd,
+ 
+ 			ap_sta_set_mld(sta, true);
+ 			sta->mld_assoc_link_id = link_id;
++			sta->mld_assoc_sta = sta;
+ 
+ 			/*
+ 			 * Set the MLD address as the station address and the
+@@ -4572,6 +4573,7 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
+ 
+ 	sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
+ 	sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
++	sta->mld_assoc_sta = origin_sta;
+ 
+ 	status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
+ 	if (status != WLAN_STATUS_SUCCESS) {
+@@ -7037,6 +7039,12 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+ 	struct sta_info *sta;
+ 
+ 	sta = ap_get_sta(hapd, src);
++
++#ifdef CONFIG_IEEE80211BE
++	if (sta && sta->mld_info.mld_sta)
++		sta = sta->mld_assoc_sta;
++#endif
++
+ 	if (sta &&
+ 	    ((sta->flags & WLAN_STA_ASSOC) ||
+ 	     ((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) {
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index d4f4eb913..ea34d347f 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -74,6 +74,7 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+ 	while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
+ 		s = s->hnext;
+ 
++#ifdef CONFIG_IEEE80211BE
+ 	if (hapd->conf->mld_ap && !s) {
+ 		u8 link_id;
+ 
+@@ -84,10 +85,13 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+ 				continue;
+ 
+ 			for (s = h->sta_list; s; s = s->next)
+-				if (!os_memcmp(s->setup_link_addr, sta, 6))
++				if ((!os_memcmp(s->setup_link_addr, sta, 6) ||
++				     !os_memcmp(s->addr, sta, 6)) &&
++				     s->flags & WLAN_STA_ASSOC)
+ 					return s;
+ 		}
+ 	}
++#endif
+ 
+ 	return s;
+ }
+diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
+index 6b88799e2..67b97671a 100644
+--- a/src/ap/sta_info.h
++++ b/src/ap/sta_info.h
+@@ -328,6 +328,7 @@ struct sta_info {
+ #ifdef CONFIG_IEEE80211BE
+ 	struct mld_info mld_info;
+ 	u8 mld_assoc_link_id;
++	struct sta_info *mld_assoc_sta;
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ 	u16 max_idle_period; /* if nonzero, the granted BSS max idle period in
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 47f4f56c0..ec00c36e4 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5366,6 +5366,9 @@ struct wpa_driver_ops {
+ 	 * @pp_mode: Value is defined in enum pp_mode
+ 	 */
+ 	int (*pp_mode_set)(void *priv, const u8 pp_mode);
++#ifdef CONFIG_IEEE80211BE
++	int (*get_mld_addr)(void *priv, u8 *addr);
++#endif
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index dec358fba..8f7215d62 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -15209,6 +15209,17 @@ fail:
+ 	return -ENOBUFS;
+ }
+ 
++#ifdef CONFIG_IEEE80211BE
++static int nl80211_get_mld_addr(void *priv, u8 *addr)
++{
++	struct i802_bss *bss = priv;
++
++	os_memcpy(addr, bss->addr, ETH_ALEN);
++
++	return 0;
++}
++#endif
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+ 	.desc = "Linux nl80211/cfg80211",
+@@ -15389,4 +15400,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.amnt_dump = nl80211_amnt_dump,
+ 	.background_radar_mode = nl80211_background_radar_mode,
+ 	.pp_mode_set = nl80211_pp_mode_set,
++#ifdef CONFIG_IEEE80211BE
++	.get_mld_addr = nl80211_get_mld_addr,
++#endif
+ };
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 7f5a3d892..07af6be77 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -2575,7 +2575,7 @@ static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb,
+ 	event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]);
+ 	event.rx_from_unknown.wds = wds;
+ 
+-	wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
++	wpa_supplicant_event(bss->ctx, EVENT_RX_FROM_UNKNOWN, &event);
+ }
+ 
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0071-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0071-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch
deleted file mode 100644
index c2760d9..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0071-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch
+++ /dev/null
@@ -1,291 +0,0 @@
-From 5b2e33617bfafa8c6776e80b13c8747f0021a804 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 2 Aug 2023 19:00:34 +0800
-Subject: [PATCH 071/104] mtk: hostapd: add zwdfs mode ctrl for eagle efem
- hwits
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- hostapd/config_file.c             |  2 ++
- hostapd/ctrl_iface.c              | 30 +++++++++++++++++++++++++++
- src/ap/ap_config.h                |  6 ++++++
- src/ap/ap_drv_ops.c               | 14 +++++++++++++
- src/ap/ap_drv_ops.h               |  1 +
- src/ap/dfs.c                      |  6 ++++++
- src/common/mtk_vendor.h           | 12 +++++++++++
- src/drivers/driver.h              |  7 +++++++
- src/drivers/driver_nl80211.c      | 34 +++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |  1 +
- src/drivers/driver_nl80211_capa.c |  3 +++
- 11 files changed, 116 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index dadc8f108..9467a1128 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3576,6 +3576,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		conf->acs_exclude_6ghz_non_psc = atoi(pos);
- 	} else if (os_strcmp(buf, "enable_background_radar") == 0) {
- 		conf->enable_background_radar = atoi(pos);
-+	} else if (os_strcmp(buf, "background_radar_mode") == 0) {
-+		conf->background_radar_mode = atoi(pos);
- 	} else if (os_strcmp(buf, "min_tx_power") == 0) {
- 		int val = atoi(pos);
- 
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 581acc260..9b072d1b2 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4782,6 +4782,33 @@ hostapd_ctrl_iface_dump_amnt(struct hostapd_data *hapd, char *cmd,
- 		return pos - buf;
- }
- 
-+static int
-+hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cmd,
-+					     char *buf, size_t buflen)
-+{
-+	struct hostapd_iface *iface = hapd->iface;
-+	char *pos, *param;
-+
-+	param = os_strchr(cmd, ' ');
-+	if (!param)
-+		return -1;
-+	*param++ = '\0';
-+
-+	pos = os_strstr(param, "mode=");
-+	if (!pos)
-+		return -1;
-+
-+	if (os_strncmp(pos + 5, "cert", 4) == 0)
-+		iface->conf->background_radar_mode = BACKGROUND_RADAR_CERT_MODE;
-+	else if (os_strncmp(pos + 5, "normal", 6) == 0)
-+		iface->conf->background_radar_mode = BACKGROUND_RADAR_NORMAL_MODE;
-+
-+	if (hostapd_drv_background_radar_mode(hapd) < 0)
-+		return -1;
-+
-+	return os_snprintf(buf, buflen, "OK\n");
-+}
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -5423,6 +5450,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 		if (pos)
- 			*pos = ' ';
- 		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 23, reply, reply_size);
-+	} else if (os_strncmp(buf, "SET_BACKGROUND_RADAR_MODE", 25) == 0) {
-+		reply_len = hostapd_ctrl_iface_set_background_radar_mode(hapd, buf + 25,
-+									 reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 3827a8fc8..0b07be516 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1051,6 +1051,7 @@ struct hostapd_config {
- 	bool hw_mode_set;
- 	int acs_exclude_6ghz_non_psc;
- 	int enable_background_radar;
-+	int background_radar_mode;
- 	enum {
- 		LONG_PREAMBLE = 0,
- 		SHORT_PREAMBLE = 1
-@@ -1306,6 +1307,11 @@ enum three_wire_mode {
- 		NUM_THREE_WIRE_MODE - 1
- };
- 
-+enum background_radar_mode {
-+	BACKGROUND_RADAR_NORMAL_MODE,
-+	BACKGROUND_RADAR_CERT_MODE,
-+};
-+
- enum dfs_mode {
- 	DFS_DETECT_MODE_DISABLE,
- 	DFS_DETECT_MODE_AP_ENABLE,
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 116bc4ceb..2028e70fb 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1365,3 +1365,17 @@ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_
- 		return 0;
- 	return hapd->driver->amnt_dump(hapd->drv_priv, amnt_idx, amnt_dump_buf);
- }
-+
-+int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
-+{
-+	if (!hapd->driver || !hapd->driver->background_radar_mode ||
-+	    !(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_RADAR_BACKGROUND) ||
-+	    !hapd->iface->conf->enable_background_radar)
-+		return 0;
-+	if (hapd->iconf->background_radar_mode > BACKGROUND_RADAR_CERT_MODE) {
-+		wpa_printf(MSG_INFO, "Invalid value for background radar mode\n");
-+		return 0;
-+	}
-+	return hapd->driver->background_radar_mode(hapd->drv_priv,
-+						   hapd->iconf->background_radar_mode);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 84b41881a..f0e618bcc 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -168,6 +168,7 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
- 
- int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
- int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
-+int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index e39f3c180..b12290556 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -985,6 +985,9 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- 		if (res < 0)
- 			return res;
- 
-+		if (hostapd_drv_background_radar_mode(iface->bss[0]) < 0)
-+			return -1;
-+
- 		iface->radar_background.temp_ch = 1;
- 		return 1;
- 	} else if (dfs_use_radar_background(iface)) {
-@@ -1025,6 +1028,9 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- 		iface->radar_background.secondary_channel = sec;
- 		iface->radar_background.centr_freq_seg0_idx = cf1;
- 		iface->radar_background.centr_freq_seg1_idx = cf2;
-+
-+		if (hostapd_drv_background_radar_mode(iface->bss[0]) < 0)
-+			return -1;
- 	}
- 
- 	return 0;
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index e140de60b..5bc1e0444 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -16,6 +16,7 @@ enum mtk_nl80211_vendor_subcmds {
- 	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
- 	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
- 	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
-+	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
- };
- 
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -244,6 +245,17 @@ enum mtk_vendor_attr_bss_color_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
- };
- 
-+enum mtk_vendor_attr_background_radar_ctrl {
-+	MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL,
-+	MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
-+};
-+
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 4e3dc9bdb..863748d4f 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5319,6 +5319,13 @@ struct wpa_driver_ops {
- 	* @amnt_dump_buf: Buffer to print
- 	*/
- 	int (*amnt_dump)(void *priv, u8 amnt_idx, u8 *amnt_dump_buf);
-+
-+	/**
-+	 * background_radar_mode - set background radar mode
-+	 * @priv: Private driver interface data
-+	 * @background_radar_mode: background radar mode
-+	 */
-+	int (*background_radar_mode)(void *priv, u8 background_radar_mode);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index aeb755b11..e3f00b6d6 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -15052,6 +15052,39 @@ fail:
- 	return -ENOBUFS;
- }
- 
-+static int nl80211_background_radar_mode(void *priv, const u8 background_radar_mode)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	/* Prepare nl80211 cmd */
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_background_radar_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting background radar mode");
-+		return 0;
-+	}
-+
-+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL) ||
-+	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE, background_radar_mode)) {
-+		nlmsg_free(msg);
-+		return -ENOBUFS;
-+	}
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_cmd(drv, msg);
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to set background radar mode. ret=%d (%s) ",
-+			   ret, strerror(-ret));
-+	}
-+	return ret;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
- 	.desc = "Linux nl80211/cfg80211",
-@@ -15229,4 +15262,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.ap_trigtype = nl80211_ap_trigtype,
- 	.amnt_set = nl80211_amnt_set,
- 	.amnt_dump = nl80211_amnt_dump,
-+	.background_radar_mode = nl80211_background_radar_mode,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index adc1b9bf7..e9aae8d14 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -208,6 +208,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int mtk_bss_color_vendor_cmd_avail:1;
- 	unsigned int mtk_rfeatures_vendor_cmd_avail:1;
- 	unsigned int mtk_amnt_vendor_cmd_avail:1;
-+	unsigned int mtk_background_radar_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 38e83e42b..9bc98aae7 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1164,6 +1164,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
- 					drv->mtk_rfeatures_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL:
-+					drv->mtk_background_radar_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0071-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0071-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch
new file mode 100644
index 0000000..c62de68
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0071-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch
@@ -0,0 +1,28 @@
+From f4907a96f2e0494b03c7447ae2f5aac20dab61f8 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 1 Mar 2024 16:59:53 +0800
+Subject: [PATCH 071/126] mtk: hostapd: prevent getting non-MLD STA for other
+ links
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/sta_info.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index ea34d347f..44d98d5e0 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -87,7 +87,8 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+ 			for (s = h->sta_list; s; s = s->next)
+ 				if ((!os_memcmp(s->setup_link_addr, sta, 6) ||
+ 				     !os_memcmp(s->addr, sta, 6)) &&
+-				     s->flags & WLAN_STA_ASSOC)
++				     s->flags & WLAN_STA_ASSOC &&
++				     s->mld_info.mld_sta)
+ 					return s;
+ 		}
+ 	}
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0072-mtk-hostapd-add-support-enable-disable-preamble-punc.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0072-mtk-hostapd-add-support-enable-disable-preamble-punc.patch
deleted file mode 100644
index 4959fb4..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0072-mtk-hostapd-add-support-enable-disable-preamble-punc.patch
+++ /dev/null
@@ -1,375 +0,0 @@
-From 51377e7c81b4164be42de4a5c4c48ba53a638afe Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Thu, 21 Sep 2023 10:29:46 +0800
-Subject: [PATCH 072/104] mtk: hostapd: add support enable/disable preamble
- puncture from mtk vendor command
-
-This commit supports two ways to enable/disable preamble puncture
-feature.
-
-1. Add new hostapd configuration "pp_mode". The possible value could be
-1 to 3. When the value is 0, it means that the firmware will turn off
-the pp algorithm. When the value is 1, it means that the firmware will
-enable the pp algorithm, allowing the algorithm to determine whether pp
-could be applied on each txcmd. When the value is 2, it means that pp
-feature is manually configured by the user. Please noted that for
-current implementation, the default configuration is 0.
-
-2. $ hostapd_cli -i <intf_name> raw set_pp mode val
-The argument "val" could be 0 for PP feature disabled or 1 to configure
-PP feature as auto mode.
-
-This commit also let user check whether pp feature is enabled by
-hostapd_cli command. The usage shows as below:
-$ hostapd_cli -i <intf_name> raw get_pp mode
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- hostapd/config_file.c             | 12 +++++++
- hostapd/ctrl_iface.c              | 59 +++++++++++++++++++++++++++++++
- src/ap/ap_config.c                |  1 +
- src/ap/ap_config.h                |  7 ++++
- src/ap/ap_drv_ops.c               |  9 +++++
- src/ap/ap_drv_ops.h               |  1 +
- src/ap/hostapd.c                  |  2 ++
- src/common/mtk_vendor.h           | 12 +++++++
- src/drivers/driver.h              |  6 ++++
- src/drivers/driver_nl80211.c      | 49 +++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |  1 +
- src/drivers/driver_nl80211_capa.c |  3 ++
- 12 files changed, 162 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 9467a1128..050ef290e 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5333,6 +5333,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 	} else if (os_strcmp(buf, "punct_bitmap") == 0) {
- 		if (get_u16(pos, line, &conf->punct_bitmap))
- 			return 1;
-+		conf->punct_bitmap = atoi(pos);
-+		conf->pp_mode = PP_MANUAL_MODE;
- 	} else if (os_strcmp(buf, "punct_acs_threshold") == 0) {
- 		int val = atoi(pos);
- 
-@@ -5415,6 +5417,16 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 			return 1;
- 		}
- 		conf->amsdu = val;
-+	} else if (os_strcmp(buf, "pp_mode") == 0) {
-+		int val = atoi(pos);
-+
-+		if ((val != PP_MANUAL_MODE && conf->punct_bitmap) ||
-+		    val < PP_DISABLE || val > PP_MANUAL_MODE) {
-+			wpa_printf(MSG_ERROR, "Line %d: invalid pp_mode value",
-+				   line);
-+			return 1;
-+		}
-+		conf->pp_mode = (u8) val;
- 	} else {
- 		wpa_printf(MSG_ERROR,
- 			   "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 9b072d1b2..c9b53c64e 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4809,6 +4809,59 @@ hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cm
- 	return os_snprintf(buf, buflen, "OK\n");
- }
- 
-+static int
-+hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
-+			  size_t buflen)
-+{
-+	char *pos, *config, *value;
-+
-+	config = cmd;
-+	pos = os_strchr(config, ' ');
-+	if (pos == NULL)
-+		return -1;
-+	*pos++ = '\0';
-+
-+	if (pos == NULL)
-+		return -1;
-+	value = pos;
-+
-+	if (os_strcmp(config, "mode") == 0) {
-+		int val = atoi(value);
-+
-+		if (val < PP_DISABLE || val > PP_AUTO_MODE) {
-+			wpa_printf(MSG_ERROR, "Invalid value for set_pp");
-+			return -1;
-+		}
-+		hapd->iconf->pp_mode = (u8) val;
-+		if (hostapd_drv_pp_mode_set(hapd) != 0)
-+			return -1;
-+	} else {
-+		wpa_printf(MSG_ERROR,
-+			   "Unsupported parameter %s for set_pp", config);
-+		return -1;
-+	}
-+	return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+static int
-+hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
-+			  size_t buflen)
-+{
-+	char *pos, *end;
-+
-+	pos = buf;
-+	end = buf + buflen;
-+
-+	if (os_strcmp(cmd, "mode") == 0) {
-+		return os_snprintf(pos, end - pos, "pp_mode: %d\n",
-+				   hapd->iconf->pp_mode);
-+	} else {
-+		wpa_printf(MSG_ERROR,
-+			   "Unsupported parameter %s for get_pp", cmd);
-+		return -1;
-+	}
-+}
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -5442,6 +5495,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 	} else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
- 		reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
- 							reply, reply_size);
-+	} else if (os_strncmp(buf, "set_pp", 6) == 0) {
-+		reply_len = hostapd_ctrl_iface_set_pp(hapd, buf + 7, reply,
-+						      reply_size);
-+	} else if (os_strncmp(buf, "get_pp", 6) == 0) {
-+		reply_len = hostapd_ctrl_iface_get_pp(hapd, buf + 7, reply,
-+						      reply_size);
- 	} else if (os_strncmp(buf, "set_muru_manual_config=", 23) == 0) {
- 		// Replace first ':' with a single space ' '
- 		char *pos = buf + 23;
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index ba1b2a7a3..056c38f73 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -310,6 +310,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- 	conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
- 	conf->ibf_enable = IBF_DEFAULT_ENABLE;
- 	conf->amsdu = 1;
-+	conf->pp_mode = PP_DISABLE;
- 
- 	hostapd_set_and_check_bw320_offset(conf, 0);
- 
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 0b07be516..40edcdaa7 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1293,6 +1293,7 @@ struct hostapd_config {
- 	u8 dfs_detect_mode;
- 	u8 amsdu;
- 	void *muru_config;
-+	u8 pp_mode;
- };
- 
- enum three_wire_mode {
-@@ -1345,6 +1346,12 @@ enum mtk_vendor_attr_edcca_ctrl_mode {
- 	EDCCA_CTRL_NUM,
- };
- 
-+enum pp_mode {
-+	PP_DISABLE = 0,
-+	PP_AUTO_MODE,
-+	PP_MANUAL_MODE,
-+};
-+
- #define EDCCA_DEFAULT_COMPENSATION -6
- #define EDCCA_MIN_COMPENSATION -126
- #define EDCCA_MAX_COMPENSATION 126
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 2028e70fb..c71cfe1bd 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1379,3 +1379,12 @@ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
- 	return hapd->driver->background_radar_mode(hapd->drv_priv,
- 						   hapd->iconf->background_radar_mode);
- }
-+
-+int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
-+{
-+	if (!hapd->driver || !hapd->driver->pp_mode_set ||
-+	    hapd->iconf->pp_mode > PP_AUTO_MODE)
-+		return 0;
-+	return hapd->driver->pp_mode_set(hapd->drv_priv,
-+					 hapd->iconf->pp_mode);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index f0e618bcc..ef61001e5 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -169,6 +169,7 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
- int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
- int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
- int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
-+int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 0d4b79b48..cdbf81e38 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2708,6 +2708,8 @@ dfs_offload:
- 		goto fail;
- 	if (hostapd_drv_amsdu_ctrl(hapd) < 0)
- 		goto fail;
-+	if (hostapd_drv_pp_mode_set(hapd) < 0)
-+		goto fail;
- 
- 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- 		   iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 5bc1e0444..6275c141d 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -17,6 +17,7 @@ enum mtk_nl80211_vendor_subcmds {
- 	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
- 	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
- 	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
-+	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
- };
- 
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -256,6 +257,17 @@ enum mtk_vendor_attr_background_radar_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
- };
- 
-+enum mtk_vendor_attr_pp_ctrl {
-+	MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_PP_MODE,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_PP_CTRL,
-+	MTK_VENDOR_ATTR_PP_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
-+};
-+
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 863748d4f..be0e89ba3 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5326,6 +5326,12 @@ struct wpa_driver_ops {
- 	 * @background_radar_mode: background radar mode
- 	 */
- 	int (*background_radar_mode)(void *priv, u8 background_radar_mode);
-+	/**
-+	 * pp_mode_set - Set preamble puncture operation mode
-+	 * @priv: Private driver interface data
-+	 * @pp_mode: Value is defined in enum pp_mode
-+	 */
-+	int (*pp_mode_set)(void *priv, const u8 pp_mode);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index e3f00b6d6..b47ab07ea 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -156,6 +156,11 @@ amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
- 	[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
- };
- 
-+static struct nla_policy
-+pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
-+	[MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
-+};
-+
- static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
- {
- 	struct nl_sock *handle;
-@@ -15085,6 +15090,49 @@ static int nl80211_background_radar_mode(void *priv, const u8 background_radar_m
- 	return ret;
- }
- 
-+static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_pp_vendor_cmd_avail) {
-+		wpa_printf(MSG_DEBUG,
-+			   "nl80211: Driver does not support setting preamble puncture");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_PP_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+	if (!data)
-+		goto fail;
-+
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_PP_MODE, pp_mode);
-+
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_cmd(drv, msg);
-+
-+	if (ret)
-+		wpa_printf(MSG_ERROR, "Failed to set pp_enable. ret=%d (%s)",
-+			   ret, strerror(-ret));
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
- 	.desc = "Linux nl80211/cfg80211",
-@@ -15263,4 +15311,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.amnt_set = nl80211_amnt_set,
- 	.amnt_dump = nl80211_amnt_dump,
- 	.background_radar_mode = nl80211_background_radar_mode,
-+	.pp_mode_set = nl80211_pp_mode_set,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index e9aae8d14..707bb7fe4 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -209,6 +209,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int mtk_rfeatures_vendor_cmd_avail:1;
- 	unsigned int mtk_amnt_vendor_cmd_avail:1;
- 	unsigned int mtk_background_radar_vendor_cmd_avail:1;
-+	unsigned int mtk_pp_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 9bc98aae7..ba3c0817b 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1167,6 +1167,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL:
- 					drv->mtk_background_radar_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_PP_CTRL:
-+					drv->mtk_pp_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0072-mtk-hostapd-specify-link-id-for-unicast-DEAUTH.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0072-mtk-hostapd-specify-link-id-for-unicast-DEAUTH.patch
new file mode 100644
index 0000000..1deaede
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0072-mtk-hostapd-specify-link-id-for-unicast-DEAUTH.patch
@@ -0,0 +1,37 @@
+From f9b54baaa5037a174963b2dc255a16e60a9cade8 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 27 Feb 2024 15:04:35 +0800
+Subject: [PATCH 072/126] mtk: hostapd: specify link id for unicast DEAUTH
+
+When deauthenticating the STA, hostapd should specifies the setup link
+of the target STA so that the TX status of the DEAUTH can be forwarded
+to the correct link (BSS).
+
+(The original gerrit somehow disappears, so I commit it again)
+(https://gerrit.mediatek.inc/c/gateway/WiFi7/mac80211/hostapd/+/8715613)
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ap_drv_ops.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index f51d5be8e..34c2ff211 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -896,7 +896,11 @@ int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+ 	if (hapd->conf->mld_ap) {
+ 		struct sta_info *sta = ap_get_sta(hapd, addr);
+ 
+-		link_id = hapd->mld_link_id;
++		if (sta)
++			link_id = sta->mld_assoc_link_id;
++		else
++			link_id = hapd->mld_link_id;
++
+ 		if (ap_sta_is_mld(hapd, sta))
+ 			own_addr = hapd->mld->mld_addr;
+ 	}
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0073-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0073-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch
deleted file mode 100644
index 31d1951..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0073-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch
+++ /dev/null
@@ -1,247 +0,0 @@
-From a7adff7d782e329e9f8b1063f78616757f944d51 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Wed, 22 Nov 2023 21:41:34 +0800
-Subject: [PATCH 073/104] mtk: hostapd: add no_beacon vendor command for cert
-
-Add the vendor command to disable/enable beacon
-
-[Usage]
-hostapd_cli -i <interface> no_beacon <value>
- <value>
- 0: enable beacon
- 1: disable beacon
-
-Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
----
- hostapd/ctrl_iface.c              | 21 +++++++++++++++++++
- hostapd/hostapd_cli.c             |  7 +++++++
- src/ap/ap_drv_ops.c               |  8 ++++++++
- src/ap/ap_drv_ops.h               |  1 +
- src/common/mtk_vendor.h           | 12 +++++++++++
- src/drivers/driver.h              |  7 +++++++
- src/drivers/driver_nl80211.c      | 34 +++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |  1 +
- src/drivers/driver_nl80211_capa.c |  3 +++
- 9 files changed, 94 insertions(+)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index c9b53c64e..0fded7ed4 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4862,6 +4862,24 @@ hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
- 	}
- }
- 
-+static int
-+hostapd_ctrl_iface_disable_beacon(struct hostapd_data *hapd, char *value,
-+				  char *buf, size_t buflen)
-+{
-+	int disable_beacon = atoi(value);
-+
-+	if (disable_beacon < 0) {
-+		wpa_printf(MSG_ERROR, "Invalid value for beacon ctrl");
-+		return -1;
-+	}
-+
-+	if (hostapd_drv_beacon_ctrl(hapd, !disable_beacon) == 0)
-+		return os_snprintf(buf, buflen, "OK\n");
-+	else
-+		return -1;
-+
-+}
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -5512,6 +5530,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 	} else if (os_strncmp(buf, "SET_BACKGROUND_RADAR_MODE", 25) == 0) {
- 		reply_len = hostapd_ctrl_iface_set_background_radar_mode(hapd, buf + 25,
- 									 reply, reply_size);
-+	} else if (os_strncmp(buf, "NO_BEACON ", 10) == 0) {
-+		reply_len = hostapd_ctrl_iface_disable_beacon(hapd, buf + 10, reply,
-+							      reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index e0b175386..7e4485cb8 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1464,6 +1464,11 @@ static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
- 	return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
- }
- 
-+static int hostapd_cli_cmd_disable_beacon(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "NO_BEACON", 1, argc, argv);
-+}
- 
- #ifdef CONFIG_DPP
- 
-@@ -1871,6 +1876,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 		"<value> [0-15] bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0)"},
- 	{ "get_mu", hostapd_cli_cmd_get_mu, NULL,
- 		" = show mu onoff value in 0-15 bitmap"},
-+	{ "no_beacon", hostapd_cli_cmd_disable_beacon, NULL,
-+		"<value> 0: Enable beacon, 1: Disable beacon"},
- #ifdef CONFIG_DPP
- 	{ "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_drv_ops.c b/src/ap/ap_drv_ops.c
-index c71cfe1bd..d6bd157d8 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1388,3 +1388,11 @@ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
- 	return hapd->driver->pp_mode_set(hapd->drv_priv,
- 					 hapd->iconf->pp_mode);
- }
-+
-+int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
-+{
-+	if (!hapd->driver || !hapd->driver->beacon_ctrl)
-+		return 0;
-+	return hapd->driver->beacon_ctrl(hapd->drv_priv, beacon_mode);
-+}
-+
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index ef61001e5..78e5c8d5a 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -170,6 +170,7 @@ int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_ma
- int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
- int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
- int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
-+int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 6275c141d..5531802b8 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -18,6 +18,7 @@ enum mtk_nl80211_vendor_subcmds {
- 	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
- 	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
- 	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
-+	MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
- };
- 
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -268,6 +269,17 @@ enum mtk_vendor_attr_pp_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
- };
- 
-+enum mtk_vendor_attr_beacon_ctrl {
-+	MTK_VENDOR_ATTR_BEACON_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_BEACON_CTRL_MODE,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_BEACON_CTRL,
-+	MTK_VENDOR_ATTR_BEACON_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
-+};
-+
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index be0e89ba3..332a51c55 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5243,6 +5243,13 @@ struct wpa_driver_ops {
- 	 int (*mu_ctrl)(void *priv, u8 mode, void *config);
- 	 int (*mu_dump)(void *priv, u8 *mu_onoff);
- 
-+	/**
-+	 * beacon_ctrl - ctrl on off for beacon
-+	 * @priv: Private driver interface data
-+	 *
-+	 */
-+	int (*beacon_ctrl)(void *priv, u8 beacon_mode);
-+
- 	/**
- 	 * three_wire_ctrl - set three_wire_ctrl mode
- 	 * @priv: Private driver interface data
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index b47ab07ea..e588e7538 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -14111,6 +14111,39 @@ static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
- 
- 	return ret;
- }
-+static int nl80211_beacon_ctrl(void *priv, u8 beacon_mode)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_beacon_ctrl_vendor_cmd_avail) {
-+		wpa_printf(MSG_ERROR,
-+			   "nl80211: Driver does not support setting beacon control");
-+		return 0;
-+	}
-+
-+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL) ||
-+		!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+		nla_put_u8(msg, MTK_VENDOR_ATTR_BEACON_CTRL_MODE, beacon_mode)) {
-+		nlmsg_free(msg);
-+		return -ENOBUFS;
-+	}
-+
-+	nla_nest_end(msg, data);
-+
-+	ret = send_and_recv_cmd(drv, msg);
-+
-+	if (ret)
-+		wpa_printf(MSG_ERROR, "Failed to set beacon_ctrl. ret=%d (%s)", ret, strerror(-ret));
-+
-+	return ret;
-+}
-+
- #endif /* CONFIG_IEEE80211AX */
- 
- 
-@@ -15281,6 +15314,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.set_4addr_mode = nl80211_set_4addr_mode,
- 	.mu_ctrl = nl80211_mu_ctrl,
- 	.mu_dump = nl80211_mu_dump,
-+	.beacon_ctrl = nl80211_beacon_ctrl,
- #ifdef CONFIG_DPP
- 	.dpp_listen = nl80211_dpp_listen,
- #endif /* CONFIG_DPP */
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 707bb7fe4..9866c221c 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -210,6 +210,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int mtk_amnt_vendor_cmd_avail:1;
- 	unsigned int mtk_background_radar_vendor_cmd_avail:1;
- 	unsigned int mtk_pp_vendor_cmd_avail:1;
-+	unsigned int mtk_beacon_ctrl_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index ba3c0817b..f3e3d52e2 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1170,6 +1170,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_PP_CTRL:
- 					drv->mtk_pp_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL :
-+					drv->mtk_beacon_ctrl_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0073-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0073-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch
new file mode 100644
index 0000000..0dd558d
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0073-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch
@@ -0,0 +1,36 @@
+From 2c8d9fc4050a47913a4e77fc6fad3118a59a2e01 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 14 Mar 2024 14:31:28 +0800
+Subject: [PATCH 073/126] mtk: hostapd: using MLD addr as SA/BSSID for
+ broadcast DEAUTH
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ap_drv_ops.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 34c2ff211..c7635cae6 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -901,7 +901,7 @@ int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+ 		else
+ 			link_id = hapd->mld_link_id;
+ 
+-		if (ap_sta_is_mld(hapd, sta))
++		if (ap_sta_is_mld(hapd, sta) || is_multicast_ether_addr(addr))
+ 			own_addr = hapd->mld->mld_addr;
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+@@ -922,7 +922,7 @@ int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+ 	if (hapd->conf->mld_ap) {
+ 		struct sta_info *sta = ap_get_sta(hapd, addr);
+ 
+-		if (ap_sta_is_mld(hapd, sta))
++		if (ap_sta_is_mld(hapd, sta) || is_multicast_ether_addr(addr))
+ 			own_addr = hapd->mld->mld_addr;
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0074-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0074-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch
deleted file mode 100644
index 06ee95c..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0074-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch
+++ /dev/null
@@ -1,64 +0,0 @@
-From 817f2a256b166e07d7b0abcee789976b47224429 Mon Sep 17 00:00:00 2001
-From: "amlendu.mishra" <amlendu.mishra@mediatek.com>
-Date: Wed, 13 Dec 2023 18:13:01 +0530
-Subject: [PATCH 074/104] mtk: hostapd: WPS added change to configure AP PIN
- lock timout
-
-added config paramter ap_pin_lockout_time to configure
-AP PIN timeout from hosatpd.conf
-
-Signed-off-by: amlendu.mishra <amlendu.mishra@mediatek.com>
----
- hostapd/config_file.c | 2 ++
- src/ap/ap_config.h    | 1 +
- src/ap/wps_hostapd.c  | 9 ++++++---
- 3 files changed, 9 insertions(+), 3 deletions(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 050ef290e..7bc19479d 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4191,6 +4191,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		bss->wps_independent = atoi(pos);
- 	} else if (os_strcmp(buf, "ap_setup_locked") == 0) {
- 		bss->ap_setup_locked = atoi(pos);
-+	} else if (os_strcmp(buf, "ap_pin_lockout_time") == 0) {
-+		bss->ap_pin_lockout_time = atoi(pos);
- 	} else if (os_strcmp(buf, "uuid") == 0) {
- 		if (uuid_str2bin(pos, bss->uuid)) {
- 			wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 40edcdaa7..7f48c71f5 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -493,6 +493,7 @@ struct hostapd_bss_config {
- #ifdef CONFIG_WPS
- 	int wps_independent;
- 	int ap_setup_locked;
-+	unsigned int ap_pin_lockout_time;
- 	u8 uuid[16];
- 	char *wps_pin_requests;
- 	char *device_name;
-diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
-index dfc5c3ecb..8a6fc42b2 100644
---- a/src/ap/wps_hostapd.c
-+++ b/src/ap/wps_hostapd.c
-@@ -768,9 +768,12 @@ static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
- 		eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
- 		wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely");
- 	} else if (!hapd->conf->ap_setup_locked) {
--		if (hapd->ap_pin_lockout_time == 0)
--			hapd->ap_pin_lockout_time = 60;
--		else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
-+		if (hapd->ap_pin_lockout_time == 0) {
-+			if (hapd->conf->ap_pin_lockout_time)
-+				hapd->ap_pin_lockout_time = hapd->conf->ap_pin_lockout_time;
-+			else
-+				hapd->ap_pin_lockout_time = 60;
-+		} else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
- 			 (hapd->ap_pin_failures % 3) == 0)
- 			hapd->ap_pin_lockout_time *= 2;
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0074-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0074-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch
new file mode 100644
index 0000000..ce90a5d
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0074-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch
@@ -0,0 +1,30 @@
+From 7e0e24bc6487a6664b6fa209758d00773ed38a8d Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Tue, 27 Feb 2024 15:32:06 +0800
+Subject: [PATCH 074/126] mtk: hostapd: support vht bfee sts can be up to 0x4
+
+Without this commit, the maximum vht bfee sts can only be 0x3. This commit
+support to read BF-ANTENNA-5 to set vht bfee sts capability as 4.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ hostapd/config_file.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 50d63e259..c1bc344dd 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -1190,6 +1190,9 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf,
+ 	if (os_strstr(capab, "[BF-ANTENNA-4]") &&
+ 	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+ 		conf->vht_capab |= (3 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
++	if (os_strstr(capab, "[BF-ANTENNA-5]") &&
++	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
++		conf->vht_capab |= (4 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
+ 	if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") &&
+ 	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
+ 		conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0075-hostapd-mtk-ACS-remove-chan-freq-list-check-when-sca.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0075-hostapd-mtk-ACS-remove-chan-freq-list-check-when-sca.patch
deleted file mode 100644
index 6a9f126..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0075-hostapd-mtk-ACS-remove-chan-freq-list-check-when-sca.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 76d8a7f29b56075df3ad756f65374b2b238c2120 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Tue, 23 Jan 2024 10:52:57 +0800
-Subject: [PATCH 075/104] hostapd: mtk: ACS: remove chan/freq list check when
- scan request and factor calculation
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/acs.c | 12 ------------
- 1 file changed, 12 deletions(-)
-
-diff --git a/src/ap/acs.c b/src/ap/acs.c
-index 4c4c750ab..cb4db7147 100644
---- a/src/ap/acs.c
-+++ b/src/ap/acs.c
-@@ -595,12 +595,6 @@ static void acs_survey_mode_interference_factor(
- 		    iface->conf->acs_exclude_dfs)
- 			continue;
- 
--		if (!is_in_chanlist(iface, chan))
--			continue;
--
--		if (!is_in_freqlist(iface, chan))
--			continue;
--
- 		if (chan->max_tx_power < iface->conf->min_tx_power)
- 			continue;
- 
-@@ -1358,12 +1352,6 @@ static int * acs_request_scan_add_freqs(struct hostapd_iface *iface,
- 		     iface->conf->acs_exclude_dfs))
- 			continue;
- 
--		if (!is_in_chanlist(iface, chan))
--			continue;
--
--		if (!is_in_freqlist(iface, chan))
--			continue;
--
- 		if (chan->max_tx_power < iface->conf->min_tx_power)
- 			continue;
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0075-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0075-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch
new file mode 100644
index 0000000..f6297dc
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0075-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch
@@ -0,0 +1,54 @@
+From e9192ac36cdeb2a3d2bfd979161c0016c19e1a5c Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Thu, 19 Oct 2023 14:08:50 +0800
+Subject: [PATCH 075/126] mtk: hostapd: fix issue that tx status handle with
+ unmatch hostapd_data.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ src/ap/ieee802_11.c                | 11 ++++++++++-
+ src/drivers/driver_nl80211_event.c |  2 +-
+ 2 files changed, 11 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index d26b50031..fbe3f582f 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -6614,11 +6614,20 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
+ #ifdef CONFIG_IEEE80211BE
+ 	if (ap_sta_is_mld(hapd, sta) &&
+ 	    hapd->mld_link_id != sta->mld_assoc_link_id) {
++		struct hostapd_data *temp_hapd = hapd;
++
+ 		/* See ieee80211_ml_link_sta_assoc_cb() for the MLD case */
+ 		wpa_printf(MSG_DEBUG,
+ 			   "%s: MLD: ignore on link station (%d != %d)",
+ 			   __func__, hapd->mld_link_id, sta->mld_assoc_link_id);
+-		return;
++
++		if (temp_hapd->conf->mld_ap && sta->mld_assoc_link_id >= 0) {
++			struct hostapd_data *link_bss;
++
++			link_bss = hostapd_mld_get_link_bss(temp_hapd, sta->mld_assoc_link_id);
++			if (link_bss)
++				hapd = link_bss;
++		}
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 07af6be77..635401564 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -1381,7 +1381,7 @@ static void mlme_event_mgmt(struct i802_bss *bss,
+ 	event.rx_mgmt.ctx = bss->ctx;
+ 	event.rx_mgmt.link_id = link_id;
+ 
+-	wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
++	wpa_supplicant_event(bss->ctx, EVENT_RX_MGMT, &event);
+ }
+ 
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0076-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0076-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch
deleted file mode 100644
index d4e52f8..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0076-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 29b7ebf83cad69c48012eb5a03eb01a2a9fbfcab Mon Sep 17 00:00:00 2001
-From: "fancy.liu" <fancy.liu@mediatek.com>
-Date: Wed, 1 Nov 2023 19:58:05 +0800
-Subject: [PATCH 076/104] mtk: hostapd: Fix chan_switch to usable DFS channel
- fail due to ACS
-
-Step and issue:
-1. Enable ACS in hostapd config;
-2. Bootup and then use hostapd_cli cmd switch channel to a DFS channel;
-3. Will do ACS again, and no work on channel specified in step 2.
-
-Root cause:
-When need do DFS-CAC, hostapd will do intf disable, then set the new
-channel into running config settings, and finally enable intf;
-In the test case, new DFS channel is set to runnint config settings, but
-another param "acs" is still 1 (enable), caused the ACS running when
-intf enabled.
-
-Solution:
-In the hostapd_switch_channel_fallback, need to disable acs if channel
-is valid.
-
-Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
----
- src/ap/hostapd.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index cdbf81e38..636655ea1 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -4580,6 +4580,9 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
- 
- 	iface->freq = freq_params->freq;
- 	iface->conf->channel = freq_params->channel;
-+	if (iface->conf->channel != 0) /* If channel not zero, will disable acs. */
-+		iface->conf->acs = 0;
-+
- 	iface->conf->secondary_channel = freq_params->sec_channel_offset;
- 	hostapd_set_oper_centr_freq_seg0_idx(iface->conf, seg0_idx);
- 	hostapd_set_oper_centr_freq_seg1_idx(iface->conf, seg1_idx);
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0076-mtk-hostapd-add-connac3-csi-control-interface.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0076-mtk-hostapd-add-connac3-csi-control-interface.patch
new file mode 100644
index 0000000..f276767
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0076-mtk-hostapd-add-connac3-csi-control-interface.patch
@@ -0,0 +1,703 @@
+From b0f18aac90c7144db84f880aedb5aeaba3f37305 Mon Sep 17 00:00:00 2001
+From: mtk20656 <chank.chen@mediatek.com>
+Date: Tue, 6 Feb 2024 15:46:05 +0800
+Subject: [PATCH 076/126] mtk: hostapd: add connac3 csi control interface
+
+1. add hostapd_cli interface
+2. add csi set/dump flow
+3. add csi raw data to json
+
+Signed-off-by: mtk20656 <chank.chen@mediatek.com>
+---
+ hostapd/ctrl_iface.c              | 193 ++++++++++++++++++++++++
+ hostapd/hostapd_cli.c             |  16 ++
+ src/ap/ap_drv_ops.c               |  13 ++
+ src/ap/ap_drv_ops.h               |   2 +
+ src/common/mtk_vendor.h           |  33 ++++-
+ src/drivers/driver.h              |  18 +++
+ src/drivers/driver_nl80211.c      | 239 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   3 +
+ 9 files changed, 511 insertions(+), 7 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 76fd25d8b..9bd8a46b3 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -5063,6 +5063,193 @@ hostapd_ctrl_iface_disable_beacon(struct hostapd_data *hapd, char *value,
+ 
+ }
+ 
++static int
++hostapd_ctrl_iface_set_csi(struct hostapd_data *hapd, char *cmd,
++					char *buf, size_t buflen)
++{
++	char *tmp;
++	u8 sta_mac[ETH_ALEN] = {0};
++	u32 csi_para[4] = {0};
++	char mac_str[18] = {0};
++	u8 csi_para_cnt = 0;
++
++	tmp = strtok_r(cmd, ",", &cmd);
++
++	while (tmp) {
++		csi_para_cnt++;
++
++		if (csi_para_cnt <= 4)
++			csi_para[csi_para_cnt - 1] = strtol(tmp, &tmp, 10);
++		else if (csi_para_cnt == 5) {
++			memcpy(mac_str, tmp, sizeof(mac_str) - 1);
++			break;
++		}
++
++		tmp = strtok_r(NULL, ",", &cmd);
++	}
++
++	if (strlen(mac_str)) {	/* user input mac string */
++		if (hwaddr_aton(mac_str, sta_mac) < 0) {
++			wpa_printf(MSG_ERROR, "station mac is not right.\n");
++			return -1;
++		}
++
++		if (hostapd_drv_csi_set(hapd, csi_para[0], csi_para[1], csi_para[2], csi_para[3], sta_mac)) {
++			wpa_printf(MSG_ERROR, "Not able to set csi, %d,%d,%d,%d,%s\n",
++					csi_para[0], csi_para[1], csi_para[2], csi_para[3], mac_str);
++			return -1;
++		}
++	} else {
++		if (hostapd_drv_csi_set(hapd, csi_para[0], csi_para[1], csi_para[2], csi_para[3], NULL)) {
++			wpa_printf(MSG_ERROR, "Not able to set csi, %d,%d,%d,%d\n",
++					csi_para[0], csi_para[1], csi_para[2], csi_para[3]);
++			return -1;
++		}
++	}
++
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
++static int mt76_csi_to_json(char *fname, struct csi_resp_data *resp_buf)
++{
++#define MAX_BUF_SIZE	10000
++	FILE *f;
++	int i;
++
++	if (!fname) {
++		wpa_printf(MSG_ERROR, "csi dump file name is null!\n");
++		return -1;
++	}
++
++	f = fopen(fname, "a+");
++	if (!f) {
++		wpa_printf(MSG_ERROR, "open csi dump file %s failed\n", fname);
++		return -1;
++	}
++
++	if (fwrite("[", 1, 1, f) != 1) {
++		fclose(f);
++		return -1;
++	}
++
++	for (i = 0; i < resp_buf->buf_cnt; i++) {
++		struct csi_data *c = &resp_buf->csi_buf[i];
++		char *pos, *buf;
++		int j;
++
++		buf = malloc(MAX_BUF_SIZE);
++		if (!buf) {
++			fclose(f);
++			return -1;
++		}
++
++		pos = buf;
++		pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
++
++		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->ts);
++		pos += snprintf(pos, MAX_BUF_SIZE, "\"%02x%02x%02x%02x%02x%02x\",", c->ta[0], c->ta[1], c->ta[2], c->ta[3], c->ta[4], c->ta[5]);
++
++		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->rssi);
++		pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->snr);
++		pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->data_bw);
++		pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->pri_ch_idx);
++		pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->rx_mode);
++		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->tx_idx);
++		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->rx_idx);
++		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->chain_info);
++		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->ext_info);
++
++		pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
++		for (j = 0; j < c->data_num; j++) {
++			pos += snprintf(pos, MAX_BUF_SIZE, "%d", c->data_i[j]);
++			if (j != (c->data_num - 1))
++				pos += snprintf(pos, MAX_BUF_SIZE, ",");
++		}
++		pos += snprintf(pos, MAX_BUF_SIZE, "%c,", ']');
++
++		pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
++		for (j = 0; j < c->data_num; j++) {
++			pos += snprintf(pos, MAX_BUF_SIZE, "%d", c->data_q[j]);
++			if (j != (c->data_num - 1))
++				pos += snprintf(pos, MAX_BUF_SIZE, ",");
++		}
++		pos += snprintf(pos, MAX_BUF_SIZE, "%c", ']');
++
++		pos += snprintf(pos, MAX_BUF_SIZE, "%c", ']');
++		if (i != resp_buf->buf_cnt - 1)
++			pos += snprintf(pos, MAX_BUF_SIZE, ",");
++
++		if (fwrite(buf, 1, pos - buf, f) != (pos - buf)) {
++			perror("fwrite");
++			free(buf);
++			fclose(f);
++			return -1;
++		}
++
++		free(buf);
++	}
++
++	if (fwrite("]", 1, 1, f) != 1) {
++		fclose(f);
++		return -1;
++	}
++
++	fclose(f);
++
++	return 0;
++}
++
++static int
++hostapd_ctrl_iface_dump_csi(struct hostapd_data *hapd, char *cmd,
++				char *buf, size_t buflen)
++{
++	char *tmp, *fname;
++	int data_cnt = 0, ret = 0;
++	struct csi_resp_data resp_buf;
++
++	tmp = strtok_r(cmd, ",", &cmd);
++
++	if (!tmp) {
++		wpa_printf(MSG_ERROR, "Error in command format\n");
++		return -1;
++	}
++
++	data_cnt = strtoul(tmp, &tmp, 0);
++
++	if (data_cnt > 3000) {
++		wpa_printf(MSG_ERROR, "Wrong input csi data cnt\n");
++		return -1;
++	}
++
++	fname = strtok_r(NULL, ",", &cmd);
++
++	if (!fname) {
++		wpa_printf(MSG_ERROR, "Error in command format, csi_filename.\n");
++		return -1;
++	}
++
++	resp_buf.csi_buf = (struct csi_data *)os_zalloc(sizeof(struct csi_data) * data_cnt);
++
++	if (resp_buf.csi_buf == NULL) {
++		wpa_printf(MSG_ERROR, "Error in memory allocation\n");
++		return -1;
++	}
++
++	resp_buf.usr_need_cnt = data_cnt;
++	resp_buf.buf_cnt = 0;
++
++	if (hostapd_drv_csi_dump(hapd, (void *)&resp_buf)) {
++		wpa_printf(MSG_ERROR, "Not able to set csi dump\n");
++		os_free(resp_buf.csi_buf);
++		return -1;
++	}
++
++	mt76_csi_to_json(fname, &resp_buf);
++
++	os_free(resp_buf.csi_buf);
++	return 0;
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -5724,6 +5911,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 	} else if (os_strncmp(buf, "NO_BEACON ", 10) == 0) {
+ 		reply_len = hostapd_ctrl_iface_disable_beacon(hapd, buf + 10, reply,
+ 							      reply_size);
++	} else if (os_strncmp(buf, "SET_CSI ", 7) == 0) {
++		reply_len = hostapd_ctrl_iface_set_csi(hapd, buf + 8,
++							reply, reply_size);
++	} else if (os_strncmp(buf, "DUMP_CSI ", 8) == 0) {
++		reply_len = hostapd_ctrl_iface_dump_csi(hapd, buf + 9,
++							reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index a98f76eee..f54fa9997 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1744,6 +1744,18 @@ static int hostapd_cli_cmd_dump_amnt(struct wpa_ctrl *ctrl, int argc,
+ 	return hostapd_cli_cmd(ctrl, "DUMP_AMNT", 1, argc, argv);
+ }
+ 
++static int hostapd_cli_cmd_set_csi(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "SET_CSI", 1, argc, argv);
++}
++
++static int hostapd_cli_cmd_dump_csi(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "DUMP_CSI", 1, argc, argv);
++}
++
+ struct hostapd_cli_cmd {
+ 	const char *cmd;
+ 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -1992,6 +2004,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 		" = Set Station index and mac to monitor"},
+ 	{ "dump_amnt", hostapd_cli_cmd_dump_amnt, NULL,
+ 		" = Dump RSSI of monitoring Station"},
++	{ "set_csi", hostapd_cli_cmd_set_csi, NULL,
++		" = Set csi configuaration"},
++	{ "dump_csi", hostapd_cli_cmd_dump_csi, NULL,
++		" = Dump csi data to a json file"},
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index c7635cae6..a26a3f45d 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1445,3 +1445,16 @@ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
+ 	return hapd->driver->beacon_ctrl(hapd->drv_priv, beacon_mode);
+ }
+ 
++int hostapd_drv_csi_set(struct hostapd_data *hapd, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac)
++{
++	if (!hapd->driver || !hapd->driver->csi_set)
++		return 0;
++	return hapd->driver->csi_set(hapd->drv_priv, hapd->iconf->band_idx, mode, cfg, v1, v2, mac);
++}
++
++int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf)
++{
++	if (!hapd->driver || !hapd->driver->csi_dump)
++		return 0;
++	return hapd->driver->csi_dump(hapd->drv_priv, hapd->iconf->band_idx, dump_buf);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 8c783610c..9f2e86a9b 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -174,6 +174,8 @@ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_
+ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
+ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
+ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode);
++int hostapd_drv_csi_set(struct hostapd_data *hapd, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac);
++int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 9b054ef43..ce3a2ad9a 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -73,12 +73,13 @@ enum mtk_vendor_attr_csi_ctrl {
+ 	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
+ 	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
+ 	MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
+-	MTK_VENDOR_ATTR_CSI_CTRL_INTERVAL,
+ 
+ 	MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
+ 
+ 	MTK_VENDOR_ATTR_CSI_CTRL_DATA,
+ 
++        MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX,
++
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_CSI_CTRL,
+ 	MTK_VENDOR_ATTR_CSI_CTRL_MAX =
+@@ -96,6 +97,7 @@ enum mtk_vendor_attr_csi_data {
+ 	MTK_VENDOR_ATTR_CSI_DATA_BW,
+ 	MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
+ 	MTK_VENDOR_ATTR_CSI_DATA_TA,
++	MTK_VENDOR_ATTR_CSI_DATA_NUM,
+ 	MTK_VENDOR_ATTR_CSI_DATA_I,
+ 	MTK_VENDOR_ATTR_CSI_DATA_Q,
+ 	MTK_VENDOR_ATTR_CSI_DATA_INFO,
+@@ -106,7 +108,7 @@ enum mtk_vendor_attr_csi_data {
+ 	MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
+ 	MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
+ 	MTK_VENDOR_ATTR_CSI_DATA_MODE,
+-	MTK_VENDOR_ATTR_CSI_DATA_H_IDX,
++	MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_CSI_DATA,
+@@ -284,23 +286,40 @@ enum mtk_vendor_attr_beacon_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
+ };
+ 
+-#define CSI_MAX_COUNT 256
++#define CSI_BW20_DATA_COUNT	64
++#define CSI_BW40_DATA_COUNT	128
++#define CSI_BW80_DATA_COUNT	256
++#define CSI_BW160_DATA_COUNT	512
++#define CSI_BW320_DATA_COUNT	1024
+ #define ETH_ALEN 6
+ 
+ struct csi_data {
+-	s16 data_i[CSI_MAX_COUNT];
+-	s16 data_q[CSI_MAX_COUNT];
++	u8 ch_bw;
++	u16 data_num;
++	s16 data_i[CSI_BW320_DATA_COUNT];
++	s16 data_q[CSI_BW320_DATA_COUNT];
++	u8 band;
+ 	s8 rssi;
+ 	u8 snr;
+ 	u32 ts;
+ 	u8 data_bw;
+ 	u8 pri_ch_idx;
+ 	u8 ta[ETH_ALEN];
+-	u32 info;
++	u32 ext_info;
+ 	u8 rx_mode;
+-	u32 h_idx;
++	u32 chain_info;
+ 	u16 tx_idx;
+ 	u16 rx_idx;
++	u32 segment_num;
++	u8 remain_last;
++	u16 pkt_sn;
++	u8 tr_stream;
++};
++
++struct csi_resp_data {
++	u16 usr_need_cnt;
++	u16 buf_cnt;
++	struct csi_data *csi_buf;
+ };
+ 
+ #define AIR_MONITOR_MAX_ENTRY 16
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index ec00c36e4..48cb93cfd 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5369,6 +5369,24 @@ struct wpa_driver_ops {
+ #ifdef CONFIG_IEEE80211BE
+ 	int (*get_mld_addr)(void *priv, u8 *addr);
+ #endif
++	/**
++	 * csi_set - Set csi related mode and parameter
++	 * @priv: Private driver interface data
++	 * @band_idx: band idx
++	 * @mode: Csi mode parameter
++	 * @cfg: Csi config parameter
++	 * @v1: Value1
++	 * @v2: Value2
++	 * @mac: Station mac for station filter
++	 */
++	int (*csi_set)(void *priv, u8 band_idx, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac);
++	/**
++	* csi_dump - Dump csi data to json file
++	* @priv: Private driver interface data
++	* @band_idx: band idx
++	* @dump_buf: Dump_struct that store csi data and related info
++	*/
++	int (*csi_dump)(void *priv, u8 band_idx, void *dump_buf);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 8f7215d62..afb068091 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -161,6 +161,36 @@ pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
+ 	[MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
+ };
+ 
++static struct nla_policy csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
++	[MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG] = { .type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2] = { .type = NLA_U32 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR] = { .type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM] = { .type = NLA_U16 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_DATA] = { .type = NLA_NESTED },
++};
++
++static struct nla_policy csi_data_policy[NUM_MTK_VENDOR_ATTRS_CSI_DATA] = {
++	[MTK_VENDOR_ATTR_CSI_DATA_VER] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_DATA_TS] = { .type = NLA_U32 },
++	[MTK_VENDOR_ATTR_CSI_DATA_RSSI] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_DATA_SNR] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_DATA_BW] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_DATA_TA] = { .type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_CSI_DATA_NUM] = { .type = NLA_U32 },
++	[MTK_VENDOR_ATTR_CSI_DATA_I] = { .type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_CSI_DATA_Q] = { .type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_CSI_DATA_INFO] = { .type = NLA_U32 },
++	[MTK_VENDOR_ATTR_CSI_DATA_TX_ANT] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_DATA_RX_ANT] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_DATA_MODE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO] = { .type = NLA_U32 },
++};
++
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+ 	struct nl_sock *handle;
+@@ -15220,6 +15250,213 @@ static int nl80211_get_mld_addr(void *priv, u8 *addr)
+ }
+ #endif
+ 
++static int
++nl80211_csi_set(void *priv, u8 band_idx, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	void *tb1, *tb2;
++	int ret, i;
++
++	if (!drv->mtk_csi_vendor_cmd_avail) {
++		wpa_printf(MSG_ERROR,
++			"nl80211: Driver does not support csi");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++			nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++	if (!data)
++		goto fail;
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX, band_idx);
++
++	tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG | NLA_F_NESTED);
++	if (!tb1)
++		goto fail;
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE, mode);
++	nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE, cfg);
++	nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1, v1);
++	nla_put_u32(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2, v2);
++
++	nla_nest_end(msg, tb1);
++
++	if (mac) {
++		tb2 = nla_nest_start(msg, MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR | NLA_F_NESTED);
++		if (!tb2)
++			goto fail;
++
++		for (i = 0; i < ETH_ALEN; i++)
++			nla_put_u8(msg, i, mac[i]);
++
++		nla_nest_end(msg, tb2);
++	}
++
++	nla_nest_end(msg, data);
++
++	ret = send_and_recv_cmd(drv, msg);
++
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set csi. ret=%d (%s)",
++			ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++
++}
++
++static int
++mt76_csi_dump_cb(struct nl_msg *msg, void *arg)
++{
++	struct nlattr *tb[NL80211_ATTR_MAX + 1];
++	struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_CSI_CTRL];
++	struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_CSI_DATA];
++	struct nlattr *attr, *cur, *data;
++	int len = 0, rem, idx;
++	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++	struct csi_resp_data *csi_resp = (struct csi_resp_data *)arg;
++	struct csi_data *c = csi_resp->csi_buf;
++
++	c += csi_resp->buf_cnt;
++
++	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++		genlmsg_attrlen(gnlh, 0), NULL);
++
++	attr = tb[NL80211_ATTR_VENDOR_DATA];
++	if (!attr)
++		return NL_SKIP;
++
++	nla_parse_nested(tb1, MTK_VENDOR_ATTR_CSI_CTRL_MAX,
++			attr, csi_ctrl_policy);
++
++	if (!tb1[MTK_VENDOR_ATTR_CSI_CTRL_DATA])
++		return NL_SKIP;
++
++	nla_parse_nested(tb2, MTK_VENDOR_ATTR_CSI_DATA_MAX,
++			tb1[MTK_VENDOR_ATTR_CSI_CTRL_DATA], csi_data_policy);
++
++	if (!(tb2[MTK_VENDOR_ATTR_CSI_DATA_VER] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_TS] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_RSSI] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_SNR] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_BW] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_TA] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_I] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_Q] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_INFO] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_MODE] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO])) {
++		fprintf(stderr, "Attributes error for CSI data\n");
++		return NL_SKIP;
++	}
++
++	c->rssi = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_RSSI]);
++	c->snr = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_SNR]);
++	c->data_bw = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_BW]);
++	c->pri_ch_idx = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX]);
++	c->rx_mode = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_MODE]);
++
++	c->tx_idx = nla_get_u16(tb2[MTK_VENDOR_ATTR_CSI_DATA_TX_ANT]);
++	c->rx_idx = nla_get_u16(tb2[MTK_VENDOR_ATTR_CSI_DATA_RX_ANT]);
++
++	c->ext_info = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_INFO]);
++	c->chain_info = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO]);
++
++	c->ts = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_TS]);
++
++	c->data_num = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_NUM]);
++
++	idx = 0;
++	nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_TA], rem) {
++		if (idx < ETH_ALEN)
++			c->ta[idx++] = nla_get_u8(cur);
++	}
++
++	idx = 0;
++	nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_I], rem) {
++		if (idx < c->data_num)
++			c->data_i[idx++] = nla_get_u16(cur);
++	}
++
++	idx = 0;
++	nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_Q], rem) {
++		if (idx < c->data_num)
++			c->data_q[idx++] = nla_get_u16(cur);
++	}
++
++	csi_resp->buf_cnt++;
++
++	return NL_SKIP;
++}
++
++static int
++nl80211_csi_dump(void *priv, u8 band_idx, void *dump_buf)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++	struct csi_resp_data *csi_resp;
++	u16 pkt_num, i;
++
++	if (!drv->mtk_csi_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			"nl80211: Driver does not support csi");
++		return 0;
++	}
++
++	csi_resp = (struct csi_resp_data *)dump_buf;
++	pkt_num =  csi_resp->usr_need_cnt;
++
++	if (pkt_num > 3000)
++		return -EINVAL;
++
++#define CSI_DUMP_PER_NUM	3
++	for (i = 0; i < pkt_num / CSI_DUMP_PER_NUM; i++) {
++		msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
++		if (!msg)
++			goto fail;
++
++		if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++				nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++				MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL))
++			goto fail;
++
++		data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++		if (!data)
++			goto fail;
++
++		nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX, band_idx);
++		nla_put_u16(msg, MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM, CSI_DUMP_PER_NUM);
++
++		nla_nest_end(msg, data);
++
++		ret = send_and_recv_resp(drv, msg, mt76_csi_dump_cb, dump_buf);
++	}
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+ 	.desc = "Linux nl80211/cfg80211",
+@@ -15403,4 +15640,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ #ifdef CONFIG_IEEE80211BE
+ 	.get_mld_addr = nl80211_get_mld_addr,
+ #endif
++	.csi_set = nl80211_csi_set,
++	.csi_dump = nl80211_csi_dump,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index a0a62e536..b710b50cd 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -212,6 +212,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_background_radar_vendor_cmd_avail:1;
+ 	unsigned int mtk_pp_vendor_cmd_avail:1;
+ 	unsigned int mtk_beacon_ctrl_vendor_cmd_avail:1;
++	unsigned int mtk_csi_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 30f89b687..5a53700d1 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1173,6 +1173,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL :
+ 					drv->mtk_beacon_ctrl_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL:
++					drv->mtk_csi_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0077-Revert-ACS-upstream-changes.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0077-Revert-ACS-upstream-changes.patch
deleted file mode 100644
index 16f32cf..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0077-Revert-ACS-upstream-changes.patch
+++ /dev/null
@@ -1,398 +0,0 @@
-From 5a471a9025d9bf2a871339f5306e5c9050357703 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Mon, 29 Jan 2024 10:26:53 +0800
-Subject: [PATCH 077/104] Revert ACS upstream changes
-
-- 348c047af ACS: More consistent checking of the best channel pointer
-- 98f3bd26d ACS: Extend the 320 MHz support
-- 733de8568 ACS: Fix not selecting the best channel in the segment
-- dc57ede01 tests: Full validation of ACS selecting HT40- channel
-- 4881accbb ACS: Add HT40- support in the 2.4 GHz band
-- 29f38ebcf ACS: Check whether iface->current_mode is NULL before use
-- 6f014c0d0 ACS: Add 320 MHz support for EHT
-
-Note that "e6f2494c3 hostapd: Add eht_bw320_offset configuration option"
-is not reverted due to conflict.
----
- src/ap/acs.c               | 160 +++++++++----------------------------
- tests/hwsim/test_ap_acs.py |  19 ++---
- 2 files changed, 41 insertions(+), 138 deletions(-)
-
-diff --git a/src/ap/acs.c b/src/ap/acs.c
-index cb4db7147..cfa4a7d27 100644
---- a/src/ap/acs.c
-+++ b/src/ap/acs.c
-@@ -245,8 +245,6 @@ enum bw_type {
- 	ACS_BW40,
- 	ACS_BW80,
- 	ACS_BW160,
--	ACS_BW320_1,
--	ACS_BW320_2,
- };
- 
- struct bw_item {
-@@ -288,20 +286,10 @@ static const struct bw_item bw_160[] = {
- 	{ 6435, 6575, 111 }, { 6595, 6735, 143 },
- 	{ 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
- };
--static const struct bw_item bw_320_1[] = {
--	{ 5955, 6255, 31 }, { 6275, 6575, 95 }, { 6595, 6895, 159 },
--	{ -1, -1, -1 }
--};
--static const struct bw_item bw_320_2[] = {
--	{ 6115, 6415, 63 }, { 6435, 6735, 127 }, { 6755, 7055, 191 },
--	{ -1, -1, -1 }
--};
- static const struct bw_item *bw_desc[] = {
- 	[ACS_BW40] = bw_40,
- 	[ACS_BW80] = bw_80,
- 	[ACS_BW160] = bw_160,
--	[ACS_BW320_1] = bw_320_1,
--	[ACS_BW320_2] = bw_320_2,
- };
- 
- 
-@@ -773,42 +761,6 @@ static void acs_update_puncturing_bitmap(struct hostapd_iface *iface,
- #endif /* CONFIG_IEEE80211BE */
- 
- 
--static bool
--acs_usable_bw320_chan(struct hostapd_iface *iface,
--		      struct hostapd_channel_data *chan, int *bw320_offset)
--{
--	const char *bw320_str[] = { "320 MHz", "320 MHz-1", "320 MHz-2" };
--	int conf_bw320_offset = hostapd_get_bw320_offset(iface->conf);
--
--	*bw320_offset = 0;
--	switch (conf_bw320_offset) {
--	case 1:
--		if (acs_usable_bw_chan(chan, ACS_BW320_1))
--			*bw320_offset = 1;
--		break;
--	case 2:
--		if (acs_usable_bw_chan(chan, ACS_BW320_2))
--			*bw320_offset = 2;
--		break;
--	case 0:
--	default:
--		conf_bw320_offset = 0;
--		if (acs_usable_bw_chan(chan, ACS_BW320_1))
--			*bw320_offset = 1;
--		else if (acs_usable_bw_chan(chan, ACS_BW320_2))
--			*bw320_offset = 2;
--		break;
--	}
--
--	if (!*bw320_offset)
--		wpa_printf(MSG_DEBUG,
--			   "ACS: Channel %d: not allowed as primary channel for %s bandwidth",
--			   chan->chan, bw320_str[conf_bw320_offset]);
--
--	return *bw320_offset != 0;
--}
--
--
- static void
- acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 			 struct hostapd_hw_modes *mode,
-@@ -820,18 +772,14 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 	struct hostapd_channel_data *chan, *adj_chan = NULL, *best;
- 	long double factor;
- 	int i, j;
--	int bw320_offset = 0, ideal_bw320_offset = 0;
- 	unsigned int k;
--	int secondary_channel = 1, freq_offset;
--
--	if (is_24ghz_mode(mode->mode))
--		secondary_channel = iface->conf->secondary_channel;
- 
- 	for (i = 0; i < mode->num_channels; i++) {
--		double total_weight = 0;
-+		double total_weight;
- 		struct acs_bias *bias, tmp_bias;
-+		bool update_best = true;
- 
--		chan = &mode->channels[i];
-+		best = chan = &mode->channels[i];
- 
- 		/* Since in the current ACS implementation the first channel is
- 		 * always a primary channel, skip channels not available as
-@@ -863,7 +811,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 		    iface->conf->country[2] == 0x4f)
- 			continue;
- 
--		if (!chan_bw_allowed(chan, bw, secondary_channel != -1, 1)) {
-+		if (!chan_bw_allowed(chan, bw, 1, 1)) {
- 			wpa_printf(MSG_DEBUG,
- 				   "ACS: Channel %d: BW %u is not supported",
- 				   chan->chan, bw);
-@@ -884,8 +832,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 		}
- 
- 		if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
--		    (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
--		     iface->conf->ieee80211be)) {
-+		    (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
- 			if (hostapd_get_oper_chwidth(iface->conf) ==
- 			    CONF_OPER_CHWIDTH_80MHZ &&
- 			    !acs_usable_bw_chan(chan, ACS_BW80)) {
-@@ -905,25 +852,13 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 			}
- 		}
- 
--		if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
--		    iface->conf->ieee80211be) {
--			if (hostapd_get_oper_chwidth(iface->conf) ==
--			    CONF_OPER_CHWIDTH_320MHZ &&
--			    !acs_usable_bw320_chan(iface, chan, &bw320_offset))
--				continue;
--		}
--
- 		factor = 0;
--		best = NULL;
--		if (acs_usable_chan(chan)) {
-+		if (acs_usable_chan(chan))
- 			factor = chan->interference_factor;
--			total_weight = 1;
--			best = chan;
--		}
-+		total_weight = 1;
- 
- 		for (j = 1; j < n_chans; j++) {
--			adj_chan = acs_find_chan(iface, chan->freq +
--						 j * secondary_channel * 20);
-+			adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
- 			if (!adj_chan)
- 				break;
- 
-@@ -934,14 +869,16 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 				break;
- 			}
- 
--			if (!acs_usable_chan(adj_chan))
--				continue;
--
--			factor += adj_chan->interference_factor;
--			total_weight += 1;
-+			if (acs_usable_chan(adj_chan)) {
-+				factor += adj_chan->interference_factor;
-+				total_weight += 1;
-+			} else {
-+				update_best = false;
-+			}
- 
- 			/* find the best channel in this segment */
--			if (!best || adj_chan->interference_factor <
-+			if (update_best &&
-+			    adj_chan->interference_factor <
- 			    best->interference_factor)
- 				best = adj_chan;
- 		}
-@@ -954,9 +891,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 
- 		/* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less
- 		 * crowded primary channel if one was found in the segment */
--		if (iface->current_mode &&
--		    iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
--		    best && chan != best) {
-+		if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
-+		    chan != best) {
- 			wpa_printf(MSG_DEBUG,
- 				   "ACS: promoting channel %d over %d (less interference %Lg/%Lg)",
- 				   best->chan, chan->chan,
-@@ -969,9 +905,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 		 * channel interference factor. */
- 		if (is_24ghz_mode(mode->mode)) {
- 			for (j = 0; j < n_chans; j++) {
--				freq_offset = j * 20 * secondary_channel;
- 				adj_chan = acs_find_chan(iface, chan->freq +
--							 freq_offset - 5);
-+							 (j * 20) - 5);
- 				if (adj_chan && acs_usable_chan(adj_chan)) {
- 					factor += ACS_ADJ_WEIGHT *
- 						adj_chan->interference_factor;
-@@ -979,7 +914,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 				}
- 
- 				adj_chan = acs_find_chan(iface, chan->freq +
--							 freq_offset - 10);
-+							 (j * 20) - 10);
- 				if (adj_chan && acs_usable_chan(adj_chan)) {
- 					factor += ACS_NEXT_ADJ_WEIGHT *
- 						adj_chan->interference_factor;
-@@ -987,7 +922,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 				}
- 
- 				adj_chan = acs_find_chan(iface, chan->freq +
--							 freq_offset + 5);
-+							 (j * 20) + 5);
- 				if (adj_chan && acs_usable_chan(adj_chan)) {
- 					factor += ACS_ADJ_WEIGHT *
- 						adj_chan->interference_factor;
-@@ -995,7 +930,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 				}
- 
- 				adj_chan = acs_find_chan(iface, chan->freq +
--							 freq_offset + 10);
-+							 (j * 20) + 10);
- 				if (adj_chan && acs_usable_chan(adj_chan)) {
- 					factor += ACS_NEXT_ADJ_WEIGHT *
- 						adj_chan->interference_factor;
-@@ -1004,9 +939,6 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 			}
- 		}
- 
--		if (total_weight == 0)
--			continue;
--
- 		factor /= total_weight;
- 
- 		bias = NULL;
-@@ -1044,7 +976,6 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 
- 			*ideal_factor = factor;
- 			*ideal_chan = chan;
--			ideal_bw320_offset = bw320_offset;
- 
- #ifdef CONFIG_IEEE80211BE
- 			if (iface->conf->ieee80211be)
-@@ -1055,13 +986,9 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 		}
- 
- 		/* This channel would at least be usable */
--		if (!(*rand_chan)) {
-+		if (!(*rand_chan))
- 			*rand_chan = chan;
--			ideal_bw320_offset = bw320_offset;
--		}
- 	}
--
--	hostapd_set_and_check_bw320_offset(iface->conf, ideal_bw320_offset);
- }
- 
- 
-@@ -1088,12 +1015,19 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
- 		goto bw_selected;
- 	}
- 
-+	/* TODO: HT40- support */
-+
-+	if (iface->conf->ieee80211n &&
-+	    iface->conf->secondary_channel == -1) {
-+		wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
-+		return NULL;
-+	}
-+
- 	if (iface->conf->ieee80211n &&
- 	    iface->conf->secondary_channel)
- 		n_chans = 2;
- 
--	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
--	    iface->conf->ieee80211be) {
-+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
- 		switch (hostapd_get_oper_chwidth(iface->conf)) {
- 		case CONF_OPER_CHWIDTH_80MHZ:
- 			n_chans = 4;
-@@ -1101,9 +1035,6 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
- 		case CONF_OPER_CHWIDTH_160MHZ:
- 			n_chans = 8;
- 			break;
--		case CONF_OPER_CHWIDTH_320MHZ:
--			n_chans = 16;
--			break;
- 		default:
- 			break;
- 		}
-@@ -1153,8 +1084,7 @@ static void acs_adjust_secondary(struct hostapd_iface *iface)
- 	    acs_find_mode(iface, iface->freq) != HOSTAPD_MODE_IEEE80211A)
- 		return;
- 
--	wpa_printf(MSG_DEBUG,
--		   "ACS: Adjusting HT/VHT/HE/EHT secondary frequency");
-+	wpa_printf(MSG_DEBUG, "ACS: Adjusting HT/VHT/HE secondary frequency");
- 
- 	for (i = 0; bw_desc[ACS_BW40][i].first != -1; i++) {
- 		if (iface->freq == bw_desc[ACS_BW40][i].first)
-@@ -1169,7 +1099,7 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
- {
- 	int center;
- 
--	wpa_printf(MSG_DEBUG, "ACS: Adjusting center frequency");
-+	wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
- 
- 	switch (hostapd_get_oper_chwidth(iface->conf)) {
- 	case CONF_OPER_CHWIDTH_USE_HT:
-@@ -1187,29 +1117,12 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
- 		break;
- 	case CONF_OPER_CHWIDTH_160MHZ:
- 		center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
--		break;
--	case CONF_OPER_CHWIDTH_320MHZ:
--		switch (hostapd_get_bw320_offset(iface->conf)) {
--		case 1:
--			center = acs_get_bw_center_chan(iface->freq,
--							ACS_BW320_1);
--			break;
--		case 2:
--			center = acs_get_bw_center_chan(iface->freq,
--							ACS_BW320_2);
--			break;
--		default:
--			wpa_printf(MSG_INFO,
--				   "ACS: BW320 offset is not selected");
--			return;
--		}
--
- 		break;
- 	default:
- 		/* TODO: How can this be calculated? Adjust
- 		 * acs_find_ideal_chan() */
- 		wpa_printf(MSG_INFO,
--			   "ACS: Only VHT20/40/80/160/320 is supported now");
-+			   "ACS: Only VHT20/40/80/160 is supported now");
- 		return;
- 	}
- 
-@@ -1272,8 +1185,7 @@ static void acs_study(struct hostapd_iface *iface)
- 	iface->conf->punct_bitmap = ideal_chan->punct_bitmap;
- #endif /* CONFIG_IEEE80211BE */
- 
--	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
--	    iface->conf->ieee80211be) {
-+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
- 		acs_adjust_secondary(iface);
- 		acs_adjust_center_freq(iface);
- 	}
-diff --git a/tests/hwsim/test_ap_acs.py b/tests/hwsim/test_ap_acs.py
-index 001a5d4fd..e1359b6eb 100644
---- a/tests/hwsim/test_ap_acs.py
-+++ b/tests/hwsim/test_ap_acs.py
-@@ -205,20 +205,11 @@ def test_ap_acs_40mhz_minus(dev, apdev):
-     params['acs_num_scans'] = '1'
-     params['chanlist'] = '1 11'
-     hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
--    wait_acs(hapd)
--
--    freq = hapd.get_status_field("freq")
--    if int(freq) < 2400:
--        raise Exception("Unexpected frequency")
--    sec = hapd.get_status_field("secondary_channel")
--    if int(sec) != -1:
--        raise Exception("Unexpected secondary_channel: " + sec)
--
--    dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
--    sig = dev[0].request("SIGNAL_POLL").splitlines()
--    logger.info("SIGNAL_POLL: " + str(sig))
--    if "WIDTH=40 MHz" not in sig:
--        raise Exception("Station did not report 40 MHz bandwidth")
-+    ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=10)
-+    if not ev:
-+        raise Exception("ACS start timed out")
-+    # HT40- is not currently supported in hostapd ACS, so do not try to connect
-+    # or verify that this operation succeeded.
- 
- def test_ap_acs_5ghz(dev, apdev):
-     """Automatic channel selection on 5 GHz"""
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0077-mtk-hostapd-add-support-for-basic-MLD-Extender.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0077-mtk-hostapd-add-support-for-basic-MLD-Extender.patch
new file mode 100644
index 0000000..12d2f11
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0077-mtk-hostapd-add-support-for-basic-MLD-Extender.patch
@@ -0,0 +1,271 @@
+From 0a10a7b41777ea926967d603bd4e14f9bca43bca Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 2 Apr 2024 15:36:29 +0800
+Subject: [PATCH 077/126] mtk: hostapd: add support for basic MLD Extender
+
+Add basic MLD Extender support, including
+1. Extender STA stops all Extender AP's links before scaning.
+2. After finishing the connection with root AP, Extender STA
+   synchronizes control channel with each link on the Extender
+   AP.
+
+Advanced support includes BW cynchronization and channel switch.
+
+Add a check for band_idx, if the iface has different band_idx from the
+one wpa_s provided, this iface is not the target.
+
+Also add a log for switching iface.
+
+Legacy AP interfaces that sharing the same hostapd_iface with the target
+hapd should also be disabled.
+
+If parsing 'band_idx' fails, iface starting only works for current
+iface.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ucode.c         | 60 ++++++++++++++++++++++++++++++++++++++----
+ src/utils/ucode.c      |  4 ++-
+ wpa_supplicant/ucode.c | 45 +++++++++++++++++++++++++------
+ 3 files changed, 95 insertions(+), 14 deletions(-)
+
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index 8c3212f6f..68f76dbe5 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -9,6 +9,7 @@
+ #include "ap_drv_ops.h"
+ #include "dfs.h"
+ #include "acs.h"
++#include "ieee802_11.h"
+ #include <libubox/uloop.h>
+ 
+ static uc_resource_type_t *global_type, *bss_type, *iface_type;
+@@ -491,14 +492,16 @@ static uc_value_t *
+ uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
+ {
+ 	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
+-	int i;
+-
+-	wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
+-			iface->phy, hostapd_state_text(iface->state));
++	struct hostapd_data *first_hapd;
++	struct hostapd_bss_config *conf;
++	int i, j;
+ 
+ 	if (!iface)
+ 		return NULL;
+ 
++	wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
++			iface->phy, hostapd_state_text(iface->state));
++
+ 	if (iface->state != HAPD_IFACE_ENABLED)
+ 		uc_hostapd_disable_iface(iface);
+ 
+@@ -509,6 +512,34 @@ uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
+ 		hapd->beacon_set_done = 0;
+ 	}
+ 
++#ifdef CONFIG_IEEE80211BE
++	first_hapd = iface->bss[0];
++	conf = first_hapd->conf;
++	for (i = 0; conf->mld_ap && i < iface->interfaces->count; i++) {
++		struct hostapd_iface *h = iface->interfaces->iface[i];
++		struct hostapd_data *h_hapd = h->bss[0];
++		struct hostapd_bss_config *hconf = h_hapd->conf;
++
++		if (h == iface) {
++			wpa_printf(MSG_DEBUG, "MLD: Skip own interface");
++			continue;
++		}
++
++		if (!hconf->mld_ap) {
++			wpa_printf(MSG_DEBUG,
++				   "MLD: Skip non MLD");
++			continue;
++		}
++
++		if (hostapd_is_ml_partner(first_hapd, h_hapd)) {
++			for (j = 0; j < h->num_bss; j++) {
++				hostapd_drv_stop_ap(h->bss[j]);
++				h->bss[j]->beacon_set_done = 0;
++			}
++		}
++	}
++#endif /* CONFIG_IEEE80211BE */
++
+ 	return NULL;
+ }
+ 
+@@ -516,11 +547,12 @@ static uc_value_t *
+ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
+ {
+ 	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++	struct hostapd_data *tmp_hapd;
+ 	uc_value_t *info = uc_fn_arg(0);
+ 	struct hostapd_config *conf;
+ 	bool changed = false;
+ 	uint64_t intval;
+-	int i;
++	int i, band_idx;
+ 
+ 	wpa_printf(MSG_INFO, "ucode: mtk: start iface for %s in state %s\n",
+ 			iface->phy, hostapd_state_text(iface->state));
+@@ -536,6 +568,24 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
+ 	if (ucv_type(info) != UC_OBJECT)
+ 		return NULL;
+ 
++	intval = ucv_int64_get(ucv_object_get(info, "band_idx", NULL));
++	band_idx = errno ? iface->conf->band_idx : intval;
++
++#ifdef CONFIG_IEEE80211BE
++	if (hostapd_is_mld_ap(iface->bss[0])) {
++		for_each_mld_link(tmp_hapd, iface->bss[0]) {
++			if (band_idx == tmp_hapd->iconf->band_idx) {
++				wpa_printf(MSG_INFO, "ucode: mtk: MLD: switch to iface with band_idx %d \n", band_idx);
++				iface = tmp_hapd->iface;
++				break;
++			}
++		}
++	}
++
++	if (band_idx != iface->bss[0]->iconf->band_idx)
++		return NULL;
++#endif /* CONFIG_IEEE80211BE */
++
+ #define UPDATE_VAL(field, name)							\
+ 	if ((intval = ucv_int64_get(ucv_object_get(info, name, NULL))) &&	\
+ 		!errno && intval != conf->field) do {				\
+diff --git a/src/utils/ucode.c b/src/utils/ucode.c
+index 6f82382f3..81d472f6b 100644
+--- a/src/utils/ucode.c
++++ b/src/utils/ucode.c
+@@ -110,7 +110,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 	uc_value_t *freq = uc_fn_arg(0);
+ 	uc_value_t *sec = uc_fn_arg(1);
+ 	int width = ucv_uint64_get(uc_fn_arg(2));
+-	int bw320_offset = 1;
++	int bw320_offset = 1, band_idx;
+ 	int freq_val, center_idx, center_ofs;
+ 	enum oper_chan_width chanwidth;
+ 	enum hostapd_hw_mode hw_mode;
+@@ -123,6 +123,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 		return NULL;
+ 
+ 	freq_val = ucv_int64_get(freq);
++	band_idx = ucv_int64_get(uc_fn_arg(4));
+ 	if (ucv_type(sec) == UC_INTEGER)
+ 		sec_channel = ucv_int64_get(sec);
+ 	else if (sec)
+@@ -183,6 +184,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 	ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
+ 	ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
+ 	ucv_object_add(ret, "oper_chwidth", ucv_int64_new(chanwidth));
++	ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
+ 
+ 	if (chanwidth == CONF_OPER_CHWIDTH_USE_HT && !sec_channel) {
+ 		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(channel));
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+index 542ca25c9..ac0639a90 100644
+--- a/wpa_supplicant/ucode.c
++++ b/wpa_supplicant/ucode.c
+@@ -246,12 +246,13 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ {
+ 	struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
+ 	struct wpa_bss *bss;
+-	uc_value_t *ret, *val;
++	uc_value_t *ret, *val, *link_obj = uc_fn_arg(0);
+ 	struct wpa_channel_info ci;
+ 	u8 op_class, channel;
+-	enum oper_chan_width ch_width;
+-	int center_freq1, bw320_offset = 1, is_24ghz;
++	enum oper_chan_width ch_width = CONF_OPER_CHWIDTH_USE_HT;
++	int center_freq1, bw320_offset = 1, is_24ghz, band_idx;
+ 	enum hostapd_hw_mode hw_mode;
++	int link_id = ucv_int64_get(link_obj);
+ 
+ 	if (!wpa_s)
+ 		return NULL;
+@@ -261,13 +262,25 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 	val = ucv_string_new(wpa_supplicant_state_txt(wpa_s->wpa_state));
+ 	ucv_object_add(ret, "state", ucv_get(val));
+ 
+-	bss = wpa_s->current_bss;
++	bss = link_id == -1 ? wpa_s->current_bss : wpa_s->links[link_id].bss;
+ 	if (bss) {
+ 		int sec_chan = 0;
+ 
+ 		hw_mode = ieee80211_freq_to_chan(bss->freq, &channel);
+ 		is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
+ 			hw_mode == HOSTAPD_MODE_IEEE80211B;
++		/*
++		 * Assume that the mapping between band and band_idx is
++		 * 2 GHz band: band_idx 0
++		 * 5 GHz band: band_idx 1
++		 * 6 GHz band: band_idx 2
++		 * */
++		if (is_24ghz)
++			band_idx = 0;
++		else if (IS_5GHZ(bss->freq))
++			band_idx = 1;
++		else if (is_6ghz_freq(bss->freq))
++			band_idx = 2;
+ 
+ 		wpa_drv_channel_info(wpa_s, &ci);
+ 		center_freq1 = ci.center_frq1;
+@@ -279,11 +292,10 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 				sec_chan = (bss->freq / 20) & 1 ? 1 : -1;
+ 		}
+ 
+-		if (ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
+-						  sec_chan, &op_class, &channel))
+-			return NULL;
++		if (!ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
++						   sec_chan, &op_class, &channel))
++			ch_width = op_class_to_ch_width(op_class);
+ 
+-		ch_width = op_class_to_ch_width(op_class);
+ 		if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
+ 		    (center_freq1 == 6265) || center_freq1 == 6585 ||
+ 		     center_freq1 == 6905) {
+@@ -295,6 +307,7 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 		ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
+ 		ucv_object_add(ret, "ch_width", ucv_int64_new(ch_width));
+ 		ucv_object_add(ret, "bw320_offset", ucv_int64_new(bw320_offset));
++		ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
+ 	}
+ 
+ #ifdef CONFIG_MESH
+@@ -309,6 +322,21 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 	return ret;
+ }
+ 
++static uc_value_t *
++uc_wpas_iface_get_valid_links(uc_vm_t *vm, size_t nargs)
++{
++	struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
++	uc_value_t *ret;
++
++	if (!wpa_s)
++		return NULL;
++
++	ret = ucv_object_new(vm);
++	ucv_object_add(ret, "valid_links", ucv_uint64_new(wpa_s->valid_links));
++
++	return ret;
++}
++
+ int wpas_ucode_init(struct wpa_global *gl)
+ {
+ 	static const uc_function_list_t global_fns[] = {
+@@ -320,6 +348,7 @@ int wpas_ucode_init(struct wpa_global *gl)
+ 	};
+ 	static const uc_function_list_t iface_fns[] = {
+ 		{ "status", uc_wpas_iface_status },
++		{ "get_valid_links", uc_wpas_iface_get_valid_links },
+ 	};
+ 	uc_value_t *data, *proto;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0078-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0078-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch
deleted file mode 100644
index 3f267b0..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0078-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch
+++ /dev/null
@@ -1,379 +0,0 @@
-From 12fe53a7919abb791658bcc7a74b8131f0362300 Mon Sep 17 00:00:00 2001
-From: "fancy.liu" <fancy.liu@mediatek.com>
-Date: Thu, 28 Sep 2023 18:03:08 +0800
-Subject: [PATCH 078/104] mtk: hostapd: ACS: Add EHT320 and HT40- support, fix
- issue
-
-1. Add 6G EHT320 support;
-2. Add 2.4G HT40- support;
-3. Fix issue: selected best channel is out of channels;
-
-Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
----
- src/ap/acs.c | 191 +++++++++++++++++++++++++++++++++------------------
- 1 file changed, 124 insertions(+), 67 deletions(-)
-
-diff --git a/src/ap/acs.c b/src/ap/acs.c
-index cfa4a7d27..1fa8b8e64 100644
---- a/src/ap/acs.c
-+++ b/src/ap/acs.c
-@@ -245,6 +245,7 @@ enum bw_type {
- 	ACS_BW40,
- 	ACS_BW80,
- 	ACS_BW160,
-+	ACS_BW320,
- };
- 
- struct bw_item {
-@@ -286,10 +287,16 @@ static const struct bw_item bw_160[] = {
- 	{ 6435, 6575, 111 }, { 6595, 6735, 143 },
- 	{ 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
- };
-+static const struct bw_item bw_320[] = {
-+	{ 5955, 6255, 31 }, { 6115, 6415, 63 }, { 6275, 6575, 95 },
-+	{ 6435, 6735, 127 }, { 6595, 6895, 159 }, { 6755, 7055, 191 },
-+	{ -1, -1, -1 }
-+};
- static const struct bw_item *bw_desc[] = {
- 	[ACS_BW40] = bw_40,
- 	[ACS_BW80] = bw_80,
- 	[ACS_BW160] = bw_160,
-+	[ACS_BW320] = bw_320,
- };
- 
- 
-@@ -769,10 +776,19 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 			 struct hostapd_channel_data **ideal_chan,
- 			 long double *ideal_factor)
- {
--	struct hostapd_channel_data *chan, *adj_chan = NULL, *best;
-+	struct hostapd_channel_data *chan, *adj_chan = NULL, *tmp_chan = NULL, *best;
- 	long double factor;
- 	int i, j;
- 	unsigned int k;
-+	int ht40_plus = 1, sec_ch_factor = 1;
-+
-+	if (is_24ghz_mode(mode->mode)) {
-+		ht40_plus = (iface->conf->secondary_channel == -1) ? 0 : 1;
-+		sec_ch_factor = (iface->conf->secondary_channel == -1) ? -1 : 1;
-+	}
-+
-+	wpa_printf(MSG_INFO, "%s:%d, bw(%u), n_chans(%d), num_channels(%d), sec_ch(%d)",
-+		__func__, __LINE__, bw, n_chans, mode->num_channels, iface->conf->secondary_channel);
- 
- 	for (i = 0; i < mode->num_channels; i++) {
- 		double total_weight;
-@@ -780,6 +796,9 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 		bool update_best = true;
- 
- 		best = chan = &mode->channels[i];
-+		wpa_printf(MSG_INFO,
-+			   "ACS: Channel[%d] %d: interference_factor %Lg",
-+			   i, chan->chan, chan->interference_factor);
- 
- 		/* Since in the current ACS implementation the first channel is
- 		 * always a primary channel, skip channels not available as
-@@ -811,7 +830,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 		    iface->conf->country[2] == 0x4f)
- 			continue;
- 
--		if (!chan_bw_allowed(chan, bw, 1, 1)) {
-+		if (!chan_bw_allowed(chan, bw, ht40_plus, 1)) {
- 			wpa_printf(MSG_DEBUG,
- 				   "ACS: Channel %d: BW %u is not supported",
- 				   chan->chan, bw);
-@@ -832,7 +851,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 		}
- 
- 		if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
--		    (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
-+		    (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
-+		     iface->conf->ieee80211be)) {
- 			if (hostapd_get_oper_chwidth(iface->conf) ==
- 			    CONF_OPER_CHWIDTH_80MHZ &&
- 			    !acs_usable_bw_chan(chan, ACS_BW80)) {
-@@ -850,63 +870,86 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 					   chan->chan);
- 				continue;
- 			}
--		}
- 
--		factor = 0;
--		if (acs_usable_chan(chan))
--			factor = chan->interference_factor;
--		total_weight = 1;
--
--		for (j = 1; j < n_chans; j++) {
--			adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
--			if (!adj_chan)
--				break;
--
--			if (!chan_bw_allowed(adj_chan, bw, 1, 0)) {
-+			if (iface->conf->ieee80211be &&
-+			    hostapd_get_oper_chwidth(iface->conf) ==
-+			    CONF_OPER_CHWIDTH_320MHZ &&
-+			    !acs_usable_bw_chan(chan, ACS_BW320)) {
- 				wpa_printf(MSG_DEBUG,
--					   "ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
--					   chan->chan, adj_chan->chan, bw);
--				break;
-+					   "ACS: Channel %d: not allowed as primary channel for 320 MHz bandwidth",
-+					   chan->chan);
-+				continue;
- 			}
-+		}
- 
--			if (acs_usable_chan(adj_chan)) {
--				factor += adj_chan->interference_factor;
-+		factor = 0;
-+		total_weight = 0;
-+
-+		if (!is_24ghz_mode(mode->mode)) {
-+			/* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less
-+			 * crowded primary channel if one was found in the segment */
-+			if (acs_usable_chan(chan)) {
-+				factor += chan->interference_factor;
- 				total_weight += 1;
--			} else {
--				update_best = false;
- 			}
- 
--			/* find the best channel in this segment */
--			if (update_best &&
--			    adj_chan->interference_factor <
--			    best->interference_factor)
--				best = adj_chan;
--		}
-+			for (j = 1; j < n_chans; j++) {
-+				adj_chan = acs_find_chan(iface, chan->freq + j * 20);
-+				if (!adj_chan)
-+					break;
- 
--		if (j != n_chans) {
--			wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth",
--				   chan->chan);
--			continue;
--		}
-+				if (!is_in_chanlist(iface, adj_chan) || !is_in_freqlist(iface, adj_chan))
-+					break;
- 
--		/* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less
--		 * crowded primary channel if one was found in the segment */
--		if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
--		    chan != best) {
--			wpa_printf(MSG_DEBUG,
--				   "ACS: promoting channel %d over %d (less interference %Lg/%Lg)",
--				   best->chan, chan->chan,
--				   chan->interference_factor,
--				   best->interference_factor);
--			chan = best;
--		}
-+				if (!chan_bw_allowed(adj_chan, bw, 1, 0)) {
-+					wpa_printf(MSG_DEBUG,
-+						   "ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
-+						   chan->chan, adj_chan->chan, bw);
-+					break;
-+				}
-+
-+				update_best = true;
-+				if (acs_usable_chan(adj_chan)) {
-+					factor += adj_chan->interference_factor;
-+					total_weight += 1;
-+				} else {
-+					update_best = false;
-+				}
- 
--		/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
--		 * channel interference factor. */
--		if (is_24ghz_mode(mode->mode)) {
-+				/* find the best channel in this segment */
-+				if (update_best &&
-+					adj_chan->interference_factor < best->interference_factor)
-+					best = adj_chan;
-+			}
-+
-+			if (j != n_chans) {
-+				wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth",
-+					   chan->chan);
-+				continue;
-+			}
-+
-+			if (chan != best) {
-+				wpa_printf(MSG_INFO,
-+					   "ACS: promoting channel %d over %d (less interference %Lg/%Lg)",
-+					   best->chan, chan->chan,
-+					   chan->interference_factor,
-+					   best->interference_factor);
-+				chan = best;
-+			}
-+		} else {
- 			for (j = 0; j < n_chans; j++) {
-+				/* Will set primary_channel / secondary_channel(40M case) weight to 1 */
-+				tmp_chan = acs_find_chan(iface, chan->freq +
-+							 (j * sec_ch_factor * 20));
-+				if (tmp_chan && acs_usable_chan(tmp_chan)) {
-+					factor += tmp_chan->interference_factor;
-+					total_weight += 1;
-+				}
-+
-+				/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent channel
-+				interference factor, separately for primary/secondary channel. */
- 				adj_chan = acs_find_chan(iface, chan->freq +
--							 (j * 20) - 5);
-+							 (j * sec_ch_factor * 20) - 5);
- 				if (adj_chan && acs_usable_chan(adj_chan)) {
- 					factor += ACS_ADJ_WEIGHT *
- 						adj_chan->interference_factor;
-@@ -914,7 +957,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 				}
- 
- 				adj_chan = acs_find_chan(iface, chan->freq +
--							 (j * 20) - 10);
-+							 (j * sec_ch_factor * 20) - 10);
- 				if (adj_chan && acs_usable_chan(adj_chan)) {
- 					factor += ACS_NEXT_ADJ_WEIGHT *
- 						adj_chan->interference_factor;
-@@ -922,7 +965,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 				}
- 
- 				adj_chan = acs_find_chan(iface, chan->freq +
--							 (j * 20) + 5);
-+							 (j * sec_ch_factor * 20) + 5);
- 				if (adj_chan && acs_usable_chan(adj_chan)) {
- 					factor += ACS_ADJ_WEIGHT *
- 						adj_chan->interference_factor;
-@@ -930,7 +973,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 				}
- 
- 				adj_chan = acs_find_chan(iface, chan->freq +
--							 (j * 20) + 10);
-+							 (j * sec_ch_factor * 20) + 10);
- 				if (adj_chan && acs_usable_chan(adj_chan)) {
- 					factor += ACS_NEXT_ADJ_WEIGHT *
- 						adj_chan->interference_factor;
-@@ -939,7 +982,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 			}
- 		}
- 
--		factor /= total_weight;
-+		if (total_weight)
-+			factor /= total_weight;
- 
- 		bias = NULL;
- 		if (iface->conf->acs_chan_bias) {
-@@ -958,11 +1002,11 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 
- 		if (bias) {
- 			factor *= bias->bias;
--			wpa_printf(MSG_DEBUG,
-+			wpa_printf(MSG_INFO,
- 				   "ACS:  * channel %d: total interference = %Lg (%f bias)",
- 				   chan->chan, factor, bias->bias);
- 		} else {
--			wpa_printf(MSG_DEBUG,
-+			wpa_printf(MSG_INFO,
- 				   "ACS:  * channel %d: total interference = %Lg",
- 				   chan->chan, factor);
- 		}
-@@ -1015,19 +1059,12 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
- 		goto bw_selected;
- 	}
- 
--	/* TODO: HT40- support */
--
--	if (iface->conf->ieee80211n &&
--	    iface->conf->secondary_channel == -1) {
--		wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
--		return NULL;
--	}
--
- 	if (iface->conf->ieee80211n &&
- 	    iface->conf->secondary_channel)
- 		n_chans = 2;
- 
--	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
-+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
-+	    iface->conf->ieee80211be) {
- 		switch (hostapd_get_oper_chwidth(iface->conf)) {
- 		case CONF_OPER_CHWIDTH_80MHZ:
- 			n_chans = 4;
-@@ -1037,6 +1074,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
- 			break;
- 		default:
- 			break;
-+		/* 320 is supported only in 6GHz 11be mode */
- 		}
- 	}
- 
-@@ -1057,7 +1095,7 @@ bw_selected:
- 	}
- 
- 	if (ideal_chan) {
--		wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg",
-+		wpa_printf(MSG_INFO, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg",
- 			   ideal_chan->chan, ideal_chan->freq, ideal_factor);
- 
- #ifdef CONFIG_IEEE80211BE
-@@ -1072,6 +1110,21 @@ bw_selected:
- 	return rand_chan;
- }
- 
-+static int acs_get_center_freq_320mhz(int channel)
-+{
-+	if (channel >= 1 && channel <= 45)
-+		return 31;
-+	else if (channel >= 49 && channel <= 77)
-+		return 63;
-+	else if (channel >= 81 && channel <= 109)
-+		return 95;
-+	else if (channel >= 113 && channel <= 141)
-+		return 127;
-+	else if (channel >= 145 && channel <= 173)
-+		return 159;
-+	else
-+		return 191;
-+}
- 
- static void acs_adjust_secondary(struct hostapd_iface *iface)
- {
-@@ -1099,7 +1152,7 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
- {
- 	int center;
- 
--	wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
-+	wpa_printf(MSG_DEBUG, "ACS: Adjusting center frequency");
- 
- 	switch (hostapd_get_oper_chwidth(iface->conf)) {
- 	case CONF_OPER_CHWIDTH_USE_HT:
-@@ -1115,6 +1168,9 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
- 	case CONF_OPER_CHWIDTH_80MHZ:
- 		center = acs_get_bw_center_chan(iface->freq, ACS_BW80);
- 		break;
-+	case CONF_OPER_CHWIDTH_320MHZ:
-+		center = acs_get_center_freq_320mhz(iface->conf->channel);
-+		break;
- 	case CONF_OPER_CHWIDTH_160MHZ:
- 		center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
- 		break;
-@@ -1122,7 +1178,7 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
- 		/* TODO: How can this be calculated? Adjust
- 		 * acs_find_ideal_chan() */
- 		wpa_printf(MSG_INFO,
--			   "ACS: Only VHT20/40/80/160 is supported now");
-+			   "ACS: Only VHT20/40/80/160 EHT320 is supported now");
- 		return;
- 	}
- 
-@@ -1185,7 +1241,8 @@ static void acs_study(struct hostapd_iface *iface)
- 	iface->conf->punct_bitmap = ideal_chan->punct_bitmap;
- #endif /* CONFIG_IEEE80211BE */
- 
--	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
-+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
-+		iface->conf->ieee80211be) {
- 		acs_adjust_secondary(iface);
- 		acs_adjust_center_freq(iface);
- 	}
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0078-mtk-hostapd-Refactor-static-PP-and-mld-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0078-mtk-hostapd-Refactor-static-PP-and-mld-support.patch
new file mode 100644
index 0000000..c20f618
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0078-mtk-hostapd-Refactor-static-PP-and-mld-support.patch
@@ -0,0 +1,336 @@
+From a6baff35c3303204be7df2a0ec001e31e428a6c3 Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Tue, 2 Apr 2024 16:51:07 +0800
+Subject: [PATCH 078/126] mtk: hostapd: Refactor static PP and mld support
+
+Add band_idx attribute in pp cmd for vendor cmd under mld setting.
+
+---
+ hostapd/config_file.c        |  6 ++--
+ hostapd/ctrl_iface.c         | 69 +++++++++++++++++++++++++-----------
+ hostapd/ctrl_iface.h         |  3 +-
+ hostapd/hostapd_cli.c        | 15 ++++++++
+ src/ap/ap_config.h           |  4 +--
+ src/ap/ap_drv_ops.c          |  7 ++--
+ src/ap/dfs.c                 |  3 ++
+ src/ap/hostapd.c             |  4 +--
+ src/common/mtk_vendor.h      |  1 +
+ src/drivers/driver.h         |  3 +-
+ src/drivers/driver_nl80211.c |  4 ++-
+ 11 files changed, 87 insertions(+), 32 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index c1bc344dd..44615c564 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5452,7 +5452,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		if (get_u16(pos, line, &conf->punct_bitmap))
+ 			return 1;
+ 		conf->punct_bitmap = atoi(pos);
+-		conf->pp_mode = PP_MANUAL_MODE;
++		conf->pp_mode = PP_USR_MODE;
+ 	} else if (os_strcmp(buf, "punct_acs_threshold") == 0) {
+ 		int val = atoi(pos);
+ 
+@@ -5549,8 +5549,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 	} else if (os_strcmp(buf, "pp_mode") == 0) {
+ 		int val = atoi(pos);
+ 
+-		if ((val != PP_MANUAL_MODE && conf->punct_bitmap) ||
+-		    val < PP_DISABLE || val > PP_MANUAL_MODE) {
++		if ((val != PP_USR_MODE && conf->punct_bitmap) ||
++		    val < PP_DISABLE || val > PP_USR_MODE) {
+ 			wpa_printf(MSG_ERROR, "Line %d: invalid pp_mode value",
+ 				   line);
+ 			return 1;
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 9bd8a46b3..e19502d1d 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4992,27 +4992,57 @@ hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cm
+ 	return os_snprintf(buf, buflen, "OK\n");
+ }
+ 
++struct hostapd_data *
++hostapd_get_hapd_by_band_idx(struct hostapd_data *hapd, u8 band_idx)
++{
++	struct hostapd_data *link;
++
++	if (!hostapd_is_mld_ap(hapd))
++		return hapd;
++
++	for_each_mld_link(link, hapd) {
++		if (link->iconf->band_idx == band_idx)
++			break;
++	}
++
++	if (!link || link->iconf->band_idx != band_idx) {
++		wpa_printf(MSG_ERROR, "Invalid band idx %d\n", band_idx);
++		return NULL;
++	}
++
++	return link;
++}
++
+ static int
+ hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ 			  size_t buflen)
+ {
+-	char *pos, *config, *value;
++	char *band, *config, *value;
++	u8 band_idx;
+ 
+ 	config = cmd;
+-	pos = os_strchr(config, ' ');
+-	if (pos == NULL)
++
++	value = os_strchr(config, ' ');
++	if (value == NULL)
+ 		return -1;
+-	*pos++ = '\0';
++	*value++ = '\0';
+ 
+-	if (pos == NULL)
++	band = os_strchr(value, ' ');
++	if (band == NULL)
++		return -1;
++	*band++ = '\0';
++	band_idx = strtol(band, NULL, 10);
++
++	hapd = hostapd_get_hapd_by_band_idx(hapd, band_idx);
++
++	if (!hapd)
+ 		return -1;
+-	value = pos;
+ 
+ 	if (os_strcmp(config, "mode") == 0) {
+-		int val = atoi(value);
++		int val = strtol(value, NULL, 10);
+ 
+-		if (val < PP_DISABLE || val > PP_AUTO_MODE) {
+-			wpa_printf(MSG_ERROR, "Invalid value for set_pp");
++		if (val < PP_DISABLE || val > PP_FW_MODE) {
++			wpa_printf(MSG_ERROR, "Invalid value for SET_PP");
+ 			return -1;
+ 		}
+ 		hapd->iconf->pp_mode = (u8) val;
+@@ -5020,7 +5050,8 @@ hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ 			return -1;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+-			   "Unsupported parameter %s for set_pp", config);
++			   "Unsupported parameter %s for SET_PP"
++			   "Usage: set_pp mode <value> <band_idx>", config);
+ 		return -1;
+ 	}
+ 	return os_snprintf(buf, buflen, "OK\n");
+@@ -5030,19 +5061,17 @@ static int
+ hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ 			  size_t buflen)
+ {
+-	char *pos, *end;
++	u8 band_idx;
+ 
+-	pos = buf;
+-	end = buf + buflen;
++	band_idx = strtol(cmd, NULL, 10);
+ 
+-	if (os_strcmp(cmd, "mode") == 0) {
+-		return os_snprintf(pos, end - pos, "pp_mode: %d\n",
+-				   hapd->iconf->pp_mode);
+-	} else {
+-		wpa_printf(MSG_ERROR,
+-			   "Unsupported parameter %s for get_pp", cmd);
++	hapd = hostapd_get_hapd_by_band_idx(hapd, band_idx);
++
++	if (!hapd)
+ 		return -1;
+-	}
++
++	return os_snprintf(buf, buflen, "pp_mode: %d, punct_bitmap: 0x%04x\n",
++			   hapd->iconf->pp_mode, hapd->iconf->punct_bitmap);
+ }
+ 
+ static int
+diff --git a/hostapd/ctrl_iface.h b/hostapd/ctrl_iface.h
+index ec5a95be7..753761c22 100644
+--- a/hostapd/ctrl_iface.h
++++ b/hostapd/ctrl_iface.h
+@@ -39,5 +39,6 @@ hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface)
+ {
+ }
+ #endif /* CONFIG_NO_CTRL_IFACE */
+-
++struct hostapd_data *
++hostapd_get_hapd_by_band_idx(struct hostapd_data *hapd, u8 band_idx);
+ #endif /* CTRL_IFACE_H */
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index f54fa9997..4e94db21d 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1755,6 +1755,17 @@ static int hostapd_cli_cmd_dump_csi(struct wpa_ctrl *ctrl, int argc,
+ {
+ 	return hostapd_cli_cmd(ctrl, "DUMP_CSI", 1, argc, argv);
+ }
++static int hostapd_cli_cmd_set_pp(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "set_pp", 3, argc, argv);
++}
++
++static int hostapd_cli_cmd_get_pp(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "get_pp", 1, argc, argv);
++}
+ 
+ struct hostapd_cli_cmd {
+ 	const char *cmd;
+@@ -2008,6 +2019,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 		" = Set csi configuaration"},
+ 	{ "dump_csi", hostapd_cli_cmd_dump_csi, NULL,
+ 		" = Dump csi data to a json file"},
++	{ "set_pp", hostapd_cli_cmd_set_pp, NULL,
++		" = Set preamble puncture mode"},
++	{ "get_pp", hostapd_cli_cmd_get_pp, NULL,
++		" = Get preamble puncture status"},
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index c61e67403..417b1d630 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1406,8 +1406,8 @@ enum mtk_vendor_attr_edcca_ctrl_mode {
+ 
+ enum pp_mode {
+ 	PP_DISABLE = 0,
+-	PP_AUTO_MODE,
+-	PP_MANUAL_MODE,
++	PP_FW_MODE,
++	PP_USR_MODE,
+ };
+ 
+ #define EDCCA_DEFAULT_COMPENSATION -6
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index a26a3f45d..fb09a3290 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1432,10 +1432,13 @@ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
+ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
+ {
+ 	if (!hapd->driver || !hapd->driver->pp_mode_set ||
+-	    hapd->iconf->pp_mode > PP_AUTO_MODE)
++	    hapd->iconf->pp_mode >= PP_USR_MODE ||
++	    hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+ 		return 0;
++
+ 	return hapd->driver->pp_mode_set(hapd->drv_priv,
+-					 hapd->iconf->pp_mode);
++					 hapd->iconf->pp_mode,
++					 hapd->iconf->band_idx);
+ }
+ 
+ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index db26f6536..fc7699973 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1081,6 +1081,7 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
+ 	os_memset(&csa_settings, 0, sizeof(csa_settings));
+ 	csa_settings.cs_count = 5;
+ 	csa_settings.block_tx = 1;
++	csa_settings.punct_bitmap = hostapd_get_punct_bitmap(iface->bss[0]);
+ 	csa_settings.link_id = -1;
+ #ifdef CONFIG_IEEE80211BE
+ 	if (iface->bss[0]->conf->mld_ap)
+@@ -1657,6 +1658,8 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+ 			return 0;
+ 	}
+ 
++	iface->bss[0]->iconf->punct_bitmap = 0;
++
+ 	if (hostapd_dfs_background_start_channel_switch(iface, freq)) {
+ 		/* Radar detected while operating, switch the channel. */
+ 		return hostapd_dfs_start_channel_switch(iface);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 93c164177..f5e11d43d 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2766,6 +2766,8 @@ dfs_offload:
+ 	}
+ #endif /* CONFIG_MESH */
+ 
++	if (hostapd_drv_pp_mode_set(hapd) < 0)
++		goto fail;
+ 	if (hostapd_drv_configure_edcca_enable(hapd) < 0)
+ 		goto fail;
+ 
+@@ -2780,8 +2782,6 @@ dfs_offload:
+ 		goto fail;
+ 	if (hostapd_drv_amsdu_ctrl(hapd) < 0)
+ 		goto fail;
+-	if (hostapd_drv_pp_mode_set(hapd) < 0)
+-		goto fail;
+ 
+ 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ 		   iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index ce3a2ad9a..be516e017 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -268,6 +268,7 @@ enum mtk_vendor_attr_pp_ctrl {
+ 	MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
+ 
+ 	MTK_VENDOR_ATTR_PP_MODE,
++	MTK_VENDOR_ATTR_PP_BAND_IDX,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_PP_CTRL,
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 48cb93cfd..b75580374 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5364,8 +5364,9 @@ struct wpa_driver_ops {
+ 	 * pp_mode_set - Set preamble puncture operation mode
+ 	 * @priv: Private driver interface data
+ 	 * @pp_mode: Value is defined in enum pp_mode
++	 * @band_idx: chip band index
+ 	 */
+-	int (*pp_mode_set)(void *priv, const u8 pp_mode);
++	int (*pp_mode_set)(void *priv, const u8 pp_mode, u8 band_idx);
+ #ifdef CONFIG_IEEE80211BE
+ 	int (*get_mld_addr)(void *priv, u8 *addr);
+ #endif
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index afb068091..8b64c3983 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -159,6 +159,7 @@ amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
+ static struct nla_policy
+ pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
+ 	[MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_PP_BAND_IDX] = { .type = NLA_U8 },
+ };
+ 
+ static struct nla_policy csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
+@@ -15196,7 +15197,7 @@ static int nl80211_background_radar_mode(void *priv, const u8 background_radar_m
+ 	return ret;
+ }
+ 
+-static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
++static int nl80211_pp_mode_set(void *priv, const u8 pp_mode, u8 band_idx)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+@@ -15223,6 +15224,7 @@ static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
+ 	if (!data)
+ 		goto fail;
+ 
++	nla_put_u8(msg, MTK_VENDOR_ATTR_PP_BAND_IDX, band_idx);
+ 	nla_put_u8(msg, MTK_VENDOR_ATTR_PP_MODE, pp_mode);
+ 
+ 	nla_nest_end(msg, data);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0079-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0079-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch
deleted file mode 100644
index a28db10..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0079-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch
+++ /dev/null
@@ -1,122 +0,0 @@
-From 26c23f7dc1fe47e22ceab581b7abed089148c68f Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Thu, 19 Oct 2023 13:38:11 +0800
-Subject: [PATCH 079/104] mtk: hostapd: initialize i802_bss's flink->freq with
- iface freq.
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
----
- src/ap/ap_drv_ops.c          | 6 +++---
- src/ap/ap_drv_ops.h          | 2 +-
- src/ap/hostapd.c             | 2 +-
- src/drivers/driver.h         | 2 +-
- src/drivers/driver_nl80211.c | 4 ++--
- wpa_supplicant/driver_i.h    | 2 +-
- 6 files changed, 9 insertions(+), 9 deletions(-)
-
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index d6bd157d8..b7896c110 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -368,7 +368,7 @@ int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
- 	char force_ifname[IFNAMSIZ];
- 	u8 if_addr[ETH_ALEN];
- 	return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
--			      NULL, NULL, force_ifname, if_addr, NULL, 0);
-+			      NULL, NULL, force_ifname, if_addr, NULL, 0, hapd->iface->freq);
- }
- 
- 
-@@ -560,13 +560,13 @@ int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
- int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
- 		   const char *ifname, const u8 *addr, void *bss_ctx,
- 		   void **drv_priv, char *force_ifname, u8 *if_addr,
--		   const char *bridge, int use_existing)
-+		   const char *bridge, int use_existing, int freq)
- {
- 	if (hapd->driver == NULL || hapd->driver->if_add == NULL)
- 		return -1;
- 	return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
- 				    bss_ctx, drv_priv, force_ifname, if_addr,
--				    bridge, use_existing, 1);
-+				    bridge, use_existing, 1, freq);
- }
- 
- 
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 78e5c8d5a..5830705a3 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -58,7 +58,7 @@ int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
- int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
- 		   const char *ifname, const u8 *addr, void *bss_ctx,
- 		   void **drv_priv, char *force_ifname, u8 *if_addr,
--		   const char *bridge, int use_existing);
-+		   const char *bridge, int use_existing, int freq);
- int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
- 		      const char *ifname);
- int hostapd_if_link_remove(struct hostapd_data *hapd,
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 636655ea1..e4fc1f85a 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -1433,7 +1433,7 @@ int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
- 				   conf->iface, addr, hapd,
- 				   &hapd->drv_priv, force_ifname, if_addr,
- 				   conf->bridge[0] ? conf->bridge : NULL,
--				   first == -1)) {
-+				   first == -1, hapd->iface->freq)) {
- 			wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
- 				   MACSTR ")", MAC2STR(hapd->own_addr));
- 			hapd->interface_added = 0;
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 332a51c55..2940650df 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -3866,7 +3866,7 @@ struct wpa_driver_ops {
- 	int (*if_add)(void *priv, enum wpa_driver_if_type type,
- 		      const char *ifname, const u8 *addr, void *bss_ctx,
- 		      void **drv_priv, char *force_ifname, u8 *if_addr,
--		      const char *bridge, int use_existing, int setup_ap);
-+		      const char *bridge, int use_existing, int setup_ap, int freq);
- 
- 	/**
- 	 * if_remove - Remove a virtual interface
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index e588e7538..3d69c9c49 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -8872,7 +8872,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
- 				     void *bss_ctx, void **drv_priv,
- 				     char *force_ifname, u8 *if_addr,
- 				     const char *bridge, int use_existing,
--				     int setup_ap)
-+				     int setup_ap, int freq)
- {
- 	enum nl80211_iftype nlmode;
- 	struct i802_bss *bss = priv;
-@@ -8992,7 +8992,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
- 		new_bss->valid_links = 0;
- 		os_memcpy(new_bss->flink->addr, new_bss->addr, ETH_ALEN);
- 
--		new_bss->flink->freq = drv->first_bss->flink->freq;
-+		new_bss->flink->freq = (freq == -1) ? drv->first_bss->flink->freq : freq;
- 		new_bss->ctx = bss_ctx;
- 		new_bss->added_if = added;
- 		drv->first_bss->next = new_bss;
-diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
-index 663e16053..624192ebd 100644
---- a/wpa_supplicant/driver_i.h
-+++ b/wpa_supplicant/driver_i.h
-@@ -446,7 +446,7 @@ static inline int wpa_drv_if_add(struct wpa_supplicant *wpa_s,
- 	if (wpa_s->driver->if_add)
- 		return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname,
- 					     addr, bss_ctx, NULL, force_ifname,
--					     if_addr, bridge, 0, 0);
-+					     if_addr, bridge, 0, 0, -1);
- 	return -1;
- }
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0079-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0079-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch
new file mode 100644
index 0000000..afc66a5
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0079-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch
@@ -0,0 +1,183 @@
+From 288062b9de2d669b809ae7b0035ca4680c358192 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 11 Apr 2024 18:16:38 +0800
+Subject: [PATCH 079/126] mtk: hostapd: make sure all links are set before
+ enabling beacon
+
+NL80211_CMD_NEW_BEACON will first be set, but we've modified mac80211 to
+disable this beacon. After that, hostapd will block
+NL80211_CMD_SET_BEACON until all links are setting up.
+(use NL80211_CMD_START_AP event to check if all expected links are enabled)
+
+Update: in wpa_driver_nl80211_set_ap(), send_and_recv() is used, implies
+that hostapd should already sync with driver, so don't need to use
+NL80211_CMD_START_AP event.
+
+This can make sure that the first beacon of each link includes the
+correct RNR and per-STA profile.
+
+Note that in NL80211_CMD_NEW_BEACON, we also set beacon interval to 0,
+which helps to bypass some mac80211 beacon active checks, e.g., during ACS.
+
+Add is_mld_finished check for ucode need.
+This function returns ture only if all links fromt all MLD APs are
+ready.
+
+Only after hostapd sets beacon for all links that hapd->mld->started is
+set to true. However, if the interface is about to do CAC,
+hapd->mld->started will be false until the CAC is done.
+
+For ucode, it only have to ckeck whether all link is added. Instead of
+checking hapd->mld->started, this commits check the link one by one, and
+return false if there are links unadded.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ hostapd/config_file.c |  2 ++
+ src/ap/ap_config.h    |  2 ++
+ src/ap/beacon.c       | 10 ++++++++++
+ src/ap/hostapd.c      | 14 ++++++++++++++
+ src/ap/hostapd.h      |  1 +
+ src/ap/ucode.c        | 27 +++++++++++++++++++++++++++
+ 6 files changed, 56 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 44615c564..206055b75 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5467,6 +5467,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		bss->mld_ap = !!atoi(pos);
+ 	} else if (os_strcmp(buf, "mld_primary") == 0) {
+ 		bss->mld_primary = !!atoi(pos);
++	} else if (os_strcmp(buf, "mld_allowed_links") == 0) {
++		bss->mld_allowed_links = atoi(pos);
+ 	} else if (os_strcmp(buf, "mld_addr") == 0) {
+ 		if (hwaddr_aton(pos, bss->mld_addr)) {
+ 			wpa_printf(MSG_ERROR, "Line %d: Invalid mld_addr",
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 417b1d630..15b66ca30 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -989,6 +989,8 @@ struct hostapd_bss_config {
+ 
+ 	/* The AP is the primary AP of an AP MLD */
+ 	u8 mld_primary;
++	/* Allowed link bitmap of the AP MLD to which the AP is affiliated */
++	u16 mld_allowed_links;
+ 
+ 	/* The MLD ID to which the AP MLD is affiliated with */
+ 	u8 mld_id;
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 9bf73747f..88e35acc2 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -2276,6 +2276,12 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ 	os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
+ 	head->u.beacon.beacon_int =
+ 		host_to_le16(hapd->iconf->beacon_int);
++	/* if MLD AP hasn't finished setting up all links, also set beacon interval
++	 * to 0. This allows mac80211 to bypass some beacon active checks, for
++	 * example, when doing ACS
++	 */
++	if (hapd->conf->mld_ap && !hapd->mld->started)
++		head->u.beacon.beacon_int = host_to_le16(0);
+ 
+ 	/* hardware or low-level driver will setup seq_ctrl and timestamp */
+ 	capab_info = hostapd_own_capab_info(hapd);
+@@ -2677,6 +2683,10 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
+ 	int res, ret = -1, i;
+ 	struct hostapd_hw_modes *mode;
+ 
++	/* skip setting beacon if other links are not started yet */
++	if (hapd->conf->mld_ap && !hapd->mld->started && hapd->beacon_set_done)
++		return 0;
++
+ 	if (!hapd->drv_priv) {
+ 		wpa_printf(MSG_ERROR, "Interface is disabled");
+ 		return -1;
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index f5e11d43d..fe41f1821 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -1315,6 +1315,20 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
+ 	if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
+ 		return -1;
+ 
++	if (hapd->conf->mld_ap && !hapd->mld->started) {
++		struct hostapd_data *p_hapd;
++		u16 valid_links = 0;
++
++		for_each_mld_link(p_hapd, hapd)
++			valid_links |= BIT(p_hapd->mld_link_id);
++
++		if (valid_links == hapd->conf->mld_allowed_links ||
++		    !hapd->conf->mld_allowed_links) {
++			hapd->mld->started = 1;
++			ieee802_11_set_beacon(hapd);
++		}
++	}
++
+ 	if (flush_old_stations && !conf->start_disabled &&
+ 	    conf->broadcast_deauth) {
+ 		u8 addr[ETH_ALEN];
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 574a5ba1d..4db67096b 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -544,6 +544,7 @@ struct hostapd_mld {
+ 	 * freed when num_links is 0.
+ 	 */
+ 	u8 refcount;
++	bool started;
+ 
+ 	struct hostapd_data *fbss;
+ 	struct dl_list links; /* List head of all affiliated links */
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index 68f76dbe5..da1c4c1ac 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -744,6 +744,32 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 	return ucv_boolean_new(!ret);
+ }
+ 
++static uc_value_t *
++uc_hostapd_iface_is_mld_finished(uc_vm_t *vm, size_t nargs)
++{
++	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++	bool finished = true;
++	int i;
++
++	for (i = 0; i < iface->num_bss; i++) {
++		if (iface->bss[i]->conf->mld_ap) {
++			struct hostapd_data *p_hapd;
++			u16 valid_links = 0;
++
++			for_each_mld_link(p_hapd, iface->bss[i])
++				valid_links |= BIT(p_hapd->mld_link_id);
++
++			if (iface->bss[i]->conf->mld_allowed_links > 0 &&
++			    valid_links != iface->bss[i]->conf->mld_allowed_links) {
++				finished = false;
++				break;
++			}
++		}
++	}
++
++	return ucv_boolean_new(finished);
++}
++
+ static uc_value_t *
+ uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
+ {
+@@ -816,6 +842,7 @@ int hostapd_ucode_init(struct hapd_interfaces *ifaces)
+ 		{ "stop", uc_hostapd_iface_stop },
+ 		{ "start", uc_hostapd_iface_start },
+ 		{ "switch_channel", uc_hostapd_iface_switch_channel },
++		{ "is_mld_finished", uc_hostapd_iface_is_mld_finished },
+ 	};
+ 	uc_value_t *data, *proto;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0080-mtk-hostapd-add-hidden-SSID-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0080-mtk-hostapd-add-hidden-SSID-support.patch
new file mode 100644
index 0000000..6399fc1
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0080-mtk-hostapd-add-hidden-SSID-support.patch
@@ -0,0 +1,67 @@
+From dd7f4e849efee36586e0b39f9f1e54a1a2000a49 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Wed, 24 Apr 2024 17:44:40 +0800
+Subject: [PATCH 080/126] mtk: hostapd: add hidden SSID support
+
+Add hidden SSID support for MLD AP. Now the parnter link's information is
+included in RNR even if the link is hidden.
+Note that the hidden links' information appear in both beacon and probe
+response.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ieee802_11.c | 17 ++++++++++++++---
+ 1 file changed, 14 insertions(+), 3 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index fbe3f582f..37a2c5ab8 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7483,16 +7483,21 @@ repeat_rnr_len:
+ 		for (i = start; i < hapd->iface->num_bss; i++) {
+ 			struct hostapd_data *bss = hapd->iface->bss[i];
+ 			bool ap_mld = false;
++			bool ignore_broadcast_ssid;
+ 
+ 			if (!bss || !bss->conf || !bss->started)
+ 				continue;
+ 
++			ignore_broadcast_ssid = bss->conf->ignore_broadcast_ssid;
+ #ifdef CONFIG_IEEE80211BE
+ 			ap_mld = bss->conf->mld_ap;
++			/* FIXME How to exclude the hidden link in beacon? */
++			ignore_broadcast_ssid &=
++				!hostapd_is_ml_partner(bss, reporting_hapd);
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ 			if (bss == reporting_hapd ||
+-			    bss->conf->ignore_broadcast_ssid)
++			    ignore_broadcast_ssid)
+ 				continue;
+ 
+ 			if (hostapd_skip_rnr(i, skip_profiles, ap_mld,
+@@ -7751,13 +7756,19 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
+ 	u8 bss_param = 0;
+ 	bool ap_mld = false;
+ 	u8 *eid = *pos;
++	bool ignore_broadcast_ssid;
+ 
++	if (!bss || !bss->conf || !bss->started)
++		return false;
++
++	ignore_broadcast_ssid = bss->conf->ignore_broadcast_ssid;
+ #ifdef CONFIG_IEEE80211BE
+ 	ap_mld = !!hapd->conf->mld_ap;
++	/* FIXME How to exclude the hidden link in beacon? */
++	ignore_broadcast_ssid &= !hostapd_is_ml_partner(bss, reporting_hapd);
+ #endif /* CONFIG_IEEE80211BE */
+ 
+-	if (!bss || !bss->conf || !bss->started ||
+-	    bss == reporting_hapd || bss->conf->ignore_broadcast_ssid)
++	if (bss == reporting_hapd || ignore_broadcast_ssid)
+ 		return false;
+ 
+ 	if (hostapd_skip_rnr(i, skip_profiles, ap_mld, tbtt_info_len,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0080-mtk-hostapd-fix-mld_assoc_link_id.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0080-mtk-hostapd-fix-mld_assoc_link_id.patch
deleted file mode 100644
index 42c06e4..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0080-mtk-hostapd-fix-mld_assoc_link_id.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From d06dd0d45977ce098df718a29ffbc765896a2758 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Mon, 29 Jan 2024 11:24:28 +0800
-Subject: [PATCH 080/104] mtk: hostapd: fix mld_assoc_link_id
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- src/ap/hostapd.c | 10 ++++++----
- 1 file changed, 6 insertions(+), 4 deletions(-)
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index e4fc1f85a..f8b05de45 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -4034,11 +4034,13 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
- 	}
- 
- #ifdef CONFIG_IEEE80211BE
--	if (ap_sta_is_mld(hapd, sta) &&
--	    sta->mld_assoc_link_id != hapd->mld_link_id)
--		return;
-+	if (ap_sta_is_mld(hapd, sta)) {
-+		if (sta->mld_assoc_link_id != hapd->mld_link_id)
-+			return;
-+		mld_assoc_link_id = sta->mld_assoc_link_id;
-+	}
- #endif /* CONFIG_IEEE80211BE */
--        if (mld_assoc_link_id != -2)
-+	if (mld_assoc_link_id != -2)
- 		hostapd_prune_associations(hapd, sta->addr, mld_assoc_link_id);
- 
- 	ap_sta_clear_disconnect_timeouts(hapd, sta);
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0081-mtk-hostapd-do-not-roam-within-the-same-MLD.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0081-mtk-hostapd-do-not-roam-within-the-same-MLD.patch
new file mode 100644
index 0000000..9519c2f
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0081-mtk-hostapd-do-not-roam-within-the-same-MLD.patch
@@ -0,0 +1,31 @@
+From 1d64b54fc9ea3e53a6369e609a9b4e83e800a1e4 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 26 Apr 2024 08:36:57 +0800
+Subject: [PATCH 081/126] mtk: hostapd: do not roam within the same MLD
+
+If STA scaned and selected a different links from the same MLD AP, the
+check by wpa_s would make it to roam within ESS, which is unnecessary
+for a non-AP MLD.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/events.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
+index 68f4d2dbe..83b1dcc21 100644
+--- a/wpa_supplicant/events.c
++++ b/wpa_supplicant/events.c
+@@ -2395,6 +2395,9 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
+ 	if (current_bss == selected)
+ 		return 0;
+ 
++	if (wpa_s->valid_links && ether_addr_equal(selected->mld_addr, wpa_s->bssid))
++		return 0; /* same AP MLD but different links */
++
+ 	if (selected->last_update_idx > current_bss->last_update_idx)
+ 		return 1; /* current BSS not seen in the last scan */
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0081-mtk-wpa_s-correctly-get-assoc-frequency.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0081-mtk-wpa_s-correctly-get-assoc-frequency.patch
deleted file mode 100644
index 7bdb4fe..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0081-mtk-wpa_s-correctly-get-assoc-frequency.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From 3dbd0105364c15225a18098eeaae58119490918d Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 19 Oct 2023 10:48:11 +0800
-Subject: [PATCH 081/104] mtk: wpa_s: correctly get assoc frequency
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- src/drivers/driver_nl80211_event.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 6631285bf..90084356d 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -328,6 +328,7 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
- 			   wpa_ssid_txt(drv->ssid, drv->ssid_len));
- 	}
- 
-+	drv->assoc_freq = nl80211_get_assoc_freq(drv);
- 	event.assoc_info.freq = drv->assoc_freq;
- 	drv->first_bss->flink->freq = drv->assoc_freq;
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0082-mtk-hostapd-update-op_class-in-channel-switch-fallba.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0082-mtk-hostapd-update-op_class-in-channel-switch-fallba.patch
new file mode 100644
index 0000000..b7812cd
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0082-mtk-hostapd-update-op_class-in-channel-switch-fallba.patch
@@ -0,0 +1,34 @@
+From 80eac0c00795cccd43db3c972a6c903dda55f4b0 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 29 Apr 2024 10:52:31 +0800
+Subject: [PATCH 082/126] mtk: hostapd: update op_class in channel switch
+ fallback
+
+Switching to a DFS channel includes an AP interface teardown and setup.
+An op_class update is necessary for passing the channel information
+check during setup.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/hostapd.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index fe41f1821..2c9e54b23 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4710,6 +4710,10 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+ 	iface->conf->ieee80211ac = freq_params->vht_enabled;
+ 	iface->conf->ieee80211ax = freq_params->he_enabled;
+ 	iface->conf->ieee80211be = freq_params->eht_enabled;
++	if (ieee80211_freq_to_channel_ext(iface->freq, iface->conf->secondary_channel,
++					  hostapd_get_oper_chwidth(iface->conf),
++					  &op_class, &chan) != NUM_HOSTAPD_MODES)
++		iface->conf->op_class = op_class;
+ 
+ 	/*
+ 	 * cs_params must not be cleared earlier because the freq_params
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0082-mtk-wpa_s-force-MLD-STA-to-use-SAE-H2E-during-authen.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0082-mtk-wpa_s-force-MLD-STA-to-use-SAE-H2E-during-authen.patch
deleted file mode 100644
index 8ddecc9..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0082-mtk-wpa_s-force-MLD-STA-to-use-SAE-H2E-during-authen.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From 50e36560d14dbb6bf38b46dcfc58f9414e56b283 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 19 Oct 2023 10:51:55 +0800
-Subject: [PATCH 082/104] mtk: wpa_s: force MLD STA to use SAE H2E during
- authentication
-
-Otherwise the MLD STA setup will fail with hostapd MLD AP.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- wpa_supplicant/sme.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
-index f08184f98..e1183722f 100644
---- a/wpa_supplicant/sme.c
-+++ b/wpa_supplicant/sme.c
-@@ -199,7 +199,7 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
- 	if (wpa_key_mgmt_sae_ext_key(key_mgmt) &&
- 	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
- 		use_pt = 1;
--	if (bss && is_6ghz_freq(bss->freq) &&
-+	if (bss && (is_6ghz_freq(bss->freq) || !is_zero_ether_addr(bss->mld_addr)) &&
- 	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
- 		use_pt = 1;
- #ifdef CONFIG_SAE_PK
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0083-mtk-hostapd-always-do-ML-probe-request-before-authen.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0083-mtk-hostapd-always-do-ML-probe-request-before-authen.patch
new file mode 100644
index 0000000..0327d5e
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0083-mtk-hostapd-always-do-ML-probe-request-before-authen.patch
@@ -0,0 +1,41 @@
+From 13f84bce5e02c21e0915b5f4d46a02133237e847 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 6 May 2024 18:20:52 +0800
+Subject: [PATCH 083/126] mtk: hostapd: always do ML probe request before
+ authentication
+
+The scan result might contain old information of an AP MLD, so a ML
+probe might be necessary to update the scan result before the
+authentication.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/events.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
+index 83b1dcc21..7b91ce988 100644
+--- a/wpa_supplicant/events.c
++++ b/wpa_supplicant/events.c
+@@ -1925,15 +1925,16 @@ static int wpa_supplicant_connect_ml_missing(struct wpa_supplicant *wpa_s,
+ 
+ 	if (wpa_bss_parse_basic_ml_element(wpa_s, selected, NULL,
+ 					   &missing_links, ssid,
+-					   &ap_mld_id) ||
+-	    !missing_links)
++					   &ap_mld_id))
+ 		return 0;
+ 
+ 	removed_links = wpa_bss_parse_reconf_ml_element(wpa_s, selected);
+ 	missing_links &= ~removed_links;
+ 
++	/* FIXME Always do ML probe for the sake of stability.
+ 	if (!missing_links)
+ 		return 0;
++	*/
+ 
+ 	wpa_dbg(wpa_s, MSG_DEBUG,
+ 		"MLD: Doing an ML probe for missing links 0x%04x",
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0083-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0083-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch
deleted file mode 100644
index b3144cb..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0083-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch
+++ /dev/null
@@ -1,71 +0,0 @@
-From fb3820ff9fff1b15c13d4a799fbef8932fda7a1b Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Mon, 11 Dec 2023 17:02:05 +0800
-Subject: [PATCH 083/104] mtk: hostapd: extend ap_get_sta() to find the correct
- sta
-
-There're still some mld address tranlation issues that need to be dealt
-with on driver side (e.g. RX eapol frames). So add the code that find
-station also with link address and across hapds at the moment.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- src/ap/ieee802_11.c |  1 +
- src/ap/sta_info.c   | 16 ++++++++++++++++
- src/ap/sta_info.h   |  1 +
- 3 files changed, 18 insertions(+)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index ce3874901..0f357d786 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -3116,6 +3116,7 @@ static void handle_auth(struct hostapd_data *hapd,
- 				  mgmt->sa, ETH_ALEN);
- 			os_memcpy(sta->mld_info.links[link_id].local_addr,
- 				  hapd->own_addr, ETH_ALEN);
-+			os_memcpy(sta->setup_link_addr, mgmt->sa, ETH_ALEN);
- 		}
- 	}
- #endif /* CONFIG_IEEE80211BE */
-diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
-index ee6e20538..e9fa0ed6e 100644
---- a/src/ap/sta_info.c
-+++ b/src/ap/sta_info.c
-@@ -73,6 +73,22 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
- 	s = hapd->sta_hash[STA_HASH(sta)];
- 	while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
- 		s = s->hnext;
-+
-+	if (hapd->conf->mld_ap && !s) {
-+		u8 link_id;
-+
-+		for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
-+			struct hostapd_data *h = hostapd_mld_get_link_bss(hapd, link_id);
-+
-+			if (!h)
-+				continue;
-+
-+			for (s = h->sta_list; s; s = s->next)
-+				if (!os_memcmp(s->setup_link_addr, sta, 6))
-+					return s;
-+		}
-+	}
-+
- 	return s;
- }
- 
-diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
-index 38b80903d..cd89db6c8 100644
---- a/src/ap/sta_info.h
-+++ b/src/ap/sta_info.h
-@@ -102,6 +102,7 @@ struct sta_info {
- 	struct sta_info *next; /* next entry in sta list */
- 	struct sta_info *hnext; /* next entry in hash table list */
- 	u8 addr[6];
-+	u8 setup_link_addr[6];
- 	be32 ipaddr;
- 	struct dl_list ip6addr; /* list head for struct ip6addr */
- 	u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0084-mtk-hostapd-prevent-responding-to-mgmt-while-AP-MLD-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0084-mtk-hostapd-prevent-responding-to-mgmt-while-AP-MLD-.patch
new file mode 100644
index 0000000..82b31c7
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0084-mtk-hostapd-prevent-responding-to-mgmt-while-AP-MLD-.patch
@@ -0,0 +1,36 @@
+From 1e54da693f94da15372d3e397c362a20fb5b1af1 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 6 May 2024 18:14:35 +0800
+Subject: [PATCH 084/126] mtk: hostapd: prevent responding to mgmt while AP MLD
+ is initializing
+
+While AP MLD is initializing, it might include incomplete information
+inside its response of mgmt.
+Therefore this commit prevents responding to mgmt while AP MLD is initializing
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ieee802_11.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 37a2c5ab8..1365bc822 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -6305,6 +6305,13 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+ 	if (len < 24)
+ 		return 0;
+ 
++#ifdef CONFIG_IEEE80211BE
++	if (hapd->conf->mld_ap && !hapd->mld->started) {
++		wpa_printf(MSG_DEBUG, "MGMT: Drop the frame - MLD not ready");
++		return 1;
++	}
++#endif /* CONFIG_IEEE80211BE */
++
+ 	if (fi && fi->freq)
+ 		freq = fi->freq;
+ 	else
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0084-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0084-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch
deleted file mode 100644
index 650da0e..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0084-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From 665bc7cb59b4383aab615fff82fa601c468a4634 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Mon, 18 Dec 2023 18:53:35 +0800
-Subject: [PATCH 084/104] mtk: hostapd: update cookie only when noack is unset
-
-This can prevent cookie unmatched problems during setup.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- src/drivers/driver_nl80211.c | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 3d69c9c49..6d300c0c8 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -4472,7 +4472,7 @@ send_frame_cmd:
- 	res = nl80211_send_frame_cmd(bss, freq, wait_time, data, data_len,
- 				     use_cookie, no_cck, noack, offchanok,
- 				     csa_offs, csa_offs_len, link_id);
--	if (!res)
-+	if (!res && !noack)
- 		drv->send_frame_link_id = link_id;
- 
- 	return res;
-@@ -9205,8 +9205,8 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss,
- 			   "cookie 0x%llx", no_ack ? " (no ACK)" : "",
- 			   (long long unsigned int) cookie);
- 
--		if (save_cookie)
--			drv->send_frame_cookie = no_ack ? (u64) -1 : cookie;
-+		if (save_cookie && !no_ack)
-+			drv->send_frame_cookie = cookie;
- 
- 		if (!wait) {
- 			 /* There is no need to store this cookie since there
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0085-mtk-hostapd-add-critical-update-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0085-mtk-hostapd-add-critical-update-support.patch
new file mode 100644
index 0000000..f34a8ad
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0085-mtk-hostapd-add-critical-update-support.patch
@@ -0,0 +1,945 @@
+From d46df816cadde59bde528c08db281923874152cc Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 23 Apr 2024 16:03:19 +0800
+Subject: [PATCH 085/126] mtk: hostapd: add critical update support
+
+Add critical update support
+modification: wmm configuration
+inclusion: channel switch
+(affiliated link's per-STA profile CSA/eCSA countdown &
+ channel switch wrapper is included)
+Note that max channel switch time IE is not implemented
+in hostapd yet.
+
+1.Add max channel switch time IE
+Note that fw will find the MCST IE on its own,
+so there is no need to send the offset of the MCST IE to the kernel.
+The MCST's value is currently hardcoded to 500 TUs, since the WFA test plan
+restricts it to not exceeding 500 TUs (APUT 4.22.1).
+2. Add critical update support for radar triggered channel switch
+
+According to the test plan of APUT 4.44, the CSA after beacon should also
+include the CU flag and increase BPCC due to operation IE modification.
+Additionally, avoid setting beacons in hostapd_event_ch_switch after CSA
+is finished since it would conflict with the CSA after beacon.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/ctrl_iface.c         |  88 +++++++++++++++++++
+ hostapd/hostapd_cli.c        |   8 ++
+ hostapd/main.c               |   4 +-
+ src/ap/beacon.c              | 161 ++++++++++++++++++++++++++++++++++-
+ src/ap/beacon.h              |  37 ++++++++
+ src/ap/dfs.c                 |  12 +++
+ src/ap/drv_callbacks.c       |   1 -
+ src/ap/hostapd.c             |  85 +++++++++++++++++-
+ src/ap/hostapd.h             |   5 ++
+ src/ap/ieee802_11.c          |  15 ++++
+ src/ap/ieee802_11_eht.c      |   7 +-
+ src/ap/sta_info.h            |   6 ++
+ src/ap/ucode.c               |   9 +-
+ src/common/ieee802_11_defs.h |   1 +
+ src/drivers/driver.h         |   1 +
+ src/drivers/driver_nl80211.c |  53 +++++++-----
+ src/drivers/nl80211_copy.h   |   8 ++
+ 17 files changed, 470 insertions(+), 31 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index e19502d1d..ebe9053cf 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -2911,6 +2911,18 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ 			ret = err;
+ 			num_err++;
+ 		}
++
++#ifdef CONFIG_IEEE80211BE
++		if (iface->bss[i]->conf->mld_ap)
++			hostapd_update_aff_link_beacon(iface->bss[i], settings.cs_count);
++
++		/*
++		 * Currently, no FW notification event for clearing CU flag after DTIM period.
++		 * Also, another CU or set beacon is not allowed during CSA period.
++		 * Therefore, just clear it manually here for workaround.
++		 */
++		iface->bss[i]->eht_mld_bss_critical_update = 0;
++#endif /* CONFIG_IEEE80211BE */
+ 	}
+ 
+ 	return (iface->num_bss == num_err) ? ret : 0;
+@@ -5279,6 +5291,79 @@ hostapd_ctrl_iface_dump_csi(struct hostapd_data *hapd, char *cmd,
+ 	return 0;
+ }
+ 
++static int
++hostapd_ctrl_iface_wmm(struct hostapd_data *hapd, char *cmd, char *buf,
++		       size_t buflen)
++{
++	char *pos = cmd, *ac, *token, *context = NULL;
++	struct hostapd_wmm_ac_params *acp;
++	int num;
++
++	if (!hapd->conf->mld_ap)
++		return -1;
++
++	ac = pos;
++	pos = os_strchr(pos, ' ');
++	if (pos)
++		*pos++ = '\0';
++
++	if (os_strncmp(ac, "BE", 2) == 0) {
++		num = 0;
++	} else if (os_strncmp(ac, "BK", 2) == 0) {
++		num = 1;
++	} else if (os_strncmp(ac, "VI", 2) == 0) {
++		num = 2;
++	} else if (os_strncmp(ac, "VO", 2) == 0) {
++		num = 3;
++	} else {
++		wpa_printf(MSG_ERROR, "Unknown AC name '%s'", ac);
++		return -1;
++	}
++
++	acp = &hapd->iconf->wmm_ac_params[num];
++
++	/* if only ac is provied, show wmm params */
++	if (!pos)
++		return os_snprintf(buf, buflen,
++				   "link=%d ac=%s cwmin=%d cwmax=%d aifs=%d txop_limit=%d\n",
++				   hapd->mld_link_id, ac, acp->cwmin, acp->cwmax, acp->aifs, acp->txop_limit);
++
++	while ((token = str_token(pos, " ", &context))) {
++		if (os_strncmp(token, "cwmin=", 6) == 0) {
++			acp->cwmin = atoi(token + 6);
++			continue;
++		}
++
++		if (os_strncmp(token, "cwmax=", 6) == 0) {
++			acp->cwmax = atoi(token + 6);
++			continue;
++		}
++
++		if (os_strncmp(token, "aifs=", 5) == 0) {
++			acp->aifs = atoi(token + 5);
++			continue;
++		}
++
++		if (os_strncmp(token, "txop_limit=", 11) == 0) {
++			acp->txop_limit = atoi(token + 11);
++			continue;
++		}
++
++		wpa_printf(MSG_ERROR, "CTRL: Invalid WMM parameter: %s", token);
++		return -1;
++	}
++
++	if (acp->cwmin > acp->cwmax)
++		return -1;
++
++	ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_EDCA);
++
++	if (ieee802_11_set_beacon(hapd))
++		return -1;
++
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -5946,6 +6031,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 	} else if (os_strncmp(buf, "DUMP_CSI ", 8) == 0) {
+ 		reply_len = hostapd_ctrl_iface_dump_csi(hapd, buf + 9,
+ 							reply, reply_size);
++	} else if (os_strncmp(buf, "WMM", 3) == 0) {
++		reply_len = hostapd_ctrl_iface_wmm(hapd, buf + 4,
++						   reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 4e94db21d..e578c5b7e 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1767,6 +1767,12 @@ static int hostapd_cli_cmd_get_pp(struct wpa_ctrl *ctrl, int argc,
+ 	return hostapd_cli_cmd(ctrl, "get_pp", 1, argc, argv);
+ }
+ 
++static int hostapd_cli_cmd_wmm(struct wpa_ctrl *ctrl, int argc,
++			       char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "WMM", 1, argc, argv);
++}
++
+ struct hostapd_cli_cmd {
+ 	const char *cmd;
+ 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -2023,6 +2029,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 		" = Set preamble puncture mode"},
+ 	{ "get_pp", hostapd_cli_cmd_get_pp, NULL,
+ 		" = Get preamble puncture status"},
++	{ "wmm", hostapd_cli_cmd_wmm, NULL,
++		" = <ac> [cwmin=] [cwmax=] [aifs=] [txop_limit=]"},
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/hostapd/main.c b/hostapd/main.c
+index e790f18ce..8ecec6c1a 100644
+--- a/hostapd/main.c
++++ b/hostapd/main.c
+@@ -330,8 +330,8 @@ setup_mld:
+ 			return -1;
+ 		}
+ 
+-		/* Initialize the BSS parameter change to 1 */
+-		hapd->eht_mld_bss_param_change = 1;
++		/* Initialize the BSS parameter change to 0 */
++		hapd->eht_mld_bss_param_change = 0;
+ 
+ 		wpa_printf(MSG_DEBUG,
+ 			   "MLD: Set link_id=%u, mld_addr=" MACSTR
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 88e35acc2..9b5c4fc1e 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -514,6 +514,23 @@ static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid)
+ }
+ 
+ 
++static u8 * hostapd_eid_max_chsw_time(struct hostapd_data *hapd, u8 *eid)
++{
++	u32 max_chsw_time = 800;
++
++	if (!hapd->cs_freq_params.channel)
++		return eid;
++
++	*eid++ = WLAN_EID_EXTENSION;
++	*eid++ = 4;
++	*eid++ = WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME;
++	WPA_PUT_LE24(eid, max_chsw_time);
++	eid += 3;
++
++	return eid;
++}
++
++
+ static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid)
+ {
+ 	u8 op_class, channel;
+@@ -878,6 +895,7 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
+ #endif /* CONFIG_IEEE80211AX */
+ 
+ 	pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
++	pos = hostapd_eid_max_chsw_time(hapd, pos);
+ 
+ 	if (!params->is_ml_sta_info)
+ 		pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP,
+@@ -2185,6 +2203,63 @@ static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
+ #endif /* CONFIG_FILS */
+ 
+ 
++static void hostapd_fill_bcn_sta_profile(struct hostapd_data *hapd,
++					 struct mld_info *info)
++{
++	struct hostapd_data *h;
++
++	if (!info)
++		return;
++
++	os_memset(info, 0, sizeof(*info));
++
++	for_each_mld_link(h, hapd) {
++		unsigned int link_id = h->mld_link_id;
++		struct mld_link_info *link = &info->links[link_id];
++		u8 *epos, *csa_pos, buf[EHT_ML_MAX_STA_PROF_LEN];
++
++		if (!h->started || h == hapd ||
++		    h->eht_mld_bss_critical_update != BSS_CRIT_UPDATE_ALL)
++			continue;
++
++		link->valid = true;
++		os_memcpy(link->local_addr, h->own_addr, ETH_ALEN);
++
++		/* Build per-STA profile */
++		epos = buf;
++		/* Capabilities */
++		WPA_PUT_LE16(epos, hostapd_own_capab_info(h));
++		epos += 2;
++
++		/* CSA IE */
++		csa_pos = hostapd_eid_csa(h, epos);
++		if (csa_pos != epos)
++			link->sta_prof_csa_offset = csa_pos - 1 - buf;
++		epos = csa_pos;
++
++		/* eCSA IE */
++		csa_pos = hostapd_eid_ecsa(h, epos);
++		if (csa_pos != epos)
++			link->sta_prof_ecsa_offset = csa_pos - 1 - buf;
++		epos = csa_pos;
++
++		/* channel switch wrapper */
++		epos = hostapd_eid_wb_chsw_wrapper(h, epos);
++
++		/* max channel switch time */
++		epos = hostapd_eid_max_chsw_time(h, epos);
++
++		link->resp_sta_profile_len = epos - buf;
++		link->resp_sta_profile = os_memdup(buf, link->resp_sta_profile_len);
++
++		/* TODO:
++		 * 1. add other IEs
++		 * 2. handle per-STA profile inheritance
++		 * 3. handle csa offset if fragmentation is required
++		 */
++	}
++}
++
+ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ 			       struct wpa_driver_ap_params *params)
+ {
+@@ -2394,6 +2469,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ #endif /* CONFIG_IEEE80211AX */
+ 
+ 	tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
++	tailpos = hostapd_eid_max_chsw_time(hapd, tailpos);
+ 
+ 	tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON, true);
+ 	tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
+@@ -2423,9 +2499,30 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ 
+ #ifdef CONFIG_IEEE80211BE
+ 	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+-		if (hapd->conf->mld_ap)
+-			tailpos = hostapd_eid_eht_ml_beacon(hapd, NULL,
++		if (hapd->conf->mld_ap) {
++			struct hostapd_data *h;
++			struct mld_info info;
++			struct mld_link_info *link;
++			u32 base;
++			u8 link_id, *ml_pos = tailpos;
++
++			hostapd_fill_bcn_sta_profile(hapd, &info);
++			tailpos = hostapd_eid_eht_ml_beacon(hapd, &info,
+ 							    tailpos, false);
++
++			for_each_mld_link(h, hapd) {
++				link_id = h->mld_link_id;
++				link = &info.links[link_id];
++				base = ml_pos - tail + link->sta_prof_offset;
++				if (link->sta_prof_csa_offset)
++					hapd->cs_c_off_sta_prof[link_id] =
++							base + link->sta_prof_csa_offset;
++				if (link->sta_prof_ecsa_offset)
++					hapd->cs_c_off_ecsa_sta_prof[link_id] =
++							base + link->sta_prof_ecsa_offset;
++			}
++			ap_sta_free_sta_profile(&info);
++		}
+ 		tailpos = hostapd_eid_eht_capab(hapd, tailpos,
+ 						IEEE80211_MODE_AP);
+ 		tailpos = hostapd_eid_eht_operation(hapd, tailpos);
+@@ -2803,7 +2900,8 @@ void ieee802_11_set_beacon_per_bss_only(struct hostapd_data *hapd)
+ int ieee802_11_set_beacon(struct hostapd_data *hapd)
+ {
+ 	struct hostapd_iface *iface = hapd->iface;
+-	int ret;
++	struct hostapd_data *h;
++	int ret, link_id;
+ 	size_t i, j;
+ 	bool is_6g, hapd_mld = false;
+ 
+@@ -2846,6 +2944,17 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
+ 		}
+ 	}
+ 
++#ifdef CONFIG_IEEE80211BE
++	/* clear critical update flag for UPDATE_SINGLE type, for other types,
++	 * we should get some notified events from driver
++	 */
++	if (hapd->conf->mld_ap) {
++		for_each_mld_link(h, hapd)
++			if (h->eht_mld_bss_critical_update == BSS_CRIT_UPDATE_SINGLE)
++				h->eht_mld_bss_critical_update = 0;
++	}
++#endif /* CONFIG_IEEE80211BE */
++
+ 	return 0;
+ }
+ 
+@@ -2880,4 +2989,50 @@ int ieee802_11_update_beacons(struct hostapd_iface *iface)
+ 	return ret;
+ }
+ 
++
++int ieee802_11_set_bss_critical_update(struct hostapd_data *hapd,
++				       enum bss_crit_update_event event)
++{
++	if (!hapd->conf->mld_ap)
++		return 0;
++
++	switch (event) {
++	case BSS_CRIT_UPDATE_EVENT_CSA:
++	case BSS_CRIT_UPDATE_EVENT_ECSA:
++	case BSS_CRIT_UPDATE_EVENT_QUIET:
++	case BSS_CRIT_UPDATE_EVENT_WBCS:
++	case BSS_CRIT_UPDATE_EVENT_CS_WRAP:
++	case BSS_CRIT_UPDATE_EVENT_OP_MODE_NOTIF:
++	case BSS_CRIT_UPDATE_EVENT_QUIET_CH:
++	case BSS_CRIT_UPDATE_EVENT_CCA:
++	case BSS_CRIT_UPDATE_EVENT_BCAST_TWT:
++	case BSS_CRIT_UPDATE_EVENT_BCAST_TWT_PARAM_SET:
++	case BSS_CRIT_UPDATE_EVENT_IDX_ADJUST_FACTOR:
++	case BSS_CRIT_UPDATE_EVENT_TPE:
++		hapd->eht_mld_bss_param_change += 1;
++		hapd->eht_mld_bss_critical_update = BSS_CRIT_UPDATE_ALL;
++		return 0;
++	case BSS_CRIT_UPDATE_EVENT_EDCA:
++	case BSS_CRIT_UPDATE_EVENT_DSSS:
++	case BSS_CRIT_UPDATE_EVENT_HT_OPERATION:
++	case BSS_CRIT_UPDATE_EVENT_VHT_OPERATION:
++	case BSS_CRIT_UPDATE_EVENT_HE_OPERATION:
++	case BSS_CRIT_UPDATE_EVENT_MU_EDCA:
++	case BSS_CRIT_UPDATE_EVENT_SR:
++	case BSS_CRIT_UPDATE_EVENT_UORA:
++	case BSS_CRIT_UPDATE_EVENT_EHT_OPERATION:
++		hapd->eht_mld_bss_param_change += 1;
++		hapd->eht_mld_bss_critical_update = BSS_CRIT_UPDATE_SINGLE;
++		return 0;
++	case BSS_CRIT_UPDATE_EVENT_RECONFIG:
++	case BSS_CRIT_UPDATE_EVENT_ADD_LINK:
++	case BSS_CRIT_UPDATE_EVENT_ATTLM:
++		hapd->eht_mld_bss_critical_update = BSS_CRIT_UPDATE_FLAG;
++		return 0;
++	default:
++		hapd->eht_mld_bss_critical_update = BSS_CRIT_UPDATE_NONE;
++		return -1;
++	}
++}
++
+ #endif /* CONFIG_NATIVE_WINDOWS */
+diff --git a/src/ap/beacon.h b/src/ap/beacon.h
+index e381542a8..809393902 100644
+--- a/src/ap/beacon.h
++++ b/src/ap/beacon.h
+@@ -12,12 +12,49 @@
+ 
+ struct ieee80211_mgmt;
+ 
++enum bss_crit_update_event {
++	BSS_CRIT_UPDATE_EVENT_CSA,
++	BSS_CRIT_UPDATE_EVENT_ECSA,
++	BSS_CRIT_UPDATE_EVENT_EDCA,
++	BSS_CRIT_UPDATE_EVENT_QUIET,
++	BSS_CRIT_UPDATE_EVENT_DSSS,
++	BSS_CRIT_UPDATE_EVENT_HT_OPERATION,
++	BSS_CRIT_UPDATE_EVENT_WBCS,
++	BSS_CRIT_UPDATE_EVENT_CS_WRAP,
++	BSS_CRIT_UPDATE_EVENT_OP_MODE_NOTIF,
++	BSS_CRIT_UPDATE_EVENT_QUIET_CH,
++	BSS_CRIT_UPDATE_EVENT_VHT_OPERATION,
++	BSS_CRIT_UPDATE_EVENT_HE_OPERATION,
++	BSS_CRIT_UPDATE_EVENT_BCAST_TWT,
++	BSS_CRIT_UPDATE_EVENT_BCAST_TWT_PARAM_SET,
++	BSS_CRIT_UPDATE_EVENT_CCA,
++	BSS_CRIT_UPDATE_EVENT_MU_EDCA,
++	BSS_CRIT_UPDATE_EVENT_SR,
++	BSS_CRIT_UPDATE_EVENT_UORA,
++	BSS_CRIT_UPDATE_EVENT_IDX_ADJUST_FACTOR,
++	BSS_CRIT_UPDATE_EVENT_EHT_OPERATION,
++	BSS_CRIT_UPDATE_EVENT_TPE,
++	BSS_CRIT_UPDATE_EVENT_CH_CHANGED,
++	BSS_CRIT_UPDATE_EVENT_RECONFIG,
++	BSS_CRIT_UPDATE_EVENT_ADD_LINK,
++	BSS_CRIT_UPDATE_EVENT_ATTLM
++};
++
++enum {
++	BSS_CRIT_UPDATE_NONE,
++	BSS_CRIT_UPDATE_SINGLE,
++	BSS_CRIT_UPDATE_ALL,
++	BSS_CRIT_UPDATE_FLAG
++};
++
+ void handle_probe_req(struct hostapd_data *hapd,
+ 		      const struct ieee80211_mgmt *mgmt, size_t len,
+ 		      int ssi_signal);
+ void ieee802_11_set_beacon_per_bss_only(struct hostapd_data *hapd);
+ int ieee802_11_set_beacon(struct hostapd_data *hapd);
+ int ieee802_11_set_beacons(struct hostapd_iface *iface);
++int ieee802_11_set_bss_critical_update(struct hostapd_data *hapd,
++				       enum bss_crit_update_event event);
+ int ieee802_11_update_beacons(struct hostapd_iface *iface);
+ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ 			       struct wpa_driver_ap_params *params);
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index fc7699973..697e6364c 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1120,6 +1120,18 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
+ 		err = hostapd_switch_channel(iface->bss[i], &csa_settings);
+ 		if (err)
+ 			num_err++;
++
++#ifdef CONFIG_IEEE80211BE
++		if (iface->bss[i]->conf->mld_ap)
++			hostapd_update_aff_link_beacon(iface->bss[i], csa_settings.cs_count);
++
++		/*
++		 * Currently, no FW notification event for clearing CU flag after DTIM period.
++		 * Also, another CU or set beacon is not allowed during CSA period.
++		 * Therefore, just clear it manually here for workaround.
++		 */
++		iface->bss[i]->eht_mld_bss_critical_update = 0;
++#endif /* CONFIG_IEEE80211BE */
+ 	}
+ 
+ 	if (num_err == iface->num_bss) {
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 1107fd70e..705acfb67 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -1314,7 +1314,6 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
+ 	if (hapd->csa_in_progress &&
+ 	    freq == hapd->cs_freq_params.freq) {
+ 		hostapd_cleanup_cs_params(hapd);
+-		ieee802_11_set_beacon(hapd);
+ 
+ 		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
+ 			"freq=%d dfs=%d", freq, is_dfs);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 2c9e54b23..f03a6242f 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4524,6 +4524,9 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
+ 	old_punct_bitmap = iface->conf->punct_bitmap;
+ 	iface->conf->punct_bitmap = settings->punct_bitmap;
+ #endif /* CONFIG_IEEE80211BE */
++
++	/* Another CU in the new channel due to OP element modification */
++	ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_EHT_OPERATION);
+ 	ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
+ 
+ 	/* change back the configuration */
+@@ -4541,20 +4544,32 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
+ 	hapd->cs_count = settings->cs_count;
+ 	hapd->cs_block_tx = settings->block_tx;
+ 
++#ifdef CONFIG_IEEE80211BE
++	/* Restore BPCC to build the CSA beacon */
++	hapd->eht_mld_bss_param_change--;
++	hapd->eht_mld_bss_critical_update = BSS_CRIT_UPDATE_ALL;
++#endif /* CONFIG_IEEE80211BE */
++
+ 	ret = hostapd_build_beacon_data(hapd, &settings->beacon_csa);
+ 	if (ret) {
+ 		free_beacon_data(&settings->beacon_after);
+ 		return ret;
+ 	}
+ 
++	/* Change back to the final BPCC and CU flag */
++	ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_EHT_OPERATION);
++
+ 	settings->counter_offset_beacon[0] = hapd->cs_c_off_beacon;
+ 	settings->counter_offset_presp[0] = hapd->cs_c_off_proberesp;
+ 	settings->counter_offset_beacon[1] = hapd->cs_c_off_ecsa_beacon;
+ 	settings->counter_offset_presp[1] = hapd->cs_c_off_ecsa_proberesp;
+ 	settings->link_id = -1;
++	settings->freq_params.link_id = -1;
+ #ifdef CONFIG_IEEE80211BE
+-	if (hapd->conf->mld_ap)
++	if (hapd->conf->mld_ap) {
+ 		settings->link_id = hapd->mld_link_id;
++		settings->freq_params.link_id = hapd->mld_link_id;
++	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ #ifdef CONFIG_IEEE80211AX
+@@ -4616,6 +4631,8 @@ int hostapd_switch_channel(struct hostapd_data *hapd,
+ 		return -1;
+ 	}
+ 
++	ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_CSA);
++
+ 	ret = hostapd_fill_csa_settings(hapd, settings);
+ 	if (ret)
+ 		return ret;
+@@ -4637,6 +4654,72 @@ int hostapd_switch_channel(struct hostapd_data *hapd,
+ 	return 0;
+ }
+ 
++int hostapd_update_aff_link_beacon(struct hostapd_data *hapd, u8 cs_count)
++{
++	struct hostapd_data *h;
++	unsigned int cs_link_id = hapd->mld_link_id;
++	int cs_channel = hapd->cs_freq_params.channel;
++
++	/* TODO: add beacon offload driver flag */
++	for_each_mld_link(h, hapd) {
++		struct hostapd_config *conf = h->iconf;
++		struct hostapd_hw_modes *mode = h->iface->current_mode;
++		struct csa_settings settings = {};
++		unsigned int link_id = h->mld_link_id;
++		int ret;
++
++		if (!h->started || h == hapd)
++			continue;
++
++		hostapd_set_freq_params(&settings.freq_params, conf->hw_mode,
++					hostapd_hw_get_freq(h, conf->channel),
++					conf->channel, conf->enable_edmg,
++					conf->edmg_channel, conf->ieee80211n,
++					conf->ieee80211ac, conf->ieee80211ax,
++					conf->ieee80211be, conf->secondary_channel,
++					hostapd_get_oper_chwidth(conf),
++					hostapd_get_oper_centr_freq_seg0_idx(conf),
++					hostapd_get_oper_centr_freq_seg1_idx(conf),
++					conf->vht_capab,
++					mode ? &mode->he_capab[IEEE80211_MODE_AP] : NULL,
++					mode ? &mode->eht_capab[IEEE80211_MODE_AP] : NULL,
++					hostapd_get_punct_bitmap(h));
++		hapd->cs_freq_params.channel = 0;
++		ret = hostapd_build_beacon_data(h, &settings.beacon_after);
++		if (ret)
++			return ret;
++
++		hapd->cs_freq_params.channel = cs_channel;
++		/* Restore BPCC to build the RNR for the CS link */
++		hapd->eht_mld_bss_param_change--;
++		hapd->eht_mld_bss_critical_update = BSS_CRIT_UPDATE_ALL;
++		ret = hostapd_build_beacon_data(h, &settings.beacon_csa);
++		if (ret) {
++			free_beacon_data(&settings.beacon_after);
++			return ret;
++		}
++
++		/* Change back to the final BPCC and CU flag */
++		ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_EHT_OPERATION);
++
++		settings.counter_offset_sta_prof[cs_link_id][0] =
++						h->cs_c_off_sta_prof[cs_link_id];
++		settings.counter_offset_sta_prof[cs_link_id][1] =
++						h->cs_c_off_ecsa_sta_prof[cs_link_id];
++		settings.link_id = cs_link_id;
++		settings.freq_params.link_id = link_id;
++		settings.cs_count = cs_count;
++		settings.punct_bitmap = conf->punct_bitmap;
++		ret = hostapd_drv_switch_channel(h, &settings);
++		free_beacon_data(&settings.beacon_csa);
++		free_beacon_data(&settings.beacon_after);
++		if (ret)
++			return ret;
++	}
++
++	return 0;
++}
++
+ 
+ void
+ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 4db67096b..99d5a01d6 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -342,6 +342,9 @@ struct hostapd_data {
+ 	unsigned int cs_c_off_ecsa_beacon;
+ 	unsigned int cs_c_off_ecsa_proberesp;
+ 
++	unsigned int cs_c_off_sta_prof[MAX_NUM_MLD_LINKS];
++	unsigned int cs_c_off_ecsa_sta_prof[MAX_NUM_MLD_LINKS];
++
+ #ifdef CONFIG_IEEE80211AX
+ 	bool cca_in_progress;
+ 	u8 cca_count;
+@@ -501,6 +504,7 @@ struct hostapd_data {
+ 
+ #ifdef CONFIG_IEEE80211BE
+ 	u8 eht_mld_bss_param_change;
++	u8 eht_mld_bss_critical_update;
+ 	struct hostapd_mld *mld;
+ 	struct dl_list link;
+ 	u8 mld_link_id;
+@@ -850,6 +854,7 @@ void hostapd_chan_switch_config(struct hostapd_data *hapd,
+ 				struct hostapd_freq_params *freq_params);
+ int hostapd_switch_channel(struct hostapd_data *hapd,
+ 			   struct csa_settings *settings);
++int hostapd_update_aff_link_beacon(struct hostapd_data *hapd, u8 cs_count);
+ void
+ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+ 				const struct hostapd_freq_params *freq_params);
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 1365bc822..bb508fe79 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -282,6 +282,7 @@ u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid,
+ 
+ u16 hostapd_own_capab_info(struct hostapd_data *hapd)
+ {
++	struct hostapd_data *h;
+ 	int capab = WLAN_CAPABILITY_ESS;
+ 	int privacy = 0;
+ 	int dfs;
+@@ -342,6 +343,18 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd)
+ 		}
+ 	}
+ 
++#ifdef CONFIG_IEEE80211BE
++	if (hapd->conf->mld_ap) {
++		for_each_mld_link(h, hapd) {
++			if (h->eht_mld_bss_critical_update) {
++				capab |= WLAN_CAPABILITY_PBCC;
++				break;
++			}
++		}
++	}
++#endif /* CONFIG_IEEE80211BE */
++
++
+ 	return capab;
+ }
+ 
+@@ -7838,6 +7851,8 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
+ 			(MAX_NUM_MLD_LINKS | 0xF0);
+ 		/* BPCC (Bit 3 to Bit 0) */
+ 		*eid = is_partner ? ((param_ch & 0xF0) >> 4) : 0x0F;
++		if (bss->eht_mld_bss_critical_update == BSS_CRIT_UPDATE_ALL)
++			*eid |= RNR_TBTT_INFO_MLD_PARAM2_ALL_UPDATE_INC;
+ #ifdef CONFIG_TESTING_OPTIONS
+ 		if (bss->conf->mld_indicate_disabled)
+ 			*eid |= RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index a04eb23e0..2b5c06d6d 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -495,7 +495,7 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 	wpabuf_put_u8(buf, hapd->mld_link_id);
+ 
+ 	/* Currently hard code the BSS Parameters Change Count to 0x1 */
+-	wpabuf_put_u8(buf, 0x1);
++	wpabuf_put_u8(buf, hapd->eht_mld_bss_param_change);
+ 
+ 	wpa_printf(MSG_DEBUG, "MLD: EML Capabilities=0x%x",
+ 		   hapd->iface->mld_eml_capa);
+@@ -593,11 +593,14 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 		wpabuf_put_u8(buf, link_bss->conf->dtim_period);
+ 
+ 		/* BSS Parameters Change Count */
+-		wpabuf_put_u8(buf, hapd->eht_mld_bss_param_change);
++		wpabuf_put_u8(buf, link_bss->eht_mld_bss_param_change);
+ 
+ 		if (!link->resp_sta_profile)
+ 			continue;
+ 
++#define EXT_EID_TAG_LEN 3
++		link->sta_prof_offset = wpabuf_len(buf) + EXT_EID_TAG_LEN;
++
+ 		/* Fragment the sub element if needed */
+ 		if (total_len <= 255) {
+ 			wpabuf_put_data(buf, link->resp_sta_profile,
+diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
+index 67b97671a..60b33f049 100644
+--- a/src/ap/sta_info.h
++++ b/src/ap/sta_info.h
+@@ -89,6 +89,12 @@ struct mld_info {
+ 		u16 status;
+ 		u16 resp_sta_profile_len;
+ 		u8 *resp_sta_profile;
++
++		u32 sta_prof_csa_offset;
++		u32 sta_prof_ecsa_offset;
++		u32 sta_prof_offset;
++
++		const u8 *rsne, *rsnxe;
+ 	} links[MAX_NUM_MLD_LINKS];
+ };
+ 
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index da1c4c1ac..a72193282 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -738,9 +738,16 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 	wpa_printf(MSG_INFO, "    * center_freq2 is %d\n",
+ 			csa.freq_params.center_freq2);
+ 
+-	for (i = 0; i < iface->num_bss; i++)
++	for (i = 0; i < iface->num_bss; i++) {
+ 		ret = hostapd_switch_channel(iface->bss[i], &csa);
+ 
++		if (iface->bss[i]->conf->mld_ap)
++			hostapd_update_aff_link_beacon(iface->bss[i], csa.cs_count);
++
++		/* FIXME: remove this line after CU event merged */
++		iface->bss[i]->eht_mld_bss_critical_update = 0;
++	}
++
+ 	return ucv_boolean_new(!ret);
+ }
+ 
+diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
+index c380d0c7e..efb584c66 100644
+--- a/src/common/ieee802_11_defs.h
++++ b/src/common/ieee802_11_defs.h
+@@ -504,6 +504,7 @@
+ #define WLAN_EID_EXT_HE_MU_EDCA_PARAMS 38
+ #define WLAN_EID_EXT_SPATIAL_REUSE 39
+ #define WLAN_EID_EXT_COLOR_CHANGE_ANNOUNCEMENT 42
++#define WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME 52
+ #define WLAN_EID_EXT_OCV_OCI 54
+ #define WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION 55
+ #define WLAN_EID_EXT_NON_INHERITANCE 56
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index b75580374..924a1baba 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -2763,6 +2763,7 @@ struct csa_settings {
+ 
+ 	u16 counter_offset_beacon[2];
+ 	u16 counter_offset_presp[2];
++	u16 counter_offset_sta_prof[MAX_NUM_MLD_LINKS][2];
+ 
+ 	u16 punct_bitmap;
+ 	int link_id;
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 8b64c3983..555c97bf7 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -11417,9 +11417,10 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+ 	struct nlattr *beacon_csa;
+-	int ret = -ENOBUFS;
+-	int csa_off_len = 0;
+-	int i;
++	int i, csa_off_len = 0, ret = -ENOBUFS;
++	unsigned int cs_link_id = settings->link_id;
++	u16 *counter_offset_beacon = settings->counter_offset_beacon;
++	u16 *counter_offset_presp = settings->counter_offset_presp;
+ 
+ 	wpa_printf(MSG_DEBUG,
+ 		   "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d channel=%d sec_channel_offset=%d width=%d cf1=%d cf2=%d puncturing_bitmap=0x%04x link_id=%d%s%s%s)",
+@@ -11431,7 +11432,7 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ 		   settings->freq_params.center_freq1,
+ 		   settings->freq_params.center_freq2,
+ 		   settings->punct_bitmap,
+-		   settings->link_id,
++		   settings->freq_params.link_id,
+ 		   settings->freq_params.ht_enabled ? " ht" : "",
+ 		   settings->freq_params.vht_enabled ? " vht" : "",
+ 		   settings->freq_params.he_enabled ? " he" : "");
+@@ -11451,18 +11452,19 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ 	 * counters match. This implementation assumes that there are only two
+ 	 * counters.
+ 	 */
+-	if (settings->counter_offset_beacon[0] &&
+-	    !settings->counter_offset_beacon[1]) {
++	if (cs_link_id != settings->freq_params.link_id) {
++		counter_offset_beacon = settings->counter_offset_sta_prof[cs_link_id];
++		counter_offset_presp = NULL;
++	}
++
++	if (counter_offset_beacon[0] && !counter_offset_beacon[1]) {
+ 		csa_off_len = 1;
+-	} else if (settings->counter_offset_beacon[1] &&
+-		   !settings->counter_offset_beacon[0]) {
++	} else if (counter_offset_beacon[1] && !counter_offset_beacon[0]) {
+ 		csa_off_len = 1;
+-		settings->counter_offset_beacon[0] =
+-			settings->counter_offset_beacon[1];
+-		settings->counter_offset_presp[0] =
+-			settings->counter_offset_presp[1];
+-	} else if (settings->counter_offset_beacon[1] &&
+-		   settings->counter_offset_beacon[0]) {
++		counter_offset_beacon[0] = counter_offset_beacon[1];
++		if (counter_offset_presp)
++			counter_offset_presp[0] = counter_offset_presp[1];
++	} else if (counter_offset_beacon[1] && counter_offset_beacon[0]) {
+ 		csa_off_len = 2;
+ 	} else {
+ 		wpa_printf(MSG_ERROR, "nl80211: No CSA counters provided");
+@@ -11481,14 +11483,18 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ 		return -EINVAL;
+ 
+ 	for (i = 0; i < csa_off_len; i++) {
+-		u16 csa_c_off_bcn = settings->counter_offset_beacon[i];
+-		u16 csa_c_off_presp = settings->counter_offset_presp[i];
++		u16 csa_c_off_bcn = counter_offset_beacon[i];
++		u16 csa_c_off_presp;
+ 
+ 		if ((settings->beacon_csa.tail_len <= csa_c_off_bcn) ||
+ 		    (settings->beacon_csa.tail[csa_c_off_bcn] !=
+ 		     settings->cs_count))
+ 			return -EINVAL;
+ 
++		if (!counter_offset_presp)
++			continue;
++
++		csa_c_off_presp = counter_offset_presp[i];
+ 		if (settings->beacon_csa.probe_resp &&
+ 		    ((settings->beacon_csa.probe_resp_len <=
+ 		      csa_c_off_presp) ||
+@@ -11506,8 +11512,8 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ 	    (settings->punct_bitmap &&
+ 	     nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP,
+ 			 settings->punct_bitmap)) ||
+-	    (settings->link_id != NL80211_DRV_LINK_ID_NA &&
+-	     nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, settings->link_id)))
++	    (settings->freq_params.link_id != NL80211_DRV_LINK_ID_NA &&
++	     nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, settings->freq_params.link_id)))
+ 		goto error;
+ 
+ 	/* beacon_after params */
+@@ -11528,9 +11534,14 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ 	if (ret)
+ 		goto error;
+ 
+-	if (nla_put(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
+-		    csa_off_len * sizeof(u16),
+-		    settings->counter_offset_beacon) ||
++	if ((cs_link_id == settings->freq_params.link_id &&
++	     nla_put(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
++		     csa_off_len * sizeof(u16),
++		     settings->counter_offset_beacon)) ||
++	    (cs_link_id != settings->freq_params.link_id &&
++	     nla_put(msg, NL80211_ATTR_CSA_C_OFF_STA_PROF,
++		     csa_off_len * sizeof(u16),
++		     settings->counter_offset_sta_prof[cs_link_id])) ||
+ 	    (settings->beacon_csa.probe_resp &&
+ 	     nla_put(msg, NL80211_ATTR_CSA_C_OFF_PRESP,
+ 		     csa_off_len * sizeof(u16),
+diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
+index d425c797c..13837297c 100644
+--- a/src/drivers/nl80211_copy.h
++++ b/src/drivers/nl80211_copy.h
+@@ -2868,6 +2868,10 @@ enum nl80211_commands {
+  *	nested item, it contains attributes defined in
+  *	&enum nl80211_if_combination_attrs.
+  *
++ * @NL80211_ATTR_CNTDWN_OFFS_STA_PROF: An array of offsets (u16) to the channel
++ *	switch or color change counters in the per-STA profile corresponding to
++ *	the affected AP.
++ *
+  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
+  * @NL80211_ATTR_MAX: highest attribute number currently defined
+  * @__NL80211_ATTR_AFTER_LAST: internal use
+@@ -3418,6 +3422,9 @@ enum nl80211_attrs {
+ 
+ 	/* add attributes here, update the policy in nl80211.c */
+ 
++	/* MTK internal */
++	NL80211_ATTR_CNTDWN_OFFS_STA_PROF,
++
+ 	__NL80211_ATTR_AFTER_LAST,
+ 	NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST,
+ 	NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
+@@ -3430,6 +3437,7 @@ enum nl80211_attrs {
+ #define NL80211_ATTR_SAE_DATA NL80211_ATTR_AUTH_DATA
+ #define NL80211_ATTR_CSA_C_OFF_BEACON NL80211_ATTR_CNTDWN_OFFS_BEACON
+ #define NL80211_ATTR_CSA_C_OFF_PRESP NL80211_ATTR_CNTDWN_OFFS_PRESP
++#define NL80211_ATTR_CSA_C_OFF_STA_PROF NL80211_ATTR_CNTDWN_OFFS_STA_PROF
+ 
+ /*
+  * Allow user space programs to use #ifdef on new attributes by defining them
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0085-mtk-wpa_s-fix-bss-selection-when-setting-mld_connect.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0085-mtk-wpa_s-fix-bss-selection-when-setting-mld_connect.patch
deleted file mode 100644
index 820e085..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0085-mtk-wpa_s-fix-bss-selection-when-setting-mld_connect.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From e2e07813d1e05a72aa649614ad942036b387aaf1 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Fri, 29 Dec 2023 15:04:27 +0800
-Subject: [PATCH 085/104] mtk: wpa_s: fix bss selection when setting
- mld_connect_band_pref
-
-Without this patch, when setting mld_connect_band_pref as 5g, wrong bss
-will be selected.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- wpa_supplicant/sme.c | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
-diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
-index e1183722f..5b69812b5 100644
---- a/wpa_supplicant/sme.c
-+++ b/wpa_supplicant/sme.c
-@@ -437,8 +437,11 @@ static struct wpa_bss * wpas_ml_connect_pref(struct wpa_supplicant *wpa_s,
- 	}
- 
- 	for_each_link(wpa_s->valid_links, i) {
--		if (wpa_s->mlo_assoc_link_id == i)
-+		if (wpa_s->mlo_assoc_link_id == i) {
-+			if (bss->freq >= low && bss->freq <= high)
-+				return bss;
- 			continue;
-+		}
- 
- 		if (wpa_s->links[i].freq >= low && wpa_s->links[i].freq <= high)
- 			goto found;
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0086-mtk-hostapd-add-mld_primary-option.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0086-mtk-hostapd-add-mld_primary-option.patch
deleted file mode 100644
index 4fa5b6f..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0086-mtk-hostapd-add-mld_primary-option.patch
+++ /dev/null
@@ -1,41 +0,0 @@
-From bcb603194f7df4fd3060ed6a13a2e4da2715d959 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Tue, 26 Dec 2023 08:05:41 +0800
-Subject: [PATCH 086/104] mtk: hostapd: add mld_primary option
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- hostapd/config_file.c | 2 ++
- src/ap/ap_config.h    | 3 +++
- 2 files changed, 5 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 7bc19479d..e9caa45f3 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5349,6 +5349,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		conf->punct_acs_threshold = val;
- 	} else if (os_strcmp(buf, "mld_ap") == 0) {
- 		bss->mld_ap = !!atoi(pos);
-+	} else if (os_strcmp(buf, "mld_primary") == 0) {
-+		bss->mld_primary = !!atoi(pos);
- 	} else if (os_strcmp(buf, "mld_addr") == 0) {
- 		if (hwaddr_aton(pos, bss->mld_addr)) {
- 			wpa_printf(MSG_ERROR, "Line %d: Invalid mld_addr",
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 7f48c71f5..1f686550e 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -966,6 +966,9 @@ struct hostapd_bss_config {
- 	/* The AP is part of an AP MLD */
- 	u8 mld_ap;
- 
-+	/* The AP is the primary AP of an AP MLD */
-+	u8 mld_primary;
-+
- 	/* The MLD ID to which the AP MLD is affiliated with */
- 	u8 mld_id;
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0086-mtk-hostapd-add-support-for-emlsr.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0086-mtk-hostapd-add-support-for-emlsr.patch
new file mode 100644
index 0000000..0cf1d0b
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0086-mtk-hostapd-add-support-for-emlsr.patch
@@ -0,0 +1,733 @@
+From 093ac6b9d2f9c79b137f327af1eb0236ba71064a Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+Date: Thu, 9 May 2024 15:41:18 +0800
+Subject: [PATCH 086/126] mtk: hostapd: add support for emlsr
+
+1. Processing the EML capability IE in the association request,
+and sending the value of EML field to the kernel.
+
+2. Processing the EML Operating Mode Notification frame,
+and sending the EML Operating Mode Notification frame if eml_resp is
+true.
+
+Command Usage:
+eml_resp -
+hostapd_cli -i <interface> eml_resp <enable>
+
+Processing the EML capability IE in the association request,
+and sending the value of EML field to the kernel.
+
+The original flow send the mcu command to the firmware using the WCID of
+the primary link. Therefore, a  is passed in the hostapd layer
+to ensure that the EML OMN is processed and sent to the firmware using
+the WCID of the receiving link.
+
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ hostapd/config_file.c             |   4 +
+ hostapd/ctrl_iface.c              |  36 +++++++++
+ hostapd/hostapd.conf              |  10 +++
+ hostapd/hostapd_cli.c             |   8 ++
+ src/ap/ap_config.h                |   2 +
+ src/ap/ap_drv_ops.c               |  20 ++++-
+ src/ap/ap_drv_ops.h               |   4 +-
+ src/ap/apup.c                     |   4 +-
+ src/ap/ieee802_11.c               |   8 +-
+ src/ap/ieee802_11.h               |   4 +-
+ src/ap/ieee802_11_eht.c           | 124 ++++++++++++++++++++++++++++--
+ src/ap/sta_info.c                 |   2 +-
+ src/common/ieee802_11_defs.h      |  34 ++++++++
+ src/common/mtk_vendor.h           |  15 ++++
+ src/drivers/driver.h              |  12 +++
+ src/drivers/driver_nl80211.c      |  53 +++++++++++++
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   3 +
+ 18 files changed, 331 insertions(+), 13 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 206055b75..38273a4f2 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5477,6 +5477,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		}
+ 	} else if (os_strcmp(buf, "eht_bw320_offset") == 0) {
+ 		conf->eht_bw320_offset = atoi(pos);
++	} else if (os_strcmp(buf, "eml_disable") == 0) {
++		conf->eml_disable = atoi(pos);
++	} else if (os_strcmp(buf, "eml_resp") == 0) {
++		conf->eml_resp = atoi(pos);
+ #ifdef CONFIG_TESTING_OPTIONS
+ 	} else if (os_strcmp(buf, "eht_oper_puncturing_override") == 0) {
+ 		if (get_u16(pos, line, &bss->eht_oper_puncturing_override))
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index ebe9053cf..d9775e13e 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -5104,6 +5104,40 @@ hostapd_ctrl_iface_disable_beacon(struct hostapd_data *hapd, char *value,
+ 
+ }
+ 
++static int
++hostapd_ctrl_iface_set_eml_resp(struct hostapd_data *hapd, char *value,
++				char *buf, size_t buflen)
++{
++	struct hostapd_data *link;
++	int cnt = 0;
++	u16 *val;
++
++	if (!hostapd_is_mld_ap(hapd))
++		return -1;
++
++	cnt = hostapd_parse_argument_helper(value, &val);
++	if (cnt == -1)
++		goto fail;
++	if (cnt != 1 || val[0] < 0)
++		goto para_fail;
++
++	for_each_mld_link(link, hapd) {
++		link->iconf->eml_resp = val[0];
++		wpa_printf(MSG_ERROR, "Link:%d, Response EML:%d\n",
++			   link->iconf->band_idx, link->iconf->eml_resp);
++	}
++
++	os_free(val);
++
++	return os_snprintf(buf, buflen, "OK\n");
++
++para_fail:
++	os_free(val);
++	wpa_printf(MSG_ERROR, "Input number or value is incorrect\n");
++fail:
++	return os_snprintf(buf, buflen, "FAIL\n");
++}
++
+ static int
+ hostapd_ctrl_iface_set_csi(struct hostapd_data *hapd, char *cmd,
+ 					char *buf, size_t buflen)
+@@ -6034,6 +6068,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 	} else if (os_strncmp(buf, "WMM", 3) == 0) {
+ 		reply_len = hostapd_ctrl_iface_wmm(hapd, buf + 4,
+ 						   reply, reply_size);
++	} else if (os_strncmp(buf, "EML_RESP ", 9) == 0) {
++		reply_len = hostapd_ctrl_iface_set_eml_resp(hapd, buf + 9, reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
+index 118754800..d0830eecd 100644
+--- a/hostapd/hostapd.conf
++++ b/hostapd/hostapd.conf
+@@ -1149,6 +1149,16 @@ wmm_ac_vo_acm=0
+ # will be used as the AP MLD MAC address.
+ #mld_addr=02:03:04:05:06:07
+ 
++# EML Capabilities
++# 0 = Enable EML capabilities in Multi-Link Control subfield
++# 1 = Disable EML capabilitites in Multi-Link Control subfield
++#eml_disable=0
++
++# EML Operating Mode Notification frame
++# 0 = AP does not send EML Operating Mode Notification frame to the station
++# 1 = AP sends EML Operating Mode Notification frame to the station
++#eml_resp=1
++
+ ##### IEEE 802.1X-2004 related configuration ##################################
+ 
+ # Require IEEE 802.1X authorization
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index e578c5b7e..54fda5c45 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1488,6 +1488,12 @@ static int hostapd_cli_cmd_disable_beacon(struct wpa_ctrl *ctrl, int argc,
+ 	return hostapd_cli_cmd(ctrl, "NO_BEACON", 1, argc, argv);
+ }
+ 
++static int hostapd_cli_cmd_set_eml_resp(struct wpa_ctrl *ctrl, int argc,
++					char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "EML_RESP", 1, argc, argv);
++}
++
+ #ifdef CONFIG_DPP
+ 
+ static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
+@@ -1937,6 +1943,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 		" = show mu onoff value in 0-15 bitmap"},
+ 	{ "no_beacon", hostapd_cli_cmd_disable_beacon, NULL,
+ 		"<value> 0: Enable beacon, 1: Disable beacon"},
++	{ "eml_resp", hostapd_cli_cmd_set_eml_resp, NULL,
++		"<value> 0: AP does not send EML Operating Mode Notification frame, 1: AP sends EML OMN frame"},
+ #ifdef CONFIG_DPP
+ 	{ "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.h b/src/ap/ap_config.h
+index 15b66ca30..9c3a28cf4 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1281,6 +1281,8 @@ struct hostapd_config {
+ 	u8 punct_acs_threshold;
+ 	u8 eht_default_pe_duration;
+ 	u8 eht_bw320_offset;
++	u8 eml_disable;
++	u8 eml_resp;
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ 	/* EHT enable/disable config from CHAN_SWITCH */
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index fb09a3290..2d2198a44 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -496,7 +496,8 @@ int hostapd_sta_add(struct hostapd_data *hapd,
+ 		    size_t eht_capab_len,
+ 		    const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
+ 		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
+-		    int set, const u8 *link_addr, bool mld_link_sta)
++		    int set, const u8 *link_addr, bool mld_link_sta,
++		    u16 eml_capa)
+ {
+ 	struct hostapd_sta_add_params params;
+ 
+@@ -536,6 +537,7 @@ int hostapd_sta_add(struct hostapd_data *hapd,
+ 		params.mld_link_id = hapd->mld_link_id;
+ 		params.mld_link_addr = link_addr;
+ 		params.mld_link_sta = mld_link_sta;
++		params.eml_capa = eml_capa;
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+@@ -1448,6 +1450,22 @@ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
+ 	return hapd->driver->beacon_ctrl(hapd->drv_priv, beacon_mode);
+ }
+ 
++int hostapd_drv_set_eml_omn(struct hostapd_data *hapd, u8 *mac,
++			    struct eml_omn_element *omn_ie)
++{
++	u8 link_id;
++
++	if (!hapd->driver || !hapd->driver->set_eml_omn)
++		return 0;
++
++	if (!hapd->conf->mld_ap)
++		return 0;
++
++	link_id = hapd->mld_link_id;
++
++	return hapd->driver->set_eml_omn(hapd->drv_priv, link_id, mac, omn_ie);
++}
++
+ int hostapd_drv_csi_set(struct hostapd_data *hapd, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac)
+ {
+ 	if (!hapd->driver || !hapd->driver->csi_set)
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 9f2e86a9b..ca2efab82 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -52,7 +52,8 @@ int hostapd_sta_add(struct hostapd_data *hapd,
+ 		    size_t eht_capab_len,
+ 		    const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
+ 		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
+-		    int set, const u8 *link_addr, bool mld_link_sta);
++		    int set, const u8 *link_addr, bool mld_link_sta,
++		    u16 eml_capa);
+ int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
+ int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
+ 			     size_t elem_len);
+@@ -174,6 +175,7 @@ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_
+ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
+ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
+ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode);
++int hostapd_drv_set_eml_omn(struct hostapd_data *hapd, u8 *mac, struct eml_omn_element *omn_ie);
+ int hostapd_drv_csi_set(struct hostapd_data *hapd, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac);
+ int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf);
+ 
+diff --git a/src/ap/apup.c b/src/ap/apup.c
+index f736ddc8e..cb0264e9e 100644
+--- a/src/ap/apup.c
++++ b/src/ap/apup.c
+@@ -67,7 +67,7 @@ void apup_process_beacon(struct hostapd_data *hapd,
+ 	            NULL, 0, 0, NULL, NULL, NULL, 0, NULL, 0, NULL,
+ 	            sta_ret->flags, 0, 0, 0,
+ 	            0, // 0 add, 1 set
+-	            mld_link_addr, mld_link_sta);
++	            mld_link_addr, mld_link_sta, 0);
+ 
+ 	sta_ret->flags |= WLAN_STA_AUTH;
+ 	wpa_auth_sm_event(sta_ret->wpa_sm, WPA_AUTH);
+@@ -141,7 +141,7 @@ void apup_process_beacon(struct hostapd_data *hapd,
+ 	            sta_ret->vht_opmode,
+ 	            0, // int supp_p2p_ps
+ 	            1, // 0 add, 1 set
+-	            mld_link_addr, mld_link_sta);
++	            mld_link_addr, mld_link_sta, 0);
+ 
+ 	ap_sta_set_authorized(hapd, sta_ret, 1);
+ 	hostapd_set_sta_flags(hapd, sta_ret);
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index bb508fe79..09f033e13 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -4763,6 +4763,7 @@ static int add_associated_sta(struct hostapd_data *hapd,
+ 	int set = 1;
+ 	const u8 *mld_link_addr = NULL;
+ 	bool mld_link_sta = false;
++	u16 eml_capa = 0;
+ 
+ #ifdef CONFIG_IEEE80211BE
+ 	if (ap_sta_is_mld(hapd, sta)) {
+@@ -4773,6 +4774,8 @@ static int add_associated_sta(struct hostapd_data *hapd,
+ 
+ 		if (hapd->mld_link_id != sta->mld_assoc_link_id)
+ 			set = 0;
++
++		eml_capa = sta->mld_info.common_info.eml_capa;
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+@@ -4861,7 +4864,7 @@ static int add_associated_sta(struct hostapd_data *hapd,
+ 			    sta->he_6ghz_capab,
+ 			    sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
+ 			    sta->vht_opmode, sta->p2p_ie ? 1 : 0,
+-			    set, mld_link_addr, mld_link_sta)) {
++			    set, mld_link_addr, mld_link_sta, eml_capa)) {
+ 		hostapd_logger(hapd, sta->addr,
+ 			       HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE,
+ 			       "Could not %s STA to kernel driver",
+@@ -6214,6 +6217,9 @@ static int handle_action(struct hostapd_data *hapd,
+ 		if (hapd->public_action_cb || hapd->public_action_cb2)
+ 			return 1;
+ 		break;
++	case WLAN_ACTION_PROTECTED_EHT:
++		ieee802_11_rx_prot_eht(hapd, mgmt, len);
++		return 1;
+ 	case WLAN_ACTION_VENDOR_SPECIFIC:
+ 		if (hapd->vendor_action_cb) {
+ 			if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx,
+diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
+index 0e13d2940..18f97890b 100644
+--- a/src/ap/ieee802_11.h
++++ b/src/ap/ieee802_11.h
+@@ -265,5 +265,7 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
+ 				  const u8 *ies, size_t ies_len,
+ 				  bool reassoc, int tx_link_status,
+ 				  bool offload);
+-
++void ieee802_11_rx_prot_eht(struct hostapd_data *hapd,
++			    const struct ieee80211_mgmt *mgmt,
++			    size_t len);
+ #endif /* IEEE802_11_H */
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index 2b5c06d6d..59ada2f86 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -465,9 +465,11 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 	control = MULTI_LINK_CONTROL_TYPE_BASIC |
+ 		BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
+ 		BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
+-		BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA |
+ 		BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA;
+ 
++	if (!hapd->iconf->eml_disable)
++		control |= BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA;
++
+ 	/*
+ 	 * Set the basic Multi-Link common information. Hard code the common
+ 	 * info length to 13 based on the length of the present fields:
+@@ -478,6 +480,9 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ #define EHT_ML_COMMON_INFO_LEN 13
+ 	common_info_len = EHT_ML_COMMON_INFO_LEN;
+ 
++	if (hapd->iconf->eml_disable)
++		common_info_len -= 2; /* EML Capabilities (2) */
++
+ 	if (include_mld_id) {
+ 		/* AP MLD ID */
+ 		control |= BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID;
+@@ -497,9 +502,11 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 	/* Currently hard code the BSS Parameters Change Count to 0x1 */
+ 	wpabuf_put_u8(buf, hapd->eht_mld_bss_param_change);
+ 
+-	wpa_printf(MSG_DEBUG, "MLD: EML Capabilities=0x%x",
+-		   hapd->iface->mld_eml_capa);
+-	wpabuf_put_le16(buf, hapd->iface->mld_eml_capa);
++	if (!hapd->iconf->eml_disable) {
++		wpa_printf(MSG_DEBUG, "MLD: EML Capabilities=0x%x",
++			   hapd->iface->mld_eml_capa);
++		wpabuf_put_le16(buf, hapd->iface->mld_eml_capa);
++	}
+ 
+ 	mld_cap = hapd->iface->mld_mld_capa;
+ 	max_simul_links = mld_cap & EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
+@@ -744,12 +751,16 @@ static u8 * hostapd_eid_eht_reconf_ml(struct hostapd_data *hapd, u8 *eid)
+ 
+ 
+ static size_t hostapd_eid_eht_ml_len(struct mld_info *info,
+-				     bool include_mld_id)
++				     bool include_mld_id,
++				     u8 eml_disable)
+ {
+ 	size_t len = 0;
+ 	size_t eht_ml_len = 2 + EHT_ML_COMMON_INFO_LEN;
+ 	u8 link_id;
+ 
++	if (eml_disable)
++		eht_ml_len -= 2; /* EML Capabilities (2) */
++
+ 	if (include_mld_id)
+ 		eht_ml_len++;
+ 
+@@ -811,7 +822,8 @@ size_t hostapd_eid_eht_ml_beacon_len(struct hostapd_data *hapd,
+ 				     struct mld_info *info,
+ 				     bool include_mld_id)
+ {
+-	return hostapd_eid_eht_ml_len(info, include_mld_id);
++	return hostapd_eid_eht_ml_len(info, include_mld_id,
++				      hapd->iconf->eml_disable);
+ }
+ 
+ 
+@@ -1406,3 +1418,103 @@ out:
+ 
+ 	return WLAN_STATUS_SUCCESS;
+ }
++
++static void ieee802_11_send_eml_omn(struct hostapd_data *hapd,
++				    const u8 *addr,
++				    struct eml_omn_element *omn_ie,
++				    size_t len)
++{
++	struct wpabuf *buf;
++
++	buf = wpabuf_alloc(2 + len);
++	if (!buf)
++		return;
++
++	wpabuf_put_u8(buf, WLAN_ACTION_PROTECTED_EHT);
++	wpabuf_put_u8(buf, WLAN_PROTECTED_EHT_ACTION_EML_OMN);
++	wpabuf_put_data(buf, omn_ie, len);
++
++	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
++				wpabuf_head(buf), wpabuf_len(buf));
++
++	wpabuf_free(buf);
++}
++
++static void ieee802_11_rx_eml_omn(struct hostapd_data *hapd,
++				  const u8 *addr, const u8 *frm,
++				  size_t len)
++{
++	struct eml_omn_element *omn_ie;
++
++	if (hapd->iconf->eml_disable) {
++		wpa_printf(MSG_ERROR,
++			   "Ignore EML Operating Mode Notification from "
++			   MACSTR
++			   " since EML Capabilities is disabled",
++			   MAC2STR(addr));
++		return;
++	}
++
++	/* EML Operating Mode Notification IE */
++	omn_ie = os_zalloc(sizeof(struct eml_omn_element));
++	if (omn_ie == NULL)
++		return;
++
++	os_memcpy(omn_ie, frm, len);
++
++	if (omn_ie->control & EHT_EML_OMN_CONTROL_EMLMR_MODE) {
++		wpa_printf(MSG_ERROR,
++			   "EML: Ignore EML Operating Mode Fotification from "
++			   MACSTR
++			   " since doesn't support EMLMR",
++			   MAC2STR(addr));
++		goto out;
++	}
++
++	hostapd_drv_set_eml_omn(hapd, addr, omn_ie);
++
++	omn_ie->control &= ~(EHT_EML_OMN_CONTROL_EMLSR_PARA_UPDATE_COUNT |
++			     EHT_EML_OMN_CONTROL_INDEV_COEX_ACTIVITIES);
++
++	if (hapd->iconf->eml_resp) {
++		ieee802_11_send_eml_omn(hapd, addr, omn_ie, len);
++		wpa_printf(MSG_ERROR, "EML: AP send EML Operating Mode Fotification to "
++				       MACSTR,
++				       MAC2STR(addr));
++	}
++out:
++	os_free(omn_ie);
++	return;
++}
++
++void ieee802_11_rx_prot_eht(struct hostapd_data *hapd,
++			    const struct ieee80211_mgmt *mgmt,
++			    size_t len)
++{
++	u8 action;
++	const u8 *payload;
++	size_t plen;
++
++	if (!hapd->conf->mld_ap)
++		return;
++
++	if (len < IEEE80211_HDRLEN + 2)
++		return;
++
++	payload = mgmt->u.action.u.eht_prot.variable;
++	action = mgmt->u.action.u.eht_prot.action;
++	plen = len - IEEE80211_HDRLEN - 2;
++
++	switch (action) {
++	case WLAN_PROTECTED_EHT_ACTION_EML_OMN:
++		ieee802_11_rx_eml_omn(hapd, mgmt->sa, payload, plen);
++		return;
++	}
++
++	wpa_printf(MSG_ERROR, "EHT: Unsupported Protected EHT Action %u from " MACSTR,
++		   action, MAC2STR(mgmt->sa));
++
++	return;
++
++}
++
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index 44d98d5e0..58e66f555 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -1901,7 +1901,7 @@ int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta)
+ 			    sta->supported_rates_len,
+ 			    0, NULL, NULL, NULL, 0, NULL, 0, NULL,
+ 			    sta->flags, 0, 0, 0, 0,
+-			    mld_link_addr, mld_link_sta)) {
++			    mld_link_addr, mld_link_sta, 0)) {
+ 		hostapd_logger(hapd, sta->addr,
+ 			       HOSTAPD_MODULE_IEEE80211,
+ 			       HOSTAPD_LEVEL_NOTICE,
+diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
+index efb584c66..fb481b8b2 100644
+--- a/src/common/ieee802_11_defs.h
++++ b/src/common/ieee802_11_defs.h
+@@ -775,6 +775,36 @@
+ #define WLAN_PROT_FTM 2
+ #define WLAN_PROT_FTM_REPORT 3
+ 
++/* Protected EHT action codes */
++#define WLAN_PROTECTED_EHT_ACTION_EML_OMN 6
++
++/* EML Operating Mode Notification frame */
++#define EHT_EML_OMN_CONTROL_EMLSR_MODE 0x1
++#define EHT_EML_OMN_CONTROL_EMLMR_MODE 0x2
++#define EHT_EML_OMN_CONTROL_EMLSR_PARA_UPDATE_COUNT 0x4
++#define EHT_EML_OMN_CONTROL_INDEV_COEX_ACTIVITIES 0x8
++
++/* EMLSR Parameter Update field */
++#define EHT_EML_OMN_EMLSR_PADDING_DELAY_MASK 0x07
++#define EHT_EML_OMN_EMLSR_TRANSITION_DELAY_MASK 0x38
++
++struct eml_omn_element {
++	u8 dialog_token;
++	u8 control;
++	le16 bitmap;
++	union {
++		struct {
++			u8 emlsr_para_update;
++		} STRUCT_PACKED emlsr_info;
++		struct {
++			u8 mcs_map_count_control;
++			u8 mcs_map_bw80[3];
++			u8 mcs_map_bw160[3];
++			u8 mcs_map_bw320[3];
++		} STRUCT_PACKED emlmr_info;
++	} u;
++} STRUCT_PACKED;
++
+ /* Radio Measurement capabilities (from RM Enabled Capabilities element)
+  * IEEE Std 802.11-2020, 9.4.2.44, Table 9-179 */
+ /* byte 1 (out of 5) */
+@@ -1161,6 +1191,10 @@ struct ieee80211_mgmt {
+ 					u8 dialog_token;
+ 					u8 variable[];
+ 				} STRUCT_PACKED rrm;
++				struct {
++					u8 action;
++					u8 variable[];
++				} STRUCT_PACKED eht_prot;
+ 			} u;
+ 		} STRUCT_PACKED action;
+ 	} u;
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index be516e017..c6de8862b 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -19,6 +19,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
+ 	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
+ 	MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
++	MTK_NL80211_VENDOR_SUBCMD_EML_CTRL = 0xd3,
+ };
+ 
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -287,6 +288,20 @@ enum mtk_vendor_attr_beacon_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_eml_ctrl {
++
++	MTK_VENDOR_ATTR_EML_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_EML_LINK_ID,
++	MTK_VENDOR_ATTR_EML_STA_ADDR,
++	MTK_VENDOR_ATTR_EML_CTRL_STRUCT,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_EML_CTRL,
++	MTK_VENDOR_ATTR_EML_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_EML_CTRL -1
++};
++
+ #define CSI_BW20_DATA_COUNT	64
+ #define CSI_BW40_DATA_COUNT	128
+ #define CSI_BW80_DATA_COUNT	256
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 924a1baba..e7e62c5ad 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -2575,6 +2575,7 @@ struct hostapd_sta_add_params {
+ 	bool mld_link_sta;
+ 	s8 mld_link_id;
+ 	const u8 *mld_link_addr;
++	u16 eml_capa;
+ };
+ 
+ struct mac_address {
+@@ -5277,6 +5278,17 @@ struct wpa_driver_ops {
+ 	 */
+ 	int (*beacon_ctrl)(void *priv, u8 beacon_mode);
+ 
++	/**
++	 * set eml omn - Send the EML Operating Mode
++	 * 		 Notification content to driver
++	 * @priv: Private driver interface data
++	 * @link_id: MLD link id
++	 * @addr: MLD STA address
++	 * @omn_ie: EML OMN content sent by the MLD STA
++	 */
++	int (*set_eml_omn)(void *priv, u8 link_id,
++			   u8 *addr, struct eml_omn_element *omn_ie);
++
+ 	/**
+ 	 * three_wire_ctrl - set three_wire_ctrl mode
+ 	 * @priv: Private driver interface data
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 555c97bf7..9451714a7 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -2893,6 +2893,9 @@ static int nl80211_action_subscribe_ap(struct i802_bss *bss)
+ 	if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0)
+ 		ret = -1;
+ #endif /* CONFIG_FST */
++	/* Protected EHT */
++	if (nl80211_register_action_frame(bss, (u8 *) "\x25", 1) < 0)
++		ret = -1;
+ 	/* Vendor-specific */
+ 	if (nl80211_register_action_frame(bss, (u8 *) "\x7f", 1) < 0)
+ 		ret = -1;
+@@ -5981,6 +5984,14 @@ static int wpa_driver_nl80211_sta_add(void *priv,
+ 			goto fail;
+ 	}
+ 
++	if (params->eml_capa) {
++		wpa_printf(MSG_DEBUG, "  * eml_capa=%u",
++			   params->eml_capa);
++		if (nla_put_u16(msg, NL80211_ATTR_EML_CAPABILITY,
++				params->eml_capa))
++			goto fail;
++	}
++
+ 	ret = send_and_recv_cmd(drv, msg);
+ 	msg = NULL;
+ 	if (ret)
+@@ -15261,6 +15272,47 @@ static int nl80211_get_mld_addr(void *priv, u8 *addr)
+ 
+ 	return 0;
+ }
++
++static int nl80211_set_eml_omn(void *priv, u8 link_id, u8 *addr,
++			       struct eml_omn_element *omn_ie)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret = -ENOBUFS;
++
++	if (!drv->mtk_eml_vendor_cmd_avail) {
++		wpa_printf(MSG_ERROR,
++			   "nl80211: Driver does not support setting EML control");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_EML_CTRL) ||
++	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EML_LINK_ID, link_id) ||
++	    nla_put(msg, MTK_VENDOR_ATTR_EML_STA_ADDR, ETH_ALEN, addr) ||
++	    nla_put(msg, MTK_VENDOR_ATTR_EML_CTRL_STRUCT,
++		    sizeof(struct eml_omn_element), omn_ie))
++		goto fail;
++
++
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set EML OMN ctrl. ret = %d (%s)",
++			   ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return ret;
++}
+ #endif
+ 
+ static int
+@@ -15619,6 +15671,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.mu_ctrl = nl80211_mu_ctrl,
+ 	.mu_dump = nl80211_mu_dump,
+ 	.beacon_ctrl = nl80211_beacon_ctrl,
++	.set_eml_omn = nl80211_set_eml_omn,
+ #ifdef CONFIG_DPP
+ 	.dpp_listen = nl80211_dpp_listen,
+ #endif /* CONFIG_DPP */
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index b710b50cd..2cc40e0dc 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -213,6 +213,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_pp_vendor_cmd_avail:1;
+ 	unsigned int mtk_beacon_ctrl_vendor_cmd_avail:1;
+ 	unsigned int mtk_csi_vendor_cmd_avail:1;
++	unsigned int mtk_eml_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 5a53700d1..c1327a679 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1176,6 +1176,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL:
+ 					drv->mtk_csi_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_EML_CTRL:
++					drv->mtk_eml_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0087-mtk-hostapd-Fix-Operating-Mode-Notification-issue-in.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0087-mtk-hostapd-Fix-Operating-Mode-Notification-issue-in.patch
new file mode 100644
index 0000000..e637185
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0087-mtk-hostapd-Fix-Operating-Mode-Notification-issue-in.patch
@@ -0,0 +1,36 @@
+From 9b059de49d2c441ec3b83c82de3c4c6e0078f4f7 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+Date: Wed, 15 May 2024 15:23:55 +0800
+Subject: [PATCH 087/126] mtk: hostapd: Fix Operating Mode Notification issue
+ in 2GHz
+
+If this patch is not applied, since the driver enables the
+Operating Mode Notification feature, hostapd will enable the OMN bit
+in the Extended Capabilities Element when constructing beacon.
+However, this is not allowed on frequency bands that do not support
+ieee80211ac, hence add this patch to fix this issue.
+
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ src/ap/ieee802_11_shared.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
+index 3dd3a6a77..58283eae9 100644
+--- a/src/ap/ieee802_11_shared.c
++++ b/src/ap/ieee802_11_shared.c
+@@ -510,6 +510,11 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid,
+ 			*pos &= ~0x08;
+ 		if (i == 2 && !hapd->iconf->mbssid)
+ 			*pos &= ~0x40;
++
++		/* Clear bits 62 (Operating Mode Notification)
++		 * if ieee80211ac is not enabled (mainly 2.4G and 6G) */
++		if (i == 7 && !hapd->iconf->ieee80211ac)
++			*pos &= ~0x40;
+ 	}
+ 
+ 	while (len > 0 && eid[1 + len] == 0) {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0087-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0087-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch
deleted file mode 100644
index ac847f5..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0087-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch
+++ /dev/null
@@ -1,102 +0,0 @@
-From d6e854c62cd825756cc1b46c8b006855cf9e057e Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Wed, 6 Mar 2024 15:01:33 +0800
-Subject: [PATCH 087/104] mtk: wpa_supplicant: add 'mld_allowed_phy'
- configuration option for MLD STA
-
-A new configuration option named 'mld_allowed_phy' is added for MLD STA.
-This option indicates the bitmap of allowed phy for MLO connection.
-Note that setting 'mld_allowed_phy' to 0 makes no phy allowed for MLO.
-In other word, the STA becomes a legacy STA.
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- wpa_supplicant/config.c      |  1 +
- wpa_supplicant/config.h      |  1 +
- wpa_supplicant/config_file.c |  2 ++
- wpa_supplicant/sme.c         | 18 ++++++++++++++++++
- 4 files changed, 22 insertions(+)
-
-diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
-index 7bb57e2ab..d3c75ee94 100644
---- a/wpa_supplicant/config.c
-+++ b/wpa_supplicant/config.c
-@@ -5680,6 +5680,7 @@ static const struct global_parse_data global_fields[] = {
- #endif /* CONFIG_PASN */
- #ifdef CONFIG_TESTING_OPTIONS
- 	{ INT_RANGE(mld_force_single_link, 0, 1), 0 },
-+	{ INT_RANGE(mld_allowed_phy, 0, 7), 0 },
- 	{ INT_RANGE(mld_connect_band_pref, 0, MLD_CONNECT_BAND_PREF_MAX), 0 },
- 	{ FUNC(mld_connect_bssid_pref), 0 },
- #endif /* CONFIG_TESTING_OPTIONS */
-diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
-index 8981305c2..c0164fa76 100644
---- a/wpa_supplicant/config.h
-+++ b/wpa_supplicant/config.h
-@@ -1800,6 +1800,7 @@ struct wpa_config {
- 	u8 mld_connect_bssid_pref[ETH_ALEN];
- 
- 	int mld_force_single_link;
-+	u8 mld_allowed_phy; /* bitmap of allowed phy for MLO connection */
- #endif /* CONFIG_TESTING_OPTIONS */
- };
- 
-diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
-index 7a3ed6373..875d00bb4 100644
---- a/wpa_supplicant/config_file.c
-+++ b/wpa_supplicant/config_file.c
-@@ -1622,6 +1622,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
- #ifdef CONFIG_TESTING_OPTIONS
- 	if (config->mld_force_single_link)
- 		fprintf(f, "mld_force_single_link=1\n");
-+	if (config->mld_allowed_phy)
-+		fprintf(f, "mld_allowed_phy=%u\n", config->mld_allowed_phy);
- 	if (config->mld_connect_band_pref != MLD_CONNECT_BAND_PREF_AUTO)
- 		fprintf(f, "mld_connect_band_pref=%d\n",
- 			config->mld_connect_band_pref);
-diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
-index 5b69812b5..ef258fadc 100644
---- a/wpa_supplicant/sme.c
-+++ b/wpa_supplicant/sme.c
-@@ -517,6 +517,16 @@ out:
- }
- 
- 
-+#ifdef CONFIG_TESTING_OPTIONS
-+static bool check_mld_allowed_phy(struct wpa_supplicant *wpa_s, int freq)
-+{
-+	return ((wpa_s->conf->mld_allowed_phy & BIT(0)) && IS_2P4GHZ(freq)) ||
-+	       ((wpa_s->conf->mld_allowed_phy & BIT(1)) && IS_5GHZ(freq)) ||
-+	       ((wpa_s->conf->mld_allowed_phy & BIT(2)) && is_6ghz_freq(freq));
-+}
-+#endif /* CONFIG_TESTING_OPTIONS */
-+
-+
- static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
- 				   struct wpa_bss *bss)
- {
-@@ -528,6 +538,11 @@ static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
- 	for_each_link(bss->valid_links, i) {
- 		const u8 *bssid = bss->mld_links[i].bssid;
- 
-+#ifdef CONFIG_TESTING_OPTIONS
-+		if (!check_mld_allowed_phy(wpa_s, bss->mld_links[i].freq))
-+			continue;
-+#endif /* CONFIG_TESTING_OPTIONS */
-+
- 		wpa_s->valid_links |= BIT(i);
- 		os_memcpy(wpa_s->links[i].bssid, bssid, ETH_ALEN);
- 		wpa_s->links[i].freq = bss->mld_links[i].freq;
-@@ -577,6 +592,9 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
- 	if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) &&
- 	    !wpa_bss_parse_basic_ml_element(wpa_s, bss, wpa_s->ap_mld_addr,
- 					    NULL, ssid, NULL) &&
-+#ifdef CONFIG_TESTING_OPTIONS
-+	    wpa_s->conf->mld_allowed_phy &&
-+#endif /* CONFIG_TESTING_OPTIONS */
- 	    bss->valid_links) {
- 		wpa_printf(MSG_DEBUG, "MLD: In authentication");
- 		wpas_sme_set_mlo_links(wpa_s, bss);
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0088-mtk-hostapd-add-mlo-probe-client-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0088-mtk-hostapd-add-mlo-probe-client-support.patch
new file mode 100644
index 0000000..8cf4e0a
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0088-mtk-hostapd-add-mlo-probe-client-support.patch
@@ -0,0 +1,76 @@
+From 2b760a3738dbc5d0c9fa71a1f73bba2265453f46 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 10 May 2024 17:52:41 +0800
+Subject: [PATCH 088/126] mtk: hostapd: add mlo probe client support
+
+Add mld-level probe client support
+Only register one eloop ap_handle_timeout per mld
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 12 ++++++++++++
+ src/ap/sta_info.c    | 18 +++++++++++++++++-
+ 2 files changed, 29 insertions(+), 1 deletion(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index d9775e13e..2038a3712 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -1469,6 +1469,18 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
+ 							hapd->conf->transition_disable);
+ 		}
+ 
++#ifdef CONFIG_IEEE80211BE
++		/* workaround before hostapd cli support per link configuration */
++		if (hapd->conf->mld_ap) {
++			struct hostapd_data *h;
++
++			for_each_mld_link(h, hapd) {
++				if (os_strcasecmp(cmd, "ap_max_inactivity") == 0)
++					h->conf->ap_max_inactivity = hapd->conf->ap_max_inactivity;
++			}
++		}
++#endif /* CONFIG_IEEE80211BE */
++
+ #ifdef CONFIG_TESTING_OPTIONS
+ 		if (os_strcmp(cmd, "ft_rsnxe_used") == 0)
+ 			wpa_auth_set_ft_rsnxe_used(hapd->wpa_auth,
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index 58e66f555..bc729137d 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -819,6 +819,7 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
+ 	struct sta_info *sta;
+ 	int i;
+ 	int max_inactivity = hapd->conf->ap_max_inactivity;
++	bool registered = false;
+ 
+ 	sta = ap_get_sta(hapd, addr);
+ 	if (sta)
+@@ -855,7 +856,22 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
+ 	if (sta->max_idle_period)
+ 		max_inactivity = (sta->max_idle_period * 1024 + 999) / 1000;
+ 
+-	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
++#ifdef CONFIG_IEEE80211BE
++	if (hapd->conf->mld_ap) {
++		struct hostapd_data *h;
++		struct sta_info *s;
++
++		for_each_mld_link(h, hapd) {
++			s = ap_get_sta(h, addr);
++			if (s && eloop_is_timeout_registered(ap_handle_timer, h, s)) {
++				registered = true;
++				break;
++			}
++		}
++	}
++#endif /* CONFIG_IEEE80211BE */
++
++	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER) && !registered) {
+ 		wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+ 			   "for " MACSTR " (%d seconds - ap_max_inactivity)",
+ 			   __func__, MAC2STR(addr),
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0088-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0088-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch
deleted file mode 100644
index 1ba9e75..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0088-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch
+++ /dev/null
@@ -1,284 +0,0 @@
-From a5b5d12eac0a1a4c53de67adc5793dbee87cb769 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Thu, 29 Feb 2024 19:55:34 +0800
-Subject: [PATCH 088/104] mtk: hostapd: support band_idx option for
- set_mu/get_mu vendor command
-
-Support band_idx for set_mu and get_mu vendor command. The usage shows
-as below:
-1. get_mu: $ hostapd_cli -i <intf> get_mu <band_idx>
-2. set_mu: $ hostapd_cli -i <intf> set_mu <mu_onff>:<band_idx>
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- hostapd/config_file.c        |  9 +++++
- hostapd/ctrl_iface.c         | 78 ++++++++++++++++++++++++++++--------
- hostapd/hostapd_cli.c        |  2 +-
- src/ap/ap_config.h           |  1 +
- src/ap/ap_drv_ops.c          |  2 +-
- src/common/mtk_vendor.h      |  1 +
- src/drivers/driver.h         |  2 +-
- src/drivers/driver_nl80211.c | 15 +++----
- 8 files changed, 82 insertions(+), 28 deletions(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index e9caa45f3..ef9bafb28 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5431,6 +5431,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 			return 1;
- 		}
- 		conf->pp_mode = (u8) val;
-+	} else if (os_strcmp(buf, "band_idx") == 0) {
-+		int val = atoi(pos);
-+
-+		if (val < 0) {
-+			wpa_printf(MSG_ERROR, "Line %d: invalid band_idx value",
-+				   line);
-+			return 1;
-+		}
-+		conf->band_idx = (u8) val;
- 	} else {
- 		wpa_printf(MSG_ERROR,
- 			   "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 0fded7ed4..c5540f5fd 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4178,17 +4178,42 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
- 	value = pos;
- 
- 	if (os_strcmp(config, "onoff") == 0) {
--		int mu = atoi(value);
--		if (mu < 0 || mu > 15) {
--			wpa_printf(MSG_ERROR, "Invalid value for mu");
--			return -1;
-+		cnt = hostapd_parse_argument_helper(value, &val);
-+		if (cnt == -1)
-+			goto fail;
-+		if (cnt < 1 || val[0] > 15)
-+			goto para_fail;
-+
-+		if (hostapd_is_mld_ap(hapd)) {
-+			u8 band_idx;
-+
-+			if (cnt != 2)
-+				goto para_fail;
-+
-+			band_idx = val[1];
-+
-+			for (i = 0; i < hapd->iface->interfaces->count; i++) {
-+				struct hostapd_iface *iface;
-+
-+				iface = hapd->iface->interfaces->iface[i];
-+				if (!iface || !iface->conf)
-+					continue;
-+
-+				if (iface->conf->band_idx == band_idx) {
-+					hapd = iface->bss[0];
-+					break;
-+				}
-+			}
-+			if (hapd->iface->conf->band_idx != band_idx)
-+				goto para_fail;
- 		}
--		hapd->iconf->mu_onoff = (u8) mu;
- 
--		if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) == 0)
--			return os_snprintf(buf, buflen, "OK\n");
--		else
-+		hapd->iconf->mu_onoff = val[0];
-+		os_free(val);
-+		if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) != 0)
- 			goto fail;
-+
-+		return os_snprintf(buf, buflen, "OK\n");
- 	}
- 
- 	if (hapd->iconf->muru_config == NULL)
-@@ -4200,6 +4225,7 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
- 	comm = &muru->comm;
- 
- 	if (os_strncmp(config, "update", 6) == 0) {
-+		// [ToDo] "update" needs to support band_idx argument
- 		ret = hostapd_drv_mu_ctrl(hapd, MU_CTRL_UPDATE);
- 
- 		os_free(hapd->iconf->muru_config);
-@@ -4342,15 +4368,14 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
- 
- para_fail:
- 	os_free(val);
--	wpa_printf(MSG_ERROR, "Incorrect input number\n");
-+	wpa_printf(MSG_ERROR, "Input number or value is incorrect\n");
- fail:
- 	return os_snprintf(buf, buflen, "FAIL\n");
- }
- 
--
- static int
--hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
--					 size_t buflen)
-+hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *input, char *buf,
-+			  size_t buflen)
- {
- 	u8 mu_onoff;
- 	char *pos, *end;
-@@ -4358,14 +4383,35 @@ hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
- 	pos = buf;
- 	end = buf + buflen;
- 
-+	if (hostapd_is_mld_ap(hapd)) {
-+		u8 band_idx, i;
-+
-+		band_idx = (u8)atoi(input);
-+
-+		for (i = 0; i < hapd->iface->interfaces->count; i++) {
-+			struct hostapd_iface *iface;
-+
-+			iface = hapd->iface->interfaces->iface[i];
-+			if (!iface || !iface->conf)
-+				continue;
-+
-+			if (iface->conf->band_idx == band_idx) {
-+				hapd = iface->bss[0];
-+				break;
-+			}
-+		}
-+		if (hapd->iface->conf->band_idx != band_idx)
-+			return os_snprintf(pos, end - pos, "Invalid band idx to get_mu\n");
-+	}
-+
- 	if (hapd->iface->state != HAPD_IFACE_ENABLED)
- 		return os_snprintf(pos, end - pos, "Not allowed to get_mu when current state is %s\n",
- 				   hostapd_state_text(hapd->iface->state));
- 
- 	if (hostapd_drv_mu_dump(hapd, &mu_onoff) == 0) {
- 		hapd->iconf->mu_onoff = mu_onoff;
--		return os_snprintf(pos, end - pos, "[hostapd_cli] = UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
--			!!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
-+		return os_snprintf(pos, end - pos, "Band idx %u: UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
-+			hapd->iconf->band_idx, !!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
- 	} else {
- 		wpa_printf(MSG_INFO, "ctrl iface failed to call");
- 		return -1;
-@@ -5488,8 +5534,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 							  reply_size);
- 	} else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
- 		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply, reply_size);
--	} else if (os_strncmp(buf, "GET_MU", 6) == 0) {
--		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
-+	} else if (os_strncmp(buf, "GET_MU ", 7) == 0) {
-+		reply_len = hostapd_ctrl_iface_get_mu(hapd, buf + 7, reply, reply_size);
- 	} else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
- 		reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
- 	} else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 7e4485cb8..100896c34 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1461,7 +1461,7 @@ static int hostapd_cli_cmd_set_mu(struct wpa_ctrl *ctrl, int argc,
- static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
- 					   char *argv[])
- {
--	return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
-+	return hostapd_cli_cmd(ctrl, "GET_MU", 0, argc, argv);
- }
- 
- static int hostapd_cli_cmd_disable_beacon(struct wpa_ctrl *ctrl, int argc,
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 1f686550e..3bd8df9ce 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1298,6 +1298,7 @@ struct hostapd_config {
- 	u8 amsdu;
- 	void *muru_config;
- 	u8 pp_mode;
-+	u8 band_idx;
- };
- 
- enum three_wire_mode {
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index b7896c110..ac7ef00cd 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1281,7 +1281,7 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
- {
- 	if (!hapd->driver || !hapd->driver->mu_dump)
- 		return 0;
--	return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
-+	return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff, hapd->iconf->band_idx);
- }
- 
- int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 5531802b8..1e4c3670e 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -207,6 +207,7 @@ enum mtk_vendor_attr_mu_ctrl {
- 	 * above data structure.
- 	 */
- 	MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
-+	MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX,
- 
- 	/* keep last */
- 	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 2940650df..10ae48729 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5241,7 +5241,7 @@ struct wpa_driver_ops {
- 	 *
- 	 */
- 	 int (*mu_ctrl)(void *priv, u8 mode, void *config);
--	 int (*mu_dump)(void *priv, u8 *mu_onoff);
-+	 int (*mu_dump)(void *priv, u8 *mu_onoff, u8 band_idx);
- 
- 	/**
- 	 * beacon_ctrl - ctrl on off for beacon
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 6d300c0c8..17aaa16a8 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -14010,7 +14010,8 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, void *config)
- 
- 	switch (mode) {
- 	case MU_CTRL_ONOFF:
--		if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff))
-+		if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff) ||
-+		    nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX, cfg->band_idx))
- 			goto fail;
- 		break;
- 	case MU_CTRL_UPDATE:
-@@ -14074,7 +14075,7 @@ static int mu_dump_handler(struct nl_msg *msg, void *arg)
- 	return 0;
- }
- 
--static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
-+static int nl80211_mu_dump(void *priv, u8 *mu_onoff, u8 band_idx)
- {
- 	struct i802_bss *bss = priv;
- 	struct wpa_driver_nl80211_data *drv = bss->drv;
-@@ -14090,17 +14091,13 @@ static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
- 
- 	if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
- 		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
--		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL)) {
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
-+		!(attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+		nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX, band_idx)) {
- 		nlmsg_free(msg);
- 		return -ENOBUFS;
- 	}
- 
--  attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
--	if (!attr) {
--		nlmsg_free(msg);
--		return -1;
--	}
--
- 	nla_nest_end(msg, attr);
- 
- 	ret = send_and_recv_resp(drv, msg, mu_dump_handler, mu_onoff);
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0089-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0089-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch
deleted file mode 100644
index cbf05f5..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0089-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch
+++ /dev/null
@@ -1,131 +0,0 @@
-From a0cbcd04400458bf7f4f4086beecbf8db6800c36 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 19 Jan 2024 14:11:05 +0800
-Subject: [PATCH 089/104] mtk: hostapd: Handle DFS radar detection in MLO
-
-To handle DFS CAC in MLO, we add the following changes:
-1. Add link id info to radar detect cmd for the kernel to use the correct link.
-2. Block RNR IE for disabled iface. (the EID len would be wrong without it)
-3. Only flush the old stations for the first BSS; otherwise, after DFS CAC
-stations would be flushed again.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Add background radar handling
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/ap_drv_ops.c                |  9 +++++++++
- src/ap/hostapd.c                   |  3 +++
- src/ap/ieee802_11.c                |  3 +++
- src/drivers/driver_nl80211.c       | 18 ++++++++++++++++++
- src/drivers/driver_nl80211.h       |  1 +
- src/drivers/driver_nl80211_event.c |  3 ++-
- 6 files changed, 36 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index ac7ef00cd..9357ce7b6 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1012,6 +1012,15 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
- 		return -1;
- 	}
- 	data.radar_background = radar_background;
-+	data.link_id = -1;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	if (hapd->conf->mld_ap) {
-+		data.link_id = hapd->mld_link_id;
-+		wpa_printf(MSG_DEBUG,
-+			   "hostapd_start_dfs_cac: link_id=%d", data.link_id);
-+	}
-+#endif /* CONFIG_IEEE80211BE */
- 
- 	res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
- 	if (!res) {
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index f8b05de45..8e3f0b281 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -1371,6 +1371,9 @@ int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
- 	if (!hostapd_mld_is_first_bss(hapd))
- 		wpa_printf(MSG_DEBUG,
- 			   "MLD: %s: Setting non-first BSS", __func__);
-+	else
-+		/* Only flush old stations when setting up first BSS for MLD. */
-+		flush_old_stations = 0;
- 
- 	wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)",
- 		   __func__, hapd, conf->iface, first);
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 0f357d786..fe0a5bce4 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -7852,6 +7852,9 @@ u8 *hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
- 		    !is_6ghz_op_class(iface->conf->op_class))
- 			continue;
- 
-+		if (!iface->bss[0]->started)
-+			continue;
-+
- 		eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
- 					    current_len, NULL, false);
- 	}
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 17aaa16a8..ad73b4ac1 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -10589,6 +10589,24 @@ static int nl80211_start_radar_detection(void *priv,
- 		return -1;
- 	}
- 
-+	if (freq->link_id != NL80211_DRV_LINK_ID_NA) {
-+		wpa_printf(MSG_DEBUG, "nl80211: Set link_id=%u for radar detect",
-+			   freq->link_id);
-+
-+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, freq->link_id)) {
-+			nlmsg_free(msg);
-+			return -ENOBUFS;
-+		}
-+
-+		if (freq->radar_background) {
-+			struct i802_link *link = nl80211_get_link(bss, freq->link_id);
-+
-+			link->background_freq = freq->freq;
-+		} else {
-+			nl80211_link_set_freq(bss, freq->link_id, freq->freq);
-+		}
-+	}
-+
- 	ret = send_and_recv_cmd(drv, msg);
- 	if (ret == 0)
- 		return 0;
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 9866c221c..a0a62e536 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -56,6 +56,7 @@ struct i802_link {
- 	unsigned int beacon_set:1;
- 
- 	int freq;
-+	int background_freq;
- 	int bandwidth;
- 	u8 addr[ETH_ALEN];
- 	void *ctx;
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 90084356d..03bad4bb3 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -1631,7 +1631,8 @@ nl80211_get_link_id_by_freq(struct i802_bss *bss, unsigned int freq)
- 	unsigned int i;
- 
- 	for_each_link(bss->valid_links, i) {
--		if ((unsigned int) bss->links[i].freq == freq)
-+		if ((unsigned int) bss->links[i].freq == freq ||
-+		    (unsigned int) bss->links[i].background_freq == freq)
- 			return i;
- 	}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0089-mtk-hostapd-add-support-for-channel-switch.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0089-mtk-hostapd-add-support-for-channel-switch.patch
new file mode 100644
index 0000000..209c997
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0089-mtk-hostapd-add-support-for-channel-switch.patch
@@ -0,0 +1,134 @@
+From 6a5460e02c6d6303146a43ffd3ee10ae5e1fb2a1 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 16 May 2024 15:59:30 +0800
+Subject: [PATCH 089/126] mtk: hostapd: add support for channel switch
+
+For extender channel switch, a 'band_idx' is necessary so that
+Extender STA can tell Extender AP exactly which band is doing
+a channel switch.
+
+The original flow fails on parsing a 0 band_idx. Updated flow can
+avoid the problem and also a default 'band_idx' is assigned for an
+error parsing.
+
+Another error handling is also added for the case that the iface with
+target 'band_idx' is not found.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ucode.c         | 20 +++++++++++++++++++-
+ wpa_supplicant/ucode.c | 31 ++++++++++++++++++++++++++++---
+ 2 files changed, 47 insertions(+), 4 deletions(-)
+
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index a72193282..a69caa6cf 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -693,7 +693,7 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 	struct hostapd_config *conf;
+ 	struct csa_settings csa = {};
+ 	uint64_t intval;
+-	int i, ret = 0;
++	int i, ret = 0, band_idx;
+ 
+ 	wpa_printf(MSG_INFO, "ucode: mtk: channel switch for %s\n", iface->phy);
+ 	if (!iface || ucv_type(info) != UC_OBJECT)
+@@ -727,6 +727,9 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 	if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
+ 		csa.freq_params.center_freq2 = intval;
+ 
++	intval = ucv_int64_get(ucv_object_get(info, "band_idx", NULL));
++	band_idx = errno ? iface->conf->band_idx : intval;
++
+ 	wpa_printf(MSG_INFO, "ucode: mtk: switch channel information:\n");
+ 	wpa_printf(MSG_INFO, "    * freq is %d\n", csa.freq_params.freq);
+ 	wpa_printf(MSG_INFO, "    * bandwidth is %d\n",
+@@ -737,6 +740,21 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 			csa.freq_params.center_freq1);
+ 	wpa_printf(MSG_INFO, "    * center_freq2 is %d\n",
+ 			csa.freq_params.center_freq2);
++	wpa_printf(MSG_INFO, "    * band_idx is %d\n",
++			band_idx);
++
++#ifdef CONFIG_IEEE80211BE
++	for (i = 0; i < iface->interfaces->count; i++) {
++		if (band_idx == iface->interfaces->iface[i]->conf->band_idx) {
++			iface = iface->interfaces->iface[i];
++			wpa_printf(MSG_INFO, "ucode: mtk: MLD: switch to iface with band_idx %d \n", band_idx);
++			break;
++		}
++	}
++
++	if (band_idx != iface->bss[0]->iconf->band_idx)
++		return NULL;
++#endif /* CONFIG_IEEE80211BE */
+ 
+ 	for (i = 0; i < iface->num_bss; i++) {
+ 		ret = hostapd_switch_channel(iface->bss[i], &csa);
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+index ac0639a90..050bed6ae 100644
+--- a/wpa_supplicant/ucode.c
++++ b/wpa_supplicant/ucode.c
+@@ -99,9 +99,10 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ 	const char *state;
+ 	uc_value_t *val;
+ 	enum oper_chan_width ch_width;
+-	int center_freq1, bw320_offset = 1;
++	int control_freq, center_freq1, bw320_offset = 1, band_idx;
+ 
+-	if (event != EVENT_CH_SWITCH_STARTED)
++	if (event != EVENT_CH_SWITCH_STARTED &&
++	    event != EVENT_LINK_CH_SWITCH_STARTED)
+ 		return;
+ 
+ 	val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
+@@ -117,7 +118,13 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ 	val = ucv_object_new(vm);
+ 	uc_value_push(ucv_get(val));
+ 
+-	if (event == EVENT_CH_SWITCH_STARTED) {
++	if (event == EVENT_CH_SWITCH_STARTED ||
++	    event == EVENT_LINK_CH_SWITCH_STARTED) {
++		enum hostapd_hw_mode hw_mode;
++		int is_24ghz;
++		u8 channel;
++
++		control_freq = data->ch_switch.freq;
+ 		center_freq1 = data->ch_switch.cf1;
+ 
+ 		switch (data->ch_switch.ch_width) {
+@@ -147,6 +154,23 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ 		     center_freq1 == 6905)
+ 			bw320_offset = 2;
+ 
++		hw_mode = ieee80211_freq_to_chan(control_freq, &channel);
++		is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
++			hw_mode == HOSTAPD_MODE_IEEE80211B;
++		/*
++		 * Assume that the mapping between band and band_idx is
++		 * 2 GHz band: band_idx 0
++		 * 5 GHz band: band_idx 1
++		 * 6 GHz band: band_idx 2
++		 * */
++		if (is_24ghz)
++			band_idx = 0;
++		else if (IS_5GHZ(control_freq))
++			band_idx = 1;
++		else if (is_6ghz_freq(control_freq))
++			band_idx = 2;
++
++
+ 		ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count));
+ 		ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq));
+ 		ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
+@@ -154,6 +178,7 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ 		ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
+ 		ucv_object_add(val, "ch_width", ucv_int64_new(ch_width));
+ 		ucv_object_add(val, "bw320_offset", ucv_int64_new(bw320_offset));
++		ucv_object_add(val, "band_idx", ucv_int64_new(band_idx));
+ 	}
+ 
+ 	ucv_put(wpa_ucode_call(4));
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0090-mtk-hostapd-add-link-id-to-hostapd-cli-chan-switch.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0090-mtk-hostapd-add-link-id-to-hostapd-cli-chan-switch.patch
deleted file mode 100644
index 129f54b..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0090-mtk-hostapd-add-link-id-to-hostapd-cli-chan-switch.patch
+++ /dev/null
@@ -1,59 +0,0 @@
-From d71c29484010bcb0bda82eb529689d0748bd653e Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 31 Jan 2024 10:39:08 +0800
-Subject: [PATCH 090/104] mtk: hostapd: add link id to hostapd cli chan switch
-
-temporary workaround for mlo channel switch
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- hostapd/ctrl_iface.c   | 3 +--
- hostapd/hostapd_cli.c  | 2 +-
- src/ap/ctrl_iface_ap.c | 2 ++
- 3 files changed, 4 insertions(+), 3 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index c5540f5fd..f0c990314 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -2801,10 +2801,9 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
- 	if (ret)
- 		return ret;
- 
--	settings.link_id = -1;
- #ifdef CONFIG_IEEE80211BE
- 	if (iface->num_bss && iface->bss[0]->conf->mld_ap)
--		settings.link_id = iface->bss[0]->mld_link_id;
-+		iface = iface->interfaces->iface[settings.link_id];
- #endif /* CONFIG_IEEE80211BE */
- 
- 	ret = hostapd_ctrl_check_freq_params(&settings.freq_params,
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 100896c34..acfa3b1d1 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1195,7 +1195,7 @@ static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
- 		printf("Invalid chan_switch command: needs at least two "
- 		       "arguments (count and freq)\n"
- 		       "usage: <cs_count> <freq> [sec_channel_offset=] "
--		       "[center_freq1=] [center_freq2=] [bandwidth=] "
-+		       "[center_freq1=] [center_freq2=] [bandwidth=] [link_id=] "
- 		       "[blocktx] [ht|vht|he|eht]\n");
- 		return -1;
- 	}
-diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index b92311e32..ca4e3e7a4 100644
---- a/src/ap/ctrl_iface_ap.c
-+++ b/src/ap/ctrl_iface_ap.c
-@@ -1108,6 +1108,8 @@ int hostapd_parse_csa_settings(const char *pos,
- 		} \
- 	} while (0)
- 
-+	SET_CSA_SETTING(link_id);
-+	settings->link_id = settings->freq_params.link_id;
- 	SET_CSA_SETTING(center_freq1);
- 	SET_CSA_SETTING(center_freq2);
- 	SET_CSA_SETTING(bandwidth);
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0090-mtk-hostapd-extend-MLO-information-getting.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0090-mtk-hostapd-extend-MLO-information-getting.patch
new file mode 100644
index 0000000..a73671f
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0090-mtk-hostapd-extend-MLO-information-getting.patch
@@ -0,0 +1,105 @@
+From e3acb288c0e30eef9b734b62045f505c1392aab3 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 17 May 2024 16:36:19 +0800
+Subject: [PATCH 090/126] mtk: hostapd: extend MLO information getting
+
+Extend MLO information getting, including center frequency & bandwidth,
+from the driver. These informations are helpful for Extender STA to
+synchronize channel informations to Extender AP.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/drivers/driver.h              |  3 ++-
+ src/drivers/driver_nl80211.c      | 24 ++++++++++++++++++++++--
+ wpa_supplicant/events.c           |  3 +++
+ wpa_supplicant/wpa_supplicant_i.h |  3 ++-
+ 4 files changed, 29 insertions(+), 4 deletions(-)
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index e7e62c5ad..eed99dac8 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -3058,8 +3058,9 @@ struct driver_sta_mlo_info {
+ 	struct {
+ 		u8 addr[ETH_ALEN];
+ 		u8 bssid[ETH_ALEN];
+-		unsigned int freq;
++		unsigned int freq, center_freq1, center_freq2;
+ 		struct t2lm_mapping t2lmap;
++		enum chan_width width;
+ 	} links[MAX_NUM_MLD_LINKS];
+ };
+ 
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 9451714a7..b3ae50d15 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -1171,6 +1171,27 @@ static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid)
+ }
+ 
+ 
++static void get_link_channel_info(struct nlattr **link_data, u8 link_id,
++				  struct driver_sta_mlo_info *info)
++{
++	info->links[link_id].freq =
++		nla_get_u32(link_data[NL80211_ATTR_WIPHY_FREQ]);
++
++	if (link_data[NL80211_ATTR_CHANNEL_WIDTH]) {
++		info->links[link_id].width =
++	       convert2width(nla_get_u32(link_data[NL80211_ATTR_CHANNEL_WIDTH]));
++
++		if (link_data[NL80211_ATTR_CENTER_FREQ1])
++			info->links[link_id].center_freq1 =
++			      nla_get_u32(link_data[NL80211_ATTR_CENTER_FREQ1]);
++
++		if (link_data[NL80211_ATTR_CENTER_FREQ2])
++			info->links[link_id].center_freq2 =
++			      nla_get_u32(link_data[NL80211_ATTR_CENTER_FREQ2]);
++	}
++}
++
++
+ static int get_mlo_info(struct nl_msg *msg, void *arg)
+ {
+ 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+@@ -1208,8 +1229,7 @@ static int get_mlo_info(struct nl_msg *msg, void *arg)
+ 		os_memcpy(info->links[link_id].addr,
+ 			  nla_data(link_data[NL80211_ATTR_MAC]), ETH_ALEN);
+ 		if (link_data[NL80211_ATTR_WIPHY_FREQ])
+-			info->links[link_id].freq =
+-				nla_get_u32(link_data[NL80211_ATTR_WIPHY_FREQ]);
++			get_link_channel_info(link_data, link_id, info);
+ 	}
+ 
+ 	return NL_SKIP;
+diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
+index 7b91ce988..cd838c6cf 100644
+--- a/wpa_supplicant/events.c
++++ b/wpa_supplicant/events.c
+@@ -4122,6 +4122,9 @@ static int wpa_drv_get_mlo_info(struct wpa_supplicant *wpa_s)
+ 		os_memcpy(wpa_s->links[i].addr, mlo.links[i].addr, ETH_ALEN);
+ 		os_memcpy(wpa_s->links[i].bssid, mlo.links[i].bssid, ETH_ALEN);
+ 		wpa_s->links[i].freq = mlo.links[i].freq;
++		wpa_s->links[i].center_freq1 = mlo.links[i].center_freq1;
++		wpa_s->links[i].center_freq2 = mlo.links[i].center_freq2;
++		wpa_s->links[i].width = mlo.links[i].width;
+ 		wpa_supplicant_update_link_bss(wpa_s, i, mlo.links[i].bssid);
+ 	}
+ 
+diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
+index 952a3bd5a..d2835cc68 100644
+--- a/wpa_supplicant/wpa_supplicant_i.h
++++ b/wpa_supplicant/wpa_supplicant_i.h
+@@ -740,7 +740,8 @@ struct wpa_supplicant {
+ 	struct {
+ 		u8 addr[ETH_ALEN];
+ 		u8 bssid[ETH_ALEN];
+-		unsigned int freq;
++		unsigned int freq, center_freq1, center_freq2;
++		enum chan_width width;
+ 		struct wpa_bss *bss;
+ 		bool disabled;
+ 	} links[MAX_NUM_MLD_LINKS];
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0091-mtk-hostapd-refactor-the-operating-channel-width-nam.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0091-mtk-hostapd-refactor-the-operating-channel-width-nam.patch
new file mode 100644
index 0000000..d4a6433
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0091-mtk-hostapd-refactor-the-operating-channel-width-nam.patch
@@ -0,0 +1,105 @@
+From 7f0f9705e413d6a879b03e1989b7691e031ea2a0 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 17 May 2024 17:02:08 +0800
+Subject: [PATCH 091/126] mtk: hostapd: refactor the operating channel width
+ naming
+
+There are many data structures about channel width in hostapd. This
+commit re-names the one which is used to describe the operating
+channel width so that following commits can use other channel width
+variables.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/ucode.c | 24 ++++++++++++------------
+ 1 file changed, 12 insertions(+), 12 deletions(-)
+
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+index 050bed6ae..bc5cf2ab9 100644
+--- a/wpa_supplicant/ucode.c
++++ b/wpa_supplicant/ucode.c
+@@ -98,7 +98,7 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ {
+ 	const char *state;
+ 	uc_value_t *val;
+-	enum oper_chan_width ch_width;
++	enum oper_chan_width oper_chwidth;
+ 	int control_freq, center_freq1, bw320_offset = 1, band_idx;
+ 
+ 	if (event != EVENT_CH_SWITCH_STARTED &&
+@@ -129,27 +129,27 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ 
+ 		switch (data->ch_switch.ch_width) {
+ 		case CHAN_WIDTH_80:
+-			ch_width = CONF_OPER_CHWIDTH_80MHZ;
++			oper_chwidth = CONF_OPER_CHWIDTH_80MHZ;
+ 			break;
+ 		case CHAN_WIDTH_80P80:
+-			ch_width = CONF_OPER_CHWIDTH_80P80MHZ;
++			oper_chwidth = CONF_OPER_CHWIDTH_80P80MHZ;
+ 			break;
+ 		case CHAN_WIDTH_160:
+-			ch_width = CONF_OPER_CHWIDTH_160MHZ;
++			oper_chwidth = CONF_OPER_CHWIDTH_160MHZ;
+ 			break;
+ 		case CHAN_WIDTH_320:
+-			ch_width = CONF_OPER_CHWIDTH_320MHZ;
++			oper_chwidth = CONF_OPER_CHWIDTH_320MHZ;
+ 			break;
+ 		case CHAN_WIDTH_20_NOHT:
+ 		case CHAN_WIDTH_20:
+ 		case CHAN_WIDTH_40:
+ 		default:
+-			ch_width = CONF_OPER_CHWIDTH_USE_HT;
++			oper_chwidth = CONF_OPER_CHWIDTH_USE_HT;
+ 			break;
+ 		}
+ 
+ 		/* Check bandwidth 320 MHz-2 */
+-		if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
++		if (oper_chwidth == CONF_OPER_CHWIDTH_320MHZ &&
+ 		    (center_freq1 == 6265) || center_freq1 == 6585 ||
+ 		     center_freq1 == 6905)
+ 			bw320_offset = 2;
+@@ -176,7 +176,7 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ 		ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
+ 		ucv_object_add(val, "center_freq1", ucv_int64_new(center_freq1));
+ 		ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
+-		ucv_object_add(val, "ch_width", ucv_int64_new(ch_width));
++		ucv_object_add(val, "ch_width", ucv_int64_new(oper_chwidth));
+ 		ucv_object_add(val, "bw320_offset", ucv_int64_new(bw320_offset));
+ 		ucv_object_add(val, "band_idx", ucv_int64_new(band_idx));
+ 	}
+@@ -274,7 +274,7 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 	uc_value_t *ret, *val, *link_obj = uc_fn_arg(0);
+ 	struct wpa_channel_info ci;
+ 	u8 op_class, channel;
+-	enum oper_chan_width ch_width = CONF_OPER_CHWIDTH_USE_HT;
++	enum oper_chan_width oper_chwidth = CONF_OPER_CHWIDTH_USE_HT;
+ 	int center_freq1, bw320_offset = 1, is_24ghz, band_idx;
+ 	enum hostapd_hw_mode hw_mode;
+ 	int link_id = ucv_int64_get(link_obj);
+@@ -319,9 +319,9 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 
+ 		if (!ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
+ 						   sec_chan, &op_class, &channel))
+-			ch_width = op_class_to_ch_width(op_class);
++			oper_chwidth = op_class_to_ch_width(op_class);
+ 
+-		if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
++		if (oper_chwidth == CONF_OPER_CHWIDTH_320MHZ &&
+ 		    (center_freq1 == 6265) || center_freq1 == 6585 ||
+ 		     center_freq1 == 6905) {
+ 			/* Bandwidth 320 MHz-2 */
+@@ -330,7 +330,7 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 
+ 		ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
+ 		ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
+-		ucv_object_add(ret, "ch_width", ucv_int64_new(ch_width));
++		ucv_object_add(ret, "ch_width", ucv_int64_new(oper_chwidth));
+ 		ucv_object_add(ret, "bw320_offset", ucv_int64_new(bw320_offset));
+ 		ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
+ 	}
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0091-mtk-wifi-hostapd-add-wds-mlo-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0091-mtk-wifi-hostapd-add-wds-mlo-support.patch
deleted file mode 100644
index 4ee23cd..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0091-mtk-wifi-hostapd-add-wds-mlo-support.patch
+++ /dev/null
@@ -1,158 +0,0 @@
-From 11e9653a0df41e119ff5acad3ffcbd2825690413 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Tue, 16 Jan 2024 16:22:17 +0800
-Subject: [PATCH 091/104] mtk: wifi: hostapd: add wds mlo support
-
-1. Add mld_assoc_sta to get the primary sta_info.
-2. Find hapd according to mld address.
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- src/ap/drv_callbacks.c       |  3 ++-
- src/ap/ieee802_11.c          |  8 ++++++++
- src/ap/sta_info.c            |  6 +++++-
- src/ap/sta_info.h            |  1 +
- src/drivers/driver.h         |  3 +++
- src/drivers/driver_nl80211.c | 14 ++++++++++++++
- 6 files changed, 33 insertions(+), 2 deletions(-)
-
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index 2d946afd6..27c7555a1 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -1762,6 +1762,7 @@ switch_link_hapd(struct hostapd_data *hapd, int link_id)
- static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
- 					    const u8 *bssid, int link_id)
- {
-+	struct hostapd_data *ret = NULL;
- 	size_t i;
- 
- 	if (bssid == NULL)
-@@ -1797,7 +1798,7 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
- #endif /*CONFIG_IEEE80211BE */
- 	}
- 
--	return NULL;
-+	return ret;
- }
- 
- 
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index fe0a5bce4..688393422 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -3105,6 +3105,7 @@ static void handle_auth(struct hostapd_data *hapd,
- 
- 			ap_sta_set_mld(sta, true);
- 			sta->mld_assoc_link_id = link_id;
-+			sta->mld_assoc_sta = sta;
- 
- 			/*
- 			 * Set the MLD address as the station address and the
-@@ -4479,6 +4480,7 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
- 
- 	sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
- 	sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
-+	sta->mld_assoc_sta = origin_sta;
- 
- 	status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
- 	if (status != WLAN_STATUS_SUCCESS) {
-@@ -6957,6 +6959,12 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
- 	struct sta_info *sta;
- 
- 	sta = ap_get_sta(hapd, src);
-+
-+#ifdef CONFIG_IEEE80211BE
-+	if (sta && sta->mld_info.mld_sta)
-+		sta = sta->mld_assoc_sta;
-+#endif
-+
- 	if (sta &&
- 	    ((sta->flags & WLAN_STA_ASSOC) ||
- 	     ((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) {
-diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
-index e9fa0ed6e..2b8307a27 100644
---- a/src/ap/sta_info.c
-+++ b/src/ap/sta_info.c
-@@ -74,6 +74,7 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
- 	while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
- 		s = s->hnext;
- 
-+#ifdef CONFIG_IEEE80211BE
- 	if (hapd->conf->mld_ap && !s) {
- 		u8 link_id;
- 
-@@ -84,10 +85,13 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
- 				continue;
- 
- 			for (s = h->sta_list; s; s = s->next)
--				if (!os_memcmp(s->setup_link_addr, sta, 6))
-+				if ((!os_memcmp(s->setup_link_addr, sta, 6) ||
-+				     !os_memcmp(s->addr, sta, 6)) &&
-+				     s->flags & WLAN_STA_ASSOC)
- 					return s;
- 		}
- 	}
-+#endif
- 
- 	return s;
- }
-diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
-index cd89db6c8..8e500ec9a 100644
---- a/src/ap/sta_info.h
-+++ b/src/ap/sta_info.h
-@@ -334,6 +334,7 @@ struct sta_info {
- #ifdef CONFIG_IEEE80211BE
- 	struct mld_info mld_info;
- 	u8 mld_assoc_link_id;
-+	struct sta_info *mld_assoc_sta;
- #endif /* CONFIG_IEEE80211BE */
- };
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 10ae48729..ba61f5842 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5339,6 +5339,9 @@ struct wpa_driver_ops {
- 	 * @pp_mode: Value is defined in enum pp_mode
- 	 */
- 	int (*pp_mode_set)(void *priv, const u8 pp_mode);
-+#ifdef CONFIG_IEEE80211BE
-+	int (*get_mld_addr)(void *priv, u8 *addr);
-+#endif
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index ad73b4ac1..df4a7ec41 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -15181,6 +15181,17 @@ fail:
- 	return -ENOBUFS;
- }
- 
-+#ifdef CONFIG_IEEE80211BE
-+static int nl80211_get_mld_addr(void *priv, u8 *addr)
-+{
-+	struct i802_bss *bss = priv;
-+
-+	os_memcpy(addr, bss->addr, ETH_ALEN);
-+
-+	return 0;
-+}
-+#endif
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
- 	.desc = "Linux nl80211/cfg80211",
-@@ -15361,4 +15372,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.amnt_dump = nl80211_amnt_dump,
- 	.background_radar_mode = nl80211_background_radar_mode,
- 	.pp_mode_set = nl80211_pp_mode_set,
-+#ifdef CONFIG_IEEE80211BE
-+	.get_mld_addr = nl80211_get_mld_addr,
-+#endif
- };
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0092-mtk-hostapd-refactor-legacy-STA-getting-operating-ch.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0092-mtk-hostapd-refactor-legacy-STA-getting-operating-ch.patch
new file mode 100644
index 0000000..ecd524f
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0092-mtk-hostapd-refactor-legacy-STA-getting-operating-ch.patch
@@ -0,0 +1,63 @@
+From b81e4dd857867be4af9bff4b698aec0edc333b34 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 17 May 2024 17:05:54 +0800
+Subject: [PATCH 092/126] mtk: hostapd: refactor legacy STA getting operating
+ channel information
+
+The refactor is for following MLO extension.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/ucode.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+index bc5cf2ab9..d4b2160b6 100644
+--- a/wpa_supplicant/ucode.c
++++ b/wpa_supplicant/ucode.c
+@@ -274,10 +274,12 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 	uc_value_t *ret, *val, *link_obj = uc_fn_arg(0);
+ 	struct wpa_channel_info ci;
+ 	u8 op_class, channel;
++	enum chan_width chwidth;
+ 	enum oper_chan_width oper_chwidth = CONF_OPER_CHWIDTH_USE_HT;
+ 	int center_freq1, bw320_offset = 1, is_24ghz, band_idx;
+ 	enum hostapd_hw_mode hw_mode;
+ 	int link_id = ucv_int64_get(link_obj);
++	u32 freq;
+ 
+ 	if (!wpa_s)
+ 		return NULL;
+@@ -308,7 +310,9 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 			band_idx = 2;
+ 
+ 		wpa_drv_channel_info(wpa_s, &ci);
++		freq = ci.frequency;
+ 		center_freq1 = ci.center_frq1;
++		chwidth=ci.chanwidth;
+ 
+ 		if (bss->freq != center_freq1) {
+ 			if (is_24ghz)
+@@ -317,8 +321,8 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 				sec_chan = (bss->freq / 20) & 1 ? 1 : -1;
+ 		}
+ 
+-		if (!ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
+-						   sec_chan, &op_class, &channel))
++		if (!ieee80211_chaninfo_to_channel(freq, chwidth, sec_chan,
++						   &op_class, &channel))
+ 			oper_chwidth = op_class_to_ch_width(op_class);
+ 
+ 		if (oper_chwidth == CONF_OPER_CHWIDTH_320MHZ &&
+@@ -329,7 +333,7 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 		}
+ 
+ 		ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
+-		ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
++		ucv_object_add(ret, "frequency", ucv_int64_new(freq));
+ 		ucv_object_add(ret, "ch_width", ucv_int64_new(oper_chwidth));
+ 		ucv_object_add(ret, "bw320_offset", ucv_int64_new(bw320_offset));
+ 		ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0093-mtk-hostapd-get-link-channel-information-and-synchro.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0093-mtk-hostapd-get-link-channel-information-and-synchro.patch
new file mode 100644
index 0000000..15283a0
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0093-mtk-hostapd-get-link-channel-information-and-synchro.patch
@@ -0,0 +1,83 @@
+From 0946b9ece456bc11e4ef620063e7b0078d3d73aa Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 17 May 2024 17:21:06 +0800
+Subject: [PATCH 093/126] mtk: hostapd: get link channel information and
+ synchronize to AP
+
+'wpa_s->valid_links' is used to determine the connection is MLO or not,
+and different ways are used to retrieve operating channel information.
+
+Refactor center frequency calculation part in the function
+uc_wpa_freq_info.
+1. It does not have to set seg0 for 2GHz.
+2. The original center frequency calculation is wrong for 2G. Also, center
+   frequency 1 of BW 20/40 MHz can be derived from control frequency &
+   secondary channel offset.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ucode.c         |  2 +-
+ src/utils/ucode.c      |  8 +++++---
+ wpa_supplicant/ucode.c | 14 ++++++++++----
+ 3 files changed, 16 insertions(+), 8 deletions(-)
+
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index a69caa6cf..8c05404f5 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -657,7 +657,7 @@ out:
+ 	wpa_printf(MSG_INFO, "    * seg0: %d\n",
+ 			hostapd_get_oper_centr_freq_seg0_idx(conf));
+ 	wpa_printf(MSG_INFO, "    * seg1: %d\n",
+-			hostapd_get_oper_centr_freq_seg0_idx(conf));
++			hostapd_get_oper_centr_freq_seg1_idx(conf));
+ 	wpa_printf(MSG_INFO, "    * oper_chwidth: %d\n",
+ 			hostapd_get_oper_chwidth(conf));
+ 
+diff --git a/src/utils/ucode.c b/src/utils/ucode.c
+index 81d472f6b..8bbbbbeff 100644
+--- a/src/utils/ucode.c
++++ b/src/utils/ucode.c
+@@ -186,9 +186,11 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 	ucv_object_add(ret, "oper_chwidth", ucv_int64_new(chanwidth));
+ 	ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
+ 
+-	if (chanwidth == CONF_OPER_CHWIDTH_USE_HT && !sec_channel) {
+-		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(channel));
+-		ucv_object_add(ret, "center_freq1", ucv_int64_new(freq_val));
++	if (chanwidth == CONF_OPER_CHWIDTH_USE_HT) {
++		center_idx = freq_val < 3000 ? 0 : channel;
++		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(center_idx));
++		ucv_object_add(ret, "center_freq1",
++			       ucv_int64_new(freq_val + sec_channel * 10));
+ 		return ret;
+ 	}
+ 
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+index d4b2160b6..450780737 100644
+--- a/wpa_supplicant/ucode.c
++++ b/wpa_supplicant/ucode.c
+@@ -309,10 +309,16 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 		else if (is_6ghz_freq(bss->freq))
+ 			band_idx = 2;
+ 
+-		wpa_drv_channel_info(wpa_s, &ci);
+-		freq = ci.frequency;
+-		center_freq1 = ci.center_frq1;
+-		chwidth=ci.chanwidth;
++		if (wpa_s->valid_links) {
++			freq = wpa_s->links[link_id].freq;
++			center_freq1 = wpa_s->links[link_id].center_freq1;
++			chwidth = wpa_s->links[link_id].width;
++		} else {
++			wpa_drv_channel_info(wpa_s, &ci);
++			freq = ci.frequency;
++			center_freq1 = ci.center_frq1;
++			chwidth=ci.chanwidth;
++		}
+ 
+ 		if (bss->freq != center_freq1) {
+ 			if (is_24ghz)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0093-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0093-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch
deleted file mode 100644
index 0cff032..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0093-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From ce0ccc758fc8a5076ce3476f627b00019cf90ab1 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Fri, 1 Mar 2024 16:59:53 +0800
-Subject: [PATCH 093/104] mtk: hostapd: prevent getting non-MLD STA for other
- links
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/sta_info.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
-index 2b8307a27..9a8510980 100644
---- a/src/ap/sta_info.c
-+++ b/src/ap/sta_info.c
-@@ -87,7 +87,8 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
- 			for (s = h->sta_list; s; s = s->next)
- 				if ((!os_memcmp(s->setup_link_addr, sta, 6) ||
- 				     !os_memcmp(s->addr, sta, 6)) &&
--				     s->flags & WLAN_STA_ASSOC)
-+				     s->flags & WLAN_STA_ASSOC &&
-+				     s->mld_info.mld_sta)
- 					return s;
- 		}
- 	}
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0094-mtk-hostapd-AP-MLD-specify-link-id-for-unicast-DEAUT.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0094-mtk-hostapd-AP-MLD-specify-link-id-for-unicast-DEAUT.patch
deleted file mode 100644
index c276024..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0094-mtk-hostapd-AP-MLD-specify-link-id-for-unicast-DEAUT.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-From 8c4eb9b740a9f2ae57e048edbbc4aad1d62734f2 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Tue, 27 Feb 2024 15:04:35 +0800
-Subject: [PATCH 094/104] mtk: hostapd: AP MLD: specify link id for unicast
- DEAUTH
-
-When deauthenticating the STA, hostapd should specifies the setup link
-of the target STA so that the TX status of the DEAUTH can be forwarded
-to the correct link (BSS).
-
-(The original gerrit somehow disappears, so I commit it again)
-(https://gerrit.mediatek.inc/c/gateway/WiFi7/mac80211/hostapd/+/8715613)
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/ap_drv_ops.c | 6 +++++-
- 1 file changed, 5 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 9357ce7b6..2c535f24a 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -867,7 +867,11 @@ int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
- 	if (hapd->conf->mld_ap) {
- 		struct sta_info *sta = ap_get_sta(hapd, addr);
- 
--		link_id = hapd->mld_link_id;
-+		if (sta)
-+			link_id = sta->mld_assoc_link_id;
-+		else
-+			link_id = hapd->mld_link_id;
-+
- 		if (ap_sta_is_mld(hapd, sta))
- 			own_addr = hapd->mld->mld_addr;
- 	}
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0094-mtk-hostapd-adjust-Basic-EHT-MCS-and-Nss-Set-in-EHT-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0094-mtk-hostapd-adjust-Basic-EHT-MCS-and-Nss-Set-in-EHT-.patch
new file mode 100644
index 0000000..6f0544b
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0094-mtk-hostapd-adjust-Basic-EHT-MCS-and-Nss-Set-in-EHT-.patch
@@ -0,0 +1,38 @@
+From 8cb7f55db558b56cc687e4d3407210935a2a4c78 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 20 May 2024 14:59:45 +0800
+Subject: [PATCH 094/126] mtk: hostapd: adjust Basic EHT-MCS and Nss Set in EHT
+ Oper IE
+
+Adjust basic EHT-MCS and Nss set in EHT Operation IE. Only set Rx/Tx Max
+Nss that supports EHT-MCS 0-7 to 1, and the other field shall be set to
+0 (0x11). Without this commit, Intel BE200L in WiFi7 R1 Lab will not send
+auth to ap. The reason why we only set EHT-MCS 0-7 as 1 is we align the IE
+value with other testbed ap.
+
+Please note that this patch is for WiFi7 cert Intel BE200L connection
+issue. hostapd shall support configure this IE by hostapd configuration,
+just like he_basic_mcs_nss_set in HE operation IE.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ src/ap/ieee802_11_eht.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index 59ada2f86..7c328799a 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -237,9 +237,6 @@ u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
+ 
+ 	/* TODO: Fill in appropriate EHT-MCS max Nss information */
+ 	oper->basic_eht_mcs_nss_set[0] = 0x11;
+-	oper->basic_eht_mcs_nss_set[1] = 0x11;
+-	oper->basic_eht_mcs_nss_set[2] = 0x11;
+-	oper->basic_eht_mcs_nss_set[3] = 0x11;
+ 
+ 	if (!eht_oper_info_present)
+ 		return pos + elen;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0095-mtk-hostapd-fix-using-wrong-link-id-in-nl80211_set_c.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0095-mtk-hostapd-fix-using-wrong-link-id-in-nl80211_set_c.patch
new file mode 100644
index 0000000..e5d058a
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0095-mtk-hostapd-fix-using-wrong-link-id-in-nl80211_set_c.patch
@@ -0,0 +1,35 @@
+From eaa5c322744016e6e5ecb45e095fec2bd77af0a5 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 24 May 2024 11:01:10 +0800
+Subject: [PATCH 095/126] mtk: hostapd: fix using wrong link id in
+ nl80211_set_channel during set beacon
+
+param.freq->link_id is used in nl80211_set_channel but it is not set in __ieee802_11_set_beacon.
+nl80211_set_channel will return EBUSY after channel switch.
+This error occurs repeatedly especially when the bandwidth is changed.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/beacon.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 9b5c4fc1e..59db8be8d 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -2866,6 +2866,12 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
+ 		params.freq = &freq;
+ 	}
+ 
++	params.freq->link_id = -1;
++#ifdef CONFIG_IEEE80211BE
++	if (hapd->conf->mld_ap)
++		params.freq->link_id = hapd->mld_link_id;
++#endif /* CONFIG_IEEE80211BE */
++
+ 	for (i = 0; i < hapd->iface->num_hw_features; i++) {
+ 		mode = &hapd->iface->hw_features[i];
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0095-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0095-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch
deleted file mode 100644
index e31a05c..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0095-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 161c066295847f1c1828f641e789565fe4bacd11 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Thu, 14 Mar 2024 14:31:28 +0800
-Subject: [PATCH 095/104] mtk: hostapd: using MLD addr as SA/BSSID for
- broadcast DEAUTH
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/ap_drv_ops.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 2c535f24a..3c1609656 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -872,7 +872,7 @@ int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
- 		else
- 			link_id = hapd->mld_link_id;
- 
--		if (ap_sta_is_mld(hapd, sta))
-+		if (ap_sta_is_mld(hapd, sta) || is_multicast_ether_addr(addr))
- 			own_addr = hapd->mld->mld_addr;
- 	}
- #endif /* CONFIG_IEEE80211BE */
-@@ -893,7 +893,7 @@ int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
- 	if (hapd->conf->mld_ap) {
- 		struct sta_info *sta = ap_get_sta(hapd, addr);
- 
--		if (ap_sta_is_mld(hapd, sta))
-+		if (ap_sta_is_mld(hapd, sta) || is_multicast_ether_addr(addr))
- 			own_addr = hapd->mld->mld_addr;
- 	}
- #endif /* CONFIG_IEEE80211BE */
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0096-mtk-hostapd-Remove-BPCC-and-ML-ie-in-per-sta-profile.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0096-mtk-hostapd-Remove-BPCC-and-ML-ie-in-per-sta-profile.patch
new file mode 100644
index 0000000..6133ae8
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0096-mtk-hostapd-Remove-BPCC-and-ML-ie-in-per-sta-profile.patch
@@ -0,0 +1,104 @@
+From 7c009a478fec002ec626163eff2c686b14bc9aa5 Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Thu, 23 May 2024 11:07:25 +0800
+Subject: [PATCH 096/126] mtk: hostapd: Remove BPCC and ML ie in per-sta
+ profile of ML probe response
+
+wifi7 cert testplan request DUT do not bring BPCC in STA info of
+per-sta profile of ML probe response.
+The standard defined not bring ML ie in per-sta profile.
+
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ src/ap/beacon.c         |  2 +-
+ src/ap/ieee802_11_eht.c | 21 +++++++++++----------
+ 2 files changed, 12 insertions(+), 11 deletions(-)
+
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 59db8be8d..1ffe2fb56 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -929,7 +929,7 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
+ 		struct hostapd_data *ml_elem_ap =
+ 			params->mld_ap ? params->mld_ap : hapd;
+ 
+-		if (ml_elem_ap->conf->mld_ap)
++		if (!params->is_ml_sta_info && ml_elem_ap->conf->mld_ap)
+ 			pos = hostapd_eid_eht_ml_beacon(
+ 				ml_elem_ap, params->mld_info,
+ 				pos, !!params->mld_ap);
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index 7c328799a..02e85194c 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -545,8 +545,8 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 		 * beacon interval (2) + TSF offset (8) + DTIM info (2) + BSS
+ 		 * parameters change counter (1) + station profile length.
+ 		 */
+-#define EHT_ML_STA_INFO_LEN 22
+-		size_t total_len = EHT_ML_STA_INFO_LEN +
++		size_t sta_info_len = include_mld_id ? 21 : 22;
++		size_t total_len = sta_info_len +
+ 			link->resp_sta_profile_len;
+ 
+ 		/* Skip the local one */
+@@ -574,14 +574,16 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 			EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK |
+ 			EHT_PER_STA_CTRL_TSF_OFFSET_PRESENT_MSK |
+ 			EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK |
+-			EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK |
+-			EHT_PER_STA_CTRL_BSS_PARAM_CNT_PRESENT_MSK;
++			EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK;
++
++		if (!include_mld_id)
++			control |= EHT_PER_STA_CTRL_BSS_PARAM_CNT_PRESENT_MSK;
+ 		wpabuf_put_le16(buf, control);
+ 
+ 		/* STA Info */
+ 
+ 		/* STA Info Length */
+-		wpabuf_put_u8(buf, EHT_ML_STA_INFO_LEN - 2);
++		wpabuf_put_u8(buf, sta_info_len - 2);
+ 		wpabuf_put_data(buf, link->local_addr, ETH_ALEN);
+ 		wpabuf_put_le16(buf, link_bss->iconf->beacon_int);
+ 
+@@ -597,7 +599,8 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 		wpabuf_put_u8(buf, link_bss->conf->dtim_period);
+ 
+ 		/* BSS Parameters Change Count */
+-		wpabuf_put_u8(buf, link_bss->eht_mld_bss_param_change);
++		if (!include_mld_id)
++			wpabuf_put_u8(buf, link_bss->eht_mld_bss_param_change);
+ 
+ 		if (!link->resp_sta_profile)
+ 			continue;
+@@ -613,8 +616,7 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 			ptr = link->resp_sta_profile;
+ 			len = link->resp_sta_profile_len;
+ 
+-			slice_len = 255 - EHT_ML_STA_INFO_LEN;
+-
++			slice_len = 255 - sta_info_len;
+ 			wpabuf_put_data(buf, ptr, slice_len);
+ 			len -= slice_len;
+ 			ptr += slice_len;
+@@ -764,7 +766,7 @@ static size_t hostapd_eid_eht_ml_len(struct mld_info *info,
+ 	for (link_id = 0; info && link_id < ARRAY_SIZE(info->links);
+ 	     link_id++) {
+ 		struct mld_link_info *link;
+-		size_t sta_len = EHT_ML_STA_INFO_LEN;
++		size_t sta_len = include_mld_id ? 21 : 22;
+ 
+ 		link = &info->links[link_id];
+ 		if (!link->valid)
+@@ -789,7 +791,6 @@ static size_t hostapd_eid_eht_ml_len(struct mld_info *info,
+ 	return len;
+ }
+ #undef EHT_ML_COMMON_INFO_LEN
+-#undef EHT_ML_STA_INFO_LEN
+ 
+ 
+ u8 * hostapd_eid_eht_ml_beacon(struct hostapd_data *hapd,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0096-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0096-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch
deleted file mode 100644
index f311581..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0096-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From eb9916b7e0226793f14616dc98fda76c4d8337bf Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Tue, 27 Feb 2024 15:32:06 +0800
-Subject: [PATCH 096/104] mtk: hostapd: support vht bfee sts can be up to 0x4
-
-Without this commit, the maximum vht bfee sts can only be 0x3. This commit
-support to read BF-ANTENNA-5 to set vht bfee sts capability as 4.
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- hostapd/config_file.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index ef9bafb28..b9a062193 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -1190,6 +1190,9 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf,
- 	if (os_strstr(capab, "[BF-ANTENNA-4]") &&
- 	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
- 		conf->vht_capab |= (3 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
-+	if (os_strstr(capab, "[BF-ANTENNA-5]") &&
-+	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
-+		conf->vht_capab |= (4 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
- 	if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") &&
- 	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
- 		conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0097-mtk-hostapd-add-channel-switch-band-sanity-check.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0097-mtk-hostapd-add-channel-switch-band-sanity-check.patch
new file mode 100644
index 0000000..7ea83ec
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0097-mtk-hostapd-add-channel-switch-band-sanity-check.patch
@@ -0,0 +1,40 @@
+From 0710501eee6f7a2c06fc8f0c5d3e4db8fddc349d Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 30 May 2024 11:24:54 +0800
+Subject: [PATCH 097/126] mtk: hostapd: add channel switch band sanity check
+
+Add band sanity check in case user selecting the wrong freq or link id
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 2038a3712..14a0483bf 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -2816,6 +2816,7 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ 					  char *pos)
+ {
+ #ifdef NEED_AP_MLME
++	struct hostapd_hw_modes *mode = iface->current_mode;
+ 	struct csa_settings settings;
+ 	int ret;
+ 	int dfs_range = 0;
+@@ -2835,6 +2836,12 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ 		settings.link_id = iface->bss[0]->mld_link_id;
+ #endif /* CONFIG_IEEE80211BE */
+ 
++	if (!mode ||
++	    !is_same_band(mode->channels->freq, settings.freq_params.freq)) {
++		wpa_printf(MSG_ERROR, "Invalid band for current mode");
++		return -1;
++	}
++
+ 	ret = hostapd_ctrl_check_freq_params(&settings.freq_params,
+ 					     settings.punct_bitmap);
+ 	if (ret) {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0097-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0097-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch
deleted file mode 100644
index 1afcf3e..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0097-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch
+++ /dev/null
@@ -1,54 +0,0 @@
-From c1e7c5e556cd372594a789091d2ab65a9d8dd56f Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Thu, 19 Oct 2023 14:08:50 +0800
-Subject: [PATCH 097/104] mtk: hostapd: fix issue that tx status handle with
- unmatch hostapd_data.
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
----
- src/ap/ieee802_11.c                | 11 ++++++++++-
- src/drivers/driver_nl80211_event.c |  2 +-
- 2 files changed, 11 insertions(+), 2 deletions(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 688393422..2dd763e63 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -6536,11 +6536,20 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
- #ifdef CONFIG_IEEE80211BE
- 	if (ap_sta_is_mld(hapd, sta) &&
- 	    hapd->mld_link_id != sta->mld_assoc_link_id) {
-+		struct hostapd_data *temp_hapd = hapd;
-+
- 		/* See ieee80211_ml_link_sta_assoc_cb() for the MLD case */
- 		wpa_printf(MSG_DEBUG,
- 			   "%s: MLD: ignore on link station (%d != %d)",
- 			   __func__, hapd->mld_link_id, sta->mld_assoc_link_id);
--		return;
-+
-+		if (temp_hapd->conf->mld_ap && sta->mld_assoc_link_id >= 0) {
-+			struct hostapd_data *link_bss;
-+
-+			link_bss = hostapd_mld_get_link_bss(temp_hapd, sta->mld_assoc_link_id);
-+			if (link_bss)
-+				hapd = link_bss;
-+		}
- 	}
- #endif /* CONFIG_IEEE80211BE */
- 
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 03bad4bb3..885fc4c9e 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -1374,7 +1374,7 @@ static void mlme_event_mgmt(struct i802_bss *bss,
- 	event.rx_mgmt.ctx = bss->ctx;
- 	event.rx_mgmt.link_id = link_id;
- 
--	wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
-+	wpa_supplicant_event(bss->ctx, EVENT_RX_MGMT, &event);
- }
- 
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0098-mtk-hostapd-Fix-the-issue-with-the-presence-of-MLD_I.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0098-mtk-hostapd-Fix-the-issue-with-the-presence-of-MLD_I.patch
new file mode 100644
index 0000000..e35a423
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0098-mtk-hostapd-Fix-the-issue-with-the-presence-of-MLD_I.patch
@@ -0,0 +1,51 @@
+From 933e864265735dd4b668f13cced18e18b789b41c Mon Sep 17 00:00:00 2001
+From: "MeiChia.Chiu" <MeiChia.Chiu@mediatek.com>
+Date: Mon, 3 Jun 2024 14:29:24 +0800
+Subject: [PATCH 098/126] mtk: hostapd: Fix the issue with the presence of
+ MLD_ID in the probe response
+
+The probe response carries the MLD_ID value only
+when mld_id is not 0 (e.g., NonTxBSS).
+
+Signed-off-by: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Signed-off-by: Money Wang <money.wang@mediatek.com>
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ src/ap/ieee802_11_eht.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index 02e85194c..8fc239f36 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -480,7 +480,7 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 	if (hapd->iconf->eml_disable)
+ 		common_info_len -= 2; /* EML Capabilities (2) */
+ 
+-	if (include_mld_id) {
++	if (include_mld_id && hostapd_get_mld_id(hapd)) {
+ 		/* AP MLD ID */
+ 		control |= BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID;
+ 		common_info_len++;
+@@ -526,7 +526,7 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 		   mld_cap);
+ 	wpabuf_put_le16(buf, mld_cap);
+ 
+-	if (include_mld_id) {
++	if (include_mld_id && hostapd_get_mld_id(hapd)) {
+ 		wpa_printf(MSG_DEBUG, "MLD: AP MLD ID=0x%x",
+ 			   hostapd_get_mld_id(hapd));
+ 		wpabuf_put_u8(buf, hostapd_get_mld_id(hapd));
+@@ -820,7 +820,8 @@ size_t hostapd_eid_eht_ml_beacon_len(struct hostapd_data *hapd,
+ 				     struct mld_info *info,
+ 				     bool include_mld_id)
+ {
+-	return hostapd_eid_eht_ml_len(info, include_mld_id,
++	return hostapd_eid_eht_ml_len(info,
++				      include_mld_id && hostapd_get_mld_id(hapd),
+ 				      hapd->iconf->eml_disable);
+ }
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0098-mtk-hostapd-add-connac3-csi-control-interface.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0098-mtk-hostapd-add-connac3-csi-control-interface.patch
deleted file mode 100644
index 9c4c4e5..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0098-mtk-hostapd-add-connac3-csi-control-interface.patch
+++ /dev/null
@@ -1,690 +0,0 @@
-From c125d2615df25bd9a5a4801abfbcc91f21482e02 Mon Sep 17 00:00:00 2001
-From: mtk20656 <chank.chen@mediatek.com>
-Date: Tue, 6 Feb 2024 15:46:05 +0800
-Subject: [PATCH 098/104] mtk: hostapd: add connac3 csi control interface
-
-1. add hostapd_cli interface
-2. add csi set/dump flow
-3. add csi raw data to json
-
-Signed-off-by: mtk20656 <chank.chen@mediatek.com>
----
- hostapd/ctrl_iface.c              | 193 ++++++++++++++++++++++++
- hostapd/hostapd_cli.c             |  16 ++
- src/ap/ap_drv_ops.c               |  13 ++
- src/ap/ap_drv_ops.h               |   2 +
- src/common/mtk_vendor.h           |  31 +++-
- src/drivers/driver.h              |  16 ++
- src/drivers/driver_nl80211.c      | 235 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |   1 +
- src/drivers/driver_nl80211_capa.c |   3 +
- 9 files changed, 503 insertions(+), 7 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index f0c990314..b5f6431bf 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4925,6 +4925,193 @@ hostapd_ctrl_iface_disable_beacon(struct hostapd_data *hapd, char *value,
- 
- }
- 
-+static int
-+hostapd_ctrl_iface_set_csi(struct hostapd_data *hapd, char *cmd,
-+					char *buf, size_t buflen)
-+{
-+	char *tmp;
-+	u8 sta_mac[ETH_ALEN] = {0};
-+	u32 csi_para[4] = {0};
-+	char mac_str[18] = {0};
-+	u8 csi_para_cnt = 0;
-+
-+	tmp = strtok_r(cmd, ",", &cmd);
-+
-+	while (tmp) {
-+		csi_para_cnt++;
-+
-+		if (csi_para_cnt <= 4)
-+			csi_para[csi_para_cnt - 1] = strtol(tmp, &tmp, 10);
-+		else if (csi_para_cnt == 5) {
-+			memcpy(mac_str, tmp, sizeof(mac_str) - 1);
-+			break;
-+		}
-+
-+		tmp = strtok_r(NULL, ",", &cmd);
-+	}
-+
-+	if (strlen(mac_str)) {	/* user input mac string */
-+		if (hwaddr_aton(mac_str, sta_mac) < 0) {
-+			wpa_printf(MSG_ERROR, "station mac is not right.\n");
-+			return -1;
-+		}
-+
-+		if (hostapd_drv_csi_set(hapd, csi_para[0], csi_para[1], csi_para[2], csi_para[3], sta_mac)) {
-+			wpa_printf(MSG_ERROR, "Not able to set csi, %d,%d,%d,%d,%s\n",
-+					csi_para[0], csi_para[1], csi_para[2], csi_para[3], mac_str);
-+			return -1;
-+		}
-+	} else {
-+		if (hostapd_drv_csi_set(hapd, csi_para[0], csi_para[1], csi_para[2], csi_para[3], NULL)) {
-+			wpa_printf(MSG_ERROR, "Not able to set csi, %d,%d,%d,%d\n",
-+					csi_para[0], csi_para[1], csi_para[2], csi_para[3]);
-+			return -1;
-+		}
-+	}
-+
-+	return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+static int mt76_csi_to_json(char *fname, struct csi_resp_data *resp_buf)
-+{
-+#define MAX_BUF_SIZE	10000
-+	FILE *f;
-+	int i;
-+
-+	if (!fname) {
-+		wpa_printf(MSG_ERROR, "csi dump file name is null!\n");
-+		return -1;
-+	}
-+
-+	f = fopen(fname, "a+");
-+	if (!f) {
-+		wpa_printf(MSG_ERROR, "open csi dump file %s failed\n", fname);
-+		return -1;
-+	}
-+
-+	if (fwrite("[", 1, 1, f) != 1) {
-+		fclose(f);
-+		return -1;
-+	}
-+
-+	for (i = 0; i < resp_buf->buf_cnt; i++) {
-+		struct csi_data *c = &resp_buf->csi_buf[i];
-+		char *pos, *buf;
-+		int j;
-+
-+		buf = malloc(MAX_BUF_SIZE);
-+		if (!buf) {
-+			fclose(f);
-+			return -1;
-+		}
-+
-+		pos = buf;
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
-+
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->ts);
-+		pos += snprintf(pos, MAX_BUF_SIZE, "\"%02x%02x%02x%02x%02x%02x\",", c->ta[0], c->ta[1], c->ta[2], c->ta[3], c->ta[4], c->ta[5]);
-+
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->rssi);
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->snr);
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->data_bw);
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->pri_ch_idx);
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->rx_mode);
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->tx_idx);
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->rx_idx);
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->chain_info);
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->ext_info);
-+
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
-+		for (j = 0; j < c->data_num; j++) {
-+			pos += snprintf(pos, MAX_BUF_SIZE, "%d", c->data_i[j]);
-+			if (j != (c->data_num - 1))
-+				pos += snprintf(pos, MAX_BUF_SIZE, ",");
-+		}
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%c,", ']');
-+
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
-+		for (j = 0; j < c->data_num; j++) {
-+			pos += snprintf(pos, MAX_BUF_SIZE, "%d", c->data_q[j]);
-+			if (j != (c->data_num - 1))
-+				pos += snprintf(pos, MAX_BUF_SIZE, ",");
-+		}
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%c", ']');
-+
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%c", ']');
-+		if (i != resp_buf->buf_cnt - 1)
-+			pos += snprintf(pos, MAX_BUF_SIZE, ",");
-+
-+		if (fwrite(buf, 1, pos - buf, f) != (pos - buf)) {
-+			perror("fwrite");
-+			free(buf);
-+			fclose(f);
-+			return -1;
-+		}
-+
-+		free(buf);
-+	}
-+
-+	if (fwrite("]", 1, 1, f) != 1) {
-+		fclose(f);
-+		return -1;
-+	}
-+
-+	fclose(f);
-+
-+	return 0;
-+}
-+
-+static int
-+hostapd_ctrl_iface_dump_csi(struct hostapd_data *hapd, char *cmd,
-+				char *buf, size_t buflen)
-+{
-+	char *tmp, *fname;
-+	int data_cnt = 0, ret = 0;
-+	struct csi_resp_data resp_buf;
-+
-+	tmp = strtok_r(cmd, ",", &cmd);
-+
-+	if (!tmp) {
-+		wpa_printf(MSG_ERROR, "Error in command format\n");
-+		return -1;
-+	}
-+
-+	data_cnt = strtoul(tmp, &tmp, 0);
-+
-+	if (data_cnt > 3000) {
-+		wpa_printf(MSG_ERROR, "Wrong input csi data cnt\n");
-+		return -1;
-+	}
-+
-+	fname = strtok_r(NULL, ",", &cmd);
-+
-+	if (!fname) {
-+		wpa_printf(MSG_ERROR, "Error in command format, csi_filename.\n");
-+		return -1;
-+	}
-+
-+	resp_buf.csi_buf = (struct csi_data *)os_zalloc(sizeof(struct csi_data) * data_cnt);
-+
-+	if (resp_buf.csi_buf == NULL) {
-+		wpa_printf(MSG_ERROR, "Error in memory allocation\n");
-+		return -1;
-+	}
-+
-+	resp_buf.usr_need_cnt = data_cnt;
-+	resp_buf.buf_cnt = 0;
-+
-+	if (hostapd_drv_csi_dump(hapd, (void *)&resp_buf)) {
-+		wpa_printf(MSG_ERROR, "Not able to set csi dump\n");
-+		os_free(resp_buf.csi_buf);
-+		return -1;
-+	}
-+
-+	mt76_csi_to_json(fname, &resp_buf);
-+
-+	os_free(resp_buf.csi_buf);
-+	return 0;
-+}
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -5578,6 +5765,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 	} else if (os_strncmp(buf, "NO_BEACON ", 10) == 0) {
- 		reply_len = hostapd_ctrl_iface_disable_beacon(hapd, buf + 10, reply,
- 							      reply_size);
-+	} else if (os_strncmp(buf, "SET_CSI ", 7) == 0) {
-+		reply_len = hostapd_ctrl_iface_set_csi(hapd, buf + 8,
-+							reply, reply_size);
-+	} else if (os_strncmp(buf, "DUMP_CSI ", 8) == 0) {
-+		reply_len = hostapd_ctrl_iface_dump_csi(hapd, buf + 9,
-+							reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index acfa3b1d1..81b74d6de 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1719,6 +1719,18 @@ static int hostapd_cli_cmd_dump_amnt(struct wpa_ctrl *ctrl, int argc,
- 	return hostapd_cli_cmd(ctrl, "DUMP_AMNT", 1, argc, argv);
- }
- 
-+static int hostapd_cli_cmd_set_csi(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "SET_CSI", 1, argc, argv);
-+}
-+
-+static int hostapd_cli_cmd_dump_csi(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "DUMP_CSI", 1, argc, argv);
-+}
-+
- struct hostapd_cli_cmd {
- 	const char *cmd;
- 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
-@@ -1960,6 +1972,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 		" = Set Station index and mac to monitor"},
- 	{ "dump_amnt", hostapd_cli_cmd_dump_amnt, NULL,
- 		" = Dump RSSI of monitoring Station"},
-+	{ "set_csi", hostapd_cli_cmd_set_csi, NULL,
-+		" = Set csi configuaration"},
-+	{ "dump_csi", hostapd_cli_cmd_dump_csi, NULL,
-+		" = Dump csi data to a json file"},
- 	{ NULL, NULL, NULL, NULL }
- };
- 
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 3c1609656..cb782fa31 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1409,3 +1409,16 @@ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
- 	return hapd->driver->beacon_ctrl(hapd->drv_priv, beacon_mode);
- }
- 
-+int hostapd_drv_csi_set(struct hostapd_data *hapd, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac)
-+{
-+	if (!hapd->driver || !hapd->driver->csi_set)
-+		return 0;
-+	return hapd->driver->csi_set(hapd->drv_priv, mode, cfg, v1, v2, mac);
-+}
-+
-+int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf)
-+{
-+	if (!hapd->driver || !hapd->driver->csi_dump)
-+		return 0;
-+	return hapd->driver->csi_dump(hapd->drv_priv, dump_buf);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 5830705a3..7abc58377 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -171,6 +171,8 @@ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_
- int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
- int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
- int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode);
-+int hostapd_drv_csi_set(struct hostapd_data *hapd, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac);
-+int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 1e4c3670e..c290e72a7 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -73,7 +73,6 @@ enum mtk_vendor_attr_csi_ctrl {
- 	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
- 	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
- 	MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
--	MTK_VENDOR_ATTR_CSI_CTRL_INTERVAL,
- 
- 	MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
- 
-@@ -96,6 +95,7 @@ enum mtk_vendor_attr_csi_data {
- 	MTK_VENDOR_ATTR_CSI_DATA_BW,
- 	MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
- 	MTK_VENDOR_ATTR_CSI_DATA_TA,
-+	MTK_VENDOR_ATTR_CSI_DATA_NUM,
- 	MTK_VENDOR_ATTR_CSI_DATA_I,
- 	MTK_VENDOR_ATTR_CSI_DATA_Q,
- 	MTK_VENDOR_ATTR_CSI_DATA_INFO,
-@@ -106,7 +106,7 @@ enum mtk_vendor_attr_csi_data {
- 	MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
- 	MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
- 	MTK_VENDOR_ATTR_CSI_DATA_MODE,
--	MTK_VENDOR_ATTR_CSI_DATA_H_IDX,
-+	MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO,
- 
- 	/* keep last */
- 	NUM_MTK_VENDOR_ATTRS_CSI_DATA,
-@@ -281,23 +281,40 @@ enum mtk_vendor_attr_beacon_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
- };
- 
--#define CSI_MAX_COUNT 256
-+#define CSI_BW20_DATA_COUNT	64
-+#define CSI_BW40_DATA_COUNT	128
-+#define CSI_BW80_DATA_COUNT	256
-+#define CSI_BW160_DATA_COUNT	512
-+#define CSI_BW320_DATA_COUNT	1024
- #define ETH_ALEN 6
- 
- struct csi_data {
--	s16 data_i[CSI_MAX_COUNT];
--	s16 data_q[CSI_MAX_COUNT];
-+	u8 ch_bw;
-+	u16 data_num;
-+	s16 data_i[CSI_BW320_DATA_COUNT];
-+	s16 data_q[CSI_BW320_DATA_COUNT];
-+	u8 band;
- 	s8 rssi;
- 	u8 snr;
- 	u32 ts;
- 	u8 data_bw;
- 	u8 pri_ch_idx;
- 	u8 ta[ETH_ALEN];
--	u32 info;
-+	u32 ext_info;
- 	u8 rx_mode;
--	u32 h_idx;
-+	u32 chain_info;
- 	u16 tx_idx;
- 	u16 rx_idx;
-+	u32 segment_num;
-+	u8 remain_last;
-+	u16 pkt_sn;
-+	u8 tr_stream;
-+};
-+
-+struct csi_resp_data {
-+	u16 usr_need_cnt;
-+	u16 buf_cnt;
-+	struct csi_data *csi_buf;
- };
- 
- #define AIR_MONITOR_MAX_ENTRY 16
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index ba61f5842..7efb5e342 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5342,6 +5342,22 @@ struct wpa_driver_ops {
- #ifdef CONFIG_IEEE80211BE
- 	int (*get_mld_addr)(void *priv, u8 *addr);
- #endif
-+	/**
-+	 * csi_set - Set csi related mode and parameter
-+	 * @priv: Private driver interface data
-+	 * @mode: Csi mode parameter
-+	 * @cfg: Csi config parameter
-+	 * @v1: Value1
-+	 * @v2: Value2
-+	 * @mac: Station mac for station filter
-+	 */
-+	int (*csi_set)(void *priv, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac);
-+	/**
-+	* csi_dump - Dump csi data to json file
-+	* @priv: Private driver interface data
-+	* @dump_buf: Dump_struct that store csi data and related info
-+	*/
-+	int (*csi_dump)(void *priv, void *dump_buf);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index df4a7ec41..39b45ef4b 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -161,6 +161,35 @@ pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
- 	[MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
- };
- 
-+static struct nla_policy csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
-+	[MTK_VENDOR_ATTR_CSI_CTRL_CFG] = { .type = NLA_NESTED },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2] = { .type = NLA_U32 },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR] = { .type = NLA_NESTED },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM] = { .type = NLA_U16 },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_DATA] = { .type = NLA_NESTED },
-+};
-+
-+static struct nla_policy csi_data_policy[NUM_MTK_VENDOR_ATTRS_CSI_DATA] = {
-+	[MTK_VENDOR_ATTR_CSI_DATA_VER] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_TS] = { .type = NLA_U32 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_RSSI] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_SNR] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_BW] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_TA] = { .type = NLA_NESTED },
-+	[MTK_VENDOR_ATTR_CSI_DATA_NUM] = { .type = NLA_U32 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_I] = { .type = NLA_NESTED },
-+	[MTK_VENDOR_ATTR_CSI_DATA_Q] = { .type = NLA_NESTED },
-+	[MTK_VENDOR_ATTR_CSI_DATA_INFO] = { .type = NLA_U32 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_TX_ANT] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_RX_ANT] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_MODE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO] = { .type = NLA_U32 },
-+};
-+
- static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
- {
- 	struct nl_sock *handle;
-@@ -15192,6 +15221,210 @@ static int nl80211_get_mld_addr(void *priv, u8 *addr)
- }
- #endif
- 
-+static int
-+nl80211_csi_set(void *priv, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	void *tb1, *tb2;
-+	int ret, i;
-+
-+	if (!drv->mtk_csi_vendor_cmd_avail) {
-+		wpa_printf(MSG_ERROR,
-+			"nl80211: Driver does not support csi");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+			nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
-+	if (!data)
-+		goto fail;
-+
-+	tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG | NLA_F_NESTED);
-+	if (!tb1)
-+		goto fail;
-+
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE, mode);
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE, cfg);
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1, v1);
-+	nla_put_u32(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2, v2);
-+
-+	nla_nest_end(msg, tb1);
-+
-+	if (mac) {
-+		tb2 = nla_nest_start(msg, MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR | NLA_F_NESTED);
-+		if (!tb2)
-+			goto fail;
-+
-+		for (i = 0; i < ETH_ALEN; i++)
-+			nla_put_u8(msg, i, mac[i]);
-+
-+		nla_nest_end(msg, tb2);
-+	}
-+
-+	nla_nest_end(msg, data);
-+
-+	ret = send_and_recv_cmd(drv, msg);
-+
-+	if (ret)
-+		wpa_printf(MSG_ERROR, "Failed to set csi. ret=%d (%s)",
-+			ret, strerror(-ret));
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+
-+}
-+
-+static int
-+mt76_csi_dump_cb(struct nl_msg *msg, void *arg)
-+{
-+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+	struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_CSI_CTRL];
-+	struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_CSI_DATA];
-+	struct nlattr *attr, *cur, *data;
-+	int len = 0, rem, idx;
-+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+	struct csi_resp_data *csi_resp = (struct csi_resp_data *)arg;
-+	struct csi_data *c = csi_resp->csi_buf;
-+
-+	c += csi_resp->buf_cnt;
-+
-+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+		genlmsg_attrlen(gnlh, 0), NULL);
-+
-+	attr = tb[NL80211_ATTR_VENDOR_DATA];
-+	if (!attr)
-+		return NL_SKIP;
-+
-+	nla_parse_nested(tb1, MTK_VENDOR_ATTR_CSI_CTRL_MAX,
-+			attr, csi_ctrl_policy);
-+
-+	if (!tb1[MTK_VENDOR_ATTR_CSI_CTRL_DATA])
-+		return NL_SKIP;
-+
-+	nla_parse_nested(tb2, MTK_VENDOR_ATTR_CSI_DATA_MAX,
-+			tb1[MTK_VENDOR_ATTR_CSI_CTRL_DATA], csi_data_policy);
-+
-+	if (!(tb2[MTK_VENDOR_ATTR_CSI_DATA_VER] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_TS] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_RSSI] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_SNR] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_BW] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_TA] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_I] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_Q] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_INFO] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_MODE] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO])) {
-+		fprintf(stderr, "Attributes error for CSI data\n");
-+		return NL_SKIP;
-+	}
-+
-+	c->rssi = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_RSSI]);
-+	c->snr = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_SNR]);
-+	c->data_bw = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_BW]);
-+	c->pri_ch_idx = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX]);
-+	c->rx_mode = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_MODE]);
-+
-+	c->tx_idx = nla_get_u16(tb2[MTK_VENDOR_ATTR_CSI_DATA_TX_ANT]);
-+	c->rx_idx = nla_get_u16(tb2[MTK_VENDOR_ATTR_CSI_DATA_RX_ANT]);
-+
-+	c->ext_info = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_INFO]);
-+	c->chain_info = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO]);
-+
-+	c->ts = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_TS]);
-+
-+	c->data_num = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_NUM]);
-+
-+	idx = 0;
-+	nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_TA], rem) {
-+		if (idx < ETH_ALEN)
-+			c->ta[idx++] = nla_get_u8(cur);
-+	}
-+
-+	idx = 0;
-+	nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_I], rem) {
-+		if (idx < c->data_num)
-+			c->data_i[idx++] = nla_get_u16(cur);
-+	}
-+
-+	idx = 0;
-+	nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_Q], rem) {
-+		if (idx < c->data_num)
-+			c->data_q[idx++] = nla_get_u16(cur);
-+	}
-+
-+	csi_resp->buf_cnt++;
-+
-+	return NL_SKIP;
-+}
-+
-+static int
-+nl80211_csi_dump(void *priv, void *dump_buf)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+	struct csi_resp_data *csi_resp;
-+	u16 pkt_num, i;
-+
-+	if (!drv->mtk_csi_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			"nl80211: Driver does not support csi");
-+		return 0;
-+	}
-+
-+	csi_resp = (struct csi_resp_data *)dump_buf;
-+	pkt_num =  csi_resp->usr_need_cnt;
-+
-+	if (pkt_num > 3000)
-+		return -EINVAL;
-+
-+#define CSI_DUMP_PER_NUM	3
-+	for (i = 0; i < pkt_num / CSI_DUMP_PER_NUM; i++) {
-+		msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
-+		if (!msg)
-+			goto fail;
-+
-+		if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+				nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+				MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL))
-+			goto fail;
-+
-+		data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
-+		if (!data)
-+			goto fail;
-+
-+		nla_put_u16(msg, MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM, CSI_DUMP_PER_NUM);
-+
-+		nla_nest_end(msg, data);
-+
-+		ret = send_and_recv_resp(drv, msg, mt76_csi_dump_cb, dump_buf);
-+	}
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
- 	.desc = "Linux nl80211/cfg80211",
-@@ -15375,4 +15608,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- #ifdef CONFIG_IEEE80211BE
- 	.get_mld_addr = nl80211_get_mld_addr,
- #endif
-+	.csi_set = nl80211_csi_set,
-+	.csi_dump = nl80211_csi_dump,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index a0a62e536..b710b50cd 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -212,6 +212,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int mtk_background_radar_vendor_cmd_avail:1;
- 	unsigned int mtk_pp_vendor_cmd_avail:1;
- 	unsigned int mtk_beacon_ctrl_vendor_cmd_avail:1;
-+	unsigned int mtk_csi_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index f3e3d52e2..8688b849f 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1173,6 +1173,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL :
- 					drv->mtk_beacon_ctrl_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL:
-+					drv->mtk_csi_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0099-fixup-mtk-wifi-hostapd-add-wds-mlo-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0099-fixup-mtk-wifi-hostapd-add-wds-mlo-support.patch
deleted file mode 100644
index c27e065..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0099-fixup-mtk-wifi-hostapd-add-wds-mlo-support.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From a5c0e5c09398a247236d73078a4f86a960a97e34 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Mon, 8 Apr 2024 16:27:51 +0800
-Subject: [PATCH 099/104] fixup! mtk: wifi: hostapd: add wds mlo support
-
-The latest get_hapd_bssid return hapd only if link id is matched.
-However,the hostapd_rx_from_unknown_sta does not have link
-information so it cannot get hapd.
-
-Modify get_hapd_bssid to ignore link id when link id is -1.
-
-Without this patch, wds mode cannot work and the AP would not be
-aware that station is using 4 address.
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- src/ap/drv_callbacks.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index 27c7555a1..82973c5e4 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -1779,7 +1779,7 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
- 		if (ether_addr_equal(bssid, hapd->own_addr) ||
- 		    (hapd->conf->mld_ap &&
- 		     ether_addr_equal(bssid, hapd->mld->mld_addr) &&
--		     link_id == hapd->mld_link_id)) {
-+		     (link_id == hapd->mld_link_id || link_id == -1))) {
- 			return hapd;
- 		} else if (hapd->conf->mld_ap) {
- 			for_each_mld_link(p_hapd, hapd) {
-@@ -1788,7 +1788,7 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
- 
- 				if (ether_addr_equal(bssid, p_hapd->own_addr) ||
- 				    (ether_addr_equal(bssid, p_hapd->mld->mld_addr) &&
--				     link_id == p_hapd->mld_link_id))
-+				     (link_id == p_hapd->mld_link_id || link_id == -1)))
- 					return p_hapd;
- 			}
- 		}
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0099-mtk-hostapd-add-support-for-DFS-channel-switching-wi.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0099-mtk-hostapd-add-support-for-DFS-channel-switching-wi.patch
new file mode 100644
index 0000000..3274297
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0099-mtk-hostapd-add-support-for-DFS-channel-switching-wi.patch
@@ -0,0 +1,539 @@
+From 6805573a7891d68ef5d71c6dcf362ffbd2119240 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 15 Nov 2023 15:06:00 +0800
+Subject: [PATCH 099/126] mtk: hostapd: add support for DFS channel switching
+ with csa sent
+
+Add support for DFS channel switch
+
+When searching for a DFS channel for a background radar channel switch,
+we should not base the selection on the cap of the original channel.
+Otherwise, the selected channel may become invalid when the bandwidth is changed.
+For example, iface->conf->secondary_channel is set to 0 when operating on BW 20.
+Therefore, if the user tries to switch from BW 20 to BW 80, the pre-selected channel
+for BW 80 will not be checked by dfs_is_chan_allowed in dfs_find_channel.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/ctrl_iface.c   | 105 +++++++++++++++---------
+ src/ap/beacon.c        |   5 ++
+ src/ap/ctrl_iface_ap.c |   5 +-
+ src/ap/dfs.c           | 182 +++++++++++++++++++++++++++++++++++------
+ src/ap/dfs.h           |  11 ++-
+ src/ap/ieee802_11.c    |   5 ++
+ 6 files changed, 243 insertions(+), 70 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 14a0483bf..3943cb3aa 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -2817,12 +2817,12 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ {
+ #ifdef NEED_AP_MLME
+ 	struct hostapd_hw_modes *mode = iface->current_mode;
+-	struct csa_settings settings;
++	struct csa_settings settings, background_settings;
+ 	int ret;
+-	int dfs_range = 0;
++	int freq, state;
++	int bandwidth, oper_chwidth;
++	bool background_radar, bw_changed, cac_required = false;
+ 	unsigned int i;
+-	int bandwidth;
+-	u8 chan;
+ 	unsigned int num_err = 0;
+ 	int err = 0;
+ 
+@@ -2853,21 +2853,28 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ 	switch (settings.freq_params.bandwidth) {
+ 	case 40:
+ 		bandwidth = CHAN_WIDTH_40;
++		oper_chwidth = CONF_OPER_CHWIDTH_USE_HT;
+ 		break;
+ 	case 80:
+-		if (settings.freq_params.center_freq2)
++		if (settings.freq_params.center_freq2) {
+ 			bandwidth = CHAN_WIDTH_80P80;
+-		else
++			oper_chwidth = CONF_OPER_CHWIDTH_80P80MHZ;
++		} else {
+ 			bandwidth = CHAN_WIDTH_80;
++			oper_chwidth = CONF_OPER_CHWIDTH_80MHZ;
++		}
+ 		break;
+ 	case 160:
+ 		bandwidth = CHAN_WIDTH_160;
++		oper_chwidth = CONF_OPER_CHWIDTH_160MHZ;
+ 		break;
+ 	case 320:
+ 		bandwidth = CHAN_WIDTH_320;
++		oper_chwidth = CONF_OPER_CHWIDTH_320MHZ;
+ 		break;
+ 	default:
+ 		bandwidth = CHAN_WIDTH_20;
++		oper_chwidth = CONF_OPER_CHWIDTH_USE_HT;
+ 		break;
+ 	}
+ 
+@@ -2882,41 +2889,29 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ 	}
+ 
+ 	if (settings.freq_params.center_freq1)
+-		dfs_range += hostapd_is_dfs_overlap(
+-			iface, bandwidth, settings.freq_params.center_freq1);
++		freq = settings.freq_params.center_freq1;
+ 	else
+-		dfs_range += hostapd_is_dfs_overlap(
+-			iface, bandwidth, settings.freq_params.freq);
+-
+-	if (settings.freq_params.center_freq2)
+-		dfs_range += hostapd_is_dfs_overlap(
+-			iface, bandwidth, settings.freq_params.center_freq2);
+-
+-	if (dfs_range) {
+-		ret = ieee80211_freq_to_chan(settings.freq_params.freq, &chan);
+-		if (ret == NUM_HOSTAPD_MODES) {
+-			wpa_printf(MSG_ERROR,
+-				   "Failed to get channel for (freq=%d, sec_channel_offset=%d, bw=%d)",
+-				   settings.freq_params.freq,
+-				   settings.freq_params.sec_channel_offset,
+-				   settings.freq_params.bandwidth);
+-			return -1;
+-		}
+-
+-		settings.freq_params.channel = chan;
+-
+-		wpa_printf(MSG_DEBUG,
+-			   "DFS/CAC to (channel=%u, freq=%d, sec_channel_offset=%d, bw=%d, center_freq1=%d)",
+-			   settings.freq_params.channel,
+-			   settings.freq_params.freq,
+-			   settings.freq_params.sec_channel_offset,
+-			   settings.freq_params.bandwidth,
+-			   settings.freq_params.center_freq1);
+-
+-		/* Perform CAC and switch channel */
+-		iface->is_ch_switch_dfs = true;
+-		hostapd_switch_channel_fallback(iface, &settings.freq_params);
+-		return 0;
++		freq = settings.freq_params.freq;
++
++	bw_changed = oper_chwidth != hostapd_get_oper_chwidth(iface->conf);
++	state = hostapd_dfs_get_target_state(iface, bandwidth, freq,
++					     settings.freq_params.center_freq2);
++	switch (state) {
++	case HOSTAPD_CHAN_DFS_USABLE:
++		cac_required = true;
++		/* fallthrough */
++	case HOSTAPD_CHAN_DFS_AVAILABLE:
++		background_radar = hostapd_dfs_handle_csa(iface, &settings,
++							  &background_settings,
++							  cac_required,
++							  bw_changed);
++		break;
++	case HOSTAPD_CHAN_DFS_UNAVAILABLE:
++		wpa_printf(MSG_INFO,
++			   "chanswitch: target channel is UNAVAILABLE, so stop switching");
++		return -1;
++	default:
++		break;
+ 	}
+ 
+ 	for (i = 0; i < iface->num_bss; i++) {
+@@ -2944,6 +2939,36 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ #endif /* CONFIG_IEEE80211BE */
+ 	}
+ 
++	if (background_radar) {
++		u8 seg0 = 0, seg1 = 0;
++
++		ieee80211_freq_to_chan(background_settings.freq_params.center_freq1, &seg0);
++		ieee80211_freq_to_chan(background_settings.freq_params.center_freq2, &seg1);
++		ret = hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
++					    background_settings.freq_params.freq,
++					    background_settings.freq_params.channel,
++					    background_settings.freq_params.ht_enabled,
++					    background_settings.freq_params.vht_enabled,
++					    background_settings.freq_params.he_enabled,
++					    background_settings.freq_params.eht_enabled,
++					    background_settings.freq_params.sec_channel_offset,
++					    oper_chwidth, seg0, seg1, true);
++		if (ret) {
++			wpa_printf(MSG_ERROR, "Background radar start dfs cac failed, %d",
++				   ret);
++			iface->radar_background.channel = -1;
++			return -1;
++		}
++
++		/* Cache background radar parameters. */
++		iface->radar_background.channel = background_settings.freq_params.channel;
++		iface->radar_background.secondary_channel =
++			background_settings.freq_params.sec_channel_offset;
++		iface->radar_background.freq = background_settings.freq_params.freq;
++		iface->radar_background.centr_freq_seg0_idx = seg0;
++		iface->radar_background.centr_freq_seg1_idx = seg1;
++	}
++
+ 	return (iface->num_bss == num_err) ? ret : 0;
+ #else /* NEED_AP_MLME */
+ 	return -1;
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 1ffe2fb56..36f4feb3a 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -2794,6 +2794,11 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
+ 		return -1;
+ 	}
+ 
++	if (iface->cac_started) {
++		wpa_printf(MSG_DEBUG, "Ignore set beacons during CAC period");
++		return 0;
++	}
++
+ 	hapd->beacon_set_done = 1;
+ 
+ 	if (ieee802_11_build_ap_params(hapd, &params) < 0)
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index 20d426560..b0ee00b90 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -1099,6 +1099,7 @@ int hostapd_parse_csa_settings(const char *pos,
+ 			       struct csa_settings *settings)
+ {
+ 	char *end;
++	int ret;
+ 
+ 	os_memset(settings, 0, sizeof(*settings));
+ 	settings->cs_count = strtol(pos, &end, 10);
+@@ -1108,7 +1109,9 @@ int hostapd_parse_csa_settings(const char *pos,
+ 	}
+ 
+ 	settings->freq_params.freq = atoi(end);
+-	if (settings->freq_params.freq == 0) {
++	ret = ieee80211_freq_to_chan(settings->freq_params.freq,
++				     (u8 *)&settings->freq_params.channel);
++	if (ret == NUM_HOSTAPD_MODES) {
+ 		wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided");
+ 		return -1;
+ 	}
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 697e6364c..5e9a2a4ce 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -242,22 +242,22 @@ static int is_in_chanlist(struct hostapd_iface *iface,
+  */
+ int dfs_find_channel(struct hostapd_iface *iface,
+ 		     struct hostapd_channel_data **ret_chan,
+-		     int idx, enum dfs_channel_type type)
++		     int n_chans, int idx, enum dfs_channel_type type)
+ {
+ 	struct hostapd_hw_modes *mode;
+ 	struct hostapd_channel_data *chan;
+-	int i, channel_idx = 0, n_chans, n_chans1;
++	int i, channel_idx = 0, n_chans1;
+ 
+ 	mode = iface->current_mode;
+-	n_chans = dfs_get_used_n_chans(iface, &n_chans1);
++	if (!n_chans)
++		n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+ 
+ 	wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
+ 	for (i = 0; i < mode->num_channels; i++) {
+ 		chan = &mode->channels[i];
+ 
+ 		/* Skip HT40/VHT incompatible channels */
+-		if (iface->conf->ieee80211n &&
+-		    iface->conf->secondary_channel &&
++		if (iface->conf->ieee80211n && n_chans > 1 &&
+ 		    (!dfs_is_chan_allowed(chan, n_chans) ||
+ 		     !(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P))) {
+ 			wpa_printf(MSG_DEBUG,
+@@ -550,7 +550,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+ 		return NULL;
+ 
+ 	/* Get the count first */
+-	num_available_chandefs = dfs_find_channel(iface, NULL, 0, type);
++	num_available_chandefs = dfs_find_channel(iface, NULL, 0, 0, type);
+ 	wpa_printf(MSG_DEBUG, "DFS: num_available_chandefs=%d",
+ 		   num_available_chandefs);
+ 	if (num_available_chandefs == 0)
+@@ -573,7 +573,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+ 	chan_idx = _rand % num_available_chandefs;
+ 	wpa_printf(MSG_DEBUG, "DFS: Picked random entry from the list: %d/%d",
+ 		   chan_idx, num_available_chandefs);
+-	dfs_find_channel(iface, &chan, chan_idx, type);
++	dfs_find_channel(iface, &chan, 0, chan_idx, type);
+ 	if (!chan) {
+ 		wpa_printf(MSG_DEBUG, "DFS: no random channel found");
+ 		return NULL;
+@@ -603,7 +603,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+ 		for (i = 0; i < num_available_chandefs - 1; i++) {
+ 			/* start from chan_idx + 1, end when chan_idx - 1 */
+ 			chan_idx2 = (chan_idx + 1 + i) % num_available_chandefs;
+-			dfs_find_channel(iface, &chan2, chan_idx2, type);
++			dfs_find_channel(iface, &chan2, 0, chan_idx2, type);
+ 			if (chan2 && abs(chan2->chan - chan->chan) > 12) {
+ 				/* two channels are not adjacent */
+ 				sec_chan_idx_80p80 = chan2->chan;
+@@ -1367,10 +1367,10 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ 			 * another radio.
+ 			 */
+ 			if (iface->state != HAPD_IFACE_ENABLED &&
+-			    hostapd_is_dfs_chan_available(iface)) {
++			    hostapd_is_dfs_chan_available(iface))
+ 				hostapd_setup_interface_complete(iface, 0);
+-				iface->cac_started = 0;
+-			}
++
++			iface->cac_started = 0;
+ 
+ 			/*
+ 			 * When background radar is enabled but the CAC completion
+@@ -1386,6 +1386,13 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ 		iface->radar_background.temp_ch = 0;
+ 		iface->radar_background.expand_ch = 0;
+ 		hostapd_dfs_update_background_chain(iface);
++	} else if (iface->state == HAPD_IFACE_ENABLED) {
++		int i;
++
++		iface->cac_started = 0;
++		/* Clear all CSA flags once channel switch to DFS channel fails */
++		for (i = 0; i < iface->num_bss; i++)
++			iface->bss[i]->csa_in_progress = 0;
+ 	}
+ 
+ 	iface->radar_detected = false;
+@@ -1410,6 +1417,9 @@ int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
+ 	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+ 		      cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
+ 
++	if (dfs_use_radar_background(iface) && iface->radar_background.channel == -1)
++		hostapd_dfs_update_background_chain(iface);
++
+ 	return 0;
+ }
+ 
+@@ -1798,7 +1808,8 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ 		/* This is called when the driver indicates that an offloaded
+ 		 * DFS has started CAC. radar_detected might be set for previous
+ 		 * DFS channel. Clear it for this new CAC process. */
+-		hostapd_set_state(iface, HAPD_IFACE_DFS);
++		if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
++			hostapd_set_state(iface, HAPD_IFACE_DFS);
+ 		iface->cac_started = 1;
+ 
+ 		/* Clear radar_detected in case it is for the previous
+@@ -1866,14 +1877,15 @@ int hostapd_handle_dfs_offload(struct hostapd_iface *iface)
+ }
+ 
+ 
+-int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
+-			   int center_freq)
++int hostapd_dfs_get_target_state(struct hostapd_iface *iface, enum chan_width width,
++				 int center_freq, int center_freq2)
+ {
+ 	struct hostapd_channel_data *chan;
+ 	struct hostapd_hw_modes *mode = iface->current_mode;
+-	int half_width;
+-	int res = 0;
++	int half_width, chan_state, state = 0;
++	int upper, lower;
+ 	int i;
++	bool in_range;
+ 
+ 	if (!iface->conf->ieee80211h || !mode ||
+ 	    mode->mode != HOSTAPD_MODE_IEEE80211A)
+@@ -1906,18 +1918,136 @@ int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
+ 		if (!(chan->flag & HOSTAPD_CHAN_RADAR))
+ 			continue;
+ 
+-		if ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+-		    HOSTAPD_CHAN_DFS_AVAILABLE)
+-			continue;
++		upper = chan->freq + half_width;
++		lower = chan->freq - half_width;
++		in_range = (lower < center_freq && center_freq < upper) ||
++			   (center_freq2 && (lower < center_freq2 && center_freq2 < upper));
++		if (in_range) {
++			chan_state = chan->flag & HOSTAPD_CHAN_DFS_MASK;
++			switch (chan_state) {
++			case HOSTAPD_CHAN_DFS_USABLE:
++				state = HOSTAPD_CHAN_DFS_USABLE;
++				break;
++			case HOSTAPD_CHAN_DFS_AVAILABLE:
++				if (state != HOSTAPD_CHAN_DFS_USABLE)
++					state = HOSTAPD_CHAN_DFS_AVAILABLE;
++				break;
++			case HOSTAPD_CHAN_DFS_UNKNOWN:
++				wpa_printf(MSG_WARNING, "chan %d DFS state: UNKNOWN",
++					   chan->freq);
++				/* fallthrough */
++			case HOSTAPD_CHAN_DFS_UNAVAILABLE:
++			default:
++				return HOSTAPD_CHAN_DFS_UNAVAILABLE;
++			}
++		}
++	}
+ 
+-		if (center_freq - chan->freq < half_width &&
+-		    chan->freq - center_freq < half_width)
+-			res++;
++	wpa_printf(MSG_DEBUG, "freq range (%d, %d) has DFS state %d",
++		   center_freq - half_width, center_freq + half_width, state);
++
++	return state;
++}
++
++
++static struct hostapd_channel_data *
++dfs_get_csa_channel(struct hostapd_iface *iface,
++		    int n_chans, int cur_center,
++		    enum dfs_channel_type type)
++{
++	struct hostapd_channel_data *chan;
++	int avail_chan_num;
++	u32 _rand, idx;
++
++	if (os_get_random((u8 *)&_rand, sizeof(_rand)) < 0)
++		return NULL;
++
++	avail_chan_num = dfs_find_channel(iface, NULL, n_chans, 0, type);
++	if (!avail_chan_num)
++		return NULL;
++
++	idx = _rand % avail_chan_num;
++	dfs_find_channel(iface, &chan, n_chans, idx, type);
++	if (cur_center == chan->freq + (n_chans - 1) * 10) {
++		if (avail_chan_num == 1)
++			return NULL;
++
++		/* Get the next channel if the found channel is same as current channel */
++		idx = (idx + 1) % avail_chan_num;
++		dfs_find_channel(iface, &chan, n_chans, idx, type);
+ 	}
+ 
+-	wpa_printf(MSG_DEBUG, "DFS CAC required: (%d, %d): in range: %s",
+-		   center_freq - half_width, center_freq + half_width,
+-		   res ? "yes" : "no");
++	return chan;
++}
++
+ 
+-	return res;
++/*
++ * DFS handler for CSA
++ * 1  - update background radar with the filled setting
++ * 0  - background radar is not enabled / background radar remain at the same channel /
++ *	disable background radar
++ */
++int hostapd_dfs_handle_csa(struct hostapd_iface *iface,
++			   struct csa_settings *settings,
++			   struct csa_settings *background_settings,
++			   bool cac_required, bool bw_changed)
++{
++	struct hostapd_channel_data *chan;
++	struct hostapd_freq_params *freq_params = &settings->freq_params;
++	int center = settings->freq_params.center_freq1;
++	int background_center = 5000 + iface->radar_background.centr_freq_seg0_idx * 5;
++	int n_chans = settings->freq_params.bandwidth / 20;
++	bool update_background = false;
++
++	if (!dfs_use_radar_background(iface)) {
++		settings->cs_count = 5;
++		settings->block_tx = cac_required;
++		return 0;
++	}
++
++	if (!cac_required) {
++		if (!bw_changed && center != background_center)
++			return 0;
++		/* Update background radar due to bw change or channel overlapping */
++		update_background = true;
++	} else {
++		/*
++		 * Get available channel for main channel if background radar
++		 * is ready (no CAC in progress).
++		 * If no available channel exists or background radar is not ready,
++		 * then perform the CAC of the target channel on the main channel.
++		 * Also, select an usable channel for background radar if no
++		 * available channel exists.
++		 */
++		if (!iface->radar_background.cac_started) {
++			iface->radar_background.temp_ch = 1;
++			chan = dfs_get_csa_channel(iface, n_chans, 0, DFS_AVAILABLE);
++			if (!chan)
++				update_background = true;
++		} else {
++			iface->radar_background.temp_ch = 0;
++			return 0;
++		}
++	}
++
++	if (update_background) {
++		chan = dfs_get_csa_channel(iface, n_chans, center, DFS_NO_CAC_YET);
++		if (!chan)
++			goto bkg_disable;
++		freq_params = &background_settings->freq_params;
++		iface->radar_background.temp_ch = 0;
++	}
++
++	memcpy(background_settings, settings, sizeof(*settings));
++	freq_params->freq = chan->freq;
++	freq_params->channel = chan->chan;
++	freq_params->sec_channel_offset = 1;
++	freq_params->center_freq1 = chan->freq + (n_chans - 1) * 10;
++	freq_params->center_freq2 = 0;
++
++	return 1;
++
++bkg_disable:
++	iface->radar_background.channel = -1;
++	return 0;
+ }
+diff --git a/src/ap/dfs.h b/src/ap/dfs.h
+index a1a2be5ec..adb09deaf 100644
+--- a/src/ap/dfs.h
++++ b/src/ap/dfs.h
+@@ -42,16 +42,21 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ 			  int ht_enabled, int chan_offset, int chan_width,
+ 			  int cf1, int cf2);
+ int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
+-int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
+-			   int center_freq);
++int hostapd_dfs_get_target_state(struct hostapd_iface *iface, enum chan_width width,
++				 int center_freq, int center_freq2);
+ int dfs_find_channel(struct hostapd_iface *iface,
+ 		     struct hostapd_channel_data **ret_chan,
+-		     int idx, enum dfs_channel_type type);
++		     int n_chans, int idx, enum dfs_channel_type type);
+ void dfs_adjust_center_freq(struct hostapd_iface *iface,
+ 			    struct hostapd_channel_data *chan,
+ 			    int secondary_channel,
+ 			    int sec_chan_idx_80p80,
+ 			    u8 *oper_centr_freq_seg0_idx,
+ 			    u8 *oper_centr_freq_seg1_idx);
++int hostapd_dfs_handle_csa(struct hostapd_iface *iface,
++			   struct csa_settings *settings,
++			   struct csa_settings *background_settings,
++			   bool cac_required, bool bw_changed);
++
+ 
+ #endif /* DFS_H */
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 09f033e13..7dac7a896 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -6382,6 +6382,11 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+ 		return 1;
+ 	}
+ 
++	if (hapd->iface->cac_started) {
++		wpa_printf(MSG_DEBUG, "MGMT: Ignore management frame during CAC");
++		return 1;
++	}
++
+ 	if (stype == WLAN_FC_STYPE_PROBE_REQ) {
+ 		handle_probe_req(hapd, mgmt, len, ssi_signal);
+ 		return 1;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0100-Revert-AP-MLD-Add-MLO-Link-KDE-for-each-affiliated-l.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0100-Revert-AP-MLD-Add-MLO-Link-KDE-for-each-affiliated-l.patch
new file mode 100644
index 0000000..9fcefe2
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0100-Revert-AP-MLD-Add-MLO-Link-KDE-for-each-affiliated-l.patch
@@ -0,0 +1,49 @@
+From b5bf8726bf81b90d628dd398579309fef5b6651b Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 6 Jun 2024 22:11:45 +0800
+Subject: [PATCH 100/126] Revert "AP MLD: Add MLO Link KDE for each affiliated
+ link in EAPOL-Key 3/4"
+
+This reverts commit df59880042cd8d9b4bdd2dce6de0a6e233be1b64.
+
+Please noted that this commit is a workaround for MTK STA IoT issue within WiFi7 R1 certification.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+
+---
+ src/ap/wpa_auth.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
+index 3a1d288dd..6cb5a4be7 100644
+--- a/src/ap/wpa_auth.c
++++ b/src/ap/wpa_auth.c
+@@ -4484,6 +4484,12 @@ static size_t wpa_auth_ml_kdes_len(struct wpa_state_machine *sm)
+ 		struct wpa_authenticator *wpa_auth;
+ 		const u8 *ie;
+ 
++		/* FIXME: This is a temporary workaround for MTK
++		 * sta IoT issue in WiFi7 cert.
++		 */
++		if (!sm->mld_links[link_id].valid)
++			continue;
++
+ 		wpa_auth = wpa_get_link_auth(sm->wpa_auth, link_id);
+ 		if (!wpa_auth)
+ 			continue;
+@@ -4548,6 +4554,12 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
+ 		const u8 *rsne, *rsnxe, *rsnoe, *rsno2e, *rsnxoe;
+ 		size_t rsne_len, rsnxe_len, rsnoe_len, rsno2e_len, rsnxoe_len;
+ 
++		/* FIXME: This is a temporary workaround for MTK
++		 * sta IoT issue in WiFi7 cert.
++		 */
++		if (!sm->mld_links[link_id].valid)
++			continue;
++
+ 		wpa_auth = wpa_get_link_auth(sm->wpa_auth, link_id);
+ 		if (!wpa_auth)
+ 			continue;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0100-mtk-hostapd-MLD-find-partner-links-by-BSSID-and-SSID.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0100-mtk-hostapd-MLD-find-partner-links-by-BSSID-and-SSID.patch
deleted file mode 100644
index 7e2c70b..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0100-mtk-hostapd-MLD-find-partner-links-by-BSSID-and-SSID.patch
+++ /dev/null
@@ -1,89 +0,0 @@
-From 30cfdaf10a5b3bb2a173a096b2c708c4a18d55bc Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Mon, 8 Apr 2024 14:34:36 +0800
-Subject: [PATCH 100/104] mtk: hostapd: MLD: find partner links by BSSID and
- SSID
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- wpa_supplicant/bss.c |  8 ++++++--
- wpa_supplicant/sme.c | 14 ++++++++------
- 2 files changed, 14 insertions(+), 8 deletions(-)
-
-diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
-index 289035310..ae0e61bc3 100644
---- a/wpa_supplicant/bss.c
-+++ b/wpa_supplicant/bss.c
-@@ -1529,8 +1529,12 @@ wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s,
- 			wpa_printf(MSG_DEBUG,
- 				   "MLD: Reported link not part of MLD");
- 		} else if (!(BIT(link_id) & *seen)) {
--			struct wpa_bss *neigh_bss =
--				wpa_bss_get_bssid(wpa_s, pos + 1);
-+			struct wpa_bss *neigh_bss;
-+
-+			if (ssid)
-+				neigh_bss = wpa_bss_get(wpa_s, pos + 1, ssid->ssid, ssid->ssid_len);
-+			else
-+				neigh_bss = wpa_bss_get_bssid(wpa_s, pos + 1);
- 
- 			*seen |= BIT(link_id);
- 			wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link ID=%u",
-diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
-index ef258fadc..0b4b8e3ce 100644
---- a/wpa_supplicant/sme.c
-+++ b/wpa_supplicant/sme.c
-@@ -390,7 +390,8 @@ static void wpas_ml_handle_removed_links(struct wpa_supplicant *wpa_s,
- 
- #ifdef CONFIG_TESTING_OPTIONS
- static struct wpa_bss * wpas_ml_connect_pref(struct wpa_supplicant *wpa_s,
--					     struct wpa_bss *bss)
-+					     struct wpa_bss *bss,
-+					     struct wpa_ssid *ssid)
- {
- 	unsigned int low, high, i;
- 
-@@ -459,7 +460,7 @@ found:
- 		   MAC2STR(wpa_s->links[i].bssid));
- 
- 	/* Get the BSS entry and do the switch */
--	bss = wpa_bss_get_bssid(wpa_s, wpa_s->links[i].bssid);
-+	bss = wpa_bss_get(wpa_s, wpa_s->links[i].bssid, ssid->ssid, ssid->ssid_len);
- 	wpa_s->mlo_assoc_link_id = i;
- 
- 	return bss;
-@@ -528,7 +529,7 @@ static bool check_mld_allowed_phy(struct wpa_supplicant *wpa_s, int freq)
- 
- 
- static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
--				   struct wpa_bss *bss)
-+				   struct wpa_bss *bss, struct wpa_ssid *ssid)
- {
- 	u8 i;
- 
-@@ -551,7 +552,8 @@ static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
- 		if (bss->mld_link_id == i)
- 			wpa_s->links[i].bss = bss;
- 		else
--			wpa_s->links[i].bss = wpa_bss_get_bssid(wpa_s, bssid);
-+			wpa_s->links[i].bss = wpa_bss_get(wpa_s, bssid, ssid->ssid,
-+							  ssid->ssid_len);
- 	}
- }
- 
-@@ -597,10 +599,10 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
- #endif /* CONFIG_TESTING_OPTIONS */
- 	    bss->valid_links) {
- 		wpa_printf(MSG_DEBUG, "MLD: In authentication");
--		wpas_sme_set_mlo_links(wpa_s, bss);
-+		wpas_sme_set_mlo_links(wpa_s, bss, ssid);
- 
- #ifdef CONFIG_TESTING_OPTIONS
--		bss = wpas_ml_connect_pref(wpa_s, bss);
-+		bss = wpas_ml_connect_pref(wpa_s, bss, ssid);
- 
- 		if (wpa_s->conf->mld_force_single_link) {
- 			wpa_printf(MSG_DEBUG, "MLD: Force single link");
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0101-mtk-hostapd-MLD-hostapd-add-support-for-basic-MLD-Ex.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0101-mtk-hostapd-MLD-hostapd-add-support-for-basic-MLD-Ex.patch
deleted file mode 100644
index 08814ed..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0101-mtk-hostapd-MLD-hostapd-add-support-for-basic-MLD-Ex.patch
+++ /dev/null
@@ -1,247 +0,0 @@
-From 0cc6f925c0a97da331f41462a07c4ae4b6698409 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Tue, 2 Apr 2024 15:36:29 +0800
-Subject: [PATCH 101/104] mtk: hostapd: MLD: hostapd: add support for basic MLD
- Extender
-
-Add basic MLD Extender support, including
-1. Extender STA stops all Extender AP's links before scaning.
-2. After finishing the connection with root AP, Extender STA
-   synchronizes control channel with each link on the Extender
-   AP.
-
-Advanced support includes BW cynchronization and channel switch.
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/ucode.c         | 52 ++++++++++++++++++++++++++++++++++++++----
- src/utils/ucode.c      |  4 +++-
- wpa_supplicant/ucode.c | 45 +++++++++++++++++++++++++++++-------
- 3 files changed, 88 insertions(+), 13 deletions(-)
-
-diff --git a/src/ap/ucode.c b/src/ap/ucode.c
-index 98b2a3bf2..2642e87c7 100644
---- a/src/ap/ucode.c
-+++ b/src/ap/ucode.c
-@@ -487,14 +487,16 @@ static uc_value_t *
- uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
- {
- 	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
-+	struct hostapd_data *first_hapd;
-+	struct hostapd_bss_config *conf;
- 	int i;
- 
--	wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
--			iface->phy, hostapd_state_text(iface->state));
--
- 	if (!iface)
- 		return NULL;
- 
-+	wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
-+			iface->phy, hostapd_state_text(iface->state));
-+
- 	if (iface->state != HAPD_IFACE_ENABLED)
- 		uc_hostapd_disable_iface(iface);
- 
-@@ -505,6 +507,32 @@ uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
- 		hapd->beacon_set_done = 0;
- 	}
- 
-+#ifdef CONFIG_IEEE80211BE
-+	first_hapd = iface->bss[0];
-+	conf = first_hapd->conf;
-+	for (i = 0; conf->mld_ap && i < iface->interfaces->count; i++) {
-+		struct hostapd_iface *h = iface->interfaces->iface[i];
-+		struct hostapd_data *h_hapd = h->bss[0];
-+		struct hostapd_bss_config *hconf = h_hapd->conf;
-+
-+		if (h == iface) {
-+			wpa_printf(MSG_DEBUG, "MLD: Skip own interface");
-+			continue;
-+		}
-+
-+		if (!hconf->mld_ap) {
-+			wpa_printf(MSG_DEBUG,
-+				   "MLD: Skip non MLD");
-+			continue;
-+		}
-+
-+		if (hostapd_is_ml_partner(first_hapd, h_hapd)) {
-+			hostapd_drv_stop_ap(h_hapd);
-+			h_hapd->beacon_set_done = 0;
-+		}
-+	}
-+#endif /* CONFIG_IEEE80211BE */
-+
- 	return NULL;
- }
- 
-@@ -512,11 +540,12 @@ static uc_value_t *
- uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
- {
- 	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
-+	struct hostapd_data *tmp_hapd;
- 	uc_value_t *info = uc_fn_arg(0);
- 	struct hostapd_config *conf;
- 	bool changed = false;
- 	uint64_t intval;
--	int i;
-+	int i, band_idx;
- 
- 	wpa_printf(MSG_INFO, "ucode: mtk: start iface for %s in state %s\n",
- 			iface->phy, hostapd_state_text(iface->state));
-@@ -532,6 +561,21 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
- 	if (ucv_type(info) != UC_OBJECT)
- 		return NULL;
- 
-+	intval = ucv_int64_get(ucv_object_get(info, "band_idx", NULL));
-+	if (!errno)
-+		band_idx = intval;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	if (hostapd_is_mld_ap(iface->bss[0])) {
-+		for_each_mld_link(tmp_hapd, iface->bss[0]) {
-+			if (band_idx == tmp_hapd->iconf->band_idx) {
-+				iface = tmp_hapd->iface;
-+				break;
-+			}
-+		}
-+	}
-+#endif /* CONFIG_IEEE80211BE */
-+
- #define UPDATE_VAL(field, name)							\
- 	if ((intval = ucv_int64_get(ucv_object_get(info, name, NULL))) &&	\
- 		!errno && intval != conf->field) do {				\
-diff --git a/src/utils/ucode.c b/src/utils/ucode.c
-index 6f82382f3..81d472f6b 100644
---- a/src/utils/ucode.c
-+++ b/src/utils/ucode.c
-@@ -110,7 +110,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- 	uc_value_t *freq = uc_fn_arg(0);
- 	uc_value_t *sec = uc_fn_arg(1);
- 	int width = ucv_uint64_get(uc_fn_arg(2));
--	int bw320_offset = 1;
-+	int bw320_offset = 1, band_idx;
- 	int freq_val, center_idx, center_ofs;
- 	enum oper_chan_width chanwidth;
- 	enum hostapd_hw_mode hw_mode;
-@@ -123,6 +123,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- 		return NULL;
- 
- 	freq_val = ucv_int64_get(freq);
-+	band_idx = ucv_int64_get(uc_fn_arg(4));
- 	if (ucv_type(sec) == UC_INTEGER)
- 		sec_channel = ucv_int64_get(sec);
- 	else if (sec)
-@@ -183,6 +184,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- 	ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
- 	ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
- 	ucv_object_add(ret, "oper_chwidth", ucv_int64_new(chanwidth));
-+	ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
- 
- 	if (chanwidth == CONF_OPER_CHWIDTH_USE_HT && !sec_channel) {
- 		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(channel));
-diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
-index 542ca25c9..ac0639a90 100644
---- a/wpa_supplicant/ucode.c
-+++ b/wpa_supplicant/ucode.c
-@@ -246,12 +246,13 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
- {
- 	struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
- 	struct wpa_bss *bss;
--	uc_value_t *ret, *val;
-+	uc_value_t *ret, *val, *link_obj = uc_fn_arg(0);
- 	struct wpa_channel_info ci;
- 	u8 op_class, channel;
--	enum oper_chan_width ch_width;
--	int center_freq1, bw320_offset = 1, is_24ghz;
-+	enum oper_chan_width ch_width = CONF_OPER_CHWIDTH_USE_HT;
-+	int center_freq1, bw320_offset = 1, is_24ghz, band_idx;
- 	enum hostapd_hw_mode hw_mode;
-+	int link_id = ucv_int64_get(link_obj);
- 
- 	if (!wpa_s)
- 		return NULL;
-@@ -261,13 +262,25 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
- 	val = ucv_string_new(wpa_supplicant_state_txt(wpa_s->wpa_state));
- 	ucv_object_add(ret, "state", ucv_get(val));
- 
--	bss = wpa_s->current_bss;
-+	bss = link_id == -1 ? wpa_s->current_bss : wpa_s->links[link_id].bss;
- 	if (bss) {
- 		int sec_chan = 0;
- 
- 		hw_mode = ieee80211_freq_to_chan(bss->freq, &channel);
- 		is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
- 			hw_mode == HOSTAPD_MODE_IEEE80211B;
-+		/*
-+		 * Assume that the mapping between band and band_idx is
-+		 * 2 GHz band: band_idx 0
-+		 * 5 GHz band: band_idx 1
-+		 * 6 GHz band: band_idx 2
-+		 * */
-+		if (is_24ghz)
-+			band_idx = 0;
-+		else if (IS_5GHZ(bss->freq))
-+			band_idx = 1;
-+		else if (is_6ghz_freq(bss->freq))
-+			band_idx = 2;
- 
- 		wpa_drv_channel_info(wpa_s, &ci);
- 		center_freq1 = ci.center_frq1;
-@@ -279,11 +292,10 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
- 				sec_chan = (bss->freq / 20) & 1 ? 1 : -1;
- 		}
- 
--		if (ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
--						  sec_chan, &op_class, &channel))
--			return NULL;
-+		if (!ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
-+						   sec_chan, &op_class, &channel))
-+			ch_width = op_class_to_ch_width(op_class);
- 
--		ch_width = op_class_to_ch_width(op_class);
- 		if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
- 		    (center_freq1 == 6265) || center_freq1 == 6585 ||
- 		     center_freq1 == 6905) {
-@@ -295,6 +307,7 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
- 		ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
- 		ucv_object_add(ret, "ch_width", ucv_int64_new(ch_width));
- 		ucv_object_add(ret, "bw320_offset", ucv_int64_new(bw320_offset));
-+		ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
- 	}
- 
- #ifdef CONFIG_MESH
-@@ -309,6 +322,21 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
- 	return ret;
- }
- 
-+static uc_value_t *
-+uc_wpas_iface_get_valid_links(uc_vm_t *vm, size_t nargs)
-+{
-+	struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
-+	uc_value_t *ret;
-+
-+	if (!wpa_s)
-+		return NULL;
-+
-+	ret = ucv_object_new(vm);
-+	ucv_object_add(ret, "valid_links", ucv_uint64_new(wpa_s->valid_links));
-+
-+	return ret;
-+}
-+
- int wpas_ucode_init(struct wpa_global *gl)
- {
- 	static const uc_function_list_t global_fns[] = {
-@@ -320,6 +348,7 @@ int wpas_ucode_init(struct wpa_global *gl)
- 	};
- 	static const uc_function_list_t iface_fns[] = {
- 		{ "status", uc_wpas_iface_status },
-+		{ "get_valid_links", uc_wpas_iface_get_valid_links },
- 	};
- 	uc_value_t *data, *proto;
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0101-mtk-hostapd-Temporary-non-inheritance-IE-solution.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0101-mtk-hostapd-Temporary-non-inheritance-IE-solution.patch
new file mode 100644
index 0000000..2759e8e
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0101-mtk-hostapd-Temporary-non-inheritance-IE-solution.patch
@@ -0,0 +1,213 @@
+From 097f4b42d450ba1ae1f9acbb52cbae5f061d2ded Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Mon, 20 May 2024 17:29:36 +0800
+Subject: [PATCH 101/126] mtk: hostapd: Temporary non-inheritance IE solution
+
+Remove MBSSID IE and FILS indication IE in per-STA profile
+The patch append non-inheritance IE in per-STA profile of a ML IE.
+To add new IE in non-inheritance IE, just append the tag to IE list.
+
+Fix the EHT-4.6.1_RUN1_ITER2 (2G+5G) BRCM assoc issue.
+Without this patch, if the AP is an AP MLD 2G+5G (with 5G as the Setup link), the BRCM station will only connect to the AP using one link (i.e., the per-station profile count in the Association request is 0).
+
+Note: Regardless of whether this patch is applied, EHT-4.6.1_RUN1_ITER1 (2G+5G, with 2G as the setup link) can pass.
+
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ src/ap/beacon.c         | 28 +++++++++++------
+ src/ap/ieee802_11.c     |  2 ++
+ src/ap/ieee802_11.h     |  2 ++
+ src/ap/ieee802_11_eht.c | 68 ++++++++++++++++++++++++++++++++++++++++-
+ 4 files changed, 90 insertions(+), 10 deletions(-)
+
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 36f4feb3a..50d45d532 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -766,15 +766,20 @@ static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd,
+ 			buflen += hostapd_eid_eht_ml_beacon_len(
+ 				ml_elem_ap, params->mld_info, !!params->mld_ap);
+ 		}
++		/* non-inheritance element */
++		if (params->is_ml_sta_info)
++			buflen += hostapd_eid_non_inheritance_len(hapd);
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+-	buflen += hostapd_eid_mbssid_len(hapd, WLAN_FC_STYPE_PROBE_RESP, NULL,
+-					 params->known_bss,
+-					 params->known_bss_len, NULL);
+-	if (!params->is_ml_sta_info)
++	if (!params->is_ml_sta_info) {
++		buflen += hostapd_eid_mbssid_len(hapd, WLAN_FC_STYPE_PROBE_RESP, NULL,
++						 params->known_bss,
++						 params->known_bss_len, NULL);
+ 		buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP,
+ 					      true);
++	}
++
+ 	buflen += hostapd_mbo_ie_len(hapd);
+ 	buflen += hostapd_eid_owe_trans_len(hapd);
+ 	buflen += hostapd_eid_dpp_cc_len(hapd);
+@@ -835,9 +840,10 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
+ 
+ 	pos = hostapd_get_rsne(hapd, pos, epos - pos);
+ 	pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
+-	pos = hostapd_eid_mbssid(hapd, pos, epos, WLAN_FC_STYPE_PROBE_RESP, 0,
+-				 NULL, params->known_bss, params->known_bss_len,
+-				 NULL, NULL, NULL, 0);
++	if (!params->is_ml_sta_info)
++		pos = hostapd_eid_mbssid(hapd, pos, epos, WLAN_FC_STYPE_PROBE_RESP, 0,
++					 NULL, params->known_bss, params->known_bss_len,
++					 NULL, NULL, NULL, 0);
+ 	pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
+ 	pos = hostapd_get_mde(hapd, pos, epos - pos);
+ 
+@@ -897,10 +903,11 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
+ 	pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
+ 	pos = hostapd_eid_max_chsw_time(hapd, pos);
+ 
+-	if (!params->is_ml_sta_info)
++	if (!params->is_ml_sta_info) {
+ 		pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP,
+ 				      true);
+-	pos = hostapd_eid_fils_indic(hapd, pos, 0);
++		pos = hostapd_eid_fils_indic(hapd, pos, 0);
++	}
+ 	pos = hostapd_get_rsnxe(hapd, pos, epos - pos);
+ 
+ #ifdef CONFIG_IEEE80211AX
+@@ -999,6 +1006,9 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
+ 	}
+ #endif /* CONFIG_TESTING_OPTIONS */
+ 
++	if (params->is_ml_sta_info)
++		pos = hostapd_eid_non_inheritance(hapd, pos);
++
+ 	return pos;
+ }
+ 
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 7dac7a896..fd954b6f5 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -4520,6 +4520,8 @@ static void ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
+ 	p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
+ 	p = hostapd_eid_wmm(hapd, p);
+ 
++	p = hostapd_eid_non_inheritance(hapd, p);
++
+ 	if (hapd->conf->assocresp_elements &&
+ 	    (size_t) (buf + buflen - p) >=
+ 	    wpabuf_len(hapd->conf->assocresp_elements)) {
+diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
+index 18f97890b..2d9adb910 100644
+--- a/src/ap/ieee802_11.h
++++ b/src/ap/ieee802_11.h
+@@ -239,6 +239,7 @@ size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
+ 				 enum ieee80211_op_mode opmode);
+ u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
+ 			   enum ieee80211_op_mode opmode);
++u8 * hostapd_eid_non_inheritance(struct hostapd_data *hapd, u8 *eid);
+ u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid);
+ u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ 		       enum ieee80211_op_mode opmode,
+@@ -252,6 +253,7 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+ 			u8 **elem_offset,
+ 			const u8 *known_bss, size_t known_bss_len, u8 *rnr_eid,
+ 			u8 *rnr_count, u8 **rnr_offset, size_t rnr_len);
++size_t hostapd_eid_non_inheritance_len(struct hostapd_data *hapd);
+ bool hostapd_is_mld_ap(struct hostapd_data *hapd);
+ const char * sae_get_password(struct hostapd_data *hapd,
+ 			      struct sta_info *sta, const char *rx_id,
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index 8fc239f36..63713bc39 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -131,7 +131,6 @@ size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
+ 	return len;
+ }
+ 
+-
+ u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
+ 			   enum ieee80211_op_mode opmode)
+ {
+@@ -284,7 +283,74 @@ u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
+ 	return pos + elen;
+ }
+ 
++u8 mlo_non_inherit_list_6ghz[] = {
++	WLAN_EID_AP_CHANNEL_REPORT,
++	WLAN_EID_HT_CAP,
++	WLAN_EID_HT_OPERATION,
++	WLAN_EID_VHT_CAP,
++	WLAN_EID_VHT_OPERATION,
++};
++
++u8 mlo_non_inherit_list_6ghz_ext[] = {
++};
++
++u8 mlo_non_inherit_list_2_5ghz[] = {
++	WLAN_EID_VHT_CAP,
++	WLAN_EID_VHT_OPERATION,
++	WLAN_EID_TRANSMIT_POWER_ENVELOPE,
++};
++
++u8 mlo_non_inherit_list_2_5ghz_ext[] = {
++	WLAN_EID_EXT_HE_6GHZ_BAND_CAP,
++};
++
++size_t hostapd_eid_non_inheritance_len(struct hostapd_data *hapd)
++{
++	size_t len = 4;
+ 
++	if (is_6ghz_op_class(hapd->iconf->op_class)) {
++		len += sizeof(mlo_non_inherit_list_6ghz);
++		len += sizeof(mlo_non_inherit_list_6ghz_ext);
++	} else {
++		len += sizeof(mlo_non_inherit_list_2_5ghz);
++		len += sizeof(mlo_non_inherit_list_2_5ghz_ext);
++	}
++
++	return len;
++}
++
++u8 * hostapd_eid_non_inheritance(struct hostapd_data *hapd, u8 *eid)
++{
++	u8 *pos = eid, *len_pos;
++	int i;
++
++	*pos++ = WLAN_EID_EXTENSION;
++	len_pos = pos++;
++	*pos++ = WLAN_EID_EXT_NON_INHERITANCE;
++	if (is_6ghz_op_class(hapd->iconf->op_class)) {
++		/* Element ID list */
++		*pos++ = sizeof(mlo_non_inherit_list_6ghz);
++		for (i = 0; i < sizeof(mlo_non_inherit_list_6ghz); i++)
++			*pos++ = mlo_non_inherit_list_6ghz[i];
++
++		/* Element ID Extension list */
++		*pos++ = sizeof(mlo_non_inherit_list_6ghz_ext);
++		for (i = 0; i < sizeof(mlo_non_inherit_list_6ghz_ext); i++)
++			*pos++ = mlo_non_inherit_list_6ghz_ext[i];
++	} else {
++		/* Element ID list */
++		*pos++ = sizeof(mlo_non_inherit_list_2_5ghz);
++		for (i = 0; i < sizeof(mlo_non_inherit_list_2_5ghz); i++)
++			*pos++ = mlo_non_inherit_list_2_5ghz[i];
++
++		/* Element ID Extension list */
++		*pos++ = sizeof(mlo_non_inherit_list_2_5ghz_ext);
++		for (i = 0; i < sizeof(mlo_non_inherit_list_2_5ghz_ext); i++)
++			*pos++ = mlo_non_inherit_list_2_5ghz_ext[i];
++	}
++	*len_pos = pos - (eid + 2);
++	return pos;
++}
+ static bool check_valid_eht_mcs_nss(struct hostapd_data *hapd, const u8 *ap_mcs,
+ 				    const u8 *sta_mcs, u8 mcs_count, u8 map_len)
+ {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0102-mtk-hostapd-Fix-multiple-link-connect-get-pmkid-fail.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0102-mtk-hostapd-Fix-multiple-link-connect-get-pmkid-fail.patch
new file mode 100644
index 0000000..b3d973d
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0102-mtk-hostapd-Fix-multiple-link-connect-get-pmkid-fail.patch
@@ -0,0 +1,50 @@
+From b43473e506400cc142f3d464c621714014ca196c Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Thu, 6 Jun 2024 17:41:56 +0800
+Subject: [PATCH 102/126] mtk: hostapd: Fix multiple link connect get pmkid
+ failed
+
+Store pmkid in each link when receive STA auth.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ src/ap/ieee802_11.c | 15 ++++++++++++---
+ 1 file changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index fd954b6f5..ff0f24aaa 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -945,6 +945,7 @@ static void sae_sme_send_external_auth_status(struct hostapd_data *hapd,
+ 
+ void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
+ {
++	struct hostapd_data *link;
+ #ifndef CONFIG_NO_VLAN
+ 	struct vlan_description vlan_desc;
+ 
+@@ -986,9 +987,17 @@ void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
+ 	crypto_bignum_deinit(sta->sae->peer_commit_scalar_accepted, 0);
+ 	sta->sae->peer_commit_scalar_accepted = sta->sae->peer_commit_scalar;
+ 	sta->sae->peer_commit_scalar = NULL;
+-	wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
+-			       sta->sae->pmk, sta->sae->pmk_len,
+-			       sta->sae->pmkid, sta->sae->akmp);
++	if (hostapd_is_mld_ap(hapd)) {
++		for_each_mld_link(link, hapd) {
++			wpa_auth_pmksa_add_sae(link->wpa_auth, sta->addr,
++					sta->sae->pmk, sta->sae->pmk_len,
++					sta->sae->pmkid, sta->sae->akmp);
++		}
++	} else {
++		wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
++				sta->sae->pmk, sta->sae->pmk_len,
++				sta->sae->pmkid, sta->sae->akmp);
++	}
+ 	sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS);
+ }
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0102-mtk-hostapd-Refactor-static-PP-and-mld-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0102-mtk-hostapd-Refactor-static-PP-and-mld-support.patch
deleted file mode 100644
index c958591..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0102-mtk-hostapd-Refactor-static-PP-and-mld-support.patch
+++ /dev/null
@@ -1,336 +0,0 @@
-From 80b2cbbd80700d1fa04d97aae5b8a083a4b4f2ba Mon Sep 17 00:00:00 2001
-From: Allen Ye <allen.ye@mediatek.com>
-Date: Tue, 2 Apr 2024 16:51:07 +0800
-Subject: [PATCH 102/104] mtk: hostapd: Refactor static PP and mld support
-
-Add band_idx attribute in pp cmd for vendor cmd under mld setting.
-
----
- hostapd/config_file.c        |  6 ++--
- hostapd/ctrl_iface.c         | 69 +++++++++++++++++++++++++-----------
- hostapd/ctrl_iface.h         |  3 +-
- hostapd/hostapd_cli.c        | 15 ++++++++
- src/ap/ap_config.h           |  4 +--
- src/ap/ap_drv_ops.c          |  7 ++--
- src/ap/dfs.c                 |  3 ++
- src/ap/hostapd.c             |  4 +--
- src/common/mtk_vendor.h      |  1 +
- src/drivers/driver.h         |  3 +-
- src/drivers/driver_nl80211.c |  4 ++-
- 11 files changed, 87 insertions(+), 32 deletions(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index b9a062193..2add62ca9 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5339,7 +5339,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		if (get_u16(pos, line, &conf->punct_bitmap))
- 			return 1;
- 		conf->punct_bitmap = atoi(pos);
--		conf->pp_mode = PP_MANUAL_MODE;
-+		conf->pp_mode = PP_USR_MODE;
- 	} else if (os_strcmp(buf, "punct_acs_threshold") == 0) {
- 		int val = atoi(pos);
- 
-@@ -5427,8 +5427,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 	} else if (os_strcmp(buf, "pp_mode") == 0) {
- 		int val = atoi(pos);
- 
--		if ((val != PP_MANUAL_MODE && conf->punct_bitmap) ||
--		    val < PP_DISABLE || val > PP_MANUAL_MODE) {
-+		if ((val != PP_USR_MODE && conf->punct_bitmap) ||
-+		    val < PP_DISABLE || val > PP_USR_MODE) {
- 			wpa_printf(MSG_ERROR, "Line %d: invalid pp_mode value",
- 				   line);
- 			return 1;
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index b5f6431bf..6d4ce8acb 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4854,27 +4854,57 @@ hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cm
- 	return os_snprintf(buf, buflen, "OK\n");
- }
- 
-+struct hostapd_data *
-+hostapd_get_hapd_by_band_idx(struct hostapd_data *hapd, u8 band_idx)
-+{
-+	struct hostapd_data *link;
-+
-+	if (!hostapd_is_mld_ap(hapd))
-+		return hapd;
-+
-+	for_each_mld_link(link, hapd) {
-+		if (link->iconf->band_idx == band_idx)
-+			break;
-+	}
-+
-+	if (!link || link->iconf->band_idx != band_idx) {
-+		wpa_printf(MSG_ERROR, "Invalid band idx %d\n", band_idx);
-+		return NULL;
-+	}
-+
-+	return link;
-+}
-+
- static int
- hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
- 			  size_t buflen)
- {
--	char *pos, *config, *value;
-+	char *band, *config, *value;
-+	u8 band_idx;
- 
- 	config = cmd;
--	pos = os_strchr(config, ' ');
--	if (pos == NULL)
-+
-+	value = os_strchr(config, ' ');
-+	if (value == NULL)
- 		return -1;
--	*pos++ = '\0';
-+	*value++ = '\0';
- 
--	if (pos == NULL)
-+	band = os_strchr(value, ' ');
-+	if (band == NULL)
-+		return -1;
-+	*band++ = '\0';
-+	band_idx = strtol(band, NULL, 10);
-+
-+	hapd = hostapd_get_hapd_by_band_idx(hapd, band_idx);
-+
-+	if (!hapd)
- 		return -1;
--	value = pos;
- 
- 	if (os_strcmp(config, "mode") == 0) {
--		int val = atoi(value);
-+		int val = strtol(value, NULL, 10);
- 
--		if (val < PP_DISABLE || val > PP_AUTO_MODE) {
--			wpa_printf(MSG_ERROR, "Invalid value for set_pp");
-+		if (val < PP_DISABLE || val > PP_FW_MODE) {
-+			wpa_printf(MSG_ERROR, "Invalid value for SET_PP");
- 			return -1;
- 		}
- 		hapd->iconf->pp_mode = (u8) val;
-@@ -4882,7 +4912,8 @@ hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
- 			return -1;
- 	} else {
- 		wpa_printf(MSG_ERROR,
--			   "Unsupported parameter %s for set_pp", config);
-+			   "Unsupported parameter %s for SET_PP"
-+			   "Usage: set_pp mode <value> <band_idx>", config);
- 		return -1;
- 	}
- 	return os_snprintf(buf, buflen, "OK\n");
-@@ -4892,19 +4923,17 @@ static int
- hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
- 			  size_t buflen)
- {
--	char *pos, *end;
-+	u8 band_idx;
- 
--	pos = buf;
--	end = buf + buflen;
-+	band_idx = strtol(cmd, NULL, 10);
- 
--	if (os_strcmp(cmd, "mode") == 0) {
--		return os_snprintf(pos, end - pos, "pp_mode: %d\n",
--				   hapd->iconf->pp_mode);
--	} else {
--		wpa_printf(MSG_ERROR,
--			   "Unsupported parameter %s for get_pp", cmd);
-+	hapd = hostapd_get_hapd_by_band_idx(hapd, band_idx);
-+
-+	if (!hapd)
- 		return -1;
--	}
-+
-+	return os_snprintf(buf, buflen, "pp_mode: %d, punct_bitmap: 0x%04x\n",
-+			   hapd->iconf->pp_mode, hapd->iconf->punct_bitmap);
- }
- 
- static int
-diff --git a/hostapd/ctrl_iface.h b/hostapd/ctrl_iface.h
-index 3341a66bd..82a64b880 100644
---- a/hostapd/ctrl_iface.h
-+++ b/hostapd/ctrl_iface.h
-@@ -35,5 +35,6 @@ hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface)
- {
- }
- #endif /* CONFIG_NO_CTRL_IFACE */
--
-+struct hostapd_data *
-+hostapd_get_hapd_by_band_idx(struct hostapd_data *hapd, u8 band_idx);
- #endif /* CTRL_IFACE_H */
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 81b74d6de..4bf70d183 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1730,6 +1730,17 @@ static int hostapd_cli_cmd_dump_csi(struct wpa_ctrl *ctrl, int argc,
- {
- 	return hostapd_cli_cmd(ctrl, "DUMP_CSI", 1, argc, argv);
- }
-+static int hostapd_cli_cmd_set_pp(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "set_pp", 3, argc, argv);
-+}
-+
-+static int hostapd_cli_cmd_get_pp(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "get_pp", 1, argc, argv);
-+}
- 
- struct hostapd_cli_cmd {
- 	const char *cmd;
-@@ -1976,6 +1987,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 		" = Set csi configuaration"},
- 	{ "dump_csi", hostapd_cli_cmd_dump_csi, NULL,
- 		" = Dump csi data to a json file"},
-+	{ "set_pp", hostapd_cli_cmd_set_pp, NULL,
-+		" = Set preamble puncture mode"},
-+	{ "get_pp", hostapd_cli_cmd_get_pp, NULL,
-+		" = Get preamble puncture status"},
- 	{ NULL, NULL, NULL, NULL }
- };
- 
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 3bd8df9ce..5192c1f07 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1353,8 +1353,8 @@ enum mtk_vendor_attr_edcca_ctrl_mode {
- 
- enum pp_mode {
- 	PP_DISABLE = 0,
--	PP_AUTO_MODE,
--	PP_MANUAL_MODE,
-+	PP_FW_MODE,
-+	PP_USR_MODE,
- };
- 
- #define EDCCA_DEFAULT_COMPENSATION -6
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index cb782fa31..a76148eba 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1396,10 +1396,13 @@ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
- int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
- {
- 	if (!hapd->driver || !hapd->driver->pp_mode_set ||
--	    hapd->iconf->pp_mode > PP_AUTO_MODE)
-+	    hapd->iconf->pp_mode >= PP_USR_MODE ||
-+	    hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
- 		return 0;
-+
- 	return hapd->driver->pp_mode_set(hapd->drv_priv,
--					 hapd->iconf->pp_mode);
-+					 hapd->iconf->pp_mode,
-+					 hapd->iconf->band_idx);
- }
- 
- int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index b12290556..9bbeab1d6 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1080,6 +1080,7 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
- 	os_memset(&csa_settings, 0, sizeof(csa_settings));
- 	csa_settings.cs_count = 5;
- 	csa_settings.block_tx = 1;
-+	csa_settings.punct_bitmap = hostapd_get_punct_bitmap(iface->bss[0]);
- 	csa_settings.link_id = -1;
- #ifdef CONFIG_IEEE80211BE
- 	if (iface->bss[0]->conf->mld_ap)
-@@ -1644,6 +1645,8 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
- 			return 0;
- 	}
- 
-+	iface->bss[0]->iconf->punct_bitmap = 0;
-+
- 	if (hostapd_dfs_background_start_channel_switch(iface, freq)) {
- 		/* Radar detected while operating, switch the channel. */
- 		return hostapd_dfs_start_channel_switch(iface);
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 8e3f0b281..3d3359291 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2697,6 +2697,8 @@ dfs_offload:
- 	}
- #endif /* CONFIG_MESH */
- 
-+	if (hostapd_drv_pp_mode_set(hapd) < 0)
-+		goto fail;
- 	if (hostapd_drv_configure_edcca_enable(hapd) < 0)
- 		goto fail;
- 
-@@ -2711,8 +2713,6 @@ dfs_offload:
- 		goto fail;
- 	if (hostapd_drv_amsdu_ctrl(hapd) < 0)
- 		goto fail;
--	if (hostapd_drv_pp_mode_set(hapd) < 0)
--		goto fail;
- 
- 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- 		   iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index c290e72a7..09805b6fb 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -263,6 +263,7 @@ enum mtk_vendor_attr_pp_ctrl {
- 	MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
- 
- 	MTK_VENDOR_ATTR_PP_MODE,
-+	MTK_VENDOR_ATTR_PP_BAND_IDX,
- 
- 	/* keep last */
- 	NUM_MTK_VENDOR_ATTRS_PP_CTRL,
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 7efb5e342..539771729 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5337,8 +5337,9 @@ struct wpa_driver_ops {
- 	 * pp_mode_set - Set preamble puncture operation mode
- 	 * @priv: Private driver interface data
- 	 * @pp_mode: Value is defined in enum pp_mode
-+	 * @band_idx: chip band index
- 	 */
--	int (*pp_mode_set)(void *priv, const u8 pp_mode);
-+	int (*pp_mode_set)(void *priv, const u8 pp_mode, u8 band_idx);
- #ifdef CONFIG_IEEE80211BE
- 	int (*get_mld_addr)(void *priv, u8 *addr);
- #endif
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 39b45ef4b..6ec2c32df 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -159,6 +159,7 @@ amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
- static struct nla_policy
- pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
- 	[MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_PP_BAND_IDX] = { .type = NLA_U8 },
- };
- 
- static struct nla_policy csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
-@@ -15167,7 +15168,7 @@ static int nl80211_background_radar_mode(void *priv, const u8 background_radar_m
- 	return ret;
- }
- 
--static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
-+static int nl80211_pp_mode_set(void *priv, const u8 pp_mode, u8 band_idx)
- {
- 	struct i802_bss *bss = priv;
- 	struct wpa_driver_nl80211_data *drv = bss->drv;
-@@ -15194,6 +15195,7 @@ static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
- 	if (!data)
- 		goto fail;
- 
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_PP_BAND_IDX, band_idx);
- 	nla_put_u8(msg, MTK_VENDOR_ATTR_PP_MODE, pp_mode);
- 
- 	nla_nest_end(msg, data);
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0103-mtk-hostapd-ensure-only-a-scan-callback-is-called-on.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0103-mtk-hostapd-ensure-only-a-scan-callback-is-called-on.patch
new file mode 100644
index 0000000..7b12f21
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0103-mtk-hostapd-ensure-only-a-scan-callback-is-called-on.patch
@@ -0,0 +1,49 @@
+From 6297062e6c302d2de6888580c2f396da47c95908 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 4 Jun 2024 08:24:32 +0800
+Subject: [PATCH 103/126] mtk: hostapd: ensure only a scan callback is called
+ on each scan result event
+
+Every scan callback is assigned along with a scan request to driver.
+Therefore on each scan result event, there should be only one scan
+callback to be handled.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/drv_callbacks.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 705acfb67..1b91232f2 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2493,10 +2493,12 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ 			hapd = switch_link_scan(hapd,
+ 						data->scan_info.scan_cookie);
+ #endif /* NEED_AP_MLME */
+-		if (hapd->iface->scan_cb)
++		if (hapd->iface->scan_cb) {
+ 			hapd->iface->scan_cb(hapd->iface);
++			break;
++		}
+ #ifdef CONFIG_IEEE80211BE
+-		if (!hapd->iface->scan_cb && hapd->conf->mld_ap) {
++		if (hapd->conf->mld_ap) {
+ 			/* Other links may be waiting for HT scan result */
+ 			unsigned int i;
+ 
+@@ -2506,8 +2508,10 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ 				struct hostapd_data *h_hapd = h->bss[0];
+ 
+ 				if (hostapd_is_ml_partner(hapd, h_hapd) &&
+-				    h_hapd->iface->scan_cb)
++				    h_hapd->iface->scan_cb) {
+ 					h_hapd->iface->scan_cb(h_hapd->iface);
++					break;
++				}
+ 			}
+ 		}
+ #endif /* CONFIG_IEEE80211BE */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0103-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0103-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch
deleted file mode 100644
index cca2e9a..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0103-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch
+++ /dev/null
@@ -1,124 +0,0 @@
-From ec7d47b2566da59c52d995f5e404a1c00e746fe5 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 11 Apr 2024 18:16:38 +0800
-Subject: [PATCH 103/104] mtk: hostapd: make sure all links are set before
- enabling beacon
-
-NL80211_CMD_NEW_BEACON will first be set, but we've modified mac80211 to
-disable this beacon. After that, hostapd will block
-NL80211_CMD_SET_BEACON until all links are setting up.
-(use NL80211_CMD_START_AP event to check if all expected links are enabled)
-
-Update: in wpa_driver_nl80211_set_ap(), send_and_recv() is used, implies
-that hostapd should already sync with driver, so don't need to use
-NL80211_CMD_START_AP event.
-
-This can make sure that the first beacon of each link includes the
-correct RNR and per-STA profile.
-
-Note that in NL80211_CMD_NEW_BEACON, we also set beacon interval to 0,
-which helps to bypass some mac80211 beacon active checks, e.g., during ACS.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- hostapd/config_file.c |  2 ++
- src/ap/ap_config.h    |  2 ++
- src/ap/beacon.c       | 10 ++++++++++
- src/ap/hostapd.c      | 14 ++++++++++++++
- src/ap/hostapd.h      |  1 +
- 5 files changed, 29 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 2add62ca9..8abe1bc46 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5354,6 +5354,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		bss->mld_ap = !!atoi(pos);
- 	} else if (os_strcmp(buf, "mld_primary") == 0) {
- 		bss->mld_primary = !!atoi(pos);
-+	} else if (os_strcmp(buf, "mld_allowed_links") == 0) {
-+		bss->mld_allowed_links = atoi(pos);
- 	} else if (os_strcmp(buf, "mld_addr") == 0) {
- 		if (hwaddr_aton(pos, bss->mld_addr)) {
- 			wpa_printf(MSG_ERROR, "Line %d: Invalid mld_addr",
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 5192c1f07..0ea5a04e2 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -968,6 +968,8 @@ struct hostapd_bss_config {
- 
- 	/* The AP is the primary AP of an AP MLD */
- 	u8 mld_primary;
-+	/* Allowed link bitmap of the AP MLD to which the AP is affiliated */
-+	u16 mld_allowed_links;
- 
- 	/* The MLD ID to which the AP MLD is affiliated with */
- 	u8 mld_id;
-diff --git a/src/ap/beacon.c b/src/ap/beacon.c
-index a5c46b067..a5c9dd87e 100644
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -2164,6 +2164,12 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
- 	os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
- 	head->u.beacon.beacon_int =
- 		host_to_le16(hapd->iconf->beacon_int);
-+	/* if MLD AP hasn't finished setting up all links, also set beacon interval
-+	 * to 0. This allows mac80211 to bypass some beacon active checks, for
-+	 * example, when doing ACS
-+	 */
-+	if (hapd->conf->mld_ap && !hapd->mld->started)
-+		head->u.beacon.beacon_int = host_to_le16(0);
- 
- 	/* hardware or low-level driver will setup seq_ctrl and timestamp */
- 	capab_info = hostapd_own_capab_info(hapd);
-@@ -2553,6 +2559,10 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
- 	int res, ret = -1, i;
- 	struct hostapd_hw_modes *mode;
- 
-+	/* skip setting beacon if other links are not started yet */
-+	if (hapd->conf->mld_ap && !hapd->mld->started && hapd->beacon_set_done)
-+		return 0;
-+
- 	if (!hapd->drv_priv) {
- 		wpa_printf(MSG_ERROR, "Interface is disabled");
- 		return -1;
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 3d3359291..c31e0badd 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -1314,6 +1314,20 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
- 	if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
- 		return -1;
- 
-+	if (hapd->conf->mld_ap && !hapd->mld->started) {
-+		struct hostapd_data *p_hapd;
-+		u16 valid_links = 0;
-+
-+		for_each_mld_link(p_hapd, hapd)
-+			valid_links |= BIT(p_hapd->mld_link_id);
-+
-+		if (valid_links == hapd->conf->mld_allowed_links ||
-+		    !hapd->conf->mld_allowed_links) {
-+			hapd->mld->started = 1;
-+			ieee802_11_set_beacon(hapd);
-+		}
-+	}
-+
- 	if (flush_old_stations && !conf->start_disabled &&
- 	    conf->broadcast_deauth) {
- 		u8 addr[ETH_ALEN];
-diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
-index 5b37be87b..83636649f 100644
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -537,6 +537,7 @@ struct hostapd_mld {
- 	 * freed when num_links is 0.
- 	 */
- 	u8 refcount;
-+	bool started;
- 
- 	struct hostapd_data *fbss;
- 	struct dl_list links; /* List head of all affiliated links */
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0104-mtk-hostapd-add-support-for-get_survey-to-specify-li.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0104-mtk-hostapd-add-support-for-get_survey-to-specify-li.patch
new file mode 100644
index 0000000..6ee64b4
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0104-mtk-hostapd-add-support-for-get_survey-to-specify-li.patch
@@ -0,0 +1,111 @@
+From b424816d5509a5d2137c5e674ca39e03c74d3bdd Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 4 Jun 2024 08:26:04 +0800
+Subject: [PATCH 104/126] mtk: hostapd: add support for get_survey to specify
+ link id
+
+Specifying the link id when request get_survey is useful for passing
+the event to correcy hapd.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ap_drv_ops.h          | 8 +++++++-
+ src/ap/drv_callbacks.c       | 1 +
+ src/drivers/driver.h         | 5 ++++-
+ src/drivers/driver_nl80211.c | 4 +++-
+ 4 files changed, 15 insertions(+), 3 deletions(-)
+
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index ca2efab82..e74284b96 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -316,11 +316,17 @@ static inline void hostapd_drv_poll_client(struct hostapd_data *hapd,
+ static inline int hostapd_drv_get_survey(struct hostapd_data *hapd,
+ 					 unsigned int freq)
+ {
++	int link_id = -1;
++
+ 	if (hapd->driver == NULL)
+ 		return -1;
+ 	if (!hapd->driver->get_survey)
+ 		return -1;
+-	return hapd->driver->get_survey(hapd->drv_priv, freq);
++#ifdef CONFIG_IEEE80211BE
++	if (hapd->conf->mld_ap)
++		link_id = hapd->mld_link_id;
++#endif /* CONFIG_IEEE80211BE */
++	return hapd->driver->get_survey(hapd->drv_priv, freq, link_id);
+ }
+ 
+ static inline int hostapd_get_country(struct hostapd_data *hapd, char *alpha2)
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 1b91232f2..0e7cfd285 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2685,6 +2685,7 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ 			data->connect_failed_reason.code);
+ 		break;
+ 	case EVENT_SURVEY:
++		hapd = switch_link_hapd(hapd, data->survey_results.link_id);
+ 		hostapd_event_get_survey(hapd->iface, &data->survey_results);
+ 		break;
+ #ifdef NEED_AP_MLME
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index eed99dac8..d5d00db9d 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -4642,6 +4642,7 @@ struct wpa_driver_ops {
+ 	 * @priv: Private driver interface data
+ 	 * @freq: If set, survey data for the specified frequency is only
+ 	 *	being requested. If not set, all survey data is requested.
++	 * @link_id: The link ID that requests the get_survey.
+ 	 * Returns: 0 on success, -1 on failure
+ 	 *
+ 	 * Use this to retrieve:
+@@ -4660,7 +4661,7 @@ struct wpa_driver_ops {
+ 	 * for each survey. The min_nf of the channel is updated for each
+ 	 * survey.
+ 	 */
+-	int (*get_survey)(void *priv, unsigned int freq);
++	int (*get_survey)(void *priv, unsigned int freq, int link_id);
+ 
+ 	/**
+ 	 * status - Get driver interface status information
+@@ -6851,10 +6852,12 @@ union wpa_event_data {
+ 	 * @freq_filter: Requested frequency survey filter, 0 if request
+ 	 *	was for all survey data
+ 	 * @survey_list: Linked list of survey data (struct freq_survey)
++	 * @link_id: Link ID of the MLO link
+ 	 */
+ 	struct survey_results {
+ 		unsigned int freq_filter;
+ 		struct dl_list survey_list; /* struct freq_survey */
++		int link_id;
+ 	} survey_results;
+ 
+ 	/**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index b3ae50d15..3683f6034 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -10432,7 +10432,8 @@ static int survey_handler(struct nl_msg *msg, void *arg)
+ }
+ 
+ 
+-static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq)
++static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq,
++					 int link_id)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+@@ -10450,6 +10451,7 @@ static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq)
+ 	if (!msg)
+ 		return -ENOBUFS;
+ 
++	data.survey_results.link_id = link_id;
+ 	if (freq)
+ 		data.survey_results.freq_filter = freq;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0104-mtk-hostapd-ucode-add-is_mld_finished-check.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0104-mtk-hostapd-ucode-add-is_mld_finished-check.patch
deleted file mode 100644
index e1c0274..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0104-mtk-hostapd-ucode-add-is_mld_finished-check.patch
+++ /dev/null
@@ -1,53 +0,0 @@
-From 7b4363892397c667e65fae9e036c83ac039e308d Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Wed, 17 Apr 2024 13:17:59 +0800
-Subject: [PATCH 104/104] mtk: hostapd: ucode: add is_mld_finished check
-
-Add is_mld_finished check for ucode need.
-This function returns ture only if all links fromt all MLD APs are
-ready.
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/ucode.c | 18 ++++++++++++++++++
- 1 file changed, 18 insertions(+)
-
-diff --git a/src/ap/ucode.c b/src/ap/ucode.c
-index 2642e87c7..9f9cc2022 100644
---- a/src/ap/ucode.c
-+++ b/src/ap/ucode.c
-@@ -734,6 +734,23 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
- 	return ucv_boolean_new(!ret);
- }
- 
-+static uc_value_t *
-+uc_hostapd_iface_is_mld_finished(uc_vm_t *vm, size_t nargs)
-+{
-+	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
-+	bool finished = true;
-+	int i;
-+
-+	for (i = 0; i < iface->num_bss; i++) {
-+		if (iface->bss[i]->conf->mld_ap && !iface->bss[i]->mld->started) {
-+			finished = false;
-+			break;
-+		}
-+	}
-+
-+	return ucv_boolean_new(finished);
-+}
-+
- static uc_value_t *
- uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
- {
-@@ -806,6 +823,7 @@ int hostapd_ucode_init(struct hapd_interfaces *ifaces)
- 		{ "stop", uc_hostapd_iface_stop },
- 		{ "start", uc_hostapd_iface_start },
- 		{ "switch_channel", uc_hostapd_iface_switch_channel },
-+		{ "is_mld_finished", uc_hostapd_iface_is_mld_finished },
- 	};
- 	uc_value_t *data, *proto;
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0105-mtk-hostapd-free-station-when-hapd-deinit.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0105-mtk-hostapd-free-station-when-hapd-deinit.patch
deleted file mode 100644
index 81b2015..0000000
--- a/recipes-wifi/hostapd/files/patches-2.10.3/0105-mtk-hostapd-free-station-when-hapd-deinit.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From 95dcf88b68837221ad937fe7c675b169c8034384 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Mon, 22 Apr 2024 08:40:18 +0800
-Subject: [PATCH] mtk: hostapd: free station when hapd deinit
-
-Free all stations in the same MLD when a bss is deinit.
-Without this patch, the AP_VLAN interface may be free after
-stop ap and leads to kernel crash.
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- src/ap/hostapd.c | 14 ++++++++++++++
- 1 file changed, 14 insertions(+)
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index c31e0badd..62943c561 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -842,7 +842,21 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
- 
- void hostapd_bss_deinit_no_free(struct hostapd_data *hapd)
- {
-+#ifdef CONFIG_IEEE80211BE
-+	struct hostapd_data *link;
-+
-+	/* FIXME: free all stations to remvoe AP_VLAN interface. Allocate */
-+	/* per-link structures for AP_VLAN in mac80211 and only remove single */
-+	/* link here. */
-+	if (hostapd_is_mld_ap(hapd)) {
-+		for_each_mld_link(link, hapd)
-+			hostapd_free_stas(link);
-+	} else {
-+		hostapd_free_stas(hapd);
-+	}
-+#else
- 	hostapd_free_stas(hapd);
-+#endif
- 	hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
- #ifdef CONFIG_WEP
- 	hostapd_clear_wep(hapd);
--- 
-2.18.0
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0105-mtk-hostapd-sequentially-conduct-multiple-setup-ACS.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0105-mtk-hostapd-sequentially-conduct-multiple-setup-ACS.patch
new file mode 100644
index 0000000..77a06b3
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0105-mtk-hostapd-sequentially-conduct-multiple-setup-ACS.patch
@@ -0,0 +1,84 @@
+From 93ff9ff205e52c9dd5ac582261c5476ff87198ad Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 4 Jun 2024 08:31:29 +0800
+Subject: [PATCH 105/126] mtk: hostapd: sequentially conduct multiple setup ACS
+
+In the single-wiphy architecture, only one scan request is allowed at
+the same time (no matter the AP is legacy or MLD). However, if multiple
+AP interfaces need setup ACS, multiple scan requests are sent to the
+driver and only one can be accept. Other failed requests lead to
+interface setup failure.
+
+A sequentially conducting for multiple ACS is needed to prevent such a
+failure. It is realized by
+  1. A BSS (link) postpones its ACS initializaion if it detects another
+  active ACS.
+  2. Once the ACS is finished, the BSS (link) starts the ACS initizlization
+  for one of those BSSes (links) waiting for it.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/hostapd.c     | 18 ++++++++++++++++++
+ src/ap/hw_features.c | 10 ++++++++++
+ 2 files changed, 28 insertions(+)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index f03a6242f..63d89e614 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2860,6 +2860,24 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
+ 	unsigned int i;
+ 	int not_ready_in_sync_ifaces = 0;
+ 
++	if (iface->state == HAPD_IFACE_ACS) {
++		int i;
++
++		for (i = 0; i < interfaces->count; i++) {
++			if (!interfaces->iface[i]->freq) {
++				/* FIXME problems remained
++				 * 1. the return value of acs_init() is
++				 *    not check
++				 * 2. if it fails the setup, next acs_init()
++				 *    will not be handled
++				 */
++				wpa_printf(MSG_DEBUG, "mtk: trigger acs_init for %s", interfaces->iface[i]->phy);
++				acs_init(interfaces->iface[i]);
++				break;
++			}
++		}
++	}
++
+ 	if (!iface->need_to_start_in_sync)
+ 		return hostapd_setup_interface_complete_sync(iface, err);
+ 
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index 2a2832cd4..f35832e2e 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -1158,6 +1158,8 @@ int hostapd_determine_mode(struct hostapd_iface *iface)
+ static enum hostapd_chan_status
+ hostapd_check_chans(struct hostapd_iface *iface)
+ {
++	int i;
++
+ 	if (iface->freq) {
+ 		int err;
+ 
+@@ -1177,6 +1179,14 @@ hostapd_check_chans(struct hostapd_iface *iface)
+ 	 * which is used to trigger ACS.
+ 	 */
+ 
++	/*
++	 * Only allow an ACS at one time.
++	 */
++	for (i = 0; i < iface->interfaces->count; i++) {
++		if (iface->interfaces->iface[i]->state == HAPD_IFACE_ACS)
++			return HOSTAPD_CHAN_ACS;
++	}
++
+ 	switch (acs_init(iface)) {
+ 	case HOSTAPD_CHAN_ACS:
+ 		return HOSTAPD_CHAN_ACS;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0106-mtk-hostapd-find-sta_info-for-legacy-STA-associating.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0106-mtk-hostapd-find-sta_info-for-legacy-STA-associating.patch
new file mode 100644
index 0000000..83f53fc
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0106-mtk-hostapd-find-sta_info-for-legacy-STA-associating.patch
@@ -0,0 +1,48 @@
+From ec640d581610ce8d2eac74ee38459bc84ace18ca Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 14 Jun 2024 16:19:46 +0800
+Subject: [PATCH 106/126] mtk: hostapd: find sta_info for legacy STA
+ associating with non-first link when handling rx_from_unknown event
+
+The legacy STA might associates with the AP MLD's non-first link, but
+the 4-addr QoS NULL from STA is still sent to first link's hapd and
+sta_info can not be found, leading to the 4-addr QoS NULL skb being
+dropped.
+
+This commit solves the problem by checking other link's hapd when the
+sta_info is not found in handling rx_from_unknown event.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ieee802_11.c | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index ff0f24aaa..62fdfb288 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7092,6 +7092,21 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+ 	sta = ap_get_sta(hapd, src);
+ 
+ #ifdef CONFIG_IEEE80211BE
++	if (!sta && hapd->conf->mld_ap) {
++		struct hostapd_data *h;
++
++		/* The data frame might be sent by a non-MLD STA via non-first
++		 * link, so we must also check other links.
++		 */
++		for_each_mld_link(h, hapd) {
++			if (h == hapd)
++				continue;
++
++			if (sta = ap_get_sta(h, src))
++				break;
++		}
++	}
++
+ 	if (sta && sta->mld_info.mld_sta)
+ 		sta = sta->mld_assoc_sta;
+ #endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0107-mtk-hostapd-do-not-consider-bss-ignore-list-when-par.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0107-mtk-hostapd-do-not-consider-bss-ignore-list-when-par.patch
new file mode 100644
index 0000000..6634548
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0107-mtk-hostapd-do-not-consider-bss-ignore-list-when-par.patch
@@ -0,0 +1,44 @@
+From 1ed61a27f27d721697c4e0705ce934afa4804605 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Wed, 19 Jun 2024 11:31:09 +0800
+Subject: [PATCH 107/126] mtk: hostapd: do not consider bss ignore list when
+ parsing ML parnter link
+
+By definition, adding BSS into the ignore list only makes wpa_supplicant
+tries the listed BSS with a low priority, but does not prevent the
+listed BSS from being used.
+
+It migth not be suitable to check the ignore list when parsing ML
+partner links. It made wpa_supplicant associate with AP MLD by only
+partial links with the link inside the ignore list being excluded.
+
+After apply this commit, a partner link in the BSS ignore list will
+not be ignored
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/bss.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
+index cf94d4be5..b85b251bf 100644
+--- a/wpa_supplicant/bss.c
++++ b/wpa_supplicant/bss.c
+@@ -1573,9 +1573,12 @@ wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s,
+ 				    (bss_params & (RNR_BSS_PARAM_SAME_SSID |
+ 						   RNR_BSS_PARAM_CO_LOCATED)) ||
+ 				    wpa_scan_res_match(wpa_s, 0, neigh_bss,
+-						       ssid, 1, 0)) &&
++						       ssid, 1, 0))
++#if 0 /* MLD partner link should not be excluded */
+ 				   !wpa_bssid_ignore_is_listed(
+-					   wpa_s, neigh_bss->bssid)) {
++					   wpa_s, neigh_bss->bssid)
++#endif
++				    ) {
+ 				struct mld_link *l;
+ 
+ 				bss->valid_links |= BIT(link_id);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0108-mtk-hostapd-support-vendor-command-trig_type-and-ap_.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0108-mtk-hostapd-support-vendor-command-trig_type-and-ap_.patch
new file mode 100644
index 0000000..bac1e4a
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0108-mtk-hostapd-support-vendor-command-trig_type-and-ap_.patch
@@ -0,0 +1,149 @@
+From 41742b992f23847935411c69d676c8c8813190bf Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Wed, 19 Jun 2024 17:22:41 +0800
+Subject: [PATCH 108/126] mtk: hostapd: support vendor command trig_type and
+ ap_wireless with link_id
+
+For mld ap, we need to add link_id in nl80211 msg so that mt76 driver
+could use link_id to find the corresponding phy.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ src/ap/ap_drv_ops.c          | 18 ++++++++++++++++--
+ src/common/mtk_vendor.h      |  2 ++
+ src/drivers/driver.h         |  6 ++++--
+ src/drivers/driver_nl80211.c | 11 +++++++++--
+ 4 files changed, 31 insertions(+), 6 deletions(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 2d2198a44..8631bf960 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1377,9 +1377,16 @@ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_colo
+ 
+ int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
+ {
++	s8 link_id = -1;
++
+ 	if (!hapd->driver || !hapd->driver->ap_wireless)
+ 		return 0;
+-	return hapd->driver->ap_wireless(hapd->drv_priv, sub_vendor_id, value);
++
++	if (hapd->conf->mld_ap)
++		link_id = hapd->mld_link_id;
++
++	return hapd->driver->ap_wireless(hapd->drv_priv, sub_vendor_id, value,
++					 link_id);
+ }
+ 
+ int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
+@@ -1398,9 +1405,16 @@ int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int va
+ 
+ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type)
+ {
++	s8 link_id = -1;
++
+ 	if (!hapd->driver || !hapd->driver->ap_trigtype)
+ 		return 0;
+-	return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type);
++
++	if (hapd->conf->mld_ap)
++		link_id = hapd->mld_link_id;
++
++	return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type,
++					 link_id);
+ }
+ 
+ int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac)
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index c6de8862b..937b968d5 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -165,6 +165,8 @@ enum mtk_vendor_attr_wireless_ctrl {
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT = 9,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_MU_EDCA,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_LINK_ID,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index d5d00db9d..dacf0a98d 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5334,8 +5334,9 @@ struct wpa_driver_ops {
+ 	* ap_wireless - set wireless command
+ 	* @priv: Private driver interface data
+ 	* @value: value
++	* @link_id: MLD link id. -1 if this is an non-MLD AP.
+ 	*/
+-	int (*ap_wireless)(void *priv, u8 mode, int value);
++	int (*ap_wireless)(void *priv, u8 mode, int value, s8 link_id);
+ 
+ 	/**
+ 	* ap_rfeatures - set ap rf features command
+@@ -5350,8 +5351,9 @@ struct wpa_driver_ops {
+ 	* @priv: Private driver interface data
+ 	* @enable: enable or disable
+ 	* @type: trigger type
++	* @link_id: MLD link id. -1 if this is an non-MLD AP.
+ 	*/
+-	int (*ap_trigtype)(void *priv, u8 enable, u8 type);
++	int (*ap_trigtype)(void *priv, u8 enable, u8 type, s8 link_id);
+ 
+ 	/**
+ 	* amnt_set - add/delete station from monitoring
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 3683f6034..80fe2e591 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -141,6 +141,7 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
+ 	[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE] = {.type = NLA_U16 },
+ 	[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU] = {.type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_LINK_ID] = {.type = NLA_U8 },
+ };
+ 
+ static struct nla_policy
+@@ -14910,7 +14911,7 @@ static int nl80211_get_aval_color_bmp(void *priv, u64 *aval_color_bmp)
+ 	return ret;
+ }
+ 
+-static int nl80211_ap_wireless(void *priv, u8 sub_vendor_id, int value)
++static int nl80211_ap_wireless(void *priv, u8 sub_vendor_id, int value, s8 link_id)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+@@ -14941,6 +14942,9 @@ static int nl80211_ap_wireless(void *priv, u8 sub_vendor_id, int value)
+ 	else
+ 		nla_put_u8(msg, sub_vendor_id, (u8) value);
+ 
++	if (link_id > -1)
++		nla_put_u8(msg, MTK_VENDOR_ATTR_WIRELESS_CTRL_LINK_ID, link_id);
++
+ 	nla_nest_end(msg, data);
+ 	ret = send_and_recv_cmd(drv, msg);
+ 	if (ret)
+@@ -14997,7 +15001,7 @@ fail:
+ 	return -ENOBUFS;
+ }
+ 
+-static int nl80211_ap_trigtype(void *priv, u8 enable, u8 type)
++static int nl80211_ap_trigtype(void *priv, u8 enable, u8 type, s8 link_id)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+@@ -15023,6 +15027,9 @@ static int nl80211_ap_trigtype(void *priv, u8 enable, u8 type)
+ 	if (!data)
+ 		goto fail;
+ 
++	if (link_id > -1)
++		nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_LINK_ID, link_id);
++
+ 	data2 = nla_nest_start(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG);
+ 	if (!data2)
+ 		goto fail;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0109-mtk-hostapd-rework-radar-event-handling.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0109-mtk-hostapd-rework-radar-event-handling.patch
new file mode 100644
index 0000000..6daae7e
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0109-mtk-hostapd-rework-radar-event-handling.patch
@@ -0,0 +1,124 @@
+From 3443edc9c46258a0ce8a11ca579f7be29028e363 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 27 Jun 2024 13:44:00 +0800
+Subject: [PATCH 109/126] mtk: hostapd: rework radar event handling
+
+Specify the ifindex for the radar event in the sinlge wiphy model.
+This resolves the following MBSS MLD radar event issue.
+For example, if the topology is:
+MLD 1 (2G, 6G)
+MLD 2 (2G, 5G)
+2G legacy AP
+5G legacy AP
+6G legacy AP
+Without specifying the ifindex, hostapd will handle the radar event
+with the drv->ctx (MLD 1 2G hapd).
+However, in this case, MLD 1 has no 5G link, so the radar event will
+be ignored.
+
+Depends-On: I41d67b4d6f4610694f3830fdd0154fd392bc7c1f
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/drivers/driver_nl80211_event.c | 42 +++++++++++++++++++-----------
+ 1 file changed, 27 insertions(+), 15 deletions(-)
+
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 635401564..e95593a5b 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -2464,11 +2464,23 @@ static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv,
+ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
+ 				struct nlattr **tb)
+ {
++	struct i802_bss *bss;
+ 	union wpa_event_data data;
+ 	enum nl80211_radar_event event_type;
++	int ifidx;
++
++	if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT] ||
++	    !tb[NL80211_ATTR_IFINDEX])
++		return;
+ 
+-	if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT])
++	ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
++	bss = get_bss_ifindex(drv, ifidx);
++	if (!bss) {
++		wpa_printf(MSG_ERROR,
++			   "nl80211: Unknown ifindex (%d) for radar event, ignoring",
++			   ifidx);
+ 		return;
++	}
+ 
+ 	os_memset(&data, 0, sizeof(data));
+ 	data.dfs_event.link_id = NL80211_DRV_LINK_ID_NA;
+@@ -2480,8 +2492,7 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
+ 			nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
+ 	} else if (data.dfs_event.freq) {
+ 		data.dfs_event.link_id =
+-			nl80211_get_link_id_by_freq(drv->first_bss,
+-						    data.dfs_event.freq);
++			nl80211_get_link_id_by_freq(bss, data.dfs_event.freq);
+ 	}
+ 
+ 	/* Check HT params */
+@@ -2515,43 +2526,44 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
+ 		data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+ 
+ 	wpa_printf(MSG_DEBUG,
+-		   "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz, link_id=%d",
++		   "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, "
++		   "cf1: %dMHz, cf2: %dMHz, ifindex=%d, link_id=%d",
+ 		   data.dfs_event.freq, data.dfs_event.ht_enabled,
+ 		   data.dfs_event.chan_offset, data.dfs_event.chan_width,
+-		   data.dfs_event.cf1, data.dfs_event.cf2,
++		   data.dfs_event.cf1, data.dfs_event.cf2, ifidx,
+ 		   data.dfs_event.link_id);
+ 
+ 	switch (event_type) {
+ 	case NL80211_RADAR_DETECTED:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data);
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_RADAR_DETECTED, &data);
+ 		break;
+ 	case NL80211_RADAR_CAC_FINISHED:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data);
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_CAC_FINISHED, &data);
+ 		break;
+ 	case NL80211_RADAR_CAC_ABORTED:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data);
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_CAC_ABORTED, &data);
+ 		break;
+ 	case NL80211_RADAR_NOP_FINISHED:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_NOP_FINISHED, &data);
+ 		break;
+ 	case NL80211_RADAR_PRE_CAC_EXPIRED:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_PRE_CAC_EXPIRED,
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_PRE_CAC_EXPIRED,
+ 				     &data);
+ 		break;
+ 	case NL80211_RADAR_CAC_STARTED:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_CAC_STARTED, &data);
+ 		break;
+ 	case NL80211_RADAR_BACKGROUND_CHAN_UPDATE:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_UPDATE, &data);
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_BACKGROUND_CHAN_UPDATE, &data);
+ 		break;
+ 	case NL80211_RADAR_BACKGROUND_CHAN_EXPAND:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_EXPAND, &data);
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_BACKGROUND_CHAN_EXPAND, &data);
+ 		break;
+ 	case NL80211_RADAR_STA_CAC_SKIPPED:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
+ 		break;
+ 	case NL80211_RADAR_STA_CAC_EXPIRED:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_EXPIRED, &data);
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_STA_CAC_EXPIRED, &data);
+ 		break;
+ 	default:
+ 		wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0110-mtk-hostapd-add-support-for-link-add.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0110-mtk-hostapd-add-support-for-link-add.patch
new file mode 100644
index 0000000..ae3b4da
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0110-mtk-hostapd-add-support-for-link-add.patch
@@ -0,0 +1,292 @@
+From 4418652f8216f0fcdb74e4534d90f21f87d06dbc Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 28 Jun 2024 14:05:21 +0800
+Subject: [PATCH 110/126] mtk: hostapd: add support for link add
+
+Add support for adding affiliated APs to AP MLD via the following link add command
+hostapd_cli -i <mld intf> raw LINK_ADD bss_config=phyX:<bss_config>
+<bss_config> is per-bss config
+
+Add another cmd format:
+hostapd_cli -i <mld intf> link_add bss_config=phyX:<bss_config>
+
+Current link removal is implemented by hostapd_disable_iface for
+the sake of simplicity.
+When a link is added back after removal, hostapd_enable_iface should be
+called instead of hostapd_add_iface.
+Therefore, this patch is added to support iface enablement in link add cmd
+as a temporary workaround.
+
+Avoid modifying the original cmd str before entering hostapd_add_iface.
+Otherwise, hostapd_add_iface will fail to parse the cmd str correctly.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/ctrl_iface.c  | 104 ++++++++++++++++++++++++++++++++++++++++++
+ hostapd/hostapd_cli.c |   8 ++++
+ src/ap/beacon.c       |  20 ++++++--
+ src/ap/hostapd.c      |  24 +++++++---
+ src/ap/hostapd.h      |   1 +
+ 5 files changed, 148 insertions(+), 9 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 3943cb3aa..988bc90de 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -3910,6 +3910,105 @@ static int hostapd_ctrl_iface_link_remove(struct hostapd_data *hapd, char *cmd,
+ 
+ 	return ret;
+ }
++
++
++static int hostapd_ctrl_iface_link_add(struct hostapd_data *hapd, char *cmd,
++				       char *buf, size_t buflen)
++{
++	struct hapd_interfaces *interfaces = hapd->iface->interfaces;
++	struct hostapd_iface *iface = NULL;
++	struct hostapd_data *h;
++	struct hostapd_config *conf;
++	const char *ifname, *conf_file, *phy;
++	u16 old_valid_links = 0;
++	bool hapd_existed = false;
++	char *pos, *tmp;
++	int i, ret = -1;
++	size_t len;
++
++	if (!hapd || !hapd->conf->mld_ap || !hapd->mld) {
++		wpa_printf(MSG_ERROR,
++			   "Trying to add link to non-MLD AP or non-existed AP");
++		return -1;
++	}
++
++	if (os_strncmp(cmd, "bss_config=", 11))
++		return -1;
++
++	len = os_strlen(cmd) + 1;
++	tmp = os_malloc(len);
++	if (!tmp)
++		return -1;
++
++	os_snprintf(tmp, len, "%s", cmd);
++	phy = tmp + 11;
++	pos = os_strchr(phy, ':');
++	if (!pos)
++		goto out;
++	*pos++ = '\0';
++	conf_file = pos;
++	if (!os_strlen(conf_file))
++		goto out;
++
++	conf = interfaces->config_read_cb(conf_file);
++	if (!conf)
++		goto out;
++
++	ifname = conf->bss[0]->iface;
++	if (ifname[0] != '\0' &&
++	    os_strncmp(ifname, hapd->conf->iface, sizeof(hapd->conf->iface))) {
++		wpa_printf(MSG_ERROR,
++			   "Interface name %s mismatch (expected %s)",
++			   ifname, hapd->conf->iface);
++		hostapd_config_free(conf);
++		goto out;
++	}
++
++	if (!conf->bss[0]->mld_ap) {
++		wpa_printf(MSG_ERROR, "The added interface is not MLD AP");
++		hostapd_config_free(conf);
++		goto out;
++	}
++
++	for (i = 0; i < interfaces->count; i++) {
++		if (os_strcmp(interfaces->iface[i]->phy, phy) == 0) {
++			iface = interfaces->iface[i];
++			break;
++		}
++	}
++	if (iface && iface->state == HAPD_IFACE_DISABLED) {
++		for (i = 0; i < iface->num_bss; i++) {
++			h = iface->bss[i];
++			if (ifname[0] != '\0' &&
++			    !os_strncmp(ifname, h->conf->iface, sizeof(h->conf->iface)))
++				hapd_existed = true;
++		}
++	}
++	hostapd_config_free(conf);
++
++	for_each_mld_link(h, hapd)
++		old_valid_links |= BIT(h->mld_link_id);
++	hapd->mld->link_reconf_in_progress = old_valid_links;
++
++	if (hapd_existed)
++		ret = hostapd_enable_iface(iface);
++	else
++		ret = hostapd_add_iface(interfaces, cmd);
++	if (ret < 0)
++		goto out;
++
++	ret = os_snprintf(buf, buflen, "%s\n", "OK");
++	if (os_snprintf_error(buflen, ret))
++		ret = -1;
++	else
++		ret = 0;
++
++out:
++	os_free(tmp);
++
++	return ret;
++}
++
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
+ 
+@@ -6048,6 +6147,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		if (hostapd_ctrl_iface_link_remove(hapd, buf + 12,
+ 						   reply, reply_size))
+ 			reply_len = -1;
++	} else if (os_strncmp(buf, "LINK_ADD ", 9) == 0) {
++		if (hostapd_ctrl_iface_link_add(hapd, buf + 9,
++						reply, reply_size))
++			reply_len = -1;
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
+ 	} else if (os_strncmp(buf, "SET_EDCCA ", 10) == 0) {
+@@ -6938,6 +7041,7 @@ void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
+ static int hostapd_ctrl_iface_add(struct hapd_interfaces *interfaces,
+ 				  char *buf)
+ {
++	/* TODO: handle link add via global ADD command */
+ 	if (hostapd_add_iface(interfaces, buf) < 0) {
+ 		wpa_printf(MSG_ERROR, "Adding interface %s failed", buf);
+ 		return -1;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 54fda5c45..bfa912dff 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1779,6 +1779,12 @@ static int hostapd_cli_cmd_wmm(struct wpa_ctrl *ctrl, int argc,
+ 	return hostapd_cli_cmd(ctrl, "WMM", 1, argc, argv);
+ }
+ 
++static int hostapd_cli_cmd_link_add(struct wpa_ctrl *ctrl, int argc,
++				    char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "LINK_ADD", 1, argc, argv);
++}
++
+ struct hostapd_cli_cmd {
+ 	const char *cmd;
+ 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -2039,6 +2045,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 		" = Get preamble puncture status"},
+ 	{ "wmm", hostapd_cli_cmd_wmm, NULL,
+ 		" = <ac> [cwmin=] [cwmax=] [aifs=] [txop_limit=]"},
++	{ "link_add", hostapd_cli_cmd_link_add, NULL,
++		" = Add a new link to a MLD AP"},
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 50d45d532..8a9569549 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -2809,6 +2809,14 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
+ 		return 0;
+ 	}
+ 
++#ifdef CONFIG_IEEE80211BE
++#ifdef CONFIG_TESTING_OPTIONS
++	if (hapd->conf->mld_ap && hapd->mld &&
++	    (hapd->mld->link_reconf_in_progress & BIT(hapd->mld_link_id)))
++		ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_ADD_LINK);
++#endif /* CONFIG_TESTING_OPTIONS */
++#endif /* CONFIG_IEEE80211BE */
++
+ 	hapd->beacon_set_done = 1;
+ 
+ 	if (ieee802_11_build_ap_params(hapd, &params) < 0)
+@@ -2966,13 +2974,19 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
+ 	}
+ 
+ #ifdef CONFIG_IEEE80211BE
+-	/* clear critical update flag for UPDATE_SINGLE type, for other types,
+-	 * we should get some notified events from driver
++	/* clear critical update flag for UPDATE_SINGLE type & link adding,
++	 * for other types, we should get some notified events from driver
+ 	 */
+ 	if (hapd->conf->mld_ap) {
+-		for_each_mld_link(h, hapd)
++		for_each_mld_link(h, hapd) {
+ 			if (h->eht_mld_bss_critical_update == BSS_CRIT_UPDATE_SINGLE)
+ 				h->eht_mld_bss_critical_update = 0;
++			if (h->eht_mld_bss_critical_update == BSS_CRIT_UPDATE_FLAG &&
++			    (h->mld->link_reconf_in_progress & BIT(h->mld_link_id))) {
++				h->mld->link_reconf_in_progress &= ~BIT(h->mld_link_id);
++				h->eht_mld_bss_critical_update = 0;
++			}
++		}
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 63d89e614..257849536 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -574,6 +574,10 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
+ 	    hapd->iface->bss[0] != hapd)
+ 		hostapd_if_link_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface,
+ 				       hapd->mld_link_id);
++	/* Clear the link reconfiguration flag when the added link failed to setup */
++	if (hapd->conf->mld_ap && hapd->mld &&
++	    !(hapd->mld->link_reconf_in_progress & BIT(hapd->mld_link_id)))
++		hapd->mld->link_reconf_in_progress = 0;
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ 	wpabuf_free(hapd->time_adv);
+@@ -3450,11 +3454,20 @@ hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+ 		}
+ 
+ 		ifname = conf->bss[0]->iface;
+-		if (ifname[0] != '\0' && ifname_in_use(interfaces, ifname)) {
+-			wpa_printf(MSG_ERROR,
+-				   "Interface name %s already in use", ifname);
+-			hostapd_config_free(conf);
+-			return NULL;
++		if (conf->bss[0]->mld_ap) {
++			if (!iface->bss[0]->conf->mld_ap) {
++				wpa_printf(MSG_ERROR,
++					   "Cannot add a MLO BSS when the first BSS is non-MLO");
++				hostapd_config_free(conf);
++				return NULL;
++			}
++		} else {
++			if (ifname[0] != '\0' && ifname_in_use(interfaces, ifname)) {
++				wpa_printf(MSG_ERROR,
++					   "Interface name %s already in use", ifname);
++				hostapd_config_free(conf);
++				return NULL;
++			}
+ 		}
+ 
+ 		tmp_conf = os_realloc_array(
+@@ -3486,7 +3499,6 @@ hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+ 		hapd->msg_ctx = hapd;
+ 		hostapd_bss_setup_multi_link(hapd, interfaces);
+ 
+-
+ 		bss_idx = iface->num_bss++;
+ 		conf->num_bss--;
+ 		conf->bss[0] = NULL;
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 99d5a01d6..52875cca1 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -549,6 +549,7 @@ struct hostapd_mld {
+ 	 */
+ 	u8 refcount;
+ 	bool started;
++	u16 link_reconf_in_progress;
+ 
+ 	struct hostapd_data *fbss;
+ 	struct dl_list links; /* List head of all affiliated links */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0111-mtk-hostapd-Fix-the-op_class-value-in-the-RNR-IE-to-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0111-mtk-hostapd-Fix-the-op_class-value-in-the-RNR-IE-to-.patch
new file mode 100644
index 0000000..0dd281b
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0111-mtk-hostapd-Fix-the-op_class-value-in-the-RNR-IE-to-.patch
@@ -0,0 +1,42 @@
+From b039d88e5924a340709646c5e855bc06fcb1da78 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+Date: Thu, 4 Jul 2024 13:37:23 +0800
+Subject: [PATCH 111/126] mtk: hostapd: Fix the op_class value in the RNR IE to
+ achieve backward compatibility
+
+According to the chapter 11.49 Reduced neighbor report
+of DraftP802.11be_D5.1, the AP should select (one of) the operating
+class(es) that is expected to be understood by all STAs that might
+intend to connect to the reported AP, even if the channel spacing of
+that operating class is less than the BSS bandwidth of all the reported
+APs in the Neighbor AP Information field.
+
+For example, if an AP reports an EHT AP that is operating a BSS with 320
+MHz bandwidth in 6 GHz band using operating class 137, it is recommended
+that the operating class selected by the AP in the Reduced Neighbor Report
+element is operating class 131, 132, 133 or 134, since these are expected
+to be understood by all STAs that can connect to the reported 6GHz AP.
+
+Signed-off-by: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Signed-off-by: Money Wang <money.wang@mediatek.com>
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ src/ap/ieee802_11.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 62fdfb288..75ffb2a18 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7841,7 +7841,7 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
+ 		 * one TBTT info available. */
+ 		*tbtt_count_pos = eid++;
+ 		*eid++ = tbtt_info_len;
+-		*eid++ = op_class;
++		*eid++ = (op_class == 137 ? 134 : op_class);
+ 		*eid++ = bss->iconf->channel;
+ 		*len += RNR_TBTT_HEADER_LEN;
+ 	}
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0112-mtk-hostapd-support-enable-disable-preamble-puncture.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0112-mtk-hostapd-support-enable-disable-preamble-puncture.patch
new file mode 100644
index 0000000..0f8af3e
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0112-mtk-hostapd-support-enable-disable-preamble-puncture.patch
@@ -0,0 +1,470 @@
+From d45a052e2c7efe295705ad2185f36b38058ede83 Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Tue, 28 May 2024 17:46:26 +0800
+Subject: [PATCH 112/126] mtk: hostapd: support enable/disable preamble
+ puncture from mtk vendor command
+
+Add mtk vendor event to update punct bitmap and trigger channel switch.
+Change to pp user mode when use hostapd_cli to channel switch.
+Change pp vendor cmd use link_id instead of band_idx.
+Remove band_ind in hostapd_cli get/set pp cmd.
+
+Change pp default enable firmware mode.
+
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ hostapd/config_file.c              |  1 -
+ hostapd/ctrl_iface.c               | 45 ++++++++--------
+ hostapd/hostapd_cli.c              |  4 +-
+ src/ap/ap_config.c                 |  2 +-
+ src/ap/ap_drv_ops.c                | 10 +++-
+ src/ap/drv_callbacks.c             | 82 ++++++++++++++++++++++++++++++
+ src/common/mtk_vendor.h            |  8 ++-
+ src/drivers/driver.h               | 11 +++-
+ src/drivers/driver_nl80211.c       | 10 ++--
+ src/drivers/driver_nl80211_event.c | 48 +++++++++++++++++
+ 10 files changed, 185 insertions(+), 36 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 38273a4f2..f7bfc357a 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5452,7 +5452,6 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		if (get_u16(pos, line, &conf->punct_bitmap))
+ 			return 1;
+ 		conf->punct_bitmap = atoi(pos);
+-		conf->pp_mode = PP_USR_MODE;
+ 	} else if (os_strcmp(buf, "punct_acs_threshold") == 0) {
+ 		int val = atoi(pos);
+ 
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 988bc90de..337261f8f 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -2818,6 +2818,7 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ #ifdef NEED_AP_MLME
+ 	struct hostapd_hw_modes *mode = iface->current_mode;
+ 	struct csa_settings settings, background_settings;
++	struct hostapd_data *hapd;
+ 	int ret;
+ 	int freq, state;
+ 	int bandwidth, oper_chwidth;
+@@ -2914,6 +2915,17 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ 		break;
+ 	}
+ 
++#ifdef CONFIG_IEEE80211BE
++	hapd = iface->bss[0];
++	if (hapd->iconf->punct_bitmap != settings.punct_bitmap &&
++	    hapd->iconf->pp_mode != PP_USR_MODE) {
++		hapd->iconf->pp_mode = PP_USR_MODE;
++		ret = hostapd_drv_pp_mode_set(hapd);
++		if (ret)
++			return ret;
++	}
++#endif /* CONFIG_IEEE80211BE */
++
+ 	for (i = 0; i < iface->num_bss; i++) {
+ 
+ 		/* Save CHAN_SWITCH VHT, HE, and EHT config */
+@@ -5172,8 +5184,7 @@ static int
+ hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ 			  size_t buflen)
+ {
+-	char *band, *config, *value;
+-	u8 band_idx;
++	char *config, *value;
+ 
+ 	config = cmd;
+ 
+@@ -5182,31 +5193,26 @@ hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ 		return -1;
+ 	*value++ = '\0';
+ 
+-	band = os_strchr(value, ' ');
+-	if (band == NULL)
+-		return -1;
+-	*band++ = '\0';
+-	band_idx = strtol(band, NULL, 10);
+-
+-	hapd = hostapd_get_hapd_by_band_idx(hapd, band_idx);
+-
+-	if (!hapd)
+-		return -1;
+-
+ 	if (os_strcmp(config, "mode") == 0) {
+ 		int val = strtol(value, NULL, 10);
+ 
+-		if (val < PP_DISABLE || val > PP_FW_MODE) {
++		switch(val) {
++		case PP_DISABLE:
++		case PP_FW_MODE:
++			break;
++		case PP_USR_MODE:
++		default:
+ 			wpa_printf(MSG_ERROR, "Invalid value for SET_PP");
+ 			return -1;
+ 		}
+ 		hapd->iconf->pp_mode = (u8) val;
++		hapd->iconf->punct_bitmap = 0;
+ 		if (hostapd_drv_pp_mode_set(hapd) != 0)
+ 			return -1;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Unsupported parameter %s for SET_PP"
+-			   "Usage: set_pp mode <value> <band_idx>", config);
++			   "Usage: set_pp mode <value>", config);
+ 		return -1;
+ 	}
+ 	return os_snprintf(buf, buflen, "OK\n");
+@@ -5216,15 +5222,6 @@ static int
+ hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ 			  size_t buflen)
+ {
+-	u8 band_idx;
+-
+-	band_idx = strtol(cmd, NULL, 10);
+-
+-	hapd = hostapd_get_hapd_by_band_idx(hapd, band_idx);
+-
+-	if (!hapd)
+-		return -1;
+-
+ 	return os_snprintf(buf, buflen, "pp_mode: %d, punct_bitmap: 0x%04x\n",
+ 			   hapd->iconf->pp_mode, hapd->iconf->punct_bitmap);
+ }
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index bfa912dff..12ee0a18f 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1764,13 +1764,13 @@ static int hostapd_cli_cmd_dump_csi(struct wpa_ctrl *ctrl, int argc,
+ static int hostapd_cli_cmd_set_pp(struct wpa_ctrl *ctrl, int argc,
+ 					   char *argv[])
+ {
+-	return hostapd_cli_cmd(ctrl, "set_pp", 3, argc, argv);
++	return hostapd_cli_cmd(ctrl, "set_pp", 2, argc, argv);
+ }
+ 
+ static int hostapd_cli_cmd_get_pp(struct wpa_ctrl *ctrl, int argc,
+ 					   char *argv[])
+ {
+-	return hostapd_cli_cmd(ctrl, "get_pp", 1, argc, argv);
++	return hostapd_cli_cmd(ctrl, "get_pp", 0, argc, argv);
+ }
+ 
+ static int hostapd_cli_cmd_wmm(struct wpa_ctrl *ctrl, int argc,
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 95100b25c..4528df823 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -312,7 +312,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 	conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
+ 	conf->ibf_enable = IBF_DEFAULT_ENABLE;
+ 	conf->amsdu = 1;
+-	conf->pp_mode = PP_DISABLE;
++	conf->pp_mode = PP_FW_MODE;
+ 	conf->band_idx = 255;
+ 
+ 	hostapd_set_and_check_bw320_offset(conf, 0);
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 8631bf960..f9ec9a689 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1447,14 +1447,20 @@ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
+ 
+ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
+ {
++	s8 link_id = -1;
++
+ 	if (!hapd->driver || !hapd->driver->pp_mode_set ||
+-	    hapd->iconf->pp_mode >= PP_USR_MODE ||
++	    hapd->iconf->pp_mode > PP_USR_MODE ||
+ 	    hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+ 		return 0;
+ 
++	if (hapd->conf->mld_ap)
++		link_id = hapd->mld_link_id;
++
+ 	return hapd->driver->pp_mode_set(hapd->drv_priv,
+ 					 hapd->iconf->pp_mode,
+-					 hapd->iconf->band_idx);
++					 link_id,
++					 hapd->iconf->punct_bitmap);
+ }
+ 
+ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 0e7cfd285..420d156c8 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -43,6 +43,7 @@
+ #include "fils_hlp.h"
+ #include "neighbor_db.h"
+ #include "nan_usd_ap.h"
++#include "ap/beacon.h"
+ 
+ 
+ #ifdef CONFIG_FILS
+@@ -2456,6 +2457,81 @@ static void hostapd_event_color_change(struct hostapd_data *hapd, bool success)
+ #endif  /* CONFIG_IEEE80211AX */
+ 
+ 
++static void hostapd_event_pp_bitmap_update(struct hostapd_data *hapd,
++					   struct ch_switch *ch_switch)
++{
++	struct hostapd_iface *iface = hapd->iface;
++	struct hostapd_hw_modes *cmode = iface->current_mode;
++	int err, freq;
++	struct csa_settings csa_settings;
++	unsigned int i;
++
++	/* Check if CSA in progress */
++	if (hostapd_csa_in_progress(iface))
++		return;
++
++	if (!hw_get_channel_chan(cmode, iface->conf->channel, &freq))
++		return;
++
++	if (iface->conf->punct_bitmap == ch_switch->punct_bitmap ||
++	    freq != ch_switch->freq)
++		return;
++
++	/* Setup CSA request */
++	os_memset(&csa_settings, 0, sizeof(csa_settings));
++	csa_settings.cs_count = 5;
++	csa_settings.block_tx = 0;
++	csa_settings.punct_bitmap = ch_switch->punct_bitmap;
++	csa_settings.link_id = ch_switch->link_id;
++
++	err = hostapd_set_freq_params(&csa_settings.freq_params,
++				      iface->conf->hw_mode,
++				      freq,
++				      iface->conf->channel,
++				      iface->conf->enable_edmg,
++				      iface->conf->edmg_channel,
++				      iface->conf->ieee80211n,
++				      iface->conf->ieee80211ac,
++				      iface->conf->ieee80211ax,
++				      iface->conf->ieee80211be,
++				      iface->conf->secondary_channel,
++				      hostapd_get_oper_chwidth(iface->conf),
++				      hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
++				      hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
++				      cmode->vht_capab,
++				      &cmode->he_capab[IEEE80211_MODE_AP],
++				      &cmode->eht_capab[IEEE80211_MODE_AP],
++				      ch_switch->punct_bitmap);
++
++	if (err) {
++		wpa_printf(MSG_ERROR,
++			   "Failed to calculate CSA freq params");
++		hostapd_disable_iface(iface);
++		return;
++	}
++
++	for (i = 0; i < iface->num_bss; i++) {
++		ieee802_11_set_bss_critical_update(iface->bss[i],
++						   BSS_CRIT_UPDATE_EVENT_CSA);
++
++		err = hostapd_switch_channel(iface->bss[i], &csa_settings);
++		if (err)
++			break;
++
++#ifdef CONFIG_IEEE80211BE
++		if (iface->bss[i]->conf->mld_ap)
++			hostapd_update_aff_link_beacon(iface->bss[i],
++						       csa_settings.cs_count);
++
++		/* FIXME:
++		 * CU flag should be cleared when receiving DTIM event from FW
++		 */
++		iface->bss[i]->eht_mld_bss_critical_update = 0;
++#endif /* CONFIG_IEEE80211BE */
++	}
++}
++
++
+ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ 		       union wpa_event_data *data)
+ {
+@@ -2759,6 +2835,12 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ 		hostapd_event_dfs_cac_started(hapd, &data->dfs_event);
+ 		break;
+ #endif /* NEED_AP_MLME */
++	case EVENT_PP_BITMAP_UPDATE:
++		if (!data)
++			break;
++		hapd = switch_link_hapd(hapd, data->ch_switch.link_id);
++		hostapd_event_pp_bitmap_update(hapd, &data->ch_switch);
++		break;
+ 	case EVENT_INTERFACE_ENABLED:
+ 		wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_ENABLED);
+ 		if (hapd->disabled && hapd->started) {
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 937b968d5..933f0099d 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -22,6 +22,10 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_EML_CTRL = 0xd3,
+ };
+ 
++enum mtk_nl80211_vendor_subevents {
++	MTK_NL80211_VENDOR_EVENT_PP_BMP_UPDATE = 0x5,
++};
++
+ enum mtk_vendor_attr_edcca_ctrl {
+ 	MTK_VENDOR_ATTR_EDCCA_THRESHOLD_INVALID = 0,
+ 
+@@ -271,7 +275,9 @@ enum mtk_vendor_attr_pp_ctrl {
+ 	MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
+ 
+ 	MTK_VENDOR_ATTR_PP_MODE,
+-	MTK_VENDOR_ATTR_PP_BAND_IDX,
++	MTK_VENDOR_ATTR_PP_LINK_ID,
++	MTK_VENDOR_ATTR_PP_BITMAP,
++	MTK_VENDOR_ATTR_PP_CURR_FREQ,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_PP_CTRL,
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index dacf0a98d..5e65d9e0b 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5381,9 +5381,10 @@ struct wpa_driver_ops {
+ 	 * pp_mode_set - Set preamble puncture operation mode
+ 	 * @priv: Private driver interface data
+ 	 * @pp_mode: Value is defined in enum pp_mode
+-	 * @band_idx: chip band index
++	 * @link_id: MLD link id. -1 if this is an non-MLD AP
++	 * @punct_bitmap: current puncture bitmap
+ 	 */
+-	int (*pp_mode_set)(void *priv, const u8 pp_mode, u8 band_idx);
++	int (*pp_mode_set)(void *priv, const u8 pp_mode, s8 link_id, u16 punct_bitmap);
+ #ifdef CONFIG_IEEE80211BE
+ 	int (*get_mld_addr)(void *priv, u8 *addr);
+ #endif
+@@ -6058,6 +6059,12 @@ enum wpa_event_type {
+ 	 * channel has been updated and operating channel should expand its width.
+ 	 */
+ 	EVENT_DFS_BACKGROUND_CHAN_EXPAND,
++
++	/**
++	 * EVENT_PP_BITMAP_UPDATE - Notification that the new puncture bitmap
++	 * has been applied and a channel switch should be triggered.
++	 */
++	EVENT_PP_BITMAP_UPDATE,
+ };
+ 
+ 
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 80fe2e591..5d97317f7 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -160,7 +160,9 @@ amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
+ static struct nla_policy
+ pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
+ 	[MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
+-	[MTK_VENDOR_ATTR_PP_BAND_IDX] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_PP_LINK_ID] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_PP_BITMAP] = { .type = NLA_U16 },
++	[MTK_VENDOR_ATTR_PP_CURR_FREQ] = { .type = NLA_U32 },
+ };
+ 
+ static struct nla_policy csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
+@@ -15248,7 +15250,7 @@ static int nl80211_background_radar_mode(void *priv, const u8 background_radar_m
+ 	return ret;
+ }
+ 
+-static int nl80211_pp_mode_set(void *priv, const u8 pp_mode, u8 band_idx)
++static int nl80211_pp_mode_set(void *priv, const u8 pp_mode, s8 link_id, u16 punct_bitmap)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+@@ -15275,8 +15277,10 @@ static int nl80211_pp_mode_set(void *priv, const u8 pp_mode, u8 band_idx)
+ 	if (!data)
+ 		goto fail;
+ 
+-	nla_put_u8(msg, MTK_VENDOR_ATTR_PP_BAND_IDX, band_idx);
++	if (link_id > -1)
++		nla_put_u8(msg, MTK_VENDOR_ATTR_PP_LINK_ID, link_id);
+ 	nla_put_u8(msg, MTK_VENDOR_ATTR_PP_MODE, pp_mode);
++	nla_put_u16(msg, MTK_VENDOR_ATTR_PP_BITMAP, punct_bitmap);
+ 
+ 	nla_nest_end(msg, data);
+ 	ret = send_and_recv_cmd(drv, msg);
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index e95593a5b..efdb8ef7f 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -19,6 +19,7 @@
+ #include "common/ieee802_11_defs.h"
+ #include "common/ieee802_11_common.h"
+ #include "driver_nl80211.h"
++#include "common/mtk_vendor.h"
+ 
+ 
+ static void
+@@ -3332,6 +3333,50 @@ static void nl80211_vendor_event_brcm(struct wpa_driver_nl80211_data *drv,
+ 
+ #endif /* CONFIG_DRIVER_NL80211_BRCM */
+ 
++static void mtk_nl80211_pp_bitmap_update(struct wpa_driver_nl80211_data *drv,
++					 const u8 *data, size_t len)
++{
++	struct nlattr *tb[MTK_VENDOR_ATTR_PP_CTRL_MAX + 1];
++	union wpa_event_data event;
++
++	wpa_printf(MSG_DEBUG,
++		   "nl80211: MTK pp bitmap update vendor event received");
++
++	if (nla_parse(tb, MTK_VENDOR_ATTR_PP_CTRL_MAX,
++		      (struct nlattr *) data, len, NULL) ||
++	    !tb[MTK_VENDOR_ATTR_PP_CURR_FREQ] ||
++	    !tb[MTK_VENDOR_ATTR_PP_BITMAP])
++		return;
++
++	os_memset(&event, 0, sizeof(event));
++	event.ch_switch.freq = nla_get_u32(tb[MTK_VENDOR_ATTR_PP_CURR_FREQ]);
++
++	event.ch_switch.link_id =
++		nl80211_get_link_id_by_freq(drv->first_bss, event.ch_switch.freq);
++	event.ch_switch.punct_bitmap =
++		nla_get_u16(tb[MTK_VENDOR_ATTR_PP_BITMAP]);
++
++	wpa_printf(MSG_DEBUG,
++		   "nl80211: puncture bitmap: 0x%04x, link_id: %d",
++		   event.ch_switch.punct_bitmap, event.ch_switch.link_id);
++	wpa_supplicant_event(drv->ctx, EVENT_PP_BITMAP_UPDATE, &event);
++}
++
++static void nl80211_vendor_event_mtk(struct wpa_driver_nl80211_data *drv,
++				      u32 subcmd, u8 *data, size_t len)
++{
++	wpa_printf(MSG_DEBUG, "nl80211: Got MTK vendor event %u", subcmd);
++	switch (subcmd) {
++	case MTK_NL80211_VENDOR_EVENT_PP_BMP_UPDATE:
++		mtk_nl80211_pp_bitmap_update(drv, data, len);
++		break;
++	default:
++		wpa_printf(MSG_DEBUG,
++			   "%s: Ignore unsupported MTK vendor event %u",
++			   __func__, subcmd);
++		break;
++	}
++}
+ 
+ static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv,
+ 				 struct nlattr **tb)
+@@ -3388,6 +3433,9 @@ static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv,
+ 		nl80211_vendor_event_brcm(drv, subcmd, data, len);
+ 		break;
+ #endif /* CONFIG_DRIVER_NL80211_BRCM */
++	case OUI_MTK:
++		nl80211_vendor_event_mtk(drv, subcmd, data, len);
++		break;
+ 	default:
+ 		wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event");
+ 		break;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0113-mtk-hostapd-do-not-consider-ht-operation-update-for-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0113-mtk-hostapd-do-not-consider-ht-operation-update-for-.patch
new file mode 100644
index 0000000..f69891a
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0113-mtk-hostapd-do-not-consider-ht-operation-update-for-.patch
@@ -0,0 +1,28 @@
+From 5cc02e29a2725dcffbee12fece9d946dd809108b Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Wed, 10 Jul 2024 16:13:24 +0800
+Subject: [PATCH 113/126] mtk: hostapd: do not consider ht operation update for
+ 6G BSS
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ieee802_11.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 75ffb2a18..10b7587a0 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -4628,7 +4628,8 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
+ 		}
+ 		hapd->sta_aid[(sta->aid - 1) / 32] |= BIT((sta->aid - 1) % 32);
+ 		sta->listen_interval = origin_sta->listen_interval;
+-		if (update_ht_state(hapd, sta) > 0)
++		if (!is_6ghz_op_class(hapd->iconf->op_class) &&
++		    update_ht_state(hapd, sta) > 0)
+ 			ieee802_11_update_beacons(hapd->iface);
+ 	}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0114-mtk-hostapd-Add-bandwidth-indication-IE.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0114-mtk-hostapd-Add-bandwidth-indication-IE.patch
new file mode 100644
index 0000000..473d91f
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0114-mtk-hostapd-Add-bandwidth-indication-IE.patch
@@ -0,0 +1,485 @@
+From fd208c32b7e95c3aafce8f89917d4a961713fa75 Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Tue, 2 Jul 2024 10:38:49 +0800
+Subject: [PATCH 114/126] mtk: hostapd: Add bandwidth indication IE
+
+Move punct_bitmap from csa_settings to hostapd_freq_params for
+filling bandwidth indication IE while channel switch occurs.
+Handle bitmap change in hostapd_set_freq_params.
+
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ hostapd/ctrl_iface.c            |  18 +++---
+ src/ap/ctrl_iface_ap.c          |  12 +---
+ src/ap/dfs.c                    |   1 -
+ src/ap/drv_callbacks.c          |   1 -
+ src/ap/hostapd.c                |  14 +---
+ src/ap/ieee802_11.c             | 110 +++++++++++++++++++++++++++++---
+ src/ap/ieee802_11.h             |   2 +
+ src/common/hw_features_common.c |   1 +
+ src/common/ieee802_11_defs.h    |  30 +++++++++
+ src/drivers/driver.h            |  10 ++-
+ src/drivers/driver_nl80211.c    |   6 +-
+ 11 files changed, 156 insertions(+), 49 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 337261f8f..c8456513e 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -2621,8 +2621,7 @@ static int hostapd_ctrl_register_frame(struct hostapd_data *hapd,
+ 
+ 
+ #ifdef NEED_AP_MLME
+-static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params,
+-					  u16 punct_bitmap)
++static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params)
+ {
+ 	u32 start_freq;
+ 
+@@ -2673,14 +2672,14 @@ static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params,
+ 		if (params->center_freq2 || params->sec_channel_offset)
+ 			return -1;
+ 
+-		if (punct_bitmap)
++		if (params->punct_bitmap)
+ 			return -1;
+ 		break;
+ 	case 40:
+ 		if (params->center_freq2 || !params->sec_channel_offset)
+ 			return -1;
+ 
+-		if (punct_bitmap)
++		if (params->punct_bitmap)
+ 			return -1;
+ 
+ 		if (!params->center_freq1)
+@@ -2717,7 +2716,7 @@ static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params,
+ 			return -1;
+ 		}
+ 
+-		if (params->center_freq2 && punct_bitmap)
++		if (params->center_freq2 && params->punct_bitmap)
+ 			return -1;
+ 
+ 		/* Adjacent and overlapped are not allowed for 80+80 */
+@@ -2784,7 +2783,7 @@ static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params,
+ 		return -1;
+ 	}
+ 
+-	if (!punct_bitmap)
++	if (!params->punct_bitmap)
+ 		return 0;
+ 
+ 	if (!params->eht_enabled) {
+@@ -2802,7 +2801,7 @@ static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params,
+ 	start_freq = params->center_freq1 - (params->bandwidth / 2);
+ 	if (!is_punct_bitmap_valid(params->bandwidth,
+ 				   (params->freq - start_freq) / 20,
+-				   punct_bitmap)) {
++				   params->punct_bitmap)) {
+ 		wpa_printf(MSG_ERROR, "Invalid preamble puncturing bitmap");
+ 		return -1;
+ 	}
+@@ -2843,8 +2842,7 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ 		return -1;
+ 	}
+ 
+-	ret = hostapd_ctrl_check_freq_params(&settings.freq_params,
+-					     settings.punct_bitmap);
++	ret = hostapd_ctrl_check_freq_params(&settings.freq_params);
+ 	if (ret) {
+ 		wpa_printf(MSG_INFO,
+ 			   "chanswitch: invalid frequency settings provided");
+@@ -2917,7 +2915,7 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ 
+ #ifdef CONFIG_IEEE80211BE
+ 	hapd = iface->bss[0];
+-	if (hapd->iconf->punct_bitmap != settings.punct_bitmap &&
++	if (hapd->iconf->punct_bitmap != settings.freq_params.punct_bitmap &&
+ 	    hapd->iconf->pp_mode != PP_USR_MODE) {
+ 		hapd->iconf->pp_mode = PP_USR_MODE;
+ 		ret = hostapd_drv_pp_mode_set(hapd);
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index b0ee00b90..e1722620d 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -1125,20 +1125,11 @@ int hostapd_parse_csa_settings(const char *pos,
+ 		} \
+ 	} while (0)
+ 
+-#define SET_CSA_SETTING_EXT(str) \
+-	do { \
+-		const char *pos2 = os_strstr(pos, " " #str "="); \
+-		if (pos2) { \
+-			pos2 += sizeof(" " #str "=") - 1; \
+-			settings->str = atoi(pos2); \
+-		} \
+-	} while (0)
+-
+ 	SET_CSA_SETTING(center_freq1);
+ 	SET_CSA_SETTING(center_freq2);
+ 	SET_CSA_SETTING(bandwidth);
+ 	SET_CSA_SETTING(sec_channel_offset);
+-	SET_CSA_SETTING_EXT(punct_bitmap);
++	SET_CSA_SETTING(punct_bitmap);
+ 	settings->freq_params.ht_enabled = !!os_strstr(pos, " ht");
+ 	settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
+ 	settings->freq_params.he_enabled = !!os_strstr(pos, " he");
+@@ -1146,7 +1137,6 @@ int hostapd_parse_csa_settings(const char *pos,
+ 	settings->freq_params.radar_background = !!os_strstr(pos, " skip_cac");
+ 	settings->block_tx = !!os_strstr(pos, " blocktx");
+ #undef SET_CSA_SETTING
+-#undef SET_CSA_SETTING_EXT
+ 
+ 	return 0;
+ }
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 5e9a2a4ce..5a3112d40 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1081,7 +1081,6 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
+ 	os_memset(&csa_settings, 0, sizeof(csa_settings));
+ 	csa_settings.cs_count = 5;
+ 	csa_settings.block_tx = 1;
+-	csa_settings.punct_bitmap = hostapd_get_punct_bitmap(iface->bss[0]);
+ 	csa_settings.link_id = -1;
+ #ifdef CONFIG_IEEE80211BE
+ 	if (iface->bss[0]->conf->mld_ap)
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 420d156c8..96b8e856e 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2481,7 +2481,6 @@ static void hostapd_event_pp_bitmap_update(struct hostapd_data *hapd,
+ 	os_memset(&csa_settings, 0, sizeof(csa_settings));
+ 	csa_settings.cs_count = 5;
+ 	csa_settings.block_tx = 0;
+-	csa_settings.punct_bitmap = ch_switch->punct_bitmap;
+ 	csa_settings.link_id = ch_switch->link_id;
+ 
+ 	err = hostapd_set_freq_params(&csa_settings.freq_params,
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 257849536..73378053b 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4487,6 +4487,8 @@ static int hostapd_change_config_freq(struct hostapd_data *hapd,
+ 	hostapd_set_oper_centr_freq_seg0_idx(conf, seg0);
+ 	hostapd_set_oper_centr_freq_seg1_idx(conf, seg1);
+ 
++	conf->punct_bitmap = params->punct_bitmap;
++
+ 	/* TODO: maybe call here hostapd_config_check here? */
+ 
+ 	return 0;
+@@ -4499,9 +4501,6 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
+ 	struct hostapd_iface *iface = hapd->iface;
+ 	struct hostapd_freq_params old_freq;
+ 	int ret;
+-#ifdef CONFIG_IEEE80211BE
+-	u16 old_punct_bitmap;
+-#endif /* CONFIG_IEEE80211BE */
+ 	u8 chan, bandwidth;
+ 
+ 	os_memset(&old_freq, 0, sizeof(old_freq));
+@@ -4550,19 +4549,11 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
+ 	if (ret)
+ 		return ret;
+ 
+-#ifdef CONFIG_IEEE80211BE
+-	old_punct_bitmap = iface->conf->punct_bitmap;
+-	iface->conf->punct_bitmap = settings->punct_bitmap;
+-#endif /* CONFIG_IEEE80211BE */
+-
+ 	/* Another CU in the new channel due to OP element modification */
+ 	ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_EHT_OPERATION);
+ 	ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
+ 
+ 	/* change back the configuration */
+-#ifdef CONFIG_IEEE80211BE
+-	iface->conf->punct_bitmap = old_punct_bitmap;
+-#endif /* CONFIG_IEEE80211BE */
+ 	hostapd_change_config_freq(iface->bss[0], iface->conf,
+ 				   &old_freq, NULL);
+ 
+@@ -4739,7 +4730,6 @@ int hostapd_update_aff_link_beacon(struct hostapd_data *hapd, u8 cs_count)
+ 		settings.link_id = cs_link_id;
+ 		settings.freq_params.link_id = link_id;
+ 		settings.cs_count = cs_count;
+-		settings.punct_bitmap = conf->punct_bitmap;
+ 		ret = hostapd_drv_switch_channel(h, &settings);
+ 		free_beacon_data(&settings.beacon_csa);
+ 		free_beacon_data(&settings.beacon_after);
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 10b7587a0..93bd8255c 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7368,10 +7368,95 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
+ 				    tx_pwr);
+ }
+ 
++#define DISABLED_SUBCHANNEL_BITMAP_BYTES_SIZE 2
++u8 *hostapd_eid_bw_indication(struct hostapd_data *hapd, u8 *eid, u8 ccfs0, u8 ccfs1)
++{
++	u8 *pos = eid, *length_pos;
++	struct ieee80211_bw_ind_element *bw_ind_elem;
++	int bw_ind_status = false;
++	size_t fixed_eid_len;
++
++	if (hapd->cs_freq_params.bandwidth > 160 ||
++	    hapd->cs_freq_params.punct_bitmap)
++		bw_ind_status = true;
++
++	if (!bw_ind_status)
++		return eid;
++
++	if (!hapd->cs_freq_params.channel || !hapd->cs_freq_params.eht_enabled)
++		return eid;
++
++	*pos++ = WLAN_EID_EXTENSION;
++	length_pos = pos++;
++	*pos++ = WLAN_EID_EXT_BW_IND;
++
++	fixed_eid_len = (sizeof(struct ieee80211_bw_ind_element) -
++			DISABLED_SUBCHANNEL_BITMAP_BYTES_SIZE);
++
++	bw_ind_elem = (struct ieee80211_bw_ind_element *) pos;
++	os_memset(bw_ind_elem, 0, sizeof(struct ieee80211_bw_ind_element));
++
++	if (hapd->cs_freq_params.punct_bitmap) {
++		bw_ind_elem->bw_ind_params |=
++			BW_IND_PARAMETER_DISABLED_SUBCHAN_BITMAP_PRESENT;
++		bw_ind_elem->bw_ind_info.disabled_chan_bitmap =
++			host_to_le16(hapd->cs_freq_params.punct_bitmap);
++		pos += DISABLED_SUBCHANNEL_BITMAP_BYTES_SIZE;
++	} else {
++		bw_ind_elem->bw_ind_params &=
++			~BW_IND_PARAMETER_DISABLED_SUBCHAN_BITMAP_PRESENT;
++	}
++
++	switch (hapd->cs_freq_params.bandwidth) {
++	case 320:
++		bw_ind_elem->bw_ind_info.control |=
++			BW_IND_CHANNEL_WIDTH_320MHZ;
++		ccfs1 = ccfs0;
++		if (hapd->cs_freq_params.channel < ccfs0)
++			ccfs0 -= 16;
++		else
++			ccfs0 += 16;
++		break;
++	case 160:
++		bw_ind_elem->bw_ind_info.control |=
++			BW_IND_CHANNEL_WIDTH_160MHZ;
++		ccfs1 = ccfs0;
++		if (hapd->cs_freq_params.channel < ccfs0)
++			ccfs0 -= 8;
++		else
++			ccfs0 += 8;
++		break;
++	case 80:
++		bw_ind_elem->bw_ind_info.control |=
++			BW_IND_CHANNEL_WIDTH_80MHZ;
++		break;
++	case 40:
++		if (hapd->cs_freq_params.sec_channel_offset == 1)
++			bw_ind_elem->bw_ind_info.control |=
++				BW_IND_CHANNEL_WIDTH_40MHZ;
++		else
++			bw_ind_elem->bw_ind_info.control |=
++				BW_IND_CHANNEL_WIDTH_20MHZ;
++		break;
++	default:
++		bw_ind_elem->bw_ind_info.control |=
++			BW_IND_CHANNEL_WIDTH_20MHZ;
++		break;
++	}
++
++	bw_ind_elem->bw_ind_info.ccfs0 = ccfs0;
++	bw_ind_elem->bw_ind_info.ccfs1 = ccfs1;
++
++	pos += fixed_eid_len;
++	*length_pos = pos - (eid + 2);
++
++	return pos;
++}
++
+ 
+ u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
+ {
+-	u8 bw, chan1 = 0, chan2 = 0;
++	u8 bw, chan1 = 0, chan2 = 0, ccfs0, ccfs1, *pos = eid, *length_pos;
+ 	int freq1;
+ 
+ 	if (!hapd->cs_freq_params.channel ||
+@@ -7412,11 +7497,14 @@ u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
+ 				   &chan2) != HOSTAPD_MODE_IEEE80211A)
+ 		return eid;
+ 
+-	*eid++ = WLAN_EID_CHANNEL_SWITCH_WRAPPER;
+-	*eid++ = 5; /* Length of Channel Switch Wrapper */
+-	*eid++ = WLAN_EID_WIDE_BW_CHSWITCH;
+-	*eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
+-	*eid++ = bw; /* New Channel Width */
++	*pos++ = WLAN_EID_CHANNEL_SWITCH_WRAPPER;
++	length_pos = pos++; /* Length of Channel Switch Wrapper */
++	*pos++ = WLAN_EID_WIDE_BW_CHSWITCH;
++	*pos++ = 3; /* Length of Wide Bandwidth Channel Switch element */
++	*pos++ = bw; /* New Channel Width */
++
++	ccfs0 = chan1;
++	ccfs1 = chan2;
+ 	if (hapd->cs_freq_params.bandwidth == 160) {
+ 		/* Update the CCFS0 and CCFS1 values in the element based on
+ 		 * IEEE P802.11-REVme/D4.0, Table 9-314 */
+@@ -7432,10 +7520,14 @@ u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
+ 		else
+ 			chan1 += 8;
+ 	}
+-	*eid++ = chan1; /* New Channel Center Frequency Segment 0 */
+-	*eid++ = chan2; /* New Channel Center Frequency Segment 1 */
++	*pos++ = chan1; /* New Channel Center Frequency Segment 0 */
++	*pos++ = chan2; /* New Channel Center Frequency Segment 1 */
+ 
+-	return eid;
++#ifdef CONFIG_IEEE80211BE
++	pos = hostapd_eid_bw_indication(hapd, pos, ccfs0, ccfs1);
++#endif /* CONFIG_IEEE80211BE */
++	*length_pos = pos - (eid + 2);
++	return pos;
+ }
+ 
+ 
+diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
+index 2d9adb910..40301bce9 100644
+--- a/src/ap/ieee802_11.h
++++ b/src/ap/ieee802_11.h
+@@ -64,6 +64,8 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts);
+ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
+ u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
+ u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
++u8 * hostapd_eid_bw_indication(struct hostapd_data *hapd, u8 *eid,
++			       u8 ccfs0, u8 ccfs1);
+ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
+ u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
+ 			  enum ieee80211_op_mode opmode);
+diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
+index 8bd6e994d..99106c277 100644
+--- a/src/common/hw_features_common.c
++++ b/src/common/hw_features_common.c
+@@ -481,6 +481,7 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
+ 	data->sec_channel_offset = sec_channel_offset;
+ 	data->center_freq1 = freq + sec_channel_offset * 10;
+ 	data->center_freq2 = 0;
++	data->punct_bitmap = punct_bitmap;
+ 	if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
+ 		data->bandwidth = 80;
+ 	else if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ ||
+diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
+index fb481b8b2..d93fa6660 100644
+--- a/src/common/ieee802_11_defs.h
++++ b/src/common/ieee802_11_defs.h
+@@ -525,6 +525,7 @@
+ #define WLAN_EID_EXT_MULTI_LINK_TRAFFIC_INDICATION 110
+ #define WLAN_EID_EXT_QOS_CHARACTERISTICS 113
+ #define WLAN_EID_EXT_AKM_SUITE_SELECTOR 114
++#define WLAN_EID_EXT_BW_IND 135
+ 
+ /* Extended Capabilities field */
+ #define WLAN_EXT_CAPAB_20_40_COEX 0
+@@ -3088,6 +3089,35 @@ enum dscp_policy_request_type {
+ #define WFA_CAPA_QM_UNSOLIC_DSCP BIT(1)
+ #define WFA_CAPA_QM_NON_EHT_SCS_TRAFFIC_DESC BIT(2)
+ 
++/* IEEE P802.11be/D3.0, 9.4.2.319 - Bandwidth Indication element */
++
++/* Figure 9-1002ba: Bandwidth Indication Parameters field subfields */
++#define BW_IND_PARAMETER_RESERVED                              BIT(0)
++#define BW_IND_PARAMETER_DISABLED_SUBCHAN_BITMAP_PRESENT       BIT(1)
++
++/* Table 9-467: Control subfield: Channel Width subfield; */
++#define BW_IND_CHANNEL_WIDTH_20MHZ                   0
++#define BW_IND_CHANNEL_WIDTH_40MHZ                   1
++#define BW_IND_CHANNEL_WIDTH_80MHZ                   2
++#define BW_IND_CHANNEL_WIDTH_160MHZ                  3
++#define BW_IND_CHANNEL_WIDTH_320MHZ                  4
++
++/* Figure 9-1002c: Bandwidth Indication information
++ * field format similar to EHT Operation Information field format
++ */
++struct ieee80211_bw_ind_info {
++	u8 control; /* B0..B2: Channel Width */
++	u8 ccfs0;
++	u8 ccfs1;
++	le16 disabled_chan_bitmap; /* 0 or 2 octets */
++} STRUCT_PACKED;
++
++/* Figure 9-1002ba—Bandwidth Indication element format */
++struct ieee80211_bw_ind_element {
++	u8 bw_ind_params; /* Bandwidth Indication Parameters */
++	struct ieee80211_bw_ind_info bw_ind_info; /* 3 or 5 octets */
++} STRUCT_PACKED;
++
+ struct ieee80211_neighbor_ap_info {
+ 	u8 tbtt_info_hdr;
+ 	u8 tbtt_info_len;
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 5e65d9e0b..498eff91f 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -876,6 +876,14 @@ struct hostapd_freq_params {
+ 	 */
+ 	bool eht_enabled;
+ 
++	/**
++	 * punct_bitmap - puncturing bitmap
++	 * Each bit corresponds to a 20 MHz subchannel, lowest bit for the
++	 * channel with the lowest frequency. Bit set to 1 indicates that the
++	 * subchannel is punctured, otherwise active.
++	 */
++	u16 punct_bitmap;
++
+ 	/**
+ 	 * link_id: If >=0 indicates the link of the AP MLD to configure
+ 	 */
+@@ -2750,7 +2758,6 @@ struct beacon_data {
+  * @beacon_after: Next beacon/probe resp/asooc resp info
+  * @counter_offset_beacon: Offset to the count field in beacon's tail
+  * @counter_offset_presp: Offset to the count field in probe resp.
+- * @punct_bitmap - Preamble puncturing bitmap
+  * @link_id: Link ID to determine the link for MLD; -1 for non-MLD
+  * @ubpr: Unsolicited broadcast Probe Response frame data
+  */
+@@ -2766,7 +2773,6 @@ struct csa_settings {
+ 	u16 counter_offset_presp[2];
+ 	u16 counter_offset_sta_prof[MAX_NUM_MLD_LINKS][2];
+ 
+-	u16 punct_bitmap;
+ 	int link_id;
+ 
+ 	struct unsol_bcast_probe_resp ubpr;
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 5d97317f7..639c5e7e1 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -11467,7 +11467,7 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ 		   settings->freq_params.bandwidth,
+ 		   settings->freq_params.center_freq1,
+ 		   settings->freq_params.center_freq2,
+-		   settings->punct_bitmap,
++		   settings->freq_params.punct_bitmap,
+ 		   settings->freq_params.link_id,
+ 		   settings->freq_params.ht_enabled ? " ht" : "",
+ 		   settings->freq_params.vht_enabled ? " vht" : "",
+@@ -11545,9 +11545,9 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ 	    (ret = nl80211_put_freq_params(msg, &settings->freq_params)) ||
+ 	    (settings->block_tx &&
+ 	     nla_put_flag(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX)) ||
+-	    (settings->punct_bitmap &&
++	    (settings->freq_params.punct_bitmap &&
+ 	     nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP,
+-			 settings->punct_bitmap)) ||
++			 settings->freq_params.punct_bitmap)) ||
+ 	    (settings->freq_params.link_id != NL80211_DRV_LINK_ID_NA &&
+ 	     nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, settings->freq_params.link_id)))
+ 		goto error;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0115-mtk-hostapd-fix-Multiple-MLDs-to-use-the-conf-s-own_.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0115-mtk-hostapd-fix-Multiple-MLDs-to-use-the-conf-s-own_.patch
new file mode 100644
index 0000000..daba900
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0115-mtk-hostapd-fix-Multiple-MLDs-to-use-the-conf-s-own_.patch
@@ -0,0 +1,39 @@
+From 377b708eb38c5a7056eaca0cd54994a226f940b1 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Wed, 10 Jul 2024 14:49:43 +0800
+Subject: [PATCH 115/126] mtk: hostapd: fix Multiple MLDs to use the conf's
+ own_addr/mld_addr.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@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 73378053b..a89628fe7 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -1506,6 +1506,9 @@ int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
+ 					   hapd->mld_link_id, hapd->conf->iface);
+ 				goto setup_mld;
+ 			}
++
++			if (addr && !is_zero_ether_addr(hapd->conf->mld_addr))
++				os_memcpy(addr, hapd->conf->mld_addr, ETH_ALEN);
+ 		}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+@@ -1531,6 +1534,10 @@ int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
+ 				   hapd->mld_link_id, hapd->conf->iface);
+ 			os_memcpy(hapd->mld->mld_addr, hapd->own_addr,
+ 				  ETH_ALEN);
++
++			if (!is_zero_ether_addr(conf->bssid))
++				os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN);
++
+ 			hostapd_mld_add_link(hapd);
+ 		}
+ #endif /* CONFIG_IEEE80211BE */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0116-mtk-hostapd-distribute-the-mgmt-rx-frame-to-bss-with.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0116-mtk-hostapd-distribute-the-mgmt-rx-frame-to-bss-with.patch
new file mode 100644
index 0000000..0d3a4f1
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0116-mtk-hostapd-distribute-the-mgmt-rx-frame-to-bss-with.patch
@@ -0,0 +1,31 @@
+From d66477d21526bd4aa8ba9cdc3f8223df15e64386 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Wed, 10 Jul 2024 15:00:50 +0800
+Subject: [PATCH 116/126] mtk: hostapd: distribute the mgmt rx frame to bss
+ with same freq.
+
+there is no need to distribute the mgmt rx frame to all bss, we use
+mgmt->freq to filter.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ src/ap/drv_callbacks.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 96b8e856e..7ae26ab7c 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -1904,6 +1904,9 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
+ 			return 0;
+ 	}
+ 
++	if (rx_mgmt->freq != 0 && rx_mgmt->freq != iface->freq)
++		return 0;
++
+ 	os_memset(&fi, 0, sizeof(fi));
+ 	fi.freq = rx_mgmt->freq;
+ 	fi.datarate = rx_mgmt->datarate;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0117-mtk-hostapd-handle-5G-link-setup-after-DFS-bootup-CA.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0117-mtk-hostapd-handle-5G-link-setup-after-DFS-bootup-CA.patch
new file mode 100644
index 0000000..edfd4ca
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0117-mtk-hostapd-handle-5G-link-setup-after-DFS-bootup-CA.patch
@@ -0,0 +1,189 @@
+From 4bae58d6ded6d2b08975054e1c4097dcedb6b333 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 28 Jun 2024 14:06:20 +0800
+Subject: [PATCH 117/126] mtk: hostapd: handle 5G link setup after DFS bootup
+ CAC as link reconfig
+
+Consider the setup of 5G link after bootup CAC as a link adding
+process via link reconfiguration.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c            | 38 +++++++++++++++++++++++++++++++++++---
+ src/ap/hostapd.c        |  2 ++
+ src/ap/hostapd.h        |  1 +
+ src/ap/ieee802_11.c     |  7 +++++--
+ src/ap/ieee802_11_eht.c | 13 +++++++++++--
+ 5 files changed, 54 insertions(+), 7 deletions(-)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 5a3112d40..31e37a2ab 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1034,6 +1034,22 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ 			return -1;
+ 	}
+ 
++	/* Remove the CAC link from the active links of AP MLD temporarily to avoid
++	 * it being reported in the RNR of the affiliated APs of the same AP MLD
++	 */
++	if (iface->cac_started) {
++		int i;
++
++		for (i = 0; i < iface->num_bss; i++) {
++			struct hostapd_data *hapd = iface->bss[i];
++
++			if (!hapd->conf->mld_ap || !hapd->mld)
++				continue;
++
++			hapd->mld->active_links &= ~BIT(hapd->mld_link_id);
++		}
++	}
++
+ 	return 0;
+ }
+ 
+@@ -1310,6 +1326,8 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ 			     int ht_enabled, int chan_offset, int chan_width,
+ 			     int cf1, int cf2)
+ {
++	int i;
++
+ 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
+ 		"success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d radar_detected=%d",
+ 		success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2,
+@@ -1364,10 +1382,26 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ 			 * sure the configured channel is available because this
+ 			 * CAC completion event could have been propagated from
+ 			 * another radio.
++			 * For a AP MLD, the setup of a DFS link after bootup CAC is
++			 * considered as link adding process via link reconfiguration.
+ 			 */
+ 			if (iface->state != HAPD_IFACE_ENABLED &&
+-			    hostapd_is_dfs_chan_available(iface))
++			    hostapd_is_dfs_chan_available(iface)) {
++				for (i = 0; i < iface->num_bss; i++) {
++					struct hostapd_data *h, *hapd = iface->bss[i];
++
++					if (!hapd->conf->mld_ap || !hapd->mld)
++						continue;
++
++					hapd->mld->active_links |= BIT(hapd->mld_link_id);
++					for_each_mld_link(h, hapd)
++						h->mld->link_reconf_in_progress |=
++								BIT(h->mld_link_id);
++					hapd->mld->link_reconf_in_progress &=
++								~BIT(hapd->mld_link_id);
++				}
+ 				hostapd_setup_interface_complete(iface, 0);
++			}
+ 
+ 			iface->cac_started = 0;
+ 
+@@ -1386,8 +1420,6 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ 		iface->radar_background.expand_ch = 0;
+ 		hostapd_dfs_update_background_chain(iface);
+ 	} else if (iface->state == HAPD_IFACE_ENABLED) {
+-		int i;
+-
+ 		iface->cac_started = 0;
+ 		/* Clear all CSA flags once channel switch to DFS channel fails */
+ 		for (i = 0; i < iface->num_bss; i++)
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index a89628fe7..7518a8b53 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -5109,6 +5109,7 @@ int hostapd_mld_add_link(struct hostapd_data *hapd)
+ 
+ 	dl_list_add_tail(&mld->links, &hapd->link);
+ 	mld->num_links++;
++	mld->active_links |= BIT(hapd->mld_link_id);
+ 
+ 	wpa_printf(MSG_DEBUG, "AP MLD %s: Link ID %d added. num_links: %d",
+ 		   mld->name, hapd->mld_link_id, mld->num_links);
+@@ -5137,6 +5138,7 @@ int hostapd_mld_remove_link(struct hostapd_data *hapd)
+ 
+ 	dl_list_del(&hapd->link);
+ 	mld->num_links--;
++	mld->active_links &= ~BIT(hapd->mld_link_id);
+ 
+ 	wpa_printf(MSG_DEBUG, "AP MLD %s: Link ID %d removed. num_links: %d",
+ 		   mld->name, hapd->mld_link_id, mld->num_links);
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 52875cca1..668f1c44d 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -550,6 +550,7 @@ struct hostapd_mld {
+ 	u8 refcount;
+ 	bool started;
+ 	u16 link_reconf_in_progress;
++	u16 active_links;
+ 
+ 	struct hostapd_data *fbss;
+ 	struct dl_list links; /* List head of all affiliated links */
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 93bd8255c..2861c09d7 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -989,6 +989,8 @@ void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
+ 	sta->sae->peer_commit_scalar = NULL;
+ 	if (hostapd_is_mld_ap(hapd)) {
+ 		for_each_mld_link(link, hapd) {
++			if (!(hapd->mld->active_links & BIT(link->mld_link_id)))
++				continue;
+ 			wpa_auth_pmksa_add_sae(link->wpa_auth, sta->addr,
+ 					sta->sae->pmk, sta->sae->pmk_len,
+ 					sta->sae->pmkid, sta->sae->akmp);
+@@ -8108,7 +8110,7 @@ u8 * hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
+ 	struct hostapd_iface *iface;
+ 	size_t i;
+ 
+-	if (!hapd->iface || !hapd->iface->interfaces || !hapd->conf->mld_ap)
++	if (!hapd->iface || !hapd->iface->interfaces || !hapd->conf->mld_ap || !hapd->mld)
+ 		return eid;
+ 
+ 	/* TODO: Allow for FILS/Action as well */
+@@ -8119,7 +8121,8 @@ u8 * hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
+ 		iface = hapd->iface->interfaces->iface[i];
+ 
+ 		if (!iface || iface == hapd->iface ||
+-		    hapd->iface->freq == iface->freq)
++		    hapd->iface->freq == iface->freq ||
++		    !(hapd->mld->active_links & BIT(hapd->mld_link_id)))
+ 			continue;
+ 
+ 		eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index 63713bc39..cad0d8437 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -514,7 +514,13 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 	u8 link_id;
+ 	u8 common_info_len;
+ 	u16 mld_cap;
+-	u8 max_simul_links, active_links;
++	u8 max_simul_links, active_links = 0;
++
++	if (hapd->mld && !(hapd->mld->active_links & BIT(hapd->mld_link_id))) {
++		wpa_printf(MSG_ERROR, "MLD: Current link %d is not active for %s",
++			   hapd->mld_link_id, hapd->mld->name);
++		return pos;
++	}
+ 
+ 	/*
+ 	 * As the Multi-Link element can exceed the size of 255 bytes need to
+@@ -573,7 +579,10 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 
+ 	mld_cap = hapd->iface->mld_mld_capa;
+ 	max_simul_links = mld_cap & EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
+-	active_links = hapd->mld->num_links - 1;
++	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++)
++		if (hapd->mld_link_id != link_id &&
++		    (hapd->mld->active_links & BIT(link_id)))
++			active_links++;
+ 
+ 	if (active_links > max_simul_links) {
+ 		wpa_printf(MSG_ERROR,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0118-mtk-hostapd-support-MLD-AP-affiliated-link-removal.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0118-mtk-hostapd-support-MLD-AP-affiliated-link-removal.patch
new file mode 100644
index 0000000..e459fed
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0118-mtk-hostapd-support-MLD-AP-affiliated-link-removal.patch
@@ -0,0 +1,434 @@
+From 2f6ed87f722be77d39188f84158b27945c6f41ef Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 29 Apr 2024 12:04:52 +0800
+Subject: [PATCH 118/126] mtk: hostapd: support MLD AP affiliated link removal
+
+Support ap link removal of MLD reconfiguration. Main changes include:
+- support to trigger it from hostapd_cli, with the following command:
+  hostapd_cli -i ap-mld-1 -l <link_id> link_remove count=10
+
+- handle NL80211_CMD_LINKS_REMOVED event
+- modify some parts that will teardown whole VIF or STA when removing
+  a link
+- handle case that the setup link sta is removed
+
+Note that currently the code only supports to remove one link at one time.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ hostapd/ctrl_iface.c               |  26 ++++++-
+ hostapd/hostapd_cli.c              |   8 +++
+ src/ap/ap_drv_ops.c                |   7 ++
+ src/ap/beacon.c                    |   4 ++
+ src/ap/drv_callbacks.c             |   3 +
+ src/ap/hostapd.c                   | 110 +++++++++++++++++++++++------
+ src/ap/hostapd.h                   |   2 +
+ src/ap/wpa_auth.c                  |   6 ++
+ src/ap/wpa_auth.h                  |   3 +
+ src/drivers/driver.h               |   7 ++
+ src/drivers/driver_nl80211.c       |   5 ++
+ src/drivers/driver_nl80211_event.c |  34 ++++++++-
+ 12 files changed, 188 insertions(+), 27 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index c8456513e..6db9fa617 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -3903,11 +3903,31 @@ static int hostapd_ctrl_iface_disable_mld(struct hostapd_iface *iface)
+ static int hostapd_ctrl_iface_link_remove(struct hostapd_data *hapd, char *cmd,
+ 					  char *buf, size_t buflen)
+ {
++	char *token, *context = NULL;
++	u32 count = 0;
+ 	int ret;
+-	u32 count = atoi(cmd);
+ 
+-	if (!count)
+-		count = 1;
++	while ((token = str_token(cmd, " ", &context))) {
++		if (os_strncmp(token, "count=", 6) == 0) {
++			count = atoi(token + 6);
++			continue;
++		}
++
++		wpa_printf(MSG_ERROR, "CTRL: Invalid LINK_REMOVE parameter: %s",
++			   token);
++		return -1;
++	}
++
++	if (!count) {
++		wpa_printf(MSG_ERROR, "Invalid ap removal count");
++		return -1;
++	}
++
++	/* limit total countdown time to be multiple of second */
++	if ((hapd->iconf->beacon_int * count) % 1000) {
++		wpa_printf(MSG_ERROR, "Total countdown time should be multiple of second");
++		return -1;
++	}
+ 
+ 	ret = hostapd_link_remove(hapd, count);
+ 	if (ret == 0) {
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 12ee0a18f..9dcc0d74b 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1785,6 +1785,12 @@ static int hostapd_cli_cmd_link_add(struct wpa_ctrl *ctrl, int argc,
+ 	return hostapd_cli_cmd(ctrl, "LINK_ADD", 1, argc, argv);
+ }
+ 
++static int hostapd_cli_cmd_link_remove(struct wpa_ctrl *ctrl, int argc,
++				       char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "LINK_REMOVE", 1, argc, argv);
++}
++
+ struct hostapd_cli_cmd {
+ 	const char *cmd;
+ 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -2047,6 +2053,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 		" = <ac> [cwmin=] [cwmax=] [aifs=] [txop_limit=]"},
+ 	{ "link_add", hostapd_cli_cmd_link_add, NULL,
+ 		" = Add a new link to a MLD AP"},
++	{ "link_remove", hostapd_cli_cmd_link_remove, NULL,
++		" [count=<count>] = Remove affiliated link of a MLD AP"},
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index f9ec9a689..8b6aed0c7 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -332,6 +332,13 @@ int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta)
+ 		return 0;
+ 	}
+ 
++	if (hapd->conf->mld_ap && hapd->mld->removed_links) {
++		wpa_printf(MSG_DEBUG,
++			   "%s: Do not update station flags (" MACSTR ")",
++			   " during ap link removal", __func__, MAC2STR(sta->addr));
++		return 0;
++	}
++
+ 	flags_or = total_flags & set_flags;
+ 	flags_and = total_flags | ~set_flags;
+ 	return hostapd_sta_set_flags(hapd, sta->addr, total_flags,
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 8a9569549..5293ee4c1 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -2794,6 +2794,10 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
+ 	if (hapd->conf->mld_ap && !hapd->mld->started && hapd->beacon_set_done)
+ 		return 0;
+ 
++	/* skip setting beacon during ap link removal */
++	if (hapd->conf->mld_ap && hapd->mld->removed_links)
++		return 0;
++
+ 	if (!hapd->drv_priv) {
+ 		wpa_printf(MSG_ERROR, "Interface is disabled");
+ 		return -1;
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 7ae26ab7c..9deb87c3d 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2919,6 +2919,9 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ 			   hapd->conf->iface);
+ 		hostapd_event_color_change(hapd, true);
+ 		break;
++	case EVENT_LINK_RECONFIG:
++		hostapd_link_remove_cb(hapd, data->reconfig_info.removed_links);
++		break;
+ #endif /* CONFIG_IEEE80211AX */
+ 	default:
+ 		wpa_printf(MSG_DEBUG, "Unknown event %d", event);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 7518a8b53..e34bc1fa8 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -428,30 +428,74 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
+ 
+ #define TU_TO_USEC(_val) ((_val) * 1024)
+ 
+-static void hostapd_link_remove_timeout_handler(void *eloop_data,
+-						void *user_ctx)
++static void hostapd_switch_sta_setup_link(struct hostapd_data *hapd)
+ {
+-	struct hostapd_data *hapd = (struct hostapd_data *) eloop_data;
++	struct hostapd_data *p_hapd, *new_setup_hapd = NULL;
++	struct sta_info *cur, *sta;
++
++	/* use the current least link_id as new setup link */
++	for_each_mld_link(p_hapd, hapd) {
++		if (p_hapd == hapd)
++			continue;
++		if (!new_setup_hapd || p_hapd->mld_link_id < new_setup_hapd->mld_link_id)
++			new_setup_hapd = p_hapd;
++	}
+ 
+-	if (hapd->eht_mld_link_removal_count == 0)
++	if (!new_setup_hapd)
+ 		return;
+-	hapd->eht_mld_link_removal_count--;
+ 
+-	wpa_printf(MSG_DEBUG, "MLD: Remove link_id=%u in %u beacons",
+-		   hapd->mld_link_id,
+-		   hapd->eht_mld_link_removal_count);
++	sta = hapd->sta_list;
++	while (sta) {
++		struct sta_info *new_setup_sta;
+ 
+-	ieee802_11_set_beacon(hapd);
++		cur = sta;
++		sta = sta->next;
++		if (hostapd_sta_is_link_sta(hapd, cur))
++			continue;
+ 
+-	if (!hapd->eht_mld_link_removal_count) {
+-		hostapd_free_link_stas(hapd);
+-		hostapd_disable_iface(hapd->iface);
+-		return;
++		new_setup_sta = ap_get_sta(new_setup_hapd, cur->addr);
++		if (!new_setup_sta)
++			continue;
++
++		new_setup_sta->mld_assoc_link_id = new_setup_hapd->mld_link_id;
++		new_setup_sta->mld_assoc_sta = new_setup_sta;
++		switch_setup_wpa_auth(new_setup_sta->wpa_sm,
++				      new_setup_hapd->wpa_auth);
++		new_setup_sta->flags = cur->flags;
++		cur->flags &= ~(WLAN_STA_AUTH | WLAN_STA_AUTHORIZED);
++
++		for_each_mld_link(p_hapd, hapd) {
++			struct sta_info *p_sta;
++
++			p_sta = ap_get_sta(p_hapd, new_setup_sta->addr);
++			if (!p_sta)
++				continue;
++
++			p_sta->mld_assoc_link_id = new_setup_hapd->mld_link_id;
++			p_sta->mld_assoc_sta = new_setup_sta;
++		}
++
++		wpa_printf(MSG_INFO, "Switch assoc link of station " MACSTR " from %u to %u",
++			   MAC2STR(cur->addr), hapd->mld_link_id, new_setup_hapd->mld_link_id);
+ 	}
++}
+ 
+-	eloop_register_timeout(0, TU_TO_USEC(hapd->iconf->beacon_int),
+-			       hostapd_link_remove_timeout_handler,
+-			       hapd, NULL);
++static void hostapd_link_remove_timeout_handler(void *eloop_data,
++						void *user_ctx)
++{
++	struct hostapd_data *hapd = (struct hostapd_data *) eloop_data;
++	struct hostapd_mld *mld = hapd->mld;
++	u8 link_id = hapd->mld_link_id;
++
++	if (!mld || !mld->removed_links)
++		return;
++
++	wpa_printf(MSG_DEBUG, "MLD: Remove link_id=%u", hapd->mld_link_id);
++
++	hostapd_switch_sta_setup_link(hapd);
++	hostapd_free_link_stas(hapd);
++	hostapd_disable_iface(hapd->iface);
++	mld->removed_links &= ~BIT(link_id);
+ }
+ 
+ 
+@@ -465,16 +509,34 @@ int hostapd_link_remove(struct hostapd_data *hapd, u32 count)
+ 		   hapd->mld_link_id, count);
+ 
+ 	hapd->eht_mld_link_removal_count = count;
+-	hapd->eht_mld_bss_param_change++;
+-
+-	eloop_register_timeout(0, TU_TO_USEC(hapd->iconf->beacon_int),
+-			       hostapd_link_remove_timeout_handler,
+-			       hapd, NULL);
++	ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_RECONFIG);
+ 
+ 	ieee802_11_set_beacon(hapd);
++
++	hapd->eht_mld_link_removal_count = 0;
++	hapd->mld->removed_links |= BIT(hapd->mld_link_id);
+ 	return 0;
+ }
+ 
++
++void hostapd_link_remove_cb(struct hostapd_data *hapd, u16 removed_links)
++{
++	struct hostapd_data *link;
++
++	if (!hapd->conf->mld_ap)
++		return;
++
++	for_each_mld_link(link, hapd) {
++		if (!(BIT(link->mld_link_id) & removed_links))
++			continue;
++
++		link->eht_mld_bss_critical_update = 0;
++		eloop_register_timeout(0, 0,
++				       hostapd_link_remove_timeout_handler,
++				       link, NULL);
++	}
++}
++
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
+ 
+@@ -848,7 +910,9 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
+ void hostapd_bss_deinit_no_free(struct hostapd_data *hapd)
+ {
+ 	hostapd_free_stas(hapd);
+-	hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
++	/* do not flush stations during ap link removal */
++	if (!hapd->conf->mld_ap || !hapd->mld->removed_links)
++		hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
+ #ifdef CONFIG_WEP
+ 	hostapd_clear_wep(hapd);
+ #endif /* CONFIG_WEP */
+@@ -1334,7 +1398,7 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
+ 	}
+ 
+ 	if (flush_old_stations && !conf->start_disabled &&
+-	    conf->broadcast_deauth) {
++	    conf->broadcast_deauth && (hapd->conf->mld_ap && !hapd->mld->started)) {
+ 		u8 addr[ETH_ALEN];
+ 
+ 		/* Should any previously associated STA not have noticed that
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 668f1c44d..c6c286304 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -551,6 +551,7 @@ struct hostapd_mld {
+ 	bool started;
+ 	u16 link_reconf_in_progress;
+ 	u16 active_links;
++	u16 removed_links;
+ 
+ 	struct hostapd_data *fbss;
+ 	struct dl_list links; /* List head of all affiliated links */
+@@ -922,6 +923,7 @@ int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd);
+ struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
+ 					       u8 link_id);
+ int hostapd_link_remove(struct hostapd_data *hapd, u32 count);
++void hostapd_link_remove_cb(struct hostapd_data *hapd, u16 removed_links);
+ bool hostapd_is_ml_partner(struct hostapd_data *hapd1,
+ 			   struct hostapd_data *hapd2);
+ u8 hostapd_get_mld_id(struct hostapd_data *hapd);
+diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
+index 6cb5a4be7..8fb52b697 100644
+--- a/src/ap/wpa_auth.c
++++ b/src/ap/wpa_auth.c
+@@ -7428,3 +7428,9 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm,
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ }
++
++void switch_setup_wpa_auth(struct wpa_state_machine *sm,
++			   struct wpa_authenticator *wpa_auth)
++{
++	sm->wpa_auth = wpa_auth;
++}
+diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
+index b22c4199b..4cd46849a 100644
+--- a/src/ap/wpa_auth.h
++++ b/src/ap/wpa_auth.h
+@@ -681,4 +681,7 @@ void wpa_release_link_auth_ref(struct wpa_state_machine *sm,
+ 		    sm->mld_links[link_id].wpa_auth &&			\
+ 		    sm->wpa_auth != sm->mld_links[link_id].wpa_auth)
+ 
++void switch_setup_wpa_auth(struct wpa_state_machine *sm,
++			   struct wpa_authenticator *wpa_auth);
++
+ #endif /* WPA_AUTH_H */
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 498eff91f..eb2a48381 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -7062,6 +7062,13 @@ union wpa_event_data {
+ 		u8 valid_links;
+ 		struct t2lm_mapping t2lmap[MAX_NUM_MLD_LINKS];
+ 	} t2l_map_info;
++
++	/**
++	 * struct reconfig_info - Data for EVENT_LINK_RECONFIG
++	 */
++	struct reconfig_info {
++		u16 removed_links;
++	} reconfig_info;
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 639c5e7e1..1d334b75f 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -1117,6 +1117,11 @@ static void nl80211_put_wiphy_data_ap(struct i802_bss *bss)
+ 
+ 	if (w == NULL)
+ 		return;
++
++	/* do not clear wiphy data if there are still more than one links */
++	if (bss->valid_links && (bss->valid_links & (bss->valid_links - 1)))
++		return;
++
+ 	bss->wiphy_data = NULL;
+ 	dl_list_del(&bss->wiphy_list);
+ 
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index efdb8ef7f..24a4bf3cf 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -3930,6 +3930,38 @@ static void nl80211_obss_color_event(struct i802_bss *bss,
+ 
+ #endif /* CONFIG_IEEE80211AX */
+ 
++static void nl80211_links_removed(struct wpa_driver_nl80211_data *drv,
++				  struct nlattr **tb)
++{
++	struct nlattr *link_attr, *link_data[NL80211_ATTR_MAX + 1];
++	static struct nla_policy link_policy[NL80211_ATTR_MAX + 1] = {
++		[NL80211_ATTR_MLO_LINK_ID] = { .type = NLA_U8 },
++	};
++	union wpa_event_data data = {};
++	int rem;
++
++	if (!tb[NL80211_ATTR_MLO_LINKS])
++		return;
++
++	nla_for_each_nested(link_attr, tb[NL80211_ATTR_MLO_LINKS], rem) {
++		u8 link_id;
++
++		if (nla_parse_nested(link_data, NL80211_ATTR_MAX,
++				     link_attr, link_policy) != 0)
++			continue;
++
++		if (!link_data[NL80211_ATTR_MLO_LINK_ID])
++			continue;
++
++		link_id = nla_get_u8(link_data[NL80211_ATTR_MLO_LINK_ID]);
++		if (link_id >= MAX_NUM_MLD_LINKS)
++			continue;
++		data.reconfig_info.removed_links |= BIT(link_id);
++	}
++
++	wpa_supplicant_event(drv->ctx, EVENT_LINK_RECONFIG, &data);
++}
++
+ 
+ static void do_process_drv_event(struct i802_bss *bss, int cmd,
+ 				 struct nlattr **tb)
+@@ -4195,7 +4227,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
+ 		break;
+ #endif /* CONFIG_IEEE80211AX */
+ 	case NL80211_CMD_LINKS_REMOVED:
+-		wpa_supplicant_event(drv->ctx, EVENT_LINK_RECONFIG, NULL);
++		nl80211_links_removed(drv, tb);
+ 		break;
+ 	default:
+ 		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0119-mtk-hostapd-add-A-TTLM-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0119-mtk-hostapd-add-A-TTLM-support.patch
new file mode 100644
index 0000000..a6bdd09
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0119-mtk-hostapd-add-A-TTLM-support.patch
@@ -0,0 +1,760 @@
+From 8aabcfee2bb77e19fc592c27f1121c5725ee665f Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Wed, 3 Jul 2024 15:39:53 +0800
+Subject: [PATCH 119/126] mtk: hostapd: add A-TTLM support
+
+Add a data structure for AP MLD's Advertised Tid-to-Link Mapping (A-TTLM).
+Since it is MLD-level, this commit also adds it into struct hostapd_mld.
+
+There should be 'curr_attlm' and 'new_attlm', which means 2 TTLMs can be
+advertised at the same time ('curr_attlm' is used currently while
+'new_attlm' is about to be used)
+However, since the FW only support 1 attlm now, there is currently only
+'new_attlm'.
+
+There are 3 A-TTLM events from the driver, and hostap should handle them
+accordingly
+1. A-TTLM started: link(s) sould start to advertise TTLM in the beacon.
+2. A-TTLM switch time expired: enabled link(s) keep advertising TTLM in
+   the beacon with switch time excluded, whiel disabled link(s) should
+   stop TX/RX, including beacon
+3. A-TTLM ended: all links stop advertising TTLM in the beacon, and
+   disabled link recover to TX/RX.
+
+This commit adds the support to set ATTLM, from hostapd_cli to driver.
+Setting an ATTLM requires 3 parameters
+1. disabled_links: disabled link ID bitmap
+2. switch_time: how much time it takes to start the A-TTLM (in ms)
+3. duration: how long the A-TTLM lasts (in ms)
+
+Below is a hostapd_cli example that requires an A-TTLM starts to disable
+link_id=1 after 5 seconds and last for 20 seconds:
+$ hostapd_cli -i ap-mld-1 set_attlm disabled_links=2 switch_time=5000
+duration=20000
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ hostapd/ctrl_iface.c               | 66 +++++++++++++++++++++++++++++
+ hostapd/hostapd_cli.c              |  8 ++++
+ src/ap/ap_drv_ops.c                |  9 ++++
+ src/ap/ap_drv_ops.h                |  1 +
+ src/ap/beacon.c                    |  4 ++
+ src/ap/drv_callbacks.c             | 53 +++++++++++++++++++++++
+ src/ap/hostapd.c                   |  8 ++++
+ src/ap/hostapd.h                   |  4 ++
+ src/ap/ieee802_11.c                |  8 ++++
+ src/ap/ieee802_11.h                |  1 +
+ src/ap/ieee802_11_eht.c            | 68 +++++++++++++++++++++++++++++-
+ src/common/ieee802_11_defs.h       | 13 ++++++
+ src/drivers/driver.h               | 56 ++++++++++++++++++++++++
+ src/drivers/driver_nl80211.c       | 33 +++++++++++++++
+ src/drivers/driver_nl80211_event.c | 54 ++++++++++++++++++++++++
+ src/drivers/nl80211_copy.h         | 24 +++++++++++
+ 16 files changed, 409 insertions(+), 1 deletion(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 6db9fa617..2cd5487ca 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4039,6 +4039,68 @@ out:
+ 	return ret;
+ }
+ 
++static int hostapd_ctrl_iface_set_attlm(struct hostapd_data *hapd, char *cmd,
++					char *buf, size_t buflen)
++{
++#define MAX_SWITCH_TIME_MS 30000
++#define MAX_DURATION_MS 16000000
++	struct attlm_settings *attlm;
++	struct hostapd_data *h;
++	char *token, *context = NULL;
++	u16 switch_time, disabled_links, valid_links = 0;
++	u32 duration;
++	int ret, i;
++
++	if (!hapd->conf->mld_ap || !hapd->mld)
++		return -1;
++
++	attlm = &hapd->mld->new_attlm;
++	if (attlm->valid) {
++		wpa_printf(MSG_ERROR, "Busy: A-TTLM is on-going");
++		return -1;
++	}
++
++	for_each_mld_link(h, hapd)
++		valid_links |= BIT(h->mld_link_id);
++
++	while ((token = str_token(cmd, " ", &context))) {
++		if (os_strncmp(token, "switch_time=", 12) == 0) {
++			switch_time = atoi(token + 12);
++			if (switch_time > 0 && switch_time <= MAX_SWITCH_TIME_MS)
++				continue;
++		}
++
++		if (os_strncmp(token, "disabled_links=", 15) == 0) {
++			disabled_links = atoi(token + 15);
++
++			if ((disabled_links & valid_links) &&
++			    !(disabled_links & ~valid_links))
++				continue;
++		}
++
++		if (os_strncmp(token, "duration=", 9) == 0) {
++			duration = atoi(token + 9);
++			if (duration > 0 && duration <= MAX_DURATION_MS)
++				continue;
++		}
++
++		wpa_printf(MSG_INFO, "CTRL: Invalid SET_ATTLM parameter: %s",
++			   token);
++		return -1;
++	}
++
++	wpa_printf(MSG_DEBUG,
++		   "MLD: set A-TTLM disabled_links=%u, switch_time=%u, duration=%u",
++		   disabled_links, switch_time, duration);
++
++	attlm->valid = true;
++	attlm->direction = IEEE80211_TTLM_DIRECTION_BOTH;
++	attlm->duration = duration;
++	attlm->switch_time = switch_time;
++	attlm->disabled_links = hapd->conf->mld_allowed_links & disabled_links;
++
++	return hostapd_mld_set_attlm(hapd);
++}
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
+ 
+@@ -6166,6 +6228,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		if (hostapd_ctrl_iface_link_add(hapd, buf + 9,
+ 						reply, reply_size))
+ 			reply_len = -1;
++	} else if (os_strncmp(buf, "SET_ATTLM ", 10) == 0) {
++		if (hostapd_ctrl_iface_set_attlm(hapd, buf + 10, reply,
++						 reply_size))
++			reply_len = -1;
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
+ 	} else if (os_strncmp(buf, "SET_EDCCA ", 10) == 0) {
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 9dcc0d74b..f81211ba4 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1791,6 +1791,12 @@ static int hostapd_cli_cmd_link_remove(struct wpa_ctrl *ctrl, int argc,
+ 	return hostapd_cli_cmd(ctrl, "LINK_REMOVE", 1, argc, argv);
+ }
+ 
++static int hostapd_cli_cmd_set_attlm(struct wpa_ctrl *ctrl, int argc,
++				     char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "SET_ATTLM", 1, argc, argv);
++}
++
+ struct hostapd_cli_cmd {
+ 	const char *cmd;
+ 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -2055,6 +2061,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 		" = Add a new link to a MLD AP"},
+ 	{ "link_remove", hostapd_cli_cmd_link_remove, NULL,
+ 		" [count=<count>] = Remove affiliated link of a MLD AP"},
++	{ "set_attlm", hostapd_cli_cmd_set_attlm, NULL,
++		" = Disable the affiliated AP of a MLD AP" },
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 8b6aed0c7..a25a67cdd 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -619,6 +619,15 @@ int hostapd_if_link_remove(struct hostapd_data *hapd,
+ 	return hapd->driver->link_remove(hapd->drv_priv, type, ifname,
+ 					 hapd->mld_link_id);
+ }
++
++
++int hostapd_drv_set_attlm(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->drv_priv || !hapd->driver->set_attlm)
++		return -1;
++
++	return hapd->driver->set_attlm(hapd->drv_priv, &hapd->mld->new_attlm);
++}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ 
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index e74284b96..7f108bc1d 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -68,6 +68,7 @@ int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ int hostapd_if_link_remove(struct hostapd_data *hapd,
+ 			   enum wpa_driver_if_type type,
+ 			   const char *ifname, u8 link_id);
++int hostapd_drv_set_attlm(struct hostapd_data *hapd);
+ int hostapd_set_ieee8021x(struct hostapd_data *hapd,
+ 			  struct wpa_bss_params *params);
+ int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 5293ee4c1..a5a885213 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -943,6 +943,9 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
+ 
+ 		pos = hostapd_eid_eht_capab(hapd, pos, IEEE80211_MODE_AP);
+ 		pos = hostapd_eid_eht_operation(hapd, pos);
++
++		if (!params->is_ml_sta_info)
++			pos = hostapd_eid_eht_attlm(hapd, pos);
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+@@ -2536,6 +2539,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ 		tailpos = hostapd_eid_eht_capab(hapd, tailpos,
+ 						IEEE80211_MODE_AP);
+ 		tailpos = hostapd_eid_eht_operation(hapd, tailpos);
++		tailpos = hostapd_eid_eht_attlm(hapd, tailpos);
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 9deb87c3d..17dc09807 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -1368,6 +1368,54 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
+ }
+ 
+ 
++#ifdef CONFIG_IEEE80211BE
++void hostapd_event_attlm(struct hostapd_data *hapd, struct attlm_event *attlm_event)
++{
++	struct hostapd_mld *mld = hapd->mld;
++	struct hostapd_data *p_hapd;
++	bool mld_indicate_disabled = false;
++
++	if (!hapd->conf->mld_ap || !mld)
++		return;
++
++	wpa_printf(MSG_DEBUG, "A-TTLM event");
++	/*
++	 * T0: driver notifies A-TTLM has started and reports Switch Time TSF in TUs
++	 * T1: driver notifies Switch Time Expiry of a started A-TTLM
++	 * T2: driver notifies Duration Expiry of a started A-TTLM.
++	 */
++	switch (attlm_event->event) {
++		case EVENT_ATTLM_STARTED:
++			ieee802_11_set_bss_critical_update(hapd,
++						BSS_CRIT_UPDATE_EVENT_ATTLM);
++			mld->new_attlm.switch_time_tsf_tu =
++						attlm_event->switch_time_tsf_tu;
++			break;
++		case EVENT_ATTLM_SWITCH_TIME_EXPIRED:
++			mld_indicate_disabled = true;
++			mld->new_attlm.switch_time_tsf_tu = 0;
++			os_get_reltime(&mld->new_attlm.start_time);
++			break;
++		case EVENT_ATTLM_END:
++			mld->new_attlm.valid = false;
++			break;
++		default:
++			wpa_printf(MSG_DEBUG, "Unsupported A-TTLM event");
++			return;
++	}
++
++	for_each_mld_link(p_hapd, hapd) {
++		if (mld->new_attlm.disabled_links & BIT(p_hapd->mld_link_id))
++			p_hapd->conf->mld_indicate_disabled =
++							mld_indicate_disabled;
++	}
++
++	ieee802_11_set_beacon(hapd);
++	hapd->eht_mld_bss_critical_update = 0;
++}
++#endif /* CONFIG_IEEE80211BE */
++
++
+ void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
+ 					 const u8 *addr, int reason_code)
+ {
+@@ -2755,6 +2803,11 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ 					data->ch_switch.punct_bitmap,
+ 					event == EVENT_CH_SWITCH);
+ 		break;
++	case EVENT_ATTLM:
++#ifdef CONFIG_IEEE80211BE
++		hostapd_event_attlm(hapd, &data->attlm_event);
++#endif /* CONFIG_IEEE80211BE */
++		break;
+ 	case EVENT_CONNECT_FAILED_REASON:
+ 		if (!data)
+ 			break;
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index e34bc1fa8..9388f6070 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -537,6 +537,14 @@ void hostapd_link_remove_cb(struct hostapd_data *hapd, u16 removed_links)
+ 	}
+ }
+ 
++
++int hostapd_mld_set_attlm(struct hostapd_data *hapd)
++{
++	if (!hapd->drv_priv)
++		return -1;
++
++	return hostapd_drv_set_attlm(hapd);
++}
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
+ 
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index c6c286304..f69fa0062 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -553,6 +553,7 @@ struct hostapd_mld {
+ 	u16 active_links;
+ 	u16 removed_links;
+ 
++	struct attlm_settings new_attlm;
+ 	struct hostapd_data *fbss;
+ 	struct dl_list links; /* List head of all affiliated links */
+ 
+@@ -924,6 +925,7 @@ struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
+ 					       u8 link_id);
+ int hostapd_link_remove(struct hostapd_data *hapd, u32 count);
+ void hostapd_link_remove_cb(struct hostapd_data *hapd, u16 removed_links);
++int hostapd_mld_set_attlm(struct hostapd_data *hapd);
+ bool hostapd_is_ml_partner(struct hostapd_data *hapd1,
+ 			   struct hostapd_data *hapd2);
+ u8 hostapd_get_mld_id(struct hostapd_data *hapd);
+@@ -937,6 +939,8 @@ int hostapd_fill_cca_settings(struct hostapd_data *hapd,
+ 
+ #ifdef CONFIG_IEEE80211BE
+ 
++void hostapd_event_attlm(struct hostapd_data *hapd, struct attlm_event *attlm_event);
++
+ bool hostapd_mld_is_first_bss(struct hostapd_data *hapd);
+ 
+ #define for_each_mld_link(partner, self) \
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 2861c09d7..886a21a66 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -5088,6 +5088,7 @@ rsnxe_done:
+ 			p = hostapd_eid_eht_ml_assoc(hapd, sta, p);
+ 		p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
+ 		p = hostapd_eid_eht_operation(hapd, p);
++		p = hostapd_eid_eht_attlm(hapd, p);
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+@@ -6343,6 +6344,13 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+ 		wpa_printf(MSG_DEBUG, "MGMT: Drop the frame - MLD not ready");
+ 		return 1;
+ 	}
++
++	if (hapd->conf->mld_ap && hapd->mld->new_attlm.valid &&
++	    !hapd->mld->new_attlm.switch_time_tsf_tu &&
++	    (hapd->mld->new_attlm.disabled_links & BIT(hapd->mld_link_id))) {
++		wpa_printf(MSG_DEBUG, "MGMT: Drop the frame - Disabled link");
++		return 1;
++	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ 	if (fi && fi->freq)
+diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
+index 40301bce9..efaa20c86 100644
+--- a/src/ap/ieee802_11.h
++++ b/src/ap/ieee802_11.h
+@@ -243,6 +243,7 @@ u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
+ 			   enum ieee80211_op_mode opmode);
+ u8 * hostapd_eid_non_inheritance(struct hostapd_data *hapd, u8 *eid);
+ u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid);
++u8 * hostapd_eid_eht_attlm(struct hostapd_data *hapd, u8 *eid);
+ u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ 		       enum ieee80211_op_mode opmode,
+ 		       const u8 *he_capab, size_t he_capab_len,
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index cad0d8437..2ed9414b8 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -595,7 +595,7 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 	mld_cap |= active_links & EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
+ 
+ 	/* TODO: Advertise T2LM based on driver support as well */
+-	mld_cap &= ~EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_NEG_SUPP_MSK;
++	mld_cap |= EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_ALL_TO_ALL;
+ 
+ 	wpa_printf(MSG_DEBUG, "MLD: MLD Capabilities and Operations=0x%x",
+ 		   mld_cap);
+@@ -824,6 +824,72 @@ static u8 * hostapd_eid_eht_reconf_ml(struct hostapd_data *hapd, u8 *eid)
+ }
+ 
+ 
++u8 * hostapd_eid_eht_attlm(struct hostapd_data *hapd, u8 *eid)
++{
++	struct attlm_settings *attlm;
++	struct os_reltime now, res;
++	int i;
++	u16 control = 0;
++	u8 *pos = eid;
++	u16 enabled_links;
++
++	if (!hapd->conf->mld_ap)
++		return eid;
++
++	attlm = &hapd->mld->new_attlm;
++	if (!attlm || !attlm->valid)
++		return eid;
++
++	/* The length will be set at the end */
++	*pos++ = WLAN_EID_EXTENSION;
++	*pos++ = 0;
++	*pos++ = WLAN_EID_EXT_TID_TO_LINK_MAPPING;
++
++	/* Set the A-TTLM Control field */
++	control = (IEEE80211_TTLM_CONTROL_DIRECTION & attlm->direction) |
++		  IEEE80211_TTLM_CONTROL_EXPECTED_DUR_PRESENT |
++		  IEEE80211_TTLM_CONTROL_INDICATOR;
++
++	if (attlm->switch_time_tsf_tu != 0)
++		control |= IEEE80211_TTLM_CONTROL_SWITCH_TIME_PRESENT;
++
++	WPA_PUT_LE16(pos, control);
++	pos += 2;
++
++	/* switch time & expected duration */
++	if (attlm->switch_time_tsf_tu != 0) {
++		WPA_PUT_LE16(pos, attlm->switch_time_tsf_tu);
++		pos += 2;
++
++		WPA_PUT_LE24(pos, (attlm->duration * 1000) >> 10);
++		pos += 3;
++	} else {
++		u32 diff;
++
++		os_get_reltime(&now);
++		os_reltime_sub(&now, &attlm->start_time, &res);
++		diff = (u32)os_reltime_in_ms(&res);
++
++		if (attlm->duration <= diff)
++			return eid;
++
++		WPA_PUT_LE24(pos, ((attlm->duration - diff) * 1000) >> 10);
++		pos += 3;
++	}
++
++	/* Link Mapping of each TID (0 - 7) */
++	enabled_links = hapd->conf->mld_allowed_links & ~attlm->disabled_links;
++	for (i = 0; i < 8; i++) {
++		WPA_PUT_LE16(pos, enabled_links);
++		pos += 2;
++	}
++
++	eid[1] = pos - eid - 2;
++
++	return pos;
++}
++
++
+ static size_t hostapd_eid_eht_ml_len(struct mld_info *info,
+ 				     bool include_mld_id,
+ 				     u8 eml_disable)
+diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
+index d93fa6660..afcc2f861 100644
+--- a/src/common/ieee802_11_defs.h
++++ b/src/common/ieee802_11_defs.h
+@@ -2912,6 +2912,19 @@ enum ieee80211_eht_ml_sub_elem {
+ 	EHT_ML_SUB_ELEM_FRAGMENT = 254,
+ };
+ 
++/* IEEE P802.11be/D5.0, 9.4.2.314 - TID-to-Link Mapping control */
++#define IEEE80211_TTLM_CONTROL_DIRECTION		0x0003
++#define IEEE80211_TTLM_CONTROL_DEF_LINK_MAP		0x0004
++#define IEEE80211_TTLM_CONTROL_SWITCH_TIME_PRESENT	0x0008
++#define IEEE80211_TTLM_CONTROL_EXPECTED_DUR_PRESENT	0x0010
++#define IEEE80211_TTLM_CONTROL_LINK_MAP_SIZE		0x0020
++#define IEEE80211_TTLM_CONTROL_INDICATOR		0xff00
++
++/* TTLM direction */
++#define IEEE80211_TTLM_DIRECTION_DOWN		0
++#define IEEE80211_TTLM_DIRECTION_UP		1
++#define IEEE80211_TTLM_DIRECTION_BOTH		2
++
+ /* IEEE P802.11ay/D4.0, 9.4.2.251 - EDMG Operation element */
+ #define EDMG_BSS_OPERATING_CHANNELS_OFFSET	6
+ #define EDMG_OPERATING_CHANNEL_WIDTH_OFFSET	7
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index eb2a48381..fe327e560 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -2805,6 +2805,29 @@ struct cca_settings {
+ 	int link_id;
+ };
+ 
++#ifdef CONFIG_IEEE80211BE
++/**
++ * struct attlm_settings - Setting for Advertised Tid-to-Link Mapping
++ * @valid: whether this A-TTLM is still valid
++ * @direction: direction of this A-TTLM
++ * @disabled_links: disabled link ID bitmap
++ * @switch_time: duration in ms to establish the A-TTLM
++ * @switch_time_tsf_tu: time in TUs that the A-TTLM is established. It should be
++ * the bits 10 to 25 of the TSF
++ * @duration_tu: duration in ms that the A-TTLM lasts
++ * @start_time: the relative time that this A-TTLM is entablished
++ */
++struct attlm_settings {
++	bool valid;
++	u8 direction;
++	u16 disabled_links;
++	u16 switch_time;
++	u16 switch_time_tsf_tu;
++	u32 duration;
++	struct os_reltime start_time;
++};
++#endif /* CONFIG_IEEE80211BE */
++
+ /* TDLS peer capabilities for send_tdls_mgmt() */
+ enum tdls_peer_capability {
+ 	TDLS_PEER_HT = BIT(0),
+@@ -5239,6 +5262,14 @@ struct wpa_driver_ops {
+ 	int (*link_remove)(void *priv, enum wpa_driver_if_type type,
+ 			   const char *ifname, u8 link_id);
+ 
++	/**
++	 * set_attlm - Set AP MLD advertised Tid-to-Link Mapping
++	 * @priv: Private driver interface data
++	 * @attlm: setting of Tid-to-Link Mapping
++	 * Returns: 0 on success, negative value on failure
++	 */
++	int (*set_attlm)(void *priv, struct attlm_settings *attlm);
++
+ 	/**
+ 	 * is_drv_shared - Check whether the driver interface is shared
+ 	 * @priv: Private driver interface data from init()
+@@ -6025,6 +6056,16 @@ enum wpa_event_type {
+ 	 */
+ 	EVENT_LINK_CH_SWITCH_STARTED,
+ 
++	/**
++	 * EVENT_ATTLM - MLD AP Advertised Tid-to-Link Mapping event
++	 *
++	 * This event is used by the driver to indicate the state transition of
++	 * A-TTLM.
++	 *
++	 * Described in wpa_event_data.attlm_event
++	 */
++	EVENT_ATTLM,
++
+ 	/**
+ 	 * EVENT_TID_LINK_MAP - MLD event to set TID-to-link mapping
+ 	 *
+@@ -6834,6 +6875,21 @@ union wpa_event_data {
+ 		u16 punct_bitmap;
+ 	} ch_switch;
+ 
++	/**
++	 * struct attlm_event
++	 * @switch_time_tsf_tu: the TSF of switch time in unit of TUs
++	 * @started: the ATTLM is started or has been done.
++	 * @switch_time_expired: the switch time has expired
++	 */
++	struct attlm_event {
++		enum {
++			EVENT_ATTLM_STARTED,
++			EVENT_ATTLM_SWITCH_TIME_EXPIRED,
++			EVENT_ATTLM_END
++		} event;
++		u16 switch_time_tsf_tu;
++	} attlm_event;
++
+ 	/**
+ 	 * struct connect_failed - Data for EVENT_CONNECT_FAILED_REASON
+ 	 * @addr: Remote client address
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 1d334b75f..05b231c52 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -11020,6 +11020,38 @@ static bool nl80211_is_drv_shared(void *priv, void *bss_ctx)
+ 	return true;
+ }
+ 
++
++static int nl80211_set_attlm(void *priv, struct attlm_settings *attlm)
++{
++	struct nl_msg *msg;
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	int ret = -ENOBUFS;
++
++	wpa_printf(MSG_DEBUG, "nl80211: Set A-TTLM");
++
++	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_ATTLM)) ||
++	    nla_put_u16(msg, NL80211_ATTR_MLO_LINK_DISABLED_BMP,
++			attlm->disabled_links) ||
++	    nla_put_u16(msg, NL80211_ATTR_MLO_ATTLM_SWITCH_TIME,
++			attlm->switch_time) ||
++	    nla_put_u32(msg, NL80211_ATTR_MLO_ATTLM_DURATION,
++			attlm->duration))
++		goto error;
++
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret) {
++		wpa_printf(MSG_DEBUG,
++			   "nl80211: disable link failed err=%d (%s)",
++			   ret, strerror(-ret));
++	}
++
++	return ret;
++error:
++	nlmsg_free(msg);
++	wpa_printf(MSG_DEBUG, "nl80211: Could not build link disabling request");
++	return ret;
++}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ 
+@@ -15717,6 +15749,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.link_add = nl80211_link_add,
+ #ifdef CONFIG_IEEE80211BE
+ 	.link_remove = driver_nl80211_link_remove,
++	.set_attlm = nl80211_set_attlm,
+ 	.is_drv_shared = nl80211_is_drv_shared,
+ 	.link_sta_remove = wpa_driver_nl80211_link_sta_remove,
+ #endif /* CONFIG_IEEE80211BE */
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 24a4bf3cf..863c4eb65 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -188,6 +188,8 @@ static const char * nl80211_command_to_string(enum nl80211_commands cmd)
+ 	C2S(NL80211_CMD_SET_HW_TIMESTAMP)
+ 	C2S(NL80211_CMD_LINKS_REMOVED)
+ 	C2S(NL80211_CMD_SET_TID_TO_LINK_MAPPING)
++	C2S(NL80211_CMD_ATTLM_EVENT)
++	C2S(NL80211_CMD_SET_ATTLM)
+ 	C2S(__NL80211_CMD_AFTER_LAST)
+ 	}
+ #undef C2S
+@@ -1313,6 +1315,53 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
+ }
+ 
+ 
++static void mlme_event_attlm(struct wpa_driver_nl80211_data *drv,
++			     struct nlattr *ifindex,
++			     struct nlattr *event,
++			     struct nlattr *switch_time_tsf_tu)
++{
++	enum nl80211_attlm_event event_type;
++	union wpa_event_data data;
++	struct i802_bss *bss;
++	int ifidx;
++
++	ifidx = nla_get_u32(ifindex);
++	bss = get_bss_ifindex(drv, ifidx);
++	if (bss == NULL) {
++		wpa_printf(MSG_WARNING,
++			   "nl80211: Unknown ifindex (%d) for A-TTLM, ignoring",
++			   ifidx);
++		return;
++	}
++
++	if (!event)
++		return;
++
++	wpa_printf(MSG_DEBUG, "nl80211: %s: A-TTLM event", bss->ifname);
++
++	data.attlm_event.switch_time_tsf_tu = switch_time_tsf_tu ?
++					nla_get_u16(switch_time_tsf_tu) : 0;
++	event_type = nla_get_u32(event);
++	switch (event_type) {
++		case NL80211_ATTLM_STARTED:
++			data.attlm_event.event = EVENT_ATTLM_STARTED;
++			break;
++		case NL80211_ATTLM_SWITCH_TIME_EXPIRED:
++			data.attlm_event.event = EVENT_ATTLM_SWITCH_TIME_EXPIRED;
++			break;
++		case NL80211_ATTLM_END:
++			data.attlm_event.event = EVENT_ATTLM_END;
++			break;
++		default:
++			wpa_printf(MSG_DEBUG,
++				   "nl80211: Unsupported A-TTLM event");
++			return;
++	}
++
++	wpa_supplicant_event(bss->ctx, EVENT_ATTLM, &data);
++}
++
++
+ static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
+ 			       enum nl80211_commands cmd, struct nlattr *addr)
+ {
+@@ -4125,6 +4174,11 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
+ 				     NULL,
+ 				     1);
+ 		break;
++	case NL80211_CMD_ATTLM_EVENT:
++		mlme_event_attlm(drv, tb[NL80211_ATTR_IFINDEX],
++				 tb[NL80211_ATTR_MLO_ATTLM_EVENT],
++				 tb[NL80211_ATTR_MLO_ATTLM_SWITCH_TIME_TSF_TU]);
++		break;
+ 	case NL80211_CMD_DISCONNECT:
+ 		mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
+ 				      tb[NL80211_ATTR_MAC],
+diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
+index 13837297c..f997edd6e 100644
+--- a/src/drivers/nl80211_copy.h
++++ b/src/drivers/nl80211_copy.h
+@@ -1588,6 +1588,10 @@ enum nl80211_commands {
+ 
+ 	/* add new commands above here */
+ 
++	/* MTK internal */
++	NL80211_CMD_ATTLM_EVENT,
++	NL80211_CMD_SET_ATTLM,
++
+ 	/* used to define NL80211_CMD_MAX below */
+ 	__NL80211_CMD_AFTER_LAST,
+ 	NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1
+@@ -3388,6 +3392,7 @@ enum nl80211_attrs {
+ 
+ 	NL80211_ATTR_MLO_LINKS,
+ 	NL80211_ATTR_MLO_LINK_ID,
++	NL80211_ATTR_MLO_LINK_DISABLED_BMP,
+ 	NL80211_ATTR_MLD_ADDR,
+ 
+ 	NL80211_ATTR_MLO_SUPPORT,
+@@ -3425,6 +3430,11 @@ enum nl80211_attrs {
+ 	/* MTK internal */
+ 	NL80211_ATTR_CNTDWN_OFFS_STA_PROF,
+ 
++	NL80211_ATTR_MLO_ATTLM_EVENT,
++	NL80211_ATTR_MLO_ATTLM_SWITCH_TIME,
++	NL80211_ATTR_MLO_ATTLM_DURATION,
++	NL80211_ATTR_MLO_ATTLM_SWITCH_TIME_TSF_TU,
++
+ 	__NL80211_ATTR_AFTER_LAST,
+ 	NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST,
+ 	NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
+@@ -8090,4 +8100,18 @@ enum nl80211_wiphy_radio_freq_range {
+ 	NL80211_WIPHY_RADIO_FREQ_ATTR_MAX = __NL80211_WIPHY_RADIO_FREQ_ATTR_LAST - 1,
+ };
+ 
++/**
++ * enum nl80211_attlm_event - type of events for Advertised Tid-to-Link
++ * Mapping operations
++ *
++ * @NL80211_ATTLM_STARTED: A A-TTLM request has been set and start to count down.
++ * @NL80211_ATTLM_SWITCH_TIME_EXPIRED: The switch time of A-TTLM has expired.
++ * @NL80211ATTLM_END: The A-TTLM has been done.
++ */
++enum nl80211_attlm_event {
++	NL80211_ATTLM_STARTED,
++	NL80211_ATTLM_SWITCH_TIME_EXPIRED,
++	NL80211_ATTLM_END,
++};
++
+ #endif /* __LINUX_NL80211_H */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0120-mtk-hostapd-Add-txpower-vendor-command.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0120-mtk-hostapd-Add-txpower-vendor-command.patch
new file mode 100644
index 0000000..4201379
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0120-mtk-hostapd-Add-txpower-vendor-command.patch
@@ -0,0 +1,299 @@
+From 4c0dd29eb15b2cef5cf5256459a19d59e5c98adc Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Fri, 22 Dec 2023 18:09:20 +0800
+Subject: [PATCH 120/126] mtk: hostapd: Add txpower vendor command
+
+Porting and refactor from wifi6 power vendor cmd. Add lpi psd control,
+sku index and duplicate mode enhancement.
+
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ hostapd/config_file.c             |  8 +++++
+ src/ap/ap_config.c                |  4 +++
+ src/ap/ap_config.h                |  3 ++
+ src/ap/ap_drv_ops.c               | 16 +++++++++
+ src/ap/ap_drv_ops.h               |  1 +
+ src/ap/hostapd.c                  |  2 ++
+ src/ap/ieee802_11_he.c            |  3 ++
+ src/common/mtk_vendor.h           | 15 +++++++++
+ src/drivers/driver.h              | 10 ++++++
+ src/drivers/driver_nl80211.c      | 55 +++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |  1 +
+ src/drivers/driver_nl80211_capa.c |  3 ++
+ 12 files changed, 121 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index f7bfc357a..944669270 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5570,6 +5570,14 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 			return 1;
+ 		}
+ 		conf->band_idx = (u8) val;
++	} else if (os_strcmp(buf, "lpi_psd") == 0) {
++		u8 en = strtol(pos, NULL, 10);
++		conf->lpi_psd = !!en;
++	} else if (os_strcmp(buf, "sku_idx") == 0) {
++		conf->sku_idx = strtol(pos, NULL, 10);
++	} else if (os_strcmp(buf, "lpi_bcn_enhance") == 0) {
++		u8 en = strtol(pos, NULL, 10);
++		conf->lpi_bcn_enhance = !!en;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 4528df823..bb5ec78a1 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -315,6 +315,10 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 	conf->pp_mode = PP_FW_MODE;
+ 	conf->band_idx = 255;
+ 
++	conf->lpi_psd = 0;
++	conf->sku_idx = 0;
++	conf->lpi_bcn_enhance = 0;
++
+ 	hostapd_set_and_check_bw320_offset(conf, 0);
+ 
+ 	return conf;
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 9c3a28cf4..ea8507cea 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1356,6 +1356,9 @@ struct hostapd_config {
+ 	void *muru_config;
+ 	u8 pp_mode;
+ 	u8 band_idx;
++	u8 lpi_psd;
++	u8 sku_idx;
++	u8 lpi_bcn_enhance;
+ };
+ 
+ enum three_wire_mode {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index a25a67cdd..2f53c518f 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1391,6 +1391,22 @@ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_colo
+ 	return hapd->driver->get_aval_color_bmp(hapd->drv_priv, aval_color_bmp);
+ }
+ 
++int hostapd_drv_txpower_ctrl(struct hostapd_data *hapd)
++{
++	s8 link_id = -1;
++
++	if (!hapd->driver || !hapd->driver->txpower_ctrl)
++		return 0;
++
++	if (hapd->conf->mld_ap)
++		link_id = hapd->mld_link_id;
++
++	return hapd->driver->txpower_ctrl(hapd->drv_priv, hapd->iconf->lpi_psd,
++					  hapd->iconf->sku_idx,
++					  hapd->iconf->lpi_bcn_enhance,
++					  link_id);
++}
++
+ int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
+ {
+ 	s8 link_id = -1;
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 7f108bc1d..a8c60b6f6 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -167,6 +167,7 @@ int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
+ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd,
+ 				       u64 *aval_color_bmp);
++int hostapd_drv_txpower_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
+ int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
+ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 9388f6070..6f7f13f9f 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2879,6 +2879,8 @@ dfs_offload:
+ 		goto fail;
+ 	if (hostapd_drv_amsdu_ctrl(hapd) < 0)
+ 		goto fail;
++	if (hostapd_drv_txpower_ctrl(hapd) < 0)
++		goto fail;
+ 
+ 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ 		   iface->bss[0]->conf->iface);
+diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
+index 3b6b2041c..f3a5679a0 100644
+--- a/src/ap/ieee802_11_he.c
++++ b/src/ap/ieee802_11_he.c
+@@ -264,6 +264,9 @@ u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
+ 		else
+ 			control = center_idx_to_bw_6ghz(seg0);
+ 
++		if (hapd->iconf->lpi_bcn_enhance)
++			control |= HE_6GHZ_OPER_INFO_CTRL_DUP_BEACON;
++
+ 		control |= hapd->iconf->he_6ghz_reg_pwr_type <<
+ 			HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT;
+ 
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 933f0099d..6d75d39c2 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -19,6 +19,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
+ 	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
+ 	MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
++	MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL = 0xce,
+ 	MTK_NL80211_VENDOR_SUBCMD_EML_CTRL = 0xd3,
+ };
+ 
+@@ -310,6 +311,20 @@ enum mtk_vendor_attr_eml_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_EML_CTRL -1
+ };
+ 
++enum mtk_vendor_attr_txpower_ctrl {
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_PSD,
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX,
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE,
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL,
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL - 1
++};
++
+ #define CSI_BW20_DATA_COUNT	64
+ #define CSI_BW40_DATA_COUNT	128
+ #define CSI_BW80_DATA_COUNT	256
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index fe327e560..55f62f537 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5443,6 +5443,16 @@ struct wpa_driver_ops {
+ 	* @dump_buf: Dump_struct that store csi data and related info
+ 	*/
+ 	int (*csi_dump)(void *priv, u8 band_idx, void *dump_buf);
++	/**
++	* txpower_ctrl - ctrl txpower operation
++	* @priv: Private driver interface data
++	* @lpi_psd: 1 to enable lpi psd compensate, 0 to disable
++	* @lpi_bcn_enhance: 1 to enable beacon duplicate enhancement in 6G lpi mode, 0 to disable enhancement
++	* @sku_idx: index used to indicate which sku table should be used
++	* @link_id: MLD link id. -1 if this is an non-MLD AP
++	*/
++	int (*txpower_ctrl)(void *priv, u8 lpi_psd, u8 sku_idx, u8 lpi_bcn_enhance,
++			    u8 link_id);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 05b231c52..395bb5926 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -195,6 +195,14 @@ static struct nla_policy csi_data_policy[NUM_MTK_VENDOR_ATTRS_CSI_DATA] = {
+ 	[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO] = { .type = NLA_U32 },
+ };
+ 
++static struct nla_policy
++txpower_ctrl_policy[NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL] = {
++	[MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_PSD] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID] = { .type = NLA_U8 },
++};
++
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+ 	struct nl_sock *handle;
+@@ -15592,6 +15600,52 @@ fail:
+ 	return -ENOBUFS;
+ }
+ 
++static int nl80211_txpower_ctrl(void *priv, u8 lpi_psd, u8 sku_idx, u8 lpi_bcn_enhance,
++				u8 link_id)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_txpower_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting txpower control");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			    MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_PSD, lpi_psd);
++	nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX, sku_idx);
++	nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE, lpi_bcn_enhance);
++	nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID, link_id);
++
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set power. ret=%d (%s)",
++			   ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+ 	.desc = "Linux nl80211/cfg80211",
+@@ -15779,4 +15833,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ #endif
+ 	.csi_set = nl80211_csi_set,
+ 	.csi_dump = nl80211_csi_dump,
++	.txpower_ctrl = nl80211_txpower_ctrl,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 2cc40e0dc..d32c0ff4d 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -214,6 +214,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_beacon_ctrl_vendor_cmd_avail:1;
+ 	unsigned int mtk_csi_vendor_cmd_avail:1;
+ 	unsigned int mtk_eml_vendor_cmd_avail:1;
++	unsigned int mtk_txpower_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index c1327a679..fc7f9062f 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1179,6 +1179,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_EML_CTRL:
+ 					drv->mtk_eml_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL:
++					drv->mtk_txpower_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0121-mtk-hostapd-Add-Triggered-Uplink-Access-Optimization.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0121-mtk-hostapd-Add-Triggered-Uplink-Access-Optimization.patch
new file mode 100644
index 0000000..567d0d3
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0121-mtk-hostapd-Add-Triggered-Uplink-Access-Optimization.patch
@@ -0,0 +1,651 @@
+From d612e6c3981212812e87374f90b7be07edf9bc2a Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Tue, 2 Jul 2024 09:46:26 +0800
+Subject: [PATCH 121/126] mtk: hostapd: Add Triggered Uplink Access
+ Optimization support
+
+Add TUAO feature support.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ hostapd/Makefile                  |   1 +
+ src/ap/Makefile                   |   3 +-
+ src/ap/ap_drv_ops.c               |  13 ++
+ src/ap/ap_drv_ops.h               |   5 +
+ src/ap/ieee802_11.c               |   6 +
+ src/ap/scs.c                      | 247 ++++++++++++++++++++++++++++++
+ src/ap/scs.h                      |  30 ++++
+ src/ap/sta_info.h                 |   5 +
+ src/common/ieee802_11_defs.h      |   5 +
+ src/common/mtk_vendor.h           |  17 ++
+ src/drivers/driver.h              |  26 ++++
+ src/drivers/driver_nl80211.c      |  62 ++++++++
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   3 +
+ 14 files changed, 423 insertions(+), 1 deletion(-)
+ create mode 100644 src/ap/scs.c
+ create mode 100644 src/ap/scs.h
+
+diff --git a/hostapd/Makefile b/hostapd/Makefile
+index 8dc6e6216..233176ae5 100644
+--- a/hostapd/Makefile
++++ b/hostapd/Makefile
+@@ -382,6 +382,7 @@ ifdef CONFIG_IEEE80211BE
+ CONFIG_IEEE80211AX=y
+ CFLAGS += -DCONFIG_IEEE80211BE
+ OBJS += ../src/ap/ieee802_11_eht.o
++OBJS += ../src/ap/scs.o
+ endif
+ 
+ ifdef CONFIG_IEEE80211AX
+diff --git a/src/ap/Makefile b/src/ap/Makefile
+index a1e9b7c44..49c6d4a13 100644
+--- a/src/ap/Makefile
++++ b/src/ap/Makefile
+@@ -55,6 +55,7 @@ LIB_OBJS= \
+ 	wpa_auth_glue.o \
+ 	wpa_auth_ie.o \
+ 	wps_hostapd.o \
+-	x_snoop.o
++	x_snoop.o \
++	scs.o
+ 
+ include ../lib.rules
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 2f53c518f..64fd0c04c 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -23,6 +23,10 @@
+ #include "wpa_auth.h"
+ #include "ap_drv_ops.h"
+ 
++#ifdef CONFIG_IEEE80211BE
++#include "scs.h"
++#endif
++
+ 
+ u32 hostapd_sta_flags_to_drv(u32 flags)
+ {
+@@ -1531,3 +1535,12 @@ int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf)
+ 		return 0;
+ 	return hapd->driver->csi_dump(hapd->drv_priv, hapd->iconf->band_idx, dump_buf);
+ }
++
++#ifdef CONFIG_IEEE80211BE
++int hostapd_drv_set_scs(struct hostapd_data *hapd, struct hostapd_scs_desc_info *info)
++{
++	if (!hapd->driver || !hapd->driver->set_scs)
++		return 0;
++	return hapd->driver->set_scs(hapd->drv_priv, info, hapd->mld_link_id);
++}
++#endif
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index a8c60b6f6..3e5743754 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -190,6 +190,11 @@ int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
+ int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set,
+ 			    u8 qos_map_set_len);
+ 
++#ifdef CONFIG_IEEE80211BE
++int hostapd_drv_set_scs(struct hostapd_data *hapd,
++			struct hostapd_scs_desc_info *info);
++#endif
++
+ void hostapd_get_ext_capa(struct hostapd_iface *iface);
+ void hostapd_get_mld_capa(struct hostapd_iface *iface);
+ 
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 886a21a66..09aa1141b 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -62,6 +62,9 @@
+ #ifdef CONFIG_APUP
+ #	include "apup.h"
+ #endif // def CONFIG_APUP
++#ifdef CONFIG_IEEE80211BE
++#include "scs.h"
++#endif
+ 
+ #ifdef CONFIG_FILS
+ static struct wpabuf *
+@@ -6247,6 +6250,9 @@ static int handle_action(struct hostapd_data *hapd,
+ 		hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len);
+ 		return 1;
+ #endif /* CONFIG_NO_RRM */
++	case WLAN_ACTION_ROBUST_AV_STREAMING:
++		hostapd_handle_scs(hapd, (const u8 *) mgmt, len);
++		return 1;
+ 	}
+ 
+ 	hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+diff --git a/src/ap/scs.c b/src/ap/scs.c
+new file mode 100644
+index 000000000..6f3c0444e
+--- /dev/null
++++ b/src/ap/scs.c
+@@ -0,0 +1,247 @@
++#include "utils/includes.h"
++
++#include "utils/common.h"
++#include "common/ieee802_11_defs.h"
++#include "common/ieee802_11_common.h"
++#include "hostapd.h"
++#include "ieee802_11.h"
++#include "sta_info.h"
++#include "ap_config.h"
++#include "ap_drv_ops.h"
++#include "scs.h"
++
++static bool hostapd_find_scs_session(struct sta_info *sta, u8 scsid,
++				     u8 *session_idx)
++{
++	u8 idx;
++
++	for (idx = 0; idx < SCS_MAX_CFG_CNT; idx++) {
++		if (sta->scs_session[idx].scs_id == scsid) {
++			*session_idx = idx;
++			return sta->scs_session[idx].alive;
++		}
++	}
++
++	return false;
++}
++
++static int hostapd_find_available_scs_session(struct sta_info *sta)
++{
++	u8 idx;
++
++	for (idx = 0; idx < SCS_MAX_CFG_CNT; idx++) {
++		if (!sta->scs_session[idx].alive)
++			return idx;
++	}
++
++	return -1;
++}
++
++static bool hostapd_parse_qos_char_element(const struct element *elem,
++					   struct hostapd_scs_desc_info *info)
++{
++#define SCS_DIRECTION_UPLINK 0
++	u8 id_extension = elem->data[0];
++	u32 control_info;
++
++	info->qos_ie_len = elem->datalen + 2;
++
++	if (id_extension != WLAN_EID_EXT_QOS_CHARACTERISTICS ||
++	    info->qos_ie_len > sizeof(info->qos_ie))
++		return false;
++
++	control_info = WPA_GET_LE32(&elem->data[1]);
++	info->dir = control_info & 0x3;
++
++	/* Only support Uplink direction SCS request now. */
++	if (info->dir != SCS_DIRECTION_UPLINK)
++		return false;
++
++	os_memcpy(info->qos_ie, elem, info->qos_ie_len);
++
++	return true;
++}
++
++static u16 hostapd_process_scs_descriptor(struct hostapd_data *hapd,
++					  struct sta_info *sta, const u8 *payload,
++					  u8 scs_desc_len,
++					  struct hostapd_scs_desc_info *info)
++{
++	bool scs_avail, qos_char_elem_avail = false;
++	const struct element *elem;
++	u8 session_idx;
++	int ret;
++
++	scs_avail = hostapd_find_scs_session(sta, info->id, &session_idx);
++
++	switch (info->req_type) {
++	case SCS_REQ_TYPE_ADD:
++	case SCS_REQ_TYPE_CHANGE:
++		if ((info->req_type == SCS_REQ_TYPE_ADD && scs_avail) ||
++		    (info->req_type == SCS_REQ_TYPE_CHANGE && !scs_avail))
++			goto decline;
++
++		if (info->req_type == SCS_REQ_TYPE_ADD) {
++			session_idx = hostapd_find_available_scs_session(sta);
++			if (session_idx == -1) {
++				wpa_printf(MSG_ERROR, "%s: Out of SCS resource.\n",
++					   __func__);
++				goto decline;
++			}
++		}
++
++		for_each_element(elem, payload + 2, scs_desc_len - 2) {
++			switch (elem->id) {
++			case WLAN_EID_EXTENSION:
++				qos_char_elem_avail =
++					hostapd_parse_qos_char_element(elem, info);
++				break;
++			default:
++				/* The rest elements would be ignored now. */
++				break;
++			}
++		}
++
++		if (!qos_char_elem_avail) {
++			wpa_printf(MSG_ERROR, "%s: The content of QoS Charactristics"
++				   " element is empty or not supported yet!\n",
++				   __func__);
++			goto decline;
++		}
++
++		break;
++	case SCS_REQ_TYPE_REMOVE:
++		if (!scs_avail)
++			goto decline;
++
++		break;
++	default:
++		goto decline;
++	}
++
++	ret = hostapd_drv_set_scs(hapd, info);
++	if (ret)
++		goto decline;
++
++	sta->scs_session[session_idx].scs_id = info->id;
++	sta->scs_session[session_idx].alive =
++		info->req_type == SCS_REQ_TYPE_REMOVE ? false : true;
++
++	return (info->req_type == SCS_REQ_TYPE_REMOVE) ?
++		SCS_REQ_TCLAS_PROCESSING_TERMINATED : SCS_REQ_SUCCESS;
++
++decline:
++	wpa_printf(MSG_ERROR, "%s: Decline Request Type %d\n",
++		   __func__, info->req_type);
++
++	return SCS_REQ_DECLINED;
++}
++
++static void send_scs_response(struct hostapd_data *hapd,
++			      struct scs_status_duple *scs_status, const u8 *da,
++			      u8 dialog_token, u8 count)
++{
++	struct wpabuf *buf;
++	size_t len;
++	u8 i;
++
++	if (count == 0)
++		return;
++
++	/* Reference to 802_11be_D5.0 Figure 9-1183  */
++	len = 4 + count * sizeof(struct scs_status_duple);
++	buf = wpabuf_alloc(len);
++	if (buf == NULL)
++		return;
++
++	wpabuf_put_u8(buf, WLAN_ACTION_ROBUST_AV_STREAMING);
++	wpabuf_put_u8(buf, ROBUST_AV_SCS_RESP);
++	wpabuf_put_u8(buf, dialog_token);
++	wpabuf_put_u8(buf, count);
++
++	for (i = 0; i < count && i < SCS_MAX_CFG_CNT; i++) {
++		wpabuf_put_u8(buf, scs_status[i].scs_id);
++		wpabuf_put_le16(buf, scs_status[i].status);
++	}
++
++	len = wpabuf_len(buf);
++	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, da,
++				wpabuf_head(buf), len);
++	wpabuf_free(buf);
++}
++
++static void hostapd_handle_scs_req(struct hostapd_data *hapd,
++				   const u8 *buf, size_t len)
++{
++	const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
++	struct hostapd_scs_desc_info info;
++	struct sta_info *sta;
++	struct scs_status_duple scs_status_list[SCS_MAX_CFG_CNT];
++	const u8 *pos, *end;
++	u8 token, index = 0;
++	const struct element *elem;
++
++	sta = ap_get_sta(hapd, mgmt->sa);
++
++	if (!sta) {
++		wpa_printf(MSG_ERROR, "Station " MACSTR " not found "
++			   "for SCS Request frame\n", MAC2STR(mgmt->sa));
++		return;
++	}
++
++	token = mgmt->u.action.u.scs.dialog_token;
++	pos = mgmt->u.action.u.scs.variable;
++
++	end = buf + len;
++	len = end - pos;
++
++	for_each_element(elem, pos, len) {
++		if (elem->id != WLAN_EID_SCS_DESCRIPTOR) {
++			wpa_printf(MSG_ERROR, "%s: no scs elem %d in scs req frame!\n",
++				   __func__, WLAN_EID_SCS_DESCRIPTOR);
++			break;
++		}
++
++		info.id = elem->data[0];
++		if (!info.id) {
++			wpa_printf(MSG_ERROR, "%s: SCSID = 0 is invalid\n", __func__);
++			break;
++		}
++
++		info.req_type = elem->data[1];
++		os_memcpy(info.peer_addr, mgmt->sa, ETH_ALEN);
++		scs_status_list[index].scs_id = info.id;
++		scs_status_list[index].status =
++			hostapd_process_scs_descriptor(hapd, sta, elem->data,
++						       elem->datalen, &info);
++		index++;
++	}
++
++	send_scs_response(hapd, scs_status_list, mgmt->sa, token, index);
++}
++
++void hostapd_handle_scs(struct hostapd_data *hapd, const u8 *buf, size_t len)
++{
++	const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
++
++	/*
++	 * Check for enough bytes: header + (1B)Category + (1B)Action +
++	 * (1B)Dialog Token.
++	 */
++	if (len < IEEE80211_HDRLEN + 3) {
++		wpa_printf(MSG_ERROR, "%s SCS frame len %lu is not enough!",
++			   __func__, len);
++		return;
++	}
++
++	switch (mgmt->u.action.u.scs.action) {
++	case ROBUST_AV_SCS_REQ:
++		hostapd_handle_scs_req(hapd, buf, len);
++		break;
++	case ROBUST_AV_SCS_RESP:
++		/* Not supported yet. */
++		break;
++	default:
++		break;
++	}
++}
+diff --git a/src/ap/scs.h b/src/ap/scs.h
+new file mode 100644
+index 000000000..b69190b65
+--- /dev/null
++++ b/src/ap/scs.h
+@@ -0,0 +1,30 @@
++#ifndef SCS_H
++#define SCS_H
++
++struct hostapd_data;
++
++/* Only support TUAO certification */
++#define SCS_MAX_CFG_CNT 2
++
++struct scs_status_duple {
++	u8 scs_id;
++	u16 status;
++};
++
++struct scs_session_status {
++	u8 scs_id;
++	bool alive;
++};
++
++enum scs_req_type {
++	SCS_REQ_TYPE_ADD,
++	SCS_REQ_TYPE_REMOVE,
++	SCS_REQ_TYPE_CHANGE,
++};
++
++#define SCS_REQ_SUCCESS		0
++#define SCS_REQ_DECLINED	37
++#define SCS_REQ_TCLAS_PROCESSING_TERMINATED	97
++
++void hostapd_handle_scs(struct hostapd_data *hapd, const u8 *buf, size_t len);
++#endif
+diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
+index 60b33f049..339adf987 100644
+--- a/src/ap/sta_info.h
++++ b/src/ap/sta_info.h
+@@ -18,6 +18,10 @@
+ #include "crypto/sha384.h"
+ #include "pasn/pasn_common.h"
+ 
++#ifdef CONFIG_IEEE80211BE
++#include "scs.h"
++#endif
++
+ /* STA flags */
+ #define WLAN_STA_AUTH BIT(0)
+ #define WLAN_STA_ASSOC BIT(1)
+@@ -335,6 +339,7 @@ struct sta_info {
+ 	struct mld_info mld_info;
+ 	u8 mld_assoc_link_id;
+ 	struct sta_info *mld_assoc_sta;
++	struct scs_session_status scs_session[SCS_MAX_CFG_CNT];
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ 	u16 max_idle_period; /* if nonzero, the granted BSS max idle period in
+diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
+index afcc2f861..4754d5a69 100644
+--- a/src/common/ieee802_11_defs.h
++++ b/src/common/ieee802_11_defs.h
+@@ -1196,6 +1196,11 @@ struct ieee80211_mgmt {
+ 					u8 action;
+ 					u8 variable[];
+ 				} STRUCT_PACKED eht_prot;
++				struct {
++					u8 action;
++					u8 dialog_token;
++					u8 variable[];
++				} STRUCT_PACKED scs;
+ 			} u;
+ 		} STRUCT_PACKED action;
+ 	} u;
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 6d75d39c2..1fe459126 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -20,6 +20,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
+ 	MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
+ 	MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL = 0xce,
++	MTK_NL80211_VENDOR_SUBCMD_SCS_CTRL = 0xd0,
+ 	MTK_NL80211_VENDOR_SUBCMD_EML_CTRL = 0xd3,
+ };
+ 
+@@ -325,6 +326,22 @@ enum mtk_vendor_attr_txpower_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_scs_ctrl {
++	MTK_VENDOR_ATTR_SCS_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_SCS_ID,
++	MTK_VENDOR_ATTR_SCS_REQ_TYPE,
++	MTK_VENDOR_ATTR_SCS_DIR,
++	MTK_VENDOR_ATTR_SCS_QOS_IE,
++	MTK_VENDOR_ATTR_SCS_MAC_ADDR,
++	MTK_VENDOR_ATTR_SCS_LINK_ID,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_SCS_CTRL,
++	MTK_VENDOR_ATTR_SCS_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_SCS_CTRL - 1
++};
++
+ #define CSI_BW20_DATA_COUNT	64
+ #define CSI_BW40_DATA_COUNT	128
+ #define CSI_BW80_DATA_COUNT	256
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 55f62f537..f3de2b3bc 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -70,6 +70,25 @@ enum hostapd_chan_width_attr {
+ #define HOSTAPD_DFS_REGION_ETSI	2
+ #define HOSTAPD_DFS_REGION_JP	3
+ 
++/**
++ * struct hostapd_scs_desc_info - SCS Req information
++ * @id: SCSID of each SCS stream
++ * @req_type: request type in SCS Descriptor element
++ * @dir: Direction in the control info of QoS Characteristics element
++ * @peer_addr: the mac addr of SCS requester station
++ * @qos_ie: QoS Characteristics IE in SCS Descriptor element
++ * @qos_ie_len: the length of QoS Characteristics element
++ */
++#define EID_EXT_QOS_CHAR_MAX_SIZE 44
++struct hostapd_scs_desc_info {
++	u8 id;
++	u8 req_type;
++	u8 dir;
++	u8 peer_addr[ETH_ALEN];
++	u8 qos_ie[EID_EXT_QOS_CHAR_MAX_SIZE];
++	u8 qos_ie_len;
++};
++
+ /**
+  * enum reg_change_initiator - Regulatory change initiator
+  */
+@@ -5424,6 +5443,13 @@ struct wpa_driver_ops {
+ 	int (*pp_mode_set)(void *priv, const u8 pp_mode, s8 link_id, u16 punct_bitmap);
+ #ifdef CONFIG_IEEE80211BE
+ 	int (*get_mld_addr)(void *priv, u8 *addr);
++	/**
++	 * set_scs - Configure Stream Classification Service
++	 * @priv: Private driver interface data
++	 * @info: Stream classidication service configuration
++	 * @link_id: MLD link id
++	 */
++	int (*set_scs)(void *priv, struct hostapd_scs_desc_info *info, u8 link_id);
+ #endif
+ 	/**
+ 	 * csi_set - Set csi related mode and parameter
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 395bb5926..d2064fbd6 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -42,6 +42,10 @@
+ #include "common/mtk_vendor.h"
+ #include "ap/ap_config.h"
+ 
++#ifdef CONFIG_IEEE80211BE
++#include "ap/scs.h"
++#endif
++
+ 
+ #ifndef NETLINK_CAP_ACK
+ #define NETLINK_CAP_ACK 10
+@@ -2932,6 +2936,9 @@ static int nl80211_action_subscribe_ap(struct i802_bss *bss)
+ 	/* Protected EHT */
+ 	if (nl80211_register_action_frame(bss, (u8 *) "\x25", 1) < 0)
+ 		ret = -1;
++	/* Robust AV SCS Request */
++	if (nl80211_register_action_frame(bss, (u8 *) "\x13\x00", 2) < 0)
++		ret = -1;
+ 	/* Vendor-specific */
+ 	if (nl80211_register_action_frame(bss, (u8 *) "\x7f", 1) < 0)
+ 		ret = -1;
+@@ -15387,6 +15394,60 @@ static int nl80211_set_eml_omn(void *priv, u8 link_id, u8 *addr,
+ 
+ 	return ret;
+ 
++fail:
++	nlmsg_free(msg);
++	return ret;
++}
++
++static int
++nl80211_set_scs(void *priv, struct hostapd_scs_desc_info *info, u8 link_id)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_scs_vendor_cmd_avail) {
++		wpa_printf(MSG_ERROR,
++			   "nl80211: Driver does not support scs");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_SCS_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	if (nla_put_u8(msg, MTK_VENDOR_ATTR_SCS_ID, info->id) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_SCS_REQ_TYPE, info->req_type) ||
++	    nla_put(msg, MTK_VENDOR_ATTR_SCS_MAC_ADDR, ETH_ALEN, info->peer_addr) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_SCS_LINK_ID, link_id))
++		goto fail;
++
++	if (info->req_type == SCS_REQ_TYPE_ADD ||
++	    info->req_type == SCS_REQ_TYPE_CHANGE)
++		if (nla_put_u8(msg, MTK_VENDOR_ATTR_SCS_DIR, info->dir) ||
++		    nla_put(msg, MTK_VENDOR_ATTR_SCS_QOS_IE, info->qos_ie_len,
++			    info->qos_ie))
++			goto fail;
++
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set scs. ret = %d (%s)",
++			   ret, strerror(-ret));
++
++	return ret;
++
+ fail:
+ 	nlmsg_free(msg);
+ 	return ret;
+@@ -15830,6 +15891,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.pp_mode_set = nl80211_pp_mode_set,
+ #ifdef CONFIG_IEEE80211BE
+ 	.get_mld_addr = nl80211_get_mld_addr,
++	.set_scs = nl80211_set_scs,
+ #endif
+ 	.csi_set = nl80211_csi_set,
+ 	.csi_dump = nl80211_csi_dump,
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index d32c0ff4d..87ade150b 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -215,6 +215,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_csi_vendor_cmd_avail:1;
+ 	unsigned int mtk_eml_vendor_cmd_avail:1;
+ 	unsigned int mtk_txpower_vendor_cmd_avail:1;
++	unsigned int mtk_scs_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index fc7f9062f..5db6bf6e6 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1182,6 +1182,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL:
+ 					drv->mtk_txpower_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_SCS_CTRL:
++					drv->mtk_scs_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0122-mtk-hostapd-fix-6G-EHT-BW-320-channel-switch-issue.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0122-mtk-hostapd-fix-6G-EHT-BW-320-channel-switch-issue.patch
new file mode 100644
index 0000000..8358b97
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0122-mtk-hostapd-fix-6G-EHT-BW-320-channel-switch-issue.patch
@@ -0,0 +1,100 @@
+From 562e8a54538b200256ba0e0d820951d88686044b Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 5 Aug 2024 14:56:17 +0800
+Subject: [PATCH 122/126] mtk: hostapd: fix 6G EHT BW 320 channel switch issue
+
+When channel switching from BW 320 to BW 160, the op class is not
+changed for building csa after beacon.
+Therefore, hostapd_eid_eht_operation will use the old op class to fill
+the EHT operation chwidth info for csa after beacon, leading to
+disconnections for stations due to an invalid chandef.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/hostapd.c     | 39 ++++++++++++++++++++++-----------------
+ src/drivers/driver.h |  5 +++++
+ 2 files changed, 27 insertions(+), 17 deletions(-)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 6f7f13f9f..d06a0395b 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4496,23 +4496,26 @@ static int hostapd_change_config_freq(struct hostapd_data *hapd,
+ 	mode = hapd->iface->current_mode;
+ 
+ 	/* if a pointer to old_params is provided we save previous state */
+-	if (old_params &&
+-	    hostapd_set_freq_params(old_params, conf->hw_mode,
+-				    hostapd_hw_get_freq(hapd, conf->channel),
+-				    conf->channel, conf->enable_edmg,
+-				    conf->edmg_channel, conf->ieee80211n,
+-				    conf->ieee80211ac, conf->ieee80211ax,
+-				    conf->ieee80211be, conf->secondary_channel,
+-				    hostapd_get_oper_chwidth(conf),
+-				    hostapd_get_oper_centr_freq_seg0_idx(conf),
+-				    hostapd_get_oper_centr_freq_seg1_idx(conf),
+-				    conf->vht_capab,
+-				    mode ? &mode->he_capab[IEEE80211_MODE_AP] :
+-				    NULL,
+-				    mode ? &mode->eht_capab[IEEE80211_MODE_AP] :
+-				    NULL,
+-				    hostapd_get_punct_bitmap(hapd)))
+-		return -1;
++	if (old_params) {
++		if (hostapd_set_freq_params(old_params, conf->hw_mode,
++					    hostapd_hw_get_freq(hapd, conf->channel),
++					    conf->channel, conf->enable_edmg,
++					    conf->edmg_channel, conf->ieee80211n,
++					    conf->ieee80211ac, conf->ieee80211ax,
++					    conf->ieee80211be, conf->secondary_channel,
++					    hostapd_get_oper_chwidth(conf),
++					    hostapd_get_oper_centr_freq_seg0_idx(conf),
++					    hostapd_get_oper_centr_freq_seg1_idx(conf),
++					    conf->vht_capab,
++					    mode ? &mode->he_capab[IEEE80211_MODE_AP] :
++					    NULL,
++					    mode ? &mode->eht_capab[IEEE80211_MODE_AP] :
++					    NULL,
++					    hostapd_get_punct_bitmap(hapd)))
++			return -1;
++
++		old_params->op_class = conf->op_class;
++	}
+ 
+ 	switch (params->bandwidth) {
+ 	case 0:
+@@ -4557,6 +4560,7 @@ static int hostapd_change_config_freq(struct hostapd_data *hapd,
+ 	conf->ieee80211n = params->ht_enabled;
+ 	conf->ieee80211ac = params->vht_enabled;
+ 	conf->secondary_channel = params->sec_channel_offset;
++	conf->op_class = params->op_class;
+ 	if (params->center_freq1 &&
+ 	    ieee80211_freq_to_chan(params->center_freq1, &seg0) ==
+ 	    NUM_HOSTAPD_MODES)
+@@ -4624,6 +4628,7 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
+ 
+ 	settings->freq_params.channel = chan;
+ 
++	settings->freq_params.op_class = hapd->iface->cs_oper_class;
+ 	ret = hostapd_change_config_freq(iface->bss[0], iface->conf,
+ 					 &settings->freq_params,
+ 					 &old_freq);
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index f3de2b3bc..829274bfe 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -907,6 +907,11 @@ struct hostapd_freq_params {
+ 	 * link_id: If >=0 indicates the link of the AP MLD to configure
+ 	 */
+ 	int link_id;
++
++	/**
++	 * op_class: Operating class of the channel
++	 */
++	u8 op_class;
+ };
+ 
+ /**
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0123-mtk-hostapd-add-puncture-bitmap-to-ucode.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0123-mtk-hostapd-add-puncture-bitmap-to-ucode.patch
new file mode 100644
index 0000000..e480428
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0123-mtk-hostapd-add-puncture-bitmap-to-ucode.patch
@@ -0,0 +1,74 @@
+From f046d62580187077b4d75c3e5f183a8ad8ad9079 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 6 Aug 2024 10:11:22 +0800
+Subject: [PATCH 123/126] mtk: hostapd: add puncture bitmap to ucode
+
+Add puncture bitmap to ucode since fw might trigger channel switch due
+to pp bitmap change.
+Therefore, the changed pp bitmap should be synchronized to extender's AP
+when root AP changes its pp bitmap.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/ucode.c         | 4 ++++
+ src/utils/ucode.c      | 2 ++
+ wpa_supplicant/ucode.c | 1 +
+ 3 files changed, 7 insertions(+)
+
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index 8c05404f5..0c6f4043c 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -726,6 +726,8 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 		csa.freq_params.center_freq1 = intval;
+ 	if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
+ 		csa.freq_params.center_freq2 = intval;
++	if ((intval = ucv_int64_get(ucv_object_get(info, "punct_bitmap", NULL))) && !errno)
++		csa.freq_params.punct_bitmap = intval;
+ 
+ 	intval = ucv_int64_get(ucv_object_get(info, "band_idx", NULL));
+ 	band_idx = errno ? iface->conf->band_idx : intval;
+@@ -740,6 +742,8 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 			csa.freq_params.center_freq1);
+ 	wpa_printf(MSG_INFO, "    * center_freq2 is %d\n",
+ 			csa.freq_params.center_freq2);
++	wpa_printf(MSG_INFO, "    * punct_bitmap is %d\n",
++			csa.freq_params.punct_bitmap);
+ 	wpa_printf(MSG_INFO, "    * band_idx is %d\n",
+ 			band_idx);
+ 
+diff --git a/src/utils/ucode.c b/src/utils/ucode.c
+index 8bbbbbeff..c688d9bee 100644
+--- a/src/utils/ucode.c
++++ b/src/utils/ucode.c
+@@ -110,6 +110,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 	uc_value_t *freq = uc_fn_arg(0);
+ 	uc_value_t *sec = uc_fn_arg(1);
+ 	int width = ucv_uint64_get(uc_fn_arg(2));
++	int punct_bitmap = ucv_uint64_get(uc_fn_arg(5));
+ 	int bw320_offset = 1, band_idx;
+ 	int freq_val, center_idx, center_ofs;
+ 	enum oper_chan_width chanwidth;
+@@ -185,6 +186,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 	ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
+ 	ucv_object_add(ret, "oper_chwidth", ucv_int64_new(chanwidth));
+ 	ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
++	ucv_object_add(ret, "punct_bitmap", ucv_int64_new(punct_bitmap));
+ 
+ 	if (chanwidth == CONF_OPER_CHWIDTH_USE_HT) {
+ 		center_idx = freq_val < 3000 ? 0 : channel;
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+index 450780737..124038def 100644
+--- a/wpa_supplicant/ucode.c
++++ b/wpa_supplicant/ucode.c
+@@ -178,6 +178,7 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ 		ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
+ 		ucv_object_add(val, "ch_width", ucv_int64_new(oper_chwidth));
+ 		ucv_object_add(val, "bw320_offset", ucv_int64_new(bw320_offset));
++		ucv_object_add(val, "punct_bitmap", ucv_int64_new(data->ch_switch.punct_bitmap));
+ 		ucv_object_add(val, "band_idx", ucv_int64_new(band_idx));
+ 	}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0124-mtk-hostapd-Add-AFC-and-lpi-driver-power-support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0124-mtk-hostapd-Add-AFC-and-lpi-driver-power-support.patch
new file mode 100644
index 0000000..357b17e
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0124-mtk-hostapd-Add-AFC-and-lpi-driver-power-support.patch
@@ -0,0 +1,832 @@
+From 330346c577b4623e52767ad0939cd735a83d9837 Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Mon, 22 Jul 2024 20:51:18 +0800
+Subject: [PATCH 124/126] mtk: hostapd: Add AFC and lpi driver power support
+
+Add AFC and lpi driver power support
+This patch parse the AFC response into mtk sku power table format and send
+it to driver by vendor cmd. The table format is like below:
+col\row	 bw20  bw40  ...  ru26  ...  ru3472
+chan 1
+chan 5
+...
+chan 233
+
+ - Once the afc procedure start failed, the device would set as lpi mode by
+telling driver lpi_sku_index to use specify sku-index in dst.
+ - Add afc_max_timeout conf is use to limit the maximum interval between the
+two afc requests.
+ - Set default use lpi & sp mode
+
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ hostapd/config_file.c        |   4 +
+ src/ap/afc.c                 | 365 +++++++++++++++++++++++++++++++++--
+ src/ap/ap_config.c           |   3 +-
+ src/ap/ap_config.h           |   2 +
+ src/ap/ap_drv_ops.c          |  34 +++-
+ src/ap/hostapd.h             |  60 ++++++
+ src/common/mtk_vendor.h      |   2 +
+ src/drivers/driver.h         |   4 +-
+ src/drivers/driver_nl80211.c |  36 +++-
+ 9 files changed, 477 insertions(+), 33 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 944669270..11c5f8947 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4248,6 +4248,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 	} else if (os_strcmp(buf, "afc_op_class") == 0) {
+ 		if (hostapd_afc_parse_op_class(conf, pos))
+ 			return 1;
++	} else if (os_strcmp(buf, "afc_max_timeout") == 0) {
++		conf->afc.max_timeout = atoi(pos);
+ #endif /* CONFIG_AFC */
+ 	} else if (os_strcmp(buf, "mbssid") == 0) {
+ 		int mbssid = atoi(pos);
+@@ -5575,6 +5577,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		conf->lpi_psd = !!en;
+ 	} else if (os_strcmp(buf, "sku_idx") == 0) {
+ 		conf->sku_idx = strtol(pos, NULL, 10);
++	} else if (os_strcmp(buf, "lpi_sku_idx") == 0) {
++		conf->lpi_sku_idx = strtol(pos, NULL, 10);
+ 	} else if (os_strcmp(buf, "lpi_bcn_enhance") == 0) {
+ 		u8 en = strtol(pos, NULL, 10);
+ 		conf->lpi_bcn_enhance = !!en;
+diff --git a/src/ap/afc.c b/src/ap/afc.c
+index 361ecb575..d36ce00d7 100644
+--- a/src/ap/afc.c
++++ b/src/ap/afc.c
+@@ -16,6 +16,7 @@
+ #include "hostapd.h"
+ #include "acs.h"
+ #include "hw_features.h"
++#include "ap_drv_ops.h"
+ 
+ #define HOSTAPD_AFC_RETRY_TIMEOUT	180
+ #define HOSTAPD_AFC_TIMEOUT		86400 /* 24h */
+@@ -749,6 +750,9 @@ static int hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply)
+ 	iface->afc.timeout = request_timeout;
+ 	if (iface->afc.timeout < 0)
+ 		iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
++	else if (iface->afc.timeout > iconf->afc.max_timeout &&
++		 iconf->afc.max_timeout >= HOSTAPD_AFC_RETRY_TIMEOUT)
++		iface->afc.timeout = iconf->afc.max_timeout;
+ 
+ 	return ret;
+ }
+@@ -772,6 +776,7 @@ static int hostapd_afc_send_receive(struct hostapd_iface *iface)
+ 	int sockfd, ret;
+ 	fd_set read_set;
+ 
++	iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
+ 	if (iface->afc.data_valid) {
+ 		/* AFC data already downloaded from the server */
+ 		return 0;
+@@ -782,7 +787,6 @@ static int hostapd_afc_send_receive(struct hostapd_iface *iface)
+ 		return -EINVAL;
+ 	}
+ 
+-	iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
+ 	if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) {
+ 		wpa_printf(MSG_ERROR, "Malformed AFC socket string %s",
+ 			   iconf->afc.socket);
+@@ -880,7 +884,19 @@ static bool hostapd_afc_has_usable_chans(struct hostapd_iface *iface)
+ int hostapd_afc_handle_request(struct hostapd_iface *iface)
+ {
+ 	struct hostapd_config *iconf = iface->conf;
++	bool lpi_mode;
+ 	int ret;
++	int afc_status = AFC_CONTINUE;
++
++	lpi_mode = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type);
++	if (lpi_mode && !he_reg_is_sp(iconf->he_6ghz_reg_pwr_type)) {
++		iface->afc.lpi_mode = true;
++		return 1;
++	}
++
++	if (strncmp(iconf->country, "US", 2) != 0 &&
++	    strncmp(iconf->country, "CA", 2) != 0)
++		return 1;
+ 
+ 	/* AFC is required just for standard power AP */
+ 	if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
+@@ -894,34 +910,50 @@ int hostapd_afc_handle_request(struct hostapd_iface *iface)
+ 
+ 	ret = hostapd_afc_send_receive(iface);
+ 	if (ret < 0) {
+-		/*
+-		 * If the connection to the AFCD failed, resched for a
+-		 * future attempt.
+-		 */
+-		wpa_printf(MSG_ERROR, "AFC connection failed: %d", ret);
+-		if (ret == -EIO)
+-			ret = 0;
++		afc_status = lpi_mode ? AFC_LPI : AFC_DISABLE;
+ 		goto resched;
+ 	}
+ 
+ 	hostap_afc_disable_channels(iface);
+-	if (!hostapd_afc_has_usable_chans(iface))
++	if (!hostapd_afc_has_usable_chans(iface)) {
++		afc_status = lpi_mode ? AFC_LPI : AFC_DISABLE;
+ 		goto resched;
++	}
+ 
+ 	if (!hostapd_is_usable_chans(iface)) {
+ 		/* Trigger an ACS freq scan */
++		afc_status = AFC_RESTART_IFACE;
+ 		iconf->channel = 0;
+ 		iface->freq = 0;
+ 
+ 		if (acs_init(iface) != HOSTAPD_CHAN_ACS) {
+ 			wpa_printf(MSG_ERROR, "Could not start ACS");
++			afc_status = AFC_DISABLE;
+ 			ret = -EINVAL;
+ 		}
+ 	} else {
++		afc_status = AFC_CONTINUE;
+ 		ret = 1;
+ 	}
+ 
+ resched:
++	switch(afc_status) {
++	case AFC_LPI:
++		iface->afc.lpi_mode = true;
++		hostapd_afc_enable_lpi_channels(iface);
++		ret = 1;
++		break;
++	/* Disable and restart iface would be finished in hostapd setup flow. */
++	case AFC_RESTART_IFACE:
++		ret = 0;
++		fallthrough;
++	case AFC_DISABLE:
++	case AFC_CONTINUE:
++		break;
++	default:
++		break;
++	}
++
+ 	eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
+ 	eloop_register_timeout(iface->afc.timeout, 0,
+ 			       hostapd_afc_timeout_handler, iface, NULL);
+@@ -948,34 +980,58 @@ static void hostapd_afc_delete_data_from_server(struct hostapd_iface *iface)
+ static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx)
+ {
+ 	struct hostapd_iface *iface = eloop_ctx;
+-	bool restart_iface = true;
++	bool lpi_mode;
++	int afc_status = AFC_CONTINUE, ret;
++
++	lpi_mode = he_reg_is_indoor(iface->conf->he_6ghz_reg_pwr_type);
++	iface->afc.lpi_mode = false;
+ 
+ 	hostapd_afc_delete_data_from_server(iface);
+ 	if (iface->state != HAPD_IFACE_ENABLED) {
++		afc_status = AFC_RESTART_IFACE;
+ 		/* Hostapd is not fully enabled yet, toggle the interface */
+ 		goto restart_interface;
+ 	}
+ 
+ 	if (hostapd_afc_send_receive(iface) < 0 ||
+ 	    hostapd_get_hw_features(iface)) {
+-		restart_iface = false;
++		afc_status = lpi_mode ? AFC_LPI : AFC_DISABLE;
+ 		goto restart_interface;
+ 	}
+ 
+-	if (hostapd_is_usable_chans(iface))
+-		goto resched;
++	ret = hostapd_is_usable_chans(iface);
++	if (ret != 1) {
++		afc_status = lpi_mode && ret == 0 ? AFC_LPI : AFC_DISABLE;
++		goto restart_interface;
++	}
+ 
+-	restart_iface = hostapd_afc_has_usable_chans(iface);
+-	if (restart_iface) {
++	ret = hostapd_afc_has_usable_chans(iface);
++	if (ret) {
+ 		/* Trigger an ACS freq scan */
++		afc_status = AFC_RESTART_IFACE;
+ 		iface->conf->channel = 0;
+ 		iface->freq = 0;
+ 	}
+ 
+ restart_interface:
+-	hostapd_disable_iface(iface);
+-	if (restart_iface)
++	switch(afc_status) {
++	case AFC_DISABLE:
++		hostapd_disable_iface(iface);
++		break;
++	case AFC_RESTART_IFACE:
++		hostapd_disable_iface(iface);
+ 		hostapd_enable_iface(iface);
++		break;
++	case AFC_LPI:
++		iface->afc.lpi_mode = true;
++		hostapd_afc_enable_lpi_channels(iface);
++		hostapd_drv_txpower_ctrl(iface->bss[0]);
++		break;
++	case AFC_CONTINUE:
++		break;
++	default:
++		break;
++	}
+ resched:
+ 	eloop_register_timeout(iface->afc.timeout, 0,
+ 			       hostapd_afc_timeout_handler, iface, NULL);
+@@ -1040,6 +1096,37 @@ void hostap_afc_disable_channels(struct hostapd_iface *iface)
+ 	}
+ }
+ 
++void hostapd_afc_enable_lpi_channels(struct hostapd_iface *iface)
++{
++	struct hostapd_hw_modes *mode = NULL;
++	int i;
++
++	for (i = 0; i < iface->num_hw_features; i++) {
++		mode = &iface->hw_features[i];
++		if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
++		    mode->is_6ghz)
++			break;
++	}
++
++	if (i == iface->num_hw_features)
++		return;
++
++	if (!he_reg_is_indoor(iface->conf->he_6ghz_reg_pwr_type))
++		return;
++
++	for (i = 0; i < mode->num_channels; i++) {
++		struct hostapd_channel_data *chan = &mode->channels[i];
++
++		if (!is_6ghz_freq(chan->freq))
++			continue;
++
++		chan->flag &= ~HOSTAPD_CHAN_DISABLED;
++		wpa_printf(MSG_MSGDUMP,
++			   "Enabling freq=%d MHz for lpi mode",
++			   chan->freq);
++	}
++}
++
+ 
+ int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
+ 				       int *power)
+@@ -1076,3 +1163,247 @@ int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
+ 	}
+ 	return -EINVAL;
+ }
++
++void hostapd_afc_init_power_table(s8 ***power_table)
++{
++	int table_idx, bw;
++	s8 *chan_power_list;
++
++	/* init power table */
++	for (table_idx = 0; table_idx < MAX_CHANNEL_NUM_6G; table_idx++) {
++		chan_power_list = (*power_table)[table_idx];
++		for (bw = 0; bw < afc_power_table_num; bw++)
++			chan_power_list[bw] = AFC_INVALID_POWER;
++	}
++}
++
++int hostapd_afc_parse_psd_to_dbm(struct hostapd_iface *iface, s8 ***power_table)
++{
++	int i, freq, channel, bw, table_idx, target_power;
++	s8 *chan_power_list;
++
++	for (i = 0; i < iface->afc.num_freq_range; i++) {
++		struct afc_freq_range_elem *freq_range = &iface->afc.freq_range[i];
++
++		if (!freq_range)
++			continue;
++
++		freq = freq_range->low_freq + 10;
++		channel = hostapd_hw_get_channel(iface->bss[0], freq);
++		if (channel == 0)
++			return -EINVAL;
++
++		table_idx = channel / 4;
++
++		if (table_idx >= MAX_CHANNEL_NUM_6G)
++			return -EINVAL;
++
++		chan_power_list = (*power_table)[table_idx];
++		for (bw = 0; bw < afc_power_bw320_2; bw++) {
++			target_power = freq_range->max_psd * 2 + PSD_TO_DBM_OFFSET +
++				bw * DOUBLE_BW_POWER;
++			target_power = MIN(AFC_MAXIMUM_POWER, target_power);
++			chan_power_list[bw] = MIN(chan_power_list[bw],
++						  target_power);
++		}
++		chan_power_list[afc_power_bw320_2] = chan_power_list[afc_power_bw320_1];
++	}
++	return 0;
++}
++
++int hostapd_afc_parse_eirp_to_dbm(struct hostapd_iface *iface, s8 ***power_table)
++{
++	int i, bw, table_idx, target_power;
++	s8 *chan_power_list;
++
++	for (i = 0; i < iface->afc.num_chan_info; i++) {
++		struct afc_chan_info_elem *chan_info = &iface->afc.chan_info_list[i];
++
++		if (!chan_info)
++			continue;
++
++		table_idx = chan_info->chan / 4;
++
++		if (table_idx >= MAX_CHANNEL_NUM_6G)
++			return -EINVAL;
++
++		chan_power_list = (*power_table)[table_idx];
++		target_power = MIN(AFC_MAXIMUM_POWER, chan_info->power * 2);
++		/* FIXME: wider bandwidth power is not stored. */
++		chan_power_list[afc_power_bw20] = MIN(chan_power_list[afc_power_bw20],
++						      target_power);
++	}
++	return 0;
++}
++
++int afc_get_ru_be_offset(int bw, int *target_bw, int *offset)
++{
++	switch (bw) {
++	case afc_power_ru26:
++		*target_bw = afc_power_bw20;
++		*offset = RU26_OFFSET_20MHZ;
++		break;
++	case afc_power_ru52:
++		*target_bw = afc_power_bw20;
++		*offset = RU52_OFFSET_20MHZ;
++		break;
++	case afc_power_ru78:
++		*target_bw = afc_power_bw20;
++		*offset = RU78_OFFSET_20MHZ;
++		break;
++	case afc_power_ru106:
++		*target_bw = afc_power_bw20;
++		*offset = RU106_OFFSET_20MHZ;
++		break;
++	case afc_power_ru132:
++		*target_bw = afc_power_bw20;
++		*offset = RU132_OFFSET_20MHZ;
++		break;
++	case afc_power_ru726:
++		*target_bw = afc_power_bw80;
++		*offset = RU726_OFFSET_80MHZ;
++		break;
++	case afc_power_ru1480:
++		*target_bw = afc_power_bw160;
++		*offset = RU1480_OFFSET_160MHZ;
++		break;
++	case afc_power_ru1772:
++		*target_bw = afc_power_bw160;
++		*offset = RU1772_OFFSET_160MHZ;
++		break;
++	case afc_power_ru2476:
++		*target_bw = afc_power_bw320_1;
++		*offset = RU2476_OFFSET_320MHZ;
++		break;
++	case afc_power_ru2988:
++		*target_bw = afc_power_bw320_1;
++		*offset = RU2988_OFFSET_320MHZ;
++		break;
++	case afc_power_ru3472:
++		*target_bw = afc_power_bw320_1;
++		*offset = RU3472_OFFSET_320MHZ;
++		break;
++	default:
++		return -EINVAL;
++	}
++	return 0;
++}
++
++int hostapd_afc_fill_wide_bandwidth_power(s8 ***power_table)
++{
++	int table_idx, bw;
++	s8 *chan_power_list;
++
++	for (table_idx = 0; table_idx < MAX_CHANNEL_NUM_6G; table_idx++) {
++		int target_power, ru26_power;
++
++		if ((*power_table)[table_idx][afc_power_bw20] == AFC_INVALID_POWER)
++			continue;
++
++		chan_power_list = (*power_table)[table_idx];
++
++		/* Check wide bandwidth power minimum or valid. */
++		for (bw = afc_power_bw40; bw <= afc_power_bw320_2; bw++) {
++			int bw_ch_num, first_ch, last_ch;
++
++			target_power = AFC_INVALID_POWER;
++			switch (bw) {
++			case afc_power_bw40:
++				bw_ch_num = 2;
++				break;
++			case afc_power_bw80:
++				bw_ch_num = 4;
++				break;
++			case afc_power_bw160:
++				bw_ch_num = 8;
++				break;
++			case afc_power_bw320_1:
++			case afc_power_bw320_2:
++				bw_ch_num = 16;
++				break;
++			}
++			if ((bw == afc_power_bw320_1 && table_idx > 47) ||
++			    (bw == afc_power_bw320_2 && table_idx < 8))
++				continue;
++
++			if (bw == afc_power_bw320_2)
++				first_ch = table_idx - (table_idx + 8) % bw_ch_num;
++			else
++				first_ch = table_idx - table_idx % bw_ch_num;
++			last_ch = first_ch + bw_ch_num;
++			for (int ch = first_ch; ch < last_ch; ch++) {
++				if ((*power_table)[ch][bw] == AFC_INVALID_POWER) {
++					target_power = AFC_INVALID_POWER;
++					break;
++				}
++				target_power = MIN((*power_table)[ch][bw], target_power);
++			}
++			chan_power_list[bw] = target_power;
++		}
++
++		/* Update remain ru */
++		for (bw = afc_power_ru26; bw < afc_power_table_num; bw++) {
++			int target_bw, offset;
++
++			if (afc_get_ru_be_offset(bw, &target_bw, &offset))
++				return -EINVAL;
++
++			if (target_bw == afc_power_bw320_1 &&
++			    chan_power_list[target_bw] == AFC_INVALID_POWER)
++			    target_bw++;
++
++			if (chan_power_list[target_bw] == AFC_INVALID_POWER) {
++				chan_power_list[bw] = AFC_INVALID_POWER;
++				continue;
++			}
++
++			target_power = chan_power_list[target_bw] - offset;
++			chan_power_list[bw] = target_power;
++		}
++	}
++	return 0;
++}
++
++
++int hostapd_afc_translate_table(struct hostapd_iface *iface,
++				s8 ***power_table)
++{
++	int i, ret, bw320_offset;
++
++	if (!iface->afc.data_valid)
++		return -EINVAL;
++
++	*power_table = (s8**)os_zalloc(MAX_CHANNEL_NUM_6G * sizeof(s8*));
++
++	if (!(*power_table))
++		return -ENOMEM;
++
++	for (i = 0; i < MAX_CHANNEL_NUM_6G; i++) {
++		(*power_table)[i] = (s8*)os_zalloc(afc_power_table_num * sizeof(s8));
++		if (!(*power_table)[i])
++			goto out;
++	}
++
++	hostapd_afc_init_power_table(power_table);
++
++	ret = hostapd_afc_parse_psd_to_dbm(iface, power_table);
++	if (ret)
++		goto out;
++
++	ret = hostapd_afc_parse_eirp_to_dbm(iface, power_table);
++	if (ret)
++		goto out;
++
++	ret = hostapd_afc_fill_wide_bandwidth_power(power_table);
++	if (ret)
++		goto out;
++
++	return 0;
++out:
++	for (i = 0; i < MAX_CHANNEL_NUM_6G; i++)
++		os_free((*power_table)[i]);
++
++	os_free(*power_table);
++	power_table = NULL;
++	return -ENOMEM;
++}
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index bb5ec78a1..185dae089 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -288,7 +288,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->he_6ghz_reg_pwr_type = HE_REG_INFO_6GHZ_AP_TYPE_VLP;
++	conf->he_6ghz_reg_pwr_type = HE_REG_INFO_6GHZ_AP_TYPE_INDOOR;
+ 	conf->reg_def_cli_eirp_psd = -1;
+ 	conf->reg_sub_cli_eirp_psd = -1;
+ 	conf->reg_def_cli_eirp = -1;
+@@ -317,6 +317,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 
+ 	conf->lpi_psd = 0;
+ 	conf->sku_idx = 0;
++	conf->lpi_sku_idx = 0;
+ 	conf->lpi_bcn_enhance = 0;
+ 
+ 	hostapd_set_and_check_bw320_offset(conf, 0);
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index ea8507cea..966a02d6a 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1343,6 +1343,7 @@ struct hostapd_config {
+ 		unsigned int n_op_class;
+ 		unsigned int *op_class;
+ 		int min_power;
++		int max_timeout;
+ 	} afc;
+ #endif /* CONFIG_AFC */
+ 
+@@ -1358,6 +1359,7 @@ struct hostapd_config {
+ 	u8 band_idx;
+ 	u8 lpi_psd;
+ 	u8 sku_idx;
++	u8 lpi_sku_idx;
+ 	u8 lpi_bcn_enhance;
+ };
+ 
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 64fd0c04c..bd710832a 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1397,7 +1397,8 @@ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_colo
+ 
+ int hostapd_drv_txpower_ctrl(struct hostapd_data *hapd)
+ {
+-	s8 link_id = -1;
++	s8 link_id = -1, sku_idx = hapd->iconf->sku_idx, ret = 0, i;
++	s8 **afc_power_table = NULL;
+ 
+ 	if (!hapd->driver || !hapd->driver->txpower_ctrl)
+ 		return 0;
+@@ -1405,10 +1406,33 @@ int hostapd_drv_txpower_ctrl(struct hostapd_data *hapd)
+ 	if (hapd->conf->mld_ap)
+ 		link_id = hapd->mld_link_id;
+ 
+-	return hapd->driver->txpower_ctrl(hapd->drv_priv, hapd->iconf->lpi_psd,
+-					  hapd->iconf->sku_idx,
+-					  hapd->iconf->lpi_bcn_enhance,
+-					  link_id);
++#ifdef CONFIG_AFC
++	if (hapd->iface->current_mode->is_6ghz &&
++	    he_reg_is_sp(hapd->iface->conf->he_6ghz_reg_pwr_type) &&
++	    !hapd->iface->afc.lpi_mode) {
++		ret = hostapd_afc_translate_table(hapd->iface, &afc_power_table);
++		if (ret)
++			goto out;
++	}
++
++	if (hapd->iface->afc.lpi_mode == true)
++		sku_idx = hapd->iconf->lpi_sku_idx;
++#endif /* CONFIG_AFC */
++
++	ret = hapd->driver->txpower_ctrl(hapd->drv_priv, hapd->iconf->lpi_psd,
++					 sku_idx,
++					 hapd->iconf->lpi_bcn_enhance,
++					 link_id,
++					 afc_power_table,
++					 hapd->iface->afc.lpi_mode);
++#ifdef CONFIG_AFC
++out:
++	if (afc_power_table)
++		for (i = 0; i < MAX_CHANNEL_NUM_6G; i++)
++			os_free(afc_power_table[i]);
++	os_free(afc_power_table);
++#endif /* CONFIG_AFC */
++	return ret;
+ }
+ 
+ int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index f69fa0062..0625bb762 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -783,17 +783,71 @@ struct hostapd_iface {
+ 			int power;
+ 		} *chan_info_list;
+ 		bool data_valid;
++		bool lpi_mode;
+ 	} afc;
+ #endif /* CONFIG_AFC */
+ };
+ 
+ /* hostapd.c */
+ #ifdef CONFIG_AFC
++
++enum afc_state {
++	AFC_DISABLE,
++	AFC_RESTART_IFACE,
++	AFC_LPI,
++	AFC_CONTINUE,
++};
++
++#define MAX_CHANNEL_NUM_6G 59
++
++/* The power unit is 0.5 dBm */
++#define AFC_MAXIMUM_POWER 72
++#define AFC_INVALID_POWER 127
++#define PSD_TO_DBM_OFFSET 26
++#define BW20_TO_RU26_OFFSET 20
++#define DOUBLE_BW_POWER 6
++
++#define RU26_OFFSET_20MHZ 20
++#define RU52_OFFSET_20MHZ 14
++#define RU78_OFFSET_20MHZ 10
++#define RU106_OFFSET_20MHZ 8
++#define RU132_OFFSET_20MHZ 6
++
++#define RU726_OFFSET_80MHZ 2
++#define RU1480_OFFSET_160MHZ 2
++#define RU1772_OFFSET_160MHZ 1
++#define RU2476_OFFSET_320MHZ 4
++#define RU2988_OFFSET_320MHZ 2
++#define RU3472_OFFSET_320MHZ 1
++
++enum afc_table_info {
++	afc_power_bw20,
++	afc_power_bw40,
++	afc_power_bw80,
++	afc_power_bw160,
++	afc_power_bw320_1,
++	afc_power_bw320_2,
++	afc_power_ru26,
++	afc_power_ru52,
++	afc_power_ru78,
++	afc_power_ru106,
++	afc_power_ru132,
++	afc_power_ru726,
++	afc_power_ru1480,
++	afc_power_ru1772,
++	afc_power_ru2476,
++	afc_power_ru2988,
++	afc_power_ru3472,
++	afc_power_table_num,
++};
++
+ int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
+ 				       int *power);
+ int hostapd_afc_handle_request(struct hostapd_iface *iface);
+ void hostapd_afc_stop(struct hostapd_iface *iface);
+ void hostap_afc_disable_channels(struct hostapd_iface *iface);
++int hostapd_afc_translate_table(struct hostapd_iface *iface,
++				s8 ***power_table);
+ #else
+ static inline int
+ hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
+@@ -814,6 +868,12 @@ static inline void hostapd_afc_stop(struct hostapd_iface *iface)
+ static inline void hostap_afc_disable_channels(struct hostapd_iface *iface)
+ {
+ }
++
++int hostapd_afc_translate_table(struct hostapd_iface *iface,
++				 s8 ***power_table)
++{
++	return -EINVAL;
++}
+ #endif /* CONFIG_AFC */
+ 
+ int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 1fe459126..4b900162b 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -319,6 +319,8 @@ enum mtk_vendor_attr_txpower_ctrl {
+ 	MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX,
+ 	MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE,
+ 	MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID,
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE,
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL,
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 829274bfe..6aac87ce1 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5481,9 +5481,11 @@ struct wpa_driver_ops {
+ 	* @lpi_bcn_enhance: 1 to enable beacon duplicate enhancement in 6G lpi mode, 0 to disable enhancement
+ 	* @sku_idx: index used to indicate which sku table should be used
+ 	* @link_id: MLD link id. -1 if this is an non-MLD AP
++	* @power_table: power table generated from AFC response
++	* @lpi_mode: specify the current mode is whether lpi
+ 	*/
+ 	int (*txpower_ctrl)(void *priv, u8 lpi_psd, u8 sku_idx, u8 lpi_bcn_enhance,
+-			    u8 link_id);
++			    u8 link_id, s8 **power_table, u8 lpi_mode);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index d2064fbd6..efee210b9 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -41,6 +41,7 @@
+ #include "driver_nl80211.h"
+ #include "common/mtk_vendor.h"
+ #include "ap/ap_config.h"
++#include "ap/hostapd.h"
+ 
+ #ifdef CONFIG_IEEE80211BE
+ #include "ap/scs.h"
+@@ -205,6 +206,8 @@ txpower_ctrl_policy[NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL] = {
+ 	[MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX] = { .type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE] = { .type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE] = { .type = NLA_BINARY },
++	[MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI] = { .type = NLA_U8 },
+ };
+ 
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+@@ -15662,38 +15665,53 @@ fail:
+ }
+ 
+ static int nl80211_txpower_ctrl(void *priv, u8 lpi_psd, u8 sku_idx, u8 lpi_bcn_enhance,
+-				u8 link_id)
++				u8 link_id, s8 **power_table, u8 lpi_mode)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+ 	struct nl_msg *msg;
+ 	struct nlattr *data;
+-	int ret;
++	struct nlattr *table_attr, *channel_list;
++	int ret = 0;
+ 
+ 	if (!drv->mtk_txpower_vendor_cmd_avail) {
+ 		wpa_printf(MSG_INFO,
+ 			   "nl80211: Driver does not support setting txpower control");
+-		return 0;
++		goto fail;
+ 	}
+ 
+ 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+-	if (!msg)
++	if (!msg) {
++		ret = -ENOBUFS;
+ 		goto fail;
++	}
+ 
+ 	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
+ 		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+-			    MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL))
++			    MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL)) {
++		ret = -ENOBUFS;
+ 		goto fail;
++	}
+ 
+-	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+-	if (!data)
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++	if (!data) {
++		ret = -ENOBUFS;
+ 		goto fail;
++	}
+ 
+ 	nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_PSD, lpi_psd);
+ 	nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX, sku_idx);
+ 	nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE, lpi_bcn_enhance);
+-	nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID, link_id);
+ 
++	if (link_id > -1)
++		nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID, link_id);
++
++	if (power_table && *power_table) {
++		nla_put(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE,
++		        MAX_CHANNEL_NUM_6G * afc_power_table_num, power_table);
++	}
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI, lpi_mode);
+ 	nla_nest_end(msg, data);
+ 	ret = send_and_recv_cmd(drv, msg);
+ 	if (ret)
+@@ -15704,7 +15722,7 @@ static int nl80211_txpower_ctrl(void *priv, u8 lpi_psd, u8 sku_idx, u8 lpi_bcn_e
+ 
+ fail:
+ 	nlmsg_free(msg);
+-	return -ENOBUFS;
++	return ret;
+ }
+ 
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0125-mtk-hostapd-do-not-set-secondary-channel-in-HT-opera.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0125-mtk-hostapd-do-not-set-secondary-channel-in-HT-opera.patch
new file mode 100644
index 0000000..8c24366
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0125-mtk-hostapd-do-not-set-secondary-channel-in-HT-opera.patch
@@ -0,0 +1,91 @@
+From 35e67264aef9b6f050e01465ff419495198ad110 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Wed, 14 Aug 2024 17:42:00 +0800
+Subject: [PATCH 125/126] mtk: hostapd: do not set secondary channel in HT
+ operation if punctured
+
+If the secondary channel is punctured, the HT operation in the beacon
+should not indicate a secondary channel offset
+(HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE or HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ieee802_11_ht.c | 53 ++++++++++++++++++++++++++++++++++++------
+ 1 file changed, 46 insertions(+), 7 deletions(-)
+
+diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
+index 7f0a00f95..1b323819e 100644
+--- a/src/ap/ieee802_11_ht.c
++++ b/src/ap/ieee802_11_ht.c
+@@ -79,6 +79,49 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
+ }
+ 
+ 
++void set_ht_param(struct hostapd_data *hapd, struct ieee80211_ht_operation *oper,
++		  u8 chwidth)
++{
++	int secondary_channel = hapd->iconf->secondary_channel;
++#ifdef CONFIG_IEEE80211BE
++	u8 offset, chan_bit_pos;
++	u16 bw = 0, punct_bitmap = hostapd_get_punct_bitmap(hapd);
++
++	switch (chwidth) {
++	case CONF_OPER_CHWIDTH_80MHZ:
++		bw = 80;
++		offset = 6;
++		break;
++	case CONF_OPER_CHWIDTH_160MHZ:
++		bw = 160;
++		offset = 14;
++		break;
++	case CONF_OPER_CHWIDTH_320MHZ:
++		bw = 320;
++		offset = 30;
++		break;
++	default:
++		break;
++	}
++
++	chan_bit_pos = (hapd->iconf->channel -
++			hostapd_get_oper_centr_freq_seg0_idx(hapd->iconf) +
++			offset) / 4;
++	/* check if secondary channel is punctured */
++	if (bw >= 80 && punct_bitmap && secondary_channel &&
++	    (punct_bitmap & BIT(chan_bit_pos + secondary_channel)))
++		return;
++#endif /* CONFIG_IEEE80211BE */
++
++	if (secondary_channel == 1)
++		oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE |
++			HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
++	if (secondary_channel == -1)
++		oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
++			HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
++}
++
++
+ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
+ {
+ 	struct ieee80211_ht_operation *oper;
+@@ -98,15 +141,11 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
+ 
+ 	oper->primary_chan = hapd->iconf->channel;
+ 	oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode);
+-	if (hapd->iconf->secondary_channel == 1)
+-		oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE |
+-			HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
+-	if (hapd->iconf->secondary_channel == -1)
+-		oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
+-			HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
+ 
+-	vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
+ 	chwidth = hostapd_get_oper_chwidth(hapd->iconf);
++	set_ht_param(hapd, oper, chwidth);
++
++	vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
+ 	if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT
+ 		&& ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
+ 		oper->operation_mode = host_to_le16(hapd->iconf->vht_oper_centr_freq_seg0_idx << 5);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0126-mtk-hostapd-Fix-hostapd-crash-in-wds-mode-during-fre.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0126-mtk-hostapd-Fix-hostapd-crash-in-wds-mode-during-fre.patch
new file mode 100644
index 0000000..cf9dd47
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0126-mtk-hostapd-Fix-hostapd-crash-in-wds-mode-during-fre.patch
@@ -0,0 +1,40 @@
+From 6207a7d1f0601a12be9639aa3507faf10b84398a Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 15 Aug 2024 18:50:41 +0800
+Subject: [PATCH 126/126] mtk: hostapd: Fix hostapd crash in wds mode during
+ freeing sta
+
+When freeing sta in wds mode, ap_free_sta will pass NULL pointer (ifname_wds)
+to hostapd_set_wds_sta.
+The ifname_wds will directly assigned to name and being used without
+checking whether it is NULL or not in i802_set_wds_sta.
+The following hostapd patch of the openwrt trunk leads to this issue.
+https://github.com/openwrt/openwrt/commit/e80520197c9ca7bced50d3605d6baba6dead6e35
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/drivers/driver_nl80211.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index efee210b9..a08afb5d4 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -8603,10 +8603,13 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+-	const char *name = ifname_wds; // Kept to reduce changes to the minimum
++	char name[IFNAMSIZ + 1];
+ 	union wpa_event_data event;
+ 	int ret;
+ 
++	if (ifname_wds)
++		os_strlcpy(name, ifname_wds, IFNAMSIZ + 1);
++
+ 	wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
+ 		   " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
+ 	if (val) {
+-- 
+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 d61727b..dec3493 100644
--- a/recipes-wifi/hostapd/files/patches-2.10.3/patches.inc
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/patches.inc
@@ -1,107 +1,129 @@
 #patch patches (come from openwrt/lede/target/linux/mediatek)
 SRC_URI_append = " \
-    file://0001-hostapd-MLO-fix-for_each_mld_link-macro.patch \
-    file://0002-hostapd-MLO-frame-link-add-command-on-per-BSS-basis.patch \
-    file://0003-nl80211-Print-the-interface-name-in-debug-during-lin.patch \
-    file://0004-hostapd-MLO-send-link_id-on-sta_deauth.patch \
-    file://0005-hostapd-MLO-handle-auth-assoc-on-link-address.patch \
-    file://0006-hostapd-MLO-reset-auth-state-machine-s-ML-info.patch \
-    file://0007-hostapd-MLO-add-support-for-cohosted-ML-BSS.patch \
-    file://0008-hostapd-MLO-extend-support-for-cohosted-ML-BSS.patch \
-    file://0009-hostapd-MLO-pass-link_id-in-get_hapd_bssid-helper-fu.patch \
-    file://0010-hostapd-MLO-pass-ctx-in-mlme_event_mgmt.patch \
-    file://0011-hostapd-MLO-move-mgmt-and-control-port-Tx-status-to-.patch \
-    file://0012-hostapd-make-hostapd_eapol_tx_status-function-static.patch \
-    file://0013-hostapd-MLO-handle-link_id-in-EAPOL-Tx-status-handle.patch \
-    file://0014-hostapd-MLO-update-all-partner-s-link-beacon.patch \
-    file://0015-hostapd-MLO-skip-assoc-link-processing-in-ML-info.patch \
-    file://0016-hostapd-MLO-Enhance-wpa-state-machine.patch \
-    file://0017-hostapd-MLO-add-support-for-MLO-rekey.patch \
-    file://0018-hostapd-MLO-send-link-id-during-flushing-stations.patch \
-    file://0019-hostapd-MLO-display-link-details-in-status-command.patch \
-    file://0020-hostapd-fix-RNR-building-for-co-location-and-MLO.patch \
-    file://0021-tests-MLO-add-basic-cohosted-MLDs-functionality-test.patch \
-    file://0022-tests-MLO-add-cohosted-MLDs-connectivity-testing.patch \
-    file://0023-backport-hostapd-afcd-add-AFC-daemon-support.patch \
-    file://0024-backport-hostapd-export-hostapd_is_usable_chans-util.patch \
-    file://0025-backport-hostapd-ap-add-AFC-client-support.patch \
-    file://0026-backport-hostapd-update-TPE-IE-according-to-AFC.patch \
-    file://0027-hostapd-sync-2024-01-18-openwrt-trunk-src-folder.patch \
-    file://0028-hostapd-sync-2024-01-18-openwrt-trunk-patch-folder.patch \
-    file://0029-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch \
-    file://0030-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch \
-    file://0031-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch \
-    file://0032-mtk-hostapd-Add-mtk_vendor.h.patch \
-    file://0033-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch \
-    file://0034-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch \
-    file://0035-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch \
-    file://0036-mtk-hostapd-Add-hostapd-iBF-control.patch \
-    file://0037-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch \
-    file://0038-mtk-hostapd-Add-DFS-detection-mode.patch \
-    file://0039-mtk-hostapd-Add-DFS-offchan-channel-switch.patch \
-    file://0040-mtk-hostapd-Add-amsdu-set-get-ctrl.patch \
-    file://0041-mtk-hostapd-Add-he_ldpc-configuration.patch \
-    file://0042-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch \
-    file://0043-mtk-hostapd-6G-band-does-not-require-DFS.patch \
-    file://0044-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch \
-    file://0045-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch \
-    file://0046-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch \
-    file://0047-mtk-hostapd-Add-available-color-bitmap.patch \
-    file://0048-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch \
-    file://0049-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch \
-    file://0050-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch \
-    file://0051-mtk-hostapd-Add-muru-user-number-debug-command.patch \
-    file://0052-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch \
-    file://0053-mtk-hostapd-Add-HE-capabilities-check.patch \
-    file://0054-mtk-hostapd-Fix-background-channel-overlapping-opera.patch \
-    file://0055-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch \
-    file://0056-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch \
-    file://0057-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch \
-    file://0058-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch \
-    file://0059-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch \
-    file://0060-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch \
-    file://0061-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch \
-    file://0062-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch \
-    file://0063-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch \
-    file://0064-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch \
-    file://0065-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch \
-    file://0066-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch \
-    file://0067-mtk-hostapd-update-eht-operation-element.patch \
-    file://0068-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch \
-    file://0069-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch \
-    file://0070-mtk-hostapd-Add-support-for-updating-background-chan.patch \
-    file://0071-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch \
-    file://0072-mtk-hostapd-add-support-enable-disable-preamble-punc.patch \
-    file://0073-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch \
-    file://0074-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch \
-    file://0075-hostapd-mtk-ACS-remove-chan-freq-list-check-when-sca.patch \
-    file://0076-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch \
-    file://0077-Revert-ACS-upstream-changes.patch \
-    file://0078-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch \
-    file://0079-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch \
-    file://0080-mtk-hostapd-fix-mld_assoc_link_id.patch \
-    file://0081-mtk-wpa_s-correctly-get-assoc-frequency.patch \
-    file://0082-mtk-wpa_s-force-MLD-STA-to-use-SAE-H2E-during-authen.patch \
-    file://0083-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch \
-    file://0084-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch \
-    file://0085-mtk-wpa_s-fix-bss-selection-when-setting-mld_connect.patch \
-    file://0086-mtk-hostapd-add-mld_primary-option.patch \
-    file://0087-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch \
-    file://0088-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch \
-    file://0089-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch \
-    file://0090-mtk-hostapd-add-link-id-to-hostapd-cli-chan-switch.patch \
-    file://0091-mtk-wifi-hostapd-add-wds-mlo-support.patch \
-    file://0093-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch \
-    file://0094-mtk-hostapd-AP-MLD-specify-link-id-for-unicast-DEAUT.patch \
-    file://0095-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch \
-    file://0096-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch \
-    file://0097-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch \
-    file://0098-mtk-hostapd-add-connac3-csi-control-interface.patch \
-    file://0099-fixup-mtk-wifi-hostapd-add-wds-mlo-support.patch \
-    file://0100-mtk-hostapd-MLD-find-partner-links-by-BSSID-and-SSID.patch \
-    file://0101-mtk-hostapd-MLD-hostapd-add-support-for-basic-MLD-Ex.patch \
-    file://0102-mtk-hostapd-Refactor-static-PP-and-mld-support.patch \
-    file://0103-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch \
-    file://0104-mtk-hostapd-ucode-add-is_mld_finished-check.patch \
-    file://0105-mtk-hostapd-free-station-when-hapd-deinit.patch \
+    file://0001-ctrl_iface-create-link-based-hapd-control-sockets.patch \
+    file://0002-ctrl_iface-MLO-introduce-MLD-level-socket.patch \
+    file://0003-hostapd_cli-MLO-pass-LINKID-in-the-command.patch \
+    file://0004-hostapd_cli-MLO-add-status-command-for-MLD-socket.patch \
+    file://0005-hostapd-afcd-add-AFC-daemon-support.patch \
+    file://0006-hostapd-export-hostapd_is_usable_chans-utility-routi.patch \
+    file://0007-hostapd-ap-add-AFC-client-support.patch \
+    file://0008-hostapd-update-TPE-IE-according-to-AFC.patch \
+    file://0009-sync-2024-06-21-openwrt-trunk-src-folder.patch \
+    file://0010-sync-2024-08-13-openwrt-trunk-patches-folder.patch \
+    file://0011-mtk-hostapd-sync-with-wireless-next.git-include-uapi.patch \
+    file://0012-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch \
+    file://0013-mtk-hostapd-print-some-sae-info-by-hostapd-ctrl.patch \
+    file://0014-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch \
+    file://0015-mtk-hostapd-Add-mtk_vendor.h.patch \
+    file://0016-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch \
+    file://0017-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch \
+    file://0018-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch \
+    file://0019-mtk-hostapd-Add-hostapd-iBF-control.patch \
+    file://0020-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch \
+    file://0021-mtk-hostapd-Add-DFS-detection-mode.patch \
+    file://0022-mtk-hostapd-Add-DFS-offchan-channel-switch.patch \
+    file://0023-mtk-hostapd-Add-amsdu-set-get-ctrl.patch \
+    file://0024-mtk-hostapd-Add-he_ldpc-configuration.patch \
+    file://0025-mtk-hostapd-6G-band-does-not-require-DFS.patch \
+    file://0026-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch \
+    file://0027-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch \
+    file://0028-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch \
+    file://0029-mtk-hostapd-Add-available-color-bitmap.patch \
+    file://0030-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch \
+    file://0031-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch \
+    file://0032-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch \
+    file://0033-mtk-hostapd-Add-muru-user-number-debug-command.patch \
+    file://0034-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch \
+    file://0035-mtk-hostapd-Add-HE-capabilities-check.patch \
+    file://0036-mtk-hostapd-Fix-background-channel-overlapping-opera.patch \
+    file://0037-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch \
+    file://0038-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch \
+    file://0039-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch \
+    file://0040-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch \
+    file://0041-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch \
+    file://0042-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch \
+    file://0043-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch \
+    file://0044-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch \
+    file://0045-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch \
+    file://0046-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch \
+    file://0047-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch \
+    file://0048-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch \
+    file://0049-mtk-hostapd-update-eht-operation-element.patch \
+    file://0050-mtk-hostapd-add-support-for-ucode-to-parse-BW320MHz-.patch \
+    file://0051-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch \
+    file://0052-mtk-hostapd-Add-support-for-updating-background-chan.patch \
+    file://0053-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem.patch \
+    file://0054-mtk-hostapd-add-support-enable-disable-preamble-punc.patch \
+    file://0055-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch \
+    file://0056-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch \
+    file://0057-mtk-hostapd-remove-chan-freq-list-check-when-scan-re.patch \
+    file://0058-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch \
+    file://0059-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch \
+    file://0060-mtk-hostapd-fix-mld_assoc_link_id.patch \
+    file://0061-mtk-wpa_supplicant-correctly-get-assoc-frequency.patch \
+    file://0062-mtk-wpa_supplicant-force-MLD-STA-to-use-SAE-H2E-duri.patch \
+    file://0063-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch \
+    file://0064-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch \
+    file://0065-mtk-wpa_supplicant-fix-bss-selection-when-setting-ml.patch \
+    file://0066-mtk-hostapd-add-mld_primary-option.patch \
+    file://0067-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch \
+    file://0068-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch \
+    file://0069-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch \
+    file://0070-mtk-hostapd-add-wds-mlo-support.patch \
+    file://0071-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch \
+    file://0072-mtk-hostapd-specify-link-id-for-unicast-DEAUTH.patch \
+    file://0073-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch \
+    file://0074-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch \
+    file://0075-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch \
+    file://0076-mtk-hostapd-add-connac3-csi-control-interface.patch \
+    file://0077-mtk-hostapd-add-support-for-basic-MLD-Extender.patch \
+    file://0078-mtk-hostapd-Refactor-static-PP-and-mld-support.patch \
+    file://0079-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch \
+    file://0080-mtk-hostapd-add-hidden-SSID-support.patch \
+    file://0081-mtk-hostapd-do-not-roam-within-the-same-MLD.patch \
+    file://0082-mtk-hostapd-update-op_class-in-channel-switch-fallba.patch \
+    file://0083-mtk-hostapd-always-do-ML-probe-request-before-authen.patch \
+    file://0084-mtk-hostapd-prevent-responding-to-mgmt-while-AP-MLD-.patch \
+    file://0085-mtk-hostapd-add-critical-update-support.patch \
+    file://0086-mtk-hostapd-add-support-for-emlsr.patch \
+    file://0087-mtk-hostapd-Fix-Operating-Mode-Notification-issue-in.patch \
+    file://0088-mtk-hostapd-add-mlo-probe-client-support.patch \
+    file://0089-mtk-hostapd-add-support-for-channel-switch.patch \
+    file://0090-mtk-hostapd-extend-MLO-information-getting.patch \
+    file://0091-mtk-hostapd-refactor-the-operating-channel-width-nam.patch \
+    file://0092-mtk-hostapd-refactor-legacy-STA-getting-operating-ch.patch \
+    file://0093-mtk-hostapd-get-link-channel-information-and-synchro.patch \
+    file://0094-mtk-hostapd-adjust-Basic-EHT-MCS-and-Nss-Set-in-EHT-.patch \
+    file://0095-mtk-hostapd-fix-using-wrong-link-id-in-nl80211_set_c.patch \
+    file://0096-mtk-hostapd-Remove-BPCC-and-ML-ie-in-per-sta-profile.patch \
+    file://0097-mtk-hostapd-add-channel-switch-band-sanity-check.patch \
+    file://0098-mtk-hostapd-Fix-the-issue-with-the-presence-of-MLD_I.patch \
+    file://0099-mtk-hostapd-add-support-for-DFS-channel-switching-wi.patch \
+    file://0100-Revert-AP-MLD-Add-MLO-Link-KDE-for-each-affiliated-l.patch \
+    file://0101-mtk-hostapd-Temporary-non-inheritance-IE-solution.patch \
+    file://0102-mtk-hostapd-Fix-multiple-link-connect-get-pmkid-fail.patch \
+    file://0103-mtk-hostapd-ensure-only-a-scan-callback-is-called-on.patch \
+    file://0104-mtk-hostapd-add-support-for-get_survey-to-specify-li.patch \
+    file://0105-mtk-hostapd-sequentially-conduct-multiple-setup-ACS.patch \
+    file://0106-mtk-hostapd-find-sta_info-for-legacy-STA-associating.patch \
+    file://0107-mtk-hostapd-do-not-consider-bss-ignore-list-when-par.patch \
+    file://0108-mtk-hostapd-support-vendor-command-trig_type-and-ap_.patch \
+    file://0109-mtk-hostapd-rework-radar-event-handling.patch \
+    file://0110-mtk-hostapd-add-support-for-link-add.patch \
+    file://0111-mtk-hostapd-Fix-the-op_class-value-in-the-RNR-IE-to-.patch \
+    file://0112-mtk-hostapd-support-enable-disable-preamble-puncture.patch \
+    file://0113-mtk-hostapd-do-not-consider-ht-operation-update-for-.patch \
+    file://0114-mtk-hostapd-Add-bandwidth-indication-IE.patch \
+    file://0115-mtk-hostapd-fix-Multiple-MLDs-to-use-the-conf-s-own_.patch \
+    file://0116-mtk-hostapd-distribute-the-mgmt-rx-frame-to-bss-with.patch \
+    file://0117-mtk-hostapd-handle-5G-link-setup-after-DFS-bootup-CA.patch \
+    file://0118-mtk-hostapd-support-MLD-AP-affiliated-link-removal.patch \
+    file://0119-mtk-hostapd-add-A-TTLM-support.patch \
+    file://0120-mtk-hostapd-Add-txpower-vendor-command.patch \
+    file://0121-mtk-hostapd-Add-Triggered-Uplink-Access-Optimization.patch \
+    file://0122-mtk-hostapd-fix-6G-EHT-BW-320-channel-switch-issue.patch \
+    file://0123-mtk-hostapd-add-puncture-bitmap-to-ucode.patch \
+    file://0124-mtk-hostapd-Add-AFC-and-lpi-driver-power-support.patch \
+    file://0125-mtk-hostapd-do-not-set-secondary-channel-in-HT-opera.patch \
+    file://0126-mtk-hostapd-Fix-hostapd-crash-in-wds-mode-during-fre.patch \
     "
diff --git a/recipes-wifi/hostapd/files/patches/mtk-0048-hostapd-mtk-add-support-for-channel-switching-with-c.patch b/recipes-wifi/hostapd/files/patches/mtk-0048-hostapd-mtk-add-support-for-channel-switching-with-c.patch
index fc37d6c..97e66b8 100644
--- a/recipes-wifi/hostapd/files/patches/mtk-0048-hostapd-mtk-add-support-for-channel-switching-with-c.patch
+++ b/recipes-wifi/hostapd/files/patches/mtk-0048-hostapd-mtk-add-support-for-channel-switching-with-c.patch
@@ -1,4 +1,4 @@
-From 5d4d4397d0628a1aa83f83163e97ff34a0a5de43 Mon Sep 17 00:00:00 2001
+From cc405da7c2fd5176cd0423246e83a358d477e360 Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Thu, 16 Nov 2023 13:18:48 +0800
 Subject: [PATCH] hostapd: mtk: add support for channel switching with csa sent
@@ -8,10 +8,10 @@
 ---
  hostapd/ctrl_iface.c   |  84 +++++++++++++++++-----
  src/ap/ctrl_iface_ap.c |   5 +-
- src/ap/dfs.c           | 156 +++++++++++++++++++++++++++++++++++------
+ src/ap/dfs.c           | 159 +++++++++++++++++++++++++++++++++++------
  src/ap/dfs.h           |   9 ++-
  src/ap/hostapd.c       |   8 ++-
- 5 files changed, 221 insertions(+), 41 deletions(-)
+ 5 files changed, 222 insertions(+), 43 deletions(-)
 
 diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
 index 96b593a..445cb34 100644
@@ -169,10 +169,10 @@
  		return -1;
  	}
 diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index d490032..26ce229 100644
+index d490032..e5f3974 100644
 --- a/src/ap/dfs.c
 +++ b/src/ap/dfs.c
-@@ -248,14 +248,15 @@ static int is_in_chanlist(struct hostapd_iface *iface,
+@@ -248,22 +248,22 @@ static int is_in_chanlist(struct hostapd_iface *iface,
   */
  static int dfs_find_channel(struct hostapd_iface *iface,
  			    struct hostapd_channel_data **ret_chan,
@@ -191,7 +191,16 @@
  
  	wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
  	for (i = 0; i < mode->num_channels; i++) {
-@@ -548,7 +549,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+ 		chan = &mode->channels[i];
+ 
+ 		/* Skip HT40/VHT incompatible channels */
+-		if (iface->conf->ieee80211n &&
+-		    iface->conf->secondary_channel &&
++		if (iface->conf->ieee80211n && n_chans > 1 &&
+ 		    (!dfs_is_chan_allowed(chan, n_chans) ||
+ 		     !(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P))) {
+ 			wpa_printf(MSG_DEBUG,
+@@ -548,7 +548,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
  		return NULL;
  
  	/* Get the count first */
@@ -200,7 +209,7 @@
  	wpa_printf(MSG_DEBUG, "DFS: num_available_chandefs=%d",
  		   num_available_chandefs);
  	if (num_available_chandefs == 0)
-@@ -569,7 +570,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+@@ -569,7 +569,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
  		return NULL;
  
  	chan_idx = _rand % num_available_chandefs;
@@ -209,7 +218,7 @@
  	if (!chan) {
  		wpa_printf(MSG_DEBUG, "DFS: no random channel found");
  		return NULL;
-@@ -599,7 +600,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+@@ -599,7 +599,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
  		for (i = 0; i < num_available_chandefs - 1; i++) {
  			/* start from chan_idx + 1, end when chan_idx - 1 */
  			chan_idx2 = (chan_idx + 1 + i) % num_available_chandefs;
@@ -218,7 +227,7 @@
  			if (chan2 && abs(chan2->chan - chan->chan) > 12) {
  				/* two channels are not adjacent */
  				sec_chan_idx_80p80 = chan2->chan;
-@@ -1304,6 +1305,9 @@ int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
+@@ -1304,6 +1304,9 @@ int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
  	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
  		      cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
  
@@ -228,7 +237,7 @@
  	return 0;
  }
  
-@@ -1717,14 +1721,15 @@ int hostapd_handle_dfs_offload(struct hostapd_iface *iface)
+@@ -1717,14 +1720,15 @@ int hostapd_handle_dfs_offload(struct hostapd_iface *iface)
  }
  
  
@@ -248,7 +257,7 @@
  
  	if (!iface->conf->ieee80211h || !mode ||
  	    mode->mode != HOSTAPD_MODE_IEEE80211A)
-@@ -1757,18 +1762,129 @@ int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
+@@ -1757,18 +1761,129 @@ int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
  		if (!(chan->flag & HOSTAPD_CHAN_RADAR))
  			continue;
  
diff --git a/recipes-wifi/hostapd/files/wpa_supplicant.uc b/recipes-wifi/hostapd/files/wpa_supplicant.uc
index 2a77551..2ccb21c 100644
--- a/recipes-wifi/hostapd/files/wpa_supplicant.uc
+++ b/recipes-wifi/hostapd/files/wpa_supplicant.uc
@@ -301,6 +301,7 @@
 		bw320_offset: info.bw320_offset,
 		band_idx: info.band_idx,
 		sec_chan_offset: info.sec_chan_offset,
+		punct_bitmap: info.punct_bitmap,
 	};
 	ubus.call("hostapd", "apsta_state", msg);
 }
diff --git a/recipes-wifi/hostapd/hostapd_2.10.3.bb b/recipes-wifi/hostapd/hostapd_2.10.3.bb
index bd511b4..6a469fc 100644
--- a/recipes-wifi/hostapd/hostapd_2.10.3.bb
+++ b/recipes-wifi/hostapd/hostapd_2.10.3.bb
@@ -2,7 +2,7 @@
 HOMEPAGE = "http://w1.fi/hostapd/"
 SECTION = "kernel/userland"
 LICENSE = "BSD-3-Clause"
-LIC_FILES_CHKSUM = "file://hostapd/README;md5=c905478466c90f1cefc0df987c40e172"
+LIC_FILES_CHKSUM = "file://hostapd/README;md5=0e430ef1be3d6eebf257cf493fc7661d"
 
 DEPENDS = "libnl-tiny openssl ubus ucode udebug"
 DEPENDS_append = " ${@bb.utils.contains('DISTRO_FEATURES', 'telemetry2_0', 'telemetry', '', d)}"
@@ -12,7 +12,7 @@
 FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
 FILESEXTRAPATHS_prepend := "${THISDIR}/files/patches-${PV}:"
 
-SRCREV ?= "07c9f183ea744ac04585fb6dd10220c75a5e2e74"
+SRCREV ?= "7e0e69cfeac300414ef0492bc76a2aa164443249"
 SRC_URI = " \
     git://w1.fi/hostap.git;protocol=https;branch=main \
     file://hostapd-full.config \
@@ -71,6 +71,10 @@
     echo "CONFIG_LIBNL20=y" >> ${B}/.config
     echo "CONFIG_LIBNL_TINY=y" >> ${B}/.config
     echo "CONFIG_AFC=y" >> ${B}/.config
+    echo "CONFIG_SAE_PK=y" >> ${B}/.config
+    echo "CONFIG_HS20=y" >> ${B}/.config
+    echo "CONFIG_HE_OVERRIDES=y" >> ${B}/.config
+    echo "CONFIG_EHT_OVERRIDES=y" >> ${B}/.config
 }
 
 do_filogic_patches() {
diff --git a/recipes-wifi/iw/patches-mlo/0001-iw-refactor-frequency-help.patch b/recipes-wifi/iw/patches-mlo/0001-iw-refactor-frequency-help.patch
index d88718b..1b34f78 100644
--- a/recipes-wifi/iw/patches-mlo/0001-iw-refactor-frequency-help.patch
+++ b/recipes-wifi/iw/patches-mlo/0001-iw-refactor-frequency-help.patch
@@ -1,7 +1,7 @@
 From cce989770bf3375bbe4398a5ce80d9a9718460a3 Mon Sep 17 00:00:00 2001
 From: Johannes Berg <johannes.berg@intel.com>
 Date: Fri, 17 May 2024 12:04:06 +0200
-Subject: [PATCH 01/10] iw: refactor frequency help
+Subject: [PATCH 01/13] iw: refactor frequency help
 
 The channel parsing is the same in most places, refactor
 the help strings for that to not duplicate them in all
diff --git a/recipes-wifi/iw/patches-mlo/0002-iw-add-puncturing-support.patch b/recipes-wifi/iw/patches-mlo/0002-iw-add-puncturing-support.patch
index 2ebc109..edfa28e 100644
--- a/recipes-wifi/iw/patches-mlo/0002-iw-add-puncturing-support.patch
+++ b/recipes-wifi/iw/patches-mlo/0002-iw-add-puncturing-support.patch
@@ -1,7 +1,7 @@
 From b29da202bdcbdc75cd75db37175261fb22d1e13b Mon Sep 17 00:00:00 2001
 From: Johannes Berg <johannes.berg@intel.com>
 Date: Fri, 17 May 2024 12:12:10 +0200
-Subject: [PATCH 02/10] iw: add puncturing support
+Subject: [PATCH 02/13] iw: add puncturing support
 
 Parse and pass the puncturing bitmap to the kernel in any
 chandef (except S1G), the kernel will check validity.
diff --git a/recipes-wifi/iw/patches-mlo/0003-util-clarify-comment-about-parsed-pointer.patch b/recipes-wifi/iw/patches-mlo/0003-util-clarify-comment-about-parsed-pointer.patch
index 9ac1723..4d1b745 100644
--- a/recipes-wifi/iw/patches-mlo/0003-util-clarify-comment-about-parsed-pointer.patch
+++ b/recipes-wifi/iw/patches-mlo/0003-util-clarify-comment-about-parsed-pointer.patch
@@ -1,7 +1,7 @@
 From c8b9e772aee35e1db245ec6baa7bce1c7b4110ff Mon Sep 17 00:00:00 2001
 From: Johannes Berg <johannes.berg@intel.com>
 Date: Fri, 17 May 2024 13:19:47 +0200
-Subject: [PATCH 03/10] util: clarify comment about 'parsed' pointer
+Subject: [PATCH 03/13] util: clarify comment about 'parsed' pointer
 
 It took me a while to understand this (again?), so
 clarify the comment here.
diff --git a/recipes-wifi/iw/patches-mlo/0004-iw-remove-sizer-section-and-related-code.patch b/recipes-wifi/iw/patches-mlo/0004-iw-remove-sizer-section-and-related-code.patch
index 909a357..58458e8 100644
--- a/recipes-wifi/iw/patches-mlo/0004-iw-remove-sizer-section-and-related-code.patch
+++ b/recipes-wifi/iw/patches-mlo/0004-iw-remove-sizer-section-and-related-code.patch
@@ -1,7 +1,7 @@
 From 8609336b71a8ae37238dc3f4ddff251a4698a4fc Mon Sep 17 00:00:00 2001
 From: Benjamin Berg <benjamin.berg@intel.com>
 Date: Fri, 3 May 2024 13:12:06 +0200
-Subject: [PATCH 04/10] iw: remove sizer section and related code
+Subject: [PATCH 04/13] iw: remove sizer section and related code
 
 With commit 338059ace9d0 ("iw: change __cmd section scheme to fit
 gcc/clang"), the __cmd section only includes pointers to struct cmd
diff --git a/recipes-wifi/iw/patches-mlo/0005-Revert-iw-allow-specifying-CFLAGS-LIBS-externally.patch b/recipes-wifi/iw/patches-mlo/0005-Revert-iw-allow-specifying-CFLAGS-LIBS-externally.patch
deleted file mode 100644
index 040dfd2..0000000
--- a/recipes-wifi/iw/patches-mlo/0005-Revert-iw-allow-specifying-CFLAGS-LIBS-externally.patch
+++ /dev/null
@@ -1,73 +0,0 @@
-From 42813f2d9097bce908f1b01982096b550d829ac7 Mon Sep 17 00:00:00 2001
-From: Hauke Mehrtens <hauke@hauke-m.de>
-Date: Sun, 21 Nov 2021 00:02:57 +0100
-Subject: [PATCH 05/10] Revert "iw: allow specifying CFLAGS/LIBS externally"
-
-This reverts commit 1325244b77d56fd7a16d1e35fdae0efc151920b1.
-
-The OpenWrt build system provides the CFLAGS and LIBS names from the
-package Makefile to overwrite them for libnl-tiny. This is not possible
-after this upstream change which we revert here any more
-
-Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
----
- Makefile | 20 ++++++++++----------
- 1 file changed, 10 insertions(+), 10 deletions(-)
-
-diff --git a/Makefile b/Makefile
-index 17be33f..2482b93 100644
---- a/Makefile
-+++ b/Makefile
-@@ -46,30 +46,30 @@ NLLIBNAME = libnl-1
- endif
- 
- ifeq ($(NL2FOUND),Y)
--override CFLAGS += -DCONFIG_LIBNL20
--override LIBS += -lnl-genl
-+CFLAGS += -DCONFIG_LIBNL20
-+LIBS += -lnl-genl
- NLLIBNAME = libnl-2.0
- endif
- 
- ifeq ($(NL3xFOUND),Y)
- # libnl 3.2 might be found as 3.2 and 3.0
- NL3FOUND = N
--override CFLAGS += -DCONFIG_LIBNL30
--override LIBS += -lnl-genl-3
-+CFLAGS += -DCONFIG_LIBNL30
-+LIBS += -lnl-genl-3
- NLLIBNAME = libnl-3.0
- endif
- 
- ifeq ($(NL3FOUND),Y)
--override CFLAGS += -DCONFIG_LIBNL30
--override LIBS += -lnl-genl
-+CFLAGS += -DCONFIG_LIBNL30
-+LIBS += -lnl-genl
- NLLIBNAME = libnl-3.0
- endif
- 
- # nl-3.1 has a broken libnl-gnl-3.1.pc file
- # as show by pkg-config --debug --libs --cflags --exact-version=3.1 libnl-genl-3.1;echo $?
- ifeq ($(NL31FOUND),Y)
--override CFLAGS += -DCONFIG_LIBNL30
--override LIBS += -lnl-genl
-+CFLAGS += -DCONFIG_LIBNL30
-+LIBS += -lnl-genl
- NLLIBNAME = libnl-3.1
- endif
- 
-@@ -77,8 +77,8 @@ ifeq ($(NLLIBNAME),)
- $(error Cannot find development files for any supported version of libnl)
- endif
- 
--override LIBS += $(shell $(PKG_CONFIG) --libs $(NLLIBNAME))
--override CFLAGS += $(shell $(PKG_CONFIG) --cflags $(NLLIBNAME))
-+LIBS += $(shell $(PKG_CONFIG) --libs $(NLLIBNAME))
-+CFLAGS += $(shell $(PKG_CONFIG) --cflags $(NLLIBNAME))
- endif # NO_PKG_CONFIG
- 
- ifeq ($(V),1)
--- 
-2.18.0
-
diff --git a/recipes-wifi/iw/patches-mlo/0005-iw-fix-formats-under-MIPS64-PPC.patch b/recipes-wifi/iw/patches-mlo/0005-iw-fix-formats-under-MIPS64-PPC.patch
new file mode 100644
index 0000000..3a4e611
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0005-iw-fix-formats-under-MIPS64-PPC.patch
@@ -0,0 +1,30 @@
+From ae9f84f0225d8499b43c33ffbc590120f4c9e82e Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Tue, 2 Jul 2024 12:35:44 -0700
+Subject: [PATCH 05/13] iw: fix formats under MIPS64/PPC
+
+__SANE_USERSPACE_TYPES__ needs to be defined to get consistent 64-bit
+type defines and to fix -Wformat warnings.
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+Link: https://lore.kernel.org/r/20240702193544.5984-1-rosenp@gmail.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ Makefile | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/Makefile b/Makefile
+index 17be33f..2652fac 100644
+--- a/Makefile
++++ b/Makefile
+@@ -17,6 +17,7 @@ CFLAGS ?= -O2 -g
+ CFLAGS += -Wall -Wextra -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common
+ CFLAGS += -Werror-implicit-function-declaration -Wsign-compare -Wno-unused-parameter
+ CFLAGS += -Wdeclaration-after-statement
++CFLAGS += -D__SANE_USERSPACE_TYPES__
+ CFLAGS += $(CFLAGS_EVAL)
+ CFLAGS += $(EXTRA_CFLAGS)
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/iw/patches-mlo/0006-survey-bss-rx-time.patch b/recipes-wifi/iw/patches-mlo/0006-survey-bss-rx-time.patch
deleted file mode 100644
index 5b61920..0000000
--- a/recipes-wifi/iw/patches-mlo/0006-survey-bss-rx-time.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From a417f83cef66ca22ce0a362e4812954d15721a70 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Mon, 25 Dec 2023 11:24:01 +0800
-Subject: [PATCH 06/10] survey bss rx time
-
----
- survey.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/survey.c b/survey.c
-index 7f5385e..53cec9d 100644
---- a/survey.c
-+++ b/survey.c
-@@ -60,6 +60,9 @@ static int print_survey_handler(struct nl_msg *msg, void *arg)
- 	if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX])
- 		printf("\tchannel receive time:\t\t%llu ms\n",
- 			(unsigned long long)nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]));
-+	if (sinfo[NL80211_SURVEY_INFO_TIME_BSS_RX])
-+		printf("\tchannel BSS receive time:\t%llu ms\n",
-+			(unsigned long long)nla_get_u64(sinfo[NL80211_SURVEY_INFO_TIME_BSS_RX]));
- 	if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX])
- 		printf("\tchannel transmit time:\t\t%llu ms\n",
- 			(unsigned long long)nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]));
--- 
-2.18.0
-
diff --git a/recipes-wifi/iw/patches-mlo/0006-update-nl80211.h.patch b/recipes-wifi/iw/patches-mlo/0006-update-nl80211.h.patch
new file mode 100644
index 0000000..2c924ac
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0006-update-nl80211.h.patch
@@ -0,0 +1,574 @@
+From b49a539e13572f6ab61834d307d5f9e84c03c929 Mon Sep 17 00:00:00 2001
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Wed, 3 Jul 2024 10:37:44 +0200
+Subject: [PATCH 06/13] update nl80211.h
+
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ nl80211.h | 242 +++++++++++++++++++++++++++++-------------------------
+ 1 file changed, 130 insertions(+), 112 deletions(-)
+
+diff --git a/nl80211.h b/nl80211.h
+index f23ecbd..6ae3997 100644
+--- a/nl80211.h
++++ b/nl80211.h
+@@ -413,8 +413,8 @@
+  *	are like for %NL80211_CMD_SET_BEACON, and additionally parameters that
+  *	do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL,
+  *	%NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID,
+- *	%NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE,
+- *	%NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS,
++ *	%NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
++ *	%NL80211_ATTR_CIPHER_SUITE_GROUP, %NL80211_ATTR_WPA_VERSIONS,
+  *	%NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY,
+  *	%NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT,
+  *	%NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS.
+@@ -442,20 +442,15 @@
+  *	stations connected and using at least that link as one of its links.
+  *
+  * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
+- * 	destination %NL80211_ATTR_MAC on the interface identified by
+- * 	%NL80211_ATTR_IFINDEX.
++ *	destination %NL80211_ATTR_MAC on the interface identified by
++ *	%NL80211_ATTR_IFINDEX.
+  * @NL80211_CMD_SET_MPATH:  Set mesh path attributes for mesh path to
+- * 	destination %NL80211_ATTR_MAC on the interface identified by
+- * 	%NL80211_ATTR_IFINDEX.
++ *	destination %NL80211_ATTR_MAC on the interface identified by
++ *	%NL80211_ATTR_IFINDEX.
+  * @NL80211_CMD_NEW_MPATH: Create a new mesh path for the destination given by
+  *	%NL80211_ATTR_MAC via %NL80211_ATTR_MPATH_NEXT_HOP.
+  * @NL80211_CMD_DEL_MPATH: Delete a mesh path to the destination given by
+  *	%NL80211_ATTR_MAC.
+- * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the
+- *	interface identified by %NL80211_ATTR_IFINDEX.
+- * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC
+- *	or, if no MAC address given, all mesh paths, on the interface identified
+- *	by %NL80211_ATTR_IFINDEX.
+  * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by
+  *	%NL80211_ATTR_IFINDEX.
+  *
+@@ -476,15 +471,15 @@
+  *	after being queried by the kernel. CRDA replies by sending a regulatory
+  *	domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our
+  *	current alpha2 if it found a match. It also provides
+- * 	NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each
+- * 	regulatory rule is a nested set of attributes  given by
+- * 	%NL80211_ATTR_REG_RULE_FREQ_[START|END] and
+- * 	%NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by
+- * 	%NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and
+- * 	%NL80211_ATTR_REG_RULE_POWER_MAX_EIRP.
++ *	NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each
++ *	regulatory rule is a nested set of attributes  given by
++ *	%NL80211_ATTR_REG_RULE_FREQ_[START|END] and
++ *	%NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by
++ *	%NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and
++ *	%NL80211_ATTR_REG_RULE_POWER_MAX_EIRP.
+  * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain
+- * 	to the specified ISO/IEC 3166-1 alpha2 country code. The core will
+- * 	store this as a valid request and then query userspace for it.
++ *	to the specified ISO/IEC 3166-1 alpha2 country code. The core will
++ *	store this as a valid request and then query userspace for it.
+  *
+  * @NL80211_CMD_GET_MESH_CONFIG: Get mesh networking properties for the
+  *	interface identified by %NL80211_ATTR_IFINDEX
+@@ -574,31 +569,31 @@
+  * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries.
+  *
+  * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain
+- * 	has been changed and provides details of the request information
+- * 	that caused the change such as who initiated the regulatory request
+- * 	(%NL80211_ATTR_REG_INITIATOR), the wiphy_idx
+- * 	(%NL80211_ATTR_REG_ALPHA2) on which the request was made from if
+- * 	the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or
+- * 	%NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain
+- * 	set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is
+- * 	%NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on
+- * 	to (%NL80211_ATTR_REG_ALPHA2).
++ *	has been changed and provides details of the request information
++ *	that caused the change such as who initiated the regulatory request
++ *	(%NL80211_ATTR_REG_INITIATOR), the wiphy_idx
++ *	(%NL80211_ATTR_REG_ALPHA2) on which the request was made from if
++ *	the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or
++ *	%NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain
++ *	set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is
++ *	%NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on
++ *	to (%NL80211_ATTR_REG_ALPHA2).
+  * @NL80211_CMD_REG_BEACON_HINT: indicates to userspace that an AP beacon
+- * 	has been found while world roaming thus enabling active scan or
+- * 	any mode of operation that initiates TX (beacons) on a channel
+- * 	where we would not have been able to do either before. As an example
+- * 	if you are world roaming (regulatory domain set to world or if your
+- * 	driver is using a custom world roaming regulatory domain) and while
+- * 	doing a passive scan on the 5 GHz band you find an AP there (if not
+- * 	on a DFS channel) you will now be able to actively scan for that AP
+- * 	or use AP mode on your card on that same channel. Note that this will
+- * 	never be used for channels 1-11 on the 2 GHz band as they are always
+- * 	enabled world wide. This beacon hint is only sent if your device had
+- * 	either disabled active scanning or beaconing on a channel. We send to
+- * 	userspace the wiphy on which we removed a restriction from
+- * 	(%NL80211_ATTR_WIPHY) and the channel on which this occurred
+- * 	before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER)
+- * 	the beacon hint was processed.
++ *	has been found while world roaming thus enabling active scan or
++ *	any mode of operation that initiates TX (beacons) on a channel
++ *	where we would not have been able to do either before. As an example
++ *	if you are world roaming (regulatory domain set to world or if your
++ *	driver is using a custom world roaming regulatory domain) and while
++ *	doing a passive scan on the 5 GHz band you find an AP there (if not
++ *	on a DFS channel) you will now be able to actively scan for that AP
++ *	or use AP mode on your card on that same channel. Note that this will
++ *	never be used for channels 1-11 on the 2 GHz band as they are always
++ *	enabled world wide. This beacon hint is only sent if your device had
++ *	either disabled active scanning or beaconing on a channel. We send to
++ *	userspace the wiphy on which we removed a restriction from
++ *	(%NL80211_ATTR_WIPHY) and the channel on which this occurred
++ *	before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER)
++ *	the beacon hint was processed.
+  *
+  * @NL80211_CMD_AUTHENTICATE: authentication request and notification.
+  *	This command is used both as a command (request to authenticate) and
+@@ -1120,7 +1115,7 @@
+  *	current configuration is not changed.  If it is present but
+  *	set to zero, the configuration is changed to don't-care
+  *	(i.e. the device can decide what to do).
+- * @NL80211_CMD_NAN_FUNC_MATCH: Notification sent when a match is reported.
++ * @NL80211_CMD_NAN_MATCH: Notification sent when a match is reported.
+  *	This will contain a %NL80211_ATTR_NAN_MATCH nested attribute and
+  *	%NL80211_ATTR_COOKIE.
+  *
+@@ -1715,21 +1710,21 @@ enum nl80211_commands {
+  *	(see &enum nl80211_plink_action).
+  * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path.
+  * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path
+- * 	info given for %NL80211_CMD_GET_MPATH, nested attribute described at
++ *	info given for %NL80211_CMD_GET_MPATH, nested attribute described at
+  *	&enum nl80211_mpath_info.
+  *
+  * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of
+  *      &enum nl80211_mntr_flags.
+  *
+  * @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the
+- * 	current regulatory domain should be set to or is already set to.
+- * 	For example, 'CR', for Costa Rica. This attribute is used by the kernel
+- * 	to query the CRDA to retrieve one regulatory domain. This attribute can
+- * 	also be used by userspace to query the kernel for the currently set
+- * 	regulatory domain. We chose an alpha2 as that is also used by the
+- * 	IEEE-802.11 country information element to identify a country.
+- * 	Users can also simply ask the wireless core to set regulatory domain
+- * 	to a specific alpha2.
++ *	current regulatory domain should be set to or is already set to.
++ *	For example, 'CR', for Costa Rica. This attribute is used by the kernel
++ *	to query the CRDA to retrieve one regulatory domain. This attribute can
++ *	also be used by userspace to query the kernel for the currently set
++ *	regulatory domain. We chose an alpha2 as that is also used by the
++ *	IEEE-802.11 country information element to identify a country.
++ *	Users can also simply ask the wireless core to set regulatory domain
++ *	to a specific alpha2.
+  * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory
+  *	rules.
+  *
+@@ -1772,9 +1767,9 @@ enum nl80211_commands {
+  * @NL80211_ATTR_BSS: scan result BSS
+  *
+  * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain
+- * 	currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_*
++ *	currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_*
+  * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently
+- * 	set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*)
++ *	set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*)
+  *
+  * @NL80211_ATTR_SUPPORTED_COMMANDS: wiphy attribute that specifies
+  *	an array of command numbers (i.e. a mapping index to command number)
+@@ -1793,15 +1788,15 @@ enum nl80211_commands {
+  *	a u32
+  *
+  * @NL80211_ATTR_FREQ_BEFORE: A channel which has suffered a regulatory change
+- * 	due to considerations from a beacon hint. This attribute reflects
+- * 	the state of the channel _before_ the beacon hint processing. This
+- * 	attributes consists of a nested attribute containing
+- * 	NL80211_FREQUENCY_ATTR_*
++ *	due to considerations from a beacon hint. This attribute reflects
++ *	the state of the channel _before_ the beacon hint processing. This
++ *	attributes consists of a nested attribute containing
++ *	NL80211_FREQUENCY_ATTR_*
+  * @NL80211_ATTR_FREQ_AFTER: A channel which has suffered a regulatory change
+- * 	due to considerations from a beacon hint. This attribute reflects
+- * 	the state of the channel _after_ the beacon hint processing. This
+- * 	attributes consists of a nested attribute containing
+- * 	NL80211_FREQUENCY_ATTR_*
++ *	due to considerations from a beacon hint. This attribute reflects
++ *	the state of the channel _after_ the beacon hint processing. This
++ *	attributes consists of a nested attribute containing
++ *	NL80211_FREQUENCY_ATTR_*
+  *
+  * @NL80211_ATTR_CIPHER_SUITES: a set of u32 values indicating the supported
+  *	cipher suites
+@@ -1862,12 +1857,6 @@ enum nl80211_commands {
+  *	that protected APs should be used. This is also used with NEW_BEACON to
+  *	indicate that the BSS is to use protection.
+  *
+- * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT, ASSOCIATE, and NEW_BEACON
+- *	to indicate which unicast key ciphers will be used with the connection
+- *	(an array of u32).
+- * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+- *	indicate which group key cipher will be used with the connection (a
+- *	u32).
+  * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+  *	indicate which WPA version(s) the AP we want to associate with is using
+  *	(a u32 with flags from &enum nl80211_wpa_versions).
+@@ -1898,6 +1887,7 @@ enum nl80211_commands {
+  *	with %NL80211_KEY_* sub-attributes
+  *
+  * @NL80211_ATTR_PID: Process ID of a network namespace.
++ * @NL80211_ATTR_NETNS_FD: File descriptor of a network namespace.
+  *
+  * @NL80211_ATTR_GENERATION: Used to indicate consistent snapshots for
+  *	dumps. This number increases whenever the object list being
+@@ -1952,6 +1942,7 @@ enum nl80211_commands {
+  *
+  * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was
+  *	acknowledged by the recipient.
++ * @NL80211_ATTR_ACK_SIGNAL: Station's ack signal strength (s32)
+  *
+  * @NL80211_ATTR_PS_STATE: powersave state, using &enum nl80211_ps_state values.
+  *
+@@ -2149,6 +2140,9 @@ enum nl80211_commands {
+  * @NL80211_ATTR_DISABLE_HE: Force HE capable interfaces to disable
+  *      this feature during association. This is a flag attribute.
+  *	Currently only supported in mac80211 drivers.
++ * @NL80211_ATTR_DISABLE_EHT: Force EHT capable interfaces to disable
++ *      this feature during association. This is a flag attribute.
++ *	Currently only supported in mac80211 drivers.
+  * @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the
+  *      ATTR_HT_CAPABILITY to which attention should be paid.
+  *      Currently, only mac80211 NICs support this feature.
+@@ -2158,6 +2152,12 @@ enum nl80211_commands {
+  *      All values are treated as suggestions and may be ignored
+  *      by the driver as required.  The actual values may be seen in
+  *      the station debugfs ht_caps file.
++ * @NL80211_ATTR_VHT_CAPABILITY_MASK: Specify which bits of the
++ *      ATTR_VHT_CAPABILITY to which attention should be paid.
++ *      Currently, only mac80211 NICs support this feature.
++ *      All values are treated as suggestions and may be ignored
++ *      by the driver as required.  The actual values may be seen in
++ *      the station debugfs vht_caps file.
+  *
+  * @NL80211_ATTR_DFS_REGION: region for regulatory rules which this country
+  *    abides to when initiating radiation on DFS channels. A country maps
+@@ -2416,7 +2416,7 @@ enum nl80211_commands {
+  *	scheduled scan is started.  Or the delay before a WoWLAN
+  *	net-detect scan is started, counting from the moment the
+  *	system is suspended.  This value is a u32, in seconds.
+-
++ *
+  * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
+  *      is operating in an indoor environment.
+  *
+@@ -3565,7 +3565,7 @@ enum nl80211_sta_flags {
+  * enum nl80211_sta_p2p_ps_status - station support of P2P PS
+  *
+  * @NL80211_P2P_PS_UNSUPPORTED: station doesn't support P2P PS mechanism
+- * @@NL80211_P2P_PS_SUPPORTED: station supports P2P PS mechanism
++ * @NL80211_P2P_PS_SUPPORTED: station supports P2P PS mechanism
+  * @NUM_NL80211_P2P_PS_STATUS: number of values
+  */
+ enum nl80211_sta_p2p_ps_status {
+@@ -3603,9 +3603,9 @@ enum nl80211_he_gi {
+ 
+ /**
+  * enum nl80211_he_ltf - HE long training field
+- * @NL80211_RATE_INFO_HE_1xLTF: 3.2 usec
+- * @NL80211_RATE_INFO_HE_2xLTF: 6.4 usec
+- * @NL80211_RATE_INFO_HE_4xLTF: 12.8 usec
++ * @NL80211_RATE_INFO_HE_1XLTF: 3.2 usec
++ * @NL80211_RATE_INFO_HE_2XLTF: 6.4 usec
++ * @NL80211_RATE_INFO_HE_4XLTF: 12.8 usec
+  */
+ enum nl80211_he_ltf {
+ 	NL80211_RATE_INFO_HE_1XLTF,
+@@ -3720,7 +3720,7 @@ enum nl80211_eht_ru_alloc {
+  * @NL80211_RATE_INFO_HE_GI: HE guard interval identifier
+  *	(u8, see &enum nl80211_he_gi)
+  * @NL80211_RATE_INFO_HE_DCM: HE DCM value (u8, 0/1)
+- * @NL80211_RATE_INFO_RU_ALLOC: HE RU allocation, if not present then
++ * @NL80211_RATE_INFO_HE_RU_ALLOC: HE RU allocation, if not present then
+  *	non-OFDMA was used (u8, see &enum nl80211_he_ru_alloc)
+  * @NL80211_RATE_INFO_320_MHZ_WIDTH: 320 MHz bitrate
+  * @NL80211_RATE_INFO_EHT_MCS: EHT MCS index (u8, 0-15)
+@@ -3823,7 +3823,7 @@ enum nl80211_sta_bss_param {
+  *	(u64, to this station)
+  * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm)
+  * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute
+- * 	containing info as possible, see &enum nl80211_rate_info
++ *	containing info as possible, see &enum nl80211_rate_info
+  * @NL80211_STA_INFO_RX_PACKETS: total received packet (MSDUs and MMPDUs)
+  *	(u32, from this station)
+  * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (MSDUs and MMPDUs)
+@@ -3852,8 +3852,8 @@ enum nl80211_sta_bss_param {
+  *	Contains a nested array of signal strength attributes (u8, dBm)
+  * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average
+  *	Same format as NL80211_STA_INFO_CHAIN_SIGNAL.
+- * @NL80211_STA_EXPECTED_THROUGHPUT: expected throughput considering also the
+- *	802.11 header (u32, kbps)
++ * @NL80211_STA_INFO_EXPECTED_THROUGHPUT: expected throughput considering also
++ *	the 802.11 header (u32, kbps)
+  * @NL80211_STA_INFO_RX_DROP_MISC: RX packets dropped for unspecified reasons
+  *	(u64)
+  * @NL80211_STA_INFO_BEACON_RX: number of beacons received from this peer (u64)
+@@ -4039,7 +4039,7 @@ enum nl80211_mpath_flags {
+  * @NL80211_MPATH_INFO_METRIC: metric (cost) of this mesh path
+  * @NL80211_MPATH_INFO_EXPTIME: expiration time for the path, in msec from now
+  * @NL80211_MPATH_INFO_FLAGS: mesh path flags, enumerated in
+- * 	&enum nl80211_mpath_flags;
++ *	&enum nl80211_mpath_flags;
+  * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec
+  * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries
+  * @NL80211_MPATH_INFO_HOP_COUNT: hop count to destination
+@@ -4179,7 +4179,7 @@ enum nl80211_band_attr {
+  * @NL80211_WMMR_CW_MAX: Maximum contention window slot.
+  * @NL80211_WMMR_AIFSN: Arbitration Inter Frame Space.
+  * @NL80211_WMMR_TXOP: Maximum allowed tx operation time.
+- * @nl80211_WMMR_MAX: highest possible wmm rule.
++ * @NL80211_WMMR_MAX: highest possible wmm rule.
+  * @__NL80211_WMMR_LAST: Internal use.
+  */
+ enum nl80211_wmm_rule {
+@@ -4201,8 +4201,9 @@ enum nl80211_wmm_rule {
+  * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current
+  *	regulatory domain.
+  * @NL80211_FREQUENCY_ATTR_NO_IR: no mechanisms that initiate radiation
+- * 	are permitted on this channel, this includes sending probe
+- * 	requests, or modes of operation that require beaconing.
++ *	are permitted on this channel, this includes sending probe
++ *	requests, or modes of operation that require beaconing.
++ * @__NL80211_FREQUENCY_ATTR_NO_IBSS: obsolete, same as _NO_IR
+  * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory
+  *	on this channel in current regulatory domain.
+  * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm
+@@ -4276,6 +4277,8 @@ enum nl80211_wmm_rule {
+  * @NL80211_FREQUENCY_ATTR_CAN_MONITOR: This channel can be used in monitor
+  *	mode despite other (regulatory) restrictions, even if the channel is
+  *	otherwise completely disabled.
++ * @NL80211_FREQUENCY_ATTR_ALLOW_6GHZ_VLP_AP: This channel can be used for a
++ *	very low power (VLP) AP, despite being NO_IR.
+  * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
+  *	currently defined
+  * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
+@@ -4319,6 +4322,7 @@ enum nl80211_frequency_attr {
+ 	NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT,
+ 	NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT,
+ 	NL80211_FREQUENCY_ATTR_CAN_MONITOR,
++	NL80211_FREQUENCY_ATTR_ALLOW_6GHZ_VLP_AP,
+ 
+ 	/* keep last */
+ 	__NL80211_FREQUENCY_ATTR_AFTER_LAST,
+@@ -4357,16 +4361,16 @@ enum nl80211_bitrate_attr {
+ };
+ 
+ /**
+- * enum nl80211_initiator - Indicates the initiator of a reg domain request
++ * enum nl80211_reg_initiator - Indicates the initiator of a reg domain request
+  * @NL80211_REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world
+- * 	regulatory domain.
++ *	regulatory domain.
+  * @NL80211_REGDOM_SET_BY_USER: User asked the wireless core to set the
+- * 	regulatory domain.
++ *	regulatory domain.
+  * @NL80211_REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the
+- * 	wireless core it thinks its knows the regulatory domain we should be in.
++ *	wireless core it thinks its knows the regulatory domain we should be in.
+  * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an
+- * 	802.11 country information element with regulatory information it
+- * 	thinks we should consider. cfg80211 only processes the country
++ *	802.11 country information element with regulatory information it
++ *	thinks we should consider. cfg80211 only processes the country
+  *	code from the IE, and relies on the regulatory domain information
+  *	structure passed by userspace (CRDA) from our wireless-regdb.
+  *	If a channel is enabled but the country code indicates it should
+@@ -4385,11 +4389,11 @@ enum nl80211_reg_initiator {
+  *	to a specific country. When this is set you can count on the
+  *	ISO / IEC 3166 alpha2 country code being valid.
+  * @NL80211_REGDOM_TYPE_WORLD: the regulatory set domain is the world regulatory
+- * 	domain.
++ *	domain.
+  * @NL80211_REGDOM_TYPE_CUSTOM_WORLD: the regulatory domain set is a custom
+- * 	driver specific world regulatory domain. These do not apply system-wide
+- * 	and are only applicable to the individual devices which have requested
+- * 	them to be applied.
++ *	driver specific world regulatory domain. These do not apply system-wide
++ *	and are only applicable to the individual devices which have requested
++ *	them to be applied.
+  * @NL80211_REGDOM_TYPE_INTERSECTION: the regulatory domain set is the product
+  *	of an intersection between two regulatory domains -- the previously
+  *	set regulatory domain on the system and the last accepted regulatory
+@@ -4406,21 +4410,21 @@ enum nl80211_reg_type {
+  * enum nl80211_reg_rule_attr - regulatory rule attributes
+  * @__NL80211_REG_RULE_ATTR_INVALID: attribute number 0 is reserved
+  * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional
+- * 	considerations for a given frequency range. These are the
+- * 	&enum nl80211_reg_rule_flags.
++ *	considerations for a given frequency range. These are the
++ *	&enum nl80211_reg_rule_flags.
+  * @NL80211_ATTR_FREQ_RANGE_START: starting frequencry for the regulatory
+- * 	rule in KHz. This is not a center of frequency but an actual regulatory
+- * 	band edge.
++ *	rule in KHz. This is not a center of frequency but an actual regulatory
++ *	band edge.
+  * @NL80211_ATTR_FREQ_RANGE_END: ending frequency for the regulatory rule
+- * 	in KHz. This is not a center a frequency but an actual regulatory
+- * 	band edge.
++ *	in KHz. This is not a center a frequency but an actual regulatory
++ *	band edge.
+  * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this
+  *	frequency range, in KHz.
+  * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain
+- * 	for a given frequency range. The value is in mBi (100 * dBi).
+- * 	If you don't have one then don't send this.
++ *	for a given frequency range. The value is in mBi (100 * dBi).
++ *	If you don't have one then don't send this.
+  * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for
+- * 	a given frequency range. The value is in mBm (100 * dBm).
++ *	a given frequency range. The value is in mBm (100 * dBm).
+  * @NL80211_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+  *	If not present or 0 default CAC time will be used.
+  * @NL80211_ATTR_POWER_RULE_PSD: power spectral density (in dBm).
+@@ -4507,8 +4511,9 @@ enum nl80211_sched_scan_match_attr {
+  * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links
+  * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links
+  * @NL80211_RRF_NO_IR: no mechanisms that initiate radiation are allowed,
+- * 	this includes probe requests or modes of operation that require
+- * 	beaconing.
++ *	this includes probe requests or modes of operation that require
++ *	beaconing.
++ * @__NL80211_RRF_NO_IBSS: obsolete, same as NO_IR
+  * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated
+  *	base on contiguous rules and wider channels will be allowed to cross
+  *	multiple contiguous/overlapping frequency ranges.
+@@ -4522,11 +4527,13 @@ enum nl80211_sched_scan_match_attr {
+  * @NL80211_RRF_NO_EHT: EHT operation not allowed
+  * @NL80211_RRF_PSD: Ruleset has power spectral density value
+  * @NL80211_RRF_DFS_CONCURRENT: Operation on this channel is allowed for
+-	peer-to-peer or adhoc communication under the control of a DFS master
+-	which operates on the same channel (FCC-594280 D01 Section B.3).
+-	Should be used together with %NL80211_RRF_DFS only.
++ *	peer-to-peer or adhoc communication under the control of a DFS master
++ *	which operates on the same channel (FCC-594280 D01 Section B.3).
++ *	Should be used together with %NL80211_RRF_DFS only.
+  * @NL80211_RRF_NO_6GHZ_VLP_CLIENT: Client connection to VLP AP not allowed
+  * @NL80211_RRF_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP not allowed
++ * @NL80211_RRF_ALLOW_6GHZ_VLP_AP: Very low power (VLP) AP can be permitted
++ *	despite NO_IR configuration.
+  */
+ enum nl80211_reg_rule_flags {
+ 	NL80211_RRF_NO_OFDM		= 1<<0,
+@@ -4551,6 +4558,7 @@ enum nl80211_reg_rule_flags {
+ 	NL80211_RRF_DFS_CONCURRENT	= 1<<21,
+ 	NL80211_RRF_NO_6GHZ_VLP_CLIENT	= 1<<22,
+ 	NL80211_RRF_NO_6GHZ_AFC_CLIENT	= 1<<23,
++	NL80211_RRF_ALLOW_6GHZ_VLP_AP	= 1<<24,
+ };
+ 
+ #define NL80211_RRF_PASSIVE_SCAN	NL80211_RRF_NO_IR
+@@ -4707,8 +4715,8 @@ enum nl80211_mntr_flags {
+  *	alternate between Active and Doze states, but may not wake up
+  *	for neighbor's beacons.
+  *
+- * @__NL80211_MESH_POWER_AFTER_LAST - internal use
+- * @NL80211_MESH_POWER_MAX - highest possible power save level
++ * @__NL80211_MESH_POWER_AFTER_LAST: internal use
++ * @NL80211_MESH_POWER_MAX: highest possible power save level
+  */
+ 
+ enum nl80211_mesh_power_mode {
+@@ -5728,7 +5736,7 @@ struct nl80211_pattern_support {
+  *	"TCP connection wakeup" for more details. This is a nested attribute
+  *	containing the exact information for establishing and keeping alive
+  *	the TCP connection.
+- * @NL80211_WOWLAN_TRIG_TCP_WAKEUP_MATCH: For wakeup reporting only, the
++ * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH: For wakeup reporting only, the
+  *	wakeup packet was received on the TCP connection
+  * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST: For wakeup reporting only, the
+  *	TCP connection was lost or failed to be established
+@@ -6077,7 +6085,7 @@ enum nl80211_plink_state {
+  * @NL80211_PLINK_ACTION_BLOCK: block traffic from this mesh peer
+  * @NUM_NL80211_PLINK_ACTIONS: number of possible actions
+  */
+-enum plink_actions {
++enum nl80211_plink_action {
+ 	NL80211_PLINK_ACTION_NO_ACTION,
+ 	NL80211_PLINK_ACTION_OPEN,
+ 	NL80211_PLINK_ACTION_BLOCK,
+@@ -6404,6 +6412,7 @@ enum nl80211_feature_flags {
+  *	receiving control port frames over nl80211 instead of the netdevice.
+  * @NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT: This driver/device supports
+  *	(average) ACK signal strength reporting.
++ * @NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT: Backward-compatible ID
+  * @NL80211_EXT_FEATURE_TXQS: Driver supports FQ-CoDel-enabled intermediate
+  *      TXQs.
+  * @NL80211_EXT_FEATURE_SCAN_RANDOM_SN: Driver/device supports randomizing the
+@@ -6787,6 +6796,8 @@ enum nl80211_acl_policy {
+  * @NL80211_SMPS_STATIC: static SMPS (use a single antenna)
+  * @NL80211_SMPS_DYNAMIC: dynamic smps (start with a single antenna and
+  *	turn on other antennas after CTS/RTS).
++ * @__NL80211_SMPS_AFTER_LAST: internal
++ * @NL80211_SMPS_MAX: highest used enumeration
+  */
+ enum nl80211_smps_mode {
+ 	NL80211_SMPS_OFF,
+@@ -7008,6 +7019,8 @@ enum nl80211_bss_select_attr {
+  * @NL80211_NAN_FUNC_PUBLISH: function is publish
+  * @NL80211_NAN_FUNC_SUBSCRIBE: function is subscribe
+  * @NL80211_NAN_FUNC_FOLLOW_UP: function is follow-up
++ * @__NL80211_NAN_FUNC_TYPE_AFTER_LAST: internal use
++ * @NL80211_NAN_FUNC_MAX_TYPE: internal use
+  */
+ enum nl80211_nan_function_type {
+ 	NL80211_NAN_FUNC_PUBLISH,
+@@ -7168,7 +7181,7 @@ enum nl80211_nan_match_attributes {
+ };
+ 
+ /**
+- * nl80211_external_auth_action - Action to perform with external
++ * enum nl80211_external_auth_action - Action to perform with external
+  *     authentication request. Used by NL80211_ATTR_EXTERNAL_AUTH_ACTION.
+  * @NL80211_EXTERNAL_AUTH_START: Start the authentication.
+  * @NL80211_EXTERNAL_AUTH_ABORT: Abort the ongoing authentication.
+@@ -7186,7 +7199,7 @@ enum nl80211_external_auth_action {
+  * @NL80211_FTM_RESP_ATTR_LCI: The content of Measurement Report Element
+  *	(9.4.2.22 in 802.11-2016) with type 8 - LCI (9.4.2.22.10),
+  *	i.e. starting with the measurement token
+- * @NL80211_FTM_RESP_ATTR_CIVIC: The content of Measurement Report Element
++ * @NL80211_FTM_RESP_ATTR_CIVICLOC: The content of Measurement Report Element
+  *	(9.4.2.22 in 802.11-2016) with type 11 - Civic (Section 9.4.2.22.13),
+  *	i.e. starting with the measurement token
+  * @__NL80211_FTM_RESP_ATTR_LAST: Internal
+@@ -7829,6 +7842,7 @@ enum nl80211_sae_pwe_mechanism {
+  *
+  * @NL80211_SAR_TYPE_POWER: power limitation specified in 0.25dBm unit
+  *
++ * @NUM_NL80211_SAR_TYPE: internal
+  */
+ enum nl80211_sar_type {
+ 	NL80211_SAR_TYPE_POWER,
+@@ -7842,6 +7856,8 @@ enum nl80211_sar_type {
+ /**
+  * enum nl80211_sar_attrs - Attributes for SAR spec
+  *
++ * @__NL80211_SAR_ATTR_INVALID: Invalid
++ *
+  * @NL80211_SAR_ATTR_TYPE: the SAR type as defined in &enum nl80211_sar_type.
+  *
+  * @NL80211_SAR_ATTR_SPECS: Nested array of SAR power
+@@ -7873,6 +7889,8 @@ enum nl80211_sar_attrs {
+ /**
+  * enum nl80211_sar_specs_attrs - Attributes for SAR power limit specs
+  *
++ * @__NL80211_SAR_ATTR_SPECS_INVALID: Invalid
++ *
+  * @NL80211_SAR_ATTR_SPECS_POWER: Required (s32)value to specify the actual
+  *	power limit value in units of 0.25 dBm if type is
+  *	NL80211_SAR_TYPE_POWER. (i.e., a value of 44 represents 11 dBm).
+-- 
+2.18.0
+
diff --git a/recipes-wifi/iw/patches-mlo/0007-Revert-iw-allow-specifying-CFLAGS-LIBS-externally.patch b/recipes-wifi/iw/patches-mlo/0007-Revert-iw-allow-specifying-CFLAGS-LIBS-externally.patch
new file mode 100644
index 0000000..7d5a0f0
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0007-Revert-iw-allow-specifying-CFLAGS-LIBS-externally.patch
@@ -0,0 +1,73 @@
+From aafc8ce9667fe00c16c3095cd3471d60a72fca0c Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Sun, 21 Nov 2021 00:02:57 +0100
+Subject: [PATCH 07/13] Revert "iw: allow specifying CFLAGS/LIBS externally"
+
+This reverts commit 1325244b77d56fd7a16d1e35fdae0efc151920b1.
+
+The OpenWrt build system provides the CFLAGS and LIBS names from the
+package Makefile to overwrite them for libnl-tiny. This is not possible
+after this upstream change which we revert here any more
+
+Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+---
+ Makefile | 20 ++++++++++----------
+ 1 file changed, 10 insertions(+), 10 deletions(-)
+
+diff --git a/Makefile b/Makefile
+index 2652fac..07289a9 100644
+--- a/Makefile
++++ b/Makefile
+@@ -47,30 +47,30 @@ NLLIBNAME = libnl-1
+ endif
+ 
+ ifeq ($(NL2FOUND),Y)
+-override CFLAGS += -DCONFIG_LIBNL20
+-override LIBS += -lnl-genl
++CFLAGS += -DCONFIG_LIBNL20
++LIBS += -lnl-genl
+ NLLIBNAME = libnl-2.0
+ endif
+ 
+ ifeq ($(NL3xFOUND),Y)
+ # libnl 3.2 might be found as 3.2 and 3.0
+ NL3FOUND = N
+-override CFLAGS += -DCONFIG_LIBNL30
+-override LIBS += -lnl-genl-3
++CFLAGS += -DCONFIG_LIBNL30
++LIBS += -lnl-genl-3
+ NLLIBNAME = libnl-3.0
+ endif
+ 
+ ifeq ($(NL3FOUND),Y)
+-override CFLAGS += -DCONFIG_LIBNL30
+-override LIBS += -lnl-genl
++CFLAGS += -DCONFIG_LIBNL30
++LIBS += -lnl-genl
+ NLLIBNAME = libnl-3.0
+ endif
+ 
+ # nl-3.1 has a broken libnl-gnl-3.1.pc file
+ # as show by pkg-config --debug --libs --cflags --exact-version=3.1 libnl-genl-3.1;echo $?
+ ifeq ($(NL31FOUND),Y)
+-override CFLAGS += -DCONFIG_LIBNL30
+-override LIBS += -lnl-genl
++CFLAGS += -DCONFIG_LIBNL30
++LIBS += -lnl-genl
+ NLLIBNAME = libnl-3.1
+ endif
+ 
+@@ -78,8 +78,8 @@ ifeq ($(NLLIBNAME),)
+ $(error Cannot find development files for any supported version of libnl)
+ endif
+ 
+-override LIBS += $(shell $(PKG_CONFIG) --libs $(NLLIBNAME))
+-override CFLAGS += $(shell $(PKG_CONFIG) --cflags $(NLLIBNAME))
++LIBS += $(shell $(PKG_CONFIG) --libs $(NLLIBNAME))
++CFLAGS += $(shell $(PKG_CONFIG) --cflags $(NLLIBNAME))
+ endif # NO_PKG_CONFIG
+ 
+ ifeq ($(V),1)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/iw/patches-mlo/0007-iw-support-link-id-in-set-bitrates-command.patch b/recipes-wifi/iw/patches-mlo/0007-iw-support-link-id-in-set-bitrates-command.patch
deleted file mode 100644
index 8d0e0a0..0000000
--- a/recipes-wifi/iw/patches-mlo/0007-iw-support-link-id-in-set-bitrates-command.patch
+++ /dev/null
@@ -1,59 +0,0 @@
-From f885f9be220545bc3d62c369d68ded69e2711b52 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Thu, 30 May 2024 10:17:29 +0800
-Subject: [PATCH 07/10] iw: support link id in set bitrates command
-
-Since NL80211_CMD_SET_TX_BITRATE_MASK includes flag
-NL80211_FLAG_MLO_VALID_LINK_ID, nl80211_pre_do_it() will check whether
-nl80211_msg includes LINK_ID. Without this patch, iw set bitrates
-command must failed.
-
-Usage:
-iw dev <intf> set bitrates -l <link_id> [legacy | he-gi-xx ...]
-
-CR-Id: WCNCR00261410
-Change-Id: I4984054543f63c9324d9b81a240382023fbc0b0b
----
- bitrate.c | 19 +++++++++++++++++--
- 1 file changed, 17 insertions(+), 2 deletions(-)
-
-diff --git a/bitrate.c b/bitrate.c
-index 8714669..f0f81a0 100644
---- a/bitrate.c
-+++ b/bitrate.c
-@@ -146,7 +146,7 @@ int set_bitrates(struct nl_msg *msg,
- 		 enum nl80211_attrs attr)
- {
- 	struct nlattr *nl_rates, *nl_band;
--	int i, ret = 0;
-+	int i, ret = 0, index = 0;
- 	bool have_legacy_24 = false, have_legacy_5 = false;
- 	uint8_t legacy_24[32], legacy_5[32];
- 	int n_legacy_24 = 0, n_legacy_5 = 0;
-@@ -190,7 +190,22 @@ int set_bitrates(struct nl_msg *msg,
- 		S_HE_LTF,
- 	} parser_state = S_NONE;
- 
--	for (i = 0; i < argc; i++) {
-+	if (!strcmp(argv[0], "-l")) {
-+		unsigned int link_id;
-+		char *endptr;
-+
-+		link_id = strtol(argv[1], &endptr, 10);
-+		if (*endptr)
-+			return 1;
-+
-+		if (link_id > 15)
-+			return 1;
-+
-+		nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id);
-+		index = 2;
-+	}
-+
-+	for (i = index; i < argc; i++) {
- 		char *end;
- 		double tmpd;
- 		long tmpl;
--- 
-2.18.0
-
diff --git a/recipes-wifi/iw/patches-mlo/0008-iw-add-per-link-txpower-config.patch b/recipes-wifi/iw/patches-mlo/0008-iw-add-per-link-txpower-config.patch
deleted file mode 100644
index 61bf8e0..0000000
--- a/recipes-wifi/iw/patches-mlo/0008-iw-add-per-link-txpower-config.patch
+++ /dev/null
@@ -1,140 +0,0 @@
-From 248f4644bcfcf5701fb3ab576736091b6eb4f662 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 5 Jun 2024 22:10:24 +0800
-Subject: [PATCH 08/10] iw: add per-link txpower config
-
-Add per-link txpower config & info dump
-
-CR-Id: WCNCR00274293
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- ieee80211.h |  3 +++
- interface.c |  9 +++++++--
- phy.c       | 38 ++++++++++++++++++++++++++------------
- 3 files changed, 36 insertions(+), 14 deletions(-)
-
-diff --git a/ieee80211.h b/ieee80211.h
-index 3713a4d..c86984d 100644
---- a/ieee80211.h
-+++ b/ieee80211.h
-@@ -75,4 +75,7 @@ struct ieee80211_vht_cap {
- #define WLAN_CIPHER_SUITE_BIP_GMAC_256  SUITE(0x000FAC, 12)
- #define WLAN_CIPHER_SUITE_BIP_CMAC_256  SUITE(0x000FAC, 13)
- 
-+/* multi-link device */
-+#define IEEE80211_MLD_MAX_NUM_LINKS	15
-+
- #endif /* __IEEE80211 */
-diff --git a/interface.c b/interface.c
-index bb1a1d3..7a690fa 100644
---- a/interface.c
-+++ b/interface.c
-@@ -406,6 +406,7 @@ static int print_iface_handler(struct nl_msg *msg, void *arg)
- 	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
- 	unsigned int *wiphy = arg;
- 	const char *indent = "";
-+	int32_t txp;
- 
- 	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
- 		  genlmsg_attrlen(gnlh, 0), NULL);
-@@ -449,8 +450,7 @@ static int print_iface_handler(struct nl_msg *msg, void *arg)
- 	}
- 
- 	if (tb_msg[NL80211_ATTR_WIPHY_TX_POWER_LEVEL]) {
--		int32_t txp = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_TX_POWER_LEVEL]);
--
-+		txp = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_TX_POWER_LEVEL]);
- 		printf("%s\ttxpower %d.%.2d dBm\n",
- 		       indent, txp / 100, txp % 100);
- 	}
-@@ -490,6 +490,11 @@ static int print_iface_handler(struct nl_msg *msg, void *arg)
- 				printf("\n%s\t   ", indent);
- 				print_channel(tb);
- 			}
-+			if (tb[NL80211_ATTR_WIPHY_TX_POWER_LEVEL]) {
-+				txp = nla_get_u32(tb[NL80211_ATTR_WIPHY_TX_POWER_LEVEL]);
-+				printf("\n%s\t   ", indent);
-+				printf("txpower %d.%.2d dBm", txp / 100, txp % 100);
-+			}
- 			printf("\n");
- 		}
- 	}
-diff --git a/phy.c b/phy.c
-index 584b103..f9a5b0f 100644
---- a/phy.c
-+++ b/phy.c
-@@ -686,37 +686,51 @@ static int handle_txpower(struct nl80211_state *state,
- 			  enum id_input id)
- {
- 	enum nl80211_tx_power_setting type;
--	int mbm;
-+	unsigned int link_id;
-+	int mbm, pos = 0;
-+	char *endptr;
- 
--	/* get the required args */
--	if (argc != 1 && argc != 2)
-+	/* check args number */
-+	if (argc < 1 && argc > 4)
- 		return 1;
- 
--	if (!strcmp(argv[0], "auto"))
-+	if (!strcmp(argv[0], "-l")) {
-+		link_id = strtol(argv[1], &endptr, 10);
-+		if (*endptr)
-+			return 1;
-+
-+		if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) {
-+			printf("link id %d exceeds max number of links\n", link_id);
-+			return 2;
-+		}
-+		NLA_PUT_U8(msg, NL80211_ATTR_MLO_LINK_ID, link_id);
-+		pos += 2;
-+	}
-+
-+	if (!strcmp(argv[pos], "auto"))
- 		type = NL80211_TX_POWER_AUTOMATIC;
--	else if (!strcmp(argv[0], "fixed"))
-+	else if (!strcmp(argv[pos], "fixed"))
- 		type = NL80211_TX_POWER_FIXED;
--	else if (!strcmp(argv[0], "limit"))
-+	else if (!strcmp(argv[pos], "limit"))
- 		type = NL80211_TX_POWER_LIMITED;
- 	else {
--		printf("Invalid parameter: %s\n", argv[0]);
-+		printf("Invalid parameter: %s\n", argv[pos]);
- 		return 2;
- 	}
- 
- 	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_SETTING, type);
- 
- 	if (type != NL80211_TX_POWER_AUTOMATIC) {
--		char *endptr;
--		if (argc != 2) {
-+		if (argc < 2 + pos) {
- 			printf("Missing TX power level argument.\n");
- 			return 2;
- 		}
- 
--		mbm = strtol(argv[1], &endptr, 10);
-+		mbm = strtol(argv[pos + 1], &endptr, 10);
- 		if (*endptr)
- 			return 2;
- 		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL, mbm);
--	} else if (argc != 1)
-+	} else if (argc != 1 + pos)
- 		return 1;
- 
- 	return 0;
-@@ -727,7 +741,7 @@ static int handle_txpower(struct nl80211_state *state,
- COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]",
- 	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_txpower,
- 	"Specify transmit power level and setting type.");
--COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]",
-+COMMAND(set, txpower, "[-l <link_id>] <auto|fixed|limit> [<tx power in mBm>]",
- 	NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_txpower,
- 	"Specify transmit power level and setting type.");
- 
--- 
-2.18.0
-
diff --git a/recipes-wifi/iw/patches-mlo/0008-survey-bss-rx-time.patch b/recipes-wifi/iw/patches-mlo/0008-survey-bss-rx-time.patch
new file mode 100644
index 0000000..915997f
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0008-survey-bss-rx-time.patch
@@ -0,0 +1,26 @@
+From c8d3c48f01b0007f7eeb132fb8bc0d2c0cb85ed7 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 25 Dec 2023 11:24:01 +0800
+Subject: [PATCH 08/13] survey bss rx time
+
+---
+ survey.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/survey.c b/survey.c
+index 7f5385e..53cec9d 100644
+--- a/survey.c
++++ b/survey.c
+@@ -60,6 +60,9 @@ static int print_survey_handler(struct nl_msg *msg, void *arg)
+ 	if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX])
+ 		printf("\tchannel receive time:\t\t%llu ms\n",
+ 			(unsigned long long)nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]));
++	if (sinfo[NL80211_SURVEY_INFO_TIME_BSS_RX])
++		printf("\tchannel BSS receive time:\t%llu ms\n",
++			(unsigned long long)nla_get_u64(sinfo[NL80211_SURVEY_INFO_TIME_BSS_RX]));
+ 	if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX])
+ 		printf("\tchannel transmit time:\t\t%llu ms\n",
+ 			(unsigned long long)nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]));
+-- 
+2.18.0
+
diff --git a/recipes-wifi/iw/patches-mlo/0009-iw-support-link-id-in-set-bitrates-command.patch b/recipes-wifi/iw/patches-mlo/0009-iw-support-link-id-in-set-bitrates-command.patch
new file mode 100644
index 0000000..1914814
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0009-iw-support-link-id-in-set-bitrates-command.patch
@@ -0,0 +1,57 @@
+From e243763029729ced434bcf7de3c5f7909d481c41 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 30 May 2024 10:17:29 +0800
+Subject: [PATCH 09/13] iw: support link id in set bitrates command
+
+Since NL80211_CMD_SET_TX_BITRATE_MASK includes flag
+NL80211_FLAG_MLO_VALID_LINK_ID, nl80211_pre_do_it() will check whether
+nl80211_msg includes LINK_ID. Without this patch, iw set bitrates
+command must failed.
+
+Usage:
+iw dev <intf> set bitrates -l <link_id> [legacy | he-gi-xx ...]
+
+---
+ bitrate.c | 19 +++++++++++++++++--
+ 1 file changed, 17 insertions(+), 2 deletions(-)
+
+diff --git a/bitrate.c b/bitrate.c
+index 8714669..f0f81a0 100644
+--- a/bitrate.c
++++ b/bitrate.c
+@@ -146,7 +146,7 @@ int set_bitrates(struct nl_msg *msg,
+ 		 enum nl80211_attrs attr)
+ {
+ 	struct nlattr *nl_rates, *nl_band;
+-	int i, ret = 0;
++	int i, ret = 0, index = 0;
+ 	bool have_legacy_24 = false, have_legacy_5 = false;
+ 	uint8_t legacy_24[32], legacy_5[32];
+ 	int n_legacy_24 = 0, n_legacy_5 = 0;
+@@ -190,7 +190,22 @@ int set_bitrates(struct nl_msg *msg,
+ 		S_HE_LTF,
+ 	} parser_state = S_NONE;
+ 
+-	for (i = 0; i < argc; i++) {
++	if (!strcmp(argv[0], "-l")) {
++		unsigned int link_id;
++		char *endptr;
++
++		link_id = strtol(argv[1], &endptr, 10);
++		if (*endptr)
++			return 1;
++
++		if (link_id > 15)
++			return 1;
++
++		nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id);
++		index = 2;
++	}
++
++	for (i = index; i < argc; i++) {
+ 		char *end;
+ 		double tmpd;
+ 		long tmpl;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/iw/patches-mlo/0009-mtk-wifi-iw-dump-links-information-in-station-dump.patch b/recipes-wifi/iw/patches-mlo/0009-mtk-wifi-iw-dump-links-information-in-station-dump.patch
deleted file mode 100644
index f6c01f0..0000000
--- a/recipes-wifi/iw/patches-mlo/0009-mtk-wifi-iw-dump-links-information-in-station-dump.patch
+++ /dev/null
@@ -1,94 +0,0 @@
-From be69477058da6305cceb451a8449f41f8f9fe24d Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Tue, 11 Jun 2024 11:18:41 +0800
-Subject: [PATCH 09/10] mtk: wifi: iw: dump links information in station dump
-
-Parse and show the following link information
-1. link address
-2. Rssi
-3. Tx rate
-4. Rx rate
-5. dtim period
-4. beacon interval
-
-CR-Id: WCNCR00240772
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
-Change-Id: Ia6122b7938a38ca0b2eb5060c21193f705d48181
----
- station.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 60 insertions(+)
-
-diff --git a/station.c b/station.c
-index bf7c0f5..e91cb73 100644
---- a/station.c
-+++ b/station.c
-@@ -636,6 +636,66 @@ static int print_sta_handler(struct nl_msg *msg, void *arg)
- 	}
- 
- 	printf("\n\tcurrent time:\t%llu ms\n", now_ms);
-+
-+	printf("\t*** MLD Information ***");
-+	if (tb[NL80211_ATTR_MLO_LINK_ID])
-+		printf("\n\tSetup link = %d", nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]));
-+
-+	if (tb[NL80211_ATTR_MLD_ADDR]) {
-+		mac_addr_n2a(mac_addr, nla_data(tb[NL80211_ATTR_MLD_ADDR]));
-+		printf("\n\tMLD Address: %s", mac_addr);
-+	}
-+
-+	if (tb[NL80211_ATTR_MLO_LINKS]) {
-+		struct nlattr *link;
-+		char buf[100];
-+		int rem;
-+
-+		nla_for_each_nested(link, tb[NL80211_ATTR_MLO_LINKS], rem) {
-+			struct nlattr *tb_link[NL80211_ATTR_MAX + 1];
-+
-+			nla_parse_nested(tb_link, NL80211_ATTR_MAX, link, NULL);
-+
-+			if (tb_link[NL80211_ATTR_MLO_LINK_ID])
-+				printf("\n\t***** Link ID: %2d *****",
-+				       nla_get_u32(tb_link[NL80211_ATTR_MLO_LINK_ID]));
-+			if (tb_link[NL80211_ATTR_MAC]) {
-+				mac_addr_n2a(buf, nla_data(tb_link[NL80211_ATTR_MAC]));
-+				printf("\n\tLink addr: %s", buf);
-+			}
-+			if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
-+					     tb_link[NL80211_ATTR_STA_INFO],
-+					     stats_policy)) {
-+				fprintf(stderr, "failed to parse nested attributes!\n");
-+				return NL_SKIP;
-+			}
-+			if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
-+				parse_bitrate(sinfo[NL80211_STA_INFO_TX_BITRATE],
-+					      buf, sizeof(buf));
-+				printf("\n\ttx bitrate:\t%s", buf);
-+			}
-+			if (sinfo[NL80211_STA_INFO_RX_BITRATE]) {
-+				parse_bitrate(sinfo[NL80211_STA_INFO_RX_BITRATE],
-+					      buf, sizeof(buf));
-+				printf("\n\trx bitrate:\t%s", buf);
-+			}
-+			chain = get_chain_signal(sinfo[NL80211_STA_INFO_CHAIN_SIGNAL]);
-+			if (sinfo[NL80211_STA_INFO_SIGNAL])
-+				printf("\n\tsignal:  \t%d %sdBm",
-+					(int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]),
-+					chain);
-+
-+			chain = get_chain_signal(sinfo[NL80211_STA_INFO_CHAIN_SIGNAL_AVG]);
-+			if (sinfo[NL80211_STA_INFO_SIGNAL_AVG])
-+				printf("\n\tsignal avg:\t%d %sdBm",
-+					(int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]),
-+					chain);
-+
-+			if (sinfo[NL80211_STA_INFO_BSS_PARAM])
-+				parse_bss_param(sinfo[NL80211_STA_INFO_BSS_PARAM]);
-+		}
-+	}
-+	printf("\n");
- 	return NL_SKIP;
- }
- 
--- 
-2.18.0
-
diff --git a/recipes-wifi/iw/patches-mlo/0010-iw-add-per-link-txpower-config.patch b/recipes-wifi/iw/patches-mlo/0010-iw-add-per-link-txpower-config.patch
new file mode 100644
index 0000000..0195296
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0010-iw-add-per-link-txpower-config.patch
@@ -0,0 +1,139 @@
+From 32c260621b73c5662442dd29bdfddbf5269e831b Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 5 Jun 2024 22:10:24 +0800
+Subject: [PATCH 10/13] iw: add per-link txpower config
+
+Add per-link txpower config & info dump
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ ieee80211.h |  3 +++
+ interface.c |  9 +++++++--
+ phy.c       | 38 ++++++++++++++++++++++++++------------
+ 3 files changed, 36 insertions(+), 14 deletions(-)
+
+diff --git a/ieee80211.h b/ieee80211.h
+index 3713a4d..c86984d 100644
+--- a/ieee80211.h
++++ b/ieee80211.h
+@@ -75,4 +75,7 @@ struct ieee80211_vht_cap {
+ #define WLAN_CIPHER_SUITE_BIP_GMAC_256  SUITE(0x000FAC, 12)
+ #define WLAN_CIPHER_SUITE_BIP_CMAC_256  SUITE(0x000FAC, 13)
+ 
++/* multi-link device */
++#define IEEE80211_MLD_MAX_NUM_LINKS	15
++
+ #endif /* __IEEE80211 */
+diff --git a/interface.c b/interface.c
+index bb1a1d3..7a690fa 100644
+--- a/interface.c
++++ b/interface.c
+@@ -406,6 +406,7 @@ static int print_iface_handler(struct nl_msg *msg, void *arg)
+ 	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+ 	unsigned int *wiphy = arg;
+ 	const char *indent = "";
++	int32_t txp;
+ 
+ 	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ 		  genlmsg_attrlen(gnlh, 0), NULL);
+@@ -449,8 +450,7 @@ static int print_iface_handler(struct nl_msg *msg, void *arg)
+ 	}
+ 
+ 	if (tb_msg[NL80211_ATTR_WIPHY_TX_POWER_LEVEL]) {
+-		int32_t txp = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_TX_POWER_LEVEL]);
+-
++		txp = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_TX_POWER_LEVEL]);
+ 		printf("%s\ttxpower %d.%.2d dBm\n",
+ 		       indent, txp / 100, txp % 100);
+ 	}
+@@ -490,6 +490,11 @@ static int print_iface_handler(struct nl_msg *msg, void *arg)
+ 				printf("\n%s\t   ", indent);
+ 				print_channel(tb);
+ 			}
++			if (tb[NL80211_ATTR_WIPHY_TX_POWER_LEVEL]) {
++				txp = nla_get_u32(tb[NL80211_ATTR_WIPHY_TX_POWER_LEVEL]);
++				printf("\n%s\t   ", indent);
++				printf("txpower %d.%.2d dBm", txp / 100, txp % 100);
++			}
+ 			printf("\n");
+ 		}
+ 	}
+diff --git a/phy.c b/phy.c
+index 584b103..f9a5b0f 100644
+--- a/phy.c
++++ b/phy.c
+@@ -686,37 +686,51 @@ static int handle_txpower(struct nl80211_state *state,
+ 			  enum id_input id)
+ {
+ 	enum nl80211_tx_power_setting type;
+-	int mbm;
++	unsigned int link_id;
++	int mbm, pos = 0;
++	char *endptr;
+ 
+-	/* get the required args */
+-	if (argc != 1 && argc != 2)
++	/* check args number */
++	if (argc < 1 && argc > 4)
+ 		return 1;
+ 
+-	if (!strcmp(argv[0], "auto"))
++	if (!strcmp(argv[0], "-l")) {
++		link_id = strtol(argv[1], &endptr, 10);
++		if (*endptr)
++			return 1;
++
++		if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) {
++			printf("link id %d exceeds max number of links\n", link_id);
++			return 2;
++		}
++		NLA_PUT_U8(msg, NL80211_ATTR_MLO_LINK_ID, link_id);
++		pos += 2;
++	}
++
++	if (!strcmp(argv[pos], "auto"))
+ 		type = NL80211_TX_POWER_AUTOMATIC;
+-	else if (!strcmp(argv[0], "fixed"))
++	else if (!strcmp(argv[pos], "fixed"))
+ 		type = NL80211_TX_POWER_FIXED;
+-	else if (!strcmp(argv[0], "limit"))
++	else if (!strcmp(argv[pos], "limit"))
+ 		type = NL80211_TX_POWER_LIMITED;
+ 	else {
+-		printf("Invalid parameter: %s\n", argv[0]);
++		printf("Invalid parameter: %s\n", argv[pos]);
+ 		return 2;
+ 	}
+ 
+ 	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_SETTING, type);
+ 
+ 	if (type != NL80211_TX_POWER_AUTOMATIC) {
+-		char *endptr;
+-		if (argc != 2) {
++		if (argc < 2 + pos) {
+ 			printf("Missing TX power level argument.\n");
+ 			return 2;
+ 		}
+ 
+-		mbm = strtol(argv[1], &endptr, 10);
++		mbm = strtol(argv[pos + 1], &endptr, 10);
+ 		if (*endptr)
+ 			return 2;
+ 		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL, mbm);
+-	} else if (argc != 1)
++	} else if (argc != 1 + pos)
+ 		return 1;
+ 
+ 	return 0;
+@@ -727,7 +741,7 @@ static int handle_txpower(struct nl80211_state *state,
+ COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]",
+ 	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_txpower,
+ 	"Specify transmit power level and setting type.");
+-COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]",
++COMMAND(set, txpower, "[-l <link_id>] <auto|fixed|limit> [<tx power in mBm>]",
+ 	NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_txpower,
+ 	"Specify transmit power level and setting type.");
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/iw/patches-mlo/0010-iw-add-per-radio-antenna-config.patch b/recipes-wifi/iw/patches-mlo/0010-iw-add-per-radio-antenna-config.patch
deleted file mode 100644
index 8a009fe..0000000
--- a/recipes-wifi/iw/patches-mlo/0010-iw-add-per-radio-antenna-config.patch
+++ /dev/null
@@ -1,170 +0,0 @@
-From a050861779a1e01ad2079830336b67d926ed7af7 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Thu, 13 Jun 2024 16:48:00 +0800
-Subject: [PATCH 10/10] iw: add per-radio antenna config
-
-Add per-radio antenna config & info dump
-Currently, there is no radio index or supported band bitmap in wiphy data struct
-& NL80211 ATTRS.
-Therefore, we just use NL80211_BANDS_XX to specify the radio we desire
-to config.
-
-CR-Id: WCNCR00274293
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Change-Id: Ibf0b3efb99b7568ba2195358cbb5483f2d91794a
----
- info.c | 49 +++++++++++++++++++++++++++++++++++++++++--------
- phy.c  | 50 ++++++++++++++++++++++++++++++++++++++++----------
- 2 files changed, 81 insertions(+), 18 deletions(-)
-
-diff --git a/info.c b/info.c
-index c5e863f..ddfef67 100644
---- a/info.c
-+++ b/info.c
-@@ -335,6 +335,14 @@ static int print_phy_handler(struct nl_msg *msg, void *arg)
- 	static int last_band = -1;
- 	static bool band_had_freq = false;
- 	bool print_name = true;
-+	static const char * const bands[] = {
-+		[NL80211_BAND_2GHZ] = "2G",
-+		[NL80211_BAND_5GHZ] = "5G",
-+		[NL80211_BAND_60GHZ] = "60G",
-+		[NL80211_BAND_6GHZ] = "6G",
-+		[NL80211_BAND_S1GHZ] = "S1G",
-+		[NL80211_BAND_LC] = "LC",
-+	};
- 
- 	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
- 		  genlmsg_attrlen(gnlh, 0), NULL);
-@@ -540,16 +548,41 @@ next:
- 	}
- 
- 	if (tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX] &&
--	    tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX])
--		printf("\tAvailable Antennas: TX %#x RX %#x\n",
--		       nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX]),
--		       nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX]));
-+	    tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX]) {
-+		int i, tx_num, rx_num;
-+		__u32 *avail_ants_tx, *avail_ants_rx;
-+
-+		tx_num = nla_len(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX]) / sizeof(__u32);
-+		rx_num = nla_len(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX]) / sizeof(__u32);
-+		if (tx_num == rx_num && tx_num == NUM_NL80211_BANDS) {
-+			avail_ants_tx = nla_data(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX]);
-+			avail_ants_rx = nla_data(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX]);
-+			printf("\tAvailable Antennas:\n");
-+			for (i = 0; i < tx_num; i++)
-+				if (avail_ants_tx[i] && avail_ants_rx[i])
-+					printf("\t\t%s band: TX %#x RX %#x\n",
-+					       bands[i], avail_ants_tx[i],
-+					       avail_ants_rx[i]);
-+		}
-+	}
- 
- 	if (tb_msg[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
--	    tb_msg[NL80211_ATTR_WIPHY_ANTENNA_RX])
--		printf("\tConfigured Antennas: TX %#x RX %#x\n",
--		       nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_TX]),
--		       nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_RX]));
-+	    tb_msg[NL80211_ATTR_WIPHY_ANTENNA_RX]) {
-+		int i, tx_num, rx_num;
-+		__u32 *ants_tx, *ants_rx;
-+
-+		tx_num = nla_len(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_TX]) / sizeof(__u32);
-+		rx_num = nla_len(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_RX]) / sizeof(__u32);
-+		if (tx_num == rx_num && tx_num == NUM_NL80211_BANDS) {
-+			ants_tx = nla_data(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_TX]);
-+			ants_rx = nla_data(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_RX]);
-+			printf("\tConfigured Antennas:\n");
-+			for (i = 0; i < tx_num; i++)
-+				if (ants_tx[i] && ants_rx[i])
-+					printf("\t\t%s band: TX %#x RX %#x\n",
-+					       bands[i], ants_tx[i], ants_rx[i]);
-+		}
-+	}
- 
- 	if (tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES])
- 		print_iftype_list("\tSupported interface modes", "\t\t",
-diff --git a/phy.c b/phy.c
-index f9a5b0f..594fb25 100644
---- a/phy.c
-+++ b/phy.c
-@@ -752,34 +752,64 @@ static int handle_antenna(struct nl80211_state *state,
- {
- 	char *end;
- 	uint32_t tx_ant = 0, rx_ant = 0;
-+	uint32_t tx_ants[NUM_NL80211_BANDS], rx_ants[NUM_NL80211_BANDS];
-+	int i = 0, bands = 0xffffffff;
-+
-+	if (!strncmp(argv[0], "-b", 2)) {
-+		bands = 0;
-+		for (i = 1; i < argc; i++) {
-+			if (!strncasecmp("2ghz", argv[i], 4))
-+				bands |= BIT(NL80211_BAND_2GHZ);
-+			else if (!strncasecmp("5ghz", argv[i], 4))
-+				bands |= BIT(NL80211_BAND_5GHZ);
-+			else if (!strncasecmp("6ghz", argv[i], 4))
-+				bands |= BIT(NL80211_BAND_6GHZ);
-+			else
-+				break;
-+		}
-+
-+		if (i == 1) {
-+			printf("Missing configured bands argument.\n");
-+			return 2;
-+		}
-+	}
- 
--	if (argc == 1 && strcmp(argv[0], "all") == 0) {
-+	if (argc == 1 + i && strncmp(argv[i], "all", 3) == 0) {
- 		tx_ant = 0xffffffff;
- 		rx_ant = 0xffffffff;
--	} else if (argc == 1) {
--		tx_ant = rx_ant = strtoul(argv[0], &end, 0);
-+	} else if (argc == 1 + i) {
-+		tx_ant = rx_ant = strtoul(argv[i], &end, 0);
- 		if (*end)
- 			return 1;
--	}
--	else if (argc == 2) {
--		tx_ant = strtoul(argv[0], &end, 0);
-+	} else if (argc == 2 + i) {
-+		tx_ant = strtoul(argv[i], &end, 0);
- 		if (*end)
- 			return 1;
--		rx_ant = strtoul(argv[1], &end, 0);
-+		rx_ant = strtoul(argv[i + 1], &end, 0);
- 		if (*end)
- 			return 1;
- 	} else
- 		return 1;
- 
--	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant);
--	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant);
-+	memset(tx_ants, 0, sizeof(tx_ants));
-+	memset(rx_ants, 0, sizeof(rx_ants));
-+	for (i = 0; i < NUM_NL80211_BANDS; i++) {
-+		if (!(bands & BIT(i)))
-+			continue;
-+
-+		tx_ants[i] = tx_ant;
-+		rx_ants[i] = rx_ant;
-+	}
-+
-+	NLA_PUT(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, sizeof(tx_ants), tx_ants);
-+	NLA_PUT(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, sizeof(rx_ants), rx_ants);
- 
- 	return 0;
- 
-  nla_put_failure:
- 	return -ENOBUFS;
- }
--COMMAND(set, antenna, "<bitmap> | all | <tx bitmap> <rx bitmap>",
-+COMMAND(set, antenna, "[-b [2GHz] [5GHz] [6GHz]] <bitmap> | all | <tx bitmap> <rx bitmap>",
- 	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_antenna,
- 	"Set a bitmap of allowed antennas to use for TX and RX.\n"
- 	"The driver may reject antenna configurations it cannot support.");
--- 
-2.18.0
-
diff --git a/recipes-wifi/iw/patches-mlo/0011-mtk-wifi-iw-dump-links-information-in-station-dump.patch b/recipes-wifi/iw/patches-mlo/0011-mtk-wifi-iw-dump-links-information-in-station-dump.patch
new file mode 100644
index 0000000..aff9a72
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0011-mtk-wifi-iw-dump-links-information-in-station-dump.patch
@@ -0,0 +1,139 @@
+From 8857b2c2e18abe3b00ed6aa11d821ece68f6c468 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Tue, 11 Jun 2024 11:18:41 +0800
+Subject: [PATCH 11/13] mtk: wifi: iw: dump links information in station dump
+
+Parse and show the following link information
+1. link address
+2. Rssi
+3. Tx rate
+4. Rx rate
+5. dtim period
+4. beacon interval
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ station.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 100 insertions(+)
+
+diff --git a/station.c b/station.c
+index bf7c0f5..8e4f67d 100644
+--- a/station.c
++++ b/station.c
+@@ -329,6 +329,7 @@ static int print_sta_handler(struct nl_msg *msg, void *arg)
+ 		[NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
+ 		[NL80211_STA_INFO_BEACON_RX] = { .type = NLA_U64},
+ 		[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
++		[NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 },
+ 		[NL80211_STA_INFO_T_OFFSET] = { .type = NLA_U64 },
+ 		[NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
+ 		[NL80211_STA_INFO_RX_BITRATE] = { .type = NLA_NESTED },
+@@ -636,6 +637,105 @@ static int print_sta_handler(struct nl_msg *msg, void *arg)
+ 	}
+ 
+ 	printf("\n\tcurrent time:\t%llu ms\n", now_ms);
++
++	printf("\t*** MLD Information ***");
++	if (tb[NL80211_ATTR_MLO_LINK_ID])
++		printf("\n\tSetup link = %d", nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]));
++
++	if (tb[NL80211_ATTR_MLD_ADDR]) {
++		mac_addr_n2a(mac_addr, nla_data(tb[NL80211_ATTR_MLD_ADDR]));
++		printf("\n\tMLD Address: %s", mac_addr);
++	}
++
++	if (tb[NL80211_ATTR_MLO_LINKS]) {
++		struct nlattr *link;
++		char buf[100];
++		int rem;
++
++		nla_for_each_nested(link, tb[NL80211_ATTR_MLO_LINKS], rem) {
++			struct nlattr *tb_link[NL80211_ATTR_MAX + 1];
++
++			nla_parse_nested(tb_link, NL80211_ATTR_MAX, link, NULL);
++
++			if (tb_link[NL80211_ATTR_MLO_LINK_ID])
++				printf("\n\t***** Link ID: %2d *****",
++				       nla_get_u32(tb_link[NL80211_ATTR_MLO_LINK_ID]));
++			if (tb_link[NL80211_ATTR_MAC]) {
++				mac_addr_n2a(buf, nla_data(tb_link[NL80211_ATTR_MAC]));
++				printf("\n\tLink addr: %s", buf);
++			}
++			if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
++					     tb_link[NL80211_ATTR_STA_INFO],
++					     stats_policy)) {
++				fprintf(stderr, "failed to parse nested attributes!\n");
++				return NL_SKIP;
++			}
++
++			if (sinfo[NL80211_STA_INFO_RX_BYTES64])
++				printf("\n\trx bytes:\t%llu",
++				       nla_get_u64(sinfo[NL80211_STA_INFO_RX_BYTES64]));
++			if (sinfo[NL80211_STA_INFO_RX_MPDUS])
++				printf("\n\trx mpdus:\t%u",
++				       nla_get_u32(sinfo[NL80211_STA_INFO_RX_MPDUS]));
++			if (sinfo[NL80211_STA_INFO_FCS_ERROR_COUNT])
++				printf("\n\trx fcs errors:\t%u",
++				       nla_get_u32(sinfo[NL80211_STA_INFO_FCS_ERROR_COUNT]));
++
++			if (sinfo[NL80211_STA_INFO_TX_BYTES64])
++				printf("\n\ttx bytes:\t%llu",
++				       nla_get_u64(sinfo[NL80211_STA_INFO_TX_BYTES64]));
++			if (sinfo[NL80211_STA_INFO_TX_RETRIES])
++				printf("\n\ttx retries:\t%u",
++				       nla_get_u32(sinfo[NL80211_STA_INFO_TX_RETRIES]));
++			if (sinfo[NL80211_STA_INFO_TX_FAILED])
++				printf("\n\ttx failed:\t%u",
++				       nla_get_u32(sinfo[NL80211_STA_INFO_TX_FAILED]));
++
++			chain = get_chain_signal(sinfo[NL80211_STA_INFO_CHAIN_SIGNAL]);
++			if (sinfo[NL80211_STA_INFO_SIGNAL])
++				printf("\n\tsignal:  \t%d %sdBm",
++				       (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]),
++				       chain);
++
++			chain = get_chain_signal(sinfo[NL80211_STA_INFO_CHAIN_SIGNAL_AVG]);
++			if (sinfo[NL80211_STA_INFO_SIGNAL_AVG])
++				printf("\n\tsignal avg:\t%d %sdBm",
++				       (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]),
++				       chain);
++
++			if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
++				parse_bitrate(sinfo[NL80211_STA_INFO_TX_BITRATE],
++					      buf, sizeof(buf));
++				printf("\n\ttx bitrate:\t%s", buf);
++			}
++
++			if (sinfo[NL80211_STA_INFO_TX_DURATION])
++				printf("\n\ttx duration:\t%llu us",
++				       nla_get_u64(sinfo[NL80211_STA_INFO_TX_DURATION]));
++
++			if (sinfo[NL80211_STA_INFO_RX_BITRATE]) {
++				parse_bitrate(sinfo[NL80211_STA_INFO_RX_BITRATE],
++					      buf, sizeof(buf));
++				printf("\n\trx bitrate:\t%s", buf);
++			}
++
++			if (sinfo[NL80211_STA_INFO_RX_DURATION])
++				printf("\n\trx duration:\t%llu us",
++				       nla_get_u64(sinfo[NL80211_STA_INFO_RX_DURATION]));
++
++			if (sinfo[NL80211_STA_INFO_ACK_SIGNAL])
++				printf("\n\tlast ack signal:%d dBm",
++				       (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_ACK_SIGNAL]));
++
++			if (sinfo[NL80211_STA_INFO_ACK_SIGNAL_AVG])
++				printf("\n\tavg ack signal:\t%d dBm",
++				       (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_ACK_SIGNAL_AVG]));
++
++			if (sinfo[NL80211_STA_INFO_BSS_PARAM])
++				parse_bss_param(sinfo[NL80211_STA_INFO_BSS_PARAM]);
++		}
++	}
++	printf("\n");
+ 	return NL_SKIP;
+ }
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/iw/patches-mlo/0012-iw-add-per-radio-antenna-config.patch b/recipes-wifi/iw/patches-mlo/0012-iw-add-per-radio-antenna-config.patch
new file mode 100644
index 0000000..1d915e1
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0012-iw-add-per-radio-antenna-config.patch
@@ -0,0 +1,168 @@
+From ecfbe0e44f98de44dc7a1e3d8566cf9eb9987458 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 13 Jun 2024 16:48:00 +0800
+Subject: [PATCH 12/13] iw: add per-radio antenna config
+
+Add per-radio antenna config & info dump
+Currently, there is no radio index or supported band bitmap in wiphy data struct
+& NL80211 ATTRS.
+Therefore, we just use NL80211_BANDS_XX to specify the radio we desire
+to config.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ info.c | 49 +++++++++++++++++++++++++++++++++++++++++--------
+ phy.c  | 50 ++++++++++++++++++++++++++++++++++++++++----------
+ 2 files changed, 81 insertions(+), 18 deletions(-)
+
+diff --git a/info.c b/info.c
+index c5e863f..ddfef67 100644
+--- a/info.c
++++ b/info.c
+@@ -335,6 +335,14 @@ static int print_phy_handler(struct nl_msg *msg, void *arg)
+ 	static int last_band = -1;
+ 	static bool band_had_freq = false;
+ 	bool print_name = true;
++	static const char * const bands[] = {
++		[NL80211_BAND_2GHZ] = "2G",
++		[NL80211_BAND_5GHZ] = "5G",
++		[NL80211_BAND_60GHZ] = "60G",
++		[NL80211_BAND_6GHZ] = "6G",
++		[NL80211_BAND_S1GHZ] = "S1G",
++		[NL80211_BAND_LC] = "LC",
++	};
+ 
+ 	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ 		  genlmsg_attrlen(gnlh, 0), NULL);
+@@ -540,16 +548,41 @@ next:
+ 	}
+ 
+ 	if (tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX] &&
+-	    tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX])
+-		printf("\tAvailable Antennas: TX %#x RX %#x\n",
+-		       nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX]),
+-		       nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX]));
++	    tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX]) {
++		int i, tx_num, rx_num;
++		__u32 *avail_ants_tx, *avail_ants_rx;
++
++		tx_num = nla_len(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX]) / sizeof(__u32);
++		rx_num = nla_len(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX]) / sizeof(__u32);
++		if (tx_num == rx_num && tx_num == NUM_NL80211_BANDS) {
++			avail_ants_tx = nla_data(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX]);
++			avail_ants_rx = nla_data(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX]);
++			printf("\tAvailable Antennas:\n");
++			for (i = 0; i < tx_num; i++)
++				if (avail_ants_tx[i] && avail_ants_rx[i])
++					printf("\t\t%s band: TX %#x RX %#x\n",
++					       bands[i], avail_ants_tx[i],
++					       avail_ants_rx[i]);
++		}
++	}
+ 
+ 	if (tb_msg[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
+-	    tb_msg[NL80211_ATTR_WIPHY_ANTENNA_RX])
+-		printf("\tConfigured Antennas: TX %#x RX %#x\n",
+-		       nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_TX]),
+-		       nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_RX]));
++	    tb_msg[NL80211_ATTR_WIPHY_ANTENNA_RX]) {
++		int i, tx_num, rx_num;
++		__u32 *ants_tx, *ants_rx;
++
++		tx_num = nla_len(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_TX]) / sizeof(__u32);
++		rx_num = nla_len(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_RX]) / sizeof(__u32);
++		if (tx_num == rx_num && tx_num == NUM_NL80211_BANDS) {
++			ants_tx = nla_data(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_TX]);
++			ants_rx = nla_data(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_RX]);
++			printf("\tConfigured Antennas:\n");
++			for (i = 0; i < tx_num; i++)
++				if (ants_tx[i] && ants_rx[i])
++					printf("\t\t%s band: TX %#x RX %#x\n",
++					       bands[i], ants_tx[i], ants_rx[i]);
++		}
++	}
+ 
+ 	if (tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES])
+ 		print_iftype_list("\tSupported interface modes", "\t\t",
+diff --git a/phy.c b/phy.c
+index f9a5b0f..594fb25 100644
+--- a/phy.c
++++ b/phy.c
+@@ -752,34 +752,64 @@ static int handle_antenna(struct nl80211_state *state,
+ {
+ 	char *end;
+ 	uint32_t tx_ant = 0, rx_ant = 0;
++	uint32_t tx_ants[NUM_NL80211_BANDS], rx_ants[NUM_NL80211_BANDS];
++	int i = 0, bands = 0xffffffff;
++
++	if (!strncmp(argv[0], "-b", 2)) {
++		bands = 0;
++		for (i = 1; i < argc; i++) {
++			if (!strncasecmp("2ghz", argv[i], 4))
++				bands |= BIT(NL80211_BAND_2GHZ);
++			else if (!strncasecmp("5ghz", argv[i], 4))
++				bands |= BIT(NL80211_BAND_5GHZ);
++			else if (!strncasecmp("6ghz", argv[i], 4))
++				bands |= BIT(NL80211_BAND_6GHZ);
++			else
++				break;
++		}
++
++		if (i == 1) {
++			printf("Missing configured bands argument.\n");
++			return 2;
++		}
++	}
+ 
+-	if (argc == 1 && strcmp(argv[0], "all") == 0) {
++	if (argc == 1 + i && strncmp(argv[i], "all", 3) == 0) {
+ 		tx_ant = 0xffffffff;
+ 		rx_ant = 0xffffffff;
+-	} else if (argc == 1) {
+-		tx_ant = rx_ant = strtoul(argv[0], &end, 0);
++	} else if (argc == 1 + i) {
++		tx_ant = rx_ant = strtoul(argv[i], &end, 0);
+ 		if (*end)
+ 			return 1;
+-	}
+-	else if (argc == 2) {
+-		tx_ant = strtoul(argv[0], &end, 0);
++	} else if (argc == 2 + i) {
++		tx_ant = strtoul(argv[i], &end, 0);
+ 		if (*end)
+ 			return 1;
+-		rx_ant = strtoul(argv[1], &end, 0);
++		rx_ant = strtoul(argv[i + 1], &end, 0);
+ 		if (*end)
+ 			return 1;
+ 	} else
+ 		return 1;
+ 
+-	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant);
+-	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant);
++	memset(tx_ants, 0, sizeof(tx_ants));
++	memset(rx_ants, 0, sizeof(rx_ants));
++	for (i = 0; i < NUM_NL80211_BANDS; i++) {
++		if (!(bands & BIT(i)))
++			continue;
++
++		tx_ants[i] = tx_ant;
++		rx_ants[i] = rx_ant;
++	}
++
++	NLA_PUT(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, sizeof(tx_ants), tx_ants);
++	NLA_PUT(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, sizeof(rx_ants), rx_ants);
+ 
+ 	return 0;
+ 
+  nla_put_failure:
+ 	return -ENOBUFS;
+ }
+-COMMAND(set, antenna, "<bitmap> | all | <tx bitmap> <rx bitmap>",
++COMMAND(set, antenna, "[-b [2GHz] [5GHz] [6GHz]] <bitmap> | all | <tx bitmap> <rx bitmap>",
+ 	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_antenna,
+ 	"Set a bitmap of allowed antennas to use for TX and RX.\n"
+ 	"The driver may reject antenna configurations it cannot support.");
+-- 
+2.18.0
+
diff --git a/recipes-wifi/iw/patches-mlo/0013-iw-add-link_id-for-offchannel-operation.patch b/recipes-wifi/iw/patches-mlo/0013-iw-add-link_id-for-offchannel-operation.patch
new file mode 100644
index 0000000..c9f6cf9
--- /dev/null
+++ b/recipes-wifi/iw/patches-mlo/0013-iw-add-link_id-for-offchannel-operation.patch
@@ -0,0 +1,59 @@
+From 1bc783af16926b73f98f0c1b67ab90cf39e030a1 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 27 Jun 2024 08:37:52 +0800
+Subject: [PATCH 13/13] iw: add link_id for offchannel operation
+
+NL80211_CMD_REMAIN_ON_CANNEL has flag NL80211_FLAG_MLO_VALID_LINK_ID, so
+a link_id is necessary when the interface is MLD.
+
+Usage:
+iw dev <ifname> offchannel [-l link_id] <freq> <duration>
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ offch.c | 24 ++++++++++++++++++++++--
+ 1 file changed, 22 insertions(+), 2 deletions(-)
+
+diff --git a/offch.c b/offch.c
+index 19e170e..3858ccd 100644
+--- a/offch.c
++++ b/offch.c
+@@ -14,6 +14,26 @@ static int offchannel(struct nl80211_state *state,
+ 		      enum id_input id)
+ {
+ 	char *end;
++	unsigned int link_id;
++
++	if (argc < 2)
++		return 1;
++
++	/* link id */
++	if (!strcmp(argv[0], "-l")) {
++		link_id = strtol(argv[1], &end, 10);
++		if (*end)
++			return 1;
++
++		if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) {
++			printf("link id %d exceeds max number of links\n",
++				link_id);
++			return 2;
++		}
++		NLA_PUT_U8(msg, NL80211_ATTR_MLO_LINK_ID, link_id);
++		argv += 2;
++		argc -= 2;
++	}
+ 
+ 	if (argc < 2)
+ 		return 1;
+@@ -42,6 +62,6 @@ static int offchannel(struct nl80211_state *state,
+ 	return -ENOSPC;
+ }
+ 
+-TOPLEVEL(offchannel, "<freq> <duration>", NL80211_CMD_REMAIN_ON_CHANNEL, 0,
+-	 CIB_NETDEV, offchannel,
++TOPLEVEL(offchannel, "[-l link_id] <freq> <duration>",
++	 NL80211_CMD_REMAIN_ON_CHANNEL, 0, CIB_NETDEV, offchannel,
+ 	 "Leave operating channel and go to the given channel for a while.");
+-- 
+2.18.0
+
diff --git a/recipes-wifi/iw/patches-mlo/patches.inc b/recipes-wifi/iw/patches-mlo/patches.inc
index 5c18fcd..963d304 100644
--- a/recipes-wifi/iw/patches-mlo/patches.inc
+++ b/recipes-wifi/iw/patches-mlo/patches.inc
@@ -4,10 +4,13 @@
     file://0002-iw-add-puncturing-support.patch \
     file://0003-util-clarify-comment-about-parsed-pointer.patch \
     file://0004-iw-remove-sizer-section-and-related-code.patch \
-    file://0005-Revert-iw-allow-specifying-CFLAGS-LIBS-externally.patch \
-    file://0006-survey-bss-rx-time.patch \
-    file://0007-iw-support-link-id-in-set-bitrates-command.patch \
-    file://0008-iw-add-per-link-txpower-config.patch \
-    file://0009-mtk-wifi-iw-dump-links-information-in-station-dump.patch \
-    file://0010-iw-add-per-radio-antenna-config.patch \
+    file://0005-iw-fix-formats-under-MIPS64-PPC.patch \
+    file://0006-update-nl80211.h.patch \
+    file://0007-Revert-iw-allow-specifying-CFLAGS-LIBS-externally.patch \
+    file://0008-survey-bss-rx-time.patch \
+    file://0009-iw-support-link-id-in-set-bitrates-command.patch \
+    file://0010-iw-add-per-link-txpower-config.patch \
+    file://0011-mtk-wifi-iw-dump-links-information-in-station-dump.patch \
+    file://0012-iw-add-per-radio-antenna-config.patch \
+    file://0013-iw-add-link_id-for-offchannel-operation.patch \
     "
diff --git a/recipes-wifi/linux-mac80211/files/backports-2024-04-03.tar.xz b/recipes-wifi/linux-mac80211/files/backports-2024-04-03.tar.xz
deleted file mode 100644
index a6715ac..0000000
--- a/recipes-wifi/linux-mac80211/files/backports-2024-04-03.tar.xz
+++ /dev/null
Binary files differ
diff --git a/recipes-wifi/linux-mac80211/files/backports-2024-07-11.tar.xz b/recipes-wifi/linux-mac80211/files/backports-2024-07-11.tar.xz
new file mode 100644
index 0000000..fe0fdf5
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/backports-2024-07-11.tar.xz
Binary files differ
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0001-backports-sync-openwrt-patches-build.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0001-backports-sync-openwrt-patches-build.patch
new file mode 100644
index 0000000..17147cd
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0001-backports-sync-openwrt-patches-build.patch
@@ -0,0 +1,947 @@
+From a0f11f79e303ee8fc152c388bf58ed192e735102 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 17 Jul 2024 20:40:32 +0800
+Subject: [PATCH 01/89] backports: sync openwrt patches/build
+
+---
+ Kconfig.local                                 | 111 -----------------
+ Kconfig.sources                               |   5 -
+ Makefile                                      | 115 ++++++++++--------
+ Makefile.kernel                               |   4 -
+ Makefile.real                                 |  29 ++++-
+ .../linux/bcma/bcma_driver_chipcommon.h       |  10 ++
+ backport-include/linux/iommu.h                |  23 ++++
+ backport-include/linux/list.h                 |   2 +-
+ backport-include/linux/random.h               |   2 +-
+ backport-include/net/dropreason.h             |   3 +-
+ compat/main.c                                 |  25 ----
+ drivers/net/wireless/ath/ath11k/Kconfig       |   6 +-
+ drivers/net/wireless/broadcom/b43/Kconfig     |  12 +-
+ drivers/net/wireless/broadcom/b43/main.c      |   4 +-
+ .../net/wireless/broadcom/b43legacy/Kconfig   |   8 +-
+ .../net/wireless/broadcom/b43legacy/main.c    |   4 +-
+ .../net/wireless/broadcom/brcm80211/Kconfig   |   2 +-
+ .../broadcom/brcm80211/brcmfmac/usb.c         |   4 +
+ drivers/net/wireless/intel/iwlwifi/mvm/tt.c   |  10 ++
+ drivers/staging/rtl8723bs/Kconfig             |   1 -
+ kconf/Makefile                                |   4 +-
+ kconf/conf.c                                  |  30 +----
+ kconf/confdata.c                              |   4 +-
+ local-symbols                                 |  45 -------
+ net/wireless/nl80211.c                        |  10 ++
+ net/wireless/sysfs.c                          |   4 +
+ 26 files changed, 171 insertions(+), 306 deletions(-)
+ create mode 100644 backport-include/linux/bcma/bcma_driver_chipcommon.h
+ create mode 100644 backport-include/linux/iommu.h
+
+diff --git a/Kconfig.local b/Kconfig.local
+index 91158d6..f142374 100644
+--- a/Kconfig.local
++++ b/Kconfig.local
+@@ -1414,117 +1414,6 @@ config BACKPORTED_USB_NET_AQC111
+ config BACKPORTED_USB_RTL8153_ECM
+ 	tristate
+ 	default USB_RTL8153_ECM
+-config BACKPORTED_SSB_POSSIBLE
+-	tristate
+-	default SSB_POSSIBLE
+-config BACKPORTED_SSB
+-	tristate
+-	default SSB
+-config BACKPORTED_SSB_SPROM
+-	tristate
+-	default SSB_SPROM
+-config BACKPORTED_SSB_BLOCKIO
+-	tristate
+-	default SSB_BLOCKIO
+-config BACKPORTED_SSB_PCIHOST_POSSIBLE
+-	tristate
+-	default SSB_PCIHOST_POSSIBLE
+-config BACKPORTED_SSB_PCIHOST
+-	tristate
+-	default SSB_PCIHOST
+-config BACKPORTED_SSB_B43_PCI_BRIDGE
+-	tristate
+-	default SSB_B43_PCI_BRIDGE
+-config BACKPORTED_SSB_PCMCIAHOST_POSSIBLE
+-	tristate
+-	default SSB_PCMCIAHOST_POSSIBLE
+-config BACKPORTED_SSB_PCMCIAHOST
+-	tristate
+-	default SSB_PCMCIAHOST
+-config BACKPORTED_SSB_SDIOHOST_POSSIBLE
+-	tristate
+-	default SSB_SDIOHOST_POSSIBLE
+-config BACKPORTED_SSB_SDIOHOST
+-	tristate
+-	default SSB_SDIOHOST
+-config BACKPORTED_SSB_HOST_SOC
+-	tristate
+-	default SSB_HOST_SOC
+-config BACKPORTED_SSB_SERIAL
+-	tristate
+-	default SSB_SERIAL
+-config BACKPORTED_SSB_DRIVER_PCICORE_POSSIBLE
+-	tristate
+-	default SSB_DRIVER_PCICORE_POSSIBLE
+-config BACKPORTED_SSB_DRIVER_PCICORE
+-	tristate
+-	default SSB_DRIVER_PCICORE
+-config BACKPORTED_SSB_PCICORE_HOSTMODE
+-	tristate
+-	default SSB_PCICORE_HOSTMODE
+-config BACKPORTED_SSB_DRIVER_MIPS
+-	tristate
+-	default SSB_DRIVER_MIPS
+-config BACKPORTED_SSB_SFLASH
+-	tristate
+-	default SSB_SFLASH
+-config BACKPORTED_SSB_EMBEDDED
+-	tristate
+-	default SSB_EMBEDDED
+-config BACKPORTED_SSB_DRIVER_EXTIF
+-	tristate
+-	default SSB_DRIVER_EXTIF
+-config BACKPORTED_SSB_DRIVER_GIGE
+-	tristate
+-	default SSB_DRIVER_GIGE
+-config BACKPORTED_SSB_DRIVER_GPIO
+-	tristate
+-	default SSB_DRIVER_GPIO
+-config BACKPORTED_BCMA_POSSIBLE
+-	tristate
+-	default BCMA_POSSIBLE
+-config BACKPORTED_BCMA
+-	tristate
+-	default BCMA
+-config BACKPORTED_BCMA_BLOCKIO
+-	tristate
+-	default BCMA_BLOCKIO
+-config BACKPORTED_BCMA_HOST_PCI_POSSIBLE
+-	tristate
+-	default BCMA_HOST_PCI_POSSIBLE
+-config BACKPORTED_BCMA_HOST_PCI
+-	tristate
+-	default BCMA_HOST_PCI
+-config BACKPORTED_BCMA_HOST_SOC
+-	tristate
+-	default BCMA_HOST_SOC
+-config BACKPORTED_BCMA_DRIVER_PCI
+-	tristate
+-	default BCMA_DRIVER_PCI
+-config BACKPORTED_BCMA_DRIVER_PCI_HOSTMODE
+-	tristate
+-	default BCMA_DRIVER_PCI_HOSTMODE
+-config BACKPORTED_BCMA_DRIVER_MIPS
+-	tristate
+-	default BCMA_DRIVER_MIPS
+-config BACKPORTED_BCMA_PFLASH
+-	tristate
+-	default BCMA_PFLASH
+-config BACKPORTED_BCMA_SFLASH
+-	tristate
+-	default BCMA_SFLASH
+-config BACKPORTED_BCMA_NFLASH
+-	tristate
+-	default BCMA_NFLASH
+-config BACKPORTED_BCMA_DRIVER_GMAC_CMN
+-	tristate
+-	default BCMA_DRIVER_GMAC_CMN
+-config BACKPORTED_BCMA_DRIVER_GPIO
+-	tristate
+-	default BCMA_DRIVER_GPIO
+-config BACKPORTED_BCMA_DEBUG
+-	tristate
+-	default BCMA_DEBUG
+ config BACKPORTED_USB_ACM
+ 	tristate
+ 	default USB_ACM
+diff --git a/Kconfig.sources b/Kconfig.sources
+index 2ea4d8a..d74affd 100644
+--- a/Kconfig.sources
++++ b/Kconfig.sources
+@@ -4,15 +4,10 @@ source "$BACKPORT_DIR/compat/Kconfig"
+ # these are copied from the kernel
+ source "$BACKPORT_DIR/net/wireless/Kconfig"
+ source "$BACKPORT_DIR/net/mac80211/Kconfig"
+-source "$BACKPORT_DIR/net/qrtr/Kconfig"
+-source "$BACKPORT_DIR/drivers/bus/mhi/Kconfig"
+ source "$BACKPORT_DIR/drivers/soc/qcom/Kconfig"
+ source "$BACKPORT_DIR/drivers/net/wireless/Kconfig"
+ source "$BACKPORT_DIR/drivers/net/usb/Kconfig"
+ 
+-source "$BACKPORT_DIR/drivers/ssb/Kconfig"
+-source "$BACKPORT_DIR/drivers/bcma/Kconfig"
+-
+ source "$BACKPORT_DIR/drivers/usb/class/Kconfig"
+ 
+ source "$BACKPORT_DIR/drivers/staging/Kconfig"
+diff --git a/Makefile b/Makefile
+index 548d813..989faff 100644
+--- a/Makefile
++++ b/Makefile
+@@ -2,10 +2,10 @@
+ # Makefile for the output source package
+ #
+ 
+-ifeq ($(KERNELRELEASE),)
++ifeq ($(KERNELVERSION),)
+ 
+ MAKEFLAGS += --no-print-directory
+-SHELL := /bin/bash
++SHELL := /usr/bin/env bash
+ BACKPORT_DIR := $(shell pwd)
+ 
+ KMODDIR ?= updates
+@@ -19,6 +19,7 @@ KLIB_BUILD ?= $(KLIB)/build/
+ KERNEL_CONFIG := $(KLIB_BUILD)/.config
+ KERNEL_MAKEFILE := $(KLIB_BUILD)/Makefile
+ CONFIG_MD5 := $(shell md5sum $(KERNEL_CONFIG) 2>/dev/null | sed 's/\s.*//')
++STAMP_KERNEL_CONFIG := .kernel_config_md5_$(CONFIG_MD5)
+ 
+ export KLIB KLIB_BUILD BACKPORT_DIR KMODDIR KMODPATH_ARG
+ 
+@@ -36,7 +37,8 @@ mrproper:
+ 	@rm -f .kernel_config_md5 Kconfig.versions Kconfig.kernel
+ 	@rm -f backport-include/backport/autoconf.h
+ 
+-.DEFAULT:
++.SILENT: $(STAMP_KERNEL_CONFIG)
++$(STAMP_KERNEL_CONFIG):
+ 	@set -e ; test -f local-symbols || (						\
+ 	echo "/--------------"								;\
+ 	echo "| You shouldn't run make in the backports tree, but only in"		;\
+@@ -60,57 +62,62 @@ mrproper:
+ 	echo "| (that isn't currently running.)"					;\
+ 	echo "\\--"									;\
+ 	false)
+-	@set -e ; if [ "$$(cat .kernel_config_md5 2>/dev/null)" != "$(CONFIG_MD5)" ]	;\
+-	then 										\
+-		echo -n "Generating local configuration database from kernel ..."	;\
+-		grep -v -f local-symbols $(KERNEL_CONFIG) | grep = | (			\
+-			while read l ; do						\
+-				if [ "$${l:0:7}" != "CONFIG_" ] ; then			\
+-					continue					;\
+-				fi							;\
+-				l=$${l:7}						;\
+-				n=$${l%%=*}						;\
+-				v=$${l#*=}						;\
+-				if [ "$$v" = "m" ] ; then				\
+-					echo config $$n					;\
+-					echo '    tristate' 				;\
+-				elif [ "$$v" = "y" ] ; then				\
+-					echo config $$n					;\
+-					echo '    bool'					;\
+-				else							\
+-					continue					;\
+-				fi							;\
+-				echo "    default $$v"					;\
+-				echo ""							;\
+-			done								\
+-		) > Kconfig.kernel							;\
+-		kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) M=$(BACKPORT_DIR)	\
+-			kernelversion |	sed 's/^\(\([3-6]\|2\.6\)\.[0-9]\+\).*/\1/;t;d');\
+-		test "$$kver" != "" || echo "Kernel version parse failed!"		;\
+-		test "$$kver" != ""							;\
+-		kvers="$$kvers $$(seq 0 20 | sed 's/^/4./')"				;\
+-		kvers="$$kvers $$(seq 0 19 | sed 's/^/5./')"				;\
+-		kvers="$$kvers $$(seq 0 20 | sed 's/^/6./')"				;\
+-		print=0									;\
+-		for v in $$kvers ; do							\
+-			if [ "$$print" = "1" ] ; then					\
+-				echo config KERNEL_$$(echo $$v | tr . _)	;\
+-				echo "    def_bool y"					;\
+-			fi								;\
+-			if [ "$$v" = "$$kver" ] ; then print=1 ; fi			;\
+-		done > Kconfig.versions							;\
+-		# RHEL as well, sadly we need to grep for it				;\
+-		RHEL_MAJOR=$$(grep '^RHEL_MAJOR' $(KERNEL_MAKEFILE) | 			\
+-					sed 's/.*=\s*\([0-9]*\)/\1/;t;d')		;\
+-		RHEL_MINOR=$$(grep '^RHEL_MINOR' $(KERNEL_MAKEFILE) | 			\
+-					sed 's/.*=\s*\([0-9]*\)/\1/;t;d')		;\
+-		for v in $$(seq 0 $$RHEL_MINOR) ; do 					\
+-			echo config BACKPORT_RHEL_KERNEL_$${RHEL_MAJOR}_$$v		;\
+-			echo "    def_bool y"						;\
+-		done >> Kconfig.versions						;\
+-		echo " done."								;\
+-	fi										;\
+-	echo "$(CONFIG_MD5)" > .kernel_config_md5
++	@rm -f .kernel_config_md5_*
++	@touch $@
++
++Kconfig.kernel: $(STAMP_KERNEL_CONFIG) local-symbols
++	@printf "Generating local configuration database from kernel ..."
++	@grep -v -f local-symbols $(KERNEL_CONFIG) | grep = | (			\
++		while read l ; do						\
++			if [ "$${l:0:7}" != "CONFIG_" ] ; then			\
++				continue					;\
++			fi							;\
++			l=$${l:7}						;\
++			n=$${l%%=*}						;\
++			v=$${l#*=}						;\
++			if [ "$$v" = "m" ] ; then				\
++				echo config $$n					;\
++				echo '    tristate' 				;\
++			elif [ "$$v" = "y" ] ; then				\
++				echo config $$n					;\
++				echo '    bool'					;\
++			else							\
++				continue					;\
++			fi							;\
++			echo "    default $$v"					;\
++			echo ""							;\
++		done								\
++	) > $@
++	@echo " done."
++
++Kconfig.versions: Kconfig.kernel
++	@kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) M=$(BACKPORT_DIR)	\
++		kernelversion |	sed 's/^\(\([3-6]\|2\.6\)\.[0-9]\+\).*/\1/;t;d');\
++	test "$$kver" != "" || echo "Kernel version parse failed!"		;\
++	test "$$kver" != ""							;\
++	kvers="$$kvers $$(seq 0 20 | sed 's/^/4./')"				;\
++	kvers="$$kvers $$(seq 0 19 | sed 's/^/5./')"				;\
++	kvers="$$kvers $$(seq 0 20 | sed 's/^/6./')"				;\
++	print=0									;\
++	for v in $$kvers ; do							\
++		if [ "$$print" = "1" ] ; then					\
++			echo config KERNEL_$$(echo $$v | tr . _)	;\
++			echo "    def_bool y"					;\
++		fi								;\
++		if [ "$$v" = "$$kver" ] ; then print=1 ; fi			;\
++	done > Kconfig.versions							;\
++	# RHEL as well, sadly we need to grep for it				;\
++	RHEL_MAJOR=$$(grep '^RHEL_MAJOR' $(KERNEL_MAKEFILE) | 			\
++				sed 's/.*=\s*\([0-9]*\)/\1/;t;d')		;\
++	RHEL_MINOR=$$(grep '^RHEL_MINOR' $(KERNEL_MAKEFILE) | 			\
++				sed 's/.*=\s*\([0-9]*\)/\1/;t;d')		;\
++	for v in $$(seq 0 $$RHEL_MINOR) ; do 					\
++		echo config BACKPORT_RHEL_KERNEL_$${RHEL_MAJOR}_$$v		;\
++		echo "    def_bool y"						;\
++	done >> $@
++
++.DEFAULT:
++	@$(MAKE) Kconfig.versions
+ 	@$(MAKE) -f Makefile.real "$@"
+ 
+ .PHONY: defconfig-help
+diff --git a/Makefile.kernel b/Makefile.kernel
+index 9d9c341..c72a1ab 100644
+--- a/Makefile.kernel
++++ b/Makefile.kernel
+@@ -38,12 +38,8 @@ obj-y += compat/
+ 
+ obj-$(CPTCFG_CFG80211) += net/wireless/
+ obj-$(CPTCFG_MAC80211) += net/mac80211/
+-obj-$(CPTCFG_QRTR) += net/qrtr/
+ obj-$(CPTCFG_QCOM_QMI_HELPERS) += drivers/soc/qcom/
+-obj-$(CPTCFG_MHI_BUS) += drivers/bus/mhi/
+ #obj-$(CPTCFG_WLAN) += drivers/net/wireless/
+-obj-$(CPTCFG_SSB) += drivers/ssb/
+-obj-$(CPTCFG_BCMA) += drivers/bcma/
+ obj-$(CPTCFG_USB_NET_RNDIS_WLAN) += drivers/net/usb/
+ 
+ obj-$(CPTCFG_USB_WDM) += drivers/usb/class/
+diff --git a/Makefile.real b/Makefile.real
+index 6550802..971a543 100644
+--- a/Makefile.real
++++ b/Makefile.real
+@@ -6,6 +6,18 @@ else
+ export BACKPORTS_GIT_TRACKER_DEF=
+ endif
+ 
++ifneq ($(LLVM),)
++ifneq ($(filter %/,$(LLVM)),)
++LLVM_PREFIX := $(LLVM)
++else ifneq ($(filter -%,$(LLVM)),)
++LLVM_SUFFIX := $(LLVM)
++endif
++
++HOSTCC	= $(LLVM_PREFIX)clang$(LLVM_SUFFIX)
++else
++HOSTCC	= gcc
++endif
++
+ # disable built-in rules for this file
+ .SUFFIXES:
+ 
+@@ -24,21 +36,21 @@ listnewconfig oldaskconfig oldconfig \
+ silentoldconfig olddefconfig oldnoconfig \
+ allnoconfig allyesconfig allmodconfig \
+ alldefconfig randconfig:
+-	@$(MAKE) -C kconf conf
++	@$(MAKE) -C kconf CC=$(HOSTCC) conf
+ 	@./kconf/conf --$@ Kconfig
+ 
+ .PHONY: usedefconfig
+ usedefconfig:
+-	@$(MAKE) -C kconf conf
++	@$(MAKE) -C kconf CC=$(HOSTCC) conf
+ 	@./kconf/conf --defconfig=defconfig Kconfig
+ 
+ .PHONY: savedefconfig
+ savedefconfig:
+-	@$(MAKE) -C kconf conf
++	@$(MAKE) -C kconf CC=$(HOSTCC) conf
+ 	@./kconf/conf --savedefconfig=defconfig Kconfig
+ 
+ defconfig-%::
+-	@$(MAKE) -C kconf conf
++	@$(MAKE) -C kconf CC=$(HOSTCC) conf
+ 	@./kconf/conf --defconfig=defconfigs/$(@:defconfig-%=%) Kconfig
+ 
+ .config:
+@@ -59,7 +71,7 @@ defconfig-%::
+ 
+ backport-include/backport/autoconf.h: .config Kconfig.versions Kconfig.kernel
+ 	@$(MAKE) oldconfig
+-	@echo -n "Building backport-include/backport/autoconf.h ..."
++	@printf "Building backport-include/backport/autoconf.h ..."
+ 	@grep -f local-symbols .config | (				\
+ 		echo "#ifndef COMPAT_AUTOCONF_INCLUDED"			;\
+ 		echo "#define COMPAT_AUTOCONF_INCLUDED"			;\
+@@ -80,7 +92,12 @@ backport-include/backport/autoconf.h: .config Kconfig.versions Kconfig.kernel
+ 			esac						;\
+ 		done							;\
+ 		echo "#endif /* COMPAT_AUTOCONF_INCLUDED */"		;\
+-	) > backport-include/backport/autoconf.h
++	) > $@.new
++	@if cmp -s $@ $@.new; then \
++		rm -f $@.new; \
++	else \
++		mv $@.new $@; \
++	fi
+ 	@echo " done."
+ 
+ .PHONY: modules
+diff --git a/backport-include/linux/bcma/bcma_driver_chipcommon.h b/backport-include/linux/bcma/bcma_driver_chipcommon.h
+new file mode 100644
+index 0000000..42e028b
+--- /dev/null
++++ b/backport-include/linux/bcma/bcma_driver_chipcommon.h
+@@ -0,0 +1,10 @@
++#ifndef __BACKPORT_BCMA_DRIVER_CHIPCOMMON_H
++#define __BACKPORT_BCMA_DRIVER_CHIPCOMMON_H
++
++#include_next <linux/bcma/bcma_driver_chipcommon.h>
++
++#ifndef BCMA_CC_SROM_CONTROL_OTP_PRESENT
++#define BCMA_CC_SROM_CONTROL_OTP_PRESENT 0x00000020
++#endif
++
++#endif
+diff --git a/backport-include/linux/iommu.h b/backport-include/linux/iommu.h
+new file mode 100644
+index 0000000..1c1e877
+--- /dev/null
++++ b/backport-include/linux/iommu.h
+@@ -0,0 +1,23 @@
++#ifndef __BACKPORT_LINUX_IOMMU_H
++#define __BACKPORT_LINUX_IOMMU_H
++
++#include_next <linux/iommu.h>
++#include <linux/version.h>
++
++#if LINUX_VERSION_IS_LESS(6,3,0)
++
++static inline int LINUX_BACKPORT(iommu_map)(struct iommu_domain *domain,
++					    unsigned long iova,
++					    phys_addr_t paddr, size_t size,
++					    int prot, gfp_t gfp)
++{
++	if (gfp == GFP_ATOMIC)
++		return iommu_map_atomic(domain, iova, paddr, size, prot);
++
++	return iommu_map(domain, iova, paddr, size, prot);
++}
++#define iommu_map LINUX_BACKPORT(iommu_map)
++
++#endif /* < 6.3 */
++
++#endif
+diff --git a/backport-include/linux/list.h b/backport-include/linux/list.h
+index 78367e9..a948c22 100644
+--- a/backport-include/linux/list.h
++++ b/backport-include/linux/list.h
+@@ -3,7 +3,7 @@
+ #include_next <linux/list.h>
+ #include <linux/version.h>
+ 
+-#if LINUX_VERSION_IS_LESS(6,3,0)
++#if 0 /* OpenWrt backports list_count_nodes() on its own */
+ /**
+  * list_count_nodes - count nodes in the list
+  * @head:	the head for your list.
+diff --git a/backport-include/linux/random.h b/backport-include/linux/random.h
+index 51bb17d..ca206c4 100644
+--- a/backport-include/linux/random.h
++++ b/backport-include/linux/random.h
+@@ -15,7 +15,7 @@ static inline u16 get_random_u16(void)
+ }
+ #endif
+ 
+-#if LINUX_VERSION_IS_LESS(6,2,0)
++#if LINUX_VERSION_IS_LESS(6,1,4)
+ static inline u32 __get_random_u32_below(u32 ceil)
+ {
+ 	/*
+diff --git a/backport-include/net/dropreason.h b/backport-include/net/dropreason.h
+index ec74e71..ab6a632 100644
+--- a/backport-include/net/dropreason.h
++++ b/backport-include/net/dropreason.h
+@@ -3,10 +3,9 @@
+ 
+ #include <linux/version.h>
+ 
++#include <net/dropreason-core.h>
+ #if LINUX_VERSION_IS_GEQ(6,0,0)
+ #include_next <net/dropreason.h>
+-#else
+-#include <net/dropreason-core.h>
+ #endif
+ 
+ #if LINUX_VERSION_IS_LESS(6,4,0)
+diff --git a/compat/main.c b/compat/main.c
+index d4f3340..651ab63 100644
+--- a/compat/main.c
++++ b/compat/main.c
+@@ -19,31 +19,6 @@ MODULE_LICENSE("GPL");
+ #error "You need a CPTCFG_VERSION"
+ #endif
+ 
+-static char *backported_kernel_name = CPTCFG_KERNEL_NAME;
+-
+-module_param(backported_kernel_name, charp, 0400);
+-MODULE_PARM_DESC(backported_kernel_name,
+-		 "The kernel tree name that was used for this backport (" CPTCFG_KERNEL_NAME ")");
+-
+-#ifdef BACKPORTS_GIT_TRACKED
+-static char *backports_tracker_id = BACKPORTS_GIT_TRACKED;
+-module_param(backports_tracker_id, charp, 0400);
+-MODULE_PARM_DESC(backports_tracker_id,
+-		 "The version of the tree containing this backport (" BACKPORTS_GIT_TRACKED ")");
+-#else
+-static char *backported_kernel_version = CPTCFG_KERNEL_VERSION;
+-static char *backports_version = CPTCFG_VERSION;
+-
+-module_param(backported_kernel_version, charp, 0400);
+-MODULE_PARM_DESC(backported_kernel_version,
+-		 "The kernel version that was used for this backport (" CPTCFG_KERNEL_VERSION ")");
+-
+-module_param(backports_version, charp, 0400);
+-MODULE_PARM_DESC(backports_version,
+-		 "The git version of the backports tree used to generate this backport (" CPTCFG_VERSION ")");
+-
+-#endif
+-
+ void backport_dependency_symbol(void)
+ {
+ }
+diff --git a/drivers/net/wireless/ath/ath11k/Kconfig b/drivers/net/wireless/ath/ath11k/Kconfig
+index 7430aaf..f5f58a5 100644
+--- a/drivers/net/wireless/ath/ath11k/Kconfig
++++ b/drivers/net/wireless/ath/ath11k/Kconfig
+@@ -25,9 +25,9 @@ config ATH11K_PCI
+ 	tristate "Atheros ath11k PCI support"
+ 	depends on m
+ 	depends on ATH11K && PCI
+-	select MHI_BUS
+-	select QRTR
+-	select QRTR_MHI
++	depends on MHI_BUS
++	depends on QRTR
++	depends on QRTR_MHI
+ 	help
+ 	  This module adds support for PCIE bus
+ 
+diff --git a/drivers/net/wireless/broadcom/b43/Kconfig b/drivers/net/wireless/broadcom/b43/Kconfig
+index 2e196b5..84cbe38 100644
+--- a/drivers/net/wireless/broadcom/b43/Kconfig
++++ b/drivers/net/wireless/broadcom/b43/Kconfig
+@@ -63,21 +63,21 @@ endchoice
+ config B43_PCI_AUTOSELECT
+ 	bool
+ 	depends on B43 && SSB_PCIHOST_POSSIBLE
+-	select SSB_PCIHOST
+-	select SSB_B43_PCI_BRIDGE
++	depends on SSB_PCIHOST
++	depends on SSB_B43_PCI_BRIDGE
+ 	default y
+ 
+ # Auto-select SSB PCICORE driver, if possible
+ config B43_PCICORE_AUTOSELECT
+ 	bool
+ 	depends on B43 && SSB_DRIVER_PCICORE_POSSIBLE
+-	select SSB_DRIVER_PCICORE
++	depends on SSB_DRIVER_PCICORE
+ 	default y
+ 
+ config B43_SDIO
+ 	bool "Broadcom 43xx SDIO device support"
+ 	depends on B43 && B43_SSB && SSB_SDIOHOST_POSSIBLE
+-	select SSB_SDIOHOST
++	depends on SSB_SDIOHOST
+ 	help
+ 	  Broadcom 43xx device support for Soft-MAC SDIO devices.
+ 
+@@ -96,13 +96,13 @@ config B43_SDIO
+ config B43_BCMA_PIO
+ 	bool
+ 	depends on B43 && B43_BCMA
+-	select BCMA_BLOCKIO
++	depends on BCMA_BLOCKIO
+ 	default y
+ 
+ config B43_PIO
+ 	bool
+ 	depends on B43 && B43_SSB
+-	select SSB_BLOCKIO
++	depends on SSB_BLOCKIO
+ 	default y
+ 
+ config B43_PHY_G
+diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c
+index a9e16ad..b6dbcdb 100644
+--- a/drivers/net/wireless/broadcom/b43/main.c
++++ b/drivers/net/wireless/broadcom/b43/main.c
+@@ -2854,7 +2854,7 @@ static struct ssb_device *b43_ssb_gpio_dev(struct b43_wldev *dev)
+ {
+ 	struct ssb_bus *bus = dev->dev->sdev->bus;
+ 
+-#ifdef CPTCFG_SSB_DRIVER_PCICORE
++#ifdef CONFIG_SSB_DRIVER_PCICORE
+ 	return (bus->chipco.dev ? bus->chipco.dev : bus->pcicore.dev);
+ #else
+ 	return bus->chipco.dev;
+@@ -4873,7 +4873,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
+ 	}
+ 	if (sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW)
+ 		hf |= B43_HF_DSCRQ; /* Disable slowclock requests from ucode. */
+-#if defined(CPTCFG_B43_SSB) && defined(CPTCFG_SSB_DRIVER_PCICORE)
++#if defined(CPTCFG_B43_SSB) && defined(CONFIG_SSB_DRIVER_PCICORE)
+ 	if (dev->dev->bus_type == B43_BUS_SSB &&
+ 	    dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI &&
+ 	    dev->dev->sdev->bus->pcicore.dev->id.revision <= 10)
+diff --git a/drivers/net/wireless/broadcom/b43legacy/Kconfig b/drivers/net/wireless/broadcom/b43legacy/Kconfig
+index 6ba7eb7..b924f63 100644
+--- a/drivers/net/wireless/broadcom/b43legacy/Kconfig
++++ b/drivers/net/wireless/broadcom/b43legacy/Kconfig
+@@ -3,7 +3,7 @@ config B43LEGACY
+ 	tristate "Broadcom 43xx-legacy wireless support (mac80211 stack)"
+ 	depends on m
+ 	depends on SSB_POSSIBLE && MAC80211 && HAS_DMA
+-	select SSB
++	depends on SSB
+ 	depends on FW_LOADER
+ 	help
+ 	  b43legacy is a driver for 802.11b devices from Broadcom (BCM4301 and
+@@ -25,15 +25,15 @@ config B43LEGACY
+ config B43LEGACY_PCI_AUTOSELECT
+ 	bool
+ 	depends on B43LEGACY && SSB_PCIHOST_POSSIBLE
+-	select SSB_PCIHOST
+-	select SSB_B43_PCI_BRIDGE
++	depends on SSB_PCIHOST
++	depends on SSB_B43_PCI_BRIDGE
+ 	default y
+ 
+ # Auto-select SSB PCICORE driver, if possible
+ config B43LEGACY_PCICORE_AUTOSELECT
+ 	bool
+ 	depends on B43LEGACY && SSB_DRIVER_PCICORE_POSSIBLE
+-	select SSB_DRIVER_PCICORE
++	depends on SSB_DRIVER_PCICORE
+ 	default y
+ 
+ # LED support
+diff --git a/drivers/net/wireless/broadcom/b43legacy/main.c b/drivers/net/wireless/broadcom/b43legacy/main.c
+index 1154b4a..f2873a1 100644
+--- a/drivers/net/wireless/broadcom/b43legacy/main.c
++++ b/drivers/net/wireless/broadcom/b43legacy/main.c
+@@ -1907,7 +1907,7 @@ static int b43legacy_gpio_init(struct b43legacy_wldev *dev)
+ 	if (dev->dev->id.revision >= 2)
+ 		mask  |= 0x0010; /* FIXME: This is redundant. */
+ 
+-#ifdef CPTCFG_SSB_DRIVER_PCICORE
++#ifdef CONFIG_SSB_DRIVER_PCICORE
+ 	pcidev = bus->pcicore.dev;
+ #endif
+ 	gpiodev = bus->chipco.dev ? : pcidev;
+@@ -1926,7 +1926,7 @@ static void b43legacy_gpio_cleanup(struct b43legacy_wldev *dev)
+ 	struct ssb_bus *bus = dev->dev->bus;
+ 	struct ssb_device *gpiodev, *pcidev = NULL;
+ 
+-#ifdef CPTCFG_SSB_DRIVER_PCICORE
++#ifdef CONFIG_SSB_DRIVER_PCICORE
+ 	pcidev = bus->pcicore.dev;
+ #endif
+ 	gpiodev = bus->chipco.dev ? : pcidev;
+diff --git a/drivers/net/wireless/broadcom/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig
+index 400dc88..b2d97b8 100644
+--- a/drivers/net/wireless/broadcom/brcm80211/Kconfig
++++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig
+@@ -8,7 +8,7 @@ config BRCMSMAC
+ 	depends on m
+ 	depends on MAC80211
+ 	depends on BCMA_POSSIBLE
+-	select BCMA
++	depends on BCMA
+ 	select BRCMUTIL
+ 	depends on FW_LOADER
+ 	depends on CORDIC
+diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
+index 8afbf52..2a2831b 100644
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
+@@ -1574,7 +1574,11 @@ static int brcmf_usb_reset_device(struct device *dev, void *notused)
+ 
+ void brcmf_usb_exit(void)
+ {
++#if LINUX_VERSION_IS_GEQ(6,8,0)
+ 	struct device_driver *drv = &brcmf_usbdrvr.driver;
++#else
++	struct device_driver *drv = &brcmf_usbdrvr.drvwrap.driver;
++#endif
+ 	int ret;
+ 
+ 	brcmf_dbg(USB, "Enter\n");
+diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
+index 718184b..2f4c792 100644
+--- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
++++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
+@@ -676,13 +676,23 @@ static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
+ 	for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++) {
+ 		mvm->tz_device.trips[i].temperature = THERMAL_TEMP_INVALID;
+ 		mvm->tz_device.trips[i].type = THERMAL_TRIP_PASSIVE;
++#if LINUX_VERSION_IS_GEQ(6,9,0)
+ 		mvm->tz_device.trips[i].flags = THERMAL_TRIP_FLAG_RW_TEMP;
++#endif
+ 	}
++#if LINUX_VERSION_IS_GEQ(6,9,0)
+ 	mvm->tz_device.tzone = thermal_zone_device_register_with_trips(name,
+ 							mvm->tz_device.trips,
+ 							IWL_MAX_DTS_TRIPS,
+ 							mvm, &tzone_ops,
+ 							NULL, 0, 0);
++#else
++	mvm->tz_device.tzone = thermal_zone_device_register_with_trips(name,
++							mvm->tz_device.trips,
++							IWL_MAX_DTS_TRIPS, 0,
++							mvm, &tzone_ops,
++							NULL, 0, 0);
++#endif
+ 	if (IS_ERR(mvm->tz_device.tzone)) {
+ 		IWL_DEBUG_TEMP(mvm,
+ 			       "Failed to register to thermal zone (err = %ld)\n",
+diff --git a/drivers/staging/rtl8723bs/Kconfig b/drivers/staging/rtl8723bs/Kconfig
+index b51916c..b46ff98 100644
+--- a/drivers/staging/rtl8723bs/Kconfig
++++ b/drivers/staging/rtl8723bs/Kconfig
+@@ -5,7 +5,6 @@ config RTL8723BS
+ 	depends on m
+ 	depends on WLAN && MMC && CFG80211
+ 	depends on m
+-	select CFG80211_WEXT
+ 	depends on CRYPTO
+ 	select BPAUTO_CRYPTO_LIB_ARC4
+ 	help
+diff --git a/kconf/Makefile b/kconf/Makefile
+index 2004c44..a2790b1 100644
+--- a/kconf/Makefile
++++ b/kconf/Makefile
+@@ -1,9 +1,9 @@
+-CFLAGS=-Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer
++CFLAGS=-Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -DKBUILD_NO_NLS
+ 
+ LXDIALOG := lxdialog/checklist.o lxdialog/inputbox.o lxdialog/menubox.o lxdialog/textbox.o lxdialog/util.o lxdialog/yesno.o
+ 
+ conf: conf.o zconf.tab.o
+-mconf_CFLAGS := $(shell ./lxdialog/check-lxdialog.sh -ccflags) -DLOCALE
++mconf_CFLAGS := $(shell ./lxdialog/check-lxdialog.sh -ccflags)
+ mconf_LDFLAGS := $(shell ./lxdialog/check-lxdialog.sh -ldflags $(CC))
+ mconf: CFLAGS += $(mconf_CFLAGS)
+ 
+diff --git a/kconf/conf.c b/kconf/conf.c
+index 283eeed..1707f05 100644
+--- a/kconf/conf.c
++++ b/kconf/conf.c
+@@ -598,40 +598,12 @@ int main(int ac, char **av)
+ 	case oldconfig:
+ 	case listnewconfig:
+ 	case olddefconfig:
+-		conf_read(NULL);
+-		break;
+ 	case allnoconfig:
+ 	case allyesconfig:
+ 	case allmodconfig:
+ 	case alldefconfig:
+ 	case randconfig:
+-		name = getenv("KCONFIG_ALLCONFIG");
+-		if (!name)
+-			break;
+-		if ((strcmp(name, "") != 0) && (strcmp(name, "1") != 0)) {
+-			if (conf_read_simple(name, S_DEF_USER)) {
+-				fprintf(stderr,
+-					_("*** Can't read seed configuration \"%s\"!\n"),
+-					name);
+-				exit(1);
+-			}
+-			break;
+-		}
+-		switch (input_mode) {
+-		case allnoconfig:	name = "allno.config"; break;
+-		case allyesconfig:	name = "allyes.config"; break;
+-		case allmodconfig:	name = "allmod.config"; break;
+-		case alldefconfig:	name = "alldef.config"; break;
+-		case randconfig:	name = "allrandom.config"; break;
+-		default: break;
+-		}
+-		if (conf_read_simple(name, S_DEF_USER) &&
+-		    conf_read_simple("all.config", S_DEF_USER)) {
+-			fprintf(stderr,
+-				_("*** KCONFIG_ALLCONFIG set, but no \"%s\" or \"all.config\" file found\n"),
+-				name);
+-			exit(1);
+-		}
++		conf_read(NULL);
+ 		break;
+ 	default:
+ 		break;
+diff --git a/kconf/confdata.c b/kconf/confdata.c
+index df26c7b..1038c30 100644
+--- a/kconf/confdata.c
++++ b/kconf/confdata.c
+@@ -1170,6 +1170,8 @@ bool conf_set_all_new_symbols(enum conf_def_mode mode)
+ 	}
+ 	bool has_changed = false;
+ 
++	sym_clear_all_valid();
++
+ 	for_all_symbols(i, sym) {
+ 		if (sym_has_value(sym) || (sym->flags & SYMBOL_VALID))
+ 			continue;
+@@ -1213,8 +1215,6 @@ bool conf_set_all_new_symbols(enum conf_def_mode mode)
+ 
+ 	}
+ 
+-	sym_clear_all_valid();
+-
+ 	/*
+ 	 * We have different type of choice blocks.
+ 	 * If curr.tri equals to mod then we can select several
+diff --git a/local-symbols b/local-symbols
+index 243f776..d7653ac 100644
+--- a/local-symbols
++++ b/local-symbols
+@@ -59,14 +59,6 @@ MAC80211_MESH_PS_DEBUG=
+ MAC80211_TDLS_DEBUG=
+ MAC80211_DEBUG_COUNTERS=
+ MAC80211_STA_HASH_MAX_SIZE=
+-QRTR=
+-QRTR_SMD=
+-QRTR_TUN=
+-QRTR_MHI=
+-MHI_BUS=
+-MHI_BUS_DEBUG=
+-MHI_BUS_PCI_GENERIC=
+-MHI_BUS_EP=
+ QCOM_AOSS_QMP=
+ QCOM_COMMAND_DB=
+ QCOM_GENI_SE=
+@@ -470,43 +462,6 @@ USB_VL600=
+ USB_NET_CH9200=
+ USB_NET_AQC111=
+ USB_RTL8153_ECM=
+-SSB_POSSIBLE=
+-SSB=
+-SSB_SPROM=
+-SSB_BLOCKIO=
+-SSB_PCIHOST_POSSIBLE=
+-SSB_PCIHOST=
+-SSB_B43_PCI_BRIDGE=
+-SSB_PCMCIAHOST_POSSIBLE=
+-SSB_PCMCIAHOST=
+-SSB_SDIOHOST_POSSIBLE=
+-SSB_SDIOHOST=
+-SSB_HOST_SOC=
+-SSB_SERIAL=
+-SSB_DRIVER_PCICORE_POSSIBLE=
+-SSB_DRIVER_PCICORE=
+-SSB_PCICORE_HOSTMODE=
+-SSB_DRIVER_MIPS=
+-SSB_SFLASH=
+-SSB_EMBEDDED=
+-SSB_DRIVER_EXTIF=
+-SSB_DRIVER_GIGE=
+-SSB_DRIVER_GPIO=
+-BCMA_POSSIBLE=
+-BCMA=
+-BCMA_BLOCKIO=
+-BCMA_HOST_PCI_POSSIBLE=
+-BCMA_HOST_PCI=
+-BCMA_HOST_SOC=
+-BCMA_DRIVER_PCI=
+-BCMA_DRIVER_PCI_HOSTMODE=
+-BCMA_DRIVER_MIPS=
+-BCMA_PFLASH=
+-BCMA_SFLASH=
+-BCMA_NFLASH=
+-BCMA_DRIVER_GMAC_CMN=
+-BCMA_DRIVER_GPIO=
+-BCMA_DEBUG=
+ USB_ACM=
+ USB_PRINTER=
+ USB_WDM=
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index cbc5626..6f7273a 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -16542,9 +16542,14 @@ static u32 nl80211_internal_flags[] = {
+ #undef SELECTOR
+ };
+ 
++#if LINUX_VERSION_IS_LESS(6,2,0)
++static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
++			    struct genl_info *info)
++#else
+ static int nl80211_pre_doit(const struct genl_split_ops *ops,
+ 			    struct sk_buff *skb,
+ 			    struct genl_info *info)
++#endif
+ {
+ 	struct cfg80211_registered_device *rdev = NULL;
+ 	struct wireless_dev *wdev = NULL;
+@@ -16644,9 +16649,14 @@ out_unlock:
+ 	return err;
+ }
+ 
++#if LINUX_VERSION_IS_LESS(6,2,0)
++static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
++			      struct genl_info *info)
++#else
+ static void nl80211_post_doit(const struct genl_split_ops *ops,
+ 			      struct sk_buff *skb,
+ 			      struct genl_info *info)
++#endif
+ {
+ 	u32 internal_flags = nl80211_internal_flags[ops->internal_flags];
+ 
+diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
+index 62f2661..4f855bf 100644
+--- a/net/wireless/sysfs.c
++++ b/net/wireless/sysfs.c
+@@ -154,7 +154,11 @@ static SIMPLE_DEV_PM_OPS(wiphy_pm_ops, wiphy_suspend, wiphy_resume);
+ #define WIPHY_PM_OPS NULL
+ #endif
+ 
++#if LINUX_VERSION_IS_GEQ(6,2,0)
+ static const void *wiphy_namespace(const struct device *d)
++#else
++static const void *wiphy_namespace(struct device *d)
++#endif
+ {
+ 	struct wiphy *wiphy = container_of(d, struct wiphy, dev);
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0001-sync-backports-patches-build.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0001-sync-backports-patches-build.patch
deleted file mode 100644
index e926f0b..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0001-sync-backports-patches-build.patch
+++ /dev/null
@@ -1,815 +0,0 @@
-From 45a2c927c790c9f71a81983f6d59b15cfb6b05ac Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Tue, 12 Mar 2024 11:25:41 +0800
-Subject: [PATCH 01/61] sync backports patches/build
-
----
- Kconfig.local                                 | 111 -----------------
- Kconfig.sources                               |   5 -
- Makefile                                      | 116 +++++++++---------
- Makefile.real                                 |  29 ++++-
- compat/main.c                                 |  25 ----
- drivers/net/wireless/ath/ath11k/Kconfig       |   6 +-
- drivers/net/wireless/broadcom/b43/Kconfig     |  12 +-
- drivers/net/wireless/broadcom/b43/main.c      |   4 +-
- .../net/wireless/broadcom/b43legacy/Kconfig   |   8 +-
- .../net/wireless/broadcom/b43legacy/main.c    |   4 +-
- .../net/wireless/broadcom/brcm80211/Kconfig   |   2 +-
- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h  |   2 +-
- drivers/net/wireless/intel/iwlwifi/mvm/tt.c   |  21 ++--
- drivers/staging/rtl8723bs/Kconfig             |   1 -
- kconf/Makefile                                |   4 +-
- kconf/conf.c                                  |  30 +----
- kconf/confdata.c                              |   4 +-
- local-symbols                                 |  45 -------
- 18 files changed, 118 insertions(+), 311 deletions(-)
-
-diff --git a/Kconfig.local b/Kconfig.local
-index d9495b1..547f8ad 100644
---- a/Kconfig.local
-+++ b/Kconfig.local
-@@ -1420,117 +1420,6 @@ config BACKPORTED_USB_NET_AQC111
- config BACKPORTED_USB_RTL8153_ECM
- 	tristate
- 	default USB_RTL8153_ECM
--config BACKPORTED_SSB_POSSIBLE
--	tristate
--	default SSB_POSSIBLE
--config BACKPORTED_SSB
--	tristate
--	default SSB
--config BACKPORTED_SSB_SPROM
--	tristate
--	default SSB_SPROM
--config BACKPORTED_SSB_BLOCKIO
--	tristate
--	default SSB_BLOCKIO
--config BACKPORTED_SSB_PCIHOST_POSSIBLE
--	tristate
--	default SSB_PCIHOST_POSSIBLE
--config BACKPORTED_SSB_PCIHOST
--	tristate
--	default SSB_PCIHOST
--config BACKPORTED_SSB_B43_PCI_BRIDGE
--	tristate
--	default SSB_B43_PCI_BRIDGE
--config BACKPORTED_SSB_PCMCIAHOST_POSSIBLE
--	tristate
--	default SSB_PCMCIAHOST_POSSIBLE
--config BACKPORTED_SSB_PCMCIAHOST
--	tristate
--	default SSB_PCMCIAHOST
--config BACKPORTED_SSB_SDIOHOST_POSSIBLE
--	tristate
--	default SSB_SDIOHOST_POSSIBLE
--config BACKPORTED_SSB_SDIOHOST
--	tristate
--	default SSB_SDIOHOST
--config BACKPORTED_SSB_HOST_SOC
--	tristate
--	default SSB_HOST_SOC
--config BACKPORTED_SSB_SERIAL
--	tristate
--	default SSB_SERIAL
--config BACKPORTED_SSB_DRIVER_PCICORE_POSSIBLE
--	tristate
--	default SSB_DRIVER_PCICORE_POSSIBLE
--config BACKPORTED_SSB_DRIVER_PCICORE
--	tristate
--	default SSB_DRIVER_PCICORE
--config BACKPORTED_SSB_PCICORE_HOSTMODE
--	tristate
--	default SSB_PCICORE_HOSTMODE
--config BACKPORTED_SSB_DRIVER_MIPS
--	tristate
--	default SSB_DRIVER_MIPS
--config BACKPORTED_SSB_SFLASH
--	tristate
--	default SSB_SFLASH
--config BACKPORTED_SSB_EMBEDDED
--	tristate
--	default SSB_EMBEDDED
--config BACKPORTED_SSB_DRIVER_EXTIF
--	tristate
--	default SSB_DRIVER_EXTIF
--config BACKPORTED_SSB_DRIVER_GIGE
--	tristate
--	default SSB_DRIVER_GIGE
--config BACKPORTED_SSB_DRIVER_GPIO
--	tristate
--	default SSB_DRIVER_GPIO
--config BACKPORTED_BCMA_POSSIBLE
--	tristate
--	default BCMA_POSSIBLE
--config BACKPORTED_BCMA
--	tristate
--	default BCMA
--config BACKPORTED_BCMA_BLOCKIO
--	tristate
--	default BCMA_BLOCKIO
--config BACKPORTED_BCMA_HOST_PCI_POSSIBLE
--	tristate
--	default BCMA_HOST_PCI_POSSIBLE
--config BACKPORTED_BCMA_HOST_PCI
--	tristate
--	default BCMA_HOST_PCI
--config BACKPORTED_BCMA_HOST_SOC
--	tristate
--	default BCMA_HOST_SOC
--config BACKPORTED_BCMA_DRIVER_PCI
--	tristate
--	default BCMA_DRIVER_PCI
--config BACKPORTED_BCMA_DRIVER_PCI_HOSTMODE
--	tristate
--	default BCMA_DRIVER_PCI_HOSTMODE
--config BACKPORTED_BCMA_DRIVER_MIPS
--	tristate
--	default BCMA_DRIVER_MIPS
--config BACKPORTED_BCMA_PFLASH
--	tristate
--	default BCMA_PFLASH
--config BACKPORTED_BCMA_SFLASH
--	tristate
--	default BCMA_SFLASH
--config BACKPORTED_BCMA_NFLASH
--	tristate
--	default BCMA_NFLASH
--config BACKPORTED_BCMA_DRIVER_GMAC_CMN
--	tristate
--	default BCMA_DRIVER_GMAC_CMN
--config BACKPORTED_BCMA_DRIVER_GPIO
--	tristate
--	default BCMA_DRIVER_GPIO
--config BACKPORTED_BCMA_DEBUG
--	tristate
--	default BCMA_DEBUG
- config BACKPORTED_USB_ACM
- 	tristate
- 	default USB_ACM
-diff --git a/Kconfig.sources b/Kconfig.sources
-index 2ea4d8a..d74affd 100644
---- a/Kconfig.sources
-+++ b/Kconfig.sources
-@@ -4,15 +4,10 @@ source "$BACKPORT_DIR/compat/Kconfig"
- # these are copied from the kernel
- source "$BACKPORT_DIR/net/wireless/Kconfig"
- source "$BACKPORT_DIR/net/mac80211/Kconfig"
--source "$BACKPORT_DIR/net/qrtr/Kconfig"
--source "$BACKPORT_DIR/drivers/bus/mhi/Kconfig"
- source "$BACKPORT_DIR/drivers/soc/qcom/Kconfig"
- source "$BACKPORT_DIR/drivers/net/wireless/Kconfig"
- source "$BACKPORT_DIR/drivers/net/usb/Kconfig"
- 
--source "$BACKPORT_DIR/drivers/ssb/Kconfig"
--source "$BACKPORT_DIR/drivers/bcma/Kconfig"
--
- source "$BACKPORT_DIR/drivers/usb/class/Kconfig"
- 
- source "$BACKPORT_DIR/drivers/staging/Kconfig"
-diff --git a/Makefile b/Makefile
-index 77c2670..c431b71 100644
---- a/Makefile
-+++ b/Makefile
-@@ -2,10 +2,10 @@
- # Makefile for the output source package
- #
- 
--ifeq ($(KERNELRELEASE),)
-+ifeq ($(KERNELVERSION),)
- 
- MAKEFLAGS += --no-print-directory
--SHELL := /bin/bash
-+SHELL := /usr/bin/env bash
- BACKPORT_DIR := $(shell pwd)
- 
- KMODDIR ?= updates
-@@ -19,6 +19,7 @@ KLIB_BUILD ?= $(KLIB)/build/
- KERNEL_CONFIG := $(KLIB_BUILD)/.config
- KERNEL_MAKEFILE := $(KLIB_BUILD)/Makefile
- CONFIG_MD5 := $(shell md5sum $(KERNEL_CONFIG) 2>/dev/null | sed 's/\s.*//')
-+STAMP_KERNEL_CONFIG := .kernel_config_md5_$(CONFIG_MD5)
- 
- export KLIB KLIB_BUILD BACKPORT_DIR KMODDIR KMODPATH_ARG
- 
-@@ -36,7 +37,8 @@ mrproper:
- 	@rm -f .kernel_config_md5 Kconfig.versions Kconfig.kernel
- 	@rm -f backport-include/backport/autoconf.h
- 
--.DEFAULT:
-+.SILENT: $(STAMP_KERNEL_CONFIG)
-+$(STAMP_KERNEL_CONFIG):
- 	@set -e ; test -f local-symbols || (						\
- 	echo "/--------------"								;\
- 	echo "| You shouldn't run make in the backports tree, but only in"		;\
-@@ -60,58 +62,62 @@ mrproper:
- 	echo "| (that isn't currently running.)"					;\
- 	echo "\\--"									;\
- 	false)
--	@set -e ; if [ "$$(cat .kernel_config_md5 2>/dev/null)" != "$(CONFIG_MD5)" ]	;\
--	then 										\
--		echo -n "Generating local configuration database from kernel ..."	;\
--		grep -v -f local-symbols $(KERNEL_CONFIG) | grep = | (			\
--			while read l ; do						\
--				if [ "$${l:0:7}" != "CONFIG_" ] ; then			\
--					continue					;\
--				fi							;\
--				l=$${l:7}						;\
--				n=$${l%%=*}						;\
--				v=$${l#*=}						;\
--				if [ "$$v" = "m" ] ; then				\
--					echo config $$n					;\
--					echo '    tristate' 				;\
--				elif [ "$$v" = "y" ] ; then				\
--					echo config $$n					;\
--					echo '    bool'					;\
--				else							\
--					continue					;\
--				fi							;\
--				echo "    default $$v"					;\
--				echo ""							;\
--			done								\
--		) > Kconfig.kernel							;\
--		kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) M=$(BACKPORT_DIR)	\
--			kernelversion |	sed 's/^\(\([3-5]\|2\.6\)\.[0-9]\+\).*/\1/;t;d');\
--		test "$$kver" != "" || echo "Kernel version parse failed!"		;\
--		test "$$kver" != ""							;\
--		kvers="$$(seq 14 39 | sed 's/^/2.6./')"					;\
--		kvers="$$kvers $$(seq 0 19 | sed 's/^/3./')"				;\
--		kvers="$$kvers $$(seq 0 20 | sed 's/^/4./')"				;\
--		kvers="$$kvers $$(seq 0 99 | sed 's/^/5./')"				;\
--		print=0									;\
--		for v in $$kvers ; do							\
--			if [ "$$print" = "1" ] ; then					\
--				echo config KERNEL_$$(echo $$v | tr . _)	;\
--				echo "    def_bool y"					;\
--			fi								;\
--			if [ "$$v" = "$$kver" ] ; then print=1 ; fi			;\
--		done > Kconfig.versions							;\
--		# RHEL as well, sadly we need to grep for it				;\
--		RHEL_MAJOR=$$(grep '^RHEL_MAJOR' $(KERNEL_MAKEFILE) | 			\
--					sed 's/.*=\s*\([0-9]*\)/\1/;t;d')		;\
--		RHEL_MINOR=$$(grep '^RHEL_MINOR' $(KERNEL_MAKEFILE) | 			\
--					sed 's/.*=\s*\([0-9]*\)/\1/;t;d')		;\
--		for v in $$(seq 0 $$RHEL_MINOR) ; do 					\
--			echo config BACKPORT_RHEL_KERNEL_$${RHEL_MAJOR}_$$v		;\
--			echo "    def_bool y"						;\
--		done >> Kconfig.versions						;\
--		echo " done."								;\
--	fi										;\
--	echo "$(CONFIG_MD5)" > .kernel_config_md5
-+	@rm -f .kernel_config_md5_*
-+	@touch $@
-+
-+Kconfig.kernel: $(STAMP_KERNEL_CONFIG) local-symbols
-+	@printf "Generating local configuration database from kernel ..."
-+	@grep -v -f local-symbols $(KERNEL_CONFIG) | grep = | (			\
-+		while read l ; do						\
-+			if [ "$${l:0:7}" != "CONFIG_" ] ; then			\
-+				continue					;\
-+			fi							;\
-+			l=$${l:7}						;\
-+			n=$${l%%=*}						;\
-+			v=$${l#*=}						;\
-+			if [ "$$v" = "m" ] ; then				\
-+				echo config $$n					;\
-+				echo '    tristate' 				;\
-+			elif [ "$$v" = "y" ] ; then				\
-+				echo config $$n					;\
-+				echo '    bool'					;\
-+			else							\
-+				continue					;\
-+			fi							;\
-+			echo "    default $$v"					;\
-+			echo ""							;\
-+		done								\
-+	) > $@
-+	@echo " done."
-+
-+Kconfig.versions: Kconfig.kernel
-+	@kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) M=$(BACKPORT_DIR) \
-+		kernelversion |	sed 's/^\(\([3-5]\|2\.6\)\.[0-9]\+\).*/\1/;t;d');\
-+	test "$$kver" != "" || echo "Kernel version parse failed!"		;\
-+	test "$$kver" != ""							;\
-+	kvers="$$(seq 14 39 | sed 's/^/2.6./')"					;\
-+	kvers="$$kvers $$(seq 0 19 | sed 's/^/3./')"				;\
-+	kvers="$$kvers $$(seq 0 20 | sed 's/^/4./')"				;\
-+	kvers="$$kvers $$(seq 0 99 | sed 's/^/5./')"				;\
-+	print=0									;\
-+	for v in $$kvers ; do							\
-+		if [ "$$print" = "1" ] ; then					\
-+			echo config KERNEL_$$(echo $$v | tr . _)	;\
-+			echo "    def_bool y"					;\
-+		fi								;\
-+		if [ "$$v" = "$$kver" ] ; then print=1 ; fi			;\
-+	done > $@
-+	@RHEL_MAJOR=$$(grep '^RHEL_MAJOR' $(KERNEL_MAKEFILE) | 			\
-+				sed 's/.*=\s*\([0-9]*\)/\1/;t;d')		;\
-+	RHEL_MINOR=$$(grep '^RHEL_MINOR' $(KERNEL_MAKEFILE) | 			\
-+				sed 's/.*=\s*\([0-9]*\)/\1/;t;d')		;\
-+	for v in $$(seq 0 $$RHEL_MINOR) ; do 					\
-+		echo config BACKPORT_RHEL_KERNEL_$${RHEL_MAJOR}_$$v		;\
-+		echo "    def_bool y"						;\
-+	done >> $@
-+
-+.DEFAULT:
-+	@$(MAKE) Kconfig.versions
- 	@$(MAKE) -f Makefile.real "$@"
- 
- .PHONY: defconfig-help
-diff --git a/Makefile.real b/Makefile.real
-index 6550802..971a543 100644
---- a/Makefile.real
-+++ b/Makefile.real
-@@ -6,6 +6,18 @@ else
- export BACKPORTS_GIT_TRACKER_DEF=
- endif
- 
-+ifneq ($(LLVM),)
-+ifneq ($(filter %/,$(LLVM)),)
-+LLVM_PREFIX := $(LLVM)
-+else ifneq ($(filter -%,$(LLVM)),)
-+LLVM_SUFFIX := $(LLVM)
-+endif
-+
-+HOSTCC	= $(LLVM_PREFIX)clang$(LLVM_SUFFIX)
-+else
-+HOSTCC	= gcc
-+endif
-+
- # disable built-in rules for this file
- .SUFFIXES:
- 
-@@ -24,21 +36,21 @@ listnewconfig oldaskconfig oldconfig \
- silentoldconfig olddefconfig oldnoconfig \
- allnoconfig allyesconfig allmodconfig \
- alldefconfig randconfig:
--	@$(MAKE) -C kconf conf
-+	@$(MAKE) -C kconf CC=$(HOSTCC) conf
- 	@./kconf/conf --$@ Kconfig
- 
- .PHONY: usedefconfig
- usedefconfig:
--	@$(MAKE) -C kconf conf
-+	@$(MAKE) -C kconf CC=$(HOSTCC) conf
- 	@./kconf/conf --defconfig=defconfig Kconfig
- 
- .PHONY: savedefconfig
- savedefconfig:
--	@$(MAKE) -C kconf conf
-+	@$(MAKE) -C kconf CC=$(HOSTCC) conf
- 	@./kconf/conf --savedefconfig=defconfig Kconfig
- 
- defconfig-%::
--	@$(MAKE) -C kconf conf
-+	@$(MAKE) -C kconf CC=$(HOSTCC) conf
- 	@./kconf/conf --defconfig=defconfigs/$(@:defconfig-%=%) Kconfig
- 
- .config:
-@@ -59,7 +71,7 @@ defconfig-%::
- 
- backport-include/backport/autoconf.h: .config Kconfig.versions Kconfig.kernel
- 	@$(MAKE) oldconfig
--	@echo -n "Building backport-include/backport/autoconf.h ..."
-+	@printf "Building backport-include/backport/autoconf.h ..."
- 	@grep -f local-symbols .config | (				\
- 		echo "#ifndef COMPAT_AUTOCONF_INCLUDED"			;\
- 		echo "#define COMPAT_AUTOCONF_INCLUDED"			;\
-@@ -80,7 +92,12 @@ backport-include/backport/autoconf.h: .config Kconfig.versions Kconfig.kernel
- 			esac						;\
- 		done							;\
- 		echo "#endif /* COMPAT_AUTOCONF_INCLUDED */"		;\
--	) > backport-include/backport/autoconf.h
-+	) > $@.new
-+	@if cmp -s $@ $@.new; then \
-+		rm -f $@.new; \
-+	else \
-+		mv $@.new $@; \
-+	fi
- 	@echo " done."
- 
- .PHONY: modules
-diff --git a/compat/main.c b/compat/main.c
-index d4f3340..651ab63 100644
---- a/compat/main.c
-+++ b/compat/main.c
-@@ -19,31 +19,6 @@ MODULE_LICENSE("GPL");
- #error "You need a CPTCFG_VERSION"
- #endif
- 
--static char *backported_kernel_name = CPTCFG_KERNEL_NAME;
--
--module_param(backported_kernel_name, charp, 0400);
--MODULE_PARM_DESC(backported_kernel_name,
--		 "The kernel tree name that was used for this backport (" CPTCFG_KERNEL_NAME ")");
--
--#ifdef BACKPORTS_GIT_TRACKED
--static char *backports_tracker_id = BACKPORTS_GIT_TRACKED;
--module_param(backports_tracker_id, charp, 0400);
--MODULE_PARM_DESC(backports_tracker_id,
--		 "The version of the tree containing this backport (" BACKPORTS_GIT_TRACKED ")");
--#else
--static char *backported_kernel_version = CPTCFG_KERNEL_VERSION;
--static char *backports_version = CPTCFG_VERSION;
--
--module_param(backported_kernel_version, charp, 0400);
--MODULE_PARM_DESC(backported_kernel_version,
--		 "The kernel version that was used for this backport (" CPTCFG_KERNEL_VERSION ")");
--
--module_param(backports_version, charp, 0400);
--MODULE_PARM_DESC(backports_version,
--		 "The git version of the backports tree used to generate this backport (" CPTCFG_VERSION ")");
--
--#endif
--
- void backport_dependency_symbol(void)
- {
- }
-diff --git a/drivers/net/wireless/ath/ath11k/Kconfig b/drivers/net/wireless/ath/ath11k/Kconfig
-index 7430aaf..f5f58a5 100644
---- a/drivers/net/wireless/ath/ath11k/Kconfig
-+++ b/drivers/net/wireless/ath/ath11k/Kconfig
-@@ -25,9 +25,9 @@ config ATH11K_PCI
- 	tristate "Atheros ath11k PCI support"
- 	depends on m
- 	depends on ATH11K && PCI
--	select MHI_BUS
--	select QRTR
--	select QRTR_MHI
-+	depends on MHI_BUS
-+	depends on QRTR
-+	depends on QRTR_MHI
- 	help
- 	  This module adds support for PCIE bus
- 
-diff --git a/drivers/net/wireless/broadcom/b43/Kconfig b/drivers/net/wireless/broadcom/b43/Kconfig
-index 2e196b5..84cbe38 100644
---- a/drivers/net/wireless/broadcom/b43/Kconfig
-+++ b/drivers/net/wireless/broadcom/b43/Kconfig
-@@ -63,21 +63,21 @@ endchoice
- config B43_PCI_AUTOSELECT
- 	bool
- 	depends on B43 && SSB_PCIHOST_POSSIBLE
--	select SSB_PCIHOST
--	select SSB_B43_PCI_BRIDGE
-+	depends on SSB_PCIHOST
-+	depends on SSB_B43_PCI_BRIDGE
- 	default y
- 
- # Auto-select SSB PCICORE driver, if possible
- config B43_PCICORE_AUTOSELECT
- 	bool
- 	depends on B43 && SSB_DRIVER_PCICORE_POSSIBLE
--	select SSB_DRIVER_PCICORE
-+	depends on SSB_DRIVER_PCICORE
- 	default y
- 
- config B43_SDIO
- 	bool "Broadcom 43xx SDIO device support"
- 	depends on B43 && B43_SSB && SSB_SDIOHOST_POSSIBLE
--	select SSB_SDIOHOST
-+	depends on SSB_SDIOHOST
- 	help
- 	  Broadcom 43xx device support for Soft-MAC SDIO devices.
- 
-@@ -96,13 +96,13 @@ config B43_SDIO
- config B43_BCMA_PIO
- 	bool
- 	depends on B43 && B43_BCMA
--	select BCMA_BLOCKIO
-+	depends on BCMA_BLOCKIO
- 	default y
- 
- config B43_PIO
- 	bool
- 	depends on B43 && B43_SSB
--	select SSB_BLOCKIO
-+	depends on SSB_BLOCKIO
- 	default y
- 
- config B43_PHY_G
-diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c
-index c2e0a05..bf2be0a 100644
---- a/drivers/net/wireless/broadcom/b43/main.c
-+++ b/drivers/net/wireless/broadcom/b43/main.c
-@@ -2854,7 +2854,7 @@ static struct ssb_device *b43_ssb_gpio_dev(struct b43_wldev *dev)
- {
- 	struct ssb_bus *bus = dev->dev->sdev->bus;
- 
--#ifdef CPTCFG_SSB_DRIVER_PCICORE
-+#ifdef CONFIG_SSB_DRIVER_PCICORE
- 	return (bus->chipco.dev ? bus->chipco.dev : bus->pcicore.dev);
- #else
- 	return bus->chipco.dev;
-@@ -4873,7 +4873,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
- 	}
- 	if (sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW)
- 		hf |= B43_HF_DSCRQ; /* Disable slowclock requests from ucode. */
--#if defined(CPTCFG_B43_SSB) && defined(CPTCFG_SSB_DRIVER_PCICORE)
-+#if defined(CPTCFG_B43_SSB) && defined(CONFIG_SSB_DRIVER_PCICORE)
- 	if (dev->dev->bus_type == B43_BUS_SSB &&
- 	    dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI &&
- 	    dev->dev->sdev->bus->pcicore.dev->id.revision <= 10)
-diff --git a/drivers/net/wireless/broadcom/b43legacy/Kconfig b/drivers/net/wireless/broadcom/b43legacy/Kconfig
-index 6ba7eb7..b924f63 100644
---- a/drivers/net/wireless/broadcom/b43legacy/Kconfig
-+++ b/drivers/net/wireless/broadcom/b43legacy/Kconfig
-@@ -3,7 +3,7 @@ config B43LEGACY
- 	tristate "Broadcom 43xx-legacy wireless support (mac80211 stack)"
- 	depends on m
- 	depends on SSB_POSSIBLE && MAC80211 && HAS_DMA
--	select SSB
-+	depends on SSB
- 	depends on FW_LOADER
- 	help
- 	  b43legacy is a driver for 802.11b devices from Broadcom (BCM4301 and
-@@ -25,15 +25,15 @@ config B43LEGACY
- config B43LEGACY_PCI_AUTOSELECT
- 	bool
- 	depends on B43LEGACY && SSB_PCIHOST_POSSIBLE
--	select SSB_PCIHOST
--	select SSB_B43_PCI_BRIDGE
-+	depends on SSB_PCIHOST
-+	depends on SSB_B43_PCI_BRIDGE
- 	default y
- 
- # Auto-select SSB PCICORE driver, if possible
- config B43LEGACY_PCICORE_AUTOSELECT
- 	bool
- 	depends on B43LEGACY && SSB_DRIVER_PCICORE_POSSIBLE
--	select SSB_DRIVER_PCICORE
-+	depends on SSB_DRIVER_PCICORE
- 	default y
- 
- # LED support
-diff --git a/drivers/net/wireless/broadcom/b43legacy/main.c b/drivers/net/wireless/broadcom/b43legacy/main.c
-index edd91ff..0222f63 100644
---- a/drivers/net/wireless/broadcom/b43legacy/main.c
-+++ b/drivers/net/wireless/broadcom/b43legacy/main.c
-@@ -1907,7 +1907,7 @@ static int b43legacy_gpio_init(struct b43legacy_wldev *dev)
- 	if (dev->dev->id.revision >= 2)
- 		mask  |= 0x0010; /* FIXME: This is redundant. */
- 
--#ifdef CPTCFG_SSB_DRIVER_PCICORE
-+#ifdef CONFIG_SSB_DRIVER_PCICORE
- 	pcidev = bus->pcicore.dev;
- #endif
- 	gpiodev = bus->chipco.dev ? : pcidev;
-@@ -1926,7 +1926,7 @@ static void b43legacy_gpio_cleanup(struct b43legacy_wldev *dev)
- 	struct ssb_bus *bus = dev->dev->bus;
- 	struct ssb_device *gpiodev, *pcidev = NULL;
- 
--#ifdef CPTCFG_SSB_DRIVER_PCICORE
-+#ifdef CONFIG_SSB_DRIVER_PCICORE
- 	pcidev = bus->pcicore.dev;
- #endif
- 	gpiodev = bus->chipco.dev ? : pcidev;
-diff --git a/drivers/net/wireless/broadcom/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig
-index 400dc88..b2d97b8 100644
---- a/drivers/net/wireless/broadcom/brcm80211/Kconfig
-+++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig
-@@ -8,7 +8,7 @@ config BRCMSMAC
- 	depends on m
- 	depends on MAC80211
- 	depends on BCMA_POSSIBLE
--	select BCMA
-+	depends on BCMA
- 	select BRCMUTIL
- 	depends on FW_LOADER
- 	depends on CORDIC
-diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
-index b1a1ce5..66f9281 100644
---- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
-+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
-@@ -555,7 +555,7 @@ struct iwl_mvm_tt_mgmt {
-  * @tzone: thermal zone device data
- */
- struct iwl_mvm_thermal_device {
--	struct thermal_trip trips[IWL_MAX_DTS_TRIPS];
-+	s16 temp_trips[IWL_MAX_DTS_TRIPS];
- 	u8 fw_trips_index[IWL_MAX_DTS_TRIPS];
- 	struct thermal_zone_device *tzone;
- };
-diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
-index dee9c36..1fb364d 100644
---- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
-+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
-@@ -573,11 +573,11 @@ int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm)
- 	 * and uncompressed, the FW should get it compressed and sorted
- 	 */
- 
--	/* compress trips to cmd array, remove uninitialized values*/
-+	/* compress temp_trips to cmd array, remove uninitialized values*/
- 	for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
--		if (mvm->tz_device.trips[i].temperature != INT_MIN) {
-+		if (mvm->tz_device.temp_trips[i] != S16_MIN) {
- 			cmd.thresholds[idx++] =
--				cpu_to_le16((s16)(mvm->tz_device.trips[i].temperature / 1000));
-+				cpu_to_le16(mvm->tz_device.temp_trips[i]);
- 		}
- 	}
- 	cmd.num_temps = cpu_to_le32(idx);
-@@ -593,8 +593,8 @@ int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm)
- 	 */
- 	for (i = 0; i < idx; i++) {
- 		for (j = 0; j < IWL_MAX_DTS_TRIPS; j++) {
--			if ((int)(le16_to_cpu(cmd.thresholds[i]) * 1000) ==
--			    mvm->tz_device.trips[j].temperature)
-+			if (le16_to_cpu(cmd.thresholds[i]) ==
-+				mvm->tz_device.temp_trips[j])
- 				mvm->tz_device.fw_trips_index[i] = j;
- 		}
- 	}
-@@ -665,6 +665,8 @@ out:
- 
- static  struct thermal_zone_device_ops tzone_ops = {
- 	.get_temp = iwl_mvm_tzone_get_temp,
-+	.get_trip_temp = iwl_mvm_tzone_get_trip_temp,
-+	.get_trip_type = iwl_mvm_tzone_get_trip_type,
- 	.set_trip_temp = iwl_mvm_tzone_set_trip_temp,
- };
- 
-@@ -686,8 +688,7 @@ static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
- 	BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
- 
- 	sprintf(name, "iwlwifi_%u", atomic_inc_return(&counter) & 0xFF);
--	mvm->tz_device.tzone = thermal_zone_device_register_with_trips(name,
--							mvm->tz_device.trips,
-+	mvm->tz_device.tzone = thermal_zone_device_register(name,
- 							IWL_MAX_DTS_TRIPS,
- 							IWL_WRITABLE_TRIPS_MSK,
- 							mvm, &tzone_ops,
-@@ -710,10 +711,8 @@ static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
- 	/* 0 is a valid temperature,
- 	 * so initialize the array with S16_MIN which invalid temperature
- 	 */
--	for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++) {
--		mvm->tz_device.trips[i].temperature = INT_MIN;
--		mvm->tz_device.trips[i].type = THERMAL_TRIP_PASSIVE;
--	}
-+	for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++)
-+		mvm->tz_device.temp_trips[i] = S16_MIN;
- }
- 
- static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev,
-diff --git a/drivers/staging/rtl8723bs/Kconfig b/drivers/staging/rtl8723bs/Kconfig
-index b51916c..b46ff98 100644
---- a/drivers/staging/rtl8723bs/Kconfig
-+++ b/drivers/staging/rtl8723bs/Kconfig
-@@ -5,7 +5,6 @@ config RTL8723BS
- 	depends on m
- 	depends on WLAN && MMC && CFG80211
- 	depends on m
--	select CFG80211_WEXT
- 	depends on CRYPTO
- 	select BPAUTO_CRYPTO_LIB_ARC4
- 	help
-diff --git a/kconf/Makefile b/kconf/Makefile
-index 2004c44..a2790b1 100644
---- a/kconf/Makefile
-+++ b/kconf/Makefile
-@@ -1,9 +1,9 @@
--CFLAGS=-Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer
-+CFLAGS=-Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -DKBUILD_NO_NLS
- 
- LXDIALOG := lxdialog/checklist.o lxdialog/inputbox.o lxdialog/menubox.o lxdialog/textbox.o lxdialog/util.o lxdialog/yesno.o
- 
- conf: conf.o zconf.tab.o
--mconf_CFLAGS := $(shell ./lxdialog/check-lxdialog.sh -ccflags) -DLOCALE
-+mconf_CFLAGS := $(shell ./lxdialog/check-lxdialog.sh -ccflags)
- mconf_LDFLAGS := $(shell ./lxdialog/check-lxdialog.sh -ldflags $(CC))
- mconf: CFLAGS += $(mconf_CFLAGS)
- 
-diff --git a/kconf/conf.c b/kconf/conf.c
-index 283eeed..1707f05 100644
---- a/kconf/conf.c
-+++ b/kconf/conf.c
-@@ -598,40 +598,12 @@ int main(int ac, char **av)
- 	case oldconfig:
- 	case listnewconfig:
- 	case olddefconfig:
--		conf_read(NULL);
--		break;
- 	case allnoconfig:
- 	case allyesconfig:
- 	case allmodconfig:
- 	case alldefconfig:
- 	case randconfig:
--		name = getenv("KCONFIG_ALLCONFIG");
--		if (!name)
--			break;
--		if ((strcmp(name, "") != 0) && (strcmp(name, "1") != 0)) {
--			if (conf_read_simple(name, S_DEF_USER)) {
--				fprintf(stderr,
--					_("*** Can't read seed configuration \"%s\"!\n"),
--					name);
--				exit(1);
--			}
--			break;
--		}
--		switch (input_mode) {
--		case allnoconfig:	name = "allno.config"; break;
--		case allyesconfig:	name = "allyes.config"; break;
--		case allmodconfig:	name = "allmod.config"; break;
--		case alldefconfig:	name = "alldef.config"; break;
--		case randconfig:	name = "allrandom.config"; break;
--		default: break;
--		}
--		if (conf_read_simple(name, S_DEF_USER) &&
--		    conf_read_simple("all.config", S_DEF_USER)) {
--			fprintf(stderr,
--				_("*** KCONFIG_ALLCONFIG set, but no \"%s\" or \"all.config\" file found\n"),
--				name);
--			exit(1);
--		}
-+		conf_read(NULL);
- 		break;
- 	default:
- 		break;
-diff --git a/kconf/confdata.c b/kconf/confdata.c
-index df26c7b..1038c30 100644
---- a/kconf/confdata.c
-+++ b/kconf/confdata.c
-@@ -1170,6 +1170,8 @@ bool conf_set_all_new_symbols(enum conf_def_mode mode)
- 	}
- 	bool has_changed = false;
- 
-+	sym_clear_all_valid();
-+
- 	for_all_symbols(i, sym) {
- 		if (sym_has_value(sym) || (sym->flags & SYMBOL_VALID))
- 			continue;
-@@ -1213,8 +1215,6 @@ bool conf_set_all_new_symbols(enum conf_def_mode mode)
- 
- 	}
- 
--	sym_clear_all_valid();
--
- 	/*
- 	 * We have different type of choice blocks.
- 	 * If curr.tri equals to mod then we can select several
-diff --git a/local-symbols b/local-symbols
-index 078fe8b..0743888 100644
---- a/local-symbols
-+++ b/local-symbols
-@@ -67,14 +67,6 @@ MAC80211_MESH_PS_DEBUG=
- MAC80211_TDLS_DEBUG=
- MAC80211_DEBUG_COUNTERS=
- MAC80211_STA_HASH_MAX_SIZE=
--QRTR=
--QRTR_SMD=
--QRTR_TUN=
--QRTR_MHI=
--MHI_BUS=
--MHI_BUS_DEBUG=
--MHI_BUS_PCI_GENERIC=
--MHI_BUS_EP=
- QCOM_AOSS_QMP=
- QCOM_COMMAND_DB=
- QCOM_GENI_SE=
-@@ -472,43 +464,6 @@ USB_VL600=
- USB_NET_CH9200=
- USB_NET_AQC111=
- USB_RTL8153_ECM=
--SSB_POSSIBLE=
--SSB=
--SSB_SPROM=
--SSB_BLOCKIO=
--SSB_PCIHOST_POSSIBLE=
--SSB_PCIHOST=
--SSB_B43_PCI_BRIDGE=
--SSB_PCMCIAHOST_POSSIBLE=
--SSB_PCMCIAHOST=
--SSB_SDIOHOST_POSSIBLE=
--SSB_SDIOHOST=
--SSB_HOST_SOC=
--SSB_SERIAL=
--SSB_DRIVER_PCICORE_POSSIBLE=
--SSB_DRIVER_PCICORE=
--SSB_PCICORE_HOSTMODE=
--SSB_DRIVER_MIPS=
--SSB_SFLASH=
--SSB_EMBEDDED=
--SSB_DRIVER_EXTIF=
--SSB_DRIVER_GIGE=
--SSB_DRIVER_GPIO=
--BCMA_POSSIBLE=
--BCMA=
--BCMA_BLOCKIO=
--BCMA_HOST_PCI_POSSIBLE=
--BCMA_HOST_PCI=
--BCMA_HOST_SOC=
--BCMA_DRIVER_PCI=
--BCMA_DRIVER_PCI_HOSTMODE=
--BCMA_DRIVER_MIPS=
--BCMA_PFLASH=
--BCMA_SFLASH=
--BCMA_NFLASH=
--BCMA_DRIVER_GMAC_CMN=
--BCMA_DRIVER_GPIO=
--BCMA_DEBUG=
- USB_ACM=
- USB_PRINTER=
- USB_WDM=
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0002-backports-sync-openwrt-patches-ath.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0002-backports-sync-openwrt-patches-ath.patch
new file mode 100644
index 0000000..2995c8e
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0002-backports-sync-openwrt-patches-ath.patch
@@ -0,0 +1,377 @@
+From bcafe92bd89916905cdff549d4919429b845ccba Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 17 Jul 2024 20:40:58 +0800
+Subject: [PATCH 02/89] backports: sync openwrt patches/ath
+
+---
+ drivers/net/wireless/ath/Kconfig       |  5 +-
+ drivers/net/wireless/ath/Makefile      |  2 +-
+ drivers/net/wireless/ath/ath.h         |  7 ---
+ drivers/net/wireless/ath/ath5k/pci.c   | 26 +++++++++-
+ drivers/net/wireless/ath/regd.c        | 72 ++++++++++++++++++--------
+ drivers/net/wireless/ath/regd_common.h |  3 ++
+ local-symbols                          |  1 +
+ net/wireless/reg.c                     |  3 ++
+ 8 files changed, 88 insertions(+), 31 deletions(-)
+
+diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig
+index 9ef8d46..bd576b4 100644
+--- a/drivers/net/wireless/ath/Kconfig
++++ b/drivers/net/wireless/ath/Kconfig
+@@ -1,6 +1,6 @@
+ # SPDX-License-Identifier: ISC
+ config ATH_COMMON
+-	tristate
++	tristate "ath.ko"
+ 	depends on m
+ 
+ config WLAN_VENDOR_ATH
+@@ -24,6 +24,9 @@ config WLAN_VENDOR_ATH
+ 
+ if WLAN_VENDOR_ATH
+ 
++config ATH_USER_REGD
++	bool "Do not enforce EEPROM regulatory restrictions"
++
+ config ATH_DEBUG
+ 	bool "Atheros wireless debugging"
+ 	help
+diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile
+index 7432d87..adea25c 100644
+--- a/drivers/net/wireless/ath/Makefile
++++ b/drivers/net/wireless/ath/Makefile
+@@ -16,10 +16,10 @@ ath-objs :=	main.o \
+ 		regd.o \
+ 		hw.o \
+ 		key.o \
++		debug.o \
+ 		dfs_pattern_detector.o \
+ 		dfs_pri_detector.o
+ 
+-ath-$(CPTCFG_ATH_DEBUG) += debug.o
+ ath-$(CPTCFG_ATH_TRACEPOINTS) += trace.o
+ 
+ CFLAGS_trace.o := -I$(src)
+diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
+index 1bfd992..eb63efd 100644
+--- a/drivers/net/wireless/ath/ath.h
++++ b/drivers/net/wireless/ath/ath.h
+@@ -321,14 +321,7 @@ void _ath_dbg(struct ath_common *common, enum ATH_DEBUG dbg_mask,
+ #endif /* CPTCFG_ATH_DEBUG */
+ 
+ /** Returns string describing opmode, or NULL if unknown mode. */
+-#ifdef CPTCFG_ATH_DEBUG
+ const char *ath_opmode_to_string(enum nl80211_iftype opmode);
+-#else
+-static inline const char *ath_opmode_to_string(enum nl80211_iftype opmode)
+-{
+-	return "UNKNOWN";
+-}
+-#endif
+ 
+ extern const char *ath_bus_type_strings[];
+ static inline const char *ath_bus_type_to_string(enum ath_bus_type bustype)
+diff --git a/drivers/net/wireless/ath/ath5k/pci.c b/drivers/net/wireless/ath/ath5k/pci.c
+index b51fce5..c4315dd 100644
+--- a/drivers/net/wireless/ath/ath5k/pci.c
++++ b/drivers/net/wireless/ath/ath5k/pci.c
+@@ -20,6 +20,7 @@
+ #include <linux/pci.h>
+ #include <linux/etherdevice.h>
+ #include <linux/module.h>
++#include <linux/ath5k_platform.h>
+ #include "../ath.h"
+ #include "ath5k.h"
+ #include "debug.h"
+@@ -71,7 +72,7 @@ static void ath5k_pci_read_cachesize(struct ath_common *common, int *csz)
+ }
+ 
+ /*
+- * Read from eeprom
++ * Read from eeprom or platform_data
+  */
+ static bool
+ ath5k_pci_eeprom_read(struct ath_common *common, u32 offset, u16 *data)
+@@ -79,6 +80,19 @@ ath5k_pci_eeprom_read(struct ath_common *common, u32 offset, u16 *data)
+ 	struct ath5k_hw *ah = common->ah;
+ 	u32 status, timeout;
+ 
++	struct ath5k_platform_data *pdata = NULL;
++
++	if (ah->pdev)
++		pdata = ah->pdev->dev.platform_data;
++
++	if (pdata && pdata->eeprom_data && pdata->eeprom_data[61] == AR5K_EEPROM_MAGIC_VALUE) {
++		if (offset >= ATH5K_PLAT_EEP_MAX_WORDS)
++			return false;
++
++		*data = pdata->eeprom_data[offset];
++		return true;
++	}
++
+ 	/*
+ 	 * Initialize EEPROM access
+ 	 */
+@@ -122,6 +136,16 @@ static int ath5k_pci_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac)
+ 	u16 data;
+ 	int octet;
+ 
++	struct ath5k_platform_data *pdata = NULL;
++
++	if (ah->pdev)
++		pdata = ah->pdev->dev.platform_data;
++
++	if (pdata && pdata->macaddr) {
++		memcpy(mac, pdata->macaddr, ETH_ALEN);
++		return 0;
++	}
++
+ 	AR5K_EEPROM_READ(0x20, data);
+ 
+ 	for (offset = 0x1f, octet = 0, total = 0; offset >= 0x1d; offset--) {
+diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
+index 2afdebf..3ba9fc3 100644
+--- a/drivers/net/wireless/ath/regd.c
++++ b/drivers/net/wireless/ath/regd.c
+@@ -24,6 +24,7 @@
+ #include "regd_common.h"
+ 
+ static int __ath_regd_init(struct ath_regulatory *reg);
++static struct reg_dmn_pair_mapping *ath_get_regpair(int regdmn);
+ 
+ /*
+  * This is a set of common rules used by our world regulatory domains.
+@@ -43,7 +44,8 @@ static int __ath_regd_init(struct ath_regulatory *reg);
+ 					 NL80211_RRF_NO_OFDM)
+ 
+ /* We allow IBSS on these on a case by case basis by regulatory domain */
+-#define ATH_5GHZ_5150_5350	REG_RULE(5150-10, 5350+10, 80, 0, 30,\
++#define ATH_5GHZ_5150_5350	REG_RULE(5150-10, 5240+10, 80, 0, 30, 0),\
++				REG_RULE(5260-10, 5350+10, 80, 0, 30,\
+ 					 NL80211_RRF_NO_IR)
+ #define ATH_5GHZ_5470_5850	REG_RULE(5470-10, 5850+10, 80, 0, 30,\
+ 					 NL80211_RRF_NO_IR)
+@@ -61,64 +63,79 @@ static int __ath_regd_init(struct ath_regulatory *reg);
+ #define ATH_5GHZ_NO_MIDBAND	ATH_5GHZ_5150_5350, \
+ 				ATH_5GHZ_5725_5850
+ 
++#define REGD_RULES(...) \
++	.reg_rules = { __VA_ARGS__ }, \
++	.n_reg_rules = ARRAY_SIZE(((struct ieee80211_reg_rule[]) { __VA_ARGS__ }))
++
+ /* Can be used for:
+  * 0x60, 0x61, 0x62 */
+ static const struct ieee80211_regdomain ath_world_regdom_60_61_62 = {
+-	.n_reg_rules = 5,
+ 	.alpha2 =  "99",
+-	.reg_rules = {
++	REGD_RULES(
+ 		ATH_2GHZ_ALL,
+ 		ATH_5GHZ_ALL,
+-	}
++	)
+ };
+ 
+ /* Can be used by 0x63 and 0x65 */
+ static const struct ieee80211_regdomain ath_world_regdom_63_65 = {
+-	.n_reg_rules = 4,
+ 	.alpha2 =  "99",
+-	.reg_rules = {
++	REGD_RULES(
+ 		ATH_2GHZ_CH01_11,
+ 		ATH_2GHZ_CH12_13,
+ 		ATH_5GHZ_NO_MIDBAND,
+-	}
++	)
+ };
+ 
+ /* Can be used by 0x64 only */
+ static const struct ieee80211_regdomain ath_world_regdom_64 = {
+-	.n_reg_rules = 3,
+ 	.alpha2 =  "99",
+-	.reg_rules = {
++	REGD_RULES(
+ 		ATH_2GHZ_CH01_11,
+ 		ATH_5GHZ_NO_MIDBAND,
+-	}
++	)
+ };
+ 
+ /* Can be used by 0x66 and 0x69 */
+ static const struct ieee80211_regdomain ath_world_regdom_66_69 = {
+-	.n_reg_rules = 3,
+ 	.alpha2 =  "99",
+-	.reg_rules = {
++	REGD_RULES(
+ 		ATH_2GHZ_CH01_11,
+ 		ATH_5GHZ_ALL,
+-	}
++	)
+ };
+ 
+ /* Can be used by 0x67, 0x68, 0x6A and 0x6C */
+ static const struct ieee80211_regdomain ath_world_regdom_67_68_6A_6C = {
+-	.n_reg_rules = 4,
+ 	.alpha2 =  "99",
+-	.reg_rules = {
++	REGD_RULES(
+ 		ATH_2GHZ_CH01_11,
+ 		ATH_2GHZ_CH12_13,
+ 		ATH_5GHZ_ALL,
+-	}
++	)
+ };
+ 
++static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg)
++{
++	return reg->current_rd & ~WORLDWIDE_ROAMING_FLAG;
++}
++
++static bool is_default_regd(struct ath_regulatory *reg)
++{
++	return ath_regd_get_eepromRD(reg) == CTRY_DEFAULT;
++}
++
+ static bool dynamic_country_user_possible(struct ath_regulatory *reg)
+ {
++	if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
++		return true;
++
+ 	if (IS_ENABLED(CPTCFG_ATH_REG_DYNAMIC_USER_CERT_TESTING))
+ 		return true;
+ 
++	if (is_default_regd(reg))
++		return true;
++
+ 	switch (reg->country_code) {
+ 	case CTRY_UNITED_STATES:
+ 	case CTRY_JAPAN1:
+@@ -188,6 +205,8 @@ static bool dynamic_country_user_possible(struct ath_regulatory *reg)
+ 
+ static bool ath_reg_dyn_country_user_allow(struct ath_regulatory *reg)
+ {
++	if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
++		return true;
+ 	if (!IS_ENABLED(CPTCFG_ATH_REG_DYNAMIC_USER_REG_HINTS))
+ 		return false;
+ 	if (!dynamic_country_user_possible(reg))
+@@ -202,11 +221,6 @@ static inline bool is_wwr_sku(u16 regd)
+ 		(regd == WORLD));
+ }
+ 
+-static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg)
+-{
+-	return reg->current_rd & ~WORLDWIDE_ROAMING_FLAG;
+-}
+-
+ bool ath_is_world_regd(struct ath_regulatory *reg)
+ {
+ 	return is_wwr_sku(ath_regd_get_eepromRD(reg));
+@@ -345,6 +359,9 @@ ath_reg_apply_beaconing_flags(struct wiphy *wiphy,
+ 	struct ieee80211_channel *ch;
+ 	unsigned int i;
+ 
++	if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
++		return;
++
+ 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ 		if (!wiphy->bands[band])
+ 			continue;
+@@ -379,6 +396,9 @@ ath_reg_apply_ir_flags(struct wiphy *wiphy,
+ {
+ 	struct ieee80211_supported_band *sband;
+ 
++	if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
++		return;
++
+ 	sband = wiphy->bands[NL80211_BAND_2GHZ];
+ 	if (!sband)
+ 		return;
+@@ -408,6 +428,9 @@ static void ath_reg_apply_radar_flags(struct wiphy *wiphy,
+ 	struct ieee80211_channel *ch;
+ 	unsigned int i;
+ 
++	if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
++		return;
++
+ 	if (!wiphy->bands[NL80211_BAND_5GHZ])
+ 		return;
+ 
+@@ -640,6 +663,13 @@ ath_regd_init_wiphy(struct ath_regulatory *reg,
+ 	const struct ieee80211_regdomain *regd;
+ 
+ 	wiphy->reg_notifier = reg_notifier;
++
++	if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
++		return 0;
++
++	if (is_default_regd(reg))
++		return 0;
++
+ 	wiphy->regulatory_flags |= REGULATORY_STRICT_REG |
+ 				   REGULATORY_CUSTOM_REG;
+ 
+diff --git a/drivers/net/wireless/ath/regd_common.h b/drivers/net/wireless/ath/regd_common.h
+index cdb1e9a..2574f80 100644
+--- a/drivers/net/wireless/ath/regd_common.h
++++ b/drivers/net/wireless/ath/regd_common.h
+@@ -32,6 +32,7 @@ enum EnumRd {
+ 	FCC2_WORLD = 0x21,
+ 	FCC2_ETSIC = 0x22,
+ 	FCC6_WORLD = 0x23,
++	FCC3_FCCA_2 = 0x2A,
+ 	FRANCE_RES = 0x31,
+ 	FCC3_FCCA = 0x3A,
+ 	FCC3_WORLD = 0x3B,
+@@ -173,6 +174,7 @@ static struct reg_dmn_pair_mapping regDomainPairs[] = {
+ 	{FCC2_WORLD, CTL_FCC, CTL_ETSI},
+ 	{FCC2_ETSIC, CTL_FCC, CTL_ETSI},
+ 	{FCC3_FCCA, CTL_FCC, CTL_FCC},
++	{FCC3_FCCA_2, CTL_FCC, CTL_FCC},
+ 	{FCC3_WORLD, CTL_FCC, CTL_ETSI},
+ 	{FCC3_ETSIC, CTL_FCC, CTL_ETSI},
+ 	{FCC4_FCCA, CTL_FCC, CTL_FCC},
+@@ -486,6 +488,7 @@ static struct country_code_to_enum_rd allCountries[] = {
+ 	{CTRY_UAE, NULL1_WORLD, "AE"},
+ 	{CTRY_UNITED_KINGDOM, ETSI1_WORLD, "GB"},
+ 	{CTRY_UNITED_STATES, FCC3_FCCA, "US"},
++	{CTRY_UNITED_STATES, FCC3_FCCA_2, "US"},
+ 	{CTRY_UNITED_STATES2, FCC3_FCCA, "US"},
+ 	{CTRY_UNITED_STATES3, FCC3_FCCA, "US"},
+ 	/* This "PS" is for US public safety actually... to support this we
+diff --git a/local-symbols b/local-symbols
+index d7653ac..3421d93 100644
+--- a/local-symbols
++++ b/local-symbols
+@@ -94,6 +94,7 @@ ADM8211=
+ ATH_COMMON=
+ WLAN_VENDOR_ATH=
+ ATH_DEBUG=
++ATH_USER_REGD=
+ ATH_TRACEPOINTS=
+ ATH_REG_DYNAMIC_USER_REG_HINTS=
+ ATH_REG_DYNAMIC_USER_CERT_TESTING=
+diff --git a/net/wireless/reg.c b/net/wireless/reg.c
+index 1accfff..4219dc2 100644
+--- a/net/wireless/reg.c
++++ b/net/wireless/reg.c
+@@ -3365,6 +3365,8 @@ void regulatory_hint_country_ie(struct wiphy *wiphy, enum nl80211_band band,
+ 	enum environment_cap env = ENVIRON_ANY;
+ 	struct regulatory_request *request = NULL, *lr;
+ 
++	return;
++
+ 	/* IE len must be evenly divisible by 2 */
+ 	if (country_ie_len & 0x01)
+ 		return;
+@@ -3616,6 +3618,7 @@ static bool is_wiphy_all_set_reg_flag(enum ieee80211_regulatory_flags flag)
+ 
+ void regulatory_hint_disconnect(void)
+ {
++	return;
+ 	/* Restore of regulatory settings is not required when wiphy(s)
+ 	 * ignore IE from connected access point but clearance of beacon hints
+ 	 * is required when wiphy(s) supports beacon hints.
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0002-sync-backports-patches-ath.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0002-sync-backports-patches-ath.patch
deleted file mode 100644
index f47e6f5..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0002-sync-backports-patches-ath.patch
+++ /dev/null
@@ -1,482 +0,0 @@
-From 429745cbf1435d31a656d4d912b7ea1cdd271be2 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Tue, 12 Mar 2024 11:26:07 +0800
-Subject: [PATCH 02/61] sync backports patches/ath
-
----
- drivers/net/wireless/ath/Kconfig              |  5 +-
- drivers/net/wireless/ath/Makefile             |  2 +-
- drivers/net/wireless/ath/ath.h                | 17 ++---
- drivers/net/wireless/ath/ath5k/ani.c          |  2 +-
- drivers/net/wireless/ath/ath5k/base.c         |  4 +-
- drivers/net/wireless/ath/ath5k/mac80211-ops.c |  2 +-
- drivers/net/wireless/ath/ath5k/pci.c          | 26 ++++++-
- drivers/net/wireless/ath/ath9k/link.c         |  2 +-
- drivers/net/wireless/ath/ath9k/main.c         |  4 +-
- drivers/net/wireless/ath/hw.c                 |  2 +-
- drivers/net/wireless/ath/regd.c               | 72 +++++++++++++------
- drivers/net/wireless/ath/regd_common.h        |  3 +
- local-symbols                                 |  1 +
- net/wireless/reg.c                            |  3 +
- 14 files changed, 102 insertions(+), 43 deletions(-)
-
-diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig
-index 9ef8d46..bd576b4 100644
---- a/drivers/net/wireless/ath/Kconfig
-+++ b/drivers/net/wireless/ath/Kconfig
-@@ -1,6 +1,6 @@
- # SPDX-License-Identifier: ISC
- config ATH_COMMON
--	tristate
-+	tristate "ath.ko"
- 	depends on m
- 
- config WLAN_VENDOR_ATH
-@@ -24,6 +24,9 @@ config WLAN_VENDOR_ATH
- 
- if WLAN_VENDOR_ATH
- 
-+config ATH_USER_REGD
-+	bool "Do not enforce EEPROM regulatory restrictions"
-+
- config ATH_DEBUG
- 	bool "Atheros wireless debugging"
- 	help
-diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile
-index 7432d87..adea25c 100644
---- a/drivers/net/wireless/ath/Makefile
-+++ b/drivers/net/wireless/ath/Makefile
-@@ -16,10 +16,10 @@ ath-objs :=	main.o \
- 		regd.o \
- 		hw.o \
- 		key.o \
-+		debug.o \
- 		dfs_pattern_detector.o \
- 		dfs_pri_detector.o
- 
--ath-$(CPTCFG_ATH_DEBUG) += debug.o
- ath-$(CPTCFG_ATH_TRACEPOINTS) += trace.o
- 
- CFLAGS_trace.o := -I$(src)
-diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
-index e9d95c7..888c5e2 100644
---- a/drivers/net/wireless/ath/ath.h
-+++ b/drivers/net/wireless/ath/ath.h
-@@ -43,10 +43,12 @@ struct ath_ani {
- };
- 
- struct ath_cycle_counters {
--	u32 cycles;
--	u32 rx_busy;
--	u32 rx_frame;
--	u32 tx_frame;
-+	struct_group(cnts,
-+		u32 cycles;
-+		u32 rx_busy;
-+		u32 rx_frame;
-+		u32 tx_frame;
-+	);
- };
- 
- enum ath_device_state {
-@@ -319,14 +321,7 @@ void _ath_dbg(struct ath_common *common, enum ATH_DEBUG dbg_mask,
- #endif /* CPTCFG_ATH_DEBUG */
- 
- /** Returns string describing opmode, or NULL if unknown mode. */
--#ifdef CPTCFG_ATH_DEBUG
- const char *ath_opmode_to_string(enum nl80211_iftype opmode);
--#else
--static inline const char *ath_opmode_to_string(enum nl80211_iftype opmode)
--{
--	return "UNKNOWN";
--}
--#endif
- 
- extern const char *ath_bus_type_strings[];
- static inline const char *ath_bus_type_to_string(enum ath_bus_type bustype)
-diff --git a/drivers/net/wireless/ath/ath5k/ani.c b/drivers/net/wireless/ath/ath5k/ani.c
-index 11fd1ca..897cfd9 100644
---- a/drivers/net/wireless/ath/ath5k/ani.c
-+++ b/drivers/net/wireless/ath/ath5k/ani.c
-@@ -379,7 +379,7 @@ ath5k_hw_ani_get_listen_time(struct ath5k_hw *ah, struct ath5k_ani_state *as)
- 	spin_lock_bh(&common->cc_lock);
- 
- 	ath_hw_cycle_counters_update(common);
--	memcpy(&as->last_cc, &common->cc_ani, sizeof(as->last_cc));
-+	memcpy(&as->last_cc.cnts, &common->cc_ani.cnts, sizeof(as->last_cc.cnts));
- 
- 	/* clears common->cc_ani */
- 	listen = ath_hw_get_listen_time(common);
-diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
-index 7ec5d9a..bad83fd 100644
---- a/drivers/net/wireless/ath/ath5k/base.c
-+++ b/drivers/net/wireless/ath/ath5k/base.c
-@@ -2985,8 +2985,8 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan,
- 	memset(&ah->survey, 0, sizeof(ah->survey));
- 	spin_lock_bh(&common->cc_lock);
- 	ath_hw_cycle_counters_update(common);
--	memset(&common->cc_survey, 0, sizeof(common->cc_survey));
--	memset(&common->cc_ani, 0, sizeof(common->cc_ani));
-+	memset(&common->cc_survey.cnts, 0, sizeof(common->cc_survey.cnts));
-+	memset(&common->cc_ani.cnts, 0, sizeof(common->cc_ani.cnts));
- 	spin_unlock_bh(&common->cc_lock);
- 
- 	/*
-diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
-index eea4bda..da9dc62 100644
---- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
-+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
-@@ -664,7 +664,7 @@ ath5k_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey)
- 		ah->survey.time_rx += cc->rx_frame / div;
- 		ah->survey.time_tx += cc->tx_frame / div;
- 	}
--	memset(cc, 0, sizeof(*cc));
-+	memset(&cc->cnts, 0, sizeof(cc->cnts));
- 	spin_unlock_bh(&common->cc_lock);
- 
- 	memcpy(survey, &ah->survey, sizeof(*survey));
-diff --git a/drivers/net/wireless/ath/ath5k/pci.c b/drivers/net/wireless/ath/ath5k/pci.c
-index b51fce5..c4315dd 100644
---- a/drivers/net/wireless/ath/ath5k/pci.c
-+++ b/drivers/net/wireless/ath/ath5k/pci.c
-@@ -20,6 +20,7 @@
- #include <linux/pci.h>
- #include <linux/etherdevice.h>
- #include <linux/module.h>
-+#include <linux/ath5k_platform.h>
- #include "../ath.h"
- #include "ath5k.h"
- #include "debug.h"
-@@ -71,7 +72,7 @@ static void ath5k_pci_read_cachesize(struct ath_common *common, int *csz)
- }
- 
- /*
-- * Read from eeprom
-+ * Read from eeprom or platform_data
-  */
- static bool
- ath5k_pci_eeprom_read(struct ath_common *common, u32 offset, u16 *data)
-@@ -79,6 +80,19 @@ ath5k_pci_eeprom_read(struct ath_common *common, u32 offset, u16 *data)
- 	struct ath5k_hw *ah = common->ah;
- 	u32 status, timeout;
- 
-+	struct ath5k_platform_data *pdata = NULL;
-+
-+	if (ah->pdev)
-+		pdata = ah->pdev->dev.platform_data;
-+
-+	if (pdata && pdata->eeprom_data && pdata->eeprom_data[61] == AR5K_EEPROM_MAGIC_VALUE) {
-+		if (offset >= ATH5K_PLAT_EEP_MAX_WORDS)
-+			return false;
-+
-+		*data = pdata->eeprom_data[offset];
-+		return true;
-+	}
-+
- 	/*
- 	 * Initialize EEPROM access
- 	 */
-@@ -122,6 +136,16 @@ static int ath5k_pci_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac)
- 	u16 data;
- 	int octet;
- 
-+	struct ath5k_platform_data *pdata = NULL;
-+
-+	if (ah->pdev)
-+		pdata = ah->pdev->dev.platform_data;
-+
-+	if (pdata && pdata->macaddr) {
-+		memcpy(mac, pdata->macaddr, ETH_ALEN);
-+		return 0;
-+	}
-+
- 	AR5K_EEPROM_READ(0x20, data);
- 
- 	for (offset = 0x1f, octet = 0, total = 0; offset >= 0x1d; offset--) {
-diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c
-index d1e5767..7f18d71 100644
---- a/drivers/net/wireless/ath/ath9k/link.c
-+++ b/drivers/net/wireless/ath/ath9k/link.c
-@@ -536,7 +536,7 @@ int ath_update_survey_stats(struct ath_softc *sc)
- 	if (cc->cycles > 0)
- 		ret = cc->rx_busy * 100 / cc->cycles;
- 
--	memset(cc, 0, sizeof(*cc));
-+	memset(&cc->cnts, 0, sizeof(cc->cnts));
- 
- 	ath_update_survey_nf(sc, pos);
- 
-diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
-index 34d11b0..6d120df 100644
---- a/drivers/net/wireless/ath/ath9k/main.c
-+++ b/drivers/net/wireless/ath/ath9k/main.c
-@@ -135,8 +135,8 @@ void ath9k_ps_wakeup(struct ath_softc *sc)
- 	if (power_mode != ATH9K_PM_AWAKE) {
- 		spin_lock(&common->cc_lock);
- 		ath_hw_cycle_counters_update(common);
--		memset(&common->cc_survey, 0, sizeof(common->cc_survey));
--		memset(&common->cc_ani, 0, sizeof(common->cc_ani));
-+		memset(&common->cc_survey.cnts, 0, sizeof(common->cc_survey.cnts));
-+		memset(&common->cc_ani.cnts, 0, sizeof(common->cc_ani.cnts));
- 		spin_unlock(&common->cc_lock);
- 	}
- 
-diff --git a/drivers/net/wireless/ath/hw.c b/drivers/net/wireless/ath/hw.c
-index 8595557..be8bfed 100644
---- a/drivers/net/wireless/ath/hw.c
-+++ b/drivers/net/wireless/ath/hw.c
-@@ -183,7 +183,7 @@ int32_t ath_hw_get_listen_time(struct ath_common *common)
- 	listen_time = (cc->cycles - cc->rx_frame - cc->tx_frame) /
- 		      (common->clockrate * 1000);
- 
--	memset(cc, 0, sizeof(*cc));
-+	memset(&cc->cnts, 0, sizeof(cc->cnts));
- 
- 	return listen_time;
- }
-diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
-index 2afdebf..3ba9fc3 100644
---- a/drivers/net/wireless/ath/regd.c
-+++ b/drivers/net/wireless/ath/regd.c
-@@ -24,6 +24,7 @@
- #include "regd_common.h"
- 
- static int __ath_regd_init(struct ath_regulatory *reg);
-+static struct reg_dmn_pair_mapping *ath_get_regpair(int regdmn);
- 
- /*
-  * This is a set of common rules used by our world regulatory domains.
-@@ -43,7 +44,8 @@ static int __ath_regd_init(struct ath_regulatory *reg);
- 					 NL80211_RRF_NO_OFDM)
- 
- /* We allow IBSS on these on a case by case basis by regulatory domain */
--#define ATH_5GHZ_5150_5350	REG_RULE(5150-10, 5350+10, 80, 0, 30,\
-+#define ATH_5GHZ_5150_5350	REG_RULE(5150-10, 5240+10, 80, 0, 30, 0),\
-+				REG_RULE(5260-10, 5350+10, 80, 0, 30,\
- 					 NL80211_RRF_NO_IR)
- #define ATH_5GHZ_5470_5850	REG_RULE(5470-10, 5850+10, 80, 0, 30,\
- 					 NL80211_RRF_NO_IR)
-@@ -61,64 +63,79 @@ static int __ath_regd_init(struct ath_regulatory *reg);
- #define ATH_5GHZ_NO_MIDBAND	ATH_5GHZ_5150_5350, \
- 				ATH_5GHZ_5725_5850
- 
-+#define REGD_RULES(...) \
-+	.reg_rules = { __VA_ARGS__ }, \
-+	.n_reg_rules = ARRAY_SIZE(((struct ieee80211_reg_rule[]) { __VA_ARGS__ }))
-+
- /* Can be used for:
-  * 0x60, 0x61, 0x62 */
- static const struct ieee80211_regdomain ath_world_regdom_60_61_62 = {
--	.n_reg_rules = 5,
- 	.alpha2 =  "99",
--	.reg_rules = {
-+	REGD_RULES(
- 		ATH_2GHZ_ALL,
- 		ATH_5GHZ_ALL,
--	}
-+	)
- };
- 
- /* Can be used by 0x63 and 0x65 */
- static const struct ieee80211_regdomain ath_world_regdom_63_65 = {
--	.n_reg_rules = 4,
- 	.alpha2 =  "99",
--	.reg_rules = {
-+	REGD_RULES(
- 		ATH_2GHZ_CH01_11,
- 		ATH_2GHZ_CH12_13,
- 		ATH_5GHZ_NO_MIDBAND,
--	}
-+	)
- };
- 
- /* Can be used by 0x64 only */
- static const struct ieee80211_regdomain ath_world_regdom_64 = {
--	.n_reg_rules = 3,
- 	.alpha2 =  "99",
--	.reg_rules = {
-+	REGD_RULES(
- 		ATH_2GHZ_CH01_11,
- 		ATH_5GHZ_NO_MIDBAND,
--	}
-+	)
- };
- 
- /* Can be used by 0x66 and 0x69 */
- static const struct ieee80211_regdomain ath_world_regdom_66_69 = {
--	.n_reg_rules = 3,
- 	.alpha2 =  "99",
--	.reg_rules = {
-+	REGD_RULES(
- 		ATH_2GHZ_CH01_11,
- 		ATH_5GHZ_ALL,
--	}
-+	)
- };
- 
- /* Can be used by 0x67, 0x68, 0x6A and 0x6C */
- static const struct ieee80211_regdomain ath_world_regdom_67_68_6A_6C = {
--	.n_reg_rules = 4,
- 	.alpha2 =  "99",
--	.reg_rules = {
-+	REGD_RULES(
- 		ATH_2GHZ_CH01_11,
- 		ATH_2GHZ_CH12_13,
- 		ATH_5GHZ_ALL,
--	}
-+	)
- };
- 
-+static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg)
-+{
-+	return reg->current_rd & ~WORLDWIDE_ROAMING_FLAG;
-+}
-+
-+static bool is_default_regd(struct ath_regulatory *reg)
-+{
-+	return ath_regd_get_eepromRD(reg) == CTRY_DEFAULT;
-+}
-+
- static bool dynamic_country_user_possible(struct ath_regulatory *reg)
- {
-+	if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
-+		return true;
-+
- 	if (IS_ENABLED(CPTCFG_ATH_REG_DYNAMIC_USER_CERT_TESTING))
- 		return true;
- 
-+	if (is_default_regd(reg))
-+		return true;
-+
- 	switch (reg->country_code) {
- 	case CTRY_UNITED_STATES:
- 	case CTRY_JAPAN1:
-@@ -188,6 +205,8 @@ static bool dynamic_country_user_possible(struct ath_regulatory *reg)
- 
- static bool ath_reg_dyn_country_user_allow(struct ath_regulatory *reg)
- {
-+	if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
-+		return true;
- 	if (!IS_ENABLED(CPTCFG_ATH_REG_DYNAMIC_USER_REG_HINTS))
- 		return false;
- 	if (!dynamic_country_user_possible(reg))
-@@ -202,11 +221,6 @@ static inline bool is_wwr_sku(u16 regd)
- 		(regd == WORLD));
- }
- 
--static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg)
--{
--	return reg->current_rd & ~WORLDWIDE_ROAMING_FLAG;
--}
--
- bool ath_is_world_regd(struct ath_regulatory *reg)
- {
- 	return is_wwr_sku(ath_regd_get_eepromRD(reg));
-@@ -345,6 +359,9 @@ ath_reg_apply_beaconing_flags(struct wiphy *wiphy,
- 	struct ieee80211_channel *ch;
- 	unsigned int i;
- 
-+	if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
-+		return;
-+
- 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
- 		if (!wiphy->bands[band])
- 			continue;
-@@ -379,6 +396,9 @@ ath_reg_apply_ir_flags(struct wiphy *wiphy,
- {
- 	struct ieee80211_supported_band *sband;
- 
-+	if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
-+		return;
-+
- 	sband = wiphy->bands[NL80211_BAND_2GHZ];
- 	if (!sband)
- 		return;
-@@ -408,6 +428,9 @@ static void ath_reg_apply_radar_flags(struct wiphy *wiphy,
- 	struct ieee80211_channel *ch;
- 	unsigned int i;
- 
-+	if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
-+		return;
-+
- 	if (!wiphy->bands[NL80211_BAND_5GHZ])
- 		return;
- 
-@@ -640,6 +663,13 @@ ath_regd_init_wiphy(struct ath_regulatory *reg,
- 	const struct ieee80211_regdomain *regd;
- 
- 	wiphy->reg_notifier = reg_notifier;
-+
-+	if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
-+		return 0;
-+
-+	if (is_default_regd(reg))
-+		return 0;
-+
- 	wiphy->regulatory_flags |= REGULATORY_STRICT_REG |
- 				   REGULATORY_CUSTOM_REG;
- 
-diff --git a/drivers/net/wireless/ath/regd_common.h b/drivers/net/wireless/ath/regd_common.h
-index cdb1e9a..2574f80 100644
---- a/drivers/net/wireless/ath/regd_common.h
-+++ b/drivers/net/wireless/ath/regd_common.h
-@@ -32,6 +32,7 @@ enum EnumRd {
- 	FCC2_WORLD = 0x21,
- 	FCC2_ETSIC = 0x22,
- 	FCC6_WORLD = 0x23,
-+	FCC3_FCCA_2 = 0x2A,
- 	FRANCE_RES = 0x31,
- 	FCC3_FCCA = 0x3A,
- 	FCC3_WORLD = 0x3B,
-@@ -173,6 +174,7 @@ static struct reg_dmn_pair_mapping regDomainPairs[] = {
- 	{FCC2_WORLD, CTL_FCC, CTL_ETSI},
- 	{FCC2_ETSIC, CTL_FCC, CTL_ETSI},
- 	{FCC3_FCCA, CTL_FCC, CTL_FCC},
-+	{FCC3_FCCA_2, CTL_FCC, CTL_FCC},
- 	{FCC3_WORLD, CTL_FCC, CTL_ETSI},
- 	{FCC3_ETSIC, CTL_FCC, CTL_ETSI},
- 	{FCC4_FCCA, CTL_FCC, CTL_FCC},
-@@ -486,6 +488,7 @@ static struct country_code_to_enum_rd allCountries[] = {
- 	{CTRY_UAE, NULL1_WORLD, "AE"},
- 	{CTRY_UNITED_KINGDOM, ETSI1_WORLD, "GB"},
- 	{CTRY_UNITED_STATES, FCC3_FCCA, "US"},
-+	{CTRY_UNITED_STATES, FCC3_FCCA_2, "US"},
- 	{CTRY_UNITED_STATES2, FCC3_FCCA, "US"},
- 	{CTRY_UNITED_STATES3, FCC3_FCCA, "US"},
- 	/* This "PS" is for US public safety actually... to support this we
-diff --git a/local-symbols b/local-symbols
-index 0743888..b200b00 100644
---- a/local-symbols
-+++ b/local-symbols
-@@ -101,6 +101,7 @@ ADM8211=
- ATH_COMMON=
- WLAN_VENDOR_ATH=
- ATH_DEBUG=
-+ATH_USER_REGD=
- ATH_TRACEPOINTS=
- ATH_REG_DYNAMIC_USER_REG_HINTS=
- ATH_REG_DYNAMIC_USER_CERT_TESTING=
-diff --git a/net/wireless/reg.c b/net/wireless/reg.c
-index 233ebab..2c0c1f1 100644
---- a/net/wireless/reg.c
-+++ b/net/wireless/reg.c
-@@ -3365,6 +3365,8 @@ void regulatory_hint_country_ie(struct wiphy *wiphy, enum nl80211_band band,
- 	enum environment_cap env = ENVIRON_ANY;
- 	struct regulatory_request *request = NULL, *lr;
- 
-+	return;
-+
- 	/* IE len must be evenly divisible by 2 */
- 	if (country_ie_len & 0x01)
- 		return;
-@@ -3616,6 +3618,7 @@ static bool is_wiphy_all_set_reg_flag(enum ieee80211_regulatory_flags flag)
- 
- void regulatory_hint_disconnect(void)
- {
-+	return;
- 	/* Restore of regulatory settings is not required when wiphy(s)
- 	 * ignore IE from connected access point but clearance of beacon hints
- 	 * is required when wiphy(s) supports beacon hints.
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0003-backports-sync-openwrt-patches-ath5k.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0003-backports-sync-openwrt-patches-ath5k.patch
new file mode 100644
index 0000000..e233678
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0003-backports-sync-openwrt-patches-ath5k.patch
@@ -0,0 +1,315 @@
+From 3443489559c628356163e9bd4613152c1f6a4ffa Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 17 Jul 2024 20:41:10 +0800
+Subject: [PATCH 03/89] backports: sync openwrt patches/ath5k
+
+---
+ drivers/net/wireless/ath/ath5k/ath5k.h        |  1 +
+ drivers/net/wireless/ath/ath5k/base.c         |  8 +-
+ drivers/net/wireless/ath/ath5k/debug.c        | 93 +++++++++++++++++++
+ drivers/net/wireless/ath/ath5k/dma.c          |  8 ++
+ drivers/net/wireless/ath/ath5k/initvals.c     |  6 ++
+ drivers/net/wireless/ath/ath5k/mac80211-ops.c |  9 +-
+ drivers/net/wireless/ath/ath5k/pci.c          |  2 +
+ drivers/net/wireless/ath/ath5k/reset.c        |  2 +
+ include/linux/ath5k_platform.h                | 30 ++++++
+ 9 files changed, 150 insertions(+), 9 deletions(-)
+ create mode 100644 include/linux/ath5k_platform.h
+
+diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
+index 308a429..0e6d184 100644
+--- a/drivers/net/wireless/ath/ath5k/ath5k.h
++++ b/drivers/net/wireless/ath/ath5k/ath5k.h
+@@ -1372,6 +1372,7 @@ struct ath5k_hw {
+ 	u8			ah_coverage_class;
+ 	bool			ah_ack_bitrate_high;
+ 	u8			ah_bwmode;
++	u8			ah_bwmode_debug;
+ 	bool			ah_short_slot;
+ 
+ 	/* Antenna Control */
+diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
+index d7adbf2..1cfabd6 100644
+--- a/drivers/net/wireless/ath/ath5k/base.c
++++ b/drivers/net/wireless/ath/ath5k/base.c
+@@ -465,6 +465,9 @@ ath5k_chan_set(struct ath5k_hw *ah, struct cfg80211_chan_def *chandef)
+ 		return -EINVAL;
+ 	}
+ 
++	if (ah->ah_bwmode_debug != AR5K_BWMODE_DEFAULT)
++		ah->ah_bwmode = ah->ah_bwmode_debug;
++
+ 	/*
+ 	 * To switch channels clear any pending DMA operations;
+ 	 * wait long enough for the RX fifo to drain, reset the
+@@ -2009,7 +2012,7 @@ ath5k_beacon_send(struct ath5k_hw *ah)
+ 	}
+ 
+ 	if ((ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs +
+-			ah->num_mesh_vifs > 1) ||
++			ah->num_adhoc_vifs + ah->num_mesh_vifs > 1) ||
+ 			ah->opmode == NL80211_IFTYPE_MESH_POINT) {
+ 		u64 tsf = ath5k_hw_get_tsf64(ah);
+ 		u32 tsftu = TSF_TO_TU(tsf);
+@@ -2095,7 +2098,7 @@ ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf)
+ 
+ 	intval = ah->bintval & AR5K_BEACON_PERIOD;
+ 	if (ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs
+-		+ ah->num_mesh_vifs > 1) {
++		+ ah->num_adhoc_vifs + ah->num_mesh_vifs > 1) {
+ 		intval /= ATH_BCBUF;	/* staggered multi-bss beacons */
+ 		if (intval < 15)
+ 			ATH5K_WARN(ah, "intval %u is too low, min 15\n",
+@@ -2561,6 +2564,7 @@ static const struct ieee80211_iface_limit if_limits[] = {
+ 				 BIT(NL80211_IFTYPE_MESH_POINT) |
+ #endif
+ 				 BIT(NL80211_IFTYPE_AP) },
++	{ .max = 1,	.types = BIT(NL80211_IFTYPE_ADHOC) },
+ };
+ 
+ static const struct ieee80211_iface_combination if_comb = {
+diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c
+index ec13051..239d789 100644
+--- a/drivers/net/wireless/ath/ath5k/debug.c
++++ b/drivers/net/wireless/ath/ath5k/debug.c
+@@ -803,6 +803,97 @@ static const struct file_operations fops_ani = {
+ 	.llseek = default_llseek,
+ };
+ 
++/* debugfs: bwmode */
++
++static ssize_t read_file_bwmode(struct file *file, char __user *user_buf,
++				   size_t count, loff_t *ppos)
++{
++	struct ath5k_hw *ah = file->private_data;
++	char buf[15];
++	unsigned int len = 0;
++
++	int cur_ah_bwmode = ah->ah_bwmode_debug;
++
++#define print_selected(MODE, LABEL) \
++	if (cur_ah_bwmode == MODE) \
++		len += snprintf(buf+len, sizeof(buf)-len, "[%s]", LABEL); \
++	else \
++		len += snprintf(buf+len, sizeof(buf)-len, "%s", LABEL); \
++	len += snprintf(buf+len, sizeof(buf)-len, " ");
++
++	print_selected(AR5K_BWMODE_5MHZ, "5");
++	print_selected(AR5K_BWMODE_10MHZ, "10");
++	print_selected(AR5K_BWMODE_DEFAULT, "20");
++	print_selected(AR5K_BWMODE_40MHZ, "40");
++#undef print_selected
++
++	len += snprintf(buf+len, sizeof(buf)-len, "\n");
++
++	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
++}
++
++static ssize_t write_file_bwmode(struct file *file,
++				 const char __user *userbuf,
++				 size_t count, loff_t *ppos)
++{
++	struct ath5k_hw *ah = file->private_data;
++	char buf[3];
++	int bw = 20;
++	int tobwmode = AR5K_BWMODE_DEFAULT;
++
++	if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
++		return -EFAULT;
++
++	/* TODO: Add check for active interface */
++
++	if(strncmp(buf, "5", 1) == 0 ) {
++		tobwmode = AR5K_BWMODE_5MHZ;
++		bw = 5;
++	} else if ( strncmp(buf, "10", 2) == 0 ) {
++		tobwmode = AR5K_BWMODE_10MHZ;
++		bw = 10;
++	} else if ( strncmp(buf, "20", 2) == 0 ) {
++		tobwmode = AR5K_BWMODE_DEFAULT;
++		bw = 20;
++	} else if ( strncmp(buf, "40", 2) == 0 ) {
++		tobwmode = AR5K_BWMODE_40MHZ;
++		bw = 40;
++	} else
++		return -EINVAL;
++
++	ATH5K_INFO(ah, "Changing to %imhz channel width[%i]\n",
++		bw, tobwmode);
++
++	switch (ah->ah_radio) {
++	/* TODO: only define radios that actually support 5/10mhz channels */
++	case AR5K_RF5413:
++	case AR5K_RF5110:
++	case AR5K_RF5111:
++	case AR5K_RF5112:
++	case AR5K_RF2413:
++	case AR5K_RF2316:
++	case AR5K_RF2317:
++	case AR5K_RF2425:
++		if(ah->ah_bwmode_debug != tobwmode) {
++			mutex_lock(&ah->lock);
++			ah->ah_bwmode = tobwmode;
++			ah->ah_bwmode_debug = tobwmode;
++			mutex_unlock(&ah->lock);
++		}
++		break;
++	default:
++		return -EOPNOTSUPP;
++	}
++	return count;
++}
++
++static const struct file_operations fops_bwmode = {
++	.read = read_file_bwmode,
++	.write = write_file_bwmode,
++	.open = simple_open,
++	.owner = THIS_MODULE,
++	.llseek = default_llseek,
++};
+ 
+ /* debugfs: queues etc */
+ 
+@@ -995,6 +1086,8 @@ ath5k_debug_init_device(struct ath5k_hw *ah)
+ 	debugfs_create_file("queue", 0600, phydir, ah, &fops_queue);
+ 	debugfs_create_bool("32khz_clock", 0600, phydir,
+ 			    &ah->ah_use_32khz_clock);
++	debugfs_create_file("bwmode", S_IWUSR | S_IRUSR, phydir, ah,
++			    &fops_bwmode);
+ }
+ 
+ /* functions used in other places */
+diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c
+index d9e376e..db06ff8 100644
+--- a/drivers/net/wireless/ath/ath5k/dma.c
++++ b/drivers/net/wireless/ath/ath5k/dma.c
+@@ -854,10 +854,18 @@ ath5k_hw_dma_init(struct ath5k_hw *ah)
+ 	 * guess we can tweak it and see how it goes ;-)
+ 	 */
+ 	if (ah->ah_version != AR5K_AR5210) {
++#if !defined(CONFIG_ATHEROS_AR71XX) && !defined(CONFIG_ATH79)
+ 		AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG,
+ 			AR5K_TXCFG_SDMAMR, AR5K_DMASIZE_128B);
+ 		AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG,
+ 			AR5K_RXCFG_SDMAMW, AR5K_DMASIZE_128B);
++#else
++		/* WAR for AR71xx PCI bug */
++		AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG,
++			AR5K_TXCFG_SDMAMR, AR5K_DMASIZE_128B);
++		AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG,
++			AR5K_RXCFG_SDMAMW, AR5K_DMASIZE_4B);
++#endif
+ 	}
+ 
+ 	/* Pre-enable interrupts on 5211/5212*/
+diff --git a/drivers/net/wireless/ath/ath5k/initvals.c b/drivers/net/wireless/ath/ath5k/initvals.c
+index ee1c2fa..122fe1c 100644
+--- a/drivers/net/wireless/ath/ath5k/initvals.c
++++ b/drivers/net/wireless/ath/ath5k/initvals.c
+@@ -62,8 +62,14 @@ static const struct ath5k_ini ar5210_ini[] = {
+ 	{ AR5K_IMR,		0 },
+ 	{ AR5K_IER,		AR5K_IER_DISABLE },
+ 	{ AR5K_BSR,		0, AR5K_INI_READ },
++#if !defined(CONFIG_ATHEROS_AR71XX) && !defined(CONFIG_ATH79)
+ 	{ AR5K_TXCFG,		AR5K_DMASIZE_128B },
+ 	{ AR5K_RXCFG,		AR5K_DMASIZE_128B },
++#else
++	/* WAR for AR71xx PCI bug */
++	{ AR5K_TXCFG,		AR5K_DMASIZE_128B },
++	{ AR5K_RXCFG,		AR5K_DMASIZE_4B },
++#endif
+ 	{ AR5K_CFG,		AR5K_INIT_CFG },
+ 	{ AR5K_TOPS,		8 },
+ 	{ AR5K_RXNOFRM,		8 },
+diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+index eea4bda..67efda7 100644
+--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
++++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+@@ -86,13 +86,8 @@ ath5k_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ 		goto end;
+ 	}
+ 
+-	/* Don't allow other interfaces if one ad-hoc is configured.
+-	 * TODO: Fix the problems with ad-hoc and multiple other interfaces.
+-	 * We would need to operate the HW in ad-hoc mode to allow TSF updates
+-	 * for the IBSS, but this breaks with additional AP or STA interfaces
+-	 * at the moment. */
+-	if (ah->num_adhoc_vifs ||
+-	    (ah->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
++	/* Don't allow more than one ad-hoc interface */
++	if (ah->num_adhoc_vifs && vif->type == NL80211_IFTYPE_ADHOC) {
+ 		ATH5K_ERR(ah, "Only one single ad-hoc interface is allowed.\n");
+ 		ret = -ELNRNG;
+ 		goto end;
+diff --git a/drivers/net/wireless/ath/ath5k/pci.c b/drivers/net/wireless/ath/ath5k/pci.c
+index c4315dd..2aae4de 100644
+--- a/drivers/net/wireless/ath/ath5k/pci.c
++++ b/drivers/net/wireless/ath/ath5k/pci.c
+@@ -47,6 +47,8 @@ static const struct pci_device_id ath5k_pci_id_table[] = {
+ 	{ PCI_VDEVICE(ATHEROS, 0x001b) }, /* 5413 Eagle */
+ 	{ PCI_VDEVICE(ATHEROS, 0x001c) }, /* PCI-E cards */
+ 	{ PCI_VDEVICE(ATHEROS, 0x001d) }, /* 2417 Nala */
++	{ PCI_VDEVICE(ATHEROS, 0xff16) }, /* 2413,2414 sx76x on lantiq_danube */
++	{ PCI_VDEVICE(ATHEROS, 0xff1a) }, /* 2417 arv45xx on lantiq_danube */
+ 	{ PCI_VDEVICE(ATHEROS, 0xff1b) }, /* AR5BXB63 */
+ 	{ 0 }
+ };
+diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c
+index 9fdb528..eabf225 100644
+--- a/drivers/net/wireless/ath/ath5k/reset.c
++++ b/drivers/net/wireless/ath/ath5k/reset.c
+@@ -1154,6 +1154,7 @@ ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
+ 	tsf_lo = 0;
+ 	mode = 0;
+ 
++#if 0
+ 	/*
+ 	 * Sanity check for fast flag
+ 	 * Fast channel change only available
+@@ -1161,6 +1162,7 @@ ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
+ 	 */
+ 	if (fast && (ah->ah_radio != AR5K_RF2413) &&
+ 	(ah->ah_radio != AR5K_RF5413))
++#endif
+ 		fast = false;
+ 
+ 	/* Disable sleep clock operation
+diff --git a/include/linux/ath5k_platform.h b/include/linux/ath5k_platform.h
+new file mode 100644
+index 0000000..ec85224
+--- /dev/null
++++ b/include/linux/ath5k_platform.h
+@@ -0,0 +1,30 @@
++/*
++ * Copyright (c) 2008 Atheros Communications Inc.
++ * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
++ * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
++ * Copyright (c) 2010 Daniel Golle <daniel.golle@gmail.com>
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#ifndef _LINUX_ATH5K_PLATFORM_H
++#define _LINUX_ATH5K_PLATFORM_H
++
++#define ATH5K_PLAT_EEP_MAX_WORDS	2048
++
++struct ath5k_platform_data {
++	u16 *eeprom_data;
++	u8 *macaddr;
++};
++
++#endif /* _LINUX_ATH5K_PLATFORM_H */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0003-sync-backports-patches-ath5k.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0003-sync-backports-patches-ath5k.patch
deleted file mode 100644
index e0051b5..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0003-sync-backports-patches-ath5k.patch
+++ /dev/null
@@ -1,315 +0,0 @@
-From 410e3c08cd5f7bd85e3ca3965fcbf79b9eb6d1e4 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Tue, 12 Mar 2024 11:26:26 +0800
-Subject: [PATCH 03/61] sync backports patches/ath5k
-
----
- drivers/net/wireless/ath/ath5k/ath5k.h        |  1 +
- drivers/net/wireless/ath/ath5k/base.c         |  8 +-
- drivers/net/wireless/ath/ath5k/debug.c        | 93 +++++++++++++++++++
- drivers/net/wireless/ath/ath5k/dma.c          |  8 ++
- drivers/net/wireless/ath/ath5k/initvals.c     |  6 ++
- drivers/net/wireless/ath/ath5k/mac80211-ops.c |  9 +-
- drivers/net/wireless/ath/ath5k/pci.c          |  2 +
- drivers/net/wireless/ath/ath5k/reset.c        |  2 +
- include/linux/ath5k_platform.h                | 30 ++++++
- 9 files changed, 150 insertions(+), 9 deletions(-)
- create mode 100644 include/linux/ath5k_platform.h
-
-diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
-index 308a429..0e6d184 100644
---- a/drivers/net/wireless/ath/ath5k/ath5k.h
-+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
-@@ -1372,6 +1372,7 @@ struct ath5k_hw {
- 	u8			ah_coverage_class;
- 	bool			ah_ack_bitrate_high;
- 	u8			ah_bwmode;
-+	u8			ah_bwmode_debug;
- 	bool			ah_short_slot;
- 
- 	/* Antenna Control */
-diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
-index bad83fd..fb0366c 100644
---- a/drivers/net/wireless/ath/ath5k/base.c
-+++ b/drivers/net/wireless/ath/ath5k/base.c
-@@ -465,6 +465,9 @@ ath5k_chan_set(struct ath5k_hw *ah, struct cfg80211_chan_def *chandef)
- 		return -EINVAL;
- 	}
- 
-+	if (ah->ah_bwmode_debug != AR5K_BWMODE_DEFAULT)
-+		ah->ah_bwmode = ah->ah_bwmode_debug;
-+
- 	/*
- 	 * To switch channels clear any pending DMA operations;
- 	 * wait long enough for the RX fifo to drain, reset the
-@@ -2009,7 +2012,7 @@ ath5k_beacon_send(struct ath5k_hw *ah)
- 	}
- 
- 	if ((ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs +
--			ah->num_mesh_vifs > 1) ||
-+			ah->num_adhoc_vifs + ah->num_mesh_vifs > 1) ||
- 			ah->opmode == NL80211_IFTYPE_MESH_POINT) {
- 		u64 tsf = ath5k_hw_get_tsf64(ah);
- 		u32 tsftu = TSF_TO_TU(tsf);
-@@ -2095,7 +2098,7 @@ ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf)
- 
- 	intval = ah->bintval & AR5K_BEACON_PERIOD;
- 	if (ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs
--		+ ah->num_mesh_vifs > 1) {
-+		+ ah->num_adhoc_vifs + ah->num_mesh_vifs > 1) {
- 		intval /= ATH_BCBUF;	/* staggered multi-bss beacons */
- 		if (intval < 15)
- 			ATH5K_WARN(ah, "intval %u is too low, min 15\n",
-@@ -2561,6 +2564,7 @@ static const struct ieee80211_iface_limit if_limits[] = {
- 				 BIT(NL80211_IFTYPE_MESH_POINT) |
- #endif
- 				 BIT(NL80211_IFTYPE_AP) },
-+	{ .max = 1,	.types = BIT(NL80211_IFTYPE_ADHOC) },
- };
- 
- static const struct ieee80211_iface_combination if_comb = {
-diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c
-index ec13051..239d789 100644
---- a/drivers/net/wireless/ath/ath5k/debug.c
-+++ b/drivers/net/wireless/ath/ath5k/debug.c
-@@ -803,6 +803,97 @@ static const struct file_operations fops_ani = {
- 	.llseek = default_llseek,
- };
- 
-+/* debugfs: bwmode */
-+
-+static ssize_t read_file_bwmode(struct file *file, char __user *user_buf,
-+				   size_t count, loff_t *ppos)
-+{
-+	struct ath5k_hw *ah = file->private_data;
-+	char buf[15];
-+	unsigned int len = 0;
-+
-+	int cur_ah_bwmode = ah->ah_bwmode_debug;
-+
-+#define print_selected(MODE, LABEL) \
-+	if (cur_ah_bwmode == MODE) \
-+		len += snprintf(buf+len, sizeof(buf)-len, "[%s]", LABEL); \
-+	else \
-+		len += snprintf(buf+len, sizeof(buf)-len, "%s", LABEL); \
-+	len += snprintf(buf+len, sizeof(buf)-len, " ");
-+
-+	print_selected(AR5K_BWMODE_5MHZ, "5");
-+	print_selected(AR5K_BWMODE_10MHZ, "10");
-+	print_selected(AR5K_BWMODE_DEFAULT, "20");
-+	print_selected(AR5K_BWMODE_40MHZ, "40");
-+#undef print_selected
-+
-+	len += snprintf(buf+len, sizeof(buf)-len, "\n");
-+
-+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-+}
-+
-+static ssize_t write_file_bwmode(struct file *file,
-+				 const char __user *userbuf,
-+				 size_t count, loff_t *ppos)
-+{
-+	struct ath5k_hw *ah = file->private_data;
-+	char buf[3];
-+	int bw = 20;
-+	int tobwmode = AR5K_BWMODE_DEFAULT;
-+
-+	if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
-+		return -EFAULT;
-+
-+	/* TODO: Add check for active interface */
-+
-+	if(strncmp(buf, "5", 1) == 0 ) {
-+		tobwmode = AR5K_BWMODE_5MHZ;
-+		bw = 5;
-+	} else if ( strncmp(buf, "10", 2) == 0 ) {
-+		tobwmode = AR5K_BWMODE_10MHZ;
-+		bw = 10;
-+	} else if ( strncmp(buf, "20", 2) == 0 ) {
-+		tobwmode = AR5K_BWMODE_DEFAULT;
-+		bw = 20;
-+	} else if ( strncmp(buf, "40", 2) == 0 ) {
-+		tobwmode = AR5K_BWMODE_40MHZ;
-+		bw = 40;
-+	} else
-+		return -EINVAL;
-+
-+	ATH5K_INFO(ah, "Changing to %imhz channel width[%i]\n",
-+		bw, tobwmode);
-+
-+	switch (ah->ah_radio) {
-+	/* TODO: only define radios that actually support 5/10mhz channels */
-+	case AR5K_RF5413:
-+	case AR5K_RF5110:
-+	case AR5K_RF5111:
-+	case AR5K_RF5112:
-+	case AR5K_RF2413:
-+	case AR5K_RF2316:
-+	case AR5K_RF2317:
-+	case AR5K_RF2425:
-+		if(ah->ah_bwmode_debug != tobwmode) {
-+			mutex_lock(&ah->lock);
-+			ah->ah_bwmode = tobwmode;
-+			ah->ah_bwmode_debug = tobwmode;
-+			mutex_unlock(&ah->lock);
-+		}
-+		break;
-+	default:
-+		return -EOPNOTSUPP;
-+	}
-+	return count;
-+}
-+
-+static const struct file_operations fops_bwmode = {
-+	.read = read_file_bwmode,
-+	.write = write_file_bwmode,
-+	.open = simple_open,
-+	.owner = THIS_MODULE,
-+	.llseek = default_llseek,
-+};
- 
- /* debugfs: queues etc */
- 
-@@ -995,6 +1086,8 @@ ath5k_debug_init_device(struct ath5k_hw *ah)
- 	debugfs_create_file("queue", 0600, phydir, ah, &fops_queue);
- 	debugfs_create_bool("32khz_clock", 0600, phydir,
- 			    &ah->ah_use_32khz_clock);
-+	debugfs_create_file("bwmode", S_IWUSR | S_IRUSR, phydir, ah,
-+			    &fops_bwmode);
- }
- 
- /* functions used in other places */
-diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c
-index d9e376e..db06ff8 100644
---- a/drivers/net/wireless/ath/ath5k/dma.c
-+++ b/drivers/net/wireless/ath/ath5k/dma.c
-@@ -854,10 +854,18 @@ ath5k_hw_dma_init(struct ath5k_hw *ah)
- 	 * guess we can tweak it and see how it goes ;-)
- 	 */
- 	if (ah->ah_version != AR5K_AR5210) {
-+#if !defined(CONFIG_ATHEROS_AR71XX) && !defined(CONFIG_ATH79)
- 		AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG,
- 			AR5K_TXCFG_SDMAMR, AR5K_DMASIZE_128B);
- 		AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG,
- 			AR5K_RXCFG_SDMAMW, AR5K_DMASIZE_128B);
-+#else
-+		/* WAR for AR71xx PCI bug */
-+		AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG,
-+			AR5K_TXCFG_SDMAMR, AR5K_DMASIZE_128B);
-+		AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG,
-+			AR5K_RXCFG_SDMAMW, AR5K_DMASIZE_4B);
-+#endif
- 	}
- 
- 	/* Pre-enable interrupts on 5211/5212*/
-diff --git a/drivers/net/wireless/ath/ath5k/initvals.c b/drivers/net/wireless/ath/ath5k/initvals.c
-index ee1c2fa..122fe1c 100644
---- a/drivers/net/wireless/ath/ath5k/initvals.c
-+++ b/drivers/net/wireless/ath/ath5k/initvals.c
-@@ -62,8 +62,14 @@ static const struct ath5k_ini ar5210_ini[] = {
- 	{ AR5K_IMR,		0 },
- 	{ AR5K_IER,		AR5K_IER_DISABLE },
- 	{ AR5K_BSR,		0, AR5K_INI_READ },
-+#if !defined(CONFIG_ATHEROS_AR71XX) && !defined(CONFIG_ATH79)
- 	{ AR5K_TXCFG,		AR5K_DMASIZE_128B },
- 	{ AR5K_RXCFG,		AR5K_DMASIZE_128B },
-+#else
-+	/* WAR for AR71xx PCI bug */
-+	{ AR5K_TXCFG,		AR5K_DMASIZE_128B },
-+	{ AR5K_RXCFG,		AR5K_DMASIZE_4B },
-+#endif
- 	{ AR5K_CFG,		AR5K_INIT_CFG },
- 	{ AR5K_TOPS,		8 },
- 	{ AR5K_RXNOFRM,		8 },
-diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
-index da9dc62..2fb37d3 100644
---- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
-+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
-@@ -86,13 +86,8 @@ ath5k_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
- 		goto end;
- 	}
- 
--	/* Don't allow other interfaces if one ad-hoc is configured.
--	 * TODO: Fix the problems with ad-hoc and multiple other interfaces.
--	 * We would need to operate the HW in ad-hoc mode to allow TSF updates
--	 * for the IBSS, but this breaks with additional AP or STA interfaces
--	 * at the moment. */
--	if (ah->num_adhoc_vifs ||
--	    (ah->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
-+	/* Don't allow more than one ad-hoc interface */
-+	if (ah->num_adhoc_vifs && vif->type == NL80211_IFTYPE_ADHOC) {
- 		ATH5K_ERR(ah, "Only one single ad-hoc interface is allowed.\n");
- 		ret = -ELNRNG;
- 		goto end;
-diff --git a/drivers/net/wireless/ath/ath5k/pci.c b/drivers/net/wireless/ath/ath5k/pci.c
-index c4315dd..2aae4de 100644
---- a/drivers/net/wireless/ath/ath5k/pci.c
-+++ b/drivers/net/wireless/ath/ath5k/pci.c
-@@ -47,6 +47,8 @@ static const struct pci_device_id ath5k_pci_id_table[] = {
- 	{ PCI_VDEVICE(ATHEROS, 0x001b) }, /* 5413 Eagle */
- 	{ PCI_VDEVICE(ATHEROS, 0x001c) }, /* PCI-E cards */
- 	{ PCI_VDEVICE(ATHEROS, 0x001d) }, /* 2417 Nala */
-+	{ PCI_VDEVICE(ATHEROS, 0xff16) }, /* 2413,2414 sx76x on lantiq_danube */
-+	{ PCI_VDEVICE(ATHEROS, 0xff1a) }, /* 2417 arv45xx on lantiq_danube */
- 	{ PCI_VDEVICE(ATHEROS, 0xff1b) }, /* AR5BXB63 */
- 	{ 0 }
- };
-diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c
-index 9fdb528..eabf225 100644
---- a/drivers/net/wireless/ath/ath5k/reset.c
-+++ b/drivers/net/wireless/ath/ath5k/reset.c
-@@ -1154,6 +1154,7 @@ ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
- 	tsf_lo = 0;
- 	mode = 0;
- 
-+#if 0
- 	/*
- 	 * Sanity check for fast flag
- 	 * Fast channel change only available
-@@ -1161,6 +1162,7 @@ ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
- 	 */
- 	if (fast && (ah->ah_radio != AR5K_RF2413) &&
- 	(ah->ah_radio != AR5K_RF5413))
-+#endif
- 		fast = false;
- 
- 	/* Disable sleep clock operation
-diff --git a/include/linux/ath5k_platform.h b/include/linux/ath5k_platform.h
-new file mode 100644
-index 0000000..ec85224
---- /dev/null
-+++ b/include/linux/ath5k_platform.h
-@@ -0,0 +1,30 @@
-+/*
-+ * Copyright (c) 2008 Atheros Communications Inc.
-+ * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
-+ * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
-+ * Copyright (c) 2010 Daniel Golle <daniel.golle@gmail.com>
-+ *
-+ * Permission to use, copy, modify, and/or distribute this software for any
-+ * purpose with or without fee is hereby granted, provided that the above
-+ * copyright notice and this permission notice appear in all copies.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-+ */
-+
-+#ifndef _LINUX_ATH5K_PLATFORM_H
-+#define _LINUX_ATH5K_PLATFORM_H
-+
-+#define ATH5K_PLAT_EEP_MAX_WORDS	2048
-+
-+struct ath5k_platform_data {
-+	u16 *eeprom_data;
-+	u8 *macaddr;
-+};
-+
-+#endif /* _LINUX_ATH5K_PLATFORM_H */
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0004-backports-sync-openwrt-patches-ath9k.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0004-backports-sync-openwrt-patches-ath9k.patch
new file mode 100644
index 0000000..e23257f
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0004-backports-sync-openwrt-patches-ath9k.patch
@@ -0,0 +1,2445 @@
+From 4c05df4286198949bf3022827e3ebf897e181784 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 17 Jul 2024 20:41:29 +0800
+Subject: [PATCH 04/89] backports: sync openwrt patches/ath9k
+
+---
+ drivers/net/wireless/ath/ath.h                |   2 +
+ drivers/net/wireless/ath/ath9k/Kconfig        |  13 +
+ drivers/net/wireless/ath/ath9k/Makefile       |   1 +
+ drivers/net/wireless/ath/ath9k/ahb.c          | 216 ++++++++++-
+ drivers/net/wireless/ath/ath9k/ani.h          |   2 +-
+ drivers/net/wireless/ath/ath9k/ar5008_phy.c   |  72 ++--
+ drivers/net/wireless/ath/ath9k/ar9002_phy.h   |  11 +
+ drivers/net/wireless/ath/ath9k/ar9003_phy.c   |  95 +----
+ drivers/net/wireless/ath/ath9k/ath9k.h        |  34 +-
+ drivers/net/wireless/ath/ath9k/channel.c      |   7 +
+ drivers/net/wireless/ath/ath9k/common-debug.c | 107 +++++
+ drivers/net/wireless/ath/ath9k/common-debug.h |   4 +
+ drivers/net/wireless/ath/ath9k/common.c       |  21 +-
+ drivers/net/wireless/ath/ath9k/debug.c        | 107 +++++
+ drivers/net/wireless/ath/ath9k/gpio.c         | 365 ++++++++++++++++--
+ drivers/net/wireless/ath/ath9k/hsr.c          | 247 ++++++++++++
+ drivers/net/wireless/ath/ath9k/hsr.h          |  48 +++
+ .../net/wireless/ath/ath9k/htc_drv_debug.c    |   2 +
+ drivers/net/wireless/ath/ath9k/htc_drv_init.c |   7 +-
+ drivers/net/wireless/ath/ath9k/hw-ops.h       |   6 +
+ drivers/net/wireless/ath/ath9k/hw.c           | 154 ++++++--
+ drivers/net/wireless/ath/ath9k/hw.h           |  12 +
+ drivers/net/wireless/ath/ath9k/init.c         |  48 ++-
+ drivers/net/wireless/ath/ath9k/mac.c          |   9 +-
+ drivers/net/wireless/ath/ath9k/main.c         |  12 +
+ drivers/net/wireless/ath/ath9k/pci.c          |   1 +
+ drivers/net/wireless/ath/ath9k/phy.h          |   3 +
+ local-symbols                                 |   1 +
+ 28 files changed, 1387 insertions(+), 220 deletions(-)
+ create mode 100644 drivers/net/wireless/ath/ath9k/hsr.c
+ create mode 100644 drivers/net/wireless/ath/ath9k/hsr.h
+
+diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
+index eb63efd..5377a35 100644
+--- a/drivers/net/wireless/ath/ath.h
++++ b/drivers/net/wireless/ath/ath.h
+@@ -151,6 +151,7 @@ struct ath_common {
+ 	int debug_mask;
+ 	enum ath_device_state state;
+ 	unsigned long op_flags;
++	u32 chan_bw;
+ 
+ 	struct ath_ani ani;
+ 
+@@ -181,6 +182,7 @@ struct ath_common {
+ 	const struct ath_ops *ops;
+ 	const struct ath_bus_ops *bus_ops;
+ 	const struct ath_ps_ops *ps_ops;
++	const struct ieee80211_ops *ieee_ops;
+ 
+ 	bool btcoex_enabled;
+ 	bool disable_ani;
+diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig
+index b97598f..d3e72a8 100644
+--- a/drivers/net/wireless/ath/ath9k/Kconfig
++++ b/drivers/net/wireless/ath/ath9k/Kconfig
+@@ -58,6 +58,19 @@ config ATH9K_AHB
+ 	  Say Y, if you have a SoC with a compatible built-in
+ 	  wireless MAC. Say N if unsure.
+ 
++config ATH9K_UBNTHSR
++	bool "Ubiquiti UniFi Outdoor Plus HSR support"
++	depends on ATH9K
++	---help---
++	  This options enables code to control the HSR RF
++	  filter in the receive path of the Ubiquiti UniFi
++	  Outdoor Plus access point.
++
++	  Say Y if you want to use the access point. The
++	  code will only be used if the device is detected,
++	  so it does not harm other setup other than occupying
++	  a bit of memory.
++
+ config ATH9K_DEBUGFS
+ 	bool "Atheros ath9k debugging"
+ 	depends on ATH9K && DEBUG_FS && MAC80211_DEBUGFS
+diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
+index 847c8a8..6427bc6 100644
+--- a/drivers/net/wireless/ath/ath9k/Makefile
++++ b/drivers/net/wireless/ath/ath9k/Makefile
+@@ -17,6 +17,7 @@ ath9k-$(CPTCFG_ATH9K_DFS_CERTIFIED) += dfs.o
+ ath9k-$(CPTCFG_ATH9K_TX99) += tx99.o
+ ath9k-$(CPTCFG_ATH9K_WOW) += wow.o
+ ath9k-$(CPTCFG_ATH9K_HWRNG) += rng.o
++ath9k-$(CPTCFG_ATH9K_UBNTHSR) += hsr.o
+ 
+ ath9k-$(CPTCFG_ATH9K_DEBUGFS) += debug.o
+ 
+diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c
+index 1a6697b..70fae7a 100644
+--- a/drivers/net/wireless/ath/ath9k/ahb.c
++++ b/drivers/net/wireless/ath/ath9k/ahb.c
+@@ -20,7 +20,15 @@
+ #include <linux/platform_device.h>
+ #include <linux/module.h>
+ #include <linux/mod_devicetable.h>
++#include <linux/of_device.h>
+ #include "ath9k.h"
++#include <linux/ath9k_platform.h>
++
++#ifdef CONFIG_OF
++#include <asm/mach-ath79/ath79.h>
++#include <asm/mach-ath79/ar71xx_regs.h>
++#include <linux/mtd/mtd.h>
++#endif
+ 
+ static const struct platform_device_id ath9k_platform_id_table[] = {
+ 	{
+@@ -69,6 +77,192 @@ static const struct ath_bus_ops ath_ahb_bus_ops  = {
+ 	.eeprom_read = ath_ahb_eeprom_read,
+ };
+ 
++#ifdef CONFIG_OF
++
++#define QCA955X_DDR_CTL_CONFIG          0x108
++#define QCA955X_DDR_CTL_CONFIG_ACT_WMAC BIT(23)
++
++static int ar913x_wmac_reset(void)
++{
++	ath79_device_reset_set(AR913X_RESET_AMBA2WMAC);
++	mdelay(10);
++
++	ath79_device_reset_clear(AR913X_RESET_AMBA2WMAC);
++	mdelay(10);
++
++	return 0;
++}
++
++static int ar933x_wmac_reset(void)
++{
++	int retries = 20;
++
++	ath79_device_reset_set(AR933X_RESET_WMAC);
++	ath79_device_reset_clear(AR933X_RESET_WMAC);
++
++	while (1) {
++		u32 bootstrap;
++
++		bootstrap = ath79_reset_rr(AR933X_RESET_REG_BOOTSTRAP);
++		if ((bootstrap & AR933X_BOOTSTRAP_EEPBUSY) == 0)
++			return 0;
++
++		if (retries-- == 0)
++			break;
++
++		udelay(10000);
++	}
++
++	pr_err("ar933x: WMAC reset timed out");
++	return -ETIMEDOUT;
++}
++
++static int qca955x_wmac_reset(void)
++{
++	int i;
++
++	/* Try to wait for WMAC DDR activity to stop */
++	for (i = 0; i < 10; i++) {
++		if (!(__raw_readl(ath79_ddr_base + QCA955X_DDR_CTL_CONFIG) &
++		    QCA955X_DDR_CTL_CONFIG_ACT_WMAC))
++			break;
++
++		udelay(10);
++	}
++
++	ath79_device_reset_set(QCA955X_RESET_RTC);
++	udelay(10);
++	ath79_device_reset_clear(QCA955X_RESET_RTC);
++	udelay(10);
++
++	return 0;
++}
++
++enum {
++	AR913X_WMAC = 0,
++	AR933X_WMAC,
++	AR934X_WMAC,
++	QCA953X_WMAC,
++	QCA955X_WMAC,
++	QCA956X_WMAC,
++};
++
++static int ar9330_get_soc_revision(void)
++{
++	if (ath79_soc_rev == 1)
++		return ath79_soc_rev;
++
++	return 0;
++}
++
++static int ath79_get_soc_revision(void)
++{
++	return ath79_soc_rev;
++}
++
++static const struct of_ath_ahb_data {
++	u16 dev_id;
++	u32 bootstrap_reg;
++	u32 bootstrap_ref;
++
++	int (*soc_revision)(void);
++	int (*wmac_reset)(void);
++} of_ath_ahb_data[] = {
++	[AR913X_WMAC] = {
++		.dev_id = AR5416_AR9100_DEVID,
++		.wmac_reset = ar913x_wmac_reset,
++
++	},
++	[AR933X_WMAC] = {
++		.dev_id = AR9300_DEVID_AR9330,
++		.bootstrap_reg = AR933X_RESET_REG_BOOTSTRAP,
++		.bootstrap_ref = AR933X_BOOTSTRAP_REF_CLK_40,
++		.soc_revision = ar9330_get_soc_revision,
++		.wmac_reset = ar933x_wmac_reset,
++	},
++	[AR934X_WMAC] = {
++		.dev_id = AR9300_DEVID_AR9340,
++		.bootstrap_reg = AR934X_RESET_REG_BOOTSTRAP,
++		.bootstrap_ref = AR934X_BOOTSTRAP_REF_CLK_40,
++		.soc_revision = ath79_get_soc_revision,
++	},
++	[QCA953X_WMAC] = {
++		.dev_id = AR9300_DEVID_AR953X,
++		.bootstrap_reg = QCA953X_RESET_REG_BOOTSTRAP,
++		.bootstrap_ref = QCA953X_BOOTSTRAP_REF_CLK_40,
++		.soc_revision = ath79_get_soc_revision,
++	},
++	[QCA955X_WMAC] = {
++		.dev_id = AR9300_DEVID_QCA955X,
++		.bootstrap_reg = QCA955X_RESET_REG_BOOTSTRAP,
++		.bootstrap_ref = QCA955X_BOOTSTRAP_REF_CLK_40,
++		.wmac_reset = qca955x_wmac_reset,
++	},
++	[QCA956X_WMAC] = {
++		.dev_id = AR9300_DEVID_QCA956X,
++		.bootstrap_reg = QCA956X_RESET_REG_BOOTSTRAP,
++		.bootstrap_ref = QCA956X_BOOTSTRAP_REF_CLK_40,
++		.soc_revision = ath79_get_soc_revision,
++	},
++};
++
++const struct of_device_id of_ath_ahb_match[] = {
++	{ .compatible = "qca,ar9130-wmac", .data = &of_ath_ahb_data[AR913X_WMAC] },
++	{ .compatible = "qca,ar9330-wmac", .data = &of_ath_ahb_data[AR933X_WMAC] },
++	{ .compatible = "qca,ar9340-wmac", .data = &of_ath_ahb_data[AR934X_WMAC] },
++	{ .compatible = "qca,qca9530-wmac", .data = &of_ath_ahb_data[QCA953X_WMAC] },
++	{ .compatible = "qca,qca9550-wmac", .data = &of_ath_ahb_data[QCA955X_WMAC] },
++	{ .compatible = "qca,qca9560-wmac", .data = &of_ath_ahb_data[QCA956X_WMAC] },
++	{},
++};
++MODULE_DEVICE_TABLE(of, of_ath_ahb_match);
++
++static int of_ath_ahb_probe(struct platform_device *pdev)
++{
++	struct ath9k_platform_data *pdata;
++	const struct of_device_id *match;
++	const struct of_ath_ahb_data *data;
++	u8 led_pin;
++
++	match = of_match_device(of_ath_ahb_match, &pdev->dev);
++	data = (const struct of_ath_ahb_data *)match->data;
++
++	pdata = dev_get_platdata(&pdev->dev);
++
++	if (!of_property_read_u8(pdev->dev.of_node, "qca,led-pin", &led_pin))
++		pdata->led_pin = led_pin;
++	else
++		pdata->led_pin = -1;
++
++	if (of_property_read_bool(pdev->dev.of_node, "qca,tx-gain-buffalo"))
++		pdata->tx_gain_buffalo = true;
++
++	if (data->wmac_reset) {
++		data->wmac_reset();
++		pdata->external_reset = data->wmac_reset;
++	}
++
++	if (data->dev_id == AR9300_DEVID_AR953X) {
++		/*
++		 * QCA953x only supports 25MHz refclk.
++		 * Some vendors have an invalid bootstrap option
++		 * set, which would break the WMAC here.
++		 */
++		pdata->is_clk_25mhz = true;
++	} else if (data->bootstrap_reg && data->bootstrap_ref) {
++		u32 t = ath79_reset_rr(data->bootstrap_reg);
++		if (t & data->bootstrap_ref)
++			pdata->is_clk_25mhz = false;
++		else
++			pdata->is_clk_25mhz = true;
++	}
++
++	pdata->get_mac_revision = data->soc_revision;
++
++	return data->dev_id;
++}
++#endif
++
+ static int ath_ahb_probe(struct platform_device *pdev)
+ {
+ 	void __iomem *mem;
+@@ -80,6 +274,17 @@ static int ath_ahb_probe(struct platform_device *pdev)
+ 	int ret = 0;
+ 	struct ath_hw *ah;
+ 	char hw_name[64];
++	u16 dev_id;
++
++	if (id)
++		dev_id = id->driver_data;
++
++#ifdef CONFIG_OF
++	if (pdev->dev.of_node)
++		pdev->dev.platform_data = devm_kzalloc(&pdev->dev,
++					sizeof(struct ath9k_platform_data),
++					GFP_KERNEL);
++#endif
+ 
+ 	if (!dev_get_platdata(&pdev->dev)) {
+ 		dev_err(&pdev->dev, "no platform data specified\n");
+@@ -118,17 +323,23 @@ static int ath_ahb_probe(struct platform_device *pdev)
+ 	sc->mem = mem;
+ 	sc->irq = irq;
+ 
++#ifdef CONFIG_OF
++	dev_id = of_ath_ahb_probe(pdev);
++#endif
+ 	ret = request_irq(irq, ath_isr, IRQF_SHARED, "ath9k", sc);
+ 	if (ret) {
+ 		dev_err(&pdev->dev, "request_irq failed\n");
+ 		goto err_free_hw;
+ 	}
+ 
+-	ret = ath9k_init_device(id->driver_data, sc, &ath_ahb_bus_ops);
++	ret = ath9k_init_device(dev_id, sc, &ath_ahb_bus_ops);
+ 	if (ret) {
+ 		dev_err(&pdev->dev, "failed to initialize device\n");
+ 		goto err_irq;
+ 	}
++#ifdef CONFIG_OF
++	pdev->dev.platform_data = NULL;
++#endif
+ 
+ 	ah = sc->sc_ah;
+ 	ath9k_hw_name(ah, hw_name, sizeof(hw_name));
+@@ -162,6 +373,9 @@ static struct platform_driver ath_ahb_driver = {
+ 	.remove_new = ath_ahb_remove,
+ 	.driver		= {
+ 		.name	= "ath9k",
++#ifdef CONFIG_OF
++		.of_match_table = of_ath_ahb_match,
++#endif
+ 	},
+ 	.id_table    = ath9k_platform_id_table,
+ };
+diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h
+index c40965b..f66f7ed 100644
+--- a/drivers/net/wireless/ath/ath9k/ani.h
++++ b/drivers/net/wireless/ath/ath9k/ani.h
+@@ -42,7 +42,7 @@
+ #define ATH9K_ANI_PERIOD                  300
+ 
+ /* in ms */
+-#define ATH9K_ANI_POLLINTERVAL            1000
++#define ATH9K_ANI_POLLINTERVAL            300
+ 
+ #define ATH9K_SIG_FIRSTEP_SETTING_MIN     0
+ #define ATH9K_SIG_FIRSTEP_SETTING_MAX     20
+diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+index 7a45f5f..3f0ca1d 100644
+--- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c
++++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+@@ -969,55 +969,6 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah,
+ 		 * on == 0 means more noise imm
+ 		 */
+ 		u32 on = param ? 1 : 0;
+-		/*
+-		 * make register setting for default
+-		 * (weak sig detect ON) come from INI file
+-		 */
+-		int m1ThreshLow = on ?
+-			aniState->iniDef.m1ThreshLow : m1ThreshLow_off;
+-		int m2ThreshLow = on ?
+-			aniState->iniDef.m2ThreshLow : m2ThreshLow_off;
+-		int m1Thresh = on ?
+-			aniState->iniDef.m1Thresh : m1Thresh_off;
+-		int m2Thresh = on ?
+-			aniState->iniDef.m2Thresh : m2Thresh_off;
+-		int m2CountThr = on ?
+-			aniState->iniDef.m2CountThr : m2CountThr_off;
+-		int m2CountThrLow = on ?
+-			aniState->iniDef.m2CountThrLow : m2CountThrLow_off;
+-		int m1ThreshLowExt = on ?
+-			aniState->iniDef.m1ThreshLowExt : m1ThreshLowExt_off;
+-		int m2ThreshLowExt = on ?
+-			aniState->iniDef.m2ThreshLowExt : m2ThreshLowExt_off;
+-		int m1ThreshExt = on ?
+-			aniState->iniDef.m1ThreshExt : m1ThreshExt_off;
+-		int m2ThreshExt = on ?
+-			aniState->iniDef.m2ThreshExt : m2ThreshExt_off;
+-
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+-			      AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
+-			      m1ThreshLow);
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+-			      AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
+-			      m2ThreshLow);
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+-			      AR_PHY_SFCORR_M1_THRESH, m1Thresh);
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+-			      AR_PHY_SFCORR_M2_THRESH, m2Thresh);
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+-			      AR_PHY_SFCORR_M2COUNT_THR, m2CountThr);
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+-			      AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
+-			      m2CountThrLow);
+-
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+-			      AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1ThreshLowExt);
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+-			      AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2ThreshLowExt);
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+-			      AR_PHY_SFCORR_EXT_M1_THRESH, m1ThreshExt);
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+-			      AR_PHY_SFCORR_EXT_M2_THRESH, m2ThreshExt);
+ 
+ 		if (on)
+ 			REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
+@@ -1340,9 +1291,30 @@ void ar5008_hw_init_rate_txpower(struct ath_hw *ah, int16_t *rate_array,
+ 	}
+ }
+ 
++static void ar5008_hw_get_adc_entropy(struct ath_hw *ah, u8 *buf, size_t len)
++{
++	int i, j;
++
++	REG_RMW_FIELD(ah, AR_PHY_TEST, AR_PHY_TEST_BBB_OBS_SEL, 1);
++	REG_CLR_BIT(ah, AR_PHY_TEST, AR_PHY_TEST_RX_OBS_SEL_BIT5);
++	REG_RMW_FIELD(ah, AR_PHY_TEST2, AR_PHY_TEST2_RX_OBS_SEL, 0);
++
++	memset(buf, 0, len);
++	for (i = 0; i < len; i++) {
++		for (j = 0; j < 4; j++) {
++			u32 regval = REG_READ(ah, AR_PHY_TST_ADC);
++
++			buf[i] <<= 2;
++			buf[i] |= (regval & 1) | ((regval & BIT(9)) >> 8);
++			udelay(1);
++		}
++	}
++}
++
+ int ar5008_hw_attach_phy_ops(struct ath_hw *ah)
+ {
+ 	struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
++	struct ath_hw_ops *ops = ath9k_hw_ops(ah);
+ 	static const u32 ar5416_cca_regs[6] = {
+ 		AR_PHY_CCA,
+ 		AR_PHY_CH1_CCA,
+@@ -1357,6 +1329,8 @@ int ar5008_hw_attach_phy_ops(struct ath_hw *ah)
+ 	if (ret)
+ 	    return ret;
+ 
++	ops->get_adc_entropy = ar5008_hw_get_adc_entropy;
++
+ 	priv_ops->rf_set_freq = ar5008_hw_set_channel;
+ 	priv_ops->spur_mitigate_freq = ar5008_hw_spur_mitigate;
+ 
+diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.h b/drivers/net/wireless/ath/ath9k/ar9002_phy.h
+index 2b58245..d20a936 100644
+--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.h
++++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.h
+@@ -20,6 +20,12 @@
+ #define PHY_AGC_CLR             0x10000000
+ #define RFSILENT_BB             0x00002000
+ 
++#define AR_PHY_TEST_BBB_OBS_SEL       0x780000
++#define AR_PHY_TEST_BBB_OBS_SEL_S     19
++
++#define AR_PHY_TEST_RX_OBS_SEL_BIT5_S 23
++#define AR_PHY_TEST_RX_OBS_SEL_BIT5   (1 << AR_PHY_TEST_RX_OBS_SEL_BIT5_S)
++
+ #define AR_PHY_TURBO                0x9804
+ #define AR_PHY_FC_TURBO_MODE        0x00000001
+ #define AR_PHY_FC_TURBO_SHORT       0x00000002
+@@ -36,6 +42,9 @@
+ 
+ #define AR_PHY_TEST2			0x9808
+ 
++#define AR_PHY_TEST2_RX_OBS_SEL        0x3C00
++#define AR_PHY_TEST2_RX_OBS_SEL_S      10
++
+ #define AR_PHY_TIMING2           0x9810
+ #define AR_PHY_TIMING3           0x9814
+ #define AR_PHY_TIMING3_DSC_MAN   0xFFFE0000
+@@ -393,6 +402,8 @@
+ #define AR_PHY_RFBUS_GRANT       0x9C20
+ #define AR_PHY_RFBUS_GRANT_EN    0x00000001
+ 
++#define AR_PHY_TST_ADC      0x9C24
++
+ #define AR_PHY_CHAN_INFO_GAIN_DIFF             0x9CF4
+ #define AR_PHY_CHAN_INFO_GAIN_DIFF_UPPER_LIMIT 320
+ 
+diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+index f715149..0246ad0 100644
+--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
++++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+@@ -42,20 +42,6 @@ static const int cycpwrThr1_table[] =
+ /* level:  0   1   2   3   4   5   6   7   8  */
+ 	{ -6, -4, -2,  0,  2,  4,  6,  8 };     /* lvl 0-7, default 3 */
+ 
+-/*
+- * register values to turn OFDM weak signal detection OFF
+- */
+-static const int m1ThreshLow_off = 127;
+-static const int m2ThreshLow_off = 127;
+-static const int m1Thresh_off = 127;
+-static const int m2Thresh_off = 127;
+-static const int m2CountThr_off =  31;
+-static const int m2CountThrLow_off =  63;
+-static const int m1ThreshLowExt_off = 127;
+-static const int m2ThreshLowExt_off = 127;
+-static const int m1ThreshExt_off = 127;
+-static const int m2ThreshExt_off = 127;
+-
+ static const u8 ofdm2pwr[] = {
+ 	ALL_TARGET_LEGACY_6_24,
+ 	ALL_TARGET_LEGACY_6_24,
+@@ -1065,11 +1051,6 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
+ 	struct ath_common *common = ath9k_hw_common(ah);
+ 	struct ath9k_channel *chan = ah->curchan;
+ 	struct ar5416AniState *aniState = &ah->ani;
+-	int m1ThreshLow, m2ThreshLow;
+-	int m1Thresh, m2Thresh;
+-	int m2CountThr, m2CountThrLow;
+-	int m1ThreshLowExt, m2ThreshLowExt;
+-	int m1ThreshExt, m2ThreshExt;
+ 	s32 value, value2;
+ 
+ 	switch (cmd & ah->ani_function) {
+@@ -1083,61 +1064,6 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
+ 		 */
+ 		u32 on = param ? 1 : 0;
+ 
+-		if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
+-			goto skip_ws_det;
+-
+-		m1ThreshLow = on ?
+-			aniState->iniDef.m1ThreshLow : m1ThreshLow_off;
+-		m2ThreshLow = on ?
+-			aniState->iniDef.m2ThreshLow : m2ThreshLow_off;
+-		m1Thresh = on ?
+-			aniState->iniDef.m1Thresh : m1Thresh_off;
+-		m2Thresh = on ?
+-			aniState->iniDef.m2Thresh : m2Thresh_off;
+-		m2CountThr = on ?
+-			aniState->iniDef.m2CountThr : m2CountThr_off;
+-		m2CountThrLow = on ?
+-			aniState->iniDef.m2CountThrLow : m2CountThrLow_off;
+-		m1ThreshLowExt = on ?
+-			aniState->iniDef.m1ThreshLowExt : m1ThreshLowExt_off;
+-		m2ThreshLowExt = on ?
+-			aniState->iniDef.m2ThreshLowExt : m2ThreshLowExt_off;
+-		m1ThreshExt = on ?
+-			aniState->iniDef.m1ThreshExt : m1ThreshExt_off;
+-		m2ThreshExt = on ?
+-			aniState->iniDef.m2ThreshExt : m2ThreshExt_off;
+-
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+-			      AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
+-			      m1ThreshLow);
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+-			      AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
+-			      m2ThreshLow);
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+-			      AR_PHY_SFCORR_M1_THRESH,
+-			      m1Thresh);
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+-			      AR_PHY_SFCORR_M2_THRESH,
+-			      m2Thresh);
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+-			      AR_PHY_SFCORR_M2COUNT_THR,
+-			      m2CountThr);
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+-			      AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
+-			      m2CountThrLow);
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+-			      AR_PHY_SFCORR_EXT_M1_THRESH_LOW,
+-			      m1ThreshLowExt);
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+-			      AR_PHY_SFCORR_EXT_M2_THRESH_LOW,
+-			      m2ThreshLowExt);
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+-			      AR_PHY_SFCORR_EXT_M1_THRESH,
+-			      m1ThreshExt);
+-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+-			      AR_PHY_SFCORR_EXT_M2_THRESH,
+-			      m2ThreshExt);
+-skip_ws_det:
+ 		if (on)
+ 			REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
+ 				    AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
+@@ -1915,6 +1841,26 @@ void ar9003_hw_init_rate_txpower(struct ath_hw *ah, u8 *rate_array,
+ 	}
+ }
+ 
++static void ar9003_hw_get_adc_entropy(struct ath_hw *ah, u8 *buf, size_t len)
++{
++	int i, j;
++
++	REG_RMW_FIELD(ah, AR_PHY_TEST(ah), AR_PHY_TEST_BBB_OBS_SEL, 1);
++	REG_CLR_BIT(ah, AR_PHY_TEST(ah), AR_PHY_TEST_RX_OBS_SEL_BIT5);
++	REG_RMW_FIELD(ah, AR_PHY_TEST_CTL_STATUS(ah), AR_PHY_TEST_CTL_RX_OBS_SEL, 0);
++
++	memset(buf, 0, len);
++	for (i = 0; i < len; i++) {
++		for (j = 0; j < 4; j++) {
++			u32 regval = REG_READ(ah, AR_PHY_TST_ADC);
++
++			buf[i] <<= 2;
++			buf[i] |= (regval & 1) | ((regval & BIT(10)) >> 9);
++			udelay(1);
++		}
++	}
++}
++
+ void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
+ {
+ 	struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
+@@ -1951,6 +1897,7 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
+ 	priv_ops->set_radar_params = ar9003_hw_set_radar_params;
+ 	priv_ops->fast_chan_change = ar9003_hw_fast_chan_change;
+ 
++	ops->get_adc_entropy = ar9003_hw_get_adc_entropy;
+ 	ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get;
+ 	ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set;
+ 	ops->spectral_scan_config = ar9003_hw_spectral_scan_config;
+diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
+index 97f4710..94efbdb 100644
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -25,6 +25,8 @@
+ #include <linux/completion.h>
+ #include <linux/time.h>
+ #include <linux/hw_random.h>
++#include <linux/gpio/driver.h>
++#include <linux/reset.h>
+ 
+ #include "common.h"
+ #include "debug.h"
+@@ -90,7 +92,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
+ 		(_l) &= ((_sz) - 1);		\
+ 	} while (0)
+ 
+-#define ATH_RXBUF               512
++#define ATH_RXBUF               256
+ #define ATH_TXBUF               512
+ #define ATH_TXBUF_RESERVE       5
+ #define ATH_TXMAXTRY            13
+@@ -845,6 +847,9 @@ static inline int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 size)
+ #ifdef CPTCFG_MAC80211_LEDS
+ void ath_init_leds(struct ath_softc *sc);
+ void ath_deinit_leds(struct ath_softc *sc);
++int ath_create_gpio_led(struct ath_softc *sc, int gpio, const char *name,
++			const char *trigger, bool active_low);
++
+ #else
+ static inline void ath_init_leds(struct ath_softc *sc)
+ {
+@@ -981,6 +986,21 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);
+ 
+ #define ATH9K_NUM_CHANCTX  2 /* supports 2 operating channels */
+ 
++struct ath_led {
++	struct list_head list;
++	struct ath_softc *sc;
++	const struct gpio_led *gpio;
++	struct led_classdev cdev;
++};
++
++#ifdef CONFIG_GPIOLIB
++struct ath9k_gpio_chip {
++	struct ath_softc *sc;
++	char label[32];
++	struct gpio_chip gchip;
++};
++#endif
++
+ struct ath_softc {
+ 	struct ieee80211_hw *hw;
+ 	struct device *dev;
+@@ -994,6 +1014,9 @@ struct ath_softc {
+ 	struct ath_hw *sc_ah;
+ 	void __iomem *mem;
+ 	int irq;
++#ifdef CONFIG_OF
++	struct reset_control *reset;
++#endif
+ 	spinlock_t sc_serial_rw;
+ 	spinlock_t sc_pm_lock;
+ 	spinlock_t sc_pcu_lock;
+@@ -1034,9 +1057,12 @@ struct ath_softc {
+ 	spinlock_t chan_lock;
+ 
+ #ifdef CPTCFG_MAC80211_LEDS
+-	bool led_registered;
+-	char led_name[32];
+-	struct led_classdev led_cdev;
++	const char *led_default_trigger;
++	struct list_head leds;
++#ifdef CONFIG_GPIOLIB
++	struct ath9k_gpio_chip *gpiochip;
++	struct platform_device *btnpdev;	/* gpio-keys-polled */
++#endif
+ #endif
+ 
+ #ifdef CPTCFG_ATH9K_DEBUGFS
+diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
+index 86c59bd..49cf036 100644
+--- a/drivers/net/wireless/ath/ath9k/channel.c
++++ b/drivers/net/wireless/ath/ath9k/channel.c
+@@ -15,6 +15,7 @@
+  */
+ 
+ #include "ath9k.h"
++#include "hsr.h"
+ 
+ /* Set/change channels.  If the channel is really being changed, it's done
+  * by reseting the chip.  To accomplish this we must first cleanup any pending
+@@ -22,6 +23,7 @@
+  */
+ static int ath_set_channel(struct ath_softc *sc)
+ {
++	struct device_node *np = sc->dev->of_node;
+ 	struct ath_hw *ah = sc->sc_ah;
+ 	struct ath_common *common = ath9k_hw_common(ah);
+ 	struct ieee80211_hw *hw = sc->hw;
+@@ -42,6 +44,11 @@ static int ath_set_channel(struct ath_softc *sc)
+ 	ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
+ 		chan->center_freq, chandef->width);
+ 
++	if (of_property_read_bool(np, "ubnt,hsr")) {
++		ath9k_hsr_enable(ah, chandef->width, chan->center_freq);
++		ath9k_hsr_status(ah);
++	}
++
+ 	/* update survey stats for the old channel before switching */
+ 	spin_lock_irqsave(&common->cc_lock, flags);
+ 	ath_update_survey_stats(sc);
+diff --git a/drivers/net/wireless/ath/ath9k/common-debug.c b/drivers/net/wireless/ath/ath9k/common-debug.c
+index 7aefb79..944bbe0 100644
+--- a/drivers/net/wireless/ath/ath9k/common-debug.c
++++ b/drivers/net/wireless/ath/ath9k/common-debug.c
+@@ -260,3 +260,110 @@ void ath9k_cmn_debug_phy_err(struct dentry *debugfs_phy,
+ 			    &fops_phy_err);
+ }
+ EXPORT_SYMBOL(ath9k_cmn_debug_phy_err);
++
++static ssize_t read_file_eeprom(struct file *file, char __user *user_buf,
++			     size_t count, loff_t *ppos)
++{
++	struct ath_hw *ah = file->private_data;
++	struct ath_common *common = ath9k_hw_common(ah);
++	int bytes = 0;
++	int pos = *ppos;
++	int size = 4096;
++	u16 val;
++	int i;
++
++	if (AR_SREV_9300_20_OR_LATER(ah))
++		size = 16384;
++
++	if (*ppos < 0)
++		return -EINVAL;
++
++	if (count > size - *ppos)
++		count = size - *ppos;
++
++	for (i = *ppos / 2; count > 0; count -= bytes, *ppos += bytes, i++) {
++		void *from = &val;
++
++		if (!common->bus_ops->eeprom_read(common, i, &val))
++			val = 0xffff;
++
++		if (*ppos % 2) {
++			from++;
++			bytes = 1;
++		} else if (count == 1) {
++			bytes = 1;
++		} else {
++			bytes = 2;
++		}
++		if (copy_to_user(user_buf, from, bytes))
++			return -EFAULT;
++		user_buf += bytes;
++	}
++	return *ppos - pos;
++}
++
++static const struct file_operations fops_eeprom = {
++	.read = read_file_eeprom,
++	.open = simple_open,
++	.owner = THIS_MODULE
++};
++
++void ath9k_cmn_debug_eeprom(struct dentry *debugfs_phy,
++			    struct ath_hw *ah)
++{
++	debugfs_create_file("eeprom", S_IRUSR, debugfs_phy, ah,
++			    &fops_eeprom);
++}
++EXPORT_SYMBOL(ath9k_cmn_debug_eeprom);
++
++static ssize_t read_file_chan_bw(struct file *file, char __user *user_buf,
++			     size_t count, loff_t *ppos)
++{
++	struct ath_hw *ah = file->private_data;
++	struct ath_common *common = ath9k_hw_common(ah);
++	char buf[32];
++	unsigned int len;
++
++	len = sprintf(buf, "0x%08x\n", common->chan_bw);
++	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
++}
++
++static ssize_t write_file_chan_bw(struct file *file, const char __user *user_buf,
++			     size_t count, loff_t *ppos)
++{
++	struct ath_hw *ah = file->private_data;
++	struct ath_common *common = ath9k_hw_common(ah);
++	unsigned long chan_bw;
++	char buf[32];
++	ssize_t len;
++
++	len = min(count, sizeof(buf) - 1);
++	if (copy_from_user(buf, user_buf, len))
++		return -EFAULT;
++
++	buf[len] = '\0';
++	if (kstrtoul(buf, 0, &chan_bw))
++		return -EINVAL;
++
++	common->chan_bw = chan_bw;
++	if (!test_bit(ATH_OP_INVALID, &common->op_flags))
++		common->ieee_ops->config(ah->hw, IEEE80211_CONF_CHANGE_CHANNEL);
++
++	return count;
++}
++
++static const struct file_operations fops_chanbw = {
++	.read = read_file_chan_bw,
++	.write = write_file_chan_bw,
++	.open = simple_open,
++	.owner = THIS_MODULE,
++	.llseek = default_llseek,
++};
++
++void ath9k_cmn_debug_chanbw(struct dentry *debugfs_phy,
++			    struct ath_hw *ah)
++{
++	debugfs_create_file("chanbw", S_IRUSR | S_IWUSR, debugfs_phy, ah,
++			    &fops_chanbw);
++}
++EXPORT_SYMBOL(ath9k_cmn_debug_chanbw);
+diff --git a/drivers/net/wireless/ath/ath9k/common-debug.h b/drivers/net/wireless/ath/ath9k/common-debug.h
+index 54f4c42..7a8b7ed 100644
+--- a/drivers/net/wireless/ath/ath9k/common-debug.h
++++ b/drivers/net/wireless/ath/ath9k/common-debug.h
+@@ -69,6 +69,10 @@ void ath9k_cmn_debug_modal_eeprom(struct dentry *debugfs_phy,
+ 				  struct ath_hw *ah);
+ void ath9k_cmn_debug_base_eeprom(struct dentry *debugfs_phy,
+ 				 struct ath_hw *ah);
++void ath9k_cmn_debug_eeprom(struct dentry *debugfs_phy,
++			    struct ath_hw *ah);
++void ath9k_cmn_debug_chanbw(struct dentry *debugfs_phy,
++			    struct ath_hw *ah);
+ void ath9k_cmn_debug_stat_rx(struct ath_rx_stats *rxstats,
+ 			     struct ath_rx_status *rs);
+ void ath9k_cmn_debug_recv(struct dentry *debugfs_phy,
+diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c
+index 099f3d4..86d4a50 100644
+--- a/drivers/net/wireless/ath/ath9k/common.c
++++ b/drivers/net/wireless/ath/ath9k/common.c
+@@ -297,11 +297,13 @@ EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype);
+ /*
+  * Update internal channel flags.
+  */
+-static void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
++static void ath9k_cmn_update_ichannel(struct ath_common *common,
++				      struct ath9k_channel *ichan,
+ 				      struct cfg80211_chan_def *chandef)
+ {
+ 	struct ieee80211_channel *chan = chandef->chan;
+ 	u16 flags = 0;
++	int width;
+ 
+ 	ichan->channel = chan->center_freq;
+ 	ichan->chan = chan;
+@@ -309,7 +311,19 @@ static void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
+ 	if (chan->band == NL80211_BAND_5GHZ)
+ 		flags |= CHANNEL_5GHZ;
+ 
+-	switch (chandef->width) {
++	switch (common->chan_bw) {
++	case 5:
++		width = NL80211_CHAN_WIDTH_5;
++		break;
++	case 10:
++		width = NL80211_CHAN_WIDTH_10;
++		break;
++	default:
++		width = chandef->width;
++		break;
++	}
++
++	switch (width) {
+ 	case NL80211_CHAN_WIDTH_5:
+ 		flags |= CHANNEL_QUARTER;
+ 		break;
+@@ -342,10 +356,11 @@ struct ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw,
+ 					    struct cfg80211_chan_def *chandef)
+ {
+ 	struct ieee80211_channel *curchan = chandef->chan;
++	struct ath_common *common = ath9k_hw_common(ah);
+ 	struct ath9k_channel *channel;
+ 
+ 	channel = &ah->channels[curchan->hw_value];
+-	ath9k_cmn_update_ichannel(channel, chandef);
++	ath9k_cmn_update_ichannel(common, channel, chandef);
+ 
+ 	return channel;
+ }
+diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
+index 87ffcb4..f081d1d 100644
+--- a/drivers/net/wireless/ath/ath9k/debug.c
++++ b/drivers/net/wireless/ath/ath9k/debug.c
+@@ -123,6 +123,61 @@ static const struct file_operations fops_debug = {
+ 
+ #define DMA_BUF_LEN 1024
+ 
++#ifdef CONFIG_MAC80211_LEDS
++
++static ssize_t write_file_gpio_led(struct file *file, const char __user *ubuf,
++				   size_t count, loff_t *ppos)
++{
++	struct ath_softc *sc = file->private_data;
++	char buf[32], *str, *name, *c;
++	ssize_t len;
++	unsigned int gpio;
++	bool active_low = false;
++
++	len = min(count, sizeof(buf) - 1);
++	if (copy_from_user(buf, ubuf, len))
++		return -EFAULT;
++
++	buf[len] = '\0';
++	name = strchr(buf, ',');
++	if (!name)
++		return -EINVAL;
++
++	*(name++) = 0;
++	if (!*name)
++		return -EINVAL;
++
++	c = strchr(name, '\n');
++	if (c)
++		*c = 0;
++
++	str = buf;
++	if (*str == '!') {
++		str++;
++		active_low = true;
++	}
++
++	if (kstrtouint(str, 0, &gpio) < 0)
++		return -EINVAL;
++
++	if (gpio >= sc->sc_ah->caps.num_gpio_pins)
++		return -EINVAL;
++
++	if (ath_create_gpio_led(sc, gpio, name, NULL, active_low) < 0)
++		return -EINVAL;
++
++	return count;
++}
++
++static const struct file_operations fops_gpio_led = {
++	.write = write_file_gpio_led,
++	.open = simple_open,
++	.owner = THIS_MODULE,
++	.llseek = default_llseek,
++};
++
++#endif
++
+ 
+ static ssize_t read_file_ani(struct file *file, char __user *user_buf,
+ 			     size_t count, loff_t *ppos)
+@@ -1373,6 +1428,50 @@ void ath9k_deinit_debug(struct ath_softc *sc)
+ 	ath9k_cmn_spectral_deinit_debug(&sc->spec_priv);
+ }
+ 
++static ssize_t read_file_diag(struct file *file, char __user *user_buf,
++			     size_t count, loff_t *ppos)
++{
++	struct ath_softc *sc = file->private_data;
++	struct ath_hw *ah = sc->sc_ah;
++	char buf[32];
++	unsigned int len;
++
++	len = sprintf(buf, "0x%08lx\n", ah->diag);
++	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
++}
++
++static ssize_t write_file_diag(struct file *file, const char __user *user_buf,
++			     size_t count, loff_t *ppos)
++{
++	struct ath_softc *sc = file->private_data;
++	struct ath_hw *ah = sc->sc_ah;
++	unsigned long diag;
++	char buf[32];
++	ssize_t len;
++
++	len = min(count, sizeof(buf) - 1);
++	if (copy_from_user(buf, user_buf, len))
++		return -EFAULT;
++
++	buf[len] = '\0';
++	if (kstrtoul(buf, 0, &diag))
++		return -EINVAL;
++
++	ah->diag = diag;
++	ath9k_hw_update_diag(ah);
++
++	return count;
++}
++
++static const struct file_operations fops_diag = {
++	.read = read_file_diag,
++	.write = write_file_diag,
++	.open = simple_open,
++	.owner = THIS_MODULE,
++	.llseek = default_llseek,
++};
++
++
+ int ath9k_init_debug(struct ath_hw *ah)
+ {
+ 	struct ath_common *common = ath9k_hw_common(ah);
+@@ -1392,6 +1491,12 @@ int ath9k_init_debug(struct ath_hw *ah)
+ 	ath9k_tx99_init_debug(sc);
+ 	ath9k_cmn_spectral_init_debug(&sc->spec_priv, sc->debug.debugfs_phy);
+ 
++#ifdef CONFIG_MAC80211_LEDS
++	debugfs_create_file("gpio_led", S_IWUSR,
++			   sc->debug.debugfs_phy, sc, &fops_gpio_led);
++#endif
++	debugfs_create_file("diag", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
++			    sc, &fops_diag);
+ 	debugfs_create_devm_seqfile(sc->dev, "dma", sc->debug.debugfs_phy,
+ 				    read_file_dma);
+ 	debugfs_create_devm_seqfile(sc->dev, "interrupt", sc->debug.debugfs_phy,
+@@ -1431,6 +1536,8 @@ int ath9k_init_debug(struct ath_hw *ah)
+ 
+ 	ath9k_cmn_debug_base_eeprom(sc->debug.debugfs_phy, sc->sc_ah);
+ 	ath9k_cmn_debug_modal_eeprom(sc->debug.debugfs_phy, sc->sc_ah);
++	ath9k_cmn_debug_eeprom(sc->debug.debugfs_phy, sc->sc_ah);
++	ath9k_cmn_debug_chanbw(sc->debug.debugfs_phy, sc->sc_ah);
+ 
+ 	debugfs_create_u32("gpio_mask", 0600,
+ 			   sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
+diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c
+index a8101c9..e1449d7 100644
+--- a/drivers/net/wireless/ath/ath9k/gpio.c
++++ b/drivers/net/wireless/ath/ath9k/gpio.c
+@@ -15,13 +15,211 @@
+  */
+ 
+ #include "ath9k.h"
++#include <linux/ath9k_platform.h>
++#include <linux/gpio.h>
++#include <linux/platform_device.h>
++#include <linux/gpio_keys.h>
++
++#ifdef CPTCFG_MAC80211_LEDS
++
++#ifdef CONFIG_GPIOLIB
++
++/***************/
++/*  GPIO Chip  */
++/***************/
++
++/* gpio_chip handler : set GPIO to input */
++static int ath9k_gpio_pin_cfg_input(struct gpio_chip *chip, unsigned offset)
++{
++	struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
++						  gchip);
++
++	ath9k_hw_gpio_request_in(gc->sc->sc_ah, offset, "ath9k-gpio");
++
++	return 0;
++}
++
++/* gpio_chip handler : set GPIO to output */
++static int ath9k_gpio_pin_cfg_output(struct gpio_chip *chip, unsigned offset,
++				     int value)
++{
++	struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
++						  gchip);
++
++	ath9k_hw_gpio_request_out(gc->sc->sc_ah, offset, "ath9k-gpio",
++				  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
++	ath9k_hw_set_gpio(gc->sc->sc_ah, offset, value);
++
++	return 0;
++}
++
++/* gpio_chip handler : query GPIO direction (0=out, 1=in) */
++static int ath9k_gpio_pin_get_dir(struct gpio_chip *chip, unsigned offset)
++{
++	struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
++						  gchip);
++	struct ath_hw *ah = gc->sc->sc_ah;
++
++	return !((REG_READ(ah, AR_GPIO_OE_OUT(ah)) >> (offset * 2)) & 3);
++}
++
++/* gpio_chip handler : get GPIO pin value */
++static int ath9k_gpio_pin_get(struct gpio_chip *chip, unsigned offset)
++{
++	struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
++						  gchip);
++
++	return ath9k_hw_gpio_get(gc->sc->sc_ah, offset);
++}
++
++/* gpio_chip handler : set GPIO pin to value */
++static void ath9k_gpio_pin_set(struct gpio_chip *chip, unsigned offset,
++			       int value)
++{
++	struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
++						  gchip);
++
++	ath9k_hw_set_gpio(gc->sc->sc_ah, offset, value);
++}
++
++/* register GPIO chip */
++static void ath9k_register_gpio_chip(struct ath_softc *sc)
++{
++	struct ath9k_gpio_chip *gc;
++	struct ath_hw *ah = sc->sc_ah;
++
++	gc = kzalloc(sizeof(struct ath9k_gpio_chip), GFP_KERNEL);
++	if (!gc)
++		return;
++
++	gc->sc = sc;
++	snprintf(gc->label, sizeof(gc->label), "ath9k-%s",
++		 wiphy_name(sc->hw->wiphy));
++#ifdef CONFIG_OF
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,5,0)
++	gc->gchip.parent = sc->dev;
++#else
++	gc->gchip.dev = sc->dev;
++#endif
++#endif
++	gc->gchip.label = gc->label;
++	gc->gchip.base = -1;	/* determine base automatically */
++	gc->gchip.ngpio = ah->caps.num_gpio_pins;
++	gc->gchip.direction_input = ath9k_gpio_pin_cfg_input;
++	gc->gchip.direction_output = ath9k_gpio_pin_cfg_output;
++	gc->gchip.get_direction = ath9k_gpio_pin_get_dir;
++	gc->gchip.get = ath9k_gpio_pin_get;
++	gc->gchip.set = ath9k_gpio_pin_set;
++
++	if (gpiochip_add(&gc->gchip)) {
++		kfree(gc);
++		return;
++	}
++
++#ifdef CONFIG_OF
++	gc->gchip.owner = NULL;
++#endif
++	sc->gpiochip = gc;
++}
++
++/* remove GPIO chip */
++static void ath9k_unregister_gpio_chip(struct ath_softc *sc)
++{
++	struct ath9k_gpio_chip *gc = sc->gpiochip;
++
++	if (!gc)
++		return;
++
++	gpiochip_remove(&gc->gchip);
++	kfree(gc);
++	sc->gpiochip = NULL;
++}
++
++/******************/
++/*  GPIO Buttons  */
++/******************/
++
++/* add GPIO buttons */
++static void ath9k_init_buttons(struct ath_softc *sc)
++{
++	struct ath9k_platform_data *pdata = sc->dev->platform_data;
++	struct platform_device *pdev;
++	struct gpio_keys_platform_data gkpdata;
++	struct gpio_keys_button *bt;
++	int i;
++
++	if (!sc->gpiochip)
++		return;
++
++	if (!pdata || !pdata->btns || !pdata->num_btns)
++		return;
++
++	bt = devm_kmemdup(sc->dev, pdata->btns,
++			  pdata->num_btns * sizeof(struct gpio_keys_button),
++			  GFP_KERNEL);
++	if (!bt)
++		return;
++
++	for (i = 0; i < pdata->num_btns; i++) {
++		if (pdata->btns[i].gpio == sc->sc_ah->led_pin)
++				sc->sc_ah->led_pin = -1;
++
++		ath9k_hw_gpio_request_in(sc->sc_ah, pdata->btns[i].gpio,
++					 "ath9k-gpio");
++		bt[i].gpio = sc->gpiochip->gchip.base + pdata->btns[i].gpio;
++	}
++
++	memset(&gkpdata, 0, sizeof(struct gpio_keys_platform_data));
++	gkpdata.buttons = bt;
++	gkpdata.nbuttons = pdata->num_btns;
++	gkpdata.poll_interval = pdata->btn_poll_interval;
++
++	pdev = platform_device_register_data(sc->dev, "gpio-keys-polled",
++					     PLATFORM_DEVID_AUTO, &gkpdata,
++					     sizeof(gkpdata));
++	if (!IS_ERR_OR_NULL(pdev))
++		sc->btnpdev = pdev;
++	else {
++		sc->btnpdev = NULL;
++		devm_kfree(sc->dev, bt);
++	}
++}
++
++/* remove GPIO buttons */
++static void ath9k_deinit_buttons(struct ath_softc *sc)
++{
++	if (!sc->gpiochip || !sc->btnpdev)
++		return;
++
++	platform_device_unregister(sc->btnpdev);
++
++	sc->btnpdev = NULL;
++}
++
++#else /* CONFIG_GPIOLIB */
++
++static inline void ath9k_register_gpio_chip(struct ath_softc *sc)
++{
++}
++
++static inline void ath9k_unregister_gpio_chip(struct ath_softc *sc)
++{
++}
++
++static inline void ath9k_init_buttons(struct ath_softc *sc)
++{
++}
++
++static inline void ath9k_deinit_buttons(struct ath_softc *sc)
++{
++}
++
++#endif /* CONFIG_GPIOLIB */
+ 
+ /********************************/
+ /*	 LED functions		*/
+ /********************************/
+ 
+-#ifdef CPTCFG_MAC80211_LEDS
+-
+ static void ath_fill_led_pin(struct ath_softc *sc)
+ {
+ 	struct ath_hw *ah = sc->sc_ah;
+@@ -39,62 +237,171 @@ static void ath_fill_led_pin(struct ath_softc *sc)
+ 		else
+ 			ah->led_pin = ATH_LED_PIN_DEF;
+ 	}
++}
++
++static void ath_led_brightness(struct led_classdev *led_cdev,
++			       enum led_brightness brightness)
++{
++	struct ath_led *led = container_of(led_cdev, struct ath_led, cdev);
++	struct ath_softc *sc = led->sc;
++
++	ath9k_ps_wakeup(sc);
++	ath9k_hw_set_gpio(sc->sc_ah, led->gpio->gpio,
++			  (brightness != LED_OFF) ^ led->gpio->active_low);
++	ath9k_ps_restore(sc);
++}
++
++static int ath_add_led(struct ath_softc *sc, struct ath_led *led)
++{
++	const struct gpio_led *gpio = led->gpio;
++	int ret;
++
++	led->cdev.name = gpio->name;
++	led->cdev.default_trigger = gpio->default_trigger;
++	led->cdev.brightness_set = ath_led_brightness;
++
++	ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->cdev);
++	if (ret < 0)
++		return ret;
++
++	led->sc = sc;
++	list_add(&led->list, &sc->leds);
+ 
+ 	/* Configure gpio for output */
+-	ath9k_hw_gpio_request_out(ah, ah->led_pin, "ath9k-led",
++	ath9k_hw_gpio_request_out(sc->sc_ah, gpio->gpio, gpio->name,
+ 				  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+ 
+-	/* LED off, active low */
+-	ath9k_hw_set_gpio(ah, ah->led_pin, ah->config.led_active_high ? 0 : 1);
++	/* Set default LED state */
++	if (gpio->default_state == LEDS_GPIO_DEFSTATE_ON)
++		ath9k_hw_set_gpio(sc->sc_ah, gpio->gpio, !gpio->active_low);
++	else
++		ath9k_hw_set_gpio(sc->sc_ah, gpio->gpio, gpio->active_low);
++
++#ifdef CONFIG_GPIOLIB
++	/* If there is GPIO chip configured, reserve LED pin */
++	if (sc->gpiochip)
++		gpio_request(sc->gpiochip->gchip.base + gpio->gpio, gpio->name);
++#endif
++
++	return 0;
+ }
+ 
+-static void ath_led_brightness(struct led_classdev *led_cdev,
+-			       enum led_brightness brightness)
++int ath_create_gpio_led(struct ath_softc *sc, int gpio_num, const char *name,
++			const char *trigger, bool active_low)
+ {
+-	struct ath_softc *sc = container_of(led_cdev, struct ath_softc, led_cdev);
+-	u32 val = (brightness == LED_OFF);
++	struct ath_led *led;
++	struct gpio_led *gpio;
++	char *_name;
++	int ret;
++
++	led = kzalloc(sizeof(*led) + sizeof(*gpio) + strlen(name) + 1,
++		      GFP_KERNEL);
++	if (!led)
++		return -ENOMEM;
+ 
+-	if (sc->sc_ah->config.led_active_high)
+-		val = !val;
++	led->gpio = gpio = (struct gpio_led *) (led + 1);
++	_name = (char *) (led->gpio + 1);
+ 
+-	ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, val);
++	strcpy(_name, name);
++	gpio->name = _name;
++	gpio->gpio = gpio_num;
++	gpio->active_low = active_low;
++	gpio->default_trigger = trigger;
++
++	ret = ath_add_led(sc, led);
++	if (unlikely(ret < 0))
++		kfree(led);
++
++	return ret;
+ }
+ 
+-void ath_deinit_leds(struct ath_softc *sc)
++static int ath_create_platform_led(struct ath_softc *sc,
++				   const struct gpio_led *gpio)
+ {
+-	if (!sc->led_registered)
+-		return;
++	struct ath_led *led;
++	int ret;
+ 
+-	ath_led_brightness(&sc->led_cdev, LED_OFF);
+-	led_classdev_unregister(&sc->led_cdev);
++	led = kzalloc(sizeof(*led), GFP_KERNEL);
++	if (!led)
++		return -ENOMEM;
+ 
+-	ath9k_hw_gpio_free(sc->sc_ah, sc->sc_ah->led_pin);
++	led->gpio = gpio;
++	ret = ath_add_led(sc, led);
++	if (ret < 0)
++		kfree(led);
++
++	return ret;
++}
++
++void ath_deinit_leds(struct ath_softc *sc)
++{
++	struct ath_led *led;
++
++	ath9k_deinit_buttons(sc);
++	while (!list_empty(&sc->leds)) {
++		led = list_first_entry(&sc->leds, struct ath_led, list);
++#ifdef CONFIG_GPIOLIB
++		/* If there is GPIO chip configured, free LED pin */
++		if (sc->gpiochip)
++			gpio_free(sc->gpiochip->gchip.base + led->gpio->gpio);
++#endif
++		list_del(&led->list);
++		ath_led_brightness(&led->cdev, LED_OFF);
++		led_classdev_unregister(&led->cdev);
++		ath9k_hw_gpio_free(sc->sc_ah, led->gpio->gpio);
++		kfree(led);
++	}
++	ath9k_unregister_gpio_chip(sc);
+ }
+ 
+ void ath_init_leds(struct ath_softc *sc)
+ {
+-	int ret;
++	struct ath9k_platform_data *pdata = sc->dev->platform_data;
++	struct device_node *np = sc->dev->of_node;
++	char led_name[32];
++	const char *trigger;
++	int i;
++
++	INIT_LIST_HEAD(&sc->leds);
+ 
+ 	if (AR_SREV_9100(sc->sc_ah))
+ 		return;
+ 
++	if (!np)
++		ath9k_register_gpio_chip(sc);
++
++	/* setup gpio controller only if requested and skip the led_pin setup */
++	if (of_property_read_bool(np, "gpio-controller")) {
++		ath9k_register_gpio_chip(sc);
++		return;
++	}
++
+ 	ath_fill_led_pin(sc);
++	ath9k_init_buttons(sc);
+ 
+-	if (!ath9k_led_blink)
+-		sc->led_cdev.default_trigger =
+-			ieee80211_get_radio_led_name(sc->hw);
++	if (pdata && pdata->leds && pdata->num_leds)
++		for (i = 0; i < pdata->num_leds; i++) {
++			if (pdata->leds[i].gpio == sc->sc_ah->led_pin)
++				sc->sc_ah->led_pin = -1;
+ 
+-	snprintf(sc->led_name, sizeof(sc->led_name),
+-		"ath9k-%s", wiphy_name(sc->hw->wiphy));
+-	sc->led_cdev.name = sc->led_name;
+-	sc->led_cdev.brightness_set = ath_led_brightness;
++			ath_create_platform_led(sc, &pdata->leds[i]);
++		}
+ 
+-	ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &sc->led_cdev);
+-	if (ret < 0)
++	if (sc->sc_ah->led_pin < 0)
+ 		return;
+ 
+-	sc->led_registered = true;
++	snprintf(led_name, sizeof(led_name), "ath9k-%s",
++		 wiphy_name(sc->hw->wiphy));
++
++	if (ath9k_led_blink)
++		trigger = sc->led_default_trigger;
++	else
++		trigger = ieee80211_get_radio_led_name(sc->hw);
++
++	ath_create_gpio_led(sc, sc->sc_ah->led_pin, led_name, trigger,
++			   !sc->sc_ah->config.led_active_high);
+ }
++
+ #endif
+ 
+ /*******************/
+diff --git a/drivers/net/wireless/ath/ath9k/hsr.c b/drivers/net/wireless/ath/ath9k/hsr.c
+new file mode 100644
+index 0000000..7d12d91
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath9k/hsr.c
+@@ -0,0 +1,247 @@
++/*
++ *
++ * The MIT License (MIT)
++ *
++ * Copyright (c) 2015 Kirill Berezin
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ *
++ */
++
++#include <linux/io.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/time.h>
++#include <linux/bitops.h>
++#include <linux/etherdevice.h>
++#include <linux/rtnetlink.h>
++#include <asm/unaligned.h>
++
++#include "hw.h"
++#include "ath9k.h"
++
++#define HSR_GPIO_CSN 8
++#define HSR_GPIO_CLK 6
++#define HSR_GPIO_DOUT 7
++#define HSR_GPIO_DIN 5
++
++/* delays are in useconds */
++#define HSR_DELAY_HALF_TICK 100
++#define HSR_DELAY_PRE_WRITE 75
++#define HSR_DELAY_FINAL 20000
++#define HSR_DELAY_TRAILING 200
++
++void ath9k_hsr_init(struct ath_hw *ah)
++{
++	ath9k_hw_gpio_request_in(ah, HSR_GPIO_DIN, NULL);
++	ath9k_hw_gpio_request_out(ah, HSR_GPIO_CSN, NULL,
++				  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
++	ath9k_hw_gpio_request_out(ah, HSR_GPIO_CLK, NULL,
++				  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
++	ath9k_hw_gpio_request_out(ah, HSR_GPIO_DOUT, NULL,
++				  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
++
++	ath9k_hw_set_gpio(ah, HSR_GPIO_CSN, 1);
++	ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 0);
++	ath9k_hw_set_gpio(ah, HSR_GPIO_DOUT, 0);
++
++	udelay(HSR_DELAY_TRAILING);
++}
++
++static u32 ath9k_hsr_write_byte(struct ath_hw *ah, int delay, u32 value)
++{
++	struct ath_common *common = ath9k_hw_common(ah);
++	int i;
++	u32 rval = 0;
++
++	udelay(delay);
++
++	ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 0);
++	udelay(HSR_DELAY_HALF_TICK);
++
++	ath9k_hw_set_gpio(ah, HSR_GPIO_CSN, 0);
++	udelay(HSR_DELAY_HALF_TICK);
++
++	for (i = 0; i < 8; ++i) {
++		rval = rval << 1;
++
++		/* pattern is left to right, that is 7-th bit runs first */
++		ath9k_hw_set_gpio(ah, HSR_GPIO_DOUT, (value >> (7 - i)) & 0x1);
++		udelay(HSR_DELAY_HALF_TICK);
++
++		ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 1);
++		udelay(HSR_DELAY_HALF_TICK);
++
++		rval |= ath9k_hw_gpio_get(ah, HSR_GPIO_DIN);
++
++		ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 0);
++		udelay(HSR_DELAY_HALF_TICK);
++	}
++
++	ath9k_hw_set_gpio(ah, HSR_GPIO_CSN, 1);
++	udelay(HSR_DELAY_HALF_TICK);
++
++	ath_dbg(common, CONFIG, "ath9k_hsr_write_byte: write byte %d return value is %d %c\n",
++		value, rval, rval > 32 ? rval : '-');
++
++	return rval & 0xff;
++}
++
++static int ath9k_hsr_write_a_chain(struct ath_hw *ah, char *chain, int items)
++{
++	int status = 0;
++	int i = 0;
++	int err;
++
++	/* a preamble */
++	ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
++	status = ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
++
++	/* clear HSR's reply buffer */
++	if (status) {
++		int loop = 0;
++
++		for (loop = 0; (loop < 42) && status; ++loop)
++			status = ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE,
++						      0);
++
++		if (loop >= 42) {
++			ATH_DBG_WARN(1,
++				     "ath9k_hsr_write_a_chain: can't clear an output buffer after a 42 cycles.\n");
++			return -1;
++		}
++	}
++
++	for (i = 0; (i < items) && (chain[i] != 0); ++i)
++		ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, (u32)chain[i]);
++
++	ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
++	mdelay(HSR_DELAY_FINAL / 1000);
++
++	/* reply */
++	memset(chain, 0, items);
++
++	ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
++	udelay(HSR_DELAY_TRAILING);
++
++	for (i = 0; i < (items - 1); ++i) {
++		u32 ret;
++
++		ret = ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
++		if (ret != 0)
++			chain[i] = (char)ret;
++		else
++			break;
++
++		udelay(HSR_DELAY_TRAILING);
++	}
++
++	if (i <= 1)
++		return 0;
++
++	err = kstrtoint(chain + 1, 10, &i);
++	if (err)
++		return err;
++
++	return i;
++}
++
++int ath9k_hsr_disable(struct ath_hw *ah)
++{
++	char cmd[10] = {'b', '4', '0', 0, 0, 0, 0, 0, 0, 0};
++	int ret;
++
++	ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
++	if ((ret > 0) && (*cmd == 'B'))
++		return 0;
++
++	return -1;
++}
++
++int ath9k_hsr_enable(struct ath_hw *ah, int bw, int fq)
++{
++	char cmd[10];
++	int ret;
++
++	/* Bandwidth argument is 0 sometimes. Assume default 802.11bgn
++	 * 20MHz on invalid values
++	 */
++	if ((bw != 5) && (bw != 10) && (bw != 20) && (bw != 40))
++		bw = 20;
++
++	memset(cmd, 0, sizeof(cmd));
++	*cmd = 'b';
++	snprintf(cmd + 1, 3, "%02d", bw);
++
++	ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
++	if ((*cmd != 'B') || (ret != bw)) {
++		ATH_DBG_WARN(1,
++			     "ath9k_hsr_enable: failed changing bandwidth -> set (%d,%d) reply (%d, %d)\n",
++			     'b', bw, *cmd, ret);
++		return -1;
++	}
++
++	memset(cmd, 0, sizeof(cmd));
++	*cmd = 'x';
++	ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
++	if (*cmd != 'X') {
++		ATH_DBG_WARN(1,
++			     "ath9k_hsr_enable: failed 'x' command -> reply (%d, %d)\n",
++			     *cmd, ret);
++		return -1;
++	}
++
++	memset(cmd, 0, sizeof(cmd));
++	*cmd = 'm';
++	ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
++	if (*cmd != 'M') {
++		ATH_DBG_WARN(1,
++			     "ath9k_hsr_enable: failed 'm' command -> reply (%d, %d)\n",
++			     *cmd, ret);
++		return  -1;
++	}
++
++	memset(cmd, 0, sizeof(cmd));
++	*cmd = 'f';
++	snprintf(cmd + 1, 6, "%05d", fq);
++	ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
++	if ((*cmd != 'F') && (ret != fq)) {
++		ATH_DBG_WARN(1,
++			     "ath9k_hsr_enable: failed set frequency -> reply (%d, %d)\n",
++			     *cmd, ret);
++		return -1;
++	}
++
++	return 0;
++}
++
++int ath9k_hsr_status(struct ath_hw *ah)
++{
++	char cmd[10] = {'s', 0, 0, 0, 0, 0, 0, 0, 0, 0};
++	int ret;
++
++	ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
++	if (*cmd != 'S') {
++		ATH_DBG_WARN(1, "ath9k_hsr_status: returned %d,%d\n", *cmd,
++			     ret);
++		return -1;
++	}
++
++	return 0;
++}
+diff --git a/drivers/net/wireless/ath/ath9k/hsr.h b/drivers/net/wireless/ath/ath9k/hsr.h
+new file mode 100644
+index 0000000..78af444
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath9k/hsr.h
+@@ -0,0 +1,48 @@
++/*
++ * The MIT License (MIT)
++ *
++ * Copyright (c) 2015 Kirill Berezin
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ */
++
++#ifndef HSR_H
++#define HSR_H
++
++#ifdef CPTCFG_ATH9K_UBNTHSR
++
++void ath9k_hsr_init(struct ath_hw *ah);
++int ath9k_hsr_disable(struct ath_hw *ah);
++int ath9k_hsr_enable(struct ath_hw *ah, int bw, int fq);
++int ath9k_hsr_status(struct ath_hw *ah);
++
++#else
++static inline void ath9k_hsr_init(struct ath_hw *ah) {}
++
++static inline int ath9k_hsr_enable(struct ath_hw *ah, int bw, int fq)
++{
++	return 0;
++}
++
++static inline int ath9k_hsr_disable(struct ath_hw *ah) { return 0; }
++static inline int ath9k_hsr_status(struct ath_hw *ah) { return 0; }
++
++#endif
++
++#endif /* HSR_H */
+diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
+index f7c6d9b..5c015ac 100644
+--- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
++++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
+@@ -514,6 +514,8 @@ int ath9k_htc_init_debug(struct ath_hw *ah)
+ 
+ 	ath9k_cmn_debug_base_eeprom(priv->debug.debugfs_phy, priv->ah);
+ 	ath9k_cmn_debug_modal_eeprom(priv->debug.debugfs_phy, priv->ah);
++	ath9k_cmn_debug_eeprom(priv->debug.debugfs_phy, priv->ah);
++	ath9k_cmn_debug_chanbw(priv->debug.debugfs_phy, priv->ah);
+ 
+ 	return 0;
+ }
+diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+index fa02d9a..53e49b6 100644
+--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
++++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+@@ -631,6 +631,7 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv,
+ 	priv->ah = ah;
+ 
+ 	common = ath9k_hw_common(ah);
++	common->ieee_ops = &ath9k_htc_ops;
+ 	common->ops = &ah->reg_ops;
+ 	common->ps_ops = &ath9k_htc_ps_ops;
+ 	common->bus_ops = &ath9k_usb_bus_ops;
+@@ -746,9 +747,9 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
+ 
+ 	hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN |
+ 			    WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+-			    WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+-
+-	hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
++			    WIPHY_FLAG_HAS_CHANNEL_SWITCH |
++			    WIPHY_FLAG_SUPPORTS_5_10_MHZ |
++			    WIPHY_FLAG_SUPPORTS_TDLS;
+ 
+ 	hw->queues = 4;
+ 	hw->max_listen_interval = 1;
+diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h
+index 174d716..605abe1 100644
+--- a/drivers/net/wireless/ath/ath9k/hw-ops.h
++++ b/drivers/net/wireless/ath/ath9k/hw-ops.h
+@@ -100,6 +100,12 @@ static inline void ath9k_hw_tx99_set_txpower(struct ath_hw *ah, u8 power)
+ 		ath9k_hw_ops(ah)->tx99_set_txpower(ah, power);
+ }
+ 
++static inline void ath9k_hw_get_adc_entropy(struct ath_hw *ah,
++		u8 *buf, size_t len)
++{
++	ath9k_hw_ops(ah)->get_adc_entropy(ah, buf, len);
++}
++
+ #ifdef CPTCFG_ATH9K_BTCOEX_SUPPORT
+ 
+ static inline void ath9k_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable)
+diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
+index fafe1f0..f53964b 100644
+--- a/drivers/net/wireless/ath/ath9k/hw.c
++++ b/drivers/net/wireless/ath/ath9k/hw.c
+@@ -247,6 +247,19 @@ void ath9k_hw_get_channel_centers(struct ath_hw *ah,
+ 		centers->synth_center + (extoff * HT40_CHANNEL_CENTER_SHIFT);
+ }
+ 
++static inline void ath9k_hw_disable_pll_lock_detect(struct ath_hw *ah)
++{
++	/* On AR9330 and AR9340 devices, some PHY registers must be
++	 * tuned to gain better stability/performance. These registers
++	 * might be changed while doing wlan reset so the registers must
++	 * be reprogrammed after each reset.
++	 */
++	REG_CLR_BIT(ah, AR_PHY_USB_CTRL1, BIT(20));
++	REG_RMW(ah, AR_PHY_USB_CTRL2,
++		(1 << 21) | (0xf << 22),
++		(1 << 21) | (0x3 << 22));
++}
++
+ /******************/
+ /* Chip Revisions */
+ /******************/
+@@ -402,13 +415,8 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
+ 
+ 	ah->config.rx_intr_mitigation = true;
+ 
+-	if (AR_SREV_9300_20_OR_LATER(ah)) {
+-		ah->config.rimt_last = 500;
+-		ah->config.rimt_first = 2000;
+-	} else {
+-		ah->config.rimt_last = 250;
+-		ah->config.rimt_first = 700;
+-	}
++	ah->config.rimt_last = 250;
++	ah->config.rimt_first = 500;
+ 
+ 	if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
+ 		ah->config.pll_pwrsave = 7;
+@@ -667,6 +675,7 @@ int ath9k_hw_init(struct ath_hw *ah)
+ 
+ 	/* These are all the AR5008/AR9001/AR9002/AR9003 hardware family of chipsets */
+ 	switch (ah->hw_version.devid) {
++	case AR9300_DEVID_INVALID:
+ 	case AR5416_DEVID_PCI:
+ 	case AR5416_DEVID_PCIE:
+ 	case AR5416_AR9100_DEVID:
+@@ -1311,39 +1320,56 @@ void ath9k_hw_get_delta_slope_vals(struct ath_hw *ah, u32 coef_scaled,
+ 	*coef_exponent = coef_exp - 16;
+ }
+ 
+-/* AR9330 WAR:
+- * call external reset function to reset WMAC if:
+- * - doing a cold reset
+- * - we have pending frames in the TX queues.
+- */
+-static bool ath9k_hw_ar9330_reset_war(struct ath_hw *ah, int type)
++static bool ath9k_hw_need_external_reset(struct ath_hw *ah, int type)
+ {
+-	int i, npend = 0;
++	int i;
+ 
+-	for (i = 0; i < AR_NUM_QCU; i++) {
+-		npend = ath9k_hw_numtxpending(ah, i);
+-		if (npend)
+-			break;
++	if (type == ATH9K_RESET_COLD)
++		return true;
++
++	if (AR_SREV_9550(ah))
++		return true;
++
++	/* AR9330 WAR:
++	 * call external reset function to reset WMAC if:
++	 * - doing a cold reset
++	 * - we have pending frames in the TX queues.
++	 */
++	if (AR_SREV_9330(ah)) {
++		for (i = 0; i < AR_NUM_QCU; i++) {
++			if (ath9k_hw_numtxpending(ah, i))
++				return true;
++		}
+ 	}
+ 
+-	if (ah->external_reset &&
+-	    (npend || type == ATH9K_RESET_COLD)) {
+-		int reset_err = 0;
++	return false;
++}
+ 
+-		ath_dbg(ath9k_hw_common(ah), RESET,
+-			"reset MAC via external reset\n");
++static bool ath9k_hw_external_reset(struct ath_hw *ah, int type)
++{
++	int err;
+ 
+-		reset_err = ah->external_reset();
+-		if (reset_err) {
+-			ath_err(ath9k_hw_common(ah),
+-				"External reset failed, err=%d\n",
+-				reset_err);
+-			return false;
+-		}
++	if (!ah->external_reset || !ath9k_hw_need_external_reset(ah, type))
++		return true;
+ 
+-		REG_WRITE(ah, AR_RTC_RESET(ah), 1);
++	ath_dbg(ath9k_hw_common(ah), RESET,
++		"reset MAC via external reset\n");
++
++	err = ah->external_reset();
++	if (err) {
++		ath_err(ath9k_hw_common(ah),
++			"External reset failed, err=%d\n", err);
++		return false;
+ 	}
+ 
++	if (AR_SREV_9550(ah)) {
++		REG_WRITE(ah, AR_RTC_RESET(ah), 0);
++		udelay(10);
++	}
++
++	REG_WRITE(ah, AR_RTC_RESET(ah), 1);
++	udelay(10);
++
+ 	return true;
+ }
+ 
+@@ -1396,24 +1422,24 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type)
+ 			rst_flags |= AR_RTC_RC_MAC_COLD;
+ 	}
+ 
+-	if (AR_SREV_9330(ah)) {
+-		if (!ath9k_hw_ar9330_reset_war(ah, type))
+-			return false;
+-	}
+-
+ 	if (ath9k_hw_mci_is_enabled(ah))
+ 		ar9003_mci_check_gpm_offset(ah);
+ 
+ 	/* DMA HALT added to resolve ar9300 and ar9580 bus error during
+-	 * RTC_RC reg read
++	 * RTC_RC reg read. Also needed for AR9550 external reset
+ 	 */
+-	if (AR_SREV_9300(ah) || AR_SREV_9580(ah)) {
++	if (AR_SREV_9300(ah) || AR_SREV_9580(ah) || AR_SREV_9550(ah)) {
+ 		REG_SET_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
+ 		ath9k_hw_wait(ah, AR_CFG, AR_CFG_HALT_ACK, AR_CFG_HALT_ACK,
+ 			      20 * AH_WAIT_TIMEOUT);
+-		REG_CLR_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
+ 	}
+ 
++	if (!AR_SREV_9100(ah))
++		ath9k_hw_external_reset(ah, type);
++
++	if (AR_SREV_9300(ah) || AR_SREV_9580(ah))
++		REG_CLR_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
++
+ 	REG_WRITE(ah, AR_RTC_RC(ah), rst_flags);
+ 
+ 	REGWRITE_BUFFER_FLUSH(ah);
+@@ -1434,8 +1460,15 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type)
+ 	if (!AR_SREV_9100(ah))
+ 		REG_WRITE(ah, AR_RC, 0);
+ 
+-	if (AR_SREV_9100(ah))
++	if (AR_SREV_9100(ah)) {
++		/* Reset the AHB-WMAC interface */
++		if (ah->external_reset)
++			ah->external_reset();
+ 		udelay(50);
++	}
++
++	if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
++		ath9k_hw_disable_pll_lock_detect(ah);
+ 
+ 	return true;
+ }
+@@ -1536,6 +1569,9 @@ static bool ath9k_hw_chip_reset(struct ath_hw *ah,
+ 		ar9003_hw_internal_regulator_apply(ah);
+ 	ath9k_hw_init_pll(ah, chan);
+ 
++	if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
++		ath9k_hw_disable_pll_lock_detect(ah);
++
+ 	return true;
+ }
+ 
+@@ -1842,8 +1878,14 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan)
+ 	if (AR_SREV_9271(ah))
+ 		ar9002_hw_load_ani_reg(ah, chan);
+ 
++	if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
++		ath9k_hw_disable_pll_lock_detect(ah);
++
+ 	return 0;
+ fail:
++	if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
++		ath9k_hw_disable_pll_lock_detect(ah);
++
+ 	return -EINVAL;
+ }
+ 
+@@ -1864,6 +1906,20 @@ u32 ath9k_hw_get_tsf_offset(struct timespec64 *last, struct timespec64 *cur)
+ }
+ EXPORT_SYMBOL(ath9k_hw_get_tsf_offset);
+ 
++void ath9k_hw_update_diag(struct ath_hw *ah)
++{
++	if (test_bit(ATH_DIAG_DISABLE_RX, &ah->diag))
++		REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
++	else
++		REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
++
++	if (test_bit(ATH_DIAG_DISABLE_TX, &ah->diag))
++		REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_LOOP_BACK);
++	else
++		REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_LOOP_BACK);
++}
++EXPORT_SYMBOL(ath9k_hw_update_diag);
++
+ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
+ 		   struct ath9k_hw_cal_data *caldata, bool fastcc)
+ {
+@@ -2072,6 +2128,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
+ 		ar9003_hw_disable_phy_restart(ah);
+ 
+ 	ath9k_hw_apply_gpio_override(ah);
++	ath9k_hw_update_diag(ah);
+ 
+ 	if (AR_SREV_9565(ah) && common->bt_ant_diversity)
+ 		REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON);
+@@ -2082,6 +2139,9 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
+ 		ath9k_hw_set_radar_params(ah);
+ 	}
+ 
++	if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
++		ath9k_hw_disable_pll_lock_detect(ah);
++
+ 	return 0;
+ }
+ EXPORT_SYMBOL(ath9k_hw_reset);
+@@ -2956,7 +3016,8 @@ void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan,
+ {
+ 	struct ath_regulatory *reg = ath9k_hw_regulatory(ah);
+ 	struct ieee80211_channel *channel;
+-	int chan_pwr, new_pwr;
++	int chan_pwr, new_pwr, max_gain;
++	int ant_gain, ant_reduction = 0;
+ 	u16 ctl = NO_CTL;
+ 
+ 	if (!chan)
+@@ -2968,9 +3029,18 @@ void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan,
+ 	channel = chan->chan;
+ 	chan_pwr = min_t(int, channel->max_power * 2, MAX_COMBINED_POWER);
+ 	new_pwr = min_t(int, chan_pwr, reg->power_limit);
++	max_gain = chan_pwr - new_pwr + channel->max_antenna_gain * 2;
++
++	ant_gain = get_antenna_gain(ah, chan);
++	if (ant_gain > max_gain)
++		ant_reduction = ant_gain - max_gain;
++
++	/* FCC allows maximum antenna gain of 6 dBi */
++	if (reg->region == NL80211_DFS_FCC)
++		ant_reduction = max_t(int, ant_reduction - 12, 0);
+ 
+ 	ah->eep_ops->set_txpower(ah, chan, ctl,
+-				 get_antenna_gain(ah, chan), new_pwr, test);
++				 ant_reduction, new_pwr, test);
+ }
+ 
+ void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit, bool test)
+diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
+index 6243626..8d756dc 100644
+--- a/drivers/net/wireless/ath/ath9k/hw.h
++++ b/drivers/net/wireless/ath/ath9k/hw.h
+@@ -36,6 +36,7 @@
+ 
+ #define ATHEROS_VENDOR_ID	0x168c
+ 
++#define AR9300_DEVID_INVALID	0xabcd
+ #define AR5416_DEVID_PCI	0x0023
+ #define AR5416_DEVID_PCIE	0x0024
+ #define AR9160_DEVID_PCI	0x0027
+@@ -521,6 +522,12 @@ enum {
+ 	ATH9K_RESET_COLD,
+ };
+ 
++enum {
++	ATH_DIAG_DISABLE_RX,
++	ATH_DIAG_DISABLE_TX,
++	ATH_DIAG_TRIGGER_ERROR,
++};
++
+ struct ath9k_hw_version {
+ 	u32 magic;
+ 	u16 devid;
+@@ -716,6 +723,7 @@ struct ath_spec_scan {
+  * @config_pci_powersave:
+  * @calibrate: periodic calibration for NF, ANI, IQ, ADC gain, ADC-DC
+  *
++ * @get_adc_entropy: get entropy from the raw ADC I/Q output
+  * @spectral_scan_config: set parameters for spectral scan and enable/disable it
+  * @spectral_scan_trigger: trigger a spectral scan run
+  * @spectral_scan_wait: wait for a spectral scan run to finish
+@@ -738,6 +746,7 @@ struct ath_hw_ops {
+ 			struct ath_hw_antcomb_conf *antconf);
+ 	void (*antdiv_comb_conf_set)(struct ath_hw *ah,
+ 			struct ath_hw_antcomb_conf *antconf);
++	void (*get_adc_entropy)(struct ath_hw *ah, u8 *buf, size_t len);
+ 	void (*spectral_scan_config)(struct ath_hw *ah,
+ 				     struct ath_spec_scan *param);
+ 	void (*spectral_scan_trigger)(struct ath_hw *ah);
+@@ -809,6 +818,8 @@ struct ath_hw {
+ 	u32 ah_flags;
+ 	s16 nf_override;
+ 
++	unsigned long diag;
++
+ 	bool reset_power_on;
+ 	bool htc_reset_init;
+ 
+@@ -1078,6 +1089,7 @@ void ath9k_hw_check_nav(struct ath_hw *ah);
+ bool ath9k_hw_check_alive(struct ath_hw *ah);
+ 
+ bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode);
++void ath9k_hw_update_diag(struct ath_hw *ah);
+ 
+ /* Generic hw timer primitives */
+ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
+diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
+index 2458387..2f5266c 100644
+--- a/drivers/net/wireless/ath/ath9k/init.c
++++ b/drivers/net/wireless/ath/ath9k/init.c
+@@ -48,7 +48,7 @@ int ath9k_modparam_nohwcrypt;
+ module_param_named(nohwcrypt, ath9k_modparam_nohwcrypt, int, 0444);
+ MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption");
+ 
+-int ath9k_led_blink;
++int ath9k_led_blink = 1;
+ module_param_named(blink, ath9k_led_blink, int, 0444);
+ MODULE_PARM_DESC(blink, "Enable LED blink on activity");
+ 
+@@ -696,6 +696,12 @@ static int ath9k_of_init(struct ath_softc *sc)
+ 	return 0;
+ }
+ 
++static void ath9k_of_gpio_mask(struct ath_softc *sc)
++{
++	of_property_read_u32(sc->dev->of_node, "qca,gpio-mask",
++			     &sc->sc_ah->caps.gpio_mask);
++}
++
+ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
+ 			    const struct ath_bus_ops *bus_ops)
+ {
+@@ -733,6 +739,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
+ 	if (!ath9k_is_chanctx_enabled())
+ 		sc->cur_chan->hw_queue_base = 0;
+ 
++	common->ieee_ops = &ath9k_ops;
+ 	common->ops = &ah->reg_ops;
+ 	common->bus_ops = bus_ops;
+ 	common->ps_ops = &ath9k_ps_ops;
+@@ -803,6 +810,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
+ 	if (ret)
+ 		goto err_hw;
+ 
++	/* GPIO mask quirk */
++	ath9k_of_gpio_mask(sc);
++
+ 	ret = ath9k_init_queues(sc);
+ 	if (ret)
+ 		goto err_queues;
+@@ -870,7 +880,8 @@ static void ath9k_init_txpower_limits(struct ath_softc *sc)
+ 	if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
+ 		ath9k_init_band_txpower(sc, NL80211_BAND_5GHZ);
+ 
+-	ah->curchan = curchan;
++	if (curchan)
++		ah->curchan = curchan;
+ }
+ 
+ static const struct ieee80211_iface_limit if_limits[] = {
+@@ -882,6 +893,7 @@ static const struct ieee80211_iface_limit if_limits[] = {
+ 				 BIT(NL80211_IFTYPE_AP) },
+ 	{ .max = 1,	.types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ 				 BIT(NL80211_IFTYPE_P2P_GO) },
++	{ .max = 1,	.types = BIT(NL80211_IFTYPE_ADHOC) },
+ };
+ 
+ #ifdef CPTCFG_ATH9K_CHANNEL_CONTEXT
+@@ -962,6 +974,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
+ 	ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+ 	ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
+ 	ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
++	ieee80211_hw_set(hw, MFP_CAPABLE);
+ 
+ 	if (ath9k_ps_enable)
+ 		ieee80211_hw_set(hw, SUPPORTS_PS);
+@@ -974,9 +987,6 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
+ 				IEEE80211_RADIOTAP_MCS_HAVE_STBC;
+ 	}
+ 
+-	if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || ath9k_modparam_nohwcrypt)
+-		ieee80211_hw_set(hw, MFP_CAPABLE);
+-
+ 	hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR |
+ 			       NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
+ 			       NL80211_FEATURE_P2P_GO_CTWIN;
+@@ -1049,6 +1059,18 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
+ 	wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
+ }
+ 
++static void ath_get_initial_entropy(struct ath_softc *sc)
++{
++	struct ath_hw *ah = sc->sc_ah;
++	char buf[256];
++
++	/* reuse last channel initialized by the tx power test */
++	ath9k_hw_reset(ah, ah->curchan, NULL, false);
++
++	ath9k_hw_get_adc_entropy(ah, buf, sizeof(buf));
++	add_device_randomness(buf, sizeof(buf));
++}
++
+ int ath9k_init_device(u16 devid, struct ath_softc *sc,
+ 		    const struct ath_bus_ops *bus_ops)
+ {
+@@ -1089,13 +1111,15 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
+ 
+ #ifdef CPTCFG_MAC80211_LEDS
+ 	/* must be initialized before ieee80211_register_hw */
+-	sc->led_cdev.default_trigger = ieee80211_create_tpt_led_trigger(sc->hw,
++	sc->led_default_trigger = ieee80211_create_tpt_led_trigger(sc->hw,
+ 		IEEE80211_TPT_LEDTRIG_FL_RADIO, ath9k_tpt_blink,
+ 		ARRAY_SIZE(ath9k_tpt_blink));
+ #endif
+ 
+ 	wiphy_read_of_freq_limits(hw->wiphy);
+ 
++	ath_get_initial_entropy(sc);
++
+ 	/* Register with mac80211 */
+ 	error = ieee80211_register_hw(hw);
+ 	if (error)
+@@ -1179,25 +1203,25 @@ static int __init ath9k_init(void)
+ {
+ 	int error;
+ 
+-	error = ath_pci_init();
++	error = ath_ahb_init();
+ 	if (error < 0) {
+-		pr_err("No PCI devices found, driver not installed\n");
+ 		error = -ENODEV;
+ 		goto err_out;
+ 	}
+ 
+-	error = ath_ahb_init();
++	error = ath_pci_init();
+ 	if (error < 0) {
++		pr_err("No PCI devices found, driver not installed\n");
+ 		error = -ENODEV;
+-		goto err_pci_exit;
++		goto err_ahb_exit;
+ 	}
+ 
+ 	dmi_check_system(ath9k_quirks);
+ 
+ 	return 0;
+ 
+- err_pci_exit:
+-	ath_pci_exit();
++ err_ahb_exit:
++	ath_ahb_exit();
+  err_out:
+ 	return error;
+ }
+diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
+index b070403..d8d14a5 100644
+--- a/drivers/net/wireless/ath/ath9k/mac.c
++++ b/drivers/net/wireless/ath/ath9k/mac.c
+@@ -678,13 +678,18 @@ void ath9k_hw_startpcureceive(struct ath_hw *ah, bool is_scanning)
+ 
+ 	ath9k_ani_reset(ah, is_scanning);
+ 
+-	REG_CLR_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
++	REG_CLR_BIT(ah, AR_DIAG_SW,
++		    AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT | AR_DIAG_FORCE_RX_CLEAR);
+ }
+ EXPORT_SYMBOL(ath9k_hw_startpcureceive);
+ 
+ void ath9k_hw_abortpcurecv(struct ath_hw *ah)
+ {
+-	REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_ABORT | AR_DIAG_RX_DIS);
++	u32 reg = AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT;
++
++	if (!IS_ENABLED(CPTCFG_ATH9K_TX99))
++		reg |= AR_DIAG_FORCE_RX_CLEAR;
++	REG_SET_BIT(ah, AR_DIAG_SW, reg);
+ 
+ 	ath9k_hw_disable_mib_counters(ah);
+ }
+diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
+index 9310844..1e3484a 100644
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -18,6 +18,7 @@
+ #include <linux/delay.h>
+ #include "ath9k.h"
+ #include "btcoex.h"
++#include "hsr.h"
+ 
+ static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 			u32 queues, bool drop);
+@@ -537,6 +538,11 @@ irqreturn_t ath_isr(int irq, void *dev)
+ 		return IRQ_HANDLED;
+ 	}
+ 
++	if (test_bit(ATH_DIAG_TRIGGER_ERROR, &ah->diag)) {
++		status |= ATH9K_INT_FATAL;
++		clear_bit(ATH_DIAG_TRIGGER_ERROR, &ah->diag);
++	}
++
+ 	/*
+ 	 * If there are no status bits set, then this interrupt was not
+ 	 * for me (should have been caught above).
+@@ -653,6 +659,7 @@ void ath_reset_work(struct work_struct *work)
+ static int ath9k_start(struct ieee80211_hw *hw)
+ {
+ 	struct ath_softc *sc = hw->priv;
++	struct device_node *np = sc->dev->of_node;
+ 	struct ath_hw *ah = sc->sc_ah;
+ 	struct ath_common *common = ath9k_hw_common(ah);
+ 	struct ieee80211_channel *curchan = sc->cur_chan->chandef.chan;
+@@ -731,6 +738,11 @@ static int ath9k_start(struct ieee80211_hw *hw)
+ 					  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+ 	}
+ 
++	if (of_property_read_bool(np, "ubnt,hsr")) {
++		ath9k_hsr_init(ah);
++		ath9k_hsr_disable(ah);
++	}
++
+ 	/*
+ 	 * Reset key cache to sane defaults (all entries cleared) instead of
+ 	 * semi-random values after suspend/resume.
+diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
+index 6421e49..20c7095 100644
+--- a/drivers/net/wireless/ath/ath9k/pci.c
++++ b/drivers/net/wireless/ath/ath9k/pci.c
+@@ -772,6 +772,7 @@ static const struct pci_device_id ath_pci_id_table[] = {
+ 	  .driver_data = ATH9K_PCI_BT_ANT_DIV },
+ #endif
+ 
++	{ PCI_VDEVICE(ATHEROS, 0xabcd) }, /* PCI-E  internal chip default ID */
+ 	{ 0 }
+ };
+ 
+diff --git a/drivers/net/wireless/ath/ath9k/phy.h b/drivers/net/wireless/ath/ath9k/phy.h
+index 4a1b992..af667a3 100644
+--- a/drivers/net/wireless/ath/ath9k/phy.h
++++ b/drivers/net/wireless/ath/ath9k/phy.h
+@@ -48,6 +48,9 @@
+ #define AR_PHY_PLL_CONTROL 0x16180
+ #define AR_PHY_PLL_MODE 0x16184
+ 
++#define AR_PHY_USB_CTRL1	0x16c84
++#define AR_PHY_USB_CTRL2	0x16c88
++
+ enum ath9k_ant_div_comb_lna_conf {
+ 	ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2,
+ 	ATH_ANT_DIV_COMB_LNA2,
+diff --git a/local-symbols b/local-symbols
+index 3421d93..3b2db43 100644
+--- a/local-symbols
++++ b/local-symbols
+@@ -121,6 +121,7 @@ ATH9K_WOW=
+ ATH9K_RFKILL=
+ ATH9K_CHANNEL_CONTEXT=
+ ATH9K_PCOEM=
++ATH9K_UBNTHSR=
+ ATH9K_PCI_NO_EEPROM=
+ ATH9K_HTC=
+ ATH9K_HTC_DEBUGFS=
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0004-sync-backports-patches-ath9k.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0004-sync-backports-patches-ath9k.patch
deleted file mode 100644
index 93b4572..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0004-sync-backports-patches-ath9k.patch
+++ /dev/null
@@ -1,2508 +0,0 @@
-From 58736b0bd1ae576889c23c0639aa36f29ab6a74f Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Tue, 12 Mar 2024 11:26:51 +0800
-Subject: [PATCH 04/61] sync backports patches/ath9k
-
----
- drivers/net/wireless/ath/ath.h                |   2 +
- drivers/net/wireless/ath/ath9k/Kconfig        |  13 +
- drivers/net/wireless/ath/ath9k/Makefile       |   1 +
- drivers/net/wireless/ath/ath9k/ahb.c          | 260 ++++++++++++-
- drivers/net/wireless/ath/ath9k/ani.h          |   2 +-
- drivers/net/wireless/ath/ath9k/ar5008_phy.c   |  72 ++--
- drivers/net/wireless/ath/ath9k/ar9002_phy.h   |  11 +
- drivers/net/wireless/ath/ath9k/ar9003_phy.c   |  95 +----
- drivers/net/wireless/ath/ath9k/ath9k.h        |  34 +-
- drivers/net/wireless/ath/ath9k/channel.c      |   7 +
- drivers/net/wireless/ath/ath9k/common-debug.c | 107 +++++
- drivers/net/wireless/ath/ath9k/common-debug.h |   4 +
- drivers/net/wireless/ath/ath9k/common.c       |  21 +-
- drivers/net/wireless/ath/ath9k/debug.c        | 107 +++++
- drivers/net/wireless/ath/ath9k/gpio.c         | 365 ++++++++++++++++--
- drivers/net/wireless/ath/ath9k/hsr.c          | 247 ++++++++++++
- drivers/net/wireless/ath/ath9k/hsr.h          |  48 +++
- .../net/wireless/ath/ath9k/htc_drv_debug.c    |   2 +
- drivers/net/wireless/ath/ath9k/htc_drv_init.c |   7 +-
- drivers/net/wireless/ath/ath9k/hw-ops.h       |   6 +
- drivers/net/wireless/ath/ath9k/hw.c           | 154 ++++++--
- drivers/net/wireless/ath/ath9k/hw.h           |  12 +
- drivers/net/wireless/ath/ath9k/init.c         |  48 ++-
- drivers/net/wireless/ath/ath9k/mac.c          |   9 +-
- drivers/net/wireless/ath/ath9k/main.c         |  12 +
- drivers/net/wireless/ath/ath9k/pci.c          |   1 +
- drivers/net/wireless/ath/ath9k/phy.h          |   3 +
- include/linux/ath9k_platform.h                |   7 +
- local-symbols                                 |   1 +
- 29 files changed, 1438 insertions(+), 220 deletions(-)
- create mode 100644 drivers/net/wireless/ath/ath9k/hsr.c
- create mode 100644 drivers/net/wireless/ath/ath9k/hsr.h
-
-diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
-index 888c5e2..f7e2e89 100644
---- a/drivers/net/wireless/ath/ath.h
-+++ b/drivers/net/wireless/ath/ath.h
-@@ -153,6 +153,7 @@ struct ath_common {
- 	int debug_mask;
- 	enum ath_device_state state;
- 	unsigned long op_flags;
-+	u32 chan_bw;
- 
- 	struct ath_ani ani;
- 
-@@ -181,6 +182,7 @@ struct ath_common {
- 	const struct ath_ops *ops;
- 	const struct ath_bus_ops *bus_ops;
- 	const struct ath_ps_ops *ps_ops;
-+	const struct ieee80211_ops *ieee_ops;
- 
- 	bool btcoex_enabled;
- 	bool disable_ani;
-diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig
-index b97598f..d3e72a8 100644
---- a/drivers/net/wireless/ath/ath9k/Kconfig
-+++ b/drivers/net/wireless/ath/ath9k/Kconfig
-@@ -58,6 +58,19 @@ config ATH9K_AHB
- 	  Say Y, if you have a SoC with a compatible built-in
- 	  wireless MAC. Say N if unsure.
- 
-+config ATH9K_UBNTHSR
-+	bool "Ubiquiti UniFi Outdoor Plus HSR support"
-+	depends on ATH9K
-+	---help---
-+	  This options enables code to control the HSR RF
-+	  filter in the receive path of the Ubiquiti UniFi
-+	  Outdoor Plus access point.
-+
-+	  Say Y if you want to use the access point. The
-+	  code will only be used if the device is detected,
-+	  so it does not harm other setup other than occupying
-+	  a bit of memory.
-+
- config ATH9K_DEBUGFS
- 	bool "Atheros ath9k debugging"
- 	depends on ATH9K && DEBUG_FS && MAC80211_DEBUGFS
-diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
-index 847c8a8..6427bc6 100644
---- a/drivers/net/wireless/ath/ath9k/Makefile
-+++ b/drivers/net/wireless/ath/ath9k/Makefile
-@@ -17,6 +17,7 @@ ath9k-$(CPTCFG_ATH9K_DFS_CERTIFIED) += dfs.o
- ath9k-$(CPTCFG_ATH9K_TX99) += tx99.o
- ath9k-$(CPTCFG_ATH9K_WOW) += wow.o
- ath9k-$(CPTCFG_ATH9K_HWRNG) += rng.o
-+ath9k-$(CPTCFG_ATH9K_UBNTHSR) += hsr.o
- 
- ath9k-$(CPTCFG_ATH9K_DEBUGFS) += debug.o
- 
-diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c
-index 1a6697b..f9a76e7 100644
---- a/drivers/net/wireless/ath/ath9k/ahb.c
-+++ b/drivers/net/wireless/ath/ath9k/ahb.c
-@@ -20,7 +20,15 @@
- #include <linux/platform_device.h>
- #include <linux/module.h>
- #include <linux/mod_devicetable.h>
-+#include <linux/of_device.h>
- #include "ath9k.h"
-+#include <linux/ath9k_platform.h>
-+
-+#ifdef CONFIG_OF
-+#include <asm/mach-ath79/ath79.h>
-+#include <asm/mach-ath79/ar71xx_regs.h>
-+#include <linux/mtd/mtd.h>
-+#endif
- 
- static const struct platform_device_id ath9k_platform_id_table[] = {
- 	{
-@@ -69,6 +77,236 @@ static const struct ath_bus_ops ath_ahb_bus_ops  = {
- 	.eeprom_read = ath_ahb_eeprom_read,
- };
- 
-+#ifdef CONFIG_OF
-+
-+#define QCA955X_DDR_CTL_CONFIG          0x108
-+#define QCA955X_DDR_CTL_CONFIG_ACT_WMAC BIT(23)
-+
-+static int of_get_wifi_cal(struct device_node *np, struct ath9k_platform_data *pdata)
-+{
-+#ifdef CONFIG_MTD
-+	struct device_node *mtd_np = NULL;
-+	size_t retlen;
-+	int size, ret;
-+	struct mtd_info *mtd;
-+	const char *part;
-+	const __be32 *list;
-+	phandle phandle;
-+
-+	list = of_get_property(np, "mtd-cal-data", &size);
-+	if (!list)
-+		return 0;
-+
-+	if (size != (2 * sizeof(*list)))
-+		return 1;
-+
-+	phandle = be32_to_cpup(list++);
-+	if (phandle)
-+		mtd_np = of_find_node_by_phandle(phandle);
-+
-+	if (!mtd_np)
-+		return 1;
-+
-+	part = of_get_property(mtd_np, "label", NULL);
-+	if (!part)
-+		part = mtd_np->name;
-+
-+	mtd = get_mtd_device_nm(part);
-+	if (IS_ERR(mtd))
-+		return 1;
-+
-+	ret = mtd_read(mtd, be32_to_cpup(list), sizeof(pdata->eeprom_data),
-+			&retlen, (u8*)pdata->eeprom_data);
-+	put_mtd_device(mtd);
-+
-+#endif
-+	return 0;
-+}
-+
-+static int ar913x_wmac_reset(void)
-+{
-+	ath79_device_reset_set(AR913X_RESET_AMBA2WMAC);
-+	mdelay(10);
-+
-+	ath79_device_reset_clear(AR913X_RESET_AMBA2WMAC);
-+	mdelay(10);
-+
-+	return 0;
-+}
-+
-+static int ar933x_wmac_reset(void)
-+{
-+	int retries = 20;
-+
-+	ath79_device_reset_set(AR933X_RESET_WMAC);
-+	ath79_device_reset_clear(AR933X_RESET_WMAC);
-+
-+	while (1) {
-+		u32 bootstrap;
-+
-+		bootstrap = ath79_reset_rr(AR933X_RESET_REG_BOOTSTRAP);
-+		if ((bootstrap & AR933X_BOOTSTRAP_EEPBUSY) == 0)
-+			return 0;
-+
-+		if (retries-- == 0)
-+			break;
-+
-+		udelay(10000);
-+	}
-+
-+	pr_err("ar933x: WMAC reset timed out");
-+	return -ETIMEDOUT;
-+}
-+
-+static int qca955x_wmac_reset(void)
-+{
-+	int i;
-+
-+	/* Try to wait for WMAC DDR activity to stop */
-+	for (i = 0; i < 10; i++) {
-+		if (!(__raw_readl(ath79_ddr_base + QCA955X_DDR_CTL_CONFIG) &
-+		    QCA955X_DDR_CTL_CONFIG_ACT_WMAC))
-+			break;
-+
-+		udelay(10);
-+	}
-+
-+	ath79_device_reset_set(QCA955X_RESET_RTC);
-+	udelay(10);
-+	ath79_device_reset_clear(QCA955X_RESET_RTC);
-+	udelay(10);
-+
-+	return 0;
-+}
-+
-+enum {
-+	AR913X_WMAC = 0,
-+	AR933X_WMAC,
-+	AR934X_WMAC,
-+	QCA953X_WMAC,
-+	QCA955X_WMAC,
-+	QCA956X_WMAC,
-+};
-+
-+static int ar9330_get_soc_revision(void)
-+{
-+	if (ath79_soc_rev == 1)
-+		return ath79_soc_rev;
-+
-+	return 0;
-+}
-+
-+static int ath79_get_soc_revision(void)
-+{
-+	return ath79_soc_rev;
-+}
-+
-+static const struct of_ath_ahb_data {
-+	u16 dev_id;
-+	u32 bootstrap_reg;
-+	u32 bootstrap_ref;
-+
-+	int (*soc_revision)(void);
-+	int (*wmac_reset)(void);
-+} of_ath_ahb_data[] = {
-+	[AR913X_WMAC] = {
-+		.dev_id = AR5416_AR9100_DEVID,
-+		.wmac_reset = ar913x_wmac_reset,
-+
-+	},
-+	[AR933X_WMAC] = {
-+		.dev_id = AR9300_DEVID_AR9330,
-+		.bootstrap_reg = AR933X_RESET_REG_BOOTSTRAP,
-+		.bootstrap_ref = AR933X_BOOTSTRAP_REF_CLK_40,
-+		.soc_revision = ar9330_get_soc_revision,
-+		.wmac_reset = ar933x_wmac_reset,
-+	},
-+	[AR934X_WMAC] = {
-+		.dev_id = AR9300_DEVID_AR9340,
-+		.bootstrap_reg = AR934X_RESET_REG_BOOTSTRAP,
-+		.bootstrap_ref = AR934X_BOOTSTRAP_REF_CLK_40,
-+		.soc_revision = ath79_get_soc_revision,
-+	},
-+	[QCA953X_WMAC] = {
-+		.dev_id = AR9300_DEVID_AR953X,
-+		.bootstrap_reg = QCA953X_RESET_REG_BOOTSTRAP,
-+		.bootstrap_ref = QCA953X_BOOTSTRAP_REF_CLK_40,
-+		.soc_revision = ath79_get_soc_revision,
-+	},
-+	[QCA955X_WMAC] = {
-+		.dev_id = AR9300_DEVID_QCA955X,
-+		.bootstrap_reg = QCA955X_RESET_REG_BOOTSTRAP,
-+		.bootstrap_ref = QCA955X_BOOTSTRAP_REF_CLK_40,
-+		.wmac_reset = qca955x_wmac_reset,
-+	},
-+	[QCA956X_WMAC] = {
-+		.dev_id = AR9300_DEVID_QCA956X,
-+		.bootstrap_reg = QCA956X_RESET_REG_BOOTSTRAP,
-+		.bootstrap_ref = QCA956X_BOOTSTRAP_REF_CLK_40,
-+		.soc_revision = ath79_get_soc_revision,
-+	},
-+};
-+
-+const struct of_device_id of_ath_ahb_match[] = {
-+	{ .compatible = "qca,ar9130-wmac", .data = &of_ath_ahb_data[AR913X_WMAC] },
-+	{ .compatible = "qca,ar9330-wmac", .data = &of_ath_ahb_data[AR933X_WMAC] },
-+	{ .compatible = "qca,ar9340-wmac", .data = &of_ath_ahb_data[AR934X_WMAC] },
-+	{ .compatible = "qca,qca9530-wmac", .data = &of_ath_ahb_data[QCA953X_WMAC] },
-+	{ .compatible = "qca,qca9550-wmac", .data = &of_ath_ahb_data[QCA955X_WMAC] },
-+	{ .compatible = "qca,qca9560-wmac", .data = &of_ath_ahb_data[QCA956X_WMAC] },
-+	{},
-+};
-+MODULE_DEVICE_TABLE(of, of_ath_ahb_match);
-+
-+static int of_ath_ahb_probe(struct platform_device *pdev)
-+{
-+	struct ath9k_platform_data *pdata;
-+	const struct of_device_id *match;
-+	const struct of_ath_ahb_data *data;
-+	u8 led_pin;
-+
-+	match = of_match_device(of_ath_ahb_match, &pdev->dev);
-+	data = (const struct of_ath_ahb_data *)match->data;
-+
-+	pdata = dev_get_platdata(&pdev->dev);
-+
-+	if (!of_property_read_u8(pdev->dev.of_node, "qca,led-pin", &led_pin))
-+		pdata->led_pin = led_pin;
-+	else
-+		pdata->led_pin = -1;
-+
-+	if (of_property_read_bool(pdev->dev.of_node, "qca,tx-gain-buffalo"))
-+		pdata->tx_gain_buffalo = true;
-+
-+	if (data->wmac_reset) {
-+		data->wmac_reset();
-+		pdata->external_reset = data->wmac_reset;
-+	}
-+
-+	if (data->dev_id == AR9300_DEVID_AR953X) {
-+		/*
-+		 * QCA953x only supports 25MHz refclk.
-+		 * Some vendors have an invalid bootstrap option
-+		 * set, which would break the WMAC here.
-+		 */
-+		pdata->is_clk_25mhz = true;
-+	} else if (data->bootstrap_reg && data->bootstrap_ref) {
-+		u32 t = ath79_reset_rr(data->bootstrap_reg);
-+		if (t & data->bootstrap_ref)
-+			pdata->is_clk_25mhz = false;
-+		else
-+			pdata->is_clk_25mhz = true;
-+	}
-+
-+	pdata->get_mac_revision = data->soc_revision;
-+
-+	if (of_get_wifi_cal(pdev->dev.of_node, pdata))
-+		dev_err(&pdev->dev, "failed to load calibration data from mtd device\n");
-+
-+	return data->dev_id;
-+}
-+#endif
-+
- static int ath_ahb_probe(struct platform_device *pdev)
- {
- 	void __iomem *mem;
-@@ -80,6 +318,17 @@ static int ath_ahb_probe(struct platform_device *pdev)
- 	int ret = 0;
- 	struct ath_hw *ah;
- 	char hw_name[64];
-+	u16 dev_id;
-+
-+	if (id)
-+		dev_id = id->driver_data;
-+
-+#ifdef CONFIG_OF
-+	if (pdev->dev.of_node)
-+		pdev->dev.platform_data = devm_kzalloc(&pdev->dev,
-+					sizeof(struct ath9k_platform_data),
-+					GFP_KERNEL);
-+#endif
- 
- 	if (!dev_get_platdata(&pdev->dev)) {
- 		dev_err(&pdev->dev, "no platform data specified\n");
-@@ -118,17 +367,23 @@ static int ath_ahb_probe(struct platform_device *pdev)
- 	sc->mem = mem;
- 	sc->irq = irq;
- 
-+#ifdef CONFIG_OF
-+	dev_id = of_ath_ahb_probe(pdev);
-+#endif
- 	ret = request_irq(irq, ath_isr, IRQF_SHARED, "ath9k", sc);
- 	if (ret) {
- 		dev_err(&pdev->dev, "request_irq failed\n");
- 		goto err_free_hw;
- 	}
- 
--	ret = ath9k_init_device(id->driver_data, sc, &ath_ahb_bus_ops);
-+	ret = ath9k_init_device(dev_id, sc, &ath_ahb_bus_ops);
- 	if (ret) {
- 		dev_err(&pdev->dev, "failed to initialize device\n");
- 		goto err_irq;
- 	}
-+#ifdef CONFIG_OF
-+	pdev->dev.platform_data = NULL;
-+#endif
- 
- 	ah = sc->sc_ah;
- 	ath9k_hw_name(ah, hw_name, sizeof(hw_name));
-@@ -162,6 +417,9 @@ static struct platform_driver ath_ahb_driver = {
- 	.remove_new = ath_ahb_remove,
- 	.driver		= {
- 		.name	= "ath9k",
-+#ifdef CONFIG_OF
-+		.of_match_table = of_ath_ahb_match,
-+#endif
- 	},
- 	.id_table    = ath9k_platform_id_table,
- };
-diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h
-index c40965b..f66f7ed 100644
---- a/drivers/net/wireless/ath/ath9k/ani.h
-+++ b/drivers/net/wireless/ath/ath9k/ani.h
-@@ -42,7 +42,7 @@
- #define ATH9K_ANI_PERIOD                  300
- 
- /* in ms */
--#define ATH9K_ANI_POLLINTERVAL            1000
-+#define ATH9K_ANI_POLLINTERVAL            300
- 
- #define ATH9K_SIG_FIRSTEP_SETTING_MIN     0
- #define ATH9K_SIG_FIRSTEP_SETTING_MAX     20
-diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
-index 7a45f5f..3f0ca1d 100644
---- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c
-+++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
-@@ -969,55 +969,6 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah,
- 		 * on == 0 means more noise imm
- 		 */
- 		u32 on = param ? 1 : 0;
--		/*
--		 * make register setting for default
--		 * (weak sig detect ON) come from INI file
--		 */
--		int m1ThreshLow = on ?
--			aniState->iniDef.m1ThreshLow : m1ThreshLow_off;
--		int m2ThreshLow = on ?
--			aniState->iniDef.m2ThreshLow : m2ThreshLow_off;
--		int m1Thresh = on ?
--			aniState->iniDef.m1Thresh : m1Thresh_off;
--		int m2Thresh = on ?
--			aniState->iniDef.m2Thresh : m2Thresh_off;
--		int m2CountThr = on ?
--			aniState->iniDef.m2CountThr : m2CountThr_off;
--		int m2CountThrLow = on ?
--			aniState->iniDef.m2CountThrLow : m2CountThrLow_off;
--		int m1ThreshLowExt = on ?
--			aniState->iniDef.m1ThreshLowExt : m1ThreshLowExt_off;
--		int m2ThreshLowExt = on ?
--			aniState->iniDef.m2ThreshLowExt : m2ThreshLowExt_off;
--		int m1ThreshExt = on ?
--			aniState->iniDef.m1ThreshExt : m1ThreshExt_off;
--		int m2ThreshExt = on ?
--			aniState->iniDef.m2ThreshExt : m2ThreshExt_off;
--
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
--			      AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
--			      m1ThreshLow);
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
--			      AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
--			      m2ThreshLow);
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR,
--			      AR_PHY_SFCORR_M1_THRESH, m1Thresh);
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR,
--			      AR_PHY_SFCORR_M2_THRESH, m2Thresh);
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR,
--			      AR_PHY_SFCORR_M2COUNT_THR, m2CountThr);
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
--			      AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
--			      m2CountThrLow);
--
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
--			      AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1ThreshLowExt);
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
--			      AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2ThreshLowExt);
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
--			      AR_PHY_SFCORR_EXT_M1_THRESH, m1ThreshExt);
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
--			      AR_PHY_SFCORR_EXT_M2_THRESH, m2ThreshExt);
- 
- 		if (on)
- 			REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
-@@ -1340,9 +1291,30 @@ void ar5008_hw_init_rate_txpower(struct ath_hw *ah, int16_t *rate_array,
- 	}
- }
- 
-+static void ar5008_hw_get_adc_entropy(struct ath_hw *ah, u8 *buf, size_t len)
-+{
-+	int i, j;
-+
-+	REG_RMW_FIELD(ah, AR_PHY_TEST, AR_PHY_TEST_BBB_OBS_SEL, 1);
-+	REG_CLR_BIT(ah, AR_PHY_TEST, AR_PHY_TEST_RX_OBS_SEL_BIT5);
-+	REG_RMW_FIELD(ah, AR_PHY_TEST2, AR_PHY_TEST2_RX_OBS_SEL, 0);
-+
-+	memset(buf, 0, len);
-+	for (i = 0; i < len; i++) {
-+		for (j = 0; j < 4; j++) {
-+			u32 regval = REG_READ(ah, AR_PHY_TST_ADC);
-+
-+			buf[i] <<= 2;
-+			buf[i] |= (regval & 1) | ((regval & BIT(9)) >> 8);
-+			udelay(1);
-+		}
-+	}
-+}
-+
- int ar5008_hw_attach_phy_ops(struct ath_hw *ah)
- {
- 	struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
-+	struct ath_hw_ops *ops = ath9k_hw_ops(ah);
- 	static const u32 ar5416_cca_regs[6] = {
- 		AR_PHY_CCA,
- 		AR_PHY_CH1_CCA,
-@@ -1357,6 +1329,8 @@ int ar5008_hw_attach_phy_ops(struct ath_hw *ah)
- 	if (ret)
- 	    return ret;
- 
-+	ops->get_adc_entropy = ar5008_hw_get_adc_entropy;
-+
- 	priv_ops->rf_set_freq = ar5008_hw_set_channel;
- 	priv_ops->spur_mitigate_freq = ar5008_hw_spur_mitigate;
- 
-diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.h b/drivers/net/wireless/ath/ath9k/ar9002_phy.h
-index 2b58245..d20a936 100644
---- a/drivers/net/wireless/ath/ath9k/ar9002_phy.h
-+++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.h
-@@ -20,6 +20,12 @@
- #define PHY_AGC_CLR             0x10000000
- #define RFSILENT_BB             0x00002000
- 
-+#define AR_PHY_TEST_BBB_OBS_SEL       0x780000
-+#define AR_PHY_TEST_BBB_OBS_SEL_S     19
-+
-+#define AR_PHY_TEST_RX_OBS_SEL_BIT5_S 23
-+#define AR_PHY_TEST_RX_OBS_SEL_BIT5   (1 << AR_PHY_TEST_RX_OBS_SEL_BIT5_S)
-+
- #define AR_PHY_TURBO                0x9804
- #define AR_PHY_FC_TURBO_MODE        0x00000001
- #define AR_PHY_FC_TURBO_SHORT       0x00000002
-@@ -36,6 +42,9 @@
- 
- #define AR_PHY_TEST2			0x9808
- 
-+#define AR_PHY_TEST2_RX_OBS_SEL        0x3C00
-+#define AR_PHY_TEST2_RX_OBS_SEL_S      10
-+
- #define AR_PHY_TIMING2           0x9810
- #define AR_PHY_TIMING3           0x9814
- #define AR_PHY_TIMING3_DSC_MAN   0xFFFE0000
-@@ -393,6 +402,8 @@
- #define AR_PHY_RFBUS_GRANT       0x9C20
- #define AR_PHY_RFBUS_GRANT_EN    0x00000001
- 
-+#define AR_PHY_TST_ADC      0x9C24
-+
- #define AR_PHY_CHAN_INFO_GAIN_DIFF             0x9CF4
- #define AR_PHY_CHAN_INFO_GAIN_DIFF_UPPER_LIMIT 320
- 
-diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
-index f715149..0246ad0 100644
---- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
-+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
-@@ -42,20 +42,6 @@ static const int cycpwrThr1_table[] =
- /* level:  0   1   2   3   4   5   6   7   8  */
- 	{ -6, -4, -2,  0,  2,  4,  6,  8 };     /* lvl 0-7, default 3 */
- 
--/*
-- * register values to turn OFDM weak signal detection OFF
-- */
--static const int m1ThreshLow_off = 127;
--static const int m2ThreshLow_off = 127;
--static const int m1Thresh_off = 127;
--static const int m2Thresh_off = 127;
--static const int m2CountThr_off =  31;
--static const int m2CountThrLow_off =  63;
--static const int m1ThreshLowExt_off = 127;
--static const int m2ThreshLowExt_off = 127;
--static const int m1ThreshExt_off = 127;
--static const int m2ThreshExt_off = 127;
--
- static const u8 ofdm2pwr[] = {
- 	ALL_TARGET_LEGACY_6_24,
- 	ALL_TARGET_LEGACY_6_24,
-@@ -1065,11 +1051,6 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
- 	struct ath_common *common = ath9k_hw_common(ah);
- 	struct ath9k_channel *chan = ah->curchan;
- 	struct ar5416AniState *aniState = &ah->ani;
--	int m1ThreshLow, m2ThreshLow;
--	int m1Thresh, m2Thresh;
--	int m2CountThr, m2CountThrLow;
--	int m1ThreshLowExt, m2ThreshLowExt;
--	int m1ThreshExt, m2ThreshExt;
- 	s32 value, value2;
- 
- 	switch (cmd & ah->ani_function) {
-@@ -1083,61 +1064,6 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
- 		 */
- 		u32 on = param ? 1 : 0;
- 
--		if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
--			goto skip_ws_det;
--
--		m1ThreshLow = on ?
--			aniState->iniDef.m1ThreshLow : m1ThreshLow_off;
--		m2ThreshLow = on ?
--			aniState->iniDef.m2ThreshLow : m2ThreshLow_off;
--		m1Thresh = on ?
--			aniState->iniDef.m1Thresh : m1Thresh_off;
--		m2Thresh = on ?
--			aniState->iniDef.m2Thresh : m2Thresh_off;
--		m2CountThr = on ?
--			aniState->iniDef.m2CountThr : m2CountThr_off;
--		m2CountThrLow = on ?
--			aniState->iniDef.m2CountThrLow : m2CountThrLow_off;
--		m1ThreshLowExt = on ?
--			aniState->iniDef.m1ThreshLowExt : m1ThreshLowExt_off;
--		m2ThreshLowExt = on ?
--			aniState->iniDef.m2ThreshLowExt : m2ThreshLowExt_off;
--		m1ThreshExt = on ?
--			aniState->iniDef.m1ThreshExt : m1ThreshExt_off;
--		m2ThreshExt = on ?
--			aniState->iniDef.m2ThreshExt : m2ThreshExt_off;
--
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
--			      AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
--			      m1ThreshLow);
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
--			      AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
--			      m2ThreshLow);
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR,
--			      AR_PHY_SFCORR_M1_THRESH,
--			      m1Thresh);
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR,
--			      AR_PHY_SFCORR_M2_THRESH,
--			      m2Thresh);
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR,
--			      AR_PHY_SFCORR_M2COUNT_THR,
--			      m2CountThr);
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
--			      AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
--			      m2CountThrLow);
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
--			      AR_PHY_SFCORR_EXT_M1_THRESH_LOW,
--			      m1ThreshLowExt);
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
--			      AR_PHY_SFCORR_EXT_M2_THRESH_LOW,
--			      m2ThreshLowExt);
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
--			      AR_PHY_SFCORR_EXT_M1_THRESH,
--			      m1ThreshExt);
--		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
--			      AR_PHY_SFCORR_EXT_M2_THRESH,
--			      m2ThreshExt);
--skip_ws_det:
- 		if (on)
- 			REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
- 				    AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
-@@ -1915,6 +1841,26 @@ void ar9003_hw_init_rate_txpower(struct ath_hw *ah, u8 *rate_array,
- 	}
- }
- 
-+static void ar9003_hw_get_adc_entropy(struct ath_hw *ah, u8 *buf, size_t len)
-+{
-+	int i, j;
-+
-+	REG_RMW_FIELD(ah, AR_PHY_TEST(ah), AR_PHY_TEST_BBB_OBS_SEL, 1);
-+	REG_CLR_BIT(ah, AR_PHY_TEST(ah), AR_PHY_TEST_RX_OBS_SEL_BIT5);
-+	REG_RMW_FIELD(ah, AR_PHY_TEST_CTL_STATUS(ah), AR_PHY_TEST_CTL_RX_OBS_SEL, 0);
-+
-+	memset(buf, 0, len);
-+	for (i = 0; i < len; i++) {
-+		for (j = 0; j < 4; j++) {
-+			u32 regval = REG_READ(ah, AR_PHY_TST_ADC);
-+
-+			buf[i] <<= 2;
-+			buf[i] |= (regval & 1) | ((regval & BIT(10)) >> 9);
-+			udelay(1);
-+		}
-+	}
-+}
-+
- void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
- {
- 	struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
-@@ -1951,6 +1897,7 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
- 	priv_ops->set_radar_params = ar9003_hw_set_radar_params;
- 	priv_ops->fast_chan_change = ar9003_hw_fast_chan_change;
- 
-+	ops->get_adc_entropy = ar9003_hw_get_adc_entropy;
- 	ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get;
- 	ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set;
- 	ops->spectral_scan_config = ar9003_hw_spectral_scan_config;
-diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
-index 97f4710..94efbdb 100644
---- a/drivers/net/wireless/ath/ath9k/ath9k.h
-+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
-@@ -25,6 +25,8 @@
- #include <linux/completion.h>
- #include <linux/time.h>
- #include <linux/hw_random.h>
-+#include <linux/gpio/driver.h>
-+#include <linux/reset.h>
- 
- #include "common.h"
- #include "debug.h"
-@@ -90,7 +92,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
- 		(_l) &= ((_sz) - 1);		\
- 	} while (0)
- 
--#define ATH_RXBUF               512
-+#define ATH_RXBUF               256
- #define ATH_TXBUF               512
- #define ATH_TXBUF_RESERVE       5
- #define ATH_TXMAXTRY            13
-@@ -845,6 +847,9 @@ static inline int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 size)
- #ifdef CPTCFG_MAC80211_LEDS
- void ath_init_leds(struct ath_softc *sc);
- void ath_deinit_leds(struct ath_softc *sc);
-+int ath_create_gpio_led(struct ath_softc *sc, int gpio, const char *name,
-+			const char *trigger, bool active_low);
-+
- #else
- static inline void ath_init_leds(struct ath_softc *sc)
- {
-@@ -981,6 +986,21 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);
- 
- #define ATH9K_NUM_CHANCTX  2 /* supports 2 operating channels */
- 
-+struct ath_led {
-+	struct list_head list;
-+	struct ath_softc *sc;
-+	const struct gpio_led *gpio;
-+	struct led_classdev cdev;
-+};
-+
-+#ifdef CONFIG_GPIOLIB
-+struct ath9k_gpio_chip {
-+	struct ath_softc *sc;
-+	char label[32];
-+	struct gpio_chip gchip;
-+};
-+#endif
-+
- struct ath_softc {
- 	struct ieee80211_hw *hw;
- 	struct device *dev;
-@@ -994,6 +1014,9 @@ struct ath_softc {
- 	struct ath_hw *sc_ah;
- 	void __iomem *mem;
- 	int irq;
-+#ifdef CONFIG_OF
-+	struct reset_control *reset;
-+#endif
- 	spinlock_t sc_serial_rw;
- 	spinlock_t sc_pm_lock;
- 	spinlock_t sc_pcu_lock;
-@@ -1034,9 +1057,12 @@ struct ath_softc {
- 	spinlock_t chan_lock;
- 
- #ifdef CPTCFG_MAC80211_LEDS
--	bool led_registered;
--	char led_name[32];
--	struct led_classdev led_cdev;
-+	const char *led_default_trigger;
-+	struct list_head leds;
-+#ifdef CONFIG_GPIOLIB
-+	struct ath9k_gpio_chip *gpiochip;
-+	struct platform_device *btnpdev;	/* gpio-keys-polled */
-+#endif
- #endif
- 
- #ifdef CPTCFG_ATH9K_DEBUGFS
-diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
-index 86c59bd..49cf036 100644
---- a/drivers/net/wireless/ath/ath9k/channel.c
-+++ b/drivers/net/wireless/ath/ath9k/channel.c
-@@ -15,6 +15,7 @@
-  */
- 
- #include "ath9k.h"
-+#include "hsr.h"
- 
- /* Set/change channels.  If the channel is really being changed, it's done
-  * by reseting the chip.  To accomplish this we must first cleanup any pending
-@@ -22,6 +23,7 @@
-  */
- static int ath_set_channel(struct ath_softc *sc)
- {
-+	struct device_node *np = sc->dev->of_node;
- 	struct ath_hw *ah = sc->sc_ah;
- 	struct ath_common *common = ath9k_hw_common(ah);
- 	struct ieee80211_hw *hw = sc->hw;
-@@ -42,6 +44,11 @@ static int ath_set_channel(struct ath_softc *sc)
- 	ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
- 		chan->center_freq, chandef->width);
- 
-+	if (of_property_read_bool(np, "ubnt,hsr")) {
-+		ath9k_hsr_enable(ah, chandef->width, chan->center_freq);
-+		ath9k_hsr_status(ah);
-+	}
-+
- 	/* update survey stats for the old channel before switching */
- 	spin_lock_irqsave(&common->cc_lock, flags);
- 	ath_update_survey_stats(sc);
-diff --git a/drivers/net/wireless/ath/ath9k/common-debug.c b/drivers/net/wireless/ath/ath9k/common-debug.c
-index 7aefb79..944bbe0 100644
---- a/drivers/net/wireless/ath/ath9k/common-debug.c
-+++ b/drivers/net/wireless/ath/ath9k/common-debug.c
-@@ -260,3 +260,110 @@ void ath9k_cmn_debug_phy_err(struct dentry *debugfs_phy,
- 			    &fops_phy_err);
- }
- EXPORT_SYMBOL(ath9k_cmn_debug_phy_err);
-+
-+static ssize_t read_file_eeprom(struct file *file, char __user *user_buf,
-+			     size_t count, loff_t *ppos)
-+{
-+	struct ath_hw *ah = file->private_data;
-+	struct ath_common *common = ath9k_hw_common(ah);
-+	int bytes = 0;
-+	int pos = *ppos;
-+	int size = 4096;
-+	u16 val;
-+	int i;
-+
-+	if (AR_SREV_9300_20_OR_LATER(ah))
-+		size = 16384;
-+
-+	if (*ppos < 0)
-+		return -EINVAL;
-+
-+	if (count > size - *ppos)
-+		count = size - *ppos;
-+
-+	for (i = *ppos / 2; count > 0; count -= bytes, *ppos += bytes, i++) {
-+		void *from = &val;
-+
-+		if (!common->bus_ops->eeprom_read(common, i, &val))
-+			val = 0xffff;
-+
-+		if (*ppos % 2) {
-+			from++;
-+			bytes = 1;
-+		} else if (count == 1) {
-+			bytes = 1;
-+		} else {
-+			bytes = 2;
-+		}
-+		if (copy_to_user(user_buf, from, bytes))
-+			return -EFAULT;
-+		user_buf += bytes;
-+	}
-+	return *ppos - pos;
-+}
-+
-+static const struct file_operations fops_eeprom = {
-+	.read = read_file_eeprom,
-+	.open = simple_open,
-+	.owner = THIS_MODULE
-+};
-+
-+void ath9k_cmn_debug_eeprom(struct dentry *debugfs_phy,
-+			    struct ath_hw *ah)
-+{
-+	debugfs_create_file("eeprom", S_IRUSR, debugfs_phy, ah,
-+			    &fops_eeprom);
-+}
-+EXPORT_SYMBOL(ath9k_cmn_debug_eeprom);
-+
-+static ssize_t read_file_chan_bw(struct file *file, char __user *user_buf,
-+			     size_t count, loff_t *ppos)
-+{
-+	struct ath_hw *ah = file->private_data;
-+	struct ath_common *common = ath9k_hw_common(ah);
-+	char buf[32];
-+	unsigned int len;
-+
-+	len = sprintf(buf, "0x%08x\n", common->chan_bw);
-+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-+}
-+
-+static ssize_t write_file_chan_bw(struct file *file, const char __user *user_buf,
-+			     size_t count, loff_t *ppos)
-+{
-+	struct ath_hw *ah = file->private_data;
-+	struct ath_common *common = ath9k_hw_common(ah);
-+	unsigned long chan_bw;
-+	char buf[32];
-+	ssize_t len;
-+
-+	len = min(count, sizeof(buf) - 1);
-+	if (copy_from_user(buf, user_buf, len))
-+		return -EFAULT;
-+
-+	buf[len] = '\0';
-+	if (kstrtoul(buf, 0, &chan_bw))
-+		return -EINVAL;
-+
-+	common->chan_bw = chan_bw;
-+	if (!test_bit(ATH_OP_INVALID, &common->op_flags))
-+		common->ieee_ops->config(ah->hw, IEEE80211_CONF_CHANGE_CHANNEL);
-+
-+	return count;
-+}
-+
-+static const struct file_operations fops_chanbw = {
-+	.read = read_file_chan_bw,
-+	.write = write_file_chan_bw,
-+	.open = simple_open,
-+	.owner = THIS_MODULE,
-+	.llseek = default_llseek,
-+};
-+
-+void ath9k_cmn_debug_chanbw(struct dentry *debugfs_phy,
-+			    struct ath_hw *ah)
-+{
-+	debugfs_create_file("chanbw", S_IRUSR | S_IWUSR, debugfs_phy, ah,
-+			    &fops_chanbw);
-+}
-+EXPORT_SYMBOL(ath9k_cmn_debug_chanbw);
-diff --git a/drivers/net/wireless/ath/ath9k/common-debug.h b/drivers/net/wireless/ath/ath9k/common-debug.h
-index 54f4c42..7a8b7ed 100644
---- a/drivers/net/wireless/ath/ath9k/common-debug.h
-+++ b/drivers/net/wireless/ath/ath9k/common-debug.h
-@@ -69,6 +69,10 @@ void ath9k_cmn_debug_modal_eeprom(struct dentry *debugfs_phy,
- 				  struct ath_hw *ah);
- void ath9k_cmn_debug_base_eeprom(struct dentry *debugfs_phy,
- 				 struct ath_hw *ah);
-+void ath9k_cmn_debug_eeprom(struct dentry *debugfs_phy,
-+			    struct ath_hw *ah);
-+void ath9k_cmn_debug_chanbw(struct dentry *debugfs_phy,
-+			    struct ath_hw *ah);
- void ath9k_cmn_debug_stat_rx(struct ath_rx_stats *rxstats,
- 			     struct ath_rx_status *rs);
- void ath9k_cmn_debug_recv(struct dentry *debugfs_phy,
-diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c
-index 099f3d4..86d4a50 100644
---- a/drivers/net/wireless/ath/ath9k/common.c
-+++ b/drivers/net/wireless/ath/ath9k/common.c
-@@ -297,11 +297,13 @@ EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype);
- /*
-  * Update internal channel flags.
-  */
--static void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
-+static void ath9k_cmn_update_ichannel(struct ath_common *common,
-+				      struct ath9k_channel *ichan,
- 				      struct cfg80211_chan_def *chandef)
- {
- 	struct ieee80211_channel *chan = chandef->chan;
- 	u16 flags = 0;
-+	int width;
- 
- 	ichan->channel = chan->center_freq;
- 	ichan->chan = chan;
-@@ -309,7 +311,19 @@ static void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
- 	if (chan->band == NL80211_BAND_5GHZ)
- 		flags |= CHANNEL_5GHZ;
- 
--	switch (chandef->width) {
-+	switch (common->chan_bw) {
-+	case 5:
-+		width = NL80211_CHAN_WIDTH_5;
-+		break;
-+	case 10:
-+		width = NL80211_CHAN_WIDTH_10;
-+		break;
-+	default:
-+		width = chandef->width;
-+		break;
-+	}
-+
-+	switch (width) {
- 	case NL80211_CHAN_WIDTH_5:
- 		flags |= CHANNEL_QUARTER;
- 		break;
-@@ -342,10 +356,11 @@ struct ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw,
- 					    struct cfg80211_chan_def *chandef)
- {
- 	struct ieee80211_channel *curchan = chandef->chan;
-+	struct ath_common *common = ath9k_hw_common(ah);
- 	struct ath9k_channel *channel;
- 
- 	channel = &ah->channels[curchan->hw_value];
--	ath9k_cmn_update_ichannel(channel, chandef);
-+	ath9k_cmn_update_ichannel(common, channel, chandef);
- 
- 	return channel;
- }
-diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
-index 87ffcb4..f081d1d 100644
---- a/drivers/net/wireless/ath/ath9k/debug.c
-+++ b/drivers/net/wireless/ath/ath9k/debug.c
-@@ -123,6 +123,61 @@ static const struct file_operations fops_debug = {
- 
- #define DMA_BUF_LEN 1024
- 
-+#ifdef CONFIG_MAC80211_LEDS
-+
-+static ssize_t write_file_gpio_led(struct file *file, const char __user *ubuf,
-+				   size_t count, loff_t *ppos)
-+{
-+	struct ath_softc *sc = file->private_data;
-+	char buf[32], *str, *name, *c;
-+	ssize_t len;
-+	unsigned int gpio;
-+	bool active_low = false;
-+
-+	len = min(count, sizeof(buf) - 1);
-+	if (copy_from_user(buf, ubuf, len))
-+		return -EFAULT;
-+
-+	buf[len] = '\0';
-+	name = strchr(buf, ',');
-+	if (!name)
-+		return -EINVAL;
-+
-+	*(name++) = 0;
-+	if (!*name)
-+		return -EINVAL;
-+
-+	c = strchr(name, '\n');
-+	if (c)
-+		*c = 0;
-+
-+	str = buf;
-+	if (*str == '!') {
-+		str++;
-+		active_low = true;
-+	}
-+
-+	if (kstrtouint(str, 0, &gpio) < 0)
-+		return -EINVAL;
-+
-+	if (gpio >= sc->sc_ah->caps.num_gpio_pins)
-+		return -EINVAL;
-+
-+	if (ath_create_gpio_led(sc, gpio, name, NULL, active_low) < 0)
-+		return -EINVAL;
-+
-+	return count;
-+}
-+
-+static const struct file_operations fops_gpio_led = {
-+	.write = write_file_gpio_led,
-+	.open = simple_open,
-+	.owner = THIS_MODULE,
-+	.llseek = default_llseek,
-+};
-+
-+#endif
-+
- 
- static ssize_t read_file_ani(struct file *file, char __user *user_buf,
- 			     size_t count, loff_t *ppos)
-@@ -1373,6 +1428,50 @@ void ath9k_deinit_debug(struct ath_softc *sc)
- 	ath9k_cmn_spectral_deinit_debug(&sc->spec_priv);
- }
- 
-+static ssize_t read_file_diag(struct file *file, char __user *user_buf,
-+			     size_t count, loff_t *ppos)
-+{
-+	struct ath_softc *sc = file->private_data;
-+	struct ath_hw *ah = sc->sc_ah;
-+	char buf[32];
-+	unsigned int len;
-+
-+	len = sprintf(buf, "0x%08lx\n", ah->diag);
-+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-+}
-+
-+static ssize_t write_file_diag(struct file *file, const char __user *user_buf,
-+			     size_t count, loff_t *ppos)
-+{
-+	struct ath_softc *sc = file->private_data;
-+	struct ath_hw *ah = sc->sc_ah;
-+	unsigned long diag;
-+	char buf[32];
-+	ssize_t len;
-+
-+	len = min(count, sizeof(buf) - 1);
-+	if (copy_from_user(buf, user_buf, len))
-+		return -EFAULT;
-+
-+	buf[len] = '\0';
-+	if (kstrtoul(buf, 0, &diag))
-+		return -EINVAL;
-+
-+	ah->diag = diag;
-+	ath9k_hw_update_diag(ah);
-+
-+	return count;
-+}
-+
-+static const struct file_operations fops_diag = {
-+	.read = read_file_diag,
-+	.write = write_file_diag,
-+	.open = simple_open,
-+	.owner = THIS_MODULE,
-+	.llseek = default_llseek,
-+};
-+
-+
- int ath9k_init_debug(struct ath_hw *ah)
- {
- 	struct ath_common *common = ath9k_hw_common(ah);
-@@ -1392,6 +1491,12 @@ int ath9k_init_debug(struct ath_hw *ah)
- 	ath9k_tx99_init_debug(sc);
- 	ath9k_cmn_spectral_init_debug(&sc->spec_priv, sc->debug.debugfs_phy);
- 
-+#ifdef CONFIG_MAC80211_LEDS
-+	debugfs_create_file("gpio_led", S_IWUSR,
-+			   sc->debug.debugfs_phy, sc, &fops_gpio_led);
-+#endif
-+	debugfs_create_file("diag", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
-+			    sc, &fops_diag);
- 	debugfs_create_devm_seqfile(sc->dev, "dma", sc->debug.debugfs_phy,
- 				    read_file_dma);
- 	debugfs_create_devm_seqfile(sc->dev, "interrupt", sc->debug.debugfs_phy,
-@@ -1431,6 +1536,8 @@ int ath9k_init_debug(struct ath_hw *ah)
- 
- 	ath9k_cmn_debug_base_eeprom(sc->debug.debugfs_phy, sc->sc_ah);
- 	ath9k_cmn_debug_modal_eeprom(sc->debug.debugfs_phy, sc->sc_ah);
-+	ath9k_cmn_debug_eeprom(sc->debug.debugfs_phy, sc->sc_ah);
-+	ath9k_cmn_debug_chanbw(sc->debug.debugfs_phy, sc->sc_ah);
- 
- 	debugfs_create_u32("gpio_mask", 0600,
- 			   sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
-diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c
-index a8101c9..e1449d7 100644
---- a/drivers/net/wireless/ath/ath9k/gpio.c
-+++ b/drivers/net/wireless/ath/ath9k/gpio.c
-@@ -15,13 +15,211 @@
-  */
- 
- #include "ath9k.h"
-+#include <linux/ath9k_platform.h>
-+#include <linux/gpio.h>
-+#include <linux/platform_device.h>
-+#include <linux/gpio_keys.h>
-+
-+#ifdef CPTCFG_MAC80211_LEDS
-+
-+#ifdef CONFIG_GPIOLIB
-+
-+/***************/
-+/*  GPIO Chip  */
-+/***************/
-+
-+/* gpio_chip handler : set GPIO to input */
-+static int ath9k_gpio_pin_cfg_input(struct gpio_chip *chip, unsigned offset)
-+{
-+	struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
-+						  gchip);
-+
-+	ath9k_hw_gpio_request_in(gc->sc->sc_ah, offset, "ath9k-gpio");
-+
-+	return 0;
-+}
-+
-+/* gpio_chip handler : set GPIO to output */
-+static int ath9k_gpio_pin_cfg_output(struct gpio_chip *chip, unsigned offset,
-+				     int value)
-+{
-+	struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
-+						  gchip);
-+
-+	ath9k_hw_gpio_request_out(gc->sc->sc_ah, offset, "ath9k-gpio",
-+				  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
-+	ath9k_hw_set_gpio(gc->sc->sc_ah, offset, value);
-+
-+	return 0;
-+}
-+
-+/* gpio_chip handler : query GPIO direction (0=out, 1=in) */
-+static int ath9k_gpio_pin_get_dir(struct gpio_chip *chip, unsigned offset)
-+{
-+	struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
-+						  gchip);
-+	struct ath_hw *ah = gc->sc->sc_ah;
-+
-+	return !((REG_READ(ah, AR_GPIO_OE_OUT(ah)) >> (offset * 2)) & 3);
-+}
-+
-+/* gpio_chip handler : get GPIO pin value */
-+static int ath9k_gpio_pin_get(struct gpio_chip *chip, unsigned offset)
-+{
-+	struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
-+						  gchip);
-+
-+	return ath9k_hw_gpio_get(gc->sc->sc_ah, offset);
-+}
-+
-+/* gpio_chip handler : set GPIO pin to value */
-+static void ath9k_gpio_pin_set(struct gpio_chip *chip, unsigned offset,
-+			       int value)
-+{
-+	struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
-+						  gchip);
-+
-+	ath9k_hw_set_gpio(gc->sc->sc_ah, offset, value);
-+}
-+
-+/* register GPIO chip */
-+static void ath9k_register_gpio_chip(struct ath_softc *sc)
-+{
-+	struct ath9k_gpio_chip *gc;
-+	struct ath_hw *ah = sc->sc_ah;
-+
-+	gc = kzalloc(sizeof(struct ath9k_gpio_chip), GFP_KERNEL);
-+	if (!gc)
-+		return;
-+
-+	gc->sc = sc;
-+	snprintf(gc->label, sizeof(gc->label), "ath9k-%s",
-+		 wiphy_name(sc->hw->wiphy));
-+#ifdef CONFIG_OF
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,5,0)
-+	gc->gchip.parent = sc->dev;
-+#else
-+	gc->gchip.dev = sc->dev;
-+#endif
-+#endif
-+	gc->gchip.label = gc->label;
-+	gc->gchip.base = -1;	/* determine base automatically */
-+	gc->gchip.ngpio = ah->caps.num_gpio_pins;
-+	gc->gchip.direction_input = ath9k_gpio_pin_cfg_input;
-+	gc->gchip.direction_output = ath9k_gpio_pin_cfg_output;
-+	gc->gchip.get_direction = ath9k_gpio_pin_get_dir;
-+	gc->gchip.get = ath9k_gpio_pin_get;
-+	gc->gchip.set = ath9k_gpio_pin_set;
-+
-+	if (gpiochip_add(&gc->gchip)) {
-+		kfree(gc);
-+		return;
-+	}
-+
-+#ifdef CONFIG_OF
-+	gc->gchip.owner = NULL;
-+#endif
-+	sc->gpiochip = gc;
-+}
-+
-+/* remove GPIO chip */
-+static void ath9k_unregister_gpio_chip(struct ath_softc *sc)
-+{
-+	struct ath9k_gpio_chip *gc = sc->gpiochip;
-+
-+	if (!gc)
-+		return;
-+
-+	gpiochip_remove(&gc->gchip);
-+	kfree(gc);
-+	sc->gpiochip = NULL;
-+}
-+
-+/******************/
-+/*  GPIO Buttons  */
-+/******************/
-+
-+/* add GPIO buttons */
-+static void ath9k_init_buttons(struct ath_softc *sc)
-+{
-+	struct ath9k_platform_data *pdata = sc->dev->platform_data;
-+	struct platform_device *pdev;
-+	struct gpio_keys_platform_data gkpdata;
-+	struct gpio_keys_button *bt;
-+	int i;
-+
-+	if (!sc->gpiochip)
-+		return;
-+
-+	if (!pdata || !pdata->btns || !pdata->num_btns)
-+		return;
-+
-+	bt = devm_kmemdup(sc->dev, pdata->btns,
-+			  pdata->num_btns * sizeof(struct gpio_keys_button),
-+			  GFP_KERNEL);
-+	if (!bt)
-+		return;
-+
-+	for (i = 0; i < pdata->num_btns; i++) {
-+		if (pdata->btns[i].gpio == sc->sc_ah->led_pin)
-+				sc->sc_ah->led_pin = -1;
-+
-+		ath9k_hw_gpio_request_in(sc->sc_ah, pdata->btns[i].gpio,
-+					 "ath9k-gpio");
-+		bt[i].gpio = sc->gpiochip->gchip.base + pdata->btns[i].gpio;
-+	}
-+
-+	memset(&gkpdata, 0, sizeof(struct gpio_keys_platform_data));
-+	gkpdata.buttons = bt;
-+	gkpdata.nbuttons = pdata->num_btns;
-+	gkpdata.poll_interval = pdata->btn_poll_interval;
-+
-+	pdev = platform_device_register_data(sc->dev, "gpio-keys-polled",
-+					     PLATFORM_DEVID_AUTO, &gkpdata,
-+					     sizeof(gkpdata));
-+	if (!IS_ERR_OR_NULL(pdev))
-+		sc->btnpdev = pdev;
-+	else {
-+		sc->btnpdev = NULL;
-+		devm_kfree(sc->dev, bt);
-+	}
-+}
-+
-+/* remove GPIO buttons */
-+static void ath9k_deinit_buttons(struct ath_softc *sc)
-+{
-+	if (!sc->gpiochip || !sc->btnpdev)
-+		return;
-+
-+	platform_device_unregister(sc->btnpdev);
-+
-+	sc->btnpdev = NULL;
-+}
-+
-+#else /* CONFIG_GPIOLIB */
-+
-+static inline void ath9k_register_gpio_chip(struct ath_softc *sc)
-+{
-+}
-+
-+static inline void ath9k_unregister_gpio_chip(struct ath_softc *sc)
-+{
-+}
-+
-+static inline void ath9k_init_buttons(struct ath_softc *sc)
-+{
-+}
-+
-+static inline void ath9k_deinit_buttons(struct ath_softc *sc)
-+{
-+}
-+
-+#endif /* CONFIG_GPIOLIB */
- 
- /********************************/
- /*	 LED functions		*/
- /********************************/
- 
--#ifdef CPTCFG_MAC80211_LEDS
--
- static void ath_fill_led_pin(struct ath_softc *sc)
- {
- 	struct ath_hw *ah = sc->sc_ah;
-@@ -39,62 +237,171 @@ static void ath_fill_led_pin(struct ath_softc *sc)
- 		else
- 			ah->led_pin = ATH_LED_PIN_DEF;
- 	}
-+}
-+
-+static void ath_led_brightness(struct led_classdev *led_cdev,
-+			       enum led_brightness brightness)
-+{
-+	struct ath_led *led = container_of(led_cdev, struct ath_led, cdev);
-+	struct ath_softc *sc = led->sc;
-+
-+	ath9k_ps_wakeup(sc);
-+	ath9k_hw_set_gpio(sc->sc_ah, led->gpio->gpio,
-+			  (brightness != LED_OFF) ^ led->gpio->active_low);
-+	ath9k_ps_restore(sc);
-+}
-+
-+static int ath_add_led(struct ath_softc *sc, struct ath_led *led)
-+{
-+	const struct gpio_led *gpio = led->gpio;
-+	int ret;
-+
-+	led->cdev.name = gpio->name;
-+	led->cdev.default_trigger = gpio->default_trigger;
-+	led->cdev.brightness_set = ath_led_brightness;
-+
-+	ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->cdev);
-+	if (ret < 0)
-+		return ret;
-+
-+	led->sc = sc;
-+	list_add(&led->list, &sc->leds);
- 
- 	/* Configure gpio for output */
--	ath9k_hw_gpio_request_out(ah, ah->led_pin, "ath9k-led",
-+	ath9k_hw_gpio_request_out(sc->sc_ah, gpio->gpio, gpio->name,
- 				  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
- 
--	/* LED off, active low */
--	ath9k_hw_set_gpio(ah, ah->led_pin, ah->config.led_active_high ? 0 : 1);
-+	/* Set default LED state */
-+	if (gpio->default_state == LEDS_GPIO_DEFSTATE_ON)
-+		ath9k_hw_set_gpio(sc->sc_ah, gpio->gpio, !gpio->active_low);
-+	else
-+		ath9k_hw_set_gpio(sc->sc_ah, gpio->gpio, gpio->active_low);
-+
-+#ifdef CONFIG_GPIOLIB
-+	/* If there is GPIO chip configured, reserve LED pin */
-+	if (sc->gpiochip)
-+		gpio_request(sc->gpiochip->gchip.base + gpio->gpio, gpio->name);
-+#endif
-+
-+	return 0;
- }
- 
--static void ath_led_brightness(struct led_classdev *led_cdev,
--			       enum led_brightness brightness)
-+int ath_create_gpio_led(struct ath_softc *sc, int gpio_num, const char *name,
-+			const char *trigger, bool active_low)
- {
--	struct ath_softc *sc = container_of(led_cdev, struct ath_softc, led_cdev);
--	u32 val = (brightness == LED_OFF);
-+	struct ath_led *led;
-+	struct gpio_led *gpio;
-+	char *_name;
-+	int ret;
-+
-+	led = kzalloc(sizeof(*led) + sizeof(*gpio) + strlen(name) + 1,
-+		      GFP_KERNEL);
-+	if (!led)
-+		return -ENOMEM;
- 
--	if (sc->sc_ah->config.led_active_high)
--		val = !val;
-+	led->gpio = gpio = (struct gpio_led *) (led + 1);
-+	_name = (char *) (led->gpio + 1);
- 
--	ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, val);
-+	strcpy(_name, name);
-+	gpio->name = _name;
-+	gpio->gpio = gpio_num;
-+	gpio->active_low = active_low;
-+	gpio->default_trigger = trigger;
-+
-+	ret = ath_add_led(sc, led);
-+	if (unlikely(ret < 0))
-+		kfree(led);
-+
-+	return ret;
- }
- 
--void ath_deinit_leds(struct ath_softc *sc)
-+static int ath_create_platform_led(struct ath_softc *sc,
-+				   const struct gpio_led *gpio)
- {
--	if (!sc->led_registered)
--		return;
-+	struct ath_led *led;
-+	int ret;
- 
--	ath_led_brightness(&sc->led_cdev, LED_OFF);
--	led_classdev_unregister(&sc->led_cdev);
-+	led = kzalloc(sizeof(*led), GFP_KERNEL);
-+	if (!led)
-+		return -ENOMEM;
- 
--	ath9k_hw_gpio_free(sc->sc_ah, sc->sc_ah->led_pin);
-+	led->gpio = gpio;
-+	ret = ath_add_led(sc, led);
-+	if (ret < 0)
-+		kfree(led);
-+
-+	return ret;
-+}
-+
-+void ath_deinit_leds(struct ath_softc *sc)
-+{
-+	struct ath_led *led;
-+
-+	ath9k_deinit_buttons(sc);
-+	while (!list_empty(&sc->leds)) {
-+		led = list_first_entry(&sc->leds, struct ath_led, list);
-+#ifdef CONFIG_GPIOLIB
-+		/* If there is GPIO chip configured, free LED pin */
-+		if (sc->gpiochip)
-+			gpio_free(sc->gpiochip->gchip.base + led->gpio->gpio);
-+#endif
-+		list_del(&led->list);
-+		ath_led_brightness(&led->cdev, LED_OFF);
-+		led_classdev_unregister(&led->cdev);
-+		ath9k_hw_gpio_free(sc->sc_ah, led->gpio->gpio);
-+		kfree(led);
-+	}
-+	ath9k_unregister_gpio_chip(sc);
- }
- 
- void ath_init_leds(struct ath_softc *sc)
- {
--	int ret;
-+	struct ath9k_platform_data *pdata = sc->dev->platform_data;
-+	struct device_node *np = sc->dev->of_node;
-+	char led_name[32];
-+	const char *trigger;
-+	int i;
-+
-+	INIT_LIST_HEAD(&sc->leds);
- 
- 	if (AR_SREV_9100(sc->sc_ah))
- 		return;
- 
-+	if (!np)
-+		ath9k_register_gpio_chip(sc);
-+
-+	/* setup gpio controller only if requested and skip the led_pin setup */
-+	if (of_property_read_bool(np, "gpio-controller")) {
-+		ath9k_register_gpio_chip(sc);
-+		return;
-+	}
-+
- 	ath_fill_led_pin(sc);
-+	ath9k_init_buttons(sc);
- 
--	if (!ath9k_led_blink)
--		sc->led_cdev.default_trigger =
--			ieee80211_get_radio_led_name(sc->hw);
-+	if (pdata && pdata->leds && pdata->num_leds)
-+		for (i = 0; i < pdata->num_leds; i++) {
-+			if (pdata->leds[i].gpio == sc->sc_ah->led_pin)
-+				sc->sc_ah->led_pin = -1;
- 
--	snprintf(sc->led_name, sizeof(sc->led_name),
--		"ath9k-%s", wiphy_name(sc->hw->wiphy));
--	sc->led_cdev.name = sc->led_name;
--	sc->led_cdev.brightness_set = ath_led_brightness;
-+			ath_create_platform_led(sc, &pdata->leds[i]);
-+		}
- 
--	ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &sc->led_cdev);
--	if (ret < 0)
-+	if (sc->sc_ah->led_pin < 0)
- 		return;
- 
--	sc->led_registered = true;
-+	snprintf(led_name, sizeof(led_name), "ath9k-%s",
-+		 wiphy_name(sc->hw->wiphy));
-+
-+	if (ath9k_led_blink)
-+		trigger = sc->led_default_trigger;
-+	else
-+		trigger = ieee80211_get_radio_led_name(sc->hw);
-+
-+	ath_create_gpio_led(sc, sc->sc_ah->led_pin, led_name, trigger,
-+			   !sc->sc_ah->config.led_active_high);
- }
-+
- #endif
- 
- /*******************/
-diff --git a/drivers/net/wireless/ath/ath9k/hsr.c b/drivers/net/wireless/ath/ath9k/hsr.c
-new file mode 100644
-index 0000000..7d12d91
---- /dev/null
-+++ b/drivers/net/wireless/ath/ath9k/hsr.c
-@@ -0,0 +1,247 @@
-+/*
-+ *
-+ * The MIT License (MIT)
-+ *
-+ * Copyright (c) 2015 Kirill Berezin
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a copy
-+ * of this software and associated documentation files (the "Software"), to deal
-+ * in the Software without restriction, including without limitation the rights
-+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-+ * copies of the Software, and to permit persons to whom the Software is
-+ * furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice shall be included in
-+ * all copies or substantial portions of the Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-+ * SOFTWARE.
-+ *
-+ */
-+
-+#include <linux/io.h>
-+#include <linux/slab.h>
-+#include <linux/module.h>
-+#include <linux/time.h>
-+#include <linux/bitops.h>
-+#include <linux/etherdevice.h>
-+#include <linux/rtnetlink.h>
-+#include <asm/unaligned.h>
-+
-+#include "hw.h"
-+#include "ath9k.h"
-+
-+#define HSR_GPIO_CSN 8
-+#define HSR_GPIO_CLK 6
-+#define HSR_GPIO_DOUT 7
-+#define HSR_GPIO_DIN 5
-+
-+/* delays are in useconds */
-+#define HSR_DELAY_HALF_TICK 100
-+#define HSR_DELAY_PRE_WRITE 75
-+#define HSR_DELAY_FINAL 20000
-+#define HSR_DELAY_TRAILING 200
-+
-+void ath9k_hsr_init(struct ath_hw *ah)
-+{
-+	ath9k_hw_gpio_request_in(ah, HSR_GPIO_DIN, NULL);
-+	ath9k_hw_gpio_request_out(ah, HSR_GPIO_CSN, NULL,
-+				  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
-+	ath9k_hw_gpio_request_out(ah, HSR_GPIO_CLK, NULL,
-+				  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
-+	ath9k_hw_gpio_request_out(ah, HSR_GPIO_DOUT, NULL,
-+				  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
-+
-+	ath9k_hw_set_gpio(ah, HSR_GPIO_CSN, 1);
-+	ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 0);
-+	ath9k_hw_set_gpio(ah, HSR_GPIO_DOUT, 0);
-+
-+	udelay(HSR_DELAY_TRAILING);
-+}
-+
-+static u32 ath9k_hsr_write_byte(struct ath_hw *ah, int delay, u32 value)
-+{
-+	struct ath_common *common = ath9k_hw_common(ah);
-+	int i;
-+	u32 rval = 0;
-+
-+	udelay(delay);
-+
-+	ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 0);
-+	udelay(HSR_DELAY_HALF_TICK);
-+
-+	ath9k_hw_set_gpio(ah, HSR_GPIO_CSN, 0);
-+	udelay(HSR_DELAY_HALF_TICK);
-+
-+	for (i = 0; i < 8; ++i) {
-+		rval = rval << 1;
-+
-+		/* pattern is left to right, that is 7-th bit runs first */
-+		ath9k_hw_set_gpio(ah, HSR_GPIO_DOUT, (value >> (7 - i)) & 0x1);
-+		udelay(HSR_DELAY_HALF_TICK);
-+
-+		ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 1);
-+		udelay(HSR_DELAY_HALF_TICK);
-+
-+		rval |= ath9k_hw_gpio_get(ah, HSR_GPIO_DIN);
-+
-+		ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 0);
-+		udelay(HSR_DELAY_HALF_TICK);
-+	}
-+
-+	ath9k_hw_set_gpio(ah, HSR_GPIO_CSN, 1);
-+	udelay(HSR_DELAY_HALF_TICK);
-+
-+	ath_dbg(common, CONFIG, "ath9k_hsr_write_byte: write byte %d return value is %d %c\n",
-+		value, rval, rval > 32 ? rval : '-');
-+
-+	return rval & 0xff;
-+}
-+
-+static int ath9k_hsr_write_a_chain(struct ath_hw *ah, char *chain, int items)
-+{
-+	int status = 0;
-+	int i = 0;
-+	int err;
-+
-+	/* a preamble */
-+	ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
-+	status = ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
-+
-+	/* clear HSR's reply buffer */
-+	if (status) {
-+		int loop = 0;
-+
-+		for (loop = 0; (loop < 42) && status; ++loop)
-+			status = ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE,
-+						      0);
-+
-+		if (loop >= 42) {
-+			ATH_DBG_WARN(1,
-+				     "ath9k_hsr_write_a_chain: can't clear an output buffer after a 42 cycles.\n");
-+			return -1;
-+		}
-+	}
-+
-+	for (i = 0; (i < items) && (chain[i] != 0); ++i)
-+		ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, (u32)chain[i]);
-+
-+	ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
-+	mdelay(HSR_DELAY_FINAL / 1000);
-+
-+	/* reply */
-+	memset(chain, 0, items);
-+
-+	ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
-+	udelay(HSR_DELAY_TRAILING);
-+
-+	for (i = 0; i < (items - 1); ++i) {
-+		u32 ret;
-+
-+		ret = ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
-+		if (ret != 0)
-+			chain[i] = (char)ret;
-+		else
-+			break;
-+
-+		udelay(HSR_DELAY_TRAILING);
-+	}
-+
-+	if (i <= 1)
-+		return 0;
-+
-+	err = kstrtoint(chain + 1, 10, &i);
-+	if (err)
-+		return err;
-+
-+	return i;
-+}
-+
-+int ath9k_hsr_disable(struct ath_hw *ah)
-+{
-+	char cmd[10] = {'b', '4', '0', 0, 0, 0, 0, 0, 0, 0};
-+	int ret;
-+
-+	ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
-+	if ((ret > 0) && (*cmd == 'B'))
-+		return 0;
-+
-+	return -1;
-+}
-+
-+int ath9k_hsr_enable(struct ath_hw *ah, int bw, int fq)
-+{
-+	char cmd[10];
-+	int ret;
-+
-+	/* Bandwidth argument is 0 sometimes. Assume default 802.11bgn
-+	 * 20MHz on invalid values
-+	 */
-+	if ((bw != 5) && (bw != 10) && (bw != 20) && (bw != 40))
-+		bw = 20;
-+
-+	memset(cmd, 0, sizeof(cmd));
-+	*cmd = 'b';
-+	snprintf(cmd + 1, 3, "%02d", bw);
-+
-+	ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
-+	if ((*cmd != 'B') || (ret != bw)) {
-+		ATH_DBG_WARN(1,
-+			     "ath9k_hsr_enable: failed changing bandwidth -> set (%d,%d) reply (%d, %d)\n",
-+			     'b', bw, *cmd, ret);
-+		return -1;
-+	}
-+
-+	memset(cmd, 0, sizeof(cmd));
-+	*cmd = 'x';
-+	ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
-+	if (*cmd != 'X') {
-+		ATH_DBG_WARN(1,
-+			     "ath9k_hsr_enable: failed 'x' command -> reply (%d, %d)\n",
-+			     *cmd, ret);
-+		return -1;
-+	}
-+
-+	memset(cmd, 0, sizeof(cmd));
-+	*cmd = 'm';
-+	ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
-+	if (*cmd != 'M') {
-+		ATH_DBG_WARN(1,
-+			     "ath9k_hsr_enable: failed 'm' command -> reply (%d, %d)\n",
-+			     *cmd, ret);
-+		return  -1;
-+	}
-+
-+	memset(cmd, 0, sizeof(cmd));
-+	*cmd = 'f';
-+	snprintf(cmd + 1, 6, "%05d", fq);
-+	ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
-+	if ((*cmd != 'F') && (ret != fq)) {
-+		ATH_DBG_WARN(1,
-+			     "ath9k_hsr_enable: failed set frequency -> reply (%d, %d)\n",
-+			     *cmd, ret);
-+		return -1;
-+	}
-+
-+	return 0;
-+}
-+
-+int ath9k_hsr_status(struct ath_hw *ah)
-+{
-+	char cmd[10] = {'s', 0, 0, 0, 0, 0, 0, 0, 0, 0};
-+	int ret;
-+
-+	ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
-+	if (*cmd != 'S') {
-+		ATH_DBG_WARN(1, "ath9k_hsr_status: returned %d,%d\n", *cmd,
-+			     ret);
-+		return -1;
-+	}
-+
-+	return 0;
-+}
-diff --git a/drivers/net/wireless/ath/ath9k/hsr.h b/drivers/net/wireless/ath/ath9k/hsr.h
-new file mode 100644
-index 0000000..78af444
---- /dev/null
-+++ b/drivers/net/wireless/ath/ath9k/hsr.h
-@@ -0,0 +1,48 @@
-+/*
-+ * The MIT License (MIT)
-+ *
-+ * Copyright (c) 2015 Kirill Berezin
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a copy
-+ * of this software and associated documentation files (the "Software"), to deal
-+ * in the Software without restriction, including without limitation the rights
-+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-+ * copies of the Software, and to permit persons to whom the Software is
-+ * furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice shall be included in
-+ * all copies or substantial portions of the Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-+ * SOFTWARE.
-+ */
-+
-+#ifndef HSR_H
-+#define HSR_H
-+
-+#ifdef CPTCFG_ATH9K_UBNTHSR
-+
-+void ath9k_hsr_init(struct ath_hw *ah);
-+int ath9k_hsr_disable(struct ath_hw *ah);
-+int ath9k_hsr_enable(struct ath_hw *ah, int bw, int fq);
-+int ath9k_hsr_status(struct ath_hw *ah);
-+
-+#else
-+static inline void ath9k_hsr_init(struct ath_hw *ah) {}
-+
-+static inline int ath9k_hsr_enable(struct ath_hw *ah, int bw, int fq)
-+{
-+	return 0;
-+}
-+
-+static inline int ath9k_hsr_disable(struct ath_hw *ah) { return 0; }
-+static inline int ath9k_hsr_status(struct ath_hw *ah) { return 0; }
-+
-+#endif
-+
-+#endif /* HSR_H */
-diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
-index f7c6d9b..5c015ac 100644
---- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
-+++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
-@@ -514,6 +514,8 @@ int ath9k_htc_init_debug(struct ath_hw *ah)
- 
- 	ath9k_cmn_debug_base_eeprom(priv->debug.debugfs_phy, priv->ah);
- 	ath9k_cmn_debug_modal_eeprom(priv->debug.debugfs_phy, priv->ah);
-+	ath9k_cmn_debug_eeprom(priv->debug.debugfs_phy, priv->ah);
-+	ath9k_cmn_debug_chanbw(priv->debug.debugfs_phy, priv->ah);
- 
- 	return 0;
- }
-diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
-index fa02d9a..53e49b6 100644
---- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
-+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
-@@ -631,6 +631,7 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv,
- 	priv->ah = ah;
- 
- 	common = ath9k_hw_common(ah);
-+	common->ieee_ops = &ath9k_htc_ops;
- 	common->ops = &ah->reg_ops;
- 	common->ps_ops = &ath9k_htc_ps_ops;
- 	common->bus_ops = &ath9k_usb_bus_ops;
-@@ -746,9 +747,9 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
- 
- 	hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN |
- 			    WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
--			    WIPHY_FLAG_HAS_CHANNEL_SWITCH;
--
--	hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
-+			    WIPHY_FLAG_HAS_CHANNEL_SWITCH |
-+			    WIPHY_FLAG_SUPPORTS_5_10_MHZ |
-+			    WIPHY_FLAG_SUPPORTS_TDLS;
- 
- 	hw->queues = 4;
- 	hw->max_listen_interval = 1;
-diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h
-index 174d716..605abe1 100644
---- a/drivers/net/wireless/ath/ath9k/hw-ops.h
-+++ b/drivers/net/wireless/ath/ath9k/hw-ops.h
-@@ -100,6 +100,12 @@ static inline void ath9k_hw_tx99_set_txpower(struct ath_hw *ah, u8 power)
- 		ath9k_hw_ops(ah)->tx99_set_txpower(ah, power);
- }
- 
-+static inline void ath9k_hw_get_adc_entropy(struct ath_hw *ah,
-+		u8 *buf, size_t len)
-+{
-+	ath9k_hw_ops(ah)->get_adc_entropy(ah, buf, len);
-+}
-+
- #ifdef CPTCFG_ATH9K_BTCOEX_SUPPORT
- 
- static inline void ath9k_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable)
-diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
-index fafe1f0..f53964b 100644
---- a/drivers/net/wireless/ath/ath9k/hw.c
-+++ b/drivers/net/wireless/ath/ath9k/hw.c
-@@ -247,6 +247,19 @@ void ath9k_hw_get_channel_centers(struct ath_hw *ah,
- 		centers->synth_center + (extoff * HT40_CHANNEL_CENTER_SHIFT);
- }
- 
-+static inline void ath9k_hw_disable_pll_lock_detect(struct ath_hw *ah)
-+{
-+	/* On AR9330 and AR9340 devices, some PHY registers must be
-+	 * tuned to gain better stability/performance. These registers
-+	 * might be changed while doing wlan reset so the registers must
-+	 * be reprogrammed after each reset.
-+	 */
-+	REG_CLR_BIT(ah, AR_PHY_USB_CTRL1, BIT(20));
-+	REG_RMW(ah, AR_PHY_USB_CTRL2,
-+		(1 << 21) | (0xf << 22),
-+		(1 << 21) | (0x3 << 22));
-+}
-+
- /******************/
- /* Chip Revisions */
- /******************/
-@@ -402,13 +415,8 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
- 
- 	ah->config.rx_intr_mitigation = true;
- 
--	if (AR_SREV_9300_20_OR_LATER(ah)) {
--		ah->config.rimt_last = 500;
--		ah->config.rimt_first = 2000;
--	} else {
--		ah->config.rimt_last = 250;
--		ah->config.rimt_first = 700;
--	}
-+	ah->config.rimt_last = 250;
-+	ah->config.rimt_first = 500;
- 
- 	if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
- 		ah->config.pll_pwrsave = 7;
-@@ -667,6 +675,7 @@ int ath9k_hw_init(struct ath_hw *ah)
- 
- 	/* These are all the AR5008/AR9001/AR9002/AR9003 hardware family of chipsets */
- 	switch (ah->hw_version.devid) {
-+	case AR9300_DEVID_INVALID:
- 	case AR5416_DEVID_PCI:
- 	case AR5416_DEVID_PCIE:
- 	case AR5416_AR9100_DEVID:
-@@ -1311,39 +1320,56 @@ void ath9k_hw_get_delta_slope_vals(struct ath_hw *ah, u32 coef_scaled,
- 	*coef_exponent = coef_exp - 16;
- }
- 
--/* AR9330 WAR:
-- * call external reset function to reset WMAC if:
-- * - doing a cold reset
-- * - we have pending frames in the TX queues.
-- */
--static bool ath9k_hw_ar9330_reset_war(struct ath_hw *ah, int type)
-+static bool ath9k_hw_need_external_reset(struct ath_hw *ah, int type)
- {
--	int i, npend = 0;
-+	int i;
- 
--	for (i = 0; i < AR_NUM_QCU; i++) {
--		npend = ath9k_hw_numtxpending(ah, i);
--		if (npend)
--			break;
-+	if (type == ATH9K_RESET_COLD)
-+		return true;
-+
-+	if (AR_SREV_9550(ah))
-+		return true;
-+
-+	/* AR9330 WAR:
-+	 * call external reset function to reset WMAC if:
-+	 * - doing a cold reset
-+	 * - we have pending frames in the TX queues.
-+	 */
-+	if (AR_SREV_9330(ah)) {
-+		for (i = 0; i < AR_NUM_QCU; i++) {
-+			if (ath9k_hw_numtxpending(ah, i))
-+				return true;
-+		}
- 	}
- 
--	if (ah->external_reset &&
--	    (npend || type == ATH9K_RESET_COLD)) {
--		int reset_err = 0;
-+	return false;
-+}
- 
--		ath_dbg(ath9k_hw_common(ah), RESET,
--			"reset MAC via external reset\n");
-+static bool ath9k_hw_external_reset(struct ath_hw *ah, int type)
-+{
-+	int err;
- 
--		reset_err = ah->external_reset();
--		if (reset_err) {
--			ath_err(ath9k_hw_common(ah),
--				"External reset failed, err=%d\n",
--				reset_err);
--			return false;
--		}
-+	if (!ah->external_reset || !ath9k_hw_need_external_reset(ah, type))
-+		return true;
- 
--		REG_WRITE(ah, AR_RTC_RESET(ah), 1);
-+	ath_dbg(ath9k_hw_common(ah), RESET,
-+		"reset MAC via external reset\n");
-+
-+	err = ah->external_reset();
-+	if (err) {
-+		ath_err(ath9k_hw_common(ah),
-+			"External reset failed, err=%d\n", err);
-+		return false;
- 	}
- 
-+	if (AR_SREV_9550(ah)) {
-+		REG_WRITE(ah, AR_RTC_RESET(ah), 0);
-+		udelay(10);
-+	}
-+
-+	REG_WRITE(ah, AR_RTC_RESET(ah), 1);
-+	udelay(10);
-+
- 	return true;
- }
- 
-@@ -1396,24 +1422,24 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type)
- 			rst_flags |= AR_RTC_RC_MAC_COLD;
- 	}
- 
--	if (AR_SREV_9330(ah)) {
--		if (!ath9k_hw_ar9330_reset_war(ah, type))
--			return false;
--	}
--
- 	if (ath9k_hw_mci_is_enabled(ah))
- 		ar9003_mci_check_gpm_offset(ah);
- 
- 	/* DMA HALT added to resolve ar9300 and ar9580 bus error during
--	 * RTC_RC reg read
-+	 * RTC_RC reg read. Also needed for AR9550 external reset
- 	 */
--	if (AR_SREV_9300(ah) || AR_SREV_9580(ah)) {
-+	if (AR_SREV_9300(ah) || AR_SREV_9580(ah) || AR_SREV_9550(ah)) {
- 		REG_SET_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
- 		ath9k_hw_wait(ah, AR_CFG, AR_CFG_HALT_ACK, AR_CFG_HALT_ACK,
- 			      20 * AH_WAIT_TIMEOUT);
--		REG_CLR_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
- 	}
- 
-+	if (!AR_SREV_9100(ah))
-+		ath9k_hw_external_reset(ah, type);
-+
-+	if (AR_SREV_9300(ah) || AR_SREV_9580(ah))
-+		REG_CLR_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
-+
- 	REG_WRITE(ah, AR_RTC_RC(ah), rst_flags);
- 
- 	REGWRITE_BUFFER_FLUSH(ah);
-@@ -1434,8 +1460,15 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type)
- 	if (!AR_SREV_9100(ah))
- 		REG_WRITE(ah, AR_RC, 0);
- 
--	if (AR_SREV_9100(ah))
-+	if (AR_SREV_9100(ah)) {
-+		/* Reset the AHB-WMAC interface */
-+		if (ah->external_reset)
-+			ah->external_reset();
- 		udelay(50);
-+	}
-+
-+	if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
-+		ath9k_hw_disable_pll_lock_detect(ah);
- 
- 	return true;
- }
-@@ -1536,6 +1569,9 @@ static bool ath9k_hw_chip_reset(struct ath_hw *ah,
- 		ar9003_hw_internal_regulator_apply(ah);
- 	ath9k_hw_init_pll(ah, chan);
- 
-+	if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
-+		ath9k_hw_disable_pll_lock_detect(ah);
-+
- 	return true;
- }
- 
-@@ -1842,8 +1878,14 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan)
- 	if (AR_SREV_9271(ah))
- 		ar9002_hw_load_ani_reg(ah, chan);
- 
-+	if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
-+		ath9k_hw_disable_pll_lock_detect(ah);
-+
- 	return 0;
- fail:
-+	if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
-+		ath9k_hw_disable_pll_lock_detect(ah);
-+
- 	return -EINVAL;
- }
- 
-@@ -1864,6 +1906,20 @@ u32 ath9k_hw_get_tsf_offset(struct timespec64 *last, struct timespec64 *cur)
- }
- EXPORT_SYMBOL(ath9k_hw_get_tsf_offset);
- 
-+void ath9k_hw_update_diag(struct ath_hw *ah)
-+{
-+	if (test_bit(ATH_DIAG_DISABLE_RX, &ah->diag))
-+		REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
-+	else
-+		REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
-+
-+	if (test_bit(ATH_DIAG_DISABLE_TX, &ah->diag))
-+		REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_LOOP_BACK);
-+	else
-+		REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_LOOP_BACK);
-+}
-+EXPORT_SYMBOL(ath9k_hw_update_diag);
-+
- int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
- 		   struct ath9k_hw_cal_data *caldata, bool fastcc)
- {
-@@ -2072,6 +2128,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
- 		ar9003_hw_disable_phy_restart(ah);
- 
- 	ath9k_hw_apply_gpio_override(ah);
-+	ath9k_hw_update_diag(ah);
- 
- 	if (AR_SREV_9565(ah) && common->bt_ant_diversity)
- 		REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON);
-@@ -2082,6 +2139,9 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
- 		ath9k_hw_set_radar_params(ah);
- 	}
- 
-+	if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
-+		ath9k_hw_disable_pll_lock_detect(ah);
-+
- 	return 0;
- }
- EXPORT_SYMBOL(ath9k_hw_reset);
-@@ -2956,7 +3016,8 @@ void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan,
- {
- 	struct ath_regulatory *reg = ath9k_hw_regulatory(ah);
- 	struct ieee80211_channel *channel;
--	int chan_pwr, new_pwr;
-+	int chan_pwr, new_pwr, max_gain;
-+	int ant_gain, ant_reduction = 0;
- 	u16 ctl = NO_CTL;
- 
- 	if (!chan)
-@@ -2968,9 +3029,18 @@ void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan,
- 	channel = chan->chan;
- 	chan_pwr = min_t(int, channel->max_power * 2, MAX_COMBINED_POWER);
- 	new_pwr = min_t(int, chan_pwr, reg->power_limit);
-+	max_gain = chan_pwr - new_pwr + channel->max_antenna_gain * 2;
-+
-+	ant_gain = get_antenna_gain(ah, chan);
-+	if (ant_gain > max_gain)
-+		ant_reduction = ant_gain - max_gain;
-+
-+	/* FCC allows maximum antenna gain of 6 dBi */
-+	if (reg->region == NL80211_DFS_FCC)
-+		ant_reduction = max_t(int, ant_reduction - 12, 0);
- 
- 	ah->eep_ops->set_txpower(ah, chan, ctl,
--				 get_antenna_gain(ah, chan), new_pwr, test);
-+				 ant_reduction, new_pwr, test);
- }
- 
- void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit, bool test)
-diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
-index 6243626..8d756dc 100644
---- a/drivers/net/wireless/ath/ath9k/hw.h
-+++ b/drivers/net/wireless/ath/ath9k/hw.h
-@@ -36,6 +36,7 @@
- 
- #define ATHEROS_VENDOR_ID	0x168c
- 
-+#define AR9300_DEVID_INVALID	0xabcd
- #define AR5416_DEVID_PCI	0x0023
- #define AR5416_DEVID_PCIE	0x0024
- #define AR9160_DEVID_PCI	0x0027
-@@ -521,6 +522,12 @@ enum {
- 	ATH9K_RESET_COLD,
- };
- 
-+enum {
-+	ATH_DIAG_DISABLE_RX,
-+	ATH_DIAG_DISABLE_TX,
-+	ATH_DIAG_TRIGGER_ERROR,
-+};
-+
- struct ath9k_hw_version {
- 	u32 magic;
- 	u16 devid;
-@@ -716,6 +723,7 @@ struct ath_spec_scan {
-  * @config_pci_powersave:
-  * @calibrate: periodic calibration for NF, ANI, IQ, ADC gain, ADC-DC
-  *
-+ * @get_adc_entropy: get entropy from the raw ADC I/Q output
-  * @spectral_scan_config: set parameters for spectral scan and enable/disable it
-  * @spectral_scan_trigger: trigger a spectral scan run
-  * @spectral_scan_wait: wait for a spectral scan run to finish
-@@ -738,6 +746,7 @@ struct ath_hw_ops {
- 			struct ath_hw_antcomb_conf *antconf);
- 	void (*antdiv_comb_conf_set)(struct ath_hw *ah,
- 			struct ath_hw_antcomb_conf *antconf);
-+	void (*get_adc_entropy)(struct ath_hw *ah, u8 *buf, size_t len);
- 	void (*spectral_scan_config)(struct ath_hw *ah,
- 				     struct ath_spec_scan *param);
- 	void (*spectral_scan_trigger)(struct ath_hw *ah);
-@@ -809,6 +818,8 @@ struct ath_hw {
- 	u32 ah_flags;
- 	s16 nf_override;
- 
-+	unsigned long diag;
-+
- 	bool reset_power_on;
- 	bool htc_reset_init;
- 
-@@ -1078,6 +1089,7 @@ void ath9k_hw_check_nav(struct ath_hw *ah);
- bool ath9k_hw_check_alive(struct ath_hw *ah);
- 
- bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode);
-+void ath9k_hw_update_diag(struct ath_hw *ah);
- 
- /* Generic hw timer primitives */
- struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
-diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
-index 2458387..2f5266c 100644
---- a/drivers/net/wireless/ath/ath9k/init.c
-+++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -48,7 +48,7 @@ int ath9k_modparam_nohwcrypt;
- module_param_named(nohwcrypt, ath9k_modparam_nohwcrypt, int, 0444);
- MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption");
- 
--int ath9k_led_blink;
-+int ath9k_led_blink = 1;
- module_param_named(blink, ath9k_led_blink, int, 0444);
- MODULE_PARM_DESC(blink, "Enable LED blink on activity");
- 
-@@ -696,6 +696,12 @@ static int ath9k_of_init(struct ath_softc *sc)
- 	return 0;
- }
- 
-+static void ath9k_of_gpio_mask(struct ath_softc *sc)
-+{
-+	of_property_read_u32(sc->dev->of_node, "qca,gpio-mask",
-+			     &sc->sc_ah->caps.gpio_mask);
-+}
-+
- static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
- 			    const struct ath_bus_ops *bus_ops)
- {
-@@ -733,6 +739,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
- 	if (!ath9k_is_chanctx_enabled())
- 		sc->cur_chan->hw_queue_base = 0;
- 
-+	common->ieee_ops = &ath9k_ops;
- 	common->ops = &ah->reg_ops;
- 	common->bus_ops = bus_ops;
- 	common->ps_ops = &ath9k_ps_ops;
-@@ -803,6 +810,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
- 	if (ret)
- 		goto err_hw;
- 
-+	/* GPIO mask quirk */
-+	ath9k_of_gpio_mask(sc);
-+
- 	ret = ath9k_init_queues(sc);
- 	if (ret)
- 		goto err_queues;
-@@ -870,7 +880,8 @@ static void ath9k_init_txpower_limits(struct ath_softc *sc)
- 	if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
- 		ath9k_init_band_txpower(sc, NL80211_BAND_5GHZ);
- 
--	ah->curchan = curchan;
-+	if (curchan)
-+		ah->curchan = curchan;
- }
- 
- static const struct ieee80211_iface_limit if_limits[] = {
-@@ -882,6 +893,7 @@ static const struct ieee80211_iface_limit if_limits[] = {
- 				 BIT(NL80211_IFTYPE_AP) },
- 	{ .max = 1,	.types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
- 				 BIT(NL80211_IFTYPE_P2P_GO) },
-+	{ .max = 1,	.types = BIT(NL80211_IFTYPE_ADHOC) },
- };
- 
- #ifdef CPTCFG_ATH9K_CHANNEL_CONTEXT
-@@ -962,6 +974,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
- 	ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
- 	ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
- 	ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
-+	ieee80211_hw_set(hw, MFP_CAPABLE);
- 
- 	if (ath9k_ps_enable)
- 		ieee80211_hw_set(hw, SUPPORTS_PS);
-@@ -974,9 +987,6 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
- 				IEEE80211_RADIOTAP_MCS_HAVE_STBC;
- 	}
- 
--	if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || ath9k_modparam_nohwcrypt)
--		ieee80211_hw_set(hw, MFP_CAPABLE);
--
- 	hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR |
- 			       NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
- 			       NL80211_FEATURE_P2P_GO_CTWIN;
-@@ -1049,6 +1059,18 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
- 	wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
- }
- 
-+static void ath_get_initial_entropy(struct ath_softc *sc)
-+{
-+	struct ath_hw *ah = sc->sc_ah;
-+	char buf[256];
-+
-+	/* reuse last channel initialized by the tx power test */
-+	ath9k_hw_reset(ah, ah->curchan, NULL, false);
-+
-+	ath9k_hw_get_adc_entropy(ah, buf, sizeof(buf));
-+	add_device_randomness(buf, sizeof(buf));
-+}
-+
- int ath9k_init_device(u16 devid, struct ath_softc *sc,
- 		    const struct ath_bus_ops *bus_ops)
- {
-@@ -1089,13 +1111,15 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
- 
- #ifdef CPTCFG_MAC80211_LEDS
- 	/* must be initialized before ieee80211_register_hw */
--	sc->led_cdev.default_trigger = ieee80211_create_tpt_led_trigger(sc->hw,
-+	sc->led_default_trigger = ieee80211_create_tpt_led_trigger(sc->hw,
- 		IEEE80211_TPT_LEDTRIG_FL_RADIO, ath9k_tpt_blink,
- 		ARRAY_SIZE(ath9k_tpt_blink));
- #endif
- 
- 	wiphy_read_of_freq_limits(hw->wiphy);
- 
-+	ath_get_initial_entropy(sc);
-+
- 	/* Register with mac80211 */
- 	error = ieee80211_register_hw(hw);
- 	if (error)
-@@ -1179,25 +1203,25 @@ static int __init ath9k_init(void)
- {
- 	int error;
- 
--	error = ath_pci_init();
-+	error = ath_ahb_init();
- 	if (error < 0) {
--		pr_err("No PCI devices found, driver not installed\n");
- 		error = -ENODEV;
- 		goto err_out;
- 	}
- 
--	error = ath_ahb_init();
-+	error = ath_pci_init();
- 	if (error < 0) {
-+		pr_err("No PCI devices found, driver not installed\n");
- 		error = -ENODEV;
--		goto err_pci_exit;
-+		goto err_ahb_exit;
- 	}
- 
- 	dmi_check_system(ath9k_quirks);
- 
- 	return 0;
- 
-- err_pci_exit:
--	ath_pci_exit();
-+ err_ahb_exit:
-+	ath_ahb_exit();
-  err_out:
- 	return error;
- }
-diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
-index b070403..d8d14a5 100644
---- a/drivers/net/wireless/ath/ath9k/mac.c
-+++ b/drivers/net/wireless/ath/ath9k/mac.c
-@@ -678,13 +678,18 @@ void ath9k_hw_startpcureceive(struct ath_hw *ah, bool is_scanning)
- 
- 	ath9k_ani_reset(ah, is_scanning);
- 
--	REG_CLR_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
-+	REG_CLR_BIT(ah, AR_DIAG_SW,
-+		    AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT | AR_DIAG_FORCE_RX_CLEAR);
- }
- EXPORT_SYMBOL(ath9k_hw_startpcureceive);
- 
- void ath9k_hw_abortpcurecv(struct ath_hw *ah)
- {
--	REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_ABORT | AR_DIAG_RX_DIS);
-+	u32 reg = AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT;
-+
-+	if (!IS_ENABLED(CPTCFG_ATH9K_TX99))
-+		reg |= AR_DIAG_FORCE_RX_CLEAR;
-+	REG_SET_BIT(ah, AR_DIAG_SW, reg);
- 
- 	ath9k_hw_disable_mib_counters(ah);
- }
-diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
-index 6d120df..2807d36 100644
---- a/drivers/net/wireless/ath/ath9k/main.c
-+++ b/drivers/net/wireless/ath/ath9k/main.c
-@@ -18,6 +18,7 @@
- #include <linux/delay.h>
- #include "ath9k.h"
- #include "btcoex.h"
-+#include "hsr.h"
- 
- static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 			u32 queues, bool drop);
-@@ -538,6 +539,11 @@ irqreturn_t ath_isr(int irq, void *dev)
- 		return IRQ_HANDLED;
- 	}
- 
-+	if (test_bit(ATH_DIAG_TRIGGER_ERROR, &ah->diag)) {
-+		status |= ATH9K_INT_FATAL;
-+		clear_bit(ATH_DIAG_TRIGGER_ERROR, &ah->diag);
-+	}
-+
- 	/*
- 	 * If there are no status bits set, then this interrupt was not
- 	 * for me (should have been caught above).
-@@ -654,6 +660,7 @@ void ath_reset_work(struct work_struct *work)
- static int ath9k_start(struct ieee80211_hw *hw)
- {
- 	struct ath_softc *sc = hw->priv;
-+	struct device_node *np = sc->dev->of_node;
- 	struct ath_hw *ah = sc->sc_ah;
- 	struct ath_common *common = ath9k_hw_common(ah);
- 	struct ieee80211_channel *curchan = sc->cur_chan->chandef.chan;
-@@ -732,6 +739,11 @@ static int ath9k_start(struct ieee80211_hw *hw)
- 					  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
- 	}
- 
-+	if (of_property_read_bool(np, "ubnt,hsr")) {
-+		ath9k_hsr_init(ah);
-+		ath9k_hsr_disable(ah);
-+	}
-+
- 	/*
- 	 * Reset key cache to sane defaults (all entries cleared) instead of
- 	 * semi-random values after suspend/resume.
-diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
-index 6421e49..20c7095 100644
---- a/drivers/net/wireless/ath/ath9k/pci.c
-+++ b/drivers/net/wireless/ath/ath9k/pci.c
-@@ -772,6 +772,7 @@ static const struct pci_device_id ath_pci_id_table[] = {
- 	  .driver_data = ATH9K_PCI_BT_ANT_DIV },
- #endif
- 
-+	{ PCI_VDEVICE(ATHEROS, 0xabcd) }, /* PCI-E  internal chip default ID */
- 	{ 0 }
- };
- 
-diff --git a/drivers/net/wireless/ath/ath9k/phy.h b/drivers/net/wireless/ath/ath9k/phy.h
-index 4a1b992..af667a3 100644
---- a/drivers/net/wireless/ath/ath9k/phy.h
-+++ b/drivers/net/wireless/ath/ath9k/phy.h
-@@ -48,6 +48,9 @@
- #define AR_PHY_PLL_CONTROL 0x16180
- #define AR_PHY_PLL_MODE 0x16184
- 
-+#define AR_PHY_USB_CTRL1	0x16c84
-+#define AR_PHY_USB_CTRL2	0x16c88
-+
- enum ath9k_ant_div_comb_lna_conf {
- 	ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2,
- 	ATH_ANT_DIV_COMB_LNA2,
-diff --git a/include/linux/ath9k_platform.h b/include/linux/ath9k_platform.h
-index 76860a4..e210108 100644
---- a/include/linux/ath9k_platform.h
-+++ b/include/linux/ath9k_platform.h
-@@ -46,6 +46,13 @@ struct ath9k_platform_data {
- 	int (*external_reset)(void);
- 
- 	bool use_eeprom;
-+
-+	int num_leds;
-+	const struct gpio_led *leds;
-+
-+	unsigned num_btns;
-+	const struct gpio_keys_button *btns;
-+	unsigned btn_poll_interval;
- };
- 
- #endif /* _LINUX_ATH9K_PLATFORM_H */
-diff --git a/local-symbols b/local-symbols
-index b200b00..b5745bf 100644
---- a/local-symbols
-+++ b/local-symbols
-@@ -128,6 +128,7 @@ ATH9K_WOW=
- ATH9K_RFKILL=
- ATH9K_CHANNEL_CONTEXT=
- ATH9K_PCOEM=
-+ATH9K_UBNTHSR=
- ATH9K_PCI_NO_EEPROM=
- ATH9K_HTC=
- ATH9K_HTC_DEBUGFS=
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0005-backports-sync-openwrt-patches-ath10k.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0005-backports-sync-openwrt-patches-ath10k.patch
new file mode 100644
index 0000000..3c17715
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0005-backports-sync-openwrt-patches-ath10k.patch
@@ -0,0 +1,417 @@
+From e99a66604dd4cfdfcbf349d3b1fe2007af87e1e6 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 17 Jul 2024 20:42:15 +0800
+Subject: [PATCH 05/89] backports: sync openwrt patches/ath10k
+
+---
+ drivers/net/wireless/ath/ath10k/Kconfig   |  6 +++
+ drivers/net/wireless/ath/ath10k/core.c    | 13 +++++
+ drivers/net/wireless/ath/ath10k/core.h    |  4 ++
+ drivers/net/wireless/ath/ath10k/htt.h     |  4 ++
+ drivers/net/wireless/ath/ath10k/leds.c    |  2 +-
+ drivers/net/wireless/ath/ath10k/mac.c     | 65 +++++++++++++++++++++--
+ drivers/net/wireless/ath/ath10k/pci.c     | 16 ++++++
+ drivers/net/wireless/ath/ath10k/thermal.h |  2 +-
+ drivers/net/wireless/ath/ath10k/wmi.c     | 43 +++++++++++++++
+ drivers/net/wireless/ath/ath10k/wmi.h     | 35 ++++++++++++
+ local-symbols                             |  2 +
+ 11 files changed, 186 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig
+index 57c3be9..d8f919c 100644
+--- a/drivers/net/wireless/ath/ath10k/Kconfig
++++ b/drivers/net/wireless/ath/ath10k/Kconfig
+@@ -94,6 +94,12 @@ config ATH10K_TRACING
+ 	help
+ 	  Select this to ath10k use tracing infrastructure.
+ 
++config ATH10K_THERMAL
++	bool "Atheros ath10k thermal monitoring support"
++	depends on THERMAL
++	---help---
++	  Select this to ath10k use hwmon for thermal measurement.
++
+ config ATH10K_DFS_CERTIFIED
+ 	bool "Atheros DFS support for certified platforms"
+ 	depends on ATH10K && CFG80211_CERTIFICATION_ONUS
+diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
+index b329428..ff87580 100644
+--- a/drivers/net/wireless/ath/ath10k/core.c
++++ b/drivers/net/wireless/ath/ath10k/core.c
+@@ -9,6 +9,7 @@
+ #include <linux/module.h>
+ #include <linux/firmware.h>
+ #include <linux/of.h>
++#include <linux/of_net.h>
+ #include <linux/property.h>
+ #include <linux/dmi.h>
+ #include <linux/ctype.h>
+@@ -3411,6 +3412,8 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
+ 
+ 	device_get_mac_address(ar->dev, ar->mac_addr);
+ 
++	of_get_mac_address(ar->dev->of_node, ar->mac_addr);
++
+ 	ret = ath10k_core_init_firmware_features(ar);
+ 	if (ret) {
+ 		ath10k_err(ar, "fatal problem with firmware features: %d\n",
+@@ -3539,6 +3542,16 @@ int ath10k_core_register(struct ath10k *ar,
+ 
+ 	queue_work(ar->workqueue, &ar->register_work);
+ 
++	/* OpenWrt requires all PHYs to be initialized to create the
++	 * configuration files during bootup. ath10k violates this
++	 * because it delays the creation of the PHY to a not well defined
++	 * point in the future.
++	 *
++	 * Forcing the work to be done immediately works around this problem
++	 * but may also delay the boot when firmware images cannot be found.
++	 */
++	flush_workqueue(ar->workqueue);
++
+ 	return 0;
+ }
+ EXPORT_SYMBOL(ath10k_core_register);
+diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
+index b893c43..394c978 100644
+--- a/drivers/net/wireless/ath/ath10k/core.h
++++ b/drivers/net/wireless/ath/ath10k/core.h
+@@ -1315,6 +1315,10 @@ struct ath10k {
+ 	s32 tx_power_2g_limit;
+ 	s32 tx_power_5g_limit;
+ 
++#ifdef CPTCFG_MAC80211_LEDS
++	const char *led_default_trigger;
++#endif
++
+ 	/* must be last */
+ 	u8 drv_priv[] __aligned(sizeof(void *));
+ };
+diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
+index 603f6de..bea1d16 100644
+--- a/drivers/net/wireless/ath/ath10k/htt.h
++++ b/drivers/net/wireless/ath/ath10k/htt.h
+@@ -236,7 +236,11 @@ enum htt_rx_ring_flags {
+ };
+ 
+ #define HTT_RX_RING_SIZE_MIN 128
++#ifndef CONFIG_ATH10K_SMALLBUFFERS
+ #define HTT_RX_RING_SIZE_MAX 2048
++#else
++#define HTT_RX_RING_SIZE_MAX 512
++#endif
+ #define HTT_RX_RING_SIZE HTT_RX_RING_SIZE_MAX
+ #define HTT_RX_RING_FILL_LEVEL (((HTT_RX_RING_SIZE) / 2) - 1)
+ #define HTT_RX_RING_FILL_LEVEL_DUAL_MAC (HTT_RX_RING_SIZE - 1)
+diff --git a/drivers/net/wireless/ath/ath10k/leds.c b/drivers/net/wireless/ath/ath10k/leds.c
+index 9b1d04e..b82033b 100644
+--- a/drivers/net/wireless/ath/ath10k/leds.c
++++ b/drivers/net/wireless/ath/ath10k/leds.c
+@@ -70,7 +70,7 @@ int ath10k_leds_register(struct ath10k *ar)
+ 
+ 	ar->leds.cdev.name = ar->leds.label;
+ 	ar->leds.cdev.brightness_set_blocking = ath10k_leds_set_brightness_blocking;
+-	ar->leds.cdev.default_trigger = ar->leds.wifi_led.default_trigger;
++	ar->leds.cdev.default_trigger = ar->led_default_trigger;
+ 
+ 	ret = led_classdev_register(wiphy_dev(ar->hw->wiphy), &ar->leds.cdev);
+ 	if (ret)
+diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
+index 35cb06d..9f96315 100644
+--- a/drivers/net/wireless/ath/ath10k/mac.c
++++ b/drivers/net/wireless/ath/ath10k/mac.c
+@@ -1022,6 +1022,40 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
+ 	return ar->last_wmi_vdev_start_status;
+ }
+ 
++static u32 ath10k_get_max_antenna_gain(struct ath10k *ar,
++				       u32 ch_max_antenna_gain)
++{
++	u32 max_antenna_gain;
++
++	if (ar->dfs_detector && ar->dfs_detector->region == NL80211_DFS_FCC) {
++		/* FCC allows maximum antenna gain of 6 dBi. 15.247(b)(4):
++		 *
++		 * > (4) The conducted output power limit
++		 * > specified in paragraph (b) of this section
++		 * > is based on the use of antennas
++		 * > with directional gains that do not exceed
++		 * > 6 dBi. Except as shown in paragraph
++		 * > (c) of this section, if transmitting
++		 * > antennas of directional gain greater
++		 * > than 6 dBi are used, the conducted
++		 * > output power from the intentional radiator
++		 * > shall be reduced below the stated
++		 * > values in paragraphs (b)(1), (b)(2),
++		 * > and (b)(3) of this section, as appropriate,
++		 * > by the amount in dB that the
++		 * > directional gain of the antenna exceeds
++		 * > 6 dBi.
++		 *
++		 * https://www.gpo.gov/fdsys/pkg/CFR-2013-title47-vol1/pdf/CFR-2013-title47-vol1-sec15-247.pdf
++		 */
++		max_antenna_gain = 6;
++	} else {
++		max_antenna_gain = 0;
++	}
++
++	return max(ch_max_antenna_gain, max_antenna_gain);
++}
++
+ static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
+ {
+ 	struct cfg80211_chan_def *chandef = NULL;
+@@ -1054,7 +1088,8 @@ static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
+ 	arg.channel.min_power = 0;
+ 	arg.channel.max_power = channel->max_power * 2;
+ 	arg.channel.max_reg_power = channel->max_reg_power * 2;
+-	arg.channel.max_antenna_gain = channel->max_antenna_gain;
++	arg.channel.max_antenna_gain = ath10k_get_max_antenna_gain(ar,
++						channel->max_antenna_gain);
+ 
+ 	reinit_completion(&ar->vdev_setup_done);
+ 	reinit_completion(&ar->vdev_delete_done);
+@@ -1500,7 +1535,8 @@ static int ath10k_vdev_start_restart(struct ath10k_vif *arvif,
+ 	arg.channel.min_power = 0;
+ 	arg.channel.max_power = chandef->chan->max_power * 2;
+ 	arg.channel.max_reg_power = chandef->chan->max_reg_power * 2;
+-	arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain;
++	arg.channel.max_antenna_gain = ath10k_get_max_antenna_gain(ar,
++					chandef->chan->max_antenna_gain);
+ 
+ 	if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+ 		arg.ssid = arvif->u.ap.ssid;
+@@ -3431,7 +3467,8 @@ static int ath10k_update_channel_list(struct ath10k *ar)
+ 			ch->min_power = 0;
+ 			ch->max_power = channel->max_power * 2;
+ 			ch->max_reg_power = channel->max_reg_power * 2;
+-			ch->max_antenna_gain = channel->max_antenna_gain;
++			ch->max_antenna_gain = ath10k_get_max_antenna_gain(ar,
++						channel->max_antenna_gain);
+ 			ch->reg_class_id = 0; /* FIXME */
+ 
+ 			/* FIXME: why use only legacy modes, why not any
+@@ -9919,6 +9956,21 @@ static int ath10k_mac_init_rd(struct ath10k *ar)
+ 	return 0;
+ }
+ 
++#ifdef CPTCFG_MAC80211_LEDS
++static const struct ieee80211_tpt_blink ath10k_tpt_blink[] = {
++	{ .throughput = 0 * 1024, .blink_time = 334 },
++	{ .throughput = 1 * 1024, .blink_time = 260 },
++	{ .throughput = 2 * 1024, .blink_time = 220 },
++	{ .throughput = 5 * 1024, .blink_time = 190 },
++	{ .throughput = 10 * 1024, .blink_time = 170 },
++	{ .throughput = 25 * 1024, .blink_time = 150 },
++	{ .throughput = 54 * 1024, .blink_time = 130 },
++	{ .throughput = 120 * 1024, .blink_time = 110 },
++	{ .throughput = 265 * 1024, .blink_time = 80 },
++	{ .throughput = 586 * 1024, .blink_time = 50 },
++};
++#endif
++
+ int ath10k_mac_register(struct ath10k *ar)
+ {
+ 	static const u32 cipher_suites[] = {
+@@ -10037,7 +10089,6 @@ int ath10k_mac_register(struct ath10k *ar)
+ 	ieee80211_hw_set(ar->hw, CHANCTX_STA_CSA);
+ 	ieee80211_hw_set(ar->hw, QUEUE_CONTROL);
+ 	ieee80211_hw_set(ar->hw, SUPPORTS_TX_FRAG);
+-	ieee80211_hw_set(ar->hw, REPORTS_LOW_ACK);
+ 
+ 	if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags))
+ 		ieee80211_hw_set(ar->hw, SW_CRYPTO_CONTROL);
+@@ -10281,6 +10332,12 @@ int ath10k_mac_register(struct ath10k *ar)
+ 
+ 	ar->hw->weight_multiplier = ATH10K_AIRTIME_WEIGHT_MULTIPLIER;
+ 
++#ifdef CPTCFG_MAC80211_LEDS
++	ar->led_default_trigger = ieee80211_create_tpt_led_trigger(ar->hw,
++		IEEE80211_TPT_LEDTRIG_FL_RADIO, ath10k_tpt_blink,
++		ARRAY_SIZE(ath10k_tpt_blink));
++#endif
++
+ 	ret = ieee80211_register_hw(ar->hw);
+ 	if (ret) {
+ 		ath10k_err(ar, "failed to register ieee80211: %d\n", ret);
+diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
+index 36ac452..3c5164c 100644
+--- a/drivers/net/wireless/ath/ath10k/pci.c
++++ b/drivers/net/wireless/ath/ath10k/pci.c
+@@ -132,7 +132,11 @@ static const struct ce_attr pci_host_ce_config_wlan[] = {
+ 		.flags = CE_ATTR_FLAGS,
+ 		.src_nentries = 0,
+ 		.src_sz_max = 2048,
++#ifndef CONFIG_ATH10K_SMALLBUFFERS
+ 		.dest_nentries = 512,
++#else
++		.dest_nentries = 128,
++#endif
+ 		.recv_cb = ath10k_pci_htt_htc_rx_cb,
+ 	},
+ 
+@@ -141,7 +145,11 @@ static const struct ce_attr pci_host_ce_config_wlan[] = {
+ 		.flags = CE_ATTR_FLAGS,
+ 		.src_nentries = 0,
+ 		.src_sz_max = 2048,
++#ifndef CONFIG_ATH10K_SMALLBUFFERS
+ 		.dest_nentries = 128,
++#else
++		.dest_nentries = 64,
++#endif
+ 		.recv_cb = ath10k_pci_htc_rx_cb,
+ 	},
+ 
+@@ -168,7 +176,11 @@ static const struct ce_attr pci_host_ce_config_wlan[] = {
+ 		.flags = CE_ATTR_FLAGS,
+ 		.src_nentries = 0,
+ 		.src_sz_max = 512,
++#ifndef CONFIG_ATH10K_SMALLBUFFERS
+ 		.dest_nentries = 512,
++#else
++		.dest_nentries = 128,
++#endif
+ 		.recv_cb = ath10k_pci_htt_rx_cb,
+ 	},
+ 
+@@ -193,7 +205,11 @@ static const struct ce_attr pci_host_ce_config_wlan[] = {
+ 		.flags = CE_ATTR_FLAGS,
+ 		.src_nentries = 0,
+ 		.src_sz_max = 2048,
++#ifndef CONFIG_ATH10K_SMALLBUFFERS
+ 		.dest_nentries = 128,
++#else
++		.dest_nentries = 96,
++#endif
+ 		.recv_cb = ath10k_pci_pktlog_rx_cb,
+ 	},
+ 
+diff --git a/drivers/net/wireless/ath/ath10k/thermal.h b/drivers/net/wireless/ath/ath10k/thermal.h
+index 1f4de9f..fcfa3c2 100644
+--- a/drivers/net/wireless/ath/ath10k/thermal.h
++++ b/drivers/net/wireless/ath/ath10k/thermal.h
+@@ -25,7 +25,7 @@ struct ath10k_thermal {
+ 	int temperature;
+ };
+ 
+-#if IS_REACHABLE(CONFIG_THERMAL)
++#if IS_REACHABLE(CPTCFG_ATH10K_THERMAL)
+ int ath10k_thermal_register(struct ath10k *ar);
+ void ath10k_thermal_unregister(struct ath10k *ar);
+ void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature);
+diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
+index 6b428b4..6a54ba7 100644
+--- a/drivers/net/wireless/ath/ath10k/wmi.c
++++ b/drivers/net/wireless/ath/ath10k/wmi.c
+@@ -7536,6 +7536,49 @@ static struct sk_buff *ath10k_wmi_op_gen_gpio_output(struct ath10k *ar,
+ 	return skb;
+ }
+ 
++static struct sk_buff *ath10k_wmi_op_gen_gpio_config(struct ath10k *ar,
++						     u32 gpio_num, u32 input,
++						     u32 pull_type, u32 intr_mode)
++{
++	struct wmi_gpio_config_cmd *cmd;
++	struct sk_buff *skb;
++
++	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
++	if (!skb)
++		return ERR_PTR(-ENOMEM);
++
++	cmd = (struct wmi_gpio_config_cmd *)skb->data;
++	cmd->pull_type = __cpu_to_le32(pull_type);
++	cmd->gpio_num = __cpu_to_le32(gpio_num);
++	cmd->input = __cpu_to_le32(input);
++	cmd->intr_mode = __cpu_to_le32(intr_mode);
++
++	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi gpio_config gpio_num 0x%08x input 0x%08x pull_type 0x%08x intr_mode 0x%08x\n",
++		   gpio_num, input, pull_type, intr_mode);
++
++	return skb;
++}
++
++static struct sk_buff *ath10k_wmi_op_gen_gpio_output(struct ath10k *ar,
++						     u32 gpio_num, u32 set)
++{
++	struct wmi_gpio_output_cmd *cmd;
++	struct sk_buff *skb;
++
++	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
++	if (!skb)
++		return ERR_PTR(-ENOMEM);
++
++	cmd = (struct wmi_gpio_output_cmd *)skb->data;
++	cmd->gpio_num = __cpu_to_le32(gpio_num);
++	cmd->set = __cpu_to_le32(set);
++
++	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi gpio_output gpio_num 0x%08x set 0x%08x\n",
++		   gpio_num, set);
++
++	return skb;
++}
++
+ static struct sk_buff *
+ ath10k_wmi_op_gen_set_psmode(struct ath10k *ar, u32 vdev_id,
+ 			     enum wmi_sta_ps_mode psmode)
+diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
+index 0faefc0..06a7b3a 100644
+--- a/drivers/net/wireless/ath/ath10k/wmi.h
++++ b/drivers/net/wireless/ath/ath10k/wmi.h
+@@ -3069,6 +3069,41 @@ struct wmi_gpio_input_event {
+ 	__le32 gpio_num;    /* GPIO number which changed state */
+ } __packed;
+ 
++/* WMI_GPIO_CPTCFG_CMDID */
++enum {
++	WMI_GPIO_PULL_NONE,
++	WMI_GPIO_PULL_UP,
++	WMI_GPIO_PULL_DOWN,
++};
++
++enum {
++	WMI_GPIO_INTTYPE_DISABLE,
++	WMI_GPIO_INTTYPE_RISING_EDGE,
++	WMI_GPIO_INTTYPE_FALLING_EDGE,
++	WMI_GPIO_INTTYPE_BOTH_EDGE,
++	WMI_GPIO_INTTYPE_LEVEL_LOW,
++	WMI_GPIO_INTTYPE_LEVEL_HIGH
++};
++
++/* WMI_GPIO_CPTCFG_CMDID */
++struct wmi_gpio_config_cmd {
++	__le32 gpio_num;             /* GPIO number to be setup */
++	__le32 input;                /* 0 - Output/ 1 - Input */
++	__le32 pull_type;            /* Pull type defined above */
++	__le32 intr_mode;            /* Interrupt mode defined above (Input) */
++} __packed;
++
++/* WMI_GPIO_OUTPUT_CMDID */
++struct wmi_gpio_output_cmd {
++	__le32 gpio_num;    /* GPIO number to be setup */
++	__le32 set;         /* Set the GPIO pin*/
++} __packed;
++
++/* WMI_GPIO_INPUT_EVENTID */
++struct wmi_gpio_input_event {
++	__le32 gpio_num;    /* GPIO number which changed state */
++} __packed;
++
+ struct wmi_ext_resource_config_10_4_cmd {
+ 	/* contains enum wmi_host_platform_type */
+ 	__le32 host_platform_config;
+diff --git a/local-symbols b/local-symbols
+index 3b2db43..c8f7132 100644
+--- a/local-symbols
++++ b/local-symbols
+@@ -154,6 +154,8 @@ ATH10K_DEBUG=
+ ATH10K_DEBUGFS=
+ ATH10K_LEDS=
+ ATH10K_SPECTRAL=
++ATH10K_THERMAL=
++ATH10K_LEDS=
+ ATH10K_TRACING=
+ ATH10K_DFS_CERTIFIED=
+ WCN36XX=
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0005-sync-backports-patches-ath10k.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0005-sync-backports-patches-ath10k.patch
deleted file mode 100644
index feebf11..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0005-sync-backports-patches-ath10k.patch
+++ /dev/null
@@ -1,835 +0,0 @@
-From 38cce019a8791ff3d295bd936362432cc592e061 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Tue, 12 Mar 2024 11:27:11 +0800
-Subject: [PATCH 05/61] sync backports patches/ath10k
-
----
- drivers/net/wireless/ath/ath10k/Kconfig   |  16 ++++
- drivers/net/wireless/ath/ath10k/Makefile  |   3 +-
- drivers/net/wireless/ath/ath10k/core.c    |  34 ++++++++
- drivers/net/wireless/ath/ath10k/core.h    |  12 +++
- drivers/net/wireless/ath/ath10k/htt.h     |   4 +
- drivers/net/wireless/ath/ath10k/hw.h      |   1 +
- drivers/net/wireless/ath/ath10k/leds.c    | 101 ++++++++++++++++++++++
- drivers/net/wireless/ath/ath10k/leds.h    |  41 +++++++++
- drivers/net/wireless/ath/ath10k/mac.c     |  66 +++++++++++++-
- drivers/net/wireless/ath/ath10k/pci.c     |  16 ++++
- drivers/net/wireless/ath/ath10k/thermal.h |   2 +-
- drivers/net/wireless/ath/ath10k/wmi-ops.h |  32 +++++++
- drivers/net/wireless/ath/ath10k/wmi-tlv.c |   2 +
- drivers/net/wireless/ath/ath10k/wmi.c     |  54 ++++++++++++
- drivers/net/wireless/ath/ath10k/wmi.h     |  35 ++++++++
- local-symbols                             |   2 +
- 16 files changed, 415 insertions(+), 6 deletions(-)
- create mode 100644 drivers/net/wireless/ath/ath10k/leds.c
- create mode 100644 drivers/net/wireless/ath/ath10k/leds.h
-
-diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig
-index d10c65d..5cabe0e 100644
---- a/drivers/net/wireless/ath/ath10k/Kconfig
-+++ b/drivers/net/wireless/ath/ath10k/Kconfig
-@@ -72,6 +72,16 @@ config ATH10K_DEBUGFS
- 
- 	  If unsure, say Y to make it easier to debug problems.
- 
-+config ATH10K_LEDS
-+	bool "Atheros ath10k LED support"
-+	depends on ATH10K
-+	select MAC80211_LEDS
-+	select LEDS_CLASS
-+	select NEW_LEDS
-+	default y
-+	---help---
-+	  This option is necessary, if you want LED support for chipset connected led pins. If unsure, say N.
-+
- config ATH10K_SPECTRAL
- 	bool "Atheros ath10k spectral scan support"
- 	depends on ATH10K_DEBUGFS
-@@ -87,6 +97,12 @@ config ATH10K_TRACING
- 	help
- 	  Select this to ath10k use tracing infrastructure.
- 
-+config ATH10K_THERMAL
-+	bool "Atheros ath10k thermal monitoring support"
-+	depends on THERMAL
-+	---help---
-+	  Select this to ath10k use hwmon for thermal measurement.
-+
- config ATH10K_DFS_CERTIFIED
- 	bool "Atheros DFS support for certified platforms"
- 	depends on ATH10K && CFG80211_CERTIFICATION_ONUS
-diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile
-index 24d846a..e040d84 100644
---- a/drivers/net/wireless/ath/ath10k/Makefile
-+++ b/drivers/net/wireless/ath/ath10k/Makefile
-@@ -18,7 +18,8 @@ ath10k_core-y += mac.o \
- ath10k_core-$(CPTCFG_ATH10K_SPECTRAL) += spectral.o
- ath10k_core-$(CPTCFG_NL80211_TESTMODE) += testmode.o
- ath10k_core-$(CPTCFG_ATH10K_TRACING) += trace.o
--ath10k_core-$(CONFIG_THERMAL) += thermal.o
-+ath10k_core-$(CPTCFG_ATH10K_THERMAL) += thermal.o
-+ath10k_core-$(CPTCFG_ATH10K_LEDS) += leds.o
- ath10k_core-$(CPTCFG_MAC80211_DEBUGFS) += debugfs_sta.o
- ath10k_core-$(CONFIG_PM) += wow.o
- ath10k_core-$(CONFIG_DEV_COREDUMP) += coredump.o
-diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
-index 9ce6f49..a168fdf 100644
---- a/drivers/net/wireless/ath/ath10k/core.c
-+++ b/drivers/net/wireless/ath/ath10k/core.c
-@@ -9,6 +9,7 @@
- #include <linux/module.h>
- #include <linux/firmware.h>
- #include <linux/of.h>
-+#include <linux/of_net.h>
- #include <linux/property.h>
- #include <linux/dmi.h>
- #include <linux/ctype.h>
-@@ -27,6 +28,7 @@
- #include "testmode.h"
- #include "wmi-ops.h"
- #include "coredump.h"
-+#include "leds.h"
- 
- unsigned int ath10k_debug_mask;
- EXPORT_SYMBOL(ath10k_debug_mask);
-@@ -66,6 +68,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
- 		.dev_id = QCA988X_2_0_DEVICE_ID,
- 		.bus = ATH10K_BUS_PCI,
- 		.name = "qca988x hw2.0",
-+		.led_pin = 1,
- 		.patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR,
- 		.uart_pin = 7,
- 		.cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_ALL,
-@@ -149,6 +152,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
- 		.dev_id = QCA9887_1_0_DEVICE_ID,
- 		.bus = ATH10K_BUS_PCI,
- 		.name = "qca9887 hw1.0",
-+		.led_pin = 1,
- 		.patch_load_addr = QCA9887_HW_1_0_PATCH_LOAD_ADDR,
- 		.uart_pin = 7,
- 		.cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_ALL,
-@@ -396,6 +400,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
- 		.dev_id = QCA99X0_2_0_DEVICE_ID,
- 		.bus = ATH10K_BUS_PCI,
- 		.name = "qca99x0 hw2.0",
-+		.led_pin = 17,
- 		.patch_load_addr = QCA99X0_HW_2_0_PATCH_LOAD_ADDR,
- 		.uart_pin = 7,
- 		.otp_exe_param = 0x00000700,
-@@ -443,6 +448,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
- 		.dev_id = QCA9984_1_0_DEVICE_ID,
- 		.bus = ATH10K_BUS_PCI,
- 		.name = "qca9984/qca9994 hw1.0",
-+		.led_pin = 17,
- 		.patch_load_addr = QCA9984_HW_1_0_PATCH_LOAD_ADDR,
- 		.uart_pin = 7,
- 		.cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_EACH,
-@@ -497,6 +503,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
- 		.dev_id = QCA9888_2_0_DEVICE_ID,
- 		.bus = ATH10K_BUS_PCI,
- 		.name = "qca9888 hw2.0",
-+		.led_pin = 17,
- 		.patch_load_addr = QCA9888_HW_2_0_PATCH_LOAD_ADDR,
- 		.uart_pin = 7,
- 		.cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_EACH,
-@@ -3239,6 +3246,10 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode,
- 		goto err_hif_stop;
- 	}
- 
-+	status = ath10k_leds_start(ar);
-+	if (status)
-+		goto err_hif_stop;
-+
- 	return 0;
- 
- err_hif_stop:
-@@ -3405,6 +3416,8 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
- 
- 	device_get_mac_address(ar->dev, ar->mac_addr);
- 
-+	of_get_mac_address(ar->dev->of_node, ar->mac_addr);
-+
- 	ret = ath10k_core_init_firmware_features(ar);
- 	if (ret) {
- 		ath10k_err(ar, "fatal problem with firmware features: %d\n",
-@@ -3497,9 +3510,18 @@ static void ath10k_core_register_work(struct work_struct *work)
- 		goto err_spectral_destroy;
- 	}
- 
-+	status = ath10k_leds_register(ar);
-+	if (status) {
-+		ath10k_err(ar, "could not register leds: %d\n",
-+			   status);
-+		goto err_thermal_unregister;
-+	}
-+
- 	set_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags);
- 	return;
- 
-+err_thermal_unregister:
-+	ath10k_thermal_unregister(ar);
- err_spectral_destroy:
- 	ath10k_spectral_destroy(ar);
- err_debug_destroy:
-@@ -3524,6 +3546,16 @@ int ath10k_core_register(struct ath10k *ar,
- 
- 	queue_work(ar->workqueue, &ar->register_work);
- 
-+	/* OpenWrt requires all PHYs to be initialized to create the
-+	 * configuration files during bootup. ath10k violates this
-+	 * because it delays the creation of the PHY to a not well defined
-+	 * point in the future.
-+	 *
-+	 * Forcing the work to be done immediately works around this problem
-+	 * but may also delay the boot when firmware images cannot be found.
-+	 */
-+	flush_workqueue(ar->workqueue);
-+
- 	return 0;
- }
- EXPORT_SYMBOL(ath10k_core_register);
-@@ -3535,6 +3567,8 @@ void ath10k_core_unregister(struct ath10k *ar)
- 	if (!test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags))
- 		return;
- 
-+	ath10k_leds_unregister(ar);
-+
- 	ath10k_thermal_unregister(ar);
- 	/* Stop spectral before unregistering from mac80211 to remove the
- 	 * relayfs debugfs file cleanly. Otherwise the parent debugfs tree
-diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
-index 2435291..8546f2f 100644
---- a/drivers/net/wireless/ath/ath10k/core.h
-+++ b/drivers/net/wireless/ath/ath10k/core.h
-@@ -15,6 +15,7 @@
- #include <linux/pci.h>
- #include <linux/uuid.h>
- #include <linux/time.h>
-+#include <linux/leds.h>
- 
- #include "htt.h"
- #include "htc.h"
-@@ -1256,6 +1257,13 @@ struct ath10k {
- 		bool utf_monitor;
- 	} testmode;
- 
-+	struct {
-+		struct gpio_led wifi_led;
-+		struct led_classdev cdev;
-+		char label[48];
-+		u32 gpio_state_pin;
-+	} leds;
-+
- 	struct {
- 		/* protected by data_lock */
- 		u32 rx_crc_err_drop;
-@@ -1305,6 +1313,10 @@ struct ath10k {
- 	s32 tx_power_2g_limit;
- 	s32 tx_power_5g_limit;
- 
-+#ifdef CPTCFG_MAC80211_LEDS
-+	const char *led_default_trigger;
-+#endif
-+
- 	/* must be last */
- 	u8 drv_priv[] __aligned(sizeof(void *));
- };
-diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
-index 603f6de..bea1d16 100644
---- a/drivers/net/wireless/ath/ath10k/htt.h
-+++ b/drivers/net/wireless/ath/ath10k/htt.h
-@@ -236,7 +236,11 @@ enum htt_rx_ring_flags {
- };
- 
- #define HTT_RX_RING_SIZE_MIN 128
-+#ifndef CONFIG_ATH10K_SMALLBUFFERS
- #define HTT_RX_RING_SIZE_MAX 2048
-+#else
-+#define HTT_RX_RING_SIZE_MAX 512
-+#endif
- #define HTT_RX_RING_SIZE HTT_RX_RING_SIZE_MAX
- #define HTT_RX_RING_FILL_LEVEL (((HTT_RX_RING_SIZE) / 2) - 1)
- #define HTT_RX_RING_FILL_LEVEL_DUAL_MAC (HTT_RX_RING_SIZE - 1)
-diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
-index 3b26994..aeada6c 100644
---- a/drivers/net/wireless/ath/ath10k/hw.h
-+++ b/drivers/net/wireless/ath/ath10k/hw.h
-@@ -520,6 +520,7 @@ struct ath10k_hw_params {
- 	const char *name;
- 	u32 patch_load_addr;
- 	int uart_pin;
-+	int led_pin;
- 	u32 otp_exe_param;
- 
- 	/* Type of hw cycle counter wraparound logic, for more info
-diff --git a/drivers/net/wireless/ath/ath10k/leds.c b/drivers/net/wireless/ath/ath10k/leds.c
-new file mode 100644
-index 0000000..be8f255
---- /dev/null
-+++ b/drivers/net/wireless/ath/ath10k/leds.c
-@@ -0,0 +1,101 @@
-+/*
-+ * Copyright (c) 2005-2011 Atheros Communications Inc.
-+ * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
-+ * Copyright (c) 2018 Sebastian Gottschall <s.gottschall@dd-wrt.com>
-+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
-+ *
-+ * Permission to use, copy, modify, and/or distribute this software for any
-+ * purpose with or without fee is hereby granted, provided that the above
-+ * copyright notice and this permission notice appear in all copies.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-+ */
-+
-+#include <linux/leds.h>
-+
-+#include "core.h"
-+#include "wmi.h"
-+#include "wmi-ops.h"
-+
-+#include "leds.h"
-+
-+static int ath10k_leds_set_brightness_blocking(struct led_classdev *led_cdev,
-+					       enum led_brightness brightness)
-+{
-+	struct ath10k *ar = container_of(led_cdev, struct ath10k,
-+					 leds.cdev);
-+	struct gpio_led *led = &ar->leds.wifi_led;
-+
-+	mutex_lock(&ar->conf_mutex);
-+
-+	if (ar->state != ATH10K_STATE_ON)
-+		goto out;
-+
-+	ar->leds.gpio_state_pin = (brightness != LED_OFF) ^ led->active_low;
-+	ath10k_wmi_gpio_output(ar, led->gpio, ar->leds.gpio_state_pin);
-+
-+out:
-+	mutex_unlock(&ar->conf_mutex);
-+
-+	return 0;
-+}
-+
-+int ath10k_leds_start(struct ath10k *ar)
-+{
-+	if (ar->hw_params.led_pin == 0)
-+		/* leds not supported */
-+		return 0;
-+
-+	/* under some circumstances, the gpio pin gets reconfigured
-+	 * to default state by the firmware, so we need to
-+	 * reconfigure it this behaviour has only ben seen on
-+	 * QCA9984 and QCA99XX devices so far
-+	 */
-+	ath10k_wmi_gpio_config(ar, ar->hw_params.led_pin, 0,
-+			       WMI_GPIO_PULL_NONE, WMI_GPIO_INTTYPE_DISABLE);
-+	ath10k_wmi_gpio_output(ar, ar->hw_params.led_pin, 1);
-+
-+	return 0;
-+}
-+
-+int ath10k_leds_register(struct ath10k *ar)
-+{
-+	int ret;
-+
-+	if (ar->hw_params.led_pin == 0)
-+		/* leds not supported */
-+		return 0;
-+
-+	snprintf(ar->leds.label, sizeof(ar->leds.label), "ath10k-%s",
-+		 wiphy_name(ar->hw->wiphy));
-+	ar->leds.wifi_led.active_low = 1;
-+	ar->leds.wifi_led.gpio = ar->hw_params.led_pin;
-+	ar->leds.wifi_led.name = ar->leds.label;
-+	ar->leds.wifi_led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
-+
-+	ar->leds.cdev.name = ar->leds.label;
-+	ar->leds.cdev.brightness_set_blocking = ath10k_leds_set_brightness_blocking;
-+	ar->leds.cdev.default_trigger = ar->led_default_trigger;
-+
-+	ret = led_classdev_register(wiphy_dev(ar->hw->wiphy), &ar->leds.cdev);
-+	if (ret)
-+		return ret;
-+
-+	return 0;
-+}
-+
-+void ath10k_leds_unregister(struct ath10k *ar)
-+{
-+	if (ar->hw_params.led_pin == 0)
-+		/* leds not supported */
-+		return;
-+
-+	led_classdev_unregister(&ar->leds.cdev);
-+}
-+
-diff --git a/drivers/net/wireless/ath/ath10k/leds.h b/drivers/net/wireless/ath/ath10k/leds.h
-new file mode 100644
-index 0000000..a0f5c84
---- /dev/null
-+++ b/drivers/net/wireless/ath/ath10k/leds.h
-@@ -0,0 +1,41 @@
-+/*
-+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
-+ *
-+ * Permission to use, copy, modify, and/or distribute this software for any
-+ * purpose with or without fee is hereby granted, provided that the above
-+ * copyright notice and this permission notice appear in all copies.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-+ */
-+#ifndef _LEDS_H_
-+#define _LEDS_H_
-+
-+#include "core.h"
-+
-+#ifdef CPTCFG_ATH10K_LEDS
-+void ath10k_leds_unregister(struct ath10k *ar);
-+int ath10k_leds_start(struct ath10k *ar);
-+int ath10k_leds_register(struct ath10k *ar);
-+#else
-+static inline void ath10k_leds_unregister(struct ath10k *ar)
-+{
-+}
-+
-+static inline int ath10k_leds_start(struct ath10k *ar)
-+{
-+	return 0;
-+}
-+
-+static inline int ath10k_leds_register(struct ath10k *ar)
-+{
-+	return 0;
-+}
-+
-+#endif
-+#endif /* _LEDS_H_ */
-diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
-index ec43216..f179dff 100644
---- a/drivers/net/wireless/ath/ath10k/mac.c
-+++ b/drivers/net/wireless/ath/ath10k/mac.c
-@@ -25,6 +25,7 @@
- #include "wmi-tlv.h"
- #include "wmi-ops.h"
- #include "wow.h"
-+#include "leds.h"
- 
- /*********/
- /* Rates */
-@@ -1021,6 +1022,40 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
- 	return ar->last_wmi_vdev_start_status;
- }
- 
-+static u32 ath10k_get_max_antenna_gain(struct ath10k *ar,
-+				       u32 ch_max_antenna_gain)
-+{
-+	u32 max_antenna_gain;
-+
-+	if (ar->dfs_detector && ar->dfs_detector->region == NL80211_DFS_FCC) {
-+		/* FCC allows maximum antenna gain of 6 dBi. 15.247(b)(4):
-+		 *
-+		 * > (4) The conducted output power limit
-+		 * > specified in paragraph (b) of this section
-+		 * > is based on the use of antennas
-+		 * > with directional gains that do not exceed
-+		 * > 6 dBi. Except as shown in paragraph
-+		 * > (c) of this section, if transmitting
-+		 * > antennas of directional gain greater
-+		 * > than 6 dBi are used, the conducted
-+		 * > output power from the intentional radiator
-+		 * > shall be reduced below the stated
-+		 * > values in paragraphs (b)(1), (b)(2),
-+		 * > and (b)(3) of this section, as appropriate,
-+		 * > by the amount in dB that the
-+		 * > directional gain of the antenna exceeds
-+		 * > 6 dBi.
-+		 *
-+		 * https://www.gpo.gov/fdsys/pkg/CFR-2013-title47-vol1/pdf/CFR-2013-title47-vol1-sec15-247.pdf
-+		 */
-+		max_antenna_gain = 6;
-+	} else {
-+		max_antenna_gain = 0;
-+	}
-+
-+	return max(ch_max_antenna_gain, max_antenna_gain);
-+}
-+
- static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
- {
- 	struct cfg80211_chan_def *chandef = NULL;
-@@ -1053,7 +1088,8 @@ static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
- 	arg.channel.min_power = 0;
- 	arg.channel.max_power = channel->max_power * 2;
- 	arg.channel.max_reg_power = channel->max_reg_power * 2;
--	arg.channel.max_antenna_gain = channel->max_antenna_gain;
-+	arg.channel.max_antenna_gain = ath10k_get_max_antenna_gain(ar,
-+						channel->max_antenna_gain);
- 
- 	reinit_completion(&ar->vdev_setup_done);
- 	reinit_completion(&ar->vdev_delete_done);
-@@ -1499,7 +1535,8 @@ static int ath10k_vdev_start_restart(struct ath10k_vif *arvif,
- 	arg.channel.min_power = 0;
- 	arg.channel.max_power = chandef->chan->max_power * 2;
- 	arg.channel.max_reg_power = chandef->chan->max_reg_power * 2;
--	arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain;
-+	arg.channel.max_antenna_gain = ath10k_get_max_antenna_gain(ar,
-+					chandef->chan->max_antenna_gain);
- 
- 	if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
- 		arg.ssid = arvif->u.ap.ssid;
-@@ -3430,7 +3467,8 @@ static int ath10k_update_channel_list(struct ath10k *ar)
- 			ch->min_power = 0;
- 			ch->max_power = channel->max_power * 2;
- 			ch->max_reg_power = channel->max_reg_power * 2;
--			ch->max_antenna_gain = channel->max_antenna_gain;
-+			ch->max_antenna_gain = ath10k_get_max_antenna_gain(ar,
-+						channel->max_antenna_gain);
- 			ch->reg_class_id = 0; /* FIXME */
- 
- 			/* FIXME: why use only legacy modes, why not any
-@@ -9918,6 +9956,21 @@ static int ath10k_mac_init_rd(struct ath10k *ar)
- 	return 0;
- }
- 
-+#ifdef CPTCFG_MAC80211_LEDS
-+static const struct ieee80211_tpt_blink ath10k_tpt_blink[] = {
-+	{ .throughput = 0 * 1024, .blink_time = 334 },
-+	{ .throughput = 1 * 1024, .blink_time = 260 },
-+	{ .throughput = 2 * 1024, .blink_time = 220 },
-+	{ .throughput = 5 * 1024, .blink_time = 190 },
-+	{ .throughput = 10 * 1024, .blink_time = 170 },
-+	{ .throughput = 25 * 1024, .blink_time = 150 },
-+	{ .throughput = 54 * 1024, .blink_time = 130 },
-+	{ .throughput = 120 * 1024, .blink_time = 110 },
-+	{ .throughput = 265 * 1024, .blink_time = 80 },
-+	{ .throughput = 586 * 1024, .blink_time = 50 },
-+};
-+#endif
-+
- int ath10k_mac_register(struct ath10k *ar)
- {
- 	static const u32 cipher_suites[] = {
-@@ -10036,7 +10089,6 @@ int ath10k_mac_register(struct ath10k *ar)
- 	ieee80211_hw_set(ar->hw, CHANCTX_STA_CSA);
- 	ieee80211_hw_set(ar->hw, QUEUE_CONTROL);
- 	ieee80211_hw_set(ar->hw, SUPPORTS_TX_FRAG);
--	ieee80211_hw_set(ar->hw, REPORTS_LOW_ACK);
- 
- 	if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags))
- 		ieee80211_hw_set(ar->hw, SW_CRYPTO_CONTROL);
-@@ -10280,6 +10332,12 @@ int ath10k_mac_register(struct ath10k *ar)
- 
- 	ar->hw->weight_multiplier = ATH10K_AIRTIME_WEIGHT_MULTIPLIER;
- 
-+#ifdef CPTCFG_MAC80211_LEDS
-+	ar->led_default_trigger = ieee80211_create_tpt_led_trigger(ar->hw,
-+		IEEE80211_TPT_LEDTRIG_FL_RADIO, ath10k_tpt_blink,
-+		ARRAY_SIZE(ath10k_tpt_blink));
-+#endif
-+
- 	ret = ieee80211_register_hw(ar->hw);
- 	if (ret) {
- 		ath10k_err(ar, "failed to register ieee80211: %d\n", ret);
-diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
-index d7ac5ae..c7e8e90 100644
---- a/drivers/net/wireless/ath/ath10k/pci.c
-+++ b/drivers/net/wireless/ath/ath10k/pci.c
-@@ -132,7 +132,11 @@ static const struct ce_attr pci_host_ce_config_wlan[] = {
- 		.flags = CE_ATTR_FLAGS,
- 		.src_nentries = 0,
- 		.src_sz_max = 2048,
-+#ifndef CONFIG_ATH10K_SMALLBUFFERS
- 		.dest_nentries = 512,
-+#else
-+		.dest_nentries = 128,
-+#endif
- 		.recv_cb = ath10k_pci_htt_htc_rx_cb,
- 	},
- 
-@@ -141,7 +145,11 @@ static const struct ce_attr pci_host_ce_config_wlan[] = {
- 		.flags = CE_ATTR_FLAGS,
- 		.src_nentries = 0,
- 		.src_sz_max = 2048,
-+#ifndef CONFIG_ATH10K_SMALLBUFFERS
- 		.dest_nentries = 128,
-+#else
-+		.dest_nentries = 64,
-+#endif
- 		.recv_cb = ath10k_pci_htc_rx_cb,
- 	},
- 
-@@ -168,7 +176,11 @@ static const struct ce_attr pci_host_ce_config_wlan[] = {
- 		.flags = CE_ATTR_FLAGS,
- 		.src_nentries = 0,
- 		.src_sz_max = 512,
-+#ifndef CONFIG_ATH10K_SMALLBUFFERS
- 		.dest_nentries = 512,
-+#else
-+		.dest_nentries = 128,
-+#endif
- 		.recv_cb = ath10k_pci_htt_rx_cb,
- 	},
- 
-@@ -193,7 +205,11 @@ static const struct ce_attr pci_host_ce_config_wlan[] = {
- 		.flags = CE_ATTR_FLAGS,
- 		.src_nentries = 0,
- 		.src_sz_max = 2048,
-+#ifndef CONFIG_ATH10K_SMALLBUFFERS
- 		.dest_nentries = 128,
-+#else
-+		.dest_nentries = 96,
-+#endif
- 		.recv_cb = ath10k_pci_pktlog_rx_cb,
- 	},
- 
-diff --git a/drivers/net/wireless/ath/ath10k/thermal.h b/drivers/net/wireless/ath/ath10k/thermal.h
-index 1f4de9f..fcfa3c2 100644
---- a/drivers/net/wireless/ath/ath10k/thermal.h
-+++ b/drivers/net/wireless/ath/ath10k/thermal.h
-@@ -25,7 +25,7 @@ struct ath10k_thermal {
- 	int temperature;
- };
- 
--#if IS_REACHABLE(CONFIG_THERMAL)
-+#if IS_REACHABLE(CPTCFG_ATH10K_THERMAL)
- int ath10k_thermal_register(struct ath10k *ar);
- void ath10k_thermal_unregister(struct ath10k *ar);
- void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature);
-diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h
-index aa57d80..f3f6b59 100644
---- a/drivers/net/wireless/ath/ath10k/wmi-ops.h
-+++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h
-@@ -226,7 +226,10 @@ struct wmi_ops {
- 			 const struct wmi_bb_timing_cfg_arg *arg);
- 	struct sk_buff *(*gen_per_peer_per_tid_cfg)(struct ath10k *ar,
- 						    const struct wmi_per_peer_per_tid_cfg_arg *arg);
-+	struct sk_buff *(*gen_gpio_config)(struct ath10k *ar, u32 gpio_num,
-+					   u32 input, u32 pull_type, u32 intr_mode);
- 
-+	struct sk_buff *(*gen_gpio_output)(struct ath10k *ar, u32 gpio_num, u32 set);
- };
- 
- int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id);
-@@ -1122,6 +1125,35 @@ ath10k_wmi_force_fw_hang(struct ath10k *ar,
- 	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid);
- }
- 
-+static inline int ath10k_wmi_gpio_config(struct ath10k *ar, u32 gpio_num,
-+					 u32 input, u32 pull_type, u32 intr_mode)
-+{
-+	struct sk_buff *skb;
-+
-+	if (!ar->wmi.ops->gen_gpio_config)
-+		return -EOPNOTSUPP;
-+
-+	skb = ar->wmi.ops->gen_gpio_config(ar, gpio_num, input, pull_type, intr_mode);
-+	if (IS_ERR(skb))
-+		return PTR_ERR(skb);
-+
-+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->gpio_config_cmdid);
-+}
-+
-+static inline int ath10k_wmi_gpio_output(struct ath10k *ar, u32 gpio_num, u32 set)
-+{
-+	struct sk_buff *skb;
-+
-+	if (!ar->wmi.ops->gen_gpio_config)
-+		return -EOPNOTSUPP;
-+
-+	skb = ar->wmi.ops->gen_gpio_output(ar, gpio_num, set);
-+	if (IS_ERR(skb))
-+		return PTR_ERR(skb);
-+
-+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->gpio_output_cmdid);
-+}
-+
- static inline int
- ath10k_wmi_dbglog_cfg(struct ath10k *ar, u64 module_enable, u32 log_level)
- {
-diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
-index aed97fd..dbaf26d 100644
---- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c
-+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
-@@ -4606,6 +4606,8 @@ static const struct wmi_ops wmi_tlv_ops = {
- 	.gen_echo = ath10k_wmi_tlv_op_gen_echo,
- 	.gen_vdev_spectral_conf = ath10k_wmi_tlv_op_gen_vdev_spectral_conf,
- 	.gen_vdev_spectral_enable = ath10k_wmi_tlv_op_gen_vdev_spectral_enable,
-+	/* .gen_gpio_config not implemented */
-+	/* .gen_gpio_output not implemented */
- };
- 
- static const struct wmi_peer_flags_map wmi_tlv_peer_flags_map = {
-diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
-index 5d80988..6b428b4 100644
---- a/drivers/net/wireless/ath/ath10k/wmi.c
-+++ b/drivers/net/wireless/ath/ath10k/wmi.c
-@@ -7493,6 +7493,49 @@ ath10k_wmi_op_gen_peer_set_param(struct ath10k *ar, u32 vdev_id,
- 	return skb;
- }
- 
-+static struct sk_buff *ath10k_wmi_op_gen_gpio_config(struct ath10k *ar,
-+						     u32 gpio_num, u32 input,
-+						     u32 pull_type, u32 intr_mode)
-+{
-+	struct wmi_gpio_config_cmd *cmd;
-+	struct sk_buff *skb;
-+
-+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
-+	if (!skb)
-+		return ERR_PTR(-ENOMEM);
-+
-+	cmd = (struct wmi_gpio_config_cmd *)skb->data;
-+	cmd->pull_type = __cpu_to_le32(pull_type);
-+	cmd->gpio_num = __cpu_to_le32(gpio_num);
-+	cmd->input = __cpu_to_le32(input);
-+	cmd->intr_mode = __cpu_to_le32(intr_mode);
-+
-+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi gpio_config gpio_num 0x%08x input 0x%08x pull_type 0x%08x intr_mode 0x%08x\n",
-+		   gpio_num, input, pull_type, intr_mode);
-+
-+	return skb;
-+}
-+
-+static struct sk_buff *ath10k_wmi_op_gen_gpio_output(struct ath10k *ar,
-+						     u32 gpio_num, u32 set)
-+{
-+	struct wmi_gpio_output_cmd *cmd;
-+	struct sk_buff *skb;
-+
-+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
-+	if (!skb)
-+		return ERR_PTR(-ENOMEM);
-+
-+	cmd = (struct wmi_gpio_output_cmd *)skb->data;
-+	cmd->gpio_num = __cpu_to_le32(gpio_num);
-+	cmd->set = __cpu_to_le32(set);
-+
-+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi gpio_output gpio_num 0x%08x set 0x%08x\n",
-+		   gpio_num, set);
-+
-+	return skb;
-+}
-+
- static struct sk_buff *
- ath10k_wmi_op_gen_set_psmode(struct ath10k *ar, u32 vdev_id,
- 			     enum wmi_sta_ps_mode psmode)
-@@ -9157,6 +9200,9 @@ static const struct wmi_ops wmi_ops = {
- 	.fw_stats_fill = ath10k_wmi_main_op_fw_stats_fill,
- 	.get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype,
- 	.gen_echo = ath10k_wmi_op_gen_echo,
-+	.gen_gpio_config = ath10k_wmi_op_gen_gpio_config,
-+	.gen_gpio_output = ath10k_wmi_op_gen_gpio_output,
-+
- 	/* .gen_bcn_tmpl not implemented */
- 	/* .gen_prb_tmpl not implemented */
- 	/* .gen_p2p_go_bcn_ie not implemented */
-@@ -9227,6 +9273,8 @@ static const struct wmi_ops wmi_10_1_ops = {
- 	.fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill,
- 	.get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype,
- 	.gen_echo = ath10k_wmi_op_gen_echo,
-+	.gen_gpio_config = ath10k_wmi_op_gen_gpio_config,
-+	.gen_gpio_output = ath10k_wmi_op_gen_gpio_output,
- 	/* .gen_bcn_tmpl not implemented */
- 	/* .gen_prb_tmpl not implemented */
- 	/* .gen_p2p_go_bcn_ie not implemented */
-@@ -9299,6 +9347,8 @@ static const struct wmi_ops wmi_10_2_ops = {
- 	.gen_delba_send = ath10k_wmi_op_gen_delba_send,
- 	.fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill,
- 	.get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype,
-+	.gen_gpio_config = ath10k_wmi_op_gen_gpio_config,
-+	.gen_gpio_output = ath10k_wmi_op_gen_gpio_output,
- 	/* .gen_pdev_enable_adaptive_cca not implemented */
- };
- 
-@@ -9370,6 +9420,8 @@ static const struct wmi_ops wmi_10_2_4_ops = {
- 		ath10k_wmi_op_gen_pdev_enable_adaptive_cca,
- 	.get_vdev_subtype = ath10k_wmi_10_2_4_op_get_vdev_subtype,
- 	.gen_bb_timing = ath10k_wmi_10_2_4_op_gen_bb_timing,
-+	.gen_gpio_config = ath10k_wmi_op_gen_gpio_config,
-+	.gen_gpio_output = ath10k_wmi_op_gen_gpio_output,
- 	/* .gen_bcn_tmpl not implemented */
- 	/* .gen_prb_tmpl not implemented */
- 	/* .gen_p2p_go_bcn_ie not implemented */
-@@ -9451,6 +9503,8 @@ static const struct wmi_ops wmi_10_4_ops = {
- 	.gen_pdev_bss_chan_info_req = ath10k_wmi_10_2_op_gen_pdev_bss_chan_info,
- 	.gen_echo = ath10k_wmi_op_gen_echo,
- 	.gen_pdev_get_tpc_config = ath10k_wmi_10_2_4_op_gen_pdev_get_tpc_config,
-+	.gen_gpio_config = ath10k_wmi_op_gen_gpio_config,
-+	.gen_gpio_output = ath10k_wmi_op_gen_gpio_output,
- };
- 
- int ath10k_wmi_attach(struct ath10k *ar)
-diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
-index 2379501..0faefc0 100644
---- a/drivers/net/wireless/ath/ath10k/wmi.h
-+++ b/drivers/net/wireless/ath/ath10k/wmi.h
-@@ -3034,6 +3034,41 @@ enum wmi_10_4_feature_mask {
- 
- };
- 
-+/* WMI_GPIO_CONFIG_CMDID */
-+enum {
-+	WMI_GPIO_PULL_NONE,
-+	WMI_GPIO_PULL_UP,
-+	WMI_GPIO_PULL_DOWN,
-+};
-+
-+enum {
-+	WMI_GPIO_INTTYPE_DISABLE,
-+	WMI_GPIO_INTTYPE_RISING_EDGE,
-+	WMI_GPIO_INTTYPE_FALLING_EDGE,
-+	WMI_GPIO_INTTYPE_BOTH_EDGE,
-+	WMI_GPIO_INTTYPE_LEVEL_LOW,
-+	WMI_GPIO_INTTYPE_LEVEL_HIGH
-+};
-+
-+/* WMI_GPIO_CONFIG_CMDID */
-+struct wmi_gpio_config_cmd {
-+	__le32 gpio_num;             /* GPIO number to be setup */
-+	__le32 input;                /* 0 - Output/ 1 - Input */
-+	__le32 pull_type;            /* Pull type defined above */
-+	__le32 intr_mode;            /* Interrupt mode defined above (Input) */
-+} __packed;
-+
-+/* WMI_GPIO_OUTPUT_CMDID */
-+struct wmi_gpio_output_cmd {
-+	__le32 gpio_num;    /* GPIO number to be setup */
-+	__le32 set;         /* Set the GPIO pin*/
-+} __packed;
-+
-+/* WMI_GPIO_INPUT_EVENTID */
-+struct wmi_gpio_input_event {
-+	__le32 gpio_num;    /* GPIO number which changed state */
-+} __packed;
-+
- struct wmi_ext_resource_config_10_4_cmd {
- 	/* contains enum wmi_host_platform_type */
- 	__le32 host_platform_config;
-diff --git a/local-symbols b/local-symbols
-index b5745bf..3d81ba5 100644
---- a/local-symbols
-+++ b/local-symbols
-@@ -160,6 +160,8 @@ ATH10K_SNOC=
- ATH10K_DEBUG=
- ATH10K_DEBUGFS=
- ATH10K_SPECTRAL=
-+ATH10K_THERMAL=
-+ATH10K_LEDS=
- ATH10K_TRACING=
- ATH10K_DFS_CERTIFIED=
- WCN36XX=
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0006-backports-sync-openwrt-patches-rt2x00.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0006-backports-sync-openwrt-patches-rt2x00.patch
new file mode 100644
index 0000000..be2f21e
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0006-backports-sync-openwrt-patches-rt2x00.patch
@@ -0,0 +1,1250 @@
+From c36bd4545de8ae553fde7710a979cd2ddf87ec8c Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 17 Jul 2024 20:42:40 +0800
+Subject: [PATCH 06/89] backports: sync openwrt patches/rt2x00
+
+---
+ drivers/net/wireless/ralink/rt2x00/Kconfig    |  23 +-
+ drivers/net/wireless/ralink/rt2x00/Makefile   |   1 +
+ drivers/net/wireless/ralink/rt2x00/rt2800.h   |   5 +
+ .../net/wireless/ralink/rt2x00/rt2800lib.c    | 373 +++++++++++-------
+ .../net/wireless/ralink/rt2x00/rt2800lib.h    |  24 ++
+ .../net/wireless/ralink/rt2x00/rt2800pci.c    |   7 +
+ .../net/wireless/ralink/rt2x00/rt2800soc.c    |  52 ++-
+ .../net/wireless/ralink/rt2x00/rt2800usb.c    |   7 +
+ drivers/net/wireless/ralink/rt2x00/rt2x00.h   |   9 +
+ .../net/wireless/ralink/rt2x00/rt2x00dev.c    |  34 +-
+ .../net/wireless/ralink/rt2x00/rt2x00eeprom.c | 208 ++++++++++
+ .../net/wireless/ralink/rt2x00/rt2x00leds.c   |   3 +
+ .../net/wireless/ralink/rt2x00/rt2x00soc.c    |  16 +
+ .../net/wireless/ralink/rt2x00/rt2x00soc.h    |   9 +
+ include/linux/rt2x00_platform.h               |  23 ++
+ local-symbols                                 |   1 +
+ 16 files changed, 629 insertions(+), 166 deletions(-)
+ create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00eeprom.c
+ create mode 100644 include/linux/rt2x00_platform.h
+
+diff --git a/drivers/net/wireless/ralink/rt2x00/Kconfig b/drivers/net/wireless/ralink/rt2x00/Kconfig
+index 8f6e3d2..abaa51b 100644
+--- a/drivers/net/wireless/ralink/rt2x00/Kconfig
++++ b/drivers/net/wireless/ralink/rt2x00/Kconfig
+@@ -70,6 +70,7 @@ config RT2800PCI
+ 	select RT2X00_LIB_MMIO
+ 	select RT2X00_LIB_PCI
+ 	select RT2X00_LIB_FIRMWARE
++	select RT2X00_LIB_EEPROM
+ 	select RT2X00_LIB_CRYPTO
+ 	depends on CRC_CCITT
+ 	depends on EEPROM_93CX6
+@@ -211,13 +212,15 @@ endif
+ config RT2800SOC
+ 	tristate "Ralink WiSoC support"
+ 	depends on m
+-	depends on SOC_RT288X || SOC_RT305X || SOC_MT7620
++	depends on SOC_RT288X || SOC_RT305X || SOC_RT3883 || SOC_MT7620
+ 	select RT2X00_LIB_SOC
+ 	select RT2X00_LIB_MMIO
+ 	select RT2X00_LIB_CRYPTO
+ 	select RT2X00_LIB_FIRMWARE
++	select RT2X00_LIB_EEPROM
+ 	select RT2800_LIB
+ 	select RT2800_LIB_MMIO
++	select MTD if SOC_RT288X || SOC_RT305X
+ 	help
+ 	  This adds support for Ralink WiSoC devices.
+ 	  Supported chips: RT2880, RT3050, RT3052, RT3350, RT3352.
+@@ -226,36 +229,37 @@ config RT2800SOC
+ 
+ 
+ config RT2800_LIB
+-	tristate
++	tristate "RT2800 USB/PCI support"
+ 	depends on m
+ 
+ config RT2800_LIB_MMIO
+-	tristate
++	tristate "RT2800 MMIO support"
+ 	depends on m
+ 	select RT2X00_LIB_MMIO
+ 	select RT2800_LIB
+ 
+ config RT2X00_LIB_MMIO
+-	tristate
++	tristate "RT2x00 MMIO support"
+ 	depends on m
+ 
+ config RT2X00_LIB_PCI
+-	tristate
++	tristate "RT2x00 PCI support"
+ 	depends on m
+ 	select RT2X00_LIB
+ 
+ config RT2X00_LIB_SOC
+-	tristate
++	tristate "RT2x00 SoC support"
++	depends on SOC_RT288X || SOC_RT305X || SOC_RT3883 || SOC_MT7620
+ 	depends on m
+ 	select RT2X00_LIB
+ 
+ config RT2X00_LIB_USB
+-	tristate
++	tristate "RT2x00 USB support"
+ 	depends on m
+ 	select RT2X00_LIB
+ 
+ config RT2X00_LIB
+-	tristate
++	tristate "RT2x00 support"
+ 	depends on m
+ 
+ config RT2X00_LIB_FIRMWARE
+@@ -265,6 +269,9 @@ config RT2X00_LIB_FIRMWARE
+ config RT2X00_LIB_CRYPTO
+ 	bool
+ 
++config RT2X00_LIB_EEPROM
++	bool
++
+ config RT2X00_LIB_LEDS
+ 	bool
+ 	default y if (RT2X00_LIB=y && LEDS_CLASS=y) || (RT2X00_LIB=m && LEDS_CLASS!=n)
+diff --git a/drivers/net/wireless/ralink/rt2x00/Makefile b/drivers/net/wireless/ralink/rt2x00/Makefile
+index 4a2156b..94335ec 100644
+--- a/drivers/net/wireless/ralink/rt2x00/Makefile
++++ b/drivers/net/wireless/ralink/rt2x00/Makefile
+@@ -8,6 +8,7 @@ rt2x00lib-$(CPTCFG_RT2X00_LIB_DEBUGFS)	+= rt2x00debug.o
+ rt2x00lib-$(CPTCFG_RT2X00_LIB_CRYPTO)	+= rt2x00crypto.o
+ rt2x00lib-$(CPTCFG_RT2X00_LIB_FIRMWARE)	+= rt2x00firmware.o
+ rt2x00lib-$(CPTCFG_RT2X00_LIB_LEDS)	+= rt2x00leds.o
++rt2x00lib-$(CPTCFG_RT2X00_LIB_EEPROM)	+= rt2x00eeprom.o
+ 
+ obj-$(CPTCFG_RT2X00_LIB)		+= rt2x00lib.o
+ obj-$(CPTCFG_RT2X00_LIB_MMIO)		+= rt2x00mmio.o
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800.h b/drivers/net/wireless/ralink/rt2x00/rt2800.h
+index 8930589..cbfa680 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800.h
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h
+@@ -1056,6 +1056,11 @@
+ #define MIMO_PS_CFG_RX_STBY_POL		FIELD32(0x00000010)
+ #define MIMO_PS_CFG_RX_RX_STBY0		FIELD32(0x00000020)
+ 
++#define BB_PA_MODE_CFG0			0x1214
++#define BB_PA_MODE_CFG1			0x1218
++#define RF_PA_MODE_CFG0			0x121C
++#define RF_PA_MODE_CFG1			0x1220
++
+ /*
+  * EDCA_AC0_CFG:
+  */
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+index d2ab374..7461d2e 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+@@ -25,6 +25,7 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/slab.h>
++#include <linux/of.h>
+ 
+ #include "rt2x00.h"
+ #include "rt2800lib.h"
+@@ -304,6 +305,24 @@ static void rt2800_rf_write(struct rt2x00_dev *rt2x00dev,
+ 	mutex_unlock(&rt2x00dev->csr_mutex);
+ }
+ 
++void rt6352_enable_pa_pin(struct rt2x00_dev *rt2x00dev, int enable)
++{
++	if (!rt2x00dev->pinctrl)
++		return;
++
++	if (enable) {
++		if (!rt2x00dev->pins_default)
++			return;
++
++		pinctrl_select_state(rt2x00dev->pinctrl, rt2x00dev->pins_default);
++	} else {
++		if (!rt2x00dev->pins_pa_gpio)
++			return;
++
++		pinctrl_select_state(rt2x00dev->pinctrl, rt2x00dev->pins_pa_gpio);
++	}
++}
++
+ static const unsigned int rt2800_eeprom_map[EEPROM_WORD_COUNT] = {
+ 	[EEPROM_CHIP_ID]		= 0x0000,
+ 	[EEPROM_VERSION]		= 0x0001,
+@@ -3817,14 +3836,16 @@ static void rt2800_config_channel_rf7620(struct rt2x00_dev *rt2x00dev,
+ 	rt2x00_set_field8(&rfcsr, RFCSR19_K, rf->rf4);
+ 	rt2800_rfcsr_write(rt2x00dev, 19, rfcsr);
+ 
+-	/* Default: XO=20MHz , SDM mode */
+-	rfcsr = rt2800_rfcsr_read(rt2x00dev, 16);
+-	rt2x00_set_field8(&rfcsr, RFCSR16_SDM_MODE_MT7620, 0x80);
+-	rt2800_rfcsr_write(rt2x00dev, 16, rfcsr);
++	if (rt2800_hw_get_chipver(rt2x00dev) > 1) {
++		/* Default: XO=20MHz , SDM mode */
++		rfcsr = rt2800_rfcsr_read(rt2x00dev, 16);
++		rt2x00_set_field8(&rfcsr, RFCSR16_SDM_MODE_MT7620, 0x80);
++		rt2800_rfcsr_write(rt2x00dev, 16, rfcsr);
+ 
+-	rfcsr = rt2800_rfcsr_read(rt2x00dev, 21);
+-	rt2x00_set_field8(&rfcsr, RFCSR21_BIT8, 1);
+-	rt2800_rfcsr_write(rt2x00dev, 21, rfcsr);
++		rfcsr = rt2800_rfcsr_read(rt2x00dev, 21);
++		rt2x00_set_field8(&rfcsr, RFCSR21_BIT8, 1);
++		rt2800_rfcsr_write(rt2x00dev, 21, rfcsr);
++	}
+ 
+ 	rfcsr = rt2800_rfcsr_read(rt2x00dev, 1);
+ 	rt2x00_set_field8(&rfcsr, RFCSR1_TX2_EN_MT7620,
+@@ -3858,18 +3879,23 @@ static void rt2800_config_channel_rf7620(struct rt2x00_dev *rt2x00dev,
+ 		rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x20);
+ 	}
+ 
+-	if (conf_is_ht40(conf)) {
+-		rt2800_rfcsr_write_dccal(rt2x00dev, 58, 0x08);
+-		rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x08);
+-	} else {
+-		rt2800_rfcsr_write_dccal(rt2x00dev, 58, 0x28);
+-		rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x28);
++	if (rt2800_hw_get_chipver(rt2x00dev) > 1) {
++		if (conf_is_ht40(conf)) {
++			rt2800_rfcsr_write_dccal(rt2x00dev, 58, 0x08);
++			rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x08);
++		} else {
++			rt2800_rfcsr_write_dccal(rt2x00dev, 58, 0x28);
++			rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x28);
++		}
+ 	}
+ 
+-	rfcsr = rt2800_rfcsr_read(rt2x00dev, 28);
+-	rt2x00_set_field8(&rfcsr, RFCSR28_CH11_HT40,
+-			  conf_is_ht40(conf) && (rf->channel == 11));
+-	rt2800_rfcsr_write(rt2x00dev, 28, rfcsr);
++	if (rt2800_hw_get_chipver(rt2x00dev) > 1 &&
++	    rt2800_hw_get_chipeco(rt2x00dev) == 2) {
++		rfcsr = rt2800_rfcsr_read(rt2x00dev, 28);
++		rt2x00_set_field8(&rfcsr, RFCSR28_CH11_HT40,
++				  conf_is_ht40(conf) && (rf->channel == 11));
++		rt2800_rfcsr_write(rt2x00dev, 28, rfcsr);
++	}
+ 
+ 	if (!test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags)) {
+ 		if (conf_is_ht40(conf)) {
+@@ -3983,25 +4009,29 @@ static void rt2800_config_alc_rt6352(struct rt2x00_dev *rt2x00dev,
+ 	if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY)))
+ 		rt2x00_warn(rt2x00dev, "RF busy while configuring ALC\n");
+ 
+-	if (chan->center_freq > 2457) {
+-		bbp = rt2800_bbp_read(rt2x00dev, 30);
+-		bbp = 0x40;
+-		rt2800_bbp_write(rt2x00dev, 30, bbp);
+-		rt2800_rfcsr_write(rt2x00dev, 39, 0);
+-		if (rt2x00_has_cap_external_lna_bg(rt2x00dev))
+-			rt2800_rfcsr_write(rt2x00dev, 42, 0xfb);
+-		else
+-			rt2800_rfcsr_write(rt2x00dev, 42, 0x7b);
+-	} else {
+-		bbp = rt2800_bbp_read(rt2x00dev, 30);
+-		bbp = 0x1f;
+-		rt2800_bbp_write(rt2x00dev, 30, bbp);
+-		rt2800_rfcsr_write(rt2x00dev, 39, 0x80);
+-		if (rt2x00_has_cap_external_lna_bg(rt2x00dev))
+-			rt2800_rfcsr_write(rt2x00dev, 42, 0xdb);
+-		else
+-			rt2800_rfcsr_write(rt2x00dev, 42, 0x5b);
++	if (rt2800_hw_get_chipver(rt2x00dev) > 1 &&
++	    rt2800_hw_get_chipeco(rt2x00dev) >= 2) {
++		if (chan->center_freq > 2457) {
++			bbp = rt2800_bbp_read(rt2x00dev, 30);
++			bbp = 0x40;
++			rt2800_bbp_write(rt2x00dev, 30, bbp);
++			rt2800_rfcsr_write(rt2x00dev, 39, 0);
++			if (rt2x00_has_cap_external_lna_bg(rt2x00dev))
++				rt2800_rfcsr_write(rt2x00dev, 42, 0xfb);
++			else
++				rt2800_rfcsr_write(rt2x00dev, 42, 0x7b);
++		} else {
++			bbp = rt2800_bbp_read(rt2x00dev, 30);
++			bbp = 0x1f;
++			rt2800_bbp_write(rt2x00dev, 30, bbp);
++			rt2800_rfcsr_write(rt2x00dev, 39, 0x80);
++			if (rt2x00_has_cap_external_lna_bg(rt2x00dev))
++				rt2800_rfcsr_write(rt2x00dev, 42, 0xdb);
++			else
++				rt2800_rfcsr_write(rt2x00dev, 42, 0x5b);
++		}
+ 	}
++
+ 	rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, mac_sys_ctrl);
+ 
+ 	rt2800_vco_calibration(rt2x00dev);
+@@ -4494,7 +4524,8 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
+ 	if (rt2x00_rt(rt2x00dev, RT6352)) {
+ 		/* BBP for GLRT BW */
+ 		bbp = conf_is_ht40(conf) ?
+-		      0x10 : rt2x00_has_cap_external_lna_bg(rt2x00dev) ?
++		      0x10 : !rt2x00_has_cap_external_lna_bg(rt2x00dev) ?
++		      0x1a : rt2800_hw_get_chippkg(rt2x00dev) == 1 ?
+ 		      0x15 : 0x1a;
+ 		rt2800_bbp_glrt_write(rt2x00dev, 141, bbp);
+ 
+@@ -5998,18 +6029,33 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
+ 	} else if (rt2x00_rt(rt2x00dev, RT5350)) {
+ 		rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000404);
+ 	} else if (rt2x00_rt(rt2x00dev, RT6352)) {
+-		rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000401);
+-		rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x000C0001);
+-		rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000);
+-		rt2800_register_write(rt2x00dev, TX_ALC_VGA3, 0x00000000);
+-		rt2800_register_write(rt2x00dev, TX0_BB_GAIN_ATTEN, 0x0);
+-		rt2800_register_write(rt2x00dev, TX1_BB_GAIN_ATTEN, 0x0);
+-		rt2800_register_write(rt2x00dev, TX0_RF_GAIN_ATTEN, 0x6C6C666C);
+-		rt2800_register_write(rt2x00dev, TX1_RF_GAIN_ATTEN, 0x6C6C666C);
+-		rt2800_register_write(rt2x00dev, TX0_RF_GAIN_CORRECT,
+-				      0x3630363A);
+-		rt2800_register_write(rt2x00dev, TX1_RF_GAIN_CORRECT,
+-				      0x3630363A);
++		if (rt2800_hw_get_chipver(rt2x00dev) <= 1) {
++			rt2800_register_write(rt2x00dev, TX_ALC_VGA3,
++					      0x00000000);
++			rt2800_register_write(rt2x00dev, BB_PA_MODE_CFG0,
++					      0x000055FF);
++			rt2800_register_write(rt2x00dev, BB_PA_MODE_CFG1,
++					      0x00550055);
++			rt2800_register_write(rt2x00dev, RF_PA_MODE_CFG0,
++					      0x000055FF);
++			rt2800_register_write(rt2x00dev, RF_PA_MODE_CFG1,
++					      0x00550055);
++		} else {
++			rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000401);
++			rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x000C0001);
++			rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000);
++			rt2800_register_write(rt2x00dev, TX_ALC_VGA3, 0x00000000);
++			rt2800_register_write(rt2x00dev, TX0_BB_GAIN_ATTEN, 0x0);
++			rt2800_register_write(rt2x00dev, TX1_BB_GAIN_ATTEN, 0x0);
++			rt2800_register_write(rt2x00dev, TX0_RF_GAIN_ATTEN,
++					      0x6C6C666C);
++			rt2800_register_write(rt2x00dev, TX1_RF_GAIN_ATTEN,
++					      0x6C6C666C);
++			rt2800_register_write(rt2x00dev, TX0_RF_GAIN_CORRECT,
++					      0x3630363A);
++			rt2800_register_write(rt2x00dev, TX1_RF_GAIN_CORRECT,
++					      0x3630363A);
++		}
+ 		reg = rt2800_register_read(rt2x00dev, TX_ALC_CFG_1);
+ 		rt2x00_set_field32(&reg, TX_ALC_CFG_1_ROS_BUSY_EN, 0);
+ 		rt2800_register_write(rt2x00dev, TX_ALC_CFG_1, reg);
+@@ -7122,14 +7168,16 @@ static void rt2800_init_bbp_6352(struct rt2x00_dev *rt2x00dev)
+ 	rt2800_bbp_write(rt2x00dev, 188, 0x00);
+ 	rt2800_bbp_write(rt2x00dev, 189, 0x00);
+ 
+-	rt2800_bbp_write(rt2x00dev, 91, 0x06);
+-	rt2800_bbp_write(rt2x00dev, 92, 0x04);
+-	rt2800_bbp_write(rt2x00dev, 93, 0x54);
+-	rt2800_bbp_write(rt2x00dev, 99, 0x50);
+-	rt2800_bbp_write(rt2x00dev, 148, 0x84);
+-	rt2800_bbp_write(rt2x00dev, 167, 0x80);
+-	rt2800_bbp_write(rt2x00dev, 178, 0xFF);
+-	rt2800_bbp_write(rt2x00dev, 106, 0x13);
++	if (rt2800_hw_get_chipver(rt2x00dev) > 1) {
++		rt2800_bbp_write(rt2x00dev, 91, 0x06);
++		rt2800_bbp_write(rt2x00dev, 92, 0x04);
++		rt2800_bbp_write(rt2x00dev, 93, 0x54);
++		rt2800_bbp_write(rt2x00dev, 99, 0x50);
++		rt2800_bbp_write(rt2x00dev, 148, 0x84);
++		rt2800_bbp_write(rt2x00dev, 167, 0x80);
++		rt2800_bbp_write(rt2x00dev, 178, 0xFF);
++		rt2800_bbp_write(rt2x00dev, 106, 0x13);
++	}
+ 
+ 	/* BBP for G band GLRT function (BBP_128 ~ BBP_221) */
+ 	rt2800_bbp_glrt_write(rt2x00dev, 0, 0x00);
+@@ -10359,6 +10407,9 @@ static void rt2800_restore_rf_bbp_rt6352(struct rt2x00_dev *rt2x00dev)
+ 		rt2800_register_write(rt2x00dev, RF_BYPASS3, 0x0);
+ 	}
+ 
++	if (rt2800_hw_get_chippkg(rt2x00dev) != 1)
++		return;
++
+ 	if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
+ 		rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x16);
+ 		rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x23);
+@@ -10403,8 +10454,10 @@ static void rt2800_calibration_rt6352(struct rt2x00_dev *rt2x00dev)
+ 	u32 reg;
+ 
+ 	if (rt2x00_has_cap_external_pa(rt2x00dev) ||
+-	    rt2x00_has_cap_external_lna_bg(rt2x00dev))
++	    rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
++		rt6352_enable_pa_pin(rt2x00dev, 0);
+ 		rt2800_restore_rf_bbp_rt6352(rt2x00dev);
++	}
+ 
+ 	rt2800_r_calibration(rt2x00dev);
+ 	rt2800_rf_self_txdc_cal(rt2x00dev);
+@@ -10422,6 +10475,8 @@ static void rt2800_calibration_rt6352(struct rt2x00_dev *rt2x00dev)
+ 	    !rt2x00_has_cap_external_lna_bg(rt2x00dev))
+ 		return;
+ 
++	rt6352_enable_pa_pin(rt2x00dev, 1);
++
+ 	if (rt2x00_has_cap_external_pa(rt2x00dev)) {
+ 		reg = rt2800_register_read(rt2x00dev, RF_CONTROL3);
+ 		reg |= 0x00000101;
+@@ -10432,6 +10487,9 @@ static void rt2800_calibration_rt6352(struct rt2x00_dev *rt2x00dev)
+ 		rt2800_register_write(rt2x00dev, RF_BYPASS3, reg);
+ 	}
+ 
++	if (rt2800_hw_get_chippkg(rt2x00dev) != 1)
++		return;
++
+ 	if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
+ 		rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x66);
+ 		rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x20);
+@@ -10522,31 +10580,36 @@ static void rt2800_init_rfcsr_6352(struct rt2x00_dev *rt2x00dev)
+ 	rt2800_rfcsr_write(rt2x00dev, 42, 0x5B);
+ 	rt2800_rfcsr_write(rt2x00dev, 43, 0x00);
+ 
+-	rt2800_rfcsr_write(rt2x00dev, 11, 0x21);
+-	if (rt2800_clk_is_20mhz(rt2x00dev))
+-		rt2800_rfcsr_write(rt2x00dev, 13, 0x03);
+-	else
+-		rt2800_rfcsr_write(rt2x00dev, 13, 0x00);
+-	rt2800_rfcsr_write(rt2x00dev, 14, 0x7C);
+-	rt2800_rfcsr_write(rt2x00dev, 16, 0x80);
+-	rt2800_rfcsr_write(rt2x00dev, 17, 0x99);
+-	rt2800_rfcsr_write(rt2x00dev, 18, 0x99);
+-	rt2800_rfcsr_write(rt2x00dev, 19, 0x09);
+-	rt2800_rfcsr_write(rt2x00dev, 20, 0x50);
+-	rt2800_rfcsr_write(rt2x00dev, 21, 0xB0);
+-	rt2800_rfcsr_write(rt2x00dev, 22, 0x00);
+-	rt2800_rfcsr_write(rt2x00dev, 23, 0x06);
+-	rt2800_rfcsr_write(rt2x00dev, 24, 0x00);
+-	rt2800_rfcsr_write(rt2x00dev, 25, 0x00);
+-	rt2800_rfcsr_write(rt2x00dev, 26, 0x5D);
+-	rt2800_rfcsr_write(rt2x00dev, 27, 0x00);
+-	rt2800_rfcsr_write(rt2x00dev, 28, 0x61);
+-	rt2800_rfcsr_write(rt2x00dev, 29, 0xB5);
+-	rt2800_rfcsr_write(rt2x00dev, 43, 0x02);
++	if (rt2800_hw_get_chipver(rt2x00dev) > 1) {
++		rt2800_rfcsr_write(rt2x00dev, 11, 0x21);
++		if (rt2800_clk_is_20mhz(rt2x00dev))
++			rt2800_rfcsr_write(rt2x00dev, 13, 0x03);
++		else
++			rt2800_rfcsr_write(rt2x00dev, 13, 0x00);
++		rt2800_rfcsr_write(rt2x00dev, 14, 0x7C);
++		rt2800_rfcsr_write(rt2x00dev, 16, 0x80);
++		rt2800_rfcsr_write(rt2x00dev, 17, 0x99);
++		rt2800_rfcsr_write(rt2x00dev, 18, 0x99);
++		rt2800_rfcsr_write(rt2x00dev, 19, 0x09);
++		rt2800_rfcsr_write(rt2x00dev, 20, 0x50);
++		rt2800_rfcsr_write(rt2x00dev, 21, 0xB0);
++		rt2800_rfcsr_write(rt2x00dev, 22, 0x00);
++		rt2800_rfcsr_write(rt2x00dev, 23, 0x06);
++		rt2800_rfcsr_write(rt2x00dev, 24, 0x00);
++		rt2800_rfcsr_write(rt2x00dev, 25, 0x00);
++		rt2800_rfcsr_write(rt2x00dev, 26, 0x5D);
++		rt2800_rfcsr_write(rt2x00dev, 27, 0x00);
++		rt2800_rfcsr_write(rt2x00dev, 28, 0x61);
++		rt2800_rfcsr_write(rt2x00dev, 29, 0xB5);
++		rt2800_rfcsr_write(rt2x00dev, 43, 0x02);
++	}
+ 
+-	rt2800_rfcsr_write(rt2x00dev, 28, 0x62);
+-	rt2800_rfcsr_write(rt2x00dev, 29, 0xAD);
+-	rt2800_rfcsr_write(rt2x00dev, 39, 0x80);
++	if (rt2800_hw_get_chipver(rt2x00dev) > 1 &&
++	    rt2800_hw_get_chipeco(rt2x00dev) >= 2) {
++		rt2800_rfcsr_write(rt2x00dev, 28, 0x62);
++		rt2800_rfcsr_write(rt2x00dev, 29, 0xAD);
++		rt2800_rfcsr_write(rt2x00dev, 39, 0x80);
++	}
+ 
+ 	/* Initialize RF channel register to default value */
+ 	rt2800_rfcsr_write_chanreg(rt2x00dev, 0, 0x03);
+@@ -10612,63 +10675,71 @@ static void rt2800_init_rfcsr_6352(struct rt2x00_dev *rt2x00dev)
+ 
+ 	rt2800_rfcsr_write_bank(rt2x00dev, 6, 45, 0xC5);
+ 
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 9, 0x47);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 10, 0x71);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 11, 0x33);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x0E);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x23);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 19, 0xA4);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 20, 0x02);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 21, 0x12);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 28, 0x1C);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 29, 0xEB);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 32, 0x7D);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 34, 0xD6);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 36, 0x08);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 38, 0xB4);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 43, 0xD3);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 44, 0xB3);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 45, 0xD5);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 46, 0x27);
+-	rt2800_rfcsr_write_bank(rt2x00dev, 4, 47, 0x67);
+-	rt2800_rfcsr_write_bank(rt2x00dev, 6, 47, 0x69);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 48, 0xFF);
+-	rt2800_rfcsr_write_bank(rt2x00dev, 4, 54, 0x27);
+-	rt2800_rfcsr_write_bank(rt2x00dev, 6, 54, 0x20);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x66);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 56, 0xFF);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 57, 0x1C);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 58, 0x20);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 59, 0x6B);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 60, 0xF7);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 61, 0x09);
+-
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 10, 0x51);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x06);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 19, 0xA7);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 28, 0x2C);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x64);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 8, 0x51);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 9, 0x36);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 11, 0x53);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x16);
+-
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 47, 0x6C);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 48, 0xFC);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 49, 0x1F);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 54, 0x27);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x66);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 59, 0x6B);
+-
+-	/* Initialize RF channel register for DRQFN */
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 43, 0xD3);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 44, 0xE3);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 45, 0xE5);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 47, 0x28);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x68);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 56, 0xF7);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 58, 0x02);
+-	rt2800_rfcsr_write_chanreg(rt2x00dev, 60, 0xC7);
++	if (rt2800_hw_get_chipver(rt2x00dev) > 1) {
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 9, 0x47);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 10, 0x71);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 11, 0x33);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x0E);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x23);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 19, 0xA4);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 20, 0x02);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 21, 0x12);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 28, 0x1C);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 29, 0xEB);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 32, 0x7D);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 34, 0xD6);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 36, 0x08);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 38, 0xB4);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 43, 0xD3);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 44, 0xB3);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 45, 0xD5);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 46, 0x27);
++		rt2800_rfcsr_write_bank(rt2x00dev, 4, 47, 0x67);
++		rt2800_rfcsr_write_bank(rt2x00dev, 6, 47, 0x69);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 48, 0xFF);
++		rt2800_rfcsr_write_bank(rt2x00dev, 4, 54, 0x27);
++		rt2800_rfcsr_write_bank(rt2x00dev, 6, 54, 0x20);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x66);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 56, 0xFF);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 57, 0x1C);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 58, 0x20);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 59, 0x6B);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 60, 0xF7);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 61, 0x09);
++	}
++
++	if (rt2800_hw_get_chipver(rt2x00dev) > 1 &&
++	    rt2800_hw_get_chipeco(rt2x00dev) >= 2) {
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 10, 0x51);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x06);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 19, 0xA7);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 28, 0x2C);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x64);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 8, 0x51);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 9, 0x36);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 11, 0x53);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x16);
++
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 47, 0x6C);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 48, 0xFC);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 49, 0x1F);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 54, 0x27);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x66);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 59, 0x6B);
++	}
++
++	if (rt2800_hw_get_chippkg(rt2x00dev) == 0 &&
++	    rt2800_hw_get_chipver(rt2x00dev) == 1) {
++		/* Initialize RF channel register for DRQFN */
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 43, 0xD3);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 44, 0xE3);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 45, 0xE5);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 47, 0x28);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x68);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 56, 0xF7);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 58, 0x02);
++		rt2800_rfcsr_write_chanreg(rt2x00dev, 60, 0xC7);
++	}
+ 
+ 	/* Initialize RF DC calibration register to default value */
+ 	rt2800_rfcsr_write_dccal(rt2x00dev, 0, 0x47);
+@@ -10731,12 +10802,17 @@ static void rt2800_init_rfcsr_6352(struct rt2x00_dev *rt2x00dev)
+ 	rt2800_rfcsr_write_dccal(rt2x00dev, 62, 0x00);
+ 	rt2800_rfcsr_write_dccal(rt2x00dev, 63, 0x00);
+ 
+-	rt2800_rfcsr_write_dccal(rt2x00dev, 3, 0x08);
+-	rt2800_rfcsr_write_dccal(rt2x00dev, 4, 0x04);
+-	rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x20);
++	if (rt2800_hw_get_chipver(rt2x00dev) > 1) {
++		rt2800_rfcsr_write_dccal(rt2x00dev, 3, 0x08);
++		rt2800_rfcsr_write_dccal(rt2x00dev, 4, 0x04);
++		rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x20);
++	}
+ 
+-	rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x00);
+-	rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x7C);
++	if (rt2800_hw_get_chipver(rt2x00dev) > 1 &&
++	    rt2800_hw_get_chipeco(rt2x00dev) >= 2) {
++		rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x00);
++		rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x7C);
++	}
+ 
+ 	/* Do calibration and init PA/LNA */
+ 	rt2800_calibration_rt6352(rt2x00dev);
+@@ -11282,6 +11358,17 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
+ 	rt2800_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC);
+ 	rt2800_init_led(rt2x00dev, &rt2x00dev->led_qual, LED_TYPE_QUALITY);
+ 
++	{
++		struct device_node *np = rt2x00dev->dev->of_node;
++		unsigned int led_polarity;
++
++		/* Allow overriding polarity from OF */
++		if (!of_property_read_u32(np, "ralink,led-polarity",
++					  &led_polarity))
++			rt2x00_set_field16(&eeprom, EEPROM_FREQ_LED_POLARITY,
++					   led_polarity);
++	}
++
+ 	rt2x00dev->led_mcu_reg = eeprom;
+ #endif /* CPTCFG_RT2X00_LIB_LEDS */
+ 
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
+index 194de67..a18140c 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
+@@ -76,6 +76,9 @@ struct rt2800_ops {
+ 	int (*drv_init_registers)(struct rt2x00_dev *rt2x00dev);
+ 	__le32 *(*drv_get_txwi)(struct queue_entry *entry);
+ 	unsigned int (*drv_get_dma_done)(struct data_queue *queue);
++	int (*hw_get_chippkg)(void);
++	int (*hw_get_chipver)(void);
++	int (*hw_get_chipeco)(void);
+ };
+ 
+ static inline u32 rt2800_register_read(struct rt2x00_dev *rt2x00dev,
+@@ -184,6 +187,27 @@ static inline unsigned int rt2800_drv_get_dma_done(struct data_queue *queue)
+ 	return rt2800ops->drv_get_dma_done(queue);
+ }
+ 
++static inline int rt2800_hw_get_chippkg(struct rt2x00_dev *rt2x00dev)
++{
++	const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv;
++
++	return rt2800ops->hw_get_chippkg();
++}
++
++static inline int rt2800_hw_get_chipver(struct rt2x00_dev *rt2x00dev)
++{
++	const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv;
++
++	return rt2800ops->hw_get_chipver();
++}
++
++static inline int rt2800_hw_get_chipeco(struct rt2x00_dev *rt2x00dev)
++{
++	const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv;
++
++	return rt2800ops->hw_get_chipeco();
++}
++
+ void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev,
+ 			const u8 command, const u8 token,
+ 			const u8 arg0, const u8 arg1);
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
+index c891043..b041952 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
+@@ -286,6 +286,10 @@ static int rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev)
+ 	return retval;
+ }
+ 
++static int rt2800pci_get_chippkg(void) { return 0; }
++static int rt2800pci_get_chipver(void) { return 0; }
++static int rt2800pci_get_chipeco(void) { return 0; }
++
+ static const struct ieee80211_ops rt2800pci_mac80211_ops = {
+ 	.add_chanctx = ieee80211_emulate_add_chanctx,
+ 	.remove_chanctx = ieee80211_emulate_remove_chanctx,
+@@ -333,6 +337,9 @@ static const struct rt2800_ops rt2800pci_rt2800_ops = {
+ 	.drv_init_registers	= rt2800mmio_init_registers,
+ 	.drv_get_txwi		= rt2800mmio_get_txwi,
+ 	.drv_get_dma_done	= rt2800mmio_get_dma_done,
++	.hw_get_chippkg		= rt2800pci_get_chippkg,
++	.hw_get_chipver		= rt2800pci_get_chipver,
++	.hw_get_chipeco		= rt2800pci_get_chipeco,
+ };
+ 
+ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
+index 787dbf0..e0d7893 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
+@@ -27,6 +27,12 @@
+ #include "rt2800lib.h"
+ #include "rt2800mmio.h"
+ 
++/* Needed to probe CHIP_VER register on MT7620 */
++#ifdef CONFIG_SOC_MT7620
++#include <asm/mach-ralink/ralink_regs.h>
++#include <asm/mach-ralink/mt7620.h>
++#endif
++
+ /* Allow hardware encryption to be disabled. */
+ static bool modparam_nohwcrypt;
+ module_param_named(nohwcrypt, modparam_nohwcrypt, bool, 0444);
+@@ -90,19 +96,6 @@ static int rt2800soc_set_device_state(struct rt2x00_dev *rt2x00dev,
+ 	return retval;
+ }
+ 
+-static int rt2800soc_read_eeprom(struct rt2x00_dev *rt2x00dev)
+-{
+-	void __iomem *base_addr = ioremap(0x1F040000, EEPROM_SIZE);
+-
+-	if (!base_addr)
+-		return -ENOMEM;
+-
+-	memcpy_fromio(rt2x00dev->eeprom, base_addr, EEPROM_SIZE);
+-
+-	iounmap(base_addr);
+-	return 0;
+-}
+-
+ /* Firmware functions */
+ static char *rt2800soc_get_firmware_name(struct rt2x00_dev *rt2x00dev)
+ {
+@@ -131,6 +124,27 @@ static int rt2800soc_write_firmware(struct rt2x00_dev *rt2x00dev,
+ 	return 0;
+ }
+ 
++#ifdef CONFIG_SOC_MT7620
++static int rt2800soc_get_chippkg(void)
++{
++	return mt7620_get_pkg();
++}
++
++static int rt2800soc_get_chipver(void)
++{
++	return mt7620_get_chipver();
++}
++
++static int rt2800soc_get_chipeco(void)
++{
++	return mt7620_get_eco();
++}
++#else
++static int rt2800soc_get_chippkg(void) { return 0; }
++static int rt2800soc_get_chipver(void) { return 0; }
++static int rt2800soc_get_chipeco(void) { return 0; }
++#endif
++
+ static const struct ieee80211_ops rt2800soc_mac80211_ops = {
+ 	.add_chanctx = ieee80211_emulate_add_chanctx,
+ 	.remove_chanctx = ieee80211_emulate_remove_chanctx,
+@@ -172,12 +186,15 @@ static const struct rt2800_ops rt2800soc_rt2800_ops = {
+ 	.register_multiread	= rt2x00mmio_register_multiread,
+ 	.register_multiwrite	= rt2x00mmio_register_multiwrite,
+ 	.regbusy_read		= rt2x00mmio_regbusy_read,
+-	.read_eeprom		= rt2800soc_read_eeprom,
++	.read_eeprom		= rt2x00lib_read_eeprom,
+ 	.hwcrypt_disabled	= rt2800soc_hwcrypt_disabled,
+ 	.drv_write_firmware	= rt2800soc_write_firmware,
+ 	.drv_init_registers	= rt2800mmio_init_registers,
+ 	.drv_get_txwi		= rt2800mmio_get_txwi,
+ 	.drv_get_dma_done	= rt2800mmio_get_dma_done,
++	.hw_get_chippkg		= rt2800soc_get_chippkg,
++	.hw_get_chipver		= rt2800soc_get_chipver,
++	.hw_get_chipeco		= rt2800soc_get_chipeco,
+ };
+ 
+ static const struct rt2x00lib_ops rt2800soc_rt2x00_ops = {
+@@ -243,10 +260,17 @@ static int rt2800soc_probe(struct platform_device *pdev)
+ 	return rt2x00soc_probe(pdev, &rt2800soc_ops);
+ }
+ 
++static const struct of_device_id rt2880_wmac_match[] = {
++	{ .compatible = "ralink,rt2880-wmac" },
++	{},
++};
++MODULE_DEVICE_TABLE(of, rt2880_wmac_match);
++
+ static struct platform_driver rt2800soc_driver = {
+ 	.driver		= {
+ 		.name		= "rt2800_wmac",
+ 		.mod_name	= KBUILD_MODNAME,
++		.of_match_table	= rt2880_wmac_match,
+ 	},
+ 	.probe		= rt2800soc_probe,
+ 	.remove		= rt2x00soc_remove,
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
+index a37f8ea..2663447 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
+@@ -628,6 +628,10 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
+ 	return 0;
+ }
+ 
++static int rt2800usb_get_chippkg(void) { return 0; }
++static int rt2800usb_get_chipver(void) { return 0; }
++static int rt2800usb_get_chipeco(void) { return 0; }
++
+ static const struct ieee80211_ops rt2800usb_mac80211_ops = {
+ 	.add_chanctx = ieee80211_emulate_add_chanctx,
+ 	.remove_chanctx = ieee80211_emulate_remove_chanctx,
+@@ -676,6 +680,9 @@ static const struct rt2800_ops rt2800usb_rt2800_ops = {
+ 	.drv_init_registers	= rt2800usb_init_registers,
+ 	.drv_get_txwi		= rt2800usb_get_txwi,
+ 	.drv_get_dma_done	= rt2800usb_get_dma_done,
++	.hw_get_chippkg		= rt2800usb_get_chippkg,
++	.hw_get_chipver		= rt2800usb_get_chipver,
++	.hw_get_chipeco		= rt2800usb_get_chipeco,
+ };
+ 
+ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
+index 4e1ef18..b4b8b89 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
+@@ -28,6 +28,8 @@
+ #include <linux/average.h>
+ #include <linux/usb.h>
+ #include <linux/clk.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/rt2x00_platform.h>
+ 
+ #include <net/mac80211.h>
+ 
+@@ -397,6 +399,7 @@ struct hw_mode_spec {
+ 	unsigned int supported_bands;
+ #define SUPPORT_BAND_2GHZ	0x00000001
+ #define SUPPORT_BAND_5GHZ	0x00000002
++#define SUPPORT_BAND_BOTH	(SUPPORT_BAND_2GHZ | SUPPORT_BAND_5GHZ)
+ 
+ 	unsigned int supported_rates;
+ #define SUPPORT_RATE_CCK	0x00000001
+@@ -692,6 +695,7 @@ enum rt2x00_capability_flags {
+ 	REQUIRE_HT_TX_DESC,
+ 	REQUIRE_PS_AUTOWAKE,
+ 	REQUIRE_DELAYED_RFKILL,
++	REQUIRE_EEPROM_FILE,
+ 
+ 	/*
+ 	 * Capabilities
+@@ -1014,6 +1018,11 @@ struct rt2x00_dev {
+ 
+ 	/* Clock for System On Chip devices. */
+ 	struct clk *clk;
++
++	/* pinctrl and states for System On Chip devices with PA/LNA. */
++	struct pinctrl *pinctrl;
++	struct pinctrl_state *pins_default;
++	struct pinctrl_state *pins_pa_gpio;
+ };
+ 
+ struct rt2x00_bar_list_entry {
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+index 274524e..69f8d5a 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+@@ -990,6 +990,12 @@ static void rt2x00lib_rate(struct ieee80211_rate *entry,
+ 
+ void rt2x00lib_set_mac_address(struct rt2x00_dev *rt2x00dev, u8 *eeprom_mac_addr)
+ {
++	struct rt2x00_platform_data *pdata;
++
++	pdata = rt2x00dev->dev->platform_data;
++	if (pdata && pdata->mac_address)
++		ether_addr_copy(eeprom_mac_addr, pdata->mac_address);
++
+ 	of_get_mac_address(rt2x00dev->dev->of_node, eeprom_mac_addr);
+ 
+ 	if (!is_valid_ether_addr(eeprom_mac_addr)) {
+@@ -1007,6 +1013,32 @@ static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev,
+ 	struct ieee80211_rate *rates;
+ 	unsigned int num_rates;
+ 	unsigned int i;
++#ifdef CONFIG_OF
++	struct device_node *np = rt2x00dev->dev->of_node;
++	unsigned int enabled;
++	if (!of_property_read_u32(np, "ralink,2ghz",
++                                          &enabled) && !enabled)
++		spec->supported_bands &= ~SUPPORT_BAND_2GHZ;
++	if (!of_property_read_u32(np, "ralink,5ghz",
++                                          &enabled) && !enabled)
++		spec->supported_bands &= ~SUPPORT_BAND_5GHZ;
++#endif /* CONFIG_OF */
++
++	if (rt2x00dev->dev->platform_data) {
++		struct rt2x00_platform_data *pdata;
++
++		pdata = rt2x00dev->dev->platform_data;
++		if (pdata->disable_2ghz)
++			spec->supported_bands &= ~SUPPORT_BAND_2GHZ;
++		if (pdata->disable_5ghz)
++			spec->supported_bands &= ~SUPPORT_BAND_5GHZ;
++	}
++
++	if ((spec->supported_bands & SUPPORT_BAND_BOTH) == 0) {
++		rt2x00_err(rt2x00dev, "No supported bands\n");
++		return -EINVAL;
++	}
++
+ 
+ 	num_rates = 0;
+ 	if (spec->supported_rates & SUPPORT_RATE_CCK)
+@@ -1330,7 +1362,7 @@ static inline void rt2x00lib_set_if_combinations(struct rt2x00_dev *rt2x00dev)
+ 	 */
+ 	if_limit = &rt2x00dev->if_limits_ap;
+ 	if_limit->max = rt2x00dev->ops->max_ap_intf;
+-	if_limit->types = BIT(NL80211_IFTYPE_AP);
++	if_limit->types = BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_STATION);
+ #ifdef CPTCFG_MAC80211_MESH
+ 	if_limit->types |= BIT(NL80211_IFTYPE_MESH_POINT);
+ #endif
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00eeprom.c b/drivers/net/wireless/ralink/rt2x00/rt2x00eeprom.c
+new file mode 100644
+index 0000000..15c4e0b
+--- /dev/null
++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00eeprom.c
+@@ -0,0 +1,208 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*	Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
++ *	Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com>
++ *	<http://rt2x00.serialmonkey.com>
++ */
++
++/*	Module: rt2x00lib
++ *	Abstract: rt2x00 eeprom file loading routines.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#if IS_ENABLED(CONFIG_MTD)
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#endif
++#include <linux/nvmem-consumer.h>
++#include <linux/of.h>
++
++#include "rt2x00.h"
++#include "rt2x00soc.h"
++
++static void rt2800lib_eeprom_swap(struct rt2x00_dev *rt2x00dev)
++{
++	struct device_node *np = rt2x00dev->dev->of_node;
++	size_t len = rt2x00dev->ops->eeprom_size;
++	int i;
++
++	if (!of_find_property(np, "ralink,eeprom-swap", NULL))
++		return;
++
++	for (i = 0; i < len / sizeof(u16); i++)
++		rt2x00dev->eeprom[i] = swab16(rt2x00dev->eeprom[i]);
++}
++
++#if IS_ENABLED(CONFIG_MTD)
++static int rt2800lib_read_eeprom_mtd(struct rt2x00_dev *rt2x00dev)
++{
++	int ret = -EINVAL;
++#ifdef CONFIG_OF
++	struct device_node *np = rt2x00dev->dev->of_node, *mtd_np = NULL;
++	int size, offset = 0;
++	struct mtd_info *mtd;
++	const char *part;
++	const __be32 *list;
++	phandle phandle;
++	size_t retlen;
++
++	list = of_get_property(np, "ralink,mtd-eeprom", &size);
++	if (!list)
++		return -ENOENT;
++
++	phandle = be32_to_cpup(list++);
++	if (phandle)
++		mtd_np = of_find_node_by_phandle(phandle);
++	if (!mtd_np) {
++		dev_err(rt2x00dev->dev, "failed to load mtd phandle\n");
++		return -EINVAL;
++	}
++
++	part = of_get_property(mtd_np, "label", NULL);
++	if (!part)
++		part = mtd_np->name;
++
++	mtd = get_mtd_device_nm(part);
++	if (IS_ERR(mtd)) {
++		dev_err(rt2x00dev->dev, "failed to get mtd device \"%s\"\n", part);
++		return PTR_ERR(mtd);
++	}
++
++	if (size > sizeof(*list))
++		offset = be32_to_cpup(list);
++
++	ret = mtd_read(mtd, offset, rt2x00dev->ops->eeprom_size,
++		       &retlen, (u_char *)rt2x00dev->eeprom);
++	put_mtd_device(mtd);
++
++	if (retlen != rt2x00dev->ops->eeprom_size || ret) {
++		dev_err(rt2x00dev->dev, "failed to load eeprom from device \"%s\"\n", part);
++		return ret;
++	}
++
++	rt2800lib_eeprom_swap(rt2x00dev);
++
++	dev_info(rt2x00dev->dev, "loaded eeprom from mtd device \"%s\"\n", part);
++#endif
++
++	return ret;
++}
++#endif
++
++static int rt2800lib_read_eeprom_nvmem(struct rt2x00_dev *rt2x00dev)
++{
++	struct device_node *np = rt2x00dev->dev->of_node;
++	unsigned int len = rt2x00dev->ops->eeprom_size;
++	struct nvmem_cell *cell;
++	const void *data;
++	size_t retlen;
++	int ret = 0;
++
++	cell = of_nvmem_cell_get(np, "eeprom");
++	if (IS_ERR(cell))
++		return PTR_ERR(cell);
++
++	data = nvmem_cell_read(cell, &retlen);
++	nvmem_cell_put(cell);
++
++	if (IS_ERR(data))
++		return PTR_ERR(data);
++
++	if (retlen != len) {
++		dev_err(rt2x00dev->dev, "invalid eeprom size, required: 0x%04x\n", len);
++		ret = -EINVAL;
++		goto exit;
++	}
++
++	memcpy(rt2x00dev->eeprom, data, len);
++
++	rt2800lib_eeprom_swap(rt2x00dev);
++
++exit:
++	kfree(data);
++	return ret;
++}
++
++static const char *
++rt2x00lib_get_eeprom_file_name(struct rt2x00_dev *rt2x00dev)
++{
++	struct rt2x00_platform_data *pdata = rt2x00dev->dev->platform_data;
++#ifdef CONFIG_OF
++	struct device_node *np;
++	const char *eep;
++#endif
++
++	if (pdata && pdata->eeprom_file_name)
++		return pdata->eeprom_file_name;
++
++#ifdef CONFIG_OF
++	np = rt2x00dev->dev->of_node;
++	if (np && !of_property_read_string(np, "ralink,eeprom", &eep))
++		return eep;
++#endif
++
++	return NULL;
++}
++
++static int rt2x00lib_read_eeprom_file(struct rt2x00_dev *rt2x00dev)
++{
++	const struct firmware *ee;
++	const char *ee_name;
++	int retval;
++
++	ee_name = rt2x00lib_get_eeprom_file_name(rt2x00dev);
++	if (!ee_name && test_bit(REQUIRE_EEPROM_FILE, &rt2x00dev->cap_flags)) {
++		rt2x00_err(rt2x00dev, "Required EEPROM name is missing.");
++		return -EINVAL;
++	}
++
++	if (!ee_name)
++		return 0;
++
++	rt2x00_info(rt2x00dev, "Loading EEPROM data from '%s'.\n", ee_name);
++
++	retval = request_firmware(&ee, ee_name, rt2x00dev->dev);
++	if (retval) {
++		rt2x00_err(rt2x00dev, "Failed to request EEPROM.\n");
++		return retval;
++	}
++
++	if (!ee || !ee->size || !ee->data) {
++		rt2x00_err(rt2x00dev, "Failed to read EEPROM file.\n");
++		retval = -ENOENT;
++		goto err_exit;
++	}
++
++	if (ee->size != rt2x00dev->ops->eeprom_size) {
++		rt2x00_err(rt2x00dev,
++			   "EEPROM file size is invalid, it should be %d bytes\n",
++			   rt2x00dev->ops->eeprom_size);
++		retval = -EINVAL;
++		goto err_release_ee;
++	}
++
++	memcpy(rt2x00dev->eeprom, ee->data, rt2x00dev->ops->eeprom_size);
++
++err_release_ee:
++	release_firmware(ee);
++err_exit:
++	return retval;
++}
++
++int rt2x00lib_read_eeprom(struct rt2x00_dev *rt2x00dev)
++{
++	int ret;
++
++#if IS_ENABLED(CONFIG_MTD)
++	ret = rt2800lib_read_eeprom_mtd(rt2x00dev);
++	if (!ret)
++		return 0;
++#endif
++
++	ret = rt2800lib_read_eeprom_nvmem(rt2x00dev);
++	if (!ret)
++		return 0;
++
++	return rt2x00lib_read_eeprom_file(rt2x00dev);
++}
++EXPORT_SYMBOL_GPL(rt2x00lib_read_eeprom);
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00leds.c b/drivers/net/wireless/ralink/rt2x00/rt2x00leds.c
+index f5361d5..bad5ce2 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2x00leds.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00leds.c
+@@ -98,6 +98,9 @@ static int rt2x00leds_register_led(struct rt2x00_dev *rt2x00dev,
+ 	led->led_dev.name = name;
+ 	led->led_dev.brightness = LED_OFF;
+ 
++	if (rt2x00_is_soc(rt2x00dev))
++		led->led_dev.brightness_set(&led->led_dev, LED_OFF);
++
+ 	retval = led_classdev_register(device, &led->led_dev);
+ 	if (retval) {
+ 		rt2x00_err(rt2x00dev, "Failed to register led handler\n");
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c
+index eface61..541b718 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c
+@@ -86,6 +86,7 @@ int rt2x00soc_probe(struct platform_device *pdev, const struct rt2x00_ops *ops)
+ 	if (IS_ERR(rt2x00dev->clk))
+ 		rt2x00dev->clk = NULL;
+ 
++	set_bit(REQUIRE_EEPROM_FILE, &rt2x00dev->cap_flags);
+ 	rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC);
+ 
+ 	retval = rt2x00soc_alloc_reg(rt2x00dev);
+@@ -96,6 +97,21 @@ int rt2x00soc_probe(struct platform_device *pdev, const struct rt2x00_ops *ops)
+ 	if (retval)
+ 		goto exit_free_reg;
+ 
++	rt2x00dev->pinctrl = devm_pinctrl_get(&pdev->dev);
++	if (IS_ERR(rt2x00dev->pinctrl)) {
++		rt2x00dev->pinctrl = NULL;
++		rt2x00dev->pins_default = NULL;
++		rt2x00dev->pins_pa_gpio = NULL;
++	} else {
++		rt2x00dev->pins_default = pinctrl_lookup_state(rt2x00dev->pinctrl, "default");
++		if (IS_ERR(rt2x00dev->pins_default))
++			rt2x00dev->pins_default = NULL;
++
++		rt2x00dev->pins_pa_gpio = pinctrl_lookup_state(rt2x00dev->pinctrl, "pa_gpio");
++		if (IS_ERR(rt2x00dev->pins_pa_gpio))
++			rt2x00dev->pins_pa_gpio = NULL;
++	}
++
+ 	return 0;
+ 
+ exit_free_reg:
+diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00soc.h b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.h
+index 021fd06..21cd951 100644
+--- a/drivers/net/wireless/ralink/rt2x00/rt2x00soc.h
++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.h
+@@ -26,4 +26,13 @@ int rt2x00soc_resume(struct platform_device *pdev);
+ #define rt2x00soc_resume	NULL
+ #endif /* CONFIG_PM */
+ 
++/*
++ * EEPROM file handlers.
++ */
++#ifdef CPTCFG_RT2X00_LIB_EEPROM
++int rt2x00lib_read_eeprom(struct rt2x00_dev *rt2x00dev);
++#else
++#define rt2x00lib_read_eeprom	NULL
++#endif /* CPTCFG_RT2X00_LIB_EEPROM */
++
+ #endif /* RT2X00SOC_H */
+diff --git a/include/linux/rt2x00_platform.h b/include/linux/rt2x00_platform.h
+new file mode 100644
+index 0000000..e10377e
+--- /dev/null
++++ b/include/linux/rt2x00_platform.h
+@@ -0,0 +1,23 @@
++/*
++ * Platform data definition for the rt2x00 driver
++ *
++ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 as published
++ * by the Free Software Foundation.
++ *
++ */
++
++#ifndef _RT2X00_PLATFORM_H
++#define _RT2X00_PLATFORM_H
++
++struct rt2x00_platform_data {
++	char *eeprom_file_name;
++	const u8 *mac_address;
++
++	int disable_2ghz;
++	int disable_5ghz;
++};
++
++#endif /* _RT2X00_PLATFORM_H */
+diff --git a/local-symbols b/local-symbols
+index c8f7132..ccc6ef7 100644
+--- a/local-symbols
++++ b/local-symbols
+@@ -326,6 +326,7 @@ RT2X00_LIB_FIRMWARE=
+ RT2X00_LIB_CRYPTO=
+ RT2X00_LIB_LEDS=
+ RT2X00_LIB_DEBUGFS=
++RT2X00_LIB_EEPROM=
+ RT2X00_DEBUG=
+ WLAN_VENDOR_REALTEK=
+ RTL8180=
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0006-sync-backports-patches-rt2x00.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0006-sync-backports-patches-rt2x00.patch
deleted file mode 100644
index 39975b3..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0006-sync-backports-patches-rt2x00.patch
+++ /dev/null
@@ -1,1263 +0,0 @@
-From a08aa9e789ade7bb35eb442afa6566368d5a318f Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Tue, 12 Mar 2024 11:27:40 +0800
-Subject: [PATCH 06/61] sync backports patches/rt2x00
-
----
- drivers/net/wireless/ralink/rt2x00/Kconfig    |  23 +-
- drivers/net/wireless/ralink/rt2x00/Makefile   |   1 +
- drivers/net/wireless/ralink/rt2x00/rt2800.h   |   5 +
- .../net/wireless/ralink/rt2x00/rt2800lib.c    | 373 +++++++++++-------
- .../net/wireless/ralink/rt2x00/rt2800lib.h    |  24 ++
- .../net/wireless/ralink/rt2x00/rt2800pci.c    |   7 +
- .../net/wireless/ralink/rt2x00/rt2800soc.c    |  52 ++-
- .../net/wireless/ralink/rt2x00/rt2800usb.c    |   7 +
- drivers/net/wireless/ralink/rt2x00/rt2x00.h   |  15 +
- .../net/wireless/ralink/rt2x00/rt2x00dev.c    |  34 +-
- .../net/wireless/ralink/rt2x00/rt2x00eeprom.c | 208 ++++++++++
- .../net/wireless/ralink/rt2x00/rt2x00leds.c   |   3 +
- .../net/wireless/ralink/rt2x00/rt2x00soc.c    |  16 +
- .../net/wireless/ralink/rt2x00/rt2x00soc.h    |   9 +
- include/linux/rt2x00_platform.h               |  23 ++
- local-symbols                                 |   1 +
- 16 files changed, 635 insertions(+), 166 deletions(-)
- create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00eeprom.c
- create mode 100644 include/linux/rt2x00_platform.h
-
-diff --git a/drivers/net/wireless/ralink/rt2x00/Kconfig b/drivers/net/wireless/ralink/rt2x00/Kconfig
-index 8f6e3d2..abaa51b 100644
---- a/drivers/net/wireless/ralink/rt2x00/Kconfig
-+++ b/drivers/net/wireless/ralink/rt2x00/Kconfig
-@@ -70,6 +70,7 @@ config RT2800PCI
- 	select RT2X00_LIB_MMIO
- 	select RT2X00_LIB_PCI
- 	select RT2X00_LIB_FIRMWARE
-+	select RT2X00_LIB_EEPROM
- 	select RT2X00_LIB_CRYPTO
- 	depends on CRC_CCITT
- 	depends on EEPROM_93CX6
-@@ -211,13 +212,15 @@ endif
- config RT2800SOC
- 	tristate "Ralink WiSoC support"
- 	depends on m
--	depends on SOC_RT288X || SOC_RT305X || SOC_MT7620
-+	depends on SOC_RT288X || SOC_RT305X || SOC_RT3883 || SOC_MT7620
- 	select RT2X00_LIB_SOC
- 	select RT2X00_LIB_MMIO
- 	select RT2X00_LIB_CRYPTO
- 	select RT2X00_LIB_FIRMWARE
-+	select RT2X00_LIB_EEPROM
- 	select RT2800_LIB
- 	select RT2800_LIB_MMIO
-+	select MTD if SOC_RT288X || SOC_RT305X
- 	help
- 	  This adds support for Ralink WiSoC devices.
- 	  Supported chips: RT2880, RT3050, RT3052, RT3350, RT3352.
-@@ -226,36 +229,37 @@ config RT2800SOC
- 
- 
- config RT2800_LIB
--	tristate
-+	tristate "RT2800 USB/PCI support"
- 	depends on m
- 
- config RT2800_LIB_MMIO
--	tristate
-+	tristate "RT2800 MMIO support"
- 	depends on m
- 	select RT2X00_LIB_MMIO
- 	select RT2800_LIB
- 
- config RT2X00_LIB_MMIO
--	tristate
-+	tristate "RT2x00 MMIO support"
- 	depends on m
- 
- config RT2X00_LIB_PCI
--	tristate
-+	tristate "RT2x00 PCI support"
- 	depends on m
- 	select RT2X00_LIB
- 
- config RT2X00_LIB_SOC
--	tristate
-+	tristate "RT2x00 SoC support"
-+	depends on SOC_RT288X || SOC_RT305X || SOC_RT3883 || SOC_MT7620
- 	depends on m
- 	select RT2X00_LIB
- 
- config RT2X00_LIB_USB
--	tristate
-+	tristate "RT2x00 USB support"
- 	depends on m
- 	select RT2X00_LIB
- 
- config RT2X00_LIB
--	tristate
-+	tristate "RT2x00 support"
- 	depends on m
- 
- config RT2X00_LIB_FIRMWARE
-@@ -265,6 +269,9 @@ config RT2X00_LIB_FIRMWARE
- config RT2X00_LIB_CRYPTO
- 	bool
- 
-+config RT2X00_LIB_EEPROM
-+	bool
-+
- config RT2X00_LIB_LEDS
- 	bool
- 	default y if (RT2X00_LIB=y && LEDS_CLASS=y) || (RT2X00_LIB=m && LEDS_CLASS!=n)
-diff --git a/drivers/net/wireless/ralink/rt2x00/Makefile b/drivers/net/wireless/ralink/rt2x00/Makefile
-index 4a2156b..94335ec 100644
---- a/drivers/net/wireless/ralink/rt2x00/Makefile
-+++ b/drivers/net/wireless/ralink/rt2x00/Makefile
-@@ -8,6 +8,7 @@ rt2x00lib-$(CPTCFG_RT2X00_LIB_DEBUGFS)	+= rt2x00debug.o
- rt2x00lib-$(CPTCFG_RT2X00_LIB_CRYPTO)	+= rt2x00crypto.o
- rt2x00lib-$(CPTCFG_RT2X00_LIB_FIRMWARE)	+= rt2x00firmware.o
- rt2x00lib-$(CPTCFG_RT2X00_LIB_LEDS)	+= rt2x00leds.o
-+rt2x00lib-$(CPTCFG_RT2X00_LIB_EEPROM)	+= rt2x00eeprom.o
- 
- obj-$(CPTCFG_RT2X00_LIB)		+= rt2x00lib.o
- obj-$(CPTCFG_RT2X00_LIB_MMIO)		+= rt2x00mmio.o
-diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800.h b/drivers/net/wireless/ralink/rt2x00/rt2800.h
-index 8930589..cbfa680 100644
---- a/drivers/net/wireless/ralink/rt2x00/rt2800.h
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h
-@@ -1056,6 +1056,11 @@
- #define MIMO_PS_CFG_RX_STBY_POL		FIELD32(0x00000010)
- #define MIMO_PS_CFG_RX_RX_STBY0		FIELD32(0x00000020)
- 
-+#define BB_PA_MODE_CFG0			0x1214
-+#define BB_PA_MODE_CFG1			0x1218
-+#define RF_PA_MODE_CFG0			0x121C
-+#define RF_PA_MODE_CFG1			0x1220
-+
- /*
-  * EDCA_AC0_CFG:
-  */
-diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-index d2ab374..7461d2e 100644
---- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-@@ -25,6 +25,7 @@
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/slab.h>
-+#include <linux/of.h>
- 
- #include "rt2x00.h"
- #include "rt2800lib.h"
-@@ -304,6 +305,24 @@ static void rt2800_rf_write(struct rt2x00_dev *rt2x00dev,
- 	mutex_unlock(&rt2x00dev->csr_mutex);
- }
- 
-+void rt6352_enable_pa_pin(struct rt2x00_dev *rt2x00dev, int enable)
-+{
-+	if (!rt2x00dev->pinctrl)
-+		return;
-+
-+	if (enable) {
-+		if (!rt2x00dev->pins_default)
-+			return;
-+
-+		pinctrl_select_state(rt2x00dev->pinctrl, rt2x00dev->pins_default);
-+	} else {
-+		if (!rt2x00dev->pins_pa_gpio)
-+			return;
-+
-+		pinctrl_select_state(rt2x00dev->pinctrl, rt2x00dev->pins_pa_gpio);
-+	}
-+}
-+
- static const unsigned int rt2800_eeprom_map[EEPROM_WORD_COUNT] = {
- 	[EEPROM_CHIP_ID]		= 0x0000,
- 	[EEPROM_VERSION]		= 0x0001,
-@@ -3817,14 +3836,16 @@ static void rt2800_config_channel_rf7620(struct rt2x00_dev *rt2x00dev,
- 	rt2x00_set_field8(&rfcsr, RFCSR19_K, rf->rf4);
- 	rt2800_rfcsr_write(rt2x00dev, 19, rfcsr);
- 
--	/* Default: XO=20MHz , SDM mode */
--	rfcsr = rt2800_rfcsr_read(rt2x00dev, 16);
--	rt2x00_set_field8(&rfcsr, RFCSR16_SDM_MODE_MT7620, 0x80);
--	rt2800_rfcsr_write(rt2x00dev, 16, rfcsr);
-+	if (rt2800_hw_get_chipver(rt2x00dev) > 1) {
-+		/* Default: XO=20MHz , SDM mode */
-+		rfcsr = rt2800_rfcsr_read(rt2x00dev, 16);
-+		rt2x00_set_field8(&rfcsr, RFCSR16_SDM_MODE_MT7620, 0x80);
-+		rt2800_rfcsr_write(rt2x00dev, 16, rfcsr);
- 
--	rfcsr = rt2800_rfcsr_read(rt2x00dev, 21);
--	rt2x00_set_field8(&rfcsr, RFCSR21_BIT8, 1);
--	rt2800_rfcsr_write(rt2x00dev, 21, rfcsr);
-+		rfcsr = rt2800_rfcsr_read(rt2x00dev, 21);
-+		rt2x00_set_field8(&rfcsr, RFCSR21_BIT8, 1);
-+		rt2800_rfcsr_write(rt2x00dev, 21, rfcsr);
-+	}
- 
- 	rfcsr = rt2800_rfcsr_read(rt2x00dev, 1);
- 	rt2x00_set_field8(&rfcsr, RFCSR1_TX2_EN_MT7620,
-@@ -3858,18 +3879,23 @@ static void rt2800_config_channel_rf7620(struct rt2x00_dev *rt2x00dev,
- 		rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x20);
- 	}
- 
--	if (conf_is_ht40(conf)) {
--		rt2800_rfcsr_write_dccal(rt2x00dev, 58, 0x08);
--		rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x08);
--	} else {
--		rt2800_rfcsr_write_dccal(rt2x00dev, 58, 0x28);
--		rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x28);
-+	if (rt2800_hw_get_chipver(rt2x00dev) > 1) {
-+		if (conf_is_ht40(conf)) {
-+			rt2800_rfcsr_write_dccal(rt2x00dev, 58, 0x08);
-+			rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x08);
-+		} else {
-+			rt2800_rfcsr_write_dccal(rt2x00dev, 58, 0x28);
-+			rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x28);
-+		}
- 	}
- 
--	rfcsr = rt2800_rfcsr_read(rt2x00dev, 28);
--	rt2x00_set_field8(&rfcsr, RFCSR28_CH11_HT40,
--			  conf_is_ht40(conf) && (rf->channel == 11));
--	rt2800_rfcsr_write(rt2x00dev, 28, rfcsr);
-+	if (rt2800_hw_get_chipver(rt2x00dev) > 1 &&
-+	    rt2800_hw_get_chipeco(rt2x00dev) == 2) {
-+		rfcsr = rt2800_rfcsr_read(rt2x00dev, 28);
-+		rt2x00_set_field8(&rfcsr, RFCSR28_CH11_HT40,
-+				  conf_is_ht40(conf) && (rf->channel == 11));
-+		rt2800_rfcsr_write(rt2x00dev, 28, rfcsr);
-+	}
- 
- 	if (!test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags)) {
- 		if (conf_is_ht40(conf)) {
-@@ -3983,25 +4009,29 @@ static void rt2800_config_alc_rt6352(struct rt2x00_dev *rt2x00dev,
- 	if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY)))
- 		rt2x00_warn(rt2x00dev, "RF busy while configuring ALC\n");
- 
--	if (chan->center_freq > 2457) {
--		bbp = rt2800_bbp_read(rt2x00dev, 30);
--		bbp = 0x40;
--		rt2800_bbp_write(rt2x00dev, 30, bbp);
--		rt2800_rfcsr_write(rt2x00dev, 39, 0);
--		if (rt2x00_has_cap_external_lna_bg(rt2x00dev))
--			rt2800_rfcsr_write(rt2x00dev, 42, 0xfb);
--		else
--			rt2800_rfcsr_write(rt2x00dev, 42, 0x7b);
--	} else {
--		bbp = rt2800_bbp_read(rt2x00dev, 30);
--		bbp = 0x1f;
--		rt2800_bbp_write(rt2x00dev, 30, bbp);
--		rt2800_rfcsr_write(rt2x00dev, 39, 0x80);
--		if (rt2x00_has_cap_external_lna_bg(rt2x00dev))
--			rt2800_rfcsr_write(rt2x00dev, 42, 0xdb);
--		else
--			rt2800_rfcsr_write(rt2x00dev, 42, 0x5b);
-+	if (rt2800_hw_get_chipver(rt2x00dev) > 1 &&
-+	    rt2800_hw_get_chipeco(rt2x00dev) >= 2) {
-+		if (chan->center_freq > 2457) {
-+			bbp = rt2800_bbp_read(rt2x00dev, 30);
-+			bbp = 0x40;
-+			rt2800_bbp_write(rt2x00dev, 30, bbp);
-+			rt2800_rfcsr_write(rt2x00dev, 39, 0);
-+			if (rt2x00_has_cap_external_lna_bg(rt2x00dev))
-+				rt2800_rfcsr_write(rt2x00dev, 42, 0xfb);
-+			else
-+				rt2800_rfcsr_write(rt2x00dev, 42, 0x7b);
-+		} else {
-+			bbp = rt2800_bbp_read(rt2x00dev, 30);
-+			bbp = 0x1f;
-+			rt2800_bbp_write(rt2x00dev, 30, bbp);
-+			rt2800_rfcsr_write(rt2x00dev, 39, 0x80);
-+			if (rt2x00_has_cap_external_lna_bg(rt2x00dev))
-+				rt2800_rfcsr_write(rt2x00dev, 42, 0xdb);
-+			else
-+				rt2800_rfcsr_write(rt2x00dev, 42, 0x5b);
-+		}
- 	}
-+
- 	rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, mac_sys_ctrl);
- 
- 	rt2800_vco_calibration(rt2x00dev);
-@@ -4494,7 +4524,8 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
- 	if (rt2x00_rt(rt2x00dev, RT6352)) {
- 		/* BBP for GLRT BW */
- 		bbp = conf_is_ht40(conf) ?
--		      0x10 : rt2x00_has_cap_external_lna_bg(rt2x00dev) ?
-+		      0x10 : !rt2x00_has_cap_external_lna_bg(rt2x00dev) ?
-+		      0x1a : rt2800_hw_get_chippkg(rt2x00dev) == 1 ?
- 		      0x15 : 0x1a;
- 		rt2800_bbp_glrt_write(rt2x00dev, 141, bbp);
- 
-@@ -5998,18 +6029,33 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
- 	} else if (rt2x00_rt(rt2x00dev, RT5350)) {
- 		rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000404);
- 	} else if (rt2x00_rt(rt2x00dev, RT6352)) {
--		rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000401);
--		rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x000C0001);
--		rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000);
--		rt2800_register_write(rt2x00dev, TX_ALC_VGA3, 0x00000000);
--		rt2800_register_write(rt2x00dev, TX0_BB_GAIN_ATTEN, 0x0);
--		rt2800_register_write(rt2x00dev, TX1_BB_GAIN_ATTEN, 0x0);
--		rt2800_register_write(rt2x00dev, TX0_RF_GAIN_ATTEN, 0x6C6C666C);
--		rt2800_register_write(rt2x00dev, TX1_RF_GAIN_ATTEN, 0x6C6C666C);
--		rt2800_register_write(rt2x00dev, TX0_RF_GAIN_CORRECT,
--				      0x3630363A);
--		rt2800_register_write(rt2x00dev, TX1_RF_GAIN_CORRECT,
--				      0x3630363A);
-+		if (rt2800_hw_get_chipver(rt2x00dev) <= 1) {
-+			rt2800_register_write(rt2x00dev, TX_ALC_VGA3,
-+					      0x00000000);
-+			rt2800_register_write(rt2x00dev, BB_PA_MODE_CFG0,
-+					      0x000055FF);
-+			rt2800_register_write(rt2x00dev, BB_PA_MODE_CFG1,
-+					      0x00550055);
-+			rt2800_register_write(rt2x00dev, RF_PA_MODE_CFG0,
-+					      0x000055FF);
-+			rt2800_register_write(rt2x00dev, RF_PA_MODE_CFG1,
-+					      0x00550055);
-+		} else {
-+			rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000401);
-+			rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x000C0001);
-+			rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000);
-+			rt2800_register_write(rt2x00dev, TX_ALC_VGA3, 0x00000000);
-+			rt2800_register_write(rt2x00dev, TX0_BB_GAIN_ATTEN, 0x0);
-+			rt2800_register_write(rt2x00dev, TX1_BB_GAIN_ATTEN, 0x0);
-+			rt2800_register_write(rt2x00dev, TX0_RF_GAIN_ATTEN,
-+					      0x6C6C666C);
-+			rt2800_register_write(rt2x00dev, TX1_RF_GAIN_ATTEN,
-+					      0x6C6C666C);
-+			rt2800_register_write(rt2x00dev, TX0_RF_GAIN_CORRECT,
-+					      0x3630363A);
-+			rt2800_register_write(rt2x00dev, TX1_RF_GAIN_CORRECT,
-+					      0x3630363A);
-+		}
- 		reg = rt2800_register_read(rt2x00dev, TX_ALC_CFG_1);
- 		rt2x00_set_field32(&reg, TX_ALC_CFG_1_ROS_BUSY_EN, 0);
- 		rt2800_register_write(rt2x00dev, TX_ALC_CFG_1, reg);
-@@ -7122,14 +7168,16 @@ static void rt2800_init_bbp_6352(struct rt2x00_dev *rt2x00dev)
- 	rt2800_bbp_write(rt2x00dev, 188, 0x00);
- 	rt2800_bbp_write(rt2x00dev, 189, 0x00);
- 
--	rt2800_bbp_write(rt2x00dev, 91, 0x06);
--	rt2800_bbp_write(rt2x00dev, 92, 0x04);
--	rt2800_bbp_write(rt2x00dev, 93, 0x54);
--	rt2800_bbp_write(rt2x00dev, 99, 0x50);
--	rt2800_bbp_write(rt2x00dev, 148, 0x84);
--	rt2800_bbp_write(rt2x00dev, 167, 0x80);
--	rt2800_bbp_write(rt2x00dev, 178, 0xFF);
--	rt2800_bbp_write(rt2x00dev, 106, 0x13);
-+	if (rt2800_hw_get_chipver(rt2x00dev) > 1) {
-+		rt2800_bbp_write(rt2x00dev, 91, 0x06);
-+		rt2800_bbp_write(rt2x00dev, 92, 0x04);
-+		rt2800_bbp_write(rt2x00dev, 93, 0x54);
-+		rt2800_bbp_write(rt2x00dev, 99, 0x50);
-+		rt2800_bbp_write(rt2x00dev, 148, 0x84);
-+		rt2800_bbp_write(rt2x00dev, 167, 0x80);
-+		rt2800_bbp_write(rt2x00dev, 178, 0xFF);
-+		rt2800_bbp_write(rt2x00dev, 106, 0x13);
-+	}
- 
- 	/* BBP for G band GLRT function (BBP_128 ~ BBP_221) */
- 	rt2800_bbp_glrt_write(rt2x00dev, 0, 0x00);
-@@ -10359,6 +10407,9 @@ static void rt2800_restore_rf_bbp_rt6352(struct rt2x00_dev *rt2x00dev)
- 		rt2800_register_write(rt2x00dev, RF_BYPASS3, 0x0);
- 	}
- 
-+	if (rt2800_hw_get_chippkg(rt2x00dev) != 1)
-+		return;
-+
- 	if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
- 		rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x16);
- 		rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x23);
-@@ -10403,8 +10454,10 @@ static void rt2800_calibration_rt6352(struct rt2x00_dev *rt2x00dev)
- 	u32 reg;
- 
- 	if (rt2x00_has_cap_external_pa(rt2x00dev) ||
--	    rt2x00_has_cap_external_lna_bg(rt2x00dev))
-+	    rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
-+		rt6352_enable_pa_pin(rt2x00dev, 0);
- 		rt2800_restore_rf_bbp_rt6352(rt2x00dev);
-+	}
- 
- 	rt2800_r_calibration(rt2x00dev);
- 	rt2800_rf_self_txdc_cal(rt2x00dev);
-@@ -10422,6 +10475,8 @@ static void rt2800_calibration_rt6352(struct rt2x00_dev *rt2x00dev)
- 	    !rt2x00_has_cap_external_lna_bg(rt2x00dev))
- 		return;
- 
-+	rt6352_enable_pa_pin(rt2x00dev, 1);
-+
- 	if (rt2x00_has_cap_external_pa(rt2x00dev)) {
- 		reg = rt2800_register_read(rt2x00dev, RF_CONTROL3);
- 		reg |= 0x00000101;
-@@ -10432,6 +10487,9 @@ static void rt2800_calibration_rt6352(struct rt2x00_dev *rt2x00dev)
- 		rt2800_register_write(rt2x00dev, RF_BYPASS3, reg);
- 	}
- 
-+	if (rt2800_hw_get_chippkg(rt2x00dev) != 1)
-+		return;
-+
- 	if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
- 		rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x66);
- 		rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x20);
-@@ -10522,31 +10580,36 @@ static void rt2800_init_rfcsr_6352(struct rt2x00_dev *rt2x00dev)
- 	rt2800_rfcsr_write(rt2x00dev, 42, 0x5B);
- 	rt2800_rfcsr_write(rt2x00dev, 43, 0x00);
- 
--	rt2800_rfcsr_write(rt2x00dev, 11, 0x21);
--	if (rt2800_clk_is_20mhz(rt2x00dev))
--		rt2800_rfcsr_write(rt2x00dev, 13, 0x03);
--	else
--		rt2800_rfcsr_write(rt2x00dev, 13, 0x00);
--	rt2800_rfcsr_write(rt2x00dev, 14, 0x7C);
--	rt2800_rfcsr_write(rt2x00dev, 16, 0x80);
--	rt2800_rfcsr_write(rt2x00dev, 17, 0x99);
--	rt2800_rfcsr_write(rt2x00dev, 18, 0x99);
--	rt2800_rfcsr_write(rt2x00dev, 19, 0x09);
--	rt2800_rfcsr_write(rt2x00dev, 20, 0x50);
--	rt2800_rfcsr_write(rt2x00dev, 21, 0xB0);
--	rt2800_rfcsr_write(rt2x00dev, 22, 0x00);
--	rt2800_rfcsr_write(rt2x00dev, 23, 0x06);
--	rt2800_rfcsr_write(rt2x00dev, 24, 0x00);
--	rt2800_rfcsr_write(rt2x00dev, 25, 0x00);
--	rt2800_rfcsr_write(rt2x00dev, 26, 0x5D);
--	rt2800_rfcsr_write(rt2x00dev, 27, 0x00);
--	rt2800_rfcsr_write(rt2x00dev, 28, 0x61);
--	rt2800_rfcsr_write(rt2x00dev, 29, 0xB5);
--	rt2800_rfcsr_write(rt2x00dev, 43, 0x02);
-+	if (rt2800_hw_get_chipver(rt2x00dev) > 1) {
-+		rt2800_rfcsr_write(rt2x00dev, 11, 0x21);
-+		if (rt2800_clk_is_20mhz(rt2x00dev))
-+			rt2800_rfcsr_write(rt2x00dev, 13, 0x03);
-+		else
-+			rt2800_rfcsr_write(rt2x00dev, 13, 0x00);
-+		rt2800_rfcsr_write(rt2x00dev, 14, 0x7C);
-+		rt2800_rfcsr_write(rt2x00dev, 16, 0x80);
-+		rt2800_rfcsr_write(rt2x00dev, 17, 0x99);
-+		rt2800_rfcsr_write(rt2x00dev, 18, 0x99);
-+		rt2800_rfcsr_write(rt2x00dev, 19, 0x09);
-+		rt2800_rfcsr_write(rt2x00dev, 20, 0x50);
-+		rt2800_rfcsr_write(rt2x00dev, 21, 0xB0);
-+		rt2800_rfcsr_write(rt2x00dev, 22, 0x00);
-+		rt2800_rfcsr_write(rt2x00dev, 23, 0x06);
-+		rt2800_rfcsr_write(rt2x00dev, 24, 0x00);
-+		rt2800_rfcsr_write(rt2x00dev, 25, 0x00);
-+		rt2800_rfcsr_write(rt2x00dev, 26, 0x5D);
-+		rt2800_rfcsr_write(rt2x00dev, 27, 0x00);
-+		rt2800_rfcsr_write(rt2x00dev, 28, 0x61);
-+		rt2800_rfcsr_write(rt2x00dev, 29, 0xB5);
-+		rt2800_rfcsr_write(rt2x00dev, 43, 0x02);
-+	}
- 
--	rt2800_rfcsr_write(rt2x00dev, 28, 0x62);
--	rt2800_rfcsr_write(rt2x00dev, 29, 0xAD);
--	rt2800_rfcsr_write(rt2x00dev, 39, 0x80);
-+	if (rt2800_hw_get_chipver(rt2x00dev) > 1 &&
-+	    rt2800_hw_get_chipeco(rt2x00dev) >= 2) {
-+		rt2800_rfcsr_write(rt2x00dev, 28, 0x62);
-+		rt2800_rfcsr_write(rt2x00dev, 29, 0xAD);
-+		rt2800_rfcsr_write(rt2x00dev, 39, 0x80);
-+	}
- 
- 	/* Initialize RF channel register to default value */
- 	rt2800_rfcsr_write_chanreg(rt2x00dev, 0, 0x03);
-@@ -10612,63 +10675,71 @@ static void rt2800_init_rfcsr_6352(struct rt2x00_dev *rt2x00dev)
- 
- 	rt2800_rfcsr_write_bank(rt2x00dev, 6, 45, 0xC5);
- 
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 9, 0x47);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 10, 0x71);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 11, 0x33);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x0E);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x23);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 19, 0xA4);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 20, 0x02);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 21, 0x12);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 28, 0x1C);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 29, 0xEB);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 32, 0x7D);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 34, 0xD6);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 36, 0x08);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 38, 0xB4);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 43, 0xD3);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 44, 0xB3);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 45, 0xD5);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 46, 0x27);
--	rt2800_rfcsr_write_bank(rt2x00dev, 4, 47, 0x67);
--	rt2800_rfcsr_write_bank(rt2x00dev, 6, 47, 0x69);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 48, 0xFF);
--	rt2800_rfcsr_write_bank(rt2x00dev, 4, 54, 0x27);
--	rt2800_rfcsr_write_bank(rt2x00dev, 6, 54, 0x20);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x66);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 56, 0xFF);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 57, 0x1C);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 58, 0x20);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 59, 0x6B);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 60, 0xF7);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 61, 0x09);
--
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 10, 0x51);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x06);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 19, 0xA7);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 28, 0x2C);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x64);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 8, 0x51);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 9, 0x36);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 11, 0x53);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x16);
--
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 47, 0x6C);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 48, 0xFC);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 49, 0x1F);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 54, 0x27);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x66);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 59, 0x6B);
--
--	/* Initialize RF channel register for DRQFN */
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 43, 0xD3);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 44, 0xE3);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 45, 0xE5);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 47, 0x28);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x68);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 56, 0xF7);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 58, 0x02);
--	rt2800_rfcsr_write_chanreg(rt2x00dev, 60, 0xC7);
-+	if (rt2800_hw_get_chipver(rt2x00dev) > 1) {
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 9, 0x47);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 10, 0x71);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 11, 0x33);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x0E);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x23);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 19, 0xA4);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 20, 0x02);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 21, 0x12);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 28, 0x1C);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 29, 0xEB);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 32, 0x7D);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 34, 0xD6);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 36, 0x08);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 38, 0xB4);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 43, 0xD3);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 44, 0xB3);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 45, 0xD5);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 46, 0x27);
-+		rt2800_rfcsr_write_bank(rt2x00dev, 4, 47, 0x67);
-+		rt2800_rfcsr_write_bank(rt2x00dev, 6, 47, 0x69);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 48, 0xFF);
-+		rt2800_rfcsr_write_bank(rt2x00dev, 4, 54, 0x27);
-+		rt2800_rfcsr_write_bank(rt2x00dev, 6, 54, 0x20);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x66);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 56, 0xFF);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 57, 0x1C);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 58, 0x20);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 59, 0x6B);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 60, 0xF7);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 61, 0x09);
-+	}
-+
-+	if (rt2800_hw_get_chipver(rt2x00dev) > 1 &&
-+	    rt2800_hw_get_chipeco(rt2x00dev) >= 2) {
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 10, 0x51);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x06);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 19, 0xA7);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 28, 0x2C);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x64);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 8, 0x51);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 9, 0x36);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 11, 0x53);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x16);
-+
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 47, 0x6C);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 48, 0xFC);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 49, 0x1F);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 54, 0x27);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x66);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 59, 0x6B);
-+	}
-+
-+	if (rt2800_hw_get_chippkg(rt2x00dev) == 0 &&
-+	    rt2800_hw_get_chipver(rt2x00dev) == 1) {
-+		/* Initialize RF channel register for DRQFN */
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 43, 0xD3);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 44, 0xE3);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 45, 0xE5);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 47, 0x28);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x68);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 56, 0xF7);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 58, 0x02);
-+		rt2800_rfcsr_write_chanreg(rt2x00dev, 60, 0xC7);
-+	}
- 
- 	/* Initialize RF DC calibration register to default value */
- 	rt2800_rfcsr_write_dccal(rt2x00dev, 0, 0x47);
-@@ -10731,12 +10802,17 @@ static void rt2800_init_rfcsr_6352(struct rt2x00_dev *rt2x00dev)
- 	rt2800_rfcsr_write_dccal(rt2x00dev, 62, 0x00);
- 	rt2800_rfcsr_write_dccal(rt2x00dev, 63, 0x00);
- 
--	rt2800_rfcsr_write_dccal(rt2x00dev, 3, 0x08);
--	rt2800_rfcsr_write_dccal(rt2x00dev, 4, 0x04);
--	rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x20);
-+	if (rt2800_hw_get_chipver(rt2x00dev) > 1) {
-+		rt2800_rfcsr_write_dccal(rt2x00dev, 3, 0x08);
-+		rt2800_rfcsr_write_dccal(rt2x00dev, 4, 0x04);
-+		rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x20);
-+	}
- 
--	rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x00);
--	rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x7C);
-+	if (rt2800_hw_get_chipver(rt2x00dev) > 1 &&
-+	    rt2800_hw_get_chipeco(rt2x00dev) >= 2) {
-+		rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x00);
-+		rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x7C);
-+	}
- 
- 	/* Do calibration and init PA/LNA */
- 	rt2800_calibration_rt6352(rt2x00dev);
-@@ -11282,6 +11358,17 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
- 	rt2800_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC);
- 	rt2800_init_led(rt2x00dev, &rt2x00dev->led_qual, LED_TYPE_QUALITY);
- 
-+	{
-+		struct device_node *np = rt2x00dev->dev->of_node;
-+		unsigned int led_polarity;
-+
-+		/* Allow overriding polarity from OF */
-+		if (!of_property_read_u32(np, "ralink,led-polarity",
-+					  &led_polarity))
-+			rt2x00_set_field16(&eeprom, EEPROM_FREQ_LED_POLARITY,
-+					   led_polarity);
-+	}
-+
- 	rt2x00dev->led_mcu_reg = eeprom;
- #endif /* CPTCFG_RT2X00_LIB_LEDS */
- 
-diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
-index 194de67..a18140c 100644
---- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
-@@ -76,6 +76,9 @@ struct rt2800_ops {
- 	int (*drv_init_registers)(struct rt2x00_dev *rt2x00dev);
- 	__le32 *(*drv_get_txwi)(struct queue_entry *entry);
- 	unsigned int (*drv_get_dma_done)(struct data_queue *queue);
-+	int (*hw_get_chippkg)(void);
-+	int (*hw_get_chipver)(void);
-+	int (*hw_get_chipeco)(void);
- };
- 
- static inline u32 rt2800_register_read(struct rt2x00_dev *rt2x00dev,
-@@ -184,6 +187,27 @@ static inline unsigned int rt2800_drv_get_dma_done(struct data_queue *queue)
- 	return rt2800ops->drv_get_dma_done(queue);
- }
- 
-+static inline int rt2800_hw_get_chippkg(struct rt2x00_dev *rt2x00dev)
-+{
-+	const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv;
-+
-+	return rt2800ops->hw_get_chippkg();
-+}
-+
-+static inline int rt2800_hw_get_chipver(struct rt2x00_dev *rt2x00dev)
-+{
-+	const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv;
-+
-+	return rt2800ops->hw_get_chipver();
-+}
-+
-+static inline int rt2800_hw_get_chipeco(struct rt2x00_dev *rt2x00dev)
-+{
-+	const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv;
-+
-+	return rt2800ops->hw_get_chipeco();
-+}
-+
- void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev,
- 			const u8 command, const u8 token,
- 			const u8 arg0, const u8 arg1);
-diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
-index c891043..b041952 100644
---- a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
-@@ -286,6 +286,10 @@ static int rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev)
- 	return retval;
- }
- 
-+static int rt2800pci_get_chippkg(void) { return 0; }
-+static int rt2800pci_get_chipver(void) { return 0; }
-+static int rt2800pci_get_chipeco(void) { return 0; }
-+
- static const struct ieee80211_ops rt2800pci_mac80211_ops = {
- 	.add_chanctx = ieee80211_emulate_add_chanctx,
- 	.remove_chanctx = ieee80211_emulate_remove_chanctx,
-@@ -333,6 +337,9 @@ static const struct rt2800_ops rt2800pci_rt2800_ops = {
- 	.drv_init_registers	= rt2800mmio_init_registers,
- 	.drv_get_txwi		= rt2800mmio_get_txwi,
- 	.drv_get_dma_done	= rt2800mmio_get_dma_done,
-+	.hw_get_chippkg		= rt2800pci_get_chippkg,
-+	.hw_get_chipver		= rt2800pci_get_chipver,
-+	.hw_get_chipeco		= rt2800pci_get_chipeco,
- };
- 
- static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
-diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
-index 787dbf0..e0d7893 100644
---- a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
-@@ -27,6 +27,12 @@
- #include "rt2800lib.h"
- #include "rt2800mmio.h"
- 
-+/* Needed to probe CHIP_VER register on MT7620 */
-+#ifdef CONFIG_SOC_MT7620
-+#include <asm/mach-ralink/ralink_regs.h>
-+#include <asm/mach-ralink/mt7620.h>
-+#endif
-+
- /* Allow hardware encryption to be disabled. */
- static bool modparam_nohwcrypt;
- module_param_named(nohwcrypt, modparam_nohwcrypt, bool, 0444);
-@@ -90,19 +96,6 @@ static int rt2800soc_set_device_state(struct rt2x00_dev *rt2x00dev,
- 	return retval;
- }
- 
--static int rt2800soc_read_eeprom(struct rt2x00_dev *rt2x00dev)
--{
--	void __iomem *base_addr = ioremap(0x1F040000, EEPROM_SIZE);
--
--	if (!base_addr)
--		return -ENOMEM;
--
--	memcpy_fromio(rt2x00dev->eeprom, base_addr, EEPROM_SIZE);
--
--	iounmap(base_addr);
--	return 0;
--}
--
- /* Firmware functions */
- static char *rt2800soc_get_firmware_name(struct rt2x00_dev *rt2x00dev)
- {
-@@ -131,6 +124,27 @@ static int rt2800soc_write_firmware(struct rt2x00_dev *rt2x00dev,
- 	return 0;
- }
- 
-+#ifdef CONFIG_SOC_MT7620
-+static int rt2800soc_get_chippkg(void)
-+{
-+	return mt7620_get_pkg();
-+}
-+
-+static int rt2800soc_get_chipver(void)
-+{
-+	return mt7620_get_chipver();
-+}
-+
-+static int rt2800soc_get_chipeco(void)
-+{
-+	return mt7620_get_eco();
-+}
-+#else
-+static int rt2800soc_get_chippkg(void) { return 0; }
-+static int rt2800soc_get_chipver(void) { return 0; }
-+static int rt2800soc_get_chipeco(void) { return 0; }
-+#endif
-+
- static const struct ieee80211_ops rt2800soc_mac80211_ops = {
- 	.add_chanctx = ieee80211_emulate_add_chanctx,
- 	.remove_chanctx = ieee80211_emulate_remove_chanctx,
-@@ -172,12 +186,15 @@ static const struct rt2800_ops rt2800soc_rt2800_ops = {
- 	.register_multiread	= rt2x00mmio_register_multiread,
- 	.register_multiwrite	= rt2x00mmio_register_multiwrite,
- 	.regbusy_read		= rt2x00mmio_regbusy_read,
--	.read_eeprom		= rt2800soc_read_eeprom,
-+	.read_eeprom		= rt2x00lib_read_eeprom,
- 	.hwcrypt_disabled	= rt2800soc_hwcrypt_disabled,
- 	.drv_write_firmware	= rt2800soc_write_firmware,
- 	.drv_init_registers	= rt2800mmio_init_registers,
- 	.drv_get_txwi		= rt2800mmio_get_txwi,
- 	.drv_get_dma_done	= rt2800mmio_get_dma_done,
-+	.hw_get_chippkg		= rt2800soc_get_chippkg,
-+	.hw_get_chipver		= rt2800soc_get_chipver,
-+	.hw_get_chipeco		= rt2800soc_get_chipeco,
- };
- 
- static const struct rt2x00lib_ops rt2800soc_rt2x00_ops = {
-@@ -243,10 +260,17 @@ static int rt2800soc_probe(struct platform_device *pdev)
- 	return rt2x00soc_probe(pdev, &rt2800soc_ops);
- }
- 
-+static const struct of_device_id rt2880_wmac_match[] = {
-+	{ .compatible = "ralink,rt2880-wmac" },
-+	{},
-+};
-+MODULE_DEVICE_TABLE(of, rt2880_wmac_match);
-+
- static struct platform_driver rt2800soc_driver = {
- 	.driver		= {
- 		.name		= "rt2800_wmac",
- 		.mod_name	= KBUILD_MODNAME,
-+		.of_match_table	= rt2880_wmac_match,
- 	},
- 	.probe		= rt2800soc_probe,
- 	.remove		= rt2x00soc_remove,
-diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
-index a37f8ea..2663447 100644
---- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
-@@ -628,6 +628,10 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
- 	return 0;
- }
- 
-+static int rt2800usb_get_chippkg(void) { return 0; }
-+static int rt2800usb_get_chipver(void) { return 0; }
-+static int rt2800usb_get_chipeco(void) { return 0; }
-+
- static const struct ieee80211_ops rt2800usb_mac80211_ops = {
- 	.add_chanctx = ieee80211_emulate_add_chanctx,
- 	.remove_chanctx = ieee80211_emulate_remove_chanctx,
-@@ -676,6 +680,9 @@ static const struct rt2800_ops rt2800usb_rt2800_ops = {
- 	.drv_init_registers	= rt2800usb_init_registers,
- 	.drv_get_txwi		= rt2800usb_get_txwi,
- 	.drv_get_dma_done	= rt2800usb_get_dma_done,
-+	.hw_get_chippkg		= rt2800usb_get_chippkg,
-+	.hw_get_chipver		= rt2800usb_get_chipver,
-+	.hw_get_chipeco		= rt2800usb_get_chipeco,
- };
- 
- static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
-diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
-index 0ac4ae9..27d283b 100644
---- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
-@@ -28,6 +28,8 @@
- #include <linux/average.h>
- #include <linux/usb.h>
- #include <linux/clk.h>
-+#include <linux/pinctrl/consumer.h>
-+#include <linux/rt2x00_platform.h>
- 
- #include <net/mac80211.h>
- 
-@@ -407,6 +409,7 @@ struct hw_mode_spec {
- 	unsigned int supported_bands;
- #define SUPPORT_BAND_2GHZ	0x00000001
- #define SUPPORT_BAND_5GHZ	0x00000002
-+#define SUPPORT_BAND_BOTH	(SUPPORT_BAND_2GHZ | SUPPORT_BAND_5GHZ)
- 
- 	unsigned int supported_rates;
- #define SUPPORT_RATE_CCK	0x00000001
-@@ -702,6 +705,7 @@ enum rt2x00_capability_flags {
- 	REQUIRE_HT_TX_DESC,
- 	REQUIRE_PS_AUTOWAKE,
- 	REQUIRE_DELAYED_RFKILL,
-+	REQUIRE_EEPROM_FILE,
- 
- 	/*
- 	 * Capabilities
-@@ -1024,6 +1028,11 @@ struct rt2x00_dev {
- 
- 	/* Clock for System On Chip devices. */
- 	struct clk *clk;
-+
-+	/* pinctrl and states for System On Chip devices with PA/LNA. */
-+	struct pinctrl *pinctrl;
-+	struct pinctrl_state *pins_default;
-+	struct pinctrl_state *pins_pa_gpio;
- };
- 
- struct rt2x00_bar_list_entry {
-@@ -1271,6 +1280,12 @@ rt2x00_has_cap_external_pa(struct rt2x00_dev *rt2x00dev)
- 	return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_PA_TX0);
- }
- 
-+static inline bool
-+rt2x00_has_cap_external_pa(struct rt2x00_dev *rt2x00dev)
-+{
-+	return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_PA_TX0);
-+}
-+
- static inline bool
- rt2x00_has_cap_double_antenna(struct rt2x00_dev *rt2x00dev)
- {
-diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
-index 274524e..69f8d5a 100644
---- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
-@@ -990,6 +990,12 @@ static void rt2x00lib_rate(struct ieee80211_rate *entry,
- 
- void rt2x00lib_set_mac_address(struct rt2x00_dev *rt2x00dev, u8 *eeprom_mac_addr)
- {
-+	struct rt2x00_platform_data *pdata;
-+
-+	pdata = rt2x00dev->dev->platform_data;
-+	if (pdata && pdata->mac_address)
-+		ether_addr_copy(eeprom_mac_addr, pdata->mac_address);
-+
- 	of_get_mac_address(rt2x00dev->dev->of_node, eeprom_mac_addr);
- 
- 	if (!is_valid_ether_addr(eeprom_mac_addr)) {
-@@ -1007,6 +1013,32 @@ static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev,
- 	struct ieee80211_rate *rates;
- 	unsigned int num_rates;
- 	unsigned int i;
-+#ifdef CONFIG_OF
-+	struct device_node *np = rt2x00dev->dev->of_node;
-+	unsigned int enabled;
-+	if (!of_property_read_u32(np, "ralink,2ghz",
-+                                          &enabled) && !enabled)
-+		spec->supported_bands &= ~SUPPORT_BAND_2GHZ;
-+	if (!of_property_read_u32(np, "ralink,5ghz",
-+                                          &enabled) && !enabled)
-+		spec->supported_bands &= ~SUPPORT_BAND_5GHZ;
-+#endif /* CONFIG_OF */
-+
-+	if (rt2x00dev->dev->platform_data) {
-+		struct rt2x00_platform_data *pdata;
-+
-+		pdata = rt2x00dev->dev->platform_data;
-+		if (pdata->disable_2ghz)
-+			spec->supported_bands &= ~SUPPORT_BAND_2GHZ;
-+		if (pdata->disable_5ghz)
-+			spec->supported_bands &= ~SUPPORT_BAND_5GHZ;
-+	}
-+
-+	if ((spec->supported_bands & SUPPORT_BAND_BOTH) == 0) {
-+		rt2x00_err(rt2x00dev, "No supported bands\n");
-+		return -EINVAL;
-+	}
-+
- 
- 	num_rates = 0;
- 	if (spec->supported_rates & SUPPORT_RATE_CCK)
-@@ -1330,7 +1362,7 @@ static inline void rt2x00lib_set_if_combinations(struct rt2x00_dev *rt2x00dev)
- 	 */
- 	if_limit = &rt2x00dev->if_limits_ap;
- 	if_limit->max = rt2x00dev->ops->max_ap_intf;
--	if_limit->types = BIT(NL80211_IFTYPE_AP);
-+	if_limit->types = BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_STATION);
- #ifdef CPTCFG_MAC80211_MESH
- 	if_limit->types |= BIT(NL80211_IFTYPE_MESH_POINT);
- #endif
-diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00eeprom.c b/drivers/net/wireless/ralink/rt2x00/rt2x00eeprom.c
-new file mode 100644
-index 0000000..15c4e0b
---- /dev/null
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00eeprom.c
-@@ -0,0 +1,208 @@
-+// SPDX-License-Identifier: GPL-2.0-or-later
-+/*	Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
-+ *	Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com>
-+ *	<http://rt2x00.serialmonkey.com>
-+ */
-+
-+/*	Module: rt2x00lib
-+ *	Abstract: rt2x00 eeprom file loading routines.
-+ */
-+
-+#include <linux/kernel.h>
-+#include <linux/module.h>
-+#if IS_ENABLED(CONFIG_MTD)
-+#include <linux/mtd/mtd.h>
-+#include <linux/mtd/partitions.h>
-+#endif
-+#include <linux/nvmem-consumer.h>
-+#include <linux/of.h>
-+
-+#include "rt2x00.h"
-+#include "rt2x00soc.h"
-+
-+static void rt2800lib_eeprom_swap(struct rt2x00_dev *rt2x00dev)
-+{
-+	struct device_node *np = rt2x00dev->dev->of_node;
-+	size_t len = rt2x00dev->ops->eeprom_size;
-+	int i;
-+
-+	if (!of_find_property(np, "ralink,eeprom-swap", NULL))
-+		return;
-+
-+	for (i = 0; i < len / sizeof(u16); i++)
-+		rt2x00dev->eeprom[i] = swab16(rt2x00dev->eeprom[i]);
-+}
-+
-+#if IS_ENABLED(CONFIG_MTD)
-+static int rt2800lib_read_eeprom_mtd(struct rt2x00_dev *rt2x00dev)
-+{
-+	int ret = -EINVAL;
-+#ifdef CONFIG_OF
-+	struct device_node *np = rt2x00dev->dev->of_node, *mtd_np = NULL;
-+	int size, offset = 0;
-+	struct mtd_info *mtd;
-+	const char *part;
-+	const __be32 *list;
-+	phandle phandle;
-+	size_t retlen;
-+
-+	list = of_get_property(np, "ralink,mtd-eeprom", &size);
-+	if (!list)
-+		return -ENOENT;
-+
-+	phandle = be32_to_cpup(list++);
-+	if (phandle)
-+		mtd_np = of_find_node_by_phandle(phandle);
-+	if (!mtd_np) {
-+		dev_err(rt2x00dev->dev, "failed to load mtd phandle\n");
-+		return -EINVAL;
-+	}
-+
-+	part = of_get_property(mtd_np, "label", NULL);
-+	if (!part)
-+		part = mtd_np->name;
-+
-+	mtd = get_mtd_device_nm(part);
-+	if (IS_ERR(mtd)) {
-+		dev_err(rt2x00dev->dev, "failed to get mtd device \"%s\"\n", part);
-+		return PTR_ERR(mtd);
-+	}
-+
-+	if (size > sizeof(*list))
-+		offset = be32_to_cpup(list);
-+
-+	ret = mtd_read(mtd, offset, rt2x00dev->ops->eeprom_size,
-+		       &retlen, (u_char *)rt2x00dev->eeprom);
-+	put_mtd_device(mtd);
-+
-+	if (retlen != rt2x00dev->ops->eeprom_size || ret) {
-+		dev_err(rt2x00dev->dev, "failed to load eeprom from device \"%s\"\n", part);
-+		return ret;
-+	}
-+
-+	rt2800lib_eeprom_swap(rt2x00dev);
-+
-+	dev_info(rt2x00dev->dev, "loaded eeprom from mtd device \"%s\"\n", part);
-+#endif
-+
-+	return ret;
-+}
-+#endif
-+
-+static int rt2800lib_read_eeprom_nvmem(struct rt2x00_dev *rt2x00dev)
-+{
-+	struct device_node *np = rt2x00dev->dev->of_node;
-+	unsigned int len = rt2x00dev->ops->eeprom_size;
-+	struct nvmem_cell *cell;
-+	const void *data;
-+	size_t retlen;
-+	int ret = 0;
-+
-+	cell = of_nvmem_cell_get(np, "eeprom");
-+	if (IS_ERR(cell))
-+		return PTR_ERR(cell);
-+
-+	data = nvmem_cell_read(cell, &retlen);
-+	nvmem_cell_put(cell);
-+
-+	if (IS_ERR(data))
-+		return PTR_ERR(data);
-+
-+	if (retlen != len) {
-+		dev_err(rt2x00dev->dev, "invalid eeprom size, required: 0x%04x\n", len);
-+		ret = -EINVAL;
-+		goto exit;
-+	}
-+
-+	memcpy(rt2x00dev->eeprom, data, len);
-+
-+	rt2800lib_eeprom_swap(rt2x00dev);
-+
-+exit:
-+	kfree(data);
-+	return ret;
-+}
-+
-+static const char *
-+rt2x00lib_get_eeprom_file_name(struct rt2x00_dev *rt2x00dev)
-+{
-+	struct rt2x00_platform_data *pdata = rt2x00dev->dev->platform_data;
-+#ifdef CONFIG_OF
-+	struct device_node *np;
-+	const char *eep;
-+#endif
-+
-+	if (pdata && pdata->eeprom_file_name)
-+		return pdata->eeprom_file_name;
-+
-+#ifdef CONFIG_OF
-+	np = rt2x00dev->dev->of_node;
-+	if (np && !of_property_read_string(np, "ralink,eeprom", &eep))
-+		return eep;
-+#endif
-+
-+	return NULL;
-+}
-+
-+static int rt2x00lib_read_eeprom_file(struct rt2x00_dev *rt2x00dev)
-+{
-+	const struct firmware *ee;
-+	const char *ee_name;
-+	int retval;
-+
-+	ee_name = rt2x00lib_get_eeprom_file_name(rt2x00dev);
-+	if (!ee_name && test_bit(REQUIRE_EEPROM_FILE, &rt2x00dev->cap_flags)) {
-+		rt2x00_err(rt2x00dev, "Required EEPROM name is missing.");
-+		return -EINVAL;
-+	}
-+
-+	if (!ee_name)
-+		return 0;
-+
-+	rt2x00_info(rt2x00dev, "Loading EEPROM data from '%s'.\n", ee_name);
-+
-+	retval = request_firmware(&ee, ee_name, rt2x00dev->dev);
-+	if (retval) {
-+		rt2x00_err(rt2x00dev, "Failed to request EEPROM.\n");
-+		return retval;
-+	}
-+
-+	if (!ee || !ee->size || !ee->data) {
-+		rt2x00_err(rt2x00dev, "Failed to read EEPROM file.\n");
-+		retval = -ENOENT;
-+		goto err_exit;
-+	}
-+
-+	if (ee->size != rt2x00dev->ops->eeprom_size) {
-+		rt2x00_err(rt2x00dev,
-+			   "EEPROM file size is invalid, it should be %d bytes\n",
-+			   rt2x00dev->ops->eeprom_size);
-+		retval = -EINVAL;
-+		goto err_release_ee;
-+	}
-+
-+	memcpy(rt2x00dev->eeprom, ee->data, rt2x00dev->ops->eeprom_size);
-+
-+err_release_ee:
-+	release_firmware(ee);
-+err_exit:
-+	return retval;
-+}
-+
-+int rt2x00lib_read_eeprom(struct rt2x00_dev *rt2x00dev)
-+{
-+	int ret;
-+
-+#if IS_ENABLED(CONFIG_MTD)
-+	ret = rt2800lib_read_eeprom_mtd(rt2x00dev);
-+	if (!ret)
-+		return 0;
-+#endif
-+
-+	ret = rt2800lib_read_eeprom_nvmem(rt2x00dev);
-+	if (!ret)
-+		return 0;
-+
-+	return rt2x00lib_read_eeprom_file(rt2x00dev);
-+}
-+EXPORT_SYMBOL_GPL(rt2x00lib_read_eeprom);
-diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00leds.c b/drivers/net/wireless/ralink/rt2x00/rt2x00leds.c
-index f5361d5..bad5ce2 100644
---- a/drivers/net/wireless/ralink/rt2x00/rt2x00leds.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00leds.c
-@@ -98,6 +98,9 @@ static int rt2x00leds_register_led(struct rt2x00_dev *rt2x00dev,
- 	led->led_dev.name = name;
- 	led->led_dev.brightness = LED_OFF;
- 
-+	if (rt2x00_is_soc(rt2x00dev))
-+		led->led_dev.brightness_set(&led->led_dev, LED_OFF);
-+
- 	retval = led_classdev_register(device, &led->led_dev);
- 	if (retval) {
- 		rt2x00_err(rt2x00dev, "Failed to register led handler\n");
-diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c
-index eface61..541b718 100644
---- a/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c
-@@ -86,6 +86,7 @@ int rt2x00soc_probe(struct platform_device *pdev, const struct rt2x00_ops *ops)
- 	if (IS_ERR(rt2x00dev->clk))
- 		rt2x00dev->clk = NULL;
- 
-+	set_bit(REQUIRE_EEPROM_FILE, &rt2x00dev->cap_flags);
- 	rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC);
- 
- 	retval = rt2x00soc_alloc_reg(rt2x00dev);
-@@ -96,6 +97,21 @@ int rt2x00soc_probe(struct platform_device *pdev, const struct rt2x00_ops *ops)
- 	if (retval)
- 		goto exit_free_reg;
- 
-+	rt2x00dev->pinctrl = devm_pinctrl_get(&pdev->dev);
-+	if (IS_ERR(rt2x00dev->pinctrl)) {
-+		rt2x00dev->pinctrl = NULL;
-+		rt2x00dev->pins_default = NULL;
-+		rt2x00dev->pins_pa_gpio = NULL;
-+	} else {
-+		rt2x00dev->pins_default = pinctrl_lookup_state(rt2x00dev->pinctrl, "default");
-+		if (IS_ERR(rt2x00dev->pins_default))
-+			rt2x00dev->pins_default = NULL;
-+
-+		rt2x00dev->pins_pa_gpio = pinctrl_lookup_state(rt2x00dev->pinctrl, "pa_gpio");
-+		if (IS_ERR(rt2x00dev->pins_pa_gpio))
-+			rt2x00dev->pins_pa_gpio = NULL;
-+	}
-+
- 	return 0;
- 
- exit_free_reg:
-diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00soc.h b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.h
-index 021fd06..21cd951 100644
---- a/drivers/net/wireless/ralink/rt2x00/rt2x00soc.h
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.h
-@@ -26,4 +26,13 @@ int rt2x00soc_resume(struct platform_device *pdev);
- #define rt2x00soc_resume	NULL
- #endif /* CONFIG_PM */
- 
-+/*
-+ * EEPROM file handlers.
-+ */
-+#ifdef CPTCFG_RT2X00_LIB_EEPROM
-+int rt2x00lib_read_eeprom(struct rt2x00_dev *rt2x00dev);
-+#else
-+#define rt2x00lib_read_eeprom	NULL
-+#endif /* CPTCFG_RT2X00_LIB_EEPROM */
-+
- #endif /* RT2X00SOC_H */
-diff --git a/include/linux/rt2x00_platform.h b/include/linux/rt2x00_platform.h
-new file mode 100644
-index 0000000..e10377e
---- /dev/null
-+++ b/include/linux/rt2x00_platform.h
-@@ -0,0 +1,23 @@
-+/*
-+ * Platform data definition for the rt2x00 driver
-+ *
-+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License version 2 as published
-+ * by the Free Software Foundation.
-+ *
-+ */
-+
-+#ifndef _RT2X00_PLATFORM_H
-+#define _RT2X00_PLATFORM_H
-+
-+struct rt2x00_platform_data {
-+	char *eeprom_file_name;
-+	const u8 *mac_address;
-+
-+	int disable_2ghz;
-+	int disable_5ghz;
-+};
-+
-+#endif /* _RT2X00_PLATFORM_H */
-diff --git a/local-symbols b/local-symbols
-index 3d81ba5..beafad9 100644
---- a/local-symbols
-+++ b/local-symbols
-@@ -331,6 +331,7 @@ RT2X00_LIB_FIRMWARE=
- RT2X00_LIB_CRYPTO=
- RT2X00_LIB_LEDS=
- RT2X00_LIB_DEBUGFS=
-+RT2X00_LIB_EEPROM=
- RT2X00_DEBUG=
- WLAN_VENDOR_REALTEK=
- RTL8180=
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0007-backports-sync-openwrt-patches-subsys.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0007-backports-sync-openwrt-patches-subsys.patch
new file mode 100644
index 0000000..e274749
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0007-backports-sync-openwrt-patches-subsys.patch
@@ -0,0 +1,857 @@
+From 9e1ab5bbae5b4f2282ede448efb780d92f7187bb Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 17 Jul 2024 20:48:15 +0800
+Subject: [PATCH 07/89] backports: sync openwrt patches/subsys
+
+---
+ backport-include/linux/of_net.h    | 26 -----------
+ include/net/cfg80211.h             | 13 ++++++
+ include/net/mac80211.h             |  2 +-
+ net/mac80211/cfg.c                 |  8 +---
+ net/mac80211/debugfs.c             | 13 +++++-
+ net/mac80211/ieee80211_i.h         |  4 ++
+ net/mac80211/main.c                | 19 +-------
+ net/mac80211/rc80211_minstrel_ht.c | 75 +++++++-----------------------
+ net/mac80211/rc80211_minstrel_ht.h |  2 +-
+ net/mac80211/sta_info.c            | 29 +++++++++++-
+ net/mac80211/sta_info.h            |  3 +-
+ net/mac80211/status.c              |  5 +-
+ net/mac80211/tx.c                  | 63 +++++++++++++------------
+ net/wireless/ap.c                  |  6 +--
+ net/wireless/chan.c                | 45 ++++++++++++++++++
+ net/wireless/core.c                | 15 ------
+ net/wireless/core.h                |  2 +
+ net/wireless/mlme.c                |  7 +--
+ net/wireless/sysfs.c               | 27 +++++++++--
+ 19 files changed, 192 insertions(+), 172 deletions(-)
+ delete mode 100644 backport-include/linux/of_net.h
+
+diff --git a/backport-include/linux/of_net.h b/backport-include/linux/of_net.h
+deleted file mode 100644
+index 9b9276f..0000000
+--- a/backport-include/linux/of_net.h
++++ /dev/null
+@@ -1,26 +0,0 @@
+-#ifndef _BP_OF_NET_H
+-#define _BP_OF_NET_H
+-#include_next <linux/of_net.h>
+-#include <linux/version.h>
+-#include <linux/etherdevice.h>
+-
+-/* The behavior of of_get_mac_address() changed in kernel 5.2, it now
+- * returns an error code and not NULL in case of an error.
+- */
+-#if LINUX_VERSION_IS_LESS(5,13,0)
+-static inline int backport_of_get_mac_address(struct device_node *np, u8 *mac_out)
+-{
+-	const void *mac = of_get_mac_address(np);
+-
+-	if (!mac)
+-		return -ENODEV;
+-	if (IS_ERR(mac))
+-		return PTR_ERR(mac);
+-	ether_addr_copy(mac_out, mac);
+-	
+-	return 0;
+-}
+-#define of_get_mac_address LINUX_BACKPORT(of_get_mac_address)
+-#endif /* < 5.2 */
+-
+-#endif /* _BP_OF_NET_H */
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index ca7135d..850861b 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -192,6 +192,8 @@ enum ieee80211_channel_flags {
+  * @dfs_state: current state of this channel. Only relevant if radar is required
+  *	on this channel.
+  * @dfs_state_entered: timestamp (jiffies) when the dfs state was entered.
++ * @dfs_state_last_available: timestamp (jiffies) of the last time when the
++ *	channel was available.
+  * @dfs_cac_ms: DFS CAC time in milliseconds, this is valid for DFS channels.
+  * @psd: power spectral density (in dBm)
+  */
+@@ -209,6 +211,7 @@ struct ieee80211_channel {
+ 	int orig_mag, orig_mpwr;
+ 	enum nl80211_dfs_state dfs_state;
+ 	unsigned long dfs_state_entered;
++	unsigned long dfs_state_last_available;
+ 	unsigned int dfs_cac_ms;
+ 	s8 psd;
+ };
+@@ -3425,6 +3428,7 @@ enum wiphy_params_flags {
+ /* The per TXQ device queue limit in airtime */
+ #define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L	5000
+ #define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H	12000
++#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_BC	50000
+ 
+ /* The per interface airtime threshold to switch to lower queue limit */
+ #define IEEE80211_AQL_THRESHOLD			24000
+@@ -6527,6 +6531,15 @@ static inline bool cfg80211_channel_is_psc(struct ieee80211_channel *chan)
+ bool cfg80211_radio_chandef_valid(const struct wiphy_radio *radio,
+ 				  const struct cfg80211_chan_def *chandef);
+ 
++/**
++ * cfg80211_radio_chandef_valid - Check if the radio supports the chandef
++ *
++ * @radio: wiphy radio
++ * @chandef: chandef for current channel
++ */
++bool cfg80211_radio_chandef_valid(const struct wiphy_radio *radio,
++				  const struct cfg80211_chan_def *chandef);
++
+ /**
+  * ieee80211_get_response_rate - get basic rate for a given rate
+  *
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index c50b8a2..c43d511 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -1221,8 +1221,8 @@ struct ieee80211_tx_info {
+ 	    status_data_idr:1,
+ 	    status_data:13,
+ 	    hw_queue:4,
++	    tx_time_mc:1,
+ 	    tx_time_est:10;
+-	/* 1 free bit */
+ 
+ 	union {
+ 		struct {
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index a88cc41..1bf8626 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1649,12 +1649,6 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
+ 
+ 	__sta_info_flush(sdata, true, link_id);
+ 
+-	ieee80211_remove_link_keys(link, &keys);
+-	if (!list_empty(&keys)) {
+-		synchronize_net();
+-		ieee80211_free_key_list(local, &keys);
+-	}
+-
+ 	link_conf->enable_beacon = false;
+ 	sdata->beacon_rate_set = false;
+ 	sdata->vif.cfg.ssid_len = 0;
+@@ -2869,6 +2863,8 @@ static int ieee80211_scan(struct wiphy *wiphy,
+ 		 */
+ 		fallthrough;
+ 	case NL80211_IFTYPE_AP:
++		/* skip check */
++		break;
+ 		/*
+ 		 * If the scan has been forced (and the driver supports
+ 		 * forcing), don't care about being beaconing already.
+diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
+index f9528ab..ce8eb40 100644
+--- a/net/mac80211/debugfs.c
++++ b/net/mac80211/debugfs.c
+@@ -215,11 +215,13 @@ static ssize_t aql_pending_read(struct file *file,
+ 			"VI     %u us\n"
+ 			"BE     %u us\n"
+ 			"BK     %u us\n"
++			"BC/MC  %u us\n"
+ 			"total  %u us\n",
+ 			atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VO]),
+ 			atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VI]),
+ 			atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BE]),
+ 			atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BK]),
++			atomic_read(&local->aql_bc_pending_airtime),
+ 			atomic_read(&local->aql_total_pending_airtime));
+ 	return simple_read_from_buffer(user_buf, count, ppos,
+ 				       buf, len);
+@@ -245,7 +247,8 @@ static ssize_t aql_txq_limit_read(struct file *file,
+ 			"VO	%u		%u\n"
+ 			"VI	%u		%u\n"
+ 			"BE	%u		%u\n"
+-			"BK	%u		%u\n",
++			"BK	%u		%u\n"
++			"BC/MC	%u\n",
+ 			local->aql_txq_limit_low[IEEE80211_AC_VO],
+ 			local->aql_txq_limit_high[IEEE80211_AC_VO],
+ 			local->aql_txq_limit_low[IEEE80211_AC_VI],
+@@ -253,7 +256,8 @@ static ssize_t aql_txq_limit_read(struct file *file,
+ 			local->aql_txq_limit_low[IEEE80211_AC_BE],
+ 			local->aql_txq_limit_high[IEEE80211_AC_BE],
+ 			local->aql_txq_limit_low[IEEE80211_AC_BK],
+-			local->aql_txq_limit_high[IEEE80211_AC_BK]);
++			local->aql_txq_limit_high[IEEE80211_AC_BK],
++			local->aql_txq_limit_bc);
+ 	return simple_read_from_buffer(user_buf, count, ppos,
+ 				       buf, len);
+ }
+@@ -279,6 +283,11 @@ static ssize_t aql_txq_limit_write(struct file *file,
+ 	else
+ 		buf[count] = '\0';
+ 
++	if (sscanf(buf, "mcast %u", &q_limit_low) == 1) {
++		local->aql_txq_limit_bc = q_limit_low;
++		return count;
++	}
++
+ 	if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3)
+ 		return -EINVAL;
+ 
+diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
+index 44cc978..c9d6743 100644
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -103,6 +103,8 @@ ieee80211_sta_keep_active(struct sta_info *sta, u8 ac)
+ 	return time_before_eq(jiffies, sta->airtime[ac].last_active + HZ / 10);
+ }
+ 
++#define AIRTIME_QUANTUM_SHIFT	3
++
+ struct ieee80211_bss {
+ 	u32 device_ts_beacon, device_ts_presp;
+ 
+@@ -1353,10 +1355,12 @@ struct ieee80211_local {
+ 	spinlock_t handle_wake_tx_queue_lock;
+ 
+ 	u16 airtime_flags;
++	u32 aql_txq_limit_bc;
+ 	u32 aql_txq_limit_low[IEEE80211_NUM_ACS];
+ 	u32 aql_txq_limit_high[IEEE80211_NUM_ACS];
+ 	u32 aql_threshold;
+ 	atomic_t aql_total_pending_airtime;
++	atomic_t aql_bc_pending_airtime;
+ 	atomic_t aql_ac_pending_airtime[IEEE80211_NUM_ACS];
+ 
+ 	const struct ieee80211_ops *ops;
+diff --git a/net/mac80211/main.c b/net/mac80211/main.c
+index 3257545..79c77af 100644
+--- a/net/mac80211/main.c
++++ b/net/mac80211/main.c
+@@ -952,6 +952,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
+ 	spin_lock_init(&local->rx_path_lock);
+ 	spin_lock_init(&local->queue_stop_reason_lock);
+ 
++	local->aql_txq_limit_bc = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_BC;
+ 	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ 		INIT_LIST_HEAD(&local->active_txqs[i]);
+ 		spin_lock_init(&local->active_txq_lock[i]);
+@@ -1581,24 +1582,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
+ 
+ 	ieee80211_check_wbrf_support(local);
+ 
+-	rtnl_lock();
+-	wiphy_lock(hw->wiphy);
+-
+-	/* add one default STA interface if supported */
+-	if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) &&
+-	    !ieee80211_hw_check(hw, NO_AUTO_VIF)) {
+-		struct vif_params params = {0};
+-
+-		result = ieee80211_if_add(local, "wlan%d", NET_NAME_ENUM, NULL,
+-					  NL80211_IFTYPE_STATION, &params);
+-		if (result)
+-			wiphy_warn(local->hw.wiphy,
+-				   "Failed to add default virtual iface\n");
+-	}
+-
+-	wiphy_unlock(hw->wiphy);
+-	rtnl_unlock();
+-
+ #ifdef CONFIG_INET
+ 	local->ifa_notifier.notifier_call = ieee80211_ifa_changed;
+ 	result = register_inetaddr_notifier(&local->ifa_notifier);
+diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
+index b128191..802cd6c 100644
+--- a/net/mac80211/rc80211_minstrel_ht.c
++++ b/net/mac80211/rc80211_minstrel_ht.c
+@@ -580,6 +580,14 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 *dest, u16 index)
+ 	int cur_tp_avg, cur_group, cur_idx;
+ 	int max_gpr_group, max_gpr_idx;
+ 	int max_gpr_tp_avg, max_gpr_prob;
++	int min_dur;
++
++	min_dur = max(minstrel_get_duration(mi->max_tp_rate[0]),
++		      minstrel_get_duration(mi->max_tp_rate[1]));
++
++	/* make the rate at least 18% slower than max tp rates */
++	if (minstrel_get_duration(index) <= min_dur * 19 / 16)
++		return;
+ 
+ 	cur_group = MI_RATE_GROUP(index);
+ 	cur_idx = MI_RATE_IDX(index);
+@@ -601,11 +609,6 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 *dest, u16 index)
+ 	    !minstrel_ht_is_legacy_group(max_tp_group))
+ 		return;
+ 
+-	/* skip rates faster than max tp rate with lower prob */
+-	if (minstrel_get_duration(mi->max_tp_rate[0]) > minstrel_get_duration(index) &&
+-	    mrs->prob_avg < max_tp_prob)
+-		return;
+-
+ 	max_gpr_group = MI_RATE_GROUP(mg->max_group_prob_rate);
+ 	max_gpr_idx = MI_RATE_IDX(mg->max_group_prob_rate);
+ 	max_gpr_prob = mi->groups[max_gpr_group].rates[max_gpr_idx].prob_avg;
+@@ -663,40 +666,6 @@ minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi,
+ 
+ }
+ 
+-/*
+- * Try to increase robustness of max_prob rate by decrease number of
+- * streams if possible.
+- */
+-static inline void
+-minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
+-{
+-	struct minstrel_mcs_group_data *mg;
+-	int tmp_max_streams, group, tmp_idx, tmp_prob;
+-	int tmp_tp = 0;
+-
+-	if (!mi->sta->deflink.ht_cap.ht_supported)
+-		return;
+-
+-	group = MI_RATE_GROUP(mi->max_tp_rate[0]);
+-	tmp_max_streams = minstrel_mcs_groups[group].streams;
+-	for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
+-		mg = &mi->groups[group];
+-		if (!mi->supported[group] || group == MINSTREL_CCK_GROUP)
+-			continue;
+-
+-		tmp_idx = MI_RATE_IDX(mg->max_group_prob_rate);
+-		tmp_prob = mi->groups[group].rates[tmp_idx].prob_avg;
+-
+-		if (tmp_tp < minstrel_ht_get_tp_avg(mi, group, tmp_idx, tmp_prob) &&
+-		   (minstrel_mcs_groups[group].streams < tmp_max_streams)) {
+-				mi->max_prob_rate = mg->max_group_prob_rate;
+-				tmp_tp = minstrel_ht_get_tp_avg(mi, group,
+-								tmp_idx,
+-								tmp_prob);
+-		}
+-	}
+-}
+-
+ static u16
+ __minstrel_ht_get_sample_rate(struct minstrel_ht_sta *mi,
+ 			      enum minstrel_sample_type type)
+@@ -769,7 +738,8 @@ minstrel_ht_calc_rate_stats(struct minstrel_priv *mp,
+ 	unsigned int cur_prob;
+ 
+ 	if (unlikely(mrs->attempts > 0)) {
+-		cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
++		cur_prob = MINSTREL_FRAC(mrs->success + mrs->last_success,
++					 mrs->attempts + mrs->last_attempts);
+ 		minstrel_filter_avg_add(&mrs->prob_avg,
+ 					&mrs->prob_avg_1, cur_prob);
+ 		mrs->att_hist += mrs->attempts;
+@@ -1175,8 +1145,6 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
+ 
+ 	mi->max_prob_rate = tmp_max_prob_rate;
+ 
+-	/* Try to increase robustness of max_prob_rate*/
+-	minstrel_ht_prob_rate_reduce_streams(mi);
+ 	minstrel_ht_refill_sample_rates(mi);
+ 
+ #ifdef CPTCFG_MAC80211_DEBUGFS
+@@ -1255,7 +1223,7 @@ minstrel_ht_ri_txstat_valid(struct minstrel_priv *mp,
+ }
+ 
+ static void
+-minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary)
++minstrel_downgrade_prob_rate(struct minstrel_ht_sta *mi, u16 *idx)
+ {
+ 	int group, orig_group;
+ 
+@@ -1270,11 +1238,7 @@ minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary)
+ 		    minstrel_mcs_groups[orig_group].streams)
+ 			continue;
+ 
+-		if (primary)
+-			*idx = mi->groups[group].max_group_tp_rate[0];
+-		else
+-			*idx = mi->groups[group].max_group_tp_rate[1];
+-		break;
++		*idx = mi->groups[group].max_group_prob_rate;
+ 	}
+ }
+ 
+@@ -1285,7 +1249,7 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
+ 	struct ieee80211_tx_info *info = st->info;
+ 	struct minstrel_ht_sta *mi = priv_sta;
+ 	struct ieee80211_tx_rate *ar = info->status.rates;
+-	struct minstrel_rate_stats *rate, *rate2;
++	struct minstrel_rate_stats *rate;
+ 	struct minstrel_priv *mp = priv;
+ 	u32 update_interval = mp->update_interval;
+ 	bool last, update = false;
+@@ -1353,18 +1317,13 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
+ 		/*
+ 		 * check for sudden death of spatial multiplexing,
+ 		 * downgrade to a lower number of streams if necessary.
++		 * only do this for the max_prob_rate to prevent spurious
++		 * rate fluctuations when the link changes suddenly
+ 		 */
+-		rate = minstrel_get_ratestats(mi, mi->max_tp_rate[0]);
++		rate = minstrel_get_ratestats(mi, mi->max_prob_rate);
+ 		if (rate->attempts > 30 &&
+ 		    rate->success < rate->attempts / 4) {
+-			minstrel_downgrade_rate(mi, &mi->max_tp_rate[0], true);
+-			update = true;
+-		}
+-
+-		rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate[1]);
+-		if (rate2->attempts > 30 &&
+-		    rate2->success < rate2->attempts / 4) {
+-			minstrel_downgrade_rate(mi, &mi->max_tp_rate[1], false);
++			minstrel_downgrade_prob_rate(mi, &mi->max_prob_rate);
+ 			update = true;
+ 		}
+ 	}
+diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h
+index f385cf6..1f78a94 100644
+--- a/net/mac80211/rc80211_minstrel_ht.h
++++ b/net/mac80211/rc80211_minstrel_ht.h
+@@ -14,7 +14,7 @@
+ 
+ /* scaled fraction values */
+ #define MINSTREL_SCALE  12
+-#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div)
++#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / (div))
+ #define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE)
+ 
+ #define EWMA_LEVEL	96	/* ewma weighting factor [/EWMA_DIV] */
+diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
+index 16f28db..83e98c7 100644
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -565,6 +565,11 @@ __sta_info_alloc(struct ieee80211_sub_if_data *sdata,
+ 	spin_lock_init(&sta->ps_lock);
+ 	INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
+ 	wiphy_work_init(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
++#if LINUX_VERSION_IS_LESS(6,2,0)
++	sta->ampdu_mlme.dialog_token_allocator = prandom_u32_max(U8_MAX);
++#else
++	sta->ampdu_mlme.dialog_token_allocator = get_random_u32_below(U8_MAX);
++#endif
+ #ifdef CPTCFG_MAC80211_MESH
+ 	if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ 		sta->mesh = kzalloc(sizeof(*sta->mesh), gfp);
+@@ -2352,13 +2357,28 @@ EXPORT_SYMBOL(ieee80211_sta_recalc_aggregates);
+ 
+ void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
+ 					  struct sta_info *sta, u8 ac,
+-					  u16 tx_airtime, bool tx_completed)
++					  u16 tx_airtime, bool tx_completed,
++					  bool mcast)
+ {
+ 	int tx_pending;
+ 
+ 	if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL))
+ 		return;
+ 
++	if (mcast) {
++		if (!tx_completed) {
++			atomic_add(tx_airtime, &local->aql_bc_pending_airtime);
++			return;
++		}
++
++		tx_pending = atomic_sub_return(tx_airtime,
++					       &local->aql_bc_pending_airtime);
++		if (tx_pending < 0)
++			atomic_cmpxchg(&local->aql_bc_pending_airtime,
++				       tx_pending, 0);
++		return;
++	}
++
+ 	if (!tx_completed) {
+ 		if (sta)
+ 			atomic_add(tx_airtime,
+@@ -2439,6 +2459,13 @@ static void sta_stats_decode_rate(struct ieee80211_local *local, u32 rate,
+ 
+ 		sband = local->hw.wiphy->bands[band];
+ 
++		if (!sband) {
++			wiphy_warn(local->hw.wiphy,
++				    "Invalid band %d\n",
++				    band);
++			break;
++		}
++
+ 		if (WARN_ON_ONCE(!sband->bitrates))
+ 			break;
+ 
+diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
+index d1b3a61..40513d8 100644
+--- a/net/mac80211/sta_info.h
++++ b/net/mac80211/sta_info.h
+@@ -147,7 +147,8 @@ struct airtime_info {
+ 
+ void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
+ 					  struct sta_info *sta, u8 ac,
+-					  u16 tx_airtime, bool tx_completed);
++					  u16 tx_airtime, bool tx_completed,
++					  bool mcast);
+ 
+ struct sta_info;
+ 
+diff --git a/net/mac80211/status.c b/net/mac80211/status.c
+index df5a329..3f72c7e 100644
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -734,7 +734,7 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
+ 		ieee80211_sta_update_pending_airtime(local, sta,
+ 						     skb_get_queue_mapping(skb),
+ 						     tx_time_est,
+-						     true);
++						     true, info->tx_time_mc);
+ 		rcu_read_unlock();
+ 	}
+ 
+@@ -1162,10 +1162,11 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw,
+ 		/* Do this here to avoid the expensive lookup of the sta
+ 		 * in ieee80211_report_used_skb().
+ 		 */
++		bool mcast = IEEE80211_SKB_CB(skb)->tx_time_mc;
+ 		ieee80211_sta_update_pending_airtime(local, sta,
+ 						     skb_get_queue_mapping(skb),
+ 						     tx_time_est,
+-						     true);
++						     true, mcast);
+ 		ieee80211_info_set_tx_time_est(IEEE80211_SKB_CB(skb), 0);
+ 	}
+ 
+diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
+index bec9cc3..65f9c26 100644
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -2554,7 +2554,7 @@ static u16 ieee80211_store_ack_skb(struct ieee80211_local *local,
+ 
+ 		spin_lock_irqsave(&local->ack_status_lock, flags);
+ 		id = idr_alloc(&local->ack_status_frames, ack_skb,
+-			       1, 0x2000, GFP_ATOMIC);
++			       1, 0x1000, GFP_ATOMIC);
+ 		spin_unlock_irqrestore(&local->ack_status_lock, flags);
+ 
+ 		if (id >= 0) {
+@@ -3981,20 +3981,20 @@ begin:
+ encap_out:
+ 	info->control.vif = vif;
+ 
+-	if (tx.sta &&
+-	    wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) {
+-		bool ampdu = txq->ac != IEEE80211_AC_VO;
++	if (wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) {
++		bool ampdu = txq->sta && txq->ac != IEEE80211_AC_VO;
+ 		u32 airtime;
+ 
+ 		airtime = ieee80211_calc_expected_tx_airtime(hw, vif, txq->sta,
+ 							     skb->len, ampdu);
+-		if (airtime) {
+-			airtime = ieee80211_info_set_tx_time_est(info, airtime);
+-			ieee80211_sta_update_pending_airtime(local, tx.sta,
+-							     txq->ac,
+-							     airtime,
+-							     false);
+-		}
++		if (!airtime)
++			return skb;
++
++		airtime = ieee80211_info_set_tx_time_est(info, airtime);
++		info->tx_time_mc = !tx.sta;
++		ieee80211_sta_update_pending_airtime(local, tx.sta, txq->ac,
++						     airtime, false,
++						     info->tx_time_mc);
+ 	}
+ 
+ 	return skb;
+@@ -4046,6 +4046,7 @@ struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
+ 	struct ieee80211_txq *ret = NULL;
+ 	struct txq_info *txqi = NULL, *head = NULL;
+ 	bool found_eligible_txq = false;
++	bool aql_check;
+ 
+ 	spin_lock_bh(&local->active_txq_lock[ac]);
+ 
+@@ -4069,26 +4070,26 @@ struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
+ 	if (!head)
+ 		head = txqi;
+ 
++	aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
++	if (aql_check)
++		found_eligible_txq = true;
++
+ 	if (txqi->txq.sta) {
+ 		struct sta_info *sta = container_of(txqi->txq.sta,
+ 						    struct sta_info, sta);
+-		bool aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
+-		s32 deficit = ieee80211_sta_deficit(sta, txqi->txq.ac);
+-
+-		if (aql_check)
+-			found_eligible_txq = true;
+-
+-		if (deficit < 0)
++		if (ieee80211_sta_deficit(sta, txqi->txq.ac) < 0) {
+ 			sta->airtime[txqi->txq.ac].deficit +=
+-				sta->airtime_weight;
+-
+-		if (deficit < 0 || !aql_check) {
+-			list_move_tail(&txqi->schedule_order,
+-				       &local->active_txqs[txqi->txq.ac]);
+-			goto begin;
++				sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
++			aql_check = false;
+ 		}
+ 	}
+ 
++	if (!aql_check) {
++		list_move_tail(&txqi->schedule_order,
++				   &local->active_txqs[txqi->txq.ac]);
++		goto begin;
++	}
++
+ 	if (txqi->schedule_round == local->schedule_round[ac])
+ 		goto out;
+ 
+@@ -4153,7 +4154,8 @@ bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw,
+ 		return true;
+ 
+ 	if (!txq->sta)
+-		return true;
++		return atomic_read(&local->aql_bc_pending_airtime) <
++		       local->aql_txq_limit_bc;
+ 
+ 	if (unlikely(txq->tid == IEEE80211_NUM_TIDS))
+ 		return true;
+@@ -4202,15 +4204,15 @@ bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
+ 
+ 	spin_lock_bh(&local->active_txq_lock[ac]);
+ 
+-	if (!txqi->txq.sta)
+-		goto out;
+-
+ 	if (list_empty(&txqi->schedule_order))
+ 		goto out;
+ 
+ 	if (!ieee80211_txq_schedule_airtime_check(local, ac))
+ 		goto out;
+ 
++	if (!txqi->txq.sta)
++		goto out;
++
+ 	list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac],
+ 				 schedule_order) {
+ 		if (iter == txqi)
+@@ -4223,7 +4225,8 @@ bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
+ 		}
+ 		sta = container_of(iter->txq.sta, struct sta_info, sta);
+ 		if (ieee80211_sta_deficit(sta, ac) < 0)
+-			sta->airtime[ac].deficit += sta->airtime_weight;
++			sta->airtime[ac].deficit += sta->airtime_weight <<
++						    AIRTIME_QUANTUM_SHIFT;
+ 		list_move_tail(&iter->schedule_order, &local->active_txqs[ac]);
+ 	}
+ 
+@@ -4231,7 +4234,7 @@ bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
+ 	if (sta->airtime[ac].deficit >= 0)
+ 		goto out;
+ 
+-	sta->airtime[ac].deficit += sta->airtime_weight;
++	sta->airtime[ac].deficit += sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
+ 	list_move_tail(&txqi->schedule_order, &local->active_txqs[ac]);
+ 	spin_unlock_bh(&local->active_txq_lock[ac]);
+ 
+diff --git a/net/wireless/ap.c b/net/wireless/ap.c
+index 9a9a870..9cd0ab4 100644
+--- a/net/wireless/ap.c
++++ b/net/wireless/ap.c
+@@ -30,6 +30,9 @@ static int ___cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
+ 	if (!wdev->links[link_id].ap.beacon_interval)
+ 		return -ENOENT;
+ 
++	cfg80211_update_last_available(wdev->wiphy,
++				       &wdev->links[link_id].ap.chandef);
++
+ 	err = rdev_stop_ap(rdev, dev, link_id);
+ 	if (!err) {
+ 		wdev->conn_owner_nlportid = 0;
+@@ -41,9 +44,6 @@ static int ___cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
+ 		if (notify)
+ 			nl80211_send_ap_stopped(wdev, link_id);
+ 
+-		/* Should we apply the grace period during beaconing interface
+-		 * shutdown also?
+-		 */
+ 		cfg80211_sched_dfs_chan_update(rdev);
+ 	}
+ 
+diff --git a/net/wireless/chan.c b/net/wireless/chan.c
+index 138df3a..edc9034 100644
+--- a/net/wireless/chan.c
++++ b/net/wireless/chan.c
+@@ -598,6 +598,8 @@ static void cfg80211_set_chans_dfs_state(struct wiphy *wiphy, u32 center_freq,
+ 
+ 		c->dfs_state = dfs_state;
+ 		c->dfs_state_entered = jiffies;
++		if (dfs_state == NL80211_DFS_AVAILABLE)
++			c->dfs_state_last_available = jiffies;
+ 	}
+ }
+ 
+@@ -1087,6 +1089,49 @@ static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy,
+ 	return true;
+ }
+ 
++static void
++__cfg80211_update_last_available(struct wiphy *wiphy,
++					 u32 center_freq,
++					 u32 bandwidth)
++{
++	struct ieee80211_channel *c;
++	u32 freq, start_freq, end_freq;
++
++	start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
++	end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
++
++	/*
++	 * Check entire range of channels for the bandwidth.
++	 * If any channel in between is disabled or has not
++	 * had gone through CAC return false
++	 */
++	for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
++		c = ieee80211_get_channel_khz(wiphy, freq);
++		if (!c)
++			return;
++
++		c->dfs_state_last_available = jiffies;
++	}
++}
++
++void cfg80211_update_last_available(struct wiphy *wiphy,
++				    const struct cfg80211_chan_def *chandef)
++{
++	int width;
++
++	width = cfg80211_chandef_get_width(chandef);
++	if (width < 0)
++		return;
++
++	__cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq1),
++						 width);
++	if (chandef->width != NL80211_CHAN_WIDTH_80P80)
++	    return;
++
++	__cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq2),
++						 width);
++}
++
+ static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
+ 				const struct cfg80211_chan_def *chandef)
+ {
+diff --git a/net/wireless/core.c b/net/wireless/core.c
+index 79d5526..45e2d94 100644
+--- a/net/wireless/core.c
++++ b/net/wireless/core.c
+@@ -665,21 +665,6 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
+ 				    c->limits[j].max > 1))
+ 				return -EINVAL;
+ 
+-			/*
+-			 * This isn't well-defined right now. If you have an
+-			 * IBSS interface, then its beacon interval may change
+-			 * by joining other networks, and nothing prevents it
+-			 * from doing that.
+-			 * So technically we probably shouldn't even allow AP
+-			 * and IBSS in the same interface, but it seems that
+-			 * some drivers support that, possibly only with fixed
+-			 * beacon intervals for IBSS.
+-			 */
+-			if (WARN_ON(types & BIT(NL80211_IFTYPE_ADHOC) &&
+-				    c->beacon_int_min_gcd)) {
+-				return -EINVAL;
+-			}
+-
+ 			cnt += c->limits[j].max;
+ 			/*
+ 			 * Don't advertise an unsupported type
+diff --git a/net/wireless/core.h b/net/wireless/core.h
+index 4304d1a..cebc5a1 100644
+--- a/net/wireless/core.h
++++ b/net/wireless/core.h
+@@ -467,6 +467,8 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy,
+ 			    enum nl80211_dfs_state dfs_state);
+ 
+ void cfg80211_dfs_channels_update_work(struct work_struct *work);
++void cfg80211_update_last_available(struct wiphy *wiphy,
++				    const struct cfg80211_chan_def *chandef);
+ 
+ void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
+ 
+diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
+index 3b0fe7c..c7e62eb 100644
+--- a/net/wireless/mlme.c
++++ b/net/wireless/mlme.c
+@@ -1037,6 +1037,8 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
+ 			if (c->dfs_state == NL80211_DFS_UNAVAILABLE) {
+ 				time_dfs_update = IEEE80211_DFS_MIN_NOP_TIME_MS;
+ 				radar_event = NL80211_RADAR_NOP_FINISHED;
++				timeout = c->dfs_state_entered +
++					  msecs_to_jiffies(time_dfs_update);
+ 			} else {
+ 				if (regulatory_pre_cac_allowed(wiphy) ||
+ 				    cfg80211_any_wiphy_oper_chan(wiphy, c))
+@@ -1044,11 +1046,10 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
+ 
+ 				time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS;
+ 				radar_event = NL80211_RADAR_PRE_CAC_EXPIRED;
++				timeout = c->dfs_state_last_available +
++					  msecs_to_jiffies(time_dfs_update);
+ 			}
+ 
+-			timeout = c->dfs_state_entered +
+-				  msecs_to_jiffies(time_dfs_update);
+-
+ 			if (time_after_eq(jiffies, timeout)) {
+ 				c->dfs_state = NL80211_DFS_USABLE;
+ 				c->dfs_state_entered = jiffies;
+diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
+index 4f855bf..7d0cca8 100644
+--- a/net/wireless/sysfs.c
++++ b/net/wireless/sysfs.c
+@@ -24,18 +24,35 @@ static inline struct cfg80211_registered_device *dev_to_rdev(
+ 	return container_of(dev, struct cfg80211_registered_device, wiphy.dev);
+ }
+ 
+-#define SHOW_FMT(name, fmt, member)					\
++#define SHOW_FMT(name, fmt, member, mode)				\
+ static ssize_t name ## _show(struct device *dev,			\
+ 			      struct device_attribute *attr,		\
+ 			      char *buf)				\
+ {									\
+ 	return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member);	\
+ }									\
+-static DEVICE_ATTR_RO(name)
++static DEVICE_ATTR_##mode(name)
+ 
+-SHOW_FMT(index, "%d", wiphy_idx);
+-SHOW_FMT(macaddress, "%pM", wiphy.perm_addr);
+-SHOW_FMT(address_mask, "%pM", wiphy.addr_mask);
++static ssize_t macaddress_store(struct device *dev,
++				struct device_attribute *attr,
++				const char *buf, size_t len)
++{
++	u8 mac[ETH_ALEN];
++
++	if (!mac_pton(buf, mac))
++		return -EINVAL;
++
++	if (buf[3 * ETH_ALEN - 1] && buf[3 * ETH_ALEN - 1] != '\n')
++		return -EINVAL;
++
++	memcpy(dev_to_rdev(dev)->wiphy.perm_addr, mac, ETH_ALEN);
++
++	return strnlen(buf, len);
++}
++
++SHOW_FMT(index, "%d", wiphy_idx, RO);
++SHOW_FMT(macaddress, "%pM", wiphy.perm_addr, RW);
++SHOW_FMT(address_mask, "%pM", wiphy.addr_mask, RO);
+ 
+ static ssize_t name_show(struct device *dev,
+ 			 struct device_attribute *attr,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0007-sync-backports-patches-subsys.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0007-sync-backports-patches-subsys.patch
deleted file mode 100644
index 2ab7252..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0007-sync-backports-patches-subsys.patch
+++ /dev/null
@@ -1,941 +0,0 @@
-From 3a7f4236d9d089c749bbc4ff537f8ff2437acf2e Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Tue, 12 Mar 2024 11:29:55 +0800
-Subject: [PATCH 07/61] sync backports patches/subsys
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- include/net/cfg80211.h             | 28 +++++++++++
- net/mac80211/cfg.c                 |  2 +
- net/mac80211/debugfs.c             | 13 +++++-
- net/mac80211/ieee80211_i.h         |  4 ++
- net/mac80211/main.c                | 19 +-------
- net/mac80211/mesh.c                |  8 +++-
- net/mac80211/mesh.h                | 31 ++++++++++--
- net/mac80211/mesh_pathtbl.c        | 19 +++++---
- net/mac80211/rc80211_minstrel_ht.c | 75 +++++++-----------------------
- net/mac80211/rc80211_minstrel_ht.h |  2 +-
- net/mac80211/rx.c                  | 13 ++++--
- net/mac80211/sta_info.c            | 29 +++++++-----
- net/mac80211/tx.c                  | 46 +++++++++---------
- net/wireless/ap.c                  |  6 +--
- net/wireless/chan.c                | 45 ++++++++++++++++++
- net/wireless/core.c                | 15 ------
- net/wireless/core.h                |  2 +
- net/wireless/mlme.c                |  7 +--
- net/wireless/sysfs.c               | 27 +++++++++--
- 19 files changed, 240 insertions(+), 151 deletions(-)
-
-diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
-index 0b5799f..4c5daf9 100644
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -189,6 +189,8 @@ enum ieee80211_channel_flags {
-  * @dfs_state: current state of this channel. Only relevant if radar is required
-  *	on this channel.
-  * @dfs_state_entered: timestamp (jiffies) when the dfs state was entered.
-+ * @dfs_state_last_available: timestamp (jiffies) of the last time when the
-+ *	channel was available.
-  * @dfs_cac_ms: DFS CAC time in milliseconds, this is valid for DFS channels.
-  * @psd: power spectral density (in dBm)
-  */
-@@ -206,6 +208,7 @@ struct ieee80211_channel {
- 	int orig_mag, orig_mpwr;
- 	enum nl80211_dfs_state dfs_state;
- 	unsigned long dfs_state_entered;
-+	unsigned long dfs_state_last_available;
- 	unsigned int dfs_cac_ms;
- 	s8 psd;
- };
-@@ -1075,6 +1078,30 @@ int cfg80211_chandef_primary(const struct cfg80211_chan_def *chandef,
- 			     enum nl80211_chan_width primary_chan_width,
- 			     u16 *punctured);
- 
-+/**
-+ * cfg80211_chandef_dfs_usable - checks if chandef is DFS usable and we
-+ *				 can/need start CAC on such channel
-+ * @wiphy: the wiphy to validate against
-+ * @chandef: the channel definition to check
-+ *
-+ * Return: true if all channels available and at least
-+ *	   one channel requires CAC (NL80211_DFS_USABLE)
-+ */
-+bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
-+				 const struct cfg80211_chan_def *chandef);
-+
-+/**
-+ * cfg80211_chandef_dfs_cac_time - get the DFS CAC time (in ms) for given
-+ *				   channel definition
-+ * @wiphy: the wiphy to validate against
-+ * @chandef: the channel definition to check
-+ *
-+ * Returns: DFS CAC time (in ms) which applies for this channel definition
-+ */
-+unsigned int
-+cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
-+			      const struct cfg80211_chan_def *chandef);
-+
- /**
-  * nl80211_send_chandef - sends the channel definition.
-  * @msg: the msg to send channel definition
-@@ -3413,6 +3440,7 @@ enum wiphy_params_flags {
- /* The per TXQ device queue limit in airtime */
- #define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L	5000
- #define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H	12000
-+#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_BC	50000
- 
- /* The per interface airtime threshold to switch to lower queue limit */
- #define IEEE80211_AQL_THRESHOLD			24000
-diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
-index db71792..72e64be 100644
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -2851,6 +2851,8 @@ static int ieee80211_scan(struct wiphy *wiphy,
- 		 */
- 		fallthrough;
- 	case NL80211_IFTYPE_AP:
-+		/* skip check */
-+		break;
- 		/*
- 		 * If the scan has been forced (and the driver supports
- 		 * forcing), don't care about being beaconing already.
-diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
-index c660138..f9c5ed8 100644
---- a/net/mac80211/debugfs.c
-+++ b/net/mac80211/debugfs.c
-@@ -215,11 +215,13 @@ static ssize_t aql_pending_read(struct file *file,
- 			"VI     %u us\n"
- 			"BE     %u us\n"
- 			"BK     %u us\n"
-+			"BC/MC  %u us\n"
- 			"total  %u us\n",
- 			atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VO]),
- 			atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VI]),
- 			atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BE]),
- 			atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BK]),
-+			atomic_read(&local->aql_bc_pending_airtime),
- 			atomic_read(&local->aql_total_pending_airtime));
- 	return simple_read_from_buffer(user_buf, count, ppos,
- 				       buf, len);
-@@ -245,7 +247,8 @@ static ssize_t aql_txq_limit_read(struct file *file,
- 			"VO	%u		%u\n"
- 			"VI	%u		%u\n"
- 			"BE	%u		%u\n"
--			"BK	%u		%u\n",
-+			"BK	%u		%u\n"
-+			"BC/MC	%u\n",
- 			local->aql_txq_limit_low[IEEE80211_AC_VO],
- 			local->aql_txq_limit_high[IEEE80211_AC_VO],
- 			local->aql_txq_limit_low[IEEE80211_AC_VI],
-@@ -253,7 +256,8 @@ static ssize_t aql_txq_limit_read(struct file *file,
- 			local->aql_txq_limit_low[IEEE80211_AC_BE],
- 			local->aql_txq_limit_high[IEEE80211_AC_BE],
- 			local->aql_txq_limit_low[IEEE80211_AC_BK],
--			local->aql_txq_limit_high[IEEE80211_AC_BK]);
-+			local->aql_txq_limit_high[IEEE80211_AC_BK],
-+			local->aql_txq_limit_bc);
- 	return simple_read_from_buffer(user_buf, count, ppos,
- 				       buf, len);
- }
-@@ -279,6 +283,11 @@ static ssize_t aql_txq_limit_write(struct file *file,
- 	else
- 		buf[count] = '\0';
- 
-+	if (sscanf(buf, "mcast %u", &q_limit_low) == 1) {
-+		local->aql_txq_limit_bc = q_limit_low;
-+		return count;
-+	}
-+
- 	if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3)
- 		return -EINVAL;
- 
-diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
-index 4827825..c5781c3 100644
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -102,6 +102,8 @@ ieee80211_sta_keep_active(struct sta_info *sta, u8 ac)
- 	return time_before_eq(jiffies, sta->airtime[ac].last_active + HZ / 10);
- }
- 
-+#define AIRTIME_QUANTUM_SHIFT	3
-+
- struct ieee80211_bss {
- 	u32 device_ts_beacon, device_ts_presp;
- 
-@@ -1343,10 +1345,12 @@ struct ieee80211_local {
- 	spinlock_t handle_wake_tx_queue_lock;
- 
- 	u16 airtime_flags;
-+	u32 aql_txq_limit_bc;
- 	u32 aql_txq_limit_low[IEEE80211_NUM_ACS];
- 	u32 aql_txq_limit_high[IEEE80211_NUM_ACS];
- 	u32 aql_threshold;
- 	atomic_t aql_total_pending_airtime;
-+	atomic_t aql_bc_pending_airtime;
- 	atomic_t aql_ac_pending_airtime[IEEE80211_NUM_ACS];
- 
- 	const struct ieee80211_ops *ops;
-diff --git a/net/mac80211/main.c b/net/mac80211/main.c
-index 6518ca5..81a9645 100644
---- a/net/mac80211/main.c
-+++ b/net/mac80211/main.c
-@@ -944,6 +944,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
- 	spin_lock_init(&local->rx_path_lock);
- 	spin_lock_init(&local->queue_stop_reason_lock);
- 
-+	local->aql_txq_limit_bc = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_BC;
- 	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
- 		INIT_LIST_HEAD(&local->active_txqs[i]);
- 		spin_lock_init(&local->active_txq_lock[i]);
-@@ -1562,24 +1563,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
- 	debugfs_hw_add(local);
- 	rate_control_add_debugfs(local);
- 
--	rtnl_lock();
--	wiphy_lock(hw->wiphy);
--
--	/* add one default STA interface if supported */
--	if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) &&
--	    !ieee80211_hw_check(hw, NO_AUTO_VIF)) {
--		struct vif_params params = {0};
--
--		result = ieee80211_if_add(local, "wlan%d", NET_NAME_ENUM, NULL,
--					  NL80211_IFTYPE_STATION, &params);
--		if (result)
--			wiphy_warn(local->hw.wiphy,
--				   "Failed to add default virtual iface\n");
--	}
--
--	wiphy_unlock(hw->wiphy);
--	rtnl_unlock();
--
- #ifdef CONFIG_INET
- 	local->ifa_notifier.notifier_call = ieee80211_ifa_changed;
- 	result = register_inetaddr_notifier(&local->ifa_notifier);
-diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
-index 32475da..cbc9b5e 100644
---- a/net/mac80211/mesh.c
-+++ b/net/mac80211/mesh.c
-@@ -747,6 +747,9 @@ bool ieee80211_mesh_xmit_fast(struct ieee80211_sub_if_data *sdata,
- 			      struct sk_buff *skb, u32 ctrl_flags)
- {
- 	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-+	struct ieee80211_mesh_fast_tx_key key = {
-+		.type = MESH_FAST_TX_TYPE_LOCAL
-+	};
- 	struct ieee80211_mesh_fast_tx *entry;
- 	struct ieee80211s_hdr *meshhdr;
- 	u8 sa[ETH_ALEN] __aligned(2);
-@@ -782,7 +785,10 @@ bool ieee80211_mesh_xmit_fast(struct ieee80211_sub_if_data *sdata,
- 			return false;
- 	}
- 
--	entry = mesh_fast_tx_get(sdata, skb->data);
-+	ether_addr_copy(key.addr, skb->data);
-+	if (!ether_addr_equal(skb->data + ETH_ALEN, sdata->vif.addr))
-+		key.type = MESH_FAST_TX_TYPE_PROXIED;
-+	entry = mesh_fast_tx_get(sdata, &key);
- 	if (!entry)
- 		return false;
- 
-diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
-index c472b49..c0c357f 100644
---- a/net/mac80211/mesh.h
-+++ b/net/mac80211/mesh.h
-@@ -134,10 +134,34 @@ struct mesh_path {
- #define MESH_FAST_TX_CACHE_THRESHOLD_SIZE	384
- #define MESH_FAST_TX_CACHE_TIMEOUT		8000 /* msecs */
- 
-+/**
-+ * enum ieee80211_mesh_fast_tx_type - cached mesh fast tx entry type
-+ *
-+ * @MESH_FAST_TX_TYPE_LOCAL: tx from the local vif address as SA
-+ * @MESH_FAST_TX_TYPE_PROXIED: local tx with a different SA (e.g. bridged)
-+ * @MESH_FAST_TX_TYPE_FORWARDED: forwarded from a different mesh point
-+ */
-+enum ieee80211_mesh_fast_tx_type {
-+	MESH_FAST_TX_TYPE_LOCAL,
-+	MESH_FAST_TX_TYPE_PROXIED,
-+	MESH_FAST_TX_TYPE_FORWARDED,
-+};
-+
-+/**
-+ * struct ieee80211_mesh_fast_tx_key - cached mesh fast tx entry key
-+ *
-+ * @addr: The Ethernet DA for this entry
-+ * @type: cache entry type
-+ */
-+struct ieee80211_mesh_fast_tx_key {
-+	u8 addr[ETH_ALEN] __aligned(2);
-+	enum ieee80211_mesh_fast_tx_type type;
-+};
-+
- /**
-  * struct ieee80211_mesh_fast_tx - cached mesh fast tx entry
-  * @rhash: rhashtable pointer
-- * @addr_key: The Ethernet DA which is the key for this entry
-+ * @key: the lookup key for this cache entry
-  * @fast_tx: base fast_tx data
-  * @hdr: cached mesh and rfc1042 headers
-  * @hdrlen: length of mesh + rfc1042
-@@ -148,7 +172,7 @@ struct mesh_path {
-  */
- struct ieee80211_mesh_fast_tx {
- 	struct rhash_head rhash;
--	u8 addr_key[ETH_ALEN] __aligned(2);
-+	struct ieee80211_mesh_fast_tx_key key;
- 
- 	struct ieee80211_fast_tx fast_tx;
- 	u8 hdr[sizeof(struct ieee80211s_hdr) + sizeof(rfc1042_header)];
-@@ -334,7 +358,8 @@ void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata);
- 
- bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt);
- struct ieee80211_mesh_fast_tx *
--mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata, const u8 *addr);
-+mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata,
-+		 struct ieee80211_mesh_fast_tx_key *key);
- bool ieee80211_mesh_xmit_fast(struct ieee80211_sub_if_data *sdata,
- 			      struct sk_buff *skb, u32 ctrl_flags);
- void mesh_fast_tx_cache(struct ieee80211_sub_if_data *sdata,
-diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
-index 91b55d6..93f6a03 100644
---- a/net/mac80211/mesh_pathtbl.c
-+++ b/net/mac80211/mesh_pathtbl.c
-@@ -37,8 +37,8 @@ static const struct rhashtable_params mesh_rht_params = {
- static const struct rhashtable_params fast_tx_rht_params = {
- 	.nelem_hint = 10,
- 	.automatic_shrinking = true,
--	.key_len = ETH_ALEN,
--	.key_offset = offsetof(struct ieee80211_mesh_fast_tx, addr_key),
-+	.key_len = sizeof(struct ieee80211_mesh_fast_tx_key),
-+	.key_offset = offsetof(struct ieee80211_mesh_fast_tx, key),
- 	.head_offset = offsetof(struct ieee80211_mesh_fast_tx, rhash),
- 	.hashfn = mesh_table_hash,
- };
-@@ -431,20 +431,21 @@ static void mesh_fast_tx_entry_free(struct mesh_tx_cache *cache,
- }
- 
- struct ieee80211_mesh_fast_tx *
--mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata, const u8 *addr)
-+mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata,
-+		 struct ieee80211_mesh_fast_tx_key *key)
- {
- 	struct ieee80211_mesh_fast_tx *entry;
- 	struct mesh_tx_cache *cache;
- 
- 	cache = &sdata->u.mesh.tx_cache;
--	entry = rhashtable_lookup(&cache->rht, addr, fast_tx_rht_params);
-+	entry = rhashtable_lookup(&cache->rht, key, fast_tx_rht_params);
- 	if (!entry)
- 		return NULL;
- 
- 	if (!(entry->mpath->flags & MESH_PATH_ACTIVE) ||
- 	    mpath_expired(entry->mpath)) {
- 		spin_lock_bh(&cache->walk_lock);
--		entry = rhashtable_lookup(&cache->rht, addr, fast_tx_rht_params);
-+		entry = rhashtable_lookup(&cache->rht, key, fast_tx_rht_params);
- 		if (entry)
- 		    mesh_fast_tx_entry_free(cache, entry);
- 		spin_unlock_bh(&cache->walk_lock);
-@@ -489,18 +490,24 @@ void mesh_fast_tx_cache(struct ieee80211_sub_if_data *sdata,
- 	if (!sta)
- 		return;
- 
-+	build.key.type = MESH_FAST_TX_TYPE_LOCAL;
- 	if ((meshhdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6) {
- 		/* This is required to keep the mppath alive */
- 		mppath = mpp_path_lookup(sdata, meshhdr->eaddr1);
- 		if (!mppath)
- 			return;
- 		build.mppath = mppath;
-+		if (!ether_addr_equal(meshhdr->eaddr2, sdata->vif.addr))
-+			build.key.type = MESH_FAST_TX_TYPE_PROXIED;
- 	} else if (ieee80211_has_a4(hdr->frame_control)) {
- 		mppath = mpath;
- 	} else {
- 		return;
- 	}
- 
-+	if (!ether_addr_equal(hdr->addr4, sdata->vif.addr))
-+		build.key.type = MESH_FAST_TX_TYPE_FORWARDED;
-+
- 	/* rate limit, in case fast xmit can't be enabled */
- 	if (mppath->fast_tx_check == jiffies)
- 		return;
-@@ -547,7 +554,7 @@ void mesh_fast_tx_cache(struct ieee80211_sub_if_data *sdata,
- 		}
- 	}
- 
--	memcpy(build.addr_key, mppath->dst, ETH_ALEN);
-+	memcpy(build.key.addr, mppath->dst, ETH_ALEN);
- 	build.timestamp = jiffies;
- 	build.fast_tx.band = info->band;
- 	build.fast_tx.da_offs = offsetof(struct ieee80211_hdr, addr3);
-diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
-index 62f323a..74413d7 100644
---- a/net/mac80211/rc80211_minstrel_ht.c
-+++ b/net/mac80211/rc80211_minstrel_ht.c
-@@ -582,6 +582,14 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 *dest, u16 index)
- 	int cur_tp_avg, cur_group, cur_idx;
- 	int max_gpr_group, max_gpr_idx;
- 	int max_gpr_tp_avg, max_gpr_prob;
-+	int min_dur;
-+
-+	min_dur = max(minstrel_get_duration(mi->max_tp_rate[0]),
-+		      minstrel_get_duration(mi->max_tp_rate[1]));
-+
-+	/* make the rate at least 18% slower than max tp rates */
-+	if (minstrel_get_duration(index) <= min_dur * 19 / 16)
-+		return;
- 
- 	cur_group = MI_RATE_GROUP(index);
- 	cur_idx = MI_RATE_IDX(index);
-@@ -603,11 +611,6 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 *dest, u16 index)
- 	    !minstrel_ht_is_legacy_group(max_tp_group))
- 		return;
- 
--	/* skip rates faster than max tp rate with lower prob */
--	if (minstrel_get_duration(mi->max_tp_rate[0]) > minstrel_get_duration(index) &&
--	    mrs->prob_avg < max_tp_prob)
--		return;
--
- 	max_gpr_group = MI_RATE_GROUP(mg->max_group_prob_rate);
- 	max_gpr_idx = MI_RATE_IDX(mg->max_group_prob_rate);
- 	max_gpr_prob = mi->groups[max_gpr_group].rates[max_gpr_idx].prob_avg;
-@@ -665,40 +668,6 @@ minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi,
- 
- }
- 
--/*
-- * Try to increase robustness of max_prob rate by decrease number of
-- * streams if possible.
-- */
--static inline void
--minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
--{
--	struct minstrel_mcs_group_data *mg;
--	int tmp_max_streams, group, tmp_idx, tmp_prob;
--	int tmp_tp = 0;
--
--	if (!mi->sta->deflink.ht_cap.ht_supported)
--		return;
--
--	group = MI_RATE_GROUP(mi->max_tp_rate[0]);
--	tmp_max_streams = minstrel_mcs_groups[group].streams;
--	for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
--		mg = &mi->groups[group];
--		if (!mi->supported[group] || group == MINSTREL_CCK_GROUP)
--			continue;
--
--		tmp_idx = MI_RATE_IDX(mg->max_group_prob_rate);
--		tmp_prob = mi->groups[group].rates[tmp_idx].prob_avg;
--
--		if (tmp_tp < minstrel_ht_get_tp_avg(mi, group, tmp_idx, tmp_prob) &&
--		   (minstrel_mcs_groups[group].streams < tmp_max_streams)) {
--				mi->max_prob_rate = mg->max_group_prob_rate;
--				tmp_tp = minstrel_ht_get_tp_avg(mi, group,
--								tmp_idx,
--								tmp_prob);
--		}
--	}
--}
--
- static u16
- __minstrel_ht_get_sample_rate(struct minstrel_ht_sta *mi,
- 			      enum minstrel_sample_type type)
-@@ -771,7 +740,8 @@ minstrel_ht_calc_rate_stats(struct minstrel_priv *mp,
- 	unsigned int cur_prob;
- 
- 	if (unlikely(mrs->attempts > 0)) {
--		cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
-+		cur_prob = MINSTREL_FRAC(mrs->success + mrs->last_success,
-+					 mrs->attempts + mrs->last_attempts);
- 		minstrel_filter_avg_add(&mrs->prob_avg,
- 					&mrs->prob_avg_1, cur_prob);
- 		mrs->att_hist += mrs->attempts;
-@@ -1177,8 +1147,6 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
- 
- 	mi->max_prob_rate = tmp_max_prob_rate;
- 
--	/* Try to increase robustness of max_prob_rate*/
--	minstrel_ht_prob_rate_reduce_streams(mi);
- 	minstrel_ht_refill_sample_rates(mi);
- 
- #ifdef CPTCFG_MAC80211_DEBUGFS
-@@ -1257,7 +1225,7 @@ minstrel_ht_ri_txstat_valid(struct minstrel_priv *mp,
- }
- 
- static void
--minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary)
-+minstrel_downgrade_prob_rate(struct minstrel_ht_sta *mi, u16 *idx)
- {
- 	int group, orig_group;
- 
-@@ -1272,11 +1240,7 @@ minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary)
- 		    minstrel_mcs_groups[orig_group].streams)
- 			continue;
- 
--		if (primary)
--			*idx = mi->groups[group].max_group_tp_rate[0];
--		else
--			*idx = mi->groups[group].max_group_tp_rate[1];
--		break;
-+		*idx = mi->groups[group].max_group_prob_rate;
- 	}
- }
- 
-@@ -1287,7 +1251,7 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
- 	struct ieee80211_tx_info *info = st->info;
- 	struct minstrel_ht_sta *mi = priv_sta;
- 	struct ieee80211_tx_rate *ar = info->status.rates;
--	struct minstrel_rate_stats *rate, *rate2;
-+	struct minstrel_rate_stats *rate;
- 	struct minstrel_priv *mp = priv;
- 	u32 update_interval = mp->update_interval;
- 	bool last, update = false;
-@@ -1355,18 +1319,13 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
- 		/*
- 		 * check for sudden death of spatial multiplexing,
- 		 * downgrade to a lower number of streams if necessary.
-+		 * only do this for the max_prob_rate to prevent spurious
-+		 * rate fluctuations when the link changes suddenly
- 		 */
--		rate = minstrel_get_ratestats(mi, mi->max_tp_rate[0]);
-+		rate = minstrel_get_ratestats(mi, mi->max_prob_rate);
- 		if (rate->attempts > 30 &&
- 		    rate->success < rate->attempts / 4) {
--			minstrel_downgrade_rate(mi, &mi->max_tp_rate[0], true);
--			update = true;
--		}
--
--		rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate[1]);
--		if (rate2->attempts > 30 &&
--		    rate2->success < rate2->attempts / 4) {
--			minstrel_downgrade_rate(mi, &mi->max_tp_rate[1], false);
-+			minstrel_downgrade_prob_rate(mi, &mi->max_prob_rate);
- 			update = true;
- 		}
- 	}
-diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h
-index f385cf6..1f78a94 100644
---- a/net/mac80211/rc80211_minstrel_ht.h
-+++ b/net/mac80211/rc80211_minstrel_ht.h
-@@ -14,7 +14,7 @@
- 
- /* scaled fraction values */
- #define MINSTREL_SCALE  12
--#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div)
-+#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / (div))
- #define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE)
- 
- #define EWMA_LEVEL	96	/* ewma weighting factor [/EWMA_DIV] */
-diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
-index 42ffd1e..be724c2 100644
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -2767,7 +2767,10 @@ ieee80211_rx_mesh_fast_forward(struct ieee80211_sub_if_data *sdata,
- 			       struct sk_buff *skb, int hdrlen)
- {
- 	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
--	struct ieee80211_mesh_fast_tx *entry = NULL;
-+	struct ieee80211_mesh_fast_tx_key key = {
-+		.type = MESH_FAST_TX_TYPE_FORWARDED
-+	};
-+	struct ieee80211_mesh_fast_tx *entry;
- 	struct ieee80211s_hdr *mesh_hdr;
- 	struct tid_ampdu_tx *tid_tx;
- 	struct sta_info *sta;
-@@ -2776,9 +2779,13 @@ ieee80211_rx_mesh_fast_forward(struct ieee80211_sub_if_data *sdata,
- 
- 	mesh_hdr = (struct ieee80211s_hdr *)(skb->data + sizeof(eth));
- 	if ((mesh_hdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6)
--		entry = mesh_fast_tx_get(sdata, mesh_hdr->eaddr1);
-+		ether_addr_copy(key.addr, mesh_hdr->eaddr1);
- 	else if (!(mesh_hdr->flags & MESH_FLAGS_AE))
--		entry = mesh_fast_tx_get(sdata, skb->data);
-+		ether_addr_copy(key.addr, skb->data);
-+	else
-+		return false;
-+
-+	entry = mesh_fast_tx_get(sdata, &key);
- 	if (!entry)
- 		return false;
- 
-diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
-index 32d050c..411a610 100644
---- a/net/mac80211/sta_info.c
-+++ b/net/mac80211/sta_info.c
-@@ -912,6 +912,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
- 
- 	if (ieee80211_vif_is_mesh(&sdata->vif))
- 		mesh_accept_plinks_update(sdata);
-+	ieee80211_check_fast_xmit(sta);
- 
- 	ieee80211_check_fast_xmit(sta);
- 
-@@ -2354,28 +2355,27 @@ void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
- 					  struct sta_info *sta, u8 ac,
- 					  u16 tx_airtime, bool tx_completed)
- {
-+	atomic_t *counter;
- 	int tx_pending;
- 
- 	if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL))
- 		return;
- 
--	if (!tx_completed) {
--		if (sta)
--			atomic_add(tx_airtime,
--				   &sta->airtime[ac].aql_tx_pending);
-+	if (sta)
-+		counter = &sta->airtime[ac].aql_tx_pending;
-+	else
-+		counter = &local->aql_bc_pending_airtime;
- 
-+	if (!tx_completed) {
-+		atomic_add(tx_airtime, counter);
- 		atomic_add(tx_airtime, &local->aql_total_pending_airtime);
- 		atomic_add(tx_airtime, &local->aql_ac_pending_airtime[ac]);
- 		return;
- 	}
- 
--	if (sta) {
--		tx_pending = atomic_sub_return(tx_airtime,
--					       &sta->airtime[ac].aql_tx_pending);
--		if (tx_pending < 0)
--			atomic_cmpxchg(&sta->airtime[ac].aql_tx_pending,
--				       tx_pending, 0);
--	}
-+	tx_pending = atomic_sub_return(tx_airtime, counter);
-+	if (tx_pending < 0)
-+		atomic_cmpxchg(counter, tx_pending, 0);
- 
- 	atomic_sub(tx_airtime, &local->aql_total_pending_airtime);
- 	tx_pending = atomic_sub_return(tx_airtime,
-@@ -2439,6 +2439,13 @@ static void sta_stats_decode_rate(struct ieee80211_local *local, u32 rate,
- 
- 		sband = local->hw.wiphy->bands[band];
- 
-+		if (!sband) {
-+			wiphy_warn(local->hw.wiphy,
-+				    "Invalid band %d\n",
-+				    band);
-+			break;
-+		}
-+
- 		if (WARN_ON_ONCE(!sband->bitrates))
- 			break;
- 
-diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
-index 141b094..f479d87 100644
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -3978,9 +3978,8 @@ begin:
- encap_out:
- 	info->control.vif = vif;
- 
--	if (tx.sta &&
--	    wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) {
--		bool ampdu = txq->ac != IEEE80211_AC_VO;
-+	if (wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) {
-+		bool ampdu = txq->sta && txq->ac != IEEE80211_AC_VO;
- 		u32 airtime;
- 
- 		airtime = ieee80211_calc_expected_tx_airtime(hw, vif, txq->sta,
-@@ -4043,6 +4042,7 @@ struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
- 	struct ieee80211_txq *ret = NULL;
- 	struct txq_info *txqi = NULL, *head = NULL;
- 	bool found_eligible_txq = false;
-+	bool aql_check;
- 
- 	spin_lock_bh(&local->active_txq_lock[ac]);
- 
-@@ -4066,26 +4066,26 @@ struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
- 	if (!head)
- 		head = txqi;
- 
-+	aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
-+	if (aql_check)
-+		found_eligible_txq = true;
-+
- 	if (txqi->txq.sta) {
- 		struct sta_info *sta = container_of(txqi->txq.sta,
- 						    struct sta_info, sta);
--		bool aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
--		s32 deficit = ieee80211_sta_deficit(sta, txqi->txq.ac);
--
--		if (aql_check)
--			found_eligible_txq = true;
--
--		if (deficit < 0)
-+		if (ieee80211_sta_deficit(sta, txqi->txq.ac) < 0) {
- 			sta->airtime[txqi->txq.ac].deficit +=
--				sta->airtime_weight;
--
--		if (deficit < 0 || !aql_check) {
--			list_move_tail(&txqi->schedule_order,
--				       &local->active_txqs[txqi->txq.ac]);
--			goto begin;
-+				sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
-+			aql_check = false;
- 		}
- 	}
- 
-+	if (!aql_check) {
-+		list_move_tail(&txqi->schedule_order,
-+				   &local->active_txqs[txqi->txq.ac]);
-+		goto begin;
-+	}
-+
- 	if (txqi->schedule_round == local->schedule_round[ac])
- 		goto out;
- 
-@@ -4150,7 +4150,8 @@ bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw,
- 		return true;
- 
- 	if (!txq->sta)
--		return true;
-+		return atomic_read(&local->aql_bc_pending_airtime) <
-+		       local->aql_txq_limit_bc;
- 
- 	if (unlikely(txq->tid == IEEE80211_NUM_TIDS))
- 		return true;
-@@ -4199,15 +4200,15 @@ bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
- 
- 	spin_lock_bh(&local->active_txq_lock[ac]);
- 
--	if (!txqi->txq.sta)
--		goto out;
--
- 	if (list_empty(&txqi->schedule_order))
- 		goto out;
- 
- 	if (!ieee80211_txq_schedule_airtime_check(local, ac))
- 		goto out;
- 
-+	if (!txqi->txq.sta)
-+		goto out;
-+
- 	list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac],
- 				 schedule_order) {
- 		if (iter == txqi)
-@@ -4220,7 +4221,8 @@ bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
- 		}
- 		sta = container_of(iter->txq.sta, struct sta_info, sta);
- 		if (ieee80211_sta_deficit(sta, ac) < 0)
--			sta->airtime[ac].deficit += sta->airtime_weight;
-+			sta->airtime[ac].deficit += sta->airtime_weight <<
-+						    AIRTIME_QUANTUM_SHIFT;
- 		list_move_tail(&iter->schedule_order, &local->active_txqs[ac]);
- 	}
- 
-@@ -4228,7 +4230,7 @@ bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
- 	if (sta->airtime[ac].deficit >= 0)
- 		goto out;
- 
--	sta->airtime[ac].deficit += sta->airtime_weight;
-+	sta->airtime[ac].deficit += sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
- 	list_move_tail(&txqi->schedule_order, &local->active_txqs[ac]);
- 	spin_unlock_bh(&local->active_txq_lock[ac]);
- 
-diff --git a/net/wireless/ap.c b/net/wireless/ap.c
-index 9a9a870..9cd0ab4 100644
---- a/net/wireless/ap.c
-+++ b/net/wireless/ap.c
-@@ -30,6 +30,9 @@ static int ___cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
- 	if (!wdev->links[link_id].ap.beacon_interval)
- 		return -ENOENT;
- 
-+	cfg80211_update_last_available(wdev->wiphy,
-+				       &wdev->links[link_id].ap.chandef);
-+
- 	err = rdev_stop_ap(rdev, dev, link_id);
- 	if (!err) {
- 		wdev->conn_owner_nlportid = 0;
-@@ -41,9 +44,6 @@ static int ___cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
- 		if (notify)
- 			nl80211_send_ap_stopped(wdev, link_id);
- 
--		/* Should we apply the grace period during beaconing interface
--		 * shutdown also?
--		 */
- 		cfg80211_sched_dfs_chan_update(rdev);
- 	}
- 
-diff --git a/net/wireless/chan.c b/net/wireless/chan.c
-index 14c27bc..4bac395 100644
---- a/net/wireless/chan.c
-+++ b/net/wireless/chan.c
-@@ -560,6 +560,8 @@ static void cfg80211_set_chans_dfs_state(struct wiphy *wiphy, u32 center_freq,
- 
- 		c->dfs_state = dfs_state;
- 		c->dfs_state_entered = jiffies;
-+		if (dfs_state == NL80211_DFS_AVAILABLE)
-+			c->dfs_state_last_available = jiffies;
- 	}
- }
- 
-@@ -1049,6 +1051,49 @@ static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy,
- 	return true;
- }
- 
-+static void
-+__cfg80211_update_last_available(struct wiphy *wiphy,
-+					 u32 center_freq,
-+					 u32 bandwidth)
-+{
-+	struct ieee80211_channel *c;
-+	u32 freq, start_freq, end_freq;
-+
-+	start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
-+	end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
-+
-+	/*
-+	 * Check entire range of channels for the bandwidth.
-+	 * If any channel in between is disabled or has not
-+	 * had gone through CAC return false
-+	 */
-+	for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
-+		c = ieee80211_get_channel_khz(wiphy, freq);
-+		if (!c)
-+			return;
-+
-+		c->dfs_state_last_available = jiffies;
-+	}
-+}
-+
-+void cfg80211_update_last_available(struct wiphy *wiphy,
-+				    const struct cfg80211_chan_def *chandef)
-+{
-+	int width;
-+
-+	width = cfg80211_chandef_get_width(chandef);
-+	if (width < 0)
-+		return;
-+
-+	__cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq1),
-+						 width);
-+	if (chandef->width != NL80211_CHAN_WIDTH_80P80)
-+	    return;
-+
-+	__cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq2),
-+						 width);
-+}
-+
- static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
- 				const struct cfg80211_chan_def *chandef)
- {
-diff --git a/net/wireless/core.c b/net/wireless/core.c
-index 0cd5c78..ac9417e 100644
---- a/net/wireless/core.c
-+++ b/net/wireless/core.c
-@@ -662,21 +662,6 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
- 				    c->limits[j].max > 1))
- 				return -EINVAL;
- 
--			/*
--			 * This isn't well-defined right now. If you have an
--			 * IBSS interface, then its beacon interval may change
--			 * by joining other networks, and nothing prevents it
--			 * from doing that.
--			 * So technically we probably shouldn't even allow AP
--			 * and IBSS in the same interface, but it seems that
--			 * some drivers support that, possibly only with fixed
--			 * beacon intervals for IBSS.
--			 */
--			if (WARN_ON(types & BIT(NL80211_IFTYPE_ADHOC) &&
--				    c->beacon_int_min_gcd)) {
--				return -EINVAL;
--			}
--
- 			cnt += c->limits[j].max;
- 			/*
- 			 * Don't advertise an unsupported type
-diff --git a/net/wireless/core.h b/net/wireless/core.h
-index 2e19279..7bef6b0 100644
---- a/net/wireless/core.h
-+++ b/net/wireless/core.h
-@@ -467,6 +467,8 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy,
- 			    enum nl80211_dfs_state dfs_state);
- 
- void cfg80211_dfs_channels_update_work(struct work_struct *work);
-+void cfg80211_update_last_available(struct wiphy *wiphy,
-+				    const struct cfg80211_chan_def *chandef);
- 
- void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
- 
-diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
-index 3b0fe7c..c7e62eb 100644
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -1037,6 +1037,8 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
- 			if (c->dfs_state == NL80211_DFS_UNAVAILABLE) {
- 				time_dfs_update = IEEE80211_DFS_MIN_NOP_TIME_MS;
- 				radar_event = NL80211_RADAR_NOP_FINISHED;
-+				timeout = c->dfs_state_entered +
-+					  msecs_to_jiffies(time_dfs_update);
- 			} else {
- 				if (regulatory_pre_cac_allowed(wiphy) ||
- 				    cfg80211_any_wiphy_oper_chan(wiphy, c))
-@@ -1044,11 +1046,10 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
- 
- 				time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS;
- 				radar_event = NL80211_RADAR_PRE_CAC_EXPIRED;
-+				timeout = c->dfs_state_last_available +
-+					  msecs_to_jiffies(time_dfs_update);
- 			}
- 
--			timeout = c->dfs_state_entered +
--				  msecs_to_jiffies(time_dfs_update);
--
- 			if (time_after_eq(jiffies, timeout)) {
- 				c->dfs_state = NL80211_DFS_USABLE;
- 				c->dfs_state_entered = jiffies;
-diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
-index 1387106..49aac4c 100644
---- a/net/wireless/sysfs.c
-+++ b/net/wireless/sysfs.c
-@@ -24,18 +24,35 @@ static inline struct cfg80211_registered_device *dev_to_rdev(
- 	return container_of(dev, struct cfg80211_registered_device, wiphy.dev);
- }
- 
--#define SHOW_FMT(name, fmt, member)					\
-+#define SHOW_FMT(name, fmt, member, mode)				\
- static ssize_t name ## _show(struct device *dev,			\
- 			      struct device_attribute *attr,		\
- 			      char *buf)				\
- {									\
- 	return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member);	\
- }									\
--static DEVICE_ATTR_RO(name)
-+static DEVICE_ATTR_##mode(name)
- 
--SHOW_FMT(index, "%d", wiphy_idx);
--SHOW_FMT(macaddress, "%pM", wiphy.perm_addr);
--SHOW_FMT(address_mask, "%pM", wiphy.addr_mask);
-+static ssize_t macaddress_store(struct device *dev,
-+				struct device_attribute *attr,
-+				const char *buf, size_t len)
-+{
-+	u8 mac[ETH_ALEN];
-+
-+	if (!mac_pton(buf, mac))
-+		return -EINVAL;
-+
-+	if (buf[3 * ETH_ALEN - 1] && buf[3 * ETH_ALEN - 1] != '\n')
-+		return -EINVAL;
-+
-+	memcpy(dev_to_rdev(dev)->wiphy.perm_addr, mac, ETH_ALEN);
-+
-+	return strnlen(buf, len);
-+}
-+
-+SHOW_FMT(index, "%d", wiphy_idx, RO);
-+SHOW_FMT(macaddress, "%pM", wiphy.perm_addr, RW);
-+SHOW_FMT(address_mask, "%pM", wiphy.addr_mask, RO);
- 
- static ssize_t name_show(struct device *dev,
- 			 struct device_attribute *attr,
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0008-backports-additional-fixes-for-5.4.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0008-backports-additional-fixes-for-5.4.patch
new file mode 100644
index 0000000..fb741ce
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0008-backports-additional-fixes-for-5.4.patch
@@ -0,0 +1,351 @@
+From 583499fbb339855db968cbee875b3d267ee7eedf Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 18 Jul 2024 12:05:11 +0800
+Subject: [PATCH 08/89] backports: additional fixes for 5.4
+
+Modify some parts to let current backports work on kernel 5.4.
+
+Including:
+Revert "wifi: mac80211: Move stats allocation to core"
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ backport-include/linux/of_net.h               | 26 +++++++++++++++++++
+ backport-include/linux/skbuff.h               |  8 ++++++
+ backport-include/linux/soc/mediatek/mtk_wed.h |  2 +-
+ backport-include/linux/thermal.h              |  4 +--
+ backport-include/net/dropreason-core.h        |  6 +++++
+ backport-include/net/netlink.h                | 26 ++++++++++++++++++-
+ net/mac80211/iface.c                          | 17 ++++++++----
+ net/mac80211/trace.h                          |  2 +-
+ net/wireless/nl80211.c                        | 22 +++++++++++++++-
+ net/wireless/trace.h                          |  2 +-
+ 10 files changed, 103 insertions(+), 12 deletions(-)
+ create mode 100644 backport-include/linux/of_net.h
+
+diff --git a/backport-include/linux/of_net.h b/backport-include/linux/of_net.h
+new file mode 100644
+index 0000000..9b9276f
+--- /dev/null
++++ b/backport-include/linux/of_net.h
+@@ -0,0 +1,26 @@
++#ifndef _BP_OF_NET_H
++#define _BP_OF_NET_H
++#include_next <linux/of_net.h>
++#include <linux/version.h>
++#include <linux/etherdevice.h>
++
++/* The behavior of of_get_mac_address() changed in kernel 5.2, it now
++ * returns an error code and not NULL in case of an error.
++ */
++#if LINUX_VERSION_IS_LESS(5,13,0)
++static inline int backport_of_get_mac_address(struct device_node *np, u8 *mac_out)
++{
++	const void *mac = of_get_mac_address(np);
++
++	if (!mac)
++		return -ENODEV;
++	if (IS_ERR(mac))
++		return PTR_ERR(mac);
++	ether_addr_copy(mac_out, mac);
++	
++	return 0;
++}
++#define of_get_mac_address LINUX_BACKPORT(of_get_mac_address)
++#endif /* < 5.2 */
++
++#endif /* _BP_OF_NET_H */
+diff --git a/backport-include/linux/skbuff.h b/backport-include/linux/skbuff.h
+index b40d25c..8c3d839 100644
+--- a/backport-include/linux/skbuff.h
++++ b/backport-include/linux/skbuff.h
+@@ -88,4 +88,12 @@ static inline struct sk_buff *LINUX_BACKPORT(skb_recv_datagram)(struct sock *sk,
+ #define skb_recv_datagram LINUX_BACKPORT(skb_recv_datagram)
+ #endif /* < 5.17 */
+ 
++#if LINUX_VERSION_IS_LESS(6,0,0)
++#define kfree_skb_reason LINUX_BACKPORT(kfree_skb_reason)
++static inline void kfree_skb_reason(struct sk_buff *skb, int reason)
++{
++	return kfree_skb(skb);
++}
++#endif /* < 6.0.0 */
++
+ #endif /* __BACKPORT_SKBUFF_H */
+diff --git a/backport-include/linux/soc/mediatek/mtk_wed.h b/backport-include/linux/soc/mediatek/mtk_wed.h
+index 46caa89..cd96b4a 100644
+--- a/backport-include/linux/soc/mediatek/mtk_wed.h
++++ b/backport-include/linux/soc/mediatek/mtk_wed.h
+@@ -2,7 +2,7 @@
+ #define __BACKPORT_MTK_WED_H
+ #include <linux/version.h>
+ 
+-#if LINUX_VERSION_IS_GEQ(5,19,0)
++#if LINUX_VERSION_IS_GEQ(5,4,0)
+ #include_next <linux/soc/mediatek/mtk_wed.h>
+ #else
+ #include <linux/kernel.h>
+diff --git a/backport-include/linux/thermal.h b/backport-include/linux/thermal.h
+index d6b8a8f..e78f2fb 100644
+--- a/backport-include/linux/thermal.h
++++ b/backport-include/linux/thermal.h
+@@ -37,7 +37,7 @@ static inline void *thermal_zone_device_priv(struct thermal_zone_device *tzd)
+ }
+ #endif
+ 
+-#if LINUX_VERSION_IS_LESS(6,6,0)
++#if LINUX_VERSION_IS_LESS(5,4,0)
+ #define for_each_thermal_trip LINUX_BACKPORT(for_each_thermal_trip)
+ static inline int for_each_thermal_trip(struct thermal_zone_device *tz,
+ 					int (*cb)(struct thermal_trip *, void *),
+@@ -56,6 +56,6 @@ static inline int for_each_thermal_trip(struct thermal_zone_device *tz,
+ 
+ 	return 0;
+ }
+-#endif /* < 6.6 */
++#endif /* < 5.4 */
+ 
+ #endif /* __BACKPORT_LINUX_THERMAL_H */
+diff --git a/backport-include/net/dropreason-core.h b/backport-include/net/dropreason-core.h
+index ab8532e..31c60b9 100644
+--- a/backport-include/net/dropreason-core.h
++++ b/backport-include/net/dropreason-core.h
+@@ -13,6 +13,12 @@
+ 
+ #include <linux/version.h>
+ 
++/* backport for 5.4 */
++#if LINUX_VERSION_IS_LESS(5,5,0)
++#define SKB_DROP_REASON_NOT_SPECIFIED 2
++#define SKB_DROP_REASON_MAX 69
++#endif
++
+ #if LINUX_VERSION_IS_LESS(5,18,0)
+ #define SKB_NOT_DROPPED_YET SKB_DROP_REASON_MAX
+ #endif
+diff --git a/backport-include/net/netlink.h b/backport-include/net/netlink.h
+index 0a6740b..68bf3e8 100644
+--- a/backport-include/net/netlink.h
++++ b/backport-include/net/netlink.h
+@@ -348,6 +348,30 @@ enum nla_policy_validation {
+ }
+ #endif /* < 4.20 */
+ 
++#if LINUX_VERSION_IS_GEQ(5,10,0)
++#define __NLA_IS_UINT_TYPE(tp)					\
++	(tp == NLA_U8 || tp == NLA_U16 || tp == NLA_U32 ||	\
++	 tp == NLA_U64 || tp == NLA_UINT ||			\
++	 tp == NLA_BE16 || tp == NLA_BE32)
++#define __NLA_IS_SINT_TYPE(tp)						\
++	(tp == NLA_S8 || tp == NLA_S16 || tp == NLA_S32 || tp == NLA_S64 || \
++	 tp == NLA_SINT)
++
++#define NLA_ENSURE_UINT_TYPE(tp)			\
++	(__NLA_ENSURE(__NLA_IS_UINT_TYPE(tp)) + tp)
++#define NLA_ENSURE_UINT_OR_BINARY_TYPE(tp)		\
++	(__NLA_ENSURE(__NLA_IS_UINT_TYPE(tp) ||	\
++		      tp == NLA_MSECS ||		\
++		      tp == NLA_BINARY) + tp)
++#define NLA_ENSURE_SINT_TYPE(tp)			\
++	(__NLA_ENSURE(__NLA_IS_SINT_TYPE(tp)) + tp)
++#define NLA_ENSURE_INT_OR_BINARY_TYPE(tp)		\
++	(__NLA_ENSURE(__NLA_IS_UINT_TYPE(tp) ||		\
++		      __NLA_IS_SINT_TYPE(tp) ||		\
++		      tp == NLA_MSECS ||		\
++		      tp == NLA_BINARY) + tp)
++#endif
++
+ #if LINUX_VERSION_IS_LESS(5,10,0)
+ // pre-declare all the minimum lengths in use
+ #define MIN_LEN_VALIDATION(n)						\
+@@ -382,7 +406,7 @@ MIN_LEN_VALIDATION(42)
+ #define NLA_POLICY_FULL_RANGE(tp, _range) {		\
+ 	.type = NLA_ENSURE_UINT_OR_BINARY_TYPE(tp),	\
+ 	.validation_type = NLA_VALIDATE_RANGE_PTR,	\
+-	.range = (struct netlink_range_validation *)_range, \
+ }
+ 
++/* .range = (struct netlink_range_validation *)_range, */
+ #endif /* __BACKPORT_NET_NETLINK_H */
+diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
+index ee93439..b63a3c4 100644
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -873,7 +873,6 @@ static const struct net_device_ops ieee80211_monitorif_ops = {
+ 	.ndo_select_queue	= ieee80211_monitor_select_queue,
+ };
+ 
+-#if LINUX_VERSION_IS_GEQ(5,13,0)
+ static int ieee80211_netdev_fill_forward_path(struct net_device_path_ctx *ctx,
+ 					      struct net_device_path *path)
+ {
+@@ -931,7 +930,6 @@ out:
+ 
+ 	return ret;
+ }
+-#endif /* LINUX_VERSION_IS_GEQ(5,13,0) */
+ 
+ static const struct net_device_ops ieee80211_dataif_8023_ops = {
+ 	.ndo_open		= ieee80211_open,
+@@ -940,9 +938,7 @@ static const struct net_device_ops ieee80211_dataif_8023_ops = {
+ 	.ndo_start_xmit		= ieee80211_subif_start_xmit_8023,
+ 	.ndo_set_rx_mode	= ieee80211_set_multicast_list,
+ 	.ndo_set_mac_address	= ieee80211_change_mac,
+-#if LINUX_VERSION_IS_GEQ(5,13,0)
+ 	.ndo_fill_forward_path	= ieee80211_netdev_fill_forward_path,
+-#endif
+ 	.ndo_setup_tc		= ieee80211_netdev_setup_tc,
+ };
+ 
+@@ -1451,6 +1447,11 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
+ 	return res;
+ }
+ 
++static void ieee80211_if_free(struct net_device *dev)
++{
++	free_percpu(dev->tstats);
++}
++
+ static void ieee80211_if_setup(struct net_device *dev)
+ {
+ 	ether_setup(dev);
+@@ -1458,6 +1459,7 @@ static void ieee80211_if_setup(struct net_device *dev)
+ 	dev->priv_flags |= IFF_NO_QUEUE;
+ 	dev->netdev_ops = &ieee80211_dataif_ops;
+ 	dev->needs_free_netdev = true;
++	dev->priv_destructor = ieee80211_if_free;
+ }
+ 
+ static void ieee80211_iface_process_skb(struct ieee80211_local *local,
+@@ -2090,7 +2092,11 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
+ 
+ 		dev_net_set(ndev, wiphy_net(local->hw.wiphy));
+ 
+-		ndev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS;
++		ndev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
++		if (!ndev->tstats) {
++			free_netdev(ndev);
++			return -ENOMEM;
++		}
+ 
+ 		ndev->needed_headroom = local->tx_headroom +
+ 					4*6 /* four MAC addresses */
+@@ -2103,6 +2109,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
+ 
+ 		ret = dev_alloc_name(ndev, ndev->name);
+ 		if (ret < 0) {
++			ieee80211_if_free(ndev);
+ 			free_netdev(ndev);
+ 			return ret;
+ 		}
+diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
+index dc498cd..78ffd3b 100644
+--- a/net/mac80211/trace.h
++++ b/net/mac80211/trace.h
+@@ -33,7 +33,7 @@
+ 			__string(vif_name, sdata->name)
+ #define VIF_ASSIGN	__entry->vif_type = sdata->vif.type; __entry->sdata = sdata;	\
+ 			__entry->p2p = sdata->vif.p2p;					\
+-			__assign_str(vif_name)
++			__assign_str(vif_name, sdata->name)
+ #define VIF_PR_FMT	" vif:%s(%d%s)"
+ #define VIF_PR_ARG	__get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : ""
+ 
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index 6f7273a..6489fe9 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -285,6 +285,7 @@ static int validate_ie_attr(const struct nlattr *attr,
+ 	return -EINVAL;
+ }
+ 
++#if LINUX_VERSION_IS_GEQ(5,10,0)
+ static int validate_he_capa(const struct nlattr *attr,
+ 			    struct netlink_ext_ack *extack)
+ {
+@@ -293,6 +294,7 @@ static int validate_he_capa(const struct nlattr *attr,
+ 
+ 	return 0;
+ }
++#endif
+ 
+ /* policy for the attributes */
+ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR];
+@@ -465,6 +467,7 @@ nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] = {
+ 	[NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
+ };
+ 
++#if LINUX_VERSION_IS_GEQ(5,8,0)
+ static const struct netlink_range_validation nl80211_punct_bitmap_range = {
+ 	.min = 0,
+ 	.max = 0xffff,
+@@ -473,6 +476,7 @@ static const struct netlink_range_validation nl80211_punct_bitmap_range = {
+ static const struct netlink_range_validation q_range = {
+ 	.max = INT_MAX,
+ };
++#endif
+ 
+ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
+ 	[0] = { .strict_start_type = NL80211_ATTR_HE_OBSS_PD },
+@@ -772,10 +776,19 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
+ 
+ 	[NL80211_ATTR_TXQ_LIMIT] = { .type = NLA_U32 },
+ 	[NL80211_ATTR_TXQ_MEMORY_LIMIT] = { .type = NLA_U32 },
++#if LINUX_VERSION_IS_GEQ(5,8,0)
+ 	[NL80211_ATTR_TXQ_QUANTUM] = NLA_POLICY_FULL_RANGE(NLA_U32, &q_range),
++#else
++	[NL80211_ATTR_TXQ_QUANTUM] = { .type = NLA_U32 },
++#endif
++#if LINUX_VERSION_IS_GEQ(5,10,0)
+ 	[NL80211_ATTR_HE_CAPABILITY] =
+ 		NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_he_capa,
+ 				       NL80211_HE_MAX_CAPABILITY_LEN),
++#else
++	[NL80211_ATTR_HE_CAPABILITY] = { .type = NLA_BINARY,
++					 .len = NL80211_HE_MAX_CAPABILITY_LEN },
++#endif
+ 	[NL80211_ATTR_FTM_RESPONDER] =
+ 		NLA_POLICY_NESTED(nl80211_ftm_responder_policy),
+ 	[NL80211_ATTR_TIMEOUT] = NLA_POLICY_MIN(NLA_U32, 1),
+@@ -821,8 +834,12 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
+ 	[NL80211_ATTR_MBSSID_ELEMS] = { .type = NLA_NESTED },
+ 	[NL80211_ATTR_RADAR_BACKGROUND] = { .type = NLA_FLAG },
+ 	[NL80211_ATTR_AP_SETTINGS_FLAGS] = { .type = NLA_U32 },
++#if LINUX_VERSION_IS_GEQ(5,10,0)
+ 	[NL80211_ATTR_EHT_CAPABILITY] =
+ 		NLA_POLICY_BINARY_RANGE(NL80211_EHT_MIN_CAPABILITY_LEN, NL80211_EHT_MAX_CAPABILITY_LEN),
++#else
++	[NL80211_ATTR_EHT_CAPABILITY] = { .type = NLA_BINARY, .len = NL80211_EHT_MAX_CAPABILITY_LEN },
++#endif
+ 		[NL80211_ATTR_DISABLE_EHT] = { .type = NLA_FLAG },
+ 		[NL80211_ATTR_MLO_LINKS] =
+ 		NLA_POLICY_NESTED_ARRAY(nl80211_policy),
+@@ -832,8 +849,11 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
+ 	[NL80211_ATTR_MLO_SUPPORT] = { .type = NLA_FLAG },
+ 	[NL80211_ATTR_MAX_NUM_AKM_SUITES] = { .type = NLA_REJECT },
+ 	[NL80211_ATTR_PUNCT_BITMAP] =
++#if LINUX_VERSION_IS_GEQ(5,8,0)
+ 		NLA_POLICY_FULL_RANGE(NLA_U32, &nl80211_punct_bitmap_range),
+-
++#else
++		{ .type = NLA_U32 },
++#endif
+ 	[NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS] = { .type = NLA_U16 },
+ 	[NL80211_ATTR_HW_TIMESTAMP_ENABLED] = { .type = NLA_FLAG },
+ 	[NL80211_ATTR_EMA_RNR_ELEMS] = { .type = NLA_NESTED },
+diff --git a/net/wireless/trace.h b/net/wireless/trace.h
+index e52164d..6f2e9a5 100644
+--- a/net/wireless/trace.h
++++ b/net/wireless/trace.h
+@@ -446,7 +446,7 @@ TRACE_EVENT(rdev_add_virtual_intf,
+ 	),
+ 	TP_fast_assign(
+ 		WIPHY_ASSIGN;
+-		__assign_str(vir_intf_name);
++		__assign_str(vir_intf_name, name ? name : "<noname>");
+ 		__entry->type = type;
+ 	),
+ 	TP_printk(WIPHY_PR_FMT ", virtual intf name: %s, type: %d",
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0008-mtk-mac80211-do-not-setup-twt-when-twt-responder-is-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0008-mtk-mac80211-do-not-setup-twt-when-twt-responder-is-.patch
deleted file mode 100644
index b7d52f5..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0008-mtk-mac80211-do-not-setup-twt-when-twt-responder-is-.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From ab5065afa6302d7241b2252a6f2ebdba8dc9763b Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Tue, 18 Jan 2022 20:29:44 +0800
-Subject: [PATCH 08/61] mtk: mac80211: do not setup twt when twt responder is
- false
-
----
- net/mac80211/rx.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
-index be724c2..89a1199 100644
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -3458,6 +3458,9 @@ ieee80211_process_rx_twt_action(struct ieee80211_rx_data *rx)
- 	if (sdata->vif.type != NL80211_IFTYPE_AP)
- 		return false;
- 
-+	if (!sdata->vif.bss_conf.twt_responder)
-+		return false;
-+
- 	if (!rx->local->ops->add_twt_setup)
- 		return false;
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0009-bp-Revert-wifi-mac80211-move-radar-detect-work-to-sd.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0009-bp-Revert-wifi-mac80211-move-radar-detect-work-to-sd.patch
new file mode 100644
index 0000000..201e792
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0009-bp-Revert-wifi-mac80211-move-radar-detect-work-to-sd.patch
@@ -0,0 +1,158 @@
+From 52b5f8547ca2c00bc4c491901d56340205a241ac Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 11 Jul 2024 09:21:40 +0530
+Subject: [PATCH 09/89] bp: Revert "wifi: mac80211: move radar detect work to
+ sdata"
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This reverts commit ce9e660ef32e ("wifi: mac80211: move radar detect work to sdata").
+
+To enable radar detection with MLO, it’s essential to handle it on a
+per-link basis. This is because when using MLO, multiple links may already
+be active and beaconing. In this scenario, another link should be able to
+initiate a radar detection. Also, if underlying links are associated with
+different hardware devices but grouped together for MLO, they could
+potentially start radar detection simultaneously. Therefore, it makes
+sense to manage radar detection settings separately for each link by moving
+them back to a per-link data structure.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ net/mac80211/cfg.c         | 6 +++---
+ net/mac80211/ieee80211_i.h | 3 +--
+ net/mac80211/iface.c       | 4 +---
+ net/mac80211/link.c        | 2 ++
+ net/mac80211/mlme.c        | 9 +++++----
+ net/mac80211/util.c        | 2 +-
+ 6 files changed, 13 insertions(+), 13 deletions(-)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 1bf8626..18b9f7c 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1658,7 +1658,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
+ 
+ 	if (sdata->wdev.cac_started) {
+ 		chandef = link_conf->chanreq.oper;
+-		wiphy_delayed_work_cancel(wiphy, &sdata->dfs_cac_timer_work);
++		wiphy_delayed_work_cancel(wiphy, &link->dfs_cac_timer_work);
+ 		cfg80211_cac_event(sdata->dev, &chandef,
+ 				   NL80211_RADAR_CAC_ABORTED,
+ 				   GFP_KERNEL);
+@@ -3478,7 +3478,7 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
+ 	if (err)
+ 		goto out_unlock;
+ 
+-	wiphy_delayed_work_queue(wiphy, &sdata->dfs_cac_timer_work,
++	wiphy_delayed_work_queue(wiphy, &sdata->deflink.dfs_cac_timer_work,
+ 				 msecs_to_jiffies(cac_time_ms));
+ 
+  out_unlock:
+@@ -3495,7 +3495,7 @@ static void ieee80211_end_cac(struct wiphy *wiphy,
+ 
+ 	list_for_each_entry(sdata, &local->interfaces, list) {
+ 		wiphy_delayed_work_cancel(wiphy,
+-					  &sdata->dfs_cac_timer_work);
++					  &sdata->deflink.dfs_cac_timer_work);
+ 
+ 		if (sdata->wdev.cac_started) {
+ 			ieee80211_link_release_channel(&sdata->deflink);
+diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
+index c9d6743..f7c9892 100644
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -1073,6 +1073,7 @@ struct ieee80211_link_data {
+ 	int ap_power_level; /* in dBm */
+ 
+ 	bool radar_required;
++	struct wiphy_delayed_work dfs_cac_timer_work;
+ 
+ 	union {
+ 		struct ieee80211_link_data_managed mgd;
+@@ -1171,8 +1172,6 @@ struct ieee80211_sub_if_data {
+ 	struct ieee80211_link_data deflink;
+ 	struct ieee80211_link_data __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
+ 
+-	struct wiphy_delayed_work dfs_cac_timer_work;
+-
+ 	/* for ieee80211_set_active_links_async() */
+ 	struct wiphy_work activate_links_work;
+ 	u16 desired_active_links;
+diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
+index b63a3c4..00d75e0 100644
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -550,7 +550,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
+ 	wiphy_work_cancel(local->hw.wiphy,
+ 			  &sdata->deflink.color_change_finalize_work);
+ 	wiphy_delayed_work_cancel(local->hw.wiphy,
+-				  &sdata->dfs_cac_timer_work);
++				  &sdata->deflink.dfs_cac_timer_work);
+ 
+ 	if (sdata->wdev.cac_started) {
+ 		chandef = sdata->vif.bss_conf.chanreq.oper;
+@@ -1735,8 +1735,6 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
+ 	wiphy_work_init(&sdata->work, ieee80211_iface_work);
+ 	wiphy_work_init(&sdata->activate_links_work,
+ 			ieee80211_activate_links_work);
+-	wiphy_delayed_work_init(&sdata->dfs_cac_timer_work,
+-				ieee80211_dfs_cac_timer_work);
+ 
+ 	switch (type) {
+ 	case NL80211_IFTYPE_P2P_GO:
+diff --git a/net/mac80211/link.c b/net/mac80211/link.c
+index 1a211b8..b437896 100644
+--- a/net/mac80211/link.c
++++ b/net/mac80211/link.c
+@@ -45,6 +45,8 @@ void ieee80211_link_init(struct ieee80211_sub_if_data *sdata,
+ 			  ieee80211_color_collision_detection_work);
+ 	INIT_LIST_HEAD(&link->assigned_chanctx_list);
+ 	INIT_LIST_HEAD(&link->reserved_chanctx_list);
++	wiphy_delayed_work_init(&link->dfs_cac_timer_work,
++				ieee80211_dfs_cac_timer_work);
+ 
+ 	if (!deflink) {
+ 		switch (sdata->vif.type) {
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 4779a18..5b83e7b 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -3031,15 +3031,16 @@ void ieee80211_dynamic_ps_timer(struct timer_list *t)
+ 
+ void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work)
+ {
+-	struct ieee80211_sub_if_data *sdata =
+-		container_of(work, struct ieee80211_sub_if_data,
++	struct ieee80211_link_data *link =
++		container_of(work, struct ieee80211_link_data,
+ 			     dfs_cac_timer_work.work);
+-	struct cfg80211_chan_def chandef = sdata->vif.bss_conf.chanreq.oper;
++	struct cfg80211_chan_def chandef = link->conf->chanreq.oper;
++	struct ieee80211_sub_if_data *sdata = link->sdata;
+ 
+ 	lockdep_assert_wiphy(sdata->local->hw.wiphy);
+ 
+ 	if (sdata->wdev.cac_started) {
+-		ieee80211_link_release_channel(&sdata->deflink);
++		ieee80211_link_release_channel(link);
+ 		cfg80211_cac_event(sdata->dev, &chandef,
+ 				   NL80211_RADAR_CAC_FINISHED,
+ 				   GFP_KERNEL);
+diff --git a/net/mac80211/util.c b/net/mac80211/util.c
+index ced19ce..77d1e44 100644
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -3460,7 +3460,7 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
+ 
+ 	list_for_each_entry(sdata, &local->interfaces, list) {
+ 		wiphy_delayed_work_cancel(local->hw.wiphy,
+-					  &sdata->dfs_cac_timer_work);
++					  &sdata->deflink.dfs_cac_timer_work);
+ 
+ 		if (sdata->wdev.cac_started) {
+ 			chandef = sdata->vif.bss_conf.chanreq.oper;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0009-mtk-cfg80211-extend-CAC-time-for-weather-radar-chann.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0009-mtk-cfg80211-extend-CAC-time-for-weather-radar-chann.patch
deleted file mode 100644
index d5a498d..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0009-mtk-cfg80211-extend-CAC-time-for-weather-radar-chann.patch
+++ /dev/null
@@ -1,61 +0,0 @@
-From e27e641fd771a6d882a61a889b9295cdab5dcc72 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Tue, 29 Mar 2022 16:06:30 +0800
-Subject: [PATCH 09/61] mtk: cfg80211: extend CAC time for weather radar
- channels
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- include/net/cfg80211.h | 1 +
- net/wireless/chan.c    | 7 +++++++
- net/wireless/nl80211.c | 3 +++
- 3 files changed, 11 insertions(+)
-
-diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
-index 4c5daf9..d987b62 100644
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -163,6 +163,7 @@ enum ieee80211_channel_flags {
- 	(IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
- 
- #define IEEE80211_DFS_MIN_CAC_TIME_MS		60000
-+#define IEEE80211_DFS_WEATHER_MIN_CAC_TIME_MS	600000
- #define IEEE80211_DFS_MIN_NOP_TIME_MS		(30 * 60 * 1000)
- 
- /**
-diff --git a/net/wireless/chan.c b/net/wireless/chan.c
-index 4bac395..2224329 100644
---- a/net/wireless/chan.c
-+++ b/net/wireless/chan.c
-@@ -1152,6 +1152,13 @@ static unsigned int cfg80211_get_chans_dfs_cac_time(struct wiphy *wiphy,
- 		if (!(c->flags & IEEE80211_CHAN_RADAR))
- 			continue;
- 
-+		/* weather radar in ETSI */
-+		if (reg_get_dfs_region(wiphy) == NL80211_DFS_ETSI &&
-+		    freq >= MHZ_TO_KHZ(5600) && freq <= MHZ_TO_KHZ(5640) &&
-+		    dfs_cac_ms < IEEE80211_DFS_WEATHER_MIN_CAC_TIME_MS &&
-+		    c->dfs_state == NL80211_DFS_USABLE)
-+			dfs_cac_ms = IEEE80211_DFS_WEATHER_MIN_CAC_TIME_MS;
-+
- 		if (c->dfs_cac_ms > dfs_cac_ms)
- 			dfs_cac_ms = c->dfs_cac_ms;
- 	}
-diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
-index 3d11013..b8c8848 100644
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -10019,6 +10019,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
- 	if (WARN_ON(!cac_time_ms))
- 		cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
- 
-+	pr_info("%s: region = %u, center freq1 = %u, center freq2 = %u, cac time ms = %u\n",
-+		__func__, dfs_region, chandef.center_freq1, chandef.center_freq2, cac_time_ms);
-+
- 	err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms);
- 	if (!err) {
- 		wdev->links[0].ap.chandef = chandef;
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0010-bp-wifi-mac80211-remove-label-usage-in-ieee80211_sta.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0010-bp-wifi-mac80211-remove-label-usage-in-ieee80211_sta.patch
new file mode 100644
index 0000000..c2405f1
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0010-bp-wifi-mac80211-remove-label-usage-in-ieee80211_sta.patch
@@ -0,0 +1,56 @@
+From f7ecd5b188e49fd8edb08b2d5d14f98ccfa0a9b5 Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 11 Jul 2024 09:21:41 +0530
+Subject: [PATCH 10/89] bp: wifi: mac80211: remove label usage in
+ ieee80211_start_radar_detection()
+
+After locks rework [1], ieee80211_start_radar_detection() function is no
+longer acquiring any lock as such explicitly. Hence, it is not unlocking
+anything as well. However, label "out_unlock" is still used which creates
+confusion. Also, now there is no need of goto label as such.
+
+Get rid of the goto logic and use direct return statements.
+
+[1]: https://lore.kernel.org/all/20230828135928.b1c6efffe9ad.I4aec875e25abc9ef0b5ad1e70b5747fd483fbd3c@changeid/
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ net/mac80211/cfg.c | 11 ++++-------
+ 1 file changed, 4 insertions(+), 7 deletions(-)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 18b9f7c..ec47ab1 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -3464,10 +3464,8 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
+ 
+ 	lockdep_assert_wiphy(local->hw.wiphy);
+ 
+-	if (!list_empty(&local->roc_list) || local->scanning) {
+-		err = -EBUSY;
+-		goto out_unlock;
+-	}
++	if (!list_empty(&local->roc_list) || local->scanning)
++		return -EBUSY;
+ 
+ 	/* whatever, but channel contexts should not complain about that one */
+ 	sdata->deflink.smps_mode = IEEE80211_SMPS_OFF;
+@@ -3476,13 +3474,12 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
+ 	err = ieee80211_link_use_channel(&sdata->deflink, &chanreq,
+ 					 IEEE80211_CHANCTX_SHARED);
+ 	if (err)
+-		goto out_unlock;
++		return err;
+ 
+ 	wiphy_delayed_work_queue(wiphy, &sdata->deflink.dfs_cac_timer_work,
+ 				 msecs_to_jiffies(cac_time_ms));
+ 
+- out_unlock:
+-	return err;
++	return 0;
+ }
+ 
+ static void ieee80211_end_cac(struct wiphy *wiphy,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0010-mtk-mac80211-it-s-invalid-case-when-frag_threshold-i.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0010-mtk-mac80211-it-s-invalid-case-when-frag_threshold-i.patch
deleted file mode 100644
index 1fefb28..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0010-mtk-mac80211-it-s-invalid-case-when-frag_threshold-i.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From 639c4598fd67a554c8502d113eb64ef3cf7660d4 Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Fri, 1 Apr 2022 09:15:21 +0800
-Subject: [PATCH 10/61] mtk: mac80211: it's invalid case when frag_threshold is
- greater than 2346
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
----
- net/wireless/nl80211.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
-index b8c8848..54e19b1 100644
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -3701,6 +3701,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
- 			goto out;
- 		}
- 
-+		if (frag_threshold >= 2346)
-+			frag_threshold = (u32) -1;
-+
- 		if (frag_threshold != (u32) -1) {
- 			/*
- 			 * Fragments (apart from the last one) are required to
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0011-bp-wifi-trace-unlink-rdev_end_cac-trace-event-from-w.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0011-bp-wifi-trace-unlink-rdev_end_cac-trace-event-from-w.patch
new file mode 100644
index 0000000..d77e086
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0011-bp-wifi-trace-unlink-rdev_end_cac-trace-event-from-w.patch
@@ -0,0 +1,48 @@
+From 0ad2cafa823d9ebff0ef978ec37d3142c3d42a9f Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 11 Jul 2024 09:21:42 +0530
+Subject: [PATCH 11/89] bp: wifi: trace: unlink rdev_end_cac trace event from
+ wiphy_netdev_evt class
+
+rdev_end_cac trace event is linked with wiphy_netdev_evt event class.
+There is no option to pass link ID currently to wiphy_netdev_evt class.
+A subsequent change would pass link ID to rdev_end_cac event and hence
+it can no longer derive the event class from wiphy_netdev_evt.
+
+Therefore, unlink rdev_end_cac event from wiphy_netdev_evt and define it's
+own independent trace event. Link ID would be passed in subsequent change.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ net/wireless/trace.h | 15 ++++++++++++---
+ 1 file changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/net/wireless/trace.h b/net/wireless/trace.h
+index 6f2e9a5..881c01d 100644
+--- a/net/wireless/trace.h
++++ b/net/wireless/trace.h
+@@ -805,9 +805,18 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_flush_pmksa,
+ 	TP_ARGS(wiphy, netdev)
+ );
+ 
+-DEFINE_EVENT(wiphy_netdev_evt, rdev_end_cac,
+-	     TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+-	     TP_ARGS(wiphy, netdev)
++TRACE_EVENT(rdev_end_cac,
++	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
++	TP_ARGS(wiphy, netdev),
++	TP_STRUCT__entry(
++		WIPHY_ENTRY
++		NETDEV_ENTRY
++	),
++	TP_fast_assign(
++		WIPHY_ASSIGN;
++		NETDEV_ASSIGN;
++	),
++	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG)
+ );
+ 
+ DECLARE_EVENT_CLASS(station_add_change,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0011-mtk-cfg80211-implement-DFS-status-show-cac-and-nop-s.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0011-mtk-cfg80211-implement-DFS-status-show-cac-and-nop-s.patch
deleted file mode 100644
index be103d3..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0011-mtk-cfg80211-implement-DFS-status-show-cac-and-nop-s.patch
+++ /dev/null
@@ -1,527 +0,0 @@
-From 60adbabe8df3bdbab9bd3c2146f19b4c83b69def Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Thu, 22 Sep 2022 14:27:41 +0800
-Subject: [PATCH 11/61] mtk: cfg80211: implement DFS status show, cac and nop
- skip command via debugfs
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Refactor DFS debugfs command for MLO
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- include/net/cfg80211.h  |   1 +
- net/mac80211/cfg.c      |  25 +++
- net/wireless/core.h     |   3 +
- net/wireless/debugfs.c  | 326 +++++++++++++++++++++++++++++++++++++++-
- net/wireless/mlme.c     |   6 +
- net/wireless/rdev-ops.h |  14 ++
- net/wireless/trace.h    |  13 ++
- 7 files changed, 381 insertions(+), 7 deletions(-)
-
-diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
-index d987b62..c55028f 100644
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -4965,6 +4965,7 @@ struct cfg80211_ops {
- 				    struct cfg80211_set_hw_timestamp *hwts);
- 	int	(*set_ttlm)(struct wiphy *wiphy, struct net_device *dev,
- 			    struct cfg80211_ttlm_params *params);
-+	void	(*skip_cac)(struct wireless_dev *wdev, unsigned int link_id);
- };
- 
- /*
-diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
-index 72e64be..3f4c129 100644
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -5055,6 +5055,30 @@ ieee80211_set_ttlm(struct wiphy *wiphy, struct net_device *dev,
- 	return ieee80211_req_neg_ttlm(sdata, params);
- }
- 
-+static void
-+ieee80211_skip_cac(struct wireless_dev *wdev, unsigned int link_id)
-+{
-+	struct net_device *dev = wdev->netdev;
-+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-+	struct ieee80211_link_data *link;
-+	unsigned int cac_time_ms;
-+
-+	link = sdata_dereference(sdata->link[link_id], sdata);
-+	if (!link)
-+		return;
-+
-+	wiphy_delayed_work_cancel(sdata->local->hw.wiphy,
-+				  &link->dfs_cac_timer_work);
-+	if (wdev->cac_started) {
-+		ieee80211_link_release_channel(link);
-+		cac_time_ms = wdev->cac_time_ms;
-+		wdev->cac_start_time = jiffies -
-+				       msecs_to_jiffies(cac_time_ms + 1);
-+		cfg80211_cac_event(wdev->netdev, &link->conf->chanreq.oper,
-+				   NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
-+	}
-+}
-+
- const struct cfg80211_ops mac80211_config_ops = {
- 	.add_virtual_intf = ieee80211_add_iface,
- 	.del_virtual_intf = ieee80211_del_iface,
-@@ -5168,4 +5192,5 @@ const struct cfg80211_ops mac80211_config_ops = {
- 	.del_link_station = ieee80211_del_link_station,
- 	.set_hw_timestamp = ieee80211_set_hw_timestamp,
- 	.set_ttlm = ieee80211_set_ttlm,
-+	.skip_cac = ieee80211_skip_cac,
- };
-diff --git a/net/wireless/core.h b/net/wireless/core.h
-index 7bef6b0..ae6b7fa 100644
---- a/net/wireless/core.h
-+++ b/net/wireless/core.h
-@@ -86,6 +86,9 @@ struct cfg80211_registered_device {
- 
- 	struct wireless_dev *background_radar_wdev;
- 	struct cfg80211_chan_def background_radar_chandef;
-+	bool background_cac_started;
-+	unsigned long background_cac_start_time;
-+	unsigned int background_cac_time_ms;
- 	struct delayed_work background_cac_done_wk;
- 	struct work_struct background_cac_abort_wk;
- 
-diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c
-index 7c59a25..a246b2c 100644
---- a/net/wireless/debugfs.c
-+++ b/net/wireless/debugfs.c
-@@ -10,6 +10,7 @@
- #include <linux/slab.h>
- #include "core.h"
- #include "debugfs.h"
-+#include "rdev-ops.h"
- 
- #define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...)		\
- static ssize_t name## _read(struct file *file, char __user *userbuf,	\
-@@ -97,18 +98,329 @@ static const struct file_operations ht40allow_map_ops = {
- 	.llseek = default_llseek,
- };
- 
--#define DEBUGFS_ADD(name)						\
--	debugfs_create_file(#name, 0444, phyd, &rdev->wiphy, &name## _ops)
-+static int dfs_print_chan(struct ieee80211_channel *chan, int remain_time, int wait_time,
-+			  char *buf, int buf_size, int offset, bool is_background)
-+{
-+	if (WARN_ON(offset > buf_size))
-+		return 0;
-+
-+	if (chan->dfs_state == NL80211_DFS_UNAVAILABLE) {
-+		offset += scnprintf(buf + offset, buf_size - offset,
-+				    "	Channel = %d, DFS_state = Unavailable",
-+				    chan->hw_value);
-+		if (remain_time > 0)
-+			offset += scnprintf(buf + offset, buf_size - offset,
-+					    ", Non-occupancy Remain Time = %d / %d [sec]",
-+					    remain_time, wait_time);
-+		else
-+			offset += scnprintf(buf + offset, buf_size - offset,
-+					    ", Changing state...");
-+	} else if (chan->dfs_state == NL80211_DFS_USABLE) {
-+		offset += scnprintf(buf + offset, buf_size - offset,
-+				    "	Channel = %d, DFS_state = Usable",
-+				    chan->hw_value);
-+		if (remain_time > 0)
-+			offset += scnprintf(buf + offset, buf_size - offset,
-+					    ", CAC Remain Time = %d / %d [sec]",
-+					    remain_time, wait_time);
-+	} else if (chan->dfs_state == NL80211_DFS_AVAILABLE) {
-+		offset += scnprintf(buf + offset, buf_size - offset,
-+				    "	Channel = %d, DFS_state = Available",
-+				    chan->hw_value);
-+	} else {
-+		offset += scnprintf(buf + offset, buf_size - offset,
-+				    "	Channel = %d, DFS_state = Unknown",
-+				    chan->hw_value);
-+	}
-+
-+	if (is_background)
-+		offset += scnprintf(buf + offset, buf_size - offset,
-+				    " (background chain)");
-+	offset += scnprintf(buf + offset, buf_size - offset, "\n");
-+
-+	return offset;
-+}
-+
-+static int dfs_status_read_wdev(struct wiphy *wiphy, struct wireless_dev *wdev, char *buf,
-+				unsigned int buf_size, unsigned int offset)
-+{
-+	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-+	struct cfg80211_chan_def *chandef;
-+	struct cfg80211_chan_def *background_chandef = &rdev->background_radar_chandef;
-+	enum nl80211_band band;
-+	struct ieee80211_supported_band *sband;
-+	struct ieee80211_channel *chan;
-+	unsigned long jiffies_passed;
-+	unsigned int link_id;
-+	int i, remain_time = 0, wait_time_ms = 0;
-+	bool is_background;
-+
-+	for (band = 0; band < NUM_NL80211_BANDS; band++)
-+		if (wiphy->bands[band] &&
-+		    wiphy->bands[band]->band == NL80211_BAND_5GHZ)
-+			sband = wiphy->bands[band];
-+
-+	if (!sband) {
-+		offset += scnprintf(buf + offset, buf_size - offset, "No 5G band\n");
-+		return offset;
-+	}
-+
-+	for_each_valid_link(wdev, link_id) {
-+		chandef = wdev_chandef(wdev, link_id);
-+		if (!chandef || !chandef->chan ||
-+		    chandef->chan->band != NL80211_BAND_5GHZ)
-+			continue;
-+
-+		offset += scnprintf(buf + offset, buf_size - offset,
-+				    "Link %d DFS channel:\n", link_id);
-+		for (i = 0; i < sband->n_channels; i++) {
-+			is_background = false;
-+			chan = &sband->channels[i];
-+
-+			if (!(chan->flags & IEEE80211_CHAN_RADAR))
-+				continue;
-+
-+			if (chan->dfs_state == NL80211_DFS_UNAVAILABLE) {
-+				jiffies_passed = jiffies - chan->dfs_state_entered;
-+				wait_time_ms = IEEE80211_DFS_MIN_NOP_TIME_MS;
-+				remain_time = (wait_time_ms - jiffies_to_msecs(jiffies_passed));
-+				if (remain_time > wait_time_ms)
-+					remain_time = 0;
-+			} else if (chan->dfs_state == NL80211_DFS_USABLE) {
-+				if (wdev->cac_started &&
-+				    cfg80211_is_sub_chan(chandef, chan, false)) {
-+					jiffies_passed = jiffies - wdev->cac_start_time;
-+					wait_time_ms = wdev->cac_time_ms;
-+					remain_time = (wait_time_ms -
-+						       jiffies_to_msecs(jiffies_passed));
-+				}
-+
-+				if (rdev->background_radar_wdev == wdev &&
-+				    rdev->background_cac_started &&
-+				    cfg80211_is_sub_chan(background_chandef, chan, false)) {
-+					jiffies_passed = jiffies - rdev->background_cac_start_time;
-+					wait_time_ms = rdev->background_cac_time_ms;
-+					remain_time = (wait_time_ms -
-+						       jiffies_to_msecs(jiffies_passed));
-+					is_background = true;
-+				}
-+
-+				if (remain_time > wait_time_ms)
-+					remain_time = 0;
-+
-+			} else {
-+				if (rdev->background_radar_wdev == wdev &&
-+				    cfg80211_is_sub_chan(background_chandef, chan, false))
-+					is_background = true;
-+			}
-+
-+			offset = dfs_print_chan(chan, remain_time / 1000, wait_time_ms / 1000,
-+						buf, buf_size, offset, is_background);
-+			remain_time = 0;
-+		}
-+	}
-+
-+	return offset;
-+}
-+
-+static ssize_t dfs_status_read(struct file *file, char __user *user_buf,
-+			       size_t count, loff_t *ppos)
-+{
-+	struct wiphy *wiphy = file->private_data;
-+	struct wireless_dev *wdev;
-+	char *buf;
-+	unsigned int offset = 0, buf_size = PAGE_SIZE, r;
-+	const char * const iftype_str[] = {
-+		[NL80211_IFTYPE_UNSPECIFIED] = "unspecified",
-+		[NL80211_IFTYPE_ADHOC] = "adhoc",
-+		[NL80211_IFTYPE_STATION] = "station",
-+		[NL80211_IFTYPE_AP] = "ap",
-+		[NL80211_IFTYPE_AP_VLAN] = "ap vlan",
-+		[NL80211_IFTYPE_WDS] = "wds",
-+		[NL80211_IFTYPE_MONITOR] = "monitor",
-+		[NL80211_IFTYPE_MESH_POINT] = "mesh point",
-+		[NL80211_IFTYPE_P2P_CLIENT] = "p2p client",
-+		[NL80211_IFTYPE_P2P_GO] = "p2p go",
-+		[NL80211_IFTYPE_P2P_DEVICE] = "p2p device",
-+		[NL80211_IFTYPE_OCB] = "ocb",
-+		[NL80211_IFTYPE_NAN] = "nan",
-+	};
-+
-+	buf = kzalloc(buf_size, GFP_KERNEL);
-+	if (!buf)
-+		return -ENOMEM;
-+
-+	list_for_each_entry(wdev, &wiphy->wdev_list, list) {
-+		offset += scnprintf(buf + offset, buf_size - offset,
-+				    "wdev 0x%x\n"
-+				    "interface type %s\n",
-+				    wdev->identifier, iftype_str[wdev->iftype]);
-+		offset = dfs_status_read_wdev(wiphy, wdev, buf, buf_size, offset);
-+	}
-+
-+	r = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
-+
-+	kfree(buf);
-+
-+	return r;
-+}
-+
-+static const struct file_operations dfs_status_ops = {
-+	.read = dfs_status_read,
-+	.open = simple_open,
-+	.llseek = default_llseek,
-+};
-+
-+static int
-+dfs_nop_skip(void *data, u64 val)
-+{
-+	struct wiphy *wiphy = data;
-+	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-+	bool en = !!val;
-+	enum nl80211_band band;
-+	struct ieee80211_supported_band *sband;
-+	struct ieee80211_channel *chan;
-+	u32 nop_time = IEEE80211_DFS_MIN_NOP_TIME_MS;
-+	int i;
-+
-+	if (!en)
-+		return 0;
-+
-+	for (band = 0; band < NUM_NL80211_BANDS; band++) {
-+		sband = wiphy->bands[band];
-+		if (!sband)
-+			continue;
-+		for (i = 0; i < sband->n_channels; i++) {
-+			chan = &sband->channels[i];
-+
-+			if (!(chan->flags & IEEE80211_CHAN_RADAR))
-+				continue;
-+
-+			if (chan->dfs_state == NL80211_DFS_UNAVAILABLE) {
-+				// Let current jiffies > dfs_state_entered_jiffies + NOP time
-+				chan->dfs_state_entered = jiffies -
-+						       msecs_to_jiffies(nop_time + 1);
-+			}
-+		}
-+	}
-+
-+	cfg80211_sched_dfs_chan_update(rdev);
-+
-+	return 0;
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(dfs_skip_nop_ops, NULL,
-+			 dfs_nop_skip, "0x%08llx\n");
-+
-+static int
-+dfs_cac_skip(void *data, u64 val)
-+{
-+#define CAC_SKIP_MASK			BIT(0)
-+#define CAC_SKIP_BACKGROUND_MASK	BIT(1)
-+	struct wiphy *wiphy = data;
-+	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-+	struct wireless_dev *wdev;
-+	struct cfg80211_chan_def *c;
-+	unsigned int link_id, skip_mode = val;
-+	unsigned long cac_time;
-+
-+	if (!skip_mode || skip_mode > (CAC_SKIP_MASK | CAC_SKIP_BACKGROUND_MASK))
-+		return 0;
-+
-+	list_for_each_entry(wdev, &wiphy->wdev_list, list) {
-+		if (skip_mode & CAC_SKIP_MASK) {
-+			for_each_valid_link(wdev, link_id) {
-+				c = wdev_chandef(wdev, link_id);
-+				if (!c || !c->chan ||
-+				    c->chan->band != NL80211_BAND_5GHZ)
-+					continue;
-+
-+				if (cfg80211_chandef_dfs_required(wiphy, c, wdev->iftype) > 0 &&
-+				    cfg80211_chandef_dfs_usable(wiphy, c) && wdev->cac_started) {
-+					rdev_skip_cac(rdev, wdev, link_id);
-+				}
-+			}
-+		}
-+
-+		if ((skip_mode & CAC_SKIP_BACKGROUND_MASK) &&
-+		    rdev->background_radar_wdev == wdev &&
-+		    rdev->background_radar_chandef.chan) {
-+			c = &rdev->background_radar_chandef;
-+
-+			if ((cfg80211_chandef_dfs_required(wiphy, c, wdev->iftype) > 0) &&
-+			    cfg80211_chandef_dfs_usable(wiphy, c) &&
-+			    rdev->background_cac_started) {
-+				// Let current jiffies > dfs_state_entered_jiffies + CAC time
-+				cac_time = rdev->background_cac_time_ms;
-+				rdev->background_cac_start_time = jiffies -
-+								  msecs_to_jiffies(cac_time + 1);
-+				cancel_delayed_work(&rdev->background_cac_done_wk);
-+				queue_delayed_work(cfg80211_wq, &rdev->background_cac_done_wk, 0);
-+			}
-+		}
-+	}
-+
-+	return 0;
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(dfs_skip_cac_ops, NULL,
-+			 dfs_cac_skip, "0x%08llx\n");
-+
-+static int
-+dfs_available_reset(void *data, u64 val)
-+{
-+	struct wiphy *wiphy = data;
-+	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-+	bool en = !!val;
-+	enum nl80211_band band;
-+	struct ieee80211_supported_band *sband;
-+	struct ieee80211_channel *chan;
-+	int i;
-+
-+	if (!en)
-+		return 0;
-+
-+	for (band = 0; band < NUM_NL80211_BANDS; band++) {
-+		sband = wiphy->bands[band];
-+		if (!sband)
-+			continue;
-+		for (i = 0; i < sband->n_channels; i++) {
-+			chan = &sband->channels[i];
-+
-+			if (!(chan->flags & IEEE80211_CHAN_RADAR))
-+				continue;
-+
-+			if (chan->dfs_state == NL80211_DFS_AVAILABLE) {
-+				chan->dfs_state = NL80211_DFS_USABLE;
-+				chan->dfs_state_entered = jiffies;
-+			}
-+		}
-+	}
-+
-+	cfg80211_sched_dfs_chan_update(rdev);
-+
-+	return 0;
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(dfs_available_reset_ops, NULL,
-+			 dfs_available_reset, "0x%08llx\n");
-+
-+#define DEBUGFS_ADD(name, chmod)						\
-+	debugfs_create_file(#name, chmod, phyd, &rdev->wiphy, &name## _ops)
- 
- void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev)
- {
- 	struct dentry *phyd = rdev->wiphy.debugfsdir;
- 
--	DEBUGFS_ADD(rts_threshold);
--	DEBUGFS_ADD(fragmentation_threshold);
--	DEBUGFS_ADD(short_retry_limit);
--	DEBUGFS_ADD(long_retry_limit);
--	DEBUGFS_ADD(ht40allow_map);
-+	DEBUGFS_ADD(rts_threshold, 0444);
-+	DEBUGFS_ADD(fragmentation_threshold, 0444);
-+	DEBUGFS_ADD(short_retry_limit, 0444);
-+	DEBUGFS_ADD(long_retry_limit, 0444);
-+	DEBUGFS_ADD(ht40allow_map, 0444);
-+	DEBUGFS_ADD(dfs_status, 0444);
-+	DEBUGFS_ADD(dfs_skip_nop, 0600);
-+	DEBUGFS_ADD(dfs_skip_cac, 0600);
-+	DEBUGFS_ADD(dfs_available_reset, 0600);
- }
- 
- struct debugfs_read_work {
-diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
-index c7e62eb..d42b65b 100644
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -1177,13 +1177,16 @@ __cfg80211_background_cac_event(struct cfg80211_registered_device *rdev,
- 		queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk);
- 		cfg80211_sched_dfs_chan_update(rdev);
- 		wdev = rdev->background_radar_wdev;
-+		rdev->background_cac_started = false;
- 		break;
- 	case NL80211_RADAR_CAC_ABORTED:
- 		if (!cancel_delayed_work(&rdev->background_cac_done_wk))
- 			return;
- 		wdev = rdev->background_radar_wdev;
-+		rdev->background_cac_started = false;
- 		break;
- 	case NL80211_RADAR_CAC_STARTED:
-+		rdev->background_cac_started = true;
- 		break;
- 	default:
- 		return;
-@@ -1203,6 +1206,7 @@ cfg80211_background_cac_event(struct cfg80211_registered_device *rdev,
- 					chandef, event);
- 	wiphy_unlock(&rdev->wiphy);
- }
-+EXPORT_SYMBOL(cfg80211_background_cac_event);
- 
- void cfg80211_background_cac_done_wk(struct work_struct *work)
- {
-@@ -1264,8 +1268,10 @@ cfg80211_start_background_radar_detection(struct cfg80211_registered_device *rde
- 	if (!cac_time_ms)
- 		cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
- 
-+	rdev->background_cac_time_ms = cac_time_ms;
- 	rdev->background_radar_chandef = *chandef;
- 	rdev->background_radar_wdev = wdev; /* Get offchain ownership */
-+	rdev->background_cac_start_time = jiffies;
- 
- 	__cfg80211_background_cac_event(rdev, wdev, chandef,
- 					NL80211_RADAR_CAC_STARTED);
-diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
-index 466828f..2ae7fc5 100644
---- a/net/wireless/rdev-ops.h
-+++ b/net/wireless/rdev-ops.h
-@@ -1542,4 +1542,18 @@ rdev_set_ttlm(struct cfg80211_registered_device *rdev,
- 
- 	return ret;
- }
-+
-+static inline int
-+rdev_skip_cac(struct cfg80211_registered_device *rdev,
-+	      struct wireless_dev *wdev, unsigned int link_id)
-+{
-+	if (!rdev->ops->skip_cac)
-+		return -EOPNOTSUPP;
-+
-+	trace_rdev_skip_cac(wdev, link_id);
-+	rdev->ops->skip_cac(wdev, link_id);
-+	trace_rdev_return_void(&rdev->wiphy);
-+
-+	return 0;
-+}
- #endif /* __CFG80211_RDEV_OPS */
-diff --git a/net/wireless/trace.h b/net/wireless/trace.h
-index 7073a70..aa3284f 100644
---- a/net/wireless/trace.h
-+++ b/net/wireless/trace.h
-@@ -4005,6 +4005,19 @@ TRACE_EVENT(rdev_set_ttlm,
- 		  WIPHY_PR_ARG, NETDEV_PR_ARG)
- );
- 
-+TRACE_EVENT(rdev_skip_cac,
-+	TP_PROTO(struct wireless_dev *wdev, unsigned int link_id),
-+	TP_ARGS(wdev, link_id),
-+	TP_STRUCT__entry(
-+		WDEV_ENTRY
-+		__field(unsigned int, link_id)
-+	),
-+	TP_fast_assign(
-+		WDEV_ASSIGN;
-+		__entry->link_id = link_id;
-+	),
-+	TP_printk(WDEV_PR_FMT ", link_id: %d", WDEV_PR_ARG, __entry->link_id)
-+);
- #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
- 
- #undef TRACE_INCLUDE_PATH
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0012-bp-wifi-cfg80211-move-DFS-related-members-to-links-i.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0012-bp-wifi-cfg80211-move-DFS-related-members-to-links-i.patch
new file mode 100644
index 0000000..c9f99a3
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0012-bp-wifi-cfg80211-move-DFS-related-members-to-links-i.patch
@@ -0,0 +1,354 @@
+From 47d4c2ad004b10dff89ff8c5a506df3d78a042f5 Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 11 Jul 2024 09:21:43 +0530
+Subject: [PATCH 12/89] bp: wifi: cfg80211: move DFS related members to links[]
+ in wireless_dev
+
+A few members related to DFS handling are currently under per wireless
+device data structure. However, in order to support DFS with MLO, there is
+a need to have them on a per-link manner.
+
+Hence, as a preliminary step, move members cac_started, cac_start_time
+and cac_time_ms to be on a per-link basis.
+
+Since currently, link ID is not known at all places, use default value of
+0 for now.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ drivers/net/wireless/marvell/mwifiex/11h.c      |  4 ++--
+ drivers/net/wireless/marvell/mwifiex/cfg80211.c |  4 ++--
+ drivers/net/wireless/quantenna/qtnfmac/event.c  |  6 +++---
+ include/net/cfg80211.h                          | 17 +++++++++--------
+ net/mac80211/cfg.c                              |  8 ++++----
+ net/mac80211/iface.c                            |  2 +-
+ net/mac80211/mlme.c                             |  2 +-
+ net/mac80211/scan.c                             |  2 +-
+ net/mac80211/util.c                             |  2 +-
+ net/wireless/ibss.c                             |  2 +-
+ net/wireless/mesh.c                             |  2 +-
+ net/wireless/mlme.c                             | 11 ++++++-----
+ net/wireless/nl80211.c                          | 10 +++++-----
+ net/wireless/reg.c                              |  2 +-
+ 14 files changed, 38 insertions(+), 36 deletions(-)
+
+diff --git a/drivers/net/wireless/marvell/mwifiex/11h.c b/drivers/net/wireless/marvell/mwifiex/11h.c
+index b90f922..fb2cad0 100644
+--- a/drivers/net/wireless/marvell/mwifiex/11h.c
++++ b/drivers/net/wireless/marvell/mwifiex/11h.c
+@@ -117,7 +117,7 @@ void mwifiex_dfs_cac_work_queue(struct work_struct *work)
+ 				     dfs_cac_work);
+ 
+ 	chandef = priv->dfs_chandef;
+-	if (priv->wdev.cac_started) {
++	if (priv->wdev.links[0].cac_started) {
+ 		mwifiex_dbg(priv->adapter, MSG,
+ 			    "CAC timer finished; No radar detected\n");
+ 		cfg80211_cac_event(priv->netdev, &chandef,
+@@ -174,7 +174,7 @@ int mwifiex_stop_radar_detection(struct mwifiex_private *priv,
+  */
+ void mwifiex_abort_cac(struct mwifiex_private *priv)
+ {
+-	if (priv->wdev.cac_started) {
++	if (priv->wdev.links[0].cac_started) {
+ 		if (mwifiex_stop_radar_detection(priv, &priv->dfs_chandef))
+ 			mwifiex_dbg(priv->adapter, ERROR,
+ 				    "failed to stop CAC in FW\n");
+diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
+index 7a744ce..436d391 100644
+--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
++++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
+@@ -1880,7 +1880,7 @@ mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
+ 	struct mwifiex_sta_node *sta_node;
+ 	u8 deauth_mac[ETH_ALEN];
+ 
+-	if (!priv->bss_started && priv->wdev.cac_started) {
++	if (!priv->bss_started && priv->wdev.links[0].cac_started) {
+ 		mwifiex_dbg(priv->adapter, INFO, "%s: abort CAC!\n", __func__);
+ 		mwifiex_abort_cac(priv);
+ 	}
+@@ -3978,7 +3978,7 @@ mwifiex_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ 		return -EBUSY;
+ 	}
+ 
+-	if (priv->wdev.cac_started)
++	if (priv->wdev.links[0].cac_started)
+ 		return -EBUSY;
+ 
+ 	if (cfg80211_chandef_identical(&params->chandef,
+diff --git a/drivers/net/wireless/quantenna/qtnfmac/event.c b/drivers/net/wireless/quantenna/qtnfmac/event.c
+index 76b07db..8bd1e14 100644
+--- a/drivers/net/wireless/quantenna/qtnfmac/event.c
++++ b/drivers/net/wireless/quantenna/qtnfmac/event.c
+@@ -520,21 +520,21 @@ static int qtnf_event_handle_radar(struct qtnf_vif *vif,
+ 		cfg80211_radar_event(wiphy, &chandef, GFP_KERNEL);
+ 		break;
+ 	case QLINK_RADAR_CAC_FINISHED:
+-		if (!vif->wdev.cac_started)
++		if (!vif->wdev.links[0].cac_started)
+ 			break;
+ 
+ 		cfg80211_cac_event(vif->netdev, &chandef,
+ 				   NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
+ 		break;
+ 	case QLINK_RADAR_CAC_ABORTED:
+-		if (!vif->wdev.cac_started)
++		if (!vif->wdev.links[0].cac_started)
+ 			break;
+ 
+ 		cfg80211_cac_event(vif->netdev, &chandef,
+ 				   NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
+ 		break;
+ 	case QLINK_RADAR_CAC_STARTED:
+-		if (vif->wdev.cac_started)
++		if (vif->wdev.links[0].cac_started)
+ 			break;
+ 
+ 		if (!wiphy_ext_feature_isset(wiphy,
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index 850861b..86634cc 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -6206,9 +6206,6 @@ enum ieee80211_ap_reg_power {
+  * @address: The address for this device, valid only if @netdev is %NULL
+  * @is_running: true if this is a non-netdev device that has been started, e.g.
+  *	the P2P Device.
+- * @cac_started: true if DFS channel availability check has been started
+- * @cac_start_time: timestamp (jiffies) when the dfs state was entered.
+- * @cac_time_ms: CAC time in ms
+  * @ps: powersave mode is enabled
+  * @ps_timeout: dynamic powersave timeout
+  * @ap_unexpected_nlportid: (private) netlink port ID of application
+@@ -6232,6 +6229,11 @@ enum ieee80211_ap_reg_power {
+  *	unprotected beacon report
+  * @links: array of %IEEE80211_MLD_MAX_NUM_LINKS elements containing @addr
+  *	@ap and @client for each link
++ * @links[].cac_started: true if DFS channel availability check has been
++ *	started
++ * @links[].cac_start_time: timestamp (jiffies) when the dfs state was
++ *	entered.
++ * @links[].cac_time_ms: CAC time in ms
+  * @valid_links: bitmap describing what elements of @links are valid
+  */
+ struct wireless_dev {
+@@ -6273,11 +6275,6 @@ struct wireless_dev {
+ 	u32 owner_nlportid;
+ 	bool nl_owner_dead;
+ 
+-	/* FIXME: need to rework radar detection for MLO */
+-	bool cac_started;
+-	unsigned long cac_start_time;
+-	unsigned int cac_time_ms;
+-
+ #ifdef CPTCFG_CFG80211_WEXT
+ 	/* wext data */
+ 	struct {
+@@ -6344,6 +6341,10 @@ struct wireless_dev {
+ 				struct cfg80211_internal_bss *current_bss;
+ 			} client;
+ 		};
++
++		bool cac_started;
++		unsigned long cac_start_time;
++		unsigned int cac_time_ms;
+ 	} links[IEEE80211_MLD_MAX_NUM_LINKS];
+ 	u16 valid_links;
+ };
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index ec47ab1..4d5eb60 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1656,7 +1656,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
+ 	ieee80211_link_info_change_notify(sdata, link,
+ 					  BSS_CHANGED_BEACON_ENABLED);
+ 
+-	if (sdata->wdev.cac_started) {
++	if (sdata->wdev.links[0].cac_started) {
+ 		chandef = link_conf->chanreq.oper;
+ 		wiphy_delayed_work_cancel(wiphy, &link->dfs_cac_timer_work);
+ 		cfg80211_cac_event(sdata->dev, &chandef,
+@@ -3494,9 +3494,9 @@ static void ieee80211_end_cac(struct wiphy *wiphy,
+ 		wiphy_delayed_work_cancel(wiphy,
+ 					  &sdata->deflink.dfs_cac_timer_work);
+ 
+-		if (sdata->wdev.cac_started) {
++		if (sdata->wdev.links[0].cac_started) {
+ 			ieee80211_link_release_channel(&sdata->deflink);
+-			sdata->wdev.cac_started = false;
++			sdata->wdev.links[0].cac_started = false;
+ 		}
+ 	}
+ }
+@@ -3951,7 +3951,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ 	if (!list_empty(&local->roc_list) || local->scanning)
+ 		return -EBUSY;
+ 
+-	if (sdata->wdev.cac_started)
++	if (sdata->wdev.links[0].cac_started)
+ 		return -EBUSY;
+ 
+ 	if (WARN_ON(link_id >= IEEE80211_MLD_MAX_NUM_LINKS))
+diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
+index 00d75e0..f0d9024 100644
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -552,7 +552,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
+ 	wiphy_delayed_work_cancel(local->hw.wiphy,
+ 				  &sdata->deflink.dfs_cac_timer_work);
+ 
+-	if (sdata->wdev.cac_started) {
++	if (sdata->wdev.links[0].cac_started) {
+ 		chandef = sdata->vif.bss_conf.chanreq.oper;
+ 		WARN_ON(local->suspended);
+ 		ieee80211_link_release_channel(&sdata->deflink);
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 5b83e7b..2258858 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -3039,7 +3039,7 @@ void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work)
+ 
+ 	lockdep_assert_wiphy(sdata->local->hw.wiphy);
+ 
+-	if (sdata->wdev.cac_started) {
++	if (sdata->wdev.links[0].cac_started) {
+ 		ieee80211_link_release_channel(link);
+ 		cfg80211_cac_event(sdata->dev, &chandef,
+ 				   NL80211_RADAR_CAC_FINISHED,
+diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
+index b5f2df6..7d8e7af 100644
+--- a/net/mac80211/scan.c
++++ b/net/mac80211/scan.c
+@@ -585,7 +585,7 @@ static bool __ieee80211_can_leave_ch(struct ieee80211_sub_if_data *sdata)
+ 		return false;
+ 
+ 	list_for_each_entry(sdata_iter, &local->interfaces, list) {
+-		if (sdata_iter->wdev.cac_started)
++		if (sdata_iter->wdev.links[0].cac_started)
+ 			return false;
+ 	}
+ 
+diff --git a/net/mac80211/util.c b/net/mac80211/util.c
+index 77d1e44..424d8d6 100644
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -3462,7 +3462,7 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
+ 		wiphy_delayed_work_cancel(local->hw.wiphy,
+ 					  &sdata->deflink.dfs_cac_timer_work);
+ 
+-		if (sdata->wdev.cac_started) {
++		if (sdata->wdev.links[0].cac_started) {
+ 			chandef = sdata->vif.bss_conf.chanreq.oper;
+ 			ieee80211_link_release_channel(&sdata->deflink);
+ 			cfg80211_cac_event(sdata->dev,
+diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
+index 0651e9b..0fc930a 100644
+--- a/net/wireless/ibss.c
++++ b/net/wireless/ibss.c
+@@ -94,7 +94,7 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
+ 
+ 	lockdep_assert_held(&rdev->wiphy.mtx);
+ 
+-	if (wdev->cac_started)
++	if (wdev->links[0].cac_started)
+ 		return -EBUSY;
+ 
+ 	if (wdev->u.ibss.ssid_len)
+diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
+index aaca65b..2c66540 100644
+--- a/net/wireless/mesh.c
++++ b/net/wireless/mesh.c
+@@ -127,7 +127,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
+ 	if (!rdev->ops->join_mesh)
+ 		return -EOPNOTSUPP;
+ 
+-	if (wdev->cac_started)
++	if (wdev->links[0].cac_started)
+ 		return -EBUSY;
+ 
+ 	if (!setup->chandef.chan) {
+diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
+index c7e62eb..eda999d 100644
+--- a/net/wireless/mlme.c
++++ b/net/wireless/mlme.c
+@@ -1124,13 +1124,14 @@ void cfg80211_cac_event(struct net_device *netdev,
+ 
+ 	trace_cfg80211_cac_event(netdev, event);
+ 
+-	if (WARN_ON(!wdev->cac_started && event != NL80211_RADAR_CAC_STARTED))
++	if (WARN_ON(!wdev->links[0].cac_started &&
++		    event != NL80211_RADAR_CAC_STARTED))
+ 		return;
+ 
+ 	switch (event) {
+ 	case NL80211_RADAR_CAC_FINISHED:
+-		timeout = wdev->cac_start_time +
+-			  msecs_to_jiffies(wdev->cac_time_ms);
++		timeout = wdev->links[0].cac_start_time +
++			  msecs_to_jiffies(wdev->links[0].cac_time_ms);
+ 		WARN_ON(!time_after_eq(jiffies, timeout));
+ 		cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
+ 		memcpy(&rdev->cac_done_chandef, chandef,
+@@ -1139,10 +1140,10 @@ void cfg80211_cac_event(struct net_device *netdev,
+ 		cfg80211_sched_dfs_chan_update(rdev);
+ 		fallthrough;
+ 	case NL80211_RADAR_CAC_ABORTED:
+-		wdev->cac_started = false;
++		wdev->links[0].cac_started = false;
+ 		break;
+ 	case NL80211_RADAR_CAC_STARTED:
+-		wdev->cac_started = true;
++		wdev->links[0].cac_started = true;
+ 		break;
+ 	default:
+ 		WARN_ON(1);
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index 6489fe9..3e87f5a 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -6099,7 +6099,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
+ 	if (!rdev->ops->start_ap)
+ 		return -EOPNOTSUPP;
+ 
+-	if (wdev->cac_started)
++	if (wdev->links[0].cac_started)
+ 		return -EBUSY;
+ 
+ 	if (wdev->links[link_id].ap.beacon_interval)
+@@ -10154,7 +10154,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
+ 		goto unlock;
+ 	}
+ 
+-	if (cfg80211_beaconing_iface_active(wdev) || wdev->cac_started) {
++	if (cfg80211_beaconing_iface_active(wdev) || wdev->links[0].cac_started) {
+ 		err = -EBUSY;
+ 		goto unlock;
+ 	}
+@@ -10177,9 +10177,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
+ 	err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms);
+ 	if (!err) {
+ 		wdev->links[0].ap.chandef = chandef;
+-		wdev->cac_started = true;
+-		wdev->cac_start_time = jiffies;
+-		wdev->cac_time_ms = cac_time_ms;
++		wdev->links[0].cac_started = true;
++		wdev->links[0].cac_start_time = jiffies;
++		wdev->links[0].cac_time_ms = cac_time_ms;
+ 	}
+ unlock:
+ 	wiphy_unlock(wiphy);
+diff --git a/net/wireless/reg.c b/net/wireless/reg.c
+index 4219dc2..0040862 100644
+--- a/net/wireless/reg.c
++++ b/net/wireless/reg.c
+@@ -4245,7 +4245,7 @@ static void cfg80211_check_and_end_cac(struct cfg80211_registered_device *rdev)
+ 	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ 		struct cfg80211_chan_def *chandef;
+ 
+-		if (!wdev->cac_started)
++		if (!wdev->links[0].cac_started)
+ 			continue;
+ 
+ 		/* FIXME: radar detection is tied to link 0 for now */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0012-mtk-mac80211-Set-TWT-Information-Frame-Disabled-bit-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0012-mtk-mac80211-Set-TWT-Information-Frame-Disabled-bit-.patch
deleted file mode 100644
index ee54b68..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0012-mtk-mac80211-Set-TWT-Information-Frame-Disabled-bit-.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From f3260628a2860a7af6176205fb201ffe7a5334e7 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Tue, 4 Oct 2022 10:47:05 +0800
-Subject: [PATCH 12/61] mtk: mac80211: Set TWT Information Frame Disabled bit
- as 1.
-
-This modification means that current implementation do not support twt information frame.
----
- net/mac80211/s1g.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c
-index d4ed0c0..27eccbb 100644
---- a/net/mac80211/s1g.c
-+++ b/net/mac80211/s1g.c
-@@ -102,6 +102,7 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata,
- 	struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
- 
- 	twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST);
-+	twt->control |= IEEE80211_TWT_CONTROL_RX_DISABLED;
- 
- 	/* broadcast TWT not supported yet */
- 	if (twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) {
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0013-bp-wifi-cfg80211-handle-DFS-per-link.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0013-bp-wifi-cfg80211-handle-DFS-per-link.patch
new file mode 100644
index 0000000..0dc5ea2
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0013-bp-wifi-cfg80211-handle-DFS-per-link.patch
@@ -0,0 +1,480 @@
+From f08e9c0b36a76033554c0fc89b6e3cfbf8019d8c Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 11 Jul 2024 09:21:44 +0530
+Subject: [PATCH 13/89] bp: wifi: cfg80211: handle DFS per link
+
+Currently, during starting a radar detection, no link id information is
+parsed and passed down. In order to support starting radar detection
+during Multi Link Operation, it is required to pass link id as well.
+
+Add changes to first parse and then pass link id in the start radar
+detection path.
+
+Additionally, update notification APIs to allow drivers/mac80211 to
+pass the link ID.
+
+However, everything is handled at link 0 only until all API's are ready to
+handle it per link.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ drivers/net/wireless/marvell/mwifiex/11h.c    |  7 +++--
+ .../net/wireless/marvell/mwifiex/cfg80211.c   |  2 +-
+ .../net/wireless/quantenna/qtnfmac/cfg80211.c |  2 +-
+ .../net/wireless/quantenna/qtnfmac/event.c    |  6 ++--
+ include/net/cfg80211.h                        |  8 +++--
+ net/mac80211/cfg.c                            |  6 ++--
+ net/mac80211/iface.c                          |  2 +-
+ net/mac80211/mlme.c                           |  2 +-
+ net/mac80211/util.c                           |  2 +-
+ net/wireless/mlme.c                           |  9 +++---
+ net/wireless/nl80211.c                        | 24 +++++++++++---
+ net/wireless/rdev-ops.h                       | 13 ++++----
+ net/wireless/reg.c                            | 19 +++++++-----
+ net/wireless/trace.h                          | 31 ++++++++++++-------
+ 14 files changed, 82 insertions(+), 51 deletions(-)
+
+diff --git a/drivers/net/wireless/marvell/mwifiex/11h.c b/drivers/net/wireless/marvell/mwifiex/11h.c
+index fb2cad0..032b93a 100644
+--- a/drivers/net/wireless/marvell/mwifiex/11h.c
++++ b/drivers/net/wireless/marvell/mwifiex/11h.c
+@@ -122,7 +122,7 @@ void mwifiex_dfs_cac_work_queue(struct work_struct *work)
+ 			    "CAC timer finished; No radar detected\n");
+ 		cfg80211_cac_event(priv->netdev, &chandef,
+ 				   NL80211_RADAR_CAC_FINISHED,
+-				   GFP_KERNEL);
++				   GFP_KERNEL, 0);
+ 	}
+ }
+ 
+@@ -182,7 +182,8 @@ void mwifiex_abort_cac(struct mwifiex_private *priv)
+ 			    "Aborting delayed work for CAC.\n");
+ 		cancel_delayed_work_sync(&priv->dfs_cac_work);
+ 		cfg80211_cac_event(priv->netdev, &priv->dfs_chandef,
+-				   NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
++				   NL80211_RADAR_CAC_ABORTED, GFP_KERNEL,
++				   0);
+ 	}
+ }
+ 
+@@ -221,7 +222,7 @@ int mwifiex_11h_handle_chanrpt_ready(struct mwifiex_private *priv,
+ 				cfg80211_cac_event(priv->netdev,
+ 						   &priv->dfs_chandef,
+ 						   NL80211_RADAR_DETECTED,
+-						   GFP_KERNEL);
++						   GFP_KERNEL, 0);
+ 			}
+ 			break;
+ 		default:
+diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
+index 436d391..18fe850 100644
+--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
++++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
+@@ -4145,7 +4145,7 @@ static int
+ mwifiex_cfg80211_start_radar_detection(struct wiphy *wiphy,
+ 				       struct net_device *dev,
+ 				       struct cfg80211_chan_def *chandef,
+-				       u32 cac_time_ms)
++				       u32 cac_time_ms, int link_id)
+ {
+ 	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ 	struct mwifiex_radar_params radar_params;
+diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
+index 663d777..8b97acc 100644
+--- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
++++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
+@@ -837,7 +837,7 @@ static int qtnf_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ static int qtnf_start_radar_detection(struct wiphy *wiphy,
+ 				      struct net_device *ndev,
+ 				      struct cfg80211_chan_def *chandef,
+-				      u32 cac_time_ms)
++				      u32 cac_time_ms, int link_id)
+ {
+ 	struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev);
+ 	int ret;
+diff --git a/drivers/net/wireless/quantenna/qtnfmac/event.c b/drivers/net/wireless/quantenna/qtnfmac/event.c
+index 8bd1e14..71840f4 100644
+--- a/drivers/net/wireless/quantenna/qtnfmac/event.c
++++ b/drivers/net/wireless/quantenna/qtnfmac/event.c
+@@ -524,14 +524,14 @@ static int qtnf_event_handle_radar(struct qtnf_vif *vif,
+ 			break;
+ 
+ 		cfg80211_cac_event(vif->netdev, &chandef,
+-				   NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
++				   NL80211_RADAR_CAC_FINISHED, GFP_KERNEL, 0);
+ 		break;
+ 	case QLINK_RADAR_CAC_ABORTED:
+ 		if (!vif->wdev.links[0].cac_started)
+ 			break;
+ 
+ 		cfg80211_cac_event(vif->netdev, &chandef,
+-				   NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
++				   NL80211_RADAR_CAC_ABORTED, GFP_KERNEL, 0);
+ 		break;
+ 	case QLINK_RADAR_CAC_STARTED:
+ 		if (vif->wdev.links[0].cac_started)
+@@ -542,7 +542,7 @@ static int qtnf_event_handle_radar(struct qtnf_vif *vif,
+ 			break;
+ 
+ 		cfg80211_cac_event(vif->netdev, &chandef,
+-				   NL80211_RADAR_CAC_STARTED, GFP_KERNEL);
++				   NL80211_RADAR_CAC_STARTED, GFP_KERNEL, 0);
+ 		break;
+ 	default:
+ 		pr_warn("%s: unhandled radar event %u\n",
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index 86634cc..906e48b 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -4846,9 +4846,9 @@ struct cfg80211_ops {
+ 	int	(*start_radar_detection)(struct wiphy *wiphy,
+ 					 struct net_device *dev,
+ 					 struct cfg80211_chan_def *chandef,
+-					 u32 cac_time_ms);
++					 u32 cac_time_ms, int link_id);
+ 	void	(*end_cac)(struct wiphy *wiphy,
+-				struct net_device *dev);
++			   struct net_device *dev, unsigned int link_id);
+ 	int	(*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev,
+ 				 struct cfg80211_update_ft_ies_params *ftie);
+ 	int	(*crit_proto_start)(struct wiphy *wiphy,
+@@ -8762,6 +8762,7 @@ void cfg80211_sta_opmode_change_notify(struct net_device *dev, const u8 *mac,
+  * @chandef: chandef for the current channel
+  * @event: type of event
+  * @gfp: context flags
++ * @link_id: valid link_id for MLO operation or 0 otherwise.
+  *
+  * This function is called when a Channel availability check (CAC) is finished
+  * or aborted. This must be called to notify the completion of a CAC process,
+@@ -8769,7 +8770,8 @@ void cfg80211_sta_opmode_change_notify(struct net_device *dev, const u8 *mac,
+  */
+ void cfg80211_cac_event(struct net_device *netdev,
+ 			const struct cfg80211_chan_def *chandef,
+-			enum nl80211_radar_event event, gfp_t gfp);
++			enum nl80211_radar_event event, gfp_t gfp,
++			unsigned int link_id);
+ 
+ /**
+  * cfg80211_background_cac_abort - Channel Availability Check offchan abort event
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 4d5eb60..cf60420 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1661,7 +1661,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
+ 		wiphy_delayed_work_cancel(wiphy, &link->dfs_cac_timer_work);
+ 		cfg80211_cac_event(sdata->dev, &chandef,
+ 				   NL80211_RADAR_CAC_ABORTED,
+-				   GFP_KERNEL);
++				   GFP_KERNEL, 0);
+ 	}
+ 
+ 	drv_stop_ap(sdata->local, sdata, link_conf);
+@@ -3455,7 +3455,7 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
+ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
+ 					   struct net_device *dev,
+ 					   struct cfg80211_chan_def *chandef,
+-					   u32 cac_time_ms)
++					   u32 cac_time_ms, int link_id)
+ {
+ 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ 	struct ieee80211_chan_req chanreq = { .oper = *chandef };
+@@ -3483,7 +3483,7 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
+ }
+ 
+ static void ieee80211_end_cac(struct wiphy *wiphy,
+-			      struct net_device *dev)
++			      struct net_device *dev, unsigned int link_id)
+ {
+ 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ 	struct ieee80211_local *local = sdata->local;
+diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
+index f0d9024..71e8b0d 100644
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -558,7 +558,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
+ 		ieee80211_link_release_channel(&sdata->deflink);
+ 		cfg80211_cac_event(sdata->dev, &chandef,
+ 				   NL80211_RADAR_CAC_ABORTED,
+-				   GFP_KERNEL);
++				   GFP_KERNEL, 0);
+ 	}
+ 
+ 	if (sdata->vif.type == NL80211_IFTYPE_AP) {
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 2258858..2c7a3dc 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -3043,7 +3043,7 @@ void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work)
+ 		ieee80211_link_release_channel(link);
+ 		cfg80211_cac_event(sdata->dev, &chandef,
+ 				   NL80211_RADAR_CAC_FINISHED,
+-				   GFP_KERNEL);
++				   GFP_KERNEL, 0);
+ 	}
+ }
+ 
+diff --git a/net/mac80211/util.c b/net/mac80211/util.c
+index 424d8d6..5996ea7 100644
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -3468,7 +3468,7 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
+ 			cfg80211_cac_event(sdata->dev,
+ 					   &chandef,
+ 					   NL80211_RADAR_CAC_ABORTED,
+-					   GFP_KERNEL);
++					   GFP_KERNEL, 0);
+ 		}
+ 	}
+ }
+diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
+index eda999d..7d52222 100644
+--- a/net/wireless/mlme.c
++++ b/net/wireless/mlme.c
+@@ -1111,18 +1111,19 @@ EXPORT_SYMBOL(__cfg80211_radar_event);
+ 
+ void cfg80211_cac_event(struct net_device *netdev,
+ 			const struct cfg80211_chan_def *chandef,
+-			enum nl80211_radar_event event, gfp_t gfp)
++			enum nl80211_radar_event event, gfp_t gfp,
++			unsigned int link_id)
+ {
+ 	struct wireless_dev *wdev = netdev->ieee80211_ptr;
+ 	struct wiphy *wiphy = wdev->wiphy;
+ 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ 	unsigned long timeout;
+ 
+-	/* not yet supported */
+-	if (wdev->valid_links)
++	if (WARN_ON(wdev->valid_links &&
++		    !(wdev->valid_links & BIT(link_id))))
+ 		return;
+ 
+-	trace_cfg80211_cac_event(netdev, event);
++	trace_cfg80211_cac_event(netdev, event, link_id);
+ 
+ 	if (WARN_ON(!wdev->links[0].cac_started &&
+ 		    event != NL80211_RADAR_CAC_STARTED))
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index 3e87f5a..6893256 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -10154,7 +10154,20 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
+ 		goto unlock;
+ 	}
+ 
+-	if (cfg80211_beaconing_iface_active(wdev) || wdev->links[0].cac_started) {
++	if (cfg80211_beaconing_iface_active(wdev)) {
++		/* During MLO other link(s) can beacon, only the current link
++		 * can not already beacon
++		 */
++		if (wdev->valid_links &&
++		    !wdev->links[0].ap.beacon_interval) {
++			/* nothing */
++		} else {
++			err = -EBUSY;
++			goto unlock;
++		}
++	}
++
++	if (wdev->links[0].cac_started) {
+ 		err = -EBUSY;
+ 		goto unlock;
+ 	}
+@@ -10174,7 +10187,8 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
+ 	if (WARN_ON(!cac_time_ms))
+ 		cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+ 
+-	err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms);
++	err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms,
++					 0);
+ 	if (!err) {
+ 		wdev->links[0].ap.chandef = chandef;
+ 		wdev->links[0].cac_started = true;
+@@ -16531,10 +16545,10 @@ nl80211_set_ttlm(struct sk_buff *skb, struct genl_info *info)
+ 	SELECTOR(__sel, NETDEV_UP_NOTMX,		\
+ 		 NL80211_FLAG_NEED_NETDEV_UP |		\
+ 		 NL80211_FLAG_NO_WIPHY_MTX)		\
+-	SELECTOR(__sel, NETDEV_UP_NOTMX_NOMLO,		\
++	SELECTOR(__sel, NETDEV_UP_NOTMX_MLO,		\
+ 		 NL80211_FLAG_NEED_NETDEV_UP |		\
+ 		 NL80211_FLAG_NO_WIPHY_MTX |		\
+-		 NL80211_FLAG_MLO_UNSUPPORTED)		\
++		 NL80211_FLAG_MLO_VALID_LINK_ID)	\
+ 	SELECTOR(__sel, NETDEV_UP_CLEAR,		\
+ 		 NL80211_FLAG_NEED_NETDEV_UP |		\
+ 		 NL80211_FLAG_CLEAR_SKB)		\
+@@ -17441,7 +17455,7 @@ static const struct genl_small_ops nl80211_small_ops[] = {
+ 		.flags = GENL_UNS_ADMIN_PERM,
+ 		.internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP |
+ 					 NL80211_FLAG_NO_WIPHY_MTX |
+-					 NL80211_FLAG_MLO_UNSUPPORTED),
++					 NL80211_FLAG_MLO_VALID_LINK_ID),
+ 	},
+ 	{
+ 		.cmd = NL80211_CMD_GET_PROTOCOL_FEATURES,
+diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
+index 951ca95..4e8c895 100644
+--- a/net/wireless/rdev-ops.h
++++ b/net/wireless/rdev-ops.h
+@@ -1200,26 +1200,27 @@ static inline int
+ rdev_start_radar_detection(struct cfg80211_registered_device *rdev,
+ 			   struct net_device *dev,
+ 			   struct cfg80211_chan_def *chandef,
+-			   u32 cac_time_ms)
++			   u32 cac_time_ms, int link_id)
+ {
+ 	int ret = -EOPNOTSUPP;
+ 
+ 	trace_rdev_start_radar_detection(&rdev->wiphy, dev, chandef,
+-					 cac_time_ms);
++					 cac_time_ms, link_id);
+ 	if (rdev->ops->start_radar_detection)
+ 		ret = rdev->ops->start_radar_detection(&rdev->wiphy, dev,
+-						       chandef, cac_time_ms);
++						       chandef, cac_time_ms,
++						       link_id);
+ 	trace_rdev_return_int(&rdev->wiphy, ret);
+ 	return ret;
+ }
+ 
+ static inline void
+ rdev_end_cac(struct cfg80211_registered_device *rdev,
+-	     struct net_device *dev)
++	     struct net_device *dev, unsigned int link_id)
+ {
+-	trace_rdev_end_cac(&rdev->wiphy, dev);
++	trace_rdev_end_cac(&rdev->wiphy, dev, link_id);
+ 	if (rdev->ops->end_cac)
+-		rdev->ops->end_cac(&rdev->wiphy, dev);
++		rdev->ops->end_cac(&rdev->wiphy, dev, link_id);
+ 	trace_rdev_return_void(&rdev->wiphy);
+ }
+ 
+diff --git a/net/wireless/reg.c b/net/wireless/reg.c
+index 0040862..1a393f3 100644
+--- a/net/wireless/reg.c
++++ b/net/wireless/reg.c
+@@ -4233,6 +4233,8 @@ EXPORT_SYMBOL(regulatory_pre_cac_allowed);
+ static void cfg80211_check_and_end_cac(struct cfg80211_registered_device *rdev)
+ {
+ 	struct wireless_dev *wdev;
++	unsigned int link_id;
++
+ 	/* If we finished CAC or received radar, we should end any
+ 	 * CAC running on the same channels.
+ 	 * the check !cfg80211_chandef_dfs_usable contain 2 options:
+@@ -4245,16 +4247,17 @@ static void cfg80211_check_and_end_cac(struct cfg80211_registered_device *rdev)
+ 	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ 		struct cfg80211_chan_def *chandef;
+ 
+-		if (!wdev->links[0].cac_started)
+-			continue;
++		for_each_valid_link(wdev, link_id) {
++			if (!wdev->links[link_id].cac_started)
++				continue;
+ 
+-		/* FIXME: radar detection is tied to link 0 for now */
+-		chandef = wdev_chandef(wdev, 0);
+-		if (!chandef)
+-			continue;
++			chandef = wdev_chandef(wdev, link_id);
++			if (!chandef)
++				continue;
+ 
+-		if (!cfg80211_chandef_dfs_usable(&rdev->wiphy, chandef))
+-			rdev_end_cac(rdev, wdev->netdev);
++			if (!cfg80211_chandef_dfs_usable(&rdev->wiphy, chandef))
++				rdev_end_cac(rdev, wdev->netdev, link_id);
++		}
+ 	}
+ }
+ 
+diff --git a/net/wireless/trace.h b/net/wireless/trace.h
+index 881c01d..a831f94 100644
+--- a/net/wireless/trace.h
++++ b/net/wireless/trace.h
+@@ -806,17 +806,21 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_flush_pmksa,
+ );
+ 
+ TRACE_EVENT(rdev_end_cac,
+-	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+-	TP_ARGS(wiphy, netdev),
++	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
++		 unsigned int link_id),
++	TP_ARGS(wiphy, netdev, link_id),
+ 	TP_STRUCT__entry(
+ 		WIPHY_ENTRY
+ 		NETDEV_ENTRY
++		__field(unsigned int, link_id)
+ 	),
+ 	TP_fast_assign(
+ 		WIPHY_ASSIGN;
+ 		NETDEV_ASSIGN;
++		__entry->link_id = link_id;
+ 	),
+-	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG)
++	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", link_id: %d",
++		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->link_id)
+ );
+ 
+ DECLARE_EVENT_CLASS(station_add_change,
+@@ -2661,24 +2665,26 @@ TRACE_EVENT(rdev_external_auth,
+ TRACE_EVENT(rdev_start_radar_detection,
+ 	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ 		 struct cfg80211_chan_def *chandef,
+-		 u32 cac_time_ms),
+-	TP_ARGS(wiphy, netdev, chandef, cac_time_ms),
++		 u32 cac_time_ms, int link_id),
++	TP_ARGS(wiphy, netdev, chandef, cac_time_ms, link_id),
+ 	TP_STRUCT__entry(
+ 		WIPHY_ENTRY
+ 		NETDEV_ENTRY
+ 		CHAN_DEF_ENTRY
+ 		__field(u32, cac_time_ms)
++		__field(int, link_id)
+ 	),
+ 	TP_fast_assign(
+ 		WIPHY_ASSIGN;
+ 		NETDEV_ASSIGN;
+ 		CHAN_DEF_ASSIGN(chandef);
+ 		__entry->cac_time_ms = cac_time_ms;
++		__entry->link_id = link_id;
+ 	),
+ 	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT
+-		  ", cac_time_ms=%u",
++		  ", cac_time_ms=%u, link_id=%d",
+ 		  WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG,
+-		  __entry->cac_time_ms)
++		  __entry->cac_time_ms, __entry->link_id)
+ );
+ 
+ TRACE_EVENT(rdev_set_mcast_rate,
+@@ -3492,18 +3498,21 @@ TRACE_EVENT(cfg80211_radar_event,
+ );
+ 
+ TRACE_EVENT(cfg80211_cac_event,
+-	TP_PROTO(struct net_device *netdev, enum nl80211_radar_event evt),
+-	TP_ARGS(netdev, evt),
++	TP_PROTO(struct net_device *netdev, enum nl80211_radar_event evt,
++		 unsigned int link_id),
++	TP_ARGS(netdev, evt, link_id),
+ 	TP_STRUCT__entry(
+ 		NETDEV_ENTRY
+ 		__field(enum nl80211_radar_event, evt)
++		__field(unsigned int, link_id)
+ 	),
+ 	TP_fast_assign(
+ 		NETDEV_ASSIGN;
+ 		__entry->evt = evt;
++		__entry->link_id = link_id;
+ 	),
+-	TP_printk(NETDEV_PR_FMT ",  event: %d",
+-		  NETDEV_PR_ARG, __entry->evt)
++	TP_printk(NETDEV_PR_FMT ",  event: %d, link_id=%u",
++		  NETDEV_PR_ARG, __entry->evt, __entry->link_id)
+ );
+ 
+ DECLARE_EVENT_CLASS(cfg80211_rx_evt,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0013-mtk-mac80211-check-the-control-channel-before-downgr.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0013-mtk-mac80211-check-the-control-channel-before-downgr.patch
deleted file mode 100644
index 6aaf0f5..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0013-mtk-mac80211-check-the-control-channel-before-downgr.patch
+++ /dev/null
@@ -1,54 +0,0 @@
-From 32cfa2d7e115d5cdeeb130fbec61a248e9fe3676 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 16 Dec 2022 03:31:06 +0800
-Subject: [PATCH 13/61] mtk: mac80211: check the control channel before
- downgrading the bandwidth
-
----
- net/mac80211/mlme.c | 23 +++++++++++++++++++++++
- 1 file changed, 23 insertions(+)
-
-diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
-index b653c7d..77e5898 100644
---- a/net/mac80211/mlme.c
-+++ b/net/mac80211/mlme.c
-@@ -4994,6 +4994,26 @@ ieee80211_determine_our_sta_mode_assoc(struct ieee80211_sub_if_data *sdata,
- 			       conn->bw_limit, tmp.bw_limit);
- }
- 
-+static bool ieee80211_check_same_ctrl_channel(struct ieee80211_sub_if_data *sdata,
-+					      const struct cfg80211_chan_def *chandef)
-+{
-+	struct ieee80211_local *local = sdata->local;
-+	struct ieee80211_chanctx *ctx;
-+
-+	lockdep_assert_wiphy(local->hw.wiphy);
-+
-+	list_for_each_entry(ctx, &local->chanctx_list, list) {
-+		if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
-+			continue;
-+		if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
-+			continue;
-+		if (chandef->chan == ctx->conf.def.chan)
-+			return true;
-+	}
-+
-+	return false;
-+}
-+
- static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
- 				  struct ieee80211_link_data *link,
- 				  int link_id,
-@@ -5073,6 +5093,9 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
- 	    chanreq.oper.width == NL80211_CHAN_WIDTH_10)
- 		return ret;
- 
-+	if (!ret || !ieee80211_check_same_ctrl_channel(sdata, &chanreq.oper))
-+		return ret;
-+
- 	while (ret && chanreq.oper.width != NL80211_CHAN_WIDTH_20_NOHT) {
- 		ieee80211_chanreq_downgrade(&chanreq, conn);
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0014-bp-wifi-mac80211-handle-DFS-per-link.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0014-bp-wifi-mac80211-handle-DFS-per-link.patch
new file mode 100644
index 0000000..e11bb25
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0014-bp-wifi-mac80211-handle-DFS-per-link.patch
@@ -0,0 +1,151 @@
+From bd62e65e77de5464c1014610d391699e9d8e1bb6 Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 11 Jul 2024 09:21:45 +0530
+Subject: [PATCH 14/89] bp: wifi: mac80211: handle DFS per link
+
+In order to support DFS with MLO, handle the link ID now passed from
+cfg80211, adjust the code to do everything per link and call the
+notifications to cfg80211 correctly.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ net/mac80211/cfg.c  | 26 ++++++++++++++++++--------
+ net/mac80211/link.c | 10 ++++++++++
+ net/mac80211/util.c | 29 +++++++++++++++++++++++------
+ 3 files changed, 51 insertions(+), 14 deletions(-)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index cf60420..d5ddbce 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -3460,6 +3460,7 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
+ 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ 	struct ieee80211_chan_req chanreq = { .oper = *chandef };
+ 	struct ieee80211_local *local = sdata->local;
++	struct ieee80211_link_data *link_data;
+ 	int err;
+ 
+ 	lockdep_assert_wiphy(local->hw.wiphy);
+@@ -3467,16 +3468,20 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
+ 	if (!list_empty(&local->roc_list) || local->scanning)
+ 		return -EBUSY;
+ 
++	link_data = sdata_dereference(sdata->link[link_id], sdata);
++	if (!link_data)
++		return -ENOLINK;
++
+ 	/* whatever, but channel contexts should not complain about that one */
+-	sdata->deflink.smps_mode = IEEE80211_SMPS_OFF;
+-	sdata->deflink.needed_rx_chains = local->rx_chains;
++	link_data->smps_mode = IEEE80211_SMPS_OFF;
++	link_data->needed_rx_chains = local->rx_chains;
+ 
+-	err = ieee80211_link_use_channel(&sdata->deflink, &chanreq,
++	err = ieee80211_link_use_channel(link_data, &chanreq,
+ 					 IEEE80211_CHANCTX_SHARED);
+ 	if (err)
+ 		return err;
+ 
+-	wiphy_delayed_work_queue(wiphy, &sdata->deflink.dfs_cac_timer_work,
++	wiphy_delayed_work_queue(wiphy, &link_data->dfs_cac_timer_work,
+ 				 msecs_to_jiffies(cac_time_ms));
+ 
+ 	return 0;
+@@ -3487,16 +3492,21 @@ static void ieee80211_end_cac(struct wiphy *wiphy,
+ {
+ 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ 	struct ieee80211_local *local = sdata->local;
++	struct ieee80211_link_data *link_data;
+ 
+ 	lockdep_assert_wiphy(local->hw.wiphy);
+ 
+ 	list_for_each_entry(sdata, &local->interfaces, list) {
++		link_data = sdata_dereference(sdata->link[link_id], sdata);
++		if (!link_data)
++			continue;
++
+ 		wiphy_delayed_work_cancel(wiphy,
+-					  &sdata->deflink.dfs_cac_timer_work);
++					  &link_data->dfs_cac_timer_work);
+ 
+-		if (sdata->wdev.links[0].cac_started) {
+-			ieee80211_link_release_channel(&sdata->deflink);
+-			sdata->wdev.links[0].cac_started = false;
++		if (sdata->wdev.links[link_id].cac_started) {
++			ieee80211_link_release_channel(link_data);
++			sdata->wdev.links[link_id].cac_started = false;
+ 		}
+ 	}
+ }
+diff --git a/net/mac80211/link.c b/net/mac80211/link.c
+index b437896..0bbac64 100644
+--- a/net/mac80211/link.c
++++ b/net/mac80211/link.c
+@@ -77,6 +77,16 @@ void ieee80211_link_stop(struct ieee80211_link_data *link)
+ 			  &link->color_change_finalize_work);
+ 	wiphy_work_cancel(link->sdata->local->hw.wiphy,
+ 			  &link->csa.finalize_work);
++
++	if (link->sdata->wdev.links[link->link_id].cac_started) {
++		wiphy_delayed_work_cancel(link->sdata->local->hw.wiphy,
++					  &link->dfs_cac_timer_work);
++		cfg80211_cac_event(link->sdata->dev,
++				   &link->conf->chanreq.oper,
++				   NL80211_RADAR_CAC_ABORTED,
++				   GFP_KERNEL, link->link_id);
++	}
++
+ 	ieee80211_link_release_channel(link);
+ }
+ 
+diff --git a/net/mac80211/util.c b/net/mac80211/util.c
+index 5996ea7..59dde03 100644
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -3455,20 +3455,37 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
+ {
+ 	struct ieee80211_sub_if_data *sdata;
+ 	struct cfg80211_chan_def chandef;
++	struct ieee80211_link_data *link_data;
++	struct ieee80211_bss_conf *link_conf;
++	unsigned int link_id;
+ 
+ 	lockdep_assert_wiphy(local->hw.wiphy);
+ 
+ 	list_for_each_entry(sdata, &local->interfaces, list) {
+-		wiphy_delayed_work_cancel(local->hw.wiphy,
+-					  &sdata->deflink.dfs_cac_timer_work);
++		for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS;
++		     link_id++) {
++			link_data = sdata_dereference(sdata->link[link_id],
++						      sdata);
++			if (!link_data)
++				continue;
++
++			wiphy_delayed_work_cancel(local->hw.wiphy,
++						  &link_data->dfs_cac_timer_work);
++
++			if (!sdata->wdev.links[link_id].cac_started)
++				continue;
++
++			link_conf =
++				rcu_dereference(sdata->vif.link_conf[link_id]);
++			if (!link_conf)
++				continue;
+ 
+-		if (sdata->wdev.links[0].cac_started) {
+-			chandef = sdata->vif.bss_conf.chanreq.oper;
+-			ieee80211_link_release_channel(&sdata->deflink);
++			chandef = link_conf->chanreq.oper;
++			ieee80211_link_release_channel(link_data);
+ 			cfg80211_cac_event(sdata->dev,
+ 					   &chandef,
+ 					   NL80211_RADAR_CAC_ABORTED,
+-					   GFP_KERNEL, 0);
++					   GFP_KERNEL, link_id);
+ 		}
+ 	}
+ }
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0014-mtk-mac80211-fix-tx-amsdu-aggregation.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0014-mtk-mac80211-fix-tx-amsdu-aggregation.patch
deleted file mode 100644
index 8c29c26..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0014-mtk-mac80211-fix-tx-amsdu-aggregation.patch
+++ /dev/null
@@ -1,55 +0,0 @@
-From ead7bd30ec2e85057d69c23429fe7cc2a184e45c Mon Sep 17 00:00:00 2001
-From: TomLiu <tomml.liu@mediatek.com>
-Date: Wed, 14 Dec 2022 00:26:50 -0800
-Subject: [PATCH 14/61] mtk: mac80211: fix tx amsdu aggregation
-
----
- include/net/mac80211.h | 7 +++++++
- net/mac80211/agg-tx.c  | 6 ++++--
- 2 files changed, 11 insertions(+), 2 deletions(-)
-
-diff --git a/include/net/mac80211.h b/include/net/mac80211.h
-index c8375b1..8cda233 100644
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -3043,6 +3043,13 @@ static inline void _ieee80211_hw_set(struct ieee80211_hw *hw,
- }
- #define ieee80211_hw_set(hw, flg)	_ieee80211_hw_set(hw, IEEE80211_HW_##flg)
- 
-+static inline void _ieee80211_hw_clear(struct ieee80211_hw *hw,
-+				     enum ieee80211_hw_flags flg)
-+{
-+	return __clear_bit(flg, hw->flags);
-+}
-+#define ieee80211_hw_clear(hw, flg)	_ieee80211_hw_clear(hw, IEEE80211_HW_##flg)
-+
- /**
-  * struct ieee80211_scan_request - hw scan request
-  *
-diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
-index 21d55dc..068b5b9 100644
---- a/net/mac80211/agg-tx.c
-+++ b/net/mac80211/agg-tx.c
-@@ -66,7 +66,8 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
- 	struct ieee80211_local *local = sdata->local;
- 	struct sk_buff *skb;
- 	struct ieee80211_mgmt *mgmt;
--	u16 capab;
-+	u16 capab = 0;
-+	bool amsdu = ieee80211_hw_check(&local->hw, SUPPORTS_AMSDU_IN_AMPDU);
- 
- 	skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
- 
-@@ -95,7 +96,8 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
- 	mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ;
- 
- 	mgmt->u.action.u.addba_req.dialog_token = dialog_token;
--	capab = IEEE80211_ADDBA_PARAM_AMSDU_MASK;
-+	if (amsdu)
-+		capab = IEEE80211_ADDBA_PARAM_AMSDU_MASK;
- 	capab |= IEEE80211_ADDBA_PARAM_POLICY_MASK;
- 	capab |= u16_encode_bits(tid, IEEE80211_ADDBA_PARAM_TID_MASK);
- 	capab |= u16_encode_bits(agg_size, IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK);
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0015-bp-wifi-cfg80211-mac80211-use-proper-link-ID-for-DFS.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0015-bp-wifi-cfg80211-mac80211-use-proper-link-ID-for-DFS.patch
new file mode 100644
index 0000000..27424fd
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0015-bp-wifi-cfg80211-mac80211-use-proper-link-ID-for-DFS.patch
@@ -0,0 +1,182 @@
+From df89e7fc87a11e44e8a074370a22987467c8e973 Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 11 Jul 2024 09:21:46 +0530
+Subject: [PATCH 15/89] bp: wifi: cfg80211/mac80211: use proper link ID for DFS
+
+Now that all APIs have support to handle DFS per link, use proper link ID
+instead of 0.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ net/mac80211/cfg.c     |  6 +++---
+ net/mac80211/mlme.c    |  4 ++--
+ net/mac80211/scan.c    |  6 ++++--
+ net/wireless/mlme.c    | 10 +++++-----
+ net/wireless/nl80211.c | 17 +++++++++--------
+ 5 files changed, 23 insertions(+), 20 deletions(-)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index d5ddbce..a8e7540 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1656,12 +1656,12 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
+ 	ieee80211_link_info_change_notify(sdata, link,
+ 					  BSS_CHANGED_BEACON_ENABLED);
+ 
+-	if (sdata->wdev.links[0].cac_started) {
++	if (sdata->wdev.links[link_id].cac_started) {
+ 		chandef = link_conf->chanreq.oper;
+ 		wiphy_delayed_work_cancel(wiphy, &link->dfs_cac_timer_work);
+ 		cfg80211_cac_event(sdata->dev, &chandef,
+ 				   NL80211_RADAR_CAC_ABORTED,
+-				   GFP_KERNEL, 0);
++				   GFP_KERNEL, link_id);
+ 	}
+ 
+ 	drv_stop_ap(sdata->local, sdata, link_conf);
+@@ -3961,7 +3961,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ 	if (!list_empty(&local->roc_list) || local->scanning)
+ 		return -EBUSY;
+ 
+-	if (sdata->wdev.links[0].cac_started)
++	if (sdata->wdev.links[link_id].cac_started)
+ 		return -EBUSY;
+ 
+ 	if (WARN_ON(link_id >= IEEE80211_MLD_MAX_NUM_LINKS))
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 2c7a3dc..75b9976 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -3039,11 +3039,11 @@ void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work)
+ 
+ 	lockdep_assert_wiphy(sdata->local->hw.wiphy);
+ 
+-	if (sdata->wdev.links[0].cac_started) {
++	if (sdata->wdev.links[link->link_id].cac_started) {
+ 		ieee80211_link_release_channel(link);
+ 		cfg80211_cac_event(sdata->dev, &chandef,
+ 				   NL80211_RADAR_CAC_FINISHED,
+-				   GFP_KERNEL, 0);
++				   GFP_KERNEL, link->link_id);
+ 	}
+ }
+ 
+diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
+index 7d8e7af..0b8d69f 100644
+--- a/net/mac80211/scan.c
++++ b/net/mac80211/scan.c
+@@ -575,6 +575,7 @@ static bool __ieee80211_can_leave_ch(struct ieee80211_sub_if_data *sdata)
+ {
+ 	struct ieee80211_local *local = sdata->local;
+ 	struct ieee80211_sub_if_data *sdata_iter;
++	unsigned int link_id;
+ 
+ 	lockdep_assert_wiphy(local->hw.wiphy);
+ 
+@@ -585,8 +586,9 @@ static bool __ieee80211_can_leave_ch(struct ieee80211_sub_if_data *sdata)
+ 		return false;
+ 
+ 	list_for_each_entry(sdata_iter, &local->interfaces, list) {
+-		if (sdata_iter->wdev.links[0].cac_started)
+-			return false;
++		for_each_valid_link(&sdata_iter->wdev, link_id)
++			if (sdata_iter->wdev.links[link_id].cac_started)
++				return false;
+ 	}
+ 
+ 	return true;
+diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
+index 7d52222..5c96273 100644
+--- a/net/wireless/mlme.c
++++ b/net/wireless/mlme.c
+@@ -1125,14 +1125,14 @@ void cfg80211_cac_event(struct net_device *netdev,
+ 
+ 	trace_cfg80211_cac_event(netdev, event, link_id);
+ 
+-	if (WARN_ON(!wdev->links[0].cac_started &&
++	if (WARN_ON(!wdev->links[link_id].cac_started &&
+ 		    event != NL80211_RADAR_CAC_STARTED))
+ 		return;
+ 
+ 	switch (event) {
+ 	case NL80211_RADAR_CAC_FINISHED:
+-		timeout = wdev->links[0].cac_start_time +
+-			  msecs_to_jiffies(wdev->links[0].cac_time_ms);
++		timeout = wdev->links[link_id].cac_start_time +
++			  msecs_to_jiffies(wdev->links[link_id].cac_time_ms);
+ 		WARN_ON(!time_after_eq(jiffies, timeout));
+ 		cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
+ 		memcpy(&rdev->cac_done_chandef, chandef,
+@@ -1141,10 +1141,10 @@ void cfg80211_cac_event(struct net_device *netdev,
+ 		cfg80211_sched_dfs_chan_update(rdev);
+ 		fallthrough;
+ 	case NL80211_RADAR_CAC_ABORTED:
+-		wdev->links[0].cac_started = false;
++		wdev->links[link_id].cac_started = false;
+ 		break;
+ 	case NL80211_RADAR_CAC_STARTED:
+-		wdev->links[0].cac_started = true;
++		wdev->links[link_id].cac_started = true;
+ 		break;
+ 	default:
+ 		WARN_ON(1);
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index 6893256..b0c1a4a 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -6099,7 +6099,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
+ 	if (!rdev->ops->start_ap)
+ 		return -EOPNOTSUPP;
+ 
+-	if (wdev->links[0].cac_started)
++	if (wdev->links[link_id].cac_started)
+ 		return -EBUSY;
+ 
+ 	if (wdev->links[link_id].ap.beacon_interval)
+@@ -10105,6 +10105,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
+ 	struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ 	struct net_device *dev = info->user_ptr[1];
+ 	struct wireless_dev *wdev = dev->ieee80211_ptr;
++	int link_id = nl80211_link_id(info->attrs);
+ 	struct wiphy *wiphy = wdev->wiphy;
+ 	struct cfg80211_chan_def chandef;
+ 	enum nl80211_dfs_regions dfs_region;
+@@ -10159,7 +10160,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
+ 		 * can not already beacon
+ 		 */
+ 		if (wdev->valid_links &&
+-		    !wdev->links[0].ap.beacon_interval) {
++		    !wdev->links[link_id].ap.beacon_interval) {
+ 			/* nothing */
+ 		} else {
+ 			err = -EBUSY;
+@@ -10167,7 +10168,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
+ 		}
+ 	}
+ 
+-	if (wdev->links[0].cac_started) {
++	if (wdev->links[link_id].cac_started) {
+ 		err = -EBUSY;
+ 		goto unlock;
+ 	}
+@@ -10188,12 +10189,12 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
+ 		cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+ 
+ 	err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms,
+-					 0);
++					 link_id);
+ 	if (!err) {
+-		wdev->links[0].ap.chandef = chandef;
+-		wdev->links[0].cac_started = true;
+-		wdev->links[0].cac_start_time = jiffies;
+-		wdev->links[0].cac_time_ms = cac_time_ms;
++		wdev->links[link_id].ap.chandef = chandef;
++		wdev->links[link_id].cac_started = true;
++		wdev->links[link_id].cac_start_time = jiffies;
++		wdev->links[link_id].cac_time_ms = cac_time_ms;
+ 	}
+ unlock:
+ 	wiphy_unlock(wiphy);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0015-mtk-mac80211-add-fill-receive-path-ops-to-get-wed-id.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0015-mtk-mac80211-add-fill-receive-path-ops-to-get-wed-id.patch
deleted file mode 100644
index 1521470..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0015-mtk-mac80211-add-fill-receive-path-ops-to-get-wed-id.patch
+++ /dev/null
@@ -1,124 +0,0 @@
-From 5c16c13e56474ee77a9bd8ea59aeaeaae8923ea8 Mon Sep 17 00:00:00 2001
-From: Sujuan Chen <sujuan.chen@mediatek.com>
-Date: Wed, 18 May 2022 15:10:22 +0800
-Subject: [PATCH 15/61] mtk: mac80211: add fill receive path ops to get wed idx
-
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
----
- include/net/mac80211.h    |  5 +++++
- net/mac80211/driver-ops.h | 13 +++++++++++++
- net/mac80211/iface.c      | 23 +++++++++++++++++++++++
- net/mac80211/util.c       |  9 +++++++++
- 4 files changed, 50 insertions(+)
-
-diff --git a/include/net/mac80211.h b/include/net/mac80211.h
-index 8cda233..01cfcc0 100644
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -4390,6 +4390,8 @@ struct ieee80211_prep_tx_info {
-  *	resolve a path for hardware flow offloading
-  * @can_activate_links: Checks if a specific active_links bitmap is
-  *	supported by the driver.
-+ * @net_fill_receive_path: Called from .ndo_fill_receive_path in order to
-+ *	get a path for hardware flow offloading
-  * @change_vif_links: Change the valid links on an interface, note that while
-  *	removing the old link information is still valid (link_conf pointer),
-  *	but may immediately disappear after the function returns. The old or
-@@ -4778,6 +4780,9 @@ struct ieee80211_ops {
- 	bool (*can_activate_links)(struct ieee80211_hw *hw,
- 				   struct ieee80211_vif *vif,
- 				   u16 active_links);
-+	int (*net_fill_receive_path)(struct ieee80211_hw *hw,
-+				     struct net_device_path_ctx *ctx,
-+				     struct net_device_path *path);
- 	int (*change_vif_links)(struct ieee80211_hw *hw,
- 				struct ieee80211_vif *vif,
- 				u16 old_links, u16 new_links,
-diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
-index 1eda9ec..9be87b8 100644
---- a/net/mac80211/driver-ops.h
-+++ b/net/mac80211/driver-ops.h
-@@ -1649,6 +1649,19 @@ static inline int drv_net_fill_forward_path(struct ieee80211_local *local,
- 	return ret;
- }
- 
-+static inline int drv_net_fill_receive_path(struct ieee80211_local *local,
-+					    struct net_device_path_ctx *ctx,
-+					    struct net_device_path *path)
-+{
-+	int ret = -EOPNOTSUPP;
-+
-+	if (local->ops->net_fill_receive_path)
-+		ret = local->ops->net_fill_receive_path(&local->hw,
-+							ctx, path);
-+
-+	return ret;
-+}
-+
- static inline int drv_net_setup_tc(struct ieee80211_local *local,
- 				   struct ieee80211_sub_if_data *sdata,
- 				   struct net_device *dev,
-diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
-index 6363e8c..0ae31a9 100644
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -988,6 +988,28 @@ out:
- 	return ret;
- }
- 
-+static int ieee80211_netdev_fill_receive_path(struct net_device_path_ctx *ctx,
-+					      struct net_device_path *path)
-+{
-+	struct ieee80211_sub_if_data *sdata;
-+	struct ieee80211_local *local;
-+	int ret = -ENOENT;
-+
-+	sdata = IEEE80211_DEV_TO_SUB_IF(ctx->dev);
-+	local = sdata->local;
-+
-+	if (!local->ops->net_fill_receive_path)
-+		return -EOPNOTSUPP;
-+
-+	rcu_read_lock();
-+
-+	ret = drv_net_fill_receive_path(local, ctx, path);
-+
-+	rcu_read_unlock();
-+
-+	return ret;
-+}
-+
- static const struct net_device_ops ieee80211_dataif_8023_ops = {
- #if LINUX_VERSION_IS_LESS(4,10,0)
- 	.ndo_change_mtu = __change_mtu,
-@@ -1006,6 +1028,7 @@ static const struct net_device_ops ieee80211_dataif_8023_ops = {
- #endif
- 	
- 	.ndo_fill_forward_path	= ieee80211_netdev_fill_forward_path,
-+	.ndo_fill_receive_path	= ieee80211_netdev_fill_receive_path,
- 	.ndo_setup_tc		= ieee80211_netdev_setup_tc,
- };
- 
-diff --git a/net/mac80211/util.c b/net/mac80211/util.c
-index cda398d..dd06bd2 100644
---- a/net/mac80211/util.c
-+++ b/net/mac80211/util.c
-@@ -874,6 +874,15 @@ struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif)
- }
- EXPORT_SYMBOL_GPL(ieee80211_vif_to_wdev);
- 
-+struct net_device *ieee80211_vif_to_netdev(struct ieee80211_vif *vif)
-+{
-+	if (!vif)
-+		return NULL;
-+
-+	return vif_to_sdata(vif)->dev;
-+}
-+EXPORT_SYMBOL_GPL(ieee80211_vif_to_netdev);
-+
- /*
-  * Nothing should have been stuffed into the workqueue during
-  * the suspend->resume cycle. Since we can't check each caller
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0016-bp-wifi-mac80211-handle-ieee80211_radar_detected-for.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0016-bp-wifi-mac80211-handle-ieee80211_radar_detected-for.patch
new file mode 100644
index 0000000..16e5390
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0016-bp-wifi-mac80211-handle-ieee80211_radar_detected-for.patch
@@ -0,0 +1,396 @@
+From 758238bd1a839b399d4b1cbf1e8ee8a16729603a Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Thu, 11 Jul 2024 09:21:47 +0530
+Subject: [PATCH 16/89] bp: wifi: mac80211: handle ieee80211_radar_detected()
+ for MLO
+
+Currently DFS works under assumption there could be only one channel
+context in the hardware. Hence, drivers just calls the function
+ieee80211_radar_detected() passing the hardware structure. However, with
+MLO, this obviously will not work since number of channel contexts will be
+more than one and hence drivers would need to pass the channel information
+as well on which the radar is detected.
+
+Hence, in order to support DFS with MLO, do the following changes -
+  * Add channel context conf pointer as an argument to the function
+    ieee80211_radar_detected(). During MLO, drivers would have to pass on
+    which channel context conf radar is detected. Otherwise, drivers could
+    just pass NULL.
+  * ieee80211_radar_detected() will iterate over all channel contexts
+    present and
+  	* if channel context conf is passed, only mark that as radar
+  	  detected
+  	* if NULL is passed, then mark all channel contexts as radar
+  	  detected
+  	* Then as usual, schedule the radar detected work.
+  * In the worker, go over all the contexts again and for all such context
+    which is marked with radar detected, add it to a local linked list.
+  * Cancel the ongoing CAC.
+  * If number of contexts found marked with radar is more than one and if
+    the wiphy does not support MLO flag, throw a warning and return.
+  * Process the local linked list and call the radar event for each entry.
+
+This would also help in scenarios where there is split phy 5 GHz radio,
+which is capable of DFS channels in both lower and upper band. In this
+case, simultaneous radars can be detected.
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ drivers/net/wireless/ath/ath10k/debug.c       |  4 +-
+ drivers/net/wireless/ath/ath10k/mac.c         |  2 +-
+ drivers/net/wireless/ath/ath10k/wmi.c         |  2 +-
+ drivers/net/wireless/ath/ath11k/wmi.c         |  2 +-
+ drivers/net/wireless/ath/ath12k/wmi.c         |  2 +-
+ drivers/net/wireless/ath/ath9k/dfs.c          |  2 +-
+ drivers/net/wireless/ath/ath9k/dfs_debug.c    |  2 +-
+ .../net/wireless/mediatek/mt76/mt7615/mcu.c   |  2 +-
+ .../net/wireless/mediatek/mt76/mt76x02_dfs.c  |  4 +-
+ .../net/wireless/mediatek/mt76/mt7915/mcu.c   |  2 +-
+ .../net/wireless/mediatek/mt76/mt7996/mcu.c   |  2 +-
+ drivers/net/wireless/ti/wl18xx/event.c        |  2 +-
+ include/net/mac80211.h                        |  7 +-
+ net/mac80211/chan.c                           |  1 +
+ net/mac80211/ieee80211_i.h                    |  5 ++
+ net/mac80211/util.c                           | 71 ++++++++++++++++---
+ 16 files changed, 88 insertions(+), 24 deletions(-)
+
+diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
+index e577eed..8337ffb 100644
+--- a/drivers/net/wireless/ath/ath10k/debug.c
++++ b/drivers/net/wireless/ath/ath10k/debug.c
+@@ -3,7 +3,7 @@
+  * Copyright (c) 2005-2011 Atheros Communications Inc.
+  * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
+  * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
++ * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+  */
+ 
+ #include <linux/module.h>
+@@ -1774,7 +1774,7 @@ static ssize_t ath10k_write_simulate_radar(struct file *file,
+ 	if (!arvif->is_started)
+ 		return -EINVAL;
+ 
+-	ieee80211_radar_detected(ar->hw);
++	ieee80211_radar_detected(ar->hw, NULL);
+ 
+ 	return count;
+ }
+diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
+index 9f96315..97bdd1d 100644
+--- a/drivers/net/wireless/ath/ath10k/mac.c
++++ b/drivers/net/wireless/ath/ath10k/mac.c
+@@ -1472,7 +1472,7 @@ static void ath10k_recalc_radar_detection(struct ath10k *ar)
+ 		 * by indicating that radar was detected.
+ 		 */
+ 		ath10k_warn(ar, "failed to start CAC: %d\n", ret);
+-		ieee80211_radar_detected(ar->hw);
++		ieee80211_radar_detected(ar->hw, NULL);
+ 	}
+ }
+ 
+diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
+index 6a54ba7..b36ced3 100644
+--- a/drivers/net/wireless/ath/ath10k/wmi.c
++++ b/drivers/net/wireless/ath/ath10k/wmi.c
+@@ -3990,7 +3990,7 @@ static void ath10k_radar_detected(struct ath10k *ar)
+ 	if (ar->dfs_block_radar_events)
+ 		ath10k_info(ar, "DFS Radar detected, but ignored as requested\n");
+ 	else
+-		ieee80211_radar_detected(ar->hw);
++		ieee80211_radar_detected(ar->hw, NULL);
+ }
+ 
+ static void ath10k_radar_confirmation_work(struct work_struct *work)
+diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
+index 38f175d..8839825 100644
+--- a/drivers/net/wireless/ath/ath11k/wmi.c
++++ b/drivers/net/wireless/ath/ath11k/wmi.c
+@@ -8356,7 +8356,7 @@ ath11k_wmi_pdev_dfs_radar_detected_event(struct ath11k_base *ab, struct sk_buff
+ 	if (ar->dfs_block_radar_events)
+ 		ath11k_info(ab, "DFS Radar detected, but ignored as requested\n");
+ 	else
+-		ieee80211_radar_detected(ar->hw);
++		ieee80211_radar_detected(ar->hw, NULL);
+ 
+ exit:
+ 	rcu_read_unlock();
+diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
+index 9f6be55..350a87a 100644
+--- a/drivers/net/wireless/ath/ath12k/wmi.c
++++ b/drivers/net/wireless/ath/ath12k/wmi.c
+@@ -6788,7 +6788,7 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff
+ 	if (ar->dfs_block_radar_events)
+ 		ath12k_info(ab, "DFS Radar detected, but ignored as requested\n");
+ 	else
+-		ieee80211_radar_detected(ath12k_ar_to_hw(ar));
++		ieee80211_radar_detected(ath12k_ar_to_hw(ar), NULL);
+ 
+ exit:
+ 	rcu_read_unlock();
+diff --git a/drivers/net/wireless/ath/ath9k/dfs.c b/drivers/net/wireless/ath/ath9k/dfs.c
+index 1134921..3689e12 100644
+--- a/drivers/net/wireless/ath/ath9k/dfs.c
++++ b/drivers/net/wireless/ath/ath9k/dfs.c
+@@ -280,7 +280,7 @@ ath9k_dfs_process_radar_pulse(struct ath_softc *sc, struct pulse_event *pe)
+ 	if (!pd->add_pulse(pd, pe, NULL))
+ 		return;
+ 	DFS_STAT_INC(sc, radar_detected);
+-	ieee80211_radar_detected(sc->hw);
++	ieee80211_radar_detected(sc->hw, NULL);
+ }
+ 
+ /*
+diff --git a/drivers/net/wireless/ath/ath9k/dfs_debug.c b/drivers/net/wireless/ath/ath9k/dfs_debug.c
+index 8e18e9b..426caa0 100644
+--- a/drivers/net/wireless/ath/ath9k/dfs_debug.c
++++ b/drivers/net/wireless/ath/ath9k/dfs_debug.c
+@@ -116,7 +116,7 @@ static ssize_t write_file_simulate_radar(struct file *file,
+ {
+ 	struct ath_softc *sc = file->private_data;
+ 
+-	ieee80211_radar_detected(sc->hw);
++	ieee80211_radar_detected(sc->hw, NULL);
+ 
+ 	return count;
+ }
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
+index d50d967..53c8ebe 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
+@@ -394,7 +394,7 @@ mt7615_mcu_rx_radar_detected(struct mt7615_dev *dev, struct sk_buff *skb)
+ 	if (mt76_phy_dfs_state(mphy) < MT_DFS_STATE_CAC)
+ 		return;
+ 
+-	ieee80211_radar_detected(mphy->hw);
++	ieee80211_radar_detected(mphy->hw, NULL);
+ 	dev->hw_pattern++;
+ }
+ 
+diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c
+index 024a5c0..7a07636 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c
++++ b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c
+@@ -630,7 +630,7 @@ static void mt76x02_dfs_tasklet(struct tasklet_struct *t)
+ 		radar_detected = mt76x02_dfs_check_detection(dev);
+ 		if (radar_detected) {
+ 			/* sw detector rx radar pattern */
+-			ieee80211_radar_detected(dev->mt76.hw);
++			ieee80211_radar_detected(dev->mt76.hw, NULL);
+ 			mt76x02_dfs_detector_reset(dev);
+ 
+ 			return;
+@@ -658,7 +658,7 @@ static void mt76x02_dfs_tasklet(struct tasklet_struct *t)
+ 
+ 		/* hw detector rx radar pattern */
+ 		dfs_pd->stats[i].hw_pattern++;
+-		ieee80211_radar_detected(dev->mt76.hw);
++		ieee80211_radar_detected(dev->mt76.hw, NULL);
+ 		mt76x02_dfs_detector_reset(dev);
+ 
+ 		return;
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
+index bce6cda..590c185 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
+@@ -293,7 +293,7 @@ mt7915_mcu_rx_radar_detected(struct mt7915_dev *dev, struct sk_buff *skb)
+ 						&dev->rdd2_chandef,
+ 						GFP_ATOMIC);
+ 	else
+-		ieee80211_radar_detected(mphy->hw);
++		ieee80211_radar_detected(mphy->hw, NULL);
+ 	dev->hw_pattern++;
+ }
+ 
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
+index 2e4fa9f..f892154 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
+@@ -371,7 +371,7 @@ mt7996_mcu_rx_radar_detected(struct mt7996_dev *dev, struct sk_buff *skb)
+ 						&dev->rdd2_chandef,
+ 						GFP_ATOMIC);
+ 	else
+-		ieee80211_radar_detected(mphy->hw);
++		ieee80211_radar_detected(mphy->hw, NULL);
+ 	dev->hw_pattern++;
+ }
+ 
+diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c
+index 34d95f4..a9f090e 100644
+--- a/drivers/net/wireless/ti/wl18xx/event.c
++++ b/drivers/net/wireless/ti/wl18xx/event.c
+@@ -142,7 +142,7 @@ int wl18xx_process_mailbox_events(struct wl1271 *wl)
+ 			    wl18xx_radar_type_decode(mbox->radar_type));
+ 
+ 		if (!wl->radar_debug_mode)
+-			ieee80211_radar_detected(wl->hw);
++			ieee80211_radar_detected(wl->hw, NULL);
+ 	}
+ 
+ 	if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index c43d511..1953f91 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -257,6 +257,7 @@ struct ieee80211_chan_req {
+  *	after RTS/CTS handshake to receive SMPS MIMO transmissions;
+  *	this will always be >= @rx_chains_static.
+  * @radar_enabled: whether radar detection is enabled on this channel.
++ * @radar_detected: whether radar got detected on this channel.
+  * @drv_priv: data area for driver use, will always be aligned to
+  *	sizeof(void *), size is determined in hw information.
+  */
+@@ -269,6 +270,7 @@ struct ieee80211_chanctx_conf {
+ 	u8 rx_chains_static, rx_chains_dynamic;
+ 
+ 	bool radar_enabled;
++	bool radar_detected;
+ 
+ 	u8 drv_priv[] __aligned(sizeof(void *));
+ };
+@@ -6724,8 +6726,11 @@ void ieee80211_cqm_beacon_loss_notify(struct ieee80211_vif *vif, gfp_t gfp);
+  * ieee80211_radar_detected - inform that a radar was detected
+  *
+  * @hw: pointer as obtained from ieee80211_alloc_hw()
++ * @chanctx_conf: Channel context on which radar is detected. Mandatory to
++ *	pass a valid pointer during MLO. For non-MLO %NULL can be passed
+  */
+-void ieee80211_radar_detected(struct ieee80211_hw *hw);
++void ieee80211_radar_detected(struct ieee80211_hw *hw,
++			      struct ieee80211_chanctx_conf *chanctx_conf);
+ 
+ /**
+  * ieee80211_chswitch_done - Complete channel switch process
+diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
+index e856772..6041735 100644
+--- a/net/mac80211/chan.c
++++ b/net/mac80211/chan.c
+@@ -681,6 +681,7 @@ ieee80211_alloc_chanctx(struct ieee80211_local *local,
+ 	ctx->mode = mode;
+ 	ctx->conf.radar_enabled = false;
+ 	ctx->conf.radio_idx = radio_idx;
++	ctx->conf.radar_detected = false;
+ 	_ieee80211_recalc_chanctx_min_def(local, ctx, NULL, false);
+ 
+ 	return ctx;
+diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
+index f7c9892..bea5058 100644
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -1335,6 +1335,11 @@ enum mac80211_scan_state {
+ 
+ DECLARE_STATIC_KEY_FALSE(aql_disable);
+ 
++struct radar_info {
++	struct list_head list;
++	struct cfg80211_chan_def chandef;
++};
++
+ struct ieee80211_local {
+ 	/* embed the driver visible part.
+ 	 * don't cast (use the static inlines below), but we keep
+diff --git a/net/mac80211/util.c b/net/mac80211/util.c
+index 59dde03..ecda005 100644
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -3495,35 +3495,88 @@ void ieee80211_dfs_radar_detected_work(struct wiphy *wiphy,
+ {
+ 	struct ieee80211_local *local =
+ 		container_of(work, struct ieee80211_local, radar_detected_work);
+-	struct cfg80211_chan_def chandef = local->hw.conf.chandef;
++	struct radar_info *radar_info, *temp;
++	struct list_head radar_info_list;
+ 	struct ieee80211_chanctx *ctx;
++	bool trigger_event = true;
+ 	int num_chanctx = 0;
+ 
+ 	lockdep_assert_wiphy(local->hw.wiphy);
+ 
++	INIT_LIST_HEAD(&radar_info_list);
++
+ 	list_for_each_entry(ctx, &local->chanctx_list, list) {
+ 		if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER)
+ 			continue;
+ 
+-		num_chanctx++;
+-		chandef = ctx->conf.def;
++		if (ctx->conf.radar_detected) {
++			ctx->conf.radar_detected = false;
++			num_chanctx++;
++
++			radar_info = kzalloc(sizeof(*radar_info), GFP_KERNEL);
++			if (WARN_ON(!radar_info))
++				continue;
++
++			INIT_LIST_HEAD(&radar_info->list);
++			radar_info->chandef = ctx->conf.def;
++			list_add_tail(&radar_info->list, &radar_info_list);
++		}
+ 	}
+ 
+ 	ieee80211_dfs_cac_cancel(local);
+ 
+-	if (num_chanctx > 1)
+-		/* XXX: multi-channel is not supported yet */
+-		WARN_ON(1);
+-	else
+-		cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);
++	if (num_chanctx > 1) {
++		/* XXX: multi-channel is not supported yet in case of non-MLO */
++		if (WARN_ON(!(wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO)))
++			trigger_event = false;
++	}
++
++	/* this will clear the nodes which were created and added above.
++	 * trigger_event decides whether to trigger the radar event or not
++	 */
++	list_for_each_entry_safe(radar_info, temp, &radar_info_list,
++				 list) {
++		if (trigger_event)
++			cfg80211_radar_event(local->hw.wiphy,
++					     &radar_info->chandef,
++					     GFP_KERNEL);
++		kfree(radar_info);
++	}
+ }
+ 
+-void ieee80211_radar_detected(struct ieee80211_hw *hw)
++static void
++ieee80211_radar_mark_chan_ctx_iterator(struct ieee80211_hw *hw,
++				       struct ieee80211_chanctx_conf *chanctx_conf,
++				       void *data)
++{
++	struct ieee80211_chanctx *ctx =
++		container_of(chanctx_conf, struct ieee80211_chanctx,
++			     conf);
++	struct ieee80211_chanctx_conf *itr_data =
++		(struct ieee80211_chanctx_conf *)data;
++
++	if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER)
++		return;
++
++	if (itr_data) {
++		if (itr_data == chanctx_conf)
++			chanctx_conf->radar_detected = true;
++		return;
++	}
++
++	chanctx_conf->radar_detected = true;
++}
++
++void ieee80211_radar_detected(struct ieee80211_hw *hw,
++			      struct ieee80211_chanctx_conf *chanctx_conf)
+ {
+ 	struct ieee80211_local *local = hw_to_local(hw);
+ 
+ 	trace_api_radar_detected(local);
+ 
++	ieee80211_iter_chan_contexts_atomic(hw, ieee80211_radar_mark_chan_ctx_iterator,
++					    chanctx_conf);
++
+ 	wiphy_work_queue(hw->wiphy, &local->radar_detected_work);
+ }
+ EXPORT_SYMBOL(ieee80211_radar_detected);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0016-mtk-mac80211-track-obss-color-bitmap.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0016-mtk-mac80211-track-obss-color-bitmap.patch
deleted file mode 100644
index 01c513d..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0016-mtk-mac80211-track-obss-color-bitmap.patch
+++ /dev/null
@@ -1,84 +0,0 @@
-From 71be2e718dc884fb35433338dc21d6547b237819 Mon Sep 17 00:00:00 2001
-From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Date: Mon, 13 Mar 2023 05:23:37 +0800
-Subject: [PATCH 16/61] mtk: mac80211: track obss color bitmap
-
-Track OBSS BSS color when receive their beacon.
-
-Adding 2 tracepoint for debug, usage:
-echo 1 > /sys/kernel/debug/tracing/events/mac80211/bss_color_bitmap/enable
-echo 1 > /sys/kernel/debug/tracing/events/mac80211/bss_color_collision/enable
-
----
- include/net/mac80211.h |  1 +
- net/mac80211/rx.c      |  6 +++++-
- net/mac80211/trace.h   | 22 ++++++++++++++++++++++
- 3 files changed, 28 insertions(+), 1 deletion(-)
-
-diff --git a/include/net/mac80211.h b/include/net/mac80211.h
-index 01cfcc0..965a026 100644
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -762,6 +762,7 @@ struct ieee80211_bss_conf {
- 	} he_oper;
- 	struct ieee80211_he_obss_pd he_obss_pd;
- 	struct cfg80211_he_bss_color he_bss_color;
-+	u64 used_color_bitmap;
- 	struct ieee80211_fils_discovery fils_discovery;
- 	u32 unsol_bcast_probe_resp_interval;
- 	struct cfg80211_bitrate_mask beacon_tx_rate;
-diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
-index 89a1199..65982a6 100644
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -3395,9 +3395,13 @@ ieee80211_rx_check_bss_color_collision(struct ieee80211_rx_data *rx)
- 
- 		color = le32_get_bits(he_oper->he_oper_params,
- 				      IEEE80211_HE_OPERATION_BSS_COLOR_MASK);
-+
-+		bss_conf->used_color_bitmap |= BIT_ULL(color);
-+
-+		// trace_bss_color_bitmap(color, bss_conf->used_color_bitmap);
- 		if (color == bss_conf->he_bss_color.color)
- 			ieee80211_obss_color_collision_notify(&rx->sdata->vif,
--							      BIT_ULL(color));
-+							      bss_conf->used_color_bitmap);
- 	}
- }
- 
-diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
-index 8e758b5..9ec45ce 100644
---- a/net/mac80211/trace.h
-+++ b/net/mac80211/trace.h
-@@ -3145,6 +3145,28 @@ TRACE_EVENT(drv_neg_ttlm_res,
- 		  LOCAL_PR_ARG, VIF_PR_ARG, __entry->res
- 	)
- );
-+
-+TRACE_EVENT(bss_color_bitmap,
-+	TP_PROTO(u8 color,
-+		u64 color_bitmap),
-+
-+	TP_ARGS(color, color_bitmap),
-+
-+	TP_STRUCT__entry(
-+		__field(u8, color)
-+		__field(u64, color_bitmap)
-+	),
-+
-+	TP_fast_assign(
-+		__entry->color = color;
-+		__entry->color_bitmap = color_bitmap;
-+	),
-+
-+	TP_printk(
-+		"color=%u color_bitmap=0x%llx", __entry->color, __entry->color_bitmap
-+	)
-+);
-+
- #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
- 
- #undef TRACE_INCLUDE_PATH
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0017-mtk-mac80211-do-not-setup-twt-when-twt-responder-is-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0017-mtk-mac80211-do-not-setup-twt-when-twt-responder-is-.patch
new file mode 100644
index 0000000..6cced0d
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0017-mtk-mac80211-do-not-setup-twt-when-twt-responder-is-.patch
@@ -0,0 +1,27 @@
+From 1741b996a7e2470a5b706f14e61f2729cdb02e2c Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Tue, 18 Jan 2022 20:29:44 +0800
+Subject: [PATCH 17/89] mtk: mac80211: do not setup twt when twt responder is
+ false
+
+---
+ net/mac80211/rx.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
+index 956110f..4ca7ae1 100644
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -3461,6 +3461,9 @@ ieee80211_process_rx_twt_action(struct ieee80211_rx_data *rx)
+ 	if (sdata->vif.type != NL80211_IFTYPE_AP)
+ 		return false;
+ 
++	if (!sdata->vif.bss_conf.twt_responder)
++		return false;
++
+ 	if (!rx->local->ops->add_twt_setup)
+ 		return false;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0017-mtk-mac80211-update-max_bssid_indicator-based-on-rea.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0017-mtk-mac80211-update-max_bssid_indicator-based-on-rea.patch
deleted file mode 100644
index b764033..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0017-mtk-mac80211-update-max_bssid_indicator-based-on-rea.patch
+++ /dev/null
@@ -1,41 +0,0 @@
-From cd9eddae9afaece218189e1d3ccaffe256b3ccc8 Mon Sep 17 00:00:00 2001
-From: "Allen.Ye" <allen.ye@mediatek.com>
-Date: Fri, 14 Apr 2023 05:05:17 +0800
-Subject: [PATCH 17/61] mtk: mac80211: update max_bssid_indicator based on real
- BSS numbers
-
-Fix max_bssid_indicator get empty value due to wrong pointer.
-
----
- net/mac80211/cfg.c | 5 +++--
- 1 file changed, 3 insertions(+), 2 deletions(-)
-
-diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
-index 3f4c129..cb91223 100644
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -1167,9 +1167,11 @@ ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
- 	/* copy in optional mbssid_ies */
- 	if (mbssid) {
- 		u8 *pos = new->tail + new->tail_len;
-+		u8 *bssid_indicator;
- 
- 		new->mbssid_ies = (void *)pos;
- 		pos += struct_size(new->mbssid_ies, elem, mbssid->cnt);
-+		bssid_indicator = pos + 2;
- 		pos += ieee80211_copy_mbssid_beacon(pos, new->mbssid_ies,
- 						    mbssid);
- 		if (rnr) {
-@@ -1178,8 +1180,7 @@ ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
- 			ieee80211_copy_rnr_beacon(pos, new->rnr_ies, rnr);
- 		}
- 		/* update bssid_indicator */
--		link_conf->bssid_indicator =
--			ilog2(__roundup_pow_of_two(mbssid->cnt + 1));
-+		sdata->vif.bss_conf.bssid_indicator = *(bssid_indicator);
- 	}
- 
- 	if (csa) {
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0018-mtk-cfg80211-extend-CAC-time-for-weather-radar-chann.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0018-mtk-cfg80211-extend-CAC-time-for-weather-radar-chann.patch
new file mode 100644
index 0000000..af9ca97
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0018-mtk-cfg80211-extend-CAC-time-for-weather-radar-chann.patch
@@ -0,0 +1,61 @@
+From 1711e567e6a8508b831b80d604501260d67ea214 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 29 Mar 2022 16:06:30 +0800
+Subject: [PATCH 18/89] mtk: cfg80211: extend CAC time for weather radar
+ channels
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ include/net/cfg80211.h | 1 +
+ net/wireless/chan.c    | 7 +++++++
+ net/wireless/nl80211.c | 3 +++
+ 3 files changed, 11 insertions(+)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index 906e48b..c78a4a2 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -166,6 +166,7 @@ enum ieee80211_channel_flags {
+ 	(IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
+ 
+ #define IEEE80211_DFS_MIN_CAC_TIME_MS		60000
++#define IEEE80211_DFS_WEATHER_MIN_CAC_TIME_MS	600000
+ #define IEEE80211_DFS_MIN_NOP_TIME_MS		(30 * 60 * 1000)
+ 
+ /**
+diff --git a/net/wireless/chan.c b/net/wireless/chan.c
+index edc9034..be2261f 100644
+--- a/net/wireless/chan.c
++++ b/net/wireless/chan.c
+@@ -1190,6 +1190,13 @@ static unsigned int cfg80211_get_chans_dfs_cac_time(struct wiphy *wiphy,
+ 		if (!(c->flags & IEEE80211_CHAN_RADAR))
+ 			continue;
+ 
++		/* weather radar in ETSI */
++		if (reg_get_dfs_region(wiphy) == NL80211_DFS_ETSI &&
++		    freq >= MHZ_TO_KHZ(5600) && freq <= MHZ_TO_KHZ(5640) &&
++		    dfs_cac_ms < IEEE80211_DFS_WEATHER_MIN_CAC_TIME_MS &&
++		    c->dfs_state == NL80211_DFS_USABLE)
++			dfs_cac_ms = IEEE80211_DFS_WEATHER_MIN_CAC_TIME_MS;
++
+ 		if (c->dfs_cac_ms > dfs_cac_ms)
+ 			dfs_cac_ms = c->dfs_cac_ms;
+ 	}
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index b0c1a4a..93b37dd 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -10188,6 +10188,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
+ 	if (WARN_ON(!cac_time_ms))
+ 		cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+ 
++	pr_info("%s: region = %u, center freq1 = %u, center freq2 = %u, cac time ms = %u\n",
++		__func__, dfs_region, chandef.center_freq1, chandef.center_freq2, cac_time_ms);
++
+ 	err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms,
+ 					 link_id);
+ 	if (!err) {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0018-mtk-mac80211-support-configurable-addba-resp-time.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0018-mtk-mac80211-support-configurable-addba-resp-time.patch
deleted file mode 100644
index a4b0d15..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0018-mtk-mac80211-support-configurable-addba-resp-time.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From cc6fe6fd359d941ccd376aa6f185eaeb0020fb04 Mon Sep 17 00:00:00 2001
-From: Lian Chen <lian.chen@mediatek.com>
-Date: Wed, 7 Jun 2023 15:30:34 +0800
-Subject: [PATCH 18/61] mtk: mac80211: support configurable addba resp time.
-
----
- net/mac80211/agg-tx.c | 8 +++++++-
- 1 file changed, 7 insertions(+), 1 deletion(-)
-
-diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
-index 068b5b9..af3d8e6 100644
---- a/net/mac80211/agg-tx.c
-+++ b/net/mac80211/agg-tx.c
-@@ -16,10 +16,16 @@
- #include <linux/slab.h>
- #include <linux/export.h>
- #include <net/mac80211.h>
-+#include <linux/moduleparam.h>
- #include "ieee80211_i.h"
- #include "driver-ops.h"
- #include "wme.h"
- 
-+static int addba_resp_wait_count = 2;
-+module_param(addba_resp_wait_count, int, 0644);
-+MODULE_PARM_DESC(addba_resp_wait_count,
-+		 "Number of ADDBA_RESP_INTERVAL to wait for addba response");
-+
- /**
-  * DOC: TX A-MPDU aggregation
-  *
-@@ -466,7 +472,7 @@ static void ieee80211_send_addba_with_timeout(struct sta_info *sta,
- 	lockdep_assert_wiphy(sta->local->hw.wiphy);
- 
- 	/* activate the timer for the recipient's addBA response */
--	mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL);
-+	mod_timer(&tid_tx->addba_resp_timer, jiffies + addba_resp_wait_count * ADDBA_RESP_INTERVAL);
- 	ht_dbg(sdata, "activated addBA response timer on %pM tid %d\n",
- 	       sta->sta.addr, tid);
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0019-mtk-mac80211-add-sta-assisted-DFS-state-update-mecha.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0019-mtk-mac80211-add-sta-assisted-DFS-state-update-mecha.patch
deleted file mode 100644
index aa94d8f..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0019-mtk-mac80211-add-sta-assisted-DFS-state-update-mecha.patch
+++ /dev/null
@@ -1,184 +0,0 @@
-From 103b24de6741cb11d088e6e2be478c7839d32ec4 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Feb 2023 14:25:24 +0800
-Subject: [PATCH 19/61] mtk: mac80211: add sta-assisted DFS state update
- mechanism
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- include/net/cfg80211.h       | 14 +++++++++
- include/uapi/linux/nl80211.h |  6 ++++
- net/mac80211/mlme.c          | 14 +++++++++
- net/wireless/chan.c          | 61 ++++++++++++++++++++++++++++++++++++
- 4 files changed, 95 insertions(+)
-
-diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
-index c55028f..19cf7f7 100644
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -8679,6 +8679,20 @@ void cfg80211_cac_event(struct net_device *netdev,
- 			const struct cfg80211_chan_def *chandef,
- 			enum nl80211_radar_event event, gfp_t gfp);
- 
-+/**
-+ * cfg80211_sta_update_dfs_state - Update channel's DFS state during STA channel switch,
-+ *				   association, and disassociation
-+ * @wdev: the wireless device
-+ * @bss_chandef: the current BSS channel definition
-+ * @csa_chandef: the CSA channel definition
-+ * @associated: whether STA is during association or disassociation process
-+ *
-+ */
-+void cfg80211_sta_update_dfs_state(struct wireless_dev *wdev,
-+				   const struct cfg80211_chan_def *bss_chandef,
-+				   const struct cfg80211_chan_def *csa_chandef,
-+				   bool associated);
-+
- /**
-  * cfg80211_background_cac_abort - Channel Availability Check offchan abort event
-  * @wiphy: the wiphy
-diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
-index f917bc6..7999a65 100644
---- a/include/uapi/linux/nl80211.h
-+++ b/include/uapi/linux/nl80211.h
-@@ -6822,6 +6822,10 @@ enum nl80211_smps_mode {
-  *	applicable for ETSI dfs domain where pre-CAC is valid for ever.
-  * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
-  *	should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
-+ * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
-+ *	when receiving CSA/assoc resp
-+ * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
-+ *	when STA is disconnected or leaving the channel
-  */
- enum nl80211_radar_event {
- 	NL80211_RADAR_DETECTED,
-@@ -6830,6 +6834,8 @@ enum nl80211_radar_event {
- 	NL80211_RADAR_NOP_FINISHED,
- 	NL80211_RADAR_PRE_CAC_EXPIRED,
- 	NL80211_RADAR_CAC_STARTED,
-+	NL80211_RADAR_STA_CAC_SKIPPED,
-+	NL80211_RADAR_STA_CAC_EXPIRED,
- };
- 
- /**
-diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
-index 77e5898..52d1cd8 100644
---- a/net/mac80211/mlme.c
-+++ b/net/mac80211/mlme.c
-@@ -2170,6 +2170,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link,
- 		sdata->csa_blocked_tx = true;
- 	}
- 
-+	cfg80211_sta_update_dfs_state(&sdata->wdev,
-+				      &link->conf->chanreq.oper,
-+				      &link->csa_chanreq.oper,
-+				      sdata->vif.cfg.assoc);
-+
- 	cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chanreq.oper,
- 					  link->link_id, csa_ie.count,
- 					  csa_ie.mode);
-@@ -3245,6 +3250,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
- 		link = sdata_dereference(sdata->link[link_id], sdata);
- 		if (!link)
- 			continue;
-+
-+		cfg80211_sta_update_dfs_state(&sdata->wdev,
-+					      &link->conf->chanreq.oper,
-+					      NULL, sdata->vif.cfg.assoc);
- 		ieee80211_link_release_channel(link);
- 	}
- 
-@@ -5512,6 +5521,11 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
- 		for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
- 			if (link->tx_conf[ac].uapsd)
- 				resp.uapsd_queues |= ieee80211_ac_to_qos_mask[ac];
-+
-+		if (status_code == WLAN_STATUS_SUCCESS)
-+			cfg80211_sta_update_dfs_state(&sdata->wdev,
-+						      &link->conf->chanreq.oper,
-+						      NULL, sdata->vif.cfg.assoc);
- 	}
- 
- 	if (ieee80211_vif_is_mld(&sdata->vif)) {
-diff --git a/net/wireless/chan.c b/net/wireless/chan.c
-index 2224329..c851db8 100644
---- a/net/wireless/chan.c
-+++ b/net/wireless/chan.c
-@@ -14,6 +14,7 @@
- #include <net/cfg80211.h>
- #include "core.h"
- #include "rdev-ops.h"
-+#include "nl80211.h"
- 
- static bool cfg80211_valid_60g_freq(u32 freq)
- {
-@@ -1671,6 +1672,66 @@ bool cfg80211_any_usable_channels(struct wiphy *wiphy,
- }
- EXPORT_SYMBOL(cfg80211_any_usable_channels);
- 
-+static void cfg80211_sta_radar_notify(struct wiphy *wiphy,
-+				      const struct cfg80211_chan_def *chandef,
-+				      enum nl80211_radar_event event)
-+{
-+	struct wireless_dev *wdev;
-+
-+	list_for_each_entry(wdev, &wiphy->wdev_list, list) {
-+		if (cfg80211_chandef_dfs_required(wiphy, chandef, wdev->iftype) > 0) {
-+			nl80211_radar_notify(wiphy_to_rdev(wiphy), chandef,
-+					     event, wdev->netdev, GFP_KERNEL);
-+			return;
-+		}
-+	}
-+}
-+
-+void cfg80211_sta_update_dfs_state(struct wireless_dev *wdev,
-+				   const struct cfg80211_chan_def *bss_chandef,
-+				   const struct cfg80211_chan_def *csa_chandef,
-+				   bool associated)
-+{
-+	bool csa_active = !!csa_chandef;
-+	enum nl80211_dfs_state dfs_state = NL80211_DFS_USABLE;
-+	enum nl80211_radar_event event = NL80211_RADAR_STA_CAC_EXPIRED;
-+
-+	if (!bss_chandef || !bss_chandef->chan ||
-+	    bss_chandef->chan->band != NL80211_BAND_5GHZ)
-+		return;
-+
-+	/* assume csa channel is cac completed */
-+	if (csa_active &&
-+	    (cfg80211_chandef_dfs_usable(wdev->wiphy, csa_chandef) ||
-+	    cfg80211_chandef_dfs_available(wdev->wiphy, csa_chandef))) {
-+		cfg80211_set_dfs_state(wdev->wiphy, csa_chandef, NL80211_DFS_AVAILABLE);
-+		cfg80211_sta_radar_notify(wdev->wiphy, csa_chandef,
-+					  NL80211_RADAR_STA_CAC_SKIPPED);
-+		netdev_info(wdev->netdev, "Set CSA channel's DFS state to available\n");
-+	}
-+
-+	/* avoid updating the dfs state during nop */
-+	if (!cfg80211_chandef_dfs_usable(wdev->wiphy, bss_chandef) &&
-+	    !cfg80211_chandef_dfs_available(wdev->wiphy, bss_chandef))
-+		return;
-+
-+	if (associated && !csa_active) {
-+		dfs_state = NL80211_DFS_AVAILABLE;
-+		event = NL80211_RADAR_STA_CAC_SKIPPED;
-+	}
-+
-+	cfg80211_set_dfs_state(wdev->wiphy, bss_chandef, dfs_state);
-+	cfg80211_sta_radar_notify(wdev->wiphy, bss_chandef, event);
-+
-+	if (csa_active)
-+		netdev_info(wdev->netdev, "Set origin channel's DFS state to usable\n");
-+	else
-+		netdev_info(wdev->netdev, "Set BSS channel's DFS state to %s due to %s\n",
-+			    (dfs_state == NL80211_DFS_USABLE) ? "usable" : "available",
-+			    associated ? "association" : "disassociation");
-+}
-+EXPORT_SYMBOL(cfg80211_sta_update_dfs_state);
-+
- struct cfg80211_chan_def *wdev_chandef(struct wireless_dev *wdev,
- 				       unsigned int link_id)
- {
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0019-mtk-mac80211-it-s-invalid-case-when-frag_threshold-i.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0019-mtk-mac80211-it-s-invalid-case-when-frag_threshold-i.patch
new file mode 100644
index 0000000..97c32d9
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0019-mtk-mac80211-it-s-invalid-case-when-frag_threshold-i.patch
@@ -0,0 +1,28 @@
+From 31b43a255c2fc56b6cd6d6ccd70a98346d8df78d Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Fri, 1 Apr 2022 09:15:21 +0800
+Subject: [PATCH 19/89] mtk: mac80211: it's invalid case when frag_threshold is
+ greater than 2346
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ net/wireless/nl80211.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index 93b37dd..abda8ca 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -3832,6 +3832,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
+ 			goto out;
+ 		}
+ 
++		if (frag_threshold >= 2346)
++			frag_threshold = (u32) -1;
++
+ 		if (frag_threshold != (u32) -1) {
+ 			/*
+ 			 * Fragments (apart from the last one) are required to
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0020-mtk-cfg80211-implement-DFS-status-show-cac-and-nop-s.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0020-mtk-cfg80211-implement-DFS-status-show-cac-and-nop-s.patch
new file mode 100644
index 0000000..0f137f6
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0020-mtk-cfg80211-implement-DFS-status-show-cac-and-nop-s.patch
@@ -0,0 +1,527 @@
+From 83c09e9304119117ce7d517de2fa44e2c158b307 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 22 Sep 2022 14:27:41 +0800
+Subject: [PATCH 20/89] mtk: cfg80211: implement DFS status show, cac and nop
+ skip command via debugfs
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+Refactor DFS debugfs command for MLO
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ include/net/cfg80211.h  |   1 +
+ net/mac80211/cfg.c      |  25 +++
+ net/wireless/core.h     |   3 +
+ net/wireless/debugfs.c  | 326 +++++++++++++++++++++++++++++++++++++++-
+ net/wireless/mlme.c     |   6 +
+ net/wireless/rdev-ops.h |  14 ++
+ net/wireless/trace.h    |  13 ++
+ 7 files changed, 381 insertions(+), 7 deletions(-)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index c78a4a2..cc1ed48 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -4956,6 +4956,7 @@ struct cfg80211_ops {
+ 	int	(*set_ttlm)(struct wiphy *wiphy, struct net_device *dev,
+ 			    struct cfg80211_ttlm_params *params);
+ 	u32	(*get_radio_mask)(struct wiphy *wiphy, struct net_device *dev);
++	void	(*skip_cac)(struct wireless_dev *wdev, unsigned int link_id);
+ };
+ 
+ /*
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index a8e7540..42b6d4a 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -5093,6 +5093,30 @@ ieee80211_set_ttlm(struct wiphy *wiphy, struct net_device *dev,
+ 	return ieee80211_req_neg_ttlm(sdata, params);
+ }
+ 
++static void
++ieee80211_skip_cac(struct wireless_dev *wdev, unsigned int link_id)
++{
++	struct net_device *dev = wdev->netdev;
++	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
++	struct ieee80211_link_data *link;
++	unsigned int cac_time_ms;
++
++	link = sdata_dereference(sdata->link[link_id], sdata);
++	if (!link)
++		return;
++
++	wiphy_delayed_work_cancel(sdata->local->hw.wiphy,
++				  &link->dfs_cac_timer_work);
++	if (wdev->cac_started) {
++		ieee80211_link_release_channel(link);
++		cac_time_ms = wdev->cac_time_ms;
++		wdev->cac_start_time = jiffies -
++				       msecs_to_jiffies(cac_time_ms + 1);
++		cfg80211_cac_event(wdev->netdev, &link->conf->chanreq.oper,
++				   NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
++	}
++}
++
+ const struct cfg80211_ops mac80211_config_ops = {
+ 	.add_virtual_intf = ieee80211_add_iface,
+ 	.del_virtual_intf = ieee80211_del_iface,
+@@ -5207,4 +5231,5 @@ const struct cfg80211_ops mac80211_config_ops = {
+ 	.set_hw_timestamp = ieee80211_set_hw_timestamp,
+ 	.set_ttlm = ieee80211_set_ttlm,
+ 	.get_radio_mask = ieee80211_get_radio_mask,
++	.skip_cac = ieee80211_skip_cac,
+ };
+diff --git a/net/wireless/core.h b/net/wireless/core.h
+index cebc5a1..d6ed8be 100644
+--- a/net/wireless/core.h
++++ b/net/wireless/core.h
+@@ -86,6 +86,9 @@ struct cfg80211_registered_device {
+ 
+ 	struct wireless_dev *background_radar_wdev;
+ 	struct cfg80211_chan_def background_radar_chandef;
++	bool background_cac_started;
++	unsigned long background_cac_start_time;
++	unsigned int background_cac_time_ms;
+ 	struct delayed_work background_cac_done_wk;
+ 	struct work_struct background_cac_abort_wk;
+ 
+diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c
+index 40e4907..27b4608 100644
+--- a/net/wireless/debugfs.c
++++ b/net/wireless/debugfs.c
+@@ -10,6 +10,7 @@
+ #include <linux/slab.h>
+ #include "core.h"
+ #include "debugfs.h"
++#include "rdev-ops.h"
+ 
+ #define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...)		\
+ static ssize_t name## _read(struct file *file, char __user *userbuf,	\
+@@ -97,18 +98,329 @@ static const struct file_operations ht40allow_map_ops = {
+ 	.llseek = default_llseek,
+ };
+ 
+-#define DEBUGFS_ADD(name)						\
+-	debugfs_create_file(#name, 0444, phyd, &rdev->wiphy, &name## _ops)
++static int dfs_print_chan(struct ieee80211_channel *chan, int remain_time, int wait_time,
++			  char *buf, int buf_size, int offset, bool is_background)
++{
++	if (WARN_ON(offset > buf_size))
++		return 0;
++
++	if (chan->dfs_state == NL80211_DFS_UNAVAILABLE) {
++		offset += scnprintf(buf + offset, buf_size - offset,
++				    "	Channel = %d, DFS_state = Unavailable",
++				    chan->hw_value);
++		if (remain_time > 0)
++			offset += scnprintf(buf + offset, buf_size - offset,
++					    ", Non-occupancy Remain Time = %d / %d [sec]",
++					    remain_time, wait_time);
++		else
++			offset += scnprintf(buf + offset, buf_size - offset,
++					    ", Changing state...");
++	} else if (chan->dfs_state == NL80211_DFS_USABLE) {
++		offset += scnprintf(buf + offset, buf_size - offset,
++				    "	Channel = %d, DFS_state = Usable",
++				    chan->hw_value);
++		if (remain_time > 0)
++			offset += scnprintf(buf + offset, buf_size - offset,
++					    ", CAC Remain Time = %d / %d [sec]",
++					    remain_time, wait_time);
++	} else if (chan->dfs_state == NL80211_DFS_AVAILABLE) {
++		offset += scnprintf(buf + offset, buf_size - offset,
++				    "	Channel = %d, DFS_state = Available",
++				    chan->hw_value);
++	} else {
++		offset += scnprintf(buf + offset, buf_size - offset,
++				    "	Channel = %d, DFS_state = Unknown",
++				    chan->hw_value);
++	}
++
++	if (is_background)
++		offset += scnprintf(buf + offset, buf_size - offset,
++				    " (background chain)");
++	offset += scnprintf(buf + offset, buf_size - offset, "\n");
++
++	return offset;
++}
++
++static int dfs_status_read_wdev(struct wiphy *wiphy, struct wireless_dev *wdev, char *buf,
++				unsigned int buf_size, unsigned int offset)
++{
++	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
++	struct cfg80211_chan_def *chandef;
++	struct cfg80211_chan_def *background_chandef = &rdev->background_radar_chandef;
++	enum nl80211_band band;
++	struct ieee80211_supported_band *sband;
++	struct ieee80211_channel *chan;
++	unsigned long jiffies_passed;
++	unsigned int link_id;
++	int i, remain_time = 0, wait_time_ms = 0;
++	bool is_background;
++
++	for (band = 0; band < NUM_NL80211_BANDS; band++)
++		if (wiphy->bands[band] &&
++		    wiphy->bands[band]->band == NL80211_BAND_5GHZ)
++			sband = wiphy->bands[band];
++
++	if (!sband) {
++		offset += scnprintf(buf + offset, buf_size - offset, "No 5G band\n");
++		return offset;
++	}
++
++	for_each_valid_link(wdev, link_id) {
++		chandef = wdev_chandef(wdev, link_id);
++		if (!chandef || !chandef->chan ||
++		    chandef->chan->band != NL80211_BAND_5GHZ)
++			continue;
++
++		offset += scnprintf(buf + offset, buf_size - offset,
++				    "Link %d DFS channel:\n", link_id);
++		for (i = 0; i < sband->n_channels; i++) {
++			is_background = false;
++			chan = &sband->channels[i];
++
++			if (!(chan->flags & IEEE80211_CHAN_RADAR))
++				continue;
++
++			if (chan->dfs_state == NL80211_DFS_UNAVAILABLE) {
++				jiffies_passed = jiffies - chan->dfs_state_entered;
++				wait_time_ms = IEEE80211_DFS_MIN_NOP_TIME_MS;
++				remain_time = (wait_time_ms - jiffies_to_msecs(jiffies_passed));
++				if (remain_time > wait_time_ms)
++					remain_time = 0;
++			} else if (chan->dfs_state == NL80211_DFS_USABLE) {
++				if (wdev->cac_started &&
++				    cfg80211_is_sub_chan(chandef, chan, false)) {
++					jiffies_passed = jiffies - wdev->cac_start_time;
++					wait_time_ms = wdev->cac_time_ms;
++					remain_time = (wait_time_ms -
++						       jiffies_to_msecs(jiffies_passed));
++				}
++
++				if (rdev->background_radar_wdev == wdev &&
++				    rdev->background_cac_started &&
++				    cfg80211_is_sub_chan(background_chandef, chan, false)) {
++					jiffies_passed = jiffies - rdev->background_cac_start_time;
++					wait_time_ms = rdev->background_cac_time_ms;
++					remain_time = (wait_time_ms -
++						       jiffies_to_msecs(jiffies_passed));
++					is_background = true;
++				}
++
++				if (remain_time > wait_time_ms)
++					remain_time = 0;
++
++			} else {
++				if (rdev->background_radar_wdev == wdev &&
++				    cfg80211_is_sub_chan(background_chandef, chan, false))
++					is_background = true;
++			}
++
++			offset = dfs_print_chan(chan, remain_time / 1000, wait_time_ms / 1000,
++						buf, buf_size, offset, is_background);
++			remain_time = 0;
++		}
++	}
++
++	return offset;
++}
++
++static ssize_t dfs_status_read(struct file *file, char __user *user_buf,
++			       size_t count, loff_t *ppos)
++{
++	struct wiphy *wiphy = file->private_data;
++	struct wireless_dev *wdev;
++	char *buf;
++	unsigned int offset = 0, buf_size = PAGE_SIZE, r;
++	const char * const iftype_str[] = {
++		[NL80211_IFTYPE_UNSPECIFIED] = "unspecified",
++		[NL80211_IFTYPE_ADHOC] = "adhoc",
++		[NL80211_IFTYPE_STATION] = "station",
++		[NL80211_IFTYPE_AP] = "ap",
++		[NL80211_IFTYPE_AP_VLAN] = "ap vlan",
++		[NL80211_IFTYPE_WDS] = "wds",
++		[NL80211_IFTYPE_MONITOR] = "monitor",
++		[NL80211_IFTYPE_MESH_POINT] = "mesh point",
++		[NL80211_IFTYPE_P2P_CLIENT] = "p2p client",
++		[NL80211_IFTYPE_P2P_GO] = "p2p go",
++		[NL80211_IFTYPE_P2P_DEVICE] = "p2p device",
++		[NL80211_IFTYPE_OCB] = "ocb",
++		[NL80211_IFTYPE_NAN] = "nan",
++	};
++
++	buf = kzalloc(buf_size, GFP_KERNEL);
++	if (!buf)
++		return -ENOMEM;
++
++	list_for_each_entry(wdev, &wiphy->wdev_list, list) {
++		offset += scnprintf(buf + offset, buf_size - offset,
++				    "wdev 0x%x\n"
++				    "interface type %s\n",
++				    wdev->identifier, iftype_str[wdev->iftype]);
++		offset = dfs_status_read_wdev(wiphy, wdev, buf, buf_size, offset);
++	}
++
++	r = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
++
++	kfree(buf);
++
++	return r;
++}
++
++static const struct file_operations dfs_status_ops = {
++	.read = dfs_status_read,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static int
++dfs_nop_skip(void *data, u64 val)
++{
++	struct wiphy *wiphy = data;
++	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
++	bool en = !!val;
++	enum nl80211_band band;
++	struct ieee80211_supported_band *sband;
++	struct ieee80211_channel *chan;
++	u32 nop_time = IEEE80211_DFS_MIN_NOP_TIME_MS;
++	int i;
++
++	if (!en)
++		return 0;
++
++	for (band = 0; band < NUM_NL80211_BANDS; band++) {
++		sband = wiphy->bands[band];
++		if (!sband)
++			continue;
++		for (i = 0; i < sband->n_channels; i++) {
++			chan = &sband->channels[i];
++
++			if (!(chan->flags & IEEE80211_CHAN_RADAR))
++				continue;
++
++			if (chan->dfs_state == NL80211_DFS_UNAVAILABLE) {
++				// Let current jiffies > dfs_state_entered_jiffies + NOP time
++				chan->dfs_state_entered = jiffies -
++						       msecs_to_jiffies(nop_time + 1);
++			}
++		}
++	}
++
++	cfg80211_sched_dfs_chan_update(rdev);
++
++	return 0;
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(dfs_skip_nop_ops, NULL,
++			 dfs_nop_skip, "0x%08llx\n");
++
++static int
++dfs_cac_skip(void *data, u64 val)
++{
++#define CAC_SKIP_MASK			BIT(0)
++#define CAC_SKIP_BACKGROUND_MASK	BIT(1)
++	struct wiphy *wiphy = data;
++	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
++	struct wireless_dev *wdev;
++	struct cfg80211_chan_def *c;
++	unsigned int link_id, skip_mode = val;
++	unsigned long cac_time;
++
++	if (!skip_mode || skip_mode > (CAC_SKIP_MASK | CAC_SKIP_BACKGROUND_MASK))
++		return 0;
++
++	list_for_each_entry(wdev, &wiphy->wdev_list, list) {
++		if (skip_mode & CAC_SKIP_MASK) {
++			for_each_valid_link(wdev, link_id) {
++				c = wdev_chandef(wdev, link_id);
++				if (!c || !c->chan ||
++				    c->chan->band != NL80211_BAND_5GHZ)
++					continue;
++
++				if (cfg80211_chandef_dfs_required(wiphy, c, wdev->iftype) > 0 &&
++				    cfg80211_chandef_dfs_usable(wiphy, c) && wdev->cac_started) {
++					rdev_skip_cac(rdev, wdev, link_id);
++				}
++			}
++		}
++
++		if ((skip_mode & CAC_SKIP_BACKGROUND_MASK) &&
++		    rdev->background_radar_wdev == wdev &&
++		    rdev->background_radar_chandef.chan) {
++			c = &rdev->background_radar_chandef;
++
++			if ((cfg80211_chandef_dfs_required(wiphy, c, wdev->iftype) > 0) &&
++			    cfg80211_chandef_dfs_usable(wiphy, c) &&
++			    rdev->background_cac_started) {
++				// Let current jiffies > dfs_state_entered_jiffies + CAC time
++				cac_time = rdev->background_cac_time_ms;
++				rdev->background_cac_start_time = jiffies -
++								  msecs_to_jiffies(cac_time + 1);
++				cancel_delayed_work(&rdev->background_cac_done_wk);
++				queue_delayed_work(cfg80211_wq, &rdev->background_cac_done_wk, 0);
++			}
++		}
++	}
++
++	return 0;
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(dfs_skip_cac_ops, NULL,
++			 dfs_cac_skip, "0x%08llx\n");
++
++static int
++dfs_available_reset(void *data, u64 val)
++{
++	struct wiphy *wiphy = data;
++	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
++	bool en = !!val;
++	enum nl80211_band band;
++	struct ieee80211_supported_band *sband;
++	struct ieee80211_channel *chan;
++	int i;
++
++	if (!en)
++		return 0;
++
++	for (band = 0; band < NUM_NL80211_BANDS; band++) {
++		sband = wiphy->bands[band];
++		if (!sband)
++			continue;
++		for (i = 0; i < sband->n_channels; i++) {
++			chan = &sband->channels[i];
++
++			if (!(chan->flags & IEEE80211_CHAN_RADAR))
++				continue;
++
++			if (chan->dfs_state == NL80211_DFS_AVAILABLE) {
++				chan->dfs_state = NL80211_DFS_USABLE;
++				chan->dfs_state_entered = jiffies;
++			}
++		}
++	}
++
++	cfg80211_sched_dfs_chan_update(rdev);
++
++	return 0;
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(dfs_available_reset_ops, NULL,
++			 dfs_available_reset, "0x%08llx\n");
++
++#define DEBUGFS_ADD(name, chmod)						\
++	debugfs_create_file(#name, chmod, phyd, &rdev->wiphy, &name## _ops)
+ 
+ void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev)
+ {
+ 	struct dentry *phyd = rdev->wiphy.debugfsdir;
+ 
+-	DEBUGFS_ADD(rts_threshold);
+-	DEBUGFS_ADD(fragmentation_threshold);
+-	DEBUGFS_ADD(short_retry_limit);
+-	DEBUGFS_ADD(long_retry_limit);
+-	DEBUGFS_ADD(ht40allow_map);
++	DEBUGFS_ADD(rts_threshold, 0444);
++	DEBUGFS_ADD(fragmentation_threshold, 0444);
++	DEBUGFS_ADD(short_retry_limit, 0444);
++	DEBUGFS_ADD(long_retry_limit, 0444);
++	DEBUGFS_ADD(ht40allow_map, 0444);
++	DEBUGFS_ADD(dfs_status, 0444);
++	DEBUGFS_ADD(dfs_skip_nop, 0600);
++	DEBUGFS_ADD(dfs_skip_cac, 0600);
++	DEBUGFS_ADD(dfs_available_reset, 0600);
+ }
+ 
+ struct debugfs_read_work {
+diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
+index 5c96273..7a92f87 100644
+--- a/net/wireless/mlme.c
++++ b/net/wireless/mlme.c
+@@ -1179,13 +1179,16 @@ __cfg80211_background_cac_event(struct cfg80211_registered_device *rdev,
+ 		queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk);
+ 		cfg80211_sched_dfs_chan_update(rdev);
+ 		wdev = rdev->background_radar_wdev;
++		rdev->background_cac_started = false;
+ 		break;
+ 	case NL80211_RADAR_CAC_ABORTED:
+ 		if (!cancel_delayed_work(&rdev->background_cac_done_wk))
+ 			return;
+ 		wdev = rdev->background_radar_wdev;
++		rdev->background_cac_started = false;
+ 		break;
+ 	case NL80211_RADAR_CAC_STARTED:
++		rdev->background_cac_started = true;
+ 		break;
+ 	default:
+ 		return;
+@@ -1205,6 +1208,7 @@ cfg80211_background_cac_event(struct cfg80211_registered_device *rdev,
+ 					chandef, event);
+ 	wiphy_unlock(&rdev->wiphy);
+ }
++EXPORT_SYMBOL(cfg80211_background_cac_event);
+ 
+ void cfg80211_background_cac_done_wk(struct work_struct *work)
+ {
+@@ -1266,8 +1270,10 @@ cfg80211_start_background_radar_detection(struct cfg80211_registered_device *rde
+ 	if (!cac_time_ms)
+ 		cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+ 
++	rdev->background_cac_time_ms = cac_time_ms;
+ 	rdev->background_radar_chandef = *chandef;
+ 	rdev->background_radar_wdev = wdev; /* Get offchain ownership */
++	rdev->background_cac_start_time = jiffies;
+ 
+ 	__cfg80211_background_cac_event(rdev, wdev, chandef,
+ 					NL80211_RADAR_CAC_STARTED);
+diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
+index 4e8c895..e4a77a8 100644
+--- a/net/wireless/rdev-ops.h
++++ b/net/wireless/rdev-ops.h
+@@ -1545,4 +1545,18 @@ rdev_get_radio_mask(struct cfg80211_registered_device *rdev,
+ 
+ 	return rdev->ops->get_radio_mask(wiphy, dev);
+ }
++
++static inline int
++rdev_skip_cac(struct cfg80211_registered_device *rdev,
++	      struct wireless_dev *wdev, unsigned int link_id)
++{
++	if (!rdev->ops->skip_cac)
++		return -EOPNOTSUPP;
++
++	trace_rdev_skip_cac(wdev, link_id);
++	rdev->ops->skip_cac(wdev, link_id);
++	trace_rdev_return_void(&rdev->wiphy);
++
++	return 0;
++}
+ #endif /* __CFG80211_RDEV_OPS */
+diff --git a/net/wireless/trace.h b/net/wireless/trace.h
+index a831f94..21956c8 100644
+--- a/net/wireless/trace.h
++++ b/net/wireless/trace.h
+@@ -4102,6 +4102,19 @@ TRACE_EVENT(cfg80211_links_removed,
+ 		  __entry->link_mask)
+ );
+ 
++TRACE_EVENT(rdev_skip_cac,
++	TP_PROTO(struct wireless_dev *wdev, unsigned int link_id),
++	TP_ARGS(wdev, link_id),
++	TP_STRUCT__entry(
++		WDEV_ENTRY
++		__field(unsigned int, link_id)
++	),
++	TP_fast_assign(
++		WDEV_ASSIGN;
++		__entry->link_id = link_id;
++	),
++	TP_printk(WDEV_PR_FMT ", link_id: %d", WDEV_PR_ARG, __entry->link_id)
++);
+ #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
+ 
+ #undef TRACE_INCLUDE_PATH
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0020-mtk-nl80211-Mark-DFS-channel-as-available-for-CSA.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0020-mtk-nl80211-Mark-DFS-channel-as-available-for-CSA.patch
deleted file mode 100644
index 1810958..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0020-mtk-nl80211-Mark-DFS-channel-as-available-for-CSA.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From 0be90bf2df2df04a24a376f1aab1078874a7d5cc Mon Sep 17 00:00:00 2001
-From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
-Date: Fri, 17 Mar 2023 17:36:01 +0800
-Subject: [PATCH 20/61] mtk: nl80211: Mark DFS channel as available for CSA.
-
----
- net/wireless/nl80211.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
-diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
-index 54e19b1..7085133 100644
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -10247,6 +10247,11 @@ skip_beacons:
- 	if (err)
- 		goto free;
- 
-+	/* Use RADAR_BACKGROUND attribute here for skipping CAC */
-+	if (info->attrs[NL80211_ATTR_RADAR_BACKGROUND]) {
-+		cfg80211_set_dfs_state(&rdev->wiphy, &params.chandef, NL80211_DFS_AVAILABLE);
-+	}
-+
- 	if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, &params.chandef,
- 					   wdev->iftype)) {
- 		err = -EINVAL;
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0021-mtk-cfg80211-fix-early-return-in-cfg80211_stop_backg.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0021-mtk-cfg80211-fix-early-return-in-cfg80211_stop_backg.patch
deleted file mode 100644
index acb1a8a..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0021-mtk-cfg80211-fix-early-return-in-cfg80211_stop_backg.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From 5f16034ca52f980a612f1fddb4d730480d12decd 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 21/61] mtk: cfg80211: 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 d42b65b..56095b3 100644
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -1292,9 +1292,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.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0021-mtk-mac80211-Set-TWT-Information-Frame-Disabled-bit-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0021-mtk-mac80211-Set-TWT-Information-Frame-Disabled-bit-.patch
new file mode 100644
index 0000000..a86472f
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0021-mtk-mac80211-Set-TWT-Information-Frame-Disabled-bit-.patch
@@ -0,0 +1,26 @@
+From 94b249091fc28f486ee9dcf5434169b10cd31ce7 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Tue, 4 Oct 2022 10:47:05 +0800
+Subject: [PATCH 21/89] mtk: mac80211: Set TWT Information Frame Disabled bit
+ as 1.
+
+This modification means that current implementation do not support twt information frame.
+---
+ net/mac80211/s1g.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c
+index d4ed0c0..27eccbb 100644
+--- a/net/mac80211/s1g.c
++++ b/net/mac80211/s1g.c
+@@ -102,6 +102,7 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata,
+ 	struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
+ 
+ 	twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST);
++	twt->control |= IEEE80211_TWT_CONTROL_RX_DISABLED;
+ 
+ 	/* broadcast TWT not supported yet */
+ 	if (twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0022-mtk-cfg80211-add-background-radar-stop-when-backgrou.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0022-mtk-cfg80211-add-background-radar-stop-when-backgrou.patch
deleted file mode 100644
index c8c5f69..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0022-mtk-cfg80211-add-background-radar-stop-when-backgrou.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From b4960511afc66cff070b19204da6e8cc54cf9630 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 22/61] mtk: cfg80211: 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 7085133..1f1856b 100644
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -10031,6 +10031,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.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0022-mtk-mac80211-check-the-control-channel-before-downgr.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0022-mtk-mac80211-check-the-control-channel-before-downgr.patch
new file mode 100644
index 0000000..305cd11
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0022-mtk-mac80211-check-the-control-channel-before-downgr.patch
@@ -0,0 +1,54 @@
+From 38b24ff76fe1532825f8926057d11b60cb444613 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Fri, 16 Dec 2022 03:31:06 +0800
+Subject: [PATCH 22/89] mtk: mac80211: check the control channel before
+ downgrading the bandwidth
+
+---
+ net/mac80211/mlme.c | 23 +++++++++++++++++++++++
+ 1 file changed, 23 insertions(+)
+
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 75b9976..073e361 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -5417,6 +5417,26 @@ ieee80211_ap_power_type(u8 control)
+ 	}
+ }
+ 
++static bool ieee80211_check_same_ctrl_channel(struct ieee80211_sub_if_data *sdata,
++					      const struct cfg80211_chan_def *chandef)
++{
++	struct ieee80211_local *local = sdata->local;
++	struct ieee80211_chanctx *ctx;
++
++	lockdep_assert_wiphy(local->hw.wiphy);
++
++	list_for_each_entry(ctx, &local->chanctx_list, list) {
++		if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
++			continue;
++		if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
++			continue;
++		if (chandef->chan == ctx->conf.def.chan)
++			return true;
++	}
++
++	return false;
++}
++
+ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
+ 				  struct ieee80211_link_data *link,
+ 				  int link_id,
+@@ -5493,6 +5513,9 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
+ 	    chanreq.oper.width == NL80211_CHAN_WIDTH_10)
+ 		return ret;
+ 
++	if (!ret || !ieee80211_check_same_ctrl_channel(sdata, &chanreq.oper))
++		return ret;
++
+ 	while (ret && chanreq.oper.width != NL80211_CHAN_WIDTH_20_NOHT) {
+ 		ieee80211_chanreq_downgrade(&chanreq, conn);
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0023-mtk-mac80211-avoid-kernel-warning-of-check_flush_dep.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0023-mtk-mac80211-avoid-kernel-warning-of-check_flush_dep.patch
deleted file mode 100644
index 6fdac3c..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0023-mtk-mac80211-avoid-kernel-warning-of-check_flush_dep.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From 9ccc4eaa0168f60bd9f78f4122433ec0528a70cd 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 23/61] mtk: mac80211: 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 81a9645..e9d8581 100644
---- a/net/mac80211/main.c
-+++ b/net/mac80211/main.c
-@@ -1458,7 +1458,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.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0023-mtk-mac80211-fix-tx-amsdu-aggregation.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0023-mtk-mac80211-fix-tx-amsdu-aggregation.patch
new file mode 100644
index 0000000..aff5ba5
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0023-mtk-mac80211-fix-tx-amsdu-aggregation.patch
@@ -0,0 +1,55 @@
+From 19b60e35c6662ae573af038a5fec349366e3269a Mon Sep 17 00:00:00 2001
+From: TomLiu <tomml.liu@mediatek.com>
+Date: Wed, 14 Dec 2022 00:26:50 -0800
+Subject: [PATCH 23/89] mtk: mac80211: fix tx amsdu aggregation
+
+---
+ include/net/mac80211.h | 7 +++++++
+ net/mac80211/agg-tx.c  | 6 ++++--
+ 2 files changed, 11 insertions(+), 2 deletions(-)
+
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 1953f91..859afcf 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -3074,6 +3074,13 @@ static inline void _ieee80211_hw_set(struct ieee80211_hw *hw,
+ }
+ #define ieee80211_hw_set(hw, flg)	_ieee80211_hw_set(hw, IEEE80211_HW_##flg)
+ 
++static inline void _ieee80211_hw_clear(struct ieee80211_hw *hw,
++				     enum ieee80211_hw_flags flg)
++{
++	return __clear_bit(flg, hw->flags);
++}
++#define ieee80211_hw_clear(hw, flg)	_ieee80211_hw_clear(hw, IEEE80211_HW_##flg)
++
+ /**
+  * struct ieee80211_scan_request - hw scan request
+  *
+diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
+index 677bbba..589494f 100644
+--- a/net/mac80211/agg-tx.c
++++ b/net/mac80211/agg-tx.c
+@@ -66,7 +66,8 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
+ 	struct ieee80211_local *local = sdata->local;
+ 	struct sk_buff *skb;
+ 	struct ieee80211_mgmt *mgmt;
+-	u16 capab;
++	u16 capab = 0;
++	bool amsdu = ieee80211_hw_check(&local->hw, SUPPORTS_AMSDU_IN_AMPDU);
+ 
+ 	skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
+ 
+@@ -95,7 +96,8 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
+ 	mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ;
+ 
+ 	mgmt->u.action.u.addba_req.dialog_token = dialog_token;
+-	capab = IEEE80211_ADDBA_PARAM_AMSDU_MASK;
++	if (amsdu)
++		capab = IEEE80211_ADDBA_PARAM_AMSDU_MASK;
+ 	capab |= IEEE80211_ADDBA_PARAM_POLICY_MASK;
+ 	capab |= u16_encode_bits(tid, IEEE80211_ADDBA_PARAM_TID_MASK);
+ 	capab |= u16_encode_bits(agg_size, IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0024-mtk-mac80211-add-fill-receive-path-ops-to-get-wed-id.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0024-mtk-mac80211-add-fill-receive-path-ops-to-get-wed-id.patch
new file mode 100644
index 0000000..aa96627
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0024-mtk-mac80211-add-fill-receive-path-ops-to-get-wed-id.patch
@@ -0,0 +1,124 @@
+From 7e201d350d68f2c0588b8e5f7748e4f733fd8bbc Mon Sep 17 00:00:00 2001
+From: Sujuan Chen <sujuan.chen@mediatek.com>
+Date: Wed, 18 May 2022 15:10:22 +0800
+Subject: [PATCH 24/89] mtk: mac80211: add fill receive path ops to get wed idx
+
+Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
+---
+ include/net/mac80211.h    |  5 +++++
+ net/mac80211/driver-ops.h | 13 +++++++++++++
+ net/mac80211/iface.c      | 23 +++++++++++++++++++++++
+ net/mac80211/util.c       |  9 +++++++++
+ 4 files changed, 50 insertions(+)
+
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 859afcf..5856fc6 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -4420,6 +4420,8 @@ struct ieee80211_prep_tx_info {
+  *	resolve a path for hardware flow offloading
+  * @can_activate_links: Checks if a specific active_links bitmap is
+  *	supported by the driver.
++ * @net_fill_receive_path: Called from .ndo_fill_receive_path in order to
++ *	get a path for hardware flow offloading
+  * @change_vif_links: Change the valid links on an interface, note that while
+  *	removing the old link information is still valid (link_conf pointer),
+  *	but may immediately disappear after the function returns. The old or
+@@ -4808,6 +4810,9 @@ struct ieee80211_ops {
+ 	bool (*can_activate_links)(struct ieee80211_hw *hw,
+ 				   struct ieee80211_vif *vif,
+ 				   u16 active_links);
++	int (*net_fill_receive_path)(struct ieee80211_hw *hw,
++				     struct net_device_path_ctx *ctx,
++				     struct net_device_path *path);
+ 	int (*change_vif_links)(struct ieee80211_hw *hw,
+ 				struct ieee80211_vif *vif,
+ 				u16 old_links, u16 new_links,
+diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
+index 9ffbca2..4cddc3c 100644
+--- a/net/mac80211/driver-ops.h
++++ b/net/mac80211/driver-ops.h
+@@ -1661,6 +1661,19 @@ static inline int drv_net_fill_forward_path(struct ieee80211_local *local,
+ 	return ret;
+ }
+ 
++static inline int drv_net_fill_receive_path(struct ieee80211_local *local,
++					    struct net_device_path_ctx *ctx,
++					    struct net_device_path *path)
++{
++	int ret = -EOPNOTSUPP;
++
++	if (local->ops->net_fill_receive_path)
++		ret = local->ops->net_fill_receive_path(&local->hw,
++							ctx, path);
++
++	return ret;
++}
++
+ static inline int drv_net_setup_tc(struct ieee80211_local *local,
+ 				   struct ieee80211_sub_if_data *sdata,
+ 				   struct net_device *dev,
+diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
+index 71e8b0d..c454826 100644
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -931,6 +931,28 @@ out:
+ 	return ret;
+ }
+ 
++static int ieee80211_netdev_fill_receive_path(struct net_device_path_ctx *ctx,
++					      struct net_device_path *path)
++{
++	struct ieee80211_sub_if_data *sdata;
++	struct ieee80211_local *local;
++	int ret = -ENOENT;
++
++	sdata = IEEE80211_DEV_TO_SUB_IF(ctx->dev);
++	local = sdata->local;
++
++	if (!local->ops->net_fill_receive_path)
++		return -EOPNOTSUPP;
++
++	rcu_read_lock();
++
++	ret = drv_net_fill_receive_path(local, ctx, path);
++
++	rcu_read_unlock();
++
++	return ret;
++}
++
+ static const struct net_device_ops ieee80211_dataif_8023_ops = {
+ 	.ndo_open		= ieee80211_open,
+ 	.ndo_stop		= ieee80211_stop,
+@@ -939,6 +961,7 @@ static const struct net_device_ops ieee80211_dataif_8023_ops = {
+ 	.ndo_set_rx_mode	= ieee80211_set_multicast_list,
+ 	.ndo_set_mac_address	= ieee80211_change_mac,
+ 	.ndo_fill_forward_path	= ieee80211_netdev_fill_forward_path,
++	.ndo_fill_receive_path	= ieee80211_netdev_fill_receive_path,
+ 	.ndo_setup_tc		= ieee80211_netdev_setup_tc,
+ };
+ 
+diff --git a/net/mac80211/util.c b/net/mac80211/util.c
+index ecda005..1877400 100644
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -874,6 +874,15 @@ struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif)
+ }
+ EXPORT_SYMBOL_GPL(ieee80211_vif_to_wdev);
+ 
++struct net_device *ieee80211_vif_to_netdev(struct ieee80211_vif *vif)
++{
++	if (!vif)
++		return NULL;
++
++	return vif_to_sdata(vif)->dev;
++}
++EXPORT_SYMBOL_GPL(ieee80211_vif_to_netdev);
++
+ /*
+  * Nothing should have been stuffed into the workqueue during
+  * the suspend->resume cycle. Since we can't check each caller
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0024-mtk-mac80211-avoid-calling-switch_vif_chanctx-when-u.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0024-mtk-mac80211-avoid-calling-switch_vif_chanctx-when-u.patch
deleted file mode 100644
index 6a68e82..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0024-mtk-mac80211-avoid-calling-switch_vif_chanctx-when-u.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-From d9d2bfeed54c0506a2f387354464d3ef474e1777 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 24/61] mtk: mac80211: 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 32094ef..8043d1d 100644
---- a/net/mac80211/chan.c
-+++ b/net/mac80211/chan.c
-@@ -1219,13 +1219,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, false);
-+	if (!local->emulate_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, false);
- 
--		goto out;
-+			goto out;
-+		}
- 	}
- 
- 	list_move(&link->assigned_chanctx_list, &new_ctx->assigned_links);
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0025-mtk-mac80211-add-EHT-BA1024-support.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0025-mtk-mac80211-add-EHT-BA1024-support.patch
deleted file mode 100644
index c16cfe8..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0025-mtk-mac80211-add-EHT-BA1024-support.patch
+++ /dev/null
@@ -1,116 +0,0 @@
-From 61947ae7c04a86c4d6526d080e37e53fe4d17599 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Sun, 25 Dec 2022 22:43:46 +0800
-Subject: [PATCH 25/61] mtk: mac80211: add EHT BA1024 support
-
----
- include/linux/ieee80211.h |  2 ++
- net/mac80211/agg-tx.c     | 45 +++++++++++++++++++++++++++++++++++++--
- 2 files changed, 45 insertions(+), 2 deletions(-)
-
-diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
-index 95c39b7..70f0135 100644
---- a/include/linux/ieee80211.h
-+++ b/include/linux/ieee80211.h
-@@ -1391,6 +1391,8 @@ struct ieee80211_mgmt {
- 					__le16 status;
- 					__le16 capab;
- 					__le16 timeout;
-+					/* followed by BA Extension */
-+					u8 variable[0];
- 				} __packed addba_resp;
- 				struct{
- 					u8 action_code;
-diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
-index af3d8e6..5cf478e 100644
---- a/net/mac80211/agg-tx.c
-+++ b/net/mac80211/agg-tx.c
-@@ -72,10 +72,17 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
- 	struct ieee80211_local *local = sdata->local;
- 	struct sk_buff *skb;
- 	struct ieee80211_mgmt *mgmt;
-+	struct ieee80211_addba_ext_ie *addba_ext;
-+	u8 *pos;
- 	u16 capab = 0;
- 	bool amsdu = ieee80211_hw_check(&local->hw, SUPPORTS_AMSDU_IN_AMPDU);
- 
--	skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
-+	if (agg_size >= 1024)
-+		skb = dev_alloc_skb(sizeof(*mgmt) +
-+				    2 + sizeof(struct ieee80211_addba_ext_ie) +
-+				    local->hw.extra_tx_headroom);
-+	else
-+		skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
- 
- 	if (!skb)
- 		return;
-@@ -114,6 +121,15 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
- 	mgmt->u.action.u.addba_req.start_seq_num =
- 					cpu_to_le16(start_seq_num << 4);
- 
-+	if (agg_size >= 1024 ) {
-+		pos = skb_put_zero(skb, 2 + sizeof(struct ieee80211_addba_ext_ie));
-+		*pos++ = WLAN_EID_ADDBA_EXT;
-+		*pos++ = sizeof(struct ieee80211_addba_ext_ie);
-+		addba_ext = (struct ieee80211_addba_ext_ie *)pos;
-+		addba_ext->data = u8_encode_bits(agg_size >> IEEE80211_ADDBA_EXT_BUF_SIZE_SHIFT,
-+						 IEEE80211_ADDBA_EXT_BUF_SIZE_MASK);
-+	}
-+
- 	ieee80211_tx_skb_tid(sdata, skb, tid, -1);
- }
- 
-@@ -481,8 +497,11 @@ static void ieee80211_send_addba_with_timeout(struct sta_info *sta,
- 	sta->ampdu_mlme.addba_req_num[tid]++;
- 	spin_unlock_bh(&sta->lock);
- 
--	if (sta->sta.deflink.he_cap.has_he) {
-+	if (sta->sta.deflink.eht_cap.has_eht) {
- 		buf_size = local->hw.max_tx_aggregation_subframes;
-+	} else if (sta->sta.deflink.he_cap.has_he) {
-+		buf_size = min_t(u16, local->hw.max_tx_aggregation_subframes,
-+				 IEEE80211_MAX_AMPDU_BUF_HE);
- 	} else {
- 		/*
- 		 * We really should use what the driver told us it will
-@@ -980,8 +999,10 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
- {
- 	struct tid_ampdu_tx *tid_tx;
- 	struct ieee80211_txq *txq;
-+	struct ieee802_11_elems *elems;
- 	u16 capab, tid, buf_size;
- 	bool amsdu;
-+	int ext_ie_len;
- 
- 	lockdep_assert_wiphy(sta->local->hw.wiphy);
- 
-@@ -989,6 +1010,26 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
- 	amsdu = capab & IEEE80211_ADDBA_PARAM_AMSDU_MASK;
- 	tid = u16_get_bits(capab, IEEE80211_ADDBA_PARAM_TID_MASK);
- 	buf_size = u16_get_bits(capab, IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK);
-+	ext_ie_len = len - offsetof(struct ieee80211_mgmt,
-+				    u.action.u.addba_resp.variable);
-+
-+	if (ext_ie_len < 0)
-+		goto next;
-+
-+	elems = ieee802_11_parse_elems(mgmt->u.action.u.addba_resp.variable,
-+				       ext_ie_len, true, NULL);
-+
-+	if (elems && !elems->parse_error) {
-+		if (sta->sta.deflink.eht_cap.has_eht && elems->addba_ext_ie) {
-+			u8 buf_size_1k = u8_get_bits(elems->addba_ext_ie->data,
-+						     IEEE80211_ADDBA_EXT_BUF_SIZE_MASK);
-+			buf_size |= buf_size_1k << IEEE80211_ADDBA_EXT_BUF_SIZE_SHIFT;
-+		}
-+	}
-+
-+	if (elems)
-+		kfree(elems);
-+next:
- 	buf_size = min(buf_size, local->hw.max_tx_aggregation_subframes);
- 
- 	txq = sta->sta.txq[tid];
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0025-mtk-mac80211-track-obss-color-bitmap.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0025-mtk-mac80211-track-obss-color-bitmap.patch
new file mode 100644
index 0000000..09b75e0
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0025-mtk-mac80211-track-obss-color-bitmap.patch
@@ -0,0 +1,83 @@
+From 6361a2d7187f547a498fee824207c27345ec3d99 Mon Sep 17 00:00:00 2001
+From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Date: Mon, 13 Mar 2023 05:23:37 +0800
+Subject: [PATCH 25/89] mtk: mac80211: track obss color bitmap
+
+Track OBSS BSS color when receive their beacon.
+
+Adding 2 tracepoint for debug, usage:
+echo 1 > /sys/kernel/debug/tracing/events/mac80211/bss_color_bitmap/enable
+echo 1 > /sys/kernel/debug/tracing/events/mac80211/bss_color_collision/enable
+
+---
+ include/net/mac80211.h |  1 +
+ net/mac80211/rx.c      |  6 +++++-
+ net/mac80211/trace.h   | 21 +++++++++++++++++++++
+ 3 files changed, 27 insertions(+), 1 deletion(-)
+
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 5856fc6..6449af9 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -803,6 +803,7 @@ struct ieee80211_bss_conf {
+ 	} he_oper;
+ 	struct ieee80211_he_obss_pd he_obss_pd;
+ 	struct cfg80211_he_bss_color he_bss_color;
++	u64 used_color_bitmap;
+ 	struct ieee80211_fils_discovery fils_discovery;
+ 	u32 unsol_bcast_probe_resp_interval;
+ 	struct cfg80211_bitrate_mask beacon_tx_rate;
+diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
+index 4ca7ae1..5c7fc70 100644
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -3397,9 +3397,13 @@ ieee80211_rx_check_bss_color_collision(struct ieee80211_rx_data *rx)
+ 
+ 		color = le32_get_bits(he_oper->he_oper_params,
+ 				      IEEE80211_HE_OPERATION_BSS_COLOR_MASK);
++
++		bss_conf->used_color_bitmap |= BIT_ULL(color);
++
++		// trace_bss_color_bitmap(color, bss_conf->used_color_bitmap);
+ 		if (color == bss_conf->he_bss_color.color)
+ 			ieee80211_obss_color_collision_notify(&rx->sdata->vif,
+-							      BIT_ULL(color),
++							      bss_conf->used_color_bitmap,
+ 							      bss_conf->link_id);
+ 	}
+ }
+diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
+index 78ffd3b..68f86c3 100644
+--- a/net/mac80211/trace.h
++++ b/net/mac80211/trace.h
+@@ -3154,6 +3154,27 @@ TRACE_EVENT(drv_neg_ttlm_res,
+ 		  LOCAL_PR_ARG, VIF_PR_ARG, __entry->res
+ 	)
+ );
++
++TRACE_EVENT(bss_color_bitmap,
++	TP_PROTO(u8 color,
++		u64 color_bitmap),
++
++	TP_ARGS(color, color_bitmap),
++
++	TP_STRUCT__entry(
++		__field(u8, color)
++		__field(u64, color_bitmap)
++	),
++
++	TP_fast_assign(
++		__entry->color = color;
++		__entry->color_bitmap = color_bitmap;
++	),
++
++	TP_printk(
++		"color=%u color_bitmap=0x%llx", __entry->color, __entry->color_bitmap
++	)
++);
+ #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
+ 
+ #undef TRACE_INCLUDE_PATH
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0026-mtk-mac80211-add-rate-duration-for-EHT-rate.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0026-mtk-mac80211-add-rate-duration-for-EHT-rate.patch
deleted file mode 100644
index 91b1b07..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0026-mtk-mac80211-add-rate-duration-for-EHT-rate.patch
+++ /dev/null
@@ -1,440 +0,0 @@
-From f2f18cce7c4d2467312ccebd927308d65d5cc27b Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Sun, 25 Dec 2022 22:43:46 +0800
-Subject: [PATCH 26/61] mtk: mac80211: add rate duration for EHT rate.
-
----
- net/mac80211/airtime.c | 349 ++++++++++++++++++++++++++++++++++++++++-
- 1 file changed, 346 insertions(+), 3 deletions(-)
-
-diff --git a/net/mac80211/airtime.c b/net/mac80211/airtime.c
-index fdf8b65..370477c 100644
---- a/net/mac80211/airtime.c
-+++ b/net/mac80211/airtime.c
-@@ -55,10 +55,21 @@
- #define HE_DURATION_S(shift, streams, gi, bps)		\
- 	(HE_DURATION(streams, gi, bps) >> shift)
- 
-+/* Transmit duration for the raw data part of an average sized packet */
-+#define EHT_GI_08 HE_GI_08
-+#define EHT_GI_16 HE_GI_16
-+#define EHT_GI_32 HE_GI_32
-+
-+#define EHT_DURATION(streams, gi, bps)			\
-+	HE_DURATION(streams, gi, bps)
-+#define EHT_DURATION_S(shift, streams, gi, bps)		\
-+	HE_DURATION_S(shift, streams, gi, bps)
-+
- #define BW_20			0
- #define BW_40			1
- #define BW_80			2
- #define BW_160			3
-+#define BW_320			4
- 
- /*
-  * Define group sort order: HT40 -> SGI -> #streams
-@@ -68,17 +79,26 @@
- #define IEEE80211_VHT_STREAM_GROUPS	8 /* BW(=4) * SGI(=2) */
- 
- #define IEEE80211_HE_MAX_STREAMS	8
-+#define IEEE80211_HE_STREAM_GROUPS	12 /* BW(=4) * GI(=3) */
-+
-+#define IEEE80211_EHT_MAX_STREAMS	16
-+#define IEEE80211_EHT_STREAM_GROUPS	15 /* BW(=5) * GI(=3) */
- 
- #define IEEE80211_HT_GROUPS_NB	(IEEE80211_MAX_STREAMS *	\
- 				 IEEE80211_HT_STREAM_GROUPS)
- #define IEEE80211_VHT_GROUPS_NB	(IEEE80211_MAX_STREAMS *	\
- 					 IEEE80211_VHT_STREAM_GROUPS)
-+#define IEEE80211_HE_GROUPS_NB	(IEEE80211_HE_MAX_STREAMS *	\
-+				 IEEE80211_HE_STREAM_GROUPS)
-+#define IEEE80211_EHT_GROUPS_NB	(IEEE80211_EHT_MAX_STREAMS *	\
-+				 IEEE80211_EHT_STREAM_GROUPS)
- 
- #define IEEE80211_HT_GROUP_0	0
- #define IEEE80211_VHT_GROUP_0	(IEEE80211_HT_GROUP_0 + IEEE80211_HT_GROUPS_NB)
- #define IEEE80211_HE_GROUP_0	(IEEE80211_VHT_GROUP_0 + IEEE80211_VHT_GROUPS_NB)
-+#define IEEE80211_EHT_GROUP_0	(IEEE80211_HE_GROUP_0 + IEEE80211_HE_GROUPS_NB)
- 
--#define MCS_GROUP_RATES		12
-+#define MCS_GROUP_RATES		14
- 
- #define HT_GROUP_IDX(_streams, _sgi, _ht40)	\
- 	IEEE80211_HT_GROUP_0 +			\
-@@ -203,6 +223,59 @@
- #define HE_GROUP(_streams, _gi, _bw)					\
- 	__HE_GROUP(_streams, _gi, _bw,				\
- 		   HE_GROUP_SHIFT(_streams, _gi, _bw))
-+
-+#define EHT_BW2VBPS(_bw, r5, r4, r3, r2, r1)					\
-+	(_bw == BW_320 ? r5 : _bw == BW_160 ? r4 : _bw == BW_80 ? r3 : _bw == BW_40 ? r2 : r1)
-+
-+#define EHT_GROUP_IDX(_streams, _gi, _bw)				\
-+	(IEEE80211_EHT_GROUP_0 +					\
-+	 IEEE80211_EHT_MAX_STREAMS * 3 * (_bw) +			\
-+	 IEEE80211_EHT_MAX_STREAMS * (_gi) +				\
-+	 (_streams) - 1)
-+
-+#define __EHT_GROUP(_streams, _gi, _bw, _s)				\
-+	[EHT_GROUP_IDX(_streams, _gi, _bw)] = {			\
-+	.shift = _s,							\
-+	.duration = {							\
-+		EHT_DURATION_S(_s, _streams, _gi,			\
-+			      EHT_BW2VBPS(_bw,   1960,   979,  489,  230,  115)),	\
-+		EHT_DURATION_S(_s, _streams, _gi,			\
-+			      EHT_BW2VBPS(_bw,   3920,  1958,  979,  475,  230)),	\
-+		EHT_DURATION_S(_s, _streams, _gi,			\
-+			      EHT_BW2VBPS(_bw,   5880,  2937, 1468,  705,  345)),	\
-+		EHT_DURATION_S(_s, _streams, _gi,			\
-+			      EHT_BW2VBPS(_bw,   7840,  3916, 1958,  936,  475)),	\
-+		EHT_DURATION_S(_s, _streams, _gi,			\
-+			      EHT_BW2VBPS(_bw,  11760,  5875, 2937, 1411,  705)),	\
-+		EHT_DURATION_S(_s, _streams, _gi,			\
-+			      EHT_BW2VBPS(_bw,  15680,  7833, 3916, 1872,  936)),	\
-+		EHT_DURATION_S(_s, _streams, _gi,			\
-+			      EHT_BW2VBPS(_bw,  17640,  8827, 4406, 2102, 1051)),	\
-+		EHT_DURATION_S(_s, _streams, _gi,			\
-+			      EHT_BW2VBPS(_bw,  19600,  9806, 4896, 2347, 1166)),	\
-+		EHT_DURATION_S(_s, _streams, _gi,			\
-+			      EHT_BW2VBPS(_bw,  23520, 11764, 5875, 2808, 1411)),	\
-+		EHT_DURATION_S(_s, _streams, _gi,			\
-+			      EHT_BW2VBPS(_bw,  26133, 13060, 6523, 3124, 1555)),	\
-+		EHT_DURATION_S(_s, _streams, _gi,			\
-+			      EHT_BW2VBPS(_bw,  29400, 14702, 7344, 3513, 1756)),	\
-+		EHT_DURATION_S(_s, _streams, _gi,			\
-+			      EHT_BW2VBPS(_bw,  32666, 16329, 8164, 3902, 1944)),	\
-+		EHT_DURATION_S(_s, _streams, _gi,			\
-+			      EHT_BW2VBPS(_bw,  35280, 17640, 8820, 4212, 2106)),	\
-+		EHT_DURATION_S(_s, _streams, _gi,			\
-+			      EHT_BW2VBPS(_bw,  39200, 19600, 9800, 4680, 2340))	\
-+        }								\
-+}
-+
-+#define EHT_GROUP_SHIFT(_streams, _gi, _bw)				\
-+	GROUP_SHIFT(EHT_DURATION(_streams, _gi,			\
-+				EHT_BW2VBPS(_bw,   1960,   979,  489,  230,  115)))
-+
-+#define EHT_GROUP(_streams, _gi, _bw)					\
-+	__EHT_GROUP(_streams, _gi, _bw,				\
-+		   EHT_GROUP_SHIFT(_streams, _gi, _bw))
-+
- struct mcs_group {
- 	u8 shift;
- 	u16 duration[MCS_GROUP_RATES];
-@@ -376,6 +449,262 @@ static const struct mcs_group airtime_mcs_groups[] = {
- 	HE_GROUP(6, HE_GI_32, BW_160),
- 	HE_GROUP(7, HE_GI_32, BW_160),
- 	HE_GROUP(8, HE_GI_32, BW_160),
-+
-+	/* EHT */
-+	EHT_GROUP( 1, EHT_GI_08, BW_20),
-+	EHT_GROUP( 2, EHT_GI_08, BW_20),
-+	EHT_GROUP( 3, EHT_GI_08, BW_20),
-+	EHT_GROUP( 4, EHT_GI_08, BW_20),
-+	EHT_GROUP( 5, EHT_GI_08, BW_20),
-+	EHT_GROUP( 6, EHT_GI_08, BW_20),
-+	EHT_GROUP( 7, EHT_GI_08, BW_20),
-+	EHT_GROUP( 8, EHT_GI_08, BW_20),
-+	EHT_GROUP( 9, EHT_GI_08, BW_20),
-+	EHT_GROUP(10, EHT_GI_08, BW_20),
-+	EHT_GROUP(11, EHT_GI_08, BW_20),
-+	EHT_GROUP(12, EHT_GI_08, BW_20),
-+	EHT_GROUP(13, EHT_GI_08, BW_20),
-+	EHT_GROUP(14, EHT_GI_08, BW_20),
-+	EHT_GROUP(15, EHT_GI_08, BW_20),
-+	EHT_GROUP(16, EHT_GI_08, BW_20),
-+
-+	EHT_GROUP( 1, EHT_GI_16, BW_20),
-+	EHT_GROUP( 2, EHT_GI_16, BW_20),
-+	EHT_GROUP( 3, EHT_GI_16, BW_20),
-+	EHT_GROUP( 4, EHT_GI_16, BW_20),
-+	EHT_GROUP( 5, EHT_GI_16, BW_20),
-+	EHT_GROUP( 6, EHT_GI_16, BW_20),
-+	EHT_GROUP( 7, EHT_GI_16, BW_20),
-+	EHT_GROUP( 8, EHT_GI_16, BW_20),
-+	EHT_GROUP( 9, EHT_GI_16, BW_20),
-+	EHT_GROUP(10, EHT_GI_16, BW_20),
-+	EHT_GROUP(11, EHT_GI_16, BW_20),
-+	EHT_GROUP(12, EHT_GI_16, BW_20),
-+	EHT_GROUP(13, EHT_GI_16, BW_20),
-+	EHT_GROUP(14, EHT_GI_16, BW_20),
-+	EHT_GROUP(15, EHT_GI_16, BW_20),
-+	EHT_GROUP(16, EHT_GI_16, BW_20),
-+
-+	EHT_GROUP( 1, EHT_GI_32, BW_20),
-+	EHT_GROUP( 2, EHT_GI_32, BW_20),
-+	EHT_GROUP( 3, EHT_GI_32, BW_20),
-+	EHT_GROUP( 4, EHT_GI_32, BW_20),
-+	EHT_GROUP( 5, EHT_GI_32, BW_20),
-+	EHT_GROUP( 6, EHT_GI_32, BW_20),
-+	EHT_GROUP( 7, EHT_GI_32, BW_20),
-+	EHT_GROUP( 8, EHT_GI_32, BW_20),
-+	EHT_GROUP( 9, EHT_GI_32, BW_20),
-+	EHT_GROUP(10, EHT_GI_32, BW_20),
-+	EHT_GROUP(11, EHT_GI_32, BW_20),
-+	EHT_GROUP(12, EHT_GI_32, BW_20),
-+	EHT_GROUP(13, EHT_GI_32, BW_20),
-+	EHT_GROUP(14, EHT_GI_32, BW_20),
-+	EHT_GROUP(15, EHT_GI_32, BW_20),
-+	EHT_GROUP(16, EHT_GI_32, BW_20),
-+
-+	EHT_GROUP( 1, EHT_GI_08, BW_40),
-+	EHT_GROUP( 2, EHT_GI_08, BW_40),
-+	EHT_GROUP( 3, EHT_GI_08, BW_40),
-+	EHT_GROUP( 4, EHT_GI_08, BW_40),
-+	EHT_GROUP( 5, EHT_GI_08, BW_40),
-+	EHT_GROUP( 6, EHT_GI_08, BW_40),
-+	EHT_GROUP( 7, EHT_GI_08, BW_40),
-+	EHT_GROUP( 8, EHT_GI_08, BW_40),
-+	EHT_GROUP( 9, EHT_GI_08, BW_40),
-+	EHT_GROUP(10, EHT_GI_08, BW_40),
-+	EHT_GROUP(11, EHT_GI_08, BW_40),
-+	EHT_GROUP(12, EHT_GI_08, BW_40),
-+	EHT_GROUP(13, EHT_GI_08, BW_40),
-+	EHT_GROUP(14, EHT_GI_08, BW_40),
-+	EHT_GROUP(15, EHT_GI_08, BW_40),
-+	EHT_GROUP(16, EHT_GI_08, BW_40),
-+
-+	EHT_GROUP( 1, EHT_GI_16, BW_40),
-+	EHT_GROUP( 2, EHT_GI_16, BW_40),
-+	EHT_GROUP( 3, EHT_GI_16, BW_40),
-+	EHT_GROUP( 4, EHT_GI_16, BW_40),
-+	EHT_GROUP( 5, EHT_GI_16, BW_40),
-+	EHT_GROUP( 6, EHT_GI_16, BW_40),
-+	EHT_GROUP( 7, EHT_GI_16, BW_40),
-+	EHT_GROUP( 8, EHT_GI_16, BW_40),
-+	EHT_GROUP( 9, EHT_GI_16, BW_40),
-+	EHT_GROUP(10, EHT_GI_16, BW_40),
-+	EHT_GROUP(11, EHT_GI_16, BW_40),
-+	EHT_GROUP(12, EHT_GI_16, BW_40),
-+	EHT_GROUP(13, EHT_GI_16, BW_40),
-+	EHT_GROUP(14, EHT_GI_16, BW_40),
-+	EHT_GROUP(15, EHT_GI_16, BW_40),
-+	EHT_GROUP(16, EHT_GI_16, BW_40),
-+
-+	EHT_GROUP( 1, EHT_GI_32, BW_40),
-+	EHT_GROUP( 2, EHT_GI_32, BW_40),
-+	EHT_GROUP( 3, EHT_GI_32, BW_40),
-+	EHT_GROUP( 4, EHT_GI_32, BW_40),
-+	EHT_GROUP( 5, EHT_GI_32, BW_40),
-+	EHT_GROUP( 6, EHT_GI_32, BW_40),
-+	EHT_GROUP( 7, EHT_GI_32, BW_40),
-+	EHT_GROUP( 8, EHT_GI_32, BW_40),
-+	EHT_GROUP( 9, EHT_GI_32, BW_40),
-+	EHT_GROUP(10, EHT_GI_32, BW_40),
-+	EHT_GROUP(11, EHT_GI_32, BW_40),
-+	EHT_GROUP(12, EHT_GI_32, BW_40),
-+	EHT_GROUP(13, EHT_GI_32, BW_40),
-+	EHT_GROUP(14, EHT_GI_32, BW_40),
-+	EHT_GROUP(15, EHT_GI_32, BW_40),
-+	EHT_GROUP(16, EHT_GI_32, BW_40),
-+
-+	EHT_GROUP( 1, EHT_GI_08, BW_80),
-+	EHT_GROUP( 2, EHT_GI_08, BW_80),
-+	EHT_GROUP( 3, EHT_GI_08, BW_80),
-+	EHT_GROUP( 4, EHT_GI_08, BW_80),
-+	EHT_GROUP( 5, EHT_GI_08, BW_80),
-+	EHT_GROUP( 6, EHT_GI_08, BW_80),
-+	EHT_GROUP( 7, EHT_GI_08, BW_80),
-+	EHT_GROUP( 8, EHT_GI_08, BW_80),
-+	EHT_GROUP( 9, EHT_GI_08, BW_80),
-+	EHT_GROUP(10, EHT_GI_08, BW_80),
-+	EHT_GROUP(11, EHT_GI_08, BW_80),
-+	EHT_GROUP(12, EHT_GI_08, BW_80),
-+	EHT_GROUP(13, EHT_GI_08, BW_80),
-+	EHT_GROUP(14, EHT_GI_08, BW_80),
-+	EHT_GROUP(15, EHT_GI_08, BW_80),
-+	EHT_GROUP(16, EHT_GI_08, BW_80),
-+
-+	EHT_GROUP( 1, EHT_GI_16, BW_80),
-+	EHT_GROUP( 2, EHT_GI_16, BW_80),
-+	EHT_GROUP( 3, EHT_GI_16, BW_80),
-+	EHT_GROUP( 4, EHT_GI_16, BW_80),
-+	EHT_GROUP( 5, EHT_GI_16, BW_80),
-+	EHT_GROUP( 6, EHT_GI_16, BW_80),
-+	EHT_GROUP( 7, EHT_GI_16, BW_80),
-+	EHT_GROUP( 8, EHT_GI_16, BW_80),
-+	EHT_GROUP( 9, EHT_GI_16, BW_80),
-+	EHT_GROUP(10, EHT_GI_16, BW_80),
-+	EHT_GROUP(11, EHT_GI_16, BW_80),
-+	EHT_GROUP(12, EHT_GI_16, BW_80),
-+	EHT_GROUP(13, EHT_GI_16, BW_80),
-+	EHT_GROUP(14, EHT_GI_16, BW_80),
-+	EHT_GROUP(15, EHT_GI_16, BW_80),
-+	EHT_GROUP(16, EHT_GI_16, BW_80),
-+
-+	EHT_GROUP( 1, EHT_GI_32, BW_80),
-+	EHT_GROUP( 2, EHT_GI_32, BW_80),
-+	EHT_GROUP( 3, EHT_GI_32, BW_80),
-+	EHT_GROUP( 4, EHT_GI_32, BW_80),
-+	EHT_GROUP( 5, EHT_GI_32, BW_80),
-+	EHT_GROUP( 6, EHT_GI_32, BW_80),
-+	EHT_GROUP( 7, EHT_GI_32, BW_80),
-+	EHT_GROUP( 8, EHT_GI_32, BW_80),
-+	EHT_GROUP( 9, EHT_GI_32, BW_80),
-+	EHT_GROUP(10, EHT_GI_32, BW_80),
-+	EHT_GROUP(11, EHT_GI_32, BW_80),
-+	EHT_GROUP(12, EHT_GI_32, BW_80),
-+	EHT_GROUP(13, EHT_GI_32, BW_80),
-+	EHT_GROUP(14, EHT_GI_32, BW_80),
-+	EHT_GROUP(15, EHT_GI_32, BW_80),
-+	EHT_GROUP(16, EHT_GI_32, BW_80),
-+
-+	EHT_GROUP( 1, EHT_GI_08, BW_160),
-+	EHT_GROUP( 2, EHT_GI_08, BW_160),
-+	EHT_GROUP( 3, EHT_GI_08, BW_160),
-+	EHT_GROUP( 4, EHT_GI_08, BW_160),
-+	EHT_GROUP( 5, EHT_GI_08, BW_160),
-+	EHT_GROUP( 6, EHT_GI_08, BW_160),
-+	EHT_GROUP( 7, EHT_GI_08, BW_160),
-+	EHT_GROUP( 8, EHT_GI_08, BW_160),
-+	EHT_GROUP( 9, EHT_GI_08, BW_160),
-+	EHT_GROUP(10, EHT_GI_08, BW_160),
-+	EHT_GROUP(11, EHT_GI_08, BW_160),
-+	EHT_GROUP(12, EHT_GI_08, BW_160),
-+	EHT_GROUP(13, EHT_GI_08, BW_160),
-+	EHT_GROUP(14, EHT_GI_08, BW_160),
-+	EHT_GROUP(15, EHT_GI_08, BW_160),
-+	EHT_GROUP(16, EHT_GI_08, BW_160),
-+
-+	EHT_GROUP( 1, EHT_GI_16, BW_160),
-+	EHT_GROUP( 2, EHT_GI_16, BW_160),
-+	EHT_GROUP( 3, EHT_GI_16, BW_160),
-+	EHT_GROUP( 4, EHT_GI_16, BW_160),
-+	EHT_GROUP( 5, EHT_GI_16, BW_160),
-+	EHT_GROUP( 6, EHT_GI_16, BW_160),
-+	EHT_GROUP( 7, EHT_GI_16, BW_160),
-+	EHT_GROUP( 8, EHT_GI_16, BW_160),
-+	EHT_GROUP( 9, EHT_GI_16, BW_160),
-+	EHT_GROUP(10, EHT_GI_16, BW_160),
-+	EHT_GROUP(11, EHT_GI_16, BW_160),
-+	EHT_GROUP(12, EHT_GI_16, BW_160),
-+	EHT_GROUP(13, EHT_GI_16, BW_160),
-+	EHT_GROUP(14, EHT_GI_16, BW_160),
-+	EHT_GROUP(15, EHT_GI_16, BW_160),
-+	EHT_GROUP(16, EHT_GI_16, BW_160),
-+
-+	EHT_GROUP( 1, EHT_GI_32, BW_160),
-+	EHT_GROUP( 2, EHT_GI_32, BW_160),
-+	EHT_GROUP( 3, EHT_GI_32, BW_160),
-+	EHT_GROUP( 4, EHT_GI_32, BW_160),
-+	EHT_GROUP( 5, EHT_GI_32, BW_160),
-+	EHT_GROUP( 6, EHT_GI_32, BW_160),
-+	EHT_GROUP( 7, EHT_GI_32, BW_160),
-+	EHT_GROUP( 8, EHT_GI_32, BW_160),
-+	EHT_GROUP( 9, EHT_GI_32, BW_160),
-+	EHT_GROUP(10, EHT_GI_32, BW_160),
-+	EHT_GROUP(11, EHT_GI_32, BW_160),
-+	EHT_GROUP(12, EHT_GI_32, BW_160),
-+	EHT_GROUP(13, EHT_GI_32, BW_160),
-+	EHT_GROUP(14, EHT_GI_32, BW_160),
-+	EHT_GROUP(15, EHT_GI_32, BW_160),
-+	EHT_GROUP(16, EHT_GI_32, BW_160),
-+
-+	EHT_GROUP( 1, EHT_GI_08, BW_320),
-+	EHT_GROUP( 2, EHT_GI_08, BW_320),
-+	EHT_GROUP( 3, EHT_GI_08, BW_320),
-+	EHT_GROUP( 4, EHT_GI_08, BW_320),
-+	EHT_GROUP( 5, EHT_GI_08, BW_320),
-+	EHT_GROUP( 6, EHT_GI_08, BW_320),
-+	EHT_GROUP( 7, EHT_GI_08, BW_320),
-+	EHT_GROUP( 8, EHT_GI_08, BW_320),
-+	EHT_GROUP( 9, EHT_GI_08, BW_320),
-+	EHT_GROUP(10, EHT_GI_08, BW_320),
-+	EHT_GROUP(11, EHT_GI_08, BW_320),
-+	EHT_GROUP(12, EHT_GI_08, BW_320),
-+	EHT_GROUP(13, EHT_GI_08, BW_320),
-+	EHT_GROUP(14, EHT_GI_08, BW_320),
-+	EHT_GROUP(15, EHT_GI_08, BW_320),
-+	EHT_GROUP(16, EHT_GI_08, BW_320),
-+
-+	EHT_GROUP( 1, EHT_GI_16, BW_320),
-+	EHT_GROUP( 2, EHT_GI_16, BW_320),
-+	EHT_GROUP( 3, EHT_GI_16, BW_320),
-+	EHT_GROUP( 4, EHT_GI_16, BW_320),
-+	EHT_GROUP( 5, EHT_GI_16, BW_320),
-+	EHT_GROUP( 6, EHT_GI_16, BW_320),
-+	EHT_GROUP( 7, EHT_GI_16, BW_320),
-+	EHT_GROUP( 8, EHT_GI_16, BW_320),
-+	EHT_GROUP( 9, EHT_GI_16, BW_320),
-+	EHT_GROUP(10, EHT_GI_16, BW_320),
-+	EHT_GROUP(11, EHT_GI_16, BW_320),
-+	EHT_GROUP(12, EHT_GI_16, BW_320),
-+	EHT_GROUP(13, EHT_GI_16, BW_320),
-+	EHT_GROUP(14, EHT_GI_16, BW_320),
-+	EHT_GROUP(15, EHT_GI_16, BW_320),
-+	EHT_GROUP(16, EHT_GI_16, BW_320),
-+
-+	EHT_GROUP( 1, EHT_GI_32, BW_320),
-+	EHT_GROUP( 2, EHT_GI_32, BW_320),
-+	EHT_GROUP( 3, EHT_GI_32, BW_320),
-+	EHT_GROUP( 4, EHT_GI_32, BW_320),
-+	EHT_GROUP( 5, EHT_GI_32, BW_320),
-+	EHT_GROUP( 6, EHT_GI_32, BW_320),
-+	EHT_GROUP( 7, EHT_GI_32, BW_320),
-+	EHT_GROUP( 8, EHT_GI_32, BW_320),
-+	EHT_GROUP( 9, EHT_GI_32, BW_320),
-+	EHT_GROUP(10, EHT_GI_32, BW_320),
-+	EHT_GROUP(11, EHT_GI_32, BW_320),
-+	EHT_GROUP(12, EHT_GI_32, BW_320),
-+	EHT_GROUP(13, EHT_GI_32, BW_320),
-+	EHT_GROUP(14, EHT_GI_32, BW_320),
-+	EHT_GROUP(15, EHT_GI_32, BW_320),
-+	EHT_GROUP(16, EHT_GI_32, BW_320),
- };
- 
- static u32
-@@ -422,6 +751,9 @@ static u32 ieee80211_get_rate_duration(struct ieee80211_hw *hw,
- 	case RATE_INFO_BW_160:
- 		bw = BW_160;
- 		break;
-+	case RATE_INFO_BW_320:
-+		bw = BW_320;
-+		break;
- 	default:
- 		WARN_ON_ONCE(1);
- 		return 0;
-@@ -443,11 +775,20 @@ static u32 ieee80211_get_rate_duration(struct ieee80211_hw *hw,
- 		idx = status->rate_idx;
- 		group = HE_GROUP_IDX(streams, status->he_gi, bw);
- 		break;
-+	case RX_ENC_EHT:
-+		streams = status->nss;
-+		idx = status->rate_idx;
-+		group = EHT_GROUP_IDX(streams, status->he_gi, bw);
-+		break;
- 	default:
- 		WARN_ON_ONCE(1);
- 		return 0;
- 	}
- 
-+	if (WARN_ON_ONCE((status->encoding != RX_ENC_EHT && streams > 8) ||
-+			 (status->encoding == RX_ENC_EHT && streams > 16)))
-+		return 0;
-+
- 	if (WARN_ON_ONCE((status->encoding != RX_ENC_HE && streams > 4) ||
- 			 (status->encoding == RX_ENC_HE && streams > 8)))
- 		return 0;
-@@ -517,7 +858,9 @@ static bool ieee80211_fill_rate_info(struct ieee80211_hw *hw,
- 	stat->nss = ri->nss;
- 	stat->rate_idx = ri->mcs;
- 
--	if (ri->flags & RATE_INFO_FLAGS_HE_MCS)
-+	if (ri->flags & RATE_INFO_FLAGS_EHT_MCS)
-+		stat->encoding = RX_ENC_EHT;
-+	else if (ri->flags & RATE_INFO_FLAGS_HE_MCS)
- 		stat->encoding = RX_ENC_HE;
- 	else if (ri->flags & RATE_INFO_FLAGS_VHT_MCS)
- 		stat->encoding = RX_ENC_VHT;
-@@ -529,7 +872,7 @@ static bool ieee80211_fill_rate_info(struct ieee80211_hw *hw,
- 	if (ri->flags & RATE_INFO_FLAGS_SHORT_GI)
- 		stat->enc_flags |= RX_ENC_FLAG_SHORT_GI;
- 
--	stat->he_gi = ri->he_gi;
-+	stat->he_gi = (ri->flags & RATE_INFO_FLAGS_EHT_MCS) ? ri->eht_gi : ri->he_gi;
- 
- 	if (stat->encoding != RX_ENC_LEGACY)
- 		return true;
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0026-mtk-mac80211-update-max_bssid_indicator-based-on-rea.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0026-mtk-mac80211-update-max_bssid_indicator-based-on-rea.patch
new file mode 100644
index 0000000..f65257f
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0026-mtk-mac80211-update-max_bssid_indicator-based-on-rea.patch
@@ -0,0 +1,41 @@
+From 191969896637f094e9da64eccd8d110864891252 Mon Sep 17 00:00:00 2001
+From: "Allen.Ye" <allen.ye@mediatek.com>
+Date: Fri, 14 Apr 2023 05:05:17 +0800
+Subject: [PATCH 26/89] mtk: mac80211: update max_bssid_indicator based on real
+ BSS numbers
+
+Fix max_bssid_indicator get empty value due to wrong pointer.
+
+---
+ net/mac80211/cfg.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 42b6d4a..e119373 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1164,9 +1164,11 @@ ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+ 	/* copy in optional mbssid_ies */
+ 	if (mbssid) {
+ 		u8 *pos = new->tail + new->tail_len;
++		u8 *bssid_indicator;
+ 
+ 		new->mbssid_ies = (void *)pos;
+ 		pos += struct_size(new->mbssid_ies, elem, mbssid->cnt);
++		bssid_indicator = pos + 2;
+ 		pos += ieee80211_copy_mbssid_beacon(pos, new->mbssid_ies,
+ 						    mbssid);
+ 		if (rnr) {
+@@ -1175,8 +1177,7 @@ ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+ 			ieee80211_copy_rnr_beacon(pos, new->rnr_ies, rnr);
+ 		}
+ 		/* update bssid_indicator */
+-		link_conf->bssid_indicator =
+-			ilog2(__roundup_pow_of_two(mbssid->cnt + 1));
++		sdata->vif.bss_conf.bssid_indicator = *(bssid_indicator);
+ 	}
+ 
+ 	if (csa) {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0027-mtk-mac80211-add-send-bar-action-when-recieve-addba-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0027-mtk-mac80211-add-send-bar-action-when-recieve-addba-.patch
deleted file mode 100644
index 8de0ea7..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0027-mtk-mac80211-add-send-bar-action-when-recieve-addba-.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From baa27bc8d70bab050630a127c7d58d140ac9ed21 Mon Sep 17 00:00:00 2001
-From: ye he <ye.he@mediatek.com>
-Date: Wed, 22 Feb 2023 16:09:32 +0800
-Subject: [PATCH 27/61] mtk: mac80211: add send bar action when recieve addba
- rsp
-
-Signed-off-by: ye he <ye.he@mediatek.com>
----
- net/mac80211/agg-tx.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
-index 5cf478e..8480b64 100644
---- a/net/mac80211/agg-tx.c
-+++ b/net/mac80211/agg-tx.c
-@@ -1080,7 +1080,8 @@ next:
- 
- 		tid_tx->buf_size = buf_size;
- 		tid_tx->amsdu = amsdu;
--
-+		ieee80211_send_bar(&sta->sdata->vif, sta->sta.addr,
-+					   tid, 0);
- 		if (test_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state))
- 			ieee80211_agg_tx_operational(local, sta, tid);
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0027-mtk-mac80211-support-configurable-addba-resp-time.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0027-mtk-mac80211-support-configurable-addba-resp-time.patch
new file mode 100644
index 0000000..b44933d
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0027-mtk-mac80211-support-configurable-addba-resp-time.patch
@@ -0,0 +1,42 @@
+From 274b34c23a8f35e3d8260fe88badb10af80be3d4 Mon Sep 17 00:00:00 2001
+From: Lian Chen <lian.chen@mediatek.com>
+Date: Wed, 7 Jun 2023 15:30:34 +0800
+Subject: [PATCH 27/89] mtk: mac80211: support configurable addba resp time.
+
+---
+ net/mac80211/agg-tx.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
+index 589494f..9c640ca 100644
+--- a/net/mac80211/agg-tx.c
++++ b/net/mac80211/agg-tx.c
+@@ -16,10 +16,16 @@
+ #include <linux/slab.h>
+ #include <linux/export.h>
+ #include <net/mac80211.h>
++#include <linux/moduleparam.h>
+ #include "ieee80211_i.h"
+ #include "driver-ops.h"
+ #include "wme.h"
+ 
++static int addba_resp_wait_count = 2;
++module_param(addba_resp_wait_count, int, 0644);
++MODULE_PARM_DESC(addba_resp_wait_count,
++		 "Number of ADDBA_RESP_INTERVAL to wait for addba response");
++
+ /**
+  * DOC: TX A-MPDU aggregation
+  *
+@@ -466,7 +472,7 @@ static void ieee80211_send_addba_with_timeout(struct sta_info *sta,
+ 	lockdep_assert_wiphy(sta->local->hw.wiphy);
+ 
+ 	/* activate the timer for the recipient's addBA response */
+-	mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL);
++	mod_timer(&tid_tx->addba_resp_timer, jiffies + addba_resp_wait_count * ADDBA_RESP_INTERVAL);
+ 	ht_dbg(sdata, "activated addBA response timer on %pM tid %d\n",
+ 	       sta->sta.addr, tid);
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0028-mtk-mac80211-add-sta-assisted-DFS-state-update-mecha.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0028-mtk-mac80211-add-sta-assisted-DFS-state-update-mecha.patch
new file mode 100644
index 0000000..669ddd0
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0028-mtk-mac80211-add-sta-assisted-DFS-state-update-mecha.patch
@@ -0,0 +1,201 @@
+From 5c4cda67753544ea5bb793d6d36fbbb7330ca0c8 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 14:25:24 +0800
+Subject: [PATCH 28/89] mtk: mac80211: add sta-assisted DFS state update
+ mechanism
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+Add cfg80211_any_wiphy_oper_chan check before clearing dfs state.
+This avoids STA clearing the dfs state of the channel, which still has
+APs/offchain operating on the it.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ include/net/cfg80211.h       | 14 +++++++
+ include/uapi/linux/nl80211.h |  6 +++
+ net/mac80211/mlme.c          | 14 +++++++
+ net/wireless/chan.c          | 72 ++++++++++++++++++++++++++++++++++++
+ 4 files changed, 106 insertions(+)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index cc1ed48..16a9a24 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -8775,6 +8775,20 @@ void cfg80211_cac_event(struct net_device *netdev,
+ 			enum nl80211_radar_event event, gfp_t gfp,
+ 			unsigned int link_id);
+ 
++/**
++ * cfg80211_sta_update_dfs_state - Update channel's DFS state during STA channel switch,
++ *				   association, and disassociation
++ * @wdev: the wireless device
++ * @bss_chandef: the current BSS channel definition
++ * @csa_chandef: the CSA channel definition
++ * @associated: whether STA is during association or disassociation process
++ *
++ */
++void cfg80211_sta_update_dfs_state(struct wireless_dev *wdev,
++				   const struct cfg80211_chan_def *bss_chandef,
++				   const struct cfg80211_chan_def *csa_chandef,
++				   bool associated);
++
+ /**
+  * cfg80211_background_cac_abort - Channel Availability Check offchan abort event
+  * @wiphy: the wiphy
+diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
+index f97f5ad..622fa71 100644
+--- a/include/uapi/linux/nl80211.h
++++ b/include/uapi/linux/nl80211.h
+@@ -6843,6 +6843,10 @@ enum nl80211_smps_mode {
+  *	applicable for ETSI dfs domain where pre-CAC is valid for ever.
+  * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
+  *	should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
++ * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
++ *	when receiving CSA/assoc resp
++ * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
++ *	when STA is disconnected or leaving the channel
+  */
+ enum nl80211_radar_event {
+ 	NL80211_RADAR_DETECTED,
+@@ -6851,6 +6855,8 @@ enum nl80211_radar_event {
+ 	NL80211_RADAR_NOP_FINISHED,
+ 	NL80211_RADAR_PRE_CAC_EXPIRED,
+ 	NL80211_RADAR_CAC_STARTED,
++	NL80211_RADAR_STA_CAC_SKIPPED,
++	NL80211_RADAR_STA_CAC_EXPIRED,
+ };
+ 
+ /**
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 073e361..bf8a532 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -2599,6 +2599,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link,
+ 	if (csa_ie.mode)
+ 		ieee80211_vif_block_queues_csa(sdata);
+ 
++	cfg80211_sta_update_dfs_state(&sdata->wdev,
++				      &link->conf->chanreq.oper,
++				      &link->csa_chanreq.oper,
++				      sdata->vif.cfg.assoc);
++
+ 	cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chanreq.oper,
+ 					  link->link_id, csa_ie.count,
+ 					  csa_ie.mode);
+@@ -3665,6 +3670,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
+ 		link = sdata_dereference(sdata->link[link_id], sdata);
+ 		if (!link)
+ 			continue;
++
++		cfg80211_sta_update_dfs_state(&sdata->wdev,
++					      &link->conf->chanreq.oper,
++					      NULL, sdata->vif.cfg.assoc);
+ 		ieee80211_link_release_channel(link);
+ 	}
+ 
+@@ -5932,6 +5941,11 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
+ 		for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+ 			if (link->tx_conf[ac].uapsd)
+ 				resp.uapsd_queues |= ieee80211_ac_to_qos_mask[ac];
++
++		if (status_code == WLAN_STATUS_SUCCESS)
++			cfg80211_sta_update_dfs_state(&sdata->wdev,
++						      &link->conf->chanreq.oper,
++						      NULL, sdata->vif.cfg.assoc);
+ 	}
+ 
+ 	if (ieee80211_vif_is_mld(&sdata->vif)) {
+diff --git a/net/wireless/chan.c b/net/wireless/chan.c
+index be2261f..7b511d3 100644
+--- a/net/wireless/chan.c
++++ b/net/wireless/chan.c
+@@ -14,6 +14,7 @@
+ #include <net/cfg80211.h>
+ #include "core.h"
+ #include "rdev-ops.h"
++#include "nl80211.h"
+ 
+ static bool cfg80211_valid_60g_freq(u32 freq)
+ {
+@@ -1721,6 +1722,77 @@ bool cfg80211_any_usable_channels(struct wiphy *wiphy,
+ }
+ EXPORT_SYMBOL(cfg80211_any_usable_channels);
+ 
++static void cfg80211_sta_radar_notify(struct wiphy *wiphy,
++				      const struct cfg80211_chan_def *chandef,
++				      enum nl80211_radar_event event)
++{
++	struct wireless_dev *wdev;
++
++	list_for_each_entry(wdev, &wiphy->wdev_list, list) {
++		if (cfg80211_chandef_dfs_required(wiphy, chandef, wdev->iftype) > 0) {
++			nl80211_radar_notify(wiphy_to_rdev(wiphy), chandef,
++					     event, wdev->netdev, GFP_KERNEL);
++			return;
++		}
++	}
++}
++
++void cfg80211_sta_update_dfs_state(struct wireless_dev *wdev,
++				   const struct cfg80211_chan_def *bss_chandef,
++				   const struct cfg80211_chan_def *csa_chandef,
++				   bool associated)
++{
++	bool csa_active = !!csa_chandef;
++	enum nl80211_dfs_state dfs_state = NL80211_DFS_USABLE;
++	enum nl80211_radar_event event = NL80211_RADAR_STA_CAC_EXPIRED;
++
++	lockdep_assert_wiphy(wdev->wiphy);
++
++	if (!bss_chandef || !bss_chandef->chan ||
++	    bss_chandef->chan->band != NL80211_BAND_5GHZ)
++		return;
++
++	/* assume csa channel is cac completed */
++	if (csa_active &&
++	    (cfg80211_chandef_dfs_usable(wdev->wiphy, csa_chandef) ||
++	    cfg80211_chandef_dfs_available(wdev->wiphy, csa_chandef))) {
++		cfg80211_set_dfs_state(wdev->wiphy, csa_chandef, NL80211_DFS_AVAILABLE);
++		cfg80211_sta_radar_notify(wdev->wiphy, csa_chandef,
++					  NL80211_RADAR_STA_CAC_SKIPPED);
++		netdev_info(wdev->netdev, "Set CSA channel's DFS state to available\n");
++	}
++
++	/* avoid updating the dfs state during nop */
++	if (!cfg80211_chandef_dfs_usable(wdev->wiphy, bss_chandef) &&
++	    !cfg80211_chandef_dfs_available(wdev->wiphy, bss_chandef))
++		return;
++
++	if (associated && !csa_active) {
++		dfs_state = NL80211_DFS_AVAILABLE;
++		event = NL80211_RADAR_STA_CAC_SKIPPED;
++	}
++
++	/* avoid setting the dfs state to usable
++	 * when other interfaces still operate on this channel
++	 */
++	if (dfs_state == NL80211_DFS_USABLE &&
++	    (cfg80211_is_wiphy_oper_chan(wdev->wiphy, bss_chandef->chan) ||
++	     cfg80211_offchan_chain_is_active(wiphy_to_rdev(wdev->wiphy),
++					      bss_chandef->chan)))
++		return;
++
++	cfg80211_set_dfs_state(wdev->wiphy, bss_chandef, dfs_state);
++	cfg80211_sta_radar_notify(wdev->wiphy, bss_chandef, event);
++
++	if (csa_active)
++		netdev_info(wdev->netdev, "Set origin channel's DFS state to usable\n");
++	else
++		netdev_info(wdev->netdev, "Set BSS channel's DFS state to %s due to %s\n",
++			    (dfs_state == NL80211_DFS_USABLE) ? "usable" : "available",
++			    associated ? "association" : "disassociation");
++}
++EXPORT_SYMBOL(cfg80211_sta_update_dfs_state);
++
+ struct cfg80211_chan_def *wdev_chandef(struct wireless_dev *wdev,
+ 				       unsigned int link_id)
+ {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0028-mtk-mac80211-inrease-beacon-loss-count.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0028-mtk-mac80211-inrease-beacon-loss-count.patch
deleted file mode 100644
index c6a9e9d..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0028-mtk-mac80211-inrease-beacon-loss-count.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From 235df226520ae9c161bed6fe45920331662b26e6 Mon Sep 17 00:00:00 2001
-From: Amit Khatri <amit.khatri@mediatek.com>
-Date: Thu, 6 Apr 2023 21:37:33 +0800
-Subject: [PATCH 28/61] mtk: mac80211: inrease beacon loss count
-
-as per eagle code beacone loss time out is
-4 seconds.
-in 2G connection getting beacon loss logs in routed client
-scenario.
-
-so increasing beacon loss count from 7 to 20
-
-Signed-off-by: Amit Khatri <amit.khatri@mediatek.com>
----
- net/mac80211/mlme.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
-index 52d1cd8..a27682e 100644
---- a/net/mac80211/mlme.c
-+++ b/net/mac80211/mlme.c
-@@ -66,7 +66,7 @@ MODULE_PARM_DESC(max_probe_tries,
-  * probe on beacon miss before declaring the connection lost
-  * default to what we want.
-  */
--static int beacon_loss_count = 7;
-+static int beacon_loss_count = 20;
- module_param(beacon_loss_count, int, 0644);
- MODULE_PARM_DESC(beacon_loss_count,
- 		 "Number of beacon intervals before we decide beacon was lost.");
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0029-mtk-cfg80211-add-support-for-updating-background-cha.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0029-mtk-cfg80211-add-support-for-updating-background-cha.patch
deleted file mode 100644
index ffc8f1c..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0029-mtk-cfg80211-add-support-for-updating-background-cha.patch
+++ /dev/null
@@ -1,88 +0,0 @@
-From e4e9d4cdfc3b5e27a3ad3f05810a96447ccf1f56 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 5 Jul 2023 09:49:02 +0800
-Subject: [PATCH 29/61] mtk: cfg80211: add support for updating background
- channel
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- include/net/cfg80211.h       | 14 ++++++++++++++
- include/uapi/linux/nl80211.h |  6 ++++++
- net/wireless/mlme.c          | 12 ++++++++++++
- 3 files changed, 32 insertions(+)
-
-diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
-index 19cf7f7..bd516d1 100644
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -8693,6 +8693,20 @@ void cfg80211_sta_update_dfs_state(struct wireless_dev *wdev,
- 				   const struct cfg80211_chan_def *csa_chandef,
- 				   bool associated);
- 
-+/**
-+ * cfg80211_background_radar_update_channel - notify background chandef has been updated
-+ * @wiphy: the wiphy
-+ * @chandef: the updated chandef
-+ * @expand: whether or not the operating channel should expand its width
-+ * after offchan CAC
-+ *
-+ * Update the background chandef based on driver's decision, and notify the userspace
-+ * that the current channel of background chain should be updated.
-+ */
-+void cfg80211_background_radar_update_channel(struct wiphy *wiphy,
-+					      const struct cfg80211_chan_def *chandef,
-+					      bool expand);
-+
- /**
-  * cfg80211_background_cac_abort - Channel Availability Check offchan abort event
-  * @wiphy: the wiphy
-diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
-index 7999a65..e859c96 100644
---- a/include/uapi/linux/nl80211.h
-+++ b/include/uapi/linux/nl80211.h
-@@ -6822,6 +6822,10 @@ enum nl80211_smps_mode {
-  *	applicable for ETSI dfs domain where pre-CAC is valid for ever.
-  * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
-  *	should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
-+ * @NL80211_RADAR_BACKGROUND_CHAN_UPDATE: background channel is updated by the
-+ *	driver.
-+ * @NL80211_RADAR_BACKGROUND_CHAN_EXPAND: background channel is updated by the
-+ *	driver and required to expand main operating channel.
-  * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
-  *	when receiving CSA/assoc resp
-  * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
-@@ -6834,6 +6838,8 @@ enum nl80211_radar_event {
- 	NL80211_RADAR_NOP_FINISHED,
- 	NL80211_RADAR_PRE_CAC_EXPIRED,
- 	NL80211_RADAR_CAC_STARTED,
-+	NL80211_RADAR_BACKGROUND_CHAN_UPDATE,
-+	NL80211_RADAR_BACKGROUND_CHAN_EXPAND,
- 	NL80211_RADAR_STA_CAC_SKIPPED,
- 	NL80211_RADAR_STA_CAC_EXPIRED,
- };
-diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
-index 56095b3..3da7886 100644
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -1281,6 +1281,18 @@ cfg80211_start_background_radar_detection(struct cfg80211_registered_device *rde
- 	return 0;
- }
- 
-+void cfg80211_background_radar_update_channel(struct wiphy *wiphy,
-+					      const struct cfg80211_chan_def *chandef,
-+					      bool expand)
-+{
-+	enum nl80211_radar_event event;
-+
-+	event = expand ? NL80211_RADAR_BACKGROUND_CHAN_EXPAND :
-+			 NL80211_RADAR_BACKGROUND_CHAN_UPDATE;
-+	nl80211_radar_notify(wiphy_to_rdev(wiphy), chandef, event, NULL, GFP_ATOMIC);
-+}
-+EXPORT_SYMBOL(cfg80211_background_radar_update_channel);
-+
- void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev)
- {
- 	struct wiphy *wiphy = wdev->wiphy;
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0029-mtk-nl80211-Mark-DFS-channel-as-available-for-CSA.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0029-mtk-nl80211-Mark-DFS-channel-as-available-for-CSA.patch
new file mode 100644
index 0000000..4dd3216
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0029-mtk-nl80211-Mark-DFS-channel-as-available-for-CSA.patch
@@ -0,0 +1,28 @@
+From 7ae92738c631b522bcca96ed5212fff330ab8381 Mon Sep 17 00:00:00 2001
+From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
+Date: Fri, 17 Mar 2023 17:36:01 +0800
+Subject: [PATCH 29/89] mtk: nl80211: Mark DFS channel as available for CSA.
+
+---
+ net/wireless/nl80211.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index abda8ca..41e9f8d 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -10416,6 +10416,11 @@ skip_beacons:
+ 	if (err)
+ 		goto free;
+ 
++	/* Use RADAR_BACKGROUND attribute here for skipping CAC */
++	if (info->attrs[NL80211_ATTR_RADAR_BACKGROUND]) {
++		cfg80211_set_dfs_state(&rdev->wiphy, &params.chandef, NL80211_DFS_AVAILABLE);
++	}
++
+ 	if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, &params.chandef,
+ 					   wdev->iftype)) {
+ 		err = -EINVAL;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0030-mtk-cfg80211-fix-early-return-in-cfg80211_stop_backg.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0030-mtk-cfg80211-fix-early-return-in-cfg80211_stop_backg.patch
new file mode 100644
index 0000000..937efeb
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0030-mtk-cfg80211-fix-early-return-in-cfg80211_stop_backg.patch
@@ -0,0 +1,29 @@
+From 1a56eee4f7f5a7f9312219d41ae74d5472b75857 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 30/89] mtk: cfg80211: 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 7a92f87..3b62f9a 100644
+--- a/net/wireless/mlme.c
++++ b/net/wireless/mlme.c
+@@ -1294,9 +1294,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/0030-mtk-mac80211-Allow-STA-interface-to-set-TX-queue-par.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0030-mtk-mac80211-Allow-STA-interface-to-set-TX-queue-par.patch
deleted file mode 100644
index ca8cdc0..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0030-mtk-mac80211-Allow-STA-interface-to-set-TX-queue-par.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From bfe137c0bceae36a34e766d2623893c4b1cbdd53 Mon Sep 17 00:00:00 2001
-From: Michael Lee <michael-cy.lee@mediatek.com>
-Date: Fri, 7 Jul 2023 17:17:30 +0800
-Subject: [PATCH 30/61] mtk: mac80211: Allow STA interface to set TX queue
- parameters
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- net/wireless/nl80211.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
-index 1f1856b..079fedf 100644
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -3557,6 +3557,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
- 		}
- 
- 		if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
-+		    netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
- 		    netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
- 			result = -EINVAL;
- 			goto out;
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0031-mtk-cfg80211-add-background-radar-stop-when-backgrou.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0031-mtk-cfg80211-add-background-radar-stop-when-backgrou.patch
new file mode 100644
index 0000000..8470097
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0031-mtk-cfg80211-add-background-radar-stop-when-backgrou.patch
@@ -0,0 +1,29 @@
+From af06ac5ac954a801dceb279fd42a5e7af88b4222 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 31/89] mtk: cfg80211: 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 41e9f8d..6b03037 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -10201,6 +10201,10 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
+ 		wdev->links[link_id].cac_started = true;
+ 		wdev->links[link_id].cac_start_time = jiffies;
+ 		wdev->links[link_id].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/0031-mtk-mac80211-export-ieee80211_tpt_led_trig_tx-rx-for.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0031-mtk-mac80211-export-ieee80211_tpt_led_trig_tx-rx-for.patch
deleted file mode 100644
index f070841..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0031-mtk-mac80211-export-ieee80211_tpt_led_trig_tx-rx-for.patch
+++ /dev/null
@@ -1,150 +0,0 @@
-From 2a5c863cc42d21d20a25496426696c95cea45312 Mon Sep 17 00:00:00 2001
-From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Date: Fri, 23 Jun 2023 05:53:50 +0800
-Subject: [PATCH 31/61] mtk: mac80211: export ieee80211_tpt_led_trig_tx/rx for
- driver
-
-Whenever the H/W path is enabled and traffic is in the binding state,
-mac80211 is not aware of the traffic. Consequently, the LED does not
-blink for that reason.
-
-The ieee80211_tpt_led_trig_tx/rx functions are exported for the driver
-so that we can report the tx and rx bytes from the driver when
-the H/W path is being used.
-
-Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
----
- include/net/mac80211.h | 17 +++++++++++++++++
- net/mac80211/led.c     | 16 ++++++++++++++++
- net/mac80211/led.h     | 17 -----------------
- net/mac80211/rx.c      |  2 +-
- net/mac80211/tx.c      |  4 ++--
- 5 files changed, 36 insertions(+), 20 deletions(-)
-
-diff --git a/include/net/mac80211.h b/include/net/mac80211.h
-index 965a026..860ad6e 100644
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -4893,6 +4893,8 @@ __ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
- 				   unsigned int flags,
- 				   const struct ieee80211_tpt_blink *blink_table,
- 				   unsigned int blink_table_len);
-+void __ieee80211_tpt_led_trig_tx(struct ieee80211_hw *hw, int bytes);
-+void __ieee80211_tpt_led_trig_rx(struct ieee80211_hw *hw, int bytes);
- #endif
- /**
-  * ieee80211_get_tx_led_name - get name of TX LED
-@@ -5003,6 +5005,21 @@ ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw, unsigned int flags,
- #endif
- }
- 
-+static inline void
-+ieee80211_tpt_led_trig_tx(struct ieee80211_hw *hw, int bytes)
-+{
-+#ifdef CPTCFG_MAC80211_LEDS
-+	__ieee80211_tpt_led_trig_tx(hw, bytes);
-+#endif
-+}
-+
-+static inline void
-+ieee80211_tpt_led_trig_rx(struct ieee80211_hw *hw, int bytes)
-+{
-+#ifdef CPTCFG_MAC80211_LEDS
-+	__ieee80211_tpt_led_trig_rx(hw, bytes);
-+#endif
-+}
- /**
-  * ieee80211_unregister_hw - Unregister a hardware device
-  *
-diff --git a/net/mac80211/led.c b/net/mac80211/led.c
-index b992430..3109501 100644
---- a/net/mac80211/led.c
-+++ b/net/mac80211/led.c
-@@ -364,6 +364,22 @@ __ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
- }
- EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
- 
-+void __ieee80211_tpt_led_trig_tx(struct ieee80211_hw *hw, int bytes)
-+{
-+	struct ieee80211_local *local = hw_to_local(hw);
-+	if (atomic_read(&local->tpt_led_active))
-+		local->tpt_led_trigger->tx_bytes += bytes;
-+}
-+EXPORT_SYMBOL(__ieee80211_tpt_led_trig_tx);
-+
-+void __ieee80211_tpt_led_trig_rx(struct ieee80211_hw *hw, int bytes)
-+{
-+	struct ieee80211_local *local = hw_to_local(hw);
-+	if (atomic_read(&local->tpt_led_active))
-+		local->tpt_led_trigger->rx_bytes += bytes;
-+}
-+EXPORT_SYMBOL(__ieee80211_tpt_led_trig_rx);
-+
- static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
- {
- 	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
-diff --git a/net/mac80211/led.h b/net/mac80211/led.h
-index 59f5a83..f381790 100644
---- a/net/mac80211/led.h
-+++ b/net/mac80211/led.h
-@@ -65,22 +65,5 @@ static inline void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local,
- 					      unsigned int types_off)
- {
- }
--#endif
- 
--static inline void
--ieee80211_tpt_led_trig_tx(struct ieee80211_local *local, int bytes)
--{
--#ifdef CPTCFG_MAC80211_LEDS
--	if (atomic_read(&local->tpt_led_active))
--		local->tpt_led_trigger->tx_bytes += bytes;
- #endif
--}
--
--static inline void
--ieee80211_tpt_led_trig_rx(struct ieee80211_local *local, int bytes)
--{
--#ifdef CPTCFG_MAC80211_LEDS
--	if (atomic_read(&local->tpt_led_active))
--		local->tpt_led_trigger->rx_bytes += bytes;
--#endif
--}
-diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
-index 65982a6..fe3d9fb 100644
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -5469,7 +5469,7 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
- 	if (skb) {
- 		if ((status->flag & RX_FLAG_8023) ||
- 			ieee80211_is_data_present(hdr->frame_control))
--			ieee80211_tpt_led_trig_rx(local, skb->len);
-+			ieee80211_tpt_led_trig_rx(&local->hw, skb->len);
- 
- 		if (status->flag & RX_FLAG_8023)
- 			__ieee80211_rx_handle_8023(hw, pubsta, skb, list);
-diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
-index f479d87..344f4bf 100644
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -4340,7 +4340,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
- 	len = 0;
-  out:
- 	if (len)
--		ieee80211_tpt_led_trig_tx(local, len);
-+		ieee80211_tpt_led_trig_tx(&local->hw, len);
- 	rcu_read_unlock();
- }
- 
-@@ -4671,7 +4671,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
- 	sta->deflink.tx_stats.packets[queue] += skbs;
- 	sta->deflink.tx_stats.bytes[queue] += len;
- 
--	ieee80211_tpt_led_trig_tx(local, len);
-+	ieee80211_tpt_led_trig_tx(&local->hw, len);
- 
- 	ieee80211_tx_8023(sdata, skb, sta, false);
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0032-mtk-mac80211-add-packet-count-input-for-dev_sw_netst.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0032-mtk-mac80211-add-packet-count-input-for-dev_sw_netst.patch
deleted file mode 100644
index d879e5e..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0032-mtk-mac80211-add-packet-count-input-for-dev_sw_netst.patch
+++ /dev/null
@@ -1,136 +0,0 @@
-From fc5c4fd0f0edc27c7a21e43a51e1a90a2f7b17b2 Mon Sep 17 00:00:00 2001
-From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Date: Tue, 22 Aug 2023 05:02:53 +0800
-Subject: [PATCH 32/61] mtk: mac80211: add packet count input for
- dev_sw_netstat_rx_add
-
----
- backport-include/linux/netdevice.h                   | 12 ++++++++----
- drivers/net/usb/qmi_wwan.c                           |  2 +-
- .../net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c |  2 +-
- .../net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c |  2 +-
- net/mac80211/rx.c                                    |  8 ++++----
- 5 files changed, 15 insertions(+), 11 deletions(-)
-
-diff --git a/backport-include/linux/netdevice.h b/backport-include/linux/netdevice.h
-index 1d2ac66..04d76d7 100644
---- a/backport-include/linux/netdevice.h
-+++ b/backport-include/linux/netdevice.h
-@@ -112,13 +112,15 @@ void dev_fetch_sw_netstats(struct rtnl_link_stats64 *s,
- #define netif_rx_any_context LINUX_BACKPORT(netif_rx_any_context)
- int netif_rx_any_context(struct sk_buff *skb);
- 
--static inline void dev_sw_netstats_rx_add(struct net_device *dev, unsigned int len)
-+static inline void dev_sw_netstats_rx_add(struct net_device *dev,
-+					  unsigned int packets,
-+					  unsigned int len)
- {
- 	struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
- 
- 	u64_stats_update_begin(&tstats->syncp);
- 	tstats->rx_bytes += len;
--	tstats->rx_packets++;
-+	tstats->rx_packets += packets;
- 	u64_stats_update_end(&tstats->syncp);
- }
- 
-@@ -140,13 +142,15 @@ static inline void dev_sw_netstats_tx_add(struct net_device *dev,
- 
- #if LINUX_VERSION_IS_LESS(5,10,0)
- #define dev_sw_netstats_rx_add LINUX_BACKPORT(dev_sw_netstats_rx_add)
--static inline void dev_sw_netstats_rx_add(struct net_device *dev, unsigned int len)
-+static inline void dev_sw_netstats_rx_add(struct net_device *dev,
-+					  unsigned int packets,
-+					  unsigned int len)
- {
- 	struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
- 
- 	u64_stats_update_begin(&tstats->syncp);
- 	tstats->rx_bytes += len;
--	tstats->rx_packets++;
-+	tstats->rx_packets += packets;
- 	u64_stats_update_end(&tstats->syncp);
- }
- #endif /* < 5.10 */
-diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
-index f7f640f..436e37d 100644
---- a/drivers/net/usb/qmi_wwan.c
-+++ b/drivers/net/usb/qmi_wwan.c
-@@ -228,7 +228,7 @@ static int qmimux_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
- 			net->stats.rx_errors++;
- 			return 0;
- 		} else {
--			dev_sw_netstats_rx_add(net, pkt_len);
-+			dev_sw_netstats_rx_add(net, 1, pkt_len);
- 		}
- 
- skip:
-diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
-index c1a53e1..01ff00f 100644
---- a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
-+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
-@@ -756,7 +756,7 @@ static int qtnf_pcie_pearl_rx_poll(struct napi_struct *napi, int budget)
- 			skb_put(skb, psize);
- 			ndev = qtnf_classify_skb(bus, skb);
- 			if (likely(ndev)) {
--				dev_sw_netstats_rx_add(ndev, skb->len);
-+				dev_sw_netstats_rx_add(ndev, 1, skb->len);
- 				skb->protocol = eth_type_trans(skb, ndev);
- 				napi_gro_receive(napi, skb);
- 			} else {
-diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
-index ef5c069..8136745 100644
---- a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
-+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
-@@ -662,7 +662,7 @@ static int qtnf_topaz_rx_poll(struct napi_struct *napi, int budget)
- 			skb_put(skb, psize);
- 			ndev = qtnf_classify_skb(bus, skb);
- 			if (likely(ndev)) {
--				dev_sw_netstats_rx_add(ndev, skb->len);
-+				dev_sw_netstats_rx_add(ndev, 1, skb->len);
- 				skb->protocol = eth_type_trans(skb, ndev);
- 				netif_receive_skb(skb);
- 			} else {
-diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
-index fe3d9fb..da3fc51 100644
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -863,7 +863,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
- 
- 			if (skb) {
- 				skb->dev = sdata->dev;
--				dev_sw_netstats_rx_add(skb->dev, skb->len);
-+				dev_sw_netstats_rx_add(skb->dev, 1, skb->len);
- 				netif_receive_skb(skb);
- 			}
- 		}
-@@ -2672,7 +2672,7 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
- 	skb = rx->skb;
- 	xmit_skb = NULL;
- 
--	dev_sw_netstats_rx_add(dev, skb->len);
-+	dev_sw_netstats_rx_add(dev, 1, skb->len);
- 
- 	if (rx->sta) {
- 		/* The seqno index has the same property as needed
-@@ -4111,7 +4111,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
- 		}
- 
- 		prev_dev = sdata->dev;
--		dev_sw_netstats_rx_add(sdata->dev, skb->len);
-+		dev_sw_netstats_rx_add(sdata->dev, 1, skb->len);
- 	}
- 
- 	if (prev_dev) {
-@@ -4819,7 +4819,7 @@ static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,
- 
- 	skb->dev = fast_rx->dev;
- 
--	dev_sw_netstats_rx_add(fast_rx->dev, skb->len);
-+	dev_sw_netstats_rx_add(fast_rx->dev, 1, skb->len);
- 
- 	/* The seqno index has the same property as needed
- 	 * for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0032-mtk-mac80211-avoid-kernel-warning-of-check_flush_dep.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0032-mtk-mac80211-avoid-kernel-warning-of-check_flush_dep.patch
new file mode 100644
index 0000000..9eb0d55
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0032-mtk-mac80211-avoid-kernel-warning-of-check_flush_dep.patch
@@ -0,0 +1,26 @@
+From 3bdb886e69a33e97bddd62032390f0bf6c0df24d 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 32/89] mtk: mac80211: 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 79c77af..ca371ef 100644
+--- a/net/mac80211/main.c
++++ b/net/mac80211/main.c
+@@ -1475,7 +1475,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/0033-mtk-mac80211-add-EHT-BA1024-support.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0033-mtk-mac80211-add-EHT-BA1024-support.patch
new file mode 100644
index 0000000..3194de2
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0033-mtk-mac80211-add-EHT-BA1024-support.patch
@@ -0,0 +1,116 @@
+From 64158fc5e1118537334c5fb512f18e7e1e1daa02 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Sun, 25 Dec 2022 22:43:46 +0800
+Subject: [PATCH 33/89] mtk: mac80211: add EHT BA1024 support
+
+---
+ include/linux/ieee80211.h |  2 ++
+ net/mac80211/agg-tx.c     | 45 +++++++++++++++++++++++++++++++++++++--
+ 2 files changed, 45 insertions(+), 2 deletions(-)
+
+diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
+index 30cef3b..3039cb1 100644
+--- a/include/linux/ieee80211.h
++++ b/include/linux/ieee80211.h
+@@ -1445,6 +1445,8 @@ struct ieee80211_mgmt {
+ 					__le16 status;
+ 					__le16 capab;
+ 					__le16 timeout;
++					/* followed by BA Extension */
++					u8 variable[0];
+ 				} __packed addba_resp;
+ 				struct{
+ 					u8 action_code;
+diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
+index 9c640ca..de1b2b2 100644
+--- a/net/mac80211/agg-tx.c
++++ b/net/mac80211/agg-tx.c
+@@ -72,10 +72,17 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
+ 	struct ieee80211_local *local = sdata->local;
+ 	struct sk_buff *skb;
+ 	struct ieee80211_mgmt *mgmt;
++	struct ieee80211_addba_ext_ie *addba_ext;
++	u8 *pos;
+ 	u16 capab = 0;
+ 	bool amsdu = ieee80211_hw_check(&local->hw, SUPPORTS_AMSDU_IN_AMPDU);
+ 
+-	skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
++	if (agg_size >= 1024)
++		skb = dev_alloc_skb(sizeof(*mgmt) +
++				    2 + sizeof(struct ieee80211_addba_ext_ie) +
++				    local->hw.extra_tx_headroom);
++	else
++		skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
+ 
+ 	if (!skb)
+ 		return;
+@@ -114,6 +121,15 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
+ 	mgmt->u.action.u.addba_req.start_seq_num =
+ 					cpu_to_le16(start_seq_num << 4);
+ 
++	if (agg_size >= 1024 ) {
++		pos = skb_put_zero(skb, 2 + sizeof(struct ieee80211_addba_ext_ie));
++		*pos++ = WLAN_EID_ADDBA_EXT;
++		*pos++ = sizeof(struct ieee80211_addba_ext_ie);
++		addba_ext = (struct ieee80211_addba_ext_ie *)pos;
++		addba_ext->data = u8_encode_bits(agg_size >> IEEE80211_ADDBA_EXT_BUF_SIZE_SHIFT,
++						 IEEE80211_ADDBA_EXT_BUF_SIZE_MASK);
++	}
++
+ 	ieee80211_tx_skb_tid(sdata, skb, tid, -1);
+ }
+ 
+@@ -481,8 +497,11 @@ static void ieee80211_send_addba_with_timeout(struct sta_info *sta,
+ 	sta->ampdu_mlme.addba_req_num[tid]++;
+ 	spin_unlock_bh(&sta->lock);
+ 
+-	if (sta->sta.deflink.he_cap.has_he) {
++	if (sta->sta.deflink.eht_cap.has_eht) {
+ 		buf_size = local->hw.max_tx_aggregation_subframes;
++	} else if (sta->sta.deflink.he_cap.has_he) {
++		buf_size = min_t(u16, local->hw.max_tx_aggregation_subframes,
++				 IEEE80211_MAX_AMPDU_BUF_HE);
+ 	} else {
+ 		/*
+ 		 * We really should use what the driver told us it will
+@@ -982,8 +1001,10 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
+ {
+ 	struct tid_ampdu_tx *tid_tx;
+ 	struct ieee80211_txq *txq;
++	struct ieee802_11_elems *elems;
+ 	u16 capab, tid, buf_size;
+ 	bool amsdu;
++	int ext_ie_len;
+ 
+ 	lockdep_assert_wiphy(sta->local->hw.wiphy);
+ 
+@@ -991,6 +1012,26 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
+ 	amsdu = capab & IEEE80211_ADDBA_PARAM_AMSDU_MASK;
+ 	tid = u16_get_bits(capab, IEEE80211_ADDBA_PARAM_TID_MASK);
+ 	buf_size = u16_get_bits(capab, IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK);
++	ext_ie_len = len - offsetof(struct ieee80211_mgmt,
++				    u.action.u.addba_resp.variable);
++
++	if (ext_ie_len < 0)
++		goto next;
++
++	elems = ieee802_11_parse_elems(mgmt->u.action.u.addba_resp.variable,
++				       ext_ie_len, true, NULL);
++
++	if (elems && !elems->parse_error) {
++		if (sta->sta.deflink.eht_cap.has_eht && elems->addba_ext_ie) {
++			u8 buf_size_1k = u8_get_bits(elems->addba_ext_ie->data,
++						     IEEE80211_ADDBA_EXT_BUF_SIZE_MASK);
++			buf_size |= buf_size_1k << IEEE80211_ADDBA_EXT_BUF_SIZE_SHIFT;
++		}
++	}
++
++	if (elems)
++		kfree(elems);
++next:
+ 	buf_size = min(buf_size, local->hw.max_tx_aggregation_subframes);
+ 
+ 	txq = sta->sta.txq[tid];
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0033-mtk-mac80211-add-per-bss-flag-to-support-vendors-cou.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0033-mtk-mac80211-add-per-bss-flag-to-support-vendors-cou.patch
deleted file mode 100644
index cdc396e..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0033-mtk-mac80211-add-per-bss-flag-to-support-vendors-cou.patch
+++ /dev/null
@@ -1,91 +0,0 @@
-From 4d85822963f5d150be638d78ea84c5199f80d676 Mon Sep 17 00:00:00 2001
-From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Date: Wed, 16 Aug 2023 07:23:34 +0800
-Subject: [PATCH 33/61] mtk: mac80211: add per-bss flag to support vendors
- counter
-
----
- include/uapi/linux/nl80211.h |  1 +
- net/mac80211/rx.c            |  8 ++++++--
- net/mac80211/tx.c            | 13 ++++++++++---
- 3 files changed, 17 insertions(+), 5 deletions(-)
-
-diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
-index e859c96..220d20d 100644
---- a/include/uapi/linux/nl80211.h
-+++ b/include/uapi/linux/nl80211.h
-@@ -6620,6 +6620,7 @@ enum nl80211_ext_feature_index {
- 	NL80211_EXT_FEATURE_OWE_OFFLOAD_AP,
- 	NL80211_EXT_FEATURE_DFS_CONCURRENT,
- 	NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT,
-+	NL80211_EXT_FEATURE_STAS_COUNT,
- 
- 	/* add new features before the definition below */
- 	NUM_NL80211_EXT_FEATURES,
-diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
-index da3fc51..06725f3 100644
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -2672,7 +2672,9 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
- 	skb = rx->skb;
- 	xmit_skb = NULL;
- 
--	dev_sw_netstats_rx_add(dev, 1, skb->len);
-+	if (!wiphy_ext_feature_isset(sdata->local->hw.wiphy,
-+	    NL80211_EXT_FEATURE_STAS_COUNT) || !rx->sta)
-+		dev_sw_netstats_rx_add(dev, 1, skb->len);
- 
- 	if (rx->sta) {
- 		/* The seqno index has the same property as needed
-@@ -4819,7 +4821,9 @@ static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,
- 
- 	skb->dev = fast_rx->dev;
- 
--	dev_sw_netstats_rx_add(fast_rx->dev, 1, skb->len);
-+	if (!wiphy_ext_feature_isset(sta->local->hw.wiphy,
-+	    NL80211_EXT_FEATURE_STAS_COUNT))
-+		dev_sw_netstats_rx_add(fast_rx->dev, 1, skb->len);
- 
- 	/* The seqno index has the same property as needed
- 	 * for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
-diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
-index 344f4bf..a2ed041 100644
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -3559,7 +3559,9 @@ ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata,
- 	if (key)
- 		info->control.hw_key = &key->conf;
- 
--	dev_sw_netstats_tx_add(skb->dev, 1, skb->len);
-+	if (!wiphy_ext_feature_isset(sta->local->hw.wiphy,
-+	    NL80211_EXT_FEATURE_STAS_COUNT))
-+		dev_sw_netstats_tx_add(skb->dev, 1, skb->len);
- 
- 	if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
- 		tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
-@@ -4330,7 +4332,9 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
- 			goto out;
- 		}
- 
--		dev_sw_netstats_tx_add(dev, 1, skb->len);
-+		if (!wiphy_ext_feature_isset(sdata->local->hw.wiphy,
-+		    NL80211_EXT_FEATURE_STAS_COUNT) || !sta)
-+			dev_sw_netstats_tx_add(dev, 1, skb->len);
- 
- 		ieee80211_xmit(sdata, sta, skb);
- 	}
-@@ -4667,7 +4671,10 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
- 			info->status_data_idr = 1;
- 	}
- 
--	dev_sw_netstats_tx_add(dev, skbs, len);
-+	if (!wiphy_ext_feature_isset(sta->local->hw.wiphy,
-+	    NL80211_EXT_FEATURE_STAS_COUNT))
-+		dev_sw_netstats_tx_add(dev, skbs, len);
-+
- 	sta->deflink.tx_stats.packets[queue] += skbs;
- 	sta->deflink.tx_stats.bytes[queue] += len;
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0034-mtk-mac80211-add-rate-duration-for-EHT-rate.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0034-mtk-mac80211-add-rate-duration-for-EHT-rate.patch
new file mode 100644
index 0000000..99dbb95
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0034-mtk-mac80211-add-rate-duration-for-EHT-rate.patch
@@ -0,0 +1,440 @@
+From 4540637b8a7905d67cd86bce3f85a42a79226561 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Sun, 25 Dec 2022 22:43:46 +0800
+Subject: [PATCH 34/89] mtk: mac80211: add rate duration for EHT rate.
+
+---
+ net/mac80211/airtime.c | 349 ++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 346 insertions(+), 3 deletions(-)
+
+diff --git a/net/mac80211/airtime.c b/net/mac80211/airtime.c
+index fdf8b65..370477c 100644
+--- a/net/mac80211/airtime.c
++++ b/net/mac80211/airtime.c
+@@ -55,10 +55,21 @@
+ #define HE_DURATION_S(shift, streams, gi, bps)		\
+ 	(HE_DURATION(streams, gi, bps) >> shift)
+ 
++/* Transmit duration for the raw data part of an average sized packet */
++#define EHT_GI_08 HE_GI_08
++#define EHT_GI_16 HE_GI_16
++#define EHT_GI_32 HE_GI_32
++
++#define EHT_DURATION(streams, gi, bps)			\
++	HE_DURATION(streams, gi, bps)
++#define EHT_DURATION_S(shift, streams, gi, bps)		\
++	HE_DURATION_S(shift, streams, gi, bps)
++
+ #define BW_20			0
+ #define BW_40			1
+ #define BW_80			2
+ #define BW_160			3
++#define BW_320			4
+ 
+ /*
+  * Define group sort order: HT40 -> SGI -> #streams
+@@ -68,17 +79,26 @@
+ #define IEEE80211_VHT_STREAM_GROUPS	8 /* BW(=4) * SGI(=2) */
+ 
+ #define IEEE80211_HE_MAX_STREAMS	8
++#define IEEE80211_HE_STREAM_GROUPS	12 /* BW(=4) * GI(=3) */
++
++#define IEEE80211_EHT_MAX_STREAMS	16
++#define IEEE80211_EHT_STREAM_GROUPS	15 /* BW(=5) * GI(=3) */
+ 
+ #define IEEE80211_HT_GROUPS_NB	(IEEE80211_MAX_STREAMS *	\
+ 				 IEEE80211_HT_STREAM_GROUPS)
+ #define IEEE80211_VHT_GROUPS_NB	(IEEE80211_MAX_STREAMS *	\
+ 					 IEEE80211_VHT_STREAM_GROUPS)
++#define IEEE80211_HE_GROUPS_NB	(IEEE80211_HE_MAX_STREAMS *	\
++				 IEEE80211_HE_STREAM_GROUPS)
++#define IEEE80211_EHT_GROUPS_NB	(IEEE80211_EHT_MAX_STREAMS *	\
++				 IEEE80211_EHT_STREAM_GROUPS)
+ 
+ #define IEEE80211_HT_GROUP_0	0
+ #define IEEE80211_VHT_GROUP_0	(IEEE80211_HT_GROUP_0 + IEEE80211_HT_GROUPS_NB)
+ #define IEEE80211_HE_GROUP_0	(IEEE80211_VHT_GROUP_0 + IEEE80211_VHT_GROUPS_NB)
++#define IEEE80211_EHT_GROUP_0	(IEEE80211_HE_GROUP_0 + IEEE80211_HE_GROUPS_NB)
+ 
+-#define MCS_GROUP_RATES		12
++#define MCS_GROUP_RATES		14
+ 
+ #define HT_GROUP_IDX(_streams, _sgi, _ht40)	\
+ 	IEEE80211_HT_GROUP_0 +			\
+@@ -203,6 +223,59 @@
+ #define HE_GROUP(_streams, _gi, _bw)					\
+ 	__HE_GROUP(_streams, _gi, _bw,				\
+ 		   HE_GROUP_SHIFT(_streams, _gi, _bw))
++
++#define EHT_BW2VBPS(_bw, r5, r4, r3, r2, r1)					\
++	(_bw == BW_320 ? r5 : _bw == BW_160 ? r4 : _bw == BW_80 ? r3 : _bw == BW_40 ? r2 : r1)
++
++#define EHT_GROUP_IDX(_streams, _gi, _bw)				\
++	(IEEE80211_EHT_GROUP_0 +					\
++	 IEEE80211_EHT_MAX_STREAMS * 3 * (_bw) +			\
++	 IEEE80211_EHT_MAX_STREAMS * (_gi) +				\
++	 (_streams) - 1)
++
++#define __EHT_GROUP(_streams, _gi, _bw, _s)				\
++	[EHT_GROUP_IDX(_streams, _gi, _bw)] = {			\
++	.shift = _s,							\
++	.duration = {							\
++		EHT_DURATION_S(_s, _streams, _gi,			\
++			      EHT_BW2VBPS(_bw,   1960,   979,  489,  230,  115)),	\
++		EHT_DURATION_S(_s, _streams, _gi,			\
++			      EHT_BW2VBPS(_bw,   3920,  1958,  979,  475,  230)),	\
++		EHT_DURATION_S(_s, _streams, _gi,			\
++			      EHT_BW2VBPS(_bw,   5880,  2937, 1468,  705,  345)),	\
++		EHT_DURATION_S(_s, _streams, _gi,			\
++			      EHT_BW2VBPS(_bw,   7840,  3916, 1958,  936,  475)),	\
++		EHT_DURATION_S(_s, _streams, _gi,			\
++			      EHT_BW2VBPS(_bw,  11760,  5875, 2937, 1411,  705)),	\
++		EHT_DURATION_S(_s, _streams, _gi,			\
++			      EHT_BW2VBPS(_bw,  15680,  7833, 3916, 1872,  936)),	\
++		EHT_DURATION_S(_s, _streams, _gi,			\
++			      EHT_BW2VBPS(_bw,  17640,  8827, 4406, 2102, 1051)),	\
++		EHT_DURATION_S(_s, _streams, _gi,			\
++			      EHT_BW2VBPS(_bw,  19600,  9806, 4896, 2347, 1166)),	\
++		EHT_DURATION_S(_s, _streams, _gi,			\
++			      EHT_BW2VBPS(_bw,  23520, 11764, 5875, 2808, 1411)),	\
++		EHT_DURATION_S(_s, _streams, _gi,			\
++			      EHT_BW2VBPS(_bw,  26133, 13060, 6523, 3124, 1555)),	\
++		EHT_DURATION_S(_s, _streams, _gi,			\
++			      EHT_BW2VBPS(_bw,  29400, 14702, 7344, 3513, 1756)),	\
++		EHT_DURATION_S(_s, _streams, _gi,			\
++			      EHT_BW2VBPS(_bw,  32666, 16329, 8164, 3902, 1944)),	\
++		EHT_DURATION_S(_s, _streams, _gi,			\
++			      EHT_BW2VBPS(_bw,  35280, 17640, 8820, 4212, 2106)),	\
++		EHT_DURATION_S(_s, _streams, _gi,			\
++			      EHT_BW2VBPS(_bw,  39200, 19600, 9800, 4680, 2340))	\
++        }								\
++}
++
++#define EHT_GROUP_SHIFT(_streams, _gi, _bw)				\
++	GROUP_SHIFT(EHT_DURATION(_streams, _gi,			\
++				EHT_BW2VBPS(_bw,   1960,   979,  489,  230,  115)))
++
++#define EHT_GROUP(_streams, _gi, _bw)					\
++	__EHT_GROUP(_streams, _gi, _bw,				\
++		   EHT_GROUP_SHIFT(_streams, _gi, _bw))
++
+ struct mcs_group {
+ 	u8 shift;
+ 	u16 duration[MCS_GROUP_RATES];
+@@ -376,6 +449,262 @@ static const struct mcs_group airtime_mcs_groups[] = {
+ 	HE_GROUP(6, HE_GI_32, BW_160),
+ 	HE_GROUP(7, HE_GI_32, BW_160),
+ 	HE_GROUP(8, HE_GI_32, BW_160),
++
++	/* EHT */
++	EHT_GROUP( 1, EHT_GI_08, BW_20),
++	EHT_GROUP( 2, EHT_GI_08, BW_20),
++	EHT_GROUP( 3, EHT_GI_08, BW_20),
++	EHT_GROUP( 4, EHT_GI_08, BW_20),
++	EHT_GROUP( 5, EHT_GI_08, BW_20),
++	EHT_GROUP( 6, EHT_GI_08, BW_20),
++	EHT_GROUP( 7, EHT_GI_08, BW_20),
++	EHT_GROUP( 8, EHT_GI_08, BW_20),
++	EHT_GROUP( 9, EHT_GI_08, BW_20),
++	EHT_GROUP(10, EHT_GI_08, BW_20),
++	EHT_GROUP(11, EHT_GI_08, BW_20),
++	EHT_GROUP(12, EHT_GI_08, BW_20),
++	EHT_GROUP(13, EHT_GI_08, BW_20),
++	EHT_GROUP(14, EHT_GI_08, BW_20),
++	EHT_GROUP(15, EHT_GI_08, BW_20),
++	EHT_GROUP(16, EHT_GI_08, BW_20),
++
++	EHT_GROUP( 1, EHT_GI_16, BW_20),
++	EHT_GROUP( 2, EHT_GI_16, BW_20),
++	EHT_GROUP( 3, EHT_GI_16, BW_20),
++	EHT_GROUP( 4, EHT_GI_16, BW_20),
++	EHT_GROUP( 5, EHT_GI_16, BW_20),
++	EHT_GROUP( 6, EHT_GI_16, BW_20),
++	EHT_GROUP( 7, EHT_GI_16, BW_20),
++	EHT_GROUP( 8, EHT_GI_16, BW_20),
++	EHT_GROUP( 9, EHT_GI_16, BW_20),
++	EHT_GROUP(10, EHT_GI_16, BW_20),
++	EHT_GROUP(11, EHT_GI_16, BW_20),
++	EHT_GROUP(12, EHT_GI_16, BW_20),
++	EHT_GROUP(13, EHT_GI_16, BW_20),
++	EHT_GROUP(14, EHT_GI_16, BW_20),
++	EHT_GROUP(15, EHT_GI_16, BW_20),
++	EHT_GROUP(16, EHT_GI_16, BW_20),
++
++	EHT_GROUP( 1, EHT_GI_32, BW_20),
++	EHT_GROUP( 2, EHT_GI_32, BW_20),
++	EHT_GROUP( 3, EHT_GI_32, BW_20),
++	EHT_GROUP( 4, EHT_GI_32, BW_20),
++	EHT_GROUP( 5, EHT_GI_32, BW_20),
++	EHT_GROUP( 6, EHT_GI_32, BW_20),
++	EHT_GROUP( 7, EHT_GI_32, BW_20),
++	EHT_GROUP( 8, EHT_GI_32, BW_20),
++	EHT_GROUP( 9, EHT_GI_32, BW_20),
++	EHT_GROUP(10, EHT_GI_32, BW_20),
++	EHT_GROUP(11, EHT_GI_32, BW_20),
++	EHT_GROUP(12, EHT_GI_32, BW_20),
++	EHT_GROUP(13, EHT_GI_32, BW_20),
++	EHT_GROUP(14, EHT_GI_32, BW_20),
++	EHT_GROUP(15, EHT_GI_32, BW_20),
++	EHT_GROUP(16, EHT_GI_32, BW_20),
++
++	EHT_GROUP( 1, EHT_GI_08, BW_40),
++	EHT_GROUP( 2, EHT_GI_08, BW_40),
++	EHT_GROUP( 3, EHT_GI_08, BW_40),
++	EHT_GROUP( 4, EHT_GI_08, BW_40),
++	EHT_GROUP( 5, EHT_GI_08, BW_40),
++	EHT_GROUP( 6, EHT_GI_08, BW_40),
++	EHT_GROUP( 7, EHT_GI_08, BW_40),
++	EHT_GROUP( 8, EHT_GI_08, BW_40),
++	EHT_GROUP( 9, EHT_GI_08, BW_40),
++	EHT_GROUP(10, EHT_GI_08, BW_40),
++	EHT_GROUP(11, EHT_GI_08, BW_40),
++	EHT_GROUP(12, EHT_GI_08, BW_40),
++	EHT_GROUP(13, EHT_GI_08, BW_40),
++	EHT_GROUP(14, EHT_GI_08, BW_40),
++	EHT_GROUP(15, EHT_GI_08, BW_40),
++	EHT_GROUP(16, EHT_GI_08, BW_40),
++
++	EHT_GROUP( 1, EHT_GI_16, BW_40),
++	EHT_GROUP( 2, EHT_GI_16, BW_40),
++	EHT_GROUP( 3, EHT_GI_16, BW_40),
++	EHT_GROUP( 4, EHT_GI_16, BW_40),
++	EHT_GROUP( 5, EHT_GI_16, BW_40),
++	EHT_GROUP( 6, EHT_GI_16, BW_40),
++	EHT_GROUP( 7, EHT_GI_16, BW_40),
++	EHT_GROUP( 8, EHT_GI_16, BW_40),
++	EHT_GROUP( 9, EHT_GI_16, BW_40),
++	EHT_GROUP(10, EHT_GI_16, BW_40),
++	EHT_GROUP(11, EHT_GI_16, BW_40),
++	EHT_GROUP(12, EHT_GI_16, BW_40),
++	EHT_GROUP(13, EHT_GI_16, BW_40),
++	EHT_GROUP(14, EHT_GI_16, BW_40),
++	EHT_GROUP(15, EHT_GI_16, BW_40),
++	EHT_GROUP(16, EHT_GI_16, BW_40),
++
++	EHT_GROUP( 1, EHT_GI_32, BW_40),
++	EHT_GROUP( 2, EHT_GI_32, BW_40),
++	EHT_GROUP( 3, EHT_GI_32, BW_40),
++	EHT_GROUP( 4, EHT_GI_32, BW_40),
++	EHT_GROUP( 5, EHT_GI_32, BW_40),
++	EHT_GROUP( 6, EHT_GI_32, BW_40),
++	EHT_GROUP( 7, EHT_GI_32, BW_40),
++	EHT_GROUP( 8, EHT_GI_32, BW_40),
++	EHT_GROUP( 9, EHT_GI_32, BW_40),
++	EHT_GROUP(10, EHT_GI_32, BW_40),
++	EHT_GROUP(11, EHT_GI_32, BW_40),
++	EHT_GROUP(12, EHT_GI_32, BW_40),
++	EHT_GROUP(13, EHT_GI_32, BW_40),
++	EHT_GROUP(14, EHT_GI_32, BW_40),
++	EHT_GROUP(15, EHT_GI_32, BW_40),
++	EHT_GROUP(16, EHT_GI_32, BW_40),
++
++	EHT_GROUP( 1, EHT_GI_08, BW_80),
++	EHT_GROUP( 2, EHT_GI_08, BW_80),
++	EHT_GROUP( 3, EHT_GI_08, BW_80),
++	EHT_GROUP( 4, EHT_GI_08, BW_80),
++	EHT_GROUP( 5, EHT_GI_08, BW_80),
++	EHT_GROUP( 6, EHT_GI_08, BW_80),
++	EHT_GROUP( 7, EHT_GI_08, BW_80),
++	EHT_GROUP( 8, EHT_GI_08, BW_80),
++	EHT_GROUP( 9, EHT_GI_08, BW_80),
++	EHT_GROUP(10, EHT_GI_08, BW_80),
++	EHT_GROUP(11, EHT_GI_08, BW_80),
++	EHT_GROUP(12, EHT_GI_08, BW_80),
++	EHT_GROUP(13, EHT_GI_08, BW_80),
++	EHT_GROUP(14, EHT_GI_08, BW_80),
++	EHT_GROUP(15, EHT_GI_08, BW_80),
++	EHT_GROUP(16, EHT_GI_08, BW_80),
++
++	EHT_GROUP( 1, EHT_GI_16, BW_80),
++	EHT_GROUP( 2, EHT_GI_16, BW_80),
++	EHT_GROUP( 3, EHT_GI_16, BW_80),
++	EHT_GROUP( 4, EHT_GI_16, BW_80),
++	EHT_GROUP( 5, EHT_GI_16, BW_80),
++	EHT_GROUP( 6, EHT_GI_16, BW_80),
++	EHT_GROUP( 7, EHT_GI_16, BW_80),
++	EHT_GROUP( 8, EHT_GI_16, BW_80),
++	EHT_GROUP( 9, EHT_GI_16, BW_80),
++	EHT_GROUP(10, EHT_GI_16, BW_80),
++	EHT_GROUP(11, EHT_GI_16, BW_80),
++	EHT_GROUP(12, EHT_GI_16, BW_80),
++	EHT_GROUP(13, EHT_GI_16, BW_80),
++	EHT_GROUP(14, EHT_GI_16, BW_80),
++	EHT_GROUP(15, EHT_GI_16, BW_80),
++	EHT_GROUP(16, EHT_GI_16, BW_80),
++
++	EHT_GROUP( 1, EHT_GI_32, BW_80),
++	EHT_GROUP( 2, EHT_GI_32, BW_80),
++	EHT_GROUP( 3, EHT_GI_32, BW_80),
++	EHT_GROUP( 4, EHT_GI_32, BW_80),
++	EHT_GROUP( 5, EHT_GI_32, BW_80),
++	EHT_GROUP( 6, EHT_GI_32, BW_80),
++	EHT_GROUP( 7, EHT_GI_32, BW_80),
++	EHT_GROUP( 8, EHT_GI_32, BW_80),
++	EHT_GROUP( 9, EHT_GI_32, BW_80),
++	EHT_GROUP(10, EHT_GI_32, BW_80),
++	EHT_GROUP(11, EHT_GI_32, BW_80),
++	EHT_GROUP(12, EHT_GI_32, BW_80),
++	EHT_GROUP(13, EHT_GI_32, BW_80),
++	EHT_GROUP(14, EHT_GI_32, BW_80),
++	EHT_GROUP(15, EHT_GI_32, BW_80),
++	EHT_GROUP(16, EHT_GI_32, BW_80),
++
++	EHT_GROUP( 1, EHT_GI_08, BW_160),
++	EHT_GROUP( 2, EHT_GI_08, BW_160),
++	EHT_GROUP( 3, EHT_GI_08, BW_160),
++	EHT_GROUP( 4, EHT_GI_08, BW_160),
++	EHT_GROUP( 5, EHT_GI_08, BW_160),
++	EHT_GROUP( 6, EHT_GI_08, BW_160),
++	EHT_GROUP( 7, EHT_GI_08, BW_160),
++	EHT_GROUP( 8, EHT_GI_08, BW_160),
++	EHT_GROUP( 9, EHT_GI_08, BW_160),
++	EHT_GROUP(10, EHT_GI_08, BW_160),
++	EHT_GROUP(11, EHT_GI_08, BW_160),
++	EHT_GROUP(12, EHT_GI_08, BW_160),
++	EHT_GROUP(13, EHT_GI_08, BW_160),
++	EHT_GROUP(14, EHT_GI_08, BW_160),
++	EHT_GROUP(15, EHT_GI_08, BW_160),
++	EHT_GROUP(16, EHT_GI_08, BW_160),
++
++	EHT_GROUP( 1, EHT_GI_16, BW_160),
++	EHT_GROUP( 2, EHT_GI_16, BW_160),
++	EHT_GROUP( 3, EHT_GI_16, BW_160),
++	EHT_GROUP( 4, EHT_GI_16, BW_160),
++	EHT_GROUP( 5, EHT_GI_16, BW_160),
++	EHT_GROUP( 6, EHT_GI_16, BW_160),
++	EHT_GROUP( 7, EHT_GI_16, BW_160),
++	EHT_GROUP( 8, EHT_GI_16, BW_160),
++	EHT_GROUP( 9, EHT_GI_16, BW_160),
++	EHT_GROUP(10, EHT_GI_16, BW_160),
++	EHT_GROUP(11, EHT_GI_16, BW_160),
++	EHT_GROUP(12, EHT_GI_16, BW_160),
++	EHT_GROUP(13, EHT_GI_16, BW_160),
++	EHT_GROUP(14, EHT_GI_16, BW_160),
++	EHT_GROUP(15, EHT_GI_16, BW_160),
++	EHT_GROUP(16, EHT_GI_16, BW_160),
++
++	EHT_GROUP( 1, EHT_GI_32, BW_160),
++	EHT_GROUP( 2, EHT_GI_32, BW_160),
++	EHT_GROUP( 3, EHT_GI_32, BW_160),
++	EHT_GROUP( 4, EHT_GI_32, BW_160),
++	EHT_GROUP( 5, EHT_GI_32, BW_160),
++	EHT_GROUP( 6, EHT_GI_32, BW_160),
++	EHT_GROUP( 7, EHT_GI_32, BW_160),
++	EHT_GROUP( 8, EHT_GI_32, BW_160),
++	EHT_GROUP( 9, EHT_GI_32, BW_160),
++	EHT_GROUP(10, EHT_GI_32, BW_160),
++	EHT_GROUP(11, EHT_GI_32, BW_160),
++	EHT_GROUP(12, EHT_GI_32, BW_160),
++	EHT_GROUP(13, EHT_GI_32, BW_160),
++	EHT_GROUP(14, EHT_GI_32, BW_160),
++	EHT_GROUP(15, EHT_GI_32, BW_160),
++	EHT_GROUP(16, EHT_GI_32, BW_160),
++
++	EHT_GROUP( 1, EHT_GI_08, BW_320),
++	EHT_GROUP( 2, EHT_GI_08, BW_320),
++	EHT_GROUP( 3, EHT_GI_08, BW_320),
++	EHT_GROUP( 4, EHT_GI_08, BW_320),
++	EHT_GROUP( 5, EHT_GI_08, BW_320),
++	EHT_GROUP( 6, EHT_GI_08, BW_320),
++	EHT_GROUP( 7, EHT_GI_08, BW_320),
++	EHT_GROUP( 8, EHT_GI_08, BW_320),
++	EHT_GROUP( 9, EHT_GI_08, BW_320),
++	EHT_GROUP(10, EHT_GI_08, BW_320),
++	EHT_GROUP(11, EHT_GI_08, BW_320),
++	EHT_GROUP(12, EHT_GI_08, BW_320),
++	EHT_GROUP(13, EHT_GI_08, BW_320),
++	EHT_GROUP(14, EHT_GI_08, BW_320),
++	EHT_GROUP(15, EHT_GI_08, BW_320),
++	EHT_GROUP(16, EHT_GI_08, BW_320),
++
++	EHT_GROUP( 1, EHT_GI_16, BW_320),
++	EHT_GROUP( 2, EHT_GI_16, BW_320),
++	EHT_GROUP( 3, EHT_GI_16, BW_320),
++	EHT_GROUP( 4, EHT_GI_16, BW_320),
++	EHT_GROUP( 5, EHT_GI_16, BW_320),
++	EHT_GROUP( 6, EHT_GI_16, BW_320),
++	EHT_GROUP( 7, EHT_GI_16, BW_320),
++	EHT_GROUP( 8, EHT_GI_16, BW_320),
++	EHT_GROUP( 9, EHT_GI_16, BW_320),
++	EHT_GROUP(10, EHT_GI_16, BW_320),
++	EHT_GROUP(11, EHT_GI_16, BW_320),
++	EHT_GROUP(12, EHT_GI_16, BW_320),
++	EHT_GROUP(13, EHT_GI_16, BW_320),
++	EHT_GROUP(14, EHT_GI_16, BW_320),
++	EHT_GROUP(15, EHT_GI_16, BW_320),
++	EHT_GROUP(16, EHT_GI_16, BW_320),
++
++	EHT_GROUP( 1, EHT_GI_32, BW_320),
++	EHT_GROUP( 2, EHT_GI_32, BW_320),
++	EHT_GROUP( 3, EHT_GI_32, BW_320),
++	EHT_GROUP( 4, EHT_GI_32, BW_320),
++	EHT_GROUP( 5, EHT_GI_32, BW_320),
++	EHT_GROUP( 6, EHT_GI_32, BW_320),
++	EHT_GROUP( 7, EHT_GI_32, BW_320),
++	EHT_GROUP( 8, EHT_GI_32, BW_320),
++	EHT_GROUP( 9, EHT_GI_32, BW_320),
++	EHT_GROUP(10, EHT_GI_32, BW_320),
++	EHT_GROUP(11, EHT_GI_32, BW_320),
++	EHT_GROUP(12, EHT_GI_32, BW_320),
++	EHT_GROUP(13, EHT_GI_32, BW_320),
++	EHT_GROUP(14, EHT_GI_32, BW_320),
++	EHT_GROUP(15, EHT_GI_32, BW_320),
++	EHT_GROUP(16, EHT_GI_32, BW_320),
+ };
+ 
+ static u32
+@@ -422,6 +751,9 @@ static u32 ieee80211_get_rate_duration(struct ieee80211_hw *hw,
+ 	case RATE_INFO_BW_160:
+ 		bw = BW_160;
+ 		break;
++	case RATE_INFO_BW_320:
++		bw = BW_320;
++		break;
+ 	default:
+ 		WARN_ON_ONCE(1);
+ 		return 0;
+@@ -443,11 +775,20 @@ static u32 ieee80211_get_rate_duration(struct ieee80211_hw *hw,
+ 		idx = status->rate_idx;
+ 		group = HE_GROUP_IDX(streams, status->he_gi, bw);
+ 		break;
++	case RX_ENC_EHT:
++		streams = status->nss;
++		idx = status->rate_idx;
++		group = EHT_GROUP_IDX(streams, status->he_gi, bw);
++		break;
+ 	default:
+ 		WARN_ON_ONCE(1);
+ 		return 0;
+ 	}
+ 
++	if (WARN_ON_ONCE((status->encoding != RX_ENC_EHT && streams > 8) ||
++			 (status->encoding == RX_ENC_EHT && streams > 16)))
++		return 0;
++
+ 	if (WARN_ON_ONCE((status->encoding != RX_ENC_HE && streams > 4) ||
+ 			 (status->encoding == RX_ENC_HE && streams > 8)))
+ 		return 0;
+@@ -517,7 +858,9 @@ static bool ieee80211_fill_rate_info(struct ieee80211_hw *hw,
+ 	stat->nss = ri->nss;
+ 	stat->rate_idx = ri->mcs;
+ 
+-	if (ri->flags & RATE_INFO_FLAGS_HE_MCS)
++	if (ri->flags & RATE_INFO_FLAGS_EHT_MCS)
++		stat->encoding = RX_ENC_EHT;
++	else if (ri->flags & RATE_INFO_FLAGS_HE_MCS)
+ 		stat->encoding = RX_ENC_HE;
+ 	else if (ri->flags & RATE_INFO_FLAGS_VHT_MCS)
+ 		stat->encoding = RX_ENC_VHT;
+@@ -529,7 +872,7 @@ static bool ieee80211_fill_rate_info(struct ieee80211_hw *hw,
+ 	if (ri->flags & RATE_INFO_FLAGS_SHORT_GI)
+ 		stat->enc_flags |= RX_ENC_FLAG_SHORT_GI;
+ 
+-	stat->he_gi = ri->he_gi;
++	stat->he_gi = (ri->flags & RATE_INFO_FLAGS_EHT_MCS) ? ri->eht_gi : ri->he_gi;
+ 
+ 	if (stat->encoding != RX_ENC_LEGACY)
+ 		return true;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0034-mtk-mac80211-set-eht_support-to-false-when-AP-is-not.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0034-mtk-mac80211-set-eht_support-to-false-when-AP-is-not.patch
deleted file mode 100644
index ecdc233..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0034-mtk-mac80211-set-eht_support-to-false-when-AP-is-not.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From 2531350e6d261ef54adb85ce59c5012dc4d5db90 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Wed, 25 Oct 2023 13:37:00 +0800
-Subject: [PATCH 34/61] mtk: mac80211: set eht_support to false when AP is not
- in EHT mode
-
-Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
----
- net/mac80211/cfg.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
-index cb91223..215b5d2 100644
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -1384,6 +1384,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
- 		link_conf->eht_su_beamformer = false;
- 		link_conf->eht_su_beamformee = false;
- 		link_conf->eht_mu_beamformer = false;
-+		link_conf->eht_support = false;
- 	}
- 
- 	if (sdata->vif.type == NL80211_IFTYPE_AP &&
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0035-mtk-mac80211-Add-cert-mode-to-disable-ba-timeout.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0035-mtk-mac80211-Add-cert-mode-to-disable-ba-timeout.patch
deleted file mode 100644
index 9d76079..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0035-mtk-mac80211-Add-cert-mode-to-disable-ba-timeout.patch
+++ /dev/null
@@ -1,124 +0,0 @@
-From ba967d748120a6681f3e0f106dfaf3ad882034c0 Mon Sep 17 00:00:00 2001
-From: "Allen.Ye" <allen.ye@mediatek.com>
-Date: Thu, 9 Nov 2023 11:37:37 +0800
-Subject: [PATCH 35/61] mtk: mac80211: Add cert mode to disable ba timeout
-
-Add a switch of certification mode in debugfs as cert_mode. In the case
-we use it to disable BA timeout from STA to prevent crashing STA.
-
-Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
-
-Move the variable 'cert_mode' from ieee80211_local to ieee80211_hw
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- include/net/mac80211.h |  6 ++++++
- net/mac80211/agg-tx.c  |  5 ++++-
- net/mac80211/debugfs.c | 49 ++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 59 insertions(+), 1 deletion(-)
-
-diff --git a/include/net/mac80211.h b/include/net/mac80211.h
-index 860ad6e..89bba19 100644
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -3028,8 +3028,14 @@ struct ieee80211_hw {
- 	u32 max_mtu;
- 	const s8 *tx_power_levels;
- 	u8 max_txpwr_levels_idx;
-+	bool cert_mode;
- };
- 
-+static inline bool ieee80211_is_cert_mode(struct ieee80211_hw *hw)
-+{
-+	return hw->cert_mode;
-+}
-+
- static inline bool _ieee80211_hw_check(struct ieee80211_hw *hw,
- 				       enum ieee80211_hw_flags flg)
- {
-diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
-index 8480b64..7117576 100644
---- a/net/mac80211/agg-tx.c
-+++ b/net/mac80211/agg-tx.c
-@@ -1090,7 +1090,10 @@ next:
- 		tid_tx->timeout =
- 			le16_to_cpu(mgmt->u.action.u.addba_resp.timeout);
- 
--		if (tid_tx->timeout) {
-+		/* In the case of certification env, testbed STA cannot accept frequent DelBA.
-+		 * Therefore, we remove the session timer check here to avoid crashing testbed STA.
-+		 */
-+		if (tid_tx->timeout && !ieee80211_is_cert_mode(&local->hw)) {
- 			mod_timer(&tid_tx->session_timer,
- 				  TU_TO_EXP_TIME(tid_tx->timeout));
- 			tid_tx->last_tx = jiffies;
-diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
-index f9c5ed8..d655a19 100644
---- a/net/mac80211/debugfs.c
-+++ b/net/mac80211/debugfs.c
-@@ -450,6 +450,54 @@ static const struct file_operations reset_ops = {
- };
- #endif
- 
-+static ssize_t cert_mode_read(struct file *file,
-+			      char __user *user_buf,
-+			      size_t count,
-+			      loff_t *ppos)
-+{
-+	struct ieee80211_local *local = file->private_data;
-+	char buf[32];
-+	int len = 0;
-+
-+	len = scnprintf(buf, sizeof(buf), "cert_mode: %d\n",
-+			local->hw.cert_mode);
-+
-+	return simple_read_from_buffer(user_buf, count, ppos,
-+				       buf, len);
-+}
-+
-+static ssize_t cert_mode_write(struct file *file,
-+			       const char __user *user_buf,
-+			       size_t count,
-+			       loff_t *ppos)
-+{
-+	struct ieee80211_local *local = file->private_data;
-+	char buf[16];
-+
-+	if (count >= sizeof(buf))
-+		return -EINVAL;
-+
-+	if (copy_from_user(buf, user_buf, count))
-+		return -EFAULT;
-+
-+	if (count && buf[count - 1] == '\n')
-+		buf[count - 1] = '\0';
-+	else
-+		buf[count] = '\0';
-+
-+	if (kstrtobool(buf, &local->hw.cert_mode))
-+		return -EINVAL;
-+
-+	return count;
-+}
-+
-+static const struct file_operations cert_mode_ops = {
-+	.write = cert_mode_write,
-+	.read = cert_mode_read,
-+	.open = simple_open,
-+	.llseek = noop_llseek,
-+};
-+
- static const char *hw_flag_names[] = {
- #define FLAG(F)	[IEEE80211_HW_##F] = #F
- 	FLAG(HAS_RATE_CONTROL),
-@@ -684,6 +732,7 @@ void debugfs_hw_add(struct ieee80211_local *local)
- 	debugfs_create_u32("aql_threshold", 0600,
- 			   phyd, &local->aql_threshold);
- 
-+	DEBUGFS_ADD_MODE(cert_mode, 0644);
- 	statsd = debugfs_create_dir("statistics", phyd);
- 
- #ifdef CPTCFG_MAC80211_DEBUG_COUNTERS
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0035-mtk-mac80211-add-send-bar-action-when-recieve-addba-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0035-mtk-mac80211-add-send-bar-action-when-recieve-addba-.patch
new file mode 100644
index 0000000..83cd829
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0035-mtk-mac80211-add-send-bar-action-when-recieve-addba-.patch
@@ -0,0 +1,28 @@
+From b277487ebbe327c928e6ed30733ee5adebf9a7b2 Mon Sep 17 00:00:00 2001
+From: ye he <ye.he@mediatek.com>
+Date: Wed, 22 Feb 2023 16:09:32 +0800
+Subject: [PATCH 35/89] mtk: mac80211: add send bar action when recieve addba
+ rsp
+
+Signed-off-by: ye he <ye.he@mediatek.com>
+---
+ net/mac80211/agg-tx.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
+index de1b2b2..0abc77b 100644
+--- a/net/mac80211/agg-tx.c
++++ b/net/mac80211/agg-tx.c
+@@ -1082,7 +1082,8 @@ next:
+ 
+ 		tid_tx->buf_size = buf_size;
+ 		tid_tx->amsdu = amsdu;
+-
++		ieee80211_send_bar(&sta->sdata->vif, sta->sta.addr,
++					   tid, 0);
+ 		if (test_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state))
+ 			ieee80211_agg_tx_operational(local, sta, tid);
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0036-backports-update-kernel-version-check-for-eth_hw_add.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0036-backports-update-kernel-version-check-for-eth_hw_add.patch
deleted file mode 100644
index 01f9aaf..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0036-backports-update-kernel-version-check-for-eth_hw_add.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From 0bf57d6aacff7859372d546ef491f2722098bc0c Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Mon, 27 Nov 2023 16:39:36 +0800
-Subject: [PATCH 36/61] backports: update kernel version check for
- eth_hw_addr_set()
-
-Kernel v5.4.260 has added this API, so update kernel version check in
-backports include.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- backport-include/linux/etherdevice.h | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/backport-include/linux/etherdevice.h b/backport-include/linux/etherdevice.h
-index 51a7d6d..ecc3bc2 100644
---- a/backport-include/linux/etherdevice.h
-+++ b/backport-include/linux/etherdevice.h
-@@ -39,7 +39,7 @@ static inline void u64_to_ether_addr(u64 u, u8 *addr)
- }
- #endif /* LINUX_VERSION_IS_LESS(4,11,0) */
- 
--#if LINUX_VERSION_IS_LESS(5,15,0)
-+#if LINUX_VERSION_IS_LESS(5,4,260)
- /**
-  * eth_hw_addr_set - Assign Ethernet address to a net_device
-  * @dev: pointer to net_device structure
-@@ -51,7 +51,7 @@ static inline void eth_hw_addr_set(struct net_device *dev, const u8 *addr)
- {
- 	ether_addr_copy(dev->dev_addr, addr);
- }
--#endif /* LINUX_VERSION_IS_LESS(5,15,0) */
-+#endif /* LINUX_VERSION_IS_LESS(5,4,260) */
- 
- #if LINUX_VERSION_IS_LESS(5,16,0)
- static inline int backport_device_get_mac_address(struct device *dev, char *addr)
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0036-mtk-mac80211-inrease-beacon-loss-count.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0036-mtk-mac80211-inrease-beacon-loss-count.patch
new file mode 100644
index 0000000..4bee695
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0036-mtk-mac80211-inrease-beacon-loss-count.patch
@@ -0,0 +1,33 @@
+From 3116b6dcf3eeace943d3775658eb81dca7f6053a Mon Sep 17 00:00:00 2001
+From: Amit Khatri <amit.khatri@mediatek.com>
+Date: Thu, 6 Apr 2023 21:37:33 +0800
+Subject: [PATCH 36/89] mtk: mac80211: inrease beacon loss count
+
+as per eagle code beacone loss time out is
+4 seconds.
+in 2G connection getting beacon loss logs in routed client
+scenario.
+
+so increasing beacon loss count from 7 to 20
+
+Signed-off-by: Amit Khatri <amit.khatri@mediatek.com>
+---
+ net/mac80211/mlme.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index bf8a532..9c83b96 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -66,7 +66,7 @@ MODULE_PARM_DESC(max_probe_tries,
+  * probe on beacon miss before declaring the connection lost
+  * default to what we want.
+  */
+-static int beacon_loss_count = 7;
++static int beacon_loss_count = 20;
+ module_param(beacon_loss_count, int, 0644);
+ MODULE_PARM_DESC(beacon_loss_count,
+ 		 "Number of beacon intervals before we decide beacon was lost.");
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0037-mac80211-mtk-ACS-channel-time-is-reset-by-ch_restore.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0037-mac80211-mtk-ACS-channel-time-is-reset-by-ch_restore.patch
deleted file mode 100644
index 806bfd5..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0037-mac80211-mtk-ACS-channel-time-is-reset-by-ch_restore.patch
+++ /dev/null
@@ -1,66 +0,0 @@
-From 9c1106eb80f31723780a6eb098b5c146a33d45a2 Mon Sep 17 00:00:00 2001
-From: "fancy.liu" <fancy.liu@mediatek.com>
-Date: Wed, 29 Nov 2023 13:51:13 +0800
-Subject: [PATCH 37/61] mac80211: mtk: ACS channel time is reset by ch_restore
-
-Issue:
-There's a chance that the channel time for duty channel is zero in ACS
-scan.
-
-Root cause:
-The chan_stat may be reset when restore to duty channel.
-Mac80211 will notify to hostapd when scan done and then restore to duty
-channel.
-And mt76 will clear scan flag after restore done.
-If hostapd get the chan_stat before channel_restore, will get the
-correct channel time;
-If hostapd get the chan_stat after channel_restore, will get zero
-channel time;
-
-Solution:
-When channel switch, will check the mac80211 scan state but not the mt76 scan flag.
-Mac80211 scan state will be set in scanning, and will be reset after
-scan done and before restore to duty channel.
-
-Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
----
- include/net/mac80211.h | 7 +++++++
- net/mac80211/util.c    | 9 +++++++++
- 2 files changed, 16 insertions(+)
-
-diff --git a/include/net/mac80211.h b/include/net/mac80211.h
-index 89bba19..d7b1f9d 100644
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -7665,4 +7665,11 @@ int ieee80211_emulate_switch_vif_chanctx(struct ieee80211_hw *hw,
- 					 int n_vifs,
- 					 enum ieee80211_chanctx_switch_mode mode);
- 
-+/**
-+ * ieee80211_get_scanning - get scanning bitmask
-+ *
-+ * @hw: pointer as obtained from ieee80211_alloc_hw()
-+ */
-+unsigned long ieee80211_get_scanning(struct ieee80211_hw *hw);
-+
- #endif /* MAC80211_H */
-diff --git a/net/mac80211/util.c b/net/mac80211/util.c
-index dd06bd2..55f1566 100644
---- a/net/mac80211/util.c
-+++ b/net/mac80211/util.c
-@@ -4340,3 +4340,12 @@ ieee80211_min_bw_limit_from_chandef(struct cfg80211_chan_def *chandef)
- 		return IEEE80211_CONN_BW_LIMIT_20;
- 	}
- }
-+
-+unsigned long ieee80211_get_scanning(struct ieee80211_hw *hw)
-+{
-+	struct ieee80211_local *local = hw_to_local(hw);
-+
-+	return local->scanning;
-+}
-+EXPORT_SYMBOL(ieee80211_get_scanning);
-+
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0037-mtk-cfg80211-add-support-for-updating-background-cha.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0037-mtk-cfg80211-add-support-for-updating-background-cha.patch
new file mode 100644
index 0000000..c94d277
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0037-mtk-cfg80211-add-support-for-updating-background-cha.patch
@@ -0,0 +1,88 @@
+From 00e391d6cfcebf553c4f9378fe56d17b48e7f9b2 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 5 Jul 2023 09:49:02 +0800
+Subject: [PATCH 37/89] mtk: cfg80211: add support for updating background
+ channel
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ include/net/cfg80211.h       | 14 ++++++++++++++
+ include/uapi/linux/nl80211.h |  6 ++++++
+ net/wireless/mlme.c          | 12 ++++++++++++
+ 3 files changed, 32 insertions(+)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index 16a9a24..993b9a1 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -8789,6 +8789,20 @@ void cfg80211_sta_update_dfs_state(struct wireless_dev *wdev,
+ 				   const struct cfg80211_chan_def *csa_chandef,
+ 				   bool associated);
+ 
++/**
++ * cfg80211_background_radar_update_channel - notify background chandef has been updated
++ * @wiphy: the wiphy
++ * @chandef: the updated chandef
++ * @expand: whether or not the operating channel should expand its width
++ * after offchan CAC
++ *
++ * Update the background chandef based on driver's decision, and notify the userspace
++ * that the current channel of background chain should be updated.
++ */
++void cfg80211_background_radar_update_channel(struct wiphy *wiphy,
++					      const struct cfg80211_chan_def *chandef,
++					      bool expand);
++
+ /**
+  * cfg80211_background_cac_abort - Channel Availability Check offchan abort event
+  * @wiphy: the wiphy
+diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
+index 622fa71..d425c79 100644
+--- a/include/uapi/linux/nl80211.h
++++ b/include/uapi/linux/nl80211.h
+@@ -6843,6 +6843,10 @@ enum nl80211_smps_mode {
+  *	applicable for ETSI dfs domain where pre-CAC is valid for ever.
+  * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
+  *	should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
++ * @NL80211_RADAR_BACKGROUND_CHAN_UPDATE: background channel is updated by the
++ *	driver.
++ * @NL80211_RADAR_BACKGROUND_CHAN_EXPAND: background channel is updated by the
++ *	driver and required to expand main operating channel.
+  * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
+  *	when receiving CSA/assoc resp
+  * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
+@@ -6855,6 +6859,8 @@ enum nl80211_radar_event {
+ 	NL80211_RADAR_NOP_FINISHED,
+ 	NL80211_RADAR_PRE_CAC_EXPIRED,
+ 	NL80211_RADAR_CAC_STARTED,
++	NL80211_RADAR_BACKGROUND_CHAN_UPDATE,
++	NL80211_RADAR_BACKGROUND_CHAN_EXPAND,
+ 	NL80211_RADAR_STA_CAC_SKIPPED,
+ 	NL80211_RADAR_STA_CAC_EXPIRED,
+ };
+diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
+index 3b62f9a..cddee2f 100644
+--- a/net/wireless/mlme.c
++++ b/net/wireless/mlme.c
+@@ -1283,6 +1283,18 @@ cfg80211_start_background_radar_detection(struct cfg80211_registered_device *rde
+ 	return 0;
+ }
+ 
++void cfg80211_background_radar_update_channel(struct wiphy *wiphy,
++					      const struct cfg80211_chan_def *chandef,
++					      bool expand)
++{
++	enum nl80211_radar_event event;
++
++	event = expand ? NL80211_RADAR_BACKGROUND_CHAN_EXPAND :
++			 NL80211_RADAR_BACKGROUND_CHAN_UPDATE;
++	nl80211_radar_notify(wiphy_to_rdev(wiphy), chandef, event, NULL, GFP_ATOMIC);
++}
++EXPORT_SYMBOL(cfg80211_background_radar_update_channel);
++
+ void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev)
+ {
+ 	struct wiphy *wiphy = wdev->wiphy;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0038-mtk-mac80211-Allow-STA-interface-to-set-TX-queue-par.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0038-mtk-mac80211-Allow-STA-interface-to-set-TX-queue-par.patch
new file mode 100644
index 0000000..e8190ba
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0038-mtk-mac80211-Allow-STA-interface-to-set-TX-queue-par.patch
@@ -0,0 +1,26 @@
+From 75f507486da757510fddd49289d783c6529ab9fc Mon Sep 17 00:00:00 2001
+From: Michael Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 7 Jul 2023 17:17:30 +0800
+Subject: [PATCH 38/89] mtk: mac80211: Allow STA interface to set TX queue
+ parameters
+
+Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+---
+ net/wireless/nl80211.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index 6b03037..d4e7ed8 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -3688,6 +3688,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
+ 		}
+ 
+ 		if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
++		    netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+ 		    netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
+ 			result = -EINVAL;
+ 			goto out;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0038-mtk-mac80211-Fix-SMPS-action-frame-cap-check.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0038-mtk-mac80211-Fix-SMPS-action-frame-cap-check.patch
deleted file mode 100644
index 93a9f79..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0038-mtk-mac80211-Fix-SMPS-action-frame-cap-check.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From 8a8535abf02e5cb6f0dcaf924e33183a1a1fa410 Mon Sep 17 00:00:00 2001
-From: "Allen.Ye" <allen.ye@mediatek.com>
-Date: Thu, 30 Nov 2023 14:01:29 +0800
-Subject: [PATCH 38/61] mtk: mac80211: Fix SMPS action frame cap check
-
-Fix SMPS action frame cap check.
-Due to 6G band doesn't have HT cap, we change cap check into each action
-frame section.
-
-Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
----
- net/mac80211/rx.c | 8 +++++---
- 1 file changed, 5 insertions(+), 3 deletions(-)
-
-diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
-index 06725f3..1d273ef 100644
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -3528,9 +3528,6 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
- 
- 	switch (mgmt->u.action.category) {
- 	case WLAN_CATEGORY_HT:
--		/* reject HT action frames from stations not supporting HT */
--		if (!rx->link_sta->pub->ht_cap.ht_supported)
--			goto invalid;
- 
- 		if (sdata->vif.type != NL80211_IFTYPE_STATION &&
- 		    sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
-@@ -3549,6 +3546,11 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
- 			enum ieee80211_smps_mode smps_mode;
- 			struct sta_opmode_info sta_opmode = {};
- 
-+			if (rx->link_sta->pub->he_cap.has_he &&
-+			    !(rx->link_sta->pub->he_cap.he_cap_elem.mac_cap_info[5] &
-+			    IEEE80211_HE_MAC_CAP5_HE_DYNAMIC_SM_PS))
-+				goto invalid;
-+
- 			if (sdata->vif.type != NL80211_IFTYPE_AP &&
- 			    sdata->vif.type != NL80211_IFTYPE_AP_VLAN)
- 				goto handled;
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0039-mtk-mac80211-allow-multiple-links-for-STA-vif.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0039-mtk-mac80211-allow-multiple-links-for-STA-vif.patch
deleted file mode 100644
index 2161fef..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0039-mtk-mac80211-allow-multiple-links-for-STA-vif.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From 9de692d990d4d83e957ea89e3fc23367f696bbc3 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 19 Oct 2023 00:27:15 +0800
-Subject: [PATCH 39/61] mtk: mac80211: allow multiple links for STA vif
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- net/mac80211/link.c | 3 ---
- 1 file changed, 3 deletions(-)
-
-diff --git a/net/mac80211/link.c b/net/mac80211/link.c
-index 43f9672..a33a845 100644
---- a/net/mac80211/link.c
-+++ b/net/mac80211/link.c
-@@ -168,10 +168,7 @@ static void ieee80211_set_vif_links_bitmaps(struct ieee80211_sub_if_data *sdata,
- 		WARN_ON(dormant_links);
- 		break;
- 	case NL80211_IFTYPE_STATION:
--		if (sdata->vif.active_links)
--			break;
- 		sdata->vif.active_links = valid_links & ~dormant_links;
--		WARN_ON(hweight16(sdata->vif.active_links) > 1);
- 		break;
- 	default:
- 		WARN_ON(1);
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0039-mtk-mac80211-export-ieee80211_tpt_led_trig_tx-rx-for.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0039-mtk-mac80211-export-ieee80211_tpt_led_trig_tx-rx-for.patch
new file mode 100644
index 0000000..e9deb46
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0039-mtk-mac80211-export-ieee80211_tpt_led_trig_tx-rx-for.patch
@@ -0,0 +1,150 @@
+From fbc38ce3f33a8171a197d34fc5d6c57c1209c81a Mon Sep 17 00:00:00 2001
+From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Date: Fri, 23 Jun 2023 05:53:50 +0800
+Subject: [PATCH 39/89] mtk: mac80211: export ieee80211_tpt_led_trig_tx/rx for
+ driver
+
+Whenever the H/W path is enabled and traffic is in the binding state,
+mac80211 is not aware of the traffic. Consequently, the LED does not
+blink for that reason.
+
+The ieee80211_tpt_led_trig_tx/rx functions are exported for the driver
+so that we can report the tx and rx bytes from the driver when
+the H/W path is being used.
+
+Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+---
+ include/net/mac80211.h | 17 +++++++++++++++++
+ net/mac80211/led.c     | 16 ++++++++++++++++
+ net/mac80211/led.h     | 17 -----------------
+ net/mac80211/rx.c      |  2 +-
+ net/mac80211/tx.c      |  4 ++--
+ 5 files changed, 36 insertions(+), 20 deletions(-)
+
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 6449af9..215d499 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -4923,6 +4923,8 @@ __ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
+ 				   unsigned int flags,
+ 				   const struct ieee80211_tpt_blink *blink_table,
+ 				   unsigned int blink_table_len);
++void __ieee80211_tpt_led_trig_tx(struct ieee80211_hw *hw, int bytes);
++void __ieee80211_tpt_led_trig_rx(struct ieee80211_hw *hw, int bytes);
+ #endif
+ /**
+  * ieee80211_get_tx_led_name - get name of TX LED
+@@ -5033,6 +5035,21 @@ ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw, unsigned int flags,
+ #endif
+ }
+ 
++static inline void
++ieee80211_tpt_led_trig_tx(struct ieee80211_hw *hw, int bytes)
++{
++#ifdef CPTCFG_MAC80211_LEDS
++	__ieee80211_tpt_led_trig_tx(hw, bytes);
++#endif
++}
++
++static inline void
++ieee80211_tpt_led_trig_rx(struct ieee80211_hw *hw, int bytes)
++{
++#ifdef CPTCFG_MAC80211_LEDS
++	__ieee80211_tpt_led_trig_rx(hw, bytes);
++#endif
++}
+ /**
+  * ieee80211_unregister_hw - Unregister a hardware device
+  *
+diff --git a/net/mac80211/led.c b/net/mac80211/led.c
+index b992430..3109501 100644
+--- a/net/mac80211/led.c
++++ b/net/mac80211/led.c
+@@ -364,6 +364,22 @@ __ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
+ }
+ EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
+ 
++void __ieee80211_tpt_led_trig_tx(struct ieee80211_hw *hw, int bytes)
++{
++	struct ieee80211_local *local = hw_to_local(hw);
++	if (atomic_read(&local->tpt_led_active))
++		local->tpt_led_trigger->tx_bytes += bytes;
++}
++EXPORT_SYMBOL(__ieee80211_tpt_led_trig_tx);
++
++void __ieee80211_tpt_led_trig_rx(struct ieee80211_hw *hw, int bytes)
++{
++	struct ieee80211_local *local = hw_to_local(hw);
++	if (atomic_read(&local->tpt_led_active))
++		local->tpt_led_trigger->rx_bytes += bytes;
++}
++EXPORT_SYMBOL(__ieee80211_tpt_led_trig_rx);
++
+ static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
+ {
+ 	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+diff --git a/net/mac80211/led.h b/net/mac80211/led.h
+index 59f5a83..f381790 100644
+--- a/net/mac80211/led.h
++++ b/net/mac80211/led.h
+@@ -65,22 +65,5 @@ static inline void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local,
+ 					      unsigned int types_off)
+ {
+ }
+-#endif
+ 
+-static inline void
+-ieee80211_tpt_led_trig_tx(struct ieee80211_local *local, int bytes)
+-{
+-#ifdef CPTCFG_MAC80211_LEDS
+-	if (atomic_read(&local->tpt_led_active))
+-		local->tpt_led_trigger->tx_bytes += bytes;
+ #endif
+-}
+-
+-static inline void
+-ieee80211_tpt_led_trig_rx(struct ieee80211_local *local, int bytes)
+-{
+-#ifdef CPTCFG_MAC80211_LEDS
+-	if (atomic_read(&local->tpt_led_active))
+-		local->tpt_led_trigger->rx_bytes += bytes;
+-#endif
+-}
+diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
+index 5c7fc70..733f365 100644
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -5477,7 +5477,7 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
+ 	if (skb) {
+ 		if ((status->flag & RX_FLAG_8023) ||
+ 			ieee80211_is_data_present(hdr->frame_control))
+-			ieee80211_tpt_led_trig_rx(local, skb->len);
++			ieee80211_tpt_led_trig_rx(&local->hw, skb->len);
+ 
+ 		if (status->flag & RX_FLAG_8023)
+ 			__ieee80211_rx_handle_8023(hw, pubsta, skb, list);
+diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
+index 65f9c26..02e7ae6 100644
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -4344,7 +4344,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
+ 	len = 0;
+  out:
+ 	if (len)
+-		ieee80211_tpt_led_trig_tx(local, len);
++		ieee80211_tpt_led_trig_tx(&local->hw, len);
+ 	rcu_read_unlock();
+ }
+ 
+@@ -4675,7 +4675,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
+ 	sta->deflink.tx_stats.packets[queue] += skbs;
+ 	sta->deflink.tx_stats.bytes[queue] += len;
+ 
+-	ieee80211_tpt_led_trig_tx(local, len);
++	ieee80211_tpt_led_trig_tx(&local->hw, len);
+ 
+ 	ieee80211_tx_8023(sdata, skb, sta, false);
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0040-mtk-mac80211-add-packet-count-input-for-dev_sw_netst.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0040-mtk-mac80211-add-packet-count-input-for-dev_sw_netst.patch
new file mode 100644
index 0000000..b6591a5
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0040-mtk-mac80211-add-packet-count-input-for-dev_sw_netst.patch
@@ -0,0 +1,136 @@
+From be1bbf4ad51cf99310f9d17206afd22975d13d2f Mon Sep 17 00:00:00 2001
+From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Date: Tue, 22 Aug 2023 05:02:53 +0800
+Subject: [PATCH 40/89] mtk: mac80211: add packet count input for
+ dev_sw_netstat_rx_add
+
+---
+ backport-include/linux/netdevice.h                   | 12 ++++++++----
+ drivers/net/usb/qmi_wwan.c                           |  2 +-
+ .../net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c |  2 +-
+ .../net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c |  2 +-
+ net/mac80211/rx.c                                    |  8 ++++----
+ 5 files changed, 15 insertions(+), 11 deletions(-)
+
+diff --git a/backport-include/linux/netdevice.h b/backport-include/linux/netdevice.h
+index bd35ec7..e638f31 100644
+--- a/backport-include/linux/netdevice.h
++++ b/backport-include/linux/netdevice.h
+@@ -46,13 +46,15 @@ void dev_fetch_sw_netstats(struct rtnl_link_stats64 *s,
+ #define netif_rx_any_context LINUX_BACKPORT(netif_rx_any_context)
+ int netif_rx_any_context(struct sk_buff *skb);
+ 
+-static inline void dev_sw_netstats_rx_add(struct net_device *dev, unsigned int len)
++static inline void dev_sw_netstats_rx_add(struct net_device *dev,
++					  unsigned int packets,
++					  unsigned int len)
+ {
+ 	struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
+ 
+ 	u64_stats_update_begin(&tstats->syncp);
+ 	tstats->rx_bytes += len;
+-	tstats->rx_packets++;
++	tstats->rx_packets += packets;
+ 	u64_stats_update_end(&tstats->syncp);
+ }
+ 
+@@ -74,13 +76,15 @@ static inline void dev_sw_netstats_tx_add(struct net_device *dev,
+ 
+ #if LINUX_VERSION_IS_LESS(5,10,0)
+ #define dev_sw_netstats_rx_add LINUX_BACKPORT(dev_sw_netstats_rx_add)
+-static inline void dev_sw_netstats_rx_add(struct net_device *dev, unsigned int len)
++static inline void dev_sw_netstats_rx_add(struct net_device *dev,
++					  unsigned int packets,
++					  unsigned int len)
+ {
+ 	struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
+ 
+ 	u64_stats_update_begin(&tstats->syncp);
+ 	tstats->rx_bytes += len;
+-	tstats->rx_packets++;
++	tstats->rx_packets += packets;
+ 	u64_stats_update_end(&tstats->syncp);
+ }
+ #endif /* < 5.10 */
+diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
+index e656f73..768d9d8 100644
+--- a/drivers/net/usb/qmi_wwan.c
++++ b/drivers/net/usb/qmi_wwan.c
+@@ -210,7 +210,7 @@ static int qmimux_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+ 			net->stats.rx_errors++;
+ 			return 0;
+ 		} else {
+-			dev_sw_netstats_rx_add(net, pkt_len);
++			dev_sw_netstats_rx_add(net, 1, pkt_len);
+ 		}
+ 
+ skip:
+diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
+index c1a53e1..01ff00f 100644
+--- a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
++++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
+@@ -756,7 +756,7 @@ static int qtnf_pcie_pearl_rx_poll(struct napi_struct *napi, int budget)
+ 			skb_put(skb, psize);
+ 			ndev = qtnf_classify_skb(bus, skb);
+ 			if (likely(ndev)) {
+-				dev_sw_netstats_rx_add(ndev, skb->len);
++				dev_sw_netstats_rx_add(ndev, 1, skb->len);
+ 				skb->protocol = eth_type_trans(skb, ndev);
+ 				napi_gro_receive(napi, skb);
+ 			} else {
+diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
+index ef5c069..8136745 100644
+--- a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
++++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
+@@ -662,7 +662,7 @@ static int qtnf_topaz_rx_poll(struct napi_struct *napi, int budget)
+ 			skb_put(skb, psize);
+ 			ndev = qtnf_classify_skb(bus, skb);
+ 			if (likely(ndev)) {
+-				dev_sw_netstats_rx_add(ndev, skb->len);
++				dev_sw_netstats_rx_add(ndev, 1, skb->len);
+ 				skb->protocol = eth_type_trans(skb, ndev);
+ 				netif_receive_skb(skb);
+ 			} else {
+diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
+index 733f365..334c246 100644
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -863,7 +863,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
+ 
+ 			if (skb) {
+ 				skb->dev = sdata->dev;
+-				dev_sw_netstats_rx_add(skb->dev, skb->len);
++				dev_sw_netstats_rx_add(skb->dev, 1, skb->len);
+ 				netif_receive_skb(skb);
+ 			}
+ 		}
+@@ -2672,7 +2672,7 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
+ 	skb = rx->skb;
+ 	xmit_skb = NULL;
+ 
+-	dev_sw_netstats_rx_add(dev, skb->len);
++	dev_sw_netstats_rx_add(dev, 1, skb->len);
+ 
+ 	if (rx->sta) {
+ 		/* The seqno index has the same property as needed
+@@ -4119,7 +4119,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
+ 		}
+ 
+ 		prev_dev = sdata->dev;
+-		dev_sw_netstats_rx_add(sdata->dev, skb->len);
++		dev_sw_netstats_rx_add(sdata->dev, 1, skb->len);
+ 	}
+ 
+ 	if (prev_dev) {
+@@ -4827,7 +4827,7 @@ static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,
+ 
+ 	skb->dev = fast_rx->dev;
+ 
+-	dev_sw_netstats_rx_add(fast_rx->dev, skb->len);
++	dev_sw_netstats_rx_add(fast_rx->dev, 1, skb->len);
+ 
+ 	/* The seqno index has the same property as needed
+ 	 * for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0040-mtk-mac80211-increase-association-timeout-time.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0040-mtk-mac80211-increase-association-timeout-time.patch
deleted file mode 100644
index eb85ebc..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0040-mtk-mac80211-increase-association-timeout-time.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From 48d97cc83f3098481fbc2a6d38da75e94703708f Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 19 Oct 2023 00:35:11 +0800
-Subject: [PATCH 40/61] mtk: mac80211: increase association timeout time
-
-Prevent from sending multiple association requests while AP is already
-hanlding the request.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- net/mac80211/mlme.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
-index a27682e..84ea805 100644
---- a/net/mac80211/mlme.c
-+++ b/net/mac80211/mlme.c
-@@ -7202,7 +7202,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
- 			 */
- 			if (status_acked) {
- 				ifmgd->assoc_data->timeout =
--					jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT;
-+					jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT * 4;
- 				run_again(sdata, ifmgd->assoc_data->timeout);
- 			} else {
- 				ifmgd->assoc_data->timeout = jiffies - 1;
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0041-mtk-mac80211-add-per-bss-flag-to-support-vendors-cou.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0041-mtk-mac80211-add-per-bss-flag-to-support-vendors-cou.patch
new file mode 100644
index 0000000..4557a06
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0041-mtk-mac80211-add-per-bss-flag-to-support-vendors-cou.patch
@@ -0,0 +1,91 @@
+From 9cb21d47008d6fd6f0aee1cef7c5c92a03074f4a Mon Sep 17 00:00:00 2001
+From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Date: Wed, 16 Aug 2023 07:23:34 +0800
+Subject: [PATCH 41/89] mtk: mac80211: add per-bss flag to support vendors
+ counter
+
+---
+ include/uapi/linux/nl80211.h |  1 +
+ net/mac80211/rx.c            |  8 ++++++--
+ net/mac80211/tx.c            | 13 ++++++++++---
+ 3 files changed, 17 insertions(+), 5 deletions(-)
+
+diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
+index d425c79..257528d 100644
+--- a/include/uapi/linux/nl80211.h
++++ b/include/uapi/linux/nl80211.h
+@@ -6641,6 +6641,7 @@ enum nl80211_ext_feature_index {
+ 	NL80211_EXT_FEATURE_OWE_OFFLOAD_AP,
+ 	NL80211_EXT_FEATURE_DFS_CONCURRENT,
+ 	NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT,
++	NL80211_EXT_FEATURE_STAS_COUNT,
+ 
+ 	/* add new features before the definition below */
+ 	NUM_NL80211_EXT_FEATURES,
+diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
+index 334c246..78e9c6e 100644
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -2672,7 +2672,9 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
+ 	skb = rx->skb;
+ 	xmit_skb = NULL;
+ 
+-	dev_sw_netstats_rx_add(dev, 1, skb->len);
++	if (!wiphy_ext_feature_isset(sdata->local->hw.wiphy,
++	    NL80211_EXT_FEATURE_STAS_COUNT) || !rx->sta)
++		dev_sw_netstats_rx_add(dev, 1, skb->len);
+ 
+ 	if (rx->sta) {
+ 		/* The seqno index has the same property as needed
+@@ -4827,7 +4829,9 @@ static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,
+ 
+ 	skb->dev = fast_rx->dev;
+ 
+-	dev_sw_netstats_rx_add(fast_rx->dev, 1, skb->len);
++	if (!wiphy_ext_feature_isset(sta->local->hw.wiphy,
++	    NL80211_EXT_FEATURE_STAS_COUNT))
++		dev_sw_netstats_rx_add(fast_rx->dev, 1, skb->len);
+ 
+ 	/* The seqno index has the same property as needed
+ 	 * for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
+diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
+index 02e7ae6..3ac51a2 100644
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -3562,7 +3562,9 @@ ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata,
+ 	if (key)
+ 		info->control.hw_key = &key->conf;
+ 
+-	dev_sw_netstats_tx_add(skb->dev, 1, skb->len);
++	if (!wiphy_ext_feature_isset(sta->local->hw.wiphy,
++	    NL80211_EXT_FEATURE_STAS_COUNT))
++		dev_sw_netstats_tx_add(skb->dev, 1, skb->len);
+ 
+ 	if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
+ 		tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+@@ -4334,7 +4336,9 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
+ 			goto out;
+ 		}
+ 
+-		dev_sw_netstats_tx_add(dev, 1, skb->len);
++		if (!wiphy_ext_feature_isset(sdata->local->hw.wiphy,
++		    NL80211_EXT_FEATURE_STAS_COUNT) || !sta)
++			dev_sw_netstats_tx_add(dev, 1, skb->len);
+ 
+ 		ieee80211_xmit(sdata, sta, skb);
+ 	}
+@@ -4671,7 +4675,10 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
+ 			info->status_data_idr = 1;
+ 	}
+ 
+-	dev_sw_netstats_tx_add(dev, skbs, len);
++	if (!wiphy_ext_feature_isset(sta->local->hw.wiphy,
++	    NL80211_EXT_FEATURE_STAS_COUNT))
++		dev_sw_netstats_tx_add(dev, skbs, len);
++
+ 	sta->deflink.tx_stats.packets[queue] += skbs;
+ 	sta->deflink.tx_stats.bytes[queue] += len;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0041-mtk-mac80211-fix-crash-when-starting-tx-ba-session.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0041-mtk-mac80211-fix-crash-when-starting-tx-ba-session.patch
deleted file mode 100644
index aa1b7ae..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0041-mtk-mac80211-fix-crash-when-starting-tx-ba-session.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From c115759fb0b11ba3ce09d078e8609203dc5e772f Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Fri, 10 Nov 2023 15:34:57 +0800
-Subject: [PATCH 41/61] mtk: mac80211: fix crash when starting tx ba session
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
----
- net/mac80211/agg-tx.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
-index 7117576..da9b7f0 100644
---- a/net/mac80211/agg-tx.c
-+++ b/net/mac80211/agg-tx.c
-@@ -642,7 +642,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
- 		 "Requested to start BA session on reserved tid=%d", tid))
- 		return -EINVAL;
- 
--	if (!pubsta->deflink.ht_cap.ht_supported &&
-+	if (!sta->sdata->vif.active_links &&
-+	    !pubsta->deflink.ht_cap.ht_supported &&
- 	    sta->sdata->vif.bss_conf.chanreq.oper.chan->band != NL80211_BAND_6GHZ)
- 		return -EINVAL;
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0042-mtk-mac80211-set-eht_support-to-false-when-AP-is-not.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0042-mtk-mac80211-set-eht_support-to-false-when-AP-is-not.patch
new file mode 100644
index 0000000..bab839c
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0042-mtk-mac80211-set-eht_support-to-false-when-AP-is-not.patch
@@ -0,0 +1,26 @@
+From 1520597ada5bda09bdad5883f79d028fb6082ce0 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Wed, 25 Oct 2023 13:37:00 +0800
+Subject: [PATCH 42/89] mtk: mac80211: set eht_support to false when AP is not
+ in EHT mode
+
+Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
+---
+ net/mac80211/cfg.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index e119373..0b7763d 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1386,6 +1386,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
+ 		link_conf->eht_su_beamformer = false;
+ 		link_conf->eht_su_beamformee = false;
+ 		link_conf->eht_mu_beamformer = false;
++		link_conf->eht_support = false;
+ 	}
+ 
+ 	if (sdata->vif.type == NL80211_IFTYPE_AP &&
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0042-mtk-mac80211-use-link-address-for-eapol-source-in-ie.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0042-mtk-mac80211-use-link-address-for-eapol-source-in-ie.patch
deleted file mode 100644
index f223bff..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0042-mtk-mac80211-use-link-address-for-eapol-source-in-ie.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From 7ec8b9cd491c09f83dfc03976fa0160f89c60eb1 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Tue, 19 Dec 2023 17:42:56 +0800
-Subject: [PATCH 42/61] mtk: mac80211: use link address for eapol source in
- ieee80211_tx_control_port()
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- net/mac80211/tx.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
-index a2ed041..e72bb7e 100644
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -6237,9 +6237,10 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
- 		 * for MLO STA, the SA should be the AP MLD address, but
- 		 * the link ID has been selected already
- 		 */
--		if (sta && sta->sta.mlo)
-+		if (sta && sta->sta.mlo && link_id == IEEE80211_LINK_UNSPECIFIED)
- 			memcpy(ehdr->h_source, sdata->vif.addr, ETH_ALEN);
- 	}
-+
- 	rcu_read_unlock();
- 
- start_xmit:
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0043-cfg80211-mtk-implement-DFS-radar-detect-for-MLO.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0043-cfg80211-mtk-implement-DFS-radar-detect-for-MLO.patch
deleted file mode 100644
index b2a7cbf..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0043-cfg80211-mtk-implement-DFS-radar-detect-for-MLO.patch
+++ /dev/null
@@ -1,469 +0,0 @@
-From 1c581514b49ffe917cb40d4e073c56b6d1f47416 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 19 Jan 2024 14:35:17 +0800
-Subject: [PATCH 43/61] cfg80211: mtk: implement DFS radar detect for MLO
-
-Implement DFS radar detect for MLO
-1. Add link id info for radar detection in MLD
-2. Note that the radar detection flow requires channel switch, which is not yet
-complete in MLO, so postpone it.
-   (a) cac_started, cac_start_time should be moved into wdev->link, but
-channel switch will use it, so wait until channel switch is completed.
-   (b) ieee80211_dfs_cac_cancel, ieee80211_dfs_radar_detected_work, ...
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-rework radar detected flow for mlo
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- include/net/cfg80211.h     |  4 +++-
- net/mac80211/cfg.c         | 26 +++++++++++++++++++-------
- net/mac80211/ieee80211_i.h |  2 +-
- net/mac80211/main.c        |  6 +++---
- net/mac80211/pm.c          |  9 ++++++++-
- net/mac80211/util.c        | 26 +++++++++++++++++++-------
- net/wireless/core.c        |  4 ++--
- net/wireless/mlme.c        |  4 ++--
- net/wireless/nl80211.c     | 10 +++++-----
- net/wireless/rdev-ops.h    | 13 +++++++------
- net/wireless/reg.c         | 15 +++++++++------
- net/wireless/trace.h       | 28 ++++++++++++++++++++++------
- 12 files changed, 100 insertions(+), 47 deletions(-)
-
-diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
-index bd516d1..e55309b 100644
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -4856,10 +4856,12 @@ struct cfg80211_ops {
- 
- 	int	(*start_radar_detection)(struct wiphy *wiphy,
- 					 struct net_device *dev,
-+					 unsigned int link_id,
- 					 struct cfg80211_chan_def *chandef,
- 					 u32 cac_time_ms);
- 	void	(*end_cac)(struct wiphy *wiphy,
--				struct net_device *dev);
-+			   struct net_device *dev,
-+			   unsigned int link_id);
- 	int	(*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev,
- 				 struct cfg80211_update_ft_ies_params *ftie);
- 	int	(*crit_proto_start)(struct wiphy *wiphy,
-diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
-index 215b5d2..3233e2a 100644
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -3443,12 +3443,14 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
- 
- static int ieee80211_start_radar_detection(struct wiphy *wiphy,
- 					   struct net_device *dev,
-+					   unsigned int link_id,
- 					   struct cfg80211_chan_def *chandef,
- 					   u32 cac_time_ms)
- {
- 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- 	struct ieee80211_chan_req chanreq = { .oper = *chandef };
- 	struct ieee80211_local *local = sdata->local;
-+	struct ieee80211_link_data *link;
- 	int err;
- 
- 	lockdep_assert_wiphy(local->hw.wiphy);
-@@ -3458,16 +3460,20 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
- 		goto out_unlock;
- 	}
- 
-+	link = sdata_dereference(sdata->link[link_id], sdata);
-+	if (!link)
-+		return -ENOLINK;
-+
- 	/* whatever, but channel contexts should not complain about that one */
--	sdata->deflink.smps_mode = IEEE80211_SMPS_OFF;
--	sdata->deflink.needed_rx_chains = local->rx_chains;
-+	link->smps_mode = IEEE80211_SMPS_OFF;
-+	link->needed_rx_chains = local->rx_chains;
- 
--	err = ieee80211_link_use_channel(&sdata->deflink, &chanreq,
-+	err = ieee80211_link_use_channel(link, &chanreq,
- 					 IEEE80211_CHANCTX_SHARED);
- 	if (err)
- 		goto out_unlock;
- 
--	wiphy_delayed_work_queue(wiphy, &sdata->deflink.dfs_cac_timer_work,
-+	wiphy_delayed_work_queue(wiphy, &link->dfs_cac_timer_work,
- 				 msecs_to_jiffies(cac_time_ms));
- 
-  out_unlock:
-@@ -3475,23 +3481,29 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
- }
- 
- static void ieee80211_end_cac(struct wiphy *wiphy,
--			      struct net_device *dev)
-+			      struct net_device *dev,
-+			      unsigned int link_id)
- {
- 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- 	struct ieee80211_local *local = sdata->local;
-+	struct ieee80211_link_data *link;
- 
- 	lockdep_assert_wiphy(local->hw.wiphy);
- 
-+	link = sdata_dereference(sdata->link[link_id], sdata);
-+	if (!link)
-+		return;
-+
- 	list_for_each_entry(sdata, &local->interfaces, list) {
- 		/* it might be waiting for the local->mtx, but then
- 		 * by the time it gets it, sdata->wdev.cac_started
- 		 * will no longer be true
- 		 */
- 		wiphy_delayed_work_cancel(wiphy,
--					  &sdata->deflink.dfs_cac_timer_work);
-+					  &link->dfs_cac_timer_work);
- 
- 		if (sdata->wdev.cac_started) {
--			ieee80211_link_release_channel(&sdata->deflink);
-+			ieee80211_link_release_channel(link);
- 			sdata->wdev.cac_started = false;
- 		}
- 	}
-diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
-index c5781c3..608e442 100644
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -2592,7 +2592,7 @@ void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
- bool ieee80211_is_radar_required(struct ieee80211_local *local);
- 
- void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work);
--void ieee80211_dfs_cac_cancel(struct ieee80211_local *local);
-+void ieee80211_dfs_cac_cancel(struct ieee80211_local *local, unsigned int link_id);
- void ieee80211_dfs_radar_detected_work(struct wiphy *wiphy,
- 				       struct wiphy_work *work);
- int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
-diff --git a/net/mac80211/main.c b/net/mac80211/main.c
-index e9d8581..f6f0509 100644
---- a/net/mac80211/main.c
-+++ b/net/mac80211/main.c
-@@ -1173,8 +1173,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
- 			if (comb->num_different_channels > 1)
- 				return -EINVAL;
- 		}
--	} else {
--		/* DFS is not supported with multi-channel combinations yet */
-+	}/* else {
-+		// DFS is not supported with multi-channel combinations yet
- 		for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) {
- 			const struct ieee80211_iface_combination *comb;
- 
-@@ -1184,7 +1184,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
- 			    comb->num_different_channels > 1)
- 				return -EINVAL;
- 		}
--	}
-+	} */
- 
- 	/* Only HW csum features are currently compatible with mac80211 */
- 	if (WARN_ON(hw->netdev_features & ~MAC80211_SUPPORTED_FEATURES))
-diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
-index c1fa26e..5a25245 100644
---- a/net/mac80211/pm.c
-+++ b/net/mac80211/pm.c
-@@ -22,6 +22,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
- {
- 	struct ieee80211_local *local = hw_to_local(hw);
- 	struct ieee80211_sub_if_data *sdata;
-+	struct ieee80211_chanctx *ctx;
- 	struct sta_info *sta;
- 
- 	if (!local->open_count)
-@@ -32,7 +33,13 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
- 
- 	ieee80211_scan_cancel(local);
- 
--	ieee80211_dfs_cac_cancel(local);
-+	list_for_each_entry(ctx, &local->chanctx_list, list) {
-+		struct ieee80211_link_data *link, *tmp;
-+
-+		list_for_each_entry_safe(link, tmp, &ctx->assigned_links,
-+					 assigned_chanctx_list)
-+			ieee80211_dfs_cac_cancel(local, link->link_id);
-+	}
- 
- 	ieee80211_roc_purge(local, NULL);
- 
-diff --git a/net/mac80211/util.c b/net/mac80211/util.c
-index 55f1566..e5ac902 100644
---- a/net/mac80211/util.c
-+++ b/net/mac80211/util.c
-@@ -3450,9 +3450,10 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
- 	return ts;
- }
- 
--void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
-+void ieee80211_dfs_cac_cancel(struct ieee80211_local *local, unsigned int link_id)
- {
- 	struct ieee80211_sub_if_data *sdata;
-+	struct ieee80211_link_data *link;
- 	struct cfg80211_chan_def chandef;
- 
- 	lockdep_assert_wiphy(local->hw.wiphy);
-@@ -3462,12 +3463,20 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
- 		 * by the time it gets it, sdata->wdev.cac_started
- 		 * will no longer be true
- 		 */
-+		link = sdata_dereference(sdata->link[link_id], sdata);
-+		if (!link)
-+			continue;
-+
-+		if (link->conf->chanreq.oper.chan &&
-+		    link->conf->chanreq.oper.chan->band != NL80211_BAND_5GHZ)
-+			return;
-+
- 		wiphy_delayed_work_cancel(local->hw.wiphy,
--					  &sdata->deflink.dfs_cac_timer_work);
-+					  &link->dfs_cac_timer_work);
- 
- 		if (sdata->wdev.cac_started) {
--			chandef = sdata->vif.bss_conf.chanreq.oper;
--			ieee80211_link_release_channel(&sdata->deflink);
-+			chandef = link->conf->chanreq.oper;
-+			ieee80211_link_release_channel(link);
- 			cfg80211_cac_event(sdata->dev,
- 					   &chandef,
- 					   NL80211_RADAR_CAC_ABORTED,
-@@ -3483,20 +3492,23 @@ void ieee80211_dfs_radar_detected_work(struct wiphy *wiphy,
- 		container_of(work, struct ieee80211_local, radar_detected_work);
- 	struct cfg80211_chan_def chandef = local->hw.conf.chandef;
- 	struct ieee80211_chanctx *ctx;
-+	struct ieee80211_link_data *link, *tmp;
- 	int num_chanctx = 0;
- 
- 	lockdep_assert_wiphy(local->hw.wiphy);
- 
- 	list_for_each_entry(ctx, &local->chanctx_list, list) {
--		if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER)
-+		if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER ||
-+		    !ctx->conf.def.chan || ctx->conf.def.chan->band != NL80211_BAND_5GHZ)
- 			continue;
- 
- 		num_chanctx++;
- 		chandef = ctx->conf.def;
-+		list_for_each_entry_safe(link, tmp, &ctx->assigned_links,
-+					 assigned_chanctx_list)
-+			ieee80211_dfs_cac_cancel(local, link->link_id);
- 	}
- 
--	ieee80211_dfs_cac_cancel(local);
--
- 	if (num_chanctx > 1)
- 		/* XXX: multi-channel is not supported yet */
- 		WARN_ON(1);
-diff --git a/net/wireless/core.c b/net/wireless/core.c
-index ac9417e..16f40f3 100644
---- a/net/wireless/core.c
-+++ b/net/wireless/core.c
-@@ -629,10 +629,10 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
- 		if (WARN_ON(!c->num_different_channels))
- 			return -EINVAL;
- 
--		/* DFS only works on one channel. */
-+		/* DFS only works on one channel.
- 		if (WARN_ON(c->radar_detect_widths &&
- 			    (c->num_different_channels > 1)))
--			return -EINVAL;
-+			return -EINVAL; */
- 
- 		if (WARN_ON(!c->n_limits))
- 			return -EINVAL;
-diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
-index 3da7886..a957989 100644
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -1118,9 +1118,9 @@ void cfg80211_cac_event(struct net_device *netdev,
- 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
- 	unsigned long timeout;
- 
--	/* not yet supported */
-+	/* not yet supported
- 	if (wdev->valid_links)
--		return;
-+		return; */
- 
- 	trace_cfg80211_cac_event(netdev, event);
- 
-diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
-index 079fedf..2045cdc 100644
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -9964,6 +9964,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
- 	struct cfg80211_chan_def chandef;
- 	enum nl80211_dfs_regions dfs_region;
- 	unsigned int cac_time_ms;
-+	unsigned int link_id = nl80211_link_id(info->attrs);
- 	int err = -EINVAL;
- 
- 	flush_delayed_work(&rdev->dfs_update_channels_wk);
-@@ -9998,7 +9999,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
- 		goto unlock;
- 	}
- 
--	if (netif_carrier_ok(dev)) {
-+	if (!wdev->valid_links && netif_carrier_ok(dev)) {
- 		err = -EBUSY;
- 		goto unlock;
- 	}
-@@ -10026,9 +10027,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
- 	pr_info("%s: region = %u, center freq1 = %u, center freq2 = %u, cac time ms = %u\n",
- 		__func__, dfs_region, chandef.center_freq1, chandef.center_freq2, cac_time_ms);
- 
--	err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms);
-+	err = rdev_start_radar_detection(rdev, dev, link_id, &chandef, cac_time_ms);
- 	if (!err) {
--		wdev->links[0].ap.chandef = chandef;
-+		wdev->links[link_id].ap.chandef = chandef;
- 		wdev->cac_started = true;
- 		wdev->cac_start_time = jiffies;
- 		wdev->cac_time_ms = cac_time_ms;
-@@ -17304,8 +17305,7 @@ static const struct genl_small_ops nl80211_small_ops[] = {
- 		.doit = nl80211_start_radar_detection,
- 		.flags = GENL_UNS_ADMIN_PERM,
- 		.internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP |
--					 NL80211_FLAG_NO_WIPHY_MTX |
--					 NL80211_FLAG_MLO_UNSUPPORTED),
-+					 NL80211_FLAG_NO_WIPHY_MTX),
- 	},
- 	{
- 		.cmd = NL80211_CMD_GET_PROTOCOL_FEATURES,
-diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
-index 2ae7fc5..561637c 100644
---- a/net/wireless/rdev-ops.h
-+++ b/net/wireless/rdev-ops.h
-@@ -1197,15 +1197,16 @@ rdev_tdls_cancel_channel_switch(struct cfg80211_registered_device *rdev,
- static inline int
- rdev_start_radar_detection(struct cfg80211_registered_device *rdev,
- 			   struct net_device *dev,
-+			   unsigned int link_id,
- 			   struct cfg80211_chan_def *chandef,
- 			   u32 cac_time_ms)
- {
- 	int ret = -EOPNOTSUPP;
- 
--	trace_rdev_start_radar_detection(&rdev->wiphy, dev, chandef,
--					 cac_time_ms);
-+	trace_rdev_start_radar_detection(&rdev->wiphy, dev, link_id,
-+					 chandef, cac_time_ms);
- 	if (rdev->ops->start_radar_detection)
--		ret = rdev->ops->start_radar_detection(&rdev->wiphy, dev,
-+		ret = rdev->ops->start_radar_detection(&rdev->wiphy, dev, link_id,
- 						       chandef, cac_time_ms);
- 	trace_rdev_return_int(&rdev->wiphy, ret);
- 	return ret;
-@@ -1213,11 +1214,11 @@ rdev_start_radar_detection(struct cfg80211_registered_device *rdev,
- 
- static inline void
- rdev_end_cac(struct cfg80211_registered_device *rdev,
--	     struct net_device *dev)
-+	     struct net_device *dev, unsigned int link_id)
- {
--	trace_rdev_end_cac(&rdev->wiphy, dev);
-+	trace_rdev_end_cac(&rdev->wiphy, dev, link_id);
- 	if (rdev->ops->end_cac)
--		rdev->ops->end_cac(&rdev->wiphy, dev);
-+		rdev->ops->end_cac(&rdev->wiphy, dev, link_id);
- 	trace_rdev_return_void(&rdev->wiphy);
- }
- 
-diff --git a/net/wireless/reg.c b/net/wireless/reg.c
-index 2c0c1f1..6883aa0 100644
---- a/net/wireless/reg.c
-+++ b/net/wireless/reg.c
-@@ -4246,17 +4246,20 @@ static void cfg80211_check_and_end_cac(struct cfg80211_registered_device *rdev)
- 	 */
- 	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
- 		struct cfg80211_chan_def *chandef;
-+		unsigned int link_id;
- 
- 		if (!wdev->cac_started)
- 			continue;
- 
--		/* FIXME: radar detection is tied to link 0 for now */
--		chandef = wdev_chandef(wdev, 0);
--		if (!chandef)
--			continue;
-+		for_each_valid_link(wdev, link_id) {
-+			chandef = wdev_chandef(wdev, link_id);
-+			if (!chandef || !chandef->chan ||
-+			    chandef->chan->band != NL80211_BAND_5GHZ)
-+				continue;
- 
--		if (!cfg80211_chandef_dfs_usable(&rdev->wiphy, chandef))
--			rdev_end_cac(rdev, wdev->netdev);
-+			if (!cfg80211_chandef_dfs_usable(&rdev->wiphy, chandef))
-+				rdev_end_cac(rdev, wdev->netdev, link_id);
-+		}
- 	}
- }
- 
-diff --git a/net/wireless/trace.h b/net/wireless/trace.h
-index aa3284f..22b1fcc 100644
---- a/net/wireless/trace.h
-+++ b/net/wireless/trace.h
-@@ -731,9 +731,22 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_flush_pmksa,
- 	TP_ARGS(wiphy, netdev)
- );
- 
--DEFINE_EVENT(wiphy_netdev_evt, rdev_end_cac,
--	     TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
--	     TP_ARGS(wiphy, netdev)
-+TRACE_EVENT(rdev_end_cac,
-+	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
-+		 unsigned int link_id),
-+	TP_ARGS(wiphy, netdev, link_id),
-+	TP_STRUCT__entry(
-+		WIPHY_ENTRY
-+		NETDEV_ENTRY
-+		__field(u32, link_id)
-+	),
-+	TP_fast_assign(
-+		WIPHY_ASSIGN;
-+		NETDEV_ASSIGN;
-+		__entry->link_id = link_id;
-+	),
-+	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", link_id=%u",
-+		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->link_id)
- );
- 
- DECLARE_EVENT_CLASS(station_add_change,
-@@ -2577,24 +2590,27 @@ TRACE_EVENT(rdev_external_auth,
- 
- TRACE_EVENT(rdev_start_radar_detection,
- 	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
-+		 unsigned int link_id,
- 		 struct cfg80211_chan_def *chandef,
- 		 u32 cac_time_ms),
--	TP_ARGS(wiphy, netdev, chandef, cac_time_ms),
-+	TP_ARGS(wiphy, netdev, link_id, chandef, cac_time_ms),
- 	TP_STRUCT__entry(
- 		WIPHY_ENTRY
- 		NETDEV_ENTRY
-+		__field(u32, link_id)
- 		CHAN_DEF_ENTRY
- 		__field(u32, cac_time_ms)
- 	),
- 	TP_fast_assign(
- 		WIPHY_ASSIGN;
- 		NETDEV_ASSIGN;
-+		__entry->link_id = link_id;
- 		CHAN_DEF_ASSIGN(chandef);
- 		__entry->cac_time_ms = cac_time_ms;
- 	),
--	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT
-+	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", link_id=%u, " CHAN_DEF_PR_FMT
- 		  ", cac_time_ms=%u",
--		  WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG,
-+		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->link_id, CHAN_DEF_PR_ARG,
- 		  __entry->cac_time_ms)
- );
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0043-mtk-mac80211-Add-cert-mode-to-disable-ba-timeout.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0043-mtk-mac80211-Add-cert-mode-to-disable-ba-timeout.patch
new file mode 100644
index 0000000..612ed69
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0043-mtk-mac80211-Add-cert-mode-to-disable-ba-timeout.patch
@@ -0,0 +1,124 @@
+From 1519b6ddc0a4256bddcaaeb8fc43db7fd7c6ee8f Mon Sep 17 00:00:00 2001
+From: "Allen.Ye" <allen.ye@mediatek.com>
+Date: Thu, 9 Nov 2023 11:37:37 +0800
+Subject: [PATCH 43/89] mtk: mac80211: Add cert mode to disable ba timeout
+
+Add a switch of certification mode in debugfs as cert_mode. In the case
+we use it to disable BA timeout from STA to prevent crashing STA.
+
+Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
+
+Move the variable 'cert_mode' from ieee80211_local to ieee80211_hw
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ include/net/mac80211.h |  6 ++++++
+ net/mac80211/agg-tx.c  |  5 ++++-
+ net/mac80211/debugfs.c | 49 ++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 59 insertions(+), 1 deletion(-)
+
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 215d499..98678c8 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -3059,8 +3059,14 @@ struct ieee80211_hw {
+ 	u32 max_mtu;
+ 	const s8 *tx_power_levels;
+ 	u8 max_txpwr_levels_idx;
++	bool cert_mode;
+ };
+ 
++static inline bool ieee80211_is_cert_mode(struct ieee80211_hw *hw)
++{
++	return hw->cert_mode;
++}
++
+ static inline bool _ieee80211_hw_check(struct ieee80211_hw *hw,
+ 				       enum ieee80211_hw_flags flg)
+ {
+diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
+index 0abc77b..2f351a7 100644
+--- a/net/mac80211/agg-tx.c
++++ b/net/mac80211/agg-tx.c
+@@ -1092,7 +1092,10 @@ next:
+ 		tid_tx->timeout =
+ 			le16_to_cpu(mgmt->u.action.u.addba_resp.timeout);
+ 
+-		if (tid_tx->timeout) {
++		/* In the case of certification env, testbed STA cannot accept frequent DelBA.
++		 * Therefore, we remove the session timer check here to avoid crashing testbed STA.
++		 */
++		if (tid_tx->timeout && !ieee80211_is_cert_mode(&local->hw)) {
+ 			mod_timer(&tid_tx->session_timer,
+ 				  TU_TO_EXP_TIME(tid_tx->timeout));
+ 			tid_tx->last_tx = jiffies;
+diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
+index ce8eb40..376abfc 100644
+--- a/net/mac80211/debugfs.c
++++ b/net/mac80211/debugfs.c
+@@ -450,6 +450,54 @@ static const struct file_operations reset_ops = {
+ };
+ #endif
+ 
++static ssize_t cert_mode_read(struct file *file,
++			      char __user *user_buf,
++			      size_t count,
++			      loff_t *ppos)
++{
++	struct ieee80211_local *local = file->private_data;
++	char buf[32];
++	int len = 0;
++
++	len = scnprintf(buf, sizeof(buf), "cert_mode: %d\n",
++			local->hw.cert_mode);
++
++	return simple_read_from_buffer(user_buf, count, ppos,
++				       buf, len);
++}
++
++static ssize_t cert_mode_write(struct file *file,
++			       const char __user *user_buf,
++			       size_t count,
++			       loff_t *ppos)
++{
++	struct ieee80211_local *local = file->private_data;
++	char buf[16];
++
++	if (count >= sizeof(buf))
++		return -EINVAL;
++
++	if (copy_from_user(buf, user_buf, count))
++		return -EFAULT;
++
++	if (count && buf[count - 1] == '\n')
++		buf[count - 1] = '\0';
++	else
++		buf[count] = '\0';
++
++	if (kstrtobool(buf, &local->hw.cert_mode))
++		return -EINVAL;
++
++	return count;
++}
++
++static const struct file_operations cert_mode_ops = {
++	.write = cert_mode_write,
++	.read = cert_mode_read,
++	.open = simple_open,
++	.llseek = noop_llseek,
++};
++
+ static const char *hw_flag_names[] = {
+ #define FLAG(F)	[IEEE80211_HW_##F] = #F
+ 	FLAG(HAS_RATE_CONTROL),
+@@ -683,6 +731,7 @@ void debugfs_hw_add(struct ieee80211_local *local)
+ 	debugfs_create_u32("aql_threshold", 0600,
+ 			   phyd, &local->aql_threshold);
+ 
++	DEBUGFS_ADD_MODE(cert_mode, 0644);
+ 	statsd = debugfs_create_dir("statistics", phyd);
+ 
+ #ifdef CPTCFG_MAC80211_DEBUG_COUNTERS
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0044-mtk-mac80211-ACS-channel-time-is-reset-by-ch_restore.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0044-mtk-mac80211-ACS-channel-time-is-reset-by-ch_restore.patch
new file mode 100644
index 0000000..e558be2
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0044-mtk-mac80211-ACS-channel-time-is-reset-by-ch_restore.patch
@@ -0,0 +1,65 @@
+From 41a3454ac2b2bd0116deb0f00553f1959f29329e Mon Sep 17 00:00:00 2001
+From: "fancy.liu" <fancy.liu@mediatek.com>
+Date: Wed, 29 Nov 2023 13:51:13 +0800
+Subject: [PATCH 44/89] mtk: mac80211: ACS channel time is reset by ch_restore
+
+Issue:
+There's a chance that the channel time for duty channel is zero in ACS
+scan.
+
+Root cause:
+The chan_stat may be reset when restore to duty channel.
+Mac80211 will notify to hostapd when scan done and then restore to duty
+channel.
+And mt76 will clear scan flag after restore done.
+If hostapd get the chan_stat before channel_restore, will get the
+correct channel time;
+If hostapd get the chan_stat after channel_restore, will get zero
+channel time;
+
+Solution:
+When channel switch, will check the mac80211 scan state but not the mt76 scan flag.
+Mac80211 scan state will be set in scanning, and will be reset after
+scan done and before restore to duty channel.
+
+Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
+---
+ include/net/mac80211.h | 7 +++++++
+ net/mac80211/util.c    | 8 ++++++++
+ 2 files changed, 15 insertions(+)
+
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 98678c8..5321c22 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -7708,4 +7708,11 @@ int ieee80211_emulate_switch_vif_chanctx(struct ieee80211_hw *hw,
+ 					 int n_vifs,
+ 					 enum ieee80211_chanctx_switch_mode mode);
+ 
++/**
++ * ieee80211_get_scanning - get scanning bitmask
++ *
++ * @hw: pointer as obtained from ieee80211_alloc_hw()
++ */
++unsigned long ieee80211_get_scanning(struct ieee80211_hw *hw);
++
+ #endif /* MAC80211_H */
+diff --git a/net/mac80211/util.c b/net/mac80211/util.c
+index 1877400..7f93739 100644
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -4486,3 +4486,11 @@ void ieee80211_clear_tpe(struct ieee80211_parsed_tpe *tpe)
+ 		       sizeof(tpe->psd_reg_client[i].power));
+ 	}
+ }
++
++unsigned long ieee80211_get_scanning(struct ieee80211_hw *hw)
++{
++	struct ieee80211_local *local = hw_to_local(hw);
++
++	return local->scanning;
++}
++EXPORT_SYMBOL(ieee80211_get_scanning);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0044-mtk-wifi-mac80211-add-wds-mlo-support.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0044-mtk-wifi-mac80211-add-wds-mlo-support.patch
deleted file mode 100644
index 52e91c0..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0044-mtk-wifi-mac80211-add-wds-mlo-support.patch
+++ /dev/null
@@ -1,127 +0,0 @@
-From 5f294c8632c08814df00df8ed297e852d21b0e02 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Fri, 19 Jan 2024 15:22:00 +0800
-Subject: [PATCH 44/61] mtk: wifi: mac80211: add wds mlo support
-
-Support WDS mode when using MLO.
-1. Remove use_4addr check.
-2. Copy link information to AP_VLAN interface.
-3. Fill 4addr nullfunc by mld address.
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- net/mac80211/cfg.c   |  7 -------
- net/mac80211/iface.c | 23 +++++++++++++++--------
- net/mac80211/mlme.c  | 15 +++++++++------
- 3 files changed, 24 insertions(+), 21 deletions(-)
-
-diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
-index 3233e2a..0cb68fb 100644
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -229,10 +229,6 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
- 		if (params->use_4addr == ifmgd->use_4addr)
- 			return 0;
- 
--		/* FIXME: no support for 4-addr MLO yet */
--		if (ieee80211_vif_is_mld(&sdata->vif))
--			return -EOPNOTSUPP;
--
- 		sdata->u.mgd.use_4addr = params->use_4addr;
- 		if (!ifmgd->associated)
- 			return 0;
-@@ -4922,9 +4918,6 @@ static int ieee80211_add_intf_link(struct wiphy *wiphy,
- 
- 	lockdep_assert_wiphy(sdata->local->hw.wiphy);
- 
--	if (wdev->use_4addr)
--		return -EOPNOTSUPP;
--
- 	return ieee80211_vif_set_links(sdata, wdev->valid_links, 0);
- }
- 
-diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
-index 0ae31a9..fce9834 100644
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -381,19 +381,26 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
- 							nsdata->vif.type))
- 				return -ENOTUNIQ;
- 
--			/* No support for VLAN with MLO yet */
--			if (iftype == NL80211_IFTYPE_AP_VLAN &&
--			    sdata->wdev.use_4addr &&
--			    nsdata->vif.type == NL80211_IFTYPE_AP &&
--			    nsdata->vif.valid_links)
--				return -EOPNOTSUPP;
--
- 			/*
- 			 * can only add VLANs to enabled APs
- 			 */
- 			if (iftype == NL80211_IFTYPE_AP_VLAN &&
--			    nsdata->vif.type == NL80211_IFTYPE_AP)
-+			    nsdata->vif.type == NL80211_IFTYPE_AP) {
-+				int i;
-+
- 				sdata->bss = &nsdata->u.ap;
-+				sdata->vif.valid_links = nsdata->vif.valid_links;
-+				sdata->vif.active_links = nsdata->vif.active_links;
-+				sdata->vif.dormant_links = nsdata->vif.dormant_links;
-+				for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
-+					if (!ieee80211_vif_is_mld(&nsdata->vif) &&
-+					    sdata->link[i] == &sdata->deflink)
-+						continue;
-+
-+					sdata->link[i] = nsdata->link[i];
-+					sdata->vif.link_conf[i] = nsdata->vif.link_conf[i];
-+				}
-+			}
- 		}
- 	}
- 
-diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
-index 84ea805..674f3f5 100644
---- a/net/mac80211/mlme.c
-+++ b/net/mac80211/mlme.c
-@@ -1829,6 +1829,7 @@ void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
- {
- 	struct sk_buff *skb;
- 	struct ieee80211_hdr *nullfunc;
-+	u8 assoc_link_id = ifmgd->assoc_data->assoc_link_id;
- 	__le16 fc;
- 
- 	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
-@@ -1844,11 +1845,17 @@ void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
- 	fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
- 			 IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
- 	nullfunc->frame_control = fc;
--	memcpy(nullfunc->addr1, sdata->deflink.u.mgd.bssid, ETH_ALEN);
- 	memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);
--	memcpy(nullfunc->addr3, sdata->deflink.u.mgd.bssid, ETH_ALEN);
- 	memcpy(nullfunc->addr4, sdata->vif.addr, ETH_ALEN);
- 
-+	if (ieee80211_vif_is_mld(&sdata->vif)) {
-+		memcpy(nullfunc->addr1, sdata->vif.cfg.ap_addr, ETH_ALEN);
-+		memcpy(nullfunc->addr3, sdata->vif.cfg.ap_addr, ETH_ALEN);
-+	} else {
-+		memcpy(nullfunc->addr1, sdata->deflink.u.mgd.bssid, ETH_ALEN);
-+		memcpy(nullfunc->addr3, sdata->deflink.u.mgd.bssid, ETH_ALEN);
-+	}
-+
- 	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
- 	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
- 	ieee80211_tx_skb(sdata, skb);
-@@ -8229,10 +8236,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
- 	for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++)
- 		size += req->links[i].elems_len;
- 
--	/* FIXME: no support for 4-addr MLO yet */
--	if (sdata->u.mgd.use_4addr && req->link_id >= 0)
--		return -EOPNOTSUPP;
--
- 	assoc_data = kzalloc(size, GFP_KERNEL);
- 	if (!assoc_data)
- 		return -ENOMEM;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0045-mtk-mac80211-Fix-SMPS-action-frame-cap-check.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0045-mtk-mac80211-Fix-SMPS-action-frame-cap-check.patch
new file mode 100644
index 0000000..aa67634
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0045-mtk-mac80211-Fix-SMPS-action-frame-cap-check.patch
@@ -0,0 +1,43 @@
+From 8b48d9cd47d92a99b296f187aa0aa53a39af4179 Mon Sep 17 00:00:00 2001
+From: "Allen.Ye" <allen.ye@mediatek.com>
+Date: Thu, 30 Nov 2023 14:01:29 +0800
+Subject: [PATCH 45/89] mtk: mac80211: Fix SMPS action frame cap check
+
+Fix SMPS action frame cap check.
+Due to 6G band doesn't have HT cap, we change cap check into each action
+frame section.
+
+Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
+---
+ net/mac80211/rx.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
+index 78e9c6e..237ab52 100644
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -3531,9 +3531,6 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
+ 
+ 	switch (mgmt->u.action.category) {
+ 	case WLAN_CATEGORY_HT:
+-		/* reject HT action frames from stations not supporting HT */
+-		if (!rx->link_sta->pub->ht_cap.ht_supported)
+-			goto invalid;
+ 
+ 		if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+ 		    sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
+@@ -3552,6 +3549,11 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
+ 			enum ieee80211_smps_mode smps_mode;
+ 			struct sta_opmode_info sta_opmode = {};
+ 
++			if (rx->link_sta->pub->he_cap.has_he &&
++			    !(rx->link_sta->pub->he_cap.he_cap_elem.mac_cap_info[5] &
++			    IEEE80211_HE_MAC_CAP5_HE_DYNAMIC_SM_PS))
++				goto invalid;
++
+ 			if (sdata->vif.type != NL80211_IFTYPE_AP &&
+ 			    sdata->vif.type != NL80211_IFTYPE_AP_VLAN)
+ 				goto handled;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0045-mtk-mac80211-fix-ieee80211_probe_client-warning.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0045-mtk-mac80211-fix-ieee80211_probe_client-warning.patch
deleted file mode 100644
index 1bb036d..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0045-mtk-mac80211-fix-ieee80211_probe_client-warning.patch
+++ /dev/null
@@ -1,37 +0,0 @@
-From 65e5845ce53a6060cb3201a7dcae96b57eb2ea45 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Mon, 29 Jan 2024 18:37:15 +0800
-Subject: [PATCH 45/61] mtk: mac80211: fix ieee80211_probe_client warning
-
-Only get chanctx for non-mld VIF.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- net/mac80211/cfg.c | 10 ++++++----
- 1 file changed, 6 insertions(+), 4 deletions(-)
-
-diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
-index 0cb68fb..6e3b28e 100644
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -4211,11 +4211,13 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
- 	qos = sta->sta.wme;
- 
- 	chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
--	if (WARN_ON(!chanctx_conf)) {
--		ret = -EINVAL;
--		goto unlock;
-+	if (!ieee80211_vif_is_mld(&sdata->vif)) {
-+		if (WARN_ON(!chanctx_conf)) {
-+			ret = -EINVAL;
-+			goto unlock;
-+		}
- 	}
--	band = chanctx_conf->def.chan->band;
-+	band = chanctx_conf ? chanctx_conf->def.chan->band : 0;
- 
- 	if (qos) {
- 		fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0046-mtk-mac80211-allow-multiple-links-for-STA-vif.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0046-mtk-mac80211-allow-multiple-links-for-STA-vif.patch
new file mode 100644
index 0000000..bdc3dcd
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0046-mtk-mac80211-allow-multiple-links-for-STA-vif.patch
@@ -0,0 +1,28 @@
+From 87be910eb2392b8d9c40330dde73bf01f7e07323 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 19 Oct 2023 00:27:15 +0800
+Subject: [PATCH 46/89] mtk: mac80211: allow multiple links for STA vif
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ net/mac80211/link.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/net/mac80211/link.c b/net/mac80211/link.c
+index 0bbac64..f77dab2 100644
+--- a/net/mac80211/link.c
++++ b/net/mac80211/link.c
+@@ -180,10 +180,7 @@ static void ieee80211_set_vif_links_bitmaps(struct ieee80211_sub_if_data *sdata,
+ 		WARN_ON(dormant_links);
+ 		break;
+ 	case NL80211_IFTYPE_STATION:
+-		if (sdata->vif.active_links)
+-			break;
+ 		sdata->vif.active_links = valid_links & ~dormant_links;
+-		WARN_ON(hweight16(sdata->vif.active_links) > 1);
+ 		break;
+ 	default:
+ 		WARN_ON(1);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0046-mtk-mac80211-remove-link-0-warn-on-in-rate_control_r.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0046-mtk-mac80211-remove-link-0-warn-on-in-rate_control_r.patch
deleted file mode 100644
index 94b0038..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0046-mtk-mac80211-remove-link-0-warn-on-in-rate_control_r.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From 5ec53c93f1361412ff263a1bf8eb38c18e447d4a Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 31 Jan 2024 11:40:28 +0800
-Subject: [PATCH 46/61] mtk: mac80211: remove link != 0 warn on in
- rate_control_rate_update for mlo channel switch
-
-Remove link warning for mlo channel switch
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- net/mac80211/rate.c | 10 ++++++++--
- 1 file changed, 8 insertions(+), 2 deletions(-)
-
-diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
-index bbdd3b8..8f54562 100644
---- a/net/mac80211/rate.c
-+++ b/net/mac80211/rate.c
-@@ -100,13 +100,19 @@ void rate_control_rate_update(struct ieee80211_local *local,
- 	struct ieee80211_sta *ista = &sta->sta;
- 	void *priv_sta = sta->rate_ctrl_priv;
- 	struct ieee80211_chanctx_conf *chanctx_conf;
-+	struct ieee80211_link_data *link;
- 
--	WARN_ON(link_id != 0);
-+	// WARN_ON(link_id != 0);
- 
- 	if (ref && ref->ops->rate_update) {
- 		rcu_read_lock();
- 
--		chanctx_conf = rcu_dereference(sta->sdata->vif.bss_conf.chanctx_conf);
-+		link = rcu_dereference(sta->sdata->link[link_id]);
-+		if (!link) {
-+			rcu_read_unlock();
-+			return;
-+		}
-+		chanctx_conf = rcu_dereference(link->conf->chanctx_conf);
- 		if (WARN_ON(!chanctx_conf)) {
- 			rcu_read_unlock();
- 			return;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0047-mtk-mac80211-fix-radar-required-of-link-issue-in-res.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0047-mtk-mac80211-fix-radar-required-of-link-issue-in-res.patch
deleted file mode 100644
index d79d929..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0047-mtk-mac80211-fix-radar-required-of-link-issue-in-res.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From f5a7e57bca203a7d063faf0b9b54b4da8c42f884 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Thu, 1 Feb 2024 17:46:49 +0800
-Subject: [PATCH 47/61] mtk: mac80211: fix radar required of link issue in
- reserve_reassign and reserve_assign
-
-link->radar_required is not updated in
-ieee80211_link_use_reserved_assign & ieee80211_link_use_reserved_reassign
-This will lead to DFS RDD init incomplete (RDD_CAC_START, RDD_CAC_END &
-RDD_DET_MODE is not set to fw)
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- net/mac80211/chan.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
-index 8043d1d..ac22524 100644
---- a/net/mac80211/chan.c
-+++ b/net/mac80211/chan.c
-@@ -1207,6 +1207,7 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link)
- 	if (link_conf->chanreq.oper.width != link->reserved.oper.width)
- 		changed = BSS_CHANGED_BANDWIDTH;
- 
-+	link->radar_required = link->reserved_radar_required;
- 	ieee80211_link_update_chanreq(link, &link->reserved);
- 
- 	_ieee80211_change_chanctx(local, new_ctx, old_ctx, chanreq, link);
-@@ -1290,6 +1291,7 @@ ieee80211_link_use_reserved_assign(struct ieee80211_link_data *link)
- 	list_del(&link->reserved_chanctx_list);
- 	link->reserved_chanctx = NULL;
- 
-+	link->radar_required = link->reserved_radar_required;
- 	err = ieee80211_assign_link_chanctx(link, new_ctx);
- 	if (err) {
- 		if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0047-mtk-mac80211-increase-association-timeout-time.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0047-mtk-mac80211-increase-association-timeout-time.patch
new file mode 100644
index 0000000..b34ef91
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0047-mtk-mac80211-increase-association-timeout-time.patch
@@ -0,0 +1,29 @@
+From 786c6183c040ed6f6e440410957aa42d4086df03 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 19 Oct 2023 00:35:11 +0800
+Subject: [PATCH 47/89] mtk: mac80211: increase association timeout time
+
+Prevent from sending multiple association requests while AP is already
+hanlding the request.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ net/mac80211/mlme.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 9c83b96..ebd4d74 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -7789,7 +7789,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
+ 			 */
+ 			if (status_acked) {
+ 				ifmgd->assoc_data->timeout =
+-					jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT;
++					jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT * 4;
+ 				run_again(sdata, ifmgd->assoc_data->timeout);
+ 			} else {
+ 				ifmgd->assoc_data->timeout = jiffies - 1;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0048-mtk-cfg80211-rework-cac-started-cac-start-time-for-m.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0048-mtk-cfg80211-rework-cac-started-cac-start-time-for-m.patch
deleted file mode 100644
index f711fcd..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0048-mtk-cfg80211-rework-cac-started-cac-start-time-for-m.patch
+++ /dev/null
@@ -1,388 +0,0 @@
-From 286c36e1252e070ae30be3231b1349edb8cb74b6 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Thu, 1 Feb 2024 10:57:39 +0800
-Subject: [PATCH 48/61] mtk: cfg80211: rework cac started, cac start time for
- multi-link support
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- include/net/cfg80211.h | 20 ++++++++++++++------
- net/mac80211/cfg.c     | 26 +++++++++++++-------------
- net/mac80211/iface.c   | 22 +++++++++++++++-------
- net/mac80211/mlme.c    |  4 ++--
- net/mac80211/pm.c      | 10 ++++------
- net/mac80211/scan.c    |  2 +-
- net/mac80211/util.c    |  6 +++---
- net/wireless/debugfs.c | 10 ++++++----
- net/wireless/mlme.c    | 13 +++++++------
- net/wireless/nl80211.c |  8 ++++----
- net/wireless/reg.c     |  4 ++--
- 11 files changed, 71 insertions(+), 54 deletions(-)
-
-diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
-index e55309b..e88cc1e 100644
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -6223,11 +6223,6 @@ struct wireless_dev {
- 	u32 owner_nlportid;
- 	bool nl_owner_dead;
- 
--	/* FIXME: need to rework radar detection for MLO */
--	bool cac_started;
--	unsigned long cac_start_time;
--	unsigned int cac_time_ms;
--
- #ifdef CPTCFG_CFG80211_WEXT
- 	/* wext data */
- 	struct {
-@@ -6294,8 +6289,11 @@ struct wireless_dev {
- 				struct cfg80211_internal_bss *current_bss;
- 			} client;
- 		};
-+		unsigned long cac_start_time;
-+		unsigned int cac_time_ms;
- 	} links[IEEE80211_MLD_MAX_NUM_LINKS];
- 	u16 valid_links;
-+	u16 cac_links;
- };
- 
- static inline const u8 *wdev_address(struct wireless_dev *wdev)
-@@ -6350,6 +6348,15 @@ static inline void WARN_INVALID_LINK_ID(struct wireless_dev *wdev,
- 		if (!(link_info)->valid_links ||		\
- 		    ((link_info)->valid_links & BIT(link_id)))
- 
-+#define for_each_cac_link(link_info, link_id)			\
-+	for (link_id = 0;					\
-+	     link_id < ((link_info)->cac_links ?		\
-+			ARRAY_SIZE((link_info)->links) : 1);	\
-+	     link_id++)						\
-+		if (!(link_info)->cac_links ||			\
-+		    ((link_info)->cac_links & BIT(link_id)))
-+
-+
- /**
-  * DOC: Utility functions
-  *
-@@ -8669,6 +8676,7 @@ void cfg80211_sta_opmode_change_notify(struct net_device *dev, const u8 *mac,
- /**
-  * cfg80211_cac_event - Channel availability check (CAC) event
-  * @netdev: network device
-+ * @link_id: the link ID for MLO, must be 0 for non-MLO
-  * @chandef: chandef for the current channel
-  * @event: type of event
-  * @gfp: context flags
-@@ -8677,7 +8685,7 @@ void cfg80211_sta_opmode_change_notify(struct net_device *dev, const u8 *mac,
-  * or aborted. This must be called to notify the completion of a CAC process,
-  * also by full-MAC drivers.
-  */
--void cfg80211_cac_event(struct net_device *netdev,
-+void cfg80211_cac_event(struct net_device *netdev, unsigned int link_id,
- 			const struct cfg80211_chan_def *chandef,
- 			enum nl80211_radar_event event, gfp_t gfp);
- 
-diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
-index 6e3b28e..871623d 100644
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -1658,10 +1658,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
- 	ieee80211_link_info_change_notify(sdata, link,
- 					  BSS_CHANGED_BEACON_ENABLED);
- 
--	if (sdata->wdev.cac_started) {
-+	if (sdata->wdev.cac_links & BIT(link_id)) {
- 		chandef = link_conf->chanreq.oper;
- 		wiphy_delayed_work_cancel(wiphy, &link->dfs_cac_timer_work);
--		cfg80211_cac_event(sdata->dev, &chandef,
-+		cfg80211_cac_event(sdata->dev, link_id, &chandef,
- 				   NL80211_RADAR_CAC_ABORTED,
- 				   GFP_KERNEL);
- 	}
-@@ -3492,15 +3492,15 @@ static void ieee80211_end_cac(struct wiphy *wiphy,
- 
- 	list_for_each_entry(sdata, &local->interfaces, list) {
- 		/* it might be waiting for the local->mtx, but then
--		 * by the time it gets it, sdata->wdev.cac_started
-+		 * by the time it gets it, sdata->wdev.cac_links & BIT(link_id)
- 		 * will no longer be true
- 		 */
- 		wiphy_delayed_work_cancel(wiphy,
- 					  &link->dfs_cac_timer_work);
- 
--		if (sdata->wdev.cac_started) {
-+		if (sdata->wdev.cac_links & BIT(link_id)) {
- 			ieee80211_link_release_channel(link);
--			sdata->wdev.cac_started = false;
-+			sdata->wdev.cac_links &= ~BIT(link_id);
- 		}
- 	}
- }
-@@ -3959,12 +3959,12 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
- 	if (!list_empty(&local->roc_list) || local->scanning)
- 		return -EBUSY;
- 
--	if (sdata->wdev.cac_started)
--		return -EBUSY;
--
- 	if (WARN_ON(link_id >= IEEE80211_MLD_MAX_NUM_LINKS))
- 		return -EINVAL;
- 
-+	if (sdata->wdev.cac_links & BIT(link_id))
-+		return -EBUSY;
-+
- 	link_data = wiphy_dereference(wiphy, sdata->link[link_id]);
- 	if (!link_data)
- 		return -ENOLINK;
-@@ -5078,12 +5078,12 @@ ieee80211_skip_cac(struct wireless_dev *wdev, unsigned int link_id)
- 
- 	wiphy_delayed_work_cancel(sdata->local->hw.wiphy,
- 				  &link->dfs_cac_timer_work);
--	if (wdev->cac_started) {
-+	if (wdev->cac_links & BIT(link_id)) {
- 		ieee80211_link_release_channel(link);
--		cac_time_ms = wdev->cac_time_ms;
--		wdev->cac_start_time = jiffies -
--				       msecs_to_jiffies(cac_time_ms + 1);
--		cfg80211_cac_event(wdev->netdev, &link->conf->chanreq.oper,
-+		cac_time_ms = wdev->links[link_id].cac_time_ms;
-+		wdev->links[link_id].cac_start_time = jiffies -
-+						      msecs_to_jiffies(cac_time_ms + 1);
-+		cfg80211_cac_event(wdev->netdev, link_id, &link->conf->chanreq.oper,
- 				   NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
- 	}
- }
-diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
-index fce9834..be52a83 100644
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -563,13 +563,21 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
- 	wiphy_delayed_work_cancel(local->hw.wiphy,
- 				  &sdata->deflink.dfs_cac_timer_work);
- 
--	if (sdata->wdev.cac_started) {
--		chandef = sdata->vif.bss_conf.chanreq.oper;
--		WARN_ON(local->suspended);
--		ieee80211_link_release_channel(&sdata->deflink);
--		cfg80211_cac_event(sdata->dev, &chandef,
--				   NL80211_RADAR_CAC_ABORTED,
--				   GFP_KERNEL);
-+	if (sdata->wdev.cac_links) {
-+		struct ieee80211_link_data *link;
-+		unsigned int link_id;
-+
-+		for_each_cac_link(&sdata->wdev, link_id) {
-+			link = sdata_dereference(sdata->link[link_id], sdata);
-+			if (!link)
-+				continue;
-+			chandef = link->conf->chanreq.oper;
-+			WARN_ON(local->suspended);
-+			ieee80211_link_release_channel(link);
-+			cfg80211_cac_event(sdata->dev, link_id, &chandef,
-+					   NL80211_RADAR_CAC_ABORTED,
-+					   GFP_KERNEL);
-+		}
- 	}
- 
- 	if (sdata->vif.type == NL80211_IFTYPE_AP) {
-diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
-index 674f3f5..e4564de 100644
---- a/net/mac80211/mlme.c
-+++ b/net/mac80211/mlme.c
-@@ -2611,9 +2611,9 @@ void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work)
- 
- 	lockdep_assert_wiphy(sdata->local->hw.wiphy);
- 
--	if (sdata->wdev.cac_started) {
-+	if (sdata->wdev.cac_links & BIT(link->link_id)) {
- 		ieee80211_link_release_channel(link);
--		cfg80211_cac_event(sdata->dev, &chandef,
-+		cfg80211_cac_event(sdata->dev, link->link_id, &chandef,
- 				   NL80211_RADAR_CAC_FINISHED,
- 				   GFP_KERNEL);
- 	}
-diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
-index 5a25245..6fae264 100644
---- a/net/mac80211/pm.c
-+++ b/net/mac80211/pm.c
-@@ -22,7 +22,6 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
- {
- 	struct ieee80211_local *local = hw_to_local(hw);
- 	struct ieee80211_sub_if_data *sdata;
--	struct ieee80211_chanctx *ctx;
- 	struct sta_info *sta;
- 
- 	if (!local->open_count)
-@@ -33,12 +32,11 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
- 
- 	ieee80211_scan_cancel(local);
- 
--	list_for_each_entry(ctx, &local->chanctx_list, list) {
--		struct ieee80211_link_data *link, *tmp;
-+	list_for_each_entry(sdata, &local->interfaces, list) {
-+		unsigned int link_id;
- 
--		list_for_each_entry_safe(link, tmp, &ctx->assigned_links,
--					 assigned_chanctx_list)
--			ieee80211_dfs_cac_cancel(local, link->link_id);
-+		for_each_cac_link(&sdata->wdev, link_id)
-+			ieee80211_dfs_cac_cancel(local, link_id);
- 	}
- 
- 	ieee80211_roc_purge(local, NULL);
-diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
-index 977f8eb..5a430cb 100644
---- a/net/mac80211/scan.c
-+++ b/net/mac80211/scan.c
-@@ -584,7 +584,7 @@ static bool __ieee80211_can_leave_ch(struct ieee80211_sub_if_data *sdata)
- 		return false;
- 
- 	list_for_each_entry(sdata_iter, &local->interfaces, list) {
--		if (sdata_iter->wdev.cac_started)
-+		if (sdata_iter->wdev.cac_links)
- 			return false;
- 	}
- 
-diff --git a/net/mac80211/util.c b/net/mac80211/util.c
-index e5ac902..732232a 100644
---- a/net/mac80211/util.c
-+++ b/net/mac80211/util.c
-@@ -3460,7 +3460,7 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local, unsigned int link_i
- 
- 	list_for_each_entry(sdata, &local->interfaces, list) {
- 		/* it might be waiting for the local->mtx, but then
--		 * by the time it gets it, sdata->wdev.cac_started
-+		 * by the time it gets it, sdata->wdev.cac_links & BIT(link_id)
- 		 * will no longer be true
- 		 */
- 		link = sdata_dereference(sdata->link[link_id], sdata);
-@@ -3474,10 +3474,10 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local, unsigned int link_i
- 		wiphy_delayed_work_cancel(local->hw.wiphy,
- 					  &link->dfs_cac_timer_work);
- 
--		if (sdata->wdev.cac_started) {
-+		if (sdata->wdev.cac_links & BIT(link_id)) {
- 			chandef = link->conf->chanreq.oper;
- 			ieee80211_link_release_channel(link);
--			cfg80211_cac_event(sdata->dev,
-+			cfg80211_cac_event(sdata->dev, link_id,
- 					   &chandef,
- 					   NL80211_RADAR_CAC_ABORTED,
- 					   GFP_KERNEL);
-diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c
-index a246b2c..cbeed9f 100644
---- a/net/wireless/debugfs.c
-+++ b/net/wireless/debugfs.c
-@@ -187,10 +187,11 @@ static int dfs_status_read_wdev(struct wiphy *wiphy, struct wireless_dev *wdev,
- 				if (remain_time > wait_time_ms)
- 					remain_time = 0;
- 			} else if (chan->dfs_state == NL80211_DFS_USABLE) {
--				if (wdev->cac_started &&
-+				if ((wdev->cac_links & BIT(link_id)) &&
- 				    cfg80211_is_sub_chan(chandef, chan, false)) {
--					jiffies_passed = jiffies - wdev->cac_start_time;
--					wait_time_ms = wdev->cac_time_ms;
-+					jiffies_passed = jiffies -
-+							 wdev->links[link_id].cac_start_time;
-+					wait_time_ms = wdev->links[link_id].cac_time_ms;
- 					remain_time = (wait_time_ms -
- 						       jiffies_to_msecs(jiffies_passed));
- 				}
-@@ -336,7 +337,8 @@ dfs_cac_skip(void *data, u64 val)
- 					continue;
- 
- 				if (cfg80211_chandef_dfs_required(wiphy, c, wdev->iftype) > 0 &&
--				    cfg80211_chandef_dfs_usable(wiphy, c) && wdev->cac_started) {
-+				    cfg80211_chandef_dfs_usable(wiphy, c) &&
-+				    (wdev->cac_links & BIT(link_id))) {
- 					rdev_skip_cac(rdev, wdev, link_id);
- 				}
- 			}
-diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
-index a957989..265c2f2 100644
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -1109,7 +1109,7 @@ void __cfg80211_radar_event(struct wiphy *wiphy,
- }
- EXPORT_SYMBOL(__cfg80211_radar_event);
- 
--void cfg80211_cac_event(struct net_device *netdev,
-+void cfg80211_cac_event(struct net_device *netdev, unsigned int link_id,
- 			const struct cfg80211_chan_def *chandef,
- 			enum nl80211_radar_event event, gfp_t gfp)
- {
-@@ -1124,13 +1124,14 @@ void cfg80211_cac_event(struct net_device *netdev,
- 
- 	trace_cfg80211_cac_event(netdev, event);
- 
--	if (WARN_ON(!wdev->cac_started && event != NL80211_RADAR_CAC_STARTED))
-+	if (WARN_ON(!(wdev->cac_links & BIT(link_id)) &&
-+		    event != NL80211_RADAR_CAC_STARTED))
- 		return;
- 
- 	switch (event) {
- 	case NL80211_RADAR_CAC_FINISHED:
--		timeout = wdev->cac_start_time +
--			  msecs_to_jiffies(wdev->cac_time_ms);
-+		timeout = wdev->links[link_id].cac_start_time +
-+			  msecs_to_jiffies(wdev->links[link_id].cac_time_ms);
- 		WARN_ON(!time_after_eq(jiffies, timeout));
- 		cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
- 		memcpy(&rdev->cac_done_chandef, chandef,
-@@ -1139,10 +1140,10 @@ void cfg80211_cac_event(struct net_device *netdev,
- 		cfg80211_sched_dfs_chan_update(rdev);
- 		fallthrough;
- 	case NL80211_RADAR_CAC_ABORTED:
--		wdev->cac_started = false;
-+		wdev->cac_links &= ~BIT(link_id);
- 		break;
- 	case NL80211_RADAR_CAC_STARTED:
--		wdev->cac_started = true;
-+		wdev->cac_links |= BIT(link_id);
- 		break;
- 	default:
- 		WARN_ON(1);
-diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
-index 2045cdc..2a34884 100644
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -10004,7 +10004,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
- 		goto unlock;
- 	}
- 
--	if (wdev->cac_started) {
-+	if (wdev->cac_links & BIT(link_id)) {
- 		err = -EBUSY;
- 		goto unlock;
- 	}
-@@ -10030,9 +10030,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
- 	err = rdev_start_radar_detection(rdev, dev, link_id, &chandef, cac_time_ms);
- 	if (!err) {
- 		wdev->links[link_id].ap.chandef = chandef;
--		wdev->cac_started = true;
--		wdev->cac_start_time = jiffies;
--		wdev->cac_time_ms = cac_time_ms;
-+		wdev->cac_links |= BIT(link_id);
-+		wdev->links[link_id].cac_start_time = jiffies;
-+		wdev->links[link_id].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);
-diff --git a/net/wireless/reg.c b/net/wireless/reg.c
-index 6883aa0..ebe0ed4 100644
---- a/net/wireless/reg.c
-+++ b/net/wireless/reg.c
-@@ -4248,10 +4248,10 @@ static void cfg80211_check_and_end_cac(struct cfg80211_registered_device *rdev)
- 		struct cfg80211_chan_def *chandef;
- 		unsigned int link_id;
- 
--		if (!wdev->cac_started)
-+		if (!wdev->cac_links)
- 			continue;
- 
--		for_each_valid_link(wdev, link_id) {
-+		for_each_cac_link(wdev, link_id) {
- 			chandef = wdev_chandef(wdev, link_id);
- 			if (!chandef || !chandef->chan ||
- 			    chandef->chan->band != NL80211_BAND_5GHZ)
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0048-mtk-mac80211-use-link-address-for-eapol-source-in-ie.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0048-mtk-mac80211-use-link-address-for-eapol-source-in-ie.patch
new file mode 100644
index 0000000..9c2fd48
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0048-mtk-mac80211-use-link-address-for-eapol-source-in-ie.patch
@@ -0,0 +1,30 @@
+From 70b9d48977bb89474da6996e4ef3124dc5f4f6a9 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 19 Dec 2023 17:42:56 +0800
+Subject: [PATCH 48/89] mtk: mac80211: use link address for eapol source in
+ ieee80211_tx_control_port()
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ net/mac80211/tx.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
+index 3ac51a2..9e95dbd 100644
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -6241,9 +6241,10 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
+ 		 * for MLO STA, the SA should be the AP MLD address, but
+ 		 * the link ID has been selected already
+ 		 */
+-		if (sta && sta->sta.mlo)
++		if (sta && sta->sta.mlo && link_id == IEEE80211_LINK_UNSPECIFIED)
+ 			memcpy(ehdr->h_source, sdata->vif.addr, ETH_ALEN);
+ 	}
++
+ 	rcu_read_unlock();
+ 
+ start_xmit:
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0049-mtk-cfg80211-implement-DFS-radar-detect-for-MLO.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0049-mtk-cfg80211-implement-DFS-radar-detect-for-MLO.patch
new file mode 100644
index 0000000..1523dff
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0049-mtk-cfg80211-implement-DFS-radar-detect-for-MLO.patch
@@ -0,0 +1,151 @@
+From 78281526ce83b2623939c6060c7baf65cb5644ee Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 19 Jan 2024 14:35:17 +0800
+Subject: [PATCH 49/89] mtk: cfg80211: implement DFS radar detect for MLO
+
+Implement DFS radar detect for MLO
+1. Add link id info for radar detection in MLD
+2. Note that the radar detection flow requires channel switch, which is not yet
+complete in MLO, so postpone it.
+   (a) cac_started, cac_start_time should be moved into wdev->link, but
+channel switch will use it, so wait until channel switch is completed.
+   (b) ieee80211_dfs_cac_cancel, ieee80211_dfs_radar_detected_work, ...
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+rework radar detected flow for mlo
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ include/net/cfg80211.h | 1 +
+ net/mac80211/main.c    | 3 +++
+ net/mac80211/pm.c      | 1 +
+ net/mac80211/util.c    | 7 ++++++-
+ net/wireless/core.c    | 4 ++--
+ net/wireless/reg.c     | 4 +++-
+ net/wireless/trace.h   | 1 +
+ 7 files changed, 17 insertions(+), 4 deletions(-)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index 993b9a1..862932a 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -4846,6 +4846,7 @@ struct cfg80211_ops {
+ 
+ 	int	(*start_radar_detection)(struct wiphy *wiphy,
+ 					 struct net_device *dev,
++					 unsigned int link_id,
+ 					 struct cfg80211_chan_def *chandef,
+ 					 u32 cac_time_ms, int link_id);
+ 	void	(*end_cac)(struct wiphy *wiphy,
+diff --git a/net/mac80211/main.c b/net/mac80211/main.c
+index ca371ef..1de7f1d 100644
+--- a/net/mac80211/main.c
++++ b/net/mac80211/main.c
+@@ -1095,6 +1095,8 @@ static int ieee80211_init_cipher_suites(struct ieee80211_local *local)
+ static bool
+ ieee80211_ifcomb_check(const struct ieee80211_iface_combination *c, int n_comb)
+ {
++	/* FIXME: currently skip all checks */
++#if 0
+ 	int i, j;
+ 
+ 	for (i = 0; i < n_comb; i++, c++) {
+@@ -1109,6 +1111,7 @@ ieee80211_ifcomb_check(const struct ieee80211_iface_combination *c, int n_comb)
+ 			    c->limits[j].max > 1)
+ 				return false;
+ 	}
++#endif
+ 
+ 	return true;
+ }
+diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
+index d823d58..e47c2c4 100644
+--- a/net/mac80211/pm.c
++++ b/net/mac80211/pm.c
+@@ -22,6 +22,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+ {
+ 	struct ieee80211_local *local = hw_to_local(hw);
+ 	struct ieee80211_sub_if_data *sdata;
++	struct ieee80211_chanctx *ctx;
+ 	struct sta_info *sta;
+ 
+ 	if (!local->open_count)
+diff --git a/net/mac80211/util.c b/net/mac80211/util.c
+index 7f93739..2d2b871 100644
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -3478,6 +3478,10 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
+ 			if (!link_data)
+ 				continue;
+ 
++			if (link_data->conf->chanreq.oper.chan &&
++			    link_data->conf->chanreq.oper.chan->band != NL80211_BAND_5GHZ)
++				continue;
++
+ 			wiphy_delayed_work_cancel(local->hw.wiphy,
+ 						  &link_data->dfs_cac_timer_work);
+ 
+@@ -3515,7 +3519,8 @@ void ieee80211_dfs_radar_detected_work(struct wiphy *wiphy,
+ 	INIT_LIST_HEAD(&radar_info_list);
+ 
+ 	list_for_each_entry(ctx, &local->chanctx_list, list) {
+-		if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER)
++		if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER ||
++		    !ctx->conf.def.chan || ctx->conf.def.chan->band != NL80211_BAND_5GHZ)
+ 			continue;
+ 
+ 		if (ctx->conf.radar_detected) {
+diff --git a/net/wireless/core.c b/net/wireless/core.c
+index 45e2d94..51fb382 100644
+--- a/net/wireless/core.c
++++ b/net/wireless/core.c
+@@ -632,10 +632,10 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
+ 		if (WARN_ON(!c->num_different_channels))
+ 			return -EINVAL;
+ 
+-		/* DFS only works on one channel. */
++		/* DFS only works on one channel.
+ 		if (WARN_ON(c->radar_detect_widths &&
+ 			    (c->num_different_channels > 1)))
+-			return -EINVAL;
++			return -EINVAL; */
+ 
+ 		if (WARN_ON(!c->n_limits))
+ 			return -EINVAL;
+diff --git a/net/wireless/reg.c b/net/wireless/reg.c
+index 1a393f3..39a456b 100644
+--- a/net/wireless/reg.c
++++ b/net/wireless/reg.c
+@@ -4246,13 +4246,15 @@ static void cfg80211_check_and_end_cac(struct cfg80211_registered_device *rdev)
+ 	 */
+ 	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ 		struct cfg80211_chan_def *chandef;
++		unsigned int link_id;
+ 
+ 		for_each_valid_link(wdev, link_id) {
+ 			if (!wdev->links[link_id].cac_started)
+ 				continue;
+ 
+ 			chandef = wdev_chandef(wdev, link_id);
+-			if (!chandef)
++			if (!chandef || !chandef->chan ||
++			    chandef->chan->band != NL80211_BAND_5GHZ)
+ 				continue;
+ 
+ 			if (!cfg80211_chandef_dfs_usable(&rdev->wiphy, chandef))
+diff --git a/net/wireless/trace.h b/net/wireless/trace.h
+index 21956c8..6ff6091 100644
+--- a/net/wireless/trace.h
++++ b/net/wireless/trace.h
+@@ -2677,6 +2677,7 @@ TRACE_EVENT(rdev_start_radar_detection,
+ 	TP_fast_assign(
+ 		WIPHY_ASSIGN;
+ 		NETDEV_ASSIGN;
++		__entry->link_id = link_id;
+ 		CHAN_DEF_ASSIGN(chandef);
+ 		__entry->cac_time_ms = cac_time_ms;
+ 		__entry->link_id = link_id;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0049-mtk-mac80211-remove-links-when-removing-AP_VLAN-inte.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0049-mtk-mac80211-remove-links-when-removing-AP_VLAN-inte.patch
deleted file mode 100644
index 9bbab60..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0049-mtk-mac80211-remove-links-when-removing-AP_VLAN-inte.patch
+++ /dev/null
@@ -1,92 +0,0 @@
-From c1553f36bfb1b80be2306a50d98c0a2d256725b7 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Tue, 6 Feb 2024 15:03:49 +0800
-Subject: [PATCH 49/61] mtk: mac80211: remove links when removing AP_VLAN
- interface
-
-Remove links information when removing AP_VLAN interface.
-Without this patch, there would be a kernel crash when station disconnect
-from AP.
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- net/mac80211/cfg.c   | 14 ++++++++++++++
- net/mac80211/iface.c |  4 ++--
- net/mac80211/mlme.c  |  1 -
- net/wireless/util.c  |  8 ++++++++
- 4 files changed, 24 insertions(+), 3 deletions(-)
-
-diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
-index 871623d..c3b9d10 100644
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -4929,6 +4929,20 @@ static void ieee80211_del_intf_link(struct wiphy *wiphy,
- {
- 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
- 
-+	if (wdev->iftype == NL80211_IFTYPE_AP_VLAN) {
-+		int i;
-+
-+		sdata->vif.valid_links = 0;
-+		sdata->vif.active_links = 0;
-+		sdata->vif.dormant_links = 0;
-+		for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
-+			rcu_assign_pointer(sdata->link[i], NULL);
-+			rcu_assign_pointer(sdata->vif.link_conf[i], NULL);
-+		}
-+
-+		return;
-+	}
-+
- 	lockdep_assert_wiphy(sdata->local->hw.wiphy);
- 
- 	ieee80211_vif_set_links(sdata, wdev->valid_links, 0);
-diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
-index be52a83..e41bf5c 100644
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -397,8 +397,8 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
- 					    sdata->link[i] == &sdata->deflink)
- 						continue;
- 
--					sdata->link[i] = nsdata->link[i];
--					sdata->vif.link_conf[i] = nsdata->vif.link_conf[i];
-+					rcu_assign_pointer(sdata->link[i], nsdata->link[i]);
-+					rcu_assign_pointer(sdata->vif.link_conf[i], nsdata->vif.link_conf[i]);
- 				}
- 			}
- 		}
-diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
-index e4564de..ebdcf57 100644
---- a/net/mac80211/mlme.c
-+++ b/net/mac80211/mlme.c
-@@ -1829,7 +1829,6 @@ void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
- {
- 	struct sk_buff *skb;
- 	struct ieee80211_hdr *nullfunc;
--	u8 assoc_link_id = ifmgd->assoc_data->assoc_link_id;
- 	__le16 fc;
- 
- 	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
-diff --git a/net/wireless/util.c b/net/wireless/util.c
-index 2bde8a3..d03f612 100644
---- a/net/wireless/util.c
-+++ b/net/wireless/util.c
-@@ -2826,6 +2826,14 @@ void cfg80211_remove_links(struct wireless_dev *wdev)
- {
- 	unsigned int link_id;
- 
-+	if (wdev->iftype == NL80211_IFTYPE_AP_VLAN) {
-+		struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
-+
-+		if (rdev->ops->del_intf_link)
-+			rdev->ops->del_intf_link(&rdev->wiphy, wdev, 0);
-+
-+		return;
-+	}
- 	/*
- 	 * links are controlled by upper layers (userspace/cfg)
- 	 * only for AP mode, so only remove them here for AP
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0050-mtk-mac80211-add-wds-mlo-support.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0050-mtk-mac80211-add-wds-mlo-support.patch
new file mode 100644
index 0000000..7ef9758
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0050-mtk-mac80211-add-wds-mlo-support.patch
@@ -0,0 +1,304 @@
+From 6dd24bc475be0ba5ca1c4dfd717ac21bc2427430 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Fri, 19 Jan 2024 15:22:00 +0800
+Subject: [PATCH 50/89] mtk: mac80211: add wds mlo support
+
+Support WDS mode when using MLO.
+1. Remove use_4addr check.
+2. Copy link information to AP_VLAN interface.
+3. Fill 4addr nullfunc by mld address.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+
+1. For the MLO AP_VLAN interface, it would use the same link_data and
+bss_conf as AP interface. AP interface would maintain the bss_conf
+and link_data so AP_VLAN interface should not modify them.
+2. ieee80211_check_concurrent_iface is used to check if interface
+setting are valid so copy AP link information to AP_VLAN in
+ieee80211_do_open.
+3. Assign station's valid links to AP_VLAN's valid links so the iw
+command can show correct link information.
+4. Reassign AP_VLAN link information when AP set links.
+5. Use AP's sdata in ieee80211_sta_remove_link to prevent kernel warning
+in drv_change_sta_links.
+
+The link->reserved of the AP VLAN might be used in the following commit
+change.
+However, the link->reserved of the AP VLAN is not initialized and not
+assigned during the channel switch, so we should use the link->reserved
+of the AP instead.
+Without this fix, wds ap & sta will crash during the channel switch.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ net/mac80211/cfg.c         | 13 +++++--------
+ net/mac80211/chan.c        | 18 ++++++++++++++++--
+ net/mac80211/ieee80211_i.h |  2 ++
+ net/mac80211/iface.c       | 10 +++-------
+ net/mac80211/link.c        | 31 +++++++++++++++++++++++++++++++
+ net/mac80211/mlme.c        | 15 +++++++++------
+ net/mac80211/sta_info.c    |  7 ++++++-
+ 7 files changed, 72 insertions(+), 24 deletions(-)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 0b7763d..87066e1 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -222,6 +222,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
+ 
+ 	if (type == NL80211_IFTYPE_AP_VLAN && params->use_4addr == 0) {
+ 		RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
++		sdata->wdev.valid_links = 0;
+ 		ieee80211_check_fast_rx_iface(sdata);
+ 	} else if (type == NL80211_IFTYPE_STATION && params->use_4addr >= 0) {
+ 		struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+@@ -229,10 +230,6 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
+ 		if (params->use_4addr == ifmgd->use_4addr)
+ 			return 0;
+ 
+-		/* FIXME: no support for 4-addr MLO yet */
+-		if (ieee80211_vif_is_mld(&sdata->vif))
+-			return -EOPNOTSUPP;
+-
+ 		sdata->u.mgd.use_4addr = params->use_4addr;
+ 		if (!ifmgd->associated)
+ 			return 0;
+@@ -2211,11 +2208,14 @@ static int ieee80211_change_station(struct wiphy *wiphy,
+ 			rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
+ 			__ieee80211_check_fast_rx_iface(vlansdata);
+ 			drv_sta_set_4addr(local, sta->sdata, &sta->sta, true);
++			vlansdata->wdev.valid_links = sta->sta.valid_links;
+ 		}
+ 
+ 		if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
+-		    sta->sdata->u.vlan.sta)
++		    sta->sdata->u.vlan.sta) {
+ 			RCU_INIT_POINTER(sta->sdata->u.vlan.sta, NULL);
++			sta->sdata->wdev.valid_links = 0;
++		}
+ 
+ 		if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+ 			ieee80211_vif_dec_num_mcast(sta->sdata);
+@@ -4970,9 +4970,6 @@ static int ieee80211_add_intf_link(struct wiphy *wiphy,
+ 
+ 	lockdep_assert_wiphy(sdata->local->hw.wiphy);
+ 
+-	if (wdev->use_4addr)
+-		return -EOPNOTSUPP;
+-
+ 	return ieee80211_vif_set_links(sdata, wdev->valid_links, 0);
+ }
+ 
+diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
+index 6041735..608ed52 100644
+--- a/net/mac80211/chan.c
++++ b/net/mac80211/chan.c
+@@ -452,6 +452,16 @@ static void ieee80211_chan_bw_change(struct ieee80211_local *local,
+ 			else
+ 				new_chandef = &link_conf->chanreq.oper;
+ 
++			/* Access the reserved chanreq of the AP when it is AP VLAN */
++			if (reserved && sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
++				struct ieee80211_sub_if_data *ap;
++				struct ieee80211_link_data *ap_link;
++
++				ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
++				ap_link = rcu_dereference(ap->link[link_id]);
++				new_chandef = &ap_link->reserved.oper;
++			}
++
+ 			new_sta_bw = _ieee80211_sta_cur_vht_bw(link_sta,
+ 							       new_chandef);
+ 
+@@ -1015,7 +1025,8 @@ __ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link,
+ 	struct ieee80211_sub_if_data *vlan;
+ 	struct ieee80211_chanctx_conf *conf;
+ 
+-	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
++	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP) ||
++	    ieee80211_vif_is_mld(&sdata->vif))
+ 		return;
+ 
+ 	lockdep_assert_wiphy(local->hw.wiphy);
+@@ -1271,7 +1282,7 @@ ieee80211_link_update_chanreq(struct ieee80211_link_data *link,
+ 
+ 	link->conf->chanreq = *chanreq;
+ 
+-	if (sdata->vif.type != NL80211_IFTYPE_AP)
++	if (sdata->vif.type != NL80211_IFTYPE_AP || ieee80211_vif_is_mld(&sdata->vif))
+ 		return;
+ 
+ 	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
+@@ -2118,6 +2129,9 @@ void ieee80211_link_vlan_copy_chanctx(struct ieee80211_link_data *link)
+ 
+ 	ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
+ 
++	if (ieee80211_vif_is_mld(&ap->vif))
++		return;
++
+ 	ap_conf = wiphy_dereference(local->hw.wiphy,
+ 				    ap->vif.link_conf[link_id]);
+ 	conf = wiphy_dereference(local->hw.wiphy,
+diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
+index bea5058..d18f049 100644
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -2065,6 +2065,8 @@ void ieee80211_link_init(struct ieee80211_sub_if_data *sdata,
+ void ieee80211_link_stop(struct ieee80211_link_data *link);
+ int ieee80211_vif_set_links(struct ieee80211_sub_if_data *sdata,
+ 			    u16 new_links, u16 dormant_links);
++void __ieee80211_copy_links_to_vlan(struct ieee80211_sub_if_data *vlan,
++				    struct ieee80211_sub_if_data *ap);
+ static inline void ieee80211_vif_clear_links(struct ieee80211_sub_if_data *sdata)
+ {
+ 	ieee80211_vif_set_links(sdata, 0, 0);
+diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
+index c454826..9b95230 100644
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -381,13 +381,6 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
+ 							nsdata->vif.type))
+ 				return -ENOTUNIQ;
+ 
+-			/* No support for VLAN with MLO yet */
+-			if (iftype == NL80211_IFTYPE_AP_VLAN &&
+-			    sdata->wdev.use_4addr &&
+-			    nsdata->vif.type == NL80211_IFTYPE_AP &&
+-			    nsdata->vif.valid_links)
+-				return -EOPNOTSUPP;
+-
+ 			/*
+ 			 * can only add VLANs to enabled APs
+ 			 */
+@@ -1266,6 +1259,9 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
+ 		       sizeof(sdata->vif.hw_queue));
+ 		sdata->vif.bss_conf.chanreq = master->vif.bss_conf.chanreq;
+ 
++		if (ieee80211_vif_is_mld(&master->vif))
++			__ieee80211_copy_links_to_vlan(sdata, master);
++
+ 		sdata->crypto_tx_tailroom_needed_cnt +=
+ 			master->crypto_tx_tailroom_needed_cnt;
+ 
+diff --git a/net/mac80211/link.c b/net/mac80211/link.c
+index f77dab2..11502da 100644
+--- a/net/mac80211/link.c
++++ b/net/mac80211/link.c
+@@ -306,6 +306,36 @@ deinit:
+ 	return ret;
+ }
+ 
++void __ieee80211_copy_links_to_vlan(struct ieee80211_sub_if_data *vlan,
++				    struct ieee80211_sub_if_data *ap)
++{
++	int i;
++
++	vlan->vif.valid_links = ap->vif.valid_links;
++	vlan->vif.active_links = ap->vif.active_links;
++	vlan->vif.dormant_links = ap->vif.dormant_links;
++	memcpy(vlan->wdev.links, ap->wdev.links, sizeof(vlan->wdev.links));
++
++	for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
++		rcu_assign_pointer(vlan->link[i], ap->link[i]);
++		rcu_assign_pointer(vlan->vif.link_conf[i], ap->vif.link_conf[i]);
++	}
++}
++
++static void ieee80211_copy_links_to_vlan(struct ieee80211_sub_if_data *sdata)
++{
++	struct ieee80211_sub_if_data *vlan;
++
++	if (sdata->vif.type != NL80211_IFTYPE_AP || !ieee80211_vif_is_mld(&sdata->vif))
++		return;
++
++	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
++		__ieee80211_copy_links_to_vlan(vlan, sdata);
++		/* Todo: modify it when reconfiguration */
++		vlan->wdev.valid_links &= sdata->wdev.valid_links;
++	}
++}
++
+ int ieee80211_vif_set_links(struct ieee80211_sub_if_data *sdata,
+ 			    u16 new_links, u16 dormant_links)
+ {
+@@ -314,6 +344,7 @@ int ieee80211_vif_set_links(struct ieee80211_sub_if_data *sdata,
+ 
+ 	ret = ieee80211_vif_update_links(sdata, links, new_links,
+ 					 dormant_links);
++	ieee80211_copy_links_to_vlan(sdata);
+ 	ieee80211_free_links(sdata, links);
+ 
+ 	return ret;
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index ebd4d74..c7d08fb 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -2047,6 +2047,7 @@ void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
+ {
+ 	struct sk_buff *skb;
+ 	struct ieee80211_hdr *nullfunc;
++	u8 assoc_link_id = ifmgd->assoc_data->assoc_link_id;
+ 	__le16 fc;
+ 
+ 	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
+@@ -2062,11 +2063,17 @@ void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
+ 	fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
+ 			 IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
+ 	nullfunc->frame_control = fc;
+-	memcpy(nullfunc->addr1, sdata->deflink.u.mgd.bssid, ETH_ALEN);
+ 	memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);
+-	memcpy(nullfunc->addr3, sdata->deflink.u.mgd.bssid, ETH_ALEN);
+ 	memcpy(nullfunc->addr4, sdata->vif.addr, ETH_ALEN);
+ 
++	if (ieee80211_vif_is_mld(&sdata->vif)) {
++		memcpy(nullfunc->addr1, sdata->vif.cfg.ap_addr, ETH_ALEN);
++		memcpy(nullfunc->addr3, sdata->vif.cfg.ap_addr, ETH_ALEN);
++	} else {
++		memcpy(nullfunc->addr1, sdata->deflink.u.mgd.bssid, ETH_ALEN);
++		memcpy(nullfunc->addr3, sdata->deflink.u.mgd.bssid, ETH_ALEN);
++	}
++
+ 	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+ 	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
+ 	ieee80211_tx_skb(sdata, skb);
+@@ -8819,10 +8826,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
+ 	for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++)
+ 		size += req->links[i].elems_len;
+ 
+-	/* FIXME: no support for 4-addr MLO yet */
+-	if (sdata->u.mgd.use_4addr && req->link_id >= 0)
+-		return -EOPNOTSUPP;
+-
+ 	assoc_data = kzalloc(size, GFP_KERNEL);
+ 	if (!assoc_data)
+ 		return -ENOMEM;
+diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
+index 83e98c7..57a2809 100644
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -1275,8 +1275,10 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
+ 		drv_sta_pre_rcu_remove(local, sta->sdata, sta);
+ 
+ 	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
+-	    rcu_access_pointer(sdata->u.vlan.sta) == sta)
++	    rcu_access_pointer(sdata->u.vlan.sta) == sta) {
+ 		RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
++		sdata->wdev.valid_links = 0;
++	}
+ 
+ 	return 0;
+ }
+@@ -3013,6 +3015,9 @@ void ieee80211_sta_remove_link(struct sta_info *sta, unsigned int link_id)
+ 
+ 	sta->sta.valid_links &= ~BIT(link_id);
+ 
++	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN && sdata->bss)
++		sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
++
+ 	if (!WARN_ON(!test_sta_flag(sta, WLAN_STA_INSERTED)))
+ 		drv_change_sta_links(sdata->local, sdata, &sta->sta,
+ 				     old_links, sta->sta.valid_links);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0050-mtk-mac80211-fix-AP-mgmt-not-encrypted-in-WDS-mode-w.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0050-mtk-mac80211-fix-AP-mgmt-not-encrypted-in-WDS-mode-w.patch
deleted file mode 100644
index 41f46d6..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0050-mtk-mac80211-fix-AP-mgmt-not-encrypted-in-WDS-mode-w.patch
+++ /dev/null
@@ -1,37 +0,0 @@
-From d90c8383297753b4d89012b6b5e5760e919b6503 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Thu, 25 Jan 2024 14:07:23 +0800
-Subject: [PATCH 50/61] mtk: mac80211: fix AP mgmt not encrypted in WDS mode
- with PMF on
-
-In ieee80211_tx_prepare(), if tx->sta is still NULL after calling
-sta_info_get(), the skb might be mgmt for WDS peer, so sta_info_get_bss()
-if called to find sta from AP_VLAN, and then interface type & 4-addr
-using is checked.
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- net/mac80211/tx.c | 7 +++++++
- 1 file changed, 7 insertions(+)
-
-diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
-index e72bb7e..2808bc2 100644
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -1234,6 +1234,13 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
- 		if (!tx->sta && !is_multicast_ether_addr(hdr->addr1)) {
- 			tx->sta = sta_info_get(sdata, hdr->addr1);
- 			aggr_check = true;
-+
-+			if (!tx->sta) {
-+				tx->sta = sta_info_get_bss(sdata, hdr->addr1);
-+				if (!tx->sta || !tx->sta->sdata->wdev.use_4addr ||
-+				    tx->sta->sdata->vif.type != NL80211_IFTYPE_AP_VLAN)
-+					tx->sta = NULL;
-+			}
- 		}
- 	}
- 
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0051-mtk-mac80211-fix-ieee80211_ht_cap_ie_to_sta_ht_cap-w.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0051-mtk-mac80211-fix-ieee80211_ht_cap_ie_to_sta_ht_cap-w.patch
deleted file mode 100644
index 70677dd..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0051-mtk-mac80211-fix-ieee80211_ht_cap_ie_to_sta_ht_cap-w.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From 53ca97bf26ea7ce77d2cf34b90349e08d2569326 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Thu, 15 Feb 2024 14:30:02 +0800
-Subject: [PATCH 51/61] mtk: mac80211: fix ieee80211_ht_cap_ie_to_sta_ht_cap
- warn on
-
-Fix ieee80211_ht_cap_ie_to_sta_ht_cap warning.
-For MLD with a 2/5G primary link, auth/assoc is done in the 2G or 5G link.
-Therefore, 6G link will enter ieee80211_ht_cap_ie_to_sta_ht_cap, as elems->ht_cap_elem of 2/5G is NOT NULL.
-This should be avoided; otherwise, if 6G is bw 320, then the warning will be triggered.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- net/mac80211/mlme.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
-index ebdcf57..b9d10e9 100644
---- a/net/mac80211/mlme.c
-+++ b/net/mac80211/mlme.c
-@@ -4398,7 +4398,8 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
- 	sband = local->hw.wiphy->bands[link->conf->chanreq.oper.chan->band];
- 
- 	/* Set up internal HT/VHT capabilities */
--	if (elems->ht_cap_elem && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT)
-+	if (elems->ht_cap_elem && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT &&
-+	    !is_6ghz)
- 		ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
- 						  elems->ht_cap_elem,
- 						  link_sta);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0051-mtk-mac80211-fix-ieee80211_probe_client-warning.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0051-mtk-mac80211-fix-ieee80211_probe_client-warning.patch
new file mode 100644
index 0000000..7514a82
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0051-mtk-mac80211-fix-ieee80211_probe_client-warning.patch
@@ -0,0 +1,37 @@
+From 0a76f35b9b5cbd42f5b7a6d5fafb19d787a14e1d Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 29 Jan 2024 18:37:15 +0800
+Subject: [PATCH 51/89] mtk: mac80211: fix ieee80211_probe_client warning
+
+Only get chanctx for non-mld VIF.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ net/mac80211/cfg.c | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 87066e1..e7c0d24 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -4211,11 +4211,13 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
+ 	qos = sta->sta.wme;
+ 
+ 	chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
+-	if (WARN_ON(!chanctx_conf)) {
+-		ret = -EINVAL;
+-		goto unlock;
++	if (!ieee80211_vif_is_mld(&sdata->vif)) {
++		if (WARN_ON(!chanctx_conf)) {
++			ret = -EINVAL;
++			goto unlock;
++		}
+ 	}
+-	band = chanctx_conf->def.chan->band;
++	band = chanctx_conf ? chanctx_conf->def.chan->band : 0;
+ 
+ 	if (qos) {
+ 		fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0052-mtk-mac80211-fix-mac-address-to-support-hw-path-in-s.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0052-mtk-mac80211-fix-mac-address-to-support-hw-path-in-s.patch
deleted file mode 100644
index 74531ef..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0052-mtk-mac80211-fix-mac-address-to-support-hw-path-in-s.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-From 93ef1345d2859a21daf239a6c931b16e46286d59 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Fri, 16 Feb 2024 17:38:22 +0800
-Subject: [PATCH 52/61] mtk: mac80211: fix mac address to support hw path in
- station mode
-
-Use AP's MLD address instead of using deflink.
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- net/mac80211/iface.c | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
-diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
-index e41bf5c..b267b21 100644
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -987,7 +987,10 @@ static int ieee80211_netdev_fill_forward_path(struct net_device_path_ctx *ctx,
- 			}
- 		}
- 
--		sta = sta_info_get(sdata, sdata->deflink.u.mgd.bssid);
-+		if (ieee80211_vif_is_mld(&sdata->vif))
-+			sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr);
-+		else
-+			sta = sta_info_get(sdata, sdata->deflink.u.mgd.bssid);
- 		break;
- 	default:
- 		goto out;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0052-mtk-mac80211-remove-link-0-warn-on-in-rate_control_r.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0052-mtk-mac80211-remove-link-0-warn-on-in-rate_control_r.patch
new file mode 100644
index 0000000..5711846
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0052-mtk-mac80211-remove-link-0-warn-on-in-rate_control_r.patch
@@ -0,0 +1,42 @@
+From 07204d9541bd6b6eff3b77d7698bf406e546d267 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 31 Jan 2024 11:40:28 +0800
+Subject: [PATCH 52/89] mtk: mac80211: remove link != 0 warn on in
+ rate_control_rate_update for mlo channel switch
+
+Remove link warning for mlo channel switch
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ net/mac80211/rate.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
+index 7c31a3c..c287576 100644
+--- a/net/mac80211/rate.c
++++ b/net/mac80211/rate.c
+@@ -100,13 +100,19 @@ void rate_control_rate_update(struct ieee80211_local *local,
+ 	struct ieee80211_sta *ista = &sta->sta;
+ 	void *priv_sta = sta->rate_ctrl_priv;
+ 	struct ieee80211_chanctx_conf *chanctx_conf;
++	struct ieee80211_link_data *link;
+ 
+-	WARN_ON(link_id != 0);
++	// WARN_ON(link_id != 0);
+ 
+ 	if (ref && ref->ops->rate_update) {
+ 		rcu_read_lock();
+ 
+-		chanctx_conf = rcu_dereference(sta->sdata->vif.bss_conf.chanctx_conf);
++		link = rcu_dereference(sta->sdata->link[link_id]);
++		if (!link) {
++			rcu_read_unlock();
++			return;
++		}
++		chanctx_conf = rcu_dereference(link->conf->chanctx_conf);
+ 		if (WARN_ON(!chanctx_conf)) {
+ 			rcu_read_unlock();
+ 			return;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0053-mtk-mac80211-fix-radar-required-of-link-issue-in-res.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0053-mtk-mac80211-fix-radar-required-of-link-issue-in-res.patch
new file mode 100644
index 0000000..680e21c
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0053-mtk-mac80211-fix-radar-required-of-link-issue-in-res.patch
@@ -0,0 +1,39 @@
+From 19524840c953100d64d44763576b75a30377f3bf Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 1 Feb 2024 17:46:49 +0800
+Subject: [PATCH 53/89] mtk: mac80211: fix radar required of link issue in
+ reserve_reassign and reserve_assign
+
+link->radar_required is not updated in
+ieee80211_link_use_reserved_assign & ieee80211_link_use_reserved_reassign
+This will lead to DFS RDD init incomplete (RDD_CAC_START, RDD_CAC_END &
+RDD_DET_MODE is not set to fw)
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ net/mac80211/chan.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
+index 608ed52..bdff227 100644
+--- a/net/mac80211/chan.c
++++ b/net/mac80211/chan.c
+@@ -1337,6 +1337,7 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link)
+ 	if (link_conf->chanreq.oper.width != link->reserved.oper.width)
+ 		changed = BSS_CHANGED_BANDWIDTH;
+ 
++	link->radar_required = link->reserved_radar_required;
+ 	ieee80211_link_update_chanreq(link, &link->reserved);
+ 
+ 	_ieee80211_change_chanctx(local, new_ctx, old_ctx, chanreq, link);
+@@ -1418,6 +1419,7 @@ ieee80211_link_use_reserved_assign(struct ieee80211_link_data *link)
+ 	list_del(&link->reserved_chanctx_list);
+ 	link->reserved_chanctx = NULL;
+ 
++	link->radar_required = link->reserved_radar_required;
+ 	err = ieee80211_assign_link_chanctx(link, new_ctx, false);
+ 	if (err) {
+ 		if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0053-mtk-mac80211-workaround-for-configuring-txpower-in-m.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0053-mtk-mac80211-workaround-for-configuring-txpower-in-m.patch
deleted file mode 100644
index 40da0b4..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0053-mtk-mac80211-workaround-for-configuring-txpower-in-m.patch
+++ /dev/null
@@ -1,164 +0,0 @@
-From 23f6000da41a1c393b3c2e85554e6c8da4d728a2 Mon Sep 17 00:00:00 2001
-From: Allen Ye <allen.ye@mediatek.com>
-Date: Thu, 22 Feb 2024 15:21:49 +0800
-Subject: [PATCH 53/61] mtk: mac80211: workaround for configuring txpower in
- mld ap
-
-As for mt76 design, we expect to set txpower per link. So, we add
-another parameter to ieee80211_recalc_txpower function to set
-txpower to per link. For the functions that mac80211 don't pass link
-id to which, we specify to use the FIRST link as the parameter.
-
-Apply the patch will make uci and iw set txpower commamd only effect
-the link which id is 0 when we enable mld AP.
-
----
- net/mac80211/cfg.c         | 15 ++++++++++++---
- net/mac80211/chan.c        |  4 ++--
- net/mac80211/ieee80211_i.h |  5 +++--
- net/mac80211/iface.c       | 15 ++++++++-------
- net/mac80211/mlme.c        |  2 +-
- 5 files changed, 26 insertions(+), 15 deletions(-)
-
-diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
-index c3b9d10..cca3e08 100644
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -3059,7 +3059,11 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
- 			sdata->vif.bss_conf.txpower_type = txp_type;
- 		}
- 
--		ieee80211_recalc_txpower(sdata, update_txp_type);
-+		/* Due to mac80211 not pass link id to here, use first link for now */
-+		if (ieee80211_vif_is_mld(&sdata->vif))
-+			ieee80211_recalc_txpower(sdata, update_txp_type, sdata->link[0]);
-+		else
-+			ieee80211_recalc_txpower(sdata, update_txp_type, &sdata->deflink);
- 
- 		return 0;
- 	}
-@@ -3090,7 +3094,12 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
- 	list_for_each_entry(sdata, &local->interfaces, list) {
- 		if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
- 			continue;
--		ieee80211_recalc_txpower(sdata, update_txp_type);
-+		/* Due to mac80211 not pass link id to here, use first link for now */
-+		if (ieee80211_vif_is_mld(&sdata->vif))
-+			ieee80211_recalc_txpower(sdata, update_txp_type, sdata->link[0]);
-+		else
-+			ieee80211_recalc_txpower(sdata, update_txp_type, &sdata->deflink);
-+
- 	}
- 
- 	if (has_monitor) {
-@@ -3102,7 +3111,7 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
- 				update_txp_type = true;
- 			sdata->vif.bss_conf.txpower_type = txp_type;
- 
--			ieee80211_recalc_txpower(sdata, update_txp_type);
-+			ieee80211_recalc_txpower(sdata, update_txp_type, &sdata->deflink);
- 		}
- 	}
- 
-diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
-index ac22524..f09cac4 100644
---- a/net/mac80211/chan.c
-+++ b/net/mac80211/chan.c
-@@ -842,7 +842,7 @@ out:
- 	}
- 
- 	if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) {
--		ieee80211_recalc_txpower(sdata, false);
-+		ieee80211_recalc_txpower(sdata, false, link);
- 		ieee80211_recalc_chanctx_min_def(local, new_ctx, NULL);
- 	}
- 
-@@ -1570,7 +1570,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
- 								  link,
- 								  changed);
- 
--			ieee80211_recalc_txpower(sdata, false);
-+			ieee80211_recalc_txpower(sdata, false, link);
- 		}
- 
- 		ieee80211_recalc_chanctx_chantype(local, ctx);
-diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
-index 608e442..6f1b783 100644
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -2023,9 +2023,10 @@ void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata);
- int ieee80211_add_virtual_monitor(struct ieee80211_local *local);
- void ieee80211_del_virtual_monitor(struct ieee80211_local *local);
- 
--bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
-+bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
-+				struct ieee80211_link_data *link);
- void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
--			      bool update_bss);
-+			      bool update_bss, struct ieee80211_link_data *link);
- void ieee80211_recalc_offload(struct ieee80211_local *local);
- 
- static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
-diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
-index b267b21..5070c24 100644
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -44,13 +44,14 @@
- 
- static void ieee80211_iface_work(struct wiphy *wiphy, struct wiphy_work *work);
- 
--bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
-+bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
-+				struct ieee80211_link_data *link)
- {
- 	struct ieee80211_chanctx_conf *chanctx_conf;
- 	int power;
- 
- 	rcu_read_lock();
--	chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
-+	chanctx_conf = rcu_dereference(link->conf->chanctx_conf);
- 	if (!chanctx_conf) {
- 		rcu_read_unlock();
- 		return false;
-@@ -65,8 +66,8 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
- 	if (sdata->deflink.ap_power_level != IEEE80211_UNSET_POWER_LEVEL)
- 		power = min(power, sdata->deflink.ap_power_level);
- 
--	if (power != sdata->vif.bss_conf.txpower) {
--		sdata->vif.bss_conf.txpower = power;
-+	if (power != link->conf->txpower) {
-+		link->conf->txpower = power;
- 		ieee80211_hw_config(sdata->local, 0);
- 		return true;
- 	}
-@@ -75,11 +76,11 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
- }
- 
- void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
--			      bool update_bss)
-+			      bool update_bss, struct ieee80211_link_data *link)
- {
--	if (__ieee80211_recalc_txpower(sdata) ||
-+	if (__ieee80211_recalc_txpower(sdata, link) ||
- 	    (update_bss && ieee80211_sdata_running(sdata)))
--		ieee80211_link_info_change_notify(sdata, &sdata->deflink,
-+		ieee80211_link_info_change_notify(sdata, link,
- 						  BSS_CHANGED_TXPOWER);
- }
- 
-diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
-index b9d10e9..2efd98e 100644
---- a/net/mac80211/mlme.c
-+++ b/net/mac80211/mlme.c
-@@ -2362,7 +2362,7 @@ static u64 ieee80211_handle_pwr_constr(struct ieee80211_link_data *link,
- 	}
- 
- 	link->ap_power_level = new_ap_level;
--	if (__ieee80211_recalc_txpower(sdata))
-+	if (__ieee80211_recalc_txpower(sdata, link))
- 		return BSS_CHANGED_TXPOWER;
- 	return 0;
- }
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0054-mtk-cfg80211-rework-cac-started-cac-start-time-for-m.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0054-mtk-cfg80211-rework-cac-started-cac-start-time-for-m.patch
new file mode 100644
index 0000000..1abdf93
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0054-mtk-cfg80211-rework-cac-started-cac-start-time-for-m.patch
@@ -0,0 +1,176 @@
+From 0ccd5699b0f780baf72c943ea9f49d875fc199f5 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 1 Feb 2024 10:57:39 +0800
+Subject: [PATCH 54/89] mtk: cfg80211: rework cac started, cac start time for
+ multi-link support
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+During wifi reload, the dfs_cac_timer_work of a link in the pre-setup CAC stage
+will not be canceled in ieee80211_stop_ap since the AP of this
+link hasn't started up yet.
+If the link exists after the dfs_cac_timer_work that failed to be canceled
+reaches its timeout, then nothing will happen.
+On the contrary, if the link is released, then a call trace will occur
+since the dfs_cac_timer_work will access protected memory space.
+This explains why this call trace appears only when we switch from the
+MLD AP config to the legacy AP config, but not when we simply reload the MLD AP.
+
+The link in the pre-setup CAC stage will be released in
+ieee80211_tear_down_links when the interface is going to be deleted.
+Therefore, this commit adds wiphy_delayed_work_cancel for
+dfs_cac_timer_work in ieee80211_link_stop and ieee80211_do_stop.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ include/net/cfg80211.h |  1 +
+ net/mac80211/cfg.c     | 10 +++++-----
+ net/mac80211/iface.c   | 19 +++++++++++++------
+ net/mac80211/mlme.c    |  2 +-
+ net/mac80211/pm.c      |  1 -
+ net/wireless/debugfs.c | 10 ++++++----
+ net/wireless/reg.c     |  1 -
+ 7 files changed, 26 insertions(+), 18 deletions(-)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index 862932a..a104094 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -8762,6 +8762,7 @@ void cfg80211_sta_opmode_change_notify(struct net_device *dev, const u8 *mac,
+ /**
+  * cfg80211_cac_event - Channel availability check (CAC) event
+  * @netdev: network device
++ * @link_id: the link ID for MLO, must be 0 for non-MLO
+  * @chandef: chandef for the current channel
+  * @event: type of event
+  * @gfp: context flags
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index e7c0d24..f98b65f 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -5108,13 +5108,13 @@ ieee80211_skip_cac(struct wireless_dev *wdev, unsigned int link_id)
+ 
+ 	wiphy_delayed_work_cancel(sdata->local->hw.wiphy,
+ 				  &link->dfs_cac_timer_work);
+-	if (wdev->cac_started) {
++	if (wdev->cac_links & BIT(link_id)) {
+ 		ieee80211_link_release_channel(link);
+-		cac_time_ms = wdev->cac_time_ms;
+-		wdev->cac_start_time = jiffies -
+-				       msecs_to_jiffies(cac_time_ms + 1);
++		cac_time_ms = wdev->links[link_id].cac_time_ms;
++		wdev->links[link_id].cac_start_time = jiffies -
++						      msecs_to_jiffies(cac_time_ms + 1);
+ 		cfg80211_cac_event(wdev->netdev, &link->conf->chanreq.oper,
+-				   NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
++				   NL80211_RADAR_CAC_FINISHED, GFP_KERNEL, link_id);
+ 	}
+ }
+ 
+diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
+index 9b95230..09bb321 100644
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -462,6 +462,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
+ 	struct cfg80211_chan_def chandef;
+ 	bool cancel_scan;
+ 	struct cfg80211_nan_func *func;
++	struct ieee80211_link_data *link;
++	unsigned int link_id;
+ 
+ 	lockdep_assert_wiphy(local->hw.wiphy);
+ 
+@@ -545,13 +547,18 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
+ 	wiphy_delayed_work_cancel(local->hw.wiphy,
+ 				  &sdata->deflink.dfs_cac_timer_work);
+ 
+-	if (sdata->wdev.links[0].cac_started) {
+-		chandef = sdata->vif.bss_conf.chanreq.oper;
++	for_each_valid_link(&sdata->wdev, link_id) {
++		link = sdata_dereference(sdata->link[link_id], sdata);
++		if (!link)
++			continue;
++		chandef = link->conf->chanreq.oper;
+ 		WARN_ON(local->suspended);
+-		ieee80211_link_release_channel(&sdata->deflink);
+-		cfg80211_cac_event(sdata->dev, &chandef,
+-				   NL80211_RADAR_CAC_ABORTED,
+-				   GFP_KERNEL, 0);
++		wiphy_delayed_work_cancel(local->hw.wiphy, &link->dfs_cac_timer_work);
++		ieee80211_link_release_channel(link);
++		if (sdata->wdev.links[link_id].cac_started)
++			cfg80211_cac_event(sdata->dev, &chandef,
++					   NL80211_RADAR_CAC_ABORTED, GFP_KERNEL,
++					   link_id);
+ 	}
+ 
+ 	if (sdata->vif.type == NL80211_IFTYPE_AP) {
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index c7d08fb..13712a5 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -2608,7 +2608,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link,
+ 
+ 	cfg80211_sta_update_dfs_state(&sdata->wdev,
+ 				      &link->conf->chanreq.oper,
+-				      &link->csa_chanreq.oper,
++				      &link->csa.chanreq.oper,
+ 				      sdata->vif.cfg.assoc);
+ 
+ 	cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chanreq.oper,
+diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
+index e47c2c4..d823d58 100644
+--- a/net/mac80211/pm.c
++++ b/net/mac80211/pm.c
+@@ -22,7 +22,6 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+ {
+ 	struct ieee80211_local *local = hw_to_local(hw);
+ 	struct ieee80211_sub_if_data *sdata;
+-	struct ieee80211_chanctx *ctx;
+ 	struct sta_info *sta;
+ 
+ 	if (!local->open_count)
+diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c
+index 27b4608..a0398c7 100644
+--- a/net/wireless/debugfs.c
++++ b/net/wireless/debugfs.c
+@@ -187,10 +187,11 @@ static int dfs_status_read_wdev(struct wiphy *wiphy, struct wireless_dev *wdev,
+ 				if (remain_time > wait_time_ms)
+ 					remain_time = 0;
+ 			} else if (chan->dfs_state == NL80211_DFS_USABLE) {
+-				if (wdev->cac_started &&
++				if (wdev->links[link_id].cac_started &&
+ 				    cfg80211_is_sub_chan(chandef, chan, false)) {
+-					jiffies_passed = jiffies - wdev->cac_start_time;
+-					wait_time_ms = wdev->cac_time_ms;
++					jiffies_passed = jiffies -
++							 wdev->links[link_id].cac_start_time;
++					wait_time_ms = wdev->links[link_id].cac_time_ms;
+ 					remain_time = (wait_time_ms -
+ 						       jiffies_to_msecs(jiffies_passed));
+ 				}
+@@ -336,7 +337,8 @@ dfs_cac_skip(void *data, u64 val)
+ 					continue;
+ 
+ 				if (cfg80211_chandef_dfs_required(wiphy, c, wdev->iftype) > 0 &&
+-				    cfg80211_chandef_dfs_usable(wiphy, c) && wdev->cac_started) {
++				    cfg80211_chandef_dfs_usable(wiphy, c) &&
++				    wdev->links[link_id].cac_started) {
+ 					rdev_skip_cac(rdev, wdev, link_id);
+ 				}
+ 			}
+diff --git a/net/wireless/reg.c b/net/wireless/reg.c
+index 39a456b..9cd7fb2 100644
+--- a/net/wireless/reg.c
++++ b/net/wireless/reg.c
+@@ -4233,7 +4233,6 @@ EXPORT_SYMBOL(regulatory_pre_cac_allowed);
+ static void cfg80211_check_and_end_cac(struct cfg80211_registered_device *rdev)
+ {
+ 	struct wireless_dev *wdev;
+-	unsigned int link_id;
+ 
+ 	/* If we finished CAC or received radar, we should end any
+ 	 * CAC running on the same channels.
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0054-mtk-mac80211-send-broadcast-multicast-mgmt.-via-all-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0054-mtk-mac80211-send-broadcast-multicast-mgmt.-via-all-.patch
deleted file mode 100644
index 26c07c3..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0054-mtk-mac80211-send-broadcast-multicast-mgmt.-via-all-.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From 06d3d93fc257b9f8cef083b9b84711fc9c3cee19 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Wed, 21 Feb 2024 16:32:13 +0800
-Subject: [PATCH 54/61] mtk: mac80211: send broadcast/multicast mgmt. via all
- links.
-
-This patch makes broadcast/multicast mgmt. be sent via all links.
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Change-id: Id1bd06694a73cc29b3f571a57c4c864ee3742441
----
- net/mac80211/offchannel.c | 20 +++++++++++++++++++-
- 1 file changed, 19 insertions(+), 1 deletion(-)
-
-diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
-index 3ed5fc5..ec1d7a1 100644
---- a/net/mac80211/offchannel.c
-+++ b/net/mac80211/offchannel.c
-@@ -976,7 +976,25 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
- 	}
- 
- 	if (!need_offchan) {
--		ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
-+		unsigned long links = sdata->vif.active_links;
-+		if (is_multicast_ether_addr(mgmt->da) && hweight16(links) > 1) {
-+			unsigned int link;
-+			struct sk_buff *dskb;
-+
-+			for_each_set_bit(link, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
-+				dskb = skb_clone(skb, GFP_ATOMIC);
-+				if (dskb) {
-+					ieee80211_tx_skb_tid(sdata, dskb, 7, link);
-+				} else {
-+					ret = -ENOMEM;
-+					kfree_skb(skb);
-+					goto out_unlock;
-+				}
-+			}
-+			kfree_skb(skb);
-+		} else {
-+			ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
-+		}
- 		ret = 0;
- 		goto out_unlock;
- 	}
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0055-mtk-mac80211-fix-mlo-BW-160-channel-switch-issue.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0055-mtk-mac80211-fix-mlo-BW-160-channel-switch-issue.patch
deleted file mode 100644
index e210794..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0055-mtk-mac80211-fix-mlo-BW-160-channel-switch-issue.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From 3529be517dc9be0c911b06a1165ae80e3d051238 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 15 Mar 2024 14:34:11 +0800
-Subject: [PATCH 55/61] mtk: mac80211: fix mlo BW 160 channel switch issue
-
-The link_id argument for cfg80211_ch_switch_started_notify is missing
-after maintainer rebasing for chanreq
-The original commit has link_id instead of 0
-https://patchwork.kernel.org/project/linux-wireless/list/?series=821321&state=*
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- net/mac80211/cfg.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
-index cca3e08..856c956 100644
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -4045,7 +4045,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
- 	}
- 
- 	cfg80211_ch_switch_started_notify(sdata->dev,
--					  &link_data->csa_chanreq.oper, 0,
-+					  &link_data->csa_chanreq.oper, link_id,
- 					  params->count, params->block_tx);
- 
- 	if (changed) {
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0055-mtk-mac80211-remove-links-when-removing-AP_VLAN-inte.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0055-mtk-mac80211-remove-links-when-removing-AP_VLAN-inte.patch
new file mode 100644
index 0000000..ae4a70d
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0055-mtk-mac80211-remove-links-when-removing-AP_VLAN-inte.patch
@@ -0,0 +1,76 @@
+From 5d9b5ac643659b15be86a2ae1bdb84dabb56b762 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Tue, 6 Feb 2024 15:03:49 +0800
+Subject: [PATCH 55/89] mtk: mac80211: remove links when removing AP_VLAN
+ interface
+
+Remove links information when removing AP_VLAN interface.
+Without this patch, there would be a kernel crash when station disconnect
+from AP.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ net/mac80211/cfg.c  | 14 ++++++++++++++
+ net/mac80211/mlme.c |  1 -
+ net/wireless/util.c |  8 ++++++++
+ 3 files changed, 22 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index f98b65f..27afd90 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -4981,6 +4981,20 @@ static void ieee80211_del_intf_link(struct wiphy *wiphy,
+ {
+ 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ 
++	if (wdev->iftype == NL80211_IFTYPE_AP_VLAN) {
++		int i;
++
++		sdata->vif.valid_links = 0;
++		sdata->vif.active_links = 0;
++		sdata->vif.dormant_links = 0;
++		for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
++			rcu_assign_pointer(sdata->link[i], NULL);
++			rcu_assign_pointer(sdata->vif.link_conf[i], NULL);
++		}
++
++		return;
++	}
++
+ 	lockdep_assert_wiphy(sdata->local->hw.wiphy);
+ 
+ 	ieee80211_vif_set_links(sdata, wdev->valid_links, 0);
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 13712a5..91ba00d 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -2047,7 +2047,6 @@ void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
+ {
+ 	struct sk_buff *skb;
+ 	struct ieee80211_hdr *nullfunc;
+-	u8 assoc_link_id = ifmgd->assoc_data->assoc_link_id;
+ 	__le16 fc;
+ 
+ 	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
+diff --git a/net/wireless/util.c b/net/wireless/util.c
+index 9a7c3ad..eb2ded7 100644
+--- a/net/wireless/util.c
++++ b/net/wireless/util.c
+@@ -2852,6 +2852,14 @@ void cfg80211_remove_links(struct wireless_dev *wdev)
+ {
+ 	unsigned int link_id;
+ 
++	if (wdev->iftype == NL80211_IFTYPE_AP_VLAN) {
++		struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
++
++		if (rdev->ops->del_intf_link)
++			rdev->ops->del_intf_link(&rdev->wiphy, wdev, 0);
++
++		return;
++	}
+ 	/*
+ 	 * links are controlled by upper layers (userspace/cfg)
+ 	 * only for AP mode, so only remove them here for AP
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0056-mtk-mac80211-extend-IEEE80211_KEY_FLAG_GENERATE_MMIE.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0056-mtk-mac80211-extend-IEEE80211_KEY_FLAG_GENERATE_MMIE.patch
deleted file mode 100644
index a3cdf5e..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0056-mtk-mac80211-extend-IEEE80211_KEY_FLAG_GENERATE_MMIE.patch
+++ /dev/null
@@ -1,62 +0,0 @@
-From c2aed160872b89580f253fdfb2369e59951e3695 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Thu, 21 Mar 2024 10:58:59 +0800
-Subject: [PATCH 56/61] mtk: mac80211: extend IEEE80211_KEY_FLAG_GENERATE_MMIE
- to other ciphers
-
-This commit extends the flag IEEE80211_KEY_FLAG_GENERATE_MMIE to
-CMAC-256 and GMAC for the same reason that this flag was added.
-(a0b449: mac80211: add IEEE80211_KEY_FLAG_GENERATE_MMIE to ieee80211_key_flags)
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- net/mac80211/wpa.c | 12 ++++++++++--
- 1 file changed, 10 insertions(+), 2 deletions(-)
-
-diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
-index daf1bcc..e66f917 100644
---- a/net/mac80211/wpa.c
-+++ b/net/mac80211/wpa.c
-@@ -903,7 +903,8 @@ ieee80211_crypto_aes_cmac_256_encrypt(struct ieee80211_tx_data *tx)
- 
- 	info = IEEE80211_SKB_CB(skb);
- 
--	if (info->control.hw_key)
-+	if (info->control.hw_key &&
-+	    !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIE))
- 		return TX_CONTINUE;
- 
- 	if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
-@@ -919,6 +920,9 @@ ieee80211_crypto_aes_cmac_256_encrypt(struct ieee80211_tx_data *tx)
- 
- 	bip_ipn_set64(mmie->sequence_number, pn64);
- 
-+	if (info->control.hw_key)
-+		return TX_CONTINUE;
-+
- 	bip_aad(skb, aad);
- 
- 	/* MIC = AES-256-CMAC(IGTK, AAD || Management Frame Body || MMIE, 128)
-@@ -1048,7 +1052,8 @@ ieee80211_crypto_aes_gmac_encrypt(struct ieee80211_tx_data *tx)
- 
- 	info = IEEE80211_SKB_CB(skb);
- 
--	if (info->control.hw_key)
-+	if (info->control.hw_key &&
-+	    !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIE))
- 		return TX_CONTINUE;
- 
- 	if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
-@@ -1064,6 +1069,9 @@ ieee80211_crypto_aes_gmac_encrypt(struct ieee80211_tx_data *tx)
- 
- 	bip_ipn_set64(mmie->sequence_number, pn64);
- 
-+	if (info->control.hw_key)
-+		return TX_CONTINUE;
-+
- 	bip_aad(skb, aad);
- 
- 	hdr = (struct ieee80211_hdr *)skb->data;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0056-mtk-mac80211-fix-AP-mgmt-not-encrypted-in-WDS-mode-w.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0056-mtk-mac80211-fix-AP-mgmt-not-encrypted-in-WDS-mode-w.patch
new file mode 100644
index 0000000..d81b7a0
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0056-mtk-mac80211-fix-AP-mgmt-not-encrypted-in-WDS-mode-w.patch
@@ -0,0 +1,37 @@
+From 918df54f28978540816e00860677b87a36922c6a Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 25 Jan 2024 14:07:23 +0800
+Subject: [PATCH 56/89] mtk: mac80211: fix AP mgmt not encrypted in WDS mode
+ with PMF on
+
+In ieee80211_tx_prepare(), if tx->sta is still NULL after calling
+sta_info_get(), the skb might be mgmt for WDS peer, so sta_info_get_bss()
+if called to find sta from AP_VLAN, and then interface type & 4-addr
+using is checked.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ net/mac80211/tx.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
+index 9e95dbd..00294cc 100644
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -1239,6 +1239,13 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
+ 		if (!tx->sta && !is_multicast_ether_addr(hdr->addr1)) {
+ 			tx->sta = sta_info_get(sdata, hdr->addr1);
+ 			aggr_check = true;
++
++			if (!tx->sta) {
++				tx->sta = sta_info_get_bss(sdata, hdr->addr1);
++				if (!tx->sta || !tx->sta->sdata->wdev.use_4addr ||
++				    tx->sta->sdata->vif.type != NL80211_IFTYPE_AP_VLAN)
++					tx->sta = NULL;
++			}
+ 		}
+ 	}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0057-mtk-mac80211-fix-ieee80211_ht_cap_ie_to_sta_ht_cap-w.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0057-mtk-mac80211-fix-ieee80211_ht_cap_ie_to_sta_ht_cap-w.patch
new file mode 100644
index 0000000..1d5630a
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0057-mtk-mac80211-fix-ieee80211_ht_cap_ie_to_sta_ht_cap-w.patch
@@ -0,0 +1,33 @@
+From 93932d436b0f688fb57cad5a29562f8049542c52 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 15 Feb 2024 14:30:02 +0800
+Subject: [PATCH 57/89] mtk: mac80211: fix ieee80211_ht_cap_ie_to_sta_ht_cap
+ warn on
+
+Fix ieee80211_ht_cap_ie_to_sta_ht_cap warning.
+For MLD with a 2/5G primary link, auth/assoc is done in the 2G or 5G link.
+Therefore, 6G link will enter ieee80211_ht_cap_ie_to_sta_ht_cap, as elems->ht_cap_elem of 2/5G is NOT NULL.
+This should be avoided; otherwise, if 6G is bw 320, then the warning will be triggered.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ net/mac80211/mlme.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 91ba00d..ae171cd 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -4832,7 +4832,8 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
+ 	sband = local->hw.wiphy->bands[link->conf->chanreq.oper.chan->band];
+ 
+ 	/* Set up internal HT/VHT capabilities */
+-	if (elems->ht_cap_elem && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT)
++	if (elems->ht_cap_elem && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT &&
++	    !is_6ghz)
+ 		ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
+ 						  elems->ht_cap_elem,
+ 						  link_sta);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0057-mtk-wifi-mt76-mt7996-not-to-check-need_offchan-for-M.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0057-mtk-wifi-mt76-mt7996-not-to-check-need_offchan-for-M.patch
deleted file mode 100644
index 16278ac..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0057-mtk-wifi-mt76-mt7996-not-to-check-need_offchan-for-M.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From b311c85cdc3fe0a91d4b93628b6f559a2dfe3f5c Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Mon, 25 Mar 2024 18:59:35 +0800
-Subject: [PATCH 57/61] mtk: wifi: mt76: mt7996: not to check 'need_offchan'
- for MLD multicast mgmt.
-
-Multicast mgmt. sent by the MLD AP should be transmitted via all links,
-so it is not necessary to check 'need_offchan'.
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- net/mac80211/offchannel.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
-index ec1d7a1..a571be0 100644
---- a/net/mac80211/offchannel.c
-+++ b/net/mac80211/offchannel.c
-@@ -874,7 +874,8 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
- 	/* Check if the operating channel is the requested channel */
- 	if (!params->chan && mlo_sta) {
- 		need_offchan = false;
--	} else if (!need_offchan) {
-+	} else if (!need_offchan && !(ieee80211_vif_is_mld(&sdata->vif) &&
-+		   is_multicast_ether_addr(mgmt->da))) {
- 		struct ieee80211_chanctx_conf *chanctx_conf = NULL;
- 		int i;
- 
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0058-mtk-mac80211-fix-mac-address-to-support-hw-path-in-s.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0058-mtk-mac80211-fix-mac-address-to-support-hw-path-in-s.patch
new file mode 100644
index 0000000..083b961
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0058-mtk-mac80211-fix-mac-address-to-support-hw-path-in-s.patch
@@ -0,0 +1,32 @@
+From d192caa409dcd10acb6ecfdfac5c940db6c8e264 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Fri, 16 Feb 2024 17:38:22 +0800
+Subject: [PATCH 58/89] mtk: mac80211: fix mac address to support hw path in
+ station mode
+
+Use AP's MLD address instead of using deflink.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ net/mac80211/iface.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
+index 09bb321..3936181 100644
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -915,7 +915,10 @@ static int ieee80211_netdev_fill_forward_path(struct net_device_path_ctx *ctx,
+ 			}
+ 		}
+ 
+-		sta = sta_info_get(sdata, sdata->deflink.u.mgd.bssid);
++		if (ieee80211_vif_is_mld(&sdata->vif))
++			sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr);
++		else
++			sta = sta_info_get(sdata, sdata->deflink.u.mgd.bssid);
+ 		break;
+ 	default:
+ 		goto out;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0058-mtk-wifi-mt76-mt7996-assign-link-address-to-the-head.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0058-mtk-wifi-mt76-mt7996-assign-link-address-to-the-head.patch
deleted file mode 100644
index 136ef38..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0058-mtk-wifi-mt76-mt7996-assign-link-address-to-the-head.patch
+++ /dev/null
@@ -1,52 +0,0 @@
-From db3b035491cb7be00b1ac169863a172a5aebbacf Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Mon, 25 Mar 2024 16:26:34 +0800
-Subject: [PATCH 58/61] mtk: wifi: mt76: mt7996: assign link address to the
- header of broadcast mgmt.
-
-AAD calculation should use link addr as input for broadcast mgmt. skb.
-This commit assigns link address to the header of cloned broadcast mgmt.
-for the correct AAD calculation.
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- net/mac80211/offchannel.c | 16 +++++++++++++---
- 1 file changed, 13 insertions(+), 3 deletions(-)
-
-diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
-index a571be0..15948f9 100644
---- a/net/mac80211/offchannel.c
-+++ b/net/mac80211/offchannel.c
-@@ -981,16 +981,26 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
- 		if (is_multicast_ether_addr(mgmt->da) && hweight16(links) > 1) {
- 			unsigned int link;
- 			struct sk_buff *dskb;
-+			struct ieee80211_hdr *hdr;
-+			struct ieee80211_bss_conf *conf;
- 
- 			for_each_set_bit(link, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
-+				conf = rcu_dereference(sdata->vif.link_conf[link]);
-+				if (!conf)
-+					continue;
-+
- 				dskb = skb_clone(skb, GFP_ATOMIC);
--				if (dskb) {
--					ieee80211_tx_skb_tid(sdata, dskb, 7, link);
--				} else {
-+				if (!dskb) {
- 					ret = -ENOMEM;
- 					kfree_skb(skb);
- 					goto out_unlock;
- 				}
-+
-+				/* Assign link address */
-+				hdr = (void *)dskb->data;
-+				memcpy(hdr->addr2, conf->addr, ETH_ALEN);
-+				memcpy(hdr->addr3, conf->addr, ETH_ALEN);
-+				ieee80211_tx_skb_tid(sdata, dskb, 7, link);
- 			}
- 			kfree_skb(skb);
- 		} else {
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0059-mtk-mac80211-send-broadcast-multicast-mgmt.-via-all-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0059-mtk-mac80211-send-broadcast-multicast-mgmt.-via-all-.patch
new file mode 100644
index 0000000..12d35b2
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0059-mtk-mac80211-send-broadcast-multicast-mgmt.-via-all-.patch
@@ -0,0 +1,48 @@
+From 33536ba0cdb3937796ac410303f6cb9f2efd2965 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Wed, 21 Feb 2024 16:32:13 +0800
+Subject: [PATCH 59/89] mtk: mac80211: send broadcast/multicast mgmt. via all
+ links.
+
+This patch makes broadcast/multicast mgmt. be sent via all links.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Change-id: Id1bd06694a73cc29b3f571a57c4c864ee3742441
+---
+ net/mac80211/offchannel.c | 20 +++++++++++++++++++-
+ 1 file changed, 19 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
+index e014f1b..520e8bf 100644
+--- a/net/mac80211/offchannel.c
++++ b/net/mac80211/offchannel.c
+@@ -1019,7 +1019,25 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ 	}
+ 
+ 	if (!need_offchan) {
+-		ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
++		unsigned long links = sdata->vif.active_links;
++		if (is_multicast_ether_addr(mgmt->da) && hweight16(links) > 1) {
++			unsigned int link;
++			struct sk_buff *dskb;
++
++			for_each_set_bit(link, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
++				dskb = skb_clone(skb, GFP_ATOMIC);
++				if (dskb) {
++					ieee80211_tx_skb_tid(sdata, dskb, 7, link);
++				} else {
++					ret = -ENOMEM;
++					kfree_skb(skb);
++					goto out_unlock;
++				}
++			}
++			kfree_skb(skb);
++		} else {
++			ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
++		}
+ 		ret = 0;
+ 		goto out_unlock;
+ 	}
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0059-mtk-wifi-mt76-mt7996-Do-MLD-address-translation-befo.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0059-mtk-wifi-mt76-mt7996-Do-MLD-address-translation-befo.patch
deleted file mode 100644
index 7d99fae..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0059-mtk-wifi-mt76-mt7996-Do-MLD-address-translation-befo.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From 26fc78265ac13821bb7d075be2859a1f5896f924 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Tue, 26 Mar 2024 11:36:35 +0800
-Subject: [PATCH 59/61] mtk: wifi: mt76: mt7996: Do MLD address translation
- before STA process BMC mgmt. frame
-
-In the function ieee80211_prepare_and_rx_handle(), BMC mgmt. frames are
-not MLD translated since the AAD calculation needs the header being link
-addressed. However, after the AAD calculation, STA processes the mgmt.
-frames on an MLD level, and it fails to match the link address in the
-header with the self MLD address.
-
-This commit does MLD address translation again after the AAD calculation
-and before STA's mgmt. frames processing.
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- net/mac80211/mlme.c | 9 +++++++++
- 1 file changed, 9 insertions(+)
-
-diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
-index 2efd98e..e4d5eac 100644
---- a/net/mac80211/mlme.c
-+++ b/net/mac80211/mlme.c
-@@ -6931,6 +6931,15 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
- 			return;
- 	}
- 
-+	/* Do MLD address translation for Multicast/Broadcast frame. */
-+	if (is_multicast_ether_addr(mgmt->da) && !ieee80211_is_probe_resp(fc) &&
-+	    !ieee80211_is_beacon(fc)) {
-+		if (ether_addr_equal(mgmt->sa, link->conf->bssid))
-+			ether_addr_copy(mgmt->sa, sdata->vif.cfg.ap_addr);
-+		if (ether_addr_equal(mgmt->bssid, link->conf->bssid))
-+			ether_addr_copy(mgmt->bssid, sdata->vif.cfg.ap_addr);
-+	}
-+
- 	switch (fc & IEEE80211_FCTL_STYPE) {
- 	case IEEE80211_STYPE_BEACON:
- 		ieee80211_rx_mgmt_beacon(link, (void *)mgmt,
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0060-mtk-mac80211-not-to-check-need_offchan-for-MLD-multi.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0060-mtk-mac80211-not-to-check-need_offchan-for-MLD-multi.patch
new file mode 100644
index 0000000..014fca1
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0060-mtk-mac80211-not-to-check-need_offchan-for-MLD-multi.patch
@@ -0,0 +1,31 @@
+From ba7e99be3159a093c0632920b05799cd7fa269be Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 25 Mar 2024 18:59:35 +0800
+Subject: [PATCH 60/89] mtk: mac80211: not to check 'need_offchan' for MLD
+ multicast mgmt.
+
+Multicast mgmt. sent by the MLD AP should be transmitted via all links,
+so it is not necessary to check 'need_offchan'.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ net/mac80211/offchannel.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
+index 520e8bf..dd3ebee 100644
+--- a/net/mac80211/offchannel.c
++++ b/net/mac80211/offchannel.c
+@@ -907,7 +907,8 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ 	/* Check if the operating channel is the requested channel */
+ 	if (!params->chan && mlo_sta) {
+ 		need_offchan = false;
+-	} else if (!need_offchan) {
++	} else if (!need_offchan && !(ieee80211_vif_is_mld(&sdata->vif) &&
++		   is_multicast_ether_addr(mgmt->da))) {
+ 		struct ieee80211_chanctx_conf *chanctx_conf = NULL;
+ 		int i;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0060-mtk-wifi-mac80211-defer-enabling-beacon-for-MLD-AP.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0060-mtk-wifi-mac80211-defer-enabling-beacon-for-MLD-AP.patch
deleted file mode 100644
index 2dfd135..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0060-mtk-wifi-mac80211-defer-enabling-beacon-for-MLD-AP.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From cd576eae15b45ef97e3ee180482d2e884889fb09 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Fri, 12 Apr 2024 11:45:41 +0800
-Subject: [PATCH 60/61] mtk: wifi: mac80211: defer enabling beacon for MLD AP
-
-Do not enable beacon on the first beacon update (NL80211_CMD_NEW_BEACON)
-for MLD AP, let it start from the next beacon update
-(NL80211_CMD_SET_BEACON).
-This is used to make sure that MLD AP start beacon after all links
-finish settings.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- net/mac80211/cfg.c | 7 ++++++-
- 1 file changed, 6 insertions(+), 1 deletion(-)
-
-diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
-index 856c956..e091ccd 100644
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -1424,7 +1424,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
- 	}
- 
- 	link_conf->dtim_period = params->dtim_period;
--	link_conf->enable_beacon = true;
-+	link_conf->enable_beacon = !ieee80211_vif_is_mld(&sdata->vif);
- 	link_conf->allow_p2p_go_ps = sdata->vif.p2p;
- 	link_conf->twt_responder = params->twt_responder;
- 	link_conf->he_obss_pd = params->he_obss_pd;
-@@ -1491,6 +1491,11 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
- 	ieee80211_recalc_dtim(local, sdata);
- 	ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_SSID);
- 	ieee80211_link_info_change_notify(sdata, link, changed);
-+	/* for MLD AP, enable_beacon is false during the first beacon set,
-+	 * enable it after that. This allows userspace to control the
-+	 * beacon enable timing.
-+	 */
-+	link_conf->enable_beacon = true;
- 
- 	if (ieee80211_num_beaconing_links(sdata) <= 1)
- 		netif_carrier_on(dev);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0061-mtk-mac80211-assign-link-address-to-the-header-of-br.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0061-mtk-mac80211-assign-link-address-to-the-header-of-br.patch
new file mode 100644
index 0000000..06f48b1
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0061-mtk-mac80211-assign-link-address-to-the-header-of-br.patch
@@ -0,0 +1,52 @@
+From 699ee38bca47b22ee46e560587a3a0a5630075f3 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 25 Mar 2024 16:26:34 +0800
+Subject: [PATCH 61/89] mtk: mac80211: assign link address to the header of
+ broadcast mgmt
+
+AAD calculation should use link addr as input for broadcast mgmt. skb.
+This commit assigns link address to the header of cloned broadcast mgmt.
+for the correct AAD calculation.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ net/mac80211/offchannel.c | 16 +++++++++++++---
+ 1 file changed, 13 insertions(+), 3 deletions(-)
+
+diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
+index dd3ebee..9e4f26a 100644
+--- a/net/mac80211/offchannel.c
++++ b/net/mac80211/offchannel.c
+@@ -1024,16 +1024,26 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ 		if (is_multicast_ether_addr(mgmt->da) && hweight16(links) > 1) {
+ 			unsigned int link;
+ 			struct sk_buff *dskb;
++			struct ieee80211_hdr *hdr;
++			struct ieee80211_bss_conf *conf;
+ 
+ 			for_each_set_bit(link, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
++				conf = rcu_dereference(sdata->vif.link_conf[link]);
++				if (!conf)
++					continue;
++
+ 				dskb = skb_clone(skb, GFP_ATOMIC);
+-				if (dskb) {
+-					ieee80211_tx_skb_tid(sdata, dskb, 7, link);
+-				} else {
++				if (!dskb) {
+ 					ret = -ENOMEM;
+ 					kfree_skb(skb);
+ 					goto out_unlock;
+ 				}
++
++				/* Assign link address */
++				hdr = (void *)dskb->data;
++				memcpy(hdr->addr2, conf->addr, ETH_ALEN);
++				memcpy(hdr->addr3, conf->addr, ETH_ALEN);
++				ieee80211_tx_skb_tid(sdata, dskb, 7, link);
+ 			}
+ 			kfree_skb(skb);
+ 		} else {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0061-mtk-wifi-mac80211-fix-radar-trigger-issue-due-to-ref.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0061-mtk-wifi-mac80211-fix-radar-trigger-issue-due-to-ref.patch
deleted file mode 100644
index 8e19d75..0000000
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0061-mtk-wifi-mac80211-fix-radar-trigger-issue-due-to-ref.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From 017795da32681ed521917cf446ddacf5d33f649d Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Thu, 18 Apr 2024 18:08:48 +0800
-Subject: [PATCH 61/61] mtk: wifi: mac80211: fix radar trigger issue due to
- refactoring to single wiphy
-
-Since we change to single wiphy, we cannot directly return during cac cancel if the link conf in local->interfaces list is not for 5G band.
-local->interfaces might contain 2/5/6G sdata
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- net/mac80211/util.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/net/mac80211/util.c b/net/mac80211/util.c
-index 732232a..d030ebc 100644
---- a/net/mac80211/util.c
-+++ b/net/mac80211/util.c
-@@ -3469,7 +3469,7 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local, unsigned int link_i
- 
- 		if (link->conf->chanreq.oper.chan &&
- 		    link->conf->chanreq.oper.chan->band != NL80211_BAND_5GHZ)
--			return;
-+			continue;
- 
- 		wiphy_delayed_work_cancel(local->hw.wiphy,
- 					  &link->dfs_cac_timer_work);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0062-mtk-mac80211-Do-MLD-address-translation-before-STA-p.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0062-mtk-mac80211-Do-MLD-address-translation-before-STA-p.patch
new file mode 100644
index 0000000..8355500
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0062-mtk-mac80211-Do-MLD-address-translation-before-STA-p.patch
@@ -0,0 +1,43 @@
+From dadbe24860119878b5d1ff45ca8fd16d32a10919 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 26 Mar 2024 11:36:35 +0800
+Subject: [PATCH 62/89] mtk: mac80211: Do MLD address translation before STA
+ process BMC mgmt. frame
+
+In the function ieee80211_prepare_and_rx_handle(), BMC mgmt. frames are
+not MLD translated since the AAD calculation needs the header being link
+addressed. However, after the AAD calculation, STA processes the mgmt.
+frames on an MLD level, and it fails to match the link address in the
+header with the self MLD address.
+
+This commit does MLD address translation again after the AAD calculation
+and before STA's mgmt. frames processing.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ net/mac80211/mlme.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index ae171cd..4d6dfd6 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -7504,6 +7504,15 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
+ 			return;
+ 	}
+ 
++	/* Do MLD address translation for Multicast/Broadcast frame. */
++	if (is_multicast_ether_addr(mgmt->da) && !ieee80211_is_probe_resp(fc) &&
++	    !ieee80211_is_beacon(fc)) {
++		if (ether_addr_equal(mgmt->sa, link->conf->bssid))
++			ether_addr_copy(mgmt->sa, sdata->vif.cfg.ap_addr);
++		if (ether_addr_equal(mgmt->bssid, link->conf->bssid))
++			ether_addr_copy(mgmt->bssid, sdata->vif.cfg.ap_addr);
++	}
++
+ 	switch (fc & IEEE80211_FCTL_STYPE) {
+ 	case IEEE80211_STYPE_BEACON:
+ 		ieee80211_rx_mgmt_beacon(link, (void *)mgmt,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0063-mtk-mac80211-defer-enabling-beacon-for-MLD-AP.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0063-mtk-mac80211-defer-enabling-beacon-for-MLD-AP.patch
new file mode 100644
index 0000000..7966ae1
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0063-mtk-mac80211-defer-enabling-beacon-for-MLD-AP.patch
@@ -0,0 +1,44 @@
+From 7476aaf4ab6a998ee5f3736e8db5460187777ebf Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 12 Apr 2024 11:45:41 +0800
+Subject: [PATCH 63/89] mtk: mac80211: defer enabling beacon for MLD AP
+
+Do not enable beacon on the first beacon update (NL80211_CMD_NEW_BEACON)
+for MLD AP, let it start from the next beacon update
+(NL80211_CMD_SET_BEACON).
+This is used to make sure that MLD AP start beacon after all links
+finish settings.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ net/mac80211/cfg.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 27afd90..20e48f0 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1427,7 +1427,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
+ 	}
+ 
+ 	link_conf->dtim_period = params->dtim_period;
+-	link_conf->enable_beacon = true;
++	link_conf->enable_beacon = !ieee80211_vif_is_mld(&sdata->vif);
+ 	link_conf->allow_p2p_go_ps = sdata->vif.p2p;
+ 	link_conf->twt_responder = params->twt_responder;
+ 	link_conf->he_obss_pd = params->he_obss_pd;
+@@ -1497,6 +1497,11 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
+ 	ieee80211_recalc_dtim(local, sdata);
+ 	ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_SSID);
+ 	ieee80211_link_info_change_notify(sdata, link, changed);
++	/* for MLD AP, enable_beacon is false during the first beacon set,
++	 * enable it after that. This allows userspace to control the
++	 * beacon enable timing.
++	 */
++	link_conf->enable_beacon = true;
+ 
+ 	if (ieee80211_num_beaconing_links(sdata) <= 1)
+ 		netif_carrier_on(dev);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0064-mtk-mac80211-prevent-STA-MLD-s-link-addr-from-being-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0064-mtk-mac80211-prevent-STA-MLD-s-link-addr-from-being-.patch
new file mode 100644
index 0000000..dbfd2de
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0064-mtk-mac80211-prevent-STA-MLD-s-link-addr-from-being-.patch
@@ -0,0 +1,57 @@
+From 3266d6c2cc19929c310361f00c4823c09a3ae8b4 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 6 May 2024 15:06:55 +0800
+Subject: [PATCH 64/89] mtk: mac80211: prevent STA MLD's link addr from being
+ randaomized
+
+STA MLD's link address should be fixed, otherwise it sends AUTH request
+via different link address every time and causes connection issues.
+
+STA MLD's link address is determined by MLD address and link_id.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ net/mac80211/mlme.c | 16 ++++++++++------
+ 1 file changed, 10 insertions(+), 6 deletions(-)
+
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 4d6dfd6..1b19583 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -8163,11 +8163,13 @@ void ieee80211_mgd_setup_link(struct ieee80211_link_data *link)
+ 
+ 	ieee80211_clear_tpe(&link->conf->tpe);
+ 
+-	if (sdata->u.mgd.assoc_data)
++	if (sdata->u.mgd.assoc_data) {
+ 		ether_addr_copy(link->conf->addr,
+ 				sdata->u.mgd.assoc_data->link[link_id].addr);
+-	else if (!is_valid_ether_addr(link->conf->addr))
+-		eth_random_addr(link->conf->addr);
++	} else if (!is_valid_ether_addr(link->conf->addr)) {
++		ether_addr_copy(link->conf->addr, sdata->vif.addr);
++		link->conf->addr[4] += link_id + 1;
++	}
+ }
+ 
+ /* scan finished notification */
+@@ -8932,11 +8934,13 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
+ 			}
+ 
+ 			link = sdata_dereference(sdata->link[i], sdata);
+-			if (link)
++			if (link) {
+ 				ether_addr_copy(assoc_data->link[i].addr,
+ 						link->conf->addr);
+-			else
+-				eth_random_addr(assoc_data->link[i].addr);
++			} else {
++				ether_addr_copy(assoc_data->link[i].addr, sdata->vif.addr);
++				assoc_data->link[i].addr[4] += i + 1;
++			}
+ 			sband = local->hw.wiphy->bands[link_cbss->channel->band];
+ 
+ 			if (match_auth && i == assoc_link_id && link)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0065-mtk-mac80211-add-per-sta-prof-CSA-countdown-support.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0065-mtk-mac80211-add-per-sta-prof-CSA-countdown-support.patch
new file mode 100644
index 0000000..e370afa
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0065-mtk-mac80211-add-per-sta-prof-CSA-countdown-support.patch
@@ -0,0 +1,242 @@
+From 462379a27140857e77f4fe1131d6f2856e394f08 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 9 Apr 2024 11:01:51 +0800
+Subject: [PATCH 65/89] mtk: mac80211: add per-sta prof CSA countdown support
+
+Add CSA/eCSA offset of per-sta profile
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ include/net/cfg80211.h       |  6 ++++++
+ include/net/mac80211.h       |  4 ++++
+ include/uapi/linux/nl80211.h |  8 ++++++++
+ net/mac80211/cfg.c           | 27 +++++++++++++++++----------
+ net/mac80211/ieee80211_i.h   |  3 +++
+ net/mac80211/tx.c            |  8 +++++---
+ net/wireless/nl80211.c       | 13 ++++++++++++-
+ 7 files changed, 55 insertions(+), 14 deletions(-)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index a104094..4dde28b 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -1555,8 +1555,12 @@ struct cfg80211_ap_update {
+  * @beacon_csa: beacon data while performing the switch
+  * @counter_offsets_beacon: offsets of the counters within the beacon (tail)
+  * @counter_offsets_presp: offsets of the counters within the probe response
++ * @counter_offsets_sta_prof: offsets of the counters within the per-STA profile
++ *	corresponding to the channel switch link
+  * @n_counter_offsets_beacon: number of csa counters the beacon (tail)
+  * @n_counter_offsets_presp: number of csa counters in the probe response
++ * @n_counter_offsets_sta_prof: number of csa counters in the per-STA profile
++ *	corresponding to the channel switch link
+  * @beacon_after: beacon data to be used on the new channel
+  * @radar_required: whether radar detection is required on the new channel
+  * @block_tx: whether transmissions should be blocked while changing
+@@ -1569,8 +1573,10 @@ struct cfg80211_csa_settings {
+ 	struct cfg80211_beacon_data beacon_csa;
+ 	const u16 *counter_offsets_beacon;
+ 	const u16 *counter_offsets_presp;
++	const u16 *counter_offsets_sta_prof;
+ 	unsigned int n_counter_offsets_beacon;
+ 	unsigned int n_counter_offsets_presp;
++	unsigned int n_counter_offsets_sta_prof;
+ 	struct cfg80211_beacon_data beacon_after;
+ 	bool radar_required;
+ 	bool block_tx;
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 5321c22..9e03302 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -5487,6 +5487,9 @@ void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets);
+  * @cntdwn_counter_offs: array of IEEE80211_MAX_CNTDWN_COUNTERS_NUM offsets
+  *	to countdown counters.  This array can contain zero values which
+  *	should be ignored.
++ * @sta_prof_cntdwn_offs: array of IEEE80211_MAX_CNTDWN_COUNTERS_NUM offsets
++ *	to countdown counters in per-STA profile.
++ *	This array can contain zero values which should be ignored.
+  * @mbssid_off: position of the multiple bssid element
+  */
+ struct ieee80211_mutable_offsets {
+@@ -5494,6 +5497,7 @@ struct ieee80211_mutable_offsets {
+ 	u16 tim_length;
+ 
+ 	u16 cntdwn_counter_offs[IEEE80211_MAX_CNTDWN_COUNTERS_NUM];
++	u16 sta_prof_cntdwn_offs[IEEE80211_MAX_CNTDWN_COUNTERS_NUM];
+ 	u16 mbssid_off;
+ };
+ 
+diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
+index 257528d..6a2291d 100644
+--- a/include/uapi/linux/nl80211.h
++++ b/include/uapi/linux/nl80211.h
+@@ -2868,6 +2868,10 @@ enum nl80211_commands {
+  *	nested item, it contains attributes defined in
+  *	&enum nl80211_if_combination_attrs.
+  *
++ * @NL80211_ATTR_CNTDWN_OFFS_STA_PROF: An array of offsets (u16) to the channel
++ *	switch or color change counters in the per-STA profile corresponding to
++ *	the affected AP.
++ *
+  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
+  * @NL80211_ATTR_MAX: highest attribute number currently defined
+  * @__NL80211_ATTR_AFTER_LAST: internal use
+@@ -3418,6 +3422,9 @@ enum nl80211_attrs {
+ 
+ 	/* add attributes here, update the policy in nl80211.c */
+ 
++	/* MTK internal */
++	NL80211_ATTR_CNTDWN_OFFS_STA_PROF,
++
+ 	__NL80211_ATTR_AFTER_LAST,
+ 	NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST,
+ 	NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
+@@ -3430,6 +3437,7 @@ enum nl80211_attrs {
+ #define NL80211_ATTR_SAE_DATA NL80211_ATTR_AUTH_DATA
+ #define NL80211_ATTR_CSA_C_OFF_BEACON NL80211_ATTR_CNTDWN_OFFS_BEACON
+ #define NL80211_ATTR_CSA_C_OFF_PRESP NL80211_ATTR_CNTDWN_OFFS_PRESP
++#define NL80211_ATTR_CSA_C_OFF_STA_PROF NL80211_ATTR_CNTDWN_OFFS_STA_PROF
+ 
+ /*
+  * Allow user space programs to use #ifdef on new attributes by defining them
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 20e48f0..28c35ad 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1182,6 +1182,9 @@ ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+ 		memcpy(new->cntdwn_counter_offsets, csa->counter_offsets_beacon,
+ 		       csa->n_counter_offsets_beacon *
+ 		       sizeof(new->cntdwn_counter_offsets[0]));
++		memcpy(new->sta_prof_cntdwn_offs, csa->counter_offsets_sta_prof,
++		       csa->n_counter_offsets_sta_prof *
++		       sizeof(new->sta_prof_cntdwn_offs[0]));
+ 	} else if (cca) {
+ 		new->cntdwn_current_counter = cca->count;
+ 		new->cntdwn_counter_offsets[0] = cca->counter_offset_beacon;
+@@ -3847,8 +3850,10 @@ static int ieee80211_set_csa_beacon(struct ieee80211_link_data *link_data,
+ 
+ 		csa.counter_offsets_beacon = params->counter_offsets_beacon;
+ 		csa.counter_offsets_presp = params->counter_offsets_presp;
++		csa.counter_offsets_sta_prof = params->counter_offsets_sta_prof;
+ 		csa.n_counter_offsets_beacon = params->n_counter_offsets_beacon;
+ 		csa.n_counter_offsets_presp = params->n_counter_offsets_presp;
++		csa.n_counter_offsets_sta_prof = params->n_counter_offsets_sta_prof;
+ 		csa.count = params->count;
+ 
+ 		err = ieee80211_assign_beacon(sdata, link_data,
+@@ -4011,17 +4016,19 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ 	if (err)
+ 		goto out;
+ 
+-	err = ieee80211_link_reserve_chanctx(link_data, &chanreq,
+-					     chanctx->mode,
+-					     params->radar_required);
+-	if (err)
+-		goto out;
++	if (!cfg80211_chandef_identical(&conf->def, &chanreq.oper)) {
++		err = ieee80211_link_reserve_chanctx(link_data, &chanreq,
++						     chanctx->mode,
++						     params->radar_required);
++		if (err)
++			goto out;
+ 
+-	/* if reservation is invalid then this will fail */
+-	err = ieee80211_check_combinations(sdata, NULL, chanctx->mode, 0, -1);
+-	if (err) {
+-		ieee80211_link_unreserve_chanctx(link_data);
+-		goto out;
++		/* if reservation is invalid then this will fail */
++		err = ieee80211_check_combinations(sdata, NULL, chanctx->mode, 0, -1);
++		if (err) {
++			ieee80211_link_unreserve_chanctx(link_data);
++			goto out;
++		}
+ 	}
+ 
+ 	/* if there is a color change in progress, abort it */
+diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
+index d18f049..a5c0d6c 100644
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -261,9 +261,11 @@ struct ieee80211_rx_data {
+ struct ieee80211_csa_settings {
+ 	const u16 *counter_offsets_beacon;
+ 	const u16 *counter_offsets_presp;
++	const u16 *counter_offsets_sta_prof;
+ 
+ 	int n_counter_offsets_beacon;
+ 	int n_counter_offsets_presp;
++	int n_counter_offsets_sta_prof;
+ 
+ 	u8 count;
+ };
+@@ -280,6 +282,7 @@ struct beacon_data {
+ 	struct ieee80211_meshconf_ie *meshconf;
+ 	u16 cntdwn_counter_offsets[IEEE80211_MAX_CNTDWN_COUNTERS_NUM];
+ 	u8 cntdwn_current_counter;
++	u16 sta_prof_cntdwn_offs[IEEE80211_MAX_CNTDWN_COUNTERS_NUM];
+ 	struct cfg80211_mbssid_elems *mbssid_ies;
+ 	struct cfg80211_rnr_elems *rnr_ies;
+ 	struct rcu_head rcu_head;
+diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
+index 00294cc..ebe3ae2 100644
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -5241,11 +5241,13 @@ ieee80211_beacon_get_finish(struct ieee80211_hw *hw,
+ 
+ 		for (i = 0; i < IEEE80211_MAX_CNTDWN_COUNTERS_NUM; i++) {
+ 			u16 csa_off = beacon->cntdwn_counter_offsets[i];
++			u16 sta_prof_csa_off = beacon->sta_prof_cntdwn_offs[i];
+ 
+-			if (!csa_off)
+-				continue;
++			if (csa_off)
++				offs->cntdwn_counter_offs[i] = csa_off_base + csa_off;
+ 
+-			offs->cntdwn_counter_offs[i] = csa_off_base + csa_off;
++			if (sta_prof_csa_off)
++				offs->sta_prof_cntdwn_offs[i] = csa_off_base + sta_prof_csa_off;
+ 		}
+ 	}
+ 
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index d4e7ed8..b6f8fc1 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -862,6 +862,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
+ 	[NL80211_ATTR_MLO_TTLM_DLINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8),
+ 	[NL80211_ATTR_MLO_TTLM_ULINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8),
+ 	[NL80211_ATTR_ASSOC_SPP_AMSDU] = { .type = NLA_FLAG },
++	[NL80211_ATTR_CNTDWN_OFFS_STA_PROF] = { .type = NLA_BINARY },
+ };
+ 
+ /* policy for the key attributes */
+@@ -10393,7 +10394,8 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
+ 	if (err)
+ 		goto free;
+ 
+-	if (!csa_attrs[NL80211_ATTR_CNTDWN_OFFS_BEACON]) {
++	if (!csa_attrs[NL80211_ATTR_CNTDWN_OFFS_BEACON] &&
++	    !csa_attrs[NL80211_ATTR_CNTDWN_OFFS_STA_PROF]) {
+ 		err = -EINVAL;
+ 		goto free;
+ 	}
+@@ -10416,6 +10418,15 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
+ 	if (err)
+ 		goto free;
+ 
++	err = nl80211_parse_counter_offsets(rdev, params.beacon_csa.tail,
++					    params.beacon_csa.tail_len,
++					    params.count,
++					    csa_attrs[NL80211_ATTR_CNTDWN_OFFS_STA_PROF],
++					    &params.counter_offsets_sta_prof,
++					    &params.n_counter_offsets_sta_prof);
++	if (err)
++		goto free;
++
+ skip_beacons:
+ 	err = nl80211_parse_chandef(rdev, info, &params.chandef);
+ 	if (err)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0066-mtk-mac80211-Add-support-for-EMLSR-support.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0066-mtk-mac80211-Add-support-for-EMLSR-support.patch
new file mode 100644
index 0000000..3bf6b3f
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0066-mtk-mac80211-Add-support-for-EMLSR-support.patch
@@ -0,0 +1,81 @@
+From 077f0bdcee690c198def8700b38e70fd5e63c6ff Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+Date: Mon, 29 Apr 2024 10:20:07 +0800
+Subject: [PATCH 66/89] mtk: mac80211: Add support for EMLSR support
+
+Send the EML capability to driver
+Specify the time for eml_capa in advance to avoid the driver setting
+padding delay and transition delay without having obtained the correct
+values.
+
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ include/net/cfg80211.h | 1 +
+ include/net/mac80211.h | 1 +
+ net/mac80211/cfg.c     | 3 +++
+ net/wireless/nl80211.c | 4 ++++
+ 4 files changed, 9 insertions(+)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index 4dde28b..8ce51d2 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -1797,6 +1797,7 @@ struct station_parameters {
+ 	u8 supported_oper_classes_len;
+ 	int support_p2p_ps;
+ 	u16 airtime_weight;
++	u16 eml_capa;
+ 	struct link_station_parameters link_sta_params;
+ };
+ 
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 9e03302..5cd9432 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -2507,6 +2507,7 @@ struct ieee80211_sta {
+ 	bool mlo;
+ 	bool spp_amsdu;
+ 	u8 max_amsdu_subframes;
++	u16 eml_capa;
+ 
+ 	struct ieee80211_sta_aggregates *cur;
+ 
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 28c35ad..8eb9800 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -2044,6 +2044,9 @@ static int sta_apply_parameters(struct ieee80211_local *local,
+ 	if (ret)
+ 		return ret;
+ 
++	if (params->eml_capa)
++		sta->sta.eml_capa = params->eml_capa;
++
+ 	if (params->support_p2p_ps >= 0)
+ 		sta->sta.support_p2p_ps = params->support_p2p_ps;
+ 
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index b6f8fc1..ea251a9 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -861,6 +861,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
+ 	[NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA] = { .type = NLA_FLAG },
+ 	[NL80211_ATTR_MLO_TTLM_DLINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8),
+ 	[NL80211_ATTR_MLO_TTLM_ULINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8),
++	[NL80211_ATTR_EML_CAPABILITY] = { .type = NLA_U16 },
+ 	[NL80211_ATTR_ASSOC_SPP_AMSDU] = { .type = NLA_FLAG },
+ 	[NL80211_ATTR_CNTDWN_OFFS_STA_PROF] = { .type = NLA_BINARY },
+ };
+@@ -7432,6 +7433,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
+ 		mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ 	}
+ 
++	if (info->attrs[NL80211_ATTR_EML_CAPABILITY])
++		params.eml_capa =
++			nla_get_u16(info->attrs[NL80211_ATTR_EML_CAPABILITY]);
+ 
+ 	if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
+ 		params.link_sta_params.supported_rates =
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0067-mtk-mac80211-set-max_amsdu_len-for-link_sta.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0067-mtk-mac80211-set-max_amsdu_len-for-link_sta.patch
new file mode 100644
index 0000000..b1a8084
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0067-mtk-mac80211-set-max_amsdu_len-for-link_sta.patch
@@ -0,0 +1,42 @@
+From f2be5c5dc8ca3f2fb9b195a16d5717abc5dba797 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Thu, 9 May 2024 11:10:43 +0800
+Subject: [PATCH 67/89] mtk: mac80211: set max_amsdu_len for link_sta
+
+Get station's max mpdu length from eht cap and compare with AP's
+capability. Update agg.max_amsdu_len in link_sta for driver can
+get correct max mpdu length.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ net/mac80211/eht.c | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+diff --git a/net/mac80211/eht.c b/net/mac80211/eht.c
+index ddc7acc..020a035 100644
+--- a/net/mac80211/eht.c
++++ b/net/mac80211/eht.c
+@@ -75,4 +75,20 @@ ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata,
+ 
+ 	link_sta->cur_max_bandwidth = ieee80211_sta_cap_rx_bw(link_sta);
+ 	link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta);
++
++	switch (u8_get_bits(eht_cap->eht_cap_elem.mac_cap_info[0],
++			    IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK)) {
++	case IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_11454:
++		link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_11454;
++		break;
++	case IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_7991:
++		link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_7991;
++		break;
++	case IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_3895:
++	default:
++		link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_3895;
++		break;
++	}
++
++	ieee80211_sta_recalc_aggregates(&link_sta->sta->sta);
+ }
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0068-mtk-mac80211-legacy-AP-scan-request-should-contain-v.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0068-mtk-mac80211-legacy-AP-scan-request-should-contain-v.patch
new file mode 100644
index 0000000..c77d97d
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0068-mtk-mac80211-legacy-AP-scan-request-should-contain-v.patch
@@ -0,0 +1,65 @@
+From aaa8ad411db97a885da1d3a7f925df38df294f75 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 9 May 2024 09:24:43 +0800
+Subject: [PATCH 68/89] mtk: mac80211: legacy AP scan request should contain
+ valid channels
+
+In single-wiphy, if scan_freqs is not specified in scan request,
+mac80211 will trigger a scan that includes all bands.
+However, legacy AP should only scan the channels of its operating band.
+
+This commit adds the checks for including valid channels in legacy AP
+scan.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+
+preset_chandef is only set on the first wdev of each phy, so we refer to
+another data structure for the band of current wdev.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ net/wireless/nl80211.c | 21 +++++++++++++++++++++
+ 1 file changed, 21 insertions(+)
+
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index ea251a9..cb14460 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -9382,6 +9382,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
+ 		n_channels = validate_scan_freqs(scan_freqs);
+ 		if (!n_channels)
+ 			return -EINVAL;
++	} else if (wdev->iftype == NL80211_IFTYPE_AP && !wdev->valid_links) {
++		struct ieee80211_channel *chan = wdev->links[0].ap.chandef.chan;
++		if (!chan || !wiphy->bands[chan->band])
++			return -EINVAL;
++
++		n_channels = wiphy->bands[chan->band]->n_channels;
+ 	} else {
+ 		n_channels = ieee80211_get_num_supported_channels(wiphy);
+ 	}
+@@ -9434,6 +9440,21 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
+ 			}
+ 
+ 			/* ignore disabled channels */
++			if (chan->flags & IEEE80211_CHAN_DISABLED)
++				continue;
++
++			request->channels[i] = chan;
++			i++;
++		}
++	} else if (wdev->iftype == NL80211_IFTYPE_AP && !wdev->valid_links) {
++		enum nl80211_band band = wdev->links[0].ap.chandef.chan->band;
++		int j;
++
++		for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
++			struct ieee80211_channel *chan;
++
++			chan = &wiphy->bands[band]->channels[j];
++
+ 			if (chan->flags & IEEE80211_CHAN_DISABLED)
+ 				continue;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0069-mtk-mac80211-do-not-check-pre-CAC-allowed-for-scan.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0069-mtk-mac80211-do-not-check-pre-CAC-allowed-for-scan.patch
new file mode 100644
index 0000000..ae16703
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0069-mtk-mac80211-do-not-check-pre-CAC-allowed-for-scan.patch
@@ -0,0 +1,32 @@
+From 91cc054d85812aad1a3203f7a009b1b2083df074 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 10 May 2024 13:14:43 +0800
+Subject: [PATCH 69/89] mtk: mac80211: do not check pre-CAC allowed for scan
+
+When scanning, interfaces only leave the channels in a very short time,
+which can be tolerated. Therefore we do not check pre-CAC allowed for
+scan.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ net/mac80211/scan.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
+index 0b8d69f..c6dcffa 100644
+--- a/net/mac80211/scan.c
++++ b/net/mac80211/scan.c
+@@ -582,8 +582,10 @@ static bool __ieee80211_can_leave_ch(struct ieee80211_sub_if_data *sdata)
+ 	if (!ieee80211_is_radar_required(local))
+ 		return true;
+ 
++	/* FIXME do not check pre-CAC allowed for scan.
+ 	if (!regulatory_pre_cac_allowed(local->hw.wiphy))
+ 		return false;
++	*/
+ 
+ 	list_for_each_entry(sdata_iter, &local->interfaces, list) {
+ 		for_each_valid_link(&sdata_iter->wdev, link_id)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0070-mtk-mac80211-add-mlo-probe-client-support.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0070-mtk-mac80211-add-mlo-probe-client-support.patch
new file mode 100644
index 0000000..877730e
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0070-mtk-mac80211-add-mlo-probe-client-support.patch
@@ -0,0 +1,39 @@
+From 997603fb7f8335d007430e2682cc3db7ee5bcae5 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 15 May 2024 11:25:19 +0800
+Subject: [PATCH 70/89] mtk: mac80211: add mlo probe client support
+
+Add link unspec for mld case; otherwise it would be link 0
+instead of primary link
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+Send the null func in min rate.
+Without this patch, the null data frame will fail to tx in both legacy
+AP and MLD AP.
+(appears in ICS log but not in air).
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ net/mac80211/cfg.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 8eb9800..c52cde0 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -4266,7 +4266,10 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
+ 	info = IEEE80211_SKB_CB(skb);
+ 
+ 	info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
+-		       IEEE80211_TX_INTFL_NL80211_FRAME_TX;
++		       IEEE80211_TX_INTFL_NL80211_FRAME_TX |
++		       IEEE80211_TX_CTL_USE_MINRATE;
++	if (ieee80211_vif_is_mld(&sdata->vif))
++		info->control.flags |= IEEE80211_TX_CTRL_MLO_LINK_UNSPEC;
+ 	info->band = band;
+ 
+ 	skb_set_queue_mapping(skb, IEEE80211_AC_VO);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0071-mtk-mac80211-rework-radar-notify-for-MLO.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0071-mtk-mac80211-rework-radar-notify-for-MLO.patch
new file mode 100644
index 0000000..05e2de2
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0071-mtk-mac80211-rework-radar-notify-for-MLO.patch
@@ -0,0 +1,322 @@
+From 4cbed3e79fa9c53deaa251254a21650a7fe6dcb0 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 27 May 2024 13:23:59 +0800
+Subject: [PATCH 71/89] mtk: mac80211: rework radar notify for MLO
+
+Rework radar notify for MLO.
+A netdev/wdev containing 5G link is required for all radar events since
+userspace daemon will only process the event with the first netdev
+(might not include 5G links) if netdev/wdev is not specified.
+For instance, the radar event will be ignored in the following
+configuration.
+wdev 1: MLD AP (2+6G)
+wdev 2: MLD AP (2+5+6G)
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+There is a chance that the DFS pre-CAC check, triggered by a non-5G interface,
+is executed during the switching time of 5G interfaces.
+In this case, cfg80211_any_wiphy_oper_chan of target channel  will return 0,
+as the 5G interface is not yet operating on the target channel.
+Therefore, a pre-CAC expired event of the target channel will be issued to hostapd.
+This causes the AP to re-CAC the target channel after switching to it,
+which usually occurs after background radar CAC is completed.
+
+=> Avoid scheduling dfs channel update work for non-5G link.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ include/net/cfg80211.h | 20 ++++++++++++++++++++
+ net/wireless/chan.c    | 42 ++++++++++++++----------------------------
+ net/wireless/mlme.c    | 15 +++++----------
+ net/wireless/nl80211.c | 34 +++++++++++++++++++++-------------
+ net/wireless/nl80211.h |  3 +--
+ net/wireless/reg.c     |  2 +-
+ 6 files changed, 62 insertions(+), 54 deletions(-)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index 8ce51d2..5de1d1c 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -6411,6 +6411,26 @@ static inline void WARN_INVALID_LINK_ID(struct wireless_dev *wdev,
+ 		if (!(link_info)->valid_links ||		\
+ 		    ((link_info)->valid_links & BIT(link_id)))
+ 
++static inline struct wireless_dev *
++wiphy_get_band_first_wdev(struct wiphy *wiphy, int band, unsigned int *link)
++{
++	struct wireless_dev *wdev;
++	struct cfg80211_chan_def *c;
++	unsigned int link_id;
++
++	list_for_each_entry(wdev, &wiphy->wdev_list, list) {
++		for_each_valid_link(wdev, link_id) {
++			c = wdev_chandef(wdev, link_id);
++			if (c && c->chan && c->chan->band == band) {
++				*link = link_id;
++				return wdev;
++			}
++		}
++	}
++
++	return NULL;
++}
++
+ /**
+  * DOC: Utility functions
+  *
+diff --git a/net/wireless/chan.c b/net/wireless/chan.c
+index 7b511d3..38e8432 100644
+--- a/net/wireless/chan.c
++++ b/net/wireless/chan.c
+@@ -1722,31 +1722,18 @@ bool cfg80211_any_usable_channels(struct wiphy *wiphy,
+ }
+ EXPORT_SYMBOL(cfg80211_any_usable_channels);
+ 
+-static void cfg80211_sta_radar_notify(struct wiphy *wiphy,
+-				      const struct cfg80211_chan_def *chandef,
+-				      enum nl80211_radar_event event)
+-{
+-	struct wireless_dev *wdev;
+-
+-	list_for_each_entry(wdev, &wiphy->wdev_list, list) {
+-		if (cfg80211_chandef_dfs_required(wiphy, chandef, wdev->iftype) > 0) {
+-			nl80211_radar_notify(wiphy_to_rdev(wiphy), chandef,
+-					     event, wdev->netdev, GFP_KERNEL);
+-			return;
+-		}
+-	}
+-}
+-
+ void cfg80211_sta_update_dfs_state(struct wireless_dev *wdev,
+ 				   const struct cfg80211_chan_def *bss_chandef,
+ 				   const struct cfg80211_chan_def *csa_chandef,
+ 				   bool associated)
+ {
++	struct wiphy *wiphy = wdev->wiphy;
++	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ 	bool csa_active = !!csa_chandef;
+ 	enum nl80211_dfs_state dfs_state = NL80211_DFS_USABLE;
+ 	enum nl80211_radar_event event = NL80211_RADAR_STA_CAC_EXPIRED;
+ 
+-	lockdep_assert_wiphy(wdev->wiphy);
++	lockdep_assert_wiphy(wiphy);
+ 
+ 	if (!bss_chandef || !bss_chandef->chan ||
+ 	    bss_chandef->chan->band != NL80211_BAND_5GHZ)
+@@ -1754,17 +1741,17 @@ void cfg80211_sta_update_dfs_state(struct wireless_dev *wdev,
+ 
+ 	/* assume csa channel is cac completed */
+ 	if (csa_active &&
+-	    (cfg80211_chandef_dfs_usable(wdev->wiphy, csa_chandef) ||
+-	    cfg80211_chandef_dfs_available(wdev->wiphy, csa_chandef))) {
+-		cfg80211_set_dfs_state(wdev->wiphy, csa_chandef, NL80211_DFS_AVAILABLE);
+-		cfg80211_sta_radar_notify(wdev->wiphy, csa_chandef,
+-					  NL80211_RADAR_STA_CAC_SKIPPED);
++	    (cfg80211_chandef_dfs_usable(wiphy, csa_chandef) ||
++	    cfg80211_chandef_dfs_available(wiphy, csa_chandef))) {
++		cfg80211_set_dfs_state(wiphy, csa_chandef, NL80211_DFS_AVAILABLE);
++		nl80211_radar_notify(rdev, csa_chandef,
++				     NL80211_RADAR_STA_CAC_SKIPPED, GFP_KERNEL);
+ 		netdev_info(wdev->netdev, "Set CSA channel's DFS state to available\n");
+ 	}
+ 
+ 	/* avoid updating the dfs state during nop */
+-	if (!cfg80211_chandef_dfs_usable(wdev->wiphy, bss_chandef) &&
+-	    !cfg80211_chandef_dfs_available(wdev->wiphy, bss_chandef))
++	if (!cfg80211_chandef_dfs_usable(wiphy, bss_chandef) &&
++	    !cfg80211_chandef_dfs_available(wiphy, bss_chandef))
+ 		return;
+ 
+ 	if (associated && !csa_active) {
+@@ -1776,13 +1763,12 @@ void cfg80211_sta_update_dfs_state(struct wireless_dev *wdev,
+ 	 * when other interfaces still operate on this channel
+ 	 */
+ 	if (dfs_state == NL80211_DFS_USABLE &&
+-	    (cfg80211_is_wiphy_oper_chan(wdev->wiphy, bss_chandef->chan) ||
+-	     cfg80211_offchan_chain_is_active(wiphy_to_rdev(wdev->wiphy),
+-					      bss_chandef->chan)))
++	    (cfg80211_is_wiphy_oper_chan(wiphy, bss_chandef->chan) ||
++	     cfg80211_offchan_chain_is_active(rdev, bss_chandef->chan)))
+ 		return;
+ 
+-	cfg80211_set_dfs_state(wdev->wiphy, bss_chandef, dfs_state);
+-	cfg80211_sta_radar_notify(wdev->wiphy, bss_chandef, event);
++	cfg80211_set_dfs_state(wiphy, bss_chandef, dfs_state);
++	nl80211_radar_notify(rdev, bss_chandef, event, GFP_KERNEL);
+ 
+ 	if (csa_active)
+ 		netdev_info(wdev->netdev, "Set origin channel's DFS state to usable\n");
+diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
+index cddee2f..9cc8071 100644
+--- a/net/wireless/mlme.c
++++ b/net/wireless/mlme.c
+@@ -1058,8 +1058,7 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
+ 							NL80211_CHAN_NO_HT);
+ 
+ 				nl80211_radar_notify(rdev, &chandef,
+-						     radar_event, NULL,
+-						     GFP_ATOMIC);
++						     radar_event, GFP_ATOMIC);
+ 
+ 				regulatory_propagate_dfs_state(wiphy, &chandef,
+ 							       c->dfs_state,
+@@ -1102,7 +1101,7 @@ void __cfg80211_radar_event(struct wiphy *wiphy,
+ 
+ 	cfg80211_sched_dfs_chan_update(rdev);
+ 
+-	nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp);
++	nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, gfp);
+ 
+ 	memcpy(&rdev->radar_chandef, chandef, sizeof(struct cfg80211_chan_def));
+ 	queue_work(cfg80211_wq, &rdev->propagate_radar_detect_wk);
+@@ -1151,7 +1150,7 @@ void cfg80211_cac_event(struct net_device *netdev,
+ 		return;
+ 	}
+ 
+-	nl80211_radar_notify(rdev, chandef, event, netdev, gfp);
++	nl80211_radar_notify(rdev, chandef, event, gfp);
+ }
+ EXPORT_SYMBOL(cfg80211_cac_event);
+ 
+@@ -1162,7 +1161,6 @@ __cfg80211_background_cac_event(struct cfg80211_registered_device *rdev,
+ 				enum nl80211_radar_event event)
+ {
+ 	struct wiphy *wiphy = &rdev->wiphy;
+-	struct net_device *netdev;
+ 
+ 	lockdep_assert_wiphy(&rdev->wiphy);
+ 
+@@ -1178,13 +1176,11 @@ __cfg80211_background_cac_event(struct cfg80211_registered_device *rdev,
+ 		memcpy(&rdev->cac_done_chandef, chandef, sizeof(*chandef));
+ 		queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk);
+ 		cfg80211_sched_dfs_chan_update(rdev);
+-		wdev = rdev->background_radar_wdev;
+ 		rdev->background_cac_started = false;
+ 		break;
+ 	case NL80211_RADAR_CAC_ABORTED:
+ 		if (!cancel_delayed_work(&rdev->background_cac_done_wk))
+ 			return;
+-		wdev = rdev->background_radar_wdev;
+ 		rdev->background_cac_started = false;
+ 		break;
+ 	case NL80211_RADAR_CAC_STARTED:
+@@ -1194,8 +1190,7 @@ __cfg80211_background_cac_event(struct cfg80211_registered_device *rdev,
+ 		return;
+ 	}
+ 
+-	netdev = wdev ? wdev->netdev : NULL;
+-	nl80211_radar_notify(rdev, chandef, event, netdev, GFP_KERNEL);
++	nl80211_radar_notify(rdev, chandef, event, GFP_KERNEL);
+ }
+ 
+ static void
+@@ -1291,7 +1286,7 @@ void cfg80211_background_radar_update_channel(struct wiphy *wiphy,
+ 
+ 	event = expand ? NL80211_RADAR_BACKGROUND_CHAN_EXPAND :
+ 			 NL80211_RADAR_BACKGROUND_CHAN_UPDATE;
+-	nl80211_radar_notify(wiphy_to_rdev(wiphy), chandef, event, NULL, GFP_ATOMIC);
++	nl80211_radar_notify(wiphy_to_rdev(wiphy), chandef, event, GFP_ATOMIC);
+ }
+ EXPORT_SYMBOL(cfg80211_background_radar_update_channel);
+ 
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index cb14460..ba1190d 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -19686,7 +19686,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
+ 	}
+ 
+ 	cfg80211_schedule_channels_check(wdev);
+-	cfg80211_sched_dfs_chan_update(rdev);
++	if (chandef->chan && chandef->chan->band == NL80211_BAND_5GHZ)
++		cfg80211_sched_dfs_chan_update(rdev);
+ 
+ 	nl80211_ch_switch_notify(rdev, dev, link_id, chandef, GFP_KERNEL,
+ 				 NL80211_CMD_CH_SWITCH_NOTIFY, 0, false);
+@@ -19766,12 +19767,22 @@ EXPORT_SYMBOL(cfg80211_bss_color_notify);
+ void
+ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
+ 		     const struct cfg80211_chan_def *chandef,
+-		     enum nl80211_radar_event event,
+-		     struct net_device *netdev, gfp_t gfp)
++		     enum nl80211_radar_event event, gfp_t gfp)
+ {
++	struct wiphy *wiphy = &rdev->wiphy;
++	struct wireless_dev *wdev;
+ 	struct sk_buff *msg;
++	unsigned int link_id;
+ 	void *hdr;
+ 
++	/* Specifying a wdev containing a 5G link is necessary for MLO.
++	 * Otherwise, userspace will only process the radar event
++	 * with the first wdev, which may not have 5G links.
++	 */
++	wdev = wiphy_get_band_first_wdev(wiphy, NL80211_BAND_5GHZ, &link_id);
++	if (!wdev)
++		return;
++
+ 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ 	if (!msg)
+ 		return;
+@@ -19785,15 +19796,12 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
+ 	if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
+ 		goto nla_put_failure;
+ 
+-	/* NOP and radar events don't need a netdev parameter */
+-	if (netdev) {
+-		struct wireless_dev *wdev = netdev->ieee80211_ptr;
+-
+-		if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+-		    nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+-				      NL80211_ATTR_PAD))
+-			goto nla_put_failure;
+-	}
++	if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex) ||
++	    nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
++			      NL80211_ATTR_PAD) ||
++	    (wdev->valid_links &&
++	     nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)))
++		goto nla_put_failure;
+ 
+ 	if (nla_put_u32(msg, NL80211_ATTR_RADAR_EVENT, event))
+ 		goto nla_put_failure;
+@@ -19803,7 +19811,7 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
+ 
+ 	genlmsg_end(msg, hdr);
+ 
+-	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
++	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(wiphy), msg, 0,
+ 				NL80211_MCGRP_MLME, gfp);
+ 	return;
+ 
+diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
+index ffaab9a..ee68488 100644
+--- a/net/wireless/nl80211.h
++++ b/net/wireless/nl80211.h
+@@ -114,8 +114,7 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
+ void
+ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
+ 		     const struct cfg80211_chan_def *chandef,
+-		     enum nl80211_radar_event event,
+-		     struct net_device *netdev, gfp_t gfp);
++		     enum nl80211_radar_event event, gfp_t gfp);
+ 
+ void nl80211_send_ap_stopped(struct wireless_dev *wdev, unsigned int link_id);
+ 
+diff --git a/net/wireless/reg.c b/net/wireless/reg.c
+index 9cd7fb2..2a13721 100644
+--- a/net/wireless/reg.c
++++ b/net/wireless/reg.c
+@@ -4293,7 +4293,7 @@ void regulatory_propagate_dfs_state(struct wiphy *wiphy,
+ 			cfg80211_check_and_end_cac(rdev);
+ 		}
+ 
+-		nl80211_radar_notify(rdev, chandef, event, NULL, GFP_KERNEL);
++		nl80211_radar_notify(rdev, chandef, event, GFP_KERNEL);
+ 	}
+ }
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0072-mtk-mac80211-add-DFS-CAC-countdown-in-CSA-flow.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0072-mtk-mac80211-add-DFS-CAC-countdown-in-CSA-flow.patch
new file mode 100644
index 0000000..39e8817
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0072-mtk-mac80211-add-DFS-CAC-countdown-in-CSA-flow.patch
@@ -0,0 +1,415 @@
+From 16ec7de7b7f4d09a4b84ea2d85fb287a579626b2 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 6 Feb 2024 17:46:10 +0800
+Subject: [PATCH 72/89] mtk: mac80211: add DFS CAC countdown in CSA flow
+
+Add DFS channel CAC countdown mechanism in CSA flow
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ include/net/cfg80211.h     | 38 ++++++++++++++++-
+ net/mac80211/cfg.c         | 85 ++++++++++++++++++++++++++++++++++++--
+ net/mac80211/ieee80211_i.h |  1 +
+ net/mac80211/mlme.c        |  6 ++-
+ net/mac80211/util.c        |  7 +++-
+ net/wireless/chan.c        | 74 +++++++++++++++++++++++++++++++++
+ net/wireless/nl80211.c     |  5 ++-
+ net/wireless/rdev-ops.h    | 18 ++++++++
+ net/wireless/reg.c         |  8 +++-
+ 9 files changed, 232 insertions(+), 10 deletions(-)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index 5de1d1c..bedf711 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -4481,6 +4481,8 @@ struct mgmt_frame_regs {
+  *
+  * @start_radar_detection: Start radar detection in the driver.
+  *
++ * @start_radar_detection_post_csa: Start radar detection during post CSA.
++ *
+  * @end_cac: End running CAC, probably because a related CAC
+  *	was finished on another phy.
+  *
+@@ -4853,9 +4855,13 @@ struct cfg80211_ops {
+ 
+ 	int	(*start_radar_detection)(struct wiphy *wiphy,
+ 					 struct net_device *dev,
+-					 unsigned int link_id,
+ 					 struct cfg80211_chan_def *chandef,
+ 					 u32 cac_time_ms, int link_id);
++	int	(*start_radar_detection_post_csa)(struct wiphy *wiphy,
++						  struct net_device *dev,
++						  unsigned int link_id,
++						  struct cfg80211_chan_def *chandef,
++						  u32 cac_time_ms);
+ 	void	(*end_cac)(struct wiphy *wiphy,
+ 			   struct net_device *dev, unsigned int link_id);
+ 	int	(*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev,
+@@ -9018,6 +9024,36 @@ cfg80211_reg_can_beacon_relax(struct wiphy *wiphy,
+ }
+ 
+ /**
++ * cfg80211_reg_can_beacon_dfs_relax - check if beaconing is allowed with DFS & IR-relaxation
++ * @wiphy: the wiphy
++ * @chandef: the channel definition
++ * @iftype: interface type
++ *
++ * Return: %true if there is no secondary channel or the secondary channel(s)
++ * can be used for beaconing. This version bypasses radar channel check, allowing
++ * channel switch to a USABLE DFS channel and performing CAC after the channel switch.
++ * It also checks if IR-relaxation conditions apply, to allow beaconing under more
++ * permissive conditions.
++ *
++ * Requires the wiphy mutex to be held.
++ */
++bool cfg80211_reg_can_beacon_dfs_relax(struct wiphy *wiphy,
++				       struct cfg80211_chan_def *chandef,
++				       enum nl80211_iftype iftype);
++
++/**
++ * cfg80211_start_radar_detection_post_csa - start radar detection after CSA
++ * @wiphy: the wiphy
++ * @wdev: the wireless device
++ * @link_id: the link ID for MLO, must be 0 for non-MLO
++ * @chandef: the channel definition to start radar detection on
++ */
++int cfg80211_start_radar_detection_post_csa(struct wiphy *wiphy,
++					    struct wireless_dev *wdev,
++					    unsigned int link_id,
++					    struct cfg80211_chan_def *chandef);
++
++/*
+  * cfg80211_ch_switch_notify - update wdev channel and notify userspace
+  * @dev: the device which switched channels
+  * @chandef: the new channel definition
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index c52cde0..36c898c 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1576,7 +1576,7 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
+ 	return 0;
+ }
+ 
+-static void ieee80211_free_next_beacon(struct ieee80211_link_data *link)
++void ieee80211_free_next_beacon(struct ieee80211_link_data *link)
+ {
+ 	if (!link->u.ap.next_beacon)
+ 		return;
+@@ -3725,6 +3725,72 @@ static int ieee80211_set_after_csa_beacon(struct ieee80211_link_data *link_data,
+ 	return 0;
+ }
+ 
++static int ieee80211_start_radar_detection_post_csa(struct wiphy *wiphy,
++						    struct net_device *dev,
++						    unsigned int link_id,
++						    struct cfg80211_chan_def *chandef,
++						    u32 cac_time_ms)
++{
++	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
++	struct ieee80211_local *local = sdata->local;
++	struct ieee80211_link_data *link;
++
++	if (!list_empty(&local->roc_list) || local->scanning)
++		return -EBUSY;
++
++	link = sdata_dereference(sdata->link[link_id], sdata);
++	if (!link)
++		return -ENOLINK;
++
++	/* whatever, but channel contexts should not complain about that one */
++	link->smps_mode = IEEE80211_SMPS_OFF;
++	link->needed_rx_chains = local->rx_chains;
++
++	if (hweight16(sdata->vif.valid_links) <= 1)
++		sta_info_flush(sdata, -1);
++
++	wiphy_delayed_work_queue(wiphy, &link->dfs_cac_timer_work,
++				 msecs_to_jiffies(cac_time_ms));
++
++	return 1;
++}
++
++static void ieee80211_csa_send_deauth(struct ieee80211_link_data *link_data)
++{
++	struct ieee80211_sub_if_data *sdata = link_data->sdata;
++	struct ieee80211_local *local = sdata->local;
++	struct ieee80211_bss_conf *link_conf = link_data->conf;
++	u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
++	u8 broadcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
++	bool send_deauth;
++
++	send_deauth = !cfg80211_chandef_identical(&link_conf->chanreq.oper,
++						  &link_data->csa.chanreq.oper) &&
++		      !cfg80211_reg_can_beacon_relax(local->hw.wiphy,
++						     &link_data->csa.chanreq.oper,
++						     sdata->wdev.iftype) &&
++		      hweight16(sdata->vif.valid_links) <= 1;
++	/* broadcast deauth frame if CAC is required for non MLD or single link MLD AP */
++	if (!send_deauth)
++		return;
++
++	if (sdata->csa_blocked_queues) {
++		ieee80211_wake_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_CSA);
++		ieee80211_send_deauth_disassoc(sdata, broadcast,
++					       link_conf->bssid,
++					       IEEE80211_STYPE_DEAUTH,
++					       WLAN_REASON_DEAUTH_LEAVING,
++					       send_deauth, frame_buf);
++		ieee80211_stop_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_CSA);
++		return;
++	}
++
++	ieee80211_send_deauth_disassoc(sdata, broadcast,
++				       link_conf->bssid, IEEE80211_STYPE_DEAUTH,
++				       WLAN_REASON_DEAUTH_LEAVING,
++				       send_deauth, frame_buf);
++}
++
+ static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data)
+ {
+ 	struct ieee80211_sub_if_data *sdata = link_data->sdata;
+@@ -3735,6 +3801,8 @@ static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data)
+ 
+ 	lockdep_assert_wiphy(local->hw.wiphy);
+ 
++	ieee80211_csa_send_deauth(link_data);
++
+ 	/*
+ 	 * using reservation isn't immediate as it may be deferred until later
+ 	 * with multi-vif. once reservation is complete it will re-schedule the
+@@ -3758,6 +3826,12 @@ static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data)
+ 					&link_data->csa.chanreq.oper))
+ 		return -EINVAL;
+ 
++	err = cfg80211_start_radar_detection_post_csa(local->hw.wiphy, &sdata->wdev,
++						      link_data->link_id,
++						      &link_conf->chanreq.oper);
++	if (err)
++		return err > 0 ? 0 : err;
++
+ 	link_conf->csa_active = false;
+ 
+ 	err = ieee80211_set_after_csa_beacon(link_data, &changed);
+@@ -5140,8 +5214,12 @@ ieee80211_skip_cac(struct wireless_dev *wdev, unsigned int link_id)
+ 
+ 	wiphy_delayed_work_cancel(sdata->local->hw.wiphy,
+ 				  &link->dfs_cac_timer_work);
+-	if (wdev->cac_links & BIT(link_id)) {
+-		ieee80211_link_release_channel(link);
++	if (wdev->links[link_id].cac_started) {
++		if (link->conf->csa_active)
++			wiphy_work_queue(sdata->local->hw.wiphy,
++					 &link->csa.finalize_work);
++		else
++			ieee80211_link_release_channel(link);
+ 		cac_time_ms = wdev->links[link_id].cac_time_ms;
+ 		wdev->links[link_id].cac_start_time = jiffies -
+ 						      msecs_to_jiffies(cac_time_ms + 1);
+@@ -5233,6 +5311,7 @@ const struct cfg80211_ops mac80211_config_ops = {
+ #endif
+ 	.get_channel = ieee80211_cfg_get_channel,
+ 	.start_radar_detection = ieee80211_start_radar_detection,
++	.start_radar_detection_post_csa = ieee80211_start_radar_detection_post_csa,
+ 	.end_cac = ieee80211_end_cac,
+ 	.channel_switch = ieee80211_channel_switch,
+ 	.set_qos_map = ieee80211_set_qos_map,
+diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
+index a5c0d6c..2cb80c3 100644
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -2015,6 +2015,7 @@ int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
+ void ieee80211_csa_finalize_work(struct wiphy *wiphy, struct wiphy_work *work);
+ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ 			     struct cfg80211_csa_settings *params);
++void ieee80211_free_next_beacon(struct ieee80211_link_data *link);
+ 
+ /* color change handling */
+ void ieee80211_color_change_finalize_work(struct wiphy *wiphy,
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 1b19583..b684fed 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -3051,7 +3051,11 @@ void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work)
+ 	lockdep_assert_wiphy(sdata->local->hw.wiphy);
+ 
+ 	if (sdata->wdev.links[link->link_id].cac_started) {
+-		ieee80211_link_release_channel(link);
++		if (link->conf->csa_active)
++			wiphy_work_queue(sdata->local->hw.wiphy,
++					 &link->csa.finalize_work);
++		else
++			ieee80211_link_release_channel(link);
+ 		cfg80211_cac_event(sdata->dev, &chandef,
+ 				   NL80211_RADAR_CAC_FINISHED,
+ 				   GFP_KERNEL, link->link_id);
+diff --git a/net/mac80211/util.c b/net/mac80211/util.c
+index 2d2b871..df4fa1a 100644
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -3494,7 +3494,12 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
+ 				continue;
+ 
+ 			chandef = link_conf->chanreq.oper;
+-			ieee80211_link_release_channel(link_data);
++			if (link_conf->csa_active) {
++				link_conf->csa_active = false;
++				ieee80211_free_next_beacon(link_data);
++			} else {
++				ieee80211_link_release_channel(link_data);
++			}
+ 			cfg80211_cac_event(sdata->dev,
+ 					   &chandef,
+ 					   NL80211_RADAR_CAC_ABORTED,
+diff --git a/net/wireless/chan.c b/net/wireless/chan.c
+index 38e8432..03747bb 100644
+--- a/net/wireless/chan.c
++++ b/net/wireless/chan.c
+@@ -1680,6 +1680,80 @@ bool cfg80211_reg_check_beaconing(struct wiphy *wiphy,
+ }
+ EXPORT_SYMBOL(cfg80211_reg_check_beaconing);
+ 
++bool cfg80211_reg_can_beacon_dfs_relax(struct wiphy *wiphy,
++				       struct cfg80211_chan_def *chandef,
++				       enum nl80211_iftype iftype)
++{
++	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
++	u32 prohibited_flags = IEEE80211_CHAN_DISABLED |
++			       IEEE80211_CHAN_RADAR;
++
++	lockdep_assert_held(&rdev->wiphy.mtx);
++
++	/* Bypass available and usable dfs channel */
++	if (cfg80211_chandef_dfs_required(wiphy, chandef, iftype) > 0 &&
++	    (cfg80211_chandef_dfs_usable(wiphy, chandef) ||
++	     cfg80211_chandef_dfs_available(wiphy, chandef)))
++		prohibited_flags = IEEE80211_CHAN_DISABLED;
++
++	/*
++	 * Under certain conditions suggested by some regulatory bodies a
++	 * GO/STA can IR on channels marked with IEEE80211_NO_IR. Set this flag
++	 * only if such relaxations are not enabled and the conditions are not
++	 * met.
++	 */
++	if (!cfg80211_ir_permissive_chan(wiphy, iftype, chandef->chan))
++		prohibited_flags |= IEEE80211_CHAN_NO_IR;
++
++	return cfg80211_chandef_usable(wiphy, chandef, prohibited_flags);
++}
++EXPORT_SYMBOL(cfg80211_reg_can_beacon_dfs_relax);
++
++int cfg80211_start_radar_detection_post_csa(struct wiphy *wiphy,
++					    struct wireless_dev *wdev,
++					    unsigned int link_id,
++					    struct cfg80211_chan_def *chandef)
++{
++	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
++	u32 cac_time_ms;
++	enum nl80211_dfs_regions dfs_region;
++	int ret = 0;
++
++	if (cfg80211_chandef_dfs_available(wiphy, chandef))
++		goto out;
++
++	/* Update DFS channel state especially when original channel include DFS channel */
++	cfg80211_sched_dfs_chan_update(rdev);
++
++	dfs_region = reg_get_dfs_region(wiphy);
++	if (dfs_region == NL80211_DFS_UNSET)
++		goto out;
++
++	cac_time_ms = cfg80211_chandef_dfs_cac_time(wiphy, chandef);
++	if (WARN_ON(!cac_time_ms))
++		cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
++
++	pr_info("%s: region = %u, center freq1 = %u, center freq2 = %u, cac time ms = %u\n",
++		__func__, dfs_region, chandef->center_freq1, chandef->center_freq2, cac_time_ms);
++
++	ret = rdev_start_radar_detection_post_csa(rdev, wdev->netdev, link_id,
++						  chandef, cac_time_ms);
++	if (ret > 0) {
++		wdev->links[link_id].ap.chandef = *chandef;
++		wdev->links[link_id].cac_start_time = jiffies;
++		wdev->links[link_id].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);
++		cfg80211_cac_event(wdev->netdev, chandef,
++				   NL80211_RADAR_CAC_STARTED, GFP_KERNEL, link_id);
++	}
++
++out:
++	return ret;
++}
++EXPORT_SYMBOL(cfg80211_start_radar_detection_post_csa);
++
+ int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
+ 				 struct cfg80211_chan_def *chandef)
+ {
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index ba1190d..756a29a 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -10462,8 +10462,9 @@ skip_beacons:
+ 		cfg80211_set_dfs_state(&rdev->wiphy, &params.chandef, NL80211_DFS_AVAILABLE);
+ 	}
+ 
+-	if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, &params.chandef,
+-					   wdev->iftype)) {
++	/* handle DFS CAC after CSA is sent */
++	if (!cfg80211_reg_can_beacon_dfs_relax(&rdev->wiphy, &params.chandef,
++					       wdev->iftype)) {
+ 		err = -EINVAL;
+ 		goto free;
+ 	}
+diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
+index e4a77a8..5b3c94f 100644
+--- a/net/wireless/rdev-ops.h
++++ b/net/wireless/rdev-ops.h
+@@ -1214,6 +1214,24 @@ rdev_start_radar_detection(struct cfg80211_registered_device *rdev,
+ 	return ret;
+ }
+ 
++static inline int
++rdev_start_radar_detection_post_csa(struct cfg80211_registered_device *rdev,
++				    struct net_device *dev,
++				    unsigned int link_id,
++				    struct cfg80211_chan_def *chandef,
++				    u32 cac_time_ms)
++{
++	int ret = -EOPNOTSUPP;
++
++	trace_rdev_start_radar_detection(&rdev->wiphy, dev, chandef,
++					 cac_time_ms, link_id);
++	if (rdev->ops->start_radar_detection_post_csa)
++		ret = rdev->ops->start_radar_detection_post_csa(&rdev->wiphy, dev, link_id,
++								chandef, cac_time_ms);
++	trace_rdev_return_int(&rdev->wiphy, ret);
++	return ret;
++}
++
+ static inline void
+ rdev_end_cac(struct cfg80211_registered_device *rdev,
+ 	     struct net_device *dev, unsigned int link_id)
+diff --git a/net/wireless/reg.c b/net/wireless/reg.c
+index 2a13721..651f286 100644
+--- a/net/wireless/reg.c
++++ b/net/wireless/reg.c
+@@ -2441,8 +2441,12 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
+ 		case NL80211_IFTYPE_P2P_GO:
+ 		case NL80211_IFTYPE_ADHOC:
+ 		case NL80211_IFTYPE_MESH_POINT:
+-			ret = cfg80211_reg_can_beacon_relax(wiphy, &chandef,
+-							    iftype);
++			if (wdev->links[link].cac_started)
++				ret = cfg80211_reg_can_beacon_dfs_relax(wiphy, &chandef,
++									iftype);
++			else
++				ret = cfg80211_reg_can_beacon_relax(wiphy, &chandef,
++								    iftype);
+ 			if (!ret)
+ 				return ret;
+ 			break;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0073-mtk-mac80211-add-mlo-related-debugfs-knob.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0073-mtk-mac80211-add-mlo-related-debugfs-knob.patch
new file mode 100644
index 0000000..e9a2ebc
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0073-mtk-mac80211-add-mlo-related-debugfs-knob.patch
@@ -0,0 +1,69 @@
+From 77c5db25a8d03ab48f06d66cdab058757ba9a2fc Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 27 May 2024 19:10:51 +0800
+Subject: [PATCH 73/89] mtk: mac80211: add mlo related debugfs knob
+
+Add the following debugfs knob
+- /sys/kernel/debug/ieee80211/phy0/<interface>/ttlm
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ net/mac80211/debugfs_netdev.c | 21 +++++++++++++++++++++
+ 1 file changed, 21 insertions(+)
+
+diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
+index 3017f09..33fa0d9 100644
+--- a/net/mac80211/debugfs_netdev.c
++++ b/net/mac80211/debugfs_netdev.c
+@@ -613,6 +613,24 @@ static ssize_t ieee80211_if_fmt_num_buffered_multicast(
+ }
+ IEEE80211_IF_FILE_R(num_buffered_multicast);
+ 
++static ssize_t ieee80211_if_fmt_ttlm(
++	const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
++{
++	const struct ieee80211_vif *vif = &sdata->vif;
++	int i = 0, len = 0;
++
++	len += scnprintf(buf + len, buflen - len, "valid = %d\n", vif->neg_ttlm.valid);
++
++	for(i = 0; i < IEEE80211_TTLM_NUM_TIDS; i++)
++		len += scnprintf(buf + len, buflen - len,
++				 "tid%d: dl = %u, ul = %u\n", i,
++				 vif->neg_ttlm.downlink[i],
++				 vif->neg_ttlm.uplink[i]);
++
++	return len;
++}
++IEEE80211_IF_FILE_R(ttlm);
++
+ static ssize_t ieee80211_if_fmt_aqm(
+ 	const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
+ {
+@@ -839,6 +857,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
+ {
+ 	DEBUGFS_ADD(bssid);
+ 	DEBUGFS_ADD(aid);
++	DEBUGFS_ADD(ttlm);
+ 	DEBUGFS_ADD(beacon_timeout);
+ 	DEBUGFS_ADD_MODE(tkip_mic_test, 0200);
+ 	DEBUGFS_ADD_MODE(beacon_loss, 0200);
+@@ -855,6 +874,7 @@ static void add_ap_files(struct ieee80211_sub_if_data *sdata)
+ 	DEBUGFS_ADD(num_mcast_sta);
+ 	DEBUGFS_ADD(num_sta_ps);
+ 	DEBUGFS_ADD(dtim_count);
++	DEBUGFS_ADD(ttlm);
+ 	DEBUGFS_ADD(num_buffered_multicast);
+ 	DEBUGFS_ADD_MODE(tkip_mic_test, 0200);
+ 	DEBUGFS_ADD_MODE(multicast_to_unicast, 0600);
+@@ -865,6 +885,7 @@ static void add_vlan_files(struct ieee80211_sub_if_data *sdata)
+ 	/* add num_mcast_sta_vlan using name num_mcast_sta */
+ 	debugfs_create_file("num_mcast_sta", 0400, sdata->vif.debugfs_dir,
+ 			    sdata, &num_mcast_sta_vlan_ops);
++	DEBUGFS_ADD(ttlm);
+ }
+ 
+ static void add_ibss_files(struct ieee80211_sub_if_data *sdata)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0074-mtk-mac80211-Add-exported-function-for-SoftMAC-drive.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0074-mtk-mac80211-Add-exported-function-for-SoftMAC-drive.patch
new file mode 100644
index 0000000..991bbf4
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0074-mtk-mac80211-Add-exported-function-for-SoftMAC-drive.patch
@@ -0,0 +1,197 @@
+From f7b9caab67609cfc9e33228d575e47b6fab4a4ba Mon Sep 17 00:00:00 2001
+From: Rex Lu <rex.lu@mediatek.com>
+Date: Fri, 7 Jun 2024 13:25:08 +0800
+Subject: [PATCH 74/89] mtk: mac80211: Add exported function for SoftMAC driver
+ to get QoS map
+
+Add exported function for SoftMAC driver to get QoS map.
+1. Because the mapping from IP DSCP to IEEE 802.11 user priority may be customized.
+Therefore, driver needs to pass the mapping to HW, so that the QoS type of traffic can be mapped in a consistent manner for both SW and HW paths.
+2. due to this change(https://github.com/torvalds/linux/commit/6fdb8b8781d59796324efa25909f3e2112833f01) in backport 6.10.
+we need to add a default QoS map. when hostapd config didn't set QoS map, we need pass default QoS map to HW.
+
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+
+1. Remove exported function for SoftMAC driver to get QoS map.
+Instead, add callback function for mac80211 to set QoS map in HW, which is more intuitive.
+2. Fix inconsistent QoS mapping between AP and AP_VLAN IFs.
+Specifically, when WDS AP IF is connected by a WDS STA, the QoS map of the AP_VLAN VIF is NULL.
+So the QoS types of packets to the WDS STA will be determined using the default mapping rule.
+However, SoftMAC driver uses the QoS map of the AP VIF, which may already be set.
+Therefore, it is possible that the QoS mappings of SW and HW are inconsistent.
+Thus, sync QoS map of AP VIF to that of AP_VLAN VIF.
+
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+
+Refactor drv_set_qos_map function.
+1. use dscp exception to instead of dscp range.
+for example: if dscp value is 15. Original way will translte to tid 0.
+but mac80211 will translate to tid 1.
+
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ include/net/mac80211.h    |  4 +++-
+ net/mac80211/cfg.c        |  2 +-
+ net/mac80211/chan.c       |  7 +++++++
+ net/mac80211/driver-ops.h | 22 ++++++++++++++++++++++
+ net/mac80211/iface.c      | 23 ++++++++++++++++++++++-
+ net/mac80211/trace.h      |  6 ++++++
+ net/mac80211/util.c       |  2 +-
+ 7 files changed, 62 insertions(+), 4 deletions(-)
+
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 5cd9432..bb4f12f 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -4453,6 +4453,7 @@ struct ieee80211_prep_tx_info {
+  *	if the requested TID-To-Link mapping can be accepted or not.
+  *	If it's not accepted the driver may suggest a preferred mapping and
+  *	modify @ttlm parameter with the suggested TID-to-Link mapping.
++ * @set_qos_map: Set QoS mapping information to driver.
+  */
+ struct ieee80211_ops {
+ 	void (*tx)(struct ieee80211_hw *hw,
+@@ -4840,6 +4841,8 @@ struct ieee80211_ops {
+ 	enum ieee80211_neg_ttlm_res
+ 	(*can_neg_ttlm)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 			struct ieee80211_neg_ttlm *ttlm);
++	int (*set_qos_map)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++			   struct cfg80211_qos_map *qos_map);
+ };
+ 
+ /**
+@@ -7719,5 +7722,4 @@ int ieee80211_emulate_switch_vif_chanctx(struct ieee80211_hw *hw,
+  * @hw: pointer as obtained from ieee80211_alloc_hw()
+  */
+ unsigned long ieee80211_get_scanning(struct ieee80211_hw *hw);
+-
+ #endif /* MAC80211_H */
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 36c898c..02374b0 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -4431,7 +4431,7 @@ static int ieee80211_set_qos_map(struct wiphy *wiphy,
+ 	if (old_qos_map)
+ 		kfree_rcu(old_qos_map, rcu_head);
+ 
+-	return 0;
++	return drv_set_qos_map(sdata->local, sdata);
+ }
+ 
+ static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy,
+diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
+index bdff227..31d7fa8 100644
+--- a/net/mac80211/chan.c
++++ b/net/mac80211/chan.c
+@@ -941,6 +941,13 @@ static int ieee80211_assign_link_chanctx(struct ieee80211_link_data *link,
+ 
+ 	ieee80211_check_fast_xmit_iface(sdata);
+ 
++	/* FIXME: QoS MAP should be configured for each link (BSS).
++	 * We use assign_link_chanctx for the time being.
++	 * The problematic part is that everytime channel switch happens
++	 * the qos_map would get redundantly configured once.
++	 */
++	drv_set_qos_map(local, sdata);
++
+ 	return ret;
+ }
+ 
+diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
+index 4cddc3c..d226e31 100644
+--- a/net/mac80211/driver-ops.h
++++ b/net/mac80211/driver-ops.h
+@@ -1741,4 +1741,26 @@ drv_can_neg_ttlm(struct ieee80211_local *local,
+ 
+ 	return res;
+ }
++
++static inline int drv_set_qos_map(struct ieee80211_local *local,
++				  struct ieee80211_sub_if_data *sdata)
++{
++	int ret = -EOPNOTSUPP;
++	struct mac80211_qos_map *qos_map;
++
++	might_sleep();
++	if (!check_sdata_in_driver(sdata))
++		return -EIO;
++
++	qos_map = sdata_dereference(sdata->qos_map, sdata);
++
++	trace_drv_set_qos_map(local, sdata);
++	if (local->ops->set_qos_map)
++		ret = local->ops->set_qos_map(&local->hw, &sdata->vif,
++					      qos_map ? &qos_map->qos_map : NULL);
++	trace_drv_return_int(local, ret);
++
++	return ret;
++}
++
+ #endif /* __MAC80211_DRIVER_OPS */
+diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
+index 3936181..d959901 100644
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -385,8 +385,29 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
+ 			 * can only add VLANs to enabled APs
+ 			 */
+ 			if (iftype == NL80211_IFTYPE_AP_VLAN &&
+-			    nsdata->vif.type == NL80211_IFTYPE_AP)
++			    nsdata->vif.type == NL80211_IFTYPE_AP) {
++				struct mac80211_qos_map *old_map, *new_map = NULL;
++
+ 				sdata->bss = &nsdata->u.ap;
++
++				rcu_read_lock();
++				old_map = rcu_dereference(nsdata->qos_map);
++				if (old_map) {
++					new_map = kzalloc(sizeof(*new_map), GFP_KERNEL);
++					if (!new_map) {
++						rcu_read_unlock();
++						return -ENOMEM;
++					}
++					memcpy(&new_map->qos_map, &old_map->qos_map,
++					       sizeof(new_map->qos_map));
++				}
++				rcu_read_unlock();
++
++				old_map = sdata_dereference(sdata->qos_map, sdata);
++				rcu_assign_pointer(sdata->qos_map, new_map);
++				if (old_map)
++					kfree_rcu(old_map, rcu_head);
++			}
+ 		}
+ 	}
+ 
+diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
+index 68f86c3..36e500d 100644
+--- a/net/mac80211/trace.h
++++ b/net/mac80211/trace.h
+@@ -3175,6 +3175,12 @@ TRACE_EVENT(bss_color_bitmap,
+ 		"color=%u color_bitmap=0x%llx", __entry->color, __entry->color_bitmap
+ 	)
+ );
++
++DEFINE_EVENT(local_sdata_evt, drv_set_qos_map,
++	TP_PROTO(struct ieee80211_local *local,
++		 struct ieee80211_sub_if_data *sdata),
++	TP_ARGS(local, sdata)
++);
+ #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
+ 
+ #undef TRACE_INCLUDE_PATH
+diff --git a/net/mac80211/util.c b/net/mac80211/util.c
+index df4fa1a..4da6831 100644
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -4503,4 +4503,4 @@ unsigned long ieee80211_get_scanning(struct ieee80211_hw *hw)
+ 
+ 	return local->scanning;
+ }
+-EXPORT_SYMBOL(ieee80211_get_scanning);
++EXPORT_SYMBOL(ieee80211_get_scanning);
+\ No newline at end of file
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0075-mtk-mac80211-add-per-link-txpower-config.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0075-mtk-mac80211-add-per-link-txpower-config.patch
new file mode 100644
index 0000000..7320f77
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0075-mtk-mac80211-add-per-link-txpower-config.patch
@@ -0,0 +1,528 @@
+From 0f6ae2024ce001cf003239b3aaa56cb4a279bba9 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 22 Feb 2024 15:21:49 +0800
+Subject: [PATCH 75/89] mtk: mac80211: add per-link txpower config
+
+Add per-link txpower config & info dump
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ include/net/cfg80211.h     |  5 ++--
+ include/net/mac80211.h     |  2 +-
+ net/mac80211/cfg.c         | 48 +++++++++++++++++++++++++++-----------
+ net/mac80211/chan.c        |  4 ++--
+ net/mac80211/driver-ops.h  |  7 +++---
+ net/mac80211/ieee80211_i.h |  5 ++--
+ net/mac80211/iface.c       | 23 +++++++++---------
+ net/mac80211/link.c        |  3 +++
+ net/mac80211/mlme.c        |  2 +-
+ net/mac80211/trace.h       | 10 ++++----
+ net/wireless/nl80211.c     | 17 +++++++++++---
+ net/wireless/rdev-ops.h    | 12 ++++++----
+ net/wireless/trace.h       | 30 ++++++++++++++++++------
+ net/wireless/wext-compat.c |  4 ++--
+ 14 files changed, 116 insertions(+), 56 deletions(-)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index bedf711..a63f5bb 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -4749,9 +4749,10 @@ struct cfg80211_ops {
+ 	int	(*set_wiphy_params)(struct wiphy *wiphy, u32 changed);
+ 
+ 	int	(*set_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev,
+-				enum nl80211_tx_power_setting type, int mbm);
++				unsigned int link_id, enum nl80211_tx_power_setting type,
++				int mbm);
+ 	int	(*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev,
+-				int *dbm);
++				unsigned int link_id, int *dbm);
+ 
+ 	void	(*rfkill_poll)(struct wiphy *wiphy);
+ 
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index bb4f12f..30aa436 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -4746,7 +4746,7 @@ struct ieee80211_ops {
+ 	u32 (*get_expected_throughput)(struct ieee80211_hw *hw,
+ 				       struct ieee80211_sta *sta);
+ 	int (*get_txpower)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+-			   int *dbm);
++			   unsigned int link_id, int *dbm);
+ 
+ 	int (*tdls_channel_switch)(struct ieee80211_hw *hw,
+ 				   struct ieee80211_vif *vif,
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 02374b0..19171b0 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -3048,10 +3048,13 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+ 
+ static int ieee80211_set_tx_power(struct wiphy *wiphy,
+ 				  struct wireless_dev *wdev,
++				  unsigned int link_id,
+ 				  enum nl80211_tx_power_setting type, int mbm)
+ {
+ 	struct ieee80211_local *local = wiphy_priv(wiphy);
+ 	struct ieee80211_sub_if_data *sdata;
++	struct ieee80211_link_data *link;
++	struct ieee80211_bss_conf *link_conf;
+ 	enum nl80211_tx_power_setting txp_type = type;
+ 	bool update_txp_type = false;
+ 	bool has_monitor = false;
+@@ -3060,6 +3063,11 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
+ 
+ 	if (wdev) {
+ 		sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
++		link = sdata_dereference(sdata->link[link_id], sdata);
++		if (!link)
++			return -ENOLINK;
++
++		link_conf = link->conf;
+ 
+ 		if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
+ 			sdata = wiphy_dereference(local->hw.wiphy,
+@@ -3070,7 +3078,7 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
+ 
+ 		switch (type) {
+ 		case NL80211_TX_POWER_AUTOMATIC:
+-			sdata->deflink.user_power_level =
++			link->user_power_level =
+ 				IEEE80211_UNSET_POWER_LEVEL;
+ 			txp_type = NL80211_TX_POWER_LIMITED;
+ 			break;
+@@ -3078,20 +3086,24 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
+ 		case NL80211_TX_POWER_FIXED:
+ 			if (mbm < 0 || (mbm % 100))
+ 				return -EOPNOTSUPP;
+-			sdata->deflink.user_power_level = MBM_TO_DBM(mbm);
++			link->user_power_level = MBM_TO_DBM(mbm);
+ 			break;
+ 		}
+ 
+-		if (txp_type != sdata->vif.bss_conf.txpower_type) {
++		if (txp_type != link_conf->txpower_type) {
+ 			update_txp_type = true;
+-			sdata->vif.bss_conf.txpower_type = txp_type;
++			link_conf->txpower_type = txp_type;
+ 		}
+ 
+-		ieee80211_recalc_txpower(sdata, update_txp_type);
++		ieee80211_recalc_txpower(sdata, update_txp_type, link);
+ 
+ 		return 0;
+ 	}
+ 
++	/*TODO: handle single wiphy */
++	wiphy_info(wiphy, "Setting txpower for the entire band is not supported\n");
++	return -EOPNOTSUPP;
++
+ 	switch (type) {
+ 	case NL80211_TX_POWER_AUTOMATIC:
+ 		local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
+@@ -3118,7 +3130,12 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
+ 	list_for_each_entry(sdata, &local->interfaces, list) {
+ 		if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
+ 			continue;
+-		ieee80211_recalc_txpower(sdata, update_txp_type);
++		/* Due to mac80211 not pass link id to here, use first link for now */
++		if (ieee80211_vif_is_mld(&sdata->vif))
++			ieee80211_recalc_txpower(sdata, update_txp_type, sdata->link[0]);
++		else
++			ieee80211_recalc_txpower(sdata, update_txp_type, &sdata->deflink);
++
+ 	}
+ 
+ 	if (has_monitor) {
+@@ -3130,7 +3147,7 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
+ 				update_txp_type = true;
+ 			sdata->vif.bss_conf.txpower_type = txp_type;
+ 
+-			ieee80211_recalc_txpower(sdata, update_txp_type);
++			ieee80211_recalc_txpower(sdata, update_txp_type, &sdata->deflink);
+ 		}
+ 	}
+ 
+@@ -3139,18 +3156,23 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
+ 
+ static int ieee80211_get_tx_power(struct wiphy *wiphy,
+ 				  struct wireless_dev *wdev,
+-				  int *dbm)
++				  unsigned int link_id, int *dbm)
+ {
+ 	struct ieee80211_local *local = wiphy_priv(wiphy);
+ 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
++	struct ieee80211_link_data *link;
+ 
+ 	if (local->ops->get_txpower)
+-		return drv_get_txpower(local, sdata, dbm);
++		return drv_get_txpower(local, sdata, link_id, dbm);
+ 
+-	if (local->emulate_chanctx)
+-		*dbm = local->hw.conf.power_level;
+-	else
+-		*dbm = sdata->vif.bss_conf.txpower;
++	*dbm = local->hw.conf.power_level;
++	if (!local->emulate_chanctx) {
++		link = sdata_dereference(sdata->link[link_id], sdata);
++		if (!link)
++			return -ENOLINK;
++
++		*dbm = link->conf->txpower;
++	}
+ 
+ 	/* INT_MIN indicates no power level was set yet */
+ 	if (*dbm == INT_MIN)
+diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
+index 31d7fa8..e7d2ee2 100644
+--- a/net/mac80211/chan.c
++++ b/net/mac80211/chan.c
+@@ -913,7 +913,7 @@ static int ieee80211_assign_link_chanctx(struct ieee80211_link_data *link,
+ 	}
+ 
+ 	if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) {
+-		ieee80211_recalc_txpower(sdata, false);
++		ieee80211_recalc_txpower(sdata, false, link);
+ 		ieee80211_recalc_chanctx_min_def(local, new_ctx, NULL, false);
+ 	}
+ 
+@@ -1730,7 +1730,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
+ 								  link,
+ 								  changed);
+ 
+-			ieee80211_recalc_txpower(sdata, false);
++			ieee80211_recalc_txpower(sdata, false, link);
+ 		}
+ 
+ 		ieee80211_recalc_chanctx_chantype(local, ctx);
+diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
+index d226e31..aca4426 100644
+--- a/net/mac80211/driver-ops.h
++++ b/net/mac80211/driver-ops.h
+@@ -1273,7 +1273,8 @@ static inline u32 drv_get_expected_throughput(struct ieee80211_local *local,
+ }
+ 
+ static inline int drv_get_txpower(struct ieee80211_local *local,
+-				  struct ieee80211_sub_if_data *sdata, int *dbm)
++				  struct ieee80211_sub_if_data *sdata,
++				  unsigned int link_id, int *dbm)
+ {
+ 	int ret;
+ 
+@@ -1283,8 +1284,8 @@ static inline int drv_get_txpower(struct ieee80211_local *local,
+ 	if (!local->ops->get_txpower)
+ 		return -EOPNOTSUPP;
+ 
+-	ret = local->ops->get_txpower(&local->hw, &sdata->vif, dbm);
+-	trace_drv_get_txpower(local, sdata, *dbm, ret);
++	ret = local->ops->get_txpower(&local->hw, &sdata->vif, link_id, dbm);
++	trace_drv_get_txpower(local, sdata, link_id, *dbm, ret);
+ 
+ 	return ret;
+ }
+diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
+index 2cb80c3..6f9a9a6 100644
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -2050,9 +2050,10 @@ void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata);
+ int ieee80211_add_virtual_monitor(struct ieee80211_local *local);
+ void ieee80211_del_virtual_monitor(struct ieee80211_local *local);
+ 
+-bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
++bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
++				struct ieee80211_link_data *link);
+ void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
+-			      bool update_bss);
++			      bool update_bss, struct ieee80211_link_data *link);
+ void ieee80211_recalc_offload(struct ieee80211_local *local);
+ 
+ static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
+diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
+index d959901..eb7a05d 100644
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -44,13 +44,14 @@
+ 
+ static void ieee80211_iface_work(struct wiphy *wiphy, struct wiphy_work *work);
+ 
+-bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
++bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
++				struct ieee80211_link_data *link)
+ {
+ 	struct ieee80211_chanctx_conf *chanctx_conf;
+ 	int power;
+ 
+ 	rcu_read_lock();
+-	chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
++	chanctx_conf = rcu_dereference(link->conf->chanctx_conf);
+ 	if (!chanctx_conf) {
+ 		rcu_read_unlock();
+ 		return false;
+@@ -59,14 +60,14 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
+ 	power = ieee80211_chandef_max_power(&chanctx_conf->def);
+ 	rcu_read_unlock();
+ 
+-	if (sdata->deflink.user_power_level != IEEE80211_UNSET_POWER_LEVEL)
+-		power = min(power, sdata->deflink.user_power_level);
++	if (link->user_power_level != IEEE80211_UNSET_POWER_LEVEL)
++		power = min(power, link->user_power_level);
+ 
+-	if (sdata->deflink.ap_power_level != IEEE80211_UNSET_POWER_LEVEL)
+-		power = min(power, sdata->deflink.ap_power_level);
++	if (link->ap_power_level != IEEE80211_UNSET_POWER_LEVEL)
++		power = min(power, link->ap_power_level);
+ 
+-	if (power != sdata->vif.bss_conf.txpower) {
+-		sdata->vif.bss_conf.txpower = power;
++	if (power != link->conf->txpower) {
++		link->conf->txpower = power;
+ 		ieee80211_hw_config(sdata->local, 0);
+ 		return true;
+ 	}
+@@ -75,11 +76,11 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
+ }
+ 
+ void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
+-			      bool update_bss)
++			      bool update_bss, struct ieee80211_link_data *link)
+ {
+-	if (__ieee80211_recalc_txpower(sdata) ||
++	if (__ieee80211_recalc_txpower(sdata, link) ||
+ 	    (update_bss && ieee80211_sdata_running(sdata)))
+-		ieee80211_link_info_change_notify(sdata, &sdata->deflink,
++		ieee80211_link_info_change_notify(sdata, link,
+ 						  BSS_CHANGED_TXPOWER);
+ }
+ 
+diff --git a/net/mac80211/link.c b/net/mac80211/link.c
+index 11502da..349f596 100644
+--- a/net/mac80211/link.c
++++ b/net/mac80211/link.c
+@@ -37,6 +37,9 @@ void ieee80211_link_init(struct ieee80211_sub_if_data *sdata,
+ 	link_conf->link_id = link_id;
+ 	link_conf->vif = &sdata->vif;
+ 
++	link->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
++	link->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
++
+ 	wiphy_work_init(&link->csa.finalize_work,
+ 			ieee80211_csa_finalize_work);
+ 	wiphy_work_init(&link->color_change_finalize_work,
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index b684fed..a3873d2 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -2802,7 +2802,7 @@ static u64 ieee80211_handle_pwr_constr(struct ieee80211_link_data *link,
+ 	}
+ 
+ 	link->ap_power_level = new_ap_level;
+-	if (__ieee80211_recalc_txpower(sdata))
++	if (__ieee80211_recalc_txpower(sdata, link))
+ 		return BSS_CHANGED_TXPOWER;
+ 	return 0;
+ }
+diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
+index 36e500d..3e06017 100644
+--- a/net/mac80211/trace.h
++++ b/net/mac80211/trace.h
+@@ -2170,13 +2170,14 @@ DEFINE_EVENT(chanswitch_evt, drv_channel_switch_rx_beacon,
+ TRACE_EVENT(drv_get_txpower,
+ 	TP_PROTO(struct ieee80211_local *local,
+ 		 struct ieee80211_sub_if_data *sdata,
+-		 int dbm, int ret),
++		 unsigned int link_id, int dbm, int ret),
+ 
+-	TP_ARGS(local, sdata, dbm, ret),
++	TP_ARGS(local, sdata, link_id, dbm, ret),
+ 
+ 	TP_STRUCT__entry(
+ 		LOCAL_ENTRY
+ 		VIF_ENTRY
++		__field(unsigned int, link_id)
+ 		__field(int, dbm)
+ 		__field(int, ret)
+ 	),
+@@ -2184,13 +2185,14 @@ TRACE_EVENT(drv_get_txpower,
+ 	TP_fast_assign(
+ 		LOCAL_ASSIGN;
+ 		VIF_ASSIGN;
++		__entry->link_id = link_id;
+ 		__entry->dbm = dbm;
+ 		__entry->ret = ret;
+ 	),
+ 
+ 	TP_printk(
+-		LOCAL_PR_FMT VIF_PR_FMT " dbm:%d ret:%d",
+-		LOCAL_PR_ARG, VIF_PR_ARG, __entry->dbm, __entry->ret
++		LOCAL_PR_FMT VIF_PR_FMT " link_id:%d dbm:%d ret:%d",
++		LOCAL_PR_ARG, VIF_PR_ARG, __entry->link_id, __entry->dbm, __entry->ret
+ 	)
+ );
+ 
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index 756a29a..36d35f0 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -3753,6 +3753,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
+ 		struct wireless_dev *txp_wdev = wdev;
+ 		enum nl80211_tx_power_setting type;
+ 		int idx, mbm = 0;
++		unsigned int link_id = nl80211_link_id(info->attrs);
+ 
+ 		if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER))
+ 			txp_wdev = NULL;
+@@ -3776,7 +3777,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
+ 			mbm = nla_get_u32(info->attrs[idx]);
+ 		}
+ 
+-		result = rdev_set_tx_power(rdev, txp_wdev, type, mbm);
++		result = rdev_set_tx_power(rdev, txp_wdev, link_id, type, mbm);
+ 		if (result)
+ 			goto out;
+ 	}
+@@ -4047,10 +4048,10 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
+ 			goto nla_put_failure;
+ 	}
+ 
+-	if (rdev->ops->get_tx_power) {
++	if (!wdev->valid_links && rdev->ops->get_tx_power) {
+ 		int dbm, ret;
+ 
+-		ret = rdev_get_tx_power(rdev, wdev, &dbm);
++		ret = rdev_get_tx_power(rdev, wdev, 0, &dbm);
+ 		if (ret == 0 &&
+ 		    nla_put_u32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL,
+ 				DBM_TO_MBM(dbm)))
+@@ -4119,6 +4120,16 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
+ 			if (ret == 0 && nl80211_send_chandef(msg, &chandef))
+ 				goto nla_put_failure;
+ 
++			if (rdev->ops->get_tx_power) {
++				int dbm, ret;
++
++				ret = rdev_get_tx_power(rdev, wdev, link_id, &dbm);
++				if (ret == 0 &&
++				    nla_put_u32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL,
++						DBM_TO_MBM(dbm)))
++					goto nla_put_failure;
++			}
++
+ 			nla_nest_end(msg, link);
+ 		}
+ 
+diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
+index 5b3c94f..9b411db 100644
+--- a/net/wireless/rdev-ops.h
++++ b/net/wireless/rdev-ops.h
+@@ -589,21 +589,23 @@ rdev_set_wiphy_params(struct cfg80211_registered_device *rdev, u32 changed)
+ 
+ static inline int rdev_set_tx_power(struct cfg80211_registered_device *rdev,
+ 				    struct wireless_dev *wdev,
++				    unsigned int link_id,
+ 				    enum nl80211_tx_power_setting type, int mbm)
+ {
+ 	int ret;
+-	trace_rdev_set_tx_power(&rdev->wiphy, wdev, type, mbm);
+-	ret = rdev->ops->set_tx_power(&rdev->wiphy, wdev, type, mbm);
++	trace_rdev_set_tx_power(&rdev->wiphy, wdev, link_id, type, mbm);
++	ret = rdev->ops->set_tx_power(&rdev->wiphy, wdev, link_id, type, mbm);
+ 	trace_rdev_return_int(&rdev->wiphy, ret);
+ 	return ret;
+ }
+ 
+ static inline int rdev_get_tx_power(struct cfg80211_registered_device *rdev,
+-				    struct wireless_dev *wdev, int *dbm)
++				    struct wireless_dev *wdev,
++				    unsigned int link_id, int *dbm)
+ {
+ 	int ret;
+-	trace_rdev_get_tx_power(&rdev->wiphy, wdev);
+-	ret = rdev->ops->get_tx_power(&rdev->wiphy, wdev, dbm);
++	trace_rdev_get_tx_power(&rdev->wiphy, wdev, link_id);
++	ret = rdev->ops->get_tx_power(&rdev->wiphy, wdev, link_id, dbm);
+ 	trace_rdev_return_int_int(&rdev->wiphy, ret, *dbm);
+ 	return ret;
+ }
+diff --git a/net/wireless/trace.h b/net/wireless/trace.h
+index 6ff6091..6af432e 100644
+--- a/net/wireless/trace.h
++++ b/net/wireless/trace.h
+@@ -1688,29 +1688,45 @@ TRACE_EVENT(rdev_set_wiphy_params,
+ 		  WIPHY_PR_ARG, __entry->changed)
+ );
+ 
+-DEFINE_EVENT(wiphy_wdev_evt, rdev_get_tx_power,
+-	TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+-	TP_ARGS(wiphy, wdev)
++TRACE_EVENT(rdev_get_tx_power,
++	TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
++		 unsigned int link_id),
++	TP_ARGS(wiphy, wdev, link_id),
++	TP_STRUCT__entry(
++		WIPHY_ENTRY
++		WDEV_ENTRY
++		__field(unsigned int, link_id)
++	),
++	TP_fast_assign(
++		WIPHY_ASSIGN;
++		WDEV_ASSIGN;
++		__entry->link_id = link_id;
++	),
++	TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", link_id: %d",
++		  WIPHY_PR_ARG, WDEV_PR_ARG, __entry->link_id)
+ );
+ 
+ TRACE_EVENT(rdev_set_tx_power,
+ 	TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+-		 enum nl80211_tx_power_setting type, int mbm),
+-	TP_ARGS(wiphy, wdev, type, mbm),
++		 unsigned int link_id, enum nl80211_tx_power_setting type,
++		 int mbm),
++	TP_ARGS(wiphy, wdev, link_id, type, mbm),
+ 	TP_STRUCT__entry(
+ 		WIPHY_ENTRY
+ 		WDEV_ENTRY
++		__field(unsigned int, link_id)
+ 		__field(enum nl80211_tx_power_setting, type)
+ 		__field(int, mbm)
+ 	),
+ 	TP_fast_assign(
+ 		WIPHY_ASSIGN;
+ 		WDEV_ASSIGN;
++		__entry->link_id = link_id;
+ 		__entry->type = type;
+ 		__entry->mbm = mbm;
+ 	),
+-	TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", type: %u, mbm: %d",
+-		  WIPHY_PR_ARG, WDEV_PR_ARG,__entry->type, __entry->mbm)
++	TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", link_id: %d, type: %u, mbm: %d",
++		  WIPHY_PR_ARG, WDEV_PR_ARG, __entry->link_id, __entry->type, __entry->mbm)
+ );
+ 
+ TRACE_EVENT(rdev_return_int_int,
+diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
+index 2371069..73ef854 100644
+--- a/net/wireless/wext-compat.c
++++ b/net/wireless/wext-compat.c
+@@ -952,7 +952,7 @@ static int cfg80211_wext_siwtxpower(struct net_device *dev,
+ 	}
+ 
+ 	wiphy_lock(&rdev->wiphy);
+-	ret = rdev_set_tx_power(rdev, wdev, type, DBM_TO_MBM(dbm));
++	ret = rdev_set_tx_power(rdev, wdev, 0, type, DBM_TO_MBM(dbm));
+ 	wiphy_unlock(&rdev->wiphy);
+ 
+ 	return ret;
+@@ -975,7 +975,7 @@ static int cfg80211_wext_giwtxpower(struct net_device *dev,
+ 		return -EOPNOTSUPP;
+ 
+ 	wiphy_lock(&rdev->wiphy);
+-	err = rdev_get_tx_power(rdev, wdev, &val);
++	err = rdev_get_tx_power(rdev, wdev, 0, &val);
+ 	wiphy_unlock(&rdev->wiphy);
+ 	if (err)
+ 		return err;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0076-mtk-mac80211-add-link-information-when-dump-station.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0076-mtk-mac80211-add-link-information-when-dump-station.patch
new file mode 100644
index 0000000..20d05c5
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0076-mtk-mac80211-add-link-information-when-dump-station.patch
@@ -0,0 +1,657 @@
+From 419c3cdbd941ce47b8b660e7fa69e48b8de5b12b Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Tue, 11 Jun 2024 18:08:13 +0800
+Subject: [PATCH 76/89] mtk: mac80211: add link information when dump station
+
+Report following per-link information to upper-layer application:
+- TX/RX byte counts
+- TX MPDU failed/retried counts
+- RX MPDU total/failed counts
+- TX/RX airtime
+- ACK RSSI
+
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ include/net/cfg80211.h    |  67 ++++++++++++
+ include/net/mac80211.h    |   9 +-
+ net/mac80211/driver-ops.h |  18 ++++
+ net/mac80211/ethtool.c    |  39 +++----
+ net/mac80211/sta_info.c   |  26 +++++
+ net/wireless/nl80211.c    | 211 ++++++++++++++++++++++++++++++++------
+ 6 files changed, 317 insertions(+), 53 deletions(-)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index a63f5bb..110e555 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -2051,6 +2051,64 @@ struct cfg80211_tid_stats {
+ 
+ #define IEEE80211_MAX_CHAINS	4
+ 
++/**
++ * struct station_link_info - station link information
++ *
++ * @filled: bitflag of flags using the bits of &enum nl80211_sta_info to
++ *	indicate the relevant values in this struct for them
++ * @link_addr: link address
++ * @rx_bytes: bytes (size of MPDUs) received from this station link
++ * @tx_bytes: bytes (size of MPDUs) transmitted to this station link
++ * @signal: The signal strength, type depends on the wiphy's signal_type.
++ *	For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_.
++ * @signal_avg: Average signal strength, type depends on the wiphy's signal_type.
++ *	For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_.
++ * @chains: bitmask for filled values in @chain_signal, @chain_signal_avg
++ * @chain_signal: per-chain signal strength of last received packet in dBm
++ * @chain_signal_avg: per-chain signal strength average in dBm
++ * @txrate: current unicast bitrate to this station link
++ * @rxrate: current unicast bitrate from this station link
++ * @tx_retries: cumulative retry counts (MPDUs)
++ * @tx_failed: number of failed transmissions (MPDUs) (retries exceeded, no ACK)
++ * @bss_param: current BSS parameters
++ * @tx_duration: aggregate PPDU duration(usecs) for all the frames to a peer
++ * @rx_duration: aggregate PPDU duration(usecs) for all the frames from a peer
++ * @ack_signal: signal strength (in dBm) of the last ACK frame.
++ * @avg_ack_signal: average rssi value of ack packet for the no of msdu's has
++ *	been sent.
++ * @rx_mpdu_count: number of MPDUs received from this station link
++ * @fcs_err_count: number of packets (MPDUs) received from this station link with
++ *	an FCS error. This counter should be incremented only when TA of the
++ *	received packet with an FCS error matches the peer MAC address.
++ */
++struct station_link_info {
++	u64 filled;
++	u8 link_addr[ETH_ALEN] __aligned(2);
++	u64 rx_bytes;
++	u64 tx_bytes;
++	s8 signal;
++	s8 signal_avg;
++
++	u8 chains;
++	s8 chain_signal[IEEE80211_MAX_CHAINS];
++	s8 chain_signal_avg[IEEE80211_MAX_CHAINS];
++
++	struct rate_info txrate;
++	struct rate_info rxrate;
++	u32 tx_retries;
++	u32 tx_failed;
++	struct sta_bss_parameters bss_param;
++
++	u64 tx_duration;
++	u64 rx_duration;
++
++	s8 ack_signal;
++	s8 avg_ack_signal;
++
++	u32 rx_mpdu_count;
++	u32 fcs_err_count;
++};
++
+ /**
+  * struct station_info - station information
+  *
+@@ -2200,6 +2258,15 @@ struct station_info {
+ 	u8 mld_addr[ETH_ALEN] __aligned(2);
+ 	const u8 *assoc_resp_ies;
+ 	size_t assoc_resp_ies_len;
++
++	u16 valid_links;
++
++	/*
++	 * FIXME: Should be refactored to legacy (real) + links (pointer)
++	 * for saving memory space, as MAX_NUM_LINKS is 15 which would
++	 * cause much of the space allocated but never unused
++	 */
++	struct station_link_info links[IEEE80211_MLD_MAX_NUM_LINKS];
+ };
+ 
+ /**
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 30aa436..916f37c 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -1491,7 +1491,7 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
+  * @RX_FLAG_AMPDU_EOF_BIT_KNOWN: The EOF value is known
+  * @RX_FLAG_RADIOTAP_HE: HE radiotap data is present
+  *	(&struct ieee80211_radiotap_he, mac80211 will fill in
+- *	
++ *
+  *	 - DATA3_DATA_MCS
+  *	 - DATA3_DATA_DCM
+  *	 - DATA3_CODING
+@@ -1499,7 +1499,7 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
+  *	 - DATA5_DATA_BW_RU_ALLOC
+  *	 - DATA6_NSTS
+  *	 - DATA3_STBC
+- *	
++ *
+  *	from the RX info data, so leave those zeroed when building this data)
+  * @RX_FLAG_RADIOTAP_HE_MU: HE MU radiotap data is present
+  *	(&struct ieee80211_radiotap_he_mu)
+@@ -4581,6 +4581,11 @@ struct ieee80211_ops {
+ 			       struct ieee80211_vif *vif,
+ 			       struct ieee80211_sta *sta,
+ 			       struct station_info *sinfo);
++	void (*sta_link_statistics)(struct ieee80211_hw *hw,
++				    struct ieee80211_vif *vif,
++				    struct ieee80211_sta *sta,
++				    unsigned int link_id,
++				    struct station_link_info *linfo);
+ 	int (*conf_tx)(struct ieee80211_hw *hw,
+ 		       struct ieee80211_vif *vif,
+ 		       unsigned int link_id, u16 ac,
+diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
+index aca4426..b76fae2 100644
+--- a/net/mac80211/driver-ops.h
++++ b/net/mac80211/driver-ops.h
+@@ -631,6 +631,24 @@ static inline void drv_sta_statistics(struct ieee80211_local *local,
+ 	trace_drv_return_void(local);
+ }
+ 
++static inline void drv_sta_link_statistics(struct ieee80211_local *local,
++					   struct ieee80211_sub_if_data *sdata,
++					   struct ieee80211_sta *sta,
++					   unsigned int link_id,
++					   struct station_link_info *linfo)
++{
++	might_sleep();
++	lockdep_assert_wiphy(local->hw.wiphy);
++
++	sdata = get_bss_sdata(sdata);
++	if (!check_sdata_in_driver(sdata))
++		return;
++
++	if (local->ops->sta_link_statistics)
++		local->ops->sta_link_statistics(&local->hw, &sdata->vif, sta,
++						link_id, linfo);
++}
++
+ int drv_conf_tx(struct ieee80211_local *local,
+ 		struct ieee80211_link_data *link, u16 ac,
+ 		const struct ieee80211_tx_queue_params *params);
+diff --git a/net/mac80211/ethtool.c b/net/mac80211/ethtool.c
+index 03c2de9..0f25fb9 100644
+--- a/net/mac80211/ethtool.c
++++ b/net/mac80211/ethtool.c
+@@ -89,7 +89,7 @@ static void ieee80211_get_stats(struct net_device *dev,
+ 	struct ieee80211_channel *channel;
+ 	struct sta_info *sta;
+ 	struct ieee80211_local *local = sdata->local;
+-	struct station_info sinfo;
++	struct station_info *sinfo;
+ 	struct survey_info survey;
+ 	int i, q;
+ #define STA_STATS_SURVEY_LEN 7
+@@ -98,17 +98,17 @@ static void ieee80211_get_stats(struct net_device *dev,
+ 
+ #define ADD_STA_STATS(sta)					\
+ 	do {							\
+-		data[i++] += sinfo.rx_packets;			\
+-		data[i++] += sinfo.rx_bytes;			\
++		data[i++] += sinfo->rx_packets;			\
++		data[i++] += sinfo->rx_bytes;			\
+ 		data[i++] += (sta)->rx_stats.num_duplicates;	\
+ 		data[i++] += (sta)->rx_stats.fragments;		\
+-		data[i++] += sinfo.rx_dropped_misc;		\
++		data[i++] += sinfo->rx_dropped_misc;		\
+ 								\
+-		data[i++] += sinfo.tx_packets;			\
+-		data[i++] += sinfo.tx_bytes;			\
++		data[i++] += sinfo->tx_packets;			\
++		data[i++] += sinfo->tx_bytes;			\
+ 		data[i++] += (sta)->status_stats.filtered;	\
+-		data[i++] += sinfo.tx_failed;			\
+-		data[i++] += sinfo.tx_retries;			\
++		data[i++] += sinfo->tx_failed;			\
++		data[i++] += sinfo->tx_retries;			\
+ 	} while (0)
+ 
+ 	/* For Managed stations, find the single station based on BSSID
+@@ -116,6 +116,9 @@ static void ieee80211_get_stats(struct net_device *dev,
+ 	 * stations and add stats for any station that is assigned to this
+ 	 * network device.
+ 	 */
++	sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL);
++	if (!sinfo)
++		return;
+ 
+ 	wiphy_lock(local->hw.wiphy);
+ 
+@@ -125,8 +128,7 @@ static void ieee80211_get_stats(struct net_device *dev,
+ 		if (!(sta && !WARN_ON(sta->sdata->dev != dev)))
+ 			goto do_survey;
+ 
+-		memset(&sinfo, 0, sizeof(sinfo));
+-		sta_set_sinfo(sta, &sinfo, false);
++		sta_set_sinfo(sta, sinfo, false);
+ 
+ 		i = 0;
+ 		ADD_STA_STATS(&sta->deflink);
+@@ -134,17 +136,17 @@ static void ieee80211_get_stats(struct net_device *dev,
+ 		data[i++] = sta->sta_state;
+ 
+ 
+-		if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_TX_BITRATE))
++		if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_TX_BITRATE))
+ 			data[i] = 100000ULL *
+-				cfg80211_calculate_bitrate(&sinfo.txrate);
++				cfg80211_calculate_bitrate(&sinfo->txrate);
+ 		i++;
+-		if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_RX_BITRATE))
++		if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_RX_BITRATE))
+ 			data[i] = 100000ULL *
+-				cfg80211_calculate_bitrate(&sinfo.rxrate);
++				cfg80211_calculate_bitrate(&sinfo->rxrate);
+ 		i++;
+ 
+-		if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG))
+-			data[i] = (u8)sinfo.signal_avg;
++		if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG))
++			data[i] = (u8)sinfo->signal_avg;
+ 		i++;
+ 	} else {
+ 		list_for_each_entry(sta, &local->sta_list, list) {
+@@ -152,14 +154,15 @@ static void ieee80211_get_stats(struct net_device *dev,
+ 			if (sta->sdata->dev != dev)
+ 				continue;
+ 
+-			memset(&sinfo, 0, sizeof(sinfo));
+-			sta_set_sinfo(sta, &sinfo, false);
++			memset(sinfo, 0, sizeof(*sinfo));
++			sta_set_sinfo(sta, sinfo, false);
+ 			i = 0;
+ 			ADD_STA_STATS(&sta->deflink);
+ 		}
+ 	}
+ 
+ do_survey:
++	kfree(sinfo);
+ 	i = STA_STATS_LEN - STA_STATS_SURVEY_LEN;
+ 	/* Get survey stats for current channel */
+ 	survey.filled = 0;
+diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
+index 57a2809..b1eb519 100644
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -2862,6 +2862,32 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
+ 		sinfo->airtime_link_metric =
+ 			airtime_link_metric_get(local, sta);
+ 	}
++
++	sinfo->valid_links = sta->sta.valid_links;
++	if (sinfo->valid_links) {
++		unsigned int link_id;
++
++		sinfo->mlo_params_valid = true;
++		memcpy(sinfo->mld_addr, sta->sta.addr, ETH_ALEN);
++		for_each_valid_link(sinfo, link_id) {
++			struct ieee80211_link_sta *link_sta =
++				link_sta_dereference_protected(&sta->sta, link_id);
++			struct ieee80211_bss_conf *link_conf =
++				sdata_dereference(sdata->vif.link_conf[link_id],
++						  sdata);
++			struct station_link_info *linfo = &sinfo->links[link_id];
++
++			if (!link_sta || !link_conf)
++				continue;
++
++			memcpy(linfo->link_addr, link_sta->addr, ETH_ALEN);
++			drv_sta_link_statistics(local, sdata, &sta->sta,
++						link_id, linfo);
++			linfo->filled |= BIT_ULL(NL80211_STA_INFO_BSS_PARAM);
++			linfo->bss_param.dtim_period = link_conf->dtim_period;
++			linfo->bss_param.beacon_interval = link_conf->beacon_int;
++		}
++	}
+ }
+ 
+ u32 sta_get_expected_throughput(struct sta_info *sta)
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index 36d35f0..e62116e 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -6939,14 +6939,121 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
+ 		goto nla_put_failure;
+ 
+ 	if (sinfo->mlo_params_valid) {
+-		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+-			       sinfo->assoc_link_id))
+-			goto nla_put_failure;
++		struct nlattr *nested;
++		unsigned int link_id;
++		int i = 1;
+ 
+ 		if (!is_zero_ether_addr(sinfo->mld_addr) &&
+ 		    nla_put(msg, NL80211_ATTR_MLD_ADDR, ETH_ALEN,
+ 			    sinfo->mld_addr))
+ 			goto nla_put_failure;
++
++		nested = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS);
++		if (!nested)
++			goto nla_put_failure;
++
++		for_each_valid_link(sinfo, link_id) {
++			struct nlattr *nested_mlo_links;
++			struct station_link_info *linfo = &sinfo->links[link_id];
++
++			nested_mlo_links = nla_nest_start(msg, i);
++			if (!nested_mlo_links)
++				goto nla_put_failure;
++
++			if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
++			    (linfo->link_addr &&
++			    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
++				    linfo->link_addr)))
++				goto nla_put_failure;
++
++			sinfoattr = nla_nest_start_noflag(msg, NL80211_ATTR_STA_INFO);
++			if (!sinfoattr)
++				goto nla_put_failure;
++
++#define PUT_LINFO(attr, memb, type) do {				\
++	BUILD_BUG_ON(sizeof(type) == sizeof(u64));			\
++	if (linfo->filled & BIT_ULL(NL80211_STA_INFO_ ## attr) &&	\
++	    nla_put_ ## type(msg, NL80211_STA_INFO_ ## attr,		\
++			     linfo->memb))				\
++		goto nla_put_failure;					\
++	} while (0)
++#define PUT_LINFO_U64(attr, memb) do {					\
++	if (linfo->filled & BIT_ULL(NL80211_STA_INFO_ ## attr) &&	\
++	    nla_put_u64_64bit(msg, NL80211_STA_INFO_ ## attr,		\
++			      linfo->memb, NL80211_STA_INFO_PAD))	\
++		goto nla_put_failure;					\
++	} while (0)
++
++			PUT_LINFO_U64(RX_BYTES64, rx_bytes);
++			PUT_LINFO_U64(TX_BYTES64, tx_bytes);
++			PUT_LINFO_U64(RX_DURATION, rx_duration);
++			PUT_LINFO_U64(TX_DURATION, tx_duration);
++
++			switch (rdev->wiphy.signal_type) {
++			case CFG80211_SIGNAL_TYPE_MBM:
++				PUT_LINFO(SIGNAL, signal, u8);
++				PUT_LINFO(SIGNAL_AVG, signal_avg, u8);
++				break;
++			default:
++				break;
++			}
++			if (linfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) {
++				if (!nl80211_put_signal(msg, linfo->chains,
++							linfo->chain_signal,
++							NL80211_STA_INFO_CHAIN_SIGNAL))
++					goto nla_put_failure;
++			}
++			if (linfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) {
++				if (!nl80211_put_signal(msg, linfo->chains,
++							linfo->chain_signal_avg,
++							NL80211_STA_INFO_CHAIN_SIGNAL_AVG))
++					goto nla_put_failure;
++			}
++			if (linfo->filled & BIT_ULL(NL80211_STA_INFO_TX_BITRATE)) {
++				if (!nl80211_put_sta_rate(msg, &linfo->txrate,
++							  NL80211_STA_INFO_TX_BITRATE))
++					goto nla_put_failure;
++			}
++			if (linfo->filled & BIT_ULL(NL80211_STA_INFO_RX_BITRATE)) {
++				if (!nl80211_put_sta_rate(msg, &linfo->rxrate,
++							  NL80211_STA_INFO_RX_BITRATE))
++					goto nla_put_failure;
++			}
++
++			PUT_LINFO(TX_RETRIES, tx_retries, u32);
++			PUT_LINFO(TX_FAILED, tx_failed, u32);
++
++			if (linfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM)) {
++				bss_param = nla_nest_start_noflag(msg,
++								  NL80211_STA_INFO_BSS_PARAM);
++				if (!bss_param)
++					goto nla_put_failure;
++
++				if (nla_put_u8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD,
++					       linfo->bss_param.dtim_period) ||
++				    nla_put_u16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
++						linfo->bss_param.beacon_interval))
++					goto nla_put_failure;
++
++				nla_nest_end(msg, bss_param);
++			}
++
++			PUT_LINFO(RX_MPDUS, rx_mpdu_count, u32);
++			PUT_LINFO(FCS_ERROR_COUNT, fcs_err_count, u32);
++			if (wiphy_ext_feature_isset(&rdev->wiphy,
++						    NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT)) {
++				PUT_LINFO(ACK_SIGNAL, ack_signal, u8);
++				PUT_LINFO(ACK_SIGNAL_AVG, avg_ack_signal, u8);
++			}
++
++#undef PUT_LINFO
++#undef PUT_LINFO_U64
++
++			nla_nest_end(msg, sinfoattr);
++			nla_nest_end(msg, nested_mlo_links);
++			i++;
++		}
++		nla_nest_end(msg, nested);
+ 	}
+ 
+ 	cfg80211_sinfo_release_content(sinfo);
+@@ -6962,7 +7069,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
+ static int nl80211_dump_station(struct sk_buff *skb,
+ 				struct netlink_callback *cb)
+ {
+-	struct station_info sinfo;
++	struct station_info *sinfo;
+ 	struct cfg80211_registered_device *rdev;
+ 	struct wireless_dev *wdev;
+ 	u8 mac_addr[ETH_ALEN];
+@@ -6972,6 +7079,10 @@ static int nl80211_dump_station(struct sk_buff *skb,
+ 	err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev, NULL);
+ 	if (err)
+ 		return err;
++
++	sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL);
++	if (!sinfo)
++		return -ENOMEM;
+ 	/* nl80211_prepare_wdev_dump acquired it in the successful case */
+ 	__acquire(&rdev->wiphy.mtx);
+ 
+@@ -6986,9 +7097,9 @@ static int nl80211_dump_station(struct sk_buff *skb,
+ 	}
+ 
+ 	while (1) {
+-		memset(&sinfo, 0, sizeof(sinfo));
++		memset(sinfo, 0, sizeof(*sinfo));
+ 		err = rdev_dump_station(rdev, wdev->netdev, sta_idx,
+-					mac_addr, &sinfo);
++					mac_addr, sinfo);
+ 		if (err == -ENOENT)
+ 			break;
+ 		if (err)
+@@ -6998,7 +7109,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
+ 				NETLINK_CB(cb->skb).portid,
+ 				cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ 				rdev, wdev->netdev, mac_addr,
+-				&sinfo) < 0)
++				sinfo) < 0)
+ 			goto out;
+ 
+ 		sta_idx++;
+@@ -7009,6 +7120,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
+ 	err = skb->len;
+  out_err:
+ 	wiphy_unlock(&rdev->wiphy);
++	kfree(sinfo);
+ 
+ 	return err;
+ }
+@@ -7017,13 +7129,11 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
+ {
+ 	struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ 	struct net_device *dev = info->user_ptr[1];
+-	struct station_info sinfo;
++	struct station_info *sinfo;
+ 	struct sk_buff *msg;
+ 	u8 *mac_addr = NULL;
+ 	int err;
+ 
+-	memset(&sinfo, 0, sizeof(sinfo));
+-
+ 	if (!info->attrs[NL80211_ATTR_MAC])
+ 		return -EINVAL;
+ 
+@@ -7032,24 +7142,35 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
+ 	if (!rdev->ops->get_station)
+ 		return -EOPNOTSUPP;
+ 
+-	err = rdev_get_station(rdev, dev, mac_addr, &sinfo);
++	sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL);
++	if (!sinfo)
++		return -ENOMEM;
++
++	err = rdev_get_station(rdev, dev, mac_addr, sinfo);
+ 	if (err)
+-		return err;
++		goto out;
+ 
+ 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ 	if (!msg) {
+-		cfg80211_sinfo_release_content(&sinfo);
+-		return -ENOMEM;
++		cfg80211_sinfo_release_content(sinfo);
++		err = -ENOMEM;
++		goto out;
+ 	}
+ 
+ 	if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION,
+ 				 info->snd_portid, info->snd_seq, 0,
+-				 rdev, dev, mac_addr, &sinfo) < 0) {
++				 rdev, dev, mac_addr, sinfo) < 0) {
+ 		nlmsg_free(msg);
+-		return -ENOBUFS;
++		err = -ENOBUFS;
++		goto out;
+ 	}
+ 
+-	return genlmsg_reply(msg, info);
++	err = genlmsg_reply(msg, info);
++
++out:
++	kfree(sinfo);
++
++	return err;
+ }
+ 
+ int cfg80211_check_station_change(struct wiphy *wiphy,
+@@ -13098,19 +13219,27 @@ static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev,
+ 	if (!cqm_config->last_rssi_event_value &&
+ 	    wdev->links[0].client.current_bss &&
+ 	    rdev->ops->get_station) {
+-		struct station_info sinfo = {};
++		struct station_info *sinfo;
+ 		u8 *mac_addr;
+ 
++		sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL);
++		if (!sinfo)
++			return -ENOMEM;
++
+ 		mac_addr = wdev->links[0].client.current_bss->pub.bssid;
+ 
+-		err = rdev_get_station(rdev, dev, mac_addr, &sinfo);
+-		if (err)
++		err = rdev_get_station(rdev, dev, mac_addr, sinfo);
++		if (err) {
++			kfree(sinfo);
+ 			return err;
++		}
+ 
+-		cfg80211_sinfo_release_content(&sinfo);
+-		if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_BEACON_SIGNAL_AVG))
++		cfg80211_sinfo_release_content(sinfo);
++		if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_BEACON_SIGNAL_AVG))
+ 			cqm_config->last_rssi_event_value =
+-				(s8) sinfo.rx_beacon_signal_avg;
++				(s8) sinfo->rx_beacon_signal_avg;
++
++		kfree(sinfo);
+ 	}
+ 
+ 	last = cqm_config->last_rssi_event_value;
+@@ -15997,7 +16126,7 @@ static int nl80211_probe_mesh_link(struct sk_buff *skb, struct genl_info *info)
+ 	struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ 	struct net_device *dev = info->user_ptr[1];
+ 	struct wireless_dev *wdev = dev->ieee80211_ptr;
+-	struct station_info sinfo = {};
++	struct station_info *sinfo;
+ 	const u8 *buf;
+ 	size_t len;
+ 	u8 *dest;
+@@ -16026,13 +16155,22 @@ static int nl80211_probe_mesh_link(struct sk_buff *skb, struct genl_info *info)
+ 	    !ether_addr_equal(buf + ETH_ALEN, dev->dev_addr))
+ 		return -EINVAL;
+ 
+-	err = rdev_get_station(rdev, dev, dest, &sinfo);
++	sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL);
++	if (!sinfo)
++		return -ENOMEM;
++
++	err = rdev_get_station(rdev, dev, dest, sinfo);
+ 	if (err)
+-		return err;
++		goto out;
+ 
+-	cfg80211_sinfo_release_content(&sinfo);
++	cfg80211_sinfo_release_content(sinfo);
+ 
+-	return rdev_probe_mesh_link(rdev, dev, dest, buf, len);
++	err = rdev_probe_mesh_link(rdev, dev, dest, buf, len);
++
++out:
++	kfree(sinfo);
++
++	return err;
+ }
+ 
+ static int parse_tid_conf(struct cfg80211_registered_device *rdev,
+@@ -18981,27 +19119,34 @@ void cfg80211_del_sta_sinfo(struct net_device *dev, const u8 *mac_addr,
+ 	struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ 	struct sk_buff *msg;
+-	struct station_info empty_sinfo = {};
++	bool new_allocate = false;
+ 
+-	if (!sinfo)
+-		sinfo = &empty_sinfo;
++	if (!sinfo) {
++		sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL);
++		if (!sinfo)
++			return;
++		new_allocate = true;
++	}
+ 
+ 	trace_cfg80211_del_sta(dev, mac_addr);
+ 
+ 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ 	if (!msg) {
+ 		cfg80211_sinfo_release_content(sinfo);
+-		return;
++		goto out;
+ 	}
+ 
+ 	if (nl80211_send_station(msg, NL80211_CMD_DEL_STATION, 0, 0, 0,
+ 				 rdev, dev, mac_addr, sinfo) < 0) {
+ 		nlmsg_free(msg);
+-		return;
++		goto out;
+ 	}
+ 
+ 	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ 				NL80211_MCGRP_MLME, gfp);
++out:
++	if (new_allocate)
++		kfree(sinfo);
+ }
+ EXPORT_SYMBOL(cfg80211_del_sta_sinfo);
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0077-mtk-mac80211-add-new-argument-link_id-in-set_bitrate.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0077-mtk-mac80211-add-new-argument-link_id-in-set_bitrate.patch
new file mode 100644
index 0000000..f8cc34b
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0077-mtk-mac80211-add-new-argument-link_id-in-set_bitrate.patch
@@ -0,0 +1,114 @@
+From 39cfb7b5c0e2b92676a351c4bc3949f79bd41a86 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 30 May 2024 16:08:54 +0800
+Subject: [PATCH 77/89] mtk: mac80211: add new argument link_id in
+ set_bitrate_mask
+
+mt76 driver needs link id to fix parital rate on the specific link.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+
+---
+ include/net/mac80211.h    |  3 ++-
+ net/mac80211/cfg.c        |  2 +-
+ net/mac80211/driver-ops.h |  7 ++++---
+ net/mac80211/trace.h      | 12 ++++++++----
+ 4 files changed, 15 insertions(+), 9 deletions(-)
+
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 916f37c..08c15a7 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -4665,7 +4665,8 @@ struct ieee80211_ops {
+ 			      u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max);
+ 	bool (*tx_frames_pending)(struct ieee80211_hw *hw);
+ 	int (*set_bitrate_mask)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+-				const struct cfg80211_bitrate_mask *mask);
++				const struct cfg80211_bitrate_mask *mask,
++				unsigned int link_id);
+ 	void (*event_callback)(struct ieee80211_hw *hw,
+ 			       struct ieee80211_vif *vif,
+ 			       const struct ieee80211_event *event);
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 19171b0..51a9b21 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -3448,7 +3448,7 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
+ 	}
+ 
+ 	if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)) {
+-		ret = drv_set_bitrate_mask(local, sdata, mask);
++		ret = drv_set_bitrate_mask(local, sdata, mask, link_id);
+ 		if (ret)
+ 			return ret;
+ 	}
+diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
+index b76fae2..4c0ac85 100644
+--- a/net/mac80211/driver-ops.h
++++ b/net/mac80211/driver-ops.h
+@@ -865,7 +865,8 @@ static inline bool drv_tx_frames_pending(struct ieee80211_local *local)
+ 
+ static inline int drv_set_bitrate_mask(struct ieee80211_local *local,
+ 				       struct ieee80211_sub_if_data *sdata,
+-				       const struct cfg80211_bitrate_mask *mask)
++				       const struct cfg80211_bitrate_mask *mask,
++				       unsigned int link_id)
+ {
+ 	int ret = -EOPNOTSUPP;
+ 
+@@ -875,10 +876,10 @@ static inline int drv_set_bitrate_mask(struct ieee80211_local *local,
+ 	if (!check_sdata_in_driver(sdata))
+ 		return -EIO;
+ 
+-	trace_drv_set_bitrate_mask(local, sdata, mask);
++	trace_drv_set_bitrate_mask(local, sdata, mask, link_id);
+ 	if (local->ops->set_bitrate_mask)
+ 		ret = local->ops->set_bitrate_mask(&local->hw,
+-						   &sdata->vif, mask);
++						   &sdata->vif, mask, link_id);
+ 	trace_drv_return_int(local, ret);
+ 
+ 	return ret;
+diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
+index 3e06017..48fcc8f 100644
+--- a/net/mac80211/trace.h
++++ b/net/mac80211/trace.h
+@@ -1418,15 +1418,17 @@ DEFINE_EVENT(local_only_evt, drv_offchannel_tx_cancel_wait,
+ TRACE_EVENT(drv_set_bitrate_mask,
+ 	TP_PROTO(struct ieee80211_local *local,
+ 		 struct ieee80211_sub_if_data *sdata,
+-		 const struct cfg80211_bitrate_mask *mask),
++		 const struct cfg80211_bitrate_mask *mask,
++		 unsigned int link_id),
+ 
+-	TP_ARGS(local, sdata, mask),
++	TP_ARGS(local, sdata, mask, link_id),
+ 
+ 	TP_STRUCT__entry(
+ 		LOCAL_ENTRY
+ 		VIF_ENTRY
+ 		__field(u32, legacy_2g)
+ 		__field(u32, legacy_5g)
++		__field(unsigned int, link_id)
+ 	),
+ 
+ 	TP_fast_assign(
+@@ -1434,11 +1436,13 @@ TRACE_EVENT(drv_set_bitrate_mask,
+ 		VIF_ASSIGN;
+ 		__entry->legacy_2g = mask->control[NL80211_BAND_2GHZ].legacy;
+ 		__entry->legacy_5g = mask->control[NL80211_BAND_5GHZ].legacy;
++		__entry->link_id = link_id;
+ 	),
+ 
+ 	TP_printk(
+-		LOCAL_PR_FMT  VIF_PR_FMT " 2G Mask:0x%x 5G Mask:0x%x",
+-		LOCAL_PR_ARG, VIF_PR_ARG, __entry->legacy_2g, __entry->legacy_5g
++		LOCAL_PR_FMT  VIF_PR_FMT " 2G Mask:0x%x 5G Mask:0x%x, link_id: %d",
++		LOCAL_PR_ARG, VIF_PR_ARG, __entry->legacy_2g, __entry->legacy_5g,
++		__entry->link_id
+ 	)
+ );
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0078-mtk-mac80211-add-per-radio-antenna-config.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0078-mtk-mac80211-add-per-radio-antenna-config.patch
new file mode 100644
index 0000000..44043ac
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0078-mtk-mac80211-add-per-radio-antenna-config.patch
@@ -0,0 +1,463 @@
+From a218fd8e2e8a2d390d995e62ba9e351c6cee30fb Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 11 Jun 2024 16:10:12 +0800
+Subject: [PATCH 78/89] mtk: mac80211: add per-radio antenna config
+
+Add per-radio antenna config
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ include/net/cfg80211.h    |  9 ++--
+ include/net/mac80211.h    |  4 +-
+ net/mac80211/cfg.c        | 19 +++++---
+ net/mac80211/driver-ops.h | 12 ++---
+ net/mac80211/trace.h      | 22 +++++----
+ net/wireless/nl80211.c    | 97 +++++++++++++++++++++++++--------------
+ net/wireless/rdev-ops.h   | 12 ++---
+ net/wireless/trace.h      | 24 ++++++----
+ 8 files changed, 122 insertions(+), 77 deletions(-)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index 110e555..835735f 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -4881,8 +4881,8 @@ struct cfg80211_ops {
+ 						   struct wireless_dev *wdev,
+ 						   struct mgmt_frame_regs *upd);
+ 
+-	int	(*set_antenna)(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant);
+-	int	(*get_antenna)(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant);
++	int	(*set_antenna)(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant, int band);
++	int	(*get_antenna)(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant, int band);
+ 
+ 	int	(*sched_scan_start)(struct wiphy *wiphy,
+ 				struct net_device *dev,
+@@ -5831,8 +5831,9 @@ struct wiphy {
+ 
+ 	u8 max_num_pmkids;
+ 
+-	u32 available_antennas_tx;
+-	u32 available_antennas_rx;
++	/* FIXME: This should move to per-radio data struct */
++	u32 available_antennas_tx[NUM_NL80211_BANDS];
++	u32 available_antennas_rx[NUM_NL80211_BANDS];
+ 
+ 	u32 probe_resp_offload;
+ 
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 08c15a7..9471892 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -4650,8 +4650,8 @@ struct ieee80211_ops {
+ 	void (*channel_switch)(struct ieee80211_hw *hw,
+ 			       struct ieee80211_vif *vif,
+ 			       struct ieee80211_channel_switch *ch_switch);
+-	int (*set_antenna)(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant);
+-	int (*get_antenna)(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant);
++	int (*set_antenna)(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant, int band);
++	int (*get_antenna)(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant, int band);
+ 
+ 	int (*remain_on_channel)(struct ieee80211_hw *hw,
+ 				 struct ieee80211_vif *vif,
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 51a9b21..69c3e81 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -4255,15 +4255,22 @@ ieee80211_update_mgmt_frame_registrations(struct wiphy *wiphy,
+ 		ieee80211_configure_filter(local);
+ }
+ 
+-static int ieee80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
++static int ieee80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant, int band)
+ {
+ 	struct ieee80211_local *local = wiphy_priv(wiphy);
+ 	int ret;
+ 
+-	if (local->started)
+-		return -EOPNOTSUPP;
++	/* FIXME:
++	 * This flag should be moved to per-radio data struct; otherwise,
++	 * radio 1 or 2 will be blocked when radio 0 is started.
++	 * Temporarily disable this check until we have a better solution.
++	 * if (local->started)
++	 *	return -EOPNOTSUPP;
++	 */
++	wiphy_info(wiphy,
++		   "Temporarily disable local->started check during setting antenna\n");
+ 
+-	ret = drv_set_antenna(local, tx_ant, rx_ant);
++	ret = drv_set_antenna(local, tx_ant, rx_ant, band);
+ 	if (ret)
+ 		return ret;
+ 
+@@ -4271,11 +4278,11 @@ static int ieee80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
+ 	return 0;
+ }
+ 
+-static int ieee80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant)
++static int ieee80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant, int band)
+ {
+ 	struct ieee80211_local *local = wiphy_priv(wiphy);
+ 
+-	return drv_get_antenna(local, tx_ant, rx_ant);
++	return drv_get_antenna(local, tx_ant, rx_ant, band);
+ }
+ 
+ static int ieee80211_set_rekey_data(struct wiphy *wiphy,
+diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
+index 4c0ac85..3bf8768 100644
+--- a/net/mac80211/driver-ops.h
++++ b/net/mac80211/driver-ops.h
+@@ -762,26 +762,26 @@ static inline void drv_channel_switch(struct ieee80211_local *local,
+ 
+ 
+ static inline int drv_set_antenna(struct ieee80211_local *local,
+-				  u32 tx_ant, u32 rx_ant)
++				  u32 tx_ant, u32 rx_ant, int band)
+ {
+ 	int ret = -EOPNOTSUPP;
+ 	might_sleep();
+ 	lockdep_assert_wiphy(local->hw.wiphy);
+ 	if (local->ops->set_antenna)
+-		ret = local->ops->set_antenna(&local->hw, tx_ant, rx_ant);
+-	trace_drv_set_antenna(local, tx_ant, rx_ant, ret);
++		ret = local->ops->set_antenna(&local->hw, tx_ant, rx_ant, band);
++	trace_drv_set_antenna(local, tx_ant, rx_ant, band, ret);
+ 	return ret;
+ }
+ 
+ static inline int drv_get_antenna(struct ieee80211_local *local,
+-				  u32 *tx_ant, u32 *rx_ant)
++				  u32 *tx_ant, u32 *rx_ant, int band)
+ {
+ 	int ret = -EOPNOTSUPP;
+ 	might_sleep();
+ 	lockdep_assert_wiphy(local->hw.wiphy);
+ 	if (local->ops->get_antenna)
+-		ret = local->ops->get_antenna(&local->hw, tx_ant, rx_ant);
+-	trace_drv_get_antenna(local, *tx_ant, *rx_ant, ret);
++		ret = local->ops->get_antenna(&local->hw, tx_ant, rx_ant, band);
++	trace_drv_get_antenna(local, *tx_ant, *rx_ant, band, ret);
+ 	return ret;
+ }
+ 
+diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
+index 48fcc8f..84b5c35 100644
+--- a/net/mac80211/trace.h
++++ b/net/mac80211/trace.h
+@@ -1263,14 +1263,16 @@ DEFINE_EVENT(chanswitch_evt, drv_channel_switch,
+ );
+ 
+ TRACE_EVENT(drv_set_antenna,
+-	TP_PROTO(struct ieee80211_local *local, u32 tx_ant, u32 rx_ant, int ret),
++	TP_PROTO(struct ieee80211_local *local, u32 tx_ant, u32 rx_ant,
++		 int band, int ret),
+ 
+-	TP_ARGS(local, tx_ant, rx_ant, ret),
++	TP_ARGS(local, tx_ant, rx_ant, band, ret),
+ 
+ 	TP_STRUCT__entry(
+ 		LOCAL_ENTRY
+ 		__field(u32, tx_ant)
+ 		__field(u32, rx_ant)
++		__field(int, band)
+ 		__field(int, ret)
+ 	),
+ 
+@@ -1278,24 +1280,27 @@ TRACE_EVENT(drv_set_antenna,
+ 		LOCAL_ASSIGN;
+ 		__entry->tx_ant = tx_ant;
+ 		__entry->rx_ant = rx_ant;
++		__entry->band = band;
+ 		__entry->ret = ret;
+ 	),
+ 
+ 	TP_printk(
+-		LOCAL_PR_FMT " tx_ant:%d rx_ant:%d ret:%d",
+-		LOCAL_PR_ARG, __entry->tx_ant, __entry->rx_ant, __entry->ret
++		LOCAL_PR_FMT " tx_ant:%d rx_ant:%d band:%d ret:%d",
++		LOCAL_PR_ARG, __entry->tx_ant, __entry->rx_ant,
++		__entry->band, __entry->ret
+ 	)
+ );
+ 
+ TRACE_EVENT(drv_get_antenna,
+-	TP_PROTO(struct ieee80211_local *local, u32 tx_ant, u32 rx_ant, int ret),
++	TP_PROTO(struct ieee80211_local *local, u32 tx_ant, u32 rx_ant, int band, int ret),
+ 
+-	TP_ARGS(local, tx_ant, rx_ant, ret),
++	TP_ARGS(local, tx_ant, rx_ant, band, ret),
+ 
+ 	TP_STRUCT__entry(
+ 		LOCAL_ENTRY
+ 		__field(u32, tx_ant)
+ 		__field(u32, rx_ant)
++		__field(int, band)
+ 		__field(int, ret)
+ 	),
+ 
+@@ -1303,12 +1308,13 @@ TRACE_EVENT(drv_get_antenna,
+ 		LOCAL_ASSIGN;
+ 		__entry->tx_ant = tx_ant;
+ 		__entry->rx_ant = rx_ant;
++		__entry->rx_ant = band;
+ 		__entry->ret = ret;
+ 	),
+ 
+ 	TP_printk(
+-		LOCAL_PR_FMT " tx_ant:%d rx_ant:%d ret:%d",
+-		LOCAL_PR_ARG, __entry->tx_ant, __entry->rx_ant, __entry->ret
++		LOCAL_PR_FMT " tx_ant:%d rx_ant:%d band:%d ret:%d",
++		LOCAL_PR_ARG, __entry->tx_ant, __entry->rx_ant, __entry->band, __entry->ret
+ 	)
+ );
+ 
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index e62116e..c38a5d0 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -618,8 +618,6 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
+ 	[NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 },
+ 	[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 },
+ 	[NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 },
+-	[NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 },
+-	[NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 },
+ 	[NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 },
+ 	[NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG },
+ 	[NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
+@@ -2626,10 +2624,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
+ 		    nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))
+ 			goto nla_put_failure;
+ 
+-		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
+-				rdev->wiphy.available_antennas_tx) ||
+-		    nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
+-				rdev->wiphy.available_antennas_rx))
++		if (nla_put(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
++			    sizeof(rdev->wiphy.available_antennas_tx),
++			    rdev->wiphy.available_antennas_tx) ||
++		    nla_put(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
++			    sizeof(rdev->wiphy.available_antennas_rx),
++			    rdev->wiphy.available_antennas_rx))
+ 			goto nla_put_failure;
+ 
+ 		if ((rdev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
+@@ -2637,22 +2637,29 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
+ 				rdev->wiphy.probe_resp_offload))
+ 			goto nla_put_failure;
+ 
+-		if ((rdev->wiphy.available_antennas_tx ||
+-		     rdev->wiphy.available_antennas_rx) &&
+-		    rdev->ops->get_antenna) {
+-			u32 tx_ant = 0, rx_ant = 0;
++		if (rdev->ops->get_antenna) {
++			u32 tx_ants[NUM_NL80211_BANDS], rx_ants[NUM_NL80211_BANDS];
++			u32 tx_ant, rx_ant;
+ 			int res;
+ 
+-			res = rdev_get_antenna(rdev, &tx_ant, &rx_ant);
+-			if (!res) {
+-				if (nla_put_u32(msg,
+-						NL80211_ATTR_WIPHY_ANTENNA_TX,
+-						tx_ant) ||
+-				    nla_put_u32(msg,
+-						NL80211_ATTR_WIPHY_ANTENNA_RX,
+-						rx_ant))
+-					goto nla_put_failure;
++			memset(tx_ants, 0, sizeof(tx_ants));
++			memset(rx_ants, 0, sizeof(rx_ants));
++			for (i = 0; i < NUM_NL80211_BANDS; i++) {
++				if (!rdev->wiphy.available_antennas_tx[i] ||
++				    !rdev->wiphy.available_antennas_rx[i])
++					continue;
++
++				res = rdev_get_antenna(rdev, &tx_ant, &rx_ant, i);
++				if (!res) {
++					tx_ants[i] = tx_ant;
++					rx_ants[i] = rx_ant;
++				}
+ 			}
++			if (nla_put(msg, NL80211_ATTR_WIPHY_ANTENNA_TX,
++				    sizeof(tx_ants), tx_ants) ||
++			    nla_put(msg, NL80211_ATTR_WIPHY_ANTENNA_RX,
++				    sizeof(rx_ants), rx_ants))
++				goto nla_put_failure;
+ 		}
+ 
+ 		state->split_start++;
+@@ -3784,32 +3791,52 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
+ 
+ 	if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
+ 	    info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) {
+-		u32 tx_ant, rx_ant;
++		u32 *tx_ants, *rx_ants;
++		int bandid, tx_num, rx_num;
+ 
+-		if ((!rdev->wiphy.available_antennas_tx &&
+-		     !rdev->wiphy.available_antennas_rx) ||
+-		    !rdev->ops->set_antenna) {
++		if (!rdev->ops->set_antenna) {
+ 			result = -EOPNOTSUPP;
+ 			goto out;
+ 		}
+ 
+-		tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]);
+-		rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]);
+-
+-		/* reject antenna configurations which don't match the
+-		 * available antenna masks, except for the "all" mask */
+-		if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) ||
+-		    (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) {
++		tx_num = nla_len(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]) / sizeof(u32);
++		rx_num = nla_len(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) / sizeof(u32);
++		if (tx_num != rx_num || tx_num != NUM_NL80211_BANDS) {
+ 			result = -EINVAL;
+ 			goto out;
+ 		}
++		tx_ants = nla_data(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]);
++		rx_ants = nla_data(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]);
+ 
+-		tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;
+-		rx_ant = rx_ant & rdev->wiphy.available_antennas_rx;
++		/* reject antenna configurations which don't match the
++		 * available antenna masks in wiphy, except for the "all" mask
++		 */
++		for (bandid = 0; bandid < NUM_NL80211_BANDS; bandid++) {
++			struct ieee80211_supported_band *sband = rdev->wiphy.bands[bandid];
++			u32 tx_ant = tx_ants[bandid], rx_ant = rx_ants[bandid];
++			u32 avail_ants_tx = rdev->wiphy.available_antennas_tx[bandid];
++			u32 avail_ants_rx = rdev->wiphy.available_antennas_rx[bandid];
+ 
+-		result = rdev_set_antenna(rdev, tx_ant, rx_ant);
+-		if (result)
+-			goto out;
++			if (!sband || !tx_ant || !rx_ant)
++				continue;
++
++			if (!avail_ants_tx && !avail_ants_rx) {
++				result = -EOPNOTSUPP;
++				goto out;
++			}
++
++			if ((~tx_ant && (tx_ant & ~avail_ants_tx)) ||
++			    (~rx_ant && (rx_ant & ~avail_ants_rx))) {
++				result = -EINVAL;
++				goto out;
++			}
++
++			tx_ant = tx_ant & avail_ants_tx;
++			rx_ant = rx_ant & avail_ants_rx;
++			result = rdev_set_antenna(rdev, tx_ant, rx_ant, bandid);
++			if (result)
++				goto out;
++		}
+ 	}
+ 
+ 	changed = 0;
+diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
+index 9b411db..bcd51fc 100644
+--- a/net/wireless/rdev-ops.h
++++ b/net/wireless/rdev-ops.h
+@@ -857,26 +857,26 @@ rdev_update_mgmt_frame_registrations(struct cfg80211_registered_device *rdev,
+ }
+ 
+ static inline int rdev_set_antenna(struct cfg80211_registered_device *rdev,
+-				   u32 tx_ant, u32 rx_ant)
++				   u32 tx_ant, u32 rx_ant, int band)
+ {
+ 	int ret;
+-	trace_rdev_set_antenna(&rdev->wiphy, tx_ant, rx_ant);
+-	ret = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant);
++	trace_rdev_set_antenna(&rdev->wiphy, tx_ant, rx_ant, band);
++	ret = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant, band);
+ 	trace_rdev_return_int(&rdev->wiphy, ret);
+ 	return ret;
+ }
+ 
+ static inline int rdev_get_antenna(struct cfg80211_registered_device *rdev,
+-				   u32 *tx_ant, u32 *rx_ant)
++				   u32 *tx_ant, u32 *rx_ant, int band)
+ {
+ 	int ret;
+ 	trace_rdev_get_antenna(&rdev->wiphy);
+-	ret = rdev->ops->get_antenna(&rdev->wiphy, tx_ant, rx_ant);
++	ret = rdev->ops->get_antenna(&rdev->wiphy, tx_ant, rx_ant, band);
+ 	if (ret)
+ 		trace_rdev_return_int(&rdev->wiphy, ret);
+ 	else
+ 		trace_rdev_return_int_tx_rx(&rdev->wiphy, ret, *tx_ant,
+-					    *rx_ant);
++					    *rx_ant, band);
+ 	return ret;
+ }
+ 
+diff --git a/net/wireless/trace.h b/net/wireless/trace.h
+index 6af432e..90ba38a 100644
+--- a/net/wireless/trace.h
++++ b/net/wireless/trace.h
+@@ -1818,22 +1818,24 @@ TRACE_EVENT(rdev_update_mgmt_frame_registrations,
+ );
+ 
+ TRACE_EVENT(rdev_return_int_tx_rx,
+-	TP_PROTO(struct wiphy *wiphy, int ret, u32 tx, u32 rx),
+-	TP_ARGS(wiphy, ret, tx, rx),
++	TP_PROTO(struct wiphy *wiphy, int ret, u32 tx, u32 rx, int band),
++	TP_ARGS(wiphy, ret, tx, rx, band),
+ 	TP_STRUCT__entry(
+ 		WIPHY_ENTRY
+ 		__field(int, ret)
+ 		__field(u32, tx)
+ 		__field(u32, rx)
++		__field(int, band)
+ 	),
+ 	TP_fast_assign(
+ 		WIPHY_ASSIGN;
+ 		__entry->ret = ret;
+ 		__entry->tx = tx;
+ 		__entry->rx = rx;
++		__entry->band = band;
+ 	),
+-	TP_printk(WIPHY_PR_FMT ", returned %d, tx: %u, rx: %u",
+-		  WIPHY_PR_ARG, __entry->ret, __entry->tx, __entry->rx)
++	TP_printk(WIPHY_PR_FMT ", returned %d, tx: %u, rx: %u band %d",
++		  WIPHY_PR_ARG, __entry->ret, __entry->tx, __entry->rx, __entry->band)
+ );
+ 
+ TRACE_EVENT(rdev_return_void_tx_rx,
+@@ -1860,25 +1862,27 @@ TRACE_EVENT(rdev_return_void_tx_rx,
+ );
+ 
+ DECLARE_EVENT_CLASS(tx_rx_evt,
+-	TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx),
+-	TP_ARGS(wiphy, tx, rx),
++	TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx, int band),
++	TP_ARGS(wiphy, rx, tx, band),
+ 	TP_STRUCT__entry(
+ 		WIPHY_ENTRY
+ 		__field(u32, tx)
+ 		__field(u32, rx)
++		__field(int, band)
+ 	),
+ 	TP_fast_assign(
+ 		WIPHY_ASSIGN;
+ 		__entry->tx = tx;
+ 		__entry->rx = rx;
++		__entry->band = band;
+ 	),
+-	TP_printk(WIPHY_PR_FMT ", tx: %u, rx: %u ",
+-		  WIPHY_PR_ARG, __entry->tx, __entry->rx)
++	TP_printk(WIPHY_PR_FMT ", tx: %u, rx: %u band: %d ",
++		  WIPHY_PR_ARG, __entry->tx, __entry->rx, __entry->band)
+ );
+ 
+ DEFINE_EVENT(tx_rx_evt, rdev_set_antenna,
+-	TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx),
+-	TP_ARGS(wiphy, tx, rx)
++	TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx, int band),
++	TP_ARGS(wiphy, rx, tx, band)
+ );
+ 
+ DECLARE_EVENT_CLASS(wiphy_netdev_id_evt,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0079-mtk-mac80211-add-new-rc-changed-enum-for-wifi7-cert.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0079-mtk-mac80211-add-new-rc-changed-enum-for-wifi7-cert.patch
new file mode 100644
index 0000000..e37fd17
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0079-mtk-mac80211-add-new-rc-changed-enum-for-wifi7-cert.patch
@@ -0,0 +1,31 @@
+From 56333cc7dbfb80bf94c86b614f330a981be21258 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 20 Jun 2024 09:01:35 +0800
+Subject: [PATCH 79/89] mtk: mac80211: add new rc changed enum for wifi7 cert
+
+Add a new rc enum "IEEE80211_RC_CODING_TYPE_CHANGED" for wifi7 r1. This
+enum is used to mark that the coding type has changed by user space
+commands.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+
+---
+ include/net/mac80211.h | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 9471892..f9afddd 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -3757,6 +3757,8 @@ enum ieee80211_rate_control_changed {
+ 	IEEE80211_RC_SMPS_CHANGED	= BIT(1),
+ 	IEEE80211_RC_SUPP_RATES_CHANGED	= BIT(2),
+ 	IEEE80211_RC_NSS_CHANGED	= BIT(3),
++	/* Defined for mtk vendor command */
++	IEEE80211_RC_CODING_TYPE_CHANGED= BIT(7),
+ };
+ 
+ /**
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0080-mtk-mac80211-do-not-check-radar-required-for-remain-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0080-mtk-mac80211-do-not-check-radar-required-for-remain-.patch
new file mode 100644
index 0000000..b5f7195
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0080-mtk-mac80211-do-not-check-radar-required-for-remain-.patch
@@ -0,0 +1,28 @@
+From 49baf7ad24eed63700f62fa631b32014c686b6e3 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 14 Jun 2024 11:16:07 +0800
+Subject: [PATCH 80/89] mtk: mac80211: do not check radar required for
+ remain-on-channel operation
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ net/mac80211/offchannel.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
+index 9e4f26a..b73cf54 100644
+--- a/net/mac80211/offchannel.c
++++ b/net/mac80211/offchannel.c
+@@ -613,8 +613,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
+ 	}
+ 
+ 	/* if there's no need to queue, handle it immediately */
+-	if (list_empty(&local->roc_list) &&
+-	    !local->scanning && !ieee80211_is_radar_required(local)) {
++	if (list_empty(&local->roc_list) && !local->scanning) {
+ 		/* if not HW assist, just queue & schedule work */
+ 		if (!local->ops->remain_on_channel) {
+ 			list_add_tail(&roc->list, &local->roc_list);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0081-mtk-mac80211-Add-ba_disable-debugfs-to-disable-ba.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0081-mtk-mac80211-Add-ba_disable-debugfs-to-disable-ba.patch
new file mode 100644
index 0000000..3356048
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0081-mtk-mac80211-Add-ba_disable-debugfs-to-disable-ba.patch
@@ -0,0 +1,125 @@
+From d9f41790746aa35852953ea5f1944abb58ad48ec Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+Date: Thu, 20 Jun 2024 14:56:42 +0800
+Subject: [PATCH 81/89] mtk: mac80211: Add ba_disable debugfs to disable ba
+
+Add ba_disable debugfs to diable ba for Wi-Fi 7 cert
+
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ include/net/mac80211.h |  6 ++++++
+ net/mac80211/agg-tx.c  |  6 ++++++
+ net/mac80211/debugfs.c | 49 ++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 61 insertions(+)
+
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index f9afddd..5ff8ced 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -3061,6 +3061,7 @@ struct ieee80211_hw {
+ 	const s8 *tx_power_levels;
+ 	u8 max_txpwr_levels_idx;
+ 	bool cert_mode;
++	bool ba_disable;
+ };
+ 
+ static inline bool ieee80211_is_cert_mode(struct ieee80211_hw *hw)
+@@ -3068,6 +3069,11 @@ static inline bool ieee80211_is_cert_mode(struct ieee80211_hw *hw)
+ 	return hw->cert_mode;
+ }
+ 
++static inline bool ieee80211_is_ba_disable(struct ieee80211_hw *hw)
++{
++	return hw->ba_disable;
++}
++
+ static inline bool _ieee80211_hw_check(struct ieee80211_hw *hw,
+ 				       enum ieee80211_hw_flags flg)
+ {
+diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
+index 2f351a7..e398540 100644
+--- a/net/mac80211/agg-tx.c
++++ b/net/mac80211/agg-tx.c
+@@ -638,6 +638,12 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
+ 
+ 	trace_api_start_tx_ba_session(pubsta, tid);
+ 
++	if (ieee80211_is_ba_disable(&local->hw)) {
++		ht_dbg(sdata,
++		       "BA session is forced to be shut down due to debugfs config\n");
++		return -EINVAL;
++	}
++
+ 	if (WARN(sta->reserved_tid == tid,
+ 		 "Requested to start BA session on reserved tid=%d", tid))
+ 		return -EINVAL;
+diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
+index 376abfc..fc7888a 100644
+--- a/net/mac80211/debugfs.c
++++ b/net/mac80211/debugfs.c
+@@ -498,6 +498,54 @@ static const struct file_operations cert_mode_ops = {
+ 	.llseek = noop_llseek,
+ };
+ 
++static ssize_t ba_disable_read(struct file *file,
++			       char __user *user_buf,
++			       size_t count,
++			       loff_t *ppos)
++{
++	struct ieee80211_local *local = file->private_data;
++	char buf[32];
++	int len = 0;
++
++	len = scnprintf(buf, sizeof(buf), "ba_disable: %d\n",
++			local->hw.ba_disable);
++
++	return simple_read_from_buffer(user_buf, count, ppos,
++				       buf, len);
++}
++
++static ssize_t ba_disable_write(struct file *file,
++				const char __user *user_buf,
++				size_t count,
++				loff_t *ppos)
++{
++	struct ieee80211_local *local = file->private_data;
++	char buf[16];
++
++	if (count >= sizeof(buf))
++		return -EINVAL;
++
++	if (copy_from_user(buf, user_buf, count))
++		return -EFAULT;
++
++	if (count && buf[count - 1] == '\n')
++		buf[count - 1] = '\0';
++	else
++		buf[count] = '\0';
++
++	if (kstrtobool(buf, &local->hw.ba_disable))
++		return -EINVAL;
++
++	return count;
++}
++
++static const struct file_operations ba_disable_ops = {
++	.write = ba_disable_write,
++	.read = ba_disable_read,
++	.open = simple_open,
++	.llseek = noop_llseek,
++};
++
+ static const char *hw_flag_names[] = {
+ #define FLAG(F)	[IEEE80211_HW_##F] = #F
+ 	FLAG(HAS_RATE_CONTROL),
+@@ -732,6 +780,7 @@ void debugfs_hw_add(struct ieee80211_local *local)
+ 			   phyd, &local->aql_threshold);
+ 
+ 	DEBUGFS_ADD_MODE(cert_mode, 0644);
++	DEBUGFS_ADD_MODE(ba_disable, 0644);
+ 	statsd = debugfs_create_dir("statistics", phyd);
+ 
+ #ifdef CPTCFG_MAC80211_DEBUG_COUNTERS
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0082-mtk-mac80211-fix-incorrect-VIF-assignment-for-IEEE-8.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0082-mtk-mac80211-fix-incorrect-VIF-assignment-for-IEEE-8.patch
new file mode 100644
index 0000000..8e1fffe
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0082-mtk-mac80211-fix-incorrect-VIF-assignment-for-IEEE-8.patch
@@ -0,0 +1,36 @@
+From 326501581d550d0397a1405425d1304ecec87e32 Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Fri, 12 Jul 2024 15:44:03 +0800
+Subject: [PATCH 82/89] mtk: mac80211: fix incorrect VIF assignment for IEEE
+ 802.11 fragments
+
+In WDS mode, first fragment is obtained from ieee80211_local->fq, so its VIF is later changed from AP_VLAN to AP VIF.
+On the other hand, subsequent fragments are obtained from txq_info->frags, so its VIF remains AP_VLAN.
+Inconsistency in VIFs of fragments results in transmission failure.
+Therefore, VIF assignment for non-first fragments is added.
+
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ net/mac80211/tx.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
+index ebe3ae2..c6d3f2f 100644
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -3850,8 +3850,11 @@ begin:
+ 	skb = __skb_dequeue(&txqi->frags);
+ 	if (unlikely(skb)) {
+ 		if (!(IEEE80211_SKB_CB(skb)->control.flags &
+-				IEEE80211_TX_INTCFL_NEED_TXPROCESSING))
++				IEEE80211_TX_INTCFL_NEED_TXPROCESSING)) {
++			// TODO: report airtime of non-first fragments.
++			IEEE80211_SKB_CB(skb)->control.vif = vif;
+ 			goto out;
++		}
+ 		IEEE80211_SKB_CB(skb)->control.flags &=
+ 			~IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
+ 	} else {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0083-mtk-mac80211-Add-STA-site-link-add-support.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0083-mtk-mac80211-Add-STA-site-link-add-support.patch
new file mode 100644
index 0000000..2c2a8c3
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0083-mtk-mac80211-Add-STA-site-link-add-support.patch
@@ -0,0 +1,124 @@
+From b02a25d547c4959e81078bb12bdf1576182fef20 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 2 Jul 2024 14:28:26 +0800
+Subject: [PATCH 83/89] mtk: mac80211: Add STA site link add support
+
+Trigger deauth & reconnect when all the valid links of the STA receive
+the information of link addition.
+(max simultaneous link num in ML IE > the simultaneous link num stored
+after association success)
+For example, when the MLD AP completes the CAC or adds an additional link
+via the link reconfiguration command, SQC/autotest might assume that the
+MLD STA to react to it rather than ignoring it.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+
+Do not check ifmgd->reporting_add_links when the connection is not MLO.
+
+This fixes the problem that an MLD STA cannot associate with an MLD AP.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ net/mac80211/ieee80211_i.h |  1 +
+ net/mac80211/mlme.c        | 42 ++++++++++++++++++++++++++++++++++++++
+ net/wireless/scan.c        |  5 ++---
+ 3 files changed, 45 insertions(+), 3 deletions(-)
+
+diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
+index 6f9a9a6..fa8d751 100644
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -602,6 +602,7 @@ struct ieee80211_if_managed {
+ 
+ 	struct wiphy_delayed_work ml_reconf_work;
+ 	u16 removed_links;
++	u16 reporting_add_links;
+ 
+ 	/* TID-to-link mapping support */
+ 	struct wiphy_delayed_work ttlm_work;
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index a3873d2..4964d08 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -3716,6 +3716,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
+ 	sdata->u.mgd.removed_links = 0;
+ 	wiphy_delayed_work_cancel(sdata->local->hw.wiphy,
+ 				  &sdata->u.mgd.ml_reconf_work);
++	sdata->u.mgd.reporting_add_links = 0;
+ 
+ 	wiphy_work_cancel(sdata->local->hw.wiphy,
+ 			  &ifmgd->teardown_ttlm_work);
+@@ -6182,6 +6183,34 @@ static bool ieee80211_rx_our_beacon(const u8 *tx_bssid,
+ 	return ether_addr_equal(tx_bssid, bss->transmitted_bss->bssid);
+ }
+ 
++static void ieee80211_ml_link_add(struct ieee80211_link_data *link,
++				  struct ieee802_11_elems *elems)
++{
++	struct ieee80211_sub_if_data *sdata = link->sdata;
++	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
++	int max_simul_links, cur_simul_links;
++	u16 mld_capa_op;
++
++	if (!ieee80211_vif_is_mld(&sdata->vif) || !elems->ml_basic ||
++	    !ifmgd->associated)
++		return;
++
++	mld_capa_op = ieee80211_mle_get_mld_capa_op((const void *)elems->ml_basic);
++	if (!mld_capa_op)
++		return;
++
++	/* TODO: parse RNR to check the capability of STA before reconnecting */
++	max_simul_links = mld_capa_op & IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS;
++	cur_simul_links = sdata->vif.cfg.mld_capa_op &
++			  IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS;
++	if (max_simul_links > cur_simul_links) {
++		ifmgd->reporting_add_links |= BIT(link->link_id);
++		sdata_info(sdata,
++			   "MLO Reconfig: link %d: cur_simul_links=%d, max_simul_links=%d\n",
++			   link->link_id, cur_simul_links, max_simul_links);
++	}
++}
++
+ static void ieee80211_ml_reconf_work(struct wiphy *wiphy,
+ 				     struct wiphy_work *work)
+ {
+@@ -6950,6 +6979,19 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
+ 
+ 	ieee80211_rx_bss_info(link, mgmt, len, rx_status);
+ 
++	ieee80211_ml_link_add(link, elems);
++	if (ieee80211_vif_is_mld(&sdata->vif) &&
++	    ifmgd->reporting_add_links == sdata->vif.valid_links) {
++		ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
++				       WLAN_REASON_DEAUTH_LEAVING,
++				       true, deauth_buf);
++		ieee80211_report_disconnect(sdata, deauth_buf,
++					    sizeof(deauth_buf), true,
++					    WLAN_REASON_DEAUTH_LEAVING,
++					    true);
++		goto free;
++	}
++
+ 	ieee80211_sta_process_chanswitch(link, rx_status->mactime,
+ 					 rx_status->device_timestamp,
+ 					 elems, elems,
+diff --git a/net/wireless/scan.c b/net/wireless/scan.c
+index 1138c0b..35d2f00 100644
+--- a/net/wireless/scan.c
++++ b/net/wireless/scan.c
+@@ -1879,9 +1879,8 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev,
+ 
+ 		rcu_assign_pointer(known->pub.beacon_ies, new->pub.beacon_ies);
+ 
+-		/* Override IEs if they were from a beacon before */
+-		if (old == rcu_access_pointer(known->pub.ies))
+-			rcu_assign_pointer(known->pub.ies, new->pub.beacon_ies);
++		/* No points to keep pointing to the old data derived from probe resp */
++		rcu_assign_pointer(known->pub.ies, new->pub.beacon_ies);
+ 
+ 		cfg80211_update_hidden_bsses(known,
+ 					     rcu_access_pointer(new->pub.beacon_ies),
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0084-mtk-mac80211-Fix-channel-switch-punct-bitmap-would-b.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0084-mtk-mac80211-Fix-channel-switch-punct-bitmap-would-b.patch
new file mode 100644
index 0000000..b56b257
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0084-mtk-mac80211-Fix-channel-switch-punct-bitmap-would-b.patch
@@ -0,0 +1,46 @@
+From 6043522521b98b4107c339d399fd64b52b60f192 Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Wed, 10 Jul 2024 17:45:05 +0800
+Subject: [PATCH 84/89] mtk: mac80211: Fix channel switch punct bitmap would be
+ clean bug in STA mode
+
+The patch add back the puncture bitmap when mac80211 create a new channel
+definition in ieee80211_chandef_ht_oper for legacy cap check.
+
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ net/mac80211/spectmgmt.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
+index 073ff9e..e8ac8ec 100644
+--- a/net/mac80211/spectmgmt.c
++++ b/net/mac80211/spectmgmt.c
+@@ -76,6 +76,7 @@ validate_chandef_by_ht_vht_oper(struct ieee80211_sub_if_data *sdata,
+ 				struct cfg80211_chan_def *chandef)
+ {
+ 	u32 control_freq, center_freq1, center_freq2;
++	u16 punct_bitmap;
+ 	enum nl80211_chan_width chan_width;
+ 	struct ieee80211_ht_operation ht_oper;
+ 	struct ieee80211_vht_operation vht_oper;
+@@ -90,6 +91,7 @@ validate_chandef_by_ht_vht_oper(struct ieee80211_sub_if_data *sdata,
+ 	center_freq1 = chandef->center_freq1;
+ 	center_freq2 = chandef->center_freq2;
+ 	chan_width = chandef->width;
++	punct_bitmap = chandef->punctured;
+ 
+ 	ht_oper.primary_chan = ieee80211_frequency_to_channel(control_freq);
+ 	if (control_freq != center_freq1)
+@@ -101,6 +103,8 @@ validate_chandef_by_ht_vht_oper(struct ieee80211_sub_if_data *sdata,
+ 
+ 	ieee80211_chandef_ht_oper(&ht_oper, chandef);
+ 
++	chandef->punctured = punct_bitmap;
++
+ 	if (conn->mode < IEEE80211_CONN_MODE_VHT)
+ 		return;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0085-mtk-mac80211-add-ieee80211_links_removed-to-support-.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0085-mtk-mac80211-add-ieee80211_links_removed-to-support-.patch
new file mode 100644
index 0000000..5124e93
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0085-mtk-mac80211-add-ieee80211_links_removed-to-support-.patch
@@ -0,0 +1,242 @@
+From e9974899e3d65fdd626f00aafd7bc5f1ed46b6a0 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 3 May 2024 17:51:37 +0800
+Subject: [PATCH 85/89] mtk: mac80211: add ieee80211_links_removed() to support
+ MLD AP reconf
+
+Add ieee80211_links_removed() to let driver notify upper layers for MLD AP
+reconfiguration. Also modify some parts to prevent from removing whole
+STA while removing a link.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ include/net/mac80211.h     |  8 ++++++++
+ net/mac80211/cfg.c         | 23 ++++++++++++++++++-----
+ net/mac80211/ieee80211_i.h |  4 ++++
+ net/mac80211/iface.c       | 19 +++++++++++++++++++
+ net/mac80211/sta_info.c    |  6 ++++++
+ net/wireless/ap.c          | 18 +++++++++++++-----
+ net/wireless/nl80211.c     |  9 ++++++---
+ 7 files changed, 74 insertions(+), 13 deletions(-)
+
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 5ff8ced..be6631e 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -7730,6 +7730,14 @@ int ieee80211_emulate_switch_vif_chanctx(struct ieee80211_hw *hw,
+ 					 int n_vifs,
+ 					 enum ieee80211_chanctx_switch_mode mode);
+ 
++/**
++ * ieee80211_links_removed - notify removed links
++ * @vif: interface to be notified
++ * @removed_links: links bitmap being removed
++ *
++ */
++void ieee80211_links_removed(struct ieee80211_vif *vif, u16 removed_links);
++
+ /**
+  * ieee80211_get_scanning - get scanning bitmask
+  *
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 69c3e81..e8053b1 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1657,9 +1657,11 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
+ 	__sta_info_flush(sdata, true, link_id);
+ 
+ 	link_conf->enable_beacon = false;
+-	sdata->beacon_rate_set = false;
+-	sdata->vif.cfg.ssid_len = 0;
+-	clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
++	if (ieee80211_num_beaconing_links(sdata) <= 1) {
++		sdata->beacon_rate_set = false;
++		sdata->vif.cfg.ssid_len = 0;
++		clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
++	}
+ 	ieee80211_link_info_change_notify(sdata, link,
+ 					  BSS_CHANGED_BEACON_ENABLED);
+ 
+@@ -1674,8 +1676,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
+ 	drv_stop_ap(sdata->local, sdata, link_conf);
+ 
+ 	/* free all potentially still buffered bcast frames */
+-	local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
+-	ieee80211_purge_tx_queue(&local->hw, &sdata->u.ap.ps.bc_buf);
++	if (ieee80211_num_beaconing_links(sdata) <= 1) {
++		local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
++		ieee80211_purge_tx_queue(&local->hw, &sdata->u.ap.ps.bc_buf);
++	}
+ 
+ 	ieee80211_link_copy_chanctx_to_vlans(link, true);
+ 	ieee80211_link_release_channel(link);
+@@ -5257,6 +5261,15 @@ ieee80211_skip_cac(struct wireless_dev *wdev, unsigned int link_id)
+ 	}
+ }
+ 
++void ieee80211_links_removed(struct ieee80211_vif *vif, u16 removed_links)
++{
++	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
++
++	sdata->removed_links = removed_links;
++	wiphy_work_queue(sdata->local->hw.wiphy, &sdata->links_removed_work);
++}
++EXPORT_SYMBOL_GPL(ieee80211_links_removed);
++
+ const struct cfg80211_ops mac80211_config_ops = {
+ 	.add_virtual_intf = ieee80211_add_iface,
+ 	.del_virtual_intf = ieee80211_del_iface,
+diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
+index fa8d751..a77e63c 100644
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -1182,6 +1182,10 @@ struct ieee80211_sub_if_data {
+ 
+ 	u16 restart_active_links;
+ 
++	/* for MLD reconf of affliated AP removal */
++	struct wiphy_work links_removed_work;
++	u16 removed_links;
++
+ #ifdef CPTCFG_MAC80211_DEBUGFS
+ 	struct {
+ 		struct dentry *subdir_stations;
+diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
+index eb7a05d..3714204 100644
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -783,6 +783,7 @@ static int ieee80211_stop(struct net_device *dev)
+ 
+ 	wiphy_lock(sdata->local->hw.wiphy);
+ 	wiphy_work_cancel(sdata->local->hw.wiphy, &sdata->activate_links_work);
++	wiphy_work_cancel(sdata->local->hw.wiphy, &sdata->links_removed_work);
+ 
+ 	ieee80211_do_stop(sdata, true);
+ 	wiphy_unlock(sdata->local->hw.wiphy);
+@@ -1748,6 +1749,22 @@ static void ieee80211_activate_links_work(struct wiphy *wiphy,
+ 	sdata->desired_active_links = 0;
+ }
+ 
++static void ieee80211_links_removed_work(struct wiphy *wiphy,
++					 struct wiphy_work *work)
++{
++	struct ieee80211_sub_if_data *sdata =
++		container_of(work, struct ieee80211_sub_if_data, links_removed_work);
++	struct ieee80211_local *local = sdata->local;
++
++	lockdep_assert_wiphy(local->hw.wiphy);
++
++	if (!ieee80211_sdata_running(sdata))
++		return;
++
++	cfg80211_links_removed(sdata->dev, sdata->removed_links);
++	sdata->removed_links = 0;
++}
++
+ /*
+  * Helper function to initialise an interface to a specific type.
+  */
+@@ -1786,6 +1803,8 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
+ 	wiphy_work_init(&sdata->work, ieee80211_iface_work);
+ 	wiphy_work_init(&sdata->activate_links_work,
+ 			ieee80211_activate_links_work);
++	wiphy_work_init(&sdata->links_removed_work,
++			ieee80211_links_removed_work);
+ 
+ 	switch (type) {
+ 	case NL80211_IFTYPE_P2P_GO:
+diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
+index b1eb519..c9f3c8e 100644
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -1596,6 +1596,12 @@ int __sta_info_flush(struct ieee80211_sub_if_data *sdata, bool vlans,
+ 		    !(sta->sta.valid_links & BIT(link_id)))
+ 			continue;
+ 
++		/* sta still has more than one link */
++		if (link_id >= 0 && (sta->sta.valid_links & ~BIT(link_id))) {
++			ieee80211_sta_remove_link(sta, link_id);
++			continue;
++		}
++
+ 		if (!WARN_ON(__sta_info_destroy_part1(sta)))
+ 			list_add(&sta->free_list, &free_list);
+ 
+diff --git a/net/wireless/ap.c b/net/wireless/ap.c
+index 9cd0ab4..9fc296a 100644
+--- a/net/wireless/ap.c
++++ b/net/wireless/ap.c
+@@ -16,10 +16,15 @@ static int ___cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
+ 			       bool notify)
+ {
+ 	struct wireless_dev *wdev = dev->ieee80211_ptr;
+-	int err;
++	int err, i;
++	u16 beaconing_links = 0;
+ 
+ 	lockdep_assert_wiphy(wdev->wiphy);
+ 
++	for_each_valid_link(wdev, i)
++		if (wdev->links[i].ap.beacon_interval)
++			beaconing_links |= BIT(i);
++
+ 	if (!rdev->ops->stop_ap)
+ 		return -EOPNOTSUPP;
+ 
+@@ -35,19 +40,22 @@ static int ___cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
+ 
+ 	err = rdev_stop_ap(rdev, dev, link_id);
+ 	if (!err) {
+-		wdev->conn_owner_nlportid = 0;
+ 		wdev->links[link_id].ap.beacon_interval = 0;
+ 		memset(&wdev->links[link_id].ap.chandef, 0,
+ 		       sizeof(wdev->links[link_id].ap.chandef));
+-		wdev->u.ap.ssid_len = 0;
+-		rdev_set_qos_map(rdev, dev, NULL);
++		if (hweight16(beaconing_links) <= 1) {
++			wdev->conn_owner_nlportid = 0;
++			wdev->u.ap.ssid_len = 0;
++			rdev_set_qos_map(rdev, dev, NULL);
++		}
+ 		if (notify)
+ 			nl80211_send_ap_stopped(wdev, link_id);
+ 
+ 		cfg80211_sched_dfs_chan_update(rdev);
+ 	}
+ 
+-	schedule_work(&cfg80211_disconnect_work);
++	if (hweight16(beaconing_links) <= 1)
++		schedule_work(&cfg80211_disconnect_work);
+ 
+ 	return err;
+ }
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index c38a5d0..e451977 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -18767,7 +18767,8 @@ void cfg80211_links_removed(struct net_device *dev, u16 link_mask)
+ 	trace_cfg80211_links_removed(dev, link_mask);
+ 
+ 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
+-		    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
++		    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT &&
++		    wdev->iftype != NL80211_IFTYPE_AP))
+ 		return;
+ 
+ 	if (WARN_ON(!wdev->valid_links || !link_mask ||
+@@ -18775,8 +18776,10 @@ void cfg80211_links_removed(struct net_device *dev, u16 link_mask)
+ 		    wdev->valid_links == link_mask))
+ 		return;
+ 
+-	cfg80211_wdev_release_link_bsses(wdev, link_mask);
+-	wdev->valid_links &= ~link_mask;
++	if (wdev->iftype != NL80211_IFTYPE_AP) {
++		cfg80211_wdev_release_link_bsses(wdev, link_mask);
++		wdev->valid_links &= ~link_mask;
++	}
+ 
+ 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ 	if (!msg)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0086-mtk-mac80211-add-A-TTLM-support.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0086-mtk-mac80211-add-A-TTLM-support.patch
new file mode 100644
index 0000000..643f7d5
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0086-mtk-mac80211-add-A-TTLM-support.patch
@@ -0,0 +1,384 @@
+From 137d2eae370d772d579f737f9d99c9cc14850bf6 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Wed, 3 Jul 2024 17:05:01 +0800
+Subject: [PATCH 86/89] mtk: mac80211: add A-TTLM support
+
+There are 3 A-TTLM events from the driver, and mac80211 just forward
+them
+1. A-TTLM started: the switch time in TSF is also sent.
+2. A-TTLM switch time expired
+3. A-TTLM ended
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ include/net/cfg80211.h       | 11 ++++++
+ include/net/mac80211.h       | 12 ++++++
+ include/uapi/linux/nl80211.h | 16 ++++++++
+ net/mac80211/cfg.c           | 23 +++++++++++
+ net/wireless/nl80211.c       | 75 ++++++++++++++++++++++++++++++++++++
+ net/wireless/rdev-ops.h      | 18 +++++++++
+ net/wireless/trace.h         | 45 ++++++++++++++++++++++
+ 7 files changed, 200 insertions(+)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index 835735f..bc3e585 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -5035,6 +5035,8 @@ struct cfg80211_ops {
+ 				    struct link_station_del_parameters *params);
+ 	int	(*set_hw_timestamp)(struct wiphy *wiphy, struct net_device *dev,
+ 				    struct cfg80211_set_hw_timestamp *hwts);
++	int	(*set_attlm)(struct wiphy *wiphy, struct net_device *dev,
++			     u16 disabled_links, u16 switch_time, u32 duration);
+ 	int	(*set_ttlm)(struct wiphy *wiphy, struct net_device *dev,
+ 			    struct cfg80211_ttlm_params *params);
+ 	u32	(*get_radio_mask)(struct wiphy *wiphy, struct net_device *dev);
+@@ -9743,6 +9745,15 @@ int cfg80211_bss_color_notify(struct net_device *dev,
+ 			      enum nl80211_commands cmd, u8 count,
+ 			      u64 color_bitmap, u8 link_id);
+ 
++/**
++ * cfg80211_attlm_notify - notify about Advertised Tid-to-Link Mapping
++ * @wdev: the wireless device to check.
++ * @switch_time_tsf_tu: switch time TSF in unit of TUs that is reported by driver.
++ * @event: A-TTLM event
++ * @gfp: allocation flags
++ */
++void cfg80211_attlm_notify(struct wireless_dev *wdev, u16 switch_time_tsf_tu,
++			   enum nl80211_attlm_event event, gfp_t gfp);
+ /**
+  * cfg80211_obss_color_collision_notify - notify about bss color collision
+  * @dev: network device
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index be6631e..1345676 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -4852,6 +4852,8 @@ struct ieee80211_ops {
+ 			    struct net_device *dev,
+ 			    enum tc_setup_type type,
+ 			    void *type_data);
++	int (*set_attlm)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++			 u16 disabled_links, u16 switch_time, u32 druation);
+ 	enum ieee80211_neg_ttlm_res
+ 	(*can_neg_ttlm)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 			struct ieee80211_neg_ttlm *ttlm);
+@@ -7730,6 +7732,16 @@ int ieee80211_emulate_switch_vif_chanctx(struct ieee80211_hw *hw,
+ 					 int n_vifs,
+ 					 enum ieee80211_chanctx_switch_mode mode);
+ 
++/**
++ * ieee80211_attlm_notify - notify Advertised Tid-to-Link-Mapping
++ * @vif: interface to be notified
++ * @switch_time_tsf_tu: switch time TSF in unit of TUs that is reported by driver.
++ * @event: A-TTLM event
++ * @gfp: allocation flags
++ */
++void ieee80211_attlm_notify(struct ieee80211_vif *vif, u16 switch_time_tsf_tu,
++			    enum nl80211_attlm_event event, gfp_t gfp);
++
+ /**
+  * ieee80211_links_removed - notify removed links
+  * @vif: interface to be notified
+diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
+index 6a2291d..7188839 100644
+--- a/include/uapi/linux/nl80211.h
++++ b/include/uapi/linux/nl80211.h
+@@ -1588,6 +1588,10 @@ enum nl80211_commands {
+ 
+ 	/* add new commands above here */
+ 
++	/* MTK internal */
++	NL80211_CMD_ATTLM_EVENT,
++	NL80211_CMD_SET_ATTLM,
++
+ 	/* used to define NL80211_CMD_MAX below */
+ 	__NL80211_CMD_AFTER_LAST,
+ 	NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1
+@@ -3388,6 +3392,7 @@ enum nl80211_attrs {
+ 
+ 	NL80211_ATTR_MLO_LINKS,
+ 	NL80211_ATTR_MLO_LINK_ID,
++	NL80211_ATTR_MLO_LINK_DISABLED_BMP,
+ 	NL80211_ATTR_MLD_ADDR,
+ 
+ 	NL80211_ATTR_MLO_SUPPORT,
+@@ -3425,6 +3430,11 @@ enum nl80211_attrs {
+ 	/* MTK internal */
+ 	NL80211_ATTR_CNTDWN_OFFS_STA_PROF,
+ 
++	NL80211_ATTR_MLO_ATTLM_EVENT,
++	NL80211_ATTR_MLO_ATTLM_SWITCH_TIME,
++	NL80211_ATTR_MLO_ATTLM_DURATION,
++	NL80211_ATTR_MLO_ATTLM_SWITCH_TIME_TSF_TU,
++
+ 	__NL80211_ATTR_AFTER_LAST,
+ 	NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST,
+ 	NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
+@@ -8091,4 +8101,10 @@ enum nl80211_wiphy_radio_freq_range {
+ 	NL80211_WIPHY_RADIO_FREQ_ATTR_MAX = __NL80211_WIPHY_RADIO_FREQ_ATTR_LAST - 1,
+ };
+ 
++enum nl80211_attlm_event {
++	NL80211_ATTLM_STARTED,
++	NL80211_ATTLM_SWITCH_TIME_EXPIRED,
++	NL80211_ATTLM_END,
++};
++
+ #endif /* __LINUX_NL80211_H */
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index e8053b1..1784b8b 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -5222,6 +5222,20 @@ static int ieee80211_set_hw_timestamp(struct wiphy *wiphy,
+ 	return local->ops->set_hw_timestamp(&local->hw, &sdata->vif, hwts);
+ }
+ 
++static int
++ieee80211_set_attlm(struct wiphy *wiphy, struct net_device *dev,
++		    u16 disabled_links, u16 switch_time, u32 duration)
++{
++	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
++	struct ieee80211_local *local = sdata->local;
++
++	if (!local->ops->set_attlm)
++		return -EOPNOTSUPP;
++
++	return local->ops->set_attlm(&local->hw, &sdata->vif, disabled_links,
++				     switch_time, duration);
++}
++
+ static int
+ ieee80211_set_ttlm(struct wiphy *wiphy, struct net_device *dev,
+ 		   struct cfg80211_ttlm_params *params)
+@@ -5261,6 +5275,14 @@ ieee80211_skip_cac(struct wireless_dev *wdev, unsigned int link_id)
+ 	}
+ }
+ 
++void ieee80211_attlm_notify(struct ieee80211_vif *vif, u16 switch_time_tsf_tu,
++			   enum nl80211_attlm_event event, gfp_t gfp)
++{
++	cfg80211_attlm_notify(ieee80211_vif_to_wdev(vif), switch_time_tsf_tu,
++			      event, gfp);
++}
++EXPORT_SYMBOL_GPL(ieee80211_attlm_notify);
++
+ void ieee80211_links_removed(struct ieee80211_vif *vif, u16 removed_links)
+ {
+ 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+@@ -5383,6 +5405,7 @@ const struct cfg80211_ops mac80211_config_ops = {
+ 	.mod_link_station = ieee80211_mod_link_station,
+ 	.del_link_station = ieee80211_del_link_station,
+ 	.set_hw_timestamp = ieee80211_set_hw_timestamp,
++	.set_attlm = ieee80211_set_attlm,
+ 	.set_ttlm = ieee80211_set_ttlm,
+ 	.get_radio_mask = ieee80211_get_radio_mask,
+ 	.skip_cac = ieee80211_skip_cac,
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index e451977..5439aa0 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -843,6 +843,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
+ 		NLA_POLICY_NESTED_ARRAY(nl80211_policy),
+ 	[NL80211_ATTR_MLO_LINK_ID] =
+ 		NLA_POLICY_RANGE(NLA_U8, 0, IEEE80211_MLD_MAX_NUM_LINKS),
++	[NL80211_ATTR_MLO_LINK_DISABLED_BMP] = { .type = NLA_U16 },
+ 	[NL80211_ATTR_MLD_ADDR] = NLA_POLICY_EXACT_LEN(ETH_ALEN),
+ 	[NL80211_ATTR_MLO_SUPPORT] = { .type = NLA_FLAG },
+ 	[NL80211_ATTR_MAX_NUM_AKM_SUITES] = { .type = NLA_REJECT },
+@@ -856,6 +857,9 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
+ 	[NL80211_ATTR_HW_TIMESTAMP_ENABLED] = { .type = NLA_FLAG },
+ 	[NL80211_ATTR_EMA_RNR_ELEMS] = { .type = NLA_NESTED },
+ 	[NL80211_ATTR_MLO_LINK_DISABLED] = { .type = NLA_FLAG },
++	[NL80211_ATTR_MLO_ATTLM_SWITCH_TIME] = { .type = NLA_U16 },
++	[NL80211_ATTR_MLO_ATTLM_DURATION] = { .type = NLA_U32 },
++	[NL80211_ATTR_MLO_ATTLM_SWITCH_TIME_TSF_TU] = { .type = NLA_U16 },
+ 	[NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA] = { .type = NLA_FLAG },
+ 	[NL80211_ATTR_MLO_TTLM_DLINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8),
+ 	[NL80211_ATTR_MLO_TTLM_ULINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8),
+@@ -16689,6 +16693,30 @@ static int nl80211_set_hw_timestamp(struct sk_buff *skb,
+ 	return rdev_set_hw_timestamp(rdev, dev, &hwts);
+ }
+ 
++static int
++nl80211_set_attlm(struct sk_buff *skb, struct genl_info *info)
++{
++	struct cfg80211_registered_device *rdev = info->user_ptr[0];
++	struct net_device *dev = info->user_ptr[1];
++	struct wireless_dev *wdev = dev->ieee80211_ptr;
++	u16 switch_time, disabled_links;
++	u32 duration;
++
++	if (wdev->iftype != NL80211_IFTYPE_AP)
++		return -EOPNOTSUPP;
++
++	if (!info->attrs[NL80211_ATTR_MLO_LINK_DISABLED_BMP] ||
++	    !info->attrs[NL80211_ATTR_MLO_ATTLM_SWITCH_TIME] ||
++	    !info->attrs[NL80211_ATTR_MLO_ATTLM_DURATION])
++		return -EINVAL;
++
++	disabled_links = nla_get_u16(info->attrs[NL80211_ATTR_MLO_LINK_DISABLED_BMP]);
++	switch_time = nla_get_u16(info->attrs[NL80211_ATTR_MLO_ATTLM_SWITCH_TIME]);
++	duration = nla_get_u32(info->attrs[NL80211_ATTR_MLO_ATTLM_DURATION]);
++
++	return rdev_set_attlm(rdev, dev, disabled_links, switch_time, duration);
++}
++
+ static int
+ nl80211_set_ttlm(struct sk_buff *skb, struct genl_info *info)
+ {
+@@ -17916,6 +17944,12 @@ static const struct genl_small_ops nl80211_small_ops[] = {
+ 		.flags = GENL_UNS_ADMIN_PERM,
+ 		.internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP),
+ 	},
++	{
++		.cmd = NL80211_CMD_SET_ATTLM,
++		.doit = nl80211_set_attlm,
++		.flags = GENL_UNS_ADMIN_PERM,
++		.internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP),
++	},
+ 	{
+ 		.cmd = NL80211_CMD_SET_TID_TO_LINK_MAPPING,
+ 		.doit = nl80211_set_ttlm,
+@@ -19951,6 +19985,47 @@ nla_put_failure:
+ }
+ EXPORT_SYMBOL(cfg80211_bss_color_notify);
+ 
++void cfg80211_attlm_notify(struct wireless_dev *wdev, u16 switch_time_tsf_tu,
++			   enum nl80211_attlm_event event, gfp_t gfp)
++{
++	struct wiphy *wiphy = wdev->wiphy;
++	struct net_device *netdev = wdev->netdev;
++	struct sk_buff *msg;
++	void *hdr;
++
++	trace_cfg80211_attlm_notify(wiphy, netdev, event, switch_time_tsf_tu);
++
++	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
++	if (!msg)
++		return;
++
++	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ATTLM_EVENT);
++	if (!hdr) {
++		nlmsg_free(msg);
++		return;
++	}
++
++	if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
++		goto nla_put_failure;
++
++	if (nla_put_u32(msg, NL80211_ATTR_MLO_ATTLM_EVENT, event))
++		goto nla_put_failure;
++
++	if (nla_put_u16(msg, NL80211_ATTR_MLO_ATTLM_SWITCH_TIME_TSF_TU,
++			switch_time_tsf_tu))
++		goto nla_put_failure;
++
++	genlmsg_end(msg, hdr);
++
++	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(wiphy), msg, 0,
++				NL80211_MCGRP_MLME, gfp);
++	return;
++
++nla_put_failure:
++	nlmsg_free(msg);
++}
++EXPORT_SYMBOL(cfg80211_attlm_notify);
++
+ void
+ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
+ 		     const struct cfg80211_chan_def *chandef,
+diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
+index bcd51fc..4056f70 100644
+--- a/net/wireless/rdev-ops.h
++++ b/net/wireless/rdev-ops.h
+@@ -1538,6 +1538,24 @@ rdev_set_hw_timestamp(struct cfg80211_registered_device *rdev,
+ 	return ret;
+ }
+ 
++static inline int
++rdev_set_attlm(struct cfg80211_registered_device *rdev, struct net_device *dev,
++	       u16 disabled_links, u16 switch_time, u32 duration)
++{
++	struct wiphy *wiphy = &rdev->wiphy;
++	int ret;
++
++	if (!rdev->ops->set_attlm)
++		return -EOPNOTSUPP;
++
++	trace_rdev_set_attlm(wiphy, dev, disabled_links, switch_time, duration);
++	ret = rdev->ops->set_attlm(wiphy, dev, disabled_links, switch_time,
++				   duration);
++	trace_rdev_return_int(wiphy, ret);
++
++	return ret;
++}
++
+ static inline int
+ rdev_set_ttlm(struct cfg80211_registered_device *rdev,
+ 	      struct net_device *dev,
+diff --git a/net/wireless/trace.h b/net/wireless/trace.h
+index 90ba38a..9de35cc 100644
+--- a/net/wireless/trace.h
++++ b/net/wireless/trace.h
+@@ -3048,6 +3048,30 @@ TRACE_EVENT(rdev_set_hw_timestamp,
+ 		  __entry->enable)
+ );
+ 
++TRACE_EVENT(rdev_set_attlm,
++	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
++		 u16 disabled_links, u16 switch_time, u32 duration),
++	TP_ARGS(wiphy, netdev, disabled_links, switch_time, duration),
++	TP_STRUCT__entry(
++		WIPHY_ENTRY
++		NETDEV_ENTRY
++		__field(u16, disabled_links)
++		__field(u16, switch_time)
++		__field(u32, duration)
++	),
++	TP_fast_assign(
++		WIPHY_ASSIGN;
++		NETDEV_ASSIGN;
++		__entry->disabled_links = disabled_links;
++		__entry->switch_time = switch_time;
++		__entry->duration = duration;
++	),
++	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", disabled_link: %u"
++		  ", switch_time=%u, duration=%u",
++		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->disabled_links,
++		  __entry->switch_time, __entry->duration)
++);
++
+ TRACE_EVENT(rdev_set_ttlm,
+ 	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ 		 struct cfg80211_ttlm_params *params),
+@@ -4018,6 +4042,27 @@ TRACE_EVENT(cfg80211_bss_color_notify,
+ 		  __entry->color_bitmap)
+ );
+ 
++TRACE_EVENT(cfg80211_attlm_notify,
++	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
++		 enum nl80211_attlm_event event, u16 switch_time_tsf_tu),
++	TP_ARGS(wiphy, netdev, event, switch_time_tsf_tu),
++	TP_STRUCT__entry(
++		WIPHY_ENTRY
++		NETDEV_ENTRY
++		__field(u32, event)
++		__field(u16, switch_time_tsf_tu)
++	),
++	TP_fast_assign(
++		WIPHY_ASSIGN;
++		NETDEV_ASSIGN;
++		__entry->event = event;
++		__entry->switch_time_tsf_tu = switch_time_tsf_tu;
++	),
++	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", event: %x, switch_time_tsf_tu: %u",
++		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->event,
++		  __entry->switch_time_tsf_tu)
++);
++
+ TRACE_EVENT(cfg80211_assoc_comeback,
+ 	TP_PROTO(struct wireless_dev *wdev, const u8 *ap_addr, u32 timeout),
+ 	TP_ARGS(wdev, ap_addr, timeout),
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0087-mtk-mac80211-add-SCS-capa-definition.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0087-mtk-mac80211-add-SCS-capa-definition.patch
new file mode 100644
index 0000000..8e588c0
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0087-mtk-mac80211-add-SCS-capa-definition.patch
@@ -0,0 +1,29 @@
+From 5d05ee69f1435865e4b74db81a6f15dc985300f0 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Fri, 5 Jul 2024 09:42:46 +0800
+Subject: [PATCH 87/89] mtk: mac80211: add SCS capa definition
+
+Add scs capabilities definition in extended capabilities ie.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+
+---
+ include/linux/ieee80211.h | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
+index 3039cb1..10587c6 100644
+--- a/include/linux/ieee80211.h
++++ b/include/linux/ieee80211.h
+@@ -4028,6 +4028,8 @@ enum ieee80211_tdls_actioncode {
+ #define WLAN_EXT_CAPA5_TDLS_PROHIBITED	BIT(6)
+ #define WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED	BIT(7)
+ 
++#define WLAN_EXT_CAPA7_SCS_SUPPORT 	BIT(6)
++
+ #define WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED	BIT(5)
+ #define WLAN_EXT_CAPA8_OPMODE_NOTIF	BIT(6)
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0088-mtk-mac80211-add-puncture-bitmap-sync-for-extender.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0088-mtk-mac80211-add-puncture-bitmap-sync-for-extender.patch
new file mode 100644
index 0000000..68ea938
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0088-mtk-mac80211-add-puncture-bitmap-sync-for-extender.patch
@@ -0,0 +1,78 @@
+From 432b8c55111c12f1ec1fdc1c6e50c17084343cad Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 6 Aug 2024 10:15:01 +0800
+Subject: [PATCH 88/89] mtk: mac80211: add puncture bitmap sync for extender
+
+Add puncture bitmap sync for extender.
+The latest puncture bitmap is not synchronized with the bitmap in the
+chandef passed to ieee80211_determine_ap_chan.
+Therefore, extender STA should use the latest puncture bitmap to check
+the ap mode of the extender AP when it is updated by root AP.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ include/net/cfg80211.h |  2 ++
+ net/mac80211/mlme.c    | 13 ++++++++++---
+ 2 files changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index bc3e585..17fb1c7 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -838,6 +838,8 @@ struct cfg80211_chan_def {
+ 	u16 punctured;
+ };
+ 
++#define IEEE80211_PUNCT_UNSPECIFIED	0xffff
++
+ /*
+  * cfg80211_bitrate_mask - masks for bitrate control
+  */
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 4964d08..6cbcfe4 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -154,7 +154,8 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata,
+ 			    const struct ieee802_11_elems *elems,
+ 			    bool ignore_ht_channel_mismatch,
+ 			    const struct ieee80211_conn_settings *conn,
+-			    struct cfg80211_chan_def *chandef)
++			    struct cfg80211_chan_def *chandef,
++			    u16 punctured)
+ {
+ 	const struct ieee80211_ht_operation *ht_oper = elems->ht_operation;
+ 	const struct ieee80211_vht_operation *vht_oper = elems->vht_operation;
+@@ -321,6 +322,10 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata,
+ 
+ 		eht_chandef.punctured =
+ 			ieee80211_eht_oper_dis_subchan_bitmap(eht_oper);
++		if (punctured == IEEE80211_PUNCT_UNSPECIFIED)
++			chandef->punctured = eht_chandef.punctured;
++		else
++			chandef->punctured = punctured;
+ 
+ 		if (!cfg80211_chandef_valid(&eht_chandef)) {
+ 			sdata_info(sdata,
+@@ -840,7 +845,8 @@ again:
+ 		return ERR_PTR(-ENOMEM);
+ 
+ 	ap_mode = ieee80211_determine_ap_chan(sdata, channel, bss->vht_cap_info,
+-					      elems, false, conn, ap_chandef);
++					      elems, false, conn, ap_chandef,
++					      IEEE80211_PUNCT_UNSPECIFIED);
+ 
+ 	/* this should be impossible since parsing depends on our mode */
+ 	if (WARN_ON(ap_mode > conn->mode)) {
+@@ -1007,7 +1013,8 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link,
+ 
+ 	ap_mode = ieee80211_determine_ap_chan(sdata, channel, vht_cap_info,
+ 					      elems, true, &link->u.mgd.conn,
+-					      &ap_chandef);
++					      &ap_chandef,
++					      link->conf->chanreq.oper.punctured);
+ 
+ 	if (ap_mode != link->u.mgd.conn.mode) {
+ 		link_info(link,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0089-mtk-mac80211-add-STA-A-TTLM-support.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0089-mtk-mac80211-add-STA-A-TTLM-support.patch
new file mode 100644
index 0000000..96b579f
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/0089-mtk-mac80211-add-STA-A-TTLM-support.patch
@@ -0,0 +1,305 @@
+From 1d72b21f1119a0c50c70002b14408da5c800bea7 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 2 Aug 2024 17:08:08 +0800
+Subject: [PATCH 89/89] mtk: mac80211: add STA A-TTLM support
+
+Move adv_ttlm_info to ieee80211_vif so that driver can use it in
+vif_cfg_changed callback.
+
+Add BSS_CHANGED_MLD_ADV_TTLM and re-name the changed flag for
+negotiation TTLM.
+
+It is necessary to distinguish adv_ttlm from neg_ttlm because of the
+different data structure used.
+(ieee80211_adv_ttlm_info vs. ieee80211_neg_ttlm)
+
+This change includes:
+1. do not call ieee80211_set_active_link(), which will remove
+   disabled/dormant links. Removing the disabled/dormant links is not
+   the purpose of TTLM.
+2. use the flag BSS_CHANGED_MLD_ADV_TTLM properly.
+3. set adv_ttlm_info to active _before_ calling ieee80211_ttlm_set_links
+   so that driver knows the adv-TTLM is active or not when handle the
+   vif_cfg_vhanged callback.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ .../wireless/intel/iwlwifi/mvm/mld-mac80211.c |  2 +-
+ include/net/mac80211.h                        | 27 +++++++++--
+ net/mac80211/ieee80211_i.h                    | 12 -----
+ net/mac80211/main.c                           |  3 +-
+ net/mac80211/mlme.c                           | 48 +++++++++++--------
+ 5 files changed, 56 insertions(+), 36 deletions(-)
+
+diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c
+index 1dbc346..865c273 100644
+--- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c
++++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c
+@@ -952,7 +952,7 @@ static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm,
+ 			IWL_ERR(mvm, "failed to update power mode\n");
+ 	}
+ 
+-	if (changes & (BSS_CHANGED_MLD_VALID_LINKS | BSS_CHANGED_MLD_TTLM) &&
++	if (changes & (BSS_CHANGED_MLD_VALID_LINKS | BSS_CHANGED_MLD_NEG_TTLM) &&
+ 	    ieee80211_vif_is_mld(vif) && mvmvif->authorized)
+ 		wiphy_delayed_work_queue(mvm->hw->wiphy,
+ 					 &mvmvif->mlo_int_scan_wk, 0);
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index 1345676..16e1861 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -365,7 +365,8 @@ struct ieee80211_vif_chanctx_switch {
+  * @BSS_CHANGED_UNSOL_BCAST_PROBE_RESP: Unsolicited broadcast probe response
+  *	status changed.
+  * @BSS_CHANGED_MLD_VALID_LINKS: MLD valid links status changed.
+- * @BSS_CHANGED_MLD_TTLM: negotiated TID to link mapping was changed
++ * @BSS_CHANGED_MLD_NEG_TTLM: negotiated TID to link mapping was changed
++ * @BSS_CHANGED_MLD_ADV_TTLM: advertised TID to link mapping was changed
+  * @BSS_CHANGED_TPE: transmit power envelope changed
+  */
+ enum ieee80211_bss_change {
+@@ -402,8 +403,9 @@ enum ieee80211_bss_change {
+ 	BSS_CHANGED_FILS_DISCOVERY      = 1<<30,
+ 	BSS_CHANGED_UNSOL_BCAST_PROBE_RESP = 1<<31,
+ 	BSS_CHANGED_MLD_VALID_LINKS	= BIT_ULL(33),
+-	BSS_CHANGED_MLD_TTLM		= BIT_ULL(34),
+-	BSS_CHANGED_TPE			= BIT_ULL(35),
++	BSS_CHANGED_MLD_NEG_TTLM	= BIT_ULL(34),
++	BSS_CHANGED_MLD_ADV_TTLM	= BIT_ULL(35),
++	BSS_CHANGED_TPE			= BIT_ULL(36),
+ 
+ 	/* when adding here, make sure to change ieee80211_reconfig */
+ };
+@@ -1929,6 +1931,22 @@ struct ieee80211_vif_cfg {
+ 
+ #define IEEE80211_TTLM_NUM_TIDS 8
+ 
++/**
++ * struct ieee8021_adv_ttlm - advertised TID to link map info
++ *
++ * @switch_time: time in TUs at which the new mapping is established, or 0 if
++ *      there is no planned advertised TID-to-link mapping.
++ * @duration: duration of the planned TID-to-link mapping in TUs.
++ * @map: bitmap of usable links for all TIDs.
++ * @active: whether the advertised mapping is active or not.
++ */
++struct ieee80211_adv_ttlm {
++	u16 switch_time;
++	u32 duration;
++	u16 map;
++	bool active;
++};
++
+ /**
+  * struct ieee80211_neg_ttlm - negotiated TID to link map info
+  *
+@@ -1979,6 +1997,8 @@ enum ieee80211_neg_ttlm_res {
+  *	suspended due to negotiated TTLM, and could be activated in the
+  *	future by tearing down the TTLM negotiation.
+  *	0 for non-MLO.
++ * @adv_ttlm: advertised TID to link mapping info.
++ *	see &struct ieee80211_adv_ttlm.
+  * @neg_ttlm: negotiated TID to link mapping info.
+  *	see &struct ieee80211_neg_ttlm.
+  * @addr: address of this interface
+@@ -2019,6 +2039,7 @@ struct ieee80211_vif {
+ 	struct ieee80211_bss_conf bss_conf;
+ 	struct ieee80211_bss_conf __rcu *link_conf[IEEE80211_MLD_MAX_NUM_LINKS];
+ 	u16 valid_links, active_links, dormant_links, suspended_links;
++	struct ieee80211_adv_ttlm adv_ttlm;
+ 	struct ieee80211_neg_ttlm neg_ttlm;
+ 	u8 addr[ETH_ALEN] __aligned(2);
+ 	bool p2p;
+diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
+index a77e63c..9027209 100644
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -496,17 +496,6 @@ struct ieee80211_sta_tx_tspec {
+ 	bool downgraded;
+ };
+ 
+-/* Advertised TID-to-link mapping info */
+-struct ieee80211_adv_ttlm_info {
+-	/* time in TUs at which the new mapping is established, or 0 if there is
+-	 * no planned advertised TID-to-link mapping
+-	 */
+-	u16 switch_time;
+-	u32 duration; /* duration of the planned T2L map in TUs */
+-	u16 map; /* map of usable links for all TIDs */
+-	bool active; /* whether the advertised mapping is active or not */
+-};
+-
+ DECLARE_EWMA(beacon_signal, 4, 4)
+ 
+ struct ieee80211_if_managed {
+@@ -606,7 +595,6 @@ struct ieee80211_if_managed {
+ 
+ 	/* TID-to-link mapping support */
+ 	struct wiphy_delayed_work ttlm_work;
+-	struct ieee80211_adv_ttlm_info ttlm_info;
+ 	struct wiphy_work teardown_ttlm_work;
+ 
+ 	/* dialog token enumerator for neg TTLM request */
+diff --git a/net/mac80211/main.c b/net/mac80211/main.c
+index 1de7f1d..6ab7a0c 100644
+--- a/net/mac80211/main.c
++++ b/net/mac80211/main.c
+@@ -328,7 +328,8 @@ EXPORT_SYMBOL(ieee80211_emulate_switch_vif_chanctx);
+ 				   BSS_CHANGED_ARP_FILTER |\
+ 				   BSS_CHANGED_SSID |\
+ 				   BSS_CHANGED_MLD_VALID_LINKS |\
+-				   BSS_CHANGED_MLD_TTLM)
++				   BSS_CHANGED_MLD_ADV_TTLM |\
++				   BSS_CHANGED_MLD_NEG_TTLM)
+ 
+ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
+ 				      u64 changed)
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 6cbcfe4..b235a43 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -3712,8 +3712,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
+ 	sdata->vif.cfg.eml_med_sync_delay = 0;
+ 	sdata->vif.cfg.mld_capa_op = 0;
+ 
+-	memset(&sdata->u.mgd.ttlm_info, 0,
+-	       sizeof(sdata->u.mgd.ttlm_info));
++	memset(&sdata->vif.adv_ttlm, 0,
++	       sizeof(sdata->vif.adv_ttlm));
+ 	wiphy_delayed_work_cancel(sdata->local->hw.wiphy, &ifmgd->ttlm_work);
+ 
+ 	memset(&sdata->vif.neg_ttlm, 0, sizeof(sdata->vif.neg_ttlm));
+@@ -6383,7 +6383,7 @@ static int ieee80211_ttlm_set_links(struct ieee80211_sub_if_data *sdata,
+ 	if (sdata->vif.neg_ttlm.valid) {
+ 		memset(&sdata->vif.neg_ttlm, 0, sizeof(sdata->vif.neg_ttlm));
+ 		sdata->vif.suspended_links = 0;
+-		changed = BSS_CHANGED_MLD_TTLM;
++		changed = BSS_CHANGED_MLD_NEG_TTLM;
+ 	}
+ 
+ 	if (sdata->vif.active_links != active_links) {
+@@ -6391,6 +6391,9 @@ static int ieee80211_ttlm_set_links(struct ieee80211_sub_if_data *sdata,
+ 		 * so notify the driver about the status change
+ 		 */
+ 		changed |= BSS_CHANGED_MLD_VALID_LINKS;
++		/* FIXME calling ieee80211_set_active_links leads to inactive
++		 * links being deleted, which should not be the purpose of
++		 * "disabling links"
+ 		active_links &= sdata->vif.active_links;
+ 		if (!active_links)
+ 			active_links =
+@@ -6401,6 +6404,7 @@ static int ieee80211_ttlm_set_links(struct ieee80211_sub_if_data *sdata,
+ 			sdata_info(sdata, "Failed to set TTLM active links\n");
+ 			goto out;
+ 		}
++		*/
+ 	}
+ 
+ 	ret = ieee80211_vif_set_links(sdata, sdata->vif.valid_links,
+@@ -6412,7 +6416,10 @@ static int ieee80211_ttlm_set_links(struct ieee80211_sub_if_data *sdata,
+ 
+ 	sdata->vif.suspended_links = suspended_links;
+ 	if (sdata->vif.suspended_links)
+-		changed |= BSS_CHANGED_MLD_TTLM;
++		changed |= BSS_CHANGED_MLD_NEG_TTLM;
++
++	if (sdata->vif.adv_ttlm.active)
++		changed |= BSS_CHANGED_MLD_ADV_TTLM;
+ 
+ 	ieee80211_vif_cfg_change_notify(sdata, changed);
+ 
+@@ -6431,18 +6438,19 @@ static void ieee80211_tid_to_link_map_work(struct wiphy *wiphy,
+ 		container_of(work, struct ieee80211_sub_if_data,
+ 			     u.mgd.ttlm_work.work);
+ 
+-	new_active_links = sdata->u.mgd.ttlm_info.map &
++	new_active_links = sdata->vif.adv_ttlm.map &
+ 			   sdata->vif.valid_links;
+-	new_dormant_links = ~sdata->u.mgd.ttlm_info.map &
++	new_dormant_links = ~sdata->vif.adv_ttlm.map &
+ 			    sdata->vif.valid_links;
++	sdata->vif.adv_ttlm.active = true;
++	sdata->vif.adv_ttlm.switch_time = 0;
+ 
+ 	ieee80211_vif_set_links(sdata, sdata->vif.valid_links, 0);
+ 	if (ieee80211_ttlm_set_links(sdata, new_active_links, new_dormant_links,
+-				     0))
++				     0)) {
++		sdata->vif.adv_ttlm.active = false;
+ 		return;
+-
+-	sdata->u.mgd.ttlm_info.active = true;
+-	sdata->u.mgd.ttlm_info.switch_time = 0;
++	}
+ }
+ 
+ static u16 ieee80211_get_ttlm(u8 bm_size, u8 *data)
+@@ -6456,7 +6464,7 @@ static u16 ieee80211_get_ttlm(u8 bm_size, u8 *data)
+ static int
+ ieee80211_parse_adv_t2l(struct ieee80211_sub_if_data *sdata,
+ 			const struct ieee80211_ttlm_elem *ttlm,
+-			struct ieee80211_adv_ttlm_info *ttlm_info)
++			struct ieee80211_adv_ttlm *ttlm_info)
+ {
+ 	/* The element size was already validated in
+ 	 * ieee80211_tid_to_link_map_size_ok()
+@@ -6545,13 +6553,13 @@ static void ieee80211_process_adv_ttlm(struct ieee80211_sub_if_data *sdata,
+ 		return;
+ 
+ 	if (!elems->ttlm_num) {
+-		if (sdata->u.mgd.ttlm_info.switch_time) {
++		if (sdata->vif.adv_ttlm.switch_time) {
+ 			/* if a planned TID-to-link mapping was cancelled -
+ 			 * abort it
+ 			 */
+ 			wiphy_delayed_work_cancel(sdata->local->hw.wiphy,
+ 						  &sdata->u.mgd.ttlm_work);
+-		} else if (sdata->u.mgd.ttlm_info.active) {
++		} else if (sdata->vif.adv_ttlm.active) {
+ 			/* if no TID-to-link element, set to default mapping in
+ 			 * which all TIDs are mapped to all setup links
+ 			 */
+@@ -6562,16 +6570,18 @@ static void ieee80211_process_adv_ttlm(struct ieee80211_sub_if_data *sdata,
+ 				sdata_info(sdata, "Failed setting valid/dormant links\n");
+ 				return;
+ 			}
++			sdata->vif.adv_ttlm.active = false;
+ 			ieee80211_vif_cfg_change_notify(sdata,
+-							BSS_CHANGED_MLD_VALID_LINKS);
++							BSS_CHANGED_MLD_VALID_LINKS |
++							BSS_CHANGED_MLD_ADV_TTLM);
+ 		}
+-		memset(&sdata->u.mgd.ttlm_info, 0,
+-		       sizeof(sdata->u.mgd.ttlm_info));
++		memset(&sdata->vif.adv_ttlm, 0,
++		       sizeof(sdata->vif.adv_ttlm));
+ 		return;
+ 	}
+ 
+ 	for (i = 0; i < elems->ttlm_num; i++) {
+-		struct ieee80211_adv_ttlm_info ttlm_info;
++		struct ieee80211_adv_ttlm ttlm_info;
+ 		u32 res;
+ 
+ 		res = ieee80211_parse_adv_t2l(sdata, elems->ttlm[i],
+@@ -6617,7 +6627,7 @@ static void ieee80211_process_adv_ttlm(struct ieee80211_sub_if_data *sdata,
+ 			else
+ 				delay_jiffies = 0;
+ 
+-			sdata->u.mgd.ttlm_info = ttlm_info;
++			sdata->vif.adv_ttlm = ttlm_info;
+ 			wiphy_delayed_work_cancel(sdata->local->hw.wiphy,
+ 						  &sdata->u.mgd.ttlm_work);
+ 			wiphy_delayed_work_queue(sdata->local->hw.wiphy,
+@@ -7476,7 +7486,7 @@ static void ieee80211_teardown_ttlm_work(struct wiphy *wiphy,
+ 	sdata->vif.suspended_links = 0;
+ 	ieee80211_vif_set_links(sdata, sdata->vif.valid_links,
+ 				new_dormant_links);
+-	ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_MLD_TTLM |
++	ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_MLD_NEG_TTLM |
+ 					       BSS_CHANGED_MLD_VALID_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 9bb2504..55ac991 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
@@ -1,64 +1,92 @@
 #patch subsys (come from openwrt/lede/target/linux/mediatek)
 SRC_URI_append = " \
-    file://0001-sync-backports-patches-build.patch \
-    file://0002-sync-backports-patches-ath.patch \
-    file://0003-sync-backports-patches-ath5k.patch \
-    file://0004-sync-backports-patches-ath9k.patch \
-    file://0005-sync-backports-patches-ath10k.patch \
-    file://0006-sync-backports-patches-rt2x00.patch \
-    file://0007-sync-backports-patches-subsys.patch \
-    file://0008-mtk-mac80211-do-not-setup-twt-when-twt-responder-is-.patch \
-    file://0009-mtk-cfg80211-extend-CAC-time-for-weather-radar-chann.patch \
-    file://0010-mtk-mac80211-it-s-invalid-case-when-frag_threshold-i.patch \
-    file://0011-mtk-cfg80211-implement-DFS-status-show-cac-and-nop-s.patch \
-    file://0012-mtk-mac80211-Set-TWT-Information-Frame-Disabled-bit-.patch \
-    file://0013-mtk-mac80211-check-the-control-channel-before-downgr.patch \
-    file://0014-mtk-mac80211-fix-tx-amsdu-aggregation.patch \
-    file://0015-mtk-mac80211-add-fill-receive-path-ops-to-get-wed-id.patch \
-    file://0016-mtk-mac80211-track-obss-color-bitmap.patch \
-    file://0017-mtk-mac80211-update-max_bssid_indicator-based-on-rea.patch \
-    file://0018-mtk-mac80211-support-configurable-addba-resp-time.patch \
-    file://0019-mtk-mac80211-add-sta-assisted-DFS-state-update-mecha.patch \
-    file://0020-mtk-nl80211-Mark-DFS-channel-as-available-for-CSA.patch \
-    file://0021-mtk-cfg80211-fix-early-return-in-cfg80211_stop_backg.patch \
-    file://0022-mtk-cfg80211-add-background-radar-stop-when-backgrou.patch \
-    file://0023-mtk-mac80211-avoid-kernel-warning-of-check_flush_dep.patch \
-    file://0024-mtk-mac80211-avoid-calling-switch_vif_chanctx-when-u.patch \
-    file://0025-mtk-mac80211-add-EHT-BA1024-support.patch \
-    file://0026-mtk-mac80211-add-rate-duration-for-EHT-rate.patch \
-    file://0027-mtk-mac80211-add-send-bar-action-when-recieve-addba-.patch \
-    file://0028-mtk-mac80211-inrease-beacon-loss-count.patch \
-    file://0029-mtk-cfg80211-add-support-for-updating-background-cha.patch \
-    file://0030-mtk-mac80211-Allow-STA-interface-to-set-TX-queue-par.patch \
-    file://0031-mtk-mac80211-export-ieee80211_tpt_led_trig_tx-rx-for.patch \
-    file://0032-mtk-mac80211-add-packet-count-input-for-dev_sw_netst.patch \
-    file://0033-mtk-mac80211-add-per-bss-flag-to-support-vendors-cou.patch \
-    file://0034-mtk-mac80211-set-eht_support-to-false-when-AP-is-not.patch \
-    file://0035-mtk-mac80211-Add-cert-mode-to-disable-ba-timeout.patch \
-    file://0036-backports-update-kernel-version-check-for-eth_hw_add.patch \
-    file://0037-mac80211-mtk-ACS-channel-time-is-reset-by-ch_restore.patch \
-    file://0038-mtk-mac80211-Fix-SMPS-action-frame-cap-check.patch \
-    file://0039-mtk-mac80211-allow-multiple-links-for-STA-vif.patch \
-    file://0040-mtk-mac80211-increase-association-timeout-time.patch \
-    file://0041-mtk-mac80211-fix-crash-when-starting-tx-ba-session.patch \
-    file://0042-mtk-mac80211-use-link-address-for-eapol-source-in-ie.patch \
-    file://0043-cfg80211-mtk-implement-DFS-radar-detect-for-MLO.patch \
-    file://0044-mtk-wifi-mac80211-add-wds-mlo-support.patch \
-    file://0045-mtk-mac80211-fix-ieee80211_probe_client-warning.patch \
-    file://0046-mtk-mac80211-remove-link-0-warn-on-in-rate_control_r.patch \
-    file://0047-mtk-mac80211-fix-radar-required-of-link-issue-in-res.patch \
-    file://0048-mtk-cfg80211-rework-cac-started-cac-start-time-for-m.patch \
-    file://0049-mtk-mac80211-remove-links-when-removing-AP_VLAN-inte.patch \
-    file://0050-mtk-mac80211-fix-AP-mgmt-not-encrypted-in-WDS-mode-w.patch \
-    file://0051-mtk-mac80211-fix-ieee80211_ht_cap_ie_to_sta_ht_cap-w.patch \
-    file://0052-mtk-mac80211-fix-mac-address-to-support-hw-path-in-s.patch \
-    file://0053-mtk-mac80211-workaround-for-configuring-txpower-in-m.patch \
-    file://0054-mtk-mac80211-send-broadcast-multicast-mgmt.-via-all-.patch \
-    file://0055-mtk-mac80211-fix-mlo-BW-160-channel-switch-issue.patch \
-    file://0056-mtk-mac80211-extend-IEEE80211_KEY_FLAG_GENERATE_MMIE.patch \
-    file://0057-mtk-wifi-mt76-mt7996-not-to-check-need_offchan-for-M.patch \
-    file://0058-mtk-wifi-mt76-mt7996-assign-link-address-to-the-head.patch \
-    file://0059-mtk-wifi-mt76-mt7996-Do-MLD-address-translation-befo.patch \
-    file://0060-mtk-wifi-mac80211-defer-enabling-beacon-for-MLD-AP.patch \
-    file://0061-mtk-wifi-mac80211-fix-radar-trigger-issue-due-to-ref.patch \
+    file://0001-backports-sync-openwrt-patches-build.patch \
+    file://0002-backports-sync-openwrt-patches-ath.patch \
+    file://0003-backports-sync-openwrt-patches-ath5k.patch \
+    file://0004-backports-sync-openwrt-patches-ath9k.patch \
+    file://0005-backports-sync-openwrt-patches-ath10k.patch \
+    file://0006-backports-sync-openwrt-patches-rt2x00.patch \
+    file://0007-backports-sync-openwrt-patches-subsys.patch \
+    file://0008-backports-additional-fixes-for-5.4.patch \
+    file://0009-bp-Revert-wifi-mac80211-move-radar-detect-work-to-sd.patch \
+    file://0010-bp-wifi-mac80211-remove-label-usage-in-ieee80211_sta.patch \
+    file://0011-bp-wifi-trace-unlink-rdev_end_cac-trace-event-from-w.patch \
+    file://0012-bp-wifi-cfg80211-move-DFS-related-members-to-links-i.patch \
+    file://0013-bp-wifi-cfg80211-handle-DFS-per-link.patch \
+    file://0014-bp-wifi-mac80211-handle-DFS-per-link.patch \
+    file://0015-bp-wifi-cfg80211-mac80211-use-proper-link-ID-for-DFS.patch \
+    file://0016-bp-wifi-mac80211-handle-ieee80211_radar_detected-for.patch \
+    file://0017-mtk-mac80211-do-not-setup-twt-when-twt-responder-is-.patch \
+    file://0018-mtk-cfg80211-extend-CAC-time-for-weather-radar-chann.patch \
+    file://0019-mtk-mac80211-it-s-invalid-case-when-frag_threshold-i.patch \
+    file://0020-mtk-cfg80211-implement-DFS-status-show-cac-and-nop-s.patch \
+    file://0021-mtk-mac80211-Set-TWT-Information-Frame-Disabled-bit-.patch \
+    file://0022-mtk-mac80211-check-the-control-channel-before-downgr.patch \
+    file://0023-mtk-mac80211-fix-tx-amsdu-aggregation.patch \
+    file://0024-mtk-mac80211-add-fill-receive-path-ops-to-get-wed-id.patch \
+    file://0025-mtk-mac80211-track-obss-color-bitmap.patch \
+    file://0026-mtk-mac80211-update-max_bssid_indicator-based-on-rea.patch \
+    file://0027-mtk-mac80211-support-configurable-addba-resp-time.patch \
+    file://0028-mtk-mac80211-add-sta-assisted-DFS-state-update-mecha.patch \
+    file://0029-mtk-nl80211-Mark-DFS-channel-as-available-for-CSA.patch \
+    file://0030-mtk-cfg80211-fix-early-return-in-cfg80211_stop_backg.patch \
+    file://0031-mtk-cfg80211-add-background-radar-stop-when-backgrou.patch \
+    file://0032-mtk-mac80211-avoid-kernel-warning-of-check_flush_dep.patch \
+    file://0033-mtk-mac80211-add-EHT-BA1024-support.patch \
+    file://0034-mtk-mac80211-add-rate-duration-for-EHT-rate.patch \
+    file://0035-mtk-mac80211-add-send-bar-action-when-recieve-addba-.patch \
+    file://0036-mtk-mac80211-inrease-beacon-loss-count.patch \
+    file://0037-mtk-cfg80211-add-support-for-updating-background-cha.patch \
+    file://0038-mtk-mac80211-Allow-STA-interface-to-set-TX-queue-par.patch \
+    file://0039-mtk-mac80211-export-ieee80211_tpt_led_trig_tx-rx-for.patch \
+    file://0040-mtk-mac80211-add-packet-count-input-for-dev_sw_netst.patch \
+    file://0041-mtk-mac80211-add-per-bss-flag-to-support-vendors-cou.patch \
+    file://0042-mtk-mac80211-set-eht_support-to-false-when-AP-is-not.patch \
+    file://0043-mtk-mac80211-Add-cert-mode-to-disable-ba-timeout.patch \
+    file://0044-mtk-mac80211-ACS-channel-time-is-reset-by-ch_restore.patch \
+    file://0045-mtk-mac80211-Fix-SMPS-action-frame-cap-check.patch \
+    file://0046-mtk-mac80211-allow-multiple-links-for-STA-vif.patch \
+    file://0047-mtk-mac80211-increase-association-timeout-time.patch \
+    file://0048-mtk-mac80211-use-link-address-for-eapol-source-in-ie.patch \
+    file://0049-mtk-cfg80211-implement-DFS-radar-detect-for-MLO.patch \
+    file://0050-mtk-mac80211-add-wds-mlo-support.patch \
+    file://0051-mtk-mac80211-fix-ieee80211_probe_client-warning.patch \
+    file://0052-mtk-mac80211-remove-link-0-warn-on-in-rate_control_r.patch \
+    file://0053-mtk-mac80211-fix-radar-required-of-link-issue-in-res.patch \
+    file://0054-mtk-cfg80211-rework-cac-started-cac-start-time-for-m.patch \
+    file://0055-mtk-mac80211-remove-links-when-removing-AP_VLAN-inte.patch \
+    file://0056-mtk-mac80211-fix-AP-mgmt-not-encrypted-in-WDS-mode-w.patch \
+    file://0057-mtk-mac80211-fix-ieee80211_ht_cap_ie_to_sta_ht_cap-w.patch \
+    file://0058-mtk-mac80211-fix-mac-address-to-support-hw-path-in-s.patch \
+    file://0059-mtk-mac80211-send-broadcast-multicast-mgmt.-via-all-.patch \
+    file://0060-mtk-mac80211-not-to-check-need_offchan-for-MLD-multi.patch \
+    file://0061-mtk-mac80211-assign-link-address-to-the-header-of-br.patch \
+    file://0062-mtk-mac80211-Do-MLD-address-translation-before-STA-p.patch \
+    file://0063-mtk-mac80211-defer-enabling-beacon-for-MLD-AP.patch \
+    file://0064-mtk-mac80211-prevent-STA-MLD-s-link-addr-from-being-.patch \
+    file://0065-mtk-mac80211-add-per-sta-prof-CSA-countdown-support.patch \
+    file://0066-mtk-mac80211-Add-support-for-EMLSR-support.patch \
+    file://0067-mtk-mac80211-set-max_amsdu_len-for-link_sta.patch \
+    file://0068-mtk-mac80211-legacy-AP-scan-request-should-contain-v.patch \
+    file://0069-mtk-mac80211-do-not-check-pre-CAC-allowed-for-scan.patch \
+    file://0070-mtk-mac80211-add-mlo-probe-client-support.patch \
+    file://0071-mtk-mac80211-rework-radar-notify-for-MLO.patch \
+    file://0072-mtk-mac80211-add-DFS-CAC-countdown-in-CSA-flow.patch \
+    file://0073-mtk-mac80211-add-mlo-related-debugfs-knob.patch \
+    file://0074-mtk-mac80211-Add-exported-function-for-SoftMAC-drive.patch \
+    file://0075-mtk-mac80211-add-per-link-txpower-config.patch \
+    file://0076-mtk-mac80211-add-link-information-when-dump-station.patch \
+    file://0077-mtk-mac80211-add-new-argument-link_id-in-set_bitrate.patch \
+    file://0078-mtk-mac80211-add-per-radio-antenna-config.patch \
+    file://0079-mtk-mac80211-add-new-rc-changed-enum-for-wifi7-cert.patch \
+    file://0080-mtk-mac80211-do-not-check-radar-required-for-remain-.patch \
+    file://0081-mtk-mac80211-Add-ba_disable-debugfs-to-disable-ba.patch \
+    file://0082-mtk-mac80211-fix-incorrect-VIF-assignment-for-IEEE-8.patch \
+    file://0083-mtk-mac80211-Add-STA-site-link-add-support.patch \
+    file://0084-mtk-mac80211-Fix-channel-switch-punct-bitmap-would-b.patch \
+    file://0085-mtk-mac80211-add-ieee80211_links_removed-to-support-.patch \
+    file://0086-mtk-mac80211-add-A-TTLM-support.patch \
+    file://0087-mtk-mac80211-add-SCS-capa-definition.patch \
+    file://0088-mtk-mac80211-add-puncture-bitmap-sync-for-extender.patch \
+    file://0089-mtk-mac80211-add-STA-A-TTLM-support.patch \
     "
diff --git a/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0024-mac80211-mtk-add-DFS-CAC-countdown-in-CSA-flow.patch b/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0024-mac80211-mtk-add-DFS-CAC-countdown-in-CSA-flow.patch
index beead44..eddb992 100644
--- a/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0024-mac80211-mtk-add-DFS-CAC-countdown-in-CSA-flow.patch
+++ b/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0024-mac80211-mtk-add-DFS-CAC-countdown-in-CSA-flow.patch
@@ -1,4 +1,4 @@
-From bb918e40dcc7d082f898234cf29cd545de78621e Mon Sep 17 00:00:00 2001
+From 70526aabf704d778796dfbaa042fe48e03aa7d61 Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Wed, 15 Nov 2023 15:05:17 +0800
 Subject: [PATCH] mac80211: mtk: add DFS CAC countdown in CSA flow
@@ -10,11 +10,11 @@
  net/mac80211/ieee80211_i.h |  2 +
  net/mac80211/iface.c       |  2 +
  net/mac80211/mlme.c        |  6 ++-
- net/mac80211/util.c        | 11 ++++-
+ net/mac80211/util.c        | 16 +++++++-
  net/wireless/chan.c        | 72 ++++++++++++++++++++++++++++++++
  net/wireless/nl80211.c     |  5 ++-
  net/wireless/rdev-ops.h    | 17 ++++++++
- 9 files changed, 221 insertions(+), 10 deletions(-)
+ 9 files changed, 226 insertions(+), 10 deletions(-)
 
 diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
 index 03f072f..a443b0d 100644
@@ -272,16 +272,21 @@
  				   NL80211_RADAR_CAC_FINISHED,
  				   GFP_KERNEL);
 diff --git a/net/mac80211/util.c b/net/mac80211/util.c
-index 26cd627..e07fe73 100644
+index 26cd627..1e8420d 100644
 --- a/net/mac80211/util.c
 +++ b/net/mac80211/util.c
-@@ -3873,7 +3873,16 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
+@@ -3873,7 +3873,21 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
  
  		if (sdata->wdev.cac_started) {
  			chandef = sdata->vif.bss_conf.chandef;
 -			ieee80211_vif_release_channel(sdata);
 +			if (sdata->vif.csa_active) {
 +				sdata->vif.csa_active = false;
++				if (sdata->csa_block_tx) {
++					ieee80211_wake_vif_queues(local, sdata,
++								  IEEE80211_QUEUE_STOP_REASON_CSA);
++					sdata->csa_block_tx = false;
++				}
 +				if (sdata->u.ap.next_beacon) {
 +					kfree(sdata->u.ap.next_beacon->mbssid_ies);
 +					kfree(sdata->u.ap.next_beacon);
@@ -294,7 +299,7 @@
  					   &chandef,
  					   NL80211_RADAR_CAC_ABORTED,
 diff --git a/net/wireless/chan.c b/net/wireless/chan.c
-index 9f651f9..f02598b 100644
+index f48995c..c7bfa6b 100644
 --- a/net/wireless/chan.c
 +++ b/net/wireless/chan.c
 @@ -1262,6 +1262,78 @@ bool cfg80211_reg_can_beacon_relax(struct wiphy *wiphy,
diff --git a/recipes-wifi/linux-mac80211/linux-mac80211_6.%.bb b/recipes-wifi/linux-mac80211/linux-mac80211_6.%.bb
index 479fc4d..c15df82 100644
--- a/recipes-wifi/linux-mac80211/linux-mac80211_6.%.bb
+++ b/recipes-wifi/linux-mac80211/linux-mac80211_6.%.bb
@@ -6,7 +6,7 @@
 
 inherit module
 
-PV = "2024-04-03"
+PV = "2024-07-11"
 
 SRC_URI = " \
     file://backports-${PV}.tar.xz \
diff --git a/recipes-wifi/linux-mt76/files/0118-fixup-wifi-mt76-mt7996-temp-support-for-single-wiphy.patch b/recipes-wifi/linux-mt76/files/0118-fixup-wifi-mt76-mt7996-temp-support-for-single-wiphy.patch
deleted file mode 100644
index 5179acf..0000000
--- a/recipes-wifi/linux-mt76/files/0118-fixup-wifi-mt76-mt7996-temp-support-for-single-wiphy.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From 74b58da2fc1d7f264300417939702f068d8c4f27 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Mon, 29 Apr 2024 15:59:44 +0800
-Subject: [PATCH] fixup! wifi: mt76: mt7996: temp support for single wiphy
-
-Change-Id: Idb4d417064826dedca6b64873d0d1a7dc6525c0b
----
- mt7996/main.c | 3 +--
- 1 file changed, 1 insertion(+), 2 deletions(-)
-
-diff --git a/mt7996/main.c b/mt7996/main.c
-index ea230921..57786413 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -466,8 +466,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
- 	/* TODO: temporaily set this to prevent some crashes */
- 	mvif->deflink.phy = phy;
- 
--	if (vif->type == NL80211_IFTYPE_STATION)
--		ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
-+	ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
- 	mutex_unlock(&dev->mt76.mutex);
- 
- 	return ret;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0001-mtk-Revert-wifi-mt76-mt7996-fill-txd-by-host-driver.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0001-mtk-Revert-wifi-mt76-mt7996-fill-txd-by-host-driver.patch
index 84e8b97..e4da7ad 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0001-mtk-Revert-wifi-mt76-mt7996-fill-txd-by-host-driver.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0001-mtk-Revert-wifi-mt76-mt7996-fill-txd-by-host-driver.patch
@@ -1,7 +1,7 @@
-From 803b79d47849d9f35f797f86efc63d57758a1dbd Mon Sep 17 00:00:00 2001
+From bb24b3476e4f4eef8a283159d62af7d9736dbcf5 Mon Sep 17 00:00:00 2001
 From: Shayne Chen <shayne.chen@mediatek.com>
 Date: Tue, 19 Sep 2023 11:21:23 +0800
-Subject: [PATCH 001/116] mtk: Revert "wifi: mt76: mt7996: fill txd by host
+Subject: [PATCH 001/199] mtk: Revert "wifi: mt76: mt7996: fill txd by host
  driver"
 
 This reverts commit 325a0c4931990d553487024c4f76c776492bdcc2.
@@ -10,7 +10,7 @@
  1 file changed, 9 insertions(+), 4 deletions(-)
 
 diff --git a/mt7996/mac.c b/mt7996/mac.c
-index bc7111a..3afdd7e 100644
+index bc7111a7..3afdd7eb 100644
 --- a/mt7996/mac.c
 +++ b/mt7996/mac.c
 @@ -938,8 +938,11 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0002-bp-sync-upstream-changes.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0002-bp-sync-upstream-changes.patch
deleted file mode 100644
index cc34f3c..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0002-bp-sync-upstream-changes.patch
+++ /dev/null
@@ -1,79 +0,0 @@
-From 33b7b4702d0666eb4e1535ea99bcf1f4316c2ab2 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 21 Mar 2024 12:14:20 +0800
-Subject: [PATCH 002/116] bp: sync upstream changes
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mac80211.c   | 6 +++---
- mt7615/mcu.c | 2 +-
- mt7915/mcu.c | 2 +-
- mt7996/mcu.c | 2 +-
- 4 files changed, 6 insertions(+), 6 deletions(-)
-
-diff --git a/mac80211.c b/mac80211.c
-index 4871355..e4675bf 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -1614,8 +1614,8 @@ EXPORT_SYMBOL_GPL(mt76_get_sar_power);
- static void
- __mt76_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
- {
--	if (vif->bss_conf.csa_active && ieee80211_beacon_cntdwn_is_complete(vif))
--		ieee80211_csa_finish(vif);
-+	if (vif->bss_conf.csa_active && ieee80211_beacon_cntdwn_is_complete(vif, 0))
-+		ieee80211_csa_finish(vif, 0);
- }
- 
- void mt76_csa_finish(struct mt76_dev *dev)
-@@ -1639,7 +1639,7 @@ __mt76_csa_check(void *priv, u8 *mac, struct ieee80211_vif *vif)
- 	if (!vif->bss_conf.csa_active)
- 		return;
- 
--	dev->csa_complete |= ieee80211_beacon_cntdwn_is_complete(vif);
-+	dev->csa_complete |= ieee80211_beacon_cntdwn_is_complete(vif, 0);
- }
- 
- void mt76_csa_check(struct mt76_dev *dev)
-diff --git a/mt7615/mcu.c b/mt7615/mcu.c
-index ae34d01..c807bd8 100644
---- a/mt7615/mcu.c
-+++ b/mt7615/mcu.c
-@@ -353,7 +353,7 @@ static void
- mt7615_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
- {
- 	if (vif->bss_conf.csa_active)
--		ieee80211_csa_finish(vif);
-+		ieee80211_csa_finish(vif, 0);
- }
- 
- static void
-diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 29e9d66..fc194e0 100644
---- a/mt7915/mcu.c
-+++ b/mt7915/mcu.c
-@@ -228,7 +228,7 @@ mt7915_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
- 	if (!vif->bss_conf.csa_active || vif->type == NL80211_IFTYPE_STATION)
- 		return;
- 
--	ieee80211_csa_finish(vif);
-+	ieee80211_csa_finish(vif, 0);
- }
- 
- static void
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 4588696..4bd74e1 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -341,7 +341,7 @@ mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
- 	if (!vif->bss_conf.csa_active || vif->type == NL80211_IFTYPE_STATION)
- 		return;
- 
--	ieee80211_csa_finish(vif);
-+	ieee80211_csa_finish(vif, 0);
- }
- 
- static void
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0002-mtk-mt76-mt7996-use-hweight16-to-get-correct-tx_ant.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0002-mtk-mt76-mt7996-use-hweight16-to-get-correct-tx_ant.patch
new file mode 100644
index 0000000..af32ef5
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0002-mtk-mt76-mt7996-use-hweight16-to-get-correct-tx_ant.patch
@@ -0,0 +1,33 @@
+From 30225c66317a43f8e881300f2e431d8b6bf54985 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Wed, 10 Apr 2024 14:05:12 +0800
+Subject: [PATCH 002/199] mtk: mt76: mt7996: use hweight16 to get correct
+ tx_ant
+
+The chainmask is u16 so using hweight8 cannot get correct tx_ant.
+
+Without this patch, the tx_ant of band 2 would be -1 and lead to
+the following issue:
+BUG: KASAN: stack-out-of-bounds in mt7996_mcu_add_sta+0x12e0/0x16e0 [mt7996e]
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/mcu.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 3590e43f..73c3cc2d 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1653,7 +1653,7 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ {
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_phy *phy = mvif->phy;
+-	int tx_ant = hweight8(phy->mt76->chainmask) - 1;
++	int tx_ant = hweight16(phy->mt76->chainmask) - 1;
+ 	struct sta_rec_bf *bf;
+ 	struct tlv *tlv;
+ 	static const u8 matrix[4][4] = {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0003-mtk-mt76-mt7996-fix-MBSS.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0003-mtk-mt76-mt7996-fix-MBSS.patch
new file mode 100644
index 0000000..6042e11
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0003-mtk-mt76-mt7996-fix-MBSS.patch
@@ -0,0 +1,31 @@
+From d62e705d71d755dd420564e09f1686f908fd9c98 Mon Sep 17 00:00:00 2001
+From: Rex Lu <rex.lu@mediatek.com>
+Date: Thu, 18 Apr 2024 14:19:21 +0800
+Subject: [PATCH 003/199] mtk: mt76: mt7996: fix MBSS
+
+Refactor 11v mbss unicmd flow
+case1(disable->enable) : when we enable 11v MBSS, we have to add 11v mbss tlv(UNI_BSS_INFO_11V_MBSSID)
+case2(enable->disable) : when we diable 11v MBSS, we should clear 11v mbss tlv (UNI_BSS_INFO_11V_MBSSID-> all value to zero) first,
+otherwise it will cause PSE opration ERR and trigger L1SER. After clear 11v mbss tlv,we have to reset UNI_BSS_INFO_BASIC(from 11v MBSS mode to legacy mode)
+
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+---
+ mt7996/mcu.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 73c3cc2d..6d5bbe5b 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -822,7 +822,7 @@ mt7996_mcu_bss_mbssid_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ 	struct bss_info_uni_mbssid *mbssid;
+ 	struct tlv *tlv;
+ 
+-	if (!vif->bss_conf.bssid_indicator)
++	if (!vif->bss_conf.bssid_indicator && enable)
+ 		return;
+ 
+ 	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_11V_MBSSID, sizeof(*mbssid));
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0003-wifi-mt76-mt7996-use-hweight16-to-get-correct-tx_ant.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0003-wifi-mt76-mt7996-use-hweight16-to-get-correct-tx_ant.patch
deleted file mode 100644
index e1fdf6b..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0003-wifi-mt76-mt7996-use-hweight16-to-get-correct-tx_ant.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From e9039f548952da4aa2f91d125da157974d0c0fa7 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Wed, 10 Apr 2024 14:05:12 +0800
-Subject: [PATCH 003/116] wifi: mt76: mt7996: use hweight16 to get correct
- tx_ant
-
-The chainmask is u16 so using hweight8 cannot get correct tx_ant.
-
-Without this patch, the tx_ant of band 2 would be -1 and lead to
-the following issue:
-BUG: KASAN: stack-out-of-bounds in mt7996_mcu_add_sta+0x12e0/0x16e0 [mt7996e]
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- mt7996/mcu.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 4bd74e1..239b64a 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -1653,7 +1653,7 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- {
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_phy *phy = mvif->phy;
--	int tx_ant = hweight8(phy->mt76->chainmask) - 1;
-+	int tx_ant = hweight16(phy->mt76->chainmask) - 1;
- 	struct sta_rec_bf *bf;
- 	struct tlv *tlv;
- 	static const u8 matrix[4][4] = {
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0004-mtk-mt76-mt7996-fix-HE-and-EHT-phy-cap.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0004-mtk-mt76-mt7996-fix-HE-and-EHT-phy-cap.patch
new file mode 100644
index 0000000..e29be50
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0004-mtk-mt76-mt7996-fix-HE-and-EHT-phy-cap.patch
@@ -0,0 +1,138 @@
+From 184f146afaddb73c3fd0afc60338f5ed73d5b450 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Tue, 12 Mar 2024 09:07:52 +0800
+Subject: [PATCH 004/199] mtk: mt76: mt7996: fix HE and EHT phy cap
+
+---
+ mt7996/init.c | 65 ++++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 43 insertions(+), 22 deletions(-)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 283df84f..a98dcb40 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -1011,8 +1011,6 @@ mt7996_set_stream_he_txbf_caps(struct mt7996_phy *phy,
+ 		return;
+ 
+ 	elem->phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER;
+-	if (vif == NL80211_IFTYPE_AP)
+-		elem->phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER;
+ 
+ 	c = FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK,
+ 		       sts - 1) |
+@@ -1020,6 +1018,11 @@ mt7996_set_stream_he_txbf_caps(struct mt7996_phy *phy,
+ 		       sts - 1);
+ 	elem->phy_cap_info[5] |= c;
+ 
++	if (vif != NL80211_IFTYPE_AP)
++		return;
++
++	elem->phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER;
++
+ 	c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB |
+ 	    IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB;
+ 	elem->phy_cap_info[6] |= c;
+@@ -1179,7 +1182,6 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
+ 		IEEE80211_EHT_MAC_CAP0_OM_CONTROL;
+ 
+ 	eht_cap_elem->phy_cap_info[0] =
+-		IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ |
+ 		IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI |
+ 		IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER |
+ 		IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE;
+@@ -1193,30 +1195,36 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
+ 		u8_encode_bits(u8_get_bits(val, GENMASK(2, 1)),
+ 			       IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK) |
+ 		u8_encode_bits(val,
+-			       IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK) |
+-		u8_encode_bits(val,
+-			       IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK);
++			       IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK);
+ 
+ 	eht_cap_elem->phy_cap_info[2] =
+ 		u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK) |
+-		u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK) |
+-		u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK);
++		u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK);
++
++	if (band == NL80211_BAND_6GHZ) {
++		eht_cap_elem->phy_cap_info[0] |=
++			IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ;
++
++		eht_cap_elem->phy_cap_info[1] |=
++			u8_encode_bits(val,
++				       IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK);
++
++		eht_cap_elem->phy_cap_info[2] |=
++			u8_encode_bits(sts - 1,
++				       IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK);
++	}
+ 
+ 	eht_cap_elem->phy_cap_info[3] =
+ 		IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK |
+ 		IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK |
+ 		IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK |
+-		IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK |
+-		IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK |
+-		IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK |
+-		IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK;
++		IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK;
+ 
+ 	eht_cap_elem->phy_cap_info[4] =
+ 		u8_encode_bits(min_t(int, sts - 1, 2),
+ 			       IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK);
+ 
+ 	eht_cap_elem->phy_cap_info[5] =
+-		IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK |
+ 		u8_encode_bits(IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_16US,
+ 			       IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK) |
+ 		u8_encode_bits(u8_get_bits(0x11, GENMASK(1, 0)),
+@@ -1230,14 +1238,6 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
+ 			       IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK) |
+ 		u8_encode_bits(val, IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK);
+ 
+-	eht_cap_elem->phy_cap_info[7] =
+-		IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ |
+-		IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ |
+-		IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_320MHZ |
+-		IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ |
+-		IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ |
+-		IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ;
+-
+ 	val = u8_encode_bits(nss, IEEE80211_EHT_MCS_NSS_RX) |
+ 	      u8_encode_bits(nss, IEEE80211_EHT_MCS_NSS_TX);
+ #define SET_EHT_MAX_NSS(_bw, _val) do {				\
+@@ -1248,8 +1248,29 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
+ 
+ 	SET_EHT_MAX_NSS(80, val);
+ 	SET_EHT_MAX_NSS(160, val);
+-	SET_EHT_MAX_NSS(320, val);
++	if (band == NL80211_BAND_6GHZ)
++		SET_EHT_MAX_NSS(320, val);
+ #undef SET_EHT_MAX_NSS
++
++	if (iftype != NL80211_IFTYPE_AP)
++		return;
++
++	eht_cap_elem->phy_cap_info[3] |=
++		IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK |
++		IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK;
++
++	eht_cap_elem->phy_cap_info[7] =
++		IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ |
++		IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ |
++		IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ |
++		IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ;
++
++	if (band != NL80211_BAND_6GHZ)
++		return;
++
++	eht_cap_elem->phy_cap_info[7] |=
++		IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_320MHZ |
++		IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ;
+ }
+ 
+ static void
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0004-mtk-wifi-mt76-mt7996-fix-MBSS.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0004-mtk-wifi-mt76-mt7996-fix-MBSS.patch
deleted file mode 100644
index 9684e8b..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0004-mtk-wifi-mt76-mt7996-fix-MBSS.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From 3fc5e746e7fb33b2d2206f5fd3c5e9cd65b302f3 Mon Sep 17 00:00:00 2001
-From: Rex Lu <rex.lu@mediatek.com>
-Date: Thu, 18 Apr 2024 14:19:21 +0800
-Subject: [PATCH 004/116] mtk: wifi: mt76: mt7996: fix MBSS
-
-Refactor 11v mbss unicmd flow
-case1(disable->enable) : when we enable 11v MBSS, we have to add 11v mbss tlv(UNI_BSS_INFO_11V_MBSSID)
-case2(enable->disable) : when we diable 11v MBSS, we should clear 11v mbss tlv (UNI_BSS_INFO_11V_MBSSID-> all value to zero) first,
-otherwise it will cause PSE opration ERR and trigger L1SER. After clear 11v mbss tlv,we have to reset UNI_BSS_INFO_BASIC(from 11v MBSS mode to legacy mode)
-
-Signed-off-by: Rex Lu <rex.lu@mediatek.com>
----
- mt7996/mcu.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 239b64a..ceff487 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -822,7 +822,7 @@ mt7996_mcu_bss_mbssid_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
- 	struct bss_info_uni_mbssid *mbssid;
- 	struct tlv *tlv;
- 
--	if (!vif->bss_conf.bssid_indicator)
-+	if (!vif->bss_conf.bssid_indicator && enable)
- 		return;
- 
- 	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_11V_MBSSID, sizeof(*mbssid));
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0005-mtk-mt76-mt7996-adjust-Beamformee-SS-capability.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0005-mtk-mt76-mt7996-adjust-Beamformee-SS-capability.patch
new file mode 100644
index 0000000..9d4cf83
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0005-mtk-mt76-mt7996-adjust-Beamformee-SS-capability.patch
@@ -0,0 +1,66 @@
+From 070859af14a0c57a521e9efc5d4ae275666679d9 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Tue, 27 Feb 2024 14:50:20 +0800
+Subject: [PATCH 005/199] mtk: mt76: mt7996: adjust Beamformee SS capability
+
+This commit includes two changes to adjust beamformee ss capability.
+First, configure the beamformee ss capability for mt7992 chipsets.
+Second, no matter how many antenna numbers is set, always set the
+maximum capability of Beamformee SS that chipsets support.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt7996/init.c | 23 +++++++++++++++++------
+ 1 file changed, 17 insertions(+), 6 deletions(-)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index a98dcb40..7d8d1e7b 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -941,8 +941,12 @@ void mt7996_set_stream_vht_txbf_caps(struct mt7996_phy *phy)
+ 	cap = &phy->mt76->sband_5g.sband.vht_cap.cap;
+ 
+ 	*cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+-		IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
+-		FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, sts - 1);
++		IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
++
++	if (is_mt7996(phy->mt76->dev))
++		*cap |= FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, 3);
++	else
++		*cap |= FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, 4);
+ 
+ 	*cap &= ~(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK |
+ 		  IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
+@@ -987,9 +991,15 @@ mt7996_set_stream_he_txbf_caps(struct mt7996_phy *phy,
+ 	    IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO;
+ 	elem->phy_cap_info[2] |= c;
+ 
+-	c = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE |
+-	    IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 |
+-	    IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4;
++	c = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE;
++
++	if (is_mt7996(phy->mt76->dev))
++		c |= IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 |
++		     IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4;
++	else
++		c |= IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_5 |
++		     IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_5;
++
+ 	elem->phy_cap_info[4] |= c;
+ 
+ 	/* do not support NG16 due to spec D4.0 changes subcarrier idx */
+@@ -1186,7 +1196,8 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
+ 		IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER |
+ 		IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE;
+ 
+-	val = max_t(u8, sts - 1, 3);
++	/* Set the maximum capability regardless of the antenna configuration. */
++	val = is_mt7992(phy->mt76->dev) ? 4 : 3;
+ 	eht_cap_elem->phy_cap_info[0] |=
+ 		u8_encode_bits(u8_get_bits(val, BIT(0)),
+ 			       IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0005-wifi-mt76-mt7996-fix-HE-and-EHT-phy-cap.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0005-wifi-mt76-mt7996-fix-HE-and-EHT-phy-cap.patch
deleted file mode 100644
index 371bdf0..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0005-wifi-mt76-mt7996-fix-HE-and-EHT-phy-cap.patch
+++ /dev/null
@@ -1,145 +0,0 @@
-From 0a8c7ea3be6987390e2567a63659fec93aa3269c Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Tue, 12 Mar 2024 09:07:52 +0800
-Subject: [PATCH 005/116] wifi: mt76: mt7996: fix HE and EHT phy cap
-
-This commit fix he and eht phy capabailties ie. For HE phy cap, fix
-correct beamform capabilities for station vif. For EHT phy cap, remove
-unsupported capabilities.
-
-Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi7 (802.11be) devices")
-Fixes: 348533eb968d ("wifi: mt76: mt7996: add EHT capability init")
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- mt7996/init.c | 65 ++++++++++++++++++++++++++++++++++-----------------
- 1 file changed, 43 insertions(+), 22 deletions(-)
-
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 9aa97e4..c264d50 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -1010,8 +1010,6 @@ mt7996_set_stream_he_txbf_caps(struct mt7996_phy *phy,
- 		return;
- 
- 	elem->phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER;
--	if (vif == NL80211_IFTYPE_AP)
--		elem->phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER;
- 
- 	c = FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK,
- 		       sts - 1) |
-@@ -1019,6 +1017,11 @@ mt7996_set_stream_he_txbf_caps(struct mt7996_phy *phy,
- 		       sts - 1);
- 	elem->phy_cap_info[5] |= c;
- 
-+	if (vif != NL80211_IFTYPE_AP)
-+		return;
-+
-+	elem->phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER;
-+
- 	c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB |
- 	    IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB;
- 	elem->phy_cap_info[6] |= c;
-@@ -1178,7 +1181,6 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
- 		IEEE80211_EHT_MAC_CAP0_OM_CONTROL;
- 
- 	eht_cap_elem->phy_cap_info[0] =
--		IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ |
- 		IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI |
- 		IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER |
- 		IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE;
-@@ -1192,30 +1194,36 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
- 		u8_encode_bits(u8_get_bits(val, GENMASK(2, 1)),
- 			       IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK) |
- 		u8_encode_bits(val,
--			       IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK) |
--		u8_encode_bits(val,
--			       IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK);
-+			       IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK);
- 
- 	eht_cap_elem->phy_cap_info[2] =
- 		u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK) |
--		u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK) |
--		u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK);
-+		u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK);
-+
-+	if (band == NL80211_BAND_6GHZ) {
-+		eht_cap_elem->phy_cap_info[0] |=
-+			IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ;
-+
-+		eht_cap_elem->phy_cap_info[1] |=
-+			u8_encode_bits(val,
-+				       IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK);
-+
-+		eht_cap_elem->phy_cap_info[2] |=
-+			u8_encode_bits(sts - 1,
-+				       IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK);
-+	}
- 
- 	eht_cap_elem->phy_cap_info[3] =
- 		IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK |
- 		IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK |
- 		IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK |
--		IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK |
--		IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK |
--		IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK |
--		IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK;
-+		IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK;
- 
- 	eht_cap_elem->phy_cap_info[4] =
- 		u8_encode_bits(min_t(int, sts - 1, 2),
- 			       IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK);
- 
- 	eht_cap_elem->phy_cap_info[5] =
--		IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK |
- 		u8_encode_bits(IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_16US,
- 			       IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK) |
- 		u8_encode_bits(u8_get_bits(0x11, GENMASK(1, 0)),
-@@ -1229,14 +1237,6 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
- 			       IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK) |
- 		u8_encode_bits(val, IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK);
- 
--	eht_cap_elem->phy_cap_info[7] =
--		IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ |
--		IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ |
--		IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_320MHZ |
--		IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ |
--		IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ |
--		IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ;
--
- 	val = u8_encode_bits(nss, IEEE80211_EHT_MCS_NSS_RX) |
- 	      u8_encode_bits(nss, IEEE80211_EHT_MCS_NSS_TX);
- #define SET_EHT_MAX_NSS(_bw, _val) do {				\
-@@ -1247,8 +1247,29 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
- 
- 	SET_EHT_MAX_NSS(80, val);
- 	SET_EHT_MAX_NSS(160, val);
--	SET_EHT_MAX_NSS(320, val);
-+	if (band == NL80211_BAND_6GHZ)
-+		SET_EHT_MAX_NSS(320, val);
- #undef SET_EHT_MAX_NSS
-+
-+	if (iftype != NL80211_IFTYPE_AP)
-+		return;
-+
-+	eht_cap_elem->phy_cap_info[3] |=
-+		IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK |
-+		IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK;
-+
-+	eht_cap_elem->phy_cap_info[7] =
-+		IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ |
-+		IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ |
-+		IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ |
-+		IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ;
-+
-+	if (band != NL80211_BAND_6GHZ)
-+		return;
-+
-+	eht_cap_elem->phy_cap_info[7] |=
-+		IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_320MHZ |
-+		IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ;
- }
- 
- static void
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0006-mtk-mt76-mt7996-add-support-for-IEEE-802.11-fragment.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0006-mtk-mt76-mt7996-add-support-for-IEEE-802.11-fragment.patch
new file mode 100644
index 0000000..d9f38b2
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0006-mtk-mt76-mt7996-add-support-for-IEEE-802.11-fragment.patch
@@ -0,0 +1,73 @@
+From f875fc2725a26aee415ffde4ba0822f69403bdf2 Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Fri, 12 Jul 2024 16:05:50 +0800
+Subject: [PATCH 006/199] mtk: mt76: mt7996: add support for IEEE 802.11
+ fragmentation
+
+Add fragment index into TXD.DW2 to support IEEE 802.11 fragmentation.
+
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ mt76_connac3_mac.h |  7 +++++++
+ mt7996/mac.c       | 13 +++++++++++--
+ 2 files changed, 18 insertions(+), 2 deletions(-)
+
+diff --git a/mt76_connac3_mac.h b/mt76_connac3_mac.h
+index 353e6606..3fc94bd7 100644
+--- a/mt76_connac3_mac.h
++++ b/mt76_connac3_mac.h
+@@ -197,6 +197,13 @@ enum tx_mgnt_type {
+ 	MT_TX_ADDBA,
+ };
+ 
++enum tx_frag_idx {
++	MT_TX_FRAG_NONE,
++	MT_TX_FRAG_FIRST,
++	MT_TX_FRAG_MID,
++	MT_TX_FRAG_LAST
++};
++
+ #define MT_CT_INFO_APPLY_TXD		BIT(0)
+ #define MT_CT_INFO_COPY_HOST_TXD_ALL	BIT(1)
+ #define MT_CT_INFO_MGMT_FRAME		BIT(2)
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 3afdd7eb..b3f9591f 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -746,7 +746,7 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
+ 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ 	bool multicast = is_multicast_ether_addr(hdr->addr1);
+ 	u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
+-	__le16 fc = hdr->frame_control;
++	__le16 fc = hdr->frame_control, sc = hdr->seq_ctrl;
+ 	u8 fc_type, fc_stype;
+ 	u32 val;
+ 
+@@ -780,6 +780,15 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
+ 	val = FIELD_PREP(MT_TXD2_FRAME_TYPE, fc_type) |
+ 	      FIELD_PREP(MT_TXD2_SUB_TYPE, fc_stype);
+ 
++	if (ieee80211_has_morefrags(fc) && ieee80211_is_first_frag(sc))
++		val |= FIELD_PREP(MT_TXD2_FRAG, MT_TX_FRAG_FIRST);
++	else if (ieee80211_has_morefrags(fc) && !ieee80211_is_first_frag(sc))
++		val |= FIELD_PREP(MT_TXD2_FRAG, MT_TX_FRAG_MID);
++	else if (!ieee80211_has_morefrags(fc) && !ieee80211_is_first_frag(sc))
++		val |= FIELD_PREP(MT_TXD2_FRAG, MT_TX_FRAG_LAST);
++	else
++		val |= FIELD_PREP(MT_TXD2_FRAG, MT_TX_FRAG_NONE);
++
+ 	txwi[2] |= cpu_to_le32(val);
+ 
+ 	txwi[3] |= cpu_to_le32(FIELD_PREP(MT_TXD3_BCM, multicast));
+@@ -789,7 +798,7 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
+ 	}
+ 
+ 	if (info->flags & IEEE80211_TX_CTL_INJECTED) {
+-		u16 seqno = le16_to_cpu(hdr->seq_ctrl);
++		u16 seqno = le16_to_cpu(sc);
+ 
+ 		if (ieee80211_is_back_req(hdr->frame_control)) {
+ 			struct ieee80211_bar *bar;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0006-mtk-wifi-mt76-mt7996-adjust-Beamformee-SS-capability.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0006-mtk-wifi-mt76-mt7996-adjust-Beamformee-SS-capability.patch
deleted file mode 100644
index 9b56c52..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0006-mtk-wifi-mt76-mt7996-adjust-Beamformee-SS-capability.patch
+++ /dev/null
@@ -1,67 +0,0 @@
-From 346eb89054af1f40dcceec43d128d8775081fd9a Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Tue, 27 Feb 2024 14:50:20 +0800
-Subject: [PATCH 006/116] mtk: wifi: mt76: mt7996: adjust Beamformee SS
- capability
-
-This commit includes two changes to adjust beamformee ss capability.
-First, configure the beamformee ss capability for mt7992 chipsets.
-Second, no matter how many antenna numbers is set, always set the
-maximum capability of Beamformee SS that chipsets support.
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- mt7996/init.c | 23 +++++++++++++++++------
- 1 file changed, 17 insertions(+), 6 deletions(-)
-
-diff --git a/mt7996/init.c b/mt7996/init.c
-index c264d50..ab9445c 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -940,8 +940,12 @@ void mt7996_set_stream_vht_txbf_caps(struct mt7996_phy *phy)
- 	cap = &phy->mt76->sband_5g.sband.vht_cap.cap;
- 
- 	*cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
--		IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
--		FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, sts - 1);
-+		IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
-+
-+	if (is_mt7996(phy->mt76->dev))
-+		*cap |= FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, 3);
-+	else
-+		*cap |= FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, 4);
- 
- 	*cap &= ~(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK |
- 		  IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
-@@ -986,9 +990,15 @@ mt7996_set_stream_he_txbf_caps(struct mt7996_phy *phy,
- 	    IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO;
- 	elem->phy_cap_info[2] |= c;
- 
--	c = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE |
--	    IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 |
--	    IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4;
-+	c = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE;
-+
-+	if (is_mt7996(phy->mt76->dev))
-+		c |= IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 |
-+		     IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4;
-+	else
-+		c |= IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_5 |
-+		     IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_5;
-+
- 	elem->phy_cap_info[4] |= c;
- 
- 	/* do not support NG16 due to spec D4.0 changes subcarrier idx */
-@@ -1185,7 +1195,8 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
- 		IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER |
- 		IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE;
- 
--	val = max_t(u8, sts - 1, 3);
-+	/* Set the maximum capability regardless of the antenna configuration. */
-+	val = is_mt7992(phy->mt76->dev) ? 4 : 3;
- 	eht_cap_elem->phy_cap_info[0] |=
- 		u8_encode_bits(u8_get_bits(val, BIT(0)),
- 			       IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0007-mtk-mt76-mt7996-set-rx-path-when-channel-switch.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0007-mtk-mt76-mt7996-set-rx-path-when-channel-switch.patch
new file mode 100644
index 0000000..3e1400f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0007-mtk-mt76-mt7996-set-rx-path-when-channel-switch.patch
@@ -0,0 +1,33 @@
+From a76084f12577a97b1b06db539a2e928845e5a9ff Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Fri, 26 Jul 2024 15:55:01 +0800
+Subject: [PATCH 007/199] mtk: mt76: mt7996: set rx path when channel switch
+
+When scanning, driver need to send this tag to fw to notify scanning is
+start or stop. FW would stop mac tx when scanning is started and resume
+mac tx when scanning is done. Without this tag, hw needs more time to
+resume traffic when scanning is done.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/main.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 7c97140d..15d880ef 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -307,6 +307,10 @@ int mt7996_set_channel(struct mt7996_phy *phy)
+ 	if (ret)
+ 		goto out;
+ 
++	ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_RX_PATH);
++	if (ret)
++		goto out;
++
+ 	ret = mt7996_dfs_init_radar_detector(phy);
+ 	mt7996_mac_cca_stats_reset(phy);
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0007-wifi-mt76-mt7992-adjust-beamform-mcu-cmd-configurati.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0007-wifi-mt76-mt7992-adjust-beamform-mcu-cmd-configurati.patch
deleted file mode 100644
index 6608130..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0007-wifi-mt76-mt7992-adjust-beamform-mcu-cmd-configurati.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-From e4f7c4696b0701162b5bef4bf699b13313f9c9f5 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Mon, 18 Mar 2024 11:13:56 +0800
-Subject: [PATCH 007/116] wifi: mt76: mt7992: adjust beamform mcu cmd
- configuration for mt7992
-
-Adjust the correct beamform mcu cmd configuration for mt7992 chipsets.
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- mt7996/mcu.c | 5 +++--
- 1 file changed, 3 insertions(+), 2 deletions(-)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index ceff487..0e5ddf6 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -3920,8 +3920,9 @@ int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action)
- 
- 		tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_mod_en));
- 		req_mod_en = (struct bf_mod_en_ctrl *)tlv;
--		req_mod_en->bf_num = 3;
--		req_mod_en->bf_bitmap = GENMASK(2, 0);
-+		req_mod_en->bf_num = mt7996_band_valid(dev, MT_BAND2) ? 3 : 2;
-+		req_mod_en->bf_bitmap = mt7996_band_valid(dev, MT_BAND2) ?
-+					GENMASK(2, 0) : GENMASK(1, 0);
- 		break;
- 	}
- 	default:
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0008-mtk-mt76-mt7996-set-station-s-wmm-index-to-3.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0008-mtk-mt76-mt7996-set-station-s-wmm-index-to-3.patch
new file mode 100644
index 0000000..81739b6
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0008-mtk-mt76-mt7996-set-station-s-wmm-index-to-3.patch
@@ -0,0 +1,29 @@
+From ce5a75d5fb6414c0ccc0c461f0ff2bb09501275e Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Fri, 19 Jul 2024 11:50:21 +0800
+Subject: [PATCH 008/199] mtk: mt76: mt7996: set station's wmm index to 3
+
+According to HW design, the AP's WMM index is 0 and the station's wmm
+index is 3.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/main.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 15d880ef..2b094b33 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -206,7 +206,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ 	mvif->mt76.omac_idx = idx;
+ 	mvif->phy = phy;
+ 	mvif->mt76.band_idx = band_idx;
+-	mvif->mt76.wmm_idx = vif->type != NL80211_IFTYPE_AP;
++	mvif->mt76.wmm_idx = vif->type == NL80211_IFTYPE_AP ? 0 : 3;
+ 
+ 	ret = mt7996_mcu_add_dev_info(phy, vif, true);
+ 	if (ret)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0008-mtk-wifi-mt76-mt7996-add-preamble-puncture-support-f.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0008-mtk-wifi-mt76-mt7996-add-preamble-puncture-support-f.patch
deleted file mode 100644
index 8a29fda..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0008-mtk-wifi-mt76-mt7996-add-preamble-puncture-support-f.patch
+++ /dev/null
@@ -1,154 +0,0 @@
-From f8092ef14eb9b03588ea29ac1b5a3a023f34c705 Mon Sep 17 00:00:00 2001
-From: Allen Ye <allen.ye@mediatek.com>
-Date: Thu, 18 Apr 2024 11:16:24 +0800
-Subject: [PATCH 008/116] mtk: wifi: mt76: mt7996: add preamble puncture
- support for mt7996
-
-Add support configure preamble puncture feature through mcu commands.
-
-Co-developed-by: Howard Hsu <howard-yh.hsu@mediatek.com>
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
-Signed-off-by: Allen Ye <allen.ye@mediatek.com>
----
- mt76_connac_mcu.h |  1 +
- mt7996/init.c     |  1 +
- mt7996/main.c     |  5 +++++
- mt7996/mcu.c      | 40 ++++++++++++++++++++++++++++++++++++++++
- mt7996/mcu.h      | 10 ++++++++++
- mt7996/mt7996.h   |  4 ++++
- 6 files changed, 61 insertions(+)
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 915eb3a..1e187a8 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1273,6 +1273,7 @@ enum {
- 	MCU_UNI_CMD_CHANNEL_SWITCH = 0x34,
- 	MCU_UNI_CMD_THERMAL = 0x35,
- 	MCU_UNI_CMD_VOW = 0x37,
-+	MCU_UNI_CMD_PP = 0x38,
- 	MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
- 	MCU_UNI_CMD_RRO = 0x57,
- 	MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
-diff --git a/mt7996/init.c b/mt7996/init.c
-index ab9445c..afe8a0a 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -387,6 +387,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
- 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT);
- 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
- 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER);
-+	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_PUNCT);
- 
- 	if (!mdev->dev->of_node ||
- 	    !of_property_read_bool(mdev->dev->of_node,
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 40ccfdc..1791335 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -411,6 +411,11 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
- 	int ret;
- 
- 	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
-+		ret = mt7996_mcu_set_pp_en(phy, PP_USR_MODE,
-+					   phy->mt76->chandef.punctured);
-+		if (ret)
-+			return ret;
-+
- 		ieee80211_stop_queues(hw);
- 		ret = mt7996_set_channel(phy);
- 		if (ret)
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 0e5ddf6..1f99b2d 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -4550,3 +4550,43 @@ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode)
- 	return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(CP_SUPPORT),
- 				 &cp_mode, sizeof(cp_mode), true);
- }
-+
-+int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	bool pp_auto = (mode == PP_FW_MODE);
-+	struct {
-+		u8 _rsv1[4];
-+
-+		__le16 tag;
-+		__le16 len;
-+		u8 mgmt_mode;
-+		u8 band_idx;
-+		u8 force_bitmap_ctrl;
-+		u8 auto_mode;
-+		__le16 bitmap;
-+		u8 _rsv2[2];
-+	} __packed req = {
-+		.tag = cpu_to_le16(UNI_CMD_PP_EN_CTRL),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+
-+		.mgmt_mode = !pp_auto,
-+		.band_idx = phy->mt76->band_idx,
-+		.force_bitmap_ctrl = (mode == PP_USR_MODE) ? 2 : 0,
-+		.auto_mode = pp_auto,
-+		.bitmap = cpu_to_le16(bitmap),
-+	};
-+
-+	if (phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ ||
-+	    mode > PP_USR_MODE)
-+		return 0;
-+
-+	if (bitmap && phy->punct_bitmap == bitmap)
-+		return 0;
-+
-+	phy->punct_bitmap = bitmap;
-+	phy->pp_mode = mode;
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(PP),
-+				 &req, sizeof(req), false);
-+}
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index a9ba63d..2052555 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -923,6 +923,16 @@ enum {
- 	MT7996_SEC_MODE_MAX,
- };
- 
-+enum {
-+	UNI_CMD_PP_EN_CTRL,
-+};
-+
-+enum pp_mode {
-+	PP_DISABLE = 0,
-+	PP_FW_MODE,
-+	PP_USR_MODE,
-+};
-+
- #define MT7996_PATCH_SEC		GENMASK(31, 24)
- #define MT7996_PATCH_SCRAMBLE_KEY	GENMASK(15, 8)
- #define MT7996_PATCH_AES_KEY		GENMASK(7, 0)
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 177cfff..58fa6b4 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -237,6 +237,9 @@ struct mt7996_phy {
- 	struct mt76_channel_state state_ts;
- 
- 	bool has_aux_rx;
-+
-+	u8 pp_mode;
-+	u16 punct_bitmap;
- };
- 
- struct mt7996_dev {
-@@ -614,6 +617,7 @@ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
- 				     struct ieee80211_vif *vif,
- 				     struct ieee80211_sta *sta);
- int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode);
-+int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap);
- #ifdef CONFIG_MAC80211_DEBUGFS
- void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 			    struct ieee80211_sta *sta, struct dentry *dir);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0009-mtk-mt76-mt7996-fix-rxd-checksum-offload-offset.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0009-mtk-mt76-mt7996-fix-rxd-checksum-offload-offset.patch
new file mode 100644
index 0000000..c5c0717
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0009-mtk-mt76-mt7996-fix-rxd-checksum-offload-offset.patch
@@ -0,0 +1,58 @@
+From 1d799d5548538dcf501f7fb99b1f529ef001b4c8 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Fri, 26 Jul 2024 09:27:45 +0800
+Subject: [PATCH 009/199] mtk: mt76: mt7996: fix rxd checksum offload offset
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt76_connac3_mac.h | 4 ++--
+ mt7996/mac.c       | 4 ++--
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/mt76_connac3_mac.h b/mt76_connac3_mac.h
+index 3fc94bd7..db0c29e6 100644
+--- a/mt76_connac3_mac.h
++++ b/mt76_connac3_mac.h
+@@ -28,8 +28,6 @@ enum {
+ #define MT_RXD0_MESH			BIT(18)
+ #define MT_RXD0_MHCP			BIT(19)
+ #define MT_RXD0_NORMAL_ETH_TYPE_OFS	GENMASK(22, 16)
+-#define MT_RXD0_NORMAL_IP_SUM		BIT(23)
+-#define MT_RXD0_NORMAL_UDP_TCP_SUM	BIT(24)
+ 
+ #define MT_RXD0_SW_PKT_TYPE_MASK	GENMASK(31, 16)
+ #define MT_RXD0_SW_PKT_TYPE_MAP		0x380F
+@@ -80,6 +78,8 @@ enum {
+ #define MT_RXD3_NORMAL_BEACON_UC	BIT(21)
+ #define MT_RXD3_NORMAL_CO_ANT		BIT(22)
+ #define MT_RXD3_NORMAL_FCS_ERR		BIT(24)
++#define MT_RXD3_NORMAL_IP_SUM		BIT(26)
++#define MT_RXD3_NORMAL_UDP_TCP_SUM	BIT(27)
+ #define MT_RXD3_NORMAL_VLAN2ETH		BIT(31)
+ 
+ /* RXD DW4 */
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index b3f9591f..109a74a9 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -435,7 +435,7 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ 	u32 rxd2 = le32_to_cpu(rxd[2]);
+ 	u32 rxd3 = le32_to_cpu(rxd[3]);
+ 	u32 rxd4 = le32_to_cpu(rxd[4]);
+-	u32 csum_mask = MT_RXD0_NORMAL_IP_SUM | MT_RXD0_NORMAL_UDP_TCP_SUM;
++	u32 csum_mask = MT_RXD3_NORMAL_IP_SUM | MT_RXD3_NORMAL_UDP_TCP_SUM;
+ 	u32 csum_status = *(u32 *)skb->cb;
+ 	u32 mesh_mask = MT_RXD0_MESH | MT_RXD0_MHCP;
+ 	bool is_mesh = (rxd0 & mesh_mask) == mesh_mask;
+@@ -497,7 +497,7 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ 	if (!sband->channels)
+ 		return -EINVAL;
+ 
+-	if ((rxd0 & csum_mask) == csum_mask &&
++	if ((rxd3 & csum_mask) == csum_mask &&
+ 	    !(csum_status & (BIT(0) | BIT(2) | BIT(3))))
+ 		skb->ip_summed = CHECKSUM_UNNECESSARY;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0009-mtk-wifi-mt76-mt7996-add-driver-support-for-wpa3-ocv.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0009-mtk-wifi-mt76-mt7996-add-driver-support-for-wpa3-ocv.patch
deleted file mode 100644
index 93ab989..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0009-mtk-wifi-mt76-mt7996-add-driver-support-for-wpa3-ocv.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From f0e002bc52cfc5fe8d32fe647baef94920d212d6 Mon Sep 17 00:00:00 2001
-From: mtk23510 <rudra.shahi@mediatek.com>
-Date: Fri, 24 Mar 2023 19:18:53 +0800
-Subject: [PATCH 009/116] mtk: wifi: mt76: mt7996: add driver support for wpa3
- ocv and bp mt76
-
-Signed-off-by: mtk23510 <rudra.shahi@mediatek.com>
----
- mt7996/init.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/mt7996/init.c b/mt7996/init.c
-index afe8a0a..ab2e17e 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -389,6 +389,8 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
- 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER);
- 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_PUNCT);
- 
-+	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION);
-+	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_PROTECTION);
- 	if (!mdev->dev->of_node ||
- 	    !of_property_read_bool(mdev->dev->of_node,
- 				   "mediatek,disable-radar-background"))
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0010-mtk-mt76-mt7996-fix-EHT-Beamforming-capability-check.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0010-mtk-mt76-mt7996-fix-EHT-Beamforming-capability-check.patch
new file mode 100644
index 0000000..95afaf1
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0010-mtk-mt76-mt7996-fix-EHT-Beamforming-capability-check.patch
@@ -0,0 +1,30 @@
+From 18583c512c569a45767cbdb3b93dcb688158bfe6 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Fri, 28 Jun 2024 11:05:26 +0800
+Subject: [PATCH 010/199] mtk: mt76: mt7996: fix EHT Beamforming capability
+ check
+
+---
+ mt7996/mcu.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 6d5bbe5b..39963a36 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1429,10 +1429,10 @@ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ 
+ 		if (bfee)
+ 			return vif->bss_conf.eht_su_beamformee &&
+-			       EHT_PHY(CAP0_SU_BEAMFORMEE, pe->phy_cap_info[0]);
++			       EHT_PHY(CAP0_SU_BEAMFORMER, pe->phy_cap_info[0]);
+ 		else
+ 			return vif->bss_conf.eht_su_beamformer &&
+-			       EHT_PHY(CAP0_SU_BEAMFORMER, pe->phy_cap_info[0]);
++			       EHT_PHY(CAP0_SU_BEAMFORMEE, pe->phy_cap_info[0]);
+ 	}
+ 
+ 	if (sta->deflink.he_cap.has_he) {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0010-mtk-wifi-mt76-mt7996-enable-ser-query.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0010-mtk-wifi-mt76-mt7996-enable-ser-query.patch
deleted file mode 100644
index 1ac9d59..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0010-mtk-wifi-mt76-mt7996-enable-ser-query.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From 55c395dcb6ab95671cc0830e31b71c3aa8fa91c5 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Mon, 30 Oct 2023 20:19:41 +0800
-Subject: [PATCH 010/116] mtk: wifi: mt76: mt7996: enable ser query
-
-Do not return -EINVAL when action is UNI_CMD_SER_QUERY for user
-to dump SER information from FW.
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- mt7996/mcu.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 1f99b2d..9ed8638 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -3865,6 +3865,8 @@ int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 val, u8 band)
- 	};
- 
- 	switch (action) {
-+	case UNI_CMD_SER_QUERY:
-+		break;
- 	case UNI_CMD_SER_SET:
- 		req.set.mask = cpu_to_le32(val);
- 		break;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0011-mtk-mt76-mt7996-fix-amsdu-information.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0011-mtk-mt76-mt7996-fix-amsdu-information.patch
new file mode 100644
index 0000000..0afd075
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0011-mtk-mt76-mt7996-fix-amsdu-information.patch
@@ -0,0 +1,109 @@
+From aff4c26706fa1403abca8067cd0073db88c9f6cf Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Fri, 17 May 2024 15:28:06 +0800
+Subject: [PATCH 011/199] mtk: mt76: mt7996: fix amsdu information
+
+The amsdu information is common information for all bands so maintain
+it by main phy instead of calculating by each phy.
+Without this patch, the statistics of tx_amsdu_cnt and tx_amsdu would
+be incorrect.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt76.h           |  3 ++-
+ mt7996/debugfs.c | 13 -------------
+ mt7996/mac.c     | 15 +++++++++------
+ 3 files changed, 11 insertions(+), 20 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index 5c9d6c64..45039377 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -916,6 +916,7 @@ struct mt76_dev {
+ 	};
+ };
+ 
++#define MT76_MAX_AMSDU_NUM 8
+ /* per-phy stats.  */
+ struct mt76_mib_stats {
+ 	u32 ack_fail_cnt;
+@@ -975,7 +976,7 @@ struct mt76_mib_stats {
+ 	u32 rx_vec_queue_overflow_drop_cnt;
+ 	u32 rx_ba_cnt;
+ 
+-	u32 tx_amsdu[8];
++	u32 tx_amsdu[MT76_MAX_AMSDU_NUM];
+ 	u32 tx_amsdu_cnt;
+ 
+ 	/* mcu_muru_stats */
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 62c03d08..a17c99a2 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -530,7 +530,6 @@ mt7996_tx_stats_show(struct seq_file *file, void *data)
+ 	struct mt7996_phy *phy = file->private;
+ 	struct mt7996_dev *dev = phy->dev;
+ 	struct mt76_mib_stats *mib = &phy->mib;
+-	int i;
+ 	u32 attempts, success, per;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+@@ -547,18 +546,6 @@ mt7996_tx_stats_show(struct seq_file *file, void *data)
+ 
+ 	mt7996_txbf_stat_read_phy(phy, file);
+ 
+-	/* Tx amsdu info */
+-	seq_puts(file, "Tx MSDU statistics:\n");
+-	for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) {
+-		seq_printf(file, "AMSDU pack count of %d MSDU in TXD: %8d ",
+-			   i + 1, mib->tx_amsdu[i]);
+-		if (mib->tx_amsdu_cnt)
+-			seq_printf(file, "(%3d%%)\n",
+-				   mib->tx_amsdu[i] * 100 / mib->tx_amsdu_cnt);
+-		else
+-			seq_puts(file, "\n");
+-	}
+-
+ 	mutex_unlock(&dev->mt76.mutex);
+ 
+ 	return 0;
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 109a74a9..503e92c0 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2103,10 +2103,19 @@ void mt7996_mac_update_stats(struct mt7996_phy *phy)
+ {
+ 	struct mt76_mib_stats *mib = &phy->mib;
+ 	struct mt7996_dev *dev = phy->dev;
++	struct mt76_mib_stats *main_mib = &dev->phy.mib;
+ 	u8 band_idx = phy->mt76->band_idx;
+ 	u32 cnt;
+ 	int i;
+ 
++	/* Update per-dev structures */
++	for (i = 0; i < ARRAY_SIZE(main_mib->tx_amsdu); i++) {
++		cnt = mt76_rr(dev, MT_PLE_AMSDU_PACK_MSDU_CNT(i));
++		main_mib->tx_amsdu[i] += cnt;
++		main_mib->tx_amsdu_cnt += cnt;
++	}
++
++	/* Update per-phy structures */
+ 	cnt = mt76_rr(dev, MT_MIB_RSCR1(band_idx));
+ 	mib->fcs_err_cnt += cnt;
+ 
+@@ -2212,12 +2221,6 @@ void mt7996_mac_update_stats(struct mt7996_phy *phy)
+ 	cnt = mt76_rr(dev, MT_MIB_BSCR17(band_idx));
+ 	mib->tx_bf_fb_cpl_cnt += cnt;
+ 
+-	for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) {
+-		cnt = mt76_rr(dev, MT_PLE_AMSDU_PACK_MSDU_CNT(i));
+-		mib->tx_amsdu[i] += cnt;
+-		mib->tx_amsdu_cnt += cnt;
+-	}
+-
+ 	/* rts count */
+ 	cnt = mt76_rr(dev, MT_MIB_BTSCR5(band_idx));
+ 	mib->rts_cnt += cnt;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0011-mtk-wifi-mt76-mt7996-set-key-flag-IEEE80211_KEY_FLAG.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0011-mtk-wifi-mt76-mt7996-set-key-flag-IEEE80211_KEY_FLAG.patch
deleted file mode 100644
index ca8e083..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0011-mtk-wifi-mt76-mt7996-set-key-flag-IEEE80211_KEY_FLAG.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From df056f10532395d83a9bdb199ff609817840dfab Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Thu, 18 Apr 2024 17:21:22 +0800
-Subject: [PATCH 011/116] mtk: wifi: mt76: mt7996: set key flag
- 'IEEE80211_KEY_FLAG_GENERATE_MMIE' for other ciphers
-
-When beacon protection is enabled, FW checks MMIE tag & length in the
-beacon in every cipher mode. Therefore mt76 needs to set the flag
-'IEEE80211_KEY_GENERATE_MMIE' on so that MAC80211 generates and initializes
-MMIE for us.
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- mt7996/main.c | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 1791335..dc8014e 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -360,14 +360,14 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
- 	case WLAN_CIPHER_SUITE_SMS4:
- 		break;
- 	case WLAN_CIPHER_SUITE_AES_CMAC:
--		wcid_keyidx = &wcid->hw_key_idx2;
--		key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE;
--		fallthrough;
- 	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
- 	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
- 	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
--		if (key->keyidx == 6 || key->keyidx == 7)
-+		if (key->keyidx == 6 || key->keyidx == 7) {
-+			wcid_keyidx = &wcid->hw_key_idx2;
-+			key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE;
- 			break;
-+		}
- 		fallthrough;
- 	case WLAN_CIPHER_SUITE_WEP40:
- 	case WLAN_CIPHER_SUITE_WEP104:
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0012-mtk-mt76-mt7996-add-beacon_int_min_gcd-to-support-di.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0012-mtk-mt76-mt7996-add-beacon_int_min_gcd-to-support-di.patch
new file mode 100644
index 0000000..c8c218a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0012-mtk-mt76-mt7996-add-beacon_int_min_gcd-to-support-di.patch
@@ -0,0 +1,34 @@
+From 0230f41ebd90270be685fc813d23a150afbfc823 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Fri, 19 Apr 2024 11:01:21 +0800
+Subject: [PATCH 012/199] mtk: mt76: mt7996: add beacon_int_min_gcd to support
+ different beacon interval
+
+When beacon_int_min_gcd is zero, all beacon intervals for different
+interfaces should be same. If beacon_int_min_gcd is larger than zero,
+all beacon intervals for different interfaces should be larger or
+equal than beacon_int_min_gcd.
+
+Without this patch, set beacon fail when different interfaces use
+different beacon interval.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/init.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 7d8d1e7b..5e969732 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -42,6 +42,7 @@ static const struct ieee80211_iface_combination if_comb[] = {
+ 				       BIT(NL80211_CHAN_WIDTH_40) |
+ 				       BIT(NL80211_CHAN_WIDTH_80) |
+ 				       BIT(NL80211_CHAN_WIDTH_160),
++		.beacon_int_min_gcd = 100,
+ 	}
+ };
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0012-mtk-wifi-mt76-mt7996-Fix-TGax-HE-4.51.1_24G-fail.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0012-mtk-wifi-mt76-mt7996-Fix-TGax-HE-4.51.1_24G-fail.patch
deleted file mode 100644
index 3f105a8..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0012-mtk-wifi-mt76-mt7996-Fix-TGax-HE-4.51.1_24G-fail.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From 6992fbb2128d9df358760e654a7797b3f89efcb9 Mon Sep 17 00:00:00 2001
-From: mtk27745 <rex.lu@mediatek.com>
-Date: Fri, 17 Nov 2023 11:01:04 +0800
-Subject: [PATCH 012/116] mtk: wifi: mt76: mt7996: Fix TGax HE-4.51.1_24G fail
-
-According to sta capability to decide to enable/disable wed pao when create ppe entry.
-without this patch, TGax HE-4.51.1_24G will test fail
-
-Signed-off-by: mtk27745 <rex.lu@mediatek.com>
----
- mt7996/main.c | 7 ++++++-
- 1 file changed, 6 insertions(+), 1 deletion(-)
-
-diff --git a/mt7996/main.c b/mt7996/main.c
-index dc8014e..0e8abe7 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -1463,7 +1463,12 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
- 	path->mtk_wdma.queue = 0;
- 	path->mtk_wdma.wcid = msta->wcid.idx;
- 
--	path->mtk_wdma.amsdu = mtk_wed_is_amsdu_supported(wed);
-+	if (ieee80211_hw_check(hw, SUPPORTS_AMSDU_IN_AMPDU) &&
-+	    mtk_wed_is_amsdu_supported(wed))
-+		path->mtk_wdma.amsdu = msta->wcid.amsdu;
-+	else
-+		path->mtk_wdma.amsdu = 0;
-+
- 	ctx->dev = NULL;
- 
- 	return 0;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0013-mtk-mt76-adjust-beamform-mcu-cmd-configuration-for-m.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0013-mtk-mt76-adjust-beamform-mcu-cmd-configuration-for-m.patch
new file mode 100644
index 0000000..28511c6
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0013-mtk-mt76-adjust-beamform-mcu-cmd-configuration-for-m.patch
@@ -0,0 +1,32 @@
+From 46245bf2b1adb748f52772cb7550c85733aa6db7 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 18 Mar 2024 11:13:56 +0800
+Subject: [PATCH 013/199] mtk: mt76: adjust beamform mcu cmd configuration for
+ mt7992
+
+Adjust the correct beamform mcu cmd configuration for mt7992 chipsets.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt7996/mcu.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 39963a36..3816b5a5 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3923,8 +3923,9 @@ int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action)
+ 
+ 		tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_mod_en));
+ 		req_mod_en = (struct bf_mod_en_ctrl *)tlv;
+-		req_mod_en->bf_num = 3;
+-		req_mod_en->bf_bitmap = GENMASK(2, 0);
++		req_mod_en->bf_num = mt7996_band_valid(dev, MT_BAND2) ? 3 : 2;
++		req_mod_en->bf_bitmap = mt7996_band_valid(dev, MT_BAND2) ?
++					GENMASK(2, 0) : GENMASK(1, 0);
+ 		break;
+ 	}
+ 	default:
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0013-mtk-wifi-mt76-mt7996-add-support-for-different-varia.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0013-mtk-wifi-mt76-mt7996-add-support-for-different-varia.patch
deleted file mode 100644
index 4f690df..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0013-mtk-wifi-mt76-mt7996-add-support-for-different-varia.patch
+++ /dev/null
@@ -1,325 +0,0 @@
-From f269d62b33ba484c31db69d71b9ebcac8e2fc094 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Thu, 20 Jul 2023 17:27:22 +0800
-Subject: [PATCH 013/116] mtk: wifi: mt76: mt7996: add support for different
- variants
-
-Add fem type (2i5i, 2i5e, 2e5e, ...)
-Add Kite default bin for each fem type since loading wrong default bin
-will fail to setup interface
-Add eeprom fem type check
-
-Add adie 7976c efuse check
-Efuse offset 0x470 will be set to 0xc after final test if 7976c adie is used
-Chip manufactoring factories may transfer, which leads to different adie chip versions,
-so we add this efuse check to avoid 7976c recognition failure.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-GPIO ADie Combination of BE5040 should be considered as don't care
-instead of 0
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Only check eeprom chip id when fem type (= MT7996_FEM_UNSET) is not determined yet
-Without this fix, mt7996_check_eeprom will return EINVAL in mt7996_eeprom_check_fw_mode
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/eeprom.c | 40 +++++++++++++++++++++++++++++--
- mt7996/eeprom.h |  1 +
- mt7996/init.c   | 63 +++++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/mcu.c    |  7 +++++-
- mt7996/mt7996.h | 43 ++++++++++++++++++++++++++++++---
- mt7996/regs.h   |  7 ++++++
- 6 files changed, 155 insertions(+), 6 deletions(-)
-
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 4a82371..3260d1f 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -9,14 +9,33 @@
- 
- static int mt7996_check_eeprom(struct mt7996_dev *dev)
- {
-+#define FEM_INT				0
-+#define FEM_EXT				3
- 	u8 *eeprom = dev->mt76.eeprom.data;
-+	u8 i, fem[__MT_MAX_BAND], fem_type;
- 	u16 val = get_unaligned_le16(eeprom);
- 
-+	for (i = 0; i < __MT_MAX_BAND; i++)
-+		fem[i] = eeprom[MT_EE_WIFI_CONF + 6 + i] & MT_EE_WIFI_PA_LNA_CONFIG;
-+
- 	switch (val) {
- 	case 0x7990:
- 		return is_mt7996(&dev->mt76) ? 0 : -EINVAL;
- 	case 0x7992:
--		return is_mt7992(&dev->mt76) ? 0 : -EINVAL;
-+		if (dev->fem_type == MT7996_FEM_UNSET)
-+			return is_mt7992(&dev->mt76) ? 0 : -EINVAL;
-+
-+		if (fem[0] == FEM_EXT && fem[1] == FEM_EXT)
-+			fem_type = MT7996_FEM_EXT;
-+		else if (fem[0] == FEM_INT && fem[1] == FEM_INT)
-+			fem_type = MT7996_FEM_INT;
-+		else if (fem[0] == FEM_INT && fem[1] == FEM_EXT)
-+			fem_type = MT7996_FEM_MIX;
-+		else
-+			return -EINVAL;
-+
-+		return (is_mt7992(&dev->mt76) ? 0 : -EINVAL) |
-+		       (dev->fem_type == fem_type ? 0 : -EINVAL);
- 	default:
- 		return -EINVAL;
- 	}
-@@ -26,9 +45,22 @@ static char *mt7996_eeprom_name(struct mt7996_dev *dev)
- {
- 	switch (mt76_chip(&dev->mt76)) {
- 	case 0x7990:
-+		if (dev->chip_sku == MT7996_SKU_404)
-+			return MT7996_EEPROM_DEFAULT_404;
- 		return MT7996_EEPROM_DEFAULT;
- 	case 0x7992:
--		return MT7992_EEPROM_DEFAULT;
-+		if (dev->chip_sku == MT7992_SKU_23) {
-+			if (dev->fem_type == MT7996_FEM_INT)
-+				return MT7992_EEPROM_DEFAULT_23;
-+			return MT7992_EEPROM_DEFAULT_23_EXT;
-+		} else if (dev->chip_sku == MT7992_SKU_44) {
-+			if (dev->fem_type == MT7996_FEM_INT)
-+				return MT7992_EEPROM_DEFAULT;
-+			else if (dev->fem_type == MT7996_FEM_MIX)
-+				return MT7992_EEPROM_DEFAULT_MIX;
-+			return MT7992_EEPROM_DEFAULT_EXT;
-+		}
-+		return MT7992_EEPROM_DEFAULT_24;
- 	default:
- 		return MT7996_EEPROM_DEFAULT;
- 	}
-@@ -219,6 +251,10 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
- {
- 	int ret;
- 
-+	ret = mt7996_get_chip_sku(dev);
-+	if (ret)
-+		return ret;
-+
- 	ret = mt7996_eeprom_load(dev);
- 	if (ret < 0) {
- 		if (ret != -EINVAL)
-diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index 412d6e2..72c38ad 100644
---- a/mt7996/eeprom.h
-+++ b/mt7996/eeprom.h
-@@ -29,6 +29,7 @@ enum mt7996_eeprom_field {
- #define MT_EE_WIFI_CONF0_BAND_SEL		GENMASK(2, 0)
- #define MT_EE_WIFI_CONF1_BAND_SEL		GENMASK(5, 3)
- #define MT_EE_WIFI_CONF2_BAND_SEL		GENMASK(2, 0)
-+#define MT_EE_WIFI_PA_LNA_CONFIG		GENMASK(1, 0)
- 
- #define MT_EE_WIFI_CONF1_TX_PATH_BAND0		GENMASK(5, 3)
- #define MT_EE_WIFI_CONF2_TX_PATH_BAND1		GENMASK(2, 0)
-diff --git a/mt7996/init.c b/mt7996/init.c
-index ab2e17e..d58335a 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -885,6 +885,65 @@ out:
- #endif
- }
- 
-+int mt7996_get_chip_sku(struct mt7996_dev *dev)
-+{
-+#define MT7976C_CHIP_VER	0x8a10
-+#define MT7976C_HL_CHIP_VER	0x8b00
-+#define MT7976C_PS_CHIP_VER	0x8c10
-+#define MT7976C_EFUSE_OFFSET	0x470
-+#define MT7976C_EFUSE_VALUE	0xc
-+	u32 regval, val = mt76_rr(dev, MT_PAD_GPIO);
-+	u16 adie_chip_id, adie_chip_ver;
-+	u8 adie_comb, adie_num, adie_idx = 0;
-+
-+	switch (mt76_chip(&dev->mt76)) {
-+	case 0x7990:
-+		adie_comb = FIELD_GET(MT_PAD_GPIO_ADIE_COMB, val);
-+		if (adie_comb <= 1)
-+			dev->chip_sku = MT7996_SKU_444;
-+		else
-+			dev->chip_sku = MT7996_SKU_404;
-+		break;
-+	case 0x7992:
-+		adie_comb = FIELD_GET(MT_PAD_GPIO_ADIE_COMB_7992, val);
-+		adie_num = FIELD_GET(MT_PAD_GPIO_ADIE_NUM_7992, val);
-+		adie_idx = !adie_num;
-+		if (adie_num)
-+			dev->chip_sku = MT7992_SKU_23;
-+		else if (adie_comb)
-+			dev->chip_sku = MT7992_SKU_44;
-+		else
-+			dev->chip_sku = MT7992_SKU_24;
-+		break;
-+	default:
-+		return -EINVAL;
-+	}
-+
-+	if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) {
-+		u8 buf[MT7996_EEPROM_BLOCK_SIZE];
-+		u8 idx = MT7976C_EFUSE_OFFSET % MT7996_EEPROM_BLOCK_SIZE;
-+		bool is_7976c;
-+
-+		mt7996_mcu_rf_regval(dev, MT_ADIE_CHIP_ID(adie_idx), &regval, false);
-+		adie_chip_id = FIELD_GET(MT_ADIE_CHIP_ID_MASK, regval);
-+		adie_chip_ver = FIELD_GET(MT_ADIE_VERSION_MASK, regval);
-+		mt7996_mcu_get_eeprom(dev, MT7976C_EFUSE_OFFSET, buf);
-+		is_7976c = (adie_chip_ver == MT7976C_CHIP_VER) ||
-+			   (adie_chip_ver == MT7976C_HL_CHIP_VER) ||
-+			   (adie_chip_ver == MT7976C_PS_CHIP_VER) ||
-+			   (buf[idx] == MT7976C_EFUSE_VALUE);
-+		if (adie_chip_id == 0x7975 || (adie_chip_id == 0x7976 && is_7976c) ||
-+		    adie_chip_id == 0x7979)
-+			dev->fem_type = MT7996_FEM_INT;
-+		else if (adie_chip_id == 0x7977 && adie_comb == 1)
-+			dev->fem_type = MT7996_FEM_MIX;
-+		else
-+			dev->fem_type = MT7996_FEM_EXT;
-+	}
-+
-+	return 0;
-+}
-+
- static int mt7996_init_hardware(struct mt7996_dev *dev)
- {
- 	int ret, idx;
-@@ -900,6 +959,10 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
- 	INIT_LIST_HEAD(&dev->wed_rro.poll_list);
- 	spin_lock_init(&dev->wed_rro.lock);
- 
-+	ret = mt7996_get_chip_sku(dev);
-+	if (ret)
-+		return ret;
-+
- 	ret = mt7996_dma_init(dev);
- 	if (ret)
- 		return ret;
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 9ed8638..fb6ee7c 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -14,7 +14,12 @@
- 	char *_fw;						\
- 	switch (mt76_chip(&(_dev)->mt76)) {			\
- 	case 0x7992:						\
--		_fw = MT7992_##name;				\
-+		if ((_dev)->chip_sku == MT7992_SKU_23)		\
-+			_fw = MT7992_##name##_23;		\
-+		else if ((_dev)->chip_sku == MT7992_SKU_24)	\
-+			_fw = MT7992_##name##_24;		\
-+		else						\
-+			_fw = MT7992_##name;			\
- 		break;						\
- 	case 0x7990:						\
- 	default:						\
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 58fa6b4..b7197dc 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -39,8 +39,24 @@
- #define MT7992_FIRMWARE_DSP		"mediatek/mt7996/mt7992_dsp.bin"
- #define MT7992_ROM_PATCH		"mediatek/mt7996/mt7992_rom_patch.bin"
- 
-+#define MT7992_FIRMWARE_WA_24		"mediatek/mt7996/mt7992_wa_24.bin"
-+#define MT7992_FIRMWARE_WM_24		"mediatek/mt7996/mt7992_wm_24.bin"
-+#define MT7992_FIRMWARE_DSP_24		"mediatek/mt7996/mt7992_dsp_24.bin"
-+#define MT7992_ROM_PATCH_24		"mediatek/mt7996/mt7992_rom_patch_24.bin"
-+
-+#define MT7992_FIRMWARE_WA_23		"mediatek/mt7996/mt7992_wa_23.bin"
-+#define MT7992_FIRMWARE_WM_23		"mediatek/mt7996/mt7992_wm_23.bin"
-+#define MT7992_FIRMWARE_DSP_23		"mediatek/mt7996/mt7992_dsp_23.bin"
-+#define MT7992_ROM_PATCH_23		"mediatek/mt7996/mt7992_rom_patch_23.bin"
-+
- #define MT7996_EEPROM_DEFAULT		"mediatek/mt7996/mt7996_eeprom.bin"
--#define MT7992_EEPROM_DEFAULT		"mediatek/mt7996/mt7992_eeprom.bin"
-+#define MT7996_EEPROM_DEFAULT_404	"mediatek/mt7996/mt7996_eeprom_dual_404.bin"
-+#define MT7992_EEPROM_DEFAULT		"mediatek/mt7996/mt7992_eeprom_2i5i.bin"
-+#define MT7992_EEPROM_DEFAULT_EXT	"mediatek/mt7996/mt7992_eeprom_2e5e.bin"
-+#define MT7992_EEPROM_DEFAULT_MIX	"mediatek/mt7996/mt7992_eeprom_2i5e.bin"
-+#define MT7992_EEPROM_DEFAULT_24	"mediatek/mt7996/mt7992_eeprom_24_2i5i.bin"
-+#define MT7992_EEPROM_DEFAULT_23	"mediatek/mt7996/mt7992_eeprom_23_2i5i.bin"
-+#define MT7992_EEPROM_DEFAULT_23_EXT	"mediatek/mt7996/mt7992_eeprom_23_2e5e.bin"
- #define MT7996_EEPROM_SIZE		7680
- #define MT7996_EEPROM_BLOCK_SIZE	16
- #define MT7996_TOKEN_SIZE		16384
-@@ -89,6 +105,24 @@ struct mt7996_sta;
- struct mt7996_dfs_pulse;
- struct mt7996_dfs_pattern;
- 
-+enum mt7996_fem_type {
-+	MT7996_FEM_UNSET,
-+	MT7996_FEM_EXT,
-+	MT7996_FEM_INT,
-+	MT7996_FEM_MIX,
-+};
-+
-+enum mt7996_sku_type {
-+	MT7996_SKU_404,
-+	MT7996_SKU_444,
-+};
-+
-+enum mt7992_sku_type {
-+	MT7992_SKU_23,
-+	MT7992_SKU_24,
-+	MT7992_SKU_44,
-+};
-+
- enum mt7996_ram_type {
- 	MT7996_RAM_TYPE_WM,
- 	MT7996_RAM_TYPE_WA,
-@@ -261,6 +295,9 @@ struct mt7996_dev {
- 	struct cfg80211_chan_def rdd2_chandef;
- 	struct mt7996_phy *rdd2_phy;
- 
-+	u8 chip_sku;
-+	u8 fem_type;
-+
- 	u16 chainmask;
- 	u8 chainshift[__MT_MAX_BAND];
- 	u32 hif_idx;
-@@ -409,8 +446,7 @@ mt7996_band_valid(struct mt7996_dev *dev, u8 band)
- 		return band <= MT_BAND1;
- 
- 	/* tri-band support */
--	if (band <= MT_BAND2 &&
--	    mt76_get_field(dev, MT_PAD_GPIO, MT_PAD_GPIO_ADIE_COMB) <= 1)
-+	if (band <= MT_BAND2 && dev->chip_sku)
- 		return true;
- 
- 	return band == MT_BAND0 || band == MT_BAND2;
-@@ -441,6 +477,7 @@ int mt7996_init_tx_queues(struct mt7996_phy *phy, int idx,
- 			  int n_desc, int ring_base, struct mtk_wed_device *wed);
- void mt7996_init_txpower(struct mt7996_phy *phy);
- int mt7996_txbf_init(struct mt7996_dev *dev);
-+int mt7996_get_chip_sku(struct mt7996_dev *dev);
- void mt7996_reset(struct mt7996_dev *dev);
- int mt7996_run(struct ieee80211_hw *hw);
- int mt7996_mcu_init(struct mt7996_dev *dev);
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index 47b429d..cf12c5e 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -662,6 +662,13 @@ enum offs_rev {
- 
- #define MT_PAD_GPIO				0x700056f0
- #define MT_PAD_GPIO_ADIE_COMB			GENMASK(16, 15)
-+#define MT_PAD_GPIO_ADIE_COMB_7992		GENMASK(17, 16)
-+#define MT_PAD_GPIO_ADIE_NUM_7992		BIT(15)
-+
-+/* ADIE */
-+#define MT_ADIE_CHIP_ID(_idx)                  (0x0f00002c + ((_idx) << 28))
-+#define MT_ADIE_VERSION_MASK                   GENMASK(15, 0)
-+#define MT_ADIE_CHIP_ID_MASK                   GENMASK(31, 16)
- 
- #define MT_HW_REV				0x70010204
- #define MT_HW_REV1				0x8a00
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0014-mtk-mt76-mt7996-add-preamble-puncture-support-for-mt.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0014-mtk-mt76-mt7996-add-preamble-puncture-support-for-mt.patch
new file mode 100644
index 0000000..26f0118
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0014-mtk-mt76-mt7996-add-preamble-puncture-support-for-mt.patch
@@ -0,0 +1,154 @@
+From 2ee799d7d81869c1eb1d3a332bd223670c2523e5 Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Thu, 18 Apr 2024 11:16:24 +0800
+Subject: [PATCH 014/199] mtk: mt76: mt7996: add preamble puncture support for
+ mt7996
+
+Add support configure preamble puncture feature through mcu commands.
+
+Co-developed-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ mt76_connac_mcu.h |  1 +
+ mt7996/init.c     |  1 +
+ mt7996/main.c     |  5 +++++
+ mt7996/mcu.c      | 40 ++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h      | 10 ++++++++++
+ mt7996/mt7996.h   |  4 ++++
+ 6 files changed, 61 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 4242d436..a7cb33d2 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1283,6 +1283,7 @@ enum {
+ 	MCU_UNI_CMD_CHANNEL_SWITCH = 0x34,
+ 	MCU_UNI_CMD_THERMAL = 0x35,
+ 	MCU_UNI_CMD_VOW = 0x37,
++	MCU_UNI_CMD_PP = 0x38,
+ 	MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
+ 	MCU_UNI_CMD_RRO = 0x57,
+ 	MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 5e969732..a47b578c 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -389,6 +389,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT);
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER);
++	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_PUNCT);
+ 
+ 	if (!mdev->dev->of_node ||
+ 	    !of_property_read_bool(mdev->dev->of_node,
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 2b094b33..8d2b0ee7 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -415,6 +415,11 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
+ 	int ret;
+ 
+ 	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
++		ret = mt7996_mcu_set_pp_en(phy, PP_USR_MODE,
++					   phy->mt76->chandef.punctured);
++		if (ret)
++			return ret;
++
+ 		ieee80211_stop_queues(hw);
+ 		ret = mt7996_set_channel(phy);
+ 		if (ret)
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 3816b5a5..e01457de 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -4553,3 +4553,43 @@ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode)
+ 	return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(CP_SUPPORT),
+ 				 &cp_mode, sizeof(cp_mode), true);
+ }
++
++int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap)
++{
++	struct mt7996_dev *dev = phy->dev;
++	bool pp_auto = (mode == PP_FW_MODE);
++	struct {
++		u8 _rsv1[4];
++
++		__le16 tag;
++		__le16 len;
++		u8 mgmt_mode;
++		u8 band_idx;
++		u8 force_bitmap_ctrl;
++		u8 auto_mode;
++		__le16 bitmap;
++		u8 _rsv2[2];
++	} __packed req = {
++		.tag = cpu_to_le16(UNI_CMD_PP_EN_CTRL),
++		.len = cpu_to_le16(sizeof(req) - 4),
++
++		.mgmt_mode = !pp_auto,
++		.band_idx = phy->mt76->band_idx,
++		.force_bitmap_ctrl = (mode == PP_USR_MODE) ? 2 : 0,
++		.auto_mode = pp_auto,
++		.bitmap = cpu_to_le16(bitmap),
++	};
++
++	if (phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ ||
++	    mode > PP_USR_MODE)
++		return 0;
++
++	if (bitmap && phy->punct_bitmap == bitmap)
++		return 0;
++
++	phy->punct_bitmap = bitmap;
++	phy->pp_mode = mode;
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(PP),
++				 &req, sizeof(req), false);
++}
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 43468bca..df42c0f8 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -923,6 +923,16 @@ enum {
+ 	MT7996_SEC_MODE_MAX,
+ };
+ 
++enum {
++	UNI_CMD_PP_EN_CTRL,
++};
++
++enum pp_mode {
++	PP_DISABLE = 0,
++	PP_FW_MODE,
++	PP_USR_MODE,
++};
++
+ #define MT7996_PATCH_SEC		GENMASK(31, 24)
+ #define MT7996_PATCH_SCRAMBLE_KEY	GENMASK(15, 8)
+ #define MT7996_PATCH_AES_KEY		GENMASK(7, 0)
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 177cfff3..58fa6b45 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -237,6 +237,9 @@ struct mt7996_phy {
+ 	struct mt76_channel_state state_ts;
+ 
+ 	bool has_aux_rx;
++
++	u8 pp_mode;
++	u16 punct_bitmap;
+ };
+ 
+ struct mt7996_dev {
+@@ -614,6 +617,7 @@ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ 				     struct ieee80211_vif *vif,
+ 				     struct ieee80211_sta *sta);
+ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode);
++int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap);
+ #ifdef CONFIG_MAC80211_DEBUGFS
+ void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 			    struct ieee80211_sta *sta, struct dentry *dir);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0014-mtk-wifi-mt76-mt7996-ACS-channel-time-too-long-on-du.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0014-mtk-wifi-mt76-mt7996-ACS-channel-time-too-long-on-du.patch
deleted file mode 100644
index 6bcf893..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0014-mtk-wifi-mt76-mt7996-ACS-channel-time-too-long-on-du.patch
+++ /dev/null
@@ -1,68 +0,0 @@
-From d2d45d00e7644e62f366203a17924f0e2025853a Mon Sep 17 00:00:00 2001
-From: "fancy.liu" <fancy.liu@mediatek.com>
-Date: Tue, 14 Nov 2023 10:13:24 +0800
-Subject: [PATCH 014/116] mtk: wifi: mt76: mt7996: ACS channel time too long on
- duty channel
-
-Step and issue:
-1. Set channel to 36 in hostapd config;
-2. Bootup;
-3. Enable ACS through UCI command and reload;
-4. Check hostapd log, channel 36 channel_time is much longer than other channels.
-
-Root cause:
-The reset chan_stat condition missed duty channel.
-
-Solution:
-When scan start, need to reset chan_stat in each channel switch.
-
-Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
-
-Issue:
-There's a chance that the channel time for duty channel is zero in ACS
-scan.
-
-Root cause:
-The chan_stat may be reset when restore to duty channel.
-Mac80211 will notify to hostapd when scan done and then restore to duty
-channel.
-And mt76 will clear scan flag after restore done.
-If hostapd get the chan_stat before channel_restore, will get the
-correct channel time;
-If hostapd get the chan_stat after channel_restore, will get zero
-channel time;
-
-Solution:
-When channel switch, will check the mac80211 scan state but not the mt76 scan flag.
-Mac80211 scan state will be set in scanning, and will be reset after
-scan done and before restore to duty channel.
-
-Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
----
- mac80211.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/mac80211.c b/mac80211.c
-index e4675bf..d291caa 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -928,6 +928,7 @@ void mt76_set_channel(struct mt76_phy *phy)
- 	struct cfg80211_chan_def *chandef = &hw->conf.chandef;
- 	bool offchannel = hw->conf.flags & IEEE80211_CONF_OFFCHANNEL;
- 	int timeout = HZ / 5;
-+	unsigned long was_scanning = ieee80211_get_scanning(hw);
- 
- 	wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(phy), timeout);
- 	mt76_update_survey(phy);
-@@ -942,7 +943,7 @@ void mt76_set_channel(struct mt76_phy *phy)
- 	if (!offchannel)
- 		phy->main_chan = chandef->chan;
- 
--	if (chandef->chan != phy->main_chan)
-+	if (chandef->chan != phy->main_chan || was_scanning)
- 		memset(phy->chan_state, 0, sizeof(*phy->chan_state));
- }
- EXPORT_SYMBOL_GPL(mt76_set_channel);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0015-mtk-mt76-mt7996-add-driver-support-for-wpa3-ocv-and-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0015-mtk-mt76-mt7996-add-driver-support-for-wpa3-ocv-and-.patch
new file mode 100644
index 0000000..5bebed3
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0015-mtk-mt76-mt7996-add-driver-support-for-wpa3-ocv-and-.patch
@@ -0,0 +1,27 @@
+From 1dc23dac31d10114a55409bd7e29fb1710b6b3ff Mon Sep 17 00:00:00 2001
+From: mtk23510 <rudra.shahi@mediatek.com>
+Date: Fri, 24 Mar 2023 19:18:53 +0800
+Subject: [PATCH 015/199] mtk: mt76: mt7996: add driver support for wpa3 ocv
+ and bp mt76
+
+Signed-off-by: mtk23510 <rudra.shahi@mediatek.com>
+---
+ mt7996/init.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index a47b578c..2d7f42f7 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -391,6 +391,8 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER);
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_PUNCT);
+ 
++	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION);
++	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_PROTECTION);
+ 	if (!mdev->dev->of_node ||
+ 	    !of_property_read_bool(mdev->dev->of_node,
+ 				   "mediatek,disable-radar-background"))
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0015-mtk-wifi-mt76-mt7996-Fixed-null-pointer-dereference-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0015-mtk-wifi-mt76-mt7996-Fixed-null-pointer-dereference-.patch
deleted file mode 100644
index a029391..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0015-mtk-wifi-mt76-mt7996-Fixed-null-pointer-dereference-.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From c8d653d2b80ec1d58e9efd947196ee9d0fbf871b Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Thu, 26 Oct 2023 10:08:10 +0800
-Subject: [PATCH 015/116] mtk: wifi: mt76: mt7996: Fixed null pointer
- dereference issue
-
-Without this patch, when the station is still in Authentication stage and
-sends a "Notify bandwidth change action frame" to AP at the same time,
-there will be a race condition that causes a crash to occur because the AP
-access "msta->vif" that has not been fully initialized.
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Money Wang <money.wang@mediatek.com>
-Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
----
- mt7996/main.c | 7 +++++++
- 1 file changed, 7 insertions(+)
-
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 0e8abe7..e66c607 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -1079,9 +1079,16 @@ static void mt7996_sta_rc_update(struct ieee80211_hw *hw,
- 				 struct ieee80211_sta *sta,
- 				 u32 changed)
- {
-+	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
- 	struct mt7996_dev *dev = phy->dev;
- 
-+	if (!msta->vif) {
-+		dev_warn(dev->mt76.dev, "Un-initialized STA %pM wcid %d in rc_work\n",
-+			 sta->addr, msta->wcid.idx);
-+		return;
-+	}
-+
- 	mt7996_sta_rc_work(&changed, sta);
- 	ieee80211_queue_work(hw, &dev->rc_work);
- }
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0016-mtk-mt76-mt7996-enable-ser-query.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0016-mtk-mt76-mt7996-enable-ser-query.patch
new file mode 100644
index 0000000..26d632b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0016-mtk-mt76-mt7996-enable-ser-query.patch
@@ -0,0 +1,29 @@
+From 60bf48a22df088e5d26f0433b6f28621d73f7240 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 30 Oct 2023 20:19:41 +0800
+Subject: [PATCH 016/199] mtk: mt76: mt7996: enable ser query
+
+Do not return -EINVAL when action is UNI_CMD_SER_QUERY for user
+to dump SER information from FW.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/mcu.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index e01457de..0c300b81 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3868,6 +3868,8 @@ int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 val, u8 band)
+ 	};
+ 
+ 	switch (action) {
++	case UNI_CMD_SER_QUERY:
++		break;
+ 	case UNI_CMD_SER_SET:
+ 		req.set.mask = cpu_to_le32(val);
+ 		break;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0016-mtk-wifi-mt76-add-sanity-check-to-prevent-kernel-cra.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0016-mtk-wifi-mt76-add-sanity-check-to-prevent-kernel-cra.patch
deleted file mode 100644
index ebca1e8..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0016-mtk-wifi-mt76-add-sanity-check-to-prevent-kernel-cra.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From cfac6751faf2d57e1d02c44984f220b770ac6cf3 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Mon, 30 Oct 2023 11:06:19 +0800
-Subject: [PATCH 016/116] mtk: wifi: mt76: add sanity check to prevent kernel
- crash
-
-wcid may not be initialized when mac80211 calls mt76.tx and it would lead to
-kernel crash.
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- tx.c | 8 ++++++++
- 1 file changed, 8 insertions(+)
-
-diff --git a/tx.c b/tx.c
-index 5cf6ede..ab42f69 100644
---- a/tx.c
-+++ b/tx.c
-@@ -345,6 +345,14 @@ mt76_tx(struct mt76_phy *phy, struct ieee80211_sta *sta,
- 
- 	info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->band_idx);
- 
-+	if (!wcid->tx_pending.prev || !wcid->tx_pending.next) {
-+		dev_warn(phy->dev->dev, "Un-initialized STA %pM wcid %d in mt76_tx\n",
-+			 sta->addr, wcid->idx);
-+
-+		ieee80211_free_txskb(phy->hw, skb);
-+		return;
-+	}
-+
- 	spin_lock_bh(&wcid->tx_pending.lock);
- 	__skb_queue_tail(&wcid->tx_pending, skb);
- 	spin_unlock_bh(&wcid->tx_pending.lock);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0017-mtk-mt76-mt7996-set-key-flag-IEEE80211_KEY_FLAG_GENE.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0017-mtk-mt76-mt7996-set-key-flag-IEEE80211_KEY_FLAG_GENE.patch
new file mode 100644
index 0000000..8044c01
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0017-mtk-mt76-mt7996-set-key-flag-IEEE80211_KEY_FLAG_GENE.patch
@@ -0,0 +1,42 @@
+From ad30c992771e60b4150511435ea4f8935b978f02 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 18 Apr 2024 17:21:22 +0800
+Subject: [PATCH 017/199] mtk: mt76: mt7996: set key flag
+ 'IEEE80211_KEY_FLAG_GENERATE_MMIE' for other ciphers
+
+When beacon protection is enabled, FW checks MMIE tag & length in the
+beacon in every cipher mode. Therefore mt76 needs to set the flag
+'IEEE80211_KEY_GENERATE_MMIE' on so that MAC80211 generates and initializes
+MMIE for us.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ mt7996/main.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 8d2b0ee7..40281c5b 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -364,14 +364,14 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ 	case WLAN_CIPHER_SUITE_SMS4:
+ 		break;
+ 	case WLAN_CIPHER_SUITE_AES_CMAC:
+-		wcid_keyidx = &wcid->hw_key_idx2;
+-		key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE;
+-		fallthrough;
+ 	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ 	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ 	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+-		if (key->keyidx == 6 || key->keyidx == 7)
++		if (key->keyidx == 6 || key->keyidx == 7) {
++			wcid_keyidx = &wcid->hw_key_idx2;
++			key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE;
+ 			break;
++		}
+ 		fallthrough;
+ 	case WLAN_CIPHER_SUITE_WEP40:
+ 	case WLAN_CIPHER_SUITE_WEP104:
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0017-mtk-wifi-mt76-mt7996-add-firmware-WA-s-coredump.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0017-mtk-wifi-mt76-mt7996-add-firmware-WA-s-coredump.patch
deleted file mode 100644
index 7ab6d8f..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0017-mtk-wifi-mt76-mt7996-add-firmware-WA-s-coredump.patch
+++ /dev/null
@@ -1,597 +0,0 @@
-From 06ea431d801676bc203ee274503cdb876abcf73c Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Fri, 19 May 2023 14:16:50 +0800
-Subject: [PATCH 017/116] mtk: wifi: mt76: mt7996: add firmware WA's coredump.
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
----
- mt7996/coredump.c | 180 ++++++++++++++++++++++++++++++----------------
- mt7996/coredump.h |  35 ++++++---
- mt7996/mac.c      |  31 +++++---
- mt7996/mcu.c      |   5 ++
- mt7996/mt7996.h   |   7 +-
- mt7996/regs.h     |   7 +-
- 6 files changed, 182 insertions(+), 83 deletions(-)
-
-diff --git a/mt7996/coredump.c b/mt7996/coredump.c
-index ccab0d7..60b8808 100644
---- a/mt7996/coredump.c
-+++ b/mt7996/coredump.c
-@@ -7,11 +7,11 @@
- #include <linux/utsname.h>
- #include "coredump.h"
- 
--static bool coredump_memdump;
-+static bool coredump_memdump = true;
- module_param(coredump_memdump, bool, 0644);
- MODULE_PARM_DESC(coredump_memdump, "Optional ability to dump firmware memory");
- 
--static const struct mt7996_mem_region mt7996_mem_regions[] = {
-+static const struct mt7996_mem_region mt7996_wm_mem_regions[] = {
- 	{
- 		.start = 0x00800000,
- 		.len = 0x0004ffff,
-@@ -44,27 +44,55 @@ static const struct mt7996_mem_region mt7996_mem_regions[] = {
- 	},
- };
- 
-+static const struct mt7996_mem_region mt7996_wa_mem_regions[] = {
-+	{
-+		.start = 0xE0000000,
-+		.len = 0x0000ffff,
-+		.name = "CRAM",
-+	},
-+	{
-+		.start = 0xE0010000,
-+		.len = 0x000117ff,
-+		.name = "CRAM2",
-+	},
-+	{
-+		.start = 0x10000000,
-+		.len = 0x0001bfff,
-+		.name = "ILM",
-+	},
-+	{
-+		.start = 0x10200000,
-+		.len = 0x00063fff,
-+		.name = "DLM",
-+	},
-+};
-+
- const struct mt7996_mem_region*
--mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num)
-+mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num)
- {
- 	switch (mt76_chip(&dev->mt76)) {
- 	case 0x7990:
- 	case 0x7991:
--		*num = ARRAY_SIZE(mt7996_mem_regions);
--		return &mt7996_mem_regions[0];
-+		if (type == MT7996_RAM_TYPE_WA) {
-+			*num = ARRAY_SIZE(mt7996_wa_mem_regions);
-+			return &mt7996_wa_mem_regions[0];
-+		}
-+
-+		*num = ARRAY_SIZE(mt7996_wm_mem_regions);
-+		return &mt7996_wm_mem_regions[0];
- 	default:
- 		return NULL;
- 	}
- }
- 
--static int mt7996_coredump_get_mem_size(struct mt7996_dev *dev)
-+static int mt7996_coredump_get_mem_size(struct mt7996_dev *dev, u8 type)
- {
- 	const struct mt7996_mem_region *mem_region;
- 	size_t size = 0;
- 	u32 num;
- 	int i;
- 
--	mem_region = mt7996_coredump_get_mem_layout(dev, &num);
-+	mem_region = mt7996_coredump_get_mem_layout(dev, type, &num);
- 	if (!mem_region)
- 		return 0;
- 
-@@ -81,14 +109,13 @@ static int mt7996_coredump_get_mem_size(struct mt7996_dev *dev)
- 	return size;
- }
- 
--struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev)
-+struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type)
- {
--	struct mt7996_crash_data *crash_data = dev->coredump.crash_data;
-+	struct mt7996_crash_data *crash_data = dev->coredump.crash_data[type];
- 
- 	lockdep_assert_held(&dev->dump_mutex);
- 
--	if (coredump_memdump &&
--	    !mt76_poll_msec(dev, MT_FW_DUMP_STATE, 0x3, 0x2, 500))
-+	if (!coredump_memdump)
- 		return NULL;
- 
- 	guid_gen(&crash_data->guid);
-@@ -98,12 +125,15 @@ struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev)
- }
- 
- static void
--mt7996_coredump_fw_state(struct mt7996_dev *dev, struct mt7996_coredump *dump,
-+mt7996_coredump_fw_state(struct mt7996_dev *dev, u8 type, struct mt7996_coredump *dump,
- 			 bool *exception)
- {
--	u32 count;
-+	u32 count, reg = MT_FW_WM_DUMP_STATE;
-+
-+	if (type == MT7996_RAM_TYPE_WA)
-+		reg = MT_FW_WA_DUMP_STATE;
- 
--	count = mt76_rr(dev, MT_FW_ASSERT_CNT);
-+	count = mt76_rr(dev, reg);
- 
- 	/* normal mode: driver can manually trigger assert for detail info */
- 	if (!count)
-@@ -115,53 +145,59 @@ mt7996_coredump_fw_state(struct mt7996_dev *dev, struct mt7996_coredump *dump,
- }
- 
- static void
--mt7996_coredump_fw_stack(struct mt7996_dev *dev, struct mt7996_coredump *dump,
-+mt7996_coredump_fw_stack(struct mt7996_dev *dev, u8 type, struct mt7996_coredump *dump,
- 			 bool exception)
- {
--	u32 oldest, i, idx;
-+	u32 reg, i, offset = 0, val = MT7996_RAM_TYPE_WM;
- 
--	strscpy(dump->pc_current, "program counter", sizeof(dump->pc_current));
-+	if (type == MT7996_RAM_TYPE_WA) {
-+		offset = MT_MCU_WA_EXCP_BASE - MT_MCU_WM_EXCP_BASE;
-+		val = MT7996_RAM_TYPE_WA;
-+	}
- 
--	/* 0: WM PC log output */
--	mt76_wr(dev, MT_CONN_DBG_CTL_OUT_SEL, 0);
-+	/* 0: WM PC log output, 1: WA PC log output  */
-+	mt76_wr(dev, MT_CONN_DBG_CTL_OUT_SEL, val);
- 	/* choose 33th PC log buffer to read current PC index */
- 	mt76_wr(dev, MT_CONN_DBG_CTL_PC_LOG_SEL, 0x3f);
- 
- 	/* read current PC */
--	dump->pc_stack[0] = mt76_rr(dev, MT_CONN_DBG_CTL_PC_LOG);
-+	for (i = 0; i < 10; i++)
-+		dump->pc_cur[i] = mt76_rr(dev, MT_CONN_DBG_CTL_PC_LOG);
- 
- 	/* stop call stack record */
- 	if (!exception) {
--		mt76_clear(dev, MT_MCU_WM_EXCP_PC_CTRL, BIT(0));
--		mt76_clear(dev, MT_MCU_WM_EXCP_LR_CTRL, BIT(0));
-+		mt76_clear(dev, MT_MCU_WM_EXCP_PC_CTRL + offset, BIT(0));
-+		mt76_clear(dev, MT_MCU_WM_EXCP_LR_CTRL + offset, BIT(0));
- 	}
- 
--	oldest = (u32)mt76_get_field(dev, MT_MCU_WM_EXCP_PC_CTRL,
--				     GENMASK(20, 16)) + 2;
--	for (i = 0; i < 16; i++) {
--		idx = ((oldest + 2 * i + 1) % 32);
--		dump->pc_stack[i + 1] =
--			mt76_rr(dev, MT_MCU_WM_EXCP_PC_LOG + idx * 4);
-+	/* read PC log */
-+	dump->pc_dbg_ctrl = mt76_rr(dev, MT_MCU_WM_EXCP_PC_CTRL + offset);
-+	dump->pc_cur_idx = FIELD_GET(MT_MCU_WM_EXCP_PC_CTRL_IDX_STATUS,
-+				     dump->pc_dbg_ctrl);
-+	for (i = 0; i < 32; i++) {
-+		reg = MT_MCU_WM_EXCP_PC_LOG + i * 4 + offset;
-+		dump->pc_stack[i] = mt76_rr(dev, reg);
- 	}
- 
--	oldest = (u32)mt76_get_field(dev, MT_MCU_WM_EXCP_LR_CTRL,
--				     GENMASK(20, 16)) + 2;
--	for (i = 0; i < 16; i++) {
--		idx = ((oldest + 2 * i + 1) % 32);
--		dump->lr_stack[i] =
--			mt76_rr(dev, MT_MCU_WM_EXCP_LR_LOG + idx * 4);
-+	/* read LR log */
-+	dump->lr_dbg_ctrl = mt76_rr(dev, MT_MCU_WM_EXCP_LR_CTRL + offset);
-+	dump->lr_cur_idx = FIELD_GET(MT_MCU_WM_EXCP_LR_CTRL_IDX_STATUS,
-+				     dump->lr_dbg_ctrl);
-+	for (i = 0; i < 32; i++) {
-+		reg = MT_MCU_WM_EXCP_LR_LOG + i * 4 + offset;
-+		dump->lr_stack[i] = mt76_rr(dev, reg);
- 	}
- 
- 	/* start call stack record */
- 	if (!exception) {
--		mt76_set(dev, MT_MCU_WM_EXCP_PC_CTRL, BIT(0));
--		mt76_set(dev, MT_MCU_WM_EXCP_LR_CTRL, BIT(0));
-+		mt76_set(dev, MT_MCU_WM_EXCP_PC_CTRL + offset, BIT(0));
-+		mt76_set(dev, MT_MCU_WM_EXCP_LR_CTRL + offset, BIT(0));
- 	}
- }
- 
--static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev)
-+static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type)
- {
--	struct mt7996_crash_data *crash_data = dev->coredump.crash_data;
-+	struct mt7996_crash_data *crash_data = dev->coredump.crash_data[type];
- 	struct mt7996_coredump *dump;
- 	struct mt7996_coredump_mem *dump_mem;
- 	size_t len, sofar = 0, hdr_len = sizeof(*dump);
-@@ -186,20 +222,31 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev)
- 
- 	dump = (struct mt7996_coredump *)(buf);
- 	dump->len = len;
-+	dump->hdr_len = hdr_len;
- 
- 	/* plain text */
- 	strscpy(dump->magic, "mt76-crash-dump", sizeof(dump->magic));
- 	strscpy(dump->kernel, init_utsname()->release, sizeof(dump->kernel));
-+	strscpy(dump->fw_type, ((type == MT7996_RAM_TYPE_WA) ? "WA" : "WM"),
-+		sizeof(dump->fw_type));
- 	strscpy(dump->fw_ver, dev->mt76.hw->wiphy->fw_version,
- 		sizeof(dump->fw_ver));
-+	strscpy(dump->fw_patch_date, dev->patch_build_date,
-+		sizeof(dump->fw_patch_date));
-+	strscpy(dump->fw_ram_date[MT7996_RAM_TYPE_WM],
-+		dev->ram_build_date[MT7996_RAM_TYPE_WM],
-+		MT7996_BUILD_TIME_LEN);
-+	strscpy(dump->fw_ram_date[MT7996_RAM_TYPE_WA],
-+		dev->ram_build_date[MT7996_RAM_TYPE_WA],
-+		MT7996_BUILD_TIME_LEN);
- 
- 	guid_copy(&dump->guid, &crash_data->guid);
- 	dump->tv_sec = crash_data->timestamp.tv_sec;
- 	dump->tv_nsec = crash_data->timestamp.tv_nsec;
- 	dump->device_id = mt76_chip(&dev->mt76);
- 
--	mt7996_coredump_fw_state(dev, dump, &exception);
--	mt7996_coredump_fw_stack(dev, dump, exception);
-+	mt7996_coredump_fw_state(dev, type, dump, &exception);
-+	mt7996_coredump_fw_stack(dev, type, dump, exception);
- 
- 	/* gather memory content */
- 	dump_mem = (struct mt7996_coredump_mem *)(buf + sofar);
-@@ -213,17 +260,19 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev)
- 	return dump;
- }
- 
--int mt7996_coredump_submit(struct mt7996_dev *dev)
-+int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
- {
- 	struct mt7996_coredump *dump;
- 
--	dump = mt7996_coredump_build(dev);
-+	dump = mt7996_coredump_build(dev, type);
- 	if (!dump) {
- 		dev_warn(dev->mt76.dev, "no crash dump data found\n");
- 		return -ENODATA;
- 	}
- 
- 	dev_coredumpv(dev->mt76.dev, dump, dump->len, GFP_KERNEL);
-+	dev_info(dev->mt76.dev, "%s coredump completed\n",
-+		 wiphy_name(dev->mt76.hw->wiphy));
- 
- 	return 0;
- }
-@@ -231,23 +280,26 @@ int mt7996_coredump_submit(struct mt7996_dev *dev)
- int mt7996_coredump_register(struct mt7996_dev *dev)
- {
- 	struct mt7996_crash_data *crash_data;
-+	int i;
- 
--	crash_data = vzalloc(sizeof(*dev->coredump.crash_data));
--	if (!crash_data)
--		return -ENOMEM;
-+	for (i = 0; i < MT7996_COREDUMP_MAX; i++) {
-+		crash_data = vzalloc(sizeof(*dev->coredump.crash_data[i]));
-+		if (!crash_data)
-+			return -ENOMEM;
- 
--	dev->coredump.crash_data = crash_data;
-+		dev->coredump.crash_data[i] = crash_data;
- 
--	if (coredump_memdump) {
--		crash_data->memdump_buf_len = mt7996_coredump_get_mem_size(dev);
--		if (!crash_data->memdump_buf_len)
--			/* no memory content */
--			return 0;
-+		if (coredump_memdump) {
-+			crash_data->memdump_buf_len = mt7996_coredump_get_mem_size(dev, i);
-+			if (!crash_data->memdump_buf_len)
-+				/* no memory content */
-+				return 0;
- 
--		crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len);
--		if (!crash_data->memdump_buf) {
--			vfree(crash_data);
--			return -ENOMEM;
-+			crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len);
-+			if (!crash_data->memdump_buf) {
-+				vfree(crash_data);
-+				return -ENOMEM;
-+			}
- 		}
- 	}
- 
-@@ -256,13 +308,17 @@ int mt7996_coredump_register(struct mt7996_dev *dev)
- 
- void mt7996_coredump_unregister(struct mt7996_dev *dev)
- {
--	if (dev->coredump.crash_data->memdump_buf) {
--		vfree(dev->coredump.crash_data->memdump_buf);
--		dev->coredump.crash_data->memdump_buf = NULL;
--		dev->coredump.crash_data->memdump_buf_len = 0;
--	}
-+	int i;
- 
--	vfree(dev->coredump.crash_data);
--	dev->coredump.crash_data = NULL;
-+	for (i = 0; i < MT7996_COREDUMP_MAX; i++) {
-+		if (dev->coredump.crash_data[i]->memdump_buf) {
-+			vfree(dev->coredump.crash_data[i]->memdump_buf);
-+			dev->coredump.crash_data[i]->memdump_buf = NULL;
-+			dev->coredump.crash_data[i]->memdump_buf_len = 0;
-+		}
-+
-+		vfree(dev->coredump.crash_data[i]);
-+		dev->coredump.crash_data[i] = NULL;
-+	}
- }
- 
-diff --git a/mt7996/coredump.h b/mt7996/coredump.h
-index af2ba21..01ed373 100644
---- a/mt7996/coredump.h
-+++ b/mt7996/coredump.h
-@@ -6,10 +6,13 @@
- 
- #include "mt7996.h"
- 
-+#define MT7996_COREDUMP_MAX	(MT7996_RAM_TYPE_WA + 1)
-+
- struct mt7996_coredump {
- 	char magic[16];
- 
- 	u32 len;
-+	u32 hdr_len;
- 
- 	guid_t guid;
- 
-@@ -21,17 +24,28 @@ struct mt7996_coredump {
- 	char kernel[64];
- 	/* firmware version */
- 	char fw_ver[ETHTOOL_FWVERS_LEN];
-+	char fw_patch_date[MT7996_BUILD_TIME_LEN];
-+	char fw_ram_date[MT7996_COREDUMP_MAX][MT7996_BUILD_TIME_LEN];
- 
- 	u32 device_id;
- 
-+	/* fw type */
-+	char fw_type[8];
-+
- 	/* exception state */
- 	char fw_state[12];
- 
- 	/* program counters */
--	char pc_current[16];
--	u32 pc_stack[17];
--	/* link registers */
--	u32 lr_stack[16];
-+	u32 pc_dbg_ctrl;
-+	u32 pc_cur_idx;
-+	u32 pc_cur[10];
-+	/* PC registers */
-+	u32 pc_stack[32];
-+
-+	u32 lr_dbg_ctrl;
-+	u32 lr_cur_idx;
-+	/* LR registers */
-+	u32 lr_stack[32];
- 
- 	/* memory content */
- 	u8 data[];
-@@ -43,6 +57,7 @@ struct mt7996_coredump_mem {
- } __packed;
- 
- struct mt7996_mem_hdr {
-+	char name[64];
- 	u32 start;
- 	u32 len;
- 	u8 data[];
-@@ -58,27 +73,27 @@ struct mt7996_mem_region {
- #ifdef CONFIG_DEV_COREDUMP
- 
- const struct mt7996_mem_region *
--mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num);
--struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev);
--int mt7996_coredump_submit(struct mt7996_dev *dev);
-+mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num);
-+struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type);
-+int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type);
- int mt7996_coredump_register(struct mt7996_dev *dev);
- void mt7996_coredump_unregister(struct mt7996_dev *dev);
- 
- #else /* CONFIG_DEV_COREDUMP */
- 
- static inline const struct mt7996_mem_region *
--mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num)
-+mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num)
- {
- 	return NULL;
- }
- 
--static inline int mt7996_coredump_submit(struct mt7996_dev *dev)
-+static inline int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
- {
- 	return 0;
- }
- 
- static inline struct
--mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev)
-+mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type)
- {
- 	return NULL;
- }
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 3afdd7e..d88bbfb 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1998,28 +1998,25 @@ void mt7996_mac_reset_work(struct work_struct *work)
- }
- 
- /* firmware coredump */
--void mt7996_mac_dump_work(struct work_struct *work)
-+void mt7996_mac_fw_coredump(struct mt7996_dev *dev, u8 type)
- {
- 	const struct mt7996_mem_region *mem_region;
- 	struct mt7996_crash_data *crash_data;
--	struct mt7996_dev *dev;
- 	struct mt7996_mem_hdr *hdr;
- 	size_t buf_len;
- 	int i;
- 	u32 num;
- 	u8 *buf;
- 
--	dev = container_of(work, struct mt7996_dev, dump_work);
--
- 	mutex_lock(&dev->dump_mutex);
- 
--	crash_data = mt7996_coredump_new(dev);
-+	crash_data = mt7996_coredump_new(dev, type);
- 	if (!crash_data) {
- 		mutex_unlock(&dev->dump_mutex);
--		goto skip_coredump;
-+		return;
- 	}
- 
--	mem_region = mt7996_coredump_get_mem_layout(dev, &num);
-+	mem_region = mt7996_coredump_get_mem_layout(dev, type, &num);
- 	if (!mem_region || !crash_data->memdump_buf_len) {
- 		mutex_unlock(&dev->dump_mutex);
- 		goto skip_memdump;
-@@ -2029,6 +2026,9 @@ void mt7996_mac_dump_work(struct work_struct *work)
- 	buf_len = crash_data->memdump_buf_len;
- 
- 	/* dumping memory content... */
-+	dev_info(dev->mt76.dev, "%s start coredump for %s\n",
-+		 wiphy_name(dev->mt76.hw->wiphy),
-+		 ((type == MT7996_RAM_TYPE_WA) ? "WA" : "WM"));
- 	memset(buf, 0, buf_len);
- 	for (i = 0; i < num; i++) {
- 		if (mem_region->len > buf_len) {
-@@ -2045,6 +2045,7 @@ void mt7996_mac_dump_work(struct work_struct *work)
- 		mt7996_memcpy_fromio(dev, buf, mem_region->start,
- 				     mem_region->len);
- 
-+		strscpy(hdr->name, mem_region->name, sizeof(mem_region->name));
- 		hdr->start = mem_region->start;
- 		hdr->len = mem_region->len;
- 
-@@ -2061,8 +2062,20 @@ void mt7996_mac_dump_work(struct work_struct *work)
- 	mutex_unlock(&dev->dump_mutex);
- 
- skip_memdump:
--	mt7996_coredump_submit(dev);
--skip_coredump:
-+	mt7996_coredump_submit(dev, type);
-+}
-+
-+void mt7996_mac_dump_work(struct work_struct *work)
-+{
-+	struct mt7996_dev *dev;
-+
-+	dev = container_of(work, struct mt7996_dev, dump_work);
-+	if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT)
-+		mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WA);
-+
-+	if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WM_WDT)
-+		mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WM);
-+
- 	queue_work(dev->mt76.wq, &dev->reset_work);
- }
- 
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index fb6ee7c..f4bbb78 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -2704,6 +2704,8 @@ static int mt7996_load_patch(struct mt7996_dev *dev)
- 
- 	dev_info(dev->mt76.dev, "HW/SW Version: 0x%x, Build Time: %.16s\n",
- 		 be32_to_cpu(hdr->hw_sw_ver), hdr->build_date);
-+	memcpy(dev->patch_build_date, hdr->build_date,
-+	       sizeof(dev->patch_build_date));
- 
- 	for (i = 0; i < be32_to_cpu(hdr->desc.n_region); i++) {
- 		struct mt7996_patch_sec *sec;
-@@ -2830,6 +2832,9 @@ static int __mt7996_load_ram(struct mt7996_dev *dev, const char *fw_type,
- 	}
- 
- 	hdr = (const void *)(fw->data + fw->size - sizeof(*hdr));
-+	memcpy(dev->ram_build_date[ram_type],
-+	       hdr->build_date,
-+	       sizeof(dev->ram_build_date[ram_type]));
- 	dev_info(dev->mt76.dev, "%s Firmware Version: %.10s, Build Time: %.15s\n",
- 		 fw_type, hdr->fw_ver, hdr->build_date);
- 
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index b7197dc..e12ad31 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -84,6 +84,8 @@
- #define MT7996_CRIT_TEMP		110
- #define MT7996_MAX_TEMP			120
- 
-+#define MT7996_BUILD_TIME_LEN		24
-+
- #define MT7996_RRO_MAX_SESSION		1024
- #define MT7996_RRO_WINDOW_MAX_LEN	1024
- #define MT7996_RRO_ADDR_ELEM_LEN	128
-@@ -127,6 +129,7 @@ enum mt7996_ram_type {
- 	MT7996_RAM_TYPE_WM,
- 	MT7996_RAM_TYPE_WA,
- 	MT7996_RAM_TYPE_DSP,
-+	__MT7996_RAM_TYPE_MAX,
- };
- 
- enum mt7996_txq_id {
-@@ -320,9 +323,11 @@ struct mt7996_dev {
- 	struct mutex dump_mutex;
- #ifdef CONFIG_DEV_COREDUMP
- 	struct {
--		struct mt7996_crash_data *crash_data;
-+		struct mt7996_crash_data *crash_data[__MT7996_RAM_TYPE_MAX];
- 	} coredump;
- #endif
-+	char patch_build_date[MT7996_BUILD_TIME_LEN];
-+	char ram_build_date[__MT7996_RAM_TYPE_MAX][MT7996_BUILD_TIME_LEN];
- 
- 	struct list_head sta_rc_list;
- 	struct list_head twt_list;
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index cf12c5e..4c20a67 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -597,7 +597,8 @@ enum offs_rev {
- 
- /* FW MODE SYNC */
- #define MT_FW_ASSERT_CNT			0x02208274
--#define MT_FW_DUMP_STATE			0x02209e90
-+#define MT_FW_WM_DUMP_STATE			0x02209e90
-+#define MT_FW_WA_DUMP_STATE			0x7C05B080
- 
- #define MT_SWDEF_BASE				0x00401400
- 
-@@ -714,11 +715,15 @@ enum offs_rev {
- #define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR		BIT(29)
- 
- /* CONN MCU EXCP CON */
-+#define MT_MCU_WA_EXCP_BASE			0x890d0000
- #define MT_MCU_WM_EXCP_BASE			0x89050000
-+
- #define MT_MCU_WM_EXCP(ofs)			(MT_MCU_WM_EXCP_BASE + (ofs))
- #define MT_MCU_WM_EXCP_PC_CTRL			MT_MCU_WM_EXCP(0x100)
-+#define MT_MCU_WM_EXCP_PC_CTRL_IDX_STATUS	GENMASK(20, 16)
- #define MT_MCU_WM_EXCP_PC_LOG			MT_MCU_WM_EXCP(0x104)
- #define MT_MCU_WM_EXCP_LR_CTRL			MT_MCU_WM_EXCP(0x200)
-+#define MT_MCU_WM_EXCP_LR_CTRL_IDX_STATUS	GENMASK(20, 16)
- #define MT_MCU_WM_EXCP_LR_LOG			MT_MCU_WM_EXCP(0x204)
- 
- /* CONN AFE CTL CON */
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0018-mtk-mt76-mt7996-Fix-TGax-HE-4.51.1_24G-fail.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0018-mtk-mt76-mt7996-Fix-TGax-HE-4.51.1_24G-fail.patch
new file mode 100644
index 0000000..19d0b38
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0018-mtk-mt76-mt7996-Fix-TGax-HE-4.51.1_24G-fail.patch
@@ -0,0 +1,34 @@
+From 805f8428e1f5d7fc4d466d66c9104588df0c65c5 Mon Sep 17 00:00:00 2001
+From: mtk27745 <rex.lu@mediatek.com>
+Date: Fri, 17 Nov 2023 11:01:04 +0800
+Subject: [PATCH 018/199] mtk: mt76: mt7996: Fix TGax HE-4.51.1_24G fail
+
+According to sta capability to decide to enable/disable wed pao when create ppe entry.
+without this patch, TGax HE-4.51.1_24G will test fail
+
+Signed-off-by: mtk27745 <rex.lu@mediatek.com>
+---
+ mt7996/main.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 40281c5b..cbe8b009 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1467,7 +1467,12 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ 	path->mtk_wdma.queue = 0;
+ 	path->mtk_wdma.wcid = msta->wcid.idx;
+ 
+-	path->mtk_wdma.amsdu = mtk_wed_is_amsdu_supported(wed);
++	if (ieee80211_hw_check(hw, SUPPORTS_AMSDU_IN_AMPDU) &&
++	    mtk_wed_is_amsdu_supported(wed))
++		path->mtk_wdma.amsdu = msta->wcid.amsdu;
++	else
++		path->mtk_wdma.amsdu = 0;
++
+ 	ctx->dev = NULL;
+ 
+ 	return 0;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0018-mtk-wifi-mt76-mt7996-for-build-pass.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0018-mtk-wifi-mt76-mt7996-for-build-pass.patch
deleted file mode 100644
index 001f887..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0018-mtk-wifi-mt76-mt7996-for-build-pass.patch
+++ /dev/null
@@ -1,136 +0,0 @@
-From e3182cb0870f1d702a01deecdf62493832ab4fd9 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 3 Nov 2022 00:27:17 +0800
-Subject: [PATCH 018/116] mtk: wifi: mt76: mt7996: for build pass
-
----
- debugfs.c         | 3 +++
- dma.c             | 2 +-
- mcu.c             | 1 +
- mt7615/mcu.c      | 1 +
- mt76_connac_mcu.c | 1 +
- mt7915/mcu.c      | 1 +
- mt7996/dma.c      | 4 ++--
- mt7996/eeprom.c   | 1 +
- mt7996/mcu.c      | 1 +
- 9 files changed, 12 insertions(+), 3 deletions(-)
-
-diff --git a/debugfs.c b/debugfs.c
-index c4649ba..ac5207e 100644
---- a/debugfs.c
-+++ b/debugfs.c
-@@ -33,8 +33,11 @@ mt76_napi_threaded_set(void *data, u64 val)
- 	if (!mt76_is_mmio(dev))
- 		return -EOPNOTSUPP;
- 
-+#if 0
-+	/* need to backport patch from networking stack */
- 	if (dev->napi_dev.threaded != val)
- 		return dev_set_threaded(&dev->napi_dev, val);
-+#endif
- 
- 	return 0;
- }
-diff --git a/dma.c b/dma.c
-index f4f88c4..5604463 100644
---- a/dma.c
-+++ b/dma.c
-@@ -883,7 +883,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
- 		    !(dev->drv->rx_check(dev, data, len)))
- 			goto free_frag;
- 
--		skb = napi_build_skb(data, q->buf_size);
-+		skb = build_skb(data, q->buf_size);
- 		if (!skb)
- 			goto free_frag;
- 
-diff --git a/mcu.c b/mcu.c
-index a8cafa3..fa4b054 100644
---- a/mcu.c
-+++ b/mcu.c
-@@ -4,6 +4,7 @@
-  */
- 
- #include "mt76.h"
-+#include <linux/moduleparam.h>
- 
- struct sk_buff *
- __mt76_mcu_msg_alloc(struct mt76_dev *dev, const void *data,
-diff --git a/mt7615/mcu.c b/mt7615/mcu.c
-index c807bd8..a931066 100644
---- a/mt7615/mcu.c
-+++ b/mt7615/mcu.c
-@@ -10,6 +10,7 @@
- #include "mcu.h"
- #include "mac.h"
- #include "eeprom.h"
-+#include <linux/moduleparam.h>
- 
- static bool prefer_offload_fw = true;
- module_param(prefer_offload_fw, bool, 0644);
-diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
-index b35acf8..1e34e0a 100644
---- a/mt76_connac_mcu.c
-+++ b/mt76_connac_mcu.c
-@@ -4,6 +4,7 @@
- #include <linux/firmware.h>
- #include "mt76_connac2_mac.h"
- #include "mt76_connac_mcu.h"
-+#include <linux/module.h>
- 
- int mt76_connac_mcu_start_firmware(struct mt76_dev *dev, u32 addr, u32 option)
- {
-diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index fc194e0..363ea0e 100644
---- a/mt7915/mcu.c
-+++ b/mt7915/mcu.c
-@@ -6,6 +6,7 @@
- #include "mcu.h"
- #include "mac.h"
- #include "eeprom.h"
-+#include <linux/moduleparam.h>
- 
- #define fw_name(_dev, name, ...)	({			\
- 	char *_fw;						\
-diff --git a/mt7996/dma.c b/mt7996/dma.c
-index 73e633d..759a58e 100644
---- a/mt7996/dma.c
-+++ b/mt7996/dma.c
-@@ -641,8 +641,8 @@ int mt7996_dma_init(struct mt7996_dev *dev)
- 	if (ret < 0)
- 		return ret;
- 
--	netif_napi_add_tx(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
--			  mt7996_poll_tx);
-+	netif_tx_napi_add(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
-+			  mt7996_poll_tx, NAPI_POLL_WEIGHT);
- 	napi_enable(&dev->mt76.tx_napi);
- 
- 	mt7996_dma_enable(dev, false);
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 3260d1f..121a3c9 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -138,6 +138,7 @@ static int mt7996_eeprom_parse_efuse_hw_cap(struct mt7996_dev *dev)
- 	if (ret)
- 		return ret;
- 
-+	cap = 0x4b249248;	/* internal hardcode */
- 	if (cap) {
- 		dev->has_eht = !(cap & MODE_HE_ONLY);
- 		dev->wtbl_size_group = u32_get_bits(cap, WTBL_SIZE_GROUP);
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index f4bbb78..90332fe 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -5,6 +5,7 @@
- 
- #include <linux/firmware.h>
- #include <linux/fs.h>
-+#include <linux/moduleparam.h>
- #include "mt7996.h"
- #include "mcu.h"
- #include "mac.h"
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0019-mtk-mt76-mt7996-add-support-for-different-variants.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0019-mtk-mt76-mt7996-add-support-for-different-variants.patch
new file mode 100644
index 0000000..3b06798
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0019-mtk-mt76-mt7996-add-support-for-different-variants.patch
@@ -0,0 +1,325 @@
+From 56eef7fe97e8010ac211973868ab9f5753fb56d3 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 20 Jul 2023 17:27:22 +0800
+Subject: [PATCH 019/199] mtk: mt76: mt7996: add support for different variants
+
+Add fem type (2i5i, 2i5e, 2e5e, ...)
+Add Kite default bin for each fem type since loading wrong default bin
+will fail to setup interface
+Add eeprom fem type check
+
+Add adie 7976c efuse check
+Efuse offset 0x470 will be set to 0xc after final test if 7976c adie is used
+Chip manufactoring factories may transfer, which leads to different adie chip versions,
+so we add this efuse check to avoid 7976c recognition failure.
+
+GPIO ADie Combination of BE5040 should be considered as don't care
+instead of 0
+
+Only check eeprom chip id when fem type (= MT7996_FEM_UNSET) is not determined yet
+Without this fix, mt7996_check_eeprom will return EINVAL in mt7996_eeprom_check_fw_mode
+
+Align the naming rule of the default eeprom bin
+efem: XX_DEFAULT
+ifem: XX_DEFAULT_INT
+mixed fem: XX_DEFAULT_MIX
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/eeprom.c | 40 +++++++++++++++++++++++++++++--
+ mt7996/eeprom.h |  1 +
+ mt7996/init.c   | 63 +++++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.c    |  7 +++++-
+ mt7996/mt7996.h | 43 ++++++++++++++++++++++++++++++---
+ mt7996/regs.h   |  7 ++++++
+ 6 files changed, 155 insertions(+), 6 deletions(-)
+
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 4a823711..f38629e3 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -9,14 +9,33 @@
+ 
+ static int mt7996_check_eeprom(struct mt7996_dev *dev)
+ {
++#define FEM_INT				0
++#define FEM_EXT				3
+ 	u8 *eeprom = dev->mt76.eeprom.data;
++	u8 i, fem[__MT_MAX_BAND], fem_type;
+ 	u16 val = get_unaligned_le16(eeprom);
+ 
++	for (i = 0; i < __MT_MAX_BAND; i++)
++		fem[i] = eeprom[MT_EE_WIFI_CONF + 6 + i] & MT_EE_WIFI_PA_LNA_CONFIG;
++
+ 	switch (val) {
+ 	case 0x7990:
+ 		return is_mt7996(&dev->mt76) ? 0 : -EINVAL;
+ 	case 0x7992:
+-		return is_mt7992(&dev->mt76) ? 0 : -EINVAL;
++		if (dev->fem_type == MT7996_FEM_UNSET)
++			return is_mt7992(&dev->mt76) ? 0 : -EINVAL;
++
++		if (fem[0] == FEM_EXT && fem[1] == FEM_EXT)
++			fem_type = MT7996_FEM_EXT;
++		else if (fem[0] == FEM_INT && fem[1] == FEM_INT)
++			fem_type = MT7996_FEM_INT;
++		else if (fem[0] == FEM_INT && fem[1] == FEM_EXT)
++			fem_type = MT7996_FEM_MIX;
++		else
++			return -EINVAL;
++
++		return (is_mt7992(&dev->mt76) ? 0 : -EINVAL) |
++		       (dev->fem_type == fem_type ? 0 : -EINVAL);
+ 	default:
+ 		return -EINVAL;
+ 	}
+@@ -26,9 +45,22 @@ static char *mt7996_eeprom_name(struct mt7996_dev *dev)
+ {
+ 	switch (mt76_chip(&dev->mt76)) {
+ 	case 0x7990:
++		if (dev->chip_sku == MT7996_SKU_404)
++			return MT7996_EEPROM_DEFAULT_404;
+ 		return MT7996_EEPROM_DEFAULT;
+ 	case 0x7992:
+-		return MT7992_EEPROM_DEFAULT;
++		if (dev->chip_sku == MT7992_SKU_23) {
++			if (dev->fem_type == MT7996_FEM_INT)
++				return MT7992_EEPROM_DEFAULT_23_INT;
++			return MT7992_EEPROM_DEFAULT_23;
++		} else if (dev->chip_sku == MT7992_SKU_44) {
++			if (dev->fem_type == MT7996_FEM_INT)
++				return MT7992_EEPROM_DEFAULT_INT;
++			else if (dev->fem_type == MT7996_FEM_MIX)
++				return MT7992_EEPROM_DEFAULT_MIX;
++			return MT7992_EEPROM_DEFAULT;
++		}
++		return MT7992_EEPROM_DEFAULT_24;
+ 	default:
+ 		return MT7996_EEPROM_DEFAULT;
+ 	}
+@@ -219,6 +251,10 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ {
+ 	int ret;
+ 
++	ret = mt7996_get_chip_sku(dev);
++	if (ret)
++		return ret;
++
+ 	ret = mt7996_eeprom_load(dev);
+ 	if (ret < 0) {
+ 		if (ret != -EINVAL)
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index 412d6e2f..72c38ad3 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -29,6 +29,7 @@ enum mt7996_eeprom_field {
+ #define MT_EE_WIFI_CONF0_BAND_SEL		GENMASK(2, 0)
+ #define MT_EE_WIFI_CONF1_BAND_SEL		GENMASK(5, 3)
+ #define MT_EE_WIFI_CONF2_BAND_SEL		GENMASK(2, 0)
++#define MT_EE_WIFI_PA_LNA_CONFIG		GENMASK(1, 0)
+ 
+ #define MT_EE_WIFI_CONF1_TX_PATH_BAND0		GENMASK(5, 3)
+ #define MT_EE_WIFI_CONF2_TX_PATH_BAND1		GENMASK(2, 0)
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 2d7f42f7..f80fba28 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -887,6 +887,65 @@ out:
+ #endif
+ }
+ 
++int mt7996_get_chip_sku(struct mt7996_dev *dev)
++{
++#define MT7976C_CHIP_VER	0x8a10
++#define MT7976C_HL_CHIP_VER	0x8b00
++#define MT7976C_PS_CHIP_VER	0x8c10
++#define MT7976C_EFUSE_OFFSET	0x470
++#define MT7976C_EFUSE_VALUE	0xc
++	u32 regval, val = mt76_rr(dev, MT_PAD_GPIO);
++	u16 adie_chip_id, adie_chip_ver;
++	u8 adie_comb, adie_num, adie_idx = 0;
++
++	switch (mt76_chip(&dev->mt76)) {
++	case 0x7990:
++		adie_comb = FIELD_GET(MT_PAD_GPIO_ADIE_COMB, val);
++		if (adie_comb <= 1)
++			dev->chip_sku = MT7996_SKU_444;
++		else
++			dev->chip_sku = MT7996_SKU_404;
++		break;
++	case 0x7992:
++		adie_comb = FIELD_GET(MT_PAD_GPIO_ADIE_COMB_7992, val);
++		adie_num = FIELD_GET(MT_PAD_GPIO_ADIE_NUM_7992, val);
++		adie_idx = !adie_num;
++		if (adie_num)
++			dev->chip_sku = MT7992_SKU_23;
++		else if (adie_comb)
++			dev->chip_sku = MT7992_SKU_44;
++		else
++			dev->chip_sku = MT7992_SKU_24;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) {
++		u8 buf[MT7996_EEPROM_BLOCK_SIZE];
++		u8 idx = MT7976C_EFUSE_OFFSET % MT7996_EEPROM_BLOCK_SIZE;
++		bool is_7976c;
++
++		mt7996_mcu_rf_regval(dev, MT_ADIE_CHIP_ID(adie_idx), &regval, false);
++		adie_chip_id = FIELD_GET(MT_ADIE_CHIP_ID_MASK, regval);
++		adie_chip_ver = FIELD_GET(MT_ADIE_VERSION_MASK, regval);
++		mt7996_mcu_get_eeprom(dev, MT7976C_EFUSE_OFFSET, buf);
++		is_7976c = (adie_chip_ver == MT7976C_CHIP_VER) ||
++			   (adie_chip_ver == MT7976C_HL_CHIP_VER) ||
++			   (adie_chip_ver == MT7976C_PS_CHIP_VER) ||
++			   (buf[idx] == MT7976C_EFUSE_VALUE);
++		if (adie_chip_id == 0x7975 || (adie_chip_id == 0x7976 && is_7976c) ||
++		    adie_chip_id == 0x7979)
++			dev->fem_type = MT7996_FEM_INT;
++		else if (adie_chip_id == 0x7977 && adie_comb == 1)
++			dev->fem_type = MT7996_FEM_MIX;
++		else
++			dev->fem_type = MT7996_FEM_EXT;
++	}
++
++	return 0;
++}
++
+ static int mt7996_init_hardware(struct mt7996_dev *dev)
+ {
+ 	int ret, idx;
+@@ -902,6 +961,10 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
+ 	INIT_LIST_HEAD(&dev->wed_rro.poll_list);
+ 	spin_lock_init(&dev->wed_rro.lock);
+ 
++	ret = mt7996_get_chip_sku(dev);
++	if (ret)
++		return ret;
++
+ 	ret = mt7996_dma_init(dev);
+ 	if (ret)
+ 		return ret;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 0c300b81..ae01a4eb 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -14,7 +14,12 @@
+ 	char *_fw;						\
+ 	switch (mt76_chip(&(_dev)->mt76)) {			\
+ 	case 0x7992:						\
+-		_fw = MT7992_##name;				\
++		if ((_dev)->chip_sku == MT7992_SKU_23)		\
++			_fw = MT7992_##name##_23;		\
++		else if ((_dev)->chip_sku == MT7992_SKU_24)	\
++			_fw = MT7992_##name##_24;		\
++		else						\
++			_fw = MT7992_##name;			\
+ 		break;						\
+ 	case 0x7990:						\
+ 	default:						\
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 58fa6b45..16cefeac 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -39,8 +39,24 @@
+ #define MT7992_FIRMWARE_DSP		"mediatek/mt7996/mt7992_dsp.bin"
+ #define MT7992_ROM_PATCH		"mediatek/mt7996/mt7992_rom_patch.bin"
+ 
++#define MT7992_FIRMWARE_WA_24		"mediatek/mt7996/mt7992_wa_24.bin"
++#define MT7992_FIRMWARE_WM_24		"mediatek/mt7996/mt7992_wm_24.bin"
++#define MT7992_FIRMWARE_DSP_24		"mediatek/mt7996/mt7992_dsp_24.bin"
++#define MT7992_ROM_PATCH_24		"mediatek/mt7996/mt7992_rom_patch_24.bin"
++
++#define MT7992_FIRMWARE_WA_23		"mediatek/mt7996/mt7992_wa_23.bin"
++#define MT7992_FIRMWARE_WM_23		"mediatek/mt7996/mt7992_wm_23.bin"
++#define MT7992_FIRMWARE_DSP_23		"mediatek/mt7996/mt7992_dsp_23.bin"
++#define MT7992_ROM_PATCH_23		"mediatek/mt7996/mt7992_rom_patch_23.bin"
++
+ #define MT7996_EEPROM_DEFAULT		"mediatek/mt7996/mt7996_eeprom.bin"
+-#define MT7992_EEPROM_DEFAULT		"mediatek/mt7996/mt7992_eeprom.bin"
++#define MT7996_EEPROM_DEFAULT_404	"mediatek/mt7996/mt7996_eeprom_dual_404.bin"
++#define MT7992_EEPROM_DEFAULT		"mediatek/mt7996/mt7992_eeprom_2e5e.bin"
++#define MT7992_EEPROM_DEFAULT_INT	"mediatek/mt7996/mt7992_eeprom_2i5i.bin"
++#define MT7992_EEPROM_DEFAULT_MIX	"mediatek/mt7996/mt7992_eeprom_2i5e.bin"
++#define MT7992_EEPROM_DEFAULT_24	"mediatek/mt7996/mt7992_eeprom_24_2i5i.bin"
++#define MT7992_EEPROM_DEFAULT_23	"mediatek/mt7996/mt7992_eeprom_23_2e5e.bin"
++#define MT7992_EEPROM_DEFAULT_23_INT	"mediatek/mt7996/mt7992_eeprom_23_2i5i.bin"
+ #define MT7996_EEPROM_SIZE		7680
+ #define MT7996_EEPROM_BLOCK_SIZE	16
+ #define MT7996_TOKEN_SIZE		16384
+@@ -89,6 +105,24 @@ struct mt7996_sta;
+ struct mt7996_dfs_pulse;
+ struct mt7996_dfs_pattern;
+ 
++enum mt7996_fem_type {
++	MT7996_FEM_UNSET,
++	MT7996_FEM_EXT,
++	MT7996_FEM_INT,
++	MT7996_FEM_MIX,
++};
++
++enum mt7996_sku_type {
++	MT7996_SKU_404,
++	MT7996_SKU_444,
++};
++
++enum mt7992_sku_type {
++	MT7992_SKU_23,
++	MT7992_SKU_24,
++	MT7992_SKU_44,
++};
++
+ enum mt7996_ram_type {
+ 	MT7996_RAM_TYPE_WM,
+ 	MT7996_RAM_TYPE_WA,
+@@ -261,6 +295,9 @@ struct mt7996_dev {
+ 	struct cfg80211_chan_def rdd2_chandef;
+ 	struct mt7996_phy *rdd2_phy;
+ 
++	u8 chip_sku;
++	u8 fem_type;
++
+ 	u16 chainmask;
+ 	u8 chainshift[__MT_MAX_BAND];
+ 	u32 hif_idx;
+@@ -409,8 +446,7 @@ mt7996_band_valid(struct mt7996_dev *dev, u8 band)
+ 		return band <= MT_BAND1;
+ 
+ 	/* tri-band support */
+-	if (band <= MT_BAND2 &&
+-	    mt76_get_field(dev, MT_PAD_GPIO, MT_PAD_GPIO_ADIE_COMB) <= 1)
++	if (band <= MT_BAND2 && dev->chip_sku)
+ 		return true;
+ 
+ 	return band == MT_BAND0 || band == MT_BAND2;
+@@ -441,6 +477,7 @@ int mt7996_init_tx_queues(struct mt7996_phy *phy, int idx,
+ 			  int n_desc, int ring_base, struct mtk_wed_device *wed);
+ void mt7996_init_txpower(struct mt7996_phy *phy);
+ int mt7996_txbf_init(struct mt7996_dev *dev);
++int mt7996_get_chip_sku(struct mt7996_dev *dev);
+ void mt7996_reset(struct mt7996_dev *dev);
+ int mt7996_run(struct ieee80211_hw *hw);
+ int mt7996_mcu_init(struct mt7996_dev *dev);
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index 47b429d8..cf12c5e0 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -662,6 +662,13 @@ enum offs_rev {
+ 
+ #define MT_PAD_GPIO				0x700056f0
+ #define MT_PAD_GPIO_ADIE_COMB			GENMASK(16, 15)
++#define MT_PAD_GPIO_ADIE_COMB_7992		GENMASK(17, 16)
++#define MT_PAD_GPIO_ADIE_NUM_7992		BIT(15)
++
++/* ADIE */
++#define MT_ADIE_CHIP_ID(_idx)                  (0x0f00002c + ((_idx) << 28))
++#define MT_ADIE_VERSION_MASK                   GENMASK(15, 0)
++#define MT_ADIE_CHIP_ID_MASK                   GENMASK(31, 16)
+ 
+ #define MT_HW_REV				0x70010204
+ #define MT_HW_REV1				0x8a00
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0019-mtk-wifi-mt76-mt7996-add-debug-tool.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0019-mtk-wifi-mt76-mt7996-add-debug-tool.patch
deleted file mode 100644
index e6aae93..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0019-mtk-wifi-mt76-mt7996-add-debug-tool.patch
+++ /dev/null
@@ -1,5413 +0,0 @@
-From fe15bfb3335bcbdf95fed1563646d84154607b42 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Fri, 24 Mar 2023 14:02:32 +0800
-Subject: [PATCH 019/116] mtk: wifi: mt76: mt7996: add debug tool
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Add PSM bit in sta_info
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
-
-Remove the duplicate function in mtk_debugfs.c & mtk_debug_i.c
-Only enable mt7996_mcu_fw_log_2_host function in mcu.c
-
-Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
-
-Support more ids category NDPA/NDP TXD/FBK and debug log recommended by
-CTD members.
-
-This commit equals to run the follwoing commands on Logan driver:
-command:
-1. iwpriv ra0 set fw_dbg=1:84
-2. iwpriv ra0 set fw_dbg=2:84
-3. iwpriv ra0 set fw_dbg=1:101
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
-
-mtk: wifi: mt76: mt7996: add wtbl_info support for mt7992
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-mtk: wifi: mt76: mt7996: add mt7992 & mt7996 CR debug offset revision
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-mtk: wifi: mt76: mt7992: refactor code for FW log
-
-Refactor code for FW log.
-
-Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
-
-mtk: wifi: mt76: mt7996: support disable muru debug info when recording fwlog
-
-When we record fwlog, we will also enable recording muru debug info log by
-default. However, in certain test scenarios, this can result in
-recording too many logs, causing inconvenience during issue analysis.
-Therefore, this commit adds an debug option, fw_debug_muru_disable, in
-debugfs. User can modify this option to enable/disable recording muru
-debug info log.
-
-[Usage]
-Set:
-$ echo val > debugfs/fw_debug_muru_disable
-Get:
-$ cat debugfs/fw_debug_muru_disable
-
-val can be the following values:
-0 = enable recording muru debug info (Default value)
-1 = disable recording muru debug info
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
-
-mtk: wifi: mt76: mt7996: add adie id & ver dump
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt76.h               |    2 +
- mt7996/Makefile      |    4 +
- mt7996/coredump.c    |   10 +-
- mt7996/coredump.h    |    7 +
- mt7996/debugfs.c     |  128 ++-
- mt7996/mac.c         |    3 +
- mt7996/mt7996.h      |   13 +
- mt7996/mtk_debug.h   | 2286 ++++++++++++++++++++++++++++++++++++++
- mt7996/mtk_debugfs.c | 2507 ++++++++++++++++++++++++++++++++++++++++++
- mt7996/mtk_mcu.c     |   39 +
- mt7996/mtk_mcu.h     |   19 +
- tools/fwlog.c        |   25 +-
- 12 files changed, 5018 insertions(+), 25 deletions(-)
- create mode 100644 mt7996/mtk_debug.h
- create mode 100644 mt7996/mtk_debugfs.c
- create mode 100644 mt7996/mtk_mcu.c
- create mode 100644 mt7996/mtk_mcu.h
-
-diff --git a/mt76.h b/mt76.h
-index 7f3ec40..b421a98 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -399,6 +399,8 @@ struct mt76_txwi_cache {
- 		struct sk_buff *skb;
- 		void *ptr;
- 	};
-+
-+	unsigned long jiffies;
- };
- 
- struct mt76_rx_tid {
-diff --git a/mt7996/Makefile b/mt7996/Makefile
-index 07c8b55..a056b40 100644
---- a/mt7996/Makefile
-+++ b/mt7996/Makefile
-@@ -1,4 +1,6 @@
- # SPDX-License-Identifier: ISC
-+EXTRA_CFLAGS += -DCONFIG_MT76_LEDS
-+EXTRA_CFLAGS += -DCONFIG_MTK_DEBUG
- 
- obj-$(CONFIG_MT7996E) += mt7996e.o
- 
-@@ -6,3 +8,5 @@ mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
- 	     debugfs.o mmio.o
- 
- mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o
-+
-+mt7996e-y += mtk_debugfs.o mtk_mcu.o
-diff --git a/mt7996/coredump.c b/mt7996/coredump.c
-index 60b8808..a7f91b5 100644
---- a/mt7996/coredump.c
-+++ b/mt7996/coredump.c
-@@ -195,7 +195,7 @@ mt7996_coredump_fw_stack(struct mt7996_dev *dev, u8 type, struct mt7996_coredump
- 	}
- }
- 
--static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type)
-+struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type, bool full_dump)
- {
- 	struct mt7996_crash_data *crash_data = dev->coredump.crash_data[type];
- 	struct mt7996_coredump *dump;
-@@ -206,7 +206,7 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8
- 
- 	len = hdr_len;
- 
--	if (coredump_memdump && crash_data->memdump_buf_len)
-+	if (full_dump && coredump_memdump && crash_data->memdump_buf_len)
- 		len += sizeof(*dump_mem) + crash_data->memdump_buf_len;
- 
- 	sofar += hdr_len;
-@@ -248,6 +248,9 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8
- 	mt7996_coredump_fw_state(dev, type, dump, &exception);
- 	mt7996_coredump_fw_stack(dev, type, dump, exception);
- 
-+	if (!full_dump)
-+		goto skip_dump_mem;
-+
- 	/* gather memory content */
- 	dump_mem = (struct mt7996_coredump_mem *)(buf + sofar);
- 	dump_mem->len = crash_data->memdump_buf_len;
-@@ -255,6 +258,7 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8
- 		memcpy(dump_mem->data, crash_data->memdump_buf,
- 		       crash_data->memdump_buf_len);
- 
-+skip_dump_mem:
- 	mutex_unlock(&dev->dump_mutex);
- 
- 	return dump;
-@@ -264,7 +268,7 @@ int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
- {
- 	struct mt7996_coredump *dump;
- 
--	dump = mt7996_coredump_build(dev, type);
-+	dump = mt7996_coredump_build(dev, type, true);
- 	if (!dump) {
- 		dev_warn(dev->mt76.dev, "no crash dump data found\n");
- 		return -ENODATA;
-diff --git a/mt7996/coredump.h b/mt7996/coredump.h
-index 01ed373..93cd84a 100644
---- a/mt7996/coredump.h
-+++ b/mt7996/coredump.h
-@@ -75,6 +75,7 @@ struct mt7996_mem_region {
- const struct mt7996_mem_region *
- mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num);
- struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type);
-+struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type, bool full_dump);
- int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type);
- int mt7996_coredump_register(struct mt7996_dev *dev);
- void mt7996_coredump_unregister(struct mt7996_dev *dev);
-@@ -92,6 +93,12 @@ static inline int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
- 	return 0;
- }
- 
-+static inline struct
-+mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type, bool full_dump)
-+{
-+	return NULL;
-+}
-+
- static inline struct
- mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type)
- {
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index 62c03d0..344c759 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -295,11 +295,39 @@ mt7996_fw_debug_wm_set(void *data, u64 val)
- 		DEBUG_SPL,
- 		DEBUG_RPT_RX,
- 		DEBUG_RPT_RA = 68,
--	} debug;
-+		DEBUG_IDS_SND = 84,
-+		DEBUG_IDS_PP = 93,
-+		DEBUG_IDS_RA = 94,
-+		DEBUG_IDS_BF = 95,
-+		DEBUG_IDS_SR = 96,
-+		DEBUG_IDS_RU = 97,
-+		DEBUG_IDS_MUMIMO = 98,
-+		DEBUG_IDS_ERR_LOG = 101,
-+	};
-+	u8 debug_category[] = {
-+		DEBUG_TXCMD,
-+		DEBUG_CMD_RPT_TX,
-+		DEBUG_CMD_RPT_TRIG,
-+		DEBUG_SPL,
-+		DEBUG_RPT_RX,
-+		DEBUG_RPT_RA,
-+		DEBUG_IDS_SND,
-+		DEBUG_IDS_PP,
-+		DEBUG_IDS_RA,
-+		DEBUG_IDS_BF,
-+		DEBUG_IDS_SR,
-+		DEBUG_IDS_RU,
-+		DEBUG_IDS_MUMIMO,
-+		DEBUG_IDS_ERR_LOG,
-+	};
- 	bool tx, rx, en;
- 	int ret;
-+	u8 i;
- 
- 	dev->fw_debug_wm = val ? MCU_FW_LOG_TO_HOST : 0;
-+#ifdef CONFIG_MTK_DEBUG
-+	dev->fw_debug_wm = val;
-+#endif
- 
- 	if (dev->fw_debug_bin)
- 		val = MCU_FW_LOG_RELAY;
-@@ -314,18 +342,21 @@ mt7996_fw_debug_wm_set(void *data, u64 val)
- 	if (ret)
- 		return ret;
- 
--	for (debug = DEBUG_TXCMD; debug <= DEBUG_RPT_RA; debug++) {
--		if (debug == 67)
--			continue;
--
--		if (debug == DEBUG_RPT_RX)
-+	for (i = 0; i < ARRAY_SIZE(debug_category); i++) {
-+		if (debug_category[i] == DEBUG_RPT_RX)
- 			val = en && rx;
- 		else
- 			val = en && tx;
- 
--		ret = mt7996_mcu_fw_dbg_ctrl(dev, debug, val);
-+		ret = mt7996_mcu_fw_dbg_ctrl(dev, debug_category[i], val);
- 		if (ret)
- 			return ret;
-+
-+		if (debug_category[i] == DEBUG_IDS_SND && en) {
-+			ret = mt7996_mcu_fw_dbg_ctrl(dev, debug_category[i], 2);
-+			if (ret)
-+				return ret;
-+		}
- 	}
- 
- 	return 0;
-@@ -397,6 +428,39 @@ remove_buf_file_cb(struct dentry *f)
- 	return 0;
- }
- 
-+static int
-+mt7996_fw_debug_muru_set(void *data)
-+{
-+	struct mt7996_dev *dev = data;
-+	enum {
-+		DEBUG_BSRP_STATUS = 256,
-+		DEBUG_TX_DATA_BYTE_CONUT,
-+		DEBUG_RX_DATA_BYTE_CONUT,
-+		DEBUG_RX_TOTAL_BYTE_CONUT,
-+		DEBUG_INVALID_TID_BSR,
-+		DEBUG_UL_LONG_TERM_PPDU_TYPE,
-+		DEBUG_DL_LONG_TERM_PPDU_TYPE,
-+		DEBUG_PPDU_CLASS_TRIG_ONOFF,
-+		DEBUG_AIRTIME_BUSY_STATUS,
-+		DEBUG_UL_OFDMA_MIMO_STATUS,
-+		DEBUG_RU_CANDIDATE,
-+		DEBUG_MEC_UPDATE_AMSDU,
-+	} debug;
-+	int ret;
-+
-+	if (dev->fw_debug_muru_disable)
-+		return 0;
-+
-+	for (debug = DEBUG_BSRP_STATUS; debug <= DEBUG_MEC_UPDATE_AMSDU; debug++) {
-+		ret = mt7996_mcu_muru_dbg_info(dev, debug,
-+					       dev->fw_debug_bin & BIT(0));
-+		if (ret)
-+			return ret;
-+	}
-+
-+	return 0;
-+}
-+
- static int
- mt7996_fw_debug_bin_set(void *data, u64 val)
- {
-@@ -405,17 +469,23 @@ mt7996_fw_debug_bin_set(void *data, u64 val)
- 		.remove_buf_file = remove_buf_file_cb,
- 	};
- 	struct mt7996_dev *dev = data;
-+	int ret;
- 
--	if (!dev->relay_fwlog)
-+	if (!dev->relay_fwlog) {
- 		dev->relay_fwlog = relay_open("fwlog_data", dev->debugfs_dir,
- 					      1500, 512, &relay_cb, NULL);
--	if (!dev->relay_fwlog)
--		return -ENOMEM;
-+		if (!dev->relay_fwlog)
-+			return -ENOMEM;
-+	}
- 
- 	dev->fw_debug_bin = val;
- 
- 	relay_reset(dev->relay_fwlog);
- 
-+	ret = mt7996_fw_debug_muru_set(dev);
-+	if (ret)
-+		return ret;
-+
- 	return mt7996_fw_debug_wm_set(dev, dev->fw_debug_wm);
- }
- 
-@@ -785,6 +855,30 @@ mt7996_rf_regval_set(void *data, u64 val)
- DEFINE_DEBUGFS_ATTRIBUTE(fops_rf_regval, mt7996_rf_regval_get,
- 			 mt7996_rf_regval_set, "0x%08llx\n");
- 
-+static int
-+mt7996_fw_debug_muru_disable_set(void *data, u64 val)
-+{
-+	struct mt7996_dev *dev = data;
-+
-+	dev->fw_debug_muru_disable = !!val;
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_fw_debug_muru_disable_get(void *data, u64 *val)
-+{
-+	struct mt7996_dev *dev = data;
-+
-+	*val = dev->fw_debug_muru_disable;
-+
-+	return 0;
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_muru_disable,
-+			 mt7996_fw_debug_muru_disable_get,
-+			 mt7996_fw_debug_muru_disable_set, "%lld\n");
-+
- int mt7996_init_debugfs(struct mt7996_phy *phy)
- {
- 	struct mt7996_dev *dev = phy->dev;
-@@ -820,10 +914,17 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
- 		debugfs_create_devm_seqfile(dev->mt76.dev, "rdd_monitor", dir,
- 					    mt7996_rdd_monitor);
- 	}
-+	debugfs_create_file("fw_debug_muru_disable", 0600, dir, dev,
-+			    &fops_fw_debug_muru_disable);
- 
- 	if (phy == &dev->phy)
- 		dev->debugfs_dir = dir;
- 
-+#ifdef CONFIG_MTK_DEBUG
-+	debugfs_create_u16("wlan_idx", 0600, dir, &dev->wlan_idx);
-+	mt7996_mtk_init_debugfs(phy, dir);
-+#endif
-+
- 	return 0;
- }
- 
-@@ -835,7 +936,11 @@ mt7996_debugfs_write_fwlog(struct mt7996_dev *dev, const void *hdr, int hdrlen,
- 	unsigned long flags;
- 	void *dest;
- 
-+	if (!dev->relay_fwlog)
-+		return;
-+
- 	spin_lock_irqsave(&lock, flags);
-+
- 	dest = relay_reserve(dev->relay_fwlog, hdrlen + len + 4);
- 	if (dest) {
- 		*(u32 *)dest = hdrlen + len;
-@@ -868,9 +973,6 @@ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int
- 		.msg_type = cpu_to_le16(PKT_TYPE_RX_FW_MONITOR),
- 	};
- 
--	if (!dev->relay_fwlog)
--		return;
--
- 	hdr.serial_id = cpu_to_le16(dev->fw_debug_seq++);
- 	hdr.timestamp = cpu_to_le32(mt76_rr(dev, MT_LPON_FRCR(0)));
- 	hdr.len = *(__le16 *)data;
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index d88bbfb..1f53d23 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -936,6 +936,9 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
- 	id = mt76_token_consume(mdev, &t);
- 	if (id < 0)
- 		return id;
-+#ifdef CONFIG_MTK_DEBUG
-+	t->jiffies = jiffies;
-+#endif
- 
- 	pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb);
- 	memset(txwi_ptr, 0, MT_TXD_SIZE);
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index e12ad31..696e16f 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -362,6 +362,7 @@ struct mt7996_dev {
- 	u8 fw_debug_wa;
- 	u8 fw_debug_bin;
- 	u16 fw_debug_seq;
-+	bool fw_debug_muru_disable;
- 
- 	struct dentry *debugfs_dir;
- 	struct rchan *relay_fwlog;
-@@ -374,6 +375,17 @@ struct mt7996_dev {
- 	spinlock_t reg_lock;
- 
- 	u8 wtbl_size_group;
-+
-+#ifdef CONFIG_MTK_DEBUG
-+	u16 wlan_idx;
-+	struct {
-+		u8 sku_disable;
-+		u32 fw_dbg_module;
-+		u8 fw_dbg_lv;
-+		u32 bcn_total_cnt[__MT_MAX_BAND];
-+	} dbg;
-+	const struct mt7996_dbg_reg_desc *dbg_reg;
-+#endif
- };
- 
- enum {
-@@ -670,6 +682,7 @@ u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id);
- 
- #ifdef CONFIG_MTK_DEBUG
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
-+int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
- #endif
- 
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
-diff --git a/mt7996/mtk_debug.h b/mt7996/mtk_debug.h
-new file mode 100644
-index 0000000..27d8f1c
---- /dev/null
-+++ b/mt7996/mtk_debug.h
-@@ -0,0 +1,2286 @@
-+#ifndef __MTK_DEBUG_H
-+#define __MTK_DEBUG_H
-+
-+#ifdef CONFIG_MTK_DEBUG
-+#define NO_SHIFT_DEFINE 0xFFFFFFFF
-+#define BITS(m, n)              (~(BIT(m)-1) & ((BIT(n) - 1) | BIT(n)))
-+
-+#define GET_FIELD(_field, _reg)	\
-+	({	\
-+		(((_reg) & (_field##_MASK)) >> (_field##_SHIFT));	\
-+	})
-+
-+#define __DBG_OFFS(id)		(dev->dbg_reg->offs_rev[(id)])
-+
-+enum dbg_offs_rev {
-+	AGG_AALCR2,
-+	AGG_AALCR3,
-+	AGG_AALCR4,
-+	AGG_AALCR5,
-+	AGG_AALCR6,
-+	AGG_AALCR7,
-+	MIB_TDRCR0,
-+	MIB_TDRCR1,
-+	MIB_TDRCR2,
-+	MIB_TDRCR3,
-+	MIB_TDRCR4,
-+	MIB_RSCR26,
-+	MIB_TSCR18,
-+	MIB_TRDR0,
-+	MIB_TRDR2,
-+	MIB_TRDR3,
-+	MIB_TRDR4,
-+	MIB_TRDR5,
-+	MIB_TRDR6,
-+	MIB_TRDR7,
-+	MIB_TRDR8,
-+	MIB_TRDR9,
-+	MIB_TRDR10,
-+	MIB_TRDR11,
-+	MIB_TRDR12,
-+	MIB_TRDR13,
-+	MIB_TRDR14,
-+	MIB_TRDR15,
-+	MIB_MSR0,
-+	MIB_MSR1,
-+	MIB_MSR2,
-+	MIB_MCTR5,
-+	MIB_MCTR6,
-+	__MT_DBG_OFFS_REV_MAX,
-+};
-+
-+static const u32 mt7996_dbg_offs[] = {
-+	[AGG_AALCR2]		= 0x128,
-+	[AGG_AALCR3]		= 0x12c,
-+	[AGG_AALCR4]		= 0x130,
-+	[AGG_AALCR5]		= 0x134,
-+	[AGG_AALCR6]		= 0x138,
-+	[AGG_AALCR7]		= 0x13c,
-+	[MIB_TDRCR0]		= 0x728,
-+	[MIB_TDRCR1]		= 0x72c,
-+	[MIB_TDRCR2]		= 0x730,
-+	[MIB_TDRCR3]		= 0x734,
-+	[MIB_TDRCR4]		= 0x738,
-+	[MIB_RSCR26]		= 0x950,
-+	[MIB_TSCR18]		= 0xa1c,
-+	[MIB_TRDR0]		= 0xa24,
-+	[MIB_TRDR2]		= 0xa2c,
-+	[MIB_TRDR3]		= 0xa30,
-+	[MIB_TRDR4]		= 0xa34,
-+	[MIB_TRDR5]		= 0xa38,
-+	[MIB_TRDR6]		= 0xa3c,
-+	[MIB_TRDR7]		= 0xa40,
-+	[MIB_TRDR8]		= 0xa44,
-+	[MIB_TRDR9]		= 0xa48,
-+	[MIB_TRDR10]		= 0xa4c,
-+	[MIB_TRDR11]		= 0xa50,
-+	[MIB_TRDR12]		= 0xa54,
-+	[MIB_TRDR13]		= 0xa58,
-+	[MIB_TRDR14]		= 0xa5c,
-+	[MIB_TRDR15]		= 0xa60,
-+	[MIB_MSR0]		= 0xa64,
-+	[MIB_MSR1]		= 0xa68,
-+	[MIB_MSR2]		= 0xa6c,
-+	[MIB_MCTR5]		= 0xa70,
-+	[MIB_MCTR6]		= 0xa74,
-+};
-+
-+static const u32 mt7992_dbg_offs[] = {
-+	[AGG_AALCR2]		= 0x12c,
-+	[AGG_AALCR3]		= 0x130,
-+	[AGG_AALCR4]		= 0x134,
-+	[AGG_AALCR5]		= 0x138,
-+	[AGG_AALCR6]		= 0x13c,
-+	[AGG_AALCR7]		= 0x140,
-+	[MIB_TDRCR0]		= 0x768,
-+	[MIB_TDRCR1]		= 0x76c,
-+	[MIB_TDRCR2]		= 0x770,
-+	[MIB_TDRCR3]		= 0x774,
-+	[MIB_TDRCR4]		= 0x778,
-+	[MIB_RSCR26]		= 0x994,
-+	[MIB_TSCR18]		= 0xb18,
-+	[MIB_TRDR0]		= 0xb20,
-+	[MIB_TRDR2]		= 0xb28,
-+	[MIB_TRDR3]		= 0xb2c,
-+	[MIB_TRDR4]		= 0xb30,
-+	[MIB_TRDR5]		= 0xb34,
-+	[MIB_TRDR6]		= 0xb38,
-+	[MIB_TRDR7]		= 0xb3c,
-+	[MIB_TRDR8]		= 0xb40,
-+	[MIB_TRDR9]		= 0xb44,
-+	[MIB_TRDR10]		= 0xb48,
-+	[MIB_TRDR11]		= 0xb4c,
-+	[MIB_TRDR12]		= 0xb50,
-+	[MIB_TRDR13]		= 0xb54,
-+	[MIB_TRDR14]		= 0xb58,
-+	[MIB_TRDR15]		= 0xb5c,
-+	[MIB_MSR0]		= 0xb60,
-+	[MIB_MSR1]		= 0xb64,
-+	[MIB_MSR2]		= 0xb68,
-+	[MIB_MCTR5]		= 0xb6c,
-+	[MIB_MCTR6]		= 0xb70,
-+};
-+
-+/* used to differentiate between generations */
-+struct mt7996_dbg_reg_desc {
-+	const u32 id;
-+	const u32 *offs_rev;
-+};
-+
-+/* AGG */
-+#define BN0_WF_AGG_TOP_BASE                                    0x820e2000
-+#define BN1_WF_AGG_TOP_BASE                                    0x820f2000
-+#define IP1_BN0_WF_AGG_TOP_BASE                                0x830e2000
-+
-+#define BN0_WF_AGG_TOP_SCR_ADDR                                (BN0_WF_AGG_TOP_BASE + 0x0) // 2000
-+#define BN0_WF_AGG_TOP_SCR0_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x4) // 2004
-+#define BN0_WF_AGG_TOP_SCR1_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x8) // 2008
-+#define BN0_WF_AGG_TOP_BCR_ADDR                                (BN0_WF_AGG_TOP_BASE + 0xc) // 200C
-+#define BN0_WF_AGG_TOP_BWCR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x10) // 2010
-+#define BN0_WF_AGG_TOP_ARCR0_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x14) // 2014
-+#define BN0_WF_AGG_TOP_ARUCR_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x18) // 2018
-+#define BN0_WF_AGG_TOP_ARDCR_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x1c) // 201C
-+#define BN0_WF_AGG_TOP_AALCR0_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x20) // 2020
-+#define BN0_WF_AGG_TOP_AALCR1_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x24) // 2024
-+#define BN0_WF_AGG_TOP_PCR0_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x28) // 2028
-+#define BN0_WF_AGG_TOP_PCR1_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x2c) // 202C
-+#define BN0_WF_AGG_TOP_TTCR0_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x30) // 2030
-+#define BN0_WF_AGG_TOP_TTCR1_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x34) // 2034
-+#define BN0_WF_AGG_TOP_ACR1_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x38) // 2038
-+#define BN0_WF_AGG_TOP_ACR4_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x3c) // 203C
-+#define BN0_WF_AGG_TOP_ACR5_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x40) // 2040
-+#define BN0_WF_AGG_TOP_ACR6_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x44) // 2044
-+#define BN0_WF_AGG_TOP_ACR8_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x4c) // 204C
-+#define BN0_WF_AGG_TOP_MRCR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x50) // 2050
-+#define BN0_WF_AGG_TOP_MMPDR_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x54) // 2054
-+#define BN0_WF_AGG_TOP_GFPDR_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x58) // 2058
-+#define BN0_WF_AGG_TOP_VHTPDR_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x5c) // 205C
-+#define BN0_WF_AGG_TOP_HEPDR_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x60) // 2060
-+#define BN0_WF_AGG_TOP_CTCR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x64) // 2064
-+#define BN0_WF_AGG_TOP_ATCR3_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x68) // 2068
-+#define BN0_WF_AGG_TOP_SRCR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x6c) // 206C
-+#define BN0_WF_AGG_TOP_VBCR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x70) // 2070
-+#define BN0_WF_AGG_TOP_TCR_ADDR                                (BN0_WF_AGG_TOP_BASE + 0x74) // 2074
-+#define BN0_WF_AGG_TOP_SRHS_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x78) // 2078
-+#define BN0_WF_AGG_TOP_DBRCR0_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x7c) // 207C
-+#define BN0_WF_AGG_TOP_DBRCR1_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x80) // 2080
-+#define BN0_WF_AGG_TOP_CTETCR_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x84) // 2084
-+#define BN0_WF_AGG_TOP_WPDR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x88) // 2088
-+#define BN0_WF_AGG_TOP_PLRPDR_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x8c) // 208C
-+#define BN0_WF_AGG_TOP_CECR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x90) // 2090
-+#define BN0_WF_AGG_TOP_OMRCR0_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x94) // 2094
-+#define BN0_WF_AGG_TOP_OMRCR1_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x98) // 2098
-+#define BN0_WF_AGG_TOP_OMRCR2_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x9c) // 209C
-+#define BN0_WF_AGG_TOP_OMRCR3_ADDR                             (BN0_WF_AGG_TOP_BASE + 0xa0) // 20A0
-+#define BN0_WF_AGG_TOP_TMCR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0xa4) // 20A4
-+#define BN0_WF_AGG_TOP_TWTCR_ADDR                              (BN0_WF_AGG_TOP_BASE + 0xa8) // 20A8
-+#define BN0_WF_AGG_TOP_TWTSTACR_ADDR                           (BN0_WF_AGG_TOP_BASE + 0xac) // 20AC
-+#define BN0_WF_AGG_TOP_TWTE0TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xb0) // 20B0
-+#define BN0_WF_AGG_TOP_TWTE1TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xb4) // 20B4
-+#define BN0_WF_AGG_TOP_TWTE2TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xb8) // 20B8
-+#define BN0_WF_AGG_TOP_TWTE3TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xbc) // 20BC
-+#define BN0_WF_AGG_TOP_TWTE4TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xc0) // 20C0
-+#define BN0_WF_AGG_TOP_TWTE5TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xc4) // 20C4
-+#define BN0_WF_AGG_TOP_TWTE6TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xc8) // 20C8
-+#define BN0_WF_AGG_TOP_TWTE7TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xcc) // 20CC
-+#define BN0_WF_AGG_TOP_TWTE8TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xd0) // 20D0
-+#define BN0_WF_AGG_TOP_TWTE9TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xd4) // 20D4
-+#define BN0_WF_AGG_TOP_TWTEATB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xd8) // 20D8
-+#define BN0_WF_AGG_TOP_TWTEBTB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xdc) // 20DC
-+#define BN0_WF_AGG_TOP_TWTECTB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xe0) // 20E0
-+#define BN0_WF_AGG_TOP_TWTEDTB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xe4) // 20E4
-+#define BN0_WF_AGG_TOP_TWTEETB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xe8) // 20E8
-+#define BN0_WF_AGG_TOP_TWTEFTB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xec) // 20EC
-+#define BN0_WF_AGG_TOP_ATCR0_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x108) // 2108
-+#define BN0_WF_AGG_TOP_ATCR1_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x10c) // 210C
-+#define BN0_WF_AGG_TOP_TCCR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x110) // 2110
-+#define BN0_WF_AGG_TOP_TFCR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x114) // 2114
-+#define BN0_WF_AGG_TOP_MUCR0_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x118) // 2118
-+#define BN0_WF_AGG_TOP_MUCR1_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x11c) // 211C
-+#define BN0_WF_AGG_TOP_AALCR2_ADDR                             (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR2))
-+#define BN0_WF_AGG_TOP_AALCR3_ADDR                             (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR3))
-+#define BN0_WF_AGG_TOP_AALCR4_ADDR                             (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR4))
-+#define BN0_WF_AGG_TOP_AALCR5_ADDR                             (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR5))
-+#define BN0_WF_AGG_TOP_AALCR6_ADDR                             (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR6))
-+#define BN0_WF_AGG_TOP_AALCR7_ADDR                             (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR7))
-+#define BN0_WF_AGG_TOP_CSDCR0_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x150) // 2150
-+#define BN0_WF_AGG_TOP_CSDCR1_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x154) // 2154
-+#define BN0_WF_AGG_TOP_CSDCR2_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x158) // 2158
-+#define BN0_WF_AGG_TOP_CSDCR3_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x15c) // 215C
-+#define BN0_WF_AGG_TOP_CSDCR4_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x160) // 2160
-+#define BN0_WF_AGG_TOP_DYNSCR_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x178) // 2178
-+#define BN0_WF_AGG_TOP_DYNSSCR_ADDR                            (BN0_WF_AGG_TOP_BASE + 0x198) // 2198
-+#define BN0_WF_AGG_TOP_TCDCNT0_ADDR                            (BN0_WF_AGG_TOP_BASE + 0x2c8) // 22C8
-+#define BN0_WF_AGG_TOP_TCDCNT1_ADDR                            (BN0_WF_AGG_TOP_BASE + 0x2cc) // 22CC
-+#define BN0_WF_AGG_TOP_TCSR0_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x2d0) // 22D0
-+#define BN0_WF_AGG_TOP_TCSR1_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x2d4) // 22D4
-+#define BN0_WF_AGG_TOP_TCSR2_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x2d8) // 22D8
-+#define BN0_WF_AGG_TOP_DCR_ADDR                                (BN0_WF_AGG_TOP_BASE + 0x2e4) // 22E4
-+#define BN0_WF_AGG_TOP_SMDCR_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x2e8) // 22E8
-+#define BN0_WF_AGG_TOP_TXCMDSMCR_ADDR                          (BN0_WF_AGG_TOP_BASE + 0x2ec) // 22EC
-+#define BN0_WF_AGG_TOP_SMCR0_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x2f0) // 22F0
-+#define BN0_WF_AGG_TOP_SMCR1_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x2f4) // 22F4
-+#define BN0_WF_AGG_TOP_SMCR2_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x2f8) // 22F8
-+#define BN0_WF_AGG_TOP_SMCR3_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x2fc) // 22FC
-+
-+#define BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR0_ADDR
-+#define BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_MASK              0x03FF0000                // AC01_AGG_LIMIT[25..16]
-+#define BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_SHFT              16
-+#define BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR0_ADDR
-+#define BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_MASK              0x000003FF                // AC00_AGG_LIMIT[9..0]
-+#define BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_SHFT              0
-+
-+#define BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR1_ADDR
-+#define BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_MASK              0x03FF0000                // AC03_AGG_LIMIT[25..16]
-+#define BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_SHFT              16
-+#define BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR1_ADDR
-+#define BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_MASK              0x000003FF                // AC02_AGG_LIMIT[9..0]
-+#define BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_SHFT              0
-+
-+#define BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR2_ADDR
-+#define BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_MASK              0x03FF0000                // AC11_AGG_LIMIT[25..16]
-+#define BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_SHFT              16
-+#define BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR2_ADDR
-+#define BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_MASK              0x000003FF                // AC10_AGG_LIMIT[9..0]
-+#define BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_SHFT              0
-+
-+#define BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR3_ADDR
-+#define BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_MASK              0x03FF0000                // AC13_AGG_LIMIT[25..16]
-+#define BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_SHFT              16
-+#define BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR3_ADDR
-+#define BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_MASK              0x000003FF                // AC12_AGG_LIMIT[9..0]
-+#define BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_SHFT              0
-+
-+#define BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR4_ADDR
-+#define BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_MASK              0x03FF0000                // AC21_AGG_LIMIT[25..16]
-+#define BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_SHFT              16
-+#define BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR4_ADDR
-+#define BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_MASK              0x000003FF                // AC20_AGG_LIMIT[9..0]
-+#define BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_SHFT              0
-+
-+#define BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR5_ADDR
-+#define BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_MASK              0x03FF0000                // AC23_AGG_LIMIT[25..16]
-+#define BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_SHFT              16
-+#define BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR5_ADDR
-+#define BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_MASK              0x000003FF                // AC22_AGG_LIMIT[9..0]
-+#define BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_SHFT              0
-+
-+#define BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR6_ADDR
-+#define BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_MASK              0x03FF0000                // AC31_AGG_LIMIT[25..16]
-+#define BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_SHFT              16
-+#define BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR6_ADDR
-+#define BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_MASK              0x000003FF                // AC30_AGG_LIMIT[9..0]
-+#define BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_SHFT              0
-+#define BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR7_ADDR
-+#define BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_MASK              0x03FF0000                // AC33_AGG_LIMIT[25..16]
-+#define BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_SHFT              16
-+#define BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR7_ADDR
-+#define BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_MASK              0x000003FF                // AC32_AGG_LIMIT[9..0]
-+#define BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_SHFT              0
-+
-+/* DMA */
-+struct queue_desc {
-+	u32 hw_desc_base;
-+	u16 ring_size;
-+	char *const ring_info;
-+};
-+
-+// HOST DMA
-+#define WF_WFDMA_HOST_DMA0_BASE                                0xd4000
-+
-+#define WF_WFDMA_HOST_DMA0_HOST_INT_STA_ADDR                                   \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x200) /* 4200 */
-+#define WF_WFDMA_HOST_DMA0_HOST_INT_ENA_ADDR                                   \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0X204) /* 4204 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR                                  \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x208) /* 4208 */
-+
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_ADDR                      \
-+	WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK                      \
-+	0x00000008 /* RX_DMA_BUSY[3] */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT 3
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_ADDR                        \
-+	WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_MASK                        \
-+	0x00000004 /* RX_DMA_EN[2] */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_SHFT 2
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_ADDR                      \
-+	WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK                      \
-+	0x00000002 /* TX_DMA_BUSY[1] */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT 1
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_ADDR                        \
-+	WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_MASK                        \
-+	0x00000001 /* TX_DMA_EN[0] */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_SHFT 0
-+
-+
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL0_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x300) /* 4300 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL1_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x304) /* 4304 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL2_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x308) /* 4308 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL3_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x30c) /* 430C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL0_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x310) /* 4310 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL1_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x314) /* 4314 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL2_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x318) /* 4318 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL3_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x31c) /* 431C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL0_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x320) /* 4320 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL1_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x324) /* 4324 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL2_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x328) /* 4328 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL3_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x32c) /* 432C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL0_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x330) /* 4330 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL1_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x334) /* 4334 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL2_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x338) /* 4338 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL3_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x33c) /* 433C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL0_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x340) /* 4340 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL1_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x344) /* 4344 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL2_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x348) /* 4348 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL3_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x34c) /* 434C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL0_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x350) /* 4350 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL1_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x354) /* 4354 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL2_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x358) /* 4358 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL3_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x35c) /* 435C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL0_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x360) /* 4360 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL1_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x364) /* 4364 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL2_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x368) /* 4368 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL3_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x36c) /* 436C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL0_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x400) /* 4400 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL1_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x404) /* 4404 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL2_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x408) /* 4408 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL3_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x40c) /* 440C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL0_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x410) /* 4410 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL1_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x414) /* 4414 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL2_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x418) /* 4418 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL3_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x41c) /* 441C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL0_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x420) /* 4420 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL1_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x424) /* 4424 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL2_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x428) /* 4428 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL3_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x42c) /* 442C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL0_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x430) /* 4430 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL1_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x434) /* 4434 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL2_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x438) /* 4438 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL3_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x43c) /* 443C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL0_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x440) /* 4440 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL1_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x444) /* 4444 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL2_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x448) /* 4448 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL3_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x44c) /* 444C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL0_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x450) /* 4450 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL1_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x454) /* 4454 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL2_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x458) /* 4458 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL3_ADDR                          \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x45c) /* 445c */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL0_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x460) // 4460
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL1_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x464) // 4464
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL2_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x468) // 4468
-+#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL3_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x46c) // 446C
-+
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL0_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x500) /* 4500 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL1_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x504) /* 4504 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL2_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x508) /* 4508 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL3_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x50c) /* 450C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL0_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x510) /* 4510 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL1_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x514) /* 4514 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL2_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x518) /* 4518 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL3_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x51c) /* 451C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL0_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x520) /* 4520 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL1_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x524) /* 4524 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL2_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x528) /* 4528 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL3_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x52C) /* 452C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL0_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x530) /* 4530 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL1_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x534) /* 4534 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL2_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x538) /* 4538 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL3_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x53C) /* 453C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL0_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x540) /* 4540 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL1_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x544) /* 4544 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL2_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x548) /* 4548 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL3_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x54c) /* 454C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL0_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x550) /* 4550 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL1_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x554) /* 4554 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL2_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x558) /* 4558 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL3_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x55c) /* 455C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x560) /* 4560 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL1_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x564) /* 4564 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL2_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x568) /* 4568 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL3_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x56c) /* 456C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL0_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x570) /* 4570 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL1_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x574) /* 4574 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL2_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x578) /* 4578 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL3_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x57c) /* 457C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL0_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x580) /* 4580 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL1_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x584) /* 4584 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL2_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x588) /* 4588 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL3_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x58c) /* 458C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x590) /* 4590 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL1_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x594) /* 4594 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL2_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x598) /* 4598 */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL3_ADDR                           \
-+	(WF_WFDMA_HOST_DMA0_BASE + 0x59c) /* 459C */
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL0_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5a0) // 45A0
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL1_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5a4) // 45A4
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL2_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5a8) // 45A8
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL3_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5ac) // 45AC
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL0_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5b0) // 45B0
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL1_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5b4) // 45B4
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL2_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5b8) // 45B8
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL3_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5bc) // 45BC
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL0_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5C0) // 45C0
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL1_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5C4) // 45C4
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL2_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5C8) // 45C8
-+#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL3_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5CC) // 45CC
-+
-+// HOST PCIE1 DMA
-+#define WF_WFDMA_HOST_DMA0_PCIE1_BASE				0xd8000
-+
-+#define WF_WFDMA_HOST_DMA0_PCIE1_HOST_INT_STA_ADDR		(WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x200) // 8200
-+#define WF_WFDMA_HOST_DMA0_PCIE1_HOST_INT_ENA_ADDR		(WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0X204) // 8204
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_ADDR		(WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x208) // 8208
-+
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_PDMA_BT_SIZE_SHFT	4
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK		0x00000008
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT		3
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_EN_MASK		0x00000004
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_EN_SHFT		2
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK		0x00000002
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT		1
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_EN_MASK		0x00000001
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_EN_SHFT		0
-+
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL0_ADDR    (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x450) // 8450
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL1_ADDR    (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x454) // 8454
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL2_ADDR    (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x458) // 8458
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL3_ADDR    (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x45c) // 845C
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL0_ADDR    (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x460) // 8460
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL1_ADDR    (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x464) // 8464
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL2_ADDR    (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x468) // 8468
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL3_ADDR    (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x46c) // 846C
-+
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL0_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x530) // 8530
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL1_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x534) // 8534
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL2_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x538) // 8538
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL3_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x53C) // 853C
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL0_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x550) // 8550
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL1_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x554) // 8554
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL2_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x558) // 8558
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL3_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x55c) // 855C
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL0_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x560) // 8560
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL1_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x564) // 8564
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL2_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x568) // 8568
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL3_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x56c) // 856C
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL0_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x570) // 8570
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL1_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x574) // 8574
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL2_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x578) // 8578
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL3_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x57c) // 857C
-+//MCU DMA
-+//#define WF_WFDMA_MCU_DMA0_BASE                                 0x02000
-+#define WF_WFDMA_MCU_DMA0_BASE                                 0x54000000
-+
-+#define WF_WFDMA_MCU_DMA0_HOST_INT_STA_ADDR                    (WF_WFDMA_MCU_DMA0_BASE + 0x200) // 0200
-+#define WF_WFDMA_MCU_DMA0_HOST_INT_ENA_ADDR                    (WF_WFDMA_MCU_DMA0_BASE + 0X204) // 0204
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR                   (WF_WFDMA_MCU_DMA0_BASE + 0x208) // 0208
-+
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_ADDR       WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK       0x00000008                // RX_DMA_BUSY[3]
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT       3
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_ADDR         WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_MASK         0x00000004                // RX_DMA_EN[2]
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_SHFT         2
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_ADDR       WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK       0x00000002                // TX_DMA_BUSY[1]
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT       1
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_ADDR         WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_MASK         0x00000001                // TX_DMA_EN[0]
-+#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_SHFT         0
-+
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x300) // 0300
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x304) // 0304
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x308) // 0308
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x30c) // 030C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x310) // 0310
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x314) // 0314
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x318) // 0318
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x31c) // 031C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x320) // 0320
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x324) // 0324
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x328) // 0328
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x32c) // 032C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x330) // 0330
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x334) // 0334
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x338) // 0338
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x33c) // 033C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x340) // 0340
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x344) // 0344
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x348) // 0348
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x34c) // 034C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x350) // 0350
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x354) // 0354
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x358) // 0358
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x35c) // 035C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x360) // 0360
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x364) // 0364
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x368) // 0368
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x36c) // 036C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING7_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x370) // 0370
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING7_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x374) // 0374
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING7_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x378) // 0378
-+#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING7_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x37c) // 037C
-+
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x500) // 0500
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x504) // 0504
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x508) // 0508
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x50c) // 050C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x510) // 0510
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x514) // 0514
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x518) // 0518
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x51c) // 051C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x520) // 0520
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x524) // 0524
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x528) // 0528
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x52C) // 052C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x530) // 0530
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x534) // 0534
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x538) // 0538
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x53C) // 053C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x540) // 0540
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x544) // 0544
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x548) // 0548
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x54C) // 054C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x550) // 0550
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x554) // 0554
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x558) // 0558
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x55C) // 055C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x560) // 0560
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x564) // 0564
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x568) // 0568
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x56c) // 056C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x570) // 0570
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x574) // 0574
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x578) // 0578
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x57c) // 057C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x580) // 0580
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x584) // 0584
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x588) // 0588
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x58c) // 058C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x590) // 0590
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x594) // 0594
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x598) // 0598
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x59c) // 059C
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL0_ADDR           (WF_WFDMA_MCU_DMA0_BASE + 0x5A0) // 05A0
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL1_ADDR           (WF_WFDMA_MCU_DMA0_BASE + 0x5A4) // 05A4
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL2_ADDR           (WF_WFDMA_MCU_DMA0_BASE + 0x5A8) // 05A8
-+#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL3_ADDR           (WF_WFDMA_MCU_DMA0_BASE + 0x5Ac) // 05AC
-+
-+// MEM DMA
-+#define WF_WFDMA_MEM_DMA_BASE                                  0x58000000
-+
-+#define WF_WFDMA_MEM_DMA_HOST_INT_STA_ADDR                     (WF_WFDMA_MEM_DMA_BASE + 0x200) // 0200
-+#define WF_WFDMA_MEM_DMA_HOST_INT_ENA_ADDR                     (WF_WFDMA_MEM_DMA_BASE + 0X204) // 0204
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR                    (WF_WFDMA_MEM_DMA_BASE + 0x208) // 0208
-+
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_ADDR        WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK        0x00000008                // RX_DMA_BUSY[3]
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT        3
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_ADDR          WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_MASK          0x00000004                // RX_DMA_EN[2]
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_SHFT          2
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_ADDR        WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK        0x00000002                // TX_DMA_BUSY[1]
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT        1
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_ADDR          WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_MASK          0x00000001                // TX_DMA_EN[0]
-+#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_SHFT          0
-+
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL0_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x300) // 0300
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL1_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x304) // 0304
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL2_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x308) // 0308
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL3_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x30c) // 030C
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL0_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x310) // 0310
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL1_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x314) // 0314
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL2_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x318) // 0318
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL3_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x31c) // 031C
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING2_CTRL0_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x320) // 0320
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING2_CTRL1_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x324) // 0324
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING2_CTRL2_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x328) // 0328
-+#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING2_CTRL3_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x32c) // 032C
-+
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL0_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x500) // 0500
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL1_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x504) // 0504
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL2_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x508) // 0508
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL3_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x50c) // 050C
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL0_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x510) // 0510
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL1_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x514) // 0514
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL2_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x518) // 0518
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL3_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x51c) // 051C
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING2_CTRL0_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x520) // 0520
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING2_CTRL1_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x524) // 0524
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING2_CTRL2_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x528) // 0528
-+#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING2_CTRL3_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x52C) // 052C
-+
-+/* MIB */
-+#define WF_UMIB_TOP_BASE                                       0x820cd000
-+#define BN0_WF_MIB_TOP_BASE                                    0x820ed000
-+#define BN1_WF_MIB_TOP_BASE                                    0x820fd000
-+#define IP1_BN0_WF_MIB_TOP_BASE                                0x830ed000
-+
-+#define WF_UMIB_TOP_B0BROCR_ADDR                               (WF_UMIB_TOP_BASE + 0x484) // D484
-+#define WF_UMIB_TOP_B0BRBCR_ADDR                               (WF_UMIB_TOP_BASE + 0x4D4) // D4D4
-+#define WF_UMIB_TOP_B0BRDCR_ADDR                               (WF_UMIB_TOP_BASE + 0x524) // D524
-+#define WF_UMIB_TOP_B1BROCR_ADDR                               (WF_UMIB_TOP_BASE + 0x5E8) // D5E8
-+#define WF_UMIB_TOP_B2BROCR_ADDR                               (WF_UMIB_TOP_BASE + 0x74C) // D74C
-+
-+#define BN0_WF_MIB_TOP_M0SCR0_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x000) // D000
-+#define BN0_WF_MIB_TOP_M0SDR6_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x020) // D020
-+#define BN0_WF_MIB_TOP_M0SDR9_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x024) // D024
-+#define BN0_WF_MIB_TOP_M0SDR18_ADDR                            (BN0_WF_MIB_TOP_BASE + 0x030) // D030
-+#define BN0_WF_MIB_TOP_BTOCR_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x400) // D400
-+#define BN0_WF_MIB_TOP_BTBCR_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x450) // D450
-+#define BN0_WF_MIB_TOP_BTDCR_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x590) // D590
-+#define BN0_WF_MIB_TOP_BTCR_ADDR                               (BN0_WF_MIB_TOP_BASE + 0x5A0) // D5A0
-+#define BN0_WF_MIB_TOP_RVSR0_ADDR                              (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RVSR0))
-+
-+#define BN0_WF_MIB_TOP_TSCR0_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6B0) // D6B0
-+#define BN0_WF_MIB_TOP_TSCR3_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6BC) // D6BC
-+#define BN0_WF_MIB_TOP_TSCR4_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6C0) // D6C0
-+#define BN0_WF_MIB_TOP_TSCR5_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6C4) // D6C4
-+#define BN0_WF_MIB_TOP_TSCR6_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6C8) // D6C8
-+#define BN0_WF_MIB_TOP_TSCR7_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6D0) // D6D0
-+#define BN0_WF_MIB_TOP_TSCR8_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6CC) // D6CC
-+
-+#define BN0_WF_MIB_TOP_TBCR0_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6EC) // D6EC
-+#define BN0_WF_MIB_TOP_TBCR1_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6F0) // D6F0
-+#define BN0_WF_MIB_TOP_TBCR2_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6F4) // D6F4
-+#define BN0_WF_MIB_TOP_TBCR3_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6F8) // D6F8
-+#define BN0_WF_MIB_TOP_TBCR4_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6FC) // D6FC
-+
-+#define BN0_WF_MIB_TOP_TDRCR0_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR0))
-+#define BN0_WF_MIB_TOP_TDRCR1_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR1))
-+#define BN0_WF_MIB_TOP_TDRCR2_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR2))
-+#define BN0_WF_MIB_TOP_TDRCR3_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR3))
-+#define BN0_WF_MIB_TOP_TDRCR4_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR4))
-+
-+#define BN0_WF_MIB_TOP_BTSCR0_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x5E0) // D5E0
-+#define BN0_WF_MIB_TOP_BTSCR1_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x5F0) // D5F0
-+#define BN0_WF_MIB_TOP_BTSCR2_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x600) // D600
-+#define BN0_WF_MIB_TOP_BTSCR3_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x610) // D610
-+#define BN0_WF_MIB_TOP_BTSCR4_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x620) // D620
-+#define BN0_WF_MIB_TOP_BTSCR5_ADDR                             (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_BTSCR5))
-+#define BN0_WF_MIB_TOP_BTSCR6_ADDR                             (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_BTSCR6))
-+
-+#define BN0_WF_MIB_TOP_RSCR1_ADDR                              (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR1))
-+#define BN0_WF_MIB_TOP_BSCR2_ADDR                              (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_BSCR2))
-+#define BN0_WF_MIB_TOP_TSCR18_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TSCR18))
-+
-+#define BN0_WF_MIB_TOP_MSR0_ADDR                               (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MSR0))
-+#define BN0_WF_MIB_TOP_MSR1_ADDR                               (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MSR1))
-+#define BN0_WF_MIB_TOP_MSR2_ADDR                               (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MSR2))
-+#define BN0_WF_MIB_TOP_MCTR5_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MCTR5))
-+#define BN0_WF_MIB_TOP_MCTR6_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MCTR6))
-+
-+#define BN0_WF_MIB_TOP_RSCR26_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_RSCR26))
-+#define BN0_WF_MIB_TOP_RSCR27_ADDR                             (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR27))
-+#define BN0_WF_MIB_TOP_RSCR28_ADDR                             (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR28))
-+#define BN0_WF_MIB_TOP_RSCR31_ADDR                             (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR31))
-+#define BN0_WF_MIB_TOP_RSCR33_ADDR                             (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR33))
-+#define BN0_WF_MIB_TOP_RSCR35_ADDR                             (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR35))
-+#define BN0_WF_MIB_TOP_RSCR36_ADDR                             (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR36))
-+
-+#define BN0_WF_MIB_TOP_TSCR3_AMPDU_MPDU_COUNT_MASK             0xFFFFFFFF                // AMPDU_MPDU_COUNT[31..0]
-+#define BN0_WF_MIB_TOP_TSCR4_AMPDU_ACKED_COUNT_MASK            0xFFFFFFFF                // AMPDU_ACKED_COUNT[31..0]
-+#define BN0_WF_MIB_TOP_M0SDR6_CHANNEL_IDLE_COUNT_MASK          0x0000FFFF                // CHANNEL_IDLE_COUNT[15..0]
-+#define BN0_WF_MIB_TOP_M0SDR9_CCA_NAV_TX_TIME_MASK             0x00FFFFFF                // CCA_NAV_TX_TIME[23..0]
-+#define BN0_WF_MIB_TOP_RSCR26_RX_MDRDY_COUNT_MASK              0xFFFFFFFF                // RX_MDRDY_COUNT[31..0]
-+#define BN0_WF_MIB_TOP_MSR0_CCK_MDRDY_TIME_MASK                0xFFFFFFFF                // CCK_MDRDY_TIME[31..0]
-+#define BN0_WF_MIB_TOP_MSR1_OFDM_LG_MIXED_VHT_MDRDY_TIME_MASK  0xFFFFFFFF                // OFDM_LG_MIXED_VHT_MDRDY_TIME[31..0]
-+#define BN0_WF_MIB_TOP_MSR2_OFDM_GREEN_MDRDY_TIME_MASK         0xFFFFFFFF                // OFDM_GREEN_MDRDY_TIME[31..0]
-+#define BN0_WF_MIB_TOP_MCTR5_P_CCA_TIME_MASK                   0xFFFFFFFF                // P_CCA_TIME[31..0]
-+#define BN0_WF_MIB_TOP_MCTR6_S_CCA_TIME_MASK                   0xFFFFFFFF                // S_CCA_TIME[31..0]
-+#define BN0_WF_MIB_TOP_M0SDR18_P_ED_TIME_MASK                  0x00FFFFFF                // P_ED_TIME[23..0]
-+#define BN0_WF_MIB_TOP_TSCR18_BEACONTXCOUNT_MASK               0xFFFFFFFF                // BEACONTXCOUNT[31..0]
-+#define BN0_WF_MIB_TOP_TBCR0_TX_20MHZ_CNT_MASK                 0xFFFFFFFF                // TX_20MHZ_CNT[31..0]
-+#define BN0_WF_MIB_TOP_TBCR1_TX_40MHZ_CNT_MASK                 0xFFFFFFFF                // TX_40MHZ_CNT[31..0]
-+#define BN0_WF_MIB_TOP_TBCR2_TX_80MHZ_CNT_MASK                 0xFFFFFFFF                // TX_80MHZ_CNT[31..0]
-+#define BN0_WF_MIB_TOP_TBCR3_TX_160MHZ_CNT_MASK                0xFFFFFFFF                // TX_160MHZ_CNT[31..0]
-+#define BN0_WF_MIB_TOP_TBCR4_TX_320MHZ_CNT_MASK                0xFFFFFFFF                // TX_320MHZ_CNT[31..0]
-+#define BN0_WF_MIB_TOP_BSCR2_MUBF_TX_COUNT_MASK                0xFFFFFFFF                // MUBF_TX_COUNT[31..0]
-+#define BN0_WF_MIB_TOP_RVSR0_VEC_MISS_COUNT_MASK               0xFFFFFFFF                // VEC_MISS_COUNT[31..0]
-+#define BN0_WF_MIB_TOP_RSCR35_DELIMITER_FAIL_COUNT_MASK        0xFFFFFFFF                // DELIMITER_FAIL_COUNT[31..0]
-+#define BN0_WF_MIB_TOP_RSCR1_RX_FCS_ERROR_COUNT_MASK           0xFFFFFFFF                // RX_FCS_ERROR_COUNT[31..0]
-+#define BN0_WF_MIB_TOP_RSCR33_RX_FIFO_FULL_COUNT_MASK          0xFFFFFFFF                // RX_FIFO_FULL_COUNT[31..0]
-+#define BN0_WF_MIB_TOP_RSCR36_RX_LEN_MISMATCH_MASK             0xFFFFFFFF                // RX_LEN_MISMATCH[31..0]
-+#define BN0_WF_MIB_TOP_RSCR31_RX_MPDU_COUNT_MASK               0xFFFFFFFF                // RX_MPDU_COUNT[31..0]
-+#define BN0_WF_MIB_TOP_BTSCR5_RTSTXCOUNTn_MASK                 0xFFFFFFFF                // RTSTXCOUNTn[31..0]
-+#define BN0_WF_MIB_TOP_BTSCR6_RTSRETRYCOUNTn_MASK              0xFFFFFFFF                // RTSRETRYCOUNTn[31..0]
-+#define BN0_WF_MIB_TOP_BTSCR0_BAMISSCOUNTn_MASK                0xFFFFFFFF                // BAMISSCOUNTn[31..0]
-+#define BN0_WF_MIB_TOP_BTSCR1_ACKFAILCOUNTn_MASK               0xFFFFFFFF                // ACKFAILCOUNTn[31..0]
-+#define BN0_WF_MIB_TOP_BTSCR2_FRAMERETRYCOUNTn_MASK            0xFFFFFFFF                // FRAMERETRYCOUNTn[31..0]
-+#define BN0_WF_MIB_TOP_BTSCR3_FRAMERETRY2COUNTn_MASK           0xFFFFFFFF                // FRAMERETRY2COUNTn[31..0]
-+#define BN0_WF_MIB_TOP_BTSCR4_FRAMERETRY3COUNTn_MASK           0xFFFFFFFF                // FRAMERETRY3COUNTn[31..0]
-+#define BN0_WF_MIB_TOP_TRARC0_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x0B0) // D0B0
-+#define BN0_WF_MIB_TOP_TRARC1_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x0B4) // D0B4
-+#define BN0_WF_MIB_TOP_TRARC2_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x0B8) // D0B8
-+#define BN0_WF_MIB_TOP_TRARC3_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x0BC) // D0BC
-+#define BN0_WF_MIB_TOP_TRARC4_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x0C0) // D0C0
-+#define BN0_WF_MIB_TOP_TRARC5_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x0C4) // D0C4
-+#define BN0_WF_MIB_TOP_TRARC6_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x0C8) // D0C8
-+#define BN0_WF_MIB_TOP_TRARC7_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x0CC) // D0CC
-+
-+#define BN0_WF_MIB_TOP_TRDR0_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR0))
-+#define BN0_WF_MIB_TOP_TRDR1_ADDR                              (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_TRDR1))
-+#define BN0_WF_MIB_TOP_TRDR2_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR2))
-+#define BN0_WF_MIB_TOP_TRDR3_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR3))
-+#define BN0_WF_MIB_TOP_TRDR4_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR4))
-+#define BN0_WF_MIB_TOP_TRDR5_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR5))
-+#define BN0_WF_MIB_TOP_TRDR6_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR6))
-+#define BN0_WF_MIB_TOP_TRDR7_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR7))
-+#define BN0_WF_MIB_TOP_TRDR8_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR8))
-+#define BN0_WF_MIB_TOP_TRDR9_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR9))
-+#define BN0_WF_MIB_TOP_TRDR10_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR10))
-+#define BN0_WF_MIB_TOP_TRDR11_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR11))
-+#define BN0_WF_MIB_TOP_TRDR12_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR12))
-+#define BN0_WF_MIB_TOP_TRDR13_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR13))
-+#define BN0_WF_MIB_TOP_TRDR14_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR14))
-+#define BN0_WF_MIB_TOP_TRDR15_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR15))
-+
-+#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_ADDR              BN0_WF_MIB_TOP_TRARC0_ADDR
-+#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_MASK              0x03FF0000                // AGG_RANG_SEL_1[25..16]
-+#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_SHFT              16
-+#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_ADDR              BN0_WF_MIB_TOP_TRARC0_ADDR
-+#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_MASK              0x000003FF                // AGG_RANG_SEL_0[9..0]
-+#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_SHFT              0
-+
-+#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_ADDR              BN0_WF_MIB_TOP_TRARC1_ADDR
-+#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_MASK              0x03FF0000                // AGG_RANG_SEL_3[25..16]
-+#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_SHFT              16
-+#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_ADDR              BN0_WF_MIB_TOP_TRARC1_ADDR
-+#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_MASK              0x000003FF                // AGG_RANG_SEL_2[9..0]
-+#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_SHFT              0
-+
-+#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_ADDR              BN0_WF_MIB_TOP_TRARC2_ADDR
-+#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_MASK              0x03FF0000                // AGG_RANG_SEL_5[25..16]
-+#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_SHFT              16
-+#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_ADDR              BN0_WF_MIB_TOP_TRARC2_ADDR
-+#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_MASK              0x000003FF                // AGG_RANG_SEL_4[9..0]
-+#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_SHFT              0
-+
-+#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_ADDR              BN0_WF_MIB_TOP_TRARC3_ADDR
-+#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_MASK              0x03FF0000                // AGG_RANG_SEL_7[25..16]
-+#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_SHFT              16
-+#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_ADDR              BN0_WF_MIB_TOP_TRARC3_ADDR
-+#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_MASK              0x000003FF                // AGG_RANG_SEL_6[9..0]
-+#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_SHFT              0
-+
-+#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_ADDR              BN0_WF_MIB_TOP_TRARC4_ADDR
-+#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_MASK              0x03FF0000                // AGG_RANG_SEL_9[25..16]
-+#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_SHFT              16
-+#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_ADDR              BN0_WF_MIB_TOP_TRARC4_ADDR
-+#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_MASK              0x000003FF                // AGG_RANG_SEL_8[9..0]
-+#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_SHFT              0
-+
-+#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_ADDR             BN0_WF_MIB_TOP_TRARC5_ADDR
-+#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_MASK             0x03FF0000                // AGG_RANG_SEL_11[25..16]
-+#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_SHFT             16
-+#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_ADDR             BN0_WF_MIB_TOP_TRARC5_ADDR
-+#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_MASK             0x000003FF                // AGG_RANG_SEL_10[9..0]
-+#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_SHFT             0
-+
-+#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_ADDR             BN0_WF_MIB_TOP_TRARC6_ADDR
-+#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_MASK             0x03FF0000                // AGG_RANG_SEL_13[25..16]
-+#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_SHFT             16
-+#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_ADDR             BN0_WF_MIB_TOP_TRARC6_ADDR
-+#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_MASK             0x000003FF                // AGG_RANG_SEL_12[9..0]
-+#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_SHFT             0
-+
-+#define BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_ADDR             BN0_WF_MIB_TOP_TRARC7_ADDR
-+#define BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_MASK             0x000003FF                // AGG_RANG_SEL_14[9..0]
-+#define BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_SHFT             0
-+
-+/* RRO TOP */
-+#define WF_RRO_TOP_BASE                                        0xA000 /*0x820C2000 */
-+#define WF_RRO_TOP_IND_CMD_0_CTRL0_ADDR                        (WF_RRO_TOP_BASE + 0x40) // 2040
-+											//
-+/* WTBL */
-+enum mt7996_wtbl_type {
-+	WTBL_TYPE_LMAC, 	/* WTBL in LMAC */
-+	WTBL_TYPE_UMAC, 	/* WTBL in UMAC */
-+	WTBL_TYPE_KEY,		/* Key Table */
-+	MAX_NUM_WTBL_TYPE
-+};
-+
-+struct berse_wtbl_parse {
-+	u8 *name;
-+	u32 mask;
-+	u32 shift;
-+	u8 new_line;
-+};
-+
-+enum muar_idx {
-+	MUAR_INDEX_OWN_MAC_ADDR_0 = 0,
-+	MUAR_INDEX_OWN_MAC_ADDR_1,
-+	MUAR_INDEX_OWN_MAC_ADDR_2,
-+	MUAR_INDEX_OWN_MAC_ADDR_3,
-+	MUAR_INDEX_OWN_MAC_ADDR_4,
-+	MUAR_INDEX_OWN_MAC_ADDR_BC_MC = 0xE,
-+	MUAR_INDEX_UNMATCHED = 0xF,
-+	MUAR_INDEX_OWN_MAC_ADDR_11 = 0x11,
-+	MUAR_INDEX_OWN_MAC_ADDR_12,
-+	MUAR_INDEX_OWN_MAC_ADDR_13,
-+	MUAR_INDEX_OWN_MAC_ADDR_14,
-+	MUAR_INDEX_OWN_MAC_ADDR_15,
-+	MUAR_INDEX_OWN_MAC_ADDR_16,
-+	MUAR_INDEX_OWN_MAC_ADDR_17,
-+	MUAR_INDEX_OWN_MAC_ADDR_18,
-+	MUAR_INDEX_OWN_MAC_ADDR_19,
-+	MUAR_INDEX_OWN_MAC_ADDR_1A,
-+	MUAR_INDEX_OWN_MAC_ADDR_1B,
-+	MUAR_INDEX_OWN_MAC_ADDR_1C,
-+	MUAR_INDEX_OWN_MAC_ADDR_1D,
-+	MUAR_INDEX_OWN_MAC_ADDR_1E,
-+	MUAR_INDEX_OWN_MAC_ADDR_1F,
-+	MUAR_INDEX_OWN_MAC_ADDR_20,
-+	MUAR_INDEX_OWN_MAC_ADDR_21,
-+	MUAR_INDEX_OWN_MAC_ADDR_22,
-+	MUAR_INDEX_OWN_MAC_ADDR_23,
-+	MUAR_INDEX_OWN_MAC_ADDR_24,
-+	MUAR_INDEX_OWN_MAC_ADDR_25,
-+	MUAR_INDEX_OWN_MAC_ADDR_26,
-+	MUAR_INDEX_OWN_MAC_ADDR_27,
-+	MUAR_INDEX_OWN_MAC_ADDR_28,
-+	MUAR_INDEX_OWN_MAC_ADDR_29,
-+	MUAR_INDEX_OWN_MAC_ADDR_2A,
-+	MUAR_INDEX_OWN_MAC_ADDR_2B,
-+	MUAR_INDEX_OWN_MAC_ADDR_2C,
-+	MUAR_INDEX_OWN_MAC_ADDR_2D,
-+	MUAR_INDEX_OWN_MAC_ADDR_2E,
-+	MUAR_INDEX_OWN_MAC_ADDR_2F
-+};
-+
-+enum cipher_suit {
-+	IGTK_CIPHER_SUIT_NONE = 0,
-+	IGTK_CIPHER_SUIT_BIP,
-+	IGTK_CIPHER_SUIT_BIP_256
-+};
-+
-+#define LWTBL_LEN_IN_DW			36
-+#define UWTBL_LEN_IN_DW			16
-+
-+#define MT_DBG_WTBL_BASE		0x820D8000
-+
-+#define MT_DBG_WTBLON_TOP_BASE		0x820d4000
-+#define MT_DBG_WTBLON_TOP_WDUCR_ADDR	(MT_DBG_WTBLON_TOP_BASE + 0x0370) // 4370
-+#define MT_DBG_WTBLON_TOP_WDUCR_GROUP	GENMASK(4, 0)
-+
-+#define MT_DBG_UWTBL_TOP_BASE		0x820c4000
-+#define MT_DBG_UWTBL_TOP_WDUCR_ADDR	(MT_DBG_UWTBL_TOP_BASE + 0x0104) // 4104
-+#define MT_DBG_UWTBL_TOP_WDUCR_GROUP	GENMASK(5, 0)
-+#define MT_DBG_UWTBL_TOP_WDUCR_TARGET	BIT(31)
-+
-+#define LWTBL_IDX2BASE_ID		GENMASK(14, 8)
-+#define LWTBL_IDX2BASE_DW		GENMASK(7, 2)
-+#define LWTBL_IDX2BASE(_id, _dw)	(MT_DBG_WTBL_BASE | \
-+					FIELD_PREP(LWTBL_IDX2BASE_ID, _id) | \
-+					FIELD_PREP(LWTBL_IDX2BASE_DW, _dw))
-+
-+#define UWTBL_IDX2BASE_ID		GENMASK(12, 6)
-+#define UWTBL_IDX2BASE_DW		GENMASK(5, 2)
-+#define UWTBL_IDX2BASE(_id, _dw)	(MT_DBG_UWTBL_TOP_BASE | 0x2000 | \
-+					FIELD_PREP(UWTBL_IDX2BASE_ID, _id) | \
-+					FIELD_PREP(UWTBL_IDX2BASE_DW, _dw))
-+
-+#define KEYTBL_IDX2BASE_KEY		GENMASK(12, 6)
-+#define KEYTBL_IDX2BASE_DW		GENMASK(5, 2)
-+#define KEYTBL_IDX2BASE(_key, _dw)	(MT_DBG_UWTBL_TOP_BASE | 0x2000 | \
-+					FIELD_PREP(KEYTBL_IDX2BASE_KEY, _key) | \
-+					FIELD_PREP(KEYTBL_IDX2BASE_DW, _dw))
-+
-+// UMAC WTBL
-+// DW0
-+#define WF_UWTBL_PEER_MLD_ADDRESS_47_32__DW                         0
-+#define WF_UWTBL_PEER_MLD_ADDRESS_47_32__ADDR                       0
-+#define WF_UWTBL_PEER_MLD_ADDRESS_47_32__MASK                       0x0000ffff // 15- 0
-+#define WF_UWTBL_PEER_MLD_ADDRESS_47_32__SHIFT                      0
-+#define WF_UWTBL_OWN_MLD_ID_DW                                      0
-+#define WF_UWTBL_OWN_MLD_ID_ADDR                                    0
-+#define WF_UWTBL_OWN_MLD_ID_MASK                                    0x003f0000 // 21-16
-+#define WF_UWTBL_OWN_MLD_ID_SHIFT                                   16
-+// DW1
-+#define WF_UWTBL_PEER_MLD_ADDRESS_31_0__DW                          1
-+#define WF_UWTBL_PEER_MLD_ADDRESS_31_0__ADDR                        4
-+#define WF_UWTBL_PEER_MLD_ADDRESS_31_0__MASK                        0xffffffff // 31- 0
-+#define WF_UWTBL_PEER_MLD_ADDRESS_31_0__SHIFT                       0
-+// DW2
-+#define WF_UWTBL_PN_31_0__DW                                        2
-+#define WF_UWTBL_PN_31_0__ADDR                                      8
-+#define WF_UWTBL_PN_31_0__MASK                                      0xffffffff // 31- 0
-+#define WF_UWTBL_PN_31_0__SHIFT                                     0
-+// DW3
-+#define WF_UWTBL_PN_47_32__DW                                       3
-+#define WF_UWTBL_PN_47_32__ADDR                                     12
-+#define WF_UWTBL_PN_47_32__MASK                                     0x0000ffff // 15- 0
-+#define WF_UWTBL_PN_47_32__SHIFT                                    0
-+#define WF_UWTBL_COM_SN_DW                                          3
-+#define WF_UWTBL_COM_SN_ADDR                                        12
-+#define WF_UWTBL_COM_SN_MASK                                        0x0fff0000 // 27-16
-+#define WF_UWTBL_COM_SN_SHIFT                                       16
-+// DW4
-+#define WF_UWTBL_TID0_SN_DW                                         4
-+#define WF_UWTBL_TID0_SN_ADDR                                       16
-+#define WF_UWTBL_TID0_SN_MASK                                       0x00000fff // 11- 0
-+#define WF_UWTBL_TID0_SN_SHIFT                                      0
-+#define WF_UWTBL_RX_BIPN_31_0__DW                                   4
-+#define WF_UWTBL_RX_BIPN_31_0__ADDR                                 16
-+#define WF_UWTBL_RX_BIPN_31_0__MASK                                 0xffffffff // 31- 0
-+#define WF_UWTBL_RX_BIPN_31_0__SHIFT                                0
-+#define WF_UWTBL_TID1_SN_DW                                         4
-+#define WF_UWTBL_TID1_SN_ADDR                                       16
-+#define WF_UWTBL_TID1_SN_MASK                                       0x00fff000 // 23-12
-+#define WF_UWTBL_TID1_SN_SHIFT                                      12
-+#define WF_UWTBL_TID2_SN_7_0__DW                                    4
-+#define WF_UWTBL_TID2_SN_7_0__ADDR                                  16
-+#define WF_UWTBL_TID2_SN_7_0__MASK                                  0xff000000 // 31-24
-+#define WF_UWTBL_TID2_SN_7_0__SHIFT                                 24
-+// DW5
-+#define WF_UWTBL_TID2_SN_11_8__DW                                   5
-+#define WF_UWTBL_TID2_SN_11_8__ADDR                                 20
-+#define WF_UWTBL_TID2_SN_11_8__MASK                                 0x0000000f //  3- 0
-+#define WF_UWTBL_TID2_SN_11_8__SHIFT                                0
-+#define WF_UWTBL_RX_BIPN_47_32__DW                                  5
-+#define WF_UWTBL_RX_BIPN_47_32__ADDR                                20
-+#define WF_UWTBL_RX_BIPN_47_32__MASK                                0x0000ffff // 15- 0
-+#define WF_UWTBL_RX_BIPN_47_32__SHIFT                               0
-+#define WF_UWTBL_TID3_SN_DW                                         5
-+#define WF_UWTBL_TID3_SN_ADDR                                       20
-+#define WF_UWTBL_TID3_SN_MASK                                       0x0000fff0 // 15- 4
-+#define WF_UWTBL_TID3_SN_SHIFT                                      4
-+#define WF_UWTBL_TID4_SN_DW                                         5
-+#define WF_UWTBL_TID4_SN_ADDR                                       20
-+#define WF_UWTBL_TID4_SN_MASK                                       0x0fff0000 // 27-16
-+#define WF_UWTBL_TID4_SN_SHIFT                                      16
-+#define WF_UWTBL_TID5_SN_3_0__DW                                    5
-+#define WF_UWTBL_TID5_SN_3_0__ADDR                                  20
-+#define WF_UWTBL_TID5_SN_3_0__MASK                                  0xf0000000 // 31-28
-+#define WF_UWTBL_TID5_SN_3_0__SHIFT                                 28
-+// DW6
-+#define WF_UWTBL_TID5_SN_11_4__DW                                   6
-+#define WF_UWTBL_TID5_SN_11_4__ADDR                                 24
-+#define WF_UWTBL_TID5_SN_11_4__MASK                                 0x000000ff //  7- 0
-+#define WF_UWTBL_TID5_SN_11_4__SHIFT                                0
-+#define WF_UWTBL_KEY_LOC2_DW                                        6
-+#define WF_UWTBL_KEY_LOC2_ADDR                                      24
-+#define WF_UWTBL_KEY_LOC2_MASK                                      0x00001fff // 12- 0
-+#define WF_UWTBL_KEY_LOC2_SHIFT                                     0
-+#define WF_UWTBL_TID6_SN_DW                                         6
-+#define WF_UWTBL_TID6_SN_ADDR                                       24
-+#define WF_UWTBL_TID6_SN_MASK                                       0x000fff00 // 19- 8
-+#define WF_UWTBL_TID6_SN_SHIFT                                      8
-+#define WF_UWTBL_TID7_SN_DW                                         6
-+#define WF_UWTBL_TID7_SN_ADDR                                       24
-+#define WF_UWTBL_TID7_SN_MASK                                       0xfff00000 // 31-20
-+#define WF_UWTBL_TID7_SN_SHIFT                                      20
-+// DW7
-+#define WF_UWTBL_KEY_LOC0_DW                                        7
-+#define WF_UWTBL_KEY_LOC0_ADDR                                      28
-+#define WF_UWTBL_KEY_LOC0_MASK                                      0x00001fff // 12- 0
-+#define WF_UWTBL_KEY_LOC0_SHIFT                                     0
-+#define WF_UWTBL_KEY_LOC1_DW                                        7
-+#define WF_UWTBL_KEY_LOC1_ADDR                                      28
-+#define WF_UWTBL_KEY_LOC1_MASK                                      0x1fff0000 // 28-16
-+#define WF_UWTBL_KEY_LOC1_SHIFT                                     16
-+// DW8
-+#define WF_UWTBL_AMSDU_CFG_DW                                       8
-+#define WF_UWTBL_AMSDU_CFG_ADDR                                     32
-+#define WF_UWTBL_AMSDU_CFG_MASK                                     0x00000fff // 11- 0
-+#define WF_UWTBL_AMSDU_CFG_SHIFT                                    0
-+#define WF_UWTBL_SEC_ADDR_MODE_DW                                   8
-+#define WF_UWTBL_SEC_ADDR_MODE_ADDR                                 32
-+#define WF_UWTBL_SEC_ADDR_MODE_MASK                                 0x00300000 // 21-20
-+#define WF_UWTBL_SEC_ADDR_MODE_SHIFT                                20
-+#define WF_UWTBL_WMM_Q_DW                                           8
-+#define WF_UWTBL_WMM_Q_ADDR                                         32
-+#define WF_UWTBL_WMM_Q_MASK                                         0x06000000 // 26-25
-+#define WF_UWTBL_WMM_Q_SHIFT                                        25
-+#define WF_UWTBL_QOS_DW                                             8
-+#define WF_UWTBL_QOS_ADDR                                           32
-+#define WF_UWTBL_QOS_MASK                                           0x08000000 // 27-27
-+#define WF_UWTBL_QOS_SHIFT                                          27
-+#define WF_UWTBL_HT_DW                                              8
-+#define WF_UWTBL_HT_ADDR                                            32
-+#define WF_UWTBL_HT_MASK                                            0x10000000 // 28-28
-+#define WF_UWTBL_HT_SHIFT                                           28
-+#define WF_UWTBL_HDRT_MODE_DW                                       8
-+#define WF_UWTBL_HDRT_MODE_ADDR                                     32
-+#define WF_UWTBL_HDRT_MODE_MASK                                     0x20000000 // 29-29
-+#define WF_UWTBL_HDRT_MODE_SHIFT                                    29
-+// DW9
-+#define WF_UWTBL_RELATED_IDX0_DW                                    9
-+#define WF_UWTBL_RELATED_IDX0_ADDR                                  36
-+#define WF_UWTBL_RELATED_IDX0_MASK                                  0x00000fff // 11- 0
-+#define WF_UWTBL_RELATED_IDX0_SHIFT                                 0
-+#define WF_UWTBL_RELATED_BAND0_DW                                   9
-+#define WF_UWTBL_RELATED_BAND0_ADDR                                 36
-+#define WF_UWTBL_RELATED_BAND0_MASK                                 0x00003000 // 13-12
-+#define WF_UWTBL_RELATED_BAND0_SHIFT                                12
-+#define WF_UWTBL_PRIMARY_MLD_BAND_DW                                9
-+#define WF_UWTBL_PRIMARY_MLD_BAND_ADDR                              36
-+#define WF_UWTBL_PRIMARY_MLD_BAND_MASK                              0x0000c000 // 15-14
-+#define WF_UWTBL_PRIMARY_MLD_BAND_SHIFT                             14
-+#define WF_UWTBL_RELATED_IDX1_DW                                    9
-+#define WF_UWTBL_RELATED_IDX1_ADDR                                  36
-+#define WF_UWTBL_RELATED_IDX1_MASK                                  0x0fff0000 // 27-16
-+#define WF_UWTBL_RELATED_IDX1_SHIFT                                 16
-+#define WF_UWTBL_RELATED_BAND1_DW                                   9
-+#define WF_UWTBL_RELATED_BAND1_ADDR                                 36
-+#define WF_UWTBL_RELATED_BAND1_MASK                                 0x30000000 // 29-28
-+#define WF_UWTBL_RELATED_BAND1_SHIFT                                28
-+#define WF_UWTBL_SECONDARY_MLD_BAND_DW                              9
-+#define WF_UWTBL_SECONDARY_MLD_BAND_ADDR                            36
-+#define WF_UWTBL_SECONDARY_MLD_BAND_MASK                            0xc0000000 // 31-30
-+#define WF_UWTBL_SECONDARY_MLD_BAND_SHIFT                           30
-+
-+/* LMAC WTBL */
-+// DW0
-+#define WF_LWTBL_PEER_LINK_ADDRESS_47_32__DW                        0
-+#define WF_LWTBL_PEER_LINK_ADDRESS_47_32__ADDR                      0
-+#define WF_LWTBL_PEER_LINK_ADDRESS_47_32__MASK \
-+	0x0000ffff // 15- 0
-+#define WF_LWTBL_PEER_LINK_ADDRESS_47_32__SHIFT                     0
-+#define WF_LWTBL_MUAR_DW                                            0
-+#define WF_LWTBL_MUAR_ADDR                                          0
-+#define WF_LWTBL_MUAR_MASK \
-+	0x003f0000 // 21-16
-+#define WF_LWTBL_MUAR_SHIFT                                         16
-+#define WF_LWTBL_RCA1_DW                                            0
-+#define WF_LWTBL_RCA1_ADDR                                          0
-+#define WF_LWTBL_RCA1_MASK \
-+	0x00400000 // 22-22
-+#define WF_LWTBL_RCA1_SHIFT                                         22
-+#define WF_LWTBL_KID_DW                                             0
-+#define WF_LWTBL_KID_ADDR                                           0
-+#define WF_LWTBL_KID_MASK \
-+	0x01800000 // 24-23
-+#define WF_LWTBL_KID_SHIFT                                          23
-+#define WF_LWTBL_RCID_DW                                            0
-+#define WF_LWTBL_RCID_ADDR                                          0
-+#define WF_LWTBL_RCID_MASK \
-+	0x02000000 // 25-25
-+#define WF_LWTBL_RCID_SHIFT                                         25
-+#define WF_LWTBL_BAND_DW                                            0
-+#define WF_LWTBL_BAND_ADDR                                          0
-+#define WF_LWTBL_BAND_MASK \
-+	0x0c000000 // 27-26
-+#define WF_LWTBL_BAND_SHIFT                                         26
-+#define WF_LWTBL_RV_DW                                              0
-+#define WF_LWTBL_RV_ADDR                                            0
-+#define WF_LWTBL_RV_MASK \
-+	0x10000000 // 28-28
-+#define WF_LWTBL_RV_SHIFT                                           28
-+#define WF_LWTBL_RCA2_DW                                            0
-+#define WF_LWTBL_RCA2_ADDR                                          0
-+#define WF_LWTBL_RCA2_MASK \
-+	0x20000000 // 29-29
-+#define WF_LWTBL_RCA2_SHIFT                                         29
-+#define WF_LWTBL_WPI_FLAG_DW                                        0
-+#define WF_LWTBL_WPI_FLAG_ADDR                                      0
-+#define WF_LWTBL_WPI_FLAG_MASK \
-+	0x40000000 // 30-30
-+#define WF_LWTBL_WPI_FLAG_SHIFT                                     30
-+// DW1
-+#define WF_LWTBL_PEER_LINK_ADDRESS_31_0__DW                         1
-+#define WF_LWTBL_PEER_LINK_ADDRESS_31_0__ADDR                       4
-+#define WF_LWTBL_PEER_LINK_ADDRESS_31_0__MASK \
-+	0xffffffff // 31- 0
-+#define WF_LWTBL_PEER_LINK_ADDRESS_31_0__SHIFT                      0
-+// DW2
-+#define WF_LWTBL_AID_DW                                             2
-+#define WF_LWTBL_AID_ADDR                                           8
-+#define WF_LWTBL_AID_MASK \
-+	0x00000fff // 11- 0
-+#define WF_LWTBL_AID_SHIFT                                          0
-+#define WF_LWTBL_GID_SU_DW                                          2
-+#define WF_LWTBL_GID_SU_ADDR                                        8
-+#define WF_LWTBL_GID_SU_MASK \
-+	0x00001000 // 12-12
-+#define WF_LWTBL_GID_SU_SHIFT                                       12
-+#define WF_LWTBL_SPP_EN_DW                                          2
-+#define WF_LWTBL_SPP_EN_ADDR                                        8
-+#define WF_LWTBL_SPP_EN_MASK \
-+	0x00002000 // 13-13
-+#define WF_LWTBL_SPP_EN_SHIFT                                       13
-+#define WF_LWTBL_WPI_EVEN_DW                                        2
-+#define WF_LWTBL_WPI_EVEN_ADDR                                      8
-+#define WF_LWTBL_WPI_EVEN_MASK \
-+	0x00004000 // 14-14
-+#define WF_LWTBL_WPI_EVEN_SHIFT                                     14
-+#define WF_LWTBL_AAD_OM_DW                                          2
-+#define WF_LWTBL_AAD_OM_ADDR                                        8
-+#define WF_LWTBL_AAD_OM_MASK \
-+	0x00008000 // 15-15
-+#define WF_LWTBL_AAD_OM_SHIFT                                       15
-+/* kite DW2 field bit 13-14 */
-+#define WF_LWTBL_DUAL_PTEC_EN_DW                                    2
-+#define WF_LWTBL_DUAL_PTEC_EN_ADDR                                  8
-+#define WF_LWTBL_DUAL_PTEC_EN_MASK \
-+	0x00002000 // 13-13
-+#define WF_LWTBL_DUAL_PTEC_EN_SHIFT                                 13
-+#define WF_LWTBL_DUAL_CTS_CAP_DW                                    2
-+#define WF_LWTBL_DUAL_CTS_CAP_ADDR                                  8
-+#define WF_LWTBL_DUAL_CTS_CAP_MASK \
-+	0x00004000 // 14-14
-+#define WF_LWTBL_DUAL_CTS_CAP_SHIFT                                 14
-+#define WF_LWTBL_CIPHER_SUIT_PGTK_DW                                2
-+#define WF_LWTBL_CIPHER_SUIT_PGTK_ADDR                              8
-+#define WF_LWTBL_CIPHER_SUIT_PGTK_MASK \
-+	0x001f0000 // 20-16
-+#define WF_LWTBL_CIPHER_SUIT_PGTK_SHIFT                             16
-+#define WF_LWTBL_FD_DW                                              2
-+#define WF_LWTBL_FD_ADDR                                            8
-+#define WF_LWTBL_FD_MASK \
-+	0x00200000 // 21-21
-+#define WF_LWTBL_FD_SHIFT                                           21
-+#define WF_LWTBL_TD_DW                                              2
-+#define WF_LWTBL_TD_ADDR                                            8
-+#define WF_LWTBL_TD_MASK \
-+	0x00400000 // 22-22
-+#define WF_LWTBL_TD_SHIFT                                           22
-+#define WF_LWTBL_SW_DW                                              2
-+#define WF_LWTBL_SW_ADDR                                            8
-+#define WF_LWTBL_SW_MASK \
-+	0x00800000 // 23-23
-+#define WF_LWTBL_SW_SHIFT                                           23
-+#define WF_LWTBL_UL_DW                                              2
-+#define WF_LWTBL_UL_ADDR                                            8
-+#define WF_LWTBL_UL_MASK \
-+	0x01000000 // 24-24
-+#define WF_LWTBL_UL_SHIFT                                           24
-+#define WF_LWTBL_TX_PS_DW                                           2
-+#define WF_LWTBL_TX_PS_ADDR                                         8
-+#define WF_LWTBL_TX_PS_MASK \
-+	0x02000000 // 25-25
-+#define WF_LWTBL_TX_PS_SHIFT                                        25
-+#define WF_LWTBL_QOS_DW                                             2
-+#define WF_LWTBL_QOS_ADDR                                           8
-+#define WF_LWTBL_QOS_MASK \
-+	0x04000000 // 26-26
-+#define WF_LWTBL_QOS_SHIFT                                          26
-+#define WF_LWTBL_HT_DW                                              2
-+#define WF_LWTBL_HT_ADDR                                            8
-+#define WF_LWTBL_HT_MASK \
-+	0x08000000 // 27-27
-+#define WF_LWTBL_HT_SHIFT                                           27
-+#define WF_LWTBL_VHT_DW                                             2
-+#define WF_LWTBL_VHT_ADDR                                           8
-+#define WF_LWTBL_VHT_MASK \
-+	0x10000000 // 28-28
-+#define WF_LWTBL_VHT_SHIFT                                          28
-+#define WF_LWTBL_HE_DW                                              2
-+#define WF_LWTBL_HE_ADDR                                            8
-+#define WF_LWTBL_HE_MASK \
-+	0x20000000 // 29-29
-+#define WF_LWTBL_HE_SHIFT                                           29
-+#define WF_LWTBL_EHT_DW                                             2
-+#define WF_LWTBL_EHT_ADDR                                           8
-+#define WF_LWTBL_EHT_MASK \
-+	0x40000000 // 30-30
-+#define WF_LWTBL_EHT_SHIFT                                          30
-+#define WF_LWTBL_MESH_DW                                            2
-+#define WF_LWTBL_MESH_ADDR                                          8
-+#define WF_LWTBL_MESH_MASK \
-+	0x80000000 // 31-31
-+#define WF_LWTBL_MESH_SHIFT                                         31
-+// DW3
-+#define WF_LWTBL_WMM_Q_DW                                           3
-+#define WF_LWTBL_WMM_Q_ADDR                                         12
-+#define WF_LWTBL_WMM_Q_MASK \
-+	0x00000003 // 1- 0
-+#define WF_LWTBL_WMM_Q_SHIFT                                        0
-+#define WF_LWTBL_EHT_SIG_MCS_DW                                     3
-+#define WF_LWTBL_EHT_SIG_MCS_ADDR                                   12
-+#define WF_LWTBL_EHT_SIG_MCS_MASK \
-+	0x0000000c // 3- 2
-+#define WF_LWTBL_EHT_SIG_MCS_SHIFT                                  2
-+#define WF_LWTBL_HDRT_MODE_DW                                       3
-+#define WF_LWTBL_HDRT_MODE_ADDR                                     12
-+#define WF_LWTBL_HDRT_MODE_MASK \
-+	0x00000010 // 4- 4
-+#define WF_LWTBL_HDRT_MODE_SHIFT                                    4
-+#define WF_LWTBL_BEAM_CHG_DW                                        3
-+#define WF_LWTBL_BEAM_CHG_ADDR                                      12
-+#define WF_LWTBL_BEAM_CHG_MASK \
-+	0x00000020 // 5- 5
-+#define WF_LWTBL_BEAM_CHG_SHIFT                                     5
-+#define WF_LWTBL_EHT_LTF_SYM_NUM_OPT_DW                             3
-+#define WF_LWTBL_EHT_LTF_SYM_NUM_OPT_ADDR                           12
-+#define WF_LWTBL_EHT_LTF_SYM_NUM_OPT_MASK \
-+	0x000000c0 // 7- 6
-+#define WF_LWTBL_EHT_LTF_SYM_NUM_OPT_SHIFT                          6
-+#define WF_LWTBL_PFMU_IDX_DW                                        3
-+#define WF_LWTBL_PFMU_IDX_ADDR                                      12
-+#define WF_LWTBL_PFMU_IDX_MASK \
-+	0x0000ff00 // 15- 8
-+#define WF_LWTBL_PFMU_IDX_SHIFT                                     8
-+#define WF_LWTBL_ULPF_IDX_DW                                        3
-+#define WF_LWTBL_ULPF_IDX_ADDR                                      12
-+#define WF_LWTBL_ULPF_IDX_MASK \
-+	0x00ff0000 // 23-16
-+#define WF_LWTBL_ULPF_IDX_SHIFT                                     16
-+#define WF_LWTBL_RIBF_DW                                            3
-+#define WF_LWTBL_RIBF_ADDR                                          12
-+#define WF_LWTBL_RIBF_MASK \
-+	0x01000000 // 24-24
-+#define WF_LWTBL_RIBF_SHIFT                                         24
-+#define WF_LWTBL_ULPF_DW                                            3
-+#define WF_LWTBL_ULPF_ADDR                                          12
-+#define WF_LWTBL_ULPF_MASK \
-+	0x02000000 // 25-25
-+#define WF_LWTBL_ULPF_SHIFT                                         25
-+#define WF_LWTBL_BYPASS_TXSMM_DW                                    3
-+#define WF_LWTBL_BYPASS_TXSMM_ADDR                                  12
-+#define WF_LWTBL_BYPASS_TXSMM_MASK \
-+	0x04000000 // 26-26
-+#define WF_LWTBL_BYPASS_TXSMM_SHIFT                                 26
-+#define WF_LWTBL_TBF_HT_DW                                          3
-+#define WF_LWTBL_TBF_HT_ADDR                                        12
-+#define WF_LWTBL_TBF_HT_MASK \
-+	0x08000000 // 27-27
-+#define WF_LWTBL_TBF_HT_SHIFT                                       27
-+#define WF_LWTBL_TBF_VHT_DW                                         3
-+#define WF_LWTBL_TBF_VHT_ADDR                                       12
-+#define WF_LWTBL_TBF_VHT_MASK \
-+	0x10000000 // 28-28
-+#define WF_LWTBL_TBF_VHT_SHIFT                                      28
-+#define WF_LWTBL_TBF_HE_DW                                          3
-+#define WF_LWTBL_TBF_HE_ADDR                                        12
-+#define WF_LWTBL_TBF_HE_MASK \
-+	0x20000000 // 29-29
-+#define WF_LWTBL_TBF_HE_SHIFT                                       29
-+#define WF_LWTBL_TBF_EHT_DW                                         3
-+#define WF_LWTBL_TBF_EHT_ADDR                                       12
-+#define WF_LWTBL_TBF_EHT_MASK \
-+	0x40000000 // 30-30
-+#define WF_LWTBL_TBF_EHT_SHIFT                                      30
-+#define WF_LWTBL_IGN_FBK_DW                                         3
-+#define WF_LWTBL_IGN_FBK_ADDR                                       12
-+#define WF_LWTBL_IGN_FBK_MASK \
-+	0x80000000 // 31-31
-+#define WF_LWTBL_IGN_FBK_SHIFT                                      31
-+// DW4
-+#define WF_LWTBL_NEGOTIATED_WINSIZE0_DW                             4
-+#define WF_LWTBL_NEGOTIATED_WINSIZE0_ADDR                           16
-+#define WF_LWTBL_NEGOTIATED_WINSIZE0_MASK \
-+	0x00000007 // 2- 0
-+#define WF_LWTBL_NEGOTIATED_WINSIZE0_SHIFT                          0
-+#define WF_LWTBL_NEGOTIATED_WINSIZE1_DW                             4
-+#define WF_LWTBL_NEGOTIATED_WINSIZE1_ADDR                           16
-+#define WF_LWTBL_NEGOTIATED_WINSIZE1_MASK \
-+	0x00000038 // 5- 3
-+#define WF_LWTBL_NEGOTIATED_WINSIZE1_SHIFT                          3
-+#define WF_LWTBL_NEGOTIATED_WINSIZE2_DW                             4
-+#define WF_LWTBL_NEGOTIATED_WINSIZE2_ADDR                           16
-+#define WF_LWTBL_NEGOTIATED_WINSIZE2_MASK \
-+	0x000001c0 // 8- 6
-+#define WF_LWTBL_NEGOTIATED_WINSIZE2_SHIFT                          6
-+#define WF_LWTBL_NEGOTIATED_WINSIZE3_DW                             4
-+#define WF_LWTBL_NEGOTIATED_WINSIZE3_ADDR                           16
-+#define WF_LWTBL_NEGOTIATED_WINSIZE3_MASK \
-+	0x00000e00 // 11- 9
-+#define WF_LWTBL_NEGOTIATED_WINSIZE3_SHIFT                          9
-+#define WF_LWTBL_NEGOTIATED_WINSIZE4_DW                             4
-+#define WF_LWTBL_NEGOTIATED_WINSIZE4_ADDR                           16
-+#define WF_LWTBL_NEGOTIATED_WINSIZE4_MASK \
-+	0x00007000 // 14-12
-+#define WF_LWTBL_NEGOTIATED_WINSIZE4_SHIFT                          12
-+#define WF_LWTBL_NEGOTIATED_WINSIZE5_DW                             4
-+#define WF_LWTBL_NEGOTIATED_WINSIZE5_ADDR                           16
-+#define WF_LWTBL_NEGOTIATED_WINSIZE5_MASK \
-+	0x00038000 // 17-15
-+#define WF_LWTBL_NEGOTIATED_WINSIZE5_SHIFT                          15
-+#define WF_LWTBL_NEGOTIATED_WINSIZE6_DW                             4
-+#define WF_LWTBL_NEGOTIATED_WINSIZE6_ADDR                           16
-+#define WF_LWTBL_NEGOTIATED_WINSIZE6_MASK \
-+	0x001c0000 // 20-18
-+#define WF_LWTBL_NEGOTIATED_WINSIZE6_SHIFT                          18
-+#define WF_LWTBL_NEGOTIATED_WINSIZE7_DW                             4
-+#define WF_LWTBL_NEGOTIATED_WINSIZE7_ADDR                           16
-+#define WF_LWTBL_NEGOTIATED_WINSIZE7_MASK \
-+	0x00e00000 // 23-21
-+#define WF_LWTBL_NEGOTIATED_WINSIZE7_SHIFT                          21
-+#define WF_LWTBL_PE_DW                                              4
-+#define WF_LWTBL_PE_ADDR                                            16
-+#define WF_LWTBL_PE_MASK \
-+	0x03000000 // 25-24
-+#define WF_LWTBL_PE_SHIFT                                           24
-+#define WF_LWTBL_DIS_RHTR_DW                                        4
-+#define WF_LWTBL_DIS_RHTR_ADDR                                      16
-+#define WF_LWTBL_DIS_RHTR_MASK \
-+	0x04000000 // 26-26
-+#define WF_LWTBL_DIS_RHTR_SHIFT                                     26
-+#define WF_LWTBL_LDPC_HT_DW                                         4
-+#define WF_LWTBL_LDPC_HT_ADDR                                       16
-+#define WF_LWTBL_LDPC_HT_MASK \
-+	0x08000000 // 27-27
-+#define WF_LWTBL_LDPC_HT_SHIFT                                      27
-+#define WF_LWTBL_LDPC_VHT_DW                                        4
-+#define WF_LWTBL_LDPC_VHT_ADDR                                      16
-+#define WF_LWTBL_LDPC_VHT_MASK \
-+	0x10000000 // 28-28
-+#define WF_LWTBL_LDPC_VHT_SHIFT                                     28
-+#define WF_LWTBL_LDPC_HE_DW                                         4
-+#define WF_LWTBL_LDPC_HE_ADDR                                       16
-+#define WF_LWTBL_LDPC_HE_MASK \
-+	0x20000000 // 29-29
-+#define WF_LWTBL_LDPC_HE_SHIFT                                      29
-+#define WF_LWTBL_LDPC_EHT_DW                                        4
-+#define WF_LWTBL_LDPC_EHT_ADDR                                      16
-+#define WF_LWTBL_LDPC_EHT_MASK \
-+	0x40000000 // 30-30
-+#define WF_LWTBL_LDPC_EHT_SHIFT                                     30
-+#define WF_LWTBL_BA_MODE_DW                                         4
-+#define WF_LWTBL_BA_MODE_ADDR                                       16
-+#define WF_LWTBL_BA_MODE_MASK \
-+	0x80000000 // 31-31
-+#define WF_LWTBL_BA_MODE_SHIFT                                      31
-+// DW5
-+#define WF_LWTBL_AF_DW                                              5
-+#define WF_LWTBL_AF_ADDR                                            20
-+#define WF_LWTBL_AF_MASK \
-+	0x00000007 // 2- 0
-+#define WF_LWTBL_AF_MASK_7992 \
-+	0x0000000f // 3- 0
-+#define WF_LWTBL_AF_SHIFT                                           0
-+#define WF_LWTBL_AF_HE_DW                                           5
-+#define WF_LWTBL_AF_HE_ADDR                                         20
-+#define WF_LWTBL_AF_HE_MASK \
-+	0x00000018 // 4- 3
-+#define WF_LWTBL_AF_HE_SHIFT                                        3
-+#define WF_LWTBL_RTS_DW                                             5
-+#define WF_LWTBL_RTS_ADDR                                           20
-+#define WF_LWTBL_RTS_MASK \
-+	0x00000020 // 5- 5
-+#define WF_LWTBL_RTS_SHIFT                                          5
-+#define WF_LWTBL_SMPS_DW                                            5
-+#define WF_LWTBL_SMPS_ADDR                                          20
-+#define WF_LWTBL_SMPS_MASK \
-+	0x00000040 // 6- 6
-+#define WF_LWTBL_SMPS_SHIFT                                         6
-+#define WF_LWTBL_DYN_BW_DW                                          5
-+#define WF_LWTBL_DYN_BW_ADDR                                        20
-+#define WF_LWTBL_DYN_BW_MASK \
-+	0x00000080 // 7- 7
-+#define WF_LWTBL_DYN_BW_SHIFT                                       7
-+#define WF_LWTBL_MMSS_DW                                            5
-+#define WF_LWTBL_MMSS_ADDR                                          20
-+#define WF_LWTBL_MMSS_MASK \
-+	0x00000700 // 10- 8
-+#define WF_LWTBL_MMSS_SHIFT                                         8
-+#define WF_LWTBL_USR_DW                                             5
-+#define WF_LWTBL_USR_ADDR                                           20
-+#define WF_LWTBL_USR_MASK \
-+	0x00000800 // 11-11
-+#define WF_LWTBL_USR_SHIFT                                          11
-+#define WF_LWTBL_SR_R_DW                                            5
-+#define WF_LWTBL_SR_R_ADDR                                          20
-+#define WF_LWTBL_SR_R_MASK \
-+	0x00007000 // 14-12
-+#define WF_LWTBL_SR_R_SHIFT                                         12
-+#define WF_LWTBL_SR_ABORT_DW                                        5
-+#define WF_LWTBL_SR_ABORT_ADDR                                      20
-+#define WF_LWTBL_SR_ABORT_MASK \
-+	0x00008000 // 15-15
-+#define WF_LWTBL_SR_ABORT_SHIFT                                     15
-+#define WF_LWTBL_TX_POWER_OFFSET_DW                                 5
-+#define WF_LWTBL_TX_POWER_OFFSET_ADDR                               20
-+#define WF_LWTBL_TX_POWER_OFFSET_MASK \
-+	0x003f0000 // 21-16
-+#define WF_LWTBL_TX_POWER_OFFSET_SHIFT                              16
-+#define WF_LWTBL_LTF_EHT_DW                                         5
-+#define WF_LWTBL_LTF_EHT_ADDR                                       20
-+#define WF_LWTBL_LTF_EHT_MASK \
-+	0x00c00000 // 23-22
-+#define WF_LWTBL_LTF_EHT_SHIFT                                      22
-+#define WF_LWTBL_GI_EHT_DW                                          5
-+#define WF_LWTBL_GI_EHT_ADDR                                        20
-+#define WF_LWTBL_GI_EHT_MASK \
-+	0x03000000 // 25-24
-+#define WF_LWTBL_GI_EHT_SHIFT                                       24
-+#define WF_LWTBL_DOPPL_DW                                           5
-+#define WF_LWTBL_DOPPL_ADDR                                         20
-+#define WF_LWTBL_DOPPL_MASK \
-+	0x04000000 // 26-26
-+#define WF_LWTBL_DOPPL_SHIFT                                        26
-+#define WF_LWTBL_TXOP_PS_CAP_DW                                     5
-+#define WF_LWTBL_TXOP_PS_CAP_ADDR                                   20
-+#define WF_LWTBL_TXOP_PS_CAP_MASK \
-+	0x08000000 // 27-27
-+#define WF_LWTBL_TXOP_PS_CAP_SHIFT                                  27
-+#define WF_LWTBL_DU_I_PSM_DW                                        5
-+#define WF_LWTBL_DU_I_PSM_ADDR                                      20
-+#define WF_LWTBL_DU_I_PSM_MASK \
-+	0x10000000 // 28-28
-+#define WF_LWTBL_DU_I_PSM_SHIFT                                     28
-+#define WF_LWTBL_I_PSM_DW                                           5
-+#define WF_LWTBL_I_PSM_ADDR                                         20
-+#define WF_LWTBL_I_PSM_MASK \
-+	0x20000000 // 29-29
-+#define WF_LWTBL_I_PSM_SHIFT                                        29
-+#define WF_LWTBL_PSM_DW                                             5
-+#define WF_LWTBL_PSM_ADDR                                           20
-+#define WF_LWTBL_PSM_MASK \
-+	0x40000000 // 30-30
-+#define WF_LWTBL_PSM_SHIFT                                          30
-+#define WF_LWTBL_SKIP_TX_DW                                         5
-+#define WF_LWTBL_SKIP_TX_ADDR                                       20
-+#define WF_LWTBL_SKIP_TX_MASK \
-+	0x80000000 // 31-31
-+#define WF_LWTBL_SKIP_TX_SHIFT                                      31
-+// DW6
-+#define WF_LWTBL_CBRN_DW                                            6
-+#define WF_LWTBL_CBRN_ADDR                                          24
-+#define WF_LWTBL_CBRN_MASK \
-+	0x00000007 // 2- 0
-+#define WF_LWTBL_CBRN_SHIFT                                         0
-+#define WF_LWTBL_DBNSS_EN_DW                                        6
-+#define WF_LWTBL_DBNSS_EN_ADDR                                      24
-+#define WF_LWTBL_DBNSS_EN_MASK \
-+	0x00000008 // 3- 3
-+#define WF_LWTBL_DBNSS_EN_SHIFT                                     3
-+#define WF_LWTBL_BAF_EN_DW                                          6
-+#define WF_LWTBL_BAF_EN_ADDR                                        24
-+#define WF_LWTBL_BAF_EN_MASK \
-+	0x00000010 // 4- 4
-+#define WF_LWTBL_BAF_EN_SHIFT                                       4
-+#define WF_LWTBL_RDGBA_DW                                           6
-+#define WF_LWTBL_RDGBA_ADDR                                         24
-+#define WF_LWTBL_RDGBA_MASK \
-+	0x00000020 // 5- 5
-+#define WF_LWTBL_RDGBA_SHIFT                                        5
-+#define WF_LWTBL_R_DW                                               6
-+#define WF_LWTBL_R_ADDR                                             24
-+#define WF_LWTBL_R_MASK \
-+	0x00000040 // 6- 6
-+#define WF_LWTBL_R_SHIFT                                            6
-+#define WF_LWTBL_SPE_IDX_DW                                         6
-+#define WF_LWTBL_SPE_IDX_ADDR                                       24
-+#define WF_LWTBL_SPE_IDX_MASK \
-+	0x00000f80 // 11- 7
-+#define WF_LWTBL_SPE_IDX_SHIFT                                      7
-+#define WF_LWTBL_G2_DW                                              6
-+#define WF_LWTBL_G2_ADDR                                            24
-+#define WF_LWTBL_G2_MASK \
-+	0x00001000 // 12-12
-+#define WF_LWTBL_G2_SHIFT                                           12
-+#define WF_LWTBL_G4_DW                                              6
-+#define WF_LWTBL_G4_ADDR                                            24
-+#define WF_LWTBL_G4_MASK \
-+	0x00002000 // 13-13
-+#define WF_LWTBL_G4_SHIFT                                           13
-+#define WF_LWTBL_G8_DW                                              6
-+#define WF_LWTBL_G8_ADDR                                            24
-+#define WF_LWTBL_G8_MASK \
-+	0x00004000 // 14-14
-+#define WF_LWTBL_G8_SHIFT                                           14
-+#define WF_LWTBL_G16_DW                                             6
-+#define WF_LWTBL_G16_ADDR                                           24
-+#define WF_LWTBL_G16_MASK \
-+	0x00008000 // 15-15
-+#define WF_LWTBL_G16_SHIFT                                          15
-+#define WF_LWTBL_G2_LTF_DW                                          6
-+#define WF_LWTBL_G2_LTF_ADDR                                        24
-+#define WF_LWTBL_G2_LTF_MASK \
-+	0x00030000 // 17-16
-+#define WF_LWTBL_G2_LTF_SHIFT                                       16
-+#define WF_LWTBL_G4_LTF_DW                                          6
-+#define WF_LWTBL_G4_LTF_ADDR                                        24
-+#define WF_LWTBL_G4_LTF_MASK \
-+	0x000c0000 // 19-18
-+#define WF_LWTBL_G4_LTF_SHIFT                                       18
-+#define WF_LWTBL_G8_LTF_DW                                          6
-+#define WF_LWTBL_G8_LTF_ADDR                                        24
-+#define WF_LWTBL_G8_LTF_MASK \
-+	0x00300000 // 21-20
-+#define WF_LWTBL_G8_LTF_SHIFT                                       20
-+#define WF_LWTBL_G16_LTF_DW                                         6
-+#define WF_LWTBL_G16_LTF_ADDR                                       24
-+#define WF_LWTBL_G16_LTF_MASK \
-+	0x00c00000 // 23-22
-+#define WF_LWTBL_G16_LTF_SHIFT                                      22
-+#define WF_LWTBL_G2_HE_DW                                           6
-+#define WF_LWTBL_G2_HE_ADDR                                         24
-+#define WF_LWTBL_G2_HE_MASK \
-+	0x03000000 // 25-24
-+#define WF_LWTBL_G2_HE_SHIFT                                        24
-+#define WF_LWTBL_G4_HE_DW                                           6
-+#define WF_LWTBL_G4_HE_ADDR                                         24
-+#define WF_LWTBL_G4_HE_MASK \
-+	0x0c000000 // 27-26
-+#define WF_LWTBL_G4_HE_SHIFT                                        26
-+#define WF_LWTBL_G8_HE_DW                                           6
-+#define WF_LWTBL_G8_HE_ADDR                                         24
-+#define WF_LWTBL_G8_HE_MASK \
-+	0x30000000 // 29-28
-+#define WF_LWTBL_G8_HE_SHIFT                                        28
-+#define WF_LWTBL_G16_HE_DW                                          6
-+#define WF_LWTBL_G16_HE_ADDR                                        24
-+#define WF_LWTBL_G16_HE_MASK \
-+	0xc0000000 // 31-30
-+#define WF_LWTBL_G16_HE_SHIFT                                       30
-+// DW7
-+#define WF_LWTBL_BA_WIN_SIZE0_DW                                    7
-+#define WF_LWTBL_BA_WIN_SIZE0_ADDR                                  28
-+#define WF_LWTBL_BA_WIN_SIZE0_MASK \
-+	0x0000000f // 3- 0
-+#define WF_LWTBL_BA_WIN_SIZE0_SHIFT                                 0
-+#define WF_LWTBL_BA_WIN_SIZE1_DW                                    7
-+#define WF_LWTBL_BA_WIN_SIZE1_ADDR                                  28
-+#define WF_LWTBL_BA_WIN_SIZE1_MASK \
-+	0x000000f0 // 7- 4
-+#define WF_LWTBL_BA_WIN_SIZE1_SHIFT                                 4
-+#define WF_LWTBL_BA_WIN_SIZE2_DW                                    7
-+#define WF_LWTBL_BA_WIN_SIZE2_ADDR                                  28
-+#define WF_LWTBL_BA_WIN_SIZE2_MASK \
-+	0x00000f00 // 11- 8
-+#define WF_LWTBL_BA_WIN_SIZE2_SHIFT                                 8
-+#define WF_LWTBL_BA_WIN_SIZE3_DW                                    7
-+#define WF_LWTBL_BA_WIN_SIZE3_ADDR                                  28
-+#define WF_LWTBL_BA_WIN_SIZE3_MASK \
-+	0x0000f000 // 15-12
-+#define WF_LWTBL_BA_WIN_SIZE3_SHIFT                                 12
-+#define WF_LWTBL_BA_WIN_SIZE4_DW                                    7
-+#define WF_LWTBL_BA_WIN_SIZE4_ADDR                                  28
-+#define WF_LWTBL_BA_WIN_SIZE4_MASK \
-+	0x000f0000 // 19-16
-+#define WF_LWTBL_BA_WIN_SIZE4_SHIFT                                 16
-+#define WF_LWTBL_BA_WIN_SIZE5_DW                                    7
-+#define WF_LWTBL_BA_WIN_SIZE5_ADDR                                  28
-+#define WF_LWTBL_BA_WIN_SIZE5_MASK \
-+	0x00f00000 // 23-20
-+#define WF_LWTBL_BA_WIN_SIZE5_SHIFT                                 20
-+#define WF_LWTBL_BA_WIN_SIZE6_DW                                    7
-+#define WF_LWTBL_BA_WIN_SIZE6_ADDR                                  28
-+#define WF_LWTBL_BA_WIN_SIZE6_MASK \
-+	0x0f000000 // 27-24
-+#define WF_LWTBL_BA_WIN_SIZE6_SHIFT                                 24
-+#define WF_LWTBL_BA_WIN_SIZE7_DW                                    7
-+#define WF_LWTBL_BA_WIN_SIZE7_ADDR                                  28
-+#define WF_LWTBL_BA_WIN_SIZE7_MASK \
-+	0xf0000000 // 31-28
-+#define WF_LWTBL_BA_WIN_SIZE7_SHIFT                                 28
-+// DW8
-+#define WF_LWTBL_AC0_RTS_FAIL_CNT_DW                                8
-+#define WF_LWTBL_AC0_RTS_FAIL_CNT_ADDR                              32
-+#define WF_LWTBL_AC0_RTS_FAIL_CNT_MASK \
-+	0x0000001f // 4- 0
-+#define WF_LWTBL_AC0_RTS_FAIL_CNT_SHIFT                             0
-+#define WF_LWTBL_AC1_RTS_FAIL_CNT_DW                                8
-+#define WF_LWTBL_AC1_RTS_FAIL_CNT_ADDR                              32
-+#define WF_LWTBL_AC1_RTS_FAIL_CNT_MASK \
-+	0x000003e0 // 9- 5
-+#define WF_LWTBL_AC1_RTS_FAIL_CNT_SHIFT                             5
-+#define WF_LWTBL_AC2_RTS_FAIL_CNT_DW                                8
-+#define WF_LWTBL_AC2_RTS_FAIL_CNT_ADDR                              32
-+#define WF_LWTBL_AC2_RTS_FAIL_CNT_MASK \
-+	0x00007c00 // 14-10
-+#define WF_LWTBL_AC2_RTS_FAIL_CNT_SHIFT                             10
-+#define WF_LWTBL_AC3_RTS_FAIL_CNT_DW                                8
-+#define WF_LWTBL_AC3_RTS_FAIL_CNT_ADDR                              32
-+#define WF_LWTBL_AC3_RTS_FAIL_CNT_MASK \
-+	0x000f8000 // 19-15
-+#define WF_LWTBL_AC3_RTS_FAIL_CNT_SHIFT                             15
-+#define WF_LWTBL_PARTIAL_AID_DW                                     8
-+#define WF_LWTBL_PARTIAL_AID_ADDR                                   32
-+#define WF_LWTBL_PARTIAL_AID_MASK \
-+	0x1ff00000 // 28-20
-+#define WF_LWTBL_PARTIAL_AID_SHIFT                                  20
-+#define WF_LWTBL_CHK_PER_DW                                         8
-+#define WF_LWTBL_CHK_PER_ADDR                                       32
-+#define WF_LWTBL_CHK_PER_MASK \
-+	0x80000000 // 31-31
-+#define WF_LWTBL_CHK_PER_SHIFT                                      31
-+// DW9
-+#define WF_LWTBL_RX_AVG_MPDU_SIZE_DW                                9
-+#define WF_LWTBL_RX_AVG_MPDU_SIZE_ADDR                              36
-+#define WF_LWTBL_RX_AVG_MPDU_SIZE_MASK \
-+	0x00003fff // 13- 0
-+#define WF_LWTBL_RX_AVG_MPDU_SIZE_SHIFT                             0
-+#define WF_LWTBL_PRITX_SW_MODE_DW                                   9
-+#define WF_LWTBL_PRITX_SW_MODE_ADDR                                 36
-+#define WF_LWTBL_PRITX_SW_MODE_MASK \
-+	0x00008000 // 15-15
-+#define WF_LWTBL_PRITX_SW_MODE_SHIFT                                15
-+#define WF_LWTBL_PRITX_SW_MODE_MASK_7992 \
-+	0x00004000 // 14-14
-+#define WF_LWTBL_PRITX_SW_MODE_SHIFT_7992                           14
-+#define WF_LWTBL_PRITX_ERSU_DW                                      9
-+#define WF_LWTBL_PRITX_ERSU_ADDR                                    36
-+#define WF_LWTBL_PRITX_ERSU_MASK \
-+	0x00010000 // 16-16
-+#define WF_LWTBL_PRITX_ERSU_SHIFT                                   16
-+#define WF_LWTBL_PRITX_ERSU_MASK_7992 \
-+	0x00008000 // 15-15
-+#define WF_LWTBL_PRITX_ERSU_SHIFT_7992                              15
-+#define WF_LWTBL_PRITX_PLR_DW                                       9
-+#define WF_LWTBL_PRITX_PLR_ADDR                                     36
-+#define WF_LWTBL_PRITX_PLR_MASK \
-+	0x00020000 // 17-17
-+#define WF_LWTBL_PRITX_PLR_SHIFT                                    17
-+#define WF_LWTBL_PRITX_PLR_MASK_7992 \
-+	0x00030000 // 17-16
-+#define WF_LWTBL_PRITX_PLR_SHIFT_7992                               16
-+#define WF_LWTBL_PRITX_DCM_DW                                       9
-+#define WF_LWTBL_PRITX_DCM_ADDR                                     36
-+#define WF_LWTBL_PRITX_DCM_MASK \
-+	0x00040000 // 18-18
-+#define WF_LWTBL_PRITX_DCM_SHIFT                                    18
-+#define WF_LWTBL_PRITX_ER106T_DW                                    9
-+#define WF_LWTBL_PRITX_ER106T_ADDR                                  36
-+#define WF_LWTBL_PRITX_ER106T_MASK \
-+	0x00080000 // 19-19
-+#define WF_LWTBL_PRITX_ER106T_SHIFT                                 19
-+#define WF_LWTBL_FCAP_DW                                            9
-+#define WF_LWTBL_FCAP_ADDR                                          36
-+#define WF_LWTBL_FCAP_MASK \
-+	0x00700000 // 22-20
-+#define WF_LWTBL_FCAP_SHIFT                                         20
-+#define WF_LWTBL_MPDU_FAIL_CNT_DW                                   9
-+#define WF_LWTBL_MPDU_FAIL_CNT_ADDR                                 36
-+#define WF_LWTBL_MPDU_FAIL_CNT_MASK \
-+	0x03800000 // 25-23
-+#define WF_LWTBL_MPDU_FAIL_CNT_SHIFT                                23
-+#define WF_LWTBL_MPDU_OK_CNT_DW                                     9
-+#define WF_LWTBL_MPDU_OK_CNT_ADDR                                   36
-+#define WF_LWTBL_MPDU_OK_CNT_MASK \
-+	0x1c000000 // 28-26
-+#define WF_LWTBL_MPDU_OK_CNT_SHIFT                                  26
-+#define WF_LWTBL_RATE_IDX_DW                                        9
-+#define WF_LWTBL_RATE_IDX_ADDR                                      36
-+#define WF_LWTBL_RATE_IDX_MASK \
-+	0xe0000000 // 31-29
-+#define WF_LWTBL_RATE_IDX_SHIFT                                     29
-+// DW10
-+#define WF_LWTBL_RATE1_DW                                           10
-+#define WF_LWTBL_RATE1_ADDR                                         40
-+#define WF_LWTBL_RATE1_MASK \
-+	0x00007fff // 14- 0
-+#define WF_LWTBL_RATE1_SHIFT                                        0
-+#define WF_LWTBL_RATE2_DW                                           10
-+#define WF_LWTBL_RATE2_ADDR                                         40
-+#define WF_LWTBL_RATE2_MASK \
-+	0x7fff0000 // 30-16
-+#define WF_LWTBL_RATE2_SHIFT                                        16
-+// DW11
-+#define WF_LWTBL_RATE3_DW                                           11
-+#define WF_LWTBL_RATE3_ADDR                                         44
-+#define WF_LWTBL_RATE3_MASK \
-+	0x00007fff // 14- 0
-+#define WF_LWTBL_RATE3_SHIFT                                        0
-+#define WF_LWTBL_RATE4_DW                                           11
-+#define WF_LWTBL_RATE4_ADDR                                         44
-+#define WF_LWTBL_RATE4_MASK \
-+	0x7fff0000 // 30-16
-+#define WF_LWTBL_RATE4_SHIFT                                        16
-+// DW12
-+#define WF_LWTBL_RATE5_DW                                           12
-+#define WF_LWTBL_RATE5_ADDR                                         48
-+#define WF_LWTBL_RATE5_MASK \
-+	0x00007fff // 14- 0
-+#define WF_LWTBL_RATE5_SHIFT                                        0
-+#define WF_LWTBL_RATE6_DW                                           12
-+#define WF_LWTBL_RATE6_ADDR                                         48
-+#define WF_LWTBL_RATE6_MASK \
-+	0x7fff0000 // 30-16
-+#define WF_LWTBL_RATE6_SHIFT                                        16
-+// DW13
-+#define WF_LWTBL_RATE7_DW                                           13
-+#define WF_LWTBL_RATE7_ADDR                                         52
-+#define WF_LWTBL_RATE7_MASK \
-+	0x00007fff // 14- 0
-+#define WF_LWTBL_RATE7_SHIFT                                        0
-+#define WF_LWTBL_RATE8_DW                                           13
-+#define WF_LWTBL_RATE8_ADDR                                         52
-+#define WF_LWTBL_RATE8_MASK \
-+	0x7fff0000 // 30-16
-+#define WF_LWTBL_RATE8_SHIFT                                        16
-+// DW14
-+#define WF_LWTBL_RATE1_TX_CNT_DW                                    14
-+#define WF_LWTBL_RATE1_TX_CNT_ADDR                                  56
-+#define WF_LWTBL_RATE1_TX_CNT_MASK \
-+	0x0000ffff // 15- 0
-+#define WF_LWTBL_RATE1_TX_CNT_SHIFT                                 0
-+#define WF_LWTBL_CIPHER_SUIT_IGTK_DW                                14
-+#define WF_LWTBL_CIPHER_SUIT_IGTK_ADDR                              56
-+#define WF_LWTBL_CIPHER_SUIT_IGTK_MASK \
-+	0x00003000 // 13-12
-+#define WF_LWTBL_CIPHER_SUIT_IGTK_SHIFT                             12
-+#define WF_LWTBL_CIPHER_SUIT_BIGTK_DW                               14
-+#define WF_LWTBL_CIPHER_SUIT_BIGTK_ADDR                             56
-+#define WF_LWTBL_CIPHER_SUIT_BIGTK_MASK \
-+	0x0000c000 // 15-14
-+#define WF_LWTBL_CIPHER_SUIT_BIGTK_SHIFT                            14
-+#define WF_LWTBL_RATE1_FAIL_CNT_DW                                  14
-+#define WF_LWTBL_RATE1_FAIL_CNT_ADDR                                56
-+#define WF_LWTBL_RATE1_FAIL_CNT_MASK \
-+	0xffff0000 // 31-16
-+#define WF_LWTBL_RATE1_FAIL_CNT_SHIFT                               16
-+// DW15
-+#define WF_LWTBL_RATE2_OK_CNT_DW                                    15
-+#define WF_LWTBL_RATE2_OK_CNT_ADDR                                  60
-+#define WF_LWTBL_RATE2_OK_CNT_MASK \
-+	0x0000ffff // 15- 0
-+#define WF_LWTBL_RATE2_OK_CNT_SHIFT                                 0
-+#define WF_LWTBL_RATE3_OK_CNT_DW                                    15
-+#define WF_LWTBL_RATE3_OK_CNT_ADDR                                  60
-+#define WF_LWTBL_RATE3_OK_CNT_MASK \
-+	0xffff0000 // 31-16
-+#define WF_LWTBL_RATE3_OK_CNT_SHIFT                                 16
-+// DW16
-+#define WF_LWTBL_CURRENT_BW_TX_CNT_DW                               16
-+#define WF_LWTBL_CURRENT_BW_TX_CNT_ADDR                             64
-+#define WF_LWTBL_CURRENT_BW_TX_CNT_MASK \
-+	0x0000ffff // 15- 0
-+#define WF_LWTBL_CURRENT_BW_TX_CNT_SHIFT                            0
-+#define WF_LWTBL_CURRENT_BW_FAIL_CNT_DW                             16
-+#define WF_LWTBL_CURRENT_BW_FAIL_CNT_ADDR                           64
-+#define WF_LWTBL_CURRENT_BW_FAIL_CNT_MASK \
-+	0xffff0000 // 31-16
-+#define WF_LWTBL_CURRENT_BW_FAIL_CNT_SHIFT                          16
-+// DW17
-+#define WF_LWTBL_OTHER_BW_TX_CNT_DW                                 17
-+#define WF_LWTBL_OTHER_BW_TX_CNT_ADDR                               68
-+#define WF_LWTBL_OTHER_BW_TX_CNT_MASK \
-+	0x0000ffff // 15- 0
-+#define WF_LWTBL_OTHER_BW_TX_CNT_SHIFT                              0
-+#define WF_LWTBL_OTHER_BW_FAIL_CNT_DW                               17
-+#define WF_LWTBL_OTHER_BW_FAIL_CNT_ADDR                             68
-+#define WF_LWTBL_OTHER_BW_FAIL_CNT_MASK \
-+	0xffff0000 // 31-16
-+#define WF_LWTBL_OTHER_BW_FAIL_CNT_SHIFT                            16
-+// DW18
-+#define WF_LWTBL_RTS_OK_CNT_DW                                      18
-+#define WF_LWTBL_RTS_OK_CNT_ADDR                                    72
-+#define WF_LWTBL_RTS_OK_CNT_MASK \
-+	0x0000ffff // 15- 0
-+#define WF_LWTBL_RTS_OK_CNT_SHIFT                                   0
-+#define WF_LWTBL_RTS_FAIL_CNT_DW                                    18
-+#define WF_LWTBL_RTS_FAIL_CNT_ADDR                                  72
-+#define WF_LWTBL_RTS_FAIL_CNT_MASK \
-+	0xffff0000 // 31-16
-+#define WF_LWTBL_RTS_FAIL_CNT_SHIFT                                 16
-+// DW19
-+#define WF_LWTBL_DATA_RETRY_CNT_DW                                  19
-+#define WF_LWTBL_DATA_RETRY_CNT_ADDR                                76
-+#define WF_LWTBL_DATA_RETRY_CNT_MASK \
-+	0x0000ffff // 15- 0
-+#define WF_LWTBL_DATA_RETRY_CNT_SHIFT                               0
-+#define WF_LWTBL_MGNT_RETRY_CNT_DW                                  19
-+#define WF_LWTBL_MGNT_RETRY_CNT_ADDR                                76
-+#define WF_LWTBL_MGNT_RETRY_CNT_MASK \
-+	0xffff0000 // 31-16
-+#define WF_LWTBL_MGNT_RETRY_CNT_SHIFT                               16
-+// DW20
-+#define WF_LWTBL_AC0_CTT_CDT_CRB_DW                                 20
-+#define WF_LWTBL_AC0_CTT_CDT_CRB_ADDR                               80
-+#define WF_LWTBL_AC0_CTT_CDT_CRB_MASK \
-+	0xffffffff // 31- 0
-+#define WF_LWTBL_AC0_CTT_CDT_CRB_SHIFT                              0
-+// DW21
-+// DO NOT process repeat field(adm[0])
-+// DW22
-+#define WF_LWTBL_AC1_CTT_CDT_CRB_DW                                 22
-+#define WF_LWTBL_AC1_CTT_CDT_CRB_ADDR                               88
-+#define WF_LWTBL_AC1_CTT_CDT_CRB_MASK \
-+	0xffffffff // 31- 0
-+#define WF_LWTBL_AC1_CTT_CDT_CRB_SHIFT                              0
-+// DW23
-+// DO NOT process repeat field(adm[1])
-+// DW24
-+#define WF_LWTBL_AC2_CTT_CDT_CRB_DW                                 24
-+#define WF_LWTBL_AC2_CTT_CDT_CRB_ADDR                               96
-+#define WF_LWTBL_AC2_CTT_CDT_CRB_MASK \
-+	0xffffffff // 31- 0
-+#define WF_LWTBL_AC2_CTT_CDT_CRB_SHIFT                              0
-+// DW25
-+// DO NOT process repeat field(adm[2])
-+// DW26
-+#define WF_LWTBL_AC3_CTT_CDT_CRB_DW                                 26
-+#define WF_LWTBL_AC3_CTT_CDT_CRB_ADDR                               104
-+#define WF_LWTBL_AC3_CTT_CDT_CRB_MASK \
-+	0xffffffff // 31- 0
-+#define WF_LWTBL_AC3_CTT_CDT_CRB_SHIFT                              0
-+// DW27
-+// DO NOT process repeat field(adm[3])
-+// DW28
-+#define WF_LWTBL_RELATED_IDX0_DW                                    28
-+#define WF_LWTBL_RELATED_IDX0_ADDR                                  112
-+#define WF_LWTBL_RELATED_IDX0_MASK \
-+	0x00000fff // 11- 0
-+#define WF_LWTBL_RELATED_IDX0_SHIFT                                 0
-+#define WF_LWTBL_RELATED_BAND0_DW                                   28
-+#define WF_LWTBL_RELATED_BAND0_ADDR                                 112
-+#define WF_LWTBL_RELATED_BAND0_MASK \
-+	0x00003000 // 13-12
-+#define WF_LWTBL_RELATED_BAND0_SHIFT                                12
-+#define WF_LWTBL_PRIMARY_MLD_BAND_DW                                28
-+#define WF_LWTBL_PRIMARY_MLD_BAND_ADDR                              112
-+#define WF_LWTBL_PRIMARY_MLD_BAND_MASK \
-+	0x0000c000 // 15-14
-+#define WF_LWTBL_PRIMARY_MLD_BAND_SHIFT                             14
-+#define WF_LWTBL_RELATED_IDX1_DW                                    28
-+#define WF_LWTBL_RELATED_IDX1_ADDR                                  112
-+#define WF_LWTBL_RELATED_IDX1_MASK \
-+	0x0fff0000 // 27-16
-+#define WF_LWTBL_RELATED_IDX1_SHIFT                                 16
-+#define WF_LWTBL_RELATED_BAND1_DW                                   28
-+#define WF_LWTBL_RELATED_BAND1_ADDR                                 112
-+#define WF_LWTBL_RELATED_BAND1_MASK \
-+	0x30000000 // 29-28
-+#define WF_LWTBL_RELATED_BAND1_SHIFT                                28
-+#define WF_LWTBL_SECONDARY_MLD_BAND_DW                              28
-+#define WF_LWTBL_SECONDARY_MLD_BAND_ADDR                            112
-+#define WF_LWTBL_SECONDARY_MLD_BAND_MASK \
-+	0xc0000000 // 31-30
-+#define WF_LWTBL_SECONDARY_MLD_BAND_SHIFT                           30
-+// DW29
-+#define WF_LWTBL_DISPATCH_POLICY0_DW                                29
-+#define WF_LWTBL_DISPATCH_POLICY0_ADDR                              116
-+#define WF_LWTBL_DISPATCH_POLICY0_MASK \
-+	0x00000003 // 1- 0
-+#define WF_LWTBL_DISPATCH_POLICY0_SHIFT                             0
-+#define WF_LWTBL_DISPATCH_POLICY1_DW                                29
-+#define WF_LWTBL_DISPATCH_POLICY1_ADDR                              116
-+#define WF_LWTBL_DISPATCH_POLICY1_MASK \
-+	0x0000000c // 3- 2
-+#define WF_LWTBL_DISPATCH_POLICY1_SHIFT                             2
-+#define WF_LWTBL_DISPATCH_POLICY2_DW                                29
-+#define WF_LWTBL_DISPATCH_POLICY2_ADDR                              116
-+#define WF_LWTBL_DISPATCH_POLICY2_MASK \
-+	0x00000030 // 5- 4
-+#define WF_LWTBL_DISPATCH_POLICY2_SHIFT                             4
-+#define WF_LWTBL_DISPATCH_POLICY3_DW                                29
-+#define WF_LWTBL_DISPATCH_POLICY3_ADDR                              116
-+#define WF_LWTBL_DISPATCH_POLICY3_MASK \
-+	0x000000c0 // 7- 6
-+#define WF_LWTBL_DISPATCH_POLICY3_SHIFT                             6
-+#define WF_LWTBL_DISPATCH_POLICY4_DW                                29
-+#define WF_LWTBL_DISPATCH_POLICY4_ADDR                              116
-+#define WF_LWTBL_DISPATCH_POLICY4_MASK \
-+	0x00000300 // 9- 8
-+#define WF_LWTBL_DISPATCH_POLICY4_SHIFT                             8
-+#define WF_LWTBL_DISPATCH_POLICY5_DW                                29
-+#define WF_LWTBL_DISPATCH_POLICY5_ADDR                              116
-+#define WF_LWTBL_DISPATCH_POLICY5_MASK \
-+	0x00000c00 // 11-10
-+#define WF_LWTBL_DISPATCH_POLICY5_SHIFT                             10
-+#define WF_LWTBL_DISPATCH_POLICY6_DW                                29
-+#define WF_LWTBL_DISPATCH_POLICY6_ADDR                              116
-+#define WF_LWTBL_DISPATCH_POLICY6_MASK \
-+	0x00003000 // 13-12
-+#define WF_LWTBL_DISPATCH_POLICY6_SHIFT                             12
-+#define WF_LWTBL_DISPATCH_POLICY7_DW                                29
-+#define WF_LWTBL_DISPATCH_POLICY7_ADDR                              116
-+#define WF_LWTBL_DISPATCH_POLICY7_MASK \
-+	0x0000c000 // 15-14
-+#define WF_LWTBL_DISPATCH_POLICY7_SHIFT                             14
-+#define WF_LWTBL_OWN_MLD_ID_DW                                      29
-+#define WF_LWTBL_OWN_MLD_ID_ADDR                                    116
-+#define WF_LWTBL_OWN_MLD_ID_MASK \
-+	0x003f0000 // 21-16
-+#define WF_LWTBL_OWN_MLD_ID_SHIFT                                   16
-+#define WF_LWTBL_EMLSR0_DW                                          29
-+#define WF_LWTBL_EMLSR0_ADDR                                        116
-+#define WF_LWTBL_EMLSR0_MASK \
-+	0x00400000 // 22-22
-+#define WF_LWTBL_EMLSR0_SHIFT                                       22
-+#define WF_LWTBL_EMLMR0_DW                                          29
-+#define WF_LWTBL_EMLMR0_ADDR                                        116
-+#define WF_LWTBL_EMLMR0_MASK \
-+	0x00800000 // 23-23
-+#define WF_LWTBL_EMLMR0_SHIFT                                       23
-+#define WF_LWTBL_EMLSR1_DW                                          29
-+#define WF_LWTBL_EMLSR1_ADDR                                        116
-+#define WF_LWTBL_EMLSR1_MASK \
-+	0x01000000 // 24-24
-+#define WF_LWTBL_EMLSR1_SHIFT                                       24
-+#define WF_LWTBL_EMLMR1_DW                                          29
-+#define WF_LWTBL_EMLMR1_ADDR                                        116
-+#define WF_LWTBL_EMLMR1_MASK \
-+	0x02000000 // 25-25
-+#define WF_LWTBL_EMLMR1_SHIFT                                       25
-+#define WF_LWTBL_EMLSR2_DW                                          29
-+#define WF_LWTBL_EMLSR2_ADDR                                        116
-+#define WF_LWTBL_EMLSR2_MASK \
-+	0x04000000 // 26-26
-+#define WF_LWTBL_EMLSR2_SHIFT                                       26
-+#define WF_LWTBL_EMLMR2_DW                                          29
-+#define WF_LWTBL_EMLMR2_ADDR                                        116
-+#define WF_LWTBL_EMLMR2_MASK \
-+	0x08000000 // 27-27
-+#define WF_LWTBL_EMLMR2_SHIFT                                       27
-+#define WF_LWTBL_STR_BITMAP_DW                                      29
-+#define WF_LWTBL_STR_BITMAP_ADDR                                    116
-+#define WF_LWTBL_STR_BITMAP_MASK \
-+	0xe0000000 // 31-29
-+#define WF_LWTBL_STR_BITMAP_SHIFT                                   29
-+// DW30
-+#define WF_LWTBL_DISPATCH_ORDER_DW                                  30
-+#define WF_LWTBL_DISPATCH_ORDER_ADDR                                120
-+#define WF_LWTBL_DISPATCH_ORDER_MASK \
-+	0x0000007f // 6- 0
-+#define WF_LWTBL_DISPATCH_ORDER_SHIFT                               0
-+#define WF_LWTBL_DISPATCH_RATIO_DW                                  30
-+#define WF_LWTBL_DISPATCH_RATIO_ADDR                                120
-+#define WF_LWTBL_DISPATCH_RATIO_MASK \
-+	0x00003f80 // 13- 7
-+#define WF_LWTBL_DISPATCH_RATIO_SHIFT                               7
-+#define WF_LWTBL_LINK_MGF_DW                                        30
-+#define WF_LWTBL_LINK_MGF_ADDR                                      120
-+#define WF_LWTBL_LINK_MGF_MASK \
-+	0xffff0000 // 31-16
-+#define WF_LWTBL_LINK_MGF_SHIFT                                     16
-+// DW31
-+#define WF_LWTBL_BFTX_TB_DW                                         31
-+#define WF_LWTBL_BFTX_TB_ADDR                                       124
-+#define WF_LWTBL_BFTX_TB_MASK \
-+	0x00800000 // 23-23
-+#define WF_LWTBL_DROP_DW                                            31
-+#define WF_LWTBL_DROP_ADDR                                          124
-+#define WF_LWTBL_DROP_MASK \
-+	0x01000000 // 24-24
-+#define WF_LWTBL_DROP_SHIFT                                         24
-+#define WF_LWTBL_CASCAD_DW                                          31
-+#define WF_LWTBL_CASCAD_ADDR                                        124
-+#define WF_LWTBL_CASCAD_MASK \
-+	0x02000000 // 25-25
-+#define WF_LWTBL_CASCAD_SHIFT                                       25
-+#define WF_LWTBL_ALL_ACK_DW                                         31
-+#define WF_LWTBL_ALL_ACK_ADDR                                       124
-+#define WF_LWTBL_ALL_ACK_MASK \
-+	0x04000000 // 26-26
-+#define WF_LWTBL_ALL_ACK_SHIFT                                      26
-+#define WF_LWTBL_MPDU_SIZE_DW                                       31
-+#define WF_LWTBL_MPDU_SIZE_ADDR                                     124
-+#define WF_LWTBL_MPDU_SIZE_MASK \
-+	0x18000000 // 28-27
-+#define WF_LWTBL_MPDU_SIZE_SHIFT                                    27
-+#define WF_LWTBL_RXD_DUP_MODE_DW                                    31
-+#define WF_LWTBL_RXD_DUP_MODE_ADDR                                  124
-+#define WF_LWTBL_RXD_DUP_MODE_MASK \
-+	0x60000000 // 30-29
-+#define WF_LWTBL_RXD_DUP_MODE_SHIFT                                 29
-+#define WF_LWTBL_ACK_EN_DW                                          31
-+#define WF_LWTBL_ACK_EN_ADDR                                        128
-+#define WF_LWTBL_ACK_EN_MASK \
-+	0x80000000 // 31-31
-+#define WF_LWTBL_ACK_EN_SHIFT                                       31
-+// DW32
-+#define WF_LWTBL_OM_INFO_DW                                         32
-+#define WF_LWTBL_OM_INFO_ADDR                                       128
-+#define WF_LWTBL_OM_INFO_MASK \
-+	0x00000fff // 11- 0
-+#define WF_LWTBL_OM_INFO_SHIFT                                      0
-+#define WF_LWTBL_OM_INFO_EHT_DW                                     32
-+#define WF_LWTBL_OM_INFO_EHT_ADDR                                   128
-+#define WF_LWTBL_OM_INFO_EHT_MASK \
-+	0x0000f000 // 15-12
-+#define WF_LWTBL_OM_INFO_EHT_SHIFT                                  12
-+#define WF_LWTBL_RXD_DUP_FOR_OM_CHG_DW                              32
-+#define WF_LWTBL_RXD_DUP_FOR_OM_CHG_ADDR                            128
-+#define WF_LWTBL_RXD_DUP_FOR_OM_CHG_MASK \
-+	0x00010000 // 16-16
-+#define WF_LWTBL_RXD_DUP_FOR_OM_CHG_SHIFT                           16
-+#define WF_LWTBL_RXD_DUP_WHITE_LIST_DW                              32
-+#define WF_LWTBL_RXD_DUP_WHITE_LIST_ADDR                            128
-+#define WF_LWTBL_RXD_DUP_WHITE_LIST_MASK \
-+	0x1ffe0000 // 28-17
-+#define WF_LWTBL_RXD_DUP_WHITE_LIST_SHIFT                           17
-+// DW33
-+#define WF_LWTBL_USER_RSSI_DW                                       33
-+#define WF_LWTBL_USER_RSSI_ADDR                                     132
-+#define WF_LWTBL_USER_RSSI_MASK \
-+	0x000001ff // 8- 0
-+#define WF_LWTBL_USER_RSSI_SHIFT                                    0
-+#define WF_LWTBL_USER_SNR_DW                                        33
-+#define WF_LWTBL_USER_SNR_ADDR                                      132
-+#define WF_LWTBL_USER_SNR_MASK \
-+	0x00007e00 // 14- 9
-+#define WF_LWTBL_USER_SNR_SHIFT                                     9
-+#define WF_LWTBL_RAPID_REACTION_RATE_DW                             33
-+#define WF_LWTBL_RAPID_REACTION_RATE_ADDR                           132
-+#define WF_LWTBL_RAPID_REACTION_RATE_MASK \
-+	0x0fff0000 // 27-16
-+#define WF_LWTBL_RAPID_REACTION_RATE_SHIFT                          16
-+#define WF_LWTBL_HT_AMSDU_DW                                        33
-+#define WF_LWTBL_HT_AMSDU_ADDR                                      132
-+#define WF_LWTBL_HT_AMSDU_MASK \
-+	0x40000000 // 30-30
-+#define WF_LWTBL_HT_AMSDU_SHIFT                                     30
-+#define WF_LWTBL_AMSDU_CROSS_LG_DW                                  33
-+#define WF_LWTBL_AMSDU_CROSS_LG_ADDR                                132
-+#define WF_LWTBL_AMSDU_CROSS_LG_MASK \
-+	0x80000000 // 31-31
-+#define WF_LWTBL_AMSDU_CROSS_LG_SHIFT                               31
-+// DW34
-+#define WF_LWTBL_RESP_RCPI0_DW                                      34
-+#define WF_LWTBL_RESP_RCPI0_ADDR                                    136
-+#define WF_LWTBL_RESP_RCPI0_MASK \
-+	0x000000ff // 7- 0
-+#define WF_LWTBL_RESP_RCPI0_SHIFT                                   0
-+#define WF_LWTBL_RESP_RCPI1_DW                                      34
-+#define WF_LWTBL_RESP_RCPI1_ADDR                                    136
-+#define WF_LWTBL_RESP_RCPI1_MASK \
-+	0x0000ff00 // 15- 8
-+#define WF_LWTBL_RESP_RCPI1_SHIFT                                   8
-+#define WF_LWTBL_RESP_RCPI2_DW                                      34
-+#define WF_LWTBL_RESP_RCPI2_ADDR                                    136
-+#define WF_LWTBL_RESP_RCPI2_MASK \
-+	0x00ff0000 // 23-16
-+#define WF_LWTBL_RESP_RCPI2_SHIFT                                   16
-+#define WF_LWTBL_RESP_RCPI3_DW                                      34
-+#define WF_LWTBL_RESP_RCPI3_ADDR                                    136
-+#define WF_LWTBL_RESP_RCPI3_MASK \
-+	0xff000000 // 31-24
-+#define WF_LWTBL_RESP_RCPI3_SHIFT                                   24
-+// DW35
-+#define WF_LWTBL_SNR_RX0_DW                                         35
-+#define WF_LWTBL_SNR_RX0_ADDR                                       140
-+#define WF_LWTBL_SNR_RX0_MASK \
-+	0x0000003f // 5- 0
-+#define WF_LWTBL_SNR_RX0_SHIFT                                      0
-+#define WF_LWTBL_SNR_RX1_DW                                         35
-+#define WF_LWTBL_SNR_RX1_ADDR                                       140
-+#define WF_LWTBL_SNR_RX1_MASK \
-+	0x00000fc0 // 11- 6
-+#define WF_LWTBL_SNR_RX1_SHIFT                                      6
-+#define WF_LWTBL_SNR_RX2_DW                                         35
-+#define WF_LWTBL_SNR_RX2_ADDR                                       140
-+#define WF_LWTBL_SNR_RX2_MASK \
-+	0x0003f000 // 17-12
-+#define WF_LWTBL_SNR_RX2_SHIFT                                      12
-+#define WF_LWTBL_SNR_RX3_DW                                         35
-+#define WF_LWTBL_SNR_RX3_ADDR                                       140
-+#define WF_LWTBL_SNR_RX3_MASK \
-+	0x00fc0000 // 23-18
-+#define WF_LWTBL_SNR_RX3_SHIFT                                      18
-+
-+/* WTBL Group - Packet Number */
-+/* DW 2 */
-+#define WTBL_PN0_MASK                   BITS(0, 7)
-+#define WTBL_PN0_OFFSET                 0
-+#define WTBL_PN1_MASK                   BITS(8, 15)
-+#define WTBL_PN1_OFFSET                 8
-+#define WTBL_PN2_MASK                   BITS(16, 23)
-+#define WTBL_PN2_OFFSET                 16
-+#define WTBL_PN3_MASK                   BITS(24, 31)
-+#define WTBL_PN3_OFFSET                 24
-+
-+/* DW 3 */
-+#define WTBL_PN4_MASK                   BITS(0, 7)
-+#define WTBL_PN4_OFFSET                 0
-+#define WTBL_PN5_MASK                   BITS(8, 15)
-+#define WTBL_PN5_OFFSET                 8
-+
-+/* DW 4 */
-+#define WTBL_BIPN0_MASK                 BITS(0, 7)
-+#define WTBL_BIPN0_OFFSET               0
-+#define WTBL_BIPN1_MASK                 BITS(8, 15)
-+#define WTBL_BIPN1_OFFSET               8
-+#define WTBL_BIPN2_MASK                 BITS(16, 23)
-+#define WTBL_BIPN2_OFFSET               16
-+#define WTBL_BIPN3_MASK                 BITS(24, 31)
-+#define WTBL_BIPN3_OFFSET               24
-+
-+/* DW 5 */
-+#define WTBL_BIPN4_MASK                 BITS(0, 7)
-+#define WTBL_BIPN4_OFFSET               0
-+#define WTBL_BIPN5_MASK                 BITS(8, 15)
-+#define WTBL_BIPN5_OFFSET               8
-+
-+/* UWTBL DW 6 */
-+#define WTBL_AMSDU_LEN_MASK             BITS(0, 5)
-+#define WTBL_AMSDU_LEN_OFFSET           0
-+#define WTBL_AMSDU_NUM_MASK             BITS(6, 10)
-+#define WTBL_AMSDU_NUM_OFFSET           6
-+#define WTBL_AMSDU_EN_MASK              BIT(11)
-+#define WTBL_AMSDU_EN_OFFSET            11
-+
-+/* UWTBL DW 8 */
-+#define WTBL_SEC_ADDR_MODE_MASK		BITS(20, 21)
-+#define WTBL_SEC_ADDR_MODE_OFFSET	20
-+
-+/* LWTBL Rate field */
-+#define WTBL_RATE_TX_RATE_MASK          BITS(0, 5)
-+#define WTBL_RATE_TX_RATE_OFFSET        0
-+#define WTBL_RATE_TX_MODE_MASK          BITS(6, 9)
-+#define WTBL_RATE_TX_MODE_OFFSET        6
-+#define WTBL_RATE_NSTS_MASK             BITS(10, 13)
-+#define WTBL_RATE_NSTS_OFFSET           10
-+#define WTBL_RATE_STBC_MASK             BIT(14)
-+#define WTBL_RATE_STBC_OFFSET           14
-+
-+/***** WTBL(LMAC) DW Offset *****/
-+/* LMAC WTBL Group - Peer Unique Information */
-+#define WTBL_GROUP_PEER_INFO_DW_0               0
-+#define WTBL_GROUP_PEER_INFO_DW_1               1
-+
-+/* WTBL Group - TxRx Capability/Information */
-+#define WTBL_GROUP_TRX_CAP_DW_2                 2
-+#define WTBL_GROUP_TRX_CAP_DW_3                 3
-+#define WTBL_GROUP_TRX_CAP_DW_4                 4
-+#define WTBL_GROUP_TRX_CAP_DW_5                 5
-+#define WTBL_GROUP_TRX_CAP_DW_6                 6
-+#define WTBL_GROUP_TRX_CAP_DW_7                 7
-+#define WTBL_GROUP_TRX_CAP_DW_8                 8
-+#define WTBL_GROUP_TRX_CAP_DW_9                 9
-+
-+/* WTBL Group - Auto Rate Table*/
-+#define WTBL_GROUP_AUTO_RATE_1_2                10
-+#define WTBL_GROUP_AUTO_RATE_3_4                11
-+#define WTBL_GROUP_AUTO_RATE_5_6                12
-+#define WTBL_GROUP_AUTO_RATE_7_8                13
-+
-+/* WTBL Group - Tx Counter */
-+#define WTBL_GROUP_TX_CNT_LINE_1                14
-+#define WTBL_GROUP_TX_CNT_LINE_2                15
-+#define WTBL_GROUP_TX_CNT_LINE_3                16
-+#define WTBL_GROUP_TX_CNT_LINE_4                17
-+#define WTBL_GROUP_TX_CNT_LINE_5                18
-+#define WTBL_GROUP_TX_CNT_LINE_6                19
-+
-+/* WTBL Group - Admission Control Counter */
-+#define WTBL_GROUP_ADM_CNT_LINE_1               20
-+#define WTBL_GROUP_ADM_CNT_LINE_2               21
-+#define WTBL_GROUP_ADM_CNT_LINE_3               22
-+#define WTBL_GROUP_ADM_CNT_LINE_4               23
-+#define WTBL_GROUP_ADM_CNT_LINE_5               24
-+#define WTBL_GROUP_ADM_CNT_LINE_6               25
-+#define WTBL_GROUP_ADM_CNT_LINE_7               26
-+#define WTBL_GROUP_ADM_CNT_LINE_8               27
-+
-+/* WTBL Group -MLO Info */
-+#define WTBL_GROUP_MLO_INFO_LINE_1              28
-+#define WTBL_GROUP_MLO_INFO_LINE_2              29
-+#define WTBL_GROUP_MLO_INFO_LINE_3              30
-+
-+/* WTBL Group -RESP Info */
-+#define WTBL_GROUP_RESP_INFO_DW_31              31
-+
-+/* WTBL Group -RX DUP Info */
-+#define WTBL_GROUP_RX_DUP_INFO_DW_32            32
-+
-+/* WTBL Group - Rx Statistics Counter */
-+#define WTBL_GROUP_RX_STAT_CNT_LINE_1           33
-+#define WTBL_GROUP_RX_STAT_CNT_LINE_2           34
-+#define WTBL_GROUP_RX_STAT_CNT_LINE_3           35
-+
-+/* UWTBL Group - HW AMSDU */
-+#define UWTBL_HW_AMSDU_DW                       WF_UWTBL_AMSDU_CFG_DW
-+
-+/* LWTBL DW 4 */
-+#define WTBL_DIS_RHTR                           WF_LWTBL_DIS_RHTR_MASK
-+
-+/* UWTBL DW 5 */
-+#define WTBL_KEY_LINK_DW_KEY_LOC0_MASK          BITS(0, 10)
-+#define WTBL_PSM				WF_LWTBL_PSM_MASK
-+
-+/* Need to sync with FW define */
-+#define INVALID_KEY_ENTRY                       WTBL_KEY_LINK_DW_KEY_LOC0_MASK
-+
-+// RATE
-+#define WTBL_RATE_TX_RATE_MASK          	BITS(0, 5)
-+#define WTBL_RATE_TX_RATE_OFFSET        	0
-+#define WTBL_RATE_TX_MODE_MASK          	BITS(6, 9)
-+#define WTBL_RATE_TX_MODE_OFFSET        	6
-+#define WTBL_RATE_NSTS_MASK             	BITS(10, 13)
-+#define WTBL_RATE_NSTS_OFFSET           	10
-+#define WTBL_RATE_STBC_MASK            	 	BIT(14)
-+#define WTBL_RATE_STBC_OFFSET          	 	14
-+#endif
-+
-+#endif
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-new file mode 100644
-index 0000000..d79e00c
---- /dev/null
-+++ b/mt7996/mtk_debugfs.c
-@@ -0,0 +1,2507 @@
-+// SPDX-License-Identifier: ISC
-+/*
-+ * Copyright (C) 2023 MediaTek Inc.
-+ */
-+#include "mt7996.h"
-+#include "../mt76.h"
-+#include "mcu.h"
-+#include "mac.h"
-+#include "eeprom.h"
-+#include "mtk_debug.h"
-+#include "mtk_mcu.h"
-+#include "coredump.h"
-+
-+#ifdef CONFIG_MTK_DEBUG
-+
-+/* AGG INFO */
-+static int
-+mt7996_agginfo_read_per_band(struct seq_file *s, int band_idx)
-+{
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	u64 total_burst, total_ampdu, ampdu_cnt[16];
-+	u32 value, idx, row_idx, col_idx, start_range, agg_rang_sel[16], burst_cnt[16], band_offset = 0;
-+	u8 partial_str[16] = {}, full_str[64] = {};
-+
-+	switch (band_idx) {
-+	case 0:
-+		band_offset = 0;
-+		break;
-+	case 1:
-+		band_offset = BN1_WF_AGG_TOP_BASE - BN0_WF_AGG_TOP_BASE;
-+		break;
-+	case 2:
-+		band_offset = IP1_BN0_WF_AGG_TOP_BASE - BN0_WF_AGG_TOP_BASE;
-+		break;
-+	default:
-+		return 0;
-+	}
-+
-+	seq_printf(s, "Band %d AGG Status\n", band_idx);
-+	seq_printf(s, "===============================\n");
-+	value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR0_ADDR + band_offset);
-+	seq_printf(s, "AC00 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_SHFT);
-+	seq_printf(s, "AC01 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_SHFT);
-+	value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR1_ADDR + band_offset);
-+	seq_printf(s, "AC02 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_SHFT);
-+	seq_printf(s, "AC03 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_SHFT);
-+	value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR2_ADDR + band_offset);
-+	seq_printf(s, "AC10 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_SHFT);
-+	seq_printf(s, "AC11 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_SHFT);
-+	value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR3_ADDR + band_offset);
-+	seq_printf(s, "AC12 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_SHFT);
-+	seq_printf(s, "AC13 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_SHFT);
-+	value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR4_ADDR + band_offset);
-+	seq_printf(s, "AC20 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_SHFT);
-+	seq_printf(s, "AC21 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_SHFT);
-+	value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR5_ADDR + band_offset);
-+	seq_printf(s, "AC22 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_SHFT);
-+	seq_printf(s, "AC23 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_SHFT);
-+	value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR6_ADDR + band_offset);
-+	seq_printf(s, "AC30 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_SHFT);
-+	seq_printf(s, "AC31 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_SHFT);
-+	value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR7_ADDR + band_offset);
-+	seq_printf(s, "AC32 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_SHFT);
-+	seq_printf(s, "AC33 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_SHFT);
-+
-+	switch (band_idx) {
-+	case 0:
-+		band_offset = 0;
-+		break;
-+	case 1:
-+		band_offset = BN1_WF_MIB_TOP_BASE - BN0_WF_MIB_TOP_BASE;
-+		break;
-+	case 2:
-+		band_offset = IP1_BN0_WF_MIB_TOP_BASE - BN0_WF_MIB_TOP_BASE;
-+		break;
-+	default:
-+		return 0;
-+	}
-+
-+	seq_printf(s, "===AMPDU Related Counters===\n");
-+
-+	value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC0_ADDR + band_offset);
-+	agg_rang_sel[0] = (value & BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_MASK) >> BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_SHFT;
-+	agg_rang_sel[1] = (value & BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_MASK) >> BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_SHFT;
-+	value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC1_ADDR + band_offset);
-+	agg_rang_sel[2] = (value & BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_MASK) >> BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_SHFT;
-+	agg_rang_sel[3] = (value & BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_MASK) >> BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_SHFT;
-+	value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC2_ADDR + band_offset);
-+	agg_rang_sel[4] = (value & BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_MASK) >> BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_SHFT;
-+	agg_rang_sel[5] = (value & BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_MASK) >> BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_SHFT;
-+	value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC3_ADDR + band_offset);
-+	agg_rang_sel[6] = (value & BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_MASK) >> BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_SHFT;
-+	agg_rang_sel[7] = (value & BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_MASK) >> BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_SHFT;
-+	value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC4_ADDR + band_offset);
-+	agg_rang_sel[8] = (value & BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_MASK) >> BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_SHFT;
-+	agg_rang_sel[9] = (value & BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_MASK) >> BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_SHFT;
-+	value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC5_ADDR + band_offset);
-+	agg_rang_sel[10] = (value & BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_MASK) >> BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_SHFT;
-+	agg_rang_sel[11] = (value & BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_MASK) >> BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_SHFT;
-+	value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC6_ADDR + band_offset);
-+	agg_rang_sel[12] = (value & BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_MASK) >> BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_SHFT;
-+	agg_rang_sel[13] = (value & BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_MASK) >> BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_SHFT;
-+	value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC7_ADDR + band_offset);
-+	agg_rang_sel[14] = (value & BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_MASK) >> BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_SHFT;
-+
-+	burst_cnt[0] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR0_ADDR + band_offset);
-+	burst_cnt[1] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR1_ADDR + band_offset);
-+	burst_cnt[2] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR2_ADDR + band_offset);
-+	burst_cnt[3] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR3_ADDR + band_offset);
-+	burst_cnt[4] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR4_ADDR + band_offset);
-+	burst_cnt[5] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR5_ADDR + band_offset);
-+	burst_cnt[6] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR6_ADDR + band_offset);
-+	burst_cnt[7] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR7_ADDR + band_offset);
-+	burst_cnt[8] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR8_ADDR + band_offset);
-+	burst_cnt[9] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR9_ADDR + band_offset);
-+	burst_cnt[10] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR10_ADDR + band_offset);
-+	burst_cnt[11] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR11_ADDR + band_offset);
-+	burst_cnt[12] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR12_ADDR + band_offset);
-+	burst_cnt[13] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR13_ADDR + band_offset);
-+	burst_cnt[14] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR14_ADDR + band_offset);
-+	burst_cnt[15] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR15_ADDR + band_offset);
-+
-+	start_range = 1;
-+	total_burst = 0;
-+	total_ampdu = 0;
-+	agg_rang_sel[15] = 1023;
-+
-+	/* Need to add 1 after read from AGG_RANG_SEL CR */
-+	for (idx = 0; idx < 16; idx++) {
-+		agg_rang_sel[idx]++;
-+		total_burst += burst_cnt[idx];
-+
-+		if (start_range == agg_rang_sel[idx])
-+			ampdu_cnt[idx] = (u64) start_range * burst_cnt[idx];
-+		else
-+			ampdu_cnt[idx] = (u64) ((start_range + agg_rang_sel[idx]) >> 1) * burst_cnt[idx];
-+
-+		start_range = agg_rang_sel[idx] + 1;
-+		total_ampdu += ampdu_cnt[idx];
-+	}
-+
-+	start_range = 1;
-+	sprintf(full_str, "%13s ", "Tx Agg Range:");
-+
-+	for (row_idx = 0; row_idx < 4; row_idx++) {
-+		for (col_idx = 0; col_idx < 4; col_idx++, idx++) {
-+			idx = 4 * row_idx + col_idx;
-+
-+			if (start_range == agg_rang_sel[idx])
-+				sprintf(partial_str, "%d", agg_rang_sel[idx]);
-+			else
-+				sprintf(partial_str, "%d~%d", start_range, agg_rang_sel[idx]);
-+
-+			start_range = agg_rang_sel[idx] + 1;
-+			sprintf(full_str + strlen(full_str), "%-11s ", partial_str);
-+		}
-+
-+		idx = 4 * row_idx;
-+
-+		seq_printf(s, "%s\n", full_str);
-+		seq_printf(s, "%13s 0x%-9x 0x%-9x 0x%-9x 0x%-9x\n",
-+			row_idx ? "" : "Burst count:",
-+			burst_cnt[idx], burst_cnt[idx + 1],
-+			burst_cnt[idx + 2], burst_cnt[idx + 3]);
-+
-+		if (total_burst != 0) {
-+			if (row_idx == 0)
-+				sprintf(full_str, "%13s ",
-+					"Burst ratio:");
-+			else
-+				sprintf(full_str, "%13s ", "");
-+
-+			for (col_idx = 0; col_idx < 4; col_idx++) {
-+				u64 count = (u64) burst_cnt[idx + col_idx] * 100;
-+
-+				sprintf(partial_str, "(%llu%%)",
-+					div64_u64(count, total_burst));
-+				sprintf(full_str + strlen(full_str),
-+					"%-11s ", partial_str);
-+			}
-+
-+			seq_printf(s, "%s\n", full_str);
-+
-+			if (row_idx == 0)
-+				sprintf(full_str, "%13s ",
-+					"MDPU ratio:");
-+			else
-+				sprintf(full_str, "%13s ", "");
-+
-+			for (col_idx = 0; col_idx < 4; col_idx++) {
-+				u64 count = ampdu_cnt[idx + col_idx] * 100;
-+
-+				sprintf(partial_str, "(%llu%%)",
-+					div64_u64(count, total_ampdu));
-+				sprintf(full_str + strlen(full_str),
-+					"%-11s ", partial_str);
-+			}
-+
-+			seq_printf(s, "%s\n", full_str);
-+		}
-+
-+		sprintf(full_str, "%13s ", "");
-+	}
-+
-+	return 0;
-+}
-+
-+static int mt7996_agginfo_read_band0(struct seq_file *s, void *data)
-+{
-+	mt7996_agginfo_read_per_band(s, MT_BAND0);
-+	return 0;
-+}
-+
-+static int mt7996_agginfo_read_band1(struct seq_file *s, void *data)
-+{
-+	mt7996_agginfo_read_per_band(s, MT_BAND1);
-+	return 0;
-+}
-+
-+static int mt7996_agginfo_read_band2(struct seq_file *s, void *data)
-+{
-+	mt7996_agginfo_read_per_band(s, MT_BAND2);
-+	return 0;
-+}
-+
-+/* AMSDU INFO */
-+static int mt7996_amsdu_result_read(struct seq_file *s, void *data)
-+{
-+#define HW_MSDU_CNT_ADDR 0xf400
-+#define HW_MSDU_NUM_MAX 33
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	u32 ple_stat[HW_MSDU_NUM_MAX] = {0}, total_amsdu = 0;
-+	u8 i;
-+
-+	for (i = 0; i < HW_MSDU_NUM_MAX; i++)
-+		ple_stat[i] = mt76_rr(dev, HW_MSDU_CNT_ADDR + i * 0x04);
-+
-+	seq_printf(s, "TXD counter status of MSDU:\n");
-+
-+	for (i = 0; i < HW_MSDU_NUM_MAX; i++)
-+		total_amsdu += ple_stat[i];
-+
-+	for (i = 0; i < HW_MSDU_NUM_MAX; i++) {
-+		seq_printf(s, "AMSDU pack count of %d MSDU in TXD: 0x%x ", i, ple_stat[i]);
-+		if (total_amsdu != 0)
-+			seq_printf(s, "(%d%%)\n", ple_stat[i] * 100 / total_amsdu);
-+		else
-+			seq_printf(s, "\n");
-+	}
-+
-+	return 0;
-+}
-+
-+/* DBG MODLE */
-+static int
-+mt7996_fw_debug_module_set(void *data, u64 module)
-+{
-+	struct mt7996_dev *dev = data;
-+
-+	dev->dbg.fw_dbg_module = module;
-+	return 0;
-+}
-+
-+static int
-+mt7996_fw_debug_module_get(void *data, u64 *module)
-+{
-+	struct mt7996_dev *dev = data;
-+
-+	*module = dev->dbg.fw_dbg_module;
-+	return 0;
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_module, mt7996_fw_debug_module_get,
-+			 mt7996_fw_debug_module_set, "%lld\n");
-+
-+static int
-+mt7996_fw_debug_level_set(void *data, u64 level)
-+{
-+	struct mt7996_dev *dev = data;
-+
-+	dev->dbg.fw_dbg_lv = level;
-+	mt7996_mcu_fw_dbg_ctrl(dev, dev->dbg.fw_dbg_module, dev->dbg.fw_dbg_lv);
-+	return 0;
-+}
-+
-+static int
-+mt7996_fw_debug_level_get(void *data, u64 *level)
-+{
-+	struct mt7996_dev *dev = data;
-+
-+	*level = dev->dbg.fw_dbg_lv;
-+	return 0;
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_level, mt7996_fw_debug_level_get,
-+			 mt7996_fw_debug_level_set, "%lld\n");
-+
-+/* usage: echo 0x[arg3][arg2][arg1] > fw_wa_set */
-+static int
-+mt7996_wa_set(void *data, u64 val)
-+{
-+	struct mt7996_dev *dev = data;
-+	u32 arg1, arg2, arg3;
-+
-+	arg1 = FIELD_GET(GENMASK_ULL(7, 0), val);
-+	arg2 = FIELD_GET(GENMASK_ULL(15, 8), val);
-+	arg3 = FIELD_GET(GENMASK_ULL(23, 16), val);
-+
-+	return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
-+				arg1, arg2, arg3);
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_wa_set, NULL, mt7996_wa_set,
-+			 "0x%llx\n");
-+
-+/* usage: echo 0x[arg3][arg2][arg1] > fw_wa_query */
-+static int
-+mt7996_wa_query(void *data, u64 val)
-+{
-+	struct mt7996_dev *dev = data;
-+	u32 arg1, arg2, arg3;
-+
-+	arg1 = FIELD_GET(GENMASK_ULL(7, 0), val);
-+	arg2 = FIELD_GET(GENMASK_ULL(15, 8), val);
-+	arg3 = FIELD_GET(GENMASK_ULL(23, 16), val);
-+
-+	return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(QUERY),
-+				arg1, arg2, arg3);
-+	return 0;
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_wa_query, NULL, mt7996_wa_query,
-+			 "0x%llx\n");
-+
-+static int mt7996_dump_version(struct seq_file *s, void *data)
-+{
-+#define MAX_ADIE_NUM	3
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	u32 regval;
-+	u16 adie_chip_id, adie_chip_ver;
-+	int adie_idx;
-+	static const char * const fem_type[] = {
-+		[MT7996_FEM_UNSET] = "N/A",
-+		[MT7996_FEM_EXT] = "eFEM",
-+		[MT7996_FEM_INT] = "iFEM",
-+		[MT7996_FEM_MIX] = "mixed FEM",
-+	};
-+
-+	seq_printf(s, "Version: 4.3.24.5\n");
-+
-+	if (!test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state))
-+		return 0;
-+
-+	seq_printf(s, "Rom Patch Build Time: %.16s\n", dev->patch_build_date);
-+	seq_printf(s, "WM Patch Build Time: %.15s, Mode: %s\n",
-+		   dev->ram_build_date[MT7996_RAM_TYPE_WM],
-+		   dev->testmode_enable ? "Testmode" : "Normal mode");
-+	seq_printf(s, "WA Patch Build Time: %.15s\n",
-+		   dev->ram_build_date[MT7996_RAM_TYPE_WA]);
-+	seq_printf(s, "DSP Patch Build Time: %.15s\n",
-+		   dev->ram_build_date[MT7996_RAM_TYPE_DSP]);
-+	for (adie_idx = 0; adie_idx < MAX_ADIE_NUM; adie_idx++) {
-+		mt7996_mcu_rf_regval(dev, MT_ADIE_CHIP_ID(adie_idx), &regval, false);
-+		adie_chip_id = FIELD_GET(MT_ADIE_CHIP_ID_MASK, regval);
-+		adie_chip_ver = FIELD_GET(MT_ADIE_VERSION_MASK, regval);
-+		if (adie_chip_id)
-+			seq_printf(s, "Adie %d: ID = 0x%04x, Ver = 0x%04x\n",
-+				   adie_idx, adie_chip_id, adie_chip_ver);
-+		else
-+			seq_printf(s, "Adie %d: ID = N/A, Ver = N/A\n", adie_idx);
-+	}
-+	seq_printf(s, "FEM type: %s\n", fem_type[dev->fem_type]);
-+
-+	return 0;
-+}
-+
-+/* fw wm call trace info dump */
-+void mt7996_show_lp_history(struct seq_file *s, u32 type)
-+{
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	struct mt7996_crash_data *crash_data;
-+	struct mt7996_coredump *dump;
-+	u64 now = 0;
-+	int i = 0;
-+	u8 fw_type = !!type;
-+
-+	mutex_lock(&dev->dump_mutex);
-+
-+	crash_data = mt7996_coredump_new(dev, fw_type);
-+	if (!crash_data) {
-+		mutex_unlock(&dev->dump_mutex);
-+		seq_printf(s, "the coredump is disable!\n");
-+		return;
-+	}
-+	mutex_unlock(&dev->dump_mutex);
-+
-+	dump = mt7996_coredump_build(dev, fw_type, false);
-+	if (!dump) {
-+		seq_printf(s, "no call stack data found!\n");
-+		return;
-+	}
-+
-+	seq_printf(s, "\x1b[32m%s log output\x1b[0m\n", dump->fw_type);
-+	seq_printf(s, "\x1b[32mfw status: %s\n", dump->fw_state);
-+	mt7996_dump_version(s, NULL);
-+	/* PC log */
-+	now = jiffies;
-+	for (i = 0; i < 10; i++)
-+		seq_printf(s, "\tCurrent PC=%x\n", dump->pc_cur[i]);
-+
-+	seq_printf(s, "PC log contorl=0x%x(T=%llu)(latest PC index = 0x%x)\n",
-+		dump->pc_dbg_ctrl, now, dump->pc_cur_idx);
-+	for (i = 0; i < 32; i++)
-+		seq_printf(s, "\tPC log(%d)=0x%08x\n", i, dump->pc_stack[i]);
-+
-+	/* LR log */
-+	now = jiffies;
-+	seq_printf(s, "\nLR log contorl=0x%x(T=%llu)(latest LR index = 0x%x)\n",
-+		dump->lr_dbg_ctrl, now, dump->lr_cur_idx);
-+	for (i = 0; i < 32; i++)
-+		seq_printf(s, "\tLR log(%d)=0x%08x\n", i, dump->lr_stack[i]);
-+
-+	vfree(dump);
-+}
-+
-+static int mt7996_fw_wa_info_read(struct seq_file *s, void *data)
-+{
-+	seq_printf(s, "======[ShowPcLpHistory]======\n");
-+	mt7996_show_lp_history(s, MT7996_RAM_TYPE_WA);
-+	seq_printf(s, "======[End ShowPcLpHistory]==\n");
-+
-+	return 0;
-+}
-+
-+static int mt7996_fw_wm_info_read(struct seq_file *s, void *data)
-+{
-+	seq_printf(s, "======[ShowPcLpHistory]======\n");
-+	mt7996_show_lp_history(s, MT7996_RAM_TYPE_WM);
-+	seq_printf(s, "======[End ShowPcLpHistory]==\n");
-+
-+	return 0;
-+}
-+
-+/* dma info dump */
-+static void
-+dump_dma_tx_ring_info(struct seq_file *s, struct mt7996_dev *dev,  char *str1, char *str2, u32 ring_base)
-+{
-+	u32 base, cnt, cidx, didx, queue_cnt;
-+
-+	base= mt76_rr(dev, ring_base);
-+	cnt = mt76_rr(dev, ring_base + 4);
-+	cidx = mt76_rr(dev, ring_base + 8);
-+	didx = mt76_rr(dev, ring_base + 12);
-+	queue_cnt = (cidx >= didx) ? (cidx - didx) : (cidx - didx + cnt);
-+
-+	seq_printf(s, "%20s %6s %10x %15x %10x %10x %10x\n", str1, str2, base, cnt, cidx, didx, queue_cnt);
-+}
-+
-+static void
-+dump_dma_rx_ring_info(struct seq_file *s, struct mt7996_dev *dev,  char *str1, char *str2, u32 ring_base)
-+{
-+	u32 base, ctrl1, cnt, cidx, didx, queue_cnt;
-+
-+	base= mt76_rr(dev, ring_base);
-+	ctrl1 = mt76_rr(dev, ring_base + 4);
-+	cidx = mt76_rr(dev, ring_base + 8) & 0xfff;
-+	didx = mt76_rr(dev, ring_base + 12) & 0xfff;
-+	cnt = ctrl1 & 0xfff;
-+	queue_cnt = (didx > cidx) ? (didx - cidx - 1) : (didx - cidx + cnt - 1);
-+
-+	seq_printf(s, "%20s %6s %10x %10x(%3x) %10x %10x %10x\n",
-+		   str1, str2, base, ctrl1, cnt, cidx, didx, queue_cnt);
-+}
-+
-+static void
-+mt7996_show_dma_info(struct seq_file *s, struct mt7996_dev *dev)
-+{
-+	u32 sys_ctrl[10];
-+
-+	/* HOST DMA0 information */
-+	sys_ctrl[0] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_HOST_INT_STA_ADDR);
-+	sys_ctrl[1] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_HOST_INT_ENA_ADDR);
-+	sys_ctrl[2] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR);
-+
-+	seq_printf(s, "HOST_DMA Configuration\n");
-+	seq_printf(s, "%10s %10s %10s %10s %10s %10s\n",
-+		"DMA", "IntCSR", "IntMask", "Glocfg", "Tx/RxEn", "Tx/RxBusy");
-+	seq_printf(s, "%10s %10x %10x %10x %4x/%5x %4x/%5x\n",
-+		"DMA0", sys_ctrl[0], sys_ctrl[1], sys_ctrl[2],
-+		(sys_ctrl[2] & WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_MASK)
-+			>> WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_SHFT,
-+		(sys_ctrl[2] & WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_MASK)
-+			>> WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_SHFT,
-+		(sys_ctrl[2] & WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK)
-+			>> WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT,
-+		(sys_ctrl[2] & WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK)
-+			>> WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT);
-+
-+	if (dev->hif2) {
-+		/* HOST DMA1 information */
-+		sys_ctrl[0] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_PCIE1_HOST_INT_STA_ADDR);
-+		sys_ctrl[1] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_PCIE1_HOST_INT_ENA_ADDR);
-+		sys_ctrl[2] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_ADDR);
-+
-+		seq_printf(s, "%10s %10x %10x %10x %4x/%5x %4x/%5x\n",
-+			"DMA0P1", sys_ctrl[0], sys_ctrl[1], sys_ctrl[2],
-+			(sys_ctrl[2] & WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_EN_MASK)
-+				>> WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_EN_SHFT,
-+			(sys_ctrl[2] & WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_EN_MASK)
-+				>> WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_EN_SHFT,
-+			(sys_ctrl[2] & WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK)
-+				>> WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT,
-+			(sys_ctrl[2] & WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK)
-+				>> WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT);
-+	}
-+
-+	seq_printf(s, "HOST_DMA0 Ring Configuration\n");
-+	seq_printf(s, "%20s %6s %10s %15s %10s %10s %10s\n",
-+		"Name", "Used", "Base", "Ctrl1(Cnt)", "CIDX", "DIDX", "QCnt");
-+	dump_dma_tx_ring_info(s, dev, "T0:TXD0(H2MAC)", "STA",
-+		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T1:TXD1(H2MAC)", "STA",
-+		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T2:TXD2(H2MAC)", "STA",
-+		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T3:", "STA",
-+		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T4:", "STA",
-+		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T5:", "STA",
-+		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T6:", "STA",
-+		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T16:FWDL", "Both",
-+		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T17:Cmd(H2WM)", "Both",
-+		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T18:TXD0(H2WA)", "AP",
-+		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T19:TXD1(H2WA)", "AP",
-+		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T20:Cmd(H2WA)", "AP",
-+		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T21:TXD2(H2WA)", "AP",
-+		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T22:TXD3(H2WA)", "AP",
-+		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL0_ADDR);
-+
-+
-+	dump_dma_rx_ring_info(s, dev, "R0:Event(WM2H)", "Both",
-+		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R1:Event(WA2H)", "AP",
-+		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R2:TxDone0(WA2H)", "AP",
-+		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R3:TxDone1(WA2H)", "AP",
-+		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R4:Data0(MAC2H)", "Both",
-+		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R5:Data1(MAC2H)", "Both",
-+		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
-+		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R7:TxDone1(MAC2H)", "Both",
-+		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R8:BUF0(MAC2H)", "Both",
-+		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R9:TxDone0(MAC2H)", "Both",
-+		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R10:MSDU_PG0(MAC2H)", "Both",
-+		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R11:MSDU_PG1(MAC2H)", "Both",
-+		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R12:MSDU_PG2(MAC2H)", "Both",
-+		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "IND:IND_CMD(MAC2H)", "Both",
-+		WF_RRO_TOP_IND_CMD_0_CTRL0_ADDR);
-+
-+	if (dev->hif2) {
-+		seq_printf(s, "HOST_DMA0 PCIe1 Ring Configuration\n");
-+		seq_printf(s, "%20s %6s %10s %15s %10s %10s %10s\n",
-+			"Name", "Used", "Base", "Ctrl1(Cnt)", "CIDX", "DIDX", "QCnt");
-+		dump_dma_tx_ring_info(s, dev, "T21:TXD2(H2WA)", "AP",
-+			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL0_ADDR);
-+		dump_dma_tx_ring_info(s, dev, "T22:TXD?(H2WA)", "AP",
-+			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL0_ADDR);
-+
-+		dump_dma_rx_ring_info(s, dev, "R3:TxDone1(WA2H)", "AP",
-+			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL0_ADDR);
-+		dump_dma_rx_ring_info(s, dev, "R5:Data1(MAC2H)", "Both",
-+			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL0_ADDR);
-+		dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
-+			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL0_ADDR);
-+		dump_dma_rx_ring_info(s, dev, "R7:TxDone1(MAC2H)", "Both",
-+			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL0_ADDR);
-+	}
-+
-+	/* MCU DMA information */
-+	sys_ctrl[0] = mt76_rr(dev, WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR);
-+	sys_ctrl[1] = mt76_rr(dev, WF_WFDMA_MCU_DMA0_HOST_INT_STA_ADDR);
-+	sys_ctrl[2] = mt76_rr(dev, WF_WFDMA_MCU_DMA0_HOST_INT_ENA_ADDR);
-+
-+	seq_printf(s, "MCU_DMA Configuration\n");
-+	seq_printf(s, "%10s %10s %10s %10s %10s %10s\n",
-+		"DMA", "IntCSR", "IntMask", "Glocfg", "Tx/RxEn", "Tx/RxBusy");
-+	seq_printf(s, "%10s %10x %10x %10x %4x/%5x %4x/%5x\n",
-+		"DMA0", sys_ctrl[1], sys_ctrl[2], sys_ctrl[0],
-+		(sys_ctrl[0] & WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_MASK)
-+			>> WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_SHFT,
-+		(sys_ctrl[0] & WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_MASK)
-+			>> WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_SHFT,
-+		(sys_ctrl[0] & WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK)
-+			>> WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT,
-+		(sys_ctrl[0] & WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK)
-+			>> WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT);
-+
-+	seq_printf(s, "MCU_DMA0 Ring Configuration\n");
-+	seq_printf(s, "%20s %6s %10s %15s %10s %10s %10s\n",
-+		"Name", "Used", "Base", "Cnt", "CIDX", "DIDX", "QCnt");
-+	dump_dma_tx_ring_info(s, dev, "T0:Event(WM2H)", "Both",
-+		WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T1:Event(WA2H)", "AP",
-+		WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T2:TxDone0(WA2H)", "AP",
-+		WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T3:TxDone1(WA2H)", "AP",
-+		WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T4:TXD(WM2MAC)", "Both",
-+		WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T5:TXCMD(WM2MAC)", "Both",
-+		WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T6:TXD(WA2MAC)", "AP",
-+		WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R0:FWDL", "Both",
-+		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R1:Cmd(H2WM)", "Both",
-+		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R2:TXD0(H2WA)", "AP",
-+		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R3:TXD1(H2WA)", "AP",
-+		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R4:Cmd(H2WA)", "AP",
-+		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R5:Data0(MAC2WM)", "Both",
-+		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R6:TxDone(MAC2WM)", "Both",
-+		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R7:SPL/RPT(MAC2WM)", "Both",
-+		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R8:TxDone(MAC2WA)", "AP",
-+		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R9:Data1(MAC2WM)", "Both",
-+		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R10:TXD2(H2WA)", "AP",
-+		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL0_ADDR);
-+
-+	/* MEM DMA information */
-+	sys_ctrl[0] = mt76_rr(dev, WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR);
-+	sys_ctrl[1] = mt76_rr(dev, WF_WFDMA_MEM_DMA_HOST_INT_STA_ADDR);
-+	sys_ctrl[2] = mt76_rr(dev, WF_WFDMA_MEM_DMA_HOST_INT_ENA_ADDR);
-+
-+	seq_printf(s, "MEM_DMA Configuration\n");
-+	seq_printf(s, "%10s %10s %10s %10s %10s %10s\n",
-+		"DMA", "IntCSR", "IntMask", "Glocfg", "Tx/RxEn", "Tx/RxBusy");
-+	seq_printf(s, "%10s %10x %10x %10x %4x/%5x %4x/%5x\n",
-+		"MEM", sys_ctrl[1], sys_ctrl[2], sys_ctrl[0],
-+		(sys_ctrl[0] & WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_MASK)
-+			>> WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_SHFT,
-+		(sys_ctrl[0] & WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_MASK)
-+			>> WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_SHFT,
-+		(sys_ctrl[0] & WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK)
-+			>> WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT,
-+		(sys_ctrl[0] & WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK)
-+			>> WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT);
-+
-+	seq_printf(s, "MEM_DMA Ring Configuration\n");
-+	seq_printf(s, "%20s %6s %10s %10s %10s %10s %10s\n",
-+		"Name", "Used", "Base", "Cnt", "CIDX", "DIDX", "QCnt");
-+	dump_dma_tx_ring_info(s, dev, "T0:CmdEvent(WM2WA)", "AP",
-+		WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL0_ADDR);
-+	dump_dma_tx_ring_info(s, dev, "T1:CmdEvent(WA2WM)", "AP",
-+		WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R0:CmdEvent(WM2WA)", "AP",
-+		WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL0_ADDR);
-+	dump_dma_rx_ring_info(s, dev, "R1:CmdEvent(WA2WM)", "AP",
-+		WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL0_ADDR);
-+}
-+
-+static int mt7996_trinfo_read(struct seq_file *s, void *data)
-+{
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	mt7996_show_dma_info(s, dev);
-+	return 0;
-+}
-+
-+/* MIB INFO */
-+static int mt7996_mibinfo_read_per_band(struct seq_file *s, int band_idx)
-+{
-+#define BSS_NUM	4
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	u8 bss_nums = BSS_NUM;
-+	u32 idx;
-+	u32 mac_val, band_offset = 0, band_offset_umib = 0;
-+	u32 msdr6, msdr9, msdr18;
-+	u32 rvsr0, rscr26, rscr35, mctr5, mctr6, msr0, msr1, msr2;
-+	u32 tbcr0, tbcr1, tbcr2, tbcr3, tbcr4;
-+	u32 btscr[7];
-+	u32 tdrcr[5];
-+	u32 mbtocr[16], mbtbcr[16], mbrocr[16], mbrbcr[16];
-+	u32 btcr, btbcr, brocr, brbcr, btdcr, brdcr;
-+	u32 mu_cnt[5];
-+	u32 ampdu_cnt[3];
-+	u64 per;
-+
-+	switch (band_idx) {
-+	case 0:
-+		band_offset = 0;
-+		band_offset_umib = 0;
-+		break;
-+	case 1:
-+		band_offset = BN1_WF_MIB_TOP_BASE - BN0_WF_MIB_TOP_BASE;
-+		band_offset_umib = WF_UMIB_TOP_B1BROCR_ADDR - WF_UMIB_TOP_B0BROCR_ADDR;
-+		break;
-+	case 2:
-+		band_offset = IP1_BN0_WF_MIB_TOP_BASE - BN0_WF_MIB_TOP_BASE;
-+		band_offset_umib = WF_UMIB_TOP_B2BROCR_ADDR - WF_UMIB_TOP_B0BROCR_ADDR;
-+		break;
-+	default:
-+		return true;
-+	}
-+
-+	seq_printf(s, "Band %d MIB Status\n", band_idx);
-+	seq_printf(s, "===============================\n");
-+	mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_M0SCR0_ADDR + band_offset);
-+	seq_printf(s, "MIB Status Control=0x%x\n", mac_val);
-+
-+	msdr6 = mt76_rr(dev, BN0_WF_MIB_TOP_M0SDR6_ADDR + band_offset);
-+	rvsr0 = mt76_rr(dev, BN0_WF_MIB_TOP_RVSR0_ADDR + band_offset);
-+	rscr35 = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR35_ADDR + band_offset);
-+	msdr9 = mt76_rr(dev, BN0_WF_MIB_TOP_M0SDR9_ADDR + band_offset);
-+	rscr26 = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR26_ADDR + band_offset);
-+	mctr5 = mt76_rr(dev, BN0_WF_MIB_TOP_MCTR5_ADDR + band_offset);
-+	mctr6 = mt76_rr(dev, BN0_WF_MIB_TOP_MCTR6_ADDR + band_offset);
-+	msdr18 = mt76_rr(dev, BN0_WF_MIB_TOP_M0SDR18_ADDR + band_offset);
-+	msr0 = mt76_rr(dev, BN0_WF_MIB_TOP_MSR0_ADDR + band_offset);
-+	msr1 = mt76_rr(dev, BN0_WF_MIB_TOP_MSR1_ADDR + band_offset);
-+	msr2 = mt76_rr(dev, BN0_WF_MIB_TOP_MSR2_ADDR + band_offset);
-+	ampdu_cnt[0] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR0_ADDR + band_offset);
-+	ampdu_cnt[1] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR3_ADDR + band_offset);
-+	ampdu_cnt[2] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR4_ADDR + band_offset);
-+	ampdu_cnt[1] &= BN0_WF_MIB_TOP_TSCR3_AMPDU_MPDU_COUNT_MASK;
-+	ampdu_cnt[2] &= BN0_WF_MIB_TOP_TSCR4_AMPDU_ACKED_COUNT_MASK;
-+
-+	seq_printf(s, "===Phy/Timing Related Counters===\n");
-+	seq_printf(s, "\tChannelIdleCnt=0x%x\n",
-+		msdr6 & BN0_WF_MIB_TOP_M0SDR6_CHANNEL_IDLE_COUNT_MASK);
-+	seq_printf(s, "\tCCA_NAV_Tx_Time=0x%x\n",
-+		msdr9 & BN0_WF_MIB_TOP_M0SDR9_CCA_NAV_TX_TIME_MASK);
-+	seq_printf(s, "\tRx_MDRDY_CNT=0x%x\n",
-+		rscr26 & BN0_WF_MIB_TOP_RSCR26_RX_MDRDY_COUNT_MASK);
-+	seq_printf(s, "\tCCK_MDRDY_TIME=0x%x, OFDM_MDRDY_TIME=0x%x",
-+		msr0 & BN0_WF_MIB_TOP_MSR0_CCK_MDRDY_TIME_MASK,
-+		msr1 & BN0_WF_MIB_TOP_MSR1_OFDM_LG_MIXED_VHT_MDRDY_TIME_MASK);
-+	seq_printf(s, ", OFDM_GREEN_MDRDY_TIME=0x%x\n",
-+		msr2 & BN0_WF_MIB_TOP_MSR2_OFDM_GREEN_MDRDY_TIME_MASK);
-+	seq_printf(s, "\tPrim CCA Time=0x%x\n",
-+		mctr5 & BN0_WF_MIB_TOP_MCTR5_P_CCA_TIME_MASK);
-+	seq_printf(s, "\tSec CCA Time=0x%x\n",
-+		mctr6 & BN0_WF_MIB_TOP_MCTR6_S_CCA_TIME_MASK);
-+	seq_printf(s, "\tPrim ED Time=0x%x\n",
-+		msdr18 & BN0_WF_MIB_TOP_M0SDR18_P_ED_TIME_MASK);
-+
-+	seq_printf(s, "===Tx Related Counters(Generic)===\n");
-+	mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR18_ADDR + band_offset);
-+	dev->dbg.bcn_total_cnt[band_idx] +=
-+		(mac_val & BN0_WF_MIB_TOP_TSCR18_BEACONTXCOUNT_MASK);
-+	seq_printf(s, "\tBeaconTxCnt=0x%x\n", dev->dbg.bcn_total_cnt[band_idx]);
-+	dev->dbg.bcn_total_cnt[band_idx] = 0;
-+
-+	tbcr0 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR0_ADDR + band_offset);
-+	seq_printf(s, "\tTx 20MHz Cnt=0x%x\n",
-+		tbcr0 & BN0_WF_MIB_TOP_TBCR0_TX_20MHZ_CNT_MASK);
-+	tbcr1 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR1_ADDR + band_offset);
-+	seq_printf(s, "\tTx 40MHz Cnt=0x%x\n",
-+		tbcr1 & BN0_WF_MIB_TOP_TBCR1_TX_40MHZ_CNT_MASK);
-+	tbcr2 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR2_ADDR + band_offset);
-+	seq_printf(s, "\tTx 80MHz Cnt=0x%x\n",
-+		tbcr2 & BN0_WF_MIB_TOP_TBCR2_TX_80MHZ_CNT_MASK);
-+	tbcr3 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR3_ADDR + band_offset);
-+	seq_printf(s, "\tTx 160MHz Cnt=0x%x\n",
-+		tbcr3 & BN0_WF_MIB_TOP_TBCR3_TX_160MHZ_CNT_MASK);
-+	tbcr4 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR4_ADDR + band_offset);
-+	seq_printf(s, "\tTx 320MHz Cnt=0x%x\n",
-+		tbcr4 & BN0_WF_MIB_TOP_TBCR4_TX_320MHZ_CNT_MASK);
-+	seq_printf(s, "\tAMPDU Cnt=0x%x\n", ampdu_cnt[0]);
-+	seq_printf(s, "\tAMPDU MPDU Cnt=0x%x\n", ampdu_cnt[1]);
-+	seq_printf(s, "\tAMPDU MPDU Ack Cnt=0x%x\n", ampdu_cnt[2]);
-+	per = (ampdu_cnt[2] == 0 ?
-+		0 : 1000 * (ampdu_cnt[1] - ampdu_cnt[2]) / ampdu_cnt[1]);
-+	seq_printf(s, "\tAMPDU MPDU PER=%llu.%1llu%%\n", per / 10, per % 10);
-+
-+	seq_printf(s, "===MU Related Counters===\n");
-+	mu_cnt[0] = mt76_rr(dev, BN0_WF_MIB_TOP_BSCR2_ADDR + band_offset);
-+	mu_cnt[1] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR5_ADDR + band_offset);
-+	mu_cnt[2] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR6_ADDR + band_offset);
-+	mu_cnt[3] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR8_ADDR + band_offset);
-+	mu_cnt[4] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR7_ADDR + band_offset);
-+
-+	seq_printf(s, "\tMUBF_TX_COUNT=0x%x\n",
-+		mu_cnt[0] & BN0_WF_MIB_TOP_BSCR2_MUBF_TX_COUNT_MASK);
-+	seq_printf(s, "\tMU_TX_MPDU_COUNT(Ok+Fail)=0x%x\n", mu_cnt[1]);
-+	seq_printf(s, "\tMU_TX_OK_MPDU_COUNT=0x%x\n", mu_cnt[2]);
-+	seq_printf(s, "\tMU_TO_MU_FAIL_PPDU_COUNT=0x%x\n", mu_cnt[3]);
-+	seq_printf(s, "\tSU_TX_OK_MPDU_COUNT=0x%x\n", mu_cnt[4]);
-+
-+	seq_printf(s, "===Rx Related Counters(Generic)===\n");
-+	seq_printf(s, "\tVector Mismacth Cnt=0x%x\n",
-+		rvsr0 & BN0_WF_MIB_TOP_RVSR0_VEC_MISS_COUNT_MASK);
-+	seq_printf(s, "\tDelimiter Fail Cnt=0x%x\n",
-+		rscr35 & BN0_WF_MIB_TOP_RSCR35_DELIMITER_FAIL_COUNT_MASK);
-+
-+	mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR1_ADDR + band_offset);
-+	seq_printf(s, "\tRxFCSErrCnt=0x%x\n",
-+		(mac_val & BN0_WF_MIB_TOP_RSCR1_RX_FCS_ERROR_COUNT_MASK));
-+	mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR33_ADDR + band_offset);
-+	seq_printf(s, "\tRxFifoFullCnt=0x%x\n",
-+		(mac_val & BN0_WF_MIB_TOP_RSCR33_RX_FIFO_FULL_COUNT_MASK));
-+	mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR36_ADDR + band_offset);
-+	seq_printf(s, "\tRxLenMismatch=0x%x\n",
-+		(mac_val & BN0_WF_MIB_TOP_RSCR36_RX_LEN_MISMATCH_MASK));
-+	mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR31_ADDR + band_offset);
-+	seq_printf(s, "\tRxMPDUCnt=0x%x\n",
-+		(mac_val & BN0_WF_MIB_TOP_RSCR31_RX_MPDU_COUNT_MASK));
-+	mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR27_ADDR + band_offset);
-+	seq_printf(s, "\tRx AMPDU Cnt=0x%x\n", mac_val);
-+	mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR28_ADDR + band_offset);
-+	seq_printf(s, "\tRx Total ByteCnt=0x%x\n", mac_val);
-+
-+
-+	/* Per-BSS T/RX Counters */
-+	seq_printf(s, "===Per-BSS Related Tx/Rx Counters===\n");
-+	seq_printf(s, "BSS Idx TxCnt/DataCnt TxByteCnt RxOkCnt/DataCnt RxByteCnt\n");
-+	for (idx = 0; idx < bss_nums; idx++) {
-+		btcr = mt76_rr(dev, BN0_WF_MIB_TOP_BTCR_ADDR + band_offset + idx * 4);
-+		btdcr = mt76_rr(dev, BN0_WF_MIB_TOP_BTDCR_ADDR + band_offset + idx * 4);
-+		btbcr = mt76_rr(dev, BN0_WF_MIB_TOP_BTBCR_ADDR + band_offset + idx * 4);
-+
-+		brocr = mt76_rr(dev, WF_UMIB_TOP_B0BROCR_ADDR + band_offset_umib + idx * 4);
-+		brdcr = mt76_rr(dev, WF_UMIB_TOP_B0BRDCR_ADDR + band_offset_umib + idx * 4);
-+		brbcr = mt76_rr(dev, WF_UMIB_TOP_B0BRBCR_ADDR + band_offset_umib + idx * 4);
-+
-+		seq_printf(s, "%d\t 0x%x/0x%x\t 0x%x \t 0x%x/0x%x \t 0x%x\n",
-+			idx, btcr, btdcr, btbcr, brocr, brdcr, brbcr);
-+	}
-+
-+	seq_printf(s, "===Per-BSS Related MIB Counters===\n");
-+	seq_printf(s, "BSS Idx RTSTx/RetryCnt BAMissCnt AckFailCnt FrmRetry1/2/3Cnt\n");
-+
-+	/* Per-BSS TX Status */
-+	for (idx = 0; idx < bss_nums; idx++) {
-+		btscr[0] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR5_ADDR + band_offset + idx * 4);
-+		btscr[1] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR6_ADDR + band_offset + idx * 4);
-+		btscr[2] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR0_ADDR + band_offset + idx * 4);
-+		btscr[3] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR1_ADDR + band_offset + idx * 4);
-+		btscr[4] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR2_ADDR + band_offset + idx * 4);
-+		btscr[5] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR3_ADDR + band_offset + idx * 4);
-+		btscr[6] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR4_ADDR + band_offset + idx * 4);
-+
-+		seq_printf(s, "%d:\t0x%x/0x%x  0x%x \t 0x%x \t  0x%x/0x%x/0x%x\n",
-+			idx, (btscr[0] & BN0_WF_MIB_TOP_BTSCR5_RTSTXCOUNTn_MASK),
-+			(btscr[1] & BN0_WF_MIB_TOP_BTSCR6_RTSRETRYCOUNTn_MASK),
-+			(btscr[2] & BN0_WF_MIB_TOP_BTSCR0_BAMISSCOUNTn_MASK),
-+			(btscr[3] & BN0_WF_MIB_TOP_BTSCR1_ACKFAILCOUNTn_MASK),
-+			(btscr[4] & BN0_WF_MIB_TOP_BTSCR2_FRAMERETRYCOUNTn_MASK),
-+			(btscr[5] & BN0_WF_MIB_TOP_BTSCR3_FRAMERETRY2COUNTn_MASK),
-+			(btscr[6] & BN0_WF_MIB_TOP_BTSCR4_FRAMERETRY3COUNTn_MASK));
-+	}
-+
-+	/* Dummy delimiter insertion result */
-+	seq_printf(s, "===Dummy delimiter insertion result===\n");
-+	tdrcr[0] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR0_ADDR + band_offset);
-+	tdrcr[1] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR1_ADDR + band_offset);
-+	tdrcr[2] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR2_ADDR + band_offset);
-+	tdrcr[3] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR3_ADDR + band_offset);
-+	tdrcr[4] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR4_ADDR + band_offset);
-+
-+	seq_printf(s, "Range0 = %d\t Range1 = %d\t Range2 = %d\t Range3 = %d\t Range4 = %d\n",
-+		tdrcr[0],
-+		tdrcr[1],
-+		tdrcr[2],
-+		tdrcr[3],
-+		tdrcr[4]);
-+
-+	/* Per-MBSS T/RX Counters */
-+	seq_printf(s, "===Per-MBSS Related Tx/Rx Counters===\n");
-+	seq_printf(s, "MBSSIdx   TxOkCnt  TxByteCnt  RxOkCnt  RxByteCnt\n");
-+
-+	for (idx = 0; idx < 16; idx++) {
-+		mbtocr[idx] = mt76_rr(dev, BN0_WF_MIB_TOP_BTOCR_ADDR + band_offset + (bss_nums + idx) * 4);
-+		mbtbcr[idx] = mt76_rr(dev, BN0_WF_MIB_TOP_BTBCR_ADDR + band_offset + (bss_nums + idx) * 4);
-+
-+		mbrocr[idx] = mt76_rr(dev, WF_UMIB_TOP_B0BROCR_ADDR + band_offset_umib + (bss_nums + idx) * 4);
-+		mbrbcr[idx] = mt76_rr(dev, WF_UMIB_TOP_B0BRBCR_ADDR + band_offset_umib + (bss_nums + idx) * 4);
-+	}
-+
-+	for (idx = 0; idx < 16; idx++) {
-+		seq_printf(s, "%d\t 0x%x\t 0x%x \t 0x%x \t 0x%x\n",
-+			idx, mbtocr[idx], mbtbcr[idx], mbrocr[idx], mbrbcr[idx]);
-+	}
-+
-+	return 0;
-+}
-+
-+static int mt7996_mibinfo_band0(struct seq_file *s, void *data)
-+{
-+	mt7996_mibinfo_read_per_band(s, MT_BAND0);
-+	return 0;
-+}
-+
-+static int mt7996_mibinfo_band1(struct seq_file *s, void *data)
-+{
-+	mt7996_mibinfo_read_per_band(s, MT_BAND1);
-+	return 0;
-+}
-+
-+static int mt7996_mibinfo_band2(struct seq_file *s, void *data)
-+{
-+	mt7996_mibinfo_read_per_band(s, MT_BAND2);
-+	return 0;
-+}
-+
-+/* WTBL INFO */
-+static int
-+mt7996_wtbl_read_raw(struct mt7996_dev *dev, u16 idx,
-+		     enum mt7996_wtbl_type type, u16 start_dw,
-+		     u16 len, void *buf)
-+{
-+	u32 *dest_cpy = (u32 *)buf;
-+	u32 size_dw = len;
-+	u32 src = 0;
-+
-+	if (!buf)
-+		return 0xFF;
-+
-+	if (type == WTBL_TYPE_LMAC) {
-+		mt76_wr(dev, MT_DBG_WTBLON_TOP_WDUCR_ADDR,
-+			FIELD_PREP(MT_DBG_WTBLON_TOP_WDUCR_GROUP, (idx >> 7)));
-+		src = LWTBL_IDX2BASE(idx, start_dw);
-+	} else if (type == WTBL_TYPE_UMAC) {
-+		mt76_wr(dev,  MT_DBG_UWTBL_TOP_WDUCR_ADDR,
-+			FIELD_PREP(MT_DBG_UWTBL_TOP_WDUCR_GROUP, (idx >> 7)));
-+		src = UWTBL_IDX2BASE(idx, start_dw);
-+	} else if (type == WTBL_TYPE_KEY) {
-+		mt76_wr(dev,  MT_DBG_UWTBL_TOP_WDUCR_ADDR,
-+			MT_DBG_UWTBL_TOP_WDUCR_TARGET |
-+			FIELD_PREP(MT_DBG_UWTBL_TOP_WDUCR_GROUP, (idx >> 7)));
-+		src = KEYTBL_IDX2BASE(idx, start_dw);
-+	}
-+
-+	while (size_dw--) {
-+		*dest_cpy++ = mt76_rr(dev, src);
-+		src += 4;
-+	};
-+
-+	return 0;
-+}
-+
-+#if 0
-+static int
-+mt7996_wtbl_write_raw(struct mt7996_dev *dev, u16 idx,
-+			  enum mt7996_wtbl_type type, u16 start_dw,
-+			  u32 val)
-+{
-+	u32 addr = 0;
-+
-+	if (type == WTBL_TYPE_LMAC) {
-+		mt76_wr(dev, MT_DBG_WTBLON_TOP_WDUCR_ADDR,
-+			FIELD_PREP(MT_DBG_WTBLON_TOP_WDUCR_GROUP, (idx >> 7)));
-+		addr = LWTBL_IDX2BASE(idx, start_dw);
-+	} else if (type == WTBL_TYPE_UMAC) {
-+		mt76_wr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR,
-+			FIELD_PREP(MT_DBG_UWTBL_TOP_WDUCR_GROUP, (idx >> 7)));
-+		addr = UWTBL_IDX2BASE(idx, start_dw);
-+	} else if (type == WTBL_TYPE_KEY) {
-+		mt76_wr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR,
-+			MT_DBG_UWTBL_TOP_WDUCR_TARGET |
-+			FIELD_PREP(MT_DBG_UWTBL_TOP_WDUCR_GROUP, (idx >> 7)));
-+		addr = KEYTBL_IDX2BASE(idx, start_dw);
-+	}
-+
-+	mt76_wr(dev, addr, val);
-+
-+	return 0;
-+}
-+#endif
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW0[] = {
-+	{"MUAR_IDX",    WF_LWTBL_MUAR_MASK, WF_LWTBL_MUAR_SHIFT,false},
-+	{"RCA1",        WF_LWTBL_RCA1_MASK, NO_SHIFT_DEFINE,	false},
-+	{"KID",         WF_LWTBL_KID_MASK,  WF_LWTBL_KID_SHIFT,	false},
-+	{"RCID",        WF_LWTBL_RCID_MASK, NO_SHIFT_DEFINE,	false},
-+	{"BAND",        WF_LWTBL_BAND_MASK, WF_LWTBL_BAND_SHIFT,false},
-+	{"RV",          WF_LWTBL_RV_MASK,   NO_SHIFT_DEFINE,	false},
-+	{"RCA2",        WF_LWTBL_RCA2_MASK, NO_SHIFT_DEFINE,	false},
-+	{"WPI_FLAG",    WF_LWTBL_WPI_FLAG_MASK, NO_SHIFT_DEFINE,true},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw0_1(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LinkAddr: %02x:%02x:%02x:%02x:%02x:%02x(D0[B0~15], D1[B0~31])\n",
-+		lwtbl[4], lwtbl[5], lwtbl[6], lwtbl[7], lwtbl[0], lwtbl[1]);
-+
-+	/* LMAC WTBL DW 0 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 0/1\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_PEER_INFO_DW_0*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_LMAC_DW0[i].name) {
-+
-+		if (WTBL_LMAC_DW0[i].shift == NO_SHIFT_DEFINE)
-+			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW0[i].name,
-+					 (dw_value & WTBL_LMAC_DW0[i].mask) ? 1 : 0);
-+		else
-+			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW0[i].name,
-+					  (dw_value & WTBL_LMAC_DW0[i].mask) >> WTBL_LMAC_DW0[i].shift);
-+		i++;
-+	}
-+}
-+
-+static const struct berse_wtbl_parse *WTBL_LMAC_DW2;
-+static const struct berse_wtbl_parse WTBL_LMAC_DW2_7996[] = {
-+	{"AID",                 WF_LWTBL_AID_MASK,              WF_LWTBL_AID_SHIFT,	false},
-+	{"GID_SU",              WF_LWTBL_GID_SU_MASK,           NO_SHIFT_DEFINE,	false},
-+	{"SPP_EN",              WF_LWTBL_SPP_EN_MASK,           NO_SHIFT_DEFINE,	false},
-+	{"WPI_EVEN",            WF_LWTBL_WPI_EVEN_MASK,         NO_SHIFT_DEFINE,	false},
-+	{"AAD_OM",              WF_LWTBL_AAD_OM_MASK,           NO_SHIFT_DEFINE,	false},
-+	{"CIPHER_PGTK",WF_LWTBL_CIPHER_SUIT_PGTK_MASK, WF_LWTBL_CIPHER_SUIT_PGTK_SHIFT,	true},
-+	{"FROM_DS",             WF_LWTBL_FD_MASK,               NO_SHIFT_DEFINE,	false},
-+	{"TO_DS",               WF_LWTBL_TD_MASK,               NO_SHIFT_DEFINE,	false},
-+	{"SW",                  WF_LWTBL_SW_MASK,               NO_SHIFT_DEFINE,	false},
-+	{"UL",                  WF_LWTBL_UL_MASK,               NO_SHIFT_DEFINE,	false},
-+	{"TX_POWER_SAVE",       WF_LWTBL_TX_PS_MASK,            NO_SHIFT_DEFINE,	true},
-+	{"QOS",                 WF_LWTBL_QOS_MASK,              NO_SHIFT_DEFINE,	false},
-+	{"HT",                  WF_LWTBL_HT_MASK,               NO_SHIFT_DEFINE,	false},
-+	{"VHT",                 WF_LWTBL_VHT_MASK,              NO_SHIFT_DEFINE,	false},
-+	{"HE",                  WF_LWTBL_HE_MASK,               NO_SHIFT_DEFINE,	false},
-+	{"EHT",                 WF_LWTBL_EHT_MASK,              NO_SHIFT_DEFINE,	false},
-+	{"MESH",                WF_LWTBL_MESH_MASK,             NO_SHIFT_DEFINE,	true},
-+	{NULL,}
-+};
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW2_7992[] = {
-+	{"AID",                 WF_LWTBL_AID_MASK,              WF_LWTBL_AID_SHIFT,	false},
-+	{"GID_SU",              WF_LWTBL_GID_SU_MASK,           NO_SHIFT_DEFINE,	false},
-+	{"DUAL_PTEC_EN",        WF_LWTBL_DUAL_PTEC_EN_MASK,     NO_SHIFT_DEFINE,	false},
-+	{"DUAL_CTS_CAP",        WF_LWTBL_DUAL_CTS_CAP_MASK,     NO_SHIFT_DEFINE,	false},
-+	{"CIPHER_PGTK",WF_LWTBL_CIPHER_SUIT_PGTK_MASK, WF_LWTBL_CIPHER_SUIT_PGTK_SHIFT,	true},
-+	{"FROM_DS",             WF_LWTBL_FD_MASK,               NO_SHIFT_DEFINE,	false},
-+	{"TO_DS",               WF_LWTBL_TD_MASK,               NO_SHIFT_DEFINE,	false},
-+	{"SW",                  WF_LWTBL_SW_MASK,               NO_SHIFT_DEFINE,	false},
-+	{"UL",                  WF_LWTBL_UL_MASK,               NO_SHIFT_DEFINE,	false},
-+	{"TX_POWER_SAVE",       WF_LWTBL_TX_PS_MASK,            NO_SHIFT_DEFINE,	true},
-+	{"QOS",                 WF_LWTBL_QOS_MASK,              NO_SHIFT_DEFINE,	false},
-+	{"HT",                  WF_LWTBL_HT_MASK,               NO_SHIFT_DEFINE,	false},
-+	{"VHT",                 WF_LWTBL_VHT_MASK,              NO_SHIFT_DEFINE,	false},
-+	{"HE",                  WF_LWTBL_HE_MASK,               NO_SHIFT_DEFINE,	false},
-+	{"EHT",                 WF_LWTBL_EHT_MASK,              NO_SHIFT_DEFINE,	false},
-+	{"MESH",                WF_LWTBL_MESH_MASK,             NO_SHIFT_DEFINE,	true},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw2(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	/* LMAC WTBL DW 2 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 2\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_2*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_LMAC_DW2[i].name) {
-+
-+		if (WTBL_LMAC_DW2[i].shift == NO_SHIFT_DEFINE)
-+			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW2[i].name,
-+					 (dw_value & WTBL_LMAC_DW2[i].mask) ? 1 : 0);
-+		else
-+			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW2[i].name,
-+					  (dw_value & WTBL_LMAC_DW2[i].mask) >> WTBL_LMAC_DW2[i].shift);
-+		i++;
-+	}
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW3[] = {
-+	{"WMM_Q",           WF_LWTBL_WMM_Q_MASK,		WF_LWTBL_WMM_Q_SHIFT,		false},
-+	{"EHT_SIG_MCS",     WF_LWTBL_EHT_SIG_MCS_MASK,		WF_LWTBL_EHT_SIG_MCS_SHIFT,	false},
-+	{"HDRT_MODE",       WF_LWTBL_HDRT_MODE_MASK,		NO_SHIFT_DEFINE,		false},
-+	{"BEAM_CHG",        WF_LWTBL_BEAM_CHG_MASK,		NO_SHIFT_DEFINE,		false},
-+	{"EHT_LTF_SYM_NUM", WF_LWTBL_EHT_LTF_SYM_NUM_OPT_MASK,  WF_LWTBL_EHT_LTF_SYM_NUM_OPT_SHIFT,	true},
-+	{"PFMU_IDX",	    WF_LWTBL_PFMU_IDX_MASK,		WF_LWTBL_PFMU_IDX_SHIFT,	false},
-+	{"ULPF_IDX",	    WF_LWTBL_ULPF_IDX_MASK,		WF_LWTBL_ULPF_IDX_SHIFT,	false},
-+	{"RIBF",	    WF_LWTBL_RIBF_MASK,			NO_SHIFT_DEFINE,		false},
-+	{"ULPF",	    WF_LWTBL_ULPF_MASK,			NO_SHIFT_DEFINE,		false},
-+	{"BYPASS_TXSMM",    WF_LWTBL_BYPASS_TXSMM_MASK,         NO_SHIFT_DEFINE,		true},
-+	{"TBF_HT",          WF_LWTBL_TBF_HT_MASK,		NO_SHIFT_DEFINE,		false},
-+	{"TBF_VHT",         WF_LWTBL_TBF_VHT_MASK,		NO_SHIFT_DEFINE,		false},
-+	{"TBF_HE",          WF_LWTBL_TBF_HE_MASK,		NO_SHIFT_DEFINE,		false},
-+	{"TBF_EHT",         WF_LWTBL_TBF_EHT_MASK,		NO_SHIFT_DEFINE,		false},
-+	{"IGN_FBK",         WF_LWTBL_IGN_FBK_MASK,		NO_SHIFT_DEFINE,		true},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw3(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	/* LMAC WTBL DW 3 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 3\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_3*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_LMAC_DW3[i].name) {
-+
-+		if (WTBL_LMAC_DW3[i].shift == NO_SHIFT_DEFINE)
-+			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW3[i].name,
-+					 (dw_value & WTBL_LMAC_DW3[i].mask) ? 1 : 0);
-+		else
-+			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW3[i].name,
-+					  (dw_value & WTBL_LMAC_DW3[i].mask) >> WTBL_LMAC_DW3[i].shift);
-+		i++;
-+	}
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW4[] = {
-+	{"NEGOTIATED_WINSIZE0",	WF_LWTBL_NEGOTIATED_WINSIZE0_MASK,	WF_LWTBL_NEGOTIATED_WINSIZE0_SHIFT,    false},
-+	{"WINSIZE1",	WF_LWTBL_NEGOTIATED_WINSIZE1_MASK,	WF_LWTBL_NEGOTIATED_WINSIZE1_SHIFT,    false},
-+	{"WINSIZE2",	WF_LWTBL_NEGOTIATED_WINSIZE2_MASK,	WF_LWTBL_NEGOTIATED_WINSIZE2_SHIFT,    false},
-+	{"WINSIZE3",	WF_LWTBL_NEGOTIATED_WINSIZE3_MASK,	WF_LWTBL_NEGOTIATED_WINSIZE3_SHIFT,    true},
-+	{"WINSIZE4",	WF_LWTBL_NEGOTIATED_WINSIZE4_MASK,	WF_LWTBL_NEGOTIATED_WINSIZE4_SHIFT,    false},
-+	{"WINSIZE5",	WF_LWTBL_NEGOTIATED_WINSIZE5_MASK,	WF_LWTBL_NEGOTIATED_WINSIZE5_SHIFT,    false},
-+	{"WINSIZE6",	WF_LWTBL_NEGOTIATED_WINSIZE6_MASK,	WF_LWTBL_NEGOTIATED_WINSIZE6_SHIFT,    false},
-+	{"WINSIZE7",	WF_LWTBL_NEGOTIATED_WINSIZE7_MASK,	WF_LWTBL_NEGOTIATED_WINSIZE7_SHIFT,    true},
-+	{"PE",              WF_LWTBL_PE_MASK,           WF_LWTBL_PE_SHIFT,	false},
-+	{"DIS_RHTR",        WF_LWTBL_DIS_RHTR_MASK,     NO_SHIFT_DEFINE,	false},
-+	{"LDPC_HT",         WF_LWTBL_LDPC_HT_MASK,      NO_SHIFT_DEFINE,	false},
-+	{"LDPC_VHT",        WF_LWTBL_LDPC_VHT_MASK,     NO_SHIFT_DEFINE,	false},
-+	{"LDPC_HE",         WF_LWTBL_LDPC_HE_MASK,      NO_SHIFT_DEFINE,	false},
-+	{"LDPC_EHT",	    WF_LWTBL_LDPC_EHT_MASK,	NO_SHIFT_DEFINE,	true},
-+	{"BA_MODE",	    WF_LWTBL_BA_MODE_MASK,	NO_SHIFT_DEFINE,	true},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw4(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	/* LMAC WTBL DW 4 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 4\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_4*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_LMAC_DW4[i].name) {
-+		if (WTBL_LMAC_DW4[i].shift == NO_SHIFT_DEFINE)
-+			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW4[i].name,
-+					 (dw_value & WTBL_LMAC_DW4[i].mask) ? 1 : 0);
-+		else
-+			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW4[i].name,
-+					  (dw_value & WTBL_LMAC_DW4[i].mask) >> WTBL_LMAC_DW4[i].shift);
-+		i++;
-+	}
-+}
-+
-+static const struct berse_wtbl_parse *WTBL_LMAC_DW5;
-+static const struct berse_wtbl_parse WTBL_LMAC_DW5_7996[] = {
-+	{"AF",                  WF_LWTBL_AF_MASK,           WF_LWTBL_AF_SHIFT,	false},
-+	{"AF_HE",               WF_LWTBL_AF_HE_MASK,        WF_LWTBL_AF_HE_SHIFT,false},
-+	{"RTS",                 WF_LWTBL_RTS_MASK,          NO_SHIFT_DEFINE,	false},
-+	{"SMPS",                WF_LWTBL_SMPS_MASK,         NO_SHIFT_DEFINE,	false},
-+	{"DYN_BW",              WF_LWTBL_DYN_BW_MASK,       NO_SHIFT_DEFINE,	true},
-+	{"MMSS",                WF_LWTBL_MMSS_MASK,         WF_LWTBL_MMSS_SHIFT,false},
-+	{"USR",                 WF_LWTBL_USR_MASK,          NO_SHIFT_DEFINE,	false},
-+	{"SR_RATE",             WF_LWTBL_SR_R_MASK,         WF_LWTBL_SR_R_SHIFT,false},
-+	{"SR_ABORT",            WF_LWTBL_SR_ABORT_MASK,     NO_SHIFT_DEFINE,	true},
-+	{"TX_POWER_OFFSET",     WF_LWTBL_TX_POWER_OFFSET_MASK,  WF_LWTBL_TX_POWER_OFFSET_SHIFT,	false},
-+	{"LTF_EHT",		WF_LWTBL_LTF_EHT_MASK,      WF_LWTBL_LTF_EHT_SHIFT, false},
-+	{"GI_EHT",		WF_LWTBL_GI_EHT_MASK,       WF_LWTBL_GI_EHT_SHIFT, false},
-+	{"DOPPL",               WF_LWTBL_DOPPL_MASK,        NO_SHIFT_DEFINE,	false},
-+	{"TXOP_PS_CAP",         WF_LWTBL_TXOP_PS_CAP_MASK,  NO_SHIFT_DEFINE,	false},
-+	{"DONOT_UPDATE_I_PSM",  WF_LWTBL_DU_I_PSM_MASK,     NO_SHIFT_DEFINE,	true},
-+	{"I_PSM",               WF_LWTBL_I_PSM_MASK,        NO_SHIFT_DEFINE,	false},
-+	{"PSM",                 WF_LWTBL_PSM_MASK,          NO_SHIFT_DEFINE,	false},
-+	{"SKIP_TX",             WF_LWTBL_SKIP_TX_MASK,      NO_SHIFT_DEFINE,	true},
-+	{NULL,}
-+};
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW5_7992[] = {
-+	{"AF",                  WF_LWTBL_AF_MASK_7992,      WF_LWTBL_AF_SHIFT,	false},
-+	{"RTS",                 WF_LWTBL_RTS_MASK,          NO_SHIFT_DEFINE,	false},
-+	{"SMPS",                WF_LWTBL_SMPS_MASK,         NO_SHIFT_DEFINE,	false},
-+	{"DYN_BW",              WF_LWTBL_DYN_BW_MASK,       NO_SHIFT_DEFINE,	true},
-+	{"MMSS",                WF_LWTBL_MMSS_MASK,         WF_LWTBL_MMSS_SHIFT,false},
-+	{"USR",                 WF_LWTBL_USR_MASK,          NO_SHIFT_DEFINE,	false},
-+	{"SR_RATE",             WF_LWTBL_SR_R_MASK,         WF_LWTBL_SR_R_SHIFT,false},
-+	{"SR_ABORT",            WF_LWTBL_SR_ABORT_MASK,     NO_SHIFT_DEFINE,	true},
-+	{"TX_POWER_OFFSET",     WF_LWTBL_TX_POWER_OFFSET_MASK,  WF_LWTBL_TX_POWER_OFFSET_SHIFT,	false},
-+	{"LTF_EHT",		WF_LWTBL_LTF_EHT_MASK,      WF_LWTBL_LTF_EHT_SHIFT, false},
-+	{"GI_EHT",		WF_LWTBL_GI_EHT_MASK,       WF_LWTBL_GI_EHT_SHIFT, false},
-+	{"DOPPL",               WF_LWTBL_DOPPL_MASK,        NO_SHIFT_DEFINE,	false},
-+	{"TXOP_PS_CAP",         WF_LWTBL_TXOP_PS_CAP_MASK,  NO_SHIFT_DEFINE,	false},
-+	{"DONOT_UPDATE_I_PSM",  WF_LWTBL_DU_I_PSM_MASK,     NO_SHIFT_DEFINE,	true},
-+	{"I_PSM",               WF_LWTBL_I_PSM_MASK,        NO_SHIFT_DEFINE,	false},
-+	{"PSM",                 WF_LWTBL_PSM_MASK,          NO_SHIFT_DEFINE,	false},
-+	{"SKIP_TX",             WF_LWTBL_SKIP_TX_MASK,      NO_SHIFT_DEFINE,	true},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw5(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	/* LMAC WTBL DW 5 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 5\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_5*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_LMAC_DW5[i].name) {
-+		if (WTBL_LMAC_DW5[i].shift == NO_SHIFT_DEFINE)
-+			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW5[i].name,
-+					 (dw_value & WTBL_LMAC_DW5[i].mask) ? 1 : 0);
-+		else
-+			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW5[i].name,
-+					  (dw_value & WTBL_LMAC_DW5[i].mask) >> WTBL_LMAC_DW5[i].shift);
-+		i++;
-+	}
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW6[] = {
-+	{"CBRN",        WF_LWTBL_CBRN_MASK,	    WF_LWTBL_CBRN_SHIFT,	false},
-+	{"DBNSS_EN",    WF_LWTBL_DBNSS_EN_MASK, NO_SHIFT_DEFINE,	false},
-+	{"BAF_EN",      WF_LWTBL_BAF_EN_MASK,   NO_SHIFT_DEFINE,	false},
-+	{"RDGBA",       WF_LWTBL_RDGBA_MASK,    NO_SHIFT_DEFINE,	false},
-+	{"RDG",         WF_LWTBL_R_MASK,        NO_SHIFT_DEFINE,	false},
-+	{"SPE_IDX",     WF_LWTBL_SPE_IDX_MASK,  WF_LWTBL_SPE_IDX_SHIFT,	true},
-+	{"G2",          WF_LWTBL_G2_MASK,       NO_SHIFT_DEFINE,	false},
-+	{"G4",          WF_LWTBL_G4_MASK,       NO_SHIFT_DEFINE,	false},
-+	{"G8",          WF_LWTBL_G8_MASK,       NO_SHIFT_DEFINE,	false},
-+	{"G16",         WF_LWTBL_G16_MASK,      NO_SHIFT_DEFINE,	true},
-+	{"G2_LTF",      WF_LWTBL_G2_LTF_MASK,   WF_LWTBL_G2_LTF_SHIFT,	false},
-+	{"G4_LTF",      WF_LWTBL_G4_LTF_MASK,   WF_LWTBL_G4_LTF_SHIFT,	false},
-+	{"G8_LTF",      WF_LWTBL_G8_LTF_MASK,   WF_LWTBL_G8_LTF_SHIFT,	false},
-+	{"G16_LTF",     WF_LWTBL_G16_LTF_MASK,  WF_LWTBL_G16_LTF_SHIFT,	true},
-+	{"G2_HE",       WF_LWTBL_G2_HE_MASK,    WF_LWTBL_G2_HE_SHIFT,	false},
-+	{"G4_HE",       WF_LWTBL_G4_HE_MASK,    WF_LWTBL_G4_HE_SHIFT,	false},
-+	{"G8_HE",       WF_LWTBL_G8_HE_MASK,    WF_LWTBL_G8_HE_SHIFT,	false},
-+	{"G16_HE",      WF_LWTBL_G16_HE_MASK,   WF_LWTBL_G16_HE_SHIFT,	true},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw6(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	/* LMAC WTBL DW 6 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 6\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_6*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_LMAC_DW6[i].name) {
-+		if (WTBL_LMAC_DW6[i].shift == NO_SHIFT_DEFINE)
-+			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW6[i].name,
-+					 (dw_value & WTBL_LMAC_DW6[i].mask) ? 1 : 0);
-+		else
-+			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW6[i].name,
-+					  (dw_value & WTBL_LMAC_DW6[i].mask) >> WTBL_LMAC_DW6[i].shift);
-+		i++;
-+	}
-+}
-+
-+static void parse_fmac_lwtbl_dw7(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	int i = 0;
-+
-+	/* LMAC WTBL DW 7 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 7\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_7*4]);
-+	dw_value = *addr;
-+
-+	for (i = 0; i < 8; i++) {
-+		seq_printf(s, "\tBA_WIN_SIZE%u:%lu\n", i, ((dw_value & BITS(i*4, i*4+3)) >> i*4));
-+	}
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW8[] = {
-+	{"RTS_FAIL_CNT_AC0",    WF_LWTBL_AC0_RTS_FAIL_CNT_MASK,	WF_LWTBL_AC0_RTS_FAIL_CNT_SHIFT,	false},
-+	{"AC1",                 WF_LWTBL_AC1_RTS_FAIL_CNT_MASK,	WF_LWTBL_AC1_RTS_FAIL_CNT_SHIFT,	false},
-+	{"AC2",                 WF_LWTBL_AC2_RTS_FAIL_CNT_MASK,	WF_LWTBL_AC2_RTS_FAIL_CNT_SHIFT,	false},
-+	{"AC3",                 WF_LWTBL_AC3_RTS_FAIL_CNT_MASK,	WF_LWTBL_AC3_RTS_FAIL_CNT_SHIFT,	true},
-+	{"PARTIAL_AID",         WF_LWTBL_PARTIAL_AID_MASK,		WF_LWTBL_PARTIAL_AID_SHIFT,	false},
-+	{"CHK_PER",             WF_LWTBL_CHK_PER_MASK,		NO_SHIFT_DEFINE,	true},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw8(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	/* LMAC WTBL DW 8 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 8\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_8*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_LMAC_DW8[i].name) {
-+		if (WTBL_LMAC_DW8[i].shift == NO_SHIFT_DEFINE)
-+			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW8[i].name,
-+					 (dw_value & WTBL_LMAC_DW8[i].mask) ? 1 : 0);
-+		else
-+			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW8[i].name,
-+					  (dw_value & WTBL_LMAC_DW8[i].mask) >> WTBL_LMAC_DW8[i].shift);
-+		i++;
-+	}
-+}
-+
-+static const struct berse_wtbl_parse *WTBL_LMAC_DW9;
-+static const struct berse_wtbl_parse WTBL_LMAC_DW9_7996[] = {
-+	{"RX_AVG_MPDU_SIZE",    WF_LWTBL_RX_AVG_MPDU_SIZE_MASK,    WF_LWTBL_RX_AVG_MPDU_SIZE_SHIFT,	false},
-+	{"PRITX_SW_MODE",       WF_LWTBL_PRITX_SW_MODE_MASK,       NO_SHIFT_DEFINE,	false},
-+	{"PRITX_ERSU",	    WF_LWTBL_PRITX_ERSU_MASK,	       NO_SHIFT_DEFINE,	false},
-+	{"PRITX_PLR",           WF_LWTBL_PRITX_PLR_MASK,           NO_SHIFT_DEFINE,	true},
-+	{"PRITX_DCM",           WF_LWTBL_PRITX_DCM_MASK,           NO_SHIFT_DEFINE,	false},
-+	{"PRITX_ER106T",        WF_LWTBL_PRITX_ER106T_MASK,        NO_SHIFT_DEFINE,	true},
-+	/* {"FCAP(0:20 1:~40)",    WTBL_FCAP_20_TO_160_MHZ,	WTBL_FCAP_20_TO_160_MHZ_OFFSET}, */
-+	{"MPDU_FAIL_CNT",       WF_LWTBL_MPDU_FAIL_CNT_MASK,       WF_LWTBL_MPDU_FAIL_CNT_SHIFT,	false},
-+	{"MPDU_OK_CNT",         WF_LWTBL_MPDU_OK_CNT_MASK,         WF_LWTBL_MPDU_OK_CNT_SHIFT,	false},
-+	{"RATE_IDX",            WF_LWTBL_RATE_IDX_MASK,            WF_LWTBL_RATE_IDX_SHIFT,	true},
-+	{NULL,}
-+};
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW9_7992[] = {
-+	{"RX_AVG_MPDU_SIZE",    WF_LWTBL_RX_AVG_MPDU_SIZE_MASK,    WF_LWTBL_RX_AVG_MPDU_SIZE_SHIFT,	false},
-+	{"PRITX_SW_MODE",       WF_LWTBL_PRITX_SW_MODE_MASK_7992,       NO_SHIFT_DEFINE,	false},
-+	{"PRITX_ERSU",	    WF_LWTBL_PRITX_ERSU_MASK_7992,	       NO_SHIFT_DEFINE,	false},
-+	{"PRITX_PLR",           WF_LWTBL_PRITX_PLR_MASK_7992,           NO_SHIFT_DEFINE,	true},
-+	{"PRITX_DCM",           WF_LWTBL_PRITX_DCM_MASK,           NO_SHIFT_DEFINE,	false},
-+	{"PRITX_ER106T",        WF_LWTBL_PRITX_ER106T_MASK,        NO_SHIFT_DEFINE,	true},
-+	/* {"FCAP(0:20 1:~40)",    WTBL_FCAP_20_TO_160_MHZ,	WTBL_FCAP_20_TO_160_MHZ_OFFSET}, */
-+	{"MPDU_FAIL_CNT",       WF_LWTBL_MPDU_FAIL_CNT_MASK,       WF_LWTBL_MPDU_FAIL_CNT_SHIFT,	false},
-+	{"MPDU_OK_CNT",         WF_LWTBL_MPDU_OK_CNT_MASK,         WF_LWTBL_MPDU_OK_CNT_SHIFT,	false},
-+	{"RATE_IDX",            WF_LWTBL_RATE_IDX_MASK,            WF_LWTBL_RATE_IDX_SHIFT,	true},
-+	{NULL,}
-+};
-+
-+char *fcap_name[] = {"20MHz", "20/40MHz", "20/40/80MHz", "20/40/80/160/80+80MHz", "20/40/80/160/80+80/320MHz"};
-+
-+static void parse_fmac_lwtbl_dw9(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	/* LMAC WTBL DW 9 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 9\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_9*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_LMAC_DW9[i].name) {
-+		if (WTBL_LMAC_DW9[i].shift == NO_SHIFT_DEFINE)
-+			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW9[i].name,
-+					 (dw_value & WTBL_LMAC_DW9[i].mask) ? 1 : 0);
-+		else
-+			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW9[i].name,
-+					  (dw_value & WTBL_LMAC_DW9[i].mask) >> WTBL_LMAC_DW9[i].shift);
-+		i++;
-+	}
-+
-+	/* FCAP parser */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "FCAP:%s\n", fcap_name[(dw_value & WF_LWTBL_FCAP_MASK) >> WF_LWTBL_FCAP_SHIFT]);
-+}
-+
-+#define HW_TX_RATE_TO_MODE(_x)			(((_x) & WTBL_RATE_TX_MODE_MASK) >> WTBL_RATE_TX_MODE_OFFSET)
-+#define HW_TX_RATE_TO_MCS(_x, _mode)		((_x) & WTBL_RATE_TX_RATE_MASK >> WTBL_RATE_TX_RATE_OFFSET)
-+#define HW_TX_RATE_TO_NSS(_x)			(((_x) & WTBL_RATE_NSTS_MASK) >> WTBL_RATE_NSTS_OFFSET)
-+#define HW_TX_RATE_TO_STBC(_x)			(((_x) & WTBL_RATE_STBC_MASK) >> WTBL_RATE_STBC_OFFSET)
-+
-+#define MAX_TX_MODE 16
-+static char *HW_TX_MODE_STR[] = {"CCK", "OFDM", "HT-Mix", "HT-GF", "VHT",
-+				 "N/A", "N/A", "N/A",
-+				 "HE_SU", "HE_EXT_SU", "HE_TRIG", "HE_MU",
-+				 "N/A",
-+				 "EHT_EXT_SU", "EHT_TRIG", "EHT_MU",
-+				 "N/A"};
-+static char *HW_TX_RATE_CCK_STR[] = {"1M", "2Mlong", "5.5Mlong", "11Mlong", "N/A", "2Mshort", "5.5Mshort", "11Mshort", "N/A"};
-+static char *HW_TX_RATE_OFDM_STR[] = {"6M", "9M", "12M", "18M", "24M", "36M", "48M", "54M", "N/A"};
-+
-+static char *hw_rate_ofdm_str(uint16_t ofdm_idx)
-+{
-+	switch (ofdm_idx) {
-+	case 11: /* 6M */
-+		return HW_TX_RATE_OFDM_STR[0];
-+
-+	case 15: /* 9M */
-+		return HW_TX_RATE_OFDM_STR[1];
-+
-+	case 10: /* 12M */
-+		return HW_TX_RATE_OFDM_STR[2];
-+
-+	case 14: /* 18M */
-+		return HW_TX_RATE_OFDM_STR[3];
-+
-+	case 9: /* 24M */
-+		return HW_TX_RATE_OFDM_STR[4];
-+
-+	case 13: /* 36M */
-+		return HW_TX_RATE_OFDM_STR[5];
-+
-+	case 8: /* 48M */
-+		return HW_TX_RATE_OFDM_STR[6];
-+
-+	case 12: /* 54M */
-+		return HW_TX_RATE_OFDM_STR[7];
-+
-+	default:
-+		return HW_TX_RATE_OFDM_STR[8];
-+	}
-+}
-+
-+static char *hw_rate_str(u8 mode, uint16_t rate_idx)
-+{
-+	if (mode == 0)
-+		return rate_idx < 8 ? HW_TX_RATE_CCK_STR[rate_idx] : HW_TX_RATE_CCK_STR[8];
-+	else if (mode == 1)
-+		return hw_rate_ofdm_str(rate_idx);
-+	else
-+		return "MCS";
-+}
-+
-+static void
-+parse_rate(struct seq_file *s, uint16_t rate_idx, uint16_t txrate)
-+{
-+	uint16_t txmode, mcs, nss, stbc;
-+
-+	txmode = HW_TX_RATE_TO_MODE(txrate);
-+	mcs = HW_TX_RATE_TO_MCS(txrate, txmode);
-+	nss = HW_TX_RATE_TO_NSS(txrate);
-+	stbc = HW_TX_RATE_TO_STBC(txrate);
-+
-+	seq_printf(s, "\tRate%d(0x%x):TxMode=%d(%s), TxRate=%d(%s), Nsts=%d, STBC=%d\n",
-+			  rate_idx + 1, txrate,
-+			  txmode, (txmode < MAX_TX_MODE ? HW_TX_MODE_STR[txmode] : HW_TX_MODE_STR[MAX_TX_MODE]),
-+			  mcs, hw_rate_str(txmode, mcs), nss, stbc);
-+}
-+
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW10[] = {
-+	{"RATE1",       WF_LWTBL_RATE1_MASK,        WF_LWTBL_RATE1_SHIFT},
-+	{"RATE2",       WF_LWTBL_RATE2_MASK,        WF_LWTBL_RATE2_SHIFT},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw10(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	/* LMAC WTBL DW 10 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 10\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_AUTO_RATE_1_2*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_LMAC_DW10[i].name) {
-+		parse_rate(s, i, (dw_value & WTBL_LMAC_DW10[i].mask) >> WTBL_LMAC_DW10[i].shift);
-+		i++;
-+	}
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW11[] = {
-+	{"RATE3",       WF_LWTBL_RATE3_MASK,        WF_LWTBL_RATE3_SHIFT},
-+	{"RATE4",       WF_LWTBL_RATE4_MASK,        WF_LWTBL_RATE4_SHIFT},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw11(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	/* LMAC WTBL DW 11 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 11\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_AUTO_RATE_3_4*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_LMAC_DW11[i].name) {
-+		parse_rate(s, i+2, (dw_value & WTBL_LMAC_DW11[i].mask) >> WTBL_LMAC_DW11[i].shift);
-+		i++;
-+	}
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW12[] = {
-+	{"RATE5",       WF_LWTBL_RATE5_MASK,        WF_LWTBL_RATE5_SHIFT},
-+	{"RATE6",       WF_LWTBL_RATE6_MASK,        WF_LWTBL_RATE6_SHIFT},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw12(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	/* LMAC WTBL DW 12 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 12\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_AUTO_RATE_5_6*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_LMAC_DW12[i].name) {
-+		parse_rate(s, i+4, (dw_value & WTBL_LMAC_DW12[i].mask) >> WTBL_LMAC_DW12[i].shift);
-+		i++;
-+	}
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW13[] = {
-+	{"RATE7",       WF_LWTBL_RATE7_MASK,        WF_LWTBL_RATE7_SHIFT},
-+	{"RATE8",       WF_LWTBL_RATE8_MASK,        WF_LWTBL_RATE8_SHIFT},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw13(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	/* LMAC WTBL DW 13 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 13\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_AUTO_RATE_7_8*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_LMAC_DW13[i].name) {
-+		parse_rate(s, i+6, (dw_value & WTBL_LMAC_DW13[i].mask) >> WTBL_LMAC_DW13[i].shift);
-+		i++;
-+	}
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW14_BMC[] = {
-+	{"CIPHER_IGTK",         WF_LWTBL_CIPHER_SUIT_IGTK_MASK,    WF_LWTBL_CIPHER_SUIT_IGTK_SHIFT,		false},
-+	{"CIPHER_BIGTK",        WF_LWTBL_CIPHER_SUIT_BIGTK_MASK,   WF_LWTBL_CIPHER_SUIT_BIGTK_SHIFT,	true},
-+	{NULL,}
-+};
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW14[] = {
-+	{"RATE1_TX_CNT",      WF_LWTBL_RATE1_TX_CNT_MASK,     WF_LWTBL_RATE1_TX_CNT_SHIFT,   false},
-+	{"RATE1_FAIL_CNT",    WF_LWTBL_RATE1_FAIL_CNT_MASK,   WF_LWTBL_RATE1_FAIL_CNT_SHIFT, true},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw14(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr, *muar_addr = 0;
-+	u32 dw_value, muar_dw_value = 0;
-+	u16 i = 0;
-+
-+	/* DUMP DW14 for BMC entry only */
-+	muar_addr = (u32 *)&(lwtbl[WF_LWTBL_MUAR_DW*4]);
-+	muar_dw_value = *muar_addr;
-+	if (((muar_dw_value & WF_LWTBL_MUAR_MASK) >> WF_LWTBL_MUAR_SHIFT)
-+		== MUAR_INDEX_OWN_MAC_ADDR_BC_MC) {
-+		/* LMAC WTBL DW 14 */
-+		seq_printf(s, "\t\n");
-+		seq_printf(s, "LWTBL DW 14\n");
-+		addr = (u32 *)&(lwtbl[WF_LWTBL_CIPHER_SUIT_IGTK_DW*4]);
-+		dw_value = *addr;
-+
-+		while (WTBL_LMAC_DW14_BMC[i].name) {
-+			if (WTBL_LMAC_DW14_BMC[i].shift == NO_SHIFT_DEFINE)
-+				seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW14_BMC[i].name,
-+					(dw_value & WTBL_LMAC_DW14_BMC[i].mask) ? 1 : 0);
-+			else
-+				seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW14_BMC[i].name,
-+					(dw_value & WTBL_LMAC_DW14_BMC[i].mask) >> WTBL_LMAC_DW14_BMC[i].shift);
-+			i++;
-+		}
-+	} else {
-+		seq_printf(s, "\t\n");
-+		seq_printf(s, "LWTBL DW 14\n");
-+		addr = (u32 *)&(lwtbl[WF_LWTBL_CIPHER_SUIT_IGTK_DW*4]);
-+		dw_value = *addr;
-+
-+		while (WTBL_LMAC_DW14[i].name) {
-+			if (WTBL_LMAC_DW14[i].shift == NO_SHIFT_DEFINE)
-+				seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW14[i].name,
-+					(dw_value & WTBL_LMAC_DW14[i].mask) ? 1 : 0);
-+			else
-+				seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW14[i].name,
-+					(dw_value & WTBL_LMAC_DW14[i].mask) >> WTBL_LMAC_DW14[i].shift);
-+			i++;
-+		}
-+	}
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW28[] = {
-+	{"RELATED_IDX0",	WF_LWTBL_RELATED_IDX0_MASK,		WF_LWTBL_RELATED_IDX0_SHIFT,	false},
-+	{"RELATED_BAND0",	WF_LWTBL_RELATED_BAND0_MASK,		WF_LWTBL_RELATED_BAND0_SHIFT,	false},
-+	{"PRI_MLD_BAND",    WF_LWTBL_PRIMARY_MLD_BAND_MASK,		WF_LWTBL_PRIMARY_MLD_BAND_SHIFT,	true},
-+	{"RELATED_IDX1",	WF_LWTBL_RELATED_IDX1_MASK,		WF_LWTBL_RELATED_IDX1_SHIFT,	false},
-+	{"RELATED_BAND1",   WF_LWTBL_RELATED_BAND1_MASK,		WF_LWTBL_RELATED_BAND1_SHIFT,	false},
-+	{"SEC_MLD_BAND",	WF_LWTBL_SECONDARY_MLD_BAND_MASK,	WF_LWTBL_SECONDARY_MLD_BAND_SHIFT,	true},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw28(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	/* LMAC WTBL DW 28 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 28\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_MLO_INFO_LINE_1*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_LMAC_DW28[i].name) {
-+		if (WTBL_LMAC_DW28[i].shift == NO_SHIFT_DEFINE)
-+			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW28[i].name,
-+				(dw_value & WTBL_LMAC_DW28[i].mask) ? 1 : 0);
-+		else
-+			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW28[i].name,
-+				(dw_value & WTBL_LMAC_DW28[i].mask) >>
-+					WTBL_LMAC_DW28[i].shift);
-+		i++;
-+	}
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW29[] = {
-+	{"DISPATCH_POLICY_MLD_TID0", WF_LWTBL_DISPATCH_POLICY0_MASK,	WF_LWTBL_DISPATCH_POLICY0_SHIFT,	false},
-+	{"MLD_TID1",	WF_LWTBL_DISPATCH_POLICY1_MASK,		WF_LWTBL_DISPATCH_POLICY1_SHIFT,	false},
-+	{"MLD_TID2",	WF_LWTBL_DISPATCH_POLICY2_MASK,		WF_LWTBL_DISPATCH_POLICY2_SHIFT,	false},
-+	{"MLD_TID3",	WF_LWTBL_DISPATCH_POLICY3_MASK,	WF_LWTBL_DISPATCH_POLICY3_SHIFT,	true},
-+	{"MLD_TID4",	WF_LWTBL_DISPATCH_POLICY4_MASK,		WF_LWTBL_DISPATCH_POLICY4_SHIFT,	false},
-+	{"MLD_TID5",	WF_LWTBL_DISPATCH_POLICY5_MASK,		WF_LWTBL_DISPATCH_POLICY5_SHIFT,	false},
-+	{"MLD_TID6",	WF_LWTBL_DISPATCH_POLICY6_MASK,		WF_LWTBL_DISPATCH_POLICY6_SHIFT,	false},
-+	{"MLD_TID7",	WF_LWTBL_DISPATCH_POLICY7_MASK,		WF_LWTBL_DISPATCH_POLICY7_SHIFT,	true},
-+	{"OMLD_ID",		WF_LWTBL_OWN_MLD_ID_MASK,	WF_LWTBL_OWN_MLD_ID_SHIFT,	false},
-+	{"EMLSR0",		WF_LWTBL_EMLSR0_MASK,		NO_SHIFT_DEFINE,	false},
-+	{"EMLMR0",		WF_LWTBL_EMLMR0_MASK,		NO_SHIFT_DEFINE,	false},
-+	{"EMLSR1",		WF_LWTBL_EMLSR1_MASK,		NO_SHIFT_DEFINE,	false},
-+	{"EMLMR1",		WF_LWTBL_EMLMR1_MASK,		NO_SHIFT_DEFINE,	true},
-+	{"EMLSR2",		WF_LWTBL_EMLSR2_MASK,		NO_SHIFT_DEFINE,	false},
-+	{"EMLMR2",		WF_LWTBL_EMLMR2_MASK,		NO_SHIFT_DEFINE,	false},
-+	{"STR_BITMAP",	WF_LWTBL_STR_BITMAP_MASK,	WF_LWTBL_STR_BITMAP_SHIFT,	true},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw29(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	/* LMAC WTBL DW 29 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 29\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_MLO_INFO_LINE_2*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_LMAC_DW29[i].name) {
-+		if (WTBL_LMAC_DW29[i].shift == NO_SHIFT_DEFINE)
-+			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW29[i].name,
-+				(dw_value & WTBL_LMAC_DW29[i].mask) ? 1 : 0);
-+		else
-+			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW29[i].name,
-+				(dw_value & WTBL_LMAC_DW29[i].mask) >>
-+					WTBL_LMAC_DW29[i].shift);
-+		i++;
-+	}
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW30[] = {
-+	{"DISPATCH_ORDER",	WF_LWTBL_DISPATCH_ORDER_MASK,	WF_LWTBL_DISPATCH_ORDER_SHIFT,	false},
-+	{"DISPATCH_RATIO",	WF_LWTBL_DISPATCH_RATIO_MASK,	WF_LWTBL_DISPATCH_RATIO_SHIFT,	false},
-+	{"LINK_MGF",		WF_LWTBL_LINK_MGF_MASK,		WF_LWTBL_LINK_MGF_SHIFT,	true},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw30(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	/* LMAC WTBL DW 30 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 30\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_MLO_INFO_LINE_3*4]);
-+	dw_value = *addr;
-+
-+
-+	while (WTBL_LMAC_DW30[i].name) {
-+		if (WTBL_LMAC_DW30[i].shift == NO_SHIFT_DEFINE)
-+			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW30[i].name,
-+				(dw_value & WTBL_LMAC_DW30[i].mask) ? 1 : 0);
-+		else
-+			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW30[i].name,
-+				(dw_value & WTBL_LMAC_DW30[i].mask) >> WTBL_LMAC_DW30[i].shift);
-+		i++;
-+	}
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW31[] = {
-+	{"BFTX_TB",          WF_LWTBL_BFTX_TB_MASK,                 NO_SHIFT_DEFINE,    false},
-+	{"DROP",          WF_LWTBL_DROP_MASK,                 NO_SHIFT_DEFINE,    false},
-+	{"CASCAD",	        WF_LWTBL_CASCAD_MASK,			NO_SHIFT_DEFINE,    false},
-+	{"ALL_ACK",	        WF_LWTBL_ALL_ACK_MASK,			NO_SHIFT_DEFINE,    false},
-+	{"MPDU_SIZE",	WF_LWTBL_MPDU_SIZE_MASK,		WF_LWTBL_MPDU_SIZE_SHIFT,  false},
-+	{"RXD_DUP_MODE",	WF_LWTBL_RXD_DUP_MODE_MASK,			WF_LWTBL_RXD_DUP_MODE_SHIFT,  true},
-+	{"ACK_EN",		WF_LWTBL_ACK_EN_MASK,			NO_SHIFT_DEFINE,		true},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw31(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	/* LMAC WTBL DW 31 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 31\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_RESP_INFO_DW_31*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_LMAC_DW31[i].name) {
-+		if (WTBL_LMAC_DW31[i].shift == NO_SHIFT_DEFINE)
-+			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW31[i].name,
-+				(dw_value & WTBL_LMAC_DW31[i].mask) ? 1 : 0);
-+		else
-+			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW31[i].name,
-+				(dw_value & WTBL_LMAC_DW31[i].mask) >>
-+					WTBL_LMAC_DW31[i].shift);
-+		i++;
-+	}
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW32[] = {
-+	{"OM_INFO",			WF_LWTBL_OM_INFO_MASK,			WF_LWTBL_OM_INFO_SHIFT,		false},
-+	{"OM_INFO_EHT",         WF_LWTBL_OM_INFO_EHT_MASK,         WF_LWTBL_OM_INFO_EHT_SHIFT,  false},
-+	{"RXD_DUP_FOR_OM_CHG",		WF_LWTBL_RXD_DUP_FOR_OM_CHG_MASK,	NO_SHIFT_DEFINE,		false},
-+	{"RXD_DUP_WHITE_LIST",	WF_LWTBL_RXD_DUP_WHITE_LIST_MASK,	WF_LWTBL_RXD_DUP_WHITE_LIST_SHIFT,	false},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw32(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	/* LMAC WTBL DW 32 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 32\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_RX_DUP_INFO_DW_32*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_LMAC_DW32[i].name) {
-+		if (WTBL_LMAC_DW32[i].shift == NO_SHIFT_DEFINE)
-+			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW32[i].name,
-+				(dw_value & WTBL_LMAC_DW32[i].mask) ? 1 : 0);
-+		else
-+			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW32[i].name,
-+				(dw_value & WTBL_LMAC_DW32[i].mask) >>
-+					WTBL_LMAC_DW32[i].shift);
-+		i++;
-+	}
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW33[] = {
-+	{"USER_RSSI",                   WF_LWTBL_USER_RSSI_MASK,            WF_LWTBL_USER_RSSI_SHIFT,	false},
-+	{"USER_SNR",                    WF_LWTBL_USER_SNR_MASK,             WF_LWTBL_USER_SNR_SHIFT,	false},
-+	{"RAPID_REACTION_RATE",         WF_LWTBL_RAPID_REACTION_RATE_MASK,  WF_LWTBL_RAPID_REACTION_RATE_SHIFT,	true},
-+	{"HT_AMSDU(Read Only)",         WF_LWTBL_HT_AMSDU_MASK,             NO_SHIFT_DEFINE,	false},
-+	{"AMSDU_CROSS_LG(Read Only)",   WF_LWTBL_AMSDU_CROSS_LG_MASK,       NO_SHIFT_DEFINE,	true},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw33(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	/* LMAC WTBL DW 33 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 33\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_RX_STAT_CNT_LINE_1*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_LMAC_DW33[i].name) {
-+		if (WTBL_LMAC_DW33[i].shift == NO_SHIFT_DEFINE)
-+			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW33[i].name,
-+				(dw_value & WTBL_LMAC_DW33[i].mask) ? 1 : 0);
-+		else
-+			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW33[i].name,
-+				(dw_value & WTBL_LMAC_DW33[i].mask) >>
-+					WTBL_LMAC_DW33[i].shift);
-+		i++;
-+	}
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW34[] = {
-+	{"RESP_RCPI0",	WF_LWTBL_RESP_RCPI0_MASK,	WF_LWTBL_RESP_RCPI0_SHIFT,	false},
-+	{"RCPI1",	WF_LWTBL_RESP_RCPI1_MASK,	WF_LWTBL_RESP_RCPI1_SHIFT,	false},
-+	{"RCPI2",	WF_LWTBL_RESP_RCPI2_MASK,	WF_LWTBL_RESP_RCPI2_SHIFT,	false},
-+	{"RCPI3",	WF_LWTBL_RESP_RCPI3_MASK,	WF_LWTBL_RESP_RCPI3_SHIFT,	true},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw34(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	/* LMAC WTBL DW 34 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 34\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_RX_STAT_CNT_LINE_2*4]);
-+	dw_value = *addr;
-+
-+
-+	while (WTBL_LMAC_DW34[i].name) {
-+		if (WTBL_LMAC_DW34[i].shift == NO_SHIFT_DEFINE)
-+			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW34[i].name,
-+				(dw_value & WTBL_LMAC_DW34[i].mask) ? 1 : 0);
-+		else
-+			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW34[i].name,
-+				(dw_value & WTBL_LMAC_DW34[i].mask) >>
-+					WTBL_LMAC_DW34[i].shift);
-+		i++;
-+	}
-+}
-+
-+static const struct berse_wtbl_parse WTBL_LMAC_DW35[] = {
-+	{"SNR 0",	WF_LWTBL_SNR_RX0_MASK,		WF_LWTBL_SNR_RX0_SHIFT,	false},
-+	{"SNR 1",	WF_LWTBL_SNR_RX1_MASK,		WF_LWTBL_SNR_RX1_SHIFT,	false},
-+	{"SNR 2",	WF_LWTBL_SNR_RX2_MASK,		WF_LWTBL_SNR_RX2_SHIFT,	false},
-+	{"SNR 3",	WF_LWTBL_SNR_RX3_MASK,		WF_LWTBL_SNR_RX3_SHIFT,	true},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_lwtbl_dw35(struct seq_file *s, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	/* LMAC WTBL DW 35 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "LWTBL DW 35\n");
-+	addr = (u32 *)&(lwtbl[WTBL_GROUP_RX_STAT_CNT_LINE_3*4]);
-+	dw_value = *addr;
-+
-+
-+	while (WTBL_LMAC_DW35[i].name) {
-+		if (WTBL_LMAC_DW35[i].shift == NO_SHIFT_DEFINE)
-+			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW35[i].name,
-+				(dw_value & WTBL_LMAC_DW35[i].mask) ? 1 : 0);
-+		else
-+			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW35[i].name,
-+				(dw_value & WTBL_LMAC_DW35[i].mask) >>
-+					WTBL_LMAC_DW35[i].shift);
-+		i++;
-+	}
-+}
-+
-+static void parse_fmac_lwtbl_rx_stats(struct seq_file *s, u8 *lwtbl)
-+{
-+	parse_fmac_lwtbl_dw33(s, lwtbl);
-+	parse_fmac_lwtbl_dw34(s, lwtbl);
-+	parse_fmac_lwtbl_dw35(s, lwtbl);
-+}
-+
-+static void parse_fmac_lwtbl_mlo_info(struct seq_file *s, u8 *lwtbl)
-+{
-+	parse_fmac_lwtbl_dw28(s, lwtbl);
-+	parse_fmac_lwtbl_dw29(s, lwtbl);
-+	parse_fmac_lwtbl_dw30(s, lwtbl);
-+}
-+
-+static const struct berse_wtbl_parse WTBL_UMAC_DW9[] = {
-+	{"RELATED_IDX0",	WF_UWTBL_RELATED_IDX0_MASK,		WF_UWTBL_RELATED_IDX0_SHIFT,	false},
-+	{"RELATED_BAND0",	WF_UWTBL_RELATED_BAND0_MASK,		WF_UWTBL_RELATED_BAND0_SHIFT,	false},
-+	{"PRI_MLD_BAND",    WF_UWTBL_PRIMARY_MLD_BAND_MASK,		WF_UWTBL_PRIMARY_MLD_BAND_SHIFT,	true},
-+	{"RELATED_IDX1",	WF_UWTBL_RELATED_IDX1_MASK,		WF_UWTBL_RELATED_IDX1_SHIFT,	false},
-+	{"RELATED_BAND1",   WF_UWTBL_RELATED_BAND1_MASK,		WF_UWTBL_RELATED_BAND1_SHIFT,	false},
-+	{"SEC_MLD_BAND",	WF_UWTBL_SECONDARY_MLD_BAND_MASK,	WF_UWTBL_SECONDARY_MLD_BAND_SHIFT,	true},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_uwtbl_mlo_info(struct seq_file *s, u8 *uwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "MldAddr: %02x:%02x:%02x:%02x:%02x:%02x(D0[B0~15], D1[B0~31])\n",
-+		uwtbl[4], uwtbl[5], uwtbl[6], uwtbl[7], uwtbl[0], uwtbl[1]);
-+
-+	/* UMAC WTBL DW 0 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "UWTBL DW 0\n");
-+	addr = (u32 *)&(uwtbl[WF_UWTBL_OWN_MLD_ID_DW*4]);
-+	dw_value = *addr;
-+
-+	seq_printf(s, "\t%s:%u\n", "OMLD_ID",
-+		(dw_value & WF_UWTBL_OWN_MLD_ID_MASK) >> WF_UWTBL_OWN_MLD_ID_SHIFT);
-+
-+	/* UMAC WTBL DW 9 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "UWTBL DW 9\n");
-+	addr = (u32 *)&(uwtbl[WF_UWTBL_RELATED_IDX0_DW*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_UMAC_DW9[i].name) {
-+
-+		if (WTBL_UMAC_DW9[i].shift == NO_SHIFT_DEFINE)
-+			seq_printf(s, "\t%s:%d\n", WTBL_UMAC_DW9[i].name,
-+				(dw_value & WTBL_UMAC_DW9[i].mask) ? 1 : 0);
-+		else
-+			seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW9[i].name,
-+				 (dw_value & WTBL_UMAC_DW9[i].mask) >>
-+					WTBL_UMAC_DW9[i].shift);
-+		i++;
-+	}
-+}
-+
-+static bool
-+is_wtbl_bigtk_exist(u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+
-+	addr = (u32 *)&(lwtbl[WF_LWTBL_MUAR_DW*4]);
-+	dw_value = *addr;
-+	if (((dw_value & WF_LWTBL_MUAR_MASK) >> WF_LWTBL_MUAR_SHIFT) ==
-+					MUAR_INDEX_OWN_MAC_ADDR_BC_MC) {
-+		addr = (u32 *)&(lwtbl[WF_LWTBL_CIPHER_SUIT_BIGTK_DW*4]);
-+		dw_value = *addr;
-+		if (((dw_value & WF_LWTBL_CIPHER_SUIT_BIGTK_MASK) >>
-+			WF_LWTBL_CIPHER_SUIT_BIGTK_SHIFT) != IGTK_CIPHER_SUIT_NONE)
-+			return true;
-+	}
-+
-+	return false;
-+}
-+
-+static const struct berse_wtbl_parse WTBL_UMAC_DW2[] = {
-+	{"PN0",		WTBL_PN0_MASK,		WTBL_PN0_OFFSET,	false},
-+	{"PN1",		WTBL_PN1_MASK,		WTBL_PN1_OFFSET,	false},
-+	{"PN2",		WTBL_PN2_MASK,		WTBL_PN2_OFFSET,	true},
-+	{"PN3",		WTBL_PN3_MASK,		WTBL_PN3_OFFSET,	false},
-+	{NULL,}
-+};
-+
-+static const struct berse_wtbl_parse WTBL_UMAC_DW3[] = {
-+	{"PN4",     WTBL_PN4_MASK,      WTBL_PN4_OFFSET,	false},
-+	{"PN5",     WTBL_PN5_MASK,      WTBL_PN5_OFFSET,	true},
-+	{"COM_SN",     WF_UWTBL_COM_SN_MASK,     WF_UWTBL_COM_SN_SHIFT,	true},
-+	{NULL,}
-+};
-+
-+static const struct berse_wtbl_parse WTBL_UMAC_DW4_BIPN[] = {
-+	{"BIPN0",	WTBL_BIPN0_MASK,	WTBL_BIPN0_OFFSET,	false},
-+	{"BIPN1",	WTBL_BIPN1_MASK,	WTBL_BIPN1_OFFSET,	false},
-+	{"BIPN2",	WTBL_BIPN2_MASK,	WTBL_BIPN2_OFFSET,	true},
-+	{"BIPN3",	WTBL_BIPN3_MASK,	WTBL_BIPN3_OFFSET,	false},
-+	{NULL,}
-+};
-+
-+static const struct berse_wtbl_parse WTBL_UMAC_DW5_BIPN[] = {
-+	{"BIPN4",	WTBL_BIPN4_MASK,	WTBL_BIPN4_OFFSET,	false},
-+	{"BIPN5",	WTBL_BIPN5_MASK,	WTBL_BIPN5_OFFSET,	true},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_uwtbl_pn(struct seq_file *s, u8 *uwtbl, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u16 i = 0;
-+
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "UWTBL PN\n");
-+
-+	/* UMAC WTBL DW 2/3 */
-+	addr = (u32 *)&(uwtbl[WF_UWTBL_PN_31_0__DW*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_UMAC_DW2[i].name) {
-+		seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW2[i].name,
-+			(dw_value & WTBL_UMAC_DW2[i].mask) >>
-+				WTBL_UMAC_DW2[i].shift);
-+		i++;
-+	}
-+
-+	i = 0;
-+	addr = (u32 *)&(uwtbl[WF_UWTBL_PN_47_32__DW*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_UMAC_DW3[i].name) {
-+		seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW3[i].name,
-+			 (dw_value & WTBL_UMAC_DW3[i].mask) >>
-+			WTBL_UMAC_DW3[i].shift);
-+		i++;
-+	}
-+
-+
-+	/* UMAC WTBL DW 4/5 for BIGTK */
-+	if (is_wtbl_bigtk_exist(lwtbl) == true) {
-+		i = 0;
-+		addr = (u32 *)&(uwtbl[WF_UWTBL_RX_BIPN_31_0__DW*4]);
-+		dw_value = *addr;
-+
-+		while (WTBL_UMAC_DW4_BIPN[i].name) {
-+			seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW4_BIPN[i].name,
-+				(dw_value & WTBL_UMAC_DW4_BIPN[i].mask) >>
-+					WTBL_UMAC_DW4_BIPN[i].shift);
-+			i++;
-+		}
-+
-+		i = 0;
-+		addr = (u32 *)&(uwtbl[WF_UWTBL_RX_BIPN_47_32__DW*4]);
-+		dw_value = *addr;
-+
-+		while (WTBL_UMAC_DW5_BIPN[i].name) {
-+			seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW5_BIPN[i].name,
-+				(dw_value & WTBL_UMAC_DW5_BIPN[i].mask) >>
-+				WTBL_UMAC_DW5_BIPN[i].shift);
-+			i++;
-+		}
-+	}
-+}
-+
-+static void parse_fmac_uwtbl_sn(struct seq_file *s, u8 *uwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 u2SN = 0;
-+
-+	/* UMAC WTBL DW SN part */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "UWTBL SN\n");
-+
-+	addr = (u32 *)&(uwtbl[WF_UWTBL_TID0_SN_DW*4]);
-+	u2SN = ((*addr) & WF_UWTBL_TID0_SN_MASK) >> WF_UWTBL_TID0_SN_SHIFT;
-+	seq_printf(s, "\t%s:%u\n", "TID0_AC0_SN", u2SN);
-+
-+	addr = (u32 *)&(uwtbl[WF_UWTBL_TID1_SN_DW*4]);
-+	u2SN = ((*addr) & WF_UWTBL_TID1_SN_MASK) >> WF_UWTBL_TID1_SN_SHIFT;
-+	seq_printf(s, "\t%s:%u\n", "TID1_AC1_SN", u2SN);
-+
-+	addr = (u32 *)&(uwtbl[WF_UWTBL_TID2_SN_7_0__DW*4]);
-+	u2SN = ((*addr) & WF_UWTBL_TID2_SN_7_0__MASK) >>
-+				WF_UWTBL_TID2_SN_7_0__SHIFT;
-+	addr = (u32 *)&(uwtbl[WF_UWTBL_TID2_SN_11_8__DW*4]);
-+	u2SN |= (((*addr) & WF_UWTBL_TID2_SN_11_8__MASK) >>
-+			WF_UWTBL_TID2_SN_11_8__SHIFT) << 8;
-+	seq_printf(s, "\t%s:%u\n", "TID2_AC2_SN", u2SN);
-+
-+	addr = (u32 *)&(uwtbl[WF_UWTBL_TID3_SN_DW*4]);
-+	u2SN = ((*addr) & WF_UWTBL_TID3_SN_MASK) >> WF_UWTBL_TID3_SN_SHIFT;
-+	seq_printf(s, "\t%s:%u\n", "TID3_AC3_SN", u2SN);
-+
-+	addr = (u32 *)&(uwtbl[WF_UWTBL_TID4_SN_DW*4]);
-+	u2SN = ((*addr) & WF_UWTBL_TID4_SN_MASK) >> WF_UWTBL_TID4_SN_SHIFT;
-+	seq_printf(s, "\t%s:%u\n", "TID4_SN", u2SN);
-+
-+	addr = (u32 *)&(uwtbl[WF_UWTBL_TID5_SN_3_0__DW*4]);
-+	u2SN = ((*addr) & WF_UWTBL_TID5_SN_3_0__MASK) >>
-+				WF_UWTBL_TID5_SN_3_0__SHIFT;
-+	addr = (u32 *)&(uwtbl[WF_UWTBL_TID5_SN_11_4__DW*4]);
-+	u2SN |= (((*addr) & WF_UWTBL_TID5_SN_11_4__MASK) >>
-+				WF_UWTBL_TID5_SN_11_4__SHIFT) << 4;
-+	seq_printf(s, "\t%s:%u\n", "TID5_SN", u2SN);
-+
-+	addr = (u32 *)&(uwtbl[WF_UWTBL_TID6_SN_DW*4]);
-+	u2SN = ((*addr) & WF_UWTBL_TID6_SN_MASK) >> WF_UWTBL_TID6_SN_SHIFT;
-+	seq_printf(s, "\t%s:%u\n", "TID6_SN", u2SN);
-+
-+	addr = (u32 *)&(uwtbl[WF_UWTBL_TID7_SN_DW*4]);
-+	u2SN = ((*addr) & WF_UWTBL_TID7_SN_MASK) >> WF_UWTBL_TID7_SN_SHIFT;
-+	seq_printf(s, "\t%s:%u\n", "TID7_SN", u2SN);
-+
-+	addr = (u32 *)&(uwtbl[WF_UWTBL_COM_SN_DW*4]);
-+	u2SN = ((*addr) & WF_UWTBL_COM_SN_MASK) >> WF_UWTBL_COM_SN_SHIFT;
-+	seq_printf(s, "\t%s:%u\n", "COM_SN", u2SN);
-+}
-+
-+static void dump_key_table(
-+	struct seq_file *s,
-+	uint16_t keyloc0,
-+	uint16_t keyloc1,
-+	uint16_t keyloc2
-+)
-+{
-+#define ONE_KEY_ENTRY_LEN_IN_DW                8
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	u8 keytbl[ONE_KEY_ENTRY_LEN_IN_DW*4] = {0};
-+	uint16_t x;
-+
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "\t%s:%d\n", "keyloc0", keyloc0);
-+	if (keyloc0 != INVALID_KEY_ENTRY) {
-+
-+		/* Don't swap below two lines, halWtblReadRaw will
-+		* write new value WF_WTBLON_TOP_WDUCR_ADDR
-+		*/
-+		mt7996_wtbl_read_raw(dev, keyloc0,
-+			WTBL_TYPE_KEY, 0, ONE_KEY_ENTRY_LEN_IN_DW, keytbl);
-+		seq_printf(s, "\t\tKEY WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
-+			MT_DBG_UWTBL_TOP_WDUCR_ADDR,
-+			mt76_rr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR),
-+			KEYTBL_IDX2BASE(keyloc0, 0));
-+		for (x = 0; x < ONE_KEY_ENTRY_LEN_IN_DW; x++) {
-+			seq_printf(s, "\t\tDW%02d: %02x %02x %02x %02x\n",
-+				x,
-+				keytbl[x * 4 + 3],
-+				keytbl[x * 4 + 2],
-+				keytbl[x * 4 + 1],
-+				keytbl[x * 4]);
-+		}
-+	}
-+
-+	seq_printf(s, "\t%s:%d\n", "keyloc1", keyloc1);
-+	if (keyloc1 != INVALID_KEY_ENTRY) {
-+		/* Don't swap below two lines, halWtblReadRaw will
-+		* write new value WF_WTBLON_TOP_WDUCR_ADDR
-+		*/
-+		mt7996_wtbl_read_raw(dev, keyloc1,
-+			WTBL_TYPE_KEY, 0, ONE_KEY_ENTRY_LEN_IN_DW, keytbl);
-+		seq_printf(s, "\t\tKEY WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
-+			MT_DBG_UWTBL_TOP_WDUCR_ADDR,
-+			mt76_rr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR),
-+			KEYTBL_IDX2BASE(keyloc1, 0));
-+		for (x = 0; x < ONE_KEY_ENTRY_LEN_IN_DW; x++) {
-+			seq_printf(s, "\t\tDW%02d: %02x %02x %02x %02x\n",
-+				x,
-+				keytbl[x * 4 + 3],
-+				keytbl[x * 4 + 2],
-+				keytbl[x * 4 + 1],
-+				keytbl[x * 4]);
-+		}
-+	}
-+
-+	seq_printf(s, "\t%s:%d\n", "keyloc2", keyloc2);
-+	if (keyloc2 != INVALID_KEY_ENTRY) {
-+		/* Don't swap below two lines, halWtblReadRaw will
-+		* write new value WF_WTBLON_TOP_WDUCR_ADDR
-+		*/
-+		mt7996_wtbl_read_raw(dev, keyloc2,
-+			WTBL_TYPE_KEY, 0, ONE_KEY_ENTRY_LEN_IN_DW, keytbl);
-+		seq_printf(s, "\t\tKEY WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
-+			MT_DBG_UWTBL_TOP_WDUCR_ADDR,
-+			mt76_rr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR),
-+			KEYTBL_IDX2BASE(keyloc2, 0));
-+		for (x = 0; x < ONE_KEY_ENTRY_LEN_IN_DW; x++) {
-+			seq_printf(s, "\t\tDW%02d: %02x %02x %02x %02x\n",
-+				x,
-+				keytbl[x * 4 + 3],
-+				keytbl[x * 4 + 2],
-+				keytbl[x * 4 + 1],
-+				keytbl[x * 4]);
-+		}
-+	}
-+}
-+
-+static void parse_fmac_uwtbl_key_info(struct seq_file *s, u8 *uwtbl, u8 *lwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	uint16_t keyloc0 = INVALID_KEY_ENTRY;
-+	uint16_t keyloc1 = INVALID_KEY_ENTRY;
-+	uint16_t keyloc2 = INVALID_KEY_ENTRY;
-+
-+	/* UMAC WTBL DW 7 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "UWTBL key info\n");
-+
-+	addr = (u32 *)&(uwtbl[WF_UWTBL_KEY_LOC0_DW*4]);
-+	dw_value = *addr;
-+	keyloc0 = (dw_value & WF_UWTBL_KEY_LOC0_MASK) >> WF_UWTBL_KEY_LOC0_SHIFT;
-+	keyloc1 = (dw_value & WF_UWTBL_KEY_LOC1_MASK) >> WF_UWTBL_KEY_LOC1_SHIFT;
-+
-+	seq_printf(s, "\t%s:%u/%u\n", "Key Loc 0/1", keyloc0, keyloc1);
-+
-+	/* UMAC WTBL DW 6 for BIGTK */
-+	if (is_wtbl_bigtk_exist(lwtbl) == true) {
-+		addr = (u32 *)&(uwtbl[WF_UWTBL_KEY_LOC2_DW*4]);
-+		dw_value = *addr;
-+		keyloc2 = (dw_value & WF_UWTBL_KEY_LOC2_MASK) >>
-+			WF_UWTBL_KEY_LOC2_SHIFT;
-+		seq_printf(s, "\t%s:%u\n", "Key Loc 2", keyloc2);
-+	}
-+
-+	/* Parse KEY link */
-+	dump_key_table(s, keyloc0, keyloc1, keyloc2);
-+}
-+
-+static const struct berse_wtbl_parse WTBL_UMAC_DW8[] = {
-+	{"UWTBL_WMM_Q",		WF_UWTBL_WMM_Q_MASK,		WF_UWTBL_WMM_Q_SHIFT,	false},
-+	{"UWTBL_QOS",		WF_UWTBL_QOS_MASK,		NO_SHIFT_DEFINE,	false},
-+	{"UWTBL_HT_VHT_HE",	WF_UWTBL_HT_MASK,		NO_SHIFT_DEFINE,	false},
-+	{"UWTBL_HDRT_MODE",	WF_UWTBL_HDRT_MODE_MASK,	NO_SHIFT_DEFINE,	true},
-+	{NULL,}
-+};
-+
-+static void parse_fmac_uwtbl_msdu_info(struct seq_file *s, u8 *uwtbl)
-+{
-+	u32 *addr = 0;
-+	u32 dw_value = 0;
-+	u32 amsdu_len = 0;
-+	u16 i = 0;
-+
-+	/* UMAC WTBL DW 8 */
-+	seq_printf(s, "\t\n");
-+	seq_printf(s, "UWTBL DW8\n");
-+
-+	addr = (u32 *)&(uwtbl[WF_UWTBL_AMSDU_CFG_DW*4]);
-+	dw_value = *addr;
-+
-+	while (WTBL_UMAC_DW8[i].name) {
-+
-+		if (WTBL_UMAC_DW8[i].shift == NO_SHIFT_DEFINE)
-+			seq_printf(s, "\t%s:%d\n", WTBL_UMAC_DW8[i].name,
-+				(dw_value & WTBL_UMAC_DW8[i].mask) ? 1 : 0);
-+		else
-+			seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW8[i].name,
-+				(dw_value & WTBL_UMAC_DW8[i].mask) >>
-+					WTBL_UMAC_DW8[i].shift);
-+		i++;
-+	}
-+
-+	/* UMAC WTBL DW 8 - SEC_ADDR_MODE */
-+	addr = (u32 *)&(uwtbl[WF_UWTBL_SEC_ADDR_MODE_DW*4]);
-+	dw_value = *addr;
-+	seq_printf(s, "\t%s:%lu\n", "SEC_ADDR_MODE",
-+		(dw_value & WTBL_SEC_ADDR_MODE_MASK) >> WTBL_SEC_ADDR_MODE_OFFSET);
-+
-+	/* UMAC WTBL DW 8 - AMSDU_CFG */
-+	seq_printf(s, "\t%s:%d\n", "HW AMSDU Enable",
-+				(dw_value & WTBL_AMSDU_EN_MASK) ? 1 : 0);
-+
-+	amsdu_len = (dw_value & WTBL_AMSDU_LEN_MASK) >> WTBL_AMSDU_LEN_OFFSET;
-+	if (amsdu_len == 0)
-+		seq_printf(s, "\t%s:invalid (WTBL value=0x%x)\n", "HW AMSDU Len",
-+			amsdu_len);
-+	else if (amsdu_len == 1)
-+		seq_printf(s, "\t%s:%d~%d (WTBL value=0x%x)\n", "HW AMSDU Len",
-+			1,
-+			255,
-+			amsdu_len);
-+	else if (amsdu_len == 2)
-+		seq_printf(s, "\t%s:%d~%d (WTBL value=0x%x)\n", "HW AMSDU Len",
-+			256,
-+			511,
-+			amsdu_len);
-+	else if (amsdu_len == 3)
-+		seq_printf(s, "\t%s:%d~%d (WTBL value=0x%x)\n", "HW AMSDU Len",
-+			512,
-+			767,
-+			amsdu_len);
-+	else
-+		seq_printf(s, "\t%s:%d~%d (WTBL value=0x%x)\n", "HW AMSDU Len",
-+			256 * (amsdu_len - 1),
-+			256 * (amsdu_len - 1) + 255,
-+			amsdu_len);
-+
-+	seq_printf(s, "\t%s:%lu (WTBL value=0x%lx)\n", "HW AMSDU Num",
-+		((dw_value & WTBL_AMSDU_NUM_MASK) >> WTBL_AMSDU_NUM_OFFSET) + 1,
-+		(dw_value & WTBL_AMSDU_NUM_MASK) >> WTBL_AMSDU_NUM_OFFSET);
-+}
-+
-+static int mt7996_wtbl_read(struct seq_file *s, void *data)
-+{
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	u8 lwtbl[LWTBL_LEN_IN_DW * 4] = {0};
-+	u8 uwtbl[UWTBL_LEN_IN_DW * 4] = {0};
-+	int x;
-+
-+	mt7996_wtbl_read_raw(dev, dev->wlan_idx, WTBL_TYPE_LMAC, 0,
-+				 LWTBL_LEN_IN_DW, lwtbl);
-+	seq_printf(s, "Dump WTBL info of WLAN_IDX:%d\n", dev->wlan_idx);
-+	seq_printf(s, "LMAC WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
-+		   MT_DBG_WTBLON_TOP_WDUCR_ADDR,
-+		   mt76_rr(dev, MT_DBG_WTBLON_TOP_WDUCR_ADDR),
-+		   LWTBL_IDX2BASE(dev->wlan_idx, 0));
-+	for (x = 0; x < LWTBL_LEN_IN_DW; x++) {
-+		seq_printf(s, "DW%02d: %02x %02x %02x %02x\n",
-+			   x,
-+			   lwtbl[x * 4 + 3],
-+			   lwtbl[x * 4 + 2],
-+			   lwtbl[x * 4 + 1],
-+			   lwtbl[x * 4]);
-+	}
-+
-+	/* Parse LWTBL */
-+	parse_fmac_lwtbl_dw0_1(s, lwtbl);
-+	parse_fmac_lwtbl_dw2(s, lwtbl);
-+	parse_fmac_lwtbl_dw3(s, lwtbl);
-+	parse_fmac_lwtbl_dw4(s, lwtbl);
-+	parse_fmac_lwtbl_dw5(s, lwtbl);
-+	parse_fmac_lwtbl_dw6(s, lwtbl);
-+	parse_fmac_lwtbl_dw7(s, lwtbl);
-+	parse_fmac_lwtbl_dw8(s, lwtbl);
-+	parse_fmac_lwtbl_dw9(s, lwtbl);
-+	parse_fmac_lwtbl_dw10(s, lwtbl);
-+	parse_fmac_lwtbl_dw11(s, lwtbl);
-+	parse_fmac_lwtbl_dw12(s, lwtbl);
-+	parse_fmac_lwtbl_dw13(s, lwtbl);
-+	parse_fmac_lwtbl_dw14(s, lwtbl);
-+	parse_fmac_lwtbl_mlo_info(s, lwtbl);
-+	parse_fmac_lwtbl_dw31(s, lwtbl);
-+	parse_fmac_lwtbl_dw32(s, lwtbl);
-+	parse_fmac_lwtbl_rx_stats(s, lwtbl);
-+
-+	mt7996_wtbl_read_raw(dev, dev->wlan_idx, WTBL_TYPE_UMAC, 0,
-+				 UWTBL_LEN_IN_DW, uwtbl);
-+	seq_printf(s, "Dump WTBL info of WLAN_IDX:%d\n", dev->wlan_idx);
-+	seq_printf(s, "UMAC WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
-+		   MT_DBG_UWTBL_TOP_WDUCR_ADDR,
-+		   mt76_rr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR),
-+		   UWTBL_IDX2BASE(dev->wlan_idx, 0));
-+	for (x = 0; x < UWTBL_LEN_IN_DW; x++) {
-+		seq_printf(s, "DW%02d: %02x %02x %02x %02x\n",
-+			   x,
-+			   uwtbl[x * 4 + 3],
-+			   uwtbl[x * 4 + 2],
-+			   uwtbl[x * 4 + 1],
-+			   uwtbl[x * 4]);
-+	}
-+
-+	/* Parse UWTBL */
-+	parse_fmac_uwtbl_mlo_info(s, uwtbl);
-+	parse_fmac_uwtbl_pn(s, uwtbl, lwtbl);
-+	parse_fmac_uwtbl_sn(s, uwtbl);
-+	parse_fmac_uwtbl_key_info(s, uwtbl, lwtbl);
-+	parse_fmac_uwtbl_msdu_info(s, uwtbl);
-+
-+	return 0;
-+}
-+
-+static int mt7996_sta_info(struct seq_file *s, void *data)
-+{
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	u8 lwtbl[LWTBL_LEN_IN_DW*4] = {0};
-+	u16 i = 0;
-+
-+	for (i=0; i < mt7996_wtbl_size(dev); i++) {
-+		mt7996_wtbl_read_raw(dev, i, WTBL_TYPE_LMAC, 0,
-+				     LWTBL_LEN_IN_DW, lwtbl);
-+
-+		if (lwtbl[4] || lwtbl[5] || lwtbl[6] || lwtbl[7] || lwtbl[0] || lwtbl[1]) {
-+			u32 *addr, dw_value;
-+
-+			seq_printf(s, "wcid:%d\tAddr: %02x:%02x:%02x:%02x:%02x:%02x",
-+					i, lwtbl[4], lwtbl[5], lwtbl[6], lwtbl[7], lwtbl[0], lwtbl[1]);
-+
-+			addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_2*4]);
-+			dw_value = *addr;
-+			seq_printf(s, "\t%s:%u", WTBL_LMAC_DW2[0].name,
-+					(dw_value & WTBL_LMAC_DW2[0].mask) >> WTBL_LMAC_DW2[0].shift);
-+
-+			addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_5*4]);
-+			dw_value = *addr;
-+			seq_printf(s, "\tPSM:%u\n", !!(dw_value & WF_LWTBL_PSM_MASK));
-+		}
-+	}
-+
-+	return 0;
-+}
-+
-+static int mt7996_token_read(struct seq_file *s, void *data)
-+{
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	int msdu_id;
-+	struct mt76_txwi_cache *txwi;
-+
-+	seq_printf(s, "Token from host:\n");
-+	spin_lock_bh(&dev->mt76.token_lock);
-+	idr_for_each_entry(&dev->mt76.token, txwi, msdu_id) {
-+		seq_printf(s, "%4d (pending time %u ms)\n", msdu_id,
-+			   jiffies_to_msecs(jiffies - txwi->jiffies));
-+	}
-+	spin_unlock_bh(&dev->mt76.token_lock);
-+	seq_printf(s, "\n");
-+
-+	return 0;
-+}
-+
-+int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	u32 device_id = (dev->mt76.rev) >> 16;
-+	int i = 0;
-+	static const struct mt7996_dbg_reg_desc dbg_reg_s[] = {
-+		{ 0x7990, mt7996_dbg_offs },
-+		{ 0x7992, mt7992_dbg_offs },
-+	};
-+
-+	for (i = 0; i < ARRAY_SIZE(dbg_reg_s); i++) {
-+		if (device_id == dbg_reg_s[i].id) {
-+			dev->dbg_reg = &dbg_reg_s[i];
-+			break;
-+		}
-+	}
-+
-+	if (is_mt7996(&dev->mt76)) {
-+		WTBL_LMAC_DW2 = WTBL_LMAC_DW2_7996;
-+		WTBL_LMAC_DW5 = WTBL_LMAC_DW5_7996;
-+		WTBL_LMAC_DW9 = WTBL_LMAC_DW9_7996;
-+	} else {
-+		WTBL_LMAC_DW2 = WTBL_LMAC_DW2_7992;
-+		WTBL_LMAC_DW5 = WTBL_LMAC_DW5_7992;
-+		WTBL_LMAC_DW9 = WTBL_LMAC_DW9_7992;
-+	}
-+
-+	/* agg */
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "agg_info0", dir,
-+				    mt7996_agginfo_read_band0);
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "agg_info1", dir,
-+				    mt7996_agginfo_read_band1);
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "agg_info2", dir,
-+				    mt7996_agginfo_read_band2);
-+	/* amsdu */
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "amsdu_info", dir,
-+				    mt7996_amsdu_result_read);
-+
-+	debugfs_create_file("fw_debug_module", 0600, dir, dev,
-+			    &fops_fw_debug_module);
-+	debugfs_create_file("fw_debug_level", 0600, dir, dev,
-+			    &fops_fw_debug_level);
-+	debugfs_create_file("fw_wa_query", 0600, dir, dev, &fops_wa_query);
-+	debugfs_create_file("fw_wa_set", 0600, dir, dev, &fops_wa_set);
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "fw_version", dir,
-+				    mt7996_dump_version);
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "fw_wa_info", dir,
-+				    mt7996_fw_wa_info_read);
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "fw_wm_info", dir,
-+				    mt7996_fw_wm_info_read);
-+
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "mib_info0", dir,
-+				    mt7996_mibinfo_band0);
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "mib_info1", dir,
-+				    mt7996_mibinfo_band1);
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "mib_info2", dir,
-+				    mt7996_mibinfo_band2);
-+
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "sta_info", dir,
-+				    mt7996_sta_info);
-+
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "tr_info", dir,
-+				    mt7996_trinfo_read);
-+
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "wtbl_info", dir,
-+				    mt7996_wtbl_read);
-+
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "token", dir, mt7996_token_read);
-+
-+	debugfs_create_u8("sku_disable", 0600, dir, &dev->dbg.sku_disable);
-+
-+	return 0;
-+}
-+
-+#endif
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-new file mode 100644
-index 0000000..c16b25a
---- /dev/null
-+++ b/mt7996/mtk_mcu.c
-@@ -0,0 +1,39 @@
-+// SPDX-License-Identifier: ISC
-+/*
-+ * Copyright (C) 2023 MediaTek Inc.
-+ */
-+
-+#include <linux/firmware.h>
-+#include <linux/fs.h>
-+#include "mt7996.h"
-+#include "mcu.h"
-+#include "mac.h"
-+#include "mtk_mcu.h"
-+
-+#ifdef CONFIG_MTK_DEBUG
-+
-+
-+
-+
-+int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val)
-+{
-+	struct {
-+		u8 __rsv1[4];
-+
-+		__le16 tag;
-+		__le16 len;
-+
-+		__le16 item;
-+		u8 __rsv2[2];
-+		__le32 value;
-+	} __packed req = {
-+		.tag = cpu_to_le16(UNI_CMD_MURU_DBG_INFO),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.item = cpu_to_le16(item),
-+		.value = cpu_to_le32(val),
-+	};
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
-+				 sizeof(req), true);
-+}
-+#endif
-diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
-new file mode 100644
-index 0000000..7f4d4e0
---- /dev/null
-+++ b/mt7996/mtk_mcu.h
-@@ -0,0 +1,19 @@
-+/* SPDX-License-Identifier: ISC */
-+/*
-+ * Copyright (C) 2023 MediaTek Inc.
-+ */
-+
-+#ifndef __MT7996_MTK_MCU_H
-+#define __MT7996_MTK_MCU_H
-+
-+#include "../mt76_connac_mcu.h"
-+
-+#ifdef CONFIG_MTK_DEBUG
-+
-+enum {
-+	UNI_CMD_MURU_DBG_INFO = 0x18,
-+};
-+
-+#endif
-+
-+#endif
-diff --git a/tools/fwlog.c b/tools/fwlog.c
-index e5d4a10..3c6a61d 100644
---- a/tools/fwlog.c
-+++ b/tools/fwlog.c
-@@ -26,7 +26,7 @@ static const char *debugfs_path(const char *phyname, const char *file)
- 	return path;
- }
- 
--static int mt76_set_fwlog_en(const char *phyname, bool en)
-+static int mt76_set_fwlog_en(const char *phyname, bool en, char *val)
- {
- 	FILE *f = fopen(debugfs_path(phyname, "fw_debug_bin"), "w");
- 
-@@ -35,7 +35,13 @@ static int mt76_set_fwlog_en(const char *phyname, bool en)
- 		return 1;
- 	}
- 
--	fprintf(f, "7");
-+	if (en && val)
-+		fprintf(f, "%s", val);
-+	else if (en)
-+		fprintf(f, "7");
-+	else
-+		fprintf(f, "0");
-+
- 	fclose(f);
- 
- 	return 0;
-@@ -76,6 +82,7 @@ static void handle_signal(int sig)
- 
- int mt76_fwlog(const char *phyname, int argc, char **argv)
- {
-+#define BUF_SIZE 1504
- 	struct sockaddr_in local = {
- 		.sin_family = AF_INET,
- 		.sin_addr.s_addr = INADDR_ANY,
-@@ -84,9 +91,9 @@ int mt76_fwlog(const char *phyname, int argc, char **argv)
- 		.sin_family = AF_INET,
- 		.sin_port = htons(55688),
- 	};
--	char buf[1504];
-+	char *buf = calloc(BUF_SIZE, sizeof(char));
- 	int ret = 0;
--	int yes = 1;
-+	/* int yes = 1; */
- 	int s, fd;
- 
- 	if (argc < 1) {
-@@ -105,13 +112,13 @@ int mt76_fwlog(const char *phyname, int argc, char **argv)
- 		return 1;
- 	}
- 
--	setsockopt(s, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));
-+	/* setsockopt(s, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes)); */
- 	if (bind(s, (struct sockaddr *)&local, sizeof(local)) < 0) {
- 		perror("bind");
- 		return 1;
- 	}
- 
--	if (mt76_set_fwlog_en(phyname, true))
-+	if (mt76_set_fwlog_en(phyname, true, argv[1]))
- 		return 1;
- 
- 	fd = open(debugfs_path(phyname, "fwlog_data"), O_RDONLY);
-@@ -145,8 +152,8 @@ int mt76_fwlog(const char *phyname, int argc, char **argv)
- 		if (!r)
- 			continue;
- 
--		if (len > sizeof(buf)) {
--			fprintf(stderr, "Length error: %d > %d\n", len, (int)sizeof(buf));
-+		if (len > BUF_SIZE) {
-+			fprintf(stderr, "Length error: %d > %d\n", len, BUF_SIZE);
- 			ret = 1;
- 			break;
- 		}
-@@ -171,7 +178,7 @@ int mt76_fwlog(const char *phyname, int argc, char **argv)
- 	close(fd);
- 
- out:
--	mt76_set_fwlog_en(phyname, false);
-+	mt76_set_fwlog_en(phyname, false, NULL);
- 
- 	return ret;
- }
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0020-mtk-mt76-mt7996-ACS-channel-time-too-long-on-duty-ch.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0020-mtk-mt76-mt7996-ACS-channel-time-too-long-on-duty-ch.patch
new file mode 100644
index 0000000..81fecd3
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0020-mtk-mt76-mt7996-ACS-channel-time-too-long-on-duty-ch.patch
@@ -0,0 +1,66 @@
+From 1879c122435092c63eb0e8156f6e75d8e147fa13 Mon Sep 17 00:00:00 2001
+From: "fancy.liu" <fancy.liu@mediatek.com>
+Date: Tue, 14 Nov 2023 10:13:24 +0800
+Subject: [PATCH 020/199] mtk: mt76: mt7996: ACS channel time too long on duty
+ channel
+
+Step and issue:
+1. Set channel to 36 in hostapd config;
+2. Bootup;
+3. Enable ACS through UCI command and reload;
+4. Check hostapd log, channel 36 channel_time is much longer than other channels.
+
+Root cause:
+The reset chan_stat condition missed duty channel.
+
+Solution:
+When scan start, need to reset chan_stat in each channel switch.
+
+Issue:
+There's a chance that the channel time for duty channel is zero in ACS
+scan.
+
+Root cause:
+The chan_stat may be reset when restore to duty channel.
+Mac80211 will notify to hostapd when scan done and then restore to duty
+channel.
+And mt76 will clear scan flag after restore done.
+If hostapd get the chan_stat before channel_restore, will get the
+correct channel time;
+If hostapd get the chan_stat after channel_restore, will get zero
+channel time;
+
+Solution:
+When channel switch, will check the mac80211 scan state but not the mt76 scan flag.
+Mac80211 scan state will be set in scanning, and will be reset after
+scan done and before restore to duty channel.
+
+Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
+---
+ mac80211.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index 6d47b26d..9df9109f 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -928,6 +928,7 @@ void mt76_set_channel(struct mt76_phy *phy)
+ 	struct cfg80211_chan_def *chandef = &hw->conf.chandef;
+ 	bool offchannel = hw->conf.flags & IEEE80211_CONF_OFFCHANNEL;
+ 	int timeout = HZ / 5;
++	unsigned long was_scanning = ieee80211_get_scanning(hw);
+ 
+ 	wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(phy), timeout);
+ 	mt76_update_survey(phy);
+@@ -942,7 +943,7 @@ void mt76_set_channel(struct mt76_phy *phy)
+ 	if (!offchannel)
+ 		phy->main_chan = chandef->chan;
+ 
+-	if (chandef->chan != phy->main_chan)
++	if (chandef->chan != phy->main_chan || was_scanning)
+ 		memset(phy->chan_state, 0, sizeof(*phy->chan_state));
+ }
+ EXPORT_SYMBOL_GPL(mt76_set_channel);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0020-mtk-wifi-mt76-mt7996-add-check-for-hostapd-config-he.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0020-mtk-wifi-mt76-mt7996-add-check-for-hostapd-config-he.patch
deleted file mode 100644
index f02a3eb..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0020-mtk-wifi-mt76-mt7996-add-check-for-hostapd-config-he.patch
+++ /dev/null
@@ -1,63 +0,0 @@
-From 4974096ee9b3e3a6083da610ce13dde3b82608c9 Mon Sep 17 00:00:00 2001
-From: "Allen.Ye" <allen.ye@mediatek.com>
-Date: Thu, 8 Jun 2023 17:32:33 +0800
-Subject: [PATCH 020/116] mtk: wifi: mt76: mt7996: add check for hostapd config
- he_ldpc
-
-Add check for hostapd config he_ldpc.
-This capabilities is checked in mcu_beacon_check_caps in 7915.
-
-Add check for STA LDPC cap, if STA only have BCC we should not overwrite the phy_cap with config he_ldpc.
-
-Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
----
- mt7996/mcu.c | 12 +++++++++---
- 1 file changed, 9 insertions(+), 3 deletions(-)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 90332fe..6c565c9 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -1188,7 +1188,8 @@ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
- }
- 
- static void
--mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
-+mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
-+		      struct ieee80211_sta *sta)
- {
- 	struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
- 	struct ieee80211_he_mcs_nss_supp mcs_map;
-@@ -1208,6 +1209,11 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
- 		he->he_phy_cap[i] = elem->phy_cap_info[i];
- 	}
- 
-+	if (vif->type == NL80211_IFTYPE_AP &&
-+	    (elem->phy_cap_info[1] & IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD))
-+		u8p_replace_bits(&he->he_phy_cap[1], vif->bss_conf.he_ldpc,
-+				 IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD);
-+
- 	mcs_map = sta->deflink.he_cap.he_mcs_nss_supp;
- 	switch (sta->deflink.bandwidth) {
- 	case IEEE80211_STA_RX_BW_160:
-@@ -2113,7 +2119,7 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- 	 * update sta_rec_he here.
- 	 */
- 	if (changed)
--		mt7996_mcu_sta_he_tlv(skb, sta);
-+		mt7996_mcu_sta_he_tlv(skb, vif, sta);
- 
- 	/* sta_rec_ra accommodates BW, NSS and only MCS range format
- 	 * i.e 0-{7,8,9} for VHT.
-@@ -2201,7 +2207,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- 		/* starec amsdu */
- 		mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, sta);
- 		/* starec he */
--		mt7996_mcu_sta_he_tlv(skb, sta);
-+		mt7996_mcu_sta_he_tlv(skb, vif, sta);
- 		/* starec he 6g*/
- 		mt7996_mcu_sta_he_6g_tlv(skb, sta);
- 		/* starec eht */
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0021-mtk-mt76-mt7996-Fixed-null-pointer-dereference-issue.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0021-mtk-mt76-mt7996-Fixed-null-pointer-dereference-issue.patch
new file mode 100644
index 0000000..31a8dd2
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0021-mtk-mt76-mt7996-Fixed-null-pointer-dereference-issue.patch
@@ -0,0 +1,34 @@
+From 7384dd7ed2828f0681ce1d765007d3f1976e8760 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Thu, 26 Oct 2023 10:08:10 +0800
+Subject: [PATCH 021/199] mtk: mt76: mt7996: Fixed null pointer dereference
+ issue
+
+---
+ mt7996/main.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index cbe8b009..72232994 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1083,9 +1083,16 @@ static void mt7996_sta_rc_update(struct ieee80211_hw *hw,
+ 				 struct ieee80211_sta *sta,
+ 				 u32 changed)
+ {
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mt7996_dev *dev = phy->dev;
+ 
++	if (!msta->vif) {
++		dev_warn(dev->mt76.dev, "Un-initialized STA %pM wcid %d in rc_work\n",
++			 sta->addr, msta->wcid.idx);
++		return;
++	}
++
+ 	mt7996_sta_rc_work(&changed, sta);
+ 	ieee80211_queue_work(hw, &dev->rc_work);
+ }
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0021-mtk-wifi-mt76-testmode-add-basic-testmode-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0021-mtk-wifi-mt76-testmode-add-basic-testmode-support.patch
deleted file mode 100644
index 19ea968..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0021-mtk-wifi-mt76-testmode-add-basic-testmode-support.patch
+++ /dev/null
@@ -1,2370 +0,0 @@
-From 501b857b081c10d3400c8b4374db346b238d3f72 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 28 Dec 2022 22:24:25 +0800
-Subject: [PATCH 021/116] mtk: wifi: mt76: testmode: add basic testmode support
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Add testmode eeprom buffer mode support
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Fix power & freq offset issue for iTest power cal & tx/rx verifcation
-1. Wait for fw to tx. Otherwise, iTest testing tool cannot get the
-accurate tx power.
-2. In crystal mode, freq offset is set in 6G band and forwarded to 5G
-and 2G band. Therefore, we should avoid reseting freq offset to 0 when
-6G interface is off.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-edcca return err in testmode; therefore, bypass it when we are in testmode idle state or testmode bf is on
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- eeprom.c          |   6 +-
- mac80211.c        |   3 +-
- mt76.h            |  36 +++
- mt76_connac_mcu.h |   2 +
- mt7996/Makefile   |   1 +
- mt7996/eeprom.c   |  37 ++-
- mt7996/eeprom.h   |   1 +
- mt7996/init.c     |   8 +
- mt7996/mac.c      |   3 +-
- mt7996/main.c     |  27 ++
- mt7996/mcu.c      |  59 +++-
- mt7996/mcu.h      |  33 +++
- mt7996/mt7996.h   |  28 +-
- mt7996/testmode.c | 740 ++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/testmode.h | 299 +++++++++++++++++++
- testmode.c        | 126 ++++++--
- testmode.h        |  87 +++++-
- tools/fields.c    | 102 ++++++-
- 18 files changed, 1548 insertions(+), 50 deletions(-)
- create mode 100644 mt7996/testmode.c
- create mode 100644 mt7996/testmode.h
-
-diff --git a/eeprom.c b/eeprom.c
-index 0bc66cc..a0047d7 100644
---- a/eeprom.c
-+++ b/eeprom.c
-@@ -94,8 +94,10 @@ int mt76_get_of_data_from_mtd(struct mt76_dev *dev, void *eep, int offset, int l
- 	}
- 
- #ifdef CONFIG_NL80211_TESTMODE
--	dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL);
--	dev->test_mtd.offset = offset;
-+	if (len == dev->eeprom.size) {
-+		dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL);
-+		dev->test_mtd.offset = offset;
-+	}
- #endif
- 
- out_put_node:
-diff --git a/mac80211.c b/mac80211.c
-index d291caa..9a1eb47 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -846,7 +846,8 @@ void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb)
- 	}
- 
- #ifdef CONFIG_NL80211_TESTMODE
--	if (phy->test.state == MT76_TM_STATE_RX_FRAMES) {
-+	if (!(phy->test.flag & MT_TM_FW_RX_COUNT) &&
-+	    phy->test.state == MT76_TM_STATE_RX_FRAMES) {
- 		phy->test.rx_stats.packets[q]++;
- 		if (status->flag & RX_FLAG_FAILED_FCS_CRC)
- 			phy->test.rx_stats.fcs_error[q]++;
-diff --git a/mt76.h b/mt76.h
-index b421a98..57b75fa 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -700,14 +700,21 @@ struct mt76_testmode_ops {
- 	int (*set_params)(struct mt76_phy *phy, struct nlattr **tb,
- 			  enum mt76_testmode_state new_state);
- 	int (*dump_stats)(struct mt76_phy *phy, struct sk_buff *msg);
-+	void (*reset_rx_stats)(struct mt76_phy *phy);
-+	void (*tx_stop)(struct mt76_phy *phy);
-+	int (*set_eeprom)(struct mt76_phy *phy, u32 offset, u8 *val, u8 action);
- };
- 
-+#define MT_TM_FW_RX_COUNT	BIT(0)
-+
- struct mt76_testmode_data {
- 	enum mt76_testmode_state state;
- 
- 	u32 param_set[DIV_ROUND_UP(NUM_MT76_TM_ATTRS, 32)];
- 	struct sk_buff *tx_skb;
- 
-+	u8 sku_en;
-+
- 	u32 tx_count;
- 	u16 tx_mpdu_len;
- 
-@@ -717,6 +724,7 @@ struct mt76_testmode_data {
- 	u8 tx_rate_sgi;
- 	u8 tx_rate_ldpc;
- 	u8 tx_rate_stbc;
-+	u16 tx_preamble_puncture;
- 	u8 tx_ltf;
- 
- 	u8 tx_antenna_mask;
-@@ -726,6 +734,9 @@ struct mt76_testmode_data {
- 	u32 tx_time;
- 	u32 tx_ipg;
- 
-+	bool ibf;
-+	bool ebf;
-+
- 	u32 freq_offset;
- 
- 	u8 tx_power[4];
-@@ -740,7 +751,16 @@ struct mt76_testmode_data {
- 	struct {
- 		u64 packets[__MT_RXQ_MAX];
- 		u64 fcs_error[__MT_RXQ_MAX];
-+		u64 len_mismatch;
- 	} rx_stats;
-+	u8 flag;
-+
-+	struct {
-+		u8 type;
-+		u8 enable;
-+	} cfg;
-+
-+	u8 aid;
- };
- 
- struct mt76_vif {
-@@ -1444,6 +1464,22 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
- int mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state);
- int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len);
- 
-+static inline void
-+mt76_testmode_param_set(struct mt76_testmode_data *td, u16 idx)
-+{
-+#ifdef CONFIG_NL80211_TESTMODE
-+	td->param_set[idx / 32] |= BIT(idx % 32);
-+#endif
-+}
-+
-+static inline bool
-+mt76_testmode_param_present(struct mt76_testmode_data *td, u16 idx)
-+{
-+#ifdef CONFIG_NL80211_TESTMODE
-+	return td->param_set[idx / 32] & BIT(idx % 32);
-+#endif
-+}
-+
- static inline void mt76_testmode_reset(struct mt76_phy *phy, bool disable)
- {
- #ifdef CONFIG_NL80211_TESTMODE
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 1e187a8..0b9c6c5 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1269,12 +1269,14 @@ enum {
- 	MCU_UNI_CMD_EFUSE_CTRL = 0x2d,
- 	MCU_UNI_CMD_RA = 0x2f,
- 	MCU_UNI_CMD_MURU = 0x31,
-+	MCU_UNI_CMD_TESTMODE_RX_STAT = 0x32,
- 	MCU_UNI_CMD_BF = 0x33,
- 	MCU_UNI_CMD_CHANNEL_SWITCH = 0x34,
- 	MCU_UNI_CMD_THERMAL = 0x35,
- 	MCU_UNI_CMD_VOW = 0x37,
- 	MCU_UNI_CMD_PP = 0x38,
- 	MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
-+	MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
- 	MCU_UNI_CMD_RRO = 0x57,
- 	MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
- 	MCU_UNI_CMD_PER_STA_INFO = 0x6d,
-diff --git a/mt7996/Makefile b/mt7996/Makefile
-index a056b40..7bb17f4 100644
---- a/mt7996/Makefile
-+++ b/mt7996/Makefile
-@@ -8,5 +8,6 @@ mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
- 	     debugfs.o mmio.o
- 
- mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o
-+mt7996e-$(CONFIG_NL80211_TESTMODE) += testmode.o
- 
- mt7996e-y += mtk_debugfs.o mtk_mcu.o
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 121a3c9..f9b9ca2 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -6,6 +6,11 @@
- #include <linux/firmware.h>
- #include "mt7996.h"
- #include "eeprom.h"
-+#include <linux/moduleparam.h>
-+
-+static bool testmode_enable;
-+module_param(testmode_enable, bool, 0644);
-+MODULE_PARM_DESC(testmode_enable, "Enable testmode");
- 
- static int mt7996_check_eeprom(struct mt7996_dev *dev)
- {
-@@ -43,6 +48,9 @@ static int mt7996_check_eeprom(struct mt7996_dev *dev)
- 
- static char *mt7996_eeprom_name(struct mt7996_dev *dev)
- {
-+	if (dev->testmode_enable)
-+		return MT7996_EEPROM_DEFAULT_TM;
-+
- 	switch (mt76_chip(&dev->mt76)) {
- 	case 0x7990:
- 		if (dev->chip_sku == MT7996_SKU_404)
-@@ -92,21 +100,36 @@ out:
- 	return ret;
- }
- 
--static int mt7996_eeprom_load(struct mt7996_dev *dev)
-+int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev)
- {
-+	u8 *eeprom;
- 	int ret;
- 
-+	/* load eeprom in flash or bin file mode to determine fw mode */
- 	ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
- 	if (ret < 0)
- 		return ret;
- 
- 	if (ret) {
- 		dev->flash_mode = true;
--	} else {
--		u8 free_block_num;
--		u32 block_num, i;
--		u32 eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE;
-+		eeprom = dev->mt76.eeprom.data;
-+		/* testmode enable priority: eeprom field > module parameter */
-+		dev->testmode_enable = !mt7996_check_eeprom(dev) ? eeprom[MT_EE_TESTMODE_EN] :
-+								   testmode_enable;
-+	}
-+
-+	return ret;
-+}
-+
-+static int mt7996_eeprom_load(struct mt7996_dev *dev)
-+{
-+	int ret;
-+	u8 free_block_num;
-+	u32 block_num, i;
-+	u32 eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE;
- 
-+	/* flash or bin file mode eeprom is loaded before mcu init */
-+	if (!dev->flash_mode) {
- 		ret = mt7996_mcu_get_eeprom_free_block(dev, &free_block_num);
- 		if (ret < 0)
- 			return ret;
-@@ -118,8 +141,8 @@ static int mt7996_eeprom_load(struct mt7996_dev *dev)
- 		/* read eeprom data from efuse */
- 		block_num = DIV_ROUND_UP(MT7996_EEPROM_SIZE, eeprom_blk_size);
- 		for (i = 0; i < block_num; i++) {
--			ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size);
--			if (ret < 0)
-+			ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size, NULL);
-+			if (ret && ret != -EINVAL)
- 				return ret;
- 		}
- 	}
-diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index 72c38ad..de3ff4e 100644
---- a/mt7996/eeprom.h
-+++ b/mt7996/eeprom.h
-@@ -14,6 +14,7 @@ enum mt7996_eeprom_field {
- 	MT_EE_MAC_ADDR =	0x004,
- 	MT_EE_MAC_ADDR2 =	0x00a,
- 	MT_EE_WIFI_CONF =	0x190,
-+	MT_EE_TESTMODE_EN =	0x1af,
- 	MT_EE_MAC_ADDR3 =	0x2c0,
- 	MT_EE_RATE_DELTA_2G =	0x1400,
- 	MT_EE_RATE_DELTA_5G =	0x147d,
-diff --git a/mt7996/init.c b/mt7996/init.c
-index d58335a..440e26d 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -969,6 +969,10 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
- 
- 	set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state);
- 
-+	ret = mt7996_eeprom_check_fw_mode(dev);
-+	if (ret < 0)
-+		return ret;
-+
- 	ret = mt7996_mcu_init(dev);
- 	if (ret)
- 		return ret;
-@@ -1419,6 +1423,10 @@ int mt7996_register_device(struct mt7996_dev *dev)
- 
- 	mt7996_init_wiphy(hw, &dev->mt76.mmio.wed);
- 
-+#ifdef CONFIG_NL80211_TESTMODE
-+	dev->mt76.test_ops = &mt7996_testmode_ops;
-+#endif
-+
- 	ret = mt76_register_device(&dev->mt76, true, mt76_rates,
- 				   ARRAY_SIZE(mt76_rates));
- 	if (ret)
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 1f53d23..603f6c0 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -685,7 +685,8 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
- 				     *info);
- 	}
- 
--	if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023))
-+	if (rxv && mode >= MT_PHY_TYPE_HE_SU && mode < MT_PHY_TYPE_EHT_SU &&
-+	    !(status->flag & RX_FLAG_8023))
- 		mt76_connac3_mac_decode_he_radiotap(skb, rxv, mode);
- 
- 	if (!status->wcid || !ieee80211_is_data_qos(fc) || hw_aggr)
-diff --git a/mt7996/main.c b/mt7996/main.c
-index e66c607..50c7f07 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -23,6 +23,18 @@ static bool mt7996_dev_running(struct mt7996_dev *dev)
- 	return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state);
- }
- 
-+static void mt7996_testmode_disable_all(struct mt7996_dev *dev)
-+{
-+	struct mt7996_phy *phy;
-+	int i;
-+
-+	for (i = 0; i < __MT_MAX_BAND; i++) {
-+		phy = __mt7996_phy(dev, i);
-+		if (phy)
-+			mt76_testmode_set_state(phy->mt76, MT76_TM_STATE_OFF);
-+	}
-+}
-+
- int mt7996_run(struct ieee80211_hw *hw)
- {
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
-@@ -45,6 +57,8 @@ int mt7996_run(struct ieee80211_hw *hw)
- 		}
- 	}
- 
-+	mt7996_testmode_disable_all(dev);
-+
- 	mt7996_mac_enable_nf(dev, phy->mt76->band_idx);
- 
- 	ret = mt7996_mcu_set_rts_thresh(phy, 0x92b);
-@@ -303,6 +317,11 @@ int mt7996_set_channel(struct mt7996_phy *phy)
- 
- 	mt76_set_channel(phy->mt76);
- 
-+	if (mt76_testmode_enabled(phy->mt76) || phy->mt76->test.bf_en) {
-+		mt7996_tm_update_channel(phy);
-+		goto out;
-+	}
-+
- 	ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
- 	if (ret)
- 		goto out;
-@@ -411,6 +430,12 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
- 	int ret;
- 
- 	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
-+		if (!mt76_testmode_enabled(phy->mt76) && !phy->mt76->test.bf_en) {
-+			ret = mt7996_mcu_edcca_enable(phy, true);
-+			if (ret)
-+				return ret;
-+		}
-+
- 		ret = mt7996_mcu_set_pp_en(phy, PP_USR_MODE,
- 					   phy->mt76->chandef.punctured);
- 		if (ret)
-@@ -1523,6 +1548,8 @@ const struct ieee80211_ops mt7996_ops = {
- 	.sta_set_decap_offload = mt7996_sta_set_decap_offload,
- 	.add_twt_setup = mt7996_mac_add_twt_setup,
- 	.twt_teardown_request = mt7996_twt_teardown_request,
-+	CFG80211_TESTMODE_CMD(mt76_testmode_cmd)
-+	CFG80211_TESTMODE_DUMP(mt76_testmode_dump)
- #ifdef CONFIG_MAC80211_DEBUGFS
- 	.sta_add_debugfs = mt7996_sta_add_debugfs,
- #endif
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 6c565c9..2bca86a 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -2865,8 +2865,12 @@ static int mt7996_load_ram(struct mt7996_dev *dev)
- {
- 	int ret;
- 
--	ret = __mt7996_load_ram(dev, "WM", fw_name(dev, FIRMWARE_WM),
--				MT7996_RAM_TYPE_WM);
-+	if (dev->testmode_enable)
-+		ret = __mt7996_load_ram(dev, "WM_TM", fw_name(dev, FIRMWARE_WM_TM),
-+					MT7996_RAM_TYPE_WM_TM);
-+	else
-+		ret = __mt7996_load_ram(dev, "WM", fw_name(dev, FIRMWARE_WM),
-+					MT7996_RAM_TYPE_WM);
- 	if (ret)
- 		return ret;
- 
-@@ -3557,17 +3561,9 @@ int mt7996_mcu_set_eeprom(struct mt7996_dev *dev)
- 				 &req, sizeof(req), true);
- }
- 
--int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset)
-+int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf)
- {
--	struct {
--		u8 _rsv[4];
--
--		__le16 tag;
--		__le16 len;
--		__le32 addr;
--		__le32 valid;
--		u8 data[16];
--	} __packed req = {
-+	struct mt7996_mcu_eeprom_info req = {
- 		.tag = cpu_to_le16(UNI_EFUSE_ACCESS),
- 		.len = cpu_to_le16(sizeof(req) - 4),
- 		.addr = cpu_to_le32(round_down(offset,
-@@ -3576,6 +3572,7 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset)
- 	struct sk_buff *skb;
- 	bool valid;
- 	int ret;
-+	u8 *buf = read_buf;
- 
- 	ret = mt76_mcu_send_and_get_msg(&dev->mt76,
- 					MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL),
-@@ -3586,7 +3583,9 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset)
- 	valid = le32_to_cpu(*(__le32 *)(skb->data + 16));
- 	if (valid) {
- 		u32 addr = le32_to_cpu(*(__le32 *)(skb->data + 12));
--		u8 *buf = (u8 *)dev->mt76.eeprom.data + addr;
-+
-+		if (!buf)
-+			buf = (u8 *)dev->mt76.eeprom.data + addr;
- 
- 		skb_pull(skb, 48);
- 		memcpy(buf, skb->data, MT7996_EEPROM_BLOCK_SIZE);
-@@ -4609,3 +4608,37 @@ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap)
- 	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(PP),
- 				 &req, sizeof(req), false);
- }
-+
-+int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct tx_power_ctrl req = {
-+		.tag = cpu_to_le16(power_ctrl_id),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.power_ctrl_id = power_ctrl_id,
-+		.band_idx = phy->mt76->band_idx,
-+	};
-+
-+	switch (power_ctrl_id) {
-+	case UNI_TXPOWER_SKU_POWER_LIMIT_CTRL:
-+		req.sku_enable = !!data;
-+		break;
-+	case UNI_TXPOWER_PERCENTAGE_CTRL:
-+		req.percentage_ctrl_enable = !!data;
-+		break;
-+	case UNI_TXPOWER_PERCENTAGE_DROP_CTRL:
-+		req.power_drop_level = data;
-+		break;
-+	case UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL:
-+		req.bf_backoff_enable = !!data;
-+		break;
-+	case UNI_TXPOWER_ATE_MODE_CTRL:
-+		req.ate_mode_enable = !!data;
-+		break;
-+	default:
-+		req.sku_enable = !!data;
-+	}
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TXPOWER),
-+				 &req, sizeof(req), false);
-+}
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 2052555..d5ac2b3 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -157,6 +157,16 @@ struct mt7996_mcu_eeprom {
- 	__le16 buf_len;
- } __packed;
- 
-+struct mt7996_mcu_eeprom_info {
-+	u8 _rsv[4];
-+
-+	__le16 tag;
-+	__le16 len;
-+	__le32 addr;
-+	__le32 valid;
-+	u8 data[MT7996_EEPROM_BLOCK_SIZE];
-+} __packed;
-+
- struct mt7996_mcu_phy_rx_info {
- 	u8 category;
- 	u8 rate;
-@@ -889,8 +899,31 @@ enum {
- 	UNI_CMD_THERMAL_PROTECT_DUTY_CONFIG,
- };
- 
-+struct tx_power_ctrl {
-+	u8 _rsv[4];
-+
-+	__le16 tag;
-+	__le16 len;
-+
-+	u8 power_ctrl_id;
-+	union {
-+		bool sku_enable;
-+		bool ate_mode_enable;
-+		bool percentage_ctrl_enable;
-+		bool bf_backoff_enable;
-+		u8 power_drop_level;
-+	};
-+	u8 band_idx;
-+	u8 rsv[1];
-+} __packed;
-+
- enum {
-+	UNI_TXPOWER_SKU_POWER_LIMIT_CTRL = 0,
-+	UNI_TXPOWER_PERCENTAGE_CTRL = 1,
-+	UNI_TXPOWER_PERCENTAGE_DROP_CTRL = 2,
-+	UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL = 3,
- 	UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL = 4,
-+	UNI_TXPOWER_ATE_MODE_CTRL = 6,
- };
- 
- enum {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 696e16f..0c6a4b9 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -32,25 +32,30 @@
- #define MT7996_FIRMWARE_WA		"mediatek/mt7996/mt7996_wa.bin"
- #define MT7996_FIRMWARE_WM		"mediatek/mt7996/mt7996_wm.bin"
- #define MT7996_FIRMWARE_DSP		"mediatek/mt7996/mt7996_dsp.bin"
-+#define MT7996_FIRMWARE_WM_TM		"mediatek/mt7996/mt7996_wm_tm.bin"
- #define MT7996_ROM_PATCH		"mediatek/mt7996/mt7996_rom_patch.bin"
- 
- #define MT7992_FIRMWARE_WA		"mediatek/mt7996/mt7992_wa.bin"
- #define MT7992_FIRMWARE_WM		"mediatek/mt7996/mt7992_wm.bin"
- #define MT7992_FIRMWARE_DSP		"mediatek/mt7996/mt7992_dsp.bin"
-+#define MT7992_FIRMWARE_WM_TM		"mediatek/mt7996/mt7992_wm_tm.bin"
- #define MT7992_ROM_PATCH		"mediatek/mt7996/mt7992_rom_patch.bin"
- 
- #define MT7992_FIRMWARE_WA_24		"mediatek/mt7996/mt7992_wa_24.bin"
- #define MT7992_FIRMWARE_WM_24		"mediatek/mt7996/mt7992_wm_24.bin"
- #define MT7992_FIRMWARE_DSP_24		"mediatek/mt7996/mt7992_dsp_24.bin"
-+#define MT7992_FIRMWARE_WM_TM_24	"mediatek/mt7996/mt7992_wm_tm_24.bin"
- #define MT7992_ROM_PATCH_24		"mediatek/mt7996/mt7992_rom_patch_24.bin"
- 
- #define MT7992_FIRMWARE_WA_23		"mediatek/mt7996/mt7992_wa_23.bin"
- #define MT7992_FIRMWARE_WM_23		"mediatek/mt7996/mt7992_wm_23.bin"
- #define MT7992_FIRMWARE_DSP_23		"mediatek/mt7996/mt7992_dsp_23.bin"
-+#define MT7992_FIRMWARE_WM_TM_23	"mediatek/mt7996/mt7992_wm_tm_23.bin"
- #define MT7992_ROM_PATCH_23		"mediatek/mt7996/mt7992_rom_patch_23.bin"
- 
- #define MT7996_EEPROM_DEFAULT		"mediatek/mt7996/mt7996_eeprom.bin"
- #define MT7996_EEPROM_DEFAULT_404	"mediatek/mt7996/mt7996_eeprom_dual_404.bin"
-+#define MT7996_EEPROM_DEFAULT_TM	"mediatek/mt7996/mt7996_eeprom_tm.bin"
- #define MT7992_EEPROM_DEFAULT		"mediatek/mt7996/mt7992_eeprom_2i5i.bin"
- #define MT7992_EEPROM_DEFAULT_EXT	"mediatek/mt7996/mt7992_eeprom_2e5e.bin"
- #define MT7992_EEPROM_DEFAULT_MIX	"mediatek/mt7996/mt7992_eeprom_2i5e.bin"
-@@ -127,6 +132,7 @@ enum mt7992_sku_type {
- 
- enum mt7996_ram_type {
- 	MT7996_RAM_TYPE_WM,
-+	MT7996_RAM_TYPE_WM_TM = MT7996_RAM_TYPE_WM,
- 	MT7996_RAM_TYPE_WA,
- 	MT7996_RAM_TYPE_DSP,
- 	__MT7996_RAM_TYPE_MAX,
-@@ -277,6 +283,21 @@ struct mt7996_phy {
- 
- 	u8 pp_mode;
- 	u16 punct_bitmap;
-+
-+#ifdef CONFIG_NL80211_TESTMODE
-+	struct {
-+		u32 *reg_backup;
-+
-+		s32 last_freq_offset;
-+		u8 last_rcpi[4];
-+		s8 last_rssi[4];
-+		s8 last_ib_rssi[4];
-+		s8 last_wb_rssi[4];
-+		u8 last_snr;
-+
-+		u8 spe_idx;
-+	} test;
-+#endif
- };
- 
- struct mt7996_dev {
-@@ -357,6 +378,8 @@ struct mt7996_dev {
- 		spinlock_t lock;
- 	} wed_rro;
- 
-+	bool testmode_enable;
-+
- 	bool ibf;
- 	u8 fw_debug_wm;
- 	u8 fw_debug_wa;
-@@ -472,6 +495,7 @@ mt7996_band_valid(struct mt7996_dev *dev, u8 band)
- extern const struct ieee80211_ops mt7996_ops;
- extern struct pci_driver mt7996_pci_driver;
- extern struct pci_driver mt7996_hif_driver;
-+extern const struct mt76_testmode_ops mt7996_testmode_ops;
- 
- struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
- 				     void __iomem *mem_base, u32 device_id);
-@@ -481,6 +505,7 @@ u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
- int mt7996_register_device(struct mt7996_dev *dev);
- void mt7996_unregister_device(struct mt7996_dev *dev);
- int mt7996_eeprom_init(struct mt7996_dev *dev);
-+int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev);
- int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
- int mt7996_eeprom_get_target_power(struct mt7996_dev *dev,
- 				   struct ieee80211_channel *chan);
-@@ -533,7 +558,7 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
- int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- 			       struct ieee80211_sta *sta, void *data, u32 field);
- int mt7996_mcu_set_eeprom(struct mt7996_dev *dev);
--int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset);
-+int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf);
- int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num);
- int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap);
- int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 set, u8 band);
-@@ -568,6 +593,7 @@ void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
- void mt7996_mcu_exit(struct mt7996_dev *dev);
- int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
- int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
-+int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
- 
- static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
- {
-diff --git a/mt7996/testmode.c b/mt7996/testmode.c
-new file mode 100644
-index 0000000..98eebce
---- /dev/null
-+++ b/mt7996/testmode.c
-@@ -0,0 +1,740 @@
-+// SPDX-License-Identifier: ISC
-+/*
-+ * Copyright (C) 2022 MediaTek Inc.
-+ */
-+
-+#include "mt7996.h"
-+#include "mac.h"
-+#include "mcu.h"
-+#include "testmode.h"
-+
-+enum {
-+	TM_CHANGED_TXPOWER,
-+	TM_CHANGED_FREQ_OFFSET,
-+	TM_CHANGED_SKU_EN,
-+	TM_CHANGED_TX_LENGTH,
-+	TM_CHANGED_TX_TIME,
-+	TM_CHANGED_CFG,
-+
-+	/* must be last */
-+	NUM_TM_CHANGED
-+};
-+
-+static const u8 tm_change_map[] = {
-+	[TM_CHANGED_TXPOWER] = MT76_TM_ATTR_TX_POWER,
-+	[TM_CHANGED_FREQ_OFFSET] = MT76_TM_ATTR_FREQ_OFFSET,
-+	[TM_CHANGED_SKU_EN] = MT76_TM_ATTR_SKU_EN,
-+	[TM_CHANGED_TX_LENGTH] = MT76_TM_ATTR_TX_LENGTH,
-+	[TM_CHANGED_TX_TIME] = MT76_TM_ATTR_TX_TIME,
-+	[TM_CHANGED_CFG] = MT76_TM_ATTR_CFG,
-+};
-+
-+static u8 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method)
-+{
-+	static const u8 width_to_bw[][NUM_BW_MAP] = {
-+		[NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ},
-+		[NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ},
-+		[NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ},
-+		[NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ},
-+		[NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ},
-+		[NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ},
-+		[NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
-+		[NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
-+		[NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ},
-+	};
-+
-+	if (width >= ARRAY_SIZE(width_to_bw))
-+		return 0;
-+
-+	return width_to_bw[width][method];
-+}
-+
-+static u8 mt7996_tm_rate_to_phy(u8 tx_rate_mode)
-+{
-+	static const u8 rate_to_phy[] = {
-+		[MT76_TM_TX_MODE_CCK] = MT_PHY_TYPE_CCK,
-+		[MT76_TM_TX_MODE_OFDM] = MT_PHY_TYPE_OFDM,
-+		[MT76_TM_TX_MODE_HT] = MT_PHY_TYPE_HT,
-+		[MT76_TM_TX_MODE_VHT] = MT_PHY_TYPE_VHT,
-+		[MT76_TM_TX_MODE_HE_SU] = MT_PHY_TYPE_HE_SU,
-+		[MT76_TM_TX_MODE_HE_EXT_SU] = MT_PHY_TYPE_HE_EXT_SU,
-+		[MT76_TM_TX_MODE_HE_TB] = MT_PHY_TYPE_HE_TB,
-+		[MT76_TM_TX_MODE_HE_MU] = MT_PHY_TYPE_HE_MU,
-+		[MT76_TM_TX_MODE_EHT_SU] = MT_PHY_TYPE_EHT_SU,
-+		[MT76_TM_TX_MODE_EHT_TRIG] = MT_PHY_TYPE_EHT_TRIG,
-+		[MT76_TM_TX_MODE_EHT_MU] = MT_PHY_TYPE_EHT_MU,
-+	};
-+
-+	if (tx_rate_mode > MT76_TM_TX_MODE_MAX)
-+		return -EINVAL;
-+
-+	return rate_to_phy[tx_rate_mode];
-+}
-+
-+static int
-+mt7996_tm_check_antenna(struct mt7996_phy *phy)
-+{
-+	struct mt76_testmode_data *td = &phy->mt76->test;
-+	struct mt7996_dev *dev = phy->dev;
-+	u8 band_idx = phy->mt76->band_idx;
-+	u32 chainmask = phy->mt76->chainmask;
-+	u32 aux_rx_mask;
-+
-+	chainmask = chainmask >> dev->chainshift[band_idx];
-+	aux_rx_mask = BIT(fls(chainmask)) * phy->has_aux_rx;
-+	if (td->tx_antenna_mask & ~(chainmask | aux_rx_mask)) {
-+		dev_err(dev->mt76.dev,
-+			"tx antenna mask 0x%x exceeds hw limit (chainmask 0x%x, has aux rx: %s)\n",
-+			td->tx_antenna_mask, chainmask, phy->has_aux_rx ? "yes" : "no");
-+		return -EINVAL;
-+	}
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_tm_set(struct mt7996_dev *dev, u32 func_idx, u32 data)
-+{
-+	struct mt7996_tm_req req = {
-+		.rf_test = {
-+			.tag = cpu_to_le16(UNI_RF_TEST_CTRL),
-+			.len = cpu_to_le16(sizeof(req.rf_test)),
-+			.action = RF_ACTION_SET,
-+			.op.rf.func_idx = func_idx,
-+			.op.rf.param.func_data = cpu_to_le32(data),
-+		},
-+	};
-+	bool wait = (data == RF_CMD(START_TX)) ? true : false;
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), &req,
-+				 sizeof(req), wait);
-+}
-+
-+static int
-+mt7996_tm_get(struct mt7996_dev *dev, u32 func_idx, u32 data, u32 *result)
-+{
-+	struct mt7996_tm_req req = {
-+		.rf_test = {
-+			.tag = cpu_to_le16(UNI_RF_TEST_CTRL),
-+			.len = cpu_to_le16(sizeof(req.rf_test)),
-+			.action = RF_ACTION_GET,
-+			.op.rf.func_idx = func_idx,
-+			.op.rf.param.func_data = cpu_to_le32(data),
-+		},
-+	};
-+	struct mt7996_tm_event *event;
-+	struct sk_buff *skb;
-+	int ret;
-+
-+	ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(TESTMODE_CTRL),
-+					&req, sizeof(req), true, &skb);
-+	if (ret)
-+		return ret;
-+
-+	event = (struct mt7996_tm_event *)skb->data;
-+	*result = event->result.payload_length;
-+
-+	dev_kfree_skb(skb);
-+
-+	return ret;
-+}
-+
-+static void
-+mt7996_tm_set_antenna(struct mt7996_phy *phy, u32 func_idx)
-+{
-+#define SPE_INDEX_MASK		BIT(31)
-+#define TX_ANTENNA_MASK		GENMASK(3, 0)
-+#define RX_ANTENNA_MASK		GENMASK(20, 16)		/* RX antenna mask at most 5 bit */
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt76_testmode_data *td = &phy->mt76->test;
-+	u32 antenna_mask;
-+
-+	if (!mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA))
-+		return;
-+
-+	if (func_idx == SET_ID(TX_PATH))
-+		antenna_mask = td->tx_spe_idx ? (SPE_INDEX_MASK | td->tx_spe_idx) :
-+						td->tx_antenna_mask & TX_ANTENNA_MASK;
-+	else if (func_idx == SET_ID(RX_PATH))
-+		antenna_mask = u32_encode_bits(td->tx_antenna_mask, RX_ANTENNA_MASK);
-+	else
-+		return;
-+
-+	mt7996_tm_set(dev, func_idx, antenna_mask);
-+}
-+
-+static void
-+mt7996_tm_set_mac_addr(struct mt7996_dev *dev, u8 *addr, u32 func_idx)
-+{
-+#define REMAIN_PART_TAG		BIT(18)
-+	u32 own_mac_first = 0, own_mac_remain = 0;
-+	int len = sizeof(u32);
-+
-+	memcpy(&own_mac_first, addr, len);
-+	mt7996_tm_set(dev, func_idx, own_mac_first);
-+	/* Set the remain part of mac address */
-+	memcpy(&own_mac_remain, addr + len, ETH_ALEN - len);
-+	mt7996_tm_set(dev, func_idx | REMAIN_PART_TAG, own_mac_remain);
-+}
-+
-+static int
-+mt7996_tm_rf_switch_mode(struct mt7996_dev *dev, u32 op_mode)
-+{
-+	struct mt7996_tm_req req = {
-+		.rf_test = {
-+			.tag = cpu_to_le16(UNI_RF_TEST_CTRL),
-+			.len = cpu_to_le16(sizeof(req.rf_test)),
-+			.action = RF_ACTION_SWITCH_TO_RF_TEST,
-+			.op.op_mode = cpu_to_le32(op_mode),
-+		},
-+	};
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), &req,
-+				 sizeof(req), false);
-+}
-+
-+static void
-+mt7996_tm_init(struct mt7996_phy *phy, bool en)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	u8 rf_test_mode = en ? RF_OPER_RF_TEST : RF_OPER_NORMAL;
-+
-+	if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
-+		return;
-+
-+	mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(ATE_MODE), en);
-+	mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(SKU_POWER_LIMIT), !en);
-+	mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(BACKOFF_POWER_LIMIT), !en);
-+
-+	mt7996_tm_rf_switch_mode(dev, rf_test_mode);
-+
-+	mt7996_mcu_add_bss_info(phy, phy->monitor_vif, en);
-+	mt7996_mcu_add_sta(dev, phy->monitor_vif, NULL, en, false);
-+
-+	mt7996_tm_set(dev, SET_ID(BAND_IDX), phy->mt76->band_idx);
-+
-+	/* use firmware counter for RX stats */
-+	phy->mt76->test.flag |= MT_TM_FW_RX_COUNT;
-+}
-+
-+static void
-+mt7996_tm_update_channel(struct mt7996_phy *phy)
-+{
-+#define CHAN_FREQ_BW_80P80_TAG		(SET_ID(CHAN_FREQ) | BIT(16))
-+	struct mt7996_dev *dev = phy->dev;
-+	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
-+	struct ieee80211_channel *chan = chandef->chan;
-+	u8 width = chandef->width;
-+	static const u8 ch_band[] = {
-+		[NL80211_BAND_2GHZ] = 0,
-+		[NL80211_BAND_5GHZ] = 1,
-+		[NL80211_BAND_6GHZ] = 2,
-+	};
-+
-+	if (!chan || !chandef) {
-+		dev_info(dev->mt76.dev, "chandef not found, channel update failed!\n");
-+		return;
-+	}
-+
-+	/* system bw */
-+	mt7996_tm_set(dev, SET_ID(CBW), mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW));
-+
-+	if (width == NL80211_CHAN_WIDTH_80P80) {
-+		width = NL80211_CHAN_WIDTH_160;
-+		mt7996_tm_set(dev, CHAN_FREQ_BW_80P80_TAG, chandef->center_freq2 * 1000);
-+	}
-+
-+	/* TODO: define per-packet bw */
-+	/* per-packet bw */
-+	mt7996_tm_set(dev, SET_ID(DBW), mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW));
-+
-+	/* control channel selection index */
-+	mt7996_tm_set(dev, SET_ID(PRIMARY_CH), 0);
-+	mt7996_tm_set(dev, SET_ID(BAND), ch_band[chan->band]);
-+
-+	/* trigger switch channel calibration */
-+	mt7996_tm_set(dev, SET_ID(CHAN_FREQ), chandef->center_freq1 * 1000);
-+
-+	// TODO: update power limit table
-+}
-+
-+static void
-+mt7996_tm_tx_stop(struct mt76_phy *mphy)
-+{
-+	struct mt76_testmode_data *td = &mphy->test;
-+	struct mt7996_phy *phy = mphy->priv;
-+	struct mt7996_dev *dev = phy->dev;
-+
-+	mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(STOP_TEST));
-+	td->tx_pending = 0;
-+}
-+
-+static void
-+mt7996_tm_set_tx_frames(struct mt7996_phy *phy, bool en)
-+{
-+#define FRAME_CONTROL		0x88
-+	struct mt76_testmode_data *td = &phy->mt76->test;
-+	struct mt7996_dev *dev = phy->dev;
-+
-+	//TODO: RU operation, replace mcs, nss, and ldpc
-+	if (en) {
-+		mt7996_tm_set(dev, SET_ID(MAC_HEADER), FRAME_CONTROL);
-+		mt7996_tm_set(dev, SET_ID(SEQ_CTRL), 0);
-+		mt7996_tm_set(dev, SET_ID(TX_COUNT), td->tx_count);
-+		mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
-+		mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
-+
-+		if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER))
-+			mt7996_tm_set(dev, SET_ID(POWER), td->tx_power[0]);
-+
-+		if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_TIME)) {
-+			mt7996_tm_set(dev, SET_ID(TX_LEN), 0);
-+			mt7996_tm_set(dev, SET_ID(TX_TIME), td->tx_time);
-+		} else {
-+			mt7996_tm_set(dev, SET_ID(TX_LEN), td->tx_mpdu_len);
-+			mt7996_tm_set(dev, SET_ID(TX_TIME), 0);
-+		}
-+
-+		mt7996_tm_set_antenna(phy, SET_ID(TX_PATH));
-+		mt7996_tm_set_antenna(phy, SET_ID(RX_PATH));
-+		mt7996_tm_set(dev, SET_ID(STBC), td->tx_rate_stbc);
-+		mt7996_tm_set(dev, SET_ID(ENCODE_MODE), td->tx_rate_ldpc);
-+		mt7996_tm_set(dev, SET_ID(IBF_ENABLE), td->ibf);
-+		mt7996_tm_set(dev, SET_ID(EBF_ENABLE), td->ebf);
-+		mt7996_tm_set(dev, SET_ID(IPG), td->tx_ipg);
-+		mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi);
-+		mt7996_tm_set(dev, SET_ID(NSS), td->tx_rate_nss);
-+		mt7996_tm_set(dev, SET_ID(AID_OFFSET), 0);
-+		mt7996_tm_set(dev, SET_ID(PUNCTURE), td->tx_preamble_puncture);
-+
-+		mt7996_tm_set(dev, SET_ID(MAX_PE), 2);
-+		mt7996_tm_set(dev, SET_ID(HW_TX_MODE), 0);
-+		mt7996_tm_update_channel(phy);
-+
-+		/* trigger firmware to start TX */
-+		mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(START_TX));
-+	} else {
-+		mt7996_tm_tx_stop(phy->mt76);
-+	}
-+}
-+
-+static int
-+mt7996_tm_rx_stats_user_ctrl(struct mt7996_phy *phy, u16 user_idx)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt7996_tm_rx_req req = {
-+		.band = phy->mt76->band_idx,
-+		.user_ctrl = {
-+			.tag = cpu_to_le16(UNI_TM_RX_STAT_SET_USER_CTRL),
-+			.len = cpu_to_le16(sizeof(req.user_ctrl)),
-+			.band_idx = phy->mt76->band_idx,
-+			.user_idx = cpu_to_le16(user_idx),
-+		},
-+	};
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_RX_STAT), &req,
-+				 sizeof(req), false);
-+}
-+
-+static void
-+mt7996_tm_set_rx_frames(struct mt7996_phy *phy, bool en)
-+{
-+#define RX_MU_DISABLE	0xf800
-+	struct mt76_testmode_data *td = &phy->mt76->test;
-+	struct mt7996_dev *dev = phy->dev;
-+	int ret;
-+
-+	if (en) {
-+		ret = mt7996_tm_rx_stats_user_ctrl(phy, td->aid);
-+		if (ret) {
-+			dev_info(dev->mt76.dev, "Set RX stats user control failed!\n");
-+			return;
-+		}
-+
-+		mt7996_tm_update_channel(phy);
-+
-+		if (td->tx_rate_mode >= MT76_TM_TX_MODE_HE_MU) {
-+			if (td->aid)
-+				ret = mt7996_tm_set(dev, SET_ID(RX_MU_AID), td->aid);
-+			else
-+				ret = mt7996_tm_set(dev, SET_ID(RX_MU_AID), RX_MU_DISABLE);
-+		}
-+		mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
-+		mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi);
-+		mt7996_tm_set_antenna(phy, SET_ID(RX_PATH));
-+		mt7996_tm_set(dev, SET_ID(MAX_PE), 2);
-+
-+		mt7996_tm_set_mac_addr(dev, td->addr[1], SET_ID(SA));
-+
-+		/* trigger firmware to start RX */
-+		mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(START_RX));
-+	} else {
-+		/* trigger firmware to stop RX */
-+		mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(STOP_TEST));
-+	}
-+}
-+
-+static void
-+mt7996_tm_set_tx_cont(struct mt7996_phy *phy, bool en)
-+{
-+#define CONT_WAVE_MODE_OFDM	3
-+	struct mt76_testmode_data *td = &phy->mt76->test;
-+	struct mt7996_dev *dev = phy->dev;
-+
-+	if (en) {
-+		mt7996_tm_update_channel(phy);
-+		mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
-+		mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
-+		/* fix payload is OFDM */
-+		mt7996_tm_set(dev, SET_ID(CONT_WAVE_MODE), CONT_WAVE_MODE_OFDM);
-+		mt7996_tm_set(dev, SET_ID(ANT_MASK), td->tx_antenna_mask);
-+
-+		/* trigger firmware to start CONT TX */
-+		mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(CONT_WAVE));
-+	} else {
-+		/* trigger firmware to stop CONT TX  */
-+		mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(STOP_TEST));
-+	}
-+}
-+
-+static void
-+mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
-+{
-+	struct mt76_testmode_data *td = &phy->mt76->test;
-+	struct mt7996_dev *dev = phy->dev;
-+
-+	if (changed & BIT(TM_CHANGED_FREQ_OFFSET)) {
-+		mt7996_tm_set(dev, SET_ID(FREQ_OFFSET), td->freq_offset);
-+		mt7996_tm_set(dev, SET_ID(FREQ_OFFSET_C2), td->freq_offset);
-+	}
-+	if (changed & BIT(TM_CHANGED_TXPOWER))
-+		mt7996_tm_set(dev, SET_ID(POWER), td->tx_power[0]);
-+	if (changed & BIT(TM_CHANGED_SKU_EN)) {
-+		mt7996_tm_update_channel(phy);
-+		mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(SKU_POWER_LIMIT), td->sku_en);
-+		mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(BACKOFF_POWER_LIMIT), td->sku_en);
-+		mt7996_mcu_set_txpower_sku(phy);
-+	}
-+	if (changed & BIT(TM_CHANGED_TX_LENGTH)) {
-+		mt7996_tm_set(dev, SET_ID(TX_LEN), td->tx_mpdu_len);
-+		mt7996_tm_set(dev, SET_ID(TX_TIME), 0);
-+	}
-+	if (changed & BIT(TM_CHANGED_TX_TIME)) {
-+		mt7996_tm_set(dev, SET_ID(TX_LEN), 0);
-+		mt7996_tm_set(dev, SET_ID(TX_TIME), td->tx_time);
-+	}
-+	if (changed & BIT(TM_CHANGED_CFG)) {
-+		u32 func_idx = td->cfg.enable ? SET_ID(CFG_ON) : SET_ID(CFG_OFF);
-+
-+		mt7996_tm_set(dev, func_idx, td->cfg.type);
-+	}
-+}
-+
-+static int
-+mt7996_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
-+{
-+	struct mt76_testmode_data *td = &mphy->test;
-+	struct mt7996_phy *phy = mphy->priv;
-+	enum mt76_testmode_state prev_state = td->state;
-+
-+	mphy->test.state = state;
-+
-+	if (prev_state != MT76_TM_STATE_OFF)
-+		mt7996_tm_set(phy->dev, SET_ID(BAND_IDX), mphy->band_idx);
-+
-+	if (prev_state == MT76_TM_STATE_TX_FRAMES ||
-+	    state == MT76_TM_STATE_TX_FRAMES)
-+		mt7996_tm_set_tx_frames(phy, state == MT76_TM_STATE_TX_FRAMES);
-+	else if (prev_state == MT76_TM_STATE_RX_FRAMES ||
-+		 state == MT76_TM_STATE_RX_FRAMES)
-+		mt7996_tm_set_rx_frames(phy, state == MT76_TM_STATE_RX_FRAMES);
-+	else if (prev_state == MT76_TM_STATE_TX_CONT ||
-+		 state == MT76_TM_STATE_TX_CONT)
-+		mt7996_tm_set_tx_cont(phy, state == MT76_TM_STATE_TX_CONT);
-+	else if (prev_state == MT76_TM_STATE_OFF ||
-+		 state == MT76_TM_STATE_OFF)
-+		mt7996_tm_init(phy, !(state == MT76_TM_STATE_OFF));
-+
-+	if ((state == MT76_TM_STATE_IDLE &&
-+	     prev_state == MT76_TM_STATE_OFF) ||
-+	    (state == MT76_TM_STATE_OFF &&
-+	     prev_state == MT76_TM_STATE_IDLE)) {
-+		u32 changed = 0;
-+		int i, ret;
-+
-+		for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
-+			u16 cur = tm_change_map[i];
-+
-+			if (mt76_testmode_param_present(td, cur))
-+				changed |= BIT(i);
-+		}
-+
-+		ret = mt7996_tm_check_antenna(phy);
-+		if (ret)
-+			return ret;
-+
-+		mt7996_tm_update_params(phy, changed);
-+	}
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
-+		     enum mt76_testmode_state new_state)
-+{
-+	struct mt76_testmode_data *td = &mphy->test;
-+	struct mt7996_phy *phy = mphy->priv;
-+	struct mt7996_dev *dev = phy->dev;
-+	u32 changed = 0;
-+	int i, ret;
-+
-+	BUILD_BUG_ON(NUM_TM_CHANGED >= 32);
-+
-+	if (new_state == MT76_TM_STATE_OFF ||
-+	    td->state == MT76_TM_STATE_OFF)
-+		return 0;
-+
-+	ret = mt7996_tm_check_antenna(phy);
-+	if (ret)
-+		return ret;
-+
-+	for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
-+		if (tb[tm_change_map[i]])
-+			changed |= BIT(i);
-+	}
-+
-+	mt7996_tm_set(dev, SET_ID(BAND_IDX), mphy->band_idx);
-+	mt7996_tm_update_params(phy, changed);
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_tm_get_rx_stats(struct mt7996_phy *phy)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt7996_tm_rx_req req = {
-+		.band = phy->mt76->band_idx,
-+		.rx_stat_all = {
-+			.tag = cpu_to_le16(UNI_TM_RX_STAT_GET_ALL_V2),
-+			.len = cpu_to_le16(sizeof(req.rx_stat_all)),
-+			.band_idx = phy->mt76->band_idx,
-+		},
-+	};
-+	struct mt76_testmode_data *td = &phy->mt76->test;
-+	struct mt7996_tm_rx_event *rx_stats;
-+	struct mt7996_tm_rx_event_stat_all *rx_stats_all;
-+	struct sk_buff *skb;
-+	enum mt76_rxq_id qid;
-+	int i, ret = 0;
-+	u32 mac_rx_mdrdy_cnt;
-+	u16 mac_rx_len_mismatch, fcs_err_count;
-+
-+	if (td->state != MT76_TM_STATE_RX_FRAMES)
-+		return 0;
-+
-+	ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(TESTMODE_RX_STAT),
-+					&req, sizeof(req), true, &skb);
-+
-+	if (ret)
-+		return ret;
-+
-+	rx_stats = (struct mt7996_tm_rx_event *)skb->data;
-+	rx_stats_all = &rx_stats->rx_stat_all;
-+
-+	phy->test.last_freq_offset = le32_to_cpu(rx_stats_all->user_info[0].freq_offset);
-+	phy->test.last_snr = le32_to_cpu(rx_stats_all->user_info[0].snr);
-+	for (i = 0; i < ARRAY_SIZE(phy->test.last_rcpi); i++) {
-+		phy->test.last_rcpi[i] = le16_to_cpu(rx_stats_all->rxv_info[i].rcpi);
-+		phy->test.last_rssi[i] = le16_to_cpu(rx_stats_all->rxv_info[i].rssi);
-+		phy->test.last_ib_rssi[i] = rx_stats_all->fagc[i].ib_rssi;
-+		phy->test.last_wb_rssi[i] = rx_stats_all->fagc[i].wb_rssi;
-+	}
-+
-+	if (phy->mt76->band_idx == 2)
-+		qid = MT_RXQ_BAND2;
-+	else if (phy->mt76->band_idx == 1)
-+		qid = MT_RXQ_BAND1;
-+	else
-+		qid = MT_RXQ_MAIN;
-+
-+	fcs_err_count = le16_to_cpu(rx_stats_all->band_info.mac_rx_fcs_err_cnt);
-+	mac_rx_len_mismatch = le16_to_cpu(rx_stats_all->band_info.mac_rx_len_mismatch);
-+	mac_rx_mdrdy_cnt = le32_to_cpu(rx_stats_all->band_info.mac_rx_mdrdy_cnt);
-+	td->rx_stats.packets[qid] += mac_rx_mdrdy_cnt;
-+	td->rx_stats.packets[qid] += fcs_err_count;
-+	td->rx_stats.fcs_error[qid] += fcs_err_count;
-+	td->rx_stats.len_mismatch += mac_rx_len_mismatch;
-+
-+	dev_kfree_skb(skb);
-+
-+	return ret;
-+}
-+
-+static void
-+mt7996_tm_reset_trx_stats(struct mt76_phy *mphy)
-+{
-+	struct mt7996_phy *phy = mphy->priv;
-+	struct mt7996_dev *dev = phy->dev;
-+
-+	memset(&mphy->test.rx_stats, 0, sizeof(mphy->test.rx_stats));
-+	mt7996_tm_set(dev, SET_ID(TRX_COUNTER_RESET), 0);
-+}
-+
-+static int
-+mt7996_tm_get_tx_stats(struct mt7996_phy *phy)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt76_testmode_data *td = &phy->mt76->test;
-+	int ret;
-+
-+	if (td->state != MT76_TM_STATE_TX_FRAMES)
-+		return 0;
-+
-+	ret = mt7996_tm_get(dev, GET_ID(TXED_COUNT), 0, &td->tx_done);
-+	if (ret)
-+		return ret;
-+
-+	td->tx_pending = td->tx_count - td->tx_done;
-+
-+	return ret;
-+}
-+
-+static int
-+mt7996_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg)
-+{
-+	struct mt7996_phy *phy = mphy->priv;
-+	void *rx, *rssi;
-+	int i;
-+
-+	mt7996_tm_set(phy->dev, SET_ID(BAND_IDX), mphy->band_idx);
-+	mt7996_tm_get_rx_stats(phy);
-+	mt7996_tm_get_tx_stats(phy);
-+
-+	rx = nla_nest_start(msg, MT76_TM_STATS_ATTR_LAST_RX);
-+	if (!rx)
-+		return -ENOMEM;
-+
-+	if (nla_put_s32(msg, MT76_TM_RX_ATTR_FREQ_OFFSET, phy->test.last_freq_offset))
-+		return -ENOMEM;
-+
-+	rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_RCPI);
-+	if (!rssi)
-+		return -ENOMEM;
-+
-+	for (i = 0; i < ARRAY_SIZE(phy->test.last_rcpi); i++)
-+		if (nla_put_u8(msg, i, phy->test.last_rcpi[i]))
-+			return -ENOMEM;
-+
-+	nla_nest_end(msg, rssi);
-+
-+	rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_RSSI);
-+	if (!rssi)
-+		return -ENOMEM;
-+
-+	for (i = 0; i < ARRAY_SIZE(phy->test.last_rssi); i++)
-+		if (nla_put_s8(msg, i, phy->test.last_rssi[i]))
-+			return -ENOMEM;
-+
-+	nla_nest_end(msg, rssi);
-+
-+	rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_IB_RSSI);
-+	if (!rssi)
-+		return -ENOMEM;
-+
-+	for (i = 0; i < ARRAY_SIZE(phy->test.last_ib_rssi); i++)
-+		if (nla_put_s8(msg, i, phy->test.last_ib_rssi[i]))
-+			return -ENOMEM;
-+
-+	nla_nest_end(msg, rssi);
-+
-+	rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_WB_RSSI);
-+	if (!rssi)
-+		return -ENOMEM;
-+
-+	for (i = 0; i < ARRAY_SIZE(phy->test.last_wb_rssi); i++)
-+		if (nla_put_s8(msg, i, phy->test.last_wb_rssi[i]))
-+			return -ENOMEM;
-+
-+	nla_nest_end(msg, rssi);
-+
-+	if (nla_put_u8(msg, MT76_TM_RX_ATTR_SNR, phy->test.last_snr))
-+		return -ENOMEM;
-+
-+	nla_nest_end(msg, rx);
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_tm_write_back_to_efuse(struct mt7996_dev *dev)
-+{
-+	struct mt7996_mcu_eeprom_info req = {
-+		.tag = cpu_to_le16(UNI_EFUSE_ACCESS),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+	};
-+	u8 read_buf[MT76_TM_EEPROM_BLOCK_SIZE], *eeprom = dev->mt76.eeprom.data;
-+	int i, ret = -EINVAL;
-+
-+	/* prevent from damaging chip id in efuse */
-+	if (mt76_chip(&dev->mt76) != get_unaligned_le16(eeprom))
-+		goto out;
-+
-+	for (i = 0; i < MT7996_EEPROM_SIZE; i += MT76_TM_EEPROM_BLOCK_SIZE) {
-+		req.addr = cpu_to_le32(i);
-+		memcpy(req.data, eeprom + i, MT76_TM_EEPROM_BLOCK_SIZE);
-+
-+		ret = mt7996_mcu_get_eeprom(dev, i, read_buf);
-+		if (ret < 0)
-+			return ret;
-+
-+		if (!memcmp(req.data, read_buf, MT76_TM_EEPROM_BLOCK_SIZE))
-+			continue;
-+
-+		ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(EFUSE_CTRL),
-+					&req, sizeof(req), true);
-+		if (ret)
-+			return ret;
-+	}
-+
-+out:
-+	return ret;
-+}
-+
-+static int
-+mt7996_tm_set_eeprom(struct mt76_phy *mphy, u32 offset, u8 *val, u8 action)
-+{
-+	struct mt7996_phy *phy = mphy->priv;
-+	struct mt7996_dev *dev = phy->dev;
-+	u8 *eeprom = dev->mt76.eeprom.data;
-+	int ret = 0;
-+
-+	if (offset >= MT7996_EEPROM_SIZE)
-+		return -EINVAL;
-+
-+	switch (action) {
-+	case MT76_TM_EEPROM_ACTION_UPDATE_DATA:
-+		memcpy(eeprom + offset, val, MT76_TM_EEPROM_BLOCK_SIZE);
-+		break;
-+	case MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE:
-+		ret = mt7996_mcu_set_eeprom(dev);
-+		break;
-+	case MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE:
-+		ret = mt7996_tm_write_back_to_efuse(dev);
-+		break;
-+	default:
-+		break;
-+	}
-+
-+	return ret;
-+}
-+
-+const struct mt76_testmode_ops mt7996_testmode_ops = {
-+	.set_state = mt7996_tm_set_state,
-+	.set_params = mt7996_tm_set_params,
-+	.dump_stats = mt7996_tm_dump_stats,
-+	.reset_rx_stats = mt7996_tm_reset_trx_stats,
-+	.tx_stop = mt7996_tm_tx_stop,
-+	.set_eeprom = mt7996_tm_set_eeprom,
-+};
-diff --git a/mt7996/testmode.h b/mt7996/testmode.h
-new file mode 100644
-index 0000000..319ef25
---- /dev/null
-+++ b/mt7996/testmode.h
-@@ -0,0 +1,299 @@
-+/* SPDX-License-Identifier: ISC */
-+/* Copyright (C) 2020 MediaTek Inc. */
-+
-+#ifndef __MT7996_TESTMODE_H
-+#define __MT7996_TESTMODE_H
-+
-+enum {
-+	TM_CBW_20MHZ,
-+	TM_CBW_40MHZ,
-+	TM_CBW_80MHZ,
-+	TM_CBW_10MHZ,
-+	TM_CBW_5MHZ,
-+	TM_CBW_160MHZ,
-+	TM_CBW_8080MHZ,
-+	TM_CBW_320MHZ = 12,
-+};
-+
-+/* BW defined in FW hal_cal_flow_rom.h */
-+enum {
-+	FW_CDBW_20MHZ,
-+	FW_CDBW_40MHZ,
-+	FW_CDBW_80MHZ,
-+	FW_CDBW_160MHZ,
-+	FW_CDBW_320MHZ,
-+	FW_CDBW_5MHZ,
-+	FW_CDBW_10MHZ,
-+	FW_CDBW_8080MHZ,
-+};
-+
-+enum bw_mapping_method {
-+	BW_MAP_NL_TO_FW,
-+	BW_MAP_NL_TO_TM,
-+
-+	NUM_BW_MAP,
-+};
-+
-+struct mt7996_tm_rf_test {
-+	__le16 tag;
-+	__le16 len;
-+
-+	u8 action;
-+	u8 icap_len;
-+	u8 _rsv[2];
-+	union {
-+		__le32 op_mode;
-+		__le32 freq;
-+
-+		struct {
-+			__le32 func_idx;
-+			union {
-+				__le32 func_data;
-+				__le32 cal_dump;
-+
-+				u8 _pad[80];
-+			} param;
-+		} rf;
-+	} op;
-+} __packed;
-+
-+struct mt7996_tm_req {
-+	u8 _rsv[4];
-+
-+	struct mt7996_tm_rf_test rf_test;
-+} __packed;
-+
-+struct mt7996_tm_rf_test_result {
-+	__le32 func_idx;
-+	__le32 payload_length;
-+	u8 event[0];
-+};
-+
-+struct mt7996_tm_event {
-+	u8 _rsv[4];
-+
-+	__le16 tag;
-+	__le16 len;
-+	struct mt7996_tm_rf_test_result result;
-+} __packed;
-+
-+enum {
-+	RF_ACTION_SWITCH_TO_RF_TEST,
-+	RF_ACTION_IN_RF_TEST,
-+	RF_ACTION_SET = 3,
-+	RF_ACTION_GET,
-+};
-+
-+enum {
-+	RF_OPER_NORMAL,
-+	RF_OPER_RF_TEST,
-+	RF_OPER_ICAP,
-+	RF_OPER_ICAP_OVERLAP,
-+	RF_OPER_WIFI_SPECTRUM,
-+};
-+
-+enum {
-+	UNI_RF_TEST_CTRL,
-+};
-+
-+#define RF_CMD(cmd)		RF_TEST_CMD_##cmd
-+
-+enum {
-+	RF_TEST_CMD_STOP_TEST = 0,
-+	RF_TEST_CMD_START_TX = 1,
-+	RF_TEST_CMD_START_RX = 2,
-+	RF_TEST_CMD_CONT_WAVE = 10,
-+	RF_TEST_CMD_TX_COMMIT = 18,
-+	RF_TEST_CMD_RX_COMMIT = 19,
-+};
-+
-+#define SET_ID(id)		RF_TEST_ID_SET_##id
-+#define GET_ID(id)		RF_TEST_ID_GET_##id
-+
-+enum {
-+	RF_TEST_ID_SET_COMMAND = 1,
-+	RF_TEST_ID_SET_POWER = 2,
-+	RF_TEST_ID_SET_TX_RATE = 3,
-+	RF_TEST_ID_SET_TX_MODE = 4,
-+	RF_TEST_ID_SET_TX_LEN = 6,
-+	RF_TEST_ID_SET_TX_COUNT = 7,
-+	RF_TEST_ID_SET_IPG = 8,
-+	RF_TEST_ID_SET_GI = 16,
-+	RF_TEST_ID_SET_STBC = 17,
-+	RF_TEST_ID_SET_CHAN_FREQ = 18,
-+	RF_TEST_ID_GET_TXED_COUNT = 32,
-+	RF_TEST_ID_SET_CONT_WAVE_MODE = 65,
-+	RF_TEST_ID_SET_DA = 68,
-+	RF_TEST_ID_SET_SA = 69,
-+	RF_TEST_ID_SET_CBW = 71,
-+	RF_TEST_ID_SET_DBW = 72,
-+	RF_TEST_ID_SET_PRIMARY_CH = 73,
-+	RF_TEST_ID_SET_ENCODE_MODE = 74,
-+	RF_TEST_ID_SET_BAND = 90,
-+	RF_TEST_ID_SET_TRX_COUNTER_RESET = 91,
-+	RF_TEST_ID_SET_MAC_HEADER = 101,
-+	RF_TEST_ID_SET_SEQ_CTRL = 102,
-+	RF_TEST_ID_SET_PAYLOAD = 103,
-+	RF_TEST_ID_SET_BAND_IDX = 104,
-+	RF_TEST_ID_SET_RX_PATH = 106,
-+	RF_TEST_ID_SET_FREQ_OFFSET = 107,
-+	RF_TEST_ID_GET_FREQ_OFFSET = 108,
-+	RF_TEST_ID_SET_TX_PATH = 113,
-+	RF_TEST_ID_SET_NSS = 114,
-+	RF_TEST_ID_SET_ANT_MASK = 115,
-+	RF_TEST_ID_SET_IBF_ENABLE = 126,
-+	RF_TEST_ID_SET_EBF_ENABLE = 127,
-+	RF_TEST_ID_GET_TX_POWER = 136,
-+	RF_TEST_ID_SET_RX_MU_AID = 157,
-+	RF_TEST_ID_SET_HW_TX_MODE = 167,
-+	RF_TEST_ID_SET_PUNCTURE = 168,
-+	RF_TEST_ID_SET_FREQ_OFFSET_C2 = 171,
-+	RF_TEST_ID_GET_FREQ_OFFSET_C2 = 172,
-+	RF_TEST_ID_SET_CFG_ON = 176,
-+	RF_TEST_ID_SET_CFG_OFF = 177,
-+	RF_TEST_ID_SET_BSSID = 189,
-+	RF_TEST_ID_SET_TX_TIME = 190,
-+	RF_TEST_ID_SET_MAX_PE = 191,
-+	RF_TEST_ID_SET_AID_OFFSET = 204,
-+};
-+
-+#define POWER_CTRL(type)	UNI_TXPOWER_##type##_CTRL
-+
-+struct mt7996_tm_rx_stat_user_ctrl {
-+	__le16 tag;
-+	__le16 len;
-+
-+	u8 band_idx;
-+	u8 rsv;
-+	__le16 user_idx;
-+} __packed;
-+
-+struct mt7996_tm_rx_stat_all {
-+	__le16 tag;
-+	__le16 len;
-+
-+	u8 band_idx;
-+	u8 rsv[3];
-+} __packed;
-+
-+struct mt7996_tm_rx_req {
-+	u8 band;
-+	u8 _rsv[3];
-+
-+	union {
-+		struct mt7996_tm_rx_stat_user_ctrl user_ctrl;
-+		struct mt7996_tm_rx_stat_all rx_stat_all;
-+	};
-+} __packed;
-+
-+enum {
-+	UNI_TM_RX_STAT_SET_USER_CTRL = 7,
-+	UNI_TM_RX_STAT_GET_ALL_V2 = 9,
-+};
-+
-+struct rx_band_info {
-+	/* mac part */
-+	__le16 mac_rx_fcs_err_cnt;
-+	__le16 mac_rx_len_mismatch;
-+	__le16 mac_rx_fcs_ok_cnt;
-+	u8 rsv1[2];
-+	__le32 mac_rx_mdrdy_cnt;
-+
-+	/* phy part */
-+	__le16 phy_rx_fcs_err_cnt_cck;
-+	__le16 phy_rx_fcs_err_cnt_ofdm;
-+	__le16 phy_rx_pd_cck;
-+	__le16 phy_rx_pd_ofdm;
-+	__le16 phy_rx_sig_err_cck;
-+	__le16 phy_rx_sfd_err_cck;
-+	__le16 phy_rx_sig_err_ofdm;
-+	__le16 phy_rx_tag_err_ofdm;
-+	__le16 phy_rx_mdrdy_cnt_cck;
-+	__le16 phy_rx_mdrdy_cnt_ofdm;
-+} __packed;
-+
-+struct rx_band_info_ext {
-+	/* mac part */
-+	__le32 mac_rx_mpdu_cnt;
-+
-+	/* phy part */
-+	u8 rsv[4];
-+} __packed;
-+
-+struct rx_common_info {
-+	__le16 rx_fifo_full;
-+	u8 rsv[2];
-+	__le32 aci_hit_low;
-+	__le32 aci_hit_high;
-+} __packed;
-+
-+struct rx_common_info_ext {
-+	__le32 driver_rx_count;
-+	__le32 sinr;
-+	__le32 mu_pkt_count;
-+
-+	/* mac part */
-+	u8 _rsv[4];
-+
-+	/* phy part */
-+	u8 sig_mcs;
-+	u8 rsv[3];
-+} __packed;
-+
-+struct rx_rxv_info {
-+	__le16 rcpi;
-+	s16 rssi;
-+	s16 snr;
-+	s16 adc_rssi;
-+} __packed;
-+
-+struct rx_rssi_info {
-+	s8 ib_rssi;
-+	s8 wb_rssi;
-+	u8 rsv[2];
-+} __packed;
-+
-+struct rx_user_info {
-+	s32 freq_offset;
-+	s32 snr;
-+	__le32 fcs_err_count;
-+} __packed;
-+
-+struct rx_user_info_ext {
-+	s8 ne_var_db_all_user;
-+	u8 rsv[3];
-+} __packed;
-+
-+#define MAX_ANTENNA_NUM		8
-+#define MAX_USER_NUM		16
-+
-+struct mt7996_tm_rx_event_stat_all {
-+	__le16 tag;
-+	__le16 len;
-+
-+	struct rx_band_info band_info;
-+	struct rx_band_info_ext band_info_ext;
-+	struct rx_common_info common_info;
-+	struct rx_common_info_ext common_info_ext;
-+
-+	/* RXV info */
-+	struct rx_rxv_info rxv_info[MAX_ANTENNA_NUM];
-+
-+	/* RSSI info */
-+	struct rx_rssi_info fagc[MAX_ANTENNA_NUM];
-+	struct rx_rssi_info inst[MAX_ANTENNA_NUM];
-+
-+	/* User info */
-+	struct rx_user_info user_info[MAX_USER_NUM];
-+	struct rx_user_info_ext user_info_ext[MAX_USER_NUM];
-+} __packed;
-+
-+struct mt7996_tm_rx_event {
-+	u8 _rsv[4];
-+
-+	union {
-+		struct mt7996_tm_rx_event_stat_all rx_stat_all;
-+	};
-+} __packed;
-+
-+#endif
-diff --git a/testmode.c b/testmode.c
-index ca4fecc..44f3a5b 100644
---- a/testmode.c
-+++ b/testmode.c
-@@ -2,11 +2,13 @@
- /* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
- 
- #include <linux/random.h>
-+#include "mt76_connac.h"
- #include "mt76.h"
- 
- const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
- 	[MT76_TM_ATTR_RESET] = { .type = NLA_FLAG },
- 	[MT76_TM_ATTR_STATE] = { .type = NLA_U8 },
-+	[MT76_TM_ATTR_SKU_EN] = { .type = NLA_U8 },
- 	[MT76_TM_ATTR_TX_COUNT] = { .type = NLA_U32 },
- 	[MT76_TM_ATTR_TX_LENGTH] = { .type = NLA_U32 },
- 	[MT76_TM_ATTR_TX_RATE_MODE] = { .type = NLA_U8 },
-@@ -82,6 +84,11 @@ mt76_testmode_max_mpdu_len(struct mt76_phy *phy, u8 tx_rate_mode)
- 		    IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991)
- 			return IEEE80211_MAX_MPDU_LEN_VHT_7991;
- 		return IEEE80211_MAX_MPDU_LEN_VHT_11454;
-+	case MT76_TM_TX_MODE_EHT_SU:
-+	case MT76_TM_TX_MODE_EHT_TRIG:
-+	case MT76_TM_TX_MODE_EHT_MU:
-+		/* TODO: check the limit */
-+		return UINT_MAX;
- 	case MT76_TM_TX_MODE_CCK:
- 	case MT76_TM_TX_MODE_OFDM:
- 	default:
-@@ -183,6 +190,9 @@ mt76_testmode_tx_init(struct mt76_phy *phy)
- 	u8 max_nss = hweight8(phy->antenna_mask);
- 	int ret;
- 
-+	if (is_mt7996(phy->dev))
-+		return 0;
-+
- 	ret = mt76_testmode_alloc_skb(phy, td->tx_mpdu_len);
- 	if (ret)
- 		return ret;
-@@ -275,7 +285,9 @@ mt76_testmode_tx_start(struct mt76_phy *phy)
- 	td->tx_queued = 0;
- 	td->tx_done = 0;
- 	td->tx_pending = td->tx_count;
--	mt76_worker_schedule(&dev->tx_worker);
-+
-+	if (!is_mt7996(dev))
-+		mt76_worker_schedule(&dev->tx_worker);
- }
- 
- static void
-@@ -284,6 +296,11 @@ mt76_testmode_tx_stop(struct mt76_phy *phy)
- 	struct mt76_testmode_data *td = &phy->test;
- 	struct mt76_dev *dev = phy->dev;
- 
-+	if (is_mt7996(dev) && dev->test_ops->tx_stop) {
-+		dev->test_ops->tx_stop(phy);
-+		return;
-+	}
-+
- 	mt76_worker_disable(&dev->tx_worker);
- 
- 	td->tx_pending = 0;
-@@ -296,22 +313,11 @@ mt76_testmode_tx_stop(struct mt76_phy *phy)
- 	mt76_testmode_free_skb(phy);
- }
- 
--static inline void
--mt76_testmode_param_set(struct mt76_testmode_data *td, u16 idx)
--{
--	td->param_set[idx / 32] |= BIT(idx % 32);
--}
--
--static inline bool
--mt76_testmode_param_present(struct mt76_testmode_data *td, u16 idx)
--{
--	return td->param_set[idx / 32] & BIT(idx % 32);
--}
--
- static void
- mt76_testmode_init_defaults(struct mt76_phy *phy)
- {
- 	struct mt76_testmode_data *td = &phy->test;
-+	u8 addr[ETH_ALEN] = {phy->band_idx, 0x11, 0x22, 0xaa, 0xbb, 0xcc};
- 
- 	if (td->tx_mpdu_len > 0)
- 		return;
-@@ -319,11 +325,18 @@ mt76_testmode_init_defaults(struct mt76_phy *phy)
- 	td->tx_mpdu_len = 1024;
- 	td->tx_count = 1;
- 	td->tx_rate_mode = MT76_TM_TX_MODE_OFDM;
-+	td->tx_rate_idx = 7;
- 	td->tx_rate_nss = 1;
-+	/* 0xffff for OFDMA no puncture */
-+	td->tx_preamble_puncture = ~(td->tx_preamble_puncture & 0);
-+	td->tx_ipg = 50;
- 
--	memcpy(td->addr[0], phy->macaddr, ETH_ALEN);
--	memcpy(td->addr[1], phy->macaddr, ETH_ALEN);
--	memcpy(td->addr[2], phy->macaddr, ETH_ALEN);
-+	/* rx stat user config */
-+	td->aid = 1;
-+
-+	memcpy(td->addr[0], addr, ETH_ALEN);
-+	memcpy(td->addr[1], addr, ETH_ALEN);
-+	memcpy(td->addr[2], addr, ETH_ALEN);
- }
- 
- static int
-@@ -353,7 +366,7 @@ __mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state)
- 	if (state == MT76_TM_STATE_TX_FRAMES)
- 		mt76_testmode_tx_start(phy);
- 	else if (state == MT76_TM_STATE_RX_FRAMES) {
--		memset(&phy->test.rx_stats, 0, sizeof(phy->test.rx_stats));
-+		dev->test_ops->reset_rx_stats(phy);
- 	}
- 
- 	phy->test.state = state;
-@@ -404,6 +417,44 @@ mt76_tm_get_u8(struct nlattr *attr, u8 *dest, u8 min, u8 max)
- 	return 0;
- }
- 
-+static int
-+mt76_testmode_set_eeprom(struct mt76_phy *phy, struct nlattr **tb)
-+{
-+	struct mt76_dev *dev = phy->dev;
-+	u8 action, val[MT76_TM_EEPROM_BLOCK_SIZE];
-+	u32 offset = 0;
-+	int err = -EINVAL;
-+
-+	if (!dev->test_ops->set_eeprom)
-+		return -EOPNOTSUPP;
-+
-+	if (mt76_tm_get_u8(tb[MT76_TM_ATTR_EEPROM_ACTION], &action,
-+			   0, MT76_TM_EEPROM_ACTION_MAX))
-+		goto out;
-+
-+	if (tb[MT76_TM_ATTR_EEPROM_OFFSET]) {
-+		struct nlattr *cur;
-+		int rem, idx = 0;
-+
-+		offset = nla_get_u32(tb[MT76_TM_ATTR_EEPROM_OFFSET]);
-+		if (!!(offset % MT76_TM_EEPROM_BLOCK_SIZE) ||
-+		    !tb[MT76_TM_ATTR_EEPROM_VAL])
-+			goto out;
-+
-+		nla_for_each_nested(cur, tb[MT76_TM_ATTR_EEPROM_VAL], rem) {
-+			if (nla_len(cur) != 1 || idx >= ARRAY_SIZE(val))
-+				goto out;
-+
-+			val[idx++] = nla_get_u8(cur);
-+		}
-+	}
-+
-+	err = dev->test_ops->set_eeprom(phy, offset, val, action);
-+
-+out:
-+	return err;
-+}
-+
- int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 		      void *data, int len)
- {
-@@ -427,6 +478,11 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 
- 	mutex_lock(&dev->mutex);
- 
-+	if (tb[MT76_TM_ATTR_EEPROM_ACTION]) {
-+		err = mt76_testmode_set_eeprom(phy, tb);
-+		goto out;
-+	}
-+
- 	if (tb[MT76_TM_ATTR_RESET]) {
- 		mt76_testmode_set_state(phy, MT76_TM_STATE_OFF);
- 		memset(td, 0, sizeof(*td));
-@@ -434,6 +490,9 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 
- 	mt76_testmode_init_defaults(phy);
- 
-+	if (tb[MT76_TM_ATTR_SKU_EN])
-+		td->sku_en = nla_get_u8(tb[MT76_TM_ATTR_SKU_EN]);
-+
- 	if (tb[MT76_TM_ATTR_TX_COUNT])
- 		td->tx_count = nla_get_u32(tb[MT76_TM_ATTR_TX_COUNT]);
- 
-@@ -454,7 +513,8 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_DUTY_CYCLE],
- 			   &td->tx_duty_cycle, 0, 99) ||
- 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_POWER_CONTROL],
--			   &td->tx_power_control, 0, 1))
-+			   &td->tx_power_control, 0, 1) ||
-+	    mt76_tm_get_u8(tb[MT76_TM_ATTR_AID], &td->aid, 0, 16))
- 		goto out;
- 
- 	if (tb[MT76_TM_ATTR_TX_LENGTH]) {
-@@ -494,7 +554,9 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 			    idx >= ARRAY_SIZE(td->tx_power))
- 				goto out;
- 
--			td->tx_power[idx++] = nla_get_u8(cur);
-+			err = mt76_tm_get_u8(cur, &td->tx_power[idx++], 0, 63);
-+			if (err)
-+				return err;
- 		}
- 	}
- 
-@@ -512,6 +574,22 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 		}
- 	}
- 
-+	if (tb[MT76_TM_ATTR_CFG]) {
-+		struct nlattr *cur;
-+		int rem, idx = 0;
-+
-+		nla_for_each_nested(cur, tb[MT76_TM_ATTR_CFG], rem) {
-+			if (nla_len(cur) != 1 || idx >= 2)
-+				goto out;
-+
-+			if (idx == 0)
-+				td->cfg.type = nla_get_u8(cur);
-+			else
-+				td->cfg.enable = nla_get_u8(cur);
-+			idx++;
-+		}
-+	}
-+
- 	if (dev->test_ops->set_params) {
- 		err = dev->test_ops->set_params(phy, tb, state);
- 		if (err)
-@@ -561,6 +639,9 @@ mt76_testmode_dump_stats(struct mt76_phy *phy, struct sk_buff *msg)
- 	    nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_PACKETS, rx_packets,
- 			      MT76_TM_STATS_ATTR_PAD) ||
- 	    nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_FCS_ERROR, rx_fcs_error,
-+			      MT76_TM_STATS_ATTR_PAD) ||
-+	    nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_LEN_MISMATCH,
-+			      td->rx_stats.len_mismatch,
- 			      MT76_TM_STATS_ATTR_PAD))
- 		return -EMSGSIZE;
- 
-@@ -613,7 +694,8 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
- 
- 	if (dev->test_mtd.name &&
- 	    (nla_put_string(msg, MT76_TM_ATTR_MTD_PART, dev->test_mtd.name) ||
--	     nla_put_u32(msg, MT76_TM_ATTR_MTD_OFFSET, dev->test_mtd.offset)))
-+	     nla_put_u32(msg, MT76_TM_ATTR_MTD_OFFSET, dev->test_mtd.offset) ||
-+	     nla_put_u8(msg, MT76_TM_ATTR_BAND_IDX, phy->band_idx)))
- 		goto out;
- 
- 	if (nla_put_u32(msg, MT76_TM_ATTR_TX_COUNT, td->tx_count) ||
-@@ -624,6 +706,8 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
- 	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_SGI, td->tx_rate_sgi) ||
- 	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_LDPC, td->tx_rate_ldpc) ||
- 	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_STBC, td->tx_rate_stbc) ||
-+	    nla_put_u8(msg, MT76_TM_ATTR_SKU_EN, td->sku_en) ||
-+	    nla_put_u8(msg, MT76_TM_ATTR_AID, td->aid) ||
- 	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_LTF) &&
- 	     nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) ||
- 	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA) &&
-@@ -639,7 +723,7 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
- 	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER_CONTROL) &&
- 	     nla_put_u8(msg, MT76_TM_ATTR_TX_POWER_CONTROL, td->tx_power_control)) ||
- 	    (mt76_testmode_param_present(td, MT76_TM_ATTR_FREQ_OFFSET) &&
--	     nla_put_u8(msg, MT76_TM_ATTR_FREQ_OFFSET, td->freq_offset)))
-+	     nla_put_u32(msg, MT76_TM_ATTR_FREQ_OFFSET, td->freq_offset)))
- 		goto out;
- 
- 	if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER)) {
-diff --git a/testmode.h b/testmode.h
-index 5e2792d..96872e8 100644
---- a/testmode.h
-+++ b/testmode.h
-@@ -5,7 +5,8 @@
- #ifndef __MT76_TESTMODE_H
- #define __MT76_TESTMODE_H
- 
--#define MT76_TM_TIMEOUT	10
-+#define MT76_TM_TIMEOUT			10
-+#define MT76_TM_EEPROM_BLOCK_SIZE	16
- 
- /**
-  * enum mt76_testmode_attr - testmode attributes inside NL80211_ATTR_TESTDATA
-@@ -17,7 +18,9 @@
-  *
-  * @MT76_TM_ATTR_MTD_PART: mtd partition used for eeprom data (string)
-  * @MT76_TM_ATTR_MTD_OFFSET: offset of eeprom data within the partition (u32)
-+ * @MT76_TM_ATTR_BAND_IDX: band idx of the chip (u8)
-  *
-+ * @MT76_TM_ATTR_SKU_EN: config txpower sku is enabled or disabled in testmode (u8)
-  * @MT76_TM_ATTR_TX_COUNT: configured number of frames to send when setting
-  *	state to MT76_TM_STATE_TX_FRAMES (u32)
-  * @MT76_TM_ATTR_TX_PENDING: pending frames during MT76_TM_STATE_TX_FRAMES (u32)
-@@ -38,6 +41,11 @@
-  *
-  * @MT76_TM_ATTR_STATS: statistics (nested, see &enum mt76_testmode_stats_attr)
-  *
-+ * @MT76_TM_ATTR_PRECAL: Pre-cal data (u8)
-+ * @MT76_TM_ATTR_PRECAL_INFO: group size, dpd size, dpd_info, transmit size,
-+ *                            eeprom cal indicator (u32),
-+ *                            dpd_info = [dpd_per_chan_size, chan_num_2g,
-+ *                                        chan_num_5g, chan_num_6g]
-  * @MT76_TM_ATTR_TX_SPE_IDX: tx spatial extension index (u8)
-  *
-  * @MT76_TM_ATTR_TX_DUTY_CYCLE: packet tx duty cycle (u8)
-@@ -47,6 +55,29 @@
-  * @MT76_TM_ATTR_DRV_DATA: driver specific netlink attrs (nested)
-  *
-  * @MT76_TM_ATTR_MAC_ADDRS: array of nested MAC addresses (nested)
-+ *
-+ * @MT76_TM_ATTR_EEPROM_ACTION: eeprom setting actions
-+ *	(u8, see &enum mt76_testmode_eeprom_action)
-+ * @MT76_TM_ATTR_EEPROM_OFFSET: offset of eeprom data block for writing (u32)
-+ * @MT76_TM_ATTR_EEPROM_VAL: values for writing into a 16-byte data block
-+ *	(nested, u8 attrs)
-+ *
-+ * @MT76_TM_ATTR_CFG: config testmode rf feature (nested, see &mt76_testmode_cfg)
-+ * @MT76_TM_ATTR_TXBF_ACT: txbf setting actions (u8)
-+ * @MT76_TM_ATTR_TXBF_PARAM: txbf parameters (nested)
-+ *
-+ * @MT76_TM_ATTR_OFF_CH_SCAN_CH: config the channel of background chain (ZWDFS) (u8)
-+ * @MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH: config the center channel of background chain (ZWDFS) (u8)
-+ * @MT76_TM_ATTR_OFF_CH_SCAN_BW: config the bandwidth of background chain (ZWDFS) (u8)
-+ * @MT76_TM_ATTR_OFF_CH_SCAN_PATH: config the tx path of background chain (ZWDFS) (u8)
-+ *
-+ * @MT76_TM_ATTR_IPI_THRESHOLD: config the IPI index you want to read (u8)
-+ * @MT76_TM_ATTR_IPI_PERIOD: config the time period for reading
-+ *			     the histogram of specific IPI index (u8)
-+ * @MT76_TM_ATTR_IPI_ANTENNA_INDEX: config the antenna index for reading
-+ *				    the histogram of specific IPI index (u8)
-+ * @MT76_TM_ATTR_IPI_RESET: Reset the IPI counter
-+ *
-  */
- enum mt76_testmode_attr {
- 	MT76_TM_ATTR_UNSPEC,
-@@ -56,7 +87,9 @@ enum mt76_testmode_attr {
- 
- 	MT76_TM_ATTR_MTD_PART,
- 	MT76_TM_ATTR_MTD_OFFSET,
-+	MT76_TM_ATTR_BAND_IDX,
- 
-+	MT76_TM_ATTR_SKU_EN,
- 	MT76_TM_ATTR_TX_COUNT,
- 	MT76_TM_ATTR_TX_LENGTH,
- 	MT76_TM_ATTR_TX_RATE_MODE,
-@@ -74,6 +107,8 @@ enum mt76_testmode_attr {
- 	MT76_TM_ATTR_FREQ_OFFSET,
- 
- 	MT76_TM_ATTR_STATS,
-+	MT76_TM_ATTR_PRECAL,
-+	MT76_TM_ATTR_PRECAL_INFO,
- 
- 	MT76_TM_ATTR_TX_SPE_IDX,
- 
-@@ -84,6 +119,27 @@ enum mt76_testmode_attr {
- 	MT76_TM_ATTR_DRV_DATA,
- 
- 	MT76_TM_ATTR_MAC_ADDRS,
-+	MT76_TM_ATTR_AID,
-+	MT76_TM_ATTR_RU_ALLOC,
-+	MT76_TM_ATTR_RU_IDX,
-+
-+	MT76_TM_ATTR_EEPROM_ACTION,
-+	MT76_TM_ATTR_EEPROM_OFFSET,
-+	MT76_TM_ATTR_EEPROM_VAL,
-+
-+	MT76_TM_ATTR_CFG,
-+	MT76_TM_ATTR_TXBF_ACT,
-+	MT76_TM_ATTR_TXBF_PARAM,
-+
-+	MT76_TM_ATTR_OFF_CH_SCAN_CH,
-+	MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH,
-+	MT76_TM_ATTR_OFF_CH_SCAN_BW,
-+	MT76_TM_ATTR_OFF_CH_SCAN_PATH,
-+
-+	MT76_TM_ATTR_IPI_THRESHOLD,
-+	MT76_TM_ATTR_IPI_PERIOD,
-+	MT76_TM_ATTR_IPI_ANTENNA_INDEX,
-+	MT76_TM_ATTR_IPI_RESET,
- 
- 	/* keep last */
- 	NUM_MT76_TM_ATTRS,
-@@ -101,6 +157,8 @@ enum mt76_testmode_attr {
-  * @MT76_TM_STATS_ATTR_RX_FCS_ERROR: number of rx packets with FCS error (u64)
-  * @MT76_TM_STATS_ATTR_LAST_RX: information about the last received packet
-  *	see &enum mt76_testmode_rx_attr
-+ * @MT76_TM_STATS_ATTR_RX_LEN_MISMATCH: number of rx packets with length
-+ *	mismatch error (u64)
-  */
- enum mt76_testmode_stats_attr {
- 	MT76_TM_STATS_ATTR_UNSPEC,
-@@ -113,6 +171,7 @@ enum mt76_testmode_stats_attr {
- 	MT76_TM_STATS_ATTR_RX_PACKETS,
- 	MT76_TM_STATS_ATTR_RX_FCS_ERROR,
- 	MT76_TM_STATS_ATTR_LAST_RX,
-+	MT76_TM_STATS_ATTR_RX_LEN_MISMATCH,
- 
- 	/* keep last */
- 	NUM_MT76_TM_STATS_ATTRS,
-@@ -125,6 +184,7 @@ enum mt76_testmode_stats_attr {
-  *
-  * @MT76_TM_RX_ATTR_FREQ_OFFSET: frequency offset (s32)
-  * @MT76_TM_RX_ATTR_RCPI: received channel power indicator (array, u8)
-+ * @MT76_TM_RX_ATTR_RSSI: received signal strength indicator (array, s8)
-  * @MT76_TM_RX_ATTR_IB_RSSI: internal inband RSSI (array, s8)
-  * @MT76_TM_RX_ATTR_WB_RSSI: internal wideband RSSI (array, s8)
-  * @MT76_TM_RX_ATTR_SNR: signal-to-noise ratio (u8)
-@@ -134,6 +194,7 @@ enum mt76_testmode_rx_attr {
- 
- 	MT76_TM_RX_ATTR_FREQ_OFFSET,
- 	MT76_TM_RX_ATTR_RCPI,
-+	MT76_TM_RX_ATTR_RSSI,
- 	MT76_TM_RX_ATTR_IB_RSSI,
- 	MT76_TM_RX_ATTR_WB_RSSI,
- 	MT76_TM_RX_ATTR_SNR,
-@@ -177,6 +238,9 @@ enum mt76_testmode_state {
-  * @MT76_TM_TX_MODE_HE_EXT_SU: 802.11ax extended-range SU
-  * @MT76_TM_TX_MODE_HE_TB: 802.11ax trigger-based
-  * @MT76_TM_TX_MODE_HE_MU: 802.11ax multi-user MIMO
-+ * @MT76_TM_TX_MODE_EHT_SU: 802.11be single-user MIMO
-+ * @MT76_TM_TX_MODE_EHT_TRIG: 802.11be trigger-based
-+ * @MT76_TM_TX_MODE_EHT_MU: 802.11be multi-user MIMO
-  */
- enum mt76_testmode_tx_mode {
- 	MT76_TM_TX_MODE_CCK,
-@@ -187,12 +251,33 @@ enum mt76_testmode_tx_mode {
- 	MT76_TM_TX_MODE_HE_EXT_SU,
- 	MT76_TM_TX_MODE_HE_TB,
- 	MT76_TM_TX_MODE_HE_MU,
-+	MT76_TM_TX_MODE_EHT_SU,
-+	MT76_TM_TX_MODE_EHT_TRIG,
-+	MT76_TM_TX_MODE_EHT_MU,
- 
- 	/* keep last */
- 	NUM_MT76_TM_TX_MODES,
- 	MT76_TM_TX_MODE_MAX = NUM_MT76_TM_TX_MODES - 1,
- };
- 
-+/**
-+ * enum mt76_testmode_eeprom_action - eeprom setting actions
-+ *
-+ * @MT76_TM_EEPROM_ACTION_UPDATE_DATA: update rf values to specific
-+ *	eeprom data block
-+ * @MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE: send updated eeprom data to fw
-+ * @MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE: write eeprom data back to efuse
-+ */
-+enum mt76_testmode_eeprom_action {
-+	MT76_TM_EEPROM_ACTION_UPDATE_DATA,
-+	MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE,
-+	MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE,
-+
-+	/* keep last */
-+	NUM_MT76_TM_EEPROM_ACTION,
-+	MT76_TM_EEPROM_ACTION_MAX = NUM_MT76_TM_EEPROM_ACTION - 1,
-+};
-+
- extern const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS];
- 
- #endif
-diff --git a/tools/fields.c b/tools/fields.c
-index e3f6908..055f90f 100644
---- a/tools/fields.c
-+++ b/tools/fields.c
-@@ -10,6 +10,7 @@ static const char * const testmode_state[] = {
- 	[MT76_TM_STATE_IDLE] = "idle",
- 	[MT76_TM_STATE_TX_FRAMES] = "tx_frames",
- 	[MT76_TM_STATE_RX_FRAMES] = "rx_frames",
-+	[MT76_TM_STATE_TX_CONT] = "tx_cont",
- };
- 
- static const char * const testmode_tx_mode[] = {
-@@ -21,6 +22,9 @@ static const char * const testmode_tx_mode[] = {
- 	[MT76_TM_TX_MODE_HE_EXT_SU] = "he_ext_su",
- 	[MT76_TM_TX_MODE_HE_TB] = "he_tb",
- 	[MT76_TM_TX_MODE_HE_MU] = "he_mu",
-+	[MT76_TM_TX_MODE_EHT_SU] = "eht_su",
-+	[MT76_TM_TX_MODE_EHT_TRIG] = "eht_tb",
-+	[MT76_TM_TX_MODE_EHT_MU] = "eht_mu",
- };
- 
- static void print_enum(const struct tm_field *field, struct nlattr *attr)
-@@ -65,7 +69,7 @@ static bool parse_u8(const struct tm_field *field, int idx,
- 
- static void print_u8(const struct tm_field *field, struct nlattr *attr)
- {
--	printf("%d", nla_get_u8(attr));
-+	printf("%u", nla_get_u8(attr));
- }
- 
- static void print_s8(const struct tm_field *field, struct nlattr *attr)
-@@ -86,12 +90,12 @@ static void print_s32(const struct tm_field *field, struct nlattr *attr)
- 
- static void print_u32(const struct tm_field *field, struct nlattr *attr)
- {
--	printf("%d", nla_get_u32(attr));
-+	printf("%u", nla_get_u32(attr));
- }
- 
- static void print_u64(const struct tm_field *field, struct nlattr *attr)
- {
--	printf("%lld", (unsigned long long)nla_get_u64(attr));
-+	printf("%llu", (unsigned long long)nla_get_u64(attr));
- }
- 
- static bool parse_flag(const struct tm_field *field, int idx,
-@@ -201,6 +205,62 @@ static void print_extra_stats(const struct tm_field *field, struct nlattr **tb)
- 	printf("%srx_per=%.02f%%\n", prefix, 100 * failed / total);
- }
- 
-+static bool parse_mac(const struct tm_field *field, int idx,
-+		      struct nl_msg *msg, const char *val)
-+{
-+#define ETH_ALEN	6
-+	bool ret = true;
-+	char *str, *cur, *ap;
-+	void *a;
-+
-+	str = strdup(val);
-+	ap = str;
-+
-+	a = nla_nest_start(msg, idx);
-+
-+	idx = 0;
-+	while ((cur = strsep(&ap, ",")) != NULL) {
-+		unsigned char addr[ETH_ALEN];
-+		char *val, *tmp = cur;
-+		int i = 0;
-+
-+		while ((val = strsep(&tmp, ":")) != NULL) {
-+			if (i >= ETH_ALEN)
-+				break;
-+
-+			addr[i++] = strtoul(val, NULL, 16);
-+		}
-+
-+		nla_put(msg, idx, ETH_ALEN, addr);
-+
-+		idx++;
-+	}
-+
-+	nla_nest_end(msg, a);
-+
-+	free(str);
-+
-+	return ret;
-+}
-+
-+static void print_mac(const struct tm_field *field, struct nlattr *attr)
-+{
-+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
-+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
-+	unsigned char addr[3][6];
-+	struct nlattr *cur;
-+	int idx = 0;
-+	int rem;
-+
-+	nla_for_each_nested(cur, attr, rem) {
-+		if (nla_len(cur) != 6)
-+			continue;
-+		memcpy(addr[idx++], nla_data(cur), 6);
-+	}
-+
-+	printf("" MACSTR "," MACSTR "," MACSTR "",
-+	       MAC2STR(addr[0]), MAC2STR(addr[1]), MAC2STR(addr[2]));
-+}
- 
- #define FIELD_GENERIC(_field, _name, ...)	\
- 	[FIELD_NAME(_field)] = {			\
-@@ -250,10 +310,18 @@ static void print_extra_stats(const struct tm_field *field, struct nlattr **tb)
- 		 ##__VA_ARGS__				\
- 	)
- 
-+#define FIELD_MAC(_field, _name)			\
-+	[FIELD_NAME(_field)] = {			\
-+		.name = _name,				\
-+		.parse = parse_mac,			\
-+		.print = print_mac			\
-+	}
-+
- #define FIELD_NAME(_field) MT76_TM_RX_ATTR_##_field
- static const struct tm_field rx_fields[NUM_MT76_TM_RX_ATTRS] = {
- 	FIELD_RO(s32, FREQ_OFFSET, "freq_offset"),
- 	FIELD_ARRAY_RO(u8, RCPI, "rcpi"),
-+	FIELD_ARRAY_RO(s8, RSSI, "rssi"),
- 	FIELD_ARRAY_RO(s8, IB_RSSI, "ib_rssi"),
- 	FIELD_ARRAY_RO(s8, WB_RSSI, "wb_rssi"),
- 	FIELD_RO(s8, SNR, "snr"),
-@@ -261,6 +329,7 @@ static const struct tm_field rx_fields[NUM_MT76_TM_RX_ATTRS] = {
- static struct nla_policy rx_policy[NUM_MT76_TM_RX_ATTRS] = {
- 	[MT76_TM_RX_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
- 	[MT76_TM_RX_ATTR_RCPI] = { .type = NLA_NESTED },
-+	[MT76_TM_RX_ATTR_RSSI] = { .type = NLA_NESTED },
- 	[MT76_TM_RX_ATTR_IB_RSSI] = { .type = NLA_NESTED },
- 	[MT76_TM_RX_ATTR_WB_RSSI] = { .type = NLA_NESTED },
- 	[MT76_TM_RX_ATTR_SNR] = { .type = NLA_U8 },
-@@ -274,6 +343,7 @@ static const struct tm_field stats_fields[NUM_MT76_TM_STATS_ATTRS] = {
- 	FIELD_RO(u32, TX_DONE, "tx_done"),
- 	FIELD_RO(u64, RX_PACKETS, "rx_packets"),
- 	FIELD_RO(u64, RX_FCS_ERROR, "rx_fcs_error"),
-+	FIELD_RO(u64, RX_LEN_MISMATCH, "rx_len_mismatch"),
- 	FIELD_NESTED_RO(LAST_RX, rx, "last_"),
- };
- static struct nla_policy stats_policy[NUM_MT76_TM_STATS_ATTRS] = {
-@@ -282,6 +352,7 @@ static struct nla_policy stats_policy[NUM_MT76_TM_STATS_ATTRS] = {
- 	[MT76_TM_STATS_ATTR_TX_DONE] = { .type = NLA_U32 },
- 	[MT76_TM_STATS_ATTR_RX_PACKETS] = { .type = NLA_U64 },
- 	[MT76_TM_STATS_ATTR_RX_FCS_ERROR] = { .type = NLA_U64 },
-+	[MT76_TM_STATS_ATTR_RX_LEN_MISMATCH] = { .type = NLA_U64 },
- };
- #undef FIELD_NAME
- 
-@@ -291,6 +362,7 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
- 	FIELD_ENUM(STATE, "state", testmode_state),
- 	FIELD_RO(string, MTD_PART, "mtd_part"),
- 	FIELD_RO(u32, MTD_OFFSET, "mtd_offset"),
-+	FIELD(u8, SKU_EN, "sku_en"),
- 	FIELD(u32, TX_COUNT, "tx_count"),
- 	FIELD(u32, TX_LENGTH, "tx_length"),
- 	FIELD_ENUM(TX_RATE_MODE, "tx_rate_mode", testmode_tx_mode),
-@@ -300,12 +372,20 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
- 	FIELD(u8, TX_RATE_LDPC, "tx_rate_ldpc"),
- 	FIELD(u8, TX_RATE_STBC, "tx_rate_stbc"),
- 	FIELD(u8, TX_LTF, "tx_ltf"),
-+	FIELD(u8, TX_DUTY_CYCLE, "tx_duty_cycle"),
-+	FIELD(u32, TX_IPG, "tx_ipg"),
-+	FIELD(u32, TX_TIME, "tx_time"),
- 	FIELD(u8, TX_POWER_CONTROL, "tx_power_control"),
- 	FIELD_ARRAY(u8, TX_POWER, "tx_power"),
- 	FIELD(u8, TX_ANTENNA, "tx_antenna"),
- 	FIELD(u32, FREQ_OFFSET, "freq_offset"),
-+	FIELD(u8, AID, "aid"),
-+	FIELD(u8, RU_ALLOC, "ru_alloc"),
-+	FIELD(u8, RU_IDX, "ru_idx"),
-+	FIELD_MAC(MAC_ADDRS, "mac_addrs"),
- 	FIELD_NESTED_RO(STATS, stats, "",
- 			.print_extra = print_extra_stats),
-+
- };
- #undef FIELD_NAME
- 
-@@ -313,6 +393,7 @@ static struct nla_policy testdata_policy[NUM_MT76_TM_ATTRS] = {
- 	[MT76_TM_ATTR_STATE] = { .type = NLA_U8 },
- 	[MT76_TM_ATTR_MTD_PART] = { .type = NLA_STRING },
- 	[MT76_TM_ATTR_MTD_OFFSET] = { .type = NLA_U32 },
-+	[MT76_TM_ATTR_SKU_EN] = { .type = NLA_U8 },
- 	[MT76_TM_ATTR_TX_COUNT] = { .type = NLA_U32 },
- 	[MT76_TM_ATTR_TX_LENGTH] = { .type = NLA_U32 },
- 	[MT76_TM_ATTR_TX_RATE_MODE] = { .type = NLA_U8 },
-@@ -322,10 +403,25 @@ static struct nla_policy testdata_policy[NUM_MT76_TM_ATTRS] = {
- 	[MT76_TM_ATTR_TX_RATE_LDPC] = { .type = NLA_U8 },
- 	[MT76_TM_ATTR_TX_RATE_STBC] = { .type = NLA_U8 },
- 	[MT76_TM_ATTR_TX_LTF] = { .type = NLA_U8 },
-+	[MT76_TM_ATTR_TX_DUTY_CYCLE] = { .type = NLA_U8 },
-+	[MT76_TM_ATTR_TX_IPG] = { .type = NLA_U32 },
-+	[MT76_TM_ATTR_TX_TIME] = { .type = NLA_U32 },
- 	[MT76_TM_ATTR_TX_POWER_CONTROL] = { .type = NLA_U8 },
- 	[MT76_TM_ATTR_TX_ANTENNA] = { .type = NLA_U8 },
-+	[MT76_TM_ATTR_TX_SPE_IDX] = { .type = NLA_U8 },
- 	[MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
-+	[MT76_TM_ATTR_AID] = { .type = NLA_U8 },
-+	[MT76_TM_ATTR_RU_ALLOC] = { .type = NLA_U8 },
-+	[MT76_TM_ATTR_RU_IDX] = { .type = NLA_U8 },
- 	[MT76_TM_ATTR_STATS] = { .type = NLA_NESTED },
-+	[MT76_TM_ATTR_TXBF_ACT] = { .type = NLA_U8 },
-+	[MT76_TM_ATTR_OFF_CH_SCAN_CH] = { .type = NLA_U8 },
-+	[MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH] = { .type = NLA_U8 },
-+	[MT76_TM_ATTR_OFF_CH_SCAN_BW] = { .type = NLA_U8 },
-+	[MT76_TM_ATTR_IPI_THRESHOLD] = { .type = NLA_U8 },
-+	[MT76_TM_ATTR_IPI_PERIOD] = { .type = NLA_U32 },
-+	[MT76_TM_ATTR_IPI_ANTENNA_INDEX] = { .type = NLA_U8 },
-+	[MT76_TM_ATTR_IPI_RESET] = { .type = NLA_U8 },
- };
- 
- const struct tm_field msg_field = {
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0022-mtk-mt76-add-sanity-check-to-prevent-kernel-crash.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0022-mtk-mt76-add-sanity-check-to-prevent-kernel-crash.patch
new file mode 100644
index 0000000..feb14a6
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0022-mtk-mt76-add-sanity-check-to-prevent-kernel-crash.patch
@@ -0,0 +1,35 @@
+From 8c9cbc14f8956087c0262f0e9e943689a6b6b00a Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 30 Oct 2023 11:06:19 +0800
+Subject: [PATCH 022/199] mtk: mt76: add sanity check to prevent kernel crash
+
+wcid may not be initialized when mac80211 calls mt76.tx and it would lead to
+kernel crash.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ tx.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/tx.c b/tx.c
+index 5cf6edee..ab42f69b 100644
+--- a/tx.c
++++ b/tx.c
+@@ -345,6 +345,14 @@ mt76_tx(struct mt76_phy *phy, struct ieee80211_sta *sta,
+ 
+ 	info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->band_idx);
+ 
++	if (!wcid->tx_pending.prev || !wcid->tx_pending.next) {
++		dev_warn(phy->dev->dev, "Un-initialized STA %pM wcid %d in mt76_tx\n",
++			 sta->addr, wcid->idx);
++
++		ieee80211_free_txskb(phy->hw, skb);
++		return;
++	}
++
+ 	spin_lock_bh(&wcid->tx_pending.lock);
+ 	__skb_queue_tail(&wcid->tx_pending, skb);
+ 	spin_unlock_bh(&wcid->tx_pending.lock);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0022-mtk-wifi-mt76-testmode-add-testmode-pre-calibration-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0022-mtk-wifi-mt76-testmode-add-testmode-pre-calibration-.patch
deleted file mode 100644
index 27a6593..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0022-mtk-wifi-mt76-testmode-add-testmode-pre-calibration-.patch
+++ /dev/null
@@ -1,899 +0,0 @@
-From 19f055d29f67eb3ace29ccb6400a6bd59da8af7e Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 31 Mar 2023 11:27:24 +0800
-Subject: [PATCH 022/116] mtk: wifi: mt76: testmode: add testmode
- pre-calibration support
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mac80211.c        |  21 ---
- mt76.h            |  22 +++
- mt76_connac_mcu.h |   2 +
- mt7996/eeprom.c   |  66 +++++++
- mt7996/eeprom.h   |  47 +++++
- mt7996/mcu.c      |   5 +
- mt7996/mt7996.h   |   7 +
- mt7996/testmode.c | 437 ++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/testmode.h |  20 ++-
- testmode.c        |  12 ++
- testmode.h        |   8 +
- tools/fields.c    |   8 +
- 12 files changed, 632 insertions(+), 23 deletions(-)
-
-diff --git a/mac80211.c b/mac80211.c
-index 9a1eb47..e2ff011 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -6,27 +6,6 @@
- #include <linux/of.h>
- #include "mt76.h"
- 
--#define CHAN2G(_idx, _freq) {			\
--	.band = NL80211_BAND_2GHZ,		\
--	.center_freq = (_freq),			\
--	.hw_value = (_idx),			\
--	.max_power = 30,			\
--}
--
--#define CHAN5G(_idx, _freq) {			\
--	.band = NL80211_BAND_5GHZ,		\
--	.center_freq = (_freq),			\
--	.hw_value = (_idx),			\
--	.max_power = 30,			\
--}
--
--#define CHAN6G(_idx, _freq) {			\
--	.band = NL80211_BAND_6GHZ,		\
--	.center_freq = (_freq),			\
--	.hw_value = (_idx),			\
--	.max_power = 30,			\
--}
--
- static const struct ieee80211_channel mt76_channels_2ghz[] = {
- 	CHAN2G(1, 2412),
- 	CHAN2G(2, 2417),
-diff --git a/mt76.h b/mt76.h
-index 57b75fa..c591f67 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -23,6 +23,27 @@
- #include "util.h"
- #include "testmode.h"
- 
-+#define CHAN2G(_idx, _freq) {			\
-+	.band = NL80211_BAND_2GHZ,		\
-+	.center_freq = (_freq),			\
-+	.hw_value = (_idx),			\
-+	.max_power = 30,			\
-+}
-+
-+#define CHAN5G(_idx, _freq) {			\
-+	.band = NL80211_BAND_5GHZ,		\
-+	.center_freq = (_freq),			\
-+	.hw_value = (_idx),			\
-+	.max_power = 30,			\
-+}
-+
-+#define CHAN6G(_idx, _freq) {			\
-+	.band = NL80211_BAND_6GHZ,		\
-+	.center_freq = (_freq),			\
-+	.hw_value = (_idx),			\
-+	.max_power = 30,			\
-+}
-+
- #define MT_MCU_RING_SIZE	32
- #define MT_RX_BUF_SIZE		2048
- #define MT_SKB_HEAD_LEN		256
-@@ -703,6 +724,7 @@ struct mt76_testmode_ops {
- 	void (*reset_rx_stats)(struct mt76_phy *phy);
- 	void (*tx_stop)(struct mt76_phy *phy);
- 	int (*set_eeprom)(struct mt76_phy *phy, u32 offset, u8 *val, u8 action);
-+	int (*dump_precal)(struct mt76_phy *mphy, struct sk_buff *msg, int flag, int type);
- };
- 
- #define MT_TM_FW_RX_COUNT	BIT(0)
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 0b9c6c5..1919325 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1043,8 +1043,10 @@ enum {
- 	MCU_UNI_EVENT_RDD_REPORT = 0x11,
- 	MCU_UNI_EVENT_ROC = 0x27,
- 	MCU_UNI_EVENT_TX_DONE = 0x2d,
-+	MCU_UNI_EVENT_BF = 0x33,
- 	MCU_UNI_EVENT_THERMAL = 0x35,
- 	MCU_UNI_EVENT_NIC_CAPAB = 0x43,
-+	MCU_UNI_EVENT_TESTMODE_CTRL = 0x46,
- 	MCU_UNI_EVENT_WED_RRO = 0x57,
- 	MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
- 	MCU_UNI_EVENT_ALL_STA_INFO = 0x6e,
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index f9b9ca2..62c1ad4 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -12,6 +12,42 @@ static bool testmode_enable;
- module_param(testmode_enable, bool, 0644);
- MODULE_PARM_DESC(testmode_enable, "Enable testmode");
- 
-+const struct ieee80211_channel dpd_2g_ch_list_bw20[] = {
-+	CHAN2G(3, 2422),
-+	CHAN2G(7, 2442),
-+	CHAN2G(11, 2462)
-+};
-+
-+const struct ieee80211_channel dpd_5g_ch_list_bw160[] = {
-+	CHAN5G(50, 5250),
-+	CHAN5G(114, 5570),
-+	CHAN5G(163, 5815)
-+};
-+
-+const struct ieee80211_channel dpd_6g_ch_list_bw160[] = {
-+	CHAN6G(15, 6025),
-+	CHAN6G(47, 6185),
-+	CHAN6G(79, 6345),
-+	CHAN6G(111, 6505),
-+	CHAN6G(143, 6665),
-+	CHAN6G(175, 6825),
-+	CHAN6G(207, 6985)
-+};
-+
-+const struct ieee80211_channel dpd_6g_ch_list_bw320[] = {
-+	CHAN6G(31, 6105),
-+	CHAN6G(63, 6265),
-+	CHAN6G(95, 6425),
-+	CHAN6G(127, 6585),
-+	CHAN6G(159, 6745),
-+	CHAN6G(191, 6905)
-+};
-+
-+const u32 dpd_2g_bw20_ch_num = ARRAY_SIZE(dpd_2g_ch_list_bw20);
-+const u32 dpd_5g_bw160_ch_num = ARRAY_SIZE(dpd_5g_ch_list_bw160);
-+const u32 dpd_6g_bw160_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw160);
-+const u32 dpd_6g_bw320_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw320);
-+
- static int mt7996_check_eeprom(struct mt7996_dev *dev)
- {
- #define FEM_INT				0
-@@ -74,6 +110,36 @@ static char *mt7996_eeprom_name(struct mt7996_dev *dev)
- 	}
- }
- 
-+int
-+mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band)
-+{
-+	/* handle different sku */
-+	static const u8 band_to_idx[] = {
-+		[NL80211_BAND_2GHZ] = MT_BAND0,
-+		[NL80211_BAND_5GHZ] = MT_BAND1,
-+		[NL80211_BAND_6GHZ] = MT_BAND2,
-+	};
-+	struct mt7996_phy *phy = __mt7996_phy(dev, band_to_idx[band]);
-+	struct mt76_phy *mphy;
-+	int dpd_size;
-+
-+	if (!phy)
-+		return 0;
-+
-+	mphy = phy->mt76;
-+
-+	if (band == NL80211_BAND_2GHZ)
-+		dpd_size = dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
-+	else if (band == NL80211_BAND_5GHZ)
-+		dpd_size = mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
-+			   dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
-+	else
-+		dpd_size = mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
-+			   (dpd_6g_bw160_ch_num + dpd_6g_bw320_ch_num) * DPD_PER_CH_GT_BW20_SIZE;
-+
-+	return dpd_size;
-+}
-+
- static int
- mt7996_eeprom_load_default(struct mt7996_dev *dev)
- {
-diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index de3ff4e..849b8bc 100644
---- a/mt7996/eeprom.h
-+++ b/mt7996/eeprom.h
-@@ -14,6 +14,7 @@ enum mt7996_eeprom_field {
- 	MT_EE_MAC_ADDR =	0x004,
- 	MT_EE_MAC_ADDR2 =	0x00a,
- 	MT_EE_WIFI_CONF =	0x190,
-+	MT_EE_DO_PRE_CAL =	0x1a5,
- 	MT_EE_TESTMODE_EN =	0x1af,
- 	MT_EE_MAC_ADDR3 =	0x2c0,
- 	MT_EE_RATE_DELTA_2G =	0x1400,
-@@ -32,6 +33,52 @@ enum mt7996_eeprom_field {
- #define MT_EE_WIFI_CONF2_BAND_SEL		GENMASK(2, 0)
- #define MT_EE_WIFI_PA_LNA_CONFIG		GENMASK(1, 0)
- 
-+#define MT_EE_WIFI_CAL_GROUP_2G			BIT(0)
-+#define MT_EE_WIFI_CAL_GROUP_5G			BIT(1)
-+#define MT_EE_WIFI_CAL_GROUP_6G			BIT(2)
-+#define MT_EE_WIFI_CAL_GROUP			GENMASK(2, 0)
-+#define MT_EE_WIFI_CAL_DPD_2G			BIT(3)
-+#define MT_EE_WIFI_CAL_DPD_5G			BIT(4)
-+#define MT_EE_WIFI_CAL_DPD_6G			BIT(5)
-+#define MT_EE_WIFI_CAL_DPD			GENMASK(5, 3)
-+
-+#define MT_EE_CAL_UNIT				1024
-+#define MT_EE_CAL_GROUP_SIZE_2G			(4 * MT_EE_CAL_UNIT)
-+#define MT_EE_CAL_GROUP_SIZE_5G			(45 * MT_EE_CAL_UNIT)
-+#define MT_EE_CAL_GROUP_SIZE_6G			(125 * MT_EE_CAL_UNIT)
-+#define MT_EE_CAL_ADCDCOC_SIZE_2G		(4 * 4)
-+#define MT_EE_CAL_ADCDCOC_SIZE_5G		(4 * 4)
-+#define MT_EE_CAL_ADCDCOC_SIZE_6G		(4 * 5)
-+#define MT_EE_CAL_GROUP_SIZE			(MT_EE_CAL_GROUP_SIZE_2G + \
-+						 MT_EE_CAL_GROUP_SIZE_5G + \
-+						 MT_EE_CAL_GROUP_SIZE_6G + \
-+						 MT_EE_CAL_ADCDCOC_SIZE_2G + \
-+						 MT_EE_CAL_ADCDCOC_SIZE_5G + \
-+						 MT_EE_CAL_ADCDCOC_SIZE_6G)
-+
-+#define DPD_PER_CH_LEGACY_SIZE			(4 * MT_EE_CAL_UNIT)
-+#define DPD_PER_CH_MEM_SIZE			(13 * MT_EE_CAL_UNIT)
-+#define DPD_PER_CH_OTFG0_SIZE			(2 * MT_EE_CAL_UNIT)
-+#define DPD_PER_CH_BW20_SIZE			(DPD_PER_CH_LEGACY_SIZE + DPD_PER_CH_OTFG0_SIZE)
-+#define DPD_PER_CH_GT_BW20_SIZE			(DPD_PER_CH_MEM_SIZE + DPD_PER_CH_OTFG0_SIZE)
-+#define MT_EE_CAL_DPD_SIZE			(780 * MT_EE_CAL_UNIT)
-+
-+extern const struct ieee80211_channel dpd_2g_ch_list_bw20[];
-+extern const u32 dpd_2g_bw20_ch_num;
-+extern const struct ieee80211_channel dpd_5g_ch_list_bw160[];
-+extern const u32 dpd_5g_bw160_ch_num;
-+extern const struct ieee80211_channel dpd_6g_ch_list_bw160[];
-+extern const u32 dpd_6g_bw160_ch_num;
-+extern const struct ieee80211_channel dpd_6g_ch_list_bw320[];
-+extern const u32 dpd_6g_bw320_ch_num;
-+
-+#define RF_DPD_FLAT_CAL				BIT(28)
-+#define RF_PRE_CAL				BIT(29)
-+#define RF_DPD_FLAT_5G_CAL			GENMASK(29, 28)
-+#define RF_DPD_FLAT_5G_MEM_CAL			(BIT(30) | BIT(28))
-+#define RF_DPD_FLAT_6G_CAL			GENMASK(30, 28)
-+#define RF_DPD_FLAT_6G_MEM_CAL			(BIT(31) | BIT(28))
-+
- #define MT_EE_WIFI_CONF1_TX_PATH_BAND0		GENMASK(5, 3)
- #define MT_EE_WIFI_CONF2_TX_PATH_BAND1		GENMASK(2, 0)
- #define MT_EE_WIFI_CONF2_TX_PATH_BAND2		GENMASK(5, 3)
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 2bca86a..1d4f421 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -715,6 +715,11 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
- 	case MCU_UNI_EVENT_WED_RRO:
- 		mt7996_mcu_wed_rro_event(dev, skb);
- 		break;
-+#ifdef CONFIG_NL80211_TESTMODE
-+	case MCU_UNI_EVENT_TESTMODE_CTRL:
-+		mt7996_tm_rf_test_event(dev, skb);
-+		break;
-+#endif
- 	default:
- 		break;
- 	}
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 0c6a4b9..121c4a1 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -390,6 +390,9 @@ struct mt7996_dev {
- 	struct dentry *debugfs_dir;
- 	struct rchan *relay_fwlog;
- 
-+	void *cal;
-+	u32 cur_prek_offset;
-+
- 	struct {
- 		u16 table_mask;
- 		u8 n_agrt;
-@@ -510,6 +513,7 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
- int mt7996_eeprom_get_target_power(struct mt7996_dev *dev,
- 				   struct ieee80211_channel *chan);
- s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band);
-+int mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band);
- int mt7996_dma_init(struct mt7996_dev *dev);
- void mt7996_dma_reset(struct mt7996_dev *dev, bool force);
- void mt7996_dma_prefetch(struct mt7996_dev *dev);
-@@ -594,6 +598,9 @@ void mt7996_mcu_exit(struct mt7996_dev *dev);
- int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
- int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
- int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
-+#ifdef CONFIG_NL80211_TESTMODE
-+void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
-+#endif
- 
- static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
- {
-diff --git a/mt7996/testmode.c b/mt7996/testmode.c
-index 98eebce..a756ee1 100644
---- a/mt7996/testmode.c
-+++ b/mt7996/testmode.c
-@@ -7,6 +7,8 @@
- #include "mac.h"
- #include "mcu.h"
- #include "testmode.h"
-+#include "eeprom.h"
-+#include "mtk_mcu.h"
- 
- enum {
- 	TM_CHANGED_TXPOWER,
-@@ -397,6 +399,436 @@ mt7996_tm_set_tx_cont(struct mt7996_phy *phy, bool en)
- 	}
- }
- 
-+static int
-+mt7996_tm_group_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
-+{
-+	u8 *eeprom;
-+	u32 i, group_size, dpd_size, size, offs, *pre_cal;
-+	int ret = 0;
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt76_dev *mdev = &dev->mt76;
-+	struct mt7996_tm_req req = {
-+		.rf_test = {
-+			.tag = cpu_to_le16(UNI_RF_TEST_CTRL),
-+			.len = cpu_to_le16(sizeof(req.rf_test)),
-+			.action = RF_ACTION_IN_RF_TEST,
-+			.icap_len = RF_TEST_ICAP_LEN,
-+			.op.rf.func_idx = cpu_to_le32(RF_TEST_RE_CAL),
-+			.op.rf.param.cal_param.func_data = cpu_to_le32(RF_PRE_CAL),
-+			.op.rf.param.cal_param.band_idx = phy->mt76->band_idx,
-+		},
-+	};
-+
-+	if (!dev->flash_mode) {
-+		dev_err(dev->mt76.dev, "Currently not in FLASH or BIN FILE mode, return!\n");
-+		return -EOPNOTSUPP;
-+	}
-+
-+	eeprom = mdev->eeprom.data;
-+	dev->cur_prek_offset = 0;
-+	group_size = MT_EE_CAL_GROUP_SIZE;
-+	dpd_size = MT_EE_CAL_DPD_SIZE;
-+	size = group_size + dpd_size;
-+	offs = MT_EE_DO_PRE_CAL;
-+
-+	switch (state) {
-+	case MT76_TM_STATE_GROUP_PREK:
-+		if (!dev->cal) {
-+			dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
-+			if (!dev->cal)
-+				return -ENOMEM;
-+		}
-+
-+		ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), &req,
-+					sizeof(req), false);
-+		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == group_size,
-+				   30 * HZ);
-+
-+		if (ret) {
-+			dev_err(dev->mt76.dev, "Group Pre-cal: mcu send msg failed!\n");
-+			return ret;
-+		}
-+
-+		if (!ret)
-+			eeprom[offs] |= MT_EE_WIFI_CAL_GROUP;
-+		break;
-+	case MT76_TM_STATE_GROUP_PREK_DUMP:
-+		pre_cal = (u32 *)dev->cal;
-+		if (!pre_cal) {
-+			dev_info(dev->mt76.dev, "Not group pre-cal yet!\n");
-+			return ret;
-+		}
-+		dev_info(dev->mt76.dev, "Group Pre-Cal:\n");
-+		for (i = 0; i < (group_size / sizeof(u32)); i += 4) {
-+			dev_info(dev->mt76.dev, "[0x%08lx] 0x%8x 0x%8x 0x%8x 0x%8x\n",
-+				 i * sizeof(u32), pre_cal[i], pre_cal[i + 1],
-+				 pre_cal[i + 2], pre_cal[i + 3]);
-+		}
-+		break;
-+	case MT76_TM_STATE_GROUP_PREK_CLEAN:
-+		pre_cal = (u32 *)dev->cal;
-+		if (!pre_cal)
-+			return ret;
-+		memset(pre_cal, 0, group_size);
-+		eeprom[offs] &= ~MT_EE_WIFI_CAL_GROUP;
-+		break;
-+	default:
-+		return -EINVAL;
-+	}
-+
-+	return ret;
-+}
-+
-+static int
-+mt7996_tm_dpd_prek_send_req(struct mt7996_phy *phy, struct mt7996_tm_req *req,
-+			    const struct ieee80211_channel *chan_list, u32 channel_size,
-+			    enum nl80211_chan_width width, u32 func_data)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt76_phy *mphy = phy->mt76;
-+	struct cfg80211_chan_def chandef_backup, *chandef = &mphy->chandef;
-+	struct ieee80211_channel chan_backup;
-+	int i, ret;
-+
-+	if (!chan_list)
-+		return -EOPNOTSUPP;
-+
-+	req->rf_test.op.rf.param.cal_param.func_data = cpu_to_le32(func_data);
-+
-+	memcpy(&chan_backup, chandef->chan, sizeof(struct ieee80211_channel));
-+	memcpy(&chandef_backup, chandef, sizeof(struct cfg80211_chan_def));
-+
-+	for (i = 0; i < channel_size; i++) {
-+		memcpy(chandef->chan, &chan_list[i], sizeof(struct ieee80211_channel));
-+		chandef->width = width;
-+
-+		/* set channel switch reason */
-+		mphy->hw->conf.flags |= IEEE80211_CONF_OFFCHANNEL;
-+		mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
-+
-+		ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), req,
-+					sizeof(*req), false);
-+		if (ret) {
-+			dev_err(dev->mt76.dev, "DPD Pre-cal: mcu send msg failed!\n");
-+			goto out;
-+		}
-+	}
-+
-+out:
-+	mphy->hw->conf.flags &= ~IEEE80211_CONF_OFFCHANNEL;
-+	memcpy(chandef, &chandef_backup, sizeof(struct cfg80211_chan_def));
-+	memcpy(chandef->chan, &chan_backup, sizeof(struct ieee80211_channel));
-+	mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
-+
-+	return ret;
-+}
-+
-+static int
-+mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt76_dev *mdev = &dev->mt76;
-+	struct mt76_phy *mphy = phy->mt76;
-+	struct mt7996_tm_req req = {
-+		.rf_test = {
-+			.tag = cpu_to_le16(UNI_RF_TEST_CTRL),
-+			.len = cpu_to_le16(sizeof(req.rf_test)),
-+			.action = RF_ACTION_IN_RF_TEST,
-+			.icap_len = RF_TEST_ICAP_LEN,
-+			.op.rf.func_idx = cpu_to_le32(RF_TEST_RE_CAL),
-+			.op.rf.param.cal_param.band_idx = phy->mt76->band_idx,
-+		},
-+	};
-+	u32 i, j, group_size, dpd_size, size, offs, *pre_cal;
-+	u32 wait_on_prek_offset = 0;
-+	u8 do_precal, *eeprom;
-+	int ret = 0;
-+
-+	if (!dev->flash_mode) {
-+		dev_err(dev->mt76.dev, "Currently not in FLASH or BIN FILE mode, return!\n");
-+		return -EOPNOTSUPP;
-+	}
-+
-+	eeprom = mdev->eeprom.data;
-+	dev->cur_prek_offset = 0;
-+	group_size = MT_EE_CAL_GROUP_SIZE;
-+	dpd_size = MT_EE_CAL_DPD_SIZE;
-+	size = group_size + dpd_size;
-+	offs = MT_EE_DO_PRE_CAL;
-+
-+	if (!dev->cal && state < MT76_TM_STATE_DPD_DUMP) {
-+		dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
-+		if (!dev->cal)
-+			return -ENOMEM;
-+	}
-+
-+	switch (state) {
-+	case MT76_TM_STATE_DPD_2G:
-+		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_2g_ch_list_bw20,
-+						  dpd_2g_bw20_ch_num,
-+						  NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_CAL);
-+		wait_on_prek_offset += dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
-+		wait_event_timeout(mdev->mcu.wait,
-+				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+
-+		do_precal = MT_EE_WIFI_CAL_DPD_2G;
-+		break;
-+	case MT76_TM_STATE_DPD_5G:
-+		/* 5g channel bw20 calibration */
-+		ret = mt7996_tm_dpd_prek_send_req(phy, &req, mphy->sband_5g.sband.channels,
-+						  mphy->sband_5g.sband.n_channels,
-+						  NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_5G_CAL);
-+		if (ret)
-+			return ret;
-+		wait_on_prek_offset += mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
-+		wait_event_timeout(mdev->mcu.wait,
-+				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+
-+		/* 5g channel bw160 calibration */
-+		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_5g_ch_list_bw160,
-+						  dpd_5g_bw160_ch_num,
-+						  NL80211_CHAN_WIDTH_160, RF_DPD_FLAT_5G_MEM_CAL);
-+		wait_on_prek_offset += dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
-+		wait_event_timeout(mdev->mcu.wait,
-+				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+
-+		do_precal = MT_EE_WIFI_CAL_DPD_5G;
-+		break;
-+	case MT76_TM_STATE_DPD_6G:
-+		/* 6g channel bw20 calibration */
-+		ret = mt7996_tm_dpd_prek_send_req(phy, &req, mphy->sband_6g.sband.channels,
-+						  mphy->sband_6g.sband.n_channels,
-+						  NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_6G_CAL);
-+		if (ret)
-+			return ret;
-+		wait_on_prek_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
-+		wait_event_timeout(mdev->mcu.wait,
-+				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+
-+		/* 6g channel bw160 calibration */
-+		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw160,
-+						  dpd_6g_bw160_ch_num,
-+						  NL80211_CHAN_WIDTH_160, RF_DPD_FLAT_6G_MEM_CAL);
-+		if (ret)
-+			return ret;
-+		wait_on_prek_offset += dpd_6g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
-+		wait_event_timeout(mdev->mcu.wait,
-+				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+
-+		/* 6g channel bw320 calibration */
-+		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw320,
-+						  dpd_6g_bw320_ch_num,
-+						  NL80211_CHAN_WIDTH_320, RF_DPD_FLAT_6G_MEM_CAL);
-+		wait_on_prek_offset += dpd_6g_bw320_ch_num * DPD_PER_CH_GT_BW20_SIZE;
-+		wait_event_timeout(mdev->mcu.wait,
-+				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+
-+		do_precal = MT_EE_WIFI_CAL_DPD_6G;
-+		break;
-+	case MT76_TM_STATE_DPD_DUMP:
-+		if (!dev->cal) {
-+			dev_info(dev->mt76.dev, "Not DPD pre-cal yet!\n");
-+			return ret;
-+		}
-+		pre_cal = (u32 *)dev->cal;
-+		dev_info(dev->mt76.dev, "DPD Pre-Cal:\n");
-+		for (i = 0; i < dpd_size / sizeof(u32); i += 4) {
-+			j = i + (group_size / sizeof(u32));
-+			dev_info(dev->mt76.dev, "[0x%08lx] 0x%8x 0x%8x 0x%8x 0x%8x\n",
-+				 j * sizeof(u32), pre_cal[j], pre_cal[j + 1],
-+				 pre_cal[j + 2], pre_cal[j + 3]);
-+		}
-+		return 0;
-+	case MT76_TM_STATE_DPD_CLEAN:
-+		pre_cal = (u32 *)dev->cal;
-+		if (!pre_cal)
-+			return ret;
-+		memset(pre_cal + (group_size / sizeof(u32)), 0, dpd_size);
-+		do_precal = MT_EE_WIFI_CAL_DPD;
-+		eeprom[offs] &= ~do_precal;
-+		return 0;
-+	default:
-+		return -EINVAL;
-+	}
-+
-+	if (!ret)
-+		eeprom[offs] |= do_precal;
-+
-+	return ret;
-+}
-+
-+static int
-+mt7996_tm_dump_precal(struct mt76_phy *mphy, struct sk_buff *msg, int flag, int type)
-+{
-+#define DPD_PER_CHAN_SIZE_MASK		GENMASK(31, 30)
-+#define DPD_2G_RATIO_MASK		GENMASK(29, 20)
-+#define DPD_5G_RATIO_MASK		GENMASK(19, 10)
-+#define DPD_6G_RATIO_MASK		GENMASK(9, 0)
-+	struct mt7996_phy *phy = mphy->priv;
-+	struct mt7996_dev *dev = phy->dev;
-+	u32 i, group_size, dpd_size, total_size, size, dpd_info = 0;
-+	u32 dpd_size_2g, dpd_size_5g, dpd_size_6g;
-+	u32 base, offs, transmit_size = 1000;
-+	u8 *pre_cal, *eeprom;
-+	void *precal;
-+	enum prek_ops {
-+		PREK_GET_INFO,
-+		PREK_SYNC_ALL,
-+		PREK_SYNC_GROUP,
-+		PREK_SYNC_DPD_2G,
-+		PREK_SYNC_DPD_5G,
-+		PREK_SYNC_DPD_6G,
-+		PREK_CLEAN_GROUP,
-+		PREK_CLEAN_DPD,
-+	};
-+
-+	if (!dev->cal) {
-+		dev_info(dev->mt76.dev, "Not pre-cal yet!\n");
-+		return 0;
-+	}
-+
-+	group_size = MT_EE_CAL_GROUP_SIZE;
-+	dpd_size = MT_EE_CAL_DPD_SIZE;
-+	total_size = group_size + dpd_size;
-+	pre_cal = dev->cal;
-+	eeprom = dev->mt76.eeprom.data;
-+	offs = MT_EE_DO_PRE_CAL;
-+
-+	dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
-+	dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
-+	dpd_size_6g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_6GHZ);
-+
-+	switch (type) {
-+	case PREK_SYNC_ALL:
-+		base = 0;
-+		size = total_size;
-+		break;
-+	case PREK_SYNC_GROUP:
-+		base = 0;
-+		size = group_size;
-+		break;
-+	case PREK_SYNC_DPD_2G:
-+		base = group_size;
-+		size = dpd_size_2g;
-+		break;
-+	case PREK_SYNC_DPD_5G:
-+		base = group_size + dpd_size_2g;
-+		size = dpd_size_5g;
-+		break;
-+	case PREK_SYNC_DPD_6G:
-+		base = group_size + dpd_size_2g + dpd_size_5g;
-+		size = dpd_size_6g;
-+		break;
-+	case PREK_GET_INFO:
-+		break;
-+	default:
-+		return 0;
-+	}
-+
-+	if (!flag) {
-+		if (eeprom[offs] & MT_EE_WIFI_CAL_DPD) {
-+			dpd_info |= u32_encode_bits(1, DPD_PER_CHAN_SIZE_MASK) |
-+				    u32_encode_bits(dpd_size_2g / MT_EE_CAL_UNIT,
-+						    DPD_2G_RATIO_MASK) |
-+				    u32_encode_bits(dpd_size_5g / MT_EE_CAL_UNIT,
-+						    DPD_5G_RATIO_MASK) |
-+				    u32_encode_bits(dpd_size_6g / MT_EE_CAL_UNIT,
-+						    DPD_6G_RATIO_MASK);
-+		}
-+		dev->cur_prek_offset = 0;
-+		precal = nla_nest_start(msg, MT76_TM_ATTR_PRECAL_INFO);
-+		if (!precal)
-+			return -ENOMEM;
-+		nla_put_u32(msg, 0, group_size);
-+		nla_put_u32(msg, 1, dpd_size);
-+		nla_put_u32(msg, 2, dpd_info);
-+		nla_put_u32(msg, 3, transmit_size);
-+		nla_put_u32(msg, 4, eeprom[offs]);
-+		nla_nest_end(msg, precal);
-+	} else {
-+		precal = nla_nest_start(msg, MT76_TM_ATTR_PRECAL);
-+		if (!precal)
-+			return -ENOMEM;
-+
-+		transmit_size = (dev->cur_prek_offset + transmit_size < size) ?
-+				transmit_size : (size - dev->cur_prek_offset);
-+		for (i = 0; i < transmit_size; i++) {
-+			if (nla_put_u8(msg, i, pre_cal[base + dev->cur_prek_offset + i]))
-+				return -ENOMEM;
-+		}
-+		dev->cur_prek_offset += transmit_size;
-+
-+		nla_nest_end(msg, precal);
-+	}
-+
-+	return 0;
-+}
-+
-+static void
-+mt7996_tm_re_cal_event(struct mt7996_dev *dev, struct mt7996_tm_rf_test_result *result,
-+		       struct mt7996_tm_rf_test_data *data)
-+{
-+	u32 base, dpd_size_2g, dpd_size_5g, dpd_size_6g, cal_idx, cal_type, len = 0;
-+	u8 *pre_cal;
-+
-+	pre_cal = dev->cal;
-+	dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
-+	dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
-+	dpd_size_6g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_6GHZ);
-+
-+	cal_idx = le32_to_cpu(data->cal_idx);
-+	cal_type = le32_to_cpu(data->cal_type);
-+	len = le32_to_cpu(result->payload_length);
-+	len = len - sizeof(struct mt7996_tm_rf_test_data);
-+
-+	switch (cal_type) {
-+	case RF_PRE_CAL:
-+		base = 0;
-+		break;
-+	case RF_DPD_FLAT_CAL:
-+		base = MT_EE_CAL_GROUP_SIZE;
-+		break;
-+	case RF_DPD_FLAT_5G_CAL:
-+	case RF_DPD_FLAT_5G_MEM_CAL:
-+		base = MT_EE_CAL_GROUP_SIZE + dpd_size_2g;
-+		break;
-+	case RF_DPD_FLAT_6G_CAL:
-+	case RF_DPD_FLAT_6G_MEM_CAL:
-+		base = MT_EE_CAL_GROUP_SIZE + dpd_size_2g + dpd_size_5g;
-+		break;
-+	default:
-+		dev_info(dev->mt76.dev, "Unknown calibration type!\n");
-+		return;
-+	}
-+	pre_cal += (base + dev->cur_prek_offset);
-+
-+	memcpy(pre_cal, data->cal_data, len);
-+	dev->cur_prek_offset += len;
-+}
-+
-+void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb)
-+{
-+	struct mt7996_tm_event *event;
-+	struct mt7996_tm_rf_test_result *result;
-+	struct mt7996_tm_rf_test_data *data;
-+	static u32 event_type;
-+
-+	skb_pull(skb, sizeof(struct mt7996_mcu_rxd));
-+	event = (struct mt7996_tm_event *)skb->data;
-+	result = (struct mt7996_tm_rf_test_result *)&event->result;
-+	data = (struct mt7996_tm_rf_test_data *)result->data;
-+
-+	event_type = le32_to_cpu(result->func_idx);
-+
-+	switch (event_type) {
-+	case RF_TEST_RE_CAL:
-+		mt7996_tm_re_cal_event(dev, result, data);
-+		break;
-+	default:
-+		break;
-+	}
-+}
-+
- static void
- mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
- {
-@@ -454,6 +886,10 @@ mt7996_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
- 	else if (prev_state == MT76_TM_STATE_OFF ||
- 		 state == MT76_TM_STATE_OFF)
- 		mt7996_tm_init(phy, !(state == MT76_TM_STATE_OFF));
-+	else if (state >= MT76_TM_STATE_GROUP_PREK && state <= MT76_TM_STATE_GROUP_PREK_CLEAN)
-+		return mt7996_tm_group_prek(phy, state);
-+	else if (state >= MT76_TM_STATE_DPD_2G && state <= MT76_TM_STATE_DPD_CLEAN)
-+		return mt7996_tm_dpd_prek(phy, state);
- 
- 	if ((state == MT76_TM_STATE_IDLE &&
- 	     prev_state == MT76_TM_STATE_OFF) ||
-@@ -737,4 +1173,5 @@ const struct mt76_testmode_ops mt7996_testmode_ops = {
- 	.reset_rx_stats = mt7996_tm_reset_trx_stats,
- 	.tx_stop = mt7996_tm_tx_stop,
- 	.set_eeprom = mt7996_tm_set_eeprom,
-+	.dump_precal = mt7996_tm_dump_precal,
- };
-diff --git a/mt7996/testmode.h b/mt7996/testmode.h
-index 319ef25..9bfb86f 100644
---- a/mt7996/testmode.h
-+++ b/mt7996/testmode.h
-@@ -34,6 +34,12 @@ enum bw_mapping_method {
- 	NUM_BW_MAP,
- };
- 
-+struct tm_cal_param {
-+	__le32 func_data;
-+	u8 band_idx;
-+	u8 rsv[3];
-+};
-+
- struct mt7996_tm_rf_test {
- 	__le16 tag;
- 	__le16 len;
-@@ -50,7 +56,7 @@ struct mt7996_tm_rf_test {
- 			union {
- 				__le32 func_data;
- 				__le32 cal_dump;
--
-+				struct tm_cal_param cal_param;
- 				u8 _pad[80];
- 			} param;
- 		} rf;
-@@ -63,10 +69,16 @@ struct mt7996_tm_req {
- 	struct mt7996_tm_rf_test rf_test;
- } __packed;
- 
-+struct mt7996_tm_rf_test_data {
-+	__le32 cal_idx;
-+	__le32 cal_type;
-+	u8 cal_data[0];
-+} __packed;
-+
- struct mt7996_tm_rf_test_result {
- 	__le32 func_idx;
- 	__le32 payload_length;
--	u8 event[0];
-+	u8 data[0];
- };
- 
- struct mt7996_tm_event {
-@@ -77,6 +89,8 @@ struct mt7996_tm_event {
- 	struct mt7996_tm_rf_test_result result;
- } __packed;
- 
-+#define RF_TEST_RE_CAL		0x01
-+
- enum {
- 	RF_ACTION_SWITCH_TO_RF_TEST,
- 	RF_ACTION_IN_RF_TEST,
-@@ -84,6 +98,8 @@ enum {
- 	RF_ACTION_GET,
- };
- 
-+#define RF_TEST_ICAP_LEN	120
-+
- enum {
- 	RF_OPER_NORMAL,
- 	RF_OPER_RF_TEST,
-diff --git a/testmode.c b/testmode.c
-index 44f3a5b..cd8cb65 100644
---- a/testmode.c
-+++ b/testmode.c
-@@ -674,6 +674,18 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
- 
- 	mutex_lock(&dev->mutex);
- 
-+	if (tb[MT76_TM_ATTR_PRECAL] || tb[MT76_TM_ATTR_PRECAL_INFO]) {
-+		int flag, type;
-+
-+		err = -EINVAL;
-+		flag = tb[MT76_TM_ATTR_PRECAL] ? 1 : 0;
-+		type = flag ? nla_get_u8(tb[MT76_TM_ATTR_PRECAL_INFO]) : 0;
-+		if (dev->test_ops->dump_precal)
-+			err = dev->test_ops->dump_precal(phy, msg, flag, type);
-+
-+		goto out;
-+	}
-+
- 	if (tb[MT76_TM_ATTR_STATS]) {
- 		err = -EINVAL;
- 
-diff --git a/testmode.h b/testmode.h
-index 96872e8..d6601cd 100644
---- a/testmode.h
-+++ b/testmode.h
-@@ -220,6 +220,14 @@ enum mt76_testmode_state {
- 	MT76_TM_STATE_TX_FRAMES,
- 	MT76_TM_STATE_RX_FRAMES,
- 	MT76_TM_STATE_TX_CONT,
-+	MT76_TM_STATE_GROUP_PREK,
-+	MT76_TM_STATE_GROUP_PREK_DUMP,
-+	MT76_TM_STATE_GROUP_PREK_CLEAN,
-+	MT76_TM_STATE_DPD_2G,
-+	MT76_TM_STATE_DPD_5G,
-+	MT76_TM_STATE_DPD_6G,
-+	MT76_TM_STATE_DPD_DUMP,
-+	MT76_TM_STATE_DPD_CLEAN,
- 	MT76_TM_STATE_ON,
- 
- 	/* keep last */
-diff --git a/tools/fields.c b/tools/fields.c
-index 055f90f..b012276 100644
---- a/tools/fields.c
-+++ b/tools/fields.c
-@@ -11,6 +11,14 @@ static const char * const testmode_state[] = {
- 	[MT76_TM_STATE_TX_FRAMES] = "tx_frames",
- 	[MT76_TM_STATE_RX_FRAMES] = "rx_frames",
- 	[MT76_TM_STATE_TX_CONT] = "tx_cont",
-+	[MT76_TM_STATE_GROUP_PREK] = "group_prek",
-+	[MT76_TM_STATE_GROUP_PREK_DUMP] = "group_prek_dump",
-+	[MT76_TM_STATE_GROUP_PREK_CLEAN] = "group_prek_clean",
-+	[MT76_TM_STATE_DPD_2G] = "dpd_2g",
-+	[MT76_TM_STATE_DPD_5G] = "dpd_5g",
-+	[MT76_TM_STATE_DPD_6G] = "dpd_6g",
-+	[MT76_TM_STATE_DPD_DUMP] = "dpd_dump",
-+	[MT76_TM_STATE_DPD_CLEAN] = "dpd_clean",
- };
- 
- static const char * const testmode_tx_mode[] = {
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0023-mtk-mt76-mt7996-add-firmware-WA-s-coredump.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0023-mtk-mt76-mt7996-add-firmware-WA-s-coredump.patch
new file mode 100644
index 0000000..dfa716b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0023-mtk-mt76-mt7996-add-firmware-WA-s-coredump.patch
@@ -0,0 +1,597 @@
+From 107e29828372478e61e6f01f96148ab89c164c82 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Fri, 19 May 2023 14:16:50 +0800
+Subject: [PATCH 023/199] mtk: mt76: mt7996: add firmware WA's coredump.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ mt7996/coredump.c | 180 ++++++++++++++++++++++++++++++----------------
+ mt7996/coredump.h |  35 ++++++---
+ mt7996/mac.c      |  31 +++++---
+ mt7996/mcu.c      |   5 ++
+ mt7996/mt7996.h   |   7 +-
+ mt7996/regs.h     |   7 +-
+ 6 files changed, 182 insertions(+), 83 deletions(-)
+
+diff --git a/mt7996/coredump.c b/mt7996/coredump.c
+index ccab0d7b..60b88085 100644
+--- a/mt7996/coredump.c
++++ b/mt7996/coredump.c
+@@ -7,11 +7,11 @@
+ #include <linux/utsname.h>
+ #include "coredump.h"
+ 
+-static bool coredump_memdump;
++static bool coredump_memdump = true;
+ module_param(coredump_memdump, bool, 0644);
+ MODULE_PARM_DESC(coredump_memdump, "Optional ability to dump firmware memory");
+ 
+-static const struct mt7996_mem_region mt7996_mem_regions[] = {
++static const struct mt7996_mem_region mt7996_wm_mem_regions[] = {
+ 	{
+ 		.start = 0x00800000,
+ 		.len = 0x0004ffff,
+@@ -44,27 +44,55 @@ static const struct mt7996_mem_region mt7996_mem_regions[] = {
+ 	},
+ };
+ 
++static const struct mt7996_mem_region mt7996_wa_mem_regions[] = {
++	{
++		.start = 0xE0000000,
++		.len = 0x0000ffff,
++		.name = "CRAM",
++	},
++	{
++		.start = 0xE0010000,
++		.len = 0x000117ff,
++		.name = "CRAM2",
++	},
++	{
++		.start = 0x10000000,
++		.len = 0x0001bfff,
++		.name = "ILM",
++	},
++	{
++		.start = 0x10200000,
++		.len = 0x00063fff,
++		.name = "DLM",
++	},
++};
++
+ const struct mt7996_mem_region*
+-mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num)
++mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num)
+ {
+ 	switch (mt76_chip(&dev->mt76)) {
+ 	case 0x7990:
+ 	case 0x7991:
+-		*num = ARRAY_SIZE(mt7996_mem_regions);
+-		return &mt7996_mem_regions[0];
++		if (type == MT7996_RAM_TYPE_WA) {
++			*num = ARRAY_SIZE(mt7996_wa_mem_regions);
++			return &mt7996_wa_mem_regions[0];
++		}
++
++		*num = ARRAY_SIZE(mt7996_wm_mem_regions);
++		return &mt7996_wm_mem_regions[0];
+ 	default:
+ 		return NULL;
+ 	}
+ }
+ 
+-static int mt7996_coredump_get_mem_size(struct mt7996_dev *dev)
++static int mt7996_coredump_get_mem_size(struct mt7996_dev *dev, u8 type)
+ {
+ 	const struct mt7996_mem_region *mem_region;
+ 	size_t size = 0;
+ 	u32 num;
+ 	int i;
+ 
+-	mem_region = mt7996_coredump_get_mem_layout(dev, &num);
++	mem_region = mt7996_coredump_get_mem_layout(dev, type, &num);
+ 	if (!mem_region)
+ 		return 0;
+ 
+@@ -81,14 +109,13 @@ static int mt7996_coredump_get_mem_size(struct mt7996_dev *dev)
+ 	return size;
+ }
+ 
+-struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev)
++struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type)
+ {
+-	struct mt7996_crash_data *crash_data = dev->coredump.crash_data;
++	struct mt7996_crash_data *crash_data = dev->coredump.crash_data[type];
+ 
+ 	lockdep_assert_held(&dev->dump_mutex);
+ 
+-	if (coredump_memdump &&
+-	    !mt76_poll_msec(dev, MT_FW_DUMP_STATE, 0x3, 0x2, 500))
++	if (!coredump_memdump)
+ 		return NULL;
+ 
+ 	guid_gen(&crash_data->guid);
+@@ -98,12 +125,15 @@ struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev)
+ }
+ 
+ static void
+-mt7996_coredump_fw_state(struct mt7996_dev *dev, struct mt7996_coredump *dump,
++mt7996_coredump_fw_state(struct mt7996_dev *dev, u8 type, struct mt7996_coredump *dump,
+ 			 bool *exception)
+ {
+-	u32 count;
++	u32 count, reg = MT_FW_WM_DUMP_STATE;
++
++	if (type == MT7996_RAM_TYPE_WA)
++		reg = MT_FW_WA_DUMP_STATE;
+ 
+-	count = mt76_rr(dev, MT_FW_ASSERT_CNT);
++	count = mt76_rr(dev, reg);
+ 
+ 	/* normal mode: driver can manually trigger assert for detail info */
+ 	if (!count)
+@@ -115,53 +145,59 @@ mt7996_coredump_fw_state(struct mt7996_dev *dev, struct mt7996_coredump *dump,
+ }
+ 
+ static void
+-mt7996_coredump_fw_stack(struct mt7996_dev *dev, struct mt7996_coredump *dump,
++mt7996_coredump_fw_stack(struct mt7996_dev *dev, u8 type, struct mt7996_coredump *dump,
+ 			 bool exception)
+ {
+-	u32 oldest, i, idx;
++	u32 reg, i, offset = 0, val = MT7996_RAM_TYPE_WM;
+ 
+-	strscpy(dump->pc_current, "program counter", sizeof(dump->pc_current));
++	if (type == MT7996_RAM_TYPE_WA) {
++		offset = MT_MCU_WA_EXCP_BASE - MT_MCU_WM_EXCP_BASE;
++		val = MT7996_RAM_TYPE_WA;
++	}
+ 
+-	/* 0: WM PC log output */
+-	mt76_wr(dev, MT_CONN_DBG_CTL_OUT_SEL, 0);
++	/* 0: WM PC log output, 1: WA PC log output  */
++	mt76_wr(dev, MT_CONN_DBG_CTL_OUT_SEL, val);
+ 	/* choose 33th PC log buffer to read current PC index */
+ 	mt76_wr(dev, MT_CONN_DBG_CTL_PC_LOG_SEL, 0x3f);
+ 
+ 	/* read current PC */
+-	dump->pc_stack[0] = mt76_rr(dev, MT_CONN_DBG_CTL_PC_LOG);
++	for (i = 0; i < 10; i++)
++		dump->pc_cur[i] = mt76_rr(dev, MT_CONN_DBG_CTL_PC_LOG);
+ 
+ 	/* stop call stack record */
+ 	if (!exception) {
+-		mt76_clear(dev, MT_MCU_WM_EXCP_PC_CTRL, BIT(0));
+-		mt76_clear(dev, MT_MCU_WM_EXCP_LR_CTRL, BIT(0));
++		mt76_clear(dev, MT_MCU_WM_EXCP_PC_CTRL + offset, BIT(0));
++		mt76_clear(dev, MT_MCU_WM_EXCP_LR_CTRL + offset, BIT(0));
+ 	}
+ 
+-	oldest = (u32)mt76_get_field(dev, MT_MCU_WM_EXCP_PC_CTRL,
+-				     GENMASK(20, 16)) + 2;
+-	for (i = 0; i < 16; i++) {
+-		idx = ((oldest + 2 * i + 1) % 32);
+-		dump->pc_stack[i + 1] =
+-			mt76_rr(dev, MT_MCU_WM_EXCP_PC_LOG + idx * 4);
++	/* read PC log */
++	dump->pc_dbg_ctrl = mt76_rr(dev, MT_MCU_WM_EXCP_PC_CTRL + offset);
++	dump->pc_cur_idx = FIELD_GET(MT_MCU_WM_EXCP_PC_CTRL_IDX_STATUS,
++				     dump->pc_dbg_ctrl);
++	for (i = 0; i < 32; i++) {
++		reg = MT_MCU_WM_EXCP_PC_LOG + i * 4 + offset;
++		dump->pc_stack[i] = mt76_rr(dev, reg);
+ 	}
+ 
+-	oldest = (u32)mt76_get_field(dev, MT_MCU_WM_EXCP_LR_CTRL,
+-				     GENMASK(20, 16)) + 2;
+-	for (i = 0; i < 16; i++) {
+-		idx = ((oldest + 2 * i + 1) % 32);
+-		dump->lr_stack[i] =
+-			mt76_rr(dev, MT_MCU_WM_EXCP_LR_LOG + idx * 4);
++	/* read LR log */
++	dump->lr_dbg_ctrl = mt76_rr(dev, MT_MCU_WM_EXCP_LR_CTRL + offset);
++	dump->lr_cur_idx = FIELD_GET(MT_MCU_WM_EXCP_LR_CTRL_IDX_STATUS,
++				     dump->lr_dbg_ctrl);
++	for (i = 0; i < 32; i++) {
++		reg = MT_MCU_WM_EXCP_LR_LOG + i * 4 + offset;
++		dump->lr_stack[i] = mt76_rr(dev, reg);
+ 	}
+ 
+ 	/* start call stack record */
+ 	if (!exception) {
+-		mt76_set(dev, MT_MCU_WM_EXCP_PC_CTRL, BIT(0));
+-		mt76_set(dev, MT_MCU_WM_EXCP_LR_CTRL, BIT(0));
++		mt76_set(dev, MT_MCU_WM_EXCP_PC_CTRL + offset, BIT(0));
++		mt76_set(dev, MT_MCU_WM_EXCP_LR_CTRL + offset, BIT(0));
+ 	}
+ }
+ 
+-static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev)
++static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type)
+ {
+-	struct mt7996_crash_data *crash_data = dev->coredump.crash_data;
++	struct mt7996_crash_data *crash_data = dev->coredump.crash_data[type];
+ 	struct mt7996_coredump *dump;
+ 	struct mt7996_coredump_mem *dump_mem;
+ 	size_t len, sofar = 0, hdr_len = sizeof(*dump);
+@@ -186,20 +222,31 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev)
+ 
+ 	dump = (struct mt7996_coredump *)(buf);
+ 	dump->len = len;
++	dump->hdr_len = hdr_len;
+ 
+ 	/* plain text */
+ 	strscpy(dump->magic, "mt76-crash-dump", sizeof(dump->magic));
+ 	strscpy(dump->kernel, init_utsname()->release, sizeof(dump->kernel));
++	strscpy(dump->fw_type, ((type == MT7996_RAM_TYPE_WA) ? "WA" : "WM"),
++		sizeof(dump->fw_type));
+ 	strscpy(dump->fw_ver, dev->mt76.hw->wiphy->fw_version,
+ 		sizeof(dump->fw_ver));
++	strscpy(dump->fw_patch_date, dev->patch_build_date,
++		sizeof(dump->fw_patch_date));
++	strscpy(dump->fw_ram_date[MT7996_RAM_TYPE_WM],
++		dev->ram_build_date[MT7996_RAM_TYPE_WM],
++		MT7996_BUILD_TIME_LEN);
++	strscpy(dump->fw_ram_date[MT7996_RAM_TYPE_WA],
++		dev->ram_build_date[MT7996_RAM_TYPE_WA],
++		MT7996_BUILD_TIME_LEN);
+ 
+ 	guid_copy(&dump->guid, &crash_data->guid);
+ 	dump->tv_sec = crash_data->timestamp.tv_sec;
+ 	dump->tv_nsec = crash_data->timestamp.tv_nsec;
+ 	dump->device_id = mt76_chip(&dev->mt76);
+ 
+-	mt7996_coredump_fw_state(dev, dump, &exception);
+-	mt7996_coredump_fw_stack(dev, dump, exception);
++	mt7996_coredump_fw_state(dev, type, dump, &exception);
++	mt7996_coredump_fw_stack(dev, type, dump, exception);
+ 
+ 	/* gather memory content */
+ 	dump_mem = (struct mt7996_coredump_mem *)(buf + sofar);
+@@ -213,17 +260,19 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev)
+ 	return dump;
+ }
+ 
+-int mt7996_coredump_submit(struct mt7996_dev *dev)
++int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
+ {
+ 	struct mt7996_coredump *dump;
+ 
+-	dump = mt7996_coredump_build(dev);
++	dump = mt7996_coredump_build(dev, type);
+ 	if (!dump) {
+ 		dev_warn(dev->mt76.dev, "no crash dump data found\n");
+ 		return -ENODATA;
+ 	}
+ 
+ 	dev_coredumpv(dev->mt76.dev, dump, dump->len, GFP_KERNEL);
++	dev_info(dev->mt76.dev, "%s coredump completed\n",
++		 wiphy_name(dev->mt76.hw->wiphy));
+ 
+ 	return 0;
+ }
+@@ -231,23 +280,26 @@ int mt7996_coredump_submit(struct mt7996_dev *dev)
+ int mt7996_coredump_register(struct mt7996_dev *dev)
+ {
+ 	struct mt7996_crash_data *crash_data;
++	int i;
+ 
+-	crash_data = vzalloc(sizeof(*dev->coredump.crash_data));
+-	if (!crash_data)
+-		return -ENOMEM;
++	for (i = 0; i < MT7996_COREDUMP_MAX; i++) {
++		crash_data = vzalloc(sizeof(*dev->coredump.crash_data[i]));
++		if (!crash_data)
++			return -ENOMEM;
+ 
+-	dev->coredump.crash_data = crash_data;
++		dev->coredump.crash_data[i] = crash_data;
+ 
+-	if (coredump_memdump) {
+-		crash_data->memdump_buf_len = mt7996_coredump_get_mem_size(dev);
+-		if (!crash_data->memdump_buf_len)
+-			/* no memory content */
+-			return 0;
++		if (coredump_memdump) {
++			crash_data->memdump_buf_len = mt7996_coredump_get_mem_size(dev, i);
++			if (!crash_data->memdump_buf_len)
++				/* no memory content */
++				return 0;
+ 
+-		crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len);
+-		if (!crash_data->memdump_buf) {
+-			vfree(crash_data);
+-			return -ENOMEM;
++			crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len);
++			if (!crash_data->memdump_buf) {
++				vfree(crash_data);
++				return -ENOMEM;
++			}
+ 		}
+ 	}
+ 
+@@ -256,13 +308,17 @@ int mt7996_coredump_register(struct mt7996_dev *dev)
+ 
+ void mt7996_coredump_unregister(struct mt7996_dev *dev)
+ {
+-	if (dev->coredump.crash_data->memdump_buf) {
+-		vfree(dev->coredump.crash_data->memdump_buf);
+-		dev->coredump.crash_data->memdump_buf = NULL;
+-		dev->coredump.crash_data->memdump_buf_len = 0;
+-	}
++	int i;
+ 
+-	vfree(dev->coredump.crash_data);
+-	dev->coredump.crash_data = NULL;
++	for (i = 0; i < MT7996_COREDUMP_MAX; i++) {
++		if (dev->coredump.crash_data[i]->memdump_buf) {
++			vfree(dev->coredump.crash_data[i]->memdump_buf);
++			dev->coredump.crash_data[i]->memdump_buf = NULL;
++			dev->coredump.crash_data[i]->memdump_buf_len = 0;
++		}
++
++		vfree(dev->coredump.crash_data[i]);
++		dev->coredump.crash_data[i] = NULL;
++	}
+ }
+ 
+diff --git a/mt7996/coredump.h b/mt7996/coredump.h
+index af2ba219..01ed3731 100644
+--- a/mt7996/coredump.h
++++ b/mt7996/coredump.h
+@@ -6,10 +6,13 @@
+ 
+ #include "mt7996.h"
+ 
++#define MT7996_COREDUMP_MAX	(MT7996_RAM_TYPE_WA + 1)
++
+ struct mt7996_coredump {
+ 	char magic[16];
+ 
+ 	u32 len;
++	u32 hdr_len;
+ 
+ 	guid_t guid;
+ 
+@@ -21,17 +24,28 @@ struct mt7996_coredump {
+ 	char kernel[64];
+ 	/* firmware version */
+ 	char fw_ver[ETHTOOL_FWVERS_LEN];
++	char fw_patch_date[MT7996_BUILD_TIME_LEN];
++	char fw_ram_date[MT7996_COREDUMP_MAX][MT7996_BUILD_TIME_LEN];
+ 
+ 	u32 device_id;
+ 
++	/* fw type */
++	char fw_type[8];
++
+ 	/* exception state */
+ 	char fw_state[12];
+ 
+ 	/* program counters */
+-	char pc_current[16];
+-	u32 pc_stack[17];
+-	/* link registers */
+-	u32 lr_stack[16];
++	u32 pc_dbg_ctrl;
++	u32 pc_cur_idx;
++	u32 pc_cur[10];
++	/* PC registers */
++	u32 pc_stack[32];
++
++	u32 lr_dbg_ctrl;
++	u32 lr_cur_idx;
++	/* LR registers */
++	u32 lr_stack[32];
+ 
+ 	/* memory content */
+ 	u8 data[];
+@@ -43,6 +57,7 @@ struct mt7996_coredump_mem {
+ } __packed;
+ 
+ struct mt7996_mem_hdr {
++	char name[64];
+ 	u32 start;
+ 	u32 len;
+ 	u8 data[];
+@@ -58,27 +73,27 @@ struct mt7996_mem_region {
+ #ifdef CONFIG_DEV_COREDUMP
+ 
+ const struct mt7996_mem_region *
+-mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num);
+-struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev);
+-int mt7996_coredump_submit(struct mt7996_dev *dev);
++mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num);
++struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type);
++int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type);
+ int mt7996_coredump_register(struct mt7996_dev *dev);
+ void mt7996_coredump_unregister(struct mt7996_dev *dev);
+ 
+ #else /* CONFIG_DEV_COREDUMP */
+ 
+ static inline const struct mt7996_mem_region *
+-mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num)
++mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num)
+ {
+ 	return NULL;
+ }
+ 
+-static inline int mt7996_coredump_submit(struct mt7996_dev *dev)
++static inline int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
+ {
+ 	return 0;
+ }
+ 
+ static inline struct
+-mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev)
++mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type)
+ {
+ 	return NULL;
+ }
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 503e92c0..00396c82 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2007,28 +2007,25 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ }
+ 
+ /* firmware coredump */
+-void mt7996_mac_dump_work(struct work_struct *work)
++void mt7996_mac_fw_coredump(struct mt7996_dev *dev, u8 type)
+ {
+ 	const struct mt7996_mem_region *mem_region;
+ 	struct mt7996_crash_data *crash_data;
+-	struct mt7996_dev *dev;
+ 	struct mt7996_mem_hdr *hdr;
+ 	size_t buf_len;
+ 	int i;
+ 	u32 num;
+ 	u8 *buf;
+ 
+-	dev = container_of(work, struct mt7996_dev, dump_work);
+-
+ 	mutex_lock(&dev->dump_mutex);
+ 
+-	crash_data = mt7996_coredump_new(dev);
++	crash_data = mt7996_coredump_new(dev, type);
+ 	if (!crash_data) {
+ 		mutex_unlock(&dev->dump_mutex);
+-		goto skip_coredump;
++		return;
+ 	}
+ 
+-	mem_region = mt7996_coredump_get_mem_layout(dev, &num);
++	mem_region = mt7996_coredump_get_mem_layout(dev, type, &num);
+ 	if (!mem_region || !crash_data->memdump_buf_len) {
+ 		mutex_unlock(&dev->dump_mutex);
+ 		goto skip_memdump;
+@@ -2038,6 +2035,9 @@ void mt7996_mac_dump_work(struct work_struct *work)
+ 	buf_len = crash_data->memdump_buf_len;
+ 
+ 	/* dumping memory content... */
++	dev_info(dev->mt76.dev, "%s start coredump for %s\n",
++		 wiphy_name(dev->mt76.hw->wiphy),
++		 ((type == MT7996_RAM_TYPE_WA) ? "WA" : "WM"));
+ 	memset(buf, 0, buf_len);
+ 	for (i = 0; i < num; i++) {
+ 		if (mem_region->len > buf_len) {
+@@ -2054,6 +2054,7 @@ void mt7996_mac_dump_work(struct work_struct *work)
+ 		mt7996_memcpy_fromio(dev, buf, mem_region->start,
+ 				     mem_region->len);
+ 
++		strscpy(hdr->name, mem_region->name, sizeof(mem_region->name));
+ 		hdr->start = mem_region->start;
+ 		hdr->len = mem_region->len;
+ 
+@@ -2070,8 +2071,20 @@ void mt7996_mac_dump_work(struct work_struct *work)
+ 	mutex_unlock(&dev->dump_mutex);
+ 
+ skip_memdump:
+-	mt7996_coredump_submit(dev);
+-skip_coredump:
++	mt7996_coredump_submit(dev, type);
++}
++
++void mt7996_mac_dump_work(struct work_struct *work)
++{
++	struct mt7996_dev *dev;
++
++	dev = container_of(work, struct mt7996_dev, dump_work);
++	if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT)
++		mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WA);
++
++	if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WM_WDT)
++		mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WM);
++
+ 	queue_work(dev->mt76.wq, &dev->reset_work);
+ }
+ 
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index ae01a4eb..da92b33e 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2707,6 +2707,8 @@ static int mt7996_load_patch(struct mt7996_dev *dev)
+ 
+ 	dev_info(dev->mt76.dev, "HW/SW Version: 0x%x, Build Time: %.16s\n",
+ 		 be32_to_cpu(hdr->hw_sw_ver), hdr->build_date);
++	memcpy(dev->patch_build_date, hdr->build_date,
++	       sizeof(dev->patch_build_date));
+ 
+ 	for (i = 0; i < be32_to_cpu(hdr->desc.n_region); i++) {
+ 		struct mt7996_patch_sec *sec;
+@@ -2833,6 +2835,9 @@ static int __mt7996_load_ram(struct mt7996_dev *dev, const char *fw_type,
+ 	}
+ 
+ 	hdr = (const void *)(fw->data + fw->size - sizeof(*hdr));
++	memcpy(dev->ram_build_date[ram_type],
++	       hdr->build_date,
++	       sizeof(dev->ram_build_date[ram_type]));
+ 	dev_info(dev->mt76.dev, "%s Firmware Version: %.10s, Build Time: %.15s\n",
+ 		 fw_type, hdr->fw_ver, hdr->build_date);
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 16cefeac..7c2e6894 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -84,6 +84,8 @@
+ #define MT7996_CRIT_TEMP		110
+ #define MT7996_MAX_TEMP			120
+ 
++#define MT7996_BUILD_TIME_LEN		24
++
+ #define MT7996_RRO_MAX_SESSION		1024
+ #define MT7996_RRO_WINDOW_MAX_LEN	1024
+ #define MT7996_RRO_ADDR_ELEM_LEN	128
+@@ -127,6 +129,7 @@ enum mt7996_ram_type {
+ 	MT7996_RAM_TYPE_WM,
+ 	MT7996_RAM_TYPE_WA,
+ 	MT7996_RAM_TYPE_DSP,
++	__MT7996_RAM_TYPE_MAX,
+ };
+ 
+ enum mt7996_txq_id {
+@@ -320,9 +323,11 @@ struct mt7996_dev {
+ 	struct mutex dump_mutex;
+ #ifdef CONFIG_DEV_COREDUMP
+ 	struct {
+-		struct mt7996_crash_data *crash_data;
++		struct mt7996_crash_data *crash_data[__MT7996_RAM_TYPE_MAX];
+ 	} coredump;
+ #endif
++	char patch_build_date[MT7996_BUILD_TIME_LEN];
++	char ram_build_date[__MT7996_RAM_TYPE_MAX][MT7996_BUILD_TIME_LEN];
+ 
+ 	struct list_head sta_rc_list;
+ 	struct list_head twt_list;
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index cf12c5e0..4c20a67d 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -597,7 +597,8 @@ enum offs_rev {
+ 
+ /* FW MODE SYNC */
+ #define MT_FW_ASSERT_CNT			0x02208274
+-#define MT_FW_DUMP_STATE			0x02209e90
++#define MT_FW_WM_DUMP_STATE			0x02209e90
++#define MT_FW_WA_DUMP_STATE			0x7C05B080
+ 
+ #define MT_SWDEF_BASE				0x00401400
+ 
+@@ -714,11 +715,15 @@ enum offs_rev {
+ #define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR		BIT(29)
+ 
+ /* CONN MCU EXCP CON */
++#define MT_MCU_WA_EXCP_BASE			0x890d0000
+ #define MT_MCU_WM_EXCP_BASE			0x89050000
++
+ #define MT_MCU_WM_EXCP(ofs)			(MT_MCU_WM_EXCP_BASE + (ofs))
+ #define MT_MCU_WM_EXCP_PC_CTRL			MT_MCU_WM_EXCP(0x100)
++#define MT_MCU_WM_EXCP_PC_CTRL_IDX_STATUS	GENMASK(20, 16)
+ #define MT_MCU_WM_EXCP_PC_LOG			MT_MCU_WM_EXCP(0x104)
+ #define MT_MCU_WM_EXCP_LR_CTRL			MT_MCU_WM_EXCP(0x200)
++#define MT_MCU_WM_EXCP_LR_CTRL_IDX_STATUS	GENMASK(20, 16)
+ #define MT_MCU_WM_EXCP_LR_LOG			MT_MCU_WM_EXCP(0x204)
+ 
+ /* CONN AFE CTL CON */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0023-mtk-wifi-mt76-mt7996-add-normal-mode-pre-calibration.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0023-mtk-wifi-mt76-mt7996-add-normal-mode-pre-calibration.patch
deleted file mode 100644
index 7c11685..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0023-mtk-wifi-mt76-mt7996-add-normal-mode-pre-calibration.patch
+++ /dev/null
@@ -1,285 +0,0 @@
-From 51626ab22252946790a178fd0751bd7616fc2d99 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 1 Mar 2023 12:12:51 +0800
-Subject: [PATCH 023/116] mtk: wifi: mt76: mt7996: add normal mode
- pre-calibration support
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt76_connac_mcu.h |   1 +
- mt7996/eeprom.c   |   4 ++
- mt7996/eeprom.h   |   2 +
- mt7996/init.c     |   6 ++
- mt7996/main.c     |   6 ++
- mt7996/mcu.c      | 166 ++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/mt7996.h   |   3 +
- 7 files changed, 188 insertions(+)
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 1919325..8202ebb 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1279,6 +1279,7 @@ enum {
- 	MCU_UNI_CMD_PP = 0x38,
- 	MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
- 	MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
-+	MCU_UNI_CMD_PRECAL_RESULT = 0x47,
- 	MCU_UNI_CMD_RRO = 0x57,
- 	MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
- 	MCU_UNI_CMD_PER_STA_INFO = 0x6d,
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 62c1ad4..4afa2a2 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -356,6 +356,10 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
- 			return ret;
- 	}
- 
-+	ret = mt7996_eeprom_load_precal(dev);
-+	if (ret)
-+		return ret;
-+
- 	ret = mt7996_eeprom_parse_hw_cap(dev, &dev->phy);
- 	if (ret < 0)
- 		return ret;
-diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index 849b8bc..58179c0 100644
---- a/mt7996/eeprom.h
-+++ b/mt7996/eeprom.h
-@@ -25,6 +25,8 @@ enum mt7996_eeprom_field {
- 	MT_EE_TX0_POWER_6G =	0x1310,
- 
- 	__MT_EE_MAX =	0x1dff,
-+	/* 0x1e10 ~ 0x2d644 used to save group cal data */
-+	MT_EE_PRECAL =		0x1e10,
- };
- 
- #define MT_EE_WIFI_CONF0_TX_PATH		GENMASK(2, 0)
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 440e26d..201daf1 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -985,6 +985,12 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
- 	if (ret < 0)
- 		return ret;
- 
-+	if (dev->flash_mode) {
-+		ret = mt7996_mcu_apply_group_cal(dev);
-+		if (ret)
-+			return ret;
-+	}
-+
- 	/* Beacon and mgmt frames should occupy wcid 0 */
- 	idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
- 	if (idx)
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 50c7f07..89d5a17 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -317,6 +317,12 @@ int mt7996_set_channel(struct mt7996_phy *phy)
- 
- 	mt76_set_channel(phy->mt76);
- 
-+	if (dev->flash_mode) {
-+		ret = mt7996_mcu_apply_tx_dpd(phy);
-+		if (ret)
-+			goto out;
-+	}
-+
- 	if (mt76_testmode_enabled(phy->mt76) || phy->mt76->test.bf_en) {
- 		mt7996_tm_update_channel(phy);
- 		goto out;
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 1d4f421..f02d33c 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -3631,6 +3631,172 @@ int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num)
- 	return 0;
- }
- 
-+static int mt7996_mcu_set_pre_cal(struct mt7996_dev *dev, u32 idx,
-+				  u8 *cal, u32 len, u32 cal_id)
-+{
-+#define PRECAL_CMD_PRE_CAL_RESULT	0x0
-+	struct {
-+		/* fixed field */
-+		u8 action;
-+		u8 dest;
-+		u8 attribute;
-+		u8 tag_num;
-+
-+		__le16 tag;
-+		__le16 len;
-+
-+		__le32 cal_id;
-+		s8 precal;
-+		u8 band;
-+		u8 rsv[2];
-+		__le32 idx;
-+		__le32 cal_len;
-+	} req = {
-+		.tag = cpu_to_le16(PRECAL_CMD_PRE_CAL_RESULT),
-+		.len = cpu_to_le16(sizeof(req) - 4 + len),
-+		.cal_id = cpu_to_le32(cal_id),
-+		.idx = cpu_to_le32(idx),
-+		.cal_len = cpu_to_le32(len),
-+	};
-+	struct sk_buff *skb;
-+
-+	if (!len)
-+		return 0;
-+
-+	skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, sizeof(req) + len);
-+	if (!skb)
-+		return -ENOMEM;
-+
-+	skb_put_data(skb, &req, sizeof(req));
-+	skb_put_data(skb, cal, len);
-+
-+	return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(PRECAL_RESULT), false);
-+}
-+
-+int mt7996_mcu_apply_group_cal(struct mt7996_dev *dev)
-+{
-+	u8 *cal = dev->cal, *eeprom = dev->mt76.eeprom.data;
-+	u32 idx = 0, total_idx = MT_EE_CAL_GROUP_SIZE / MT_EE_CAL_UNIT;
-+	u32 offs = MT_EE_DO_PRE_CAL;
-+	int ret = 0;
-+
-+	if (!(eeprom[offs] & MT_EE_WIFI_CAL_GROUP))
-+		return 0;
-+
-+	for (idx = 0; idx < total_idx; idx++, cal += MT_EE_CAL_UNIT) {
-+		ret = mt7996_mcu_set_pre_cal(dev, idx, cal, MT_EE_CAL_UNIT, RF_PRE_CAL);
-+		if (ret)
-+			goto out;
-+	}
-+
-+	ret = mt7996_mcu_set_pre_cal(dev, total_idx, cal,
-+				     MT_EE_CAL_GROUP_SIZE % MT_EE_CAL_UNIT, RF_PRE_CAL);
-+
-+out:
-+	return ret;
-+}
-+
-+int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt76_phy *mphy = phy->mt76;
-+	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
-+	enum nl80211_band band = chandef->chan->band;
-+	enum nl80211_chan_width bw = chandef->width;
-+	const struct ieee80211_channel *chan_list;
-+	u32 cal_id, chan_list_size, base_offset = 0, offs = MT_EE_DO_PRE_CAL;
-+	u32 dpd_size_2g, dpd_size_5g, per_chan_size = DPD_PER_CH_BW20_SIZE;
-+	u16 channel = ieee80211_frequency_to_channel(chandef->center_freq1);
-+	u8 dpd_mask, *cal = dev->cal, *eeprom = dev->mt76.eeprom.data;
-+	int idx, i, ret;
-+
-+	dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
-+	dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
-+
-+	switch (band) {
-+	case NL80211_BAND_2GHZ:
-+		dpd_mask = MT_EE_WIFI_CAL_DPD_2G;
-+		/* channel 14 don't need DPD cal */
-+		if (channel >= 1 && channel <= 4)
-+			channel = 3;
-+		else if (channel >= 5 && channel <= 9)
-+			channel = 7;
-+		else if (channel >= 10 && channel <= 13)
-+			channel = 11;
-+		else
-+			return 0;
-+		cal_id = RF_DPD_FLAT_CAL;
-+		chan_list = dpd_2g_ch_list_bw20;
-+		chan_list_size = dpd_2g_bw20_ch_num;
-+		break;
-+	case NL80211_BAND_5GHZ:
-+		dpd_mask = MT_EE_WIFI_CAL_DPD_5G;
-+		cal_id = RF_DPD_FLAT_5G_CAL;
-+		chan_list = mphy->sband_5g.sband.channels;
-+		chan_list_size = mphy->sband_5g.sband.n_channels;
-+		base_offset += dpd_size_2g;
-+		if (bw == NL80211_CHAN_WIDTH_160) {
-+			base_offset += mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
-+			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
-+			cal_id = RF_DPD_FLAT_5G_MEM_CAL;
-+			chan_list = dpd_5g_ch_list_bw160;
-+			chan_list_size = dpd_5g_bw160_ch_num;
-+		} else if (bw > NL80211_CHAN_WIDTH_20) {
-+			/* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
-+			channel -= 2;
-+		}
-+		break;
-+	case NL80211_BAND_6GHZ:
-+		dpd_mask = MT_EE_WIFI_CAL_DPD_6G;
-+		cal_id = RF_DPD_FLAT_6G_CAL;
-+		chan_list = mphy->sband_6g.sband.channels;
-+		chan_list_size = mphy->sband_6g.sband.n_channels;
-+		base_offset += dpd_size_2g + dpd_size_5g;
-+		if (bw == NL80211_CHAN_WIDTH_160) {
-+			base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
-+			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
-+			cal_id = RF_DPD_FLAT_6G_MEM_CAL;
-+			chan_list = dpd_6g_ch_list_bw160;
-+			chan_list_size = dpd_6g_bw160_ch_num;
-+		} else if (bw == NL80211_CHAN_WIDTH_320) {
-+			base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
-+				       dpd_6g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
-+			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
-+			cal_id = RF_DPD_FLAT_6G_MEM_CAL;
-+			chan_list = dpd_6g_ch_list_bw320;
-+			chan_list_size = dpd_6g_bw320_ch_num;
-+		} else if (bw > NL80211_CHAN_WIDTH_20) {
-+			/* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
-+			channel -= 2;
-+		}
-+		break;
-+	default:
-+		dpd_mask = 0;
-+		break;
-+	}
-+
-+	if (!(eeprom[offs] & dpd_mask))
-+		return 0;
-+
-+	for (idx = 0; idx < chan_list_size; idx++)
-+		if (channel == chan_list[idx].hw_value)
-+			break;
-+	if (idx == chan_list_size)
-+		return -EINVAL;
-+
-+	cal += MT_EE_CAL_GROUP_SIZE + base_offset + idx * per_chan_size;
-+
-+	for (i = 0; i < per_chan_size / MT_EE_CAL_UNIT; i++) {
-+		ret = mt7996_mcu_set_pre_cal(dev, i, cal, MT_EE_CAL_UNIT, cal_id);
-+		if (ret)
-+			return ret;
-+
-+		cal += MT_EE_CAL_UNIT;
-+	}
-+
-+	return ret;
-+}
-+
- int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap)
- {
- #define NIC_CAP	3
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 121c4a1..5e2d8be 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -598,6 +598,9 @@ void mt7996_mcu_exit(struct mt7996_dev *dev);
- int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
- int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
- int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
-+int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
-+int mt7996_mcu_apply_group_cal(struct mt7996_dev *dev);
-+int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy);
- #ifdef CONFIG_NL80211_TESTMODE
- void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
- #endif
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0024-mtk-mt76-mt7996-for-build-pass.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0024-mtk-mt76-mt7996-for-build-pass.patch
new file mode 100644
index 0000000..b790e63
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0024-mtk-mt76-mt7996-for-build-pass.patch
@@ -0,0 +1,136 @@
+From 2b0fed32f1dcb4b51cc061816ece60647e497af8 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 3 Nov 2022 00:27:17 +0800
+Subject: [PATCH 024/199] mtk: mt76: mt7996: for build pass
+
+---
+ debugfs.c         | 3 +++
+ dma.c             | 2 +-
+ mcu.c             | 1 +
+ mt7615/mcu.c      | 1 +
+ mt76_connac_mcu.c | 1 +
+ mt7915/mcu.c      | 1 +
+ mt7996/dma.c      | 4 ++--
+ mt7996/eeprom.c   | 1 +
+ mt7996/mcu.c      | 1 +
+ 9 files changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/debugfs.c b/debugfs.c
+index c4649ba0..ac5207e5 100644
+--- a/debugfs.c
++++ b/debugfs.c
+@@ -33,8 +33,11 @@ mt76_napi_threaded_set(void *data, u64 val)
+ 	if (!mt76_is_mmio(dev))
+ 		return -EOPNOTSUPP;
+ 
++#if 0
++	/* need to backport patch from networking stack */
+ 	if (dev->napi_dev.threaded != val)
+ 		return dev_set_threaded(&dev->napi_dev, val);
++#endif
+ 
+ 	return 0;
+ }
+diff --git a/dma.c b/dma.c
+index f4f88c44..56044639 100644
+--- a/dma.c
++++ b/dma.c
+@@ -883,7 +883,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+ 		    !(dev->drv->rx_check(dev, data, len)))
+ 			goto free_frag;
+ 
+-		skb = napi_build_skb(data, q->buf_size);
++		skb = build_skb(data, q->buf_size);
+ 		if (!skb)
+ 			goto free_frag;
+ 
+diff --git a/mcu.c b/mcu.c
+index a8cafa39..fa4b0544 100644
+--- a/mcu.c
++++ b/mcu.c
+@@ -4,6 +4,7 @@
+  */
+ 
+ #include "mt76.h"
++#include <linux/moduleparam.h>
+ 
+ struct sk_buff *
+ __mt76_mcu_msg_alloc(struct mt76_dev *dev, const void *data,
+diff --git a/mt7615/mcu.c b/mt7615/mcu.c
+index d50d9678..18176e78 100644
+--- a/mt7615/mcu.c
++++ b/mt7615/mcu.c
+@@ -10,6 +10,7 @@
+ #include "mcu.h"
+ #include "mac.h"
+ #include "eeprom.h"
++#include <linux/moduleparam.h>
+ 
+ static bool prefer_offload_fw = true;
+ module_param(prefer_offload_fw, bool, 0644);
+diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
+index 4dce03dd..a1deeacd 100644
+--- a/mt76_connac_mcu.c
++++ b/mt76_connac_mcu.c
+@@ -4,6 +4,7 @@
+ #include <linux/firmware.h>
+ #include "mt76_connac2_mac.h"
+ #include "mt76_connac_mcu.h"
++#include <linux/module.h>
+ 
+ int mt76_connac_mcu_start_firmware(struct mt76_dev *dev, u32 addr, u32 option)
+ {
+diff --git a/mt7915/mcu.c b/mt7915/mcu.c
+index 71236fe7..3c1a8fd4 100644
+--- a/mt7915/mcu.c
++++ b/mt7915/mcu.c
+@@ -6,6 +6,7 @@
+ #include "mcu.h"
+ #include "mac.h"
+ #include "eeprom.h"
++#include <linux/moduleparam.h>
+ 
+ #define fw_name(_dev, name, ...)	({			\
+ 	char *_fw;						\
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index 73e633d0..759a58e8 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -641,8 +641,8 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 	if (ret < 0)
+ 		return ret;
+ 
+-	netif_napi_add_tx(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
+-			  mt7996_poll_tx);
++	netif_tx_napi_add(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
++			  mt7996_poll_tx, NAPI_POLL_WEIGHT);
+ 	napi_enable(&dev->mt76.tx_napi);
+ 
+ 	mt7996_dma_enable(dev, false);
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index f38629e3..bd02eb00 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -138,6 +138,7 @@ static int mt7996_eeprom_parse_efuse_hw_cap(struct mt7996_dev *dev)
+ 	if (ret)
+ 		return ret;
+ 
++	cap = 0x4b249248;	/* internal hardcode */
+ 	if (cap) {
+ 		dev->has_eht = !(cap & MODE_HE_ONLY);
+ 		dev->wtbl_size_group = u32_get_bits(cap, WTBL_SIZE_GROUP);
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index da92b33e..88c42de7 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -5,6 +5,7 @@
+ 
+ #include <linux/firmware.h>
+ #include <linux/fs.h>
++#include <linux/moduleparam.h>
+ #include "mt7996.h"
+ #include "mcu.h"
+ #include "mac.h"
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0024-mtk-wifi-mt76-mt7996-enable-SCS-feature-for-mt7996-d.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0024-mtk-wifi-mt76-mt7996-enable-SCS-feature-for-mt7996-d.patch
deleted file mode 100644
index d318959..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0024-mtk-wifi-mt76-mt7996-enable-SCS-feature-for-mt7996-d.patch
+++ /dev/null
@@ -1,304 +0,0 @@
-From db1c305572e6eba5df01d52472e027cb9ef745c6 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Mon, 8 May 2023 09:03:50 +0800
-Subject: [PATCH 024/116] mtk: wifi: mt76: mt7996: enable SCS feature for
- mt7996 driver
-
-Enable Smart Carrier Sense algorithn by default to improve performance
-in a noisy environment.
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- mt76_connac_mcu.h    |   1 +
- mt7996/init.c        |   1 +
- mt7996/mac.c         |   2 +
- mt7996/main.c        |   7 +++
- mt7996/mcu.c         | 105 +++++++++++++++++++++++++++++++++++++++++++
- mt7996/mcu.h         |   6 +++
- mt7996/mt7996.h      |  15 +++++++
- mt7996/mtk_debugfs.c |  11 +++++
- 8 files changed, 148 insertions(+)
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 8202ebb..49c5ba3 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1264,6 +1264,7 @@ enum {
- 	MCU_UNI_CMD_GET_STAT_INFO = 0x23,
- 	MCU_UNI_CMD_SNIFFER = 0x24,
- 	MCU_UNI_CMD_SR = 0x25,
-+	MCU_UNI_CMD_SCS = 0x26,
- 	MCU_UNI_CMD_ROC = 0x27,
- 	MCU_UNI_CMD_SET_DBDC_PARMS = 0x28,
- 	MCU_UNI_CMD_TXPOWER = 0x2b,
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 201daf1..6a394c3 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -1415,6 +1415,7 @@ int mt7996_register_device(struct mt7996_dev *dev)
- 	dev->mt76.phy.priv = &dev->phy;
- 	INIT_WORK(&dev->rc_work, mt7996_mac_sta_rc_work);
- 	INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7996_mac_work);
-+	INIT_DELAYED_WORK(&dev->scs_work, mt7996_mcu_scs_sta_poll);
- 	INIT_LIST_HEAD(&dev->sta_rc_list);
- 	INIT_LIST_HEAD(&dev->twt_list);
- 
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 603f6c0..c9f45ab 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1795,6 +1795,7 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
- 		cancel_delayed_work_sync(&phy2->mt76->mac_work);
- 	if (phy3)
- 		cancel_delayed_work_sync(&phy3->mt76->mac_work);
-+	cancel_delayed_work_sync(&dev->scs_work);
- 
- 	mutex_lock(&dev->mt76.mutex);
- 	for (i = 0; i < 10; i++) {
-@@ -1830,6 +1831,7 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
- 		ieee80211_queue_delayed_work(phy3->mt76->hw,
- 					     &phy3->mt76->mac_work,
- 					     MT7996_WATCHDOG_TIME);
-+	ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
- }
- 
- void mt7996_mac_reset_work(struct work_struct *work)
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 89d5a17..eb213f8 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -81,11 +81,17 @@ int mt7996_run(struct ieee80211_hw *hw)
- 	if (ret)
- 		goto out;
- 
-+	ret = mt7996_mcu_set_scs(phy, SCS_ENABLE);
-+	if (ret)
-+		goto out;
-+
- 	set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
- 
- 	ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
- 				     MT7996_WATCHDOG_TIME);
- 
-+	ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
-+
- 	if (!running)
- 		mt7996_mac_reset_counters(phy);
- 
-@@ -113,6 +119,7 @@ static void mt7996_stop(struct ieee80211_hw *hw)
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
- 
- 	cancel_delayed_work_sync(&phy->mt76->mac_work);
-+	cancel_delayed_work_sync(&dev->scs_work);
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index f02d33c..16a01b1 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -4813,3 +4813,108 @@ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 da
- 	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TXPOWER),
- 				 &req, sizeof(req), false);
- }
-+
-+int mt7996_mcu_set_scs_stats(struct mt7996_phy *phy)
-+{
-+	struct mt7996_scs_ctrl ctrl = phy->scs_ctrl;
-+	struct {
-+		u8 band_idx;
-+		u8 _rsv[3];
-+
-+		__le16 tag;
-+		__le16 len;
-+
-+		u8 _rsv2[6];
-+		s8 min_rssi;
-+		u8 _rsv3;
-+	} __packed req = {
-+		.band_idx = phy->mt76->band_idx,
-+		.tag = cpu_to_le16(UNI_CMD_SCS_SEND_DATA),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+
-+		.min_rssi = ctrl.sta_min_rssi,
-+	};
-+
-+	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SCS),
-+				 &req, sizeof(req), false);
-+}
-+
-+void mt7996_sta_rssi_work(void *data, struct ieee80211_sta *sta)
-+{
-+	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_phy *poll_phy = (struct mt7996_phy *) data;
-+
-+	if (poll_phy->scs_ctrl.sta_min_rssi > msta->ack_signal)
-+		poll_phy->scs_ctrl.sta_min_rssi = msta->ack_signal;
-+}
-+
-+void mt7996_mcu_scs_sta_poll(struct work_struct *work)
-+{
-+	struct mt7996_dev *dev = container_of(work, struct mt7996_dev,
-+				 scs_work.work);
-+	bool scs_enable_flag = false;
-+	u8 i;
-+
-+	for (i = 0; i < __MT_MAX_BAND; i++) {
-+		struct mt7996_phy *phy;
-+
-+		switch (i) {
-+		case MT_BAND0:
-+			phy = dev->mphy.priv;
-+			break;
-+		case MT_BAND1:
-+			phy = mt7996_phy2(dev);
-+			break;
-+		case MT_BAND2:
-+			phy = mt7996_phy3(dev);
-+			break;
-+		default:
-+			phy = NULL;
-+			break;
-+		}
-+
-+		if (!phy || !test_bit(MT76_STATE_RUNNING, &phy->mt76->state) ||
-+		    !phy->scs_ctrl.scs_enable)
-+			continue;
-+
-+		ieee80211_iterate_stations_atomic(phy->mt76->hw,
-+						  mt7996_sta_rssi_work, phy);
-+
-+		scs_enable_flag = true;
-+		if (mt7996_mcu_set_scs_stats(phy))
-+			dev_err(dev->mt76.dev, "Failed to send scs mcu cmd\n");
-+		phy->scs_ctrl.sta_min_rssi = 0;
-+	}
-+
-+	if (scs_enable_flag)
-+		ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
-+}
-+
-+
-+int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct {
-+		u8 band_idx;
-+		u8 _rsv[3];
-+
-+		__le16 tag;
-+		__le16 len;
-+
-+		u8 scs_enable;
-+		u8 _rsv2[3];
-+	} __packed req = {
-+		.band_idx = phy->mt76->band_idx,
-+		.tag = cpu_to_le16(UNI_CMD_SCS_ENABLE),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.scs_enable = enable,
-+	};
-+
-+	phy->scs_ctrl.scs_enable = enable;
-+
-+	if (enable == SCS_ENABLE)
-+		ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
-+
-+	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SCS),
-+				 &req, sizeof(req), false);
-+}
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index d5ac2b3..5953b25 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -966,6 +966,12 @@ enum pp_mode {
- 	PP_USR_MODE,
- };
- 
-+enum {
-+	UNI_CMD_SCS_SEND_DATA,
-+	UNI_CMD_SCS_SET_PD_THR_RANGE = 2,
-+	UNI_CMD_SCS_ENABLE,
-+};
-+
- #define MT7996_PATCH_SEC		GENMASK(31, 24)
- #define MT7996_PATCH_SCRAMBLE_KEY	GENMASK(15, 8)
- #define MT7996_PATCH_AES_KEY		GENMASK(7, 0)
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 5e2d8be..186af3c 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -234,6 +234,16 @@ struct mt7996_hif {
- 	int irq;
- };
- 
-+struct mt7996_scs_ctrl {
-+	u8 scs_enable;
-+	s8 sta_min_rssi;
-+};
-+
-+enum {
-+	SCS_DISABLE = 0,
-+	SCS_ENABLE,
-+};
-+
- struct mt7996_wed_rro_addr {
- 	u32 head_low;
- 	u32 head_high : 4;
-@@ -284,6 +294,8 @@ struct mt7996_phy {
- 	u8 pp_mode;
- 	u16 punct_bitmap;
- 
-+	struct mt7996_scs_ctrl scs_ctrl;
-+
- #ifdef CONFIG_NL80211_TESTMODE
- 	struct {
- 		u32 *reg_backup;
-@@ -330,6 +342,7 @@ struct mt7996_dev {
- 	struct work_struct rc_work;
- 	struct work_struct dump_work;
- 	struct work_struct reset_work;
-+	struct delayed_work scs_work;
- 	wait_queue_head_t reset_wait;
- 	struct {
- 		u32 state;
-@@ -604,6 +617,8 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy);
- #ifdef CONFIG_NL80211_TESTMODE
- void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
- #endif
-+int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
-+void mt7996_mcu_scs_sta_poll(struct work_struct *work);
- 
- static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
- {
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index d79e00c..24b331e 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -2430,6 +2430,16 @@ static int mt7996_token_read(struct seq_file *s, void *data)
- 	return 0;
- }
- 
-+static int
-+mt7996_scs_enable_set(void *data, u64 val)
-+{
-+	struct mt7996_phy *phy = data;
-+
-+	return mt7996_mcu_set_scs(phy, (u8) val);
-+}
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_scs_enable, NULL,
-+			 mt7996_scs_enable_set, "%lld\n");
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- 	struct mt7996_dev *dev = phy->dev;
-@@ -2500,6 +2510,7 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- 	debugfs_create_devm_seqfile(dev->mt76.dev, "token", dir, mt7996_token_read);
- 
- 	debugfs_create_u8("sku_disable", 0600, dir, &dev->dbg.sku_disable);
-+	debugfs_create_file("scs_enable", 0200, dir, phy, &fops_scs_enable);
- 
- 	return 0;
- }
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0025-mtk-mt76-mt7996-add-debug-tool.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0025-mtk-mt76-mt7996-add-debug-tool.patch
new file mode 100644
index 0000000..239b093
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0025-mtk-mt76-mt7996-add-debug-tool.patch
@@ -0,0 +1,5424 @@
+From ecccabe4621048b66cb36ceba76497cde04a8ec1 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 24 Mar 2023 14:02:32 +0800
+Subject: [PATCH 025/199] mtk: mt76: mt7996: add debug tool
+
+Add PSM bit in sta_info
+
+Remove the duplicate function in mtk_debugfs.c & mtk_debug_i.c
+Only enable mt7996_mcu_fw_log_2_host function in mcu.c
+
+Support more ids category NDPA/NDP TXD/FBK and debug log recommended by
+CTD members.
+
+This commit equals to run the follwoing commands on Logan driver:
+command:
+1. iwpriv ra0 set fw_dbg=1:84
+2. iwpriv ra0 set fw_dbg=2:84
+3. iwpriv ra0 set fw_dbg=1:101
+
+mtk: wifi: mt76: mt7996: add wtbl_info support for mt7992
+
+mtk: wifi: mt76: mt7996: add mt7992 & mt7996 CR debug offset revision
+
+mtk: wifi: mt76: mt7992: refactor code for FW log
+
+Refactor code for FW log.
+
+mtk: wifi: mt76: mt7996: support disable muru debug info when recording fwlog
+
+When we record fwlog, we will also enable recording muru debug info log by
+default. However, in certain test scenarios, this can result in
+recording too many logs, causing inconvenience during issue analysis.
+Therefore, this commit adds an debug option, fw_debug_muru_disable, in
+debugfs. User can modify this option to enable/disable recording muru
+debug info log.
+
+[Usage]
+Set:
+$ echo val > debugfs/fw_debug_muru_disable
+Get:
+$ cat debugfs/fw_debug_muru_disable
+
+val can be the following values:
+0 = enable recording muru debug info (Default value)
+1 = disable recording muru debug info
+
+mtk: wifi: mt76: mt7996: add adie id & ver dump
+
+Do not show fw version in fw_wm_info.
+The fw_wm_info is used to dump fw status when wm crash. When wm crash,
+we are not able to use any mcu command.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt76.h               |    2 +
+ mt7996/Makefile      |    4 +
+ mt7996/coredump.c    |   10 +-
+ mt7996/coredump.h    |    7 +
+ mt7996/debugfs.c     |  128 ++-
+ mt7996/mac.c         |    3 +
+ mt7996/mt7996.h      |   13 +
+ mt7996/mtk_debug.h   | 2286 ++++++++++++++++++++++++++++++++++++++
+ mt7996/mtk_debugfs.c | 2506 ++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mtk_mcu.c     |   39 +
+ mt7996/mtk_mcu.h     |   19 +
+ tools/CMakeLists.txt |    7 +
+ tools/fwlog.c        |   25 +-
+ 13 files changed, 5024 insertions(+), 25 deletions(-)
+ create mode 100644 mt7996/mtk_debug.h
+ create mode 100644 mt7996/mtk_debugfs.c
+ create mode 100644 mt7996/mtk_mcu.c
+ create mode 100644 mt7996/mtk_mcu.h
+
+diff --git a/mt76.h b/mt76.h
+index 45039377..beba1d91 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -403,6 +403,8 @@ struct mt76_txwi_cache {
+ 		struct sk_buff *skb;
+ 		void *ptr;
+ 	};
++
++	unsigned long jiffies;
+ };
+ 
+ struct mt76_rx_tid {
+diff --git a/mt7996/Makefile b/mt7996/Makefile
+index 07c8b555..a056b40e 100644
+--- a/mt7996/Makefile
++++ b/mt7996/Makefile
+@@ -1,4 +1,6 @@
+ # SPDX-License-Identifier: ISC
++EXTRA_CFLAGS += -DCONFIG_MT76_LEDS
++EXTRA_CFLAGS += -DCONFIG_MTK_DEBUG
+ 
+ obj-$(CONFIG_MT7996E) += mt7996e.o
+ 
+@@ -6,3 +8,5 @@ mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
+ 	     debugfs.o mmio.o
+ 
+ mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o
++
++mt7996e-y += mtk_debugfs.o mtk_mcu.o
+diff --git a/mt7996/coredump.c b/mt7996/coredump.c
+index 60b88085..a7f91b56 100644
+--- a/mt7996/coredump.c
++++ b/mt7996/coredump.c
+@@ -195,7 +195,7 @@ mt7996_coredump_fw_stack(struct mt7996_dev *dev, u8 type, struct mt7996_coredump
+ 	}
+ }
+ 
+-static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type)
++struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type, bool full_dump)
+ {
+ 	struct mt7996_crash_data *crash_data = dev->coredump.crash_data[type];
+ 	struct mt7996_coredump *dump;
+@@ -206,7 +206,7 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8
+ 
+ 	len = hdr_len;
+ 
+-	if (coredump_memdump && crash_data->memdump_buf_len)
++	if (full_dump && coredump_memdump && crash_data->memdump_buf_len)
+ 		len += sizeof(*dump_mem) + crash_data->memdump_buf_len;
+ 
+ 	sofar += hdr_len;
+@@ -248,6 +248,9 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8
+ 	mt7996_coredump_fw_state(dev, type, dump, &exception);
+ 	mt7996_coredump_fw_stack(dev, type, dump, exception);
+ 
++	if (!full_dump)
++		goto skip_dump_mem;
++
+ 	/* gather memory content */
+ 	dump_mem = (struct mt7996_coredump_mem *)(buf + sofar);
+ 	dump_mem->len = crash_data->memdump_buf_len;
+@@ -255,6 +258,7 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8
+ 		memcpy(dump_mem->data, crash_data->memdump_buf,
+ 		       crash_data->memdump_buf_len);
+ 
++skip_dump_mem:
+ 	mutex_unlock(&dev->dump_mutex);
+ 
+ 	return dump;
+@@ -264,7 +268,7 @@ int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
+ {
+ 	struct mt7996_coredump *dump;
+ 
+-	dump = mt7996_coredump_build(dev, type);
++	dump = mt7996_coredump_build(dev, type, true);
+ 	if (!dump) {
+ 		dev_warn(dev->mt76.dev, "no crash dump data found\n");
+ 		return -ENODATA;
+diff --git a/mt7996/coredump.h b/mt7996/coredump.h
+index 01ed3731..93cd84a0 100644
+--- a/mt7996/coredump.h
++++ b/mt7996/coredump.h
+@@ -75,6 +75,7 @@ struct mt7996_mem_region {
+ const struct mt7996_mem_region *
+ mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num);
+ struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type);
++struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type, bool full_dump);
+ int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type);
+ int mt7996_coredump_register(struct mt7996_dev *dev);
+ void mt7996_coredump_unregister(struct mt7996_dev *dev);
+@@ -92,6 +93,12 @@ static inline int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
+ 	return 0;
+ }
+ 
++static inline struct
++mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type, bool full_dump)
++{
++	return NULL;
++}
++
+ static inline struct
+ mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type)
+ {
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index a17c99a2..9671c15d 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -295,11 +295,39 @@ mt7996_fw_debug_wm_set(void *data, u64 val)
+ 		DEBUG_SPL,
+ 		DEBUG_RPT_RX,
+ 		DEBUG_RPT_RA = 68,
+-	} debug;
++		DEBUG_IDS_SND = 84,
++		DEBUG_IDS_PP = 93,
++		DEBUG_IDS_RA = 94,
++		DEBUG_IDS_BF = 95,
++		DEBUG_IDS_SR = 96,
++		DEBUG_IDS_RU = 97,
++		DEBUG_IDS_MUMIMO = 98,
++		DEBUG_IDS_ERR_LOG = 101,
++	};
++	u8 debug_category[] = {
++		DEBUG_TXCMD,
++		DEBUG_CMD_RPT_TX,
++		DEBUG_CMD_RPT_TRIG,
++		DEBUG_SPL,
++		DEBUG_RPT_RX,
++		DEBUG_RPT_RA,
++		DEBUG_IDS_SND,
++		DEBUG_IDS_PP,
++		DEBUG_IDS_RA,
++		DEBUG_IDS_BF,
++		DEBUG_IDS_SR,
++		DEBUG_IDS_RU,
++		DEBUG_IDS_MUMIMO,
++		DEBUG_IDS_ERR_LOG,
++	};
+ 	bool tx, rx, en;
+ 	int ret;
++	u8 i;
+ 
+ 	dev->fw_debug_wm = val ? MCU_FW_LOG_TO_HOST : 0;
++#ifdef CONFIG_MTK_DEBUG
++	dev->fw_debug_wm = val;
++#endif
+ 
+ 	if (dev->fw_debug_bin)
+ 		val = MCU_FW_LOG_RELAY;
+@@ -314,18 +342,21 @@ mt7996_fw_debug_wm_set(void *data, u64 val)
+ 	if (ret)
+ 		return ret;
+ 
+-	for (debug = DEBUG_TXCMD; debug <= DEBUG_RPT_RA; debug++) {
+-		if (debug == 67)
+-			continue;
+-
+-		if (debug == DEBUG_RPT_RX)
++	for (i = 0; i < ARRAY_SIZE(debug_category); i++) {
++		if (debug_category[i] == DEBUG_RPT_RX)
+ 			val = en && rx;
+ 		else
+ 			val = en && tx;
+ 
+-		ret = mt7996_mcu_fw_dbg_ctrl(dev, debug, val);
++		ret = mt7996_mcu_fw_dbg_ctrl(dev, debug_category[i], val);
+ 		if (ret)
+ 			return ret;
++
++		if (debug_category[i] == DEBUG_IDS_SND && en) {
++			ret = mt7996_mcu_fw_dbg_ctrl(dev, debug_category[i], 2);
++			if (ret)
++				return ret;
++		}
+ 	}
+ 
+ 	return 0;
+@@ -397,6 +428,39 @@ remove_buf_file_cb(struct dentry *f)
+ 	return 0;
+ }
+ 
++static int
++mt7996_fw_debug_muru_set(void *data)
++{
++	struct mt7996_dev *dev = data;
++	enum {
++		DEBUG_BSRP_STATUS = 256,
++		DEBUG_TX_DATA_BYTE_CONUT,
++		DEBUG_RX_DATA_BYTE_CONUT,
++		DEBUG_RX_TOTAL_BYTE_CONUT,
++		DEBUG_INVALID_TID_BSR,
++		DEBUG_UL_LONG_TERM_PPDU_TYPE,
++		DEBUG_DL_LONG_TERM_PPDU_TYPE,
++		DEBUG_PPDU_CLASS_TRIG_ONOFF,
++		DEBUG_AIRTIME_BUSY_STATUS,
++		DEBUG_UL_OFDMA_MIMO_STATUS,
++		DEBUG_RU_CANDIDATE,
++		DEBUG_MEC_UPDATE_AMSDU,
++	} debug;
++	int ret;
++
++	if (dev->fw_debug_muru_disable)
++		return 0;
++
++	for (debug = DEBUG_BSRP_STATUS; debug <= DEBUG_MEC_UPDATE_AMSDU; debug++) {
++		ret = mt7996_mcu_muru_dbg_info(dev, debug,
++					       dev->fw_debug_bin & BIT(0));
++		if (ret)
++			return ret;
++	}
++
++	return 0;
++}
++
+ static int
+ mt7996_fw_debug_bin_set(void *data, u64 val)
+ {
+@@ -405,17 +469,23 @@ mt7996_fw_debug_bin_set(void *data, u64 val)
+ 		.remove_buf_file = remove_buf_file_cb,
+ 	};
+ 	struct mt7996_dev *dev = data;
++	int ret;
+ 
+-	if (!dev->relay_fwlog)
++	if (!dev->relay_fwlog) {
+ 		dev->relay_fwlog = relay_open("fwlog_data", dev->debugfs_dir,
+ 					      1500, 512, &relay_cb, NULL);
+-	if (!dev->relay_fwlog)
+-		return -ENOMEM;
++		if (!dev->relay_fwlog)
++			return -ENOMEM;
++	}
+ 
+ 	dev->fw_debug_bin = val;
+ 
+ 	relay_reset(dev->relay_fwlog);
+ 
++	ret = mt7996_fw_debug_muru_set(dev);
++	if (ret)
++		return ret;
++
+ 	return mt7996_fw_debug_wm_set(dev, dev->fw_debug_wm);
+ }
+ 
+@@ -772,6 +842,30 @@ mt7996_rf_regval_set(void *data, u64 val)
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_rf_regval, mt7996_rf_regval_get,
+ 			 mt7996_rf_regval_set, "0x%08llx\n");
+ 
++static int
++mt7996_fw_debug_muru_disable_set(void *data, u64 val)
++{
++	struct mt7996_dev *dev = data;
++
++	dev->fw_debug_muru_disable = !!val;
++
++	return 0;
++}
++
++static int
++mt7996_fw_debug_muru_disable_get(void *data, u64 *val)
++{
++	struct mt7996_dev *dev = data;
++
++	*val = dev->fw_debug_muru_disable;
++
++	return 0;
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_muru_disable,
++			 mt7996_fw_debug_muru_disable_get,
++			 mt7996_fw_debug_muru_disable_set, "%lld\n");
++
+ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+@@ -807,10 +901,17 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ 		debugfs_create_devm_seqfile(dev->mt76.dev, "rdd_monitor", dir,
+ 					    mt7996_rdd_monitor);
+ 	}
++	debugfs_create_file("fw_debug_muru_disable", 0600, dir, dev,
++			    &fops_fw_debug_muru_disable);
+ 
+ 	if (phy == &dev->phy)
+ 		dev->debugfs_dir = dir;
+ 
++#ifdef CONFIG_MTK_DEBUG
++	debugfs_create_u16("wlan_idx", 0600, dir, &dev->wlan_idx);
++	mt7996_mtk_init_debugfs(phy, dir);
++#endif
++
+ 	return 0;
+ }
+ 
+@@ -822,7 +923,11 @@ mt7996_debugfs_write_fwlog(struct mt7996_dev *dev, const void *hdr, int hdrlen,
+ 	unsigned long flags;
+ 	void *dest;
+ 
++	if (!dev->relay_fwlog)
++		return;
++
+ 	spin_lock_irqsave(&lock, flags);
++
+ 	dest = relay_reserve(dev->relay_fwlog, hdrlen + len + 4);
+ 	if (dest) {
+ 		*(u32 *)dest = hdrlen + len;
+@@ -855,9 +960,6 @@ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int
+ 		.msg_type = cpu_to_le16(PKT_TYPE_RX_FW_MONITOR),
+ 	};
+ 
+-	if (!dev->relay_fwlog)
+-		return;
+-
+ 	hdr.serial_id = cpu_to_le16(dev->fw_debug_seq++);
+ 	hdr.timestamp = cpu_to_le32(mt76_rr(dev, MT_LPON_FRCR(0)));
+ 	hdr.len = *(__le16 *)data;
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 00396c82..52ea6796 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -945,6 +945,9 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 	id = mt76_token_consume(mdev, &t);
+ 	if (id < 0)
+ 		return id;
++#ifdef CONFIG_MTK_DEBUG
++	t->jiffies = jiffies;
++#endif
+ 
+ 	pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb);
+ 	memset(txwi_ptr, 0, MT_TXD_SIZE);
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 7c2e6894..bb7536ff 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -362,6 +362,7 @@ struct mt7996_dev {
+ 	u8 fw_debug_wa;
+ 	u8 fw_debug_bin;
+ 	u16 fw_debug_seq;
++	bool fw_debug_muru_disable;
+ 
+ 	struct dentry *debugfs_dir;
+ 	struct rchan *relay_fwlog;
+@@ -374,6 +375,17 @@ struct mt7996_dev {
+ 	spinlock_t reg_lock;
+ 
+ 	u8 wtbl_size_group;
++
++#ifdef CONFIG_MTK_DEBUG
++	u16 wlan_idx;
++	struct {
++		u8 sku_disable;
++		u32 fw_dbg_module;
++		u8 fw_dbg_lv;
++		u32 bcn_total_cnt[__MT_MAX_BAND];
++	} dbg;
++	const struct mt7996_dbg_reg_desc *dbg_reg;
++#endif
+ };
+ 
+ enum {
+@@ -670,6 +682,7 @@ u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id);
+ 
+ #ifdef CONFIG_MTK_DEBUG
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
++int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
+ #endif
+ 
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mtk_debug.h b/mt7996/mtk_debug.h
+new file mode 100644
+index 00000000..27d8f1cb
+--- /dev/null
++++ b/mt7996/mtk_debug.h
+@@ -0,0 +1,2286 @@
++#ifndef __MTK_DEBUG_H
++#define __MTK_DEBUG_H
++
++#ifdef CONFIG_MTK_DEBUG
++#define NO_SHIFT_DEFINE 0xFFFFFFFF
++#define BITS(m, n)              (~(BIT(m)-1) & ((BIT(n) - 1) | BIT(n)))
++
++#define GET_FIELD(_field, _reg)	\
++	({	\
++		(((_reg) & (_field##_MASK)) >> (_field##_SHIFT));	\
++	})
++
++#define __DBG_OFFS(id)		(dev->dbg_reg->offs_rev[(id)])
++
++enum dbg_offs_rev {
++	AGG_AALCR2,
++	AGG_AALCR3,
++	AGG_AALCR4,
++	AGG_AALCR5,
++	AGG_AALCR6,
++	AGG_AALCR7,
++	MIB_TDRCR0,
++	MIB_TDRCR1,
++	MIB_TDRCR2,
++	MIB_TDRCR3,
++	MIB_TDRCR4,
++	MIB_RSCR26,
++	MIB_TSCR18,
++	MIB_TRDR0,
++	MIB_TRDR2,
++	MIB_TRDR3,
++	MIB_TRDR4,
++	MIB_TRDR5,
++	MIB_TRDR6,
++	MIB_TRDR7,
++	MIB_TRDR8,
++	MIB_TRDR9,
++	MIB_TRDR10,
++	MIB_TRDR11,
++	MIB_TRDR12,
++	MIB_TRDR13,
++	MIB_TRDR14,
++	MIB_TRDR15,
++	MIB_MSR0,
++	MIB_MSR1,
++	MIB_MSR2,
++	MIB_MCTR5,
++	MIB_MCTR6,
++	__MT_DBG_OFFS_REV_MAX,
++};
++
++static const u32 mt7996_dbg_offs[] = {
++	[AGG_AALCR2]		= 0x128,
++	[AGG_AALCR3]		= 0x12c,
++	[AGG_AALCR4]		= 0x130,
++	[AGG_AALCR5]		= 0x134,
++	[AGG_AALCR6]		= 0x138,
++	[AGG_AALCR7]		= 0x13c,
++	[MIB_TDRCR0]		= 0x728,
++	[MIB_TDRCR1]		= 0x72c,
++	[MIB_TDRCR2]		= 0x730,
++	[MIB_TDRCR3]		= 0x734,
++	[MIB_TDRCR4]		= 0x738,
++	[MIB_RSCR26]		= 0x950,
++	[MIB_TSCR18]		= 0xa1c,
++	[MIB_TRDR0]		= 0xa24,
++	[MIB_TRDR2]		= 0xa2c,
++	[MIB_TRDR3]		= 0xa30,
++	[MIB_TRDR4]		= 0xa34,
++	[MIB_TRDR5]		= 0xa38,
++	[MIB_TRDR6]		= 0xa3c,
++	[MIB_TRDR7]		= 0xa40,
++	[MIB_TRDR8]		= 0xa44,
++	[MIB_TRDR9]		= 0xa48,
++	[MIB_TRDR10]		= 0xa4c,
++	[MIB_TRDR11]		= 0xa50,
++	[MIB_TRDR12]		= 0xa54,
++	[MIB_TRDR13]		= 0xa58,
++	[MIB_TRDR14]		= 0xa5c,
++	[MIB_TRDR15]		= 0xa60,
++	[MIB_MSR0]		= 0xa64,
++	[MIB_MSR1]		= 0xa68,
++	[MIB_MSR2]		= 0xa6c,
++	[MIB_MCTR5]		= 0xa70,
++	[MIB_MCTR6]		= 0xa74,
++};
++
++static const u32 mt7992_dbg_offs[] = {
++	[AGG_AALCR2]		= 0x12c,
++	[AGG_AALCR3]		= 0x130,
++	[AGG_AALCR4]		= 0x134,
++	[AGG_AALCR5]		= 0x138,
++	[AGG_AALCR6]		= 0x13c,
++	[AGG_AALCR7]		= 0x140,
++	[MIB_TDRCR0]		= 0x768,
++	[MIB_TDRCR1]		= 0x76c,
++	[MIB_TDRCR2]		= 0x770,
++	[MIB_TDRCR3]		= 0x774,
++	[MIB_TDRCR4]		= 0x778,
++	[MIB_RSCR26]		= 0x994,
++	[MIB_TSCR18]		= 0xb18,
++	[MIB_TRDR0]		= 0xb20,
++	[MIB_TRDR2]		= 0xb28,
++	[MIB_TRDR3]		= 0xb2c,
++	[MIB_TRDR4]		= 0xb30,
++	[MIB_TRDR5]		= 0xb34,
++	[MIB_TRDR6]		= 0xb38,
++	[MIB_TRDR7]		= 0xb3c,
++	[MIB_TRDR8]		= 0xb40,
++	[MIB_TRDR9]		= 0xb44,
++	[MIB_TRDR10]		= 0xb48,
++	[MIB_TRDR11]		= 0xb4c,
++	[MIB_TRDR12]		= 0xb50,
++	[MIB_TRDR13]		= 0xb54,
++	[MIB_TRDR14]		= 0xb58,
++	[MIB_TRDR15]		= 0xb5c,
++	[MIB_MSR0]		= 0xb60,
++	[MIB_MSR1]		= 0xb64,
++	[MIB_MSR2]		= 0xb68,
++	[MIB_MCTR5]		= 0xb6c,
++	[MIB_MCTR6]		= 0xb70,
++};
++
++/* used to differentiate between generations */
++struct mt7996_dbg_reg_desc {
++	const u32 id;
++	const u32 *offs_rev;
++};
++
++/* AGG */
++#define BN0_WF_AGG_TOP_BASE                                    0x820e2000
++#define BN1_WF_AGG_TOP_BASE                                    0x820f2000
++#define IP1_BN0_WF_AGG_TOP_BASE                                0x830e2000
++
++#define BN0_WF_AGG_TOP_SCR_ADDR                                (BN0_WF_AGG_TOP_BASE + 0x0) // 2000
++#define BN0_WF_AGG_TOP_SCR0_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x4) // 2004
++#define BN0_WF_AGG_TOP_SCR1_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x8) // 2008
++#define BN0_WF_AGG_TOP_BCR_ADDR                                (BN0_WF_AGG_TOP_BASE + 0xc) // 200C
++#define BN0_WF_AGG_TOP_BWCR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x10) // 2010
++#define BN0_WF_AGG_TOP_ARCR0_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x14) // 2014
++#define BN0_WF_AGG_TOP_ARUCR_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x18) // 2018
++#define BN0_WF_AGG_TOP_ARDCR_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x1c) // 201C
++#define BN0_WF_AGG_TOP_AALCR0_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x20) // 2020
++#define BN0_WF_AGG_TOP_AALCR1_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x24) // 2024
++#define BN0_WF_AGG_TOP_PCR0_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x28) // 2028
++#define BN0_WF_AGG_TOP_PCR1_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x2c) // 202C
++#define BN0_WF_AGG_TOP_TTCR0_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x30) // 2030
++#define BN0_WF_AGG_TOP_TTCR1_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x34) // 2034
++#define BN0_WF_AGG_TOP_ACR1_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x38) // 2038
++#define BN0_WF_AGG_TOP_ACR4_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x3c) // 203C
++#define BN0_WF_AGG_TOP_ACR5_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x40) // 2040
++#define BN0_WF_AGG_TOP_ACR6_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x44) // 2044
++#define BN0_WF_AGG_TOP_ACR8_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x4c) // 204C
++#define BN0_WF_AGG_TOP_MRCR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x50) // 2050
++#define BN0_WF_AGG_TOP_MMPDR_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x54) // 2054
++#define BN0_WF_AGG_TOP_GFPDR_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x58) // 2058
++#define BN0_WF_AGG_TOP_VHTPDR_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x5c) // 205C
++#define BN0_WF_AGG_TOP_HEPDR_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x60) // 2060
++#define BN0_WF_AGG_TOP_CTCR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x64) // 2064
++#define BN0_WF_AGG_TOP_ATCR3_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x68) // 2068
++#define BN0_WF_AGG_TOP_SRCR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x6c) // 206C
++#define BN0_WF_AGG_TOP_VBCR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x70) // 2070
++#define BN0_WF_AGG_TOP_TCR_ADDR                                (BN0_WF_AGG_TOP_BASE + 0x74) // 2074
++#define BN0_WF_AGG_TOP_SRHS_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x78) // 2078
++#define BN0_WF_AGG_TOP_DBRCR0_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x7c) // 207C
++#define BN0_WF_AGG_TOP_DBRCR1_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x80) // 2080
++#define BN0_WF_AGG_TOP_CTETCR_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x84) // 2084
++#define BN0_WF_AGG_TOP_WPDR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x88) // 2088
++#define BN0_WF_AGG_TOP_PLRPDR_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x8c) // 208C
++#define BN0_WF_AGG_TOP_CECR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x90) // 2090
++#define BN0_WF_AGG_TOP_OMRCR0_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x94) // 2094
++#define BN0_WF_AGG_TOP_OMRCR1_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x98) // 2098
++#define BN0_WF_AGG_TOP_OMRCR2_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x9c) // 209C
++#define BN0_WF_AGG_TOP_OMRCR3_ADDR                             (BN0_WF_AGG_TOP_BASE + 0xa0) // 20A0
++#define BN0_WF_AGG_TOP_TMCR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0xa4) // 20A4
++#define BN0_WF_AGG_TOP_TWTCR_ADDR                              (BN0_WF_AGG_TOP_BASE + 0xa8) // 20A8
++#define BN0_WF_AGG_TOP_TWTSTACR_ADDR                           (BN0_WF_AGG_TOP_BASE + 0xac) // 20AC
++#define BN0_WF_AGG_TOP_TWTE0TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xb0) // 20B0
++#define BN0_WF_AGG_TOP_TWTE1TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xb4) // 20B4
++#define BN0_WF_AGG_TOP_TWTE2TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xb8) // 20B8
++#define BN0_WF_AGG_TOP_TWTE3TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xbc) // 20BC
++#define BN0_WF_AGG_TOP_TWTE4TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xc0) // 20C0
++#define BN0_WF_AGG_TOP_TWTE5TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xc4) // 20C4
++#define BN0_WF_AGG_TOP_TWTE6TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xc8) // 20C8
++#define BN0_WF_AGG_TOP_TWTE7TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xcc) // 20CC
++#define BN0_WF_AGG_TOP_TWTE8TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xd0) // 20D0
++#define BN0_WF_AGG_TOP_TWTE9TB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xd4) // 20D4
++#define BN0_WF_AGG_TOP_TWTEATB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xd8) // 20D8
++#define BN0_WF_AGG_TOP_TWTEBTB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xdc) // 20DC
++#define BN0_WF_AGG_TOP_TWTECTB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xe0) // 20E0
++#define BN0_WF_AGG_TOP_TWTEDTB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xe4) // 20E4
++#define BN0_WF_AGG_TOP_TWTEETB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xe8) // 20E8
++#define BN0_WF_AGG_TOP_TWTEFTB_ADDR                            (BN0_WF_AGG_TOP_BASE + 0xec) // 20EC
++#define BN0_WF_AGG_TOP_ATCR0_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x108) // 2108
++#define BN0_WF_AGG_TOP_ATCR1_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x10c) // 210C
++#define BN0_WF_AGG_TOP_TCCR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x110) // 2110
++#define BN0_WF_AGG_TOP_TFCR_ADDR                               (BN0_WF_AGG_TOP_BASE + 0x114) // 2114
++#define BN0_WF_AGG_TOP_MUCR0_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x118) // 2118
++#define BN0_WF_AGG_TOP_MUCR1_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x11c) // 211C
++#define BN0_WF_AGG_TOP_AALCR2_ADDR                             (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR2))
++#define BN0_WF_AGG_TOP_AALCR3_ADDR                             (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR3))
++#define BN0_WF_AGG_TOP_AALCR4_ADDR                             (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR4))
++#define BN0_WF_AGG_TOP_AALCR5_ADDR                             (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR5))
++#define BN0_WF_AGG_TOP_AALCR6_ADDR                             (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR6))
++#define BN0_WF_AGG_TOP_AALCR7_ADDR                             (BN0_WF_AGG_TOP_BASE + __DBG_OFFS(AGG_AALCR7))
++#define BN0_WF_AGG_TOP_CSDCR0_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x150) // 2150
++#define BN0_WF_AGG_TOP_CSDCR1_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x154) // 2154
++#define BN0_WF_AGG_TOP_CSDCR2_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x158) // 2158
++#define BN0_WF_AGG_TOP_CSDCR3_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x15c) // 215C
++#define BN0_WF_AGG_TOP_CSDCR4_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x160) // 2160
++#define BN0_WF_AGG_TOP_DYNSCR_ADDR                             (BN0_WF_AGG_TOP_BASE + 0x178) // 2178
++#define BN0_WF_AGG_TOP_DYNSSCR_ADDR                            (BN0_WF_AGG_TOP_BASE + 0x198) // 2198
++#define BN0_WF_AGG_TOP_TCDCNT0_ADDR                            (BN0_WF_AGG_TOP_BASE + 0x2c8) // 22C8
++#define BN0_WF_AGG_TOP_TCDCNT1_ADDR                            (BN0_WF_AGG_TOP_BASE + 0x2cc) // 22CC
++#define BN0_WF_AGG_TOP_TCSR0_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x2d0) // 22D0
++#define BN0_WF_AGG_TOP_TCSR1_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x2d4) // 22D4
++#define BN0_WF_AGG_TOP_TCSR2_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x2d8) // 22D8
++#define BN0_WF_AGG_TOP_DCR_ADDR                                (BN0_WF_AGG_TOP_BASE + 0x2e4) // 22E4
++#define BN0_WF_AGG_TOP_SMDCR_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x2e8) // 22E8
++#define BN0_WF_AGG_TOP_TXCMDSMCR_ADDR                          (BN0_WF_AGG_TOP_BASE + 0x2ec) // 22EC
++#define BN0_WF_AGG_TOP_SMCR0_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x2f0) // 22F0
++#define BN0_WF_AGG_TOP_SMCR1_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x2f4) // 22F4
++#define BN0_WF_AGG_TOP_SMCR2_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x2f8) // 22F8
++#define BN0_WF_AGG_TOP_SMCR3_ADDR                              (BN0_WF_AGG_TOP_BASE + 0x2fc) // 22FC
++
++#define BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR0_ADDR
++#define BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_MASK              0x03FF0000                // AC01_AGG_LIMIT[25..16]
++#define BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_SHFT              16
++#define BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR0_ADDR
++#define BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_MASK              0x000003FF                // AC00_AGG_LIMIT[9..0]
++#define BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_SHFT              0
++
++#define BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR1_ADDR
++#define BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_MASK              0x03FF0000                // AC03_AGG_LIMIT[25..16]
++#define BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_SHFT              16
++#define BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR1_ADDR
++#define BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_MASK              0x000003FF                // AC02_AGG_LIMIT[9..0]
++#define BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_SHFT              0
++
++#define BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR2_ADDR
++#define BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_MASK              0x03FF0000                // AC11_AGG_LIMIT[25..16]
++#define BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_SHFT              16
++#define BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR2_ADDR
++#define BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_MASK              0x000003FF                // AC10_AGG_LIMIT[9..0]
++#define BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_SHFT              0
++
++#define BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR3_ADDR
++#define BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_MASK              0x03FF0000                // AC13_AGG_LIMIT[25..16]
++#define BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_SHFT              16
++#define BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR3_ADDR
++#define BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_MASK              0x000003FF                // AC12_AGG_LIMIT[9..0]
++#define BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_SHFT              0
++
++#define BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR4_ADDR
++#define BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_MASK              0x03FF0000                // AC21_AGG_LIMIT[25..16]
++#define BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_SHFT              16
++#define BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR4_ADDR
++#define BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_MASK              0x000003FF                // AC20_AGG_LIMIT[9..0]
++#define BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_SHFT              0
++
++#define BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR5_ADDR
++#define BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_MASK              0x03FF0000                // AC23_AGG_LIMIT[25..16]
++#define BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_SHFT              16
++#define BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR5_ADDR
++#define BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_MASK              0x000003FF                // AC22_AGG_LIMIT[9..0]
++#define BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_SHFT              0
++
++#define BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR6_ADDR
++#define BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_MASK              0x03FF0000                // AC31_AGG_LIMIT[25..16]
++#define BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_SHFT              16
++#define BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR6_ADDR
++#define BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_MASK              0x000003FF                // AC30_AGG_LIMIT[9..0]
++#define BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_SHFT              0
++#define BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR7_ADDR
++#define BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_MASK              0x03FF0000                // AC33_AGG_LIMIT[25..16]
++#define BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_SHFT              16
++#define BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_ADDR              BN0_WF_AGG_TOP_AALCR7_ADDR
++#define BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_MASK              0x000003FF                // AC32_AGG_LIMIT[9..0]
++#define BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_SHFT              0
++
++/* DMA */
++struct queue_desc {
++	u32 hw_desc_base;
++	u16 ring_size;
++	char *const ring_info;
++};
++
++// HOST DMA
++#define WF_WFDMA_HOST_DMA0_BASE                                0xd4000
++
++#define WF_WFDMA_HOST_DMA0_HOST_INT_STA_ADDR                                   \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x200) /* 4200 */
++#define WF_WFDMA_HOST_DMA0_HOST_INT_ENA_ADDR                                   \
++	(WF_WFDMA_HOST_DMA0_BASE + 0X204) /* 4204 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR                                  \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x208) /* 4208 */
++
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_ADDR                      \
++	WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK                      \
++	0x00000008 /* RX_DMA_BUSY[3] */
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT 3
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_ADDR                        \
++	WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_MASK                        \
++	0x00000004 /* RX_DMA_EN[2] */
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_SHFT 2
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_ADDR                      \
++	WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK                      \
++	0x00000002 /* TX_DMA_BUSY[1] */
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT 1
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_ADDR                        \
++	WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_MASK                        \
++	0x00000001 /* TX_DMA_EN[0] */
++#define WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_SHFT 0
++
++
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL0_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x300) /* 4300 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL1_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x304) /* 4304 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL2_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x308) /* 4308 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL3_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x30c) /* 430C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL0_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x310) /* 4310 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL1_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x314) /* 4314 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL2_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x318) /* 4318 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL3_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x31c) /* 431C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL0_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x320) /* 4320 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL1_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x324) /* 4324 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL2_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x328) /* 4328 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL3_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x32c) /* 432C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL0_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x330) /* 4330 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL1_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x334) /* 4334 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL2_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x338) /* 4338 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL3_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x33c) /* 433C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL0_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x340) /* 4340 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL1_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x344) /* 4344 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL2_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x348) /* 4348 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL3_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x34c) /* 434C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL0_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x350) /* 4350 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL1_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x354) /* 4354 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL2_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x358) /* 4358 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL3_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x35c) /* 435C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL0_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x360) /* 4360 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL1_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x364) /* 4364 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL2_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x368) /* 4368 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL3_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x36c) /* 436C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL0_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x400) /* 4400 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL1_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x404) /* 4404 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL2_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x408) /* 4408 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL3_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x40c) /* 440C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL0_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x410) /* 4410 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL1_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x414) /* 4414 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL2_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x418) /* 4418 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL3_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x41c) /* 441C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL0_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x420) /* 4420 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL1_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x424) /* 4424 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL2_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x428) /* 4428 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL3_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x42c) /* 442C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL0_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x430) /* 4430 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL1_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x434) /* 4434 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL2_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x438) /* 4438 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL3_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x43c) /* 443C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL0_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x440) /* 4440 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL1_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x444) /* 4444 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL2_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x448) /* 4448 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL3_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x44c) /* 444C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL0_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x450) /* 4450 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL1_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x454) /* 4454 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL2_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x458) /* 4458 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL3_ADDR                          \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x45c) /* 445c */
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL0_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x460) // 4460
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL1_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x464) // 4464
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL2_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x468) // 4468
++#define WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL3_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x46c) // 446C
++
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL0_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x500) /* 4500 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL1_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x504) /* 4504 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL2_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x508) /* 4508 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL3_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x50c) /* 450C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL0_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x510) /* 4510 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL1_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x514) /* 4514 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL2_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x518) /* 4518 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL3_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x51c) /* 451C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL0_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x520) /* 4520 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL1_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x524) /* 4524 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL2_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x528) /* 4528 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL3_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x52C) /* 452C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL0_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x530) /* 4530 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL1_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x534) /* 4534 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL2_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x538) /* 4538 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL3_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x53C) /* 453C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL0_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x540) /* 4540 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL1_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x544) /* 4544 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL2_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x548) /* 4548 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL3_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x54c) /* 454C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL0_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x550) /* 4550 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL1_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x554) /* 4554 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL2_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x558) /* 4558 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL3_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x55c) /* 455C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x560) /* 4560 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL1_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x564) /* 4564 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL2_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x568) /* 4568 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL3_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x56c) /* 456C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL0_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x570) /* 4570 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL1_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x574) /* 4574 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL2_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x578) /* 4578 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL3_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x57c) /* 457C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL0_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x580) /* 4580 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL1_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x584) /* 4584 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL2_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x588) /* 4588 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL3_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x58c) /* 458C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x590) /* 4590 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL1_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x594) /* 4594 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL2_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x598) /* 4598 */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL3_ADDR                           \
++	(WF_WFDMA_HOST_DMA0_BASE + 0x59c) /* 459C */
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL0_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5a0) // 45A0
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL1_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5a4) // 45A4
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL2_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5a8) // 45A8
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL3_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5ac) // 45AC
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL0_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5b0) // 45B0
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL1_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5b4) // 45B4
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL2_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5b8) // 45B8
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL3_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5bc) // 45BC
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL0_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5C0) // 45C0
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL1_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5C4) // 45C4
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL2_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5C8) // 45C8
++#define WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL3_ADDR          (WF_WFDMA_HOST_DMA0_BASE + 0x5CC) // 45CC
++
++// HOST PCIE1 DMA
++#define WF_WFDMA_HOST_DMA0_PCIE1_BASE				0xd8000
++
++#define WF_WFDMA_HOST_DMA0_PCIE1_HOST_INT_STA_ADDR		(WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x200) // 8200
++#define WF_WFDMA_HOST_DMA0_PCIE1_HOST_INT_ENA_ADDR		(WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0X204) // 8204
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_ADDR		(WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x208) // 8208
++
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_PDMA_BT_SIZE_SHFT	4
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK		0x00000008
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT		3
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_EN_MASK		0x00000004
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_EN_SHFT		2
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK		0x00000002
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT		1
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_EN_MASK		0x00000001
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_EN_SHFT		0
++
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL0_ADDR    (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x450) // 8450
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL1_ADDR    (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x454) // 8454
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL2_ADDR    (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x458) // 8458
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL3_ADDR    (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x45c) // 845C
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL0_ADDR    (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x460) // 8460
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL1_ADDR    (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x464) // 8464
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL2_ADDR    (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x468) // 8468
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL3_ADDR    (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x46c) // 846C
++
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL0_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x530) // 8530
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL1_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x534) // 8534
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL2_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x538) // 8538
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL3_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x53C) // 853C
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL0_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x550) // 8550
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL1_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x554) // 8554
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL2_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x558) // 8558
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL3_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x55c) // 855C
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL0_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x560) // 8560
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL1_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x564) // 8564
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL2_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x568) // 8568
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL3_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x56c) // 856C
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL0_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x570) // 8570
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL1_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x574) // 8574
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL2_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x578) // 8578
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL3_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x57c) // 857C
++//MCU DMA
++//#define WF_WFDMA_MCU_DMA0_BASE                                 0x02000
++#define WF_WFDMA_MCU_DMA0_BASE                                 0x54000000
++
++#define WF_WFDMA_MCU_DMA0_HOST_INT_STA_ADDR                    (WF_WFDMA_MCU_DMA0_BASE + 0x200) // 0200
++#define WF_WFDMA_MCU_DMA0_HOST_INT_ENA_ADDR                    (WF_WFDMA_MCU_DMA0_BASE + 0X204) // 0204
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR                   (WF_WFDMA_MCU_DMA0_BASE + 0x208) // 0208
++
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_ADDR       WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK       0x00000008                // RX_DMA_BUSY[3]
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT       3
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_ADDR         WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_MASK         0x00000004                // RX_DMA_EN[2]
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_SHFT         2
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_ADDR       WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK       0x00000002                // TX_DMA_BUSY[1]
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT       1
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_ADDR         WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_MASK         0x00000001                // TX_DMA_EN[0]
++#define WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_SHFT         0
++
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x300) // 0300
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x304) // 0304
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x308) // 0308
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x30c) // 030C
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x310) // 0310
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x314) // 0314
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x318) // 0318
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x31c) // 031C
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x320) // 0320
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x324) // 0324
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x328) // 0328
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x32c) // 032C
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x330) // 0330
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x334) // 0334
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x338) // 0338
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x33c) // 033C
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x340) // 0340
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x344) // 0344
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x348) // 0348
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x34c) // 034C
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x350) // 0350
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x354) // 0354
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x358) // 0358
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x35c) // 035C
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x360) // 0360
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x364) // 0364
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x368) // 0368
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x36c) // 036C
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING7_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x370) // 0370
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING7_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x374) // 0374
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING7_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x378) // 0378
++#define WF_WFDMA_MCU_DMA0_WPDMA_TX_RING7_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x37c) // 037C
++
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x500) // 0500
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x504) // 0504
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x508) // 0508
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x50c) // 050C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x510) // 0510
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x514) // 0514
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x518) // 0518
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x51c) // 051C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x520) // 0520
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x524) // 0524
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x528) // 0528
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x52C) // 052C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x530) // 0530
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x534) // 0534
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x538) // 0538
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x53C) // 053C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x540) // 0540
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x544) // 0544
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x548) // 0548
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x54C) // 054C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x550) // 0550
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x554) // 0554
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x558) // 0558
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x55C) // 055C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x560) // 0560
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x564) // 0564
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x568) // 0568
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x56c) // 056C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x570) // 0570
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x574) // 0574
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x578) // 0578
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x57c) // 057C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x580) // 0580
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x584) // 0584
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x588) // 0588
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x58c) // 058C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL0_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x590) // 0590
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL1_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x594) // 0594
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL2_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x598) // 0598
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL3_ADDR            (WF_WFDMA_MCU_DMA0_BASE + 0x59c) // 059C
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL0_ADDR           (WF_WFDMA_MCU_DMA0_BASE + 0x5A0) // 05A0
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL1_ADDR           (WF_WFDMA_MCU_DMA0_BASE + 0x5A4) // 05A4
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL2_ADDR           (WF_WFDMA_MCU_DMA0_BASE + 0x5A8) // 05A8
++#define WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL3_ADDR           (WF_WFDMA_MCU_DMA0_BASE + 0x5Ac) // 05AC
++
++// MEM DMA
++#define WF_WFDMA_MEM_DMA_BASE                                  0x58000000
++
++#define WF_WFDMA_MEM_DMA_HOST_INT_STA_ADDR                     (WF_WFDMA_MEM_DMA_BASE + 0x200) // 0200
++#define WF_WFDMA_MEM_DMA_HOST_INT_ENA_ADDR                     (WF_WFDMA_MEM_DMA_BASE + 0X204) // 0204
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR                    (WF_WFDMA_MEM_DMA_BASE + 0x208) // 0208
++
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_ADDR        WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK        0x00000008                // RX_DMA_BUSY[3]
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT        3
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_ADDR          WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_MASK          0x00000004                // RX_DMA_EN[2]
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_SHFT          2
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_ADDR        WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK        0x00000002                // TX_DMA_BUSY[1]
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT        1
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_ADDR          WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_MASK          0x00000001                // TX_DMA_EN[0]
++#define WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_SHFT          0
++
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL0_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x300) // 0300
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL1_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x304) // 0304
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL2_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x308) // 0308
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL3_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x30c) // 030C
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL0_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x310) // 0310
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL1_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x314) // 0314
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL2_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x318) // 0318
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL3_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x31c) // 031C
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING2_CTRL0_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x320) // 0320
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING2_CTRL1_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x324) // 0324
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING2_CTRL2_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x328) // 0328
++#define WF_WFDMA_MEM_DMA_WPDMA_TX_RING2_CTRL3_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x32c) // 032C
++
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL0_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x500) // 0500
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL1_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x504) // 0504
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL2_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x508) // 0508
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL3_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x50c) // 050C
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL0_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x510) // 0510
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL1_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x514) // 0514
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL2_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x518) // 0518
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL3_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x51c) // 051C
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING2_CTRL0_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x520) // 0520
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING2_CTRL1_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x524) // 0524
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING2_CTRL2_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x528) // 0528
++#define WF_WFDMA_MEM_DMA_WPDMA_RX_RING2_CTRL3_ADDR             (WF_WFDMA_MEM_DMA_BASE + 0x52C) // 052C
++
++/* MIB */
++#define WF_UMIB_TOP_BASE                                       0x820cd000
++#define BN0_WF_MIB_TOP_BASE                                    0x820ed000
++#define BN1_WF_MIB_TOP_BASE                                    0x820fd000
++#define IP1_BN0_WF_MIB_TOP_BASE                                0x830ed000
++
++#define WF_UMIB_TOP_B0BROCR_ADDR                               (WF_UMIB_TOP_BASE + 0x484) // D484
++#define WF_UMIB_TOP_B0BRBCR_ADDR                               (WF_UMIB_TOP_BASE + 0x4D4) // D4D4
++#define WF_UMIB_TOP_B0BRDCR_ADDR                               (WF_UMIB_TOP_BASE + 0x524) // D524
++#define WF_UMIB_TOP_B1BROCR_ADDR                               (WF_UMIB_TOP_BASE + 0x5E8) // D5E8
++#define WF_UMIB_TOP_B2BROCR_ADDR                               (WF_UMIB_TOP_BASE + 0x74C) // D74C
++
++#define BN0_WF_MIB_TOP_M0SCR0_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x000) // D000
++#define BN0_WF_MIB_TOP_M0SDR6_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x020) // D020
++#define BN0_WF_MIB_TOP_M0SDR9_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x024) // D024
++#define BN0_WF_MIB_TOP_M0SDR18_ADDR                            (BN0_WF_MIB_TOP_BASE + 0x030) // D030
++#define BN0_WF_MIB_TOP_BTOCR_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x400) // D400
++#define BN0_WF_MIB_TOP_BTBCR_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x450) // D450
++#define BN0_WF_MIB_TOP_BTDCR_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x590) // D590
++#define BN0_WF_MIB_TOP_BTCR_ADDR                               (BN0_WF_MIB_TOP_BASE + 0x5A0) // D5A0
++#define BN0_WF_MIB_TOP_RVSR0_ADDR                              (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RVSR0))
++
++#define BN0_WF_MIB_TOP_TSCR0_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6B0) // D6B0
++#define BN0_WF_MIB_TOP_TSCR3_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6BC) // D6BC
++#define BN0_WF_MIB_TOP_TSCR4_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6C0) // D6C0
++#define BN0_WF_MIB_TOP_TSCR5_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6C4) // D6C4
++#define BN0_WF_MIB_TOP_TSCR6_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6C8) // D6C8
++#define BN0_WF_MIB_TOP_TSCR7_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6D0) // D6D0
++#define BN0_WF_MIB_TOP_TSCR8_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6CC) // D6CC
++
++#define BN0_WF_MIB_TOP_TBCR0_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6EC) // D6EC
++#define BN0_WF_MIB_TOP_TBCR1_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6F0) // D6F0
++#define BN0_WF_MIB_TOP_TBCR2_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6F4) // D6F4
++#define BN0_WF_MIB_TOP_TBCR3_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6F8) // D6F8
++#define BN0_WF_MIB_TOP_TBCR4_ADDR                              (BN0_WF_MIB_TOP_BASE + 0x6FC) // D6FC
++
++#define BN0_WF_MIB_TOP_TDRCR0_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR0))
++#define BN0_WF_MIB_TOP_TDRCR1_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR1))
++#define BN0_WF_MIB_TOP_TDRCR2_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR2))
++#define BN0_WF_MIB_TOP_TDRCR3_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR3))
++#define BN0_WF_MIB_TOP_TDRCR4_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TDRCR4))
++
++#define BN0_WF_MIB_TOP_BTSCR0_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x5E0) // D5E0
++#define BN0_WF_MIB_TOP_BTSCR1_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x5F0) // D5F0
++#define BN0_WF_MIB_TOP_BTSCR2_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x600) // D600
++#define BN0_WF_MIB_TOP_BTSCR3_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x610) // D610
++#define BN0_WF_MIB_TOP_BTSCR4_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x620) // D620
++#define BN0_WF_MIB_TOP_BTSCR5_ADDR                             (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_BTSCR5))
++#define BN0_WF_MIB_TOP_BTSCR6_ADDR                             (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_BTSCR6))
++
++#define BN0_WF_MIB_TOP_RSCR1_ADDR                              (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR1))
++#define BN0_WF_MIB_TOP_BSCR2_ADDR                              (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_BSCR2))
++#define BN0_WF_MIB_TOP_TSCR18_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TSCR18))
++
++#define BN0_WF_MIB_TOP_MSR0_ADDR                               (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MSR0))
++#define BN0_WF_MIB_TOP_MSR1_ADDR                               (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MSR1))
++#define BN0_WF_MIB_TOP_MSR2_ADDR                               (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MSR2))
++#define BN0_WF_MIB_TOP_MCTR5_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MCTR5))
++#define BN0_WF_MIB_TOP_MCTR6_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_MCTR6))
++
++#define BN0_WF_MIB_TOP_RSCR26_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_RSCR26))
++#define BN0_WF_MIB_TOP_RSCR27_ADDR                             (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR27))
++#define BN0_WF_MIB_TOP_RSCR28_ADDR                             (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR28))
++#define BN0_WF_MIB_TOP_RSCR31_ADDR                             (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR31))
++#define BN0_WF_MIB_TOP_RSCR33_ADDR                             (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR33))
++#define BN0_WF_MIB_TOP_RSCR35_ADDR                             (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR35))
++#define BN0_WF_MIB_TOP_RSCR36_ADDR                             (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_RSCR36))
++
++#define BN0_WF_MIB_TOP_TSCR3_AMPDU_MPDU_COUNT_MASK             0xFFFFFFFF                // AMPDU_MPDU_COUNT[31..0]
++#define BN0_WF_MIB_TOP_TSCR4_AMPDU_ACKED_COUNT_MASK            0xFFFFFFFF                // AMPDU_ACKED_COUNT[31..0]
++#define BN0_WF_MIB_TOP_M0SDR6_CHANNEL_IDLE_COUNT_MASK          0x0000FFFF                // CHANNEL_IDLE_COUNT[15..0]
++#define BN0_WF_MIB_TOP_M0SDR9_CCA_NAV_TX_TIME_MASK             0x00FFFFFF                // CCA_NAV_TX_TIME[23..0]
++#define BN0_WF_MIB_TOP_RSCR26_RX_MDRDY_COUNT_MASK              0xFFFFFFFF                // RX_MDRDY_COUNT[31..0]
++#define BN0_WF_MIB_TOP_MSR0_CCK_MDRDY_TIME_MASK                0xFFFFFFFF                // CCK_MDRDY_TIME[31..0]
++#define BN0_WF_MIB_TOP_MSR1_OFDM_LG_MIXED_VHT_MDRDY_TIME_MASK  0xFFFFFFFF                // OFDM_LG_MIXED_VHT_MDRDY_TIME[31..0]
++#define BN0_WF_MIB_TOP_MSR2_OFDM_GREEN_MDRDY_TIME_MASK         0xFFFFFFFF                // OFDM_GREEN_MDRDY_TIME[31..0]
++#define BN0_WF_MIB_TOP_MCTR5_P_CCA_TIME_MASK                   0xFFFFFFFF                // P_CCA_TIME[31..0]
++#define BN0_WF_MIB_TOP_MCTR6_S_CCA_TIME_MASK                   0xFFFFFFFF                // S_CCA_TIME[31..0]
++#define BN0_WF_MIB_TOP_M0SDR18_P_ED_TIME_MASK                  0x00FFFFFF                // P_ED_TIME[23..0]
++#define BN0_WF_MIB_TOP_TSCR18_BEACONTXCOUNT_MASK               0xFFFFFFFF                // BEACONTXCOUNT[31..0]
++#define BN0_WF_MIB_TOP_TBCR0_TX_20MHZ_CNT_MASK                 0xFFFFFFFF                // TX_20MHZ_CNT[31..0]
++#define BN0_WF_MIB_TOP_TBCR1_TX_40MHZ_CNT_MASK                 0xFFFFFFFF                // TX_40MHZ_CNT[31..0]
++#define BN0_WF_MIB_TOP_TBCR2_TX_80MHZ_CNT_MASK                 0xFFFFFFFF                // TX_80MHZ_CNT[31..0]
++#define BN0_WF_MIB_TOP_TBCR3_TX_160MHZ_CNT_MASK                0xFFFFFFFF                // TX_160MHZ_CNT[31..0]
++#define BN0_WF_MIB_TOP_TBCR4_TX_320MHZ_CNT_MASK                0xFFFFFFFF                // TX_320MHZ_CNT[31..0]
++#define BN0_WF_MIB_TOP_BSCR2_MUBF_TX_COUNT_MASK                0xFFFFFFFF                // MUBF_TX_COUNT[31..0]
++#define BN0_WF_MIB_TOP_RVSR0_VEC_MISS_COUNT_MASK               0xFFFFFFFF                // VEC_MISS_COUNT[31..0]
++#define BN0_WF_MIB_TOP_RSCR35_DELIMITER_FAIL_COUNT_MASK        0xFFFFFFFF                // DELIMITER_FAIL_COUNT[31..0]
++#define BN0_WF_MIB_TOP_RSCR1_RX_FCS_ERROR_COUNT_MASK           0xFFFFFFFF                // RX_FCS_ERROR_COUNT[31..0]
++#define BN0_WF_MIB_TOP_RSCR33_RX_FIFO_FULL_COUNT_MASK          0xFFFFFFFF                // RX_FIFO_FULL_COUNT[31..0]
++#define BN0_WF_MIB_TOP_RSCR36_RX_LEN_MISMATCH_MASK             0xFFFFFFFF                // RX_LEN_MISMATCH[31..0]
++#define BN0_WF_MIB_TOP_RSCR31_RX_MPDU_COUNT_MASK               0xFFFFFFFF                // RX_MPDU_COUNT[31..0]
++#define BN0_WF_MIB_TOP_BTSCR5_RTSTXCOUNTn_MASK                 0xFFFFFFFF                // RTSTXCOUNTn[31..0]
++#define BN0_WF_MIB_TOP_BTSCR6_RTSRETRYCOUNTn_MASK              0xFFFFFFFF                // RTSRETRYCOUNTn[31..0]
++#define BN0_WF_MIB_TOP_BTSCR0_BAMISSCOUNTn_MASK                0xFFFFFFFF                // BAMISSCOUNTn[31..0]
++#define BN0_WF_MIB_TOP_BTSCR1_ACKFAILCOUNTn_MASK               0xFFFFFFFF                // ACKFAILCOUNTn[31..0]
++#define BN0_WF_MIB_TOP_BTSCR2_FRAMERETRYCOUNTn_MASK            0xFFFFFFFF                // FRAMERETRYCOUNTn[31..0]
++#define BN0_WF_MIB_TOP_BTSCR3_FRAMERETRY2COUNTn_MASK           0xFFFFFFFF                // FRAMERETRY2COUNTn[31..0]
++#define BN0_WF_MIB_TOP_BTSCR4_FRAMERETRY3COUNTn_MASK           0xFFFFFFFF                // FRAMERETRY3COUNTn[31..0]
++#define BN0_WF_MIB_TOP_TRARC0_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x0B0) // D0B0
++#define BN0_WF_MIB_TOP_TRARC1_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x0B4) // D0B4
++#define BN0_WF_MIB_TOP_TRARC2_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x0B8) // D0B8
++#define BN0_WF_MIB_TOP_TRARC3_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x0BC) // D0BC
++#define BN0_WF_MIB_TOP_TRARC4_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x0C0) // D0C0
++#define BN0_WF_MIB_TOP_TRARC5_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x0C4) // D0C4
++#define BN0_WF_MIB_TOP_TRARC6_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x0C8) // D0C8
++#define BN0_WF_MIB_TOP_TRARC7_ADDR                             (BN0_WF_MIB_TOP_BASE + 0x0CC) // D0CC
++
++#define BN0_WF_MIB_TOP_TRDR0_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR0))
++#define BN0_WF_MIB_TOP_TRDR1_ADDR                              (BN0_WF_MIB_TOP_BASE + __OFFS(MIB_TRDR1))
++#define BN0_WF_MIB_TOP_TRDR2_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR2))
++#define BN0_WF_MIB_TOP_TRDR3_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR3))
++#define BN0_WF_MIB_TOP_TRDR4_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR4))
++#define BN0_WF_MIB_TOP_TRDR5_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR5))
++#define BN0_WF_MIB_TOP_TRDR6_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR6))
++#define BN0_WF_MIB_TOP_TRDR7_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR7))
++#define BN0_WF_MIB_TOP_TRDR8_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR8))
++#define BN0_WF_MIB_TOP_TRDR9_ADDR                              (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR9))
++#define BN0_WF_MIB_TOP_TRDR10_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR10))
++#define BN0_WF_MIB_TOP_TRDR11_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR11))
++#define BN0_WF_MIB_TOP_TRDR12_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR12))
++#define BN0_WF_MIB_TOP_TRDR13_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR13))
++#define BN0_WF_MIB_TOP_TRDR14_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR14))
++#define BN0_WF_MIB_TOP_TRDR15_ADDR                             (BN0_WF_MIB_TOP_BASE + __DBG_OFFS(MIB_TRDR15))
++
++#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_ADDR              BN0_WF_MIB_TOP_TRARC0_ADDR
++#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_MASK              0x03FF0000                // AGG_RANG_SEL_1[25..16]
++#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_SHFT              16
++#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_ADDR              BN0_WF_MIB_TOP_TRARC0_ADDR
++#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_MASK              0x000003FF                // AGG_RANG_SEL_0[9..0]
++#define BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_SHFT              0
++
++#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_ADDR              BN0_WF_MIB_TOP_TRARC1_ADDR
++#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_MASK              0x03FF0000                // AGG_RANG_SEL_3[25..16]
++#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_SHFT              16
++#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_ADDR              BN0_WF_MIB_TOP_TRARC1_ADDR
++#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_MASK              0x000003FF                // AGG_RANG_SEL_2[9..0]
++#define BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_SHFT              0
++
++#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_ADDR              BN0_WF_MIB_TOP_TRARC2_ADDR
++#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_MASK              0x03FF0000                // AGG_RANG_SEL_5[25..16]
++#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_SHFT              16
++#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_ADDR              BN0_WF_MIB_TOP_TRARC2_ADDR
++#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_MASK              0x000003FF                // AGG_RANG_SEL_4[9..0]
++#define BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_SHFT              0
++
++#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_ADDR              BN0_WF_MIB_TOP_TRARC3_ADDR
++#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_MASK              0x03FF0000                // AGG_RANG_SEL_7[25..16]
++#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_SHFT              16
++#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_ADDR              BN0_WF_MIB_TOP_TRARC3_ADDR
++#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_MASK              0x000003FF                // AGG_RANG_SEL_6[9..0]
++#define BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_SHFT              0
++
++#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_ADDR              BN0_WF_MIB_TOP_TRARC4_ADDR
++#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_MASK              0x03FF0000                // AGG_RANG_SEL_9[25..16]
++#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_SHFT              16
++#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_ADDR              BN0_WF_MIB_TOP_TRARC4_ADDR
++#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_MASK              0x000003FF                // AGG_RANG_SEL_8[9..0]
++#define BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_SHFT              0
++
++#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_ADDR             BN0_WF_MIB_TOP_TRARC5_ADDR
++#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_MASK             0x03FF0000                // AGG_RANG_SEL_11[25..16]
++#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_SHFT             16
++#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_ADDR             BN0_WF_MIB_TOP_TRARC5_ADDR
++#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_MASK             0x000003FF                // AGG_RANG_SEL_10[9..0]
++#define BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_SHFT             0
++
++#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_ADDR             BN0_WF_MIB_TOP_TRARC6_ADDR
++#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_MASK             0x03FF0000                // AGG_RANG_SEL_13[25..16]
++#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_SHFT             16
++#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_ADDR             BN0_WF_MIB_TOP_TRARC6_ADDR
++#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_MASK             0x000003FF                // AGG_RANG_SEL_12[9..0]
++#define BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_SHFT             0
++
++#define BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_ADDR             BN0_WF_MIB_TOP_TRARC7_ADDR
++#define BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_MASK             0x000003FF                // AGG_RANG_SEL_14[9..0]
++#define BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_SHFT             0
++
++/* RRO TOP */
++#define WF_RRO_TOP_BASE                                        0xA000 /*0x820C2000 */
++#define WF_RRO_TOP_IND_CMD_0_CTRL0_ADDR                        (WF_RRO_TOP_BASE + 0x40) // 2040
++											//
++/* WTBL */
++enum mt7996_wtbl_type {
++	WTBL_TYPE_LMAC, 	/* WTBL in LMAC */
++	WTBL_TYPE_UMAC, 	/* WTBL in UMAC */
++	WTBL_TYPE_KEY,		/* Key Table */
++	MAX_NUM_WTBL_TYPE
++};
++
++struct berse_wtbl_parse {
++	u8 *name;
++	u32 mask;
++	u32 shift;
++	u8 new_line;
++};
++
++enum muar_idx {
++	MUAR_INDEX_OWN_MAC_ADDR_0 = 0,
++	MUAR_INDEX_OWN_MAC_ADDR_1,
++	MUAR_INDEX_OWN_MAC_ADDR_2,
++	MUAR_INDEX_OWN_MAC_ADDR_3,
++	MUAR_INDEX_OWN_MAC_ADDR_4,
++	MUAR_INDEX_OWN_MAC_ADDR_BC_MC = 0xE,
++	MUAR_INDEX_UNMATCHED = 0xF,
++	MUAR_INDEX_OWN_MAC_ADDR_11 = 0x11,
++	MUAR_INDEX_OWN_MAC_ADDR_12,
++	MUAR_INDEX_OWN_MAC_ADDR_13,
++	MUAR_INDEX_OWN_MAC_ADDR_14,
++	MUAR_INDEX_OWN_MAC_ADDR_15,
++	MUAR_INDEX_OWN_MAC_ADDR_16,
++	MUAR_INDEX_OWN_MAC_ADDR_17,
++	MUAR_INDEX_OWN_MAC_ADDR_18,
++	MUAR_INDEX_OWN_MAC_ADDR_19,
++	MUAR_INDEX_OWN_MAC_ADDR_1A,
++	MUAR_INDEX_OWN_MAC_ADDR_1B,
++	MUAR_INDEX_OWN_MAC_ADDR_1C,
++	MUAR_INDEX_OWN_MAC_ADDR_1D,
++	MUAR_INDEX_OWN_MAC_ADDR_1E,
++	MUAR_INDEX_OWN_MAC_ADDR_1F,
++	MUAR_INDEX_OWN_MAC_ADDR_20,
++	MUAR_INDEX_OWN_MAC_ADDR_21,
++	MUAR_INDEX_OWN_MAC_ADDR_22,
++	MUAR_INDEX_OWN_MAC_ADDR_23,
++	MUAR_INDEX_OWN_MAC_ADDR_24,
++	MUAR_INDEX_OWN_MAC_ADDR_25,
++	MUAR_INDEX_OWN_MAC_ADDR_26,
++	MUAR_INDEX_OWN_MAC_ADDR_27,
++	MUAR_INDEX_OWN_MAC_ADDR_28,
++	MUAR_INDEX_OWN_MAC_ADDR_29,
++	MUAR_INDEX_OWN_MAC_ADDR_2A,
++	MUAR_INDEX_OWN_MAC_ADDR_2B,
++	MUAR_INDEX_OWN_MAC_ADDR_2C,
++	MUAR_INDEX_OWN_MAC_ADDR_2D,
++	MUAR_INDEX_OWN_MAC_ADDR_2E,
++	MUAR_INDEX_OWN_MAC_ADDR_2F
++};
++
++enum cipher_suit {
++	IGTK_CIPHER_SUIT_NONE = 0,
++	IGTK_CIPHER_SUIT_BIP,
++	IGTK_CIPHER_SUIT_BIP_256
++};
++
++#define LWTBL_LEN_IN_DW			36
++#define UWTBL_LEN_IN_DW			16
++
++#define MT_DBG_WTBL_BASE		0x820D8000
++
++#define MT_DBG_WTBLON_TOP_BASE		0x820d4000
++#define MT_DBG_WTBLON_TOP_WDUCR_ADDR	(MT_DBG_WTBLON_TOP_BASE + 0x0370) // 4370
++#define MT_DBG_WTBLON_TOP_WDUCR_GROUP	GENMASK(4, 0)
++
++#define MT_DBG_UWTBL_TOP_BASE		0x820c4000
++#define MT_DBG_UWTBL_TOP_WDUCR_ADDR	(MT_DBG_UWTBL_TOP_BASE + 0x0104) // 4104
++#define MT_DBG_UWTBL_TOP_WDUCR_GROUP	GENMASK(5, 0)
++#define MT_DBG_UWTBL_TOP_WDUCR_TARGET	BIT(31)
++
++#define LWTBL_IDX2BASE_ID		GENMASK(14, 8)
++#define LWTBL_IDX2BASE_DW		GENMASK(7, 2)
++#define LWTBL_IDX2BASE(_id, _dw)	(MT_DBG_WTBL_BASE | \
++					FIELD_PREP(LWTBL_IDX2BASE_ID, _id) | \
++					FIELD_PREP(LWTBL_IDX2BASE_DW, _dw))
++
++#define UWTBL_IDX2BASE_ID		GENMASK(12, 6)
++#define UWTBL_IDX2BASE_DW		GENMASK(5, 2)
++#define UWTBL_IDX2BASE(_id, _dw)	(MT_DBG_UWTBL_TOP_BASE | 0x2000 | \
++					FIELD_PREP(UWTBL_IDX2BASE_ID, _id) | \
++					FIELD_PREP(UWTBL_IDX2BASE_DW, _dw))
++
++#define KEYTBL_IDX2BASE_KEY		GENMASK(12, 6)
++#define KEYTBL_IDX2BASE_DW		GENMASK(5, 2)
++#define KEYTBL_IDX2BASE(_key, _dw)	(MT_DBG_UWTBL_TOP_BASE | 0x2000 | \
++					FIELD_PREP(KEYTBL_IDX2BASE_KEY, _key) | \
++					FIELD_PREP(KEYTBL_IDX2BASE_DW, _dw))
++
++// UMAC WTBL
++// DW0
++#define WF_UWTBL_PEER_MLD_ADDRESS_47_32__DW                         0
++#define WF_UWTBL_PEER_MLD_ADDRESS_47_32__ADDR                       0
++#define WF_UWTBL_PEER_MLD_ADDRESS_47_32__MASK                       0x0000ffff // 15- 0
++#define WF_UWTBL_PEER_MLD_ADDRESS_47_32__SHIFT                      0
++#define WF_UWTBL_OWN_MLD_ID_DW                                      0
++#define WF_UWTBL_OWN_MLD_ID_ADDR                                    0
++#define WF_UWTBL_OWN_MLD_ID_MASK                                    0x003f0000 // 21-16
++#define WF_UWTBL_OWN_MLD_ID_SHIFT                                   16
++// DW1
++#define WF_UWTBL_PEER_MLD_ADDRESS_31_0__DW                          1
++#define WF_UWTBL_PEER_MLD_ADDRESS_31_0__ADDR                        4
++#define WF_UWTBL_PEER_MLD_ADDRESS_31_0__MASK                        0xffffffff // 31- 0
++#define WF_UWTBL_PEER_MLD_ADDRESS_31_0__SHIFT                       0
++// DW2
++#define WF_UWTBL_PN_31_0__DW                                        2
++#define WF_UWTBL_PN_31_0__ADDR                                      8
++#define WF_UWTBL_PN_31_0__MASK                                      0xffffffff // 31- 0
++#define WF_UWTBL_PN_31_0__SHIFT                                     0
++// DW3
++#define WF_UWTBL_PN_47_32__DW                                       3
++#define WF_UWTBL_PN_47_32__ADDR                                     12
++#define WF_UWTBL_PN_47_32__MASK                                     0x0000ffff // 15- 0
++#define WF_UWTBL_PN_47_32__SHIFT                                    0
++#define WF_UWTBL_COM_SN_DW                                          3
++#define WF_UWTBL_COM_SN_ADDR                                        12
++#define WF_UWTBL_COM_SN_MASK                                        0x0fff0000 // 27-16
++#define WF_UWTBL_COM_SN_SHIFT                                       16
++// DW4
++#define WF_UWTBL_TID0_SN_DW                                         4
++#define WF_UWTBL_TID0_SN_ADDR                                       16
++#define WF_UWTBL_TID0_SN_MASK                                       0x00000fff // 11- 0
++#define WF_UWTBL_TID0_SN_SHIFT                                      0
++#define WF_UWTBL_RX_BIPN_31_0__DW                                   4
++#define WF_UWTBL_RX_BIPN_31_0__ADDR                                 16
++#define WF_UWTBL_RX_BIPN_31_0__MASK                                 0xffffffff // 31- 0
++#define WF_UWTBL_RX_BIPN_31_0__SHIFT                                0
++#define WF_UWTBL_TID1_SN_DW                                         4
++#define WF_UWTBL_TID1_SN_ADDR                                       16
++#define WF_UWTBL_TID1_SN_MASK                                       0x00fff000 // 23-12
++#define WF_UWTBL_TID1_SN_SHIFT                                      12
++#define WF_UWTBL_TID2_SN_7_0__DW                                    4
++#define WF_UWTBL_TID2_SN_7_0__ADDR                                  16
++#define WF_UWTBL_TID2_SN_7_0__MASK                                  0xff000000 // 31-24
++#define WF_UWTBL_TID2_SN_7_0__SHIFT                                 24
++// DW5
++#define WF_UWTBL_TID2_SN_11_8__DW                                   5
++#define WF_UWTBL_TID2_SN_11_8__ADDR                                 20
++#define WF_UWTBL_TID2_SN_11_8__MASK                                 0x0000000f //  3- 0
++#define WF_UWTBL_TID2_SN_11_8__SHIFT                                0
++#define WF_UWTBL_RX_BIPN_47_32__DW                                  5
++#define WF_UWTBL_RX_BIPN_47_32__ADDR                                20
++#define WF_UWTBL_RX_BIPN_47_32__MASK                                0x0000ffff // 15- 0
++#define WF_UWTBL_RX_BIPN_47_32__SHIFT                               0
++#define WF_UWTBL_TID3_SN_DW                                         5
++#define WF_UWTBL_TID3_SN_ADDR                                       20
++#define WF_UWTBL_TID3_SN_MASK                                       0x0000fff0 // 15- 4
++#define WF_UWTBL_TID3_SN_SHIFT                                      4
++#define WF_UWTBL_TID4_SN_DW                                         5
++#define WF_UWTBL_TID4_SN_ADDR                                       20
++#define WF_UWTBL_TID4_SN_MASK                                       0x0fff0000 // 27-16
++#define WF_UWTBL_TID4_SN_SHIFT                                      16
++#define WF_UWTBL_TID5_SN_3_0__DW                                    5
++#define WF_UWTBL_TID5_SN_3_0__ADDR                                  20
++#define WF_UWTBL_TID5_SN_3_0__MASK                                  0xf0000000 // 31-28
++#define WF_UWTBL_TID5_SN_3_0__SHIFT                                 28
++// DW6
++#define WF_UWTBL_TID5_SN_11_4__DW                                   6
++#define WF_UWTBL_TID5_SN_11_4__ADDR                                 24
++#define WF_UWTBL_TID5_SN_11_4__MASK                                 0x000000ff //  7- 0
++#define WF_UWTBL_TID5_SN_11_4__SHIFT                                0
++#define WF_UWTBL_KEY_LOC2_DW                                        6
++#define WF_UWTBL_KEY_LOC2_ADDR                                      24
++#define WF_UWTBL_KEY_LOC2_MASK                                      0x00001fff // 12- 0
++#define WF_UWTBL_KEY_LOC2_SHIFT                                     0
++#define WF_UWTBL_TID6_SN_DW                                         6
++#define WF_UWTBL_TID6_SN_ADDR                                       24
++#define WF_UWTBL_TID6_SN_MASK                                       0x000fff00 // 19- 8
++#define WF_UWTBL_TID6_SN_SHIFT                                      8
++#define WF_UWTBL_TID7_SN_DW                                         6
++#define WF_UWTBL_TID7_SN_ADDR                                       24
++#define WF_UWTBL_TID7_SN_MASK                                       0xfff00000 // 31-20
++#define WF_UWTBL_TID7_SN_SHIFT                                      20
++// DW7
++#define WF_UWTBL_KEY_LOC0_DW                                        7
++#define WF_UWTBL_KEY_LOC0_ADDR                                      28
++#define WF_UWTBL_KEY_LOC0_MASK                                      0x00001fff // 12- 0
++#define WF_UWTBL_KEY_LOC0_SHIFT                                     0
++#define WF_UWTBL_KEY_LOC1_DW                                        7
++#define WF_UWTBL_KEY_LOC1_ADDR                                      28
++#define WF_UWTBL_KEY_LOC1_MASK                                      0x1fff0000 // 28-16
++#define WF_UWTBL_KEY_LOC1_SHIFT                                     16
++// DW8
++#define WF_UWTBL_AMSDU_CFG_DW                                       8
++#define WF_UWTBL_AMSDU_CFG_ADDR                                     32
++#define WF_UWTBL_AMSDU_CFG_MASK                                     0x00000fff // 11- 0
++#define WF_UWTBL_AMSDU_CFG_SHIFT                                    0
++#define WF_UWTBL_SEC_ADDR_MODE_DW                                   8
++#define WF_UWTBL_SEC_ADDR_MODE_ADDR                                 32
++#define WF_UWTBL_SEC_ADDR_MODE_MASK                                 0x00300000 // 21-20
++#define WF_UWTBL_SEC_ADDR_MODE_SHIFT                                20
++#define WF_UWTBL_WMM_Q_DW                                           8
++#define WF_UWTBL_WMM_Q_ADDR                                         32
++#define WF_UWTBL_WMM_Q_MASK                                         0x06000000 // 26-25
++#define WF_UWTBL_WMM_Q_SHIFT                                        25
++#define WF_UWTBL_QOS_DW                                             8
++#define WF_UWTBL_QOS_ADDR                                           32
++#define WF_UWTBL_QOS_MASK                                           0x08000000 // 27-27
++#define WF_UWTBL_QOS_SHIFT                                          27
++#define WF_UWTBL_HT_DW                                              8
++#define WF_UWTBL_HT_ADDR                                            32
++#define WF_UWTBL_HT_MASK                                            0x10000000 // 28-28
++#define WF_UWTBL_HT_SHIFT                                           28
++#define WF_UWTBL_HDRT_MODE_DW                                       8
++#define WF_UWTBL_HDRT_MODE_ADDR                                     32
++#define WF_UWTBL_HDRT_MODE_MASK                                     0x20000000 // 29-29
++#define WF_UWTBL_HDRT_MODE_SHIFT                                    29
++// DW9
++#define WF_UWTBL_RELATED_IDX0_DW                                    9
++#define WF_UWTBL_RELATED_IDX0_ADDR                                  36
++#define WF_UWTBL_RELATED_IDX0_MASK                                  0x00000fff // 11- 0
++#define WF_UWTBL_RELATED_IDX0_SHIFT                                 0
++#define WF_UWTBL_RELATED_BAND0_DW                                   9
++#define WF_UWTBL_RELATED_BAND0_ADDR                                 36
++#define WF_UWTBL_RELATED_BAND0_MASK                                 0x00003000 // 13-12
++#define WF_UWTBL_RELATED_BAND0_SHIFT                                12
++#define WF_UWTBL_PRIMARY_MLD_BAND_DW                                9
++#define WF_UWTBL_PRIMARY_MLD_BAND_ADDR                              36
++#define WF_UWTBL_PRIMARY_MLD_BAND_MASK                              0x0000c000 // 15-14
++#define WF_UWTBL_PRIMARY_MLD_BAND_SHIFT                             14
++#define WF_UWTBL_RELATED_IDX1_DW                                    9
++#define WF_UWTBL_RELATED_IDX1_ADDR                                  36
++#define WF_UWTBL_RELATED_IDX1_MASK                                  0x0fff0000 // 27-16
++#define WF_UWTBL_RELATED_IDX1_SHIFT                                 16
++#define WF_UWTBL_RELATED_BAND1_DW                                   9
++#define WF_UWTBL_RELATED_BAND1_ADDR                                 36
++#define WF_UWTBL_RELATED_BAND1_MASK                                 0x30000000 // 29-28
++#define WF_UWTBL_RELATED_BAND1_SHIFT                                28
++#define WF_UWTBL_SECONDARY_MLD_BAND_DW                              9
++#define WF_UWTBL_SECONDARY_MLD_BAND_ADDR                            36
++#define WF_UWTBL_SECONDARY_MLD_BAND_MASK                            0xc0000000 // 31-30
++#define WF_UWTBL_SECONDARY_MLD_BAND_SHIFT                           30
++
++/* LMAC WTBL */
++// DW0
++#define WF_LWTBL_PEER_LINK_ADDRESS_47_32__DW                        0
++#define WF_LWTBL_PEER_LINK_ADDRESS_47_32__ADDR                      0
++#define WF_LWTBL_PEER_LINK_ADDRESS_47_32__MASK \
++	0x0000ffff // 15- 0
++#define WF_LWTBL_PEER_LINK_ADDRESS_47_32__SHIFT                     0
++#define WF_LWTBL_MUAR_DW                                            0
++#define WF_LWTBL_MUAR_ADDR                                          0
++#define WF_LWTBL_MUAR_MASK \
++	0x003f0000 // 21-16
++#define WF_LWTBL_MUAR_SHIFT                                         16
++#define WF_LWTBL_RCA1_DW                                            0
++#define WF_LWTBL_RCA1_ADDR                                          0
++#define WF_LWTBL_RCA1_MASK \
++	0x00400000 // 22-22
++#define WF_LWTBL_RCA1_SHIFT                                         22
++#define WF_LWTBL_KID_DW                                             0
++#define WF_LWTBL_KID_ADDR                                           0
++#define WF_LWTBL_KID_MASK \
++	0x01800000 // 24-23
++#define WF_LWTBL_KID_SHIFT                                          23
++#define WF_LWTBL_RCID_DW                                            0
++#define WF_LWTBL_RCID_ADDR                                          0
++#define WF_LWTBL_RCID_MASK \
++	0x02000000 // 25-25
++#define WF_LWTBL_RCID_SHIFT                                         25
++#define WF_LWTBL_BAND_DW                                            0
++#define WF_LWTBL_BAND_ADDR                                          0
++#define WF_LWTBL_BAND_MASK \
++	0x0c000000 // 27-26
++#define WF_LWTBL_BAND_SHIFT                                         26
++#define WF_LWTBL_RV_DW                                              0
++#define WF_LWTBL_RV_ADDR                                            0
++#define WF_LWTBL_RV_MASK \
++	0x10000000 // 28-28
++#define WF_LWTBL_RV_SHIFT                                           28
++#define WF_LWTBL_RCA2_DW                                            0
++#define WF_LWTBL_RCA2_ADDR                                          0
++#define WF_LWTBL_RCA2_MASK \
++	0x20000000 // 29-29
++#define WF_LWTBL_RCA2_SHIFT                                         29
++#define WF_LWTBL_WPI_FLAG_DW                                        0
++#define WF_LWTBL_WPI_FLAG_ADDR                                      0
++#define WF_LWTBL_WPI_FLAG_MASK \
++	0x40000000 // 30-30
++#define WF_LWTBL_WPI_FLAG_SHIFT                                     30
++// DW1
++#define WF_LWTBL_PEER_LINK_ADDRESS_31_0__DW                         1
++#define WF_LWTBL_PEER_LINK_ADDRESS_31_0__ADDR                       4
++#define WF_LWTBL_PEER_LINK_ADDRESS_31_0__MASK \
++	0xffffffff // 31- 0
++#define WF_LWTBL_PEER_LINK_ADDRESS_31_0__SHIFT                      0
++// DW2
++#define WF_LWTBL_AID_DW                                             2
++#define WF_LWTBL_AID_ADDR                                           8
++#define WF_LWTBL_AID_MASK \
++	0x00000fff // 11- 0
++#define WF_LWTBL_AID_SHIFT                                          0
++#define WF_LWTBL_GID_SU_DW                                          2
++#define WF_LWTBL_GID_SU_ADDR                                        8
++#define WF_LWTBL_GID_SU_MASK \
++	0x00001000 // 12-12
++#define WF_LWTBL_GID_SU_SHIFT                                       12
++#define WF_LWTBL_SPP_EN_DW                                          2
++#define WF_LWTBL_SPP_EN_ADDR                                        8
++#define WF_LWTBL_SPP_EN_MASK \
++	0x00002000 // 13-13
++#define WF_LWTBL_SPP_EN_SHIFT                                       13
++#define WF_LWTBL_WPI_EVEN_DW                                        2
++#define WF_LWTBL_WPI_EVEN_ADDR                                      8
++#define WF_LWTBL_WPI_EVEN_MASK \
++	0x00004000 // 14-14
++#define WF_LWTBL_WPI_EVEN_SHIFT                                     14
++#define WF_LWTBL_AAD_OM_DW                                          2
++#define WF_LWTBL_AAD_OM_ADDR                                        8
++#define WF_LWTBL_AAD_OM_MASK \
++	0x00008000 // 15-15
++#define WF_LWTBL_AAD_OM_SHIFT                                       15
++/* kite DW2 field bit 13-14 */
++#define WF_LWTBL_DUAL_PTEC_EN_DW                                    2
++#define WF_LWTBL_DUAL_PTEC_EN_ADDR                                  8
++#define WF_LWTBL_DUAL_PTEC_EN_MASK \
++	0x00002000 // 13-13
++#define WF_LWTBL_DUAL_PTEC_EN_SHIFT                                 13
++#define WF_LWTBL_DUAL_CTS_CAP_DW                                    2
++#define WF_LWTBL_DUAL_CTS_CAP_ADDR                                  8
++#define WF_LWTBL_DUAL_CTS_CAP_MASK \
++	0x00004000 // 14-14
++#define WF_LWTBL_DUAL_CTS_CAP_SHIFT                                 14
++#define WF_LWTBL_CIPHER_SUIT_PGTK_DW                                2
++#define WF_LWTBL_CIPHER_SUIT_PGTK_ADDR                              8
++#define WF_LWTBL_CIPHER_SUIT_PGTK_MASK \
++	0x001f0000 // 20-16
++#define WF_LWTBL_CIPHER_SUIT_PGTK_SHIFT                             16
++#define WF_LWTBL_FD_DW                                              2
++#define WF_LWTBL_FD_ADDR                                            8
++#define WF_LWTBL_FD_MASK \
++	0x00200000 // 21-21
++#define WF_LWTBL_FD_SHIFT                                           21
++#define WF_LWTBL_TD_DW                                              2
++#define WF_LWTBL_TD_ADDR                                            8
++#define WF_LWTBL_TD_MASK \
++	0x00400000 // 22-22
++#define WF_LWTBL_TD_SHIFT                                           22
++#define WF_LWTBL_SW_DW                                              2
++#define WF_LWTBL_SW_ADDR                                            8
++#define WF_LWTBL_SW_MASK \
++	0x00800000 // 23-23
++#define WF_LWTBL_SW_SHIFT                                           23
++#define WF_LWTBL_UL_DW                                              2
++#define WF_LWTBL_UL_ADDR                                            8
++#define WF_LWTBL_UL_MASK \
++	0x01000000 // 24-24
++#define WF_LWTBL_UL_SHIFT                                           24
++#define WF_LWTBL_TX_PS_DW                                           2
++#define WF_LWTBL_TX_PS_ADDR                                         8
++#define WF_LWTBL_TX_PS_MASK \
++	0x02000000 // 25-25
++#define WF_LWTBL_TX_PS_SHIFT                                        25
++#define WF_LWTBL_QOS_DW                                             2
++#define WF_LWTBL_QOS_ADDR                                           8
++#define WF_LWTBL_QOS_MASK \
++	0x04000000 // 26-26
++#define WF_LWTBL_QOS_SHIFT                                          26
++#define WF_LWTBL_HT_DW                                              2
++#define WF_LWTBL_HT_ADDR                                            8
++#define WF_LWTBL_HT_MASK \
++	0x08000000 // 27-27
++#define WF_LWTBL_HT_SHIFT                                           27
++#define WF_LWTBL_VHT_DW                                             2
++#define WF_LWTBL_VHT_ADDR                                           8
++#define WF_LWTBL_VHT_MASK \
++	0x10000000 // 28-28
++#define WF_LWTBL_VHT_SHIFT                                          28
++#define WF_LWTBL_HE_DW                                              2
++#define WF_LWTBL_HE_ADDR                                            8
++#define WF_LWTBL_HE_MASK \
++	0x20000000 // 29-29
++#define WF_LWTBL_HE_SHIFT                                           29
++#define WF_LWTBL_EHT_DW                                             2
++#define WF_LWTBL_EHT_ADDR                                           8
++#define WF_LWTBL_EHT_MASK \
++	0x40000000 // 30-30
++#define WF_LWTBL_EHT_SHIFT                                          30
++#define WF_LWTBL_MESH_DW                                            2
++#define WF_LWTBL_MESH_ADDR                                          8
++#define WF_LWTBL_MESH_MASK \
++	0x80000000 // 31-31
++#define WF_LWTBL_MESH_SHIFT                                         31
++// DW3
++#define WF_LWTBL_WMM_Q_DW                                           3
++#define WF_LWTBL_WMM_Q_ADDR                                         12
++#define WF_LWTBL_WMM_Q_MASK \
++	0x00000003 // 1- 0
++#define WF_LWTBL_WMM_Q_SHIFT                                        0
++#define WF_LWTBL_EHT_SIG_MCS_DW                                     3
++#define WF_LWTBL_EHT_SIG_MCS_ADDR                                   12
++#define WF_LWTBL_EHT_SIG_MCS_MASK \
++	0x0000000c // 3- 2
++#define WF_LWTBL_EHT_SIG_MCS_SHIFT                                  2
++#define WF_LWTBL_HDRT_MODE_DW                                       3
++#define WF_LWTBL_HDRT_MODE_ADDR                                     12
++#define WF_LWTBL_HDRT_MODE_MASK \
++	0x00000010 // 4- 4
++#define WF_LWTBL_HDRT_MODE_SHIFT                                    4
++#define WF_LWTBL_BEAM_CHG_DW                                        3
++#define WF_LWTBL_BEAM_CHG_ADDR                                      12
++#define WF_LWTBL_BEAM_CHG_MASK \
++	0x00000020 // 5- 5
++#define WF_LWTBL_BEAM_CHG_SHIFT                                     5
++#define WF_LWTBL_EHT_LTF_SYM_NUM_OPT_DW                             3
++#define WF_LWTBL_EHT_LTF_SYM_NUM_OPT_ADDR                           12
++#define WF_LWTBL_EHT_LTF_SYM_NUM_OPT_MASK \
++	0x000000c0 // 7- 6
++#define WF_LWTBL_EHT_LTF_SYM_NUM_OPT_SHIFT                          6
++#define WF_LWTBL_PFMU_IDX_DW                                        3
++#define WF_LWTBL_PFMU_IDX_ADDR                                      12
++#define WF_LWTBL_PFMU_IDX_MASK \
++	0x0000ff00 // 15- 8
++#define WF_LWTBL_PFMU_IDX_SHIFT                                     8
++#define WF_LWTBL_ULPF_IDX_DW                                        3
++#define WF_LWTBL_ULPF_IDX_ADDR                                      12
++#define WF_LWTBL_ULPF_IDX_MASK \
++	0x00ff0000 // 23-16
++#define WF_LWTBL_ULPF_IDX_SHIFT                                     16
++#define WF_LWTBL_RIBF_DW                                            3
++#define WF_LWTBL_RIBF_ADDR                                          12
++#define WF_LWTBL_RIBF_MASK \
++	0x01000000 // 24-24
++#define WF_LWTBL_RIBF_SHIFT                                         24
++#define WF_LWTBL_ULPF_DW                                            3
++#define WF_LWTBL_ULPF_ADDR                                          12
++#define WF_LWTBL_ULPF_MASK \
++	0x02000000 // 25-25
++#define WF_LWTBL_ULPF_SHIFT                                         25
++#define WF_LWTBL_BYPASS_TXSMM_DW                                    3
++#define WF_LWTBL_BYPASS_TXSMM_ADDR                                  12
++#define WF_LWTBL_BYPASS_TXSMM_MASK \
++	0x04000000 // 26-26
++#define WF_LWTBL_BYPASS_TXSMM_SHIFT                                 26
++#define WF_LWTBL_TBF_HT_DW                                          3
++#define WF_LWTBL_TBF_HT_ADDR                                        12
++#define WF_LWTBL_TBF_HT_MASK \
++	0x08000000 // 27-27
++#define WF_LWTBL_TBF_HT_SHIFT                                       27
++#define WF_LWTBL_TBF_VHT_DW                                         3
++#define WF_LWTBL_TBF_VHT_ADDR                                       12
++#define WF_LWTBL_TBF_VHT_MASK \
++	0x10000000 // 28-28
++#define WF_LWTBL_TBF_VHT_SHIFT                                      28
++#define WF_LWTBL_TBF_HE_DW                                          3
++#define WF_LWTBL_TBF_HE_ADDR                                        12
++#define WF_LWTBL_TBF_HE_MASK \
++	0x20000000 // 29-29
++#define WF_LWTBL_TBF_HE_SHIFT                                       29
++#define WF_LWTBL_TBF_EHT_DW                                         3
++#define WF_LWTBL_TBF_EHT_ADDR                                       12
++#define WF_LWTBL_TBF_EHT_MASK \
++	0x40000000 // 30-30
++#define WF_LWTBL_TBF_EHT_SHIFT                                      30
++#define WF_LWTBL_IGN_FBK_DW                                         3
++#define WF_LWTBL_IGN_FBK_ADDR                                       12
++#define WF_LWTBL_IGN_FBK_MASK \
++	0x80000000 // 31-31
++#define WF_LWTBL_IGN_FBK_SHIFT                                      31
++// DW4
++#define WF_LWTBL_NEGOTIATED_WINSIZE0_DW                             4
++#define WF_LWTBL_NEGOTIATED_WINSIZE0_ADDR                           16
++#define WF_LWTBL_NEGOTIATED_WINSIZE0_MASK \
++	0x00000007 // 2- 0
++#define WF_LWTBL_NEGOTIATED_WINSIZE0_SHIFT                          0
++#define WF_LWTBL_NEGOTIATED_WINSIZE1_DW                             4
++#define WF_LWTBL_NEGOTIATED_WINSIZE1_ADDR                           16
++#define WF_LWTBL_NEGOTIATED_WINSIZE1_MASK \
++	0x00000038 // 5- 3
++#define WF_LWTBL_NEGOTIATED_WINSIZE1_SHIFT                          3
++#define WF_LWTBL_NEGOTIATED_WINSIZE2_DW                             4
++#define WF_LWTBL_NEGOTIATED_WINSIZE2_ADDR                           16
++#define WF_LWTBL_NEGOTIATED_WINSIZE2_MASK \
++	0x000001c0 // 8- 6
++#define WF_LWTBL_NEGOTIATED_WINSIZE2_SHIFT                          6
++#define WF_LWTBL_NEGOTIATED_WINSIZE3_DW                             4
++#define WF_LWTBL_NEGOTIATED_WINSIZE3_ADDR                           16
++#define WF_LWTBL_NEGOTIATED_WINSIZE3_MASK \
++	0x00000e00 // 11- 9
++#define WF_LWTBL_NEGOTIATED_WINSIZE3_SHIFT                          9
++#define WF_LWTBL_NEGOTIATED_WINSIZE4_DW                             4
++#define WF_LWTBL_NEGOTIATED_WINSIZE4_ADDR                           16
++#define WF_LWTBL_NEGOTIATED_WINSIZE4_MASK \
++	0x00007000 // 14-12
++#define WF_LWTBL_NEGOTIATED_WINSIZE4_SHIFT                          12
++#define WF_LWTBL_NEGOTIATED_WINSIZE5_DW                             4
++#define WF_LWTBL_NEGOTIATED_WINSIZE5_ADDR                           16
++#define WF_LWTBL_NEGOTIATED_WINSIZE5_MASK \
++	0x00038000 // 17-15
++#define WF_LWTBL_NEGOTIATED_WINSIZE5_SHIFT                          15
++#define WF_LWTBL_NEGOTIATED_WINSIZE6_DW                             4
++#define WF_LWTBL_NEGOTIATED_WINSIZE6_ADDR                           16
++#define WF_LWTBL_NEGOTIATED_WINSIZE6_MASK \
++	0x001c0000 // 20-18
++#define WF_LWTBL_NEGOTIATED_WINSIZE6_SHIFT                          18
++#define WF_LWTBL_NEGOTIATED_WINSIZE7_DW                             4
++#define WF_LWTBL_NEGOTIATED_WINSIZE7_ADDR                           16
++#define WF_LWTBL_NEGOTIATED_WINSIZE7_MASK \
++	0x00e00000 // 23-21
++#define WF_LWTBL_NEGOTIATED_WINSIZE7_SHIFT                          21
++#define WF_LWTBL_PE_DW                                              4
++#define WF_LWTBL_PE_ADDR                                            16
++#define WF_LWTBL_PE_MASK \
++	0x03000000 // 25-24
++#define WF_LWTBL_PE_SHIFT                                           24
++#define WF_LWTBL_DIS_RHTR_DW                                        4
++#define WF_LWTBL_DIS_RHTR_ADDR                                      16
++#define WF_LWTBL_DIS_RHTR_MASK \
++	0x04000000 // 26-26
++#define WF_LWTBL_DIS_RHTR_SHIFT                                     26
++#define WF_LWTBL_LDPC_HT_DW                                         4
++#define WF_LWTBL_LDPC_HT_ADDR                                       16
++#define WF_LWTBL_LDPC_HT_MASK \
++	0x08000000 // 27-27
++#define WF_LWTBL_LDPC_HT_SHIFT                                      27
++#define WF_LWTBL_LDPC_VHT_DW                                        4
++#define WF_LWTBL_LDPC_VHT_ADDR                                      16
++#define WF_LWTBL_LDPC_VHT_MASK \
++	0x10000000 // 28-28
++#define WF_LWTBL_LDPC_VHT_SHIFT                                     28
++#define WF_LWTBL_LDPC_HE_DW                                         4
++#define WF_LWTBL_LDPC_HE_ADDR                                       16
++#define WF_LWTBL_LDPC_HE_MASK \
++	0x20000000 // 29-29
++#define WF_LWTBL_LDPC_HE_SHIFT                                      29
++#define WF_LWTBL_LDPC_EHT_DW                                        4
++#define WF_LWTBL_LDPC_EHT_ADDR                                      16
++#define WF_LWTBL_LDPC_EHT_MASK \
++	0x40000000 // 30-30
++#define WF_LWTBL_LDPC_EHT_SHIFT                                     30
++#define WF_LWTBL_BA_MODE_DW                                         4
++#define WF_LWTBL_BA_MODE_ADDR                                       16
++#define WF_LWTBL_BA_MODE_MASK \
++	0x80000000 // 31-31
++#define WF_LWTBL_BA_MODE_SHIFT                                      31
++// DW5
++#define WF_LWTBL_AF_DW                                              5
++#define WF_LWTBL_AF_ADDR                                            20
++#define WF_LWTBL_AF_MASK \
++	0x00000007 // 2- 0
++#define WF_LWTBL_AF_MASK_7992 \
++	0x0000000f // 3- 0
++#define WF_LWTBL_AF_SHIFT                                           0
++#define WF_LWTBL_AF_HE_DW                                           5
++#define WF_LWTBL_AF_HE_ADDR                                         20
++#define WF_LWTBL_AF_HE_MASK \
++	0x00000018 // 4- 3
++#define WF_LWTBL_AF_HE_SHIFT                                        3
++#define WF_LWTBL_RTS_DW                                             5
++#define WF_LWTBL_RTS_ADDR                                           20
++#define WF_LWTBL_RTS_MASK \
++	0x00000020 // 5- 5
++#define WF_LWTBL_RTS_SHIFT                                          5
++#define WF_LWTBL_SMPS_DW                                            5
++#define WF_LWTBL_SMPS_ADDR                                          20
++#define WF_LWTBL_SMPS_MASK \
++	0x00000040 // 6- 6
++#define WF_LWTBL_SMPS_SHIFT                                         6
++#define WF_LWTBL_DYN_BW_DW                                          5
++#define WF_LWTBL_DYN_BW_ADDR                                        20
++#define WF_LWTBL_DYN_BW_MASK \
++	0x00000080 // 7- 7
++#define WF_LWTBL_DYN_BW_SHIFT                                       7
++#define WF_LWTBL_MMSS_DW                                            5
++#define WF_LWTBL_MMSS_ADDR                                          20
++#define WF_LWTBL_MMSS_MASK \
++	0x00000700 // 10- 8
++#define WF_LWTBL_MMSS_SHIFT                                         8
++#define WF_LWTBL_USR_DW                                             5
++#define WF_LWTBL_USR_ADDR                                           20
++#define WF_LWTBL_USR_MASK \
++	0x00000800 // 11-11
++#define WF_LWTBL_USR_SHIFT                                          11
++#define WF_LWTBL_SR_R_DW                                            5
++#define WF_LWTBL_SR_R_ADDR                                          20
++#define WF_LWTBL_SR_R_MASK \
++	0x00007000 // 14-12
++#define WF_LWTBL_SR_R_SHIFT                                         12
++#define WF_LWTBL_SR_ABORT_DW                                        5
++#define WF_LWTBL_SR_ABORT_ADDR                                      20
++#define WF_LWTBL_SR_ABORT_MASK \
++	0x00008000 // 15-15
++#define WF_LWTBL_SR_ABORT_SHIFT                                     15
++#define WF_LWTBL_TX_POWER_OFFSET_DW                                 5
++#define WF_LWTBL_TX_POWER_OFFSET_ADDR                               20
++#define WF_LWTBL_TX_POWER_OFFSET_MASK \
++	0x003f0000 // 21-16
++#define WF_LWTBL_TX_POWER_OFFSET_SHIFT                              16
++#define WF_LWTBL_LTF_EHT_DW                                         5
++#define WF_LWTBL_LTF_EHT_ADDR                                       20
++#define WF_LWTBL_LTF_EHT_MASK \
++	0x00c00000 // 23-22
++#define WF_LWTBL_LTF_EHT_SHIFT                                      22
++#define WF_LWTBL_GI_EHT_DW                                          5
++#define WF_LWTBL_GI_EHT_ADDR                                        20
++#define WF_LWTBL_GI_EHT_MASK \
++	0x03000000 // 25-24
++#define WF_LWTBL_GI_EHT_SHIFT                                       24
++#define WF_LWTBL_DOPPL_DW                                           5
++#define WF_LWTBL_DOPPL_ADDR                                         20
++#define WF_LWTBL_DOPPL_MASK \
++	0x04000000 // 26-26
++#define WF_LWTBL_DOPPL_SHIFT                                        26
++#define WF_LWTBL_TXOP_PS_CAP_DW                                     5
++#define WF_LWTBL_TXOP_PS_CAP_ADDR                                   20
++#define WF_LWTBL_TXOP_PS_CAP_MASK \
++	0x08000000 // 27-27
++#define WF_LWTBL_TXOP_PS_CAP_SHIFT                                  27
++#define WF_LWTBL_DU_I_PSM_DW                                        5
++#define WF_LWTBL_DU_I_PSM_ADDR                                      20
++#define WF_LWTBL_DU_I_PSM_MASK \
++	0x10000000 // 28-28
++#define WF_LWTBL_DU_I_PSM_SHIFT                                     28
++#define WF_LWTBL_I_PSM_DW                                           5
++#define WF_LWTBL_I_PSM_ADDR                                         20
++#define WF_LWTBL_I_PSM_MASK \
++	0x20000000 // 29-29
++#define WF_LWTBL_I_PSM_SHIFT                                        29
++#define WF_LWTBL_PSM_DW                                             5
++#define WF_LWTBL_PSM_ADDR                                           20
++#define WF_LWTBL_PSM_MASK \
++	0x40000000 // 30-30
++#define WF_LWTBL_PSM_SHIFT                                          30
++#define WF_LWTBL_SKIP_TX_DW                                         5
++#define WF_LWTBL_SKIP_TX_ADDR                                       20
++#define WF_LWTBL_SKIP_TX_MASK \
++	0x80000000 // 31-31
++#define WF_LWTBL_SKIP_TX_SHIFT                                      31
++// DW6
++#define WF_LWTBL_CBRN_DW                                            6
++#define WF_LWTBL_CBRN_ADDR                                          24
++#define WF_LWTBL_CBRN_MASK \
++	0x00000007 // 2- 0
++#define WF_LWTBL_CBRN_SHIFT                                         0
++#define WF_LWTBL_DBNSS_EN_DW                                        6
++#define WF_LWTBL_DBNSS_EN_ADDR                                      24
++#define WF_LWTBL_DBNSS_EN_MASK \
++	0x00000008 // 3- 3
++#define WF_LWTBL_DBNSS_EN_SHIFT                                     3
++#define WF_LWTBL_BAF_EN_DW                                          6
++#define WF_LWTBL_BAF_EN_ADDR                                        24
++#define WF_LWTBL_BAF_EN_MASK \
++	0x00000010 // 4- 4
++#define WF_LWTBL_BAF_EN_SHIFT                                       4
++#define WF_LWTBL_RDGBA_DW                                           6
++#define WF_LWTBL_RDGBA_ADDR                                         24
++#define WF_LWTBL_RDGBA_MASK \
++	0x00000020 // 5- 5
++#define WF_LWTBL_RDGBA_SHIFT                                        5
++#define WF_LWTBL_R_DW                                               6
++#define WF_LWTBL_R_ADDR                                             24
++#define WF_LWTBL_R_MASK \
++	0x00000040 // 6- 6
++#define WF_LWTBL_R_SHIFT                                            6
++#define WF_LWTBL_SPE_IDX_DW                                         6
++#define WF_LWTBL_SPE_IDX_ADDR                                       24
++#define WF_LWTBL_SPE_IDX_MASK \
++	0x00000f80 // 11- 7
++#define WF_LWTBL_SPE_IDX_SHIFT                                      7
++#define WF_LWTBL_G2_DW                                              6
++#define WF_LWTBL_G2_ADDR                                            24
++#define WF_LWTBL_G2_MASK \
++	0x00001000 // 12-12
++#define WF_LWTBL_G2_SHIFT                                           12
++#define WF_LWTBL_G4_DW                                              6
++#define WF_LWTBL_G4_ADDR                                            24
++#define WF_LWTBL_G4_MASK \
++	0x00002000 // 13-13
++#define WF_LWTBL_G4_SHIFT                                           13
++#define WF_LWTBL_G8_DW                                              6
++#define WF_LWTBL_G8_ADDR                                            24
++#define WF_LWTBL_G8_MASK \
++	0x00004000 // 14-14
++#define WF_LWTBL_G8_SHIFT                                           14
++#define WF_LWTBL_G16_DW                                             6
++#define WF_LWTBL_G16_ADDR                                           24
++#define WF_LWTBL_G16_MASK \
++	0x00008000 // 15-15
++#define WF_LWTBL_G16_SHIFT                                          15
++#define WF_LWTBL_G2_LTF_DW                                          6
++#define WF_LWTBL_G2_LTF_ADDR                                        24
++#define WF_LWTBL_G2_LTF_MASK \
++	0x00030000 // 17-16
++#define WF_LWTBL_G2_LTF_SHIFT                                       16
++#define WF_LWTBL_G4_LTF_DW                                          6
++#define WF_LWTBL_G4_LTF_ADDR                                        24
++#define WF_LWTBL_G4_LTF_MASK \
++	0x000c0000 // 19-18
++#define WF_LWTBL_G4_LTF_SHIFT                                       18
++#define WF_LWTBL_G8_LTF_DW                                          6
++#define WF_LWTBL_G8_LTF_ADDR                                        24
++#define WF_LWTBL_G8_LTF_MASK \
++	0x00300000 // 21-20
++#define WF_LWTBL_G8_LTF_SHIFT                                       20
++#define WF_LWTBL_G16_LTF_DW                                         6
++#define WF_LWTBL_G16_LTF_ADDR                                       24
++#define WF_LWTBL_G16_LTF_MASK \
++	0x00c00000 // 23-22
++#define WF_LWTBL_G16_LTF_SHIFT                                      22
++#define WF_LWTBL_G2_HE_DW                                           6
++#define WF_LWTBL_G2_HE_ADDR                                         24
++#define WF_LWTBL_G2_HE_MASK \
++	0x03000000 // 25-24
++#define WF_LWTBL_G2_HE_SHIFT                                        24
++#define WF_LWTBL_G4_HE_DW                                           6
++#define WF_LWTBL_G4_HE_ADDR                                         24
++#define WF_LWTBL_G4_HE_MASK \
++	0x0c000000 // 27-26
++#define WF_LWTBL_G4_HE_SHIFT                                        26
++#define WF_LWTBL_G8_HE_DW                                           6
++#define WF_LWTBL_G8_HE_ADDR                                         24
++#define WF_LWTBL_G8_HE_MASK \
++	0x30000000 // 29-28
++#define WF_LWTBL_G8_HE_SHIFT                                        28
++#define WF_LWTBL_G16_HE_DW                                          6
++#define WF_LWTBL_G16_HE_ADDR                                        24
++#define WF_LWTBL_G16_HE_MASK \
++	0xc0000000 // 31-30
++#define WF_LWTBL_G16_HE_SHIFT                                       30
++// DW7
++#define WF_LWTBL_BA_WIN_SIZE0_DW                                    7
++#define WF_LWTBL_BA_WIN_SIZE0_ADDR                                  28
++#define WF_LWTBL_BA_WIN_SIZE0_MASK \
++	0x0000000f // 3- 0
++#define WF_LWTBL_BA_WIN_SIZE0_SHIFT                                 0
++#define WF_LWTBL_BA_WIN_SIZE1_DW                                    7
++#define WF_LWTBL_BA_WIN_SIZE1_ADDR                                  28
++#define WF_LWTBL_BA_WIN_SIZE1_MASK \
++	0x000000f0 // 7- 4
++#define WF_LWTBL_BA_WIN_SIZE1_SHIFT                                 4
++#define WF_LWTBL_BA_WIN_SIZE2_DW                                    7
++#define WF_LWTBL_BA_WIN_SIZE2_ADDR                                  28
++#define WF_LWTBL_BA_WIN_SIZE2_MASK \
++	0x00000f00 // 11- 8
++#define WF_LWTBL_BA_WIN_SIZE2_SHIFT                                 8
++#define WF_LWTBL_BA_WIN_SIZE3_DW                                    7
++#define WF_LWTBL_BA_WIN_SIZE3_ADDR                                  28
++#define WF_LWTBL_BA_WIN_SIZE3_MASK \
++	0x0000f000 // 15-12
++#define WF_LWTBL_BA_WIN_SIZE3_SHIFT                                 12
++#define WF_LWTBL_BA_WIN_SIZE4_DW                                    7
++#define WF_LWTBL_BA_WIN_SIZE4_ADDR                                  28
++#define WF_LWTBL_BA_WIN_SIZE4_MASK \
++	0x000f0000 // 19-16
++#define WF_LWTBL_BA_WIN_SIZE4_SHIFT                                 16
++#define WF_LWTBL_BA_WIN_SIZE5_DW                                    7
++#define WF_LWTBL_BA_WIN_SIZE5_ADDR                                  28
++#define WF_LWTBL_BA_WIN_SIZE5_MASK \
++	0x00f00000 // 23-20
++#define WF_LWTBL_BA_WIN_SIZE5_SHIFT                                 20
++#define WF_LWTBL_BA_WIN_SIZE6_DW                                    7
++#define WF_LWTBL_BA_WIN_SIZE6_ADDR                                  28
++#define WF_LWTBL_BA_WIN_SIZE6_MASK \
++	0x0f000000 // 27-24
++#define WF_LWTBL_BA_WIN_SIZE6_SHIFT                                 24
++#define WF_LWTBL_BA_WIN_SIZE7_DW                                    7
++#define WF_LWTBL_BA_WIN_SIZE7_ADDR                                  28
++#define WF_LWTBL_BA_WIN_SIZE7_MASK \
++	0xf0000000 // 31-28
++#define WF_LWTBL_BA_WIN_SIZE7_SHIFT                                 28
++// DW8
++#define WF_LWTBL_AC0_RTS_FAIL_CNT_DW                                8
++#define WF_LWTBL_AC0_RTS_FAIL_CNT_ADDR                              32
++#define WF_LWTBL_AC0_RTS_FAIL_CNT_MASK \
++	0x0000001f // 4- 0
++#define WF_LWTBL_AC0_RTS_FAIL_CNT_SHIFT                             0
++#define WF_LWTBL_AC1_RTS_FAIL_CNT_DW                                8
++#define WF_LWTBL_AC1_RTS_FAIL_CNT_ADDR                              32
++#define WF_LWTBL_AC1_RTS_FAIL_CNT_MASK \
++	0x000003e0 // 9- 5
++#define WF_LWTBL_AC1_RTS_FAIL_CNT_SHIFT                             5
++#define WF_LWTBL_AC2_RTS_FAIL_CNT_DW                                8
++#define WF_LWTBL_AC2_RTS_FAIL_CNT_ADDR                              32
++#define WF_LWTBL_AC2_RTS_FAIL_CNT_MASK \
++	0x00007c00 // 14-10
++#define WF_LWTBL_AC2_RTS_FAIL_CNT_SHIFT                             10
++#define WF_LWTBL_AC3_RTS_FAIL_CNT_DW                                8
++#define WF_LWTBL_AC3_RTS_FAIL_CNT_ADDR                              32
++#define WF_LWTBL_AC3_RTS_FAIL_CNT_MASK \
++	0x000f8000 // 19-15
++#define WF_LWTBL_AC3_RTS_FAIL_CNT_SHIFT                             15
++#define WF_LWTBL_PARTIAL_AID_DW                                     8
++#define WF_LWTBL_PARTIAL_AID_ADDR                                   32
++#define WF_LWTBL_PARTIAL_AID_MASK \
++	0x1ff00000 // 28-20
++#define WF_LWTBL_PARTIAL_AID_SHIFT                                  20
++#define WF_LWTBL_CHK_PER_DW                                         8
++#define WF_LWTBL_CHK_PER_ADDR                                       32
++#define WF_LWTBL_CHK_PER_MASK \
++	0x80000000 // 31-31
++#define WF_LWTBL_CHK_PER_SHIFT                                      31
++// DW9
++#define WF_LWTBL_RX_AVG_MPDU_SIZE_DW                                9
++#define WF_LWTBL_RX_AVG_MPDU_SIZE_ADDR                              36
++#define WF_LWTBL_RX_AVG_MPDU_SIZE_MASK \
++	0x00003fff // 13- 0
++#define WF_LWTBL_RX_AVG_MPDU_SIZE_SHIFT                             0
++#define WF_LWTBL_PRITX_SW_MODE_DW                                   9
++#define WF_LWTBL_PRITX_SW_MODE_ADDR                                 36
++#define WF_LWTBL_PRITX_SW_MODE_MASK \
++	0x00008000 // 15-15
++#define WF_LWTBL_PRITX_SW_MODE_SHIFT                                15
++#define WF_LWTBL_PRITX_SW_MODE_MASK_7992 \
++	0x00004000 // 14-14
++#define WF_LWTBL_PRITX_SW_MODE_SHIFT_7992                           14
++#define WF_LWTBL_PRITX_ERSU_DW                                      9
++#define WF_LWTBL_PRITX_ERSU_ADDR                                    36
++#define WF_LWTBL_PRITX_ERSU_MASK \
++	0x00010000 // 16-16
++#define WF_LWTBL_PRITX_ERSU_SHIFT                                   16
++#define WF_LWTBL_PRITX_ERSU_MASK_7992 \
++	0x00008000 // 15-15
++#define WF_LWTBL_PRITX_ERSU_SHIFT_7992                              15
++#define WF_LWTBL_PRITX_PLR_DW                                       9
++#define WF_LWTBL_PRITX_PLR_ADDR                                     36
++#define WF_LWTBL_PRITX_PLR_MASK \
++	0x00020000 // 17-17
++#define WF_LWTBL_PRITX_PLR_SHIFT                                    17
++#define WF_LWTBL_PRITX_PLR_MASK_7992 \
++	0x00030000 // 17-16
++#define WF_LWTBL_PRITX_PLR_SHIFT_7992                               16
++#define WF_LWTBL_PRITX_DCM_DW                                       9
++#define WF_LWTBL_PRITX_DCM_ADDR                                     36
++#define WF_LWTBL_PRITX_DCM_MASK \
++	0x00040000 // 18-18
++#define WF_LWTBL_PRITX_DCM_SHIFT                                    18
++#define WF_LWTBL_PRITX_ER106T_DW                                    9
++#define WF_LWTBL_PRITX_ER106T_ADDR                                  36
++#define WF_LWTBL_PRITX_ER106T_MASK \
++	0x00080000 // 19-19
++#define WF_LWTBL_PRITX_ER106T_SHIFT                                 19
++#define WF_LWTBL_FCAP_DW                                            9
++#define WF_LWTBL_FCAP_ADDR                                          36
++#define WF_LWTBL_FCAP_MASK \
++	0x00700000 // 22-20
++#define WF_LWTBL_FCAP_SHIFT                                         20
++#define WF_LWTBL_MPDU_FAIL_CNT_DW                                   9
++#define WF_LWTBL_MPDU_FAIL_CNT_ADDR                                 36
++#define WF_LWTBL_MPDU_FAIL_CNT_MASK \
++	0x03800000 // 25-23
++#define WF_LWTBL_MPDU_FAIL_CNT_SHIFT                                23
++#define WF_LWTBL_MPDU_OK_CNT_DW                                     9
++#define WF_LWTBL_MPDU_OK_CNT_ADDR                                   36
++#define WF_LWTBL_MPDU_OK_CNT_MASK \
++	0x1c000000 // 28-26
++#define WF_LWTBL_MPDU_OK_CNT_SHIFT                                  26
++#define WF_LWTBL_RATE_IDX_DW                                        9
++#define WF_LWTBL_RATE_IDX_ADDR                                      36
++#define WF_LWTBL_RATE_IDX_MASK \
++	0xe0000000 // 31-29
++#define WF_LWTBL_RATE_IDX_SHIFT                                     29
++// DW10
++#define WF_LWTBL_RATE1_DW                                           10
++#define WF_LWTBL_RATE1_ADDR                                         40
++#define WF_LWTBL_RATE1_MASK \
++	0x00007fff // 14- 0
++#define WF_LWTBL_RATE1_SHIFT                                        0
++#define WF_LWTBL_RATE2_DW                                           10
++#define WF_LWTBL_RATE2_ADDR                                         40
++#define WF_LWTBL_RATE2_MASK \
++	0x7fff0000 // 30-16
++#define WF_LWTBL_RATE2_SHIFT                                        16
++// DW11
++#define WF_LWTBL_RATE3_DW                                           11
++#define WF_LWTBL_RATE3_ADDR                                         44
++#define WF_LWTBL_RATE3_MASK \
++	0x00007fff // 14- 0
++#define WF_LWTBL_RATE3_SHIFT                                        0
++#define WF_LWTBL_RATE4_DW                                           11
++#define WF_LWTBL_RATE4_ADDR                                         44
++#define WF_LWTBL_RATE4_MASK \
++	0x7fff0000 // 30-16
++#define WF_LWTBL_RATE4_SHIFT                                        16
++// DW12
++#define WF_LWTBL_RATE5_DW                                           12
++#define WF_LWTBL_RATE5_ADDR                                         48
++#define WF_LWTBL_RATE5_MASK \
++	0x00007fff // 14- 0
++#define WF_LWTBL_RATE5_SHIFT                                        0
++#define WF_LWTBL_RATE6_DW                                           12
++#define WF_LWTBL_RATE6_ADDR                                         48
++#define WF_LWTBL_RATE6_MASK \
++	0x7fff0000 // 30-16
++#define WF_LWTBL_RATE6_SHIFT                                        16
++// DW13
++#define WF_LWTBL_RATE7_DW                                           13
++#define WF_LWTBL_RATE7_ADDR                                         52
++#define WF_LWTBL_RATE7_MASK \
++	0x00007fff // 14- 0
++#define WF_LWTBL_RATE7_SHIFT                                        0
++#define WF_LWTBL_RATE8_DW                                           13
++#define WF_LWTBL_RATE8_ADDR                                         52
++#define WF_LWTBL_RATE8_MASK \
++	0x7fff0000 // 30-16
++#define WF_LWTBL_RATE8_SHIFT                                        16
++// DW14
++#define WF_LWTBL_RATE1_TX_CNT_DW                                    14
++#define WF_LWTBL_RATE1_TX_CNT_ADDR                                  56
++#define WF_LWTBL_RATE1_TX_CNT_MASK \
++	0x0000ffff // 15- 0
++#define WF_LWTBL_RATE1_TX_CNT_SHIFT                                 0
++#define WF_LWTBL_CIPHER_SUIT_IGTK_DW                                14
++#define WF_LWTBL_CIPHER_SUIT_IGTK_ADDR                              56
++#define WF_LWTBL_CIPHER_SUIT_IGTK_MASK \
++	0x00003000 // 13-12
++#define WF_LWTBL_CIPHER_SUIT_IGTK_SHIFT                             12
++#define WF_LWTBL_CIPHER_SUIT_BIGTK_DW                               14
++#define WF_LWTBL_CIPHER_SUIT_BIGTK_ADDR                             56
++#define WF_LWTBL_CIPHER_SUIT_BIGTK_MASK \
++	0x0000c000 // 15-14
++#define WF_LWTBL_CIPHER_SUIT_BIGTK_SHIFT                            14
++#define WF_LWTBL_RATE1_FAIL_CNT_DW                                  14
++#define WF_LWTBL_RATE1_FAIL_CNT_ADDR                                56
++#define WF_LWTBL_RATE1_FAIL_CNT_MASK \
++	0xffff0000 // 31-16
++#define WF_LWTBL_RATE1_FAIL_CNT_SHIFT                               16
++// DW15
++#define WF_LWTBL_RATE2_OK_CNT_DW                                    15
++#define WF_LWTBL_RATE2_OK_CNT_ADDR                                  60
++#define WF_LWTBL_RATE2_OK_CNT_MASK \
++	0x0000ffff // 15- 0
++#define WF_LWTBL_RATE2_OK_CNT_SHIFT                                 0
++#define WF_LWTBL_RATE3_OK_CNT_DW                                    15
++#define WF_LWTBL_RATE3_OK_CNT_ADDR                                  60
++#define WF_LWTBL_RATE3_OK_CNT_MASK \
++	0xffff0000 // 31-16
++#define WF_LWTBL_RATE3_OK_CNT_SHIFT                                 16
++// DW16
++#define WF_LWTBL_CURRENT_BW_TX_CNT_DW                               16
++#define WF_LWTBL_CURRENT_BW_TX_CNT_ADDR                             64
++#define WF_LWTBL_CURRENT_BW_TX_CNT_MASK \
++	0x0000ffff // 15- 0
++#define WF_LWTBL_CURRENT_BW_TX_CNT_SHIFT                            0
++#define WF_LWTBL_CURRENT_BW_FAIL_CNT_DW                             16
++#define WF_LWTBL_CURRENT_BW_FAIL_CNT_ADDR                           64
++#define WF_LWTBL_CURRENT_BW_FAIL_CNT_MASK \
++	0xffff0000 // 31-16
++#define WF_LWTBL_CURRENT_BW_FAIL_CNT_SHIFT                          16
++// DW17
++#define WF_LWTBL_OTHER_BW_TX_CNT_DW                                 17
++#define WF_LWTBL_OTHER_BW_TX_CNT_ADDR                               68
++#define WF_LWTBL_OTHER_BW_TX_CNT_MASK \
++	0x0000ffff // 15- 0
++#define WF_LWTBL_OTHER_BW_TX_CNT_SHIFT                              0
++#define WF_LWTBL_OTHER_BW_FAIL_CNT_DW                               17
++#define WF_LWTBL_OTHER_BW_FAIL_CNT_ADDR                             68
++#define WF_LWTBL_OTHER_BW_FAIL_CNT_MASK \
++	0xffff0000 // 31-16
++#define WF_LWTBL_OTHER_BW_FAIL_CNT_SHIFT                            16
++// DW18
++#define WF_LWTBL_RTS_OK_CNT_DW                                      18
++#define WF_LWTBL_RTS_OK_CNT_ADDR                                    72
++#define WF_LWTBL_RTS_OK_CNT_MASK \
++	0x0000ffff // 15- 0
++#define WF_LWTBL_RTS_OK_CNT_SHIFT                                   0
++#define WF_LWTBL_RTS_FAIL_CNT_DW                                    18
++#define WF_LWTBL_RTS_FAIL_CNT_ADDR                                  72
++#define WF_LWTBL_RTS_FAIL_CNT_MASK \
++	0xffff0000 // 31-16
++#define WF_LWTBL_RTS_FAIL_CNT_SHIFT                                 16
++// DW19
++#define WF_LWTBL_DATA_RETRY_CNT_DW                                  19
++#define WF_LWTBL_DATA_RETRY_CNT_ADDR                                76
++#define WF_LWTBL_DATA_RETRY_CNT_MASK \
++	0x0000ffff // 15- 0
++#define WF_LWTBL_DATA_RETRY_CNT_SHIFT                               0
++#define WF_LWTBL_MGNT_RETRY_CNT_DW                                  19
++#define WF_LWTBL_MGNT_RETRY_CNT_ADDR                                76
++#define WF_LWTBL_MGNT_RETRY_CNT_MASK \
++	0xffff0000 // 31-16
++#define WF_LWTBL_MGNT_RETRY_CNT_SHIFT                               16
++// DW20
++#define WF_LWTBL_AC0_CTT_CDT_CRB_DW                                 20
++#define WF_LWTBL_AC0_CTT_CDT_CRB_ADDR                               80
++#define WF_LWTBL_AC0_CTT_CDT_CRB_MASK \
++	0xffffffff // 31- 0
++#define WF_LWTBL_AC0_CTT_CDT_CRB_SHIFT                              0
++// DW21
++// DO NOT process repeat field(adm[0])
++// DW22
++#define WF_LWTBL_AC1_CTT_CDT_CRB_DW                                 22
++#define WF_LWTBL_AC1_CTT_CDT_CRB_ADDR                               88
++#define WF_LWTBL_AC1_CTT_CDT_CRB_MASK \
++	0xffffffff // 31- 0
++#define WF_LWTBL_AC1_CTT_CDT_CRB_SHIFT                              0
++// DW23
++// DO NOT process repeat field(adm[1])
++// DW24
++#define WF_LWTBL_AC2_CTT_CDT_CRB_DW                                 24
++#define WF_LWTBL_AC2_CTT_CDT_CRB_ADDR                               96
++#define WF_LWTBL_AC2_CTT_CDT_CRB_MASK \
++	0xffffffff // 31- 0
++#define WF_LWTBL_AC2_CTT_CDT_CRB_SHIFT                              0
++// DW25
++// DO NOT process repeat field(adm[2])
++// DW26
++#define WF_LWTBL_AC3_CTT_CDT_CRB_DW                                 26
++#define WF_LWTBL_AC3_CTT_CDT_CRB_ADDR                               104
++#define WF_LWTBL_AC3_CTT_CDT_CRB_MASK \
++	0xffffffff // 31- 0
++#define WF_LWTBL_AC3_CTT_CDT_CRB_SHIFT                              0
++// DW27
++// DO NOT process repeat field(adm[3])
++// DW28
++#define WF_LWTBL_RELATED_IDX0_DW                                    28
++#define WF_LWTBL_RELATED_IDX0_ADDR                                  112
++#define WF_LWTBL_RELATED_IDX0_MASK \
++	0x00000fff // 11- 0
++#define WF_LWTBL_RELATED_IDX0_SHIFT                                 0
++#define WF_LWTBL_RELATED_BAND0_DW                                   28
++#define WF_LWTBL_RELATED_BAND0_ADDR                                 112
++#define WF_LWTBL_RELATED_BAND0_MASK \
++	0x00003000 // 13-12
++#define WF_LWTBL_RELATED_BAND0_SHIFT                                12
++#define WF_LWTBL_PRIMARY_MLD_BAND_DW                                28
++#define WF_LWTBL_PRIMARY_MLD_BAND_ADDR                              112
++#define WF_LWTBL_PRIMARY_MLD_BAND_MASK \
++	0x0000c000 // 15-14
++#define WF_LWTBL_PRIMARY_MLD_BAND_SHIFT                             14
++#define WF_LWTBL_RELATED_IDX1_DW                                    28
++#define WF_LWTBL_RELATED_IDX1_ADDR                                  112
++#define WF_LWTBL_RELATED_IDX1_MASK \
++	0x0fff0000 // 27-16
++#define WF_LWTBL_RELATED_IDX1_SHIFT                                 16
++#define WF_LWTBL_RELATED_BAND1_DW                                   28
++#define WF_LWTBL_RELATED_BAND1_ADDR                                 112
++#define WF_LWTBL_RELATED_BAND1_MASK \
++	0x30000000 // 29-28
++#define WF_LWTBL_RELATED_BAND1_SHIFT                                28
++#define WF_LWTBL_SECONDARY_MLD_BAND_DW                              28
++#define WF_LWTBL_SECONDARY_MLD_BAND_ADDR                            112
++#define WF_LWTBL_SECONDARY_MLD_BAND_MASK \
++	0xc0000000 // 31-30
++#define WF_LWTBL_SECONDARY_MLD_BAND_SHIFT                           30
++// DW29
++#define WF_LWTBL_DISPATCH_POLICY0_DW                                29
++#define WF_LWTBL_DISPATCH_POLICY0_ADDR                              116
++#define WF_LWTBL_DISPATCH_POLICY0_MASK \
++	0x00000003 // 1- 0
++#define WF_LWTBL_DISPATCH_POLICY0_SHIFT                             0
++#define WF_LWTBL_DISPATCH_POLICY1_DW                                29
++#define WF_LWTBL_DISPATCH_POLICY1_ADDR                              116
++#define WF_LWTBL_DISPATCH_POLICY1_MASK \
++	0x0000000c // 3- 2
++#define WF_LWTBL_DISPATCH_POLICY1_SHIFT                             2
++#define WF_LWTBL_DISPATCH_POLICY2_DW                                29
++#define WF_LWTBL_DISPATCH_POLICY2_ADDR                              116
++#define WF_LWTBL_DISPATCH_POLICY2_MASK \
++	0x00000030 // 5- 4
++#define WF_LWTBL_DISPATCH_POLICY2_SHIFT                             4
++#define WF_LWTBL_DISPATCH_POLICY3_DW                                29
++#define WF_LWTBL_DISPATCH_POLICY3_ADDR                              116
++#define WF_LWTBL_DISPATCH_POLICY3_MASK \
++	0x000000c0 // 7- 6
++#define WF_LWTBL_DISPATCH_POLICY3_SHIFT                             6
++#define WF_LWTBL_DISPATCH_POLICY4_DW                                29
++#define WF_LWTBL_DISPATCH_POLICY4_ADDR                              116
++#define WF_LWTBL_DISPATCH_POLICY4_MASK \
++	0x00000300 // 9- 8
++#define WF_LWTBL_DISPATCH_POLICY4_SHIFT                             8
++#define WF_LWTBL_DISPATCH_POLICY5_DW                                29
++#define WF_LWTBL_DISPATCH_POLICY5_ADDR                              116
++#define WF_LWTBL_DISPATCH_POLICY5_MASK \
++	0x00000c00 // 11-10
++#define WF_LWTBL_DISPATCH_POLICY5_SHIFT                             10
++#define WF_LWTBL_DISPATCH_POLICY6_DW                                29
++#define WF_LWTBL_DISPATCH_POLICY6_ADDR                              116
++#define WF_LWTBL_DISPATCH_POLICY6_MASK \
++	0x00003000 // 13-12
++#define WF_LWTBL_DISPATCH_POLICY6_SHIFT                             12
++#define WF_LWTBL_DISPATCH_POLICY7_DW                                29
++#define WF_LWTBL_DISPATCH_POLICY7_ADDR                              116
++#define WF_LWTBL_DISPATCH_POLICY7_MASK \
++	0x0000c000 // 15-14
++#define WF_LWTBL_DISPATCH_POLICY7_SHIFT                             14
++#define WF_LWTBL_OWN_MLD_ID_DW                                      29
++#define WF_LWTBL_OWN_MLD_ID_ADDR                                    116
++#define WF_LWTBL_OWN_MLD_ID_MASK \
++	0x003f0000 // 21-16
++#define WF_LWTBL_OWN_MLD_ID_SHIFT                                   16
++#define WF_LWTBL_EMLSR0_DW                                          29
++#define WF_LWTBL_EMLSR0_ADDR                                        116
++#define WF_LWTBL_EMLSR0_MASK \
++	0x00400000 // 22-22
++#define WF_LWTBL_EMLSR0_SHIFT                                       22
++#define WF_LWTBL_EMLMR0_DW                                          29
++#define WF_LWTBL_EMLMR0_ADDR                                        116
++#define WF_LWTBL_EMLMR0_MASK \
++	0x00800000 // 23-23
++#define WF_LWTBL_EMLMR0_SHIFT                                       23
++#define WF_LWTBL_EMLSR1_DW                                          29
++#define WF_LWTBL_EMLSR1_ADDR                                        116
++#define WF_LWTBL_EMLSR1_MASK \
++	0x01000000 // 24-24
++#define WF_LWTBL_EMLSR1_SHIFT                                       24
++#define WF_LWTBL_EMLMR1_DW                                          29
++#define WF_LWTBL_EMLMR1_ADDR                                        116
++#define WF_LWTBL_EMLMR1_MASK \
++	0x02000000 // 25-25
++#define WF_LWTBL_EMLMR1_SHIFT                                       25
++#define WF_LWTBL_EMLSR2_DW                                          29
++#define WF_LWTBL_EMLSR2_ADDR                                        116
++#define WF_LWTBL_EMLSR2_MASK \
++	0x04000000 // 26-26
++#define WF_LWTBL_EMLSR2_SHIFT                                       26
++#define WF_LWTBL_EMLMR2_DW                                          29
++#define WF_LWTBL_EMLMR2_ADDR                                        116
++#define WF_LWTBL_EMLMR2_MASK \
++	0x08000000 // 27-27
++#define WF_LWTBL_EMLMR2_SHIFT                                       27
++#define WF_LWTBL_STR_BITMAP_DW                                      29
++#define WF_LWTBL_STR_BITMAP_ADDR                                    116
++#define WF_LWTBL_STR_BITMAP_MASK \
++	0xe0000000 // 31-29
++#define WF_LWTBL_STR_BITMAP_SHIFT                                   29
++// DW30
++#define WF_LWTBL_DISPATCH_ORDER_DW                                  30
++#define WF_LWTBL_DISPATCH_ORDER_ADDR                                120
++#define WF_LWTBL_DISPATCH_ORDER_MASK \
++	0x0000007f // 6- 0
++#define WF_LWTBL_DISPATCH_ORDER_SHIFT                               0
++#define WF_LWTBL_DISPATCH_RATIO_DW                                  30
++#define WF_LWTBL_DISPATCH_RATIO_ADDR                                120
++#define WF_LWTBL_DISPATCH_RATIO_MASK \
++	0x00003f80 // 13- 7
++#define WF_LWTBL_DISPATCH_RATIO_SHIFT                               7
++#define WF_LWTBL_LINK_MGF_DW                                        30
++#define WF_LWTBL_LINK_MGF_ADDR                                      120
++#define WF_LWTBL_LINK_MGF_MASK \
++	0xffff0000 // 31-16
++#define WF_LWTBL_LINK_MGF_SHIFT                                     16
++// DW31
++#define WF_LWTBL_BFTX_TB_DW                                         31
++#define WF_LWTBL_BFTX_TB_ADDR                                       124
++#define WF_LWTBL_BFTX_TB_MASK \
++	0x00800000 // 23-23
++#define WF_LWTBL_DROP_DW                                            31
++#define WF_LWTBL_DROP_ADDR                                          124
++#define WF_LWTBL_DROP_MASK \
++	0x01000000 // 24-24
++#define WF_LWTBL_DROP_SHIFT                                         24
++#define WF_LWTBL_CASCAD_DW                                          31
++#define WF_LWTBL_CASCAD_ADDR                                        124
++#define WF_LWTBL_CASCAD_MASK \
++	0x02000000 // 25-25
++#define WF_LWTBL_CASCAD_SHIFT                                       25
++#define WF_LWTBL_ALL_ACK_DW                                         31
++#define WF_LWTBL_ALL_ACK_ADDR                                       124
++#define WF_LWTBL_ALL_ACK_MASK \
++	0x04000000 // 26-26
++#define WF_LWTBL_ALL_ACK_SHIFT                                      26
++#define WF_LWTBL_MPDU_SIZE_DW                                       31
++#define WF_LWTBL_MPDU_SIZE_ADDR                                     124
++#define WF_LWTBL_MPDU_SIZE_MASK \
++	0x18000000 // 28-27
++#define WF_LWTBL_MPDU_SIZE_SHIFT                                    27
++#define WF_LWTBL_RXD_DUP_MODE_DW                                    31
++#define WF_LWTBL_RXD_DUP_MODE_ADDR                                  124
++#define WF_LWTBL_RXD_DUP_MODE_MASK \
++	0x60000000 // 30-29
++#define WF_LWTBL_RXD_DUP_MODE_SHIFT                                 29
++#define WF_LWTBL_ACK_EN_DW                                          31
++#define WF_LWTBL_ACK_EN_ADDR                                        128
++#define WF_LWTBL_ACK_EN_MASK \
++	0x80000000 // 31-31
++#define WF_LWTBL_ACK_EN_SHIFT                                       31
++// DW32
++#define WF_LWTBL_OM_INFO_DW                                         32
++#define WF_LWTBL_OM_INFO_ADDR                                       128
++#define WF_LWTBL_OM_INFO_MASK \
++	0x00000fff // 11- 0
++#define WF_LWTBL_OM_INFO_SHIFT                                      0
++#define WF_LWTBL_OM_INFO_EHT_DW                                     32
++#define WF_LWTBL_OM_INFO_EHT_ADDR                                   128
++#define WF_LWTBL_OM_INFO_EHT_MASK \
++	0x0000f000 // 15-12
++#define WF_LWTBL_OM_INFO_EHT_SHIFT                                  12
++#define WF_LWTBL_RXD_DUP_FOR_OM_CHG_DW                              32
++#define WF_LWTBL_RXD_DUP_FOR_OM_CHG_ADDR                            128
++#define WF_LWTBL_RXD_DUP_FOR_OM_CHG_MASK \
++	0x00010000 // 16-16
++#define WF_LWTBL_RXD_DUP_FOR_OM_CHG_SHIFT                           16
++#define WF_LWTBL_RXD_DUP_WHITE_LIST_DW                              32
++#define WF_LWTBL_RXD_DUP_WHITE_LIST_ADDR                            128
++#define WF_LWTBL_RXD_DUP_WHITE_LIST_MASK \
++	0x1ffe0000 // 28-17
++#define WF_LWTBL_RXD_DUP_WHITE_LIST_SHIFT                           17
++// DW33
++#define WF_LWTBL_USER_RSSI_DW                                       33
++#define WF_LWTBL_USER_RSSI_ADDR                                     132
++#define WF_LWTBL_USER_RSSI_MASK \
++	0x000001ff // 8- 0
++#define WF_LWTBL_USER_RSSI_SHIFT                                    0
++#define WF_LWTBL_USER_SNR_DW                                        33
++#define WF_LWTBL_USER_SNR_ADDR                                      132
++#define WF_LWTBL_USER_SNR_MASK \
++	0x00007e00 // 14- 9
++#define WF_LWTBL_USER_SNR_SHIFT                                     9
++#define WF_LWTBL_RAPID_REACTION_RATE_DW                             33
++#define WF_LWTBL_RAPID_REACTION_RATE_ADDR                           132
++#define WF_LWTBL_RAPID_REACTION_RATE_MASK \
++	0x0fff0000 // 27-16
++#define WF_LWTBL_RAPID_REACTION_RATE_SHIFT                          16
++#define WF_LWTBL_HT_AMSDU_DW                                        33
++#define WF_LWTBL_HT_AMSDU_ADDR                                      132
++#define WF_LWTBL_HT_AMSDU_MASK \
++	0x40000000 // 30-30
++#define WF_LWTBL_HT_AMSDU_SHIFT                                     30
++#define WF_LWTBL_AMSDU_CROSS_LG_DW                                  33
++#define WF_LWTBL_AMSDU_CROSS_LG_ADDR                                132
++#define WF_LWTBL_AMSDU_CROSS_LG_MASK \
++	0x80000000 // 31-31
++#define WF_LWTBL_AMSDU_CROSS_LG_SHIFT                               31
++// DW34
++#define WF_LWTBL_RESP_RCPI0_DW                                      34
++#define WF_LWTBL_RESP_RCPI0_ADDR                                    136
++#define WF_LWTBL_RESP_RCPI0_MASK \
++	0x000000ff // 7- 0
++#define WF_LWTBL_RESP_RCPI0_SHIFT                                   0
++#define WF_LWTBL_RESP_RCPI1_DW                                      34
++#define WF_LWTBL_RESP_RCPI1_ADDR                                    136
++#define WF_LWTBL_RESP_RCPI1_MASK \
++	0x0000ff00 // 15- 8
++#define WF_LWTBL_RESP_RCPI1_SHIFT                                   8
++#define WF_LWTBL_RESP_RCPI2_DW                                      34
++#define WF_LWTBL_RESP_RCPI2_ADDR                                    136
++#define WF_LWTBL_RESP_RCPI2_MASK \
++	0x00ff0000 // 23-16
++#define WF_LWTBL_RESP_RCPI2_SHIFT                                   16
++#define WF_LWTBL_RESP_RCPI3_DW                                      34
++#define WF_LWTBL_RESP_RCPI3_ADDR                                    136
++#define WF_LWTBL_RESP_RCPI3_MASK \
++	0xff000000 // 31-24
++#define WF_LWTBL_RESP_RCPI3_SHIFT                                   24
++// DW35
++#define WF_LWTBL_SNR_RX0_DW                                         35
++#define WF_LWTBL_SNR_RX0_ADDR                                       140
++#define WF_LWTBL_SNR_RX0_MASK \
++	0x0000003f // 5- 0
++#define WF_LWTBL_SNR_RX0_SHIFT                                      0
++#define WF_LWTBL_SNR_RX1_DW                                         35
++#define WF_LWTBL_SNR_RX1_ADDR                                       140
++#define WF_LWTBL_SNR_RX1_MASK \
++	0x00000fc0 // 11- 6
++#define WF_LWTBL_SNR_RX1_SHIFT                                      6
++#define WF_LWTBL_SNR_RX2_DW                                         35
++#define WF_LWTBL_SNR_RX2_ADDR                                       140
++#define WF_LWTBL_SNR_RX2_MASK \
++	0x0003f000 // 17-12
++#define WF_LWTBL_SNR_RX2_SHIFT                                      12
++#define WF_LWTBL_SNR_RX3_DW                                         35
++#define WF_LWTBL_SNR_RX3_ADDR                                       140
++#define WF_LWTBL_SNR_RX3_MASK \
++	0x00fc0000 // 23-18
++#define WF_LWTBL_SNR_RX3_SHIFT                                      18
++
++/* WTBL Group - Packet Number */
++/* DW 2 */
++#define WTBL_PN0_MASK                   BITS(0, 7)
++#define WTBL_PN0_OFFSET                 0
++#define WTBL_PN1_MASK                   BITS(8, 15)
++#define WTBL_PN1_OFFSET                 8
++#define WTBL_PN2_MASK                   BITS(16, 23)
++#define WTBL_PN2_OFFSET                 16
++#define WTBL_PN3_MASK                   BITS(24, 31)
++#define WTBL_PN3_OFFSET                 24
++
++/* DW 3 */
++#define WTBL_PN4_MASK                   BITS(0, 7)
++#define WTBL_PN4_OFFSET                 0
++#define WTBL_PN5_MASK                   BITS(8, 15)
++#define WTBL_PN5_OFFSET                 8
++
++/* DW 4 */
++#define WTBL_BIPN0_MASK                 BITS(0, 7)
++#define WTBL_BIPN0_OFFSET               0
++#define WTBL_BIPN1_MASK                 BITS(8, 15)
++#define WTBL_BIPN1_OFFSET               8
++#define WTBL_BIPN2_MASK                 BITS(16, 23)
++#define WTBL_BIPN2_OFFSET               16
++#define WTBL_BIPN3_MASK                 BITS(24, 31)
++#define WTBL_BIPN3_OFFSET               24
++
++/* DW 5 */
++#define WTBL_BIPN4_MASK                 BITS(0, 7)
++#define WTBL_BIPN4_OFFSET               0
++#define WTBL_BIPN5_MASK                 BITS(8, 15)
++#define WTBL_BIPN5_OFFSET               8
++
++/* UWTBL DW 6 */
++#define WTBL_AMSDU_LEN_MASK             BITS(0, 5)
++#define WTBL_AMSDU_LEN_OFFSET           0
++#define WTBL_AMSDU_NUM_MASK             BITS(6, 10)
++#define WTBL_AMSDU_NUM_OFFSET           6
++#define WTBL_AMSDU_EN_MASK              BIT(11)
++#define WTBL_AMSDU_EN_OFFSET            11
++
++/* UWTBL DW 8 */
++#define WTBL_SEC_ADDR_MODE_MASK		BITS(20, 21)
++#define WTBL_SEC_ADDR_MODE_OFFSET	20
++
++/* LWTBL Rate field */
++#define WTBL_RATE_TX_RATE_MASK          BITS(0, 5)
++#define WTBL_RATE_TX_RATE_OFFSET        0
++#define WTBL_RATE_TX_MODE_MASK          BITS(6, 9)
++#define WTBL_RATE_TX_MODE_OFFSET        6
++#define WTBL_RATE_NSTS_MASK             BITS(10, 13)
++#define WTBL_RATE_NSTS_OFFSET           10
++#define WTBL_RATE_STBC_MASK             BIT(14)
++#define WTBL_RATE_STBC_OFFSET           14
++
++/***** WTBL(LMAC) DW Offset *****/
++/* LMAC WTBL Group - Peer Unique Information */
++#define WTBL_GROUP_PEER_INFO_DW_0               0
++#define WTBL_GROUP_PEER_INFO_DW_1               1
++
++/* WTBL Group - TxRx Capability/Information */
++#define WTBL_GROUP_TRX_CAP_DW_2                 2
++#define WTBL_GROUP_TRX_CAP_DW_3                 3
++#define WTBL_GROUP_TRX_CAP_DW_4                 4
++#define WTBL_GROUP_TRX_CAP_DW_5                 5
++#define WTBL_GROUP_TRX_CAP_DW_6                 6
++#define WTBL_GROUP_TRX_CAP_DW_7                 7
++#define WTBL_GROUP_TRX_CAP_DW_8                 8
++#define WTBL_GROUP_TRX_CAP_DW_9                 9
++
++/* WTBL Group - Auto Rate Table*/
++#define WTBL_GROUP_AUTO_RATE_1_2                10
++#define WTBL_GROUP_AUTO_RATE_3_4                11
++#define WTBL_GROUP_AUTO_RATE_5_6                12
++#define WTBL_GROUP_AUTO_RATE_7_8                13
++
++/* WTBL Group - Tx Counter */
++#define WTBL_GROUP_TX_CNT_LINE_1                14
++#define WTBL_GROUP_TX_CNT_LINE_2                15
++#define WTBL_GROUP_TX_CNT_LINE_3                16
++#define WTBL_GROUP_TX_CNT_LINE_4                17
++#define WTBL_GROUP_TX_CNT_LINE_5                18
++#define WTBL_GROUP_TX_CNT_LINE_6                19
++
++/* WTBL Group - Admission Control Counter */
++#define WTBL_GROUP_ADM_CNT_LINE_1               20
++#define WTBL_GROUP_ADM_CNT_LINE_2               21
++#define WTBL_GROUP_ADM_CNT_LINE_3               22
++#define WTBL_GROUP_ADM_CNT_LINE_4               23
++#define WTBL_GROUP_ADM_CNT_LINE_5               24
++#define WTBL_GROUP_ADM_CNT_LINE_6               25
++#define WTBL_GROUP_ADM_CNT_LINE_7               26
++#define WTBL_GROUP_ADM_CNT_LINE_8               27
++
++/* WTBL Group -MLO Info */
++#define WTBL_GROUP_MLO_INFO_LINE_1              28
++#define WTBL_GROUP_MLO_INFO_LINE_2              29
++#define WTBL_GROUP_MLO_INFO_LINE_3              30
++
++/* WTBL Group -RESP Info */
++#define WTBL_GROUP_RESP_INFO_DW_31              31
++
++/* WTBL Group -RX DUP Info */
++#define WTBL_GROUP_RX_DUP_INFO_DW_32            32
++
++/* WTBL Group - Rx Statistics Counter */
++#define WTBL_GROUP_RX_STAT_CNT_LINE_1           33
++#define WTBL_GROUP_RX_STAT_CNT_LINE_2           34
++#define WTBL_GROUP_RX_STAT_CNT_LINE_3           35
++
++/* UWTBL Group - HW AMSDU */
++#define UWTBL_HW_AMSDU_DW                       WF_UWTBL_AMSDU_CFG_DW
++
++/* LWTBL DW 4 */
++#define WTBL_DIS_RHTR                           WF_LWTBL_DIS_RHTR_MASK
++
++/* UWTBL DW 5 */
++#define WTBL_KEY_LINK_DW_KEY_LOC0_MASK          BITS(0, 10)
++#define WTBL_PSM				WF_LWTBL_PSM_MASK
++
++/* Need to sync with FW define */
++#define INVALID_KEY_ENTRY                       WTBL_KEY_LINK_DW_KEY_LOC0_MASK
++
++// RATE
++#define WTBL_RATE_TX_RATE_MASK          	BITS(0, 5)
++#define WTBL_RATE_TX_RATE_OFFSET        	0
++#define WTBL_RATE_TX_MODE_MASK          	BITS(6, 9)
++#define WTBL_RATE_TX_MODE_OFFSET        	6
++#define WTBL_RATE_NSTS_MASK             	BITS(10, 13)
++#define WTBL_RATE_NSTS_OFFSET           	10
++#define WTBL_RATE_STBC_MASK            	 	BIT(14)
++#define WTBL_RATE_STBC_OFFSET          	 	14
++#endif
++
++#endif
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+new file mode 100644
+index 00000000..64952a73
+--- /dev/null
++++ b/mt7996/mtk_debugfs.c
+@@ -0,0 +1,2506 @@
++// SPDX-License-Identifier: ISC
++/*
++ * Copyright (C) 2023 MediaTek Inc.
++ */
++#include "mt7996.h"
++#include "../mt76.h"
++#include "mcu.h"
++#include "mac.h"
++#include "eeprom.h"
++#include "mtk_debug.h"
++#include "mtk_mcu.h"
++#include "coredump.h"
++
++#ifdef CONFIG_MTK_DEBUG
++
++/* AGG INFO */
++static int
++mt7996_agginfo_read_per_band(struct seq_file *s, int band_idx)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	u64 total_burst, total_ampdu, ampdu_cnt[16];
++	u32 value, idx, row_idx, col_idx, start_range, agg_rang_sel[16], burst_cnt[16], band_offset = 0;
++	u8 partial_str[16] = {}, full_str[64] = {};
++
++	switch (band_idx) {
++	case 0:
++		band_offset = 0;
++		break;
++	case 1:
++		band_offset = BN1_WF_AGG_TOP_BASE - BN0_WF_AGG_TOP_BASE;
++		break;
++	case 2:
++		band_offset = IP1_BN0_WF_AGG_TOP_BASE - BN0_WF_AGG_TOP_BASE;
++		break;
++	default:
++		return 0;
++	}
++
++	seq_printf(s, "Band %d AGG Status\n", band_idx);
++	seq_printf(s, "===============================\n");
++	value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR0_ADDR + band_offset);
++	seq_printf(s, "AC00 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR0_AC00_AGG_LIMIT_SHFT);
++	seq_printf(s, "AC01 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR0_AC01_AGG_LIMIT_SHFT);
++	value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR1_ADDR + band_offset);
++	seq_printf(s, "AC02 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR1_AC02_AGG_LIMIT_SHFT);
++	seq_printf(s, "AC03 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR1_AC03_AGG_LIMIT_SHFT);
++	value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR2_ADDR + band_offset);
++	seq_printf(s, "AC10 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR2_AC10_AGG_LIMIT_SHFT);
++	seq_printf(s, "AC11 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR2_AC11_AGG_LIMIT_SHFT);
++	value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR3_ADDR + band_offset);
++	seq_printf(s, "AC12 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR3_AC12_AGG_LIMIT_SHFT);
++	seq_printf(s, "AC13 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR3_AC13_AGG_LIMIT_SHFT);
++	value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR4_ADDR + band_offset);
++	seq_printf(s, "AC20 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR4_AC20_AGG_LIMIT_SHFT);
++	seq_printf(s, "AC21 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR4_AC21_AGG_LIMIT_SHFT);
++	value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR5_ADDR + band_offset);
++	seq_printf(s, "AC22 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR5_AC22_AGG_LIMIT_SHFT);
++	seq_printf(s, "AC23 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR5_AC23_AGG_LIMIT_SHFT);
++	value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR6_ADDR + band_offset);
++	seq_printf(s, "AC30 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR6_AC30_AGG_LIMIT_SHFT);
++	seq_printf(s, "AC31 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR6_AC31_AGG_LIMIT_SHFT);
++	value = mt76_rr(dev, BN0_WF_AGG_TOP_AALCR7_ADDR + band_offset);
++	seq_printf(s, "AC32 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR7_AC32_AGG_LIMIT_SHFT);
++	seq_printf(s, "AC33 Agg limit = %d\t", (value & BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_MASK) >>  BN0_WF_AGG_TOP_AALCR7_AC33_AGG_LIMIT_SHFT);
++
++	switch (band_idx) {
++	case 0:
++		band_offset = 0;
++		break;
++	case 1:
++		band_offset = BN1_WF_MIB_TOP_BASE - BN0_WF_MIB_TOP_BASE;
++		break;
++	case 2:
++		band_offset = IP1_BN0_WF_MIB_TOP_BASE - BN0_WF_MIB_TOP_BASE;
++		break;
++	default:
++		return 0;
++	}
++
++	seq_printf(s, "===AMPDU Related Counters===\n");
++
++	value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC0_ADDR + band_offset);
++	agg_rang_sel[0] = (value & BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_MASK) >> BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_0_SHFT;
++	agg_rang_sel[1] = (value & BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_MASK) >> BN0_WF_MIB_TOP_TRARC0_AGG_RANG_SEL_1_SHFT;
++	value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC1_ADDR + band_offset);
++	agg_rang_sel[2] = (value & BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_MASK) >> BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_2_SHFT;
++	agg_rang_sel[3] = (value & BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_MASK) >> BN0_WF_MIB_TOP_TRARC1_AGG_RANG_SEL_3_SHFT;
++	value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC2_ADDR + band_offset);
++	agg_rang_sel[4] = (value & BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_MASK) >> BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_4_SHFT;
++	agg_rang_sel[5] = (value & BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_MASK) >> BN0_WF_MIB_TOP_TRARC2_AGG_RANG_SEL_5_SHFT;
++	value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC3_ADDR + band_offset);
++	agg_rang_sel[6] = (value & BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_MASK) >> BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_6_SHFT;
++	agg_rang_sel[7] = (value & BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_MASK) >> BN0_WF_MIB_TOP_TRARC3_AGG_RANG_SEL_7_SHFT;
++	value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC4_ADDR + band_offset);
++	agg_rang_sel[8] = (value & BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_MASK) >> BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_8_SHFT;
++	agg_rang_sel[9] = (value & BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_MASK) >> BN0_WF_MIB_TOP_TRARC4_AGG_RANG_SEL_9_SHFT;
++	value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC5_ADDR + band_offset);
++	agg_rang_sel[10] = (value & BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_MASK) >> BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_10_SHFT;
++	agg_rang_sel[11] = (value & BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_MASK) >> BN0_WF_MIB_TOP_TRARC5_AGG_RANG_SEL_11_SHFT;
++	value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC6_ADDR + band_offset);
++	agg_rang_sel[12] = (value & BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_MASK) >> BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_12_SHFT;
++	agg_rang_sel[13] = (value & BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_MASK) >> BN0_WF_MIB_TOP_TRARC6_AGG_RANG_SEL_13_SHFT;
++	value = mt76_rr(dev, BN0_WF_MIB_TOP_TRARC7_ADDR + band_offset);
++	agg_rang_sel[14] = (value & BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_MASK) >> BN0_WF_MIB_TOP_TRARC7_AGG_RANG_SEL_14_SHFT;
++
++	burst_cnt[0] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR0_ADDR + band_offset);
++	burst_cnt[1] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR1_ADDR + band_offset);
++	burst_cnt[2] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR2_ADDR + band_offset);
++	burst_cnt[3] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR3_ADDR + band_offset);
++	burst_cnt[4] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR4_ADDR + band_offset);
++	burst_cnt[5] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR5_ADDR + band_offset);
++	burst_cnt[6] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR6_ADDR + band_offset);
++	burst_cnt[7] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR7_ADDR + band_offset);
++	burst_cnt[8] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR8_ADDR + band_offset);
++	burst_cnt[9] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR9_ADDR + band_offset);
++	burst_cnt[10] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR10_ADDR + band_offset);
++	burst_cnt[11] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR11_ADDR + band_offset);
++	burst_cnt[12] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR12_ADDR + band_offset);
++	burst_cnt[13] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR13_ADDR + band_offset);
++	burst_cnt[14] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR14_ADDR + band_offset);
++	burst_cnt[15] = mt76_rr(dev, BN0_WF_MIB_TOP_TRDR15_ADDR + band_offset);
++
++	start_range = 1;
++	total_burst = 0;
++	total_ampdu = 0;
++	agg_rang_sel[15] = 1023;
++
++	/* Need to add 1 after read from AGG_RANG_SEL CR */
++	for (idx = 0; idx < 16; idx++) {
++		agg_rang_sel[idx]++;
++		total_burst += burst_cnt[idx];
++
++		if (start_range == agg_rang_sel[idx])
++			ampdu_cnt[idx] = (u64) start_range * burst_cnt[idx];
++		else
++			ampdu_cnt[idx] = (u64) ((start_range + agg_rang_sel[idx]) >> 1) * burst_cnt[idx];
++
++		start_range = agg_rang_sel[idx] + 1;
++		total_ampdu += ampdu_cnt[idx];
++	}
++
++	start_range = 1;
++	sprintf(full_str, "%13s ", "Tx Agg Range:");
++
++	for (row_idx = 0; row_idx < 4; row_idx++) {
++		for (col_idx = 0; col_idx < 4; col_idx++, idx++) {
++			idx = 4 * row_idx + col_idx;
++
++			if (start_range == agg_rang_sel[idx])
++				sprintf(partial_str, "%d", agg_rang_sel[idx]);
++			else
++				sprintf(partial_str, "%d~%d", start_range, agg_rang_sel[idx]);
++
++			start_range = agg_rang_sel[idx] + 1;
++			sprintf(full_str + strlen(full_str), "%-11s ", partial_str);
++		}
++
++		idx = 4 * row_idx;
++
++		seq_printf(s, "%s\n", full_str);
++		seq_printf(s, "%13s 0x%-9x 0x%-9x 0x%-9x 0x%-9x\n",
++			row_idx ? "" : "Burst count:",
++			burst_cnt[idx], burst_cnt[idx + 1],
++			burst_cnt[idx + 2], burst_cnt[idx + 3]);
++
++		if (total_burst != 0) {
++			if (row_idx == 0)
++				sprintf(full_str, "%13s ",
++					"Burst ratio:");
++			else
++				sprintf(full_str, "%13s ", "");
++
++			for (col_idx = 0; col_idx < 4; col_idx++) {
++				u64 count = (u64) burst_cnt[idx + col_idx] * 100;
++
++				sprintf(partial_str, "(%llu%%)",
++					div64_u64(count, total_burst));
++				sprintf(full_str + strlen(full_str),
++					"%-11s ", partial_str);
++			}
++
++			seq_printf(s, "%s\n", full_str);
++
++			if (row_idx == 0)
++				sprintf(full_str, "%13s ",
++					"MDPU ratio:");
++			else
++				sprintf(full_str, "%13s ", "");
++
++			for (col_idx = 0; col_idx < 4; col_idx++) {
++				u64 count = ampdu_cnt[idx + col_idx] * 100;
++
++				sprintf(partial_str, "(%llu%%)",
++					div64_u64(count, total_ampdu));
++				sprintf(full_str + strlen(full_str),
++					"%-11s ", partial_str);
++			}
++
++			seq_printf(s, "%s\n", full_str);
++		}
++
++		sprintf(full_str, "%13s ", "");
++	}
++
++	return 0;
++}
++
++static int mt7996_agginfo_read_band0(struct seq_file *s, void *data)
++{
++	mt7996_agginfo_read_per_band(s, MT_BAND0);
++	return 0;
++}
++
++static int mt7996_agginfo_read_band1(struct seq_file *s, void *data)
++{
++	mt7996_agginfo_read_per_band(s, MT_BAND1);
++	return 0;
++}
++
++static int mt7996_agginfo_read_band2(struct seq_file *s, void *data)
++{
++	mt7996_agginfo_read_per_band(s, MT_BAND2);
++	return 0;
++}
++
++/* AMSDU INFO */
++static int mt7996_amsdu_result_read(struct seq_file *s, void *data)
++{
++#define HW_MSDU_CNT_ADDR 0xf400
++#define HW_MSDU_NUM_MAX 33
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	u32 ple_stat[HW_MSDU_NUM_MAX] = {0}, total_amsdu = 0;
++	u8 i;
++
++	for (i = 0; i < HW_MSDU_NUM_MAX; i++)
++		ple_stat[i] = mt76_rr(dev, HW_MSDU_CNT_ADDR + i * 0x04);
++
++	seq_printf(s, "TXD counter status of MSDU:\n");
++
++	for (i = 0; i < HW_MSDU_NUM_MAX; i++)
++		total_amsdu += ple_stat[i];
++
++	for (i = 0; i < HW_MSDU_NUM_MAX; i++) {
++		seq_printf(s, "AMSDU pack count of %d MSDU in TXD: 0x%x ", i, ple_stat[i]);
++		if (total_amsdu != 0)
++			seq_printf(s, "(%d%%)\n", ple_stat[i] * 100 / total_amsdu);
++		else
++			seq_printf(s, "\n");
++	}
++
++	return 0;
++}
++
++/* DBG MODLE */
++static int
++mt7996_fw_debug_module_set(void *data, u64 module)
++{
++	struct mt7996_dev *dev = data;
++
++	dev->dbg.fw_dbg_module = module;
++	return 0;
++}
++
++static int
++mt7996_fw_debug_module_get(void *data, u64 *module)
++{
++	struct mt7996_dev *dev = data;
++
++	*module = dev->dbg.fw_dbg_module;
++	return 0;
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_module, mt7996_fw_debug_module_get,
++			 mt7996_fw_debug_module_set, "%lld\n");
++
++static int
++mt7996_fw_debug_level_set(void *data, u64 level)
++{
++	struct mt7996_dev *dev = data;
++
++	dev->dbg.fw_dbg_lv = level;
++	mt7996_mcu_fw_dbg_ctrl(dev, dev->dbg.fw_dbg_module, dev->dbg.fw_dbg_lv);
++	return 0;
++}
++
++static int
++mt7996_fw_debug_level_get(void *data, u64 *level)
++{
++	struct mt7996_dev *dev = data;
++
++	*level = dev->dbg.fw_dbg_lv;
++	return 0;
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_level, mt7996_fw_debug_level_get,
++			 mt7996_fw_debug_level_set, "%lld\n");
++
++/* usage: echo 0x[arg3][arg2][arg1] > fw_wa_set */
++static int
++mt7996_wa_set(void *data, u64 val)
++{
++	struct mt7996_dev *dev = data;
++	u32 arg1, arg2, arg3;
++
++	arg1 = FIELD_GET(GENMASK_ULL(7, 0), val);
++	arg2 = FIELD_GET(GENMASK_ULL(15, 8), val);
++	arg3 = FIELD_GET(GENMASK_ULL(23, 16), val);
++
++	return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
++				arg1, arg2, arg3);
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_wa_set, NULL, mt7996_wa_set,
++			 "0x%llx\n");
++
++/* usage: echo 0x[arg3][arg2][arg1] > fw_wa_query */
++static int
++mt7996_wa_query(void *data, u64 val)
++{
++	struct mt7996_dev *dev = data;
++	u32 arg1, arg2, arg3;
++
++	arg1 = FIELD_GET(GENMASK_ULL(7, 0), val);
++	arg2 = FIELD_GET(GENMASK_ULL(15, 8), val);
++	arg3 = FIELD_GET(GENMASK_ULL(23, 16), val);
++
++	return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(QUERY),
++				arg1, arg2, arg3);
++	return 0;
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_wa_query, NULL, mt7996_wa_query,
++			 "0x%llx\n");
++
++static int mt7996_dump_version(struct seq_file *s, void *data)
++{
++#define MAX_ADIE_NUM	3
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	u32 regval;
++	u16 adie_chip_id, adie_chip_ver;
++	int adie_idx;
++	static const char * const fem_type[] = {
++		[MT7996_FEM_UNSET] = "N/A",
++		[MT7996_FEM_EXT] = "eFEM",
++		[MT7996_FEM_INT] = "iFEM",
++		[MT7996_FEM_MIX] = "mixed FEM",
++	};
++
++	seq_printf(s, "Version: 4.3.24.7\n");
++
++	if (!test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state))
++		return 0;
++
++	seq_printf(s, "Rom Patch Build Time: %.16s\n", dev->patch_build_date);
++	seq_printf(s, "WM Patch Build Time: %.15s, Mode: %s\n",
++		   dev->ram_build_date[MT7996_RAM_TYPE_WM],
++		   dev->testmode_enable ? "Testmode" : "Normal mode");
++	seq_printf(s, "WA Patch Build Time: %.15s\n",
++		   dev->ram_build_date[MT7996_RAM_TYPE_WA]);
++	seq_printf(s, "DSP Patch Build Time: %.15s\n",
++		   dev->ram_build_date[MT7996_RAM_TYPE_DSP]);
++	for (adie_idx = 0; adie_idx < MAX_ADIE_NUM; adie_idx++) {
++		mt7996_mcu_rf_regval(dev, MT_ADIE_CHIP_ID(adie_idx), &regval, false);
++		adie_chip_id = FIELD_GET(MT_ADIE_CHIP_ID_MASK, regval);
++		adie_chip_ver = FIELD_GET(MT_ADIE_VERSION_MASK, regval);
++		if (adie_chip_id)
++			seq_printf(s, "Adie %d: ID = 0x%04x, Ver = 0x%04x\n",
++				   adie_idx, adie_chip_id, adie_chip_ver);
++		else
++			seq_printf(s, "Adie %d: ID = N/A, Ver = N/A\n", adie_idx);
++	}
++	seq_printf(s, "FEM type: %s\n", fem_type[dev->fem_type]);
++
++	return 0;
++}
++
++/* fw wm call trace info dump */
++void mt7996_show_lp_history(struct seq_file *s, u32 type)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	struct mt7996_crash_data *crash_data;
++	struct mt7996_coredump *dump;
++	u64 now = 0;
++	int i = 0;
++	u8 fw_type = !!type;
++
++	mutex_lock(&dev->dump_mutex);
++
++	crash_data = mt7996_coredump_new(dev, fw_type);
++	if (!crash_data) {
++		mutex_unlock(&dev->dump_mutex);
++		seq_printf(s, "the coredump is disable!\n");
++		return;
++	}
++	mutex_unlock(&dev->dump_mutex);
++
++	dump = mt7996_coredump_build(dev, fw_type, false);
++	if (!dump) {
++		seq_printf(s, "no call stack data found!\n");
++		return;
++	}
++
++	seq_printf(s, "\x1b[32m%s log output\x1b[0m\n", dump->fw_type);
++	seq_printf(s, "\x1b[32mfw status: %s\n", dump->fw_state);
++	/* PC log */
++	now = jiffies;
++	for (i = 0; i < 10; i++)
++		seq_printf(s, "\tCurrent PC=%x\n", dump->pc_cur[i]);
++
++	seq_printf(s, "PC log contorl=0x%x(T=%llu)(latest PC index = 0x%x)\n",
++		dump->pc_dbg_ctrl, now, dump->pc_cur_idx);
++	for (i = 0; i < 32; i++)
++		seq_printf(s, "\tPC log(%d)=0x%08x\n", i, dump->pc_stack[i]);
++
++	/* LR log */
++	now = jiffies;
++	seq_printf(s, "\nLR log contorl=0x%x(T=%llu)(latest LR index = 0x%x)\n",
++		dump->lr_dbg_ctrl, now, dump->lr_cur_idx);
++	for (i = 0; i < 32; i++)
++		seq_printf(s, "\tLR log(%d)=0x%08x\n", i, dump->lr_stack[i]);
++
++	vfree(dump);
++}
++
++static int mt7996_fw_wa_info_read(struct seq_file *s, void *data)
++{
++	seq_printf(s, "======[ShowPcLpHistory]======\n");
++	mt7996_show_lp_history(s, MT7996_RAM_TYPE_WA);
++	seq_printf(s, "======[End ShowPcLpHistory]==\n");
++
++	return 0;
++}
++
++static int mt7996_fw_wm_info_read(struct seq_file *s, void *data)
++{
++	seq_printf(s, "======[ShowPcLpHistory]======\n");
++	mt7996_show_lp_history(s, MT7996_RAM_TYPE_WM);
++	seq_printf(s, "======[End ShowPcLpHistory]==\n");
++
++	return 0;
++}
++
++/* dma info dump */
++static void
++dump_dma_tx_ring_info(struct seq_file *s, struct mt7996_dev *dev,  char *str1, char *str2, u32 ring_base)
++{
++	u32 base, cnt, cidx, didx, queue_cnt;
++
++	base= mt76_rr(dev, ring_base);
++	cnt = mt76_rr(dev, ring_base + 4);
++	cidx = mt76_rr(dev, ring_base + 8);
++	didx = mt76_rr(dev, ring_base + 12);
++	queue_cnt = (cidx >= didx) ? (cidx - didx) : (cidx - didx + cnt);
++
++	seq_printf(s, "%20s %6s %10x %15x %10x %10x %10x\n", str1, str2, base, cnt, cidx, didx, queue_cnt);
++}
++
++static void
++dump_dma_rx_ring_info(struct seq_file *s, struct mt7996_dev *dev,  char *str1, char *str2, u32 ring_base)
++{
++	u32 base, ctrl1, cnt, cidx, didx, queue_cnt;
++
++	base= mt76_rr(dev, ring_base);
++	ctrl1 = mt76_rr(dev, ring_base + 4);
++	cidx = mt76_rr(dev, ring_base + 8) & 0xfff;
++	didx = mt76_rr(dev, ring_base + 12) & 0xfff;
++	cnt = ctrl1 & 0xfff;
++	queue_cnt = (didx > cidx) ? (didx - cidx - 1) : (didx - cidx + cnt - 1);
++
++	seq_printf(s, "%20s %6s %10x %10x(%3x) %10x %10x %10x\n",
++		   str1, str2, base, ctrl1, cnt, cidx, didx, queue_cnt);
++}
++
++static void
++mt7996_show_dma_info(struct seq_file *s, struct mt7996_dev *dev)
++{
++	u32 sys_ctrl[10];
++
++	/* HOST DMA0 information */
++	sys_ctrl[0] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_HOST_INT_STA_ADDR);
++	sys_ctrl[1] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_HOST_INT_ENA_ADDR);
++	sys_ctrl[2] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_ADDR);
++
++	seq_printf(s, "HOST_DMA Configuration\n");
++	seq_printf(s, "%10s %10s %10s %10s %10s %10s\n",
++		"DMA", "IntCSR", "IntMask", "Glocfg", "Tx/RxEn", "Tx/RxBusy");
++	seq_printf(s, "%10s %10x %10x %10x %4x/%5x %4x/%5x\n",
++		"DMA0", sys_ctrl[0], sys_ctrl[1], sys_ctrl[2],
++		(sys_ctrl[2] & WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_MASK)
++			>> WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_SHFT,
++		(sys_ctrl[2] & WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_MASK)
++			>> WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_SHFT,
++		(sys_ctrl[2] & WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK)
++			>> WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT,
++		(sys_ctrl[2] & WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK)
++			>> WF_WFDMA_HOST_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT);
++
++	if (dev->hif2) {
++		/* HOST DMA1 information */
++		sys_ctrl[0] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_PCIE1_HOST_INT_STA_ADDR);
++		sys_ctrl[1] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_PCIE1_HOST_INT_ENA_ADDR);
++		sys_ctrl[2] = mt76_rr(dev, WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_ADDR);
++
++		seq_printf(s, "%10s %10x %10x %10x %4x/%5x %4x/%5x\n",
++			"DMA0P1", sys_ctrl[0], sys_ctrl[1], sys_ctrl[2],
++			(sys_ctrl[2] & WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_EN_MASK)
++				>> WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_EN_SHFT,
++			(sys_ctrl[2] & WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_EN_MASK)
++				>> WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_EN_SHFT,
++			(sys_ctrl[2] & WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK)
++				>> WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT,
++			(sys_ctrl[2] & WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK)
++				>> WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT);
++	}
++
++	seq_printf(s, "HOST_DMA0 Ring Configuration\n");
++	seq_printf(s, "%20s %6s %10s %15s %10s %10s %10s\n",
++		"Name", "Used", "Base", "Ctrl1(Cnt)", "CIDX", "DIDX", "QCnt");
++	dump_dma_tx_ring_info(s, dev, "T0:TXD0(H2MAC)", "STA",
++		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING0_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T1:TXD1(H2MAC)", "STA",
++		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING1_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T2:TXD2(H2MAC)", "STA",
++		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING2_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T3:", "STA",
++		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING3_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T4:", "STA",
++		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING4_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T5:", "STA",
++		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING5_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T6:", "STA",
++		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING6_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T16:FWDL", "Both",
++		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T17:Cmd(H2WM)", "Both",
++		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T18:TXD0(H2WA)", "AP",
++		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T19:TXD1(H2WA)", "AP",
++		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T20:Cmd(H2WA)", "AP",
++		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T21:TXD2(H2WA)", "AP",
++		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T22:TXD3(H2WA)", "AP",
++		WF_WFDMA_HOST_DMA0_WPDMA_TX_RING22_CTRL0_ADDR);
++
++
++	dump_dma_rx_ring_info(s, dev, "R0:Event(WM2H)", "Both",
++		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R1:Event(WA2H)", "AP",
++		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R2:TxDone0(WA2H)", "AP",
++		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R3:TxDone1(WA2H)", "AP",
++		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING3_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R4:Data0(MAC2H)", "Both",
++		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R5:Data1(MAC2H)", "Both",
++		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
++		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R7:TxDone1(MAC2H)", "Both",
++		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R8:BUF0(MAC2H)", "Both",
++		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R9:TxDone0(MAC2H)", "Both",
++		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R10:MSDU_PG0(MAC2H)", "Both",
++		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R11:MSDU_PG1(MAC2H)", "Both",
++		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING11_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R12:MSDU_PG2(MAC2H)", "Both",
++		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING12_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "IND:IND_CMD(MAC2H)", "Both",
++		WF_RRO_TOP_IND_CMD_0_CTRL0_ADDR);
++
++	if (dev->hif2) {
++		seq_printf(s, "HOST_DMA0 PCIe1 Ring Configuration\n");
++		seq_printf(s, "%20s %6s %10s %15s %10s %10s %10s\n",
++			"Name", "Used", "Base", "Ctrl1(Cnt)", "CIDX", "DIDX", "QCnt");
++		dump_dma_tx_ring_info(s, dev, "T21:TXD2(H2WA)", "AP",
++			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL0_ADDR);
++		dump_dma_tx_ring_info(s, dev, "T22:TXD?(H2WA)", "AP",
++			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL0_ADDR);
++
++		dump_dma_rx_ring_info(s, dev, "R3:TxDone1(WA2H)", "AP",
++			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL0_ADDR);
++		dump_dma_rx_ring_info(s, dev, "R5:Data1(MAC2H)", "Both",
++			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL0_ADDR);
++		dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
++			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL0_ADDR);
++		dump_dma_rx_ring_info(s, dev, "R7:TxDone1(MAC2H)", "Both",
++			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL0_ADDR);
++	}
++
++	/* MCU DMA information */
++	sys_ctrl[0] = mt76_rr(dev, WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_ADDR);
++	sys_ctrl[1] = mt76_rr(dev, WF_WFDMA_MCU_DMA0_HOST_INT_STA_ADDR);
++	sys_ctrl[2] = mt76_rr(dev, WF_WFDMA_MCU_DMA0_HOST_INT_ENA_ADDR);
++
++	seq_printf(s, "MCU_DMA Configuration\n");
++	seq_printf(s, "%10s %10s %10s %10s %10s %10s\n",
++		"DMA", "IntCSR", "IntMask", "Glocfg", "Tx/RxEn", "Tx/RxBusy");
++	seq_printf(s, "%10s %10x %10x %10x %4x/%5x %4x/%5x\n",
++		"DMA0", sys_ctrl[1], sys_ctrl[2], sys_ctrl[0],
++		(sys_ctrl[0] & WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_MASK)
++			>> WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_EN_SHFT,
++		(sys_ctrl[0] & WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_MASK)
++			>> WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_EN_SHFT,
++		(sys_ctrl[0] & WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK)
++			>> WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT,
++		(sys_ctrl[0] & WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK)
++			>> WF_WFDMA_MCU_DMA0_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT);
++
++	seq_printf(s, "MCU_DMA0 Ring Configuration\n");
++	seq_printf(s, "%20s %6s %10s %15s %10s %10s %10s\n",
++		"Name", "Used", "Base", "Cnt", "CIDX", "DIDX", "QCnt");
++	dump_dma_tx_ring_info(s, dev, "T0:Event(WM2H)", "Both",
++		WF_WFDMA_MCU_DMA0_WPDMA_TX_RING0_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T1:Event(WA2H)", "AP",
++		WF_WFDMA_MCU_DMA0_WPDMA_TX_RING1_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T2:TxDone0(WA2H)", "AP",
++		WF_WFDMA_MCU_DMA0_WPDMA_TX_RING2_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T3:TxDone1(WA2H)", "AP",
++		WF_WFDMA_MCU_DMA0_WPDMA_TX_RING3_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T4:TXD(WM2MAC)", "Both",
++		WF_WFDMA_MCU_DMA0_WPDMA_TX_RING4_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T5:TXCMD(WM2MAC)", "Both",
++		WF_WFDMA_MCU_DMA0_WPDMA_TX_RING5_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T6:TXD(WA2MAC)", "AP",
++		WF_WFDMA_MCU_DMA0_WPDMA_TX_RING6_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R0:FWDL", "Both",
++		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING0_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R1:Cmd(H2WM)", "Both",
++		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING1_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R2:TXD0(H2WA)", "AP",
++		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING2_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R3:TXD1(H2WA)", "AP",
++		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING3_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R4:Cmd(H2WA)", "AP",
++		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING4_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R5:Data0(MAC2WM)", "Both",
++		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING5_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R6:TxDone(MAC2WM)", "Both",
++		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R7:SPL/RPT(MAC2WM)", "Both",
++		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING7_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R8:TxDone(MAC2WA)", "AP",
++		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING8_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R9:Data1(MAC2WM)", "Both",
++		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R10:TXD2(H2WA)", "AP",
++		WF_WFDMA_MCU_DMA0_WPDMA_RX_RING10_CTRL0_ADDR);
++
++	/* MEM DMA information */
++	sys_ctrl[0] = mt76_rr(dev, WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_ADDR);
++	sys_ctrl[1] = mt76_rr(dev, WF_WFDMA_MEM_DMA_HOST_INT_STA_ADDR);
++	sys_ctrl[2] = mt76_rr(dev, WF_WFDMA_MEM_DMA_HOST_INT_ENA_ADDR);
++
++	seq_printf(s, "MEM_DMA Configuration\n");
++	seq_printf(s, "%10s %10s %10s %10s %10s %10s\n",
++		"DMA", "IntCSR", "IntMask", "Glocfg", "Tx/RxEn", "Tx/RxBusy");
++	seq_printf(s, "%10s %10x %10x %10x %4x/%5x %4x/%5x\n",
++		"MEM", sys_ctrl[1], sys_ctrl[2], sys_ctrl[0],
++		(sys_ctrl[0] & WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_MASK)
++			>> WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_EN_SHFT,
++		(sys_ctrl[0] & WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_MASK)
++			>> WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_EN_SHFT,
++		(sys_ctrl[0] & WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_MASK)
++			>> WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_TX_DMA_BUSY_SHFT,
++		(sys_ctrl[0] & WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_MASK)
++			>> WF_WFDMA_MEM_DMA_WPDMA_GLO_CFG_RX_DMA_BUSY_SHFT);
++
++	seq_printf(s, "MEM_DMA Ring Configuration\n");
++	seq_printf(s, "%20s %6s %10s %10s %10s %10s %10s\n",
++		"Name", "Used", "Base", "Cnt", "CIDX", "DIDX", "QCnt");
++	dump_dma_tx_ring_info(s, dev, "T0:CmdEvent(WM2WA)", "AP",
++		WF_WFDMA_MEM_DMA_WPDMA_TX_RING0_CTRL0_ADDR);
++	dump_dma_tx_ring_info(s, dev, "T1:CmdEvent(WA2WM)", "AP",
++		WF_WFDMA_MEM_DMA_WPDMA_TX_RING1_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R0:CmdEvent(WM2WA)", "AP",
++		WF_WFDMA_MEM_DMA_WPDMA_RX_RING0_CTRL0_ADDR);
++	dump_dma_rx_ring_info(s, dev, "R1:CmdEvent(WA2WM)", "AP",
++		WF_WFDMA_MEM_DMA_WPDMA_RX_RING1_CTRL0_ADDR);
++}
++
++static int mt7996_trinfo_read(struct seq_file *s, void *data)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	mt7996_show_dma_info(s, dev);
++	return 0;
++}
++
++/* MIB INFO */
++static int mt7996_mibinfo_read_per_band(struct seq_file *s, int band_idx)
++{
++#define BSS_NUM	4
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	u8 bss_nums = BSS_NUM;
++	u32 idx;
++	u32 mac_val, band_offset = 0, band_offset_umib = 0;
++	u32 msdr6, msdr9, msdr18;
++	u32 rvsr0, rscr26, rscr35, mctr5, mctr6, msr0, msr1, msr2;
++	u32 tbcr0, tbcr1, tbcr2, tbcr3, tbcr4;
++	u32 btscr[7];
++	u32 tdrcr[5];
++	u32 mbtocr[16], mbtbcr[16], mbrocr[16], mbrbcr[16];
++	u32 btcr, btbcr, brocr, brbcr, btdcr, brdcr;
++	u32 mu_cnt[5];
++	u32 ampdu_cnt[3];
++	u64 per;
++
++	switch (band_idx) {
++	case 0:
++		band_offset = 0;
++		band_offset_umib = 0;
++		break;
++	case 1:
++		band_offset = BN1_WF_MIB_TOP_BASE - BN0_WF_MIB_TOP_BASE;
++		band_offset_umib = WF_UMIB_TOP_B1BROCR_ADDR - WF_UMIB_TOP_B0BROCR_ADDR;
++		break;
++	case 2:
++		band_offset = IP1_BN0_WF_MIB_TOP_BASE - BN0_WF_MIB_TOP_BASE;
++		band_offset_umib = WF_UMIB_TOP_B2BROCR_ADDR - WF_UMIB_TOP_B0BROCR_ADDR;
++		break;
++	default:
++		return true;
++	}
++
++	seq_printf(s, "Band %d MIB Status\n", band_idx);
++	seq_printf(s, "===============================\n");
++	mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_M0SCR0_ADDR + band_offset);
++	seq_printf(s, "MIB Status Control=0x%x\n", mac_val);
++
++	msdr6 = mt76_rr(dev, BN0_WF_MIB_TOP_M0SDR6_ADDR + band_offset);
++	rvsr0 = mt76_rr(dev, BN0_WF_MIB_TOP_RVSR0_ADDR + band_offset);
++	rscr35 = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR35_ADDR + band_offset);
++	msdr9 = mt76_rr(dev, BN0_WF_MIB_TOP_M0SDR9_ADDR + band_offset);
++	rscr26 = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR26_ADDR + band_offset);
++	mctr5 = mt76_rr(dev, BN0_WF_MIB_TOP_MCTR5_ADDR + band_offset);
++	mctr6 = mt76_rr(dev, BN0_WF_MIB_TOP_MCTR6_ADDR + band_offset);
++	msdr18 = mt76_rr(dev, BN0_WF_MIB_TOP_M0SDR18_ADDR + band_offset);
++	msr0 = mt76_rr(dev, BN0_WF_MIB_TOP_MSR0_ADDR + band_offset);
++	msr1 = mt76_rr(dev, BN0_WF_MIB_TOP_MSR1_ADDR + band_offset);
++	msr2 = mt76_rr(dev, BN0_WF_MIB_TOP_MSR2_ADDR + band_offset);
++	ampdu_cnt[0] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR0_ADDR + band_offset);
++	ampdu_cnt[1] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR3_ADDR + band_offset);
++	ampdu_cnt[2] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR4_ADDR + band_offset);
++	ampdu_cnt[1] &= BN0_WF_MIB_TOP_TSCR3_AMPDU_MPDU_COUNT_MASK;
++	ampdu_cnt[2] &= BN0_WF_MIB_TOP_TSCR4_AMPDU_ACKED_COUNT_MASK;
++
++	seq_printf(s, "===Phy/Timing Related Counters===\n");
++	seq_printf(s, "\tChannelIdleCnt=0x%x\n",
++		msdr6 & BN0_WF_MIB_TOP_M0SDR6_CHANNEL_IDLE_COUNT_MASK);
++	seq_printf(s, "\tCCA_NAV_Tx_Time=0x%x\n",
++		msdr9 & BN0_WF_MIB_TOP_M0SDR9_CCA_NAV_TX_TIME_MASK);
++	seq_printf(s, "\tRx_MDRDY_CNT=0x%x\n",
++		rscr26 & BN0_WF_MIB_TOP_RSCR26_RX_MDRDY_COUNT_MASK);
++	seq_printf(s, "\tCCK_MDRDY_TIME=0x%x, OFDM_MDRDY_TIME=0x%x",
++		msr0 & BN0_WF_MIB_TOP_MSR0_CCK_MDRDY_TIME_MASK,
++		msr1 & BN0_WF_MIB_TOP_MSR1_OFDM_LG_MIXED_VHT_MDRDY_TIME_MASK);
++	seq_printf(s, ", OFDM_GREEN_MDRDY_TIME=0x%x\n",
++		msr2 & BN0_WF_MIB_TOP_MSR2_OFDM_GREEN_MDRDY_TIME_MASK);
++	seq_printf(s, "\tPrim CCA Time=0x%x\n",
++		mctr5 & BN0_WF_MIB_TOP_MCTR5_P_CCA_TIME_MASK);
++	seq_printf(s, "\tSec CCA Time=0x%x\n",
++		mctr6 & BN0_WF_MIB_TOP_MCTR6_S_CCA_TIME_MASK);
++	seq_printf(s, "\tPrim ED Time=0x%x\n",
++		msdr18 & BN0_WF_MIB_TOP_M0SDR18_P_ED_TIME_MASK);
++
++	seq_printf(s, "===Tx Related Counters(Generic)===\n");
++	mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR18_ADDR + band_offset);
++	dev->dbg.bcn_total_cnt[band_idx] +=
++		(mac_val & BN0_WF_MIB_TOP_TSCR18_BEACONTXCOUNT_MASK);
++	seq_printf(s, "\tBeaconTxCnt=0x%x\n", dev->dbg.bcn_total_cnt[band_idx]);
++	dev->dbg.bcn_total_cnt[band_idx] = 0;
++
++	tbcr0 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR0_ADDR + band_offset);
++	seq_printf(s, "\tTx 20MHz Cnt=0x%x\n",
++		tbcr0 & BN0_WF_MIB_TOP_TBCR0_TX_20MHZ_CNT_MASK);
++	tbcr1 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR1_ADDR + band_offset);
++	seq_printf(s, "\tTx 40MHz Cnt=0x%x\n",
++		tbcr1 & BN0_WF_MIB_TOP_TBCR1_TX_40MHZ_CNT_MASK);
++	tbcr2 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR2_ADDR + band_offset);
++	seq_printf(s, "\tTx 80MHz Cnt=0x%x\n",
++		tbcr2 & BN0_WF_MIB_TOP_TBCR2_TX_80MHZ_CNT_MASK);
++	tbcr3 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR3_ADDR + band_offset);
++	seq_printf(s, "\tTx 160MHz Cnt=0x%x\n",
++		tbcr3 & BN0_WF_MIB_TOP_TBCR3_TX_160MHZ_CNT_MASK);
++	tbcr4 = mt76_rr(dev, BN0_WF_MIB_TOP_TBCR4_ADDR + band_offset);
++	seq_printf(s, "\tTx 320MHz Cnt=0x%x\n",
++		tbcr4 & BN0_WF_MIB_TOP_TBCR4_TX_320MHZ_CNT_MASK);
++	seq_printf(s, "\tAMPDU Cnt=0x%x\n", ampdu_cnt[0]);
++	seq_printf(s, "\tAMPDU MPDU Cnt=0x%x\n", ampdu_cnt[1]);
++	seq_printf(s, "\tAMPDU MPDU Ack Cnt=0x%x\n", ampdu_cnt[2]);
++	per = (ampdu_cnt[2] == 0 ?
++		0 : 1000 * (ampdu_cnt[1] - ampdu_cnt[2]) / ampdu_cnt[1]);
++	seq_printf(s, "\tAMPDU MPDU PER=%llu.%1llu%%\n", per / 10, per % 10);
++
++	seq_printf(s, "===MU Related Counters===\n");
++	mu_cnt[0] = mt76_rr(dev, BN0_WF_MIB_TOP_BSCR2_ADDR + band_offset);
++	mu_cnt[1] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR5_ADDR + band_offset);
++	mu_cnt[2] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR6_ADDR + band_offset);
++	mu_cnt[3] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR8_ADDR + band_offset);
++	mu_cnt[4] = mt76_rr(dev, BN0_WF_MIB_TOP_TSCR7_ADDR + band_offset);
++
++	seq_printf(s, "\tMUBF_TX_COUNT=0x%x\n",
++		mu_cnt[0] & BN0_WF_MIB_TOP_BSCR2_MUBF_TX_COUNT_MASK);
++	seq_printf(s, "\tMU_TX_MPDU_COUNT(Ok+Fail)=0x%x\n", mu_cnt[1]);
++	seq_printf(s, "\tMU_TX_OK_MPDU_COUNT=0x%x\n", mu_cnt[2]);
++	seq_printf(s, "\tMU_TO_MU_FAIL_PPDU_COUNT=0x%x\n", mu_cnt[3]);
++	seq_printf(s, "\tSU_TX_OK_MPDU_COUNT=0x%x\n", mu_cnt[4]);
++
++	seq_printf(s, "===Rx Related Counters(Generic)===\n");
++	seq_printf(s, "\tVector Mismacth Cnt=0x%x\n",
++		rvsr0 & BN0_WF_MIB_TOP_RVSR0_VEC_MISS_COUNT_MASK);
++	seq_printf(s, "\tDelimiter Fail Cnt=0x%x\n",
++		rscr35 & BN0_WF_MIB_TOP_RSCR35_DELIMITER_FAIL_COUNT_MASK);
++
++	mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR1_ADDR + band_offset);
++	seq_printf(s, "\tRxFCSErrCnt=0x%x\n",
++		(mac_val & BN0_WF_MIB_TOP_RSCR1_RX_FCS_ERROR_COUNT_MASK));
++	mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR33_ADDR + band_offset);
++	seq_printf(s, "\tRxFifoFullCnt=0x%x\n",
++		(mac_val & BN0_WF_MIB_TOP_RSCR33_RX_FIFO_FULL_COUNT_MASK));
++	mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR36_ADDR + band_offset);
++	seq_printf(s, "\tRxLenMismatch=0x%x\n",
++		(mac_val & BN0_WF_MIB_TOP_RSCR36_RX_LEN_MISMATCH_MASK));
++	mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR31_ADDR + band_offset);
++	seq_printf(s, "\tRxMPDUCnt=0x%x\n",
++		(mac_val & BN0_WF_MIB_TOP_RSCR31_RX_MPDU_COUNT_MASK));
++	mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR27_ADDR + band_offset);
++	seq_printf(s, "\tRx AMPDU Cnt=0x%x\n", mac_val);
++	mac_val = mt76_rr(dev, BN0_WF_MIB_TOP_RSCR28_ADDR + band_offset);
++	seq_printf(s, "\tRx Total ByteCnt=0x%x\n", mac_val);
++
++
++	/* Per-BSS T/RX Counters */
++	seq_printf(s, "===Per-BSS Related Tx/Rx Counters===\n");
++	seq_printf(s, "BSS Idx TxCnt/DataCnt TxByteCnt RxOkCnt/DataCnt RxByteCnt\n");
++	for (idx = 0; idx < bss_nums; idx++) {
++		btcr = mt76_rr(dev, BN0_WF_MIB_TOP_BTCR_ADDR + band_offset + idx * 4);
++		btdcr = mt76_rr(dev, BN0_WF_MIB_TOP_BTDCR_ADDR + band_offset + idx * 4);
++		btbcr = mt76_rr(dev, BN0_WF_MIB_TOP_BTBCR_ADDR + band_offset + idx * 4);
++
++		brocr = mt76_rr(dev, WF_UMIB_TOP_B0BROCR_ADDR + band_offset_umib + idx * 4);
++		brdcr = mt76_rr(dev, WF_UMIB_TOP_B0BRDCR_ADDR + band_offset_umib + idx * 4);
++		brbcr = mt76_rr(dev, WF_UMIB_TOP_B0BRBCR_ADDR + band_offset_umib + idx * 4);
++
++		seq_printf(s, "%d\t 0x%x/0x%x\t 0x%x \t 0x%x/0x%x \t 0x%x\n",
++			idx, btcr, btdcr, btbcr, brocr, brdcr, brbcr);
++	}
++
++	seq_printf(s, "===Per-BSS Related MIB Counters===\n");
++	seq_printf(s, "BSS Idx RTSTx/RetryCnt BAMissCnt AckFailCnt FrmRetry1/2/3Cnt\n");
++
++	/* Per-BSS TX Status */
++	for (idx = 0; idx < bss_nums; idx++) {
++		btscr[0] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR5_ADDR + band_offset + idx * 4);
++		btscr[1] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR6_ADDR + band_offset + idx * 4);
++		btscr[2] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR0_ADDR + band_offset + idx * 4);
++		btscr[3] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR1_ADDR + band_offset + idx * 4);
++		btscr[4] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR2_ADDR + band_offset + idx * 4);
++		btscr[5] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR3_ADDR + band_offset + idx * 4);
++		btscr[6] = mt76_rr(dev, BN0_WF_MIB_TOP_BTSCR4_ADDR + band_offset + idx * 4);
++
++		seq_printf(s, "%d:\t0x%x/0x%x  0x%x \t 0x%x \t  0x%x/0x%x/0x%x\n",
++			idx, (btscr[0] & BN0_WF_MIB_TOP_BTSCR5_RTSTXCOUNTn_MASK),
++			(btscr[1] & BN0_WF_MIB_TOP_BTSCR6_RTSRETRYCOUNTn_MASK),
++			(btscr[2] & BN0_WF_MIB_TOP_BTSCR0_BAMISSCOUNTn_MASK),
++			(btscr[3] & BN0_WF_MIB_TOP_BTSCR1_ACKFAILCOUNTn_MASK),
++			(btscr[4] & BN0_WF_MIB_TOP_BTSCR2_FRAMERETRYCOUNTn_MASK),
++			(btscr[5] & BN0_WF_MIB_TOP_BTSCR3_FRAMERETRY2COUNTn_MASK),
++			(btscr[6] & BN0_WF_MIB_TOP_BTSCR4_FRAMERETRY3COUNTn_MASK));
++	}
++
++	/* Dummy delimiter insertion result */
++	seq_printf(s, "===Dummy delimiter insertion result===\n");
++	tdrcr[0] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR0_ADDR + band_offset);
++	tdrcr[1] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR1_ADDR + band_offset);
++	tdrcr[2] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR2_ADDR + band_offset);
++	tdrcr[3] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR3_ADDR + band_offset);
++	tdrcr[4] = mt76_rr(dev, BN0_WF_MIB_TOP_TDRCR4_ADDR + band_offset);
++
++	seq_printf(s, "Range0 = %d\t Range1 = %d\t Range2 = %d\t Range3 = %d\t Range4 = %d\n",
++		tdrcr[0],
++		tdrcr[1],
++		tdrcr[2],
++		tdrcr[3],
++		tdrcr[4]);
++
++	/* Per-MBSS T/RX Counters */
++	seq_printf(s, "===Per-MBSS Related Tx/Rx Counters===\n");
++	seq_printf(s, "MBSSIdx   TxOkCnt  TxByteCnt  RxOkCnt  RxByteCnt\n");
++
++	for (idx = 0; idx < 16; idx++) {
++		mbtocr[idx] = mt76_rr(dev, BN0_WF_MIB_TOP_BTOCR_ADDR + band_offset + (bss_nums + idx) * 4);
++		mbtbcr[idx] = mt76_rr(dev, BN0_WF_MIB_TOP_BTBCR_ADDR + band_offset + (bss_nums + idx) * 4);
++
++		mbrocr[idx] = mt76_rr(dev, WF_UMIB_TOP_B0BROCR_ADDR + band_offset_umib + (bss_nums + idx) * 4);
++		mbrbcr[idx] = mt76_rr(dev, WF_UMIB_TOP_B0BRBCR_ADDR + band_offset_umib + (bss_nums + idx) * 4);
++	}
++
++	for (idx = 0; idx < 16; idx++) {
++		seq_printf(s, "%d\t 0x%x\t 0x%x \t 0x%x \t 0x%x\n",
++			idx, mbtocr[idx], mbtbcr[idx], mbrocr[idx], mbrbcr[idx]);
++	}
++
++	return 0;
++}
++
++static int mt7996_mibinfo_band0(struct seq_file *s, void *data)
++{
++	mt7996_mibinfo_read_per_band(s, MT_BAND0);
++	return 0;
++}
++
++static int mt7996_mibinfo_band1(struct seq_file *s, void *data)
++{
++	mt7996_mibinfo_read_per_band(s, MT_BAND1);
++	return 0;
++}
++
++static int mt7996_mibinfo_band2(struct seq_file *s, void *data)
++{
++	mt7996_mibinfo_read_per_band(s, MT_BAND2);
++	return 0;
++}
++
++/* WTBL INFO */
++static int
++mt7996_wtbl_read_raw(struct mt7996_dev *dev, u16 idx,
++		     enum mt7996_wtbl_type type, u16 start_dw,
++		     u16 len, void *buf)
++{
++	u32 *dest_cpy = (u32 *)buf;
++	u32 size_dw = len;
++	u32 src = 0;
++
++	if (!buf)
++		return 0xFF;
++
++	if (type == WTBL_TYPE_LMAC) {
++		mt76_wr(dev, MT_DBG_WTBLON_TOP_WDUCR_ADDR,
++			FIELD_PREP(MT_DBG_WTBLON_TOP_WDUCR_GROUP, (idx >> 7)));
++		src = LWTBL_IDX2BASE(idx, start_dw);
++	} else if (type == WTBL_TYPE_UMAC) {
++		mt76_wr(dev,  MT_DBG_UWTBL_TOP_WDUCR_ADDR,
++			FIELD_PREP(MT_DBG_UWTBL_TOP_WDUCR_GROUP, (idx >> 7)));
++		src = UWTBL_IDX2BASE(idx, start_dw);
++	} else if (type == WTBL_TYPE_KEY) {
++		mt76_wr(dev,  MT_DBG_UWTBL_TOP_WDUCR_ADDR,
++			MT_DBG_UWTBL_TOP_WDUCR_TARGET |
++			FIELD_PREP(MT_DBG_UWTBL_TOP_WDUCR_GROUP, (idx >> 7)));
++		src = KEYTBL_IDX2BASE(idx, start_dw);
++	}
++
++	while (size_dw--) {
++		*dest_cpy++ = mt76_rr(dev, src);
++		src += 4;
++	};
++
++	return 0;
++}
++
++#if 0
++static int
++mt7996_wtbl_write_raw(struct mt7996_dev *dev, u16 idx,
++			  enum mt7996_wtbl_type type, u16 start_dw,
++			  u32 val)
++{
++	u32 addr = 0;
++
++	if (type == WTBL_TYPE_LMAC) {
++		mt76_wr(dev, MT_DBG_WTBLON_TOP_WDUCR_ADDR,
++			FIELD_PREP(MT_DBG_WTBLON_TOP_WDUCR_GROUP, (idx >> 7)));
++		addr = LWTBL_IDX2BASE(idx, start_dw);
++	} else if (type == WTBL_TYPE_UMAC) {
++		mt76_wr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR,
++			FIELD_PREP(MT_DBG_UWTBL_TOP_WDUCR_GROUP, (idx >> 7)));
++		addr = UWTBL_IDX2BASE(idx, start_dw);
++	} else if (type == WTBL_TYPE_KEY) {
++		mt76_wr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR,
++			MT_DBG_UWTBL_TOP_WDUCR_TARGET |
++			FIELD_PREP(MT_DBG_UWTBL_TOP_WDUCR_GROUP, (idx >> 7)));
++		addr = KEYTBL_IDX2BASE(idx, start_dw);
++	}
++
++	mt76_wr(dev, addr, val);
++
++	return 0;
++}
++#endif
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW0[] = {
++	{"MUAR_IDX",    WF_LWTBL_MUAR_MASK, WF_LWTBL_MUAR_SHIFT,false},
++	{"RCA1",        WF_LWTBL_RCA1_MASK, NO_SHIFT_DEFINE,	false},
++	{"KID",         WF_LWTBL_KID_MASK,  WF_LWTBL_KID_SHIFT,	false},
++	{"RCID",        WF_LWTBL_RCID_MASK, NO_SHIFT_DEFINE,	false},
++	{"BAND",        WF_LWTBL_BAND_MASK, WF_LWTBL_BAND_SHIFT,false},
++	{"RV",          WF_LWTBL_RV_MASK,   NO_SHIFT_DEFINE,	false},
++	{"RCA2",        WF_LWTBL_RCA2_MASK, NO_SHIFT_DEFINE,	false},
++	{"WPI_FLAG",    WF_LWTBL_WPI_FLAG_MASK, NO_SHIFT_DEFINE,true},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw0_1(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LinkAddr: %02x:%02x:%02x:%02x:%02x:%02x(D0[B0~15], D1[B0~31])\n",
++		lwtbl[4], lwtbl[5], lwtbl[6], lwtbl[7], lwtbl[0], lwtbl[1]);
++
++	/* LMAC WTBL DW 0 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 0/1\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_PEER_INFO_DW_0*4]);
++	dw_value = *addr;
++
++	while (WTBL_LMAC_DW0[i].name) {
++
++		if (WTBL_LMAC_DW0[i].shift == NO_SHIFT_DEFINE)
++			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW0[i].name,
++					 (dw_value & WTBL_LMAC_DW0[i].mask) ? 1 : 0);
++		else
++			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW0[i].name,
++					  (dw_value & WTBL_LMAC_DW0[i].mask) >> WTBL_LMAC_DW0[i].shift);
++		i++;
++	}
++}
++
++static const struct berse_wtbl_parse *WTBL_LMAC_DW2;
++static const struct berse_wtbl_parse WTBL_LMAC_DW2_7996[] = {
++	{"AID",                 WF_LWTBL_AID_MASK,              WF_LWTBL_AID_SHIFT,	false},
++	{"GID_SU",              WF_LWTBL_GID_SU_MASK,           NO_SHIFT_DEFINE,	false},
++	{"SPP_EN",              WF_LWTBL_SPP_EN_MASK,           NO_SHIFT_DEFINE,	false},
++	{"WPI_EVEN",            WF_LWTBL_WPI_EVEN_MASK,         NO_SHIFT_DEFINE,	false},
++	{"AAD_OM",              WF_LWTBL_AAD_OM_MASK,           NO_SHIFT_DEFINE,	false},
++	{"CIPHER_PGTK",WF_LWTBL_CIPHER_SUIT_PGTK_MASK, WF_LWTBL_CIPHER_SUIT_PGTK_SHIFT,	true},
++	{"FROM_DS",             WF_LWTBL_FD_MASK,               NO_SHIFT_DEFINE,	false},
++	{"TO_DS",               WF_LWTBL_TD_MASK,               NO_SHIFT_DEFINE,	false},
++	{"SW",                  WF_LWTBL_SW_MASK,               NO_SHIFT_DEFINE,	false},
++	{"UL",                  WF_LWTBL_UL_MASK,               NO_SHIFT_DEFINE,	false},
++	{"TX_POWER_SAVE",       WF_LWTBL_TX_PS_MASK,            NO_SHIFT_DEFINE,	true},
++	{"QOS",                 WF_LWTBL_QOS_MASK,              NO_SHIFT_DEFINE,	false},
++	{"HT",                  WF_LWTBL_HT_MASK,               NO_SHIFT_DEFINE,	false},
++	{"VHT",                 WF_LWTBL_VHT_MASK,              NO_SHIFT_DEFINE,	false},
++	{"HE",                  WF_LWTBL_HE_MASK,               NO_SHIFT_DEFINE,	false},
++	{"EHT",                 WF_LWTBL_EHT_MASK,              NO_SHIFT_DEFINE,	false},
++	{"MESH",                WF_LWTBL_MESH_MASK,             NO_SHIFT_DEFINE,	true},
++	{NULL,}
++};
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW2_7992[] = {
++	{"AID",                 WF_LWTBL_AID_MASK,              WF_LWTBL_AID_SHIFT,	false},
++	{"GID_SU",              WF_LWTBL_GID_SU_MASK,           NO_SHIFT_DEFINE,	false},
++	{"DUAL_PTEC_EN",        WF_LWTBL_DUAL_PTEC_EN_MASK,     NO_SHIFT_DEFINE,	false},
++	{"DUAL_CTS_CAP",        WF_LWTBL_DUAL_CTS_CAP_MASK,     NO_SHIFT_DEFINE,	false},
++	{"CIPHER_PGTK",WF_LWTBL_CIPHER_SUIT_PGTK_MASK, WF_LWTBL_CIPHER_SUIT_PGTK_SHIFT,	true},
++	{"FROM_DS",             WF_LWTBL_FD_MASK,               NO_SHIFT_DEFINE,	false},
++	{"TO_DS",               WF_LWTBL_TD_MASK,               NO_SHIFT_DEFINE,	false},
++	{"SW",                  WF_LWTBL_SW_MASK,               NO_SHIFT_DEFINE,	false},
++	{"UL",                  WF_LWTBL_UL_MASK,               NO_SHIFT_DEFINE,	false},
++	{"TX_POWER_SAVE",       WF_LWTBL_TX_PS_MASK,            NO_SHIFT_DEFINE,	true},
++	{"QOS",                 WF_LWTBL_QOS_MASK,              NO_SHIFT_DEFINE,	false},
++	{"HT",                  WF_LWTBL_HT_MASK,               NO_SHIFT_DEFINE,	false},
++	{"VHT",                 WF_LWTBL_VHT_MASK,              NO_SHIFT_DEFINE,	false},
++	{"HE",                  WF_LWTBL_HE_MASK,               NO_SHIFT_DEFINE,	false},
++	{"EHT",                 WF_LWTBL_EHT_MASK,              NO_SHIFT_DEFINE,	false},
++	{"MESH",                WF_LWTBL_MESH_MASK,             NO_SHIFT_DEFINE,	true},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw2(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	/* LMAC WTBL DW 2 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 2\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_2*4]);
++	dw_value = *addr;
++
++	while (WTBL_LMAC_DW2[i].name) {
++
++		if (WTBL_LMAC_DW2[i].shift == NO_SHIFT_DEFINE)
++			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW2[i].name,
++					 (dw_value & WTBL_LMAC_DW2[i].mask) ? 1 : 0);
++		else
++			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW2[i].name,
++					  (dw_value & WTBL_LMAC_DW2[i].mask) >> WTBL_LMAC_DW2[i].shift);
++		i++;
++	}
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW3[] = {
++	{"WMM_Q",           WF_LWTBL_WMM_Q_MASK,		WF_LWTBL_WMM_Q_SHIFT,		false},
++	{"EHT_SIG_MCS",     WF_LWTBL_EHT_SIG_MCS_MASK,		WF_LWTBL_EHT_SIG_MCS_SHIFT,	false},
++	{"HDRT_MODE",       WF_LWTBL_HDRT_MODE_MASK,		NO_SHIFT_DEFINE,		false},
++	{"BEAM_CHG",        WF_LWTBL_BEAM_CHG_MASK,		NO_SHIFT_DEFINE,		false},
++	{"EHT_LTF_SYM_NUM", WF_LWTBL_EHT_LTF_SYM_NUM_OPT_MASK,  WF_LWTBL_EHT_LTF_SYM_NUM_OPT_SHIFT,	true},
++	{"PFMU_IDX",	    WF_LWTBL_PFMU_IDX_MASK,		WF_LWTBL_PFMU_IDX_SHIFT,	false},
++	{"ULPF_IDX",	    WF_LWTBL_ULPF_IDX_MASK,		WF_LWTBL_ULPF_IDX_SHIFT,	false},
++	{"RIBF",	    WF_LWTBL_RIBF_MASK,			NO_SHIFT_DEFINE,		false},
++	{"ULPF",	    WF_LWTBL_ULPF_MASK,			NO_SHIFT_DEFINE,		false},
++	{"BYPASS_TXSMM",    WF_LWTBL_BYPASS_TXSMM_MASK,         NO_SHIFT_DEFINE,		true},
++	{"TBF_HT",          WF_LWTBL_TBF_HT_MASK,		NO_SHIFT_DEFINE,		false},
++	{"TBF_VHT",         WF_LWTBL_TBF_VHT_MASK,		NO_SHIFT_DEFINE,		false},
++	{"TBF_HE",          WF_LWTBL_TBF_HE_MASK,		NO_SHIFT_DEFINE,		false},
++	{"TBF_EHT",         WF_LWTBL_TBF_EHT_MASK,		NO_SHIFT_DEFINE,		false},
++	{"IGN_FBK",         WF_LWTBL_IGN_FBK_MASK,		NO_SHIFT_DEFINE,		true},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw3(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	/* LMAC WTBL DW 3 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 3\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_3*4]);
++	dw_value = *addr;
++
++	while (WTBL_LMAC_DW3[i].name) {
++
++		if (WTBL_LMAC_DW3[i].shift == NO_SHIFT_DEFINE)
++			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW3[i].name,
++					 (dw_value & WTBL_LMAC_DW3[i].mask) ? 1 : 0);
++		else
++			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW3[i].name,
++					  (dw_value & WTBL_LMAC_DW3[i].mask) >> WTBL_LMAC_DW3[i].shift);
++		i++;
++	}
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW4[] = {
++	{"NEGOTIATED_WINSIZE0",	WF_LWTBL_NEGOTIATED_WINSIZE0_MASK,	WF_LWTBL_NEGOTIATED_WINSIZE0_SHIFT,    false},
++	{"WINSIZE1",	WF_LWTBL_NEGOTIATED_WINSIZE1_MASK,	WF_LWTBL_NEGOTIATED_WINSIZE1_SHIFT,    false},
++	{"WINSIZE2",	WF_LWTBL_NEGOTIATED_WINSIZE2_MASK,	WF_LWTBL_NEGOTIATED_WINSIZE2_SHIFT,    false},
++	{"WINSIZE3",	WF_LWTBL_NEGOTIATED_WINSIZE3_MASK,	WF_LWTBL_NEGOTIATED_WINSIZE3_SHIFT,    true},
++	{"WINSIZE4",	WF_LWTBL_NEGOTIATED_WINSIZE4_MASK,	WF_LWTBL_NEGOTIATED_WINSIZE4_SHIFT,    false},
++	{"WINSIZE5",	WF_LWTBL_NEGOTIATED_WINSIZE5_MASK,	WF_LWTBL_NEGOTIATED_WINSIZE5_SHIFT,    false},
++	{"WINSIZE6",	WF_LWTBL_NEGOTIATED_WINSIZE6_MASK,	WF_LWTBL_NEGOTIATED_WINSIZE6_SHIFT,    false},
++	{"WINSIZE7",	WF_LWTBL_NEGOTIATED_WINSIZE7_MASK,	WF_LWTBL_NEGOTIATED_WINSIZE7_SHIFT,    true},
++	{"PE",              WF_LWTBL_PE_MASK,           WF_LWTBL_PE_SHIFT,	false},
++	{"DIS_RHTR",        WF_LWTBL_DIS_RHTR_MASK,     NO_SHIFT_DEFINE,	false},
++	{"LDPC_HT",         WF_LWTBL_LDPC_HT_MASK,      NO_SHIFT_DEFINE,	false},
++	{"LDPC_VHT",        WF_LWTBL_LDPC_VHT_MASK,     NO_SHIFT_DEFINE,	false},
++	{"LDPC_HE",         WF_LWTBL_LDPC_HE_MASK,      NO_SHIFT_DEFINE,	false},
++	{"LDPC_EHT",	    WF_LWTBL_LDPC_EHT_MASK,	NO_SHIFT_DEFINE,	true},
++	{"BA_MODE",	    WF_LWTBL_BA_MODE_MASK,	NO_SHIFT_DEFINE,	true},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw4(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	/* LMAC WTBL DW 4 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 4\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_4*4]);
++	dw_value = *addr;
++
++	while (WTBL_LMAC_DW4[i].name) {
++		if (WTBL_LMAC_DW4[i].shift == NO_SHIFT_DEFINE)
++			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW4[i].name,
++					 (dw_value & WTBL_LMAC_DW4[i].mask) ? 1 : 0);
++		else
++			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW4[i].name,
++					  (dw_value & WTBL_LMAC_DW4[i].mask) >> WTBL_LMAC_DW4[i].shift);
++		i++;
++	}
++}
++
++static const struct berse_wtbl_parse *WTBL_LMAC_DW5;
++static const struct berse_wtbl_parse WTBL_LMAC_DW5_7996[] = {
++	{"AF",                  WF_LWTBL_AF_MASK,           WF_LWTBL_AF_SHIFT,	false},
++	{"AF_HE",               WF_LWTBL_AF_HE_MASK,        WF_LWTBL_AF_HE_SHIFT,false},
++	{"RTS",                 WF_LWTBL_RTS_MASK,          NO_SHIFT_DEFINE,	false},
++	{"SMPS",                WF_LWTBL_SMPS_MASK,         NO_SHIFT_DEFINE,	false},
++	{"DYN_BW",              WF_LWTBL_DYN_BW_MASK,       NO_SHIFT_DEFINE,	true},
++	{"MMSS",                WF_LWTBL_MMSS_MASK,         WF_LWTBL_MMSS_SHIFT,false},
++	{"USR",                 WF_LWTBL_USR_MASK,          NO_SHIFT_DEFINE,	false},
++	{"SR_RATE",             WF_LWTBL_SR_R_MASK,         WF_LWTBL_SR_R_SHIFT,false},
++	{"SR_ABORT",            WF_LWTBL_SR_ABORT_MASK,     NO_SHIFT_DEFINE,	true},
++	{"TX_POWER_OFFSET",     WF_LWTBL_TX_POWER_OFFSET_MASK,  WF_LWTBL_TX_POWER_OFFSET_SHIFT,	false},
++	{"LTF_EHT",		WF_LWTBL_LTF_EHT_MASK,      WF_LWTBL_LTF_EHT_SHIFT, false},
++	{"GI_EHT",		WF_LWTBL_GI_EHT_MASK,       WF_LWTBL_GI_EHT_SHIFT, false},
++	{"DOPPL",               WF_LWTBL_DOPPL_MASK,        NO_SHIFT_DEFINE,	false},
++	{"TXOP_PS_CAP",         WF_LWTBL_TXOP_PS_CAP_MASK,  NO_SHIFT_DEFINE,	false},
++	{"DONOT_UPDATE_I_PSM",  WF_LWTBL_DU_I_PSM_MASK,     NO_SHIFT_DEFINE,	true},
++	{"I_PSM",               WF_LWTBL_I_PSM_MASK,        NO_SHIFT_DEFINE,	false},
++	{"PSM",                 WF_LWTBL_PSM_MASK,          NO_SHIFT_DEFINE,	false},
++	{"SKIP_TX",             WF_LWTBL_SKIP_TX_MASK,      NO_SHIFT_DEFINE,	true},
++	{NULL,}
++};
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW5_7992[] = {
++	{"AF",                  WF_LWTBL_AF_MASK_7992,      WF_LWTBL_AF_SHIFT,	false},
++	{"RTS",                 WF_LWTBL_RTS_MASK,          NO_SHIFT_DEFINE,	false},
++	{"SMPS",                WF_LWTBL_SMPS_MASK,         NO_SHIFT_DEFINE,	false},
++	{"DYN_BW",              WF_LWTBL_DYN_BW_MASK,       NO_SHIFT_DEFINE,	true},
++	{"MMSS",                WF_LWTBL_MMSS_MASK,         WF_LWTBL_MMSS_SHIFT,false},
++	{"USR",                 WF_LWTBL_USR_MASK,          NO_SHIFT_DEFINE,	false},
++	{"SR_RATE",             WF_LWTBL_SR_R_MASK,         WF_LWTBL_SR_R_SHIFT,false},
++	{"SR_ABORT",            WF_LWTBL_SR_ABORT_MASK,     NO_SHIFT_DEFINE,	true},
++	{"TX_POWER_OFFSET",     WF_LWTBL_TX_POWER_OFFSET_MASK,  WF_LWTBL_TX_POWER_OFFSET_SHIFT,	false},
++	{"LTF_EHT",		WF_LWTBL_LTF_EHT_MASK,      WF_LWTBL_LTF_EHT_SHIFT, false},
++	{"GI_EHT",		WF_LWTBL_GI_EHT_MASK,       WF_LWTBL_GI_EHT_SHIFT, false},
++	{"DOPPL",               WF_LWTBL_DOPPL_MASK,        NO_SHIFT_DEFINE,	false},
++	{"TXOP_PS_CAP",         WF_LWTBL_TXOP_PS_CAP_MASK,  NO_SHIFT_DEFINE,	false},
++	{"DONOT_UPDATE_I_PSM",  WF_LWTBL_DU_I_PSM_MASK,     NO_SHIFT_DEFINE,	true},
++	{"I_PSM",               WF_LWTBL_I_PSM_MASK,        NO_SHIFT_DEFINE,	false},
++	{"PSM",                 WF_LWTBL_PSM_MASK,          NO_SHIFT_DEFINE,	false},
++	{"SKIP_TX",             WF_LWTBL_SKIP_TX_MASK,      NO_SHIFT_DEFINE,	true},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw5(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	/* LMAC WTBL DW 5 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 5\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_5*4]);
++	dw_value = *addr;
++
++	while (WTBL_LMAC_DW5[i].name) {
++		if (WTBL_LMAC_DW5[i].shift == NO_SHIFT_DEFINE)
++			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW5[i].name,
++					 (dw_value & WTBL_LMAC_DW5[i].mask) ? 1 : 0);
++		else
++			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW5[i].name,
++					  (dw_value & WTBL_LMAC_DW5[i].mask) >> WTBL_LMAC_DW5[i].shift);
++		i++;
++	}
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW6[] = {
++	{"CBRN",        WF_LWTBL_CBRN_MASK,	    WF_LWTBL_CBRN_SHIFT,	false},
++	{"DBNSS_EN",    WF_LWTBL_DBNSS_EN_MASK, NO_SHIFT_DEFINE,	false},
++	{"BAF_EN",      WF_LWTBL_BAF_EN_MASK,   NO_SHIFT_DEFINE,	false},
++	{"RDGBA",       WF_LWTBL_RDGBA_MASK,    NO_SHIFT_DEFINE,	false},
++	{"RDG",         WF_LWTBL_R_MASK,        NO_SHIFT_DEFINE,	false},
++	{"SPE_IDX",     WF_LWTBL_SPE_IDX_MASK,  WF_LWTBL_SPE_IDX_SHIFT,	true},
++	{"G2",          WF_LWTBL_G2_MASK,       NO_SHIFT_DEFINE,	false},
++	{"G4",          WF_LWTBL_G4_MASK,       NO_SHIFT_DEFINE,	false},
++	{"G8",          WF_LWTBL_G8_MASK,       NO_SHIFT_DEFINE,	false},
++	{"G16",         WF_LWTBL_G16_MASK,      NO_SHIFT_DEFINE,	true},
++	{"G2_LTF",      WF_LWTBL_G2_LTF_MASK,   WF_LWTBL_G2_LTF_SHIFT,	false},
++	{"G4_LTF",      WF_LWTBL_G4_LTF_MASK,   WF_LWTBL_G4_LTF_SHIFT,	false},
++	{"G8_LTF",      WF_LWTBL_G8_LTF_MASK,   WF_LWTBL_G8_LTF_SHIFT,	false},
++	{"G16_LTF",     WF_LWTBL_G16_LTF_MASK,  WF_LWTBL_G16_LTF_SHIFT,	true},
++	{"G2_HE",       WF_LWTBL_G2_HE_MASK,    WF_LWTBL_G2_HE_SHIFT,	false},
++	{"G4_HE",       WF_LWTBL_G4_HE_MASK,    WF_LWTBL_G4_HE_SHIFT,	false},
++	{"G8_HE",       WF_LWTBL_G8_HE_MASK,    WF_LWTBL_G8_HE_SHIFT,	false},
++	{"G16_HE",      WF_LWTBL_G16_HE_MASK,   WF_LWTBL_G16_HE_SHIFT,	true},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw6(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	/* LMAC WTBL DW 6 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 6\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_6*4]);
++	dw_value = *addr;
++
++	while (WTBL_LMAC_DW6[i].name) {
++		if (WTBL_LMAC_DW6[i].shift == NO_SHIFT_DEFINE)
++			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW6[i].name,
++					 (dw_value & WTBL_LMAC_DW6[i].mask) ? 1 : 0);
++		else
++			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW6[i].name,
++					  (dw_value & WTBL_LMAC_DW6[i].mask) >> WTBL_LMAC_DW6[i].shift);
++		i++;
++	}
++}
++
++static void parse_fmac_lwtbl_dw7(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	int i = 0;
++
++	/* LMAC WTBL DW 7 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 7\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_7*4]);
++	dw_value = *addr;
++
++	for (i = 0; i < 8; i++) {
++		seq_printf(s, "\tBA_WIN_SIZE%u:%lu\n", i, ((dw_value & BITS(i*4, i*4+3)) >> i*4));
++	}
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW8[] = {
++	{"RTS_FAIL_CNT_AC0",    WF_LWTBL_AC0_RTS_FAIL_CNT_MASK,	WF_LWTBL_AC0_RTS_FAIL_CNT_SHIFT,	false},
++	{"AC1",                 WF_LWTBL_AC1_RTS_FAIL_CNT_MASK,	WF_LWTBL_AC1_RTS_FAIL_CNT_SHIFT,	false},
++	{"AC2",                 WF_LWTBL_AC2_RTS_FAIL_CNT_MASK,	WF_LWTBL_AC2_RTS_FAIL_CNT_SHIFT,	false},
++	{"AC3",                 WF_LWTBL_AC3_RTS_FAIL_CNT_MASK,	WF_LWTBL_AC3_RTS_FAIL_CNT_SHIFT,	true},
++	{"PARTIAL_AID",         WF_LWTBL_PARTIAL_AID_MASK,		WF_LWTBL_PARTIAL_AID_SHIFT,	false},
++	{"CHK_PER",             WF_LWTBL_CHK_PER_MASK,		NO_SHIFT_DEFINE,	true},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw8(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	/* LMAC WTBL DW 8 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 8\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_8*4]);
++	dw_value = *addr;
++
++	while (WTBL_LMAC_DW8[i].name) {
++		if (WTBL_LMAC_DW8[i].shift == NO_SHIFT_DEFINE)
++			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW8[i].name,
++					 (dw_value & WTBL_LMAC_DW8[i].mask) ? 1 : 0);
++		else
++			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW8[i].name,
++					  (dw_value & WTBL_LMAC_DW8[i].mask) >> WTBL_LMAC_DW8[i].shift);
++		i++;
++	}
++}
++
++static const struct berse_wtbl_parse *WTBL_LMAC_DW9;
++static const struct berse_wtbl_parse WTBL_LMAC_DW9_7996[] = {
++	{"RX_AVG_MPDU_SIZE",    WF_LWTBL_RX_AVG_MPDU_SIZE_MASK,    WF_LWTBL_RX_AVG_MPDU_SIZE_SHIFT,	false},
++	{"PRITX_SW_MODE",       WF_LWTBL_PRITX_SW_MODE_MASK,       NO_SHIFT_DEFINE,	false},
++	{"PRITX_ERSU",	    WF_LWTBL_PRITX_ERSU_MASK,	       NO_SHIFT_DEFINE,	false},
++	{"PRITX_PLR",           WF_LWTBL_PRITX_PLR_MASK,           NO_SHIFT_DEFINE,	true},
++	{"PRITX_DCM",           WF_LWTBL_PRITX_DCM_MASK,           NO_SHIFT_DEFINE,	false},
++	{"PRITX_ER106T",        WF_LWTBL_PRITX_ER106T_MASK,        NO_SHIFT_DEFINE,	true},
++	/* {"FCAP(0:20 1:~40)",    WTBL_FCAP_20_TO_160_MHZ,	WTBL_FCAP_20_TO_160_MHZ_OFFSET}, */
++	{"MPDU_FAIL_CNT",       WF_LWTBL_MPDU_FAIL_CNT_MASK,       WF_LWTBL_MPDU_FAIL_CNT_SHIFT,	false},
++	{"MPDU_OK_CNT",         WF_LWTBL_MPDU_OK_CNT_MASK,         WF_LWTBL_MPDU_OK_CNT_SHIFT,	false},
++	{"RATE_IDX",            WF_LWTBL_RATE_IDX_MASK,            WF_LWTBL_RATE_IDX_SHIFT,	true},
++	{NULL,}
++};
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW9_7992[] = {
++	{"RX_AVG_MPDU_SIZE",    WF_LWTBL_RX_AVG_MPDU_SIZE_MASK,    WF_LWTBL_RX_AVG_MPDU_SIZE_SHIFT,	false},
++	{"PRITX_SW_MODE",       WF_LWTBL_PRITX_SW_MODE_MASK_7992,       NO_SHIFT_DEFINE,	false},
++	{"PRITX_ERSU",	    WF_LWTBL_PRITX_ERSU_MASK_7992,	       NO_SHIFT_DEFINE,	false},
++	{"PRITX_PLR",           WF_LWTBL_PRITX_PLR_MASK_7992,           NO_SHIFT_DEFINE,	true},
++	{"PRITX_DCM",           WF_LWTBL_PRITX_DCM_MASK,           NO_SHIFT_DEFINE,	false},
++	{"PRITX_ER106T",        WF_LWTBL_PRITX_ER106T_MASK,        NO_SHIFT_DEFINE,	true},
++	/* {"FCAP(0:20 1:~40)",    WTBL_FCAP_20_TO_160_MHZ,	WTBL_FCAP_20_TO_160_MHZ_OFFSET}, */
++	{"MPDU_FAIL_CNT",       WF_LWTBL_MPDU_FAIL_CNT_MASK,       WF_LWTBL_MPDU_FAIL_CNT_SHIFT,	false},
++	{"MPDU_OK_CNT",         WF_LWTBL_MPDU_OK_CNT_MASK,         WF_LWTBL_MPDU_OK_CNT_SHIFT,	false},
++	{"RATE_IDX",            WF_LWTBL_RATE_IDX_MASK,            WF_LWTBL_RATE_IDX_SHIFT,	true},
++	{NULL,}
++};
++
++char *fcap_name[] = {"20MHz", "20/40MHz", "20/40/80MHz", "20/40/80/160/80+80MHz", "20/40/80/160/80+80/320MHz"};
++
++static void parse_fmac_lwtbl_dw9(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	/* LMAC WTBL DW 9 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 9\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_9*4]);
++	dw_value = *addr;
++
++	while (WTBL_LMAC_DW9[i].name) {
++		if (WTBL_LMAC_DW9[i].shift == NO_SHIFT_DEFINE)
++			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW9[i].name,
++					 (dw_value & WTBL_LMAC_DW9[i].mask) ? 1 : 0);
++		else
++			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW9[i].name,
++					  (dw_value & WTBL_LMAC_DW9[i].mask) >> WTBL_LMAC_DW9[i].shift);
++		i++;
++	}
++
++	/* FCAP parser */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "FCAP:%s\n", fcap_name[(dw_value & WF_LWTBL_FCAP_MASK) >> WF_LWTBL_FCAP_SHIFT]);
++}
++
++#define HW_TX_RATE_TO_MODE(_x)			(((_x) & WTBL_RATE_TX_MODE_MASK) >> WTBL_RATE_TX_MODE_OFFSET)
++#define HW_TX_RATE_TO_MCS(_x, _mode)		((_x) & WTBL_RATE_TX_RATE_MASK >> WTBL_RATE_TX_RATE_OFFSET)
++#define HW_TX_RATE_TO_NSS(_x)			(((_x) & WTBL_RATE_NSTS_MASK) >> WTBL_RATE_NSTS_OFFSET)
++#define HW_TX_RATE_TO_STBC(_x)			(((_x) & WTBL_RATE_STBC_MASK) >> WTBL_RATE_STBC_OFFSET)
++
++#define MAX_TX_MODE 16
++static char *HW_TX_MODE_STR[] = {"CCK", "OFDM", "HT-Mix", "HT-GF", "VHT",
++				 "N/A", "N/A", "N/A",
++				 "HE_SU", "HE_EXT_SU", "HE_TRIG", "HE_MU",
++				 "N/A",
++				 "EHT_EXT_SU", "EHT_TRIG", "EHT_MU",
++				 "N/A"};
++static char *HW_TX_RATE_CCK_STR[] = {"1M", "2Mlong", "5.5Mlong", "11Mlong", "N/A", "2Mshort", "5.5Mshort", "11Mshort", "N/A"};
++static char *HW_TX_RATE_OFDM_STR[] = {"6M", "9M", "12M", "18M", "24M", "36M", "48M", "54M", "N/A"};
++
++static char *hw_rate_ofdm_str(uint16_t ofdm_idx)
++{
++	switch (ofdm_idx) {
++	case 11: /* 6M */
++		return HW_TX_RATE_OFDM_STR[0];
++
++	case 15: /* 9M */
++		return HW_TX_RATE_OFDM_STR[1];
++
++	case 10: /* 12M */
++		return HW_TX_RATE_OFDM_STR[2];
++
++	case 14: /* 18M */
++		return HW_TX_RATE_OFDM_STR[3];
++
++	case 9: /* 24M */
++		return HW_TX_RATE_OFDM_STR[4];
++
++	case 13: /* 36M */
++		return HW_TX_RATE_OFDM_STR[5];
++
++	case 8: /* 48M */
++		return HW_TX_RATE_OFDM_STR[6];
++
++	case 12: /* 54M */
++		return HW_TX_RATE_OFDM_STR[7];
++
++	default:
++		return HW_TX_RATE_OFDM_STR[8];
++	}
++}
++
++static char *hw_rate_str(u8 mode, uint16_t rate_idx)
++{
++	if (mode == 0)
++		return rate_idx < 8 ? HW_TX_RATE_CCK_STR[rate_idx] : HW_TX_RATE_CCK_STR[8];
++	else if (mode == 1)
++		return hw_rate_ofdm_str(rate_idx);
++	else
++		return "MCS";
++}
++
++static void
++parse_rate(struct seq_file *s, uint16_t rate_idx, uint16_t txrate)
++{
++	uint16_t txmode, mcs, nss, stbc;
++
++	txmode = HW_TX_RATE_TO_MODE(txrate);
++	mcs = HW_TX_RATE_TO_MCS(txrate, txmode);
++	nss = HW_TX_RATE_TO_NSS(txrate);
++	stbc = HW_TX_RATE_TO_STBC(txrate);
++
++	seq_printf(s, "\tRate%d(0x%x):TxMode=%d(%s), TxRate=%d(%s), Nsts=%d, STBC=%d\n",
++			  rate_idx + 1, txrate,
++			  txmode, (txmode < MAX_TX_MODE ? HW_TX_MODE_STR[txmode] : HW_TX_MODE_STR[MAX_TX_MODE]),
++			  mcs, hw_rate_str(txmode, mcs), nss, stbc);
++}
++
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW10[] = {
++	{"RATE1",       WF_LWTBL_RATE1_MASK,        WF_LWTBL_RATE1_SHIFT},
++	{"RATE2",       WF_LWTBL_RATE2_MASK,        WF_LWTBL_RATE2_SHIFT},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw10(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	/* LMAC WTBL DW 10 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 10\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_AUTO_RATE_1_2*4]);
++	dw_value = *addr;
++
++	while (WTBL_LMAC_DW10[i].name) {
++		parse_rate(s, i, (dw_value & WTBL_LMAC_DW10[i].mask) >> WTBL_LMAC_DW10[i].shift);
++		i++;
++	}
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW11[] = {
++	{"RATE3",       WF_LWTBL_RATE3_MASK,        WF_LWTBL_RATE3_SHIFT},
++	{"RATE4",       WF_LWTBL_RATE4_MASK,        WF_LWTBL_RATE4_SHIFT},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw11(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	/* LMAC WTBL DW 11 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 11\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_AUTO_RATE_3_4*4]);
++	dw_value = *addr;
++
++	while (WTBL_LMAC_DW11[i].name) {
++		parse_rate(s, i+2, (dw_value & WTBL_LMAC_DW11[i].mask) >> WTBL_LMAC_DW11[i].shift);
++		i++;
++	}
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW12[] = {
++	{"RATE5",       WF_LWTBL_RATE5_MASK,        WF_LWTBL_RATE5_SHIFT},
++	{"RATE6",       WF_LWTBL_RATE6_MASK,        WF_LWTBL_RATE6_SHIFT},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw12(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	/* LMAC WTBL DW 12 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 12\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_AUTO_RATE_5_6*4]);
++	dw_value = *addr;
++
++	while (WTBL_LMAC_DW12[i].name) {
++		parse_rate(s, i+4, (dw_value & WTBL_LMAC_DW12[i].mask) >> WTBL_LMAC_DW12[i].shift);
++		i++;
++	}
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW13[] = {
++	{"RATE7",       WF_LWTBL_RATE7_MASK,        WF_LWTBL_RATE7_SHIFT},
++	{"RATE8",       WF_LWTBL_RATE8_MASK,        WF_LWTBL_RATE8_SHIFT},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw13(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	/* LMAC WTBL DW 13 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 13\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_AUTO_RATE_7_8*4]);
++	dw_value = *addr;
++
++	while (WTBL_LMAC_DW13[i].name) {
++		parse_rate(s, i+6, (dw_value & WTBL_LMAC_DW13[i].mask) >> WTBL_LMAC_DW13[i].shift);
++		i++;
++	}
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW14_BMC[] = {
++	{"CIPHER_IGTK",         WF_LWTBL_CIPHER_SUIT_IGTK_MASK,    WF_LWTBL_CIPHER_SUIT_IGTK_SHIFT,		false},
++	{"CIPHER_BIGTK",        WF_LWTBL_CIPHER_SUIT_BIGTK_MASK,   WF_LWTBL_CIPHER_SUIT_BIGTK_SHIFT,	true},
++	{NULL,}
++};
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW14[] = {
++	{"RATE1_TX_CNT",      WF_LWTBL_RATE1_TX_CNT_MASK,     WF_LWTBL_RATE1_TX_CNT_SHIFT,   false},
++	{"RATE1_FAIL_CNT",    WF_LWTBL_RATE1_FAIL_CNT_MASK,   WF_LWTBL_RATE1_FAIL_CNT_SHIFT, true},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw14(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr, *muar_addr = 0;
++	u32 dw_value, muar_dw_value = 0;
++	u16 i = 0;
++
++	/* DUMP DW14 for BMC entry only */
++	muar_addr = (u32 *)&(lwtbl[WF_LWTBL_MUAR_DW*4]);
++	muar_dw_value = *muar_addr;
++	if (((muar_dw_value & WF_LWTBL_MUAR_MASK) >> WF_LWTBL_MUAR_SHIFT)
++		== MUAR_INDEX_OWN_MAC_ADDR_BC_MC) {
++		/* LMAC WTBL DW 14 */
++		seq_printf(s, "\t\n");
++		seq_printf(s, "LWTBL DW 14\n");
++		addr = (u32 *)&(lwtbl[WF_LWTBL_CIPHER_SUIT_IGTK_DW*4]);
++		dw_value = *addr;
++
++		while (WTBL_LMAC_DW14_BMC[i].name) {
++			if (WTBL_LMAC_DW14_BMC[i].shift == NO_SHIFT_DEFINE)
++				seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW14_BMC[i].name,
++					(dw_value & WTBL_LMAC_DW14_BMC[i].mask) ? 1 : 0);
++			else
++				seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW14_BMC[i].name,
++					(dw_value & WTBL_LMAC_DW14_BMC[i].mask) >> WTBL_LMAC_DW14_BMC[i].shift);
++			i++;
++		}
++	} else {
++		seq_printf(s, "\t\n");
++		seq_printf(s, "LWTBL DW 14\n");
++		addr = (u32 *)&(lwtbl[WF_LWTBL_CIPHER_SUIT_IGTK_DW*4]);
++		dw_value = *addr;
++
++		while (WTBL_LMAC_DW14[i].name) {
++			if (WTBL_LMAC_DW14[i].shift == NO_SHIFT_DEFINE)
++				seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW14[i].name,
++					(dw_value & WTBL_LMAC_DW14[i].mask) ? 1 : 0);
++			else
++				seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW14[i].name,
++					(dw_value & WTBL_LMAC_DW14[i].mask) >> WTBL_LMAC_DW14[i].shift);
++			i++;
++		}
++	}
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW28[] = {
++	{"RELATED_IDX0",	WF_LWTBL_RELATED_IDX0_MASK,		WF_LWTBL_RELATED_IDX0_SHIFT,	false},
++	{"RELATED_BAND0",	WF_LWTBL_RELATED_BAND0_MASK,		WF_LWTBL_RELATED_BAND0_SHIFT,	false},
++	{"PRI_MLD_BAND",    WF_LWTBL_PRIMARY_MLD_BAND_MASK,		WF_LWTBL_PRIMARY_MLD_BAND_SHIFT,	true},
++	{"RELATED_IDX1",	WF_LWTBL_RELATED_IDX1_MASK,		WF_LWTBL_RELATED_IDX1_SHIFT,	false},
++	{"RELATED_BAND1",   WF_LWTBL_RELATED_BAND1_MASK,		WF_LWTBL_RELATED_BAND1_SHIFT,	false},
++	{"SEC_MLD_BAND",	WF_LWTBL_SECONDARY_MLD_BAND_MASK,	WF_LWTBL_SECONDARY_MLD_BAND_SHIFT,	true},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw28(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	/* LMAC WTBL DW 28 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 28\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_MLO_INFO_LINE_1*4]);
++	dw_value = *addr;
++
++	while (WTBL_LMAC_DW28[i].name) {
++		if (WTBL_LMAC_DW28[i].shift == NO_SHIFT_DEFINE)
++			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW28[i].name,
++				(dw_value & WTBL_LMAC_DW28[i].mask) ? 1 : 0);
++		else
++			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW28[i].name,
++				(dw_value & WTBL_LMAC_DW28[i].mask) >>
++					WTBL_LMAC_DW28[i].shift);
++		i++;
++	}
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW29[] = {
++	{"DISPATCH_POLICY_MLD_TID0", WF_LWTBL_DISPATCH_POLICY0_MASK,	WF_LWTBL_DISPATCH_POLICY0_SHIFT,	false},
++	{"MLD_TID1",	WF_LWTBL_DISPATCH_POLICY1_MASK,		WF_LWTBL_DISPATCH_POLICY1_SHIFT,	false},
++	{"MLD_TID2",	WF_LWTBL_DISPATCH_POLICY2_MASK,		WF_LWTBL_DISPATCH_POLICY2_SHIFT,	false},
++	{"MLD_TID3",	WF_LWTBL_DISPATCH_POLICY3_MASK,	WF_LWTBL_DISPATCH_POLICY3_SHIFT,	true},
++	{"MLD_TID4",	WF_LWTBL_DISPATCH_POLICY4_MASK,		WF_LWTBL_DISPATCH_POLICY4_SHIFT,	false},
++	{"MLD_TID5",	WF_LWTBL_DISPATCH_POLICY5_MASK,		WF_LWTBL_DISPATCH_POLICY5_SHIFT,	false},
++	{"MLD_TID6",	WF_LWTBL_DISPATCH_POLICY6_MASK,		WF_LWTBL_DISPATCH_POLICY6_SHIFT,	false},
++	{"MLD_TID7",	WF_LWTBL_DISPATCH_POLICY7_MASK,		WF_LWTBL_DISPATCH_POLICY7_SHIFT,	true},
++	{"OMLD_ID",		WF_LWTBL_OWN_MLD_ID_MASK,	WF_LWTBL_OWN_MLD_ID_SHIFT,	false},
++	{"EMLSR0",		WF_LWTBL_EMLSR0_MASK,		NO_SHIFT_DEFINE,	false},
++	{"EMLMR0",		WF_LWTBL_EMLMR0_MASK,		NO_SHIFT_DEFINE,	false},
++	{"EMLSR1",		WF_LWTBL_EMLSR1_MASK,		NO_SHIFT_DEFINE,	false},
++	{"EMLMR1",		WF_LWTBL_EMLMR1_MASK,		NO_SHIFT_DEFINE,	true},
++	{"EMLSR2",		WF_LWTBL_EMLSR2_MASK,		NO_SHIFT_DEFINE,	false},
++	{"EMLMR2",		WF_LWTBL_EMLMR2_MASK,		NO_SHIFT_DEFINE,	false},
++	{"STR_BITMAP",	WF_LWTBL_STR_BITMAP_MASK,	WF_LWTBL_STR_BITMAP_SHIFT,	true},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw29(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	/* LMAC WTBL DW 29 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 29\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_MLO_INFO_LINE_2*4]);
++	dw_value = *addr;
++
++	while (WTBL_LMAC_DW29[i].name) {
++		if (WTBL_LMAC_DW29[i].shift == NO_SHIFT_DEFINE)
++			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW29[i].name,
++				(dw_value & WTBL_LMAC_DW29[i].mask) ? 1 : 0);
++		else
++			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW29[i].name,
++				(dw_value & WTBL_LMAC_DW29[i].mask) >>
++					WTBL_LMAC_DW29[i].shift);
++		i++;
++	}
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW30[] = {
++	{"DISPATCH_ORDER",	WF_LWTBL_DISPATCH_ORDER_MASK,	WF_LWTBL_DISPATCH_ORDER_SHIFT,	false},
++	{"DISPATCH_RATIO",	WF_LWTBL_DISPATCH_RATIO_MASK,	WF_LWTBL_DISPATCH_RATIO_SHIFT,	false},
++	{"LINK_MGF",		WF_LWTBL_LINK_MGF_MASK,		WF_LWTBL_LINK_MGF_SHIFT,	true},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw30(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	/* LMAC WTBL DW 30 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 30\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_MLO_INFO_LINE_3*4]);
++	dw_value = *addr;
++
++
++	while (WTBL_LMAC_DW30[i].name) {
++		if (WTBL_LMAC_DW30[i].shift == NO_SHIFT_DEFINE)
++			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW30[i].name,
++				(dw_value & WTBL_LMAC_DW30[i].mask) ? 1 : 0);
++		else
++			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW30[i].name,
++				(dw_value & WTBL_LMAC_DW30[i].mask) >> WTBL_LMAC_DW30[i].shift);
++		i++;
++	}
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW31[] = {
++	{"BFTX_TB",          WF_LWTBL_BFTX_TB_MASK,                 NO_SHIFT_DEFINE,    false},
++	{"DROP",          WF_LWTBL_DROP_MASK,                 NO_SHIFT_DEFINE,    false},
++	{"CASCAD",	        WF_LWTBL_CASCAD_MASK,			NO_SHIFT_DEFINE,    false},
++	{"ALL_ACK",	        WF_LWTBL_ALL_ACK_MASK,			NO_SHIFT_DEFINE,    false},
++	{"MPDU_SIZE",	WF_LWTBL_MPDU_SIZE_MASK,		WF_LWTBL_MPDU_SIZE_SHIFT,  false},
++	{"RXD_DUP_MODE",	WF_LWTBL_RXD_DUP_MODE_MASK,			WF_LWTBL_RXD_DUP_MODE_SHIFT,  true},
++	{"ACK_EN",		WF_LWTBL_ACK_EN_MASK,			NO_SHIFT_DEFINE,		true},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw31(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	/* LMAC WTBL DW 31 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 31\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_RESP_INFO_DW_31*4]);
++	dw_value = *addr;
++
++	while (WTBL_LMAC_DW31[i].name) {
++		if (WTBL_LMAC_DW31[i].shift == NO_SHIFT_DEFINE)
++			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW31[i].name,
++				(dw_value & WTBL_LMAC_DW31[i].mask) ? 1 : 0);
++		else
++			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW31[i].name,
++				(dw_value & WTBL_LMAC_DW31[i].mask) >>
++					WTBL_LMAC_DW31[i].shift);
++		i++;
++	}
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW32[] = {
++	{"OM_INFO",			WF_LWTBL_OM_INFO_MASK,			WF_LWTBL_OM_INFO_SHIFT,		false},
++	{"OM_INFO_EHT",         WF_LWTBL_OM_INFO_EHT_MASK,         WF_LWTBL_OM_INFO_EHT_SHIFT,  false},
++	{"RXD_DUP_FOR_OM_CHG",		WF_LWTBL_RXD_DUP_FOR_OM_CHG_MASK,	NO_SHIFT_DEFINE,		false},
++	{"RXD_DUP_WHITE_LIST",	WF_LWTBL_RXD_DUP_WHITE_LIST_MASK,	WF_LWTBL_RXD_DUP_WHITE_LIST_SHIFT,	false},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw32(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	/* LMAC WTBL DW 32 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 32\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_RX_DUP_INFO_DW_32*4]);
++	dw_value = *addr;
++
++	while (WTBL_LMAC_DW32[i].name) {
++		if (WTBL_LMAC_DW32[i].shift == NO_SHIFT_DEFINE)
++			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW32[i].name,
++				(dw_value & WTBL_LMAC_DW32[i].mask) ? 1 : 0);
++		else
++			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW32[i].name,
++				(dw_value & WTBL_LMAC_DW32[i].mask) >>
++					WTBL_LMAC_DW32[i].shift);
++		i++;
++	}
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW33[] = {
++	{"USER_RSSI",                   WF_LWTBL_USER_RSSI_MASK,            WF_LWTBL_USER_RSSI_SHIFT,	false},
++	{"USER_SNR",                    WF_LWTBL_USER_SNR_MASK,             WF_LWTBL_USER_SNR_SHIFT,	false},
++	{"RAPID_REACTION_RATE",         WF_LWTBL_RAPID_REACTION_RATE_MASK,  WF_LWTBL_RAPID_REACTION_RATE_SHIFT,	true},
++	{"HT_AMSDU(Read Only)",         WF_LWTBL_HT_AMSDU_MASK,             NO_SHIFT_DEFINE,	false},
++	{"AMSDU_CROSS_LG(Read Only)",   WF_LWTBL_AMSDU_CROSS_LG_MASK,       NO_SHIFT_DEFINE,	true},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw33(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	/* LMAC WTBL DW 33 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 33\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_RX_STAT_CNT_LINE_1*4]);
++	dw_value = *addr;
++
++	while (WTBL_LMAC_DW33[i].name) {
++		if (WTBL_LMAC_DW33[i].shift == NO_SHIFT_DEFINE)
++			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW33[i].name,
++				(dw_value & WTBL_LMAC_DW33[i].mask) ? 1 : 0);
++		else
++			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW33[i].name,
++				(dw_value & WTBL_LMAC_DW33[i].mask) >>
++					WTBL_LMAC_DW33[i].shift);
++		i++;
++	}
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW34[] = {
++	{"RESP_RCPI0",	WF_LWTBL_RESP_RCPI0_MASK,	WF_LWTBL_RESP_RCPI0_SHIFT,	false},
++	{"RCPI1",	WF_LWTBL_RESP_RCPI1_MASK,	WF_LWTBL_RESP_RCPI1_SHIFT,	false},
++	{"RCPI2",	WF_LWTBL_RESP_RCPI2_MASK,	WF_LWTBL_RESP_RCPI2_SHIFT,	false},
++	{"RCPI3",	WF_LWTBL_RESP_RCPI3_MASK,	WF_LWTBL_RESP_RCPI3_SHIFT,	true},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw34(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	/* LMAC WTBL DW 34 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 34\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_RX_STAT_CNT_LINE_2*4]);
++	dw_value = *addr;
++
++
++	while (WTBL_LMAC_DW34[i].name) {
++		if (WTBL_LMAC_DW34[i].shift == NO_SHIFT_DEFINE)
++			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW34[i].name,
++				(dw_value & WTBL_LMAC_DW34[i].mask) ? 1 : 0);
++		else
++			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW34[i].name,
++				(dw_value & WTBL_LMAC_DW34[i].mask) >>
++					WTBL_LMAC_DW34[i].shift);
++		i++;
++	}
++}
++
++static const struct berse_wtbl_parse WTBL_LMAC_DW35[] = {
++	{"SNR 0",	WF_LWTBL_SNR_RX0_MASK,		WF_LWTBL_SNR_RX0_SHIFT,	false},
++	{"SNR 1",	WF_LWTBL_SNR_RX1_MASK,		WF_LWTBL_SNR_RX1_SHIFT,	false},
++	{"SNR 2",	WF_LWTBL_SNR_RX2_MASK,		WF_LWTBL_SNR_RX2_SHIFT,	false},
++	{"SNR 3",	WF_LWTBL_SNR_RX3_MASK,		WF_LWTBL_SNR_RX3_SHIFT,	true},
++	{NULL,}
++};
++
++static void parse_fmac_lwtbl_dw35(struct seq_file *s, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	/* LMAC WTBL DW 35 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "LWTBL DW 35\n");
++	addr = (u32 *)&(lwtbl[WTBL_GROUP_RX_STAT_CNT_LINE_3*4]);
++	dw_value = *addr;
++
++
++	while (WTBL_LMAC_DW35[i].name) {
++		if (WTBL_LMAC_DW35[i].shift == NO_SHIFT_DEFINE)
++			seq_printf(s, "\t%s:%d\n", WTBL_LMAC_DW35[i].name,
++				(dw_value & WTBL_LMAC_DW35[i].mask) ? 1 : 0);
++		else
++			seq_printf(s, "\t%s:%u\n", WTBL_LMAC_DW35[i].name,
++				(dw_value & WTBL_LMAC_DW35[i].mask) >>
++					WTBL_LMAC_DW35[i].shift);
++		i++;
++	}
++}
++
++static void parse_fmac_lwtbl_rx_stats(struct seq_file *s, u8 *lwtbl)
++{
++	parse_fmac_lwtbl_dw33(s, lwtbl);
++	parse_fmac_lwtbl_dw34(s, lwtbl);
++	parse_fmac_lwtbl_dw35(s, lwtbl);
++}
++
++static void parse_fmac_lwtbl_mlo_info(struct seq_file *s, u8 *lwtbl)
++{
++	parse_fmac_lwtbl_dw28(s, lwtbl);
++	parse_fmac_lwtbl_dw29(s, lwtbl);
++	parse_fmac_lwtbl_dw30(s, lwtbl);
++}
++
++static const struct berse_wtbl_parse WTBL_UMAC_DW9[] = {
++	{"RELATED_IDX0",	WF_UWTBL_RELATED_IDX0_MASK,		WF_UWTBL_RELATED_IDX0_SHIFT,	false},
++	{"RELATED_BAND0",	WF_UWTBL_RELATED_BAND0_MASK,		WF_UWTBL_RELATED_BAND0_SHIFT,	false},
++	{"PRI_MLD_BAND",    WF_UWTBL_PRIMARY_MLD_BAND_MASK,		WF_UWTBL_PRIMARY_MLD_BAND_SHIFT,	true},
++	{"RELATED_IDX1",	WF_UWTBL_RELATED_IDX1_MASK,		WF_UWTBL_RELATED_IDX1_SHIFT,	false},
++	{"RELATED_BAND1",   WF_UWTBL_RELATED_BAND1_MASK,		WF_UWTBL_RELATED_BAND1_SHIFT,	false},
++	{"SEC_MLD_BAND",	WF_UWTBL_SECONDARY_MLD_BAND_MASK,	WF_UWTBL_SECONDARY_MLD_BAND_SHIFT,	true},
++	{NULL,}
++};
++
++static void parse_fmac_uwtbl_mlo_info(struct seq_file *s, u8 *uwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	seq_printf(s, "\t\n");
++	seq_printf(s, "MldAddr: %02x:%02x:%02x:%02x:%02x:%02x(D0[B0~15], D1[B0~31])\n",
++		uwtbl[4], uwtbl[5], uwtbl[6], uwtbl[7], uwtbl[0], uwtbl[1]);
++
++	/* UMAC WTBL DW 0 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "UWTBL DW 0\n");
++	addr = (u32 *)&(uwtbl[WF_UWTBL_OWN_MLD_ID_DW*4]);
++	dw_value = *addr;
++
++	seq_printf(s, "\t%s:%u\n", "OMLD_ID",
++		(dw_value & WF_UWTBL_OWN_MLD_ID_MASK) >> WF_UWTBL_OWN_MLD_ID_SHIFT);
++
++	/* UMAC WTBL DW 9 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "UWTBL DW 9\n");
++	addr = (u32 *)&(uwtbl[WF_UWTBL_RELATED_IDX0_DW*4]);
++	dw_value = *addr;
++
++	while (WTBL_UMAC_DW9[i].name) {
++
++		if (WTBL_UMAC_DW9[i].shift == NO_SHIFT_DEFINE)
++			seq_printf(s, "\t%s:%d\n", WTBL_UMAC_DW9[i].name,
++				(dw_value & WTBL_UMAC_DW9[i].mask) ? 1 : 0);
++		else
++			seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW9[i].name,
++				 (dw_value & WTBL_UMAC_DW9[i].mask) >>
++					WTBL_UMAC_DW9[i].shift);
++		i++;
++	}
++}
++
++static bool
++is_wtbl_bigtk_exist(u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++
++	addr = (u32 *)&(lwtbl[WF_LWTBL_MUAR_DW*4]);
++	dw_value = *addr;
++	if (((dw_value & WF_LWTBL_MUAR_MASK) >> WF_LWTBL_MUAR_SHIFT) ==
++					MUAR_INDEX_OWN_MAC_ADDR_BC_MC) {
++		addr = (u32 *)&(lwtbl[WF_LWTBL_CIPHER_SUIT_BIGTK_DW*4]);
++		dw_value = *addr;
++		if (((dw_value & WF_LWTBL_CIPHER_SUIT_BIGTK_MASK) >>
++			WF_LWTBL_CIPHER_SUIT_BIGTK_SHIFT) != IGTK_CIPHER_SUIT_NONE)
++			return true;
++	}
++
++	return false;
++}
++
++static const struct berse_wtbl_parse WTBL_UMAC_DW2[] = {
++	{"PN0",		WTBL_PN0_MASK,		WTBL_PN0_OFFSET,	false},
++	{"PN1",		WTBL_PN1_MASK,		WTBL_PN1_OFFSET,	false},
++	{"PN2",		WTBL_PN2_MASK,		WTBL_PN2_OFFSET,	true},
++	{"PN3",		WTBL_PN3_MASK,		WTBL_PN3_OFFSET,	false},
++	{NULL,}
++};
++
++static const struct berse_wtbl_parse WTBL_UMAC_DW3[] = {
++	{"PN4",     WTBL_PN4_MASK,      WTBL_PN4_OFFSET,	false},
++	{"PN5",     WTBL_PN5_MASK,      WTBL_PN5_OFFSET,	true},
++	{"COM_SN",     WF_UWTBL_COM_SN_MASK,     WF_UWTBL_COM_SN_SHIFT,	true},
++	{NULL,}
++};
++
++static const struct berse_wtbl_parse WTBL_UMAC_DW4_BIPN[] = {
++	{"BIPN0",	WTBL_BIPN0_MASK,	WTBL_BIPN0_OFFSET,	false},
++	{"BIPN1",	WTBL_BIPN1_MASK,	WTBL_BIPN1_OFFSET,	false},
++	{"BIPN2",	WTBL_BIPN2_MASK,	WTBL_BIPN2_OFFSET,	true},
++	{"BIPN3",	WTBL_BIPN3_MASK,	WTBL_BIPN3_OFFSET,	false},
++	{NULL,}
++};
++
++static const struct berse_wtbl_parse WTBL_UMAC_DW5_BIPN[] = {
++	{"BIPN4",	WTBL_BIPN4_MASK,	WTBL_BIPN4_OFFSET,	false},
++	{"BIPN5",	WTBL_BIPN5_MASK,	WTBL_BIPN5_OFFSET,	true},
++	{NULL,}
++};
++
++static void parse_fmac_uwtbl_pn(struct seq_file *s, u8 *uwtbl, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u16 i = 0;
++
++	seq_printf(s, "\t\n");
++	seq_printf(s, "UWTBL PN\n");
++
++	/* UMAC WTBL DW 2/3 */
++	addr = (u32 *)&(uwtbl[WF_UWTBL_PN_31_0__DW*4]);
++	dw_value = *addr;
++
++	while (WTBL_UMAC_DW2[i].name) {
++		seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW2[i].name,
++			(dw_value & WTBL_UMAC_DW2[i].mask) >>
++				WTBL_UMAC_DW2[i].shift);
++		i++;
++	}
++
++	i = 0;
++	addr = (u32 *)&(uwtbl[WF_UWTBL_PN_47_32__DW*4]);
++	dw_value = *addr;
++
++	while (WTBL_UMAC_DW3[i].name) {
++		seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW3[i].name,
++			 (dw_value & WTBL_UMAC_DW3[i].mask) >>
++			WTBL_UMAC_DW3[i].shift);
++		i++;
++	}
++
++
++	/* UMAC WTBL DW 4/5 for BIGTK */
++	if (is_wtbl_bigtk_exist(lwtbl) == true) {
++		i = 0;
++		addr = (u32 *)&(uwtbl[WF_UWTBL_RX_BIPN_31_0__DW*4]);
++		dw_value = *addr;
++
++		while (WTBL_UMAC_DW4_BIPN[i].name) {
++			seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW4_BIPN[i].name,
++				(dw_value & WTBL_UMAC_DW4_BIPN[i].mask) >>
++					WTBL_UMAC_DW4_BIPN[i].shift);
++			i++;
++		}
++
++		i = 0;
++		addr = (u32 *)&(uwtbl[WF_UWTBL_RX_BIPN_47_32__DW*4]);
++		dw_value = *addr;
++
++		while (WTBL_UMAC_DW5_BIPN[i].name) {
++			seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW5_BIPN[i].name,
++				(dw_value & WTBL_UMAC_DW5_BIPN[i].mask) >>
++				WTBL_UMAC_DW5_BIPN[i].shift);
++			i++;
++		}
++	}
++}
++
++static void parse_fmac_uwtbl_sn(struct seq_file *s, u8 *uwtbl)
++{
++	u32 *addr = 0;
++	u32 u2SN = 0;
++
++	/* UMAC WTBL DW SN part */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "UWTBL SN\n");
++
++	addr = (u32 *)&(uwtbl[WF_UWTBL_TID0_SN_DW*4]);
++	u2SN = ((*addr) & WF_UWTBL_TID0_SN_MASK) >> WF_UWTBL_TID0_SN_SHIFT;
++	seq_printf(s, "\t%s:%u\n", "TID0_AC0_SN", u2SN);
++
++	addr = (u32 *)&(uwtbl[WF_UWTBL_TID1_SN_DW*4]);
++	u2SN = ((*addr) & WF_UWTBL_TID1_SN_MASK) >> WF_UWTBL_TID1_SN_SHIFT;
++	seq_printf(s, "\t%s:%u\n", "TID1_AC1_SN", u2SN);
++
++	addr = (u32 *)&(uwtbl[WF_UWTBL_TID2_SN_7_0__DW*4]);
++	u2SN = ((*addr) & WF_UWTBL_TID2_SN_7_0__MASK) >>
++				WF_UWTBL_TID2_SN_7_0__SHIFT;
++	addr = (u32 *)&(uwtbl[WF_UWTBL_TID2_SN_11_8__DW*4]);
++	u2SN |= (((*addr) & WF_UWTBL_TID2_SN_11_8__MASK) >>
++			WF_UWTBL_TID2_SN_11_8__SHIFT) << 8;
++	seq_printf(s, "\t%s:%u\n", "TID2_AC2_SN", u2SN);
++
++	addr = (u32 *)&(uwtbl[WF_UWTBL_TID3_SN_DW*4]);
++	u2SN = ((*addr) & WF_UWTBL_TID3_SN_MASK) >> WF_UWTBL_TID3_SN_SHIFT;
++	seq_printf(s, "\t%s:%u\n", "TID3_AC3_SN", u2SN);
++
++	addr = (u32 *)&(uwtbl[WF_UWTBL_TID4_SN_DW*4]);
++	u2SN = ((*addr) & WF_UWTBL_TID4_SN_MASK) >> WF_UWTBL_TID4_SN_SHIFT;
++	seq_printf(s, "\t%s:%u\n", "TID4_SN", u2SN);
++
++	addr = (u32 *)&(uwtbl[WF_UWTBL_TID5_SN_3_0__DW*4]);
++	u2SN = ((*addr) & WF_UWTBL_TID5_SN_3_0__MASK) >>
++				WF_UWTBL_TID5_SN_3_0__SHIFT;
++	addr = (u32 *)&(uwtbl[WF_UWTBL_TID5_SN_11_4__DW*4]);
++	u2SN |= (((*addr) & WF_UWTBL_TID5_SN_11_4__MASK) >>
++				WF_UWTBL_TID5_SN_11_4__SHIFT) << 4;
++	seq_printf(s, "\t%s:%u\n", "TID5_SN", u2SN);
++
++	addr = (u32 *)&(uwtbl[WF_UWTBL_TID6_SN_DW*4]);
++	u2SN = ((*addr) & WF_UWTBL_TID6_SN_MASK) >> WF_UWTBL_TID6_SN_SHIFT;
++	seq_printf(s, "\t%s:%u\n", "TID6_SN", u2SN);
++
++	addr = (u32 *)&(uwtbl[WF_UWTBL_TID7_SN_DW*4]);
++	u2SN = ((*addr) & WF_UWTBL_TID7_SN_MASK) >> WF_UWTBL_TID7_SN_SHIFT;
++	seq_printf(s, "\t%s:%u\n", "TID7_SN", u2SN);
++
++	addr = (u32 *)&(uwtbl[WF_UWTBL_COM_SN_DW*4]);
++	u2SN = ((*addr) & WF_UWTBL_COM_SN_MASK) >> WF_UWTBL_COM_SN_SHIFT;
++	seq_printf(s, "\t%s:%u\n", "COM_SN", u2SN);
++}
++
++static void dump_key_table(
++	struct seq_file *s,
++	uint16_t keyloc0,
++	uint16_t keyloc1,
++	uint16_t keyloc2
++)
++{
++#define ONE_KEY_ENTRY_LEN_IN_DW                8
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	u8 keytbl[ONE_KEY_ENTRY_LEN_IN_DW*4] = {0};
++	uint16_t x;
++
++	seq_printf(s, "\t\n");
++	seq_printf(s, "\t%s:%d\n", "keyloc0", keyloc0);
++	if (keyloc0 != INVALID_KEY_ENTRY) {
++
++		/* Don't swap below two lines, halWtblReadRaw will
++		* write new value WF_WTBLON_TOP_WDUCR_ADDR
++		*/
++		mt7996_wtbl_read_raw(dev, keyloc0,
++			WTBL_TYPE_KEY, 0, ONE_KEY_ENTRY_LEN_IN_DW, keytbl);
++		seq_printf(s, "\t\tKEY WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
++			MT_DBG_UWTBL_TOP_WDUCR_ADDR,
++			mt76_rr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR),
++			KEYTBL_IDX2BASE(keyloc0, 0));
++		for (x = 0; x < ONE_KEY_ENTRY_LEN_IN_DW; x++) {
++			seq_printf(s, "\t\tDW%02d: %02x %02x %02x %02x\n",
++				x,
++				keytbl[x * 4 + 3],
++				keytbl[x * 4 + 2],
++				keytbl[x * 4 + 1],
++				keytbl[x * 4]);
++		}
++	}
++
++	seq_printf(s, "\t%s:%d\n", "keyloc1", keyloc1);
++	if (keyloc1 != INVALID_KEY_ENTRY) {
++		/* Don't swap below two lines, halWtblReadRaw will
++		* write new value WF_WTBLON_TOP_WDUCR_ADDR
++		*/
++		mt7996_wtbl_read_raw(dev, keyloc1,
++			WTBL_TYPE_KEY, 0, ONE_KEY_ENTRY_LEN_IN_DW, keytbl);
++		seq_printf(s, "\t\tKEY WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
++			MT_DBG_UWTBL_TOP_WDUCR_ADDR,
++			mt76_rr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR),
++			KEYTBL_IDX2BASE(keyloc1, 0));
++		for (x = 0; x < ONE_KEY_ENTRY_LEN_IN_DW; x++) {
++			seq_printf(s, "\t\tDW%02d: %02x %02x %02x %02x\n",
++				x,
++				keytbl[x * 4 + 3],
++				keytbl[x * 4 + 2],
++				keytbl[x * 4 + 1],
++				keytbl[x * 4]);
++		}
++	}
++
++	seq_printf(s, "\t%s:%d\n", "keyloc2", keyloc2);
++	if (keyloc2 != INVALID_KEY_ENTRY) {
++		/* Don't swap below two lines, halWtblReadRaw will
++		* write new value WF_WTBLON_TOP_WDUCR_ADDR
++		*/
++		mt7996_wtbl_read_raw(dev, keyloc2,
++			WTBL_TYPE_KEY, 0, ONE_KEY_ENTRY_LEN_IN_DW, keytbl);
++		seq_printf(s, "\t\tKEY WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
++			MT_DBG_UWTBL_TOP_WDUCR_ADDR,
++			mt76_rr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR),
++			KEYTBL_IDX2BASE(keyloc2, 0));
++		for (x = 0; x < ONE_KEY_ENTRY_LEN_IN_DW; x++) {
++			seq_printf(s, "\t\tDW%02d: %02x %02x %02x %02x\n",
++				x,
++				keytbl[x * 4 + 3],
++				keytbl[x * 4 + 2],
++				keytbl[x * 4 + 1],
++				keytbl[x * 4]);
++		}
++	}
++}
++
++static void parse_fmac_uwtbl_key_info(struct seq_file *s, u8 *uwtbl, u8 *lwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	uint16_t keyloc0 = INVALID_KEY_ENTRY;
++	uint16_t keyloc1 = INVALID_KEY_ENTRY;
++	uint16_t keyloc2 = INVALID_KEY_ENTRY;
++
++	/* UMAC WTBL DW 7 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "UWTBL key info\n");
++
++	addr = (u32 *)&(uwtbl[WF_UWTBL_KEY_LOC0_DW*4]);
++	dw_value = *addr;
++	keyloc0 = (dw_value & WF_UWTBL_KEY_LOC0_MASK) >> WF_UWTBL_KEY_LOC0_SHIFT;
++	keyloc1 = (dw_value & WF_UWTBL_KEY_LOC1_MASK) >> WF_UWTBL_KEY_LOC1_SHIFT;
++
++	seq_printf(s, "\t%s:%u/%u\n", "Key Loc 0/1", keyloc0, keyloc1);
++
++	/* UMAC WTBL DW 6 for BIGTK */
++	if (is_wtbl_bigtk_exist(lwtbl) == true) {
++		addr = (u32 *)&(uwtbl[WF_UWTBL_KEY_LOC2_DW*4]);
++		dw_value = *addr;
++		keyloc2 = (dw_value & WF_UWTBL_KEY_LOC2_MASK) >>
++			WF_UWTBL_KEY_LOC2_SHIFT;
++		seq_printf(s, "\t%s:%u\n", "Key Loc 2", keyloc2);
++	}
++
++	/* Parse KEY link */
++	dump_key_table(s, keyloc0, keyloc1, keyloc2);
++}
++
++static const struct berse_wtbl_parse WTBL_UMAC_DW8[] = {
++	{"UWTBL_WMM_Q",		WF_UWTBL_WMM_Q_MASK,		WF_UWTBL_WMM_Q_SHIFT,	false},
++	{"UWTBL_QOS",		WF_UWTBL_QOS_MASK,		NO_SHIFT_DEFINE,	false},
++	{"UWTBL_HT_VHT_HE",	WF_UWTBL_HT_MASK,		NO_SHIFT_DEFINE,	false},
++	{"UWTBL_HDRT_MODE",	WF_UWTBL_HDRT_MODE_MASK,	NO_SHIFT_DEFINE,	true},
++	{NULL,}
++};
++
++static void parse_fmac_uwtbl_msdu_info(struct seq_file *s, u8 *uwtbl)
++{
++	u32 *addr = 0;
++	u32 dw_value = 0;
++	u32 amsdu_len = 0;
++	u16 i = 0;
++
++	/* UMAC WTBL DW 8 */
++	seq_printf(s, "\t\n");
++	seq_printf(s, "UWTBL DW8\n");
++
++	addr = (u32 *)&(uwtbl[WF_UWTBL_AMSDU_CFG_DW*4]);
++	dw_value = *addr;
++
++	while (WTBL_UMAC_DW8[i].name) {
++
++		if (WTBL_UMAC_DW8[i].shift == NO_SHIFT_DEFINE)
++			seq_printf(s, "\t%s:%d\n", WTBL_UMAC_DW8[i].name,
++				(dw_value & WTBL_UMAC_DW8[i].mask) ? 1 : 0);
++		else
++			seq_printf(s, "\t%s:%u\n", WTBL_UMAC_DW8[i].name,
++				(dw_value & WTBL_UMAC_DW8[i].mask) >>
++					WTBL_UMAC_DW8[i].shift);
++		i++;
++	}
++
++	/* UMAC WTBL DW 8 - SEC_ADDR_MODE */
++	addr = (u32 *)&(uwtbl[WF_UWTBL_SEC_ADDR_MODE_DW*4]);
++	dw_value = *addr;
++	seq_printf(s, "\t%s:%lu\n", "SEC_ADDR_MODE",
++		(dw_value & WTBL_SEC_ADDR_MODE_MASK) >> WTBL_SEC_ADDR_MODE_OFFSET);
++
++	/* UMAC WTBL DW 8 - AMSDU_CFG */
++	seq_printf(s, "\t%s:%d\n", "HW AMSDU Enable",
++				(dw_value & WTBL_AMSDU_EN_MASK) ? 1 : 0);
++
++	amsdu_len = (dw_value & WTBL_AMSDU_LEN_MASK) >> WTBL_AMSDU_LEN_OFFSET;
++	if (amsdu_len == 0)
++		seq_printf(s, "\t%s:invalid (WTBL value=0x%x)\n", "HW AMSDU Len",
++			amsdu_len);
++	else if (amsdu_len == 1)
++		seq_printf(s, "\t%s:%d~%d (WTBL value=0x%x)\n", "HW AMSDU Len",
++			1,
++			255,
++			amsdu_len);
++	else if (amsdu_len == 2)
++		seq_printf(s, "\t%s:%d~%d (WTBL value=0x%x)\n", "HW AMSDU Len",
++			256,
++			511,
++			amsdu_len);
++	else if (amsdu_len == 3)
++		seq_printf(s, "\t%s:%d~%d (WTBL value=0x%x)\n", "HW AMSDU Len",
++			512,
++			767,
++			amsdu_len);
++	else
++		seq_printf(s, "\t%s:%d~%d (WTBL value=0x%x)\n", "HW AMSDU Len",
++			256 * (amsdu_len - 1),
++			256 * (amsdu_len - 1) + 255,
++			amsdu_len);
++
++	seq_printf(s, "\t%s:%lu (WTBL value=0x%lx)\n", "HW AMSDU Num",
++		((dw_value & WTBL_AMSDU_NUM_MASK) >> WTBL_AMSDU_NUM_OFFSET) + 1,
++		(dw_value & WTBL_AMSDU_NUM_MASK) >> WTBL_AMSDU_NUM_OFFSET);
++}
++
++static int mt7996_wtbl_read(struct seq_file *s, void *data)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	u8 lwtbl[LWTBL_LEN_IN_DW * 4] = {0};
++	u8 uwtbl[UWTBL_LEN_IN_DW * 4] = {0};
++	int x;
++
++	mt7996_wtbl_read_raw(dev, dev->wlan_idx, WTBL_TYPE_LMAC, 0,
++				 LWTBL_LEN_IN_DW, lwtbl);
++	seq_printf(s, "Dump WTBL info of WLAN_IDX:%d\n", dev->wlan_idx);
++	seq_printf(s, "LMAC WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
++		   MT_DBG_WTBLON_TOP_WDUCR_ADDR,
++		   mt76_rr(dev, MT_DBG_WTBLON_TOP_WDUCR_ADDR),
++		   LWTBL_IDX2BASE(dev->wlan_idx, 0));
++	for (x = 0; x < LWTBL_LEN_IN_DW; x++) {
++		seq_printf(s, "DW%02d: %02x %02x %02x %02x\n",
++			   x,
++			   lwtbl[x * 4 + 3],
++			   lwtbl[x * 4 + 2],
++			   lwtbl[x * 4 + 1],
++			   lwtbl[x * 4]);
++	}
++
++	/* Parse LWTBL */
++	parse_fmac_lwtbl_dw0_1(s, lwtbl);
++	parse_fmac_lwtbl_dw2(s, lwtbl);
++	parse_fmac_lwtbl_dw3(s, lwtbl);
++	parse_fmac_lwtbl_dw4(s, lwtbl);
++	parse_fmac_lwtbl_dw5(s, lwtbl);
++	parse_fmac_lwtbl_dw6(s, lwtbl);
++	parse_fmac_lwtbl_dw7(s, lwtbl);
++	parse_fmac_lwtbl_dw8(s, lwtbl);
++	parse_fmac_lwtbl_dw9(s, lwtbl);
++	parse_fmac_lwtbl_dw10(s, lwtbl);
++	parse_fmac_lwtbl_dw11(s, lwtbl);
++	parse_fmac_lwtbl_dw12(s, lwtbl);
++	parse_fmac_lwtbl_dw13(s, lwtbl);
++	parse_fmac_lwtbl_dw14(s, lwtbl);
++	parse_fmac_lwtbl_mlo_info(s, lwtbl);
++	parse_fmac_lwtbl_dw31(s, lwtbl);
++	parse_fmac_lwtbl_dw32(s, lwtbl);
++	parse_fmac_lwtbl_rx_stats(s, lwtbl);
++
++	mt7996_wtbl_read_raw(dev, dev->wlan_idx, WTBL_TYPE_UMAC, 0,
++				 UWTBL_LEN_IN_DW, uwtbl);
++	seq_printf(s, "Dump WTBL info of WLAN_IDX:%d\n", dev->wlan_idx);
++	seq_printf(s, "UMAC WTBL Addr: group:0x%x=0x%x addr: 0x%lx\n",
++		   MT_DBG_UWTBL_TOP_WDUCR_ADDR,
++		   mt76_rr(dev, MT_DBG_UWTBL_TOP_WDUCR_ADDR),
++		   UWTBL_IDX2BASE(dev->wlan_idx, 0));
++	for (x = 0; x < UWTBL_LEN_IN_DW; x++) {
++		seq_printf(s, "DW%02d: %02x %02x %02x %02x\n",
++			   x,
++			   uwtbl[x * 4 + 3],
++			   uwtbl[x * 4 + 2],
++			   uwtbl[x * 4 + 1],
++			   uwtbl[x * 4]);
++	}
++
++	/* Parse UWTBL */
++	parse_fmac_uwtbl_mlo_info(s, uwtbl);
++	parse_fmac_uwtbl_pn(s, uwtbl, lwtbl);
++	parse_fmac_uwtbl_sn(s, uwtbl);
++	parse_fmac_uwtbl_key_info(s, uwtbl, lwtbl);
++	parse_fmac_uwtbl_msdu_info(s, uwtbl);
++
++	return 0;
++}
++
++static int mt7996_sta_info(struct seq_file *s, void *data)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	u8 lwtbl[LWTBL_LEN_IN_DW*4] = {0};
++	u16 i = 0;
++
++	for (i=0; i < mt7996_wtbl_size(dev); i++) {
++		mt7996_wtbl_read_raw(dev, i, WTBL_TYPE_LMAC, 0,
++				     LWTBL_LEN_IN_DW, lwtbl);
++
++		if (lwtbl[4] || lwtbl[5] || lwtbl[6] || lwtbl[7] || lwtbl[0] || lwtbl[1]) {
++			u32 *addr, dw_value;
++
++			seq_printf(s, "wcid:%d\tAddr: %02x:%02x:%02x:%02x:%02x:%02x",
++					i, lwtbl[4], lwtbl[5], lwtbl[6], lwtbl[7], lwtbl[0], lwtbl[1]);
++
++			addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_2*4]);
++			dw_value = *addr;
++			seq_printf(s, "\t%s:%u", WTBL_LMAC_DW2[0].name,
++					(dw_value & WTBL_LMAC_DW2[0].mask) >> WTBL_LMAC_DW2[0].shift);
++
++			addr = (u32 *)&(lwtbl[WTBL_GROUP_TRX_CAP_DW_5*4]);
++			dw_value = *addr;
++			seq_printf(s, "\tPSM:%u\n", !!(dw_value & WF_LWTBL_PSM_MASK));
++		}
++	}
++
++	return 0;
++}
++
++static int mt7996_token_read(struct seq_file *s, void *data)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	int msdu_id;
++	struct mt76_txwi_cache *txwi;
++
++	seq_printf(s, "Token from host:\n");
++	spin_lock_bh(&dev->mt76.token_lock);
++	idr_for_each_entry(&dev->mt76.token, txwi, msdu_id) {
++		seq_printf(s, "%4d (pending time %u ms)\n", msdu_id,
++			   jiffies_to_msecs(jiffies - txwi->jiffies));
++	}
++	spin_unlock_bh(&dev->mt76.token_lock);
++	seq_printf(s, "\n");
++
++	return 0;
++}
++
++int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
++{
++	struct mt7996_dev *dev = phy->dev;
++	u32 device_id = (dev->mt76.rev) >> 16;
++	int i = 0;
++	static const struct mt7996_dbg_reg_desc dbg_reg_s[] = {
++		{ 0x7990, mt7996_dbg_offs },
++		{ 0x7992, mt7992_dbg_offs },
++	};
++
++	for (i = 0; i < ARRAY_SIZE(dbg_reg_s); i++) {
++		if (device_id == dbg_reg_s[i].id) {
++			dev->dbg_reg = &dbg_reg_s[i];
++			break;
++		}
++	}
++
++	if (is_mt7996(&dev->mt76)) {
++		WTBL_LMAC_DW2 = WTBL_LMAC_DW2_7996;
++		WTBL_LMAC_DW5 = WTBL_LMAC_DW5_7996;
++		WTBL_LMAC_DW9 = WTBL_LMAC_DW9_7996;
++	} else {
++		WTBL_LMAC_DW2 = WTBL_LMAC_DW2_7992;
++		WTBL_LMAC_DW5 = WTBL_LMAC_DW5_7992;
++		WTBL_LMAC_DW9 = WTBL_LMAC_DW9_7992;
++	}
++
++	/* agg */
++	debugfs_create_devm_seqfile(dev->mt76.dev, "agg_info0", dir,
++				    mt7996_agginfo_read_band0);
++	debugfs_create_devm_seqfile(dev->mt76.dev, "agg_info1", dir,
++				    mt7996_agginfo_read_band1);
++	debugfs_create_devm_seqfile(dev->mt76.dev, "agg_info2", dir,
++				    mt7996_agginfo_read_band2);
++	/* amsdu */
++	debugfs_create_devm_seqfile(dev->mt76.dev, "amsdu_info", dir,
++				    mt7996_amsdu_result_read);
++
++	debugfs_create_file("fw_debug_module", 0600, dir, dev,
++			    &fops_fw_debug_module);
++	debugfs_create_file("fw_debug_level", 0600, dir, dev,
++			    &fops_fw_debug_level);
++	debugfs_create_file("fw_wa_query", 0600, dir, dev, &fops_wa_query);
++	debugfs_create_file("fw_wa_set", 0600, dir, dev, &fops_wa_set);
++	debugfs_create_devm_seqfile(dev->mt76.dev, "fw_version", dir,
++				    mt7996_dump_version);
++	debugfs_create_devm_seqfile(dev->mt76.dev, "fw_wa_info", dir,
++				    mt7996_fw_wa_info_read);
++	debugfs_create_devm_seqfile(dev->mt76.dev, "fw_wm_info", dir,
++				    mt7996_fw_wm_info_read);
++
++	debugfs_create_devm_seqfile(dev->mt76.dev, "mib_info0", dir,
++				    mt7996_mibinfo_band0);
++	debugfs_create_devm_seqfile(dev->mt76.dev, "mib_info1", dir,
++				    mt7996_mibinfo_band1);
++	debugfs_create_devm_seqfile(dev->mt76.dev, "mib_info2", dir,
++				    mt7996_mibinfo_band2);
++
++	debugfs_create_devm_seqfile(dev->mt76.dev, "sta_info", dir,
++				    mt7996_sta_info);
++
++	debugfs_create_devm_seqfile(dev->mt76.dev, "tr_info", dir,
++				    mt7996_trinfo_read);
++
++	debugfs_create_devm_seqfile(dev->mt76.dev, "wtbl_info", dir,
++				    mt7996_wtbl_read);
++
++	debugfs_create_devm_seqfile(dev->mt76.dev, "token", dir, mt7996_token_read);
++
++	debugfs_create_u8("sku_disable", 0600, dir, &dev->dbg.sku_disable);
++
++	return 0;
++}
++
++#endif
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+new file mode 100644
+index 00000000..c16b25ab
+--- /dev/null
++++ b/mt7996/mtk_mcu.c
+@@ -0,0 +1,39 @@
++// SPDX-License-Identifier: ISC
++/*
++ * Copyright (C) 2023 MediaTek Inc.
++ */
++
++#include <linux/firmware.h>
++#include <linux/fs.h>
++#include "mt7996.h"
++#include "mcu.h"
++#include "mac.h"
++#include "mtk_mcu.h"
++
++#ifdef CONFIG_MTK_DEBUG
++
++
++
++
++int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val)
++{
++	struct {
++		u8 __rsv1[4];
++
++		__le16 tag;
++		__le16 len;
++
++		__le16 item;
++		u8 __rsv2[2];
++		__le32 value;
++	} __packed req = {
++		.tag = cpu_to_le16(UNI_CMD_MURU_DBG_INFO),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.item = cpu_to_le16(item),
++		.value = cpu_to_le32(val),
++	};
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
++				 sizeof(req), true);
++}
++#endif
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+new file mode 100644
+index 00000000..7f4d4e02
+--- /dev/null
++++ b/mt7996/mtk_mcu.h
+@@ -0,0 +1,19 @@
++/* SPDX-License-Identifier: ISC */
++/*
++ * Copyright (C) 2023 MediaTek Inc.
++ */
++
++#ifndef __MT7996_MTK_MCU_H
++#define __MT7996_MTK_MCU_H
++
++#include "../mt76_connac_mcu.h"
++
++#ifdef CONFIG_MTK_DEBUG
++
++enum {
++	UNI_CMD_MURU_DBG_INFO = 0x18,
++};
++
++#endif
++
++#endif
+diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
+index 3a83e34d..6599c444 100644
+--- a/tools/CMakeLists.txt
++++ b/tools/CMakeLists.txt
+@@ -3,6 +3,13 @@ cmake_minimum_required(VERSION 2.8)
+ PROJECT(mt76-test C)
+ ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3)
+ 
++UNSET(backports_dir CACHE)
++FIND_PATH(
++	backports_dir
++	NAMES "mac80211/uapi/linux"
++)
++INCLUDE_DIRECTORIES("${backports_dir}/mac80211/uapi")
++
+ ADD_EXECUTABLE(mt76-test main.c fields.c eeprom.c fwlog.c)
+ TARGET_LINK_LIBRARIES(mt76-test nl-tiny)
+ 
+diff --git a/tools/fwlog.c b/tools/fwlog.c
+index e5d4a105..3c6a61d7 100644
+--- a/tools/fwlog.c
++++ b/tools/fwlog.c
+@@ -26,7 +26,7 @@ static const char *debugfs_path(const char *phyname, const char *file)
+ 	return path;
+ }
+ 
+-static int mt76_set_fwlog_en(const char *phyname, bool en)
++static int mt76_set_fwlog_en(const char *phyname, bool en, char *val)
+ {
+ 	FILE *f = fopen(debugfs_path(phyname, "fw_debug_bin"), "w");
+ 
+@@ -35,7 +35,13 @@ static int mt76_set_fwlog_en(const char *phyname, bool en)
+ 		return 1;
+ 	}
+ 
+-	fprintf(f, "7");
++	if (en && val)
++		fprintf(f, "%s", val);
++	else if (en)
++		fprintf(f, "7");
++	else
++		fprintf(f, "0");
++
+ 	fclose(f);
+ 
+ 	return 0;
+@@ -76,6 +82,7 @@ static void handle_signal(int sig)
+ 
+ int mt76_fwlog(const char *phyname, int argc, char **argv)
+ {
++#define BUF_SIZE 1504
+ 	struct sockaddr_in local = {
+ 		.sin_family = AF_INET,
+ 		.sin_addr.s_addr = INADDR_ANY,
+@@ -84,9 +91,9 @@ int mt76_fwlog(const char *phyname, int argc, char **argv)
+ 		.sin_family = AF_INET,
+ 		.sin_port = htons(55688),
+ 	};
+-	char buf[1504];
++	char *buf = calloc(BUF_SIZE, sizeof(char));
+ 	int ret = 0;
+-	int yes = 1;
++	/* int yes = 1; */
+ 	int s, fd;
+ 
+ 	if (argc < 1) {
+@@ -105,13 +112,13 @@ int mt76_fwlog(const char *phyname, int argc, char **argv)
+ 		return 1;
+ 	}
+ 
+-	setsockopt(s, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));
++	/* setsockopt(s, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes)); */
+ 	if (bind(s, (struct sockaddr *)&local, sizeof(local)) < 0) {
+ 		perror("bind");
+ 		return 1;
+ 	}
+ 
+-	if (mt76_set_fwlog_en(phyname, true))
++	if (mt76_set_fwlog_en(phyname, true, argv[1]))
+ 		return 1;
+ 
+ 	fd = open(debugfs_path(phyname, "fwlog_data"), O_RDONLY);
+@@ -145,8 +152,8 @@ int mt76_fwlog(const char *phyname, int argc, char **argv)
+ 		if (!r)
+ 			continue;
+ 
+-		if (len > sizeof(buf)) {
+-			fprintf(stderr, "Length error: %d > %d\n", len, (int)sizeof(buf));
++		if (len > BUF_SIZE) {
++			fprintf(stderr, "Length error: %d > %d\n", len, BUF_SIZE);
+ 			ret = 1;
+ 			break;
+ 		}
+@@ -171,7 +178,7 @@ int mt76_fwlog(const char *phyname, int argc, char **argv)
+ 	close(fd);
+ 
+ out:
+-	mt76_set_fwlog_en(phyname, false);
++	mt76_set_fwlog_en(phyname, false, NULL);
+ 
+ 	return ret;
+ }
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0025-mtk-wifi-mt76-mt7996-add-txpower-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0025-mtk-wifi-mt76-mt7996-add-txpower-support.patch
deleted file mode 100644
index eb2c6e8..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0025-mtk-wifi-mt76-mt7996-add-txpower-support.patch
+++ /dev/null
@@ -1,1046 +0,0 @@
-From 186a919f2ff018e53c1e1bc9e015b409525e7273 Mon Sep 17 00:00:00 2001
-From: Allen Ye <allen.ye@mediatek.com>
-Date: Fri, 24 Mar 2023 23:35:30 +0800
-Subject: [PATCH 025/116] mtk: wifi: mt76: mt7996: add txpower support
-
-Add single sku and default enable sku.
-
-mtk: wifi: mt76: mt7996: Porting wifi6 txpower fix to eagle
-
-Refactor txpower flow.
-1. Fix wrong bbp CR address
-2. Ignore RegDB power limit when we have single sku table. And dump more informaiton in debugfs.
-3. Refactor get_txpower ops flow, we only consider CCK and OFDM power value as maximum.
-4. Remove sku_disable due to SQC is over and default enable both sku tables.
-
-Fix wrong power value when user set limit close to path table limit.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Signed-off-by: Allen Ye <allen.ye@mediatek.com>
----
- eeprom.c             |  54 ++++++-
- mt76.h               |   9 ++
- mt76_connac_mcu.c    |   2 +-
- mt7996/eeprom.c      |  34 ++++
- mt7996/eeprom.h      |  42 +++++
- mt7996/init.c        |  16 +-
- mt7996/main.c        |  15 ++
- mt7996/mcu.c         |  69 ++++++++-
- mt7996/mcu.h         |   2 +
- mt7996/mt7996.h      |   4 +
- mt7996/mtk_debugfs.c | 362 +++++++++++++++++++++++++++++++++++++++++++
- mt7996/mtk_mcu.c     |  23 +++
- mt7996/mtk_mcu.h     |  92 +++++++++++
- mt7996/regs.h        |  27 ++--
- 14 files changed, 724 insertions(+), 27 deletions(-)
-
-diff --git a/eeprom.c b/eeprom.c
-index a0047d7..11efe29 100644
---- a/eeprom.c
-+++ b/eeprom.c
-@@ -305,9 +305,10 @@ mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const __be32 *data,
- static void
- mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
- 			     const __be32 *data, size_t len, s8 target_power,
--			     s8 nss_delta, s8 *max_power)
-+			     s8 nss_delta)
- {
- 	int i, cur;
-+	s8 max_power = -128;
- 
- 	if (!data)
- 		return;
-@@ -319,7 +320,7 @@ mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
- 			break;
- 
- 		mt76_apply_array_limit(pwr + pwr_len * i, pwr_len, data + 1,
--				       target_power, nss_delta, max_power);
-+				       target_power, nss_delta, &max_power);
- 		if (--cur > 0)
- 			continue;
- 
-@@ -335,6 +336,7 @@ mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
- s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
- 			      struct ieee80211_channel *chan,
- 			      struct mt76_power_limits *dest,
-+			      struct mt76_power_path_limits *dest_path,
- 			      s8 target_power)
- {
- 	struct mt76_dev *dev = phy->dev;
-@@ -342,16 +344,20 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
- 	const __be32 *val;
- 	char name[16];
- 	u32 mcs_rates = dev->drv->mcs_rates;
--	u32 ru_rates = ARRAY_SIZE(dest->ru[0]);
- 	char band;
- 	size_t len;
--	s8 max_power = 0;
-+	s8 max_power = -127;
-+	s8 max_power_backoff = -127;
- 	s8 txs_delta;
-+	int n_chains = hweight16(phy->chainmask);
-+	s8 target_power_combine = target_power + mt76_tx_power_nss_delta(n_chains);
- 
- 	if (!mcs_rates)
--		mcs_rates = 10;
-+		mcs_rates = 12;
- 
- 	memset(dest, target_power, sizeof(*dest));
-+	if (dest_path != NULL)
-+		memset(dest_path, 0, sizeof(*dest_path));
- 
- 	if (!IS_ENABLED(CONFIG_OF))
- 		return target_power;
-@@ -397,12 +403,44 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
- 	val = mt76_get_of_array(np, "rates-mcs", &len, mcs_rates + 1);
- 	mt76_apply_multi_array_limit(dest->mcs[0], ARRAY_SIZE(dest->mcs[0]),
- 				     ARRAY_SIZE(dest->mcs), val, len,
--				     target_power, txs_delta, &max_power);
-+				     target_power, txs_delta);
- 
--	val = mt76_get_of_array(np, "rates-ru", &len, ru_rates + 1);
-+	val = mt76_get_of_array(np, "rates-ru", &len, ARRAY_SIZE(dest->ru[0]) + 1);
- 	mt76_apply_multi_array_limit(dest->ru[0], ARRAY_SIZE(dest->ru[0]),
- 				     ARRAY_SIZE(dest->ru), val, len,
--				     target_power, txs_delta, &max_power);
-+				     target_power, txs_delta);
-+
-+	val = mt76_get_of_array(np, "rates-eht", &len, ARRAY_SIZE(dest->eht[0]) + 1);
-+	mt76_apply_multi_array_limit(dest->eht[0], ARRAY_SIZE(dest->eht[0]),
-+				     ARRAY_SIZE(dest->eht), val, len,
-+				     target_power, txs_delta);
-+
-+	if (dest_path == NULL)
-+		return max_power;
-+
-+	max_power_backoff = max_power;
-+
-+	val = mt76_get_of_array(np, "paths-cck", &len, ARRAY_SIZE(dest_path->cck));
-+	mt76_apply_array_limit(dest_path->cck, ARRAY_SIZE(dest_path->cck), val,
-+			       target_power_combine, txs_delta, &max_power_backoff);
-+
-+	val = mt76_get_of_array(np, "paths-ofdm", &len, ARRAY_SIZE(dest_path->ofdm));
-+	mt76_apply_array_limit(dest_path->ofdm, ARRAY_SIZE(dest_path->ofdm), val,
-+			       target_power_combine, txs_delta, &max_power_backoff);
-+
-+	val = mt76_get_of_array(np, "paths-ofdm-bf", &len, ARRAY_SIZE(dest_path->ofdm_bf));
-+	mt76_apply_array_limit(dest_path->ofdm_bf, ARRAY_SIZE(dest_path->ofdm_bf), val,
-+			       target_power_combine, txs_delta, &max_power_backoff);
-+
-+	val = mt76_get_of_array(np, "paths-ru", &len, ARRAY_SIZE(dest_path->ru[0]) + 1);
-+	mt76_apply_multi_array_limit(dest_path->ru[0], ARRAY_SIZE(dest_path->ru[0]),
-+				     ARRAY_SIZE(dest_path->ru), val, len,
-+				     target_power_combine, txs_delta);
-+
-+	val = mt76_get_of_array(np, "paths-ru-bf", &len, ARRAY_SIZE(dest_path->ru_bf[0]) + 1);
-+	mt76_apply_multi_array_limit(dest_path->ru_bf[0], ARRAY_SIZE(dest_path->ru_bf[0]),
-+				     ARRAY_SIZE(dest_path->ru_bf), val, len,
-+				     target_power_combine, txs_delta);
- 
- 	return max_power;
- }
-diff --git a/mt76.h b/mt76.h
-index c591f67..c372e78 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -1059,6 +1059,14 @@ struct mt76_power_limits {
- 	s8 eht[16][16];
- };
- 
-+struct mt76_power_path_limits {
-+	s8 cck[5];
-+	s8 ofdm[5];
-+	s8 ofdm_bf[4];
-+	s8 ru[16][15];
-+	s8 ru_bf[16][15];
-+};
-+
- struct mt76_ethtool_worker_info {
- 	u64 *data;
- 	int idx;
-@@ -1669,6 +1677,7 @@ mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan);
- s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
- 			      struct ieee80211_channel *chan,
- 			      struct mt76_power_limits *dest,
-+			      struct mt76_power_path_limits *dest_path,
- 			      s8 target_power);
- 
- static inline bool mt76_queue_is_wed_tx_free(struct mt76_queue *q)
-diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
-index 1e34e0a..0c7b693 100644
---- a/mt76_connac_mcu.c
-+++ b/mt76_connac_mcu.c
-@@ -2150,7 +2150,7 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
- 			sar_power = mt76_get_sar_power(phy, &chan, reg_power);
- 
- 			mt76_get_rate_power_limits(phy, &chan, limits,
--						   sar_power);
-+						   NULL, sar_power);
- 
- 			tx_power_tlv.last_msg = ch_list[idx] == last_ch;
- 			sku_tlbv.channel = ch_list[idx];
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 4afa2a2..6ac992a 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -408,3 +408,37 @@ s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band)
- 
- 	return val & MT_EE_RATE_DELTA_SIGN ? delta : -delta;
- }
-+
-+const u8 mt7996_sku_group_len[] = {
-+	[SKU_CCK] = 4,
-+	[SKU_OFDM] = 8,
-+	[SKU_HT20] = 8,
-+	[SKU_HT40] = 9,
-+	[SKU_VHT20] = 12,
-+	[SKU_VHT40] = 12,
-+	[SKU_VHT80] = 12,
-+	[SKU_VHT160] = 12,
-+	[SKU_HE26] = 12,
-+	[SKU_HE52] = 12,
-+	[SKU_HE106] = 12,
-+	[SKU_HE242] = 12,
-+	[SKU_HE484] = 12,
-+	[SKU_HE996] = 12,
-+	[SKU_HE2x996] = 12,
-+	[SKU_EHT26] = 16,
-+	[SKU_EHT52] = 16,
-+	[SKU_EHT106] = 16,
-+	[SKU_EHT242] = 16,
-+	[SKU_EHT484] = 16,
-+	[SKU_EHT996] = 16,
-+	[SKU_EHT2x996] = 16,
-+	[SKU_EHT4x996] = 16,
-+	[SKU_EHT26_52] = 16,
-+	[SKU_EHT26_106] = 16,
-+	[SKU_EHT484_242] = 16,
-+	[SKU_EHT996_484] = 16,
-+	[SKU_EHT996_484_242] = 16,
-+	[SKU_EHT2x996_484] = 16,
-+	[SKU_EHT3x996] = 16,
-+	[SKU_EHT3x996_484] = 16,
-+};
-diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index 58179c0..b19ff06 100644
---- a/mt7996/eeprom.h
-+++ b/mt7996/eeprom.h
-@@ -125,4 +125,46 @@ mt7996_get_channel_group_6g(int channel)
- 	return DIV_ROUND_UP(channel - 29, 32);
- }
- 
-+enum mt7996_sku_rate_group {
-+	SKU_CCK,
-+	SKU_OFDM,
-+
-+	SKU_HT20,
-+	SKU_HT40,
-+
-+	SKU_VHT20,
-+	SKU_VHT40,
-+	SKU_VHT80,
-+	SKU_VHT160,
-+
-+	SKU_HE26,
-+	SKU_HE52,
-+	SKU_HE106,
-+	SKU_HE242,
-+	SKU_HE484,
-+	SKU_HE996,
-+	SKU_HE2x996,
-+
-+	SKU_EHT26,
-+	SKU_EHT52,
-+	SKU_EHT106,
-+	SKU_EHT242,
-+	SKU_EHT484,
-+	SKU_EHT996,
-+	SKU_EHT2x996,
-+	SKU_EHT4x996,
-+	SKU_EHT26_52,
-+	SKU_EHT26_106,
-+	SKU_EHT484_242,
-+	SKU_EHT996_484,
-+	SKU_EHT996_484_242,
-+	SKU_EHT2x996_484,
-+	SKU_EHT3x996,
-+	SKU_EHT3x996_484,
-+
-+	MAX_SKU_RATE_GROUP_NUM,
-+};
-+
-+extern const u8 mt7996_sku_group_len[MAX_SKU_RATE_GROUP_NUM];
-+
- #endif
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 6a394c3..f1d6817 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -295,7 +295,12 @@ static void __mt7996_init_txpower(struct mt7996_phy *phy,
- 	int nss_delta = mt76_tx_power_nss_delta(nss);
- 	int pwr_delta = mt7996_eeprom_get_power_delta(dev, sband->band);
- 	struct mt76_power_limits limits;
-+	struct mt76_power_path_limits limits_path;
-+	struct device_node *np;
- 
-+	phy->sku_limit_en = true;
-+	phy->sku_path_en = true;
-+	np = mt76_find_power_limits_node(&dev->mt76);
- 	for (i = 0; i < sband->n_channels; i++) {
- 		struct ieee80211_channel *chan = &sband->channels[i];
- 		int target_power = mt7996_eeprom_get_target_power(dev, chan);
-@@ -303,11 +308,18 @@ static void __mt7996_init_txpower(struct mt7996_phy *phy,
- 		target_power += pwr_delta;
- 		target_power = mt76_get_rate_power_limits(phy->mt76, chan,
- 							  &limits,
-+							  &limits_path,
- 							  target_power);
-+		if (!limits_path.ofdm[0])
-+			phy->sku_path_en = false;
-+
- 		target_power += nss_delta;
- 		target_power = DIV_ROUND_UP(target_power, 2);
--		chan->max_power = min_t(int, chan->max_reg_power,
--					target_power);
-+		if (!np)
-+			chan->max_power = min_t(int, chan->max_reg_power,
-+						target_power);
-+		else
-+			chan->max_power = target_power;
- 		chan->orig_mpwr = target_power;
- 	}
- }
-diff --git a/mt7996/main.c b/mt7996/main.c
-index eb213f8..46e3e5a 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -85,6 +85,21 @@ int mt7996_run(struct ieee80211_hw *hw)
- 	if (ret)
- 		goto out;
- 
-+#ifdef CONFIG_MTK_DEBUG
-+	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
-+						dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
-+
-+	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
-+						dev->dbg.sku_disable ? 0 : phy->sku_path_en);
-+#else
-+	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
-+						phy->sku_limit_en);
-+	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
-+						phy->sku_path_en);
-+#endif
-+	if (ret)
-+		goto out;
-+
- 	set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
- 
- 	ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 16a01b1..ffd3536 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -4665,9 +4665,31 @@ int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id)
- 				 sizeof(req), true);
- }
- 
-+static void
-+mt7996_update_max_txpower_cur(struct mt7996_phy *phy, int tx_power)
-+{
-+	struct mt76_phy *mphy = phy->mt76;
-+	struct ieee80211_channel *chan = mphy->main_chan;
-+	int e2p_power_limit = 0;
-+
-+	if (chan == NULL) {
-+		mphy->txpower_cur = tx_power;
-+		return;
-+	}
-+
-+	e2p_power_limit = mt7996_eeprom_get_target_power(phy->dev, chan);
-+	e2p_power_limit += mt7996_eeprom_get_power_delta(phy->dev, chan->band);
-+
-+	if (phy->sku_limit_en)
-+		mphy->txpower_cur = min_t(int, e2p_power_limit, tx_power);
-+	else
-+		mphy->txpower_cur = e2p_power_limit;
-+}
-+
- int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
- {
- #define TX_POWER_LIMIT_TABLE_RATE	0
-+#define TX_POWER_LIMIT_TABLE_PATH	1
- 	struct mt7996_dev *dev = phy->dev;
- 	struct mt76_phy *mphy = phy->mt76;
- 	struct ieee80211_hw *hw = mphy->hw;
-@@ -4687,13 +4709,22 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
- 		.band_idx = phy->mt76->band_idx,
- 	};
- 	struct mt76_power_limits la = {};
-+	struct mt76_power_path_limits la_path = {};
- 	struct sk_buff *skb;
--	int i, tx_power;
-+	int i, ret, txpower_limit;
-+
-+	if (hw->conf.power_level == INT_MIN)
-+		hw->conf.power_level = 127;
-+	txpower_limit = mt7996_get_power_bound(phy, hw->conf.power_level);
- 
--	tx_power = mt7996_get_power_bound(phy, hw->conf.power_level);
--	tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
--					      &la, tx_power);
--	mphy->txpower_cur = tx_power;
-+	if (phy->sku_limit_en) {
-+		txpower_limit = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
-+							   &la, &la_path, txpower_limit);
-+		mt7996_update_max_txpower_cur(phy, txpower_limit);
-+	} else {
-+		mt7996_update_max_txpower_cur(phy, txpower_limit);
-+		return 0;
-+	}
- 
- 	skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
- 				 sizeof(req) + MT7996_SKU_PATH_NUM);
-@@ -4723,6 +4754,34 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
- 	/* padding */
- 	skb_put_zero(skb, MT7996_SKU_PATH_NUM - MT7996_SKU_RATE_NUM);
- 
-+	ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
-+				    MCU_WM_UNI_CMD(TXPOWER), true);
-+	if (ret)
-+		return ret;
-+
-+	/* only set per-path power table when it's configured */
-+	if (!phy->sku_path_en)
-+		return 0;
-+
-+	skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
-+				 sizeof(req) + MT7996_SKU_PATH_NUM);
-+	if (!skb)
-+		return -ENOMEM;
-+	req.power_limit_type = TX_POWER_LIMIT_TABLE_PATH;
-+
-+	skb_put_data(skb, &req, sizeof(req));
-+	skb_put_data(skb, &la_path.cck, sizeof(la_path.cck));
-+	skb_put_data(skb, &la_path.ofdm, sizeof(la_path.ofdm));
-+	skb_put_data(skb, &la_path.ofdm_bf, sizeof(la_path.ofdm_bf));
-+
-+	for (i = 0; i < 32; i++) {
-+		bool bf = i % 2;
-+		u8 idx = i / 2;
-+		s8 *buf = bf ? la_path.ru_bf[idx] : la_path.ru[idx];
-+
-+		skb_put_data(skb, buf, sizeof(la_path.ru[0]));
-+	}
-+
- 	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
- 				     MCU_WM_UNI_CMD(TXPOWER), true);
- }
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 5953b25..5a60ccc 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -911,6 +911,7 @@ struct tx_power_ctrl {
- 		bool ate_mode_enable;
- 		bool percentage_ctrl_enable;
- 		bool bf_backoff_enable;
-+		u8 show_info_category;
- 		u8 power_drop_level;
- 	};
- 	u8 band_idx;
-@@ -924,6 +925,7 @@ enum {
- 	UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL = 3,
- 	UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL = 4,
- 	UNI_TXPOWER_ATE_MODE_CTRL = 6,
-+	UNI_TXPOWER_SHOW_INFO = 7,
- };
- 
- enum {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 186af3c..6ab45cc 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -296,6 +296,9 @@ struct mt7996_phy {
- 
- 	struct mt7996_scs_ctrl scs_ctrl;
- 
-+	bool sku_limit_en;
-+	bool sku_path_en;
-+
- #ifdef CONFIG_NL80211_TESTMODE
- 	struct {
- 		u32 *reg_backup;
-@@ -617,6 +620,7 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy);
- #ifdef CONFIG_NL80211_TESTMODE
- void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
- #endif
-+int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
- int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
- void mt7996_mcu_scs_sta_poll(struct work_struct *work);
- 
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index 24b331e..74aabf0 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -2440,6 +2440,364 @@ mt7996_scs_enable_set(void *data, u64 val)
- DEFINE_DEBUGFS_ATTRIBUTE(fops_scs_enable, NULL,
- 			 mt7996_scs_enable_set, "%lld\n");
- 
-+static int
-+mt7996_txpower_level_set(void *data, u64 val)
-+{
-+	struct mt7996_phy *phy = data;
-+	int ret;
-+
-+	if (val > 100)
-+		return -EINVAL;
-+
-+	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_PERCENTAGE_CTRL, !!val);
-+	if (ret)
-+		return ret;
-+
-+	return mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_PERCENTAGE_DROP_CTRL, val);
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_txpower_level, NULL,
-+			 mt7996_txpower_level_set, "%lld\n");
-+
-+static ssize_t
-+mt7996_get_txpower_info(struct file *file, char __user *user_buf,
-+			size_t count, loff_t *ppos)
-+{
-+	struct mt7996_phy *phy = file->private_data;
-+	struct mt7996_mcu_txpower_event *event;
-+	struct txpower_basic_info *basic_info;
-+	struct device_node *np;
-+	static const size_t size = 2048;
-+	int len = 0;
-+	ssize_t ret;
-+	char *buf;
-+
-+	buf = kzalloc(size, GFP_KERNEL);
-+	event = kzalloc(sizeof(*event), GFP_KERNEL);
-+	if (!buf || !event) {
-+		ret = -ENOMEM;
-+		goto out;
-+	}
-+
-+	ret = mt7996_mcu_get_tx_power_info(phy, BASIC_INFO, event);
-+	if (ret ||
-+	    le32_to_cpu(event->basic_info.category) != UNI_TXPOWER_BASIC_INFO)
-+		goto out;
-+
-+	basic_info = &event->basic_info;
-+
-+	len += scnprintf(buf + len, size - len,
-+			 "======================== BASIC INFO ========================\n");
-+	len += scnprintf(buf + len, size - len, "    Band Index: %d, Channel Band: %d\n",
-+			 basic_info->band_idx, basic_info->band);
-+	len += scnprintf(buf + len, size - len, "    PA Type: %s\n",
-+			 basic_info->is_epa ? "ePA" : "iPA");
-+	len += scnprintf(buf + len, size - len, "    LNA Type: %s\n",
-+			 basic_info->is_elna ? "eLNA" : "iLNA");
-+
-+	len += scnprintf(buf + len, size - len,
-+			 "------------------------------------------------------------\n");
-+	len += scnprintf(buf + len, size - len, "    SKU: %s\n",
-+			 basic_info->sku_enable ? "enable" : "disable");
-+	len += scnprintf(buf + len, size - len, "    Percentage Control: %s\n",
-+			 basic_info->percentage_ctrl_enable ? "enable" : "disable");
-+	len += scnprintf(buf + len, size - len, "    Power Drop: %d [dBm]\n",
-+			 basic_info->power_drop_level >> 1);
-+	len += scnprintf(buf + len, size - len, "    Backoff: %s\n",
-+			 basic_info->bf_backoff_enable ? "enable" : "disable");
-+	len += scnprintf(buf + len, size - len, "    TX Front-end Loss:  %d, %d, %d, %d\n",
-+			 basic_info->front_end_loss_tx[0], basic_info->front_end_loss_tx[1],
-+			 basic_info->front_end_loss_tx[2], basic_info->front_end_loss_tx[3]);
-+	len += scnprintf(buf + len, size - len, "    RX Front-end Loss:  %d, %d, %d, %d\n",
-+			 basic_info->front_end_loss_rx[0], basic_info->front_end_loss_rx[1],
-+			 basic_info->front_end_loss_rx[2], basic_info->front_end_loss_rx[3]);
-+	len += scnprintf(buf + len, size - len,
-+			 "    MU TX Power Mode:  %s\n",
-+			 basic_info->mu_tx_power_manual_enable ? "manual" : "auto");
-+	len += scnprintf(buf + len, size - len,
-+			 "    MU TX Power (Auto / Manual): %d / %d [0.5 dBm]\n",
-+			 basic_info->mu_tx_power_auto, basic_info->mu_tx_power_manual);
-+	len += scnprintf(buf + len, size - len,
-+			 "    Thermal Compensation:  %s\n",
-+			 basic_info->thermal_compensate_enable ? "enable" : "disable");
-+	len += scnprintf(buf + len, size - len,
-+			 "    Theraml Compensation Value: %d\n",
-+			 basic_info->thermal_compensate_value);
-+	np = mt76_find_power_limits_node(phy->mt76->dev);
-+	len += scnprintf(buf + len, size - len,
-+			 "    RegDB:  %s\n",
-+			 !np ? "enable" : "disable");
-+	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-+
-+out:
-+	kfree(buf);
-+	kfree(event);
-+	return ret;
-+}
-+
-+static const struct file_operations mt7996_txpower_info_fops = {
-+	.read = mt7996_get_txpower_info,
-+	.open = simple_open,
-+	.owner = THIS_MODULE,
-+	.llseek = default_llseek,
-+};
-+
-+#define mt7996_txpower_puts(rate, _len)							\
-+({											\
-+	len += scnprintf(buf + len, size - len, "%-*s:", _len, #rate " (TMAC)");	\
-+	for (i = 0; i < mt7996_sku_group_len[SKU_##rate]; i++, offs++)			\
-+		len += scnprintf(buf + len, size - len, " %6d",				\
-+				 event->phy_rate_info.frame_power[offs][band_idx]);	\
-+	len += scnprintf(buf + len, size - len, "\n");					\
-+})
-+
-+static ssize_t
-+mt7996_get_txpower_sku(struct file *file, char __user *user_buf,
-+		       size_t count, loff_t *ppos)
-+{
-+	struct mt7996_phy *phy = file->private_data;
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt7996_mcu_txpower_event *event;
-+	struct ieee80211_channel *chan = phy->mt76->chandef.chan;
-+	struct ieee80211_supported_band sband;
-+	u8 band_idx = phy->mt76->band_idx;
-+	static const size_t size = 5120;
-+	int i, offs = 0, len = 0;
-+	u32 target_power = 0;
-+	int n_chains = hweight16(phy->mt76->chainmask);
-+	int nss_delta = mt76_tx_power_nss_delta(n_chains);
-+	int pwr_delta;
-+	ssize_t ret;
-+	char *buf;
-+	u32 reg;
-+
-+	buf = kzalloc(size, GFP_KERNEL);
-+	event = kzalloc(sizeof(*event), GFP_KERNEL);
-+	if (!buf || !event) {
-+		ret = -ENOMEM;
-+		goto out;
-+	}
-+
-+	ret = mt7996_mcu_get_tx_power_info(phy, PHY_RATE_INFO, event);
-+	if (ret ||
-+	    le32_to_cpu(event->phy_rate_info.category) != UNI_TXPOWER_PHY_RATE_INFO)
-+		goto out;
-+
-+	len += scnprintf(buf + len, size - len,
-+			 "\nPhy %d TX Power Table (Channel %d)\n",
-+			 band_idx, phy->mt76->chandef.chan->hw_value);
-+	len += scnprintf(buf + len, size - len, "%-21s  %6s %6s %6s %6s\n",
-+			 " ", "1m", "2m", "5m", "11m");
-+	mt7996_txpower_puts(CCK, 21);
-+
-+	len += scnprintf(buf + len, size - len,
-+			 "%-21s  %6s %6s %6s %6s %6s %6s %6s %6s\n",
-+			 " ", "6m", "9m", "12m", "18m", "24m", "36m", "48m",
-+			 "54m");
-+	mt7996_txpower_puts(OFDM, 21);
-+
-+	len += scnprintf(buf + len, size - len,
-+			 "%-21s  %6s %6s %6s %6s %6s %6s %6s %6s\n",
-+			 " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4",
-+			 "mcs5", "mcs6", "mcs7");
-+	mt7996_txpower_puts(HT20, 21);
-+
-+	len += scnprintf(buf + len, size - len,
-+			 "%-21s  %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
-+			 " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
-+			 "mcs6", "mcs7", "mcs32");
-+	mt7996_txpower_puts(HT40, 21);
-+
-+	len += scnprintf(buf + len, size - len,
-+			 "%-21s  %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
-+			 " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
-+			 "mcs6", "mcs7", "mcs8", "mcs9", "mcs10", "mcs11");
-+	mt7996_txpower_puts(VHT20, 21);
-+	mt7996_txpower_puts(VHT40, 21);
-+	mt7996_txpower_puts(VHT80, 21);
-+	mt7996_txpower_puts(VHT160, 21);
-+	mt7996_txpower_puts(HE26, 21);
-+	mt7996_txpower_puts(HE52, 21);
-+	mt7996_txpower_puts(HE106, 21);
-+	len += scnprintf(buf + len, size - len, "BW20/");
-+	mt7996_txpower_puts(HE242, 16);
-+	len += scnprintf(buf + len, size - len, "BW40/");
-+	mt7996_txpower_puts(HE484, 16);
-+	len += scnprintf(buf + len, size - len, "BW80/");
-+	mt7996_txpower_puts(HE996, 16);
-+	len += scnprintf(buf + len, size - len, "BW160/");
-+	mt7996_txpower_puts(HE2x996, 15);
-+
-+	len += scnprintf(buf + len, size - len,
-+			 "%-21s  %6s %6s %6s %6s %6s %6s %6s %6s ",
-+			 " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5", "mcs6", "mcs7");
-+	len += scnprintf(buf + len, size - len,
-+			 "%6s %6s %6s %6s %6s %6s %6s %6s\n",
-+			 "mcs8", "mcs9", "mcs10", "mcs11", "mcs12", "mcs13", "mcs14", "mcs15");
-+	mt7996_txpower_puts(EHT26, 21);
-+	mt7996_txpower_puts(EHT52, 21);
-+	mt7996_txpower_puts(EHT106, 21);
-+	len += scnprintf(buf + len, size - len, "BW20/");
-+	mt7996_txpower_puts(EHT242, 16);
-+	len += scnprintf(buf + len, size - len, "BW40/");
-+	mt7996_txpower_puts(EHT484, 16);
-+	len += scnprintf(buf + len, size - len, "BW80/");
-+	mt7996_txpower_puts(EHT996, 16);
-+	len += scnprintf(buf + len, size - len, "BW160/");
-+	mt7996_txpower_puts(EHT2x996, 15);
-+	len += scnprintf(buf + len, size - len, "BW320/");
-+	mt7996_txpower_puts(EHT4x996, 15);
-+	mt7996_txpower_puts(EHT26_52, 21);
-+	mt7996_txpower_puts(EHT26_106, 21);
-+	mt7996_txpower_puts(EHT484_242, 21);
-+	mt7996_txpower_puts(EHT996_484, 21);
-+	mt7996_txpower_puts(EHT996_484_242, 21);
-+	mt7996_txpower_puts(EHT2x996_484, 21);
-+	mt7996_txpower_puts(EHT3x996, 21);
-+	mt7996_txpower_puts(EHT3x996_484, 21);
-+
-+	len += scnprintf(buf + len, size - len, "\nePA Gain: %d\n",
-+			 event->phy_rate_info.epa_gain);
-+	len += scnprintf(buf + len, size - len, "Max Power Bound: %d\n",
-+			 event->phy_rate_info.max_power_bound);
-+	len += scnprintf(buf + len, size - len, "Min Power Bound: %d\n",
-+			 event->phy_rate_info.min_power_bound);
-+
-+	reg = MT_WF_PHYDFE_TSSI_TXCTRL01(band_idx);
-+	len += scnprintf(buf + len, size - len,
-+			 "\nBBP TX Power (target power from TMAC)  : %6ld [0.5 dBm]\n",
-+			 mt76_get_field(dev, reg, MT_WF_PHYDFE_TSSI_TXCTRL_POWER_TMAC));
-+	len += scnprintf(buf + len, size - len,
-+			 "RegDB maximum power:\t%d [dBm]\n",
-+			 chan->max_reg_power);
-+
-+	if (chan->band == NL80211_BAND_2GHZ)
-+		sband = phy->mt76->sband_2g.sband;
-+	else if (chan->band == NL80211_BAND_5GHZ)
-+		sband = phy->mt76->sband_5g.sband;
-+	else if (chan->band == NL80211_BAND_6GHZ)
-+		sband = phy->mt76->sband_6g.sband;
-+
-+	pwr_delta = mt7996_eeprom_get_power_delta(dev, sband.band);
-+
-+	target_power = max_t(u32, target_power, mt7996_eeprom_get_target_power(dev, chan));
-+	target_power += pwr_delta + nss_delta;
-+	target_power = DIV_ROUND_UP(target_power, 2);
-+	len += scnprintf(buf + len, size - len,
-+			 "eeprom maximum power:\t%d [dBm]\n",
-+			 target_power);
-+
-+	len += scnprintf(buf + len, size - len,
-+			 "nss_delta:\t%d [0.5 dBm]\n",
-+			 nss_delta);
-+
-+	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-+
-+out:
-+	kfree(buf);
-+	kfree(event);
-+	return ret;
-+}
-+
-+static const struct file_operations mt7996_txpower_sku_fops = {
-+	.read = mt7996_get_txpower_sku,
-+	.open = simple_open,
-+	.owner = THIS_MODULE,
-+	.llseek = default_llseek,
-+};
-+
-+#define mt7996_txpower_path_puts(rate, arr_length)					\
-+({											\
-+	len += scnprintf(buf + len, size - len, "%23s:", #rate " (TMAC)");		\
-+	for (i = 0; i < arr_length; i++, offs++)					\
-+		len += scnprintf(buf + len, size - len, " %4d",				\
-+				 event->backoff_table_info.frame_power[offs]);		\
-+	len += scnprintf(buf + len, size - len, "\n");					\
-+})
-+
-+static ssize_t
-+mt7996_get_txpower_path(struct file *file, char __user *user_buf,
-+		       size_t count, loff_t *ppos)
-+{
-+	struct mt7996_phy *phy = file->private_data;
-+	struct mt7996_mcu_txpower_event *event;
-+	static const size_t size = 5120;
-+	int i, offs = 0, len = 0;
-+	ssize_t ret;
-+	char *buf;
-+
-+	buf = kzalloc(size, GFP_KERNEL);
-+	event = kzalloc(sizeof(*event), GFP_KERNEL);
-+	if (!buf || !event) {
-+		ret = -ENOMEM;
-+		goto out;
-+	}
-+
-+	ret = mt7996_mcu_get_tx_power_info(phy, BACKOFF_TABLE_INFO, event);
-+	if (ret ||
-+	    le32_to_cpu(event->phy_rate_info.category) != UNI_TXPOWER_BACKOFF_TABLE_SHOW_INFO)
-+		goto out;
-+
-+	len += scnprintf(buf + len, size - len, "\n%*c", 25, ' ');
-+	len += scnprintf(buf + len, size - len, "1T1S/2T1S/3T1S/4T1S/5T1S/2T2S/3T2S/4T2S/5T2S/"
-+			 "3T3S/4T3S/5T3S/4T4S/5T4S/5T5S\n");
-+
-+	mt7996_txpower_path_puts(CCK, 5);
-+	mt7996_txpower_path_puts(OFDM, 5);
-+	mt7996_txpower_path_puts(BF-OFDM, 4);
-+
-+	mt7996_txpower_path_puts(RU26, 15);
-+	mt7996_txpower_path_puts(BF-RU26, 15);
-+	mt7996_txpower_path_puts(RU52, 15);
-+	mt7996_txpower_path_puts(BF-RU52, 15);
-+	mt7996_txpower_path_puts(RU26_52, 15);
-+	mt7996_txpower_path_puts(BF-RU26_52, 15);
-+	mt7996_txpower_path_puts(RU106, 15);
-+	mt7996_txpower_path_puts(BF-RU106, 15);
-+	mt7996_txpower_path_puts(RU106_52, 15);
-+	mt7996_txpower_path_puts(BF-RU106_52, 15);
-+
-+	mt7996_txpower_path_puts(BW20/RU242, 15);
-+	mt7996_txpower_path_puts(BF-BW20/RU242, 15);
-+	mt7996_txpower_path_puts(BW40/RU484, 15);
-+	mt7996_txpower_path_puts(BF-BW40/RU484, 15);
-+	mt7996_txpower_path_puts(RU242_484, 15);
-+	mt7996_txpower_path_puts(BF-RU242_484, 15);
-+	mt7996_txpower_path_puts(BW80/RU996, 15);
-+	mt7996_txpower_path_puts(BF-BW80/RU996, 15);
-+	mt7996_txpower_path_puts(RU484_996, 15);
-+	mt7996_txpower_path_puts(BF-RU484_996, 15);
-+	mt7996_txpower_path_puts(RU242_484_996, 15);
-+	mt7996_txpower_path_puts(BF-RU242_484_996, 15);
-+	mt7996_txpower_path_puts(BW160/RU996x2, 15);
-+	mt7996_txpower_path_puts(BF-BW160/RU996x2, 15);
-+	mt7996_txpower_path_puts(RU484_996x2, 15);
-+	mt7996_txpower_path_puts(BF-RU484_996x2, 15);
-+	mt7996_txpower_path_puts(RU996x3, 15);
-+	mt7996_txpower_path_puts(BF-RU996x3, 15);
-+	mt7996_txpower_path_puts(RU484_996x3, 15);
-+	mt7996_txpower_path_puts(BF-RU484_996x3, 15);
-+	mt7996_txpower_path_puts(BW320/RU996x4, 15);
-+	mt7996_txpower_path_puts(BF-BW320/RU996x4, 15);
-+
-+	len += scnprintf(buf + len, size - len, "\nBackoff table: %s\n",
-+			 event->backoff_table_info.backoff_en ? "enable" : "disable");
-+
-+	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-+
-+out:
-+	kfree(buf);
-+	kfree(event);
-+	return ret;
-+}
-+
-+static const struct file_operations mt7996_txpower_path_fops = {
-+	.read = mt7996_get_txpower_path,
-+	.open = simple_open,
-+	.owner = THIS_MODULE,
-+	.llseek = default_llseek,
-+};
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- 	struct mt7996_dev *dev = phy->dev;
-@@ -2503,6 +2861,10 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- 
- 	debugfs_create_devm_seqfile(dev->mt76.dev, "tr_info", dir,
- 				    mt7996_trinfo_read);
-+	debugfs_create_file("txpower_level", 0600, dir, phy, &fops_txpower_level);
-+	debugfs_create_file("txpower_info", 0600, dir, phy, &mt7996_txpower_info_fops);
-+	debugfs_create_file("txpower_sku", 0600, dir, phy, &mt7996_txpower_sku_fops);
-+	debugfs_create_file("txpower_path", 0600, dir, phy, &mt7996_txpower_path_fops);
- 
- 	debugfs_create_devm_seqfile(dev->mt76.dev, "wtbl_info", dir,
- 				    mt7996_wtbl_read);
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index c16b25a..e56ddd8 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -12,8 +12,31 @@
- 
- #ifdef CONFIG_MTK_DEBUG
- 
-+int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct tx_power_ctrl req = {
-+		.tag = cpu_to_le16(UNI_TXPOWER_SHOW_INFO),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.power_ctrl_id = UNI_TXPOWER_SHOW_INFO,
-+		.show_info_category = category,
-+		.band_idx = phy->mt76->band_idx,
-+	};
-+	struct sk_buff *skb;
-+	int ret;
- 
-+	ret = mt76_mcu_send_and_get_msg(&dev->mt76,
-+					MCU_WM_UNI_CMD_QUERY(TXPOWER),
-+					&req, sizeof(req), true, &skb);
-+	if (ret)
-+		return ret;
- 
-+	memcpy(event, skb->data, sizeof(struct mt7996_mcu_txpower_event));
-+
-+	dev_kfree_skb(skb);
-+
-+	return 0;
-+}
- 
- int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val)
- {
-diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
-index 7f4d4e0..c30418c 100644
---- a/mt7996/mtk_mcu.h
-+++ b/mt7996/mtk_mcu.h
-@@ -14,6 +14,98 @@ enum {
- 	UNI_CMD_MURU_DBG_INFO = 0x18,
- };
- 
-+struct txpower_basic_info {
-+	u8 category;
-+	u8 rsv1;
-+
-+	/* basic info */
-+	u8 band_idx;
-+	u8 band;
-+
-+	/* board type info */
-+	bool is_epa;
-+	bool is_elna;
-+
-+	/* power percentage info */
-+	bool percentage_ctrl_enable;
-+	s8 power_drop_level;
-+
-+	/* frond-end loss TX info */
-+	s8 front_end_loss_tx[4];
-+
-+	/* frond-end loss RX info */
-+	s8 front_end_loss_rx[4];
-+
-+	/* thermal info */
-+	bool thermal_compensate_enable;
-+	s8 thermal_compensate_value;
-+	u8 rsv2;
-+
-+	/* TX power max/min limit info */
-+	s8 max_power_bound;
-+	s8 min_power_bound;
-+
-+	/* power limit info */
-+	bool sku_enable;
-+	bool bf_backoff_enable;
-+
-+	/* MU TX power info */
-+	bool mu_tx_power_manual_enable;
-+	s8 mu_tx_power_auto;
-+	s8 mu_tx_power_manual;
-+	u8 rsv3;
-+};
-+
-+struct txpower_phy_rate_info {
-+	u8 category;
-+	u8 band_idx;
-+	u8 band;
-+	u8 epa_gain;
-+
-+	/* rate power info [dBm] */
-+	s8 frame_power[MT7996_SKU_RATE_NUM][__MT_MAX_BAND];
-+
-+	/* TX power max/min limit info */
-+	s8 max_power_bound;
-+	s8 min_power_bound;
-+	u8 rsv1;
-+};
-+
-+struct txpower_backoff_table_info {
-+	u8 category;
-+	u8 band_idx;
-+	u8 band;
-+	u8 backoff_en;
-+
-+	s8 frame_power[MT7996_SKU_PATH_NUM];
-+	u8 rsv[3];
-+};
-+
-+struct mt7996_mcu_txpower_event {
-+	u8 _rsv[4];
-+
-+	__le16 tag;
-+	__le16 len;
-+
-+	union {
-+		struct txpower_basic_info basic_info;
-+		struct txpower_phy_rate_info phy_rate_info;
-+		struct txpower_backoff_table_info backoff_table_info;
-+	};
-+};
-+
-+enum txpower_category {
-+	BASIC_INFO,
-+	BACKOFF_TABLE_INFO,
-+	PHY_RATE_INFO,
-+};
-+
-+enum txpower_event {
-+	UNI_TXPOWER_BASIC_INFO = 0,
-+	UNI_TXPOWER_BACKOFF_TABLE_SHOW_INFO = 3,
-+	UNI_TXPOWER_PHY_RATE_INFO = 5,
-+};
-+
- #endif
- 
- #endif
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index 4c20a67..8ec1dc1 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -693,24 +693,29 @@ enum offs_rev {
- 						 ((_wf) << 16) + (ofs))
- #define MT_WF_PHYRX_CSD_IRPI(_band, _wf)	MT_WF_PHYRX_CSD(_band, _wf, 0x1000)
- 
--/* PHYRX CTRL */
--#define MT_WF_PHYRX_BAND_BASE			0x83080000
--#define MT_WF_PHYRX_BAND(_band, ofs)		(MT_WF_PHYRX_BAND_BASE + \
-+/* PHYDFE CTRL */
-+#define MT_WF_PHYDFE_TSSI_TXCTRL01(_band)	MT_WF_PHYRX_CSD(_band, 0, 0xc718)
-+#define MT_WF_PHYDFE_TSSI_TXCTRL_POWER_TMAC	GENMASK(31, 24)
-+
-+/* PHY CTRL */
-+#define MT_WF_PHY_BAND_BASE			0x83080000
-+#define MT_WF_PHY_BAND(_band, ofs)		(MT_WF_PHY_BAND_BASE + \
- 						 ((_band) << 20) + (ofs))
- 
--#define MT_WF_PHYRX_BAND_GID_TAB_VLD0(_band)	MT_WF_PHYRX_BAND(_band, 0x1054)
--#define MT_WF_PHYRX_BAND_GID_TAB_VLD1(_band)	MT_WF_PHYRX_BAND(_band, 0x1058)
--#define MT_WF_PHYRX_BAND_GID_TAB_POS0(_band)	MT_WF_PHYRX_BAND(_band, 0x105c)
--#define MT_WF_PHYRX_BAND_GID_TAB_POS1(_band)	MT_WF_PHYRX_BAND(_band, 0x1060)
--#define MT_WF_PHYRX_BAND_GID_TAB_POS2(_band)	MT_WF_PHYRX_BAND(_band, 0x1064)
--#define MT_WF_PHYRX_BAND_GID_TAB_POS3(_band)	MT_WF_PHYRX_BAND(_band, 0x1068)
-+#define MT_WF_PHYRX_BAND_GID_TAB_VLD0(_band)	MT_WF_PHY_BAND(_band, 0x1054)
-+#define MT_WF_PHYRX_BAND_GID_TAB_VLD1(_band)	MT_WF_PHY_BAND(_band, 0x1058)
-+#define MT_WF_PHYRX_BAND_GID_TAB_POS0(_band)	MT_WF_PHY_BAND(_band, 0x105c)
-+#define MT_WF_PHYRX_BAND_GID_TAB_POS1(_band)	MT_WF_PHY_BAND(_band, 0x1060)
-+#define MT_WF_PHYRX_BAND_GID_TAB_POS2(_band)	MT_WF_PHY_BAND(_band, 0x1064)
-+#define MT_WF_PHYRX_BAND_GID_TAB_POS3(_band)	MT_WF_PHY_BAND(_band, 0x1068)
- 
--#define MT_WF_PHYRX_BAND_RX_CTRL1(_band)	MT_WF_PHYRX_BAND(_band, 0x2004)
-+/* PHYRX CTRL */
-+#define MT_WF_PHYRX_BAND_RX_CTRL1(_band)	MT_WF_PHY_BAND(_band, 0x2004)
- #define MT_WF_PHYRX_BAND_RX_CTRL1_IPI_EN	GENMASK(2, 0)
- #define MT_WF_PHYRX_BAND_RX_CTRL1_STSCNT_EN	GENMASK(11, 9)
- 
- /* PHYRX CSD BAND */
--#define MT_WF_PHYRX_CSD_BAND_RXTD12(_band)		MT_WF_PHYRX_BAND(_band, 0x8230)
-+#define MT_WF_PHYRX_CSD_BAND_RXTD12(_band)		MT_WF_PHY_BAND(_band, 0x8230)
- #define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR_ONLY	BIT(18)
- #define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR		BIT(29)
- 
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0026-mtk-mt76-mt7996-add-check-for-hostapd-config-he_ldpc.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0026-mtk-mt76-mt7996-add-check-for-hostapd-config-he_ldpc.patch
new file mode 100644
index 0000000..27625e6
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0026-mtk-mt76-mt7996-add-check-for-hostapd-config-he_ldpc.patch
@@ -0,0 +1,63 @@
+From 57e5d3d3887af43a399f747f96e7a185bc726229 Mon Sep 17 00:00:00 2001
+From: "Allen.Ye" <allen.ye@mediatek.com>
+Date: Thu, 8 Jun 2023 17:32:33 +0800
+Subject: [PATCH 026/199] mtk: mt76: mt7996: add check for hostapd config
+ he_ldpc
+
+Add check for hostapd config he_ldpc.
+This capabilities is checked in mcu_beacon_check_caps in 7915.
+
+Add check for STA LDPC cap, if STA only have BCC we should not overwrite the phy_cap with config he_ldpc.
+
+Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
+---
+ mt7996/mcu.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 88c42de7..4ff1af78 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1188,7 +1188,8 @@ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
+ }
+ 
+ static void
+-mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
++mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
++		      struct ieee80211_sta *sta)
+ {
+ 	struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
+ 	struct ieee80211_he_mcs_nss_supp mcs_map;
+@@ -1208,6 +1209,11 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ 		he->he_phy_cap[i] = elem->phy_cap_info[i];
+ 	}
+ 
++	if (vif->type == NL80211_IFTYPE_AP &&
++	    (elem->phy_cap_info[1] & IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD))
++		u8p_replace_bits(&he->he_phy_cap[1], vif->bss_conf.he_ldpc,
++				 IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD);
++
+ 	mcs_map = sta->deflink.he_cap.he_mcs_nss_supp;
+ 	switch (sta->deflink.bandwidth) {
+ 	case IEEE80211_STA_RX_BW_160:
+@@ -2113,7 +2119,7 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 	 * update sta_rec_he here.
+ 	 */
+ 	if (changed)
+-		mt7996_mcu_sta_he_tlv(skb, sta);
++		mt7996_mcu_sta_he_tlv(skb, vif, sta);
+ 
+ 	/* sta_rec_ra accommodates BW, NSS and only MCS range format
+ 	 * i.e 0-{7,8,9} for VHT.
+@@ -2204,7 +2210,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 		/* starec amsdu */
+ 		mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, sta);
+ 		/* starec he */
+-		mt7996_mcu_sta_he_tlv(skb, sta);
++		mt7996_mcu_sta_he_tlv(skb, vif, sta);
+ 		/* starec he 6g*/
+ 		mt7996_mcu_sta_he_6g_tlv(skb, sta);
+ 		/* starec eht */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0026-mtk-wifi-mt76-mt7996-add-binfile-mode-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0026-mtk-wifi-mt76-mt7996-add-binfile-mode-support.patch
deleted file mode 100644
index 00b9fc6..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0026-mtk-wifi-mt76-mt7996-add-binfile-mode-support.patch
+++ /dev/null
@@ -1,366 +0,0 @@
-From 5e960ddb3cd014c0fe80526215319ffd3c497054 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 31 Mar 2023 11:36:34 +0800
-Subject: [PATCH 026/116] mtk: wifi: mt76: mt7996: add binfile mode support
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Fix binfile cannot sync precal data to atenl
-Binfile is viewed as efuse mode in atenl, so atenl does not allocate
-precal memory for its eeprom file
-Use mtd offset == 0xFFFFFFFF to determine whether it is binfile or flash mode
-Add support for loading precal in binfile mode
-
-Align upstream
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- eeprom.c             |  25 +++++++++++
- mt76.h               |   3 ++
- mt7996/eeprom.c      | 103 ++++++++++++++++++++++++++++++++++++++++---
- mt7996/eeprom.h      |   7 +++
- mt7996/mt7996.h      |   4 ++
- mt7996/mtk_debugfs.c |  41 +++++++++++++++++
- testmode.h           |   2 +-
- 7 files changed, 179 insertions(+), 6 deletions(-)
-
-diff --git a/eeprom.c b/eeprom.c
-index 11efe29..3da9492 100644
---- a/eeprom.c
-+++ b/eeprom.c
-@@ -161,6 +161,31 @@ static int mt76_get_of_eeprom(struct mt76_dev *dev, void *eep, int len)
- 	return mt76_get_of_data_from_nvmem(dev, eep, "eeprom", len);
- }
- 
-+bool mt76_check_bin_file_mode(struct mt76_dev *dev)
-+{
-+	struct device_node *np = dev->dev->of_node;
-+	const char *bin_file_name = NULL;
-+
-+	if (!np)
-+		return false;
-+
-+	of_property_read_string(np, "bin_file_name", &bin_file_name);
-+
-+	dev->bin_file_name = bin_file_name;
-+	if (dev->bin_file_name) {
-+		dev_info(dev->dev, "Using bin file %s\n", dev->bin_file_name);
-+#ifdef CONFIG_NL80211_TESTMODE
-+		dev->test_mtd.name = devm_kstrdup(dev->dev, bin_file_name, GFP_KERNEL);
-+		dev->test_mtd.offset = -1;
-+#endif
-+	}
-+
-+	of_node_put(np);
-+
-+	return dev->bin_file_name ? true : false;
-+}
-+EXPORT_SYMBOL_GPL(mt76_check_bin_file_mode);
-+
- void
- mt76_eeprom_override(struct mt76_phy *phy)
- {
-diff --git a/mt76.h b/mt76.h
-index c372e78..dbd8ab9 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -954,6 +954,8 @@ struct mt76_dev {
- 		struct mt76_usb usb;
- 		struct mt76_sdio sdio;
- 	};
-+
-+	const char *bin_file_name;
- };
- 
- /* per-phy stats.  */
-@@ -1225,6 +1227,7 @@ void mt76_eeprom_override(struct mt76_phy *phy);
- int mt76_get_of_data_from_mtd(struct mt76_dev *dev, void *eep, int offset, int len);
- int mt76_get_of_data_from_nvmem(struct mt76_dev *dev, void *eep,
- 				const char *cell_name, int len);
-+bool mt76_check_bin_file_mode(struct mt76_dev *dev);
- 
- struct mt76_queue *
- mt76_init_queue(struct mt76_dev *dev, int qid, int idx, int n_desc,
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 6ac992a..fe8b253 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -82,10 +82,17 @@ static int mt7996_check_eeprom(struct mt7996_dev *dev)
- 	}
- }
- 
--static char *mt7996_eeprom_name(struct mt7996_dev *dev)
-+const char *mt7996_eeprom_name(struct mt7996_dev *dev)
- {
--	if (dev->testmode_enable)
--		return MT7996_EEPROM_DEFAULT_TM;
-+	if (dev->bin_file_mode)
-+		return dev->mt76.bin_file_name;
-+
-+	if (dev->testmode_enable) {
-+		if (is_mt7992(&dev->mt76))
-+			return MT7992_EEPROM_DEFAULT_TM;
-+		else
-+			return MT7996_EEPROM_DEFAULT_TM;
-+	}
- 
- 	switch (mt76_chip(&dev->mt76)) {
- 	case 0x7990:
-@@ -152,7 +159,10 @@ mt7996_eeprom_load_default(struct mt7996_dev *dev)
- 		return ret;
- 
- 	if (!fw || !fw->data) {
--		dev_err(dev->mt76.dev, "Invalid default bin\n");
-+		if (dev->bin_file_mode)
-+			dev_err(dev->mt76.dev, "Invalid bin (bin file mode)\n");
-+		else
-+			dev_err(dev->mt76.dev, "Invalid default bin\n");
- 		ret = -EINVAL;
- 		goto out;
- 	}
-@@ -166,18 +176,45 @@ out:
- 	return ret;
- }
- 
-+static int mt7996_eeprom_load_flash(struct mt7996_dev *dev)
-+{
-+	int ret = 1;
-+
-+	/* return > 0 for load success, return 0 for load failed, return < 0 for non memory */
-+	dev->bin_file_mode = mt76_check_bin_file_mode(&dev->mt76);
-+	if (dev->bin_file_mode) {
-+		dev->mt76.eeprom.size = MT7996_EEPROM_SIZE;
-+		dev->mt76.eeprom.data = devm_kzalloc(dev->mt76.dev, dev->mt76.eeprom.size,
-+						     GFP_KERNEL);
-+		if (!dev->mt76.eeprom.data)
-+			return -ENOMEM;
-+
-+		if (mt7996_eeprom_load_default(dev))
-+			return 0;
-+
-+		if (mt7996_check_eeprom(dev))
-+			return 0;
-+	} else {
-+		ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
-+	}
-+
-+	return ret;
-+}
-+
- int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev)
- {
- 	u8 *eeprom;
- 	int ret;
- 
- 	/* load eeprom in flash or bin file mode to determine fw mode */
--	ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
-+	ret = mt7996_eeprom_load_flash(dev);
-+
- 	if (ret < 0)
- 		return ret;
- 
- 	if (ret) {
- 		dev->flash_mode = true;
-+		dev->eeprom_mode = dev->bin_file_mode ? BIN_FILE_MODE : FLASH_MODE;
- 		eeprom = dev->mt76.eeprom.data;
- 		/* testmode enable priority: eeprom field > module parameter */
- 		dev->testmode_enable = !mt7996_check_eeprom(dev) ? eeprom[MT_EE_TESTMODE_EN] :
-@@ -211,6 +248,7 @@ static int mt7996_eeprom_load(struct mt7996_dev *dev)
- 			if (ret && ret != -EINVAL)
- 				return ret;
- 		}
-+		dev->eeprom_mode = EFUSE_MODE;
- 	}
- 
- 	return mt7996_check_eeprom(dev);
-@@ -337,6 +375,59 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy)
- 	return mt7996_eeprom_parse_band_config(phy);
- }
- 
-+static int
-+mt7996_eeprom_load_precal_binfile(struct mt7996_dev *dev, u32 offs, u32 size)
-+{
-+	const struct firmware *fw = NULL;
-+	int ret;
-+
-+	ret = request_firmware(&fw, dev->mt76.bin_file_name, dev->mt76.dev);
-+	if (ret)
-+		return ret;
-+
-+	if (!fw || !fw->data) {
-+		dev_err(dev->mt76.dev, "Invalid bin (bin file mode), load precal fail\n");
-+		ret = -EINVAL;
-+		goto out;
-+	}
-+
-+	memcpy(dev->cal, fw->data + offs, size);
-+
-+out:
-+	release_firmware(fw);
-+
-+	return ret;
-+}
-+
-+static int mt7996_eeprom_load_precal(struct mt7996_dev *dev)
-+{
-+	struct mt76_dev *mdev = &dev->mt76;
-+	u8 *eeprom = mdev->eeprom.data;
-+	u32 offs = MT_EE_DO_PRE_CAL;
-+	u32 size, val = eeprom[offs];
-+	int ret;
-+
-+	mt7996_eeprom_init_precal(dev);
-+
-+	if (!dev->flash_mode || !val)
-+		return 0;
-+
-+	size = MT_EE_CAL_GROUP_SIZE + MT_EE_CAL_DPD_SIZE;
-+
-+	dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
-+	if (!dev->cal)
-+		return -ENOMEM;
-+
-+	if (dev->bin_file_mode)
-+		return mt7996_eeprom_load_precal_binfile(dev, MT_EE_PRECAL, size);
-+
-+	ret = mt76_get_of_data_from_mtd(mdev, dev->cal, offs, size);
-+	if (!ret)
-+		return ret;
-+
-+	return mt76_get_of_data_from_nvmem(mdev, dev->cal, "precal", size);
-+}
-+
- int mt7996_eeprom_init(struct mt7996_dev *dev)
- {
- 	int ret;
-@@ -351,6 +442,8 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
- 			return ret;
- 
- 		dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n");
-+		dev->bin_file_mode = false;
-+		dev->eeprom_mode = DEFAULT_BIN_MODE;
- 		ret = mt7996_eeprom_load_default(dev);
- 		if (ret)
- 			return ret;
-diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index b19ff06..8f0f87b 100644
---- a/mt7996/eeprom.h
-+++ b/mt7996/eeprom.h
-@@ -102,6 +102,13 @@ enum mt7996_eeprom_band {
- 	MT_EE_BAND_SEL_6GHZ,
- };
- 
-+enum mt7915_eeprom_mode {
-+	DEFAULT_BIN_MODE,
-+	EFUSE_MODE,
-+	FLASH_MODE,
-+	BIN_FILE_MODE,
-+};
-+
- static inline int
- mt7996_get_channel_group_5g(int channel)
- {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 6ab45cc..9d6e85c 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -62,6 +62,7 @@
- #define MT7992_EEPROM_DEFAULT_24	"mediatek/mt7996/mt7992_eeprom_24_2i5i.bin"
- #define MT7992_EEPROM_DEFAULT_23	"mediatek/mt7996/mt7992_eeprom_23_2i5i.bin"
- #define MT7992_EEPROM_DEFAULT_23_EXT	"mediatek/mt7996/mt7992_eeprom_23_2e5e.bin"
-+#define MT7992_EEPROM_DEFAULT_TM	"mediatek/mt7996/mt7992_eeprom_tm.bin"
- #define MT7996_EEPROM_SIZE		7680
- #define MT7996_EEPROM_BLOCK_SIZE	16
- #define MT7996_TOKEN_SIZE		16384
-@@ -395,6 +396,8 @@ struct mt7996_dev {
- 	} wed_rro;
- 
- 	bool testmode_enable;
-+	bool bin_file_mode;
-+	u8 eeprom_mode;
- 
- 	bool ibf;
- 	u8 fw_debug_wm;
-@@ -523,6 +526,7 @@ irqreturn_t mt7996_irq_handler(int irq, void *dev_instance);
- u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
- int mt7996_register_device(struct mt7996_dev *dev);
- void mt7996_unregister_device(struct mt7996_dev *dev);
-+const char *mt7996_eeprom_name(struct mt7996_dev *dev);
- int mt7996_eeprom_init(struct mt7996_dev *dev);
- int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev);
- int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index 74aabf0..2499f12 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -2798,6 +2798,44 @@ static const struct file_operations mt7996_txpower_path_fops = {
- 	.llseek = default_llseek,
- };
- 
-+static int mt7996_show_eeprom_mode(struct seq_file *s, void *data)
-+{
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	struct mt76_dev *mdev = &dev->mt76;
-+#ifdef CONFIG_NL80211_TESTMODE
-+	const char *mtd_name = mdev->test_mtd.name;
-+	u32 mtd_offset = mdev->test_mtd.offset;
-+#else
-+	const char *mtd_name = NULL;
-+	u32 mtd_offset;
-+#endif
-+
-+	seq_printf(s, "Current eeprom mode:\n");
-+
-+	switch (dev->eeprom_mode) {
-+	case DEFAULT_BIN_MODE:
-+		seq_printf(s, "   default bin mode\n   filename = %s\n", mt7996_eeprom_name(dev));
-+		break;
-+	case EFUSE_MODE:
-+		seq_printf(s, "   efuse mode\n");
-+		break;
-+	case FLASH_MODE:
-+		if (mtd_name)
-+			seq_printf(s, "   flash mode\n   mtd name = %s\n   flash offset = 0x%x\n",
-+				   mtd_name, mtd_offset);
-+		else
-+			seq_printf(s, "   flash mode\n");
-+		break;
-+	case BIN_FILE_MODE:
-+		seq_printf(s, "   bin file mode\n   filename = %s\n", mt7996_eeprom_name(dev));
-+		break;
-+	default:
-+		break;
-+	}
-+
-+	return 0;
-+}
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- 	struct mt7996_dev *dev = phy->dev;
-@@ -2866,6 +2904,9 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- 	debugfs_create_file("txpower_sku", 0600, dir, phy, &mt7996_txpower_sku_fops);
- 	debugfs_create_file("txpower_path", 0600, dir, phy, &mt7996_txpower_path_fops);
- 
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "eeprom_mode", dir,
-+				    mt7996_show_eeprom_mode);
-+
- 	debugfs_create_devm_seqfile(dev->mt76.dev, "wtbl_info", dir,
- 				    mt7996_wtbl_read);
- 
-diff --git a/testmode.h b/testmode.h
-index d6601cd..5d677f8 100644
---- a/testmode.h
-+++ b/testmode.h
-@@ -16,7 +16,7 @@
-  * @MT76_TM_ATTR_RESET: reset parameters to default (flag)
-  * @MT76_TM_ATTR_STATE: test state (u32), see &enum mt76_testmode_state
-  *
-- * @MT76_TM_ATTR_MTD_PART: mtd partition used for eeprom data (string)
-+ * @MT76_TM_ATTR_MTD_PART: mtd partition or binfile used for eeprom data (string)
-  * @MT76_TM_ATTR_MTD_OFFSET: offset of eeprom data within the partition (u32)
-  * @MT76_TM_ATTR_BAND_IDX: band idx of the chip (u8)
-  *
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0027-mtk-mt76-mt7996-add-basic-testmode-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0027-mtk-mt76-mt7996-add-basic-testmode-support.patch
new file mode 100644
index 0000000..3337917
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0027-mtk-mt76-mt7996-add-basic-testmode-support.patch
@@ -0,0 +1,2359 @@
+From c051b319e725ca566a990b375c089bf74d53e021 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 28 Dec 2022 22:24:25 +0800
+Subject: [PATCH 027/199] mtk: mt76: mt7996: add basic testmode support
+
+Add testmode eeprom buffer mode support
+
+Fix power & freq offset issue for iTest power cal & tx/rx verifcation
+1. Wait for fw to tx. Otherwise, iTest testing tool cannot get the
+accurate tx power.
+2. In crystal mode, freq offset is set in 6G band and forwarded to 5G
+and 2G band. Therefore, we should avoid reseting freq offset to 0 when
+6G interface is off.
+
+edcca return err in testmode; therefore, bypass it when we are in testmode idle state or testmode bf is on
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ eeprom.c          |   6 +-
+ mac80211.c        |   3 +-
+ mt76.h            |  36 +++
+ mt76_connac_mcu.h |   2 +
+ mt7996/Makefile   |   1 +
+ mt7996/eeprom.c   |  37 ++-
+ mt7996/eeprom.h   |   1 +
+ mt7996/init.c     |   8 +
+ mt7996/mac.c      |   3 +-
+ mt7996/main.c     |  27 ++
+ mt7996/mcu.c      |  59 +++-
+ mt7996/mcu.h      |  33 +++
+ mt7996/mt7996.h   |  27 +-
+ mt7996/testmode.c | 740 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/testmode.h | 299 +++++++++++++++++++
+ testmode.c        | 126 ++++++--
+ testmode.h        |  87 +++++-
+ tools/fields.c    | 102 ++++++-
+ 18 files changed, 1547 insertions(+), 50 deletions(-)
+ create mode 100644 mt7996/testmode.c
+ create mode 100644 mt7996/testmode.h
+
+diff --git a/eeprom.c b/eeprom.c
+index 0bc66cc1..a0047d79 100644
+--- a/eeprom.c
++++ b/eeprom.c
+@@ -94,8 +94,10 @@ int mt76_get_of_data_from_mtd(struct mt76_dev *dev, void *eep, int offset, int l
+ 	}
+ 
+ #ifdef CONFIG_NL80211_TESTMODE
+-	dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL);
+-	dev->test_mtd.offset = offset;
++	if (len == dev->eeprom.size) {
++		dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL);
++		dev->test_mtd.offset = offset;
++	}
+ #endif
+ 
+ out_put_node:
+diff --git a/mac80211.c b/mac80211.c
+index 9df9109f..7e4ee83f 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -846,7 +846,8 @@ void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb)
+ 	}
+ 
+ #ifdef CONFIG_NL80211_TESTMODE
+-	if (phy->test.state == MT76_TM_STATE_RX_FRAMES) {
++	if (!(phy->test.flag & MT_TM_FW_RX_COUNT) &&
++	    phy->test.state == MT76_TM_STATE_RX_FRAMES) {
+ 		phy->test.rx_stats.packets[q]++;
+ 		if (status->flag & RX_FLAG_FAILED_FCS_CRC)
+ 			phy->test.rx_stats.fcs_error[q]++;
+diff --git a/mt76.h b/mt76.h
+index beba1d91..27635c49 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -704,14 +704,21 @@ struct mt76_testmode_ops {
+ 	int (*set_params)(struct mt76_phy *phy, struct nlattr **tb,
+ 			  enum mt76_testmode_state new_state);
+ 	int (*dump_stats)(struct mt76_phy *phy, struct sk_buff *msg);
++	void (*reset_rx_stats)(struct mt76_phy *phy);
++	void (*tx_stop)(struct mt76_phy *phy);
++	int (*set_eeprom)(struct mt76_phy *phy, u32 offset, u8 *val, u8 action);
+ };
+ 
++#define MT_TM_FW_RX_COUNT	BIT(0)
++
+ struct mt76_testmode_data {
+ 	enum mt76_testmode_state state;
+ 
+ 	u32 param_set[DIV_ROUND_UP(NUM_MT76_TM_ATTRS, 32)];
+ 	struct sk_buff *tx_skb;
+ 
++	u8 sku_en;
++
+ 	u32 tx_count;
+ 	u16 tx_mpdu_len;
+ 
+@@ -721,6 +728,7 @@ struct mt76_testmode_data {
+ 	u8 tx_rate_sgi;
+ 	u8 tx_rate_ldpc;
+ 	u8 tx_rate_stbc;
++	u16 tx_preamble_puncture;
+ 	u8 tx_ltf;
+ 
+ 	u8 tx_antenna_mask;
+@@ -730,6 +738,9 @@ struct mt76_testmode_data {
+ 	u32 tx_time;
+ 	u32 tx_ipg;
+ 
++	bool ibf;
++	bool ebf;
++
+ 	u32 freq_offset;
+ 
+ 	u8 tx_power[4];
+@@ -744,7 +755,16 @@ struct mt76_testmode_data {
+ 	struct {
+ 		u64 packets[__MT_RXQ_MAX];
+ 		u64 fcs_error[__MT_RXQ_MAX];
++		u64 len_mismatch;
+ 	} rx_stats;
++	u8 flag;
++
++	struct {
++		u8 type;
++		u8 enable;
++	} cfg;
++
++	u8 aid;
+ };
+ 
+ struct mt76_vif {
+@@ -1453,6 +1473,22 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
+ int mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state);
+ int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len);
+ 
++static inline void
++mt76_testmode_param_set(struct mt76_testmode_data *td, u16 idx)
++{
++#ifdef CONFIG_NL80211_TESTMODE
++	td->param_set[idx / 32] |= BIT(idx % 32);
++#endif
++}
++
++static inline bool
++mt76_testmode_param_present(struct mt76_testmode_data *td, u16 idx)
++{
++#ifdef CONFIG_NL80211_TESTMODE
++	return td->param_set[idx / 32] & BIT(idx % 32);
++#endif
++}
++
+ static inline void mt76_testmode_reset(struct mt76_phy *phy, bool disable)
+ {
+ #ifdef CONFIG_NL80211_TESTMODE
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index a7cb33d2..57e0e9de 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1279,12 +1279,14 @@ enum {
+ 	MCU_UNI_CMD_EFUSE_CTRL = 0x2d,
+ 	MCU_UNI_CMD_RA = 0x2f,
+ 	MCU_UNI_CMD_MURU = 0x31,
++	MCU_UNI_CMD_TESTMODE_RX_STAT = 0x32,
+ 	MCU_UNI_CMD_BF = 0x33,
+ 	MCU_UNI_CMD_CHANNEL_SWITCH = 0x34,
+ 	MCU_UNI_CMD_THERMAL = 0x35,
+ 	MCU_UNI_CMD_VOW = 0x37,
+ 	MCU_UNI_CMD_PP = 0x38,
+ 	MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
++	MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
+ 	MCU_UNI_CMD_RRO = 0x57,
+ 	MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
+ 	MCU_UNI_CMD_PER_STA_INFO = 0x6d,
+diff --git a/mt7996/Makefile b/mt7996/Makefile
+index a056b40e..7bb17f44 100644
+--- a/mt7996/Makefile
++++ b/mt7996/Makefile
+@@ -8,5 +8,6 @@ mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
+ 	     debugfs.o mmio.o
+ 
+ mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o
++mt7996e-$(CONFIG_NL80211_TESTMODE) += testmode.o
+ 
+ mt7996e-y += mtk_debugfs.o mtk_mcu.o
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index bd02eb00..e2790109 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -6,6 +6,11 @@
+ #include <linux/firmware.h>
+ #include "mt7996.h"
+ #include "eeprom.h"
++#include <linux/moduleparam.h>
++
++static bool testmode_enable;
++module_param(testmode_enable, bool, 0644);
++MODULE_PARM_DESC(testmode_enable, "Enable testmode");
+ 
+ static int mt7996_check_eeprom(struct mt7996_dev *dev)
+ {
+@@ -43,6 +48,9 @@ static int mt7996_check_eeprom(struct mt7996_dev *dev)
+ 
+ static char *mt7996_eeprom_name(struct mt7996_dev *dev)
+ {
++	if (dev->testmode_enable)
++		return MT7996_EEPROM_DEFAULT_TM;
++
+ 	switch (mt76_chip(&dev->mt76)) {
+ 	case 0x7990:
+ 		if (dev->chip_sku == MT7996_SKU_404)
+@@ -92,21 +100,36 @@ out:
+ 	return ret;
+ }
+ 
+-static int mt7996_eeprom_load(struct mt7996_dev *dev)
++int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev)
+ {
++	u8 *eeprom;
+ 	int ret;
+ 
++	/* load eeprom in flash or bin file mode to determine fw mode */
+ 	ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
+ 	if (ret < 0)
+ 		return ret;
+ 
+ 	if (ret) {
+ 		dev->flash_mode = true;
+-	} else {
+-		u8 free_block_num;
+-		u32 block_num, i;
+-		u32 eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE;
++		eeprom = dev->mt76.eeprom.data;
++		/* testmode enable priority: eeprom field > module parameter */
++		dev->testmode_enable = !mt7996_check_eeprom(dev) ? eeprom[MT_EE_TESTMODE_EN] :
++								   testmode_enable;
++	}
++
++	return ret;
++}
++
++static int mt7996_eeprom_load(struct mt7996_dev *dev)
++{
++	int ret;
++	u8 free_block_num;
++	u32 block_num, i;
++	u32 eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE;
+ 
++	/* flash or bin file mode eeprom is loaded before mcu init */
++	if (!dev->flash_mode) {
+ 		ret = mt7996_mcu_get_eeprom_free_block(dev, &free_block_num);
+ 		if (ret < 0)
+ 			return ret;
+@@ -118,8 +141,8 @@ static int mt7996_eeprom_load(struct mt7996_dev *dev)
+ 		/* read eeprom data from efuse */
+ 		block_num = DIV_ROUND_UP(MT7996_EEPROM_SIZE, eeprom_blk_size);
+ 		for (i = 0; i < block_num; i++) {
+-			ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size);
+-			if (ret < 0)
++			ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size, NULL);
++			if (ret && ret != -EINVAL)
+ 				return ret;
+ 		}
+ 	}
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index 72c38ad3..de3ff4e2 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -14,6 +14,7 @@ enum mt7996_eeprom_field {
+ 	MT_EE_MAC_ADDR =	0x004,
+ 	MT_EE_MAC_ADDR2 =	0x00a,
+ 	MT_EE_WIFI_CONF =	0x190,
++	MT_EE_TESTMODE_EN =	0x1af,
+ 	MT_EE_MAC_ADDR3 =	0x2c0,
+ 	MT_EE_RATE_DELTA_2G =	0x1400,
+ 	MT_EE_RATE_DELTA_5G =	0x147d,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index f80fba28..ed7c7040 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -971,6 +971,10 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
+ 
+ 	set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state);
+ 
++	ret = mt7996_eeprom_check_fw_mode(dev);
++	if (ret < 0)
++		return ret;
++
+ 	ret = mt7996_mcu_init(dev);
+ 	if (ret)
+ 		return ret;
+@@ -1420,6 +1424,10 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ 
+ 	mt7996_init_wiphy(hw, &dev->mt76.mmio.wed);
+ 
++#ifdef CONFIG_NL80211_TESTMODE
++	dev->mt76.test_ops = &mt7996_testmode_ops;
++#endif
++
+ 	ret = mt76_register_device(&dev->mt76, true, mt76_rates,
+ 				   ARRAY_SIZE(mt76_rates));
+ 	if (ret)
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 52ea6796..a0fcd8aa 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -685,7 +685,8 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ 				     *info);
+ 	}
+ 
+-	if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023))
++	if (rxv && mode >= MT_PHY_TYPE_HE_SU && mode < MT_PHY_TYPE_EHT_SU &&
++	    !(status->flag & RX_FLAG_8023))
+ 		mt76_connac3_mac_decode_he_radiotap(skb, rxv, mode);
+ 
+ 	if (!status->wcid || !ieee80211_is_data_qos(fc) || hw_aggr)
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 72232994..be50f7f3 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -23,6 +23,18 @@ static bool mt7996_dev_running(struct mt7996_dev *dev)
+ 	return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+ }
+ 
++static void mt7996_testmode_disable_all(struct mt7996_dev *dev)
++{
++	struct mt7996_phy *phy;
++	int i;
++
++	for (i = 0; i < __MT_MAX_BAND; i++) {
++		phy = __mt7996_phy(dev, i);
++		if (phy)
++			mt76_testmode_set_state(phy->mt76, MT76_TM_STATE_OFF);
++	}
++}
++
+ int mt7996_run(struct ieee80211_hw *hw)
+ {
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+@@ -45,6 +57,8 @@ int mt7996_run(struct ieee80211_hw *hw)
+ 		}
+ 	}
+ 
++	mt7996_testmode_disable_all(dev);
++
+ 	mt7996_mac_enable_nf(dev, phy->mt76->band_idx);
+ 
+ 	ret = mt7996_mcu_set_rts_thresh(phy, 0x92b);
+@@ -303,6 +317,11 @@ int mt7996_set_channel(struct mt7996_phy *phy)
+ 
+ 	mt76_set_channel(phy->mt76);
+ 
++	if (mt76_testmode_enabled(phy->mt76) || phy->mt76->test.bf_en) {
++		mt7996_tm_update_channel(phy);
++		goto out;
++	}
++
+ 	ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
+ 	if (ret)
+ 		goto out;
+@@ -415,6 +434,12 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
+ 	int ret;
+ 
+ 	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
++		if (!mt76_testmode_enabled(phy->mt76) && !phy->mt76->test.bf_en) {
++			ret = mt7996_mcu_edcca_enable(phy, true);
++			if (ret)
++				return ret;
++		}
++
+ 		ret = mt7996_mcu_set_pp_en(phy, PP_USR_MODE,
+ 					   phy->mt76->chandef.punctured);
+ 		if (ret)
+@@ -1531,6 +1556,8 @@ const struct ieee80211_ops mt7996_ops = {
+ 	.sta_set_decap_offload = mt7996_sta_set_decap_offload,
+ 	.add_twt_setup = mt7996_mac_add_twt_setup,
+ 	.twt_teardown_request = mt7996_twt_teardown_request,
++	CFG80211_TESTMODE_CMD(mt76_testmode_cmd)
++	CFG80211_TESTMODE_DUMP(mt76_testmode_dump)
+ #ifdef CONFIG_MAC80211_DEBUGFS
+ 	.sta_add_debugfs = mt7996_sta_add_debugfs,
+ #endif
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 4ff1af78..61b46370 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2868,8 +2868,12 @@ static int mt7996_load_ram(struct mt7996_dev *dev)
+ {
+ 	int ret;
+ 
+-	ret = __mt7996_load_ram(dev, "WM", fw_name(dev, FIRMWARE_WM),
+-				MT7996_RAM_TYPE_WM);
++	if (dev->testmode_enable)
++		ret = __mt7996_load_ram(dev, "WM_TM", fw_name(dev, FIRMWARE_WM_TM),
++					MT7996_RAM_TYPE_WM_TM);
++	else
++		ret = __mt7996_load_ram(dev, "WM", fw_name(dev, FIRMWARE_WM),
++					MT7996_RAM_TYPE_WM);
+ 	if (ret)
+ 		return ret;
+ 
+@@ -3560,17 +3564,9 @@ int mt7996_mcu_set_eeprom(struct mt7996_dev *dev)
+ 				 &req, sizeof(req), true);
+ }
+ 
+-int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset)
++int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf)
+ {
+-	struct {
+-		u8 _rsv[4];
+-
+-		__le16 tag;
+-		__le16 len;
+-		__le32 addr;
+-		__le32 valid;
+-		u8 data[16];
+-	} __packed req = {
++	struct mt7996_mcu_eeprom_info req = {
+ 		.tag = cpu_to_le16(UNI_EFUSE_ACCESS),
+ 		.len = cpu_to_le16(sizeof(req) - 4),
+ 		.addr = cpu_to_le32(round_down(offset,
+@@ -3579,6 +3575,7 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset)
+ 	struct sk_buff *skb;
+ 	bool valid;
+ 	int ret;
++	u8 *buf = read_buf;
+ 
+ 	ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+ 					MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL),
+@@ -3589,7 +3586,9 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset)
+ 	valid = le32_to_cpu(*(__le32 *)(skb->data + 16));
+ 	if (valid) {
+ 		u32 addr = le32_to_cpu(*(__le32 *)(skb->data + 12));
+-		u8 *buf = (u8 *)dev->mt76.eeprom.data + addr;
++
++		if (!buf)
++			buf = (u8 *)dev->mt76.eeprom.data + addr;
+ 
+ 		skb_pull(skb, 48);
+ 		memcpy(buf, skb->data, MT7996_EEPROM_BLOCK_SIZE);
+@@ -4612,3 +4611,37 @@ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap)
+ 	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(PP),
+ 				 &req, sizeof(req), false);
+ }
++
++int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct tx_power_ctrl req = {
++		.tag = cpu_to_le16(power_ctrl_id),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.power_ctrl_id = power_ctrl_id,
++		.band_idx = phy->mt76->band_idx,
++	};
++
++	switch (power_ctrl_id) {
++	case UNI_TXPOWER_SKU_POWER_LIMIT_CTRL:
++		req.sku_enable = !!data;
++		break;
++	case UNI_TXPOWER_PERCENTAGE_CTRL:
++		req.percentage_ctrl_enable = !!data;
++		break;
++	case UNI_TXPOWER_PERCENTAGE_DROP_CTRL:
++		req.power_drop_level = data;
++		break;
++	case UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL:
++		req.bf_backoff_enable = !!data;
++		break;
++	case UNI_TXPOWER_ATE_MODE_CTRL:
++		req.ate_mode_enable = !!data;
++		break;
++	default:
++		req.sku_enable = !!data;
++	}
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TXPOWER),
++				 &req, sizeof(req), false);
++}
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index df42c0f8..abf8ef48 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -157,6 +157,16 @@ struct mt7996_mcu_eeprom {
+ 	__le16 buf_len;
+ } __packed;
+ 
++struct mt7996_mcu_eeprom_info {
++	u8 _rsv[4];
++
++	__le16 tag;
++	__le16 len;
++	__le32 addr;
++	__le32 valid;
++	u8 data[MT7996_EEPROM_BLOCK_SIZE];
++} __packed;
++
+ struct mt7996_mcu_phy_rx_info {
+ 	u8 category;
+ 	u8 rate;
+@@ -889,8 +899,31 @@ enum {
+ 	UNI_CMD_THERMAL_PROTECT_DUTY_CONFIG,
+ };
+ 
++struct tx_power_ctrl {
++	u8 _rsv[4];
++
++	__le16 tag;
++	__le16 len;
++
++	u8 power_ctrl_id;
++	union {
++		bool sku_enable;
++		bool ate_mode_enable;
++		bool percentage_ctrl_enable;
++		bool bf_backoff_enable;
++		u8 power_drop_level;
++	};
++	u8 band_idx;
++	u8 rsv[1];
++} __packed;
++
+ enum {
++	UNI_TXPOWER_SKU_POWER_LIMIT_CTRL = 0,
++	UNI_TXPOWER_PERCENTAGE_CTRL = 1,
++	UNI_TXPOWER_PERCENTAGE_DROP_CTRL = 2,
++	UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL = 3,
+ 	UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL = 4,
++	UNI_TXPOWER_ATE_MODE_CTRL = 6,
+ };
+ 
+ enum {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index bb7536ff..1e1e638a 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -32,21 +32,25 @@
+ #define MT7996_FIRMWARE_WA		"mediatek/mt7996/mt7996_wa.bin"
+ #define MT7996_FIRMWARE_WM		"mediatek/mt7996/mt7996_wm.bin"
+ #define MT7996_FIRMWARE_DSP		"mediatek/mt7996/mt7996_dsp.bin"
++#define MT7996_FIRMWARE_WM_TM		"mediatek/mt7996/mt7996_wm_tm.bin"
+ #define MT7996_ROM_PATCH		"mediatek/mt7996/mt7996_rom_patch.bin"
+ 
+ #define MT7992_FIRMWARE_WA		"mediatek/mt7996/mt7992_wa.bin"
+ #define MT7992_FIRMWARE_WM		"mediatek/mt7996/mt7992_wm.bin"
+ #define MT7992_FIRMWARE_DSP		"mediatek/mt7996/mt7992_dsp.bin"
++#define MT7992_FIRMWARE_WM_TM		"mediatek/mt7996/mt7992_wm_tm.bin"
+ #define MT7992_ROM_PATCH		"mediatek/mt7996/mt7992_rom_patch.bin"
+ 
+ #define MT7992_FIRMWARE_WA_24		"mediatek/mt7996/mt7992_wa_24.bin"
+ #define MT7992_FIRMWARE_WM_24		"mediatek/mt7996/mt7992_wm_24.bin"
+ #define MT7992_FIRMWARE_DSP_24		"mediatek/mt7996/mt7992_dsp_24.bin"
++#define MT7992_FIRMWARE_WM_TM_24	"mediatek/mt7996/mt7992_wm_tm_24.bin"
+ #define MT7992_ROM_PATCH_24		"mediatek/mt7996/mt7992_rom_patch_24.bin"
+ 
+ #define MT7992_FIRMWARE_WA_23		"mediatek/mt7996/mt7992_wa_23.bin"
+ #define MT7992_FIRMWARE_WM_23		"mediatek/mt7996/mt7992_wm_23.bin"
+ #define MT7992_FIRMWARE_DSP_23		"mediatek/mt7996/mt7992_dsp_23.bin"
++#define MT7992_FIRMWARE_WM_TM_23	"mediatek/mt7996/mt7992_wm_tm_23.bin"
+ #define MT7992_ROM_PATCH_23		"mediatek/mt7996/mt7992_rom_patch_23.bin"
+ 
+ #define MT7996_EEPROM_DEFAULT		"mediatek/mt7996/mt7996_eeprom.bin"
+@@ -127,6 +131,7 @@ enum mt7992_sku_type {
+ 
+ enum mt7996_ram_type {
+ 	MT7996_RAM_TYPE_WM,
++	MT7996_RAM_TYPE_WM_TM = MT7996_RAM_TYPE_WM,
+ 	MT7996_RAM_TYPE_WA,
+ 	MT7996_RAM_TYPE_DSP,
+ 	__MT7996_RAM_TYPE_MAX,
+@@ -277,6 +282,21 @@ struct mt7996_phy {
+ 
+ 	u8 pp_mode;
+ 	u16 punct_bitmap;
++
++#ifdef CONFIG_NL80211_TESTMODE
++	struct {
++		u32 *reg_backup;
++
++		s32 last_freq_offset;
++		u8 last_rcpi[4];
++		s8 last_rssi[4];
++		s8 last_ib_rssi[4];
++		s8 last_wb_rssi[4];
++		u8 last_snr;
++
++		u8 spe_idx;
++	} test;
++#endif
+ };
+ 
+ struct mt7996_dev {
+@@ -357,6 +377,8 @@ struct mt7996_dev {
+ 		spinlock_t lock;
+ 	} wed_rro;
+ 
++	bool testmode_enable;
++
+ 	bool ibf;
+ 	u8 fw_debug_wm;
+ 	u8 fw_debug_wa;
+@@ -472,6 +494,7 @@ mt7996_band_valid(struct mt7996_dev *dev, u8 band)
+ extern const struct ieee80211_ops mt7996_ops;
+ extern struct pci_driver mt7996_pci_driver;
+ extern struct pci_driver mt7996_hif_driver;
++extern const struct mt76_testmode_ops mt7996_testmode_ops;
+ 
+ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
+ 				     void __iomem *mem_base, u32 device_id);
+@@ -481,6 +504,7 @@ u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
+ int mt7996_register_device(struct mt7996_dev *dev);
+ void mt7996_unregister_device(struct mt7996_dev *dev);
+ int mt7996_eeprom_init(struct mt7996_dev *dev);
++int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev);
+ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
+ int mt7996_eeprom_get_target_power(struct mt7996_dev *dev,
+ 				   struct ieee80211_channel *chan);
+@@ -533,7 +557,7 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
+ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 			       struct ieee80211_sta *sta, void *data, u32 field);
+ int mt7996_mcu_set_eeprom(struct mt7996_dev *dev);
+-int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset);
++int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf);
+ int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num);
+ int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap);
+ int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 set, u8 band);
+@@ -568,6 +592,7 @@ void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ void mt7996_mcu_exit(struct mt7996_dev *dev);
+ int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
+ int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
++int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
+ 
+ static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
+ {
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+new file mode 100644
+index 00000000..98eebcee
+--- /dev/null
++++ b/mt7996/testmode.c
+@@ -0,0 +1,740 @@
++// SPDX-License-Identifier: ISC
++/*
++ * Copyright (C) 2022 MediaTek Inc.
++ */
++
++#include "mt7996.h"
++#include "mac.h"
++#include "mcu.h"
++#include "testmode.h"
++
++enum {
++	TM_CHANGED_TXPOWER,
++	TM_CHANGED_FREQ_OFFSET,
++	TM_CHANGED_SKU_EN,
++	TM_CHANGED_TX_LENGTH,
++	TM_CHANGED_TX_TIME,
++	TM_CHANGED_CFG,
++
++	/* must be last */
++	NUM_TM_CHANGED
++};
++
++static const u8 tm_change_map[] = {
++	[TM_CHANGED_TXPOWER] = MT76_TM_ATTR_TX_POWER,
++	[TM_CHANGED_FREQ_OFFSET] = MT76_TM_ATTR_FREQ_OFFSET,
++	[TM_CHANGED_SKU_EN] = MT76_TM_ATTR_SKU_EN,
++	[TM_CHANGED_TX_LENGTH] = MT76_TM_ATTR_TX_LENGTH,
++	[TM_CHANGED_TX_TIME] = MT76_TM_ATTR_TX_TIME,
++	[TM_CHANGED_CFG] = MT76_TM_ATTR_CFG,
++};
++
++static u8 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method)
++{
++	static const u8 width_to_bw[][NUM_BW_MAP] = {
++		[NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ},
++		[NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ},
++		[NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ},
++		[NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ},
++		[NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ},
++		[NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ},
++		[NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
++		[NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
++		[NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ},
++	};
++
++	if (width >= ARRAY_SIZE(width_to_bw))
++		return 0;
++
++	return width_to_bw[width][method];
++}
++
++static u8 mt7996_tm_rate_to_phy(u8 tx_rate_mode)
++{
++	static const u8 rate_to_phy[] = {
++		[MT76_TM_TX_MODE_CCK] = MT_PHY_TYPE_CCK,
++		[MT76_TM_TX_MODE_OFDM] = MT_PHY_TYPE_OFDM,
++		[MT76_TM_TX_MODE_HT] = MT_PHY_TYPE_HT,
++		[MT76_TM_TX_MODE_VHT] = MT_PHY_TYPE_VHT,
++		[MT76_TM_TX_MODE_HE_SU] = MT_PHY_TYPE_HE_SU,
++		[MT76_TM_TX_MODE_HE_EXT_SU] = MT_PHY_TYPE_HE_EXT_SU,
++		[MT76_TM_TX_MODE_HE_TB] = MT_PHY_TYPE_HE_TB,
++		[MT76_TM_TX_MODE_HE_MU] = MT_PHY_TYPE_HE_MU,
++		[MT76_TM_TX_MODE_EHT_SU] = MT_PHY_TYPE_EHT_SU,
++		[MT76_TM_TX_MODE_EHT_TRIG] = MT_PHY_TYPE_EHT_TRIG,
++		[MT76_TM_TX_MODE_EHT_MU] = MT_PHY_TYPE_EHT_MU,
++	};
++
++	if (tx_rate_mode > MT76_TM_TX_MODE_MAX)
++		return -EINVAL;
++
++	return rate_to_phy[tx_rate_mode];
++}
++
++static int
++mt7996_tm_check_antenna(struct mt7996_phy *phy)
++{
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct mt7996_dev *dev = phy->dev;
++	u8 band_idx = phy->mt76->band_idx;
++	u32 chainmask = phy->mt76->chainmask;
++	u32 aux_rx_mask;
++
++	chainmask = chainmask >> dev->chainshift[band_idx];
++	aux_rx_mask = BIT(fls(chainmask)) * phy->has_aux_rx;
++	if (td->tx_antenna_mask & ~(chainmask | aux_rx_mask)) {
++		dev_err(dev->mt76.dev,
++			"tx antenna mask 0x%x exceeds hw limit (chainmask 0x%x, has aux rx: %s)\n",
++			td->tx_antenna_mask, chainmask, phy->has_aux_rx ? "yes" : "no");
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++static int
++mt7996_tm_set(struct mt7996_dev *dev, u32 func_idx, u32 data)
++{
++	struct mt7996_tm_req req = {
++		.rf_test = {
++			.tag = cpu_to_le16(UNI_RF_TEST_CTRL),
++			.len = cpu_to_le16(sizeof(req.rf_test)),
++			.action = RF_ACTION_SET,
++			.op.rf.func_idx = func_idx,
++			.op.rf.param.func_data = cpu_to_le32(data),
++		},
++	};
++	bool wait = (data == RF_CMD(START_TX)) ? true : false;
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), &req,
++				 sizeof(req), wait);
++}
++
++static int
++mt7996_tm_get(struct mt7996_dev *dev, u32 func_idx, u32 data, u32 *result)
++{
++	struct mt7996_tm_req req = {
++		.rf_test = {
++			.tag = cpu_to_le16(UNI_RF_TEST_CTRL),
++			.len = cpu_to_le16(sizeof(req.rf_test)),
++			.action = RF_ACTION_GET,
++			.op.rf.func_idx = func_idx,
++			.op.rf.param.func_data = cpu_to_le32(data),
++		},
++	};
++	struct mt7996_tm_event *event;
++	struct sk_buff *skb;
++	int ret;
++
++	ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(TESTMODE_CTRL),
++					&req, sizeof(req), true, &skb);
++	if (ret)
++		return ret;
++
++	event = (struct mt7996_tm_event *)skb->data;
++	*result = event->result.payload_length;
++
++	dev_kfree_skb(skb);
++
++	return ret;
++}
++
++static void
++mt7996_tm_set_antenna(struct mt7996_phy *phy, u32 func_idx)
++{
++#define SPE_INDEX_MASK		BIT(31)
++#define TX_ANTENNA_MASK		GENMASK(3, 0)
++#define RX_ANTENNA_MASK		GENMASK(20, 16)		/* RX antenna mask at most 5 bit */
++	struct mt7996_dev *dev = phy->dev;
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	u32 antenna_mask;
++
++	if (!mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA))
++		return;
++
++	if (func_idx == SET_ID(TX_PATH))
++		antenna_mask = td->tx_spe_idx ? (SPE_INDEX_MASK | td->tx_spe_idx) :
++						td->tx_antenna_mask & TX_ANTENNA_MASK;
++	else if (func_idx == SET_ID(RX_PATH))
++		antenna_mask = u32_encode_bits(td->tx_antenna_mask, RX_ANTENNA_MASK);
++	else
++		return;
++
++	mt7996_tm_set(dev, func_idx, antenna_mask);
++}
++
++static void
++mt7996_tm_set_mac_addr(struct mt7996_dev *dev, u8 *addr, u32 func_idx)
++{
++#define REMAIN_PART_TAG		BIT(18)
++	u32 own_mac_first = 0, own_mac_remain = 0;
++	int len = sizeof(u32);
++
++	memcpy(&own_mac_first, addr, len);
++	mt7996_tm_set(dev, func_idx, own_mac_first);
++	/* Set the remain part of mac address */
++	memcpy(&own_mac_remain, addr + len, ETH_ALEN - len);
++	mt7996_tm_set(dev, func_idx | REMAIN_PART_TAG, own_mac_remain);
++}
++
++static int
++mt7996_tm_rf_switch_mode(struct mt7996_dev *dev, u32 op_mode)
++{
++	struct mt7996_tm_req req = {
++		.rf_test = {
++			.tag = cpu_to_le16(UNI_RF_TEST_CTRL),
++			.len = cpu_to_le16(sizeof(req.rf_test)),
++			.action = RF_ACTION_SWITCH_TO_RF_TEST,
++			.op.op_mode = cpu_to_le32(op_mode),
++		},
++	};
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), &req,
++				 sizeof(req), false);
++}
++
++static void
++mt7996_tm_init(struct mt7996_phy *phy, bool en)
++{
++	struct mt7996_dev *dev = phy->dev;
++	u8 rf_test_mode = en ? RF_OPER_RF_TEST : RF_OPER_NORMAL;
++
++	if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
++		return;
++
++	mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(ATE_MODE), en);
++	mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(SKU_POWER_LIMIT), !en);
++	mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(BACKOFF_POWER_LIMIT), !en);
++
++	mt7996_tm_rf_switch_mode(dev, rf_test_mode);
++
++	mt7996_mcu_add_bss_info(phy, phy->monitor_vif, en);
++	mt7996_mcu_add_sta(dev, phy->monitor_vif, NULL, en, false);
++
++	mt7996_tm_set(dev, SET_ID(BAND_IDX), phy->mt76->band_idx);
++
++	/* use firmware counter for RX stats */
++	phy->mt76->test.flag |= MT_TM_FW_RX_COUNT;
++}
++
++static void
++mt7996_tm_update_channel(struct mt7996_phy *phy)
++{
++#define CHAN_FREQ_BW_80P80_TAG		(SET_ID(CHAN_FREQ) | BIT(16))
++	struct mt7996_dev *dev = phy->dev;
++	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
++	struct ieee80211_channel *chan = chandef->chan;
++	u8 width = chandef->width;
++	static const u8 ch_band[] = {
++		[NL80211_BAND_2GHZ] = 0,
++		[NL80211_BAND_5GHZ] = 1,
++		[NL80211_BAND_6GHZ] = 2,
++	};
++
++	if (!chan || !chandef) {
++		dev_info(dev->mt76.dev, "chandef not found, channel update failed!\n");
++		return;
++	}
++
++	/* system bw */
++	mt7996_tm_set(dev, SET_ID(CBW), mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW));
++
++	if (width == NL80211_CHAN_WIDTH_80P80) {
++		width = NL80211_CHAN_WIDTH_160;
++		mt7996_tm_set(dev, CHAN_FREQ_BW_80P80_TAG, chandef->center_freq2 * 1000);
++	}
++
++	/* TODO: define per-packet bw */
++	/* per-packet bw */
++	mt7996_tm_set(dev, SET_ID(DBW), mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW));
++
++	/* control channel selection index */
++	mt7996_tm_set(dev, SET_ID(PRIMARY_CH), 0);
++	mt7996_tm_set(dev, SET_ID(BAND), ch_band[chan->band]);
++
++	/* trigger switch channel calibration */
++	mt7996_tm_set(dev, SET_ID(CHAN_FREQ), chandef->center_freq1 * 1000);
++
++	// TODO: update power limit table
++}
++
++static void
++mt7996_tm_tx_stop(struct mt76_phy *mphy)
++{
++	struct mt76_testmode_data *td = &mphy->test;
++	struct mt7996_phy *phy = mphy->priv;
++	struct mt7996_dev *dev = phy->dev;
++
++	mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(STOP_TEST));
++	td->tx_pending = 0;
++}
++
++static void
++mt7996_tm_set_tx_frames(struct mt7996_phy *phy, bool en)
++{
++#define FRAME_CONTROL		0x88
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct mt7996_dev *dev = phy->dev;
++
++	//TODO: RU operation, replace mcs, nss, and ldpc
++	if (en) {
++		mt7996_tm_set(dev, SET_ID(MAC_HEADER), FRAME_CONTROL);
++		mt7996_tm_set(dev, SET_ID(SEQ_CTRL), 0);
++		mt7996_tm_set(dev, SET_ID(TX_COUNT), td->tx_count);
++		mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
++		mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
++
++		if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER))
++			mt7996_tm_set(dev, SET_ID(POWER), td->tx_power[0]);
++
++		if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_TIME)) {
++			mt7996_tm_set(dev, SET_ID(TX_LEN), 0);
++			mt7996_tm_set(dev, SET_ID(TX_TIME), td->tx_time);
++		} else {
++			mt7996_tm_set(dev, SET_ID(TX_LEN), td->tx_mpdu_len);
++			mt7996_tm_set(dev, SET_ID(TX_TIME), 0);
++		}
++
++		mt7996_tm_set_antenna(phy, SET_ID(TX_PATH));
++		mt7996_tm_set_antenna(phy, SET_ID(RX_PATH));
++		mt7996_tm_set(dev, SET_ID(STBC), td->tx_rate_stbc);
++		mt7996_tm_set(dev, SET_ID(ENCODE_MODE), td->tx_rate_ldpc);
++		mt7996_tm_set(dev, SET_ID(IBF_ENABLE), td->ibf);
++		mt7996_tm_set(dev, SET_ID(EBF_ENABLE), td->ebf);
++		mt7996_tm_set(dev, SET_ID(IPG), td->tx_ipg);
++		mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi);
++		mt7996_tm_set(dev, SET_ID(NSS), td->tx_rate_nss);
++		mt7996_tm_set(dev, SET_ID(AID_OFFSET), 0);
++		mt7996_tm_set(dev, SET_ID(PUNCTURE), td->tx_preamble_puncture);
++
++		mt7996_tm_set(dev, SET_ID(MAX_PE), 2);
++		mt7996_tm_set(dev, SET_ID(HW_TX_MODE), 0);
++		mt7996_tm_update_channel(phy);
++
++		/* trigger firmware to start TX */
++		mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(START_TX));
++	} else {
++		mt7996_tm_tx_stop(phy->mt76);
++	}
++}
++
++static int
++mt7996_tm_rx_stats_user_ctrl(struct mt7996_phy *phy, u16 user_idx)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct mt7996_tm_rx_req req = {
++		.band = phy->mt76->band_idx,
++		.user_ctrl = {
++			.tag = cpu_to_le16(UNI_TM_RX_STAT_SET_USER_CTRL),
++			.len = cpu_to_le16(sizeof(req.user_ctrl)),
++			.band_idx = phy->mt76->band_idx,
++			.user_idx = cpu_to_le16(user_idx),
++		},
++	};
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_RX_STAT), &req,
++				 sizeof(req), false);
++}
++
++static void
++mt7996_tm_set_rx_frames(struct mt7996_phy *phy, bool en)
++{
++#define RX_MU_DISABLE	0xf800
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct mt7996_dev *dev = phy->dev;
++	int ret;
++
++	if (en) {
++		ret = mt7996_tm_rx_stats_user_ctrl(phy, td->aid);
++		if (ret) {
++			dev_info(dev->mt76.dev, "Set RX stats user control failed!\n");
++			return;
++		}
++
++		mt7996_tm_update_channel(phy);
++
++		if (td->tx_rate_mode >= MT76_TM_TX_MODE_HE_MU) {
++			if (td->aid)
++				ret = mt7996_tm_set(dev, SET_ID(RX_MU_AID), td->aid);
++			else
++				ret = mt7996_tm_set(dev, SET_ID(RX_MU_AID), RX_MU_DISABLE);
++		}
++		mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
++		mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi);
++		mt7996_tm_set_antenna(phy, SET_ID(RX_PATH));
++		mt7996_tm_set(dev, SET_ID(MAX_PE), 2);
++
++		mt7996_tm_set_mac_addr(dev, td->addr[1], SET_ID(SA));
++
++		/* trigger firmware to start RX */
++		mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(START_RX));
++	} else {
++		/* trigger firmware to stop RX */
++		mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(STOP_TEST));
++	}
++}
++
++static void
++mt7996_tm_set_tx_cont(struct mt7996_phy *phy, bool en)
++{
++#define CONT_WAVE_MODE_OFDM	3
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct mt7996_dev *dev = phy->dev;
++
++	if (en) {
++		mt7996_tm_update_channel(phy);
++		mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
++		mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
++		/* fix payload is OFDM */
++		mt7996_tm_set(dev, SET_ID(CONT_WAVE_MODE), CONT_WAVE_MODE_OFDM);
++		mt7996_tm_set(dev, SET_ID(ANT_MASK), td->tx_antenna_mask);
++
++		/* trigger firmware to start CONT TX */
++		mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(CONT_WAVE));
++	} else {
++		/* trigger firmware to stop CONT TX  */
++		mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(STOP_TEST));
++	}
++}
++
++static void
++mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
++{
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct mt7996_dev *dev = phy->dev;
++
++	if (changed & BIT(TM_CHANGED_FREQ_OFFSET)) {
++		mt7996_tm_set(dev, SET_ID(FREQ_OFFSET), td->freq_offset);
++		mt7996_tm_set(dev, SET_ID(FREQ_OFFSET_C2), td->freq_offset);
++	}
++	if (changed & BIT(TM_CHANGED_TXPOWER))
++		mt7996_tm_set(dev, SET_ID(POWER), td->tx_power[0]);
++	if (changed & BIT(TM_CHANGED_SKU_EN)) {
++		mt7996_tm_update_channel(phy);
++		mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(SKU_POWER_LIMIT), td->sku_en);
++		mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(BACKOFF_POWER_LIMIT), td->sku_en);
++		mt7996_mcu_set_txpower_sku(phy);
++	}
++	if (changed & BIT(TM_CHANGED_TX_LENGTH)) {
++		mt7996_tm_set(dev, SET_ID(TX_LEN), td->tx_mpdu_len);
++		mt7996_tm_set(dev, SET_ID(TX_TIME), 0);
++	}
++	if (changed & BIT(TM_CHANGED_TX_TIME)) {
++		mt7996_tm_set(dev, SET_ID(TX_LEN), 0);
++		mt7996_tm_set(dev, SET_ID(TX_TIME), td->tx_time);
++	}
++	if (changed & BIT(TM_CHANGED_CFG)) {
++		u32 func_idx = td->cfg.enable ? SET_ID(CFG_ON) : SET_ID(CFG_OFF);
++
++		mt7996_tm_set(dev, func_idx, td->cfg.type);
++	}
++}
++
++static int
++mt7996_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
++{
++	struct mt76_testmode_data *td = &mphy->test;
++	struct mt7996_phy *phy = mphy->priv;
++	enum mt76_testmode_state prev_state = td->state;
++
++	mphy->test.state = state;
++
++	if (prev_state != MT76_TM_STATE_OFF)
++		mt7996_tm_set(phy->dev, SET_ID(BAND_IDX), mphy->band_idx);
++
++	if (prev_state == MT76_TM_STATE_TX_FRAMES ||
++	    state == MT76_TM_STATE_TX_FRAMES)
++		mt7996_tm_set_tx_frames(phy, state == MT76_TM_STATE_TX_FRAMES);
++	else if (prev_state == MT76_TM_STATE_RX_FRAMES ||
++		 state == MT76_TM_STATE_RX_FRAMES)
++		mt7996_tm_set_rx_frames(phy, state == MT76_TM_STATE_RX_FRAMES);
++	else if (prev_state == MT76_TM_STATE_TX_CONT ||
++		 state == MT76_TM_STATE_TX_CONT)
++		mt7996_tm_set_tx_cont(phy, state == MT76_TM_STATE_TX_CONT);
++	else if (prev_state == MT76_TM_STATE_OFF ||
++		 state == MT76_TM_STATE_OFF)
++		mt7996_tm_init(phy, !(state == MT76_TM_STATE_OFF));
++
++	if ((state == MT76_TM_STATE_IDLE &&
++	     prev_state == MT76_TM_STATE_OFF) ||
++	    (state == MT76_TM_STATE_OFF &&
++	     prev_state == MT76_TM_STATE_IDLE)) {
++		u32 changed = 0;
++		int i, ret;
++
++		for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
++			u16 cur = tm_change_map[i];
++
++			if (mt76_testmode_param_present(td, cur))
++				changed |= BIT(i);
++		}
++
++		ret = mt7996_tm_check_antenna(phy);
++		if (ret)
++			return ret;
++
++		mt7996_tm_update_params(phy, changed);
++	}
++
++	return 0;
++}
++
++static int
++mt7996_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
++		     enum mt76_testmode_state new_state)
++{
++	struct mt76_testmode_data *td = &mphy->test;
++	struct mt7996_phy *phy = mphy->priv;
++	struct mt7996_dev *dev = phy->dev;
++	u32 changed = 0;
++	int i, ret;
++
++	BUILD_BUG_ON(NUM_TM_CHANGED >= 32);
++
++	if (new_state == MT76_TM_STATE_OFF ||
++	    td->state == MT76_TM_STATE_OFF)
++		return 0;
++
++	ret = mt7996_tm_check_antenna(phy);
++	if (ret)
++		return ret;
++
++	for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
++		if (tb[tm_change_map[i]])
++			changed |= BIT(i);
++	}
++
++	mt7996_tm_set(dev, SET_ID(BAND_IDX), mphy->band_idx);
++	mt7996_tm_update_params(phy, changed);
++
++	return 0;
++}
++
++static int
++mt7996_tm_get_rx_stats(struct mt7996_phy *phy)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct mt7996_tm_rx_req req = {
++		.band = phy->mt76->band_idx,
++		.rx_stat_all = {
++			.tag = cpu_to_le16(UNI_TM_RX_STAT_GET_ALL_V2),
++			.len = cpu_to_le16(sizeof(req.rx_stat_all)),
++			.band_idx = phy->mt76->band_idx,
++		},
++	};
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct mt7996_tm_rx_event *rx_stats;
++	struct mt7996_tm_rx_event_stat_all *rx_stats_all;
++	struct sk_buff *skb;
++	enum mt76_rxq_id qid;
++	int i, ret = 0;
++	u32 mac_rx_mdrdy_cnt;
++	u16 mac_rx_len_mismatch, fcs_err_count;
++
++	if (td->state != MT76_TM_STATE_RX_FRAMES)
++		return 0;
++
++	ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(TESTMODE_RX_STAT),
++					&req, sizeof(req), true, &skb);
++
++	if (ret)
++		return ret;
++
++	rx_stats = (struct mt7996_tm_rx_event *)skb->data;
++	rx_stats_all = &rx_stats->rx_stat_all;
++
++	phy->test.last_freq_offset = le32_to_cpu(rx_stats_all->user_info[0].freq_offset);
++	phy->test.last_snr = le32_to_cpu(rx_stats_all->user_info[0].snr);
++	for (i = 0; i < ARRAY_SIZE(phy->test.last_rcpi); i++) {
++		phy->test.last_rcpi[i] = le16_to_cpu(rx_stats_all->rxv_info[i].rcpi);
++		phy->test.last_rssi[i] = le16_to_cpu(rx_stats_all->rxv_info[i].rssi);
++		phy->test.last_ib_rssi[i] = rx_stats_all->fagc[i].ib_rssi;
++		phy->test.last_wb_rssi[i] = rx_stats_all->fagc[i].wb_rssi;
++	}
++
++	if (phy->mt76->band_idx == 2)
++		qid = MT_RXQ_BAND2;
++	else if (phy->mt76->band_idx == 1)
++		qid = MT_RXQ_BAND1;
++	else
++		qid = MT_RXQ_MAIN;
++
++	fcs_err_count = le16_to_cpu(rx_stats_all->band_info.mac_rx_fcs_err_cnt);
++	mac_rx_len_mismatch = le16_to_cpu(rx_stats_all->band_info.mac_rx_len_mismatch);
++	mac_rx_mdrdy_cnt = le32_to_cpu(rx_stats_all->band_info.mac_rx_mdrdy_cnt);
++	td->rx_stats.packets[qid] += mac_rx_mdrdy_cnt;
++	td->rx_stats.packets[qid] += fcs_err_count;
++	td->rx_stats.fcs_error[qid] += fcs_err_count;
++	td->rx_stats.len_mismatch += mac_rx_len_mismatch;
++
++	dev_kfree_skb(skb);
++
++	return ret;
++}
++
++static void
++mt7996_tm_reset_trx_stats(struct mt76_phy *mphy)
++{
++	struct mt7996_phy *phy = mphy->priv;
++	struct mt7996_dev *dev = phy->dev;
++
++	memset(&mphy->test.rx_stats, 0, sizeof(mphy->test.rx_stats));
++	mt7996_tm_set(dev, SET_ID(TRX_COUNTER_RESET), 0);
++}
++
++static int
++mt7996_tm_get_tx_stats(struct mt7996_phy *phy)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	int ret;
++
++	if (td->state != MT76_TM_STATE_TX_FRAMES)
++		return 0;
++
++	ret = mt7996_tm_get(dev, GET_ID(TXED_COUNT), 0, &td->tx_done);
++	if (ret)
++		return ret;
++
++	td->tx_pending = td->tx_count - td->tx_done;
++
++	return ret;
++}
++
++static int
++mt7996_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg)
++{
++	struct mt7996_phy *phy = mphy->priv;
++	void *rx, *rssi;
++	int i;
++
++	mt7996_tm_set(phy->dev, SET_ID(BAND_IDX), mphy->band_idx);
++	mt7996_tm_get_rx_stats(phy);
++	mt7996_tm_get_tx_stats(phy);
++
++	rx = nla_nest_start(msg, MT76_TM_STATS_ATTR_LAST_RX);
++	if (!rx)
++		return -ENOMEM;
++
++	if (nla_put_s32(msg, MT76_TM_RX_ATTR_FREQ_OFFSET, phy->test.last_freq_offset))
++		return -ENOMEM;
++
++	rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_RCPI);
++	if (!rssi)
++		return -ENOMEM;
++
++	for (i = 0; i < ARRAY_SIZE(phy->test.last_rcpi); i++)
++		if (nla_put_u8(msg, i, phy->test.last_rcpi[i]))
++			return -ENOMEM;
++
++	nla_nest_end(msg, rssi);
++
++	rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_RSSI);
++	if (!rssi)
++		return -ENOMEM;
++
++	for (i = 0; i < ARRAY_SIZE(phy->test.last_rssi); i++)
++		if (nla_put_s8(msg, i, phy->test.last_rssi[i]))
++			return -ENOMEM;
++
++	nla_nest_end(msg, rssi);
++
++	rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_IB_RSSI);
++	if (!rssi)
++		return -ENOMEM;
++
++	for (i = 0; i < ARRAY_SIZE(phy->test.last_ib_rssi); i++)
++		if (nla_put_s8(msg, i, phy->test.last_ib_rssi[i]))
++			return -ENOMEM;
++
++	nla_nest_end(msg, rssi);
++
++	rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_WB_RSSI);
++	if (!rssi)
++		return -ENOMEM;
++
++	for (i = 0; i < ARRAY_SIZE(phy->test.last_wb_rssi); i++)
++		if (nla_put_s8(msg, i, phy->test.last_wb_rssi[i]))
++			return -ENOMEM;
++
++	nla_nest_end(msg, rssi);
++
++	if (nla_put_u8(msg, MT76_TM_RX_ATTR_SNR, phy->test.last_snr))
++		return -ENOMEM;
++
++	nla_nest_end(msg, rx);
++
++	return 0;
++}
++
++static int
++mt7996_tm_write_back_to_efuse(struct mt7996_dev *dev)
++{
++	struct mt7996_mcu_eeprom_info req = {
++		.tag = cpu_to_le16(UNI_EFUSE_ACCESS),
++		.len = cpu_to_le16(sizeof(req) - 4),
++	};
++	u8 read_buf[MT76_TM_EEPROM_BLOCK_SIZE], *eeprom = dev->mt76.eeprom.data;
++	int i, ret = -EINVAL;
++
++	/* prevent from damaging chip id in efuse */
++	if (mt76_chip(&dev->mt76) != get_unaligned_le16(eeprom))
++		goto out;
++
++	for (i = 0; i < MT7996_EEPROM_SIZE; i += MT76_TM_EEPROM_BLOCK_SIZE) {
++		req.addr = cpu_to_le32(i);
++		memcpy(req.data, eeprom + i, MT76_TM_EEPROM_BLOCK_SIZE);
++
++		ret = mt7996_mcu_get_eeprom(dev, i, read_buf);
++		if (ret < 0)
++			return ret;
++
++		if (!memcmp(req.data, read_buf, MT76_TM_EEPROM_BLOCK_SIZE))
++			continue;
++
++		ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(EFUSE_CTRL),
++					&req, sizeof(req), true);
++		if (ret)
++			return ret;
++	}
++
++out:
++	return ret;
++}
++
++static int
++mt7996_tm_set_eeprom(struct mt76_phy *mphy, u32 offset, u8 *val, u8 action)
++{
++	struct mt7996_phy *phy = mphy->priv;
++	struct mt7996_dev *dev = phy->dev;
++	u8 *eeprom = dev->mt76.eeprom.data;
++	int ret = 0;
++
++	if (offset >= MT7996_EEPROM_SIZE)
++		return -EINVAL;
++
++	switch (action) {
++	case MT76_TM_EEPROM_ACTION_UPDATE_DATA:
++		memcpy(eeprom + offset, val, MT76_TM_EEPROM_BLOCK_SIZE);
++		break;
++	case MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE:
++		ret = mt7996_mcu_set_eeprom(dev);
++		break;
++	case MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE:
++		ret = mt7996_tm_write_back_to_efuse(dev);
++		break;
++	default:
++		break;
++	}
++
++	return ret;
++}
++
++const struct mt76_testmode_ops mt7996_testmode_ops = {
++	.set_state = mt7996_tm_set_state,
++	.set_params = mt7996_tm_set_params,
++	.dump_stats = mt7996_tm_dump_stats,
++	.reset_rx_stats = mt7996_tm_reset_trx_stats,
++	.tx_stop = mt7996_tm_tx_stop,
++	.set_eeprom = mt7996_tm_set_eeprom,
++};
+diff --git a/mt7996/testmode.h b/mt7996/testmode.h
+new file mode 100644
+index 00000000..319ef257
+--- /dev/null
++++ b/mt7996/testmode.h
+@@ -0,0 +1,299 @@
++/* SPDX-License-Identifier: ISC */
++/* Copyright (C) 2020 MediaTek Inc. */
++
++#ifndef __MT7996_TESTMODE_H
++#define __MT7996_TESTMODE_H
++
++enum {
++	TM_CBW_20MHZ,
++	TM_CBW_40MHZ,
++	TM_CBW_80MHZ,
++	TM_CBW_10MHZ,
++	TM_CBW_5MHZ,
++	TM_CBW_160MHZ,
++	TM_CBW_8080MHZ,
++	TM_CBW_320MHZ = 12,
++};
++
++/* BW defined in FW hal_cal_flow_rom.h */
++enum {
++	FW_CDBW_20MHZ,
++	FW_CDBW_40MHZ,
++	FW_CDBW_80MHZ,
++	FW_CDBW_160MHZ,
++	FW_CDBW_320MHZ,
++	FW_CDBW_5MHZ,
++	FW_CDBW_10MHZ,
++	FW_CDBW_8080MHZ,
++};
++
++enum bw_mapping_method {
++	BW_MAP_NL_TO_FW,
++	BW_MAP_NL_TO_TM,
++
++	NUM_BW_MAP,
++};
++
++struct mt7996_tm_rf_test {
++	__le16 tag;
++	__le16 len;
++
++	u8 action;
++	u8 icap_len;
++	u8 _rsv[2];
++	union {
++		__le32 op_mode;
++		__le32 freq;
++
++		struct {
++			__le32 func_idx;
++			union {
++				__le32 func_data;
++				__le32 cal_dump;
++
++				u8 _pad[80];
++			} param;
++		} rf;
++	} op;
++} __packed;
++
++struct mt7996_tm_req {
++	u8 _rsv[4];
++
++	struct mt7996_tm_rf_test rf_test;
++} __packed;
++
++struct mt7996_tm_rf_test_result {
++	__le32 func_idx;
++	__le32 payload_length;
++	u8 event[0];
++};
++
++struct mt7996_tm_event {
++	u8 _rsv[4];
++
++	__le16 tag;
++	__le16 len;
++	struct mt7996_tm_rf_test_result result;
++} __packed;
++
++enum {
++	RF_ACTION_SWITCH_TO_RF_TEST,
++	RF_ACTION_IN_RF_TEST,
++	RF_ACTION_SET = 3,
++	RF_ACTION_GET,
++};
++
++enum {
++	RF_OPER_NORMAL,
++	RF_OPER_RF_TEST,
++	RF_OPER_ICAP,
++	RF_OPER_ICAP_OVERLAP,
++	RF_OPER_WIFI_SPECTRUM,
++};
++
++enum {
++	UNI_RF_TEST_CTRL,
++};
++
++#define RF_CMD(cmd)		RF_TEST_CMD_##cmd
++
++enum {
++	RF_TEST_CMD_STOP_TEST = 0,
++	RF_TEST_CMD_START_TX = 1,
++	RF_TEST_CMD_START_RX = 2,
++	RF_TEST_CMD_CONT_WAVE = 10,
++	RF_TEST_CMD_TX_COMMIT = 18,
++	RF_TEST_CMD_RX_COMMIT = 19,
++};
++
++#define SET_ID(id)		RF_TEST_ID_SET_##id
++#define GET_ID(id)		RF_TEST_ID_GET_##id
++
++enum {
++	RF_TEST_ID_SET_COMMAND = 1,
++	RF_TEST_ID_SET_POWER = 2,
++	RF_TEST_ID_SET_TX_RATE = 3,
++	RF_TEST_ID_SET_TX_MODE = 4,
++	RF_TEST_ID_SET_TX_LEN = 6,
++	RF_TEST_ID_SET_TX_COUNT = 7,
++	RF_TEST_ID_SET_IPG = 8,
++	RF_TEST_ID_SET_GI = 16,
++	RF_TEST_ID_SET_STBC = 17,
++	RF_TEST_ID_SET_CHAN_FREQ = 18,
++	RF_TEST_ID_GET_TXED_COUNT = 32,
++	RF_TEST_ID_SET_CONT_WAVE_MODE = 65,
++	RF_TEST_ID_SET_DA = 68,
++	RF_TEST_ID_SET_SA = 69,
++	RF_TEST_ID_SET_CBW = 71,
++	RF_TEST_ID_SET_DBW = 72,
++	RF_TEST_ID_SET_PRIMARY_CH = 73,
++	RF_TEST_ID_SET_ENCODE_MODE = 74,
++	RF_TEST_ID_SET_BAND = 90,
++	RF_TEST_ID_SET_TRX_COUNTER_RESET = 91,
++	RF_TEST_ID_SET_MAC_HEADER = 101,
++	RF_TEST_ID_SET_SEQ_CTRL = 102,
++	RF_TEST_ID_SET_PAYLOAD = 103,
++	RF_TEST_ID_SET_BAND_IDX = 104,
++	RF_TEST_ID_SET_RX_PATH = 106,
++	RF_TEST_ID_SET_FREQ_OFFSET = 107,
++	RF_TEST_ID_GET_FREQ_OFFSET = 108,
++	RF_TEST_ID_SET_TX_PATH = 113,
++	RF_TEST_ID_SET_NSS = 114,
++	RF_TEST_ID_SET_ANT_MASK = 115,
++	RF_TEST_ID_SET_IBF_ENABLE = 126,
++	RF_TEST_ID_SET_EBF_ENABLE = 127,
++	RF_TEST_ID_GET_TX_POWER = 136,
++	RF_TEST_ID_SET_RX_MU_AID = 157,
++	RF_TEST_ID_SET_HW_TX_MODE = 167,
++	RF_TEST_ID_SET_PUNCTURE = 168,
++	RF_TEST_ID_SET_FREQ_OFFSET_C2 = 171,
++	RF_TEST_ID_GET_FREQ_OFFSET_C2 = 172,
++	RF_TEST_ID_SET_CFG_ON = 176,
++	RF_TEST_ID_SET_CFG_OFF = 177,
++	RF_TEST_ID_SET_BSSID = 189,
++	RF_TEST_ID_SET_TX_TIME = 190,
++	RF_TEST_ID_SET_MAX_PE = 191,
++	RF_TEST_ID_SET_AID_OFFSET = 204,
++};
++
++#define POWER_CTRL(type)	UNI_TXPOWER_##type##_CTRL
++
++struct mt7996_tm_rx_stat_user_ctrl {
++	__le16 tag;
++	__le16 len;
++
++	u8 band_idx;
++	u8 rsv;
++	__le16 user_idx;
++} __packed;
++
++struct mt7996_tm_rx_stat_all {
++	__le16 tag;
++	__le16 len;
++
++	u8 band_idx;
++	u8 rsv[3];
++} __packed;
++
++struct mt7996_tm_rx_req {
++	u8 band;
++	u8 _rsv[3];
++
++	union {
++		struct mt7996_tm_rx_stat_user_ctrl user_ctrl;
++		struct mt7996_tm_rx_stat_all rx_stat_all;
++	};
++} __packed;
++
++enum {
++	UNI_TM_RX_STAT_SET_USER_CTRL = 7,
++	UNI_TM_RX_STAT_GET_ALL_V2 = 9,
++};
++
++struct rx_band_info {
++	/* mac part */
++	__le16 mac_rx_fcs_err_cnt;
++	__le16 mac_rx_len_mismatch;
++	__le16 mac_rx_fcs_ok_cnt;
++	u8 rsv1[2];
++	__le32 mac_rx_mdrdy_cnt;
++
++	/* phy part */
++	__le16 phy_rx_fcs_err_cnt_cck;
++	__le16 phy_rx_fcs_err_cnt_ofdm;
++	__le16 phy_rx_pd_cck;
++	__le16 phy_rx_pd_ofdm;
++	__le16 phy_rx_sig_err_cck;
++	__le16 phy_rx_sfd_err_cck;
++	__le16 phy_rx_sig_err_ofdm;
++	__le16 phy_rx_tag_err_ofdm;
++	__le16 phy_rx_mdrdy_cnt_cck;
++	__le16 phy_rx_mdrdy_cnt_ofdm;
++} __packed;
++
++struct rx_band_info_ext {
++	/* mac part */
++	__le32 mac_rx_mpdu_cnt;
++
++	/* phy part */
++	u8 rsv[4];
++} __packed;
++
++struct rx_common_info {
++	__le16 rx_fifo_full;
++	u8 rsv[2];
++	__le32 aci_hit_low;
++	__le32 aci_hit_high;
++} __packed;
++
++struct rx_common_info_ext {
++	__le32 driver_rx_count;
++	__le32 sinr;
++	__le32 mu_pkt_count;
++
++	/* mac part */
++	u8 _rsv[4];
++
++	/* phy part */
++	u8 sig_mcs;
++	u8 rsv[3];
++} __packed;
++
++struct rx_rxv_info {
++	__le16 rcpi;
++	s16 rssi;
++	s16 snr;
++	s16 adc_rssi;
++} __packed;
++
++struct rx_rssi_info {
++	s8 ib_rssi;
++	s8 wb_rssi;
++	u8 rsv[2];
++} __packed;
++
++struct rx_user_info {
++	s32 freq_offset;
++	s32 snr;
++	__le32 fcs_err_count;
++} __packed;
++
++struct rx_user_info_ext {
++	s8 ne_var_db_all_user;
++	u8 rsv[3];
++} __packed;
++
++#define MAX_ANTENNA_NUM		8
++#define MAX_USER_NUM		16
++
++struct mt7996_tm_rx_event_stat_all {
++	__le16 tag;
++	__le16 len;
++
++	struct rx_band_info band_info;
++	struct rx_band_info_ext band_info_ext;
++	struct rx_common_info common_info;
++	struct rx_common_info_ext common_info_ext;
++
++	/* RXV info */
++	struct rx_rxv_info rxv_info[MAX_ANTENNA_NUM];
++
++	/* RSSI info */
++	struct rx_rssi_info fagc[MAX_ANTENNA_NUM];
++	struct rx_rssi_info inst[MAX_ANTENNA_NUM];
++
++	/* User info */
++	struct rx_user_info user_info[MAX_USER_NUM];
++	struct rx_user_info_ext user_info_ext[MAX_USER_NUM];
++} __packed;
++
++struct mt7996_tm_rx_event {
++	u8 _rsv[4];
++
++	union {
++		struct mt7996_tm_rx_event_stat_all rx_stat_all;
++	};
++} __packed;
++
++#endif
+diff --git a/testmode.c b/testmode.c
+index ca4feccf..44f3a5bf 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -2,11 +2,13 @@
+ /* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
+ 
+ #include <linux/random.h>
++#include "mt76_connac.h"
+ #include "mt76.h"
+ 
+ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
+ 	[MT76_TM_ATTR_RESET] = { .type = NLA_FLAG },
+ 	[MT76_TM_ATTR_STATE] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_SKU_EN] = { .type = NLA_U8 },
+ 	[MT76_TM_ATTR_TX_COUNT] = { .type = NLA_U32 },
+ 	[MT76_TM_ATTR_TX_LENGTH] = { .type = NLA_U32 },
+ 	[MT76_TM_ATTR_TX_RATE_MODE] = { .type = NLA_U8 },
+@@ -82,6 +84,11 @@ mt76_testmode_max_mpdu_len(struct mt76_phy *phy, u8 tx_rate_mode)
+ 		    IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991)
+ 			return IEEE80211_MAX_MPDU_LEN_VHT_7991;
+ 		return IEEE80211_MAX_MPDU_LEN_VHT_11454;
++	case MT76_TM_TX_MODE_EHT_SU:
++	case MT76_TM_TX_MODE_EHT_TRIG:
++	case MT76_TM_TX_MODE_EHT_MU:
++		/* TODO: check the limit */
++		return UINT_MAX;
+ 	case MT76_TM_TX_MODE_CCK:
+ 	case MT76_TM_TX_MODE_OFDM:
+ 	default:
+@@ -183,6 +190,9 @@ mt76_testmode_tx_init(struct mt76_phy *phy)
+ 	u8 max_nss = hweight8(phy->antenna_mask);
+ 	int ret;
+ 
++	if (is_mt7996(phy->dev))
++		return 0;
++
+ 	ret = mt76_testmode_alloc_skb(phy, td->tx_mpdu_len);
+ 	if (ret)
+ 		return ret;
+@@ -275,7 +285,9 @@ mt76_testmode_tx_start(struct mt76_phy *phy)
+ 	td->tx_queued = 0;
+ 	td->tx_done = 0;
+ 	td->tx_pending = td->tx_count;
+-	mt76_worker_schedule(&dev->tx_worker);
++
++	if (!is_mt7996(dev))
++		mt76_worker_schedule(&dev->tx_worker);
+ }
+ 
+ static void
+@@ -284,6 +296,11 @@ mt76_testmode_tx_stop(struct mt76_phy *phy)
+ 	struct mt76_testmode_data *td = &phy->test;
+ 	struct mt76_dev *dev = phy->dev;
+ 
++	if (is_mt7996(dev) && dev->test_ops->tx_stop) {
++		dev->test_ops->tx_stop(phy);
++		return;
++	}
++
+ 	mt76_worker_disable(&dev->tx_worker);
+ 
+ 	td->tx_pending = 0;
+@@ -296,22 +313,11 @@ mt76_testmode_tx_stop(struct mt76_phy *phy)
+ 	mt76_testmode_free_skb(phy);
+ }
+ 
+-static inline void
+-mt76_testmode_param_set(struct mt76_testmode_data *td, u16 idx)
+-{
+-	td->param_set[idx / 32] |= BIT(idx % 32);
+-}
+-
+-static inline bool
+-mt76_testmode_param_present(struct mt76_testmode_data *td, u16 idx)
+-{
+-	return td->param_set[idx / 32] & BIT(idx % 32);
+-}
+-
+ static void
+ mt76_testmode_init_defaults(struct mt76_phy *phy)
+ {
+ 	struct mt76_testmode_data *td = &phy->test;
++	u8 addr[ETH_ALEN] = {phy->band_idx, 0x11, 0x22, 0xaa, 0xbb, 0xcc};
+ 
+ 	if (td->tx_mpdu_len > 0)
+ 		return;
+@@ -319,11 +325,18 @@ mt76_testmode_init_defaults(struct mt76_phy *phy)
+ 	td->tx_mpdu_len = 1024;
+ 	td->tx_count = 1;
+ 	td->tx_rate_mode = MT76_TM_TX_MODE_OFDM;
++	td->tx_rate_idx = 7;
+ 	td->tx_rate_nss = 1;
++	/* 0xffff for OFDMA no puncture */
++	td->tx_preamble_puncture = ~(td->tx_preamble_puncture & 0);
++	td->tx_ipg = 50;
+ 
+-	memcpy(td->addr[0], phy->macaddr, ETH_ALEN);
+-	memcpy(td->addr[1], phy->macaddr, ETH_ALEN);
+-	memcpy(td->addr[2], phy->macaddr, ETH_ALEN);
++	/* rx stat user config */
++	td->aid = 1;
++
++	memcpy(td->addr[0], addr, ETH_ALEN);
++	memcpy(td->addr[1], addr, ETH_ALEN);
++	memcpy(td->addr[2], addr, ETH_ALEN);
+ }
+ 
+ static int
+@@ -353,7 +366,7 @@ __mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state)
+ 	if (state == MT76_TM_STATE_TX_FRAMES)
+ 		mt76_testmode_tx_start(phy);
+ 	else if (state == MT76_TM_STATE_RX_FRAMES) {
+-		memset(&phy->test.rx_stats, 0, sizeof(phy->test.rx_stats));
++		dev->test_ops->reset_rx_stats(phy);
+ 	}
+ 
+ 	phy->test.state = state;
+@@ -404,6 +417,44 @@ mt76_tm_get_u8(struct nlattr *attr, u8 *dest, u8 min, u8 max)
+ 	return 0;
+ }
+ 
++static int
++mt76_testmode_set_eeprom(struct mt76_phy *phy, struct nlattr **tb)
++{
++	struct mt76_dev *dev = phy->dev;
++	u8 action, val[MT76_TM_EEPROM_BLOCK_SIZE];
++	u32 offset = 0;
++	int err = -EINVAL;
++
++	if (!dev->test_ops->set_eeprom)
++		return -EOPNOTSUPP;
++
++	if (mt76_tm_get_u8(tb[MT76_TM_ATTR_EEPROM_ACTION], &action,
++			   0, MT76_TM_EEPROM_ACTION_MAX))
++		goto out;
++
++	if (tb[MT76_TM_ATTR_EEPROM_OFFSET]) {
++		struct nlattr *cur;
++		int rem, idx = 0;
++
++		offset = nla_get_u32(tb[MT76_TM_ATTR_EEPROM_OFFSET]);
++		if (!!(offset % MT76_TM_EEPROM_BLOCK_SIZE) ||
++		    !tb[MT76_TM_ATTR_EEPROM_VAL])
++			goto out;
++
++		nla_for_each_nested(cur, tb[MT76_TM_ATTR_EEPROM_VAL], rem) {
++			if (nla_len(cur) != 1 || idx >= ARRAY_SIZE(val))
++				goto out;
++
++			val[idx++] = nla_get_u8(cur);
++		}
++	}
++
++	err = dev->test_ops->set_eeprom(phy, offset, val, action);
++
++out:
++	return err;
++}
++
+ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 		      void *data, int len)
+ {
+@@ -427,6 +478,11 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 
+ 	mutex_lock(&dev->mutex);
+ 
++	if (tb[MT76_TM_ATTR_EEPROM_ACTION]) {
++		err = mt76_testmode_set_eeprom(phy, tb);
++		goto out;
++	}
++
+ 	if (tb[MT76_TM_ATTR_RESET]) {
+ 		mt76_testmode_set_state(phy, MT76_TM_STATE_OFF);
+ 		memset(td, 0, sizeof(*td));
+@@ -434,6 +490,9 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 
+ 	mt76_testmode_init_defaults(phy);
+ 
++	if (tb[MT76_TM_ATTR_SKU_EN])
++		td->sku_en = nla_get_u8(tb[MT76_TM_ATTR_SKU_EN]);
++
+ 	if (tb[MT76_TM_ATTR_TX_COUNT])
+ 		td->tx_count = nla_get_u32(tb[MT76_TM_ATTR_TX_COUNT]);
+ 
+@@ -454,7 +513,8 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_DUTY_CYCLE],
+ 			   &td->tx_duty_cycle, 0, 99) ||
+ 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_POWER_CONTROL],
+-			   &td->tx_power_control, 0, 1))
++			   &td->tx_power_control, 0, 1) ||
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_AID], &td->aid, 0, 16))
+ 		goto out;
+ 
+ 	if (tb[MT76_TM_ATTR_TX_LENGTH]) {
+@@ -494,7 +554,9 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 			    idx >= ARRAY_SIZE(td->tx_power))
+ 				goto out;
+ 
+-			td->tx_power[idx++] = nla_get_u8(cur);
++			err = mt76_tm_get_u8(cur, &td->tx_power[idx++], 0, 63);
++			if (err)
++				return err;
+ 		}
+ 	}
+ 
+@@ -512,6 +574,22 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 		}
+ 	}
+ 
++	if (tb[MT76_TM_ATTR_CFG]) {
++		struct nlattr *cur;
++		int rem, idx = 0;
++
++		nla_for_each_nested(cur, tb[MT76_TM_ATTR_CFG], rem) {
++			if (nla_len(cur) != 1 || idx >= 2)
++				goto out;
++
++			if (idx == 0)
++				td->cfg.type = nla_get_u8(cur);
++			else
++				td->cfg.enable = nla_get_u8(cur);
++			idx++;
++		}
++	}
++
+ 	if (dev->test_ops->set_params) {
+ 		err = dev->test_ops->set_params(phy, tb, state);
+ 		if (err)
+@@ -561,6 +639,9 @@ mt76_testmode_dump_stats(struct mt76_phy *phy, struct sk_buff *msg)
+ 	    nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_PACKETS, rx_packets,
+ 			      MT76_TM_STATS_ATTR_PAD) ||
+ 	    nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_FCS_ERROR, rx_fcs_error,
++			      MT76_TM_STATS_ATTR_PAD) ||
++	    nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_LEN_MISMATCH,
++			      td->rx_stats.len_mismatch,
+ 			      MT76_TM_STATS_ATTR_PAD))
+ 		return -EMSGSIZE;
+ 
+@@ -613,7 +694,8 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ 
+ 	if (dev->test_mtd.name &&
+ 	    (nla_put_string(msg, MT76_TM_ATTR_MTD_PART, dev->test_mtd.name) ||
+-	     nla_put_u32(msg, MT76_TM_ATTR_MTD_OFFSET, dev->test_mtd.offset)))
++	     nla_put_u32(msg, MT76_TM_ATTR_MTD_OFFSET, dev->test_mtd.offset) ||
++	     nla_put_u8(msg, MT76_TM_ATTR_BAND_IDX, phy->band_idx)))
+ 		goto out;
+ 
+ 	if (nla_put_u32(msg, MT76_TM_ATTR_TX_COUNT, td->tx_count) ||
+@@ -624,6 +706,8 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ 	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_SGI, td->tx_rate_sgi) ||
+ 	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_LDPC, td->tx_rate_ldpc) ||
+ 	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_STBC, td->tx_rate_stbc) ||
++	    nla_put_u8(msg, MT76_TM_ATTR_SKU_EN, td->sku_en) ||
++	    nla_put_u8(msg, MT76_TM_ATTR_AID, td->aid) ||
+ 	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_LTF) &&
+ 	     nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) ||
+ 	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA) &&
+@@ -639,7 +723,7 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ 	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER_CONTROL) &&
+ 	     nla_put_u8(msg, MT76_TM_ATTR_TX_POWER_CONTROL, td->tx_power_control)) ||
+ 	    (mt76_testmode_param_present(td, MT76_TM_ATTR_FREQ_OFFSET) &&
+-	     nla_put_u8(msg, MT76_TM_ATTR_FREQ_OFFSET, td->freq_offset)))
++	     nla_put_u32(msg, MT76_TM_ATTR_FREQ_OFFSET, td->freq_offset)))
+ 		goto out;
+ 
+ 	if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER)) {
+diff --git a/testmode.h b/testmode.h
+index 5e2792d8..96872e8c 100644
+--- a/testmode.h
++++ b/testmode.h
+@@ -5,7 +5,8 @@
+ #ifndef __MT76_TESTMODE_H
+ #define __MT76_TESTMODE_H
+ 
+-#define MT76_TM_TIMEOUT	10
++#define MT76_TM_TIMEOUT			10
++#define MT76_TM_EEPROM_BLOCK_SIZE	16
+ 
+ /**
+  * enum mt76_testmode_attr - testmode attributes inside NL80211_ATTR_TESTDATA
+@@ -17,7 +18,9 @@
+  *
+  * @MT76_TM_ATTR_MTD_PART: mtd partition used for eeprom data (string)
+  * @MT76_TM_ATTR_MTD_OFFSET: offset of eeprom data within the partition (u32)
++ * @MT76_TM_ATTR_BAND_IDX: band idx of the chip (u8)
+  *
++ * @MT76_TM_ATTR_SKU_EN: config txpower sku is enabled or disabled in testmode (u8)
+  * @MT76_TM_ATTR_TX_COUNT: configured number of frames to send when setting
+  *	state to MT76_TM_STATE_TX_FRAMES (u32)
+  * @MT76_TM_ATTR_TX_PENDING: pending frames during MT76_TM_STATE_TX_FRAMES (u32)
+@@ -38,6 +41,11 @@
+  *
+  * @MT76_TM_ATTR_STATS: statistics (nested, see &enum mt76_testmode_stats_attr)
+  *
++ * @MT76_TM_ATTR_PRECAL: Pre-cal data (u8)
++ * @MT76_TM_ATTR_PRECAL_INFO: group size, dpd size, dpd_info, transmit size,
++ *                            eeprom cal indicator (u32),
++ *                            dpd_info = [dpd_per_chan_size, chan_num_2g,
++ *                                        chan_num_5g, chan_num_6g]
+  * @MT76_TM_ATTR_TX_SPE_IDX: tx spatial extension index (u8)
+  *
+  * @MT76_TM_ATTR_TX_DUTY_CYCLE: packet tx duty cycle (u8)
+@@ -47,6 +55,29 @@
+  * @MT76_TM_ATTR_DRV_DATA: driver specific netlink attrs (nested)
+  *
+  * @MT76_TM_ATTR_MAC_ADDRS: array of nested MAC addresses (nested)
++ *
++ * @MT76_TM_ATTR_EEPROM_ACTION: eeprom setting actions
++ *	(u8, see &enum mt76_testmode_eeprom_action)
++ * @MT76_TM_ATTR_EEPROM_OFFSET: offset of eeprom data block for writing (u32)
++ * @MT76_TM_ATTR_EEPROM_VAL: values for writing into a 16-byte data block
++ *	(nested, u8 attrs)
++ *
++ * @MT76_TM_ATTR_CFG: config testmode rf feature (nested, see &mt76_testmode_cfg)
++ * @MT76_TM_ATTR_TXBF_ACT: txbf setting actions (u8)
++ * @MT76_TM_ATTR_TXBF_PARAM: txbf parameters (nested)
++ *
++ * @MT76_TM_ATTR_OFF_CH_SCAN_CH: config the channel of background chain (ZWDFS) (u8)
++ * @MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH: config the center channel of background chain (ZWDFS) (u8)
++ * @MT76_TM_ATTR_OFF_CH_SCAN_BW: config the bandwidth of background chain (ZWDFS) (u8)
++ * @MT76_TM_ATTR_OFF_CH_SCAN_PATH: config the tx path of background chain (ZWDFS) (u8)
++ *
++ * @MT76_TM_ATTR_IPI_THRESHOLD: config the IPI index you want to read (u8)
++ * @MT76_TM_ATTR_IPI_PERIOD: config the time period for reading
++ *			     the histogram of specific IPI index (u8)
++ * @MT76_TM_ATTR_IPI_ANTENNA_INDEX: config the antenna index for reading
++ *				    the histogram of specific IPI index (u8)
++ * @MT76_TM_ATTR_IPI_RESET: Reset the IPI counter
++ *
+  */
+ enum mt76_testmode_attr {
+ 	MT76_TM_ATTR_UNSPEC,
+@@ -56,7 +87,9 @@ enum mt76_testmode_attr {
+ 
+ 	MT76_TM_ATTR_MTD_PART,
+ 	MT76_TM_ATTR_MTD_OFFSET,
++	MT76_TM_ATTR_BAND_IDX,
+ 
++	MT76_TM_ATTR_SKU_EN,
+ 	MT76_TM_ATTR_TX_COUNT,
+ 	MT76_TM_ATTR_TX_LENGTH,
+ 	MT76_TM_ATTR_TX_RATE_MODE,
+@@ -74,6 +107,8 @@ enum mt76_testmode_attr {
+ 	MT76_TM_ATTR_FREQ_OFFSET,
+ 
+ 	MT76_TM_ATTR_STATS,
++	MT76_TM_ATTR_PRECAL,
++	MT76_TM_ATTR_PRECAL_INFO,
+ 
+ 	MT76_TM_ATTR_TX_SPE_IDX,
+ 
+@@ -84,6 +119,27 @@ enum mt76_testmode_attr {
+ 	MT76_TM_ATTR_DRV_DATA,
+ 
+ 	MT76_TM_ATTR_MAC_ADDRS,
++	MT76_TM_ATTR_AID,
++	MT76_TM_ATTR_RU_ALLOC,
++	MT76_TM_ATTR_RU_IDX,
++
++	MT76_TM_ATTR_EEPROM_ACTION,
++	MT76_TM_ATTR_EEPROM_OFFSET,
++	MT76_TM_ATTR_EEPROM_VAL,
++
++	MT76_TM_ATTR_CFG,
++	MT76_TM_ATTR_TXBF_ACT,
++	MT76_TM_ATTR_TXBF_PARAM,
++
++	MT76_TM_ATTR_OFF_CH_SCAN_CH,
++	MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH,
++	MT76_TM_ATTR_OFF_CH_SCAN_BW,
++	MT76_TM_ATTR_OFF_CH_SCAN_PATH,
++
++	MT76_TM_ATTR_IPI_THRESHOLD,
++	MT76_TM_ATTR_IPI_PERIOD,
++	MT76_TM_ATTR_IPI_ANTENNA_INDEX,
++	MT76_TM_ATTR_IPI_RESET,
+ 
+ 	/* keep last */
+ 	NUM_MT76_TM_ATTRS,
+@@ -101,6 +157,8 @@ enum mt76_testmode_attr {
+  * @MT76_TM_STATS_ATTR_RX_FCS_ERROR: number of rx packets with FCS error (u64)
+  * @MT76_TM_STATS_ATTR_LAST_RX: information about the last received packet
+  *	see &enum mt76_testmode_rx_attr
++ * @MT76_TM_STATS_ATTR_RX_LEN_MISMATCH: number of rx packets with length
++ *	mismatch error (u64)
+  */
+ enum mt76_testmode_stats_attr {
+ 	MT76_TM_STATS_ATTR_UNSPEC,
+@@ -113,6 +171,7 @@ enum mt76_testmode_stats_attr {
+ 	MT76_TM_STATS_ATTR_RX_PACKETS,
+ 	MT76_TM_STATS_ATTR_RX_FCS_ERROR,
+ 	MT76_TM_STATS_ATTR_LAST_RX,
++	MT76_TM_STATS_ATTR_RX_LEN_MISMATCH,
+ 
+ 	/* keep last */
+ 	NUM_MT76_TM_STATS_ATTRS,
+@@ -125,6 +184,7 @@ enum mt76_testmode_stats_attr {
+  *
+  * @MT76_TM_RX_ATTR_FREQ_OFFSET: frequency offset (s32)
+  * @MT76_TM_RX_ATTR_RCPI: received channel power indicator (array, u8)
++ * @MT76_TM_RX_ATTR_RSSI: received signal strength indicator (array, s8)
+  * @MT76_TM_RX_ATTR_IB_RSSI: internal inband RSSI (array, s8)
+  * @MT76_TM_RX_ATTR_WB_RSSI: internal wideband RSSI (array, s8)
+  * @MT76_TM_RX_ATTR_SNR: signal-to-noise ratio (u8)
+@@ -134,6 +194,7 @@ enum mt76_testmode_rx_attr {
+ 
+ 	MT76_TM_RX_ATTR_FREQ_OFFSET,
+ 	MT76_TM_RX_ATTR_RCPI,
++	MT76_TM_RX_ATTR_RSSI,
+ 	MT76_TM_RX_ATTR_IB_RSSI,
+ 	MT76_TM_RX_ATTR_WB_RSSI,
+ 	MT76_TM_RX_ATTR_SNR,
+@@ -177,6 +238,9 @@ enum mt76_testmode_state {
+  * @MT76_TM_TX_MODE_HE_EXT_SU: 802.11ax extended-range SU
+  * @MT76_TM_TX_MODE_HE_TB: 802.11ax trigger-based
+  * @MT76_TM_TX_MODE_HE_MU: 802.11ax multi-user MIMO
++ * @MT76_TM_TX_MODE_EHT_SU: 802.11be single-user MIMO
++ * @MT76_TM_TX_MODE_EHT_TRIG: 802.11be trigger-based
++ * @MT76_TM_TX_MODE_EHT_MU: 802.11be multi-user MIMO
+  */
+ enum mt76_testmode_tx_mode {
+ 	MT76_TM_TX_MODE_CCK,
+@@ -187,12 +251,33 @@ enum mt76_testmode_tx_mode {
+ 	MT76_TM_TX_MODE_HE_EXT_SU,
+ 	MT76_TM_TX_MODE_HE_TB,
+ 	MT76_TM_TX_MODE_HE_MU,
++	MT76_TM_TX_MODE_EHT_SU,
++	MT76_TM_TX_MODE_EHT_TRIG,
++	MT76_TM_TX_MODE_EHT_MU,
+ 
+ 	/* keep last */
+ 	NUM_MT76_TM_TX_MODES,
+ 	MT76_TM_TX_MODE_MAX = NUM_MT76_TM_TX_MODES - 1,
+ };
+ 
++/**
++ * enum mt76_testmode_eeprom_action - eeprom setting actions
++ *
++ * @MT76_TM_EEPROM_ACTION_UPDATE_DATA: update rf values to specific
++ *	eeprom data block
++ * @MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE: send updated eeprom data to fw
++ * @MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE: write eeprom data back to efuse
++ */
++enum mt76_testmode_eeprom_action {
++	MT76_TM_EEPROM_ACTION_UPDATE_DATA,
++	MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE,
++	MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE,
++
++	/* keep last */
++	NUM_MT76_TM_EEPROM_ACTION,
++	MT76_TM_EEPROM_ACTION_MAX = NUM_MT76_TM_EEPROM_ACTION - 1,
++};
++
+ extern const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS];
+ 
+ #endif
+diff --git a/tools/fields.c b/tools/fields.c
+index e3f69089..055f90f3 100644
+--- a/tools/fields.c
++++ b/tools/fields.c
+@@ -10,6 +10,7 @@ static const char * const testmode_state[] = {
+ 	[MT76_TM_STATE_IDLE] = "idle",
+ 	[MT76_TM_STATE_TX_FRAMES] = "tx_frames",
+ 	[MT76_TM_STATE_RX_FRAMES] = "rx_frames",
++	[MT76_TM_STATE_TX_CONT] = "tx_cont",
+ };
+ 
+ static const char * const testmode_tx_mode[] = {
+@@ -21,6 +22,9 @@ static const char * const testmode_tx_mode[] = {
+ 	[MT76_TM_TX_MODE_HE_EXT_SU] = "he_ext_su",
+ 	[MT76_TM_TX_MODE_HE_TB] = "he_tb",
+ 	[MT76_TM_TX_MODE_HE_MU] = "he_mu",
++	[MT76_TM_TX_MODE_EHT_SU] = "eht_su",
++	[MT76_TM_TX_MODE_EHT_TRIG] = "eht_tb",
++	[MT76_TM_TX_MODE_EHT_MU] = "eht_mu",
+ };
+ 
+ static void print_enum(const struct tm_field *field, struct nlattr *attr)
+@@ -65,7 +69,7 @@ static bool parse_u8(const struct tm_field *field, int idx,
+ 
+ static void print_u8(const struct tm_field *field, struct nlattr *attr)
+ {
+-	printf("%d", nla_get_u8(attr));
++	printf("%u", nla_get_u8(attr));
+ }
+ 
+ static void print_s8(const struct tm_field *field, struct nlattr *attr)
+@@ -86,12 +90,12 @@ static void print_s32(const struct tm_field *field, struct nlattr *attr)
+ 
+ static void print_u32(const struct tm_field *field, struct nlattr *attr)
+ {
+-	printf("%d", nla_get_u32(attr));
++	printf("%u", nla_get_u32(attr));
+ }
+ 
+ static void print_u64(const struct tm_field *field, struct nlattr *attr)
+ {
+-	printf("%lld", (unsigned long long)nla_get_u64(attr));
++	printf("%llu", (unsigned long long)nla_get_u64(attr));
+ }
+ 
+ static bool parse_flag(const struct tm_field *field, int idx,
+@@ -201,6 +205,62 @@ static void print_extra_stats(const struct tm_field *field, struct nlattr **tb)
+ 	printf("%srx_per=%.02f%%\n", prefix, 100 * failed / total);
+ }
+ 
++static bool parse_mac(const struct tm_field *field, int idx,
++		      struct nl_msg *msg, const char *val)
++{
++#define ETH_ALEN	6
++	bool ret = true;
++	char *str, *cur, *ap;
++	void *a;
++
++	str = strdup(val);
++	ap = str;
++
++	a = nla_nest_start(msg, idx);
++
++	idx = 0;
++	while ((cur = strsep(&ap, ",")) != NULL) {
++		unsigned char addr[ETH_ALEN];
++		char *val, *tmp = cur;
++		int i = 0;
++
++		while ((val = strsep(&tmp, ":")) != NULL) {
++			if (i >= ETH_ALEN)
++				break;
++
++			addr[i++] = strtoul(val, NULL, 16);
++		}
++
++		nla_put(msg, idx, ETH_ALEN, addr);
++
++		idx++;
++	}
++
++	nla_nest_end(msg, a);
++
++	free(str);
++
++	return ret;
++}
++
++static void print_mac(const struct tm_field *field, struct nlattr *attr)
++{
++#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
++#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
++	unsigned char addr[3][6];
++	struct nlattr *cur;
++	int idx = 0;
++	int rem;
++
++	nla_for_each_nested(cur, attr, rem) {
++		if (nla_len(cur) != 6)
++			continue;
++		memcpy(addr[idx++], nla_data(cur), 6);
++	}
++
++	printf("" MACSTR "," MACSTR "," MACSTR "",
++	       MAC2STR(addr[0]), MAC2STR(addr[1]), MAC2STR(addr[2]));
++}
+ 
+ #define FIELD_GENERIC(_field, _name, ...)	\
+ 	[FIELD_NAME(_field)] = {			\
+@@ -250,10 +310,18 @@ static void print_extra_stats(const struct tm_field *field, struct nlattr **tb)
+ 		 ##__VA_ARGS__				\
+ 	)
+ 
++#define FIELD_MAC(_field, _name)			\
++	[FIELD_NAME(_field)] = {			\
++		.name = _name,				\
++		.parse = parse_mac,			\
++		.print = print_mac			\
++	}
++
+ #define FIELD_NAME(_field) MT76_TM_RX_ATTR_##_field
+ static const struct tm_field rx_fields[NUM_MT76_TM_RX_ATTRS] = {
+ 	FIELD_RO(s32, FREQ_OFFSET, "freq_offset"),
+ 	FIELD_ARRAY_RO(u8, RCPI, "rcpi"),
++	FIELD_ARRAY_RO(s8, RSSI, "rssi"),
+ 	FIELD_ARRAY_RO(s8, IB_RSSI, "ib_rssi"),
+ 	FIELD_ARRAY_RO(s8, WB_RSSI, "wb_rssi"),
+ 	FIELD_RO(s8, SNR, "snr"),
+@@ -261,6 +329,7 @@ static const struct tm_field rx_fields[NUM_MT76_TM_RX_ATTRS] = {
+ static struct nla_policy rx_policy[NUM_MT76_TM_RX_ATTRS] = {
+ 	[MT76_TM_RX_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
+ 	[MT76_TM_RX_ATTR_RCPI] = { .type = NLA_NESTED },
++	[MT76_TM_RX_ATTR_RSSI] = { .type = NLA_NESTED },
+ 	[MT76_TM_RX_ATTR_IB_RSSI] = { .type = NLA_NESTED },
+ 	[MT76_TM_RX_ATTR_WB_RSSI] = { .type = NLA_NESTED },
+ 	[MT76_TM_RX_ATTR_SNR] = { .type = NLA_U8 },
+@@ -274,6 +343,7 @@ static const struct tm_field stats_fields[NUM_MT76_TM_STATS_ATTRS] = {
+ 	FIELD_RO(u32, TX_DONE, "tx_done"),
+ 	FIELD_RO(u64, RX_PACKETS, "rx_packets"),
+ 	FIELD_RO(u64, RX_FCS_ERROR, "rx_fcs_error"),
++	FIELD_RO(u64, RX_LEN_MISMATCH, "rx_len_mismatch"),
+ 	FIELD_NESTED_RO(LAST_RX, rx, "last_"),
+ };
+ static struct nla_policy stats_policy[NUM_MT76_TM_STATS_ATTRS] = {
+@@ -282,6 +352,7 @@ static struct nla_policy stats_policy[NUM_MT76_TM_STATS_ATTRS] = {
+ 	[MT76_TM_STATS_ATTR_TX_DONE] = { .type = NLA_U32 },
+ 	[MT76_TM_STATS_ATTR_RX_PACKETS] = { .type = NLA_U64 },
+ 	[MT76_TM_STATS_ATTR_RX_FCS_ERROR] = { .type = NLA_U64 },
++	[MT76_TM_STATS_ATTR_RX_LEN_MISMATCH] = { .type = NLA_U64 },
+ };
+ #undef FIELD_NAME
+ 
+@@ -291,6 +362,7 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
+ 	FIELD_ENUM(STATE, "state", testmode_state),
+ 	FIELD_RO(string, MTD_PART, "mtd_part"),
+ 	FIELD_RO(u32, MTD_OFFSET, "mtd_offset"),
++	FIELD(u8, SKU_EN, "sku_en"),
+ 	FIELD(u32, TX_COUNT, "tx_count"),
+ 	FIELD(u32, TX_LENGTH, "tx_length"),
+ 	FIELD_ENUM(TX_RATE_MODE, "tx_rate_mode", testmode_tx_mode),
+@@ -300,12 +372,20 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
+ 	FIELD(u8, TX_RATE_LDPC, "tx_rate_ldpc"),
+ 	FIELD(u8, TX_RATE_STBC, "tx_rate_stbc"),
+ 	FIELD(u8, TX_LTF, "tx_ltf"),
++	FIELD(u8, TX_DUTY_CYCLE, "tx_duty_cycle"),
++	FIELD(u32, TX_IPG, "tx_ipg"),
++	FIELD(u32, TX_TIME, "tx_time"),
+ 	FIELD(u8, TX_POWER_CONTROL, "tx_power_control"),
+ 	FIELD_ARRAY(u8, TX_POWER, "tx_power"),
+ 	FIELD(u8, TX_ANTENNA, "tx_antenna"),
+ 	FIELD(u32, FREQ_OFFSET, "freq_offset"),
++	FIELD(u8, AID, "aid"),
++	FIELD(u8, RU_ALLOC, "ru_alloc"),
++	FIELD(u8, RU_IDX, "ru_idx"),
++	FIELD_MAC(MAC_ADDRS, "mac_addrs"),
+ 	FIELD_NESTED_RO(STATS, stats, "",
+ 			.print_extra = print_extra_stats),
++
+ };
+ #undef FIELD_NAME
+ 
+@@ -313,6 +393,7 @@ static struct nla_policy testdata_policy[NUM_MT76_TM_ATTRS] = {
+ 	[MT76_TM_ATTR_STATE] = { .type = NLA_U8 },
+ 	[MT76_TM_ATTR_MTD_PART] = { .type = NLA_STRING },
+ 	[MT76_TM_ATTR_MTD_OFFSET] = { .type = NLA_U32 },
++	[MT76_TM_ATTR_SKU_EN] = { .type = NLA_U8 },
+ 	[MT76_TM_ATTR_TX_COUNT] = { .type = NLA_U32 },
+ 	[MT76_TM_ATTR_TX_LENGTH] = { .type = NLA_U32 },
+ 	[MT76_TM_ATTR_TX_RATE_MODE] = { .type = NLA_U8 },
+@@ -322,10 +403,25 @@ static struct nla_policy testdata_policy[NUM_MT76_TM_ATTRS] = {
+ 	[MT76_TM_ATTR_TX_RATE_LDPC] = { .type = NLA_U8 },
+ 	[MT76_TM_ATTR_TX_RATE_STBC] = { .type = NLA_U8 },
+ 	[MT76_TM_ATTR_TX_LTF] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_TX_DUTY_CYCLE] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_TX_IPG] = { .type = NLA_U32 },
++	[MT76_TM_ATTR_TX_TIME] = { .type = NLA_U32 },
+ 	[MT76_TM_ATTR_TX_POWER_CONTROL] = { .type = NLA_U8 },
+ 	[MT76_TM_ATTR_TX_ANTENNA] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_TX_SPE_IDX] = { .type = NLA_U8 },
+ 	[MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
++	[MT76_TM_ATTR_AID] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_RU_ALLOC] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_RU_IDX] = { .type = NLA_U8 },
+ 	[MT76_TM_ATTR_STATS] = { .type = NLA_NESTED },
++	[MT76_TM_ATTR_TXBF_ACT] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_OFF_CH_SCAN_CH] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_OFF_CH_SCAN_BW] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_IPI_THRESHOLD] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_IPI_PERIOD] = { .type = NLA_U32 },
++	[MT76_TM_ATTR_IPI_ANTENNA_INDEX] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_IPI_RESET] = { .type = NLA_U8 },
+ };
+ 
+ const struct tm_field msg_field = {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0027-mtk-wifi-mt76-testmode-add-testmode-ZWDFS-verificati.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0027-mtk-wifi-mt76-testmode-add-testmode-ZWDFS-verificati.patch
deleted file mode 100644
index 5ccf77e..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0027-mtk-wifi-mt76-testmode-add-testmode-ZWDFS-verificati.patch
+++ /dev/null
@@ -1,490 +0,0 @@
-From 2ef29df1bbd84d7b35b6830025323cead3681e85 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 22 Mar 2023 11:19:52 +0800
-Subject: [PATCH 027/116] mtk: wifi: mt76: testmode: add testmode ZWDFS
- verification support
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt76.h            |   8 ++
- mt7996/mt7996.h   |   1 +
- mt7996/testmode.c | 248 ++++++++++++++++++++++++++++++++++++++++++++--
- mt7996/testmode.h |  44 ++++++++
- testmode.c        |  22 +++-
- tools/fields.c    |  15 +++
- 6 files changed, 326 insertions(+), 12 deletions(-)
-
-diff --git a/mt76.h b/mt76.h
-index dbd8ab9..43e4585 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -783,6 +783,14 @@ struct mt76_testmode_data {
- 	} cfg;
- 
- 	u8 aid;
-+
-+	u8 offchan_ch;
-+	u8 offchan_center_ch;
-+	u8 offchan_bw;
-+
-+	u8 ipi_threshold;
-+	u32 ipi_period;
-+	u8 ipi_reset;
- };
- 
- struct mt76_vif {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 9d6e85c..7d6ec8b 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -289,6 +289,7 @@ struct mt7996_phy {
- 
- 	struct mt76_mib_stats mib;
- 	struct mt76_channel_state state_ts;
-+	struct delayed_work ipi_work;
- 
- 	bool has_aux_rx;
- 
-diff --git a/mt7996/testmode.c b/mt7996/testmode.c
-index a756ee1..836211f 100644
---- a/mt7996/testmode.c
-+++ b/mt7996/testmode.c
-@@ -17,6 +17,12 @@ enum {
- 	TM_CHANGED_TX_LENGTH,
- 	TM_CHANGED_TX_TIME,
- 	TM_CHANGED_CFG,
-+	TM_CHANGED_OFF_CHAN_CH,
-+	TM_CHANGED_OFF_CHAN_CENTER_CH,
-+	TM_CHANGED_OFF_CHAN_BW,
-+	TM_CHANGED_IPI_THRESHOLD,
-+	TM_CHANGED_IPI_PERIOD,
-+	TM_CHANGED_IPI_RESET,
- 
- 	/* must be last */
- 	NUM_TM_CHANGED
-@@ -29,20 +35,31 @@ static const u8 tm_change_map[] = {
- 	[TM_CHANGED_TX_LENGTH] = MT76_TM_ATTR_TX_LENGTH,
- 	[TM_CHANGED_TX_TIME] = MT76_TM_ATTR_TX_TIME,
- 	[TM_CHANGED_CFG] = MT76_TM_ATTR_CFG,
-+	[TM_CHANGED_OFF_CHAN_CH] = MT76_TM_ATTR_OFF_CH_SCAN_CH,
-+	[TM_CHANGED_OFF_CHAN_CENTER_CH] = MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH,
-+	[TM_CHANGED_OFF_CHAN_BW] = MT76_TM_ATTR_OFF_CH_SCAN_BW,
-+	[TM_CHANGED_IPI_THRESHOLD] = MT76_TM_ATTR_IPI_THRESHOLD,
-+	[TM_CHANGED_IPI_PERIOD] = MT76_TM_ATTR_IPI_PERIOD,
-+	[TM_CHANGED_IPI_RESET] = MT76_TM_ATTR_IPI_RESET,
- };
- 
--static u8 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method)
-+static void mt7996_tm_ipi_work(struct work_struct *work);
-+
-+static u32 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method)
- {
--	static const u8 width_to_bw[][NUM_BW_MAP] = {
--		[NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ},
--		[NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ},
--		[NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ},
--		[NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ},
--		[NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ},
--		[NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ},
--		[NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
--		[NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
--		[NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ},
-+	static const u32 width_to_bw[][NUM_BW_MAP] = {
-+		[NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ, 40,
-+					   FIRST_CONTROL_CHAN_BITMAP_BW40},
-+		[NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ, 80,
-+					   FIRST_CONTROL_CHAN_BITMAP_BW80},
-+		[NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ, 80, 0x0},
-+		[NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ, 160,
-+					    FIRST_CONTROL_CHAN_BITMAP_BW160},
-+		[NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ, 5, 0x0},
-+		[NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ, 10, 0x0},
-+		[NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0},
-+		[NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0},
-+		[NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ, 320, 0x0},
- 	};
- 
- 	if (width >= ARRAY_SIZE(width_to_bw))
-@@ -217,6 +234,9 @@ mt7996_tm_init(struct mt7996_phy *phy, bool en)
- 
- 	/* use firmware counter for RX stats */
- 	phy->mt76->test.flag |= MT_TM_FW_RX_COUNT;
-+
-+	if (en)
-+		INIT_DELAYED_WORK(&phy->ipi_work, mt7996_tm_ipi_work);
- }
- 
- static void
-@@ -829,6 +849,204 @@ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb)
- 	}
- }
- 
-+static u8
-+mt7996_tm_get_center_chan(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef)
-+{
-+	struct mt76_phy *mphy = phy->mt76;
-+	const struct ieee80211_channel *chan = mphy->sband_5g.sband.channels;
-+	u32 bitmap, i, offset, width_mhz, size = mphy->sband_5g.sband.n_channels;
-+	u16 first_control = 0, control_chan = chandef->chan->hw_value;
-+
-+	bitmap = mt7996_tm_bw_mapping(chandef->width, BW_MAP_NL_TO_CONTROL_BITMAP_5G);
-+	if (!bitmap)
-+		return control_chan;
-+
-+	width_mhz = mt7996_tm_bw_mapping(chandef->width, BW_MAP_NL_TO_MHZ);
-+	offset = width_mhz / 10 - 2;
-+
-+	for (i = 0; i < size; i++) {
-+		if (!((1 << i) & bitmap))
-+			continue;
-+
-+		if (control_chan >= chan[i].hw_value)
-+			first_control = chan[i].hw_value;
-+		else
-+			break;
-+	}
-+
-+	if (i == size || first_control == 0)
-+		return control_chan;
-+
-+	return first_control + offset;
-+}
-+
-+static int
-+mt7996_tm_set_offchan(struct mt7996_phy *phy, bool no_center)
-+{
-+	struct mt76_phy *mphy = phy->mt76;
-+	struct mt7996_dev *dev = phy->dev;
-+	struct ieee80211_hw *hw = mphy->hw;
-+	struct mt76_testmode_data *td = &phy->mt76->test;
-+	struct cfg80211_chan_def chandef = {};
-+	struct ieee80211_channel *chan;
-+	int ret, freq = ieee80211_channel_to_frequency(td->offchan_ch, NL80211_BAND_5GHZ);
-+
-+	if (!mphy->cap.has_5ghz || !freq) {
-+		ret = -EINVAL;
-+		dev_info(dev->mt76.dev, "Failed to set offchan (invalid band or channel)!\n");
-+		goto out;
-+	}
-+
-+	chandef.width = td->offchan_bw;
-+	chan = ieee80211_get_channel(hw->wiphy, freq);
-+	chandef.chan = chan;
-+	if (no_center)
-+		td->offchan_center_ch = mt7996_tm_get_center_chan(phy, &chandef);
-+	chandef.center_freq1 = ieee80211_channel_to_frequency(td->offchan_center_ch,
-+							      NL80211_BAND_5GHZ);
-+	if (!cfg80211_chandef_valid(&chandef)) {
-+		ret = -EINVAL;
-+		dev_info(dev->mt76.dev, "Failed to set offchan, chandef is invalid!\n");
-+		goto out;
-+	}
-+
-+	memset(&dev->rdd2_chandef, 0, sizeof(struct cfg80211_chan_def));
-+
-+	ret = mt7996_mcu_rdd_background_enable(phy, &chandef);
-+
-+	if (ret)
-+		goto out;
-+
-+	dev->rdd2_phy = phy;
-+	dev->rdd2_chandef = chandef;
-+
-+	return 0;
-+
-+out:
-+	td->offchan_ch = 0;
-+	td->offchan_center_ch = 0;
-+	td->offchan_bw = 0;
-+
-+	return ret;
-+}
-+
-+static void
-+mt7996_tm_ipi_hist_ctrl(struct mt7996_phy *phy, struct mt7996_tm_rdd_ipi_ctrl *data, u8 cmd)
-+{
-+#define MT_IPI_RESET		0x830a5dfc
-+#define MT_IPI_RESET_MASK	BIT(28)
-+#define MT_IPI_COUNTER_BASE	0x83041000
-+#define MT_IPI_COUNTER(idx)	(MT_IPI_COUNTER_BASE + ((idx) * 4))
-+	struct mt7996_dev *dev = phy->dev;
-+	bool val;
-+	int i;
-+
-+	if (cmd == RDD_SET_IPI_HIST_RESET) {
-+		val = mt76_rr(dev, MT_IPI_RESET) & MT_IPI_RESET_MASK;
-+		mt76_rmw_field(dev, MT_IPI_RESET, MT_IPI_RESET_MASK, !val);
-+		return;
-+	}
-+
-+	for (i = 0; i < POWER_INDICATE_HIST_MAX; i++)
-+		data->ipi_hist_val[i] = mt76_rr(dev, MT_IPI_COUNTER(i));
-+}
-+
-+static void
-+mt7996_tm_ipi_work(struct work_struct *work)
-+{
-+#define PRECISION	100
-+	struct mt7996_phy *phy = container_of(work, struct mt7996_phy, ipi_work.work);
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt76_testmode_data *td = &phy->mt76->test;
-+	struct mt7996_tm_rdd_ipi_ctrl data;
-+	u32 ipi_idx, ipi_free_count, ipi_percentage;
-+	u32 ipi_hist_count_th = 0, ipi_hist_total_count = 0;
-+	u32 self_idle_ratio, ipi_idle_ratio, channel_load;
-+	u32 *ipi_hist_data;
-+	const char *power_lower_bound, *power_upper_bound;
-+	static const char * const ipi_idx_to_power_bound[] = {
-+		[RDD_IPI_HIST_0] = "-92",
-+		[RDD_IPI_HIST_1] = "-89",
-+		[RDD_IPI_HIST_2] = "-86",
-+		[RDD_IPI_HIST_3] = "-83",
-+		[RDD_IPI_HIST_4] = "-80",
-+		[RDD_IPI_HIST_5] = "-75",
-+		[RDD_IPI_HIST_6] = "-70",
-+		[RDD_IPI_HIST_7] = "-65",
-+		[RDD_IPI_HIST_8] = "-60",
-+		[RDD_IPI_HIST_9] = "-55",
-+		[RDD_IPI_HIST_10] = "inf",
-+	};
-+
-+	memset(&data, 0, sizeof(data));
-+	mt7996_tm_ipi_hist_ctrl(phy, &data, RDD_IPI_HIST_ALL_CNT);
-+
-+	ipi_hist_data = data.ipi_hist_val;
-+	for (ipi_idx = 0; ipi_idx < POWER_INDICATE_HIST_MAX; ipi_idx++) {
-+		power_lower_bound = ipi_idx ? ipi_idx_to_power_bound[ipi_idx - 1] : "-inf";
-+		power_upper_bound = ipi_idx_to_power_bound[ipi_idx];
-+
-+		dev_info(dev->mt76.dev, "IPI %d (power range: (%s, %s] dBm): ipi count = %d\n",
-+			 ipi_idx, power_lower_bound, power_upper_bound, ipi_hist_data[ipi_idx]);
-+
-+		if (td->ipi_threshold <= ipi_idx && ipi_idx <= RDD_IPI_HIST_10)
-+			ipi_hist_count_th += ipi_hist_data[ipi_idx];
-+
-+		ipi_hist_total_count += ipi_hist_data[ipi_idx];
-+	}
-+
-+	ipi_free_count = ipi_hist_data[RDD_IPI_FREE_RUN_CNT];
-+
-+	dev_info(dev->mt76.dev, "IPI threshold %d: ipi_hist_count_th = %d, ipi_free_count = %d\n",
-+		 td->ipi_threshold, ipi_hist_count_th, ipi_free_count);
-+	dev_info(dev->mt76.dev, "TX assert time =  %d [ms]\n", data.tx_assert_time / 1000);
-+
-+	/* calculate channel load = (self idle ratio - idle ratio) / self idle ratio */
-+	if (ipi_hist_count_th >= UINT_MAX / (100 * PRECISION))
-+		ipi_percentage = 100 * PRECISION *
-+				 (ipi_hist_count_th / (100 * PRECISION)) /
-+				 (ipi_free_count / (100 * PRECISION));
-+	else
-+		ipi_percentage = PRECISION * 100 * ipi_hist_count_th / ipi_free_count;
-+
-+	ipi_idle_ratio = ((100 * PRECISION) - ipi_percentage) / PRECISION;
-+
-+	self_idle_ratio = PRECISION * 100 *
-+			  (td->ipi_period - (data.tx_assert_time / 1000)) /
-+			  td->ipi_period / PRECISION;
-+
-+	if (self_idle_ratio < ipi_idle_ratio)
-+		channel_load = 0;
-+	else
-+		channel_load = self_idle_ratio - ipi_idle_ratio;
-+
-+	if (self_idle_ratio <= td->ipi_threshold) {
-+		dev_info(dev->mt76.dev, "band[%d]: self idle ratio = %d%%, idle ratio = %d%%\n",
-+			 phy->mt76->band_idx, self_idle_ratio, ipi_idle_ratio);
-+		return;
-+	}
-+
-+	channel_load = (100 * channel_load) / self_idle_ratio;
-+	dev_info(dev->mt76.dev,
-+		 "band[%d]: chan load = %d%%, self idle ratio = %d%%, idle ratio = %d%%\n",
-+		 phy->mt76->band_idx, channel_load, self_idle_ratio, ipi_idle_ratio);
-+}
-+
-+static int
-+mt7996_tm_set_ipi(struct mt7996_phy *phy)
-+{
-+	struct mt76_testmode_data *td = &phy->mt76->test;
-+
-+	/* reset IPI CR */
-+	mt7996_tm_ipi_hist_ctrl(phy, NULL, RDD_SET_IPI_HIST_RESET);
-+
-+	cancel_delayed_work(&phy->ipi_work);
-+	ieee80211_queue_delayed_work(phy->mt76->hw, &phy->ipi_work,
-+				     msecs_to_jiffies(td->ipi_period));
-+
-+	return 0;
-+}
-+
- static void
- mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
- {
-@@ -860,6 +1078,14 @@ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
- 
- 		mt7996_tm_set(dev, func_idx, td->cfg.type);
- 	}
-+	if ((changed & BIT(TM_CHANGED_OFF_CHAN_CH)) &&
-+	    (changed & BIT(TM_CHANGED_OFF_CHAN_BW)))
-+		mt7996_tm_set_offchan(phy, !(changed & BIT(TM_CHANGED_OFF_CHAN_CENTER_CH)));
-+	if ((changed & BIT(TM_CHANGED_IPI_THRESHOLD)) &&
-+	    (changed & BIT(TM_CHANGED_IPI_PERIOD)))
-+		mt7996_tm_set_ipi(phy);
-+	if (changed & BIT(TM_CHANGED_IPI_RESET))
-+		mt7996_tm_ipi_hist_ctrl(phy, NULL, RDD_SET_IPI_HIST_RESET);
- }
- 
- static int
-diff --git a/mt7996/testmode.h b/mt7996/testmode.h
-index 9bfb86f..78662b2 100644
---- a/mt7996/testmode.h
-+++ b/mt7996/testmode.h
-@@ -27,9 +27,15 @@ enum {
- 	FW_CDBW_8080MHZ,
- };
- 
-+#define FIRST_CONTROL_CHAN_BITMAP_BW40		0x5555555
-+#define FIRST_CONTROL_CHAN_BITMAP_BW80		0x111111
-+#define FIRST_CONTROL_CHAN_BITMAP_BW160		0x100101
-+
- enum bw_mapping_method {
- 	BW_MAP_NL_TO_FW,
- 	BW_MAP_NL_TO_TM,
-+	BW_MAP_NL_TO_MHZ,
-+	BW_MAP_NL_TO_CONTROL_BITMAP_5G,
- 
- 	NUM_BW_MAP,
- };
-@@ -312,4 +318,42 @@ struct mt7996_tm_rx_event {
- 	};
- } __packed;
- 
-+enum {
-+	RDD_SET_IPI_CR_INIT,		/* CR initialization */
-+	RDD_SET_IPI_HIST_RESET,		/* Reset IPI histogram counter */
-+	RDD_SET_IDLE_POWER,		/* Idle power info */
-+	RDD_SET_IPI_HIST_NUM
-+};
-+
-+enum {
-+	RDD_IPI_HIST_0,			/* IPI count for power <= -92 (dBm) */
-+	RDD_IPI_HIST_1,			/* IPI count for -92 < power <= -89 (dBm) */
-+	RDD_IPI_HIST_2,			/* IPI count for -89 < power <= -86 (dBm) */
-+	RDD_IPI_HIST_3,			/* IPI count for -86 < power <= -83 (dBm) */
-+	RDD_IPI_HIST_4,			/* IPI count for -83 < power <= -80 (dBm) */
-+	RDD_IPI_HIST_5,			/* IPI count for -80 < power <= -75 (dBm) */
-+	RDD_IPI_HIST_6,			/* IPI count for -75 < power <= -70 (dBm) */
-+	RDD_IPI_HIST_7,			/* IPI count for -70 < power <= -65 (dBm) */
-+	RDD_IPI_HIST_8,			/* IPI count for -65 < power <= -60 (dBm) */
-+	RDD_IPI_HIST_9,			/* IPI count for -60 < power <= -55 (dBm) */
-+	RDD_IPI_HIST_10,		/* IPI count for -55 < power        (dBm) */
-+	RDD_IPI_FREE_RUN_CNT,		/* IPI count for counter++ per 8 us */
-+	RDD_IPI_HIST_ALL_CNT,		/* Get all IPI */
-+	RDD_IPI_HIST_0_TO_10_CNT,	/* Get IPI histogram 0 to 10 */
-+	RDD_IPI_HIST_2_TO_10_CNT,	/* Get IPI histogram 2 to 10 */
-+	RDD_TX_ASSERT_TIME,		/* Get band 1 TX assert time */
-+	RDD_IPI_HIST_NUM
-+};
-+
-+#define POWER_INDICATE_HIST_MAX		RDD_IPI_FREE_RUN_CNT
-+#define IPI_HIST_TYPE_NUM		(POWER_INDICATE_HIST_MAX + 1)
-+
-+struct mt7996_tm_rdd_ipi_ctrl {
-+	u8 ipi_hist_idx;
-+	u8 band_idx;
-+	u8 rsv[2];
-+	__le32 ipi_hist_val[IPI_HIST_TYPE_NUM];
-+	__le32 tx_assert_time;		/* unit: us */
-+} __packed;
-+
- #endif
-diff --git a/testmode.c b/testmode.c
-index cd8cb65..69147f8 100644
---- a/testmode.c
-+++ b/testmode.c
-@@ -27,6 +27,13 @@ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
- 	[MT76_TM_ATTR_TX_TIME] = { .type = NLA_U32 },
- 	[MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
- 	[MT76_TM_ATTR_DRV_DATA] = { .type = NLA_NESTED },
-+	[MT76_TM_ATTR_OFF_CH_SCAN_CH] = { .type = NLA_U8 },
-+	[MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH] = { .type = NLA_U8 },
-+	[MT76_TM_ATTR_OFF_CH_SCAN_BW] = { .type = NLA_U8 },
-+	[MT76_TM_ATTR_OFF_CH_SCAN_PATH] = { .type = NLA_U8 },
-+	[MT76_TM_ATTR_IPI_THRESHOLD] = { .type = NLA_U8 },
-+	[MT76_TM_ATTR_IPI_PERIOD] = { .type = NLA_U32 },
-+	[MT76_TM_ATTR_IPI_RESET] = { .type = NLA_U8 },
- };
- EXPORT_SYMBOL_GPL(mt76_tm_policy);
- 
-@@ -499,6 +506,9 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	if (tb[MT76_TM_ATTR_TX_RATE_IDX])
- 		td->tx_rate_idx = nla_get_u8(tb[MT76_TM_ATTR_TX_RATE_IDX]);
- 
-+	if (tb[MT76_TM_ATTR_IPI_PERIOD])
-+		td->ipi_period = nla_get_u32(tb[MT76_TM_ATTR_IPI_PERIOD]);
-+
- 	if (mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_MODE], &td->tx_rate_mode,
- 			   0, MT76_TM_TX_MODE_MAX) ||
- 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_NSS], &td->tx_rate_nss,
-@@ -514,7 +524,14 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 			   &td->tx_duty_cycle, 0, 99) ||
- 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_POWER_CONTROL],
- 			   &td->tx_power_control, 0, 1) ||
--	    mt76_tm_get_u8(tb[MT76_TM_ATTR_AID], &td->aid, 0, 16))
-+	    mt76_tm_get_u8(tb[MT76_TM_ATTR_AID], &td->aid, 0, 16) ||
-+	    mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_CH], &td->offchan_ch, 36, 196) ||
-+	    mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH], &td->offchan_center_ch,
-+			   36, 196) ||
-+	    mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_BW],
-+			   &td->offchan_bw, NL80211_CHAN_WIDTH_20_NOHT, NL80211_CHAN_WIDTH_160) ||
-+	    mt76_tm_get_u8(tb[MT76_TM_ATTR_IPI_THRESHOLD], &td->ipi_threshold, 0, 10) ||
-+	    mt76_tm_get_u8(tb[MT76_TM_ATTR_IPI_RESET], &td->ipi_reset, 0, 1))
- 		goto out;
- 
- 	if (tb[MT76_TM_ATTR_TX_LENGTH]) {
-@@ -720,6 +737,9 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
- 	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_STBC, td->tx_rate_stbc) ||
- 	    nla_put_u8(msg, MT76_TM_ATTR_SKU_EN, td->sku_en) ||
- 	    nla_put_u8(msg, MT76_TM_ATTR_AID, td->aid) ||
-+	    nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_CH, td->offchan_ch) ||
-+	    nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH, td->offchan_center_ch) ||
-+	    nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_BW, td->offchan_bw) ||
- 	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_LTF) &&
- 	     nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) ||
- 	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA) &&
-diff --git a/tools/fields.c b/tools/fields.c
-index b012276..77696ce 100644
---- a/tools/fields.c
-+++ b/tools/fields.c
-@@ -35,6 +35,15 @@ static const char * const testmode_tx_mode[] = {
- 	[MT76_TM_TX_MODE_EHT_MU] = "eht_mu",
- };
- 
-+static const char * const testmode_offchan_bw[] = {
-+	[NL80211_CHAN_WIDTH_20_NOHT] = "NOHT",
-+	[NL80211_CHAN_WIDTH_20] = "20",
-+	[NL80211_CHAN_WIDTH_40] = "40",
-+	[NL80211_CHAN_WIDTH_80] = "80",
-+	[NL80211_CHAN_WIDTH_80P80] = "80p80",
-+	[NL80211_CHAN_WIDTH_160] = "160",
-+};
-+
- static void print_enum(const struct tm_field *field, struct nlattr *attr)
- {
- 	unsigned int i = nla_get_u8(attr);
-@@ -390,6 +399,12 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
- 	FIELD(u8, AID, "aid"),
- 	FIELD(u8, RU_ALLOC, "ru_alloc"),
- 	FIELD(u8, RU_IDX, "ru_idx"),
-+	FIELD(u8, OFF_CH_SCAN_CH, "offchan_ch"),
-+	FIELD(u8, OFF_CH_SCAN_CENTER_CH, "offchan_center_ch"),
-+	FIELD_ENUM(OFF_CH_SCAN_BW, "offchan_bw", testmode_offchan_bw),
-+	FIELD(u8, IPI_THRESHOLD, "ipi_threshold"),
-+	FIELD(u32, IPI_PERIOD, "ipi_period"),
-+	FIELD(u8, IPI_RESET, "ipi_reset"),
- 	FIELD_MAC(MAC_ADDRS, "mac_addrs"),
- 	FIELD_NESTED_RO(STATS, stats, "",
- 			.print_extra = print_extra_stats),
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0028-mtk-mt76-mt7996-add-testmode-pre-calibration-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0028-mtk-mt76-mt7996-add-testmode-pre-calibration-support.patch
new file mode 100644
index 0000000..6ec0d14
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0028-mtk-mt76-mt7996-add-testmode-pre-calibration-support.patch
@@ -0,0 +1,899 @@
+From e003b705dbccb31a9150c04de3bfcb5e2d2e8a0b Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 31 Mar 2023 11:27:24 +0800
+Subject: [PATCH 028/199] mtk: mt76: mt7996: add testmode pre-calibration
+ support
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mac80211.c        |  21 ---
+ mt76.h            |  22 +++
+ mt76_connac_mcu.h |   2 +
+ mt7996/eeprom.c   |  66 +++++++
+ mt7996/eeprom.h   |  47 +++++
+ mt7996/mcu.c      |   5 +
+ mt7996/mt7996.h   |   7 +
+ mt7996/testmode.c | 437 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/testmode.h |  20 ++-
+ testmode.c        |  12 ++
+ testmode.h        |   8 +
+ tools/fields.c    |   8 +
+ 12 files changed, 632 insertions(+), 23 deletions(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index 7e4ee83f..5b02fa35 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -6,27 +6,6 @@
+ #include <linux/of.h>
+ #include "mt76.h"
+ 
+-#define CHAN2G(_idx, _freq) {			\
+-	.band = NL80211_BAND_2GHZ,		\
+-	.center_freq = (_freq),			\
+-	.hw_value = (_idx),			\
+-	.max_power = 30,			\
+-}
+-
+-#define CHAN5G(_idx, _freq) {			\
+-	.band = NL80211_BAND_5GHZ,		\
+-	.center_freq = (_freq),			\
+-	.hw_value = (_idx),			\
+-	.max_power = 30,			\
+-}
+-
+-#define CHAN6G(_idx, _freq) {			\
+-	.band = NL80211_BAND_6GHZ,		\
+-	.center_freq = (_freq),			\
+-	.hw_value = (_idx),			\
+-	.max_power = 30,			\
+-}
+-
+ static const struct ieee80211_channel mt76_channels_2ghz[] = {
+ 	CHAN2G(1, 2412),
+ 	CHAN2G(2, 2417),
+diff --git a/mt76.h b/mt76.h
+index 27635c49..362243f6 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -23,6 +23,27 @@
+ #include "util.h"
+ #include "testmode.h"
+ 
++#define CHAN2G(_idx, _freq) {			\
++	.band = NL80211_BAND_2GHZ,		\
++	.center_freq = (_freq),			\
++	.hw_value = (_idx),			\
++	.max_power = 30,			\
++}
++
++#define CHAN5G(_idx, _freq) {			\
++	.band = NL80211_BAND_5GHZ,		\
++	.center_freq = (_freq),			\
++	.hw_value = (_idx),			\
++	.max_power = 30,			\
++}
++
++#define CHAN6G(_idx, _freq) {			\
++	.band = NL80211_BAND_6GHZ,		\
++	.center_freq = (_freq),			\
++	.hw_value = (_idx),			\
++	.max_power = 30,			\
++}
++
+ #define MT_MCU_RING_SIZE	32
+ #define MT_RX_BUF_SIZE		2048
+ #define MT_SKB_HEAD_LEN		256
+@@ -707,6 +728,7 @@ struct mt76_testmode_ops {
+ 	void (*reset_rx_stats)(struct mt76_phy *phy);
+ 	void (*tx_stop)(struct mt76_phy *phy);
+ 	int (*set_eeprom)(struct mt76_phy *phy, u32 offset, u8 *val, u8 action);
++	int (*dump_precal)(struct mt76_phy *mphy, struct sk_buff *msg, int flag, int type);
+ };
+ 
+ #define MT_TM_FW_RX_COUNT	BIT(0)
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 57e0e9de..f994a2b3 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1053,8 +1053,10 @@ enum {
+ 	MCU_UNI_EVENT_RDD_REPORT = 0x11,
+ 	MCU_UNI_EVENT_ROC = 0x27,
+ 	MCU_UNI_EVENT_TX_DONE = 0x2d,
++	MCU_UNI_EVENT_BF = 0x33,
+ 	MCU_UNI_EVENT_THERMAL = 0x35,
+ 	MCU_UNI_EVENT_NIC_CAPAB = 0x43,
++	MCU_UNI_EVENT_TESTMODE_CTRL = 0x46,
+ 	MCU_UNI_EVENT_WED_RRO = 0x57,
+ 	MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
+ 	MCU_UNI_EVENT_ALL_STA_INFO = 0x6e,
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index e2790109..80b9f6f9 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -12,6 +12,42 @@ static bool testmode_enable;
+ module_param(testmode_enable, bool, 0644);
+ MODULE_PARM_DESC(testmode_enable, "Enable testmode");
+ 
++const struct ieee80211_channel dpd_2g_ch_list_bw20[] = {
++	CHAN2G(3, 2422),
++	CHAN2G(7, 2442),
++	CHAN2G(11, 2462)
++};
++
++const struct ieee80211_channel dpd_5g_ch_list_bw160[] = {
++	CHAN5G(50, 5250),
++	CHAN5G(114, 5570),
++	CHAN5G(163, 5815)
++};
++
++const struct ieee80211_channel dpd_6g_ch_list_bw160[] = {
++	CHAN6G(15, 6025),
++	CHAN6G(47, 6185),
++	CHAN6G(79, 6345),
++	CHAN6G(111, 6505),
++	CHAN6G(143, 6665),
++	CHAN6G(175, 6825),
++	CHAN6G(207, 6985)
++};
++
++const struct ieee80211_channel dpd_6g_ch_list_bw320[] = {
++	CHAN6G(31, 6105),
++	CHAN6G(63, 6265),
++	CHAN6G(95, 6425),
++	CHAN6G(127, 6585),
++	CHAN6G(159, 6745),
++	CHAN6G(191, 6905)
++};
++
++const u32 dpd_2g_bw20_ch_num = ARRAY_SIZE(dpd_2g_ch_list_bw20);
++const u32 dpd_5g_bw160_ch_num = ARRAY_SIZE(dpd_5g_ch_list_bw160);
++const u32 dpd_6g_bw160_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw160);
++const u32 dpd_6g_bw320_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw320);
++
+ static int mt7996_check_eeprom(struct mt7996_dev *dev)
+ {
+ #define FEM_INT				0
+@@ -74,6 +110,36 @@ static char *mt7996_eeprom_name(struct mt7996_dev *dev)
+ 	}
+ }
+ 
++int
++mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band)
++{
++	/* handle different sku */
++	static const u8 band_to_idx[] = {
++		[NL80211_BAND_2GHZ] = MT_BAND0,
++		[NL80211_BAND_5GHZ] = MT_BAND1,
++		[NL80211_BAND_6GHZ] = MT_BAND2,
++	};
++	struct mt7996_phy *phy = __mt7996_phy(dev, band_to_idx[band]);
++	struct mt76_phy *mphy;
++	int dpd_size;
++
++	if (!phy)
++		return 0;
++
++	mphy = phy->mt76;
++
++	if (band == NL80211_BAND_2GHZ)
++		dpd_size = dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
++	else if (band == NL80211_BAND_5GHZ)
++		dpd_size = mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
++			   dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
++	else
++		dpd_size = mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
++			   (dpd_6g_bw160_ch_num + dpd_6g_bw320_ch_num) * DPD_PER_CH_GT_BW20_SIZE;
++
++	return dpd_size;
++}
++
+ static int
+ mt7996_eeprom_load_default(struct mt7996_dev *dev)
+ {
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index de3ff4e2..849b8bca 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -14,6 +14,7 @@ enum mt7996_eeprom_field {
+ 	MT_EE_MAC_ADDR =	0x004,
+ 	MT_EE_MAC_ADDR2 =	0x00a,
+ 	MT_EE_WIFI_CONF =	0x190,
++	MT_EE_DO_PRE_CAL =	0x1a5,
+ 	MT_EE_TESTMODE_EN =	0x1af,
+ 	MT_EE_MAC_ADDR3 =	0x2c0,
+ 	MT_EE_RATE_DELTA_2G =	0x1400,
+@@ -32,6 +33,52 @@ enum mt7996_eeprom_field {
+ #define MT_EE_WIFI_CONF2_BAND_SEL		GENMASK(2, 0)
+ #define MT_EE_WIFI_PA_LNA_CONFIG		GENMASK(1, 0)
+ 
++#define MT_EE_WIFI_CAL_GROUP_2G			BIT(0)
++#define MT_EE_WIFI_CAL_GROUP_5G			BIT(1)
++#define MT_EE_WIFI_CAL_GROUP_6G			BIT(2)
++#define MT_EE_WIFI_CAL_GROUP			GENMASK(2, 0)
++#define MT_EE_WIFI_CAL_DPD_2G			BIT(3)
++#define MT_EE_WIFI_CAL_DPD_5G			BIT(4)
++#define MT_EE_WIFI_CAL_DPD_6G			BIT(5)
++#define MT_EE_WIFI_CAL_DPD			GENMASK(5, 3)
++
++#define MT_EE_CAL_UNIT				1024
++#define MT_EE_CAL_GROUP_SIZE_2G			(4 * MT_EE_CAL_UNIT)
++#define MT_EE_CAL_GROUP_SIZE_5G			(45 * MT_EE_CAL_UNIT)
++#define MT_EE_CAL_GROUP_SIZE_6G			(125 * MT_EE_CAL_UNIT)
++#define MT_EE_CAL_ADCDCOC_SIZE_2G		(4 * 4)
++#define MT_EE_CAL_ADCDCOC_SIZE_5G		(4 * 4)
++#define MT_EE_CAL_ADCDCOC_SIZE_6G		(4 * 5)
++#define MT_EE_CAL_GROUP_SIZE			(MT_EE_CAL_GROUP_SIZE_2G + \
++						 MT_EE_CAL_GROUP_SIZE_5G + \
++						 MT_EE_CAL_GROUP_SIZE_6G + \
++						 MT_EE_CAL_ADCDCOC_SIZE_2G + \
++						 MT_EE_CAL_ADCDCOC_SIZE_5G + \
++						 MT_EE_CAL_ADCDCOC_SIZE_6G)
++
++#define DPD_PER_CH_LEGACY_SIZE			(4 * MT_EE_CAL_UNIT)
++#define DPD_PER_CH_MEM_SIZE			(13 * MT_EE_CAL_UNIT)
++#define DPD_PER_CH_OTFG0_SIZE			(2 * MT_EE_CAL_UNIT)
++#define DPD_PER_CH_BW20_SIZE			(DPD_PER_CH_LEGACY_SIZE + DPD_PER_CH_OTFG0_SIZE)
++#define DPD_PER_CH_GT_BW20_SIZE			(DPD_PER_CH_MEM_SIZE + DPD_PER_CH_OTFG0_SIZE)
++#define MT_EE_CAL_DPD_SIZE			(780 * MT_EE_CAL_UNIT)
++
++extern const struct ieee80211_channel dpd_2g_ch_list_bw20[];
++extern const u32 dpd_2g_bw20_ch_num;
++extern const struct ieee80211_channel dpd_5g_ch_list_bw160[];
++extern const u32 dpd_5g_bw160_ch_num;
++extern const struct ieee80211_channel dpd_6g_ch_list_bw160[];
++extern const u32 dpd_6g_bw160_ch_num;
++extern const struct ieee80211_channel dpd_6g_ch_list_bw320[];
++extern const u32 dpd_6g_bw320_ch_num;
++
++#define RF_DPD_FLAT_CAL				BIT(28)
++#define RF_PRE_CAL				BIT(29)
++#define RF_DPD_FLAT_5G_CAL			GENMASK(29, 28)
++#define RF_DPD_FLAT_5G_MEM_CAL			(BIT(30) | BIT(28))
++#define RF_DPD_FLAT_6G_CAL			GENMASK(30, 28)
++#define RF_DPD_FLAT_6G_MEM_CAL			(BIT(31) | BIT(28))
++
+ #define MT_EE_WIFI_CONF1_TX_PATH_BAND0		GENMASK(5, 3)
+ #define MT_EE_WIFI_CONF2_TX_PATH_BAND1		GENMASK(2, 0)
+ #define MT_EE_WIFI_CONF2_TX_PATH_BAND2		GENMASK(5, 3)
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 61b46370..75f143e2 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -715,6 +715,11 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	case MCU_UNI_EVENT_WED_RRO:
+ 		mt7996_mcu_wed_rro_event(dev, skb);
+ 		break;
++#ifdef CONFIG_NL80211_TESTMODE
++	case MCU_UNI_EVENT_TESTMODE_CTRL:
++		mt7996_tm_rf_test_event(dev, skb);
++		break;
++#endif
+ 	default:
+ 		break;
+ 	}
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 1e1e638a..c01a2145 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -389,6 +389,9 @@ struct mt7996_dev {
+ 	struct dentry *debugfs_dir;
+ 	struct rchan *relay_fwlog;
+ 
++	void *cal;
++	u32 cur_prek_offset;
++
+ 	struct {
+ 		u16 table_mask;
+ 		u8 n_agrt;
+@@ -509,6 +512,7 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
+ int mt7996_eeprom_get_target_power(struct mt7996_dev *dev,
+ 				   struct ieee80211_channel *chan);
+ s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band);
++int mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band);
+ int mt7996_dma_init(struct mt7996_dev *dev);
+ void mt7996_dma_reset(struct mt7996_dev *dev, bool force);
+ void mt7996_dma_prefetch(struct mt7996_dev *dev);
+@@ -593,6 +597,9 @@ void mt7996_mcu_exit(struct mt7996_dev *dev);
+ int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
+ int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
+ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
++#ifdef CONFIG_NL80211_TESTMODE
++void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
++#endif
+ 
+ static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
+ {
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index 98eebcee..a756ee10 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -7,6 +7,8 @@
+ #include "mac.h"
+ #include "mcu.h"
+ #include "testmode.h"
++#include "eeprom.h"
++#include "mtk_mcu.h"
+ 
+ enum {
+ 	TM_CHANGED_TXPOWER,
+@@ -397,6 +399,436 @@ mt7996_tm_set_tx_cont(struct mt7996_phy *phy, bool en)
+ 	}
+ }
+ 
++static int
++mt7996_tm_group_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
++{
++	u8 *eeprom;
++	u32 i, group_size, dpd_size, size, offs, *pre_cal;
++	int ret = 0;
++	struct mt7996_dev *dev = phy->dev;
++	struct mt76_dev *mdev = &dev->mt76;
++	struct mt7996_tm_req req = {
++		.rf_test = {
++			.tag = cpu_to_le16(UNI_RF_TEST_CTRL),
++			.len = cpu_to_le16(sizeof(req.rf_test)),
++			.action = RF_ACTION_IN_RF_TEST,
++			.icap_len = RF_TEST_ICAP_LEN,
++			.op.rf.func_idx = cpu_to_le32(RF_TEST_RE_CAL),
++			.op.rf.param.cal_param.func_data = cpu_to_le32(RF_PRE_CAL),
++			.op.rf.param.cal_param.band_idx = phy->mt76->band_idx,
++		},
++	};
++
++	if (!dev->flash_mode) {
++		dev_err(dev->mt76.dev, "Currently not in FLASH or BIN FILE mode, return!\n");
++		return -EOPNOTSUPP;
++	}
++
++	eeprom = mdev->eeprom.data;
++	dev->cur_prek_offset = 0;
++	group_size = MT_EE_CAL_GROUP_SIZE;
++	dpd_size = MT_EE_CAL_DPD_SIZE;
++	size = group_size + dpd_size;
++	offs = MT_EE_DO_PRE_CAL;
++
++	switch (state) {
++	case MT76_TM_STATE_GROUP_PREK:
++		if (!dev->cal) {
++			dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
++			if (!dev->cal)
++				return -ENOMEM;
++		}
++
++		ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), &req,
++					sizeof(req), false);
++		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == group_size,
++				   30 * HZ);
++
++		if (ret) {
++			dev_err(dev->mt76.dev, "Group Pre-cal: mcu send msg failed!\n");
++			return ret;
++		}
++
++		if (!ret)
++			eeprom[offs] |= MT_EE_WIFI_CAL_GROUP;
++		break;
++	case MT76_TM_STATE_GROUP_PREK_DUMP:
++		pre_cal = (u32 *)dev->cal;
++		if (!pre_cal) {
++			dev_info(dev->mt76.dev, "Not group pre-cal yet!\n");
++			return ret;
++		}
++		dev_info(dev->mt76.dev, "Group Pre-Cal:\n");
++		for (i = 0; i < (group_size / sizeof(u32)); i += 4) {
++			dev_info(dev->mt76.dev, "[0x%08lx] 0x%8x 0x%8x 0x%8x 0x%8x\n",
++				 i * sizeof(u32), pre_cal[i], pre_cal[i + 1],
++				 pre_cal[i + 2], pre_cal[i + 3]);
++		}
++		break;
++	case MT76_TM_STATE_GROUP_PREK_CLEAN:
++		pre_cal = (u32 *)dev->cal;
++		if (!pre_cal)
++			return ret;
++		memset(pre_cal, 0, group_size);
++		eeprom[offs] &= ~MT_EE_WIFI_CAL_GROUP;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	return ret;
++}
++
++static int
++mt7996_tm_dpd_prek_send_req(struct mt7996_phy *phy, struct mt7996_tm_req *req,
++			    const struct ieee80211_channel *chan_list, u32 channel_size,
++			    enum nl80211_chan_width width, u32 func_data)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct mt76_phy *mphy = phy->mt76;
++	struct cfg80211_chan_def chandef_backup, *chandef = &mphy->chandef;
++	struct ieee80211_channel chan_backup;
++	int i, ret;
++
++	if (!chan_list)
++		return -EOPNOTSUPP;
++
++	req->rf_test.op.rf.param.cal_param.func_data = cpu_to_le32(func_data);
++
++	memcpy(&chan_backup, chandef->chan, sizeof(struct ieee80211_channel));
++	memcpy(&chandef_backup, chandef, sizeof(struct cfg80211_chan_def));
++
++	for (i = 0; i < channel_size; i++) {
++		memcpy(chandef->chan, &chan_list[i], sizeof(struct ieee80211_channel));
++		chandef->width = width;
++
++		/* set channel switch reason */
++		mphy->hw->conf.flags |= IEEE80211_CONF_OFFCHANNEL;
++		mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
++
++		ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), req,
++					sizeof(*req), false);
++		if (ret) {
++			dev_err(dev->mt76.dev, "DPD Pre-cal: mcu send msg failed!\n");
++			goto out;
++		}
++	}
++
++out:
++	mphy->hw->conf.flags &= ~IEEE80211_CONF_OFFCHANNEL;
++	memcpy(chandef, &chandef_backup, sizeof(struct cfg80211_chan_def));
++	memcpy(chandef->chan, &chan_backup, sizeof(struct ieee80211_channel));
++	mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
++
++	return ret;
++}
++
++static int
++mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct mt76_dev *mdev = &dev->mt76;
++	struct mt76_phy *mphy = phy->mt76;
++	struct mt7996_tm_req req = {
++		.rf_test = {
++			.tag = cpu_to_le16(UNI_RF_TEST_CTRL),
++			.len = cpu_to_le16(sizeof(req.rf_test)),
++			.action = RF_ACTION_IN_RF_TEST,
++			.icap_len = RF_TEST_ICAP_LEN,
++			.op.rf.func_idx = cpu_to_le32(RF_TEST_RE_CAL),
++			.op.rf.param.cal_param.band_idx = phy->mt76->band_idx,
++		},
++	};
++	u32 i, j, group_size, dpd_size, size, offs, *pre_cal;
++	u32 wait_on_prek_offset = 0;
++	u8 do_precal, *eeprom;
++	int ret = 0;
++
++	if (!dev->flash_mode) {
++		dev_err(dev->mt76.dev, "Currently not in FLASH or BIN FILE mode, return!\n");
++		return -EOPNOTSUPP;
++	}
++
++	eeprom = mdev->eeprom.data;
++	dev->cur_prek_offset = 0;
++	group_size = MT_EE_CAL_GROUP_SIZE;
++	dpd_size = MT_EE_CAL_DPD_SIZE;
++	size = group_size + dpd_size;
++	offs = MT_EE_DO_PRE_CAL;
++
++	if (!dev->cal && state < MT76_TM_STATE_DPD_DUMP) {
++		dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
++		if (!dev->cal)
++			return -ENOMEM;
++	}
++
++	switch (state) {
++	case MT76_TM_STATE_DPD_2G:
++		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_2g_ch_list_bw20,
++						  dpd_2g_bw20_ch_num,
++						  NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_CAL);
++		wait_on_prek_offset += dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait,
++				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++
++		do_precal = MT_EE_WIFI_CAL_DPD_2G;
++		break;
++	case MT76_TM_STATE_DPD_5G:
++		/* 5g channel bw20 calibration */
++		ret = mt7996_tm_dpd_prek_send_req(phy, &req, mphy->sband_5g.sband.channels,
++						  mphy->sband_5g.sband.n_channels,
++						  NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_5G_CAL);
++		if (ret)
++			return ret;
++		wait_on_prek_offset += mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait,
++				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++
++		/* 5g channel bw160 calibration */
++		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_5g_ch_list_bw160,
++						  dpd_5g_bw160_ch_num,
++						  NL80211_CHAN_WIDTH_160, RF_DPD_FLAT_5G_MEM_CAL);
++		wait_on_prek_offset += dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait,
++				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++
++		do_precal = MT_EE_WIFI_CAL_DPD_5G;
++		break;
++	case MT76_TM_STATE_DPD_6G:
++		/* 6g channel bw20 calibration */
++		ret = mt7996_tm_dpd_prek_send_req(phy, &req, mphy->sband_6g.sband.channels,
++						  mphy->sband_6g.sband.n_channels,
++						  NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_6G_CAL);
++		if (ret)
++			return ret;
++		wait_on_prek_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait,
++				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++
++		/* 6g channel bw160 calibration */
++		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw160,
++						  dpd_6g_bw160_ch_num,
++						  NL80211_CHAN_WIDTH_160, RF_DPD_FLAT_6G_MEM_CAL);
++		if (ret)
++			return ret;
++		wait_on_prek_offset += dpd_6g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait,
++				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++
++		/* 6g channel bw320 calibration */
++		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw320,
++						  dpd_6g_bw320_ch_num,
++						  NL80211_CHAN_WIDTH_320, RF_DPD_FLAT_6G_MEM_CAL);
++		wait_on_prek_offset += dpd_6g_bw320_ch_num * DPD_PER_CH_GT_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait,
++				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++
++		do_precal = MT_EE_WIFI_CAL_DPD_6G;
++		break;
++	case MT76_TM_STATE_DPD_DUMP:
++		if (!dev->cal) {
++			dev_info(dev->mt76.dev, "Not DPD pre-cal yet!\n");
++			return ret;
++		}
++		pre_cal = (u32 *)dev->cal;
++		dev_info(dev->mt76.dev, "DPD Pre-Cal:\n");
++		for (i = 0; i < dpd_size / sizeof(u32); i += 4) {
++			j = i + (group_size / sizeof(u32));
++			dev_info(dev->mt76.dev, "[0x%08lx] 0x%8x 0x%8x 0x%8x 0x%8x\n",
++				 j * sizeof(u32), pre_cal[j], pre_cal[j + 1],
++				 pre_cal[j + 2], pre_cal[j + 3]);
++		}
++		return 0;
++	case MT76_TM_STATE_DPD_CLEAN:
++		pre_cal = (u32 *)dev->cal;
++		if (!pre_cal)
++			return ret;
++		memset(pre_cal + (group_size / sizeof(u32)), 0, dpd_size);
++		do_precal = MT_EE_WIFI_CAL_DPD;
++		eeprom[offs] &= ~do_precal;
++		return 0;
++	default:
++		return -EINVAL;
++	}
++
++	if (!ret)
++		eeprom[offs] |= do_precal;
++
++	return ret;
++}
++
++static int
++mt7996_tm_dump_precal(struct mt76_phy *mphy, struct sk_buff *msg, int flag, int type)
++{
++#define DPD_PER_CHAN_SIZE_MASK		GENMASK(31, 30)
++#define DPD_2G_RATIO_MASK		GENMASK(29, 20)
++#define DPD_5G_RATIO_MASK		GENMASK(19, 10)
++#define DPD_6G_RATIO_MASK		GENMASK(9, 0)
++	struct mt7996_phy *phy = mphy->priv;
++	struct mt7996_dev *dev = phy->dev;
++	u32 i, group_size, dpd_size, total_size, size, dpd_info = 0;
++	u32 dpd_size_2g, dpd_size_5g, dpd_size_6g;
++	u32 base, offs, transmit_size = 1000;
++	u8 *pre_cal, *eeprom;
++	void *precal;
++	enum prek_ops {
++		PREK_GET_INFO,
++		PREK_SYNC_ALL,
++		PREK_SYNC_GROUP,
++		PREK_SYNC_DPD_2G,
++		PREK_SYNC_DPD_5G,
++		PREK_SYNC_DPD_6G,
++		PREK_CLEAN_GROUP,
++		PREK_CLEAN_DPD,
++	};
++
++	if (!dev->cal) {
++		dev_info(dev->mt76.dev, "Not pre-cal yet!\n");
++		return 0;
++	}
++
++	group_size = MT_EE_CAL_GROUP_SIZE;
++	dpd_size = MT_EE_CAL_DPD_SIZE;
++	total_size = group_size + dpd_size;
++	pre_cal = dev->cal;
++	eeprom = dev->mt76.eeprom.data;
++	offs = MT_EE_DO_PRE_CAL;
++
++	dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
++	dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
++	dpd_size_6g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_6GHZ);
++
++	switch (type) {
++	case PREK_SYNC_ALL:
++		base = 0;
++		size = total_size;
++		break;
++	case PREK_SYNC_GROUP:
++		base = 0;
++		size = group_size;
++		break;
++	case PREK_SYNC_DPD_2G:
++		base = group_size;
++		size = dpd_size_2g;
++		break;
++	case PREK_SYNC_DPD_5G:
++		base = group_size + dpd_size_2g;
++		size = dpd_size_5g;
++		break;
++	case PREK_SYNC_DPD_6G:
++		base = group_size + dpd_size_2g + dpd_size_5g;
++		size = dpd_size_6g;
++		break;
++	case PREK_GET_INFO:
++		break;
++	default:
++		return 0;
++	}
++
++	if (!flag) {
++		if (eeprom[offs] & MT_EE_WIFI_CAL_DPD) {
++			dpd_info |= u32_encode_bits(1, DPD_PER_CHAN_SIZE_MASK) |
++				    u32_encode_bits(dpd_size_2g / MT_EE_CAL_UNIT,
++						    DPD_2G_RATIO_MASK) |
++				    u32_encode_bits(dpd_size_5g / MT_EE_CAL_UNIT,
++						    DPD_5G_RATIO_MASK) |
++				    u32_encode_bits(dpd_size_6g / MT_EE_CAL_UNIT,
++						    DPD_6G_RATIO_MASK);
++		}
++		dev->cur_prek_offset = 0;
++		precal = nla_nest_start(msg, MT76_TM_ATTR_PRECAL_INFO);
++		if (!precal)
++			return -ENOMEM;
++		nla_put_u32(msg, 0, group_size);
++		nla_put_u32(msg, 1, dpd_size);
++		nla_put_u32(msg, 2, dpd_info);
++		nla_put_u32(msg, 3, transmit_size);
++		nla_put_u32(msg, 4, eeprom[offs]);
++		nla_nest_end(msg, precal);
++	} else {
++		precal = nla_nest_start(msg, MT76_TM_ATTR_PRECAL);
++		if (!precal)
++			return -ENOMEM;
++
++		transmit_size = (dev->cur_prek_offset + transmit_size < size) ?
++				transmit_size : (size - dev->cur_prek_offset);
++		for (i = 0; i < transmit_size; i++) {
++			if (nla_put_u8(msg, i, pre_cal[base + dev->cur_prek_offset + i]))
++				return -ENOMEM;
++		}
++		dev->cur_prek_offset += transmit_size;
++
++		nla_nest_end(msg, precal);
++	}
++
++	return 0;
++}
++
++static void
++mt7996_tm_re_cal_event(struct mt7996_dev *dev, struct mt7996_tm_rf_test_result *result,
++		       struct mt7996_tm_rf_test_data *data)
++{
++	u32 base, dpd_size_2g, dpd_size_5g, dpd_size_6g, cal_idx, cal_type, len = 0;
++	u8 *pre_cal;
++
++	pre_cal = dev->cal;
++	dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
++	dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
++	dpd_size_6g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_6GHZ);
++
++	cal_idx = le32_to_cpu(data->cal_idx);
++	cal_type = le32_to_cpu(data->cal_type);
++	len = le32_to_cpu(result->payload_length);
++	len = len - sizeof(struct mt7996_tm_rf_test_data);
++
++	switch (cal_type) {
++	case RF_PRE_CAL:
++		base = 0;
++		break;
++	case RF_DPD_FLAT_CAL:
++		base = MT_EE_CAL_GROUP_SIZE;
++		break;
++	case RF_DPD_FLAT_5G_CAL:
++	case RF_DPD_FLAT_5G_MEM_CAL:
++		base = MT_EE_CAL_GROUP_SIZE + dpd_size_2g;
++		break;
++	case RF_DPD_FLAT_6G_CAL:
++	case RF_DPD_FLAT_6G_MEM_CAL:
++		base = MT_EE_CAL_GROUP_SIZE + dpd_size_2g + dpd_size_5g;
++		break;
++	default:
++		dev_info(dev->mt76.dev, "Unknown calibration type!\n");
++		return;
++	}
++	pre_cal += (base + dev->cur_prek_offset);
++
++	memcpy(pre_cal, data->cal_data, len);
++	dev->cur_prek_offset += len;
++}
++
++void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++	struct mt7996_tm_event *event;
++	struct mt7996_tm_rf_test_result *result;
++	struct mt7996_tm_rf_test_data *data;
++	static u32 event_type;
++
++	skb_pull(skb, sizeof(struct mt7996_mcu_rxd));
++	event = (struct mt7996_tm_event *)skb->data;
++	result = (struct mt7996_tm_rf_test_result *)&event->result;
++	data = (struct mt7996_tm_rf_test_data *)result->data;
++
++	event_type = le32_to_cpu(result->func_idx);
++
++	switch (event_type) {
++	case RF_TEST_RE_CAL:
++		mt7996_tm_re_cal_event(dev, result, data);
++		break;
++	default:
++		break;
++	}
++}
++
+ static void
+ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
+ {
+@@ -454,6 +886,10 @@ mt7996_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
+ 	else if (prev_state == MT76_TM_STATE_OFF ||
+ 		 state == MT76_TM_STATE_OFF)
+ 		mt7996_tm_init(phy, !(state == MT76_TM_STATE_OFF));
++	else if (state >= MT76_TM_STATE_GROUP_PREK && state <= MT76_TM_STATE_GROUP_PREK_CLEAN)
++		return mt7996_tm_group_prek(phy, state);
++	else if (state >= MT76_TM_STATE_DPD_2G && state <= MT76_TM_STATE_DPD_CLEAN)
++		return mt7996_tm_dpd_prek(phy, state);
+ 
+ 	if ((state == MT76_TM_STATE_IDLE &&
+ 	     prev_state == MT76_TM_STATE_OFF) ||
+@@ -737,4 +1173,5 @@ const struct mt76_testmode_ops mt7996_testmode_ops = {
+ 	.reset_rx_stats = mt7996_tm_reset_trx_stats,
+ 	.tx_stop = mt7996_tm_tx_stop,
+ 	.set_eeprom = mt7996_tm_set_eeprom,
++	.dump_precal = mt7996_tm_dump_precal,
+ };
+diff --git a/mt7996/testmode.h b/mt7996/testmode.h
+index 319ef257..9bfb86f2 100644
+--- a/mt7996/testmode.h
++++ b/mt7996/testmode.h
+@@ -34,6 +34,12 @@ enum bw_mapping_method {
+ 	NUM_BW_MAP,
+ };
+ 
++struct tm_cal_param {
++	__le32 func_data;
++	u8 band_idx;
++	u8 rsv[3];
++};
++
+ struct mt7996_tm_rf_test {
+ 	__le16 tag;
+ 	__le16 len;
+@@ -50,7 +56,7 @@ struct mt7996_tm_rf_test {
+ 			union {
+ 				__le32 func_data;
+ 				__le32 cal_dump;
+-
++				struct tm_cal_param cal_param;
+ 				u8 _pad[80];
+ 			} param;
+ 		} rf;
+@@ -63,10 +69,16 @@ struct mt7996_tm_req {
+ 	struct mt7996_tm_rf_test rf_test;
+ } __packed;
+ 
++struct mt7996_tm_rf_test_data {
++	__le32 cal_idx;
++	__le32 cal_type;
++	u8 cal_data[0];
++} __packed;
++
+ struct mt7996_tm_rf_test_result {
+ 	__le32 func_idx;
+ 	__le32 payload_length;
+-	u8 event[0];
++	u8 data[0];
+ };
+ 
+ struct mt7996_tm_event {
+@@ -77,6 +89,8 @@ struct mt7996_tm_event {
+ 	struct mt7996_tm_rf_test_result result;
+ } __packed;
+ 
++#define RF_TEST_RE_CAL		0x01
++
+ enum {
+ 	RF_ACTION_SWITCH_TO_RF_TEST,
+ 	RF_ACTION_IN_RF_TEST,
+@@ -84,6 +98,8 @@ enum {
+ 	RF_ACTION_GET,
+ };
+ 
++#define RF_TEST_ICAP_LEN	120
++
+ enum {
+ 	RF_OPER_NORMAL,
+ 	RF_OPER_RF_TEST,
+diff --git a/testmode.c b/testmode.c
+index 44f3a5bf..cd8cb655 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -674,6 +674,18 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ 
+ 	mutex_lock(&dev->mutex);
+ 
++	if (tb[MT76_TM_ATTR_PRECAL] || tb[MT76_TM_ATTR_PRECAL_INFO]) {
++		int flag, type;
++
++		err = -EINVAL;
++		flag = tb[MT76_TM_ATTR_PRECAL] ? 1 : 0;
++		type = flag ? nla_get_u8(tb[MT76_TM_ATTR_PRECAL_INFO]) : 0;
++		if (dev->test_ops->dump_precal)
++			err = dev->test_ops->dump_precal(phy, msg, flag, type);
++
++		goto out;
++	}
++
+ 	if (tb[MT76_TM_ATTR_STATS]) {
+ 		err = -EINVAL;
+ 
+diff --git a/testmode.h b/testmode.h
+index 96872e8c..d6601cdc 100644
+--- a/testmode.h
++++ b/testmode.h
+@@ -220,6 +220,14 @@ enum mt76_testmode_state {
+ 	MT76_TM_STATE_TX_FRAMES,
+ 	MT76_TM_STATE_RX_FRAMES,
+ 	MT76_TM_STATE_TX_CONT,
++	MT76_TM_STATE_GROUP_PREK,
++	MT76_TM_STATE_GROUP_PREK_DUMP,
++	MT76_TM_STATE_GROUP_PREK_CLEAN,
++	MT76_TM_STATE_DPD_2G,
++	MT76_TM_STATE_DPD_5G,
++	MT76_TM_STATE_DPD_6G,
++	MT76_TM_STATE_DPD_DUMP,
++	MT76_TM_STATE_DPD_CLEAN,
+ 	MT76_TM_STATE_ON,
+ 
+ 	/* keep last */
+diff --git a/tools/fields.c b/tools/fields.c
+index 055f90f3..b0122763 100644
+--- a/tools/fields.c
++++ b/tools/fields.c
+@@ -11,6 +11,14 @@ static const char * const testmode_state[] = {
+ 	[MT76_TM_STATE_TX_FRAMES] = "tx_frames",
+ 	[MT76_TM_STATE_RX_FRAMES] = "rx_frames",
+ 	[MT76_TM_STATE_TX_CONT] = "tx_cont",
++	[MT76_TM_STATE_GROUP_PREK] = "group_prek",
++	[MT76_TM_STATE_GROUP_PREK_DUMP] = "group_prek_dump",
++	[MT76_TM_STATE_GROUP_PREK_CLEAN] = "group_prek_clean",
++	[MT76_TM_STATE_DPD_2G] = "dpd_2g",
++	[MT76_TM_STATE_DPD_5G] = "dpd_5g",
++	[MT76_TM_STATE_DPD_6G] = "dpd_6g",
++	[MT76_TM_STATE_DPD_DUMP] = "dpd_dump",
++	[MT76_TM_STATE_DPD_CLEAN] = "dpd_clean",
+ };
+ 
+ static const char * const testmode_tx_mode[] = {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0028-mtk-wifi-mt76-mt7996-support-eagle-ZWDFS-on-iFEM.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0028-mtk-wifi-mt76-mt7996-support-eagle-ZWDFS-on-iFEM.patch
deleted file mode 100644
index 3cbb987..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0028-mtk-wifi-mt76-mt7996-support-eagle-ZWDFS-on-iFEM.patch
+++ /dev/null
@@ -1,154 +0,0 @@
-From 1be3480176150329dd571219f69ccd8efa312daa Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 5 Jul 2023 10:00:17 +0800
-Subject: [PATCH 028/116] mtk: wifi: mt76: mt7996: support eagle ZWDFS on iFEM
-
-Fix the case that control channel is not first chan during first
-interface setup.
-Refactor ifem adie logic (if/else to switch, use sku_type & fem_type)
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/main.c   | 65 ++++++++++++++++++++++++++++++++++++++++++++++---
- mt7996/mcu.c    |  6 +++--
- mt7996/mt7996.h |  1 +
- 3 files changed, 67 insertions(+), 5 deletions(-)
-
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 46e3e5a..61396a8 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -1444,6 +1444,61 @@ mt7996_twt_teardown_request(struct ieee80211_hw *hw,
- 	mutex_unlock(&dev->mt76.mutex);
- }
- 
-+static void
-+mt7996_background_radar_handle_7975_ifem(struct ieee80211_hw *hw,
-+					 struct cfg80211_chan_def *user_chandef,
-+					 struct cfg80211_chan_def *fw_chandef)
-+{
-+	struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+	struct cfg80211_chan_def *c = user_chandef;
-+	struct ieee80211_channel *first_chan;
-+	bool is_ifem_adie, expand = false;
-+
-+	switch (mt76_chip(&dev->mt76)) {
-+	case 0x7990:
-+		is_ifem_adie = dev->fem_type == MT7996_FEM_INT &&
-+			       dev->chip_sku != MT7996_SKU_233;
-+		break;
-+	case 0x7992:
-+		is_ifem_adie = dev->chip_sku == MT7992_SKU_44 &&
-+			       dev->fem_type != MT7996_FEM_EXT;
-+		break;
-+	default:
-+		return;
-+	}
-+
-+	if (!user_chandef || !is_ifem_adie)
-+		goto out;
-+
-+	if (user_chandef->width == NL80211_CHAN_WIDTH_160) {
-+		first_chan = ieee80211_get_channel(hw->wiphy, user_chandef->center_freq1 - 70);
-+		if (dev->bg_nxt_freq)
-+			goto out;
-+
-+		if (first_chan->flags & IEEE80211_CHAN_RADAR)
-+			dev->bg_nxt_freq = first_chan->center_freq;
-+		else
-+			c = fw_chandef;
-+
-+		c->chan = ieee80211_get_channel(hw->wiphy, first_chan->center_freq + 80);
-+	} else {
-+		if (!dev->bg_nxt_freq)
-+			goto out;
-+
-+		c->chan = ieee80211_get_channel(hw->wiphy, dev->bg_nxt_freq);
-+		dev->bg_nxt_freq = 0;
-+		expand = true;
-+	}
-+	c->width = NL80211_CHAN_WIDTH_80;
-+	c->center_freq1 = c->chan->center_freq + 30;
-+
-+	if (c == user_chandef)
-+		cfg80211_background_radar_update_channel(hw->wiphy, c, expand);
-+	return;
-+out:
-+	dev->bg_nxt_freq = 0;
-+}
-+
- static int
- mt7996_set_radar_background(struct ieee80211_hw *hw,
- 			    struct cfg80211_chan_def *chandef)
-@@ -1452,6 +1507,7 @@ mt7996_set_radar_background(struct ieee80211_hw *hw,
- 	struct mt7996_dev *dev = phy->dev;
- 	int ret = -EINVAL;
- 	bool running;
-+	struct cfg80211_chan_def ifem_chandef = {};
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
-@@ -1464,13 +1520,14 @@ mt7996_set_radar_background(struct ieee80211_hw *hw,
- 		goto out;
- 	}
- 
-+	mt7996_background_radar_handle_7975_ifem(hw, chandef, &ifem_chandef);
-+
- 	/* rdd2 already configured on a radar channel */
- 	running = dev->rdd2_phy &&
- 		  cfg80211_chandef_valid(&dev->rdd2_chandef) &&
- 		  !!(dev->rdd2_chandef.chan->flags & IEEE80211_CHAN_RADAR);
- 
--	if (!chandef || running ||
--	    !(chandef->chan->flags & IEEE80211_CHAN_RADAR)) {
-+	if (!chandef || running) {
- 		ret = mt7996_mcu_rdd_background_enable(phy, NULL);
- 		if (ret)
- 			goto out;
-@@ -1479,7 +1536,9 @@ mt7996_set_radar_background(struct ieee80211_hw *hw,
- 			goto update_phy;
- 	}
- 
--	ret = mt7996_mcu_rdd_background_enable(phy, chandef);
-+	ret = mt7996_mcu_rdd_background_enable(phy,
-+					       ifem_chandef.chan ?
-+					       &ifem_chandef : chandef);
- 	if (ret)
- 		goto out;
- 
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index ffd3536..a3b66f1 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -372,12 +372,14 @@ mt7996_mcu_rx_radar_detected(struct mt7996_dev *dev, struct sk_buff *skb)
- 	if (!mphy)
- 		return;
- 
--	if (r->band_idx == MT_RX_SEL2)
-+	if (r->band_idx == MT_RX_SEL2) {
-+		dev->bg_nxt_freq = 0;
- 		cfg80211_background_radar_event(mphy->hw->wiphy,
- 						&dev->rdd2_chandef,
- 						GFP_ATOMIC);
--	else
-+	} else {
- 		ieee80211_radar_detected(mphy->hw);
-+	}
- 	dev->hw_pattern++;
- }
- 
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 7d6ec8b..fd93db2 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -399,6 +399,7 @@ struct mt7996_dev {
- 	bool testmode_enable;
- 	bool bin_file_mode;
- 	u8 eeprom_mode;
-+	u32 bg_nxt_freq;
- 
- 	bool ibf;
- 	u8 fw_debug_wm;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0029-mtk-mt76-mt7996-add-normal-mode-pre-calibration-supp.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0029-mtk-mt76-mt7996-add-normal-mode-pre-calibration-supp.patch
new file mode 100644
index 0000000..f3b6ac5
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0029-mtk-mt76-mt7996-add-normal-mode-pre-calibration-supp.patch
@@ -0,0 +1,285 @@
+From 9785970113aee510f38ef39577cb9e0154694e36 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 1 Mar 2023 12:12:51 +0800
+Subject: [PATCH 029/199] mtk: mt76: mt7996: add normal mode pre-calibration
+ support
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt76_connac_mcu.h |   1 +
+ mt7996/eeprom.c   |   4 ++
+ mt7996/eeprom.h   |   2 +
+ mt7996/init.c     |   6 ++
+ mt7996/main.c     |   6 ++
+ mt7996/mcu.c      | 166 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mt7996.h   |   3 +
+ 7 files changed, 188 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index f994a2b3..0d30aff3 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1289,6 +1289,7 @@ enum {
+ 	MCU_UNI_CMD_PP = 0x38,
+ 	MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
+ 	MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
++	MCU_UNI_CMD_PRECAL_RESULT = 0x47,
+ 	MCU_UNI_CMD_RRO = 0x57,
+ 	MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
+ 	MCU_UNI_CMD_PER_STA_INFO = 0x6d,
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 80b9f6f9..454df971 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -356,6 +356,10 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ 			return ret;
+ 	}
+ 
++	ret = mt7996_eeprom_load_precal(dev);
++	if (ret)
++		return ret;
++
+ 	ret = mt7996_eeprom_parse_hw_cap(dev, &dev->phy);
+ 	if (ret < 0)
+ 		return ret;
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index 849b8bca..58179c0c 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -25,6 +25,8 @@ enum mt7996_eeprom_field {
+ 	MT_EE_TX0_POWER_6G =	0x1310,
+ 
+ 	__MT_EE_MAX =	0x1dff,
++	/* 0x1e10 ~ 0x2d644 used to save group cal data */
++	MT_EE_PRECAL =		0x1e10,
+ };
+ 
+ #define MT_EE_WIFI_CONF0_TX_PATH		GENMASK(2, 0)
+diff --git a/mt7996/init.c b/mt7996/init.c
+index ed7c7040..2749a5d7 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -987,6 +987,12 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
+ 	if (ret < 0)
+ 		return ret;
+ 
++	if (dev->flash_mode) {
++		ret = mt7996_mcu_apply_group_cal(dev);
++		if (ret)
++			return ret;
++	}
++
+ 	/* Beacon and mgmt frames should occupy wcid 0 */
+ 	idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
+ 	if (idx)
+diff --git a/mt7996/main.c b/mt7996/main.c
+index be50f7f3..253bb2e3 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -317,6 +317,12 @@ int mt7996_set_channel(struct mt7996_phy *phy)
+ 
+ 	mt76_set_channel(phy->mt76);
+ 
++	if (dev->flash_mode) {
++		ret = mt7996_mcu_apply_tx_dpd(phy);
++		if (ret)
++			goto out;
++	}
++
+ 	if (mt76_testmode_enabled(phy->mt76) || phy->mt76->test.bf_en) {
+ 		mt7996_tm_update_channel(phy);
+ 		goto out;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 75f143e2..7c5e94f8 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3634,6 +3634,172 @@ int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num)
+ 	return 0;
+ }
+ 
++static int mt7996_mcu_set_pre_cal(struct mt7996_dev *dev, u32 idx,
++				  u8 *cal, u32 len, u32 cal_id)
++{
++#define PRECAL_CMD_PRE_CAL_RESULT	0x0
++	struct {
++		/* fixed field */
++		u8 action;
++		u8 dest;
++		u8 attribute;
++		u8 tag_num;
++
++		__le16 tag;
++		__le16 len;
++
++		__le32 cal_id;
++		s8 precal;
++		u8 band;
++		u8 rsv[2];
++		__le32 idx;
++		__le32 cal_len;
++	} req = {
++		.tag = cpu_to_le16(PRECAL_CMD_PRE_CAL_RESULT),
++		.len = cpu_to_le16(sizeof(req) - 4 + len),
++		.cal_id = cpu_to_le32(cal_id),
++		.idx = cpu_to_le32(idx),
++		.cal_len = cpu_to_le32(len),
++	};
++	struct sk_buff *skb;
++
++	if (!len)
++		return 0;
++
++	skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, sizeof(req) + len);
++	if (!skb)
++		return -ENOMEM;
++
++	skb_put_data(skb, &req, sizeof(req));
++	skb_put_data(skb, cal, len);
++
++	return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(PRECAL_RESULT), false);
++}
++
++int mt7996_mcu_apply_group_cal(struct mt7996_dev *dev)
++{
++	u8 *cal = dev->cal, *eeprom = dev->mt76.eeprom.data;
++	u32 idx = 0, total_idx = MT_EE_CAL_GROUP_SIZE / MT_EE_CAL_UNIT;
++	u32 offs = MT_EE_DO_PRE_CAL;
++	int ret = 0;
++
++	if (!(eeprom[offs] & MT_EE_WIFI_CAL_GROUP))
++		return 0;
++
++	for (idx = 0; idx < total_idx; idx++, cal += MT_EE_CAL_UNIT) {
++		ret = mt7996_mcu_set_pre_cal(dev, idx, cal, MT_EE_CAL_UNIT, RF_PRE_CAL);
++		if (ret)
++			goto out;
++	}
++
++	ret = mt7996_mcu_set_pre_cal(dev, total_idx, cal,
++				     MT_EE_CAL_GROUP_SIZE % MT_EE_CAL_UNIT, RF_PRE_CAL);
++
++out:
++	return ret;
++}
++
++int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct mt76_phy *mphy = phy->mt76;
++	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
++	enum nl80211_band band = chandef->chan->band;
++	enum nl80211_chan_width bw = chandef->width;
++	const struct ieee80211_channel *chan_list;
++	u32 cal_id, chan_list_size, base_offset = 0, offs = MT_EE_DO_PRE_CAL;
++	u32 dpd_size_2g, dpd_size_5g, per_chan_size = DPD_PER_CH_BW20_SIZE;
++	u16 channel = ieee80211_frequency_to_channel(chandef->center_freq1);
++	u8 dpd_mask, *cal = dev->cal, *eeprom = dev->mt76.eeprom.data;
++	int idx, i, ret;
++
++	dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
++	dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
++
++	switch (band) {
++	case NL80211_BAND_2GHZ:
++		dpd_mask = MT_EE_WIFI_CAL_DPD_2G;
++		/* channel 14 don't need DPD cal */
++		if (channel >= 1 && channel <= 4)
++			channel = 3;
++		else if (channel >= 5 && channel <= 9)
++			channel = 7;
++		else if (channel >= 10 && channel <= 13)
++			channel = 11;
++		else
++			return 0;
++		cal_id = RF_DPD_FLAT_CAL;
++		chan_list = dpd_2g_ch_list_bw20;
++		chan_list_size = dpd_2g_bw20_ch_num;
++		break;
++	case NL80211_BAND_5GHZ:
++		dpd_mask = MT_EE_WIFI_CAL_DPD_5G;
++		cal_id = RF_DPD_FLAT_5G_CAL;
++		chan_list = mphy->sband_5g.sband.channels;
++		chan_list_size = mphy->sband_5g.sband.n_channels;
++		base_offset += dpd_size_2g;
++		if (bw == NL80211_CHAN_WIDTH_160) {
++			base_offset += mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
++			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
++			cal_id = RF_DPD_FLAT_5G_MEM_CAL;
++			chan_list = dpd_5g_ch_list_bw160;
++			chan_list_size = dpd_5g_bw160_ch_num;
++		} else if (bw > NL80211_CHAN_WIDTH_20) {
++			/* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
++			channel -= 2;
++		}
++		break;
++	case NL80211_BAND_6GHZ:
++		dpd_mask = MT_EE_WIFI_CAL_DPD_6G;
++		cal_id = RF_DPD_FLAT_6G_CAL;
++		chan_list = mphy->sband_6g.sband.channels;
++		chan_list_size = mphy->sband_6g.sband.n_channels;
++		base_offset += dpd_size_2g + dpd_size_5g;
++		if (bw == NL80211_CHAN_WIDTH_160) {
++			base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
++			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
++			cal_id = RF_DPD_FLAT_6G_MEM_CAL;
++			chan_list = dpd_6g_ch_list_bw160;
++			chan_list_size = dpd_6g_bw160_ch_num;
++		} else if (bw == NL80211_CHAN_WIDTH_320) {
++			base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
++				       dpd_6g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
++			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
++			cal_id = RF_DPD_FLAT_6G_MEM_CAL;
++			chan_list = dpd_6g_ch_list_bw320;
++			chan_list_size = dpd_6g_bw320_ch_num;
++		} else if (bw > NL80211_CHAN_WIDTH_20) {
++			/* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
++			channel -= 2;
++		}
++		break;
++	default:
++		dpd_mask = 0;
++		break;
++	}
++
++	if (!(eeprom[offs] & dpd_mask))
++		return 0;
++
++	for (idx = 0; idx < chan_list_size; idx++)
++		if (channel == chan_list[idx].hw_value)
++			break;
++	if (idx == chan_list_size)
++		return -EINVAL;
++
++	cal += MT_EE_CAL_GROUP_SIZE + base_offset + idx * per_chan_size;
++
++	for (i = 0; i < per_chan_size / MT_EE_CAL_UNIT; i++) {
++		ret = mt7996_mcu_set_pre_cal(dev, i, cal, MT_EE_CAL_UNIT, cal_id);
++		if (ret)
++			return ret;
++
++		cal += MT_EE_CAL_UNIT;
++	}
++
++	return ret;
++}
++
+ int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap)
+ {
+ #define NIC_CAP	3
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index c01a2145..1744998d 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -597,6 +597,9 @@ void mt7996_mcu_exit(struct mt7996_dev *dev);
+ int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
+ int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
+ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
++int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
++int mt7996_mcu_apply_group_cal(struct mt7996_dev *dev);
++int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy);
+ #ifdef CONFIG_NL80211_TESTMODE
+ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ #endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0029-mtk-wifi-mt76-mt7996-refactor-eeprom-loading-flow-fo.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0029-mtk-wifi-mt76-mt7996-refactor-eeprom-loading-flow-fo.patch
deleted file mode 100644
index 786d7e6..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0029-mtk-wifi-mt76-mt7996-refactor-eeprom-loading-flow-fo.patch
+++ /dev/null
@@ -1,481 +0,0 @@
-From 1ed0aea054ae9fcd011f81333888d15cba7fc19f Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 11 Mar 2024 10:43:03 +0800
-Subject: [PATCH 029/116] mtk: wifi: mt76: mt7996: refactor eeprom loading flow
- for sku checking
-
-Add eeprom sku checking mechanism to avoid using the
-wrong eeprom in flash/binfile mode
-The fields listed below will be checked by comparing the loaded eeprom to the default bin
-1. FEM type
-2. MAC address (warning for using default MAC address)
-3. RF path & streams
-   (to distinguish cases such as BE7200 4i5i, BE6500 3i5i, and BE5040 2i3i)
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-1. Reset eeprom content before loading efuse
-   eeprom array might contain incomplete data read from flash or
-   binfile, which is not overwritten since this block is invalid
-   in efuse.
-2. Remove testmode default bin since it is unnecessary
-   Not used in logan. Directly load normal mode default bin is fine.
-   Also, this way is better since we don't need to put testmode default
-   eeprom for each sku (especially kite's sku) in our SDK.
-3. Set testmode_en field for default bin mode for consistency sake
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-1.
-Fix efuse mode txpower = 0 issue
-This fix might be changed if fw supports efuse merge for buffer mode = EE_MODE_EFUSE
-2.
-Add Eagle BE19000 ifem default bin, add Eagle default bin bootstrip
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/eeprom.c      | 207 ++++++++++++++++++++++++-------------------
- mt7996/eeprom.h      |  32 +++++++
- mt7996/mcu.c         |  18 +---
- mt7996/mt7996.h      |   3 +-
- mt7996/mtk_debugfs.c |   2 +-
- 5 files changed, 150 insertions(+), 112 deletions(-)
-
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index fe8b253..f97d76c 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -50,54 +50,84 @@ const u32 dpd_6g_bw320_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw320);
- 
- static int mt7996_check_eeprom(struct mt7996_dev *dev)
- {
--#define FEM_INT				0
--#define FEM_EXT				3
- 	u8 *eeprom = dev->mt76.eeprom.data;
--	u8 i, fem[__MT_MAX_BAND], fem_type;
- 	u16 val = get_unaligned_le16(eeprom);
- 
--	for (i = 0; i < __MT_MAX_BAND; i++)
--		fem[i] = eeprom[MT_EE_WIFI_CONF + 6 + i] & MT_EE_WIFI_PA_LNA_CONFIG;
--
- 	switch (val) {
- 	case 0x7990:
- 		return is_mt7996(&dev->mt76) ? 0 : -EINVAL;
- 	case 0x7992:
--		if (dev->fem_type == MT7996_FEM_UNSET)
--			return is_mt7992(&dev->mt76) ? 0 : -EINVAL;
--
--		if (fem[0] == FEM_EXT && fem[1] == FEM_EXT)
--			fem_type = MT7996_FEM_EXT;
--		else if (fem[0] == FEM_INT && fem[1] == FEM_INT)
--			fem_type = MT7996_FEM_INT;
--		else if (fem[0] == FEM_INT && fem[1] == FEM_EXT)
--			fem_type = MT7996_FEM_MIX;
--		else
--			return -EINVAL;
--
--		return (is_mt7992(&dev->mt76) ? 0 : -EINVAL) |
--		       (dev->fem_type == fem_type ? 0 : -EINVAL);
-+		return is_mt7992(&dev->mt76) ? 0 : -EINVAL;
- 	default:
- 		return -EINVAL;
- 	}
- }
- 
--const char *mt7996_eeprom_name(struct mt7996_dev *dev)
-+static int mt7996_check_eeprom_sku(struct mt7996_dev *dev, const u8 *dflt)
- {
--	if (dev->bin_file_mode)
--		return dev->mt76.bin_file_name;
-+#define FEM_INT				0
-+#define FEM_EXT				3
-+	u8 *eeprom = dev->mt76.eeprom.data;
-+	u8 i, fem[__MT_MAX_BAND], fem_type;
-+	u16 mac_addr[__MT_MAX_BAND] = {MT_EE_MAC_ADDR, MT_EE_MAC_ADDR2, MT_EE_MAC_ADDR3};
-+	int max_band = is_mt7996(&dev->mt76) ? __MT_MAX_BAND : 2;
-+
-+	if (dev->fem_type == MT7996_FEM_UNSET)
-+		return -EINVAL;
-+
-+	/* FEM type */
-+	for (i = 0; i < max_band; i++)
-+		fem[i] = eeprom[MT_EE_WIFI_CONF + 6 + i] & MT_EE_WIFI_PA_LNA_CONFIG;
-+
-+	if (fem[0] == FEM_EXT && fem[1] == FEM_EXT)
-+		fem_type = MT7996_FEM_EXT;
-+	else if (fem[0] == FEM_INT && fem[1] == FEM_INT)
-+		fem_type = MT7996_FEM_INT;
-+	else if (fem[0] == FEM_INT && fem[1] == FEM_EXT)
-+		fem_type = MT7996_FEM_MIX;
-+	else
-+		return -EINVAL;
- 
--	if (dev->testmode_enable) {
--		if (is_mt7992(&dev->mt76))
--			return MT7992_EEPROM_DEFAULT_TM;
--		else
--			return MT7996_EEPROM_DEFAULT_TM;
-+	if (dev->fem_type != fem_type)
-+		return -EINVAL;
-+
-+	/* RF path & stream */
-+	for (i = 0; i < max_band; i++) {
-+		u8 path, rx_path, nss;
-+		u8 dflt_path, dflt_rx_path, dflt_nss;
-+
-+		/* MAC address */
-+		if (ether_addr_equal(eeprom + mac_addr[i], dflt + mac_addr[i]))
-+			dev_warn(dev->mt76.dev,
-+				 "Currently using default MAC address for band %d\n", i);
-+
-+		mt7996_parse_eeprom_stream(eeprom, i, &path, &rx_path, &nss);
-+		mt7996_parse_eeprom_stream(dflt, i, &dflt_path, &dflt_rx_path, &dflt_nss);
-+		if (path > dflt_path || rx_path > dflt_rx_path || nss > dflt_nss) {
-+			dev_err(dev->mt76.dev,
-+				"Invalid path/stream configuration for band %d\n", i);
-+			return -EINVAL;
-+		} else if (path < dflt_path || rx_path < dflt_rx_path || nss < dflt_nss) {
-+			dev_warn(dev->mt76.dev,
-+				 "Restricted path/stream configuration for band %d\n", i);
-+			dev_warn(dev->mt76.dev,
-+				 "path: %u/%u, rx_path: %u/%u, nss: %u/%u\n",
-+				 path, dflt_path, rx_path, dflt_rx_path, nss, dflt_nss);
-+		}
- 	}
- 
-+	return 0;
-+}
-+
-+const char *mt7996_eeprom_name(struct mt7996_dev *dev)
-+{
- 	switch (mt76_chip(&dev->mt76)) {
- 	case 0x7990:
- 		if (dev->chip_sku == MT7996_SKU_404)
- 			return MT7996_EEPROM_DEFAULT_404;
-+
-+		if (dev->fem_type == MT7996_FEM_INT)
-+			return MT7996_EEPROM_DEFAULT_INT;
- 		return MT7996_EEPROM_DEFAULT;
- 	case 0x7992:
- 		if (dev->chip_sku == MT7992_SKU_23) {
-@@ -148,21 +178,18 @@ mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band)
- }
- 
- static int
--mt7996_eeprom_load_default(struct mt7996_dev *dev)
-+mt7996_eeprom_load_bin(struct mt7996_dev *dev)
- {
- 	u8 *eeprom = dev->mt76.eeprom.data;
- 	const struct firmware *fw = NULL;
- 	int ret;
- 
--	ret = request_firmware(&fw, mt7996_eeprom_name(dev), dev->mt76.dev);
-+	ret = request_firmware(&fw, dev->mt76.bin_file_name, dev->mt76.dev);
- 	if (ret)
- 		return ret;
- 
- 	if (!fw || !fw->data) {
--		if (dev->bin_file_mode)
--			dev_err(dev->mt76.dev, "Invalid bin (bin file mode)\n");
--		else
--			dev_err(dev->mt76.dev, "Invalid default bin\n");
-+		dev_err(dev->mt76.dev, "Invalid bin %s\n", dev->mt76.bin_file_name);
- 		ret = -EINVAL;
- 		goto out;
- 	}
-@@ -180,7 +207,7 @@ static int mt7996_eeprom_load_flash(struct mt7996_dev *dev)
- {
- 	int ret = 1;
- 
--	/* return > 0 for load success, return 0 for load failed, return < 0 for non memory */
-+	/* return > 0 for load success, return 0 for load failed, return < 0 for no memory */
- 	dev->bin_file_mode = mt76_check_bin_file_mode(&dev->mt76);
- 	if (dev->bin_file_mode) {
- 		dev->mt76.eeprom.size = MT7996_EEPROM_SIZE;
-@@ -189,15 +216,15 @@ static int mt7996_eeprom_load_flash(struct mt7996_dev *dev)
- 		if (!dev->mt76.eeprom.data)
- 			return -ENOMEM;
- 
--		if (mt7996_eeprom_load_default(dev))
--			return 0;
--
--		if (mt7996_check_eeprom(dev))
-+		if (mt7996_eeprom_load_bin(dev))
- 			return 0;
- 	} else {
- 		ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
- 	}
- 
-+	if (mt7996_check_eeprom(dev))
-+		return 0;
-+
- 	return ret;
- }
- 
-@@ -206,30 +233,30 @@ int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev)
- 	u8 *eeprom;
- 	int ret;
- 
-+	dev->testmode_enable = testmode_enable;
-+
- 	/* load eeprom in flash or bin file mode to determine fw mode */
- 	ret = mt7996_eeprom_load_flash(dev);
-+	if (ret <= 0)
-+		goto out;
- 
--	if (ret < 0)
--		return ret;
--
--	if (ret) {
--		dev->flash_mode = true;
--		dev->eeprom_mode = dev->bin_file_mode ? BIN_FILE_MODE : FLASH_MODE;
--		eeprom = dev->mt76.eeprom.data;
--		/* testmode enable priority: eeprom field > module parameter */
--		dev->testmode_enable = !mt7996_check_eeprom(dev) ? eeprom[MT_EE_TESTMODE_EN] :
--								   testmode_enable;
--	}
-+	dev->flash_mode = true;
-+	dev->eeprom_mode = dev->bin_file_mode ? BIN_FILE_MODE : FLASH_MODE;
-+	eeprom = dev->mt76.eeprom.data;
-+	/* testmode enable priority: eeprom field > module parameter */
-+	dev->testmode_enable = eeprom[MT_EE_TESTMODE_EN];
- 
-+out:
- 	return ret;
- }
- 
- static int mt7996_eeprom_load(struct mt7996_dev *dev)
- {
-+	const struct firmware *fw = NULL;
- 	int ret;
--	u8 free_block_num;
- 	u32 block_num, i;
- 	u32 eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE;
-+	u8 free_block_num, *eeprom = dev->mt76.eeprom.data;
- 
- 	/* flash or bin file mode eeprom is loaded before mcu init */
- 	if (!dev->flash_mode) {
-@@ -239,19 +266,49 @@ static int mt7996_eeprom_load(struct mt7996_dev *dev)
- 
- 		/* efuse info isn't enough */
- 		if (free_block_num >= 59)
--			return -EINVAL;
-+			goto dflt;
-+
-+		memset(eeprom, 0, MT7996_EEPROM_SIZE);
-+		/* check if efuse contains valid eeprom data */
-+		if (mt7996_mcu_get_eeprom(dev, 0, NULL) ||
-+		    mt7996_check_eeprom(dev))
-+			goto dflt;
- 
- 		/* read eeprom data from efuse */
- 		block_num = DIV_ROUND_UP(MT7996_EEPROM_SIZE, eeprom_blk_size);
--		for (i = 0; i < block_num; i++) {
-+		for (i = 1; i < block_num; i++) {
- 			ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size, NULL);
- 			if (ret && ret != -EINVAL)
--				return ret;
-+				goto dflt;
- 		}
- 		dev->eeprom_mode = EFUSE_MODE;
- 	}
- 
--	return mt7996_check_eeprom(dev);
-+dflt:
-+	ret = request_firmware(&fw, mt7996_eeprom_name(dev), dev->mt76.dev);
-+	if (ret)
-+		return ret;
-+
-+	if (!fw || !fw->data) {
-+		dev_err(dev->mt76.dev, "Invalid default bin\n");
-+		ret = -EINVAL;
-+		goto out;
-+	}
-+
-+	if (dev->eeprom_mode && !mt7996_check_eeprom_sku(dev, fw->data)) {
-+		ret = 0;
-+		goto out;
-+	}
-+
-+	memcpy(eeprom, fw->data, MT7996_EEPROM_SIZE);
-+	dev->bin_file_mode = false;
-+	dev->flash_mode = true;
-+	dev->eeprom_mode = DEFAULT_BIN_MODE;
-+	eeprom[MT_EE_TESTMODE_EN] = dev->testmode_enable;
-+	dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n");
-+out:
-+	release_firmware(fw);
-+	return ret;
- }
- 
- static int mt7996_eeprom_parse_efuse_hw_cap(struct mt7996_dev *dev)
-@@ -323,32 +380,7 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy)
- 	int max_path = 5, max_nss = 4;
- 	int ret;
- 
--	switch (band_idx) {
--	case MT_BAND1:
--		path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND1,
--				 eeprom[MT_EE_WIFI_CONF + 2]);
--		rx_path = FIELD_GET(MT_EE_WIFI_CONF3_RX_PATH_BAND1,
--				    eeprom[MT_EE_WIFI_CONF + 3]);
--		nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND1,
--				eeprom[MT_EE_WIFI_CONF + 5]);
--		break;
--	case MT_BAND2:
--		path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND2,
--				 eeprom[MT_EE_WIFI_CONF + 2]);
--		rx_path = FIELD_GET(MT_EE_WIFI_CONF4_RX_PATH_BAND2,
--				    eeprom[MT_EE_WIFI_CONF + 4]);
--		nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND2,
--				eeprom[MT_EE_WIFI_CONF + 5]);
--		break;
--	default:
--		path = FIELD_GET(MT_EE_WIFI_CONF1_TX_PATH_BAND0,
--				 eeprom[MT_EE_WIFI_CONF + 1]);
--		rx_path = FIELD_GET(MT_EE_WIFI_CONF3_RX_PATH_BAND0,
--				    eeprom[MT_EE_WIFI_CONF + 3]);
--		nss = FIELD_GET(MT_EE_WIFI_CONF4_STREAM_NUM_BAND0,
--				eeprom[MT_EE_WIFI_CONF + 4]);
--		break;
--	}
-+	mt7996_parse_eeprom_stream(eeprom, band_idx, &path, &rx_path, &nss);
- 
- 	if (!path || path > max_path)
- 		path = max_path;
-@@ -437,17 +469,8 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
- 		return ret;
- 
- 	ret = mt7996_eeprom_load(dev);
--	if (ret < 0) {
--		if (ret != -EINVAL)
--			return ret;
--
--		dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n");
--		dev->bin_file_mode = false;
--		dev->eeprom_mode = DEFAULT_BIN_MODE;
--		ret = mt7996_eeprom_load_default(dev);
--		if (ret)
--			return ret;
--	}
-+	if (ret)
-+		return ret;
- 
- 	ret = mt7996_eeprom_load_precal(dev);
- 	if (ret)
-diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index 8f0f87b..03a4fd0 100644
---- a/mt7996/eeprom.h
-+++ b/mt7996/eeprom.h
-@@ -132,6 +132,38 @@ mt7996_get_channel_group_6g(int channel)
- 	return DIV_ROUND_UP(channel - 29, 32);
- }
- 
-+static inline void
-+mt7996_parse_eeprom_stream(const u8 *eep, int band_idx,
-+			   u8 *path, u8 *rx_path, u8 *nss)
-+{
-+	switch (band_idx) {
-+	case MT_BAND1:
-+		*path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND1,
-+				  eep[MT_EE_WIFI_CONF + 2]);
-+		*rx_path = FIELD_GET(MT_EE_WIFI_CONF3_RX_PATH_BAND1,
-+				     eep[MT_EE_WIFI_CONF + 3]);
-+		*nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND1,
-+				 eep[MT_EE_WIFI_CONF + 5]);
-+		break;
-+	case MT_BAND2:
-+		*path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND2,
-+				  eep[MT_EE_WIFI_CONF + 2]);
-+		*rx_path = FIELD_GET(MT_EE_WIFI_CONF4_RX_PATH_BAND2,
-+				     eep[MT_EE_WIFI_CONF + 4]);
-+		*nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND2,
-+				 eep[MT_EE_WIFI_CONF + 5]);
-+		break;
-+	default:
-+		*path = FIELD_GET(MT_EE_WIFI_CONF1_TX_PATH_BAND0,
-+				  eep[MT_EE_WIFI_CONF + 1]);
-+		*rx_path = FIELD_GET(MT_EE_WIFI_CONF3_RX_PATH_BAND0,
-+				     eep[MT_EE_WIFI_CONF + 3]);
-+		*nss = FIELD_GET(MT_EE_WIFI_CONF4_STREAM_NUM_BAND0,
-+				 eep[MT_EE_WIFI_CONF + 4]);
-+		break;
-+	}
-+}
-+
- enum mt7996_sku_rate_group {
- 	SKU_CCK,
- 	SKU_OFDM,
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index a3b66f1..446fe1f 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -3507,7 +3507,7 @@ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag)
- 				 &req, sizeof(req), true);
- }
- 
--static int mt7996_mcu_set_eeprom_flash(struct mt7996_dev *dev)
-+int mt7996_mcu_set_eeprom(struct mt7996_dev *dev)
- {
- #define MAX_PAGE_IDX_MASK	GENMASK(7, 5)
- #define PAGE_IDX_MASK		GENMASK(4, 2)
-@@ -3552,22 +3552,6 @@ static int mt7996_mcu_set_eeprom_flash(struct mt7996_dev *dev)
- 	return 0;
- }
- 
--int mt7996_mcu_set_eeprom(struct mt7996_dev *dev)
--{
--	struct mt7996_mcu_eeprom req = {
--		.tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE),
--		.len = cpu_to_le16(sizeof(req) - 4),
--		.buffer_mode = EE_MODE_EFUSE,
--		.format = EE_FORMAT_WHOLE
--	};
--
--	if (dev->flash_mode)
--		return mt7996_mcu_set_eeprom_flash(dev);
--
--	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(EFUSE_CTRL),
--				 &req, sizeof(req), true);
--}
--
- int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf)
- {
- 	struct mt7996_mcu_eeprom_info req = {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index fd93db2..b2a9381 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -54,15 +54,14 @@
- #define MT7992_ROM_PATCH_23		"mediatek/mt7996/mt7992_rom_patch_23.bin"
- 
- #define MT7996_EEPROM_DEFAULT		"mediatek/mt7996/mt7996_eeprom.bin"
-+#define MT7996_EEPROM_DEFAULT_INT	"mediatek/mt7996/mt7996_eeprom_2i5i6i.bin"
- #define MT7996_EEPROM_DEFAULT_404	"mediatek/mt7996/mt7996_eeprom_dual_404.bin"
--#define MT7996_EEPROM_DEFAULT_TM	"mediatek/mt7996/mt7996_eeprom_tm.bin"
- #define MT7992_EEPROM_DEFAULT		"mediatek/mt7996/mt7992_eeprom_2i5i.bin"
- #define MT7992_EEPROM_DEFAULT_EXT	"mediatek/mt7996/mt7992_eeprom_2e5e.bin"
- #define MT7992_EEPROM_DEFAULT_MIX	"mediatek/mt7996/mt7992_eeprom_2i5e.bin"
- #define MT7992_EEPROM_DEFAULT_24	"mediatek/mt7996/mt7992_eeprom_24_2i5i.bin"
- #define MT7992_EEPROM_DEFAULT_23	"mediatek/mt7996/mt7992_eeprom_23_2i5i.bin"
- #define MT7992_EEPROM_DEFAULT_23_EXT	"mediatek/mt7996/mt7992_eeprom_23_2e5e.bin"
--#define MT7992_EEPROM_DEFAULT_TM	"mediatek/mt7996/mt7992_eeprom_tm.bin"
- #define MT7996_EEPROM_SIZE		7680
- #define MT7996_EEPROM_BLOCK_SIZE	16
- #define MT7996_TOKEN_SIZE		16384
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index 2499f12..2a9f213 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -2827,7 +2827,7 @@ static int mt7996_show_eeprom_mode(struct seq_file *s, void *data)
- 			seq_printf(s, "   flash mode\n");
- 		break;
- 	case BIN_FILE_MODE:
--		seq_printf(s, "   bin file mode\n   filename = %s\n", mt7996_eeprom_name(dev));
-+		seq_printf(s, "   bin file mode\n   filename = %s\n", dev->mt76.bin_file_name);
- 		break;
- 	default:
- 		break;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0030-mtk-mt76-mt7996-enable-SCS-feature-for-mt7996-driver.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0030-mtk-mt76-mt7996-enable-SCS-feature-for-mt7996-driver.patch
new file mode 100644
index 0000000..145e845
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0030-mtk-mt76-mt7996-enable-SCS-feature-for-mt7996-driver.patch
@@ -0,0 +1,304 @@
+From b884ffbff5630ccb06b2b9212b47b8c5cf56bb47 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 8 May 2023 09:03:50 +0800
+Subject: [PATCH 030/199] mtk: mt76: mt7996: enable SCS feature for mt7996
+ driver
+
+Enable Smart Carrier Sense algorithn by default to improve performance
+in a noisy environment.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt76_connac_mcu.h    |   1 +
+ mt7996/init.c        |   1 +
+ mt7996/mac.c         |   2 +
+ mt7996/main.c        |   7 +++
+ mt7996/mcu.c         | 105 +++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h         |   6 +++
+ mt7996/mt7996.h      |  15 +++++++
+ mt7996/mtk_debugfs.c |  11 +++++
+ 8 files changed, 148 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 0d30aff3..ea60e19c 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1274,6 +1274,7 @@ enum {
+ 	MCU_UNI_CMD_GET_STAT_INFO = 0x23,
+ 	MCU_UNI_CMD_SNIFFER = 0x24,
+ 	MCU_UNI_CMD_SR = 0x25,
++	MCU_UNI_CMD_SCS = 0x26,
+ 	MCU_UNI_CMD_ROC = 0x27,
+ 	MCU_UNI_CMD_SET_DBDC_PARMS = 0x28,
+ 	MCU_UNI_CMD_TXPOWER = 0x2b,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 2749a5d7..7e813960 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -1416,6 +1416,7 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ 	dev->mt76.phy.priv = &dev->phy;
+ 	INIT_WORK(&dev->rc_work, mt7996_mac_sta_rc_work);
+ 	INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7996_mac_work);
++	INIT_DELAYED_WORK(&dev->scs_work, mt7996_mcu_scs_sta_poll);
+ 	INIT_LIST_HEAD(&dev->sta_rc_list);
+ 	INIT_LIST_HEAD(&dev->twt_list);
+ 
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index a0fcd8aa..fd8bce0e 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1804,6 +1804,7 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
+ 		cancel_delayed_work_sync(&phy2->mt76->mac_work);
+ 	if (phy3)
+ 		cancel_delayed_work_sync(&phy3->mt76->mac_work);
++	cancel_delayed_work_sync(&dev->scs_work);
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 	for (i = 0; i < 10; i++) {
+@@ -1839,6 +1840,7 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
+ 		ieee80211_queue_delayed_work(phy3->mt76->hw,
+ 					     &phy3->mt76->mac_work,
+ 					     MT7996_WATCHDOG_TIME);
++	ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
+ }
+ 
+ void mt7996_mac_reset_work(struct work_struct *work)
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 253bb2e3..22acd04e 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -81,11 +81,17 @@ int mt7996_run(struct ieee80211_hw *hw)
+ 	if (ret)
+ 		goto out;
+ 
++	ret = mt7996_mcu_set_scs(phy, SCS_ENABLE);
++	if (ret)
++		goto out;
++
+ 	set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+ 
+ 	ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
+ 				     MT7996_WATCHDOG_TIME);
+ 
++	ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
++
+ 	if (!running)
+ 		mt7996_mac_reset_counters(phy);
+ 
+@@ -113,6 +119,7 @@ static void mt7996_stop(struct ieee80211_hw *hw)
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 
+ 	cancel_delayed_work_sync(&phy->mt76->mac_work);
++	cancel_delayed_work_sync(&dev->scs_work);
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 7c5e94f8..8f725307 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -4816,3 +4816,108 @@ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 da
+ 	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TXPOWER),
+ 				 &req, sizeof(req), false);
+ }
++
++int mt7996_mcu_set_scs_stats(struct mt7996_phy *phy)
++{
++	struct mt7996_scs_ctrl ctrl = phy->scs_ctrl;
++	struct {
++		u8 band_idx;
++		u8 _rsv[3];
++
++		__le16 tag;
++		__le16 len;
++
++		u8 _rsv2[6];
++		s8 min_rssi;
++		u8 _rsv3;
++	} __packed req = {
++		.band_idx = phy->mt76->band_idx,
++		.tag = cpu_to_le16(UNI_CMD_SCS_SEND_DATA),
++		.len = cpu_to_le16(sizeof(req) - 4),
++
++		.min_rssi = ctrl.sta_min_rssi,
++	};
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SCS),
++				 &req, sizeof(req), false);
++}
++
++void mt7996_sta_rssi_work(void *data, struct ieee80211_sta *sta)
++{
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_phy *poll_phy = (struct mt7996_phy *) data;
++
++	if (poll_phy->scs_ctrl.sta_min_rssi > msta->ack_signal)
++		poll_phy->scs_ctrl.sta_min_rssi = msta->ack_signal;
++}
++
++void mt7996_mcu_scs_sta_poll(struct work_struct *work)
++{
++	struct mt7996_dev *dev = container_of(work, struct mt7996_dev,
++				 scs_work.work);
++	bool scs_enable_flag = false;
++	u8 i;
++
++	for (i = 0; i < __MT_MAX_BAND; i++) {
++		struct mt7996_phy *phy;
++
++		switch (i) {
++		case MT_BAND0:
++			phy = dev->mphy.priv;
++			break;
++		case MT_BAND1:
++			phy = mt7996_phy2(dev);
++			break;
++		case MT_BAND2:
++			phy = mt7996_phy3(dev);
++			break;
++		default:
++			phy = NULL;
++			break;
++		}
++
++		if (!phy || !test_bit(MT76_STATE_RUNNING, &phy->mt76->state) ||
++		    !phy->scs_ctrl.scs_enable)
++			continue;
++
++		ieee80211_iterate_stations_atomic(phy->mt76->hw,
++						  mt7996_sta_rssi_work, phy);
++
++		scs_enable_flag = true;
++		if (mt7996_mcu_set_scs_stats(phy))
++			dev_err(dev->mt76.dev, "Failed to send scs mcu cmd\n");
++		phy->scs_ctrl.sta_min_rssi = 0;
++	}
++
++	if (scs_enable_flag)
++		ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
++}
++
++
++int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct {
++		u8 band_idx;
++		u8 _rsv[3];
++
++		__le16 tag;
++		__le16 len;
++
++		u8 scs_enable;
++		u8 _rsv2[3];
++	} __packed req = {
++		.band_idx = phy->mt76->band_idx,
++		.tag = cpu_to_le16(UNI_CMD_SCS_ENABLE),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.scs_enable = enable,
++	};
++
++	phy->scs_ctrl.scs_enable = enable;
++
++	if (enable == SCS_ENABLE)
++		ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SCS),
++				 &req, sizeof(req), false);
++}
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index abf8ef48..f889cb8b 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -966,6 +966,12 @@ enum pp_mode {
+ 	PP_USR_MODE,
+ };
+ 
++enum {
++	UNI_CMD_SCS_SEND_DATA,
++	UNI_CMD_SCS_SET_PD_THR_RANGE = 2,
++	UNI_CMD_SCS_ENABLE,
++};
++
+ #define MT7996_PATCH_SEC		GENMASK(31, 24)
+ #define MT7996_PATCH_SCRAMBLE_KEY	GENMASK(15, 8)
+ #define MT7996_PATCH_AES_KEY		GENMASK(7, 0)
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 1744998d..d5050d6d 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -233,6 +233,16 @@ struct mt7996_hif {
+ 	int irq;
+ };
+ 
++struct mt7996_scs_ctrl {
++	u8 scs_enable;
++	s8 sta_min_rssi;
++};
++
++enum {
++	SCS_DISABLE = 0,
++	SCS_ENABLE,
++};
++
+ struct mt7996_wed_rro_addr {
+ 	u32 head_low;
+ 	u32 head_high : 4;
+@@ -283,6 +293,8 @@ struct mt7996_phy {
+ 	u8 pp_mode;
+ 	u16 punct_bitmap;
+ 
++	struct mt7996_scs_ctrl scs_ctrl;
++
+ #ifdef CONFIG_NL80211_TESTMODE
+ 	struct {
+ 		u32 *reg_backup;
+@@ -329,6 +341,7 @@ struct mt7996_dev {
+ 	struct work_struct rc_work;
+ 	struct work_struct dump_work;
+ 	struct work_struct reset_work;
++	struct delayed_work scs_work;
+ 	wait_queue_head_t reset_wait;
+ 	struct {
+ 		u32 state;
+@@ -603,6 +616,8 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy);
+ #ifdef CONFIG_NL80211_TESTMODE
+ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ #endif
++int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
++void mt7996_mcu_scs_sta_poll(struct work_struct *work);
+ 
+ static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
+ {
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 64952a73..8c576bac 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2429,6 +2429,16 @@ static int mt7996_token_read(struct seq_file *s, void *data)
+ 	return 0;
+ }
+ 
++static int
++mt7996_scs_enable_set(void *data, u64 val)
++{
++	struct mt7996_phy *phy = data;
++
++	return mt7996_mcu_set_scs(phy, (u8) val);
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_scs_enable, NULL,
++			 mt7996_scs_enable_set, "%lld\n");
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+@@ -2499,6 +2509,7 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ 	debugfs_create_devm_seqfile(dev->mt76.dev, "token", dir, mt7996_token_read);
+ 
+ 	debugfs_create_u8("sku_disable", 0600, dir, &dev->dbg.sku_disable);
++	debugfs_create_file("scs_enable", 0200, dir, phy, &fops_scs_enable);
+ 
+ 	return 0;
+ }
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0030-mtk-wifi-mt76-mt7996-add-vendor-commands-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0030-mtk-wifi-mt76-mt7996-add-vendor-commands-support.patch
deleted file mode 100644
index c5c7739..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0030-mtk-wifi-mt76-mt7996-add-vendor-commands-support.patch
+++ /dev/null
@@ -1,1433 +0,0 @@
-From 952ed39c596363be421fab82fc8dc9043a6d6b01 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Tue, 13 Dec 2022 15:17:43 +0800
-Subject: [PATCH 030/116] mtk: wifi: mt76: mt7996: add vendor commands support
-
-mtk: wifi: mt76: fix muru_onoff as all enabled by default
-
-Fix muru_onoff default value as 0xF, which means all MU & RU are
-enabled. The purpose of this commit is to align muru_onoff value with
-hostapd and mt76 driver
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
-
-mtk: wifi: mt76: mt7996: add vendor cmd to get available color bitmap
-
-Add a vendor cmd to notify user space available color bitmap.
-The OBSS BSS color bitmap is maintained in mac80211, so mt76 will make use of that.
-
-Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-
-mtk: wifi: mt76: mt7996: add vendor subcmd EDCCA ctrl enable
-
-mtk: wifi: mt76: mt7996: add ibf control vendor cmd
-
-Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
-
-mtk: wifi: mt76: mt7996: add three wire pta support
-
-three wire enable bit 0 & 1 for EXT0 & EXT1, respectively
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-mtk: wifi: mt76: mt7996: Add static pp vendor support
-
-Add static pp vendor support for setting pp mode.
-User can enable/disable preamble puncture feature through hostapd
-configuration and hostapd_cli. Driver can receive the nl80211 vendor
-message and convert it to mcu commands.
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
-Signed-off-by: Allen Ye <allen.ye@mediatek.com>
----
- mt76_connac_mcu.h |   2 +
- mt7996/Makefile   |   3 +-
- mt7996/init.c     |   9 +
- mt7996/mac.c      |   4 +
- mt7996/main.c     |   4 +
- mt7996/mcu.c      |  37 ++-
- mt7996/mcu.h      |  14 +
- mt7996/mt7996.h   |  53 +++
- mt7996/mtk_mcu.c  |  87 +++++
- mt7996/mtk_mcu.h  |  15 +
- mt7996/vendor.c   | 805 ++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/vendor.h   | 153 +++++++++
- 12 files changed, 1180 insertions(+), 6 deletions(-)
- create mode 100644 mt7996/vendor.c
- create mode 100644 mt7996/vendor.h
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 49c5ba3..750bb93 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1254,6 +1254,7 @@ enum {
- 	MCU_UNI_CMD_REG_ACCESS = 0x0d,
- 	MCU_UNI_CMD_CHIP_CONFIG = 0x0e,
- 	MCU_UNI_CMD_POWER_CTRL = 0x0f,
-+	MCU_UNI_CMD_CFG_SMESH = 0x10,
- 	MCU_UNI_CMD_RX_HDR_TRANS = 0x12,
- 	MCU_UNI_CMD_SER = 0x13,
- 	MCU_UNI_CMD_TWT = 0x14,
-@@ -1286,6 +1287,7 @@ enum {
- 	MCU_UNI_CMD_PER_STA_INFO = 0x6d,
- 	MCU_UNI_CMD_ALL_STA_INFO = 0x6e,
- 	MCU_UNI_CMD_ASSERT_DUMP = 0x6f,
-+	MCU_UNI_CMD_PTA_3WIRE_CTRL = 0x78,
- };
- 
- enum {
-diff --git a/mt7996/Makefile b/mt7996/Makefile
-index 7bb17f4..6643c7a 100644
---- a/mt7996/Makefile
-+++ b/mt7996/Makefile
-@@ -1,11 +1,12 @@
- # SPDX-License-Identifier: ISC
- EXTRA_CFLAGS += -DCONFIG_MT76_LEDS
- EXTRA_CFLAGS += -DCONFIG_MTK_DEBUG
-+EXTRA_CFLAGS += -DCONFIG_MTK_VENDOR
- 
- obj-$(CONFIG_MT7996E) += mt7996e.o
- 
- mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
--	     debugfs.o mmio.o
-+	     debugfs.o mmio.o vendor.o
- 
- mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o
- mt7996e-$(CONFIG_NL80211_TESTMODE) += testmode.o
-diff --git a/mt7996/init.c b/mt7996/init.c
-index f1d6817..5cc2e6f 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -378,6 +378,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
- 
- 	phy->slottime = 9;
- 	phy->beacon_rate = -1;
-+	phy->muru_onoff = OFDMA_UL | OFDMA_DL | MUMIMO_DL | MUMIMO_UL;
- 
- 	hw->sta_data_size = sizeof(struct mt7996_sta);
- 	hw->vif_data_size = sizeof(struct mt7996_vif);
-@@ -629,6 +630,10 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
- 	if (ret)
- 		goto error;
- 
-+#ifdef CONFIG_MTK_VENDOR
-+	mt7996_vendor_register(phy);
-+#endif
-+
- 	ret = mt76_register_phy(mphy, true, mt76_rates,
- 				ARRAY_SIZE(mt76_rates));
- 	if (ret)
-@@ -1446,6 +1451,10 @@ int mt7996_register_device(struct mt7996_dev *dev)
- 	dev->mt76.test_ops = &mt7996_testmode_ops;
- #endif
- 
-+#ifdef CONFIG_MTK_VENDOR
-+	mt7996_vendor_register(&dev->phy);
-+#endif
-+
- 	ret = mt76_register_device(&dev->mt76, true, mt76_rates,
- 				   ARRAY_SIZE(mt76_rates));
- 	if (ret)
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index c9f45ab..d55e5a7 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -679,6 +679,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
- 			if (ieee80211_has_a4(fc) && is_mesh && status->amsdu)
- 				*qos &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
- 		}
-+#ifdef CONFIG_MTK_VENDOR
-+		if (phy->amnt_ctrl.enable && !ieee80211_is_beacon(fc))
-+			mt7996_vendor_amnt_fill_rx(phy, skb);
-+#endif
- 	} else {
- 		status->flag |= RX_FLAG_8023;
- 		mt7996_wed_check_ppe(dev, &dev->mt76.q_rx[q], msta, skb,
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 61396a8..003bf3f 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -760,6 +760,10 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 	mt7996_mac_wtbl_update(dev, idx,
- 			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
- 
-+#ifdef CONFIG_MTK_VENDOR
-+	mt7996_vendor_amnt_sta_remove(mvif->phy, sta);
-+#endif
-+
- 	ret = mt7996_mcu_add_sta(dev, vif, sta, true, true);
- 	if (ret)
- 		return ret;
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 446fe1f..ae0c11a 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -1378,6 +1378,8 @@ static void
- mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- 			struct ieee80211_vif *vif, struct ieee80211_sta *sta)
- {
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_phy *phy = mvif->phy;
- 	struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
- 	struct sta_rec_muru *muru;
- 	struct tlv *tlv;
-@@ -1389,11 +1391,14 @@ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MURU, sizeof(*muru));
- 
- 	muru = (struct sta_rec_muru *)tlv;
--	muru->cfg.mimo_dl_en = vif->bss_conf.eht_mu_beamformer ||
--			       vif->bss_conf.he_mu_beamformer ||
--			       vif->bss_conf.vht_mu_beamformer ||
--			       vif->bss_conf.vht_mu_beamformee;
--	muru->cfg.ofdma_dl_en = true;
-+	muru->cfg.mimo_dl_en = (vif->bss_conf.eht_mu_beamformer ||
-+				vif->bss_conf.he_mu_beamformer ||
-+				vif->bss_conf.vht_mu_beamformer ||
-+				vif->bss_conf.vht_mu_beamformee) &&
-+			       !!(phy->muru_onoff & MUMIMO_DL);
-+	muru->cfg.mimo_ul_en = !!(phy->muru_onoff & MUMIMO_UL);
-+	muru->cfg.ofdma_dl_en = !!(phy->muru_onoff & OFDMA_DL);
-+	muru->cfg.ofdma_ul_en = !!(phy->muru_onoff & OFDMA_UL);
- 
- 	if (sta->deflink.vht_cap.vht_supported)
- 		muru->mimo_dl.vht_mu_bfee =
-@@ -4963,3 +4968,25 @@ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
- 	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SCS),
- 				 &req, sizeof(req), false);
- }
-+
-+#ifdef CONFIG_MTK_VENDOR
-+void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
-+{
-+	u8 mode, val;
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_phy *phy =  mvif->phy;
-+
-+	mode = FIELD_GET(RATE_CFG_MODE, *((u32 *)data));
-+	val = FIELD_GET(RATE_CFG_VAL, *((u32 *)data));
-+
-+	switch (mode) {
-+	case RATE_PARAM_AUTO_MU:
-+		if (val < 0 || val > 15) {
-+			printk("Wrong value! The value is between 0-15.\n");
-+			break;
-+		}
-+		phy->muru_onoff = val;
-+		break;
-+	}
-+}
-+#endif
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 5a60ccc..2e845e9 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -754,8 +754,20 @@ enum {
- 	RATE_PARAM_FIXED_MCS,
- 	RATE_PARAM_FIXED_GI = 11,
- 	RATE_PARAM_AUTO = 20,
-+#ifdef CONFIG_MTK_VENDOR
-+	RATE_PARAM_AUTO_MU = 32,
-+#endif
- };
- 
-+#define RATE_CFG_MODE	GENMASK(15, 8)
-+#define RATE_CFG_VAL	GENMASK(7, 0)
-+
-+/* MURU */
-+#define OFDMA_DL                       BIT(0)
-+#define OFDMA_UL                       BIT(1)
-+#define MUMIMO_DL                      BIT(2)
-+#define MUMIMO_UL                      BIT(3)
-+
- enum {
- 	BF_SOUNDING_ON = 1,
- 	BF_HW_EN_UPDATE = 17,
-@@ -833,6 +845,8 @@ mt7996_get_power_bound(struct mt7996_phy *phy, s8 txpower)
- 
- enum {
- 	UNI_BAND_CONFIG_RADIO_ENABLE,
-+	UNI_BAND_CONFIG_EDCCA_ENABLE = 0x05,
-+	UNI_BAND_CONFIG_EDCCA_THRESHOLD = 0x06,
- 	UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08,
- };
- 
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index b2a9381..402327d 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -258,6 +258,34 @@ struct mt7996_wed_rro_session_id {
- 	u16 id;
- };
- 
-+#ifdef CONFIG_MTK_VENDOR
-+#define MT7996_AIR_MONITOR_MAX_ENTRY	16
-+#define MT7996_AIR_MONITOR_MAX_GROUP	(MT7996_AIR_MONITOR_MAX_ENTRY >> 1)
-+
-+struct mt7996_air_monitor_group {
-+	bool enable;
-+	bool used[2];
-+};
-+
-+struct mt7996_air_monitor_entry {
-+	bool enable;
-+
-+	u8 group_idx;
-+	u8 group_used_idx;
-+	u8 muar_idx;
-+	u8 addr[ETH_ALEN];
-+	u32 last_seen;
-+	s8 rssi[4];
-+	struct ieee80211_sta *sta;
-+};
-+
-+struct mt7996_air_monitor_ctrl {
-+	u8 enable;
-+	struct mt7996_air_monitor_group group[MT7996_AIR_MONITOR_MAX_GROUP];
-+	struct mt7996_air_monitor_entry entry[MT7996_AIR_MONITOR_MAX_ENTRY];
-+};
-+#endif
-+
- struct mt7996_phy {
- 	struct mt76_phy *mt76;
- 	struct mt7996_dev *dev;
-@@ -300,6 +328,8 @@ struct mt7996_phy {
- 	bool sku_limit_en;
- 	bool sku_path_en;
- 
-+	u8 muru_onoff;
-+
- #ifdef CONFIG_NL80211_TESTMODE
- 	struct {
- 		u32 *reg_backup;
-@@ -314,6 +344,10 @@ struct mt7996_phy {
- 		u8 spe_idx;
- 	} test;
- #endif
-+#ifdef CONFIG_MTK_VENDOR
-+	spinlock_t amnt_lock;
-+	struct mt7996_air_monitor_ctrl amnt_ctrl;
-+#endif
- };
- 
- struct mt7996_dev {
-@@ -740,6 +774,25 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- 			 bool hif2, int *irq);
- u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id);
- 
-+#ifdef CONFIG_MTK_VENDOR
-+void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
-+void mt7996_vendor_register(struct mt7996_phy *phy);
-+void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb);
-+int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy,
-+				  struct ieee80211_sta *sta);
-+#endif
-+
-+int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
-+int mt7996_mcu_edcca_threshold_ctrl(struct mt7996_phy *phy, u8 *value, bool set);
-+
-+enum edcca_bw_id {
-+	EDCCA_BW_20 = 0,
-+	EDCCA_BW_40,
-+	EDCCA_BW_80,
-+	EDCCA_BW_160,
-+	EDCCA_MAX_BW_NUM,
-+};
-+
- #ifdef CONFIG_MTK_DEBUG
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
- int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index e56ddd8..5c54d02 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -59,4 +59,91 @@ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val)
- 	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
- 				 sizeof(req), true);
- }
-+
-+int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
-+	enum nl80211_band band = chandef->chan->band;
-+	struct {
-+		u8 band_idx;
-+		u8 _rsv[3];
-+
-+		__le16 tag;
-+		__le16 len;
-+		u8 enable;
-+		u8 std;
-+		u8 _rsv2[2];
-+	} __packed req = {
-+		.band_idx = phy->mt76->band_idx,
-+		.tag = cpu_to_le16(UNI_BAND_CONFIG_EDCCA_ENABLE),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.enable = enable,
-+		.std = EDCCA_DEFAULT,
-+	};
-+
-+	switch (dev->mt76.region) {
-+	case NL80211_DFS_JP:
-+		req.std = EDCCA_JAPAN;
-+		break;
-+	case NL80211_DFS_FCC:
-+		if (band == NL80211_BAND_6GHZ)
-+			req.std = EDCCA_FCC;
-+		break;
-+	case NL80211_DFS_ETSI:
-+		if (band == NL80211_BAND_6GHZ)
-+			req.std = EDCCA_ETSI;
-+		break;
-+	default:
-+		break;
-+	}
-+
-+	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
-+				 &req, sizeof(req), true);
-+}
-+
-+int mt7996_mcu_edcca_threshold_ctrl(struct mt7996_phy *phy, u8 *value, bool set)
-+{
-+	struct {
-+		u8 band_idx;
-+		u8 _rsv[3];
-+
-+		__le16 tag;
-+		__le16 len;
-+		u8 threshold[4];
-+		bool init;
-+	} __packed *res, req = {
-+		.band_idx = phy->mt76->band_idx,
-+		.tag = cpu_to_le16(UNI_BAND_CONFIG_EDCCA_THRESHOLD),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.init = false,
-+	};
-+	struct sk_buff *skb;
-+	int ret;
-+	int i;
-+
-+	for (i = 0; i < EDCCA_MAX_BW_NUM; i++)
-+		req.threshold[i] = value[i];
-+
-+	if (set)
-+		return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
-+					 &req, sizeof(req), true);
-+
-+	ret = mt76_mcu_send_and_get_msg(&phy->dev->mt76,
-+					MCU_WM_UNI_CMD_QUERY(BAND_CONFIG),
-+					&req, sizeof(req), true, &skb);
-+
-+	if (ret)
-+		return ret;
-+
-+	res = (void *)skb->data;
-+
-+	for (i = 0; i < EDCCA_MAX_BW_NUM; i++)
-+		value[i] = res->threshold[i];
-+
-+	dev_kfree_skb(skb);
-+
-+	return 0;
-+}
-+
- #endif
-diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
-index c30418c..36a58ad 100644
---- a/mt7996/mtk_mcu.h
-+++ b/mt7996/mtk_mcu.h
-@@ -106,6 +106,21 @@ enum txpower_event {
- 	UNI_TXPOWER_PHY_RATE_INFO = 5,
- };
- 
-+enum {
-+	EDCCA_CTRL_SET_EN = 0,
-+	EDCCA_CTRL_SET_THRES,
-+	EDCCA_CTRL_GET_EN,
-+	EDCCA_CTRL_GET_THRES,
-+	EDCCA_CTRL_NUM,
-+};
-+
-+enum {
-+	EDCCA_DEFAULT = 0,
-+	EDCCA_FCC = 1,
-+	EDCCA_ETSI = 2,
-+	EDCCA_JAPAN = 3
-+};
-+
- #endif
- 
- #endif
-diff --git a/mt7996/vendor.c b/mt7996/vendor.c
-new file mode 100644
-index 0000000..0d6fa77
---- /dev/null
-+++ b/mt7996/vendor.c
-@@ -0,0 +1,805 @@
-+// SPDX-License-Identifier: ISC
-+/*
-+ * Copyright (C) 2020, MediaTek Inc. All rights reserved.
-+ */
-+
-+#include <net/netlink.h>
-+
-+#include "mt7996.h"
-+#include "mcu.h"
-+#include "vendor.h"
-+#include "mtk_mcu.h"
-+
-+static const struct nla_policy
-+mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
-+	[MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
-+};
-+
-+static const struct nla_policy
-+amnt_ctrl_policy[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL] = {
-+	[MTK_VENDOR_ATTR_AMNT_CTRL_SET] = {.type = NLA_NESTED },
-+	[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP] = { .type = NLA_NESTED },
-+};
-+
-+static const struct nla_policy
-+amnt_set_policy[NUM_MTK_VENDOR_ATTRS_AMNT_SET] = {
-+	[MTK_VENDOR_ATTR_AMNT_SET_INDEX] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_AMNT_SET_MACADDR] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
-+};
-+
-+static const struct nla_policy
-+amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
-+	[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_AMNT_DUMP_LEN] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
-+};
-+
-+static struct nla_policy
-+bss_color_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL] = {
-+	[MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP] = { .type = NLA_U64 },
-+};
-+
-+static const struct nla_policy
-+edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_S8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
-+};
-+
-+static const struct nla_policy
-+edcca_dump_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP] = {
-+	[MTK_VENDOR_ATTR_EDCCA_DUMP_MODE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL] = { .type = NLA_U8 },
-+};
-+
-+static const struct nla_policy
-+three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
-+	[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
-+};
-+
-+static const struct nla_policy
-+ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
-+	[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
-+};
-+
-+static struct nla_policy
-+pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
-+	[MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_PP_BAND_IDX] = { .type = NLA_U8 },
-+};
-+
-+struct mt7996_amnt_data {
-+	u8 idx;
-+	u8 addr[ETH_ALEN];
-+	s8 rssi[4];
-+	u32 last_seen;
-+};
-+
-+static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
-+				 struct wireless_dev *wdev,
-+				 const void *data,
-+				 int data_len)
-+{
-+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_MU_CTRL];
-+	int err;
-+	u8 val8;
-+	u32 val32 = 0;
-+
-+	err = nla_parse(tb, MTK_VENDOR_ATTR_MU_CTRL_MAX, data, data_len,
-+			mu_ctrl_policy, NULL);
-+	if (err)
-+		return err;
-+
-+	if (tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF]) {
-+		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF]);
-+		val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_AUTO_MU) |
-+			 FIELD_PREP(RATE_CFG_VAL, val8);
-+		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-+							   mt7996_set_wireless_vif, &val32);
-+	}
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_vendor_mu_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
-+			   struct sk_buff *skb, const void *data, int data_len,
-+			   unsigned long *storage)
-+{
-+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	int len = 0;
-+
-+	if (*storage == 1)
-+		return -ENOENT;
-+	*storage = 1;
-+
-+	if (nla_put_u8(skb, MTK_VENDOR_ATTR_MU_CTRL_DUMP, phy->muru_onoff))
-+		return -ENOMEM;
-+	len += 1;
-+
-+	return len;
-+}
-+
-+void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb)
-+{
-+	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
-+	struct mt7996_air_monitor_ctrl *ctrl = &phy->amnt_ctrl;
-+	struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb);
-+	__le16 fc = hdr->frame_control;
-+	u8 addr[ETH_ALEN];
-+	int i;
-+
-+	if (!ieee80211_has_fromds(fc))
-+		ether_addr_copy(addr, hdr->addr2);
-+	else if (ieee80211_has_tods(fc))
-+		ether_addr_copy(addr, hdr->addr4);
-+	else
-+		ether_addr_copy(addr, hdr->addr3);
-+
-+	spin_lock_bh(&phy->amnt_lock);
-+	for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++) {
-+		struct mt7996_air_monitor_entry *entry;
-+
-+		if (ether_addr_equal(addr, ctrl->entry[i].addr)) {
-+			entry = &ctrl->entry[i];
-+			entry->rssi[0] = status->chain_signal[0];
-+			entry->rssi[1] = status->chain_signal[1];
-+			entry->rssi[2] = status->chain_signal[2];
-+			entry->rssi[3] = status->chain_signal[3];
-+			entry->last_seen = jiffies;
-+			break;
-+		}
-+	}
-+	spin_unlock_bh(&phy->amnt_lock);
-+}
-+
-+static int
-+mt7996_vendor_smesh_ctrl(struct mt7996_phy *phy, u8 write,
-+			 u8 enable, u8 *value)
-+{
-+#define UNI_CMD_SMESH_PARAM  0
-+	struct mt7996_dev *dev = phy->dev;
-+	struct smesh_param {
-+		__le16 tag;
-+		__le16 length;
-+
-+		u8 enable;
-+		bool a2;
-+		bool a1;
-+		bool data;
-+		bool mgnt;
-+		bool ctrl;
-+		u8 padding[2];
-+	} req = {
-+		.tag = cpu_to_le16(UNI_CMD_SMESH_PARAM),
-+		.length = cpu_to_le16(sizeof(req) - 4),
-+
-+		.enable = enable,
-+		.a2 = true,
-+		.a1 = true,
-+		.data = true,
-+		.mgnt = false,
-+		.ctrl = false,
-+	};
-+	struct smesh_param *res;
-+	struct sk_buff *skb;
-+	int ret = 0;
-+
-+	if (!value)
-+		return -EINVAL;
-+
-+	ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD(CFG_SMESH),
-+					&req, sizeof(req), !write, &skb);
-+
-+	if (ret || write)
-+		return ret;
-+
-+	res = (struct smesh_param *) skb->data;
-+
-+	*value = res->enable;
-+
-+	dev_kfree_skb(skb);
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_vendor_amnt_muar(struct mt7996_phy *phy, u8 muar_idx, u8 *addr)
-+{
-+#define UNI_CMD_MUAR_ENTRY  2
-+	struct mt7996_dev *dev = phy->dev;
-+	struct muar_entry {
-+		__le16 tag;
-+		__le16 length;
-+
-+		bool smesh;
-+		u8 hw_bss_index;
-+		u8 muar_idx;
-+		u8 entry_add;
-+		u8 mac_addr[6];
-+		u8 padding[2];
-+	} __packed req = {
-+		.tag = cpu_to_le16(UNI_CMD_MUAR_ENTRY),
-+		.length = cpu_to_le16(sizeof(req) - 4),
-+
-+		.smesh = true,
-+		.hw_bss_index = phy != &dev->phy,
-+		.muar_idx = muar_idx,
-+		.entry_add = 1,
-+	};
-+
-+	ether_addr_copy(req.mac_addr, addr);
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REPT_MUAR), &req,
-+				 sizeof(req), true);
-+}
-+
-+static int
-+mt7996_vendor_amnt_set_en(struct mt7996_phy *phy, u8 enable)
-+{
-+	u8 status;
-+	int ret;
-+
-+	ret = mt7996_vendor_smesh_ctrl(phy, 0, enable, &status);
-+	if (ret)
-+		return ret;
-+
-+	if (status == enable)
-+		return 0;
-+
-+	ret = mt7996_vendor_smesh_ctrl(phy, 1, enable, &status);
-+	if (ret)
-+		return ret;
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_vendor_amnt_set_addr(struct mt7996_phy *phy, u8 index, u8 *addr)
-+{
-+	struct mt7996_air_monitor_ctrl *amnt_ctrl = &phy->amnt_ctrl;
-+	struct mt7996_air_monitor_group *group;
-+	struct mt7996_air_monitor_entry *entry;
-+	int ret, i, j;
-+
-+	if (index >= MT7996_AIR_MONITOR_MAX_ENTRY)
-+		return -1;
-+
-+	spin_lock_bh(&phy->amnt_lock);
-+	entry = &amnt_ctrl->entry[index];
-+	if (!is_zero_ether_addr(addr)) {
-+		if (entry->enable == false) {
-+			for (i = 0; i < MT7996_AIR_MONITOR_MAX_GROUP; i++) {
-+				group = &(amnt_ctrl->group[i]);
-+				if (group->used[0] == false)
-+					j = 0;
-+				else if (group->used[1] == false)
-+					j = 1;
-+				else
-+					continue;
-+
-+				group->enable = true;
-+				group->used[j] = true;
-+				entry->enable = true;
-+				entry->group_idx = i;
-+				entry->group_used_idx = j;
-+				entry->muar_idx = 32 + 4 * i + 2 * j;
-+				break;
-+			}
-+		}
-+	} else {
-+		group = &(amnt_ctrl->group[entry->group_idx]);
-+
-+		group->used[entry->group_used_idx] = false;
-+		if (group->used[0] == false && group->used[1] == false)
-+			group->enable = false;
-+
-+		entry->enable = false;
-+	}
-+	ether_addr_copy(entry->addr, addr);
-+	amnt_ctrl->enable &= ~(1 << entry->group_idx);
-+	amnt_ctrl->enable |= entry->enable << entry->group_idx;
-+	spin_unlock_bh(&phy->amnt_lock);
-+
-+	ret = mt7996_vendor_amnt_muar(phy, entry->muar_idx, addr);
-+	if (ret)
-+		return ret;
-+
-+	return mt7996_vendor_amnt_set_en(phy, amnt_ctrl->enable);
-+}
-+
-+static int
-+mt7966_vendor_amnt_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
-+			const void *data, int data_len)
-+{
-+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
-+	struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_SET];
-+	u8 index = 0;
-+	u8 mac_addr[ETH_ALEN];
-+	int err;
-+
-+	err = nla_parse(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX, data, data_len,
-+			amnt_ctrl_policy, NULL);
-+	if (err)
-+		return err;
-+
-+	if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_SET])
-+		return -EINVAL;
-+
-+	err = nla_parse_nested(tb2, MTK_VENDOR_ATTR_AMNT_SET_MAX,
-+		tb1[MTK_VENDOR_ATTR_AMNT_CTRL_SET], amnt_set_policy, NULL);
-+
-+	if (!tb2[MTK_VENDOR_ATTR_AMNT_SET_INDEX] ||
-+		!tb2[MTK_VENDOR_ATTR_AMNT_SET_MACADDR])
-+		return -EINVAL;
-+
-+	index = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_SET_INDEX]);
-+	memcpy(mac_addr, nla_data(tb2[MTK_VENDOR_ATTR_AMNT_SET_MACADDR]), ETH_ALEN);
-+
-+	return mt7996_vendor_amnt_set_addr(phy, index, mac_addr);
-+}
-+
-+int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy,
-+				  struct ieee80211_sta *sta)
-+{
-+	u8 zero[ETH_ALEN] = {};
-+	int i;
-+
-+	if (!phy->amnt_ctrl.enable)
-+		return 0;
-+
-+	for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++)
-+		if (ether_addr_equal(sta->addr, phy->amnt_ctrl.entry[i].addr))
-+			return mt7996_vendor_amnt_set_addr(phy, i, zero);
-+	return 0;
-+}
-+
-+static int
-+mt7996_amnt_dump(struct mt7996_phy *phy, struct sk_buff *skb,
-+		 u8 amnt_idx, int *attrtype)
-+{
-+	struct mt7996_air_monitor_entry *entry;
-+	struct mt7996_amnt_data data;
-+	u32 last_seen = 0;
-+
-+	spin_lock_bh(&phy->amnt_lock);
-+	entry = &phy->amnt_ctrl.entry[amnt_idx];
-+	if (entry->enable == 0) {
-+		spin_unlock_bh(&phy->amnt_lock);
-+		return 0;
-+	}
-+
-+	last_seen = jiffies_to_msecs(jiffies - entry->last_seen);
-+	ether_addr_copy(data.addr, entry->addr);
-+	data.rssi[0] = entry->rssi[0];
-+	data.rssi[1] = entry->rssi[1];
-+	data.rssi[2] = entry->rssi[2];
-+	data.rssi[3] = entry->rssi[3];
-+	spin_unlock_bh(&phy->amnt_lock);
-+
-+	data.idx = amnt_idx;
-+	data.last_seen = last_seen;
-+
-+	nla_put(skb, (*attrtype)++, sizeof(struct mt7996_amnt_data), &data);
-+
-+	return 1;
-+}
-+
-+static int
-+mt7966_vendor_amnt_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
-+			     struct sk_buff *skb, const void *data, int data_len,
-+			     unsigned long *storage)
-+{
-+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
-+	struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP];
-+	void *a, *b;
-+	int err = 0, attrtype = 0, i, len = 0;
-+	u8 amnt_idx;
-+
-+	if (*storage == 1)
-+		return -ENOENT;
-+	*storage = 1;
-+
-+	err = nla_parse(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX, data, data_len,
-+			amnt_ctrl_policy, NULL);
-+	if (err)
-+		return err;
-+
-+	if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP])
-+		return -EINVAL;
-+
-+	err = nla_parse_nested(tb2, MTK_VENDOR_ATTR_AMNT_DUMP_MAX,
-+			       tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP],
-+			       amnt_dump_policy, NULL);
-+	if (err)
-+		return err;
-+
-+	if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX])
-+		return -EINVAL;
-+
-+	amnt_idx = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX]);
-+
-+	a = nla_nest_start(skb, MTK_VENDOR_ATTR_AMNT_CTRL_DUMP);
-+	b = nla_nest_start(skb, MTK_VENDOR_ATTR_AMNT_DUMP_RESULT);
-+
-+	if (amnt_idx != 0xff) {
-+		len += mt7996_amnt_dump(phy, skb, amnt_idx, &attrtype);
-+	} else {
-+		for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++)
-+			len += mt7996_amnt_dump(phy, skb, i, &attrtype);
-+	}
-+
-+	nla_nest_end(skb, b);
-+
-+	nla_put_u8(skb, MTK_VENDOR_ATTR_AMNT_DUMP_LEN, len);
-+
-+	nla_nest_end(skb, a);
-+
-+	return len + 1;
-+}
-+
-+static int
-+mt7996_vendor_bss_color_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
-+				  struct sk_buff *skb, const void *data, int data_len,
-+				  unsigned long *storage)
-+{
-+	struct ieee80211_vif *vif = wdev_to_ieee80211_vif(wdev);
-+	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
-+	int len = 0;
-+
-+	if (*storage == 1)
-+		return -ENOENT;
-+	*storage = 1;
-+
-+	if (nla_put_u64_64bit(skb, MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
-+			      ~bss_conf->used_color_bitmap, NL80211_ATTR_PAD))
-+		return -ENOMEM;
-+	len += 1;
-+
-+	return len;
-+}
-+
-+static int mt7996_vendor_edcca_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
-+				    const void *data, int data_len)
-+{
-+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL];
-+	int err;
-+	u8 edcca_mode;
-+	u8 edcca_value[EDCCA_MAX_BW_NUM];
-+
-+	err = nla_parse(tb, MTK_VENDOR_ATTR_EDCCA_CTRL_MAX, data, data_len,
-+			edcca_ctrl_policy, NULL);
-+	if (err)
-+		return err;
-+
-+	if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE])
-+		return -EINVAL;
-+
-+	edcca_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE]);
-+	if (edcca_mode == EDCCA_CTRL_SET_EN) {
-+		if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL])
-+			return -EINVAL;
-+
-+		edcca_value[0] =
-+			nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL]);
-+
-+		err = mt7996_mcu_edcca_enable(phy, !!edcca_value[0]);
-+		if (err)
-+			return err;
-+	} else if (edcca_mode == EDCCA_CTRL_SET_THRES) {
-+		if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] ||
-+		    !tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] ||
-+		    !tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] ||
-+		    !tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL]) {
-+			return -EINVAL;
-+		}
-+		edcca_value[EDCCA_BW_20] =
-+			nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL]);
-+		edcca_value[EDCCA_BW_40] =
-+			nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL]);
-+		edcca_value[EDCCA_BW_80] =
-+			nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL]);
-+		edcca_value[EDCCA_BW_160] =
-+			nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL]);
-+
-+		err = mt7996_mcu_edcca_threshold_ctrl(phy, edcca_value, true);
-+
-+		if (err)
-+			return err;
-+	} else {
-+		return -EINVAL;
-+	}
-+
-+	return 0;
-+}
-+
-+
-+static int
-+mt7996_vendor_edcca_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
-+			     struct sk_buff *skb, const void *data, int data_len,
-+			     unsigned long *storage)
-+{
-+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL];
-+	int err;
-+	u8 edcca_mode;
-+	u8 value[EDCCA_MAX_BW_NUM];
-+
-+	if (*storage == 1)
-+		return -ENOENT;
-+	*storage = 1;
-+
-+	err = nla_parse(tb, MTK_VENDOR_ATTR_EDCCA_CTRL_MAX, data, data_len,
-+			edcca_ctrl_policy, NULL);
-+	if (err)
-+		return err;
-+
-+	if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE])
-+		return -EINVAL;
-+
-+	edcca_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE]);
-+
-+	if (edcca_mode != EDCCA_CTRL_GET_THRES)
-+		return -EINVAL;
-+
-+	err = mt7996_mcu_edcca_threshold_ctrl(phy, value, false);
-+
-+	if (err)
-+		return err;
-+
-+	if (nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL, value[EDCCA_BW_20]) ||
-+	    nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL, value[EDCCA_BW_40]) ||
-+	    nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL, value[EDCCA_BW_80]) ||
-+	    nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL, value[EDCCA_BW_160]))
-+		return -ENOMEM;
-+
-+	return EDCCA_MAX_BW_NUM;
-+}
-+
-+static int mt7996_vendor_3wire_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
-+				    const void *data, int data_len)
-+{
-+#define UNI_3WIRE_EXT_EN	0
-+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+	struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL];
-+	struct {
-+		u8 __rsv1[4];
-+
-+		__le16 tag;
-+		__le16 len;
-+		u8 three_wire_mode;
-+	} __packed req = {
-+		.tag = cpu_to_le16(UNI_3WIRE_EXT_EN),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+	};
-+	int err;
-+
-+	err = nla_parse(tb, MTK_VENDOR_ATTR_3WIRE_CTRL_MAX, data, data_len,
-+			three_wire_ctrl_policy, NULL);
-+	if (err)
-+		return err;
-+
-+	if (!tb[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE])
-+		return -EINVAL;
-+
-+	req.three_wire_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE]);
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(PTA_3WIRE_CTRL), &req,
-+				 sizeof(req), false);
-+}
-+
-+static int mt7996_vendor_ibf_ctrl(struct wiphy *wiphy,
-+				  struct wireless_dev *wdev,
-+				  const void *data,
-+				  int data_len)
-+{
-+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct mt7996_dev *dev = phy->dev;
-+	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_IBF_CTRL];
-+	int err;
-+	u8 val;
-+
-+	err = nla_parse(tb, MTK_VENDOR_ATTR_IBF_CTRL_MAX, data, data_len,
-+			ibf_ctrl_policy, NULL);
-+	if (err)
-+		return err;
-+
-+	if (tb[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE]) {
-+		val = nla_get_u8(tb[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE]);
-+
-+		dev->ibf = !!val;
-+
-+		err = mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
-+		if (err)
-+			return err;
-+	}
-+	return 0;
-+}
-+
-+static int
-+mt7996_vendor_ibf_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
-+			    struct sk_buff *skb, const void *data, int data_len,
-+			    unsigned long *storage)
-+{
-+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct mt7996_dev *dev = phy->dev;
-+
-+	if (*storage == 1)
-+		return -ENOENT;
-+	*storage = 1;
-+
-+	if (nla_put_u8(skb, MTK_VENDOR_ATTR_IBF_DUMP_ENABLE, dev->ibf))
-+		return -ENOMEM;
-+
-+	return 1;
-+}
-+
-+static int mt7996_vendor_pp_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
-+				 const void *data, int data_len)
-+{
-+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_PP_CTRL];
-+	struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+	struct mt7996_phy *phy;
-+	struct mt76_phy *mphy;
-+	struct cfg80211_chan_def *chandef;
-+	int err;
-+	u8 val8, band_idx = 0;
-+
-+	err = nla_parse(tb, MTK_VENDOR_ATTR_PP_CTRL_MAX, data, data_len,
-+			pp_ctrl_policy, NULL);
-+
-+	if (tb[MTK_VENDOR_ATTR_PP_BAND_IDX]) {
-+		band_idx = nla_get_u8(tb[MTK_VENDOR_ATTR_PP_BAND_IDX]);
-+	}
-+
-+	if (!mt7996_band_valid(dev, band_idx))
-+		goto error;
-+
-+	mphy = dev->mt76.phys[band_idx];
-+	if (!mphy)
-+		goto error;
-+
-+	phy = (struct mt7996_phy *)mphy->priv;
-+	if (!phy)
-+		goto error;
-+
-+	chandef = &phy->chanctx->chandef;
-+	if (!chandef)
-+		goto error;
-+
-+	if (chandef->chan->band == NL80211_BAND_2GHZ)
-+		return 0;
-+
-+	if (tb[MTK_VENDOR_ATTR_PP_MODE]) {
-+		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_PP_MODE]);
-+		switch (val8) {
-+		case PP_DISABLE:
-+		case PP_FW_MODE:
-+			err = mt7996_mcu_set_pp_en(phy, val8, 0);
-+			break;
-+		case PP_USR_MODE:
-+			/* handled by add_chanctx */
-+			err = 0;
-+			break;
-+		default:
-+			err = -EINVAL;
-+		}
-+	}
-+
-+	return err;
-+error:
-+	dev_err(dev->mt76.dev, "Invalid band idx: %d\n", band_idx);
-+	return -EINVAL;
-+}
-+
-+static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
-+	{
-+		.info = {
-+			.vendor_id = MTK_NL80211_VENDOR_ID,
-+			.subcmd = MTK_NL80211_VENDOR_SUBCMD_MU_CTRL,
-+		},
-+		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+			 WIPHY_VENDOR_CMD_NEED_RUNNING,
-+		.doit = mt7996_vendor_mu_ctrl,
-+		.dumpit = mt7996_vendor_mu_ctrl_dump,
-+		.policy = mu_ctrl_policy,
-+		.maxattr = MTK_VENDOR_ATTR_MU_CTRL_MAX,
-+	},
-+	{
-+		.info = {
-+			.vendor_id = MTK_NL80211_VENDOR_ID,
-+			.subcmd = MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL,
-+		},
-+		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+			 WIPHY_VENDOR_CMD_NEED_RUNNING,
-+		.doit = mt7966_vendor_amnt_ctrl,
-+		.dumpit = mt7966_vendor_amnt_ctrl_dump,
-+		.policy = amnt_ctrl_policy,
-+		.maxattr = MTK_VENDOR_ATTR_AMNT_CTRL_MAX,
-+	},
-+	{
-+		.info = {
-+			.vendor_id = MTK_NL80211_VENDOR_ID,
-+			.subcmd = MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL,
-+		},
-+		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+			WIPHY_VENDOR_CMD_NEED_RUNNING,
-+		.dumpit = mt7996_vendor_bss_color_ctrl_dump,
-+		.policy = bss_color_ctrl_policy,
-+		.maxattr = MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX,
-+	},
-+	{
-+		.info = {
-+			.vendor_id = MTK_NL80211_VENDOR_ID,
-+			.subcmd = MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL,
-+		},
-+		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+			 WIPHY_VENDOR_CMD_NEED_RUNNING,
-+		.doit = mt7996_vendor_edcca_ctrl,
-+		.dumpit = mt7996_vendor_edcca_ctrl_dump,
-+		.policy = edcca_ctrl_policy,
-+		.maxattr = MTK_VENDOR_ATTR_EDCCA_CTRL_MAX,
-+	},
-+	{
-+		.info = {
-+			.vendor_id = MTK_NL80211_VENDOR_ID,
-+			.subcmd = MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL,
-+		},
-+		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+			 WIPHY_VENDOR_CMD_NEED_RUNNING,
-+		.doit = mt7996_vendor_3wire_ctrl,
-+		.policy = three_wire_ctrl_policy,
-+		.maxattr = MTK_VENDOR_ATTR_3WIRE_CTRL_MAX,
-+	},
-+	{
-+		.info = {
-+			.vendor_id = MTK_NL80211_VENDOR_ID,
-+			.subcmd = MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL,
-+		},
-+		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+			WIPHY_VENDOR_CMD_NEED_RUNNING,
-+		.doit = mt7996_vendor_ibf_ctrl,
-+		.dumpit = mt7996_vendor_ibf_ctrl_dump,
-+		.policy = ibf_ctrl_policy,
-+		.maxattr = MTK_VENDOR_ATTR_IBF_CTRL_MAX,
-+	},
-+	{
-+		.info = {
-+			.vendor_id = MTK_NL80211_VENDOR_ID,
-+			.subcmd = MTK_NL80211_VENDOR_SUBCMD_PP_CTRL,
-+		},
-+		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+			WIPHY_VENDOR_CMD_NEED_RUNNING,
-+		.doit = mt7996_vendor_pp_ctrl,
-+		.policy = pp_ctrl_policy,
-+		.maxattr = MTK_VENDOR_ATTR_PP_CTRL_MAX,
-+	},
-+};
-+
-+void mt7996_vendor_register(struct mt7996_phy *phy)
-+{
-+	phy->mt76->hw->wiphy->vendor_commands = mt7996_vendor_commands;
-+	phy->mt76->hw->wiphy->n_vendor_commands = ARRAY_SIZE(mt7996_vendor_commands);
-+
-+	spin_lock_init(&phy->amnt_lock);
-+}
-diff --git a/mt7996/vendor.h b/mt7996/vendor.h
-new file mode 100644
-index 0000000..8aaa18e
---- /dev/null
-+++ b/mt7996/vendor.h
-@@ -0,0 +1,153 @@
-+#ifndef __MT7996_VENDOR_H
-+#define __MT7996_VENDOR_H
-+
-+#define MTK_NL80211_VENDOR_ID	0x0ce7
-+
-+enum mtk_nl80211_vendor_subcmds {
-+	MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
-+	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
-+	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
-+	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
-+	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
-+	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
-+	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
-+};
-+
-+enum mtk_vendor_attr_edcca_ctrl {
-+	MTK_VENDOR_ATTR_EDCCA_THRESHOLD_INVALID = 0,
-+
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_MODE,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_edcca_dump {
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_UNSPEC = 0,
-+
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_MODE,
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP,
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_MAX =
-+		NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
-+};
-+
-+enum mtk_vendor_attr_3wire_ctrl {
-+	MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_3WIRE_CTRL_MODE,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL,
-+	MTK_VENDOR_ATTR_3WIRE_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_mu_ctrl {
-+	MTK_VENDOR_ATTR_MU_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
-+	MTK_VENDOR_ATTR_MU_CTRL_DUMP,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
-+	MTK_VENDOR_ATTR_MU_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_mnt_ctrl {
-+	MTK_VENDOR_ATTR_AMNT_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_AMNT_CTRL_SET,
-+	MTK_VENDOR_ATTR_AMNT_CTRL_DUMP,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_AMNT_CTRL,
-+	MTK_VENDOR_ATTR_AMNT_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_AMNT_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_mnt_set {
-+	MTK_VENDOR_ATTR_AMNT_SET_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_AMNT_SET_INDEX,
-+	MTK_VENDOR_ATTR_AMNT_SET_MACADDR,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_AMNT_SET,
-+	MTK_VENDOR_ATTR_AMNT_SET_MAX =
-+		NUM_MTK_VENDOR_ATTRS_AMNT_SET - 1
-+};
-+
-+enum mtk_vendor_attr_mnt_dump {
-+	MTK_VENDOR_ATTR_AMNT_DUMP_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_AMNT_DUMP_INDEX,
-+	MTK_VENDOR_ATTR_AMNT_DUMP_LEN,
-+	MTK_VENDOR_ATTR_AMNT_DUMP_RESULT,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
-+	MTK_VENDOR_ATTR_AMNT_DUMP_MAX =
-+		NUM_MTK_VENDOR_ATTRS_AMNT_DUMP - 1
-+};
-+
-+enum mtk_vendor_attr_bss_color_ctrl {
-+	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL,
-+	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_ibf_ctrl {
-+	MTK_VENDOR_ATTR_IBF_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_IBF_CTRL_ENABLE,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_IBF_CTRL,
-+	MTK_VENDOR_ATTR_IBF_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_IBF_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_ibf_dump {
-+	MTK_VENDOR_ATTR_IBF_DUMP_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_IBF_DUMP_ENABLE,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_IBF_DUMP,
-+	MTK_VENDOR_ATTR_IBF_DUMP_MAX =
-+		NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
-+};
-+
-+enum mtk_vendor_attr_pp_ctrl {
-+	MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_PP_MODE,
-+	MTK_VENDOR_ATTR_PP_BAND_IDX,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_PP_CTRL,
-+	MTK_VENDOR_ATTR_PP_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
-+};
-+
-+#endif
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0031-mtk-mt76-mt7996-add-txpower-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0031-mtk-mt76-mt7996-add-txpower-support.patch
new file mode 100644
index 0000000..9fbc955
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0031-mtk-mt76-mt7996-add-txpower-support.patch
@@ -0,0 +1,1047 @@
+From bfa596387e89377dec2121b5548b44b419463fb0 Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Fri, 24 Mar 2023 23:35:30 +0800
+Subject: [PATCH 031/199] mtk: mt76: mt7996: add txpower support
+
+Add single sku and default enable sku.
+
+mtk: wifi: mt76: mt7996: Porting wifi6 txpower fix to eagle
+
+Refactor txpower flow.
+1. Fix wrong bbp CR address
+2. Ignore RegDB power limit when we have single sku table. And dump more informaiton in debugfs.
+3. Refactor get_txpower ops flow, we only consider CCK and OFDM power value as maximum.
+4. Remove sku_disable due to SQC is over and default enable both sku tables.
+
+Fix wrong power value when user set limit close to path table limit.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ eeprom.c             |  54 ++++++-
+ mt76.h               |   9 ++
+ mt76_connac_mcu.c    |   2 +-
+ mt7996/eeprom.c      |  34 ++++
+ mt7996/eeprom.h      |  42 +++++
+ mt7996/init.c        |  16 +-
+ mt7996/main.c        |  15 ++
+ mt7996/mcu.c         |  69 +++++++-
+ mt7996/mcu.h         |   2 +
+ mt7996/mt7996.h      |   4 +
+ mt7996/mtk_debugfs.c | 363 +++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mtk_mcu.c     |  23 +++
+ mt7996/mtk_mcu.h     |  92 +++++++++++
+ mt7996/regs.h        |  27 ++--
+ 14 files changed, 725 insertions(+), 27 deletions(-)
+
+diff --git a/eeprom.c b/eeprom.c
+index a0047d79..11efe293 100644
+--- a/eeprom.c
++++ b/eeprom.c
+@@ -305,9 +305,10 @@ mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const __be32 *data,
+ static void
+ mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
+ 			     const __be32 *data, size_t len, s8 target_power,
+-			     s8 nss_delta, s8 *max_power)
++			     s8 nss_delta)
+ {
+ 	int i, cur;
++	s8 max_power = -128;
+ 
+ 	if (!data)
+ 		return;
+@@ -319,7 +320,7 @@ mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
+ 			break;
+ 
+ 		mt76_apply_array_limit(pwr + pwr_len * i, pwr_len, data + 1,
+-				       target_power, nss_delta, max_power);
++				       target_power, nss_delta, &max_power);
+ 		if (--cur > 0)
+ 			continue;
+ 
+@@ -335,6 +336,7 @@ mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
+ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
+ 			      struct ieee80211_channel *chan,
+ 			      struct mt76_power_limits *dest,
++			      struct mt76_power_path_limits *dest_path,
+ 			      s8 target_power)
+ {
+ 	struct mt76_dev *dev = phy->dev;
+@@ -342,16 +344,20 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
+ 	const __be32 *val;
+ 	char name[16];
+ 	u32 mcs_rates = dev->drv->mcs_rates;
+-	u32 ru_rates = ARRAY_SIZE(dest->ru[0]);
+ 	char band;
+ 	size_t len;
+-	s8 max_power = 0;
++	s8 max_power = -127;
++	s8 max_power_backoff = -127;
+ 	s8 txs_delta;
++	int n_chains = hweight16(phy->chainmask);
++	s8 target_power_combine = target_power + mt76_tx_power_nss_delta(n_chains);
+ 
+ 	if (!mcs_rates)
+-		mcs_rates = 10;
++		mcs_rates = 12;
+ 
+ 	memset(dest, target_power, sizeof(*dest));
++	if (dest_path != NULL)
++		memset(dest_path, 0, sizeof(*dest_path));
+ 
+ 	if (!IS_ENABLED(CONFIG_OF))
+ 		return target_power;
+@@ -397,12 +403,44 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
+ 	val = mt76_get_of_array(np, "rates-mcs", &len, mcs_rates + 1);
+ 	mt76_apply_multi_array_limit(dest->mcs[0], ARRAY_SIZE(dest->mcs[0]),
+ 				     ARRAY_SIZE(dest->mcs), val, len,
+-				     target_power, txs_delta, &max_power);
++				     target_power, txs_delta);
+ 
+-	val = mt76_get_of_array(np, "rates-ru", &len, ru_rates + 1);
++	val = mt76_get_of_array(np, "rates-ru", &len, ARRAY_SIZE(dest->ru[0]) + 1);
+ 	mt76_apply_multi_array_limit(dest->ru[0], ARRAY_SIZE(dest->ru[0]),
+ 				     ARRAY_SIZE(dest->ru), val, len,
+-				     target_power, txs_delta, &max_power);
++				     target_power, txs_delta);
++
++	val = mt76_get_of_array(np, "rates-eht", &len, ARRAY_SIZE(dest->eht[0]) + 1);
++	mt76_apply_multi_array_limit(dest->eht[0], ARRAY_SIZE(dest->eht[0]),
++				     ARRAY_SIZE(dest->eht), val, len,
++				     target_power, txs_delta);
++
++	if (dest_path == NULL)
++		return max_power;
++
++	max_power_backoff = max_power;
++
++	val = mt76_get_of_array(np, "paths-cck", &len, ARRAY_SIZE(dest_path->cck));
++	mt76_apply_array_limit(dest_path->cck, ARRAY_SIZE(dest_path->cck), val,
++			       target_power_combine, txs_delta, &max_power_backoff);
++
++	val = mt76_get_of_array(np, "paths-ofdm", &len, ARRAY_SIZE(dest_path->ofdm));
++	mt76_apply_array_limit(dest_path->ofdm, ARRAY_SIZE(dest_path->ofdm), val,
++			       target_power_combine, txs_delta, &max_power_backoff);
++
++	val = mt76_get_of_array(np, "paths-ofdm-bf", &len, ARRAY_SIZE(dest_path->ofdm_bf));
++	mt76_apply_array_limit(dest_path->ofdm_bf, ARRAY_SIZE(dest_path->ofdm_bf), val,
++			       target_power_combine, txs_delta, &max_power_backoff);
++
++	val = mt76_get_of_array(np, "paths-ru", &len, ARRAY_SIZE(dest_path->ru[0]) + 1);
++	mt76_apply_multi_array_limit(dest_path->ru[0], ARRAY_SIZE(dest_path->ru[0]),
++				     ARRAY_SIZE(dest_path->ru), val, len,
++				     target_power_combine, txs_delta);
++
++	val = mt76_get_of_array(np, "paths-ru-bf", &len, ARRAY_SIZE(dest_path->ru_bf[0]) + 1);
++	mt76_apply_multi_array_limit(dest_path->ru_bf[0], ARRAY_SIZE(dest_path->ru_bf[0]),
++				     ARRAY_SIZE(dest_path->ru_bf), val, len,
++				     target_power_combine, txs_delta);
+ 
+ 	return max_power;
+ }
+diff --git a/mt76.h b/mt76.h
+index 362243f6..2236bc6b 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -1064,6 +1064,14 @@ struct mt76_power_limits {
+ 	s8 eht[16][16];
+ };
+ 
++struct mt76_power_path_limits {
++	s8 cck[5];
++	s8 ofdm[5];
++	s8 ofdm_bf[4];
++	s8 ru[16][15];
++	s8 ru_bf[16][15];
++};
++
+ struct mt76_ethtool_worker_info {
+ 	u64 *data;
+ 	int idx;
+@@ -1678,6 +1686,7 @@ mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan);
+ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
+ 			      struct ieee80211_channel *chan,
+ 			      struct mt76_power_limits *dest,
++			      struct mt76_power_path_limits *dest_path,
+ 			      s8 target_power);
+ 
+ static inline bool mt76_queue_is_rx(struct mt76_dev *dev, struct mt76_queue *q)
+diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
+index a1deeacd..33cbf4c2 100644
+--- a/mt76_connac_mcu.c
++++ b/mt76_connac_mcu.c
+@@ -2158,7 +2158,7 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
+ 			sar_power = mt76_get_sar_power(phy, &chan, reg_power);
+ 
+ 			mt76_get_rate_power_limits(phy, &chan, limits,
+-						   sar_power);
++						   NULL, sar_power);
+ 
+ 			tx_power_tlv.last_msg = ch_list[idx] == last_ch;
+ 			sku_tlbv.channel = ch_list[idx];
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 454df971..17485bfc 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -408,3 +408,37 @@ s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band)
+ 
+ 	return val & MT_EE_RATE_DELTA_SIGN ? delta : -delta;
+ }
++
++const u8 mt7996_sku_group_len[] = {
++	[SKU_CCK] = 4,
++	[SKU_OFDM] = 8,
++	[SKU_HT20] = 8,
++	[SKU_HT40] = 9,
++	[SKU_VHT20] = 12,
++	[SKU_VHT40] = 12,
++	[SKU_VHT80] = 12,
++	[SKU_VHT160] = 12,
++	[SKU_HE26] = 12,
++	[SKU_HE52] = 12,
++	[SKU_HE106] = 12,
++	[SKU_HE242] = 12,
++	[SKU_HE484] = 12,
++	[SKU_HE996] = 12,
++	[SKU_HE2x996] = 12,
++	[SKU_EHT26] = 16,
++	[SKU_EHT52] = 16,
++	[SKU_EHT106] = 16,
++	[SKU_EHT242] = 16,
++	[SKU_EHT484] = 16,
++	[SKU_EHT996] = 16,
++	[SKU_EHT2x996] = 16,
++	[SKU_EHT4x996] = 16,
++	[SKU_EHT26_52] = 16,
++	[SKU_EHT26_106] = 16,
++	[SKU_EHT484_242] = 16,
++	[SKU_EHT996_484] = 16,
++	[SKU_EHT996_484_242] = 16,
++	[SKU_EHT2x996_484] = 16,
++	[SKU_EHT3x996] = 16,
++	[SKU_EHT3x996_484] = 16,
++};
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index 58179c0c..b19ff068 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -125,4 +125,46 @@ mt7996_get_channel_group_6g(int channel)
+ 	return DIV_ROUND_UP(channel - 29, 32);
+ }
+ 
++enum mt7996_sku_rate_group {
++	SKU_CCK,
++	SKU_OFDM,
++
++	SKU_HT20,
++	SKU_HT40,
++
++	SKU_VHT20,
++	SKU_VHT40,
++	SKU_VHT80,
++	SKU_VHT160,
++
++	SKU_HE26,
++	SKU_HE52,
++	SKU_HE106,
++	SKU_HE242,
++	SKU_HE484,
++	SKU_HE996,
++	SKU_HE2x996,
++
++	SKU_EHT26,
++	SKU_EHT52,
++	SKU_EHT106,
++	SKU_EHT242,
++	SKU_EHT484,
++	SKU_EHT996,
++	SKU_EHT2x996,
++	SKU_EHT4x996,
++	SKU_EHT26_52,
++	SKU_EHT26_106,
++	SKU_EHT484_242,
++	SKU_EHT996_484,
++	SKU_EHT996_484_242,
++	SKU_EHT2x996_484,
++	SKU_EHT3x996,
++	SKU_EHT3x996_484,
++
++	MAX_SKU_RATE_GROUP_NUM,
++};
++
++extern const u8 mt7996_sku_group_len[MAX_SKU_RATE_GROUP_NUM];
++
+ #endif
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 7e813960..3b816070 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -297,7 +297,12 @@ static void __mt7996_init_txpower(struct mt7996_phy *phy,
+ 	int nss_delta = mt76_tx_power_nss_delta(nss);
+ 	int pwr_delta = mt7996_eeprom_get_power_delta(dev, sband->band);
+ 	struct mt76_power_limits limits;
++	struct mt76_power_path_limits limits_path;
++	struct device_node *np;
+ 
++	phy->sku_limit_en = true;
++	phy->sku_path_en = true;
++	np = mt76_find_power_limits_node(&dev->mt76);
+ 	for (i = 0; i < sband->n_channels; i++) {
+ 		struct ieee80211_channel *chan = &sband->channels[i];
+ 		int target_power = mt7996_eeprom_get_target_power(dev, chan);
+@@ -305,11 +310,18 @@ static void __mt7996_init_txpower(struct mt7996_phy *phy,
+ 		target_power += pwr_delta;
+ 		target_power = mt76_get_rate_power_limits(phy->mt76, chan,
+ 							  &limits,
++							  &limits_path,
+ 							  target_power);
++		if (!limits_path.ofdm[0])
++			phy->sku_path_en = false;
++
+ 		target_power += nss_delta;
+ 		target_power = DIV_ROUND_UP(target_power, 2);
+-		chan->max_power = min_t(int, chan->max_reg_power,
+-					target_power);
++		if (!np)
++			chan->max_power = min_t(int, chan->max_reg_power,
++						target_power);
++		else
++			chan->max_power = target_power;
+ 		chan->orig_mpwr = target_power;
+ 	}
+ }
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 22acd04e..5a6ba6a6 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -85,6 +85,21 @@ int mt7996_run(struct ieee80211_hw *hw)
+ 	if (ret)
+ 		goto out;
+ 
++#ifdef CONFIG_MTK_DEBUG
++	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
++						dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
++
++	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
++						dev->dbg.sku_disable ? 0 : phy->sku_path_en);
++#else
++	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
++						phy->sku_limit_en);
++	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
++						phy->sku_path_en);
++#endif
++	if (ret)
++		goto out;
++
+ 	set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+ 
+ 	ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 8f725307..3b4c45bb 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -4668,9 +4668,31 @@ int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id)
+ 				 sizeof(req), true);
+ }
+ 
++static void
++mt7996_update_max_txpower_cur(struct mt7996_phy *phy, int tx_power)
++{
++	struct mt76_phy *mphy = phy->mt76;
++	struct ieee80211_channel *chan = mphy->main_chan;
++	int e2p_power_limit = 0;
++
++	if (chan == NULL) {
++		mphy->txpower_cur = tx_power;
++		return;
++	}
++
++	e2p_power_limit = mt7996_eeprom_get_target_power(phy->dev, chan);
++	e2p_power_limit += mt7996_eeprom_get_power_delta(phy->dev, chan->band);
++
++	if (phy->sku_limit_en)
++		mphy->txpower_cur = min_t(int, e2p_power_limit, tx_power);
++	else
++		mphy->txpower_cur = e2p_power_limit;
++}
++
+ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
+ {
+ #define TX_POWER_LIMIT_TABLE_RATE	0
++#define TX_POWER_LIMIT_TABLE_PATH	1
+ 	struct mt7996_dev *dev = phy->dev;
+ 	struct mt76_phy *mphy = phy->mt76;
+ 	struct ieee80211_hw *hw = mphy->hw;
+@@ -4690,13 +4712,22 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
+ 		.band_idx = phy->mt76->band_idx,
+ 	};
+ 	struct mt76_power_limits la = {};
++	struct mt76_power_path_limits la_path = {};
+ 	struct sk_buff *skb;
+-	int i, tx_power;
++	int i, ret, txpower_limit;
++
++	if (hw->conf.power_level == INT_MIN)
++		hw->conf.power_level = 127;
++	txpower_limit = mt7996_get_power_bound(phy, hw->conf.power_level);
+ 
+-	tx_power = mt7996_get_power_bound(phy, hw->conf.power_level);
+-	tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
+-					      &la, tx_power);
+-	mphy->txpower_cur = tx_power;
++	if (phy->sku_limit_en) {
++		txpower_limit = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
++							   &la, &la_path, txpower_limit);
++		mt7996_update_max_txpower_cur(phy, txpower_limit);
++	} else {
++		mt7996_update_max_txpower_cur(phy, txpower_limit);
++		return 0;
++	}
+ 
+ 	skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
+ 				 sizeof(req) + MT7996_SKU_PATH_NUM);
+@@ -4726,6 +4757,34 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
+ 	/* padding */
+ 	skb_put_zero(skb, MT7996_SKU_PATH_NUM - MT7996_SKU_RATE_NUM);
+ 
++	ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
++				    MCU_WM_UNI_CMD(TXPOWER), true);
++	if (ret)
++		return ret;
++
++	/* only set per-path power table when it's configured */
++	if (!phy->sku_path_en)
++		return 0;
++
++	skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
++				 sizeof(req) + MT7996_SKU_PATH_NUM);
++	if (!skb)
++		return -ENOMEM;
++	req.power_limit_type = TX_POWER_LIMIT_TABLE_PATH;
++
++	skb_put_data(skb, &req, sizeof(req));
++	skb_put_data(skb, &la_path.cck, sizeof(la_path.cck));
++	skb_put_data(skb, &la_path.ofdm, sizeof(la_path.ofdm));
++	skb_put_data(skb, &la_path.ofdm_bf, sizeof(la_path.ofdm_bf));
++
++	for (i = 0; i < 32; i++) {
++		bool bf = i % 2;
++		u8 idx = i / 2;
++		s8 *buf = bf ? la_path.ru_bf[idx] : la_path.ru[idx];
++
++		skb_put_data(skb, buf, sizeof(la_path.ru[0]));
++	}
++
+ 	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ 				     MCU_WM_UNI_CMD(TXPOWER), true);
+ }
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index f889cb8b..1ed05d7e 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -911,6 +911,7 @@ struct tx_power_ctrl {
+ 		bool ate_mode_enable;
+ 		bool percentage_ctrl_enable;
+ 		bool bf_backoff_enable;
++		u8 show_info_category;
+ 		u8 power_drop_level;
+ 	};
+ 	u8 band_idx;
+@@ -924,6 +925,7 @@ enum {
+ 	UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL = 3,
+ 	UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL = 4,
+ 	UNI_TXPOWER_ATE_MODE_CTRL = 6,
++	UNI_TXPOWER_SHOW_INFO = 7,
+ };
+ 
+ enum {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index d5050d6d..ff7edcd8 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -295,6 +295,9 @@ struct mt7996_phy {
+ 
+ 	struct mt7996_scs_ctrl scs_ctrl;
+ 
++	bool sku_limit_en;
++	bool sku_path_en;
++
+ #ifdef CONFIG_NL80211_TESTMODE
+ 	struct {
+ 		u32 *reg_backup;
+@@ -616,6 +619,7 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy);
+ #ifdef CONFIG_NL80211_TESTMODE
+ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ #endif
++int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
+ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
+ void mt7996_mcu_scs_sta_poll(struct work_struct *work);
+ 
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 8c576bac..3558641c 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2439,6 +2439,364 @@ mt7996_scs_enable_set(void *data, u64 val)
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_scs_enable, NULL,
+ 			 mt7996_scs_enable_set, "%lld\n");
+ 
++static int
++mt7996_txpower_level_set(void *data, u64 val)
++{
++	struct mt7996_phy *phy = data;
++	int ret;
++
++	if (val > 100)
++		return -EINVAL;
++
++	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_PERCENTAGE_CTRL, !!val);
++	if (ret)
++		return ret;
++
++	return mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_PERCENTAGE_DROP_CTRL, val);
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_txpower_level, NULL,
++			 mt7996_txpower_level_set, "%lld\n");
++
++static ssize_t
++mt7996_get_txpower_info(struct file *file, char __user *user_buf,
++			size_t count, loff_t *ppos)
++{
++	struct mt7996_phy *phy = file->private_data;
++	struct mt7996_mcu_txpower_event *event;
++	struct txpower_basic_info *basic_info;
++	struct device_node *np;
++	static const size_t size = 2048;
++	int len = 0;
++	ssize_t ret;
++	char *buf;
++
++	buf = kzalloc(size, GFP_KERNEL);
++	event = kzalloc(sizeof(*event), GFP_KERNEL);
++	if (!buf || !event) {
++		ret = -ENOMEM;
++		goto out;
++	}
++
++	ret = mt7996_mcu_get_tx_power_info(phy, BASIC_INFO, event);
++	if (ret ||
++	    le32_to_cpu(event->basic_info.category) != UNI_TXPOWER_BASIC_INFO)
++		goto out;
++
++	basic_info = &event->basic_info;
++
++	len += scnprintf(buf + len, size - len,
++			 "======================== BASIC INFO ========================\n");
++	len += scnprintf(buf + len, size - len, "    Band Index: %d, Channel Band: %d\n",
++			 basic_info->band_idx, basic_info->band);
++	len += scnprintf(buf + len, size - len, "    PA Type: %s\n",
++			 basic_info->is_epa ? "ePA" : "iPA");
++	len += scnprintf(buf + len, size - len, "    LNA Type: %s\n",
++			 basic_info->is_elna ? "eLNA" : "iLNA");
++
++	len += scnprintf(buf + len, size - len,
++			 "------------------------------------------------------------\n");
++	len += scnprintf(buf + len, size - len, "    SKU: %s\n",
++			 basic_info->sku_enable ? "enable" : "disable");
++	len += scnprintf(buf + len, size - len, "    Percentage Control: %s\n",
++			 basic_info->percentage_ctrl_enable ? "enable" : "disable");
++	len += scnprintf(buf + len, size - len, "    Power Drop: %d [dBm]\n",
++			 basic_info->power_drop_level >> 1);
++	len += scnprintf(buf + len, size - len, "    Backoff: %s\n",
++			 basic_info->bf_backoff_enable ? "enable" : "disable");
++	len += scnprintf(buf + len, size - len, "    TX Front-end Loss:  %d, %d, %d, %d\n",
++			 basic_info->front_end_loss_tx[0], basic_info->front_end_loss_tx[1],
++			 basic_info->front_end_loss_tx[2], basic_info->front_end_loss_tx[3]);
++	len += scnprintf(buf + len, size - len, "    RX Front-end Loss:  %d, %d, %d, %d\n",
++			 basic_info->front_end_loss_rx[0], basic_info->front_end_loss_rx[1],
++			 basic_info->front_end_loss_rx[2], basic_info->front_end_loss_rx[3]);
++	len += scnprintf(buf + len, size - len,
++			 "    MU TX Power Mode:  %s\n",
++			 basic_info->mu_tx_power_manual_enable ? "manual" : "auto");
++	len += scnprintf(buf + len, size - len,
++			 "    MU TX Power (Auto / Manual): %d / %d [0.5 dBm]\n",
++			 basic_info->mu_tx_power_auto, basic_info->mu_tx_power_manual);
++	len += scnprintf(buf + len, size - len,
++			 "    Thermal Compensation:  %s\n",
++			 basic_info->thermal_compensate_enable ? "enable" : "disable");
++	len += scnprintf(buf + len, size - len,
++			 "    Theraml Compensation Value: %d\n",
++			 basic_info->thermal_compensate_value);
++	np = mt76_find_power_limits_node(phy->mt76->dev);
++	len += scnprintf(buf + len, size - len,
++			 "    RegDB:  %s\n",
++			 !np ? "enable" : "disable");
++	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
++
++out:
++	kfree(buf);
++	kfree(event);
++	return ret;
++}
++
++static const struct file_operations mt7996_txpower_info_fops = {
++	.read = mt7996_get_txpower_info,
++	.open = simple_open,
++	.owner = THIS_MODULE,
++	.llseek = default_llseek,
++};
++
++#define mt7996_txpower_puts(rate, _len)							\
++({											\
++	len += scnprintf(buf + len, size - len, "%-*s:", _len, #rate " (TMAC)");	\
++	for (i = 0; i < mt7996_sku_group_len[SKU_##rate]; i++, offs++)			\
++		len += scnprintf(buf + len, size - len, " %6d",				\
++				 event->phy_rate_info.frame_power[offs][band_idx]);	\
++	len += scnprintf(buf + len, size - len, "\n");					\
++})
++
++static ssize_t
++mt7996_get_txpower_sku(struct file *file, char __user *user_buf,
++		       size_t count, loff_t *ppos)
++{
++	struct mt7996_phy *phy = file->private_data;
++	struct mt7996_dev *dev = phy->dev;
++	struct mt7996_mcu_txpower_event *event;
++	struct ieee80211_channel *chan = phy->mt76->chandef.chan;
++	struct ieee80211_supported_band sband;
++	u8 band_idx = phy->mt76->band_idx;
++	static const size_t size = 5120;
++	int i, offs = 0, len = 0;
++	u32 target_power = 0;
++	int n_chains = hweight16(phy->mt76->chainmask);
++	int nss_delta = mt76_tx_power_nss_delta(n_chains);
++	int pwr_delta;
++	ssize_t ret;
++	char *buf;
++	u32 reg;
++
++	buf = kzalloc(size, GFP_KERNEL);
++	event = kzalloc(sizeof(*event), GFP_KERNEL);
++	if (!buf || !event) {
++		ret = -ENOMEM;
++		goto out;
++	}
++
++	ret = mt7996_mcu_get_tx_power_info(phy, PHY_RATE_INFO, event);
++	if (ret ||
++	    le32_to_cpu(event->phy_rate_info.category) != UNI_TXPOWER_PHY_RATE_INFO)
++		goto out;
++
++	len += scnprintf(buf + len, size - len,
++			 "\nPhy %d TX Power Table (Channel %d)\n",
++			 band_idx, phy->mt76->chandef.chan->hw_value);
++	len += scnprintf(buf + len, size - len, "%-21s  %6s %6s %6s %6s\n",
++			 " ", "1m", "2m", "5m", "11m");
++	mt7996_txpower_puts(CCK, 21);
++
++	len += scnprintf(buf + len, size - len,
++			 "%-21s  %6s %6s %6s %6s %6s %6s %6s %6s\n",
++			 " ", "6m", "9m", "12m", "18m", "24m", "36m", "48m",
++			 "54m");
++	mt7996_txpower_puts(OFDM, 21);
++
++	len += scnprintf(buf + len, size - len,
++			 "%-21s  %6s %6s %6s %6s %6s %6s %6s %6s\n",
++			 " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4",
++			 "mcs5", "mcs6", "mcs7");
++	mt7996_txpower_puts(HT20, 21);
++
++	len += scnprintf(buf + len, size - len,
++			 "%-21s  %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
++			 " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
++			 "mcs6", "mcs7", "mcs32");
++	mt7996_txpower_puts(HT40, 21);
++
++	len += scnprintf(buf + len, size - len,
++			 "%-21s  %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
++			 " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
++			 "mcs6", "mcs7", "mcs8", "mcs9", "mcs10", "mcs11");
++	mt7996_txpower_puts(VHT20, 21);
++	mt7996_txpower_puts(VHT40, 21);
++	mt7996_txpower_puts(VHT80, 21);
++	mt7996_txpower_puts(VHT160, 21);
++	mt7996_txpower_puts(HE26, 21);
++	mt7996_txpower_puts(HE52, 21);
++	mt7996_txpower_puts(HE106, 21);
++	len += scnprintf(buf + len, size - len, "BW20/");
++	mt7996_txpower_puts(HE242, 16);
++	len += scnprintf(buf + len, size - len, "BW40/");
++	mt7996_txpower_puts(HE484, 16);
++	len += scnprintf(buf + len, size - len, "BW80/");
++	mt7996_txpower_puts(HE996, 16);
++	len += scnprintf(buf + len, size - len, "BW160/");
++	mt7996_txpower_puts(HE2x996, 15);
++
++	len += scnprintf(buf + len, size - len,
++			 "%-21s  %6s %6s %6s %6s %6s %6s %6s %6s ",
++			 " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5", "mcs6", "mcs7");
++	len += scnprintf(buf + len, size - len,
++			 "%6s %6s %6s %6s %6s %6s %6s %6s\n",
++			 "mcs8", "mcs9", "mcs10", "mcs11", "mcs12", "mcs13", "mcs14", "mcs15");
++	mt7996_txpower_puts(EHT26, 21);
++	mt7996_txpower_puts(EHT52, 21);
++	mt7996_txpower_puts(EHT106, 21);
++	len += scnprintf(buf + len, size - len, "BW20/");
++	mt7996_txpower_puts(EHT242, 16);
++	len += scnprintf(buf + len, size - len, "BW40/");
++	mt7996_txpower_puts(EHT484, 16);
++	len += scnprintf(buf + len, size - len, "BW80/");
++	mt7996_txpower_puts(EHT996, 16);
++	len += scnprintf(buf + len, size - len, "BW160/");
++	mt7996_txpower_puts(EHT2x996, 15);
++	len += scnprintf(buf + len, size - len, "BW320/");
++	mt7996_txpower_puts(EHT4x996, 15);
++	mt7996_txpower_puts(EHT26_52, 21);
++	mt7996_txpower_puts(EHT26_106, 21);
++	mt7996_txpower_puts(EHT484_242, 21);
++	mt7996_txpower_puts(EHT996_484, 21);
++	mt7996_txpower_puts(EHT996_484_242, 21);
++	mt7996_txpower_puts(EHT2x996_484, 21);
++	mt7996_txpower_puts(EHT3x996, 21);
++	mt7996_txpower_puts(EHT3x996_484, 21);
++
++	len += scnprintf(buf + len, size - len, "\nePA Gain: %d\n",
++			 event->phy_rate_info.epa_gain);
++	len += scnprintf(buf + len, size - len, "Max Power Bound: %d\n",
++			 event->phy_rate_info.max_power_bound);
++	len += scnprintf(buf + len, size - len, "Min Power Bound: %d\n",
++			 event->phy_rate_info.min_power_bound);
++
++	reg = MT_WF_PHYDFE_TSSI_TXCTRL01(band_idx);
++	len += scnprintf(buf + len, size - len,
++			 "\nBBP TX Power (target power from TMAC)  : %6ld [0.5 dBm]\n",
++			 mt76_get_field(dev, reg, MT_WF_PHYDFE_TSSI_TXCTRL_POWER_TMAC));
++	len += scnprintf(buf + len, size - len,
++			 "RegDB maximum power:\t%d [dBm]\n",
++			 chan->max_reg_power);
++
++	if (chan->band == NL80211_BAND_2GHZ)
++		sband = phy->mt76->sband_2g.sband;
++	else if (chan->band == NL80211_BAND_5GHZ)
++		sband = phy->mt76->sband_5g.sband;
++	else if (chan->band == NL80211_BAND_6GHZ)
++		sband = phy->mt76->sband_6g.sband;
++
++	pwr_delta = mt7996_eeprom_get_power_delta(dev, sband.band);
++
++	target_power = max_t(u32, target_power, mt7996_eeprom_get_target_power(dev, chan));
++	target_power += pwr_delta + nss_delta;
++	target_power = DIV_ROUND_UP(target_power, 2);
++	len += scnprintf(buf + len, size - len,
++			 "eeprom maximum power:\t%d [dBm]\n",
++			 target_power);
++
++	len += scnprintf(buf + len, size - len,
++			 "nss_delta:\t%d [0.5 dBm]\n",
++			 nss_delta);
++
++	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
++
++out:
++	kfree(buf);
++	kfree(event);
++	return ret;
++}
++
++static const struct file_operations mt7996_txpower_sku_fops = {
++	.read = mt7996_get_txpower_sku,
++	.open = simple_open,
++	.owner = THIS_MODULE,
++	.llseek = default_llseek,
++};
++
++#define mt7996_txpower_path_puts(rate, arr_length)					\
++({											\
++	len += scnprintf(buf + len, size - len, "%23s:", #rate " (TMAC)");		\
++	for (i = 0; i < arr_length; i++, offs++)					\
++		len += scnprintf(buf + len, size - len, " %4d",				\
++				 event->backoff_table_info.frame_power[offs]);		\
++	len += scnprintf(buf + len, size - len, "\n");					\
++})
++
++static ssize_t
++mt7996_get_txpower_path(struct file *file, char __user *user_buf,
++		       size_t count, loff_t *ppos)
++{
++	struct mt7996_phy *phy = file->private_data;
++	struct mt7996_mcu_txpower_event *event;
++	static const size_t size = 5120;
++	int i, offs = 0, len = 0;
++	ssize_t ret;
++	char *buf;
++
++	buf = kzalloc(size, GFP_KERNEL);
++	event = kzalloc(sizeof(*event), GFP_KERNEL);
++	if (!buf || !event) {
++		ret = -ENOMEM;
++		goto out;
++	}
++
++	ret = mt7996_mcu_get_tx_power_info(phy, BACKOFF_TABLE_INFO, event);
++	if (ret ||
++	    le32_to_cpu(event->phy_rate_info.category) != UNI_TXPOWER_BACKOFF_TABLE_SHOW_INFO)
++		goto out;
++
++	len += scnprintf(buf + len, size - len, "\n%*c", 25, ' ');
++	len += scnprintf(buf + len, size - len, "1T1S/2T1S/3T1S/4T1S/5T1S/2T2S/3T2S/4T2S/5T2S/"
++			 "3T3S/4T3S/5T3S/4T4S/5T4S/5T5S\n");
++
++	mt7996_txpower_path_puts(CCK, 5);
++	mt7996_txpower_path_puts(OFDM, 5);
++	mt7996_txpower_path_puts(BF-OFDM, 4);
++
++	mt7996_txpower_path_puts(RU26, 15);
++	mt7996_txpower_path_puts(BF-RU26, 15);
++	mt7996_txpower_path_puts(RU52, 15);
++	mt7996_txpower_path_puts(BF-RU52, 15);
++	mt7996_txpower_path_puts(RU26_52, 15);
++	mt7996_txpower_path_puts(BF-RU26_52, 15);
++	mt7996_txpower_path_puts(RU106, 15);
++	mt7996_txpower_path_puts(BF-RU106, 15);
++	mt7996_txpower_path_puts(RU106_52, 15);
++	mt7996_txpower_path_puts(BF-RU106_52, 15);
++
++	mt7996_txpower_path_puts(BW20/RU242, 15);
++	mt7996_txpower_path_puts(BF-BW20/RU242, 15);
++	mt7996_txpower_path_puts(BW40/RU484, 15);
++	mt7996_txpower_path_puts(BF-BW40/RU484, 15);
++	mt7996_txpower_path_puts(RU242_484, 15);
++	mt7996_txpower_path_puts(BF-RU242_484, 15);
++	mt7996_txpower_path_puts(BW80/RU996, 15);
++	mt7996_txpower_path_puts(BF-BW80/RU996, 15);
++	mt7996_txpower_path_puts(RU484_996, 15);
++	mt7996_txpower_path_puts(BF-RU484_996, 15);
++	mt7996_txpower_path_puts(RU242_484_996, 15);
++	mt7996_txpower_path_puts(BF-RU242_484_996, 15);
++	mt7996_txpower_path_puts(BW160/RU996x2, 15);
++	mt7996_txpower_path_puts(BF-BW160/RU996x2, 15);
++	mt7996_txpower_path_puts(RU484_996x2, 15);
++	mt7996_txpower_path_puts(BF-RU484_996x2, 15);
++	mt7996_txpower_path_puts(RU996x3, 15);
++	mt7996_txpower_path_puts(BF-RU996x3, 15);
++	mt7996_txpower_path_puts(RU484_996x3, 15);
++	mt7996_txpower_path_puts(BF-RU484_996x3, 15);
++	mt7996_txpower_path_puts(BW320/RU996x4, 15);
++	mt7996_txpower_path_puts(BF-BW320/RU996x4, 15);
++
++	len += scnprintf(buf + len, size - len, "\nBackoff table: %s\n",
++			 event->backoff_table_info.backoff_en ? "enable" : "disable");
++
++	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
++
++out:
++	kfree(buf);
++	kfree(event);
++	return ret;
++}
++
++static const struct file_operations mt7996_txpower_path_fops = {
++	.read = mt7996_get_txpower_path,
++	.open = simple_open,
++	.owner = THIS_MODULE,
++	.llseek = default_llseek,
++};
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+@@ -2503,6 +2861,11 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ 	debugfs_create_devm_seqfile(dev->mt76.dev, "tr_info", dir,
+ 				    mt7996_trinfo_read);
+ 
++	debugfs_create_file("txpower_level", 0600, dir, phy, &fops_txpower_level);
++	debugfs_create_file("txpower_info", 0600, dir, phy, &mt7996_txpower_info_fops);
++	debugfs_create_file("txpower_sku", 0600, dir, phy, &mt7996_txpower_sku_fops);
++	debugfs_create_file("txpower_path", 0600, dir, phy, &mt7996_txpower_path_fops);
++
+ 	debugfs_create_devm_seqfile(dev->mt76.dev, "wtbl_info", dir,
+ 				    mt7996_wtbl_read);
+ 
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index c16b25ab..e56ddd8f 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -12,8 +12,31 @@
+ 
+ #ifdef CONFIG_MTK_DEBUG
+ 
++int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct tx_power_ctrl req = {
++		.tag = cpu_to_le16(UNI_TXPOWER_SHOW_INFO),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.power_ctrl_id = UNI_TXPOWER_SHOW_INFO,
++		.show_info_category = category,
++		.band_idx = phy->mt76->band_idx,
++	};
++	struct sk_buff *skb;
++	int ret;
+ 
++	ret = mt76_mcu_send_and_get_msg(&dev->mt76,
++					MCU_WM_UNI_CMD_QUERY(TXPOWER),
++					&req, sizeof(req), true, &skb);
++	if (ret)
++		return ret;
+ 
++	memcpy(event, skb->data, sizeof(struct mt7996_mcu_txpower_event));
++
++	dev_kfree_skb(skb);
++
++	return 0;
++}
+ 
+ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val)
+ {
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+index 7f4d4e02..c30418ca 100644
+--- a/mt7996/mtk_mcu.h
++++ b/mt7996/mtk_mcu.h
+@@ -14,6 +14,98 @@ enum {
+ 	UNI_CMD_MURU_DBG_INFO = 0x18,
+ };
+ 
++struct txpower_basic_info {
++	u8 category;
++	u8 rsv1;
++
++	/* basic info */
++	u8 band_idx;
++	u8 band;
++
++	/* board type info */
++	bool is_epa;
++	bool is_elna;
++
++	/* power percentage info */
++	bool percentage_ctrl_enable;
++	s8 power_drop_level;
++
++	/* frond-end loss TX info */
++	s8 front_end_loss_tx[4];
++
++	/* frond-end loss RX info */
++	s8 front_end_loss_rx[4];
++
++	/* thermal info */
++	bool thermal_compensate_enable;
++	s8 thermal_compensate_value;
++	u8 rsv2;
++
++	/* TX power max/min limit info */
++	s8 max_power_bound;
++	s8 min_power_bound;
++
++	/* power limit info */
++	bool sku_enable;
++	bool bf_backoff_enable;
++
++	/* MU TX power info */
++	bool mu_tx_power_manual_enable;
++	s8 mu_tx_power_auto;
++	s8 mu_tx_power_manual;
++	u8 rsv3;
++};
++
++struct txpower_phy_rate_info {
++	u8 category;
++	u8 band_idx;
++	u8 band;
++	u8 epa_gain;
++
++	/* rate power info [dBm] */
++	s8 frame_power[MT7996_SKU_RATE_NUM][__MT_MAX_BAND];
++
++	/* TX power max/min limit info */
++	s8 max_power_bound;
++	s8 min_power_bound;
++	u8 rsv1;
++};
++
++struct txpower_backoff_table_info {
++	u8 category;
++	u8 band_idx;
++	u8 band;
++	u8 backoff_en;
++
++	s8 frame_power[MT7996_SKU_PATH_NUM];
++	u8 rsv[3];
++};
++
++struct mt7996_mcu_txpower_event {
++	u8 _rsv[4];
++
++	__le16 tag;
++	__le16 len;
++
++	union {
++		struct txpower_basic_info basic_info;
++		struct txpower_phy_rate_info phy_rate_info;
++		struct txpower_backoff_table_info backoff_table_info;
++	};
++};
++
++enum txpower_category {
++	BASIC_INFO,
++	BACKOFF_TABLE_INFO,
++	PHY_RATE_INFO,
++};
++
++enum txpower_event {
++	UNI_TXPOWER_BASIC_INFO = 0,
++	UNI_TXPOWER_BACKOFF_TABLE_SHOW_INFO = 3,
++	UNI_TXPOWER_PHY_RATE_INFO = 5,
++};
++
+ #endif
+ 
+ #endif
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index 4c20a67d..8ec1dc1c 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -693,24 +693,29 @@ enum offs_rev {
+ 						 ((_wf) << 16) + (ofs))
+ #define MT_WF_PHYRX_CSD_IRPI(_band, _wf)	MT_WF_PHYRX_CSD(_band, _wf, 0x1000)
+ 
+-/* PHYRX CTRL */
+-#define MT_WF_PHYRX_BAND_BASE			0x83080000
+-#define MT_WF_PHYRX_BAND(_band, ofs)		(MT_WF_PHYRX_BAND_BASE + \
++/* PHYDFE CTRL */
++#define MT_WF_PHYDFE_TSSI_TXCTRL01(_band)	MT_WF_PHYRX_CSD(_band, 0, 0xc718)
++#define MT_WF_PHYDFE_TSSI_TXCTRL_POWER_TMAC	GENMASK(31, 24)
++
++/* PHY CTRL */
++#define MT_WF_PHY_BAND_BASE			0x83080000
++#define MT_WF_PHY_BAND(_band, ofs)		(MT_WF_PHY_BAND_BASE + \
+ 						 ((_band) << 20) + (ofs))
+ 
+-#define MT_WF_PHYRX_BAND_GID_TAB_VLD0(_band)	MT_WF_PHYRX_BAND(_band, 0x1054)
+-#define MT_WF_PHYRX_BAND_GID_TAB_VLD1(_band)	MT_WF_PHYRX_BAND(_band, 0x1058)
+-#define MT_WF_PHYRX_BAND_GID_TAB_POS0(_band)	MT_WF_PHYRX_BAND(_band, 0x105c)
+-#define MT_WF_PHYRX_BAND_GID_TAB_POS1(_band)	MT_WF_PHYRX_BAND(_band, 0x1060)
+-#define MT_WF_PHYRX_BAND_GID_TAB_POS2(_band)	MT_WF_PHYRX_BAND(_band, 0x1064)
+-#define MT_WF_PHYRX_BAND_GID_TAB_POS3(_band)	MT_WF_PHYRX_BAND(_band, 0x1068)
++#define MT_WF_PHYRX_BAND_GID_TAB_VLD0(_band)	MT_WF_PHY_BAND(_band, 0x1054)
++#define MT_WF_PHYRX_BAND_GID_TAB_VLD1(_band)	MT_WF_PHY_BAND(_band, 0x1058)
++#define MT_WF_PHYRX_BAND_GID_TAB_POS0(_band)	MT_WF_PHY_BAND(_band, 0x105c)
++#define MT_WF_PHYRX_BAND_GID_TAB_POS1(_band)	MT_WF_PHY_BAND(_band, 0x1060)
++#define MT_WF_PHYRX_BAND_GID_TAB_POS2(_band)	MT_WF_PHY_BAND(_band, 0x1064)
++#define MT_WF_PHYRX_BAND_GID_TAB_POS3(_band)	MT_WF_PHY_BAND(_band, 0x1068)
+ 
+-#define MT_WF_PHYRX_BAND_RX_CTRL1(_band)	MT_WF_PHYRX_BAND(_band, 0x2004)
++/* PHYRX CTRL */
++#define MT_WF_PHYRX_BAND_RX_CTRL1(_band)	MT_WF_PHY_BAND(_band, 0x2004)
+ #define MT_WF_PHYRX_BAND_RX_CTRL1_IPI_EN	GENMASK(2, 0)
+ #define MT_WF_PHYRX_BAND_RX_CTRL1_STSCNT_EN	GENMASK(11, 9)
+ 
+ /* PHYRX CSD BAND */
+-#define MT_WF_PHYRX_CSD_BAND_RXTD12(_band)		MT_WF_PHYRX_BAND(_band, 0x8230)
++#define MT_WF_PHYRX_CSD_BAND_RXTD12(_band)		MT_WF_PHY_BAND(_band, 0x8230)
+ #define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR_ONLY	BIT(18)
+ #define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR		BIT(29)
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0031-mtk-wifi-mt76-mt7996-add-debugfs-for-fw-coredump.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0031-mtk-wifi-mt76-mt7996-add-debugfs-for-fw-coredump.patch
deleted file mode 100644
index 0adbbdc..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0031-mtk-wifi-mt76-mt7996-add-debugfs-for-fw-coredump.patch
+++ /dev/null
@@ -1,171 +0,0 @@
-From 2b301b7df0e6aeb725683bbee25027787488b147 Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Fri, 19 May 2023 14:56:07 +0800
-Subject: [PATCH 031/116] mtk: wifi: mt76: mt7996: add debugfs for fw coredump.
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
----
- mt7996/debugfs.c | 19 +++++++++++++++++--
- mt7996/mac.c     | 28 +++++++++++++++++++++++++---
- mt7996/mcu.h     |  4 ++++
- mt7996/mt7996.h  | 10 ++++++++++
- 4 files changed, 56 insertions(+), 5 deletions(-)
-
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index 344c759..3074202 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -84,6 +84,8 @@ mt7996_sys_recovery_set(struct file *file, const char __user *user_buf,
- 	 * 7: trigger & enable system error L4 mdp recovery.
- 	 * 8: trigger & enable system error full recovery.
- 	 * 9: trigger firmware crash.
-+	 * 10: trigger grab wa firmware coredump.
-+	 * 11: trigger grab wm firmware coredump.
- 	 */
- 	case UNI_CMD_SER_QUERY:
- 		ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_QUERY, 0, band);
-@@ -105,15 +107,25 @@ mt7996_sys_recovery_set(struct file *file, const char __user *user_buf,
- 	/* enable full chip reset */
- 	case UNI_CMD_SER_SET_RECOVER_FULL:
- 		mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK);
--		dev->recovery.state |= MT_MCU_CMD_WDT_MASK;
-+		dev->recovery.state |= MT_MCU_CMD_WM_WDT;
- 		mt7996_reset(dev);
- 		break;
- 
- 	/* WARNING: trigger firmware crash */
- 	case UNI_CMD_SER_SET_SYSTEM_ASSERT:
-+		// trigger wm assert exception
- 		ret = mt7996_mcu_trigger_assert(dev);
- 		if (ret)
- 			return ret;
-+		// trigger wa assert exception
-+		mt76_wr(dev, 0x89098108, 0x20);
-+		mt76_wr(dev, 0x89098118, 0x20);
-+		break;
-+	case UNI_CMD_SER_FW_COREDUMP_WA:
-+		mt7996_coredump(dev, MT7996_COREDUMP_MANUAL_WA);
-+		break;
-+	case UNI_CMD_SER_FW_COREDUMP_WM:
-+		mt7996_coredump(dev, MT7996_COREDUMP_MANUAL_WM);
- 		break;
- 	default:
- 		break;
-@@ -160,7 +172,10 @@ mt7996_sys_recovery_get(struct file *file, char __user *user_buf,
- 			  "8: trigger system error full recovery\n");
- 	desc += scnprintf(buff + desc, bufsz - desc,
- 			  "9: trigger firmware crash\n");
--
-+	desc += scnprintf(buff + desc, bufsz - desc,
-+			  "10: trigger grab wa firmware coredump\n");
-+	desc += scnprintf(buff + desc, bufsz - desc,
-+			  "11: trigger grab wm firmware coredump\n");
- 	/* SER statistics */
- 	desc += scnprintf(buff + desc, bufsz - desc,
- 			  "\nlet's dump firmware SER statistics...\n");
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index d55e5a7..1c1b3eb 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -2080,15 +2080,36 @@ void mt7996_mac_dump_work(struct work_struct *work)
- 	struct mt7996_dev *dev;
- 
- 	dev = container_of(work, struct mt7996_dev, dump_work);
--	if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT)
-+	if (dev->dump_state == MT7996_COREDUMP_MANUAL_WA ||
-+	    READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT)
- 		mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WA);
- 
--	if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WM_WDT)
-+	if (dev->dump_state == MT7996_COREDUMP_MANUAL_WM ||
-+	    READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WM_WDT)
- 		mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WM);
- 
--	queue_work(dev->mt76.wq, &dev->reset_work);
-+	if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WDT_MASK)
-+		queue_work(dev->mt76.wq, &dev->reset_work);
-+
-+	dev->dump_state = MT7996_COREDUMP_IDLE;
- }
- 
-+void mt7996_coredump(struct mt7996_dev *dev, u8 state)
-+{
-+	if (state == MT7996_COREDUMP_IDLE ||
-+	    state >= __MT7996_COREDUMP_TYPE_MAX)
-+		return;
-+
-+	if (dev->dump_state != MT7996_COREDUMP_IDLE)
-+		return;
-+
-+	dev->dump_state = state;
-+	dev_info(dev->mt76.dev, "%s attempting grab coredump\n",
-+		 wiphy_name(dev->mt76.hw->wiphy));
-+
-+	queue_work(dev->mt76.wq, &dev->dump_work);
-+ }
-+
- void mt7996_reset(struct mt7996_dev *dev)
- {
- 	if (!dev->recovery.hw_init_done)
-@@ -2106,6 +2127,7 @@ void mt7996_reset(struct mt7996_dev *dev)
- 
- 		mt7996_irq_disable(dev, MT_INT_MCU_CMD);
- 		queue_work(dev->mt76.wq, &dev->dump_work);
-+		mt7996_coredump(dev, MT7996_COREDUMP_AUTO);
- 		return;
- 	}
- 
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 2e845e9..cb7260f 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -958,7 +958,11 @@ enum {
- 	UNI_CMD_SER_SET_RECOVER_L3_BF,
- 	UNI_CMD_SER_SET_RECOVER_L4_MDP,
- 	UNI_CMD_SER_SET_RECOVER_FULL,
-+	/* fw assert */
- 	UNI_CMD_SER_SET_SYSTEM_ASSERT,
-+	/* coredump */
-+	UNI_CMD_SER_FW_COREDUMP_WA,
-+	UNI_CMD_SER_FW_COREDUMP_WM,
- 	/* action */
- 	UNI_CMD_SER_ENABLE = 1,
- 	UNI_CMD_SER_SET,
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 402327d..623b782 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -138,6 +138,14 @@ enum mt7996_ram_type {
- 	__MT7996_RAM_TYPE_MAX,
- };
- 
-+enum mt7996_coredump_state {
-+	MT7996_COREDUMP_IDLE = 0,
-+	MT7996_COREDUMP_MANUAL_WA,
-+	MT7996_COREDUMP_MANUAL_WM,
-+	MT7996_COREDUMP_AUTO,
-+	__MT7996_COREDUMP_TYPE_MAX,
-+};
-+
- enum mt7996_txq_id {
- 	MT7996_TXQ_FWDL = 16,
- 	MT7996_TXQ_MCU_WM,
-@@ -393,6 +401,7 @@ struct mt7996_dev {
- 
- 	/* protects coredump data */
- 	struct mutex dump_mutex;
-+	u8 dump_state;
- #ifdef CONFIG_DEV_COREDUMP
- 	struct {
- 		struct mt7996_crash_data *crash_data[__MT7996_RAM_TYPE_MAX];
-@@ -580,6 +589,7 @@ void mt7996_init_txpower(struct mt7996_phy *phy);
- int mt7996_txbf_init(struct mt7996_dev *dev);
- int mt7996_get_chip_sku(struct mt7996_dev *dev);
- void mt7996_reset(struct mt7996_dev *dev);
-+void mt7996_coredump(struct mt7996_dev *dev, u8 state);
- int mt7996_run(struct ieee80211_hw *hw);
- int mt7996_mcu_init(struct mt7996_dev *dev);
- int mt7996_mcu_init_firmware(struct mt7996_dev *dev);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0032-mtk-mt76-mt7996-add-binfile-mode-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0032-mtk-mt76-mt7996-add-binfile-mode-support.patch
new file mode 100644
index 0000000..b3430bf
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0032-mtk-mt76-mt7996-add-binfile-mode-support.patch
@@ -0,0 +1,356 @@
+From e6e4969b4c1ae6f9a1fc9ac9744ca6eb76400a51 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 31 Mar 2023 11:36:34 +0800
+Subject: [PATCH 032/199] mtk: mt76: mt7996: add binfile mode support
+
+Fix binfile cannot sync precal data to atenl
+Binfile is viewed as efuse mode in atenl, so atenl does not allocate
+precal memory for its eeprom file
+Use mtd offset == 0xFFFFFFFF to determine whether it is binfile or flash mode
+Add support for loading precal in binfile mode
+
+Align upstream
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ eeprom.c             |  25 +++++++++++
+ mt76.h               |   3 ++
+ mt7996/eeprom.c      | 103 ++++++++++++++++++++++++++++++++++++++++---
+ mt7996/eeprom.h      |   7 +++
+ mt7996/mt7996.h      |   3 ++
+ mt7996/mtk_debugfs.c |  41 +++++++++++++++++
+ testmode.h           |   2 +-
+ 7 files changed, 178 insertions(+), 6 deletions(-)
+
+diff --git a/eeprom.c b/eeprom.c
+index 11efe293..3da94926 100644
+--- a/eeprom.c
++++ b/eeprom.c
+@@ -161,6 +161,31 @@ static int mt76_get_of_eeprom(struct mt76_dev *dev, void *eep, int len)
+ 	return mt76_get_of_data_from_nvmem(dev, eep, "eeprom", len);
+ }
+ 
++bool mt76_check_bin_file_mode(struct mt76_dev *dev)
++{
++	struct device_node *np = dev->dev->of_node;
++	const char *bin_file_name = NULL;
++
++	if (!np)
++		return false;
++
++	of_property_read_string(np, "bin_file_name", &bin_file_name);
++
++	dev->bin_file_name = bin_file_name;
++	if (dev->bin_file_name) {
++		dev_info(dev->dev, "Using bin file %s\n", dev->bin_file_name);
++#ifdef CONFIG_NL80211_TESTMODE
++		dev->test_mtd.name = devm_kstrdup(dev->dev, bin_file_name, GFP_KERNEL);
++		dev->test_mtd.offset = -1;
++#endif
++	}
++
++	of_node_put(np);
++
++	return dev->bin_file_name ? true : false;
++}
++EXPORT_SYMBOL_GPL(mt76_check_bin_file_mode);
++
+ void
+ mt76_eeprom_override(struct mt76_phy *phy)
+ {
+diff --git a/mt76.h b/mt76.h
+index 2236bc6b..d4a0f006 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -958,6 +958,8 @@ struct mt76_dev {
+ 		struct mt76_usb usb;
+ 		struct mt76_sdio sdio;
+ 	};
++
++	const char *bin_file_name;
+ };
+ 
+ #define MT76_MAX_AMSDU_NUM 8
+@@ -1231,6 +1233,7 @@ void mt76_eeprom_override(struct mt76_phy *phy);
+ int mt76_get_of_data_from_mtd(struct mt76_dev *dev, void *eep, int offset, int len);
+ int mt76_get_of_data_from_nvmem(struct mt76_dev *dev, void *eep,
+ 				const char *cell_name, int len);
++bool mt76_check_bin_file_mode(struct mt76_dev *dev);
+ 
+ struct mt76_queue *
+ mt76_init_queue(struct mt76_dev *dev, int qid, int idx, int n_desc,
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 17485bfc..5dc55646 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -82,10 +82,17 @@ static int mt7996_check_eeprom(struct mt7996_dev *dev)
+ 	}
+ }
+ 
+-static char *mt7996_eeprom_name(struct mt7996_dev *dev)
++const char *mt7996_eeprom_name(struct mt7996_dev *dev)
+ {
+-	if (dev->testmode_enable)
+-		return MT7996_EEPROM_DEFAULT_TM;
++	if (dev->bin_file_mode)
++		return dev->mt76.bin_file_name;
++
++	if (dev->testmode_enable) {
++		if (is_mt7992(&dev->mt76))
++			return MT7992_EEPROM_DEFAULT_TM;
++		else
++			return MT7996_EEPROM_DEFAULT_TM;
++	}
+ 
+ 	switch (mt76_chip(&dev->mt76)) {
+ 	case 0x7990:
+@@ -152,7 +159,10 @@ mt7996_eeprom_load_default(struct mt7996_dev *dev)
+ 		return ret;
+ 
+ 	if (!fw || !fw->data) {
+-		dev_err(dev->mt76.dev, "Invalid default bin\n");
++		if (dev->bin_file_mode)
++			dev_err(dev->mt76.dev, "Invalid bin (bin file mode)\n");
++		else
++			dev_err(dev->mt76.dev, "Invalid default bin\n");
+ 		ret = -EINVAL;
+ 		goto out;
+ 	}
+@@ -166,18 +176,45 @@ out:
+ 	return ret;
+ }
+ 
++static int mt7996_eeprom_load_flash(struct mt7996_dev *dev)
++{
++	int ret = 1;
++
++	/* return > 0 for load success, return 0 for load failed, return < 0 for non memory */
++	dev->bin_file_mode = mt76_check_bin_file_mode(&dev->mt76);
++	if (dev->bin_file_mode) {
++		dev->mt76.eeprom.size = MT7996_EEPROM_SIZE;
++		dev->mt76.eeprom.data = devm_kzalloc(dev->mt76.dev, dev->mt76.eeprom.size,
++						     GFP_KERNEL);
++		if (!dev->mt76.eeprom.data)
++			return -ENOMEM;
++
++		if (mt7996_eeprom_load_default(dev))
++			return 0;
++
++		if (mt7996_check_eeprom(dev))
++			return 0;
++	} else {
++		ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
++	}
++
++	return ret;
++}
++
+ int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev)
+ {
+ 	u8 *eeprom;
+ 	int ret;
+ 
+ 	/* load eeprom in flash or bin file mode to determine fw mode */
+-	ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
++	ret = mt7996_eeprom_load_flash(dev);
++
+ 	if (ret < 0)
+ 		return ret;
+ 
+ 	if (ret) {
+ 		dev->flash_mode = true;
++		dev->eeprom_mode = dev->bin_file_mode ? BIN_FILE_MODE : FLASH_MODE;
+ 		eeprom = dev->mt76.eeprom.data;
+ 		/* testmode enable priority: eeprom field > module parameter */
+ 		dev->testmode_enable = !mt7996_check_eeprom(dev) ? eeprom[MT_EE_TESTMODE_EN] :
+@@ -211,6 +248,7 @@ static int mt7996_eeprom_load(struct mt7996_dev *dev)
+ 			if (ret && ret != -EINVAL)
+ 				return ret;
+ 		}
++		dev->eeprom_mode = EFUSE_MODE;
+ 	}
+ 
+ 	return mt7996_check_eeprom(dev);
+@@ -337,6 +375,59 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy)
+ 	return mt7996_eeprom_parse_band_config(phy);
+ }
+ 
++static int
++mt7996_eeprom_load_precal_binfile(struct mt7996_dev *dev, u32 offs, u32 size)
++{
++	const struct firmware *fw = NULL;
++	int ret;
++
++	ret = request_firmware(&fw, dev->mt76.bin_file_name, dev->mt76.dev);
++	if (ret)
++		return ret;
++
++	if (!fw || !fw->data) {
++		dev_err(dev->mt76.dev, "Invalid bin (bin file mode), load precal fail\n");
++		ret = -EINVAL;
++		goto out;
++	}
++
++	memcpy(dev->cal, fw->data + offs, size);
++
++out:
++	release_firmware(fw);
++
++	return ret;
++}
++
++static int mt7996_eeprom_load_precal(struct mt7996_dev *dev)
++{
++	struct mt76_dev *mdev = &dev->mt76;
++	u8 *eeprom = mdev->eeprom.data;
++	u32 offs = MT_EE_DO_PRE_CAL;
++	u32 size, val = eeprom[offs];
++	int ret;
++
++	mt7996_eeprom_init_precal(dev);
++
++	if (!dev->flash_mode || !val)
++		return 0;
++
++	size = MT_EE_CAL_GROUP_SIZE + MT_EE_CAL_DPD_SIZE;
++
++	dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
++	if (!dev->cal)
++		return -ENOMEM;
++
++	if (dev->bin_file_mode)
++		return mt7996_eeprom_load_precal_binfile(dev, MT_EE_PRECAL, size);
++
++	ret = mt76_get_of_data_from_mtd(mdev, dev->cal, offs, size);
++	if (!ret)
++		return ret;
++
++	return mt76_get_of_data_from_nvmem(mdev, dev->cal, "precal", size);
++}
++
+ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ {
+ 	int ret;
+@@ -351,6 +442,8 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ 			return ret;
+ 
+ 		dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n");
++		dev->bin_file_mode = false;
++		dev->eeprom_mode = DEFAULT_BIN_MODE;
+ 		ret = mt7996_eeprom_load_default(dev);
+ 		if (ret)
+ 			return ret;
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index b19ff068..8f0f87b6 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -102,6 +102,13 @@ enum mt7996_eeprom_band {
+ 	MT_EE_BAND_SEL_6GHZ,
+ };
+ 
++enum mt7915_eeprom_mode {
++	DEFAULT_BIN_MODE,
++	EFUSE_MODE,
++	FLASH_MODE,
++	BIN_FILE_MODE,
++};
++
+ static inline int
+ mt7996_get_channel_group_5g(int channel)
+ {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index ff7edcd8..3abb932d 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -394,6 +394,8 @@ struct mt7996_dev {
+ 	} wed_rro;
+ 
+ 	bool testmode_enable;
++	bool bin_file_mode;
++	u8 eeprom_mode;
+ 
+ 	bool ibf;
+ 	u8 fw_debug_wm;
+@@ -522,6 +524,7 @@ irqreturn_t mt7996_irq_handler(int irq, void *dev_instance);
+ u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
+ int mt7996_register_device(struct mt7996_dev *dev);
+ void mt7996_unregister_device(struct mt7996_dev *dev);
++const char *mt7996_eeprom_name(struct mt7996_dev *dev);
+ int mt7996_eeprom_init(struct mt7996_dev *dev);
+ int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev);
+ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 3558641c..c096fb6c 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2797,6 +2797,44 @@ static const struct file_operations mt7996_txpower_path_fops = {
+ 	.llseek = default_llseek,
+ };
+ 
++static int mt7996_show_eeprom_mode(struct seq_file *s, void *data)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	struct mt76_dev *mdev = &dev->mt76;
++#ifdef CONFIG_NL80211_TESTMODE
++	const char *mtd_name = mdev->test_mtd.name;
++	u32 mtd_offset = mdev->test_mtd.offset;
++#else
++	const char *mtd_name = NULL;
++	u32 mtd_offset;
++#endif
++
++	seq_printf(s, "Current eeprom mode:\n");
++
++	switch (dev->eeprom_mode) {
++	case DEFAULT_BIN_MODE:
++		seq_printf(s, "   default bin mode\n   filename = %s\n", mt7996_eeprom_name(dev));
++		break;
++	case EFUSE_MODE:
++		seq_printf(s, "   efuse mode\n");
++		break;
++	case FLASH_MODE:
++		if (mtd_name)
++			seq_printf(s, "   flash mode\n   mtd name = %s\n   flash offset = 0x%x\n",
++				   mtd_name, mtd_offset);
++		else
++			seq_printf(s, "   flash mode\n");
++		break;
++	case BIN_FILE_MODE:
++		seq_printf(s, "   bin file mode\n   filename = %s\n", mt7996_eeprom_name(dev));
++		break;
++	default:
++		break;
++	}
++
++	return 0;
++}
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+@@ -2866,6 +2904,9 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ 	debugfs_create_file("txpower_sku", 0600, dir, phy, &mt7996_txpower_sku_fops);
+ 	debugfs_create_file("txpower_path", 0600, dir, phy, &mt7996_txpower_path_fops);
+ 
++	debugfs_create_devm_seqfile(dev->mt76.dev, "eeprom_mode", dir,
++				    mt7996_show_eeprom_mode);
++
+ 	debugfs_create_devm_seqfile(dev->mt76.dev, "wtbl_info", dir,
+ 				    mt7996_wtbl_read);
+ 
+diff --git a/testmode.h b/testmode.h
+index d6601cdc..5d677f8c 100644
+--- a/testmode.h
++++ b/testmode.h
+@@ -16,7 +16,7 @@
+  * @MT76_TM_ATTR_RESET: reset parameters to default (flag)
+  * @MT76_TM_ATTR_STATE: test state (u32), see &enum mt76_testmode_state
+  *
+- * @MT76_TM_ATTR_MTD_PART: mtd partition used for eeprom data (string)
++ * @MT76_TM_ATTR_MTD_PART: mtd partition or binfile used for eeprom data (string)
+  * @MT76_TM_ATTR_MTD_OFFSET: offset of eeprom data within the partition (u32)
+  * @MT76_TM_ATTR_BAND_IDX: band idx of the chip (u8)
+  *
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0032-mtk-wifi-mt76-mt7996-Add-mt7992-coredump-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0032-mtk-wifi-mt76-mt7996-Add-mt7992-coredump-support.patch
deleted file mode 100644
index 20f82a0..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0032-mtk-wifi-mt76-mt7996-Add-mt7992-coredump-support.patch
+++ /dev/null
@@ -1,162 +0,0 @@
-From 2df50005330ec03e1316a25e0cb3b6ece5915d13 Mon Sep 17 00:00:00 2001
-From: Rex Lu <rex.lu@mediatek.com>
-Date: Mon, 25 Dec 2023 15:17:49 +0800
-Subject: [PATCH 032/116] mtk: wifi: mt76: mt7996: Add mt7992 coredump support
-
-1. Add mt7992 coredump support
-2. fixed if new ic have not support coredump, it may cause crash when remove module
-
-Signed-off-by: Rex Lu <rex.lu@mediatek.com>
----
- mt7996/coredump.c | 80 ++++++++++++++++++++++++++++++++++++++---------
- mt7996/mt7996.h   |  1 +
- 2 files changed, 67 insertions(+), 14 deletions(-)
-
-diff --git a/mt7996/coredump.c b/mt7996/coredump.c
-index a7f91b5..d09bcd4 100644
---- a/mt7996/coredump.c
-+++ b/mt7996/coredump.c
-@@ -67,6 +67,44 @@ static const struct mt7996_mem_region mt7996_wa_mem_regions[] = {
- 	},
- };
- 
-+static const struct mt7996_mem_region mt7992_wm_mem_regions[] = {
-+	{
-+		.start = 0x00800000,
-+		.len = 0x0004bfff,
-+		.name = "ULM0",
-+	},
-+	{
-+		.start = 0x00900000,
-+		.len = 0x00035fff,
-+		.name = "ULM1",
-+	},
-+	{
-+		.start = 0x02200000,
-+		.len = 0x0003ffff,
-+		.name = "ULM2",
-+	},
-+	{
-+		.start = 0x00400000,
-+		.len = 0x00027fff,
-+		.name = "SRAM",
-+	},
-+	{
-+		.start = 0xe0000000,
-+		.len = 0x0015ffff,
-+		.name = "CRAM0",
-+	},
-+	{
-+		.start = 0xe0160000,
-+		.len = 0x00c7fff,
-+		.name = "CRAM1",
-+	},
-+	{
-+		.start = 0x7c050000,
-+		.len = 0x00007fff,
-+		.name = "CONN_INFRA",
-+	},
-+};
-+
- const struct mt7996_mem_region*
- mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num)
- {
-@@ -80,6 +118,14 @@ mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num)
- 
- 		*num = ARRAY_SIZE(mt7996_wm_mem_regions);
- 		return &mt7996_wm_mem_regions[0];
-+	case 0x7992:
-+		if (type == MT7996_RAM_TYPE_WA) {
-+			/* mt7992 wa memory regions is the same as mt7996 */
-+			*num = ARRAY_SIZE(mt7996_wa_mem_regions);
-+			return &mt7996_wa_mem_regions[0];
-+		}
-+		*num = ARRAY_SIZE(mt7992_wm_mem_regions);
-+		return &mt7992_wm_mem_regions[0];
- 	default:
- 		return NULL;
- 	}
-@@ -115,7 +161,7 @@ struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type)
- 
- 	lockdep_assert_held(&dev->dump_mutex);
- 
--	if (!coredump_memdump)
-+	if (!coredump_memdump || !crash_data->supported)
- 		return NULL;
- 
- 	guid_gen(&crash_data->guid);
-@@ -289,40 +335,46 @@ int mt7996_coredump_register(struct mt7996_dev *dev)
- 	for (i = 0; i < MT7996_COREDUMP_MAX; i++) {
- 		crash_data = vzalloc(sizeof(*dev->coredump.crash_data[i]));
- 		if (!crash_data)
--			return -ENOMEM;
-+			goto nomem;
- 
- 		dev->coredump.crash_data[i] = crash_data;
-+		crash_data->supported = false;
- 
- 		if (coredump_memdump) {
- 			crash_data->memdump_buf_len = mt7996_coredump_get_mem_size(dev, i);
- 			if (!crash_data->memdump_buf_len)
- 				/* no memory content */
--				return 0;
-+				continue;
- 
- 			crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len);
--			if (!crash_data->memdump_buf) {
--				vfree(crash_data);
--				return -ENOMEM;
--			}
-+			if (!crash_data->memdump_buf)
-+				goto nomem;
-+
-+			crash_data->supported = true;
- 		}
- 	}
- 
- 	return 0;
-+nomem:
-+	mt7996_coredump_unregister(dev);
-+	return -ENOMEM;
- }
- 
- void mt7996_coredump_unregister(struct mt7996_dev *dev)
- {
- 	int i;
-+	struct mt7996_crash_data *crash_data;
- 
- 	for (i = 0; i < MT7996_COREDUMP_MAX; i++) {
--		if (dev->coredump.crash_data[i]->memdump_buf) {
--			vfree(dev->coredump.crash_data[i]->memdump_buf);
--			dev->coredump.crash_data[i]->memdump_buf = NULL;
--			dev->coredump.crash_data[i]->memdump_buf_len = 0;
--		}
-+		crash_data = dev->coredump.crash_data[i];
-+
-+		if (!crash_data)
-+			continue;
-+
-+		if (crash_data->memdump_buf)
-+			vfree(crash_data->memdump_buf);
- 
--		vfree(dev->coredump.crash_data[i]);
--		dev->coredump.crash_data[i] = NULL;
-+		vfree(crash_data);
- 	}
- }
- 
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 623b782..97425a0 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -229,6 +229,7 @@ struct mt7996_vif {
- struct mt7996_crash_data {
- 	guid_t guid;
- 	struct timespec64 timestamp;
-+	bool supported;
- 
- 	u8 *memdump_buf;
- 	size_t memdump_buf_len;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0033-mtk-mt76-mt7996-add-testmode-ZWDFS-verification-supp.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0033-mtk-mt76-mt7996-add-testmode-ZWDFS-verification-supp.patch
new file mode 100644
index 0000000..7496e93
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0033-mtk-mt76-mt7996-add-testmode-ZWDFS-verification-supp.patch
@@ -0,0 +1,490 @@
+From e758524d7b6b53da37a6ab7d392896bf34901b46 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 22 Mar 2023 11:19:52 +0800
+Subject: [PATCH 033/199] mtk: mt76: mt7996: add testmode ZWDFS verification
+ support
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt76.h            |   8 ++
+ mt7996/mt7996.h   |   1 +
+ mt7996/testmode.c | 248 ++++++++++++++++++++++++++++++++++++++++++++--
+ mt7996/testmode.h |  44 ++++++++
+ testmode.c        |  22 +++-
+ tools/fields.c    |  15 +++
+ 6 files changed, 326 insertions(+), 12 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index d4a0f006..0cf5d573 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -787,6 +787,14 @@ struct mt76_testmode_data {
+ 	} cfg;
+ 
+ 	u8 aid;
++
++	u8 offchan_ch;
++	u8 offchan_center_ch;
++	u8 offchan_bw;
++
++	u8 ipi_threshold;
++	u32 ipi_period;
++	u8 ipi_reset;
+ };
+ 
+ struct mt76_vif {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 3abb932d..70aafb29 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -287,6 +287,7 @@ struct mt7996_phy {
+ 
+ 	struct mt76_mib_stats mib;
+ 	struct mt76_channel_state state_ts;
++	struct delayed_work ipi_work;
+ 
+ 	bool has_aux_rx;
+ 
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index a756ee10..836211f9 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -17,6 +17,12 @@ enum {
+ 	TM_CHANGED_TX_LENGTH,
+ 	TM_CHANGED_TX_TIME,
+ 	TM_CHANGED_CFG,
++	TM_CHANGED_OFF_CHAN_CH,
++	TM_CHANGED_OFF_CHAN_CENTER_CH,
++	TM_CHANGED_OFF_CHAN_BW,
++	TM_CHANGED_IPI_THRESHOLD,
++	TM_CHANGED_IPI_PERIOD,
++	TM_CHANGED_IPI_RESET,
+ 
+ 	/* must be last */
+ 	NUM_TM_CHANGED
+@@ -29,20 +35,31 @@ static const u8 tm_change_map[] = {
+ 	[TM_CHANGED_TX_LENGTH] = MT76_TM_ATTR_TX_LENGTH,
+ 	[TM_CHANGED_TX_TIME] = MT76_TM_ATTR_TX_TIME,
+ 	[TM_CHANGED_CFG] = MT76_TM_ATTR_CFG,
++	[TM_CHANGED_OFF_CHAN_CH] = MT76_TM_ATTR_OFF_CH_SCAN_CH,
++	[TM_CHANGED_OFF_CHAN_CENTER_CH] = MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH,
++	[TM_CHANGED_OFF_CHAN_BW] = MT76_TM_ATTR_OFF_CH_SCAN_BW,
++	[TM_CHANGED_IPI_THRESHOLD] = MT76_TM_ATTR_IPI_THRESHOLD,
++	[TM_CHANGED_IPI_PERIOD] = MT76_TM_ATTR_IPI_PERIOD,
++	[TM_CHANGED_IPI_RESET] = MT76_TM_ATTR_IPI_RESET,
+ };
+ 
+-static u8 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method)
++static void mt7996_tm_ipi_work(struct work_struct *work);
++
++static u32 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method)
+ {
+-	static const u8 width_to_bw[][NUM_BW_MAP] = {
+-		[NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ},
+-		[NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ},
+-		[NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ},
+-		[NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ},
+-		[NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ},
+-		[NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ},
+-		[NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
+-		[NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
+-		[NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ},
++	static const u32 width_to_bw[][NUM_BW_MAP] = {
++		[NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ, 40,
++					   FIRST_CONTROL_CHAN_BITMAP_BW40},
++		[NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ, 80,
++					   FIRST_CONTROL_CHAN_BITMAP_BW80},
++		[NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ, 80, 0x0},
++		[NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ, 160,
++					    FIRST_CONTROL_CHAN_BITMAP_BW160},
++		[NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ, 5, 0x0},
++		[NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ, 10, 0x0},
++		[NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0},
++		[NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0},
++		[NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ, 320, 0x0},
+ 	};
+ 
+ 	if (width >= ARRAY_SIZE(width_to_bw))
+@@ -217,6 +234,9 @@ mt7996_tm_init(struct mt7996_phy *phy, bool en)
+ 
+ 	/* use firmware counter for RX stats */
+ 	phy->mt76->test.flag |= MT_TM_FW_RX_COUNT;
++
++	if (en)
++		INIT_DELAYED_WORK(&phy->ipi_work, mt7996_tm_ipi_work);
+ }
+ 
+ static void
+@@ -829,6 +849,204 @@ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	}
+ }
+ 
++static u8
++mt7996_tm_get_center_chan(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef)
++{
++	struct mt76_phy *mphy = phy->mt76;
++	const struct ieee80211_channel *chan = mphy->sband_5g.sband.channels;
++	u32 bitmap, i, offset, width_mhz, size = mphy->sband_5g.sband.n_channels;
++	u16 first_control = 0, control_chan = chandef->chan->hw_value;
++
++	bitmap = mt7996_tm_bw_mapping(chandef->width, BW_MAP_NL_TO_CONTROL_BITMAP_5G);
++	if (!bitmap)
++		return control_chan;
++
++	width_mhz = mt7996_tm_bw_mapping(chandef->width, BW_MAP_NL_TO_MHZ);
++	offset = width_mhz / 10 - 2;
++
++	for (i = 0; i < size; i++) {
++		if (!((1 << i) & bitmap))
++			continue;
++
++		if (control_chan >= chan[i].hw_value)
++			first_control = chan[i].hw_value;
++		else
++			break;
++	}
++
++	if (i == size || first_control == 0)
++		return control_chan;
++
++	return first_control + offset;
++}
++
++static int
++mt7996_tm_set_offchan(struct mt7996_phy *phy, bool no_center)
++{
++	struct mt76_phy *mphy = phy->mt76;
++	struct mt7996_dev *dev = phy->dev;
++	struct ieee80211_hw *hw = mphy->hw;
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct cfg80211_chan_def chandef = {};
++	struct ieee80211_channel *chan;
++	int ret, freq = ieee80211_channel_to_frequency(td->offchan_ch, NL80211_BAND_5GHZ);
++
++	if (!mphy->cap.has_5ghz || !freq) {
++		ret = -EINVAL;
++		dev_info(dev->mt76.dev, "Failed to set offchan (invalid band or channel)!\n");
++		goto out;
++	}
++
++	chandef.width = td->offchan_bw;
++	chan = ieee80211_get_channel(hw->wiphy, freq);
++	chandef.chan = chan;
++	if (no_center)
++		td->offchan_center_ch = mt7996_tm_get_center_chan(phy, &chandef);
++	chandef.center_freq1 = ieee80211_channel_to_frequency(td->offchan_center_ch,
++							      NL80211_BAND_5GHZ);
++	if (!cfg80211_chandef_valid(&chandef)) {
++		ret = -EINVAL;
++		dev_info(dev->mt76.dev, "Failed to set offchan, chandef is invalid!\n");
++		goto out;
++	}
++
++	memset(&dev->rdd2_chandef, 0, sizeof(struct cfg80211_chan_def));
++
++	ret = mt7996_mcu_rdd_background_enable(phy, &chandef);
++
++	if (ret)
++		goto out;
++
++	dev->rdd2_phy = phy;
++	dev->rdd2_chandef = chandef;
++
++	return 0;
++
++out:
++	td->offchan_ch = 0;
++	td->offchan_center_ch = 0;
++	td->offchan_bw = 0;
++
++	return ret;
++}
++
++static void
++mt7996_tm_ipi_hist_ctrl(struct mt7996_phy *phy, struct mt7996_tm_rdd_ipi_ctrl *data, u8 cmd)
++{
++#define MT_IPI_RESET		0x830a5dfc
++#define MT_IPI_RESET_MASK	BIT(28)
++#define MT_IPI_COUNTER_BASE	0x83041000
++#define MT_IPI_COUNTER(idx)	(MT_IPI_COUNTER_BASE + ((idx) * 4))
++	struct mt7996_dev *dev = phy->dev;
++	bool val;
++	int i;
++
++	if (cmd == RDD_SET_IPI_HIST_RESET) {
++		val = mt76_rr(dev, MT_IPI_RESET) & MT_IPI_RESET_MASK;
++		mt76_rmw_field(dev, MT_IPI_RESET, MT_IPI_RESET_MASK, !val);
++		return;
++	}
++
++	for (i = 0; i < POWER_INDICATE_HIST_MAX; i++)
++		data->ipi_hist_val[i] = mt76_rr(dev, MT_IPI_COUNTER(i));
++}
++
++static void
++mt7996_tm_ipi_work(struct work_struct *work)
++{
++#define PRECISION	100
++	struct mt7996_phy *phy = container_of(work, struct mt7996_phy, ipi_work.work);
++	struct mt7996_dev *dev = phy->dev;
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct mt7996_tm_rdd_ipi_ctrl data;
++	u32 ipi_idx, ipi_free_count, ipi_percentage;
++	u32 ipi_hist_count_th = 0, ipi_hist_total_count = 0;
++	u32 self_idle_ratio, ipi_idle_ratio, channel_load;
++	u32 *ipi_hist_data;
++	const char *power_lower_bound, *power_upper_bound;
++	static const char * const ipi_idx_to_power_bound[] = {
++		[RDD_IPI_HIST_0] = "-92",
++		[RDD_IPI_HIST_1] = "-89",
++		[RDD_IPI_HIST_2] = "-86",
++		[RDD_IPI_HIST_3] = "-83",
++		[RDD_IPI_HIST_4] = "-80",
++		[RDD_IPI_HIST_5] = "-75",
++		[RDD_IPI_HIST_6] = "-70",
++		[RDD_IPI_HIST_7] = "-65",
++		[RDD_IPI_HIST_8] = "-60",
++		[RDD_IPI_HIST_9] = "-55",
++		[RDD_IPI_HIST_10] = "inf",
++	};
++
++	memset(&data, 0, sizeof(data));
++	mt7996_tm_ipi_hist_ctrl(phy, &data, RDD_IPI_HIST_ALL_CNT);
++
++	ipi_hist_data = data.ipi_hist_val;
++	for (ipi_idx = 0; ipi_idx < POWER_INDICATE_HIST_MAX; ipi_idx++) {
++		power_lower_bound = ipi_idx ? ipi_idx_to_power_bound[ipi_idx - 1] : "-inf";
++		power_upper_bound = ipi_idx_to_power_bound[ipi_idx];
++
++		dev_info(dev->mt76.dev, "IPI %d (power range: (%s, %s] dBm): ipi count = %d\n",
++			 ipi_idx, power_lower_bound, power_upper_bound, ipi_hist_data[ipi_idx]);
++
++		if (td->ipi_threshold <= ipi_idx && ipi_idx <= RDD_IPI_HIST_10)
++			ipi_hist_count_th += ipi_hist_data[ipi_idx];
++
++		ipi_hist_total_count += ipi_hist_data[ipi_idx];
++	}
++
++	ipi_free_count = ipi_hist_data[RDD_IPI_FREE_RUN_CNT];
++
++	dev_info(dev->mt76.dev, "IPI threshold %d: ipi_hist_count_th = %d, ipi_free_count = %d\n",
++		 td->ipi_threshold, ipi_hist_count_th, ipi_free_count);
++	dev_info(dev->mt76.dev, "TX assert time =  %d [ms]\n", data.tx_assert_time / 1000);
++
++	/* calculate channel load = (self idle ratio - idle ratio) / self idle ratio */
++	if (ipi_hist_count_th >= UINT_MAX / (100 * PRECISION))
++		ipi_percentage = 100 * PRECISION *
++				 (ipi_hist_count_th / (100 * PRECISION)) /
++				 (ipi_free_count / (100 * PRECISION));
++	else
++		ipi_percentage = PRECISION * 100 * ipi_hist_count_th / ipi_free_count;
++
++	ipi_idle_ratio = ((100 * PRECISION) - ipi_percentage) / PRECISION;
++
++	self_idle_ratio = PRECISION * 100 *
++			  (td->ipi_period - (data.tx_assert_time / 1000)) /
++			  td->ipi_period / PRECISION;
++
++	if (self_idle_ratio < ipi_idle_ratio)
++		channel_load = 0;
++	else
++		channel_load = self_idle_ratio - ipi_idle_ratio;
++
++	if (self_idle_ratio <= td->ipi_threshold) {
++		dev_info(dev->mt76.dev, "band[%d]: self idle ratio = %d%%, idle ratio = %d%%\n",
++			 phy->mt76->band_idx, self_idle_ratio, ipi_idle_ratio);
++		return;
++	}
++
++	channel_load = (100 * channel_load) / self_idle_ratio;
++	dev_info(dev->mt76.dev,
++		 "band[%d]: chan load = %d%%, self idle ratio = %d%%, idle ratio = %d%%\n",
++		 phy->mt76->band_idx, channel_load, self_idle_ratio, ipi_idle_ratio);
++}
++
++static int
++mt7996_tm_set_ipi(struct mt7996_phy *phy)
++{
++	struct mt76_testmode_data *td = &phy->mt76->test;
++
++	/* reset IPI CR */
++	mt7996_tm_ipi_hist_ctrl(phy, NULL, RDD_SET_IPI_HIST_RESET);
++
++	cancel_delayed_work(&phy->ipi_work);
++	ieee80211_queue_delayed_work(phy->mt76->hw, &phy->ipi_work,
++				     msecs_to_jiffies(td->ipi_period));
++
++	return 0;
++}
++
+ static void
+ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
+ {
+@@ -860,6 +1078,14 @@ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
+ 
+ 		mt7996_tm_set(dev, func_idx, td->cfg.type);
+ 	}
++	if ((changed & BIT(TM_CHANGED_OFF_CHAN_CH)) &&
++	    (changed & BIT(TM_CHANGED_OFF_CHAN_BW)))
++		mt7996_tm_set_offchan(phy, !(changed & BIT(TM_CHANGED_OFF_CHAN_CENTER_CH)));
++	if ((changed & BIT(TM_CHANGED_IPI_THRESHOLD)) &&
++	    (changed & BIT(TM_CHANGED_IPI_PERIOD)))
++		mt7996_tm_set_ipi(phy);
++	if (changed & BIT(TM_CHANGED_IPI_RESET))
++		mt7996_tm_ipi_hist_ctrl(phy, NULL, RDD_SET_IPI_HIST_RESET);
+ }
+ 
+ static int
+diff --git a/mt7996/testmode.h b/mt7996/testmode.h
+index 9bfb86f2..78662b2e 100644
+--- a/mt7996/testmode.h
++++ b/mt7996/testmode.h
+@@ -27,9 +27,15 @@ enum {
+ 	FW_CDBW_8080MHZ,
+ };
+ 
++#define FIRST_CONTROL_CHAN_BITMAP_BW40		0x5555555
++#define FIRST_CONTROL_CHAN_BITMAP_BW80		0x111111
++#define FIRST_CONTROL_CHAN_BITMAP_BW160		0x100101
++
+ enum bw_mapping_method {
+ 	BW_MAP_NL_TO_FW,
+ 	BW_MAP_NL_TO_TM,
++	BW_MAP_NL_TO_MHZ,
++	BW_MAP_NL_TO_CONTROL_BITMAP_5G,
+ 
+ 	NUM_BW_MAP,
+ };
+@@ -312,4 +318,42 @@ struct mt7996_tm_rx_event {
+ 	};
+ } __packed;
+ 
++enum {
++	RDD_SET_IPI_CR_INIT,		/* CR initialization */
++	RDD_SET_IPI_HIST_RESET,		/* Reset IPI histogram counter */
++	RDD_SET_IDLE_POWER,		/* Idle power info */
++	RDD_SET_IPI_HIST_NUM
++};
++
++enum {
++	RDD_IPI_HIST_0,			/* IPI count for power <= -92 (dBm) */
++	RDD_IPI_HIST_1,			/* IPI count for -92 < power <= -89 (dBm) */
++	RDD_IPI_HIST_2,			/* IPI count for -89 < power <= -86 (dBm) */
++	RDD_IPI_HIST_3,			/* IPI count for -86 < power <= -83 (dBm) */
++	RDD_IPI_HIST_4,			/* IPI count for -83 < power <= -80 (dBm) */
++	RDD_IPI_HIST_5,			/* IPI count for -80 < power <= -75 (dBm) */
++	RDD_IPI_HIST_6,			/* IPI count for -75 < power <= -70 (dBm) */
++	RDD_IPI_HIST_7,			/* IPI count for -70 < power <= -65 (dBm) */
++	RDD_IPI_HIST_8,			/* IPI count for -65 < power <= -60 (dBm) */
++	RDD_IPI_HIST_9,			/* IPI count for -60 < power <= -55 (dBm) */
++	RDD_IPI_HIST_10,		/* IPI count for -55 < power        (dBm) */
++	RDD_IPI_FREE_RUN_CNT,		/* IPI count for counter++ per 8 us */
++	RDD_IPI_HIST_ALL_CNT,		/* Get all IPI */
++	RDD_IPI_HIST_0_TO_10_CNT,	/* Get IPI histogram 0 to 10 */
++	RDD_IPI_HIST_2_TO_10_CNT,	/* Get IPI histogram 2 to 10 */
++	RDD_TX_ASSERT_TIME,		/* Get band 1 TX assert time */
++	RDD_IPI_HIST_NUM
++};
++
++#define POWER_INDICATE_HIST_MAX		RDD_IPI_FREE_RUN_CNT
++#define IPI_HIST_TYPE_NUM		(POWER_INDICATE_HIST_MAX + 1)
++
++struct mt7996_tm_rdd_ipi_ctrl {
++	u8 ipi_hist_idx;
++	u8 band_idx;
++	u8 rsv[2];
++	__le32 ipi_hist_val[IPI_HIST_TYPE_NUM];
++	__le32 tx_assert_time;		/* unit: us */
++} __packed;
++
+ #endif
+diff --git a/testmode.c b/testmode.c
+index cd8cb655..69147f86 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -27,6 +27,13 @@ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
+ 	[MT76_TM_ATTR_TX_TIME] = { .type = NLA_U32 },
+ 	[MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
+ 	[MT76_TM_ATTR_DRV_DATA] = { .type = NLA_NESTED },
++	[MT76_TM_ATTR_OFF_CH_SCAN_CH] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_OFF_CH_SCAN_BW] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_OFF_CH_SCAN_PATH] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_IPI_THRESHOLD] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_IPI_PERIOD] = { .type = NLA_U32 },
++	[MT76_TM_ATTR_IPI_RESET] = { .type = NLA_U8 },
+ };
+ EXPORT_SYMBOL_GPL(mt76_tm_policy);
+ 
+@@ -499,6 +506,9 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	if (tb[MT76_TM_ATTR_TX_RATE_IDX])
+ 		td->tx_rate_idx = nla_get_u8(tb[MT76_TM_ATTR_TX_RATE_IDX]);
+ 
++	if (tb[MT76_TM_ATTR_IPI_PERIOD])
++		td->ipi_period = nla_get_u32(tb[MT76_TM_ATTR_IPI_PERIOD]);
++
+ 	if (mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_MODE], &td->tx_rate_mode,
+ 			   0, MT76_TM_TX_MODE_MAX) ||
+ 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_NSS], &td->tx_rate_nss,
+@@ -514,7 +524,14 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 			   &td->tx_duty_cycle, 0, 99) ||
+ 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_POWER_CONTROL],
+ 			   &td->tx_power_control, 0, 1) ||
+-	    mt76_tm_get_u8(tb[MT76_TM_ATTR_AID], &td->aid, 0, 16))
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_AID], &td->aid, 0, 16) ||
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_CH], &td->offchan_ch, 36, 196) ||
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH], &td->offchan_center_ch,
++			   36, 196) ||
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_BW],
++			   &td->offchan_bw, NL80211_CHAN_WIDTH_20_NOHT, NL80211_CHAN_WIDTH_160) ||
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_IPI_THRESHOLD], &td->ipi_threshold, 0, 10) ||
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_IPI_RESET], &td->ipi_reset, 0, 1))
+ 		goto out;
+ 
+ 	if (tb[MT76_TM_ATTR_TX_LENGTH]) {
+@@ -720,6 +737,9 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ 	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_STBC, td->tx_rate_stbc) ||
+ 	    nla_put_u8(msg, MT76_TM_ATTR_SKU_EN, td->sku_en) ||
+ 	    nla_put_u8(msg, MT76_TM_ATTR_AID, td->aid) ||
++	    nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_CH, td->offchan_ch) ||
++	    nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH, td->offchan_center_ch) ||
++	    nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_BW, td->offchan_bw) ||
+ 	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_LTF) &&
+ 	     nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) ||
+ 	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA) &&
+diff --git a/tools/fields.c b/tools/fields.c
+index b0122763..77696ce7 100644
+--- a/tools/fields.c
++++ b/tools/fields.c
+@@ -35,6 +35,15 @@ static const char * const testmode_tx_mode[] = {
+ 	[MT76_TM_TX_MODE_EHT_MU] = "eht_mu",
+ };
+ 
++static const char * const testmode_offchan_bw[] = {
++	[NL80211_CHAN_WIDTH_20_NOHT] = "NOHT",
++	[NL80211_CHAN_WIDTH_20] = "20",
++	[NL80211_CHAN_WIDTH_40] = "40",
++	[NL80211_CHAN_WIDTH_80] = "80",
++	[NL80211_CHAN_WIDTH_80P80] = "80p80",
++	[NL80211_CHAN_WIDTH_160] = "160",
++};
++
+ static void print_enum(const struct tm_field *field, struct nlattr *attr)
+ {
+ 	unsigned int i = nla_get_u8(attr);
+@@ -390,6 +399,12 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
+ 	FIELD(u8, AID, "aid"),
+ 	FIELD(u8, RU_ALLOC, "ru_alloc"),
+ 	FIELD(u8, RU_IDX, "ru_idx"),
++	FIELD(u8, OFF_CH_SCAN_CH, "offchan_ch"),
++	FIELD(u8, OFF_CH_SCAN_CENTER_CH, "offchan_center_ch"),
++	FIELD_ENUM(OFF_CH_SCAN_BW, "offchan_bw", testmode_offchan_bw),
++	FIELD(u8, IPI_THRESHOLD, "ipi_threshold"),
++	FIELD(u32, IPI_PERIOD, "ipi_period"),
++	FIELD(u8, IPI_RESET, "ipi_reset"),
+ 	FIELD_MAC(MAC_ADDRS, "mac_addrs"),
+ 	FIELD_NESTED_RO(STATS, stats, "",
+ 			.print_extra = print_extra_stats),
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0033-mtk-wifi-mt76-mt7996-add-support-for-runtime-set-in-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0033-mtk-wifi-mt76-mt7996-add-support-for-runtime-set-in-.patch
deleted file mode 100644
index 94ad572..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0033-mtk-wifi-mt76-mt7996-add-support-for-runtime-set-in-.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 6fd185872dc77c7ca2f13237937269bb8f2af706 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Tue, 6 Jun 2023 16:57:10 +0800
-Subject: [PATCH 033/116] mtk: wifi: mt76: mt7996: add support for runtime set
- in-band discovery
-
-with this patch, AP can runtime set inband discovery via hostapd_cli
-
-Usage:
-Enable FILS: hostapd_cli -i [interface] inband_discovery 2 20
-Enable UBPR: hostapd_cli -i [interface] inband_discovery 1 20
-Disable inband discovery: hostapd_cli -i [interface] inband_discovery 0 0
-
-Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
----
- mt7996/mcu.c | 5 ++---
- 1 file changed, 2 insertions(+), 3 deletions(-)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index ae0c11a..51c771b 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -2609,8 +2609,7 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
- 	if (IS_ERR(rskb))
- 		return PTR_ERR(rskb);
- 
--	if (changed & BSS_CHANGED_FILS_DISCOVERY &&
--	    vif->bss_conf.fils_discovery.max_interval) {
-+	if (changed & BSS_CHANGED_FILS_DISCOVERY) {
- 		interval = vif->bss_conf.fils_discovery.max_interval;
- 		skb = ieee80211_get_fils_discovery_tmpl(hw, vif);
- 	} else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP &&
-@@ -2645,7 +2644,7 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
- 	discov->tx_type = !!(changed & BSS_CHANGED_FILS_DISCOVERY);
- 	discov->tx_interval = interval;
- 	discov->prob_rsp_len = cpu_to_le16(MT_TXD_SIZE + skb->len);
--	discov->enable = true;
-+	discov->enable = !!(interval);
- 	discov->wcid = cpu_to_le16(MT7996_WTBL_RESERVED);
- 
- 	buf = (u8 *)tlv + sizeof(*discov);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0034-mtk-mt76-mt7996-support-eagle-ZWDFS-on-iFEM.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0034-mtk-mt76-mt7996-support-eagle-ZWDFS-on-iFEM.patch
new file mode 100644
index 0000000..f4a098a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0034-mtk-mt76-mt7996-support-eagle-ZWDFS-on-iFEM.patch
@@ -0,0 +1,154 @@
+From e6fcd98cab30ba065534890cdb5f0775db054fcd Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 5 Jul 2023 10:00:17 +0800
+Subject: [PATCH 034/199] mtk: mt76: mt7996: support eagle ZWDFS on iFEM
+
+Fix the case that control channel is not first chan during first
+interface setup.
+Refactor ifem adie logic (if/else to switch, use sku_type & fem_type)
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/main.c   | 65 ++++++++++++++++++++++++++++++++++++++++++++++---
+ mt7996/mcu.c    |  6 +++--
+ mt7996/mt7996.h |  1 +
+ 3 files changed, 67 insertions(+), 5 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 5a6ba6a6..5b1984ac 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1448,6 +1448,61 @@ mt7996_twt_teardown_request(struct ieee80211_hw *hw,
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
++static void
++mt7996_background_radar_handle_7975_ifem(struct ieee80211_hw *hw,
++					 struct cfg80211_chan_def *user_chandef,
++					 struct cfg80211_chan_def *fw_chandef)
++{
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct cfg80211_chan_def *c = user_chandef;
++	struct ieee80211_channel *first_chan;
++	bool is_ifem_adie, expand = false;
++
++	switch (mt76_chip(&dev->mt76)) {
++	case 0x7990:
++		is_ifem_adie = dev->fem_type == MT7996_FEM_INT &&
++			       dev->chip_sku != MT7996_SKU_233;
++		break;
++	case 0x7992:
++		is_ifem_adie = dev->chip_sku == MT7992_SKU_44 &&
++			       dev->fem_type != MT7996_FEM_EXT;
++		break;
++	default:
++		return;
++	}
++
++	if (!user_chandef || !is_ifem_adie)
++		goto out;
++
++	if (user_chandef->width == NL80211_CHAN_WIDTH_160) {
++		first_chan = ieee80211_get_channel(hw->wiphy, user_chandef->center_freq1 - 70);
++		if (dev->bg_nxt_freq)
++			goto out;
++
++		if (first_chan->flags & IEEE80211_CHAN_RADAR)
++			dev->bg_nxt_freq = first_chan->center_freq;
++		else
++			c = fw_chandef;
++
++		c->chan = ieee80211_get_channel(hw->wiphy, first_chan->center_freq + 80);
++	} else {
++		if (!dev->bg_nxt_freq)
++			goto out;
++
++		c->chan = ieee80211_get_channel(hw->wiphy, dev->bg_nxt_freq);
++		dev->bg_nxt_freq = 0;
++		expand = true;
++	}
++	c->width = NL80211_CHAN_WIDTH_80;
++	c->center_freq1 = c->chan->center_freq + 30;
++
++	if (c == user_chandef)
++		cfg80211_background_radar_update_channel(hw->wiphy, c, expand);
++	return;
++out:
++	dev->bg_nxt_freq = 0;
++}
++
+ static int
+ mt7996_set_radar_background(struct ieee80211_hw *hw,
+ 			    struct cfg80211_chan_def *chandef)
+@@ -1456,6 +1511,7 @@ mt7996_set_radar_background(struct ieee80211_hw *hw,
+ 	struct mt7996_dev *dev = phy->dev;
+ 	int ret = -EINVAL;
+ 	bool running;
++	struct cfg80211_chan_def ifem_chandef = {};
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+@@ -1468,13 +1524,14 @@ mt7996_set_radar_background(struct ieee80211_hw *hw,
+ 		goto out;
+ 	}
+ 
++	mt7996_background_radar_handle_7975_ifem(hw, chandef, &ifem_chandef);
++
+ 	/* rdd2 already configured on a radar channel */
+ 	running = dev->rdd2_phy &&
+ 		  cfg80211_chandef_valid(&dev->rdd2_chandef) &&
+ 		  !!(dev->rdd2_chandef.chan->flags & IEEE80211_CHAN_RADAR);
+ 
+-	if (!chandef || running ||
+-	    !(chandef->chan->flags & IEEE80211_CHAN_RADAR)) {
++	if (!chandef || running) {
+ 		ret = mt7996_mcu_rdd_background_enable(phy, NULL);
+ 		if (ret)
+ 			goto out;
+@@ -1483,7 +1540,9 @@ mt7996_set_radar_background(struct ieee80211_hw *hw,
+ 			goto update_phy;
+ 	}
+ 
+-	ret = mt7996_mcu_rdd_background_enable(phy, chandef);
++	ret = mt7996_mcu_rdd_background_enable(phy,
++					       ifem_chandef.chan ?
++					       &ifem_chandef : chandef);
+ 	if (ret)
+ 		goto out;
+ 
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 3b4c45bb..8ac7adf6 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -372,12 +372,14 @@ mt7996_mcu_rx_radar_detected(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	if (!mphy)
+ 		return;
+ 
+-	if (r->band_idx == MT_RX_SEL2)
++	if (r->band_idx == MT_RX_SEL2) {
++		dev->bg_nxt_freq = 0;
+ 		cfg80211_background_radar_event(mphy->hw->wiphy,
+ 						&dev->rdd2_chandef,
+ 						GFP_ATOMIC);
+-	else
++	} else {
+ 		ieee80211_radar_detected(mphy->hw);
++	}
+ 	dev->hw_pattern++;
+ }
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 70aafb29..439c57f4 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -397,6 +397,7 @@ struct mt7996_dev {
+ 	bool testmode_enable;
+ 	bool bin_file_mode;
+ 	u8 eeprom_mode;
++	u32 bg_nxt_freq;
+ 
+ 	bool ibf;
+ 	u8 fw_debug_wm;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0034-mtk-wifi-mt76-mt7996-add-support-spatial-reuse-debug.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0034-mtk-wifi-mt76-mt7996-add-support-spatial-reuse-debug.patch
deleted file mode 100644
index 9c80261..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0034-mtk-wifi-mt76-mt7996-add-support-spatial-reuse-debug.patch
+++ /dev/null
@@ -1,396 +0,0 @@
-From 5e5d779286a2c289480469f3b6d1b93479a4762c Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Mon, 10 Jul 2023 11:47:29 +0800
-Subject: [PATCH 034/116] mtk: wifi: mt76: mt7996: add support spatial reuse
- debug commands
-
-This commit adds the following debug commands in debugfs:
-1. sr_enable: enable/disable spatial reuse feature. Default is on.
-2. sr_enhanced_enable: enable/disable enhanced spatial reuse feature.
-Default is on. This feature is mtk proprietary feature.
-3. sr_stats: Check the Spatial reuse tx statistics.
-4. sr_scene_cond: Check the result of mtk scene detection algorithm. Mtk
-scene detection algorithm in firmware may decide whether current
-environment can SR Tx or not.
-
-To learn more details of these commands, please check:
-https://wiki.mediatek.inc/display/APKB/mt76+Phy+feature+debug+Cheetsheet#mt76PhyfeaturedebugCheetsheet-SpatialReuse
----
- mt76_connac_mcu.h    |   1 +
- mt7996/main.c        |   6 +++
- mt7996/mcu.c         |   8 ++++
- mt7996/mt7996.h      |   6 +++
- mt7996/mtk_debugfs.c |  82 ++++++++++++++++++++++++++++++++
- mt7996/mtk_mcu.c     | 111 +++++++++++++++++++++++++++++++++++++++++++
- mt7996/mtk_mcu.h     |  56 ++++++++++++++++++++++
- 7 files changed, 270 insertions(+)
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 750bb93..7f2daf7 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1041,6 +1041,7 @@ enum {
- 	MCU_UNI_EVENT_BSS_BEACON_LOSS = 0x0c,
- 	MCU_UNI_EVENT_SCAN_DONE = 0x0e,
- 	MCU_UNI_EVENT_RDD_REPORT = 0x11,
-+	MCU_UNI_EVENT_SR = 0x25,
- 	MCU_UNI_EVENT_ROC = 0x27,
- 	MCU_UNI_EVENT_TX_DONE = 0x2d,
- 	MCU_UNI_EVENT_BF = 0x33,
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 003bf3f..92b2834 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -6,6 +6,9 @@
- #include "mt7996.h"
- #include "mcu.h"
- #include "mac.h"
-+#ifdef CONFIG_MTK_DEBUG
-+#include "mtk_mcu.h"
-+#endif
- 
- static bool mt7996_dev_running(struct mt7996_dev *dev)
- {
-@@ -86,6 +89,9 @@ int mt7996_run(struct ieee80211_hw *hw)
- 		goto out;
- 
- #ifdef CONFIG_MTK_DEBUG
-+	phy->sr_enable = true;
-+	phy->enhanced_sr_enable = true;
-+
- 	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
- 						dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
- 
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 51c771b..7318842 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -717,6 +717,14 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
- 	case MCU_UNI_EVENT_WED_RRO:
- 		mt7996_mcu_wed_rro_event(dev, skb);
- 		break;
-+#ifdef CONFIG_MTK_DEBUG
-+	case MCU_UNI_EVENT_SR:
-+		mt7996_mcu_rx_sr_event(dev, skb);
-+		break;
-+#endif
-+	case MCU_UNI_EVENT_THERMAL:
-+		mt7996_mcu_rx_thermal_notify(dev, skb);
-+		break;
- #ifdef CONFIG_NL80211_TESTMODE
- 	case MCU_UNI_EVENT_TESTMODE_CTRL:
- 		mt7996_tm_rf_test_event(dev, skb);
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 97425a0..c06aae9 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -357,6 +357,10 @@ struct mt7996_phy {
- 	spinlock_t amnt_lock;
- 	struct mt7996_air_monitor_ctrl amnt_ctrl;
- #endif
-+#ifdef CONFIG_MTK_DEBUG
-+	bool sr_enable:1;
-+	bool enhanced_sr_enable:1;
-+#endif
- };
- 
- struct mt7996_dev {
-@@ -807,6 +811,8 @@ enum edcca_bw_id {
- #ifdef CONFIG_MTK_DEBUG
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
- int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
-+int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set);
-+void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb);
- #endif
- 
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index 2a9f213..3eb55a3 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -2836,6 +2836,83 @@ static int mt7996_show_eeprom_mode(struct seq_file *s, void *data)
- 	return 0;
- }
- 
-+static int
-+mt7996_sr_enable_get(void *data, u64 *val)
-+{
-+	struct mt7996_phy *phy = data;
-+
-+	*val = phy->sr_enable;
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_sr_enable_set(void *data, u64 val)
-+{
-+	struct mt7996_phy *phy = data;
-+	int ret;
-+
-+	if (!!val == phy->sr_enable)
-+		return 0;
-+
-+	ret = mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_CFG_SR_ENABLE, val, true);
-+	if (ret)
-+		return ret;
-+
-+	return mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_CFG_SR_ENABLE, 0, false);
-+}
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_sr_enable, mt7996_sr_enable_get,
-+			 mt7996_sr_enable_set, "%lld\n");
-+static int
-+mt7996_sr_enhanced_enable_get(void *data, u64 *val)
-+{
-+	struct mt7996_phy *phy = data;
-+
-+	*val = phy->enhanced_sr_enable;
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_sr_enhanced_enable_set(void *data, u64 val)
-+{
-+	struct mt7996_phy *phy = data;
-+	int ret;
-+
-+	if (!!val == phy->enhanced_sr_enable)
-+		return 0;
-+
-+	ret = mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_HW_ENHANCE_SR_ENABLE, val, true);
-+	if (ret)
-+		return ret;
-+
-+	return mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_HW_ENHANCE_SR_ENABLE, 0, false);
-+}
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_sr_enhanced_enable, mt7996_sr_enhanced_enable_get,
-+			 mt7996_sr_enhanced_enable_set, "%lld\n");
-+
-+static int
-+mt7996_sr_stats_show(struct seq_file *file, void *data)
-+{
-+	struct mt7996_phy *phy = file->private;
-+
-+	mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_HW_IND, 0, false);
-+
-+	return 0;
-+}
-+DEFINE_SHOW_ATTRIBUTE(mt7996_sr_stats);
-+
-+static int
-+mt7996_sr_scene_cond_show(struct seq_file *file, void *data)
-+{
-+	struct mt7996_phy *phy = file->private;
-+
-+	mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_SW_SD, 0, false);
-+
-+	return 0;
-+}
-+DEFINE_SHOW_ATTRIBUTE(mt7996_sr_scene_cond);
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- 	struct mt7996_dev *dev = phy->dev;
-@@ -2915,6 +2992,11 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- 	debugfs_create_u8("sku_disable", 0600, dir, &dev->dbg.sku_disable);
- 	debugfs_create_file("scs_enable", 0200, dir, phy, &fops_scs_enable);
- 
-+	debugfs_create_file("sr_enable", 0600, dir, phy, &fops_sr_enable);
-+	debugfs_create_file("sr_enhanced_enable", 0600, dir, phy, &fops_sr_enhanced_enable);
-+	debugfs_create_file("sr_stats", 0400, dir, phy, &mt7996_sr_stats_fops);
-+	debugfs_create_file("sr_scene_cond", 0400, dir, phy, &mt7996_sr_scene_cond_fops);
-+
- 	return 0;
- }
- 
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index 5c54d02..dbdf8d8 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -146,4 +146,115 @@ int mt7996_mcu_edcca_threshold_ctrl(struct mt7996_phy *phy, u8 *value, bool set)
- 	return 0;
- }
- 
-+int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set)
-+{
-+	struct {
-+		u8 band_idx;
-+		u8 _rsv[3];
-+
-+		__le16 tag;
-+		__le16 len;
-+
-+		__le32 val;
-+
-+	} __packed req = {
-+		.band_idx = phy->mt76->band_idx,
-+
-+		.tag = cpu_to_le16(action),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+
-+		.val = cpu_to_le32((u32) val),
-+	};
-+
-+	if (set)
-+		return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SR), &req,
-+					 sizeof(req), false);
-+	else
-+		return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD_QUERY(SR), &req,
-+					 sizeof(req), false);
-+}
-+
-+void mt7996_mcu_rx_sr_swsd(struct mt7996_dev *dev, struct sk_buff *skb)
-+{
-+#define SR_SCENE_DETECTION_TIMER_PERIOD_MS 500
-+	struct mt7996_mcu_sr_swsd_event *event;
-+	static const char * const rules[] = {"1 - NO CONNECTED", "2 - NO CONGESTION",
-+					     "3 - NO INTERFERENCE", "4 - SR ON"};
-+	u8 idx;
-+
-+	event = (struct mt7996_mcu_sr_swsd_event *)skb->data;
-+	idx = event->basic.band_idx;
-+
-+	dev_info(dev->mt76.dev, "Band index = %u\n", le16_to_cpu(event->basic.band_idx));
-+	dev_info(dev->mt76.dev, "Hit Rule = %s\n", rules[event->tlv[idx].rule]);
-+	dev_info(dev->mt76.dev, "Timer Period = %d(us)\n"
-+		 "Congestion Ratio  = %d.%1d%%\n",
-+		 SR_SCENE_DETECTION_TIMER_PERIOD_MS * 1000,
-+		 le32_to_cpu(event->tlv[idx].total_airtime_ratio) / 10,
-+		 le32_to_cpu(event->tlv[idx].total_airtime_ratio) % 10);
-+	dev_info(dev->mt76.dev,
-+		 "Total Airtime = %d(us)\n"
-+		 "ChBusy = %d\n"
-+		 "SrTx = %d\n"
-+		 "OBSS = %d\n"
-+		 "MyTx = %d\n"
-+		 "MyRx = %d\n"
-+		 "Interference Ratio = %d.%1d%%\n",
-+		 le32_to_cpu(event->tlv[idx].total_airtime),
-+		 le32_to_cpu(event->tlv[idx].channel_busy_time),
-+		 le32_to_cpu(event->tlv[idx].sr_tx_airtime),
-+		 le32_to_cpu(event->tlv[idx].obss_airtime),
-+		 le32_to_cpu(event->tlv[idx].my_tx_airtime),
-+		 le32_to_cpu(event->tlv[idx].my_rx_airtime),
-+		 le32_to_cpu(event->tlv[idx].obss_airtime_ratio) / 10,
-+		 le32_to_cpu(event->tlv[idx].obss_airtime_ratio) % 10);
-+}
-+
-+void mt7996_mcu_rx_sr_hw_indicator(struct mt7996_dev *dev, struct sk_buff *skb)
-+{
-+	struct mt7996_mcu_sr_hw_ind_event *event;
-+
-+	event = (struct mt7996_mcu_sr_hw_ind_event *)skb->data;
-+
-+	dev_info(dev->mt76.dev, "Inter PPDU Count = %u\n",
-+		 le16_to_cpu(event->inter_bss_ppdu_cnt));
-+	dev_info(dev->mt76.dev, "SR Valid Count = %u\n",
-+		 le16_to_cpu(event->non_srg_valid_cnt));
-+	dev_info(dev->mt76.dev, "SR Tx Count = %u\n",
-+		 le32_to_cpu(event->sr_ampdu_mpdu_cnt));
-+	dev_info(dev->mt76.dev, "SR Tx Acked Count = %u\n",
-+		 le32_to_cpu(event->sr_ampdu_mpdu_acked_cnt));
-+}
-+
-+void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb)
-+{
-+	struct mt76_phy *mphy = &dev->mt76.phy;
-+	struct mt7996_phy *phy;
-+	struct mt7996_mcu_sr_common_event *event;
-+
-+	event = (struct mt7996_mcu_sr_common_event *)skb->data;
-+	mphy = dev->mt76.phys[event->basic.band_idx];
-+	if (!mphy)
-+		return;
-+
-+	phy = (struct mt7996_phy *)mphy->priv;
-+
-+	switch (le16_to_cpu(event->basic.tag)) {
-+	case UNI_EVENT_SR_CFG_SR_ENABLE:
-+		phy->sr_enable = le32_to_cpu(event->value) ? true : false;
-+		break;
-+	case UNI_EVENT_SR_HW_ESR_ENABLE:
-+		phy->enhanced_sr_enable = le32_to_cpu(event->value) ? true : false;
-+		break;
-+	case UNI_EVENT_SR_SW_SD:
-+		mt7996_mcu_rx_sr_swsd(dev, skb);
-+		break;
-+	case UNI_EVENT_SR_HW_IND:
-+		mt7996_mcu_rx_sr_hw_indicator(dev, skb);
-+		break;
-+	default:
-+		dev_info(dev->mt76.dev, "Unknown SR event tag %d\n",
-+			 le16_to_cpu(event->basic.tag));
-+	}
-+}
- #endif
-diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
-index 36a58ad..098e63a 100644
---- a/mt7996/mtk_mcu.h
-+++ b/mt7996/mtk_mcu.h
-@@ -121,6 +121,62 @@ enum {
- 	EDCCA_JAPAN = 3
- };
- 
-+enum {
-+	UNI_EVENT_SR_CFG_SR_ENABLE = 0x1,
-+	UNI_EVENT_SR_SW_SD = 0x83,
-+	UNI_EVENT_SR_HW_IND = 0xC9,
-+	UNI_EVENT_SR_HW_ESR_ENABLE = 0xD8,
-+};
-+enum {
-+	UNI_CMD_SR_CFG_SR_ENABLE = 0x1,
-+	UNI_CMD_SR_SW_SD = 0x84,
-+	UNI_CMD_SR_HW_IND = 0xCB,
-+	UNI_CMD_SR_HW_ENHANCE_SR_ENABLE = 0xDA,
-+};
-+
-+struct mt7996_mcu_sr_basic_event {
-+	struct mt7996_mcu_rxd rxd;
-+
-+	u8 band_idx;
-+	u8 _rsv[3];
-+
-+	__le16 tag;
-+	__le16 len;
-+};
-+
-+struct sr_sd_tlv {
-+	u8 _rsv[16];
-+	__le32 sr_tx_airtime;
-+	__le32 obss_airtime;
-+	__le32 my_tx_airtime;
-+	__le32 my_rx_airtime;
-+	__le32 channel_busy_time;
-+	__le32 total_airtime;
-+	__le32 total_airtime_ratio;
-+	__le32 obss_airtime_ratio;
-+	u8 rule;
-+	u8 _rsv2[59];
-+} __packed;
-+
-+struct mt7996_mcu_sr_swsd_event {
-+	struct mt7996_mcu_sr_basic_event basic;
-+	struct sr_sd_tlv tlv[3];
-+} __packed;
-+
-+struct mt7996_mcu_sr_common_event {
-+	struct mt7996_mcu_sr_basic_event basic;
-+	__le32 value;
-+};
-+
-+struct mt7996_mcu_sr_hw_ind_event {
-+	struct mt7996_mcu_sr_basic_event basic;
-+	__le16 non_srg_valid_cnt;
-+	u8 _rsv[4];
-+	__le16 inter_bss_ppdu_cnt;
-+	u8 _rsv2[4];
-+	__le32 sr_ampdu_mpdu_cnt;
-+	__le32 sr_ampdu_mpdu_acked_cnt;
-+};
- #endif
- 
- #endif
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0035-mtk-mt76-mt7996-refactor-eeprom-loading-flow-for-sku.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0035-mtk-mt76-mt7996-refactor-eeprom-loading-flow-for-sku.patch
new file mode 100644
index 0000000..8444a8e
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0035-mtk-mt76-mt7996-refactor-eeprom-loading-flow-for-sku.patch
@@ -0,0 +1,468 @@
+From 13b9c0e271c358a45444d35e97ad4080c3933e78 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 11 Mar 2024 10:43:03 +0800
+Subject: [PATCH 035/199] mtk: mt76: mt7996: refactor eeprom loading flow for
+ sku checking
+
+Add eeprom sku checking mechanism to avoid using the
+wrong eeprom in flash/binfile mode
+The fields listed below will be checked by comparing the loaded eeprom to the default bin
+1. FEM type
+2. MAC address (warning for using default MAC address)
+3. RF path & streams
+   (to distinguish cases such as BE7200 4i5i, BE6500 3i5i, and BE5040 2i3i)
+
+1. Reset eeprom content before loading efuse
+   eeprom array might contain incomplete data read from flash or
+   binfile, which is not overwritten since this block is invalid
+   in efuse.
+2. Remove testmode default bin since it is unnecessary
+   Not used in logan. Directly load normal mode default bin is fine.
+   Also, this way is better since we don't need to put testmode default
+   eeprom for each sku (especially kite's sku) in our SDK.
+3. Set testmode_en field for default bin mode for consistency sake
+
+1.
+Fix efuse mode txpower = 0 issue
+This fix might be changed if fw supports efuse merge for buffer mode = EE_MODE_EFUSE
+2.
+Add Eagle BE19000 ifem default bin, add Eagle default bin bootstrip
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/eeprom.c      | 207 ++++++++++++++++++++++++-------------------
+ mt7996/eeprom.h      |  32 +++++++
+ mt7996/mcu.c         |  18 +---
+ mt7996/mt7996.h      |   1 +
+ mt7996/mtk_debugfs.c |   2 +-
+ 5 files changed, 150 insertions(+), 110 deletions(-)
+
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 5dc55646..f4641321 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -50,54 +50,84 @@ const u32 dpd_6g_bw320_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw320);
+ 
+ static int mt7996_check_eeprom(struct mt7996_dev *dev)
+ {
+-#define FEM_INT				0
+-#define FEM_EXT				3
+ 	u8 *eeprom = dev->mt76.eeprom.data;
+-	u8 i, fem[__MT_MAX_BAND], fem_type;
+ 	u16 val = get_unaligned_le16(eeprom);
+ 
+-	for (i = 0; i < __MT_MAX_BAND; i++)
+-		fem[i] = eeprom[MT_EE_WIFI_CONF + 6 + i] & MT_EE_WIFI_PA_LNA_CONFIG;
+-
+ 	switch (val) {
+ 	case 0x7990:
+ 		return is_mt7996(&dev->mt76) ? 0 : -EINVAL;
+ 	case 0x7992:
+-		if (dev->fem_type == MT7996_FEM_UNSET)
+-			return is_mt7992(&dev->mt76) ? 0 : -EINVAL;
+-
+-		if (fem[0] == FEM_EXT && fem[1] == FEM_EXT)
+-			fem_type = MT7996_FEM_EXT;
+-		else if (fem[0] == FEM_INT && fem[1] == FEM_INT)
+-			fem_type = MT7996_FEM_INT;
+-		else if (fem[0] == FEM_INT && fem[1] == FEM_EXT)
+-			fem_type = MT7996_FEM_MIX;
+-		else
+-			return -EINVAL;
+-
+-		return (is_mt7992(&dev->mt76) ? 0 : -EINVAL) |
+-		       (dev->fem_type == fem_type ? 0 : -EINVAL);
++		return is_mt7992(&dev->mt76) ? 0 : -EINVAL;
+ 	default:
+ 		return -EINVAL;
+ 	}
+ }
+ 
+-const char *mt7996_eeprom_name(struct mt7996_dev *dev)
++static int mt7996_check_eeprom_sku(struct mt7996_dev *dev, const u8 *dflt)
+ {
+-	if (dev->bin_file_mode)
+-		return dev->mt76.bin_file_name;
++#define FEM_INT				0
++#define FEM_EXT				3
++	u8 *eeprom = dev->mt76.eeprom.data;
++	u8 i, fem[__MT_MAX_BAND], fem_type;
++	u16 mac_addr[__MT_MAX_BAND] = {MT_EE_MAC_ADDR, MT_EE_MAC_ADDR2, MT_EE_MAC_ADDR3};
++	int max_band = is_mt7996(&dev->mt76) ? __MT_MAX_BAND : 2;
++
++	if (dev->fem_type == MT7996_FEM_UNSET)
++		return -EINVAL;
++
++	/* FEM type */
++	for (i = 0; i < max_band; i++)
++		fem[i] = eeprom[MT_EE_WIFI_CONF + 6 + i] & MT_EE_WIFI_PA_LNA_CONFIG;
++
++	if (fem[0] == FEM_EXT && fem[1] == FEM_EXT)
++		fem_type = MT7996_FEM_EXT;
++	else if (fem[0] == FEM_INT && fem[1] == FEM_INT)
++		fem_type = MT7996_FEM_INT;
++	else if (fem[0] == FEM_INT && fem[1] == FEM_EXT)
++		fem_type = MT7996_FEM_MIX;
++	else
++		return -EINVAL;
+ 
+-	if (dev->testmode_enable) {
+-		if (is_mt7992(&dev->mt76))
+-			return MT7992_EEPROM_DEFAULT_TM;
+-		else
+-			return MT7996_EEPROM_DEFAULT_TM;
++	if (dev->fem_type != fem_type)
++		return -EINVAL;
++
++	/* RF path & stream */
++	for (i = 0; i < max_band; i++) {
++		u8 path, rx_path, nss;
++		u8 dflt_path, dflt_rx_path, dflt_nss;
++
++		/* MAC address */
++		if (ether_addr_equal(eeprom + mac_addr[i], dflt + mac_addr[i]))
++			dev_warn(dev->mt76.dev,
++				 "Currently using default MAC address for band %d\n", i);
++
++		mt7996_parse_eeprom_stream(eeprom, i, &path, &rx_path, &nss);
++		mt7996_parse_eeprom_stream(dflt, i, &dflt_path, &dflt_rx_path, &dflt_nss);
++		if (path > dflt_path || rx_path > dflt_rx_path || nss > dflt_nss) {
++			dev_err(dev->mt76.dev,
++				"Invalid path/stream configuration for band %d\n", i);
++			return -EINVAL;
++		} else if (path < dflt_path || rx_path < dflt_rx_path || nss < dflt_nss) {
++			dev_warn(dev->mt76.dev,
++				 "Restricted path/stream configuration for band %d\n", i);
++			dev_warn(dev->mt76.dev,
++				 "path: %u/%u, rx_path: %u/%u, nss: %u/%u\n",
++				 path, dflt_path, rx_path, dflt_rx_path, nss, dflt_nss);
++		}
+ 	}
+ 
++	return 0;
++}
++
++const char *mt7996_eeprom_name(struct mt7996_dev *dev)
++{
+ 	switch (mt76_chip(&dev->mt76)) {
+ 	case 0x7990:
+ 		if (dev->chip_sku == MT7996_SKU_404)
+ 			return MT7996_EEPROM_DEFAULT_404;
++
++		if (dev->fem_type == MT7996_FEM_INT)
++			return MT7996_EEPROM_DEFAULT_INT;
+ 		return MT7996_EEPROM_DEFAULT;
+ 	case 0x7992:
+ 		if (dev->chip_sku == MT7992_SKU_23) {
+@@ -148,21 +178,18 @@ mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band)
+ }
+ 
+ static int
+-mt7996_eeprom_load_default(struct mt7996_dev *dev)
++mt7996_eeprom_load_bin(struct mt7996_dev *dev)
+ {
+ 	u8 *eeprom = dev->mt76.eeprom.data;
+ 	const struct firmware *fw = NULL;
+ 	int ret;
+ 
+-	ret = request_firmware(&fw, mt7996_eeprom_name(dev), dev->mt76.dev);
++	ret = request_firmware(&fw, dev->mt76.bin_file_name, dev->mt76.dev);
+ 	if (ret)
+ 		return ret;
+ 
+ 	if (!fw || !fw->data) {
+-		if (dev->bin_file_mode)
+-			dev_err(dev->mt76.dev, "Invalid bin (bin file mode)\n");
+-		else
+-			dev_err(dev->mt76.dev, "Invalid default bin\n");
++		dev_err(dev->mt76.dev, "Invalid bin %s\n", dev->mt76.bin_file_name);
+ 		ret = -EINVAL;
+ 		goto out;
+ 	}
+@@ -180,7 +207,7 @@ static int mt7996_eeprom_load_flash(struct mt7996_dev *dev)
+ {
+ 	int ret = 1;
+ 
+-	/* return > 0 for load success, return 0 for load failed, return < 0 for non memory */
++	/* return > 0 for load success, return 0 for load failed, return < 0 for no memory */
+ 	dev->bin_file_mode = mt76_check_bin_file_mode(&dev->mt76);
+ 	if (dev->bin_file_mode) {
+ 		dev->mt76.eeprom.size = MT7996_EEPROM_SIZE;
+@@ -189,15 +216,15 @@ static int mt7996_eeprom_load_flash(struct mt7996_dev *dev)
+ 		if (!dev->mt76.eeprom.data)
+ 			return -ENOMEM;
+ 
+-		if (mt7996_eeprom_load_default(dev))
+-			return 0;
+-
+-		if (mt7996_check_eeprom(dev))
++		if (mt7996_eeprom_load_bin(dev))
+ 			return 0;
+ 	} else {
+ 		ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
+ 	}
+ 
++	if (mt7996_check_eeprom(dev))
++		return 0;
++
+ 	return ret;
+ }
+ 
+@@ -206,30 +233,30 @@ int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev)
+ 	u8 *eeprom;
+ 	int ret;
+ 
++	dev->testmode_enable = testmode_enable;
++
+ 	/* load eeprom in flash or bin file mode to determine fw mode */
+ 	ret = mt7996_eeprom_load_flash(dev);
++	if (ret <= 0)
++		goto out;
+ 
+-	if (ret < 0)
+-		return ret;
+-
+-	if (ret) {
+-		dev->flash_mode = true;
+-		dev->eeprom_mode = dev->bin_file_mode ? BIN_FILE_MODE : FLASH_MODE;
+-		eeprom = dev->mt76.eeprom.data;
+-		/* testmode enable priority: eeprom field > module parameter */
+-		dev->testmode_enable = !mt7996_check_eeprom(dev) ? eeprom[MT_EE_TESTMODE_EN] :
+-								   testmode_enable;
+-	}
++	dev->flash_mode = true;
++	dev->eeprom_mode = dev->bin_file_mode ? BIN_FILE_MODE : FLASH_MODE;
++	eeprom = dev->mt76.eeprom.data;
++	/* testmode enable priority: eeprom field > module parameter */
++	dev->testmode_enable = eeprom[MT_EE_TESTMODE_EN];
+ 
++out:
+ 	return ret;
+ }
+ 
+ static int mt7996_eeprom_load(struct mt7996_dev *dev)
+ {
++	const struct firmware *fw = NULL;
+ 	int ret;
+-	u8 free_block_num;
+ 	u32 block_num, i;
+ 	u32 eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE;
++	u8 free_block_num, *eeprom = dev->mt76.eeprom.data;
+ 
+ 	/* flash or bin file mode eeprom is loaded before mcu init */
+ 	if (!dev->flash_mode) {
+@@ -239,19 +266,49 @@ static int mt7996_eeprom_load(struct mt7996_dev *dev)
+ 
+ 		/* efuse info isn't enough */
+ 		if (free_block_num >= 59)
+-			return -EINVAL;
++			goto dflt;
++
++		memset(eeprom, 0, MT7996_EEPROM_SIZE);
++		/* check if efuse contains valid eeprom data */
++		if (mt7996_mcu_get_eeprom(dev, 0, NULL) ||
++		    mt7996_check_eeprom(dev))
++			goto dflt;
+ 
+ 		/* read eeprom data from efuse */
+ 		block_num = DIV_ROUND_UP(MT7996_EEPROM_SIZE, eeprom_blk_size);
+-		for (i = 0; i < block_num; i++) {
++		for (i = 1; i < block_num; i++) {
+ 			ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size, NULL);
+ 			if (ret && ret != -EINVAL)
+-				return ret;
++				goto dflt;
+ 		}
+ 		dev->eeprom_mode = EFUSE_MODE;
+ 	}
+ 
+-	return mt7996_check_eeprom(dev);
++dflt:
++	ret = request_firmware(&fw, mt7996_eeprom_name(dev), dev->mt76.dev);
++	if (ret)
++		return ret;
++
++	if (!fw || !fw->data) {
++		dev_err(dev->mt76.dev, "Invalid default bin\n");
++		ret = -EINVAL;
++		goto out;
++	}
++
++	if (dev->eeprom_mode && !mt7996_check_eeprom_sku(dev, fw->data)) {
++		ret = 0;
++		goto out;
++	}
++
++	memcpy(eeprom, fw->data, MT7996_EEPROM_SIZE);
++	dev->bin_file_mode = false;
++	dev->flash_mode = true;
++	dev->eeprom_mode = DEFAULT_BIN_MODE;
++	eeprom[MT_EE_TESTMODE_EN] = dev->testmode_enable;
++	dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n");
++out:
++	release_firmware(fw);
++	return ret;
+ }
+ 
+ static int mt7996_eeprom_parse_efuse_hw_cap(struct mt7996_dev *dev)
+@@ -323,32 +380,7 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy)
+ 	int max_path = 5, max_nss = 4;
+ 	int ret;
+ 
+-	switch (band_idx) {
+-	case MT_BAND1:
+-		path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND1,
+-				 eeprom[MT_EE_WIFI_CONF + 2]);
+-		rx_path = FIELD_GET(MT_EE_WIFI_CONF3_RX_PATH_BAND1,
+-				    eeprom[MT_EE_WIFI_CONF + 3]);
+-		nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND1,
+-				eeprom[MT_EE_WIFI_CONF + 5]);
+-		break;
+-	case MT_BAND2:
+-		path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND2,
+-				 eeprom[MT_EE_WIFI_CONF + 2]);
+-		rx_path = FIELD_GET(MT_EE_WIFI_CONF4_RX_PATH_BAND2,
+-				    eeprom[MT_EE_WIFI_CONF + 4]);
+-		nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND2,
+-				eeprom[MT_EE_WIFI_CONF + 5]);
+-		break;
+-	default:
+-		path = FIELD_GET(MT_EE_WIFI_CONF1_TX_PATH_BAND0,
+-				 eeprom[MT_EE_WIFI_CONF + 1]);
+-		rx_path = FIELD_GET(MT_EE_WIFI_CONF3_RX_PATH_BAND0,
+-				    eeprom[MT_EE_WIFI_CONF + 3]);
+-		nss = FIELD_GET(MT_EE_WIFI_CONF4_STREAM_NUM_BAND0,
+-				eeprom[MT_EE_WIFI_CONF + 4]);
+-		break;
+-	}
++	mt7996_parse_eeprom_stream(eeprom, band_idx, &path, &rx_path, &nss);
+ 
+ 	if (!path || path > max_path)
+ 		path = max_path;
+@@ -437,17 +469,8 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ 		return ret;
+ 
+ 	ret = mt7996_eeprom_load(dev);
+-	if (ret < 0) {
+-		if (ret != -EINVAL)
+-			return ret;
+-
+-		dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n");
+-		dev->bin_file_mode = false;
+-		dev->eeprom_mode = DEFAULT_BIN_MODE;
+-		ret = mt7996_eeprom_load_default(dev);
+-		if (ret)
+-			return ret;
+-	}
++	if (ret)
++		return ret;
+ 
+ 	ret = mt7996_eeprom_load_precal(dev);
+ 	if (ret)
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index 8f0f87b6..03a4fd07 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -132,6 +132,38 @@ mt7996_get_channel_group_6g(int channel)
+ 	return DIV_ROUND_UP(channel - 29, 32);
+ }
+ 
++static inline void
++mt7996_parse_eeprom_stream(const u8 *eep, int band_idx,
++			   u8 *path, u8 *rx_path, u8 *nss)
++{
++	switch (band_idx) {
++	case MT_BAND1:
++		*path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND1,
++				  eep[MT_EE_WIFI_CONF + 2]);
++		*rx_path = FIELD_GET(MT_EE_WIFI_CONF3_RX_PATH_BAND1,
++				     eep[MT_EE_WIFI_CONF + 3]);
++		*nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND1,
++				 eep[MT_EE_WIFI_CONF + 5]);
++		break;
++	case MT_BAND2:
++		*path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND2,
++				  eep[MT_EE_WIFI_CONF + 2]);
++		*rx_path = FIELD_GET(MT_EE_WIFI_CONF4_RX_PATH_BAND2,
++				     eep[MT_EE_WIFI_CONF + 4]);
++		*nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND2,
++				 eep[MT_EE_WIFI_CONF + 5]);
++		break;
++	default:
++		*path = FIELD_GET(MT_EE_WIFI_CONF1_TX_PATH_BAND0,
++				  eep[MT_EE_WIFI_CONF + 1]);
++		*rx_path = FIELD_GET(MT_EE_WIFI_CONF3_RX_PATH_BAND0,
++				     eep[MT_EE_WIFI_CONF + 3]);
++		*nss = FIELD_GET(MT_EE_WIFI_CONF4_STREAM_NUM_BAND0,
++				 eep[MT_EE_WIFI_CONF + 4]);
++		break;
++	}
++}
++
+ enum mt7996_sku_rate_group {
+ 	SKU_CCK,
+ 	SKU_OFDM,
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 8ac7adf6..7542953f 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3510,7 +3510,7 @@ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag)
+ 				 &req, sizeof(req), true);
+ }
+ 
+-static int mt7996_mcu_set_eeprom_flash(struct mt7996_dev *dev)
++int mt7996_mcu_set_eeprom(struct mt7996_dev *dev)
+ {
+ #define MAX_PAGE_IDX_MASK	GENMASK(7, 5)
+ #define PAGE_IDX_MASK		GENMASK(4, 2)
+@@ -3555,22 +3555,6 @@ static int mt7996_mcu_set_eeprom_flash(struct mt7996_dev *dev)
+ 	return 0;
+ }
+ 
+-int mt7996_mcu_set_eeprom(struct mt7996_dev *dev)
+-{
+-	struct mt7996_mcu_eeprom req = {
+-		.tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE),
+-		.len = cpu_to_le16(sizeof(req) - 4),
+-		.buffer_mode = EE_MODE_EFUSE,
+-		.format = EE_FORMAT_WHOLE
+-	};
+-
+-	if (dev->flash_mode)
+-		return mt7996_mcu_set_eeprom_flash(dev);
+-
+-	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(EFUSE_CTRL),
+-				 &req, sizeof(req), true);
+-}
+-
+ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf)
+ {
+ 	struct mt7996_mcu_eeprom_info req = {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 439c57f4..2994c1a5 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -54,6 +54,7 @@
+ #define MT7992_ROM_PATCH_23		"mediatek/mt7996/mt7992_rom_patch_23.bin"
+ 
+ #define MT7996_EEPROM_DEFAULT		"mediatek/mt7996/mt7996_eeprom.bin"
++#define MT7996_EEPROM_DEFAULT_INT	"mediatek/mt7996/mt7996_eeprom_2i5i6i.bin"
+ #define MT7996_EEPROM_DEFAULT_404	"mediatek/mt7996/mt7996_eeprom_dual_404.bin"
+ #define MT7992_EEPROM_DEFAULT		"mediatek/mt7996/mt7992_eeprom_2e5e.bin"
+ #define MT7992_EEPROM_DEFAULT_INT	"mediatek/mt7996/mt7992_eeprom_2i5i.bin"
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index c096fb6c..053005dd 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2826,7 +2826,7 @@ static int mt7996_show_eeprom_mode(struct seq_file *s, void *data)
+ 			seq_printf(s, "   flash mode\n");
+ 		break;
+ 	case BIN_FILE_MODE:
+-		seq_printf(s, "   bin file mode\n   filename = %s\n", mt7996_eeprom_name(dev));
++		seq_printf(s, "   bin file mode\n   filename = %s\n", dev->mt76.bin_file_name);
+ 		break;
+ 	default:
+ 		break;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0035-mtk-wifi-mt76-mt7996-Establish-BA-in-VO-queue.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0035-mtk-wifi-mt76-mt7996-Establish-BA-in-VO-queue.patch
deleted file mode 100644
index 1103857..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0035-mtk-wifi-mt76-mt7996-Establish-BA-in-VO-queue.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From e6c2efee109792cf81fa79a0b0bf56fdbc1f4787 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Tue, 1 Aug 2023 16:02:28 +0800
-Subject: [PATCH 035/116] mtk: wifi: mt76: mt7996: Establish BA in VO queue
-
----
- mt7996/mac.c | 2 --
- 1 file changed, 2 deletions(-)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 1c1b3eb..4e52aa1 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1032,8 +1032,6 @@ mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb)
- 		return;
- 
- 	tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
--	if (tid >= 6) /* skip VO queue */
--		return;
- 
- 	if (is_8023) {
- 		fc = IEEE80211_FTYPE_DATA |
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0036-mtk-mt76-mt7996-add-vendor-commands-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0036-mtk-mt76-mt7996-add-vendor-commands-support.patch
new file mode 100644
index 0000000..99e6cf8
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0036-mtk-mt76-mt7996-add-vendor-commands-support.patch
@@ -0,0 +1,1428 @@
+From 77147d23626a24c3f3dff7a5afb57fc155a8e092 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Tue, 13 Dec 2022 15:17:43 +0800
+Subject: [PATCH 036/199] mtk: mt76: mt7996: add vendor commands support
+
+mtk: wifi: mt76: fix muru_onoff as all enabled by default
+
+Fix muru_onoff default value as 0xF, which means all MU & RU are
+enabled. The purpose of this commit is to align muru_onoff value with
+hostapd and mt76 driver
+
+mtk: wifi: mt76: mt7996: add vendor cmd to get available color bitmap
+
+Add a vendor cmd to notify user space available color bitmap.
+The OBSS BSS color bitmap is maintained in mac80211, so mt76 will make use of that.
+
+mtk: wifi: mt76: mt7996: add vendor subcmd EDCCA ctrl enable
+
+mtk: wifi: mt76: mt7996: add ibf control vendor cmd
+
+mtk: wifi: mt76: mt7996: add three wire pta support
+
+three wire enable bit 0 & 1 for EXT0 & EXT1, respectively
+
+mtk: wifi: mt76: mt7996: Add static pp vendor support
+
+Add static pp vendor support for setting pp mode.
+User can enable/disable preamble puncture feature through hostapd
+configuration and hostapd_cli. Driver can receive the nl80211 vendor
+message and convert it to mcu commands.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
+Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ mt76_connac_mcu.h |   2 +
+ mt7996/Makefile   |   3 +-
+ mt7996/init.c     |   9 +
+ mt7996/mac.c      |   4 +
+ mt7996/main.c     |   4 +
+ mt7996/mcu.c      |  37 ++-
+ mt7996/mcu.h      |  14 +
+ mt7996/mt7996.h   |  53 +++
+ mt7996/mtk_mcu.c  |  87 +++++
+ mt7996/mtk_mcu.h  |  15 +
+ mt7996/vendor.c   | 805 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/vendor.h   | 153 +++++++++
+ 12 files changed, 1180 insertions(+), 6 deletions(-)
+ create mode 100644 mt7996/vendor.c
+ create mode 100644 mt7996/vendor.h
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index ea60e19c..5a1b4d0d 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1264,6 +1264,7 @@ enum {
+ 	MCU_UNI_CMD_REG_ACCESS = 0x0d,
+ 	MCU_UNI_CMD_CHIP_CONFIG = 0x0e,
+ 	MCU_UNI_CMD_POWER_CTRL = 0x0f,
++	MCU_UNI_CMD_CFG_SMESH = 0x10,
+ 	MCU_UNI_CMD_RX_HDR_TRANS = 0x12,
+ 	MCU_UNI_CMD_SER = 0x13,
+ 	MCU_UNI_CMD_TWT = 0x14,
+@@ -1296,6 +1297,7 @@ enum {
+ 	MCU_UNI_CMD_PER_STA_INFO = 0x6d,
+ 	MCU_UNI_CMD_ALL_STA_INFO = 0x6e,
+ 	MCU_UNI_CMD_ASSERT_DUMP = 0x6f,
++	MCU_UNI_CMD_PTA_3WIRE_CTRL = 0x78,
+ };
+ 
+ enum {
+diff --git a/mt7996/Makefile b/mt7996/Makefile
+index 7bb17f44..6643c7a3 100644
+--- a/mt7996/Makefile
++++ b/mt7996/Makefile
+@@ -1,11 +1,12 @@
+ # SPDX-License-Identifier: ISC
+ EXTRA_CFLAGS += -DCONFIG_MT76_LEDS
+ EXTRA_CFLAGS += -DCONFIG_MTK_DEBUG
++EXTRA_CFLAGS += -DCONFIG_MTK_VENDOR
+ 
+ obj-$(CONFIG_MT7996E) += mt7996e.o
+ 
+ mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
+-	     debugfs.o mmio.o
++	     debugfs.o mmio.o vendor.o
+ 
+ mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o
+ mt7996e-$(CONFIG_NL80211_TESTMODE) += testmode.o
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 3b816070..0cf054df 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -380,6 +380,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ 
+ 	phy->slottime = 9;
+ 	phy->beacon_rate = -1;
++	phy->muru_onoff = OFDMA_UL | OFDMA_DL | MUMIMO_DL | MUMIMO_UL;
+ 
+ 	hw->sta_data_size = sizeof(struct mt7996_sta);
+ 	hw->vif_data_size = sizeof(struct mt7996_vif);
+@@ -631,6 +632,10 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 	if (ret)
+ 		goto error;
+ 
++#ifdef CONFIG_MTK_VENDOR
++	mt7996_vendor_register(phy);
++#endif
++
+ 	ret = mt76_register_phy(mphy, true, mt76_rates,
+ 				ARRAY_SIZE(mt76_rates));
+ 	if (ret)
+@@ -1447,6 +1452,10 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ 	dev->mt76.test_ops = &mt7996_testmode_ops;
+ #endif
+ 
++#ifdef CONFIG_MTK_VENDOR
++	mt7996_vendor_register(&dev->phy);
++#endif
++
+ 	ret = mt76_register_device(&dev->mt76, true, mt76_rates,
+ 				   ARRAY_SIZE(mt76_rates));
+ 	if (ret)
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index fd8bce0e..f0c0db75 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -679,6 +679,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ 			if (ieee80211_has_a4(fc) && is_mesh && status->amsdu)
+ 				*qos &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+ 		}
++#ifdef CONFIG_MTK_VENDOR
++		if (phy->amnt_ctrl.enable && !ieee80211_is_beacon(fc))
++			mt7996_vendor_amnt_fill_rx(phy, skb);
++#endif
+ 	} else {
+ 		status->flag |= RX_FLAG_8023;
+ 		mt7996_wed_check_ppe(dev, &dev->mt76.q_rx[q], msta, skb,
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 5b1984ac..a51fc696 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -764,6 +764,10 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 	mt7996_mac_wtbl_update(dev, idx,
+ 			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+ 
++#ifdef CONFIG_MTK_VENDOR
++	mt7996_vendor_amnt_sta_remove(mvif->phy, sta);
++#endif
++
+ 	ret = mt7996_mcu_add_sta(dev, vif, sta, true, true);
+ 	if (ret)
+ 		return ret;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 7542953f..a08b24f6 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1378,6 +1378,8 @@ static void
+ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 			struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+ {
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_phy *phy = mvif->phy;
+ 	struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
+ 	struct sta_rec_muru *muru;
+ 	struct tlv *tlv;
+@@ -1389,11 +1391,14 @@ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MURU, sizeof(*muru));
+ 
+ 	muru = (struct sta_rec_muru *)tlv;
+-	muru->cfg.mimo_dl_en = vif->bss_conf.eht_mu_beamformer ||
+-			       vif->bss_conf.he_mu_beamformer ||
+-			       vif->bss_conf.vht_mu_beamformer ||
+-			       vif->bss_conf.vht_mu_beamformee;
+-	muru->cfg.ofdma_dl_en = true;
++	muru->cfg.mimo_dl_en = (vif->bss_conf.eht_mu_beamformer ||
++				vif->bss_conf.he_mu_beamformer ||
++				vif->bss_conf.vht_mu_beamformer ||
++				vif->bss_conf.vht_mu_beamformee) &&
++			       !!(phy->muru_onoff & MUMIMO_DL);
++	muru->cfg.mimo_ul_en = !!(phy->muru_onoff & MUMIMO_UL);
++	muru->cfg.ofdma_dl_en = !!(phy->muru_onoff & OFDMA_DL);
++	muru->cfg.ofdma_ul_en = !!(phy->muru_onoff & OFDMA_UL);
+ 
+ 	if (sta->deflink.vht_cap.vht_supported)
+ 		muru->mimo_dl.vht_mu_bfee =
+@@ -4966,3 +4971,25 @@ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
+ 	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SCS),
+ 				 &req, sizeof(req), false);
+ }
++
++#ifdef CONFIG_MTK_VENDOR
++void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
++{
++	u8 mode, val;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_phy *phy =  mvif->phy;
++
++	mode = FIELD_GET(RATE_CFG_MODE, *((u32 *)data));
++	val = FIELD_GET(RATE_CFG_VAL, *((u32 *)data));
++
++	switch (mode) {
++	case RATE_PARAM_AUTO_MU:
++		if (val < 0 || val > 15) {
++			printk("Wrong value! The value is between 0-15.\n");
++			break;
++		}
++		phy->muru_onoff = val;
++		break;
++	}
++}
++#endif
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 1ed05d7e..f827de9e 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -754,8 +754,20 @@ enum {
+ 	RATE_PARAM_FIXED_MCS,
+ 	RATE_PARAM_FIXED_GI = 11,
+ 	RATE_PARAM_AUTO = 20,
++#ifdef CONFIG_MTK_VENDOR
++	RATE_PARAM_AUTO_MU = 32,
++#endif
+ };
+ 
++#define RATE_CFG_MODE	GENMASK(15, 8)
++#define RATE_CFG_VAL	GENMASK(7, 0)
++
++/* MURU */
++#define OFDMA_DL                       BIT(0)
++#define OFDMA_UL                       BIT(1)
++#define MUMIMO_DL                      BIT(2)
++#define MUMIMO_UL                      BIT(3)
++
+ enum {
+ 	BF_SOUNDING_ON = 1,
+ 	BF_HW_EN_UPDATE = 17,
+@@ -833,6 +845,8 @@ mt7996_get_power_bound(struct mt7996_phy *phy, s8 txpower)
+ 
+ enum {
+ 	UNI_BAND_CONFIG_RADIO_ENABLE,
++	UNI_BAND_CONFIG_EDCCA_ENABLE = 0x05,
++	UNI_BAND_CONFIG_EDCCA_THRESHOLD = 0x06,
+ 	UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08,
+ };
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 2994c1a5..f0604368 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -258,6 +258,34 @@ struct mt7996_wed_rro_session_id {
+ 	u16 id;
+ };
+ 
++#ifdef CONFIG_MTK_VENDOR
++#define MT7996_AIR_MONITOR_MAX_ENTRY	16
++#define MT7996_AIR_MONITOR_MAX_GROUP	(MT7996_AIR_MONITOR_MAX_ENTRY >> 1)
++
++struct mt7996_air_monitor_group {
++	bool enable;
++	bool used[2];
++};
++
++struct mt7996_air_monitor_entry {
++	bool enable;
++
++	u8 group_idx;
++	u8 group_used_idx;
++	u8 muar_idx;
++	u8 addr[ETH_ALEN];
++	u32 last_seen;
++	s8 rssi[4];
++	struct ieee80211_sta *sta;
++};
++
++struct mt7996_air_monitor_ctrl {
++	u8 enable;
++	struct mt7996_air_monitor_group group[MT7996_AIR_MONITOR_MAX_GROUP];
++	struct mt7996_air_monitor_entry entry[MT7996_AIR_MONITOR_MAX_ENTRY];
++};
++#endif
++
+ struct mt7996_phy {
+ 	struct mt76_phy *mt76;
+ 	struct mt7996_dev *dev;
+@@ -300,6 +328,8 @@ struct mt7996_phy {
+ 	bool sku_limit_en;
+ 	bool sku_path_en;
+ 
++	u8 muru_onoff;
++
+ #ifdef CONFIG_NL80211_TESTMODE
+ 	struct {
+ 		u32 *reg_backup;
+@@ -314,6 +344,10 @@ struct mt7996_phy {
+ 		u8 spe_idx;
+ 	} test;
+ #endif
++#ifdef CONFIG_MTK_VENDOR
++	spinlock_t amnt_lock;
++	struct mt7996_air_monitor_ctrl amnt_ctrl;
++#endif
+ };
+ 
+ struct mt7996_dev {
+@@ -740,6 +774,25 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ 			 bool hif2, int *irq);
+ u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id);
+ 
++#ifdef CONFIG_MTK_VENDOR
++void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
++void mt7996_vendor_register(struct mt7996_phy *phy);
++void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb);
++int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy,
++				  struct ieee80211_sta *sta);
++#endif
++
++int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
++int mt7996_mcu_edcca_threshold_ctrl(struct mt7996_phy *phy, u8 *value, bool set);
++
++enum edcca_bw_id {
++	EDCCA_BW_20 = 0,
++	EDCCA_BW_40,
++	EDCCA_BW_80,
++	EDCCA_BW_160,
++	EDCCA_MAX_BW_NUM,
++};
++
+ #ifdef CONFIG_MTK_DEBUG
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
+ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index e56ddd8f..5c54d02c 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -59,4 +59,91 @@ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val)
+ 	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
+ 				 sizeof(req), true);
+ }
++
++int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
++	enum nl80211_band band = chandef->chan->band;
++	struct {
++		u8 band_idx;
++		u8 _rsv[3];
++
++		__le16 tag;
++		__le16 len;
++		u8 enable;
++		u8 std;
++		u8 _rsv2[2];
++	} __packed req = {
++		.band_idx = phy->mt76->band_idx,
++		.tag = cpu_to_le16(UNI_BAND_CONFIG_EDCCA_ENABLE),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.enable = enable,
++		.std = EDCCA_DEFAULT,
++	};
++
++	switch (dev->mt76.region) {
++	case NL80211_DFS_JP:
++		req.std = EDCCA_JAPAN;
++		break;
++	case NL80211_DFS_FCC:
++		if (band == NL80211_BAND_6GHZ)
++			req.std = EDCCA_FCC;
++		break;
++	case NL80211_DFS_ETSI:
++		if (band == NL80211_BAND_6GHZ)
++			req.std = EDCCA_ETSI;
++		break;
++	default:
++		break;
++	}
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
++				 &req, sizeof(req), true);
++}
++
++int mt7996_mcu_edcca_threshold_ctrl(struct mt7996_phy *phy, u8 *value, bool set)
++{
++	struct {
++		u8 band_idx;
++		u8 _rsv[3];
++
++		__le16 tag;
++		__le16 len;
++		u8 threshold[4];
++		bool init;
++	} __packed *res, req = {
++		.band_idx = phy->mt76->band_idx,
++		.tag = cpu_to_le16(UNI_BAND_CONFIG_EDCCA_THRESHOLD),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.init = false,
++	};
++	struct sk_buff *skb;
++	int ret;
++	int i;
++
++	for (i = 0; i < EDCCA_MAX_BW_NUM; i++)
++		req.threshold[i] = value[i];
++
++	if (set)
++		return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
++					 &req, sizeof(req), true);
++
++	ret = mt76_mcu_send_and_get_msg(&phy->dev->mt76,
++					MCU_WM_UNI_CMD_QUERY(BAND_CONFIG),
++					&req, sizeof(req), true, &skb);
++
++	if (ret)
++		return ret;
++
++	res = (void *)skb->data;
++
++	for (i = 0; i < EDCCA_MAX_BW_NUM; i++)
++		value[i] = res->threshold[i];
++
++	dev_kfree_skb(skb);
++
++	return 0;
++}
++
+ #endif
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+index c30418ca..36a58ad6 100644
+--- a/mt7996/mtk_mcu.h
++++ b/mt7996/mtk_mcu.h
+@@ -106,6 +106,21 @@ enum txpower_event {
+ 	UNI_TXPOWER_PHY_RATE_INFO = 5,
+ };
+ 
++enum {
++	EDCCA_CTRL_SET_EN = 0,
++	EDCCA_CTRL_SET_THRES,
++	EDCCA_CTRL_GET_EN,
++	EDCCA_CTRL_GET_THRES,
++	EDCCA_CTRL_NUM,
++};
++
++enum {
++	EDCCA_DEFAULT = 0,
++	EDCCA_FCC = 1,
++	EDCCA_ETSI = 2,
++	EDCCA_JAPAN = 3
++};
++
+ #endif
+ 
+ #endif
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+new file mode 100644
+index 00000000..0d6fa779
+--- /dev/null
++++ b/mt7996/vendor.c
+@@ -0,0 +1,805 @@
++// SPDX-License-Identifier: ISC
++/*
++ * Copyright (C) 2020, MediaTek Inc. All rights reserved.
++ */
++
++#include <net/netlink.h>
++
++#include "mt7996.h"
++#include "mcu.h"
++#include "vendor.h"
++#include "mtk_mcu.h"
++
++static const struct nla_policy
++mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
++	[MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
++};
++
++static const struct nla_policy
++amnt_ctrl_policy[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL] = {
++	[MTK_VENDOR_ATTR_AMNT_CTRL_SET] = {.type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP] = { .type = NLA_NESTED },
++};
++
++static const struct nla_policy
++amnt_set_policy[NUM_MTK_VENDOR_ATTRS_AMNT_SET] = {
++	[MTK_VENDOR_ATTR_AMNT_SET_INDEX] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_AMNT_SET_MACADDR] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
++};
++
++static const struct nla_policy
++amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
++	[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_AMNT_DUMP_LEN] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
++};
++
++static struct nla_policy
++bss_color_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL] = {
++	[MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP] = { .type = NLA_U64 },
++};
++
++static const struct nla_policy
++edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_S8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
++};
++
++static const struct nla_policy
++edcca_dump_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP] = {
++	[MTK_VENDOR_ATTR_EDCCA_DUMP_MODE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL] = { .type = NLA_U8 },
++};
++
++static const struct nla_policy
++three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
++	[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
++};
++
++static const struct nla_policy
++ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
++	[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
++};
++
++static struct nla_policy
++pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
++	[MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_PP_BAND_IDX] = { .type = NLA_U8 },
++};
++
++struct mt7996_amnt_data {
++	u8 idx;
++	u8 addr[ETH_ALEN];
++	s8 rssi[4];
++	u32 last_seen;
++};
++
++static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
++				 struct wireless_dev *wdev,
++				 const void *data,
++				 int data_len)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_MU_CTRL];
++	int err;
++	u8 val8;
++	u32 val32 = 0;
++
++	err = nla_parse(tb, MTK_VENDOR_ATTR_MU_CTRL_MAX, data, data_len,
++			mu_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	if (tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF]) {
++		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF]);
++		val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_AUTO_MU) |
++			 FIELD_PREP(RATE_CFG_VAL, val8);
++		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
++							   mt7996_set_wireless_vif, &val32);
++	}
++
++	return 0;
++}
++
++static int
++mt7996_vendor_mu_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
++			   struct sk_buff *skb, const void *data, int data_len,
++			   unsigned long *storage)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	int len = 0;
++
++	if (*storage == 1)
++		return -ENOENT;
++	*storage = 1;
++
++	if (nla_put_u8(skb, MTK_VENDOR_ATTR_MU_CTRL_DUMP, phy->muru_onoff))
++		return -ENOMEM;
++	len += 1;
++
++	return len;
++}
++
++void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb)
++{
++	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
++	struct mt7996_air_monitor_ctrl *ctrl = &phy->amnt_ctrl;
++	struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb);
++	__le16 fc = hdr->frame_control;
++	u8 addr[ETH_ALEN];
++	int i;
++
++	if (!ieee80211_has_fromds(fc))
++		ether_addr_copy(addr, hdr->addr2);
++	else if (ieee80211_has_tods(fc))
++		ether_addr_copy(addr, hdr->addr4);
++	else
++		ether_addr_copy(addr, hdr->addr3);
++
++	spin_lock_bh(&phy->amnt_lock);
++	for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++) {
++		struct mt7996_air_monitor_entry *entry;
++
++		if (ether_addr_equal(addr, ctrl->entry[i].addr)) {
++			entry = &ctrl->entry[i];
++			entry->rssi[0] = status->chain_signal[0];
++			entry->rssi[1] = status->chain_signal[1];
++			entry->rssi[2] = status->chain_signal[2];
++			entry->rssi[3] = status->chain_signal[3];
++			entry->last_seen = jiffies;
++			break;
++		}
++	}
++	spin_unlock_bh(&phy->amnt_lock);
++}
++
++static int
++mt7996_vendor_smesh_ctrl(struct mt7996_phy *phy, u8 write,
++			 u8 enable, u8 *value)
++{
++#define UNI_CMD_SMESH_PARAM  0
++	struct mt7996_dev *dev = phy->dev;
++	struct smesh_param {
++		__le16 tag;
++		__le16 length;
++
++		u8 enable;
++		bool a2;
++		bool a1;
++		bool data;
++		bool mgnt;
++		bool ctrl;
++		u8 padding[2];
++	} req = {
++		.tag = cpu_to_le16(UNI_CMD_SMESH_PARAM),
++		.length = cpu_to_le16(sizeof(req) - 4),
++
++		.enable = enable,
++		.a2 = true,
++		.a1 = true,
++		.data = true,
++		.mgnt = false,
++		.ctrl = false,
++	};
++	struct smesh_param *res;
++	struct sk_buff *skb;
++	int ret = 0;
++
++	if (!value)
++		return -EINVAL;
++
++	ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD(CFG_SMESH),
++					&req, sizeof(req), !write, &skb);
++
++	if (ret || write)
++		return ret;
++
++	res = (struct smesh_param *) skb->data;
++
++	*value = res->enable;
++
++	dev_kfree_skb(skb);
++
++	return 0;
++}
++
++static int
++mt7996_vendor_amnt_muar(struct mt7996_phy *phy, u8 muar_idx, u8 *addr)
++{
++#define UNI_CMD_MUAR_ENTRY  2
++	struct mt7996_dev *dev = phy->dev;
++	struct muar_entry {
++		__le16 tag;
++		__le16 length;
++
++		bool smesh;
++		u8 hw_bss_index;
++		u8 muar_idx;
++		u8 entry_add;
++		u8 mac_addr[6];
++		u8 padding[2];
++	} __packed req = {
++		.tag = cpu_to_le16(UNI_CMD_MUAR_ENTRY),
++		.length = cpu_to_le16(sizeof(req) - 4),
++
++		.smesh = true,
++		.hw_bss_index = phy != &dev->phy,
++		.muar_idx = muar_idx,
++		.entry_add = 1,
++	};
++
++	ether_addr_copy(req.mac_addr, addr);
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REPT_MUAR), &req,
++				 sizeof(req), true);
++}
++
++static int
++mt7996_vendor_amnt_set_en(struct mt7996_phy *phy, u8 enable)
++{
++	u8 status;
++	int ret;
++
++	ret = mt7996_vendor_smesh_ctrl(phy, 0, enable, &status);
++	if (ret)
++		return ret;
++
++	if (status == enable)
++		return 0;
++
++	ret = mt7996_vendor_smesh_ctrl(phy, 1, enable, &status);
++	if (ret)
++		return ret;
++
++	return 0;
++}
++
++static int
++mt7996_vendor_amnt_set_addr(struct mt7996_phy *phy, u8 index, u8 *addr)
++{
++	struct mt7996_air_monitor_ctrl *amnt_ctrl = &phy->amnt_ctrl;
++	struct mt7996_air_monitor_group *group;
++	struct mt7996_air_monitor_entry *entry;
++	int ret, i, j;
++
++	if (index >= MT7996_AIR_MONITOR_MAX_ENTRY)
++		return -1;
++
++	spin_lock_bh(&phy->amnt_lock);
++	entry = &amnt_ctrl->entry[index];
++	if (!is_zero_ether_addr(addr)) {
++		if (entry->enable == false) {
++			for (i = 0; i < MT7996_AIR_MONITOR_MAX_GROUP; i++) {
++				group = &(amnt_ctrl->group[i]);
++				if (group->used[0] == false)
++					j = 0;
++				else if (group->used[1] == false)
++					j = 1;
++				else
++					continue;
++
++				group->enable = true;
++				group->used[j] = true;
++				entry->enable = true;
++				entry->group_idx = i;
++				entry->group_used_idx = j;
++				entry->muar_idx = 32 + 4 * i + 2 * j;
++				break;
++			}
++		}
++	} else {
++		group = &(amnt_ctrl->group[entry->group_idx]);
++
++		group->used[entry->group_used_idx] = false;
++		if (group->used[0] == false && group->used[1] == false)
++			group->enable = false;
++
++		entry->enable = false;
++	}
++	ether_addr_copy(entry->addr, addr);
++	amnt_ctrl->enable &= ~(1 << entry->group_idx);
++	amnt_ctrl->enable |= entry->enable << entry->group_idx;
++	spin_unlock_bh(&phy->amnt_lock);
++
++	ret = mt7996_vendor_amnt_muar(phy, entry->muar_idx, addr);
++	if (ret)
++		return ret;
++
++	return mt7996_vendor_amnt_set_en(phy, amnt_ctrl->enable);
++}
++
++static int
++mt7966_vendor_amnt_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
++			const void *data, int data_len)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
++	struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_SET];
++	u8 index = 0;
++	u8 mac_addr[ETH_ALEN];
++	int err;
++
++	err = nla_parse(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX, data, data_len,
++			amnt_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_SET])
++		return -EINVAL;
++
++	err = nla_parse_nested(tb2, MTK_VENDOR_ATTR_AMNT_SET_MAX,
++		tb1[MTK_VENDOR_ATTR_AMNT_CTRL_SET], amnt_set_policy, NULL);
++
++	if (!tb2[MTK_VENDOR_ATTR_AMNT_SET_INDEX] ||
++		!tb2[MTK_VENDOR_ATTR_AMNT_SET_MACADDR])
++		return -EINVAL;
++
++	index = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_SET_INDEX]);
++	memcpy(mac_addr, nla_data(tb2[MTK_VENDOR_ATTR_AMNT_SET_MACADDR]), ETH_ALEN);
++
++	return mt7996_vendor_amnt_set_addr(phy, index, mac_addr);
++}
++
++int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy,
++				  struct ieee80211_sta *sta)
++{
++	u8 zero[ETH_ALEN] = {};
++	int i;
++
++	if (!phy->amnt_ctrl.enable)
++		return 0;
++
++	for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++)
++		if (ether_addr_equal(sta->addr, phy->amnt_ctrl.entry[i].addr))
++			return mt7996_vendor_amnt_set_addr(phy, i, zero);
++	return 0;
++}
++
++static int
++mt7996_amnt_dump(struct mt7996_phy *phy, struct sk_buff *skb,
++		 u8 amnt_idx, int *attrtype)
++{
++	struct mt7996_air_monitor_entry *entry;
++	struct mt7996_amnt_data data;
++	u32 last_seen = 0;
++
++	spin_lock_bh(&phy->amnt_lock);
++	entry = &phy->amnt_ctrl.entry[amnt_idx];
++	if (entry->enable == 0) {
++		spin_unlock_bh(&phy->amnt_lock);
++		return 0;
++	}
++
++	last_seen = jiffies_to_msecs(jiffies - entry->last_seen);
++	ether_addr_copy(data.addr, entry->addr);
++	data.rssi[0] = entry->rssi[0];
++	data.rssi[1] = entry->rssi[1];
++	data.rssi[2] = entry->rssi[2];
++	data.rssi[3] = entry->rssi[3];
++	spin_unlock_bh(&phy->amnt_lock);
++
++	data.idx = amnt_idx;
++	data.last_seen = last_seen;
++
++	nla_put(skb, (*attrtype)++, sizeof(struct mt7996_amnt_data), &data);
++
++	return 1;
++}
++
++static int
++mt7966_vendor_amnt_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
++			     struct sk_buff *skb, const void *data, int data_len,
++			     unsigned long *storage)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
++	struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP];
++	void *a, *b;
++	int err = 0, attrtype = 0, i, len = 0;
++	u8 amnt_idx;
++
++	if (*storage == 1)
++		return -ENOENT;
++	*storage = 1;
++
++	err = nla_parse(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX, data, data_len,
++			amnt_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP])
++		return -EINVAL;
++
++	err = nla_parse_nested(tb2, MTK_VENDOR_ATTR_AMNT_DUMP_MAX,
++			       tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP],
++			       amnt_dump_policy, NULL);
++	if (err)
++		return err;
++
++	if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX])
++		return -EINVAL;
++
++	amnt_idx = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX]);
++
++	a = nla_nest_start(skb, MTK_VENDOR_ATTR_AMNT_CTRL_DUMP);
++	b = nla_nest_start(skb, MTK_VENDOR_ATTR_AMNT_DUMP_RESULT);
++
++	if (amnt_idx != 0xff) {
++		len += mt7996_amnt_dump(phy, skb, amnt_idx, &attrtype);
++	} else {
++		for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++)
++			len += mt7996_amnt_dump(phy, skb, i, &attrtype);
++	}
++
++	nla_nest_end(skb, b);
++
++	nla_put_u8(skb, MTK_VENDOR_ATTR_AMNT_DUMP_LEN, len);
++
++	nla_nest_end(skb, a);
++
++	return len + 1;
++}
++
++static int
++mt7996_vendor_bss_color_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
++				  struct sk_buff *skb, const void *data, int data_len,
++				  unsigned long *storage)
++{
++	struct ieee80211_vif *vif = wdev_to_ieee80211_vif(wdev);
++	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
++	int len = 0;
++
++	if (*storage == 1)
++		return -ENOENT;
++	*storage = 1;
++
++	if (nla_put_u64_64bit(skb, MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
++			      ~bss_conf->used_color_bitmap, NL80211_ATTR_PAD))
++		return -ENOMEM;
++	len += 1;
++
++	return len;
++}
++
++static int mt7996_vendor_edcca_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
++				    const void *data, int data_len)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL];
++	int err;
++	u8 edcca_mode;
++	u8 edcca_value[EDCCA_MAX_BW_NUM];
++
++	err = nla_parse(tb, MTK_VENDOR_ATTR_EDCCA_CTRL_MAX, data, data_len,
++			edcca_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE])
++		return -EINVAL;
++
++	edcca_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE]);
++	if (edcca_mode == EDCCA_CTRL_SET_EN) {
++		if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL])
++			return -EINVAL;
++
++		edcca_value[0] =
++			nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL]);
++
++		err = mt7996_mcu_edcca_enable(phy, !!edcca_value[0]);
++		if (err)
++			return err;
++	} else if (edcca_mode == EDCCA_CTRL_SET_THRES) {
++		if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] ||
++		    !tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] ||
++		    !tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] ||
++		    !tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL]) {
++			return -EINVAL;
++		}
++		edcca_value[EDCCA_BW_20] =
++			nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL]);
++		edcca_value[EDCCA_BW_40] =
++			nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL]);
++		edcca_value[EDCCA_BW_80] =
++			nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL]);
++		edcca_value[EDCCA_BW_160] =
++			nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL]);
++
++		err = mt7996_mcu_edcca_threshold_ctrl(phy, edcca_value, true);
++
++		if (err)
++			return err;
++	} else {
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++
++static int
++mt7996_vendor_edcca_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
++			     struct sk_buff *skb, const void *data, int data_len,
++			     unsigned long *storage)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL];
++	int err;
++	u8 edcca_mode;
++	u8 value[EDCCA_MAX_BW_NUM];
++
++	if (*storage == 1)
++		return -ENOENT;
++	*storage = 1;
++
++	err = nla_parse(tb, MTK_VENDOR_ATTR_EDCCA_CTRL_MAX, data, data_len,
++			edcca_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE])
++		return -EINVAL;
++
++	edcca_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE]);
++
++	if (edcca_mode != EDCCA_CTRL_GET_THRES)
++		return -EINVAL;
++
++	err = mt7996_mcu_edcca_threshold_ctrl(phy, value, false);
++
++	if (err)
++		return err;
++
++	if (nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL, value[EDCCA_BW_20]) ||
++	    nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL, value[EDCCA_BW_40]) ||
++	    nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL, value[EDCCA_BW_80]) ||
++	    nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL, value[EDCCA_BW_160]))
++		return -ENOMEM;
++
++	return EDCCA_MAX_BW_NUM;
++}
++
++static int mt7996_vendor_3wire_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
++				    const void *data, int data_len)
++{
++#define UNI_3WIRE_EXT_EN	0
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL];
++	struct {
++		u8 __rsv1[4];
++
++		__le16 tag;
++		__le16 len;
++		u8 three_wire_mode;
++	} __packed req = {
++		.tag = cpu_to_le16(UNI_3WIRE_EXT_EN),
++		.len = cpu_to_le16(sizeof(req) - 4),
++	};
++	int err;
++
++	err = nla_parse(tb, MTK_VENDOR_ATTR_3WIRE_CTRL_MAX, data, data_len,
++			three_wire_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	if (!tb[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE])
++		return -EINVAL;
++
++	req.three_wire_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE]);
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(PTA_3WIRE_CTRL), &req,
++				 sizeof(req), false);
++}
++
++static int mt7996_vendor_ibf_ctrl(struct wiphy *wiphy,
++				  struct wireless_dev *wdev,
++				  const void *data,
++				  int data_len)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct mt7996_dev *dev = phy->dev;
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_IBF_CTRL];
++	int err;
++	u8 val;
++
++	err = nla_parse(tb, MTK_VENDOR_ATTR_IBF_CTRL_MAX, data, data_len,
++			ibf_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	if (tb[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE]) {
++		val = nla_get_u8(tb[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE]);
++
++		dev->ibf = !!val;
++
++		err = mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
++		if (err)
++			return err;
++	}
++	return 0;
++}
++
++static int
++mt7996_vendor_ibf_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
++			    struct sk_buff *skb, const void *data, int data_len,
++			    unsigned long *storage)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct mt7996_dev *dev = phy->dev;
++
++	if (*storage == 1)
++		return -ENOENT;
++	*storage = 1;
++
++	if (nla_put_u8(skb, MTK_VENDOR_ATTR_IBF_DUMP_ENABLE, dev->ibf))
++		return -ENOMEM;
++
++	return 1;
++}
++
++static int mt7996_vendor_pp_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
++				 const void *data, int data_len)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_PP_CTRL];
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mt7996_phy *phy;
++	struct mt76_phy *mphy;
++	struct cfg80211_chan_def *chandef;
++	int err;
++	u8 val8, band_idx = 0;
++
++	err = nla_parse(tb, MTK_VENDOR_ATTR_PP_CTRL_MAX, data, data_len,
++			pp_ctrl_policy, NULL);
++
++	if (tb[MTK_VENDOR_ATTR_PP_BAND_IDX]) {
++		band_idx = nla_get_u8(tb[MTK_VENDOR_ATTR_PP_BAND_IDX]);
++	}
++
++	if (!mt7996_band_valid(dev, band_idx))
++		goto error;
++
++	mphy = dev->mt76.phys[band_idx];
++	if (!mphy)
++		goto error;
++
++	phy = (struct mt7996_phy *)mphy->priv;
++	if (!phy)
++		goto error;
++
++	chandef = &phy->chanctx->chandef;
++	if (!chandef)
++		goto error;
++
++	if (chandef->chan->band == NL80211_BAND_2GHZ)
++		return 0;
++
++	if (tb[MTK_VENDOR_ATTR_PP_MODE]) {
++		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_PP_MODE]);
++		switch (val8) {
++		case PP_DISABLE:
++		case PP_FW_MODE:
++			err = mt7996_mcu_set_pp_en(phy, val8, 0);
++			break;
++		case PP_USR_MODE:
++			/* handled by add_chanctx */
++			err = 0;
++			break;
++		default:
++			err = -EINVAL;
++		}
++	}
++
++	return err;
++error:
++	dev_err(dev->mt76.dev, "Invalid band idx: %d\n", band_idx);
++	return -EINVAL;
++}
++
++static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
++	{
++		.info = {
++			.vendor_id = MTK_NL80211_VENDOR_ID,
++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_MU_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++			 WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.doit = mt7996_vendor_mu_ctrl,
++		.dumpit = mt7996_vendor_mu_ctrl_dump,
++		.policy = mu_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_MU_CTRL_MAX,
++	},
++	{
++		.info = {
++			.vendor_id = MTK_NL80211_VENDOR_ID,
++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++			 WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.doit = mt7966_vendor_amnt_ctrl,
++		.dumpit = mt7966_vendor_amnt_ctrl_dump,
++		.policy = amnt_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_AMNT_CTRL_MAX,
++	},
++	{
++		.info = {
++			.vendor_id = MTK_NL80211_VENDOR_ID,
++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++			WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.dumpit = mt7996_vendor_bss_color_ctrl_dump,
++		.policy = bss_color_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX,
++	},
++	{
++		.info = {
++			.vendor_id = MTK_NL80211_VENDOR_ID,
++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++			 WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.doit = mt7996_vendor_edcca_ctrl,
++		.dumpit = mt7996_vendor_edcca_ctrl_dump,
++		.policy = edcca_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_EDCCA_CTRL_MAX,
++	},
++	{
++		.info = {
++			.vendor_id = MTK_NL80211_VENDOR_ID,
++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++			 WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.doit = mt7996_vendor_3wire_ctrl,
++		.policy = three_wire_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_3WIRE_CTRL_MAX,
++	},
++	{
++		.info = {
++			.vendor_id = MTK_NL80211_VENDOR_ID,
++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++			WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.doit = mt7996_vendor_ibf_ctrl,
++		.dumpit = mt7996_vendor_ibf_ctrl_dump,
++		.policy = ibf_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_IBF_CTRL_MAX,
++	},
++	{
++		.info = {
++			.vendor_id = MTK_NL80211_VENDOR_ID,
++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_PP_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++			WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.doit = mt7996_vendor_pp_ctrl,
++		.policy = pp_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_PP_CTRL_MAX,
++	},
++};
++
++void mt7996_vendor_register(struct mt7996_phy *phy)
++{
++	phy->mt76->hw->wiphy->vendor_commands = mt7996_vendor_commands;
++	phy->mt76->hw->wiphy->n_vendor_commands = ARRAY_SIZE(mt7996_vendor_commands);
++
++	spin_lock_init(&phy->amnt_lock);
++}
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+new file mode 100644
+index 00000000..8aaa18ee
+--- /dev/null
++++ b/mt7996/vendor.h
+@@ -0,0 +1,153 @@
++#ifndef __MT7996_VENDOR_H
++#define __MT7996_VENDOR_H
++
++#define MTK_NL80211_VENDOR_ID	0x0ce7
++
++enum mtk_nl80211_vendor_subcmds {
++	MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
++	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
++	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
++	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
++	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
++	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
++	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
++};
++
++enum mtk_vendor_attr_edcca_ctrl {
++	MTK_VENDOR_ATTR_EDCCA_THRESHOLD_INVALID = 0,
++
++	MTK_VENDOR_ATTR_EDCCA_CTRL_MODE,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
++};
++
++enum mtk_vendor_attr_edcca_dump {
++	MTK_VENDOR_ATTR_EDCCA_DUMP_UNSPEC = 0,
++
++	MTK_VENDOR_ATTR_EDCCA_DUMP_MODE,
++	MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL,
++	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL,
++	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL,
++	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP,
++	MTK_VENDOR_ATTR_EDCCA_DUMP_MAX =
++		NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
++};
++
++enum mtk_vendor_attr_3wire_ctrl {
++	MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_3WIRE_CTRL_MODE,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL,
++	MTK_VENDOR_ATTR_3WIRE_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
++};
++
++enum mtk_vendor_attr_mu_ctrl {
++	MTK_VENDOR_ATTR_MU_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
++	MTK_VENDOR_ATTR_MU_CTRL_DUMP,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
++	MTK_VENDOR_ATTR_MU_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
++};
++
++enum mtk_vendor_attr_mnt_ctrl {
++	MTK_VENDOR_ATTR_AMNT_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_AMNT_CTRL_SET,
++	MTK_VENDOR_ATTR_AMNT_CTRL_DUMP,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_AMNT_CTRL,
++	MTK_VENDOR_ATTR_AMNT_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_AMNT_CTRL - 1
++};
++
++enum mtk_vendor_attr_mnt_set {
++	MTK_VENDOR_ATTR_AMNT_SET_UNSPEC,
++
++	MTK_VENDOR_ATTR_AMNT_SET_INDEX,
++	MTK_VENDOR_ATTR_AMNT_SET_MACADDR,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_AMNT_SET,
++	MTK_VENDOR_ATTR_AMNT_SET_MAX =
++		NUM_MTK_VENDOR_ATTRS_AMNT_SET - 1
++};
++
++enum mtk_vendor_attr_mnt_dump {
++	MTK_VENDOR_ATTR_AMNT_DUMP_UNSPEC,
++
++	MTK_VENDOR_ATTR_AMNT_DUMP_INDEX,
++	MTK_VENDOR_ATTR_AMNT_DUMP_LEN,
++	MTK_VENDOR_ATTR_AMNT_DUMP_RESULT,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
++	MTK_VENDOR_ATTR_AMNT_DUMP_MAX =
++		NUM_MTK_VENDOR_ATTRS_AMNT_DUMP - 1
++};
++
++enum mtk_vendor_attr_bss_color_ctrl {
++	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL,
++	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
++};
++
++enum mtk_vendor_attr_ibf_ctrl {
++	MTK_VENDOR_ATTR_IBF_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_IBF_CTRL_ENABLE,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_IBF_CTRL,
++	MTK_VENDOR_ATTR_IBF_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_IBF_CTRL - 1
++};
++
++enum mtk_vendor_attr_ibf_dump {
++	MTK_VENDOR_ATTR_IBF_DUMP_UNSPEC,
++
++	MTK_VENDOR_ATTR_IBF_DUMP_ENABLE,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_IBF_DUMP,
++	MTK_VENDOR_ATTR_IBF_DUMP_MAX =
++		NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
++};
++
++enum mtk_vendor_attr_pp_ctrl {
++	MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_PP_MODE,
++	MTK_VENDOR_ATTR_PP_BAND_IDX,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_PP_CTRL,
++	MTK_VENDOR_ATTR_PP_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
++};
++
++#endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0036-mtk-wifi-mt76-mt7996-report-tx-and-rx-byte-to-tpt_le.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0036-mtk-wifi-mt76-mt7996-report-tx-and-rx-byte-to-tpt_le.patch
deleted file mode 100644
index e58ed5b..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0036-mtk-wifi-mt76-mt7996-report-tx-and-rx-byte-to-tpt_le.patch
+++ /dev/null
@@ -1,47 +0,0 @@
-From fdad308b7db267d46e7dc1e22f251797c9cbd79e Mon Sep 17 00:00:00 2001
-From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Date: Sat, 12 Aug 2023 04:17:22 +0800
-Subject: [PATCH 036/116] mtk: wifi: mt76: mt7996: report tx and rx byte to
- tpt_led
-
----
- mt7996/mcu.c | 15 +++++++++++----
- 1 file changed, 11 insertions(+), 4 deletions(-)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 7318842..2b9dedb 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -525,6 +525,8 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- 		u8 ac;
- 		u16 wlan_idx;
- 		struct mt76_wcid *wcid;
-+		struct mt76_phy *mphy;
-+		u32 tx_bytes, rx_bytes;
- 
- 		switch (le16_to_cpu(res->tag)) {
- 		case UNI_ALL_STA_TXRX_RATE:
-@@ -544,11 +546,16 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- 			if (!wcid)
- 				break;
- 
-+			mphy = mt76_dev_phy(&dev->mt76, wcid->phy_idx);
- 			for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
--				wcid->stats.tx_bytes +=
--					le32_to_cpu(res->adm_stat[i].tx_bytes[ac]);
--				wcid->stats.rx_bytes +=
--					le32_to_cpu(res->adm_stat[i].rx_bytes[ac]);
-+				tx_bytes = le32_to_cpu(res->adm_stat[i].tx_bytes[ac]);
-+				rx_bytes = le32_to_cpu(res->adm_stat[i].rx_bytes[ac]);
-+
-+				wcid->stats.tx_bytes += tx_bytes;
-+				wcid->stats.rx_bytes += rx_bytes;
-+
-+				ieee80211_tpt_led_trig_tx(mphy->hw, tx_bytes);
-+				ieee80211_tpt_led_trig_rx(mphy->hw, rx_bytes);
- 			}
- 			break;
- 		case UNI_ALL_STA_TXRX_MSDU_COUNT:
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0037-mtk-mt76-mt7996-add-debugfs-for-fw-coredump.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0037-mtk-mt76-mt7996-add-debugfs-for-fw-coredump.patch
new file mode 100644
index 0000000..29bd31f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0037-mtk-mt76-mt7996-add-debugfs-for-fw-coredump.patch
@@ -0,0 +1,171 @@
+From 1fd0460d51166d09eb2a6b7551479724665b88b8 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Fri, 19 May 2023 14:56:07 +0800
+Subject: [PATCH 037/199] mtk: mt76: mt7996: add debugfs for fw coredump.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ mt7996/debugfs.c | 19 +++++++++++++++++--
+ mt7996/mac.c     | 28 +++++++++++++++++++++++++---
+ mt7996/mcu.h     |  4 ++++
+ mt7996/mt7996.h  | 10 ++++++++++
+ 4 files changed, 56 insertions(+), 5 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 9671c15d..1f4bad62 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -84,6 +84,8 @@ mt7996_sys_recovery_set(struct file *file, const char __user *user_buf,
+ 	 * 7: trigger & enable system error L4 mdp recovery.
+ 	 * 8: trigger & enable system error full recovery.
+ 	 * 9: trigger firmware crash.
++	 * 10: trigger grab wa firmware coredump.
++	 * 11: trigger grab wm firmware coredump.
+ 	 */
+ 	case UNI_CMD_SER_QUERY:
+ 		ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_QUERY, 0, band);
+@@ -105,15 +107,25 @@ mt7996_sys_recovery_set(struct file *file, const char __user *user_buf,
+ 	/* enable full chip reset */
+ 	case UNI_CMD_SER_SET_RECOVER_FULL:
+ 		mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK);
+-		dev->recovery.state |= MT_MCU_CMD_WDT_MASK;
++		dev->recovery.state |= MT_MCU_CMD_WM_WDT;
+ 		mt7996_reset(dev);
+ 		break;
+ 
+ 	/* WARNING: trigger firmware crash */
+ 	case UNI_CMD_SER_SET_SYSTEM_ASSERT:
++		// trigger wm assert exception
+ 		ret = mt7996_mcu_trigger_assert(dev);
+ 		if (ret)
+ 			return ret;
++		// trigger wa assert exception
++		mt76_wr(dev, 0x89098108, 0x20);
++		mt76_wr(dev, 0x89098118, 0x20);
++		break;
++	case UNI_CMD_SER_FW_COREDUMP_WA:
++		mt7996_coredump(dev, MT7996_COREDUMP_MANUAL_WA);
++		break;
++	case UNI_CMD_SER_FW_COREDUMP_WM:
++		mt7996_coredump(dev, MT7996_COREDUMP_MANUAL_WM);
+ 		break;
+ 	default:
+ 		break;
+@@ -160,7 +172,10 @@ mt7996_sys_recovery_get(struct file *file, char __user *user_buf,
+ 			  "8: trigger system error full recovery\n");
+ 	desc += scnprintf(buff + desc, bufsz - desc,
+ 			  "9: trigger firmware crash\n");
+-
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "10: trigger grab wa firmware coredump\n");
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "11: trigger grab wm firmware coredump\n");
+ 	/* SER statistics */
+ 	desc += scnprintf(buff + desc, bufsz - desc,
+ 			  "\nlet's dump firmware SER statistics...\n");
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index f0c0db75..d9e8e751 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2089,15 +2089,36 @@ void mt7996_mac_dump_work(struct work_struct *work)
+ 	struct mt7996_dev *dev;
+ 
+ 	dev = container_of(work, struct mt7996_dev, dump_work);
+-	if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT)
++	if (dev->dump_state == MT7996_COREDUMP_MANUAL_WA ||
++	    READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT)
+ 		mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WA);
+ 
+-	if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WM_WDT)
++	if (dev->dump_state == MT7996_COREDUMP_MANUAL_WM ||
++	    READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WM_WDT)
+ 		mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WM);
+ 
+-	queue_work(dev->mt76.wq, &dev->reset_work);
++	if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WDT_MASK)
++		queue_work(dev->mt76.wq, &dev->reset_work);
++
++	dev->dump_state = MT7996_COREDUMP_IDLE;
+ }
+ 
++void mt7996_coredump(struct mt7996_dev *dev, u8 state)
++{
++	if (state == MT7996_COREDUMP_IDLE ||
++	    state >= __MT7996_COREDUMP_TYPE_MAX)
++		return;
++
++	if (dev->dump_state != MT7996_COREDUMP_IDLE)
++		return;
++
++	dev->dump_state = state;
++	dev_info(dev->mt76.dev, "%s attempting grab coredump\n",
++		 wiphy_name(dev->mt76.hw->wiphy));
++
++	queue_work(dev->mt76.wq, &dev->dump_work);
++ }
++
+ void mt7996_reset(struct mt7996_dev *dev)
+ {
+ 	if (!dev->recovery.hw_init_done)
+@@ -2115,6 +2136,7 @@ void mt7996_reset(struct mt7996_dev *dev)
+ 
+ 		mt7996_irq_disable(dev, MT_INT_MCU_CMD);
+ 		queue_work(dev->mt76.wq, &dev->dump_work);
++		mt7996_coredump(dev, MT7996_COREDUMP_AUTO);
+ 		return;
+ 	}
+ 
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index f827de9e..3e9364de 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -958,7 +958,11 @@ enum {
+ 	UNI_CMD_SER_SET_RECOVER_L3_BF,
+ 	UNI_CMD_SER_SET_RECOVER_L4_MDP,
+ 	UNI_CMD_SER_SET_RECOVER_FULL,
++	/* fw assert */
+ 	UNI_CMD_SER_SET_SYSTEM_ASSERT,
++	/* coredump */
++	UNI_CMD_SER_FW_COREDUMP_WA,
++	UNI_CMD_SER_FW_COREDUMP_WM,
+ 	/* action */
+ 	UNI_CMD_SER_ENABLE = 1,
+ 	UNI_CMD_SER_SET,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index f0604368..ce54080a 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -138,6 +138,14 @@ enum mt7996_ram_type {
+ 	__MT7996_RAM_TYPE_MAX,
+ };
+ 
++enum mt7996_coredump_state {
++	MT7996_COREDUMP_IDLE = 0,
++	MT7996_COREDUMP_MANUAL_WA,
++	MT7996_COREDUMP_MANUAL_WM,
++	MT7996_COREDUMP_AUTO,
++	__MT7996_COREDUMP_TYPE_MAX,
++};
++
+ enum mt7996_txq_id {
+ 	MT7996_TXQ_FWDL = 16,
+ 	MT7996_TXQ_MCU_WM,
+@@ -393,6 +401,7 @@ struct mt7996_dev {
+ 
+ 	/* protects coredump data */
+ 	struct mutex dump_mutex;
++	u8 dump_state;
+ #ifdef CONFIG_DEV_COREDUMP
+ 	struct {
+ 		struct mt7996_crash_data *crash_data[__MT7996_RAM_TYPE_MAX];
+@@ -580,6 +589,7 @@ void mt7996_init_txpower(struct mt7996_phy *phy);
+ int mt7996_txbf_init(struct mt7996_dev *dev);
+ int mt7996_get_chip_sku(struct mt7996_dev *dev);
+ void mt7996_reset(struct mt7996_dev *dev);
++void mt7996_coredump(struct mt7996_dev *dev, u8 state);
+ int mt7996_run(struct ieee80211_hw *hw);
+ int mt7996_mcu_init(struct mt7996_dev *dev);
+ int mt7996_mcu_init_firmware(struct mt7996_dev *dev);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0037-mtk-wifi-mt76-mt7996-support-dup-wtbl.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0037-mtk-wifi-mt76-mt7996-support-dup-wtbl.patch
deleted file mode 100644
index 77e8fb5..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0037-mtk-wifi-mt76-mt7996-support-dup-wtbl.patch
+++ /dev/null
@@ -1,71 +0,0 @@
-From a0f3047d8ba9040f39a14a202b7369efe223dd9e Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 21 Sep 2023 00:52:46 +0800
-Subject: [PATCH 037/116] mtk: wifi: mt76: mt7996: support dup wtbl
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/init.c    |  1 +
- mt7996/mt7996.h  |  1 +
- mt7996/mtk_mcu.c | 23 +++++++++++++++++++++++
- 3 files changed, 25 insertions(+)
-
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 5cc2e6f..d4b0a72 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -686,6 +686,7 @@ static void mt7996_init_work(struct work_struct *work)
- 	mt7996_mcu_set_eeprom(dev);
- 	mt7996_mac_init(dev);
- 	mt7996_txbf_init(dev);
-+	mt7996_mcu_set_dup_wtbl(dev);
- }
- 
- void mt7996_wfsys_reset(struct mt7996_dev *dev)
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index c06aae9..dbc4aa6 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -813,6 +813,7 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
- int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
- int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set);
- void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb);
-+int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev);
- #endif
- 
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index dbdf8d8..ea4e5bf 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -257,4 +257,27 @@ void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb)
- 			 le16_to_cpu(event->basic.tag));
- 	}
- }
-+
-+int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev)
-+{
-+#define CHIP_CONFIG_DUP_WTBL	4
-+#define DUP_WTBL_NUM	80
-+	struct {
-+		u8 _rsv[4];
-+
-+		__le16 tag;
-+		__le16 len;
-+		__le16 base;
-+		__le16 num;
-+		u8 _rsv2[4];
-+	} __packed req = {
-+		.tag = cpu_to_le16(CHIP_CONFIG_DUP_WTBL),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.base = cpu_to_le16(MT7996_WTBL_STA - DUP_WTBL_NUM + 1),
-+		.num = cpu_to_le16(DUP_WTBL_NUM),
-+	};
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(CHIP_CONFIG), &req,
-+				 sizeof(req), true);
-+}
- #endif
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0038-mtk-mt76-mt7996-Add-mt7992-coredump-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0038-mtk-mt76-mt7996-Add-mt7992-coredump-support.patch
new file mode 100644
index 0000000..4f8a8b5
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0038-mtk-mt76-mt7996-Add-mt7992-coredump-support.patch
@@ -0,0 +1,162 @@
+From 52bdf0e4a54de52c7223a99e950cda7e9c823f69 Mon Sep 17 00:00:00 2001
+From: Rex Lu <rex.lu@mediatek.com>
+Date: Mon, 25 Dec 2023 15:17:49 +0800
+Subject: [PATCH 038/199] mtk: mt76: mt7996: Add mt7992 coredump support
+
+1. Add mt7992 coredump support
+2. fixed if new ic have not support coredump, it may cause crash when remove module
+
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+---
+ mt7996/coredump.c | 80 ++++++++++++++++++++++++++++++++++++++---------
+ mt7996/mt7996.h   |  1 +
+ 2 files changed, 67 insertions(+), 14 deletions(-)
+
+diff --git a/mt7996/coredump.c b/mt7996/coredump.c
+index a7f91b56..d09bcd4b 100644
+--- a/mt7996/coredump.c
++++ b/mt7996/coredump.c
+@@ -67,6 +67,44 @@ static const struct mt7996_mem_region mt7996_wa_mem_regions[] = {
+ 	},
+ };
+ 
++static const struct mt7996_mem_region mt7992_wm_mem_regions[] = {
++	{
++		.start = 0x00800000,
++		.len = 0x0004bfff,
++		.name = "ULM0",
++	},
++	{
++		.start = 0x00900000,
++		.len = 0x00035fff,
++		.name = "ULM1",
++	},
++	{
++		.start = 0x02200000,
++		.len = 0x0003ffff,
++		.name = "ULM2",
++	},
++	{
++		.start = 0x00400000,
++		.len = 0x00027fff,
++		.name = "SRAM",
++	},
++	{
++		.start = 0xe0000000,
++		.len = 0x0015ffff,
++		.name = "CRAM0",
++	},
++	{
++		.start = 0xe0160000,
++		.len = 0x00c7fff,
++		.name = "CRAM1",
++	},
++	{
++		.start = 0x7c050000,
++		.len = 0x00007fff,
++		.name = "CONN_INFRA",
++	},
++};
++
+ const struct mt7996_mem_region*
+ mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num)
+ {
+@@ -80,6 +118,14 @@ mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num)
+ 
+ 		*num = ARRAY_SIZE(mt7996_wm_mem_regions);
+ 		return &mt7996_wm_mem_regions[0];
++	case 0x7992:
++		if (type == MT7996_RAM_TYPE_WA) {
++			/* mt7992 wa memory regions is the same as mt7996 */
++			*num = ARRAY_SIZE(mt7996_wa_mem_regions);
++			return &mt7996_wa_mem_regions[0];
++		}
++		*num = ARRAY_SIZE(mt7992_wm_mem_regions);
++		return &mt7992_wm_mem_regions[0];
+ 	default:
+ 		return NULL;
+ 	}
+@@ -115,7 +161,7 @@ struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type)
+ 
+ 	lockdep_assert_held(&dev->dump_mutex);
+ 
+-	if (!coredump_memdump)
++	if (!coredump_memdump || !crash_data->supported)
+ 		return NULL;
+ 
+ 	guid_gen(&crash_data->guid);
+@@ -289,40 +335,46 @@ int mt7996_coredump_register(struct mt7996_dev *dev)
+ 	for (i = 0; i < MT7996_COREDUMP_MAX; i++) {
+ 		crash_data = vzalloc(sizeof(*dev->coredump.crash_data[i]));
+ 		if (!crash_data)
+-			return -ENOMEM;
++			goto nomem;
+ 
+ 		dev->coredump.crash_data[i] = crash_data;
++		crash_data->supported = false;
+ 
+ 		if (coredump_memdump) {
+ 			crash_data->memdump_buf_len = mt7996_coredump_get_mem_size(dev, i);
+ 			if (!crash_data->memdump_buf_len)
+ 				/* no memory content */
+-				return 0;
++				continue;
+ 
+ 			crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len);
+-			if (!crash_data->memdump_buf) {
+-				vfree(crash_data);
+-				return -ENOMEM;
+-			}
++			if (!crash_data->memdump_buf)
++				goto nomem;
++
++			crash_data->supported = true;
+ 		}
+ 	}
+ 
+ 	return 0;
++nomem:
++	mt7996_coredump_unregister(dev);
++	return -ENOMEM;
+ }
+ 
+ void mt7996_coredump_unregister(struct mt7996_dev *dev)
+ {
+ 	int i;
++	struct mt7996_crash_data *crash_data;
+ 
+ 	for (i = 0; i < MT7996_COREDUMP_MAX; i++) {
+-		if (dev->coredump.crash_data[i]->memdump_buf) {
+-			vfree(dev->coredump.crash_data[i]->memdump_buf);
+-			dev->coredump.crash_data[i]->memdump_buf = NULL;
+-			dev->coredump.crash_data[i]->memdump_buf_len = 0;
+-		}
++		crash_data = dev->coredump.crash_data[i];
++
++		if (!crash_data)
++			continue;
++
++		if (crash_data->memdump_buf)
++			vfree(crash_data->memdump_buf);
+ 
+-		vfree(dev->coredump.crash_data[i]);
+-		dev->coredump.crash_data[i] = NULL;
++		vfree(crash_data);
+ 	}
+ }
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index ce54080a..01a5abdf 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -229,6 +229,7 @@ struct mt7996_vif {
+ struct mt7996_crash_data {
+ 	guid_t guid;
+ 	struct timespec64 timestamp;
++	bool supported;
+ 
+ 	u8 *memdump_buf;
+ 	size_t memdump_buf_len;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0038-mtk-wifi-mt76-try-more-times-when-send-message-timeo.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0038-mtk-wifi-mt76-try-more-times-when-send-message-timeo.patch
deleted file mode 100644
index 937ae0c..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0038-mtk-wifi-mt76-try-more-times-when-send-message-timeo.patch
+++ /dev/null
@@ -1,212 +0,0 @@
-From 3cc26e708dbef9b1eb0b5466a073a9c5c8a613b9 Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Mon, 6 Nov 2023 11:10:10 +0800
-Subject: [PATCH 038/116] mtk: wifi: mt76: try more times when send message
- timeout.
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
----
- dma.c        |  7 ++++--
- mcu.c        | 65 ++++++++++++++++++++++++++++++++++++----------------
- mt7996/mac.c | 37 ++++++++++--------------------
- 3 files changed, 62 insertions(+), 47 deletions(-)
-
-diff --git a/dma.c b/dma.c
-index 5604463..66c000e 100644
---- a/dma.c
-+++ b/dma.c
-@@ -504,9 +504,12 @@ mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, struct mt76_queue *q,
- {
- 	struct mt76_queue_buf buf = {};
- 	dma_addr_t addr;
-+	int ret = -ENOMEM;
- 
--	if (test_bit(MT76_MCU_RESET, &dev->phy.state))
-+	if (test_bit(MT76_MCU_RESET, &dev->phy.state)) {
-+		ret = -EAGAIN;
- 		goto error;
-+	}
- 
- 	if (q->queued + 1 >= q->ndesc - 1)
- 		goto error;
-@@ -528,7 +531,7 @@ mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, struct mt76_queue *q,
- 
- error:
- 	dev_kfree_skb(skb);
--	return -ENOMEM;
-+	return ret;
- }
- 
- static int
-diff --git a/mcu.c b/mcu.c
-index fa4b054..2926f71 100644
---- a/mcu.c
-+++ b/mcu.c
-@@ -4,6 +4,7 @@
-  */
- 
- #include "mt76.h"
-+#include "mt76_connac.h"
- #include <linux/moduleparam.h>
- 
- struct sk_buff *
-@@ -74,35 +75,59 @@ int mt76_mcu_skb_send_and_get_msg(struct mt76_dev *dev, struct sk_buff *skb,
- 				  int cmd, bool wait_resp,
- 				  struct sk_buff **ret_skb)
- {
-+#define MT76_MSG_MAX_RETRY_CNT 3
- 	unsigned long expires;
--	int ret, seq;
-+	int ret, seq, retry_cnt;
-+	struct sk_buff *skb_tmp;
-+	bool retry = wait_resp && is_mt7996(dev);
- 
- 	if (ret_skb)
- 		*ret_skb = NULL;
- 
- 	mutex_lock(&dev->mcu.mutex);
--
--	ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb, cmd, &seq);
--	if (ret < 0)
--		goto out;
--
--	if (!wait_resp) {
--		ret = 0;
--		goto out;
-+	retry_cnt = retry ? MT76_MSG_MAX_RETRY_CNT : 1;
-+	while (retry_cnt) {
-+		skb_tmp = mt76_mcu_msg_alloc(dev, skb->data, skb->len);
-+		if (!skb_tmp)
-+			goto out;
-+
-+		if (retry && retry_cnt < MT76_MSG_MAX_RETRY_CNT) {
-+			if (test_bit(MT76_MCU_RESET, &dev->phy.state))
-+				usleep_range(200000, 500000);
-+			dev_err(dev->dev, "send message %08x timeout, try again.\n", cmd);
-+		}
-+
-+		ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb_tmp, cmd, &seq);
-+		if (ret < 0 && ret != -EAGAIN)
-+			goto out;
-+
-+		if (!wait_resp) {
-+			ret = 0;
-+			goto out;
-+		}
-+
-+		expires = jiffies + dev->mcu.timeout;
-+
-+		do {
-+			skb_tmp = mt76_mcu_get_response(dev, expires);
-+			ret = dev->mcu_ops->mcu_parse_response(dev, cmd, skb_tmp, seq);
-+			if (ret == -ETIMEDOUT)
-+				break;
-+
-+			if (!ret && ret_skb)
-+				*ret_skb = skb_tmp;
-+			else
-+				dev_kfree_skb(skb_tmp);
-+
-+			if (ret != -EAGAIN)
-+				goto out;
-+		} while (ret == -EAGAIN);
-+
-+		retry_cnt--;
- 	}
- 
--	expires = jiffies + dev->mcu.timeout;
--
--	do {
--		skb = mt76_mcu_get_response(dev, expires);
--		ret = dev->mcu_ops->mcu_parse_response(dev, cmd, skb, seq);
--		if (!ret && ret_skb)
--			*ret_skb = skb;
--		else
--			dev_kfree_skb(skb);
--	} while (ret == -EAGAIN);
--
- out:
-+	dev_kfree_skb(skb);
- 	mutex_unlock(&dev->mcu.mutex);
- 
- 	return ret;
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 4e52aa1..56827c9 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1784,13 +1784,24 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
- 	phy3 = mt7996_phy3(dev);
- 	dev->recovery.hw_full_reset = true;
- 
--	wake_up(&dev->mt76.mcu.wait);
- 	ieee80211_stop_queues(mt76_hw(dev));
- 	if (phy2)
- 		ieee80211_stop_queues(phy2->mt76->hw);
- 	if (phy3)
- 		ieee80211_stop_queues(phy3->mt76->hw);
- 
-+	set_bit(MT76_RESET, &dev->mphy.state);
-+	set_bit(MT76_MCU_RESET, &dev->mphy.state);
-+	wake_up(&dev->mt76.mcu.wait);
-+	if (phy2) {
-+		set_bit(MT76_RESET, &phy2->mt76->state);
-+		set_bit(MT76_MCU_RESET, &phy2->mt76->state);
-+	}
-+	if (phy3) {
-+		set_bit(MT76_RESET, &phy3->mt76->state);
-+		set_bit(MT76_MCU_RESET, &phy3->mt76->state);
-+	}
-+
- 	cancel_work_sync(&dev->wed_rro.work);
- 	cancel_delayed_work_sync(&dev->mphy.mac_work);
- 	if (phy2)
-@@ -1893,16 +1904,6 @@ void mt7996_mac_reset_work(struct work_struct *work)
- 	set_bit(MT76_MCU_RESET, &dev->mphy.state);
- 	wake_up(&dev->mt76.mcu.wait);
- 
--	cancel_work_sync(&dev->wed_rro.work);
--	cancel_delayed_work_sync(&dev->mphy.mac_work);
--	if (phy2) {
--		set_bit(MT76_RESET, &phy2->mt76->state);
--		cancel_delayed_work_sync(&phy2->mt76->mac_work);
--	}
--	if (phy3) {
--		set_bit(MT76_RESET, &phy3->mt76->state);
--		cancel_delayed_work_sync(&phy3->mt76->mac_work);
--	}
- 	mt76_worker_disable(&dev->mt76.tx_worker);
- 	mt76_for_each_q_rx(&dev->mt76, i) {
- 		if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
-@@ -1913,8 +1914,6 @@ void mt7996_mac_reset_work(struct work_struct *work)
- 	}
- 	napi_disable(&dev->mt76.tx_napi);
- 
--	mutex_lock(&dev->mt76.mutex);
--
- 	mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED);
- 
- 	if (mt7996_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) {
-@@ -1987,20 +1986,8 @@ void mt7996_mac_reset_work(struct work_struct *work)
- 	if (phy3)
- 		ieee80211_wake_queues(phy3->mt76->hw);
- 
--	mutex_unlock(&dev->mt76.mutex);
--
- 	mt7996_update_beacons(dev);
- 
--	ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work,
--				     MT7996_WATCHDOG_TIME);
--	if (phy2)
--		ieee80211_queue_delayed_work(phy2->mt76->hw,
--					     &phy2->mt76->mac_work,
--					     MT7996_WATCHDOG_TIME);
--	if (phy3)
--		ieee80211_queue_delayed_work(phy3->mt76->hw,
--					     &phy3->mt76->mac_work,
--					     MT7996_WATCHDOG_TIME);
- 	dev_info(dev->mt76.dev,"\n%s L1 SER recovery completed.",
- 		 wiphy_name(dev->mt76.hw->wiphy));
- }
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0039-mtk-mt76-mt7996-add-support-for-runtime-set-in-band-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0039-mtk-mt76-mt7996-add-support-for-runtime-set-in-band-.patch
new file mode 100644
index 0000000..51039d9
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0039-mtk-mt76-mt7996-add-support-for-runtime-set-in-band-.patch
@@ -0,0 +1,44 @@
+From 532677abd25d3412eb0ac4da432ab2ef59a532f3 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Tue, 6 Jun 2023 16:57:10 +0800
+Subject: [PATCH 039/199] mtk: mt76: mt7996: add support for runtime set
+ in-band discovery
+
+with this patch, AP can runtime set inband discovery via hostapd_cli
+
+Usage:
+Enable FILS: hostapd_cli -i [interface] inband_discovery 2 20
+Enable UBPR: hostapd_cli -i [interface] inband_discovery 1 20
+Disable inband discovery: hostapd_cli -i [interface] inband_discovery 0 0
+
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ mt7996/mcu.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index a08b24f6..40860f9f 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2612,8 +2612,7 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+ 	if (IS_ERR(rskb))
+ 		return PTR_ERR(rskb);
+ 
+-	if (changed & BSS_CHANGED_FILS_DISCOVERY &&
+-	    vif->bss_conf.fils_discovery.max_interval) {
++	if (changed & BSS_CHANGED_FILS_DISCOVERY) {
+ 		interval = vif->bss_conf.fils_discovery.max_interval;
+ 		skb = ieee80211_get_fils_discovery_tmpl(hw, vif);
+ 	} else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP &&
+@@ -2648,7 +2647,7 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+ 	discov->tx_type = !!(changed & BSS_CHANGED_FILS_DISCOVERY);
+ 	discov->tx_interval = interval;
+ 	discov->prob_rsp_len = cpu_to_le16(MT_TXD_SIZE + skb->len);
+-	discov->enable = true;
++	discov->enable = !!(interval);
+ 	discov->wcid = cpu_to_le16(MT7996_WTBL_RESERVED);
+ 
+ 	buf = (u8 *)tlv + sizeof(*discov);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0039-mtk-wifi-mt76-mt7996-add-SER-overlap-handle.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0039-mtk-wifi-mt76-mt7996-add-SER-overlap-handle.patch
deleted file mode 100644
index ab691e1..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0039-mtk-wifi-mt76-mt7996-add-SER-overlap-handle.patch
+++ /dev/null
@@ -1,105 +0,0 @@
-From 5759eb68a8453f211699bb09d74a39dd9e8d4c02 Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Tue, 21 Nov 2023 09:55:46 +0800
-Subject: [PATCH 039/116] mtk: wifi: mt76: mt7996: add SER overlap handle
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
----
- mcu.c           |  3 ++-
- mt7996/mac.c    | 11 +++++++++++
- mt7996/mcu.c    |  8 ++++++++
- mt7996/mt7996.h |  2 ++
- 4 files changed, 23 insertions(+), 1 deletion(-)
-
-diff --git a/mcu.c b/mcu.c
-index 2926f71..a7afa2d 100644
---- a/mcu.c
-+++ b/mcu.c
-@@ -94,7 +94,8 @@ int mt76_mcu_skb_send_and_get_msg(struct mt76_dev *dev, struct sk_buff *skb,
- 		if (retry && retry_cnt < MT76_MSG_MAX_RETRY_CNT) {
- 			if (test_bit(MT76_MCU_RESET, &dev->phy.state))
- 				usleep_range(200000, 500000);
--			dev_err(dev->dev, "send message %08x timeout, try again.\n", cmd);
-+			dev_err(dev->dev, "send message %08x timeout, try again(%d).\n",
-+				cmd, (MT76_MSG_MAX_RETRY_CNT - retry_cnt));
- 		}
- 
- 		ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb_tmp, cmd, &seq);
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 56827c9..9b5b995 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1885,6 +1885,7 @@ void mt7996_mac_reset_work(struct work_struct *work)
- 	if (!(READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA))
- 		return;
- 
-+	dev->recovery.l1_reset_last = dev->recovery.l1_reset;
- 	dev_info(dev->mt76.dev,"\n%s L1 SER recovery start.",
- 		 wiphy_name(dev->mt76.hw->wiphy));
- 
-@@ -1902,6 +1903,10 @@ void mt7996_mac_reset_work(struct work_struct *work)
- 
- 	set_bit(MT76_RESET, &dev->mphy.state);
- 	set_bit(MT76_MCU_RESET, &dev->mphy.state);
-+	if (phy2)
-+		set_bit(MT76_RESET, &phy2->mt76->state);
-+	if (phy3)
-+		set_bit(MT76_RESET, &phy3->mt76->state);
- 	wake_up(&dev->mt76.mcu.wait);
- 
- 	mt76_worker_disable(&dev->mt76.tx_worker);
-@@ -2097,6 +2102,9 @@ void mt7996_coredump(struct mt7996_dev *dev, u8 state)
- 
- void mt7996_reset(struct mt7996_dev *dev)
- {
-+	dev_info(dev->mt76.dev, "%s SER recovery state: 0x%08x\n",
-+		 wiphy_name(dev->mt76.hw->wiphy), READ_ONCE(dev->recovery.state));
-+
- 	if (!dev->recovery.hw_init_done)
- 		return;
- 
-@@ -2116,6 +2124,9 @@ void mt7996_reset(struct mt7996_dev *dev)
- 		return;
- 	}
- 
-+	if ((READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA))
-+		dev->recovery.l1_reset++;
-+
- 	queue_work(dev->mt76.wq, &dev->reset_work);
- 	wake_up(&dev->reset_wait);
- }
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 2b9dedb..ce26cfb 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -246,6 +246,14 @@ mt7996_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
- 	u32 val;
- 	u8 seq;
- 
-+	if (dev->recovery.l1_reset_last != dev->recovery.l1_reset) {
-+		dev_info(dev->mt76.dev,"\n%s L1 SER recovery overlap, drop message %08x.",
-+			 wiphy_name(dev->mt76.hw->wiphy), cmd);
-+
-+		dev_kfree_skb(skb);
-+		return -EPERM;
-+	}
-+
- 	mdev->mcu.timeout = 20 * HZ;
- 
- 	seq = ++dev->mt76.mcu.msg_seq & 0xf;
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index dbc4aa6..d40f8bf 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -397,6 +397,8 @@ struct mt7996_dev {
- 	wait_queue_head_t reset_wait;
- 	struct {
- 		u32 state;
-+		u32 l1_reset;
-+		u32 l1_reset_last;
- 		u32 wa_reset_count;
- 		u32 wm_reset_count;
- 		bool hw_full_reset:1;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0040-mtk-mt76-mt7996-add-support-spatial-reuse-debug-comm.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0040-mtk-mt76-mt7996-add-support-spatial-reuse-debug-comm.patch
new file mode 100644
index 0000000..6bbd559
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0040-mtk-mt76-mt7996-add-support-spatial-reuse-debug-comm.patch
@@ -0,0 +1,397 @@
+From cb8632560f8055d6af142932abf723d54af5c25e Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 10 Jul 2023 11:47:29 +0800
+Subject: [PATCH 040/199] mtk: mt76: mt7996: add support spatial reuse debug
+ commands
+
+This commit adds the following debug commands in debugfs:
+1. sr_enable: enable/disable spatial reuse feature. Default is on.
+2. sr_enhanced_enable: enable/disable enhanced spatial reuse feature.
+Default is on. This feature is mtk proprietary feature.
+3. sr_stats: Check the Spatial reuse tx statistics.
+4. sr_scene_cond: Check the result of mtk scene detection algorithm. Mtk
+scene detection algorithm in firmware may decide whether current
+environment can SR Tx or not.
+
+To learn more details of these commands, please check:
+https://wiki.mediatek.inc/display/APKB/mt76+Phy+feature+debug+Cheetsheet#mt76PhyfeaturedebugCheetsheet-SpatialReuse
+
+---
+ mt76_connac_mcu.h    |   1 +
+ mt7996/main.c        |   6 +++
+ mt7996/mcu.c         |   8 ++++
+ mt7996/mt7996.h      |   6 +++
+ mt7996/mtk_debugfs.c |  82 ++++++++++++++++++++++++++++++++
+ mt7996/mtk_mcu.c     | 111 +++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mtk_mcu.h     |  56 ++++++++++++++++++++++
+ 7 files changed, 270 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 5a1b4d0d..726f5e9d 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1051,6 +1051,7 @@ enum {
+ 	MCU_UNI_EVENT_BSS_BEACON_LOSS = 0x0c,
+ 	MCU_UNI_EVENT_SCAN_DONE = 0x0e,
+ 	MCU_UNI_EVENT_RDD_REPORT = 0x11,
++	MCU_UNI_EVENT_SR = 0x25,
+ 	MCU_UNI_EVENT_ROC = 0x27,
+ 	MCU_UNI_EVENT_TX_DONE = 0x2d,
+ 	MCU_UNI_EVENT_BF = 0x33,
+diff --git a/mt7996/main.c b/mt7996/main.c
+index a51fc696..f7819a85 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -6,6 +6,9 @@
+ #include "mt7996.h"
+ #include "mcu.h"
+ #include "mac.h"
++#ifdef CONFIG_MTK_DEBUG
++#include "mtk_mcu.h"
++#endif
+ 
+ static bool mt7996_dev_running(struct mt7996_dev *dev)
+ {
+@@ -86,6 +89,9 @@ int mt7996_run(struct ieee80211_hw *hw)
+ 		goto out;
+ 
+ #ifdef CONFIG_MTK_DEBUG
++	phy->sr_enable = true;
++	phy->enhanced_sr_enable = true;
++
+ 	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
+ 						dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
+ 
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 40860f9f..362af7aa 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -717,6 +717,14 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	case MCU_UNI_EVENT_WED_RRO:
+ 		mt7996_mcu_wed_rro_event(dev, skb);
+ 		break;
++#ifdef CONFIG_MTK_DEBUG
++	case MCU_UNI_EVENT_SR:
++		mt7996_mcu_rx_sr_event(dev, skb);
++		break;
++#endif
++	case MCU_UNI_EVENT_THERMAL:
++		mt7996_mcu_rx_thermal_notify(dev, skb);
++		break;
+ #ifdef CONFIG_NL80211_TESTMODE
+ 	case MCU_UNI_EVENT_TESTMODE_CTRL:
+ 		mt7996_tm_rf_test_event(dev, skb);
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 01a5abdf..20fa4221 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -357,6 +357,10 @@ struct mt7996_phy {
+ 	spinlock_t amnt_lock;
+ 	struct mt7996_air_monitor_ctrl amnt_ctrl;
+ #endif
++#ifdef CONFIG_MTK_DEBUG
++	bool sr_enable:1;
++	bool enhanced_sr_enable:1;
++#endif
+ };
+ 
+ struct mt7996_dev {
+@@ -807,6 +811,8 @@ enum edcca_bw_id {
+ #ifdef CONFIG_MTK_DEBUG
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
+ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
++int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set);
++void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ #endif
+ 
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 053005dd..06dc794e 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2835,6 +2835,83 @@ static int mt7996_show_eeprom_mode(struct seq_file *s, void *data)
+ 	return 0;
+ }
+ 
++static int
++mt7996_sr_enable_get(void *data, u64 *val)
++{
++	struct mt7996_phy *phy = data;
++
++	*val = phy->sr_enable;
++
++	return 0;
++}
++
++static int
++mt7996_sr_enable_set(void *data, u64 val)
++{
++	struct mt7996_phy *phy = data;
++	int ret;
++
++	if (!!val == phy->sr_enable)
++		return 0;
++
++	ret = mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_CFG_SR_ENABLE, val, true);
++	if (ret)
++		return ret;
++
++	return mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_CFG_SR_ENABLE, 0, false);
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_sr_enable, mt7996_sr_enable_get,
++			 mt7996_sr_enable_set, "%lld\n");
++static int
++mt7996_sr_enhanced_enable_get(void *data, u64 *val)
++{
++	struct mt7996_phy *phy = data;
++
++	*val = phy->enhanced_sr_enable;
++
++	return 0;
++}
++
++static int
++mt7996_sr_enhanced_enable_set(void *data, u64 val)
++{
++	struct mt7996_phy *phy = data;
++	int ret;
++
++	if (!!val == phy->enhanced_sr_enable)
++		return 0;
++
++	ret = mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_HW_ENHANCE_SR_ENABLE, val, true);
++	if (ret)
++		return ret;
++
++	return mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_HW_ENHANCE_SR_ENABLE, 0, false);
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_sr_enhanced_enable, mt7996_sr_enhanced_enable_get,
++			 mt7996_sr_enhanced_enable_set, "%lld\n");
++
++static int
++mt7996_sr_stats_show(struct seq_file *file, void *data)
++{
++	struct mt7996_phy *phy = file->private;
++
++	mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_HW_IND, 0, false);
++
++	return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(mt7996_sr_stats);
++
++static int
++mt7996_sr_scene_cond_show(struct seq_file *file, void *data)
++{
++	struct mt7996_phy *phy = file->private;
++
++	mt7996_mcu_set_sr_enable(phy, UNI_CMD_SR_SW_SD, 0, false);
++
++	return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(mt7996_sr_scene_cond);
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+@@ -2915,6 +2992,11 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ 	debugfs_create_u8("sku_disable", 0600, dir, &dev->dbg.sku_disable);
+ 	debugfs_create_file("scs_enable", 0200, dir, phy, &fops_scs_enable);
+ 
++	debugfs_create_file("sr_enable", 0600, dir, phy, &fops_sr_enable);
++	debugfs_create_file("sr_enhanced_enable", 0600, dir, phy, &fops_sr_enhanced_enable);
++	debugfs_create_file("sr_stats", 0400, dir, phy, &mt7996_sr_stats_fops);
++	debugfs_create_file("sr_scene_cond", 0400, dir, phy, &mt7996_sr_scene_cond_fops);
++
+ 	return 0;
+ }
+ 
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index 5c54d02c..dbdf8d80 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -146,4 +146,115 @@ int mt7996_mcu_edcca_threshold_ctrl(struct mt7996_phy *phy, u8 *value, bool set)
+ 	return 0;
+ }
+ 
++int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set)
++{
++	struct {
++		u8 band_idx;
++		u8 _rsv[3];
++
++		__le16 tag;
++		__le16 len;
++
++		__le32 val;
++
++	} __packed req = {
++		.band_idx = phy->mt76->band_idx,
++
++		.tag = cpu_to_le16(action),
++		.len = cpu_to_le16(sizeof(req) - 4),
++
++		.val = cpu_to_le32((u32) val),
++	};
++
++	if (set)
++		return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SR), &req,
++					 sizeof(req), false);
++	else
++		return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD_QUERY(SR), &req,
++					 sizeof(req), false);
++}
++
++void mt7996_mcu_rx_sr_swsd(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++#define SR_SCENE_DETECTION_TIMER_PERIOD_MS 500
++	struct mt7996_mcu_sr_swsd_event *event;
++	static const char * const rules[] = {"1 - NO CONNECTED", "2 - NO CONGESTION",
++					     "3 - NO INTERFERENCE", "4 - SR ON"};
++	u8 idx;
++
++	event = (struct mt7996_mcu_sr_swsd_event *)skb->data;
++	idx = event->basic.band_idx;
++
++	dev_info(dev->mt76.dev, "Band index = %u\n", le16_to_cpu(event->basic.band_idx));
++	dev_info(dev->mt76.dev, "Hit Rule = %s\n", rules[event->tlv[idx].rule]);
++	dev_info(dev->mt76.dev, "Timer Period = %d(us)\n"
++		 "Congestion Ratio  = %d.%1d%%\n",
++		 SR_SCENE_DETECTION_TIMER_PERIOD_MS * 1000,
++		 le32_to_cpu(event->tlv[idx].total_airtime_ratio) / 10,
++		 le32_to_cpu(event->tlv[idx].total_airtime_ratio) % 10);
++	dev_info(dev->mt76.dev,
++		 "Total Airtime = %d(us)\n"
++		 "ChBusy = %d\n"
++		 "SrTx = %d\n"
++		 "OBSS = %d\n"
++		 "MyTx = %d\n"
++		 "MyRx = %d\n"
++		 "Interference Ratio = %d.%1d%%\n",
++		 le32_to_cpu(event->tlv[idx].total_airtime),
++		 le32_to_cpu(event->tlv[idx].channel_busy_time),
++		 le32_to_cpu(event->tlv[idx].sr_tx_airtime),
++		 le32_to_cpu(event->tlv[idx].obss_airtime),
++		 le32_to_cpu(event->tlv[idx].my_tx_airtime),
++		 le32_to_cpu(event->tlv[idx].my_rx_airtime),
++		 le32_to_cpu(event->tlv[idx].obss_airtime_ratio) / 10,
++		 le32_to_cpu(event->tlv[idx].obss_airtime_ratio) % 10);
++}
++
++void mt7996_mcu_rx_sr_hw_indicator(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++	struct mt7996_mcu_sr_hw_ind_event *event;
++
++	event = (struct mt7996_mcu_sr_hw_ind_event *)skb->data;
++
++	dev_info(dev->mt76.dev, "Inter PPDU Count = %u\n",
++		 le16_to_cpu(event->inter_bss_ppdu_cnt));
++	dev_info(dev->mt76.dev, "SR Valid Count = %u\n",
++		 le16_to_cpu(event->non_srg_valid_cnt));
++	dev_info(dev->mt76.dev, "SR Tx Count = %u\n",
++		 le32_to_cpu(event->sr_ampdu_mpdu_cnt));
++	dev_info(dev->mt76.dev, "SR Tx Acked Count = %u\n",
++		 le32_to_cpu(event->sr_ampdu_mpdu_acked_cnt));
++}
++
++void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++	struct mt76_phy *mphy = &dev->mt76.phy;
++	struct mt7996_phy *phy;
++	struct mt7996_mcu_sr_common_event *event;
++
++	event = (struct mt7996_mcu_sr_common_event *)skb->data;
++	mphy = dev->mt76.phys[event->basic.band_idx];
++	if (!mphy)
++		return;
++
++	phy = (struct mt7996_phy *)mphy->priv;
++
++	switch (le16_to_cpu(event->basic.tag)) {
++	case UNI_EVENT_SR_CFG_SR_ENABLE:
++		phy->sr_enable = le32_to_cpu(event->value) ? true : false;
++		break;
++	case UNI_EVENT_SR_HW_ESR_ENABLE:
++		phy->enhanced_sr_enable = le32_to_cpu(event->value) ? true : false;
++		break;
++	case UNI_EVENT_SR_SW_SD:
++		mt7996_mcu_rx_sr_swsd(dev, skb);
++		break;
++	case UNI_EVENT_SR_HW_IND:
++		mt7996_mcu_rx_sr_hw_indicator(dev, skb);
++		break;
++	default:
++		dev_info(dev->mt76.dev, "Unknown SR event tag %d\n",
++			 le16_to_cpu(event->basic.tag));
++	}
++}
+ #endif
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+index 36a58ad6..098e63ae 100644
+--- a/mt7996/mtk_mcu.h
++++ b/mt7996/mtk_mcu.h
+@@ -121,6 +121,62 @@ enum {
+ 	EDCCA_JAPAN = 3
+ };
+ 
++enum {
++	UNI_EVENT_SR_CFG_SR_ENABLE = 0x1,
++	UNI_EVENT_SR_SW_SD = 0x83,
++	UNI_EVENT_SR_HW_IND = 0xC9,
++	UNI_EVENT_SR_HW_ESR_ENABLE = 0xD8,
++};
++enum {
++	UNI_CMD_SR_CFG_SR_ENABLE = 0x1,
++	UNI_CMD_SR_SW_SD = 0x84,
++	UNI_CMD_SR_HW_IND = 0xCB,
++	UNI_CMD_SR_HW_ENHANCE_SR_ENABLE = 0xDA,
++};
++
++struct mt7996_mcu_sr_basic_event {
++	struct mt7996_mcu_rxd rxd;
++
++	u8 band_idx;
++	u8 _rsv[3];
++
++	__le16 tag;
++	__le16 len;
++};
++
++struct sr_sd_tlv {
++	u8 _rsv[16];
++	__le32 sr_tx_airtime;
++	__le32 obss_airtime;
++	__le32 my_tx_airtime;
++	__le32 my_rx_airtime;
++	__le32 channel_busy_time;
++	__le32 total_airtime;
++	__le32 total_airtime_ratio;
++	__le32 obss_airtime_ratio;
++	u8 rule;
++	u8 _rsv2[59];
++} __packed;
++
++struct mt7996_mcu_sr_swsd_event {
++	struct mt7996_mcu_sr_basic_event basic;
++	struct sr_sd_tlv tlv[3];
++} __packed;
++
++struct mt7996_mcu_sr_common_event {
++	struct mt7996_mcu_sr_basic_event basic;
++	__le32 value;
++};
++
++struct mt7996_mcu_sr_hw_ind_event {
++	struct mt7996_mcu_sr_basic_event basic;
++	__le16 non_srg_valid_cnt;
++	u8 _rsv[4];
++	__le16 inter_bss_ppdu_cnt;
++	u8 _rsv2[4];
++	__le32 sr_ampdu_mpdu_cnt;
++	__le32 sr_ampdu_mpdu_acked_cnt;
++};
+ #endif
+ 
+ #endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0040-mtk-wifi-mt76-mt7996-kite-default-1-pcie-setting.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0040-mtk-wifi-mt76-mt7996-kite-default-1-pcie-setting.patch
deleted file mode 100644
index 2bb7510..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0040-mtk-wifi-mt76-mt7996-kite-default-1-pcie-setting.patch
+++ /dev/null
@@ -1,56 +0,0 @@
-From 9d16189b29de2e3c2fb0daf7fe5238f489b26818 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Thu, 13 Jul 2023 16:36:36 +0800
-Subject: [PATCH 040/116] mtk: wifi: mt76: mt7996: kite default 1-pcie setting
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/pci.c | 11 +++++++++++
- 1 file changed, 11 insertions(+)
-
-diff --git a/mt7996/pci.c b/mt7996/pci.c
-index 0405618..05830c0 100644
---- a/mt7996/pci.c
-+++ b/mt7996/pci.c
-@@ -11,6 +11,9 @@
- #include "mac.h"
- #include "../trace.h"
- 
-+static bool hif2_enable = false;
-+module_param(hif2_enable, bool, 0644);
-+
- static LIST_HEAD(hif_list);
- static DEFINE_SPINLOCK(hif_lock);
- static u32 hif_idx;
-@@ -63,6 +66,9 @@ static struct mt7996_hif *mt7996_pci_init_hif2(struct pci_dev *pdev)
- {
- 	hif_idx++;
- 
-+	if (!hif2_enable)
-+		return NULL;
-+
- 	if (!pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x7991, NULL) &&
- 	    !pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x799a, NULL))
- 		return NULL;
-@@ -77,6 +83,9 @@ static int mt7996_pci_hif2_probe(struct pci_dev *pdev)
- {
- 	struct mt7996_hif *hif;
- 
-+	if (!hif2_enable)
-+		return 0;
-+
- 	hif = devm_kzalloc(&pdev->dev, sizeof(*hif), GFP_KERNEL);
- 	if (!hif)
- 		return -ENOMEM;
-@@ -101,6 +110,8 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
- 	int irq, hif2_irq, ret;
- 	struct mt76_dev *mdev;
- 
-+	hif2_enable |= (id->device == 0x7990 || id->device == 0x7991);
-+
- 	ret = pcim_enable_device(pdev);
- 	if (ret)
- 		return ret;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0041-mtk-mt76-mt7996-Establish-BA-in-VO-queue.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0041-mtk-mt76-mt7996-Establish-BA-in-VO-queue.patch
new file mode 100644
index 0000000..9a0dd2a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0041-mtk-mt76-mt7996-Establish-BA-in-VO-queue.patch
@@ -0,0 +1,25 @@
+From c8d8e7a0cc7d030616f27bd5cbb0cb9c8e51b746 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Tue, 1 Aug 2023 16:02:28 +0800
+Subject: [PATCH 041/199] mtk: mt76: mt7996: Establish BA in VO queue
+
+---
+ mt7996/mac.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index d9e8e751..8226e443 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1041,8 +1041,6 @@ mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb)
+ 		return;
+ 
+ 	tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
+-	if (tid >= 6) /* skip VO queue */
+-		return;
+ 
+ 	if (is_8023) {
+ 		fc = IEEE80211_FTYPE_DATA |
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0041-mtk-wifi-mt76-mt7996-add-debugfs-knob-for-rx_counter.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0041-mtk-wifi-mt76-mt7996-add-debugfs-knob-for-rx_counter.patch
deleted file mode 100644
index 6b2abc6..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0041-mtk-wifi-mt76-mt7996-add-debugfs-knob-for-rx_counter.patch
+++ /dev/null
@@ -1,291 +0,0 @@
-From 9e924043388155d6e27199f90755a282d4b13126 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Fri, 28 Apr 2023 10:39:58 +0800
-Subject: [PATCH 041/116] mtk: wifi: mt76: mt7996: add debugfs knob for
- rx_counters
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- agg-rx.c             |  8 ++++++++
- mac80211.c           | 16 ++++++++++++++--
- mt76.h               | 15 +++++++++++++++
- mt7996/mac.c         | 18 +++++++++++++++---
- mt7996/mtk_debugfs.c | 42 ++++++++++++++++++++++++++++++++++++++++++
- 5 files changed, 94 insertions(+), 5 deletions(-)
-
-diff --git a/agg-rx.c b/agg-rx.c
-index 07c386c..37588ac 100644
---- a/agg-rx.c
-+++ b/agg-rx.c
-@@ -33,10 +33,13 @@ mt76_rx_aggr_release_frames(struct mt76_rx_tid *tid,
- 			    struct sk_buff_head *frames,
- 			    u16 head)
- {
-+	struct mt76_phy *phy = mt76_dev_phy(tid->dev, tid->band_idx);
- 	int idx;
- 
- 	while (ieee80211_sn_less(tid->head, head)) {
- 		idx = tid->head % tid->size;
-+		if (!tid->reorder_buf[idx])
-+			phy->rx_stats.rx_agg_miss++;
- 		mt76_aggr_release(tid, frames, idx);
- 	}
- }
-@@ -151,6 +154,7 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
- 	struct mt76_wcid *wcid = status->wcid;
- 	struct ieee80211_sta *sta;
- 	struct mt76_rx_tid *tid;
-+	struct mt76_phy *phy;
- 	bool sn_less;
- 	u16 seqno, head, size, idx;
- 	u8 tidno = status->qos_ctl & IEEE80211_QOS_CTL_TID_MASK;
-@@ -186,6 +190,7 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
- 	head = tid->head;
- 	seqno = status->seqno;
- 	size = tid->size;
-+	phy = mt76_dev_phy(tid->dev, tid->band_idx);
- 	sn_less = ieee80211_sn_less(seqno, head);
- 
- 	if (!tid->started) {
-@@ -197,6 +202,7 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
- 
- 	if (sn_less) {
- 		__skb_unlink(skb, frames);
-+		phy->rx_stats.rx_dup_drop++;
- 		dev_kfree_skb(skb);
- 		goto out;
- 	}
-@@ -223,6 +229,7 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
- 
- 	/* Discard if the current slot is already in use */
- 	if (tid->reorder_buf[idx]) {
-+		phy->rx_stats.rx_dup_drop++;
- 		dev_kfree_skb(skb);
- 		goto out;
- 	}
-@@ -254,6 +261,7 @@ int mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tidno,
- 	tid->head = ssn;
- 	tid->size = size;
- 	tid->num = tidno;
-+	tid->band_idx = wcid->phy_idx;
- 	INIT_DELAYED_WORK(&tid->reorder_work, mt76_rx_aggr_reorder_work);
- 	spin_lock_init(&tid->lock);
- 
-diff --git a/mac80211.c b/mac80211.c
-index e2ff011..93e5c50 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -784,6 +784,7 @@ static void mt76_rx_release_amsdu(struct mt76_phy *phy, enum mt76_rxq_id q)
- 		}
- 
- 		if (ether_addr_equal(skb->data + offset, rfc1042_header)) {
-+			phy->rx_stats.rx_drop++;
- 			dev_kfree_skb(skb);
- 			return;
- 		}
-@@ -1100,10 +1101,16 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
- 
- 	*sta = wcid_to_sta(mstat.wcid);
- 	*hw = mt76_phy_hw(dev, mstat.phy_idx);
-+
-+	if ((mstat.flag & RX_FLAG_8023) || ieee80211_is_data_qos(hdr->frame_control)) {
-+		struct mt76_phy *phy = mt76_dev_phy(dev, mstat.phy_idx);
-+
-+		phy->rx_stats.rx_mac80211++;
-+	}
- }
- 
- static void
--mt76_check_ccmp_pn(struct sk_buff *skb)
-+mt76_check_ccmp_pn(struct mt76_dev *dev, struct sk_buff *skb)
- {
- 	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
- 	struct mt76_wcid *wcid = status->wcid;
-@@ -1150,7 +1157,11 @@ skip_hdr_check:
- 	ret = memcmp(status->iv, wcid->rx_key_pn[security_idx],
- 		     sizeof(status->iv));
- 	if (ret <= 0) {
-+		struct mt76_phy *phy = mt76_dev_phy(dev, status->phy_idx);
-+
-+		phy->rx_stats.rx_pn_iv_error++;
- 		status->flag |= RX_FLAG_ONLY_MONITOR;
-+
- 		return;
- 	}
- 
-@@ -1331,7 +1342,7 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
- 	while ((skb = __skb_dequeue(frames)) != NULL) {
- 		struct sk_buff *nskb = skb_shinfo(skb)->frag_list;
- 
--		mt76_check_ccmp_pn(skb);
-+		mt76_check_ccmp_pn(dev, skb);
- 		skb_shinfo(skb)->frag_list = NULL;
- 		mt76_rx_convert(dev, skb, &hw, &sta);
- 		ieee80211_rx_list(hw, sta, skb, &list);
-@@ -1354,6 +1365,7 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
- 	}
- 
- 	list_for_each_entry_safe(skb, tmp, &list, list) {
-+		dev->rx_kernel++;
- 		skb_list_del_init(skb);
- 		napi_gro_receive(napi, skb);
- 	}
-diff --git a/mt76.h b/mt76.h
-index 43e4585..0cc6e53 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -428,6 +428,7 @@ struct mt76_rx_tid {
- 	struct rcu_head rcu_head;
- 
- 	struct mt76_dev *dev;
-+	u8 band_idx;
- 
- 	spinlock_t lock;
- 	struct delayed_work reorder_work;
-@@ -859,6 +860,19 @@ struct mt76_phy {
- 		bool al;
- 		u8 pin;
- 	} leds;
-+
-+	struct {
-+		u32 rx_mac80211;
-+
-+		u32 rx_drop;
-+		u32 rx_rxd_drop;
-+		u32 rx_dup_drop;
-+		u32 rx_agg_miss;
-+		u32 rx_icv_error;
-+		u32 rx_fcs_error;
-+		u32 rx_tkip_mic_error;
-+		u32 rx_pn_iv_error;
-+	} rx_stats;
- };
- 
- struct mt76_dev {
-@@ -964,6 +978,7 @@ struct mt76_dev {
- 	};
- 
- 	const char *bin_file_name;
-+	u32 rx_kernel;
- };
- 
- /* per-phy stats.  */
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 9b5b995..18616fd 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -469,8 +469,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
- 		return -EINVAL;
- 
- 	/* ICV error or CCMP/BIP/WPI MIC error */
--	if (rxd1 & MT_RXD1_NORMAL_ICV_ERR)
-+	if (rxd1 & MT_RXD1_NORMAL_ICV_ERR) {
-+		mphy->rx_stats.rx_icv_error++;
- 		status->flag |= RX_FLAG_ONLY_MONITOR;
-+	}
- 
- 	unicast = FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) == MT_RXD3_NORMAL_U2M;
- 	idx = FIELD_GET(MT_RXD1_NORMAL_WLAN_IDX, rxd1);
-@@ -501,11 +503,15 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
- 	    !(csum_status & (BIT(0) | BIT(2) | BIT(3))))
- 		skb->ip_summed = CHECKSUM_UNNECESSARY;
- 
--	if (rxd1 & MT_RXD3_NORMAL_FCS_ERR)
-+	if (rxd1 & MT_RXD3_NORMAL_FCS_ERR) {
-+		mphy->rx_stats.rx_fcs_error++;
- 		status->flag |= RX_FLAG_FAILED_FCS_CRC;
-+	}
- 
--	if (rxd1 & MT_RXD1_NORMAL_TKIP_MIC_ERR)
-+	if (rxd1 & MT_RXD1_NORMAL_TKIP_MIC_ERR) {
-+		mphy->rx_stats.rx_tkip_mic_error++;
- 		status->flag |= RX_FLAG_MMIC_ERROR;
-+	}
- 
- 	if (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2) != 0 &&
- 	    !(rxd1 & (MT_RXD1_NORMAL_CLM | MT_RXD1_NORMAL_CM))) {
-@@ -1415,8 +1421,10 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
- 			 struct sk_buff *skb, u32 *info)
- {
- 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
-+	struct mt76_phy *phy;
- 	__le32 *rxd = (__le32 *)skb->data;
- 	__le32 *end = (__le32 *)&skb->data[skb->len];
-+	u8 band_idx;
- 	enum rx_pkt_type type;
- 
- 	type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE);
-@@ -1458,6 +1466,10 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
- 		}
- 		fallthrough;
- 	default:
-+		band_idx = le32_get_bits(rxd[1], MT_RXD1_NORMAL_BAND_IDX);
-+		phy = mt76_dev_phy(mdev, band_idx);
-+		if (likely(phy))
-+			phy->rx_stats.rx_rxd_drop++;
- 		dev_kfree_skb(skb);
- 		break;
- 	}
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index 3eb55a3..af7d3a1 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -2913,6 +2913,46 @@ mt7996_sr_scene_cond_show(struct seq_file *file, void *data)
- }
- DEFINE_SHOW_ATTRIBUTE(mt7996_sr_scene_cond);
- 
-+static int mt7996_rx_counters(struct seq_file *s, void *data)
-+{
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	u32 rx_mac80211 = 0;
-+	int i = 0;
-+
-+	for (i = 0; i < __MT_MAX_BAND; i++) {
-+		struct mt76_phy *phy = mt76_dev_phy(&dev->mt76, i);
-+
-+		if (!phy)
-+			continue;
-+
-+		seq_printf(s, "\n==========PHY%d==========\n", i);
-+
-+#define SEQ_PRINT(_str, _rx_param) do {					\
-+		seq_printf(s, _str"\n", phy->rx_stats._rx_param);	\
-+	} while (0)
-+
-+		SEQ_PRINT("Rx to mac80211: %u", rx_mac80211);
-+		SEQ_PRINT("Rx drop: %u", rx_drop);
-+		SEQ_PRINT("Rx drop due to RXD type error: %u", rx_rxd_drop);
-+		SEQ_PRINT("Rx duplicated drop: %u", rx_dup_drop);
-+		SEQ_PRINT("Rx agg miss: %u", rx_agg_miss);
-+		SEQ_PRINT("Rx ICV error: %u", rx_icv_error);
-+		SEQ_PRINT("Rx FCS error: %u", rx_fcs_error);
-+		SEQ_PRINT("Rx TKIP MIC error: %u", rx_tkip_mic_error);
-+		SEQ_PRINT("Rx PN/IV error: %u", rx_pn_iv_error);
-+#undef SEQ_PRINT
-+
-+		rx_mac80211 += phy->rx_stats.rx_mac80211;
-+	}
-+
-+	seq_printf(s, "\n==========SUM==========\n");
-+	seq_printf(s, "Rx to kernel: %u\n", dev->mt76.rx_kernel);
-+	seq_printf(s, "Rx to mac80211: %u\n", rx_mac80211);
-+
-+
-+	return 0;
-+}
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- 	struct mt7996_dev *dev = phy->dev;
-@@ -2976,6 +3016,8 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- 
- 	debugfs_create_devm_seqfile(dev->mt76.dev, "tr_info", dir,
- 				    mt7996_trinfo_read);
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "rx_counters", dir,
-+				    mt7996_rx_counters);
- 	debugfs_create_file("txpower_level", 0600, dir, phy, &fops_txpower_level);
- 	debugfs_create_file("txpower_info", 0600, dir, phy, &mt7996_txpower_info_fops);
- 	debugfs_create_file("txpower_sku", 0600, dir, phy, &mt7996_txpower_sku_fops);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0042-mtk-mt76-mt7996-report-tx-and-rx-byte-to-tpt_led.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0042-mtk-mt76-mt7996-report-tx-and-rx-byte-to-tpt_led.patch
new file mode 100644
index 0000000..daa1f6b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0042-mtk-mt76-mt7996-report-tx-and-rx-byte-to-tpt_led.patch
@@ -0,0 +1,46 @@
+From 495a443a1a4b9411c9926dbea98a3e54c0afc81c Mon Sep 17 00:00:00 2001
+From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Date: Sat, 12 Aug 2023 04:17:22 +0800
+Subject: [PATCH 042/199] mtk: mt76: mt7996: report tx and rx byte to tpt_led
+
+---
+ mt7996/mcu.c | 15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 362af7aa..548f6660 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -525,6 +525,8 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 		u8 ac;
+ 		u16 wlan_idx;
+ 		struct mt76_wcid *wcid;
++		struct mt76_phy *mphy;
++		u32 tx_bytes, rx_bytes;
+ 
+ 		switch (le16_to_cpu(res->tag)) {
+ 		case UNI_ALL_STA_TXRX_RATE:
+@@ -544,11 +546,16 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 			if (!wcid)
+ 				break;
+ 
++			mphy = mt76_dev_phy(&dev->mt76, wcid->phy_idx);
+ 			for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+-				wcid->stats.tx_bytes +=
+-					le32_to_cpu(res->adm_stat[i].tx_bytes[ac]);
+-				wcid->stats.rx_bytes +=
+-					le32_to_cpu(res->adm_stat[i].rx_bytes[ac]);
++				tx_bytes = le32_to_cpu(res->adm_stat[i].tx_bytes[ac]);
++				rx_bytes = le32_to_cpu(res->adm_stat[i].rx_bytes[ac]);
++
++				wcid->stats.tx_bytes += tx_bytes;
++				wcid->stats.rx_bytes += rx_bytes;
++
++				ieee80211_tpt_led_trig_tx(mphy->hw, tx_bytes);
++				ieee80211_tpt_led_trig_rx(mphy->hw, rx_bytes);
+ 			}
+ 			break;
+ 		case UNI_ALL_STA_TXRX_MSDU_COUNT:
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0042-mtk-wifi-mt76-mt7996-support-BF-MIMO-debug-commands.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0042-mtk-wifi-mt76-mt7996-support-BF-MIMO-debug-commands.patch
deleted file mode 100644
index 55a47be..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0042-mtk-wifi-mt76-mt7996-support-BF-MIMO-debug-commands.patch
+++ /dev/null
@@ -1,1210 +0,0 @@
-From 8ffdd68c3265dbb716322ccfb0a8c0e230616335 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Tue, 3 Jan 2023 09:42:07 +0800
-Subject: [PATCH 042/116] mtk: wifi: mt76: mt7996: support BF/MIMO debug
- commands
-
-This commit includes the following commands:
-1. starec_bf_read
-2. txbf_snd_info: start/stop sounding and set sounding period
-3. fbkRptInfo
-4. fix muru rate
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-fix the wrong wlan_idx for user3
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
-
-Align the format of mcu event "mt7996_mcu_bf_starec_read" with
-firmware definition.
-
-Fw gerrit change:
-https://gerrit.mediatek.inc/c/neptune/firmware/bora/wifi/core/+/8218143
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- mt7996/mcu.c         |   5 +
- mt7996/mcu.h         |   4 +
- mt7996/mt7996.h      |   5 +
- mt7996/mtk_debugfs.c | 120 +++++++++
- mt7996/mtk_mcu.c     | 624 +++++++++++++++++++++++++++++++++++++++++++
- mt7996/mtk_mcu.h     | 342 ++++++++++++++++++++++++
- 6 files changed, 1100 insertions(+)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index ce26cfb..d39a73f 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -744,6 +744,11 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
- 	case MCU_UNI_EVENT_TESTMODE_CTRL:
- 		mt7996_tm_rf_test_event(dev, skb);
- 		break;
-+#endif
-+#if defined CONFIG_NL80211_TESTMODE || defined CONFIG_MTK_DEBUG
-+	case MCU_UNI_EVENT_BF:
-+		mt7996_mcu_rx_bf_event(dev, skb);
-+		break;
- #endif
- 	default:
- 		break;
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index cb7260f..29bd7a5 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -770,8 +770,12 @@ enum {
- 
- enum {
- 	BF_SOUNDING_ON = 1,
-+	BF_PFMU_TAG_READ = 5,
-+	BF_STA_REC_READ = 11,
- 	BF_HW_EN_UPDATE = 17,
- 	BF_MOD_EN_CTRL = 20,
-+	BF_FBRPT_DBG_INFO_READ = 23,
-+	BF_TXSND_INFO = 24,
- };
- 
- enum {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index d40f8bf..4602b4e 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -816,6 +816,11 @@ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
- int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set);
- void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb);
- int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev);
-+int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx);
-+void mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb);
-+int mt7996_mcu_set_muru_fixed_rate_enable(struct mt7996_dev *dev, u8 action, int val);
-+int mt7996_mcu_set_muru_fixed_rate_parameter(struct mt7996_dev *dev, u8 action, void *para);
-+int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para);
- #endif
- 
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index af7d3a1..0851d65 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -2953,6 +2953,117 @@ static int mt7996_rx_counters(struct seq_file *s, void *data)
- 	return 0;
- }
- 
-+static int
-+mt7996_starec_bf_read_set(void *data, u64 wlan_idx)
-+{
-+	struct mt7996_phy *phy = data;
-+
-+	return mt7996_mcu_set_txbf_internal(phy, BF_STA_REC_READ, wlan_idx);
-+}
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_starec_bf_read, NULL,
-+			 mt7996_starec_bf_read_set, "%lld\n");
-+
-+static ssize_t
-+mt7996_bf_txsnd_info_set(struct file *file,
-+			 const char __user *user_buf,
-+			 size_t count, loff_t *ppos)
-+{
-+	struct mt7996_phy *phy = file->private_data;
-+	char buf[40];
-+	int ret;
-+
-+	if (count >= sizeof(buf))
-+		return -EINVAL;
-+
-+	if (copy_from_user(buf, user_buf, count))
-+		return -EFAULT;
-+
-+	if (count && buf[count - 1] == '\n')
-+		buf[count - 1] = '\0';
-+	else
-+		buf[count] = '\0';
-+
-+	ret = mt7996_mcu_set_txbf_snd_info(phy, buf);
-+
-+	if (ret) return -EFAULT;
-+
-+	return count;
-+}
-+
-+static const struct file_operations fops_bf_txsnd_info = {
-+	.write = mt7996_bf_txsnd_info_set,
-+	.read = NULL,
-+	.open = simple_open,
-+	.llseek = default_llseek,
-+};
-+
-+static int
-+mt7996_bf_fbk_rpt_set(void *data, u64 wlan_idx)
-+{
-+	struct mt7996_phy *phy = data;
-+
-+	return mt7996_mcu_set_txbf_internal(phy, BF_FBRPT_DBG_INFO_READ, wlan_idx);
-+}
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_bf_fbk_rpt, NULL,
-+			 mt7996_bf_fbk_rpt_set, "%lld\n");
-+
-+static int
-+mt7996_bf_pfmu_tag_read_set(void *data, u64 wlan_idx)
-+{
-+	struct mt7996_phy *phy = data;
-+
-+	return mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, wlan_idx);
-+}
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_bf_pfmu_tag_read, NULL,
-+			 mt7996_bf_pfmu_tag_read_set, "%lld\n");
-+
-+static int
-+mt7996_muru_fixed_rate_set(void *data, u64 val)
-+{
-+	struct mt7996_dev *dev = data;
-+
-+	return mt7996_mcu_set_muru_fixed_rate_enable(dev, UNI_CMD_MURU_FIXED_RATE_CTRL,
-+						     val);
-+}
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_muru_fixed_rate_enable, NULL,
-+			 mt7996_muru_fixed_rate_set, "%lld\n");
-+
-+static ssize_t
-+mt7996_muru_fixed_rate_parameter_set(struct file *file,
-+				     const char __user *user_buf,
-+				     size_t count, loff_t *ppos)
-+{
-+	struct mt7996_dev *dev = file->private_data;
-+	char buf[40];
-+	int ret;
-+
-+	if (count >= sizeof(buf))
-+		return -EINVAL;
-+
-+	if (copy_from_user(buf, user_buf, count))
-+		return -EFAULT;
-+
-+	if (count && buf[count - 1] == '\n')
-+		buf[count - 1] = '\0';
-+	else
-+		buf[count] = '\0';
-+
-+
-+	ret = mt7996_mcu_set_muru_fixed_rate_parameter(dev, UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
-+						       buf);
-+
-+	if (ret) return -EFAULT;
-+
-+	return count;
-+}
-+
-+static const struct file_operations fops_muru_fixed_group_rate = {
-+	.write = mt7996_muru_fixed_rate_parameter_set,
-+	.read = NULL,
-+	.open = simple_open,
-+	.llseek = default_llseek,
-+};
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- 	struct mt7996_dev *dev = phy->dev;
-@@ -3039,6 +3150,15 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- 	debugfs_create_file("sr_stats", 0400, dir, phy, &mt7996_sr_stats_fops);
- 	debugfs_create_file("sr_scene_cond", 0400, dir, phy, &mt7996_sr_scene_cond_fops);
- 
-+	debugfs_create_file("muru_fixed_rate_enable", 0600, dir, dev,
-+			    &fops_muru_fixed_rate_enable);
-+	debugfs_create_file("muru_fixed_group_rate", 0600, dir, dev,
-+			    &fops_muru_fixed_group_rate);
-+	debugfs_create_file("bf_txsnd_info", 0600, dir, phy, &fops_bf_txsnd_info);
-+	debugfs_create_file("bf_starec_read", 0600, dir, phy, &fops_starec_bf_read);
-+	debugfs_create_file("bf_fbk_rpt", 0600, dir, phy, &fops_bf_fbk_rpt);
-+	debugfs_create_file("pfmu_tag_read", 0600, dir, phy, &fops_bf_pfmu_tag_read);
-+
- 	return 0;
- }
- 
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index ea4e5bf..6b2cdad 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -280,4 +280,628 @@ int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev)
- 	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(CHIP_CONFIG), &req,
- 				 sizeof(req), true);
- }
-+
-+static struct tlv *
-+__mt7996_mcu_add_uni_tlv(struct sk_buff *skb, u16 tag, u16 len)
-+{
-+	struct tlv *ptlv, tlv = {
-+		.tag = cpu_to_le16(tag),
-+		.len = cpu_to_le16(len),
-+	};
-+
-+	ptlv = skb_put(skb, len);
-+	memcpy(ptlv, &tlv, sizeof(tlv));
-+
-+	return ptlv;
-+}
-+
-+int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+#define MT7996_MTK_BF_MAX_SIZE	sizeof(struct bf_starec_read)
-+	struct uni_header hdr;
-+	struct sk_buff *skb;
-+	struct tlv *tlv;
-+	int len = sizeof(hdr) + MT7996_MTK_BF_MAX_SIZE;
-+
-+	memset(&hdr, 0, sizeof(hdr));
-+
-+	skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len);
-+	if (!skb)
-+		return -ENOMEM;
-+
-+	skb_put_data(skb, &hdr, sizeof(hdr));
-+
-+	switch (action) {
-+	case BF_PFMU_TAG_READ: {
-+		struct bf_pfmu_tag *req;
-+
-+		tlv = __mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req));
-+		req = (struct bf_pfmu_tag *)tlv;
-+#define BFER 1
-+		req->pfmu_id = idx;
-+		req->bfer = BFER;
-+		req->band_idx = phy->mt76->band_idx;
-+		break;
-+	}
-+	case BF_STA_REC_READ: {
-+		struct bf_starec_read *req;
-+
-+		tlv = __mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req));
-+		req = (struct bf_starec_read *)tlv;
-+		req->wlan_idx = idx;
-+		break;
-+	}
-+	case BF_FBRPT_DBG_INFO_READ: {
-+		struct bf_fbk_rpt_info *req;
-+
-+		if (idx != 0) {
-+			dev_info(dev->mt76.dev, "Invalid input");
-+			return 0;
-+		}
-+
-+		tlv = __mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req));
-+		req = (struct bf_fbk_rpt_info *)tlv;
-+		req->action = idx;
-+		req->band_idx = phy->mt76->band_idx;
-+		break;
-+	}
-+	default:
-+		return -EINVAL;
-+	}
-+
-+	return mt76_mcu_skb_send_msg(&phy->dev->mt76, skb, MCU_WM_UNI_CMD(BF), false);
-+}
-+
-+int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para)
-+{
-+	char *buf = (char *)para;
-+	__le16 input[5] = {0};
-+	u8 recv_arg = 0;
-+	struct bf_txsnd_info *req;
-+	struct uni_header hdr;
-+	struct sk_buff *skb;
-+	struct tlv *tlv;
-+	int len = sizeof(hdr) + MT7996_MTK_BF_MAX_SIZE;
-+
-+	memset(&hdr, 0, sizeof(hdr));
-+
-+	skb = mt76_mcu_msg_alloc(&phy->dev->mt76, NULL, len);
-+	if (!skb)
-+		return -ENOMEM;
-+
-+	skb_put_data(skb, &hdr, sizeof(hdr));
-+
-+	recv_arg = sscanf(buf, "%hx:%hx:%hx:%hx:%hx", &input[0], &input[1], &input[2],
-+						      &input[3], &input[4]);
-+
-+	if (!recv_arg)
-+		return -EINVAL;
-+
-+	tlv = __mt7996_mcu_add_uni_tlv(skb, BF_TXSND_INFO, sizeof(*req));
-+	req = (struct bf_txsnd_info *)tlv;
-+	req->action = input[0];
-+
-+	switch (req->action) {
-+	case BF_SND_READ_INFO: {
-+		req->read_clr = input[1];
-+		break;
-+	}
-+	case BF_SND_CFG_OPT: {
-+		req->vht_opt = input[1];
-+		req->he_opt = input[2];
-+		req->glo_opt = input[3];
-+		break;
-+	}
-+	case BF_SND_CFG_INTV: {
-+		req->wlan_idx = input[1];
-+		req->snd_intv = input[2];
-+		break;
-+	}
-+	case BF_SND_STA_STOP: {
-+		req->wlan_idx = input[1];
-+		req->snd_stop = input[2];
-+		break;
-+	}
-+	case BF_SND_CFG_MAX_STA: {
-+		req->max_snd_stas = input[1];
-+		break;
-+	}
-+	case BF_SND_CFG_BFRP: {
-+		req->man = input[1];
-+		req->tx_time = input[2];
-+		req->mcs = input[3];
-+		req->ldpc = input[4];
-+		break;
-+	}
-+	case BF_SND_CFG_INF: {
-+		req->inf = input[1];
-+		break;
-+	}
-+	case BF_SND_CFG_TXOP_SND: {
-+		req->man = input[1];
-+		req->ac_queue = input[2];
-+		req->sxn_protect = input[3];
-+		req->direct_fbk = input[4];
-+		break;
-+	}
-+	default:
-+		return -EINVAL;
-+	}
-+
-+	return mt76_mcu_skb_send_msg(&phy->dev->mt76, skb, MCU_WM_UNI_CMD(BF), false);
-+}
-+
-+void
-+mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb)
-+{
-+#define HE_MODE 3
-+	struct mt7996_mcu_bf_basic_event *event;
-+
-+	event = (struct mt7996_mcu_bf_basic_event *)skb->data;
-+
-+	dev_info(dev->mt76.dev, " bf_event tag = %d\n", event->tag);
-+
-+	switch (event->tag) {
-+	case UNI_EVENT_BF_PFMU_TAG: {
-+
-+		struct mt7996_pfmu_tag_event *tag;
-+		u32 *raw_t1, *raw_t2;
-+
-+		tag = (struct mt7996_pfmu_tag_event *) skb->data;
-+
-+		raw_t1 = (u32 *)&tag->t1;
-+		raw_t2 = (u32 *)&tag->t2;
-+
-+		dev_info(dev->mt76.dev, "=================== TXBf Profile Tag1 Info ==================\n");
-+		dev_info(dev->mt76.dev,
-+			 "DW0 = 0x%08x, DW1 = 0x%08x, DW2 = 0x%08x\n",
-+			 raw_t1[0], raw_t1[1], raw_t1[2]);
-+		dev_info(dev->mt76.dev,
-+			 "DW4 = 0x%08x, DW5 = 0x%08x, DW6 = 0x%08x\n\n",
-+			 raw_t1[3], raw_t1[4], raw_t1[5]);
-+		dev_info(dev->mt76.dev, "PFMU ID = %d              Invalid status = %d\n",
-+			 tag->t1.pfmu_idx, tag->t1.invalid_prof);
-+		dev_info(dev->mt76.dev, "iBf/eBf = %d\n\n", tag->t1.ebf);
-+		dev_info(dev->mt76.dev, "DBW   = %d\n", tag->t1.data_bw);
-+		dev_info(dev->mt76.dev, "SU/MU = %d\n", tag->t1.is_mu);
-+		dev_info(dev->mt76.dev,
-+			 "nrow = %d, ncol = %d, ng = %d, LM = %d, CodeBook = %d MobCalEn = %d\n",
-+			 tag->t1.nr, tag->t1.nc, tag->t1.ngroup, tag->t1.lm, tag->t1.codebook,
-+			 tag->t1.mob_cal_en);
-+
-+		if (tag->t1.lm <= HE_MODE) {
-+			dev_info(dev->mt76.dev, "RU start = %d, RU end = %d\n",
-+				 tag->t1.field.ru_start_id, tag->t1.field.ru_end_id);
-+		} else {
-+			dev_info(dev->mt76.dev, "PartialBW = %d\n",
-+				 tag->t1.bw_info.partial_bw_info);
-+		}
-+
-+		dev_info(dev->mt76.dev, "Mem Col1 = %d, Mem Row1 = %d, Mem Col2 = %d, Mem Row2 = %d\n",
-+			 tag->t1.col_id1, tag->t1.row_id1, tag->t1.col_id2, tag->t1.row_id2);
-+		dev_info(dev->mt76.dev, "Mem Col3 = %d, Mem Row3 = %d, Mem Col4 = %d, Mem Row4 = %d\n\n",
-+			 tag->t1.col_id3, tag->t1.row_id3, tag->t1.col_id4, tag->t1.row_id4);
-+		dev_info(dev->mt76.dev,
-+			 "STS0_SNR = 0x%02x, STS1_SNR = 0x%02x, STS2_SNR = 0x%02x, STS3_SNR = 0x%02x\n",
-+			 tag->t1.snr_sts0, tag->t1.snr_sts1, tag->t1.snr_sts2, tag->t1.snr_sts3);
-+		dev_info(dev->mt76.dev,
-+			 "STS4_SNR = 0x%02x, STS5_SNR = 0x%02x, STS6_SNR = 0x%02x, STS7_SNR = 0x%02x\n",
-+			 tag->t1.snr_sts4, tag->t1.snr_sts5, tag->t1.snr_sts6, tag->t1.snr_sts7);
-+		dev_info(dev->mt76.dev, "=============================================================\n");
-+
-+		dev_info(dev->mt76.dev, "=================== TXBf Profile Tag2 Info ==================\n");
-+		dev_info(dev->mt76.dev,
-+			 "DW0 = 0x%08x, DW1 = 0x%08x, DW2 = 0x%08x\n",
-+			 raw_t2[0], raw_t2[1], raw_t2[2]);
-+		dev_info(dev->mt76.dev,
-+			 "DW3 = 0x%08x, DW4 = 0x%08x, DW5 = 0x%08x\n\n",
-+			 raw_t2[3], raw_t2[4], raw_t2[5]);
-+		dev_info(dev->mt76.dev, "Smart antenna ID = 0x%x,  SE index = %d\n",
-+			 tag->t2.smart_ant, tag->t2.se_idx);
-+		dev_info(dev->mt76.dev, "Timeout = 0x%x\n", tag->t2.ibf_timeout);
-+		dev_info(dev->mt76.dev, "Desired BW = %d, Desired Ncol = %d, Desired Nrow = %d\n",
-+			 tag->t2.ibf_data_bw, tag->t2.ibf_nc, tag->t2.ibf_nr);
-+		dev_info(dev->mt76.dev, "Desired RU Allocation = %d\n", tag->t2.ibf_ru);
-+		dev_info(dev->mt76.dev, "Mobility DeltaT = %d, Mobility LQ = %d\n",
-+			 tag->t2.mob_delta_t, tag->t2.mob_lq_result);
-+		dev_info(dev->mt76.dev, "=============================================================\n");
-+		break;
-+	}
-+	case UNI_EVENT_BF_STAREC: {
-+
-+		struct mt7996_mcu_bf_starec_read *r;
-+
-+		r = (struct mt7996_mcu_bf_starec_read *)skb->data;
-+		dev_info(dev->mt76.dev, "=================== BF StaRec ===================\n"
-+					"rStaRecBf.u2PfmuId      = %d\n"
-+					"rStaRecBf.fgSU_MU       = %d\n"
-+					"rStaRecBf.u1TxBfCap     = %d\n"
-+					"rStaRecBf.ucSoundingPhy = %d\n"
-+					"rStaRecBf.ucNdpaRate    = %d\n"
-+					"rStaRecBf.ucNdpRate     = %d\n"
-+					"rStaRecBf.ucReptPollRate= %d\n"
-+					"rStaRecBf.ucTxMode      = %d\n"
-+					"rStaRecBf.ucNc          = %d\n"
-+					"rStaRecBf.ucNr          = %d\n"
-+					"rStaRecBf.ucCBW         = %d\n"
-+					"rStaRecBf.ucMemRequire20M = %d\n"
-+					"rStaRecBf.ucMemRow0     = %d\n"
-+					"rStaRecBf.ucMemCol0     = %d\n"
-+					"rStaRecBf.ucMemRow1     = %d\n"
-+					"rStaRecBf.ucMemCol1     = %d\n"
-+					"rStaRecBf.ucMemRow2     = %d\n"
-+					"rStaRecBf.ucMemCol2     = %d\n"
-+					"rStaRecBf.ucMemRow3     = %d\n"
-+					"rStaRecBf.ucMemCol3     = %d\n",
-+					r->pfmu_id,
-+					r->is_su_mu,
-+					r->txbf_cap,
-+					r->sounding_phy,
-+					r->ndpa_rate,
-+					r->ndp_rate,
-+					r->rpt_poll_rate,
-+					r->tx_mode,
-+					r->nc,
-+					r->nr,
-+					r->bw,
-+					r->mem_require_20m,
-+					r->mem_row0,
-+					r->mem_col0,
-+					r->mem_row1,
-+					r->mem_col1,
-+					r->mem_row2,
-+					r->mem_col2,
-+					r->mem_row3,
-+					r->mem_col3);
-+
-+		dev_info(dev->mt76.dev, "rStaRecBf.u2SmartAnt    = 0x%x\n"
-+					"rStaRecBf.ucSEIdx       = %d\n"
-+					"rStaRecBf.uciBfTimeOut  = 0x%x\n"
-+					"rStaRecBf.uciBfDBW      = %d\n"
-+					"rStaRecBf.uciBfNcol     = %d\n"
-+					"rStaRecBf.uciBfNrow     = %d\n"
-+					"rStaRecBf.nr_bw160      = %d\n"
-+					"rStaRecBf.nc_bw160 	  = %d\n"
-+					"rStaRecBf.ru_start_idx  = %d\n"
-+					"rStaRecBf.ru_end_idx 	  = %d\n"
-+					"rStaRecBf.trigger_su 	  = %d\n"
-+					"rStaRecBf.trigger_mu 	  = %d\n"
-+					"rStaRecBf.ng16_su 	  = %d\n"
-+					"rStaRecBf.ng16_mu 	  = %d\n"
-+					"rStaRecBf.codebook42_su = %d\n"
-+					"rStaRecBf.codebook75_mu = %d\n"
-+					"rStaRecBf.he_ltf 	      = %d\n"
-+					"======================================\n",
-+					r->smart_ant,
-+					r->se_idx,
-+					r->bf_timeout,
-+					r->bf_dbw,
-+					r->bf_ncol,
-+					r->bf_nrow,
-+					r->nr_lt_bw80,
-+					r->nc_lt_bw80,
-+					r->ru_start_idx,
-+					r->ru_end_idx,
-+					r->trigger_su,
-+					r->trigger_mu,
-+					r->ng16_su,
-+					r->ng16_mu,
-+					r->codebook42_su,
-+					r->codebook75_mu,
-+					r->he_ltf);
-+		break;
-+	}
-+	case UNI_EVENT_BF_FBK_INFO: {
-+		struct mt7996_mcu_txbf_fbk_info *info;
-+		__le32 total, i;
-+
-+		info = (struct mt7996_mcu_txbf_fbk_info *)skb->data;
-+
-+		total = info->u4PFMUWRDoneCnt + info->u4PFMUWRFailCnt;
-+		total += info->u4PFMUWRTimeoutFreeCnt + info->u4FbRptPktDropCnt;
-+
-+		dev_info(dev->mt76.dev, "\n");
-+		dev_info(dev->mt76.dev, "\x1b[32m =================================\x1b[m\n");
-+		dev_info(dev->mt76.dev, "\x1b[32m PFMUWRDoneCnt              = %u\x1b[m\n",
-+			info->u4PFMUWRDoneCnt);
-+		dev_info(dev->mt76.dev, "\x1b[32m PFMUWRFailCnt              = %u\x1b[m\n",
-+			info->u4PFMUWRFailCnt);
-+		dev_info(dev->mt76.dev, "\x1b[32m PFMUWRTimeOutCnt           = %u\x1b[m\n",
-+			info->u4PFMUWRTimeOutCnt);
-+		dev_info(dev->mt76.dev, "\x1b[32m PFMUWRTimeoutFreeCnt       = %u\x1b[m\n",
-+			info->u4PFMUWRTimeoutFreeCnt);
-+		dev_info(dev->mt76.dev, "\x1b[32m FbRptPktDropCnt            = %u\x1b[m\n",
-+			info->u4FbRptPktDropCnt);
-+		dev_info(dev->mt76.dev, "\x1b[32m TotalFbRptPkt              = %u\x1b[m\n", total);
-+		dev_info(dev->mt76.dev, "\x1b[32m PollPFMUIntrStatTimeOut    = %u(micro-sec)\x1b[m\n",
-+			info->u4PollPFMUIntrStatTimeOut);
-+		dev_info(dev->mt76.dev, "\x1b[32m FbRptDeQInterval           = %u(milli-sec)\x1b[m\n",
-+			info->u4DeQInterval);
-+		dev_info(dev->mt76.dev, "\x1b[32m PktCntInFbRptTimeOutQ      = %u\x1b[m\n",
-+			info->u4RptPktTimeOutListNum);
-+		dev_info(dev->mt76.dev, "\x1b[32m PktCntInFbRptQ             = %u\x1b[m\n",
-+			info->u4RptPktListNum);
-+
-+		// [ToDo] Check if it is valid entry
-+		for (i = 0; ((i < 5) && (i < CFG_BF_STA_REC_NUM)); i++) {
-+
-+			// [ToDo] AID needs to be refined
-+			dev_info(dev->mt76.dev,"\x1b[32m AID%u  RxFbRptCnt           = %u\x1b[m\n"
-+				, i, info->au4RxPerStaFbRptCnt[i]);
-+		}
-+
-+		break;
-+	}
-+	case UNI_EVENT_BF_TXSND_INFO: {
-+		struct mt7996_mcu_tx_snd_info *info;
-+		struct uni_event_bf_txsnd_sta_info *snd_sta_info;
-+		int Idx;
-+		int max_wtbl_size = mt7996_wtbl_size(dev);
-+
-+		info = (struct mt7996_mcu_tx_snd_info *)skb->data;
-+		dev_info(dev->mt76.dev, "=================== Global Setting ===================\n");
-+
-+		dev_info(dev->mt76.dev, "VhtOpt = 0x%02X, HeOpt = 0x%02X, GloOpt = 0x%02X\n",
-+			info->vht_opt, info->he_opt, info->glo_opt);
-+
-+		for (Idx = 0; Idx < BF_SND_CTRL_STA_DWORD_CNT; Idx++) {
-+			dev_info(dev->mt76.dev, "SuSta[%d] = 0x%08X,", Idx,
-+				 info->snd_rec_su_sta[Idx]);
-+			if ((Idx & 0x03) == 0x03)
-+				dev_info(dev->mt76.dev, "\n");
-+		}
-+
-+		if ((Idx & 0x03) != 0x03)
-+			dev_info(dev->mt76.dev, "\n");
-+
-+
-+		for (Idx = 0; Idx < BF_SND_CTRL_STA_DWORD_CNT; Idx++) {
-+			dev_info(dev->mt76.dev, "VhtMuSta[%d] = 0x%08X,", Idx, info->snd_rec_vht_mu_sta[Idx]);
-+			if ((Idx & 0x03) == 0x03)
-+				dev_info(dev->mt76.dev, "\n");
-+		}
-+
-+		if ((Idx & 0x03) != 0x03)
-+			dev_info(dev->mt76.dev, "\n");
-+
-+		for (Idx = 0; Idx < BF_SND_CTRL_STA_DWORD_CNT; Idx++) {
-+			dev_info(dev->mt76.dev, "HeTBSta[%d] = 0x%08X,", Idx, info->snd_rec_he_tb_sta[Idx]);
-+			if ((Idx & 0x03) == 0x03)
-+				dev_info(dev->mt76.dev, "\n");
-+		}
-+
-+		if ((Idx & 0x03) != 0x03)
-+			dev_info(dev->mt76.dev, "\n");
-+
-+		for (Idx = 0; Idx < BF_SND_CTRL_STA_DWORD_CNT; Idx++) {
-+			dev_info(dev->mt76.dev, "EhtTBSta[%d] = 0x%08X,", Idx, info->snd_rec_eht_tb_sta[Idx]);
-+			if ((Idx & 0x03) == 0x03)
-+				dev_info(dev->mt76.dev, "\n");
-+		}
-+
-+		if ((Idx & 0x03) != 0x03)
-+			dev_info(dev->mt76.dev, "\n");
-+
-+		for (Idx = 0; Idx < CFG_WIFI_RAM_BAND_NUM; Idx++) {
-+			dev_info(dev->mt76.dev, "Band%u:\n", Idx);
-+			dev_info(dev->mt76.dev, "	 Wlan Idx For VHT MC Sounding = %u\n", info->wlan_idx_for_mc_snd[Idx]);
-+			dev_info(dev->mt76.dev, "	 Wlan Idx For HE TB Sounding = %u\n", info->wlan_idx_for_he_tb_snd[Idx]);
-+			dev_info(dev->mt76.dev, "	 Wlan Idx For EHT TB Sounding = %u\n", info->wlan_idx_for_eht_tb_snd[Idx]);
-+		}
-+
-+		dev_info(dev->mt76.dev, "ULLen = %d, ULMcs = %d, ULLDCP = %d\n",
-+			info->ul_length, info->mcs, info->ldpc);
-+
-+		dev_info(dev->mt76.dev, "=================== STA Info ===================\n");
-+
-+		for (Idx = 1; (Idx < 5 && (Idx < CFG_BF_STA_REC_NUM)); Idx++) {
-+			snd_sta_info = &info->snd_sta_info[Idx];
-+			dev_info(dev->mt76.dev, "Idx%2u Interval = %d, interval counter = %d, TxCnt = %d, StopReason = 0x%02X\n",
-+				Idx,
-+				snd_sta_info->snd_intv,
-+				snd_sta_info->snd_intv_cnt,
-+				snd_sta_info->snd_tx_cnt,
-+				snd_sta_info->snd_stop_reason);
-+		}
-+
-+		dev_info(dev->mt76.dev, "=================== STA Info Connected ===================\n");
-+		// [ToDo] How to iterate and get AID info of station
-+		// Check UniEventBFCtrlTxSndHandle() on Logan
-+
-+		//hardcode max_wtbl_size as 5
-+		max_wtbl_size = 5;
-+		for (Idx = 1; ((Idx < max_wtbl_size) && (Idx < CFG_BF_STA_REC_NUM)); Idx++) {
-+
-+			// [ToDo] We do not show AID info here
-+			snd_sta_info = &info->snd_sta_info[Idx];
-+			dev_info(dev->mt76.dev, " Interval = %d (%u ms), interval counter = %d (%u ms), TxCnt = %d, StopReason = 0x%02X\n",
-+				snd_sta_info->snd_intv,
-+				snd_sta_info->snd_intv * 10,
-+				snd_sta_info->snd_intv_cnt,
-+				snd_sta_info->snd_intv_cnt * 10,
-+				snd_sta_info->snd_tx_cnt,
-+				snd_sta_info->snd_stop_reason);
-+		}
-+
-+		dev_info(dev->mt76.dev, "======================================\n");
-+
-+		break;
-+	}
-+	default:
-+		dev_info(dev->mt76.dev, "%s: unknown bf event tag %d\n",
-+			 __func__, event->tag);
-+	}
-+
-+}
-+
-+
-+int mt7996_mcu_set_muru_fixed_rate_enable(struct mt7996_dev *dev, u8 action, int val)
-+{
-+	struct {
-+		u8 _rsv[4];
-+
-+		__le16 tag;
-+		__le16 len;
-+
-+		__le16 value;
-+		__le16 rsv;
-+	} __packed data = {
-+		.tag = cpu_to_le16(action),
-+		.len = cpu_to_le16(sizeof(data) - 4),
-+		.value = cpu_to_le16(!!val),
-+	};
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &data, sizeof(data),
-+				 false);
-+}
-+
-+int mt7996_mcu_set_muru_fixed_rate_parameter(struct mt7996_dev *dev, u8 action, void *para)
-+{
-+	char *buf = (char *)para;
-+	u8 num_user = 0, recv_arg = 0, max_mcs = 0, usr_mcs[4] = {0};
-+	__le16 bw;
-+	int i;
-+	struct {
-+		u8 _rsv[4];
-+
-+		__le16 tag;
-+		__le16 len;
-+
-+		u8 cmd_version;
-+		u8 cmd_revision;
-+		__le16 rsv;
-+
-+		struct uni_muru_mum_set_group_tbl_entry entry;
-+	} __packed data = {
-+		.tag = cpu_to_le16(action),
-+		.len = cpu_to_le16(sizeof(data) - 4),
-+	};
-+
-+#define __RUALLOC_TYPE_CHECK_HE(BW) ((BW == RUALLOC_BW20) || (BW == RUALLOC_BW40) || (BW == RUALLOC_BW80) || (BW == RUALLOC_BW160))
-+#define __RUALLOC_TYPE_CHECK_EHT(BW) (__RUALLOC_TYPE_CHECK_HE(BW) || (BW == RUALLOC_BW320))
-+	/* [Num of user] - 1~4
-+	 * [RUAlloc] - BW320: 395, BW160: 137, BW80: 134, BW40: 130, BW20: 122
-+	 * [LTF/GI] - For VHT, short GI: 0, Long GI: 1; 	 *
-+	 * For HE/EHT, 4xLTF+3.2us: 0, 4xLTF+0.8us: 1, 2xLTF+0.8us:2
-+	 * [Phy/FullBW] - VHT: 0 / HEFullBw: 1 / HEPartialBw: 2 / EHTFullBW: 3, EHTPartialBW: 4
-+	 * [DL/UL] DL: 0, UL: 1, DL_UL: 2
-+	 * [Wcid User0] - WCID 0
-+	 * [MCS of WCID0] - For HE/VHT, 0-11: 1ss MCS0-MCS11, 12-23: 2SS MCS0-MCS11
-+	 * For EHT, 0-13: 1ss MCS0-MCS13, 14-27: 2SS MCS0-MCS13
-+	 * [WCID 1]
-+	 * [MCS of WCID1]
-+	 * [WCID 2]
-+	 * [MCS of WCID2]
-+	 * [WCID 3]
-+	 * [MCS of WCID3]
-+	 */
-+
-+	recv_arg = sscanf(buf, "%hhu %hu %hhu %hhu %hhu %hu %hhu %hu %hhu %hu %hhu %hu %hhu",
-+			  &num_user, &bw, &data.entry.gi, &data.entry.capa, &data.entry.dl_ul,
-+			  &data.entry.wlan_idx0, &usr_mcs[0],
-+			  &data.entry.wlan_idx1, &usr_mcs[1],
-+			  &data.entry.wlan_idx2, &usr_mcs[2],
-+			  &data.entry.wlan_idx3, &usr_mcs[3]);
-+
-+	if (recv_arg != (5 + (2 * num_user))) {
-+		dev_err(dev->mt76.dev, "The number of argument is invalid\n");
-+		goto error;
-+	}
-+
-+	if (num_user > 0 && num_user < 5)
-+		data.entry.num_user = num_user - 1;
-+	else {
-+		dev_err(dev->mt76.dev, "The number of user count is invalid\n");
-+		goto error;
-+	}
-+
-+	/**
-+	 * Older chip shall be set as HE. Refer to getHWSupportByChip() in Logan
-+	 * driver to know the value for differnt chips
-+	 */
-+	data.cmd_version = UNI_CMD_MURU_VER_EHT;
-+
-+	if (data.cmd_version == UNI_CMD_MURU_VER_EHT)
-+		max_mcs = UNI_MAX_MCS_SUPPORT_EHT;
-+	else
-+		max_mcs = UNI_MAX_MCS_SUPPORT_HE;
-+
-+
-+	// Parameter Check
-+	if (data.cmd_version != UNI_CMD_MURU_VER_EHT) {
-+		if ((data.entry.capa > MAX_MODBF_HE) || (bw == RUALLOC_BW320))
-+			goto error;
-+	} else {
-+		if ((data.entry.capa <= MAX_MODBF_HE) && (bw == RUALLOC_BW320))
-+			goto error;
-+	}
-+
-+	if (data.entry.capa <= MAX_MODBF_HE)
-+		max_mcs = UNI_MAX_MCS_SUPPORT_HE;
-+
-+	if (__RUALLOC_TYPE_CHECK_EHT(bw)) {
-+		data.entry.ru_alloc = (u8)(bw & 0xFF);
-+		if (bw == RUALLOC_BW320)
-+			data.entry.ru_alloc_ext = (u8)(bw >> 8);
-+	} else {
-+		dev_err(dev->mt76.dev, "RU_ALLOC argument is invalid\n");
-+		goto error;
-+	}
-+
-+	if ((data.entry.gi > 2) ||
-+	    ((data.entry.gi > 1) && (data.entry.capa == MAX_MODBF_VHT))) {
-+		dev_err(dev->mt76.dev, "GI argument is invalid\n");
-+		goto error;
-+	}
-+
-+	if (data.entry.dl_ul > 2) {
-+		dev_err(dev->mt76.dev, "DL_UL argument is invalid\n");
-+		goto error;
-+	}
-+
-+#define __mcs_handler(_n)							\
-+	do {									\
-+		if (usr_mcs[_n] > max_mcs) {					\
-+			usr_mcs[_n] -= (max_mcs + 1);				\
-+			data.entry.nss##_n = 1;					\
-+			if (usr_mcs[_n] > max_mcs)				\
-+				usr_mcs[_n] = max_mcs;				\
-+		}								\
-+		if ((data.entry.dl_ul & 0x1) == 0)				\
-+			data.entry.dl_mcs_user##_n = usr_mcs[_n];		\
-+		if ((data.entry.dl_ul & 0x3) > 0)				\
-+			data.entry.ul_mcs_user##_n = usr_mcs[_n];		\
-+	}									\
-+	while (0)
-+
-+	for (i=0; i<= data.entry.num_user; i++) {
-+		switch (i) {
-+			case 0:
-+				__mcs_handler(0);
-+				break;
-+			case 1:
-+				__mcs_handler(1);
-+				break;
-+			case 2:
-+				__mcs_handler(2);
-+				break;
-+			case 3:
-+				__mcs_handler(3);
-+				break;
-+			default:
-+				break;
-+		}
-+	}
-+#undef __mcs_handler
-+
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &data,
-+				 sizeof(data), false);
-+
-+error:
-+	dev_err(dev->mt76.dev, "Command failed!\n");
-+	return -EINVAL;
-+}
-+
- #endif
-diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
-index 098e63a..27d6a05 100644
---- a/mt7996/mtk_mcu.h
-+++ b/mt7996/mtk_mcu.h
-@@ -119,6 +119,348 @@ enum {
- 	EDCCA_FCC = 1,
- 	EDCCA_ETSI = 2,
- 	EDCCA_JAPAN = 3
-+
-+struct bf_pfmu_tag {
-+	__le16 tag;
-+	__le16 len;
-+
-+	u8 pfmu_id;
-+	bool bfer;
-+	u8 band_idx;
-+	u8 __rsv[5];
-+	u8 buf[56];
-+} __packed;
-+
-+struct bf_starec_read {
-+	__le16 tag;
-+	__le16 len;
-+
-+	__le16 wlan_idx;
-+	u8 __rsv[2];
-+} __packed;
-+
-+struct bf_fbk_rpt_info {
-+	__le16 tag;
-+	__le16 len;
-+
-+	__le16 wlan_idx; // Only need for dynamic_pfmu_update 0x4
-+	u8 action;
-+	u8 band_idx;
-+	u8 __rsv[4];
-+
-+} __packed;
-+
-+struct bf_txsnd_info {
-+	__le16 tag;
-+	__le16 len;
-+
-+	u8 action;
-+	u8 read_clr;
-+	u8 vht_opt;
-+	u8 he_opt;
-+	__le16 wlan_idx;
-+	u8 glo_opt;
-+	u8 snd_intv;
-+	u8 snd_stop;
-+	u8 max_snd_stas;
-+	u8 tx_time;
-+	u8 mcs;
-+	u8 ldpc;
-+	u8 inf;
-+	u8 man;
-+	u8 ac_queue;
-+	u8 sxn_protect;
-+	u8 direct_fbk;
-+	u8 __rsv[2];
-+} __packed;
-+
-+struct mt7996_mcu_bf_basic_event {
-+	struct mt7996_mcu_rxd rxd;
-+
-+	u8 __rsv1[4];
-+
-+	__le16 tag;
-+	__le16 len;
-+};
-+
-+struct mt7996_mcu_bf_starec_read {
-+
-+	struct mt7996_mcu_bf_basic_event event;
-+
-+	__le16 pfmu_id;
-+	bool is_su_mu;
-+	u8 txbf_cap;
-+	u8 sounding_phy;
-+	u8 ndpa_rate;
-+	u8 ndp_rate;
-+	u8 rpt_poll_rate;
-+	u8 tx_mode;
-+	u8 nc;
-+	u8 nr;
-+	u8 bw;
-+	u8 total_mem_require;
-+	u8 mem_require_20m;
-+	u8 mem_row0;
-+	u8 mem_col0:6;
-+	u8 mem_row0_msb:2;
-+	u8 mem_row1;
-+	u8 mem_col1:6;
-+	u8 mem_row1_msb:2;
-+	u8 mem_row2;
-+	u8 mem_col2:6;
-+	u8 mem_row2_msb:2;
-+	u8 mem_row3;
-+	u8 mem_col3:6;
-+	u8 mem_row3_msb:2;
-+
-+	__le16 smart_ant;
-+	u8 se_idx;
-+	u8 auto_sounding_ctrl;
-+
-+	u8 bf_timeout;
-+	u8 bf_dbw;
-+	u8 bf_ncol;
-+	u8 bf_nrow;
-+
-+	u8 nr_lt_bw80;
-+	u8 nc_lt_bw80;
-+	u8 ru_start_idx;
-+	u8 ru_end_idx;
-+
-+	bool trigger_su;
-+	bool trigger_mu;
-+
-+	bool ng16_su;
-+	bool ng16_mu;
-+
-+	bool codebook42_su;
-+	bool codebook75_mu;
-+
-+	u8 he_ltf;
-+	u8 rsv[3];
-+};
-+
-+#define TXBF_PFMU_ID_NUM_MAX 48
-+
-+#define TXBF_PFMU_ID_NUM_MAX_TBTC_BAND0 TXBF_PFMU_ID_NUM_MAX
-+#define TXBF_PFMU_ID_NUM_MAX_TBTC_BAND1 TXBF_PFMU_ID_NUM_MAX
-+#define TXBF_PFMU_ID_NUM_MAX_TBTC_BAND2 TXBF_PFMU_ID_NUM_MAX
-+
-+/* CFG_BF_STA_REC shall be varied based on BAND Num */
-+#define CFG_BF_STA_REC_NUM (TXBF_PFMU_ID_NUM_MAX_TBTC_BAND0 + TXBF_PFMU_ID_NUM_MAX_TBTC_BAND1 + TXBF_PFMU_ID_NUM_MAX_TBTC_BAND2)
-+
-+#define BF_SND_CTRL_STA_DWORD_CNT   ((CFG_BF_STA_REC_NUM + 0x1F) >> 5)
-+
-+#ifndef ALIGN_4
-+	#define ALIGN_4(_value)             (((_value) + 3) & ~3u)
-+#endif /* ALIGN_4 */
-+
-+#define CFG_WIFI_RAM_BAND_NUM 3
-+
-+struct uni_event_bf_txsnd_sta_info {
-+	u8 snd_intv;       /* Sounding interval upper bound, unit:15ms */
-+	u8 snd_intv_cnt;   /* Sounding interval counter */
-+	u8 snd_tx_cnt;     /* Tx sounding count for debug */
-+	u8 snd_stop_reason;  /* Bitwise reason to put in Stop Queue */
-+};
-+
-+struct mt7996_mcu_tx_snd_info {
-+
-+	struct mt7996_mcu_bf_basic_event event;
-+
-+	u8 vht_opt;
-+	u8 he_opt;
-+	u8 glo_opt;
-+	u8 __rsv;
-+	__le32 snd_rec_su_sta[BF_SND_CTRL_STA_DWORD_CNT];
-+	__le32 snd_rec_vht_mu_sta[BF_SND_CTRL_STA_DWORD_CNT];
-+	__le32 snd_rec_he_tb_sta[BF_SND_CTRL_STA_DWORD_CNT];
-+	__le32 snd_rec_eht_tb_sta[BF_SND_CTRL_STA_DWORD_CNT];
-+	__le16 wlan_idx_for_mc_snd[ALIGN_4(CFG_WIFI_RAM_BAND_NUM)];
-+	__le16 wlan_idx_for_he_tb_snd[ALIGN_4(CFG_WIFI_RAM_BAND_NUM)];
-+	__le16 wlan_idx_for_eht_tb_snd[ALIGN_4(CFG_WIFI_RAM_BAND_NUM)];
-+	__le16 ul_length;
-+	u8 mcs;
-+	u8 ldpc;
-+	struct uni_event_bf_txsnd_sta_info snd_sta_info[CFG_BF_STA_REC_NUM];
-+};
-+
-+struct mt7996_mcu_txbf_fbk_info {
-+
-+	struct mt7996_mcu_bf_basic_event event;
-+
-+	__le32 u4DeQInterval;     /* By ms */
-+	__le32 u4PollPFMUIntrStatTimeOut; /* micro-sec */
-+	__le32 u4RptPktTimeOutListNum;
-+	__le32 u4RptPktListNum;
-+	__le32 u4PFMUWRTimeOutCnt;
-+	__le32 u4PFMUWRFailCnt;
-+	__le32 u4PFMUWRDoneCnt;
-+	__le32 u4PFMUWRTimeoutFreeCnt;
-+	__le32 u4FbRptPktDropCnt;
-+	__le32 au4RxPerStaFbRptCnt[CFG_BF_STA_REC_NUM];
-+};
-+
-+struct pfmu_ru_field {
-+	__le32 ru_start_id:7;
-+	__le32 _rsv1:1;
-+	__le32 ru_end_id:7;
-+	__le32 _rsv2:1;
-+} __packed;
-+
-+struct pfmu_partial_bw_info {
-+	__le32 partial_bw_info:9;
-+	__le32 _rsv1:7;
-+} __packed;
-+
-+struct mt7996_pfmu_tag1 {
-+	__le32 pfmu_idx:10;
-+	__le32 ebf:1;
-+	__le32 data_bw:3;
-+	__le32 lm:3;
-+	__le32 is_mu:1;
-+	__le32 nr:3;
-+	__le32 nc:3;
-+	__le32 codebook:2;
-+	__le32 ngroup:2;
-+	__le32 invalid_prof:1;
-+	__le32 _rsv:3;
-+
-+	__le32 col_id1:7, row_id1:9;
-+	__le32 col_id2:7, row_id2:9;
-+	__le32 col_id3:7, row_id3:9;
-+	__le32 col_id4:7, row_id4:9;
-+
-+	union {
-+		struct pfmu_ru_field field;
-+		struct pfmu_partial_bw_info bw_info;
-+	};
-+	__le32 mob_cal_en:1;
-+	__le32 _rsv2:3;
-+	__le32 mob_ru_alloc:9;	/* EHT profile uses full 9 bit */
-+	__le32 _rsv3:3;
-+
-+	__le32 snr_sts0:8, snr_sts1:8, snr_sts2:8, snr_sts3:8;
-+	__le32 snr_sts4:8, snr_sts5:8, snr_sts6:8, snr_sts7:8;
-+
-+	__le32 _rsv4;
-+} __packed;
-+
-+struct mt7996_pfmu_tag2 {
-+	__le32 smart_ant:24;
-+	__le32 se_idx:5;
-+	__le32 _rsv:3;
-+
-+	__le32 _rsv1:16;
-+	__le32 ibf_timeout:8;
-+	__le32 _rsv2:8;
-+
-+	__le32 ibf_data_bw:3;
-+	__le32 ibf_nc:3;
-+	__le32 ibf_nr:3;
-+	__le32 ibf_ru:9;
-+	__le32 _rsv3:14;
-+
-+	__le32 mob_delta_t:8;
-+	__le32 mob_lq_result:7;
-+	__le32 _rsv5:1;
-+	__le32 _rsv6:16;
-+
-+	__le32 _rsv7;
-+} __packed;
-+
-+struct mt7996_pfmu_tag_event {
-+	struct mt7996_mcu_bf_basic_event event;
-+
-+	u8 bfer;
-+	u8 __rsv[3];
-+
-+	struct mt7996_pfmu_tag1 t1;
-+	struct mt7996_pfmu_tag2 t2;
-+};
-+
-+enum {
-+	UNI_EVENT_BF_PFMU_TAG = 0x5,
-+	UNI_EVENT_BF_PFMU_DATA = 0x7,
-+	UNI_EVENT_BF_STAREC = 0xB,
-+	UNI_EVENT_BF_CAL_PHASE = 0xC,
-+	UNI_EVENT_BF_FBK_INFO = 0x17,
-+	UNI_EVENT_BF_TXSND_INFO = 0x18,
-+	UNI_EVENT_BF_PLY_INFO = 0x19,
-+	UNI_EVENT_BF_METRIC_INFO = 0x1A,
-+	UNI_EVENT_BF_TXCMD_CFG_INFO = 0x1B,
-+	UNI_EVENT_BF_SND_CNT_INFO = 0x1D,
-+	UNI_EVENT_BF_MAX_NUM
-+};
-+
-+enum {
-+	UNI_CMD_MURU_FIXED_RATE_CTRL = 0x11,
-+	UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
-+};
-+
-+struct uni_muru_mum_set_group_tbl_entry {
-+	__le16 wlan_idx0;
-+	__le16 wlan_idx1;
-+	__le16 wlan_idx2;
-+	__le16 wlan_idx3;
-+
-+	u8 dl_mcs_user0:4;
-+	u8 dl_mcs_user1:4;
-+	u8 dl_mcs_user2:4;
-+	u8 dl_mcs_user3:4;
-+	u8 ul_mcs_user0:4;
-+	u8 ul_mcs_user1:4;
-+	u8 ul_mcs_user2:4;
-+	u8 ul_mcs_user3:4;
-+
-+	u8 num_user:2;
-+	u8 rsv:6;
-+	u8 nss0:2;
-+	u8 nss1:2;
-+	u8 nss2:2;
-+	u8 nss3:2;
-+	u8 ru_alloc;
-+	u8 ru_alloc_ext;
-+
-+	u8 capa;
-+	u8 gi;
-+	u8 dl_ul;
-+	u8 _rsv2;
-+};
-+
-+enum UNI_CMD_MURU_VER_T {
-+	UNI_CMD_MURU_VER_LEG = 0,
-+	UNI_CMD_MURU_VER_HE,
-+	UNI_CMD_MURU_VER_EHT,
-+	UNI_CMD_MURU_VER_MAX
-+};
-+
-+#define UNI_MAX_MCS_SUPPORT_HE 11
-+#define UNI_MAX_MCS_SUPPORT_EHT 13
-+
-+enum {
-+	RUALLOC_BW20 = 122,
-+	RUALLOC_BW40 = 130,
-+	RUALLOC_BW80 = 134,
-+	RUALLOC_BW160 = 137,
-+	RUALLOC_BW320 = 395,
-+};
-+
-+enum {
-+	MAX_MODBF_VHT = 0,
-+	MAX_MODBF_HE = 2,
-+	MAX_MODBF_EHT = 4,
-+};
-+
-+enum {
-+	BF_SND_READ_INFO = 0,
-+	BF_SND_CFG_OPT,
-+	BF_SND_CFG_INTV,
-+	BF_SND_STA_STOP,
-+	BF_SND_CFG_MAX_STA,
-+	BF_SND_CFG_BFRP,
-+	BF_SND_CFG_INF,
-+	BF_SND_CFG_TXOP_SND
- };
- 
- enum {
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0043-mtk-mt76-mt7996-support-dup-wtbl.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0043-mtk-mt76-mt7996-support-dup-wtbl.patch
new file mode 100644
index 0000000..b3afa44
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0043-mtk-mt76-mt7996-support-dup-wtbl.patch
@@ -0,0 +1,71 @@
+From bd575b80c7fb5acfb16c3180f0f7cea4a07eff42 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 21 Sep 2023 00:52:46 +0800
+Subject: [PATCH 043/199] mtk: mt76: mt7996: support dup wtbl
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/init.c    |  1 +
+ mt7996/mt7996.h  |  1 +
+ mt7996/mtk_mcu.c | 23 +++++++++++++++++++++++
+ 3 files changed, 25 insertions(+)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 0cf054df..0201d9fc 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -688,6 +688,7 @@ static void mt7996_init_work(struct work_struct *work)
+ 	mt7996_mcu_set_eeprom(dev);
+ 	mt7996_mac_init(dev);
+ 	mt7996_txbf_init(dev);
++	mt7996_mcu_set_dup_wtbl(dev);
+ }
+ 
+ void mt7996_wfsys_reset(struct mt7996_dev *dev)
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 20fa4221..9ec2090b 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -813,6 +813,7 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
+ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
+ int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set);
+ void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb);
++int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev);
+ #endif
+ 
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index dbdf8d80..ea4e5bf2 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -257,4 +257,27 @@ void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 			 le16_to_cpu(event->basic.tag));
+ 	}
+ }
++
++int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev)
++{
++#define CHIP_CONFIG_DUP_WTBL	4
++#define DUP_WTBL_NUM	80
++	struct {
++		u8 _rsv[4];
++
++		__le16 tag;
++		__le16 len;
++		__le16 base;
++		__le16 num;
++		u8 _rsv2[4];
++	} __packed req = {
++		.tag = cpu_to_le16(CHIP_CONFIG_DUP_WTBL),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.base = cpu_to_le16(MT7996_WTBL_STA - DUP_WTBL_NUM + 1),
++		.num = cpu_to_le16(DUP_WTBL_NUM),
++	};
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(CHIP_CONFIG), &req,
++				 sizeof(req), true);
++}
+ #endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0043-mtk-wifi-mt76-mt7996-add-build-the-following-MURU-mc.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0043-mtk-wifi-mt76-mt7996-add-build-the-following-MURU-mc.patch
deleted file mode 100644
index 4064983..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0043-mtk-wifi-mt76-mt7996-add-build-the-following-MURU-mc.patch
+++ /dev/null
@@ -1,188 +0,0 @@
-From 786203d2878c468d179ec324c9d91b331e09e16a Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Tue, 13 Jun 2023 14:49:02 +0800
-Subject: [PATCH 043/116] mtk: wifi: mt76: mt7996: add build the following MURU
- mcu command tlvs
-
-It includes the following tlvs:
-1. MURU tlv id 0x10, 0x33, 0xC8, 0xC9, 0xCA, 0xCC, 0xCD
-2. BF tlv id 0x1c
----
- mt7996/mcu.h         |  1 +
- mt7996/mt7996.h      |  3 ++
- mt7996/mtk_debugfs.c | 12 +++++++
- mt7996/mtk_mcu.c     | 78 ++++++++++++++++++++++++++++++++++++++++++++
- mt7996/mtk_mcu.h     | 14 ++++++++
- 5 files changed, 108 insertions(+)
-
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 29bd7a5..848c85d 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -776,6 +776,7 @@ enum {
- 	BF_MOD_EN_CTRL = 20,
- 	BF_FBRPT_DBG_INFO_READ = 23,
- 	BF_TXSND_INFO = 24,
-+	BF_CFG_PHY = 28,
- };
- 
- enum {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 4602b4e..205a3c7 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -821,6 +821,9 @@ void mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb);
- int mt7996_mcu_set_muru_fixed_rate_enable(struct mt7996_dev *dev, u8 action, int val);
- int mt7996_mcu_set_muru_fixed_rate_parameter(struct mt7996_dev *dev, u8 action, void *para);
- int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para);
-+int mt7996_mcu_set_muru_cmd(struct mt7996_dev *dev, u16 action, int val);
-+int mt7996_mcu_muru_set_prot_frame_thr(struct mt7996_dev *dev, u32 val);
-+int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val);
- #endif
- 
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index 0851d65..b7fef1b 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -3064,6 +3064,16 @@ static const struct file_operations fops_muru_fixed_group_rate = {
- 	.llseek = default_llseek,
- };
- 
-+static int mt7996_muru_prot_thr_set(void *data, u64 val)
-+{
-+	struct mt7996_phy *phy = data;
-+
-+	return mt7996_mcu_muru_set_prot_frame_thr(phy->dev, (u32)val);
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_muru_prot_thr, NULL,
-+			 mt7996_muru_prot_thr_set, "%lld\n");
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- 	struct mt7996_dev *dev = phy->dev;
-@@ -3159,6 +3169,8 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- 	debugfs_create_file("bf_fbk_rpt", 0600, dir, phy, &fops_bf_fbk_rpt);
- 	debugfs_create_file("pfmu_tag_read", 0600, dir, phy, &fops_bf_pfmu_tag_read);
- 
-+	debugfs_create_file("muru_prot_thr", 0200, dir, phy, &fops_muru_prot_thr);
-+
- 	return 0;
- }
- 
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index 6b2cdad..6865062 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -904,4 +904,82 @@ error:
- 	return -EINVAL;
- }
- 
-+/**
-+ * This function can be used to build the following commands
-+ * MURU_SUTX_CTRL (0x10)
-+ * SET_FORCE_MU (0x33)
-+ * SET_MUDL_ACK_POLICY (0xC8)
-+ * SET_TRIG_TYPE (0xC9)
-+ * SET_20M_DYN_ALGO (0xCA)
-+ * SET_CERT_MU_EDCA_OVERRIDE (0xCD)
-+ */
-+int mt7996_mcu_set_muru_cmd(struct mt7996_dev *dev, u16 action, int val)
-+{
-+	struct {
-+		u8 _rsv[4];
-+
-+		__le16 tag;
-+		__le16 len;
-+
-+		u8 config;
-+		u8 rsv[3];
-+	} __packed data = {
-+		.tag = cpu_to_le16(action),
-+		.len = cpu_to_le16(sizeof(data) - 4),
-+		.config = (u8) val,
-+	};
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &data, sizeof(data),
-+				 false);
-+}
-+
-+int mt7996_mcu_muru_set_prot_frame_thr(struct mt7996_dev *dev, u32 val)
-+{
-+	struct {
-+		u8 _rsv[4];
-+
-+		__le16 tag;
-+		__le16 len;
-+
-+		__le32 prot_frame_thr;
-+	} __packed data = {
-+		.tag = cpu_to_le16(UNI_CMD_MURU_PROT_FRAME_THR),
-+		.len = cpu_to_le16(sizeof(data) - 4),
-+		.prot_frame_thr = cpu_to_le32(val),
-+	};
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &data, sizeof(data),
-+				 false);
-+}
-+
-+int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val)
-+{
-+#define BF_PHY_SMTH_INT_BYPASS 0
-+#define BYPASS_VAL 1
-+	struct mt7996_dev *dev = phy->dev;
-+	struct {
-+		u8 _rsv[4];
-+
-+		u16 tag;
-+		u16 len;
-+
-+		u8 action;
-+		u8 band_idx;
-+		u8 smthintbypass;
-+		u8 __rsv2[5];
-+	} __packed data = {
-+		.tag = cpu_to_le16(BF_CFG_PHY),
-+		.len = cpu_to_le16(sizeof(data) - 4),
-+		.action = BF_PHY_SMTH_INT_BYPASS,
-+		.band_idx = phy->mt76->band_idx,
-+		.smthintbypass = val,
-+	};
-+
-+	if (val != BYPASS_VAL)
-+		return -EINVAL;
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &data, sizeof(data),
-+				 true);
-+}
-+
- #endif
-diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
-index 27d6a05..d9686eb 100644
---- a/mt7996/mtk_mcu.h
-+++ b/mt7996/mtk_mcu.h
-@@ -119,6 +119,20 @@ enum {
- 	EDCCA_FCC = 1,
- 	EDCCA_ETSI = 2,
- 	EDCCA_JAPAN = 3
-+};
-+
-+enum {
-+	UNI_CMD_MURU_SUTX_CTRL = 0x10,
-+	UNI_CMD_MURU_FIXED_RATE_CTRL,
-+	UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
-+	UNI_CMD_MURU_SET_FORCE_MU = 0x33,
-+	UNI_CMD_MURU_MUNUAL_CONFIG = 0x64,
-+	UNI_CMD_MURU_SET_MUDL_ACK_POLICY = 0xC9,
-+	UNI_CMD_MURU_SET_TRIG_TYPE,
-+	UNI_CMD_MURU_SET_20M_DYN_ALGO,
-+	UNI_CMD_MURU_PROT_FRAME_THR = 0xCC,
-+	UNI_CMD_MURU_SET_CERT_MU_EDCA_OVERRIDE,
-+};
- 
- struct bf_pfmu_tag {
- 	__le16 tag;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0044-mtk-mt76-try-more-times-when-send-message-timeout.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0044-mtk-mt76-try-more-times-when-send-message-timeout.patch
new file mode 100644
index 0000000..6b7f250
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0044-mtk-mt76-try-more-times-when-send-message-timeout.patch
@@ -0,0 +1,211 @@
+From 9344da8ab6885e336d0fbc7c90f59feeb580362d Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Mon, 6 Nov 2023 11:10:10 +0800
+Subject: [PATCH 044/199] mtk: mt76: try more times when send message timeout.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ dma.c        |  7 ++++--
+ mcu.c        | 65 ++++++++++++++++++++++++++++++++++++----------------
+ mt7996/mac.c | 37 ++++++++++--------------------
+ 3 files changed, 62 insertions(+), 47 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index 56044639..66c000ef 100644
+--- a/dma.c
++++ b/dma.c
+@@ -504,9 +504,12 @@ mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, struct mt76_queue *q,
+ {
+ 	struct mt76_queue_buf buf = {};
+ 	dma_addr_t addr;
++	int ret = -ENOMEM;
+ 
+-	if (test_bit(MT76_MCU_RESET, &dev->phy.state))
++	if (test_bit(MT76_MCU_RESET, &dev->phy.state)) {
++		ret = -EAGAIN;
+ 		goto error;
++	}
+ 
+ 	if (q->queued + 1 >= q->ndesc - 1)
+ 		goto error;
+@@ -528,7 +531,7 @@ mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, struct mt76_queue *q,
+ 
+ error:
+ 	dev_kfree_skb(skb);
+-	return -ENOMEM;
++	return ret;
+ }
+ 
+ static int
+diff --git a/mcu.c b/mcu.c
+index fa4b0544..2926f715 100644
+--- a/mcu.c
++++ b/mcu.c
+@@ -4,6 +4,7 @@
+  */
+ 
+ #include "mt76.h"
++#include "mt76_connac.h"
+ #include <linux/moduleparam.h>
+ 
+ struct sk_buff *
+@@ -74,35 +75,59 @@ int mt76_mcu_skb_send_and_get_msg(struct mt76_dev *dev, struct sk_buff *skb,
+ 				  int cmd, bool wait_resp,
+ 				  struct sk_buff **ret_skb)
+ {
++#define MT76_MSG_MAX_RETRY_CNT 3
+ 	unsigned long expires;
+-	int ret, seq;
++	int ret, seq, retry_cnt;
++	struct sk_buff *skb_tmp;
++	bool retry = wait_resp && is_mt7996(dev);
+ 
+ 	if (ret_skb)
+ 		*ret_skb = NULL;
+ 
+ 	mutex_lock(&dev->mcu.mutex);
+-
+-	ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb, cmd, &seq);
+-	if (ret < 0)
+-		goto out;
+-
+-	if (!wait_resp) {
+-		ret = 0;
+-		goto out;
++	retry_cnt = retry ? MT76_MSG_MAX_RETRY_CNT : 1;
++	while (retry_cnt) {
++		skb_tmp = mt76_mcu_msg_alloc(dev, skb->data, skb->len);
++		if (!skb_tmp)
++			goto out;
++
++		if (retry && retry_cnt < MT76_MSG_MAX_RETRY_CNT) {
++			if (test_bit(MT76_MCU_RESET, &dev->phy.state))
++				usleep_range(200000, 500000);
++			dev_err(dev->dev, "send message %08x timeout, try again.\n", cmd);
++		}
++
++		ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb_tmp, cmd, &seq);
++		if (ret < 0 && ret != -EAGAIN)
++			goto out;
++
++		if (!wait_resp) {
++			ret = 0;
++			goto out;
++		}
++
++		expires = jiffies + dev->mcu.timeout;
++
++		do {
++			skb_tmp = mt76_mcu_get_response(dev, expires);
++			ret = dev->mcu_ops->mcu_parse_response(dev, cmd, skb_tmp, seq);
++			if (ret == -ETIMEDOUT)
++				break;
++
++			if (!ret && ret_skb)
++				*ret_skb = skb_tmp;
++			else
++				dev_kfree_skb(skb_tmp);
++
++			if (ret != -EAGAIN)
++				goto out;
++		} while (ret == -EAGAIN);
++
++		retry_cnt--;
+ 	}
+ 
+-	expires = jiffies + dev->mcu.timeout;
+-
+-	do {
+-		skb = mt76_mcu_get_response(dev, expires);
+-		ret = dev->mcu_ops->mcu_parse_response(dev, cmd, skb, seq);
+-		if (!ret && ret_skb)
+-			*ret_skb = skb;
+-		else
+-			dev_kfree_skb(skb);
+-	} while (ret == -EAGAIN);
+-
+ out:
++	dev_kfree_skb(skb);
+ 	mutex_unlock(&dev->mcu.mutex);
+ 
+ 	return ret;
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 8226e443..503a562a 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1793,13 +1793,24 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
+ 	phy3 = mt7996_phy3(dev);
+ 	dev->recovery.hw_full_reset = true;
+ 
+-	wake_up(&dev->mt76.mcu.wait);
+ 	ieee80211_stop_queues(mt76_hw(dev));
+ 	if (phy2)
+ 		ieee80211_stop_queues(phy2->mt76->hw);
+ 	if (phy3)
+ 		ieee80211_stop_queues(phy3->mt76->hw);
+ 
++	set_bit(MT76_RESET, &dev->mphy.state);
++	set_bit(MT76_MCU_RESET, &dev->mphy.state);
++	wake_up(&dev->mt76.mcu.wait);
++	if (phy2) {
++		set_bit(MT76_RESET, &phy2->mt76->state);
++		set_bit(MT76_MCU_RESET, &phy2->mt76->state);
++	}
++	if (phy3) {
++		set_bit(MT76_RESET, &phy3->mt76->state);
++		set_bit(MT76_MCU_RESET, &phy3->mt76->state);
++	}
++
+ 	cancel_work_sync(&dev->wed_rro.work);
+ 	cancel_delayed_work_sync(&dev->mphy.mac_work);
+ 	if (phy2)
+@@ -1902,16 +1913,6 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 	set_bit(MT76_MCU_RESET, &dev->mphy.state);
+ 	wake_up(&dev->mt76.mcu.wait);
+ 
+-	cancel_work_sync(&dev->wed_rro.work);
+-	cancel_delayed_work_sync(&dev->mphy.mac_work);
+-	if (phy2) {
+-		set_bit(MT76_RESET, &phy2->mt76->state);
+-		cancel_delayed_work_sync(&phy2->mt76->mac_work);
+-	}
+-	if (phy3) {
+-		set_bit(MT76_RESET, &phy3->mt76->state);
+-		cancel_delayed_work_sync(&phy3->mt76->mac_work);
+-	}
+ 	mt76_worker_disable(&dev->mt76.tx_worker);
+ 	mt76_for_each_q_rx(&dev->mt76, i) {
+ 		if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
+@@ -1922,8 +1923,6 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 	}
+ 	napi_disable(&dev->mt76.tx_napi);
+ 
+-	mutex_lock(&dev->mt76.mutex);
+-
+ 	mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED);
+ 
+ 	if (mt7996_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) {
+@@ -1996,20 +1995,8 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 	if (phy3)
+ 		ieee80211_wake_queues(phy3->mt76->hw);
+ 
+-	mutex_unlock(&dev->mt76.mutex);
+-
+ 	mt7996_update_beacons(dev);
+ 
+-	ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work,
+-				     MT7996_WATCHDOG_TIME);
+-	if (phy2)
+-		ieee80211_queue_delayed_work(phy2->mt76->hw,
+-					     &phy2->mt76->mac_work,
+-					     MT7996_WATCHDOG_TIME);
+-	if (phy3)
+-		ieee80211_queue_delayed_work(phy3->mt76->hw,
+-					     &phy3->mt76->mac_work,
+-					     MT7996_WATCHDOG_TIME);
+ 	dev_info(dev->mt76.dev,"\n%s L1 SER recovery completed.",
+ 		 wiphy_name(dev->mt76.hw->wiphy));
+ }
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0044-mtk-wifi-mt76-mt7996-add-cert-patch.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0044-mtk-wifi-mt76-mt7996-add-cert-patch.patch
deleted file mode 100644
index cdb3a91..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0044-mtk-wifi-mt76-mt7996-add-cert-patch.patch
+++ /dev/null
@@ -1,1094 +0,0 @@
-From c9230b110a7c2db2d8863499d7a1d72b9fc74cad Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Mon, 14 Aug 2023 13:36:58 +0800
-Subject: [PATCH 044/116] mtk: wifi: mt76: mt7996: add cert patch
-
-This patch includes TGac and TGax
-
-Commit histroy:
-
-Add vendor cmd set ap wireless rts_sigta support
-
-Signed-off-by: ye he <ye.he@mediatek.com>
----
- mt7996/mac.c     |   9 ++
- mt7996/main.c    |  31 ++++++-
- mt7996/mcu.c     |  40 +++++++++
- mt7996/mcu.h     |   6 ++
- mt7996/mt7996.h  |  13 +++
- mt7996/mtk_mcu.c | 205 ++++++++++++++++++++++++++++++++++++++++++
- mt7996/mtk_mcu.h | 184 +++++++++++++++++++++++++++++++++++--
- mt7996/vendor.c  | 230 ++++++++++++++++++++++++++++++++++++++++++++++-
- mt7996/vendor.h  |  67 ++++++++++++++
- 9 files changed, 778 insertions(+), 7 deletions(-)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 18616fd..70f0c56 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -10,6 +10,7 @@
- #include "../dma.h"
- #include "mac.h"
- #include "mcu.h"
-+#include "vendor.h"
- 
- #define to_rssi(field, rcpi)	((FIELD_GET(field, rcpi) - 220) / 2)
- 
-@@ -2284,6 +2285,14 @@ void mt7996_mac_update_stats(struct mt7996_phy *phy)
- 	}
- }
- 
-+void mt7996_set_wireless_amsdu(struct ieee80211_hw *hw, u8 en)
-+{
-+	if (en)
-+		ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
-+	else
-+		ieee80211_hw_clear(hw, SUPPORTS_AMSDU_IN_AMPDU);
-+}
-+
- void mt7996_mac_sta_rc_work(struct work_struct *work)
- {
- 	struct mt7996_dev *dev = container_of(work, struct mt7996_dev, rc_work);
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 92b2834..8e67616 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -613,6 +613,7 @@ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 		       bool beacon, bool mcast)
- {
- 	struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
-+	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt76_phy *mphy = hw->priv;
- 	u16 rate;
- 	u8 i, idx;
-@@ -622,6 +623,9 @@ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	if (beacon) {
- 		struct mt7996_phy *phy = mphy->priv;
- 
-+		if (dev->cert_mode && phy->mt76->band_idx == MT_BAND2)
-+			rate = 0x0200;
-+
- 		/* odd index for driver, even index for firmware */
- 		idx = MT7996_BEACON_RATES_TBL + 2 * phy->mt76->band_idx;
- 		if (phy->beacon_rate != rate)
-@@ -749,6 +753,10 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 	u8 band_idx = mvif->phy->mt76->band_idx;
- 	int ret, idx;
- 
-+#ifdef CONFIG_MTK_VENDOR
-+	struct mt7996_phy *phy = &dev->phy;
-+#endif
-+
- 	idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
- 	if (idx < 0)
- 		return -ENOSPC;
-@@ -774,7 +782,28 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 	if (ret)
- 		return ret;
- 
--	return mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
-+	ret = mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
-+	if (ret)
-+		return ret;
-+
-+#ifdef CONFIG_MTK_VENDOR
-+	switch (band_idx) {
-+	case MT_BAND1:
-+		phy = mt7996_phy2(dev);
-+		break;
-+	case MT_BAND2:
-+		phy = mt7996_phy3(dev);
-+		break;
-+	case MT_BAND0:
-+	default:
-+		break;
-+	}
-+
-+	if (phy && phy->muru_onoff & MUMIMO_DL_CERT)
-+		mt7996_mcu_set_mimo(phy);
-+#endif
-+
-+	return 0;
- }
- 
- void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index d39a73f..423d918 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -1352,6 +1352,10 @@ mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
- {
- 	struct sta_rec_vht *vht;
- 	struct tlv *tlv;
-+#ifdef CONFIG_MTK_VENDOR
-+	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_phy *phy = (struct mt7996_phy *)msta->vif->phy;
-+#endif
- 
- 	/* For 6G band, this tlv is necessary to let hw work normally */
- 	if (!sta->deflink.he_6ghz_capa.capa && !sta->deflink.vht_cap.vht_supported)
-@@ -1363,6 +1367,9 @@ mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
- 	vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap);
- 	vht->vht_rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map;
- 	vht->vht_tx_mcs_map = sta->deflink.vht_cap.vht_mcs.tx_mcs_map;
-+#ifdef CONFIG_MTK_VENDOR
-+	vht->rts_bw_sig = phy->rts_bw_sig;
-+#endif
- }
- 
- static void
-@@ -4440,6 +4447,27 @@ int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val)
- 				 &req, sizeof(req), true);
- }
- 
-+int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable)
-+{
-+	struct {
-+		u8 band_idx;
-+		u8 _rsv[3];
-+
-+		__le16 tag;
-+		__le16 len;
-+		bool enable;
-+		u8 _rsv2[3];
-+	} __packed req = {
-+		.band_idx = phy->mt76->band_idx,
-+		.tag = cpu_to_le16(option),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.enable = enable,
-+	};
-+
-+	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
-+				 &req, sizeof(req), true);
-+}
-+
- int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable)
- {
- 	struct {
-@@ -5007,6 +5035,18 @@ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
- 	val = FIELD_GET(RATE_CFG_VAL, *((u32 *)data));
- 
- 	switch (mode) {
-+	case RATE_PARAM_FIXED_OFDMA:
-+		if (val == 3)
-+			phy->muru_onoff = OFDMA_DL;
-+		else
-+			phy->muru_onoff = val;
-+		break;
-+	case RATE_PARAM_FIXED_MIMO:
-+		if (val == 0)
-+			phy->muru_onoff = MUMIMO_DL_CERT | MUMIMO_DL;
-+		else
-+			phy->muru_onoff = MUMIMO_UL;
-+		break;
- 	case RATE_PARAM_AUTO_MU:
- 		if (val < 0 || val > 15) {
- 			printk("Wrong value! The value is between 0-15.\n");
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 848c85d..8061638 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -755,6 +755,8 @@ enum {
- 	RATE_PARAM_FIXED_GI = 11,
- 	RATE_PARAM_AUTO = 20,
- #ifdef CONFIG_MTK_VENDOR
-+	RATE_PARAM_FIXED_MIMO = 30,
-+	RATE_PARAM_FIXED_OFDMA = 31,
- 	RATE_PARAM_AUTO_MU = 32,
- #endif
- };
-@@ -767,6 +769,7 @@ enum {
- #define OFDMA_UL                       BIT(1)
- #define MUMIMO_DL                      BIT(2)
- #define MUMIMO_UL                      BIT(3)
-+#define MUMIMO_DL_CERT                 BIT(4)
- 
- enum {
- 	BF_SOUNDING_ON = 1,
-@@ -853,11 +856,14 @@ enum {
- 	UNI_BAND_CONFIG_EDCCA_ENABLE = 0x05,
- 	UNI_BAND_CONFIG_EDCCA_THRESHOLD = 0x06,
- 	UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08,
-+	UNI_BAND_CONFIG_RTS_SIGTA_EN = 0x09,
-+	UNI_BAND_CONFIG_DIS_SECCH_CCA_DET = 0x0a,
- };
- 
- enum {
- 	UNI_WSYS_CONFIG_FW_LOG_CTRL,
- 	UNI_WSYS_CONFIG_FW_DBG_CTRL,
-+	UNI_CMD_CERT_CFG = 6,
- };
- 
- enum {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 205a3c7..30ceb00 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -354,6 +354,7 @@ struct mt7996_phy {
- 	} test;
- #endif
- #ifdef CONFIG_MTK_VENDOR
-+	u8 rts_bw_sig;
- 	spinlock_t amnt_lock;
- 	struct mt7996_air_monitor_ctrl amnt_ctrl;
- #endif
-@@ -482,6 +483,9 @@ struct mt7996_dev {
- 	} dbg;
- 	const struct mt7996_dbg_reg_desc *dbg_reg;
- #endif
-+#ifdef CONFIG_MTK_VENDOR
-+	bool cert_mode;
-+#endif
- };
- 
- enum {
-@@ -679,6 +683,7 @@ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
- int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
- int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
- void mt7996_mcu_scs_sta_poll(struct work_struct *work);
-+int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable);
- 
- static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
- {
-@@ -797,6 +802,10 @@ void mt7996_vendor_register(struct mt7996_phy *phy);
- void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb);
- int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy,
- 				  struct ieee80211_sta *sta);
-+void mt7996_set_wireless_amsdu(struct ieee80211_hw *hw, u8 en);
-+void mt7996_mcu_set_mimo(struct mt7996_phy *phy);
-+int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val);
-+int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data);
- #endif
- 
- int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
-@@ -824,6 +833,10 @@ int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para);
- int mt7996_mcu_set_muru_cmd(struct mt7996_dev *dev, u16 action, int val);
- int mt7996_mcu_muru_set_prot_frame_thr(struct mt7996_dev *dev, u32 val);
- int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val);
-+int mt7996_mcu_set_rfeature_trig_type(struct mt7996_phy *phy, u8 enable, u8 trig_type);
-+void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type);
-+void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 ofdma_user_cnt);
-+void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type);
- #endif
- 
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index 6865062..b67d366 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -982,4 +982,209 @@ int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val)
- 				 true);
- }
- 
-+int mt7996_mcu_set_bsrp_ctrl(struct mt7996_phy *phy, u16 interval,
-+			     u16 ru_alloc, u32 trig_type, u8 trig_flow, u8 ext_cmd)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct {
-+		u8 _rsv[4];
-+
-+		__le16 tag;
-+		__le16 len;
-+
-+		__le16 interval;
-+		__le16 ru_alloc;
-+		__le32 trigger_type;
-+		u8 trigger_flow;
-+		u8 ext_cmd_bsrp;
-+		u8 band_bitmap;
-+		u8 _rsv2;
-+	} __packed req = {
-+		.tag = cpu_to_le16(UNI_CMD_MURU_BSRP_CTRL),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.interval = cpu_to_le16(interval),
-+		.ru_alloc = cpu_to_le16(ru_alloc),
-+		.trigger_type = cpu_to_le32(trig_type),
-+		.trigger_flow = trig_flow,
-+		.ext_cmd_bsrp = ext_cmd,
-+		.band_bitmap = BIT(phy->mt76->band_idx),
-+	};
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
-+				 sizeof(req), false);
-+}
-+
-+int mt7996_mcu_set_rfeature_trig_type(struct mt7996_phy *phy, u8 enable, u8 trig_type)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	int ret = 0;
-+	char buf[] = "01:00:00:1B";
-+
-+	if (enable) {
-+		ret = mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_TRIG_TYPE, trig_type);
-+		if (ret)
-+			return ret;
-+	}
-+
-+	switch (trig_type) {
-+	case CAPI_BASIC:
-+		return mt7996_mcu_set_bsrp_ctrl(phy, 5, 67, 0, 0, enable);
-+	case CAPI_BRP:
-+		return mt7996_mcu_set_txbf_snd_info(phy, buf);
-+	case CAPI_MU_BAR:
-+		return mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY,
-+					       MU_DL_ACK_POLICY_MU_BAR);
-+	case CAPI_BSRP:
-+		return mt7996_mcu_set_bsrp_ctrl(phy, 5, 67, 4, 0, enable);
-+	default:
-+		return 0;
-+	}
-+}
-+
-+int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt7996_muru *muru;
-+	struct {
-+		u8 _rsv[4];
-+
-+		__le16 tag;
-+		__le16 len;
-+
-+		u8 version;
-+		u8 revision;
-+		u8 _rsv2[2];
-+
-+		struct mt7996_muru muru;
-+	} __packed req = {
-+		.tag = cpu_to_le16(UNI_CMD_MURU_MUNUAL_CONFIG),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.version = UNI_CMD_MURU_VER_EHT,
-+	};
-+
-+	muru = (struct mt7996_muru *) data;
-+	memcpy(&req.muru, muru, sizeof(struct mt7996_muru));
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
-+				 sizeof(req), false);
-+}
-+
-+int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val)
-+{
-+	struct mt7996_muru *muru;
-+	struct mt7996_muru_dl *dl;
-+	struct mt7996_muru_ul *ul;
-+	struct mt7996_muru_comm *comm;
-+	int ret = 0;
-+
-+	muru = kzalloc(sizeof(struct mt7996_muru), GFP_KERNEL);
-+	dl = &muru->dl;
-+	ul = &muru->ul;
-+	comm = &muru->comm;
-+
-+	switch (action) {
-+	case MU_CTRL_DL_USER_CNT:
-+		dl->user_num = val;
-+		comm->ppdu_format = MURU_PPDU_HE_MU;
-+		comm->sch_type = MURU_OFDMA_SCH_TYPE_DL;
-+		muru->cfg_comm = cpu_to_le32(MURU_COMM_SET);
-+		muru->cfg_dl = cpu_to_le32(MURU_FIXED_DL_TOTAL_USER_CNT);
-+		ret = mt7996_mcu_set_muru_cfg(phy, muru);
-+		break;
-+	case MU_CTRL_UL_USER_CNT:
-+		ul->user_num = val;
-+		comm->ppdu_format = MURU_PPDU_HE_TRIG;
-+		comm->sch_type = MURU_OFDMA_SCH_TYPE_UL;
-+		muru->cfg_comm = cpu_to_le32(MURU_COMM_SET);
-+		muru->cfg_ul = cpu_to_le32(MURU_FIXED_UL_TOTAL_USER_CNT);
-+		ret = mt7996_mcu_set_muru_cfg(phy, muru);
-+		break;
-+	default:
-+		break;
-+	}
-+
-+	kfree(muru);
-+	return ret;
-+}
-+
-+void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	int enable_su;
-+
-+	switch (ppdu_type) {
-+	case CAPI_SU:
-+		enable_su = 1;
-+		mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SUTX_CTRL, enable_su);
-+		mt7996_set_muru_cfg(phy, MU_CTRL_DL_USER_CNT, 0);
-+		break;
-+	case CAPI_MU:
-+		enable_su = 0;
-+		mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SUTX_CTRL, enable_su);
-+		break;
-+	default:
-+		break;
-+	}
-+}
-+
-+void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 user_cnt)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	int enable_su = 0;
-+
-+	mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SUTX_CTRL, enable_su);
-+	mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY, MU_DL_ACK_POLICY_SU_BAR);
-+	mt7996_mcu_muru_set_prot_frame_thr(dev, 9999);
-+
-+	mt7996_set_muru_cfg(phy, type, user_cnt);
-+}
-+
-+void mt7996_mcu_set_mimo(struct mt7996_phy *phy)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
-+	int disable_ra = 1;
-+	char buf[] = "2 134 0 1 0 1 2 2 2";
-+	int force_mu = 1;
-+
-+	switch (chandef->width) {
-+	case NL80211_CHAN_WIDTH_20_NOHT:
-+	case NL80211_CHAN_WIDTH_20:
-+		strscpy(buf, "2 122 0 1 0 1 2 2 2", sizeof(buf));
-+		break;
-+	case NL80211_CHAN_WIDTH_80:
-+		break;
-+	case NL80211_CHAN_WIDTH_160:
-+		strscpy(buf, "2 137 0 1 0 1 2 2 2", sizeof(buf));
-+		break;
-+	default:
-+		break;
-+	}
-+
-+	mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY, MU_DL_ACK_POLICY_SU_BAR);
-+	mt7996_mcu_set_muru_fixed_rate_enable(dev, UNI_CMD_MURU_FIXED_RATE_CTRL, disable_ra);
-+	mt7996_mcu_set_muru_fixed_rate_parameter(dev, UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL, buf);
-+	mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_FORCE_MU, force_mu);
-+}
-+
-+void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct {
-+		u8 _rsv[4];
-+
-+		__le16 tag;
-+		__le16 len;
-+		u8 action;
-+		u8 _rsv2[3];
-+	} __packed req = {
-+		.tag = cpu_to_le16(UNI_CMD_CERT_CFG),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.action = type, /* 1: CAPI Enable */
-+	};
-+
-+	mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(WSYS_CONFIG), &req,
-+			  sizeof(req), false);
-+}
-+
- #endif
-diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
-index d9686eb..7a4140b 100644
---- a/mt7996/mtk_mcu.h
-+++ b/mt7996/mtk_mcu.h
-@@ -122,14 +122,15 @@ enum {
- };
- 
- enum {
-+	UNI_CMD_MURU_BSRP_CTRL = 0x01,
- 	UNI_CMD_MURU_SUTX_CTRL = 0x10,
--	UNI_CMD_MURU_FIXED_RATE_CTRL,
--	UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
-+	UNI_CMD_MURU_FIXED_RATE_CTRL = 0x11,
-+	UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL = 0x12,
- 	UNI_CMD_MURU_SET_FORCE_MU = 0x33,
- 	UNI_CMD_MURU_MUNUAL_CONFIG = 0x64,
--	UNI_CMD_MURU_SET_MUDL_ACK_POLICY = 0xC9,
--	UNI_CMD_MURU_SET_TRIG_TYPE,
--	UNI_CMD_MURU_SET_20M_DYN_ALGO,
-+	UNI_CMD_MURU_SET_MUDL_ACK_POLICY = 0xC8,
-+	UNI_CMD_MURU_SET_TRIG_TYPE = 0xC9,
-+	UNI_CMD_MURU_SET_20M_DYN_ALGO = 0xCA,
- 	UNI_CMD_MURU_PROT_FRAME_THR = 0xCC,
- 	UNI_CMD_MURU_SET_CERT_MU_EDCA_OVERRIDE,
- };
-@@ -533,6 +534,179 @@ struct mt7996_mcu_sr_hw_ind_event {
- 	__le32 sr_ampdu_mpdu_cnt;
- 	__le32 sr_ampdu_mpdu_acked_cnt;
- };
-+
-+struct mt7996_muru_comm {
-+	u8 pda_pol;
-+	u8 band;
-+	u8 spe_idx;
-+	u8 proc_type;
-+
-+	__le16 mlo_ctrl;
-+	u8 sch_type;
-+	u8 ppdu_format;
-+	u8 ac;
-+	u8 _rsv[3];
-+};
-+
-+struct mt7996_muru_dl {
-+	u8 user_num;
-+	u8 tx_mode;
-+	u8 bw;
-+	u8 gi;
-+
-+	u8 ltf;
-+	u8 mcs;
-+	u8 dcm;
-+	u8 cmprs;
-+
-+	__le16 ru[16];
-+
-+	u8 c26[2];
-+	u8 ack_policy;
-+	u8 tx_power;
-+
-+	__le16 mu_ppdu_duration;
-+	u8 agc_disp_order;
-+	u8 _rsv1;
-+
-+	u8 agc_disp_pol;
-+	u8 agc_disp_ratio;
-+	__le16 agc_disp_linkMFG;
-+
-+	__le16 prmbl_punc_bmp;
-+	u8 _rsv2[2];
-+
-+	struct {
-+		__le16 wlan_idx;
-+		u8 ru_alloc_seg;
-+		u8 ru_idx;
-+		u8 ldpc;
-+		u8 nss;
-+		u8 mcs;
-+		u8 mu_group_idx;
-+		u8 vht_groud_id;
-+		u8 vht_up;
-+		u8 he_start_stream;
-+		u8 he_mu_spatial;
-+		__le16 tx_power_alpha;
-+		u8 ack_policy;
-+		u8 ru_allo_ps160;
-+	} usr[16];
-+};
-+
-+struct mt7996_muru_ul {
-+	u8 user_num;
-+	u8 tx_mode;
-+
-+	u8 ba_type;
-+	u8 _rsv;
-+
-+	u8 bw;
-+	u8 gi_ltf;
-+	__le16 ul_len;
-+
-+	__le16 trig_cnt;
-+	u8 pad;
-+	u8 trig_type;
-+
-+	__le16 trig_intv;
-+	u8 trig_ta[ETH_ALEN];
-+	__le16 ul_ru[16];
-+
-+	u8 c26[2];
-+	__le16 agc_disp_linkMFG;
-+
-+	u8 agc_disp_mu_len;
-+	u8 agc_disp_pol;
-+	u8 agc_disp_ratio;
-+	u8 agc_disp_pu_idx;
-+
-+	struct {
-+		__le16 wlan_idx;
-+		u8 ru_alloc_seg;
-+		u8 ru_idx;
-+		u8 ldpc;
-+		u8 nss;
-+		u8 mcs;
-+		u8 target_rssi;
-+		__le32 trig_pkt_size;
-+		u8 ru_allo_ps160;
-+		u8 _rsv2[3];
-+	} usr[16];
-+};
-+
-+struct mt7996_muru_dbg {
-+	/* HE TB RX Debug */
-+	__le32 rx_hetb_nonsf_en_bitmap;
-+	__le32 rx_hetb_cfg[2];
-+};
-+
-+struct mt7996_muru {
-+	__le32 cfg_comm;
-+	__le32 cfg_dl;
-+	__le32 cfg_ul;
-+	__le32 cfg_dbg;
-+
-+	struct mt7996_muru_comm comm;
-+	struct mt7996_muru_dl dl;
-+	struct mt7996_muru_ul ul;
-+	struct mt7996_muru_dbg dbg;
-+};
-+
-+
-+#define MURU_PPDU_HE_TRIG	BIT(2)
-+#define MURU_PPDU_HE_MU		BIT(3)
-+
-+#define MURU_OFDMA_SCH_TYPE_DL	BIT(0)
-+#define MURU_OFDMA_SCH_TYPE_UL	BIT(1)
-+
-+/* Common Config */
-+#define MURU_COMM_PPDU_FMT	BIT(0)
-+#define MURU_COMM_SCH_TYPE	BIT(1)
-+#define MURU_COMM_BAND		BIT(2)
-+#define MURU_COMM_WMM		BIT(3)
-+#define MURU_COMM_SPE_IDX	BIT(4)
-+#define MURU_COMM_PROC_TYPE	BIT(5)
-+#define MURU_COMM_SET		(MURU_COMM_PPDU_FMT | MURU_COMM_SCH_TYPE)
-+#define MURU_COMM_SET_TM	(MURU_COMM_PPDU_FMT | MURU_COMM_BAND | \
-+				 MURU_COMM_WMM | MURU_COMM_SPE_IDX)
-+
-+/* DL Common config */
-+#define MURU_FIXED_DL_TOTAL_USER_CNT	BIT(4)
-+
-+/* UL Common Config */
-+#define MURU_FIXED_UL_TOTAL_USER_CNT	BIT(4)
-+
-+enum {
-+	CAPI_SU,
-+	CAPI_MU,
-+	CAPI_ER_SU,
-+	CAPI_TB,
-+	CAPI_LEGACY
-+};
-+
-+enum {
-+	CAPI_BASIC,
-+	CAPI_BRP,
-+	CAPI_MU_BAR,
-+	CAPI_MU_RTS,
-+	CAPI_BSRP,
-+	CAPI_GCR_MU_BAR,
-+	CAPI_BQRP,
-+	CAPI_NDP_FRP,
-+};
-+
-+enum {
-+	MU_DL_ACK_POLICY_MU_BAR = 3,
-+	MU_DL_ACK_POLICY_TF_FOR_ACK = 4,
-+	MU_DL_ACK_POLICY_SU_BAR = 5,
-+};
-+
-+enum muru_vendor_ctrl {
-+	MU_CTRL_UPDATE,
-+	MU_CTRL_DL_USER_CNT,
-+	MU_CTRL_UL_USER_CNT,
-+};
- #endif
- 
- #endif
-diff --git a/mt7996/vendor.c b/mt7996/vendor.c
-index 0d6fa77..7ab6447 100644
---- a/mt7996/vendor.c
-+++ b/mt7996/vendor.c
-@@ -10,10 +10,31 @@
- #include "vendor.h"
- #include "mtk_mcu.h"
- 
-+#ifdef CONFIG_MTK_VENDOR
- static const struct nla_policy
- mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
- 	[MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
- 	[MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_MU_CTRL_STRUCT] = {.type = NLA_BINARY },
-+};
-+
-+static const struct nla_policy
-+wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE] = {.type = NLA_U16 },
-+};
-+
-+static const struct nla_policy
-+wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
-+	[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
- };
- 
- static const struct nla_policy
-@@ -76,6 +97,17 @@ pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
- 	[MTK_VENDOR_ATTR_PP_BAND_IDX] = { .type = NLA_U8 },
- };
- 
-+static const struct nla_policy
-+rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG] = { .type = NLA_NESTED },
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
-+};
-+
- struct mt7996_amnt_data {
- 	u8 idx;
- 	u8 addr[ETH_ALEN];
-@@ -90,6 +122,8 @@ static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
- {
- 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
- 	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_MU_CTRL];
-+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct mt7996_muru *muru;
- 	int err;
- 	u8 val8;
- 	u32 val32 = 0;
-@@ -105,9 +139,17 @@ static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
- 			 FIELD_PREP(RATE_CFG_VAL, val8);
- 		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
- 							   mt7996_set_wireless_vif, &val32);
-+	} else if (tb[MTK_VENDOR_ATTR_MU_CTRL_STRUCT]) {
-+		muru = kzalloc(sizeof(struct mt7996_muru), GFP_KERNEL);
-+
-+		nla_memcpy(muru, tb[MTK_VENDOR_ATTR_MU_CTRL_STRUCT],
-+			   sizeof(struct mt7996_muru));
-+
-+		err = mt7996_mcu_set_muru_cfg(phy, muru);
-+		kfree(muru);
- 	}
- 
--	return 0;
-+	return err;
- }
- 
- static int
-@@ -130,6 +172,48 @@ mt7996_vendor_mu_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
- 	return len;
- }
- 
-+void mt7996_set_wireless_rts_sigta(struct ieee80211_hw *hw, u8 value) {
-+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+
-+	switch (value) {
-+	case BW_SIGNALING_STATIC:
-+	case BW_SIGNALING_DYNAMIC:
-+		mt7996_mcu_set_band_confg(phy, UNI_BAND_CONFIG_RTS_SIGTA_EN, true);
-+		mt7996_mcu_set_band_confg(phy, UNI_BAND_CONFIG_DIS_SECCH_CCA_DET, false);
-+		break;
-+	default:
-+		value = BW_SIGNALING_DISABLE;
-+		mt7996_mcu_set_band_confg(phy, UNI_BAND_CONFIG_RTS_SIGTA_EN, false);
-+		mt7996_mcu_set_band_confg(phy, UNI_BAND_CONFIG_DIS_SECCH_CCA_DET, true);
-+		break;
-+      }
-+
-+	phy->rts_bw_sig = value;
-+
-+	/* Set RTS Threshold to a lower Value */
-+	mt7996_mcu_set_rts_thresh(phy, 500);
-+}
-+
-+static int
-+mt7996_vendor_wireless_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
-+				 struct sk_buff *skb, const void *data, int data_len,
-+				 unsigned long *storage)
-+{
-+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+	int len = 0;
-+
-+	if (*storage == 1)
-+		return -ENOENT;
-+	*storage = 1;
-+
-+	if (nla_put_u8(skb, MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU,
-+		       ieee80211_hw_check(hw, SUPPORTS_AMSDU_IN_AMPDU)))
-+	return -ENOMEM;
-+	len += 1;
-+
-+	return len;
-+ }
-+
- void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb)
- {
- 	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
-@@ -712,6 +796,126 @@ error:
- 	return -EINVAL;
- }
- 
-+static int mt7996_vendor_rfeature_ctrl(struct wiphy *wiphy,
-+				       struct wireless_dev *wdev,
-+				       const void *data,
-+				       int data_len)
-+{
-+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct mt7996_dev *dev = phy->dev;
-+	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL];
-+	int err;
-+	u32 val;
-+
-+	err = nla_parse(tb, MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX, data, data_len,
-+			rfeature_ctrl_policy, NULL);
-+	if (err)
-+		return err;
-+
-+	val = CAPI_RFEATURE_CHANGED;
-+
-+	if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG]) {
-+		u8 enable, trig_type;
-+		int rem;
-+		struct nlattr *cur;
-+
-+		nla_for_each_nested(cur, tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG], rem) {
-+			switch (nla_type(cur)) {
-+			case MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN:
-+				enable = nla_get_u8(cur);
-+				break;
-+			case MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE:
-+				trig_type = nla_get_u8(cur);
-+				break;
-+			default:
-+				return -EINVAL;
-+			};
-+		}
-+
-+		err = mt7996_mcu_set_rfeature_trig_type(phy, enable, trig_type);
-+		if (err)
-+			return err;
-+	} else if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY]) {
-+		u8 ack_policy;
-+
-+		ack_policy = nla_get_u8(tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY]);
-+		switch (ack_policy) {
-+		case MU_DL_ACK_POLICY_TF_FOR_ACK:
-+			return mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY,
-+						       ack_policy);
-+		default:
-+			return 0;
-+		}
-+	}
-+
-+	return 0;
-+}
-+
-+static int mt7996_vendor_wireless_ctrl(struct wiphy *wiphy,
-+				       struct wireless_dev *wdev,
-+				       const void *data,
-+				       int data_len)
-+{
-+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct mt7996_dev *dev = phy->dev;
-+	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL];
-+	int err;
-+	u8 val8;
-+	u16 val16;
-+	u32 val32;
-+
-+	err = nla_parse(tb, MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX, data, data_len,
-+			wireless_ctrl_policy, NULL);
-+	if (err)
-+		return err;
-+
-+	val32 = CAPI_WIRELESS_CHANGED;
-+
-+	if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA]) {
-+		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA]);
-+		val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_FIXED_OFDMA) |
-+			 FIELD_PREP(RATE_CFG_VAL, val8);
-+		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-+			mt7996_set_wireless_vif, &val32);
-+		if (val8 == 3) /* DL20and80 */
-+			mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_20M_DYN_ALGO, 1);
-+	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE]) {
-+		val16 = nla_get_u16(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE]);
-+		hw->max_tx_aggregation_subframes = val16;
-+		hw->max_rx_aggregation_subframes = val16;
-+	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE]) {
-+		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE]);
-+		mt7996_mcu_set_ppdu_tx_type(phy, val8);
-+	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA]) {
-+		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA]);
-+		if (phy->muru_onoff & OFDMA_UL)
-+			mt7996_mcu_set_nusers_ofdma(phy, MU_CTRL_UL_USER_CNT, val8);
-+		else
-+			mt7996_mcu_set_nusers_ofdma(phy, MU_CTRL_DL_USER_CNT, val8);
-+	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO]) {
-+		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO]);
-+		val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_FIXED_MIMO) |
-+			 FIELD_PREP(RATE_CFG_VAL, val8);
-+		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-+			mt7996_set_wireless_vif, &val32);
-+	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]) {
-+		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]);
-+		dev->cert_mode = val8;
-+		mt7996_mcu_set_cert(phy, val8);
-+		mt7996_mcu_set_bypass_smthint(phy, val8);
-+	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU]) {
-+		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU]);
-+		mt7996_set_wireless_amsdu(hw, val8);
-+	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA]) {
-+		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA]);
-+		mt7996_set_wireless_rts_sigta(hw, val8);
-+	}
-+
-+	return 0;
-+}
-+
- static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- 	{
- 		.info = {
-@@ -725,6 +929,18 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- 		.policy = mu_ctrl_policy,
- 		.maxattr = MTK_VENDOR_ATTR_MU_CTRL_MAX,
- 	},
-+	{
-+		.info = {
-+		        .vendor_id = MTK_NL80211_VENDOR_ID,
-+		        .subcmd = MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL,
-+		},
-+		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+		        WIPHY_VENDOR_CMD_NEED_RUNNING,
-+		.doit = mt7996_vendor_wireless_ctrl,
-+		.dumpit = mt7996_vendor_wireless_ctrl_dump,
-+		.policy = wireless_ctrl_policy,
-+		.maxattr = MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX,
-+	},
- 	{
- 		.info = {
- 			.vendor_id = MTK_NL80211_VENDOR_ID,
-@@ -794,6 +1010,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- 		.policy = pp_ctrl_policy,
- 		.maxattr = MTK_VENDOR_ATTR_PP_CTRL_MAX,
- 	},
-+	{
-+		.info = {
-+			.vendor_id = MTK_NL80211_VENDOR_ID,
-+			.subcmd = MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL,
-+		},
-+		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+			WIPHY_VENDOR_CMD_NEED_RUNNING,
-+		.doit = mt7996_vendor_rfeature_ctrl,
-+		.policy = rfeature_ctrl_policy,
-+		.maxattr = MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX,
-+	},
- };
- 
- void mt7996_vendor_register(struct mt7996_phy *phy)
-@@ -803,3 +1030,4 @@ void mt7996_vendor_register(struct mt7996_phy *phy)
- 
- 	spin_lock_init(&phy->amnt_lock);
- }
-+#endif
-diff --git a/mt7996/vendor.h b/mt7996/vendor.h
-index 8aaa18e..2ee1339 100644
---- a/mt7996/vendor.h
-+++ b/mt7996/vendor.h
-@@ -3,8 +3,12 @@
- 
- #define MTK_NL80211_VENDOR_ID	0x0ce7
- 
-+#ifdef CONFIG_MTK_VENDOR
-+
- enum mtk_nl80211_vendor_subcmds {
- 	MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
-+	MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
-+	MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
- 	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
- 	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
- 	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
-@@ -61,6 +65,7 @@ enum mtk_vendor_attr_mu_ctrl {
- 
- 	MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
- 	MTK_VENDOR_ATTR_MU_CTRL_DUMP,
-+	MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
- 
- 	/* keep last */
- 	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
-@@ -68,6 +73,66 @@ enum mtk_vendor_attr_mu_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
- };
- 
-+enum mtk_capi_control_changed {
-+	CAPI_RFEATURE_CHANGED = BIT(16),
-+	CAPI_WIRELESS_CHANGED = BIT(17),
-+};
-+
-+enum mtk_vendor_attr_rfeature_ctrl {
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX =
-+	NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_wireless_ctrl {
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT = 9,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_MU_EDCA, /* reserve */
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_wireless_dump {
-+	MTK_VENDOR_ATTR_WIRELESS_DUMP_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP,
-+	MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX =
-+		NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
-+};
-+
-+enum bw_sig {
-+	BW_SIGNALING_DISABLE,
-+	BW_SIGNALING_STATIC,
-+	BW_SIGNALING_DYNAMIC
-+};
-+
- enum mtk_vendor_attr_mnt_ctrl {
- 	MTK_VENDOR_ATTR_AMNT_CTRL_UNSPEC,
- 
-@@ -151,3 +216,5 @@ enum mtk_vendor_attr_pp_ctrl {
- };
- 
- #endif
-+
-+#endif
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0045-mtk-mt76-mt7996-add-SER-overlap-handle.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0045-mtk-mt76-mt7996-add-SER-overlap-handle.patch
new file mode 100644
index 0000000..d574613
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0045-mtk-mt76-mt7996-add-SER-overlap-handle.patch
@@ -0,0 +1,105 @@
+From 4a53b4dbb4c727e79752c35c588cb6ebb82d072a Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Tue, 21 Nov 2023 09:55:46 +0800
+Subject: [PATCH 045/199] mtk: mt76: mt7996: add SER overlap handle
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ mcu.c           |  3 ++-
+ mt7996/mac.c    | 11 +++++++++++
+ mt7996/mcu.c    |  8 ++++++++
+ mt7996/mt7996.h |  2 ++
+ 4 files changed, 23 insertions(+), 1 deletion(-)
+
+diff --git a/mcu.c b/mcu.c
+index 2926f715..a7afa2d7 100644
+--- a/mcu.c
++++ b/mcu.c
+@@ -94,7 +94,8 @@ int mt76_mcu_skb_send_and_get_msg(struct mt76_dev *dev, struct sk_buff *skb,
+ 		if (retry && retry_cnt < MT76_MSG_MAX_RETRY_CNT) {
+ 			if (test_bit(MT76_MCU_RESET, &dev->phy.state))
+ 				usleep_range(200000, 500000);
+-			dev_err(dev->dev, "send message %08x timeout, try again.\n", cmd);
++			dev_err(dev->dev, "send message %08x timeout, try again(%d).\n",
++				cmd, (MT76_MSG_MAX_RETRY_CNT - retry_cnt));
+ 		}
+ 
+ 		ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb_tmp, cmd, &seq);
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 503a562a..63408421 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1894,6 +1894,7 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 	if (!(READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA))
+ 		return;
+ 
++	dev->recovery.l1_reset_last = dev->recovery.l1_reset;
+ 	dev_info(dev->mt76.dev,"\n%s L1 SER recovery start.",
+ 		 wiphy_name(dev->mt76.hw->wiphy));
+ 
+@@ -1911,6 +1912,10 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 
+ 	set_bit(MT76_RESET, &dev->mphy.state);
+ 	set_bit(MT76_MCU_RESET, &dev->mphy.state);
++	if (phy2)
++		set_bit(MT76_RESET, &phy2->mt76->state);
++	if (phy3)
++		set_bit(MT76_RESET, &phy3->mt76->state);
+ 	wake_up(&dev->mt76.mcu.wait);
+ 
+ 	mt76_worker_disable(&dev->mt76.tx_worker);
+@@ -2106,6 +2111,9 @@ void mt7996_coredump(struct mt7996_dev *dev, u8 state)
+ 
+ void mt7996_reset(struct mt7996_dev *dev)
+ {
++	dev_info(dev->mt76.dev, "%s SER recovery state: 0x%08x\n",
++		 wiphy_name(dev->mt76.hw->wiphy), READ_ONCE(dev->recovery.state));
++
+ 	if (!dev->recovery.hw_init_done)
+ 		return;
+ 
+@@ -2125,6 +2133,9 @@ void mt7996_reset(struct mt7996_dev *dev)
+ 		return;
+ 	}
+ 
++	if ((READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA))
++		dev->recovery.l1_reset++;
++
+ 	queue_work(dev->mt76.wq, &dev->reset_work);
+ 	wake_up(&dev->reset_wait);
+ }
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 548f6660..49a55bd3 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -246,6 +246,14 @@ mt7996_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
+ 	u32 val;
+ 	u8 seq;
+ 
++	if (dev->recovery.l1_reset_last != dev->recovery.l1_reset) {
++		dev_info(dev->mt76.dev,"\n%s L1 SER recovery overlap, drop message %08x.",
++			 wiphy_name(dev->mt76.hw->wiphy), cmd);
++
++		dev_kfree_skb(skb);
++		return -EPERM;
++	}
++
+ 	mdev->mcu.timeout = 20 * HZ;
+ 
+ 	seq = ++dev->mt76.mcu.msg_seq & 0xf;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 9ec2090b..47a316e1 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -397,6 +397,8 @@ struct mt7996_dev {
+ 	wait_queue_head_t reset_wait;
+ 	struct {
+ 		u32 state;
++		u32 l1_reset;
++		u32 l1_reset_last;
+ 		u32 wa_reset_count;
+ 		u32 wm_reset_count;
+ 		bool hw_full_reset:1;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0045-mtk-wifi-mt76-testmode-add-testmode-bf-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0045-mtk-wifi-mt76-testmode-add-testmode-bf-support.patch
deleted file mode 100644
index fc0ff2e..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0045-mtk-wifi-mt76-testmode-add-testmode-bf-support.patch
+++ /dev/null
@@ -1,2067 +0,0 @@
-From 822d3687ba4c131b1850a06116ee39e5ebc3767c Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Thu, 6 Apr 2023 16:40:28 +0800
-Subject: [PATCH 045/116] mtk: wifi: mt76: testmode: add testmode bf support
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Add iTest additional bf command
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-fw return Gx (5g band) ibf cal struct for both 2G and 5G ibf.
-Therefore, memcpy cannot be used for 2G ibf cal.
-https://gerrit.mediatek.inc/c/neptune/wlan_driver/logan/+/8206056
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-mtk: wifi: mt76: testmode: add testmode ibf ver2 support
-
-Add ibf ver2 support for chips after Kite
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-mtk: wifi: mt76: testmode: add testmode ibf 5T5R support
-
-Add testmode ibf 5T5R support for Kite BE7200 2i5i
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt76.h               |   5 +
- mt76_connac_mcu.h    |   4 +-
- mt7996/mcu.c         |   8 +-
- mt7996/mcu.h         |  46 ++-
- mt7996/mt7996.h      |  12 +-
- mt7996/mtk_debugfs.c |   6 +-
- mt7996/mtk_mcu.c     | 143 +++++++-
- mt7996/mtk_mcu.h     | 441 +++++++++++++++++++++++-
- mt7996/regs.h        |   3 +
- mt7996/testmode.c    | 796 +++++++++++++++++++++++++++++++++++++++++--
- mt7996/testmode.h    |  19 ++
- testmode.c           |  62 ++++
- testmode.h           |  53 +++
- tools/fields.c       |  37 ++
- 14 files changed, 1576 insertions(+), 59 deletions(-)
-
-diff --git a/mt76.h b/mt76.h
-index 0cc6e53..8b17dfb 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -757,6 +757,11 @@ struct mt76_testmode_data {
- 	u32 tx_time;
- 	u32 tx_ipg;
- 
-+	u8 txbf_act;
-+	u16 txbf_param[8];
-+	bool is_txbf_dut;
-+	bool bf_en;
-+	bool bf_ever_en;
- 	bool ibf;
- 	bool ebf;
- 
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 7f2daf7..61a14a8 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -488,7 +488,8 @@ struct sta_rec_bf {
- 	bool codebook75_mu;
- 
- 	u8 he_ltf;
--	u8 rsv[3];
-+	u8 pp_fd_val;
-+	u8 rsv[2];
- } __packed;
- 
- struct sta_rec_bfee {
-@@ -1281,6 +1282,7 @@ enum {
- 	MCU_UNI_CMD_VOW = 0x37,
- 	MCU_UNI_CMD_PP = 0x38,
- 	MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
-+	MCU_UNI_CMD_TESTMODE_TRX_PARAM = 0x42,
- 	MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
- 	MCU_UNI_CMD_PRECAL_RESULT = 0x47,
- 	MCU_UNI_CMD_RRO = 0x57,
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 423d918..18d8f4d 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -1073,7 +1073,12 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
- 	bss->hw_bss_idx = idx;
- 
- 	if (vif->type == NL80211_IFTYPE_MONITOR) {
--		memcpy(bss->bssid, phy->macaddr, ETH_ALEN);
-+		struct mt76_testmode_data *td = &phy->test;
-+
-+		if (!td->bf_en)
-+			memcpy(bss->bssid, phy->macaddr, ETH_ALEN);
-+		else
-+			memcpy(bss->bssid, td->addr[2], ETH_ALEN);
- 		return 0;
- 	}
- 
-@@ -4097,7 +4102,6 @@ int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 val, u8 band)
- int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action)
- {
- #define MT7996_BF_MAX_SIZE	sizeof(union bf_tag_tlv)
--#define BF_PROCESSING	4
- 	struct uni_header hdr;
- 	struct sk_buff *skb;
- 	struct tlv *tlv;
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 8061638..6631281 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -685,6 +685,22 @@ struct bf_sounding_on {
- 	__le32 snd_period;
- } __packed;
- 
-+enum sounding_mode {
-+	SU_SOUNDING,
-+	MU_SOUNDING,
-+	SU_PERIODIC_SOUNDING,
-+	MU_PERIODIC_SOUNDING,
-+	BF_PROCESSING,
-+	TXCMD_NONTB_SU_SOUNDING,
-+	TXCMD_VHT_MU_SOUNDING,
-+	TXCMD_TB_PER_BRP_SOUNDING,
-+	TXCMD_TB_SOUNDING,
-+
-+	/* keep last */
-+	NUM_SOUNDING_MODE,
-+	SOUNDING_MODE_MAX = NUM_SOUNDING_MODE - 1,
-+};
-+
- struct bf_hw_en_status_update {
- 	__le16 tag;
- 	__le16 len;
-@@ -710,6 +726,25 @@ union bf_tag_tlv {
- 	struct bf_mod_en_ctrl bf_mod_en;
- };
- 
-+enum {
-+	BF_SOUNDING_OFF = 0,
-+	BF_SOUNDING_ON = 1,
-+	BF_DATA_PACKET_APPLY = 2,
-+	BF_PFMU_TAG_READ = 5,
-+	BF_PFMU_TAG_WRITE = 6,
-+	BF_STA_REC_READ = 11,
-+	BF_PHASE_CALIBRATION = 12,
-+	BF_IBF_PHASE_COMP = 13,
-+	BF_PROFILE_WRITE_20M_ALL = 15,
-+	BF_HW_EN_UPDATE = 17,
-+	BF_MOD_EN_CTRL = 20,
-+	BF_FBRPT_DBG_INFO_READ = 23,
-+	BF_TXSND_INFO = 24,
-+	BF_CMD_TXCMD = 27,
-+	BF_CFG_PHY = 28,
-+	BF_PROFILE_WRITE_20M_ALL_5X5 = 30,
-+};
-+
- struct ra_rate {
- 	__le16 wlan_idx;
- 	u8 mode;
-@@ -771,17 +806,6 @@ enum {
- #define MUMIMO_UL                      BIT(3)
- #define MUMIMO_DL_CERT                 BIT(4)
- 
--enum {
--	BF_SOUNDING_ON = 1,
--	BF_PFMU_TAG_READ = 5,
--	BF_STA_REC_READ = 11,
--	BF_HW_EN_UPDATE = 17,
--	BF_MOD_EN_CTRL = 20,
--	BF_FBRPT_DBG_INFO_READ = 23,
--	BF_TXSND_INFO = 24,
--	BF_CFG_PHY = 28,
--};
--
- enum {
- 	CMD_BAND_NONE,
- 	CMD_BAND_24G,
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 30ceb00..84fbd0f 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -486,6 +486,14 @@ struct mt7996_dev {
- #ifdef CONFIG_MTK_VENDOR
- 	bool cert_mode;
- #endif
-+
-+#if defined CONFIG_NL80211_TESTMODE || defined CONFIG_MTK_DEBUG
-+	struct {
-+		void *txbf_phase_cal;
-+		void *txbf_pfmu_data;
-+		void *txbf_pfmu_tag;
-+	} test;
-+#endif
- };
- 
- enum {
-@@ -825,7 +833,7 @@ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
- int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set);
- void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb);
- int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev);
--int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx);
-+int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx, bool bfer);
- void mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb);
- int mt7996_mcu_set_muru_fixed_rate_enable(struct mt7996_dev *dev, u8 action, int val);
- int mt7996_mcu_set_muru_fixed_rate_parameter(struct mt7996_dev *dev, u8 action, void *para);
-@@ -837,10 +845,12 @@ int mt7996_mcu_set_rfeature_trig_type(struct mt7996_phy *phy, u8 enable, u8 trig
- void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type);
- void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 ofdma_user_cnt);
- void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type);
-+void mt7996_tm_update_channel(struct mt7996_phy *phy);
- #endif
- 
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
- int mt7996_dma_rro_init(struct mt7996_dev *dev);
- #endif /* CONFIG_NET_MEDIATEK_SOC_WED */
- 
-+
- #endif
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index b7fef1b..c1b665f 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -2958,7 +2958,7 @@ mt7996_starec_bf_read_set(void *data, u64 wlan_idx)
- {
- 	struct mt7996_phy *phy = data;
- 
--	return mt7996_mcu_set_txbf_internal(phy, BF_STA_REC_READ, wlan_idx);
-+	return mt7996_mcu_set_txbf_internal(phy, BF_STA_REC_READ, wlan_idx, 0);
- }
- DEFINE_DEBUGFS_ATTRIBUTE(fops_starec_bf_read, NULL,
- 			 mt7996_starec_bf_read_set, "%lld\n");
-@@ -3002,7 +3002,7 @@ mt7996_bf_fbk_rpt_set(void *data, u64 wlan_idx)
- {
- 	struct mt7996_phy *phy = data;
- 
--	return mt7996_mcu_set_txbf_internal(phy, BF_FBRPT_DBG_INFO_READ, wlan_idx);
-+	return mt7996_mcu_set_txbf_internal(phy, BF_FBRPT_DBG_INFO_READ, wlan_idx, 0);
- }
- DEFINE_DEBUGFS_ATTRIBUTE(fops_bf_fbk_rpt, NULL,
- 			 mt7996_bf_fbk_rpt_set, "%lld\n");
-@@ -3012,7 +3012,7 @@ mt7996_bf_pfmu_tag_read_set(void *data, u64 wlan_idx)
- {
- 	struct mt7996_phy *phy = data;
- 
--	return mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, wlan_idx);
-+	return mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, wlan_idx, 1);
- }
- DEFINE_DEBUGFS_ATTRIBUTE(fops_bf_pfmu_tag_read, NULL,
- 			 mt7996_bf_pfmu_tag_read_set, "%lld\n");
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index b67d366..a9a7db1 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -295,7 +295,7 @@ __mt7996_mcu_add_uni_tlv(struct sk_buff *skb, u16 tag, u16 len)
- 	return ptlv;
- }
- 
--int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx)
-+int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx, bool bfer)
- {
- 	struct mt7996_dev *dev = phy->dev;
- #define MT7996_MTK_BF_MAX_SIZE	sizeof(struct bf_starec_read)
-@@ -318,9 +318,8 @@ int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx)
- 
- 		tlv = __mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req));
- 		req = (struct bf_pfmu_tag *)tlv;
--#define BFER 1
- 		req->pfmu_id = idx;
--		req->bfer = BFER;
-+		req->bfer = bfer;
- 		req->band_idx = phy->mt76->band_idx;
- 		break;
- 	}
-@@ -432,10 +431,61 @@ int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para)
- 	return mt76_mcu_skb_send_msg(&phy->dev->mt76, skb, MCU_WM_UNI_CMD(BF), false);
- }
- 
-+static inline void
-+mt7996_ibf_phase_assign(struct mt7996_dev *dev,
-+			struct mt7996_ibf_cal_info *cal,
-+			struct mt7996_txbf_phase *phase)
-+{
-+	/* fw return ibf calibrated data with
-+	 * the mt7996_txbf_phase_info_5g struct for both 2G and 5G.
-+	 * (return struct mt7992_txbf_phase_info_5g for ibf 2.0)
-+	 * Therefore, memcpy cannot be used here.
-+	 */
-+	if (get_ibf_version(dev) != IBF_VER_2) {
-+		phase_assign(cal->group, v1, m_t0_h, true);
-+		phase_assign(cal->group, v1, m_t1_h, true);
-+		phase_assign(cal->group, v1, m_t2_h, true);
-+		phase_assign(cal->group, v1, m_t2_h_sx2, false);
-+		phase_assign_rx_v1(cal->group, v1, r0);
-+		phase_assign_rx_v1(cal->group, v1, r1);
-+		phase_assign_rx_v1(cal->group, v1, r2);
-+		phase_assign_rx_v1(cal->group, v1, r3);
-+		phase_assign_rx(cal->group, v1, r2_sx2, false);
-+		phase_assign_rx(cal->group, v1, r3_sx2, false);
-+		phase_assign(cal->group, v1, r0_reserved, false);
-+		phase_assign(cal->group, v1, r1_reserved, false);
-+		phase_assign(cal->group, v1, r2_reserved, false);
-+		phase_assign(cal->group, v1, r3_reserved, false);
-+		phase_assign(cal->group, v1, r2_sx2_reserved, false);
-+		phase_assign(cal->group, v1, r3_sx2_reserved, false);
-+	} else {
-+		phase_assign(cal->group, v2, m_t0_h, true);
-+		phase_assign(cal->group, v2, m_t1_h, true);
-+		phase_assign(cal->group, v2, m_t2_h, true);
-+		if (cal->group) {
-+			phase->v2.phase_5g.m_t3_h = cal->v2.phase_5g.m_t3_h;
-+			dev_info(dev->mt76.dev, "m_t3_h = %d\n", phase->v2.phase_5g.m_t3_h);
-+		}
-+		phase_assign_rx_ext(cal->group, v2, r0, true);
-+		phase_assign_rx_ext(cal->group, v2, r1, true);
-+		phase_assign_rx_ext(cal->group, v2, r2, true);
-+		phase_assign_rx_ext(cal->group, v2, r3, true);
-+		if (cal->group) {
-+			memcpy(&phase->v2.phase_5g.r4, &cal->v2.phase_5g.r4,
-+			       sizeof(struct txbf_rx_phase_ext));
-+			dev_info(dev->mt76.dev, "r4.rx_uh = %d\n", phase->v2.phase_5g.r4.rx_uh);
-+			dev_info(dev->mt76.dev, "r4.rx_h = %d\n", phase->v2.phase_5g.r4.rx_h);
-+			dev_info(dev->mt76.dev, "r4.rx_mh = %d\n", phase->v2.phase_5g.r4.rx_mh);
-+			dev_info(dev->mt76.dev, "r4.rx_m = %d\n", phase->v2.phase_5g.r4.rx_m);
-+			dev_info(dev->mt76.dev, "r4.rx_l = %d\n", phase->v2.phase_5g.r4.rx_l);
-+			dev_info(dev->mt76.dev, "r4.rx_ul = %d\n", phase->v2.phase_5g.r4.rx_ul);
-+		}
-+	}
-+}
-+
- void
- mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb)
- {
--#define HE_MODE 3
- 	struct mt7996_mcu_bf_basic_event *event;
- 
- 	event = (struct mt7996_mcu_bf_basic_event *)skb->data;
-@@ -470,13 +520,12 @@ mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb)
- 			 tag->t1.nr, tag->t1.nc, tag->t1.ngroup, tag->t1.lm, tag->t1.codebook,
- 			 tag->t1.mob_cal_en);
- 
--		if (tag->t1.lm <= HE_MODE) {
-+		if (tag->t1.lm <= BF_LM_HE)
- 			dev_info(dev->mt76.dev, "RU start = %d, RU end = %d\n",
- 				 tag->t1.field.ru_start_id, tag->t1.field.ru_end_id);
--		} else {
-+		else
- 			dev_info(dev->mt76.dev, "PartialBW = %d\n",
- 				 tag->t1.bw_info.partial_bw_info);
--		}
- 
- 		dev_info(dev->mt76.dev, "Mem Col1 = %d, Mem Row1 = %d, Mem Col2 = %d, Mem Row2 = %d\n",
- 			 tag->t1.col_id1, tag->t1.row_id1, tag->t1.col_id2, tag->t1.row_id2);
-@@ -728,6 +777,86 @@ mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb)
- 
- 		break;
- 	}
-+	case UNI_EVENT_BF_CAL_PHASE: {
-+		struct mt7996_ibf_cal_info *cal;
-+		struct mt7996_txbf_phase *phase;
-+		union {
-+			struct mt7996_txbf_phase_out v1;
-+			struct mt7992_txbf_phase_out v2;
-+		} phase_out;
-+		int phase_out_size = sizeof(struct mt7996_txbf_phase_out);
-+
-+		cal = (struct mt7996_ibf_cal_info *)skb->data;
-+		phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal;
-+		if (get_ibf_version(dev) == IBF_VER_2)
-+			phase_out_size = sizeof(struct mt7992_txbf_phase_out);
-+		memcpy(&phase_out, &cal->buf, phase_out_size);
-+		switch (cal->cal_type) {
-+		case IBF_PHASE_CAL_NORMAL:
-+		case IBF_PHASE_CAL_NORMAL_INSTRUMENT:
-+			/* Only calibrate group M */
-+			if (cal->group_l_m_n != GROUP_M)
-+				break;
-+			phase = &phase[cal->group];
-+			phase->status = cal->status;
-+			dev_info(dev->mt76.dev, "Calibrated result = %d\n", phase->status);
-+			dev_info(dev->mt76.dev, "Group %d and Group M\n", cal->group);
-+			mt7996_ibf_phase_assign(dev, cal, phase);
-+			break;
-+		case IBF_PHASE_CAL_VERIFY:
-+		case IBF_PHASE_CAL_VERIFY_INSTRUMENT:
-+			dev_info(dev->mt76.dev, "Verification result = %d\n", cal->status);
-+			break;
-+		default:
-+			break;
-+		}
-+
-+		if (get_ibf_version(dev) == IBF_VER_2) {
-+			dev_info(dev->mt76.dev,
-+				 "c0_uh = %d, c1_uh = %d, c2_uh = %d, c3_uh = %d c4_uh = %d\n",
-+				 phase_out.v2.c0_uh, phase_out.v2.c1_uh, phase_out.v2.c2_uh,
-+				 phase_out.v2.c3_uh, phase_out.v2.c4_uh);
-+			dev_info(dev->mt76.dev,
-+				 "c0_h = %d, c1_h = %d, c2_h = %d, c3_h = %d c4_h = %d\n",
-+				 phase_out.v2.c0_h, phase_out.v2.c1_h, phase_out.v2.c2_h,
-+				 phase_out.v2.c3_h, phase_out.v2.c4_h);
-+			dev_info(dev->mt76.dev,
-+				 "c0_mh = %d, c1_mh = %d, c2_mh = %d, c3_mh = %d c4_mh = %d\n",
-+				 phase_out.v2.c0_mh, phase_out.v2.c1_mh, phase_out.v2.c2_mh,
-+				 phase_out.v2.c3_mh, phase_out.v2.c4_mh);
-+			dev_info(dev->mt76.dev,
-+				 "c0_m = %d, c1_m = %d, c2_m = %d, c3_m = %d c4_m = %d\n",
-+				 phase_out.v2.c0_m, phase_out.v2.c1_m, phase_out.v2.c2_m,
-+				 phase_out.v2.c3_m, phase_out.v2.c4_m);
-+			dev_info(dev->mt76.dev,
-+				 "c0_l = %d, c1_l = %d, c2_l = %d, c3_l = %d c4_l = %d\n",
-+				 phase_out.v2.c0_l, phase_out.v2.c1_l, phase_out.v2.c2_l,
-+				 phase_out.v2.c3_l, phase_out.v2.c4_l);
-+		} else {
-+			dev_info(dev->mt76.dev,
-+				 "c0_uh = %d, c1_uh = %d, c2_uh = %d, c3_uh = %d\n",
-+				 phase_out.v1.c0_uh, phase_out.v1.c1_uh,
-+				 phase_out.v1.c2_uh, phase_out.v1.c3_uh);
-+			dev_info(dev->mt76.dev,
-+				 "c0_h = %d, c1_h = %d, c2_h = %d, c3_h = %d\n",
-+				 phase_out.v1.c0_h, phase_out.v1.c1_h,
-+				 phase_out.v1.c2_h, phase_out.v1.c3_h);
-+			dev_info(dev->mt76.dev,
-+				 "c0_mh = %d, c1_mh = %d, c2_mh = %d, c3_mh = %d\n",
-+				 phase_out.v1.c0_mh, phase_out.v1.c1_mh,
-+				 phase_out.v1.c2_mh, phase_out.v1.c3_mh);
-+			dev_info(dev->mt76.dev,
-+				 "c0_m = %d, c1_m = %d, c2_m = %d, c3_m = %d\n",
-+				 phase_out.v1.c0_m, phase_out.v1.c1_m,
-+				 phase_out.v1.c2_m, phase_out.v1.c3_m);
-+			dev_info(dev->mt76.dev,
-+				 "c0_l = %d, c1_l = %d, c2_l = %d, c3_l = %d\n",
-+				 phase_out.v1.c0_l, phase_out.v1.c1_l,
-+				 phase_out.v1.c2_l, phase_out.v1.c3_l);
-+		}
-+
-+		break;
-+	}
- 	default:
- 		dev_info(dev->mt76.dev, "%s: unknown bf event tag %d\n",
- 			 __func__, event->tag);
-diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
-index 7a4140b..58d61c5 100644
---- a/mt7996/mtk_mcu.h
-+++ b/mt7996/mtk_mcu.h
-@@ -189,6 +189,165 @@ struct bf_txsnd_info {
- 	u8 __rsv[2];
- } __packed;
- 
-+#define MAX_PHASE_GROUP_NUM	13
-+
-+struct bf_phase_comp {
-+	__le16 tag;
-+	__le16 len;
-+
-+	u8 bw;
-+	u8 jp_band;
-+	u8 band_idx;
-+	bool read_from_e2p;
-+	bool disable;
-+	u8 group;
-+	u8 rsv[2];
-+	u8 buf[44];
-+} __packed;
-+
-+struct bf_tx_apply {
-+	__le16 tag;
-+	__le16 len;
-+
-+	__le16 wlan_idx;
-+	bool ebf;
-+	bool ibf;
-+	bool mu_txbf;
-+	bool phase_cal;
-+	u8 rsv[2];
-+} __packed;
-+
-+struct bf_phase_cal {
-+	__le16 tag;
-+	__le16 len;
-+
-+	u8 group_l_m_n;
-+	u8 group;
-+	u8 sx2;
-+	u8 cal_type;
-+	u8 lna_gain_level;
-+	u8 band_idx;
-+	u8 version;
-+	u8 rsv[1];
-+} __packed;
-+
-+struct bf_txcmd {
-+	__le16 tag;
-+	__le16 len;
-+
-+	u8 action;
-+	u8 bf_manual;
-+	u8 bf_bit;
-+	u8 rsv[5];
-+} __packed;
-+
-+struct bf_pfmu_data_all {
-+	__le16 tag;
-+	__le16 len;
-+
-+	u8 pfmu_id;
-+	u8 band_idx;
-+	u8 rsv[2];
-+
-+	u8 buf[640];
-+} __packed;
-+
-+#define TXBF_DUT_MAC_SUBADDR		0x22
-+#define TXBF_GOLDEN_MAC_SUBADDR		0x11
-+
-+struct mt7996_tm_bf_req {
-+	u8 _rsv[4];
-+
-+	union {
-+		struct bf_sounding_on sounding;
-+		struct bf_tx_apply tx_apply;
-+		struct bf_pfmu_tag pfmu_tag;
-+		struct bf_pfmu_data_all pfmu_data_all;
-+		struct bf_phase_cal phase_cal;
-+		struct bf_phase_comp phase_comp;
-+		struct bf_txcmd txcmd;
-+	};
-+} __packed;
-+
-+enum tm_trx_mac_type {
-+	TM_TRX_MAC_TX = 1,
-+	TM_TRX_MAC_RX,
-+	TM_TRX_MAC_TXRX,
-+	TM_TRX_MAC_TXRX_RXV,
-+	TM_TRX_MAC_RXV,
-+	TM_TRX_MAC_RX_RXV,
-+};
-+
-+enum tm_trx_param_idx {
-+	TM_TRX_PARAM_RSV,
-+	/* MAC */
-+	TM_TRX_PARAM_SET_TRX,
-+	TM_TRX_PARAM_RX_FILTER,
-+	TM_TRX_PARAM_RX_FILTER_PKT_LEN,
-+	TM_TRX_PARAM_SLOT_TIME,
-+	TM_TRX_PARAM_CLEAN_PERSTA_TXQUEUE,
-+	TM_TRX_PARAM_AMPDU_WTBL,
-+	TM_TRX_PARAM_MU_RX_AID,
-+	TM_TRX_PARAM_PHY_MANUAL_TX,
-+
-+	/* PHY */
-+	TM_TRX_PARAM_RX_PATH,
-+	TM_TRX_PARAM_TX_STREAM,
-+	TM_TRX_PARAM_TSSI_STATUS,
-+	TM_TRX_PARAM_DPD_STATUS,
-+	TM_TRX_PARAM_RATE_POWER_OFFSET_ON_OFF,
-+	TM_TRX_PARAM_THERMO_COMP_STATUS,
-+	TM_TRX_PARAM_FREQ_OFFSET,
-+	TM_TRX_PARAM_FAGC_RSSI_PATH,
-+	TM_TRX_PARAM_PHY_STATUS_COUNT,
-+	TM_TRX_PARAM_RXV_INDEX,
-+
-+	TM_TRX_PARAM_ANTENNA_PORT,
-+	TM_TRX_PARAM_THERMAL_ONOFF,
-+	TM_TRX_PARAM_TX_POWER_CONTROL_ALL_RF,
-+	TM_TRX_PARAM_RATE_POWER_OFFSET,
-+	TM_TRX_PARAM_SLT_CMD_TEST,
-+	TM_TRX_PARAM_SKU,
-+	TM_TRX_PARAM_POWER_PERCENTAGE_ON_OFF,
-+	TM_TRX_PARAM_BF_BACKOFF_ON_OFF,
-+	TM_TRX_PARAM_POWER_PERCENTAGE_LEVEL,
-+	TM_TRX_PARAM_FRTBL_CFG,
-+	TM_TRX_PARAM_PREAMBLE_PUNC_ON_OFF,
-+
-+	TM_TRX_PARAM_MAX_NUM,
-+};
-+
-+enum trx_action {
-+	TM_TRX_ACTION_SET,
-+	TM_TRX_ACTION_GET,
-+};
-+
-+struct tm_trx_set {
-+	u8 type;
-+	u8 enable;
-+	u8 band_idx;
-+	u8 rsv;
-+} __packed;
-+
-+struct mt7996_tm_trx_req {
-+	u8 param_num;
-+	u8 _rsv[3];
-+
-+	__le16 tag;
-+	__le16 len;
-+
-+	__le16 param_idx;
-+	u8 band_idx;
-+	u8 testmode_en;
-+	u8 action;
-+	u8 rsv[3];
-+
-+	u32 data;
-+	struct tm_trx_set set_trx;
-+
-+	u8 buf[220];
-+} __packed;
-+
- struct mt7996_mcu_bf_basic_event {
- 	struct mt7996_mcu_rxd rxd;
- 
-@@ -394,6 +553,283 @@ struct mt7996_pfmu_tag_event {
- 	struct mt7996_pfmu_tag2 t2;
- };
- 
-+struct mt7996_pfmu_tag {
-+	struct mt7996_pfmu_tag1 t1;
-+	struct mt7996_pfmu_tag2 t2;
-+};
-+
-+enum bf_lm_type {
-+	BF_LM_LEGACY,
-+	BF_LM_HT,
-+	BF_LM_VHT,
-+	BF_LM_HE,
-+	BF_LM_EHT,
-+};
-+
-+struct mt7996_txbf_phase_out {
-+	u8 c0_l;
-+	u8 c1_l;
-+	u8 c2_l;
-+	u8 c3_l;
-+	u8 c0_m;
-+	u8 c1_m;
-+	u8 c2_m;
-+	u8 c3_m;
-+	u8 c0_mh;
-+	u8 c1_mh;
-+	u8 c2_mh;
-+	u8 c3_mh;
-+	u8 c0_h;
-+	u8 c1_h;
-+	u8 c2_h;
-+	u8 c3_h;
-+	u8 c0_uh;
-+	u8 c1_uh;
-+	u8 c2_uh;
-+	u8 c3_uh;
-+};
-+
-+struct mt7992_txbf_phase_out {
-+	u8 c0_l;
-+	u8 c1_l;
-+	u8 c2_l;
-+	u8 c3_l;
-+	u8 c4_l;
-+	u8 c0_m;
-+	u8 c1_m;
-+	u8 c2_m;
-+	u8 c3_m;
-+	u8 c4_m;
-+	u8 c0_mh;
-+	u8 c1_mh;
-+	u8 c2_mh;
-+	u8 c3_mh;
-+	u8 c4_mh;
-+	u8 c0_h;
-+	u8 c1_h;
-+	u8 c2_h;
-+	u8 c3_h;
-+	u8 c4_h;
-+	u8 c0_uh;
-+	u8 c1_uh;
-+	u8 c2_uh;
-+	u8 c3_uh;
-+	u8 c4_uh;
-+};
-+
-+struct txbf_rx_phase {
-+	u8 rx_uh;
-+	u8 rx_h;
-+	u8 rx_m;
-+	u8 rx_l;
-+	u8 rx_ul;
-+};
-+
-+struct txbf_rx_phase_ext {
-+	u8 rx_uh;
-+	u8 rx_h;
-+	u8 rx_mh;
-+	u8 rx_m;
-+	u8 rx_l;
-+	u8 rx_ul;
-+};
-+
-+struct mt7996_txbf_phase_info_2g {
-+	struct txbf_rx_phase r0;
-+	struct txbf_rx_phase r1;
-+	struct txbf_rx_phase r2;
-+	struct txbf_rx_phase r3;
-+	struct txbf_rx_phase r2_sx2;
-+	struct txbf_rx_phase r3_sx2;
-+	u8 m_t0_h;
-+	u8 m_t1_h;
-+	u8 m_t2_h;
-+	u8 m_t2_h_sx2;
-+	u8 r0_reserved;
-+	u8 r1_reserved;
-+	u8 r2_reserved;
-+	u8 r3_reserved;
-+	u8 r2_sx2_reserved;
-+	u8 r3_sx2_reserved;
-+};
-+
-+struct mt7996_txbf_phase_info_5g {
-+	struct txbf_rx_phase_ext r0;
-+	struct txbf_rx_phase_ext r1;
-+	struct txbf_rx_phase_ext r2;
-+	struct txbf_rx_phase_ext r3;
-+	struct txbf_rx_phase r2_sx2;	/* no middle-high in r2_sx2 */
-+	struct txbf_rx_phase r3_sx2;	/* no middle-high in r3_sx2 */
-+	u8 m_t0_h;
-+	u8 m_t1_h;
-+	u8 m_t2_h;
-+	u8 m_t2_h_sx2;
-+	u8 r0_reserved;
-+	u8 r1_reserved;
-+	u8 r2_reserved;
-+	u8 r3_reserved;
-+	u8 r2_sx2_reserved;
-+	u8 r3_sx2_reserved;
-+};
-+
-+struct mt7992_txbf_phase_info_2g {
-+	struct txbf_rx_phase_ext r0;
-+	struct txbf_rx_phase_ext r1;
-+	struct txbf_rx_phase_ext r2;
-+	struct txbf_rx_phase_ext r3;
-+	u8 m_t0_h;
-+	u8 m_t1_h;
-+	u8 m_t2_h;
-+};
-+
-+struct mt7992_txbf_phase_info_5g {
-+	struct txbf_rx_phase_ext r0;
-+	struct txbf_rx_phase_ext r1;
-+	struct txbf_rx_phase_ext r2;
-+	struct txbf_rx_phase_ext r3;
-+	struct txbf_rx_phase_ext r4;
-+	u8 m_t0_h;
-+	u8 m_t1_h;
-+	u8 m_t2_h;
-+	u8 m_t3_h;
-+};
-+
-+struct mt7996_txbf_phase {
-+	u8 status;
-+	union {
-+		union {
-+			struct mt7996_txbf_phase_info_2g phase_2g;
-+			struct mt7996_txbf_phase_info_5g phase_5g;
-+		} v1;
-+		union {
-+			struct mt7992_txbf_phase_info_2g phase_2g;
-+			struct mt7992_txbf_phase_info_5g phase_5g;
-+		} v2;
-+		u8 buf[44];
-+	};
-+};
-+
-+#define phase_assign(group, v, field, dump, ...)	({					\
-+	if (group) {										\
-+		phase->v.phase_5g.field = cal->v.phase_5g.field;				\
-+		if (dump)									\
-+			dev_info(dev->mt76.dev, "%s = %d\n", #field, phase->v.phase_5g.field);	\
-+	} else {										\
-+		phase->v.phase_2g.field = cal->v.phase_5g.field;				\
-+		if (dump)									\
-+			dev_info(dev->mt76.dev, "%s = %d\n", #field, phase->v.phase_2g.field);	\
-+	}											\
-+})
-+
-+#define phase_assign_rx(group, v, rx, dump, ...)	({					\
-+	phase_assign(group, v, rx.rx_uh, dump);							\
-+	phase_assign(group, v, rx.rx_h, dump);							\
-+	phase_assign(group, v, rx.rx_m, dump);							\
-+	phase_assign(group, v, rx.rx_l, dump);							\
-+	phase_assign(group, v, rx.rx_ul, dump);							\
-+})
-+
-+#define phase_assign_rx_ext(group, v, rx, dump, ...)	({					\
-+	phase_assign(group, v, rx.rx_uh, dump);							\
-+	phase_assign(group, v, rx.rx_h, dump);							\
-+	phase_assign(group, v, rx.rx_mh, dump);							\
-+	phase_assign(group, v, rx.rx_m, dump);							\
-+	phase_assign(group, v, rx.rx_l, dump);							\
-+	phase_assign(group, v, rx.rx_ul, dump);							\
-+})
-+
-+#define phase_assign_rx_v1(group, v, rx, ...)	({						\
-+	if (group) {										\
-+		phase_assign(group, v, rx.rx_uh, true);						\
-+		phase_assign(group, v, rx.rx_h, true);						\
-+		phase->v.phase_5g.rx.rx_mh = cal->v.phase_5g.rx.rx_mh;				\
-+		dev_info(dev->mt76.dev, "%s.rx_mh = %d\n", #rx, phase->v.phase_5g.rx.rx_mh);	\
-+		phase_assign(group, v, rx.rx_m, true);						\
-+		phase_assign(group, v, rx.rx_l, true);						\
-+		phase_assign(group, v, rx.rx_ul, true);						\
-+	} else {										\
-+		phase_assign_rx(group, v, rx, true, ...);					\
-+	}											\
-+})
-+
-+#define GROUP_L		0
-+#define GROUP_M		1
-+#define GROUP_H		2
-+
-+struct mt7996_pfmu_data {
-+	__le16 subc_idx;
-+	__le16 phi11;
-+	__le16 phi21;
-+	__le16 phi31;
-+};
-+
-+struct mt7996_pfmu_data_5x5 {
-+	__le16 subc_idx;
-+	__le16 phi11;
-+	__le16 phi21;
-+	__le16 phi31;
-+	__le16 phi41;
-+};
-+
-+struct mt7996_ibf_cal_info {
-+	struct mt7996_mcu_bf_basic_event event;
-+
-+	u8 category_id;
-+	u8 group_l_m_n;
-+	u8 group;
-+	bool sx2;
-+	u8 status;
-+	u8 cal_type;
-+	u8 nsts;
-+	u8 version;
-+	union {
-+		struct {
-+			struct mt7996_txbf_phase_out phase_out;
-+			union {
-+				struct mt7996_txbf_phase_info_2g phase_2g;
-+				struct mt7996_txbf_phase_info_5g phase_5g;
-+			};
-+		} v1;
-+		struct {
-+			struct mt7992_txbf_phase_out phase_out;
-+			union {
-+				struct mt7992_txbf_phase_info_2g phase_2g;
-+				struct mt7992_txbf_phase_info_5g phase_5g;
-+			};
-+		} v2;
-+		u8 buf[64];
-+	};
-+} __packed;
-+
-+enum {
-+	IBF_PHASE_CAL_UNSPEC,
-+	IBF_PHASE_CAL_NORMAL,
-+	IBF_PHASE_CAL_VERIFY,
-+	IBF_PHASE_CAL_NORMAL_INSTRUMENT,
-+	IBF_PHASE_CAL_VERIFY_INSTRUMENT,
-+};
-+
-+enum ibf_version {
-+	IBF_VER_1,
-+	IBF_VER_2 = 3,
-+};
-+
-+static inline int get_ibf_version(struct mt7996_dev *dev)
-+{
-+	switch (mt76_chip(&dev->mt76)) {
-+	case 0x7990:
-+		return IBF_VER_1;
-+	case 0x7992:
-+	default:
-+		return IBF_VER_2;
-+	}
-+}
-+
-+#define MT7996_TXBF_SUBCAR_NUM		64
-+#define MT7996_TXBF_PFMU_DATA_LEN	(MT7996_TXBF_SUBCAR_NUM * sizeof(struct mt7996_pfmu_data))
-+#define MT7996_TXBF_PFMU_DATA_LEN_5X5	(MT7996_TXBF_SUBCAR_NUM * \
-+					 sizeof(struct mt7996_pfmu_data_5x5))
-+
- enum {
- 	UNI_EVENT_BF_PFMU_TAG = 0x5,
- 	UNI_EVENT_BF_PFMU_DATA = 0x7,
-@@ -408,11 +844,6 @@ enum {
- 	UNI_EVENT_BF_MAX_NUM
- };
- 
--enum {
--	UNI_CMD_MURU_FIXED_RATE_CTRL = 0x11,
--	UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
--};
--
- struct uni_muru_mum_set_group_tbl_entry {
- 	__le16 wlan_idx0;
- 	__le16 wlan_idx1;
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index 8ec1dc1..263737c 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -326,6 +326,9 @@ enum offs_rev {
- #define MT_ARB_SCR_TX_DISABLE			BIT(8)
- #define MT_ARB_SCR_RX_DISABLE			BIT(9)
- 
-+#define MT_ARB_TQSAXM0(_band)			MT_WF_ARB(_band, 0x180)
-+#define MT_ARB_TQSAXM_ALTX_START_MASK		GENMASK(12, 8)
-+
- /* RMAC: band 0(0x820e5000), band 1(0x820f5000), band 2(0x830e5000), */
- #define MT_WF_RMAC_BASE(_band)			__BASE(WF_RMAC_BASE, (_band))
- #define MT_WF_RMAC(_band, ofs)			(MT_WF_RMAC_BASE(_band) + (ofs))
-diff --git a/mt7996/testmode.c b/mt7996/testmode.c
-index 836211f..5cec1ee 100644
---- a/mt7996/testmode.c
-+++ b/mt7996/testmode.c
-@@ -23,6 +23,7 @@ enum {
- 	TM_CHANGED_IPI_THRESHOLD,
- 	TM_CHANGED_IPI_PERIOD,
- 	TM_CHANGED_IPI_RESET,
-+	TM_CHANGED_TXBF_ACT,
- 
- 	/* must be last */
- 	NUM_TM_CHANGED
-@@ -41,25 +42,31 @@ static const u8 tm_change_map[] = {
- 	[TM_CHANGED_IPI_THRESHOLD] = MT76_TM_ATTR_IPI_THRESHOLD,
- 	[TM_CHANGED_IPI_PERIOD] = MT76_TM_ATTR_IPI_PERIOD,
- 	[TM_CHANGED_IPI_RESET] = MT76_TM_ATTR_IPI_RESET,
-+	[TM_CHANGED_TXBF_ACT] = MT76_TM_ATTR_TXBF_ACT,
- };
- 
- static void mt7996_tm_ipi_work(struct work_struct *work);
-+static int mt7996_tm_txbf_apply_tx(struct mt7996_phy *phy, u16 wlan_idx,
-+				   bool ebf, bool ibf, bool phase_cal);
- 
- static u32 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method)
- {
- 	static const u32 width_to_bw[][NUM_BW_MAP] = {
--		[NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ, 40,
-+		[NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ, BF_CDBW_40MHZ, 40,
- 					   FIRST_CONTROL_CHAN_BITMAP_BW40},
--		[NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ, 80,
-+		[NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ, BF_CDBW_80MHZ, 80,
- 					   FIRST_CONTROL_CHAN_BITMAP_BW80},
--		[NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ, 80, 0x0},
--		[NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ, 160,
-+		[NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ, BF_CDBW_8080MHZ,
-+					      80, 0x0},
-+		[NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ, BF_CDBW_160MHZ, 160,
- 					    FIRST_CONTROL_CHAN_BITMAP_BW160},
--		[NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ, 5, 0x0},
--		[NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ, 10, 0x0},
--		[NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0},
--		[NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0},
--		[NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ, 320, 0x0},
-+		[NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ, BF_CDBW_5MHZ, 5, 0x0},
-+		[NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ, BF_CDBW_10MHZ, 10, 0x0},
-+		[NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, BF_CDBW_20MHZ, 20, 0x0},
-+		[NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, BF_CDBW_20MHZ,
-+						20, 0x0},
-+		[NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ, BF_CDBW_320MHZ,
-+					    320, 0x0},
- 	};
- 
- 	if (width >= ARRAY_SIZE(width_to_bw))
-@@ -68,26 +75,26 @@ static u32 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_m
- 	return width_to_bw[width][method];
- }
- 
--static u8 mt7996_tm_rate_to_phy(u8 tx_rate_mode)
-+static u8 mt7996_tm_rate_mapping(u8 tx_rate_mode, enum rate_mapping_type type)
- {
--	static const u8 rate_to_phy[] = {
--		[MT76_TM_TX_MODE_CCK] = MT_PHY_TYPE_CCK,
--		[MT76_TM_TX_MODE_OFDM] = MT_PHY_TYPE_OFDM,
--		[MT76_TM_TX_MODE_HT] = MT_PHY_TYPE_HT,
--		[MT76_TM_TX_MODE_VHT] = MT_PHY_TYPE_VHT,
--		[MT76_TM_TX_MODE_HE_SU] = MT_PHY_TYPE_HE_SU,
--		[MT76_TM_TX_MODE_HE_EXT_SU] = MT_PHY_TYPE_HE_EXT_SU,
--		[MT76_TM_TX_MODE_HE_TB] = MT_PHY_TYPE_HE_TB,
--		[MT76_TM_TX_MODE_HE_MU] = MT_PHY_TYPE_HE_MU,
--		[MT76_TM_TX_MODE_EHT_SU] = MT_PHY_TYPE_EHT_SU,
--		[MT76_TM_TX_MODE_EHT_TRIG] = MT_PHY_TYPE_EHT_TRIG,
--		[MT76_TM_TX_MODE_EHT_MU] = MT_PHY_TYPE_EHT_MU,
-+	static const u8 rate_to_phy[][NUM_RATE_MAP] = {
-+		[MT76_TM_TX_MODE_CCK] = {MT_PHY_TYPE_CCK, BF_LM_LEGACY},
-+		[MT76_TM_TX_MODE_OFDM] = {MT_PHY_TYPE_OFDM, BF_LM_LEGACY},
-+		[MT76_TM_TX_MODE_HT] = {MT_PHY_TYPE_HT, BF_LM_HT},
-+		[MT76_TM_TX_MODE_VHT] = {MT_PHY_TYPE_VHT, BF_LM_VHT},
-+		[MT76_TM_TX_MODE_HE_SU] = {MT_PHY_TYPE_HE_SU, BF_LM_HE},
-+		[MT76_TM_TX_MODE_HE_EXT_SU] = {MT_PHY_TYPE_HE_EXT_SU, BF_LM_HE},
-+		[MT76_TM_TX_MODE_HE_TB] = {MT_PHY_TYPE_HE_TB, BF_LM_HE},
-+		[MT76_TM_TX_MODE_HE_MU] = {MT_PHY_TYPE_HE_MU, BF_LM_HE},
-+		[MT76_TM_TX_MODE_EHT_SU] = {MT_PHY_TYPE_EHT_SU, BF_LM_EHT},
-+		[MT76_TM_TX_MODE_EHT_TRIG] = {MT_PHY_TYPE_EHT_TRIG, BF_LM_EHT},
-+		[MT76_TM_TX_MODE_EHT_MU] = {MT_PHY_TYPE_EHT_MU, BF_LM_EHT},
- 	};
- 
- 	if (tx_rate_mode > MT76_TM_TX_MODE_MAX)
- 		return -EINVAL;
- 
--	return rate_to_phy[tx_rate_mode];
-+	return rate_to_phy[tx_rate_mode][type];
- }
- 
- static int
-@@ -239,7 +246,7 @@ mt7996_tm_init(struct mt7996_phy *phy, bool en)
- 		INIT_DELAYED_WORK(&phy->ipi_work, mt7996_tm_ipi_work);
- }
- 
--static void
-+void
- mt7996_tm_update_channel(struct mt7996_phy *phy)
- {
- #define CHAN_FREQ_BW_80P80_TAG		(SET_ID(CHAN_FREQ) | BIT(16))
-@@ -303,7 +310,8 @@ mt7996_tm_set_tx_frames(struct mt7996_phy *phy, bool en)
- 		mt7996_tm_set(dev, SET_ID(MAC_HEADER), FRAME_CONTROL);
- 		mt7996_tm_set(dev, SET_ID(SEQ_CTRL), 0);
- 		mt7996_tm_set(dev, SET_ID(TX_COUNT), td->tx_count);
--		mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
-+		mt7996_tm_set(dev, SET_ID(TX_MODE),
-+			      mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY));
- 		mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
- 
- 		if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER))
-@@ -331,7 +339,8 @@ mt7996_tm_set_tx_frames(struct mt7996_phy *phy, bool en)
- 
- 		mt7996_tm_set(dev, SET_ID(MAX_PE), 2);
- 		mt7996_tm_set(dev, SET_ID(HW_TX_MODE), 0);
--		mt7996_tm_update_channel(phy);
-+		if (!td->bf_en)
-+			mt7996_tm_update_channel(phy);
- 
- 		/* trigger firmware to start TX */
- 		mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(START_TX));
-@@ -373,7 +382,8 @@ mt7996_tm_set_rx_frames(struct mt7996_phy *phy, bool en)
- 			return;
- 		}
- 
--		mt7996_tm_update_channel(phy);
-+		if (!td->bf_en)
-+			mt7996_tm_update_channel(phy);
- 
- 		if (td->tx_rate_mode >= MT76_TM_TX_MODE_HE_MU) {
- 			if (td->aid)
-@@ -381,7 +391,8 @@ mt7996_tm_set_rx_frames(struct mt7996_phy *phy, bool en)
- 			else
- 				ret = mt7996_tm_set(dev, SET_ID(RX_MU_AID), RX_MU_DISABLE);
- 		}
--		mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
-+		mt7996_tm_set(dev, SET_ID(TX_MODE),
-+			      mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY));
- 		mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi);
- 		mt7996_tm_set_antenna(phy, SET_ID(RX_PATH));
- 		mt7996_tm_set(dev, SET_ID(MAX_PE), 2);
-@@ -405,7 +416,8 @@ mt7996_tm_set_tx_cont(struct mt7996_phy *phy, bool en)
- 
- 	if (en) {
- 		mt7996_tm_update_channel(phy);
--		mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
-+		mt7996_tm_set(dev, SET_ID(TX_MODE),
-+			      mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY));
- 		mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
- 		/* fix payload is OFDM */
- 		mt7996_tm_set(dev, SET_ID(CONT_WAVE_MODE), CONT_WAVE_MODE_OFDM);
-@@ -1047,6 +1059,730 @@ mt7996_tm_set_ipi(struct mt7996_phy *phy)
- 	return 0;
- }
- 
-+static int
-+mt7996_tm_set_trx_mac(struct mt7996_phy *phy, u8 type, bool en)
-+{
-+#define UNI_TM_TRX_CTRL 0
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt7996_tm_trx_req req = {
-+		.param_num = 1,
-+		.tag = cpu_to_le16(UNI_TM_TRX_CTRL),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.param_idx = cpu_to_le16(TM_TRX_PARAM_SET_TRX),
-+		.band_idx = phy->mt76->band_idx,
-+		.testmode_en = 1,
-+		.action = TM_TRX_ACTION_SET,
-+		.set_trx = {
-+			.type = type,
-+			.enable = en,
-+			.band_idx = phy->mt76->band_idx,
-+		}
-+	};
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_TRX_PARAM),
-+				 &req, sizeof(req), false);
-+}
-+
-+static int
-+mt7996_tm_txbf_init(struct mt7996_phy *phy, u16 *val)
-+{
-+#define EBF_BBP_RX_OFFSET	0x10280
-+#define EBF_BBP_RX_ENABLE	(BIT(0) | BIT(15))
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt76_testmode_data *td = &phy->mt76->test;
-+	bool enable = val[0];
-+	void *phase_cal, *pfmu_data, *pfmu_tag;
-+	u8 nss, band_idx = phy->mt76->band_idx;
-+	enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20;
-+	u8 sub_addr = td->is_txbf_dut ? TXBF_DUT_MAC_SUBADDR : TXBF_GOLDEN_MAC_SUBADDR;
-+	u8 peer_addr = td->is_txbf_dut ? TXBF_GOLDEN_MAC_SUBADDR : TXBF_DUT_MAC_SUBADDR;
-+	u8 bss_addr = TXBF_DUT_MAC_SUBADDR;
-+	u8 addr[ETH_ALEN] = {0x00, sub_addr, sub_addr, sub_addr, sub_addr, sub_addr};
-+	u8 bssid[ETH_ALEN] = {0x00, bss_addr, bss_addr, bss_addr, bss_addr, bss_addr};
-+	u8 peer_addrs[ETH_ALEN] = {0x00, peer_addr, peer_addr, peer_addr, peer_addr, peer_addr};
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)phy->monitor_vif->drv_priv;
-+
-+	if (!enable) {
-+		td->bf_en = false;
-+		return 0;
-+	}
-+
-+	if (!dev->test.txbf_phase_cal) {
-+		phase_cal = devm_kzalloc(dev->mt76.dev,
-+					 sizeof(struct mt7996_txbf_phase) *
-+					 MAX_PHASE_GROUP_NUM,
-+					 GFP_KERNEL);
-+		if (!phase_cal)
-+			return -ENOMEM;
-+
-+		dev->test.txbf_phase_cal = phase_cal;
-+	}
-+
-+	if (!dev->test.txbf_pfmu_data) {
-+		/* allocate max size for 5x5 pfmu data */
-+		pfmu_data = devm_kzalloc(dev->mt76.dev,
-+					 MT7996_TXBF_PFMU_DATA_LEN_5X5,
-+					 GFP_KERNEL);
-+		if (!pfmu_data)
-+			return -ENOMEM;
-+
-+		dev->test.txbf_pfmu_data = pfmu_data;
-+	}
-+
-+	if (!dev->test.txbf_pfmu_tag) {
-+		pfmu_tag = devm_kzalloc(dev->mt76.dev,
-+					sizeof(struct mt7996_pfmu_tag), GFP_KERNEL);
-+		if (!pfmu_tag)
-+			return -ENOMEM;
-+
-+		dev->test.txbf_pfmu_tag = pfmu_tag;
-+	}
-+
-+	td->bf_en = true;
-+	dev->ibf = td->ibf;
-+	memcpy(td->addr[0], peer_addrs, ETH_ALEN);
-+	memcpy(td->addr[1], addr, ETH_ALEN);
-+	memcpy(td->addr[2], bssid, ETH_ALEN);
-+	memcpy(phy->monitor_vif->addr, addr, ETH_ALEN);
-+	mt7996_tm_set_mac_addr(dev, td->addr[0], SET_ID(DA));
-+	mt7996_tm_set_mac_addr(dev, td->addr[1], SET_ID(SA));
-+	mt7996_tm_set_mac_addr(dev, td->addr[2], SET_ID(BSSID));
-+
-+	/* bss idx & omac idx should be set to band idx for ibf cal */
-+	mvif->mt76.idx = band_idx;
-+	dev->mt76.vif_mask |= BIT_ULL(mvif->mt76.idx);
-+	mvif->mt76.omac_idx = band_idx;
-+	phy->omac_mask |= BIT_ULL(mvif->mt76.omac_idx);
-+
-+	mt7996_mcu_add_dev_info(phy, phy->monitor_vif, true);
-+	mt7996_mcu_add_bss_info(phy, phy->monitor_vif, true);
-+
-+	if (td->ibf) {
-+		if (td->is_txbf_dut) {
-+			/* Enable ITxBF Capability */
-+			mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
-+			mt7996_tm_set_trx_mac(phy, TM_TRX_MAC_TX, true);
-+
-+			td->tx_ipg = 999;
-+			td->tx_mpdu_len = 1024;
-+			td->tx_antenna_mask = phy->mt76->chainmask >> dev->chainshift[band_idx];
-+			nss = hweight8(td->tx_antenna_mask);
-+			if (nss > 1 && nss <= 4)
-+				td->tx_rate_idx = 15 + 8 * (nss - 2);
-+			else
-+				td->tx_rate_idx = 31;
-+		} else {
-+			td->tx_antenna_mask = 1;
-+			td->tx_mpdu_len = 1024;
-+			td->tx_rate_idx = 0;
-+			mt76_set(dev, EBF_BBP_RX_OFFSET, EBF_BBP_RX_ENABLE);
-+			dev_info(dev->mt76.dev, "Set BBP RX CR = %x\n",
-+				 mt76_rr(dev, EBF_BBP_RX_OFFSET));
-+		}
-+
-+		td->tx_rate_mode = MT76_TM_TX_MODE_HT;
-+		td->tx_rate_sgi = 0;
-+		/* 5T5R ibf */
-+		if (nss == 5) {
-+			td->tx_rate_mode = MT76_TM_TX_MODE_VHT;
-+			td->tx_rate_idx = 7;
-+		}
-+	} else {
-+		if (td->is_txbf_dut) {
-+			/* Enable ETxBF Capability */
-+			mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
-+			td->tx_antenna_mask = phy->mt76->chainmask >> dev->chainshift[band_idx];
-+			td->tx_spe_idx = 24 + phy->mt76->band_idx;
-+			if (td->tx_rate_mode == MT76_TM_TX_MODE_VHT ||
-+			    td->tx_rate_mode == MT76_TM_TX_MODE_HE_SU)
-+				mt7996_tm_set(dev, SET_ID(NSS), td->tx_rate_nss);
-+
-+			mt7996_tm_set(dev, SET_ID(ENCODE_MODE), td->tx_rate_ldpc);
-+			mt7996_tm_set(dev, SET_ID(TX_COUNT), td->tx_count);
-+		} else {
-+			/* Turn On BBP CR for RX */
-+			mt76_set(dev, EBF_BBP_RX_OFFSET, EBF_BBP_RX_ENABLE);
-+			dev_info(dev->mt76.dev, "Set BBP RX CR = %x\n",
-+				 mt76_rr(dev, EBF_BBP_RX_OFFSET));
-+
-+			td->tx_antenna_mask = 1;
-+		}
-+		width = phy->mt76->chandef.width;
-+
-+		if (td->tx_rate_mode == MT76_TM_TX_MODE_EHT_MU)
-+			td->tx_rate_mode = MT76_TM_TX_MODE_EHT_SU;
-+	}
-+	mt76_testmode_param_set(td, MT76_TM_ATTR_TX_ANTENNA);
-+
-+	mt7996_tm_set(dev, SET_ID(TX_MODE),
-+		      mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY));
-+	mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
-+	mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi);
-+	mt7996_tm_set(dev, SET_ID(CBW),
-+		      mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW));
-+	mt7996_tm_set(dev, SET_ID(DBW),
-+		      mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW));
-+	mt7996_tm_set_antenna(phy, SET_ID(TX_PATH));
-+	mt7996_tm_set_antenna(phy, SET_ID(RX_PATH));
-+	mt7996_tm_set(dev, SET_ID(IPG), td->tx_ipg);
-+	mt7996_tm_set(dev, SET_ID(TX_LEN), td->tx_mpdu_len);
-+	mt7996_tm_set(dev, SET_ID(TX_TIME), 0);
-+	mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(TX_COMMIT));
-+
-+	return 0;
-+}
-+
-+static inline void
-+mt7996_tm_txbf_phase_copy(struct mt7996_dev *dev, void *des, void *src, int group)
-+{
-+	int phase_size;
-+
-+	if (group && get_ibf_version(dev) == IBF_VER_1)
-+		phase_size = sizeof(struct mt7996_txbf_phase_info_5g);
-+	else if (get_ibf_version(dev) == IBF_VER_1)
-+		phase_size = sizeof(struct mt7996_txbf_phase_info_2g);
-+	else if (group)
-+		phase_size = sizeof(struct mt7992_txbf_phase_info_5g);
-+	else
-+		phase_size = sizeof(struct mt7992_txbf_phase_info_2g);
-+
-+	memcpy(des, src, phase_size);
-+}
-+
-+static int
-+mt7996_tm_txbf_phase_comp(struct mt7996_phy *phy, u16 *val)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt7996_tm_bf_req req = {
-+		.phase_comp = {
-+			.tag = cpu_to_le16(BF_IBF_PHASE_COMP),
-+			.len = cpu_to_le16(sizeof(req.phase_comp)),
-+			.bw = val[0],
-+			.jp_band = (val[2] == 1) ? 1 : 0,
-+			.band_idx = phy->mt76->band_idx,
-+			.read_from_e2p = val[3],
-+			.disable = val[4],
-+			.group = val[2],
-+		}
-+	};
-+	struct mt7996_txbf_phase *phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal;
-+	int group = val[2];
-+
-+	wait_event_timeout(dev->mt76.tx_wait, phase[group].status != 0, HZ);
-+	mt7996_tm_txbf_phase_copy(dev, req.phase_comp.buf, phase[group].buf, group);
-+
-+	pr_info("ibf cal process: phase comp info\n");
-+	print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1,
-+		       &req, sizeof(req), 0);
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req,
-+				 sizeof(req), false);
-+}
-+
-+static int
-+mt7996_tm_txbf_profile_tag_write(struct mt7996_phy *phy, u8 pfmu_idx, struct mt7996_pfmu_tag *tag)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt7996_tm_bf_req req = {
-+		.pfmu_tag = {
-+			.tag = cpu_to_le16(BF_PFMU_TAG_WRITE),
-+			.len = cpu_to_le16(sizeof(req.pfmu_tag)),
-+			.pfmu_id = pfmu_idx,
-+			.bfer = true,
-+			.band_idx = phy->mt76->band_idx,
-+		}
-+	};
-+
-+	memcpy(req.pfmu_tag.buf, tag, sizeof(*tag));
-+	wait_event_timeout(dev->mt76.tx_wait, tag->t1.pfmu_idx != 0, HZ);
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req,
-+				 sizeof(req), false);
-+}
-+
-+static int
-+mt7996_tm_add_txbf_sta(struct mt7996_phy *phy, u8 pfmu_idx, u8 nr, u8 nc, bool ebf)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt76_testmode_data *td = &phy->mt76->test;
-+	struct {
-+		struct sta_req_hdr hdr;
-+		struct sta_rec_bf bf;
-+	} __packed req = {
-+		.hdr = {
-+			.bss_idx = phy->mt76->band_idx,
-+			.wlan_idx_lo = to_wcid_lo(phy->mt76->band_idx + 1),
-+			.tlv_num = 1,
-+			.is_tlv_append = 1,
-+			.muar_idx = 0,
-+			.wlan_idx_hi = to_wcid_hi(phy->mt76->band_idx + 1),
-+		},
-+		.bf = {
-+			.tag = cpu_to_le16(STA_REC_BF),
-+			.len = cpu_to_le16(sizeof(req.bf)),
-+			.pfmu = cpu_to_le16(pfmu_idx),
-+			.sounding_phy = 1,
-+			.bf_cap = ebf,
-+			.ncol = nc,
-+			.nrow = nr,
-+			.ibf_timeout = 0xff,
-+			.tx_mode = mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY),
-+		},
-+	};
-+	u8 ndp_rate, ndpa_rate, rept_poll_rate, bf_bw;
-+
-+	if ((td->tx_rate_mode == MT76_TM_TX_MODE_HE_SU ||
-+	     td->tx_rate_mode == MT76_TM_TX_MODE_EHT_SU) && !td->ibf) {
-+		rept_poll_rate = 0x49;
-+		ndpa_rate = 0x49;
-+		ndp_rate = 0;
-+	} else if (td->tx_rate_mode == MT76_TM_TX_MODE_VHT && !td->ibf) {
-+		rept_poll_rate = 0x9;
-+		ndpa_rate = 0x9;
-+		ndp_rate = 0;
-+	} else {
-+		rept_poll_rate = 0;
-+		ndpa_rate = 0;
-+		if (nr == 1)
-+			ndp_rate = 8;
-+		else if (nr == 2)
-+			ndp_rate = 16;
-+		else if (nr == 4)
-+			ndp_rate = 32;
-+		else
-+			ndp_rate = 24;
-+
-+		/* 5T5R ebf profile for ibf cal */
-+		if (nr == 4 && td->ibf && ebf) {
-+			ndp_rate = 0;
-+			ndpa_rate = 11;
-+		}
-+	}
-+
-+	bf_bw = mt7996_tm_bw_mapping(phy->mt76->chandef.width, BW_MAP_NL_TO_BF);
-+	req.bf.ndp_rate = ndp_rate;
-+	req.bf.ndpa_rate = ndpa_rate;
-+	req.bf.rept_poll_rate = rept_poll_rate;
-+	req.bf.bw = bf_bw;
-+	req.bf.tx_mode = (td->tx_rate_mode == MT76_TM_TX_MODE_EHT_SU) ? 0xf : req.bf.tx_mode;
-+
-+	if (ebf) {
-+		req.bf.mem[0].row = 0;
-+		req.bf.mem[1].row = 1;
-+		req.bf.mem[2].row = 2;
-+		req.bf.mem[3].row = 3;
-+	} else {
-+		req.bf.mem[0].row = 4;
-+		req.bf.mem[1].row = 5;
-+		req.bf.mem[2].row = 6;
-+		req.bf.mem[3].row = 7;
-+	}
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), &req,
-+				 sizeof(req), true);
-+}
-+
-+static int
-+mt7996_tm_txbf_profile_update(struct mt7996_phy *phy, u16 *val, bool ebf)
-+{
-+#define MT_ARB_IBF_ENABLE			(BIT(0) | GENMASK(9, 8))
-+	struct mt76_testmode_data *td = &phy->mt76->test;
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt7996_pfmu_tag *tag = dev->test.txbf_pfmu_tag;
-+	u8 rate, pfmu_idx = val[0], nc = val[2], nr;
-+	int ret;
-+	bool is_atenl = val[5];
-+
-+	if (td->tx_antenna_mask == 3)
-+		nr = 1;
-+	else if (td->tx_antenna_mask == 7)
-+		nr = 2;
-+	else if (td->tx_antenna_mask == 31)
-+		nr = 4;
-+	else
-+		nr = 3;
-+
-+	memset(tag, 0, sizeof(*tag));
-+	tag->t1.pfmu_idx = pfmu_idx;
-+	tag->t1.ebf = ebf;
-+	tag->t1.nr = nr;
-+	tag->t1.nc = nc;
-+	tag->t1.invalid_prof = true;
-+	tag->t1.data_bw = mt7996_tm_bw_mapping(phy->mt76->chandef.width, BW_MAP_NL_TO_BF);
-+	tag->t2.se_idx = td->tx_spe_idx;
-+
-+	if (ebf) {
-+		tag->t1.row_id1 = 0;
-+		tag->t1.row_id2 = 1;
-+		tag->t1.row_id3 = 2;
-+		tag->t1.row_id4 = 3;
-+		tag->t1.lm = mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_LM);
-+	} else {
-+		tag->t1.row_id1 = 4;
-+		tag->t1.row_id2 = 5;
-+		tag->t1.row_id3 = 6;
-+		tag->t1.row_id4 = 7;
-+		rate = nr == 4 ? td->tx_rate_mode : MT76_TM_TX_MODE_OFDM;
-+		tag->t1.lm = mt7996_tm_rate_mapping(rate, RATE_MODE_TO_LM);
-+
-+		tag->t2.ibf_timeout = 0xff;
-+		tag->t2.ibf_nr = nr;
-+		tag->t2.ibf_nc = nc;
-+	}
-+
-+	ret = mt7996_tm_txbf_profile_tag_write(phy, pfmu_idx, tag);
-+	if (ret)
-+		return ret;
-+
-+	ret = mt7996_tm_add_txbf_sta(phy, pfmu_idx, nr, nc, ebf);
-+	if (ret)
-+		return ret;
-+
-+	if (!is_atenl && !td->ibf) {
-+		mt76_set(dev, MT_ARB_TQSAXM0(phy->mt76->band_idx), MT_ARB_TQSAXM_ALTX_START_MASK);
-+		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)));
-+	} else if (!is_atenl && td->ibf && ebf) {
-+		/* iBF's ebf profile update */
-+		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)));
-+	}
-+
-+	if (!ebf && is_atenl)
-+		return mt7996_tm_txbf_apply_tx(phy, 1, false, true, true);
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_tm_txbf_phase_cal(struct mt7996_phy *phy, u16 *val)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt7996_tm_bf_req req = {
-+		.phase_cal = {
-+			.tag = cpu_to_le16(BF_PHASE_CALIBRATION),
-+			.len = cpu_to_le16(sizeof(req.phase_cal)),
-+			.group = val[0],
-+			.group_l_m_n = val[1],
-+			.sx2 = val[2],
-+			.cal_type = val[3],
-+			.lna_gain_level = val[4],
-+			.band_idx = phy->mt76->band_idx,
-+			.version = val[5],
-+		},
-+	};
-+	struct mt7996_txbf_phase *phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal;
-+
-+	/* reset phase status before update phase cal data */
-+	phase[req.phase_cal.group].status = 0;
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req,
-+				 sizeof(req), false);
-+}
-+
-+static int
-+mt7996_tm_txbf_profile_update_all(struct mt7996_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];
-+	u16 angle21 = val[3];
-+	u16 angle31 = val[4];
-+	u16 angle41 = val[5];
-+	u16 angle51 = val[6];
-+	s16 phi11 = 0, phi21 = 0, phi31 = 0, phi41 = 0;
-+	s16 *pfmu_data;
-+	int offs = subc_id * sizeof(struct mt7996_pfmu_data) / sizeof(*pfmu_data);
-+
-+	if (subc_id > MT7996_TXBF_SUBCAR_NUM - 1)
-+		return -EINVAL;
-+
-+	if (nss == 2) {
-+		phi11 = (s16)(angle21 - angle11);
-+	} else if (nss == 3) {
-+		phi11 = (s16)(angle31 - angle11);
-+		phi21 = (s16)(angle31 - angle21);
-+	} else if (nss == 5) {
-+		phi11 = (s16)(angle51 - angle11);
-+		phi21 = (s16)(angle51 - angle21);
-+		phi31 = (s16)(angle51 - angle31);
-+		phi41 = (s16)(angle51 - angle41);
-+		offs = subc_id * sizeof(struct mt7996_pfmu_data_5x5) / sizeof(*pfmu_data);
-+	} else {
-+		phi11 = (s16)(angle41 - angle11);
-+		phi21 = (s16)(angle41 - angle21);
-+		phi31 = (s16)(angle41 - angle31);
-+	}
-+
-+	pfmu_data = (s16 *)phy->dev->test.txbf_pfmu_data;
-+	pfmu_data += offs;
-+
-+	if (subc_id < 32)
-+		pfmu_data[0] = cpu_to_le16(subc_id + 224);
-+	else
-+		pfmu_data[0] = cpu_to_le16(subc_id - 32);
-+
-+	pfmu_data[1] = cpu_to_le16(phi11);
-+	pfmu_data[2] = cpu_to_le16(phi21);
-+	pfmu_data[3] = cpu_to_le16(phi31);
-+	if (nss == 5)
-+		pfmu_data[4] = cpu_to_le16(phi41);
-+
-+	if (subc_id == MT7996_TXBF_SUBCAR_NUM - 1) {
-+		struct mt7996_dev *dev = phy->dev;
-+		struct mt7996_tm_bf_req req = {
-+			.pfmu_data_all = {
-+				.tag = cpu_to_le16(BF_PROFILE_WRITE_20M_ALL_5X5),
-+				.len = cpu_to_le16(sizeof(req.pfmu_data_all)),
-+				.pfmu_id = pfmu_idx,
-+				.band_idx = phy->mt76->band_idx,
-+			},
-+		};
-+		int size = MT7996_TXBF_PFMU_DATA_LEN_5X5;
-+
-+		if (nss != 5) {
-+			size = MT7996_TXBF_PFMU_DATA_LEN;
-+			req.pfmu_data_all.tag = cpu_to_le16(BF_PROFILE_WRITE_20M_ALL);
-+			req.pfmu_data_all.len = cpu_to_le16(sizeof(req.pfmu_data_all) -
-+							    MT7996_TXBF_PFMU_DATA_LEN_5X5 + size);
-+		}
-+		memcpy(req.pfmu_data_all.buf, dev->test.txbf_pfmu_data, size);
-+
-+		return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF),
-+					 &req, sizeof(req), true);
-+	}
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_tm_txbf_e2p_update(struct mt7996_phy *phy)
-+{
-+#define TXBF_PHASE_EEPROM_START_OFFSET		0xc00
-+#define TXBF_PHASE_GROUP_EEPROM_OFFSET_VER_1	46
-+#define TXBF_PHASE_G0_EEPROM_OFFSET_VER_2	sizeof(struct mt7992_txbf_phase_info_2g)
-+#define TXBF_PHASE_GX_EEPROM_OFFSET_VER_2	sizeof(struct mt7992_txbf_phase_info_5g)
-+	struct mt7996_txbf_phase *phase, *p;
-+	struct mt7996_dev *dev = phy->dev;
-+	u8 *eeprom = dev->mt76.eeprom.data;
-+	u16 offset;
-+	int i;
-+
-+	offset = TXBF_PHASE_EEPROM_START_OFFSET;
-+	phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal;
-+	for (i = 0; i < MAX_PHASE_GROUP_NUM; i++) {
-+		p = &phase[i];
-+
-+		if (!p->status)
-+			continue;
-+
-+		/* copy phase cal data to eeprom */
-+		mt7996_tm_txbf_phase_copy(dev, eeprom + offset, p->buf, i);
-+		if (get_ibf_version(dev) == IBF_VER_1)
-+			offset += TXBF_PHASE_GROUP_EEPROM_OFFSET_VER_1;
-+		else
-+			offset += i ? TXBF_PHASE_GX_EEPROM_OFFSET_VER_2 :
-+				      TXBF_PHASE_G0_EEPROM_OFFSET_VER_2;
-+	}
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_tm_txbf_apply_tx(struct mt7996_phy *phy, u16 wlan_idx, bool ebf,
-+			bool ibf, bool phase_cal)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt7996_tm_bf_req req = {
-+		.tx_apply = {
-+			.tag = cpu_to_le16(BF_DATA_PACKET_APPLY),
-+			.len = cpu_to_le16(sizeof(req.tx_apply)),
-+			.wlan_idx = cpu_to_le16(wlan_idx),
-+			.ebf = ebf,
-+			.ibf = ibf,
-+			.phase_cal = phase_cal,
-+		},
-+	};
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req, sizeof(req), false);
-+}
-+
-+static int
-+mt7996_tm_txbf_set_tx(struct mt7996_phy *phy, u16 *val)
-+{
-+	bool bf_on = val[0], update = val[3];
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt7996_pfmu_tag *tag = dev->test.txbf_pfmu_tag;
-+	struct mt76_testmode_data *td = &phy->mt76->test;
-+
-+	if (bf_on) {
-+		mt7996_tm_set_rx_frames(phy, false);
-+		mt7996_tm_set_tx_frames(phy, false);
-+		mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, 2, true);
-+		tag->t1.invalid_prof = false;
-+		mt7996_tm_txbf_profile_tag_write(phy, 2, tag);
-+		td->bf_ever_en = true;
-+
-+		if (update)
-+			mt7996_tm_txbf_apply_tx(phy, 1, 0, 1, 1);
-+	} else {
-+		if (!td->bf_ever_en) {
-+			mt7996_tm_set_rx_frames(phy, false);
-+			mt7996_tm_set_tx_frames(phy, false);
-+			td->ibf = false;
-+			td->ebf = false;
-+
-+			if (update)
-+				mt7996_tm_txbf_apply_tx(phy, 1, 0, 0, 0);
-+		} else {
-+			td->bf_ever_en = false;
-+
-+			mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, 2, true);
-+			tag->t1.invalid_prof = true;
-+			mt7996_tm_txbf_profile_tag_write(phy, 2, tag);
-+		}
-+	}
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_tm_trigger_sounding(struct mt7996_phy *phy, u16 *val, bool en)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	u8 sounding_mode = val[0];
-+	u8 sta_num = val[1];
-+	u32 sounding_interval = (u32)val[2] << 2;	/* input unit: 4ms */
-+	u16 tag = en ? BF_SOUNDING_ON : BF_SOUNDING_OFF;
-+	struct mt7996_tm_bf_req req = {
-+		.sounding = {
-+			.tag = cpu_to_le16(tag),
-+			.len = cpu_to_le16(sizeof(req.sounding)),
-+			.snd_mode = sounding_mode,
-+			.sta_num = sta_num,
-+			.wlan_id = {
-+				cpu_to_le16(val[3]),
-+				cpu_to_le16(val[4]),
-+				cpu_to_le16(val[5]),
-+				cpu_to_le16(val[6])
-+			},
-+			.snd_period = cpu_to_le32(sounding_interval),
-+		},
-+	};
-+
-+	if (sounding_mode > SOUNDING_MODE_MAX)
-+		return -EINVAL;
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF),
-+				 &req, sizeof(req), false);
-+}
-+
-+static int
-+mt7996_tm_txbf_txcmd(struct mt7996_phy *phy, u16 *val)
-+{
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt7996_tm_bf_req req = {
-+		.txcmd = {
-+			.tag = cpu_to_le16(BF_CMD_TXCMD),
-+			.len = cpu_to_le16(sizeof(req.txcmd)),
-+			.action = val[0],
-+			.bf_manual = val[1],
-+			.bf_bit = val[2],
-+		},
-+	};
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req, sizeof(req), false);
-+}
-+
-+static int
-+mt7996_tm_set_txbf(struct mt7996_phy *phy)
-+{
-+#define TXBF_IS_DUT_MASK	BIT(0)
-+#define TXBF_IBF_MASK		BIT(1)
-+	struct mt76_testmode_data *td = &phy->mt76->test;
-+	u16 *val = td->txbf_param;
-+
-+	dev_info(phy->dev->mt76.dev,
-+		 "ibf cal process: act = %u, val = %u, %u, %u, %u, %u, %u, %u, %u\n",
-+		 td->txbf_act, val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]);
-+
-+	switch (td->txbf_act) {
-+	case MT76_TM_TXBF_ACT_GOLDEN_INIT:
-+	case MT76_TM_TXBF_ACT_INIT:
-+	case MT76_TM_TX_EBF_ACT_GOLDEN_INIT:
-+	case MT76_TM_TX_EBF_ACT_INIT:
-+		td->ibf = !u32_get_bits(td->txbf_act, TXBF_IBF_MASK);
-+		td->ebf = true;
-+		td->is_txbf_dut = !!u32_get_bits(td->txbf_act, TXBF_IS_DUT_MASK);
-+		return mt7996_tm_txbf_init(phy, val);
-+	case MT76_TM_TXBF_ACT_UPDATE_CH:
-+		mt7996_tm_update_channel(phy);
-+		break;
-+	case MT76_TM_TXBF_ACT_PHASE_COMP:
-+		return mt7996_tm_txbf_phase_comp(phy, val);
-+	case MT76_TM_TXBF_ACT_TX_PREP:
-+		return mt7996_tm_txbf_set_tx(phy, val);
-+	case MT76_TM_TXBF_ACT_IBF_PROF_UPDATE:
-+		return mt7996_tm_txbf_profile_update(phy, val, false);
-+	case MT76_TM_TXBF_ACT_EBF_PROF_UPDATE:
-+		return mt7996_tm_txbf_profile_update(phy, val, true);
-+	case MT76_TM_TXBF_ACT_PHASE_CAL:
-+		return mt7996_tm_txbf_phase_cal(phy, val);
-+	case MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD:
-+	case MT76_TM_TXBF_ACT_PROF_UPDATE_ALL:
-+		return mt7996_tm_txbf_profile_update_all(phy, val);
-+	case MT76_TM_TXBF_ACT_E2P_UPDATE:
-+		return mt7996_tm_txbf_e2p_update(phy);
-+	case MT76_TM_TXBF_ACT_APPLY_TX: {
-+		u16 wlan_idx = val[0];
-+		bool ebf = !!val[1], ibf = !!val[2], phase_cal = !!val[4];
-+
-+		return mt7996_tm_txbf_apply_tx(phy, wlan_idx, ebf, ibf, phase_cal);
-+	}
-+	case MT76_TM_TXBF_ACT_TRIGGER_SOUNDING:
-+		return mt7996_tm_trigger_sounding(phy, val, true);
-+	case MT76_TM_TXBF_ACT_STOP_SOUNDING:
-+		memset(val, 0, sizeof(td->txbf_param));
-+		return mt7996_tm_trigger_sounding(phy, val, false);
-+	case MT76_TM_TXBF_ACT_PROFILE_TAG_READ:
-+	case MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE:
-+	case MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID: {
-+		u8 pfmu_idx = val[0];
-+		bool bfer = !!val[1];
-+		struct mt7996_dev *dev = phy->dev;
-+		struct mt7996_pfmu_tag *tag = dev->test.txbf_pfmu_tag;
-+
-+		if (!tag) {
-+			dev_err(dev->mt76.dev,
-+				"pfmu tag is not initialized!\n");
-+			return 0;
-+		}
-+
-+		if (td->txbf_act == MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE)
-+			return mt7996_tm_txbf_profile_tag_write(phy, pfmu_idx, tag);
-+		else if (td->txbf_act == MT76_TM_TXBF_ACT_PROFILE_TAG_READ)
-+			return mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, pfmu_idx, bfer);
-+
-+		tag->t1.invalid_prof = !!val[0];
-+
-+		return 0;
-+	}
-+	case MT76_TM_TXBF_ACT_STA_REC_READ:
-+		return mt7996_mcu_set_txbf_internal(phy, BF_STA_REC_READ, val[0], 0);
-+	case MT76_TM_TXBF_ACT_TXCMD:
-+		return mt7996_tm_txbf_txcmd(phy, val);
-+	default:
-+		break;
-+	};
-+
-+	return 0;
-+}
-+
- static void
- mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
- {
-@@ -1086,6 +1822,8 @@ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
- 		mt7996_tm_set_ipi(phy);
- 	if (changed & BIT(TM_CHANGED_IPI_RESET))
- 		mt7996_tm_ipi_hist_ctrl(phy, NULL, RDD_SET_IPI_HIST_RESET);
-+	if (changed & BIT(TM_CHANGED_TXBF_ACT))
-+		mt7996_tm_set_txbf(phy);
- }
- 
- static int
-diff --git a/mt7996/testmode.h b/mt7996/testmode.h
-index 78662b2..f97ccb2 100644
---- a/mt7996/testmode.h
-+++ b/mt7996/testmode.h
-@@ -27,6 +27,17 @@ enum {
- 	FW_CDBW_8080MHZ,
- };
- 
-+enum {
-+	BF_CDBW_20MHZ,
-+	BF_CDBW_40MHZ,
-+	BF_CDBW_80MHZ,
-+	BF_CDBW_160MHZ,
-+	BF_CDBW_320MHZ,
-+	BF_CDBW_10MHZ = BF_CDBW_320MHZ,
-+	BF_CDBW_5MHZ,
-+	BF_CDBW_8080MHZ,
-+};
-+
- #define FIRST_CONTROL_CHAN_BITMAP_BW40		0x5555555
- #define FIRST_CONTROL_CHAN_BITMAP_BW80		0x111111
- #define FIRST_CONTROL_CHAN_BITMAP_BW160		0x100101
-@@ -34,12 +45,20 @@ enum {
- enum bw_mapping_method {
- 	BW_MAP_NL_TO_FW,
- 	BW_MAP_NL_TO_TM,
-+	BW_MAP_NL_TO_BF,
- 	BW_MAP_NL_TO_MHZ,
- 	BW_MAP_NL_TO_CONTROL_BITMAP_5G,
- 
- 	NUM_BW_MAP,
- };
- 
-+enum rate_mapping_type {
-+	RATE_MODE_TO_PHY,
-+	RATE_MODE_TO_LM,
-+
-+	NUM_RATE_MAP,
-+};
-+
- struct tm_cal_param {
- 	__le32 func_data;
- 	u8 band_idx;
-diff --git a/testmode.c b/testmode.c
-index 69147f8..b9f7109 100644
---- a/testmode.c
-+++ b/testmode.c
-@@ -462,6 +462,44 @@ out:
- 	return err;
- }
- 
-+static int
-+mt76_testmode_txbf_profile_update_all_cmd(struct mt76_phy *phy, struct nlattr **tb, u32 state)
-+{
-+#define PARAM_UNIT	5
-+#define PARAM_UNIT_5X5	6
-+	static u8 pfmu_idx;
-+	struct mt76_testmode_data *td = &phy->test;
-+	struct mt76_dev *dev = phy->dev;
-+	struct nlattr *cur;
-+	u16 tmp_val[PARAM_UNIT_5X5], *val = td->txbf_param;
-+	int idx, rem, ret, i = 0;
-+	int param_len = td->tx_antenna_mask == 31 ? PARAM_UNIT_5X5 : PARAM_UNIT;
-+
-+	memset(td->txbf_param, 0, sizeof(td->txbf_param));
-+	nla_for_each_nested(cur, tb[MT76_TM_ATTR_TXBF_PARAM], rem) {
-+		if (nla_len(cur) != 2)
-+			return -EINVAL;
-+		idx = i % param_len;
-+		tmp_val[idx] = nla_get_u16(cur);
-+		if (idx == 1 && (tmp_val[idx] == 0xf0 || tmp_val[idx] == 0xff)) {
-+			pfmu_idx = tmp_val[0];
-+			return 0;
-+		}
-+		if (idx == param_len - 1) {
-+			val[0] = pfmu_idx;
-+			memcpy(val + 1, tmp_val, param_len * sizeof(u16));
-+			if (dev->test_ops->set_params) {
-+				ret = dev->test_ops->set_params(phy, tb, state);
-+				if (ret)
-+					return ret;
-+			}
-+		}
-+		i++;
-+	}
-+
-+	return 0;
-+}
-+
- int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 		      void *data, int len)
- {
-@@ -607,6 +645,30 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 		}
- 	}
- 
-+	if (tb[MT76_TM_ATTR_TXBF_ACT]) {
-+		struct nlattr *cur;
-+		int rem, idx = 0;
-+
-+		if (!tb[MT76_TM_ATTR_TXBF_PARAM] ||
-+		    mt76_tm_get_u8(tb[MT76_TM_ATTR_TXBF_ACT], &td->txbf_act,
-+				   0, MT76_TM_TXBF_ACT_MAX))
-+			goto out;
-+
-+		if (td->txbf_act == MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD) {
-+			err = mt76_testmode_txbf_profile_update_all_cmd(phy, tb, state);
-+			goto out;
-+		}
-+
-+		memset(td->txbf_param, 0, sizeof(td->txbf_param));
-+		nla_for_each_nested(cur, tb[MT76_TM_ATTR_TXBF_PARAM], rem) {
-+			if (nla_len(cur) != 2 ||
-+			    idx >= ARRAY_SIZE(td->txbf_param))
-+				goto out;
-+
-+			td->txbf_param[idx++] = nla_get_u16(cur);
-+		}
-+	}
-+
- 	if (dev->test_ops->set_params) {
- 		err = dev->test_ops->set_params(phy, tb, state);
- 		if (err)
-diff --git a/testmode.h b/testmode.h
-index 5d677f8..bda7624 100644
---- a/testmode.h
-+++ b/testmode.h
-@@ -286,6 +286,59 @@ enum mt76_testmode_eeprom_action {
- 	MT76_TM_EEPROM_ACTION_MAX = NUM_MT76_TM_EEPROM_ACTION - 1,
- };
- 
-+/**
-+ * enum mt76_testmode_txbf_act - txbf action
-+ *
-+ * @MT76_TM_TXBF_ACT_GOLDEN_INIT: init ibf setting for golden device
-+ * @MT76_TM_TXBF_ACT_INIT: init ibf setting for DUT
-+ * @MT76_TM_TX_EBF_ACT_GOLDEN_INIT: init ebf setting for golden device
-+ * @MT76_TM_TX_EBF_ACT_INIT: init ebf setting for DUT
-+ * @MT76_TM_TXBF_ACT_UPDATE_CH: update channel info
-+ * @MT76_TM_TXBF_ACT_PHASE_COMP: txbf phase compensation
-+ * @MT76_TM_TXBF_ACT_TX_PREP: TX preparation for txbf
-+ * @MT76_TM_TXBF_ACT_IBF_PROF_UPDATE: update ibf profile (pfmu tag, bf sta record)
-+ * @MT76_TM_TXBF_ACT_EBF_PROF_UPDATE: update ebf profile
-+ * @MT76_TM_TXBF_ACT_APPLY_TX: apply TX setting for txbf
-+ * @MT76_TM_TXBF_ACT_PHASE_CAL: perform txbf phase calibration
-+ * @MT76_TM_TXBF_ACT_PROF_UPDATE_ALL: update bf profile via instrument
-+ * @MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD: update bf profile via instrument
-+ * @MT76_TM_TXBF_ACT_E2P_UPDATE: write back txbf calibration result to eeprom
-+ * @MT76_TM_TXBF_ACT_TRIGGER_SOUNDING: trigger beamformer to send sounding packet
-+ * @MT76_TM_TXBF_ACT_STOP_SOUNDING: stop sending sounding packet
-+ * @MT76_TM_TXBF_ACT_PROFILE_TAG_READ: read pfmu tag
-+ * @MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE: update pfmu tag
-+ * @MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID: invalidate pfmu tag
-+ * @MT76_TM_TXBF_ACT_STA_REC_READ: read bf sta record
-+ * @MT76_TM_TXBF_ACT_TXCMD: configure txcmd bf bit manually
-+ */
-+enum mt76_testmode_txbf_act {
-+	MT76_TM_TXBF_ACT_GOLDEN_INIT,
-+	MT76_TM_TXBF_ACT_INIT,
-+	MT76_TM_TX_EBF_ACT_GOLDEN_INIT,
-+	MT76_TM_TX_EBF_ACT_INIT,
-+	MT76_TM_TXBF_ACT_UPDATE_CH,
-+	MT76_TM_TXBF_ACT_PHASE_COMP,
-+	MT76_TM_TXBF_ACT_TX_PREP,
-+	MT76_TM_TXBF_ACT_IBF_PROF_UPDATE,
-+	MT76_TM_TXBF_ACT_EBF_PROF_UPDATE,
-+	MT76_TM_TXBF_ACT_APPLY_TX,
-+	MT76_TM_TXBF_ACT_PHASE_CAL,
-+	MT76_TM_TXBF_ACT_PROF_UPDATE_ALL,
-+	MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD,
-+	MT76_TM_TXBF_ACT_E2P_UPDATE,
-+	MT76_TM_TXBF_ACT_TRIGGER_SOUNDING,
-+	MT76_TM_TXBF_ACT_STOP_SOUNDING,
-+	MT76_TM_TXBF_ACT_PROFILE_TAG_READ,
-+	MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE,
-+	MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID,
-+	MT76_TM_TXBF_ACT_STA_REC_READ,
-+	MT76_TM_TXBF_ACT_TXCMD,
-+
-+	/* keep last */
-+	NUM_MT76_TM_TXBF_ACT,
-+	MT76_TM_TXBF_ACT_MAX = NUM_MT76_TM_TXBF_ACT - 1,
-+};
-+
- extern const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS];
- 
- #endif
-diff --git a/tools/fields.c b/tools/fields.c
-index 77696ce..f793d1a 100644
---- a/tools/fields.c
-+++ b/tools/fields.c
-@@ -44,6 +44,30 @@ static const char * const testmode_offchan_bw[] = {
- 	[NL80211_CHAN_WIDTH_160] = "160",
- };
- 
-+static const char * const testmode_txbf_act[] = {
-+	[MT76_TM_TXBF_ACT_GOLDEN_INIT] = "golden_init",
-+	[MT76_TM_TXBF_ACT_INIT] = "init",
-+	[MT76_TM_TX_EBF_ACT_GOLDEN_INIT] = "ebf_golden_init",
-+	[MT76_TM_TX_EBF_ACT_INIT] = "ebf_init",
-+	[MT76_TM_TXBF_ACT_UPDATE_CH] = "update_ch",
-+	[MT76_TM_TXBF_ACT_PHASE_COMP] = "phase_comp",
-+	[MT76_TM_TXBF_ACT_TX_PREP] = "tx_prep",
-+	[MT76_TM_TXBF_ACT_IBF_PROF_UPDATE] = "ibf_prof_update",
-+	[MT76_TM_TXBF_ACT_EBF_PROF_UPDATE] = "ebf_prof_update",
-+	[MT76_TM_TXBF_ACT_APPLY_TX] = "apply_tx",
-+	[MT76_TM_TXBF_ACT_PHASE_CAL] = "phase_cal",
-+	[MT76_TM_TXBF_ACT_PROF_UPDATE_ALL] = "prof_update",
-+	[MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD] = "prof_update_all",
-+	[MT76_TM_TXBF_ACT_E2P_UPDATE] = "e2p_update",
-+	[MT76_TM_TXBF_ACT_TRIGGER_SOUNDING] = "trigger_sounding",
-+	[MT76_TM_TXBF_ACT_STOP_SOUNDING] = "stop_sounding",
-+	[MT76_TM_TXBF_ACT_PROFILE_TAG_READ] = "pfmu_tag_read",
-+	[MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE] = "pfmu_tag_write",
-+	[MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID] = "set_invalid_prof",
-+	[MT76_TM_TXBF_ACT_STA_REC_READ] = "sta_rec_read",
-+	[MT76_TM_TXBF_ACT_TXCMD] = "txcmd",
-+};
-+
- static void print_enum(const struct tm_field *field, struct nlattr *attr)
- {
- 	unsigned int i = nla_get_u8(attr);
-@@ -94,6 +118,17 @@ static void print_s8(const struct tm_field *field, struct nlattr *attr)
- 	printf("%d", (int8_t)nla_get_u8(attr));
- }
- 
-+static bool parse_u16_hex(const struct tm_field *field, int idx,
-+			  struct nl_msg *msg, const char *val)
-+{
-+	return !nla_put_u16(msg, idx, strtoul(val, NULL, 16));
-+}
-+
-+static void print_u16_hex(const struct tm_field *field, struct nlattr *attr)
-+{
-+	printf("%d", nla_get_u16(attr));
-+}
-+
- static bool parse_u32(const struct tm_field *field, int idx,
- 		      struct nl_msg *msg, const char *val)
- {
-@@ -399,6 +434,8 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
- 	FIELD(u8, AID, "aid"),
- 	FIELD(u8, RU_ALLOC, "ru_alloc"),
- 	FIELD(u8, RU_IDX, "ru_idx"),
-+	FIELD_ENUM(TXBF_ACT, "txbf_act", testmode_txbf_act),
-+	FIELD_ARRAY(u16_hex, TXBF_PARAM, "txbf_param"),
- 	FIELD(u8, OFF_CH_SCAN_CH, "offchan_ch"),
- 	FIELD(u8, OFF_CH_SCAN_CENTER_CH, "offchan_center_ch"),
- 	FIELD_ENUM(OFF_CH_SCAN_BW, "offchan_bw", testmode_offchan_bw),
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0046-mtk-mt76-mt7996-kite-default-1-pcie-setting.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0046-mtk-mt76-mt7996-kite-default-1-pcie-setting.patch
new file mode 100644
index 0000000..a42d9e3
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0046-mtk-mt76-mt7996-kite-default-1-pcie-setting.patch
@@ -0,0 +1,56 @@
+From 3557ff32b5eb1d0625c2e0427946629d631788a4 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 13 Jul 2023 16:36:36 +0800
+Subject: [PATCH 046/199] mtk: mt76: mt7996: kite default 1-pcie setting
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/pci.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/mt7996/pci.c b/mt7996/pci.c
+index 04056181..05830c01 100644
+--- a/mt7996/pci.c
++++ b/mt7996/pci.c
+@@ -11,6 +11,9 @@
+ #include "mac.h"
+ #include "../trace.h"
+ 
++static bool hif2_enable = false;
++module_param(hif2_enable, bool, 0644);
++
+ static LIST_HEAD(hif_list);
+ static DEFINE_SPINLOCK(hif_lock);
+ static u32 hif_idx;
+@@ -63,6 +66,9 @@ static struct mt7996_hif *mt7996_pci_init_hif2(struct pci_dev *pdev)
+ {
+ 	hif_idx++;
+ 
++	if (!hif2_enable)
++		return NULL;
++
+ 	if (!pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x7991, NULL) &&
+ 	    !pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x799a, NULL))
+ 		return NULL;
+@@ -77,6 +83,9 @@ static int mt7996_pci_hif2_probe(struct pci_dev *pdev)
+ {
+ 	struct mt7996_hif *hif;
+ 
++	if (!hif2_enable)
++		return 0;
++
+ 	hif = devm_kzalloc(&pdev->dev, sizeof(*hif), GFP_KERNEL);
+ 	if (!hif)
+ 		return -ENOMEM;
+@@ -101,6 +110,8 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ 	int irq, hif2_irq, ret;
+ 	struct mt76_dev *mdev;
+ 
++	hif2_enable |= (id->device == 0x7990 || id->device == 0x7991);
++
+ 	ret = pcim_enable_device(pdev);
+ 	if (ret)
+ 		return ret;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0046-mtk-wifi-mt76-mt7996-add-zwdfs-cert-mode.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0046-mtk-wifi-mt76-mt7996-add-zwdfs-cert-mode.patch
deleted file mode 100644
index 9088f6e..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0046-mtk-wifi-mt76-mt7996-add-zwdfs-cert-mode.patch
+++ /dev/null
@@ -1,221 +0,0 @@
-From f0bff585456e12c9b5e5b0e5cc33bf920d8cb6d0 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 22 Sep 2023 12:33:06 +0800
-Subject: [PATCH 046/116] mtk: wifi: mt76: mt7996: add zwdfs cert mode
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/mcu.c    | 44 ++++++++++++++++++++++++++++++++------------
- mt7996/mcu.h    | 14 ++++++++++++++
- mt7996/mt7996.h |  5 +++++
- mt7996/vendor.c | 37 +++++++++++++++++++++++++++++++++++++
- mt7996/vendor.h | 12 ++++++++++++
- 5 files changed, 100 insertions(+), 12 deletions(-)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 18d8f4d..d46c5ae 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -4496,18 +4496,7 @@ int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable)
- int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
- 		       u8 rx_sel, u8 val)
- {
--	struct {
--		u8 _rsv[4];
--
--		__le16 tag;
--		__le16 len;
--
--		u8 ctrl;
--		u8 rdd_idx;
--		u8 rdd_rx_sel;
--		u8 val;
--		u8 rsv[4];
--	} __packed req = {
-+	struct mt7996_rdd_ctrl req = {
- 		.tag = cpu_to_le16(UNI_RDD_CTRL_PARM),
- 		.len = cpu_to_le16(sizeof(req) - 4),
- 		.ctrl = cmd,
-@@ -4520,6 +4509,37 @@ int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
- 				 &req, sizeof(req), true);
- }
- 
-+int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev, bool disable_timer)
-+{
-+	struct mt7996_rdd_ctrl req = {
-+		.tag = cpu_to_le16(UNI_RDD_CTRL_PARM),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.ctrl = RDD_DISABLE_ZW_TIMER,
-+		.rdd_idx = MT_RX_SEL2,
-+		.disable_timer = disable_timer,
-+	};
-+
-+	if (!is_mt7996(&dev->mt76) ||
-+	    (mt76_get_field(dev, MT_PAD_GPIO, MT_PAD_GPIO_ADIE_COMB) % 2))
-+		return 0;
-+
-+	switch (dev->mt76.region) {
-+	case NL80211_DFS_ETSI:
-+		req.val = 0;
-+		break;
-+	case NL80211_DFS_JP:
-+		req.val = 2;
-+		break;
-+	case NL80211_DFS_FCC:
-+	default:
-+		req.val = 1;
-+		break;
-+	}
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL),
-+				 &req, sizeof(req), true);
-+}
-+
- int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
- 				     struct ieee80211_vif *vif,
- 				     struct ieee80211_sta *sta)
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 6631281..f5e91a8 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -119,6 +119,20 @@ struct mt7996_mcu_rdd_report {
- 	} hw_pulse[32];
- } __packed;
- 
-+struct mt7996_rdd_ctrl {
-+	u8 _rsv[4];
-+
-+	__le16 tag;
-+	__le16 len;
-+
-+	u8 ctrl;
-+	u8 rdd_idx;
-+	u8 rdd_rx_sel;
-+	u8 val;
-+	u8 disable_timer;
-+	u8 rsv[3];
-+} __packed;
-+
- struct mt7996_mcu_background_chain_ctrl {
- 	u8 _rsv[4];
- 
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 84fbd0f..7f7a622 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -523,8 +523,11 @@ enum mt7996_rdd_cmd {
- 	RDD_READ_PULSE,
- 	RDD_RESUME_BF,
- 	RDD_IRQ_OFF,
-+	RDD_DISABLE_ZW_TIMER,
- };
- 
-+#define RDD_ZW_TIMER_OFF	BIT(31)
-+
- static inline struct mt7996_phy *
- mt7996_hw_phy(struct ieee80211_hw *hw)
- {
-@@ -666,6 +669,8 @@ int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable);
- int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy);
- int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
- 		       u8 rx_sel, u8 val);
-+int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev,
-+					    bool disable_timer);
- int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy,
- 				     struct cfg80211_chan_def *chandef);
- int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx,
-diff --git a/mt7996/vendor.c b/mt7996/vendor.c
-index 7ab6447..ba00ef3 100644
---- a/mt7996/vendor.c
-+++ b/mt7996/vendor.c
-@@ -108,6 +108,11 @@ rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
- 	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
- };
- 
-+static const struct nla_policy
-+background_radar_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL] = {
-+	[MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE] = {.type = NLA_U8 },
-+};
-+
- struct mt7996_amnt_data {
- 	u8 idx;
- 	u8 addr[ETH_ALEN];
-@@ -916,6 +921,27 @@ static int mt7996_vendor_wireless_ctrl(struct wiphy *wiphy,
- 	return 0;
- }
- 
-+static int mt7996_vendor_background_radar_mode_ctrl(struct wiphy *wiphy,
-+						    struct wireless_dev *wdev,
-+						    const void *data,
-+						    int data_len)
-+{
-+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+	struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL];
-+	int err;
-+	u8 background_radar_mode;
-+
-+	err = nla_parse(tb, MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX, data, data_len,
-+			background_radar_ctrl_policy, NULL);
-+	if (err)
-+		return err;
-+
-+	background_radar_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE]);
-+
-+	return mt7996_mcu_rdd_background_disable_timer(dev, !!background_radar_mode);
-+}
-+
- static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- 	{
- 		.info = {
-@@ -1021,6 +1047,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- 		.policy = rfeature_ctrl_policy,
- 		.maxattr = MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX,
- 	},
-+	{
-+		.info = {
-+			.vendor_id = MTK_NL80211_VENDOR_ID,
-+			.subcmd = MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL,
-+		},
-+		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+			WIPHY_VENDOR_CMD_NEED_RUNNING,
-+		.doit = mt7996_vendor_background_radar_mode_ctrl,
-+		.policy = background_radar_ctrl_policy,
-+		.maxattr = MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX,
-+	},
- };
- 
- void mt7996_vendor_register(struct mt7996_phy *phy)
-diff --git a/mt7996/vendor.h b/mt7996/vendor.h
-index 2ee1339..7c812f9 100644
---- a/mt7996/vendor.h
-+++ b/mt7996/vendor.h
-@@ -14,6 +14,7 @@ enum mtk_nl80211_vendor_subcmds {
- 	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
- 	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
- 	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
-+	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
- 	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
- };
- 
-@@ -127,6 +128,17 @@ enum mtk_vendor_attr_wireless_dump {
- 		NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
- };
- 
-+enum mtk_vendor_attr_background_radar_ctrl {
-+	MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL,
-+	MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
-+};
-+
- enum bw_sig {
- 	BW_SIGNALING_DISABLE,
- 	BW_SIGNALING_STATIC,
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0047-mtk-mt76-mt7996-support-BF-MIMO-debug-commands.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0047-mtk-mt76-mt7996-support-BF-MIMO-debug-commands.patch
new file mode 100644
index 0000000..707dbde
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0047-mtk-mt76-mt7996-support-BF-MIMO-debug-commands.patch
@@ -0,0 +1,1088 @@
+From 8f94ecaccf64cd36ad869a3c4d6fcc56648c12a4 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Tue, 3 Jan 2023 09:42:07 +0800
+Subject: [PATCH 047/199] mtk: mt76: mt7996: support BF/MIMO debug commands
+
+This commit includes the following commands:
+1. starec_bf_read
+2. txbf_snd_info: start/stop sounding and set sounding period
+3. fbkRptInfo
+4. fix muru rate
+
+fix the wrong wlan_idx for user3
+
+Align the format of mcu event mt7996_mcu_bf_starec_read with
+firmware definition.
+
+Fw gerrit change:
+https://gerrit.mediatek.inc/c/neptune/firmware/bora/wifi/core/+/8218143
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/mcu.c         |   5 +
+ mt7996/mcu.h         |   4 +
+ mt7996/mt7996.h      |   5 +
+ mt7996/mtk_debugfs.c |   9 +
+ mt7996/mtk_mcu.c     | 624 +++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mtk_mcu.h     | 342 ++++++++++++++++++++++++
+ 6 files changed, 989 insertions(+)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 49a55bd3..c47dee02 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -744,6 +744,11 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	case MCU_UNI_EVENT_TESTMODE_CTRL:
+ 		mt7996_tm_rf_test_event(dev, skb);
+ 		break;
++#endif
++#if defined CONFIG_NL80211_TESTMODE || defined CONFIG_MTK_DEBUG
++	case MCU_UNI_EVENT_BF:
++		mt7996_mcu_rx_bf_event(dev, skb);
++		break;
+ #endif
+ 	default:
+ 		break;
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 3e9364de..8a718513 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -770,8 +770,12 @@ enum {
+ 
+ enum {
+ 	BF_SOUNDING_ON = 1,
++	BF_PFMU_TAG_READ = 5,
++	BF_STA_REC_READ = 11,
+ 	BF_HW_EN_UPDATE = 17,
+ 	BF_MOD_EN_CTRL = 20,
++	BF_FBRPT_DBG_INFO_READ = 23,
++	BF_TXSND_INFO = 24,
+ };
+ 
+ enum {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 47a316e1..8935ef22 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -816,6 +816,11 @@ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
+ int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set);
+ void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev);
++int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx);
++void mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb);
++int mt7996_mcu_set_muru_fixed_rate_enable(struct mt7996_dev *dev, u8 action, int val);
++int mt7996_mcu_set_muru_fixed_rate_parameter(struct mt7996_dev *dev, u8 action, void *para);
++int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para);
+ #endif
+ 
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 06dc794e..b3cc8119 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2997,6 +2997,15 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ 	debugfs_create_file("sr_stats", 0400, dir, phy, &mt7996_sr_stats_fops);
+ 	debugfs_create_file("sr_scene_cond", 0400, dir, phy, &mt7996_sr_scene_cond_fops);
+ 
++	debugfs_create_file("muru_fixed_rate_enable", 0600, dir, dev,
++			    &fops_muru_fixed_rate_enable);
++	debugfs_create_file("muru_fixed_group_rate", 0600, dir, dev,
++			    &fops_muru_fixed_group_rate);
++	debugfs_create_file("bf_txsnd_info", 0600, dir, phy, &fops_bf_txsnd_info);
++	debugfs_create_file("bf_starec_read", 0600, dir, phy, &fops_starec_bf_read);
++	debugfs_create_file("bf_fbk_rpt", 0600, dir, phy, &fops_bf_fbk_rpt);
++	debugfs_create_file("pfmu_tag_read", 0600, dir, phy, &fops_bf_pfmu_tag_read);
++
+ 	return 0;
+ }
+ 
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index ea4e5bf2..6b2cdad6 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -280,4 +280,628 @@ int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev)
+ 	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(CHIP_CONFIG), &req,
+ 				 sizeof(req), true);
+ }
++
++static struct tlv *
++__mt7996_mcu_add_uni_tlv(struct sk_buff *skb, u16 tag, u16 len)
++{
++	struct tlv *ptlv, tlv = {
++		.tag = cpu_to_le16(tag),
++		.len = cpu_to_le16(len),
++	};
++
++	ptlv = skb_put(skb, len);
++	memcpy(ptlv, &tlv, sizeof(tlv));
++
++	return ptlv;
++}
++
++int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx)
++{
++	struct mt7996_dev *dev = phy->dev;
++#define MT7996_MTK_BF_MAX_SIZE	sizeof(struct bf_starec_read)
++	struct uni_header hdr;
++	struct sk_buff *skb;
++	struct tlv *tlv;
++	int len = sizeof(hdr) + MT7996_MTK_BF_MAX_SIZE;
++
++	memset(&hdr, 0, sizeof(hdr));
++
++	skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len);
++	if (!skb)
++		return -ENOMEM;
++
++	skb_put_data(skb, &hdr, sizeof(hdr));
++
++	switch (action) {
++	case BF_PFMU_TAG_READ: {
++		struct bf_pfmu_tag *req;
++
++		tlv = __mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req));
++		req = (struct bf_pfmu_tag *)tlv;
++#define BFER 1
++		req->pfmu_id = idx;
++		req->bfer = BFER;
++		req->band_idx = phy->mt76->band_idx;
++		break;
++	}
++	case BF_STA_REC_READ: {
++		struct bf_starec_read *req;
++
++		tlv = __mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req));
++		req = (struct bf_starec_read *)tlv;
++		req->wlan_idx = idx;
++		break;
++	}
++	case BF_FBRPT_DBG_INFO_READ: {
++		struct bf_fbk_rpt_info *req;
++
++		if (idx != 0) {
++			dev_info(dev->mt76.dev, "Invalid input");
++			return 0;
++		}
++
++		tlv = __mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req));
++		req = (struct bf_fbk_rpt_info *)tlv;
++		req->action = idx;
++		req->band_idx = phy->mt76->band_idx;
++		break;
++	}
++	default:
++		return -EINVAL;
++	}
++
++	return mt76_mcu_skb_send_msg(&phy->dev->mt76, skb, MCU_WM_UNI_CMD(BF), false);
++}
++
++int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para)
++{
++	char *buf = (char *)para;
++	__le16 input[5] = {0};
++	u8 recv_arg = 0;
++	struct bf_txsnd_info *req;
++	struct uni_header hdr;
++	struct sk_buff *skb;
++	struct tlv *tlv;
++	int len = sizeof(hdr) + MT7996_MTK_BF_MAX_SIZE;
++
++	memset(&hdr, 0, sizeof(hdr));
++
++	skb = mt76_mcu_msg_alloc(&phy->dev->mt76, NULL, len);
++	if (!skb)
++		return -ENOMEM;
++
++	skb_put_data(skb, &hdr, sizeof(hdr));
++
++	recv_arg = sscanf(buf, "%hx:%hx:%hx:%hx:%hx", &input[0], &input[1], &input[2],
++						      &input[3], &input[4]);
++
++	if (!recv_arg)
++		return -EINVAL;
++
++	tlv = __mt7996_mcu_add_uni_tlv(skb, BF_TXSND_INFO, sizeof(*req));
++	req = (struct bf_txsnd_info *)tlv;
++	req->action = input[0];
++
++	switch (req->action) {
++	case BF_SND_READ_INFO: {
++		req->read_clr = input[1];
++		break;
++	}
++	case BF_SND_CFG_OPT: {
++		req->vht_opt = input[1];
++		req->he_opt = input[2];
++		req->glo_opt = input[3];
++		break;
++	}
++	case BF_SND_CFG_INTV: {
++		req->wlan_idx = input[1];
++		req->snd_intv = input[2];
++		break;
++	}
++	case BF_SND_STA_STOP: {
++		req->wlan_idx = input[1];
++		req->snd_stop = input[2];
++		break;
++	}
++	case BF_SND_CFG_MAX_STA: {
++		req->max_snd_stas = input[1];
++		break;
++	}
++	case BF_SND_CFG_BFRP: {
++		req->man = input[1];
++		req->tx_time = input[2];
++		req->mcs = input[3];
++		req->ldpc = input[4];
++		break;
++	}
++	case BF_SND_CFG_INF: {
++		req->inf = input[1];
++		break;
++	}
++	case BF_SND_CFG_TXOP_SND: {
++		req->man = input[1];
++		req->ac_queue = input[2];
++		req->sxn_protect = input[3];
++		req->direct_fbk = input[4];
++		break;
++	}
++	default:
++		return -EINVAL;
++	}
++
++	return mt76_mcu_skb_send_msg(&phy->dev->mt76, skb, MCU_WM_UNI_CMD(BF), false);
++}
++
++void
++mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++#define HE_MODE 3
++	struct mt7996_mcu_bf_basic_event *event;
++
++	event = (struct mt7996_mcu_bf_basic_event *)skb->data;
++
++	dev_info(dev->mt76.dev, " bf_event tag = %d\n", event->tag);
++
++	switch (event->tag) {
++	case UNI_EVENT_BF_PFMU_TAG: {
++
++		struct mt7996_pfmu_tag_event *tag;
++		u32 *raw_t1, *raw_t2;
++
++		tag = (struct mt7996_pfmu_tag_event *) skb->data;
++
++		raw_t1 = (u32 *)&tag->t1;
++		raw_t2 = (u32 *)&tag->t2;
++
++		dev_info(dev->mt76.dev, "=================== TXBf Profile Tag1 Info ==================\n");
++		dev_info(dev->mt76.dev,
++			 "DW0 = 0x%08x, DW1 = 0x%08x, DW2 = 0x%08x\n",
++			 raw_t1[0], raw_t1[1], raw_t1[2]);
++		dev_info(dev->mt76.dev,
++			 "DW4 = 0x%08x, DW5 = 0x%08x, DW6 = 0x%08x\n\n",
++			 raw_t1[3], raw_t1[4], raw_t1[5]);
++		dev_info(dev->mt76.dev, "PFMU ID = %d              Invalid status = %d\n",
++			 tag->t1.pfmu_idx, tag->t1.invalid_prof);
++		dev_info(dev->mt76.dev, "iBf/eBf = %d\n\n", tag->t1.ebf);
++		dev_info(dev->mt76.dev, "DBW   = %d\n", tag->t1.data_bw);
++		dev_info(dev->mt76.dev, "SU/MU = %d\n", tag->t1.is_mu);
++		dev_info(dev->mt76.dev,
++			 "nrow = %d, ncol = %d, ng = %d, LM = %d, CodeBook = %d MobCalEn = %d\n",
++			 tag->t1.nr, tag->t1.nc, tag->t1.ngroup, tag->t1.lm, tag->t1.codebook,
++			 tag->t1.mob_cal_en);
++
++		if (tag->t1.lm <= HE_MODE) {
++			dev_info(dev->mt76.dev, "RU start = %d, RU end = %d\n",
++				 tag->t1.field.ru_start_id, tag->t1.field.ru_end_id);
++		} else {
++			dev_info(dev->mt76.dev, "PartialBW = %d\n",
++				 tag->t1.bw_info.partial_bw_info);
++		}
++
++		dev_info(dev->mt76.dev, "Mem Col1 = %d, Mem Row1 = %d, Mem Col2 = %d, Mem Row2 = %d\n",
++			 tag->t1.col_id1, tag->t1.row_id1, tag->t1.col_id2, tag->t1.row_id2);
++		dev_info(dev->mt76.dev, "Mem Col3 = %d, Mem Row3 = %d, Mem Col4 = %d, Mem Row4 = %d\n\n",
++			 tag->t1.col_id3, tag->t1.row_id3, tag->t1.col_id4, tag->t1.row_id4);
++		dev_info(dev->mt76.dev,
++			 "STS0_SNR = 0x%02x, STS1_SNR = 0x%02x, STS2_SNR = 0x%02x, STS3_SNR = 0x%02x\n",
++			 tag->t1.snr_sts0, tag->t1.snr_sts1, tag->t1.snr_sts2, tag->t1.snr_sts3);
++		dev_info(dev->mt76.dev,
++			 "STS4_SNR = 0x%02x, STS5_SNR = 0x%02x, STS6_SNR = 0x%02x, STS7_SNR = 0x%02x\n",
++			 tag->t1.snr_sts4, tag->t1.snr_sts5, tag->t1.snr_sts6, tag->t1.snr_sts7);
++		dev_info(dev->mt76.dev, "=============================================================\n");
++
++		dev_info(dev->mt76.dev, "=================== TXBf Profile Tag2 Info ==================\n");
++		dev_info(dev->mt76.dev,
++			 "DW0 = 0x%08x, DW1 = 0x%08x, DW2 = 0x%08x\n",
++			 raw_t2[0], raw_t2[1], raw_t2[2]);
++		dev_info(dev->mt76.dev,
++			 "DW3 = 0x%08x, DW4 = 0x%08x, DW5 = 0x%08x\n\n",
++			 raw_t2[3], raw_t2[4], raw_t2[5]);
++		dev_info(dev->mt76.dev, "Smart antenna ID = 0x%x,  SE index = %d\n",
++			 tag->t2.smart_ant, tag->t2.se_idx);
++		dev_info(dev->mt76.dev, "Timeout = 0x%x\n", tag->t2.ibf_timeout);
++		dev_info(dev->mt76.dev, "Desired BW = %d, Desired Ncol = %d, Desired Nrow = %d\n",
++			 tag->t2.ibf_data_bw, tag->t2.ibf_nc, tag->t2.ibf_nr);
++		dev_info(dev->mt76.dev, "Desired RU Allocation = %d\n", tag->t2.ibf_ru);
++		dev_info(dev->mt76.dev, "Mobility DeltaT = %d, Mobility LQ = %d\n",
++			 tag->t2.mob_delta_t, tag->t2.mob_lq_result);
++		dev_info(dev->mt76.dev, "=============================================================\n");
++		break;
++	}
++	case UNI_EVENT_BF_STAREC: {
++
++		struct mt7996_mcu_bf_starec_read *r;
++
++		r = (struct mt7996_mcu_bf_starec_read *)skb->data;
++		dev_info(dev->mt76.dev, "=================== BF StaRec ===================\n"
++					"rStaRecBf.u2PfmuId      = %d\n"
++					"rStaRecBf.fgSU_MU       = %d\n"
++					"rStaRecBf.u1TxBfCap     = %d\n"
++					"rStaRecBf.ucSoundingPhy = %d\n"
++					"rStaRecBf.ucNdpaRate    = %d\n"
++					"rStaRecBf.ucNdpRate     = %d\n"
++					"rStaRecBf.ucReptPollRate= %d\n"
++					"rStaRecBf.ucTxMode      = %d\n"
++					"rStaRecBf.ucNc          = %d\n"
++					"rStaRecBf.ucNr          = %d\n"
++					"rStaRecBf.ucCBW         = %d\n"
++					"rStaRecBf.ucMemRequire20M = %d\n"
++					"rStaRecBf.ucMemRow0     = %d\n"
++					"rStaRecBf.ucMemCol0     = %d\n"
++					"rStaRecBf.ucMemRow1     = %d\n"
++					"rStaRecBf.ucMemCol1     = %d\n"
++					"rStaRecBf.ucMemRow2     = %d\n"
++					"rStaRecBf.ucMemCol2     = %d\n"
++					"rStaRecBf.ucMemRow3     = %d\n"
++					"rStaRecBf.ucMemCol3     = %d\n",
++					r->pfmu_id,
++					r->is_su_mu,
++					r->txbf_cap,
++					r->sounding_phy,
++					r->ndpa_rate,
++					r->ndp_rate,
++					r->rpt_poll_rate,
++					r->tx_mode,
++					r->nc,
++					r->nr,
++					r->bw,
++					r->mem_require_20m,
++					r->mem_row0,
++					r->mem_col0,
++					r->mem_row1,
++					r->mem_col1,
++					r->mem_row2,
++					r->mem_col2,
++					r->mem_row3,
++					r->mem_col3);
++
++		dev_info(dev->mt76.dev, "rStaRecBf.u2SmartAnt    = 0x%x\n"
++					"rStaRecBf.ucSEIdx       = %d\n"
++					"rStaRecBf.uciBfTimeOut  = 0x%x\n"
++					"rStaRecBf.uciBfDBW      = %d\n"
++					"rStaRecBf.uciBfNcol     = %d\n"
++					"rStaRecBf.uciBfNrow     = %d\n"
++					"rStaRecBf.nr_bw160      = %d\n"
++					"rStaRecBf.nc_bw160 	  = %d\n"
++					"rStaRecBf.ru_start_idx  = %d\n"
++					"rStaRecBf.ru_end_idx 	  = %d\n"
++					"rStaRecBf.trigger_su 	  = %d\n"
++					"rStaRecBf.trigger_mu 	  = %d\n"
++					"rStaRecBf.ng16_su 	  = %d\n"
++					"rStaRecBf.ng16_mu 	  = %d\n"
++					"rStaRecBf.codebook42_su = %d\n"
++					"rStaRecBf.codebook75_mu = %d\n"
++					"rStaRecBf.he_ltf 	      = %d\n"
++					"======================================\n",
++					r->smart_ant,
++					r->se_idx,
++					r->bf_timeout,
++					r->bf_dbw,
++					r->bf_ncol,
++					r->bf_nrow,
++					r->nr_lt_bw80,
++					r->nc_lt_bw80,
++					r->ru_start_idx,
++					r->ru_end_idx,
++					r->trigger_su,
++					r->trigger_mu,
++					r->ng16_su,
++					r->ng16_mu,
++					r->codebook42_su,
++					r->codebook75_mu,
++					r->he_ltf);
++		break;
++	}
++	case UNI_EVENT_BF_FBK_INFO: {
++		struct mt7996_mcu_txbf_fbk_info *info;
++		__le32 total, i;
++
++		info = (struct mt7996_mcu_txbf_fbk_info *)skb->data;
++
++		total = info->u4PFMUWRDoneCnt + info->u4PFMUWRFailCnt;
++		total += info->u4PFMUWRTimeoutFreeCnt + info->u4FbRptPktDropCnt;
++
++		dev_info(dev->mt76.dev, "\n");
++		dev_info(dev->mt76.dev, "\x1b[32m =================================\x1b[m\n");
++		dev_info(dev->mt76.dev, "\x1b[32m PFMUWRDoneCnt              = %u\x1b[m\n",
++			info->u4PFMUWRDoneCnt);
++		dev_info(dev->mt76.dev, "\x1b[32m PFMUWRFailCnt              = %u\x1b[m\n",
++			info->u4PFMUWRFailCnt);
++		dev_info(dev->mt76.dev, "\x1b[32m PFMUWRTimeOutCnt           = %u\x1b[m\n",
++			info->u4PFMUWRTimeOutCnt);
++		dev_info(dev->mt76.dev, "\x1b[32m PFMUWRTimeoutFreeCnt       = %u\x1b[m\n",
++			info->u4PFMUWRTimeoutFreeCnt);
++		dev_info(dev->mt76.dev, "\x1b[32m FbRptPktDropCnt            = %u\x1b[m\n",
++			info->u4FbRptPktDropCnt);
++		dev_info(dev->mt76.dev, "\x1b[32m TotalFbRptPkt              = %u\x1b[m\n", total);
++		dev_info(dev->mt76.dev, "\x1b[32m PollPFMUIntrStatTimeOut    = %u(micro-sec)\x1b[m\n",
++			info->u4PollPFMUIntrStatTimeOut);
++		dev_info(dev->mt76.dev, "\x1b[32m FbRptDeQInterval           = %u(milli-sec)\x1b[m\n",
++			info->u4DeQInterval);
++		dev_info(dev->mt76.dev, "\x1b[32m PktCntInFbRptTimeOutQ      = %u\x1b[m\n",
++			info->u4RptPktTimeOutListNum);
++		dev_info(dev->mt76.dev, "\x1b[32m PktCntInFbRptQ             = %u\x1b[m\n",
++			info->u4RptPktListNum);
++
++		// [ToDo] Check if it is valid entry
++		for (i = 0; ((i < 5) && (i < CFG_BF_STA_REC_NUM)); i++) {
++
++			// [ToDo] AID needs to be refined
++			dev_info(dev->mt76.dev,"\x1b[32m AID%u  RxFbRptCnt           = %u\x1b[m\n"
++				, i, info->au4RxPerStaFbRptCnt[i]);
++		}
++
++		break;
++	}
++	case UNI_EVENT_BF_TXSND_INFO: {
++		struct mt7996_mcu_tx_snd_info *info;
++		struct uni_event_bf_txsnd_sta_info *snd_sta_info;
++		int Idx;
++		int max_wtbl_size = mt7996_wtbl_size(dev);
++
++		info = (struct mt7996_mcu_tx_snd_info *)skb->data;
++		dev_info(dev->mt76.dev, "=================== Global Setting ===================\n");
++
++		dev_info(dev->mt76.dev, "VhtOpt = 0x%02X, HeOpt = 0x%02X, GloOpt = 0x%02X\n",
++			info->vht_opt, info->he_opt, info->glo_opt);
++
++		for (Idx = 0; Idx < BF_SND_CTRL_STA_DWORD_CNT; Idx++) {
++			dev_info(dev->mt76.dev, "SuSta[%d] = 0x%08X,", Idx,
++				 info->snd_rec_su_sta[Idx]);
++			if ((Idx & 0x03) == 0x03)
++				dev_info(dev->mt76.dev, "\n");
++		}
++
++		if ((Idx & 0x03) != 0x03)
++			dev_info(dev->mt76.dev, "\n");
++
++
++		for (Idx = 0; Idx < BF_SND_CTRL_STA_DWORD_CNT; Idx++) {
++			dev_info(dev->mt76.dev, "VhtMuSta[%d] = 0x%08X,", Idx, info->snd_rec_vht_mu_sta[Idx]);
++			if ((Idx & 0x03) == 0x03)
++				dev_info(dev->mt76.dev, "\n");
++		}
++
++		if ((Idx & 0x03) != 0x03)
++			dev_info(dev->mt76.dev, "\n");
++
++		for (Idx = 0; Idx < BF_SND_CTRL_STA_DWORD_CNT; Idx++) {
++			dev_info(dev->mt76.dev, "HeTBSta[%d] = 0x%08X,", Idx, info->snd_rec_he_tb_sta[Idx]);
++			if ((Idx & 0x03) == 0x03)
++				dev_info(dev->mt76.dev, "\n");
++		}
++
++		if ((Idx & 0x03) != 0x03)
++			dev_info(dev->mt76.dev, "\n");
++
++		for (Idx = 0; Idx < BF_SND_CTRL_STA_DWORD_CNT; Idx++) {
++			dev_info(dev->mt76.dev, "EhtTBSta[%d] = 0x%08X,", Idx, info->snd_rec_eht_tb_sta[Idx]);
++			if ((Idx & 0x03) == 0x03)
++				dev_info(dev->mt76.dev, "\n");
++		}
++
++		if ((Idx & 0x03) != 0x03)
++			dev_info(dev->mt76.dev, "\n");
++
++		for (Idx = 0; Idx < CFG_WIFI_RAM_BAND_NUM; Idx++) {
++			dev_info(dev->mt76.dev, "Band%u:\n", Idx);
++			dev_info(dev->mt76.dev, "	 Wlan Idx For VHT MC Sounding = %u\n", info->wlan_idx_for_mc_snd[Idx]);
++			dev_info(dev->mt76.dev, "	 Wlan Idx For HE TB Sounding = %u\n", info->wlan_idx_for_he_tb_snd[Idx]);
++			dev_info(dev->mt76.dev, "	 Wlan Idx For EHT TB Sounding = %u\n", info->wlan_idx_for_eht_tb_snd[Idx]);
++		}
++
++		dev_info(dev->mt76.dev, "ULLen = %d, ULMcs = %d, ULLDCP = %d\n",
++			info->ul_length, info->mcs, info->ldpc);
++
++		dev_info(dev->mt76.dev, "=================== STA Info ===================\n");
++
++		for (Idx = 1; (Idx < 5 && (Idx < CFG_BF_STA_REC_NUM)); Idx++) {
++			snd_sta_info = &info->snd_sta_info[Idx];
++			dev_info(dev->mt76.dev, "Idx%2u Interval = %d, interval counter = %d, TxCnt = %d, StopReason = 0x%02X\n",
++				Idx,
++				snd_sta_info->snd_intv,
++				snd_sta_info->snd_intv_cnt,
++				snd_sta_info->snd_tx_cnt,
++				snd_sta_info->snd_stop_reason);
++		}
++
++		dev_info(dev->mt76.dev, "=================== STA Info Connected ===================\n");
++		// [ToDo] How to iterate and get AID info of station
++		// Check UniEventBFCtrlTxSndHandle() on Logan
++
++		//hardcode max_wtbl_size as 5
++		max_wtbl_size = 5;
++		for (Idx = 1; ((Idx < max_wtbl_size) && (Idx < CFG_BF_STA_REC_NUM)); Idx++) {
++
++			// [ToDo] We do not show AID info here
++			snd_sta_info = &info->snd_sta_info[Idx];
++			dev_info(dev->mt76.dev, " Interval = %d (%u ms), interval counter = %d (%u ms), TxCnt = %d, StopReason = 0x%02X\n",
++				snd_sta_info->snd_intv,
++				snd_sta_info->snd_intv * 10,
++				snd_sta_info->snd_intv_cnt,
++				snd_sta_info->snd_intv_cnt * 10,
++				snd_sta_info->snd_tx_cnt,
++				snd_sta_info->snd_stop_reason);
++		}
++
++		dev_info(dev->mt76.dev, "======================================\n");
++
++		break;
++	}
++	default:
++		dev_info(dev->mt76.dev, "%s: unknown bf event tag %d\n",
++			 __func__, event->tag);
++	}
++
++}
++
++
++int mt7996_mcu_set_muru_fixed_rate_enable(struct mt7996_dev *dev, u8 action, int val)
++{
++	struct {
++		u8 _rsv[4];
++
++		__le16 tag;
++		__le16 len;
++
++		__le16 value;
++		__le16 rsv;
++	} __packed data = {
++		.tag = cpu_to_le16(action),
++		.len = cpu_to_le16(sizeof(data) - 4),
++		.value = cpu_to_le16(!!val),
++	};
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &data, sizeof(data),
++				 false);
++}
++
++int mt7996_mcu_set_muru_fixed_rate_parameter(struct mt7996_dev *dev, u8 action, void *para)
++{
++	char *buf = (char *)para;
++	u8 num_user = 0, recv_arg = 0, max_mcs = 0, usr_mcs[4] = {0};
++	__le16 bw;
++	int i;
++	struct {
++		u8 _rsv[4];
++
++		__le16 tag;
++		__le16 len;
++
++		u8 cmd_version;
++		u8 cmd_revision;
++		__le16 rsv;
++
++		struct uni_muru_mum_set_group_tbl_entry entry;
++	} __packed data = {
++		.tag = cpu_to_le16(action),
++		.len = cpu_to_le16(sizeof(data) - 4),
++	};
++
++#define __RUALLOC_TYPE_CHECK_HE(BW) ((BW == RUALLOC_BW20) || (BW == RUALLOC_BW40) || (BW == RUALLOC_BW80) || (BW == RUALLOC_BW160))
++#define __RUALLOC_TYPE_CHECK_EHT(BW) (__RUALLOC_TYPE_CHECK_HE(BW) || (BW == RUALLOC_BW320))
++	/* [Num of user] - 1~4
++	 * [RUAlloc] - BW320: 395, BW160: 137, BW80: 134, BW40: 130, BW20: 122
++	 * [LTF/GI] - For VHT, short GI: 0, Long GI: 1; 	 *
++	 * For HE/EHT, 4xLTF+3.2us: 0, 4xLTF+0.8us: 1, 2xLTF+0.8us:2
++	 * [Phy/FullBW] - VHT: 0 / HEFullBw: 1 / HEPartialBw: 2 / EHTFullBW: 3, EHTPartialBW: 4
++	 * [DL/UL] DL: 0, UL: 1, DL_UL: 2
++	 * [Wcid User0] - WCID 0
++	 * [MCS of WCID0] - For HE/VHT, 0-11: 1ss MCS0-MCS11, 12-23: 2SS MCS0-MCS11
++	 * For EHT, 0-13: 1ss MCS0-MCS13, 14-27: 2SS MCS0-MCS13
++	 * [WCID 1]
++	 * [MCS of WCID1]
++	 * [WCID 2]
++	 * [MCS of WCID2]
++	 * [WCID 3]
++	 * [MCS of WCID3]
++	 */
++
++	recv_arg = sscanf(buf, "%hhu %hu %hhu %hhu %hhu %hu %hhu %hu %hhu %hu %hhu %hu %hhu",
++			  &num_user, &bw, &data.entry.gi, &data.entry.capa, &data.entry.dl_ul,
++			  &data.entry.wlan_idx0, &usr_mcs[0],
++			  &data.entry.wlan_idx1, &usr_mcs[1],
++			  &data.entry.wlan_idx2, &usr_mcs[2],
++			  &data.entry.wlan_idx3, &usr_mcs[3]);
++
++	if (recv_arg != (5 + (2 * num_user))) {
++		dev_err(dev->mt76.dev, "The number of argument is invalid\n");
++		goto error;
++	}
++
++	if (num_user > 0 && num_user < 5)
++		data.entry.num_user = num_user - 1;
++	else {
++		dev_err(dev->mt76.dev, "The number of user count is invalid\n");
++		goto error;
++	}
++
++	/**
++	 * Older chip shall be set as HE. Refer to getHWSupportByChip() in Logan
++	 * driver to know the value for differnt chips
++	 */
++	data.cmd_version = UNI_CMD_MURU_VER_EHT;
++
++	if (data.cmd_version == UNI_CMD_MURU_VER_EHT)
++		max_mcs = UNI_MAX_MCS_SUPPORT_EHT;
++	else
++		max_mcs = UNI_MAX_MCS_SUPPORT_HE;
++
++
++	// Parameter Check
++	if (data.cmd_version != UNI_CMD_MURU_VER_EHT) {
++		if ((data.entry.capa > MAX_MODBF_HE) || (bw == RUALLOC_BW320))
++			goto error;
++	} else {
++		if ((data.entry.capa <= MAX_MODBF_HE) && (bw == RUALLOC_BW320))
++			goto error;
++	}
++
++	if (data.entry.capa <= MAX_MODBF_HE)
++		max_mcs = UNI_MAX_MCS_SUPPORT_HE;
++
++	if (__RUALLOC_TYPE_CHECK_EHT(bw)) {
++		data.entry.ru_alloc = (u8)(bw & 0xFF);
++		if (bw == RUALLOC_BW320)
++			data.entry.ru_alloc_ext = (u8)(bw >> 8);
++	} else {
++		dev_err(dev->mt76.dev, "RU_ALLOC argument is invalid\n");
++		goto error;
++	}
++
++	if ((data.entry.gi > 2) ||
++	    ((data.entry.gi > 1) && (data.entry.capa == MAX_MODBF_VHT))) {
++		dev_err(dev->mt76.dev, "GI argument is invalid\n");
++		goto error;
++	}
++
++	if (data.entry.dl_ul > 2) {
++		dev_err(dev->mt76.dev, "DL_UL argument is invalid\n");
++		goto error;
++	}
++
++#define __mcs_handler(_n)							\
++	do {									\
++		if (usr_mcs[_n] > max_mcs) {					\
++			usr_mcs[_n] -= (max_mcs + 1);				\
++			data.entry.nss##_n = 1;					\
++			if (usr_mcs[_n] > max_mcs)				\
++				usr_mcs[_n] = max_mcs;				\
++		}								\
++		if ((data.entry.dl_ul & 0x1) == 0)				\
++			data.entry.dl_mcs_user##_n = usr_mcs[_n];		\
++		if ((data.entry.dl_ul & 0x3) > 0)				\
++			data.entry.ul_mcs_user##_n = usr_mcs[_n];		\
++	}									\
++	while (0)
++
++	for (i=0; i<= data.entry.num_user; i++) {
++		switch (i) {
++			case 0:
++				__mcs_handler(0);
++				break;
++			case 1:
++				__mcs_handler(1);
++				break;
++			case 2:
++				__mcs_handler(2);
++				break;
++			case 3:
++				__mcs_handler(3);
++				break;
++			default:
++				break;
++		}
++	}
++#undef __mcs_handler
++
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &data,
++				 sizeof(data), false);
++
++error:
++	dev_err(dev->mt76.dev, "Command failed!\n");
++	return -EINVAL;
++}
++
+ #endif
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+index 098e63ae..27d6a05b 100644
+--- a/mt7996/mtk_mcu.h
++++ b/mt7996/mtk_mcu.h
+@@ -119,6 +119,348 @@ enum {
+ 	EDCCA_FCC = 1,
+ 	EDCCA_ETSI = 2,
+ 	EDCCA_JAPAN = 3
++
++struct bf_pfmu_tag {
++	__le16 tag;
++	__le16 len;
++
++	u8 pfmu_id;
++	bool bfer;
++	u8 band_idx;
++	u8 __rsv[5];
++	u8 buf[56];
++} __packed;
++
++struct bf_starec_read {
++	__le16 tag;
++	__le16 len;
++
++	__le16 wlan_idx;
++	u8 __rsv[2];
++} __packed;
++
++struct bf_fbk_rpt_info {
++	__le16 tag;
++	__le16 len;
++
++	__le16 wlan_idx; // Only need for dynamic_pfmu_update 0x4
++	u8 action;
++	u8 band_idx;
++	u8 __rsv[4];
++
++} __packed;
++
++struct bf_txsnd_info {
++	__le16 tag;
++	__le16 len;
++
++	u8 action;
++	u8 read_clr;
++	u8 vht_opt;
++	u8 he_opt;
++	__le16 wlan_idx;
++	u8 glo_opt;
++	u8 snd_intv;
++	u8 snd_stop;
++	u8 max_snd_stas;
++	u8 tx_time;
++	u8 mcs;
++	u8 ldpc;
++	u8 inf;
++	u8 man;
++	u8 ac_queue;
++	u8 sxn_protect;
++	u8 direct_fbk;
++	u8 __rsv[2];
++} __packed;
++
++struct mt7996_mcu_bf_basic_event {
++	struct mt7996_mcu_rxd rxd;
++
++	u8 __rsv1[4];
++
++	__le16 tag;
++	__le16 len;
++};
++
++struct mt7996_mcu_bf_starec_read {
++
++	struct mt7996_mcu_bf_basic_event event;
++
++	__le16 pfmu_id;
++	bool is_su_mu;
++	u8 txbf_cap;
++	u8 sounding_phy;
++	u8 ndpa_rate;
++	u8 ndp_rate;
++	u8 rpt_poll_rate;
++	u8 tx_mode;
++	u8 nc;
++	u8 nr;
++	u8 bw;
++	u8 total_mem_require;
++	u8 mem_require_20m;
++	u8 mem_row0;
++	u8 mem_col0:6;
++	u8 mem_row0_msb:2;
++	u8 mem_row1;
++	u8 mem_col1:6;
++	u8 mem_row1_msb:2;
++	u8 mem_row2;
++	u8 mem_col2:6;
++	u8 mem_row2_msb:2;
++	u8 mem_row3;
++	u8 mem_col3:6;
++	u8 mem_row3_msb:2;
++
++	__le16 smart_ant;
++	u8 se_idx;
++	u8 auto_sounding_ctrl;
++
++	u8 bf_timeout;
++	u8 bf_dbw;
++	u8 bf_ncol;
++	u8 bf_nrow;
++
++	u8 nr_lt_bw80;
++	u8 nc_lt_bw80;
++	u8 ru_start_idx;
++	u8 ru_end_idx;
++
++	bool trigger_su;
++	bool trigger_mu;
++
++	bool ng16_su;
++	bool ng16_mu;
++
++	bool codebook42_su;
++	bool codebook75_mu;
++
++	u8 he_ltf;
++	u8 rsv[3];
++};
++
++#define TXBF_PFMU_ID_NUM_MAX 48
++
++#define TXBF_PFMU_ID_NUM_MAX_TBTC_BAND0 TXBF_PFMU_ID_NUM_MAX
++#define TXBF_PFMU_ID_NUM_MAX_TBTC_BAND1 TXBF_PFMU_ID_NUM_MAX
++#define TXBF_PFMU_ID_NUM_MAX_TBTC_BAND2 TXBF_PFMU_ID_NUM_MAX
++
++/* CFG_BF_STA_REC shall be varied based on BAND Num */
++#define CFG_BF_STA_REC_NUM (TXBF_PFMU_ID_NUM_MAX_TBTC_BAND0 + TXBF_PFMU_ID_NUM_MAX_TBTC_BAND1 + TXBF_PFMU_ID_NUM_MAX_TBTC_BAND2)
++
++#define BF_SND_CTRL_STA_DWORD_CNT   ((CFG_BF_STA_REC_NUM + 0x1F) >> 5)
++
++#ifndef ALIGN_4
++	#define ALIGN_4(_value)             (((_value) + 3) & ~3u)
++#endif /* ALIGN_4 */
++
++#define CFG_WIFI_RAM_BAND_NUM 3
++
++struct uni_event_bf_txsnd_sta_info {
++	u8 snd_intv;       /* Sounding interval upper bound, unit:15ms */
++	u8 snd_intv_cnt;   /* Sounding interval counter */
++	u8 snd_tx_cnt;     /* Tx sounding count for debug */
++	u8 snd_stop_reason;  /* Bitwise reason to put in Stop Queue */
++};
++
++struct mt7996_mcu_tx_snd_info {
++
++	struct mt7996_mcu_bf_basic_event event;
++
++	u8 vht_opt;
++	u8 he_opt;
++	u8 glo_opt;
++	u8 __rsv;
++	__le32 snd_rec_su_sta[BF_SND_CTRL_STA_DWORD_CNT];
++	__le32 snd_rec_vht_mu_sta[BF_SND_CTRL_STA_DWORD_CNT];
++	__le32 snd_rec_he_tb_sta[BF_SND_CTRL_STA_DWORD_CNT];
++	__le32 snd_rec_eht_tb_sta[BF_SND_CTRL_STA_DWORD_CNT];
++	__le16 wlan_idx_for_mc_snd[ALIGN_4(CFG_WIFI_RAM_BAND_NUM)];
++	__le16 wlan_idx_for_he_tb_snd[ALIGN_4(CFG_WIFI_RAM_BAND_NUM)];
++	__le16 wlan_idx_for_eht_tb_snd[ALIGN_4(CFG_WIFI_RAM_BAND_NUM)];
++	__le16 ul_length;
++	u8 mcs;
++	u8 ldpc;
++	struct uni_event_bf_txsnd_sta_info snd_sta_info[CFG_BF_STA_REC_NUM];
++};
++
++struct mt7996_mcu_txbf_fbk_info {
++
++	struct mt7996_mcu_bf_basic_event event;
++
++	__le32 u4DeQInterval;     /* By ms */
++	__le32 u4PollPFMUIntrStatTimeOut; /* micro-sec */
++	__le32 u4RptPktTimeOutListNum;
++	__le32 u4RptPktListNum;
++	__le32 u4PFMUWRTimeOutCnt;
++	__le32 u4PFMUWRFailCnt;
++	__le32 u4PFMUWRDoneCnt;
++	__le32 u4PFMUWRTimeoutFreeCnt;
++	__le32 u4FbRptPktDropCnt;
++	__le32 au4RxPerStaFbRptCnt[CFG_BF_STA_REC_NUM];
++};
++
++struct pfmu_ru_field {
++	__le32 ru_start_id:7;
++	__le32 _rsv1:1;
++	__le32 ru_end_id:7;
++	__le32 _rsv2:1;
++} __packed;
++
++struct pfmu_partial_bw_info {
++	__le32 partial_bw_info:9;
++	__le32 _rsv1:7;
++} __packed;
++
++struct mt7996_pfmu_tag1 {
++	__le32 pfmu_idx:10;
++	__le32 ebf:1;
++	__le32 data_bw:3;
++	__le32 lm:3;
++	__le32 is_mu:1;
++	__le32 nr:3;
++	__le32 nc:3;
++	__le32 codebook:2;
++	__le32 ngroup:2;
++	__le32 invalid_prof:1;
++	__le32 _rsv:3;
++
++	__le32 col_id1:7, row_id1:9;
++	__le32 col_id2:7, row_id2:9;
++	__le32 col_id3:7, row_id3:9;
++	__le32 col_id4:7, row_id4:9;
++
++	union {
++		struct pfmu_ru_field field;
++		struct pfmu_partial_bw_info bw_info;
++	};
++	__le32 mob_cal_en:1;
++	__le32 _rsv2:3;
++	__le32 mob_ru_alloc:9;	/* EHT profile uses full 9 bit */
++	__le32 _rsv3:3;
++
++	__le32 snr_sts0:8, snr_sts1:8, snr_sts2:8, snr_sts3:8;
++	__le32 snr_sts4:8, snr_sts5:8, snr_sts6:8, snr_sts7:8;
++
++	__le32 _rsv4;
++} __packed;
++
++struct mt7996_pfmu_tag2 {
++	__le32 smart_ant:24;
++	__le32 se_idx:5;
++	__le32 _rsv:3;
++
++	__le32 _rsv1:16;
++	__le32 ibf_timeout:8;
++	__le32 _rsv2:8;
++
++	__le32 ibf_data_bw:3;
++	__le32 ibf_nc:3;
++	__le32 ibf_nr:3;
++	__le32 ibf_ru:9;
++	__le32 _rsv3:14;
++
++	__le32 mob_delta_t:8;
++	__le32 mob_lq_result:7;
++	__le32 _rsv5:1;
++	__le32 _rsv6:16;
++
++	__le32 _rsv7;
++} __packed;
++
++struct mt7996_pfmu_tag_event {
++	struct mt7996_mcu_bf_basic_event event;
++
++	u8 bfer;
++	u8 __rsv[3];
++
++	struct mt7996_pfmu_tag1 t1;
++	struct mt7996_pfmu_tag2 t2;
++};
++
++enum {
++	UNI_EVENT_BF_PFMU_TAG = 0x5,
++	UNI_EVENT_BF_PFMU_DATA = 0x7,
++	UNI_EVENT_BF_STAREC = 0xB,
++	UNI_EVENT_BF_CAL_PHASE = 0xC,
++	UNI_EVENT_BF_FBK_INFO = 0x17,
++	UNI_EVENT_BF_TXSND_INFO = 0x18,
++	UNI_EVENT_BF_PLY_INFO = 0x19,
++	UNI_EVENT_BF_METRIC_INFO = 0x1A,
++	UNI_EVENT_BF_TXCMD_CFG_INFO = 0x1B,
++	UNI_EVENT_BF_SND_CNT_INFO = 0x1D,
++	UNI_EVENT_BF_MAX_NUM
++};
++
++enum {
++	UNI_CMD_MURU_FIXED_RATE_CTRL = 0x11,
++	UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
++};
++
++struct uni_muru_mum_set_group_tbl_entry {
++	__le16 wlan_idx0;
++	__le16 wlan_idx1;
++	__le16 wlan_idx2;
++	__le16 wlan_idx3;
++
++	u8 dl_mcs_user0:4;
++	u8 dl_mcs_user1:4;
++	u8 dl_mcs_user2:4;
++	u8 dl_mcs_user3:4;
++	u8 ul_mcs_user0:4;
++	u8 ul_mcs_user1:4;
++	u8 ul_mcs_user2:4;
++	u8 ul_mcs_user3:4;
++
++	u8 num_user:2;
++	u8 rsv:6;
++	u8 nss0:2;
++	u8 nss1:2;
++	u8 nss2:2;
++	u8 nss3:2;
++	u8 ru_alloc;
++	u8 ru_alloc_ext;
++
++	u8 capa;
++	u8 gi;
++	u8 dl_ul;
++	u8 _rsv2;
++};
++
++enum UNI_CMD_MURU_VER_T {
++	UNI_CMD_MURU_VER_LEG = 0,
++	UNI_CMD_MURU_VER_HE,
++	UNI_CMD_MURU_VER_EHT,
++	UNI_CMD_MURU_VER_MAX
++};
++
++#define UNI_MAX_MCS_SUPPORT_HE 11
++#define UNI_MAX_MCS_SUPPORT_EHT 13
++
++enum {
++	RUALLOC_BW20 = 122,
++	RUALLOC_BW40 = 130,
++	RUALLOC_BW80 = 134,
++	RUALLOC_BW160 = 137,
++	RUALLOC_BW320 = 395,
++};
++
++enum {
++	MAX_MODBF_VHT = 0,
++	MAX_MODBF_HE = 2,
++	MAX_MODBF_EHT = 4,
++};
++
++enum {
++	BF_SND_READ_INFO = 0,
++	BF_SND_CFG_OPT,
++	BF_SND_CFG_INTV,
++	BF_SND_STA_STOP,
++	BF_SND_CFG_MAX_STA,
++	BF_SND_CFG_BFRP,
++	BF_SND_CFG_INF,
++	BF_SND_CFG_TXOP_SND
+ };
+ 
+ enum {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0047-mtk-wifi-mt76-testmode-add-channel-68-96.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0047-mtk-wifi-mt76-testmode-add-channel-68-96.patch
deleted file mode 100644
index 309f186..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0047-mtk-wifi-mt76-testmode-add-channel-68-96.patch
+++ /dev/null
@@ -1,248 +0,0 @@
-From 2904eb321441ee487d4438fdc46efe3d8032e74f Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 11 Sep 2023 14:43:07 +0800
-Subject: [PATCH 047/116] mtk: wifi: mt76: testmode: add channel 68 & 96
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Add all the channel between 68 & 96 since ibf 5g channel group 3 will use channel 84.
-Also, "mtk: wifi: mt76: testmode: add channel 68 & 96" can be
-merged into to "mtk: wifi: mt76: testmode: add basic testmode support"
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Fix 5g channel list size
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mac80211.c        |  9 +++++++++
- mt7996/eeprom.c   | 49 +++++++++++++++++++++++++++++++++++++++++++++--
- mt7996/eeprom.h   |  2 ++
- mt7996/mcu.c      | 10 +++++++++-
- mt7996/testmode.c | 15 ++++++++++++---
- mt7996/testmode.h |  6 +++---
- 6 files changed, 82 insertions(+), 9 deletions(-)
-
-diff --git a/mac80211.c b/mac80211.c
-index 93e5c50..de0a398 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -34,6 +34,15 @@ static const struct ieee80211_channel mt76_channels_5ghz[] = {
- 	CHAN5G(60, 5300),
- 	CHAN5G(64, 5320),
- 
-+	CHAN5G(68, 5340),
-+	CHAN5G(72, 5360),
-+	CHAN5G(76, 5380),
-+	CHAN5G(80, 5400),
-+	CHAN5G(84, 5420),
-+	CHAN5G(88, 5440),
-+	CHAN5G(92, 5460),
-+	CHAN5G(96, 5480),
-+
- 	CHAN5G(100, 5500),
- 	CHAN5G(104, 5520),
- 	CHAN5G(108, 5540),
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index f97d76c..acf5bc1 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -18,6 +18,17 @@ const struct ieee80211_channel dpd_2g_ch_list_bw20[] = {
- 	CHAN2G(11, 2462)
- };
- 
-+const struct ieee80211_channel dpd_5g_skip_ch_list[] = {
-+	CHAN5G(68, 5340),
-+	CHAN5G(72, 5360),
-+	CHAN5G(76, 5380),
-+	CHAN5G(80, 5400),
-+	CHAN5G(84, 5420),
-+	CHAN5G(88, 5440),
-+	CHAN5G(92, 5460),
-+	CHAN5G(96, 5480)
-+};
-+
- const struct ieee80211_channel dpd_5g_ch_list_bw160[] = {
- 	CHAN5G(50, 5250),
- 	CHAN5G(114, 5570),
-@@ -44,6 +55,7 @@ const struct ieee80211_channel dpd_6g_ch_list_bw320[] = {
- };
- 
- const u32 dpd_2g_bw20_ch_num = ARRAY_SIZE(dpd_2g_ch_list_bw20);
-+const u32 dpd_5g_skip_ch_num = ARRAY_SIZE(dpd_5g_skip_ch_list);
- const u32 dpd_5g_bw160_ch_num = ARRAY_SIZE(dpd_5g_ch_list_bw160);
- const u32 dpd_6g_bw160_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw160);
- const u32 dpd_6g_bw320_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw320);
-@@ -168,8 +180,8 @@ mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band)
- 	if (band == NL80211_BAND_2GHZ)
- 		dpd_size = dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
- 	else if (band == NL80211_BAND_5GHZ)
--		dpd_size = mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
--			   dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
-+		dpd_size = (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
-+			   DPD_PER_CH_BW20_SIZE + dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
- 	else
- 		dpd_size = mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
- 			   (dpd_6g_bw160_ch_num + dpd_6g_bw320_ch_num) * DPD_PER_CH_GT_BW20_SIZE;
-@@ -431,6 +443,39 @@ out:
- 	return ret;
- }
- 
-+static void mt7996_eeprom_init_precal(struct mt7996_dev *dev)
-+{
-+#define MT76_CHANNELS_5GHZ_SIZE		36	/* ARRAY_SIZE(mt76_channels_5ghz) */
-+#define MT76_CHANNELS_6GHZ_SIZE		59	/* ARRAY_SIZE(mt76_channels_6ghz) */
-+
-+	dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_2G] = ARRAY_SIZE(dpd_2g_ch_list_bw20);
-+	dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_5G_SKIP] = ARRAY_SIZE(dpd_5g_skip_ch_list);
-+	dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_5G] = MT76_CHANNELS_5GHZ_SIZE -
-+						   DPD_CH_NUM(BW20_5G_SKIP);
-+	dev->prek.dpd_ch_num[DPD_CH_NUM_BW160_5G] = ARRAY_SIZE(dpd_5g_ch_list_bw160);
-+	dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_6G] = MT76_CHANNELS_6GHZ_SIZE;
-+	dev->prek.dpd_ch_num[DPD_CH_NUM_BW160_6G] = ARRAY_SIZE(dpd_6g_ch_list_bw160);
-+
-+	switch (mt76_chip(&dev->mt76)) {
-+	case 0x7990:
-+		dev->prek.rev = mt7996_prek_rev;
-+		/* 5g & 6g bw 80 dpd channel list is not used */
-+		dev->prek.dpd_ch_num[DPD_CH_NUM_BW320_6G] = ARRAY_SIZE(dpd_6g_ch_list_bw320);
-+		break;
-+	case 0x7992:
-+		dev->prek.rev  = mt7992_prek_rev;
-+		dev->prek.dpd_ch_num[DPD_CH_NUM_BW80_5G] = ARRAY_SIZE(dpd_5g_ch_list_bw80);
-+		/* 6g is not used in current sku */
-+		dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_6G] = 0;
-+		dev->prek.dpd_ch_num[DPD_CH_NUM_BW80_6G] = 0;
-+		dev->prek.dpd_ch_num[DPD_CH_NUM_BW160_6G] = 0;
-+		break;
-+	default:
-+		dev->prek.rev  = mt7996_prek_rev;
-+		break;
-+	}
-+}
-+
- static int mt7996_eeprom_load_precal(struct mt7996_dev *dev)
- {
- 	struct mt76_dev *mdev = &dev->mt76;
-diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index 03a4fd0..9a15b44 100644
---- a/mt7996/eeprom.h
-+++ b/mt7996/eeprom.h
-@@ -67,6 +67,8 @@ enum mt7996_eeprom_field {
- 
- extern const struct ieee80211_channel dpd_2g_ch_list_bw20[];
- extern const u32 dpd_2g_bw20_ch_num;
-+extern const struct ieee80211_channel dpd_5g_skip_ch_list[];
-+extern const u32 dpd_5g_skip_ch_num;
- extern const struct ieee80211_channel dpd_5g_ch_list_bw160[];
- extern const u32 dpd_5g_bw160_ch_num;
- extern const struct ieee80211_channel dpd_6g_ch_list_bw160[];
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index d46c5ae..6f08161 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -3766,7 +3766,8 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
- 		chan_list_size = mphy->sband_5g.sband.n_channels;
- 		base_offset += dpd_size_2g;
- 		if (bw == NL80211_CHAN_WIDTH_160) {
--			base_offset += mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
-+			base_offset += (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
-+				       DPD_PER_CH_BW20_SIZE;
- 			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
- 			cal_id = RF_DPD_FLAT_5G_MEM_CAL;
- 			chan_list = dpd_5g_ch_list_bw160;
-@@ -3775,6 +3776,9 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
- 			/* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
- 			channel -= 2;
- 		}
-+		if (channel >= dpd_5g_skip_ch_list[0].hw_value &&
-+		    channel <= dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
-+			return 0;
- 		break;
- 	case NL80211_BAND_6GHZ:
- 		dpd_mask = MT_EE_WIFI_CAL_DPD_6G;
-@@ -3814,6 +3818,10 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
- 	if (idx == chan_list_size)
- 		return -EINVAL;
- 
-+	if (band == NL80211_BAND_5GHZ && bw != NL80211_CHAN_WIDTH_160 &&
-+	    channel > dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
-+		idx -= dpd_5g_skip_ch_num;
-+
- 	cal += MT_EE_CAL_GROUP_SIZE + base_offset + idx * per_chan_size;
- 
- 	for (i = 0; i < per_chan_size / MT_EE_CAL_UNIT; i++) {
-diff --git a/mt7996/testmode.c b/mt7996/testmode.c
-index 5cec1ee..95d3bde 100644
---- a/mt7996/testmode.c
-+++ b/mt7996/testmode.c
-@@ -531,6 +531,11 @@ mt7996_tm_dpd_prek_send_req(struct mt7996_phy *phy, struct mt7996_tm_req *req,
- 	memcpy(&chandef_backup, chandef, sizeof(struct cfg80211_chan_def));
- 
- 	for (i = 0; i < channel_size; i++) {
-+		if (chan_list[i].band == NL80211_BAND_5GHZ &&
-+		    chan_list[i].hw_value >= dpd_5g_skip_ch_list[0].hw_value &&
-+		    chan_list[i].hw_value <= dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
-+			continue;
-+
- 		memcpy(chandef->chan, &chan_list[i], sizeof(struct ieee80211_channel));
- 		chandef->width = width;
- 
-@@ -612,7 +617,8 @@ mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
- 						  NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_5G_CAL);
- 		if (ret)
- 			return ret;
--		wait_on_prek_offset += mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
-+		wait_on_prek_offset += (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
-+				       DPD_PER_CH_BW20_SIZE;
- 		wait_event_timeout(mdev->mcu.wait,
- 				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
- 
-@@ -868,6 +874,7 @@ mt7996_tm_get_center_chan(struct mt7996_phy *phy, struct cfg80211_chan_def *chan
- 	const struct ieee80211_channel *chan = mphy->sband_5g.sband.channels;
- 	u32 bitmap, i, offset, width_mhz, size = mphy->sband_5g.sband.n_channels;
- 	u16 first_control = 0, control_chan = chandef->chan->hw_value;
-+	bool not_first;
- 
- 	bitmap = mt7996_tm_bw_mapping(chandef->width, BW_MAP_NL_TO_CONTROL_BITMAP_5G);
- 	if (!bitmap)
-@@ -877,7 +884,9 @@ mt7996_tm_get_center_chan(struct mt7996_phy *phy, struct cfg80211_chan_def *chan
- 	offset = width_mhz / 10 - 2;
- 
- 	for (i = 0; i < size; i++) {
--		if (!((1 << i) & bitmap))
-+		not_first = (chandef->width != NL80211_CHAN_WIDTH_160) ?
-+			    (i % bitmap) : (i >= 32) || !((1 << i) & bitmap);
-+		if (not_first)
- 			continue;
- 
- 		if (control_chan >= chan[i].hw_value)
-@@ -886,7 +895,7 @@ mt7996_tm_get_center_chan(struct mt7996_phy *phy, struct cfg80211_chan_def *chan
- 			break;
- 	}
- 
--	if (i == size || first_control == 0)
-+	if (first_control == 0)
- 		return control_chan;
- 
- 	return first_control + offset;
-diff --git a/mt7996/testmode.h b/mt7996/testmode.h
-index f97ccb2..ba1767a 100644
---- a/mt7996/testmode.h
-+++ b/mt7996/testmode.h
-@@ -38,9 +38,9 @@ enum {
- 	BF_CDBW_8080MHZ,
- };
- 
--#define FIRST_CONTROL_CHAN_BITMAP_BW40		0x5555555
--#define FIRST_CONTROL_CHAN_BITMAP_BW80		0x111111
--#define FIRST_CONTROL_CHAN_BITMAP_BW160		0x100101
-+#define FIRST_CONTROL_CHAN_BITMAP_BW40		2
-+#define FIRST_CONTROL_CHAN_BITMAP_BW80		4
-+#define FIRST_CONTROL_CHAN_BITMAP_BW160		0x10010101
- 
- enum bw_mapping_method {
- 	BW_MAP_NL_TO_FW,
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0048-mtk-mt76-mt7996-add-build-the-following-MURU-mcu-com.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0048-mtk-mt76-mt7996-add-build-the-following-MURU-mcu-com.patch
new file mode 100644
index 0000000..0914922
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0048-mtk-mt76-mt7996-add-build-the-following-MURU-mcu-com.patch
@@ -0,0 +1,300 @@
+From 721ea64eb33f3708435f86501121602666d2caec Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Tue, 13 Jun 2023 14:49:02 +0800
+Subject: [PATCH 048/199] mtk: mt76: mt7996: add build the following MURU mcu
+ command tlvs
+
+It includes the following tlvs:
+1. MURU tlv id 0x10, 0x33, 0xC8, 0xC9, 0xCA, 0xCC, 0xCD
+2. BF tlv id 0x1c
+
+---
+ mt7996/mcu.h         |   1 +
+ mt7996/mt7996.h      |   3 ++
+ mt7996/mtk_debugfs.c | 123 +++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mtk_mcu.c     |  78 +++++++++++++++++++++++++++
+ mt7996/mtk_mcu.h     |  14 +++++
+ 5 files changed, 219 insertions(+)
+
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 8a718513..a98b174e 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -776,6 +776,7 @@ enum {
+ 	BF_MOD_EN_CTRL = 20,
+ 	BF_FBRPT_DBG_INFO_READ = 23,
+ 	BF_TXSND_INFO = 24,
++	BF_CFG_PHY = 28,
+ };
+ 
+ enum {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 8935ef22..45ce7db7 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -821,6 +821,9 @@ void mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ int mt7996_mcu_set_muru_fixed_rate_enable(struct mt7996_dev *dev, u8 action, int val);
+ int mt7996_mcu_set_muru_fixed_rate_parameter(struct mt7996_dev *dev, u8 action, void *para);
+ int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para);
++int mt7996_mcu_set_muru_cmd(struct mt7996_dev *dev, u16 action, int val);
++int mt7996_mcu_muru_set_prot_frame_thr(struct mt7996_dev *dev, u32 val);
++int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val);
+ #endif
+ 
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index b3cc8119..0e029d5d 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2912,6 +2912,127 @@ mt7996_sr_scene_cond_show(struct seq_file *file, void *data)
+ }
+ DEFINE_SHOW_ATTRIBUTE(mt7996_sr_scene_cond);
+ 
++static int
++mt7996_starec_bf_read_set(void *data, u64 wlan_idx)
++{
++	struct mt7996_phy *phy = data;
++
++	return mt7996_mcu_set_txbf_internal(phy, BF_STA_REC_READ, wlan_idx);
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_starec_bf_read, NULL,
++			 mt7996_starec_bf_read_set, "%lld\n");
++
++static ssize_t
++mt7996_bf_txsnd_info_set(struct file *file,
++			 const char __user *user_buf,
++			 size_t count, loff_t *ppos)
++{
++	struct mt7996_phy *phy = file->private_data;
++	char buf[40];
++	int ret;
++
++	if (count >= sizeof(buf))
++		return -EINVAL;
++
++	if (copy_from_user(buf, user_buf, count))
++		return -EFAULT;
++
++	if (count && buf[count - 1] == '\n')
++		buf[count - 1] = '\0';
++	else
++		buf[count] = '\0';
++
++	ret = mt7996_mcu_set_txbf_snd_info(phy, buf);
++
++	if (ret) return -EFAULT;
++
++	return count;
++}
++
++static const struct file_operations fops_bf_txsnd_info = {
++	.write = mt7996_bf_txsnd_info_set,
++	.read = NULL,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static int
++mt7996_bf_fbk_rpt_set(void *data, u64 wlan_idx)
++{
++	struct mt7996_phy *phy = data;
++
++	return mt7996_mcu_set_txbf_internal(phy, BF_FBRPT_DBG_INFO_READ, wlan_idx);
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_bf_fbk_rpt, NULL,
++			 mt7996_bf_fbk_rpt_set, "%lld\n");
++
++static int
++mt7996_bf_pfmu_tag_read_set(void *data, u64 wlan_idx)
++{
++	struct mt7996_phy *phy = data;
++
++	return mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, wlan_idx);
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_bf_pfmu_tag_read, NULL,
++			 mt7996_bf_pfmu_tag_read_set, "%lld\n");
++
++static int
++mt7996_muru_fixed_rate_set(void *data, u64 val)
++{
++	struct mt7996_dev *dev = data;
++
++	return mt7996_mcu_set_muru_fixed_rate_enable(dev, UNI_CMD_MURU_FIXED_RATE_CTRL,
++						     val);
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_muru_fixed_rate_enable, NULL,
++			 mt7996_muru_fixed_rate_set, "%lld\n");
++
++static ssize_t
++mt7996_muru_fixed_rate_parameter_set(struct file *file,
++				     const char __user *user_buf,
++				     size_t count, loff_t *ppos)
++{
++	struct mt7996_dev *dev = file->private_data;
++	char buf[40];
++	int ret;
++
++	if (count >= sizeof(buf))
++		return -EINVAL;
++
++	if (copy_from_user(buf, user_buf, count))
++		return -EFAULT;
++
++	if (count && buf[count - 1] == '\n')
++		buf[count - 1] = '\0';
++	else
++		buf[count] = '\0';
++
++
++	ret = mt7996_mcu_set_muru_fixed_rate_parameter(dev, UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
++						       buf);
++
++	if (ret) return -EFAULT;
++
++	return count;
++}
++
++static const struct file_operations fops_muru_fixed_group_rate = {
++	.write = mt7996_muru_fixed_rate_parameter_set,
++	.read = NULL,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static int mt7996_muru_prot_thr_set(void *data, u64 val)
++{
++	struct mt7996_phy *phy = data;
++
++	return mt7996_mcu_muru_set_prot_frame_thr(phy->dev, (u32)val);
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_muru_prot_thr, NULL,
++			 mt7996_muru_prot_thr_set, "%lld\n");
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+@@ -3006,6 +3127,8 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ 	debugfs_create_file("bf_fbk_rpt", 0600, dir, phy, &fops_bf_fbk_rpt);
+ 	debugfs_create_file("pfmu_tag_read", 0600, dir, phy, &fops_bf_pfmu_tag_read);
+ 
++	debugfs_create_file("muru_prot_thr", 0200, dir, phy, &fops_muru_prot_thr);
++
+ 	return 0;
+ }
+ 
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index 6b2cdad6..68650623 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -904,4 +904,82 @@ error:
+ 	return -EINVAL;
+ }
+ 
++/**
++ * This function can be used to build the following commands
++ * MURU_SUTX_CTRL (0x10)
++ * SET_FORCE_MU (0x33)
++ * SET_MUDL_ACK_POLICY (0xC8)
++ * SET_TRIG_TYPE (0xC9)
++ * SET_20M_DYN_ALGO (0xCA)
++ * SET_CERT_MU_EDCA_OVERRIDE (0xCD)
++ */
++int mt7996_mcu_set_muru_cmd(struct mt7996_dev *dev, u16 action, int val)
++{
++	struct {
++		u8 _rsv[4];
++
++		__le16 tag;
++		__le16 len;
++
++		u8 config;
++		u8 rsv[3];
++	} __packed data = {
++		.tag = cpu_to_le16(action),
++		.len = cpu_to_le16(sizeof(data) - 4),
++		.config = (u8) val,
++	};
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &data, sizeof(data),
++				 false);
++}
++
++int mt7996_mcu_muru_set_prot_frame_thr(struct mt7996_dev *dev, u32 val)
++{
++	struct {
++		u8 _rsv[4];
++
++		__le16 tag;
++		__le16 len;
++
++		__le32 prot_frame_thr;
++	} __packed data = {
++		.tag = cpu_to_le16(UNI_CMD_MURU_PROT_FRAME_THR),
++		.len = cpu_to_le16(sizeof(data) - 4),
++		.prot_frame_thr = cpu_to_le32(val),
++	};
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &data, sizeof(data),
++				 false);
++}
++
++int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val)
++{
++#define BF_PHY_SMTH_INT_BYPASS 0
++#define BYPASS_VAL 1
++	struct mt7996_dev *dev = phy->dev;
++	struct {
++		u8 _rsv[4];
++
++		u16 tag;
++		u16 len;
++
++		u8 action;
++		u8 band_idx;
++		u8 smthintbypass;
++		u8 __rsv2[5];
++	} __packed data = {
++		.tag = cpu_to_le16(BF_CFG_PHY),
++		.len = cpu_to_le16(sizeof(data) - 4),
++		.action = BF_PHY_SMTH_INT_BYPASS,
++		.band_idx = phy->mt76->band_idx,
++		.smthintbypass = val,
++	};
++
++	if (val != BYPASS_VAL)
++		return -EINVAL;
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &data, sizeof(data),
++				 true);
++}
++
+ #endif
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+index 27d6a05b..d9686ebb 100644
+--- a/mt7996/mtk_mcu.h
++++ b/mt7996/mtk_mcu.h
+@@ -119,6 +119,20 @@ enum {
+ 	EDCCA_FCC = 1,
+ 	EDCCA_ETSI = 2,
+ 	EDCCA_JAPAN = 3
++};
++
++enum {
++	UNI_CMD_MURU_SUTX_CTRL = 0x10,
++	UNI_CMD_MURU_FIXED_RATE_CTRL,
++	UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
++	UNI_CMD_MURU_SET_FORCE_MU = 0x33,
++	UNI_CMD_MURU_MUNUAL_CONFIG = 0x64,
++	UNI_CMD_MURU_SET_MUDL_ACK_POLICY = 0xC9,
++	UNI_CMD_MURU_SET_TRIG_TYPE,
++	UNI_CMD_MURU_SET_20M_DYN_ALGO,
++	UNI_CMD_MURU_PROT_FRAME_THR = 0xCC,
++	UNI_CMD_MURU_SET_CERT_MU_EDCA_OVERRIDE,
++};
+ 
+ struct bf_pfmu_tag {
+ 	__le16 tag;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0048-mtk-wifi-mt76-testmode-add-kite-testmode-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0048-mtk-wifi-mt76-testmode-add-kite-testmode-support.patch
deleted file mode 100644
index ef91f55..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0048-mtk-wifi-mt76-testmode-add-kite-testmode-support.patch
+++ /dev/null
@@ -1,597 +0,0 @@
-From 85865dc300bbe3bc8ef98907a145e8a9875858af Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Thu, 12 Oct 2023 16:17:33 +0800
-Subject: [PATCH 048/116] mtk: wifi: mt76: testmode: add kite testmode support
-
-Add Kite testmode support
-1. avoid entering connac 2 testmode flow in kite
-2. refactor prek implementation for handling chip difference
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/eeprom.c   | 63 +++++++++++++-----------------
- mt7996/eeprom.h   | 81 +++++++++++++++++++++++++++------------
- mt7996/mcu.c      | 48 ++++++++++++++---------
- mt7996/mt7996.h   | 18 ++++++++-
- mt7996/testmode.c | 97 ++++++++++++++++++++++++++++-------------------
- testmode.c        | 11 ++++--
- 6 files changed, 198 insertions(+), 120 deletions(-)
-
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index acf5bc1..215d81e 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -29,12 +29,39 @@ const struct ieee80211_channel dpd_5g_skip_ch_list[] = {
- 	CHAN5G(96, 5480)
- };
- 
-+const struct ieee80211_channel dpd_5g_ch_list_bw80[] = {
-+	CHAN5G(42, 5210),
-+	CHAN5G(58, 5290),
-+	CHAN5G(106, 5530),
-+	CHAN5G(122, 5610),
-+	CHAN5G(138, 5690),
-+	CHAN5G(155, 5775),
-+	CHAN5G(171, 5855)
-+};
-+
- const struct ieee80211_channel dpd_5g_ch_list_bw160[] = {
- 	CHAN5G(50, 5250),
- 	CHAN5G(114, 5570),
- 	CHAN5G(163, 5815)
- };
- 
-+const struct ieee80211_channel dpd_6g_ch_list_bw80[] = {
-+	CHAN6G(7, 5985),
-+	CHAN6G(23, 6065),
-+	CHAN6G(39, 6145),
-+	CHAN6G(55, 6225),
-+	CHAN6G(71, 6305),
-+	CHAN6G(87, 6385),
-+	CHAN6G(103, 6465),
-+	CHAN6G(119, 6545),
-+	CHAN6G(135, 6625),
-+	CHAN6G(151, 6705),
-+	CHAN6G(167, 6785),
-+	CHAN6G(183, 6865),
-+	CHAN6G(199, 6945),
-+	CHAN6G(215, 7025)
-+};
-+
- const struct ieee80211_channel dpd_6g_ch_list_bw160[] = {
- 	CHAN6G(15, 6025),
- 	CHAN6G(47, 6185),
-@@ -54,12 +81,6 @@ const struct ieee80211_channel dpd_6g_ch_list_bw320[] = {
- 	CHAN6G(191, 6905)
- };
- 
--const u32 dpd_2g_bw20_ch_num = ARRAY_SIZE(dpd_2g_ch_list_bw20);
--const u32 dpd_5g_skip_ch_num = ARRAY_SIZE(dpd_5g_skip_ch_list);
--const u32 dpd_5g_bw160_ch_num = ARRAY_SIZE(dpd_5g_ch_list_bw160);
--const u32 dpd_6g_bw160_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw160);
--const u32 dpd_6g_bw320_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw320);
--
- static int mt7996_check_eeprom(struct mt7996_dev *dev)
- {
- 	u8 *eeprom = dev->mt76.eeprom.data;
-@@ -159,36 +180,6 @@ const char *mt7996_eeprom_name(struct mt7996_dev *dev)
- 	}
- }
- 
--int
--mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band)
--{
--	/* handle different sku */
--	static const u8 band_to_idx[] = {
--		[NL80211_BAND_2GHZ] = MT_BAND0,
--		[NL80211_BAND_5GHZ] = MT_BAND1,
--		[NL80211_BAND_6GHZ] = MT_BAND2,
--	};
--	struct mt7996_phy *phy = __mt7996_phy(dev, band_to_idx[band]);
--	struct mt76_phy *mphy;
--	int dpd_size;
--
--	if (!phy)
--		return 0;
--
--	mphy = phy->mt76;
--
--	if (band == NL80211_BAND_2GHZ)
--		dpd_size = dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
--	else if (band == NL80211_BAND_5GHZ)
--		dpd_size = (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
--			   DPD_PER_CH_BW20_SIZE + dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
--	else
--		dpd_size = mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
--			   (dpd_6g_bw160_ch_num + dpd_6g_bw320_ch_num) * DPD_PER_CH_GT_BW20_SIZE;
--
--	return dpd_size;
--}
--
- static int
- mt7996_eeprom_load_bin(struct mt7996_dev *dev)
- {
-diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index 9a15b44..fa9c31e 100644
---- a/mt7996/eeprom.h
-+++ b/mt7996/eeprom.h
-@@ -45,36 +45,69 @@ enum mt7996_eeprom_field {
- #define MT_EE_WIFI_CAL_DPD			GENMASK(5, 3)
- 
- #define MT_EE_CAL_UNIT				1024
--#define MT_EE_CAL_GROUP_SIZE_2G			(4 * MT_EE_CAL_UNIT)
--#define MT_EE_CAL_GROUP_SIZE_5G			(45 * MT_EE_CAL_UNIT)
--#define MT_EE_CAL_GROUP_SIZE_6G			(125 * MT_EE_CAL_UNIT)
--#define MT_EE_CAL_ADCDCOC_SIZE_2G		(4 * 4)
--#define MT_EE_CAL_ADCDCOC_SIZE_5G		(4 * 4)
--#define MT_EE_CAL_ADCDCOC_SIZE_6G		(4 * 5)
--#define MT_EE_CAL_GROUP_SIZE			(MT_EE_CAL_GROUP_SIZE_2G + \
--						 MT_EE_CAL_GROUP_SIZE_5G + \
--						 MT_EE_CAL_GROUP_SIZE_6G + \
--						 MT_EE_CAL_ADCDCOC_SIZE_2G + \
--						 MT_EE_CAL_ADCDCOC_SIZE_5G + \
--						 MT_EE_CAL_ADCDCOC_SIZE_6G)
--
--#define DPD_PER_CH_LEGACY_SIZE			(4 * MT_EE_CAL_UNIT)
--#define DPD_PER_CH_MEM_SIZE			(13 * MT_EE_CAL_UNIT)
--#define DPD_PER_CH_OTFG0_SIZE			(2 * MT_EE_CAL_UNIT)
--#define DPD_PER_CH_BW20_SIZE			(DPD_PER_CH_LEGACY_SIZE + DPD_PER_CH_OTFG0_SIZE)
--#define DPD_PER_CH_GT_BW20_SIZE			(DPD_PER_CH_MEM_SIZE + DPD_PER_CH_OTFG0_SIZE)
--#define MT_EE_CAL_DPD_SIZE			(780 * MT_EE_CAL_UNIT)
-+
-+enum mt7996_prek_rev {
-+	GROUP_SIZE_2G,
-+	GROUP_SIZE_5G,
-+	GROUP_SIZE_6G,
-+	ADCDCOC_SIZE_2G,
-+	ADCDCOC_SIZE_5G,
-+	ADCDCOC_SIZE_6G,
-+	DPD_LEGACY_SIZE,
-+	DPD_MEM_SIZE,
-+	DPD_OTFG0_SIZE,
-+};
-+
-+static const u32 mt7996_prek_rev[] = {
-+	[GROUP_SIZE_2G] =			4 * MT_EE_CAL_UNIT,
-+	[GROUP_SIZE_5G] =			45 * MT_EE_CAL_UNIT,
-+	[GROUP_SIZE_6G] =			125 * MT_EE_CAL_UNIT,
-+	[ADCDCOC_SIZE_2G] =			4 * 4,
-+	[ADCDCOC_SIZE_5G] =			4 * 4,
-+	[ADCDCOC_SIZE_6G] =			4 * 5,
-+	[DPD_LEGACY_SIZE] =			4 * MT_EE_CAL_UNIT,
-+	[DPD_MEM_SIZE] =			13 * MT_EE_CAL_UNIT,
-+	[DPD_OTFG0_SIZE] =			2 * MT_EE_CAL_UNIT,
-+};
-+
-+/* kite 2/5g config */
-+static const u32 mt7992_prek_rev[] = {
-+	[GROUP_SIZE_2G] =			4 * MT_EE_CAL_UNIT,
-+	[GROUP_SIZE_5G] =			110 * MT_EE_CAL_UNIT,
-+	[GROUP_SIZE_6G] =			0,
-+	[ADCDCOC_SIZE_2G] =			4 * 4,
-+	[ADCDCOC_SIZE_5G] =			4 * 5,
-+	[ADCDCOC_SIZE_6G] =			0,
-+	[DPD_LEGACY_SIZE] =			5 * MT_EE_CAL_UNIT,
-+	[DPD_MEM_SIZE] =			16 * MT_EE_CAL_UNIT,
-+	[DPD_OTFG0_SIZE] =			2 * MT_EE_CAL_UNIT,
-+};
- 
- extern const struct ieee80211_channel dpd_2g_ch_list_bw20[];
--extern const u32 dpd_2g_bw20_ch_num;
- extern const struct ieee80211_channel dpd_5g_skip_ch_list[];
--extern const u32 dpd_5g_skip_ch_num;
-+extern const struct ieee80211_channel dpd_5g_ch_list_bw80[];
- extern const struct ieee80211_channel dpd_5g_ch_list_bw160[];
--extern const u32 dpd_5g_bw160_ch_num;
-+extern const struct ieee80211_channel dpd_6g_ch_list_bw80[];
- extern const struct ieee80211_channel dpd_6g_ch_list_bw160[];
--extern const u32 dpd_6g_bw160_ch_num;
- extern const struct ieee80211_channel dpd_6g_ch_list_bw320[];
--extern const u32 dpd_6g_bw320_ch_num;
-+
-+#define PREK(id)				(dev->prek.rev[(id)])
-+#define DPD_CH_NUM(_type)			(dev->prek.dpd_ch_num[DPD_CH_NUM_##_type])
-+#define MT_EE_CAL_GROUP_SIZE			(PREK(GROUP_SIZE_2G) + PREK(GROUP_SIZE_5G) + \
-+						 PREK(GROUP_SIZE_6G) + PREK(ADCDCOC_SIZE_2G) + \
-+						 PREK(ADCDCOC_SIZE_5G) + PREK(ADCDCOC_SIZE_6G))
-+#define DPD_PER_CH_BW20_SIZE			(PREK(DPD_LEGACY_SIZE) + PREK(DPD_OTFG0_SIZE))
-+#define DPD_PER_CH_GT_BW20_SIZE			(PREK(DPD_MEM_SIZE) + PREK(DPD_OTFG0_SIZE))
-+#define MT_EE_CAL_DPD_SIZE_2G			(DPD_CH_NUM(BW20_2G) * DPD_PER_CH_BW20_SIZE)
-+#define MT_EE_CAL_DPD_SIZE_5G			(DPD_CH_NUM(BW20_5G) * DPD_PER_CH_BW20_SIZE + \
-+						 DPD_CH_NUM(BW80_5G) * DPD_PER_CH_GT_BW20_SIZE + \
-+						 DPD_CH_NUM(BW160_5G) * DPD_PER_CH_GT_BW20_SIZE)
-+#define MT_EE_CAL_DPD_SIZE_6G			(DPD_CH_NUM(BW20_6G) * DPD_PER_CH_BW20_SIZE + \
-+						 DPD_CH_NUM(BW80_6G) * DPD_PER_CH_GT_BW20_SIZE + \
-+						 DPD_CH_NUM(BW160_6G) * DPD_PER_CH_GT_BW20_SIZE + \
-+						 DPD_CH_NUM(BW320_6G) * DPD_PER_CH_GT_BW20_SIZE)
-+#define MT_EE_CAL_DPD_SIZE			(MT_EE_CAL_DPD_SIZE_2G + MT_EE_CAL_DPD_SIZE_5G + \
-+						 MT_EE_CAL_DPD_SIZE_6G)
- 
- #define RF_DPD_FLAT_CAL				BIT(28)
- #define RF_PRE_CAL				BIT(29)
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 6f08161..528bce8 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -3735,13 +3735,11 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
- 	enum nl80211_chan_width bw = chandef->width;
- 	const struct ieee80211_channel *chan_list;
- 	u32 cal_id, chan_list_size, base_offset = 0, offs = MT_EE_DO_PRE_CAL;
--	u32 dpd_size_2g, dpd_size_5g, per_chan_size = DPD_PER_CH_BW20_SIZE;
-+	u32 per_chan_size = DPD_PER_CH_BW20_SIZE;
- 	u16 channel = ieee80211_frequency_to_channel(chandef->center_freq1);
- 	u8 dpd_mask, *cal = dev->cal, *eeprom = dev->mt76.eeprom.data;
- 	int idx, i, ret;
--
--	dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
--	dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
-+	bool has_skip_ch = (band == NL80211_BAND_5GHZ);
- 
- 	switch (band) {
- 	case NL80211_BAND_2GHZ:
-@@ -3757,27 +3755,35 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
- 			return 0;
- 		cal_id = RF_DPD_FLAT_CAL;
- 		chan_list = dpd_2g_ch_list_bw20;
--		chan_list_size = dpd_2g_bw20_ch_num;
-+		chan_list_size = DPD_CH_NUM(BW20_2G);
- 		break;
- 	case NL80211_BAND_5GHZ:
- 		dpd_mask = MT_EE_WIFI_CAL_DPD_5G;
- 		cal_id = RF_DPD_FLAT_5G_CAL;
- 		chan_list = mphy->sband_5g.sband.channels;
- 		chan_list_size = mphy->sband_5g.sband.n_channels;
--		base_offset += dpd_size_2g;
-+		base_offset += MT_EE_CAL_DPD_SIZE_2G;
- 		if (bw == NL80211_CHAN_WIDTH_160) {
--			base_offset += (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
--				       DPD_PER_CH_BW20_SIZE;
-+			base_offset += DPD_CH_NUM(BW20_5G) * DPD_PER_CH_BW20_SIZE +
-+				       DPD_CH_NUM(BW80_5G) * DPD_PER_CH_GT_BW20_SIZE;
- 			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
- 			cal_id = RF_DPD_FLAT_5G_MEM_CAL;
- 			chan_list = dpd_5g_ch_list_bw160;
--			chan_list_size = dpd_5g_bw160_ch_num;
-+			chan_list_size = DPD_CH_NUM(BW160_5G);
-+			has_skip_ch = false;
-+		} else if (is_mt7992(&dev->mt76) && bw == NL80211_CHAN_WIDTH_80) {
-+			base_offset += DPD_CH_NUM(BW20_5G) * DPD_PER_CH_BW20_SIZE;
-+			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
-+			cal_id = RF_DPD_FLAT_5G_MEM_CAL;
-+			chan_list = dpd_5g_ch_list_bw80;
-+			chan_list_size = DPD_CH_NUM(BW80_5G);
-+			has_skip_ch = false;
- 		} else if (bw > NL80211_CHAN_WIDTH_20) {
- 			/* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
- 			channel -= 2;
- 		}
- 		if (channel >= dpd_5g_skip_ch_list[0].hw_value &&
--		    channel <= dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
-+		    channel <= dpd_5g_skip_ch_list[DPD_CH_NUM(BW20_5G_SKIP) - 1].hw_value)
- 			return 0;
- 		break;
- 	case NL80211_BAND_6GHZ:
-@@ -3785,20 +3791,27 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
- 		cal_id = RF_DPD_FLAT_6G_CAL;
- 		chan_list = mphy->sband_6g.sband.channels;
- 		chan_list_size = mphy->sband_6g.sband.n_channels;
--		base_offset += dpd_size_2g + dpd_size_5g;
-+		base_offset += MT_EE_CAL_DPD_SIZE_2G + MT_EE_CAL_DPD_SIZE_5G;
- 		if (bw == NL80211_CHAN_WIDTH_160) {
- 			base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
- 			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
- 			cal_id = RF_DPD_FLAT_6G_MEM_CAL;
- 			chan_list = dpd_6g_ch_list_bw160;
--			chan_list_size = dpd_6g_bw160_ch_num;
--		} else if (bw == NL80211_CHAN_WIDTH_320) {
-+			chan_list_size = DPD_CH_NUM(BW160_6G);
-+		} else if (is_mt7996(&dev->mt76) && bw == NL80211_CHAN_WIDTH_320) {
- 			base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
--				       dpd_6g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
-+				       DPD_CH_NUM(BW80_6G) * DPD_PER_CH_GT_BW20_SIZE +
-+				       DPD_CH_NUM(BW160_6G) * DPD_PER_CH_GT_BW20_SIZE;
- 			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
- 			cal_id = RF_DPD_FLAT_6G_MEM_CAL;
- 			chan_list = dpd_6g_ch_list_bw320;
--			chan_list_size = dpd_6g_bw320_ch_num;
-+			chan_list_size = DPD_CH_NUM(BW320_6G);
-+		} else if (is_mt7992(&dev->mt76) && bw == NL80211_CHAN_WIDTH_80) {
-+			base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
-+			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
-+			cal_id = RF_DPD_FLAT_6G_MEM_CAL;
-+			chan_list = dpd_6g_ch_list_bw80;
-+			chan_list_size = DPD_CH_NUM(BW80_6G);
- 		} else if (bw > NL80211_CHAN_WIDTH_20) {
- 			/* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
- 			channel -= 2;
-@@ -3818,9 +3831,8 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
- 	if (idx == chan_list_size)
- 		return -EINVAL;
- 
--	if (band == NL80211_BAND_5GHZ && bw != NL80211_CHAN_WIDTH_160 &&
--	    channel > dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
--		idx -= dpd_5g_skip_ch_num;
-+	if (has_skip_ch && channel > dpd_5g_skip_ch_list[DPD_CH_NUM(BW20_5G_SKIP) - 1].hw_value)
-+		idx -= DPD_CH_NUM(BW20_5G_SKIP);
- 
- 	cal += MT_EE_CAL_GROUP_SIZE + base_offset + idx * per_chan_size;
- 
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 7f7a622..0ebeb24 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -194,6 +194,19 @@ struct mt7996_twt_flow {
- 
- DECLARE_EWMA(avg_signal, 10, 8)
- 
-+enum mt7996_dpd_ch_num {
-+	DPD_CH_NUM_BW20_2G,
-+	DPD_CH_NUM_BW20_5G,
-+	DPD_CH_NUM_BW20_5G_SKIP,
-+	DPD_CH_NUM_BW80_5G,
-+	DPD_CH_NUM_BW160_5G,
-+	DPD_CH_NUM_BW20_6G,
-+	DPD_CH_NUM_BW80_6G,
-+	DPD_CH_NUM_BW160_6G,
-+	DPD_CH_NUM_BW320_6G,
-+	DPD_CH_NUM_TYPE_MAX,
-+};
-+
- struct mt7996_sta {
- 	struct mt76_wcid wcid; /* must be first */
- 
-@@ -463,6 +476,10 @@ struct mt7996_dev {
- 
- 	void *cal;
- 	u32 cur_prek_offset;
-+	struct {
-+		const u32 *rev;
-+		u8 dpd_ch_num[DPD_CH_NUM_TYPE_MAX];
-+	} prek;
- 
- 	struct {
- 		u16 table_mask;
-@@ -599,7 +616,6 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
- int mt7996_eeprom_get_target_power(struct mt7996_dev *dev,
- 				   struct ieee80211_channel *chan);
- s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band);
--int mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band);
- int mt7996_dma_init(struct mt7996_dev *dev);
- void mt7996_dma_reset(struct mt7996_dev *dev, bool force);
- void mt7996_dma_prefetch(struct mt7996_dev *dev);
-diff --git a/mt7996/testmode.c b/mt7996/testmode.c
-index 95d3bde..9fa4edc 100644
---- a/mt7996/testmode.c
-+++ b/mt7996/testmode.c
-@@ -434,7 +434,7 @@ mt7996_tm_set_tx_cont(struct mt7996_phy *phy, bool en)
- static int
- mt7996_tm_group_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
- {
--	u8 *eeprom;
-+	u8 *eeprom, do_precal;
- 	u32 i, group_size, dpd_size, size, offs, *pre_cal;
- 	int ret = 0;
- 	struct mt7996_dev *dev = phy->dev;
-@@ -462,6 +462,9 @@ mt7996_tm_group_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
- 	dpd_size = MT_EE_CAL_DPD_SIZE;
- 	size = group_size + dpd_size;
- 	offs = MT_EE_DO_PRE_CAL;
-+	do_precal = (MT_EE_WIFI_CAL_GROUP_2G * !!PREK(GROUP_SIZE_2G)) |
-+		    (MT_EE_WIFI_CAL_GROUP_5G * !!PREK(GROUP_SIZE_5G)) |
-+		    (MT_EE_WIFI_CAL_GROUP_6G * !!PREK(GROUP_SIZE_6G));
- 
- 	switch (state) {
- 	case MT76_TM_STATE_GROUP_PREK:
-@@ -476,13 +479,10 @@ mt7996_tm_group_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
- 		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == group_size,
- 				   30 * HZ);
- 
--		if (ret) {
-+		if (ret)
- 			dev_err(dev->mt76.dev, "Group Pre-cal: mcu send msg failed!\n");
--			return ret;
--		}
--
--		if (!ret)
--			eeprom[offs] |= MT_EE_WIFI_CAL_GROUP;
-+		else
-+			eeprom[offs] |= do_precal;
- 		break;
- 	case MT76_TM_STATE_GROUP_PREK_DUMP:
- 		pre_cal = (u32 *)dev->cal;
-@@ -520,10 +520,12 @@ mt7996_tm_dpd_prek_send_req(struct mt7996_phy *phy, struct mt7996_tm_req *req,
- 	struct mt76_phy *mphy = phy->mt76;
- 	struct cfg80211_chan_def chandef_backup, *chandef = &mphy->chandef;
- 	struct ieee80211_channel chan_backup;
--	int i, ret;
-+	int i, ret, skip_ch_num = DPD_CH_NUM(BW20_5G_SKIP);
- 
- 	if (!chan_list)
- 		return -EOPNOTSUPP;
-+	if (!channel_size)
-+		return 0;
- 
- 	req->rf_test.op.rf.param.cal_param.func_data = cpu_to_le32(func_data);
- 
-@@ -533,7 +535,7 @@ mt7996_tm_dpd_prek_send_req(struct mt7996_phy *phy, struct mt7996_tm_req *req,
- 	for (i = 0; i < channel_size; i++) {
- 		if (chan_list[i].band == NL80211_BAND_5GHZ &&
- 		    chan_list[i].hw_value >= dpd_5g_skip_ch_list[0].hw_value &&
--		    chan_list[i].hw_value <= dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
-+		    chan_list[i].hw_value <= dpd_5g_skip_ch_list[skip_ch_num - 1].hw_value)
- 			continue;
- 
- 		memcpy(chandef->chan, &chan_list[i], sizeof(struct ieee80211_channel));
-@@ -602,11 +604,11 @@ mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
- 	switch (state) {
- 	case MT76_TM_STATE_DPD_2G:
- 		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_2g_ch_list_bw20,
--						  dpd_2g_bw20_ch_num,
-+						  DPD_CH_NUM(BW20_2G),
- 						  NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_CAL);
--		wait_on_prek_offset += dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
--		wait_event_timeout(mdev->mcu.wait,
--				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+		wait_on_prek_offset += DPD_CH_NUM(BW20_2G) * DPD_PER_CH_BW20_SIZE;
-+		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
-+				   30 * HZ);
- 
- 		do_precal = MT_EE_WIFI_CAL_DPD_2G;
- 		break;
-@@ -617,18 +619,27 @@ mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
- 						  NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_5G_CAL);
- 		if (ret)
- 			return ret;
--		wait_on_prek_offset += (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
--				       DPD_PER_CH_BW20_SIZE;
--		wait_event_timeout(mdev->mcu.wait,
--				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+		wait_on_prek_offset += DPD_CH_NUM(BW20_5G) * DPD_PER_CH_BW20_SIZE;
-+		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
-+				   30 * HZ);
-+
-+		/* 5g channel bw80 calibration */
-+		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_5g_ch_list_bw80,
-+						  DPD_CH_NUM(BW80_5G),
-+						  NL80211_CHAN_WIDTH_80, RF_DPD_FLAT_5G_MEM_CAL);
-+		if (ret)
-+			return ret;
-+		wait_on_prek_offset += DPD_CH_NUM(BW80_5G) * DPD_PER_CH_GT_BW20_SIZE;
-+		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
-+				   30 * HZ);
- 
- 		/* 5g channel bw160 calibration */
- 		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_5g_ch_list_bw160,
--						  dpd_5g_bw160_ch_num,
-+						  DPD_CH_NUM(BW160_5G),
- 						  NL80211_CHAN_WIDTH_160, RF_DPD_FLAT_5G_MEM_CAL);
--		wait_on_prek_offset += dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
--		wait_event_timeout(mdev->mcu.wait,
--				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+		wait_on_prek_offset += DPD_CH_NUM(BW160_5G) * DPD_PER_CH_GT_BW20_SIZE;
-+		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
-+				   30 * HZ);
- 
- 		do_precal = MT_EE_WIFI_CAL_DPD_5G;
- 		break;
-@@ -639,27 +650,37 @@ mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
- 						  NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_6G_CAL);
- 		if (ret)
- 			return ret;
--		wait_on_prek_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
--		wait_event_timeout(mdev->mcu.wait,
--				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+		wait_on_prek_offset += DPD_CH_NUM(BW20_6G) * DPD_PER_CH_BW20_SIZE;
-+		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
-+				   30 * HZ);
-+
-+		/* 6g channel bw80 calibration */
-+		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw80,
-+						  DPD_CH_NUM(BW80_6G),
-+						  NL80211_CHAN_WIDTH_80, RF_DPD_FLAT_6G_MEM_CAL);
-+		if (ret)
-+			return ret;
-+		wait_on_prek_offset += DPD_CH_NUM(BW80_6G) * DPD_PER_CH_GT_BW20_SIZE;
-+		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
-+				   30 * HZ);
- 
- 		/* 6g channel bw160 calibration */
- 		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw160,
--						  dpd_6g_bw160_ch_num,
-+						  DPD_CH_NUM(BW160_6G),
- 						  NL80211_CHAN_WIDTH_160, RF_DPD_FLAT_6G_MEM_CAL);
- 		if (ret)
- 			return ret;
--		wait_on_prek_offset += dpd_6g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
--		wait_event_timeout(mdev->mcu.wait,
--				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+		wait_on_prek_offset += DPD_CH_NUM(BW160_6G) * DPD_PER_CH_GT_BW20_SIZE;
-+		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
-+				   30 * HZ);
- 
- 		/* 6g channel bw320 calibration */
- 		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw320,
--						  dpd_6g_bw320_ch_num,
-+						  DPD_CH_NUM(BW320_6G),
- 						  NL80211_CHAN_WIDTH_320, RF_DPD_FLAT_6G_MEM_CAL);
--		wait_on_prek_offset += dpd_6g_bw320_ch_num * DPD_PER_CH_GT_BW20_SIZE;
--		wait_event_timeout(mdev->mcu.wait,
--				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
-+		wait_on_prek_offset += DPD_CH_NUM(BW320_6G) * DPD_PER_CH_GT_BW20_SIZE;
-+		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
-+				   30 * HZ);
- 
- 		do_precal = MT_EE_WIFI_CAL_DPD_6G;
- 		break;
-@@ -732,9 +753,9 @@ mt7996_tm_dump_precal(struct mt76_phy *mphy, struct sk_buff *msg, int flag, int
- 	eeprom = dev->mt76.eeprom.data;
- 	offs = MT_EE_DO_PRE_CAL;
- 
--	dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
--	dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
--	dpd_size_6g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_6GHZ);
-+	dpd_size_2g = MT_EE_CAL_DPD_SIZE_2G;
-+	dpd_size_5g = MT_EE_CAL_DPD_SIZE_5G;
-+	dpd_size_6g = MT_EE_CAL_DPD_SIZE_6G;
- 
- 	switch (type) {
- 	case PREK_SYNC_ALL:
-@@ -810,9 +831,9 @@ mt7996_tm_re_cal_event(struct mt7996_dev *dev, struct mt7996_tm_rf_test_result *
- 	u8 *pre_cal;
- 
- 	pre_cal = dev->cal;
--	dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
--	dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
--	dpd_size_6g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_6GHZ);
-+	dpd_size_2g = MT_EE_CAL_DPD_SIZE_2G;
-+	dpd_size_5g = MT_EE_CAL_DPD_SIZE_5G;
-+	dpd_size_6g = MT_EE_CAL_DPD_SIZE_6G;
- 
- 	cal_idx = le32_to_cpu(data->cal_idx);
- 	cal_type = le32_to_cpu(data->cal_type);
-diff --git a/testmode.c b/testmode.c
-index b9f7109..2dd184e 100644
---- a/testmode.c
-+++ b/testmode.c
-@@ -37,6 +37,11 @@ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
- };
- EXPORT_SYMBOL_GPL(mt76_tm_policy);
- 
-+static inline bool mt76_testmode_offload(struct mt76_dev *dev)
-+{
-+	return is_mt7996(dev) || is_mt7992(dev);
-+}
-+
- void mt76_testmode_tx_pending(struct mt76_phy *phy)
- {
- 	struct mt76_testmode_data *td = &phy->test;
-@@ -197,7 +202,7 @@ mt76_testmode_tx_init(struct mt76_phy *phy)
- 	u8 max_nss = hweight8(phy->antenna_mask);
- 	int ret;
- 
--	if (is_mt7996(phy->dev))
-+	if (mt76_testmode_offload(phy->dev))
- 		return 0;
- 
- 	ret = mt76_testmode_alloc_skb(phy, td->tx_mpdu_len);
-@@ -293,7 +298,7 @@ mt76_testmode_tx_start(struct mt76_phy *phy)
- 	td->tx_done = 0;
- 	td->tx_pending = td->tx_count;
- 
--	if (!is_mt7996(dev))
-+	if (!mt76_testmode_offload(dev))
- 		mt76_worker_schedule(&dev->tx_worker);
- }
- 
-@@ -303,7 +308,7 @@ mt76_testmode_tx_stop(struct mt76_phy *phy)
- 	struct mt76_testmode_data *td = &phy->test;
- 	struct mt76_dev *dev = phy->dev;
- 
--	if (is_mt7996(dev) && dev->test_ops->tx_stop) {
-+	if (mt76_testmode_offload(dev) && dev->test_ops->tx_stop) {
- 		dev->test_ops->tx_stop(phy);
- 		return;
- 	}
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0049-mtk-mt76-mt7996-add-cert-patch.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0049-mtk-mt76-mt7996-add-cert-patch.patch
new file mode 100644
index 0000000..9a2f167
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0049-mtk-mt76-mt7996-add-cert-patch.patch
@@ -0,0 +1,1113 @@
+From 737ee81737fcefd3abbab0bb628fa9db193b1b34 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Mon, 14 Aug 2023 13:36:58 +0800
+Subject: [PATCH 049/199] mtk: mt76: mt7996: add cert patch
+
+This patch includes TGac and TGax
+
+Commit histroy:
+
+Add vendor cmd set ap wireless rts_sigta support
+
+In Wi-Fi 7 UCC, there are a command without a specified interface,
+causing the AP to send trigger frames on only one link. When testing
+EMLSR+OFDMA test cases (EHT-4.45.1), this results in the AP sending
+trigger frames on only one link, leading to test failure.
+Therefore, the band_bitmap is corrected to ensure that trigger frames are sent on all links.
+
+The UCC command specifies an interface:
+ap_set_rfeature,NAME,Wi-Fi7APUT,Interface,5G,type,EHT,TriggerType,0,
+Trigger_Variant,HE,PPDUTxType,legacy
+
+The UCC command does not specify an interface:
+ap_set_rfeature,NAME,Wi-Fi7APUT,type,EHT,TriggerType,0,Trigger_Variant,
+EHT,PPDUTxType,legacy
+
+Logan set the band_bitmap to 7 in Wi-Fi 7 regardless of whether the UCC
+command specifies an interface.
+
+Signed-off-by: ye he <ye.he@mediatek.com>
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ mt7996/mac.c     |   9 ++
+ mt7996/main.c    |  31 ++++++-
+ mt7996/mcu.c     |  40 +++++++++
+ mt7996/mcu.h     |   6 ++
+ mt7996/mt7996.h  |  13 +++
+ mt7996/mtk_mcu.c | 206 ++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mtk_mcu.h | 184 +++++++++++++++++++++++++++++++++++--
+ mt7996/vendor.c  | 230 ++++++++++++++++++++++++++++++++++++++++++++++-
+ mt7996/vendor.h  |  67 ++++++++++++++
+ 9 files changed, 779 insertions(+), 7 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 63408421..90e8e7b1 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -10,6 +10,7 @@
+ #include "../dma.h"
+ #include "mac.h"
+ #include "mcu.h"
++#include "vendor.h"
+ 
+ #define to_rssi(field, rcpi)	((FIELD_GET(field, rcpi) - 220) / 2)
+ 
+@@ -2284,6 +2285,14 @@ void mt7996_mac_update_stats(struct mt7996_phy *phy)
+ 	}
+ }
+ 
++void mt7996_set_wireless_amsdu(struct ieee80211_hw *hw, u8 en)
++{
++	if (en)
++		ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
++	else
++		ieee80211_hw_clear(hw, SUPPORTS_AMSDU_IN_AMPDU);
++}
++
+ void mt7996_mac_sta_rc_work(struct work_struct *work)
+ {
+ 	struct mt7996_dev *dev = container_of(work, struct mt7996_dev, rc_work);
+diff --git a/mt7996/main.c b/mt7996/main.c
+index f7819a85..39318595 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -617,6 +617,7 @@ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 		       bool beacon, bool mcast)
+ {
+ 	struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt76_phy *mphy = hw->priv;
+ 	u16 rate;
+ 	u8 i, idx;
+@@ -626,6 +627,9 @@ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	if (beacon) {
+ 		struct mt7996_phy *phy = mphy->priv;
+ 
++		if (dev->cert_mode && phy->mt76->band_idx == MT_BAND2)
++			rate = 0x0200;
++
+ 		/* odd index for driver, even index for firmware */
+ 		idx = MT7996_BEACON_RATES_TBL + 2 * phy->mt76->band_idx;
+ 		if (phy->beacon_rate != rate)
+@@ -753,6 +757,10 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 	u8 band_idx = mvif->phy->mt76->band_idx;
+ 	int ret, idx;
+ 
++#ifdef CONFIG_MTK_VENDOR
++	struct mt7996_phy *phy = &dev->phy;
++#endif
++
+ 	idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
+ 	if (idx < 0)
+ 		return -ENOSPC;
+@@ -778,7 +786,28 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 	if (ret)
+ 		return ret;
+ 
+-	return mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
++	ret = mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
++	if (ret)
++		return ret;
++
++#ifdef CONFIG_MTK_VENDOR
++	switch (band_idx) {
++	case MT_BAND1:
++		phy = mt7996_phy2(dev);
++		break;
++	case MT_BAND2:
++		phy = mt7996_phy3(dev);
++		break;
++	case MT_BAND0:
++	default:
++		break;
++	}
++
++	if (phy && phy->muru_onoff & MUMIMO_DL_CERT)
++		mt7996_mcu_set_mimo(phy);
++#endif
++
++	return 0;
+ }
+ 
+ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index c47dee02..ae894ac9 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1352,6 +1352,10 @@ mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ {
+ 	struct sta_rec_vht *vht;
+ 	struct tlv *tlv;
++#ifdef CONFIG_MTK_VENDOR
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_phy *phy = (struct mt7996_phy *)msta->vif->phy;
++#endif
+ 
+ 	/* For 6G band, this tlv is necessary to let hw work normally */
+ 	if (!sta->deflink.he_6ghz_capa.capa && !sta->deflink.vht_cap.vht_supported)
+@@ -1363,6 +1367,9 @@ mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ 	vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap);
+ 	vht->vht_rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map;
+ 	vht->vht_tx_mcs_map = sta->deflink.vht_cap.vht_mcs.tx_mcs_map;
++#ifdef CONFIG_MTK_VENDOR
++	vht->rts_bw_sig = phy->rts_bw_sig;
++#endif
+ }
+ 
+ static void
+@@ -4443,6 +4450,27 @@ int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val)
+ 				 &req, sizeof(req), true);
+ }
+ 
++int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable)
++{
++	struct {
++		u8 band_idx;
++		u8 _rsv[3];
++
++		__le16 tag;
++		__le16 len;
++		bool enable;
++		u8 _rsv2[3];
++	} __packed req = {
++		.band_idx = phy->mt76->band_idx,
++		.tag = cpu_to_le16(option),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.enable = enable,
++	};
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
++				 &req, sizeof(req), true);
++}
++
+ int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable)
+ {
+ 	struct {
+@@ -5010,6 +5038,18 @@ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ 	val = FIELD_GET(RATE_CFG_VAL, *((u32 *)data));
+ 
+ 	switch (mode) {
++	case RATE_PARAM_FIXED_OFDMA:
++		if (val == 3)
++			phy->muru_onoff |= OFDMA_DL;
++		else
++			phy->muru_onoff |= val;
++		break;
++	case RATE_PARAM_FIXED_MIMO:
++		if (val == 0)
++			phy->muru_onoff |= MUMIMO_DL_CERT | MUMIMO_DL;
++		else
++			phy->muru_onoff |= MUMIMO_UL;
++		break;
+ 	case RATE_PARAM_AUTO_MU:
+ 		if (val < 0 || val > 15) {
+ 			printk("Wrong value! The value is between 0-15.\n");
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index a98b174e..2546354e 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -755,6 +755,8 @@ enum {
+ 	RATE_PARAM_FIXED_GI = 11,
+ 	RATE_PARAM_AUTO = 20,
+ #ifdef CONFIG_MTK_VENDOR
++	RATE_PARAM_FIXED_MIMO = 30,
++	RATE_PARAM_FIXED_OFDMA = 31,
+ 	RATE_PARAM_AUTO_MU = 32,
+ #endif
+ };
+@@ -767,6 +769,7 @@ enum {
+ #define OFDMA_UL                       BIT(1)
+ #define MUMIMO_DL                      BIT(2)
+ #define MUMIMO_UL                      BIT(3)
++#define MUMIMO_DL_CERT                 BIT(4)
+ 
+ enum {
+ 	BF_SOUNDING_ON = 1,
+@@ -853,11 +856,14 @@ enum {
+ 	UNI_BAND_CONFIG_EDCCA_ENABLE = 0x05,
+ 	UNI_BAND_CONFIG_EDCCA_THRESHOLD = 0x06,
+ 	UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08,
++	UNI_BAND_CONFIG_RTS_SIGTA_EN = 0x09,
++	UNI_BAND_CONFIG_DIS_SECCH_CCA_DET = 0x0a,
+ };
+ 
+ enum {
+ 	UNI_WSYS_CONFIG_FW_LOG_CTRL,
+ 	UNI_WSYS_CONFIG_FW_DBG_CTRL,
++	UNI_CMD_CERT_CFG = 6,
+ };
+ 
+ enum {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 45ce7db7..00d7991b 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -354,6 +354,7 @@ struct mt7996_phy {
+ 	} test;
+ #endif
+ #ifdef CONFIG_MTK_VENDOR
++	u8 rts_bw_sig;
+ 	spinlock_t amnt_lock;
+ 	struct mt7996_air_monitor_ctrl amnt_ctrl;
+ #endif
+@@ -482,6 +483,9 @@ struct mt7996_dev {
+ 	} dbg;
+ 	const struct mt7996_dbg_reg_desc *dbg_reg;
+ #endif
++#ifdef CONFIG_MTK_VENDOR
++	bool cert_mode;
++#endif
+ };
+ 
+ enum {
+@@ -679,6 +683,7 @@ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
+ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
+ void mt7996_mcu_scs_sta_poll(struct work_struct *work);
++int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable);
+ 
+ static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
+ {
+@@ -797,6 +802,10 @@ void mt7996_vendor_register(struct mt7996_phy *phy);
+ void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb);
+ int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy,
+ 				  struct ieee80211_sta *sta);
++void mt7996_set_wireless_amsdu(struct ieee80211_hw *hw, u8 en);
++void mt7996_mcu_set_mimo(struct mt7996_phy *phy);
++int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val);
++int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data);
+ #endif
+ 
+ int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
+@@ -824,6 +833,10 @@ int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para);
+ int mt7996_mcu_set_muru_cmd(struct mt7996_dev *dev, u16 action, int val);
+ int mt7996_mcu_muru_set_prot_frame_thr(struct mt7996_dev *dev, u32 val);
+ int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val);
++int mt7996_mcu_set_rfeature_trig_type(struct mt7996_phy *phy, u8 enable, u8 trig_type);
++void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type);
++void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 ofdma_user_cnt);
++void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type);
+ #endif
+ 
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index 68650623..30da79f4 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -982,4 +982,210 @@ int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val)
+ 				 true);
+ }
+ 
++int mt7996_mcu_set_bsrp_ctrl(struct mt7996_phy *phy, u16 interval,
++			     u16 ru_alloc, u32 trig_type, u8 trig_flow, u8 ext_cmd)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct {
++		u8 _rsv[4];
++
++		__le16 tag;
++		__le16 len;
++
++		__le16 interval;
++		__le16 ru_alloc;
++		__le32 trigger_type;
++		u8 trigger_flow;
++		u8 ext_cmd_bsrp;
++		u8 band_bitmap;
++		u8 _rsv2;
++	} __packed req = {
++		.tag = cpu_to_le16(UNI_CMD_MURU_BSRP_CTRL),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.interval = cpu_to_le16(interval),
++		.ru_alloc = cpu_to_le16(ru_alloc),
++		.trigger_type = cpu_to_le32(trig_type),
++		.trigger_flow = trig_flow,
++		.ext_cmd_bsrp = ext_cmd,
++		.band_bitmap = mt7996_band_valid(dev, MT_BAND2) ?
++			       GENMASK(2, 0) : GENMASK(1, 0),
++	};
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
++				 sizeof(req), false);
++}
++
++int mt7996_mcu_set_rfeature_trig_type(struct mt7996_phy *phy, u8 enable, u8 trig_type)
++{
++	struct mt7996_dev *dev = phy->dev;
++	int ret = 0;
++	char buf[] = "01:00:00:1B";
++
++	if (enable) {
++		ret = mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_TRIG_TYPE, trig_type);
++		if (ret)
++			return ret;
++	}
++
++	switch (trig_type) {
++	case CAPI_BASIC:
++		return mt7996_mcu_set_bsrp_ctrl(phy, 5, 67, 0, 0, enable);
++	case CAPI_BRP:
++		return mt7996_mcu_set_txbf_snd_info(phy, buf);
++	case CAPI_MU_BAR:
++		return mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY,
++					       MU_DL_ACK_POLICY_MU_BAR);
++	case CAPI_BSRP:
++		return mt7996_mcu_set_bsrp_ctrl(phy, 5, 67, 4, 0, enable);
++	default:
++		return 0;
++	}
++}
++
++int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct mt7996_muru *muru;
++	struct {
++		u8 _rsv[4];
++
++		__le16 tag;
++		__le16 len;
++
++		u8 version;
++		u8 revision;
++		u8 _rsv2[2];
++
++		struct mt7996_muru muru;
++	} __packed req = {
++		.tag = cpu_to_le16(UNI_CMD_MURU_MUNUAL_CONFIG),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.version = UNI_CMD_MURU_VER_EHT,
++	};
++
++	muru = (struct mt7996_muru *) data;
++	memcpy(&req.muru, muru, sizeof(struct mt7996_muru));
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
++				 sizeof(req), false);
++}
++
++int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val)
++{
++	struct mt7996_muru *muru;
++	struct mt7996_muru_dl *dl;
++	struct mt7996_muru_ul *ul;
++	struct mt7996_muru_comm *comm;
++	int ret = 0;
++
++	muru = kzalloc(sizeof(struct mt7996_muru), GFP_KERNEL);
++	dl = &muru->dl;
++	ul = &muru->ul;
++	comm = &muru->comm;
++
++	switch (action) {
++	case MU_CTRL_DL_USER_CNT:
++		dl->user_num = val;
++		comm->ppdu_format = MURU_PPDU_HE_MU;
++		comm->sch_type = MURU_OFDMA_SCH_TYPE_DL;
++		muru->cfg_comm = cpu_to_le32(MURU_COMM_SET);
++		muru->cfg_dl = cpu_to_le32(MURU_FIXED_DL_TOTAL_USER_CNT);
++		ret = mt7996_mcu_set_muru_cfg(phy, muru);
++		break;
++	case MU_CTRL_UL_USER_CNT:
++		ul->user_num = val;
++		comm->ppdu_format = MURU_PPDU_HE_TRIG;
++		comm->sch_type = MURU_OFDMA_SCH_TYPE_UL;
++		muru->cfg_comm = cpu_to_le32(MURU_COMM_SET);
++		muru->cfg_ul = cpu_to_le32(MURU_FIXED_UL_TOTAL_USER_CNT);
++		ret = mt7996_mcu_set_muru_cfg(phy, muru);
++		break;
++	default:
++		break;
++	}
++
++	kfree(muru);
++	return ret;
++}
++
++void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type)
++{
++	struct mt7996_dev *dev = phy->dev;
++	int enable_su;
++
++	switch (ppdu_type) {
++	case CAPI_SU:
++		enable_su = 1;
++		mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SUTX_CTRL, enable_su);
++		mt7996_set_muru_cfg(phy, MU_CTRL_DL_USER_CNT, 0);
++		break;
++	case CAPI_MU:
++		enable_su = 0;
++		mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SUTX_CTRL, enable_su);
++		break;
++	default:
++		break;
++	}
++}
++
++void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 user_cnt)
++{
++	struct mt7996_dev *dev = phy->dev;
++	int enable_su = 0;
++
++	mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SUTX_CTRL, enable_su);
++	mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY, MU_DL_ACK_POLICY_SU_BAR);
++	mt7996_mcu_muru_set_prot_frame_thr(dev, 9999);
++
++	mt7996_set_muru_cfg(phy, type, user_cnt);
++}
++
++void mt7996_mcu_set_mimo(struct mt7996_phy *phy)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
++	int disable_ra = 1;
++	char buf[] = "2 134 0 1 0 1 2 2 2";
++	int force_mu = 1;
++
++	switch (chandef->width) {
++	case NL80211_CHAN_WIDTH_20_NOHT:
++	case NL80211_CHAN_WIDTH_20:
++		strscpy(buf, "2 122 0 1 0 1 2 2 2", sizeof(buf));
++		break;
++	case NL80211_CHAN_WIDTH_80:
++		break;
++	case NL80211_CHAN_WIDTH_160:
++		strscpy(buf, "2 137 0 1 0 1 2 2 2", sizeof(buf));
++		break;
++	default:
++		break;
++	}
++
++	mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY, MU_DL_ACK_POLICY_SU_BAR);
++	mt7996_mcu_set_muru_fixed_rate_enable(dev, UNI_CMD_MURU_FIXED_RATE_CTRL, disable_ra);
++	mt7996_mcu_set_muru_fixed_rate_parameter(dev, UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL, buf);
++	mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_FORCE_MU, force_mu);
++}
++
++void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct {
++		u8 _rsv[4];
++
++		__le16 tag;
++		__le16 len;
++		u8 action;
++		u8 _rsv2[3];
++	} __packed req = {
++		.tag = cpu_to_le16(UNI_CMD_CERT_CFG),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.action = type, /* 1: CAPI Enable */
++	};
++
++	mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(WSYS_CONFIG), &req,
++			  sizeof(req), false);
++}
++
+ #endif
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+index d9686ebb..7a4140b5 100644
+--- a/mt7996/mtk_mcu.h
++++ b/mt7996/mtk_mcu.h
+@@ -122,14 +122,15 @@ enum {
+ };
+ 
+ enum {
++	UNI_CMD_MURU_BSRP_CTRL = 0x01,
+ 	UNI_CMD_MURU_SUTX_CTRL = 0x10,
+-	UNI_CMD_MURU_FIXED_RATE_CTRL,
+-	UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
++	UNI_CMD_MURU_FIXED_RATE_CTRL = 0x11,
++	UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL = 0x12,
+ 	UNI_CMD_MURU_SET_FORCE_MU = 0x33,
+ 	UNI_CMD_MURU_MUNUAL_CONFIG = 0x64,
+-	UNI_CMD_MURU_SET_MUDL_ACK_POLICY = 0xC9,
+-	UNI_CMD_MURU_SET_TRIG_TYPE,
+-	UNI_CMD_MURU_SET_20M_DYN_ALGO,
++	UNI_CMD_MURU_SET_MUDL_ACK_POLICY = 0xC8,
++	UNI_CMD_MURU_SET_TRIG_TYPE = 0xC9,
++	UNI_CMD_MURU_SET_20M_DYN_ALGO = 0xCA,
+ 	UNI_CMD_MURU_PROT_FRAME_THR = 0xCC,
+ 	UNI_CMD_MURU_SET_CERT_MU_EDCA_OVERRIDE,
+ };
+@@ -533,6 +534,179 @@ struct mt7996_mcu_sr_hw_ind_event {
+ 	__le32 sr_ampdu_mpdu_cnt;
+ 	__le32 sr_ampdu_mpdu_acked_cnt;
+ };
++
++struct mt7996_muru_comm {
++	u8 pda_pol;
++	u8 band;
++	u8 spe_idx;
++	u8 proc_type;
++
++	__le16 mlo_ctrl;
++	u8 sch_type;
++	u8 ppdu_format;
++	u8 ac;
++	u8 _rsv[3];
++};
++
++struct mt7996_muru_dl {
++	u8 user_num;
++	u8 tx_mode;
++	u8 bw;
++	u8 gi;
++
++	u8 ltf;
++	u8 mcs;
++	u8 dcm;
++	u8 cmprs;
++
++	__le16 ru[16];
++
++	u8 c26[2];
++	u8 ack_policy;
++	u8 tx_power;
++
++	__le16 mu_ppdu_duration;
++	u8 agc_disp_order;
++	u8 _rsv1;
++
++	u8 agc_disp_pol;
++	u8 agc_disp_ratio;
++	__le16 agc_disp_linkMFG;
++
++	__le16 prmbl_punc_bmp;
++	u8 _rsv2[2];
++
++	struct {
++		__le16 wlan_idx;
++		u8 ru_alloc_seg;
++		u8 ru_idx;
++		u8 ldpc;
++		u8 nss;
++		u8 mcs;
++		u8 mu_group_idx;
++		u8 vht_groud_id;
++		u8 vht_up;
++		u8 he_start_stream;
++		u8 he_mu_spatial;
++		__le16 tx_power_alpha;
++		u8 ack_policy;
++		u8 ru_allo_ps160;
++	} usr[16];
++};
++
++struct mt7996_muru_ul {
++	u8 user_num;
++	u8 tx_mode;
++
++	u8 ba_type;
++	u8 _rsv;
++
++	u8 bw;
++	u8 gi_ltf;
++	__le16 ul_len;
++
++	__le16 trig_cnt;
++	u8 pad;
++	u8 trig_type;
++
++	__le16 trig_intv;
++	u8 trig_ta[ETH_ALEN];
++	__le16 ul_ru[16];
++
++	u8 c26[2];
++	__le16 agc_disp_linkMFG;
++
++	u8 agc_disp_mu_len;
++	u8 agc_disp_pol;
++	u8 agc_disp_ratio;
++	u8 agc_disp_pu_idx;
++
++	struct {
++		__le16 wlan_idx;
++		u8 ru_alloc_seg;
++		u8 ru_idx;
++		u8 ldpc;
++		u8 nss;
++		u8 mcs;
++		u8 target_rssi;
++		__le32 trig_pkt_size;
++		u8 ru_allo_ps160;
++		u8 _rsv2[3];
++	} usr[16];
++};
++
++struct mt7996_muru_dbg {
++	/* HE TB RX Debug */
++	__le32 rx_hetb_nonsf_en_bitmap;
++	__le32 rx_hetb_cfg[2];
++};
++
++struct mt7996_muru {
++	__le32 cfg_comm;
++	__le32 cfg_dl;
++	__le32 cfg_ul;
++	__le32 cfg_dbg;
++
++	struct mt7996_muru_comm comm;
++	struct mt7996_muru_dl dl;
++	struct mt7996_muru_ul ul;
++	struct mt7996_muru_dbg dbg;
++};
++
++
++#define MURU_PPDU_HE_TRIG	BIT(2)
++#define MURU_PPDU_HE_MU		BIT(3)
++
++#define MURU_OFDMA_SCH_TYPE_DL	BIT(0)
++#define MURU_OFDMA_SCH_TYPE_UL	BIT(1)
++
++/* Common Config */
++#define MURU_COMM_PPDU_FMT	BIT(0)
++#define MURU_COMM_SCH_TYPE	BIT(1)
++#define MURU_COMM_BAND		BIT(2)
++#define MURU_COMM_WMM		BIT(3)
++#define MURU_COMM_SPE_IDX	BIT(4)
++#define MURU_COMM_PROC_TYPE	BIT(5)
++#define MURU_COMM_SET		(MURU_COMM_PPDU_FMT | MURU_COMM_SCH_TYPE)
++#define MURU_COMM_SET_TM	(MURU_COMM_PPDU_FMT | MURU_COMM_BAND | \
++				 MURU_COMM_WMM | MURU_COMM_SPE_IDX)
++
++/* DL Common config */
++#define MURU_FIXED_DL_TOTAL_USER_CNT	BIT(4)
++
++/* UL Common Config */
++#define MURU_FIXED_UL_TOTAL_USER_CNT	BIT(4)
++
++enum {
++	CAPI_SU,
++	CAPI_MU,
++	CAPI_ER_SU,
++	CAPI_TB,
++	CAPI_LEGACY
++};
++
++enum {
++	CAPI_BASIC,
++	CAPI_BRP,
++	CAPI_MU_BAR,
++	CAPI_MU_RTS,
++	CAPI_BSRP,
++	CAPI_GCR_MU_BAR,
++	CAPI_BQRP,
++	CAPI_NDP_FRP,
++};
++
++enum {
++	MU_DL_ACK_POLICY_MU_BAR = 3,
++	MU_DL_ACK_POLICY_TF_FOR_ACK = 4,
++	MU_DL_ACK_POLICY_SU_BAR = 5,
++};
++
++enum muru_vendor_ctrl {
++	MU_CTRL_UPDATE,
++	MU_CTRL_DL_USER_CNT,
++	MU_CTRL_UL_USER_CNT,
++};
+ #endif
+ 
+ #endif
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index 0d6fa779..7ab64471 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -10,10 +10,31 @@
+ #include "vendor.h"
+ #include "mtk_mcu.h"
+ 
++#ifdef CONFIG_MTK_VENDOR
+ static const struct nla_policy
+ mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
+ 	[MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_MU_CTRL_STRUCT] = {.type = NLA_BINARY },
++};
++
++static const struct nla_policy
++wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE] = {.type = NLA_U16 },
++};
++
++static const struct nla_policy
++wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
++	[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
+ };
+ 
+ static const struct nla_policy
+@@ -76,6 +97,17 @@ pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
+ 	[MTK_VENDOR_ATTR_PP_BAND_IDX] = { .type = NLA_U8 },
+ };
+ 
++static const struct nla_policy
++rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG] = { .type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
++};
++
+ struct mt7996_amnt_data {
+ 	u8 idx;
+ 	u8 addr[ETH_ALEN];
+@@ -90,6 +122,8 @@ static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
+ {
+ 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ 	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_MU_CTRL];
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct mt7996_muru *muru;
+ 	int err;
+ 	u8 val8;
+ 	u32 val32 = 0;
+@@ -105,9 +139,17 @@ static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
+ 			 FIELD_PREP(RATE_CFG_VAL, val8);
+ 		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+ 							   mt7996_set_wireless_vif, &val32);
++	} else if (tb[MTK_VENDOR_ATTR_MU_CTRL_STRUCT]) {
++		muru = kzalloc(sizeof(struct mt7996_muru), GFP_KERNEL);
++
++		nla_memcpy(muru, tb[MTK_VENDOR_ATTR_MU_CTRL_STRUCT],
++			   sizeof(struct mt7996_muru));
++
++		err = mt7996_mcu_set_muru_cfg(phy, muru);
++		kfree(muru);
+ 	}
+ 
+-	return 0;
++	return err;
+ }
+ 
+ static int
+@@ -130,6 +172,48 @@ mt7996_vendor_mu_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
+ 	return len;
+ }
+ 
++void mt7996_set_wireless_rts_sigta(struct ieee80211_hw *hw, u8 value) {
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++
++	switch (value) {
++	case BW_SIGNALING_STATIC:
++	case BW_SIGNALING_DYNAMIC:
++		mt7996_mcu_set_band_confg(phy, UNI_BAND_CONFIG_RTS_SIGTA_EN, true);
++		mt7996_mcu_set_band_confg(phy, UNI_BAND_CONFIG_DIS_SECCH_CCA_DET, false);
++		break;
++	default:
++		value = BW_SIGNALING_DISABLE;
++		mt7996_mcu_set_band_confg(phy, UNI_BAND_CONFIG_RTS_SIGTA_EN, false);
++		mt7996_mcu_set_band_confg(phy, UNI_BAND_CONFIG_DIS_SECCH_CCA_DET, true);
++		break;
++      }
++
++	phy->rts_bw_sig = value;
++
++	/* Set RTS Threshold to a lower Value */
++	mt7996_mcu_set_rts_thresh(phy, 500);
++}
++
++static int
++mt7996_vendor_wireless_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
++				 struct sk_buff *skb, const void *data, int data_len,
++				 unsigned long *storage)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	int len = 0;
++
++	if (*storage == 1)
++		return -ENOENT;
++	*storage = 1;
++
++	if (nla_put_u8(skb, MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU,
++		       ieee80211_hw_check(hw, SUPPORTS_AMSDU_IN_AMPDU)))
++	return -ENOMEM;
++	len += 1;
++
++	return len;
++ }
++
+ void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb)
+ {
+ 	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+@@ -712,6 +796,126 @@ error:
+ 	return -EINVAL;
+ }
+ 
++static int mt7996_vendor_rfeature_ctrl(struct wiphy *wiphy,
++				       struct wireless_dev *wdev,
++				       const void *data,
++				       int data_len)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct mt7996_dev *dev = phy->dev;
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL];
++	int err;
++	u32 val;
++
++	err = nla_parse(tb, MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX, data, data_len,
++			rfeature_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	val = CAPI_RFEATURE_CHANGED;
++
++	if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG]) {
++		u8 enable, trig_type;
++		int rem;
++		struct nlattr *cur;
++
++		nla_for_each_nested(cur, tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG], rem) {
++			switch (nla_type(cur)) {
++			case MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN:
++				enable = nla_get_u8(cur);
++				break;
++			case MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE:
++				trig_type = nla_get_u8(cur);
++				break;
++			default:
++				return -EINVAL;
++			};
++		}
++
++		err = mt7996_mcu_set_rfeature_trig_type(phy, enable, trig_type);
++		if (err)
++			return err;
++	} else if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY]) {
++		u8 ack_policy;
++
++		ack_policy = nla_get_u8(tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY]);
++		switch (ack_policy) {
++		case MU_DL_ACK_POLICY_TF_FOR_ACK:
++			return mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY,
++						       ack_policy);
++		default:
++			return 0;
++		}
++	}
++
++	return 0;
++}
++
++static int mt7996_vendor_wireless_ctrl(struct wiphy *wiphy,
++				       struct wireless_dev *wdev,
++				       const void *data,
++				       int data_len)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct mt7996_dev *dev = phy->dev;
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL];
++	int err;
++	u8 val8;
++	u16 val16;
++	u32 val32;
++
++	err = nla_parse(tb, MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX, data, data_len,
++			wireless_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	val32 = CAPI_WIRELESS_CHANGED;
++
++	if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA]) {
++		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA]);
++		val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_FIXED_OFDMA) |
++			 FIELD_PREP(RATE_CFG_VAL, val8);
++		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
++			mt7996_set_wireless_vif, &val32);
++		if (val8 == 3) /* DL20and80 */
++			mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_20M_DYN_ALGO, 1);
++	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE]) {
++		val16 = nla_get_u16(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE]);
++		hw->max_tx_aggregation_subframes = val16;
++		hw->max_rx_aggregation_subframes = val16;
++	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE]) {
++		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE]);
++		mt7996_mcu_set_ppdu_tx_type(phy, val8);
++	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA]) {
++		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA]);
++		if (phy->muru_onoff & OFDMA_UL)
++			mt7996_mcu_set_nusers_ofdma(phy, MU_CTRL_UL_USER_CNT, val8);
++		else
++			mt7996_mcu_set_nusers_ofdma(phy, MU_CTRL_DL_USER_CNT, val8);
++	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO]) {
++		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO]);
++		val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_FIXED_MIMO) |
++			 FIELD_PREP(RATE_CFG_VAL, val8);
++		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
++			mt7996_set_wireless_vif, &val32);
++	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]) {
++		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]);
++		dev->cert_mode = val8;
++		mt7996_mcu_set_cert(phy, val8);
++		mt7996_mcu_set_bypass_smthint(phy, val8);
++	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU]) {
++		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU]);
++		mt7996_set_wireless_amsdu(hw, val8);
++	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA]) {
++		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA]);
++		mt7996_set_wireless_rts_sigta(hw, val8);
++	}
++
++	return 0;
++}
++
+ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 	{
+ 		.info = {
+@@ -725,6 +929,18 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 		.policy = mu_ctrl_policy,
+ 		.maxattr = MTK_VENDOR_ATTR_MU_CTRL_MAX,
+ 	},
++	{
++		.info = {
++		        .vendor_id = MTK_NL80211_VENDOR_ID,
++		        .subcmd = MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++		        WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.doit = mt7996_vendor_wireless_ctrl,
++		.dumpit = mt7996_vendor_wireless_ctrl_dump,
++		.policy = wireless_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX,
++	},
+ 	{
+ 		.info = {
+ 			.vendor_id = MTK_NL80211_VENDOR_ID,
+@@ -794,6 +1010,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 		.policy = pp_ctrl_policy,
+ 		.maxattr = MTK_VENDOR_ATTR_PP_CTRL_MAX,
+ 	},
++	{
++		.info = {
++			.vendor_id = MTK_NL80211_VENDOR_ID,
++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++			WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.doit = mt7996_vendor_rfeature_ctrl,
++		.policy = rfeature_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX,
++	},
+ };
+ 
+ void mt7996_vendor_register(struct mt7996_phy *phy)
+@@ -803,3 +1030,4 @@ void mt7996_vendor_register(struct mt7996_phy *phy)
+ 
+ 	spin_lock_init(&phy->amnt_lock);
+ }
++#endif
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index 8aaa18ee..2ee1339a 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -3,8 +3,12 @@
+ 
+ #define MTK_NL80211_VENDOR_ID	0x0ce7
+ 
++#ifdef CONFIG_MTK_VENDOR
++
+ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
++	MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
++	MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
+ 	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
+ 	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
+ 	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
+@@ -61,6 +65,7 @@ enum mtk_vendor_attr_mu_ctrl {
+ 
+ 	MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
+ 	MTK_VENDOR_ATTR_MU_CTRL_DUMP,
++	MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
+@@ -68,6 +73,66 @@ enum mtk_vendor_attr_mu_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
+ };
+ 
++enum mtk_capi_control_changed {
++	CAPI_RFEATURE_CHANGED = BIT(16),
++	CAPI_WIRELESS_CHANGED = BIT(17),
++};
++
++enum mtk_vendor_attr_rfeature_ctrl {
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX =
++	NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
++};
++
++enum mtk_vendor_attr_wireless_ctrl {
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT = 9,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_MU_EDCA, /* reserve */
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
++};
++
++enum mtk_vendor_attr_wireless_dump {
++	MTK_VENDOR_ATTR_WIRELESS_DUMP_UNSPEC,
++
++	MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP,
++	MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX =
++		NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
++};
++
++enum bw_sig {
++	BW_SIGNALING_DISABLE,
++	BW_SIGNALING_STATIC,
++	BW_SIGNALING_DYNAMIC
++};
++
+ enum mtk_vendor_attr_mnt_ctrl {
+ 	MTK_VENDOR_ATTR_AMNT_CTRL_UNSPEC,
+ 
+@@ -151,3 +216,5 @@ enum mtk_vendor_attr_pp_ctrl {
+ };
+ 
+ #endif
++
++#endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0049-mtk-wifi-mt76-mt7996-assign-DEAUTH-to-ALTX-queue-for.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0049-mtk-wifi-mt76-mt7996-assign-DEAUTH-to-ALTX-queue-for.patch
deleted file mode 100644
index 5145ced..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0049-mtk-wifi-mt76-mt7996-assign-DEAUTH-to-ALTX-queue-for.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From 6eb483091a4f12b40d201fb2a896a0499ea41100 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Tue, 14 Nov 2023 11:27:06 +0800
-Subject: [PATCH 049/116] mtk: wifi: mt76: mt7996: assign DEAUTH to ALTX queue
- for CERT
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- mt7996/mac.c | 10 ++++++++++
- 1 file changed, 10 insertions(+)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 70f0c56..727d1fb 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -753,6 +753,8 @@ static void
- mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
- 			    struct sk_buff *skb, struct ieee80211_key_conf *key)
- {
-+	struct mt76_phy *mphy =
-+		mt76_dev_phy(&dev->mt76, le32_get_bits(txwi[1], MT_TXD1_TGID));
- 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
- 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-@@ -762,6 +764,14 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
- 	u8 fc_type, fc_stype;
- 	u32 val;
- 
-+	if (ieee80211_is_cert_mode(mphy->hw) && ieee80211_is_deauth(fc)) {
-+		/* In WPA3 cert TC-4.8.1, the deauth must be transmitted without
-+		 * considering PSM bit
-+		 */
-+		txwi[0] &= ~cpu_to_le32(MT_TXD0_Q_IDX);
-+		txwi[0] |= cpu_to_le32(FIELD_PREP(MT_TXD0_Q_IDX, MT_LMAC_ALTX0));
-+	}
-+
- 	if (ieee80211_is_action(fc) &&
- 	    mgmt->u.action.category == WLAN_CATEGORY_BACK &&
- 	    mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ)
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0050-mtk-mt76-mt7996-add-testmode-bf-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0050-mtk-mt76-mt7996-add-testmode-bf-support.patch
new file mode 100644
index 0000000..0ea2133
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0050-mtk-mt76-mt7996-add-testmode-bf-support.patch
@@ -0,0 +1,2059 @@
+From 9d99ad5cd521b8cc92e3fd1d48803cb5b211e281 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 6 Apr 2023 16:40:28 +0800
+Subject: [PATCH 050/199] mtk: mt76: mt7996: add testmode bf support
+
+Add iTest additional bf command
+
+fw return Gx (5g band) ibf cal struct for both 2G and 5G ibf.
+Therefore, memcpy cannot be used for 2G ibf cal.
+https://gerrit.mediatek.inc/c/neptune/wlan_driver/logan/+/8206056
+
+mtk: wifi: mt76: testmode: add testmode ibf ver2 support
+
+Add ibf ver2 support for chips after Kite
+
+mtk: wifi: mt76: testmode: add testmode ibf 5T5R support
+
+Add testmode ibf 5T5R support for Kite BE7200 2i5i
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt76.h               |   5 +
+ mt76_connac_mcu.h    |   4 +-
+ mt7996/mcu.c         |   8 +-
+ mt7996/mcu.h         |  46 ++-
+ mt7996/mt7996.h      |  12 +-
+ mt7996/mtk_debugfs.c |   6 +-
+ mt7996/mtk_mcu.c     | 143 +++++++-
+ mt7996/mtk_mcu.h     | 441 +++++++++++++++++++++++-
+ mt7996/regs.h        |   3 +
+ mt7996/testmode.c    | 796 +++++++++++++++++++++++++++++++++++++++++--
+ mt7996/testmode.h    |  19 ++
+ testmode.c           |  62 ++++
+ testmode.h           |  53 +++
+ tools/fields.c       |  37 ++
+ 14 files changed, 1576 insertions(+), 59 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index 0cf5d573..36e8834e 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -760,6 +760,11 @@ struct mt76_testmode_data {
+ 	u32 tx_time;
+ 	u32 tx_ipg;
+ 
++	u8 txbf_act;
++	u16 txbf_param[8];
++	bool is_txbf_dut;
++	bool bf_en;
++	bool bf_ever_en;
+ 	bool ibf;
+ 	bool ebf;
+ 
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 726f5e9d..6be7e6a6 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -488,7 +488,8 @@ struct sta_rec_bf {
+ 	bool codebook75_mu;
+ 
+ 	u8 he_ltf;
+-	u8 rsv[3];
++	u8 pp_fd_val;
++	u8 rsv[2];
+ } __packed;
+ 
+ struct sta_rec_bfee {
+@@ -1291,6 +1292,7 @@ enum {
+ 	MCU_UNI_CMD_VOW = 0x37,
+ 	MCU_UNI_CMD_PP = 0x38,
+ 	MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
++	MCU_UNI_CMD_TESTMODE_TRX_PARAM = 0x42,
+ 	MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
+ 	MCU_UNI_CMD_PRECAL_RESULT = 0x47,
+ 	MCU_UNI_CMD_RRO = 0x57,
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index ae894ac9..43604ebc 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1073,7 +1073,12 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
+ 	bss->hw_bss_idx = idx;
+ 
+ 	if (vif->type == NL80211_IFTYPE_MONITOR) {
+-		memcpy(bss->bssid, phy->macaddr, ETH_ALEN);
++		struct mt76_testmode_data *td = &phy->test;
++
++		if (!td->bf_en)
++			memcpy(bss->bssid, phy->macaddr, ETH_ALEN);
++		else
++			memcpy(bss->bssid, td->addr[2], ETH_ALEN);
+ 		return 0;
+ 	}
+ 
+@@ -4100,7 +4105,6 @@ int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 val, u8 band)
+ int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action)
+ {
+ #define MT7996_BF_MAX_SIZE	sizeof(union bf_tag_tlv)
+-#define BF_PROCESSING	4
+ 	struct uni_header hdr;
+ 	struct sk_buff *skb;
+ 	struct tlv *tlv;
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 2546354e..c6bb93b3 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -685,6 +685,22 @@ struct bf_sounding_on {
+ 	__le32 snd_period;
+ } __packed;
+ 
++enum sounding_mode {
++	SU_SOUNDING,
++	MU_SOUNDING,
++	SU_PERIODIC_SOUNDING,
++	MU_PERIODIC_SOUNDING,
++	BF_PROCESSING,
++	TXCMD_NONTB_SU_SOUNDING,
++	TXCMD_VHT_MU_SOUNDING,
++	TXCMD_TB_PER_BRP_SOUNDING,
++	TXCMD_TB_SOUNDING,
++
++	/* keep last */
++	NUM_SOUNDING_MODE,
++	SOUNDING_MODE_MAX = NUM_SOUNDING_MODE - 1,
++};
++
+ struct bf_hw_en_status_update {
+ 	__le16 tag;
+ 	__le16 len;
+@@ -710,6 +726,25 @@ union bf_tag_tlv {
+ 	struct bf_mod_en_ctrl bf_mod_en;
+ };
+ 
++enum {
++	BF_SOUNDING_OFF = 0,
++	BF_SOUNDING_ON = 1,
++	BF_DATA_PACKET_APPLY = 2,
++	BF_PFMU_TAG_READ = 5,
++	BF_PFMU_TAG_WRITE = 6,
++	BF_STA_REC_READ = 11,
++	BF_PHASE_CALIBRATION = 12,
++	BF_IBF_PHASE_COMP = 13,
++	BF_PROFILE_WRITE_20M_ALL = 15,
++	BF_HW_EN_UPDATE = 17,
++	BF_MOD_EN_CTRL = 20,
++	BF_FBRPT_DBG_INFO_READ = 23,
++	BF_TXSND_INFO = 24,
++	BF_CMD_TXCMD = 27,
++	BF_CFG_PHY = 28,
++	BF_PROFILE_WRITE_20M_ALL_5X5 = 30,
++};
++
+ struct ra_rate {
+ 	__le16 wlan_idx;
+ 	u8 mode;
+@@ -771,17 +806,6 @@ enum {
+ #define MUMIMO_UL                      BIT(3)
+ #define MUMIMO_DL_CERT                 BIT(4)
+ 
+-enum {
+-	BF_SOUNDING_ON = 1,
+-	BF_PFMU_TAG_READ = 5,
+-	BF_STA_REC_READ = 11,
+-	BF_HW_EN_UPDATE = 17,
+-	BF_MOD_EN_CTRL = 20,
+-	BF_FBRPT_DBG_INFO_READ = 23,
+-	BF_TXSND_INFO = 24,
+-	BF_CFG_PHY = 28,
+-};
+-
+ enum {
+ 	CMD_BAND_NONE,
+ 	CMD_BAND_24G,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 00d7991b..1b774f5e 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -486,6 +486,14 @@ struct mt7996_dev {
+ #ifdef CONFIG_MTK_VENDOR
+ 	bool cert_mode;
+ #endif
++
++#if defined CONFIG_NL80211_TESTMODE || defined CONFIG_MTK_DEBUG
++	struct {
++		void *txbf_phase_cal;
++		void *txbf_pfmu_data;
++		void *txbf_pfmu_tag;
++	} test;
++#endif
+ };
+ 
+ enum {
+@@ -825,7 +833,7 @@ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
+ int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set);
+ void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev);
+-int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx);
++int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx, bool bfer);
+ void mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ int mt7996_mcu_set_muru_fixed_rate_enable(struct mt7996_dev *dev, u8 action, int val);
+ int mt7996_mcu_set_muru_fixed_rate_parameter(struct mt7996_dev *dev, u8 action, void *para);
+@@ -837,10 +845,12 @@ int mt7996_mcu_set_rfeature_trig_type(struct mt7996_phy *phy, u8 enable, u8 trig
+ void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type);
+ void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 ofdma_user_cnt);
+ void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type);
++void mt7996_tm_update_channel(struct mt7996_phy *phy);
+ #endif
+ 
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ int mt7996_dma_rro_init(struct mt7996_dev *dev);
+ #endif /* CONFIG_NET_MEDIATEK_SOC_WED */
+ 
++
+ #endif
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 0e029d5d..05cfc6ab 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2917,7 +2917,7 @@ mt7996_starec_bf_read_set(void *data, u64 wlan_idx)
+ {
+ 	struct mt7996_phy *phy = data;
+ 
+-	return mt7996_mcu_set_txbf_internal(phy, BF_STA_REC_READ, wlan_idx);
++	return mt7996_mcu_set_txbf_internal(phy, BF_STA_REC_READ, wlan_idx, 0);
+ }
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_starec_bf_read, NULL,
+ 			 mt7996_starec_bf_read_set, "%lld\n");
+@@ -2961,7 +2961,7 @@ mt7996_bf_fbk_rpt_set(void *data, u64 wlan_idx)
+ {
+ 	struct mt7996_phy *phy = data;
+ 
+-	return mt7996_mcu_set_txbf_internal(phy, BF_FBRPT_DBG_INFO_READ, wlan_idx);
++	return mt7996_mcu_set_txbf_internal(phy, BF_FBRPT_DBG_INFO_READ, wlan_idx, 0);
+ }
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_bf_fbk_rpt, NULL,
+ 			 mt7996_bf_fbk_rpt_set, "%lld\n");
+@@ -2971,7 +2971,7 @@ mt7996_bf_pfmu_tag_read_set(void *data, u64 wlan_idx)
+ {
+ 	struct mt7996_phy *phy = data;
+ 
+-	return mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, wlan_idx);
++	return mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, wlan_idx, 1);
+ }
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_bf_pfmu_tag_read, NULL,
+ 			 mt7996_bf_pfmu_tag_read_set, "%lld\n");
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index 30da79f4..b46a66bb 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -295,7 +295,7 @@ __mt7996_mcu_add_uni_tlv(struct sk_buff *skb, u16 tag, u16 len)
+ 	return ptlv;
+ }
+ 
+-int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx)
++int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx, bool bfer)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+ #define MT7996_MTK_BF_MAX_SIZE	sizeof(struct bf_starec_read)
+@@ -318,9 +318,8 @@ int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx)
+ 
+ 		tlv = __mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req));
+ 		req = (struct bf_pfmu_tag *)tlv;
+-#define BFER 1
+ 		req->pfmu_id = idx;
+-		req->bfer = BFER;
++		req->bfer = bfer;
+ 		req->band_idx = phy->mt76->band_idx;
+ 		break;
+ 	}
+@@ -432,10 +431,61 @@ int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para)
+ 	return mt76_mcu_skb_send_msg(&phy->dev->mt76, skb, MCU_WM_UNI_CMD(BF), false);
+ }
+ 
++static inline void
++mt7996_ibf_phase_assign(struct mt7996_dev *dev,
++			struct mt7996_ibf_cal_info *cal,
++			struct mt7996_txbf_phase *phase)
++{
++	/* fw return ibf calibrated data with
++	 * the mt7996_txbf_phase_info_5g struct for both 2G and 5G.
++	 * (return struct mt7992_txbf_phase_info_5g for ibf 2.0)
++	 * Therefore, memcpy cannot be used here.
++	 */
++	if (get_ibf_version(dev) != IBF_VER_2) {
++		phase_assign(cal->group, v1, m_t0_h, true);
++		phase_assign(cal->group, v1, m_t1_h, true);
++		phase_assign(cal->group, v1, m_t2_h, true);
++		phase_assign(cal->group, v1, m_t2_h_sx2, false);
++		phase_assign_rx_v1(cal->group, v1, r0);
++		phase_assign_rx_v1(cal->group, v1, r1);
++		phase_assign_rx_v1(cal->group, v1, r2);
++		phase_assign_rx_v1(cal->group, v1, r3);
++		phase_assign_rx(cal->group, v1, r2_sx2, false);
++		phase_assign_rx(cal->group, v1, r3_sx2, false);
++		phase_assign(cal->group, v1, r0_reserved, false);
++		phase_assign(cal->group, v1, r1_reserved, false);
++		phase_assign(cal->group, v1, r2_reserved, false);
++		phase_assign(cal->group, v1, r3_reserved, false);
++		phase_assign(cal->group, v1, r2_sx2_reserved, false);
++		phase_assign(cal->group, v1, r3_sx2_reserved, false);
++	} else {
++		phase_assign(cal->group, v2, m_t0_h, true);
++		phase_assign(cal->group, v2, m_t1_h, true);
++		phase_assign(cal->group, v2, m_t2_h, true);
++		if (cal->group) {
++			phase->v2.phase_5g.m_t3_h = cal->v2.phase_5g.m_t3_h;
++			dev_info(dev->mt76.dev, "m_t3_h = %d\n", phase->v2.phase_5g.m_t3_h);
++		}
++		phase_assign_rx_ext(cal->group, v2, r0, true);
++		phase_assign_rx_ext(cal->group, v2, r1, true);
++		phase_assign_rx_ext(cal->group, v2, r2, true);
++		phase_assign_rx_ext(cal->group, v2, r3, true);
++		if (cal->group) {
++			memcpy(&phase->v2.phase_5g.r4, &cal->v2.phase_5g.r4,
++			       sizeof(struct txbf_rx_phase_ext));
++			dev_info(dev->mt76.dev, "r4.rx_uh = %d\n", phase->v2.phase_5g.r4.rx_uh);
++			dev_info(dev->mt76.dev, "r4.rx_h = %d\n", phase->v2.phase_5g.r4.rx_h);
++			dev_info(dev->mt76.dev, "r4.rx_mh = %d\n", phase->v2.phase_5g.r4.rx_mh);
++			dev_info(dev->mt76.dev, "r4.rx_m = %d\n", phase->v2.phase_5g.r4.rx_m);
++			dev_info(dev->mt76.dev, "r4.rx_l = %d\n", phase->v2.phase_5g.r4.rx_l);
++			dev_info(dev->mt76.dev, "r4.rx_ul = %d\n", phase->v2.phase_5g.r4.rx_ul);
++		}
++	}
++}
++
+ void
+ mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+-#define HE_MODE 3
+ 	struct mt7996_mcu_bf_basic_event *event;
+ 
+ 	event = (struct mt7996_mcu_bf_basic_event *)skb->data;
+@@ -470,13 +520,12 @@ mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 			 tag->t1.nr, tag->t1.nc, tag->t1.ngroup, tag->t1.lm, tag->t1.codebook,
+ 			 tag->t1.mob_cal_en);
+ 
+-		if (tag->t1.lm <= HE_MODE) {
++		if (tag->t1.lm <= BF_LM_HE)
+ 			dev_info(dev->mt76.dev, "RU start = %d, RU end = %d\n",
+ 				 tag->t1.field.ru_start_id, tag->t1.field.ru_end_id);
+-		} else {
++		else
+ 			dev_info(dev->mt76.dev, "PartialBW = %d\n",
+ 				 tag->t1.bw_info.partial_bw_info);
+-		}
+ 
+ 		dev_info(dev->mt76.dev, "Mem Col1 = %d, Mem Row1 = %d, Mem Col2 = %d, Mem Row2 = %d\n",
+ 			 tag->t1.col_id1, tag->t1.row_id1, tag->t1.col_id2, tag->t1.row_id2);
+@@ -728,6 +777,86 @@ mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 
+ 		break;
+ 	}
++	case UNI_EVENT_BF_CAL_PHASE: {
++		struct mt7996_ibf_cal_info *cal;
++		struct mt7996_txbf_phase *phase;
++		union {
++			struct mt7996_txbf_phase_out v1;
++			struct mt7992_txbf_phase_out v2;
++		} phase_out;
++		int phase_out_size = sizeof(struct mt7996_txbf_phase_out);
++
++		cal = (struct mt7996_ibf_cal_info *)skb->data;
++		phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal;
++		if (get_ibf_version(dev) == IBF_VER_2)
++			phase_out_size = sizeof(struct mt7992_txbf_phase_out);
++		memcpy(&phase_out, &cal->buf, phase_out_size);
++		switch (cal->cal_type) {
++		case IBF_PHASE_CAL_NORMAL:
++		case IBF_PHASE_CAL_NORMAL_INSTRUMENT:
++			/* Only calibrate group M */
++			if (cal->group_l_m_n != GROUP_M)
++				break;
++			phase = &phase[cal->group];
++			phase->status = cal->status;
++			dev_info(dev->mt76.dev, "Calibrated result = %d\n", phase->status);
++			dev_info(dev->mt76.dev, "Group %d and Group M\n", cal->group);
++			mt7996_ibf_phase_assign(dev, cal, phase);
++			break;
++		case IBF_PHASE_CAL_VERIFY:
++		case IBF_PHASE_CAL_VERIFY_INSTRUMENT:
++			dev_info(dev->mt76.dev, "Verification result = %d\n", cal->status);
++			break;
++		default:
++			break;
++		}
++
++		if (get_ibf_version(dev) == IBF_VER_2) {
++			dev_info(dev->mt76.dev,
++				 "c0_uh = %d, c1_uh = %d, c2_uh = %d, c3_uh = %d c4_uh = %d\n",
++				 phase_out.v2.c0_uh, phase_out.v2.c1_uh, phase_out.v2.c2_uh,
++				 phase_out.v2.c3_uh, phase_out.v2.c4_uh);
++			dev_info(dev->mt76.dev,
++				 "c0_h = %d, c1_h = %d, c2_h = %d, c3_h = %d c4_h = %d\n",
++				 phase_out.v2.c0_h, phase_out.v2.c1_h, phase_out.v2.c2_h,
++				 phase_out.v2.c3_h, phase_out.v2.c4_h);
++			dev_info(dev->mt76.dev,
++				 "c0_mh = %d, c1_mh = %d, c2_mh = %d, c3_mh = %d c4_mh = %d\n",
++				 phase_out.v2.c0_mh, phase_out.v2.c1_mh, phase_out.v2.c2_mh,
++				 phase_out.v2.c3_mh, phase_out.v2.c4_mh);
++			dev_info(dev->mt76.dev,
++				 "c0_m = %d, c1_m = %d, c2_m = %d, c3_m = %d c4_m = %d\n",
++				 phase_out.v2.c0_m, phase_out.v2.c1_m, phase_out.v2.c2_m,
++				 phase_out.v2.c3_m, phase_out.v2.c4_m);
++			dev_info(dev->mt76.dev,
++				 "c0_l = %d, c1_l = %d, c2_l = %d, c3_l = %d c4_l = %d\n",
++				 phase_out.v2.c0_l, phase_out.v2.c1_l, phase_out.v2.c2_l,
++				 phase_out.v2.c3_l, phase_out.v2.c4_l);
++		} else {
++			dev_info(dev->mt76.dev,
++				 "c0_uh = %d, c1_uh = %d, c2_uh = %d, c3_uh = %d\n",
++				 phase_out.v1.c0_uh, phase_out.v1.c1_uh,
++				 phase_out.v1.c2_uh, phase_out.v1.c3_uh);
++			dev_info(dev->mt76.dev,
++				 "c0_h = %d, c1_h = %d, c2_h = %d, c3_h = %d\n",
++				 phase_out.v1.c0_h, phase_out.v1.c1_h,
++				 phase_out.v1.c2_h, phase_out.v1.c3_h);
++			dev_info(dev->mt76.dev,
++				 "c0_mh = %d, c1_mh = %d, c2_mh = %d, c3_mh = %d\n",
++				 phase_out.v1.c0_mh, phase_out.v1.c1_mh,
++				 phase_out.v1.c2_mh, phase_out.v1.c3_mh);
++			dev_info(dev->mt76.dev,
++				 "c0_m = %d, c1_m = %d, c2_m = %d, c3_m = %d\n",
++				 phase_out.v1.c0_m, phase_out.v1.c1_m,
++				 phase_out.v1.c2_m, phase_out.v1.c3_m);
++			dev_info(dev->mt76.dev,
++				 "c0_l = %d, c1_l = %d, c2_l = %d, c3_l = %d\n",
++				 phase_out.v1.c0_l, phase_out.v1.c1_l,
++				 phase_out.v1.c2_l, phase_out.v1.c3_l);
++		}
++
++		break;
++	}
+ 	default:
+ 		dev_info(dev->mt76.dev, "%s: unknown bf event tag %d\n",
+ 			 __func__, event->tag);
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+index 7a4140b5..58d61c51 100644
+--- a/mt7996/mtk_mcu.h
++++ b/mt7996/mtk_mcu.h
+@@ -189,6 +189,165 @@ struct bf_txsnd_info {
+ 	u8 __rsv[2];
+ } __packed;
+ 
++#define MAX_PHASE_GROUP_NUM	13
++
++struct bf_phase_comp {
++	__le16 tag;
++	__le16 len;
++
++	u8 bw;
++	u8 jp_band;
++	u8 band_idx;
++	bool read_from_e2p;
++	bool disable;
++	u8 group;
++	u8 rsv[2];
++	u8 buf[44];
++} __packed;
++
++struct bf_tx_apply {
++	__le16 tag;
++	__le16 len;
++
++	__le16 wlan_idx;
++	bool ebf;
++	bool ibf;
++	bool mu_txbf;
++	bool phase_cal;
++	u8 rsv[2];
++} __packed;
++
++struct bf_phase_cal {
++	__le16 tag;
++	__le16 len;
++
++	u8 group_l_m_n;
++	u8 group;
++	u8 sx2;
++	u8 cal_type;
++	u8 lna_gain_level;
++	u8 band_idx;
++	u8 version;
++	u8 rsv[1];
++} __packed;
++
++struct bf_txcmd {
++	__le16 tag;
++	__le16 len;
++
++	u8 action;
++	u8 bf_manual;
++	u8 bf_bit;
++	u8 rsv[5];
++} __packed;
++
++struct bf_pfmu_data_all {
++	__le16 tag;
++	__le16 len;
++
++	u8 pfmu_id;
++	u8 band_idx;
++	u8 rsv[2];
++
++	u8 buf[640];
++} __packed;
++
++#define TXBF_DUT_MAC_SUBADDR		0x22
++#define TXBF_GOLDEN_MAC_SUBADDR		0x11
++
++struct mt7996_tm_bf_req {
++	u8 _rsv[4];
++
++	union {
++		struct bf_sounding_on sounding;
++		struct bf_tx_apply tx_apply;
++		struct bf_pfmu_tag pfmu_tag;
++		struct bf_pfmu_data_all pfmu_data_all;
++		struct bf_phase_cal phase_cal;
++		struct bf_phase_comp phase_comp;
++		struct bf_txcmd txcmd;
++	};
++} __packed;
++
++enum tm_trx_mac_type {
++	TM_TRX_MAC_TX = 1,
++	TM_TRX_MAC_RX,
++	TM_TRX_MAC_TXRX,
++	TM_TRX_MAC_TXRX_RXV,
++	TM_TRX_MAC_RXV,
++	TM_TRX_MAC_RX_RXV,
++};
++
++enum tm_trx_param_idx {
++	TM_TRX_PARAM_RSV,
++	/* MAC */
++	TM_TRX_PARAM_SET_TRX,
++	TM_TRX_PARAM_RX_FILTER,
++	TM_TRX_PARAM_RX_FILTER_PKT_LEN,
++	TM_TRX_PARAM_SLOT_TIME,
++	TM_TRX_PARAM_CLEAN_PERSTA_TXQUEUE,
++	TM_TRX_PARAM_AMPDU_WTBL,
++	TM_TRX_PARAM_MU_RX_AID,
++	TM_TRX_PARAM_PHY_MANUAL_TX,
++
++	/* PHY */
++	TM_TRX_PARAM_RX_PATH,
++	TM_TRX_PARAM_TX_STREAM,
++	TM_TRX_PARAM_TSSI_STATUS,
++	TM_TRX_PARAM_DPD_STATUS,
++	TM_TRX_PARAM_RATE_POWER_OFFSET_ON_OFF,
++	TM_TRX_PARAM_THERMO_COMP_STATUS,
++	TM_TRX_PARAM_FREQ_OFFSET,
++	TM_TRX_PARAM_FAGC_RSSI_PATH,
++	TM_TRX_PARAM_PHY_STATUS_COUNT,
++	TM_TRX_PARAM_RXV_INDEX,
++
++	TM_TRX_PARAM_ANTENNA_PORT,
++	TM_TRX_PARAM_THERMAL_ONOFF,
++	TM_TRX_PARAM_TX_POWER_CONTROL_ALL_RF,
++	TM_TRX_PARAM_RATE_POWER_OFFSET,
++	TM_TRX_PARAM_SLT_CMD_TEST,
++	TM_TRX_PARAM_SKU,
++	TM_TRX_PARAM_POWER_PERCENTAGE_ON_OFF,
++	TM_TRX_PARAM_BF_BACKOFF_ON_OFF,
++	TM_TRX_PARAM_POWER_PERCENTAGE_LEVEL,
++	TM_TRX_PARAM_FRTBL_CFG,
++	TM_TRX_PARAM_PREAMBLE_PUNC_ON_OFF,
++
++	TM_TRX_PARAM_MAX_NUM,
++};
++
++enum trx_action {
++	TM_TRX_ACTION_SET,
++	TM_TRX_ACTION_GET,
++};
++
++struct tm_trx_set {
++	u8 type;
++	u8 enable;
++	u8 band_idx;
++	u8 rsv;
++} __packed;
++
++struct mt7996_tm_trx_req {
++	u8 param_num;
++	u8 _rsv[3];
++
++	__le16 tag;
++	__le16 len;
++
++	__le16 param_idx;
++	u8 band_idx;
++	u8 testmode_en;
++	u8 action;
++	u8 rsv[3];
++
++	u32 data;
++	struct tm_trx_set set_trx;
++
++	u8 buf[220];
++} __packed;
++
+ struct mt7996_mcu_bf_basic_event {
+ 	struct mt7996_mcu_rxd rxd;
+ 
+@@ -394,6 +553,283 @@ struct mt7996_pfmu_tag_event {
+ 	struct mt7996_pfmu_tag2 t2;
+ };
+ 
++struct mt7996_pfmu_tag {
++	struct mt7996_pfmu_tag1 t1;
++	struct mt7996_pfmu_tag2 t2;
++};
++
++enum bf_lm_type {
++	BF_LM_LEGACY,
++	BF_LM_HT,
++	BF_LM_VHT,
++	BF_LM_HE,
++	BF_LM_EHT,
++};
++
++struct mt7996_txbf_phase_out {
++	u8 c0_l;
++	u8 c1_l;
++	u8 c2_l;
++	u8 c3_l;
++	u8 c0_m;
++	u8 c1_m;
++	u8 c2_m;
++	u8 c3_m;
++	u8 c0_mh;
++	u8 c1_mh;
++	u8 c2_mh;
++	u8 c3_mh;
++	u8 c0_h;
++	u8 c1_h;
++	u8 c2_h;
++	u8 c3_h;
++	u8 c0_uh;
++	u8 c1_uh;
++	u8 c2_uh;
++	u8 c3_uh;
++};
++
++struct mt7992_txbf_phase_out {
++	u8 c0_l;
++	u8 c1_l;
++	u8 c2_l;
++	u8 c3_l;
++	u8 c4_l;
++	u8 c0_m;
++	u8 c1_m;
++	u8 c2_m;
++	u8 c3_m;
++	u8 c4_m;
++	u8 c0_mh;
++	u8 c1_mh;
++	u8 c2_mh;
++	u8 c3_mh;
++	u8 c4_mh;
++	u8 c0_h;
++	u8 c1_h;
++	u8 c2_h;
++	u8 c3_h;
++	u8 c4_h;
++	u8 c0_uh;
++	u8 c1_uh;
++	u8 c2_uh;
++	u8 c3_uh;
++	u8 c4_uh;
++};
++
++struct txbf_rx_phase {
++	u8 rx_uh;
++	u8 rx_h;
++	u8 rx_m;
++	u8 rx_l;
++	u8 rx_ul;
++};
++
++struct txbf_rx_phase_ext {
++	u8 rx_uh;
++	u8 rx_h;
++	u8 rx_mh;
++	u8 rx_m;
++	u8 rx_l;
++	u8 rx_ul;
++};
++
++struct mt7996_txbf_phase_info_2g {
++	struct txbf_rx_phase r0;
++	struct txbf_rx_phase r1;
++	struct txbf_rx_phase r2;
++	struct txbf_rx_phase r3;
++	struct txbf_rx_phase r2_sx2;
++	struct txbf_rx_phase r3_sx2;
++	u8 m_t0_h;
++	u8 m_t1_h;
++	u8 m_t2_h;
++	u8 m_t2_h_sx2;
++	u8 r0_reserved;
++	u8 r1_reserved;
++	u8 r2_reserved;
++	u8 r3_reserved;
++	u8 r2_sx2_reserved;
++	u8 r3_sx2_reserved;
++};
++
++struct mt7996_txbf_phase_info_5g {
++	struct txbf_rx_phase_ext r0;
++	struct txbf_rx_phase_ext r1;
++	struct txbf_rx_phase_ext r2;
++	struct txbf_rx_phase_ext r3;
++	struct txbf_rx_phase r2_sx2;	/* no middle-high in r2_sx2 */
++	struct txbf_rx_phase r3_sx2;	/* no middle-high in r3_sx2 */
++	u8 m_t0_h;
++	u8 m_t1_h;
++	u8 m_t2_h;
++	u8 m_t2_h_sx2;
++	u8 r0_reserved;
++	u8 r1_reserved;
++	u8 r2_reserved;
++	u8 r3_reserved;
++	u8 r2_sx2_reserved;
++	u8 r3_sx2_reserved;
++};
++
++struct mt7992_txbf_phase_info_2g {
++	struct txbf_rx_phase_ext r0;
++	struct txbf_rx_phase_ext r1;
++	struct txbf_rx_phase_ext r2;
++	struct txbf_rx_phase_ext r3;
++	u8 m_t0_h;
++	u8 m_t1_h;
++	u8 m_t2_h;
++};
++
++struct mt7992_txbf_phase_info_5g {
++	struct txbf_rx_phase_ext r0;
++	struct txbf_rx_phase_ext r1;
++	struct txbf_rx_phase_ext r2;
++	struct txbf_rx_phase_ext r3;
++	struct txbf_rx_phase_ext r4;
++	u8 m_t0_h;
++	u8 m_t1_h;
++	u8 m_t2_h;
++	u8 m_t3_h;
++};
++
++struct mt7996_txbf_phase {
++	u8 status;
++	union {
++		union {
++			struct mt7996_txbf_phase_info_2g phase_2g;
++			struct mt7996_txbf_phase_info_5g phase_5g;
++		} v1;
++		union {
++			struct mt7992_txbf_phase_info_2g phase_2g;
++			struct mt7992_txbf_phase_info_5g phase_5g;
++		} v2;
++		u8 buf[44];
++	};
++};
++
++#define phase_assign(group, v, field, dump, ...)	({					\
++	if (group) {										\
++		phase->v.phase_5g.field = cal->v.phase_5g.field;				\
++		if (dump)									\
++			dev_info(dev->mt76.dev, "%s = %d\n", #field, phase->v.phase_5g.field);	\
++	} else {										\
++		phase->v.phase_2g.field = cal->v.phase_5g.field;				\
++		if (dump)									\
++			dev_info(dev->mt76.dev, "%s = %d\n", #field, phase->v.phase_2g.field);	\
++	}											\
++})
++
++#define phase_assign_rx(group, v, rx, dump, ...)	({					\
++	phase_assign(group, v, rx.rx_uh, dump);							\
++	phase_assign(group, v, rx.rx_h, dump);							\
++	phase_assign(group, v, rx.rx_m, dump);							\
++	phase_assign(group, v, rx.rx_l, dump);							\
++	phase_assign(group, v, rx.rx_ul, dump);							\
++})
++
++#define phase_assign_rx_ext(group, v, rx, dump, ...)	({					\
++	phase_assign(group, v, rx.rx_uh, dump);							\
++	phase_assign(group, v, rx.rx_h, dump);							\
++	phase_assign(group, v, rx.rx_mh, dump);							\
++	phase_assign(group, v, rx.rx_m, dump);							\
++	phase_assign(group, v, rx.rx_l, dump);							\
++	phase_assign(group, v, rx.rx_ul, dump);							\
++})
++
++#define phase_assign_rx_v1(group, v, rx, ...)	({						\
++	if (group) {										\
++		phase_assign(group, v, rx.rx_uh, true);						\
++		phase_assign(group, v, rx.rx_h, true);						\
++		phase->v.phase_5g.rx.rx_mh = cal->v.phase_5g.rx.rx_mh;				\
++		dev_info(dev->mt76.dev, "%s.rx_mh = %d\n", #rx, phase->v.phase_5g.rx.rx_mh);	\
++		phase_assign(group, v, rx.rx_m, true);						\
++		phase_assign(group, v, rx.rx_l, true);						\
++		phase_assign(group, v, rx.rx_ul, true);						\
++	} else {										\
++		phase_assign_rx(group, v, rx, true, ...);					\
++	}											\
++})
++
++#define GROUP_L		0
++#define GROUP_M		1
++#define GROUP_H		2
++
++struct mt7996_pfmu_data {
++	__le16 subc_idx;
++	__le16 phi11;
++	__le16 phi21;
++	__le16 phi31;
++};
++
++struct mt7996_pfmu_data_5x5 {
++	__le16 subc_idx;
++	__le16 phi11;
++	__le16 phi21;
++	__le16 phi31;
++	__le16 phi41;
++};
++
++struct mt7996_ibf_cal_info {
++	struct mt7996_mcu_bf_basic_event event;
++
++	u8 category_id;
++	u8 group_l_m_n;
++	u8 group;
++	bool sx2;
++	u8 status;
++	u8 cal_type;
++	u8 nsts;
++	u8 version;
++	union {
++		struct {
++			struct mt7996_txbf_phase_out phase_out;
++			union {
++				struct mt7996_txbf_phase_info_2g phase_2g;
++				struct mt7996_txbf_phase_info_5g phase_5g;
++			};
++		} v1;
++		struct {
++			struct mt7992_txbf_phase_out phase_out;
++			union {
++				struct mt7992_txbf_phase_info_2g phase_2g;
++				struct mt7992_txbf_phase_info_5g phase_5g;
++			};
++		} v2;
++		u8 buf[64];
++	};
++} __packed;
++
++enum {
++	IBF_PHASE_CAL_UNSPEC,
++	IBF_PHASE_CAL_NORMAL,
++	IBF_PHASE_CAL_VERIFY,
++	IBF_PHASE_CAL_NORMAL_INSTRUMENT,
++	IBF_PHASE_CAL_VERIFY_INSTRUMENT,
++};
++
++enum ibf_version {
++	IBF_VER_1,
++	IBF_VER_2 = 3,
++};
++
++static inline int get_ibf_version(struct mt7996_dev *dev)
++{
++	switch (mt76_chip(&dev->mt76)) {
++	case 0x7990:
++		return IBF_VER_1;
++	case 0x7992:
++	default:
++		return IBF_VER_2;
++	}
++}
++
++#define MT7996_TXBF_SUBCAR_NUM		64
++#define MT7996_TXBF_PFMU_DATA_LEN	(MT7996_TXBF_SUBCAR_NUM * sizeof(struct mt7996_pfmu_data))
++#define MT7996_TXBF_PFMU_DATA_LEN_5X5	(MT7996_TXBF_SUBCAR_NUM * \
++					 sizeof(struct mt7996_pfmu_data_5x5))
++
+ enum {
+ 	UNI_EVENT_BF_PFMU_TAG = 0x5,
+ 	UNI_EVENT_BF_PFMU_DATA = 0x7,
+@@ -408,11 +844,6 @@ enum {
+ 	UNI_EVENT_BF_MAX_NUM
+ };
+ 
+-enum {
+-	UNI_CMD_MURU_FIXED_RATE_CTRL = 0x11,
+-	UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL,
+-};
+-
+ struct uni_muru_mum_set_group_tbl_entry {
+ 	__le16 wlan_idx0;
+ 	__le16 wlan_idx1;
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index 8ec1dc1c..263737c5 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -326,6 +326,9 @@ enum offs_rev {
+ #define MT_ARB_SCR_TX_DISABLE			BIT(8)
+ #define MT_ARB_SCR_RX_DISABLE			BIT(9)
+ 
++#define MT_ARB_TQSAXM0(_band)			MT_WF_ARB(_band, 0x180)
++#define MT_ARB_TQSAXM_ALTX_START_MASK		GENMASK(12, 8)
++
+ /* RMAC: band 0(0x820e5000), band 1(0x820f5000), band 2(0x830e5000), */
+ #define MT_WF_RMAC_BASE(_band)			__BASE(WF_RMAC_BASE, (_band))
+ #define MT_WF_RMAC(_band, ofs)			(MT_WF_RMAC_BASE(_band) + (ofs))
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index 836211f9..5cec1eef 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -23,6 +23,7 @@ enum {
+ 	TM_CHANGED_IPI_THRESHOLD,
+ 	TM_CHANGED_IPI_PERIOD,
+ 	TM_CHANGED_IPI_RESET,
++	TM_CHANGED_TXBF_ACT,
+ 
+ 	/* must be last */
+ 	NUM_TM_CHANGED
+@@ -41,25 +42,31 @@ static const u8 tm_change_map[] = {
+ 	[TM_CHANGED_IPI_THRESHOLD] = MT76_TM_ATTR_IPI_THRESHOLD,
+ 	[TM_CHANGED_IPI_PERIOD] = MT76_TM_ATTR_IPI_PERIOD,
+ 	[TM_CHANGED_IPI_RESET] = MT76_TM_ATTR_IPI_RESET,
++	[TM_CHANGED_TXBF_ACT] = MT76_TM_ATTR_TXBF_ACT,
+ };
+ 
+ static void mt7996_tm_ipi_work(struct work_struct *work);
++static int mt7996_tm_txbf_apply_tx(struct mt7996_phy *phy, u16 wlan_idx,
++				   bool ebf, bool ibf, bool phase_cal);
+ 
+ static u32 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method)
+ {
+ 	static const u32 width_to_bw[][NUM_BW_MAP] = {
+-		[NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ, 40,
++		[NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ, BF_CDBW_40MHZ, 40,
+ 					   FIRST_CONTROL_CHAN_BITMAP_BW40},
+-		[NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ, 80,
++		[NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ, BF_CDBW_80MHZ, 80,
+ 					   FIRST_CONTROL_CHAN_BITMAP_BW80},
+-		[NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ, 80, 0x0},
+-		[NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ, 160,
++		[NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ, BF_CDBW_8080MHZ,
++					      80, 0x0},
++		[NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ, BF_CDBW_160MHZ, 160,
+ 					    FIRST_CONTROL_CHAN_BITMAP_BW160},
+-		[NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ, 5, 0x0},
+-		[NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ, 10, 0x0},
+-		[NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0},
+-		[NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0},
+-		[NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ, 320, 0x0},
++		[NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ, BF_CDBW_5MHZ, 5, 0x0},
++		[NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ, BF_CDBW_10MHZ, 10, 0x0},
++		[NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, BF_CDBW_20MHZ, 20, 0x0},
++		[NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, BF_CDBW_20MHZ,
++						20, 0x0},
++		[NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ, BF_CDBW_320MHZ,
++					    320, 0x0},
+ 	};
+ 
+ 	if (width >= ARRAY_SIZE(width_to_bw))
+@@ -68,26 +75,26 @@ static u32 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_m
+ 	return width_to_bw[width][method];
+ }
+ 
+-static u8 mt7996_tm_rate_to_phy(u8 tx_rate_mode)
++static u8 mt7996_tm_rate_mapping(u8 tx_rate_mode, enum rate_mapping_type type)
+ {
+-	static const u8 rate_to_phy[] = {
+-		[MT76_TM_TX_MODE_CCK] = MT_PHY_TYPE_CCK,
+-		[MT76_TM_TX_MODE_OFDM] = MT_PHY_TYPE_OFDM,
+-		[MT76_TM_TX_MODE_HT] = MT_PHY_TYPE_HT,
+-		[MT76_TM_TX_MODE_VHT] = MT_PHY_TYPE_VHT,
+-		[MT76_TM_TX_MODE_HE_SU] = MT_PHY_TYPE_HE_SU,
+-		[MT76_TM_TX_MODE_HE_EXT_SU] = MT_PHY_TYPE_HE_EXT_SU,
+-		[MT76_TM_TX_MODE_HE_TB] = MT_PHY_TYPE_HE_TB,
+-		[MT76_TM_TX_MODE_HE_MU] = MT_PHY_TYPE_HE_MU,
+-		[MT76_TM_TX_MODE_EHT_SU] = MT_PHY_TYPE_EHT_SU,
+-		[MT76_TM_TX_MODE_EHT_TRIG] = MT_PHY_TYPE_EHT_TRIG,
+-		[MT76_TM_TX_MODE_EHT_MU] = MT_PHY_TYPE_EHT_MU,
++	static const u8 rate_to_phy[][NUM_RATE_MAP] = {
++		[MT76_TM_TX_MODE_CCK] = {MT_PHY_TYPE_CCK, BF_LM_LEGACY},
++		[MT76_TM_TX_MODE_OFDM] = {MT_PHY_TYPE_OFDM, BF_LM_LEGACY},
++		[MT76_TM_TX_MODE_HT] = {MT_PHY_TYPE_HT, BF_LM_HT},
++		[MT76_TM_TX_MODE_VHT] = {MT_PHY_TYPE_VHT, BF_LM_VHT},
++		[MT76_TM_TX_MODE_HE_SU] = {MT_PHY_TYPE_HE_SU, BF_LM_HE},
++		[MT76_TM_TX_MODE_HE_EXT_SU] = {MT_PHY_TYPE_HE_EXT_SU, BF_LM_HE},
++		[MT76_TM_TX_MODE_HE_TB] = {MT_PHY_TYPE_HE_TB, BF_LM_HE},
++		[MT76_TM_TX_MODE_HE_MU] = {MT_PHY_TYPE_HE_MU, BF_LM_HE},
++		[MT76_TM_TX_MODE_EHT_SU] = {MT_PHY_TYPE_EHT_SU, BF_LM_EHT},
++		[MT76_TM_TX_MODE_EHT_TRIG] = {MT_PHY_TYPE_EHT_TRIG, BF_LM_EHT},
++		[MT76_TM_TX_MODE_EHT_MU] = {MT_PHY_TYPE_EHT_MU, BF_LM_EHT},
+ 	};
+ 
+ 	if (tx_rate_mode > MT76_TM_TX_MODE_MAX)
+ 		return -EINVAL;
+ 
+-	return rate_to_phy[tx_rate_mode];
++	return rate_to_phy[tx_rate_mode][type];
+ }
+ 
+ static int
+@@ -239,7 +246,7 @@ mt7996_tm_init(struct mt7996_phy *phy, bool en)
+ 		INIT_DELAYED_WORK(&phy->ipi_work, mt7996_tm_ipi_work);
+ }
+ 
+-static void
++void
+ mt7996_tm_update_channel(struct mt7996_phy *phy)
+ {
+ #define CHAN_FREQ_BW_80P80_TAG		(SET_ID(CHAN_FREQ) | BIT(16))
+@@ -303,7 +310,8 @@ mt7996_tm_set_tx_frames(struct mt7996_phy *phy, bool en)
+ 		mt7996_tm_set(dev, SET_ID(MAC_HEADER), FRAME_CONTROL);
+ 		mt7996_tm_set(dev, SET_ID(SEQ_CTRL), 0);
+ 		mt7996_tm_set(dev, SET_ID(TX_COUNT), td->tx_count);
+-		mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
++		mt7996_tm_set(dev, SET_ID(TX_MODE),
++			      mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY));
+ 		mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
+ 
+ 		if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER))
+@@ -331,7 +339,8 @@ mt7996_tm_set_tx_frames(struct mt7996_phy *phy, bool en)
+ 
+ 		mt7996_tm_set(dev, SET_ID(MAX_PE), 2);
+ 		mt7996_tm_set(dev, SET_ID(HW_TX_MODE), 0);
+-		mt7996_tm_update_channel(phy);
++		if (!td->bf_en)
++			mt7996_tm_update_channel(phy);
+ 
+ 		/* trigger firmware to start TX */
+ 		mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(START_TX));
+@@ -373,7 +382,8 @@ mt7996_tm_set_rx_frames(struct mt7996_phy *phy, bool en)
+ 			return;
+ 		}
+ 
+-		mt7996_tm_update_channel(phy);
++		if (!td->bf_en)
++			mt7996_tm_update_channel(phy);
+ 
+ 		if (td->tx_rate_mode >= MT76_TM_TX_MODE_HE_MU) {
+ 			if (td->aid)
+@@ -381,7 +391,8 @@ mt7996_tm_set_rx_frames(struct mt7996_phy *phy, bool en)
+ 			else
+ 				ret = mt7996_tm_set(dev, SET_ID(RX_MU_AID), RX_MU_DISABLE);
+ 		}
+-		mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
++		mt7996_tm_set(dev, SET_ID(TX_MODE),
++			      mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY));
+ 		mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi);
+ 		mt7996_tm_set_antenna(phy, SET_ID(RX_PATH));
+ 		mt7996_tm_set(dev, SET_ID(MAX_PE), 2);
+@@ -405,7 +416,8 @@ mt7996_tm_set_tx_cont(struct mt7996_phy *phy, bool en)
+ 
+ 	if (en) {
+ 		mt7996_tm_update_channel(phy);
+-		mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
++		mt7996_tm_set(dev, SET_ID(TX_MODE),
++			      mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY));
+ 		mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
+ 		/* fix payload is OFDM */
+ 		mt7996_tm_set(dev, SET_ID(CONT_WAVE_MODE), CONT_WAVE_MODE_OFDM);
+@@ -1047,6 +1059,730 @@ mt7996_tm_set_ipi(struct mt7996_phy *phy)
+ 	return 0;
+ }
+ 
++static int
++mt7996_tm_set_trx_mac(struct mt7996_phy *phy, u8 type, bool en)
++{
++#define UNI_TM_TRX_CTRL 0
++	struct mt7996_dev *dev = phy->dev;
++	struct mt7996_tm_trx_req req = {
++		.param_num = 1,
++		.tag = cpu_to_le16(UNI_TM_TRX_CTRL),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.param_idx = cpu_to_le16(TM_TRX_PARAM_SET_TRX),
++		.band_idx = phy->mt76->band_idx,
++		.testmode_en = 1,
++		.action = TM_TRX_ACTION_SET,
++		.set_trx = {
++			.type = type,
++			.enable = en,
++			.band_idx = phy->mt76->band_idx,
++		}
++	};
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_TRX_PARAM),
++				 &req, sizeof(req), false);
++}
++
++static int
++mt7996_tm_txbf_init(struct mt7996_phy *phy, u16 *val)
++{
++#define EBF_BBP_RX_OFFSET	0x10280
++#define EBF_BBP_RX_ENABLE	(BIT(0) | BIT(15))
++	struct mt7996_dev *dev = phy->dev;
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	bool enable = val[0];
++	void *phase_cal, *pfmu_data, *pfmu_tag;
++	u8 nss, band_idx = phy->mt76->band_idx;
++	enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20;
++	u8 sub_addr = td->is_txbf_dut ? TXBF_DUT_MAC_SUBADDR : TXBF_GOLDEN_MAC_SUBADDR;
++	u8 peer_addr = td->is_txbf_dut ? TXBF_GOLDEN_MAC_SUBADDR : TXBF_DUT_MAC_SUBADDR;
++	u8 bss_addr = TXBF_DUT_MAC_SUBADDR;
++	u8 addr[ETH_ALEN] = {0x00, sub_addr, sub_addr, sub_addr, sub_addr, sub_addr};
++	u8 bssid[ETH_ALEN] = {0x00, bss_addr, bss_addr, bss_addr, bss_addr, bss_addr};
++	u8 peer_addrs[ETH_ALEN] = {0x00, peer_addr, peer_addr, peer_addr, peer_addr, peer_addr};
++	struct mt7996_vif *mvif = (struct mt7996_vif *)phy->monitor_vif->drv_priv;
++
++	if (!enable) {
++		td->bf_en = false;
++		return 0;
++	}
++
++	if (!dev->test.txbf_phase_cal) {
++		phase_cal = devm_kzalloc(dev->mt76.dev,
++					 sizeof(struct mt7996_txbf_phase) *
++					 MAX_PHASE_GROUP_NUM,
++					 GFP_KERNEL);
++		if (!phase_cal)
++			return -ENOMEM;
++
++		dev->test.txbf_phase_cal = phase_cal;
++	}
++
++	if (!dev->test.txbf_pfmu_data) {
++		/* allocate max size for 5x5 pfmu data */
++		pfmu_data = devm_kzalloc(dev->mt76.dev,
++					 MT7996_TXBF_PFMU_DATA_LEN_5X5,
++					 GFP_KERNEL);
++		if (!pfmu_data)
++			return -ENOMEM;
++
++		dev->test.txbf_pfmu_data = pfmu_data;
++	}
++
++	if (!dev->test.txbf_pfmu_tag) {
++		pfmu_tag = devm_kzalloc(dev->mt76.dev,
++					sizeof(struct mt7996_pfmu_tag), GFP_KERNEL);
++		if (!pfmu_tag)
++			return -ENOMEM;
++
++		dev->test.txbf_pfmu_tag = pfmu_tag;
++	}
++
++	td->bf_en = true;
++	dev->ibf = td->ibf;
++	memcpy(td->addr[0], peer_addrs, ETH_ALEN);
++	memcpy(td->addr[1], addr, ETH_ALEN);
++	memcpy(td->addr[2], bssid, ETH_ALEN);
++	memcpy(phy->monitor_vif->addr, addr, ETH_ALEN);
++	mt7996_tm_set_mac_addr(dev, td->addr[0], SET_ID(DA));
++	mt7996_tm_set_mac_addr(dev, td->addr[1], SET_ID(SA));
++	mt7996_tm_set_mac_addr(dev, td->addr[2], SET_ID(BSSID));
++
++	/* bss idx & omac idx should be set to band idx for ibf cal */
++	mvif->mt76.idx = band_idx;
++	dev->mt76.vif_mask |= BIT_ULL(mvif->mt76.idx);
++	mvif->mt76.omac_idx = band_idx;
++	phy->omac_mask |= BIT_ULL(mvif->mt76.omac_idx);
++
++	mt7996_mcu_add_dev_info(phy, phy->monitor_vif, true);
++	mt7996_mcu_add_bss_info(phy, phy->monitor_vif, true);
++
++	if (td->ibf) {
++		if (td->is_txbf_dut) {
++			/* Enable ITxBF Capability */
++			mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
++			mt7996_tm_set_trx_mac(phy, TM_TRX_MAC_TX, true);
++
++			td->tx_ipg = 999;
++			td->tx_mpdu_len = 1024;
++			td->tx_antenna_mask = phy->mt76->chainmask >> dev->chainshift[band_idx];
++			nss = hweight8(td->tx_antenna_mask);
++			if (nss > 1 && nss <= 4)
++				td->tx_rate_idx = 15 + 8 * (nss - 2);
++			else
++				td->tx_rate_idx = 31;
++		} else {
++			td->tx_antenna_mask = 1;
++			td->tx_mpdu_len = 1024;
++			td->tx_rate_idx = 0;
++			mt76_set(dev, EBF_BBP_RX_OFFSET, EBF_BBP_RX_ENABLE);
++			dev_info(dev->mt76.dev, "Set BBP RX CR = %x\n",
++				 mt76_rr(dev, EBF_BBP_RX_OFFSET));
++		}
++
++		td->tx_rate_mode = MT76_TM_TX_MODE_HT;
++		td->tx_rate_sgi = 0;
++		/* 5T5R ibf */
++		if (nss == 5) {
++			td->tx_rate_mode = MT76_TM_TX_MODE_VHT;
++			td->tx_rate_idx = 7;
++		}
++	} else {
++		if (td->is_txbf_dut) {
++			/* Enable ETxBF Capability */
++			mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
++			td->tx_antenna_mask = phy->mt76->chainmask >> dev->chainshift[band_idx];
++			td->tx_spe_idx = 24 + phy->mt76->band_idx;
++			if (td->tx_rate_mode == MT76_TM_TX_MODE_VHT ||
++			    td->tx_rate_mode == MT76_TM_TX_MODE_HE_SU)
++				mt7996_tm_set(dev, SET_ID(NSS), td->tx_rate_nss);
++
++			mt7996_tm_set(dev, SET_ID(ENCODE_MODE), td->tx_rate_ldpc);
++			mt7996_tm_set(dev, SET_ID(TX_COUNT), td->tx_count);
++		} else {
++			/* Turn On BBP CR for RX */
++			mt76_set(dev, EBF_BBP_RX_OFFSET, EBF_BBP_RX_ENABLE);
++			dev_info(dev->mt76.dev, "Set BBP RX CR = %x\n",
++				 mt76_rr(dev, EBF_BBP_RX_OFFSET));
++
++			td->tx_antenna_mask = 1;
++		}
++		width = phy->mt76->chandef.width;
++
++		if (td->tx_rate_mode == MT76_TM_TX_MODE_EHT_MU)
++			td->tx_rate_mode = MT76_TM_TX_MODE_EHT_SU;
++	}
++	mt76_testmode_param_set(td, MT76_TM_ATTR_TX_ANTENNA);
++
++	mt7996_tm_set(dev, SET_ID(TX_MODE),
++		      mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY));
++	mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
++	mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi);
++	mt7996_tm_set(dev, SET_ID(CBW),
++		      mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW));
++	mt7996_tm_set(dev, SET_ID(DBW),
++		      mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW));
++	mt7996_tm_set_antenna(phy, SET_ID(TX_PATH));
++	mt7996_tm_set_antenna(phy, SET_ID(RX_PATH));
++	mt7996_tm_set(dev, SET_ID(IPG), td->tx_ipg);
++	mt7996_tm_set(dev, SET_ID(TX_LEN), td->tx_mpdu_len);
++	mt7996_tm_set(dev, SET_ID(TX_TIME), 0);
++	mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(TX_COMMIT));
++
++	return 0;
++}
++
++static inline void
++mt7996_tm_txbf_phase_copy(struct mt7996_dev *dev, void *des, void *src, int group)
++{
++	int phase_size;
++
++	if (group && get_ibf_version(dev) == IBF_VER_1)
++		phase_size = sizeof(struct mt7996_txbf_phase_info_5g);
++	else if (get_ibf_version(dev) == IBF_VER_1)
++		phase_size = sizeof(struct mt7996_txbf_phase_info_2g);
++	else if (group)
++		phase_size = sizeof(struct mt7992_txbf_phase_info_5g);
++	else
++		phase_size = sizeof(struct mt7992_txbf_phase_info_2g);
++
++	memcpy(des, src, phase_size);
++}
++
++static int
++mt7996_tm_txbf_phase_comp(struct mt7996_phy *phy, u16 *val)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct mt7996_tm_bf_req req = {
++		.phase_comp = {
++			.tag = cpu_to_le16(BF_IBF_PHASE_COMP),
++			.len = cpu_to_le16(sizeof(req.phase_comp)),
++			.bw = val[0],
++			.jp_band = (val[2] == 1) ? 1 : 0,
++			.band_idx = phy->mt76->band_idx,
++			.read_from_e2p = val[3],
++			.disable = val[4],
++			.group = val[2],
++		}
++	};
++	struct mt7996_txbf_phase *phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal;
++	int group = val[2];
++
++	wait_event_timeout(dev->mt76.tx_wait, phase[group].status != 0, HZ);
++	mt7996_tm_txbf_phase_copy(dev, req.phase_comp.buf, phase[group].buf, group);
++
++	pr_info("ibf cal process: phase comp info\n");
++	print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1,
++		       &req, sizeof(req), 0);
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req,
++				 sizeof(req), false);
++}
++
++static int
++mt7996_tm_txbf_profile_tag_write(struct mt7996_phy *phy, u8 pfmu_idx, struct mt7996_pfmu_tag *tag)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct mt7996_tm_bf_req req = {
++		.pfmu_tag = {
++			.tag = cpu_to_le16(BF_PFMU_TAG_WRITE),
++			.len = cpu_to_le16(sizeof(req.pfmu_tag)),
++			.pfmu_id = pfmu_idx,
++			.bfer = true,
++			.band_idx = phy->mt76->band_idx,
++		}
++	};
++
++	memcpy(req.pfmu_tag.buf, tag, sizeof(*tag));
++	wait_event_timeout(dev->mt76.tx_wait, tag->t1.pfmu_idx != 0, HZ);
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req,
++				 sizeof(req), false);
++}
++
++static int
++mt7996_tm_add_txbf_sta(struct mt7996_phy *phy, u8 pfmu_idx, u8 nr, u8 nc, bool ebf)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct {
++		struct sta_req_hdr hdr;
++		struct sta_rec_bf bf;
++	} __packed req = {
++		.hdr = {
++			.bss_idx = phy->mt76->band_idx,
++			.wlan_idx_lo = to_wcid_lo(phy->mt76->band_idx + 1),
++			.tlv_num = 1,
++			.is_tlv_append = 1,
++			.muar_idx = 0,
++			.wlan_idx_hi = to_wcid_hi(phy->mt76->band_idx + 1),
++		},
++		.bf = {
++			.tag = cpu_to_le16(STA_REC_BF),
++			.len = cpu_to_le16(sizeof(req.bf)),
++			.pfmu = cpu_to_le16(pfmu_idx),
++			.sounding_phy = 1,
++			.bf_cap = ebf,
++			.ncol = nc,
++			.nrow = nr,
++			.ibf_timeout = 0xff,
++			.tx_mode = mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY),
++		},
++	};
++	u8 ndp_rate, ndpa_rate, rept_poll_rate, bf_bw;
++
++	if ((td->tx_rate_mode == MT76_TM_TX_MODE_HE_SU ||
++	     td->tx_rate_mode == MT76_TM_TX_MODE_EHT_SU) && !td->ibf) {
++		rept_poll_rate = 0x49;
++		ndpa_rate = 0x49;
++		ndp_rate = 0;
++	} else if (td->tx_rate_mode == MT76_TM_TX_MODE_VHT && !td->ibf) {
++		rept_poll_rate = 0x9;
++		ndpa_rate = 0x9;
++		ndp_rate = 0;
++	} else {
++		rept_poll_rate = 0;
++		ndpa_rate = 0;
++		if (nr == 1)
++			ndp_rate = 8;
++		else if (nr == 2)
++			ndp_rate = 16;
++		else if (nr == 4)
++			ndp_rate = 32;
++		else
++			ndp_rate = 24;
++
++		/* 5T5R ebf profile for ibf cal */
++		if (nr == 4 && td->ibf && ebf) {
++			ndp_rate = 0;
++			ndpa_rate = 11;
++		}
++	}
++
++	bf_bw = mt7996_tm_bw_mapping(phy->mt76->chandef.width, BW_MAP_NL_TO_BF);
++	req.bf.ndp_rate = ndp_rate;
++	req.bf.ndpa_rate = ndpa_rate;
++	req.bf.rept_poll_rate = rept_poll_rate;
++	req.bf.bw = bf_bw;
++	req.bf.tx_mode = (td->tx_rate_mode == MT76_TM_TX_MODE_EHT_SU) ? 0xf : req.bf.tx_mode;
++
++	if (ebf) {
++		req.bf.mem[0].row = 0;
++		req.bf.mem[1].row = 1;
++		req.bf.mem[2].row = 2;
++		req.bf.mem[3].row = 3;
++	} else {
++		req.bf.mem[0].row = 4;
++		req.bf.mem[1].row = 5;
++		req.bf.mem[2].row = 6;
++		req.bf.mem[3].row = 7;
++	}
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), &req,
++				 sizeof(req), true);
++}
++
++static int
++mt7996_tm_txbf_profile_update(struct mt7996_phy *phy, u16 *val, bool ebf)
++{
++#define MT_ARB_IBF_ENABLE			(BIT(0) | GENMASK(9, 8))
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct mt7996_dev *dev = phy->dev;
++	struct mt7996_pfmu_tag *tag = dev->test.txbf_pfmu_tag;
++	u8 rate, pfmu_idx = val[0], nc = val[2], nr;
++	int ret;
++	bool is_atenl = val[5];
++
++	if (td->tx_antenna_mask == 3)
++		nr = 1;
++	else if (td->tx_antenna_mask == 7)
++		nr = 2;
++	else if (td->tx_antenna_mask == 31)
++		nr = 4;
++	else
++		nr = 3;
++
++	memset(tag, 0, sizeof(*tag));
++	tag->t1.pfmu_idx = pfmu_idx;
++	tag->t1.ebf = ebf;
++	tag->t1.nr = nr;
++	tag->t1.nc = nc;
++	tag->t1.invalid_prof = true;
++	tag->t1.data_bw = mt7996_tm_bw_mapping(phy->mt76->chandef.width, BW_MAP_NL_TO_BF);
++	tag->t2.se_idx = td->tx_spe_idx;
++
++	if (ebf) {
++		tag->t1.row_id1 = 0;
++		tag->t1.row_id2 = 1;
++		tag->t1.row_id3 = 2;
++		tag->t1.row_id4 = 3;
++		tag->t1.lm = mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_LM);
++	} else {
++		tag->t1.row_id1 = 4;
++		tag->t1.row_id2 = 5;
++		tag->t1.row_id3 = 6;
++		tag->t1.row_id4 = 7;
++		rate = nr == 4 ? td->tx_rate_mode : MT76_TM_TX_MODE_OFDM;
++		tag->t1.lm = mt7996_tm_rate_mapping(rate, RATE_MODE_TO_LM);
++
++		tag->t2.ibf_timeout = 0xff;
++		tag->t2.ibf_nr = nr;
++		tag->t2.ibf_nc = nc;
++	}
++
++	ret = mt7996_tm_txbf_profile_tag_write(phy, pfmu_idx, tag);
++	if (ret)
++		return ret;
++
++	ret = mt7996_tm_add_txbf_sta(phy, pfmu_idx, nr, nc, ebf);
++	if (ret)
++		return ret;
++
++	if (!is_atenl && !td->ibf) {
++		mt76_set(dev, MT_ARB_TQSAXM0(phy->mt76->band_idx), MT_ARB_TQSAXM_ALTX_START_MASK);
++		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)));
++	} else if (!is_atenl && td->ibf && ebf) {
++		/* iBF's ebf profile update */
++		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)));
++	}
++
++	if (!ebf && is_atenl)
++		return mt7996_tm_txbf_apply_tx(phy, 1, false, true, true);
++
++	return 0;
++}
++
++static int
++mt7996_tm_txbf_phase_cal(struct mt7996_phy *phy, u16 *val)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct mt7996_tm_bf_req req = {
++		.phase_cal = {
++			.tag = cpu_to_le16(BF_PHASE_CALIBRATION),
++			.len = cpu_to_le16(sizeof(req.phase_cal)),
++			.group = val[0],
++			.group_l_m_n = val[1],
++			.sx2 = val[2],
++			.cal_type = val[3],
++			.lna_gain_level = val[4],
++			.band_idx = phy->mt76->band_idx,
++			.version = val[5],
++		},
++	};
++	struct mt7996_txbf_phase *phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal;
++
++	/* reset phase status before update phase cal data */
++	phase[req.phase_cal.group].status = 0;
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req,
++				 sizeof(req), false);
++}
++
++static int
++mt7996_tm_txbf_profile_update_all(struct mt7996_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];
++	u16 angle21 = val[3];
++	u16 angle31 = val[4];
++	u16 angle41 = val[5];
++	u16 angle51 = val[6];
++	s16 phi11 = 0, phi21 = 0, phi31 = 0, phi41 = 0;
++	s16 *pfmu_data;
++	int offs = subc_id * sizeof(struct mt7996_pfmu_data) / sizeof(*pfmu_data);
++
++	if (subc_id > MT7996_TXBF_SUBCAR_NUM - 1)
++		return -EINVAL;
++
++	if (nss == 2) {
++		phi11 = (s16)(angle21 - angle11);
++	} else if (nss == 3) {
++		phi11 = (s16)(angle31 - angle11);
++		phi21 = (s16)(angle31 - angle21);
++	} else if (nss == 5) {
++		phi11 = (s16)(angle51 - angle11);
++		phi21 = (s16)(angle51 - angle21);
++		phi31 = (s16)(angle51 - angle31);
++		phi41 = (s16)(angle51 - angle41);
++		offs = subc_id * sizeof(struct mt7996_pfmu_data_5x5) / sizeof(*pfmu_data);
++	} else {
++		phi11 = (s16)(angle41 - angle11);
++		phi21 = (s16)(angle41 - angle21);
++		phi31 = (s16)(angle41 - angle31);
++	}
++
++	pfmu_data = (s16 *)phy->dev->test.txbf_pfmu_data;
++	pfmu_data += offs;
++
++	if (subc_id < 32)
++		pfmu_data[0] = cpu_to_le16(subc_id + 224);
++	else
++		pfmu_data[0] = cpu_to_le16(subc_id - 32);
++
++	pfmu_data[1] = cpu_to_le16(phi11);
++	pfmu_data[2] = cpu_to_le16(phi21);
++	pfmu_data[3] = cpu_to_le16(phi31);
++	if (nss == 5)
++		pfmu_data[4] = cpu_to_le16(phi41);
++
++	if (subc_id == MT7996_TXBF_SUBCAR_NUM - 1) {
++		struct mt7996_dev *dev = phy->dev;
++		struct mt7996_tm_bf_req req = {
++			.pfmu_data_all = {
++				.tag = cpu_to_le16(BF_PROFILE_WRITE_20M_ALL_5X5),
++				.len = cpu_to_le16(sizeof(req.pfmu_data_all)),
++				.pfmu_id = pfmu_idx,
++				.band_idx = phy->mt76->band_idx,
++			},
++		};
++		int size = MT7996_TXBF_PFMU_DATA_LEN_5X5;
++
++		if (nss != 5) {
++			size = MT7996_TXBF_PFMU_DATA_LEN;
++			req.pfmu_data_all.tag = cpu_to_le16(BF_PROFILE_WRITE_20M_ALL);
++			req.pfmu_data_all.len = cpu_to_le16(sizeof(req.pfmu_data_all) -
++							    MT7996_TXBF_PFMU_DATA_LEN_5X5 + size);
++		}
++		memcpy(req.pfmu_data_all.buf, dev->test.txbf_pfmu_data, size);
++
++		return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF),
++					 &req, sizeof(req), true);
++	}
++
++	return 0;
++}
++
++static int
++mt7996_tm_txbf_e2p_update(struct mt7996_phy *phy)
++{
++#define TXBF_PHASE_EEPROM_START_OFFSET		0xc00
++#define TXBF_PHASE_GROUP_EEPROM_OFFSET_VER_1	46
++#define TXBF_PHASE_G0_EEPROM_OFFSET_VER_2	sizeof(struct mt7992_txbf_phase_info_2g)
++#define TXBF_PHASE_GX_EEPROM_OFFSET_VER_2	sizeof(struct mt7992_txbf_phase_info_5g)
++	struct mt7996_txbf_phase *phase, *p;
++	struct mt7996_dev *dev = phy->dev;
++	u8 *eeprom = dev->mt76.eeprom.data;
++	u16 offset;
++	int i;
++
++	offset = TXBF_PHASE_EEPROM_START_OFFSET;
++	phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal;
++	for (i = 0; i < MAX_PHASE_GROUP_NUM; i++) {
++		p = &phase[i];
++
++		if (!p->status)
++			continue;
++
++		/* copy phase cal data to eeprom */
++		mt7996_tm_txbf_phase_copy(dev, eeprom + offset, p->buf, i);
++		if (get_ibf_version(dev) == IBF_VER_1)
++			offset += TXBF_PHASE_GROUP_EEPROM_OFFSET_VER_1;
++		else
++			offset += i ? TXBF_PHASE_GX_EEPROM_OFFSET_VER_2 :
++				      TXBF_PHASE_G0_EEPROM_OFFSET_VER_2;
++	}
++
++	return 0;
++}
++
++static int
++mt7996_tm_txbf_apply_tx(struct mt7996_phy *phy, u16 wlan_idx, bool ebf,
++			bool ibf, bool phase_cal)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct mt7996_tm_bf_req req = {
++		.tx_apply = {
++			.tag = cpu_to_le16(BF_DATA_PACKET_APPLY),
++			.len = cpu_to_le16(sizeof(req.tx_apply)),
++			.wlan_idx = cpu_to_le16(wlan_idx),
++			.ebf = ebf,
++			.ibf = ibf,
++			.phase_cal = phase_cal,
++		},
++	};
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req, sizeof(req), false);
++}
++
++static int
++mt7996_tm_txbf_set_tx(struct mt7996_phy *phy, u16 *val)
++{
++	bool bf_on = val[0], update = val[3];
++	struct mt7996_dev *dev = phy->dev;
++	struct mt7996_pfmu_tag *tag = dev->test.txbf_pfmu_tag;
++	struct mt76_testmode_data *td = &phy->mt76->test;
++
++	if (bf_on) {
++		mt7996_tm_set_rx_frames(phy, false);
++		mt7996_tm_set_tx_frames(phy, false);
++		mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, 2, true);
++		tag->t1.invalid_prof = false;
++		mt7996_tm_txbf_profile_tag_write(phy, 2, tag);
++		td->bf_ever_en = true;
++
++		if (update)
++			mt7996_tm_txbf_apply_tx(phy, 1, 0, 1, 1);
++	} else {
++		if (!td->bf_ever_en) {
++			mt7996_tm_set_rx_frames(phy, false);
++			mt7996_tm_set_tx_frames(phy, false);
++			td->ibf = false;
++			td->ebf = false;
++
++			if (update)
++				mt7996_tm_txbf_apply_tx(phy, 1, 0, 0, 0);
++		} else {
++			td->bf_ever_en = false;
++
++			mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, 2, true);
++			tag->t1.invalid_prof = true;
++			mt7996_tm_txbf_profile_tag_write(phy, 2, tag);
++		}
++	}
++
++	return 0;
++}
++
++static int
++mt7996_tm_trigger_sounding(struct mt7996_phy *phy, u16 *val, bool en)
++{
++	struct mt7996_dev *dev = phy->dev;
++	u8 sounding_mode = val[0];
++	u8 sta_num = val[1];
++	u32 sounding_interval = (u32)val[2] << 2;	/* input unit: 4ms */
++	u16 tag = en ? BF_SOUNDING_ON : BF_SOUNDING_OFF;
++	struct mt7996_tm_bf_req req = {
++		.sounding = {
++			.tag = cpu_to_le16(tag),
++			.len = cpu_to_le16(sizeof(req.sounding)),
++			.snd_mode = sounding_mode,
++			.sta_num = sta_num,
++			.wlan_id = {
++				cpu_to_le16(val[3]),
++				cpu_to_le16(val[4]),
++				cpu_to_le16(val[5]),
++				cpu_to_le16(val[6])
++			},
++			.snd_period = cpu_to_le32(sounding_interval),
++		},
++	};
++
++	if (sounding_mode > SOUNDING_MODE_MAX)
++		return -EINVAL;
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF),
++				 &req, sizeof(req), false);
++}
++
++static int
++mt7996_tm_txbf_txcmd(struct mt7996_phy *phy, u16 *val)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct mt7996_tm_bf_req req = {
++		.txcmd = {
++			.tag = cpu_to_le16(BF_CMD_TXCMD),
++			.len = cpu_to_le16(sizeof(req.txcmd)),
++			.action = val[0],
++			.bf_manual = val[1],
++			.bf_bit = val[2],
++		},
++	};
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req, sizeof(req), false);
++}
++
++static int
++mt7996_tm_set_txbf(struct mt7996_phy *phy)
++{
++#define TXBF_IS_DUT_MASK	BIT(0)
++#define TXBF_IBF_MASK		BIT(1)
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	u16 *val = td->txbf_param;
++
++	dev_info(phy->dev->mt76.dev,
++		 "ibf cal process: act = %u, val = %u, %u, %u, %u, %u, %u, %u, %u\n",
++		 td->txbf_act, val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]);
++
++	switch (td->txbf_act) {
++	case MT76_TM_TXBF_ACT_GOLDEN_INIT:
++	case MT76_TM_TXBF_ACT_INIT:
++	case MT76_TM_TX_EBF_ACT_GOLDEN_INIT:
++	case MT76_TM_TX_EBF_ACT_INIT:
++		td->ibf = !u32_get_bits(td->txbf_act, TXBF_IBF_MASK);
++		td->ebf = true;
++		td->is_txbf_dut = !!u32_get_bits(td->txbf_act, TXBF_IS_DUT_MASK);
++		return mt7996_tm_txbf_init(phy, val);
++	case MT76_TM_TXBF_ACT_UPDATE_CH:
++		mt7996_tm_update_channel(phy);
++		break;
++	case MT76_TM_TXBF_ACT_PHASE_COMP:
++		return mt7996_tm_txbf_phase_comp(phy, val);
++	case MT76_TM_TXBF_ACT_TX_PREP:
++		return mt7996_tm_txbf_set_tx(phy, val);
++	case MT76_TM_TXBF_ACT_IBF_PROF_UPDATE:
++		return mt7996_tm_txbf_profile_update(phy, val, false);
++	case MT76_TM_TXBF_ACT_EBF_PROF_UPDATE:
++		return mt7996_tm_txbf_profile_update(phy, val, true);
++	case MT76_TM_TXBF_ACT_PHASE_CAL:
++		return mt7996_tm_txbf_phase_cal(phy, val);
++	case MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD:
++	case MT76_TM_TXBF_ACT_PROF_UPDATE_ALL:
++		return mt7996_tm_txbf_profile_update_all(phy, val);
++	case MT76_TM_TXBF_ACT_E2P_UPDATE:
++		return mt7996_tm_txbf_e2p_update(phy);
++	case MT76_TM_TXBF_ACT_APPLY_TX: {
++		u16 wlan_idx = val[0];
++		bool ebf = !!val[1], ibf = !!val[2], phase_cal = !!val[4];
++
++		return mt7996_tm_txbf_apply_tx(phy, wlan_idx, ebf, ibf, phase_cal);
++	}
++	case MT76_TM_TXBF_ACT_TRIGGER_SOUNDING:
++		return mt7996_tm_trigger_sounding(phy, val, true);
++	case MT76_TM_TXBF_ACT_STOP_SOUNDING:
++		memset(val, 0, sizeof(td->txbf_param));
++		return mt7996_tm_trigger_sounding(phy, val, false);
++	case MT76_TM_TXBF_ACT_PROFILE_TAG_READ:
++	case MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE:
++	case MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID: {
++		u8 pfmu_idx = val[0];
++		bool bfer = !!val[1];
++		struct mt7996_dev *dev = phy->dev;
++		struct mt7996_pfmu_tag *tag = dev->test.txbf_pfmu_tag;
++
++		if (!tag) {
++			dev_err(dev->mt76.dev,
++				"pfmu tag is not initialized!\n");
++			return 0;
++		}
++
++		if (td->txbf_act == MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE)
++			return mt7996_tm_txbf_profile_tag_write(phy, pfmu_idx, tag);
++		else if (td->txbf_act == MT76_TM_TXBF_ACT_PROFILE_TAG_READ)
++			return mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, pfmu_idx, bfer);
++
++		tag->t1.invalid_prof = !!val[0];
++
++		return 0;
++	}
++	case MT76_TM_TXBF_ACT_STA_REC_READ:
++		return mt7996_mcu_set_txbf_internal(phy, BF_STA_REC_READ, val[0], 0);
++	case MT76_TM_TXBF_ACT_TXCMD:
++		return mt7996_tm_txbf_txcmd(phy, val);
++	default:
++		break;
++	};
++
++	return 0;
++}
++
+ static void
+ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
+ {
+@@ -1086,6 +1822,8 @@ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
+ 		mt7996_tm_set_ipi(phy);
+ 	if (changed & BIT(TM_CHANGED_IPI_RESET))
+ 		mt7996_tm_ipi_hist_ctrl(phy, NULL, RDD_SET_IPI_HIST_RESET);
++	if (changed & BIT(TM_CHANGED_TXBF_ACT))
++		mt7996_tm_set_txbf(phy);
+ }
+ 
+ static int
+diff --git a/mt7996/testmode.h b/mt7996/testmode.h
+index 78662b2e..f97ccb26 100644
+--- a/mt7996/testmode.h
++++ b/mt7996/testmode.h
+@@ -27,6 +27,17 @@ enum {
+ 	FW_CDBW_8080MHZ,
+ };
+ 
++enum {
++	BF_CDBW_20MHZ,
++	BF_CDBW_40MHZ,
++	BF_CDBW_80MHZ,
++	BF_CDBW_160MHZ,
++	BF_CDBW_320MHZ,
++	BF_CDBW_10MHZ = BF_CDBW_320MHZ,
++	BF_CDBW_5MHZ,
++	BF_CDBW_8080MHZ,
++};
++
+ #define FIRST_CONTROL_CHAN_BITMAP_BW40		0x5555555
+ #define FIRST_CONTROL_CHAN_BITMAP_BW80		0x111111
+ #define FIRST_CONTROL_CHAN_BITMAP_BW160		0x100101
+@@ -34,12 +45,20 @@ enum {
+ enum bw_mapping_method {
+ 	BW_MAP_NL_TO_FW,
+ 	BW_MAP_NL_TO_TM,
++	BW_MAP_NL_TO_BF,
+ 	BW_MAP_NL_TO_MHZ,
+ 	BW_MAP_NL_TO_CONTROL_BITMAP_5G,
+ 
+ 	NUM_BW_MAP,
+ };
+ 
++enum rate_mapping_type {
++	RATE_MODE_TO_PHY,
++	RATE_MODE_TO_LM,
++
++	NUM_RATE_MAP,
++};
++
+ struct tm_cal_param {
+ 	__le32 func_data;
+ 	u8 band_idx;
+diff --git a/testmode.c b/testmode.c
+index 69147f86..b9f71097 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -462,6 +462,44 @@ out:
+ 	return err;
+ }
+ 
++static int
++mt76_testmode_txbf_profile_update_all_cmd(struct mt76_phy *phy, struct nlattr **tb, u32 state)
++{
++#define PARAM_UNIT	5
++#define PARAM_UNIT_5X5	6
++	static u8 pfmu_idx;
++	struct mt76_testmode_data *td = &phy->test;
++	struct mt76_dev *dev = phy->dev;
++	struct nlattr *cur;
++	u16 tmp_val[PARAM_UNIT_5X5], *val = td->txbf_param;
++	int idx, rem, ret, i = 0;
++	int param_len = td->tx_antenna_mask == 31 ? PARAM_UNIT_5X5 : PARAM_UNIT;
++
++	memset(td->txbf_param, 0, sizeof(td->txbf_param));
++	nla_for_each_nested(cur, tb[MT76_TM_ATTR_TXBF_PARAM], rem) {
++		if (nla_len(cur) != 2)
++			return -EINVAL;
++		idx = i % param_len;
++		tmp_val[idx] = nla_get_u16(cur);
++		if (idx == 1 && (tmp_val[idx] == 0xf0 || tmp_val[idx] == 0xff)) {
++			pfmu_idx = tmp_val[0];
++			return 0;
++		}
++		if (idx == param_len - 1) {
++			val[0] = pfmu_idx;
++			memcpy(val + 1, tmp_val, param_len * sizeof(u16));
++			if (dev->test_ops->set_params) {
++				ret = dev->test_ops->set_params(phy, tb, state);
++				if (ret)
++					return ret;
++			}
++		}
++		i++;
++	}
++
++	return 0;
++}
++
+ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 		      void *data, int len)
+ {
+@@ -607,6 +645,30 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 		}
+ 	}
+ 
++	if (tb[MT76_TM_ATTR_TXBF_ACT]) {
++		struct nlattr *cur;
++		int rem, idx = 0;
++
++		if (!tb[MT76_TM_ATTR_TXBF_PARAM] ||
++		    mt76_tm_get_u8(tb[MT76_TM_ATTR_TXBF_ACT], &td->txbf_act,
++				   0, MT76_TM_TXBF_ACT_MAX))
++			goto out;
++
++		if (td->txbf_act == MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD) {
++			err = mt76_testmode_txbf_profile_update_all_cmd(phy, tb, state);
++			goto out;
++		}
++
++		memset(td->txbf_param, 0, sizeof(td->txbf_param));
++		nla_for_each_nested(cur, tb[MT76_TM_ATTR_TXBF_PARAM], rem) {
++			if (nla_len(cur) != 2 ||
++			    idx >= ARRAY_SIZE(td->txbf_param))
++				goto out;
++
++			td->txbf_param[idx++] = nla_get_u16(cur);
++		}
++	}
++
+ 	if (dev->test_ops->set_params) {
+ 		err = dev->test_ops->set_params(phy, tb, state);
+ 		if (err)
+diff --git a/testmode.h b/testmode.h
+index 5d677f8c..bda7624a 100644
+--- a/testmode.h
++++ b/testmode.h
+@@ -286,6 +286,59 @@ enum mt76_testmode_eeprom_action {
+ 	MT76_TM_EEPROM_ACTION_MAX = NUM_MT76_TM_EEPROM_ACTION - 1,
+ };
+ 
++/**
++ * enum mt76_testmode_txbf_act - txbf action
++ *
++ * @MT76_TM_TXBF_ACT_GOLDEN_INIT: init ibf setting for golden device
++ * @MT76_TM_TXBF_ACT_INIT: init ibf setting for DUT
++ * @MT76_TM_TX_EBF_ACT_GOLDEN_INIT: init ebf setting for golden device
++ * @MT76_TM_TX_EBF_ACT_INIT: init ebf setting for DUT
++ * @MT76_TM_TXBF_ACT_UPDATE_CH: update channel info
++ * @MT76_TM_TXBF_ACT_PHASE_COMP: txbf phase compensation
++ * @MT76_TM_TXBF_ACT_TX_PREP: TX preparation for txbf
++ * @MT76_TM_TXBF_ACT_IBF_PROF_UPDATE: update ibf profile (pfmu tag, bf sta record)
++ * @MT76_TM_TXBF_ACT_EBF_PROF_UPDATE: update ebf profile
++ * @MT76_TM_TXBF_ACT_APPLY_TX: apply TX setting for txbf
++ * @MT76_TM_TXBF_ACT_PHASE_CAL: perform txbf phase calibration
++ * @MT76_TM_TXBF_ACT_PROF_UPDATE_ALL: update bf profile via instrument
++ * @MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD: update bf profile via instrument
++ * @MT76_TM_TXBF_ACT_E2P_UPDATE: write back txbf calibration result to eeprom
++ * @MT76_TM_TXBF_ACT_TRIGGER_SOUNDING: trigger beamformer to send sounding packet
++ * @MT76_TM_TXBF_ACT_STOP_SOUNDING: stop sending sounding packet
++ * @MT76_TM_TXBF_ACT_PROFILE_TAG_READ: read pfmu tag
++ * @MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE: update pfmu tag
++ * @MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID: invalidate pfmu tag
++ * @MT76_TM_TXBF_ACT_STA_REC_READ: read bf sta record
++ * @MT76_TM_TXBF_ACT_TXCMD: configure txcmd bf bit manually
++ */
++enum mt76_testmode_txbf_act {
++	MT76_TM_TXBF_ACT_GOLDEN_INIT,
++	MT76_TM_TXBF_ACT_INIT,
++	MT76_TM_TX_EBF_ACT_GOLDEN_INIT,
++	MT76_TM_TX_EBF_ACT_INIT,
++	MT76_TM_TXBF_ACT_UPDATE_CH,
++	MT76_TM_TXBF_ACT_PHASE_COMP,
++	MT76_TM_TXBF_ACT_TX_PREP,
++	MT76_TM_TXBF_ACT_IBF_PROF_UPDATE,
++	MT76_TM_TXBF_ACT_EBF_PROF_UPDATE,
++	MT76_TM_TXBF_ACT_APPLY_TX,
++	MT76_TM_TXBF_ACT_PHASE_CAL,
++	MT76_TM_TXBF_ACT_PROF_UPDATE_ALL,
++	MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD,
++	MT76_TM_TXBF_ACT_E2P_UPDATE,
++	MT76_TM_TXBF_ACT_TRIGGER_SOUNDING,
++	MT76_TM_TXBF_ACT_STOP_SOUNDING,
++	MT76_TM_TXBF_ACT_PROFILE_TAG_READ,
++	MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE,
++	MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID,
++	MT76_TM_TXBF_ACT_STA_REC_READ,
++	MT76_TM_TXBF_ACT_TXCMD,
++
++	/* keep last */
++	NUM_MT76_TM_TXBF_ACT,
++	MT76_TM_TXBF_ACT_MAX = NUM_MT76_TM_TXBF_ACT - 1,
++};
++
+ extern const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS];
+ 
+ #endif
+diff --git a/tools/fields.c b/tools/fields.c
+index 77696ce7..f793d1a5 100644
+--- a/tools/fields.c
++++ b/tools/fields.c
+@@ -44,6 +44,30 @@ static const char * const testmode_offchan_bw[] = {
+ 	[NL80211_CHAN_WIDTH_160] = "160",
+ };
+ 
++static const char * const testmode_txbf_act[] = {
++	[MT76_TM_TXBF_ACT_GOLDEN_INIT] = "golden_init",
++	[MT76_TM_TXBF_ACT_INIT] = "init",
++	[MT76_TM_TX_EBF_ACT_GOLDEN_INIT] = "ebf_golden_init",
++	[MT76_TM_TX_EBF_ACT_INIT] = "ebf_init",
++	[MT76_TM_TXBF_ACT_UPDATE_CH] = "update_ch",
++	[MT76_TM_TXBF_ACT_PHASE_COMP] = "phase_comp",
++	[MT76_TM_TXBF_ACT_TX_PREP] = "tx_prep",
++	[MT76_TM_TXBF_ACT_IBF_PROF_UPDATE] = "ibf_prof_update",
++	[MT76_TM_TXBF_ACT_EBF_PROF_UPDATE] = "ebf_prof_update",
++	[MT76_TM_TXBF_ACT_APPLY_TX] = "apply_tx",
++	[MT76_TM_TXBF_ACT_PHASE_CAL] = "phase_cal",
++	[MT76_TM_TXBF_ACT_PROF_UPDATE_ALL] = "prof_update",
++	[MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD] = "prof_update_all",
++	[MT76_TM_TXBF_ACT_E2P_UPDATE] = "e2p_update",
++	[MT76_TM_TXBF_ACT_TRIGGER_SOUNDING] = "trigger_sounding",
++	[MT76_TM_TXBF_ACT_STOP_SOUNDING] = "stop_sounding",
++	[MT76_TM_TXBF_ACT_PROFILE_TAG_READ] = "pfmu_tag_read",
++	[MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE] = "pfmu_tag_write",
++	[MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID] = "set_invalid_prof",
++	[MT76_TM_TXBF_ACT_STA_REC_READ] = "sta_rec_read",
++	[MT76_TM_TXBF_ACT_TXCMD] = "txcmd",
++};
++
+ static void print_enum(const struct tm_field *field, struct nlattr *attr)
+ {
+ 	unsigned int i = nla_get_u8(attr);
+@@ -94,6 +118,17 @@ static void print_s8(const struct tm_field *field, struct nlattr *attr)
+ 	printf("%d", (int8_t)nla_get_u8(attr));
+ }
+ 
++static bool parse_u16_hex(const struct tm_field *field, int idx,
++			  struct nl_msg *msg, const char *val)
++{
++	return !nla_put_u16(msg, idx, strtoul(val, NULL, 16));
++}
++
++static void print_u16_hex(const struct tm_field *field, struct nlattr *attr)
++{
++	printf("%d", nla_get_u16(attr));
++}
++
+ static bool parse_u32(const struct tm_field *field, int idx,
+ 		      struct nl_msg *msg, const char *val)
+ {
+@@ -399,6 +434,8 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
+ 	FIELD(u8, AID, "aid"),
+ 	FIELD(u8, RU_ALLOC, "ru_alloc"),
+ 	FIELD(u8, RU_IDX, "ru_idx"),
++	FIELD_ENUM(TXBF_ACT, "txbf_act", testmode_txbf_act),
++	FIELD_ARRAY(u16_hex, TXBF_PARAM, "txbf_param"),
+ 	FIELD(u8, OFF_CH_SCAN_CH, "offchan_ch"),
+ 	FIELD(u8, OFF_CH_SCAN_CENTER_CH, "offchan_center_ch"),
+ 	FIELD_ENUM(OFF_CH_SCAN_BW, "offchan_bw", testmode_offchan_bw),
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0050-mtk-wifi-mt76-mt7996-add-no_beacon-vendor-command-fo.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0050-mtk-wifi-mt76-mt7996-add-no_beacon-vendor-command-fo.patch
deleted file mode 100644
index bc472fd..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0050-mtk-wifi-mt76-mt7996-add-no_beacon-vendor-command-fo.patch
+++ /dev/null
@@ -1,153 +0,0 @@
-From 2fadd891ed50fdb0ba643748abec7d576ff65945 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Wed, 22 Nov 2023 22:42:09 +0800
-Subject: [PATCH 050/116] mtk: wifi: mt76: mt7996: add no_beacon vendor command
- for cert
-
-Add the vendor command to disable/enable beacon
-
-[Usage]
-hostapd_cli -i <interface> no_beacon <value>
-<value>
-0: enable beacon
-1: disable beacon
-
-Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
----
- mt7996/mcu.c    | 11 +++++++++++
- mt7996/mt7996.h |  1 +
- mt7996/vendor.c | 41 +++++++++++++++++++++++++++++++++++++++++
- mt7996/vendor.h | 12 ++++++++++++
- 4 files changed, 65 insertions(+)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 528bce8..b5c6638 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -5100,4 +5100,15 @@ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
- 		break;
- 	}
- }
-+
-+void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
-+{
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct ieee80211_hw *hw = mvif->phy->mt76->hw;
-+	u8 val = *((u8 *)data);
-+
-+	vif->bss_conf.enable_beacon = val;
-+
-+	mt7996_mcu_add_beacon(hw, vif, val);
-+}
- #endif
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 0ebeb24..1bba545 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -835,6 +835,7 @@ void mt7996_set_wireless_amsdu(struct ieee80211_hw *hw, u8 en);
- void mt7996_mcu_set_mimo(struct mt7996_phy *phy);
- int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val);
- int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data);
-+void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
- #endif
- 
- int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
-diff --git a/mt7996/vendor.c b/mt7996/vendor.c
-index ba00ef3..31688c3 100644
---- a/mt7996/vendor.c
-+++ b/mt7996/vendor.c
-@@ -113,6 +113,11 @@ background_radar_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL] = {
- 	[MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE] = {.type = NLA_U8 },
- };
- 
-+static const struct nla_policy
-+beacon_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BEACON_CTRL] = {
-+	[MTK_VENDOR_ATTR_BEACON_CTRL_MODE] = { .type = NLA_U8 },
-+};
-+
- struct mt7996_amnt_data {
- 	u8 idx;
- 	u8 addr[ETH_ALEN];
-@@ -942,6 +947,31 @@ static int mt7996_vendor_background_radar_mode_ctrl(struct wiphy *wiphy,
- 	return mt7996_mcu_rdd_background_disable_timer(dev, !!background_radar_mode);
- }
- 
-+static int mt7996_vendor_beacon_ctrl(struct wiphy *wiphy,
-+				     struct wireless_dev *wdev,
-+				     const void *data,
-+				     int data_len)
-+{
-+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_BEACON_CTRL];
-+	int err;
-+	u8 val8;
-+
-+	err = nla_parse(tb, MTK_VENDOR_ATTR_BEACON_CTRL_MAX, data, data_len,
-+			beacon_ctrl_policy, NULL);
-+	if (err)
-+		return err;
-+
-+	if (tb[MTK_VENDOR_ATTR_BEACON_CTRL_MODE]) {
-+		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_BEACON_CTRL_MODE]);
-+		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-+				mt7996_set_beacon_vif, &val8);
-+	}
-+
-+	return 0;
-+}
-+
-+
- static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- 	{
- 		.info = {
-@@ -1058,6 +1088,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- 		.policy = background_radar_ctrl_policy,
- 		.maxattr = MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX,
- 	},
-+	{
-+		.info = {
-+			.vendor_id = MTK_NL80211_VENDOR_ID,
-+			.subcmd = MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL,
-+		},
-+		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+			 WIPHY_VENDOR_CMD_NEED_RUNNING,
-+		.doit = mt7996_vendor_beacon_ctrl,
-+		.policy = beacon_ctrl_policy,
-+		.maxattr = MTK_VENDOR_ATTR_BEACON_CTRL_MAX,
-+	},
- };
- 
- void mt7996_vendor_register(struct mt7996_phy *phy)
-diff --git a/mt7996/vendor.h b/mt7996/vendor.h
-index 7c812f9..0d1ef32 100644
---- a/mt7996/vendor.h
-+++ b/mt7996/vendor.h
-@@ -16,6 +16,7 @@ enum mtk_nl80211_vendor_subcmds {
- 	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
- 	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
- 	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
-+	MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
- };
- 
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -227,6 +228,17 @@ enum mtk_vendor_attr_pp_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
- };
- 
-+enum mtk_vendor_attr_beacon_ctrl {
-+	MTK_VENDOR_ATTR_BEACON_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_BEACON_CTRL_MODE,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_BEACON_CTRL,
-+	MTK_VENDOR_ATTR_BEACON_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
-+};
-+
- #endif
- 
- #endif
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0051-mtk-mt76-mt7996-add-zwdfs-cert-mode.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0051-mtk-mt76-mt7996-add-zwdfs-cert-mode.patch
new file mode 100644
index 0000000..37a4316
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0051-mtk-mt76-mt7996-add-zwdfs-cert-mode.patch
@@ -0,0 +1,221 @@
+From 65c9dfe452ee272a2d9c1005cde421def099e478 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 22 Sep 2023 12:33:06 +0800
+Subject: [PATCH 051/199] mtk: mt76: mt7996: add zwdfs cert mode
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/mcu.c    | 44 ++++++++++++++++++++++++++++++++------------
+ mt7996/mcu.h    | 14 ++++++++++++++
+ mt7996/mt7996.h |  5 +++++
+ mt7996/vendor.c | 37 +++++++++++++++++++++++++++++++++++++
+ mt7996/vendor.h | 12 ++++++++++++
+ 5 files changed, 100 insertions(+), 12 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 43604ebc..0db30af9 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -4499,18 +4499,7 @@ int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable)
+ int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
+ 		       u8 rx_sel, u8 val)
+ {
+-	struct {
+-		u8 _rsv[4];
+-
+-		__le16 tag;
+-		__le16 len;
+-
+-		u8 ctrl;
+-		u8 rdd_idx;
+-		u8 rdd_rx_sel;
+-		u8 val;
+-		u8 rsv[4];
+-	} __packed req = {
++	struct mt7996_rdd_ctrl req = {
+ 		.tag = cpu_to_le16(UNI_RDD_CTRL_PARM),
+ 		.len = cpu_to_le16(sizeof(req) - 4),
+ 		.ctrl = cmd,
+@@ -4523,6 +4512,37 @@ int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
+ 				 &req, sizeof(req), true);
+ }
+ 
++int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev, bool disable_timer)
++{
++	struct mt7996_rdd_ctrl req = {
++		.tag = cpu_to_le16(UNI_RDD_CTRL_PARM),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.ctrl = RDD_DISABLE_ZW_TIMER,
++		.rdd_idx = MT_RX_SEL2,
++		.disable_timer = disable_timer,
++	};
++
++	if (!is_mt7996(&dev->mt76) ||
++	    (mt76_get_field(dev, MT_PAD_GPIO, MT_PAD_GPIO_ADIE_COMB) % 2))
++		return 0;
++
++	switch (dev->mt76.region) {
++	case NL80211_DFS_ETSI:
++		req.val = 0;
++		break;
++	case NL80211_DFS_JP:
++		req.val = 2;
++		break;
++	case NL80211_DFS_FCC:
++	default:
++		req.val = 1;
++		break;
++	}
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL),
++				 &req, sizeof(req), true);
++}
++
+ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ 				     struct ieee80211_vif *vif,
+ 				     struct ieee80211_sta *sta)
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index c6bb93b3..3025f849 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -119,6 +119,20 @@ struct mt7996_mcu_rdd_report {
+ 	} hw_pulse[32];
+ } __packed;
+ 
++struct mt7996_rdd_ctrl {
++	u8 _rsv[4];
++
++	__le16 tag;
++	__le16 len;
++
++	u8 ctrl;
++	u8 rdd_idx;
++	u8 rdd_rx_sel;
++	u8 val;
++	u8 disable_timer;
++	u8 rsv[3];
++} __packed;
++
+ struct mt7996_mcu_background_chain_ctrl {
+ 	u8 _rsv[4];
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 1b774f5e..5cdc42bf 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -523,8 +523,11 @@ enum mt7996_rdd_cmd {
+ 	RDD_READ_PULSE,
+ 	RDD_RESUME_BF,
+ 	RDD_IRQ_OFF,
++	RDD_DISABLE_ZW_TIMER,
+ };
+ 
++#define RDD_ZW_TIMER_OFF	BIT(31)
++
+ static inline struct mt7996_phy *
+ mt7996_hw_phy(struct ieee80211_hw *hw)
+ {
+@@ -666,6 +669,8 @@ int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable);
+ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy);
+ int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
+ 		       u8 rx_sel, u8 val);
++int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev,
++					    bool disable_timer);
+ int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy,
+ 				     struct cfg80211_chan_def *chandef);
+ int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx,
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index 7ab64471..ba00ef35 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -108,6 +108,11 @@ rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
+ 	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
+ };
+ 
++static const struct nla_policy
++background_radar_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL] = {
++	[MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE] = {.type = NLA_U8 },
++};
++
+ struct mt7996_amnt_data {
+ 	u8 idx;
+ 	u8 addr[ETH_ALEN];
+@@ -916,6 +921,27 @@ static int mt7996_vendor_wireless_ctrl(struct wiphy *wiphy,
+ 	return 0;
+ }
+ 
++static int mt7996_vendor_background_radar_mode_ctrl(struct wiphy *wiphy,
++						    struct wireless_dev *wdev,
++						    const void *data,
++						    int data_len)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL];
++	int err;
++	u8 background_radar_mode;
++
++	err = nla_parse(tb, MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX, data, data_len,
++			background_radar_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	background_radar_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE]);
++
++	return mt7996_mcu_rdd_background_disable_timer(dev, !!background_radar_mode);
++}
++
+ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 	{
+ 		.info = {
+@@ -1021,6 +1047,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 		.policy = rfeature_ctrl_policy,
+ 		.maxattr = MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX,
+ 	},
++	{
++		.info = {
++			.vendor_id = MTK_NL80211_VENDOR_ID,
++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++			WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.doit = mt7996_vendor_background_radar_mode_ctrl,
++		.policy = background_radar_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX,
++	},
+ };
+ 
+ void mt7996_vendor_register(struct mt7996_phy *phy)
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index 2ee1339a..7c812f91 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -14,6 +14,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
+ 	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
+ 	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
++	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
+ 	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
+ };
+ 
+@@ -127,6 +128,17 @@ enum mtk_vendor_attr_wireless_dump {
+ 		NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
+ };
+ 
++enum mtk_vendor_attr_background_radar_ctrl {
++	MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL,
++	MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
++};
++
+ enum bw_sig {
+ 	BW_SIGNALING_DISABLE,
+ 	BW_SIGNALING_STATIC,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0051-mtk-wifi-mt76-mt7996-add-adie-efuse-merge-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0051-mtk-wifi-mt76-mt7996-add-adie-efuse-merge-support.patch
deleted file mode 100644
index 4403909..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0051-mtk-wifi-mt76-mt7996-add-adie-efuse-merge-support.patch
+++ /dev/null
@@ -1,288 +0,0 @@
-From 571a367ee2ab7e447b7126bb05f778ec4ffdb3d0 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 24 Nov 2023 09:49:08 +0800
-Subject: [PATCH 051/116] mtk: wifi: mt76: mt7996: add adie efuse merge support
-
-Merge adie-dependent parameters in efuse into eeprom after FT.
-Note that Eagle BE14000 is not considered yet.
-Add efuse dump command.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/debugfs.c  |  41 +++++++++++++
- mt7996/eeprom.c   | 144 ++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/mcu.c      |   6 +-
- mt7996/testmode.c |   8 ++-
- 4 files changed, 195 insertions(+), 4 deletions(-)
-
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index 3074202..f3a520b 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -894,6 +894,46 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_muru_disable,
- 			 mt7996_fw_debug_muru_disable_get,
- 			 mt7996_fw_debug_muru_disable_set, "%lld\n");
- 
-+static ssize_t
-+mt7996_efuse_get(struct file *file, char __user *user_buf,
-+		 size_t count, loff_t *ppos)
-+{
-+	struct mt7996_dev *dev = file->private_data;
-+	struct mt76_dev *mdev = &dev->mt76;
-+	u8 *buff = mdev->otp.data;
-+	int i;
-+	ssize_t ret;
-+	u32 block_num;
-+
-+	mdev->otp.size = MT7996_EEPROM_SIZE;
-+	if (is_mt7996(&dev->mt76) && dev->chip_sku == MT7996_SKU_444)
-+		mdev->otp.size += 3 * MT_EE_CAL_UNIT;
-+
-+	if (!mdev->otp.data) {
-+		mdev->otp.data = devm_kzalloc(mdev->dev, mdev->otp.size, GFP_KERNEL);
-+		if (!mdev->otp.data)
-+			return -ENOMEM;
-+
-+		block_num = DIV_ROUND_UP(mdev->otp.size, MT7996_EEPROM_BLOCK_SIZE);
-+		for (i = 0; i < block_num; i++) {
-+			buff = mdev->otp.data + i * MT7996_EEPROM_BLOCK_SIZE;
-+			ret = mt7996_mcu_get_eeprom(dev, i * MT7996_EEPROM_BLOCK_SIZE, buff);
-+			if (ret && ret != -EINVAL)
-+				return ret;
-+		}
-+	}
-+
-+	ret = simple_read_from_buffer(user_buf, count, ppos, mdev->otp.data, mdev->otp.size);
-+
-+	return ret;
-+}
-+
-+static const struct file_operations mt7996_efuse_ops = {
-+	.read = mt7996_efuse_get,
-+	.open = simple_open,
-+	.llseek = default_llseek,
-+};
-+
- int mt7996_init_debugfs(struct mt7996_phy *phy)
- {
- 	struct mt7996_dev *dev = phy->dev;
-@@ -920,6 +960,7 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
- 	debugfs_create_devm_seqfile(dev->mt76.dev, "twt_stats", dir,
- 				    mt7996_twt_stats);
- 	debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval);
-+	debugfs_create_file("otp", 0400, dir, dev, &mt7996_efuse_ops);
- 
- 	if (phy->mt76->cap.has_5ghz) {
- 		debugfs_create_u32("dfs_hw_pattern", 0400, dir,
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 215d81e..3cdd585 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -496,6 +496,146 @@ static int mt7996_eeprom_load_precal(struct mt7996_dev *dev)
- 	return mt76_get_of_data_from_nvmem(mdev, dev->cal, "precal", size);
- }
- 
-+static int mt7996_apply_cal_free_data(struct mt7996_dev *dev)
-+{
-+#define MT_EE_CAL_FREE_MAX_SIZE		30
-+#define MT_EE_7977BN_OFFSET		(0x1200 - 0x500)
-+#define MT_EE_END_OFFSET		0xffff
-+	enum adie_type {
-+		ADIE_7975,
-+		ADIE_7976,
-+		ADIE_7977,
-+		ADIE_7978,
-+		ADIE_7979,
-+	};
-+	static const u16 adie_offs_list[][MT_EE_CAL_FREE_MAX_SIZE] = {
-+		[ADIE_7975] = {0x5cd, 0x5cf, 0x5d1, 0x5d3, 0x6c0, 0x6c1, 0x6c2, 0x6c3,
-+			       0x7a1, 0x7a6, 0x7a8, 0x7aa, -1},
-+		[ADIE_7976] = {0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x55, 0x57, 0x59,
-+			       0x70, 0x71, 0x790, 0x791, 0x794, 0x795, 0x7a6, 0x7a8, 0x7aa, -1},
-+		[ADIE_7977] = {0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x55, 0x57, 0x59,
-+			       0x69, 0x6a, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, -1},
-+		[ADIE_7978] = {0x91, 0x95, 0x100, 0x102, 0x104, 0x106, 0x107,
-+			       0x108, 0x109, 0x10a, 0x10b, 0x10c, 0x10e, 0x110, -1},
-+		[ADIE_7979] = {0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x55, 0x57, 0x59,
-+			       0x69, 0x6a, 0x7a, 0x7b, 0x7c, 0x7e, 0x80, -1},
-+	};
-+	static const u16 eep_offs_list[][MT_EE_CAL_FREE_MAX_SIZE] = {
-+		[ADIE_7975] = {0x451, 0x453, 0x455, 0x457, 0x44c, 0x44d, 0x44e, 0x44f,
-+			       0xba1, 0xba6, 0xba8, 0xbaa, -1},
-+		[ADIE_7976] = {0x44c, 0x44d, 0x44e, 0x44f, 0x450,
-+			       0x451, 0x453, 0x455, 0x457, 0x459,
-+			       0x470, 0x471, 0xb90, 0xb91, 0xb94, 0xb95,
-+			       0xba6, 0xba8, 0xbaa, -1},
-+		[ADIE_7977] = {0x124c, 0x124d, 0x124e, 0x124f, 0x1250,
-+			       0x1251, 0x1253, 0x1255, 0x1257, 0x1259,
-+			       0x1269, 0x126a, 0x127a, 0x127b, 0x127c, 0x127d, 0x127e, -1},
-+		[ADIE_7978] = {0xb91, 0xb95, 0x480, 0x482, 0x484, 0x486, 0x487, 0x488, 0x489,
-+			       0x48a, 0x48b, 0x48c, 0x48e, 0x490, -1},
-+		[ADIE_7979] = {0x124c, 0x124d, 0x124e, 0x124f, 0x1250, 0x1251,
-+			       0x1253, 0x1255, 0x1257, 0x1259, 0x1269, 0x126a,
-+			       0x127a, 0x127b, 0x127c, 0x127e, 0x1280, -1},
-+	};
-+	static const u16 adie_base_7996[] = {
-+		0x400, 0x1e00, 0x1200
-+	};
-+	static const u16 adie_base_7992[] = {
-+		0x400, 0x1200, 0x0
-+	};
-+	static const u16 *adie_offs[__MT_MAX_BAND];
-+	static const u16 *eep_offs[__MT_MAX_BAND];
-+	static const u16 *adie_base;
-+	u8 *eeprom = dev->mt76.eeprom.data;
-+	u8 buf[MT7996_EEPROM_BLOCK_SIZE];
-+	int adie_id, band, i, ret;
-+
-+	switch (mt76_chip(&dev->mt76)) {
-+	case 0x7990:
-+		adie_base = adie_base_7996;
-+		/* adie 0 */
-+		if (dev->fem_type == MT7996_FEM_INT)
-+			adie_id = ADIE_7975;
-+		else
-+			adie_id = ADIE_7976;
-+		adie_offs[0] = adie_offs_list[adie_id];
-+		eep_offs[0] = eep_offs_list[adie_id];
-+
-+		/* adie 1 */
-+		if (dev->chip_sku != MT7996_SKU_404) {
-+			adie_offs[1] = adie_offs_list[ADIE_7977];
-+			eep_offs[1] = eep_offs_list[ADIE_7977];
-+		}
-+
-+		/* adie 2 */
-+		adie_offs[2] = adie_offs_list[ADIE_7977];
-+		eep_offs[2] = eep_offs_list[ADIE_7977];
-+		break;
-+	case 0x7992:
-+		adie_base = adie_base_7992;
-+		/* adie 0 */
-+		if (dev->chip_sku == MT7992_SKU_44 &&
-+		    dev->fem_type != MT7996_FEM_EXT)
-+			adie_id = ADIE_7975;
-+		else if (dev->chip_sku == MT7992_SKU_24)
-+			adie_id = ADIE_7978;
-+		else
-+			adie_id = ADIE_7976;
-+		adie_offs[0] = adie_offs_list[adie_id];
-+		eep_offs[0] = eep_offs_list[adie_id];
-+
-+		/* adie 1 */
-+		if (dev->chip_sku == MT7992_SKU_44 &&
-+		    dev->fem_type != MT7996_FEM_INT)
-+			adie_id = ADIE_7977;
-+		else if (dev->chip_sku != MT7992_SKU_23)
-+			adie_id = ADIE_7979;
-+		else
-+			break;
-+		adie_offs[1] = adie_offs_list[adie_id];
-+		eep_offs[1] = eep_offs_list[adie_id];
-+		break;
-+	default:
-+		return -EINVAL;
-+	}
-+
-+	for (band = 0; band < __MT_MAX_BAND; band++) {
-+		u16 adie_offset, eep_offset;
-+		u32 block_num, prev_block_num = -1;
-+
-+		if (!adie_offs[band])
-+			continue;
-+
-+		for (i = 0; i < MT_EE_CAL_FREE_MAX_SIZE; i++) {
-+			adie_offset = adie_offs[band][i] + adie_base[band];
-+			eep_offset = eep_offs[band][i];
-+			block_num = adie_offset / MT7996_EEPROM_BLOCK_SIZE;
-+
-+			if (adie_offs[band][i] == MT_EE_END_OFFSET)
-+				break;
-+
-+			if (is_mt7996(&dev->mt76) && dev->chip_sku == MT7996_SKU_444 &&
-+			    band == MT_BAND1)
-+				eep_offset -= MT_EE_7977BN_OFFSET;
-+
-+			if (prev_block_num != block_num) {
-+				ret = mt7996_mcu_get_eeprom(dev, adie_offset, buf);
-+				if (ret) {
-+					if (ret != -EINVAL)
-+						return ret;
-+
-+					prev_block_num = -1;
-+					continue;
-+				}
-+			}
-+
-+			eeprom[eep_offset] = buf[adie_offset % MT7996_EEPROM_BLOCK_SIZE];
-+			prev_block_num = block_num;
-+		}
-+	}
-+
-+	return 0;
-+}
-+
- int mt7996_eeprom_init(struct mt7996_dev *dev)
- {
- 	int ret;
-@@ -512,6 +652,10 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
- 	if (ret)
- 		return ret;
- 
-+	ret = mt7996_apply_cal_free_data(dev);
-+	if (ret)
-+		return ret;
-+
- 	ret = mt7996_eeprom_parse_hw_cap(dev, &dev->phy);
- 	if (ret < 0)
- 		return ret;
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index b5c6638..1a904e4 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -3606,7 +3606,7 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf)
- 	};
- 	struct sk_buff *skb;
- 	bool valid;
--	int ret;
-+	int ret = 0;
- 	u8 *buf = read_buf;
- 
- 	ret = mt76_mcu_send_and_get_msg(&dev->mt76,
-@@ -3624,11 +3624,13 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf)
- 
- 		skb_pull(skb, 48);
- 		memcpy(buf, skb->data, MT7996_EEPROM_BLOCK_SIZE);
-+	} else {
-+		ret = -EINVAL;
- 	}
- 
- 	dev_kfree_skb(skb);
- 
--	return 0;
-+	return ret;
- }
- 
- int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num)
-diff --git a/mt7996/testmode.c b/mt7996/testmode.c
-index 9fa4edc..784a8be 100644
---- a/mt7996/testmode.c
-+++ b/mt7996/testmode.c
-@@ -2116,8 +2116,12 @@ mt7996_tm_write_back_to_efuse(struct mt7996_dev *dev)
- 		memcpy(req.data, eeprom + i, MT76_TM_EEPROM_BLOCK_SIZE);
- 
- 		ret = mt7996_mcu_get_eeprom(dev, i, read_buf);
--		if (ret < 0)
--			return ret;
-+		if (ret) {
-+			if (ret != -EINVAL)
-+				return ret;
-+
-+			memset(read_buf, 0, MT76_TM_EEPROM_BLOCK_SIZE);
-+		}
- 
- 		if (!memcmp(req.data, read_buf, MT76_TM_EEPROM_BLOCK_SIZE))
- 			continue;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0052-mtk-mt76-mt7996-add-channel-68-96.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0052-mtk-mt76-mt7996-add-channel-68-96.patch
new file mode 100644
index 0000000..df5e1bf
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0052-mtk-mt76-mt7996-add-channel-68-96.patch
@@ -0,0 +1,237 @@
+From 6c7bf3990d1a6ff13007178a2624e55fbaf6d823 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 11 Sep 2023 14:43:07 +0800
+Subject: [PATCH 052/199] mtk: mt76: mt7996: add channel 68 & 96
+
+---
+ mac80211.c        |  9 +++++++++
+ mt7996/eeprom.c   | 49 +++++++++++++++++++++++++++++++++++++++++++++--
+ mt7996/eeprom.h   |  2 ++
+ mt7996/mcu.c      | 10 +++++++++-
+ mt7996/testmode.c | 15 ++++++++++++---
+ mt7996/testmode.h |  6 +++---
+ 6 files changed, 82 insertions(+), 9 deletions(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index 5b02fa35..6a5201f6 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -34,6 +34,15 @@ static const struct ieee80211_channel mt76_channels_5ghz[] = {
+ 	CHAN5G(60, 5300),
+ 	CHAN5G(64, 5320),
+ 
++	CHAN5G(68, 5340),
++	CHAN5G(72, 5360),
++	CHAN5G(76, 5380),
++	CHAN5G(80, 5400),
++	CHAN5G(84, 5420),
++	CHAN5G(88, 5440),
++	CHAN5G(92, 5460),
++	CHAN5G(96, 5480),
++
+ 	CHAN5G(100, 5500),
+ 	CHAN5G(104, 5520),
+ 	CHAN5G(108, 5540),
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index f4641321..8a0f6d39 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -18,6 +18,17 @@ const struct ieee80211_channel dpd_2g_ch_list_bw20[] = {
+ 	CHAN2G(11, 2462)
+ };
+ 
++const struct ieee80211_channel dpd_5g_skip_ch_list[] = {
++	CHAN5G(68, 5340),
++	CHAN5G(72, 5360),
++	CHAN5G(76, 5380),
++	CHAN5G(80, 5400),
++	CHAN5G(84, 5420),
++	CHAN5G(88, 5440),
++	CHAN5G(92, 5460),
++	CHAN5G(96, 5480)
++};
++
+ const struct ieee80211_channel dpd_5g_ch_list_bw160[] = {
+ 	CHAN5G(50, 5250),
+ 	CHAN5G(114, 5570),
+@@ -44,6 +55,7 @@ const struct ieee80211_channel dpd_6g_ch_list_bw320[] = {
+ };
+ 
+ const u32 dpd_2g_bw20_ch_num = ARRAY_SIZE(dpd_2g_ch_list_bw20);
++const u32 dpd_5g_skip_ch_num = ARRAY_SIZE(dpd_5g_skip_ch_list);
+ const u32 dpd_5g_bw160_ch_num = ARRAY_SIZE(dpd_5g_ch_list_bw160);
+ const u32 dpd_6g_bw160_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw160);
+ const u32 dpd_6g_bw320_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw320);
+@@ -168,8 +180,8 @@ mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band)
+ 	if (band == NL80211_BAND_2GHZ)
+ 		dpd_size = dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
+ 	else if (band == NL80211_BAND_5GHZ)
+-		dpd_size = mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
+-			   dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
++		dpd_size = (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
++			   DPD_PER_CH_BW20_SIZE + dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
+ 	else
+ 		dpd_size = mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
+ 			   (dpd_6g_bw160_ch_num + dpd_6g_bw320_ch_num) * DPD_PER_CH_GT_BW20_SIZE;
+@@ -431,6 +443,39 @@ out:
+ 	return ret;
+ }
+ 
++static void mt7996_eeprom_init_precal(struct mt7996_dev *dev)
++{
++#define MT76_CHANNELS_5GHZ_SIZE		36	/* ARRAY_SIZE(mt76_channels_5ghz) */
++#define MT76_CHANNELS_6GHZ_SIZE		59	/* ARRAY_SIZE(mt76_channels_6ghz) */
++
++	dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_2G] = ARRAY_SIZE(dpd_2g_ch_list_bw20);
++	dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_5G_SKIP] = ARRAY_SIZE(dpd_5g_skip_ch_list);
++	dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_5G] = MT76_CHANNELS_5GHZ_SIZE -
++						   DPD_CH_NUM(BW20_5G_SKIP);
++	dev->prek.dpd_ch_num[DPD_CH_NUM_BW160_5G] = ARRAY_SIZE(dpd_5g_ch_list_bw160);
++	dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_6G] = MT76_CHANNELS_6GHZ_SIZE;
++	dev->prek.dpd_ch_num[DPD_CH_NUM_BW160_6G] = ARRAY_SIZE(dpd_6g_ch_list_bw160);
++
++	switch (mt76_chip(&dev->mt76)) {
++	case 0x7990:
++		dev->prek.rev = mt7996_prek_rev;
++		/* 5g & 6g bw 80 dpd channel list is not used */
++		dev->prek.dpd_ch_num[DPD_CH_NUM_BW320_6G] = ARRAY_SIZE(dpd_6g_ch_list_bw320);
++		break;
++	case 0x7992:
++		dev->prek.rev  = mt7992_prek_rev;
++		dev->prek.dpd_ch_num[DPD_CH_NUM_BW80_5G] = ARRAY_SIZE(dpd_5g_ch_list_bw80);
++		/* 6g is not used in current sku */
++		dev->prek.dpd_ch_num[DPD_CH_NUM_BW20_6G] = 0;
++		dev->prek.dpd_ch_num[DPD_CH_NUM_BW80_6G] = 0;
++		dev->prek.dpd_ch_num[DPD_CH_NUM_BW160_6G] = 0;
++		break;
++	default:
++		dev->prek.rev  = mt7996_prek_rev;
++		break;
++	}
++}
++
+ static int mt7996_eeprom_load_precal(struct mt7996_dev *dev)
+ {
+ 	struct mt76_dev *mdev = &dev->mt76;
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index 03a4fd07..9a15b446 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -67,6 +67,8 @@ enum mt7996_eeprom_field {
+ 
+ extern const struct ieee80211_channel dpd_2g_ch_list_bw20[];
+ extern const u32 dpd_2g_bw20_ch_num;
++extern const struct ieee80211_channel dpd_5g_skip_ch_list[];
++extern const u32 dpd_5g_skip_ch_num;
+ extern const struct ieee80211_channel dpd_5g_ch_list_bw160[];
+ extern const u32 dpd_5g_bw160_ch_num;
+ extern const struct ieee80211_channel dpd_6g_ch_list_bw160[];
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 0db30af9..cf94d39c 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3769,7 +3769,8 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
+ 		chan_list_size = mphy->sband_5g.sband.n_channels;
+ 		base_offset += dpd_size_2g;
+ 		if (bw == NL80211_CHAN_WIDTH_160) {
+-			base_offset += mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
++			base_offset += (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
++				       DPD_PER_CH_BW20_SIZE;
+ 			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
+ 			cal_id = RF_DPD_FLAT_5G_MEM_CAL;
+ 			chan_list = dpd_5g_ch_list_bw160;
+@@ -3778,6 +3779,9 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
+ 			/* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
+ 			channel -= 2;
+ 		}
++		if (channel >= dpd_5g_skip_ch_list[0].hw_value &&
++		    channel <= dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
++			return 0;
+ 		break;
+ 	case NL80211_BAND_6GHZ:
+ 		dpd_mask = MT_EE_WIFI_CAL_DPD_6G;
+@@ -3817,6 +3821,10 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
+ 	if (idx == chan_list_size)
+ 		return -EINVAL;
+ 
++	if (band == NL80211_BAND_5GHZ && bw != NL80211_CHAN_WIDTH_160 &&
++	    channel > dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
++		idx -= dpd_5g_skip_ch_num;
++
+ 	cal += MT_EE_CAL_GROUP_SIZE + base_offset + idx * per_chan_size;
+ 
+ 	for (i = 0; i < per_chan_size / MT_EE_CAL_UNIT; i++) {
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index 5cec1eef..95d3bde0 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -531,6 +531,11 @@ mt7996_tm_dpd_prek_send_req(struct mt7996_phy *phy, struct mt7996_tm_req *req,
+ 	memcpy(&chandef_backup, chandef, sizeof(struct cfg80211_chan_def));
+ 
+ 	for (i = 0; i < channel_size; i++) {
++		if (chan_list[i].band == NL80211_BAND_5GHZ &&
++		    chan_list[i].hw_value >= dpd_5g_skip_ch_list[0].hw_value &&
++		    chan_list[i].hw_value <= dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
++			continue;
++
+ 		memcpy(chandef->chan, &chan_list[i], sizeof(struct ieee80211_channel));
+ 		chandef->width = width;
+ 
+@@ -612,7 +617,8 @@ mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
+ 						  NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_5G_CAL);
+ 		if (ret)
+ 			return ret;
+-		wait_on_prek_offset += mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
++		wait_on_prek_offset += (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
++				       DPD_PER_CH_BW20_SIZE;
+ 		wait_event_timeout(mdev->mcu.wait,
+ 				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
+ 
+@@ -868,6 +874,7 @@ mt7996_tm_get_center_chan(struct mt7996_phy *phy, struct cfg80211_chan_def *chan
+ 	const struct ieee80211_channel *chan = mphy->sband_5g.sband.channels;
+ 	u32 bitmap, i, offset, width_mhz, size = mphy->sband_5g.sband.n_channels;
+ 	u16 first_control = 0, control_chan = chandef->chan->hw_value;
++	bool not_first;
+ 
+ 	bitmap = mt7996_tm_bw_mapping(chandef->width, BW_MAP_NL_TO_CONTROL_BITMAP_5G);
+ 	if (!bitmap)
+@@ -877,7 +884,9 @@ mt7996_tm_get_center_chan(struct mt7996_phy *phy, struct cfg80211_chan_def *chan
+ 	offset = width_mhz / 10 - 2;
+ 
+ 	for (i = 0; i < size; i++) {
+-		if (!((1 << i) & bitmap))
++		not_first = (chandef->width != NL80211_CHAN_WIDTH_160) ?
++			    (i % bitmap) : (i >= 32) || !((1 << i) & bitmap);
++		if (not_first)
+ 			continue;
+ 
+ 		if (control_chan >= chan[i].hw_value)
+@@ -886,7 +895,7 @@ mt7996_tm_get_center_chan(struct mt7996_phy *phy, struct cfg80211_chan_def *chan
+ 			break;
+ 	}
+ 
+-	if (i == size || first_control == 0)
++	if (first_control == 0)
+ 		return control_chan;
+ 
+ 	return first_control + offset;
+diff --git a/mt7996/testmode.h b/mt7996/testmode.h
+index f97ccb26..ba1767ae 100644
+--- a/mt7996/testmode.h
++++ b/mt7996/testmode.h
+@@ -38,9 +38,9 @@ enum {
+ 	BF_CDBW_8080MHZ,
+ };
+ 
+-#define FIRST_CONTROL_CHAN_BITMAP_BW40		0x5555555
+-#define FIRST_CONTROL_CHAN_BITMAP_BW80		0x111111
+-#define FIRST_CONTROL_CHAN_BITMAP_BW160		0x100101
++#define FIRST_CONTROL_CHAN_BITMAP_BW40		2
++#define FIRST_CONTROL_CHAN_BITMAP_BW80		4
++#define FIRST_CONTROL_CHAN_BITMAP_BW160		0x10010101
+ 
+ enum bw_mapping_method {
+ 	BW_MAP_NL_TO_FW,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0052-mtk-wifi-mt7996-add-Eagle-2adie-TBTC-BE14000-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0052-mtk-wifi-mt7996-add-Eagle-2adie-TBTC-BE14000-support.patch
deleted file mode 100644
index 1ab4d7d..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0052-mtk-wifi-mt7996-add-Eagle-2adie-TBTC-BE14000-support.patch
+++ /dev/null
@@ -1,167 +0,0 @@
-From eafc607f276a54844224670c0fff6b92ebe5af09 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Tue, 5 Dec 2023 16:48:33 +0800
-Subject: [PATCH 052/116] mtk: wifi: mt7996: add Eagle 2adie TBTC (BE14000)
- support
-
-Add fwdl/default eeprom load support for Eagle 2 adie TBTC
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Add Eagle 2adie TBTC efuse merge
-Add Eagle 2adie TBTC group prek size
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/eeprom.c |  8 ++++++--
- mt7996/eeprom.h | 12 ++++++++++++
- mt7996/init.c   |  6 ++++++
- mt7996/mcu.c    |  5 +++++
- mt7996/mt7996.h |  8 ++++++++
- mt7996/regs.h   |  1 +
- 6 files changed, 38 insertions(+), 2 deletions(-)
-
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 3cdd585..5eb8629 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -158,6 +158,8 @@ const char *mt7996_eeprom_name(struct mt7996_dev *dev)
- 	case 0x7990:
- 		if (dev->chip_sku == MT7996_SKU_404)
- 			return MT7996_EEPROM_DEFAULT_404;
-+		else if (dev->chip_sku == MT7996_SKU_233)
-+			return MT7996_EEPROM_DEFAULT_233;
- 
- 		if (dev->fem_type == MT7996_FEM_INT)
- 			return MT7996_EEPROM_DEFAULT_INT;
-@@ -450,6 +452,8 @@ static void mt7996_eeprom_init_precal(struct mt7996_dev *dev)
- 	switch (mt76_chip(&dev->mt76)) {
- 	case 0x7990:
- 		dev->prek.rev = mt7996_prek_rev;
-+		if (dev->chip_sku == MT7996_SKU_233)
-+			dev->prek.rev = mt7996_prek_rev_233;
- 		/* 5g & 6g bw 80 dpd channel list is not used */
- 		dev->prek.dpd_ch_num[DPD_CH_NUM_BW320_6G] = ARRAY_SIZE(dpd_6g_ch_list_bw320);
- 		break;
-@@ -553,7 +557,7 @@ static int mt7996_apply_cal_free_data(struct mt7996_dev *dev)
- 	case 0x7990:
- 		adie_base = adie_base_7996;
- 		/* adie 0 */
--		if (dev->fem_type == MT7996_FEM_INT)
-+		if (dev->fem_type == MT7996_FEM_INT && dev->chip_sku != MT7996_SKU_233)
- 			adie_id = ADIE_7975;
- 		else
- 			adie_id = ADIE_7976;
-@@ -561,7 +565,7 @@ static int mt7996_apply_cal_free_data(struct mt7996_dev *dev)
- 		eep_offs[0] = eep_offs_list[adie_id];
- 
- 		/* adie 1 */
--		if (dev->chip_sku != MT7996_SKU_404) {
-+		if (dev->chip_sku == MT7996_SKU_444) {
- 			adie_offs[1] = adie_offs_list[ADIE_7977];
- 			eep_offs[1] = eep_offs_list[ADIE_7977];
- 		}
-diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index fa9c31e..43c9783 100644
---- a/mt7996/eeprom.h
-+++ b/mt7996/eeprom.h
-@@ -70,6 +70,18 @@ static const u32 mt7996_prek_rev[] = {
- 	[DPD_OTFG0_SIZE] =			2 * MT_EE_CAL_UNIT,
- };
- 
-+static const u32 mt7996_prek_rev_233[] = {
-+	[GROUP_SIZE_2G] =			4 * MT_EE_CAL_UNIT,
-+	[GROUP_SIZE_5G] =			44 * MT_EE_CAL_UNIT,
-+	[GROUP_SIZE_6G] =			100 * MT_EE_CAL_UNIT,
-+	[ADCDCOC_SIZE_2G] =			4 * 4,
-+	[ADCDCOC_SIZE_5G] =			4 * 4,
-+	[ADCDCOC_SIZE_6G] =			4 * 5,
-+	[DPD_LEGACY_SIZE] =			4 * MT_EE_CAL_UNIT,
-+	[DPD_MEM_SIZE] =			13 * MT_EE_CAL_UNIT,
-+	[DPD_OTFG0_SIZE] =			2 * MT_EE_CAL_UNIT,
-+};
-+
- /* kite 2/5g config */
- static const u32 mt7992_prek_rev[] = {
- 	[GROUP_SIZE_2G] =			4 * MT_EE_CAL_UNIT,
-diff --git a/mt7996/init.c b/mt7996/init.c
-index d4b0a72..cb00322 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -916,6 +916,12 @@ int mt7996_get_chip_sku(struct mt7996_dev *dev)
- 
- 	switch (mt76_chip(&dev->mt76)) {
- 	case 0x7990:
-+		if (FIELD_GET(MT_PAD_GPIO_2ADIE_TBTC, val)) {
-+			dev->chip_sku = MT7996_SKU_233;
-+			dev->fem_type = MT7996_FEM_INT;
-+			return 0;
-+		}
-+
- 		adie_comb = FIELD_GET(MT_PAD_GPIO_ADIE_COMB, val);
- 		if (adie_comb <= 1)
- 			dev->chip_sku = MT7996_SKU_444;
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 1a904e4..686cdda 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -23,6 +23,11 @@
- 			_fw = MT7992_##name;			\
- 		break;						\
- 	case 0x7990:						\
-+		if ((_dev)->chip_sku == MT7996_SKU_233)		\
-+			_fw = MT7996_##name##_233;		\
-+		else						\
-+			_fw = MT7996_##name;			\
-+		break;						\
- 	default:						\
- 		_fw = MT7996_##name;				\
- 		break;						\
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 1bba545..72865c8 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -35,6 +35,12 @@
- #define MT7996_FIRMWARE_WM_TM		"mediatek/mt7996/mt7996_wm_tm.bin"
- #define MT7996_ROM_PATCH		"mediatek/mt7996/mt7996_rom_patch.bin"
- 
-+#define MT7996_FIRMWARE_WA_233		"mediatek/mt7996/mt7996_wa_233.bin"
-+#define MT7996_FIRMWARE_WM_233		"mediatek/mt7996/mt7996_wm_233.bin"
-+#define MT7996_FIRMWARE_DSP_233		MT7996_FIRMWARE_DSP
-+#define MT7996_FIRMWARE_WM_TM_233	"mediatek/mt7996/mt7996_wm_tm_233.bin"
-+#define MT7996_ROM_PATCH_233		"mediatek/mt7996/mt7996_rom_patch_233.bin"
-+
- #define MT7992_FIRMWARE_WA		"mediatek/mt7996/mt7992_wa.bin"
- #define MT7992_FIRMWARE_WM		"mediatek/mt7996/mt7992_wm.bin"
- #define MT7992_FIRMWARE_DSP		"mediatek/mt7996/mt7992_dsp.bin"
-@@ -55,6 +61,7 @@
- 
- #define MT7996_EEPROM_DEFAULT		"mediatek/mt7996/mt7996_eeprom.bin"
- #define MT7996_EEPROM_DEFAULT_INT	"mediatek/mt7996/mt7996_eeprom_2i5i6i.bin"
-+#define MT7996_EEPROM_DEFAULT_233	"mediatek/mt7996/mt7996_eeprom_233.bin"
- #define MT7996_EEPROM_DEFAULT_404	"mediatek/mt7996/mt7996_eeprom_dual_404.bin"
- #define MT7992_EEPROM_DEFAULT		"mediatek/mt7996/mt7992_eeprom_2i5i.bin"
- #define MT7992_EEPROM_DEFAULT_EXT	"mediatek/mt7996/mt7992_eeprom_2e5e.bin"
-@@ -122,6 +129,7 @@ enum mt7996_fem_type {
- enum mt7996_sku_type {
- 	MT7996_SKU_404,
- 	MT7996_SKU_444,
-+	MT7996_SKU_233,
- };
- 
- enum mt7992_sku_type {
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index 263737c..91159c6 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -666,6 +666,7 @@ enum offs_rev {
- 
- #define MT_PAD_GPIO				0x700056f0
- #define MT_PAD_GPIO_ADIE_COMB			GENMASK(16, 15)
-+#define MT_PAD_GPIO_2ADIE_TBTC			BIT(19)
- #define MT_PAD_GPIO_ADIE_COMB_7992		GENMASK(17, 16)
- #define MT_PAD_GPIO_ADIE_NUM_7992		BIT(15)
- 
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0053-mtk-mt76-mt7996-add-kite-testmode-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0053-mtk-mt76-mt7996-add-kite-testmode-support.patch
new file mode 100644
index 0000000..febce5f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0053-mtk-mt76-mt7996-add-kite-testmode-support.patch
@@ -0,0 +1,612 @@
+From 4c17654079c3618e0e0e480d54f305c4460b3af4 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 12 Oct 2023 16:17:33 +0800
+Subject: [PATCH 053/199] mtk: mt76: mt7996: add kite testmode support
+
+Add Kite testmode support
+1. avoid entering connac 2 testmode flow in kite
+2. refactor prek implementation for handling chip difference
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/eeprom.c   | 63 +++++++++++++-----------------
+ mt7996/eeprom.h   | 81 +++++++++++++++++++++++++++------------
+ mt7996/mcu.c      | 48 ++++++++++++++---------
+ mt7996/mt7996.h   | 18 ++++++++-
+ mt7996/testmode.c | 97 ++++++++++++++++++++++++++++-------------------
+ testmode.c        | 17 ++++++---
+ 6 files changed, 202 insertions(+), 122 deletions(-)
+
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 8a0f6d39..0d50d313 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -29,12 +29,39 @@ const struct ieee80211_channel dpd_5g_skip_ch_list[] = {
+ 	CHAN5G(96, 5480)
+ };
+ 
++const struct ieee80211_channel dpd_5g_ch_list_bw80[] = {
++	CHAN5G(42, 5210),
++	CHAN5G(58, 5290),
++	CHAN5G(106, 5530),
++	CHAN5G(122, 5610),
++	CHAN5G(138, 5690),
++	CHAN5G(155, 5775),
++	CHAN5G(171, 5855)
++};
++
+ const struct ieee80211_channel dpd_5g_ch_list_bw160[] = {
+ 	CHAN5G(50, 5250),
+ 	CHAN5G(114, 5570),
+ 	CHAN5G(163, 5815)
+ };
+ 
++const struct ieee80211_channel dpd_6g_ch_list_bw80[] = {
++	CHAN6G(7, 5985),
++	CHAN6G(23, 6065),
++	CHAN6G(39, 6145),
++	CHAN6G(55, 6225),
++	CHAN6G(71, 6305),
++	CHAN6G(87, 6385),
++	CHAN6G(103, 6465),
++	CHAN6G(119, 6545),
++	CHAN6G(135, 6625),
++	CHAN6G(151, 6705),
++	CHAN6G(167, 6785),
++	CHAN6G(183, 6865),
++	CHAN6G(199, 6945),
++	CHAN6G(215, 7025)
++};
++
+ const struct ieee80211_channel dpd_6g_ch_list_bw160[] = {
+ 	CHAN6G(15, 6025),
+ 	CHAN6G(47, 6185),
+@@ -54,12 +81,6 @@ const struct ieee80211_channel dpd_6g_ch_list_bw320[] = {
+ 	CHAN6G(191, 6905)
+ };
+ 
+-const u32 dpd_2g_bw20_ch_num = ARRAY_SIZE(dpd_2g_ch_list_bw20);
+-const u32 dpd_5g_skip_ch_num = ARRAY_SIZE(dpd_5g_skip_ch_list);
+-const u32 dpd_5g_bw160_ch_num = ARRAY_SIZE(dpd_5g_ch_list_bw160);
+-const u32 dpd_6g_bw160_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw160);
+-const u32 dpd_6g_bw320_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw320);
+-
+ static int mt7996_check_eeprom(struct mt7996_dev *dev)
+ {
+ 	u8 *eeprom = dev->mt76.eeprom.data;
+@@ -159,36 +180,6 @@ const char *mt7996_eeprom_name(struct mt7996_dev *dev)
+ 	}
+ }
+ 
+-int
+-mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band)
+-{
+-	/* handle different sku */
+-	static const u8 band_to_idx[] = {
+-		[NL80211_BAND_2GHZ] = MT_BAND0,
+-		[NL80211_BAND_5GHZ] = MT_BAND1,
+-		[NL80211_BAND_6GHZ] = MT_BAND2,
+-	};
+-	struct mt7996_phy *phy = __mt7996_phy(dev, band_to_idx[band]);
+-	struct mt76_phy *mphy;
+-	int dpd_size;
+-
+-	if (!phy)
+-		return 0;
+-
+-	mphy = phy->mt76;
+-
+-	if (band == NL80211_BAND_2GHZ)
+-		dpd_size = dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
+-	else if (band == NL80211_BAND_5GHZ)
+-		dpd_size = (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
+-			   DPD_PER_CH_BW20_SIZE + dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
+-	else
+-		dpd_size = mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
+-			   (dpd_6g_bw160_ch_num + dpd_6g_bw320_ch_num) * DPD_PER_CH_GT_BW20_SIZE;
+-
+-	return dpd_size;
+-}
+-
+ static int
+ mt7996_eeprom_load_bin(struct mt7996_dev *dev)
+ {
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index 9a15b446..fa9c31e7 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -45,36 +45,69 @@ enum mt7996_eeprom_field {
+ #define MT_EE_WIFI_CAL_DPD			GENMASK(5, 3)
+ 
+ #define MT_EE_CAL_UNIT				1024
+-#define MT_EE_CAL_GROUP_SIZE_2G			(4 * MT_EE_CAL_UNIT)
+-#define MT_EE_CAL_GROUP_SIZE_5G			(45 * MT_EE_CAL_UNIT)
+-#define MT_EE_CAL_GROUP_SIZE_6G			(125 * MT_EE_CAL_UNIT)
+-#define MT_EE_CAL_ADCDCOC_SIZE_2G		(4 * 4)
+-#define MT_EE_CAL_ADCDCOC_SIZE_5G		(4 * 4)
+-#define MT_EE_CAL_ADCDCOC_SIZE_6G		(4 * 5)
+-#define MT_EE_CAL_GROUP_SIZE			(MT_EE_CAL_GROUP_SIZE_2G + \
+-						 MT_EE_CAL_GROUP_SIZE_5G + \
+-						 MT_EE_CAL_GROUP_SIZE_6G + \
+-						 MT_EE_CAL_ADCDCOC_SIZE_2G + \
+-						 MT_EE_CAL_ADCDCOC_SIZE_5G + \
+-						 MT_EE_CAL_ADCDCOC_SIZE_6G)
+-
+-#define DPD_PER_CH_LEGACY_SIZE			(4 * MT_EE_CAL_UNIT)
+-#define DPD_PER_CH_MEM_SIZE			(13 * MT_EE_CAL_UNIT)
+-#define DPD_PER_CH_OTFG0_SIZE			(2 * MT_EE_CAL_UNIT)
+-#define DPD_PER_CH_BW20_SIZE			(DPD_PER_CH_LEGACY_SIZE + DPD_PER_CH_OTFG0_SIZE)
+-#define DPD_PER_CH_GT_BW20_SIZE			(DPD_PER_CH_MEM_SIZE + DPD_PER_CH_OTFG0_SIZE)
+-#define MT_EE_CAL_DPD_SIZE			(780 * MT_EE_CAL_UNIT)
++
++enum mt7996_prek_rev {
++	GROUP_SIZE_2G,
++	GROUP_SIZE_5G,
++	GROUP_SIZE_6G,
++	ADCDCOC_SIZE_2G,
++	ADCDCOC_SIZE_5G,
++	ADCDCOC_SIZE_6G,
++	DPD_LEGACY_SIZE,
++	DPD_MEM_SIZE,
++	DPD_OTFG0_SIZE,
++};
++
++static const u32 mt7996_prek_rev[] = {
++	[GROUP_SIZE_2G] =			4 * MT_EE_CAL_UNIT,
++	[GROUP_SIZE_5G] =			45 * MT_EE_CAL_UNIT,
++	[GROUP_SIZE_6G] =			125 * MT_EE_CAL_UNIT,
++	[ADCDCOC_SIZE_2G] =			4 * 4,
++	[ADCDCOC_SIZE_5G] =			4 * 4,
++	[ADCDCOC_SIZE_6G] =			4 * 5,
++	[DPD_LEGACY_SIZE] =			4 * MT_EE_CAL_UNIT,
++	[DPD_MEM_SIZE] =			13 * MT_EE_CAL_UNIT,
++	[DPD_OTFG0_SIZE] =			2 * MT_EE_CAL_UNIT,
++};
++
++/* kite 2/5g config */
++static const u32 mt7992_prek_rev[] = {
++	[GROUP_SIZE_2G] =			4 * MT_EE_CAL_UNIT,
++	[GROUP_SIZE_5G] =			110 * MT_EE_CAL_UNIT,
++	[GROUP_SIZE_6G] =			0,
++	[ADCDCOC_SIZE_2G] =			4 * 4,
++	[ADCDCOC_SIZE_5G] =			4 * 5,
++	[ADCDCOC_SIZE_6G] =			0,
++	[DPD_LEGACY_SIZE] =			5 * MT_EE_CAL_UNIT,
++	[DPD_MEM_SIZE] =			16 * MT_EE_CAL_UNIT,
++	[DPD_OTFG0_SIZE] =			2 * MT_EE_CAL_UNIT,
++};
+ 
+ extern const struct ieee80211_channel dpd_2g_ch_list_bw20[];
+-extern const u32 dpd_2g_bw20_ch_num;
+ extern const struct ieee80211_channel dpd_5g_skip_ch_list[];
+-extern const u32 dpd_5g_skip_ch_num;
++extern const struct ieee80211_channel dpd_5g_ch_list_bw80[];
+ extern const struct ieee80211_channel dpd_5g_ch_list_bw160[];
+-extern const u32 dpd_5g_bw160_ch_num;
++extern const struct ieee80211_channel dpd_6g_ch_list_bw80[];
+ extern const struct ieee80211_channel dpd_6g_ch_list_bw160[];
+-extern const u32 dpd_6g_bw160_ch_num;
+ extern const struct ieee80211_channel dpd_6g_ch_list_bw320[];
+-extern const u32 dpd_6g_bw320_ch_num;
++
++#define PREK(id)				(dev->prek.rev[(id)])
++#define DPD_CH_NUM(_type)			(dev->prek.dpd_ch_num[DPD_CH_NUM_##_type])
++#define MT_EE_CAL_GROUP_SIZE			(PREK(GROUP_SIZE_2G) + PREK(GROUP_SIZE_5G) + \
++						 PREK(GROUP_SIZE_6G) + PREK(ADCDCOC_SIZE_2G) + \
++						 PREK(ADCDCOC_SIZE_5G) + PREK(ADCDCOC_SIZE_6G))
++#define DPD_PER_CH_BW20_SIZE			(PREK(DPD_LEGACY_SIZE) + PREK(DPD_OTFG0_SIZE))
++#define DPD_PER_CH_GT_BW20_SIZE			(PREK(DPD_MEM_SIZE) + PREK(DPD_OTFG0_SIZE))
++#define MT_EE_CAL_DPD_SIZE_2G			(DPD_CH_NUM(BW20_2G) * DPD_PER_CH_BW20_SIZE)
++#define MT_EE_CAL_DPD_SIZE_5G			(DPD_CH_NUM(BW20_5G) * DPD_PER_CH_BW20_SIZE + \
++						 DPD_CH_NUM(BW80_5G) * DPD_PER_CH_GT_BW20_SIZE + \
++						 DPD_CH_NUM(BW160_5G) * DPD_PER_CH_GT_BW20_SIZE)
++#define MT_EE_CAL_DPD_SIZE_6G			(DPD_CH_NUM(BW20_6G) * DPD_PER_CH_BW20_SIZE + \
++						 DPD_CH_NUM(BW80_6G) * DPD_PER_CH_GT_BW20_SIZE + \
++						 DPD_CH_NUM(BW160_6G) * DPD_PER_CH_GT_BW20_SIZE + \
++						 DPD_CH_NUM(BW320_6G) * DPD_PER_CH_GT_BW20_SIZE)
++#define MT_EE_CAL_DPD_SIZE			(MT_EE_CAL_DPD_SIZE_2G + MT_EE_CAL_DPD_SIZE_5G + \
++						 MT_EE_CAL_DPD_SIZE_6G)
+ 
+ #define RF_DPD_FLAT_CAL				BIT(28)
+ #define RF_PRE_CAL				BIT(29)
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index cf94d39c..3a7f9df8 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3738,13 +3738,11 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
+ 	enum nl80211_chan_width bw = chandef->width;
+ 	const struct ieee80211_channel *chan_list;
+ 	u32 cal_id, chan_list_size, base_offset = 0, offs = MT_EE_DO_PRE_CAL;
+-	u32 dpd_size_2g, dpd_size_5g, per_chan_size = DPD_PER_CH_BW20_SIZE;
++	u32 per_chan_size = DPD_PER_CH_BW20_SIZE;
+ 	u16 channel = ieee80211_frequency_to_channel(chandef->center_freq1);
+ 	u8 dpd_mask, *cal = dev->cal, *eeprom = dev->mt76.eeprom.data;
+ 	int idx, i, ret;
+-
+-	dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
+-	dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
++	bool has_skip_ch = (band == NL80211_BAND_5GHZ);
+ 
+ 	switch (band) {
+ 	case NL80211_BAND_2GHZ:
+@@ -3760,27 +3758,35 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
+ 			return 0;
+ 		cal_id = RF_DPD_FLAT_CAL;
+ 		chan_list = dpd_2g_ch_list_bw20;
+-		chan_list_size = dpd_2g_bw20_ch_num;
++		chan_list_size = DPD_CH_NUM(BW20_2G);
+ 		break;
+ 	case NL80211_BAND_5GHZ:
+ 		dpd_mask = MT_EE_WIFI_CAL_DPD_5G;
+ 		cal_id = RF_DPD_FLAT_5G_CAL;
+ 		chan_list = mphy->sband_5g.sband.channels;
+ 		chan_list_size = mphy->sband_5g.sband.n_channels;
+-		base_offset += dpd_size_2g;
++		base_offset += MT_EE_CAL_DPD_SIZE_2G;
+ 		if (bw == NL80211_CHAN_WIDTH_160) {
+-			base_offset += (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
+-				       DPD_PER_CH_BW20_SIZE;
++			base_offset += DPD_CH_NUM(BW20_5G) * DPD_PER_CH_BW20_SIZE +
++				       DPD_CH_NUM(BW80_5G) * DPD_PER_CH_GT_BW20_SIZE;
+ 			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
+ 			cal_id = RF_DPD_FLAT_5G_MEM_CAL;
+ 			chan_list = dpd_5g_ch_list_bw160;
+-			chan_list_size = dpd_5g_bw160_ch_num;
++			chan_list_size = DPD_CH_NUM(BW160_5G);
++			has_skip_ch = false;
++		} else if (is_mt7992(&dev->mt76) && bw == NL80211_CHAN_WIDTH_80) {
++			base_offset += DPD_CH_NUM(BW20_5G) * DPD_PER_CH_BW20_SIZE;
++			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
++			cal_id = RF_DPD_FLAT_5G_MEM_CAL;
++			chan_list = dpd_5g_ch_list_bw80;
++			chan_list_size = DPD_CH_NUM(BW80_5G);
++			has_skip_ch = false;
+ 		} else if (bw > NL80211_CHAN_WIDTH_20) {
+ 			/* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
+ 			channel -= 2;
+ 		}
+ 		if (channel >= dpd_5g_skip_ch_list[0].hw_value &&
+-		    channel <= dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
++		    channel <= dpd_5g_skip_ch_list[DPD_CH_NUM(BW20_5G_SKIP) - 1].hw_value)
+ 			return 0;
+ 		break;
+ 	case NL80211_BAND_6GHZ:
+@@ -3788,20 +3794,27 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
+ 		cal_id = RF_DPD_FLAT_6G_CAL;
+ 		chan_list = mphy->sband_6g.sband.channels;
+ 		chan_list_size = mphy->sband_6g.sband.n_channels;
+-		base_offset += dpd_size_2g + dpd_size_5g;
++		base_offset += MT_EE_CAL_DPD_SIZE_2G + MT_EE_CAL_DPD_SIZE_5G;
+ 		if (bw == NL80211_CHAN_WIDTH_160) {
+ 			base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
+ 			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
+ 			cal_id = RF_DPD_FLAT_6G_MEM_CAL;
+ 			chan_list = dpd_6g_ch_list_bw160;
+-			chan_list_size = dpd_6g_bw160_ch_num;
+-		} else if (bw == NL80211_CHAN_WIDTH_320) {
++			chan_list_size = DPD_CH_NUM(BW160_6G);
++		} else if (is_mt7996(&dev->mt76) && bw == NL80211_CHAN_WIDTH_320) {
+ 			base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
+-				       dpd_6g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
++				       DPD_CH_NUM(BW80_6G) * DPD_PER_CH_GT_BW20_SIZE +
++				       DPD_CH_NUM(BW160_6G) * DPD_PER_CH_GT_BW20_SIZE;
+ 			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
+ 			cal_id = RF_DPD_FLAT_6G_MEM_CAL;
+ 			chan_list = dpd_6g_ch_list_bw320;
+-			chan_list_size = dpd_6g_bw320_ch_num;
++			chan_list_size = DPD_CH_NUM(BW320_6G);
++		} else if (is_mt7992(&dev->mt76) && bw == NL80211_CHAN_WIDTH_80) {
++			base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
++			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
++			cal_id = RF_DPD_FLAT_6G_MEM_CAL;
++			chan_list = dpd_6g_ch_list_bw80;
++			chan_list_size = DPD_CH_NUM(BW80_6G);
+ 		} else if (bw > NL80211_CHAN_WIDTH_20) {
+ 			/* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
+ 			channel -= 2;
+@@ -3821,9 +3834,8 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
+ 	if (idx == chan_list_size)
+ 		return -EINVAL;
+ 
+-	if (band == NL80211_BAND_5GHZ && bw != NL80211_CHAN_WIDTH_160 &&
+-	    channel > dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
+-		idx -= dpd_5g_skip_ch_num;
++	if (has_skip_ch && channel > dpd_5g_skip_ch_list[DPD_CH_NUM(BW20_5G_SKIP) - 1].hw_value)
++		idx -= DPD_CH_NUM(BW20_5G_SKIP);
+ 
+ 	cal += MT_EE_CAL_GROUP_SIZE + base_offset + idx * per_chan_size;
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 5cdc42bf..516129b0 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -194,6 +194,19 @@ struct mt7996_twt_flow {
+ 
+ DECLARE_EWMA(avg_signal, 10, 8)
+ 
++enum mt7996_dpd_ch_num {
++	DPD_CH_NUM_BW20_2G,
++	DPD_CH_NUM_BW20_5G,
++	DPD_CH_NUM_BW20_5G_SKIP,
++	DPD_CH_NUM_BW80_5G,
++	DPD_CH_NUM_BW160_5G,
++	DPD_CH_NUM_BW20_6G,
++	DPD_CH_NUM_BW80_6G,
++	DPD_CH_NUM_BW160_6G,
++	DPD_CH_NUM_BW320_6G,
++	DPD_CH_NUM_TYPE_MAX,
++};
++
+ struct mt7996_sta {
+ 	struct mt76_wcid wcid; /* must be first */
+ 
+@@ -463,6 +476,10 @@ struct mt7996_dev {
+ 
+ 	void *cal;
+ 	u32 cur_prek_offset;
++	struct {
++		const u32 *rev;
++		u8 dpd_ch_num[DPD_CH_NUM_TYPE_MAX];
++	} prek;
+ 
+ 	struct {
+ 		u16 table_mask;
+@@ -599,7 +616,6 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
+ int mt7996_eeprom_get_target_power(struct mt7996_dev *dev,
+ 				   struct ieee80211_channel *chan);
+ s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band);
+-int mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band);
+ int mt7996_dma_init(struct mt7996_dev *dev);
+ void mt7996_dma_reset(struct mt7996_dev *dev, bool force);
+ void mt7996_dma_prefetch(struct mt7996_dev *dev);
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index 95d3bde0..9fa4edcd 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -434,7 +434,7 @@ mt7996_tm_set_tx_cont(struct mt7996_phy *phy, bool en)
+ static int
+ mt7996_tm_group_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
+ {
+-	u8 *eeprom;
++	u8 *eeprom, do_precal;
+ 	u32 i, group_size, dpd_size, size, offs, *pre_cal;
+ 	int ret = 0;
+ 	struct mt7996_dev *dev = phy->dev;
+@@ -462,6 +462,9 @@ mt7996_tm_group_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
+ 	dpd_size = MT_EE_CAL_DPD_SIZE;
+ 	size = group_size + dpd_size;
+ 	offs = MT_EE_DO_PRE_CAL;
++	do_precal = (MT_EE_WIFI_CAL_GROUP_2G * !!PREK(GROUP_SIZE_2G)) |
++		    (MT_EE_WIFI_CAL_GROUP_5G * !!PREK(GROUP_SIZE_5G)) |
++		    (MT_EE_WIFI_CAL_GROUP_6G * !!PREK(GROUP_SIZE_6G));
+ 
+ 	switch (state) {
+ 	case MT76_TM_STATE_GROUP_PREK:
+@@ -476,13 +479,10 @@ mt7996_tm_group_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
+ 		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == group_size,
+ 				   30 * HZ);
+ 
+-		if (ret) {
++		if (ret)
+ 			dev_err(dev->mt76.dev, "Group Pre-cal: mcu send msg failed!\n");
+-			return ret;
+-		}
+-
+-		if (!ret)
+-			eeprom[offs] |= MT_EE_WIFI_CAL_GROUP;
++		else
++			eeprom[offs] |= do_precal;
+ 		break;
+ 	case MT76_TM_STATE_GROUP_PREK_DUMP:
+ 		pre_cal = (u32 *)dev->cal;
+@@ -520,10 +520,12 @@ mt7996_tm_dpd_prek_send_req(struct mt7996_phy *phy, struct mt7996_tm_req *req,
+ 	struct mt76_phy *mphy = phy->mt76;
+ 	struct cfg80211_chan_def chandef_backup, *chandef = &mphy->chandef;
+ 	struct ieee80211_channel chan_backup;
+-	int i, ret;
++	int i, ret, skip_ch_num = DPD_CH_NUM(BW20_5G_SKIP);
+ 
+ 	if (!chan_list)
+ 		return -EOPNOTSUPP;
++	if (!channel_size)
++		return 0;
+ 
+ 	req->rf_test.op.rf.param.cal_param.func_data = cpu_to_le32(func_data);
+ 
+@@ -533,7 +535,7 @@ mt7996_tm_dpd_prek_send_req(struct mt7996_phy *phy, struct mt7996_tm_req *req,
+ 	for (i = 0; i < channel_size; i++) {
+ 		if (chan_list[i].band == NL80211_BAND_5GHZ &&
+ 		    chan_list[i].hw_value >= dpd_5g_skip_ch_list[0].hw_value &&
+-		    chan_list[i].hw_value <= dpd_5g_skip_ch_list[dpd_5g_skip_ch_num - 1].hw_value)
++		    chan_list[i].hw_value <= dpd_5g_skip_ch_list[skip_ch_num - 1].hw_value)
+ 			continue;
+ 
+ 		memcpy(chandef->chan, &chan_list[i], sizeof(struct ieee80211_channel));
+@@ -602,11 +604,11 @@ mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
+ 	switch (state) {
+ 	case MT76_TM_STATE_DPD_2G:
+ 		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_2g_ch_list_bw20,
+-						  dpd_2g_bw20_ch_num,
++						  DPD_CH_NUM(BW20_2G),
+ 						  NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_CAL);
+-		wait_on_prek_offset += dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
+-		wait_event_timeout(mdev->mcu.wait,
+-				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++		wait_on_prek_offset += DPD_CH_NUM(BW20_2G) * DPD_PER_CH_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
++				   30 * HZ);
+ 
+ 		do_precal = MT_EE_WIFI_CAL_DPD_2G;
+ 		break;
+@@ -617,18 +619,27 @@ mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
+ 						  NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_5G_CAL);
+ 		if (ret)
+ 			return ret;
+-		wait_on_prek_offset += (mphy->sband_5g.sband.n_channels - dpd_5g_skip_ch_num) *
+-				       DPD_PER_CH_BW20_SIZE;
+-		wait_event_timeout(mdev->mcu.wait,
+-				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++		wait_on_prek_offset += DPD_CH_NUM(BW20_5G) * DPD_PER_CH_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
++				   30 * HZ);
++
++		/* 5g channel bw80 calibration */
++		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_5g_ch_list_bw80,
++						  DPD_CH_NUM(BW80_5G),
++						  NL80211_CHAN_WIDTH_80, RF_DPD_FLAT_5G_MEM_CAL);
++		if (ret)
++			return ret;
++		wait_on_prek_offset += DPD_CH_NUM(BW80_5G) * DPD_PER_CH_GT_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
++				   30 * HZ);
+ 
+ 		/* 5g channel bw160 calibration */
+ 		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_5g_ch_list_bw160,
+-						  dpd_5g_bw160_ch_num,
++						  DPD_CH_NUM(BW160_5G),
+ 						  NL80211_CHAN_WIDTH_160, RF_DPD_FLAT_5G_MEM_CAL);
+-		wait_on_prek_offset += dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
+-		wait_event_timeout(mdev->mcu.wait,
+-				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++		wait_on_prek_offset += DPD_CH_NUM(BW160_5G) * DPD_PER_CH_GT_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
++				   30 * HZ);
+ 
+ 		do_precal = MT_EE_WIFI_CAL_DPD_5G;
+ 		break;
+@@ -639,27 +650,37 @@ mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
+ 						  NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_6G_CAL);
+ 		if (ret)
+ 			return ret;
+-		wait_on_prek_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
+-		wait_event_timeout(mdev->mcu.wait,
+-				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++		wait_on_prek_offset += DPD_CH_NUM(BW20_6G) * DPD_PER_CH_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
++				   30 * HZ);
++
++		/* 6g channel bw80 calibration */
++		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw80,
++						  DPD_CH_NUM(BW80_6G),
++						  NL80211_CHAN_WIDTH_80, RF_DPD_FLAT_6G_MEM_CAL);
++		if (ret)
++			return ret;
++		wait_on_prek_offset += DPD_CH_NUM(BW80_6G) * DPD_PER_CH_GT_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
++				   30 * HZ);
+ 
+ 		/* 6g channel bw160 calibration */
+ 		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw160,
+-						  dpd_6g_bw160_ch_num,
++						  DPD_CH_NUM(BW160_6G),
+ 						  NL80211_CHAN_WIDTH_160, RF_DPD_FLAT_6G_MEM_CAL);
+ 		if (ret)
+ 			return ret;
+-		wait_on_prek_offset += dpd_6g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
+-		wait_event_timeout(mdev->mcu.wait,
+-				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++		wait_on_prek_offset += DPD_CH_NUM(BW160_6G) * DPD_PER_CH_GT_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
++				   30 * HZ);
+ 
+ 		/* 6g channel bw320 calibration */
+ 		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw320,
+-						  dpd_6g_bw320_ch_num,
++						  DPD_CH_NUM(BW320_6G),
+ 						  NL80211_CHAN_WIDTH_320, RF_DPD_FLAT_6G_MEM_CAL);
+-		wait_on_prek_offset += dpd_6g_bw320_ch_num * DPD_PER_CH_GT_BW20_SIZE;
+-		wait_event_timeout(mdev->mcu.wait,
+-				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++		wait_on_prek_offset += DPD_CH_NUM(BW320_6G) * DPD_PER_CH_GT_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == wait_on_prek_offset,
++				   30 * HZ);
+ 
+ 		do_precal = MT_EE_WIFI_CAL_DPD_6G;
+ 		break;
+@@ -732,9 +753,9 @@ mt7996_tm_dump_precal(struct mt76_phy *mphy, struct sk_buff *msg, int flag, int
+ 	eeprom = dev->mt76.eeprom.data;
+ 	offs = MT_EE_DO_PRE_CAL;
+ 
+-	dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
+-	dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
+-	dpd_size_6g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_6GHZ);
++	dpd_size_2g = MT_EE_CAL_DPD_SIZE_2G;
++	dpd_size_5g = MT_EE_CAL_DPD_SIZE_5G;
++	dpd_size_6g = MT_EE_CAL_DPD_SIZE_6G;
+ 
+ 	switch (type) {
+ 	case PREK_SYNC_ALL:
+@@ -810,9 +831,9 @@ mt7996_tm_re_cal_event(struct mt7996_dev *dev, struct mt7996_tm_rf_test_result *
+ 	u8 *pre_cal;
+ 
+ 	pre_cal = dev->cal;
+-	dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
+-	dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
+-	dpd_size_6g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_6GHZ);
++	dpd_size_2g = MT_EE_CAL_DPD_SIZE_2G;
++	dpd_size_5g = MT_EE_CAL_DPD_SIZE_5G;
++	dpd_size_6g = MT_EE_CAL_DPD_SIZE_6G;
+ 
+ 	cal_idx = le32_to_cpu(data->cal_idx);
+ 	cal_type = le32_to_cpu(data->cal_type);
+diff --git a/testmode.c b/testmode.c
+index b9f71097..f1d162ce 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -37,6 +37,11 @@ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
+ };
+ EXPORT_SYMBOL_GPL(mt76_tm_policy);
+ 
++static inline bool mt76_testmode_offload(struct mt76_dev *dev)
++{
++	return is_mt7996(dev) || is_mt7992(dev);
++}
++
+ void mt76_testmode_tx_pending(struct mt76_phy *phy)
+ {
+ 	struct mt76_testmode_data *td = &phy->test;
+@@ -197,7 +202,7 @@ mt76_testmode_tx_init(struct mt76_phy *phy)
+ 	u8 max_nss = hweight8(phy->antenna_mask);
+ 	int ret;
+ 
+-	if (is_mt7996(phy->dev))
++	if (mt76_testmode_offload(phy->dev))
+ 		return 0;
+ 
+ 	ret = mt76_testmode_alloc_skb(phy, td->tx_mpdu_len);
+@@ -293,7 +298,7 @@ mt76_testmode_tx_start(struct mt76_phy *phy)
+ 	td->tx_done = 0;
+ 	td->tx_pending = td->tx_count;
+ 
+-	if (!is_mt7996(dev))
++	if (!mt76_testmode_offload(dev))
+ 		mt76_worker_schedule(&dev->tx_worker);
+ }
+ 
+@@ -303,7 +308,7 @@ mt76_testmode_tx_stop(struct mt76_phy *phy)
+ 	struct mt76_testmode_data *td = &phy->test;
+ 	struct mt76_dev *dev = phy->dev;
+ 
+-	if (is_mt7996(dev) && dev->test_ops->tx_stop) {
++	if (mt76_testmode_offload(dev) && dev->test_ops->tx_stop) {
+ 		dev->test_ops->tx_stop(phy);
+ 		return;
+ 	}
+@@ -783,10 +788,12 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ 	if (nla_put_u32(msg, MT76_TM_ATTR_STATE, td->state))
+ 		goto out;
+ 
++	if (nla_put_u8(msg, MT76_TM_ATTR_BAND_IDX, phy->band_idx))
++		goto out;
++
+ 	if (dev->test_mtd.name &&
+ 	    (nla_put_string(msg, MT76_TM_ATTR_MTD_PART, dev->test_mtd.name) ||
+-	     nla_put_u32(msg, MT76_TM_ATTR_MTD_OFFSET, dev->test_mtd.offset) ||
+-	     nla_put_u8(msg, MT76_TM_ATTR_BAND_IDX, phy->band_idx)))
++	     nla_put_u32(msg, MT76_TM_ATTR_MTD_OFFSET, dev->test_mtd.offset)))
+ 		goto out;
+ 
+ 	if (nla_put_u32(msg, MT76_TM_ATTR_TX_COUNT, td->tx_count) ||
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0053-mtk-wifi-mt76-mt7996-add-background-radar-hw-cap-che.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0053-mtk-wifi-mt76-mt7996-add-background-radar-hw-cap-che.patch
deleted file mode 100644
index 12d06e6..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0053-mtk-wifi-mt76-mt7996-add-background-radar-hw-cap-che.patch
+++ /dev/null
@@ -1,81 +0,0 @@
-From 189d4baa960d2202e518c3e15c9959524cafc3d0 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 22 Dec 2023 17:27:10 +0800
-Subject: [PATCH 053/116] mtk: wifi: mt76: mt7996: add background radar hw cap
- check
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/debugfs.c |  5 +++++
- mt7996/init.c    |  7 ++++---
- mt7996/mt7996.h  | 20 ++++++++++++++++++++
- 3 files changed, 29 insertions(+), 3 deletions(-)
-
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index f3a520b..6c5fbc7 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -262,6 +262,11 @@ mt7996_rdd_monitor(struct seq_file *s, void *data)
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
-+	if (!mt7996_get_background_radar_cap(dev)) {
-+		seq_puts(s, "no background radar capability\n");
-+		goto out;
-+	}
-+
- 	if (!cfg80211_chandef_valid(chandef)) {
- 		ret = -EINVAL;
- 		goto out;
-diff --git a/mt7996/init.c b/mt7996/init.c
-index cb00322..6430bfa 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -404,9 +404,10 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
- 
- 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION);
- 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_PROTECTION);
--	if (!mdev->dev->of_node ||
--	    !of_property_read_bool(mdev->dev->of_node,
--				   "mediatek,disable-radar-background"))
-+	if (mt7996_get_background_radar_cap(phy->dev) &&
-+	    (!mdev->dev->of_node ||
-+	     !of_property_read_bool(mdev->dev->of_node,
-+				    "mediatek,disable-radar-background")))
- 		wiphy_ext_feature_set(wiphy,
- 				      NL80211_EXT_FEATURE_RADAR_BACKGROUND);
- 
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 72865c8..c73701d 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -605,6 +605,26 @@ mt7996_band_valid(struct mt7996_dev *dev, u8 band)
- 	return band == MT_BAND0 || band == MT_BAND2;
- }
- 
-+static inline bool
-+mt7996_get_background_radar_cap(struct mt7996_dev *dev)
-+{
-+	switch (mt76_chip(&dev->mt76)) {
-+	case 0x7990:
-+		if (dev->chip_sku == MT7996_SKU_233)
-+			return 0;
-+		break;
-+	case 0x7992:
-+		if (dev->chip_sku == MT7992_SKU_23 ||
-+		    dev->chip_sku == MT7992_SKU_24)
-+			return 0;
-+		break;
-+	default:
-+		break;
-+	}
-+
-+	return 1;
-+}
-+
- extern const struct ieee80211_ops mt7996_ops;
- extern struct pci_driver mt7996_pci_driver;
- extern struct pci_driver mt7996_hif_driver;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0054-mtk-mt76-mt7996-assign-DEAUTH-to-ALTX-queue-for-CERT.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0054-mtk-mt76-mt7996-assign-DEAUTH-to-ALTX-queue-for-CERT.patch
new file mode 100644
index 0000000..15588fe
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0054-mtk-mt76-mt7996-assign-DEAUTH-to-ALTX-queue-for-CERT.patch
@@ -0,0 +1,42 @@
+From 3c7126f27c9a7a68514c4eb134203576db47cd82 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 14 Nov 2023 11:27:06 +0800
+Subject: [PATCH 054/199] mtk: mt76: mt7996: assign DEAUTH to ALTX queue for
+ CERT
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ mt7996/mac.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 90e8e7b1..bcddff23 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -747,6 +747,8 @@ static void
+ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
+ 			    struct sk_buff *skb, struct ieee80211_key_conf *key)
+ {
++	struct mt76_phy *mphy =
++		mt76_dev_phy(&dev->mt76, le32_get_bits(txwi[1], MT_TXD1_TGID));
+ 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+ 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+@@ -756,6 +758,14 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
+ 	u8 fc_type, fc_stype;
+ 	u32 val;
+ 
++	if (ieee80211_is_cert_mode(mphy->hw) && ieee80211_is_deauth(fc)) {
++		/* In WPA3 cert TC-4.8.1, the deauth must be transmitted without
++		 * considering PSM bit
++		 */
++		txwi[0] &= ~cpu_to_le32(MT_TXD0_Q_IDX);
++		txwi[0] |= cpu_to_le32(FIELD_PREP(MT_TXD0_Q_IDX, MT_LMAC_ALTX0));
++	}
++
+ 	if (ieee80211_is_action(fc) &&
+ 	    mgmt->u.action.category == WLAN_CATEGORY_BACK &&
+ 	    mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0054-mtk-wifi-mt76-mt7996-add-fallback-in-case-of-missing.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0054-mtk-wifi-mt76-mt7996-add-fallback-in-case-of-missing.patch
deleted file mode 100644
index 35eafe9..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0054-mtk-wifi-mt76-mt7996-add-fallback-in-case-of-missing.patch
+++ /dev/null
@@ -1,98 +0,0 @@
-From 60ed10d23382fef314766291e997d422ef9d9fd5 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Tue, 19 Mar 2024 17:33:49 +0800
-Subject: [PATCH 054/116] mtk: wifi: mt76: mt7996: add fallback in case of
- missing precal data
-
-Align Wi-Fi 6 upstream changes
-https://github.com/openwrt/mt76/commit/2135e201e7a9339e018d4e2d4a33c73266e674d7
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/eeprom.c | 30 +++++++++++++++++++++---------
- mt7996/init.c   |  2 +-
- mt7996/main.c   |  2 +-
- 3 files changed, 23 insertions(+), 11 deletions(-)
-
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 5eb8629..9987bda 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -487,17 +487,31 @@ static int mt7996_eeprom_load_precal(struct mt7996_dev *dev)
- 	size = MT_EE_CAL_GROUP_SIZE + MT_EE_CAL_DPD_SIZE;
- 
- 	dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
--	if (!dev->cal)
--		return -ENOMEM;
-+	if (!dev->cal) {
-+		ret = -ENOMEM;
-+		goto fail;
-+	}
- 
--	if (dev->bin_file_mode)
--		return mt7996_eeprom_load_precal_binfile(dev, MT_EE_PRECAL, size);
-+	if (dev->bin_file_mode) {
-+		ret = mt7996_eeprom_load_precal_binfile(dev, MT_EE_PRECAL, size);
-+		if (ret)
-+			goto fail;
-+	}
- 
- 	ret = mt76_get_of_data_from_mtd(mdev, dev->cal, offs, size);
- 	if (!ret)
--		return ret;
-+		return 0;
-+
-+	ret = mt76_get_of_data_from_nvmem(mdev, dev->cal, "precal", size);
-+	if (!ret)
-+		return 0;
-+
-+fail:
-+	dev_warn(dev->mt76.dev, "Failed to load precal data: %d\n", ret);
-+	devm_kfree(dev->mt76.dev, dev->cal);
-+	dev->cal = NULL;
- 
--	return mt76_get_of_data_from_nvmem(mdev, dev->cal, "precal", size);
-+	return ret;
- }
- 
- static int mt7996_apply_cal_free_data(struct mt7996_dev *dev)
-@@ -652,9 +666,7 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
- 	if (ret)
- 		return ret;
- 
--	ret = mt7996_eeprom_load_precal(dev);
--	if (ret)
--		return ret;
-+	mt7996_eeprom_load_precal(dev);
- 
- 	ret = mt7996_apply_cal_free_data(dev);
- 	if (ret)
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 6430bfa..c10f667 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -1010,7 +1010,7 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
- 	if (ret < 0)
- 		return ret;
- 
--	if (dev->flash_mode) {
-+	if (dev->cal) {
- 		ret = mt7996_mcu_apply_group_cal(dev);
- 		if (ret)
- 			return ret;
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 8e67616..c7cc66e 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -345,7 +345,7 @@ int mt7996_set_channel(struct mt7996_phy *phy)
- 
- 	mt76_set_channel(phy->mt76);
- 
--	if (dev->flash_mode) {
-+	if (dev->cal) {
- 		ret = mt7996_mcu_apply_tx_dpd(phy);
- 		if (ret)
- 			goto out;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0055-mtk-mt76-mt7996-add-no_beacon-vendor-command-for-cer.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0055-mtk-mt76-mt7996-add-no_beacon-vendor-command-for-cer.patch
new file mode 100644
index 0000000..6741fd5
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0055-mtk-mt76-mt7996-add-no_beacon-vendor-command-for-cer.patch
@@ -0,0 +1,153 @@
+From 04adadf07c93529a7b3b0c5e5bccd6bc99c97d8f Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Wed, 22 Nov 2023 22:42:09 +0800
+Subject: [PATCH 055/199] mtk: mt76: mt7996: add no_beacon vendor command for
+ cert
+
+Add the vendor command to disable/enable beacon
+
+[Usage]
+hostapd_cli -i <interface> no_beacon <value>
+<value>
+0: enable beacon
+1: disable beacon
+
+Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
+---
+ mt7996/mcu.c    | 11 +++++++++++
+ mt7996/mt7996.h |  1 +
+ mt7996/vendor.c | 41 +++++++++++++++++++++++++++++++++++++++++
+ mt7996/vendor.h | 12 ++++++++++++
+ 4 files changed, 65 insertions(+)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 3a7f9df8..7b520a90 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -5103,4 +5103,15 @@ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ 		break;
+ 	}
+ }
++
++void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
++{
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct ieee80211_hw *hw = mvif->phy->mt76->hw;
++	u8 val = *((u8 *)data);
++
++	vif->bss_conf.enable_beacon = val;
++
++	mt7996_mcu_add_beacon(hw, vif, val);
++}
+ #endif
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 516129b0..34886128 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -835,6 +835,7 @@ void mt7996_set_wireless_amsdu(struct ieee80211_hw *hw, u8 en);
+ void mt7996_mcu_set_mimo(struct mt7996_phy *phy);
+ int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val);
+ int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data);
++void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
+ #endif
+ 
+ int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index ba00ef35..31688c37 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -113,6 +113,11 @@ background_radar_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL] = {
+ 	[MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE] = {.type = NLA_U8 },
+ };
+ 
++static const struct nla_policy
++beacon_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BEACON_CTRL] = {
++	[MTK_VENDOR_ATTR_BEACON_CTRL_MODE] = { .type = NLA_U8 },
++};
++
+ struct mt7996_amnt_data {
+ 	u8 idx;
+ 	u8 addr[ETH_ALEN];
+@@ -942,6 +947,31 @@ static int mt7996_vendor_background_radar_mode_ctrl(struct wiphy *wiphy,
+ 	return mt7996_mcu_rdd_background_disable_timer(dev, !!background_radar_mode);
+ }
+ 
++static int mt7996_vendor_beacon_ctrl(struct wiphy *wiphy,
++				     struct wireless_dev *wdev,
++				     const void *data,
++				     int data_len)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_BEACON_CTRL];
++	int err;
++	u8 val8;
++
++	err = nla_parse(tb, MTK_VENDOR_ATTR_BEACON_CTRL_MAX, data, data_len,
++			beacon_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	if (tb[MTK_VENDOR_ATTR_BEACON_CTRL_MODE]) {
++		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_BEACON_CTRL_MODE]);
++		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
++				mt7996_set_beacon_vif, &val8);
++	}
++
++	return 0;
++}
++
++
+ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 	{
+ 		.info = {
+@@ -1058,6 +1088,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 		.policy = background_radar_ctrl_policy,
+ 		.maxattr = MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX,
+ 	},
++	{
++		.info = {
++			.vendor_id = MTK_NL80211_VENDOR_ID,
++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++			 WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.doit = mt7996_vendor_beacon_ctrl,
++		.policy = beacon_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_BEACON_CTRL_MAX,
++	},
+ };
+ 
+ void mt7996_vendor_register(struct mt7996_phy *phy)
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index 7c812f91..0d1ef322 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -16,6 +16,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
+ 	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
+ 	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
++	MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
+ };
+ 
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -227,6 +228,17 @@ enum mtk_vendor_attr_pp_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_beacon_ctrl {
++	MTK_VENDOR_ATTR_BEACON_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_BEACON_CTRL_MODE,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_BEACON_CTRL,
++	MTK_VENDOR_ATTR_BEACON_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
++};
++
+ #endif
+ 
+ #endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0055-mtk-wifi-mt76-mt7996-add-kite-part-number-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0055-mtk-wifi-mt76-mt7996-add-kite-part-number-support.patch
deleted file mode 100644
index 4a4a5f0..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0055-mtk-wifi-mt76-mt7996-add-kite-part-number-support.patch
+++ /dev/null
@@ -1,92 +0,0 @@
-From 1b54509046b166213c2e9611d6d4db9d8b42e493 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 27 Mar 2024 17:50:16 +0800
-Subject: [PATCH 055/116] mtk: wifi: mt76: mt7996: add kite part number support
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/eeprom.c | 35 +++++++++++++++++++++++------------
- 1 file changed, 23 insertions(+), 12 deletions(-)
-
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 9987bda..51455d8 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -316,26 +316,39 @@ out:
- 	return ret;
- }
- 
--static int mt7996_eeprom_parse_efuse_hw_cap(struct mt7996_dev *dev)
-+static int mt7996_eeprom_parse_efuse_hw_cap(struct mt7996_phy *phy,
-+					    u8 *path, u8 *rx_path, u8 *nss)
- {
- #define MODE_HE_ONLY		BIT(0)
-+#define STREAM_MASK		GENMASK(2, 0)
-+#define STREAM_OFFSET		1
-+#define TX_PATH_OFFSET		10
-+#define RX_PATH_OFFSET		19
- #define WTBL_SIZE_GROUP		GENMASK(31, 28)
-+#define GET_STREAM_CAP(offs)	({				\
-+	typeof(offs) _offs = (offs);				\
-+	((cap & (STREAM_MASK << _offs)) >> _offs);		\
-+})
-+	struct mt7996_dev *dev = phy->dev;
- 	u32 cap = 0;
- 	int ret;
-+	u8 band_offs = phy->mt76->band_idx * hweight8(STREAM_MASK);
- 
- 	ret = mt7996_mcu_get_chip_config(dev, &cap);
- 	if (ret)
- 		return ret;
- 
--	cap = 0x4b249248;	/* internal hardcode */
-+	dev->has_eht = true;
- 	if (cap) {
- 		dev->has_eht = !(cap & MODE_HE_ONLY);
- 		dev->wtbl_size_group = u32_get_bits(cap, WTBL_SIZE_GROUP);
-+		*nss = min_t(u8, *nss, GET_STREAM_CAP(STREAM_OFFSET + band_offs));
-+		*path = min_t(u8, *path, GET_STREAM_CAP(TX_PATH_OFFSET + band_offs));
-+		*rx_path = min_t(u8, *rx_path, GET_STREAM_CAP(RX_PATH_OFFSET + band_offs));
- 	}
- 
--	if (dev->wtbl_size_group < 2 || dev->wtbl_size_group > 4 ||
--	    is_mt7992(&dev->mt76))
--		dev->wtbl_size_group = 2; /* set default */
-+	if (dev->wtbl_size_group < 2 || dev->wtbl_size_group > 4)
-+		dev->wtbl_size_group = is_mt7996(&dev->mt76) ? 4 : 2;	/* set default */
- 
- 	return 0;
- }
-@@ -379,13 +392,15 @@ static int mt7996_eeprom_parse_band_config(struct mt7996_phy *phy)
- 
- int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy)
- {
-+	struct mt76_phy *mphy = phy->mt76;
- 	u8 path, rx_path, nss, band_idx = phy->mt76->band_idx;
- 	u8 *eeprom = dev->mt76.eeprom.data;
--	struct mt76_phy *mphy = phy->mt76;
--	int max_path = 5, max_nss = 4;
--	int ret;
-+	int ret, max_path = 5, max_nss = 4;
- 
- 	mt7996_parse_eeprom_stream(eeprom, band_idx, &path, &rx_path, &nss);
-+	ret = mt7996_eeprom_parse_efuse_hw_cap(phy, &path, &rx_path, &nss);
-+	if (ret)
-+		return ret;
- 
- 	if (!path || path > max_path)
- 		path = max_path;
-@@ -405,10 +420,6 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy)
- 		dev->chainshift[band_idx + 1] = dev->chainshift[band_idx] +
- 						hweight16(mphy->chainmask);
- 
--	ret = mt7996_eeprom_parse_efuse_hw_cap(dev);
--	if (ret)
--		return ret;
--
- 	return mt7996_eeprom_parse_band_config(phy);
- }
- 
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0056-mtk-mt76-mt7996-add-adie-efuse-merge-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0056-mtk-mt76-mt7996-add-adie-efuse-merge-support.patch
new file mode 100644
index 0000000..bc5a4a9
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0056-mtk-mt76-mt7996-add-adie-efuse-merge-support.patch
@@ -0,0 +1,288 @@
+From 81d008c8fcfaa851414ee63242d9ea4c0eb888df Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 24 Nov 2023 09:49:08 +0800
+Subject: [PATCH 056/199] mtk: mt76: mt7996: add adie efuse merge support
+
+Merge adie-dependent parameters in efuse into eeprom after FT.
+Note that Eagle BE14000 is not considered yet.
+Add efuse dump command.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/debugfs.c  |  41 +++++++++++++
+ mt7996/eeprom.c   | 144 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.c      |   6 +-
+ mt7996/testmode.c |   8 ++-
+ 4 files changed, 195 insertions(+), 4 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 1f4bad62..58879695 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -881,6 +881,46 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_muru_disable,
+ 			 mt7996_fw_debug_muru_disable_get,
+ 			 mt7996_fw_debug_muru_disable_set, "%lld\n");
+ 
++static ssize_t
++mt7996_efuse_get(struct file *file, char __user *user_buf,
++		 size_t count, loff_t *ppos)
++{
++	struct mt7996_dev *dev = file->private_data;
++	struct mt76_dev *mdev = &dev->mt76;
++	u8 *buff = mdev->otp.data;
++	int i;
++	ssize_t ret;
++	u32 block_num;
++
++	mdev->otp.size = MT7996_EEPROM_SIZE;
++	if (is_mt7996(&dev->mt76) && dev->chip_sku == MT7996_SKU_444)
++		mdev->otp.size += 3 * MT_EE_CAL_UNIT;
++
++	if (!mdev->otp.data) {
++		mdev->otp.data = devm_kzalloc(mdev->dev, mdev->otp.size, GFP_KERNEL);
++		if (!mdev->otp.data)
++			return -ENOMEM;
++
++		block_num = DIV_ROUND_UP(mdev->otp.size, MT7996_EEPROM_BLOCK_SIZE);
++		for (i = 0; i < block_num; i++) {
++			buff = mdev->otp.data + i * MT7996_EEPROM_BLOCK_SIZE;
++			ret = mt7996_mcu_get_eeprom(dev, i * MT7996_EEPROM_BLOCK_SIZE, buff);
++			if (ret && ret != -EINVAL)
++				return ret;
++		}
++	}
++
++	ret = simple_read_from_buffer(user_buf, count, ppos, mdev->otp.data, mdev->otp.size);
++
++	return ret;
++}
++
++static const struct file_operations mt7996_efuse_ops = {
++	.read = mt7996_efuse_get,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
+ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+@@ -907,6 +947,7 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ 	debugfs_create_devm_seqfile(dev->mt76.dev, "twt_stats", dir,
+ 				    mt7996_twt_stats);
+ 	debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval);
++	debugfs_create_file("otp", 0400, dir, dev, &mt7996_efuse_ops);
+ 
+ 	if (phy->mt76->cap.has_5ghz) {
+ 		debugfs_create_u32("dfs_hw_pattern", 0400, dir,
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 0d50d313..8cdd6d96 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -496,6 +496,146 @@ static int mt7996_eeprom_load_precal(struct mt7996_dev *dev)
+ 	return mt76_get_of_data_from_nvmem(mdev, dev->cal, "precal", size);
+ }
+ 
++static int mt7996_apply_cal_free_data(struct mt7996_dev *dev)
++{
++#define MT_EE_CAL_FREE_MAX_SIZE		30
++#define MT_EE_7977BN_OFFSET		(0x1200 - 0x500)
++#define MT_EE_END_OFFSET		0xffff
++	enum adie_type {
++		ADIE_7975,
++		ADIE_7976,
++		ADIE_7977,
++		ADIE_7978,
++		ADIE_7979,
++	};
++	static const u16 adie_offs_list[][MT_EE_CAL_FREE_MAX_SIZE] = {
++		[ADIE_7975] = {0x5cd, 0x5cf, 0x5d1, 0x5d3, 0x6c0, 0x6c1, 0x6c2, 0x6c3,
++			       0x7a1, 0x7a6, 0x7a8, 0x7aa, -1},
++		[ADIE_7976] = {0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x55, 0x57, 0x59,
++			       0x70, 0x71, 0x790, 0x791, 0x794, 0x795, 0x7a6, 0x7a8, 0x7aa, -1},
++		[ADIE_7977] = {0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x55, 0x57, 0x59,
++			       0x69, 0x6a, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, -1},
++		[ADIE_7978] = {0x91, 0x95, 0x100, 0x102, 0x104, 0x106, 0x107,
++			       0x108, 0x109, 0x10a, 0x10b, 0x10c, 0x10e, 0x110, -1},
++		[ADIE_7979] = {0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x55, 0x57, 0x59,
++			       0x69, 0x6a, 0x7a, 0x7b, 0x7c, 0x7e, 0x80, -1},
++	};
++	static const u16 eep_offs_list[][MT_EE_CAL_FREE_MAX_SIZE] = {
++		[ADIE_7975] = {0x451, 0x453, 0x455, 0x457, 0x44c, 0x44d, 0x44e, 0x44f,
++			       0xba1, 0xba6, 0xba8, 0xbaa, -1},
++		[ADIE_7976] = {0x44c, 0x44d, 0x44e, 0x44f, 0x450,
++			       0x451, 0x453, 0x455, 0x457, 0x459,
++			       0x470, 0x471, 0xb90, 0xb91, 0xb94, 0xb95,
++			       0xba6, 0xba8, 0xbaa, -1},
++		[ADIE_7977] = {0x124c, 0x124d, 0x124e, 0x124f, 0x1250,
++			       0x1251, 0x1253, 0x1255, 0x1257, 0x1259,
++			       0x1269, 0x126a, 0x127a, 0x127b, 0x127c, 0x127d, 0x127e, -1},
++		[ADIE_7978] = {0xb91, 0xb95, 0x480, 0x482, 0x484, 0x486, 0x487, 0x488, 0x489,
++			       0x48a, 0x48b, 0x48c, 0x48e, 0x490, -1},
++		[ADIE_7979] = {0x124c, 0x124d, 0x124e, 0x124f, 0x1250, 0x1251,
++			       0x1253, 0x1255, 0x1257, 0x1259, 0x1269, 0x126a,
++			       0x127a, 0x127b, 0x127c, 0x127e, 0x1280, -1},
++	};
++	static const u16 adie_base_7996[] = {
++		0x400, 0x1e00, 0x1200
++	};
++	static const u16 adie_base_7992[] = {
++		0x400, 0x1200, 0x0
++	};
++	static const u16 *adie_offs[__MT_MAX_BAND];
++	static const u16 *eep_offs[__MT_MAX_BAND];
++	static const u16 *adie_base;
++	u8 *eeprom = dev->mt76.eeprom.data;
++	u8 buf[MT7996_EEPROM_BLOCK_SIZE];
++	int adie_id, band, i, ret;
++
++	switch (mt76_chip(&dev->mt76)) {
++	case 0x7990:
++		adie_base = adie_base_7996;
++		/* adie 0 */
++		if (dev->fem_type == MT7996_FEM_INT)
++			adie_id = ADIE_7975;
++		else
++			adie_id = ADIE_7976;
++		adie_offs[0] = adie_offs_list[adie_id];
++		eep_offs[0] = eep_offs_list[adie_id];
++
++		/* adie 1 */
++		if (dev->chip_sku != MT7996_SKU_404) {
++			adie_offs[1] = adie_offs_list[ADIE_7977];
++			eep_offs[1] = eep_offs_list[ADIE_7977];
++		}
++
++		/* adie 2 */
++		adie_offs[2] = adie_offs_list[ADIE_7977];
++		eep_offs[2] = eep_offs_list[ADIE_7977];
++		break;
++	case 0x7992:
++		adie_base = adie_base_7992;
++		/* adie 0 */
++		if (dev->chip_sku == MT7992_SKU_44 &&
++		    dev->fem_type != MT7996_FEM_EXT)
++			adie_id = ADIE_7975;
++		else if (dev->chip_sku == MT7992_SKU_24)
++			adie_id = ADIE_7978;
++		else
++			adie_id = ADIE_7976;
++		adie_offs[0] = adie_offs_list[adie_id];
++		eep_offs[0] = eep_offs_list[adie_id];
++
++		/* adie 1 */
++		if (dev->chip_sku == MT7992_SKU_44 &&
++		    dev->fem_type != MT7996_FEM_INT)
++			adie_id = ADIE_7977;
++		else if (dev->chip_sku != MT7992_SKU_23)
++			adie_id = ADIE_7979;
++		else
++			break;
++		adie_offs[1] = adie_offs_list[adie_id];
++		eep_offs[1] = eep_offs_list[adie_id];
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	for (band = 0; band < __MT_MAX_BAND; band++) {
++		u16 adie_offset, eep_offset;
++		u32 block_num, prev_block_num = -1;
++
++		if (!adie_offs[band])
++			continue;
++
++		for (i = 0; i < MT_EE_CAL_FREE_MAX_SIZE; i++) {
++			adie_offset = adie_offs[band][i] + adie_base[band];
++			eep_offset = eep_offs[band][i];
++			block_num = adie_offset / MT7996_EEPROM_BLOCK_SIZE;
++
++			if (adie_offs[band][i] == MT_EE_END_OFFSET)
++				break;
++
++			if (is_mt7996(&dev->mt76) && dev->chip_sku == MT7996_SKU_444 &&
++			    band == MT_BAND1)
++				eep_offset -= MT_EE_7977BN_OFFSET;
++
++			if (prev_block_num != block_num) {
++				ret = mt7996_mcu_get_eeprom(dev, adie_offset, buf);
++				if (ret) {
++					if (ret != -EINVAL)
++						return ret;
++
++					prev_block_num = -1;
++					continue;
++				}
++			}
++
++			eeprom[eep_offset] = buf[adie_offset % MT7996_EEPROM_BLOCK_SIZE];
++			prev_block_num = block_num;
++		}
++	}
++
++	return 0;
++}
++
+ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ {
+ 	int ret;
+@@ -512,6 +652,10 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ 	if (ret)
+ 		return ret;
+ 
++	ret = mt7996_apply_cal_free_data(dev);
++	if (ret)
++		return ret;
++
+ 	ret = mt7996_eeprom_parse_hw_cap(dev, &dev->phy);
+ 	if (ret < 0)
+ 		return ret;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 7b520a90..1c98d179 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3609,7 +3609,7 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf)
+ 	};
+ 	struct sk_buff *skb;
+ 	bool valid;
+-	int ret;
++	int ret = 0;
+ 	u8 *buf = read_buf;
+ 
+ 	ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+@@ -3627,11 +3627,13 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf)
+ 
+ 		skb_pull(skb, 48);
+ 		memcpy(buf, skb->data, MT7996_EEPROM_BLOCK_SIZE);
++	} else {
++		ret = -EINVAL;
+ 	}
+ 
+ 	dev_kfree_skb(skb);
+ 
+-	return 0;
++	return ret;
+ }
+ 
+ int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num)
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index 9fa4edcd..784a8bea 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -2116,8 +2116,12 @@ mt7996_tm_write_back_to_efuse(struct mt7996_dev *dev)
+ 		memcpy(req.data, eeprom + i, MT76_TM_EEPROM_BLOCK_SIZE);
+ 
+ 		ret = mt7996_mcu_get_eeprom(dev, i, read_buf);
+-		if (ret < 0)
+-			return ret;
++		if (ret) {
++			if (ret != -EINVAL)
++				return ret;
++
++			memset(read_buf, 0, MT76_TM_EEPROM_BLOCK_SIZE);
++		}
+ 
+ 		if (!memcmp(req.data, read_buf, MT76_TM_EEPROM_BLOCK_SIZE))
+ 			continue;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0056-mtk-wifi-mt76-revert-page_poll-for-kernel-5.4.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0056-mtk-wifi-mt76-revert-page_poll-for-kernel-5.4.patch
deleted file mode 100644
index b8213cd..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0056-mtk-wifi-mt76-revert-page_poll-for-kernel-5.4.patch
+++ /dev/null
@@ -1,617 +0,0 @@
-From f4f100bea5c4e42cfd7f815555be0a6efc79aaba Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Mon, 6 Feb 2023 19:49:22 +0800
-Subject: [PATCH 056/116] mtk: wifi: mt76: revert page_poll for kernel 5.4
-
-This reverts commit e8c10835cf062c577ddf426913788c39d30b4bd7.
----
- dma.c         | 75 ++++++++++++++++++++++++++-------------------------
- mac80211.c    | 56 --------------------------------------
- mt76.h        | 22 +--------------
- mt7915/main.c | 26 +++++++-----------
- usb.c         | 43 ++++++++++++++---------------
- wed.c         | 50 ++++++++++++++++++++++------------
- 6 files changed, 104 insertions(+), 168 deletions(-)
-
-diff --git a/dma.c b/dma.c
-index 66c000e..33a84f5 100644
---- a/dma.c
-+++ b/dma.c
-@@ -178,7 +178,7 @@ mt76_free_pending_rxwi(struct mt76_dev *dev)
- 	local_bh_disable();
- 	while ((t = __mt76_get_rxwi(dev)) != NULL) {
- 		if (t->ptr)
--			mt76_put_page_pool_buf(t->ptr, false);
-+			skb_free_frag(t->ptr);
- 		kfree(t);
- 	}
- 	local_bh_enable();
-@@ -450,9 +450,9 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
- 		if (!t)
- 			return NULL;
- 
--		dma_sync_single_for_cpu(dev->dma_dev, t->dma_addr,
--				SKB_WITH_OVERHEAD(q->buf_size),
--				page_pool_get_dma_dir(q->page_pool));
-+		dma_unmap_single(dev->dma_dev, t->dma_addr,
-+				 SKB_WITH_OVERHEAD(q->buf_size),
-+				 DMA_FROM_DEVICE);
- 
- 		buf = t->ptr;
- 		t->dma_addr = 0;
-@@ -462,9 +462,9 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
- 		if (drop)
- 			*drop |= !!(buf1 & MT_DMA_CTL_WO_DROP);
- 	} else {
--		dma_sync_single_for_cpu(dev->dma_dev, e->dma_addr[0],
--				SKB_WITH_OVERHEAD(q->buf_size),
--				page_pool_get_dma_dir(q->page_pool));
-+		dma_unmap_single(dev->dma_dev, e->dma_addr[0],
-+				 SKB_WITH_OVERHEAD(q->buf_size),
-+				 DMA_FROM_DEVICE);
- 	}
- 
- done:
-@@ -638,7 +638,8 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
- 		     bool allow_direct)
- {
- 	int len = SKB_WITH_OVERHEAD(q->buf_size);
--	int frames = 0;
-+	int frames = 0, offset = q->buf_offset;
-+	dma_addr_t addr;
- 
- 	if (!q->ndesc)
- 		return 0;
-@@ -647,28 +648,29 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
- 
- 	while (q->queued < q->ndesc - 1) {
- 		struct mt76_queue_buf qbuf = {};
--		enum dma_data_direction dir;
--		dma_addr_t addr;
--		int offset;
- 		void *buf = NULL;
- 
- 		if (mt76_queue_is_wed_rro_ind(q))
- 			goto done;
- 
--		buf = mt76_get_page_pool_buf(q, &offset, q->buf_size);
-+		buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
- 		if (!buf)
- 			break;
- 
--		addr = page_pool_get_dma_addr(virt_to_head_page(buf)) + offset;
--		dir = page_pool_get_dma_dir(q->page_pool);
--		dma_sync_single_for_device(dev->dma_dev, addr, len, dir);
-+		addr = dma_map_single(dev->dma_dev, buf, len, DMA_FROM_DEVICE);
-+		if (unlikely(dma_mapping_error(dev->dma_dev, addr))) {
-+			skb_free_frag(buf);
-+			break;
-+		}
- 
--		qbuf.addr = addr + q->buf_offset;
-+		qbuf.addr = addr + offset;
- done:
--		qbuf.len = len - q->buf_offset;
-+		qbuf.len = len - offset;
- 		qbuf.skip_unmap = false;
- 		if (mt76_dma_add_rx_buf(dev, q, &qbuf, buf) < 0) {
--			mt76_put_page_pool_buf(buf, allow_direct);
-+			dma_unmap_single(dev->dma_dev, addr, len,
-+					 DMA_FROM_DEVICE);
-+			skb_free_frag(buf);
- 			break;
- 		}
- 		frames++;
-@@ -722,10 +724,6 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
- 	if (!q->entry)
- 		return -ENOMEM;
- 
--	ret = mt76_create_page_pool(dev, q);
--	if (ret)
--		return ret;
--
- 	ret = mt76_wed_dma_setup(dev, q, false);
- 	if (ret)
- 		return ret;
-@@ -744,6 +742,7 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
- static void
- mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
- {
-+	struct page *page;
- 	void *buf;
- 	bool more;
- 
-@@ -759,7 +758,7 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
- 			break;
- 
- 		if (!mt76_queue_is_wed_rro(q))
--			mt76_put_page_pool_buf(buf, false);
-+			skb_free_frag(buf);
- 	} while (1);
- 
- 	spin_lock_bh(&q->lock);
-@@ -769,6 +768,16 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
- 	}
- 
- 	spin_unlock_bh(&q->lock);
-+
-+	if (mt76_queue_is_wed_rx(q))
-+		return;
-+
-+	if (!q->rx_page.va)
-+		return;
-+
-+	page = virt_to_page(q->rx_page.va);
-+	__page_frag_cache_drain(page, q->rx_page.pagecnt_bias);
-+	memset(&q->rx_page, 0, sizeof(q->rx_page));
- }
- 
- static void
-@@ -791,15 +800,10 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
- 	/* reset WED rx queues */
- 	mt76_wed_dma_setup(dev, q, true);
- 
--	if (mt76_queue_is_wed_tx_free(q))
--		return;
--
--	if (mtk_wed_device_active(&dev->mmio.wed) &&
--	    mt76_queue_is_wed_rro(q))
--		return;
--
--	mt76_dma_sync_idx(dev, q);
--	mt76_dma_rx_fill(dev, q, false);
-+	if (!mt76_queue_is_wed_tx_free(q)) {
-+		mt76_dma_sync_idx(dev, q);
-+		mt76_dma_rx_fill(dev, q, false);
-+	}
- }
- 
- static void
-@@ -816,7 +820,7 @@ mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
- 
- 		skb_add_rx_frag(skb, nr_frags, page, offset, len, q->buf_size);
- 	} else {
--		mt76_put_page_pool_buf(data, allow_direct);
-+		skb_free_frag(data);
- 	}
- 
- 	if (more)
-@@ -891,7 +895,6 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
- 			goto free_frag;
- 
- 		skb_reserve(skb, q->buf_offset);
--		skb_mark_for_recycle(skb);
- 
- 		*(u32 *)skb->cb = info;
- 
-@@ -907,7 +910,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
- 		continue;
- 
- free_frag:
--		mt76_put_page_pool_buf(data, allow_direct);
-+		skb_free_frag(data);
- 	}
- 
- 	mt76_dma_rx_fill(dev, q, true);
-@@ -1010,8 +1013,6 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
- 
- 		netif_napi_del(&dev->napi[i]);
- 		mt76_dma_rx_cleanup(dev, q);
--
--		page_pool_destroy(q->page_pool);
- 	}
- 
- 	if (mtk_wed_device_active(&dev->mmio.wed))
-diff --git a/mac80211.c b/mac80211.c
-index de0a398..a050d0a 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -566,47 +566,6 @@ void mt76_unregister_phy(struct mt76_phy *phy)
- }
- EXPORT_SYMBOL_GPL(mt76_unregister_phy);
- 
--int mt76_create_page_pool(struct mt76_dev *dev, struct mt76_queue *q)
--{
--	struct page_pool_params pp_params = {
--		.order = 0,
--		.flags = PP_FLAG_PAGE_FRAG,
--		.nid = NUMA_NO_NODE,
--		.dev = dev->dma_dev,
--	};
--	int idx = q - dev->q_rx;
--
--	switch (idx) {
--	case MT_RXQ_MAIN:
--	case MT_RXQ_BAND1:
--	case MT_RXQ_BAND2:
--		pp_params.pool_size = 256;
--		break;
--	default:
--		pp_params.pool_size = 16;
--		break;
--	}
--
--	if (mt76_is_mmio(dev)) {
--		/* rely on page_pool for DMA mapping */
--		pp_params.flags |= PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
--		pp_params.dma_dir = DMA_FROM_DEVICE;
--		pp_params.max_len = PAGE_SIZE;
--		pp_params.offset = 0;
--	}
--
--	q->page_pool = page_pool_create(&pp_params);
--	if (IS_ERR(q->page_pool)) {
--		int err = PTR_ERR(q->page_pool);
--
--		q->page_pool = NULL;
--		return err;
--	}
--
--	return 0;
--}
--EXPORT_SYMBOL_GPL(mt76_create_page_pool);
--
- struct mt76_dev *
- mt76_alloc_device(struct device *pdev, unsigned int size,
- 		  const struct ieee80211_ops *ops,
-@@ -1819,21 +1778,6 @@ void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi,
- }
- EXPORT_SYMBOL_GPL(mt76_ethtool_worker);
- 
--void mt76_ethtool_page_pool_stats(struct mt76_dev *dev, u64 *data, int *index)
--{
--#ifdef CONFIG_PAGE_POOL_STATS
--	struct page_pool_stats stats = {};
--	int i;
--
--	mt76_for_each_q_rx(dev, i)
--		page_pool_get_stats(dev->q_rx[i].page_pool, &stats);
--
--	page_pool_ethtool_stats_get(data, &stats);
--	*index += page_pool_ethtool_stats_get_count();
--#endif
--}
--EXPORT_SYMBOL_GPL(mt76_ethtool_page_pool_stats);
--
- enum mt76_dfs_state mt76_phy_dfs_state(struct mt76_phy *phy)
- {
- 	struct ieee80211_hw *hw = phy->hw;
-diff --git a/mt76.h b/mt76.h
-index 8b17dfb..fd6eab5 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -251,7 +251,7 @@ struct mt76_queue {
- 
- 	dma_addr_t desc_dma;
- 	struct sk_buff *rx_head;
--	struct page_pool *page_pool;
-+	struct page_frag_cache rx_page;
- };
- 
- struct mt76_mcu_ops {
-@@ -1606,7 +1606,6 @@ mt76u_bulk_msg(struct mt76_dev *dev, void *data, int len, int *actual_len,
- 	return usb_bulk_msg(udev, pipe, data, len, actual_len, timeout);
- }
- 
--void mt76_ethtool_page_pool_stats(struct mt76_dev *dev, u64 *data, int *index);
- void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi,
- 			 struct mt76_sta_stats *stats, bool eht);
- int mt76_skb_adjust_pad(struct sk_buff *skb, int pad);
-@@ -1752,25 +1751,6 @@ void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked);
- struct mt76_txwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token);
- int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
- 			  struct mt76_txwi_cache *r, dma_addr_t phys);
--int mt76_create_page_pool(struct mt76_dev *dev, struct mt76_queue *q);
--static inline void mt76_put_page_pool_buf(void *buf, bool allow_direct)
--{
--	struct page *page = virt_to_head_page(buf);
--
--	page_pool_put_full_page(page->pp, page, allow_direct);
--}
--
--static inline void *
--mt76_get_page_pool_buf(struct mt76_queue *q, u32 *offset, u32 size)
--{
--	struct page *page;
--
--	page = page_pool_dev_alloc_frag(q->page_pool, offset, size);
--	if (!page)
--		return NULL;
--
--	return page_address(page) + *offset;
--}
- 
- static inline void mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked)
- {
-diff --git a/mt7915/main.c b/mt7915/main.c
-index b16a633..ad91fc3 100644
---- a/mt7915/main.c
-+++ b/mt7915/main.c
-@@ -1402,22 +1402,19 @@ void mt7915_get_et_strings(struct ieee80211_hw *hw,
- 			   struct ieee80211_vif *vif,
- 			   u32 sset, u8 *data)
- {
--	if (sset != ETH_SS_STATS)
--		return;
--
--	memcpy(data, mt7915_gstrings_stats, sizeof(mt7915_gstrings_stats));
--	data += sizeof(mt7915_gstrings_stats);
--	page_pool_ethtool_stats_get_strings(data);
-+	if (sset == ETH_SS_STATS)
-+		memcpy(data, mt7915_gstrings_stats,
-+		       sizeof(mt7915_gstrings_stats));
- }
- 
- static
- int mt7915_get_et_sset_count(struct ieee80211_hw *hw,
- 			     struct ieee80211_vif *vif, int sset)
- {
--	if (sset != ETH_SS_STATS)
--		return 0;
-+	if (sset == ETH_SS_STATS)
-+		return MT7915_SSTATS_LEN;
- 
--	return MT7915_SSTATS_LEN + page_pool_ethtool_stats_get_count();
-+	return 0;
- }
- 
- static void mt7915_ethtool_worker(void *wi_data, struct ieee80211_sta *sta)
-@@ -1445,7 +1442,7 @@ void mt7915_get_et_stats(struct ieee80211_hw *hw,
- 		.idx = mvif->mt76.idx,
- 	};
- 	/* See mt7915_ampdu_stat_read_phy, etc */
--	int i, ei = 0, stats_size;
-+	int i, ei = 0;
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
-@@ -1557,12 +1554,9 @@ void mt7915_get_et_stats(struct ieee80211_hw *hw,
- 		return;
- 
- 	ei += wi.worker_stat_count;
--
--	mt76_ethtool_page_pool_stats(&dev->mt76, &data[ei], &ei);
--
--	stats_size = MT7915_SSTATS_LEN + page_pool_ethtool_stats_get_count();
--	if (ei != stats_size)
--		dev_err(dev->mt76.dev, "ei: %d size: %d", ei, stats_size);
-+	if (ei != MT7915_SSTATS_LEN)
-+		dev_err(dev->mt76.dev, "ei: %d  MT7915_SSTATS_LEN: %d",
-+			ei, (int)MT7915_SSTATS_LEN);
- }
- 
- static void
-diff --git a/usb.c b/usb.c
-index dc690d1..058f2d1 100644
---- a/usb.c
-+++ b/usb.c
-@@ -319,27 +319,29 @@ mt76u_set_endpoints(struct usb_interface *intf,
- 
- static int
- mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76_queue *q, struct urb *urb,
--		 int nsgs)
-+		 int nsgs, gfp_t gfp)
- {
- 	int i;
- 
- 	for (i = 0; i < nsgs; i++) {
-+		struct page *page;
- 		void *data;
- 		int offset;
- 
--		data = mt76_get_page_pool_buf(q, &offset, q->buf_size);
-+		data = page_frag_alloc(&q->rx_page, q->buf_size, gfp);
- 		if (!data)
- 			break;
- 
--		sg_set_page(&urb->sg[i], virt_to_head_page(data), q->buf_size,
--			    offset);
-+		page = virt_to_head_page(data);
-+		offset = data - page_address(page);
-+		sg_set_page(&urb->sg[i], page, q->buf_size, offset);
- 	}
- 
- 	if (i < nsgs) {
- 		int j;
- 
- 		for (j = nsgs; j < urb->num_sgs; j++)
--			mt76_put_page_pool_buf(sg_virt(&urb->sg[j]), false);
-+			skb_free_frag(sg_virt(&urb->sg[j]));
- 		urb->num_sgs = i;
- 	}
- 
-@@ -352,16 +354,15 @@ mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76_queue *q, struct urb *urb,
- 
- static int
- mt76u_refill_rx(struct mt76_dev *dev, struct mt76_queue *q,
--		struct urb *urb, int nsgs)
-+		struct urb *urb, int nsgs, gfp_t gfp)
- {
- 	enum mt76_rxq_id qid = q - &dev->q_rx[MT_RXQ_MAIN];
--	int offset;
- 
- 	if (qid == MT_RXQ_MAIN && dev->usb.sg_en)
--		return mt76u_fill_rx_sg(dev, q, urb, nsgs);
-+		return mt76u_fill_rx_sg(dev, q, urb, nsgs, gfp);
- 
- 	urb->transfer_buffer_length = q->buf_size;
--	urb->transfer_buffer = mt76_get_page_pool_buf(q, &offset, q->buf_size);
-+	urb->transfer_buffer = page_frag_alloc(&q->rx_page, q->buf_size, gfp);
- 
- 	return urb->transfer_buffer ? 0 : -ENOMEM;
- }
-@@ -399,7 +400,7 @@ mt76u_rx_urb_alloc(struct mt76_dev *dev, struct mt76_queue *q,
- 	if (err)
- 		return err;
- 
--	return mt76u_refill_rx(dev, q, e->urb, sg_size);
-+	return mt76u_refill_rx(dev, q, e->urb, sg_size, GFP_KERNEL);
- }
- 
- static void mt76u_urb_free(struct urb *urb)
-@@ -407,10 +408,10 @@ static void mt76u_urb_free(struct urb *urb)
- 	int i;
- 
- 	for (i = 0; i < urb->num_sgs; i++)
--		mt76_put_page_pool_buf(sg_virt(&urb->sg[i]), false);
-+		skb_free_frag(sg_virt(&urb->sg[i]));
- 
- 	if (urb->transfer_buffer)
--		mt76_put_page_pool_buf(urb->transfer_buffer, false);
-+		skb_free_frag(urb->transfer_buffer);
- 
- 	usb_free_urb(urb);
- }
-@@ -546,8 +547,6 @@ mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb,
- 		len -= data_len;
- 		nsgs++;
- 	}
--
--	skb_mark_for_recycle(skb);
- 	dev->drv->rx_skb(dev, MT_RXQ_MAIN, skb, NULL);
- 
- 	return nsgs;
-@@ -613,7 +612,7 @@ mt76u_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
- 
- 		count = mt76u_process_rx_entry(dev, urb, q->buf_size);
- 		if (count > 0) {
--			err = mt76u_refill_rx(dev, q, urb, count);
-+			err = mt76u_refill_rx(dev, q, urb, count, GFP_ATOMIC);
- 			if (err < 0)
- 				break;
- 		}
-@@ -664,10 +663,6 @@ mt76u_alloc_rx_queue(struct mt76_dev *dev, enum mt76_rxq_id qid)
- 	struct mt76_queue *q = &dev->q_rx[qid];
- 	int i, err;
- 
--	err = mt76_create_page_pool(dev, q);
--	if (err)
--		return err;
--
- 	spin_lock_init(&q->lock);
- 	q->entry = devm_kcalloc(dev->dev,
- 				MT_NUM_RX_ENTRIES, sizeof(*q->entry),
-@@ -696,6 +691,7 @@ EXPORT_SYMBOL_GPL(mt76u_alloc_mcu_queue);
- static void
- mt76u_free_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
- {
-+	struct page *page;
- 	int i;
- 
- 	for (i = 0; i < q->ndesc; i++) {
-@@ -705,8 +701,13 @@ mt76u_free_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
- 		mt76u_urb_free(q->entry[i].urb);
- 		q->entry[i].urb = NULL;
- 	}
--	page_pool_destroy(q->page_pool);
--	q->page_pool = NULL;
-+
-+	if (!q->rx_page.va)
-+		return;
-+
-+	page = virt_to_page(q->rx_page.va);
-+	__page_frag_cache_drain(page, q->rx_page.pagecnt_bias);
-+	memset(&q->rx_page, 0, sizeof(q->rx_page));
- }
- 
- static void mt76u_free_rx(struct mt76_dev *dev)
-diff --git a/wed.c b/wed.c
-index f89e453..8eca4d8 100644
---- a/wed.c
-+++ b/wed.c
-@@ -9,8 +9,12 @@
- void mt76_wed_release_rx_buf(struct mtk_wed_device *wed)
- {
- 	struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
-+	u32 length;
- 	int i;
- 
-+	length = SKB_DATA_ALIGN(NET_SKB_PAD + wed->wlan.rx_size +
-+				sizeof(struct skb_shared_info));
-+
- 	for (i = 0; i < dev->rx_token_size; i++) {
- 		struct mt76_txwi_cache *t;
- 
-@@ -18,7 +22,9 @@ void mt76_wed_release_rx_buf(struct mtk_wed_device *wed)
- 		if (!t || !t->ptr)
- 			continue;
- 
--		mt76_put_page_pool_buf(t->ptr, false);
-+		dma_unmap_single(dev->dma_dev, t->dma_addr,
-+				 wed->wlan.rx_size, DMA_FROM_DEVICE);
-+		__free_pages(virt_to_page(t->ptr), get_order(length));
- 		t->ptr = NULL;
- 
- 		mt76_put_rxwi(dev, t);
-@@ -33,33 +39,45 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
- {
- 	struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
- 	struct mtk_wed_bm_desc *desc = wed->rx_buf_ring.desc;
--	struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
--	int i, len = SKB_WITH_OVERHEAD(q->buf_size);
--	struct mt76_txwi_cache *t = NULL;
-+	u32 length;
-+	int i;
-+
-+	length = SKB_DATA_ALIGN(NET_SKB_PAD + wed->wlan.rx_size +
-+				sizeof(struct skb_shared_info));
- 
- 	for (i = 0; i < size; i++) {
--		enum dma_data_direction dir;
-+		struct mt76_txwi_cache *t = mt76_get_rxwi(dev);
- 		dma_addr_t addr;
--		u32 offset;
-+		struct page *page;
- 		int token;
--		void *buf;
-+		void *ptr;
- 
--		t = mt76_get_rxwi(dev);
- 		if (!t)
- 			goto unmap;
- 
--		buf = mt76_get_page_pool_buf(q, &offset, q->buf_size);
--		if (!buf)
-+		page = __dev_alloc_pages(GFP_KERNEL, get_order(length));
-+		if (!page) {
-+			mt76_put_rxwi(dev, t);
- 			goto unmap;
-+		}
- 
--		addr = page_pool_get_dma_addr(virt_to_head_page(buf)) + offset;
--		dir = page_pool_get_dma_dir(q->page_pool);
--		dma_sync_single_for_device(dev->dma_dev, addr, len, dir);
-+		addr = dma_map_single(dev->dma_dev, ptr,
-+					  wed->wlan.rx_size,
-+					  DMA_TO_DEVICE);
-+
-+		if (unlikely(dma_mapping_error(dev->dev, addr))) {
-+			skb_free_frag(ptr);
-+			mt76_put_rxwi(dev, t);
-+			goto unmap;
-+		}
- 
- 		desc->buf0 = cpu_to_le32(addr);
--		token = mt76_rx_token_consume(dev, buf, t, addr);
-+		token = mt76_rx_token_consume(dev, ptr, t, addr);
- 		if (token < 0) {
--			mt76_put_page_pool_buf(buf, false);
-+			dma_unmap_single(dev->dma_dev, addr,
-+					 wed->wlan.rx_size, DMA_TO_DEVICE);
-+			__free_pages(page, get_order(length));
-+			mt76_put_rxwi(dev, t);
- 			goto unmap;
- 		}
- 
-@@ -74,8 +92,6 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
- 	return 0;
- 
- unmap:
--	if (t)
--		mt76_put_rxwi(dev, t);
- 	mt76_wed_release_rx_buf(wed);
- 
- 	return -ENOMEM;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0057-mtk-mt76-mt7996-add-Eagle-2adie-TBTC-BE14000-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0057-mtk-mt76-mt7996-add-Eagle-2adie-TBTC-BE14000-support.patch
new file mode 100644
index 0000000..98b83ee
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0057-mtk-mt76-mt7996-add-Eagle-2adie-TBTC-BE14000-support.patch
@@ -0,0 +1,168 @@
+From 0a01643db7a700b8d78b3a1b33944bcf0fb4185d Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 5 Dec 2023 16:48:33 +0800
+Subject: [PATCH 057/199] mtk: mt76: mt7996: add Eagle 2adie TBTC (BE14000)
+ support
+
+Add fwdl/default eeprom load support for Eagle 2 adie TBTC
+
+Add Eagle 2adie TBTC efuse merge
+Add Eagle 2adie TBTC group prek size
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/eeprom.c | 11 +++++++++--
+ mt7996/eeprom.h | 12 ++++++++++++
+ mt7996/init.c   |  5 +++++
+ mt7996/mcu.c    |  5 +++++
+ mt7996/mt7996.h |  9 +++++++++
+ mt7996/regs.h   |  1 +
+ 6 files changed, 41 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 8cdd6d96..fb4d031f 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -158,6 +158,11 @@ const char *mt7996_eeprom_name(struct mt7996_dev *dev)
+ 	case 0x7990:
+ 		if (dev->chip_sku == MT7996_SKU_404)
+ 			return MT7996_EEPROM_DEFAULT_404;
++		else if (dev->chip_sku == MT7996_SKU_233 &&
++			 dev->fem_type == MT7996_FEM_INT)
++			return MT7996_EEPROM_DEFAULT_233_INT;
++		else if (dev->chip_sku == MT7996_SKU_233)
++			return MT7996_EEPROM_DEFAULT_233;
+ 
+ 		if (dev->fem_type == MT7996_FEM_INT)
+ 			return MT7996_EEPROM_DEFAULT_INT;
+@@ -450,6 +455,8 @@ static void mt7996_eeprom_init_precal(struct mt7996_dev *dev)
+ 	switch (mt76_chip(&dev->mt76)) {
+ 	case 0x7990:
+ 		dev->prek.rev = mt7996_prek_rev;
++		if (dev->chip_sku == MT7996_SKU_233)
++			dev->prek.rev = mt7996_prek_rev_233;
+ 		/* 5g & 6g bw 80 dpd channel list is not used */
+ 		dev->prek.dpd_ch_num[DPD_CH_NUM_BW320_6G] = ARRAY_SIZE(dpd_6g_ch_list_bw320);
+ 		break;
+@@ -553,7 +560,7 @@ static int mt7996_apply_cal_free_data(struct mt7996_dev *dev)
+ 	case 0x7990:
+ 		adie_base = adie_base_7996;
+ 		/* adie 0 */
+-		if (dev->fem_type == MT7996_FEM_INT)
++		if (dev->fem_type == MT7996_FEM_INT && dev->chip_sku != MT7996_SKU_233)
+ 			adie_id = ADIE_7975;
+ 		else
+ 			adie_id = ADIE_7976;
+@@ -561,7 +568,7 @@ static int mt7996_apply_cal_free_data(struct mt7996_dev *dev)
+ 		eep_offs[0] = eep_offs_list[adie_id];
+ 
+ 		/* adie 1 */
+-		if (dev->chip_sku != MT7996_SKU_404) {
++		if (dev->chip_sku == MT7996_SKU_444) {
+ 			adie_offs[1] = adie_offs_list[ADIE_7977];
+ 			eep_offs[1] = eep_offs_list[ADIE_7977];
+ 		}
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index fa9c31e7..43c9783c 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -70,6 +70,18 @@ static const u32 mt7996_prek_rev[] = {
+ 	[DPD_OTFG0_SIZE] =			2 * MT_EE_CAL_UNIT,
+ };
+ 
++static const u32 mt7996_prek_rev_233[] = {
++	[GROUP_SIZE_2G] =			4 * MT_EE_CAL_UNIT,
++	[GROUP_SIZE_5G] =			44 * MT_EE_CAL_UNIT,
++	[GROUP_SIZE_6G] =			100 * MT_EE_CAL_UNIT,
++	[ADCDCOC_SIZE_2G] =			4 * 4,
++	[ADCDCOC_SIZE_5G] =			4 * 4,
++	[ADCDCOC_SIZE_6G] =			4 * 5,
++	[DPD_LEGACY_SIZE] =			4 * MT_EE_CAL_UNIT,
++	[DPD_MEM_SIZE] =			13 * MT_EE_CAL_UNIT,
++	[DPD_OTFG0_SIZE] =			2 * MT_EE_CAL_UNIT,
++};
++
+ /* kite 2/5g config */
+ static const u32 mt7992_prek_rev[] = {
+ 	[GROUP_SIZE_2G] =			4 * MT_EE_CAL_UNIT,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 0201d9fc..342f15fb 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -918,6 +918,11 @@ int mt7996_get_chip_sku(struct mt7996_dev *dev)
+ 
+ 	switch (mt76_chip(&dev->mt76)) {
+ 	case 0x7990:
++		if (FIELD_GET(MT_PAD_GPIO_2ADIE_TBTC, val)) {
++			dev->chip_sku = MT7996_SKU_233;
++			break;
++		}
++
+ 		adie_comb = FIELD_GET(MT_PAD_GPIO_ADIE_COMB, val);
+ 		if (adie_comb <= 1)
+ 			dev->chip_sku = MT7996_SKU_444;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 1c98d179..9bef1274 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -23,6 +23,11 @@
+ 			_fw = MT7992_##name;			\
+ 		break;						\
+ 	case 0x7990:						\
++		if ((_dev)->chip_sku == MT7996_SKU_233)		\
++			_fw = MT7996_##name##_233;		\
++		else						\
++			_fw = MT7996_##name;			\
++		break;						\
+ 	default:						\
+ 		_fw = MT7996_##name;				\
+ 		break;						\
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 34886128..294818b4 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -35,6 +35,12 @@
+ #define MT7996_FIRMWARE_WM_TM		"mediatek/mt7996/mt7996_wm_tm.bin"
+ #define MT7996_ROM_PATCH		"mediatek/mt7996/mt7996_rom_patch.bin"
+ 
++#define MT7996_FIRMWARE_WA_233		"mediatek/mt7996/mt7996_wa_233.bin"
++#define MT7996_FIRMWARE_WM_233		"mediatek/mt7996/mt7996_wm_233.bin"
++#define MT7996_FIRMWARE_DSP_233		MT7996_FIRMWARE_DSP
++#define MT7996_FIRMWARE_WM_TM_233	"mediatek/mt7996/mt7996_wm_tm_233.bin"
++#define MT7996_ROM_PATCH_233		"mediatek/mt7996/mt7996_rom_patch_233.bin"
++
+ #define MT7992_FIRMWARE_WA		"mediatek/mt7996/mt7992_wa.bin"
+ #define MT7992_FIRMWARE_WM		"mediatek/mt7996/mt7992_wm.bin"
+ #define MT7992_FIRMWARE_DSP		"mediatek/mt7996/mt7992_dsp.bin"
+@@ -55,6 +61,8 @@
+ 
+ #define MT7996_EEPROM_DEFAULT		"mediatek/mt7996/mt7996_eeprom.bin"
+ #define MT7996_EEPROM_DEFAULT_INT	"mediatek/mt7996/mt7996_eeprom_2i5i6i.bin"
++#define MT7996_EEPROM_DEFAULT_233	"mediatek/mt7996/mt7996_eeprom_233.bin"
++#define MT7996_EEPROM_DEFAULT_233_INT	"mediatek/mt7996/mt7996_eeprom_233_2i5i6i.bin"
+ #define MT7996_EEPROM_DEFAULT_404	"mediatek/mt7996/mt7996_eeprom_dual_404.bin"
+ #define MT7992_EEPROM_DEFAULT		"mediatek/mt7996/mt7992_eeprom_2e5e.bin"
+ #define MT7992_EEPROM_DEFAULT_INT	"mediatek/mt7996/mt7992_eeprom_2i5i.bin"
+@@ -122,6 +130,7 @@ enum mt7996_fem_type {
+ enum mt7996_sku_type {
+ 	MT7996_SKU_404,
+ 	MT7996_SKU_444,
++	MT7996_SKU_233,
+ };
+ 
+ enum mt7992_sku_type {
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index 263737c5..91159c63 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -666,6 +666,7 @@ enum offs_rev {
+ 
+ #define MT_PAD_GPIO				0x700056f0
+ #define MT_PAD_GPIO_ADIE_COMB			GENMASK(16, 15)
++#define MT_PAD_GPIO_2ADIE_TBTC			BIT(19)
+ #define MT_PAD_GPIO_ADIE_COMB_7992		GENMASK(17, 16)
+ #define MT_PAD_GPIO_ADIE_NUM_7992		BIT(15)
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0057-mtk-wifi-mt76-rework-wed-rx-flow.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0057-mtk-wifi-mt76-rework-wed-rx-flow.patch
deleted file mode 100644
index b8f0a0d..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0057-mtk-wifi-mt76-rework-wed-rx-flow.patch
+++ /dev/null
@@ -1,541 +0,0 @@
-From 9874e44657b18d1c2c225506ce9f7a985fd9b94a Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Mon, 6 Feb 2023 13:37:23 +0800
-Subject: [PATCH 057/116] mtk: wifi: mt76: rework wed rx flow
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
----
- dma.c           | 125 +++++++++++++++++++++++++++++++-----------------
- mac80211.c      |   2 +-
- mt76.h          |  25 ++++++----
- mt7915/mmio.c   |   3 +-
- mt7915/mt7915.h |   1 +
- tx.c            |  16 +++----
- wed.c           |  57 ++++++++++++++--------
- 7 files changed, 144 insertions(+), 85 deletions(-)
-
-diff --git a/dma.c b/dma.c
-index 33a84f5..c54187b 100644
---- a/dma.c
-+++ b/dma.c
-@@ -64,17 +64,17 @@ mt76_alloc_txwi(struct mt76_dev *dev)
- 	return t;
- }
- 
--static struct mt76_txwi_cache *
-+static struct mt76_rxwi_cache *
- mt76_alloc_rxwi(struct mt76_dev *dev)
- {
--	struct mt76_txwi_cache *t;
-+	struct mt76_rxwi_cache *r;
- 
--	t = kzalloc(L1_CACHE_ALIGN(sizeof(*t)), GFP_ATOMIC);
--	if (!t)
-+	r = kzalloc(L1_CACHE_ALIGN(sizeof(*r)), GFP_ATOMIC);
-+	if (!r)
- 		return NULL;
- 
--	t->ptr = NULL;
--	return t;
-+	r->ptr = NULL;
-+	return r;
- }
- 
- static struct mt76_txwi_cache *
-@@ -93,20 +93,20 @@ __mt76_get_txwi(struct mt76_dev *dev)
- 	return t;
- }
- 
--static struct mt76_txwi_cache *
-+static struct mt76_rxwi_cache *
- __mt76_get_rxwi(struct mt76_dev *dev)
- {
--	struct mt76_txwi_cache *t = NULL;
-+	struct mt76_rxwi_cache *r = NULL;
- 
--	spin_lock_bh(&dev->wed_lock);
-+	spin_lock_bh(&dev->lock);
- 	if (!list_empty(&dev->rxwi_cache)) {
--		t = list_first_entry(&dev->rxwi_cache, struct mt76_txwi_cache,
-+		r = list_first_entry(&dev->rxwi_cache, struct mt76_rxwi_cache,
- 				     list);
--		list_del(&t->list);
-+		list_del(&r->list);
- 	}
--	spin_unlock_bh(&dev->wed_lock);
-+	spin_unlock_bh(&dev->lock);
- 
--	return t;
-+	return r;
- }
- 
- static struct mt76_txwi_cache *
-@@ -120,13 +120,13 @@ mt76_get_txwi(struct mt76_dev *dev)
- 	return mt76_alloc_txwi(dev);
- }
- 
--struct mt76_txwi_cache *
-+struct mt76_rxwi_cache *
- mt76_get_rxwi(struct mt76_dev *dev)
- {
--	struct mt76_txwi_cache *t = __mt76_get_rxwi(dev);
-+	struct mt76_rxwi_cache *r = __mt76_get_rxwi(dev);
- 
--	if (t)
--		return t;
-+	if (r)
-+		return r;
- 
- 	return mt76_alloc_rxwi(dev);
- }
-@@ -145,14 +145,14 @@ mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t)
- EXPORT_SYMBOL_GPL(mt76_put_txwi);
- 
- void
--mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t)
-+mt76_put_rxwi(struct mt76_dev *dev, struct mt76_rxwi_cache *r)
- {
--	if (!t)
-+	if (!r)
- 		return;
- 
--	spin_lock_bh(&dev->wed_lock);
--	list_add(&t->list, &dev->rxwi_cache);
--	spin_unlock_bh(&dev->wed_lock);
-+	spin_lock_bh(&dev->lock);
-+	list_add(&r->list, &dev->rxwi_cache);
-+	spin_unlock_bh(&dev->lock);
- }
- EXPORT_SYMBOL_GPL(mt76_put_rxwi);
- 
-@@ -173,13 +173,13 @@ mt76_free_pending_txwi(struct mt76_dev *dev)
- void
- mt76_free_pending_rxwi(struct mt76_dev *dev)
- {
--	struct mt76_txwi_cache *t;
-+	struct mt76_rxwi_cache *r;
- 
- 	local_bh_disable();
--	while ((t = __mt76_get_rxwi(dev)) != NULL) {
--		if (t->ptr)
--			skb_free_frag(t->ptr);
--		kfree(t);
-+	while ((r = __mt76_get_rxwi(dev)) != NULL) {
-+		if (r->ptr)
-+			skb_free_frag(r->ptr);
-+		kfree(r);
- 	}
- 	local_bh_enable();
- }
-@@ -225,10 +225,10 @@ void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q)
- 
- static int
- mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
--		    struct mt76_queue_buf *buf, void *data)
-+		    struct mt76_queue_buf *buf, void *data,
-+		    struct mt76_rxwi_cache *rxwi)
- {
- 	struct mt76_queue_entry *entry = &q->entry[q->head];
--	struct mt76_txwi_cache *txwi = NULL;
- 	struct mt76_desc *desc;
- 	int idx = q->head;
- 	u32 buf1 = 0, ctrl;
-@@ -249,13 +249,15 @@ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
- #endif
- 
- 	if (mt76_queue_is_wed_rx(q)) {
--		txwi = mt76_get_rxwi(dev);
--		if (!txwi)
--			return -ENOMEM;
-+		if (!rxwi) {
-+			rxwi = mt76_get_rxwi(dev);
-+			if (!rxwi)
-+				return -ENOMEM;
-+		}
- 
--		rx_token = mt76_rx_token_consume(dev, data, txwi, buf->addr);
-+		rx_token = mt76_rx_token_consume(dev, data, rxwi, buf->addr);
- 		if (rx_token < 0) {
--			mt76_put_rxwi(dev, txwi);
-+			mt76_put_rxwi(dev, rxwi);
- 			return -ENOMEM;
- 		}
- 
-@@ -271,7 +273,7 @@ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
- done:
- 	entry->dma_addr[0] = buf->addr;
- 	entry->dma_len[0] = buf->len;
--	entry->txwi = txwi;
-+	entry->rxwi = rxwi;
- 	entry->buf = data;
- 	entry->wcid = 0xffff;
- 	entry->skip_buf1 = true;
-@@ -420,7 +422,7 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush)
- 
- static void *
- mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
--		 int *len, u32 *info, bool *more, bool *drop)
-+		 int *len, u32 *info, bool *more, bool *drop, bool flush)
- {
- 	struct mt76_queue_entry *e = &q->entry[idx];
- 	struct mt76_desc *desc = &q->desc[idx];
-@@ -445,20 +447,53 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
- 
- 	if (mt76_queue_is_wed_rx(q)) {
- 		u32 token = FIELD_GET(MT_DMA_CTL_TOKEN, buf1);
--		struct mt76_txwi_cache *t = mt76_rx_token_release(dev, token);
-+		struct mt76_rxwi_cache *r = mt76_rx_token_release(dev, token);
- 
--		if (!t)
-+		if (!r)
- 			return NULL;
- 
--		dma_unmap_single(dev->dma_dev, t->dma_addr,
-+		dma_unmap_single(dev->dma_dev, r->dma_addr,
- 				 SKB_WITH_OVERHEAD(q->buf_size),
- 				 DMA_FROM_DEVICE);
- 
--		buf = t->ptr;
--		t->dma_addr = 0;
--		t->ptr = NULL;
-+		if (flush) {
-+			buf = r->ptr;
-+			r->dma_addr = 0;
-+			r->ptr = NULL;
-+
-+			mt76_put_rxwi(dev, r);
-+		} else {
-+			struct mt76_queue_buf qbuf;
-+
-+			buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
-+			if (!buf)
-+				return NULL;
-+
-+			memcpy(buf, r->ptr, SKB_WITH_OVERHEAD(q->buf_size));
-+
-+			r->dma_addr = dma_map_single(dev->dma_dev, r->ptr,
-+						     SKB_WITH_OVERHEAD(q->buf_size),
-+						     DMA_FROM_DEVICE);
-+			if (unlikely(dma_mapping_error(dev->dma_dev, r->dma_addr))) {
-+				skb_free_frag(r->ptr);
-+				mt76_put_rxwi(dev, r);
-+				return NULL;
-+			}
-+
-+			qbuf.addr = r->dma_addr;
-+			qbuf.len = SKB_WITH_OVERHEAD(q->buf_size);
-+			qbuf.skip_unmap = false;
-+
-+			if (mt76_dma_add_rx_buf(dev, q, &qbuf, r->ptr, r) < 0) {
-+				dma_unmap_single(dev->dma_dev, r->dma_addr,
-+						 SKB_WITH_OVERHEAD(q->buf_size),
-+						 DMA_FROM_DEVICE);
-+				skb_free_frag(r->ptr);
-+				mt76_put_rxwi(dev, r);
-+				return NULL;
-+			}
-+		}
- 
--		mt76_put_rxwi(dev, t);
- 		if (drop)
- 			*drop |= !!(buf1 & MT_DMA_CTL_WO_DROP);
- 	} else {
-@@ -495,7 +530,7 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
- 	q->tail = (q->tail + 1) % q->ndesc;
- 	q->queued--;
- 
--	return mt76_dma_get_buf(dev, q, idx, len, info, more, drop);
-+	return mt76_dma_get_buf(dev, q, idx, len, info, more, drop, flush);
- }
- 
- static int
-@@ -667,7 +702,7 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
- done:
- 		qbuf.len = len - offset;
- 		qbuf.skip_unmap = false;
--		if (mt76_dma_add_rx_buf(dev, q, &qbuf, buf) < 0) {
-+		if (mt76_dma_add_rx_buf(dev, q, &qbuf, buf, NULL) < 0) {
- 			dma_unmap_single(dev->dma_dev, addr, len,
- 					 DMA_FROM_DEVICE);
- 			skb_free_frag(buf);
-diff --git a/mac80211.c b/mac80211.c
-index a050d0a..3c2c2af 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -596,7 +596,6 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
- 	spin_lock_init(&dev->lock);
- 	spin_lock_init(&dev->cc_lock);
- 	spin_lock_init(&dev->status_lock);
--	spin_lock_init(&dev->wed_lock);
- 	mutex_init(&dev->mutex);
- 	init_waitqueue_head(&dev->tx_wait);
- 
-@@ -629,6 +628,7 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
- 	INIT_LIST_HEAD(&dev->txwi_cache);
- 	INIT_LIST_HEAD(&dev->rxwi_cache);
- 	dev->token_size = dev->drv->token_size;
-+	dev->rx_token_size = dev->drv->rx_token_size;
- 
- 	for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++)
- 		skb_queue_head_init(&dev->rx_skb[i]);
-diff --git a/mt76.h b/mt76.h
-index fd6eab5..2ad5a3f 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -205,6 +205,7 @@ struct mt76_queue_entry {
- 	};
- 	union {
- 		struct mt76_txwi_cache *txwi;
-+		struct mt76_rxwi_cache *rxwi;
- 		struct urb *urb;
- 		int buf_sz;
- 	};
-@@ -416,12 +417,16 @@ struct mt76_txwi_cache {
- 	struct list_head list;
- 	dma_addr_t dma_addr;
- 
--	union {
--		struct sk_buff *skb;
--		void *ptr;
--	};
--
- 	unsigned long jiffies;
-+
-+	struct sk_buff *skb;
-+};
-+
-+struct mt76_rxwi_cache {
-+	struct list_head list;
-+	dma_addr_t dma_addr;
-+
-+	void *ptr;
- };
- 
- struct mt76_rx_tid {
-@@ -509,6 +514,7 @@ struct mt76_driver_ops {
- 	u16 txwi_size;
- 	u16 token_size;
- 	u8 mcs_rates;
-+	u16 rx_token_size;
- 
- 	void (*update_survey)(struct mt76_phy *phy);
- 
-@@ -886,7 +892,6 @@ struct mt76_dev {
- 
- 	struct ieee80211_hw *hw;
- 
--	spinlock_t wed_lock;
- 	spinlock_t lock;
- 	spinlock_t cc_lock;
- 
-@@ -1568,8 +1573,8 @@ mt76_tx_status_get_hw(struct mt76_dev *dev, struct sk_buff *skb)
- }
- 
- void mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t);
--void mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t);
--struct mt76_txwi_cache *mt76_get_rxwi(struct mt76_dev *dev);
-+void mt76_put_rxwi(struct mt76_dev *dev, struct mt76_rxwi_cache *r);
-+struct mt76_rxwi_cache *mt76_get_rxwi(struct mt76_dev *dev);
- void mt76_free_pending_rxwi(struct mt76_dev *dev);
- void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
- 		      struct napi_struct *napi);
-@@ -1748,9 +1753,9 @@ struct mt76_txwi_cache *
- mt76_token_release(struct mt76_dev *dev, int token, bool *wake);
- int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi);
- void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked);
--struct mt76_txwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token);
-+struct mt76_rxwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token);
- int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
--			  struct mt76_txwi_cache *r, dma_addr_t phys);
-+			  struct mt76_rxwi_cache *r, dma_addr_t phys);
- 
- static inline void mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked)
- {
-diff --git a/mt7915/mmio.c b/mt7915/mmio.c
-index 6004d64..5938bd9 100644
---- a/mt7915/mmio.c
-+++ b/mt7915/mmio.c
-@@ -714,7 +714,7 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr,
- 	wed->wlan.reset = mt7915_mmio_wed_reset;
- 	wed->wlan.reset_complete = mt76_wed_reset_complete;
- 
--	dev->mt76.rx_token_size = wed->wlan.rx_npkt;
-+	dev->mt76.rx_token_size += wed->wlan.rx_npkt;
- 
- 	if (mtk_wed_device_attach(wed))
- 		return 0;
-@@ -921,6 +921,7 @@ struct mt7915_dev *mt7915_mmio_probe(struct device *pdev,
- 				SURVEY_INFO_TIME_RX |
- 				SURVEY_INFO_TIME_BSS_RX,
- 		.token_size = MT7915_TOKEN_SIZE,
-+		.rx_token_size = MT7915_RX_TOKEN_SIZE;
- 		.tx_prepare_skb = mt7915_tx_prepare_skb,
- 		.tx_complete_skb = mt76_connac_tx_complete_skb,
- 		.rx_skb = mt7915_queue_rx_skb,
-diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index a30d08e..f1e2c93 100644
---- a/mt7915/mt7915.h
-+++ b/mt7915/mt7915.h
-@@ -62,6 +62,7 @@
- #define MT7915_EEPROM_BLOCK_SIZE	16
- #define MT7915_HW_TOKEN_SIZE		4096
- #define MT7915_TOKEN_SIZE		8192
-+#define MT7915_RX_TOKEN_SIZE		4096
- 
- #define MT7915_CFEND_RATE_DEFAULT	0x49	/* OFDM 24M */
- #define MT7915_CFEND_RATE_11B		0x03	/* 11B LP, 11M */
-diff --git a/tx.c b/tx.c
-index ab42f69..46dae6e 100644
---- a/tx.c
-+++ b/tx.c
-@@ -851,16 +851,16 @@ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi)
- EXPORT_SYMBOL_GPL(mt76_token_consume);
- 
- int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
--			  struct mt76_txwi_cache *t, dma_addr_t phys)
-+			  struct mt76_rxwi_cache *r, dma_addr_t phys)
- {
- 	int token;
- 
- 	spin_lock_bh(&dev->rx_token_lock);
--	token = idr_alloc(&dev->rx_token, t, 0, dev->rx_token_size,
-+	token = idr_alloc(&dev->rx_token, r, 0, dev->rx_token_size,
- 			  GFP_ATOMIC);
- 	if (token >= 0) {
--		t->ptr = ptr;
--		t->dma_addr = phys;
-+		r->ptr = ptr;
-+		r->dma_addr = phys;
- 	}
- 	spin_unlock_bh(&dev->rx_token_lock);
- 
-@@ -897,15 +897,15 @@ mt76_token_release(struct mt76_dev *dev, int token, bool *wake)
- }
- EXPORT_SYMBOL_GPL(mt76_token_release);
- 
--struct mt76_txwi_cache *
-+struct mt76_rxwi_cache *
- mt76_rx_token_release(struct mt76_dev *dev, int token)
- {
--	struct mt76_txwi_cache *t;
-+	struct mt76_rxwi_cache *r;
- 
- 	spin_lock_bh(&dev->rx_token_lock);
--	t = idr_remove(&dev->rx_token, token);
-+	r = idr_remove(&dev->rx_token, token);
- 	spin_unlock_bh(&dev->rx_token_lock);
- 
--	return t;
-+	return r;
- }
- EXPORT_SYMBOL_GPL(mt76_rx_token_release);
-diff --git a/wed.c b/wed.c
-index 8eca4d8..0a0b5c0 100644
---- a/wed.c
-+++ b/wed.c
-@@ -9,28 +9,45 @@
- void mt76_wed_release_rx_buf(struct mtk_wed_device *wed)
- {
- 	struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
--	u32 length;
-+	struct page *page;
- 	int i;
- 
--	length = SKB_DATA_ALIGN(NET_SKB_PAD + wed->wlan.rx_size +
--				sizeof(struct skb_shared_info));
--
- 	for (i = 0; i < dev->rx_token_size; i++) {
--		struct mt76_txwi_cache *t;
-+		struct mt76_rxwi_cache *r;
- 
--		t = mt76_rx_token_release(dev, i);
--		if (!t || !t->ptr)
-+		r = mt76_rx_token_release(dev, i);
-+		if (!r || !r->ptr)
- 			continue;
- 
--		dma_unmap_single(dev->dma_dev, t->dma_addr,
-+		dma_unmap_single(dev->dma_dev, r->dma_addr,
- 				 wed->wlan.rx_size, DMA_FROM_DEVICE);
--		__free_pages(virt_to_page(t->ptr), get_order(length));
--		t->ptr = NULL;
-+		skb_free_frag(r->ptr);
-+		r->ptr = NULL;
- 
--		mt76_put_rxwi(dev, t);
-+		mt76_put_rxwi(dev, r);
- 	}
- 
- 	mt76_free_pending_rxwi(dev);
-+
-+	mt76_for_each_q_rx(dev, i) {
-+		struct mt76_queue *q = &dev->q_rx[i];
-+
-+		if (mt76_queue_is_wed_rx(q)) {
-+			if (!q->rx_page.va)
-+				continue;
-+
-+			page = virt_to_page(q->rx_page.va);
-+			__page_frag_cache_drain(page, q->rx_page.pagecnt_bias);
-+			memset(&q->rx_page, 0, sizeof(q->rx_page));
-+		}
-+	}
-+
-+	if (!wed->rx_buf_ring.rx_page.va)
-+		return;
-+
-+	page = virt_to_page(wed->rx_buf_ring.rx_page.va);
-+	__page_frag_cache_drain(page, wed->rx_buf_ring.rx_page.pagecnt_bias);
-+	memset(&wed->rx_buf_ring.rx_page, 0, sizeof(wed->rx_buf_ring.rx_page));
- }
- EXPORT_SYMBOL_GPL(mt76_wed_release_rx_buf);
- 
-@@ -46,18 +63,18 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
- 				sizeof(struct skb_shared_info));
- 
- 	for (i = 0; i < size; i++) {
--		struct mt76_txwi_cache *t = mt76_get_rxwi(dev);
-+		struct mt76_rxwi_cache *r = mt76_get_rxwi(dev);
- 		dma_addr_t addr;
- 		struct page *page;
- 		int token;
- 		void *ptr;
- 
--		if (!t)
-+		if (!r)
- 			goto unmap;
- 
--		page = __dev_alloc_pages(GFP_KERNEL, get_order(length));
--		if (!page) {
--			mt76_put_rxwi(dev, t);
-+		ptr = page_frag_alloc(&wed->rx_buf_ring.rx_page, length, GFP_ATOMIC);
-+		if (!ptr) {
-+			mt76_put_rxwi(dev, r);
- 			goto unmap;
- 		}
- 
-@@ -67,17 +84,17 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
- 
- 		if (unlikely(dma_mapping_error(dev->dev, addr))) {
- 			skb_free_frag(ptr);
--			mt76_put_rxwi(dev, t);
-+			mt76_put_rxwi(dev, r);
- 			goto unmap;
- 		}
- 
- 		desc->buf0 = cpu_to_le32(addr);
--		token = mt76_rx_token_consume(dev, ptr, t, addr);
-+		token = mt76_rx_token_consume(dev, ptr, r, addr);
- 		if (token < 0) {
- 			dma_unmap_single(dev->dma_dev, addr,
- 					 wed->wlan.rx_size, DMA_TO_DEVICE);
--			__free_pages(page, get_order(length));
--			mt76_put_rxwi(dev, t);
-+			skb_free_frag(ptr);
-+			mt76_put_rxwi(dev, r);
- 			goto unmap;
- 		}
- 
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0058-mtk-mt76-mt7996-add-background-radar-hw-cap-check.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0058-mtk-mt76-mt7996-add-background-radar-hw-cap-check.patch
new file mode 100644
index 0000000..be8bf16
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0058-mtk-mt76-mt7996-add-background-radar-hw-cap-check.patch
@@ -0,0 +1,80 @@
+From bfb5a104b7f24c396a444916d3755122b916153e Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 22 Dec 2023 17:27:10 +0800
+Subject: [PATCH 058/199] mtk: mt76: mt7996: add background radar hw cap check
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/debugfs.c |  5 +++++
+ mt7996/init.c    |  7 ++++---
+ mt7996/mt7996.h  | 20 ++++++++++++++++++++
+ 3 files changed, 29 insertions(+), 3 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 58879695..6d119467 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -262,6 +262,11 @@ mt7996_rdd_monitor(struct seq_file *s, void *data)
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
++	if (!mt7996_get_background_radar_cap(dev)) {
++		seq_puts(s, "no background radar capability\n");
++		goto out;
++	}
++
+ 	if (!cfg80211_chandef_valid(chandef)) {
+ 		ret = -EINVAL;
+ 		goto out;
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 342f15fb..ade07db7 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -406,9 +406,10 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ 
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION);
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_PROTECTION);
+-	if (!mdev->dev->of_node ||
+-	    !of_property_read_bool(mdev->dev->of_node,
+-				   "mediatek,disable-radar-background"))
++	if (mt7996_get_background_radar_cap(phy->dev) &&
++	    (!mdev->dev->of_node ||
++	     !of_property_read_bool(mdev->dev->of_node,
++				    "mediatek,disable-radar-background")))
+ 		wiphy_ext_feature_set(wiphy,
+ 				      NL80211_EXT_FEATURE_RADAR_BACKGROUND);
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 294818b4..784fb3db 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -606,6 +606,26 @@ mt7996_band_valid(struct mt7996_dev *dev, u8 band)
+ 	return band == MT_BAND0 || band == MT_BAND2;
+ }
+ 
++static inline bool
++mt7996_get_background_radar_cap(struct mt7996_dev *dev)
++{
++	switch (mt76_chip(&dev->mt76)) {
++	case 0x7990:
++		if (dev->chip_sku == MT7996_SKU_233)
++			return 0;
++		break;
++	case 0x7992:
++		if (dev->chip_sku == MT7992_SKU_23 ||
++		    dev->chip_sku == MT7992_SKU_24)
++			return 0;
++		break;
++	default:
++		break;
++	}
++
++	return 1;
++}
++
+ extern const struct ieee80211_ops mt7996_ops;
+ extern struct pci_driver mt7996_pci_driver;
+ extern struct pci_driver mt7996_hif_driver;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0058-mtk-wifi-mt76-wed-change-wed-token-init-size-to-adap.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0058-mtk-wifi-mt76-wed-change-wed-token-init-size-to-adap.patch
deleted file mode 100644
index 86ecb66..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0058-mtk-wifi-mt76-wed-change-wed-token-init-size-to-adap.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-From e5bfc7a288ec62f00e1bb7da03646268ba1b67d2 Mon Sep 17 00:00:00 2001
-From: "sujuan.chen" <sujuan.chen@mediatek.com>
-Date: Wed, 19 Apr 2023 17:13:41 +0800
-Subject: [PATCH 058/116] mtk: wifi: mt76: wed: change wed token init size to
- adapt wed3.0
-
-Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
----
- tx.c | 10 +++++++---
- 1 file changed, 7 insertions(+), 3 deletions(-)
-
-diff --git a/tx.c b/tx.c
-index 46dae6e..e279506 100644
---- a/tx.c
-+++ b/tx.c
-@@ -827,12 +827,16 @@ EXPORT_SYMBOL_GPL(__mt76_set_tx_blocked);
- 
- int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi)
- {
--	int token;
-+	int token, start = 0;
-+
-+	if (mtk_wed_device_active(&dev->mmio.wed))
-+		start = dev->mmio.wed.wlan.nbuf;
- 
- 	spin_lock_bh(&dev->token_lock);
- 
--	token = idr_alloc(&dev->token, *ptxwi, 0, dev->token_size, GFP_ATOMIC);
--	if (token >= 0)
-+	token = idr_alloc(&dev->token, *ptxwi, start, start + dev->token_size,
-+			  GFP_ATOMIC);
-+	if (token >= start)
- 		dev->token_count++;
- 
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0059-mtk-mt76-mt7996-add-fallback-in-case-of-missing-prec.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0059-mtk-mt76-mt7996-add-fallback-in-case-of-missing-prec.patch
new file mode 100644
index 0000000..d918499
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0059-mtk-mt76-mt7996-add-fallback-in-case-of-missing-prec.patch
@@ -0,0 +1,98 @@
+From 7235c5d6995ae047f9223fe077ae753fb439dfed Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 19 Mar 2024 17:33:49 +0800
+Subject: [PATCH 059/199] mtk: mt76: mt7996: add fallback in case of missing
+ precal data
+
+Align Wi-Fi 6 upstream changes
+https://github.com/openwrt/mt76/commit/2135e201e7a9339e018d4e2d4a33c73266e674d7
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/eeprom.c | 30 +++++++++++++++++++++---------
+ mt7996/init.c   |  2 +-
+ mt7996/main.c   |  2 +-
+ 3 files changed, 23 insertions(+), 11 deletions(-)
+
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index fb4d031f..dffcd09d 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -490,17 +490,31 @@ static int mt7996_eeprom_load_precal(struct mt7996_dev *dev)
+ 	size = MT_EE_CAL_GROUP_SIZE + MT_EE_CAL_DPD_SIZE;
+ 
+ 	dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
+-	if (!dev->cal)
+-		return -ENOMEM;
++	if (!dev->cal) {
++		ret = -ENOMEM;
++		goto fail;
++	}
+ 
+-	if (dev->bin_file_mode)
+-		return mt7996_eeprom_load_precal_binfile(dev, MT_EE_PRECAL, size);
++	if (dev->bin_file_mode) {
++		ret = mt7996_eeprom_load_precal_binfile(dev, MT_EE_PRECAL, size);
++		if (ret)
++			goto fail;
++	}
+ 
+ 	ret = mt76_get_of_data_from_mtd(mdev, dev->cal, offs, size);
+ 	if (!ret)
+-		return ret;
++		return 0;
++
++	ret = mt76_get_of_data_from_nvmem(mdev, dev->cal, "precal", size);
++	if (!ret)
++		return 0;
++
++fail:
++	dev_warn(dev->mt76.dev, "Failed to load precal data: %d\n", ret);
++	devm_kfree(dev->mt76.dev, dev->cal);
++	dev->cal = NULL;
+ 
+-	return mt76_get_of_data_from_nvmem(mdev, dev->cal, "precal", size);
++	return ret;
+ }
+ 
+ static int mt7996_apply_cal_free_data(struct mt7996_dev *dev)
+@@ -655,9 +669,7 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ 	if (ret)
+ 		return ret;
+ 
+-	ret = mt7996_eeprom_load_precal(dev);
+-	if (ret)
+-		return ret;
++	mt7996_eeprom_load_precal(dev);
+ 
+ 	ret = mt7996_apply_cal_free_data(dev);
+ 	if (ret)
+diff --git a/mt7996/init.c b/mt7996/init.c
+index ade07db7..8b642ecc 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -1011,7 +1011,7 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
+ 	if (ret < 0)
+ 		return ret;
+ 
+-	if (dev->flash_mode) {
++	if (dev->cal) {
+ 		ret = mt7996_mcu_apply_group_cal(dev);
+ 		if (ret)
+ 			return ret;
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 39318595..3bc9dc15 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -345,7 +345,7 @@ int mt7996_set_channel(struct mt7996_phy *phy)
+ 
+ 	mt76_set_channel(phy->mt76);
+ 
+-	if (dev->flash_mode) {
++	if (dev->cal) {
+ 		ret = mt7996_mcu_apply_tx_dpd(phy);
+ 		if (ret)
+ 			goto out;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0059-mtk-wifi-mt76-add-random-early-drop-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0059-mtk-wifi-mt76-add-random-early-drop-support.patch
deleted file mode 100644
index 342f2ce..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0059-mtk-wifi-mt76-add-random-early-drop-support.patch
+++ /dev/null
@@ -1,316 +0,0 @@
-From 4b0b49bb26433a9d8a62cd37c66fbba548d9aee7 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Wed, 19 Apr 2023 18:32:41 +0800
-Subject: [PATCH 059/116] mtk: wifi: mt76: add random early drop support
-
----
- mt7996/debugfs.c     |  1 +
- mt7996/mac.c         |  7 ++++
- mt7996/mcu.c         | 81 ++++++++++++++++++++++++++++++++++++++++++--
- mt7996/mcu.h         |  4 ++-
- mt7996/mt7996.h      |  5 ++-
- mt7996/mtk_debugfs.c | 23 +++++++++++++
- mt7996/mtk_mcu.c     | 26 ++++++++++++++
- mt7996/mtk_mcu.h     | 24 +++++++++++++
- 8 files changed, 167 insertions(+), 4 deletions(-)
-
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index 6c5fbc7..8d63937 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -634,6 +634,7 @@ mt7996_tx_stats_show(struct seq_file *file, void *data)
- 	seq_printf(file, "Tx attempts: %8u (MPDUs)\n", attempts);
- 	seq_printf(file, "Tx success: %8u (MPDUs)\n", success);
- 	seq_printf(file, "Tx PER: %u%%\n", per);
-+	seq_printf(file, "Tx RED drop: %8u\n", phy->red_drop);
- 
- 	mt7996_txbf_stat_read_phy(phy, file);
- 
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 727d1fb..78e35aa 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1176,6 +1176,13 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
- 
- 			wcid->stats.tx_retries += tx_retries;
- 			wcid->stats.tx_failed += tx_failed;
-+
-+			if (FIELD_GET(MT_TXFREE_INFO_STAT, info) == 2) {
-+				struct mt7996_phy *mphy =
-+					__mt7996_phy(dev, wcid->phy_idx);
-+
-+				mphy->red_drop++;
-+			}
- 			continue;
- 		}
- 
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 686cdda..f745139 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -3147,8 +3147,8 @@ int mt7996_mcu_init_firmware(struct mt7996_dev *dev)
- 	if (ret)
- 		return ret;
- 
--	return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
--				 MCU_WA_PARAM_RED, 0, 0);
-+	return mt7996_mcu_red_config(dev,
-+			mtk_wed_device_active(&dev->mt76.mmio.wed));
- }
- 
- int mt7996_mcu_init(struct mt7996_dev *dev)
-@@ -3180,6 +3180,83 @@ out:
- 	skb_queue_purge(&dev->mt76.mcu.res_q);
- }
- 
-+static int mt7996_mcu_wa_red_config(struct mt7996_dev *dev)
-+{
-+#define RED_TOKEN_SRC_CNT	4
-+#define RED_TOKEN_CONFIG	2
-+	struct {
-+		__le32 arg0;
-+		__le32 arg1;
-+		__le32 arg2;
-+
-+		u8 mode;
-+		u8 version;
-+		u8 _rsv[4];
-+		__le16 len;
-+
-+		__le16 tcp_offset;
-+		__le16 priority_offset;
-+		__le16 token_per_src[RED_TOKEN_SRC_CNT];
-+		__le16 token_thr_per_src[RED_TOKEN_SRC_CNT];
-+
-+		u8 _rsv2[604];
-+	} __packed req = {
-+		.arg0 = cpu_to_le32(MCU_WA_PARAM_RED_CONFIG),
-+
-+		.mode = RED_TOKEN_CONFIG,
-+		.len = cpu_to_le16(sizeof(req) - sizeof(__le32) * 3),
-+
-+		.tcp_offset = cpu_to_le16(200),
-+		.priority_offset = cpu_to_le16(255),
-+	};
-+	u8 i;
-+
-+	for (i = 0; i < RED_TOKEN_SRC_CNT; i++) {
-+		req.token_per_src[i] = cpu_to_le16(MT7996_TOKEN_SIZE);
-+		req.token_thr_per_src[i] = cpu_to_le16(MT7996_TOKEN_SIZE);
-+	}
-+
-+	if (!mtk_wed_device_active(&dev->mt76.mmio.wed))
-+		req.token_per_src[RED_TOKEN_SRC_CNT - 1] =
-+			cpu_to_le16(MT7996_TOKEN_SIZE - MT7996_HW_TOKEN_SIZE);
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WA_PARAM_CMD(SET),
-+				 &req, sizeof(req), false);
-+}
-+
-+int mt7996_mcu_red_config(struct mt7996_dev *dev, bool enable)
-+{
-+#define RED_DISABLE		0
-+#define RED_BY_WA_ENABLE	2
-+	struct {
-+		u8 __rsv1[4];
-+
-+		__le16 tag;
-+		__le16 len;
-+		u8 enable;
-+		u8 __rsv2[3];
-+	} __packed req = {
-+		.tag = cpu_to_le16(UNI_VOW_RED_ENABLE),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.enable = enable ? RED_BY_WA_ENABLE : RED_DISABLE,
-+	};
-+	int ret;
-+
-+	ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req,
-+				 sizeof(req), true);
-+
-+	if (ret)
-+		return ret;
-+
-+	ret = mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
-+				MCU_WA_PARAM_RED_EN, enable, 0);
-+
-+	if (ret || !enable)
-+		return ret;
-+
-+	return mt7996_mcu_wa_red_config(dev);
-+}
-+
- int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans)
- {
- 	struct {
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index f5e91a8..ca78cd5 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -346,8 +346,9 @@ enum {
- enum {
- 	MCU_WA_PARAM_PDMA_RX = 0x04,
- 	MCU_WA_PARAM_CPU_UTIL = 0x0b,
--	MCU_WA_PARAM_RED = 0x0e,
-+	MCU_WA_PARAM_RED_EN = 0x0e,
- 	MCU_WA_PARAM_HW_PATH_HIF_VER = 0x2f,
-+	MCU_WA_PARAM_RED_CONFIG = 0x40,
- };
- 
- enum mcu_mmps_mode {
-@@ -920,6 +921,7 @@ enum {
- 	UNI_VOW_DRR_CTRL,
- 	UNI_VOW_RX_AT_AIRTIME_EN = 0x0b,
- 	UNI_VOW_RX_AT_AIRTIME_CLR_EN = 0x0e,
-+	UNI_VOW_RED_ENABLE = 0x18,
- };
- 
- enum {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index c73701d..453d224 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -354,6 +354,7 @@ struct mt7996_phy {
- 	u16 punct_bitmap;
- 
- 	struct mt7996_scs_ctrl scs_ctrl;
-+	u32 red_drop;
- 
- 	bool sku_limit_en;
- 	bool sku_path_en;
-@@ -723,6 +724,7 @@ int mt7996_mcu_rf_regval(struct mt7996_dev *dev, u32 regidx, u32 *val, bool set)
- int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans);
- int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u16 val);
- int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3);
-+int mt7996_mcu_red_config(struct mt7996_dev *dev, bool enable);
- int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl);
- int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level);
- int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
-@@ -896,11 +898,12 @@ void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type);
- void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 ofdma_user_cnt);
- void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type);
- void mt7996_tm_update_channel(struct mt7996_phy *phy);
-+
-+int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val);
- #endif
- 
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
- int mt7996_dma_rro_init(struct mt7996_dev *dev);
- #endif /* CONFIG_NET_MEDIATEK_SOC_WED */
- 
--
- #endif
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index c1b665f..ff84d6f 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -3074,6 +3074,27 @@ static int mt7996_muru_prot_thr_set(void *data, u64 val)
- DEFINE_DEBUGFS_ATTRIBUTE(fops_muru_prot_thr, NULL,
- 			 mt7996_muru_prot_thr_set, "%lld\n");
- 
-+static int
-+mt7996_red_config_set(void *data, u64 val)
-+{
-+	struct mt7996_dev *dev = data;
-+
-+	return mt7996_mcu_red_config(dev, !!val);
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_red_config, NULL,
-+			 mt7996_red_config_set, "%lld\n");
-+
-+static int
-+mt7996_vow_drr_dbg(void *data, u64 val)
-+{
-+	struct mt7996_dev *dev = data;
-+
-+	return mt7996_mcu_set_vow_drr_dbg(dev, (u32)val);
-+}
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_vow_drr_dbg, NULL,
-+			 mt7996_vow_drr_dbg, "%lld\n");
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- 	struct mt7996_dev *dev = phy->dev;
-@@ -3151,6 +3172,8 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- 				    mt7996_wtbl_read);
- 
- 	debugfs_create_devm_seqfile(dev->mt76.dev, "token", dir, mt7996_token_read);
-+	debugfs_create_file("red", 0200, dir, dev, &fops_red_config);
-+	debugfs_create_file("vow_drr_dbg", 0200, dir, dev, &fops_vow_drr_dbg);
- 
- 	debugfs_create_u8("sku_disable", 0600, dir, &dev->dbg.sku_disable);
- 	debugfs_create_file("scs_enable", 0200, dir, phy, &fops_scs_enable);
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index a9a7db1..aed32e9 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -1316,4 +1316,30 @@ void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type)
- 			  sizeof(req), false);
- }
- 
-+int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val)
-+{
-+#define MT7996_VOW_DEBUG_MODE	0xe
-+	struct {
-+		u8 __rsv1[4];
-+
-+		__le16 tag;
-+		__le16 len;
-+		u8 __rsv2[4];
-+		__le32 action;
-+		__le32 val;
-+		u8 __rsv3[8];
-+	} __packed req = {
-+		.tag = cpu_to_le16(UNI_VOW_DRR_CTRL),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.action = cpu_to_le32(MT7996_VOW_DEBUG_MODE),
-+		.val = cpu_to_le32(val),
-+	};
-+
-+	if (val & ~VOW_DRR_DBG_FLAGS)
-+		return -EINVAL;
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req,
-+				 sizeof(req), true);
-+}
-+
- #endif
-diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
-index 58d61c5..2cffc89 100644
---- a/mt7996/mtk_mcu.h
-+++ b/mt7996/mtk_mcu.h
-@@ -1138,6 +1138,30 @@ enum muru_vendor_ctrl {
- 	MU_CTRL_DL_USER_CNT,
- 	MU_CTRL_UL_USER_CNT,
- };
-+
-+enum {
-+	VOW_DRR_DBG_DUMP_BMP = BIT(0),
-+	VOW_DRR_DBG_EST_AT_PRINT = BIT(1),
-+	VOW_DRR_DBG_ADJ_GLOBAL_THLD = BIT(21),
-+	VOW_DRR_DBG_PRN_LOUD = BIT(22),
-+	VOW_DRR_DBG_PRN_ADJ_STA = BIT(23),
-+	VOW_DRR_DBG_FIX_CR = GENMASK(27, 24),
-+	VOW_DRR_DBG_CLR_FIX_CR = BIT(28),
-+	VOW_DRR_DBG_DISABLE = BIT(29),
-+	VOW_DRR_DBG_DUMP_CR = BIT(30),
-+	VOW_DRR_DBG_PRN = BIT(31)
-+};
-+
-+#define VOW_DRR_DBG_FLAGS (VOW_DRR_DBG_DUMP_BMP |	\
-+			  VOW_DRR_DBG_EST_AT_PRINT |	\
-+			  VOW_DRR_DBG_ADJ_GLOBAL_THLD |	\
-+			  VOW_DRR_DBG_PRN_LOUD |	\
-+			  VOW_DRR_DBG_PRN_ADJ_STA |	\
-+			  VOW_DRR_DBG_FIX_CR |		\
-+			  VOW_DRR_DBG_CLR_FIX_CR |	\
-+			  VOW_DRR_DBG_DISABLE |		\
-+			  VOW_DRR_DBG_DUMP_CR |		\
-+			  VOW_DRR_DBG_PRN)
- #endif
- 
- #endif
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0060-mtk-mt76-mt7996-add-kite-part-number-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0060-mtk-mt76-mt7996-add-kite-part-number-support.patch
new file mode 100644
index 0000000..04ce43c
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0060-mtk-mt76-mt7996-add-kite-part-number-support.patch
@@ -0,0 +1,92 @@
+From 32201406775dc530ae7f62a0f0bdc0af09b69dd8 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 27 Mar 2024 17:50:16 +0800
+Subject: [PATCH 060/199] mtk: mt76: mt7996: add kite part number support
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/eeprom.c | 35 +++++++++++++++++++++++------------
+ 1 file changed, 23 insertions(+), 12 deletions(-)
+
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index dffcd09d..c4714982 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -319,26 +319,39 @@ out:
+ 	return ret;
+ }
+ 
+-static int mt7996_eeprom_parse_efuse_hw_cap(struct mt7996_dev *dev)
++static int mt7996_eeprom_parse_efuse_hw_cap(struct mt7996_phy *phy,
++					    u8 *path, u8 *rx_path, u8 *nss)
+ {
+ #define MODE_HE_ONLY		BIT(0)
++#define STREAM_MASK		GENMASK(2, 0)
++#define STREAM_OFFSET		1
++#define TX_PATH_OFFSET		10
++#define RX_PATH_OFFSET		19
+ #define WTBL_SIZE_GROUP		GENMASK(31, 28)
++#define GET_STREAM_CAP(offs)	({				\
++	typeof(offs) _offs = (offs);				\
++	((cap & (STREAM_MASK << _offs)) >> _offs);		\
++})
++	struct mt7996_dev *dev = phy->dev;
+ 	u32 cap = 0;
+ 	int ret;
++	u8 band_offs = phy->mt76->band_idx * hweight8(STREAM_MASK);
+ 
+ 	ret = mt7996_mcu_get_chip_config(dev, &cap);
+ 	if (ret)
+ 		return ret;
+ 
+-	cap = 0x4b249248;	/* internal hardcode */
++	dev->has_eht = true;
+ 	if (cap) {
+ 		dev->has_eht = !(cap & MODE_HE_ONLY);
+ 		dev->wtbl_size_group = u32_get_bits(cap, WTBL_SIZE_GROUP);
++		*nss = min_t(u8, *nss, GET_STREAM_CAP(STREAM_OFFSET + band_offs));
++		*path = min_t(u8, *path, GET_STREAM_CAP(TX_PATH_OFFSET + band_offs));
++		*rx_path = min_t(u8, *rx_path, GET_STREAM_CAP(RX_PATH_OFFSET + band_offs));
+ 	}
+ 
+-	if (dev->wtbl_size_group < 2 || dev->wtbl_size_group > 4 ||
+-	    is_mt7992(&dev->mt76))
+-		dev->wtbl_size_group = 2; /* set default */
++	if (dev->wtbl_size_group < 2 || dev->wtbl_size_group > 4)
++		dev->wtbl_size_group = is_mt7996(&dev->mt76) ? 4 : 2;	/* set default */
+ 
+ 	return 0;
+ }
+@@ -382,13 +395,15 @@ static int mt7996_eeprom_parse_band_config(struct mt7996_phy *phy)
+ 
+ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy)
+ {
++	struct mt76_phy *mphy = phy->mt76;
+ 	u8 path, rx_path, nss, band_idx = phy->mt76->band_idx;
+ 	u8 *eeprom = dev->mt76.eeprom.data;
+-	struct mt76_phy *mphy = phy->mt76;
+-	int max_path = 5, max_nss = 4;
+-	int ret;
++	int ret, max_path = 5, max_nss = 4;
+ 
+ 	mt7996_parse_eeprom_stream(eeprom, band_idx, &path, &rx_path, &nss);
++	ret = mt7996_eeprom_parse_efuse_hw_cap(phy, &path, &rx_path, &nss);
++	if (ret)
++		return ret;
+ 
+ 	if (!path || path > max_path)
+ 		path = max_path;
+@@ -408,10 +423,6 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy)
+ 		dev->chainshift[band_idx + 1] = dev->chainshift[band_idx] +
+ 						hweight16(mphy->chainmask);
+ 
+-	ret = mt7996_eeprom_parse_efuse_hw_cap(dev);
+-	if (ret)
+-		return ret;
+-
+ 	return mt7996_eeprom_parse_band_config(phy);
+ }
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0060-mtk-wifi-mt76-mt7996-reset-addr_elem-when-delete-ba.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0060-mtk-wifi-mt76-mt7996-reset-addr_elem-when-delete-ba.patch
deleted file mode 100644
index 96dff48..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0060-mtk-wifi-mt76-mt7996-reset-addr_elem-when-delete-ba.patch
+++ /dev/null
@@ -1,93 +0,0 @@
-From 7328175da3d5b1a42c26d1f7fd34fe6c278d22ca Mon Sep 17 00:00:00 2001
-From: "sujuan.chen" <sujuan.chen@mediatek.com>
-Date: Thu, 18 May 2023 15:01:47 +0800
-Subject: [PATCH 060/116] mtk: wifi: mt76: mt7996: reset addr_elem when delete
- ba
-
-The old addr element info may be used when the signature is not equel to
-0xff, and sta will find error SDP cause the SDP/SDL=0 issue.
-
-Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
-
-1. without this patch will delete wrong session id when delete ba.
-Due to fw change the cmd format.
-https://gerrit.mediatek.inc/c/neptune/firmware/bora/wifi/custom/+/7969193
-
-Signed-off-by: mtk27745 <rex.lu@mediatek.com>
----
- mt76.h       |  1 +
- mt7996/mcu.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 47 insertions(+)
-
-diff --git a/mt76.h b/mt76.h
-index 2ad5a3f..37ef803 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -444,6 +444,7 @@ struct mt76_rx_tid {
- 	u16 nframes;
- 
- 	u8 num;
-+	u16 session_id;
- 
- 	u8 started:1, stopped:1, timer_pending:1;
- 
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index ca78cd5..3c4ff7a 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -298,6 +298,52 @@ struct mt7996_mcu_thermal_notify {
- 	u8 __rsv2[4];
- } __packed;
- 
-+struct mt7996_mcu_rro_event {
-+	struct mt7996_mcu_rxd rxd;
-+
-+	u8 __rsv1[4];
-+
-+	__le16 tag;
-+	__le16 len;
-+} __packed;
-+
-+struct mt7996_mcu_rro_ba {
-+	__le16 tag;
-+	__le16 len;
-+
-+	__le16 wlan_id;
-+	u8 tid;
-+	u8 __rsv1;
-+	__le32 status;
-+	__le16 session_id;
-+	u8 __rsv2[2];
-+} __packed;
-+
-+struct mt7996_mcu_rro_ba_del_chk_done {
-+	__le16 tag;
-+	__le16 len;
-+
-+	__le16 session_id;
-+	__le16 mld_id;
-+	u8 tid;
-+	u8 __rsv[3];
-+} __packed;
-+
-+enum  {
-+	UNI_RRO_BA_SESSION_STATUS = 0,
-+	UNI_RRO_BA_SESSION_TBL	= 1,
-+	UNI_RRO_BA_SESSION_DEL_CHK_DONE = 2,
-+	UNI_RRO_BA_SESSION_MAX_NUM
-+};
-+
-+struct mt7996_mcu_rro_del_ba {
-+	struct mt7996_mcu_rro_event event;
-+
-+	u8  wlan_idx;
-+	u8  tid;
-+	u8 __rsv2[2];
-+};
-+
- enum mt7996_chan_mib_offs {
- 	UNI_MIB_OBSS_AIRTIME = 26,
- 	UNI_MIB_NON_WIFI_TIME = 27,
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0061-mtk-wifi-mt76-revert-page_poll-for-kernel-5.4.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0061-mtk-wifi-mt76-revert-page_poll-for-kernel-5.4.patch
new file mode 100644
index 0000000..c73d2e3
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0061-mtk-wifi-mt76-revert-page_poll-for-kernel-5.4.patch
@@ -0,0 +1,618 @@
+From 397ea73910dc301f6fcaa4e96ecb59108c79c126 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Mon, 6 Feb 2023 19:49:22 +0800
+Subject: [PATCH 061/199] mtk: wifi: mt76: revert page_poll for kernel 5.4
+
+This reverts commit e8c10835cf062c577ddf426913788c39d30b4bd7.
+
+---
+ dma.c         | 75 ++++++++++++++++++++++++++-------------------------
+ mac80211.c    | 56 --------------------------------------
+ mt76.h        | 22 +--------------
+ mt7915/main.c | 26 +++++++-----------
+ usb.c         | 43 ++++++++++++++---------------
+ wed.c         | 50 ++++++++++++++++++++++------------
+ 6 files changed, 104 insertions(+), 168 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index 66c000ef..33a84f5f 100644
+--- a/dma.c
++++ b/dma.c
+@@ -178,7 +178,7 @@ mt76_free_pending_rxwi(struct mt76_dev *dev)
+ 	local_bh_disable();
+ 	while ((t = __mt76_get_rxwi(dev)) != NULL) {
+ 		if (t->ptr)
+-			mt76_put_page_pool_buf(t->ptr, false);
++			skb_free_frag(t->ptr);
+ 		kfree(t);
+ 	}
+ 	local_bh_enable();
+@@ -450,9 +450,9 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ 		if (!t)
+ 			return NULL;
+ 
+-		dma_sync_single_for_cpu(dev->dma_dev, t->dma_addr,
+-				SKB_WITH_OVERHEAD(q->buf_size),
+-				page_pool_get_dma_dir(q->page_pool));
++		dma_unmap_single(dev->dma_dev, t->dma_addr,
++				 SKB_WITH_OVERHEAD(q->buf_size),
++				 DMA_FROM_DEVICE);
+ 
+ 		buf = t->ptr;
+ 		t->dma_addr = 0;
+@@ -462,9 +462,9 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ 		if (drop)
+ 			*drop |= !!(buf1 & MT_DMA_CTL_WO_DROP);
+ 	} else {
+-		dma_sync_single_for_cpu(dev->dma_dev, e->dma_addr[0],
+-				SKB_WITH_OVERHEAD(q->buf_size),
+-				page_pool_get_dma_dir(q->page_pool));
++		dma_unmap_single(dev->dma_dev, e->dma_addr[0],
++				 SKB_WITH_OVERHEAD(q->buf_size),
++				 DMA_FROM_DEVICE);
+ 	}
+ 
+ done:
+@@ -638,7 +638,8 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
+ 		     bool allow_direct)
+ {
+ 	int len = SKB_WITH_OVERHEAD(q->buf_size);
+-	int frames = 0;
++	int frames = 0, offset = q->buf_offset;
++	dma_addr_t addr;
+ 
+ 	if (!q->ndesc)
+ 		return 0;
+@@ -647,28 +648,29 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
+ 
+ 	while (q->queued < q->ndesc - 1) {
+ 		struct mt76_queue_buf qbuf = {};
+-		enum dma_data_direction dir;
+-		dma_addr_t addr;
+-		int offset;
+ 		void *buf = NULL;
+ 
+ 		if (mt76_queue_is_wed_rro_ind(q))
+ 			goto done;
+ 
+-		buf = mt76_get_page_pool_buf(q, &offset, q->buf_size);
++		buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
+ 		if (!buf)
+ 			break;
+ 
+-		addr = page_pool_get_dma_addr(virt_to_head_page(buf)) + offset;
+-		dir = page_pool_get_dma_dir(q->page_pool);
+-		dma_sync_single_for_device(dev->dma_dev, addr, len, dir);
++		addr = dma_map_single(dev->dma_dev, buf, len, DMA_FROM_DEVICE);
++		if (unlikely(dma_mapping_error(dev->dma_dev, addr))) {
++			skb_free_frag(buf);
++			break;
++		}
+ 
+-		qbuf.addr = addr + q->buf_offset;
++		qbuf.addr = addr + offset;
+ done:
+-		qbuf.len = len - q->buf_offset;
++		qbuf.len = len - offset;
+ 		qbuf.skip_unmap = false;
+ 		if (mt76_dma_add_rx_buf(dev, q, &qbuf, buf) < 0) {
+-			mt76_put_page_pool_buf(buf, allow_direct);
++			dma_unmap_single(dev->dma_dev, addr, len,
++					 DMA_FROM_DEVICE);
++			skb_free_frag(buf);
+ 			break;
+ 		}
+ 		frames++;
+@@ -722,10 +724,6 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
+ 	if (!q->entry)
+ 		return -ENOMEM;
+ 
+-	ret = mt76_create_page_pool(dev, q);
+-	if (ret)
+-		return ret;
+-
+ 	ret = mt76_wed_dma_setup(dev, q, false);
+ 	if (ret)
+ 		return ret;
+@@ -744,6 +742,7 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
+ static void
+ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
+ {
++	struct page *page;
+ 	void *buf;
+ 	bool more;
+ 
+@@ -759,7 +758,7 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
+ 			break;
+ 
+ 		if (!mt76_queue_is_wed_rro(q))
+-			mt76_put_page_pool_buf(buf, false);
++			skb_free_frag(buf);
+ 	} while (1);
+ 
+ 	spin_lock_bh(&q->lock);
+@@ -769,6 +768,16 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
+ 	}
+ 
+ 	spin_unlock_bh(&q->lock);
++
++	if (mt76_queue_is_wed_rx(q))
++		return;
++
++	if (!q->rx_page.va)
++		return;
++
++	page = virt_to_page(q->rx_page.va);
++	__page_frag_cache_drain(page, q->rx_page.pagecnt_bias);
++	memset(&q->rx_page, 0, sizeof(q->rx_page));
+ }
+ 
+ static void
+@@ -791,15 +800,10 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
+ 	/* reset WED rx queues */
+ 	mt76_wed_dma_setup(dev, q, true);
+ 
+-	if (mt76_queue_is_wed_tx_free(q))
+-		return;
+-
+-	if (mtk_wed_device_active(&dev->mmio.wed) &&
+-	    mt76_queue_is_wed_rro(q))
+-		return;
+-
+-	mt76_dma_sync_idx(dev, q);
+-	mt76_dma_rx_fill(dev, q, false);
++	if (!mt76_queue_is_wed_tx_free(q)) {
++		mt76_dma_sync_idx(dev, q);
++		mt76_dma_rx_fill(dev, q, false);
++	}
+ }
+ 
+ static void
+@@ -816,7 +820,7 @@ mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
+ 
+ 		skb_add_rx_frag(skb, nr_frags, page, offset, len, q->buf_size);
+ 	} else {
+-		mt76_put_page_pool_buf(data, allow_direct);
++		skb_free_frag(data);
+ 	}
+ 
+ 	if (more)
+@@ -891,7 +895,6 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+ 			goto free_frag;
+ 
+ 		skb_reserve(skb, q->buf_offset);
+-		skb_mark_for_recycle(skb);
+ 
+ 		*(u32 *)skb->cb = info;
+ 
+@@ -907,7 +910,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+ 		continue;
+ 
+ free_frag:
+-		mt76_put_page_pool_buf(data, allow_direct);
++		skb_free_frag(data);
+ 	}
+ 
+ 	mt76_dma_rx_fill(dev, q, true);
+@@ -1010,8 +1013,6 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
+ 
+ 		netif_napi_del(&dev->napi[i]);
+ 		mt76_dma_rx_cleanup(dev, q);
+-
+-		page_pool_destroy(q->page_pool);
+ 	}
+ 
+ 	if (mtk_wed_device_active(&dev->mmio.wed))
+diff --git a/mac80211.c b/mac80211.c
+index 6a5201f6..5f85bf1d 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -566,47 +566,6 @@ void mt76_unregister_phy(struct mt76_phy *phy)
+ }
+ EXPORT_SYMBOL_GPL(mt76_unregister_phy);
+ 
+-int mt76_create_page_pool(struct mt76_dev *dev, struct mt76_queue *q)
+-{
+-	struct page_pool_params pp_params = {
+-		.order = 0,
+-		.flags = PP_FLAG_PAGE_FRAG,
+-		.nid = NUMA_NO_NODE,
+-		.dev = dev->dma_dev,
+-	};
+-	int idx = q - dev->q_rx;
+-
+-	switch (idx) {
+-	case MT_RXQ_MAIN:
+-	case MT_RXQ_BAND1:
+-	case MT_RXQ_BAND2:
+-		pp_params.pool_size = 256;
+-		break;
+-	default:
+-		pp_params.pool_size = 16;
+-		break;
+-	}
+-
+-	if (mt76_is_mmio(dev)) {
+-		/* rely on page_pool for DMA mapping */
+-		pp_params.flags |= PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
+-		pp_params.dma_dir = DMA_FROM_DEVICE;
+-		pp_params.max_len = PAGE_SIZE;
+-		pp_params.offset = 0;
+-	}
+-
+-	q->page_pool = page_pool_create(&pp_params);
+-	if (IS_ERR(q->page_pool)) {
+-		int err = PTR_ERR(q->page_pool);
+-
+-		q->page_pool = NULL;
+-		return err;
+-	}
+-
+-	return 0;
+-}
+-EXPORT_SYMBOL_GPL(mt76_create_page_pool);
+-
+ struct mt76_dev *
+ mt76_alloc_device(struct device *pdev, unsigned int size,
+ 		  const struct ieee80211_ops *ops,
+@@ -1812,21 +1771,6 @@ void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi,
+ }
+ EXPORT_SYMBOL_GPL(mt76_ethtool_worker);
+ 
+-void mt76_ethtool_page_pool_stats(struct mt76_dev *dev, u64 *data, int *index)
+-{
+-#ifdef CONFIG_PAGE_POOL_STATS
+-	struct page_pool_stats stats = {};
+-	int i;
+-
+-	mt76_for_each_q_rx(dev, i)
+-		page_pool_get_stats(dev->q_rx[i].page_pool, &stats);
+-
+-	page_pool_ethtool_stats_get(data, &stats);
+-	*index += page_pool_ethtool_stats_get_count();
+-#endif
+-}
+-EXPORT_SYMBOL_GPL(mt76_ethtool_page_pool_stats);
+-
+ enum mt76_dfs_state mt76_phy_dfs_state(struct mt76_phy *phy)
+ {
+ 	struct ieee80211_hw *hw = phy->hw;
+diff --git a/mt76.h b/mt76.h
+index 36e8834e..fbcd5ea9 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -251,7 +251,7 @@ struct mt76_queue {
+ 
+ 	dma_addr_t desc_dma;
+ 	struct sk_buff *rx_head;
+-	struct page_pool *page_pool;
++	struct page_frag_cache rx_page;
+ };
+ 
+ struct mt76_mcu_ops {
+@@ -1600,7 +1600,6 @@ mt76u_bulk_msg(struct mt76_dev *dev, void *data, int len, int *actual_len,
+ 	return usb_bulk_msg(udev, pipe, data, len, actual_len, timeout);
+ }
+ 
+-void mt76_ethtool_page_pool_stats(struct mt76_dev *dev, u64 *data, int *index);
+ void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi,
+ 			 struct mt76_sta_stats *stats, bool eht);
+ int mt76_skb_adjust_pad(struct sk_buff *skb, int pad);
+@@ -1758,25 +1757,6 @@ void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked);
+ struct mt76_txwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token);
+ int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
+ 			  struct mt76_txwi_cache *r, dma_addr_t phys);
+-int mt76_create_page_pool(struct mt76_dev *dev, struct mt76_queue *q);
+-static inline void mt76_put_page_pool_buf(void *buf, bool allow_direct)
+-{
+-	struct page *page = virt_to_head_page(buf);
+-
+-	page_pool_put_full_page(page->pp, page, allow_direct);
+-}
+-
+-static inline void *
+-mt76_get_page_pool_buf(struct mt76_queue *q, u32 *offset, u32 size)
+-{
+-	struct page *page;
+-
+-	page = page_pool_dev_alloc_frag(q->page_pool, offset, size);
+-	if (!page)
+-		return NULL;
+-
+-	return page_address(page) + *offset;
+-}
+ 
+ static inline void mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked)
+ {
+diff --git a/mt7915/main.c b/mt7915/main.c
+index 2624edbb..1b9ef1de 100644
+--- a/mt7915/main.c
++++ b/mt7915/main.c
+@@ -1402,22 +1402,19 @@ void mt7915_get_et_strings(struct ieee80211_hw *hw,
+ 			   struct ieee80211_vif *vif,
+ 			   u32 sset, u8 *data)
+ {
+-	if (sset != ETH_SS_STATS)
+-		return;
+-
+-	memcpy(data, mt7915_gstrings_stats, sizeof(mt7915_gstrings_stats));
+-	data += sizeof(mt7915_gstrings_stats);
+-	page_pool_ethtool_stats_get_strings(data);
++	if (sset == ETH_SS_STATS)
++		memcpy(data, mt7915_gstrings_stats,
++		       sizeof(mt7915_gstrings_stats));
+ }
+ 
+ static
+ int mt7915_get_et_sset_count(struct ieee80211_hw *hw,
+ 			     struct ieee80211_vif *vif, int sset)
+ {
+-	if (sset != ETH_SS_STATS)
+-		return 0;
++	if (sset == ETH_SS_STATS)
++		return MT7915_SSTATS_LEN;
+ 
+-	return MT7915_SSTATS_LEN + page_pool_ethtool_stats_get_count();
++	return 0;
+ }
+ 
+ static void mt7915_ethtool_worker(void *wi_data, struct ieee80211_sta *sta)
+@@ -1445,7 +1442,7 @@ void mt7915_get_et_stats(struct ieee80211_hw *hw,
+ 		.idx = mvif->mt76.idx,
+ 	};
+ 	/* See mt7915_ampdu_stat_read_phy, etc */
+-	int i, ei = 0, stats_size;
++	int i, ei = 0;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+@@ -1557,12 +1554,9 @@ void mt7915_get_et_stats(struct ieee80211_hw *hw,
+ 		return;
+ 
+ 	ei += wi.worker_stat_count;
+-
+-	mt76_ethtool_page_pool_stats(&dev->mt76, &data[ei], &ei);
+-
+-	stats_size = MT7915_SSTATS_LEN + page_pool_ethtool_stats_get_count();
+-	if (ei != stats_size)
+-		dev_err(dev->mt76.dev, "ei: %d size: %d", ei, stats_size);
++	if (ei != MT7915_SSTATS_LEN)
++		dev_err(dev->mt76.dev, "ei: %d  MT7915_SSTATS_LEN: %d",
++			ei, (int)MT7915_SSTATS_LEN);
+ }
+ 
+ static void
+diff --git a/usb.c b/usb.c
+index 58ff0682..0ca3b069 100644
+--- a/usb.c
++++ b/usb.c
+@@ -318,27 +318,29 @@ mt76u_set_endpoints(struct usb_interface *intf,
+ 
+ static int
+ mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76_queue *q, struct urb *urb,
+-		 int nsgs)
++		 int nsgs, gfp_t gfp)
+ {
+ 	int i;
+ 
+ 	for (i = 0; i < nsgs; i++) {
++		struct page *page;
+ 		void *data;
+ 		int offset;
+ 
+-		data = mt76_get_page_pool_buf(q, &offset, q->buf_size);
++		data = page_frag_alloc(&q->rx_page, q->buf_size, gfp);
+ 		if (!data)
+ 			break;
+ 
+-		sg_set_page(&urb->sg[i], virt_to_head_page(data), q->buf_size,
+-			    offset);
++		page = virt_to_head_page(data);
++		offset = data - page_address(page);
++		sg_set_page(&urb->sg[i], page, q->buf_size, offset);
+ 	}
+ 
+ 	if (i < nsgs) {
+ 		int j;
+ 
+ 		for (j = nsgs; j < urb->num_sgs; j++)
+-			mt76_put_page_pool_buf(sg_virt(&urb->sg[j]), false);
++			skb_free_frag(sg_virt(&urb->sg[j]));
+ 		urb->num_sgs = i;
+ 	}
+ 
+@@ -351,16 +353,15 @@ mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76_queue *q, struct urb *urb,
+ 
+ static int
+ mt76u_refill_rx(struct mt76_dev *dev, struct mt76_queue *q,
+-		struct urb *urb, int nsgs)
++		struct urb *urb, int nsgs, gfp_t gfp)
+ {
+ 	enum mt76_rxq_id qid = q - &dev->q_rx[MT_RXQ_MAIN];
+-	int offset;
+ 
+ 	if (qid == MT_RXQ_MAIN && dev->usb.sg_en)
+-		return mt76u_fill_rx_sg(dev, q, urb, nsgs);
++		return mt76u_fill_rx_sg(dev, q, urb, nsgs, gfp);
+ 
+ 	urb->transfer_buffer_length = q->buf_size;
+-	urb->transfer_buffer = mt76_get_page_pool_buf(q, &offset, q->buf_size);
++	urb->transfer_buffer = page_frag_alloc(&q->rx_page, q->buf_size, gfp);
+ 
+ 	return urb->transfer_buffer ? 0 : -ENOMEM;
+ }
+@@ -398,7 +399,7 @@ mt76u_rx_urb_alloc(struct mt76_dev *dev, struct mt76_queue *q,
+ 	if (err)
+ 		return err;
+ 
+-	return mt76u_refill_rx(dev, q, e->urb, sg_size);
++	return mt76u_refill_rx(dev, q, e->urb, sg_size, GFP_KERNEL);
+ }
+ 
+ static void mt76u_urb_free(struct urb *urb)
+@@ -406,10 +407,10 @@ static void mt76u_urb_free(struct urb *urb)
+ 	int i;
+ 
+ 	for (i = 0; i < urb->num_sgs; i++)
+-		mt76_put_page_pool_buf(sg_virt(&urb->sg[i]), false);
++		skb_free_frag(sg_virt(&urb->sg[i]));
+ 
+ 	if (urb->transfer_buffer)
+-		mt76_put_page_pool_buf(urb->transfer_buffer, false);
++		skb_free_frag(urb->transfer_buffer);
+ 
+ 	usb_free_urb(urb);
+ }
+@@ -545,8 +546,6 @@ mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb,
+ 		len -= data_len;
+ 		nsgs++;
+ 	}
+-
+-	skb_mark_for_recycle(skb);
+ 	dev->drv->rx_skb(dev, MT_RXQ_MAIN, skb, NULL);
+ 
+ 	return nsgs;
+@@ -612,7 +611,7 @@ mt76u_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
+ 
+ 		count = mt76u_process_rx_entry(dev, urb, q->buf_size);
+ 		if (count > 0) {
+-			err = mt76u_refill_rx(dev, q, urb, count);
++			err = mt76u_refill_rx(dev, q, urb, count, GFP_ATOMIC);
+ 			if (err < 0)
+ 				break;
+ 		}
+@@ -663,10 +662,6 @@ mt76u_alloc_rx_queue(struct mt76_dev *dev, enum mt76_rxq_id qid)
+ 	struct mt76_queue *q = &dev->q_rx[qid];
+ 	int i, err;
+ 
+-	err = mt76_create_page_pool(dev, q);
+-	if (err)
+-		return err;
+-
+ 	spin_lock_init(&q->lock);
+ 	q->entry = devm_kcalloc(dev->dev,
+ 				MT_NUM_RX_ENTRIES, sizeof(*q->entry),
+@@ -695,6 +690,7 @@ EXPORT_SYMBOL_GPL(mt76u_alloc_mcu_queue);
+ static void
+ mt76u_free_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
+ {
++	struct page *page;
+ 	int i;
+ 
+ 	for (i = 0; i < q->ndesc; i++) {
+@@ -704,8 +700,13 @@ mt76u_free_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
+ 		mt76u_urb_free(q->entry[i].urb);
+ 		q->entry[i].urb = NULL;
+ 	}
+-	page_pool_destroy(q->page_pool);
+-	q->page_pool = NULL;
++
++	if (!q->rx_page.va)
++		return;
++
++	page = virt_to_page(q->rx_page.va);
++	__page_frag_cache_drain(page, q->rx_page.pagecnt_bias);
++	memset(&q->rx_page, 0, sizeof(q->rx_page));
+ }
+ 
+ static void mt76u_free_rx(struct mt76_dev *dev)
+diff --git a/wed.c b/wed.c
+index f89e4537..8eca4d81 100644
+--- a/wed.c
++++ b/wed.c
+@@ -9,8 +9,12 @@
+ void mt76_wed_release_rx_buf(struct mtk_wed_device *wed)
+ {
+ 	struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
++	u32 length;
+ 	int i;
+ 
++	length = SKB_DATA_ALIGN(NET_SKB_PAD + wed->wlan.rx_size +
++				sizeof(struct skb_shared_info));
++
+ 	for (i = 0; i < dev->rx_token_size; i++) {
+ 		struct mt76_txwi_cache *t;
+ 
+@@ -18,7 +22,9 @@ void mt76_wed_release_rx_buf(struct mtk_wed_device *wed)
+ 		if (!t || !t->ptr)
+ 			continue;
+ 
+-		mt76_put_page_pool_buf(t->ptr, false);
++		dma_unmap_single(dev->dma_dev, t->dma_addr,
++				 wed->wlan.rx_size, DMA_FROM_DEVICE);
++		__free_pages(virt_to_page(t->ptr), get_order(length));
+ 		t->ptr = NULL;
+ 
+ 		mt76_put_rxwi(dev, t);
+@@ -33,33 +39,45 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
+ {
+ 	struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
+ 	struct mtk_wed_bm_desc *desc = wed->rx_buf_ring.desc;
+-	struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
+-	int i, len = SKB_WITH_OVERHEAD(q->buf_size);
+-	struct mt76_txwi_cache *t = NULL;
++	u32 length;
++	int i;
++
++	length = SKB_DATA_ALIGN(NET_SKB_PAD + wed->wlan.rx_size +
++				sizeof(struct skb_shared_info));
+ 
+ 	for (i = 0; i < size; i++) {
+-		enum dma_data_direction dir;
++		struct mt76_txwi_cache *t = mt76_get_rxwi(dev);
+ 		dma_addr_t addr;
+-		u32 offset;
++		struct page *page;
+ 		int token;
+-		void *buf;
++		void *ptr;
+ 
+-		t = mt76_get_rxwi(dev);
+ 		if (!t)
+ 			goto unmap;
+ 
+-		buf = mt76_get_page_pool_buf(q, &offset, q->buf_size);
+-		if (!buf)
++		page = __dev_alloc_pages(GFP_KERNEL, get_order(length));
++		if (!page) {
++			mt76_put_rxwi(dev, t);
+ 			goto unmap;
++		}
+ 
+-		addr = page_pool_get_dma_addr(virt_to_head_page(buf)) + offset;
+-		dir = page_pool_get_dma_dir(q->page_pool);
+-		dma_sync_single_for_device(dev->dma_dev, addr, len, dir);
++		addr = dma_map_single(dev->dma_dev, ptr,
++					  wed->wlan.rx_size,
++					  DMA_TO_DEVICE);
++
++		if (unlikely(dma_mapping_error(dev->dev, addr))) {
++			skb_free_frag(ptr);
++			mt76_put_rxwi(dev, t);
++			goto unmap;
++		}
+ 
+ 		desc->buf0 = cpu_to_le32(addr);
+-		token = mt76_rx_token_consume(dev, buf, t, addr);
++		token = mt76_rx_token_consume(dev, ptr, t, addr);
+ 		if (token < 0) {
+-			mt76_put_page_pool_buf(buf, false);
++			dma_unmap_single(dev->dma_dev, addr,
++					 wed->wlan.rx_size, DMA_TO_DEVICE);
++			__free_pages(page, get_order(length));
++			mt76_put_rxwi(dev, t);
+ 			goto unmap;
+ 		}
+ 
+@@ -74,8 +92,6 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
+ 	return 0;
+ 
+ unmap:
+-	if (t)
+-		mt76_put_rxwi(dev, t);
+ 	mt76_wed_release_rx_buf(wed);
+ 
+ 	return -ENOMEM;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0061-mtk-wifi-mt76-wed-change-pcie0-R5-to-pcie1-to-get-6G.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0061-mtk-wifi-mt76-wed-change-pcie0-R5-to-pcie1-to-get-6G.patch
deleted file mode 100644
index f70c00d..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0061-mtk-wifi-mt76-wed-change-pcie0-R5-to-pcie1-to-get-6G.patch
+++ /dev/null
@@ -1,68 +0,0 @@
-From 51653708b92be73f1bd4a79ca11eaa3cb181ee4b Mon Sep 17 00:00:00 2001
-From: "sujuan.chen" <sujuan.chen@mediatek.com>
-Date: Fri, 6 Oct 2023 14:01:41 +0800
-Subject: [PATCH 061/116] mtk: wifi: mt76: wed: change pcie0 R5 to pcie1 to get
- 6G ICS
-
----
- mt7996/dma.c  | 4 ++++
- mt7996/init.c | 6 ++----
- mt7996/mmio.c | 5 ++++-
- 3 files changed, 10 insertions(+), 5 deletions(-)
-
-diff --git a/mt7996/dma.c b/mt7996/dma.c
-index 759a58e..5d85e9e 100644
---- a/mt7996/dma.c
-+++ b/mt7996/dma.c
-@@ -538,6 +538,10 @@ int mt7996_dma_init(struct mt7996_dev *dev)
- 	if (mt7996_band_valid(dev, MT_BAND2)) {
- 		/* rx data queue for mt7996 band2 */
- 		rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND2) + hif1_ofs;
-+		if (mtk_wed_device_active(wed_hif2) && mtk_wed_get_rx_capa(wed_hif2)) {
-+			dev->mt76.q_rx[MT_RXQ_BAND2].flags = MT_WED_Q_RX(0);
-+			dev->mt76.q_rx[MT_RXQ_BAND2].wed = wed_hif2;
-+		}
- 		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND2],
- 				       MT_RXQ_ID(MT_RXQ_BAND2),
- 				       MT7996_RX_RING_SIZE,
-diff --git a/mt7996/init.c b/mt7996/init.c
-index c10f667..de5122f 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -649,10 +649,8 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
- 		goto error;
- 
- 	if (wed == &dev->mt76.mmio.wed_hif2 && mtk_wed_device_active(wed)) {
--		u32 irq_mask = dev->mt76.mmio.irqmask | MT_INT_TX_DONE_BAND2;
--
--		mt76_wr(dev, MT_INT1_MASK_CSR, irq_mask);
--		mtk_wed_device_start(&dev->mt76.mmio.wed_hif2, irq_mask);
-+		mt76_wr(dev, MT_INT_PCIE1_MASK_CSR, MT_INT_TX_RX_DONE_EXT);
-+		mtk_wed_device_start(&dev->mt76.mmio.wed_hif2, MT_INT_TX_RX_DONE_EXT);
- 	}
- 
- 	return 0;
-diff --git a/mt7996/mmio.c b/mt7996/mmio.c
-index 8fe56ed..a082cca 100644
---- a/mt7996/mmio.c
-+++ b/mt7996/mmio.c
-@@ -527,12 +527,15 @@ static void mt7996_irq_tasklet(struct tasklet_struct *t)
- 					       dev->mt76.mmio.irqmask);
- 		if (intr1 & MT_INT_RX_TXFREE_EXT)
- 			napi_schedule(&dev->mt76.napi[MT_RXQ_TXFREE_BAND2]);
-+
-+		if (intr1 & MT_INT_RX_DONE_BAND2_EXT)
-+			napi_schedule(&dev->mt76.napi[MT_RXQ_BAND2]);
- 	}
- 
- 	if (mtk_wed_device_active(wed)) {
- 		mtk_wed_device_irq_set_mask(wed, 0);
- 		intr = mtk_wed_device_irq_get(wed, dev->mt76.mmio.irqmask);
--		intr |= (intr1 & ~MT_INT_RX_TXFREE_EXT);
-+		intr |= (intr1 & ~MT_INT_TX_RX_DONE_EXT);
- 	} else {
- 		mt76_wr(dev, MT_INT_MASK_CSR, 0);
- 		if (dev->hif2)
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0062-mtk-mt76-rework-wed-rx-flow.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0062-mtk-mt76-rework-wed-rx-flow.patch
new file mode 100644
index 0000000..a71e2b3
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0062-mtk-mt76-rework-wed-rx-flow.patch
@@ -0,0 +1,541 @@
+From 48f648333182ff895faa99cbad65d1f9baeb6f55 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Mon, 6 Feb 2023 13:37:23 +0800
+Subject: [PATCH 062/199] mtk: mt76: rework wed rx flow
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ dma.c           | 125 +++++++++++++++++++++++++++++++-----------------
+ mac80211.c      |   2 +-
+ mt76.h          |  25 ++++++----
+ mt7915/mmio.c   |   3 +-
+ mt7915/mt7915.h |   1 +
+ tx.c            |  16 +++----
+ wed.c           |  57 ++++++++++++++--------
+ 7 files changed, 144 insertions(+), 85 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index 33a84f5f..c54187bd 100644
+--- a/dma.c
++++ b/dma.c
+@@ -64,17 +64,17 @@ mt76_alloc_txwi(struct mt76_dev *dev)
+ 	return t;
+ }
+ 
+-static struct mt76_txwi_cache *
++static struct mt76_rxwi_cache *
+ mt76_alloc_rxwi(struct mt76_dev *dev)
+ {
+-	struct mt76_txwi_cache *t;
++	struct mt76_rxwi_cache *r;
+ 
+-	t = kzalloc(L1_CACHE_ALIGN(sizeof(*t)), GFP_ATOMIC);
+-	if (!t)
++	r = kzalloc(L1_CACHE_ALIGN(sizeof(*r)), GFP_ATOMIC);
++	if (!r)
+ 		return NULL;
+ 
+-	t->ptr = NULL;
+-	return t;
++	r->ptr = NULL;
++	return r;
+ }
+ 
+ static struct mt76_txwi_cache *
+@@ -93,20 +93,20 @@ __mt76_get_txwi(struct mt76_dev *dev)
+ 	return t;
+ }
+ 
+-static struct mt76_txwi_cache *
++static struct mt76_rxwi_cache *
+ __mt76_get_rxwi(struct mt76_dev *dev)
+ {
+-	struct mt76_txwi_cache *t = NULL;
++	struct mt76_rxwi_cache *r = NULL;
+ 
+-	spin_lock_bh(&dev->wed_lock);
++	spin_lock_bh(&dev->lock);
+ 	if (!list_empty(&dev->rxwi_cache)) {
+-		t = list_first_entry(&dev->rxwi_cache, struct mt76_txwi_cache,
++		r = list_first_entry(&dev->rxwi_cache, struct mt76_rxwi_cache,
+ 				     list);
+-		list_del(&t->list);
++		list_del(&r->list);
+ 	}
+-	spin_unlock_bh(&dev->wed_lock);
++	spin_unlock_bh(&dev->lock);
+ 
+-	return t;
++	return r;
+ }
+ 
+ static struct mt76_txwi_cache *
+@@ -120,13 +120,13 @@ mt76_get_txwi(struct mt76_dev *dev)
+ 	return mt76_alloc_txwi(dev);
+ }
+ 
+-struct mt76_txwi_cache *
++struct mt76_rxwi_cache *
+ mt76_get_rxwi(struct mt76_dev *dev)
+ {
+-	struct mt76_txwi_cache *t = __mt76_get_rxwi(dev);
++	struct mt76_rxwi_cache *r = __mt76_get_rxwi(dev);
+ 
+-	if (t)
+-		return t;
++	if (r)
++		return r;
+ 
+ 	return mt76_alloc_rxwi(dev);
+ }
+@@ -145,14 +145,14 @@ mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t)
+ EXPORT_SYMBOL_GPL(mt76_put_txwi);
+ 
+ void
+-mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t)
++mt76_put_rxwi(struct mt76_dev *dev, struct mt76_rxwi_cache *r)
+ {
+-	if (!t)
++	if (!r)
+ 		return;
+ 
+-	spin_lock_bh(&dev->wed_lock);
+-	list_add(&t->list, &dev->rxwi_cache);
+-	spin_unlock_bh(&dev->wed_lock);
++	spin_lock_bh(&dev->lock);
++	list_add(&r->list, &dev->rxwi_cache);
++	spin_unlock_bh(&dev->lock);
+ }
+ EXPORT_SYMBOL_GPL(mt76_put_rxwi);
+ 
+@@ -173,13 +173,13 @@ mt76_free_pending_txwi(struct mt76_dev *dev)
+ void
+ mt76_free_pending_rxwi(struct mt76_dev *dev)
+ {
+-	struct mt76_txwi_cache *t;
++	struct mt76_rxwi_cache *r;
+ 
+ 	local_bh_disable();
+-	while ((t = __mt76_get_rxwi(dev)) != NULL) {
+-		if (t->ptr)
+-			skb_free_frag(t->ptr);
+-		kfree(t);
++	while ((r = __mt76_get_rxwi(dev)) != NULL) {
++		if (r->ptr)
++			skb_free_frag(r->ptr);
++		kfree(r);
+ 	}
+ 	local_bh_enable();
+ }
+@@ -225,10 +225,10 @@ void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q)
+ 
+ static int
+ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
+-		    struct mt76_queue_buf *buf, void *data)
++		    struct mt76_queue_buf *buf, void *data,
++		    struct mt76_rxwi_cache *rxwi)
+ {
+ 	struct mt76_queue_entry *entry = &q->entry[q->head];
+-	struct mt76_txwi_cache *txwi = NULL;
+ 	struct mt76_desc *desc;
+ 	int idx = q->head;
+ 	u32 buf1 = 0, ctrl;
+@@ -249,13 +249,15 @@ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
+ #endif
+ 
+ 	if (mt76_queue_is_wed_rx(q)) {
+-		txwi = mt76_get_rxwi(dev);
+-		if (!txwi)
+-			return -ENOMEM;
++		if (!rxwi) {
++			rxwi = mt76_get_rxwi(dev);
++			if (!rxwi)
++				return -ENOMEM;
++		}
+ 
+-		rx_token = mt76_rx_token_consume(dev, data, txwi, buf->addr);
++		rx_token = mt76_rx_token_consume(dev, data, rxwi, buf->addr);
+ 		if (rx_token < 0) {
+-			mt76_put_rxwi(dev, txwi);
++			mt76_put_rxwi(dev, rxwi);
+ 			return -ENOMEM;
+ 		}
+ 
+@@ -271,7 +273,7 @@ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
+ done:
+ 	entry->dma_addr[0] = buf->addr;
+ 	entry->dma_len[0] = buf->len;
+-	entry->txwi = txwi;
++	entry->rxwi = rxwi;
+ 	entry->buf = data;
+ 	entry->wcid = 0xffff;
+ 	entry->skip_buf1 = true;
+@@ -420,7 +422,7 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush)
+ 
+ static void *
+ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+-		 int *len, u32 *info, bool *more, bool *drop)
++		 int *len, u32 *info, bool *more, bool *drop, bool flush)
+ {
+ 	struct mt76_queue_entry *e = &q->entry[idx];
+ 	struct mt76_desc *desc = &q->desc[idx];
+@@ -445,20 +447,53 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ 
+ 	if (mt76_queue_is_wed_rx(q)) {
+ 		u32 token = FIELD_GET(MT_DMA_CTL_TOKEN, buf1);
+-		struct mt76_txwi_cache *t = mt76_rx_token_release(dev, token);
++		struct mt76_rxwi_cache *r = mt76_rx_token_release(dev, token);
+ 
+-		if (!t)
++		if (!r)
+ 			return NULL;
+ 
+-		dma_unmap_single(dev->dma_dev, t->dma_addr,
++		dma_unmap_single(dev->dma_dev, r->dma_addr,
+ 				 SKB_WITH_OVERHEAD(q->buf_size),
+ 				 DMA_FROM_DEVICE);
+ 
+-		buf = t->ptr;
+-		t->dma_addr = 0;
+-		t->ptr = NULL;
++		if (flush) {
++			buf = r->ptr;
++			r->dma_addr = 0;
++			r->ptr = NULL;
++
++			mt76_put_rxwi(dev, r);
++		} else {
++			struct mt76_queue_buf qbuf;
++
++			buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
++			if (!buf)
++				return NULL;
++
++			memcpy(buf, r->ptr, SKB_WITH_OVERHEAD(q->buf_size));
++
++			r->dma_addr = dma_map_single(dev->dma_dev, r->ptr,
++						     SKB_WITH_OVERHEAD(q->buf_size),
++						     DMA_FROM_DEVICE);
++			if (unlikely(dma_mapping_error(dev->dma_dev, r->dma_addr))) {
++				skb_free_frag(r->ptr);
++				mt76_put_rxwi(dev, r);
++				return NULL;
++			}
++
++			qbuf.addr = r->dma_addr;
++			qbuf.len = SKB_WITH_OVERHEAD(q->buf_size);
++			qbuf.skip_unmap = false;
++
++			if (mt76_dma_add_rx_buf(dev, q, &qbuf, r->ptr, r) < 0) {
++				dma_unmap_single(dev->dma_dev, r->dma_addr,
++						 SKB_WITH_OVERHEAD(q->buf_size),
++						 DMA_FROM_DEVICE);
++				skb_free_frag(r->ptr);
++				mt76_put_rxwi(dev, r);
++				return NULL;
++			}
++		}
+ 
+-		mt76_put_rxwi(dev, t);
+ 		if (drop)
+ 			*drop |= !!(buf1 & MT_DMA_CTL_WO_DROP);
+ 	} else {
+@@ -495,7 +530,7 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
+ 	q->tail = (q->tail + 1) % q->ndesc;
+ 	q->queued--;
+ 
+-	return mt76_dma_get_buf(dev, q, idx, len, info, more, drop);
++	return mt76_dma_get_buf(dev, q, idx, len, info, more, drop, flush);
+ }
+ 
+ static int
+@@ -667,7 +702,7 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
+ done:
+ 		qbuf.len = len - offset;
+ 		qbuf.skip_unmap = false;
+-		if (mt76_dma_add_rx_buf(dev, q, &qbuf, buf) < 0) {
++		if (mt76_dma_add_rx_buf(dev, q, &qbuf, buf, NULL) < 0) {
+ 			dma_unmap_single(dev->dma_dev, addr, len,
+ 					 DMA_FROM_DEVICE);
+ 			skb_free_frag(buf);
+diff --git a/mac80211.c b/mac80211.c
+index 5f85bf1d..3e054d9d 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -596,7 +596,6 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
+ 	spin_lock_init(&dev->lock);
+ 	spin_lock_init(&dev->cc_lock);
+ 	spin_lock_init(&dev->status_lock);
+-	spin_lock_init(&dev->wed_lock);
+ 	mutex_init(&dev->mutex);
+ 	init_waitqueue_head(&dev->tx_wait);
+ 
+@@ -629,6 +628,7 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
+ 	INIT_LIST_HEAD(&dev->txwi_cache);
+ 	INIT_LIST_HEAD(&dev->rxwi_cache);
+ 	dev->token_size = dev->drv->token_size;
++	dev->rx_token_size = dev->drv->rx_token_size;
+ 
+ 	for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++)
+ 		skb_queue_head_init(&dev->rx_skb[i]);
+diff --git a/mt76.h b/mt76.h
+index fbcd5ea9..92b59dd6 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -205,6 +205,7 @@ struct mt76_queue_entry {
+ 	};
+ 	union {
+ 		struct mt76_txwi_cache *txwi;
++		struct mt76_rxwi_cache *rxwi;
+ 		struct urb *urb;
+ 		int buf_sz;
+ 	};
+@@ -420,12 +421,16 @@ struct mt76_txwi_cache {
+ 	struct list_head list;
+ 	dma_addr_t dma_addr;
+ 
+-	union {
+-		struct sk_buff *skb;
+-		void *ptr;
+-	};
+-
+ 	unsigned long jiffies;
++
++	struct sk_buff *skb;
++};
++
++struct mt76_rxwi_cache {
++	struct list_head list;
++	dma_addr_t dma_addr;
++
++	void *ptr;
+ };
+ 
+ struct mt76_rx_tid {
+@@ -512,6 +517,7 @@ struct mt76_driver_ops {
+ 	u16 txwi_size;
+ 	u16 token_size;
+ 	u8 mcs_rates;
++	u16 rx_token_size;
+ 
+ 	void (*update_survey)(struct mt76_phy *phy);
+ 
+@@ -876,7 +882,6 @@ struct mt76_dev {
+ 
+ 	struct ieee80211_hw *hw;
+ 
+-	spinlock_t wed_lock;
+ 	spinlock_t lock;
+ 	spinlock_t cc_lock;
+ 
+@@ -1562,8 +1567,8 @@ mt76_tx_status_get_hw(struct mt76_dev *dev, struct sk_buff *skb)
+ }
+ 
+ void mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t);
+-void mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t);
+-struct mt76_txwi_cache *mt76_get_rxwi(struct mt76_dev *dev);
++void mt76_put_rxwi(struct mt76_dev *dev, struct mt76_rxwi_cache *r);
++struct mt76_rxwi_cache *mt76_get_rxwi(struct mt76_dev *dev);
+ void mt76_free_pending_rxwi(struct mt76_dev *dev);
+ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
+ 		      struct napi_struct *napi);
+@@ -1754,9 +1759,9 @@ struct mt76_txwi_cache *
+ mt76_token_release(struct mt76_dev *dev, int token, bool *wake);
+ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi);
+ void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked);
+-struct mt76_txwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token);
++struct mt76_rxwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token);
+ int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
+-			  struct mt76_txwi_cache *r, dma_addr_t phys);
++			  struct mt76_rxwi_cache *r, dma_addr_t phys);
+ 
+ static inline void mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked)
+ {
+diff --git a/mt7915/mmio.c b/mt7915/mmio.c
+index d6ecd698..c8511867 100644
+--- a/mt7915/mmio.c
++++ b/mt7915/mmio.c
+@@ -714,7 +714,7 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr,
+ 	wed->wlan.reset = mt7915_mmio_wed_reset;
+ 	wed->wlan.reset_complete = mt76_wed_reset_complete;
+ 
+-	dev->mt76.rx_token_size = wed->wlan.rx_npkt;
++	dev->mt76.rx_token_size += wed->wlan.rx_npkt;
+ 
+ 	if (mtk_wed_device_attach(wed))
+ 		return 0;
+@@ -921,6 +921,7 @@ struct mt7915_dev *mt7915_mmio_probe(struct device *pdev,
+ 				SURVEY_INFO_TIME_RX |
+ 				SURVEY_INFO_TIME_BSS_RX,
+ 		.token_size = MT7915_TOKEN_SIZE,
++		.rx_token_size = MT7915_RX_TOKEN_SIZE;
+ 		.tx_prepare_skb = mt7915_tx_prepare_skb,
+ 		.tx_complete_skb = mt76_connac_tx_complete_skb,
+ 		.rx_skb = mt7915_queue_rx_skb,
+diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
+index a30d08eb..f1e2c93a 100644
+--- a/mt7915/mt7915.h
++++ b/mt7915/mt7915.h
+@@ -62,6 +62,7 @@
+ #define MT7915_EEPROM_BLOCK_SIZE	16
+ #define MT7915_HW_TOKEN_SIZE		4096
+ #define MT7915_TOKEN_SIZE		8192
++#define MT7915_RX_TOKEN_SIZE		4096
+ 
+ #define MT7915_CFEND_RATE_DEFAULT	0x49	/* OFDM 24M */
+ #define MT7915_CFEND_RATE_11B		0x03	/* 11B LP, 11M */
+diff --git a/tx.c b/tx.c
+index ab42f69b..46dae6e0 100644
+--- a/tx.c
++++ b/tx.c
+@@ -851,16 +851,16 @@ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi)
+ EXPORT_SYMBOL_GPL(mt76_token_consume);
+ 
+ int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
+-			  struct mt76_txwi_cache *t, dma_addr_t phys)
++			  struct mt76_rxwi_cache *r, dma_addr_t phys)
+ {
+ 	int token;
+ 
+ 	spin_lock_bh(&dev->rx_token_lock);
+-	token = idr_alloc(&dev->rx_token, t, 0, dev->rx_token_size,
++	token = idr_alloc(&dev->rx_token, r, 0, dev->rx_token_size,
+ 			  GFP_ATOMIC);
+ 	if (token >= 0) {
+-		t->ptr = ptr;
+-		t->dma_addr = phys;
++		r->ptr = ptr;
++		r->dma_addr = phys;
+ 	}
+ 	spin_unlock_bh(&dev->rx_token_lock);
+ 
+@@ -897,15 +897,15 @@ mt76_token_release(struct mt76_dev *dev, int token, bool *wake)
+ }
+ EXPORT_SYMBOL_GPL(mt76_token_release);
+ 
+-struct mt76_txwi_cache *
++struct mt76_rxwi_cache *
+ mt76_rx_token_release(struct mt76_dev *dev, int token)
+ {
+-	struct mt76_txwi_cache *t;
++	struct mt76_rxwi_cache *r;
+ 
+ 	spin_lock_bh(&dev->rx_token_lock);
+-	t = idr_remove(&dev->rx_token, token);
++	r = idr_remove(&dev->rx_token, token);
+ 	spin_unlock_bh(&dev->rx_token_lock);
+ 
+-	return t;
++	return r;
+ }
+ EXPORT_SYMBOL_GPL(mt76_rx_token_release);
+diff --git a/wed.c b/wed.c
+index 8eca4d81..0a0b5c05 100644
+--- a/wed.c
++++ b/wed.c
+@@ -9,28 +9,45 @@
+ void mt76_wed_release_rx_buf(struct mtk_wed_device *wed)
+ {
+ 	struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
+-	u32 length;
++	struct page *page;
+ 	int i;
+ 
+-	length = SKB_DATA_ALIGN(NET_SKB_PAD + wed->wlan.rx_size +
+-				sizeof(struct skb_shared_info));
+-
+ 	for (i = 0; i < dev->rx_token_size; i++) {
+-		struct mt76_txwi_cache *t;
++		struct mt76_rxwi_cache *r;
+ 
+-		t = mt76_rx_token_release(dev, i);
+-		if (!t || !t->ptr)
++		r = mt76_rx_token_release(dev, i);
++		if (!r || !r->ptr)
+ 			continue;
+ 
+-		dma_unmap_single(dev->dma_dev, t->dma_addr,
++		dma_unmap_single(dev->dma_dev, r->dma_addr,
+ 				 wed->wlan.rx_size, DMA_FROM_DEVICE);
+-		__free_pages(virt_to_page(t->ptr), get_order(length));
+-		t->ptr = NULL;
++		skb_free_frag(r->ptr);
++		r->ptr = NULL;
+ 
+-		mt76_put_rxwi(dev, t);
++		mt76_put_rxwi(dev, r);
+ 	}
+ 
+ 	mt76_free_pending_rxwi(dev);
++
++	mt76_for_each_q_rx(dev, i) {
++		struct mt76_queue *q = &dev->q_rx[i];
++
++		if (mt76_queue_is_wed_rx(q)) {
++			if (!q->rx_page.va)
++				continue;
++
++			page = virt_to_page(q->rx_page.va);
++			__page_frag_cache_drain(page, q->rx_page.pagecnt_bias);
++			memset(&q->rx_page, 0, sizeof(q->rx_page));
++		}
++	}
++
++	if (!wed->rx_buf_ring.rx_page.va)
++		return;
++
++	page = virt_to_page(wed->rx_buf_ring.rx_page.va);
++	__page_frag_cache_drain(page, wed->rx_buf_ring.rx_page.pagecnt_bias);
++	memset(&wed->rx_buf_ring.rx_page, 0, sizeof(wed->rx_buf_ring.rx_page));
+ }
+ EXPORT_SYMBOL_GPL(mt76_wed_release_rx_buf);
+ 
+@@ -46,18 +63,18 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
+ 				sizeof(struct skb_shared_info));
+ 
+ 	for (i = 0; i < size; i++) {
+-		struct mt76_txwi_cache *t = mt76_get_rxwi(dev);
++		struct mt76_rxwi_cache *r = mt76_get_rxwi(dev);
+ 		dma_addr_t addr;
+ 		struct page *page;
+ 		int token;
+ 		void *ptr;
+ 
+-		if (!t)
++		if (!r)
+ 			goto unmap;
+ 
+-		page = __dev_alloc_pages(GFP_KERNEL, get_order(length));
+-		if (!page) {
+-			mt76_put_rxwi(dev, t);
++		ptr = page_frag_alloc(&wed->rx_buf_ring.rx_page, length, GFP_ATOMIC);
++		if (!ptr) {
++			mt76_put_rxwi(dev, r);
+ 			goto unmap;
+ 		}
+ 
+@@ -67,17 +84,17 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
+ 
+ 		if (unlikely(dma_mapping_error(dev->dev, addr))) {
+ 			skb_free_frag(ptr);
+-			mt76_put_rxwi(dev, t);
++			mt76_put_rxwi(dev, r);
+ 			goto unmap;
+ 		}
+ 
+ 		desc->buf0 = cpu_to_le32(addr);
+-		token = mt76_rx_token_consume(dev, ptr, t, addr);
++		token = mt76_rx_token_consume(dev, ptr, r, addr);
+ 		if (token < 0) {
+ 			dma_unmap_single(dev->dma_dev, addr,
+ 					 wed->wlan.rx_size, DMA_TO_DEVICE);
+-			__free_pages(page, get_order(length));
+-			mt76_put_rxwi(dev, t);
++			skb_free_frag(ptr);
++			mt76_put_rxwi(dev, r);
+ 			goto unmap;
+ 		}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0062-mtk-wifi-mt76-add-SER-support-for-wed3.0.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0062-mtk-wifi-mt76-add-SER-support-for-wed3.0.patch
deleted file mode 100644
index 9653eac..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0062-mtk-wifi-mt76-add-SER-support-for-wed3.0.patch
+++ /dev/null
@@ -1,41 +0,0 @@
-From a666507a58b47e007ea1396d35941598a398cdc9 Mon Sep 17 00:00:00 2001
-From: mtk27745 <rex.lu@mediatek.com>
-Date: Tue, 23 May 2023 12:06:29 +0800
-Subject: [PATCH 062/116] mtk: wifi: mt76: add SER support for wed3.0
-
----
- dma.c         | 5 +++--
- mt7996/mmio.c | 1 +
- 2 files changed, 4 insertions(+), 2 deletions(-)
-
-diff --git a/dma.c b/dma.c
-index c54187b..e5be891 100644
---- a/dma.c
-+++ b/dma.c
-@@ -834,8 +834,9 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
- 
- 	/* reset WED rx queues */
- 	mt76_wed_dma_setup(dev, q, true);
--
--	if (!mt76_queue_is_wed_tx_free(q)) {
-+	if (!mt76_queue_is_wed_tx_free(q) &&
-+	    !(mt76_queue_is_wed_rro(q) &&
-+	    mtk_wed_device_active(&dev->mmio.wed))) {
- 		mt76_dma_sync_idx(dev, q);
- 		mt76_dma_rx_fill(dev, q, false);
- 	}
-diff --git a/mt7996/mmio.c b/mt7996/mmio.c
-index a082cca..d29579f 100644
---- a/mt7996/mmio.c
-+++ b/mt7996/mmio.c
-@@ -297,6 +297,7 @@ out:
- 
- 	return ret;
- }
-+
- #endif
- 
- int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0063-mtk-mt76-change-wed-token-init-size-to-adapt-wed3.0.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0063-mtk-mt76-change-wed-token-init-size-to-adapt-wed3.0.patch
new file mode 100644
index 0000000..15d941a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0063-mtk-mt76-change-wed-token-init-size-to-adapt-wed3.0.patch
@@ -0,0 +1,37 @@
+From 18702f417ad4d5757cda5ea88924cac918e25592 Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Wed, 19 Apr 2023 17:13:41 +0800
+Subject: [PATCH 063/199] mtk: mt76: change wed token init size to adapt wed3.0
+
+Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
+---
+ tx.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/tx.c b/tx.c
+index 46dae6e0..e2795067 100644
+--- a/tx.c
++++ b/tx.c
+@@ -827,12 +827,16 @@ EXPORT_SYMBOL_GPL(__mt76_set_tx_blocked);
+ 
+ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi)
+ {
+-	int token;
++	int token, start = 0;
++
++	if (mtk_wed_device_active(&dev->mmio.wed))
++		start = dev->mmio.wed.wlan.nbuf;
+ 
+ 	spin_lock_bh(&dev->token_lock);
+ 
+-	token = idr_alloc(&dev->token, *ptxwi, 0, dev->token_size, GFP_ATOMIC);
+-	if (token >= 0)
++	token = idr_alloc(&dev->token, *ptxwi, start, start + dev->token_size,
++			  GFP_ATOMIC);
++	if (token >= start)
+ 		dev->token_count++;
+ 
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0063-mtk-wifi-mt76-mt7915-wed-find-rx-token-by-physical-a.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0063-mtk-wifi-mt76-mt7915-wed-find-rx-token-by-physical-a.patch
deleted file mode 100644
index e40c8a0..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0063-mtk-wifi-mt76-mt7915-wed-find-rx-token-by-physical-a.patch
+++ /dev/null
@@ -1,65 +0,0 @@
-From e9774619c3b0871df9bfcb87fb4f268e8799e0c3 Mon Sep 17 00:00:00 2001
-From: "sujuan.chen" <sujuan.chen@mediatek.com>
-Date: Wed, 19 Jul 2023 10:55:09 +0800
-Subject: [PATCH 063/116] mtk: wifi: mt76: mt7915: wed: find rx token by
- physical address
-
-The token id in RxDMAD may be incorrect when it is not the last frame due to
-WED HW bug. Lookup correct token id by physical address in sdp0.
-Add len == 0 check to drop garbage frames
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- dma.c | 27 +++++++++++++++++++++++++--
- 1 file changed, 25 insertions(+), 2 deletions(-)
-
-diff --git a/dma.c b/dma.c
-index e5be891..1021b3e 100644
---- a/dma.c
-+++ b/dma.c
-@@ -446,9 +446,32 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
- 	mt76_dma_should_drop_buf(drop, ctrl, buf1, desc_info);
- 
- 	if (mt76_queue_is_wed_rx(q)) {
-+		u32 id, find = 0;
- 		u32 token = FIELD_GET(MT_DMA_CTL_TOKEN, buf1);
--		struct mt76_rxwi_cache *r = mt76_rx_token_release(dev, token);
-+		struct mt76_rxwi_cache *r;
-+
-+		if (*more) {
-+			spin_lock_bh(&dev->rx_token_lock);
-+
-+			idr_for_each_entry(&dev->rx_token, r, id) {
-+				if (r->dma_addr == le32_to_cpu(desc->buf0)) {
-+					find = 1;
-+					token = id;
-+
-+					/* Write correct id back to DMA*/
-+					u32p_replace_bits(&buf1, id,
-+							  MT_DMA_CTL_TOKEN);
-+					WRITE_ONCE(desc->buf1, cpu_to_le32(buf1));
-+					break;
-+				}
-+			}
- 
-+			spin_unlock_bh(&dev->rx_token_lock);
-+			if (!find)
-+				return NULL;
-+		}
-+
-+		r = mt76_rx_token_release(dev, token);
- 		if (!r)
- 			return NULL;
- 
-@@ -902,7 +925,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
- 		if (!data)
- 			break;
- 
--		if (drop)
-+		if (drop || (len == 0))
- 			goto free_frag;
- 
- 		if (q->rx_head)
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0064-mtk-mt76-add-random-early-drop-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0064-mtk-mt76-add-random-early-drop-support.patch
new file mode 100644
index 0000000..aac1628
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0064-mtk-mt76-add-random-early-drop-support.patch
@@ -0,0 +1,316 @@
+From a95ba55c6e8e2662d7278f0530f3aa75c4f4a798 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Wed, 19 Apr 2023 18:32:41 +0800
+Subject: [PATCH 064/199] mtk: mt76: add random early drop support
+
+---
+ mt7996/debugfs.c     |  1 +
+ mt7996/mac.c         |  7 ++++
+ mt7996/mcu.c         | 81 ++++++++++++++++++++++++++++++++++++++++++--
+ mt7996/mcu.h         |  4 ++-
+ mt7996/mt7996.h      |  5 ++-
+ mt7996/mtk_debugfs.c | 23 +++++++++++++
+ mt7996/mtk_mcu.c     | 26 ++++++++++++++
+ mt7996/mtk_mcu.h     | 24 +++++++++++++
+ 8 files changed, 167 insertions(+), 4 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 6d119467..3301701a 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -633,6 +633,7 @@ mt7996_tx_stats_show(struct seq_file *file, void *data)
+ 	seq_printf(file, "Tx attempts: %8u (MPDUs)\n", attempts);
+ 	seq_printf(file, "Tx success: %8u (MPDUs)\n", success);
+ 	seq_printf(file, "Tx PER: %u%%\n", per);
++	seq_printf(file, "Tx RED drop: %8u\n", phy->red_drop);
+ 
+ 	mt7996_txbf_stat_read_phy(phy, file);
+ 
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index bcddff23..0c2fee66 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1179,6 +1179,13 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ 
+ 			wcid->stats.tx_retries += tx_retries;
+ 			wcid->stats.tx_failed += tx_failed;
++
++			if (FIELD_GET(MT_TXFREE_INFO_STAT, info) == 2) {
++				struct mt7996_phy *mphy =
++					__mt7996_phy(dev, wcid->phy_idx);
++
++				mphy->red_drop++;
++			}
+ 			continue;
+ 		}
+ 
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 9bef1274..7fa44f84 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3150,8 +3150,8 @@ int mt7996_mcu_init_firmware(struct mt7996_dev *dev)
+ 	if (ret)
+ 		return ret;
+ 
+-	return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
+-				 MCU_WA_PARAM_RED, 0, 0);
++	return mt7996_mcu_red_config(dev,
++			mtk_wed_device_active(&dev->mt76.mmio.wed));
+ }
+ 
+ int mt7996_mcu_init(struct mt7996_dev *dev)
+@@ -3183,6 +3183,83 @@ out:
+ 	skb_queue_purge(&dev->mt76.mcu.res_q);
+ }
+ 
++static int mt7996_mcu_wa_red_config(struct mt7996_dev *dev)
++{
++#define RED_TOKEN_SRC_CNT	4
++#define RED_TOKEN_CONFIG	2
++	struct {
++		__le32 arg0;
++		__le32 arg1;
++		__le32 arg2;
++
++		u8 mode;
++		u8 version;
++		u8 _rsv[4];
++		__le16 len;
++
++		__le16 tcp_offset;
++		__le16 priority_offset;
++		__le16 token_per_src[RED_TOKEN_SRC_CNT];
++		__le16 token_thr_per_src[RED_TOKEN_SRC_CNT];
++
++		u8 _rsv2[604];
++	} __packed req = {
++		.arg0 = cpu_to_le32(MCU_WA_PARAM_RED_CONFIG),
++
++		.mode = RED_TOKEN_CONFIG,
++		.len = cpu_to_le16(sizeof(req) - sizeof(__le32) * 3),
++
++		.tcp_offset = cpu_to_le16(200),
++		.priority_offset = cpu_to_le16(255),
++	};
++	u8 i;
++
++	for (i = 0; i < RED_TOKEN_SRC_CNT; i++) {
++		req.token_per_src[i] = cpu_to_le16(MT7996_TOKEN_SIZE);
++		req.token_thr_per_src[i] = cpu_to_le16(MT7996_TOKEN_SIZE);
++	}
++
++	if (!mtk_wed_device_active(&dev->mt76.mmio.wed))
++		req.token_per_src[RED_TOKEN_SRC_CNT - 1] =
++			cpu_to_le16(MT7996_TOKEN_SIZE - MT7996_HW_TOKEN_SIZE);
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WA_PARAM_CMD(SET),
++				 &req, sizeof(req), false);
++}
++
++int mt7996_mcu_red_config(struct mt7996_dev *dev, bool enable)
++{
++#define RED_DISABLE		0
++#define RED_BY_WA_ENABLE	2
++	struct {
++		u8 __rsv1[4];
++
++		__le16 tag;
++		__le16 len;
++		u8 enable;
++		u8 __rsv2[3];
++	} __packed req = {
++		.tag = cpu_to_le16(UNI_VOW_RED_ENABLE),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.enable = enable ? RED_BY_WA_ENABLE : RED_DISABLE,
++	};
++	int ret;
++
++	ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req,
++				 sizeof(req), true);
++
++	if (ret)
++		return ret;
++
++	ret = mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
++				MCU_WA_PARAM_RED_EN, enable, 0);
++
++	if (ret || !enable)
++		return ret;
++
++	return mt7996_mcu_wa_red_config(dev);
++}
++
+ int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans)
+ {
+ 	struct {
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 3025f849..23db3b33 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -346,8 +346,9 @@ enum {
+ enum {
+ 	MCU_WA_PARAM_PDMA_RX = 0x04,
+ 	MCU_WA_PARAM_CPU_UTIL = 0x0b,
+-	MCU_WA_PARAM_RED = 0x0e,
++	MCU_WA_PARAM_RED_EN = 0x0e,
+ 	MCU_WA_PARAM_HW_PATH_HIF_VER = 0x2f,
++	MCU_WA_PARAM_RED_CONFIG = 0x40,
+ };
+ 
+ enum mcu_mmps_mode {
+@@ -920,6 +921,7 @@ enum {
+ 	UNI_VOW_DRR_CTRL,
+ 	UNI_VOW_RX_AT_AIRTIME_EN = 0x0b,
+ 	UNI_VOW_RX_AT_AIRTIME_CLR_EN = 0x0e,
++	UNI_VOW_RED_ENABLE = 0x18,
+ };
+ 
+ enum {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 784fb3db..d40f95cd 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -355,6 +355,7 @@ struct mt7996_phy {
+ 	u16 punct_bitmap;
+ 
+ 	struct mt7996_scs_ctrl scs_ctrl;
++	u32 red_drop;
+ 
+ 	bool sku_limit_en;
+ 	bool sku_path_en;
+@@ -724,6 +725,7 @@ int mt7996_mcu_rf_regval(struct mt7996_dev *dev, u32 regidx, u32 *val, bool set)
+ int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans);
+ int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u16 val);
+ int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3);
++int mt7996_mcu_red_config(struct mt7996_dev *dev, bool enable);
+ int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl);
+ int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level);
+ int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
+@@ -897,11 +899,12 @@ void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type);
+ void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 ofdma_user_cnt);
+ void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type);
+ void mt7996_tm_update_channel(struct mt7996_phy *phy);
++
++int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val);
+ #endif
+ 
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ int mt7996_dma_rro_init(struct mt7996_dev *dev);
+ #endif /* CONFIG_NET_MEDIATEK_SOC_WED */
+ 
+-
+ #endif
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 05cfc6ab..1f754796 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -3033,6 +3033,27 @@ static int mt7996_muru_prot_thr_set(void *data, u64 val)
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_muru_prot_thr, NULL,
+ 			 mt7996_muru_prot_thr_set, "%lld\n");
+ 
++static int
++mt7996_red_config_set(void *data, u64 val)
++{
++	struct mt7996_dev *dev = data;
++
++	return mt7996_mcu_red_config(dev, !!val);
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_red_config, NULL,
++			 mt7996_red_config_set, "%lld\n");
++
++static int
++mt7996_vow_drr_dbg(void *data, u64 val)
++{
++	struct mt7996_dev *dev = data;
++
++	return mt7996_mcu_set_vow_drr_dbg(dev, (u32)val);
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_vow_drr_dbg, NULL,
++			 mt7996_vow_drr_dbg, "%lld\n");
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+@@ -3109,6 +3130,8 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ 				    mt7996_wtbl_read);
+ 
+ 	debugfs_create_devm_seqfile(dev->mt76.dev, "token", dir, mt7996_token_read);
++	debugfs_create_file("red", 0200, dir, dev, &fops_red_config);
++	debugfs_create_file("vow_drr_dbg", 0200, dir, dev, &fops_vow_drr_dbg);
+ 
+ 	debugfs_create_u8("sku_disable", 0600, dir, &dev->dbg.sku_disable);
+ 	debugfs_create_file("scs_enable", 0200, dir, phy, &fops_scs_enable);
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index b46a66bb..967ee874 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -1317,4 +1317,30 @@ void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type)
+ 			  sizeof(req), false);
+ }
+ 
++int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val)
++{
++#define MT7996_VOW_DEBUG_MODE	0xe
++	struct {
++		u8 __rsv1[4];
++
++		__le16 tag;
++		__le16 len;
++		u8 __rsv2[4];
++		__le32 action;
++		__le32 val;
++		u8 __rsv3[8];
++	} __packed req = {
++		.tag = cpu_to_le16(UNI_VOW_DRR_CTRL),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.action = cpu_to_le32(MT7996_VOW_DEBUG_MODE),
++		.val = cpu_to_le32(val),
++	};
++
++	if (val & ~VOW_DRR_DBG_FLAGS)
++		return -EINVAL;
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req,
++				 sizeof(req), true);
++}
++
+ #endif
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+index 58d61c51..2cffc893 100644
+--- a/mt7996/mtk_mcu.h
++++ b/mt7996/mtk_mcu.h
+@@ -1138,6 +1138,30 @@ enum muru_vendor_ctrl {
+ 	MU_CTRL_DL_USER_CNT,
+ 	MU_CTRL_UL_USER_CNT,
+ };
++
++enum {
++	VOW_DRR_DBG_DUMP_BMP = BIT(0),
++	VOW_DRR_DBG_EST_AT_PRINT = BIT(1),
++	VOW_DRR_DBG_ADJ_GLOBAL_THLD = BIT(21),
++	VOW_DRR_DBG_PRN_LOUD = BIT(22),
++	VOW_DRR_DBG_PRN_ADJ_STA = BIT(23),
++	VOW_DRR_DBG_FIX_CR = GENMASK(27, 24),
++	VOW_DRR_DBG_CLR_FIX_CR = BIT(28),
++	VOW_DRR_DBG_DISABLE = BIT(29),
++	VOW_DRR_DBG_DUMP_CR = BIT(30),
++	VOW_DRR_DBG_PRN = BIT(31)
++};
++
++#define VOW_DRR_DBG_FLAGS (VOW_DRR_DBG_DUMP_BMP |	\
++			  VOW_DRR_DBG_EST_AT_PRINT |	\
++			  VOW_DRR_DBG_ADJ_GLOBAL_THLD |	\
++			  VOW_DRR_DBG_PRN_LOUD |	\
++			  VOW_DRR_DBG_PRN_ADJ_STA |	\
++			  VOW_DRR_DBG_FIX_CR |		\
++			  VOW_DRR_DBG_CLR_FIX_CR |	\
++			  VOW_DRR_DBG_DISABLE |		\
++			  VOW_DRR_DBG_DUMP_CR |		\
++			  VOW_DRR_DBG_PRN)
+ #endif
+ 
+ #endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0064-mtk-wifi-mt76-mt7996-add-dma-mask-limitation.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0064-mtk-wifi-mt76-mt7996-add-dma-mask-limitation.patch
deleted file mode 100644
index ae66519..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0064-mtk-wifi-mt76-mt7996-add-dma-mask-limitation.patch
+++ /dev/null
@@ -1,57 +0,0 @@
-From 947f66954b1b232255697954131532b4f005e7cd Mon Sep 17 00:00:00 2001
-From: "sujuan.chen" <sujuan.chen@mediatek.com>
-Date: Thu, 20 Jul 2023 10:25:50 +0800
-Subject: [PATCH 064/116] mtk: wifi: mt76: mt7996: add dma mask limitation
-
-Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
----
- dma.c | 4 ++--
- wed.c | 4 ++--
- 2 files changed, 4 insertions(+), 4 deletions(-)
-
-diff --git a/dma.c b/dma.c
-index 1021b3e..da21f64 100644
---- a/dma.c
-+++ b/dma.c
-@@ -488,7 +488,7 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
- 		} else {
- 			struct mt76_queue_buf qbuf;
- 
--			buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
-+			buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC | GFP_DMA32);
- 			if (!buf)
- 				return NULL;
- 
-@@ -711,7 +711,7 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
- 		if (mt76_queue_is_wed_rro_ind(q))
- 			goto done;
- 
--		buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
-+		buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC | GFP_DMA32);
- 		if (!buf)
- 			break;
- 
-diff --git a/wed.c b/wed.c
-index 0a0b5c0..1c6d53c 100644
---- a/wed.c
-+++ b/wed.c
-@@ -65,14 +65,14 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
- 	for (i = 0; i < size; i++) {
- 		struct mt76_rxwi_cache *r = mt76_get_rxwi(dev);
- 		dma_addr_t addr;
--		struct page *page;
- 		int token;
- 		void *ptr;
- 
- 		if (!r)
- 			goto unmap;
- 
--		ptr = page_frag_alloc(&wed->rx_buf_ring.rx_page, length, GFP_ATOMIC);
-+		ptr = page_frag_alloc(&wed->rx_buf_ring.rx_page, length,
-+				      GFP_ATOMIC | GFP_DMA32);
- 		if (!ptr) {
- 			mt76_put_rxwi(dev, r);
- 			goto unmap;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0065-mtk-mt76-mt7996-reset-addr_elem-when-delete-ba.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0065-mtk-mt76-mt7996-reset-addr_elem-when-delete-ba.patch
new file mode 100644
index 0000000..1b13961
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0065-mtk-mt76-mt7996-reset-addr_elem-when-delete-ba.patch
@@ -0,0 +1,91 @@
+From 8b4350b46fa5bc77a6e4314261b540b0ebd99f43 Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Thu, 18 May 2023 15:01:47 +0800
+Subject: [PATCH 065/199] mtk: mt76: mt7996: reset addr_elem when delete ba
+
+The old addr element info may be used when the signature is not equel to
+0xff, and sta will find error SDP cause the SDP/SDL=0 issue.
+
+1. without this patch will delete wrong session id when delete ba.
+Due to fw change the cmd format.
+https://gerrit.mediatek.inc/c/neptune/firmware/bora/wifi/custom/+/7969193
+
+Signed-off-by: mtk27745 <rex.lu@mediatek.com>
+Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
+---
+ mt76.h       |  1 +
+ mt7996/mcu.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 47 insertions(+)
+
+diff --git a/mt76.h b/mt76.h
+index 92b59dd6..f2052cf7 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -447,6 +447,7 @@ struct mt76_rx_tid {
+ 	u16 nframes;
+ 
+ 	u8 num;
++	u16 session_id;
+ 
+ 	u8 started:1, stopped:1, timer_pending:1;
+ 
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 23db3b33..7077a664 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -298,6 +298,52 @@ struct mt7996_mcu_thermal_notify {
+ 	u8 __rsv2[4];
+ } __packed;
+ 
++struct mt7996_mcu_rro_event {
++	struct mt7996_mcu_rxd rxd;
++
++	u8 __rsv1[4];
++
++	__le16 tag;
++	__le16 len;
++} __packed;
++
++struct mt7996_mcu_rro_ba {
++	__le16 tag;
++	__le16 len;
++
++	__le16 wlan_id;
++	u8 tid;
++	u8 __rsv1;
++	__le32 status;
++	__le16 session_id;
++	u8 __rsv2[2];
++} __packed;
++
++struct mt7996_mcu_rro_ba_del_chk_done {
++	__le16 tag;
++	__le16 len;
++
++	__le16 session_id;
++	__le16 mld_id;
++	u8 tid;
++	u8 __rsv[3];
++} __packed;
++
++enum  {
++	UNI_RRO_BA_SESSION_STATUS = 0,
++	UNI_RRO_BA_SESSION_TBL	= 1,
++	UNI_RRO_BA_SESSION_DEL_CHK_DONE = 2,
++	UNI_RRO_BA_SESSION_MAX_NUM
++};
++
++struct mt7996_mcu_rro_del_ba {
++	struct mt7996_mcu_rro_event event;
++
++	u8  wlan_idx;
++	u8  tid;
++	u8 __rsv2[2];
++};
++
+ enum mt7996_chan_mib_offs {
+ 	UNI_MIB_OBSS_AIRTIME = 26,
+ 	UNI_MIB_NON_WIFI_TIME = 27,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0065-mtk-wifi-mt76-mt7996-add-per-bss-statistic-info.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0065-mtk-wifi-mt76-mt7996-add-per-bss-statistic-info.patch
deleted file mode 100644
index 48ed97c..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0065-mtk-wifi-mt76-mt7996-add-per-bss-statistic-info.patch
+++ /dev/null
@@ -1,122 +0,0 @@
-From d86765207c6919af7c0448841f723159b0c1dcab Mon Sep 17 00:00:00 2001
-From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Date: Fri, 18 Aug 2023 10:17:08 +0800
-Subject: [PATCH 065/116] mtk: wifi: mt76: mt7996: add per bss statistic info
-
-Whenever WED is enabled, unicast traffic might run through HW path.
-As a result, we need to count them using WM event.
-Broadcast and multicast traffic on the other hand, will be counted in mac80211
-as they always go through SW path and thus mac80211 can always see and count them.
-
-|         | Tx                             | Rx                        |
-|---------|--------------------------------|---------------------------|
-| Unicast | mt76                           | mt76                      |
-|         | __mt7996_stat_to_netdev()      | __mt7996_stat_to_netdev() |
-|---------|--------------------------------|---------------------------|
-| BMCast  | mac80211                       | mac80211                  |
-|         | __ieee80211_subif_start_xmit() | ieee80211_deliver_skb()   |
----
- mt7996/init.c |  1 +
- mt7996/main.c |  1 +
- mt7996/mcu.c  | 40 +++++++++++++++++++++++++++++++++++-----
- 3 files changed, 37 insertions(+), 5 deletions(-)
-
-diff --git a/mt7996/init.c b/mt7996/init.c
-index de5122f..8b7c278 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -401,6 +401,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
- 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
- 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER);
- 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_PUNCT);
-+	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_STAS_COUNT);
- 
- 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION);
- 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_PROTECTION);
-diff --git a/mt7996/main.c b/mt7996/main.c
-index c7cc66e..801e480 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -265,6 +265,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
- 	mvif->sta.wcid.phy_idx = band_idx;
- 	mvif->sta.wcid.hw_key_idx = -1;
- 	mvif->sta.wcid.tx_info |= MT_WCID_TX_INFO_SET;
-+	mvif->sta.vif = mvif;
- 	mt76_wcid_init(&mvif->sta.wcid);
- 
- 	mt7996_mac_wtbl_update(dev, idx,
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index f745139..a238fdd 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -524,6 +524,27 @@ mt7996_mcu_update_tx_gi(struct rate_info *rate, struct all_sta_trx_rate *mcu_rat
- 	return 0;
- }
- 
-+static inline void __mt7996_stat_to_netdev(struct mt76_phy *mphy,
-+					   struct mt76_wcid *wcid,
-+					   u32 tx_bytes, u32 rx_bytes,
-+					   u32 tx_packets, u32 rx_packets)
-+{
-+	struct mt7996_sta *msta;
-+	struct ieee80211_vif *vif;
-+	struct wireless_dev *wdev;
-+
-+	if (wiphy_ext_feature_isset(mphy->hw->wiphy,
-+				    NL80211_EXT_FEATURE_STAS_COUNT)) {
-+		msta = container_of(wcid, struct mt7996_sta, wcid);
-+		vif = container_of((void *)msta->vif, struct ieee80211_vif,
-+				   drv_priv);
-+		wdev = ieee80211_vif_to_wdev(vif);
-+
-+		dev_sw_netstats_tx_add(wdev->netdev, tx_packets, tx_bytes);
-+		dev_sw_netstats_rx_add(wdev->netdev, rx_packets, rx_bytes);
-+	}
-+}
-+
- static void
- mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- {
-@@ -539,7 +560,7 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- 		u16 wlan_idx;
- 		struct mt76_wcid *wcid;
- 		struct mt76_phy *mphy;
--		u32 tx_bytes, rx_bytes;
-+		u32 tx_bytes, rx_bytes, tx_packets, rx_packets;
- 
- 		switch (le16_to_cpu(res->tag)) {
- 		case UNI_ALL_STA_TXRX_RATE:
-@@ -567,6 +588,9 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- 				wcid->stats.tx_bytes += tx_bytes;
- 				wcid->stats.rx_bytes += rx_bytes;
- 
-+				__mt7996_stat_to_netdev(mphy, wcid,
-+							tx_bytes, rx_bytes, 0, 0);
-+
- 				ieee80211_tpt_led_trig_tx(mphy->hw, tx_bytes);
- 				ieee80211_tpt_led_trig_rx(mphy->hw, rx_bytes);
- 			}
-@@ -578,10 +602,16 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- 			if (!wcid)
- 				break;
- 
--			wcid->stats.tx_packets +=
--				le32_to_cpu(res->msdu_cnt[i].tx_msdu_cnt);
--			wcid->stats.rx_packets +=
--				le32_to_cpu(res->msdu_cnt[i].rx_msdu_cnt);
-+			mphy = mt76_dev_phy(&dev->mt76, wcid->phy_idx);
-+
-+			tx_packets = le32_to_cpu(res->msdu_cnt[i].tx_msdu_cnt);
-+			rx_packets = le32_to_cpu(res->msdu_cnt[i].rx_msdu_cnt);
-+
-+			wcid->stats.tx_packets += tx_packets;
-+			wcid->stats.rx_packets += rx_packets;
-+
-+			__mt7996_stat_to_netdev(mphy, wcid, 0, 0,
-+						tx_packets, rx_packets);
- 			break;
- 		default:
- 			break;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0066-mtk-mt76-change-pcie0-R5-to-pcie1-to-get-6G-ICS.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0066-mtk-mt76-change-pcie0-R5-to-pcie1-to-get-6G-ICS.patch
new file mode 100644
index 0000000..d021759
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0066-mtk-mt76-change-pcie0-R5-to-pcie1-to-get-6G-ICS.patch
@@ -0,0 +1,67 @@
+From 84de1f40cbb93804a270e3d70824ccf8e8e5f1e7 Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Fri, 6 Oct 2023 14:01:41 +0800
+Subject: [PATCH 066/199] mtk: mt76: change pcie0 R5 to pcie1 to get 6G ICS
+
+---
+ mt7996/dma.c  | 4 ++++
+ mt7996/init.c | 6 ++----
+ mt7996/mmio.c | 5 ++++-
+ 3 files changed, 10 insertions(+), 5 deletions(-)
+
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index 759a58e8..5d85e9ea 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -538,6 +538,10 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 	if (mt7996_band_valid(dev, MT_BAND2)) {
+ 		/* rx data queue for mt7996 band2 */
+ 		rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND2) + hif1_ofs;
++		if (mtk_wed_device_active(wed_hif2) && mtk_wed_get_rx_capa(wed_hif2)) {
++			dev->mt76.q_rx[MT_RXQ_BAND2].flags = MT_WED_Q_RX(0);
++			dev->mt76.q_rx[MT_RXQ_BAND2].wed = wed_hif2;
++		}
+ 		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND2],
+ 				       MT_RXQ_ID(MT_RXQ_BAND2),
+ 				       MT7996_RX_RING_SIZE,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 8b642ecc..6563974d 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -651,10 +651,8 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 		goto error;
+ 
+ 	if (wed == &dev->mt76.mmio.wed_hif2 && mtk_wed_device_active(wed)) {
+-		u32 irq_mask = dev->mt76.mmio.irqmask | MT_INT_TX_DONE_BAND2;
+-
+-		mt76_wr(dev, MT_INT1_MASK_CSR, irq_mask);
+-		mtk_wed_device_start(&dev->mt76.mmio.wed_hif2, irq_mask);
++		mt76_wr(dev, MT_INT_PCIE1_MASK_CSR, MT_INT_TX_RX_DONE_EXT);
++		mtk_wed_device_start(&dev->mt76.mmio.wed_hif2, MT_INT_TX_RX_DONE_EXT);
+ 	}
+ 
+ 	return 0;
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index 928a9663..7940621b 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -527,12 +527,15 @@ static void mt7996_irq_tasklet(struct tasklet_struct *t)
+ 					       dev->mt76.mmio.irqmask);
+ 		if (intr1 & MT_INT_RX_TXFREE_EXT)
+ 			napi_schedule(&dev->mt76.napi[MT_RXQ_TXFREE_BAND2]);
++
++		if (intr1 & MT_INT_RX_DONE_BAND2_EXT)
++			napi_schedule(&dev->mt76.napi[MT_RXQ_BAND2]);
+ 	}
+ 
+ 	if (mtk_wed_device_active(wed)) {
+ 		mtk_wed_device_irq_set_mask(wed, 0);
+ 		intr = mtk_wed_device_irq_get(wed, dev->mt76.mmio.irqmask);
+-		intr |= (intr1 & ~MT_INT_RX_TXFREE_EXT);
++		intr |= (intr1 & ~MT_INT_TX_RX_DONE_EXT);
+ 	} else {
+ 		mt76_wr(dev, MT_INT_MASK_CSR, 0);
+ 		if (dev->hif2)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0066-mtk-wifi-mt76-mt7996-do-not-report-netdev-stats-on-m.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0066-mtk-wifi-mt76-mt7996-do-not-report-netdev-stats-on-m.patch
deleted file mode 100644
index d86ca2c..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0066-mtk-wifi-mt76-mt7996-do-not-report-netdev-stats-on-m.patch
+++ /dev/null
@@ -1,37 +0,0 @@
-From 947b71bb7b65824acf8dbaa984ead4ce638f4005 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 26 Oct 2023 17:27:43 +0800
-Subject: [PATCH 066/116] mtk: wifi: mt76: mt7996: do not report netdev stats
- on monitor vif
-
-This fixes the following NULL pointer crash when enabling monitor mode:
-[  205.593158] Call trace:
-[  205.595597]  mt7996_mcu_rx_event+0x4a0/0x6e8 [mt7996e]
-[  205.600725]  mt7996_queue_rx_skb+0x6e4/0xfa0 [mt7996e]
-[  205.605851]  mt76_dma_rx_poll+0x384/0x420 [mt76]
-[  205.610459]  __napi_poll+0x38/0x1c0
-[  205.613935]  napi_threaded_poll+0x80/0xe8
-[  205.617934]  kthread+0x124/0x128
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/mcu.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index a238fdd..b886644 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -540,6 +540,9 @@ static inline void __mt7996_stat_to_netdev(struct mt76_phy *mphy,
- 				   drv_priv);
- 		wdev = ieee80211_vif_to_wdev(vif);
- 
-+		if (vif->type == NL80211_IFTYPE_MONITOR)
-+			return;
-+
- 		dev_sw_netstats_tx_add(wdev->netdev, tx_packets, tx_bytes);
- 		dev_sw_netstats_rx_add(wdev->netdev, rx_packets, rx_bytes);
- 	}
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0067-mtk-mt76-add-SER-support-for-wed3.0.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0067-mtk-mt76-add-SER-support-for-wed3.0.patch
new file mode 100644
index 0000000..11a6537
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0067-mtk-mt76-add-SER-support-for-wed3.0.patch
@@ -0,0 +1,41 @@
+From b8b41987d94d7fca7bcf9f6b7204e62528322ecc Mon Sep 17 00:00:00 2001
+From: mtk27745 <rex.lu@mediatek.com>
+Date: Tue, 23 May 2023 12:06:29 +0800
+Subject: [PATCH 067/199] mtk: mt76: add SER support for wed3.0
+
+---
+ dma.c         | 5 +++--
+ mt7996/mmio.c | 1 +
+ 2 files changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index c54187bd..e5be891c 100644
+--- a/dma.c
++++ b/dma.c
+@@ -834,8 +834,9 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
+ 
+ 	/* reset WED rx queues */
+ 	mt76_wed_dma_setup(dev, q, true);
+-
+-	if (!mt76_queue_is_wed_tx_free(q)) {
++	if (!mt76_queue_is_wed_tx_free(q) &&
++	    !(mt76_queue_is_wed_rro(q) &&
++	    mtk_wed_device_active(&dev->mmio.wed))) {
+ 		mt76_dma_sync_idx(dev, q);
+ 		mt76_dma_rx_fill(dev, q, false);
+ 	}
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index 7940621b..0d25c5ff 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -297,6 +297,7 @@ out:
+ 
+ 	return ret;
+ }
++
+ #endif
+ 
+ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0067-mtk-wifi-mt76-mt7996-add-support-for-HW-ATF.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0067-mtk-wifi-mt76-mt7996-add-support-for-HW-ATF.patch
deleted file mode 100644
index dc84c18..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0067-mtk-wifi-mt76-mt7996-add-support-for-HW-ATF.patch
+++ /dev/null
@@ -1,675 +0,0 @@
-From f5372277983af2dd7cc3416563de0ad06ff6211c Mon Sep 17 00:00:00 2001
-From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
-Date: Mon, 11 Sep 2023 16:35:15 +0800
-Subject: [PATCH 067/116] mtk: wifi: mt76: mt7996: add support for HW-ATF
-
----
- mt7996/debugfs.c |  90 ++++++++++++++++
- mt7996/init.c    |  43 ++++++++
- mt7996/mac.c     |   6 ++
- mt7996/mcu.c     | 265 ++++++++++++++++++++++++++++++++++++++++++-----
- mt7996/mcu.h     |   1 +
- mt7996/mt7996.h  |  96 ++++++++++++++++-
- 6 files changed, 475 insertions(+), 26 deletions(-)
-
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index 8d63937..f8ba573 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -940,6 +940,91 @@ static const struct file_operations mt7996_efuse_ops = {
- 	.llseek = default_llseek,
- };
- 
-+static int
-+mt7996_vow_info_read(struct seq_file *s, void *data)
-+{
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	struct mt7996_vow_ctrl *vow = &dev->vow;
-+	int i;
-+
-+	seq_printf(s, "VoW ATF Configuration:\n");
-+	seq_printf(s, "ATF: %s\n", vow->atf_enable ? "enabled" : "disabled");
-+	seq_printf(s, "WATF: %s\n", vow->watf_enable ? "enabled" : "disabled");
-+	seq_printf(s, "Airtime Quantums (unit: 256 us)\n");
-+	for (i = 0; i < VOW_DRR_QUANTUM_NUM; ++i)
-+		seq_printf(s, "\tL%d: %hhu\n", i, vow->drr_quantum[i]);
-+	seq_printf(s, "Max Airtime Deficit: %hhu (unit: 256 us)\n", vow->max_deficit);
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_atf_enable_get(void *data, u64 *val)
-+{
-+	struct mt7996_phy *phy = data;
-+
-+	*val = phy->dev->vow.atf_enable;
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_atf_enable_set(void *data, u64 val)
-+{
-+	struct mt7996_phy *phy = data;
-+	struct mt7996_vow_ctrl *vow = &phy->dev->vow;
-+	int ret;
-+
-+	vow->max_deficit = val ? 64 : 1;
-+	ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
-+	if (ret)
-+		return ret;
-+
-+	vow->atf_enable = !!val;
-+	return mt7996_mcu_set_vow_feature_ctrl(phy);
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_atf_enable, mt7996_atf_enable_get,
-+	                 mt7996_atf_enable_set, "%llu\n");
-+
-+static int
-+mt7996_airtime_read(struct seq_file *s, void *data)
-+{
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	struct mt76_dev *mdev = &dev->mt76;
-+	struct mt7996_vow_sta_ctrl *vow;
-+	struct ieee80211_sta *sta;
-+	struct mt7996_sta *msta;
-+	struct mt76_wcid *wcid;
-+	struct mt76_vif *vif;
-+	u64 airtime;
-+	u16 i;
-+
-+	seq_printf(s, "VoW Airtime Information:\n");
-+	rcu_read_lock();
-+	for (i = 1; i < MT7996_WTBL_STA; ++i) {
-+		wcid = rcu_dereference(mdev->wcid[i]);
-+		if (!wcid || !wcid->sta)
-+			continue;
-+
-+		msta = container_of(wcid, struct mt7996_sta, wcid);
-+		sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
-+		vow = &msta->vow;
-+		vif = &msta->vif->mt76;
-+
-+		spin_lock_bh(&vow->lock);
-+		airtime = vow->tx_airtime;
-+		vow->tx_airtime = 0;
-+		spin_unlock_bh(&vow->lock);
-+
-+		seq_printf(s, "%pM WCID: %hu BandIdx: %hhu OmacIdx: 0x%hhx\tTxAirtime: %llu\n",
-+		           sta->addr, i, vif->band_idx, vif->omac_idx, airtime);
-+	}
-+	rcu_read_unlock();
-+
-+	return 0;
-+}
-+
- int mt7996_init_debugfs(struct mt7996_phy *phy)
- {
- 	struct mt7996_dev *dev = phy->dev;
-@@ -967,6 +1052,11 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
- 				    mt7996_twt_stats);
- 	debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval);
- 	debugfs_create_file("otp", 0400, dir, dev, &mt7996_efuse_ops);
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "vow_info", dir,
-+	                            mt7996_vow_info_read);
-+	debugfs_create_file("atf_enable", 0600, dir, phy, &fops_atf_enable);
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "airtime", dir,
-+	                            mt7996_airtime_read);
- 
- 	if (phy->mt76->cap.has_5ghz) {
- 		debugfs_create_u32("dfs_hw_pattern", 0400, dir,
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 8b7c278..c382ded 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -573,6 +573,37 @@ int mt7996_txbf_init(struct mt7996_dev *dev)
- 	return mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
- }
- 
-+static int mt7996_vow_init(struct mt7996_phy *phy)
-+{
-+	struct mt7996_vow_ctrl *vow = &phy->dev->vow;
-+	int ret;
-+
-+	vow->atf_enable = true;
-+	vow->watf_enable = false;
-+	vow->max_deficit = 64;
-+	vow->sch_type = VOW_SCH_TYPE_FOLLOW_POLICY;
-+	vow->sch_policy = VOW_SCH_POLICY_SRR;
-+
-+	vow->drr_quantum[0] = VOW_DRR_QUANTUM_L0;
-+	vow->drr_quantum[1] = VOW_DRR_QUANTUM_L1;
-+	vow->drr_quantum[2] = VOW_DRR_QUANTUM_L2;
-+	vow->drr_quantum[3] = VOW_DRR_QUANTUM_L3;
-+	vow->drr_quantum[4] = VOW_DRR_QUANTUM_L4;
-+	vow->drr_quantum[5] = VOW_DRR_QUANTUM_L5;
-+	vow->drr_quantum[6] = VOW_DRR_QUANTUM_L6;
-+	vow->drr_quantum[7] = VOW_DRR_QUANTUM_L7;
-+
-+	ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
-+	if (ret)
-+		return ret;
-+
-+	ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL);
-+	if (ret)
-+		return ret;
-+
-+	return mt7996_mcu_set_vow_feature_ctrl(phy);
-+}
-+
- static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
- 			       enum mt76_band_id band)
- {
-@@ -645,6 +676,12 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
- 	if (ret)
- 		goto error;
- 
-+	if (mt7996_vow_should_enable(dev)) {
-+		ret = mt7996_vow_init(phy);
-+		if (ret)
-+			goto error;
-+	}
-+
- 	ret = mt7996_init_debugfs(phy);
- 	if (ret)
- 		goto error;
-@@ -1483,6 +1520,12 @@ int mt7996_register_device(struct mt7996_dev *dev)
- 
- 	dev->recovery.hw_init_done = true;
- 
-+	if (mt7996_vow_should_enable(dev)) {
-+		ret = mt7996_vow_init(&dev->phy);
-+		if (ret)
-+			goto error;
-+	}
-+
- 	ret = mt7996_init_debugfs(&dev->phy);
- 	if (ret)
- 		goto error;
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 78e35aa..be56abe 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -103,6 +103,7 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
- 	};
- 	struct ieee80211_sta *sta;
- 	struct mt7996_sta *msta;
-+	struct mt7996_vow_sta_ctrl *vow;
- 	u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS];
- 	LIST_HEAD(sta_poll_list);
- 	int i;
-@@ -161,6 +162,7 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
- 
- 		sta = container_of((void *)msta, struct ieee80211_sta,
- 				   drv_priv);
-+		vow = &msta->vow;
- 		for (i = 0; i < IEEE80211_NUM_ACS; i++) {
- 			u8 q = mt76_connac_lmac_mapping(i);
- 			u32 tx_cur = tx_time[q];
-@@ -171,6 +173,10 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
- 				continue;
- 
- 			ieee80211_sta_register_airtime(sta, tid, tx_cur, rx_cur);
-+
-+			spin_lock_bh(&vow->lock);
-+			vow->tx_airtime += tx_cur;
-+			spin_unlock_bh(&vow->lock);
- 		}
- 
- 		/* get signal strength of resp frames (CTS/BA/ACK) */
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index b886644..ef30d8e 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -2225,34 +2225,37 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- }
- 
- static int
--mt7996_mcu_add_group(struct mt7996_dev *dev, struct ieee80211_vif *vif,
--		     struct ieee80211_sta *sta)
-+mt7996_mcu_sta_init_vow(struct mt7996_phy *phy, struct mt7996_sta *msta)
- {
--#define MT_STA_BSS_GROUP		1
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	struct mt7996_sta *msta;
--	struct {
--		u8 __rsv1[4];
-+	struct mt7996_vow_sta_ctrl *vow = &msta->vow;
-+	u8 omac_idx = msta->vif->mt76.omac_idx;
-+	int ret;
- 
--		__le16 tag;
--		__le16 len;
--		__le16 wlan_idx;
--		u8 __rsv2[2];
--		__le32 action;
--		__le32 val;
--		u8 __rsv3[8];
--	} __packed req = {
--		.tag = cpu_to_le16(UNI_VOW_DRR_CTRL),
--		.len = cpu_to_le16(sizeof(req) - 4),
--		.action = cpu_to_le32(MT_STA_BSS_GROUP),
--		.val = cpu_to_le32(mvif->mt76.idx % 16),
--	};
-+	/* Assignment of STA BSS group index aligns FW.
-+	 * Each band has its own BSS group bitmap space.
-+	 * 0: BSS 0
-+	 * 4..18: BSS 0x11..0x1f
-+	 */
-+	vow->bss_grp_idx = (omac_idx <= HW_BSSID_MAX)
-+	                   ? omac_idx
-+	                   : HW_BSSID_MAX + omac_idx - EXT_BSSID_START;
-+	vow->paused = false;
-+	vow->drr_quantum[IEEE80211_AC_VO] = VOW_DRR_QUANTUM_IDX0;
-+	vow->drr_quantum[IEEE80211_AC_VI] = VOW_DRR_QUANTUM_IDX1;
-+	vow->drr_quantum[IEEE80211_AC_BE] = VOW_DRR_QUANTUM_IDX2;
-+	vow->drr_quantum[IEEE80211_AC_BK] = VOW_DRR_QUANTUM_IDX2;
-+	vow->tx_airtime = 0;
-+	spin_lock_init(&vow->lock);
-+
-+	ret = mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_BSS_GROUP);
-+	if (ret)
-+		return ret;
- 
--	msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta;
--	req.wlan_idx = cpu_to_le16(msta->wcid.idx);
-+	ret = mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_PAUSE);
-+	if (ret)
-+		return ret;
- 
--	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req,
--				 sizeof(req), true);
-+	return mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_ALL);
- }
- 
- int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
-@@ -2308,7 +2311,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- 		mt7996_mcu_sta_bfee_tlv(dev, skb, vif, sta);
- 	}
- 
--	ret = mt7996_mcu_add_group(dev, vif, sta);
-+	ret = mt7996_mcu_sta_init_vow(mvif->phy, msta);
- 	if (ret) {
- 		dev_kfree_skb(skb);
- 		return ret;
-@@ -5185,6 +5188,218 @@ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
- 				 &req, sizeof(req), false);
- }
- 
-+int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
-+	                        enum vow_drr_ctrl_id id)
-+{
-+	struct mt7996_vow_sta_ctrl *vow = msta ? &msta->vow : NULL;
-+	u32 val = 0;
-+	struct {
-+		u8 __rsv1[4];
-+
-+		__le16 tag;
-+		__le16 len;
-+		__le16 wlan_idx;
-+		u8 band_idx;
-+		u8 wmm_idx;
-+		__le32 ctrl_id;
-+
-+		union {
-+			__le32 val;
-+			u8 drr_quantum[VOW_DRR_QUANTUM_NUM];
-+		};
-+
-+		u8 __rsv2[3];
-+		u8 omac_idx;
-+	} __packed req = {
-+		.tag = cpu_to_le16(UNI_VOW_DRR_CTRL),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.wlan_idx = cpu_to_le16(msta ? msta->wcid.idx : 0),
-+		.band_idx = phy->mt76->band_idx,
-+		.wmm_idx = msta ? msta->vif->mt76.wmm_idx : 0,
-+		.ctrl_id = cpu_to_le32(id),
-+		.omac_idx = msta ? msta->vif->mt76.omac_idx : 0
-+	};
-+
-+	switch (id) {
-+	case VOW_DRR_CTRL_STA_ALL:
-+		val |= FIELD_PREP(MT7996_DRR_STA_BSS_GRP_MASK, vow->bss_grp_idx);
-+		val |= FIELD_PREP(MT7996_DRR_STA_AC0_QNTM_MASK, vow->drr_quantum[IEEE80211_AC_BK]);
-+		val |= FIELD_PREP(MT7996_DRR_STA_AC1_QNTM_MASK, vow->drr_quantum[IEEE80211_AC_BE]);
-+		val |= FIELD_PREP(MT7996_DRR_STA_AC2_QNTM_MASK, vow->drr_quantum[IEEE80211_AC_VI]);
-+		val |= FIELD_PREP(MT7996_DRR_STA_AC3_QNTM_MASK, vow->drr_quantum[IEEE80211_AC_VO]);
-+		req.val = cpu_to_le32(val);
-+		break;
-+	case VOW_DRR_CTRL_STA_BSS_GROUP:
-+		req.val = cpu_to_le32(vow->bss_grp_idx);
-+		break;
-+	case VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND:
-+		req.val = cpu_to_le32(phy->dev->vow.max_deficit);
-+		break;
-+	case VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL:
-+		memcpy(req.drr_quantum, phy->dev->vow.drr_quantum, VOW_DRR_QUANTUM_NUM);
-+		break;
-+	case VOW_DRR_CTRL_STA_PAUSE:
-+		req.val = cpu_to_le32(vow->paused);
-+		break;
-+	default:
-+		dev_err(phy->dev->mt76.dev, "Unknown VoW DRR Control ID: %u\n", id);
-+		return -EINVAL;
-+	}
-+
-+	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(VOW),
-+	                         &req, sizeof(req), true);
-+}
-+
-+int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy)
-+{
-+	struct mt7996_vow_ctrl *vow = &phy->dev->vow;
-+	struct {
-+		u8 __rsv1[4];
-+
-+		__le16 tag;
-+		__le16 len;
-+
-+		/* DW0 */
-+		__le16 apply_bwc_enable_per_grp;
-+		__le16 apply_bwc_refill_period		: 1;
-+		__le16 __rsv2				: 3;
-+		__le16 apply_band1_search_rule		: 1;
-+		__le16 apply_band0_search_rule		: 1;
-+		__le16 __rsv3				: 3;
-+		__le16 apply_watf_enable		: 1;
-+		__le16 __rsv4				: 2;
-+		__le16 apply_grp_no_change_in_txop	: 1;
-+		__le16 apply_atf_enable			: 1;
-+		__le16 apply_bwc_token_refill_enable	: 1;
-+		__le16 apply_bwc_enable			: 1;
-+
-+		/* DW1 */
-+		__le16 apply_bwc_check_time_token_per_grp;
-+		__le16 __rsv5;
-+
-+		/* DW2 */
-+		__le16 apply_bwc_check_len_token_per_grp;
-+		__le16 __rsv6;
-+
-+		/* DW3 */
-+		u8 band_idx;
-+		u8 __rsv7[3];
-+
-+		/* DW4 */
-+		__le32 __rsv8;
-+
-+		/* DW5 */
-+		__le16 bwc_enable_per_grp;
-+		__le16 bwc_refill_period	: 3;
-+		__le16 __rsv9			: 1;
-+		__le16 band1_search_rule	: 1;
-+		__le16 band0_search_rule	: 1;
-+		__le16 __rsv10			: 3;
-+		__le16 watf_enable		: 1;
-+		__le16 __rsv11			: 2;
-+		__le16 grp_no_change_in_txop	: 1;
-+		__le16 atf_enable		: 1;
-+		__le16 bwc_token_refill_enable	: 1;
-+		__le16 bwc_enable		: 1;
-+
-+		/* DW6 */
-+		__le16 bwc_check_time_token_per_grp;
-+		__le16 __rsv12;
-+
-+		/* DW7 */
-+		__le16 bwc_check_len_token_per_grp;
-+		__le16 __rsv13;
-+
-+		/* DW8 */
-+		__le32 apply_atf_rts_sta_lock		: 1;
-+		__le32 atf_rts_sta_lock			: 1;
-+		__le32 apply_atf_keep_quantum		: 1;
-+		__le32 atf_keep_quantum			: 1;
-+		__le32 apply_tx_cnt_mode_ctrl		: 1;
-+		__le32 tx_cnt_mode_ctrl			: 4;
-+		__le32 apply_tx_measure_mode_enable	: 1;
-+		__le32 tx_measure_mode_enable		: 1;
-+		__le32 apply_backoff_ctrl		: 1;
-+		__le32 backoff_bound_enable		: 1;
-+		__le32 backoff_bound			: 5;
-+		__le32 apply_atf_rts_fail_charge	: 1;
-+		__le32 atf_rts_fail_charge		: 1;
-+		__le32 apply_zero_eifs			: 1;
-+		__le32 zero_eifs			: 1;
-+		__le32 apply_rx_rifs_enable		: 1;
-+		__le32 rx_rifs_enable			: 1;
-+		__le32 apply_vow_ctrl			: 1;
-+		__le32 vow_ctrl_val			: 1;
-+		__le32 vow_ctrl_bit			: 5;
-+		__le32 __rsv14				: 1;
-+
-+		/* DW9 */
-+		__le32 apply_spl_sta_num	: 1;
-+		__le32 spl_sta_num		: 3;
-+		__le32 dbg_lvl			: 2;
-+		__le32 apply_atf_sch_ctrl	: 1;
-+		__le32 atf_sch_type		: 2;
-+		__le32 atf_sch_policy		: 2;
-+		__le32 __rsv15			: 21;
-+	} __packed req = {
-+		.tag = cpu_to_le16(UNI_VOW_FEATURE_CTRL),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		/* DW0 */
-+		.apply_bwc_enable_per_grp = cpu_to_le16(0xffff),
-+		.apply_bwc_refill_period = true,
-+		.apply_band1_search_rule = true,
-+		.apply_band0_search_rule = true,
-+		.apply_watf_enable = true,
-+		.apply_grp_no_change_in_txop = true,
-+		.apply_atf_enable = true,
-+		.apply_bwc_token_refill_enable = true,
-+		.apply_bwc_enable = true,
-+		/* DW1 */
-+		.apply_bwc_check_time_token_per_grp = cpu_to_le16(0xffff),
-+		/* DW2 */
-+		.apply_bwc_check_len_token_per_grp = cpu_to_le16(0xffff),
-+		/* DW3 */
-+		.band_idx = phy->mt76->band_idx,
-+		/* DW5 */
-+		.bwc_enable_per_grp = cpu_to_le16(0xffff),
-+		.bwc_refill_period = VOW_REFILL_PERIOD_32US,
-+		.band1_search_rule = VOW_SEARCH_WMM_FIRST,
-+		.band0_search_rule = VOW_SEARCH_WMM_FIRST,
-+		.watf_enable = vow->watf_enable,
-+		.grp_no_change_in_txop = true,
-+		.atf_enable = vow->atf_enable,
-+		.bwc_token_refill_enable = true,
-+		.bwc_enable = false,
-+		/* DW6 */
-+		.bwc_check_time_token_per_grp = cpu_to_le16(0x0),
-+		/* DW7 */
-+		.bwc_check_len_token_per_grp = cpu_to_le16(0x0),
-+		/* DW8 */
-+		.apply_atf_rts_sta_lock = false,
-+		.apply_atf_keep_quantum = true,
-+		.atf_keep_quantum = true,
-+		.apply_tx_cnt_mode_ctrl = false,
-+		.apply_tx_measure_mode_enable = false,
-+		.apply_backoff_ctrl = false,
-+		.apply_atf_rts_fail_charge = false,
-+		.apply_zero_eifs = false,
-+		.apply_rx_rifs_enable = false,
-+		.apply_vow_ctrl = true,
-+		.vow_ctrl_val = true,
-+		/* Reset DRR table when SER occurs. */
-+		.vow_ctrl_bit = 26,
-+		/* DW9 */
-+		.apply_spl_sta_num = false,
-+		.dbg_lvl = 0,
-+		.apply_atf_sch_ctrl = true,
-+		.atf_sch_type = vow->sch_type,
-+		.atf_sch_policy = vow->sch_policy
-+	};
-+
-+	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(VOW),
-+	                         &req, sizeof(req), true);
-+}
-+
- #ifdef CONFIG_MTK_VENDOR
- void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
- {
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 3c4ff7a..700330a 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -965,6 +965,7 @@ enum {
- 
- enum {
- 	UNI_VOW_DRR_CTRL,
-+	UNI_VOW_FEATURE_CTRL,
- 	UNI_VOW_RX_AT_AIRTIME_EN = 0x0b,
- 	UNI_VOW_RX_AT_AIRTIME_CLR_EN = 0x0e,
- 	UNI_VOW_RED_ENABLE = 0x18,
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 453d224..c95c12b 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -114,6 +114,12 @@
- #define MT7996_RX_MSDU_PAGE_SIZE	(128 + \
- 					 SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
- 
-+#define MT7996_DRR_STA_BSS_GRP_MASK	GENMASK(5, 0)
-+#define MT7996_DRR_STA_AC0_QNTM_MASK	GENMASK(10, 8)
-+#define MT7996_DRR_STA_AC1_QNTM_MASK	GENMASK(14, 12)
-+#define MT7996_DRR_STA_AC2_QNTM_MASK	GENMASK(18, 16)
-+#define MT7996_DRR_STA_AC3_QNTM_MASK	GENMASK(22, 20)
-+
- struct mt7996_vif;
- struct mt7996_sta;
- struct mt7996_dfs_pulse;
-@@ -215,6 +221,81 @@ enum mt7996_dpd_ch_num {
- 	DPD_CH_NUM_TYPE_MAX,
- };
- 
-+enum {
-+	VOW_SEARCH_AC_FIRST,
-+	VOW_SEARCH_WMM_FIRST
-+};
-+
-+enum {
-+	VOW_REFILL_PERIOD_1US,
-+	VOW_REFILL_PERIOD_2US,
-+	VOW_REFILL_PERIOD_4US,
-+	VOW_REFILL_PERIOD_8US,
-+	VOW_REFILL_PERIOD_16US,
-+	VOW_REFILL_PERIOD_32US,
-+	VOW_REFILL_PERIOD_64US,
-+	VOW_REFILL_PERIOD_128US
-+};
-+
-+/* Default DRR airtime quantum of each level */
-+enum {
-+	VOW_DRR_QUANTUM_L0 = 6,
-+	VOW_DRR_QUANTUM_L1 = 12,
-+	VOW_DRR_QUANTUM_L2 = 16,
-+	VOW_DRR_QUANTUM_L3 = 20,
-+	VOW_DRR_QUANTUM_L4 = 24,
-+	VOW_DRR_QUANTUM_L5 = 28,
-+	VOW_DRR_QUANTUM_L6 = 32,
-+	VOW_DRR_QUANTUM_L7 = 36
-+};
-+
-+enum {
-+	VOW_DRR_QUANTUM_IDX0,
-+	VOW_DRR_QUANTUM_IDX1,
-+	VOW_DRR_QUANTUM_IDX2,
-+	VOW_DRR_QUANTUM_IDX3,
-+	VOW_DRR_QUANTUM_IDX4,
-+	VOW_DRR_QUANTUM_IDX5,
-+	VOW_DRR_QUANTUM_IDX6,
-+	VOW_DRR_QUANTUM_IDX7,
-+	VOW_DRR_QUANTUM_NUM
-+};
-+
-+enum {
-+	VOW_SCH_TYPE_FOLLOW_POLICY,
-+	VOW_SCH_TYPE_FOLLOW_HW
-+};
-+
-+enum {
-+	VOW_SCH_POLICY_SRR, /* Shared Round-Robin */
-+	VOW_SCH_POLICY_WRR /* Weighted Round-Robin */
-+};
-+
-+enum vow_drr_ctrl_id {
-+	VOW_DRR_CTRL_STA_ALL,
-+	VOW_DRR_CTRL_STA_BSS_GROUP,
-+	VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND = 0x10,
-+	VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL = 0x28,
-+	VOW_DRR_CTRL_STA_PAUSE = 0x30
-+};
-+
-+struct mt7996_vow_ctrl {
-+	bool atf_enable;
-+	bool watf_enable;
-+	u8 drr_quantum[VOW_DRR_QUANTUM_NUM];
-+	u8 max_deficit;
-+	u8 sch_type;
-+	u8 sch_policy;
-+};
-+
-+struct mt7996_vow_sta_ctrl {
-+	bool paused;
-+	u8 bss_grp_idx;
-+	u8 drr_quantum[IEEE80211_NUM_ACS];
-+	u64 tx_airtime;
-+	spinlock_t lock;
-+};
-+
- struct mt7996_sta {
- 	struct mt76_wcid wcid; /* must be first */
- 
-@@ -234,6 +315,8 @@ struct mt7996_sta {
- 		u8 flowid_mask;
- 		struct mt7996_twt_flow flow[MT7996_MAX_STA_TWT_AGRT];
- 	} twt;
-+
-+	struct mt7996_vow_sta_ctrl vow;
- };
- 
- struct mt7996_vif {
-@@ -499,6 +582,7 @@ struct mt7996_dev {
- 
- 	u8 wtbl_size_group;
- 
-+	struct mt7996_vow_ctrl vow;
- #ifdef CONFIG_MTK_DEBUG
- 	u16 wlan_idx;
- 	struct {
-@@ -739,10 +823,12 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy);
- #ifdef CONFIG_NL80211_TESTMODE
- void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
- #endif
--int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
- int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
- void mt7996_mcu_scs_sta_poll(struct work_struct *work);
- int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable);
-+int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
-+	                        enum vow_drr_ctrl_id id);
-+int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy);
- 
- static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
- {
-@@ -792,6 +878,14 @@ static inline u16 mt7996_rx_chainmask(struct mt7996_phy *phy)
- 	return tx_chainmask | (BIT(fls(tx_chainmask)) * phy->has_aux_rx);
- }
- 
-+static inline bool
-+mt7996_vow_should_enable(struct mt7996_dev *dev)
-+{
-+	return !wiphy_ext_feature_isset(mt76_hw(dev)->wiphy,
-+	                                NL80211_EXT_FEATURE_AIRTIME_FAIRNESS) ||
-+	       mtk_wed_device_active(&dev->mt76.mmio.wed);
-+}
-+
- void mt7996_mac_init(struct mt7996_dev *dev);
- u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw);
- bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0068-mtk-mt76-find-rx-token-by-physical-address.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0068-mtk-mt76-find-rx-token-by-physical-address.patch
new file mode 100644
index 0000000..1c5a1fb
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0068-mtk-mt76-find-rx-token-by-physical-address.patch
@@ -0,0 +1,64 @@
+From d19b337c221ed6c72d9733c3206bcd6f24b3a774 Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Wed, 19 Jul 2023 10:55:09 +0800
+Subject: [PATCH 068/199] mtk: mt76: find rx token by physical address
+
+The token id in RxDMAD may be incorrect when it is not the last frame in
+WED HW. Lookup correct token id by physical address in sdp0.
+Add len == 0 check to drop garbage frames
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ dma.c | 27 +++++++++++++++++++++++++--
+ 1 file changed, 25 insertions(+), 2 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index e5be891c..1021b3e5 100644
+--- a/dma.c
++++ b/dma.c
+@@ -446,9 +446,32 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ 	mt76_dma_should_drop_buf(drop, ctrl, buf1, desc_info);
+ 
+ 	if (mt76_queue_is_wed_rx(q)) {
++		u32 id, find = 0;
+ 		u32 token = FIELD_GET(MT_DMA_CTL_TOKEN, buf1);
+-		struct mt76_rxwi_cache *r = mt76_rx_token_release(dev, token);
++		struct mt76_rxwi_cache *r;
++
++		if (*more) {
++			spin_lock_bh(&dev->rx_token_lock);
++
++			idr_for_each_entry(&dev->rx_token, r, id) {
++				if (r->dma_addr == le32_to_cpu(desc->buf0)) {
++					find = 1;
++					token = id;
++
++					/* Write correct id back to DMA*/
++					u32p_replace_bits(&buf1, id,
++							  MT_DMA_CTL_TOKEN);
++					WRITE_ONCE(desc->buf1, cpu_to_le32(buf1));
++					break;
++				}
++			}
+ 
++			spin_unlock_bh(&dev->rx_token_lock);
++			if (!find)
++				return NULL;
++		}
++
++		r = mt76_rx_token_release(dev, token);
+ 		if (!r)
+ 			return NULL;
+ 
+@@ -902,7 +925,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+ 		if (!data)
+ 			break;
+ 
+-		if (drop)
++		if (drop || (len == 0))
+ 			goto free_frag;
+ 
+ 		if (q->rx_head)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0068-mtk-wifi-mt76-mt7996-wed-add-SER0.5-support-w-wed3.0.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0068-mtk-wifi-mt76-mt7996-wed-add-SER0.5-support-w-wed3.0.patch
deleted file mode 100644
index 8865ef4..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0068-mtk-wifi-mt76-mt7996-wed-add-SER0.5-support-w-wed3.0.patch
+++ /dev/null
@@ -1,397 +0,0 @@
-From 21fac5083443173762f0bda68768acde71e7153e Mon Sep 17 00:00:00 2001
-From: "sujuan.chen" <sujuan.chen@mediatek.com>
-Date: Thu, 12 Oct 2023 10:04:54 +0800
-Subject: [PATCH 068/116] mtk: wifi: mt76: mt7996: wed: add SER0.5 support w/
- wed3.0
-
-Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
----
- dma.c           |   9 ++--
- dma.h           |   4 +-
- mt76.h          |  14 ++++--
- mt792x_dma.c    |   6 +--
- mt7996/dma.c    |  20 ++++++--
- mt7996/init.c   | 127 +++++++++++++++++++++++++++++++-----------------
- mt7996/mac.c    |  25 ++++++++++
- mt7996/mt7996.h |   1 +
- wed.c           |   4 +-
- 9 files changed, 146 insertions(+), 64 deletions(-)
-
-diff --git a/dma.c b/dma.c
-index da21f64..e23b744 100644
---- a/dma.c
-+++ b/dma.c
-@@ -218,9 +218,9 @@ void __mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q,
- 	mt76_dma_sync_idx(dev, q);
- }
- 
--void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q)
-+void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
- {
--	__mt76_dma_queue_reset(dev, q, true);
-+	__mt76_dma_queue_reset(dev, q, reset);
- }
- 
- static int
-@@ -540,7 +540,8 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
- 	if (!q->queued)
- 		return NULL;
- 
--	if (mt76_queue_is_wed_rro_data(q))
-+	if (mt76_queue_is_wed_rro_data(q) ||
-+	    mt76_queue_is_wed_rro_msdu_pg(q))
- 		return NULL;
- 
- 	if (!mt76_queue_is_wed_rro_ind(q)) {
-@@ -792,7 +793,7 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
- 			return 0;
- 	}
- 
--	mt76_dma_queue_reset(dev, q);
-+	mt76_dma_queue_reset(dev, q, true);
- 
- 	return 0;
- }
-diff --git a/dma.h b/dma.h
-index 1de5a2b..3a8c2e5 100644
---- a/dma.h
-+++ b/dma.h
-@@ -83,12 +83,12 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
- 		     bool allow_direct);
- void __mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q,
- 			    bool reset_idx);
--void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q);
-+void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q, bool reset);
- 
- static inline void
- mt76_dma_reset_tx_queue(struct mt76_dev *dev, struct mt76_queue *q)
- {
--	dev->queue_ops->reset_q(dev, q);
-+	dev->queue_ops->reset_q(dev, q, true);
- 	if (mtk_wed_device_active(&dev->mmio.wed))
- 		mt76_wed_dma_setup(dev, q, true);
- }
-diff --git a/mt76.h b/mt76.h
-index 37ef803..9f1f7fc 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -301,7 +301,7 @@ struct mt76_queue_ops {
- 
- 	void (*kick)(struct mt76_dev *dev, struct mt76_queue *q);
- 
--	void (*reset_q)(struct mt76_dev *dev, struct mt76_queue *q);
-+	void (*reset_q)(struct mt76_dev *dev, struct mt76_queue *q, bool reset);
- };
- 
- enum mt76_phy_type {
-@@ -1736,8 +1736,13 @@ static inline bool mt76_queue_is_wed_rro_ind(struct mt76_queue *q)
- static inline bool mt76_queue_is_wed_rro_data(struct mt76_queue *q)
- {
- 	return mt76_queue_is_wed_rro(q) &&
--	       (FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_DATA ||
--		FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_MSDU_PG);
-+	       (FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_DATA);
-+}
-+
-+static inline bool mt76_queue_is_wed_rro_msdu_pg(struct mt76_queue *q)
-+{
-+	return mt76_queue_is_wed_rro(q) &&
-+	       (FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_MSDU_PG);
- }
- 
- static inline bool mt76_queue_is_wed_rx(struct mt76_queue *q)
-@@ -1746,7 +1751,8 @@ static inline bool mt76_queue_is_wed_rx(struct mt76_queue *q)
- 		return false;
- 
- 	return FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX ||
--	       mt76_queue_is_wed_rro_ind(q) || mt76_queue_is_wed_rro_data(q);
-+	       mt76_queue_is_wed_rro_ind(q) || mt76_queue_is_wed_rro_data(q) ||
-+	       mt76_queue_is_wed_rro_msdu_pg(q);
- 
- }
- 
-diff --git a/mt792x_dma.c b/mt792x_dma.c
-index 5cc2d59..c224bcc 100644
---- a/mt792x_dma.c
-+++ b/mt792x_dma.c
-@@ -181,13 +181,13 @@ mt792x_dma_reset(struct mt792x_dev *dev, bool force)
- 
- 	/* reset hw queues */
- 	for (i = 0; i < __MT_TXQ_MAX; i++)
--		mt76_queue_reset(dev, dev->mphy.q_tx[i]);
-+		mt76_queue_reset(dev, dev->mphy.q_tx[i], true);
- 
- 	for (i = 0; i < __MT_MCUQ_MAX; i++)
--		mt76_queue_reset(dev, dev->mt76.q_mcu[i]);
-+		mt76_queue_reset(dev, dev->mt76.q_mcu[i], true);
- 
- 	mt76_for_each_q_rx(&dev->mt76, i)
--		mt76_queue_reset(dev, &dev->mt76.q_rx[i]);
-+		mt76_queue_reset(dev, &dev->mt76.q_rx[i], true);
- 
- 	mt76_tx_status_check(&dev->mt76, true);
- 
-diff --git a/mt7996/dma.c b/mt7996/dma.c
-index 5d85e9e..d9e1b17 100644
---- a/mt7996/dma.c
-+++ b/mt7996/dma.c
-@@ -711,21 +711,31 @@ void mt7996_dma_reset(struct mt7996_dev *dev, bool force)
- 	}
- 
- 	for (i = 0; i < __MT_MCUQ_MAX; i++)
--		mt76_queue_reset(dev, dev->mt76.q_mcu[i]);
-+		mt76_queue_reset(dev, dev->mt76.q_mcu[i], true);
- 
- 	mt76_for_each_q_rx(&dev->mt76, i) {
--		if (mtk_wed_device_active(&dev->mt76.mmio.wed))
-+		if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
- 			if (mt76_queue_is_wed_rro(&dev->mt76.q_rx[i]) ||
--			    mt76_queue_is_wed_tx_free(&dev->mt76.q_rx[i]))
-+			    mt76_queue_is_wed_tx_free(&dev->mt76.q_rx[i])) {
-+				if (force && mt76_queue_is_wed_rro_data(&dev->mt76.q_rx[i]))
-+					mt76_queue_reset(dev, &dev->mt76.q_rx[i], false);
- 				continue;
-+			}
-+		}
- 
--		mt76_queue_reset(dev, &dev->mt76.q_rx[i]);
-+		mt76_queue_reset(dev, &dev->mt76.q_rx[i], true);
- 	}
- 
- 	mt76_tx_status_check(&dev->mt76, true);
- 
--	mt76_for_each_q_rx(&dev->mt76, i)
-+	mt76_for_each_q_rx(&dev->mt76, i) {
-+		if (mtk_wed_device_active(&dev->mt76.mmio.wed) && force &&
-+		    (mt76_queue_is_wed_rro_ind(&dev->mt76.q_rx[i]) ||
-+		     mt76_queue_is_wed_rro_msdu_pg(&dev->mt76.q_rx[i])))
-+			continue;
-+
- 		mt76_queue_rx_reset(dev, i);
-+	}
- 
- 	mt7996_dma_enable(dev, !force);
- }
-diff --git a/mt7996/init.c b/mt7996/init.c
-index c382ded..ff629e8 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -735,11 +735,91 @@ void mt7996_wfsys_reset(struct mt7996_dev *dev)
- 	msleep(20);
- }
- 
--static int mt7996_wed_rro_init(struct mt7996_dev *dev)
-+void mt7996_rro_hw_init(struct mt7996_dev *dev)
- {
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
- 	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
- 	u32 reg = MT_RRO_ADDR_ELEM_SEG_ADDR0;
-+	int i;
-+
-+	if (!dev->has_rro)
-+		return;
-+
-+	if (is_mt7992(&dev->mt76)) {
-+		/* set emul 3.0 function */
-+		mt76_wr(dev, MT_RRO_3_0_EMU_CONF,
-+			MT_RRO_3_0_EMU_CONF_EN_MASK);
-+
-+		mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE0,
-+			dev->wed_rro.addr_elem[0].phy_addr);
-+	} else {
-+		/* TODO: remove line after WM has set */
-+		mt76_clear(dev, WF_RRO_AXI_MST_CFG, WF_RRO_AXI_MST_CFG_DIDX_OK);
-+
-+		/* setup BA bitmap cache address */
-+		mt76_wr(dev, MT_RRO_BA_BITMAP_BASE0,
-+			dev->wed_rro.ba_bitmap[0].phy_addr);
-+		mt76_wr(dev, MT_RRO_BA_BITMAP_BASE1, 0);
-+		mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT0,
-+			dev->wed_rro.ba_bitmap[1].phy_addr);
-+		mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT1, 0);
-+
-+		/* setup Address element address */
-+		for (i = 0; i < ARRAY_SIZE(dev->wed_rro.addr_elem); i++) {
-+			mt76_wr(dev, reg, dev->wed_rro.addr_elem[i].phy_addr >> 4);
-+			reg += 4;
-+		}
-+
-+		/* setup Address element address - separate address segment mode */
-+		mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE1,
-+			MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE);
-+	}
-+	wed->wlan.ind_cmd.win_size = ffs(MT7996_RRO_WINDOW_MAX_LEN) - 6;
-+	if (is_mt7996(&dev->mt76))
-+		wed->wlan.ind_cmd.particular_sid = MT7996_RRO_MAX_SESSION;
-+	else
-+		wed->wlan.ind_cmd.particular_sid = 1;
-+	wed->wlan.ind_cmd.particular_se_phys = dev->wed_rro.session.phy_addr;
-+	wed->wlan.ind_cmd.se_group_nums = MT7996_RRO_ADDR_ELEM_LEN;
-+	wed->wlan.ind_cmd.ack_sn_addr = MT_RRO_ACK_SN_CTRL;
-+
-+	mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE0, 0x15010e00);
-+	mt76_set(dev, MT_RRO_IND_CMD_SIGNATURE_BASE1,
-+		 MT_RRO_IND_CMD_SIGNATURE_BASE1_EN);
-+
-+	/* particular session configure */
-+	/* use max session idx + 1 as particular session id */
-+	mt76_wr(dev, MT_RRO_PARTICULAR_CFG0, dev->wed_rro.session.phy_addr);
-+
-+	if (is_mt7992(&dev->mt76)) {
-+		reg = MT_RRO_MSDU_PG_SEG_ADDR0;
-+
-+		mt76_set(dev, MT_RRO_3_1_GLOBAL_CONFIG,
-+			 MT_RRO_3_1_GLOBAL_CONFIG_INTERLEAVE_EN);
-+
-+		/* setup Msdu page address */
-+		for (i = 0; i < MT7996_RRO_MSDU_PG_CR_CNT; i++) {
-+			mt76_wr(dev, reg, dev->wed_rro.msdu_pg[i].phy_addr >> 4);
-+			reg += 4;
-+		}
-+		mt76_wr(dev, MT_RRO_PARTICULAR_CFG1,
-+			MT_RRO_PARTICULAR_CONFG_EN |
-+			FIELD_PREP(MT_RRO_PARTICULAR_SID, 1));
-+	} else {
-+		mt76_wr(dev, MT_RRO_PARTICULAR_CFG1,
-+			MT_RRO_PARTICULAR_CONFG_EN |
-+			FIELD_PREP(MT_RRO_PARTICULAR_SID, MT7996_RRO_MAX_SESSION));
-+	}
-+	/* interrupt enable */
-+	mt76_wr(dev, MT_RRO_HOST_INT_ENA,
-+		MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA);
-+#endif
-+}
-+
-+static int mt7996_wed_rro_init(struct mt7996_dev *dev)
-+{
-+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
-+	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
- 	struct mt7996_wed_rro_addr *addr;
- 	void *ptr;
- 	int i;
-@@ -799,50 +879,9 @@ static int mt7996_wed_rro_init(struct mt7996_dev *dev)
- 		addr++;
- 	}
- 
--	/* rro hw init */
--	/* TODO: remove line after WM has set */
--	mt76_clear(dev, WF_RRO_AXI_MST_CFG, WF_RRO_AXI_MST_CFG_DIDX_OK);
--
--	/* setup BA bitmap cache address */
--	mt76_wr(dev, MT_RRO_BA_BITMAP_BASE0,
--		dev->wed_rro.ba_bitmap[0].phy_addr);
--	mt76_wr(dev, MT_RRO_BA_BITMAP_BASE1, 0);
--	mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT0,
--		dev->wed_rro.ba_bitmap[1].phy_addr);
--	mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT1, 0);
--
--	/* setup Address element address */
--	for (i = 0; i < ARRAY_SIZE(dev->wed_rro.addr_elem); i++) {
--		mt76_wr(dev, reg, dev->wed_rro.addr_elem[i].phy_addr >> 4);
--		reg += 4;
--	}
--
--	/* setup Address element address - separate address segment mode */
--	mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE1,
--		MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE);
--
--	wed->wlan.ind_cmd.win_size = ffs(MT7996_RRO_WINDOW_MAX_LEN) - 6;
--	wed->wlan.ind_cmd.particular_sid = MT7996_RRO_MAX_SESSION;
--	wed->wlan.ind_cmd.particular_se_phys = dev->wed_rro.session.phy_addr;
--	wed->wlan.ind_cmd.se_group_nums = MT7996_RRO_ADDR_ELEM_LEN;
--	wed->wlan.ind_cmd.ack_sn_addr = MT_RRO_ACK_SN_CTRL;
--
--	mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE0, 0x15010e00);
--	mt76_set(dev, MT_RRO_IND_CMD_SIGNATURE_BASE1,
--		 MT_RRO_IND_CMD_SIGNATURE_BASE1_EN);
--
--	/* particular session configure */
--	/* use max session idx + 1 as particular session id */
--	mt76_wr(dev, MT_RRO_PARTICULAR_CFG0, dev->wed_rro.session.phy_addr);
--	mt76_wr(dev, MT_RRO_PARTICULAR_CFG1,
--		MT_RRO_PARTICULAR_CONFG_EN |
--		FIELD_PREP(MT_RRO_PARTICULAR_SID, MT7996_RRO_MAX_SESSION));
--
--	/* interrupt enable */
--	mt76_wr(dev, MT_RRO_HOST_INT_ENA,
--		MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA);
--
- 	/* rro ind cmd queue init */
-+	mt7996_rro_hw_init(dev);
-+
- 	return mt7996_dma_rro_init(dev);
- #else
- 	return 0;
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index be56abe..2ad9f48 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1764,6 +1764,31 @@ mt7996_mac_restart(struct mt7996_dev *dev)
- 	if (ret)
- 		goto out;
- 
-+	if (mtk_wed_device_active(&dev->mt76.mmio.wed) && dev->has_rro) {
-+		u32 wed_irq_mask = dev->mt76.mmio.irqmask |
-+				   MT_INT_RRO_RX_DONE |
-+				   MT_INT_TX_DONE_BAND2;
-+
-+		mt7996_rro_hw_init(dev);
-+		mt76_for_each_q_rx(&dev->mt76, i) {
-+			if (mt76_queue_is_wed_rro_ind(&dev->mt76.q_rx[i]) ||
-+			    mt76_queue_is_wed_rro_msdu_pg(&dev->mt76.q_rx[i]))
-+				mt76_queue_rx_reset(dev, i);
-+		}
-+
-+		mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask);
-+		mtk_wed_device_start_hwrro(&dev->mt76.mmio.wed, wed_irq_mask, false);
-+		mt7996_irq_enable(dev, wed_irq_mask);
-+		mt7996_irq_disable(dev, 0);
-+	}
-+
-+	if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2)) {
-+		mt76_wr(dev, MT_INT_PCIE1_MASK_CSR,
-+			MT_INT_TX_RX_DONE_EXT);
-+		mtk_wed_device_start(&dev->mt76.mmio.wed_hif2,
-+				     MT_INT_TX_RX_DONE_EXT);
-+	}
-+
- 	/* set the necessary init items */
- 	ret = mt7996_mcu_set_eeprom(dev);
- 	if (ret)
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index c95c12b..4090fad 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -718,6 +718,7 @@ extern const struct mt76_testmode_ops mt7996_testmode_ops;
- struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
- 				     void __iomem *mem_base, u32 device_id);
- void mt7996_wfsys_reset(struct mt7996_dev *dev);
-+void mt7996_rro_hw_init(struct mt7996_dev *dev);
- irqreturn_t mt7996_irq_handler(int irq, void *dev_instance);
- u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
- int mt7996_register_device(struct mt7996_dev *dev);
-diff --git a/wed.c b/wed.c
-index 1c6d53c..61a6bad 100644
---- a/wed.c
-+++ b/wed.c
-@@ -155,7 +155,7 @@ int mt76_wed_dma_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
- 	case MT76_WED_Q_TXFREE:
- 		/* WED txfree queue needs ring to be initialized before setup */
- 		q->flags = 0;
--		mt76_dma_queue_reset(dev, q);
-+		mt76_dma_queue_reset(dev, q, true);
- 		mt76_dma_rx_fill(dev, q, false);
- 
- 		ret = mtk_wed_device_txfree_ring_setup(q->wed, q->regs);
-@@ -184,7 +184,7 @@ int mt76_wed_dma_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
- 		break;
- 	case MT76_WED_RRO_Q_IND:
- 		q->flags &= ~MT_QFLAG_WED;
--		mt76_dma_queue_reset(dev, q);
-+		mt76_dma_queue_reset(dev, q, true);
- 		mt76_dma_rx_fill(dev, q, false);
- 		mtk_wed_device_ind_rx_ring_setup(q->wed, q->regs);
- 		break;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0069-mtk-mt76-mt7996-add-dma-mask-limitation.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0069-mtk-mt76-mt7996-add-dma-mask-limitation.patch
new file mode 100644
index 0000000..1a394f6
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0069-mtk-mt76-mt7996-add-dma-mask-limitation.patch
@@ -0,0 +1,57 @@
+From 71b485441f2bc88349b4d69022965c85560ae52b Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Thu, 20 Jul 2023 10:25:50 +0800
+Subject: [PATCH 069/199] mtk: mt76: mt7996: add dma mask limitation
+
+Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
+---
+ dma.c | 4 ++--
+ wed.c | 4 ++--
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index 1021b3e5..da21f641 100644
+--- a/dma.c
++++ b/dma.c
+@@ -488,7 +488,7 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ 		} else {
+ 			struct mt76_queue_buf qbuf;
+ 
+-			buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
++			buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC | GFP_DMA32);
+ 			if (!buf)
+ 				return NULL;
+ 
+@@ -711,7 +711,7 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
+ 		if (mt76_queue_is_wed_rro_ind(q))
+ 			goto done;
+ 
+-		buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
++		buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC | GFP_DMA32);
+ 		if (!buf)
+ 			break;
+ 
+diff --git a/wed.c b/wed.c
+index 0a0b5c05..1c6d53c8 100644
+--- a/wed.c
++++ b/wed.c
+@@ -65,14 +65,14 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
+ 	for (i = 0; i < size; i++) {
+ 		struct mt76_rxwi_cache *r = mt76_get_rxwi(dev);
+ 		dma_addr_t addr;
+-		struct page *page;
+ 		int token;
+ 		void *ptr;
+ 
+ 		if (!r)
+ 			goto unmap;
+ 
+-		ptr = page_frag_alloc(&wed->rx_buf_ring.rx_page, length, GFP_ATOMIC);
++		ptr = page_frag_alloc(&wed->rx_buf_ring.rx_page, length,
++				      GFP_ATOMIC | GFP_DMA32);
+ 		if (!ptr) {
+ 			mt76_put_rxwi(dev, r);
+ 			goto unmap;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0069-mtk-wifi-mt76-mt7996-support-backaward-compatiable.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0069-mtk-wifi-mt76-mt7996-support-backaward-compatiable.patch
deleted file mode 100644
index 8890a59..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0069-mtk-wifi-mt76-mt7996-support-backaward-compatiable.patch
+++ /dev/null
@@ -1,208 +0,0 @@
-From 099ff0e39f8f83d3d6395db232da68448392b67b Mon Sep 17 00:00:00 2001
-From: mtk27745 <rex.lu@mediatek.com>
-Date: Fri, 6 Oct 2023 20:59:42 +0800
-Subject: [PATCH 069/116] mtk: wifi: mt76: mt7996: support backaward
- compatiable
-
-revert upstream wed trigger mode to polling mode
-
-Signed-off-by: mtk27745 <rex.lu@mediatek.com>
-
-[Description]
-Change the SW token size from 1024 to 15360 according to HW capability.
-
-[Release-log]
-N/A
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
-Signed-off-by: Rex Lu <rex.lu@mediatek.com>
----
- mt76.h          |  2 ++
- mt7996/mac.c    |  3 ++-
- mt7996/mcu.c    |  2 +-
- mt7996/mmio.c   | 12 +++++++-----
- mt7996/mt7996.h |  1 +
- mt7996/pci.c    | 17 +++++++++--------
- wed.c           |  4 ++--
- 7 files changed, 24 insertions(+), 17 deletions(-)
-
-diff --git a/mt76.h b/mt76.h
-index 9f1f7fc..677f68f 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -53,6 +53,8 @@
- 
- #define MT76_TOKEN_FREE_THR	64
- 
-+#define MT76_WED_SW_TOKEN_SIZE	15360
-+
- #define MT_QFLAG_WED_RING	GENMASK(1, 0)
- #define MT_QFLAG_WED_TYPE	GENMASK(4, 2)
- #define MT_QFLAG_WED		BIT(5)
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 2ad9f48..8396e6d 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1777,7 +1777,7 @@ mt7996_mac_restart(struct mt7996_dev *dev)
- 		}
- 
- 		mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask);
--		mtk_wed_device_start_hwrro(&dev->mt76.mmio.wed, wed_irq_mask, false);
-+		mtk_wed_device_start_hw_rro(&dev->mt76.mmio.wed, wed_irq_mask, false);
- 		mt7996_irq_enable(dev, wed_irq_mask);
- 		mt7996_irq_disable(dev, 0);
- 	}
-@@ -2009,6 +2009,7 @@ void mt7996_mac_reset_work(struct work_struct *work)
- 
- 		mtk_wed_device_start_hw_rro(&dev->mt76.mmio.wed, wed_irq_mask,
- 					    true);
-+
- 		mt7996_irq_enable(dev, wed_irq_mask);
- 		mt7996_irq_disable(dev, 0);
- 	}
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index ef30d8e..6366cf0 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -3254,7 +3254,7 @@ static int mt7996_mcu_wa_red_config(struct mt7996_dev *dev)
- 
- 	if (!mtk_wed_device_active(&dev->mt76.mmio.wed))
- 		req.token_per_src[RED_TOKEN_SRC_CNT - 1] =
--			cpu_to_le16(MT7996_TOKEN_SIZE - MT7996_HW_TOKEN_SIZE);
-+			cpu_to_le16(MT7996_SW_TOKEN_SIZE);
- 
- 	return mt76_mcu_send_msg(&dev->mt76, MCU_WA_PARAM_CMD(SET),
- 				 &req, sizeof(req), false);
-diff --git a/mt7996/mmio.c b/mt7996/mmio.c
-index d29579f..1c2cea2 100644
---- a/mt7996/mmio.c
-+++ b/mt7996/mmio.c
-@@ -14,7 +14,7 @@
- #include "../trace.h"
- #include "../dma.h"
- 
--static bool wed_enable;
-+static bool wed_enable = true;
- module_param(wed_enable, bool, 0644);
- 
- static const struct __base mt7996_reg_base[] = {
-@@ -347,7 +347,7 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- 		}
- 
- 		wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + hif1_ofs + MT_WFDMA0_GLO_CFG;
--		wed->wlan.wpdma_rx = wed->wlan.phy_base + hif1_ofs +
-+		wed->wlan.wpdma_rx[0] = wed->wlan.phy_base + hif1_ofs +
- 				     MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) +
- 				     MT7996_RXQ_BAND0 * MT_RING_SIZE;
- 
-@@ -362,7 +362,7 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- 
- 		wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + MT_WFDMA0_GLO_CFG;
- 
--		wed->wlan.wpdma_rx = wed->wlan.phy_base +
-+		wed->wlan.wpdma_rx[0] = wed->wlan.phy_base +
- 				     MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) +
- 				     MT7996_RXQ_BAND0 * MT_RING_SIZE;
- 
-@@ -404,8 +404,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- 		dev->mt76.rx_token_size = MT7996_TOKEN_SIZE + wed->wlan.rx_npkt;
- 	}
- 
--	wed->wlan.nbuf = MT7996_HW_TOKEN_SIZE;
--	wed->wlan.token_start = MT7996_TOKEN_SIZE - wed->wlan.nbuf;
-+	wed->wlan.nbuf = MT7996_TOKEN_SIZE;
-+	wed->wlan.token_start = 0;
- 
- 	wed->wlan.amsdu_max_subframes = 8;
- 	wed->wlan.amsdu_max_len = 1536;
-@@ -426,6 +426,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- 	*irq = wed->irq;
- 	dev->mt76.dma_dev = wed->dev;
- 
-+	dev->mt76.token_size = MT7996_SW_TOKEN_SIZE;
-+
- 	return 1;
- #else
- 	return 0;
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 4090fad..7011f66 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -73,6 +73,7 @@
- #define MT7996_EEPROM_BLOCK_SIZE	16
- #define MT7996_TOKEN_SIZE		16384
- #define MT7996_HW_TOKEN_SIZE		8192
-+#define MT7996_SW_TOKEN_SIZE		15360
- 
- #define MT7996_CFEND_RATE_DEFAULT	0x49	/* OFDM 24M */
- #define MT7996_CFEND_RATE_11B		0x03	/* 11B LP, 11M */
-diff --git a/mt7996/pci.c b/mt7996/pci.c
-index 05830c0..4e95777 100644
---- a/mt7996/pci.c
-+++ b/mt7996/pci.c
-@@ -171,7 +171,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
- 
- 		ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &hif2_irq);
- 		if (ret < 0)
--			goto free_hif2_wed_irq_vector;
-+			goto free_wed_or_irq_vector;
- 
- 		if (!ret) {
- 			ret = pci_alloc_irq_vectors(hif2_dev, 1, 1,
-@@ -180,14 +180,15 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
- 				goto free_hif2;
- 
- 			dev->hif2->irq = hif2_dev->irq;
--			hif2_irq = dev->hif2->irq;
-+		} else {
-+			dev->hif2->irq = irq;
- 		}
- 
--		ret = devm_request_irq(mdev->dev, hif2_irq, mt7996_irq_handler,
--				       IRQF_SHARED, KBUILD_MODNAME "-hif",
--				       dev);
-+		ret = devm_request_irq(mdev->dev, dev->hif2->irq,
-+				       mt7996_irq_handler, IRQF_SHARED,
-+				       KBUILD_MODNAME "-hif", dev);
- 		if (ret)
--			goto free_hif2_wed_irq_vector;
-+			goto free_hif2_irq_vector;
- 
- 		mt76_wr(dev, MT_INT1_MASK_CSR, 0);
- 		/* master switch of PCIe tnterrupt enable */
-@@ -202,8 +203,8 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
- 
- free_hif2_irq:
- 	if (dev->hif2)
--		devm_free_irq(mdev->dev, hif2_irq, dev);
--free_hif2_wed_irq_vector:
-+		devm_free_irq(mdev->dev, dev->hif2->irq, dev);
-+free_hif2_irq_vector:
- 	if (dev->hif2) {
- 		if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2))
- 			mtk_wed_device_detach(&dev->mt76.mmio.wed_hif2);
-diff --git a/wed.c b/wed.c
-index 61a6bad..634c95c 100644
---- a/wed.c
-+++ b/wed.c
-@@ -120,7 +120,7 @@ int mt76_wed_offload_enable(struct mtk_wed_device *wed)
- 	struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
- 
- 	spin_lock_bh(&dev->token_lock);
--	dev->token_size = wed->wlan.token_start;
-+	dev->token_size = MT76_WED_SW_TOKEN_SIZE;
- 	spin_unlock_bh(&dev->token_lock);
- 
- 	return !wait_event_timeout(dev->tx_wait, !dev->wed_token_count, HZ);
-@@ -204,7 +204,7 @@ void mt76_wed_offload_disable(struct mtk_wed_device *wed)
- 	struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
- 
- 	spin_lock_bh(&dev->token_lock);
--	dev->token_size = dev->drv->token_size;
-+	dev->token_size = MT76_WED_SW_TOKEN_SIZE;
- 	spin_unlock_bh(&dev->token_lock);
- }
- EXPORT_SYMBOL_GPL(mt76_wed_offload_disable);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0070-mtk-mt76-mt7996-add-per-bss-statistic-info.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0070-mtk-mt76-mt7996-add-per-bss-statistic-info.patch
new file mode 100644
index 0000000..38d9ef7
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0070-mtk-mt76-mt7996-add-per-bss-statistic-info.patch
@@ -0,0 +1,122 @@
+From 05a99ecc4978355bccc95be925a2b479754b193c Mon Sep 17 00:00:00 2001
+From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Date: Fri, 18 Aug 2023 10:17:08 +0800
+Subject: [PATCH 070/199] mtk: mt76: mt7996: add per bss statistic info
+
+Whenever WED is enabled, unicast traffic might run through HW path.
+As a result, we need to count them using WM event.
+Broadcast and multicast traffic on the other hand, will be counted in mac80211
+as they always go through SW path and thus mac80211 can always see and count them.
+
+|         | Tx                             | Rx                        |
+|---------|--------------------------------|---------------------------|
+| Unicast | mt76                           | mt76                      |
+|         | __mt7996_stat_to_netdev()      | __mt7996_stat_to_netdev() |
+|---------|--------------------------------|---------------------------|
+| BMCast  | mac80211                       | mac80211                  |
+|         | __ieee80211_subif_start_xmit() | ieee80211_deliver_skb()   |
+---
+ mt7996/init.c |  1 +
+ mt7996/main.c |  1 +
+ mt7996/mcu.c  | 40 +++++++++++++++++++++++++++++++++++-----
+ 3 files changed, 37 insertions(+), 5 deletions(-)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 6563974d..9d918268 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -403,6 +403,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER);
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_PUNCT);
++	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_STAS_COUNT);
+ 
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION);
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_PROTECTION);
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 3bc9dc15..d26c55ed 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -265,6 +265,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ 	mvif->sta.wcid.phy_idx = band_idx;
+ 	mvif->sta.wcid.hw_key_idx = -1;
+ 	mvif->sta.wcid.tx_info |= MT_WCID_TX_INFO_SET;
++	mvif->sta.vif = mvif;
+ 	mt76_wcid_init(&mvif->sta.wcid);
+ 
+ 	mt7996_mac_wtbl_update(dev, idx,
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 7fa44f84..061461fb 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -524,6 +524,27 @@ mt7996_mcu_update_tx_gi(struct rate_info *rate, struct all_sta_trx_rate *mcu_rat
+ 	return 0;
+ }
+ 
++static inline void __mt7996_stat_to_netdev(struct mt76_phy *mphy,
++					   struct mt76_wcid *wcid,
++					   u32 tx_bytes, u32 rx_bytes,
++					   u32 tx_packets, u32 rx_packets)
++{
++	struct mt7996_sta *msta;
++	struct ieee80211_vif *vif;
++	struct wireless_dev *wdev;
++
++	if (wiphy_ext_feature_isset(mphy->hw->wiphy,
++				    NL80211_EXT_FEATURE_STAS_COUNT)) {
++		msta = container_of(wcid, struct mt7996_sta, wcid);
++		vif = container_of((void *)msta->vif, struct ieee80211_vif,
++				   drv_priv);
++		wdev = ieee80211_vif_to_wdev(vif);
++
++		dev_sw_netstats_tx_add(wdev->netdev, tx_packets, tx_bytes);
++		dev_sw_netstats_rx_add(wdev->netdev, rx_packets, rx_bytes);
++	}
++}
++
+ static void
+ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+@@ -539,7 +560,7 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 		u16 wlan_idx;
+ 		struct mt76_wcid *wcid;
+ 		struct mt76_phy *mphy;
+-		u32 tx_bytes, rx_bytes;
++		u32 tx_bytes, rx_bytes, tx_packets, rx_packets;
+ 
+ 		switch (le16_to_cpu(res->tag)) {
+ 		case UNI_ALL_STA_TXRX_RATE:
+@@ -567,6 +588,9 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 				wcid->stats.tx_bytes += tx_bytes;
+ 				wcid->stats.rx_bytes += rx_bytes;
+ 
++				__mt7996_stat_to_netdev(mphy, wcid,
++							tx_bytes, rx_bytes, 0, 0);
++
+ 				ieee80211_tpt_led_trig_tx(mphy->hw, tx_bytes);
+ 				ieee80211_tpt_led_trig_rx(mphy->hw, rx_bytes);
+ 			}
+@@ -578,10 +602,16 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 			if (!wcid)
+ 				break;
+ 
+-			wcid->stats.tx_packets +=
+-				le32_to_cpu(res->msdu_cnt[i].tx_msdu_cnt);
+-			wcid->stats.rx_packets +=
+-				le32_to_cpu(res->msdu_cnt[i].rx_msdu_cnt);
++			mphy = mt76_dev_phy(&dev->mt76, wcid->phy_idx);
++
++			tx_packets = le32_to_cpu(res->msdu_cnt[i].tx_msdu_cnt);
++			rx_packets = le32_to_cpu(res->msdu_cnt[i].rx_msdu_cnt);
++
++			wcid->stats.tx_packets += tx_packets;
++			wcid->stats.rx_packets += rx_packets;
++
++			__mt7996_stat_to_netdev(mphy, wcid, 0, 0,
++						tx_packets, rx_packets);
+ 			break;
+ 		default:
+ 			break;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0070-mtk-wifi-mt76-mt7996-wed-add-wed-support-for-mt7992.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0070-mtk-wifi-mt76-mt7996-wed-add-wed-support-for-mt7992.patch
deleted file mode 100644
index 2e43a51..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0070-mtk-wifi-mt76-mt7996-wed-add-wed-support-for-mt7992.patch
+++ /dev/null
@@ -1,432 +0,0 @@
-From 048688dc31d7f531fc9d99d0e20820189ede9c53 Mon Sep 17 00:00:00 2001
-From: "sujuan.chen" <sujuan.chen@mediatek.com>
-Date: Fri, 8 Sep 2023 11:57:39 +0800
-Subject: [PATCH 070/116] mtk: wifi: mt76: mt7996: wed: add wed support for
- mt7992
-
-Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
-
-Fix incomplete WED initialization for Kite band-1 RX ring.
-
-Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
----
- mt7996/dma.c    | 91 +++++++++++++++++++++++++++++++++----------------
- mt7996/init.c   | 12 +++++++
- mt7996/mac.c    |  4 +++
- mt7996/mmio.c   | 49 ++++++++++++++++++--------
- mt7996/mt7996.h | 10 +++++-
- mt7996/pci.c    | 10 ++++--
- mt7996/regs.h   | 14 +++++++-
- 7 files changed, 142 insertions(+), 48 deletions(-)
-
-diff --git a/mt7996/dma.c b/mt7996/dma.c
-index d9e1b17..d62dc8b 100644
---- a/mt7996/dma.c
-+++ b/mt7996/dma.c
-@@ -77,18 +77,23 @@ static void mt7996_dma_config(struct mt7996_dev *dev)
- 			   MT7996_RXQ_RRO_BAND0);
- 		RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND0, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND0,
- 			   MT7996_RXQ_MSDU_PG_BAND0);
--		RXQ_CONFIG(MT_RXQ_TXFREE_BAND0, WFDMA0, MT_INT_RX_TXFREE_MAIN,
--			   MT7996_RXQ_TXFREE0);
--		/* band1 */
--		RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND1, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND1,
--			   MT7996_RXQ_MSDU_PG_BAND1);
--		/* band2 */
--		RXQ_CONFIG(MT_RXQ_RRO_BAND2, WFDMA0, MT_INT_RX_DONE_RRO_BAND2,
--			   MT7996_RXQ_RRO_BAND2);
--		RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND2, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND2,
--			   MT7996_RXQ_MSDU_PG_BAND2);
--		RXQ_CONFIG(MT_RXQ_TXFREE_BAND2, WFDMA0, MT_INT_RX_TXFREE_TRI,
--			   MT7996_RXQ_TXFREE2);
-+		if (is_mt7996(&dev->mt76)) {
-+			RXQ_CONFIG(MT_RXQ_TXFREE_BAND0, WFDMA0, MT_INT_RX_TXFREE_MAIN,
-+				   MT7996_RXQ_TXFREE0);
-+			/* band1 */
-+			RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND1, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND1,
-+				   MT7996_RXQ_MSDU_PG_BAND1);
-+			/* band2 */
-+			RXQ_CONFIG(MT_RXQ_RRO_BAND2, WFDMA0, MT_INT_RX_DONE_RRO_BAND2,
-+				   MT7996_RXQ_RRO_BAND2);
-+			RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND2, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND2,
-+				   MT7996_RXQ_MSDU_PG_BAND2);
-+			RXQ_CONFIG(MT_RXQ_TXFREE_BAND2, WFDMA0, MT_INT_RX_TXFREE_TRI,
-+				   MT7996_RXQ_TXFREE2);
-+		} else {
-+			RXQ_CONFIG(MT_RXQ_RRO_BAND1, WFDMA0, MT_INT_RX_DONE_RRO_BAND1,
-+				   MT7996_RXQ_RRO_BAND1);
-+		}
- 
- 		RXQ_CONFIG(MT_RXQ_RRO_IND, WFDMA0, MT_INT_RX_DONE_RRO_IND,
- 			   MT7996_RXQ_RRO_IND);
-@@ -146,8 +151,13 @@ static void __mt7996_dma_prefetch(struct mt7996_dev *dev, u32 ofs)
- 	if (dev->has_rro) {
- 		mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND0) + ofs,
- 			PREFETCH(0x10));
--		mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND2) + ofs,
--			PREFETCH(0x10));
-+		if (is_mt7996(&dev->mt76))
-+			mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND2) + ofs,
-+				PREFETCH(0x10));
-+		else
-+			mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND1) + ofs,
-+				PREFETCH(0x10));
-+
- 		mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND0) + ofs,
- 			PREFETCH(0x4));
- 		mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND1) + ofs,
-@@ -361,12 +371,16 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
- 		 * so, redirect pcie0 rx ring3 interrupt to pcie1
- 		 */
- 		if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
--		    dev->has_rro)
-+		    dev->has_rro) {
-+			u32 intr = is_mt7996(&dev->mt76) ?
-+				   MT_WFDMA0_RX_INT_SEL_RING6 :
-+				   MT_WFDMA0_RX_INT_SEL_RING9;
- 			mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL + hif1_ofs,
--				 MT_WFDMA0_RX_INT_SEL_RING6);
--		else
-+				 intr);
-+		} else {
- 			mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL,
- 				 MT_WFDMA0_RX_INT_SEL_RING3);
-+		}
- 	}
- 
- 	mt7996_dma_start(dev, reset, true);
-@@ -401,7 +415,7 @@ int mt7996_dma_rro_init(struct mt7996_dev *dev)
- 	if (ret)
- 		return ret;
- 
--	if (mt7996_band_valid(dev, MT_BAND1)) {
-+	if (mt7996_band_valid(dev, MT_BAND1) && is_mt7996(&dev->mt76)) {
- 		/* rx msdu page queue for band1 */
- 		mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND1].flags =
- 			MT_WED_RRO_Q_MSDU_PG(1) | MT_QFLAG_WED_RRO_EN;
-@@ -522,7 +536,9 @@ int mt7996_dma_init(struct mt7996_dev *dev)
- 		return ret;
- 
- 	/* tx free notify event from WA for band0 */
--	if (mtk_wed_device_active(wed) && !dev->has_rro) {
-+	if (mtk_wed_device_active(wed) &&
-+	    ((is_mt7996(&dev->mt76) && !dev->has_rro) ||
-+	     (is_mt7992(&dev->mt76)))) {
- 		dev->mt76.q_rx[MT_RXQ_MAIN_WA].flags = MT_WED_Q_TXFREE;
- 		dev->mt76.q_rx[MT_RXQ_MAIN_WA].wed = wed;
- 	}
-@@ -568,6 +584,11 @@ int mt7996_dma_init(struct mt7996_dev *dev)
- 	} else if (mt7996_band_valid(dev, MT_BAND1)) {
- 		/* rx data queue for mt7992 band1 */
- 		rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND1) + hif1_ofs;
-+		if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed)) {
-+			dev->mt76.q_rx[MT_RXQ_BAND1].flags = MT_WED_Q_RX(1);
-+			dev->mt76.q_rx[MT_RXQ_BAND1].wed = wed;
-+		}
-+
- 		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND1],
- 				       MT_RXQ_ID(MT_RXQ_BAND1),
- 				       MT7996_RX_RING_SIZE,
-@@ -601,17 +622,29 @@ int mt7996_dma_init(struct mt7996_dev *dev)
- 		if (ret)
- 			return ret;
- 
--		/* tx free notify event from WA for band0 */
--		dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
--		dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed;
-+		if (is_mt7992(&dev->mt76)) {
-+			dev->mt76.q_rx[MT_RXQ_RRO_BAND1].flags =
-+				MT_WED_RRO_Q_DATA(1) | MT_QFLAG_WED_RRO_EN;
-+			dev->mt76.q_rx[MT_RXQ_RRO_BAND1].wed = wed;
-+			ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_RRO_BAND1],
-+					       MT_RXQ_ID(MT_RXQ_RRO_BAND1),
-+					       MT7996_RX_RING_SIZE,
-+					       MT7996_RX_BUF_SIZE,
-+					       MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND1));
-+			if (ret)
-+				return ret;
-+		} else {
-+			dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
-+			dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed;
- 
--		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0],
--				       MT_RXQ_ID(MT_RXQ_TXFREE_BAND0),
--				       MT7996_RX_MCU_RING_SIZE,
--				       MT7996_RX_BUF_SIZE,
--				       MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND0));
--		if (ret)
--			return ret;
-+			ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0],
-+					       MT_RXQ_ID(MT_RXQ_TXFREE_BAND0),
-+					       MT7996_RX_MCU_RING_SIZE,
-+					       MT7996_RX_BUF_SIZE,
-+					       MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND0));
-+			if (ret)
-+				return ret;
-+		}
- 
- 		if (mt7996_band_valid(dev, MT_BAND2)) {
- 			/* rx rro data queue for band2 */
-diff --git a/mt7996/init.c b/mt7996/init.c
-index ff629e8..e29cebe 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -813,6 +813,7 @@ void mt7996_rro_hw_init(struct mt7996_dev *dev)
- 	/* interrupt enable */
- 	mt76_wr(dev, MT_RRO_HOST_INT_ENA,
- 		MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA);
-+
- #endif
- }
- 
-@@ -865,6 +866,17 @@ static int mt7996_wed_rro_init(struct mt7996_dev *dev)
- 			dev->wed_rro.addr_elem[i].phy_addr;
- 	}
- 
-+	for (i = 0; i < MT7996_RRO_MSDU_PG_CR_CNT; i++) {
-+		ptr = dmam_alloc_coherent(dev->mt76.dma_dev, MT7996_RRO_MSDU_PG_SIZE_PER_CR,
-+					  &dev->wed_rro.msdu_pg[i].phy_addr,
-+					  GFP_KERNEL);
-+		if (!ptr)
-+			return -ENOMEM;
-+		dev->wed_rro.msdu_pg[i].ptr = ptr;
-+
-+		memset(dev->wed_rro.msdu_pg[i].ptr, 0, MT7996_RRO_MSDU_PG_SIZE_PER_CR);
-+	}
-+
- 	ptr = dmam_alloc_coherent(dev->mt76.dma_dev,
- 				  MT7996_RRO_WINDOW_MAX_LEN * sizeof(*addr),
- 				  &dev->wed_rro.session.phy_addr,
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 8396e6d..b470380 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -2007,6 +2007,10 @@ void mt7996_mac_reset_work(struct work_struct *work)
- 
- 		mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask);
- 
-+		if (is_mt7992(&dev->mt76) && dev->has_rro)
-+			mt76_wr(dev, MT_RRO_3_0_EMU_CONF,
-+				MT_RRO_3_0_EMU_CONF_EN_MASK);
-+
- 		mtk_wed_device_start_hw_rro(&dev->mt76.mmio.wed, wed_irq_mask,
- 					    true);
- 
-diff --git a/mt7996/mmio.c b/mt7996/mmio.c
-index 1c2cea2..3f0692c 100644
---- a/mt7996/mmio.c
-+++ b/mt7996/mmio.c
-@@ -313,7 +313,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- 
- 	dev->has_rro = true;
- 
--	hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
-+	if (dev->hif2)
-+		hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
- 
- 	if (hif2)
- 		wed = &dev->mt76.mmio.wed_hif2;
-@@ -348,8 +349,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- 
- 		wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + hif1_ofs + MT_WFDMA0_GLO_CFG;
- 		wed->wlan.wpdma_rx[0] = wed->wlan.phy_base + hif1_ofs +
--				     MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) +
--				     MT7996_RXQ_BAND0 * MT_RING_SIZE;
-+				     MT_RXQ_RING_BASE(MT7996_RXQ_BAND2) +
-+				     MT7996_RXQ_BAND2 * MT_RING_SIZE;
- 
- 		wed->wlan.id = 0x7991;
- 		wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND2) - 1;
-@@ -369,9 +370,19 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- 		wed->wlan.wpdma_rx_rro[0] = wed->wlan.phy_base +
- 					    MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND0) +
- 					    MT7996_RXQ_RRO_BAND0 * MT_RING_SIZE;
--		wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base + hif1_ofs +
--					    MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND2) +
--					    MT7996_RXQ_RRO_BAND2 * MT_RING_SIZE;
-+		if (is_mt7996(&dev->mt76)) {
-+			wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base + hif1_ofs +
-+						    MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND2) +
-+						    MT7996_RXQ_RRO_BAND2 * MT_RING_SIZE;
-+		} else {
-+			wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base +
-+						    MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND1) +
-+						    MT7996_RXQ_RRO_BAND1 * MT_RING_SIZE;
-+			wed->wlan.wpdma_rx[1] = wed->wlan.phy_base +
-+						MT_RXQ_RING_BASE(MT7996_RXQ_BAND1) +
-+						MT7996_RXQ_BAND1 * MT_RING_SIZE;
-+		}
-+
- 		wed->wlan.wpdma_rx_pg = wed->wlan.phy_base +
- 					MT_RXQ_RING_BASE(MT7996_RXQ_MSDU_PG_BAND0) +
- 					MT7996_RXQ_MSDU_PG_BAND0 * MT_RING_SIZE;
-@@ -381,10 +392,14 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- 		wed->wlan.rx_size = SKB_WITH_OVERHEAD(MT_RX_BUF_SIZE);
- 
- 		wed->wlan.rx_tbit[0] = ffs(MT_INT_RX_DONE_BAND0) - 1;
--		wed->wlan.rx_tbit[1] = ffs(MT_INT_RX_DONE_BAND2) - 1;
--
- 		wed->wlan.rro_rx_tbit[0] = ffs(MT_INT_RX_DONE_RRO_BAND0) - 1;
--		wed->wlan.rro_rx_tbit[1] = ffs(MT_INT_RX_DONE_RRO_BAND2) - 1;
-+		if (is_mt7996(&dev->mt76)) {
-+			wed->wlan.rx_tbit[1] = ffs(MT_INT_RX_DONE_BAND2) - 1;
-+			wed->wlan.rro_rx_tbit[1] = ffs(MT_INT_RX_DONE_RRO_BAND2) - 1;
-+		} else {
-+			wed->wlan.rx_tbit[1] = ffs(MT_INT_RX_DONE_BAND1) - 1;
-+			wed->wlan.rro_rx_tbit[1] = ffs(MT_INT_RX_DONE_RRO_BAND1) - 1;
-+		}
- 
- 		wed->wlan.rx_pg_tbit[0] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND0) - 1;
- 		wed->wlan.rx_pg_tbit[1] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND1) - 1;
-@@ -392,14 +407,20 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- 
- 		wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND0) - 1;
- 		wed->wlan.tx_tbit[1] = ffs(MT_INT_TX_DONE_BAND1) - 1;
--		if (dev->has_rro) {
--			wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
--						 MT7996_RXQ_TXFREE0 * MT_RING_SIZE;
--			wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_MAIN) - 1;
-+		if (is_mt7996(&dev->mt76)) {
-+			if (dev->has_rro) {
-+				wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
-+							 MT7996_RXQ_TXFREE0 * MT_RING_SIZE;
-+				wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_MAIN) - 1;
-+			} else {
-+				wed->wlan.txfree_tbit = ffs(MT_INT_RX_DONE_WA_MAIN) - 1;
-+				wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
-+							 MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE;
-+			}
- 		} else {
- 			wed->wlan.txfree_tbit = ffs(MT_INT_RX_DONE_WA_MAIN) - 1;
- 			wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
--						  MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE;
-+						 MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE;
- 		}
- 		dev->mt76.rx_token_size = MT7996_TOKEN_SIZE + wed->wlan.rx_npkt;
- 	}
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 7011f66..929a077 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -121,6 +121,10 @@
- #define MT7996_DRR_STA_AC2_QNTM_MASK	GENMASK(18, 16)
- #define MT7996_DRR_STA_AC3_QNTM_MASK	GENMASK(22, 20)
- 
-+/* RRO 3.1 */
-+#define MT7996_RRO_MSDU_PG_CR_CNT 8
-+#define MT7996_RRO_MSDU_PG_SIZE_PER_CR 0x10000
-+
- struct mt7996_vif;
- struct mt7996_sta;
- struct mt7996_dfs_pulse;
-@@ -180,7 +184,7 @@ enum mt7996_rxq_id {
- 	MT7996_RXQ_BAND1 = 5, /* for mt7992 */
- 	MT7996_RXQ_BAND2 = 5,
- 	MT7996_RXQ_RRO_BAND0 = 8,
--	MT7996_RXQ_RRO_BAND1 = 8,/* unused */
-+	MT7996_RXQ_RRO_BAND1 = 9,
- 	MT7996_RXQ_RRO_BAND2 = 6,
- 	MT7996_RXQ_MSDU_PG_BAND0 = 10,
- 	MT7996_RXQ_MSDU_PG_BAND1 = 11,
-@@ -546,6 +550,10 @@ struct mt7996_dev {
- 			void *ptr;
- 			dma_addr_t phy_addr;
- 		} session;
-+		struct {
-+			void *ptr;
-+			dma_addr_t phy_addr;
-+		} msdu_pg[MT7996_RRO_MSDU_PG_CR_CNT];
- 
- 		struct work_struct work;
- 		struct list_head poll_list;
-diff --git a/mt7996/pci.c b/mt7996/pci.c
-index 4e95777..f0d3f19 100644
---- a/mt7996/pci.c
-+++ b/mt7996/pci.c
-@@ -107,7 +107,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
- 	struct pci_dev *hif2_dev;
- 	struct mt7996_hif *hif2;
- 	struct mt7996_dev *dev;
--	int irq, hif2_irq, ret;
-+	int irq, ret;
- 	struct mt76_dev *mdev;
- 
- 	hif2_enable |= (id->device == 0x7990 || id->device == 0x7991);
-@@ -143,6 +143,8 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
- 	mdev = &dev->mt76;
- 	mt7996_wfsys_reset(dev);
- 	hif2 = mt7996_pci_init_hif2(pdev);
-+	if (hif2)
-+		dev->hif2 = hif2;
- 
- 	ret = mt7996_mmio_wed_init(dev, pdev, false, &irq);
- 	if (ret < 0)
-@@ -167,9 +169,11 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
- 
- 	if (hif2) {
- 		hif2_dev = container_of(hif2->dev, struct pci_dev, dev);
--		dev->hif2 = hif2;
-+		ret = 0;
-+
-+		if (is_mt7996(&dev->mt76))
-+			ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &irq);
- 
--		ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &hif2_irq);
- 		if (ret < 0)
- 			goto free_wed_or_irq_vector;
- 
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index 91159c6..e6427a3 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -77,6 +77,8 @@ enum offs_rev {
- #define MT_RRO_BA_BITMAP_BASE1			MT_RRO_TOP(0xC)
- #define WF_RRO_AXI_MST_CFG			MT_RRO_TOP(0xB8)
- #define WF_RRO_AXI_MST_CFG_DIDX_OK		BIT(12)
-+
-+#define MT_RRO_ADDR_ARRAY_BASE0			MT_RRO_TOP(0x30)
- #define MT_RRO_ADDR_ARRAY_BASE1			MT_RRO_TOP(0x34)
- #define MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE	BIT(31)
- 
-@@ -97,6 +99,14 @@ enum offs_rev {
- 
- #define MT_RRO_ADDR_ELEM_SEG_ADDR0		MT_RRO_TOP(0x400)
- 
-+#define MT_RRO_3_0_EMU_CONF			MT_RRO_TOP(0x600)
-+#define MT_RRO_3_0_EMU_CONF_EN_MASK		BIT(11)
-+
-+#define MT_RRO_3_1_GLOBAL_CONFIG		MT_RRO_TOP(0x604)
-+#define MT_RRO_3_1_GLOBAL_CONFIG_INTERLEAVE_EN	BIT(0)
-+
-+#define MT_RRO_MSDU_PG_SEG_ADDR0		MT_RRO_TOP(0x620)
-+
- #define MT_RRO_ACK_SN_CTRL			MT_RRO_TOP(0x50)
- #define MT_RRO_ACK_SN_CTRL_SN_MASK		GENMASK(27, 16)
- #define MT_RRO_ACK_SN_CTRL_SESSION_MASK		GENMASK(11, 0)
-@@ -402,6 +412,7 @@ enum offs_rev {
- #define MT_WFDMA0_RX_INT_PCIE_SEL		MT_WFDMA0(0x154)
- #define MT_WFDMA0_RX_INT_SEL_RING3		BIT(3)
- #define MT_WFDMA0_RX_INT_SEL_RING6		BIT(6)
-+#define MT_WFDMA0_RX_INT_SEL_RING9		BIT(9)
- 
- #define MT_WFDMA0_MCU_HOST_INT_ENA		MT_WFDMA0(0x1f4)
- 
-@@ -503,13 +514,14 @@ enum offs_rev {
- #define MT_INT_RX_DONE_WA_EXT			BIT(3) /* for mt7992 */
- #define MT_INT_RX_DONE_WA_TRI			BIT(3)
- #define MT_INT_RX_TXFREE_MAIN			BIT(17)
-+#define MT_INT_RX_TXFREE_BAND1			BIT(15)
- #define MT_INT_RX_TXFREE_TRI			BIT(15)
- #define MT_INT_RX_DONE_BAND2_EXT		BIT(23)
- #define MT_INT_RX_TXFREE_EXT			BIT(26)
- #define MT_INT_MCU_CMD				BIT(29)
- 
- #define MT_INT_RX_DONE_RRO_BAND0		BIT(16)
--#define MT_INT_RX_DONE_RRO_BAND1		BIT(16)
-+#define MT_INT_RX_DONE_RRO_BAND1		BIT(17)
- #define MT_INT_RX_DONE_RRO_BAND2		BIT(14)
- #define MT_INT_RX_DONE_RRO_IND			BIT(11)
- #define MT_INT_RX_DONE_MSDU_PG_BAND0		BIT(18)
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0071-mtk-mt76-mt7996-do-not-report-netdev-stats-on-monito.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0071-mtk-mt76-mt7996-do-not-report-netdev-stats-on-monito.patch
new file mode 100644
index 0000000..72d71ff
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0071-mtk-mt76-mt7996-do-not-report-netdev-stats-on-monito.patch
@@ -0,0 +1,37 @@
+From 5d4625368dbd91070333a90e6e779d09cd6d3ca8 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 26 Oct 2023 17:27:43 +0800
+Subject: [PATCH 071/199] mtk: mt76: mt7996: do not report netdev stats on
+ monitor vif
+
+This fixes the following NULL pointer crash when enabling monitor mode:
+[  205.593158] Call trace:
+[  205.595597]  mt7996_mcu_rx_event+0x4a0/0x6e8 [mt7996e]
+[  205.600725]  mt7996_queue_rx_skb+0x6e4/0xfa0 [mt7996e]
+[  205.605851]  mt76_dma_rx_poll+0x384/0x420 [mt76]
+[  205.610459]  __napi_poll+0x38/0x1c0
+[  205.613935]  napi_threaded_poll+0x80/0xe8
+[  205.617934]  kthread+0x124/0x128
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mcu.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 061461fb..9d96218b 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -540,6 +540,9 @@ static inline void __mt7996_stat_to_netdev(struct mt76_phy *mphy,
+ 				   drv_priv);
+ 		wdev = ieee80211_vif_to_wdev(vif);
+ 
++		if (vif->type == NL80211_IFTYPE_MONITOR)
++			return;
++
+ 		dev_sw_netstats_tx_add(wdev->netdev, tx_packets, tx_bytes);
+ 		dev_sw_netstats_rx_add(wdev->netdev, rx_packets, rx_bytes);
+ 	}
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0071-mtk-wifi-mt76-mt7992-wed-add-2pcie-one-wed-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0071-mtk-wifi-mt76-mt7992-wed-add-2pcie-one-wed-support.patch
deleted file mode 100644
index cb745f1..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0071-mtk-wifi-mt76-mt7992-wed-add-2pcie-one-wed-support.patch
+++ /dev/null
@@ -1,177 +0,0 @@
-From 2342f41c5f878f7d419d20a46daa3b025c024b08 Mon Sep 17 00:00:00 2001
-From: "sujuan.chen" <sujuan.chen@mediatek.com>
-Date: Wed, 13 Sep 2023 17:35:43 +0800
-Subject: [PATCH 071/116] mtk: wifi: mt76: mt7992: wed: add 2pcie one wed
- support
-
-Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
----
- mt7996/dma.c         | 13 +++++++++++--
- mt7996/mmio.c        |  7 +++----
- mt7996/mtk_debug.h   |  5 +++++
- mt7996/mtk_debugfs.c | 25 ++++++++++++++++++-------
- mt7996/regs.h        |  2 ++
- 5 files changed, 39 insertions(+), 13 deletions(-)
-
-diff --git a/mt7996/dma.c b/mt7996/dma.c
-index d62dc8b..c23b0d6 100644
---- a/mt7996/dma.c
-+++ b/mt7996/dma.c
-@@ -355,6 +355,13 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
- 			 MT_WFDMA_HOST_CONFIG_PDMA_BAND |
- 			 MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
- 
-+		if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
-+		    is_mt7992(&dev->mt76)) {
-+			mt76_set(dev, MT_WFDMA_HOST_CONFIG,
-+				 MT_WFDMA_HOST_CONFIG_PDMA_BAND |
-+				 MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
-+		}
-+
- 		/* AXI read outstanding number */
- 		mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL,
- 			 MT_WFDMA_AXI_R2A_CTRL_OUTSTAND_MASK, 0x14);
-@@ -374,7 +381,8 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
- 		    dev->has_rro) {
- 			u32 intr = is_mt7996(&dev->mt76) ?
- 				   MT_WFDMA0_RX_INT_SEL_RING6 :
--				   MT_WFDMA0_RX_INT_SEL_RING9;
-+				   MT_WFDMA0_RX_INT_SEL_RING9 |
-+				   MT_WFDMA0_RX_INT_SEL_RING5;
- 			mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL + hif1_ofs,
- 				 intr);
- 		} else {
-@@ -630,10 +638,11 @@ int mt7996_dma_init(struct mt7996_dev *dev)
- 					       MT_RXQ_ID(MT_RXQ_RRO_BAND1),
- 					       MT7996_RX_RING_SIZE,
- 					       MT7996_RX_BUF_SIZE,
--					       MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND1));
-+					       MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND1) + hif1_ofs);
- 			if (ret)
- 				return ret;
- 		} else {
-+			/* tx free notify event from WA for band0 */
- 			dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
- 			dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed;
- 
-diff --git a/mt7996/mmio.c b/mt7996/mmio.c
-index 3f0692c..91567a0 100644
---- a/mt7996/mmio.c
-+++ b/mt7996/mmio.c
-@@ -375,10 +375,10 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- 						    MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND2) +
- 						    MT7996_RXQ_RRO_BAND2 * MT_RING_SIZE;
- 		} else {
--			wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base +
-+			wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base + hif1_ofs +
- 						    MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND1) +
- 						    MT7996_RXQ_RRO_BAND1 * MT_RING_SIZE;
--			wed->wlan.wpdma_rx[1] = wed->wlan.phy_base +
-+			wed->wlan.wpdma_rx[1] = wed->wlan.phy_base + hif1_ofs +
- 						MT_RXQ_RING_BASE(MT7996_RXQ_BAND1) +
- 						MT7996_RXQ_BAND1 * MT_RING_SIZE;
- 		}
-@@ -516,10 +516,9 @@ void mt7996_dual_hif_set_irq_mask(struct mt7996_dev *dev, bool write_reg,
- 		if (mtk_wed_device_active(&mdev->mmio.wed)) {
- 			mtk_wed_device_irq_set_mask(&mdev->mmio.wed,
- 						    mdev->mmio.irqmask);
--			if (mtk_wed_device_active(&mdev->mmio.wed_hif2)) {
-+			if (mtk_wed_device_active(&mdev->mmio.wed_hif2))
- 				mtk_wed_device_irq_set_mask(&mdev->mmio.wed_hif2,
- 							    mdev->mmio.irqmask);
--			}
- 		} else {
- 			mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask);
- 			mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask);
-diff --git a/mt7996/mtk_debug.h b/mt7996/mtk_debug.h
-index 27d8f1c..da2a607 100644
---- a/mt7996/mtk_debug.h
-+++ b/mt7996/mtk_debug.h
-@@ -561,6 +561,11 @@ struct queue_desc {
- #define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL1_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x574) // 8574
- #define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL2_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x578) // 8578
- #define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL3_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x57c) // 857C
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL0_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x590) // 8590
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL1_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x594) // 8594
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL2_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x598) // 8598
-+#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL3_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x59c) // 859C
-+
- //MCU DMA
- //#define WF_WFDMA_MCU_DMA0_BASE                                 0x02000
- #define WF_WFDMA_MCU_DMA0_BASE                                 0x54000000
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index ff84d6f..70ff761 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -559,14 +559,22 @@ mt7996_show_dma_info(struct seq_file *s, struct mt7996_dev *dev)
- 		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL0_ADDR);
- 	dump_dma_rx_ring_info(s, dev, "R5:Data1(MAC2H)", "Both",
- 		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL0_ADDR);
--	dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
--		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
-+	if (is_mt7996(&dev->mt76))
-+		dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
-+			WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
-+	else
-+		dump_dma_rx_ring_info(s, dev, "R6:TxDone0(MAC2H)", "Both",
-+			WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
- 	dump_dma_rx_ring_info(s, dev, "R7:TxDone1(MAC2H)", "Both",
- 		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL0_ADDR);
- 	dump_dma_rx_ring_info(s, dev, "R8:BUF0(MAC2H)", "Both",
- 		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL0_ADDR);
--	dump_dma_rx_ring_info(s, dev, "R9:TxDone0(MAC2H)", "Both",
--		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
-+	if (is_mt7996(&dev->mt76))
-+		dump_dma_rx_ring_info(s, dev, "R9:TxDone0(MAC2H)", "Both",
-+			WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
-+	else
-+		dump_dma_rx_ring_info(s, dev, "R9:BUF0(MAC2H)", "Both",
-+			WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
- 	dump_dma_rx_ring_info(s, dev, "R10:MSDU_PG0(MAC2H)", "Both",
- 		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL0_ADDR);
- 	dump_dma_rx_ring_info(s, dev, "R11:MSDU_PG1(MAC2H)", "Both",
-@@ -584,15 +592,18 @@ mt7996_show_dma_info(struct seq_file *s, struct mt7996_dev *dev)
- 			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL0_ADDR);
- 		dump_dma_tx_ring_info(s, dev, "T22:TXD?(H2WA)", "AP",
- 			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL0_ADDR);
--
- 		dump_dma_rx_ring_info(s, dev, "R3:TxDone1(WA2H)", "AP",
- 			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL0_ADDR);
- 		dump_dma_rx_ring_info(s, dev, "R5:Data1(MAC2H)", "Both",
- 			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL0_ADDR);
--		dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
--			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL0_ADDR);
-+		if (is_mt7996(&dev->mt76))
-+			dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
-+				WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL0_ADDR);
- 		dump_dma_rx_ring_info(s, dev, "R7:TxDone1(MAC2H)", "Both",
- 			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL0_ADDR);
-+		if (is_mt7992(&dev->mt76))
-+			dump_dma_rx_ring_info(s, dev, "R9:BUF1(MAC2H)", "Both",
-+				WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL0_ADDR);
- 	}
- 
- 	/* MCU DMA information */
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index e6427a3..cbd7170 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -411,6 +411,7 @@ enum offs_rev {
- 
- #define MT_WFDMA0_RX_INT_PCIE_SEL		MT_WFDMA0(0x154)
- #define MT_WFDMA0_RX_INT_SEL_RING3		BIT(3)
-+#define MT_WFDMA0_RX_INT_SEL_RING5		BIT(5)
- #define MT_WFDMA0_RX_INT_SEL_RING6		BIT(6)
- #define MT_WFDMA0_RX_INT_SEL_RING9		BIT(9)
- 
-@@ -451,6 +452,7 @@ enum offs_rev {
- 
- #define MT_WFDMA_HOST_CONFIG			MT_WFDMA_EXT_CSR(0x30)
- #define MT_WFDMA_HOST_CONFIG_PDMA_BAND		BIT(0)
-+#define MT_WFDMA_HOST_CONFIG_BAND1_PCIE1	BIT(21)
- #define MT_WFDMA_HOST_CONFIG_BAND2_PCIE1	BIT(22)
- 
- #define MT_WFDMA_EXT_CSR_HIF_MISC		MT_WFDMA_EXT_CSR(0x44)
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0072-mtk-mt76-mt7996-add-support-for-HW-ATF.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0072-mtk-mt76-mt7996-add-support-for-HW-ATF.patch
new file mode 100644
index 0000000..3cc57cb
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0072-mtk-mt76-mt7996-add-support-for-HW-ATF.patch
@@ -0,0 +1,675 @@
+From bf2d3c5764a6c6377cf9d806b4320ae4fdb5afa5 Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Mon, 11 Sep 2023 16:35:15 +0800
+Subject: [PATCH 072/199] mtk: mt76: mt7996: add support for HW-ATF
+
+---
+ mt7996/debugfs.c |  90 ++++++++++++++++
+ mt7996/init.c    |  43 ++++++++
+ mt7996/mac.c     |   6 ++
+ mt7996/mcu.c     | 265 ++++++++++++++++++++++++++++++++++++++++++-----
+ mt7996/mcu.h     |   1 +
+ mt7996/mt7996.h  |  96 ++++++++++++++++-
+ 6 files changed, 475 insertions(+), 26 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 3301701a..3b58b8ba 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -927,6 +927,91 @@ static const struct file_operations mt7996_efuse_ops = {
+ 	.llseek = default_llseek,
+ };
+ 
++static int
++mt7996_vow_info_read(struct seq_file *s, void *data)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	struct mt7996_vow_ctrl *vow = &dev->vow;
++	int i;
++
++	seq_printf(s, "VoW ATF Configuration:\n");
++	seq_printf(s, "ATF: %s\n", vow->atf_enable ? "enabled" : "disabled");
++	seq_printf(s, "WATF: %s\n", vow->watf_enable ? "enabled" : "disabled");
++	seq_printf(s, "Airtime Quantums (unit: 256 us)\n");
++	for (i = 0; i < VOW_DRR_QUANTUM_NUM; ++i)
++		seq_printf(s, "\tL%d: %hhu\n", i, vow->drr_quantum[i]);
++	seq_printf(s, "Max Airtime Deficit: %hhu (unit: 256 us)\n", vow->max_deficit);
++
++	return 0;
++}
++
++static int
++mt7996_atf_enable_get(void *data, u64 *val)
++{
++	struct mt7996_phy *phy = data;
++
++	*val = phy->dev->vow.atf_enable;
++
++	return 0;
++}
++
++static int
++mt7996_atf_enable_set(void *data, u64 val)
++{
++	struct mt7996_phy *phy = data;
++	struct mt7996_vow_ctrl *vow = &phy->dev->vow;
++	int ret;
++
++	vow->max_deficit = val ? 64 : 1;
++	ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
++	if (ret)
++		return ret;
++
++	vow->atf_enable = !!val;
++	return mt7996_mcu_set_vow_feature_ctrl(phy);
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_atf_enable, mt7996_atf_enable_get,
++	                 mt7996_atf_enable_set, "%llu\n");
++
++static int
++mt7996_airtime_read(struct seq_file *s, void *data)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	struct mt76_dev *mdev = &dev->mt76;
++	struct mt7996_vow_sta_ctrl *vow;
++	struct ieee80211_sta *sta;
++	struct mt7996_sta *msta;
++	struct mt76_wcid *wcid;
++	struct mt76_vif *vif;
++	u64 airtime;
++	u16 i;
++
++	seq_printf(s, "VoW Airtime Information:\n");
++	rcu_read_lock();
++	for (i = 1; i < MT7996_WTBL_STA; ++i) {
++		wcid = rcu_dereference(mdev->wcid[i]);
++		if (!wcid || !wcid->sta)
++			continue;
++
++		msta = container_of(wcid, struct mt7996_sta, wcid);
++		sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
++		vow = &msta->vow;
++		vif = &msta->vif->mt76;
++
++		spin_lock_bh(&vow->lock);
++		airtime = vow->tx_airtime;
++		vow->tx_airtime = 0;
++		spin_unlock_bh(&vow->lock);
++
++		seq_printf(s, "%pM WCID: %hu BandIdx: %hhu OmacIdx: 0x%hhx\tTxAirtime: %llu\n",
++		           sta->addr, i, vif->band_idx, vif->omac_idx, airtime);
++	}
++	rcu_read_unlock();
++
++	return 0;
++}
++
+ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+@@ -954,6 +1039,11 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ 				    mt7996_twt_stats);
+ 	debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval);
+ 	debugfs_create_file("otp", 0400, dir, dev, &mt7996_efuse_ops);
++	debugfs_create_devm_seqfile(dev->mt76.dev, "vow_info", dir,
++	                            mt7996_vow_info_read);
++	debugfs_create_file("atf_enable", 0600, dir, phy, &fops_atf_enable);
++	debugfs_create_devm_seqfile(dev->mt76.dev, "airtime", dir,
++	                            mt7996_airtime_read);
+ 
+ 	if (phy->mt76->cap.has_5ghz) {
+ 		debugfs_create_u32("dfs_hw_pattern", 0400, dir,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 9d918268..440c4b7c 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -575,6 +575,37 @@ int mt7996_txbf_init(struct mt7996_dev *dev)
+ 	return mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
+ }
+ 
++static int mt7996_vow_init(struct mt7996_phy *phy)
++{
++	struct mt7996_vow_ctrl *vow = &phy->dev->vow;
++	int ret;
++
++	vow->atf_enable = true;
++	vow->watf_enable = false;
++	vow->max_deficit = 64;
++	vow->sch_type = VOW_SCH_TYPE_FOLLOW_POLICY;
++	vow->sch_policy = VOW_SCH_POLICY_SRR;
++
++	vow->drr_quantum[0] = VOW_DRR_QUANTUM_L0;
++	vow->drr_quantum[1] = VOW_DRR_QUANTUM_L1;
++	vow->drr_quantum[2] = VOW_DRR_QUANTUM_L2;
++	vow->drr_quantum[3] = VOW_DRR_QUANTUM_L3;
++	vow->drr_quantum[4] = VOW_DRR_QUANTUM_L4;
++	vow->drr_quantum[5] = VOW_DRR_QUANTUM_L5;
++	vow->drr_quantum[6] = VOW_DRR_QUANTUM_L6;
++	vow->drr_quantum[7] = VOW_DRR_QUANTUM_L7;
++
++	ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
++	if (ret)
++		return ret;
++
++	ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL);
++	if (ret)
++		return ret;
++
++	return mt7996_mcu_set_vow_feature_ctrl(phy);
++}
++
+ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 			       enum mt76_band_id band)
+ {
+@@ -647,6 +678,12 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 	if (ret)
+ 		goto error;
+ 
++	if (mt7996_vow_should_enable(dev)) {
++		ret = mt7996_vow_init(phy);
++		if (ret)
++			goto error;
++	}
++
+ 	ret = mt7996_init_debugfs(phy);
+ 	if (ret)
+ 		goto error;
+@@ -1483,6 +1520,12 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ 
+ 	dev->recovery.hw_init_done = true;
+ 
++	if (mt7996_vow_should_enable(dev)) {
++		ret = mt7996_vow_init(&dev->phy);
++		if (ret)
++			goto error;
++	}
++
+ 	ret = mt7996_init_debugfs(&dev->phy);
+ 	if (ret)
+ 		goto error;
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 0c2fee66..dfc68a19 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -103,6 +103,7 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
+ 	};
+ 	struct ieee80211_sta *sta;
+ 	struct mt7996_sta *msta;
++	struct mt7996_vow_sta_ctrl *vow;
+ 	u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS];
+ 	LIST_HEAD(sta_poll_list);
+ 	int i;
+@@ -161,6 +162,7 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
+ 
+ 		sta = container_of((void *)msta, struct ieee80211_sta,
+ 				   drv_priv);
++		vow = &msta->vow;
+ 		for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ 			u8 q = mt76_connac_lmac_mapping(i);
+ 			u32 tx_cur = tx_time[q];
+@@ -171,6 +173,10 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
+ 				continue;
+ 
+ 			ieee80211_sta_register_airtime(sta, tid, tx_cur, rx_cur);
++
++			spin_lock_bh(&vow->lock);
++			vow->tx_airtime += tx_cur;
++			spin_unlock_bh(&vow->lock);
+ 		}
+ 
+ 		/* get signal strength of resp frames (CTS/BA/ACK) */
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 9d96218b..4bcc415f 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2225,34 +2225,37 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ }
+ 
+ static int
+-mt7996_mcu_add_group(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+-		     struct ieee80211_sta *sta)
++mt7996_mcu_sta_init_vow(struct mt7996_phy *phy, struct mt7996_sta *msta)
+ {
+-#define MT_STA_BSS_GROUP		1
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct mt7996_sta *msta;
+-	struct {
+-		u8 __rsv1[4];
++	struct mt7996_vow_sta_ctrl *vow = &msta->vow;
++	u8 omac_idx = msta->vif->mt76.omac_idx;
++	int ret;
+ 
+-		__le16 tag;
+-		__le16 len;
+-		__le16 wlan_idx;
+-		u8 __rsv2[2];
+-		__le32 action;
+-		__le32 val;
+-		u8 __rsv3[8];
+-	} __packed req = {
+-		.tag = cpu_to_le16(UNI_VOW_DRR_CTRL),
+-		.len = cpu_to_le16(sizeof(req) - 4),
+-		.action = cpu_to_le32(MT_STA_BSS_GROUP),
+-		.val = cpu_to_le32(mvif->mt76.idx % 16),
+-	};
++	/* Assignment of STA BSS group index aligns FW.
++	 * Each band has its own BSS group bitmap space.
++	 * 0: BSS 0
++	 * 4..18: BSS 0x11..0x1f
++	 */
++	vow->bss_grp_idx = (omac_idx <= HW_BSSID_MAX)
++	                   ? omac_idx
++	                   : HW_BSSID_MAX + omac_idx - EXT_BSSID_START;
++	vow->paused = false;
++	vow->drr_quantum[IEEE80211_AC_VO] = VOW_DRR_QUANTUM_IDX0;
++	vow->drr_quantum[IEEE80211_AC_VI] = VOW_DRR_QUANTUM_IDX1;
++	vow->drr_quantum[IEEE80211_AC_BE] = VOW_DRR_QUANTUM_IDX2;
++	vow->drr_quantum[IEEE80211_AC_BK] = VOW_DRR_QUANTUM_IDX2;
++	vow->tx_airtime = 0;
++	spin_lock_init(&vow->lock);
++
++	ret = mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_BSS_GROUP);
++	if (ret)
++		return ret;
+ 
+-	msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta;
+-	req.wlan_idx = cpu_to_le16(msta->wcid.idx);
++	ret = mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_PAUSE);
++	if (ret)
++		return ret;
+ 
+-	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req,
+-				 sizeof(req), true);
++	return mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_ALL);
+ }
+ 
+ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+@@ -2311,7 +2314,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 		mt7996_mcu_sta_bfee_tlv(dev, skb, vif, sta);
+ 	}
+ 
+-	ret = mt7996_mcu_add_group(dev, vif, sta);
++	ret = mt7996_mcu_sta_init_vow(mvif->phy, msta);
+ 	if (ret) {
+ 		dev_kfree_skb(skb);
+ 		return ret;
+@@ -5188,6 +5191,218 @@ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
+ 				 &req, sizeof(req), false);
+ }
+ 
++int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
++	                        enum vow_drr_ctrl_id id)
++{
++	struct mt7996_vow_sta_ctrl *vow = msta ? &msta->vow : NULL;
++	u32 val = 0;
++	struct {
++		u8 __rsv1[4];
++
++		__le16 tag;
++		__le16 len;
++		__le16 wlan_idx;
++		u8 band_idx;
++		u8 wmm_idx;
++		__le32 ctrl_id;
++
++		union {
++			__le32 val;
++			u8 drr_quantum[VOW_DRR_QUANTUM_NUM];
++		};
++
++		u8 __rsv2[3];
++		u8 omac_idx;
++	} __packed req = {
++		.tag = cpu_to_le16(UNI_VOW_DRR_CTRL),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.wlan_idx = cpu_to_le16(msta ? msta->wcid.idx : 0),
++		.band_idx = phy->mt76->band_idx,
++		.wmm_idx = msta ? msta->vif->mt76.wmm_idx : 0,
++		.ctrl_id = cpu_to_le32(id),
++		.omac_idx = msta ? msta->vif->mt76.omac_idx : 0
++	};
++
++	switch (id) {
++	case VOW_DRR_CTRL_STA_ALL:
++		val |= FIELD_PREP(MT7996_DRR_STA_BSS_GRP_MASK, vow->bss_grp_idx);
++		val |= FIELD_PREP(MT7996_DRR_STA_AC0_QNTM_MASK, vow->drr_quantum[IEEE80211_AC_BK]);
++		val |= FIELD_PREP(MT7996_DRR_STA_AC1_QNTM_MASK, vow->drr_quantum[IEEE80211_AC_BE]);
++		val |= FIELD_PREP(MT7996_DRR_STA_AC2_QNTM_MASK, vow->drr_quantum[IEEE80211_AC_VI]);
++		val |= FIELD_PREP(MT7996_DRR_STA_AC3_QNTM_MASK, vow->drr_quantum[IEEE80211_AC_VO]);
++		req.val = cpu_to_le32(val);
++		break;
++	case VOW_DRR_CTRL_STA_BSS_GROUP:
++		req.val = cpu_to_le32(vow->bss_grp_idx);
++		break;
++	case VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND:
++		req.val = cpu_to_le32(phy->dev->vow.max_deficit);
++		break;
++	case VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL:
++		memcpy(req.drr_quantum, phy->dev->vow.drr_quantum, VOW_DRR_QUANTUM_NUM);
++		break;
++	case VOW_DRR_CTRL_STA_PAUSE:
++		req.val = cpu_to_le32(vow->paused);
++		break;
++	default:
++		dev_err(phy->dev->mt76.dev, "Unknown VoW DRR Control ID: %u\n", id);
++		return -EINVAL;
++	}
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(VOW),
++	                         &req, sizeof(req), true);
++}
++
++int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy)
++{
++	struct mt7996_vow_ctrl *vow = &phy->dev->vow;
++	struct {
++		u8 __rsv1[4];
++
++		__le16 tag;
++		__le16 len;
++
++		/* DW0 */
++		__le16 apply_bwc_enable_per_grp;
++		__le16 apply_bwc_refill_period		: 1;
++		__le16 __rsv2				: 3;
++		__le16 apply_band1_search_rule		: 1;
++		__le16 apply_band0_search_rule		: 1;
++		__le16 __rsv3				: 3;
++		__le16 apply_watf_enable		: 1;
++		__le16 __rsv4				: 2;
++		__le16 apply_grp_no_change_in_txop	: 1;
++		__le16 apply_atf_enable			: 1;
++		__le16 apply_bwc_token_refill_enable	: 1;
++		__le16 apply_bwc_enable			: 1;
++
++		/* DW1 */
++		__le16 apply_bwc_check_time_token_per_grp;
++		__le16 __rsv5;
++
++		/* DW2 */
++		__le16 apply_bwc_check_len_token_per_grp;
++		__le16 __rsv6;
++
++		/* DW3 */
++		u8 band_idx;
++		u8 __rsv7[3];
++
++		/* DW4 */
++		__le32 __rsv8;
++
++		/* DW5 */
++		__le16 bwc_enable_per_grp;
++		__le16 bwc_refill_period	: 3;
++		__le16 __rsv9			: 1;
++		__le16 band1_search_rule	: 1;
++		__le16 band0_search_rule	: 1;
++		__le16 __rsv10			: 3;
++		__le16 watf_enable		: 1;
++		__le16 __rsv11			: 2;
++		__le16 grp_no_change_in_txop	: 1;
++		__le16 atf_enable		: 1;
++		__le16 bwc_token_refill_enable	: 1;
++		__le16 bwc_enable		: 1;
++
++		/* DW6 */
++		__le16 bwc_check_time_token_per_grp;
++		__le16 __rsv12;
++
++		/* DW7 */
++		__le16 bwc_check_len_token_per_grp;
++		__le16 __rsv13;
++
++		/* DW8 */
++		__le32 apply_atf_rts_sta_lock		: 1;
++		__le32 atf_rts_sta_lock			: 1;
++		__le32 apply_atf_keep_quantum		: 1;
++		__le32 atf_keep_quantum			: 1;
++		__le32 apply_tx_cnt_mode_ctrl		: 1;
++		__le32 tx_cnt_mode_ctrl			: 4;
++		__le32 apply_tx_measure_mode_enable	: 1;
++		__le32 tx_measure_mode_enable		: 1;
++		__le32 apply_backoff_ctrl		: 1;
++		__le32 backoff_bound_enable		: 1;
++		__le32 backoff_bound			: 5;
++		__le32 apply_atf_rts_fail_charge	: 1;
++		__le32 atf_rts_fail_charge		: 1;
++		__le32 apply_zero_eifs			: 1;
++		__le32 zero_eifs			: 1;
++		__le32 apply_rx_rifs_enable		: 1;
++		__le32 rx_rifs_enable			: 1;
++		__le32 apply_vow_ctrl			: 1;
++		__le32 vow_ctrl_val			: 1;
++		__le32 vow_ctrl_bit			: 5;
++		__le32 __rsv14				: 1;
++
++		/* DW9 */
++		__le32 apply_spl_sta_num	: 1;
++		__le32 spl_sta_num		: 3;
++		__le32 dbg_lvl			: 2;
++		__le32 apply_atf_sch_ctrl	: 1;
++		__le32 atf_sch_type		: 2;
++		__le32 atf_sch_policy		: 2;
++		__le32 __rsv15			: 21;
++	} __packed req = {
++		.tag = cpu_to_le16(UNI_VOW_FEATURE_CTRL),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		/* DW0 */
++		.apply_bwc_enable_per_grp = cpu_to_le16(0xffff),
++		.apply_bwc_refill_period = true,
++		.apply_band1_search_rule = true,
++		.apply_band0_search_rule = true,
++		.apply_watf_enable = true,
++		.apply_grp_no_change_in_txop = true,
++		.apply_atf_enable = true,
++		.apply_bwc_token_refill_enable = true,
++		.apply_bwc_enable = true,
++		/* DW1 */
++		.apply_bwc_check_time_token_per_grp = cpu_to_le16(0xffff),
++		/* DW2 */
++		.apply_bwc_check_len_token_per_grp = cpu_to_le16(0xffff),
++		/* DW3 */
++		.band_idx = phy->mt76->band_idx,
++		/* DW5 */
++		.bwc_enable_per_grp = cpu_to_le16(0xffff),
++		.bwc_refill_period = VOW_REFILL_PERIOD_32US,
++		.band1_search_rule = VOW_SEARCH_WMM_FIRST,
++		.band0_search_rule = VOW_SEARCH_WMM_FIRST,
++		.watf_enable = vow->watf_enable,
++		.grp_no_change_in_txop = true,
++		.atf_enable = vow->atf_enable,
++		.bwc_token_refill_enable = true,
++		.bwc_enable = false,
++		/* DW6 */
++		.bwc_check_time_token_per_grp = cpu_to_le16(0x0),
++		/* DW7 */
++		.bwc_check_len_token_per_grp = cpu_to_le16(0x0),
++		/* DW8 */
++		.apply_atf_rts_sta_lock = false,
++		.apply_atf_keep_quantum = true,
++		.atf_keep_quantum = true,
++		.apply_tx_cnt_mode_ctrl = false,
++		.apply_tx_measure_mode_enable = false,
++		.apply_backoff_ctrl = false,
++		.apply_atf_rts_fail_charge = false,
++		.apply_zero_eifs = false,
++		.apply_rx_rifs_enable = false,
++		.apply_vow_ctrl = true,
++		.vow_ctrl_val = true,
++		/* Reset DRR table when SER occurs. */
++		.vow_ctrl_bit = 26,
++		/* DW9 */
++		.apply_spl_sta_num = false,
++		.dbg_lvl = 0,
++		.apply_atf_sch_ctrl = true,
++		.atf_sch_type = vow->sch_type,
++		.atf_sch_policy = vow->sch_policy
++	};
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(VOW),
++	                         &req, sizeof(req), true);
++}
++
+ #ifdef CONFIG_MTK_VENDOR
+ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ {
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 7077a664..c55fb527 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -965,6 +965,7 @@ enum {
+ 
+ enum {
+ 	UNI_VOW_DRR_CTRL,
++	UNI_VOW_FEATURE_CTRL,
+ 	UNI_VOW_RX_AT_AIRTIME_EN = 0x0b,
+ 	UNI_VOW_RX_AT_AIRTIME_CLR_EN = 0x0e,
+ 	UNI_VOW_RED_ENABLE = 0x18,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index d40f95cd..a933e739 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -115,6 +115,12 @@
+ #define MT7996_RX_MSDU_PAGE_SIZE	(128 + \
+ 					 SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
+ 
++#define MT7996_DRR_STA_BSS_GRP_MASK	GENMASK(5, 0)
++#define MT7996_DRR_STA_AC0_QNTM_MASK	GENMASK(10, 8)
++#define MT7996_DRR_STA_AC1_QNTM_MASK	GENMASK(14, 12)
++#define MT7996_DRR_STA_AC2_QNTM_MASK	GENMASK(18, 16)
++#define MT7996_DRR_STA_AC3_QNTM_MASK	GENMASK(22, 20)
++
+ struct mt7996_vif;
+ struct mt7996_sta;
+ struct mt7996_dfs_pulse;
+@@ -216,6 +222,81 @@ enum mt7996_dpd_ch_num {
+ 	DPD_CH_NUM_TYPE_MAX,
+ };
+ 
++enum {
++	VOW_SEARCH_AC_FIRST,
++	VOW_SEARCH_WMM_FIRST
++};
++
++enum {
++	VOW_REFILL_PERIOD_1US,
++	VOW_REFILL_PERIOD_2US,
++	VOW_REFILL_PERIOD_4US,
++	VOW_REFILL_PERIOD_8US,
++	VOW_REFILL_PERIOD_16US,
++	VOW_REFILL_PERIOD_32US,
++	VOW_REFILL_PERIOD_64US,
++	VOW_REFILL_PERIOD_128US
++};
++
++/* Default DRR airtime quantum of each level */
++enum {
++	VOW_DRR_QUANTUM_L0 = 6,
++	VOW_DRR_QUANTUM_L1 = 12,
++	VOW_DRR_QUANTUM_L2 = 16,
++	VOW_DRR_QUANTUM_L3 = 20,
++	VOW_DRR_QUANTUM_L4 = 24,
++	VOW_DRR_QUANTUM_L5 = 28,
++	VOW_DRR_QUANTUM_L6 = 32,
++	VOW_DRR_QUANTUM_L7 = 36
++};
++
++enum {
++	VOW_DRR_QUANTUM_IDX0,
++	VOW_DRR_QUANTUM_IDX1,
++	VOW_DRR_QUANTUM_IDX2,
++	VOW_DRR_QUANTUM_IDX3,
++	VOW_DRR_QUANTUM_IDX4,
++	VOW_DRR_QUANTUM_IDX5,
++	VOW_DRR_QUANTUM_IDX6,
++	VOW_DRR_QUANTUM_IDX7,
++	VOW_DRR_QUANTUM_NUM
++};
++
++enum {
++	VOW_SCH_TYPE_FOLLOW_POLICY,
++	VOW_SCH_TYPE_FOLLOW_HW
++};
++
++enum {
++	VOW_SCH_POLICY_SRR, /* Shared Round-Robin */
++	VOW_SCH_POLICY_WRR /* Weighted Round-Robin */
++};
++
++enum vow_drr_ctrl_id {
++	VOW_DRR_CTRL_STA_ALL,
++	VOW_DRR_CTRL_STA_BSS_GROUP,
++	VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND = 0x10,
++	VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL = 0x28,
++	VOW_DRR_CTRL_STA_PAUSE = 0x30
++};
++
++struct mt7996_vow_ctrl {
++	bool atf_enable;
++	bool watf_enable;
++	u8 drr_quantum[VOW_DRR_QUANTUM_NUM];
++	u8 max_deficit;
++	u8 sch_type;
++	u8 sch_policy;
++};
++
++struct mt7996_vow_sta_ctrl {
++	bool paused;
++	u8 bss_grp_idx;
++	u8 drr_quantum[IEEE80211_NUM_ACS];
++	u64 tx_airtime;
++	spinlock_t lock;
++};
++
+ struct mt7996_sta {
+ 	struct mt76_wcid wcid; /* must be first */
+ 
+@@ -235,6 +316,8 @@ struct mt7996_sta {
+ 		u8 flowid_mask;
+ 		struct mt7996_twt_flow flow[MT7996_MAX_STA_TWT_AGRT];
+ 	} twt;
++
++	struct mt7996_vow_sta_ctrl vow;
+ };
+ 
+ struct mt7996_vif {
+@@ -500,6 +583,7 @@ struct mt7996_dev {
+ 
+ 	u8 wtbl_size_group;
+ 
++	struct mt7996_vow_ctrl vow;
+ #ifdef CONFIG_MTK_DEBUG
+ 	u16 wlan_idx;
+ 	struct {
+@@ -740,10 +824,12 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy);
+ #ifdef CONFIG_NL80211_TESTMODE
+ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ #endif
+-int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
+ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
+ void mt7996_mcu_scs_sta_poll(struct work_struct *work);
+ int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable);
++int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
++	                        enum vow_drr_ctrl_id id);
++int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy);
+ 
+ static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
+ {
+@@ -793,6 +879,14 @@ static inline u16 mt7996_rx_chainmask(struct mt7996_phy *phy)
+ 	return tx_chainmask | (BIT(fls(tx_chainmask)) * phy->has_aux_rx);
+ }
+ 
++static inline bool
++mt7996_vow_should_enable(struct mt7996_dev *dev)
++{
++	return !wiphy_ext_feature_isset(mt76_hw(dev)->wiphy,
++	                                NL80211_EXT_FEATURE_AIRTIME_FAIRNESS) ||
++	       mtk_wed_device_active(&dev->mt76.mmio.wed);
++}
++
+ void mt7996_mac_init(struct mt7996_dev *dev);
+ u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw);
+ bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0072-mtk-wifi-mt76-mt7996-Remove-wed-rro-ring-add-napi-at.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0072-mtk-wifi-mt76-mt7996-Remove-wed-rro-ring-add-napi-at.patch
deleted file mode 100644
index 759ac00..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0072-mtk-wifi-mt76-mt7996-Remove-wed-rro-ring-add-napi-at.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From 6701c4d78ed81ac7d04b73c213f38f2d3ba1a735 Mon Sep 17 00:00:00 2001
-From: mtk27745 <rex.lu@mediatek.com>
-Date: Mon, 6 Nov 2023 10:16:34 +0800
-Subject: [PATCH 072/116] mtk: wifi: mt76: mt7996: Remove wed rro ring add napi
- at init state
-
-without this patch. rro ring will add napi at initial state. once rro ring add napi, it will have chance to be used by host driver. if host driver accessed the ring data, it will cause some issue.
-
-Signed-off-by: mtk27745 <rex.lu@mediatek.com>
----
- dma.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/dma.c b/dma.c
-index e23b744..38701c7 100644
---- a/dma.c
-+++ b/dma.c
-@@ -1017,6 +1017,10 @@ mt76_dma_init(struct mt76_dev *dev,
- 	init_completion(&dev->mmio.wed_reset_complete);
- 
- 	mt76_for_each_q_rx(dev, i) {
-+		if (mtk_wed_device_active(&dev->mmio.wed) &&
-+		    mt76_queue_is_wed_rro(&dev->q_rx[i]))
-+			continue;
-+
- 		netif_napi_add(&dev->napi_dev, &dev->napi[i], poll);
- 		mt76_dma_rx_fill(dev, &dev->q_rx[i], false);
- 		napi_enable(&dev->napi[i]);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0073-mtk-mt76-mt7996-add-SER0.5-support-w-wed3.0.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0073-mtk-mt76-mt7996-add-SER0.5-support-w-wed3.0.patch
new file mode 100644
index 0000000..dae5c07
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0073-mtk-mt76-mt7996-add-SER0.5-support-w-wed3.0.patch
@@ -0,0 +1,396 @@
+From 73693f3833455addeaa13764ba39917f4fe8e171 Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Thu, 12 Oct 2023 10:04:54 +0800
+Subject: [PATCH 073/199] mtk: mt76: mt7996: add SER0.5 support w/ wed3.0
+
+Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
+---
+ dma.c           |   9 ++--
+ dma.h           |   4 +-
+ mt76.h          |  14 ++++--
+ mt792x_dma.c    |   6 +--
+ mt7996/dma.c    |  20 ++++++--
+ mt7996/init.c   | 127 +++++++++++++++++++++++++++++++-----------------
+ mt7996/mac.c    |  25 ++++++++++
+ mt7996/mt7996.h |   1 +
+ wed.c           |   4 +-
+ 9 files changed, 146 insertions(+), 64 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index da21f641..e23b744b 100644
+--- a/dma.c
++++ b/dma.c
+@@ -218,9 +218,9 @@ void __mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q,
+ 	mt76_dma_sync_idx(dev, q);
+ }
+ 
+-void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q)
++void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
+ {
+-	__mt76_dma_queue_reset(dev, q, true);
++	__mt76_dma_queue_reset(dev, q, reset);
+ }
+ 
+ static int
+@@ -540,7 +540,8 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
+ 	if (!q->queued)
+ 		return NULL;
+ 
+-	if (mt76_queue_is_wed_rro_data(q))
++	if (mt76_queue_is_wed_rro_data(q) ||
++	    mt76_queue_is_wed_rro_msdu_pg(q))
+ 		return NULL;
+ 
+ 	if (!mt76_queue_is_wed_rro_ind(q)) {
+@@ -792,7 +793,7 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
+ 			return 0;
+ 	}
+ 
+-	mt76_dma_queue_reset(dev, q);
++	mt76_dma_queue_reset(dev, q, true);
+ 
+ 	return 0;
+ }
+diff --git a/dma.h b/dma.h
+index 1de5a2b2..3a8c2e55 100644
+--- a/dma.h
++++ b/dma.h
+@@ -83,12 +83,12 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
+ 		     bool allow_direct);
+ void __mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q,
+ 			    bool reset_idx);
+-void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q);
++void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q, bool reset);
+ 
+ static inline void
+ mt76_dma_reset_tx_queue(struct mt76_dev *dev, struct mt76_queue *q)
+ {
+-	dev->queue_ops->reset_q(dev, q);
++	dev->queue_ops->reset_q(dev, q, true);
+ 	if (mtk_wed_device_active(&dev->mmio.wed))
+ 		mt76_wed_dma_setup(dev, q, true);
+ }
+diff --git a/mt76.h b/mt76.h
+index f2052cf7..1236ddb4 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -301,7 +301,7 @@ struct mt76_queue_ops {
+ 
+ 	void (*kick)(struct mt76_dev *dev, struct mt76_queue *q);
+ 
+-	void (*reset_q)(struct mt76_dev *dev, struct mt76_queue *q);
++	void (*reset_q)(struct mt76_dev *dev, struct mt76_queue *q, bool reset);
+ };
+ 
+ enum mt76_phy_type {
+@@ -1742,8 +1742,13 @@ static inline bool mt76_queue_is_wed_rro_ind(struct mt76_queue *q)
+ static inline bool mt76_queue_is_wed_rro_data(struct mt76_queue *q)
+ {
+ 	return mt76_queue_is_wed_rro(q) &&
+-	       (FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_DATA ||
+-		FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_MSDU_PG);
++	       (FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_DATA);
++}
++
++static inline bool mt76_queue_is_wed_rro_msdu_pg(struct mt76_queue *q)
++{
++	return mt76_queue_is_wed_rro(q) &&
++	       (FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_MSDU_PG);
+ }
+ 
+ static inline bool mt76_queue_is_wed_rx(struct mt76_queue *q)
+@@ -1752,7 +1757,8 @@ static inline bool mt76_queue_is_wed_rx(struct mt76_queue *q)
+ 		return false;
+ 
+ 	return FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX ||
+-	       mt76_queue_is_wed_rro_ind(q) || mt76_queue_is_wed_rro_data(q);
++	       mt76_queue_is_wed_rro_ind(q) || mt76_queue_is_wed_rro_data(q) ||
++	       mt76_queue_is_wed_rro_msdu_pg(q);
+ 
+ }
+ 
+diff --git a/mt792x_dma.c b/mt792x_dma.c
+index 5cc2d59b..c224bcc8 100644
+--- a/mt792x_dma.c
++++ b/mt792x_dma.c
+@@ -181,13 +181,13 @@ mt792x_dma_reset(struct mt792x_dev *dev, bool force)
+ 
+ 	/* reset hw queues */
+ 	for (i = 0; i < __MT_TXQ_MAX; i++)
+-		mt76_queue_reset(dev, dev->mphy.q_tx[i]);
++		mt76_queue_reset(dev, dev->mphy.q_tx[i], true);
+ 
+ 	for (i = 0; i < __MT_MCUQ_MAX; i++)
+-		mt76_queue_reset(dev, dev->mt76.q_mcu[i]);
++		mt76_queue_reset(dev, dev->mt76.q_mcu[i], true);
+ 
+ 	mt76_for_each_q_rx(&dev->mt76, i)
+-		mt76_queue_reset(dev, &dev->mt76.q_rx[i]);
++		mt76_queue_reset(dev, &dev->mt76.q_rx[i], true);
+ 
+ 	mt76_tx_status_check(&dev->mt76, true);
+ 
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index 5d85e9ea..d9e1b17f 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -711,21 +711,31 @@ void mt7996_dma_reset(struct mt7996_dev *dev, bool force)
+ 	}
+ 
+ 	for (i = 0; i < __MT_MCUQ_MAX; i++)
+-		mt76_queue_reset(dev, dev->mt76.q_mcu[i]);
++		mt76_queue_reset(dev, dev->mt76.q_mcu[i], true);
+ 
+ 	mt76_for_each_q_rx(&dev->mt76, i) {
+-		if (mtk_wed_device_active(&dev->mt76.mmio.wed))
++		if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
+ 			if (mt76_queue_is_wed_rro(&dev->mt76.q_rx[i]) ||
+-			    mt76_queue_is_wed_tx_free(&dev->mt76.q_rx[i]))
++			    mt76_queue_is_wed_tx_free(&dev->mt76.q_rx[i])) {
++				if (force && mt76_queue_is_wed_rro_data(&dev->mt76.q_rx[i]))
++					mt76_queue_reset(dev, &dev->mt76.q_rx[i], false);
+ 				continue;
++			}
++		}
+ 
+-		mt76_queue_reset(dev, &dev->mt76.q_rx[i]);
++		mt76_queue_reset(dev, &dev->mt76.q_rx[i], true);
+ 	}
+ 
+ 	mt76_tx_status_check(&dev->mt76, true);
+ 
+-	mt76_for_each_q_rx(&dev->mt76, i)
++	mt76_for_each_q_rx(&dev->mt76, i) {
++		if (mtk_wed_device_active(&dev->mt76.mmio.wed) && force &&
++		    (mt76_queue_is_wed_rro_ind(&dev->mt76.q_rx[i]) ||
++		     mt76_queue_is_wed_rro_msdu_pg(&dev->mt76.q_rx[i])))
++			continue;
++
+ 		mt76_queue_rx_reset(dev, i);
++	}
+ 
+ 	mt7996_dma_enable(dev, !force);
+ }
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 440c4b7c..3b1f4273 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -737,11 +737,91 @@ void mt7996_wfsys_reset(struct mt7996_dev *dev)
+ 	msleep(20);
+ }
+ 
+-static int mt7996_wed_rro_init(struct mt7996_dev *dev)
++void mt7996_rro_hw_init(struct mt7996_dev *dev)
+ {
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ 	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+ 	u32 reg = MT_RRO_ADDR_ELEM_SEG_ADDR0;
++	int i;
++
++	if (!dev->has_rro)
++		return;
++
++	if (is_mt7992(&dev->mt76)) {
++		/* set emul 3.0 function */
++		mt76_wr(dev, MT_RRO_3_0_EMU_CONF,
++			MT_RRO_3_0_EMU_CONF_EN_MASK);
++
++		mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE0,
++			dev->wed_rro.addr_elem[0].phy_addr);
++	} else {
++		/* TODO: remove line after WM has set */
++		mt76_clear(dev, WF_RRO_AXI_MST_CFG, WF_RRO_AXI_MST_CFG_DIDX_OK);
++
++		/* setup BA bitmap cache address */
++		mt76_wr(dev, MT_RRO_BA_BITMAP_BASE0,
++			dev->wed_rro.ba_bitmap[0].phy_addr);
++		mt76_wr(dev, MT_RRO_BA_BITMAP_BASE1, 0);
++		mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT0,
++			dev->wed_rro.ba_bitmap[1].phy_addr);
++		mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT1, 0);
++
++		/* setup Address element address */
++		for (i = 0; i < ARRAY_SIZE(dev->wed_rro.addr_elem); i++) {
++			mt76_wr(dev, reg, dev->wed_rro.addr_elem[i].phy_addr >> 4);
++			reg += 4;
++		}
++
++		/* setup Address element address - separate address segment mode */
++		mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE1,
++			MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE);
++	}
++	wed->wlan.ind_cmd.win_size = ffs(MT7996_RRO_WINDOW_MAX_LEN) - 6;
++	if (is_mt7996(&dev->mt76))
++		wed->wlan.ind_cmd.particular_sid = MT7996_RRO_MAX_SESSION;
++	else
++		wed->wlan.ind_cmd.particular_sid = 1;
++	wed->wlan.ind_cmd.particular_se_phys = dev->wed_rro.session.phy_addr;
++	wed->wlan.ind_cmd.se_group_nums = MT7996_RRO_ADDR_ELEM_LEN;
++	wed->wlan.ind_cmd.ack_sn_addr = MT_RRO_ACK_SN_CTRL;
++
++	mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE0, 0x15010e00);
++	mt76_set(dev, MT_RRO_IND_CMD_SIGNATURE_BASE1,
++		 MT_RRO_IND_CMD_SIGNATURE_BASE1_EN);
++
++	/* particular session configure */
++	/* use max session idx + 1 as particular session id */
++	mt76_wr(dev, MT_RRO_PARTICULAR_CFG0, dev->wed_rro.session.phy_addr);
++
++	if (is_mt7992(&dev->mt76)) {
++		reg = MT_RRO_MSDU_PG_SEG_ADDR0;
++
++		mt76_set(dev, MT_RRO_3_1_GLOBAL_CONFIG,
++			 MT_RRO_3_1_GLOBAL_CONFIG_INTERLEAVE_EN);
++
++		/* setup Msdu page address */
++		for (i = 0; i < MT7996_RRO_MSDU_PG_CR_CNT; i++) {
++			mt76_wr(dev, reg, dev->wed_rro.msdu_pg[i].phy_addr >> 4);
++			reg += 4;
++		}
++		mt76_wr(dev, MT_RRO_PARTICULAR_CFG1,
++			MT_RRO_PARTICULAR_CONFG_EN |
++			FIELD_PREP(MT_RRO_PARTICULAR_SID, 1));
++	} else {
++		mt76_wr(dev, MT_RRO_PARTICULAR_CFG1,
++			MT_RRO_PARTICULAR_CONFG_EN |
++			FIELD_PREP(MT_RRO_PARTICULAR_SID, MT7996_RRO_MAX_SESSION));
++	}
++	/* interrupt enable */
++	mt76_wr(dev, MT_RRO_HOST_INT_ENA,
++		MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA);
++#endif
++}
++
++static int mt7996_wed_rro_init(struct mt7996_dev *dev)
++{
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+ 	struct mt7996_wed_rro_addr *addr;
+ 	void *ptr;
+ 	int i;
+@@ -801,50 +881,9 @@ static int mt7996_wed_rro_init(struct mt7996_dev *dev)
+ 		addr++;
+ 	}
+ 
+-	/* rro hw init */
+-	/* TODO: remove line after WM has set */
+-	mt76_clear(dev, WF_RRO_AXI_MST_CFG, WF_RRO_AXI_MST_CFG_DIDX_OK);
+-
+-	/* setup BA bitmap cache address */
+-	mt76_wr(dev, MT_RRO_BA_BITMAP_BASE0,
+-		dev->wed_rro.ba_bitmap[0].phy_addr);
+-	mt76_wr(dev, MT_RRO_BA_BITMAP_BASE1, 0);
+-	mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT0,
+-		dev->wed_rro.ba_bitmap[1].phy_addr);
+-	mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT1, 0);
+-
+-	/* setup Address element address */
+-	for (i = 0; i < ARRAY_SIZE(dev->wed_rro.addr_elem); i++) {
+-		mt76_wr(dev, reg, dev->wed_rro.addr_elem[i].phy_addr >> 4);
+-		reg += 4;
+-	}
+-
+-	/* setup Address element address - separate address segment mode */
+-	mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE1,
+-		MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE);
+-
+-	wed->wlan.ind_cmd.win_size = ffs(MT7996_RRO_WINDOW_MAX_LEN) - 6;
+-	wed->wlan.ind_cmd.particular_sid = MT7996_RRO_MAX_SESSION;
+-	wed->wlan.ind_cmd.particular_se_phys = dev->wed_rro.session.phy_addr;
+-	wed->wlan.ind_cmd.se_group_nums = MT7996_RRO_ADDR_ELEM_LEN;
+-	wed->wlan.ind_cmd.ack_sn_addr = MT_RRO_ACK_SN_CTRL;
+-
+-	mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE0, 0x15010e00);
+-	mt76_set(dev, MT_RRO_IND_CMD_SIGNATURE_BASE1,
+-		 MT_RRO_IND_CMD_SIGNATURE_BASE1_EN);
+-
+-	/* particular session configure */
+-	/* use max session idx + 1 as particular session id */
+-	mt76_wr(dev, MT_RRO_PARTICULAR_CFG0, dev->wed_rro.session.phy_addr);
+-	mt76_wr(dev, MT_RRO_PARTICULAR_CFG1,
+-		MT_RRO_PARTICULAR_CONFG_EN |
+-		FIELD_PREP(MT_RRO_PARTICULAR_SID, MT7996_RRO_MAX_SESSION));
+-
+-	/* interrupt enable */
+-	mt76_wr(dev, MT_RRO_HOST_INT_ENA,
+-		MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA);
+-
+ 	/* rro ind cmd queue init */
++	mt7996_rro_hw_init(dev);
++
+ 	return mt7996_dma_rro_init(dev);
+ #else
+ 	return 0;
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index dfc68a19..be3fea21 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1761,6 +1761,31 @@ mt7996_mac_restart(struct mt7996_dev *dev)
+ 	if (ret)
+ 		goto out;
+ 
++	if (mtk_wed_device_active(&dev->mt76.mmio.wed) && dev->has_rro) {
++		u32 wed_irq_mask = dev->mt76.mmio.irqmask |
++				   MT_INT_RRO_RX_DONE |
++				   MT_INT_TX_DONE_BAND2;
++
++		mt7996_rro_hw_init(dev);
++		mt76_for_each_q_rx(&dev->mt76, i) {
++			if (mt76_queue_is_wed_rro_ind(&dev->mt76.q_rx[i]) ||
++			    mt76_queue_is_wed_rro_msdu_pg(&dev->mt76.q_rx[i]))
++				mt76_queue_rx_reset(dev, i);
++		}
++
++		mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask);
++		mtk_wed_device_start_hwrro(&dev->mt76.mmio.wed, wed_irq_mask, false);
++		mt7996_irq_enable(dev, wed_irq_mask);
++		mt7996_irq_disable(dev, 0);
++	}
++
++	if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2)) {
++		mt76_wr(dev, MT_INT_PCIE1_MASK_CSR,
++			MT_INT_TX_RX_DONE_EXT);
++		mtk_wed_device_start(&dev->mt76.mmio.wed_hif2,
++				     MT_INT_TX_RX_DONE_EXT);
++	}
++
+ 	/* set the necessary init items */
+ 	ret = mt7996_mcu_set_eeprom(dev);
+ 	if (ret)
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index a933e739..f0288cca 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -719,6 +719,7 @@ extern const struct mt76_testmode_ops mt7996_testmode_ops;
+ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
+ 				     void __iomem *mem_base, u32 device_id);
+ void mt7996_wfsys_reset(struct mt7996_dev *dev);
++void mt7996_rro_hw_init(struct mt7996_dev *dev);
+ irqreturn_t mt7996_irq_handler(int irq, void *dev_instance);
+ u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
+ int mt7996_register_device(struct mt7996_dev *dev);
+diff --git a/wed.c b/wed.c
+index 1c6d53c8..61a6badf 100644
+--- a/wed.c
++++ b/wed.c
+@@ -155,7 +155,7 @@ int mt76_wed_dma_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
+ 	case MT76_WED_Q_TXFREE:
+ 		/* WED txfree queue needs ring to be initialized before setup */
+ 		q->flags = 0;
+-		mt76_dma_queue_reset(dev, q);
++		mt76_dma_queue_reset(dev, q, true);
+ 		mt76_dma_rx_fill(dev, q, false);
+ 
+ 		ret = mtk_wed_device_txfree_ring_setup(q->wed, q->regs);
+@@ -184,7 +184,7 @@ int mt76_wed_dma_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
+ 		break;
+ 	case MT76_WED_RRO_Q_IND:
+ 		q->flags &= ~MT_QFLAG_WED;
+-		mt76_dma_queue_reset(dev, q);
++		mt76_dma_queue_reset(dev, q, true);
+ 		mt76_dma_rx_fill(dev, q, false);
+ 		mtk_wed_device_ind_rx_ring_setup(q->wed, q->regs);
+ 		break;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0073-mtk-wifi-mt76-mt7996-Remove-wed_stop-during-L1-SER.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0073-mtk-wifi-mt76-mt7996-Remove-wed_stop-during-L1-SER.patch
deleted file mode 100644
index 4e46bfe..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0073-mtk-wifi-mt76-mt7996-Remove-wed_stop-during-L1-SER.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From 86d498b85d7e85f5c75b83a39a17f744357481b7 Mon Sep 17 00:00:00 2001
-From: Rex Lu <rex.lu@mediatek.com>
-Date: Wed, 29 Nov 2023 13:56:52 +0800
-Subject: [PATCH 073/116] mtk: wifi: mt76: mt7996: Remove wed_stop during L1
- SER
-
-Align logan L1 SER flow. During L1 SER, didn't need to close wed interrupt.
-
-Signed-off-by: Rex Lu <rex.lu@mediatek.com>
----
- mt7996/mac.c | 6 ------
- 1 file changed, 6 deletions(-)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index b470380..0805251 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1950,12 +1950,6 @@ void mt7996_mac_reset_work(struct work_struct *work)
- 	dev_info(dev->mt76.dev,"\n%s L1 SER recovery start.",
- 		 wiphy_name(dev->mt76.hw->wiphy));
- 
--	if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2))
--		mtk_wed_device_stop(&dev->mt76.mmio.wed_hif2);
--
--	if (mtk_wed_device_active(&dev->mt76.mmio.wed))
--		mtk_wed_device_stop(&dev->mt76.mmio.wed);
--
- 	ieee80211_stop_queues(mt76_hw(dev));
- 	if (phy2)
- 		ieee80211_stop_queues(phy2->mt76->hw);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0074-mtk-mt76-mt7996-support-backaward-compatiable.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0074-mtk-mt76-mt7996-support-backaward-compatiable.patch
new file mode 100644
index 0000000..45101c3
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0074-mtk-mt76-mt7996-support-backaward-compatiable.patch
@@ -0,0 +1,206 @@
+From f5504bc5f7c41c0d8f1c944dd34cf22f284ed1c8 Mon Sep 17 00:00:00 2001
+From: mtk27745 <rex.lu@mediatek.com>
+Date: Fri, 6 Oct 2023 20:59:42 +0800
+Subject: [PATCH 074/199] mtk: mt76: mt7996: support backaward compatiable
+
+revert upstream wed trigger mode to polling mode
+
+[Description]
+Change the SW token size from 1024 to 15360 according to HW capability.
+
+[Release-log]
+N/A
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Signed-off-by: mtk27745 <rex.lu@mediatek.com>
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+---
+ mt76.h          |  2 ++
+ mt7996/mac.c    |  3 ++-
+ mt7996/mcu.c    |  2 +-
+ mt7996/mmio.c   | 12 +++++++-----
+ mt7996/mt7996.h |  1 +
+ mt7996/pci.c    | 17 +++++++++--------
+ wed.c           |  4 ++--
+ 7 files changed, 24 insertions(+), 17 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index 1236ddb4..e3c209ff 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -53,6 +53,8 @@
+ 
+ #define MT76_TOKEN_FREE_THR	64
+ 
++#define MT76_WED_SW_TOKEN_SIZE	15360
++
+ #define MT_QFLAG_WED_RING	GENMASK(1, 0)
+ #define MT_QFLAG_WED_TYPE	GENMASK(4, 2)
+ #define MT_QFLAG_WED		BIT(5)
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index be3fea21..1792726c 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1774,7 +1774,7 @@ mt7996_mac_restart(struct mt7996_dev *dev)
+ 		}
+ 
+ 		mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask);
+-		mtk_wed_device_start_hwrro(&dev->mt76.mmio.wed, wed_irq_mask, false);
++		mtk_wed_device_start_hw_rro(&dev->mt76.mmio.wed, wed_irq_mask, false);
+ 		mt7996_irq_enable(dev, wed_irq_mask);
+ 		mt7996_irq_disable(dev, 0);
+ 	}
+@@ -2006,6 +2006,7 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 
+ 		mtk_wed_device_start_hw_rro(&dev->mt76.mmio.wed, wed_irq_mask,
+ 					    true);
++
+ 		mt7996_irq_enable(dev, wed_irq_mask);
+ 		mt7996_irq_disable(dev, 0);
+ 	}
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 4bcc415f..46611191 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3257,7 +3257,7 @@ static int mt7996_mcu_wa_red_config(struct mt7996_dev *dev)
+ 
+ 	if (!mtk_wed_device_active(&dev->mt76.mmio.wed))
+ 		req.token_per_src[RED_TOKEN_SRC_CNT - 1] =
+-			cpu_to_le16(MT7996_TOKEN_SIZE - MT7996_HW_TOKEN_SIZE);
++			cpu_to_le16(MT7996_SW_TOKEN_SIZE);
+ 
+ 	return mt76_mcu_send_msg(&dev->mt76, MCU_WA_PARAM_CMD(SET),
+ 				 &req, sizeof(req), false);
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index 0d25c5ff..35573003 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -14,7 +14,7 @@
+ #include "../trace.h"
+ #include "../dma.h"
+ 
+-static bool wed_enable;
++static bool wed_enable = true;
+ module_param(wed_enable, bool, 0644);
+ 
+ static const struct __base mt7996_reg_base[] = {
+@@ -347,7 +347,7 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ 		}
+ 
+ 		wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + hif1_ofs + MT_WFDMA0_GLO_CFG;
+-		wed->wlan.wpdma_rx = wed->wlan.phy_base + hif1_ofs +
++		wed->wlan.wpdma_rx[0] = wed->wlan.phy_base + hif1_ofs +
+ 				     MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) +
+ 				     MT7996_RXQ_BAND0 * MT_RING_SIZE;
+ 
+@@ -362,7 +362,7 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ 
+ 		wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + MT_WFDMA0_GLO_CFG;
+ 
+-		wed->wlan.wpdma_rx = wed->wlan.phy_base +
++		wed->wlan.wpdma_rx[0] = wed->wlan.phy_base +
+ 				     MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) +
+ 				     MT7996_RXQ_BAND0 * MT_RING_SIZE;
+ 
+@@ -404,8 +404,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ 		dev->mt76.rx_token_size = MT7996_TOKEN_SIZE + wed->wlan.rx_npkt;
+ 	}
+ 
+-	wed->wlan.nbuf = MT7996_HW_TOKEN_SIZE;
+-	wed->wlan.token_start = MT7996_TOKEN_SIZE - wed->wlan.nbuf;
++	wed->wlan.nbuf = MT7996_TOKEN_SIZE;
++	wed->wlan.token_start = 0;
+ 
+ 	wed->wlan.amsdu_max_subframes = 8;
+ 	wed->wlan.amsdu_max_len = 1536;
+@@ -426,6 +426,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ 	*irq = wed->irq;
+ 	dev->mt76.dma_dev = wed->dev;
+ 
++	dev->mt76.token_size = MT7996_SW_TOKEN_SIZE;
++
+ 	return 1;
+ #else
+ 	return 0;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index f0288cca..6254604f 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -74,6 +74,7 @@
+ #define MT7996_EEPROM_BLOCK_SIZE	16
+ #define MT7996_TOKEN_SIZE		16384
+ #define MT7996_HW_TOKEN_SIZE		8192
++#define MT7996_SW_TOKEN_SIZE		15360
+ 
+ #define MT7996_CFEND_RATE_DEFAULT	0x49	/* OFDM 24M */
+ #define MT7996_CFEND_RATE_11B		0x03	/* 11B LP, 11M */
+diff --git a/mt7996/pci.c b/mt7996/pci.c
+index 05830c01..4e957771 100644
+--- a/mt7996/pci.c
++++ b/mt7996/pci.c
+@@ -171,7 +171,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ 
+ 		ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &hif2_irq);
+ 		if (ret < 0)
+-			goto free_hif2_wed_irq_vector;
++			goto free_wed_or_irq_vector;
+ 
+ 		if (!ret) {
+ 			ret = pci_alloc_irq_vectors(hif2_dev, 1, 1,
+@@ -180,14 +180,15 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ 				goto free_hif2;
+ 
+ 			dev->hif2->irq = hif2_dev->irq;
+-			hif2_irq = dev->hif2->irq;
++		} else {
++			dev->hif2->irq = irq;
+ 		}
+ 
+-		ret = devm_request_irq(mdev->dev, hif2_irq, mt7996_irq_handler,
+-				       IRQF_SHARED, KBUILD_MODNAME "-hif",
+-				       dev);
++		ret = devm_request_irq(mdev->dev, dev->hif2->irq,
++				       mt7996_irq_handler, IRQF_SHARED,
++				       KBUILD_MODNAME "-hif", dev);
+ 		if (ret)
+-			goto free_hif2_wed_irq_vector;
++			goto free_hif2_irq_vector;
+ 
+ 		mt76_wr(dev, MT_INT1_MASK_CSR, 0);
+ 		/* master switch of PCIe tnterrupt enable */
+@@ -202,8 +203,8 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ 
+ free_hif2_irq:
+ 	if (dev->hif2)
+-		devm_free_irq(mdev->dev, hif2_irq, dev);
+-free_hif2_wed_irq_vector:
++		devm_free_irq(mdev->dev, dev->hif2->irq, dev);
++free_hif2_irq_vector:
+ 	if (dev->hif2) {
+ 		if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2))
+ 			mtk_wed_device_detach(&dev->mt76.mmio.wed_hif2);
+diff --git a/wed.c b/wed.c
+index 61a6badf..634c95cf 100644
+--- a/wed.c
++++ b/wed.c
+@@ -120,7 +120,7 @@ int mt76_wed_offload_enable(struct mtk_wed_device *wed)
+ 	struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
+ 
+ 	spin_lock_bh(&dev->token_lock);
+-	dev->token_size = wed->wlan.token_start;
++	dev->token_size = MT76_WED_SW_TOKEN_SIZE;
+ 	spin_unlock_bh(&dev->token_lock);
+ 
+ 	return !wait_event_timeout(dev->tx_wait, !dev->wed_token_count, HZ);
+@@ -204,7 +204,7 @@ void mt76_wed_offload_disable(struct mtk_wed_device *wed)
+ 	struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
+ 
+ 	spin_lock_bh(&dev->token_lock);
+-	dev->token_size = dev->drv->token_size;
++	dev->token_size = MT76_WED_SW_TOKEN_SIZE;
+ 	spin_unlock_bh(&dev->token_lock);
+ }
+ EXPORT_SYMBOL_GPL(mt76_wed_offload_disable);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0074-mtk-wifi-mt76-mt7996-Refactor-rro-del-ba-command-for.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0074-mtk-wifi-mt76-mt7996-Refactor-rro-del-ba-command-for.patch
deleted file mode 100644
index 481382a..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0074-mtk-wifi-mt76-mt7996-Refactor-rro-del-ba-command-for.patch
+++ /dev/null
@@ -1,85 +0,0 @@
-From 319c0c7f599ca9835577ccfd91aff59af02b1999 Mon Sep 17 00:00:00 2001
-From: Rex Lu <rex.lu@mediatek.com>
-Date: Wed, 29 Nov 2023 15:51:04 +0800
-Subject: [PATCH 074/116] mtk: wifi: mt76: mt7996: Refactor rro del ba command
- format
-
-1. remove unused struct
-2. refactor upstream del ba command format
-
-Signed-off-by: Rex Lu <rex.lu@mediatek.com>
----
- mt7996/mcu.h | 50 +++-----------------------------------------------
- 1 file changed, 3 insertions(+), 47 deletions(-)
-
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 700330a..a58f52d 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -273,7 +273,9 @@ struct mt7996_mcu_wed_rro_ba_delete_event {
- 	__le16 len;
- 
- 	__le16 session_id;
--	u8 __rsv2[2];
-+	__le16 mld_id;
-+	u8 tid;
-+	u8 __rsv[3];
- } __packed;
- 
- enum  {
-@@ -298,52 +300,6 @@ struct mt7996_mcu_thermal_notify {
- 	u8 __rsv2[4];
- } __packed;
- 
--struct mt7996_mcu_rro_event {
--	struct mt7996_mcu_rxd rxd;
--
--	u8 __rsv1[4];
--
--	__le16 tag;
--	__le16 len;
--} __packed;
--
--struct mt7996_mcu_rro_ba {
--	__le16 tag;
--	__le16 len;
--
--	__le16 wlan_id;
--	u8 tid;
--	u8 __rsv1;
--	__le32 status;
--	__le16 session_id;
--	u8 __rsv2[2];
--} __packed;
--
--struct mt7996_mcu_rro_ba_del_chk_done {
--	__le16 tag;
--	__le16 len;
--
--	__le16 session_id;
--	__le16 mld_id;
--	u8 tid;
--	u8 __rsv[3];
--} __packed;
--
--enum  {
--	UNI_RRO_BA_SESSION_STATUS = 0,
--	UNI_RRO_BA_SESSION_TBL	= 1,
--	UNI_RRO_BA_SESSION_DEL_CHK_DONE = 2,
--	UNI_RRO_BA_SESSION_MAX_NUM
--};
--
--struct mt7996_mcu_rro_del_ba {
--	struct mt7996_mcu_rro_event event;
--
--	u8  wlan_idx;
--	u8  tid;
--	u8 __rsv2[2];
--};
--
- enum mt7996_chan_mib_offs {
- 	UNI_MIB_OBSS_AIRTIME = 26,
- 	UNI_MIB_NON_WIFI_TIME = 27,
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0075-mtk-mt76-mt7996-add-wed-support-for-mt7992.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0075-mtk-mt76-mt7996-add-wed-support-for-mt7992.patch
new file mode 100644
index 0000000..dde0b77
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0075-mtk-mt76-mt7996-add-wed-support-for-mt7992.patch
@@ -0,0 +1,430 @@
+From 3d75798a66a4bedb6b42cf28b7c79ed8076b263b Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Fri, 8 Sep 2023 11:57:39 +0800
+Subject: [PATCH 075/199] mtk: mt76: mt7996: add wed support for mt7992
+
+Fix incomplete WED initialization for Kite band-1 RX ring.
+
+Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ mt7996/dma.c    | 91 +++++++++++++++++++++++++++++++++----------------
+ mt7996/init.c   | 12 +++++++
+ mt7996/mac.c    |  4 +++
+ mt7996/mmio.c   | 49 ++++++++++++++++++--------
+ mt7996/mt7996.h | 10 +++++-
+ mt7996/pci.c    | 10 ++++--
+ mt7996/regs.h   | 14 +++++++-
+ 7 files changed, 142 insertions(+), 48 deletions(-)
+
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index d9e1b17f..d62dc8ba 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -77,18 +77,23 @@ static void mt7996_dma_config(struct mt7996_dev *dev)
+ 			   MT7996_RXQ_RRO_BAND0);
+ 		RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND0, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND0,
+ 			   MT7996_RXQ_MSDU_PG_BAND0);
+-		RXQ_CONFIG(MT_RXQ_TXFREE_BAND0, WFDMA0, MT_INT_RX_TXFREE_MAIN,
+-			   MT7996_RXQ_TXFREE0);
+-		/* band1 */
+-		RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND1, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND1,
+-			   MT7996_RXQ_MSDU_PG_BAND1);
+-		/* band2 */
+-		RXQ_CONFIG(MT_RXQ_RRO_BAND2, WFDMA0, MT_INT_RX_DONE_RRO_BAND2,
+-			   MT7996_RXQ_RRO_BAND2);
+-		RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND2, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND2,
+-			   MT7996_RXQ_MSDU_PG_BAND2);
+-		RXQ_CONFIG(MT_RXQ_TXFREE_BAND2, WFDMA0, MT_INT_RX_TXFREE_TRI,
+-			   MT7996_RXQ_TXFREE2);
++		if (is_mt7996(&dev->mt76)) {
++			RXQ_CONFIG(MT_RXQ_TXFREE_BAND0, WFDMA0, MT_INT_RX_TXFREE_MAIN,
++				   MT7996_RXQ_TXFREE0);
++			/* band1 */
++			RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND1, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND1,
++				   MT7996_RXQ_MSDU_PG_BAND1);
++			/* band2 */
++			RXQ_CONFIG(MT_RXQ_RRO_BAND2, WFDMA0, MT_INT_RX_DONE_RRO_BAND2,
++				   MT7996_RXQ_RRO_BAND2);
++			RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND2, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND2,
++				   MT7996_RXQ_MSDU_PG_BAND2);
++			RXQ_CONFIG(MT_RXQ_TXFREE_BAND2, WFDMA0, MT_INT_RX_TXFREE_TRI,
++				   MT7996_RXQ_TXFREE2);
++		} else {
++			RXQ_CONFIG(MT_RXQ_RRO_BAND1, WFDMA0, MT_INT_RX_DONE_RRO_BAND1,
++				   MT7996_RXQ_RRO_BAND1);
++		}
+ 
+ 		RXQ_CONFIG(MT_RXQ_RRO_IND, WFDMA0, MT_INT_RX_DONE_RRO_IND,
+ 			   MT7996_RXQ_RRO_IND);
+@@ -146,8 +151,13 @@ static void __mt7996_dma_prefetch(struct mt7996_dev *dev, u32 ofs)
+ 	if (dev->has_rro) {
+ 		mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND0) + ofs,
+ 			PREFETCH(0x10));
+-		mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND2) + ofs,
+-			PREFETCH(0x10));
++		if (is_mt7996(&dev->mt76))
++			mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND2) + ofs,
++				PREFETCH(0x10));
++		else
++			mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND1) + ofs,
++				PREFETCH(0x10));
++
+ 		mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND0) + ofs,
+ 			PREFETCH(0x4));
+ 		mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND1) + ofs,
+@@ -361,12 +371,16 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ 		 * so, redirect pcie0 rx ring3 interrupt to pcie1
+ 		 */
+ 		if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
+-		    dev->has_rro)
++		    dev->has_rro) {
++			u32 intr = is_mt7996(&dev->mt76) ?
++				   MT_WFDMA0_RX_INT_SEL_RING6 :
++				   MT_WFDMA0_RX_INT_SEL_RING9;
+ 			mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL + hif1_ofs,
+-				 MT_WFDMA0_RX_INT_SEL_RING6);
+-		else
++				 intr);
++		} else {
+ 			mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL,
+ 				 MT_WFDMA0_RX_INT_SEL_RING3);
++		}
+ 	}
+ 
+ 	mt7996_dma_start(dev, reset, true);
+@@ -401,7 +415,7 @@ int mt7996_dma_rro_init(struct mt7996_dev *dev)
+ 	if (ret)
+ 		return ret;
+ 
+-	if (mt7996_band_valid(dev, MT_BAND1)) {
++	if (mt7996_band_valid(dev, MT_BAND1) && is_mt7996(&dev->mt76)) {
+ 		/* rx msdu page queue for band1 */
+ 		mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND1].flags =
+ 			MT_WED_RRO_Q_MSDU_PG(1) | MT_QFLAG_WED_RRO_EN;
+@@ -522,7 +536,9 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 		return ret;
+ 
+ 	/* tx free notify event from WA for band0 */
+-	if (mtk_wed_device_active(wed) && !dev->has_rro) {
++	if (mtk_wed_device_active(wed) &&
++	    ((is_mt7996(&dev->mt76) && !dev->has_rro) ||
++	     (is_mt7992(&dev->mt76)))) {
+ 		dev->mt76.q_rx[MT_RXQ_MAIN_WA].flags = MT_WED_Q_TXFREE;
+ 		dev->mt76.q_rx[MT_RXQ_MAIN_WA].wed = wed;
+ 	}
+@@ -568,6 +584,11 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 	} else if (mt7996_band_valid(dev, MT_BAND1)) {
+ 		/* rx data queue for mt7992 band1 */
+ 		rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND1) + hif1_ofs;
++		if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed)) {
++			dev->mt76.q_rx[MT_RXQ_BAND1].flags = MT_WED_Q_RX(1);
++			dev->mt76.q_rx[MT_RXQ_BAND1].wed = wed;
++		}
++
+ 		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND1],
+ 				       MT_RXQ_ID(MT_RXQ_BAND1),
+ 				       MT7996_RX_RING_SIZE,
+@@ -601,17 +622,29 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 		if (ret)
+ 			return ret;
+ 
+-		/* tx free notify event from WA for band0 */
+-		dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
+-		dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed;
++		if (is_mt7992(&dev->mt76)) {
++			dev->mt76.q_rx[MT_RXQ_RRO_BAND1].flags =
++				MT_WED_RRO_Q_DATA(1) | MT_QFLAG_WED_RRO_EN;
++			dev->mt76.q_rx[MT_RXQ_RRO_BAND1].wed = wed;
++			ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_RRO_BAND1],
++					       MT_RXQ_ID(MT_RXQ_RRO_BAND1),
++					       MT7996_RX_RING_SIZE,
++					       MT7996_RX_BUF_SIZE,
++					       MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND1));
++			if (ret)
++				return ret;
++		} else {
++			dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
++			dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed;
+ 
+-		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0],
+-				       MT_RXQ_ID(MT_RXQ_TXFREE_BAND0),
+-				       MT7996_RX_MCU_RING_SIZE,
+-				       MT7996_RX_BUF_SIZE,
+-				       MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND0));
+-		if (ret)
+-			return ret;
++			ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0],
++					       MT_RXQ_ID(MT_RXQ_TXFREE_BAND0),
++					       MT7996_RX_MCU_RING_SIZE,
++					       MT7996_RX_BUF_SIZE,
++					       MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND0));
++			if (ret)
++				return ret;
++		}
+ 
+ 		if (mt7996_band_valid(dev, MT_BAND2)) {
+ 			/* rx rro data queue for band2 */
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 3b1f4273..f682423a 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -815,6 +815,7 @@ void mt7996_rro_hw_init(struct mt7996_dev *dev)
+ 	/* interrupt enable */
+ 	mt76_wr(dev, MT_RRO_HOST_INT_ENA,
+ 		MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA);
++
+ #endif
+ }
+ 
+@@ -867,6 +868,17 @@ static int mt7996_wed_rro_init(struct mt7996_dev *dev)
+ 			dev->wed_rro.addr_elem[i].phy_addr;
+ 	}
+ 
++	for (i = 0; i < MT7996_RRO_MSDU_PG_CR_CNT; i++) {
++		ptr = dmam_alloc_coherent(dev->mt76.dma_dev, MT7996_RRO_MSDU_PG_SIZE_PER_CR,
++					  &dev->wed_rro.msdu_pg[i].phy_addr,
++					  GFP_KERNEL);
++		if (!ptr)
++			return -ENOMEM;
++		dev->wed_rro.msdu_pg[i].ptr = ptr;
++
++		memset(dev->wed_rro.msdu_pg[i].ptr, 0, MT7996_RRO_MSDU_PG_SIZE_PER_CR);
++	}
++
+ 	ptr = dmam_alloc_coherent(dev->mt76.dma_dev,
+ 				  MT7996_RRO_WINDOW_MAX_LEN * sizeof(*addr),
+ 				  &dev->wed_rro.session.phy_addr,
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 1792726c..1f3445bb 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2004,6 +2004,10 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 
+ 		mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask);
+ 
++		if (is_mt7992(&dev->mt76) && dev->has_rro)
++			mt76_wr(dev, MT_RRO_3_0_EMU_CONF,
++				MT_RRO_3_0_EMU_CONF_EN_MASK);
++
+ 		mtk_wed_device_start_hw_rro(&dev->mt76.mmio.wed, wed_irq_mask,
+ 					    true);
+ 
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index 35573003..bfe92ceb 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -313,7 +313,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ 
+ 	dev->has_rro = true;
+ 
+-	hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
++	if (dev->hif2)
++		hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
+ 
+ 	if (hif2)
+ 		wed = &dev->mt76.mmio.wed_hif2;
+@@ -348,8 +349,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ 
+ 		wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + hif1_ofs + MT_WFDMA0_GLO_CFG;
+ 		wed->wlan.wpdma_rx[0] = wed->wlan.phy_base + hif1_ofs +
+-				     MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) +
+-				     MT7996_RXQ_BAND0 * MT_RING_SIZE;
++				     MT_RXQ_RING_BASE(MT7996_RXQ_BAND2) +
++				     MT7996_RXQ_BAND2 * MT_RING_SIZE;
+ 
+ 		wed->wlan.id = 0x7991;
+ 		wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND2) - 1;
+@@ -369,9 +370,19 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ 		wed->wlan.wpdma_rx_rro[0] = wed->wlan.phy_base +
+ 					    MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND0) +
+ 					    MT7996_RXQ_RRO_BAND0 * MT_RING_SIZE;
+-		wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base + hif1_ofs +
+-					    MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND2) +
+-					    MT7996_RXQ_RRO_BAND2 * MT_RING_SIZE;
++		if (is_mt7996(&dev->mt76)) {
++			wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base + hif1_ofs +
++						    MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND2) +
++						    MT7996_RXQ_RRO_BAND2 * MT_RING_SIZE;
++		} else {
++			wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base +
++						    MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND1) +
++						    MT7996_RXQ_RRO_BAND1 * MT_RING_SIZE;
++			wed->wlan.wpdma_rx[1] = wed->wlan.phy_base +
++						MT_RXQ_RING_BASE(MT7996_RXQ_BAND1) +
++						MT7996_RXQ_BAND1 * MT_RING_SIZE;
++		}
++
+ 		wed->wlan.wpdma_rx_pg = wed->wlan.phy_base +
+ 					MT_RXQ_RING_BASE(MT7996_RXQ_MSDU_PG_BAND0) +
+ 					MT7996_RXQ_MSDU_PG_BAND0 * MT_RING_SIZE;
+@@ -381,10 +392,14 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ 		wed->wlan.rx_size = SKB_WITH_OVERHEAD(MT_RX_BUF_SIZE);
+ 
+ 		wed->wlan.rx_tbit[0] = ffs(MT_INT_RX_DONE_BAND0) - 1;
+-		wed->wlan.rx_tbit[1] = ffs(MT_INT_RX_DONE_BAND2) - 1;
+-
+ 		wed->wlan.rro_rx_tbit[0] = ffs(MT_INT_RX_DONE_RRO_BAND0) - 1;
+-		wed->wlan.rro_rx_tbit[1] = ffs(MT_INT_RX_DONE_RRO_BAND2) - 1;
++		if (is_mt7996(&dev->mt76)) {
++			wed->wlan.rx_tbit[1] = ffs(MT_INT_RX_DONE_BAND2) - 1;
++			wed->wlan.rro_rx_tbit[1] = ffs(MT_INT_RX_DONE_RRO_BAND2) - 1;
++		} else {
++			wed->wlan.rx_tbit[1] = ffs(MT_INT_RX_DONE_BAND1) - 1;
++			wed->wlan.rro_rx_tbit[1] = ffs(MT_INT_RX_DONE_RRO_BAND1) - 1;
++		}
+ 
+ 		wed->wlan.rx_pg_tbit[0] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND0) - 1;
+ 		wed->wlan.rx_pg_tbit[1] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND1) - 1;
+@@ -392,14 +407,20 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ 
+ 		wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND0) - 1;
+ 		wed->wlan.tx_tbit[1] = ffs(MT_INT_TX_DONE_BAND1) - 1;
+-		if (dev->has_rro) {
+-			wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
+-						 MT7996_RXQ_TXFREE0 * MT_RING_SIZE;
+-			wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_MAIN) - 1;
++		if (is_mt7996(&dev->mt76)) {
++			if (dev->has_rro) {
++				wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
++							 MT7996_RXQ_TXFREE0 * MT_RING_SIZE;
++				wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_MAIN) - 1;
++			} else {
++				wed->wlan.txfree_tbit = ffs(MT_INT_RX_DONE_WA_MAIN) - 1;
++				wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
++							 MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE;
++			}
+ 		} else {
+ 			wed->wlan.txfree_tbit = ffs(MT_INT_RX_DONE_WA_MAIN) - 1;
+ 			wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
+-						  MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE;
++						 MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE;
+ 		}
+ 		dev->mt76.rx_token_size = MT7996_TOKEN_SIZE + wed->wlan.rx_npkt;
+ 	}
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 6254604f..834e8fc0 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -122,6 +122,10 @@
+ #define MT7996_DRR_STA_AC2_QNTM_MASK	GENMASK(18, 16)
+ #define MT7996_DRR_STA_AC3_QNTM_MASK	GENMASK(22, 20)
+ 
++/* RRO 3.1 */
++#define MT7996_RRO_MSDU_PG_CR_CNT 8
++#define MT7996_RRO_MSDU_PG_SIZE_PER_CR 0x10000
++
+ struct mt7996_vif;
+ struct mt7996_sta;
+ struct mt7996_dfs_pulse;
+@@ -181,7 +185,7 @@ enum mt7996_rxq_id {
+ 	MT7996_RXQ_BAND1 = 5, /* for mt7992 */
+ 	MT7996_RXQ_BAND2 = 5,
+ 	MT7996_RXQ_RRO_BAND0 = 8,
+-	MT7996_RXQ_RRO_BAND1 = 8,/* unused */
++	MT7996_RXQ_RRO_BAND1 = 9,
+ 	MT7996_RXQ_RRO_BAND2 = 6,
+ 	MT7996_RXQ_MSDU_PG_BAND0 = 10,
+ 	MT7996_RXQ_MSDU_PG_BAND1 = 11,
+@@ -547,6 +551,10 @@ struct mt7996_dev {
+ 			void *ptr;
+ 			dma_addr_t phy_addr;
+ 		} session;
++		struct {
++			void *ptr;
++			dma_addr_t phy_addr;
++		} msdu_pg[MT7996_RRO_MSDU_PG_CR_CNT];
+ 
+ 		struct work_struct work;
+ 		struct list_head poll_list;
+diff --git a/mt7996/pci.c b/mt7996/pci.c
+index 4e957771..f0d3f199 100644
+--- a/mt7996/pci.c
++++ b/mt7996/pci.c
+@@ -107,7 +107,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ 	struct pci_dev *hif2_dev;
+ 	struct mt7996_hif *hif2;
+ 	struct mt7996_dev *dev;
+-	int irq, hif2_irq, ret;
++	int irq, ret;
+ 	struct mt76_dev *mdev;
+ 
+ 	hif2_enable |= (id->device == 0x7990 || id->device == 0x7991);
+@@ -143,6 +143,8 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ 	mdev = &dev->mt76;
+ 	mt7996_wfsys_reset(dev);
+ 	hif2 = mt7996_pci_init_hif2(pdev);
++	if (hif2)
++		dev->hif2 = hif2;
+ 
+ 	ret = mt7996_mmio_wed_init(dev, pdev, false, &irq);
+ 	if (ret < 0)
+@@ -167,9 +169,11 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ 
+ 	if (hif2) {
+ 		hif2_dev = container_of(hif2->dev, struct pci_dev, dev);
+-		dev->hif2 = hif2;
++		ret = 0;
++
++		if (is_mt7996(&dev->mt76))
++			ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &irq);
+ 
+-		ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &hif2_irq);
+ 		if (ret < 0)
+ 			goto free_wed_or_irq_vector;
+ 
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index 91159c63..e6427a35 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -77,6 +77,8 @@ enum offs_rev {
+ #define MT_RRO_BA_BITMAP_BASE1			MT_RRO_TOP(0xC)
+ #define WF_RRO_AXI_MST_CFG			MT_RRO_TOP(0xB8)
+ #define WF_RRO_AXI_MST_CFG_DIDX_OK		BIT(12)
++
++#define MT_RRO_ADDR_ARRAY_BASE0			MT_RRO_TOP(0x30)
+ #define MT_RRO_ADDR_ARRAY_BASE1			MT_RRO_TOP(0x34)
+ #define MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE	BIT(31)
+ 
+@@ -97,6 +99,14 @@ enum offs_rev {
+ 
+ #define MT_RRO_ADDR_ELEM_SEG_ADDR0		MT_RRO_TOP(0x400)
+ 
++#define MT_RRO_3_0_EMU_CONF			MT_RRO_TOP(0x600)
++#define MT_RRO_3_0_EMU_CONF_EN_MASK		BIT(11)
++
++#define MT_RRO_3_1_GLOBAL_CONFIG		MT_RRO_TOP(0x604)
++#define MT_RRO_3_1_GLOBAL_CONFIG_INTERLEAVE_EN	BIT(0)
++
++#define MT_RRO_MSDU_PG_SEG_ADDR0		MT_RRO_TOP(0x620)
++
+ #define MT_RRO_ACK_SN_CTRL			MT_RRO_TOP(0x50)
+ #define MT_RRO_ACK_SN_CTRL_SN_MASK		GENMASK(27, 16)
+ #define MT_RRO_ACK_SN_CTRL_SESSION_MASK		GENMASK(11, 0)
+@@ -402,6 +412,7 @@ enum offs_rev {
+ #define MT_WFDMA0_RX_INT_PCIE_SEL		MT_WFDMA0(0x154)
+ #define MT_WFDMA0_RX_INT_SEL_RING3		BIT(3)
+ #define MT_WFDMA0_RX_INT_SEL_RING6		BIT(6)
++#define MT_WFDMA0_RX_INT_SEL_RING9		BIT(9)
+ 
+ #define MT_WFDMA0_MCU_HOST_INT_ENA		MT_WFDMA0(0x1f4)
+ 
+@@ -503,13 +514,14 @@ enum offs_rev {
+ #define MT_INT_RX_DONE_WA_EXT			BIT(3) /* for mt7992 */
+ #define MT_INT_RX_DONE_WA_TRI			BIT(3)
+ #define MT_INT_RX_TXFREE_MAIN			BIT(17)
++#define MT_INT_RX_TXFREE_BAND1			BIT(15)
+ #define MT_INT_RX_TXFREE_TRI			BIT(15)
+ #define MT_INT_RX_DONE_BAND2_EXT		BIT(23)
+ #define MT_INT_RX_TXFREE_EXT			BIT(26)
+ #define MT_INT_MCU_CMD				BIT(29)
+ 
+ #define MT_INT_RX_DONE_RRO_BAND0		BIT(16)
+-#define MT_INT_RX_DONE_RRO_BAND1		BIT(16)
++#define MT_INT_RX_DONE_RRO_BAND1		BIT(17)
+ #define MT_INT_RX_DONE_RRO_BAND2		BIT(14)
+ #define MT_INT_RX_DONE_RRO_IND			BIT(11)
+ #define MT_INT_RX_DONE_MSDU_PG_BAND0		BIT(18)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0075-mtk-wifi-mt76-mt7996-get-airtime-and-RSSI-via-MCU-co.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0075-mtk-wifi-mt76-mt7996-get-airtime-and-RSSI-via-MCU-co.patch
deleted file mode 100644
index a553070..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0075-mtk-wifi-mt76-mt7996-get-airtime-and-RSSI-via-MCU-co.patch
+++ /dev/null
@@ -1,784 +0,0 @@
-From a08f12c4e90f1c849c3e8dc0db60fe294b63f3fe Mon Sep 17 00:00:00 2001
-From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
-Date: Fri, 17 Nov 2023 18:08:06 +0800
-Subject: [PATCH 075/116] mtk: wifi: mt76: mt7996: get airtime and RSSI via MCU
- commands
-
-Direct access to WTBL for airtime and RSSI may cause synchronization issue with FW.
-Moreover, frequent access to WTBL, whenever TX-Free-Done event is received, leads to heavy CPU overheads.
-Therefore, indirect access to WTBL, through FW, with lower frequence is performed.
-
-Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
----
- mt76.h               |  20 +++++
- mt76_connac_mcu.h    |  14 +++-
- mt7996/debugfs.c     |  17 ++---
- mt7996/mac.c         | 145 ++++++-----------------------------
- mt7996/mcu.c         | 177 +++++++++++++++++++++++++++++++++++++++++--
- mt7996/mcu.h         |  32 +++++++-
- mt7996/mt7996.h      |  26 ++++++-
- mt7996/mtk_debugfs.c |  71 +++++++++++++++++
- mt7996/regs.h        |   2 +
- 9 files changed, 361 insertions(+), 143 deletions(-)
-
-diff --git a/mt76.h b/mt76.h
-index 677f68f..28defd4 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -332,11 +332,15 @@ struct mt76_sta_stats {
- 	u32 tx_packets;		/* unit: MSDU */
- 	u32 tx_retries;
- 	u32 tx_failed;
-+	u32 tx_total_mpdu_cnt;
-+	u32 tx_failed_mpdu_cnt;
-+	u64 tx_airtime;
- 	/* WED RX */
- 	u64 rx_bytes;
- 	u32 rx_packets;
- 	u32 rx_errors;
- 	u32 rx_drops;
-+	u64 rx_airtime;
- };
- 
- enum mt76_wcid_flags {
-@@ -1335,6 +1339,22 @@ static inline int mt76_decr(int val, int size)
- 
- u8 mt76_ac_to_hwq(u8 ac);
- 
-+static inline u8
-+mt76_ac_to_tid(u8 ac)
-+{
-+	static const u8 ac_to_tid[] = {
-+		[IEEE80211_AC_BE] = 0,
-+		[IEEE80211_AC_BK] = 1,
-+		[IEEE80211_AC_VI] = 4,
-+		[IEEE80211_AC_VO] = 6
-+	};
-+
-+	if (WARN_ON(ac >= IEEE80211_NUM_ACS))
-+		return 0;
-+
-+	return ac_to_tid[ac];
-+}
-+
- static inline struct ieee80211_txq *
- mtxq_to_txq(struct mt76_txq *mtxq)
- {
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 61a14a8..f389470 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1372,11 +1372,23 @@ enum {
- 	UNI_OFFLOAD_OFFLOAD_BMC_RPY_DETECT,
- };
- 
-+enum UNI_PER_STA_INFO_TAG {
-+	UNI_PER_STA_RSSI,
-+	UNI_PER_STA_CONTENTION_RX_RATE,
-+	UNI_PER_STA_PER,
-+	UNI_PER_STA_SNR,
-+	UNI_PER_STA_TX_RATE,
-+	UNI_PER_STA_TX_CNT,
-+	UNI_PER_STA_TID_SN_GET,
-+	UNI_PER_STA_TID_SN_SET,
-+	UNI_PER_STA_MAX_NUM
-+};
-+
- enum UNI_ALL_STA_INFO_TAG {
- 	UNI_ALL_STA_TXRX_RATE,
- 	UNI_ALL_STA_TX_STAT,
- 	UNI_ALL_STA_TXRX_ADM_STAT,
--	UNI_ALL_STA_TXRX_AIR_TIME,
-+	UNI_ALL_STA_TXRX_AIRTIME,
- 	UNI_ALL_STA_DATA_TX_RETRY_COUNT,
- 	UNI_ALL_STA_GI_MODE,
- 	UNI_ALL_STA_TXRX_MSDU_COUNT,
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index f8ba573..e26de48 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -992,12 +992,11 @@ mt7996_airtime_read(struct seq_file *s, void *data)
- {
- 	struct mt7996_dev *dev = dev_get_drvdata(s->private);
- 	struct mt76_dev *mdev = &dev->mt76;
--	struct mt7996_vow_sta_ctrl *vow;
-+	struct mt76_sta_stats *stats;
- 	struct ieee80211_sta *sta;
- 	struct mt7996_sta *msta;
- 	struct mt76_wcid *wcid;
- 	struct mt76_vif *vif;
--	u64 airtime;
- 	u16 i;
- 
- 	seq_printf(s, "VoW Airtime Information:\n");
-@@ -1009,16 +1008,16 @@ mt7996_airtime_read(struct seq_file *s, void *data)
- 
- 		msta = container_of(wcid, struct mt7996_sta, wcid);
- 		sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
--		vow = &msta->vow;
- 		vif = &msta->vif->mt76;
-+		stats = &wcid->stats;
- 
--		spin_lock_bh(&vow->lock);
--		airtime = vow->tx_airtime;
--		vow->tx_airtime = 0;
--		spin_unlock_bh(&vow->lock);
-+		seq_printf(s, "%pM WCID: %hu BandIdx: %hhu OmacIdx: 0x%hhx\t"
-+		              "TxAirtime: %llu\tRxAirtime: %llu\n",
-+		              sta->addr, i, vif->band_idx, vif->omac_idx,
-+		              stats->tx_airtime, stats->rx_airtime);
- 
--		seq_printf(s, "%pM WCID: %hu BandIdx: %hhu OmacIdx: 0x%hhx\tTxAirtime: %llu\n",
--		           sta->addr, i, vif->band_idx, vif->omac_idx, airtime);
-+		stats->tx_airtime = 0;
-+		stats->rx_airtime = 0;
- 	}
- 	rcu_read_unlock();
- 
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 0805251..782594c 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -12,8 +12,6 @@
- #include "mcu.h"
- #include "vendor.h"
- 
--#define to_rssi(field, rcpi)	((FIELD_GET(field, rcpi) - 220) / 2)
--
- static const struct mt7996_dfs_radar_spec etsi_radar_specs = {
- 	.pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 },
- 	.radar_pattern = {
-@@ -93,110 +91,6 @@ u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw)
- 	return MT_WTBL_LMAC_OFFS(wcid, dw);
- }
- 
--static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
--{
--	static const u8 ac_to_tid[] = {
--		[IEEE80211_AC_BE] = 0,
--		[IEEE80211_AC_BK] = 1,
--		[IEEE80211_AC_VI] = 4,
--		[IEEE80211_AC_VO] = 6
--	};
--	struct ieee80211_sta *sta;
--	struct mt7996_sta *msta;
--	struct mt7996_vow_sta_ctrl *vow;
--	u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS];
--	LIST_HEAD(sta_poll_list);
--	int i;
--
--	spin_lock_bh(&dev->mt76.sta_poll_lock);
--	list_splice_init(&dev->mt76.sta_poll_list, &sta_poll_list);
--	spin_unlock_bh(&dev->mt76.sta_poll_lock);
--
--	rcu_read_lock();
--
--	while (true) {
--		bool clear = false;
--		u32 addr, val;
--		u16 idx;
--		s8 rssi[4];
--
--		spin_lock_bh(&dev->mt76.sta_poll_lock);
--		if (list_empty(&sta_poll_list)) {
--			spin_unlock_bh(&dev->mt76.sta_poll_lock);
--			break;
--		}
--		msta = list_first_entry(&sta_poll_list,
--					struct mt7996_sta, wcid.poll_list);
--		list_del_init(&msta->wcid.poll_list);
--		spin_unlock_bh(&dev->mt76.sta_poll_lock);
--
--		idx = msta->wcid.idx;
--
--		/* refresh peer's airtime reporting */
--		addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 20);
--
--		for (i = 0; i < IEEE80211_NUM_ACS; i++) {
--			u32 tx_last = msta->airtime_ac[i];
--			u32 rx_last = msta->airtime_ac[i + 4];
--
--			msta->airtime_ac[i] = mt76_rr(dev, addr);
--			msta->airtime_ac[i + 4] = mt76_rr(dev, addr + 4);
--
--			tx_time[i] = msta->airtime_ac[i] - tx_last;
--			rx_time[i] = msta->airtime_ac[i + 4] - rx_last;
--
--			if ((tx_last | rx_last) & BIT(30))
--				clear = true;
--
--			addr += 8;
--		}
--
--		if (clear) {
--			mt7996_mac_wtbl_update(dev, idx,
--					       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
--			memset(msta->airtime_ac, 0, sizeof(msta->airtime_ac));
--		}
--
--		if (!msta->wcid.sta)
--			continue;
--
--		sta = container_of((void *)msta, struct ieee80211_sta,
--				   drv_priv);
--		vow = &msta->vow;
--		for (i = 0; i < IEEE80211_NUM_ACS; i++) {
--			u8 q = mt76_connac_lmac_mapping(i);
--			u32 tx_cur = tx_time[q];
--			u32 rx_cur = rx_time[q];
--			u8 tid = ac_to_tid[i];
--
--			if (!tx_cur && !rx_cur)
--				continue;
--
--			ieee80211_sta_register_airtime(sta, tid, tx_cur, rx_cur);
--
--			spin_lock_bh(&vow->lock);
--			vow->tx_airtime += tx_cur;
--			spin_unlock_bh(&vow->lock);
--		}
--
--		/* get signal strength of resp frames (CTS/BA/ACK) */
--		addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 34);
--		val = mt76_rr(dev, addr);
--
--		rssi[0] = to_rssi(GENMASK(7, 0), val);
--		rssi[1] = to_rssi(GENMASK(15, 8), val);
--		rssi[2] = to_rssi(GENMASK(23, 16), val);
--		rssi[3] = to_rssi(GENMASK(31, 14), val);
--
--		msta->ack_signal =
--			mt76_rx_signal(msta->vif->phy->mt76->antenna_mask, rssi);
--
--		ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal);
--	}
--
--	rcu_read_unlock();
--}
--
- void mt7996_mac_enable_rtscts(struct mt7996_dev *dev,
- 			      struct ieee80211_vif *vif, bool enable)
- {
-@@ -1206,8 +1100,6 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
- 		}
- 	}
- 
--	mt7996_mac_sta_poll(dev);
--
- 	if (wake)
- 		mt76_set_tx_blocked(&dev->mt76, false);
- 
-@@ -2379,31 +2271,42 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
- 
- void mt7996_mac_work(struct work_struct *work)
- {
--	struct mt7996_phy *phy;
--	struct mt76_phy *mphy;
--
--	mphy = (struct mt76_phy *)container_of(work, struct mt76_phy,
--					       mac_work.work);
--	phy = mphy->priv;
-+	struct mt76_phy *mphy = (struct mt76_phy *)container_of(work, struct mt76_phy,
-+	                                                        mac_work.work);
-+	struct mt7996_phy *phy = mphy->priv;
-+	struct mt76_dev *mdev = mphy->dev;
- 
--	mutex_lock(&mphy->dev->mutex);
-+	mutex_lock(&mdev->mutex);
- 
- 	mt76_update_survey(mphy);
- 	if (++mphy->mac_work_count == 5) {
-+		int i;
-+
- 		mphy->mac_work_count = 0;
- 
- 		mt7996_mac_update_stats(phy);
- 
--		mt7996_mcu_get_all_sta_info(phy, UNI_ALL_STA_TXRX_RATE);
--		if (mtk_wed_device_active(&phy->dev->mt76.mmio.wed)) {
--			mt7996_mcu_get_all_sta_info(phy, UNI_ALL_STA_TXRX_ADM_STAT);
--			mt7996_mcu_get_all_sta_info(phy, UNI_ALL_STA_TXRX_MSDU_COUNT);
-+		/* Update DEV-wise information only in
-+		 * the MAC work of the first band running.
-+		 */
-+		for (i = MT_BAND0; i <= mphy->band_idx; ++i) {
-+			if (i == mphy->band_idx) {
-+				mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_RATE);
-+				mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_AIRTIME);
-+				mt7996_mcu_get_rssi(mdev);
-+				if (mtk_wed_device_active(&mdev->mmio.wed)) {
-+					mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_ADM_STAT);
-+					mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_MSDU_COUNT);
-+				}
-+			} else if (mt7996_band_valid(phy->dev, i) &&
-+			           test_bit(MT76_STATE_RUNNING, &mdev->phys[i]->state))
-+				break;
- 		}
- 	}
- 
--	mutex_unlock(&mphy->dev->mutex);
-+	mutex_unlock(&mdev->mutex);
- 
--	mt76_tx_status_check(mphy->dev, false);
-+	mt76_tx_status_check(mdev, false);
- 
- 	ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
- 				     MT7996_WATCHDOG_TIME);
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 6366cf0..b02303b 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -563,7 +563,8 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- 		u16 wlan_idx;
- 		struct mt76_wcid *wcid;
- 		struct mt76_phy *mphy;
--		u32 tx_bytes, rx_bytes, tx_packets, rx_packets;
-+		struct ieee80211_sta *sta;
-+		u32 tx_bytes, rx_bytes, tx_airtime, rx_airtime, tx_packets, rx_packets;
- 
- 		switch (le16_to_cpu(res->tag)) {
- 		case UNI_ALL_STA_TXRX_RATE:
-@@ -584,7 +585,7 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- 				break;
- 
- 			mphy = mt76_dev_phy(&dev->mt76, wcid->phy_idx);
--			for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-+			for (ac = IEEE80211_AC_VO; ac < IEEE80211_NUM_ACS; ac++) {
- 				tx_bytes = le32_to_cpu(res->adm_stat[i].tx_bytes[ac]);
- 				rx_bytes = le32_to_cpu(res->adm_stat[i].rx_bytes[ac]);
- 
-@@ -616,6 +617,24 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- 			__mt7996_stat_to_netdev(mphy, wcid, 0, 0,
- 						tx_packets, rx_packets);
- 			break;
-+		case UNI_ALL_STA_TXRX_AIRTIME:
-+			wlan_idx = le16_to_cpu(res->airtime[i].wlan_idx);
-+			wcid = rcu_dereference(dev->mt76.wcid[wlan_idx]);
-+			sta = wcid_to_sta(wcid);
-+			if (!sta)
-+				continue;
-+
-+			for (ac = IEEE80211_AC_VO; ac < IEEE80211_NUM_ACS; ++ac) {
-+				u8 lmac_ac = mt76_connac_lmac_mapping(ac);
-+				tx_airtime = le32_to_cpu(res->airtime[i].tx[lmac_ac]);
-+				rx_airtime = le32_to_cpu(res->airtime[i].rx[lmac_ac]);
-+
-+				wcid->stats.tx_airtime += tx_airtime;
-+				wcid->stats.rx_airtime += rx_airtime;
-+				ieee80211_sta_register_airtime(sta, mt76_ac_to_tid(ac),
-+				                               tx_airtime, rx_airtime);
-+			}
-+			break;
- 		default:
- 			break;
- 		}
-@@ -2244,8 +2263,6 @@ mt7996_mcu_sta_init_vow(struct mt7996_phy *phy, struct mt7996_sta *msta)
- 	vow->drr_quantum[IEEE80211_AC_VI] = VOW_DRR_QUANTUM_IDX1;
- 	vow->drr_quantum[IEEE80211_AC_BE] = VOW_DRR_QUANTUM_IDX2;
- 	vow->drr_quantum[IEEE80211_AC_BK] = VOW_DRR_QUANTUM_IDX2;
--	vow->tx_airtime = 0;
--	spin_lock_init(&vow->lock);
- 
- 	ret = mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_BSS_GROUP);
- 	if (ret)
-@@ -4839,9 +4856,155 @@ int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u16 val)
- 				 sizeof(req), true);
- }
- 
--int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag)
-+int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
-+	                        u16 sta_num, u16 *sta_list)
-+{
-+#define PER_STA_INFO_MAX_NUM	90
-+	struct mt7996_mcu_per_sta_info_event *res;
-+	struct mt76_wcid *wcid;
-+	struct sk_buff *skb;
-+	u16 wlan_idx;
-+	int i, ret;
-+	struct {
-+		u8 __rsv1;
-+		u8 unsolicit;
-+		u8 __rsv2[2];
-+
-+		__le16 tag;
-+		__le16 len;
-+		__le16 sta_num;
-+		u8 __rsv3[2];
-+		__le16 sta_list[PER_STA_INFO_MAX_NUM];
-+	} __packed req = {
-+		.unsolicit = 0,
-+		.tag = cpu_to_le16(tag),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.sta_num = cpu_to_le16(sta_num)
-+	};
-+
-+	if (sta_num > PER_STA_INFO_MAX_NUM)
-+		return -EINVAL;
-+
-+	for (i = 0; i < sta_num; ++i)
-+		req.sta_list[i] = cpu_to_le16(sta_list[i]);
-+
-+	ret = mt76_mcu_send_and_get_msg(dev, MCU_WM_UNI_CMD(PER_STA_INFO),
-+	                                &req, sizeof(req), true, &skb);
-+	if (ret)
-+		return ret;
-+
-+	res = (struct mt7996_mcu_per_sta_info_event *)skb->data;
-+	if (le16_to_cpu(res->tag) != tag) {
-+		ret = -EINVAL;
-+		goto out;
-+	}
-+
-+	rcu_read_lock();
-+	switch (tag) {
-+	case UNI_PER_STA_RSSI:
-+		for (i = 0; i < sta_num; ++i) {
-+			struct mt7996_sta *msta;
-+			struct mt76_phy *phy;
-+			s8 rssi[4];
-+			u8 *rcpi;
-+
-+			wlan_idx = le16_to_cpu(res->rssi[i].wlan_idx);
-+			wcid = rcu_dereference(dev->wcid[wlan_idx]);
-+			if (wcid) {
-+				rcpi = res->rssi[i].rcpi;
-+				rssi[0] = to_rssi(MT_PRXV_RCPI0, rcpi[0]);
-+				rssi[1] = to_rssi(MT_PRXV_RCPI0, rcpi[1]);
-+				rssi[2] = to_rssi(MT_PRXV_RCPI0, rcpi[2]);
-+				rssi[3] = to_rssi(MT_PRXV_RCPI0, rcpi[3]);
-+
-+				msta = container_of(wcid, struct mt7996_sta, wcid);
-+				phy = msta->vif->phy->mt76;
-+				msta->ack_signal = mt76_rx_signal(phy->antenna_mask, rssi);
-+				ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal);
-+			} else {
-+				ret = -EINVAL;
-+				dev_err(dev->dev, "Failed to update RSSI for "
-+				                  "invalid WCID: %hu\n", wlan_idx);
-+			}
-+		}
-+		break;
-+	case UNI_PER_STA_TX_CNT:
-+		for (i = 0; i < sta_num; ++i) {
-+			wlan_idx = le16_to_cpu(res->tx_cnt[i].wlan_idx);
-+			wcid = rcu_dereference(dev->wcid[wlan_idx]);
-+			if (wcid) {
-+				wcid->stats.tx_total_mpdu_cnt +=
-+				            le32_to_cpu(res->tx_cnt[i].total);
-+				wcid->stats.tx_failed_mpdu_cnt +=
-+				            le32_to_cpu(res->tx_cnt[i].failed);
-+			} else {
-+				ret = -EINVAL;
-+				dev_err(dev->dev, "Failed to update TX MPDU counts "
-+				                  "for invalid WCID: %hu\n", wlan_idx);
-+			}
-+		}
-+		break;
-+	default:
-+		ret = -EINVAL;
-+		dev_err(dev->dev, "Unknown UNI_PER_STA_INFO_TAG: %d\n", tag);
-+	}
-+	rcu_read_unlock();
-+out:
-+	dev_kfree_skb(skb);
-+	return ret;
-+}
-+
-+int mt7996_mcu_get_rssi(struct mt76_dev *dev)
-+{
-+	u16 sta_list[PER_STA_INFO_MAX_NUM];
-+	LIST_HEAD(sta_poll_list);
-+	struct mt7996_sta *msta;
-+	int i, ret;
-+	bool empty = false;
-+
-+	spin_lock_bh(&dev->sta_poll_lock);
-+	list_splice_init(&dev->sta_poll_list, &sta_poll_list);
-+	spin_unlock_bh(&dev->sta_poll_lock);
-+
-+	while (!empty) {
-+		for (i = 0; i < PER_STA_INFO_MAX_NUM; ++i) {
-+			spin_lock_bh(&dev->sta_poll_lock);
-+			if (list_empty(&sta_poll_list)) {
-+				spin_unlock_bh(&dev->sta_poll_lock);
-+
-+				if (i == 0)
-+					return 0;
-+
-+				empty = true;
-+				break;
-+			}
-+			msta = list_first_entry(&sta_poll_list,
-+			                        struct mt7996_sta,
-+			                        wcid.poll_list);
-+			list_del_init(&msta->wcid.poll_list);
-+			spin_unlock_bh(&dev->sta_poll_lock);
-+
-+			sta_list[i] = msta->wcid.idx;
-+		}
-+
-+		ret = mt7996_mcu_get_per_sta_info(dev, UNI_PER_STA_RSSI,
-+		                                  i, sta_list);
-+		if (ret) {
-+			/* Add STAs, whose RSSI has not been updated,
-+			 * back to polling list.
-+			 */
-+			spin_lock_bh(&dev->sta_poll_lock);
-+			list_splice(&sta_poll_list, &dev->sta_poll_list);
-+			spin_unlock_bh(&dev->sta_poll_lock);
-+			break;
-+		}
-+	}
-+
-+	return ret;
-+}
-+
-+int mt7996_mcu_get_all_sta_info(struct mt76_dev *dev, u16 tag)
- {
--	struct mt7996_dev *dev = phy->dev;
- 	struct {
- 		u8 _rsv[4];
- 
-@@ -4852,7 +5015,7 @@ int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag)
- 		.len = cpu_to_le16(sizeof(req) - 4),
- 	};
- 
--	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(ALL_STA_INFO),
-+	return mt76_mcu_send_msg(dev, MCU_WM_UNI_CMD(ALL_STA_INFO),
- 				 &req, sizeof(req), false);
- }
- 
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index a58f52d..e64812c 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -199,6 +199,31 @@ struct mt7996_mcu_mib {
- 	__le64 data;
- } __packed;
- 
-+struct per_sta_rssi {
-+	__le16 wlan_idx;
-+	u8 __rsv[2];
-+	u8 rcpi[4];
-+} __packed;
-+
-+struct per_sta_tx_cnt {
-+	__le16 wlan_idx;
-+	u8 __rsv[2];
-+	__le32 total;
-+	__le32 failed;
-+} __packed;
-+
-+struct mt7996_mcu_per_sta_info_event {
-+	u8 __rsv[4];
-+
-+	__le16 tag;
-+	__le16 len;
-+
-+	union {
-+		struct per_sta_rssi rssi[0];
-+		struct per_sta_tx_cnt tx_cnt[0];
-+	};
-+} __packed;
-+
- struct all_sta_trx_rate {
- 	__le16 wlan_idx;
- 	u8 __rsv1[2];
-@@ -237,13 +262,18 @@ struct mt7996_mcu_all_sta_info_event {
- 			__le32 tx_bytes[IEEE80211_NUM_ACS];
- 			__le32 rx_bytes[IEEE80211_NUM_ACS];
- 		} adm_stat[0] __packed;
--
- 		struct {
- 			__le16 wlan_idx;
- 			u8 rsv[2];
- 			__le32 tx_msdu_cnt;
- 			__le32 rx_msdu_cnt;
- 		} msdu_cnt[0] __packed;
-+		struct {
-+			__le16 wlan_idx;
-+			u8 __rsv[2];
-+			__le32 tx[IEEE80211_NUM_ACS];
-+			__le32 rx[IEEE80211_NUM_ACS];
-+		} airtime[0] __packed;
- 	} __packed;
- } __packed;
- 
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 929a077..a0cc8f3 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -125,6 +125,8 @@
- #define MT7996_RRO_MSDU_PG_CR_CNT 8
- #define MT7996_RRO_MSDU_PG_SIZE_PER_CR 0x10000
- 
-+#define to_rssi(field, rcpi)	((FIELD_GET(field, rcpi) - 220) / 2)
-+
- struct mt7996_vif;
- struct mt7996_sta;
- struct mt7996_dfs_pulse;
-@@ -297,8 +299,6 @@ struct mt7996_vow_sta_ctrl {
- 	bool paused;
- 	u8 bss_grp_idx;
- 	u8 drr_quantum[IEEE80211_NUM_ACS];
--	u64 tx_airtime;
--	spinlock_t lock;
- };
- 
- struct mt7996_sta {
-@@ -307,7 +307,6 @@ struct mt7996_sta {
- 	struct mt7996_vif *vif;
- 
- 	struct list_head rc_list;
--	u32 airtime_ac[8];
- 
- 	int ack_signal;
- 	struct ewma_avg_signal avg_ack_signal;
-@@ -404,6 +403,21 @@ struct mt7996_air_monitor_ctrl {
- };
- #endif
- 
-+struct mt7996_rro_ba_session {
-+	u32 ack_sn         :12;
-+	u32 win_sz         :3;
-+	u32 bn             :1;
-+	u32 last_in_sn     :12;
-+	u32 bc             :1;
-+	u32 bd             :1;
-+	u32 sat            :1;
-+	u32 cn             :1;
-+	u32 within_cnt     :12;
-+	u32 to_sel         :3;
-+	u32 rsv            :1;
-+	u32 last_in_rxtime :12;
-+};
-+
- struct mt7996_phy {
- 	struct mt76_phy *mt76;
- 	struct mt7996_dev *dev;
-@@ -599,6 +613,7 @@ struct mt7996_dev {
- 		u32 fw_dbg_module;
- 		u8 fw_dbg_lv;
- 		u32 bcn_total_cnt[__MT_MAX_BAND];
-+		u32 sid;
- 	} dbg;
- 	const struct mt7996_dbg_reg_desc *dbg_reg;
- #endif
-@@ -824,7 +839,10 @@ int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level);
- int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
- void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
- void mt7996_mcu_exit(struct mt7996_dev *dev);
--int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
-+int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
-+	                        u16 sta_num, u16 *sta_list);
-+int mt7996_mcu_get_rssi(struct mt76_dev *dev);
-+int mt7996_mcu_get_all_sta_info(struct mt76_dev *dev, u16 tag);
- int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
- int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
- int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index 70ff761..dab5b23 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -3106,6 +3106,69 @@ mt7996_vow_drr_dbg(void *data, u64 val)
- DEFINE_DEBUGFS_ATTRIBUTE(fops_vow_drr_dbg, NULL,
- 			 mt7996_vow_drr_dbg, "%lld\n");
- 
-+static int
-+mt7996_rro_session_read(struct seq_file *s, void *data)
-+{
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	struct mt7996_rro_ba_session *tbl;
-+	u32 value[2];
-+
-+	mt76_wr(dev, MT_RRO_DBG_RD_CTRL, MT_RRO_DBG_RD_EXEC +
-+		(dev->dbg.sid >> 1) + 0x200);
-+
-+	if (dev->dbg.sid & 0x1) {
-+		value[0] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(2));
-+		value[1] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(3));
-+	} else {
-+		value[0] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(0));
-+		value[1] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(1));
-+	}
-+
-+	tbl = (struct mt7996_rro_ba_session *)&value[0];
-+
-+	seq_printf(s, " seid %d:\nba session table DW0:%08x DW2:%08x\n",
-+		   dev->dbg.sid, value[0], value[1]);
-+
-+	seq_printf(s, "ack_sn = 0x%x, last_in_sn = 0x%x, sat/bn/bc/bd/cn = %d/%d/%d/%d/%d\n",
-+		   tbl->ack_sn, tbl->last_in_sn, tbl->sat, tbl->bn, tbl->bc, tbl->bd, tbl->cn);
-+
-+	seq_printf(s, "within_cnt = %d, to_sel = %d, last_in_rxtime = %d\n",
-+		   tbl->within_cnt, tbl->to_sel, tbl->last_in_rxtime);
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_show_rro_mib(struct seq_file *s, void *data)
-+{
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	u32 reg[12];
-+
-+	seq_printf(s, "RRO mib Info:\n");
-+
-+	reg[0] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(0));
-+	reg[1] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(1));
-+	reg[2] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(2));
-+	reg[3] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(3));
-+	reg[4] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(4));
-+	reg[5] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(5));
-+	reg[6] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(6));
-+	reg[7] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(7));
-+	reg[8] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(8));
-+	reg[9] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(9));
-+	reg[10] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(10));
-+	reg[11] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(11));
-+
-+	seq_printf(s, "STEP_ONE/WITHIN/SURPASS = %x/%x/%x\n", reg[0], reg[3], reg[4]);
-+	seq_printf(s, "REPEAT/OLDPKT/BAR = %x/%x/%x\n", reg[1], reg[2], reg[5]);
-+	seq_printf(s, "SURPASS with big gap = %x\n", reg[6]);
-+	seq_printf(s, "DISCONNECT/INVALID = %x/%x\n", reg[7], reg[8]);
-+	seq_printf(s, "TO(Step one)/TO(flush all) = %x/%x\n", reg[9], reg[10]);
-+	seq_printf(s, "buf ran out = %x\n", reg[11]);
-+
-+	return 0;
-+}
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- 	struct mt7996_dev *dev = phy->dev;
-@@ -3205,6 +3268,14 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- 
- 	debugfs_create_file("muru_prot_thr", 0200, dir, phy, &fops_muru_prot_thr);
- 
-+	if (dev->has_rro) {
-+		debugfs_create_u32("rro_sid", 0600, dir, &dev->dbg.sid);
-+		debugfs_create_devm_seqfile(dev->mt76.dev, "rro_sid_info", dir,
-+					    mt7996_rro_session_read);
-+		debugfs_create_devm_seqfile(dev->mt76.dev, "rro_mib", dir,
-+					    mt7996_show_rro_mib);
-+	}
-+
- 	return 0;
- }
- 
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index cbd7170..a001d9f 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -122,6 +122,8 @@ enum offs_rev {
- #define MT_MCU_INT_EVENT_DMA_INIT		BIT(1)
- #define MT_MCU_INT_EVENT_RESET_DONE		BIT(3)
- 
-+#define WF_RRO_TOP_STATISTIC(_n)		MT_RRO_TOP(0x180 + _n * 0x4)
-+
- /* PLE */
- #define MT_PLE_BASE				0x820c0000
- #define MT_PLE(ofs)				(MT_PLE_BASE + (ofs))
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0076-mtk-mt76-add-2pcie-one-wed-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0076-mtk-mt76-add-2pcie-one-wed-support.patch
new file mode 100644
index 0000000..0930043
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0076-mtk-mt76-add-2pcie-one-wed-support.patch
@@ -0,0 +1,176 @@
+From 52d7db874a75d9efdbec2f42bdff2ce917e6dd01 Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Wed, 13 Sep 2023 17:35:43 +0800
+Subject: [PATCH 076/199] mtk: mt76: add 2pcie one wed support
+
+Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
+---
+ mt7996/dma.c         | 13 +++++++++++--
+ mt7996/mmio.c        |  7 +++----
+ mt7996/mtk_debug.h   |  5 +++++
+ mt7996/mtk_debugfs.c | 25 ++++++++++++++++++-------
+ mt7996/regs.h        |  2 ++
+ 5 files changed, 39 insertions(+), 13 deletions(-)
+
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index d62dc8ba..c23b0d65 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -355,6 +355,13 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ 			 MT_WFDMA_HOST_CONFIG_PDMA_BAND |
+ 			 MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
+ 
++		if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
++		    is_mt7992(&dev->mt76)) {
++			mt76_set(dev, MT_WFDMA_HOST_CONFIG,
++				 MT_WFDMA_HOST_CONFIG_PDMA_BAND |
++				 MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
++		}
++
+ 		/* AXI read outstanding number */
+ 		mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL,
+ 			 MT_WFDMA_AXI_R2A_CTRL_OUTSTAND_MASK, 0x14);
+@@ -374,7 +381,8 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ 		    dev->has_rro) {
+ 			u32 intr = is_mt7996(&dev->mt76) ?
+ 				   MT_WFDMA0_RX_INT_SEL_RING6 :
+-				   MT_WFDMA0_RX_INT_SEL_RING9;
++				   MT_WFDMA0_RX_INT_SEL_RING9 |
++				   MT_WFDMA0_RX_INT_SEL_RING5;
+ 			mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL + hif1_ofs,
+ 				 intr);
+ 		} else {
+@@ -630,10 +638,11 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 					       MT_RXQ_ID(MT_RXQ_RRO_BAND1),
+ 					       MT7996_RX_RING_SIZE,
+ 					       MT7996_RX_BUF_SIZE,
+-					       MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND1));
++					       MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND1) + hif1_ofs);
+ 			if (ret)
+ 				return ret;
+ 		} else {
++			/* tx free notify event from WA for band0 */
+ 			dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
+ 			dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed;
+ 
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index bfe92ceb..4baae0e9 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -375,10 +375,10 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ 						    MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND2) +
+ 						    MT7996_RXQ_RRO_BAND2 * MT_RING_SIZE;
+ 		} else {
+-			wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base +
++			wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base + hif1_ofs +
+ 						    MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND1) +
+ 						    MT7996_RXQ_RRO_BAND1 * MT_RING_SIZE;
+-			wed->wlan.wpdma_rx[1] = wed->wlan.phy_base +
++			wed->wlan.wpdma_rx[1] = wed->wlan.phy_base + hif1_ofs +
+ 						MT_RXQ_RING_BASE(MT7996_RXQ_BAND1) +
+ 						MT7996_RXQ_BAND1 * MT_RING_SIZE;
+ 		}
+@@ -516,10 +516,9 @@ void mt7996_dual_hif_set_irq_mask(struct mt7996_dev *dev, bool write_reg,
+ 		if (mtk_wed_device_active(&mdev->mmio.wed)) {
+ 			mtk_wed_device_irq_set_mask(&mdev->mmio.wed,
+ 						    mdev->mmio.irqmask);
+-			if (mtk_wed_device_active(&mdev->mmio.wed_hif2)) {
++			if (mtk_wed_device_active(&mdev->mmio.wed_hif2))
+ 				mtk_wed_device_irq_set_mask(&mdev->mmio.wed_hif2,
+ 							    mdev->mmio.irqmask);
+-			}
+ 		} else {
+ 			mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask);
+ 			mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask);
+diff --git a/mt7996/mtk_debug.h b/mt7996/mtk_debug.h
+index 27d8f1cb..da2a6072 100644
+--- a/mt7996/mtk_debug.h
++++ b/mt7996/mtk_debug.h
+@@ -561,6 +561,11 @@ struct queue_desc {
+ #define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL1_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x574) // 8574
+ #define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL2_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x578) // 8578
+ #define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL3_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x57c) // 857C
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL0_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x590) // 8590
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL1_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x594) // 8594
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL2_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x598) // 8598
++#define WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL3_ADDR     (WF_WFDMA_HOST_DMA0_PCIE1_BASE + 0x59c) // 859C
++
+ //MCU DMA
+ //#define WF_WFDMA_MCU_DMA0_BASE                                 0x02000
+ #define WF_WFDMA_MCU_DMA0_BASE                                 0x54000000
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 1f754796..06c0db3f 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -558,14 +558,22 @@ mt7996_show_dma_info(struct seq_file *s, struct mt7996_dev *dev)
+ 		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL0_ADDR);
+ 	dump_dma_rx_ring_info(s, dev, "R5:Data1(MAC2H)", "Both",
+ 		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL0_ADDR);
+-	dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
+-		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
++	if (is_mt7996(&dev->mt76))
++		dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
++			WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
++	else
++		dump_dma_rx_ring_info(s, dev, "R6:TxDone0(MAC2H)", "Both",
++			WF_WFDMA_HOST_DMA0_WPDMA_RX_RING6_CTRL0_ADDR);
+ 	dump_dma_rx_ring_info(s, dev, "R7:TxDone1(MAC2H)", "Both",
+ 		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING7_CTRL0_ADDR);
+ 	dump_dma_rx_ring_info(s, dev, "R8:BUF0(MAC2H)", "Both",
+ 		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL0_ADDR);
+-	dump_dma_rx_ring_info(s, dev, "R9:TxDone0(MAC2H)", "Both",
+-		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
++	if (is_mt7996(&dev->mt76))
++		dump_dma_rx_ring_info(s, dev, "R9:TxDone0(MAC2H)", "Both",
++			WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
++	else
++		dump_dma_rx_ring_info(s, dev, "R9:BUF0(MAC2H)", "Both",
++			WF_WFDMA_HOST_DMA0_WPDMA_RX_RING9_CTRL0_ADDR);
+ 	dump_dma_rx_ring_info(s, dev, "R10:MSDU_PG0(MAC2H)", "Both",
+ 		WF_WFDMA_HOST_DMA0_WPDMA_RX_RING10_CTRL0_ADDR);
+ 	dump_dma_rx_ring_info(s, dev, "R11:MSDU_PG1(MAC2H)", "Both",
+@@ -583,15 +591,18 @@ mt7996_show_dma_info(struct seq_file *s, struct mt7996_dev *dev)
+ 			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING21_CTRL0_ADDR);
+ 		dump_dma_tx_ring_info(s, dev, "T22:TXD?(H2WA)", "AP",
+ 			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_TX_RING22_CTRL0_ADDR);
+-
+ 		dump_dma_rx_ring_info(s, dev, "R3:TxDone1(WA2H)", "AP",
+ 			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING3_CTRL0_ADDR);
+ 		dump_dma_rx_ring_info(s, dev, "R5:Data1(MAC2H)", "Both",
+ 			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING5_CTRL0_ADDR);
+-		dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
+-			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL0_ADDR);
++		if (is_mt7996(&dev->mt76))
++			dump_dma_rx_ring_info(s, dev, "R6:BUF1(MAC2H)", "Both",
++				WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING6_CTRL0_ADDR);
+ 		dump_dma_rx_ring_info(s, dev, "R7:TxDone1(MAC2H)", "Both",
+ 			WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING7_CTRL0_ADDR);
++		if (is_mt7992(&dev->mt76))
++			dump_dma_rx_ring_info(s, dev, "R9:BUF1(MAC2H)", "Both",
++				WF_WFDMA_HOST_DMA0_PCIE1_WPDMA_RX_RING9_CTRL0_ADDR);
+ 	}
+ 
+ 	/* MCU DMA information */
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index e6427a35..cbd71706 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -411,6 +411,7 @@ enum offs_rev {
+ 
+ #define MT_WFDMA0_RX_INT_PCIE_SEL		MT_WFDMA0(0x154)
+ #define MT_WFDMA0_RX_INT_SEL_RING3		BIT(3)
++#define MT_WFDMA0_RX_INT_SEL_RING5		BIT(5)
+ #define MT_WFDMA0_RX_INT_SEL_RING6		BIT(6)
+ #define MT_WFDMA0_RX_INT_SEL_RING9		BIT(9)
+ 
+@@ -451,6 +452,7 @@ enum offs_rev {
+ 
+ #define MT_WFDMA_HOST_CONFIG			MT_WFDMA_EXT_CSR(0x30)
+ #define MT_WFDMA_HOST_CONFIG_PDMA_BAND		BIT(0)
++#define MT_WFDMA_HOST_CONFIG_BAND1_PCIE1	BIT(21)
+ #define MT_WFDMA_HOST_CONFIG_BAND2_PCIE1	BIT(22)
+ 
+ #define MT_WFDMA_EXT_CSR_HIF_MISC		MT_WFDMA_EXT_CSR(0x44)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0076-mtk-wifi-mt76-mt7996-add-support-for-WMM-PBC-configu.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0076-mtk-wifi-mt76-mt7996-add-support-for-WMM-PBC-configu.patch
deleted file mode 100644
index c3b13e6..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0076-mtk-wifi-mt76-mt7996-add-support-for-WMM-PBC-configu.patch
+++ /dev/null
@@ -1,218 +0,0 @@
-From b524ec9e1239ff6edd8ffadf8ce9fe03c2621f64 Mon Sep 17 00:00:00 2001
-From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
-Date: Thu, 4 Jan 2024 09:47:00 +0800
-Subject: [PATCH 076/116] mtk: wifi: mt76: mt7996: add support for WMM PBC
- configuration
-
-Query per-AC-queue packet statistics from WA, and determine if multi-AC transmission is ongoing.
-If it is, enable WMM mode in WA. Otherwise, disable WMM mode.
-
-Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
----
- mt76_connac_mcu.h |  2 ++
- mt7996/init.c     |  2 ++
- mt7996/mac.c      |  4 +++
- mt7996/mcu.c      | 78 +++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/mcu.h      | 15 +++++++++
- mt7996/mt7996.h   |  4 +++
- 6 files changed, 105 insertions(+)
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index f389470..92f0bd7 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1027,6 +1027,7 @@ enum {
- 	MCU_EXT_EVENT_ASSERT_DUMP = 0x23,
- 	MCU_EXT_EVENT_RDD_REPORT = 0x3a,
- 	MCU_EXT_EVENT_CSA_NOTIFY = 0x4f,
-+	MCU_EXT_EVENT_BSS_ACQ_PKT_CNT = 0x52,
- 	MCU_EXT_EVENT_WA_TX_STAT = 0x74,
- 	MCU_EXT_EVENT_BCC_NOTIFY = 0x75,
- 	MCU_EXT_EVENT_MURU_CTRL = 0x9f,
-@@ -1226,6 +1227,7 @@ enum {
- 	MCU_EXT_CMD_TXDPD_CAL = 0x60,
- 	MCU_EXT_CMD_CAL_CACHE = 0x67,
- 	MCU_EXT_CMD_RED_ENABLE = 0x68,
-+	MCU_EXT_CMD_PKT_BUDGET_CTRL = 0x6c,
- 	MCU_EXT_CMD_CP_SUPPORT = 0x75,
- 	MCU_EXT_CMD_SET_RADAR_TH = 0x7c,
- 	MCU_EXT_CMD_SET_RDD_PATTERN = 0x7d,
-diff --git a/mt7996/init.c b/mt7996/init.c
-index e29cebe..f411146 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -1536,6 +1536,8 @@ int mt7996_register_device(struct mt7996_dev *dev)
- 	INIT_WORK(&dev->dump_work, mt7996_mac_dump_work);
- 	mutex_init(&dev->dump_mutex);
- 
-+	INIT_WORK(&dev->wmm_pbc_work, mt7996_mcu_wmm_pbc_work);
-+
- 	ret = mt7996_init_hardware(dev);
- 	if (ret)
- 		return ret;
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 782594c..e3758ff 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -2298,6 +2298,10 @@ void mt7996_mac_work(struct work_struct *work)
- 					mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_ADM_STAT);
- 					mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_MSDU_COUNT);
- 				}
-+
-+				if (mt7996_mcu_wa_cmd(phy->dev, MCU_WA_PARAM_CMD(QUERY), MCU_WA_PARAM_BSS_ACQ_PKT_CNT,
-+				                      BSS_ACQ_PKT_CNT_BSS_BITMAP_ALL | BSS_ACQ_PKT_CNT_READ_CLR, 0))
-+					dev_err(mdev->dev, "Failed to query per-AC-queue packet counts.\n");
- 			} else if (mt7996_band_valid(phy->dev, i) &&
- 			           test_bit(MT76_STATE_RUNNING, &mdev->phys[i]->state))
- 				break;
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index b02303b..82ed9e8 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -669,6 +669,82 @@ mt7996_mcu_rx_thermal_notify(struct mt7996_dev *dev, struct sk_buff *skb)
- 	phy->throttle_state = n->duty_percent;
- }
- 
-+void mt7996_mcu_wmm_pbc_work(struct work_struct *work)
-+{
-+#define WMM_PBC_QUEUE_NUM	5
-+#define WMM_PBC_BSS_ALL		0xff
-+#define WMM_PBC_WLAN_IDX_ALL	0xffff
-+#define WMM_PBC_BOUND_DEFAULT	0xffff
-+#define WMM_PBC_LOW_BOUND_VO	1900
-+#define WMM_PBC_LOW_BOUND_VI	1900
-+#define WMM_PBC_LOW_BOUND_BE	1500
-+#define WMM_PBC_LOW_BOUND_BK	900
-+#define WMM_PBC_LOW_BOUND_MGMT	32
-+	struct mt7996_dev *dev = container_of(work, struct mt7996_dev, wmm_pbc_work);
-+	struct {
-+		u8 bss_idx;
-+		u8 queue_num;
-+		__le16 wlan_idx;
-+		u8 band_idx;
-+		u8 __rsv[3];
-+		struct {
-+			__le16 low;
-+			__le16 up;
-+		} __packed bound[WMM_PBC_QUEUE_NUM];
-+	} __packed req = {
-+		.bss_idx = WMM_PBC_BSS_ALL,
-+		.queue_num = WMM_PBC_QUEUE_NUM,
-+		.wlan_idx = cpu_to_le16(WMM_PBC_WLAN_IDX_ALL),
-+		.band_idx = dev->mphy.band_idx,
-+	};
-+	int i, ret;
-+
-+#define pbc_acq_low_bound_config(_ac, _bound)								\
-+	req.bound[mt76_connac_lmac_mapping(_ac)].low = dev->wmm_pbc_enable ? cpu_to_le16(_bound) : 0
-+	pbc_acq_low_bound_config(IEEE80211_AC_VO, WMM_PBC_LOW_BOUND_VO);
-+	pbc_acq_low_bound_config(IEEE80211_AC_VI, WMM_PBC_LOW_BOUND_VI);
-+	pbc_acq_low_bound_config(IEEE80211_AC_BE, WMM_PBC_LOW_BOUND_BE);
-+	pbc_acq_low_bound_config(IEEE80211_AC_BK, WMM_PBC_LOW_BOUND_BK);
-+	req.bound[4].low = dev->wmm_pbc_enable
-+	                   ? cpu_to_le16(WMM_PBC_LOW_BOUND_MGMT) : 0;
-+
-+	for (i = 0; i < WMM_PBC_QUEUE_NUM; ++i)
-+		req.bound[i].up = cpu_to_le16(WMM_PBC_BOUND_DEFAULT);
-+
-+	ret = mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(PKT_BUDGET_CTRL),
-+	                        &req, sizeof(req), true);
-+	if (ret)
-+		dev_err(dev->mt76.dev, "Failed to configure WMM PBC.\n");
-+}
-+
-+static void
-+mt7996_mcu_rx_bss_acq_pkt_cnt(struct mt7996_dev *dev, struct sk_buff *skb)
-+{
-+	struct mt7996_mcu_bss_acq_pkt_cnt_event *event = (struct mt7996_mcu_bss_acq_pkt_cnt_event *)skb->data;
-+	u32 bitmap = le32_to_cpu(event->bss_bitmap);
-+	u64 sum[IEEE80211_NUM_ACS] = {0};
-+	u8 ac_cnt = 0;
-+	int i, j;
-+
-+	for (i = 0; (i < BSS_ACQ_PKT_CNT_BSS_NUM) && (bitmap & (1 << i)); ++i) {
-+		for (j = IEEE80211_AC_VO; j < IEEE80211_NUM_ACS; ++j)
-+			sum[j] += le32_to_cpu(event->bss[i].cnt[mt76_connac_lmac_mapping(j)]);
-+	}
-+
-+	for (i = IEEE80211_AC_VO; i < IEEE80211_NUM_ACS; ++i) {
-+		if (sum[i] > WMM_PKT_THRESHOLD)
-+			++ac_cnt;
-+	}
-+
-+	if (ac_cnt > 1 && !dev->wmm_pbc_enable) {
-+		dev->wmm_pbc_enable = true;
-+		queue_work(dev->mt76.wq, &dev->wmm_pbc_work);
-+	} else if (ac_cnt <= 1 && dev->wmm_pbc_enable) {
-+		dev->wmm_pbc_enable = false;
-+		queue_work(dev->mt76.wq, &dev->wmm_pbc_work);
-+	}
-+}
-+
- static void
- mt7996_mcu_rx_ext_event(struct mt7996_dev *dev, struct sk_buff *skb)
- {
-@@ -678,6 +754,8 @@ mt7996_mcu_rx_ext_event(struct mt7996_dev *dev, struct sk_buff *skb)
- 	case MCU_EXT_EVENT_FW_LOG_2_HOST:
- 		mt7996_mcu_rx_log_message(dev, skb);
- 		break;
-+	case MCU_EXT_EVENT_BSS_ACQ_PKT_CNT:
-+		mt7996_mcu_rx_bss_acq_pkt_cnt(dev, skb);
- 	default:
- 		break;
- 	}
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index e64812c..d24874a 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -375,10 +375,25 @@ enum {
- 	MCU_WA_PARAM_CMD_DEBUG,
- };
- 
-+#define BSS_ACQ_PKT_CNT_BSS_NUM		24
-+#define BSS_ACQ_PKT_CNT_BSS_BITMAP_ALL	0x00ffffff
-+#define BSS_ACQ_PKT_CNT_READ_CLR	BIT(31)
-+#define WMM_PKT_THRESHOLD		100
-+
-+struct mt7996_mcu_bss_acq_pkt_cnt_event {
-+	struct mt7996_mcu_rxd rxd;
-+
-+	__le32 bss_bitmap;
-+	struct {
-+		__le32 cnt[IEEE80211_NUM_ACS];
-+	} __packed bss[BSS_ACQ_PKT_CNT_BSS_NUM];
-+} __packed;
-+
- enum {
- 	MCU_WA_PARAM_PDMA_RX = 0x04,
- 	MCU_WA_PARAM_CPU_UTIL = 0x0b,
- 	MCU_WA_PARAM_RED_EN = 0x0e,
-+	MCU_WA_PARAM_BSS_ACQ_PKT_CNT = 0x12,
- 	MCU_WA_PARAM_HW_PATH_HIF_VER = 0x2f,
- 	MCU_WA_PARAM_RED_CONFIG = 0x40,
- };
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index a0cc8f3..1996539 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -606,6 +606,9 @@ struct mt7996_dev {
- 	u8 wtbl_size_group;
- 
- 	struct mt7996_vow_ctrl vow;
-+
-+	bool wmm_pbc_enable;
-+	struct work_struct wmm_pbc_work;
- #ifdef CONFIG_MTK_DEBUG
- 	u16 wlan_idx;
- 	struct {
-@@ -857,6 +860,7 @@ int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable);
- int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
- 	                        enum vow_drr_ctrl_id id);
- int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy);
-+void mt7996_mcu_wmm_pbc_work(struct work_struct *work);
- 
- static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
- {
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0077-mtk-mt76-mt7996-Remove-wed-rro-ring-add-napi-at-init.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0077-mtk-mt76-mt7996-Remove-wed-rro-ring-add-napi-at-init.patch
new file mode 100644
index 0000000..afa7f1a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0077-mtk-mt76-mt7996-Remove-wed-rro-ring-add-napi-at-init.patch
@@ -0,0 +1,31 @@
+From 04d9010a357a8d1d98aefd5263fbf8d27bc19d06 Mon Sep 17 00:00:00 2001
+From: mtk27745 <rex.lu@mediatek.com>
+Date: Mon, 6 Nov 2023 10:16:34 +0800
+Subject: [PATCH 077/199] mtk: mt76: mt7996: Remove wed rro ring add napi at
+ init state
+
+without this patch. rro ring will add napi at initial state. once rro ring add napi, it will have chance to be used by host driver. if host driver accessed the ring data, it will cause some issue.
+
+Signed-off-by: mtk27745 <rex.lu@mediatek.com>
+---
+ dma.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/dma.c b/dma.c
+index e23b744b..38701c71 100644
+--- a/dma.c
++++ b/dma.c
+@@ -1017,6 +1017,10 @@ mt76_dma_init(struct mt76_dev *dev,
+ 	init_completion(&dev->mmio.wed_reset_complete);
+ 
+ 	mt76_for_each_q_rx(dev, i) {
++		if (mtk_wed_device_active(&dev->mmio.wed) &&
++		    mt76_queue_is_wed_rro(&dev->q_rx[i]))
++			continue;
++
+ 		netif_napi_add(&dev->napi_dev, &dev->napi[i], poll);
+ 		mt76_dma_rx_fill(dev, &dev->q_rx[i], false);
+ 		napi_enable(&dev->napi[i]);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0077-mtk-wifi-mt76-mt7996-eagle-support-extra-option_type.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0077-mtk-wifi-mt76-mt7996-eagle-support-extra-option_type.patch
deleted file mode 100644
index 658e5f7..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0077-mtk-wifi-mt76-mt7996-eagle-support-extra-option_type.patch
+++ /dev/null
@@ -1,326 +0,0 @@
-From 0749ce8ea6a89c47f1e5b1a85ad5dfcd8fc4dc02 Mon Sep 17 00:00:00 2001
-From: Rex Lu <rex.lu@mediatek.com>
-Date: Thu, 1 Feb 2024 10:32:42 +0800
-Subject: [PATCH 077/116] mtk: wifi: mt76: mt7996: eagle support extra
- option_type
-
-1. eagle + mt7988d option_type 2 support
-2. eagle single pcie support
-
-Signed-off-by: Rex Lu <rex.lu@mediatek.com>
-
-1. adjust pcie outstanding value by pcie speed. not no longer by option_type.
-
-Signed-off-by: Rex Lu <rex.lu@mediatek.com>
-(cherry picked from commit 31d64e0f571eb06ed67f4916bc12fbcfe1263c47)
----
- mt7996/dma.c    | 51 +++++++++++++++++++++++++++++++++----
- mt7996/init.c   | 67 ++++++++++++++++++++++++++++++++++++++-----------
- mt7996/main.c   | 15 +++++++++--
- mt7996/mt7996.h |  5 ++++
- mt7996/pci.c    |  2 +-
- mt7996/regs.h   |  5 ++++
- 6 files changed, 123 insertions(+), 22 deletions(-)
-
-diff --git a/mt7996/dma.c b/mt7996/dma.c
-index c23b0d6..3dc0e8a 100644
---- a/mt7996/dma.c
-+++ b/mt7996/dma.c
-@@ -12,12 +12,20 @@ int mt7996_init_tx_queues(struct mt7996_phy *phy, int idx, int n_desc,
- {
- 	struct mt7996_dev *dev = phy->dev;
- 	u32 flags = 0;
-+	int i;
-+
-+	if (phy->mt76->band_idx == MT_BAND1 && !dev->hif2 && is_mt7996(&dev->mt76)) {
-+		phy->mt76->q_tx[0] = phy->mt76->dev->phys[MT_BAND0]->q_tx[0];
-+		for (i = 1; i <= MT_TXQ_PSD; i++)
-+			phy->mt76->q_tx[i] = phy->mt76->q_tx[0];
-+		return 0;
-+	}
- 
- 	if (mtk_wed_device_active(wed)) {
- 		ring_base += MT_TXQ_ID(0) * MT_RING_SIZE;
- 		idx -= MT_TXQ_ID(0);
- 
--		if (phy->mt76->band_idx == MT_BAND2)
-+		if (wed == &dev->mt76.mmio.wed_hif2)
- 			flags = MT_WED_Q_TX(0);
- 		else
- 			flags = MT_WED_Q_TX(idx);
-@@ -102,8 +110,20 @@ static void mt7996_dma_config(struct mt7996_dev *dev)
- 	/* data tx queue */
- 	TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
- 	if (is_mt7996(&dev->mt76)) {
--		TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
--		TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND2, MT7996_TXQ_BAND2);
-+		if (dev->hif2) {
-+			if (dev->option_type == 2) {
-+				/*  bn1:ring21 bn2:ring19 */
-+				TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND2, MT7996_TXQ_BAND2);
-+				TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
-+			} else {
-+				/* default bn1:ring19 bn2:ring21 */
-+				TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
-+				TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND2, MT7996_TXQ_BAND2);
-+			}
-+		} else {
-+			/* single pcie bn0/1:ring18 bn2:ring19 */
-+			TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
-+		}
- 	} else {
- 		TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
- 	}
-@@ -352,8 +372,20 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
- 			 WF_WFDMA0_GLO_CFG_EXT1_TX_FCTRL_MODE);
- 
- 		mt76_set(dev, MT_WFDMA_HOST_CONFIG,
--			 MT_WFDMA_HOST_CONFIG_PDMA_BAND |
--			 MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
-+			 MT_WFDMA_HOST_CONFIG_PDMA_BAND);
-+
-+		mt76_clear(dev, MT_WFDMA_HOST_CONFIG,
-+			   MT_WFDMA_HOST_CONFIG_BAND0_PCIE1 |
-+			   MT_WFDMA_HOST_CONFIG_BAND1_PCIE1 |
-+			   MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
-+
-+		if (dev->option_type == 2)
-+			mt76_set(dev, MT_WFDMA_HOST_CONFIG,
-+				 MT_WFDMA_HOST_CONFIG_BAND0_PCIE1 |
-+				 MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
-+		else
-+			mt76_set(dev, MT_WFDMA_HOST_CONFIG,
-+				 MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
- 
- 		if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
- 		    is_mt7992(&dev->mt76)) {
-@@ -366,6 +398,15 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
- 		mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL,
- 			 MT_WFDMA_AXI_R2A_CTRL_OUTSTAND_MASK, 0x14);
- 
-+		if (dev->hif2->speed < PCIE_SPEED_8_0GT ||
-+		    (dev->hif2->speed == PCIE_SPEED_8_0GT && dev->hif2->width < 2)) {
-+			mt76_rmw(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs,
-+				 WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK,
-+				 FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, 0x3));
-+			mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL2,
-+				 MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK,
-+				 FIELD_PREP(MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK, 0x3));
-+		}
- 		/* WFDMA rx threshold */
- 		mt76_wr(dev, MT_WFDMA0_PAUSE_RX_Q_45_TH + hif1_ofs, 0xc000c);
- 		mt76_wr(dev, MT_WFDMA0_PAUSE_RX_Q_67_TH + hif1_ofs, 0x10008);
-diff --git a/mt7996/init.c b/mt7996/init.c
-index f411146..1e7cd52 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -511,7 +511,7 @@ static void mt7996_mac_init_basic_rates(struct mt7996_dev *dev)
- void mt7996_mac_init(struct mt7996_dev *dev)
- {
- #define HIF_TXD_V2_1	0x21
--	int i;
-+	int i, rx_path_type, rro_bypass, txfree_path;
- 
- 	mt76_clear(dev, MT_MDP_DCR2, MT_MDP_DCR2_RX_TRANS_SHORT);
- 
-@@ -525,22 +525,45 @@ void mt7996_mac_init(struct mt7996_dev *dev)
- 	}
- 
- 	/* rro module init */
--	if (is_mt7996(&dev->mt76))
--		mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, 2);
--	else
--		mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE,
--				   dev->hif2 ? 7 : 0);
-+	switch (dev->option_type) {
-+	case 2:
-+		/* eagle + 7988d */
-+		rx_path_type = 3;
-+		rro_bypass = dev->has_rro ? 1 : 3;
-+		txfree_path = dev->has_rro ? 0 : 1;
-+		break;
-+	case 3:
-+		/* eagle + Airoha */
-+		rx_path_type = 6;
-+		rro_bypass = dev->has_rro ? 1 : 3;
-+		txfree_path = dev->has_rro ? 0 : 1;
-+		break;
-+	case 4:
-+		/* Bollinger */
-+		rx_path_type = 2;
-+		rro_bypass = dev->has_rro ? 1 : 3;
-+		txfree_path = dev->has_rro ? 0 : 1;
-+		break;
-+	default:
-+		if (is_mt7996(&dev->mt76))
-+			rx_path_type = 2;
-+		else
-+			rx_path_type = 7;
-+
-+		rro_bypass = dev->has_rro ? 1 : 3;
-+		txfree_path = dev->has_rro ? 0 : 1;
-+		break;
-+	}
-+
-+	mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, dev->hif2 ? rx_path_type : 0);
-+	mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, rro_bypass);
-+	mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, txfree_path);
- 
- 	if (dev->has_rro) {
- 		u16 timeout;
- 
- 		timeout = mt76_rr(dev, MT_HW_REV) == MT_HW_REV1 ? 512 : 128;
- 		mt7996_mcu_set_rro(dev, UNI_RRO_SET_FLUSH_TIMEOUT, timeout);
--		mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 1);
--		mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 0);
--	} else {
--		mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 3);
--		mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 1);
- 	}
- 
- 	mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
-@@ -618,9 +641,22 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
- 	if (phy)
- 		return 0;
- 
--	if (is_mt7996(&dev->mt76) && band == MT_BAND2 && dev->hif2) {
--		hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
--		wed = &dev->mt76.mmio.wed_hif2;
-+	if (is_mt7996(&dev->mt76) && dev->hif2) {
-+		switch (dev->option_type) {
-+		case 2:
-+			/* eagle + 7988d */
-+			if (band == MT_BAND1) {
-+				hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
-+				wed = &dev->mt76.mmio.wed_hif2;
-+			}
-+			break;
-+		default:
-+			if (band == MT_BAND2) {
-+				hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
-+				wed = &dev->mt76.mmio.wed_hif2;
-+			}
-+			break;
-+		}
- 	}
- 
- 	mphy = mt76_alloc_phy(&dev->mt76, sizeof(*phy), &mt7996_ops, band);
-@@ -1059,6 +1095,9 @@ int mt7996_get_chip_sku(struct mt7996_dev *dev)
- static int mt7996_init_hardware(struct mt7996_dev *dev)
- {
- 	int ret, idx;
-+	struct device_node *np = dev->mt76.dev->of_node;
-+
-+	of_property_read_u32(np, "option_type", &dev->option_type);
- 
- 	mt76_wr(dev, MT_INT_SOURCE_CSR, ~0);
- 	if (is_mt7992(&dev->mt76)) {
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 801e480..1894588 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -1606,8 +1606,19 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
- 	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
- 
--	if (phy != &dev->phy && phy->mt76->band_idx == MT_BAND2)
--		wed = &dev->mt76.mmio.wed_hif2;
-+	if (phy != &dev->phy && dev->hif2) {
-+		switch (dev->option_type) {
-+		case 2:
-+			/* eagle + 7988d */
-+			if (phy->mt76->band_idx == MT_BAND1)
-+				wed = &dev->mt76.mmio.wed_hif2;
-+			break;
-+		default:
-+			if (phy->mt76->band_idx == MT_BAND2)
-+				wed = &dev->mt76.mmio.wed_hif2;
-+			break;
-+		}
-+	}
- 
- 	if (!mtk_wed_device_active(wed))
- 		return -ENODEV;
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 1996539..3f1f9b3 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -8,6 +8,7 @@
- 
- #include <linux/interrupt.h>
- #include <linux/ktime.h>
-+#include <linux/pci.h>
- #include "../mt76_connac.h"
- #include "regs.h"
- 
-@@ -349,6 +350,8 @@ struct mt7996_hif {
- 	struct device *dev;
- 	void __iomem *regs;
- 	int irq;
-+	enum pci_bus_speed speed;
-+	enum pcie_link_width width;
- };
- 
- struct mt7996_scs_ctrl {
-@@ -579,6 +582,8 @@ struct mt7996_dev {
- 	u8 eeprom_mode;
- 	u32 bg_nxt_freq;
- 
-+	u32 option_type;
-+
- 	bool ibf;
- 	u8 fw_debug_wm;
- 	u8 fw_debug_wa;
-diff --git a/mt7996/pci.c b/mt7996/pci.c
-index f0d3f19..24d69d4 100644
---- a/mt7996/pci.c
-+++ b/mt7996/pci.c
-@@ -5,7 +5,6 @@
- 
- #include <linux/kernel.h>
- #include <linux/module.h>
--#include <linux/pci.h>
- 
- #include "mt7996.h"
- #include "mac.h"
-@@ -93,6 +92,7 @@ static int mt7996_pci_hif2_probe(struct pci_dev *pdev)
- 	hif->dev = &pdev->dev;
- 	hif->regs = pcim_iomap_table(pdev)[0];
- 	hif->irq = pdev->irq;
-+	pcie_bandwidth_available(pdev, NULL, &hif->speed, &hif->width);
- 	spin_lock_bh(&hif_lock);
- 	list_add(&hif->list, &hif_list);
- 	spin_unlock_bh(&hif_lock);
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index a001d9f..a0e4b3e 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -435,6 +435,7 @@ enum offs_rev {
- #define WF_WFDMA0_GLO_CFG_EXT0			MT_WFDMA0(0x2b0)
- #define WF_WFDMA0_GLO_CFG_EXT0_RX_WB_RXD	BIT(18)
- #define WF_WFDMA0_GLO_CFG_EXT0_WED_MERGE_MODE	BIT(14)
-+#define WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK	GENMASK(27, 24)
- 
- #define WF_WFDMA0_GLO_CFG_EXT1			MT_WFDMA0(0x2b4)
- #define WF_WFDMA0_GLO_CFG_EXT1_CALC_MODE	BIT(31)
-@@ -454,6 +455,7 @@ enum offs_rev {
- 
- #define MT_WFDMA_HOST_CONFIG			MT_WFDMA_EXT_CSR(0x30)
- #define MT_WFDMA_HOST_CONFIG_PDMA_BAND		BIT(0)
-+#define MT_WFDMA_HOST_CONFIG_BAND0_PCIE1	BIT(20)
- #define MT_WFDMA_HOST_CONFIG_BAND1_PCIE1	BIT(21)
- #define MT_WFDMA_HOST_CONFIG_BAND2_PCIE1	BIT(22)
- 
-@@ -463,6 +465,9 @@ enum offs_rev {
- #define MT_WFDMA_AXI_R2A_CTRL			MT_WFDMA_EXT_CSR(0x500)
- #define MT_WFDMA_AXI_R2A_CTRL_OUTSTAND_MASK	GENMASK(4, 0)
- 
-+#define MT_WFDMA_AXI_R2A_CTRL2			MT_WFDMA_EXT_CSR(0x508)
-+#define MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK	GENMASK(31, 28)
-+
- #define MT_PCIE_RECOG_ID			0xd7090
- #define MT_PCIE_RECOG_ID_MASK			GENMASK(30, 0)
- #define MT_PCIE_RECOG_ID_SEM			BIT(31)
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0078-mtk-mt76-mt7996-Remove-wed_stop-during-L1-SER.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0078-mtk-mt76-mt7996-Remove-wed_stop-during-L1-SER.patch
new file mode 100644
index 0000000..987c14a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0078-mtk-mt76-mt7996-Remove-wed_stop-during-L1-SER.patch
@@ -0,0 +1,32 @@
+From a606926ce504162a64ebab655645fb39e2035f0e Mon Sep 17 00:00:00 2001
+From: Rex Lu <rex.lu@mediatek.com>
+Date: Wed, 29 Nov 2023 13:56:52 +0800
+Subject: [PATCH 078/199] mtk: mt76: mt7996: Remove wed_stop during L1 SER
+
+Align logan L1 SER flow. During L1 SER, didn't need to close wed interrupt.
+
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+---
+ mt7996/mac.c | 6 ------
+ 1 file changed, 6 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 1f3445bb..25f036bd 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1947,12 +1947,6 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 	dev_info(dev->mt76.dev,"\n%s L1 SER recovery start.",
+ 		 wiphy_name(dev->mt76.hw->wiphy));
+ 
+-	if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2))
+-		mtk_wed_device_stop(&dev->mt76.mmio.wed_hif2);
+-
+-	if (mtk_wed_device_active(&dev->mt76.mmio.wed))
+-		mtk_wed_device_stop(&dev->mt76.mmio.wed);
+-
+ 	ieee80211_stop_queues(mt76_hw(dev));
+ 	if (phy2)
+ 		ieee80211_stop_queues(phy2->mt76->hw);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0078-mtk-wifi-mt76-mt7996-support-enable-disable-thermal-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0078-mtk-wifi-mt76-mt7996-support-enable-disable-thermal-.patch
deleted file mode 100644
index 90ce5bf..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0078-mtk-wifi-mt76-mt7996-support-enable-disable-thermal-.patch
+++ /dev/null
@@ -1,115 +0,0 @@
-From 4ced99d2b25f8804f194317675f3a7cce0f493b2 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Thu, 27 Jul 2023 19:35:32 +0800
-Subject: [PATCH 078/116] mtk: wifi: mt76: mt7996: support enable/disable
- thermal protection mechanism
-
-This commit adds a new debugfs thermal_enable to enable/disable thermal
-protection mechanism. The purpose of this commit is for autotest to
-verify thermal protection mechanism.
-
-[Usage]
-Enable thermal protection: echo 1 > thermal_enable
-Disable thermal protection: echo 0 > thermal_enable
-
-Please note that if you re-enable thermal protection mechanism, all the
-configuration values will be retained from the exising configuration,
-rather than using the default values.
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- mt7996/main.c        |  1 +
- mt7996/mt7996.h      |  1 +
- mt7996/mtk_debugfs.c | 45 ++++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 47 insertions(+)
-
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 1894588..775d81e 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -91,6 +91,7 @@ int mt7996_run(struct ieee80211_hw *hw)
- #ifdef CONFIG_MTK_DEBUG
- 	phy->sr_enable = true;
- 	phy->enhanced_sr_enable = true;
-+	phy->thermal_protection_enable = true;
- 
- 	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
- 						dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 3f1f9b3..3069e90 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -488,6 +488,7 @@ struct mt7996_phy {
- #ifdef CONFIG_MTK_DEBUG
- 	bool sr_enable:1;
- 	bool enhanced_sr_enable:1;
-+	bool thermal_protection_enable:1;
- #endif
- };
- 
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index dab5b23..de21a95 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -3169,6 +3169,49 @@ mt7996_show_rro_mib(struct seq_file *s, void *data)
- 	return 0;
- }
- 
-+static int
-+mt7996_thermal_enable_get(void *data, u64 *enable)
-+{
-+	struct mt7996_phy *phy = data;
-+
-+	*enable = phy->thermal_protection_enable;
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_thermal_enable_set(void *data, u64 action)
-+{
-+	struct mt7996_phy *phy = data;
-+	int ret;
-+	u8 throttling;
-+
-+	if (action > 1)
-+		return -EINVAL;
-+
-+	if (!!action == phy->thermal_protection_enable)
-+		return 0;
-+
-+	ret = mt7996_mcu_set_thermal_protect(phy, !!action);
-+	if (ret)
-+		return ret;
-+
-+	if (!!!action)
-+		goto out;
-+
-+	throttling = MT7996_THERMAL_THROTTLE_MAX - phy->cdev_state;
-+	ret = mt7996_mcu_set_thermal_throttling(phy, throttling);
-+	if (ret)
-+		return ret;
-+
-+out:
-+	phy->thermal_protection_enable = !!action;
-+
-+	return 0;
-+}
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_thermal_enable, mt7996_thermal_enable_get,
-+			 mt7996_thermal_enable_set, "%lld\n");
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- 	struct mt7996_dev *dev = phy->dev;
-@@ -3276,6 +3319,8 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- 					    mt7996_show_rro_mib);
- 	}
- 
-+	debugfs_create_file("thermal_enable", 0600, dir, phy, &fops_thermal_enable);
-+
- 	return 0;
- }
- 
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0079-mtk-mt76-mt7996-Refactor-rro-del-ba-command-format.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0079-mtk-mt76-mt7996-Refactor-rro-del-ba-command-format.patch
new file mode 100644
index 0000000..74173c9
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0079-mtk-mt76-mt7996-Refactor-rro-del-ba-command-format.patch
@@ -0,0 +1,84 @@
+From 10e60d0f16d93912198c3903e5aa114e78fc527f Mon Sep 17 00:00:00 2001
+From: Rex Lu <rex.lu@mediatek.com>
+Date: Wed, 29 Nov 2023 15:51:04 +0800
+Subject: [PATCH 079/199] mtk: mt76: mt7996: Refactor rro del ba command format
+
+1. remove unused struct
+2. refactor upstream del ba command format
+
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+---
+ mt7996/mcu.h | 50 +++-----------------------------------------------
+ 1 file changed, 3 insertions(+), 47 deletions(-)
+
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index c55fb527..19572d85 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -273,7 +273,9 @@ struct mt7996_mcu_wed_rro_ba_delete_event {
+ 	__le16 len;
+ 
+ 	__le16 session_id;
+-	u8 __rsv2[2];
++	__le16 mld_id;
++	u8 tid;
++	u8 __rsv[3];
+ } __packed;
+ 
+ enum  {
+@@ -298,52 +300,6 @@ struct mt7996_mcu_thermal_notify {
+ 	u8 __rsv2[4];
+ } __packed;
+ 
+-struct mt7996_mcu_rro_event {
+-	struct mt7996_mcu_rxd rxd;
+-
+-	u8 __rsv1[4];
+-
+-	__le16 tag;
+-	__le16 len;
+-} __packed;
+-
+-struct mt7996_mcu_rro_ba {
+-	__le16 tag;
+-	__le16 len;
+-
+-	__le16 wlan_id;
+-	u8 tid;
+-	u8 __rsv1;
+-	__le32 status;
+-	__le16 session_id;
+-	u8 __rsv2[2];
+-} __packed;
+-
+-struct mt7996_mcu_rro_ba_del_chk_done {
+-	__le16 tag;
+-	__le16 len;
+-
+-	__le16 session_id;
+-	__le16 mld_id;
+-	u8 tid;
+-	u8 __rsv[3];
+-} __packed;
+-
+-enum  {
+-	UNI_RRO_BA_SESSION_STATUS = 0,
+-	UNI_RRO_BA_SESSION_TBL	= 1,
+-	UNI_RRO_BA_SESSION_DEL_CHK_DONE = 2,
+-	UNI_RRO_BA_SESSION_MAX_NUM
+-};
+-
+-struct mt7996_mcu_rro_del_ba {
+-	struct mt7996_mcu_rro_event event;
+-
+-	u8  wlan_idx;
+-	u8  tid;
+-	u8 __rsv2[2];
+-};
+-
+ enum mt7996_chan_mib_offs {
+ 	UNI_MIB_OBSS_AIRTIME = 26,
+ 	UNI_MIB_NON_WIFI_TIME = 27,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0079-mtk-wifi-mt76-mt7996-support-thermal-recal-debug-com.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0079-mtk-wifi-mt76-mt7996-support-thermal-recal-debug-com.patch
deleted file mode 100644
index bf9ce92..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0079-mtk-wifi-mt76-mt7996-support-thermal-recal-debug-com.patch
+++ /dev/null
@@ -1,116 +0,0 @@
-From 5a95bf563b00a4d60292580619c8bc041697b548 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Thu, 4 Jan 2024 19:53:37 +0800
-Subject: [PATCH 079/116] mtk: wifi: mt76: mt7996: support thermal recal debug
- command
-
-Add support thermal recal debug command.
-
-Usage:
-$ echo val > debugfs/thermal_recal
-
-The val can be the following values:
-0 = disable
-1 = enable
-2 = manual trigger
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- mt76_connac_mcu.h    |  1 +
- mt7996/mt7996.h      |  1 +
- mt7996/mtk_debugfs.c | 17 +++++++++++++++++
- mt7996/mtk_mcu.c     | 21 +++++++++++++++++++++
- 4 files changed, 40 insertions(+)
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 92f0bd7..c19f8e0 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1287,6 +1287,7 @@ enum {
- 	MCU_UNI_CMD_TESTMODE_TRX_PARAM = 0x42,
- 	MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
- 	MCU_UNI_CMD_PRECAL_RESULT = 0x47,
-+	MCU_UNI_CMD_THERMAL_CAL = 0x4c,
- 	MCU_UNI_CMD_RRO = 0x57,
- 	MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
- 	MCU_UNI_CMD_PER_STA_INFO = 0x6d,
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 3069e90..69bcf78 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -1032,6 +1032,7 @@ void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type);
- void mt7996_tm_update_channel(struct mt7996_phy *phy);
- 
- int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val);
-+int mt7996_mcu_thermal_debug(struct mt7996_dev *dev, u8 mode, u8 action);
- #endif
- 
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index de21a95..c787551 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -3212,6 +3212,22 @@ out:
- DEFINE_DEBUGFS_ATTRIBUTE(fops_thermal_enable, mt7996_thermal_enable_get,
- 			 mt7996_thermal_enable_set, "%lld\n");
- 
-+static int
-+mt7996_thermal_recal_set(void *data, u64 val)
-+{
-+#define THERMAL_DEBUG_OPERATION_MANUAL_TRIGGER 2
-+#define THERMAL_DEBUG_MODE_RECAL 1
-+	struct mt7996_dev *dev = data;
-+
-+	if (val > THERMAL_DEBUG_OPERATION_MANUAL_TRIGGER)
-+		return -EINVAL;
-+
-+	return mt7996_mcu_thermal_debug(dev, THERMAL_DEBUG_MODE_RECAL, val);
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_thermal_recal, NULL,
-+			 mt7996_thermal_recal_set, "%llu\n");
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- 	struct mt7996_dev *dev = phy->dev;
-@@ -3320,6 +3336,7 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- 	}
- 
- 	debugfs_create_file("thermal_enable", 0600, dir, phy, &fops_thermal_enable);
-+	debugfs_create_file("thermal_recal", 0200, dir, dev, &fops_thermal_recal);
- 
- 	return 0;
- }
-diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
-index aed32e9..aa44b47 100644
---- a/mt7996/mtk_mcu.c
-+++ b/mt7996/mtk_mcu.c
-@@ -1342,4 +1342,25 @@ int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val)
- 				 sizeof(req), true);
- }
- 
-+int mt7996_mcu_thermal_debug(struct mt7996_dev *dev, u8 mode, u8 action)
-+{
-+	struct {
-+		u8 __rsv1[4];
-+
-+		__le16 tag;
-+		__le16 len;
-+
-+		u8 mode;
-+		u8 action;
-+		u8 __rsv2[2];
-+	} __packed req = {
-+		.tag = cpu_to_le16(mode),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.mode = mode,
-+		.action = action,
-+	};
-+
-+	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(THERMAL_CAL), &req,
-+	                         sizeof(req), true);
-+}
- #endif
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0080-mtk-mt76-mt7996-get-airtime-and-RSSI-via-MCU-command.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0080-mtk-mt76-mt7996-get-airtime-and-RSSI-via-MCU-command.patch
new file mode 100644
index 0000000..e0171ea
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0080-mtk-mt76-mt7996-get-airtime-and-RSSI-via-MCU-command.patch
@@ -0,0 +1,752 @@
+From ef1bc404cc98a3ab556f1efa25debc81008e91ec Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Fri, 17 Nov 2023 18:08:06 +0800
+Subject: [PATCH 080/199] mtk: mt76: mt7996: get airtime and RSSI via MCU
+ commands
+
+Direct access to WTBL for airtime and RSSI may cause synchronization issue with FW.
+Moreover, frequent access to WTBL, whenever TX-Free-Done event is received, leads to heavy CPU overheads.
+Therefore, indirect access to WTBL, through FW, with lower frequence is performed.
+
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+---
+ mt76.h               |  18 +++++
+ mt76_connac_mcu.h    |  14 +++-
+ mt7996/debugfs.c     |  17 +++--
+ mt7996/mac.c         | 145 +++++++-------------------------------
+ mt7996/mcu.c         | 161 +++++++++++++++++++++++++++++++++++++++++--
+ mt7996/mcu.h         |  24 +++++++
+ mt7996/mt7996.h      |  26 +++++--
+ mt7996/mtk_debugfs.c |  71 +++++++++++++++++++
+ mt7996/regs.h        |   2 +
+ 9 files changed, 336 insertions(+), 142 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index e3c209ff..8f3541e2 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -332,11 +332,13 @@ struct mt76_sta_stats {
+ 	u32 tx_packets;		/* unit: MSDU */
+ 	u32 tx_retries;
+ 	u32 tx_failed;
++	u64 tx_airtime;
+ 	/* WED RX */
+ 	u64 rx_bytes;
+ 	u32 rx_packets;
+ 	u32 rx_errors;
+ 	u32 rx_drops;
++	u64 rx_airtime;
+ };
+ 
+ enum mt76_wcid_flags {
+@@ -1326,6 +1328,22 @@ static inline int mt76_decr(int val, int size)
+ 
+ u8 mt76_ac_to_hwq(u8 ac);
+ 
++static inline u8
++mt76_ac_to_tid(u8 ac)
++{
++	static const u8 ac_to_tid[] = {
++		[IEEE80211_AC_BE] = 0,
++		[IEEE80211_AC_BK] = 1,
++		[IEEE80211_AC_VI] = 4,
++		[IEEE80211_AC_VO] = 6
++	};
++
++	if (WARN_ON(ac >= IEEE80211_NUM_ACS))
++		return 0;
++
++	return ac_to_tid[ac];
++}
++
+ static inline struct ieee80211_txq *
+ mtxq_to_txq(struct mt76_txq *mtxq)
+ {
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 6be7e6a6..f4574aaa 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1382,11 +1382,23 @@ enum {
+ 	UNI_OFFLOAD_OFFLOAD_BMC_RPY_DETECT,
+ };
+ 
++enum UNI_PER_STA_INFO_TAG {
++	UNI_PER_STA_RSSI,
++	UNI_PER_STA_CONTENTION_RX_RATE,
++	UNI_PER_STA_PER,
++	UNI_PER_STA_SNR,
++	UNI_PER_STA_TX_RATE,
++	UNI_PER_STA_TX_CNT,
++	UNI_PER_STA_TID_SN_GET,
++	UNI_PER_STA_TID_SN_SET,
++	UNI_PER_STA_MAX_NUM
++};
++
+ enum UNI_ALL_STA_INFO_TAG {
+ 	UNI_ALL_STA_TXRX_RATE,
+ 	UNI_ALL_STA_TX_STAT,
+ 	UNI_ALL_STA_TXRX_ADM_STAT,
+-	UNI_ALL_STA_TXRX_AIR_TIME,
++	UNI_ALL_STA_TXRX_AIRTIME,
+ 	UNI_ALL_STA_DATA_TX_RETRY_COUNT,
+ 	UNI_ALL_STA_GI_MODE,
+ 	UNI_ALL_STA_TXRX_MSDU_COUNT,
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 3b58b8ba..8f8608fa 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -979,12 +979,11 @@ mt7996_airtime_read(struct seq_file *s, void *data)
+ {
+ 	struct mt7996_dev *dev = dev_get_drvdata(s->private);
+ 	struct mt76_dev *mdev = &dev->mt76;
+-	struct mt7996_vow_sta_ctrl *vow;
++	struct mt76_sta_stats *stats;
+ 	struct ieee80211_sta *sta;
+ 	struct mt7996_sta *msta;
+ 	struct mt76_wcid *wcid;
+ 	struct mt76_vif *vif;
+-	u64 airtime;
+ 	u16 i;
+ 
+ 	seq_printf(s, "VoW Airtime Information:\n");
+@@ -996,16 +995,16 @@ mt7996_airtime_read(struct seq_file *s, void *data)
+ 
+ 		msta = container_of(wcid, struct mt7996_sta, wcid);
+ 		sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
+-		vow = &msta->vow;
+ 		vif = &msta->vif->mt76;
++		stats = &wcid->stats;
+ 
+-		spin_lock_bh(&vow->lock);
+-		airtime = vow->tx_airtime;
+-		vow->tx_airtime = 0;
+-		spin_unlock_bh(&vow->lock);
++		seq_printf(s, "%pM WCID: %hu BandIdx: %hhu OmacIdx: 0x%hhx\t"
++		              "TxAirtime: %llu\tRxAirtime: %llu\n",
++		              sta->addr, i, vif->band_idx, vif->omac_idx,
++		              stats->tx_airtime, stats->rx_airtime);
+ 
+-		seq_printf(s, "%pM WCID: %hu BandIdx: %hhu OmacIdx: 0x%hhx\tTxAirtime: %llu\n",
+-		           sta->addr, i, vif->band_idx, vif->omac_idx, airtime);
++		stats->tx_airtime = 0;
++		stats->rx_airtime = 0;
+ 	}
+ 	rcu_read_unlock();
+ 
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 25f036bd..06a86146 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -12,8 +12,6 @@
+ #include "mcu.h"
+ #include "vendor.h"
+ 
+-#define to_rssi(field, rcpi)	((FIELD_GET(field, rcpi) - 220) / 2)
+-
+ static const struct mt7996_dfs_radar_spec etsi_radar_specs = {
+ 	.pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 },
+ 	.radar_pattern = {
+@@ -93,110 +91,6 @@ u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw)
+ 	return MT_WTBL_LMAC_OFFS(wcid, dw);
+ }
+ 
+-static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
+-{
+-	static const u8 ac_to_tid[] = {
+-		[IEEE80211_AC_BE] = 0,
+-		[IEEE80211_AC_BK] = 1,
+-		[IEEE80211_AC_VI] = 4,
+-		[IEEE80211_AC_VO] = 6
+-	};
+-	struct ieee80211_sta *sta;
+-	struct mt7996_sta *msta;
+-	struct mt7996_vow_sta_ctrl *vow;
+-	u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS];
+-	LIST_HEAD(sta_poll_list);
+-	int i;
+-
+-	spin_lock_bh(&dev->mt76.sta_poll_lock);
+-	list_splice_init(&dev->mt76.sta_poll_list, &sta_poll_list);
+-	spin_unlock_bh(&dev->mt76.sta_poll_lock);
+-
+-	rcu_read_lock();
+-
+-	while (true) {
+-		bool clear = false;
+-		u32 addr, val;
+-		u16 idx;
+-		s8 rssi[4];
+-
+-		spin_lock_bh(&dev->mt76.sta_poll_lock);
+-		if (list_empty(&sta_poll_list)) {
+-			spin_unlock_bh(&dev->mt76.sta_poll_lock);
+-			break;
+-		}
+-		msta = list_first_entry(&sta_poll_list,
+-					struct mt7996_sta, wcid.poll_list);
+-		list_del_init(&msta->wcid.poll_list);
+-		spin_unlock_bh(&dev->mt76.sta_poll_lock);
+-
+-		idx = msta->wcid.idx;
+-
+-		/* refresh peer's airtime reporting */
+-		addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 20);
+-
+-		for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+-			u32 tx_last = msta->airtime_ac[i];
+-			u32 rx_last = msta->airtime_ac[i + 4];
+-
+-			msta->airtime_ac[i] = mt76_rr(dev, addr);
+-			msta->airtime_ac[i + 4] = mt76_rr(dev, addr + 4);
+-
+-			tx_time[i] = msta->airtime_ac[i] - tx_last;
+-			rx_time[i] = msta->airtime_ac[i + 4] - rx_last;
+-
+-			if ((tx_last | rx_last) & BIT(30))
+-				clear = true;
+-
+-			addr += 8;
+-		}
+-
+-		if (clear) {
+-			mt7996_mac_wtbl_update(dev, idx,
+-					       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+-			memset(msta->airtime_ac, 0, sizeof(msta->airtime_ac));
+-		}
+-
+-		if (!msta->wcid.sta)
+-			continue;
+-
+-		sta = container_of((void *)msta, struct ieee80211_sta,
+-				   drv_priv);
+-		vow = &msta->vow;
+-		for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+-			u8 q = mt76_connac_lmac_mapping(i);
+-			u32 tx_cur = tx_time[q];
+-			u32 rx_cur = rx_time[q];
+-			u8 tid = ac_to_tid[i];
+-
+-			if (!tx_cur && !rx_cur)
+-				continue;
+-
+-			ieee80211_sta_register_airtime(sta, tid, tx_cur, rx_cur);
+-
+-			spin_lock_bh(&vow->lock);
+-			vow->tx_airtime += tx_cur;
+-			spin_unlock_bh(&vow->lock);
+-		}
+-
+-		/* get signal strength of resp frames (CTS/BA/ACK) */
+-		addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 34);
+-		val = mt76_rr(dev, addr);
+-
+-		rssi[0] = to_rssi(GENMASK(7, 0), val);
+-		rssi[1] = to_rssi(GENMASK(15, 8), val);
+-		rssi[2] = to_rssi(GENMASK(23, 16), val);
+-		rssi[3] = to_rssi(GENMASK(31, 14), val);
+-
+-		msta->ack_signal =
+-			mt76_rx_signal(msta->vif->phy->mt76->antenna_mask, rssi);
+-
+-		ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal);
+-	}
+-
+-	rcu_read_unlock();
+-}
+-
+ void mt7996_mac_enable_rtscts(struct mt7996_dev *dev,
+ 			      struct ieee80211_vif *vif, bool enable)
+ {
+@@ -1209,8 +1103,6 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ 		}
+ 	}
+ 
+-	mt7996_mac_sta_poll(dev);
+-
+ 	if (wake)
+ 		mt76_set_tx_blocked(&dev->mt76, false);
+ 
+@@ -2379,31 +2271,42 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
+ 
+ void mt7996_mac_work(struct work_struct *work)
+ {
+-	struct mt7996_phy *phy;
+-	struct mt76_phy *mphy;
+-
+-	mphy = (struct mt76_phy *)container_of(work, struct mt76_phy,
+-					       mac_work.work);
+-	phy = mphy->priv;
++	struct mt76_phy *mphy = (struct mt76_phy *)container_of(work, struct mt76_phy,
++	                                                        mac_work.work);
++	struct mt7996_phy *phy = mphy->priv;
++	struct mt76_dev *mdev = mphy->dev;
+ 
+-	mutex_lock(&mphy->dev->mutex);
++	mutex_lock(&mdev->mutex);
+ 
+ 	mt76_update_survey(mphy);
+ 	if (++mphy->mac_work_count == 5) {
++		int i;
++
+ 		mphy->mac_work_count = 0;
+ 
+ 		mt7996_mac_update_stats(phy);
+ 
+-		mt7996_mcu_get_all_sta_info(phy, UNI_ALL_STA_TXRX_RATE);
+-		if (mtk_wed_device_active(&phy->dev->mt76.mmio.wed)) {
+-			mt7996_mcu_get_all_sta_info(phy, UNI_ALL_STA_TXRX_ADM_STAT);
+-			mt7996_mcu_get_all_sta_info(phy, UNI_ALL_STA_TXRX_MSDU_COUNT);
++		/* Update DEV-wise information only in
++		 * the MAC work of the first band running.
++		 */
++		for (i = MT_BAND0; i <= mphy->band_idx; ++i) {
++			if (i == mphy->band_idx) {
++				mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_RATE);
++				mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_AIRTIME);
++				mt7996_mcu_get_rssi(mdev);
++				if (mtk_wed_device_active(&mdev->mmio.wed)) {
++					mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_ADM_STAT);
++					mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_MSDU_COUNT);
++				}
++			} else if (mt7996_band_valid(phy->dev, i) &&
++			           test_bit(MT76_STATE_RUNNING, &mdev->phys[i]->state))
++				break;
+ 		}
+ 	}
+ 
+-	mutex_unlock(&mphy->dev->mutex);
++	mutex_unlock(&mdev->mutex);
+ 
+-	mt76_tx_status_check(mphy->dev, false);
++	mt76_tx_status_check(mdev, false);
+ 
+ 	ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
+ 				     MT7996_WATCHDOG_TIME);
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 46611191..c7f9a56d 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -563,7 +563,8 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 		u16 wlan_idx;
+ 		struct mt76_wcid *wcid;
+ 		struct mt76_phy *mphy;
+-		u32 tx_bytes, rx_bytes, tx_packets, rx_packets;
++		struct ieee80211_sta *sta;
++		u32 tx_bytes, rx_bytes, tx_airtime, rx_airtime, tx_packets, rx_packets;
+ 
+ 		switch (le16_to_cpu(res->tag)) {
+ 		case UNI_ALL_STA_TXRX_RATE:
+@@ -584,7 +585,7 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 				break;
+ 
+ 			mphy = mt76_dev_phy(&dev->mt76, wcid->phy_idx);
+-			for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
++			for (ac = IEEE80211_AC_VO; ac < IEEE80211_NUM_ACS; ac++) {
+ 				tx_bytes = le32_to_cpu(res->adm_stat[i].tx_bytes[ac]);
+ 				rx_bytes = le32_to_cpu(res->adm_stat[i].rx_bytes[ac]);
+ 
+@@ -616,6 +617,24 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 			__mt7996_stat_to_netdev(mphy, wcid, 0, 0,
+ 						tx_packets, rx_packets);
+ 			break;
++		case UNI_ALL_STA_TXRX_AIRTIME:
++			wlan_idx = le16_to_cpu(res->airtime[i].wlan_idx);
++			wcid = rcu_dereference(dev->mt76.wcid[wlan_idx]);
++			sta = wcid_to_sta(wcid);
++			if (!sta)
++				continue;
++
++			for (ac = IEEE80211_AC_VO; ac < IEEE80211_NUM_ACS; ++ac) {
++				u8 lmac_ac = mt76_connac_lmac_mapping(ac);
++				tx_airtime = le32_to_cpu(res->airtime[i].tx[lmac_ac]);
++				rx_airtime = le32_to_cpu(res->airtime[i].rx[lmac_ac]);
++
++				wcid->stats.tx_airtime += tx_airtime;
++				wcid->stats.rx_airtime += rx_airtime;
++				ieee80211_sta_register_airtime(sta, mt76_ac_to_tid(ac),
++				                               tx_airtime, rx_airtime);
++			}
++			break;
+ 		default:
+ 			break;
+ 		}
+@@ -2244,8 +2263,6 @@ mt7996_mcu_sta_init_vow(struct mt7996_phy *phy, struct mt7996_sta *msta)
+ 	vow->drr_quantum[IEEE80211_AC_VI] = VOW_DRR_QUANTUM_IDX1;
+ 	vow->drr_quantum[IEEE80211_AC_BE] = VOW_DRR_QUANTUM_IDX2;
+ 	vow->drr_quantum[IEEE80211_AC_BK] = VOW_DRR_QUANTUM_IDX2;
+-	vow->tx_airtime = 0;
+-	spin_lock_init(&vow->lock);
+ 
+ 	ret = mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_BSS_GROUP);
+ 	if (ret)
+@@ -4842,9 +4859,139 @@ int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u16 val)
+ 				 sizeof(req), true);
+ }
+ 
+-int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag)
++int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
++	                        u16 sta_num, u16 *sta_list)
++{
++#define PER_STA_INFO_MAX_NUM	90
++	struct mt7996_mcu_per_sta_info_event *res;
++	struct mt76_wcid *wcid;
++	struct sk_buff *skb;
++	u16 wlan_idx;
++	int i, ret;
++	struct {
++		u8 __rsv1;
++		u8 unsolicit;
++		u8 __rsv2[2];
++
++		__le16 tag;
++		__le16 len;
++		__le16 sta_num;
++		u8 __rsv3[2];
++		__le16 sta_list[PER_STA_INFO_MAX_NUM];
++	} __packed req = {
++		.unsolicit = 0,
++		.tag = cpu_to_le16(tag),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.sta_num = cpu_to_le16(sta_num)
++	};
++
++	if (sta_num > PER_STA_INFO_MAX_NUM)
++		return -EINVAL;
++
++	for (i = 0; i < sta_num; ++i)
++		req.sta_list[i] = cpu_to_le16(sta_list[i]);
++
++	ret = mt76_mcu_send_and_get_msg(dev, MCU_WM_UNI_CMD(PER_STA_INFO),
++	                                &req, sizeof(req), true, &skb);
++	if (ret)
++		return ret;
++
++	res = (struct mt7996_mcu_per_sta_info_event *)skb->data;
++	if (le16_to_cpu(res->tag) != tag) {
++		ret = -EINVAL;
++		goto out;
++	}
++
++	rcu_read_lock();
++	switch (tag) {
++	case UNI_PER_STA_RSSI:
++		for (i = 0; i < sta_num; ++i) {
++			struct mt7996_sta *msta;
++			struct mt76_phy *phy;
++			s8 rssi[4];
++			u8 *rcpi;
++
++			wlan_idx = le16_to_cpu(res->rssi[i].wlan_idx);
++			wcid = rcu_dereference(dev->wcid[wlan_idx]);
++			if (wcid) {
++				rcpi = res->rssi[i].rcpi;
++				rssi[0] = to_rssi(MT_PRXV_RCPI0, rcpi[0]);
++				rssi[1] = to_rssi(MT_PRXV_RCPI0, rcpi[1]);
++				rssi[2] = to_rssi(MT_PRXV_RCPI0, rcpi[2]);
++				rssi[3] = to_rssi(MT_PRXV_RCPI0, rcpi[3]);
++
++				msta = container_of(wcid, struct mt7996_sta, wcid);
++				phy = msta->vif->phy->mt76;
++				msta->ack_signal = mt76_rx_signal(phy->antenna_mask, rssi);
++				ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal);
++			} else {
++				ret = -EINVAL;
++				dev_err(dev->dev, "Failed to update RSSI for "
++				                  "invalid WCID: %hu\n", wlan_idx);
++			}
++		}
++		break;
++	default:
++		ret = -EINVAL;
++		dev_err(dev->dev, "Unknown UNI_PER_STA_INFO_TAG: %d\n", tag);
++	}
++	rcu_read_unlock();
++out:
++	dev_kfree_skb(skb);
++	return ret;
++}
++
++int mt7996_mcu_get_rssi(struct mt76_dev *dev)
++{
++	u16 sta_list[PER_STA_INFO_MAX_NUM];
++	LIST_HEAD(sta_poll_list);
++	struct mt7996_sta *msta;
++	int i, ret;
++	bool empty = false;
++
++	spin_lock_bh(&dev->sta_poll_lock);
++	list_splice_init(&dev->sta_poll_list, &sta_poll_list);
++	spin_unlock_bh(&dev->sta_poll_lock);
++
++	while (!empty) {
++		for (i = 0; i < PER_STA_INFO_MAX_NUM; ++i) {
++			spin_lock_bh(&dev->sta_poll_lock);
++			if (list_empty(&sta_poll_list)) {
++				spin_unlock_bh(&dev->sta_poll_lock);
++
++				if (i == 0)
++					return 0;
++
++				empty = true;
++				break;
++			}
++			msta = list_first_entry(&sta_poll_list,
++			                        struct mt7996_sta,
++			                        wcid.poll_list);
++			list_del_init(&msta->wcid.poll_list);
++			spin_unlock_bh(&dev->sta_poll_lock);
++
++			sta_list[i] = msta->wcid.idx;
++		}
++
++		ret = mt7996_mcu_get_per_sta_info(dev, UNI_PER_STA_RSSI,
++		                                  i, sta_list);
++		if (ret) {
++			/* Add STAs, whose RSSI has not been updated,
++			 * back to polling list.
++			 */
++			spin_lock_bh(&dev->sta_poll_lock);
++			list_splice(&sta_poll_list, &dev->sta_poll_list);
++			spin_unlock_bh(&dev->sta_poll_lock);
++			break;
++		}
++	}
++
++	return ret;
++}
++
++int mt7996_mcu_get_all_sta_info(struct mt76_dev *dev, u16 tag)
+ {
+-	struct mt7996_dev *dev = phy->dev;
+ 	struct {
+ 		u8 _rsv[4];
+ 
+@@ -4855,7 +5002,7 @@ int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag)
+ 		.len = cpu_to_le16(sizeof(req) - 4),
+ 	};
+ 
+-	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(ALL_STA_INFO),
++	return mt76_mcu_send_msg(dev, MCU_WM_UNI_CMD(ALL_STA_INFO),
+ 				 &req, sizeof(req), false);
+ }
+ 
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 19572d85..5aa55e4f 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -199,6 +199,23 @@ struct mt7996_mcu_mib {
+ 	__le64 data;
+ } __packed;
+ 
++struct per_sta_rssi {
++	__le16 wlan_idx;
++	u8 __rsv[2];
++	u8 rcpi[4];
++} __packed;
++
++struct mt7996_mcu_per_sta_info_event {
++	u8 __rsv[4];
++
++	__le16 tag;
++	__le16 len;
++
++	union {
++		struct per_sta_rssi rssi[0];
++	};
++} __packed;
++
+ struct all_sta_trx_rate {
+ 	__le16 wlan_idx;
+ 	u8 __rsv1[2];
+@@ -244,6 +261,13 @@ struct mt7996_mcu_all_sta_info_event {
+ 			__le32 tx_msdu_cnt;
+ 			__le32 rx_msdu_cnt;
+ 		} __packed, msdu_cnt);
++
++		DECLARE_FLEX_ARRAY(struct {
++			__le16 wlan_idx;
++			u8 rsv[2];
++			__le32 tx[IEEE80211_NUM_ACS];
++			__le32 rx[IEEE80211_NUM_ACS];
++		} __packed, airtime);
+ 	} __packed;
+ } __packed;
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 834e8fc0..b0b61b12 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -126,6 +126,8 @@
+ #define MT7996_RRO_MSDU_PG_CR_CNT 8
+ #define MT7996_RRO_MSDU_PG_SIZE_PER_CR 0x10000
+ 
++#define to_rssi(field, rcpi)	((FIELD_GET(field, rcpi) - 220) / 2)
++
+ struct mt7996_vif;
+ struct mt7996_sta;
+ struct mt7996_dfs_pulse;
+@@ -298,8 +300,6 @@ struct mt7996_vow_sta_ctrl {
+ 	bool paused;
+ 	u8 bss_grp_idx;
+ 	u8 drr_quantum[IEEE80211_NUM_ACS];
+-	u64 tx_airtime;
+-	spinlock_t lock;
+ };
+ 
+ struct mt7996_sta {
+@@ -308,7 +308,6 @@ struct mt7996_sta {
+ 	struct mt7996_vif *vif;
+ 
+ 	struct list_head rc_list;
+-	u32 airtime_ac[8];
+ 
+ 	int ack_signal;
+ 	struct ewma_avg_signal avg_ack_signal;
+@@ -405,6 +404,21 @@ struct mt7996_air_monitor_ctrl {
+ };
+ #endif
+ 
++struct mt7996_rro_ba_session {
++	u32 ack_sn         :12;
++	u32 win_sz         :3;
++	u32 bn             :1;
++	u32 last_in_sn     :12;
++	u32 bc             :1;
++	u32 bd             :1;
++	u32 sat            :1;
++	u32 cn             :1;
++	u32 within_cnt     :12;
++	u32 to_sel         :3;
++	u32 rsv            :1;
++	u32 last_in_rxtime :12;
++};
++
+ struct mt7996_phy {
+ 	struct mt76_phy *mt76;
+ 	struct mt7996_dev *dev;
+@@ -600,6 +614,7 @@ struct mt7996_dev {
+ 		u32 fw_dbg_module;
+ 		u8 fw_dbg_lv;
+ 		u32 bcn_total_cnt[__MT_MAX_BAND];
++		u32 sid;
+ 	} dbg;
+ 	const struct mt7996_dbg_reg_desc *dbg_reg;
+ #endif
+@@ -825,7 +840,10 @@ int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level);
+ int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
+ void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ void mt7996_mcu_exit(struct mt7996_dev *dev);
+-int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
++int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
++	                        u16 sta_num, u16 *sta_list);
++int mt7996_mcu_get_rssi(struct mt76_dev *dev);
++int mt7996_mcu_get_all_sta_info(struct mt76_dev *dev, u16 tag);
+ int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
+ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
+ int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 06c0db3f..275beb48 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -3065,6 +3065,69 @@ mt7996_vow_drr_dbg(void *data, u64 val)
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_vow_drr_dbg, NULL,
+ 			 mt7996_vow_drr_dbg, "%lld\n");
+ 
++static int
++mt7996_rro_session_read(struct seq_file *s, void *data)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	struct mt7996_rro_ba_session *tbl;
++	u32 value[2];
++
++	mt76_wr(dev, MT_RRO_DBG_RD_CTRL, MT_RRO_DBG_RD_EXEC +
++		(dev->dbg.sid >> 1) + 0x200);
++
++	if (dev->dbg.sid & 0x1) {
++		value[0] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(2));
++		value[1] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(3));
++	} else {
++		value[0] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(0));
++		value[1] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(1));
++	}
++
++	tbl = (struct mt7996_rro_ba_session *)&value[0];
++
++	seq_printf(s, " seid %d:\nba session table DW0:%08x DW2:%08x\n",
++		   dev->dbg.sid, value[0], value[1]);
++
++	seq_printf(s, "ack_sn = 0x%x, last_in_sn = 0x%x, sat/bn/bc/bd/cn = %d/%d/%d/%d/%d\n",
++		   tbl->ack_sn, tbl->last_in_sn, tbl->sat, tbl->bn, tbl->bc, tbl->bd, tbl->cn);
++
++	seq_printf(s, "within_cnt = %d, to_sel = %d, last_in_rxtime = %d\n",
++		   tbl->within_cnt, tbl->to_sel, tbl->last_in_rxtime);
++
++	return 0;
++}
++
++static int
++mt7996_show_rro_mib(struct seq_file *s, void *data)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	u32 reg[12];
++
++	seq_printf(s, "RRO mib Info:\n");
++
++	reg[0] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(0));
++	reg[1] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(1));
++	reg[2] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(2));
++	reg[3] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(3));
++	reg[4] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(4));
++	reg[5] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(5));
++	reg[6] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(6));
++	reg[7] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(7));
++	reg[8] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(8));
++	reg[9] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(9));
++	reg[10] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(10));
++	reg[11] = mt76_rr(dev, WF_RRO_TOP_STATISTIC(11));
++
++	seq_printf(s, "STEP_ONE/WITHIN/SURPASS = %x/%x/%x\n", reg[0], reg[3], reg[4]);
++	seq_printf(s, "REPEAT/OLDPKT/BAR = %x/%x/%x\n", reg[1], reg[2], reg[5]);
++	seq_printf(s, "SURPASS with big gap = %x\n", reg[6]);
++	seq_printf(s, "DISCONNECT/INVALID = %x/%x\n", reg[7], reg[8]);
++	seq_printf(s, "TO(Step one)/TO(flush all) = %x/%x\n", reg[9], reg[10]);
++	seq_printf(s, "buf ran out = %x\n", reg[11]);
++
++	return 0;
++}
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+@@ -3163,6 +3226,14 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ 
+ 	debugfs_create_file("muru_prot_thr", 0200, dir, phy, &fops_muru_prot_thr);
+ 
++	if (dev->has_rro) {
++		debugfs_create_u32("rro_sid", 0600, dir, &dev->dbg.sid);
++		debugfs_create_devm_seqfile(dev->mt76.dev, "rro_sid_info", dir,
++					    mt7996_rro_session_read);
++		debugfs_create_devm_seqfile(dev->mt76.dev, "rro_mib", dir,
++					    mt7996_show_rro_mib);
++	}
++
+ 	return 0;
+ }
+ 
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index cbd71706..a001d9fd 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -122,6 +122,8 @@ enum offs_rev {
+ #define MT_MCU_INT_EVENT_DMA_INIT		BIT(1)
+ #define MT_MCU_INT_EVENT_RESET_DONE		BIT(3)
+ 
++#define WF_RRO_TOP_STATISTIC(_n)		MT_RRO_TOP(0x180 + _n * 0x4)
++
+ /* PLE */
+ #define MT_PLE_BASE				0x820c0000
+ #define MT_PLE(ofs)				(MT_PLE_BASE + (ofs))
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0080-mtk-wifi-mt76-mt7996-add-kite-two-pcie-with-two-wed-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0080-mtk-wifi-mt76-mt7996-add-kite-two-pcie-with-two-wed-.patch
deleted file mode 100644
index 7cf05e6..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0080-mtk-wifi-mt76-mt7996-add-kite-two-pcie-with-two-wed-.patch
+++ /dev/null
@@ -1,326 +0,0 @@
-From f56bd70e5c9b889582244a4f22a510de9b19bc74 Mon Sep 17 00:00:00 2001
-From: Rex Lu <rex.lu@mediatek.com>
-Date: Tue, 19 Mar 2024 13:16:12 +0800
-Subject: [PATCH 080/116] mtk: wifi: mt76: mt7996: add kite two pcie with two
- wed support
-
-Signed-off-by: Rex Lu <rex.lu@mediatek.com>
----
- mt7996/dma.c  | 68 ++++++++++++++++++++++++++++++++++++++-------------
- mt7996/init.c | 54 +++++++++++++++++++++++-----------------
- mt7996/main.c |  5 ++--
- mt7996/mmio.c | 15 ++++++++++--
- mt7996/pci.c  |  5 ++--
- mt7996/regs.h |  1 +
- 6 files changed, 101 insertions(+), 47 deletions(-)
-
-diff --git a/mt7996/dma.c b/mt7996/dma.c
-index 3dc0e8a..a2490fa 100644
---- a/mt7996/dma.c
-+++ b/mt7996/dma.c
-@@ -108,8 +108,8 @@ static void mt7996_dma_config(struct mt7996_dev *dev)
- 	}
- 
- 	/* data tx queue */
--	TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
- 	if (is_mt7996(&dev->mt76)) {
-+		TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
- 		if (dev->hif2) {
- 			if (dev->option_type == 2) {
- 				/*  bn1:ring21 bn2:ring19 */
-@@ -125,7 +125,15 @@ static void mt7996_dma_config(struct mt7996_dev *dev)
- 			TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
- 		}
- 	} else {
--		TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
-+		if (dev->hif2) {
-+			/*  bn0:ring18 bn1:ring21 */
-+			TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
-+			TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND2, MT7996_TXQ_BAND2);
-+		} else {
-+			/* single pcie bn0:ring18 bn1:ring19 */
-+			TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
-+			TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
-+		}
- 	}
- 
- 	/* mcu tx queue */
-@@ -285,8 +293,11 @@ void mt7996_dma_start(struct mt7996_dev *dev, bool reset, bool wed_reset)
- 	if (mt7996_band_valid(dev, MT_BAND0))
- 		irq_mask |= MT_INT_BAND0_RX_DONE;
- 
--	if (mt7996_band_valid(dev, MT_BAND1))
-+	if (mt7996_band_valid(dev, MT_BAND1)) {
- 		irq_mask |= MT_INT_BAND1_RX_DONE;
-+		if (is_mt7992(&dev->mt76) && dev->hif2)
-+			irq_mask |= MT_INT_RX_TXFREE_BAND1_EXT;
-+	}
- 
- 	if (mt7996_band_valid(dev, MT_BAND2))
- 		irq_mask |= MT_INT_BAND2_RX_DONE;
-@@ -379,27 +390,46 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
- 			   MT_WFDMA_HOST_CONFIG_BAND1_PCIE1 |
- 			   MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
- 
--		if (dev->option_type == 2)
--			mt76_set(dev, MT_WFDMA_HOST_CONFIG,
--				 MT_WFDMA_HOST_CONFIG_BAND0_PCIE1 |
--				 MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
--		else
--			mt76_set(dev, MT_WFDMA_HOST_CONFIG,
--				 MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
--
--		if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
--		    is_mt7992(&dev->mt76)) {
-+		switch (dev->option_type) {
-+		case 2:
-+			/* eagle + 7988d */
-+			if (is_mt7996(&dev->mt76))
-+				mt76_set(dev, MT_WFDMA_HOST_CONFIG,
-+					 MT_WFDMA_HOST_CONFIG_BAND0_PCIE1 |
-+					 MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
-+			else
-+				mt76_set(dev, MT_WFDMA_HOST_CONFIG,
-+					 MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
-+			break;
-+		case 3:
- 			mt76_set(dev, MT_WFDMA_HOST_CONFIG,
--				 MT_WFDMA_HOST_CONFIG_PDMA_BAND |
--				 MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
-+				 MT_WFDMA_HOST_CONFIG_BAND0_PCIE1);
-+
-+			break;
-+		default:
-+			if (is_mt7996(&dev->mt76))
-+				mt76_set(dev, MT_WFDMA_HOST_CONFIG,
-+					 MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
-+			else
-+				mt76_set(dev, MT_WFDMA_HOST_CONFIG,
-+					 MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
-+			break;
- 		}
- 
- 		/* AXI read outstanding number */
- 		mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL,
- 			 MT_WFDMA_AXI_R2A_CTRL_OUTSTAND_MASK, 0x14);
- 
--		if (dev->hif2->speed < PCIE_SPEED_8_0GT ||
--		    (dev->hif2->speed == PCIE_SPEED_8_0GT && dev->hif2->width < 2)) {
-+		if (dev->hif2->speed < PCIE_SPEED_5_0GT ||
-+		    (dev->hif2->speed == PCIE_SPEED_5_0GT && dev->hif2->width < 2)) {
-+			mt76_rmw(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs,
-+				 WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK,
-+				 FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, 0x1));
-+			mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL2,
-+				 MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK,
-+				 FIELD_PREP(MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK, 0x1));
-+		} else if (dev->hif2->speed < PCIE_SPEED_8_0GT ||
-+			   (dev->hif2->speed == PCIE_SPEED_8_0GT && dev->hif2->width < 2)) {
- 			mt76_rmw(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs,
- 				 WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK,
- 				 FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, 0x3));
-@@ -648,6 +678,10 @@ int mt7996_dma_init(struct mt7996_dev *dev)
- 
- 		/* tx free notify event from WA for mt7992 band1 */
- 		rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND1_WA) + hif1_ofs;
-+		if (mtk_wed_device_active(wed_hif2)) {
-+			dev->mt76.q_rx[MT_RXQ_BAND1_WA].flags = MT_WED_Q_TXFREE;
-+			dev->mt76.q_rx[MT_RXQ_BAND1_WA].wed = wed_hif2;
-+		}
- 		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND1_WA],
- 				       MT_RXQ_ID(MT_RXQ_BAND1_WA),
- 				       MT7996_RX_MCU_RING_SIZE,
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 1e7cd52..768979e 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -525,39 +525,46 @@ void mt7996_mac_init(struct mt7996_dev *dev)
- 	}
- 
- 	/* rro module init */
-+	rx_path_type = is_mt7996(&dev->mt76) ? 2 : 7;
-+	rro_bypass = is_mt7996(&dev->mt76) ? 1 : 2;
-+	txfree_path = is_mt7996(&dev->mt76) ? 0: 1;
-+
- 	switch (dev->option_type) {
- 	case 2:
--		/* eagle + 7988d */
--		rx_path_type = 3;
--		rro_bypass = dev->has_rro ? 1 : 3;
--		txfree_path = dev->has_rro ? 0 : 1;
-+		if (is_mt7996(&dev->mt76)) {
-+			/* eagle + 7988d */
-+			rx_path_type = 3;
-+			rro_bypass = 1;
-+			txfree_path = 0;
-+		}
- 		break;
- 	case 3:
--		/* eagle + Airoha */
--		rx_path_type = 6;
--		rro_bypass = dev->has_rro ? 1 : 3;
--		txfree_path = dev->has_rro ? 0 : 1;
-+		/* Airoha */
-+		if (is_mt7996(&dev->mt76)) {
-+			rx_path_type = 6;
-+			rro_bypass = 1;
-+			txfree_path = 0;
-+		} else {
-+			rx_path_type = 8;
-+			rro_bypass = 2;
-+			txfree_path = 1;
-+		}
- 		break;
- 	case 4:
--		/* Bollinger */
--		rx_path_type = 2;
--		rro_bypass = dev->has_rro ? 1 : 3;
--		txfree_path = dev->has_rro ? 0 : 1;
-+		if (is_mt7996(&dev->mt76)) {
-+			/* Bollinger */
-+			rx_path_type = 2;
-+			rro_bypass = 1;
-+			txfree_path = 0;
-+		}
- 		break;
- 	default:
--		if (is_mt7996(&dev->mt76))
--			rx_path_type = 2;
--		else
--			rx_path_type = 7;
--
--		rro_bypass = dev->has_rro ? 1 : 3;
--		txfree_path = dev->has_rro ? 0 : 1;
- 		break;
- 	}
- 
- 	mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, dev->hif2 ? rx_path_type : 0);
--	mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, rro_bypass);
--	mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, txfree_path);
-+	mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, dev->has_rro ? rro_bypass : 3);
-+	mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, dev->has_rro ? txfree_path : 1);
- 
- 	if (dev->has_rro) {
- 		u16 timeout;
-@@ -641,7 +648,7 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
- 	if (phy)
- 		return 0;
- 
--	if (is_mt7996(&dev->mt76) && dev->hif2) {
-+	if (dev->hif2) {
- 		switch (dev->option_type) {
- 		case 2:
- 			/* eagle + 7988d */
-@@ -651,7 +658,8 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
- 			}
- 			break;
- 		default:
--			if (band == MT_BAND2) {
-+			if ((is_mt7996(&dev->mt76) && band == MT_BAND2) ||
-+			    (is_mt7992(&dev->mt76) && band == MT_BAND1)) {
- 				hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
- 				wed = &dev->mt76.mmio.wed_hif2;
- 			}
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 775d81e..c9e8108 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -1607,7 +1607,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
- 	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
- 
--	if (phy != &dev->phy && dev->hif2) {
-+	if (dev->hif2) {
- 		switch (dev->option_type) {
- 		case 2:
- 			/* eagle + 7988d */
-@@ -1615,7 +1615,8 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
- 				wed = &dev->mt76.mmio.wed_hif2;
- 			break;
- 		default:
--			if (phy->mt76->band_idx == MT_BAND2)
-+			if ((is_mt7996(&dev->mt76) && phy->mt76->band_idx == MT_BAND2) ||
-+			    (is_mt7992(&dev->mt76) && phy->mt76->band_idx == MT_BAND1))
- 				wed = &dev->mt76.mmio.wed_hif2;
- 			break;
- 		}
-diff --git a/mt7996/mmio.c b/mt7996/mmio.c
-index 91567a0..6028182 100644
---- a/mt7996/mmio.c
-+++ b/mt7996/mmio.c
-@@ -336,10 +336,16 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- 					     MT_TXQ_RING_BASE(0) +
- 					     MT7996_TXQ_BAND2 * MT_RING_SIZE;
- 		if (dev->has_rro) {
-+			u8 rxq_id = is_mt7996(&dev->mt76) ?
-+				    MT7996_RXQ_TXFREE2 : MT7996_RXQ_MCU_WA_EXT;
-+
- 			wed->wlan.wpdma_txfree = wed->wlan.phy_base + hif1_ofs +
- 						 MT_RXQ_RING_BASE(0) +
--						 MT7996_RXQ_TXFREE2 * MT_RING_SIZE;
--			wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_EXT) - 1;
-+						 rxq_id * MT_RING_SIZE;
-+			if (is_mt7996(&dev->mt76))
-+				wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_EXT) - 1;
-+			else
-+				wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_BAND1_EXT) - 1;
- 		} else {
- 			wed->wlan.wpdma_txfree = wed->wlan.phy_base + hif1_ofs +
- 						 MT_RXQ_RING_BASE(0) +
-@@ -423,6 +429,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- 						 MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE;
- 		}
- 		dev->mt76.rx_token_size = MT7996_TOKEN_SIZE + wed->wlan.rx_npkt;
-+		if(dev->hif2 && is_mt7992(&dev->mt76))
-+			wed->wlan.id = 0x7992;
- 	}
- 
- 	wed->wlan.nbuf = MT7996_TOKEN_SIZE;
-@@ -553,6 +561,9 @@ static void mt7996_irq_tasklet(struct tasklet_struct *t)
- 
- 		if (intr1 & MT_INT_RX_DONE_BAND2_EXT)
- 			napi_schedule(&dev->mt76.napi[MT_RXQ_BAND2]);
-+
-+		if (is_mt7992(&dev->mt76) && (intr1 & MT_INT_RX_TXFREE_BAND1_EXT))
-+			napi_schedule(&dev->mt76.napi[MT_RXQ_BAND1_WA]);
- 	}
- 
- 	if (mtk_wed_device_active(wed)) {
-diff --git a/mt7996/pci.c b/mt7996/pci.c
-index 24d69d4..382b6a8 100644
---- a/mt7996/pci.c
-+++ b/mt7996/pci.c
-@@ -110,7 +110,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
- 	int irq, ret;
- 	struct mt76_dev *mdev;
- 
--	hif2_enable |= (id->device == 0x7990 || id->device == 0x7991);
-+	hif2_enable |= (id->device == 0x7990 || id->device == 0x7991 || id->device == 0x799a);
- 
- 	ret = pcim_enable_device(pdev);
- 	if (ret)
-@@ -171,8 +171,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
- 		hif2_dev = container_of(hif2->dev, struct pci_dev, dev);
- 		ret = 0;
- 
--		if (is_mt7996(&dev->mt76))
--			ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &irq);
-+		ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &irq);
- 
- 		if (ret < 0)
- 			goto free_wed_or_irq_vector;
-diff --git a/mt7996/regs.h b/mt7996/regs.h
-index a0e4b3e..e189351 100644
---- a/mt7996/regs.h
-+++ b/mt7996/regs.h
-@@ -525,6 +525,7 @@ enum offs_rev {
- #define MT_INT_RX_TXFREE_MAIN			BIT(17)
- #define MT_INT_RX_TXFREE_BAND1			BIT(15)
- #define MT_INT_RX_TXFREE_TRI			BIT(15)
-+#define MT_INT_RX_TXFREE_BAND1_EXT		BIT(19) /* for mt7992 two PCIE*/
- #define MT_INT_RX_DONE_BAND2_EXT		BIT(23)
- #define MT_INT_RX_TXFREE_EXT			BIT(26)
- #define MT_INT_MCU_CMD				BIT(29)
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0081-mtk-mt76-mt7996-add-support-for-WMM-PBC-configuratio.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0081-mtk-mt76-mt7996-add-support-for-WMM-PBC-configuratio.patch
new file mode 100644
index 0000000..549d7e0
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0081-mtk-mt76-mt7996-add-support-for-WMM-PBC-configuratio.patch
@@ -0,0 +1,218 @@
+From 3b97fa94c4e5a839ac9e31b63e07090b22e84330 Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Thu, 4 Jan 2024 09:47:00 +0800
+Subject: [PATCH 081/199] mtk: mt76: mt7996: add support for WMM PBC
+ configuration
+
+Query per-AC-queue packet statistics from WA, and determine if multi-AC transmission is ongoing.
+If it is, enable WMM mode in WA. Otherwise, disable WMM mode.
+
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ mt76_connac_mcu.h |  2 ++
+ mt7996/init.c     |  2 ++
+ mt7996/mac.c      |  4 +++
+ mt7996/mcu.c      | 78 +++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h      | 15 +++++++++
+ mt7996/mt7996.h   |  4 +++
+ 6 files changed, 105 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index f4574aaa..c8eedf36 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1037,6 +1037,7 @@ enum {
+ 	MCU_EXT_EVENT_ASSERT_DUMP = 0x23,
+ 	MCU_EXT_EVENT_RDD_REPORT = 0x3a,
+ 	MCU_EXT_EVENT_CSA_NOTIFY = 0x4f,
++	MCU_EXT_EVENT_BSS_ACQ_PKT_CNT = 0x52,
+ 	MCU_EXT_EVENT_WA_TX_STAT = 0x74,
+ 	MCU_EXT_EVENT_BCC_NOTIFY = 0x75,
+ 	MCU_EXT_EVENT_MURU_CTRL = 0x9f,
+@@ -1236,6 +1237,7 @@ enum {
+ 	MCU_EXT_CMD_TXDPD_CAL = 0x60,
+ 	MCU_EXT_CMD_CAL_CACHE = 0x67,
+ 	MCU_EXT_CMD_RED_ENABLE = 0x68,
++	MCU_EXT_CMD_PKT_BUDGET_CTRL = 0x6c,
+ 	MCU_EXT_CMD_CP_SUPPORT = 0x75,
+ 	MCU_EXT_CMD_SET_RADAR_TH = 0x7c,
+ 	MCU_EXT_CMD_SET_RDD_PATTERN = 0x7d,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index f682423a..7c273f9c 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -1536,6 +1536,8 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ 	INIT_WORK(&dev->dump_work, mt7996_mac_dump_work);
+ 	mutex_init(&dev->dump_mutex);
+ 
++	INIT_WORK(&dev->wmm_pbc_work, mt7996_mcu_wmm_pbc_work);
++
+ 	ret = mt7996_init_hardware(dev);
+ 	if (ret)
+ 		return ret;
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 06a86146..099e97ef 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2298,6 +2298,10 @@ void mt7996_mac_work(struct work_struct *work)
+ 					mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_ADM_STAT);
+ 					mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_MSDU_COUNT);
+ 				}
++
++				if (mt7996_mcu_wa_cmd(phy->dev, MCU_WA_PARAM_CMD(QUERY), MCU_WA_PARAM_BSS_ACQ_PKT_CNT,
++				                      BSS_ACQ_PKT_CNT_BSS_BITMAP_ALL | BSS_ACQ_PKT_CNT_READ_CLR, 0))
++					dev_err(mdev->dev, "Failed to query per-AC-queue packet counts.\n");
+ 			} else if (mt7996_band_valid(phy->dev, i) &&
+ 			           test_bit(MT76_STATE_RUNNING, &mdev->phys[i]->state))
+ 				break;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index c7f9a56d..28cedb60 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -669,6 +669,82 @@ mt7996_mcu_rx_thermal_notify(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	phy->throttle_state = n->duty_percent;
+ }
+ 
++void mt7996_mcu_wmm_pbc_work(struct work_struct *work)
++{
++#define WMM_PBC_QUEUE_NUM	5
++#define WMM_PBC_BSS_ALL		0xff
++#define WMM_PBC_WLAN_IDX_ALL	0xffff
++#define WMM_PBC_BOUND_DEFAULT	0xffff
++#define WMM_PBC_LOW_BOUND_VO	1900
++#define WMM_PBC_LOW_BOUND_VI	1900
++#define WMM_PBC_LOW_BOUND_BE	1500
++#define WMM_PBC_LOW_BOUND_BK	900
++#define WMM_PBC_LOW_BOUND_MGMT	32
++	struct mt7996_dev *dev = container_of(work, struct mt7996_dev, wmm_pbc_work);
++	struct {
++		u8 bss_idx;
++		u8 queue_num;
++		__le16 wlan_idx;
++		u8 band_idx;
++		u8 __rsv[3];
++		struct {
++			__le16 low;
++			__le16 up;
++		} __packed bound[WMM_PBC_QUEUE_NUM];
++	} __packed req = {
++		.bss_idx = WMM_PBC_BSS_ALL,
++		.queue_num = WMM_PBC_QUEUE_NUM,
++		.wlan_idx = cpu_to_le16(WMM_PBC_WLAN_IDX_ALL),
++		.band_idx = dev->mphy.band_idx,
++	};
++	int i, ret;
++
++#define pbc_acq_low_bound_config(_ac, _bound)								\
++	req.bound[mt76_connac_lmac_mapping(_ac)].low = dev->wmm_pbc_enable ? cpu_to_le16(_bound) : 0
++	pbc_acq_low_bound_config(IEEE80211_AC_VO, WMM_PBC_LOW_BOUND_VO);
++	pbc_acq_low_bound_config(IEEE80211_AC_VI, WMM_PBC_LOW_BOUND_VI);
++	pbc_acq_low_bound_config(IEEE80211_AC_BE, WMM_PBC_LOW_BOUND_BE);
++	pbc_acq_low_bound_config(IEEE80211_AC_BK, WMM_PBC_LOW_BOUND_BK);
++	req.bound[4].low = dev->wmm_pbc_enable
++	                   ? cpu_to_le16(WMM_PBC_LOW_BOUND_MGMT) : 0;
++
++	for (i = 0; i < WMM_PBC_QUEUE_NUM; ++i)
++		req.bound[i].up = cpu_to_le16(WMM_PBC_BOUND_DEFAULT);
++
++	ret = mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(PKT_BUDGET_CTRL),
++	                        &req, sizeof(req), true);
++	if (ret)
++		dev_err(dev->mt76.dev, "Failed to configure WMM PBC.\n");
++}
++
++static void
++mt7996_mcu_rx_bss_acq_pkt_cnt(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++	struct mt7996_mcu_bss_acq_pkt_cnt_event *event = (struct mt7996_mcu_bss_acq_pkt_cnt_event *)skb->data;
++	u32 bitmap = le32_to_cpu(event->bss_bitmap);
++	u64 sum[IEEE80211_NUM_ACS] = {0};
++	u8 ac_cnt = 0;
++	int i, j;
++
++	for (i = 0; (i < BSS_ACQ_PKT_CNT_BSS_NUM) && (bitmap & (1 << i)); ++i) {
++		for (j = IEEE80211_AC_VO; j < IEEE80211_NUM_ACS; ++j)
++			sum[j] += le32_to_cpu(event->bss[i].cnt[mt76_connac_lmac_mapping(j)]);
++	}
++
++	for (i = IEEE80211_AC_VO; i < IEEE80211_NUM_ACS; ++i) {
++		if (sum[i] > WMM_PKT_THRESHOLD)
++			++ac_cnt;
++	}
++
++	if (ac_cnt > 1 && !dev->wmm_pbc_enable) {
++		dev->wmm_pbc_enable = true;
++		queue_work(dev->mt76.wq, &dev->wmm_pbc_work);
++	} else if (ac_cnt <= 1 && dev->wmm_pbc_enable) {
++		dev->wmm_pbc_enable = false;
++		queue_work(dev->mt76.wq, &dev->wmm_pbc_work);
++	}
++}
++
+ static void
+ mt7996_mcu_rx_ext_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+@@ -678,6 +754,8 @@ mt7996_mcu_rx_ext_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	case MCU_EXT_EVENT_FW_LOG_2_HOST:
+ 		mt7996_mcu_rx_log_message(dev, skb);
+ 		break;
++	case MCU_EXT_EVENT_BSS_ACQ_PKT_CNT:
++		mt7996_mcu_rx_bss_acq_pkt_cnt(dev, skb);
+ 	default:
+ 		break;
+ 	}
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 5aa55e4f..8ffb16f8 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -369,10 +369,25 @@ enum {
+ 	MCU_WA_PARAM_CMD_DEBUG,
+ };
+ 
++#define BSS_ACQ_PKT_CNT_BSS_NUM		24
++#define BSS_ACQ_PKT_CNT_BSS_BITMAP_ALL	0x00ffffff
++#define BSS_ACQ_PKT_CNT_READ_CLR	BIT(31)
++#define WMM_PKT_THRESHOLD		100
++
++struct mt7996_mcu_bss_acq_pkt_cnt_event {
++	struct mt7996_mcu_rxd rxd;
++
++	__le32 bss_bitmap;
++	struct {
++		__le32 cnt[IEEE80211_NUM_ACS];
++	} __packed bss[BSS_ACQ_PKT_CNT_BSS_NUM];
++} __packed;
++
+ enum {
+ 	MCU_WA_PARAM_PDMA_RX = 0x04,
+ 	MCU_WA_PARAM_CPU_UTIL = 0x0b,
+ 	MCU_WA_PARAM_RED_EN = 0x0e,
++	MCU_WA_PARAM_BSS_ACQ_PKT_CNT = 0x12,
+ 	MCU_WA_PARAM_HW_PATH_HIF_VER = 0x2f,
+ 	MCU_WA_PARAM_RED_CONFIG = 0x40,
+ };
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index b0b61b12..fb1d1d84 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -607,6 +607,9 @@ struct mt7996_dev {
+ 	u8 wtbl_size_group;
+ 
+ 	struct mt7996_vow_ctrl vow;
++
++	bool wmm_pbc_enable;
++	struct work_struct wmm_pbc_work;
+ #ifdef CONFIG_MTK_DEBUG
+ 	u16 wlan_idx;
+ 	struct {
+@@ -858,6 +861,7 @@ int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable);
+ int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
+ 	                        enum vow_drr_ctrl_id id);
+ int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy);
++void mt7996_mcu_wmm_pbc_work(struct work_struct *work);
+ 
+ static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
+ {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0081-mtk-wifi-mt76-mt7992-add-support-to-enable-index-FW-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0081-mtk-wifi-mt76-mt7992-add-support-to-enable-index-FW-.patch
deleted file mode 100644
index 0748f93..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0081-mtk-wifi-mt76-mt7992-add-support-to-enable-index-FW-.patch
+++ /dev/null
@@ -1,628 +0,0 @@
-From 4b13a975d69ad2c954791dbdfbcb3c7c7a5bf63f Mon Sep 17 00:00:00 2001
-From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
-Date: Thu, 11 Jan 2024 08:55:13 +0800
-Subject: [PATCH 081/116] mtk: wifi: mt76: mt7992: add support to enable index
- FW log for ConsysPlanet
-
-Add support to enable and record index FW log, which is the input for ConsysPlanet, via mt76-test command.
-
-Usage:
-1. Foreground logging
-	1) Start: mt76-test phy0 idxlog
-	2) Stop: Ctrl + C
-2. Background logging
-	1) Start: mt76-test phy0 idxlog &
-	2) Stop: killall mt76-test
-3. Logging with FW Parser
-	1) Start Ethernet recording of FW Parser.
-	2) Start: mt76-test phy0 idxlog <PC's IP Address>
-	3) Stop: Ctrl + C
-	4) Stop FW Parser.
-
-Log Files
-- FW Log: FW text message
-	- Location: /tmp/log/clog_(timestamp)/WIFI_FW_(timestamp).clog
-- Driver Log: log message printed at driver layer
-	- Location: /tmp/log/clog_(timestamp)/WIFI_KERNEL_(timestamp).clog
-
-Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
----
- mt7996/debugfs.c  |  90 +++++++++++++++++--
- mt7996/mac.c      |  10 ++-
- mt7996/mcu.c      |  34 +++++++-
- mt7996/mcu.h      |   4 +-
- mt7996/mt7996.h   |   3 +
- tools/fwlog.c     | 218 ++++++++++++++++++++++++++++++++++------------
- tools/main.c      |   2 +
- tools/mt76-test.h |   3 +
- 8 files changed, 295 insertions(+), 69 deletions(-)
-
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index e26de48..8377586 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -430,8 +430,8 @@ create_buf_file_cb(const char *filename, struct dentry *parent, umode_t mode,
- {
- 	struct dentry *f;
- 
--	f = debugfs_create_file("fwlog_data", mode, parent, buf,
--				&relay_file_operations);
-+	f = debugfs_create_file(filename[0] == 'f' ? "fwlog_data" : "idxlog_data",
-+	                        mode, parent, buf, &relay_file_operations);
- 	if (IS_ERR(f))
- 		return NULL;
- 
-@@ -522,6 +522,53 @@ mt7996_fw_debug_bin_get(void *data, u64 *val)
- DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_bin, mt7996_fw_debug_bin_get,
- 			 mt7996_fw_debug_bin_set, "%lld\n");
- 
-+static int
-+mt7996_idxlog_enable_get(void *data, u64 *val)
-+{
-+	struct mt7996_dev *dev = data;
-+
-+	*val = dev->idxlog_enable;
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_idxlog_enable_set(void *data, u64 val)
-+{
-+	static struct rchan_callbacks relay_cb = {
-+		.create_buf_file = create_buf_file_cb,
-+		.remove_buf_file = remove_buf_file_cb,
-+	};
-+	struct mt7996_dev *dev = data;
-+
-+	if (dev->idxlog_enable == !!val)
-+		return 0;
-+
-+	if (!dev->relay_idxlog) {
-+		dev->relay_idxlog = relay_open("idxlog_data", dev->debugfs_dir,
-+		                               1500, 512, &relay_cb, NULL);
-+		if (!dev->relay_idxlog)
-+			return -ENOMEM;
-+	}
-+
-+	dev->idxlog_enable = !!val;
-+
-+	if (val) {
-+		int ret = mt7996_mcu_fw_time_sync(&dev->mt76);
-+		if (ret)
-+			return ret;
-+
-+		/* Reset relay channel only when it is not being written to. */
-+		relay_reset(dev->relay_idxlog);
-+	}
-+
-+	return mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM,
-+	                                val ? MCU_FW_LOG_RELAY_IDX : 0);
-+}
-+
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_idxlog_enable, mt7996_idxlog_enable_get,
-+	                 mt7996_idxlog_enable_set, "%llu\n");
-+
- static int
- mt7996_fw_util_wa_show(struct seq_file *file, void *data)
- {
-@@ -1042,6 +1089,7 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
- 	debugfs_create_file("fw_debug_wm", 0600, dir, dev, &fops_fw_debug_wm);
- 	debugfs_create_file("fw_debug_wa", 0600, dir, dev, &fops_fw_debug_wa);
- 	debugfs_create_file("fw_debug_bin", 0600, dir, dev, &fops_fw_debug_bin);
-+	debugfs_create_file("idxlog_enable", 0600, dir, dev, &fops_idxlog_enable);
- 	/* TODO: wm fw cpu utilization */
- 	debugfs_create_file("fw_util_wa", 0400, dir, dev,
- 			    &mt7996_fw_util_wa_fops);
-@@ -1108,6 +1156,32 @@ mt7996_debugfs_write_fwlog(struct mt7996_dev *dev, const void *hdr, int hdrlen,
- 	spin_unlock_irqrestore(&lock, flags);
- }
- 
-+static void
-+mt7996_debugfs_write_idxlog(struct mt7996_dev *dev, const void *data, int len)
-+{
-+	static DEFINE_SPINLOCK(lock);
-+	unsigned long flags;
-+	void *dest;
-+
-+	if (!dev->relay_idxlog)
-+		return;
-+
-+	spin_lock_irqsave(&lock, flags);
-+
-+	dest = relay_reserve(dev->relay_idxlog, len + 4);
-+	if (!dest)
-+		dev_err(dev->mt76.dev, "Failed to reserve slot in %s\n",
-+		        dev->relay_idxlog->base_filename);
-+	else {
-+		*(u32 *)dest = len;
-+		dest += 4;
-+		memcpy(dest, data, len);
-+		relay_flush(dev->relay_idxlog);
-+	}
-+
-+	spin_unlock_irqrestore(&lock, flags);
-+}
-+
- void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int len)
- {
- 	struct {
-@@ -1132,11 +1206,15 @@ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int
- 
- bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len)
- {
--	if (get_unaligned_le32(data) != FW_BIN_LOG_MAGIC)
--		return false;
-+	bool is_fwlog = get_unaligned_le32(data) == FW_BIN_LOG_MAGIC;
- 
--	if (dev->relay_fwlog)
--		mt7996_debugfs_write_fwlog(dev, NULL, 0, data, len);
-+	if (is_fwlog) {
-+		if (dev->relay_fwlog)
-+			mt7996_debugfs_write_fwlog(dev, NULL, 0, data, len);
-+	} else if (dev->relay_idxlog)
-+		mt7996_debugfs_write_idxlog(dev, data, len);
-+	else
-+		return false;
- 
- 	return true;
- }
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index e3758ff..8c44442 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -2279,11 +2279,9 @@ void mt7996_mac_work(struct work_struct *work)
- 	mutex_lock(&mdev->mutex);
- 
- 	mt76_update_survey(mphy);
--	if (++mphy->mac_work_count == 5) {
-+	if (++mphy->mac_work_count % 5 == 0) {
- 		int i;
- 
--		mphy->mac_work_count = 0;
--
- 		mt7996_mac_update_stats(phy);
- 
- 		/* Update DEV-wise information only in
-@@ -2302,6 +2300,12 @@ void mt7996_mac_work(struct work_struct *work)
- 				if (mt7996_mcu_wa_cmd(phy->dev, MCU_WA_PARAM_CMD(QUERY), MCU_WA_PARAM_BSS_ACQ_PKT_CNT,
- 				                      BSS_ACQ_PKT_CNT_BSS_BITMAP_ALL | BSS_ACQ_PKT_CNT_READ_CLR, 0))
- 					dev_err(mdev->dev, "Failed to query per-AC-queue packet counts.\n");
-+
-+				if (mphy->mac_work_count == 100) {
-+					if (phy->dev->idxlog_enable && mt7996_mcu_fw_time_sync(mdev))
-+						dev_err(mdev->dev, "Failed to synchronize time with FW.\n");
-+					mphy->mac_work_count = 0;
-+				}
- 			} else if (mt7996_band_valid(phy->dev, i) &&
- 			           test_bit(MT76_STATE_RUNNING, &mdev->phys[i]->state))
- 				break;
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 82ed9e8..e38a507 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -400,6 +400,7 @@ static void
- mt7996_mcu_rx_log_message(struct mt7996_dev *dev, struct sk_buff *skb)
- {
- #define UNI_EVENT_FW_LOG_FORMAT 0
-+#define UNI_EVENT_FW_LOG_MEMORY	1
- 	struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data;
- 	const char *data = (char *)&rxd[1] + 4, *type;
- 	struct tlv *tlv = (struct tlv *)data;
-@@ -411,7 +412,8 @@ mt7996_mcu_rx_log_message(struct mt7996_dev *dev, struct sk_buff *skb)
- 		goto out;
- 	}
- 
--	if (le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_FORMAT)
-+	if (le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_FORMAT &&
-+	    le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_MEMORY)
- 		return;
- 
- 	data += sizeof(*tlv) + 4;
-@@ -3184,6 +3186,36 @@ int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level)
- 				 sizeof(data), false);
- }
- 
-+int mt7996_mcu_fw_time_sync(struct mt76_dev *dev)
-+{
-+	struct {
-+		u8 _rsv[4];
-+
-+		__le16 tag;
-+		__le16 len;
-+		__le32 sec;
-+		__le32 usec;
-+	} data = {
-+		.tag = cpu_to_le16(UNI_WSYS_CONFIG_FW_TIME_SYNC),
-+		.len = cpu_to_le16(sizeof(data) - 4),
-+	};
-+	struct timespec64 ts;
-+	struct tm tm;
-+
-+	ktime_get_real_ts64(&ts);
-+	data.sec = cpu_to_le32((u32)ts.tv_sec);
-+	data.usec = cpu_to_le32((u32)(ts.tv_nsec / 1000));
-+
-+	/* Dump synchronized time for ConsysPlanet to parse. */
-+	time64_to_tm(ts.tv_sec, 0, &tm);
-+	dev_info(dev->dev, "%ld-%02d-%02d %02d:%02d:%02d.%ld UTC\n",
-+	        tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
-+	        tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec / 1000);
-+
-+	return mt76_mcu_send_msg(dev, MCU_WM_UNI_CMD(WSYS_CONFIG), &data,
-+	                         sizeof(data), true);
-+}
-+
- static int mt7996_mcu_set_mwds(struct mt7996_dev *dev, bool enabled)
- {
- 	struct {
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index d24874a..814072e 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -357,7 +357,8 @@ enum {
- 	MCU_FW_LOG_WM,
- 	MCU_FW_LOG_WA,
- 	MCU_FW_LOG_TO_HOST,
--	MCU_FW_LOG_RELAY = 16
-+	MCU_FW_LOG_RELAY = 16,
-+	MCU_FW_LOG_RELAY_IDX = 40
- };
- 
- enum {
-@@ -950,6 +951,7 @@ enum {
- 	UNI_WSYS_CONFIG_FW_LOG_CTRL,
- 	UNI_WSYS_CONFIG_FW_DBG_CTRL,
- 	UNI_CMD_CERT_CFG = 6,
-+	UNI_WSYS_CONFIG_FW_TIME_SYNC, /* UNI_CMD_FW_TIME_SYNC in FW */
- };
- 
- enum {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 69bcf78..d03d3d9 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -591,9 +591,11 @@ struct mt7996_dev {
- 	u8 fw_debug_bin;
- 	u16 fw_debug_seq;
- 	bool fw_debug_muru_disable;
-+	bool idxlog_enable;
- 
- 	struct dentry *debugfs_dir;
- 	struct rchan *relay_fwlog;
-+	struct rchan *relay_idxlog;
- 
- 	void *cal;
- 	u32 cur_prek_offset;
-@@ -845,6 +847,7 @@ int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3);
- int mt7996_mcu_red_config(struct mt7996_dev *dev, bool enable);
- int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl);
- int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level);
-+int mt7996_mcu_fw_time_sync(struct mt76_dev *dev);
- int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
- void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
- void mt7996_mcu_exit(struct mt7996_dev *dev);
-diff --git a/tools/fwlog.c b/tools/fwlog.c
-index 3c6a61d..0e2de2d 100644
---- a/tools/fwlog.c
-+++ b/tools/fwlog.c
-@@ -29,10 +29,9 @@ static const char *debugfs_path(const char *phyname, const char *file)
- static int mt76_set_fwlog_en(const char *phyname, bool en, char *val)
- {
- 	FILE *f = fopen(debugfs_path(phyname, "fw_debug_bin"), "w");
--
- 	if (!f) {
--		fprintf(stderr, "Could not open fw_debug_bin file\n");
--		return 1;
-+		perror("fopen");
-+		return -1;
- 	}
- 
- 	if (en && val)
-@@ -47,6 +46,21 @@ static int mt76_set_fwlog_en(const char *phyname, bool en, char *val)
- 	return 0;
- }
- 
-+static int mt76_set_idxlog_enable(const char *phyname, bool enable)
-+{
-+	FILE *f = fopen(debugfs_path(phyname, "idxlog_enable"), "w");
-+	if (!f) {
-+		perror("fopen");
-+		return -1;
-+	}
-+
-+	fprintf(f, "%hhu", enable);
-+
-+	fclose(f);
-+
-+	return 0;
-+}
-+
- int read_retry(int fd, void *buf, int len)
- {
- 	int out_len = 0;
-@@ -80,105 +94,193 @@ static void handle_signal(int sig)
- 	done = true;
- }
- 
--int mt76_fwlog(const char *phyname, int argc, char **argv)
-+static int mt76_log_socket(struct sockaddr_in *remote, char *ip)
- {
--#define BUF_SIZE 1504
- 	struct sockaddr_in local = {
- 		.sin_family = AF_INET,
- 		.sin_addr.s_addr = INADDR_ANY,
- 	};
--	struct sockaddr_in remote = {
--		.sin_family = AF_INET,
--		.sin_port = htons(55688),
--	};
--	char *buf = calloc(BUF_SIZE, sizeof(char));
--	int ret = 0;
--	/* int yes = 1; */
--	int s, fd;
--
--	if (argc < 1) {
--		fprintf(stderr, "need destination address\n");
--		return 1;
--	}
-+	int s, ret;
- 
--	if (!inet_aton(argv[0], &remote.sin_addr)) {
--		fprintf(stderr, "invalid destination address\n");
--		return 1;
-+	remote->sin_family = AF_INET;
-+	remote->sin_port = htons(55688);
-+	if (!inet_aton(ip, &remote->sin_addr)) {
-+		fprintf(stderr, "Invalid destination IP address: %s\n", ip);
-+		return -EINVAL;
- 	}
- 
- 	s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
- 	if (s < 0) {
- 		perror("socket");
--		return 1;
-+		return s;
- 	}
- 
--	/* setsockopt(s, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes)); */
--	if (bind(s, (struct sockaddr *)&local, sizeof(local)) < 0) {
-+	ret = bind(s, (struct sockaddr *)&local, sizeof(local));
-+	if (ret) {
- 		perror("bind");
--		return 1;
-+		close(s);
-+		return ret;
- 	}
- 
--	if (mt76_set_fwlog_en(phyname, true, argv[1]))
--		return 1;
-+	return s;
-+}
-+
-+static int mt76_log_relay(int in_fd, int out_fd, struct sockaddr_in *remote)
-+{
-+	char *buf = malloc(FWLOG_BUF_SIZE);
-+	int ret = 0;
- 
--	fd = open(debugfs_path(phyname, "fwlog_data"), O_RDONLY);
--	if (fd < 0) {
--		fprintf(stderr, "Could not open fwlog_data file: %s\n", strerror(errno));
--		ret = 1;
--		goto out;
-+	if (!buf) {
-+		perror("malloc");
-+		return -ENOMEM;
- 	}
- 
- 	signal(SIGTERM, handle_signal);
- 	signal(SIGINT, handle_signal);
- 	signal(SIGQUIT, handle_signal);
- 
--	while (1) {
-+	while (!done) {
- 		struct pollfd pfd = {
--			.fd = fd,
--			.events = POLLIN | POLLHUP | POLLERR,
-+			.fd = in_fd,
-+			.events = POLLIN,
- 		};
- 		uint32_t len;
--		int r;
--
--		if (done)
--			break;
-+		int rc;
- 
- 		poll(&pfd, 1, -1);
- 
--		r = read_retry(fd, &len, sizeof(len));
--		if (r < 0)
-+		rc = read_retry(in_fd, &len, sizeof(len));
-+		if (rc < 0) {
-+			if (!done) {
-+				fprintf(stderr, "Failed to read relay file.\n");
-+				ret = -1;
-+			}
- 			break;
--
--		if (!r)
-+		}
-+		if (!rc)
- 			continue;
- 
--		if (len > BUF_SIZE) {
--			fprintf(stderr, "Length error: %d > %d\n", len, BUF_SIZE);
--			ret = 1;
-+		if (len > FWLOG_BUF_SIZE) {
-+			fprintf(stderr, "Log size was too large: %u bytes\n", len);
-+			ret = -ENOMEM;
- 			break;
- 		}
- 
--		if (done)
-+		rc = read_retry(in_fd, buf, len);
-+		if (rc < 0) {
-+			if (!done) {
-+				fprintf(stderr, "Failed to read relay file.\n");
-+				ret = -1;
-+			}
- 			break;
--
--		r = read_retry(fd, buf, len);
--		if (done)
-+		}
-+		if (rc != len) {
-+			fprintf(stderr, "Expected log size: %u bytes\n", len);
-+			fprintf(stderr, "Read log size: %u bytes\n", rc);
-+			ret = -EIO;
- 			break;
-+		}
- 
--		if (r != len) {
--			fprintf(stderr, "Short read: %d < %d\n", r, len);
--			ret = 1;
-+		if (remote)
-+			rc = sendto(out_fd, buf, len, 0, (struct sockaddr *)remote, sizeof(*remote));
-+		else
-+			rc = write(out_fd, buf, len);
-+		if (rc < 0) {
-+			perror("sendto/write");
-+			ret = -1;
- 			break;
- 		}
-+	}
-+
-+	free(buf);
-+
-+	return ret;
-+}
-+
-+int mt76_fwlog(const char *phyname, int argc, char **argv)
-+{
-+	struct sockaddr_in remote;
-+	int in_fd, out_fd, ret;
-+
-+	if (argc < 1) {
-+		fprintf(stderr, "need destination address\n");
-+		return -EINVAL;
-+	}
-+
-+	out_fd = mt76_log_socket(&remote, argv[0]);
-+	if (out_fd < 0)
-+		return out_fd;
-+
-+	ret = mt76_set_fwlog_en(phyname, true, argv[1]);
-+	if (ret)
-+		goto close;
- 
--		/* send buf */
--		sendto(s, buf, len, 0, (struct sockaddr *)&remote, sizeof(remote));
-+	in_fd = open(debugfs_path(phyname, "fwlog_data"), O_RDONLY);
-+	if (in_fd < 0) {
-+		perror("open");
-+		goto disable;
- 	}
- 
--	close(fd);
-+	if (mt76_log_relay(in_fd, out_fd, &remote))
-+		fprintf(stderr, "Failed to relay FW log.\n");
- 
--out:
--	mt76_set_fwlog_en(phyname, false, NULL);
-+	close(in_fd);
-+disable:
-+	ret = mt76_set_fwlog_en(phyname, false, NULL);
-+close:
-+	close(out_fd);
-+
-+	return ret;
-+}
-+
-+int mt76_idxlog(const char *phyname, int argc, char **argv)
-+{
-+#define IDXLOG_FILE_PATH	"/tmp/log/WIFI_FW.clog"
-+	struct sockaddr_in remote;
-+	int in_fd, out_fd, ret;
-+
-+	if (argc) {
-+		out_fd = mt76_log_socket(&remote, argv[0]);
-+		if (out_fd < 0)
-+			return out_fd;
-+	} else {
-+		out_fd = open(IDXLOG_FILE_PATH, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR);
-+		if (out_fd < 0) {
-+			perror("open");
-+			return -1;
-+		}
-+	}
-+
-+	ret = mt76_set_idxlog_enable(phyname, true);
-+	if (ret)
-+		goto close;
-+
-+	in_fd = open(debugfs_path(phyname, "idxlog_data"), O_RDONLY);
-+	if (in_fd < 0) {
-+		perror("open");
-+		goto disable;
-+	}
-+
-+	if (mt76_log_relay(in_fd, out_fd, argc ? &remote : NULL))
-+		fprintf(stderr, "Failed to relay index log.\n");
-+
-+	close(in_fd);
-+disable:
-+	ret = mt76_set_idxlog_enable(phyname, false);
-+close:
-+	close(out_fd);
-+
-+	if (argc)
-+		system("timestamp=$(date +\"%y%m%d_%H%M%S\");"
-+		       "clog_dir=/tmp/log/clog_${timestamp};"
-+		       "mkdir ${clog_dir};"
-+		       "dmesg > ${clog_dir}/WIFI_KERNEL_${timestamp}.clog");
-+	else
-+		system("timestamp=$(date +\"%y%m%d_%H%M%S\");"
-+		       "clog_dir=/tmp/log/clog_${timestamp};"
-+		       "mkdir ${clog_dir};"
-+		       "mv /tmp/log/WIFI_FW.clog ${clog_dir}/WIFI_FW_${timestamp}.clog;"
-+		       "dmesg > ${clog_dir}/WIFI_KERNEL_${timestamp}.clog");
- 
- 	return ret;
- }
-diff --git a/tools/main.c b/tools/main.c
-index 699a9ee..e9e2556 100644
---- a/tools/main.c
-+++ b/tools/main.c
-@@ -198,6 +198,8 @@ int main(int argc, char **argv)
- 		ret = mt76_eeprom(phy, argc, argv);
- 	else if (!strcmp(cmd, "fwlog"))
- 		ret = mt76_fwlog(phyname, argc, argv);
-+	else if (!strcmp(cmd, "idxlog"))
-+		ret = mt76_idxlog(phyname, argc, argv);
- 	else
- 		usage();
- 
-diff --git a/tools/mt76-test.h b/tools/mt76-test.h
-index d2fafa8..b9d508c 100644
---- a/tools/mt76-test.h
-+++ b/tools/mt76-test.h
-@@ -22,6 +22,8 @@
- #define EEPROM_FILE_PATH_FMT	"/tmp/mt76-test-%s"
- #define EEPROM_PART_SIZE	20480
- 
-+#define FWLOG_BUF_SIZE	1504
-+
- struct nl_msg;
- struct nlattr;
- 
-@@ -61,5 +63,6 @@ extern unsigned char *eeprom_data;
- void usage(void);
- int mt76_eeprom(int phy, int argc, char **argv);
- int mt76_fwlog(const char *phyname, int argc, char **argv);
-+int mt76_idxlog(const char *phyname, int argc, char **argv);
- 
- #endif
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0082-mtk-mt76-mt7996-eagle-support-extra-option_type.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0082-mtk-mt76-mt7996-eagle-support-extra-option_type.patch
new file mode 100644
index 0000000..9b0e560
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0082-mtk-mt76-mt7996-eagle-support-extra-option_type.patch
@@ -0,0 +1,324 @@
+From b464b7e32093adb25adb0949df6debcb5c60780e Mon Sep 17 00:00:00 2001
+From: Rex Lu <rex.lu@mediatek.com>
+Date: Thu, 1 Feb 2024 10:32:42 +0800
+Subject: [PATCH 082/199] mtk: mt76: mt7996: eagle support extra option_type
+
+1. eagle + mt7988d option_type 2 support
+2. eagle single pcie support
+
+1. adjust pcie outstanding value by pcie speed. not no longer by option_type.
+
+(cherry picked from commit 31d64e0f571eb06ed67f4916bc12fbcfe1263c47)
+
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+---
+ mt7996/dma.c    | 51 +++++++++++++++++++++++++++++++++----
+ mt7996/init.c   | 67 ++++++++++++++++++++++++++++++++++++++-----------
+ mt7996/main.c   | 15 +++++++++--
+ mt7996/mt7996.h |  5 ++++
+ mt7996/pci.c    |  2 +-
+ mt7996/regs.h   |  5 ++++
+ 6 files changed, 123 insertions(+), 22 deletions(-)
+
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index c23b0d65..3dc0e8a1 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -12,12 +12,20 @@ int mt7996_init_tx_queues(struct mt7996_phy *phy, int idx, int n_desc,
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+ 	u32 flags = 0;
++	int i;
++
++	if (phy->mt76->band_idx == MT_BAND1 && !dev->hif2 && is_mt7996(&dev->mt76)) {
++		phy->mt76->q_tx[0] = phy->mt76->dev->phys[MT_BAND0]->q_tx[0];
++		for (i = 1; i <= MT_TXQ_PSD; i++)
++			phy->mt76->q_tx[i] = phy->mt76->q_tx[0];
++		return 0;
++	}
+ 
+ 	if (mtk_wed_device_active(wed)) {
+ 		ring_base += MT_TXQ_ID(0) * MT_RING_SIZE;
+ 		idx -= MT_TXQ_ID(0);
+ 
+-		if (phy->mt76->band_idx == MT_BAND2)
++		if (wed == &dev->mt76.mmio.wed_hif2)
+ 			flags = MT_WED_Q_TX(0);
+ 		else
+ 			flags = MT_WED_Q_TX(idx);
+@@ -102,8 +110,20 @@ static void mt7996_dma_config(struct mt7996_dev *dev)
+ 	/* data tx queue */
+ 	TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
+ 	if (is_mt7996(&dev->mt76)) {
+-		TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
+-		TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND2, MT7996_TXQ_BAND2);
++		if (dev->hif2) {
++			if (dev->option_type == 2) {
++				/*  bn1:ring21 bn2:ring19 */
++				TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND2, MT7996_TXQ_BAND2);
++				TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
++			} else {
++				/* default bn1:ring19 bn2:ring21 */
++				TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
++				TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND2, MT7996_TXQ_BAND2);
++			}
++		} else {
++			/* single pcie bn0/1:ring18 bn2:ring19 */
++			TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
++		}
+ 	} else {
+ 		TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
+ 	}
+@@ -352,8 +372,20 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ 			 WF_WFDMA0_GLO_CFG_EXT1_TX_FCTRL_MODE);
+ 
+ 		mt76_set(dev, MT_WFDMA_HOST_CONFIG,
+-			 MT_WFDMA_HOST_CONFIG_PDMA_BAND |
+-			 MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
++			 MT_WFDMA_HOST_CONFIG_PDMA_BAND);
++
++		mt76_clear(dev, MT_WFDMA_HOST_CONFIG,
++			   MT_WFDMA_HOST_CONFIG_BAND0_PCIE1 |
++			   MT_WFDMA_HOST_CONFIG_BAND1_PCIE1 |
++			   MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
++
++		if (dev->option_type == 2)
++			mt76_set(dev, MT_WFDMA_HOST_CONFIG,
++				 MT_WFDMA_HOST_CONFIG_BAND0_PCIE1 |
++				 MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
++		else
++			mt76_set(dev, MT_WFDMA_HOST_CONFIG,
++				 MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
+ 
+ 		if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
+ 		    is_mt7992(&dev->mt76)) {
+@@ -366,6 +398,15 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ 		mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL,
+ 			 MT_WFDMA_AXI_R2A_CTRL_OUTSTAND_MASK, 0x14);
+ 
++		if (dev->hif2->speed < PCIE_SPEED_8_0GT ||
++		    (dev->hif2->speed == PCIE_SPEED_8_0GT && dev->hif2->width < 2)) {
++			mt76_rmw(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs,
++				 WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK,
++				 FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, 0x3));
++			mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL2,
++				 MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK,
++				 FIELD_PREP(MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK, 0x3));
++		}
+ 		/* WFDMA rx threshold */
+ 		mt76_wr(dev, MT_WFDMA0_PAUSE_RX_Q_45_TH + hif1_ofs, 0xc000c);
+ 		mt76_wr(dev, MT_WFDMA0_PAUSE_RX_Q_67_TH + hif1_ofs, 0x10008);
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 7c273f9c..910ffccf 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -513,7 +513,7 @@ static void mt7996_mac_init_basic_rates(struct mt7996_dev *dev)
+ void mt7996_mac_init(struct mt7996_dev *dev)
+ {
+ #define HIF_TXD_V2_1	0x21
+-	int i;
++	int i, rx_path_type, rro_bypass, txfree_path;
+ 
+ 	mt76_clear(dev, MT_MDP_DCR2, MT_MDP_DCR2_RX_TRANS_SHORT);
+ 
+@@ -527,22 +527,45 @@ void mt7996_mac_init(struct mt7996_dev *dev)
+ 	}
+ 
+ 	/* rro module init */
+-	if (is_mt7996(&dev->mt76))
+-		mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, 2);
+-	else
+-		mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE,
+-				   dev->hif2 ? 7 : 0);
++	switch (dev->option_type) {
++	case 2:
++		/* eagle + 7988d */
++		rx_path_type = 3;
++		rro_bypass = dev->has_rro ? 1 : 3;
++		txfree_path = dev->has_rro ? 0 : 1;
++		break;
++	case 3:
++		/* eagle + Airoha */
++		rx_path_type = 6;
++		rro_bypass = dev->has_rro ? 1 : 3;
++		txfree_path = dev->has_rro ? 0 : 1;
++		break;
++	case 4:
++		/* Bollinger */
++		rx_path_type = 2;
++		rro_bypass = dev->has_rro ? 1 : 3;
++		txfree_path = dev->has_rro ? 0 : 1;
++		break;
++	default:
++		if (is_mt7996(&dev->mt76))
++			rx_path_type = 2;
++		else
++			rx_path_type = 7;
++
++		rro_bypass = dev->has_rro ? 1 : 3;
++		txfree_path = dev->has_rro ? 0 : 1;
++		break;
++	}
++
++	mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, dev->hif2 ? rx_path_type : 0);
++	mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, rro_bypass);
++	mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, txfree_path);
+ 
+ 	if (dev->has_rro) {
+ 		u16 timeout;
+ 
+ 		timeout = mt76_rr(dev, MT_HW_REV) == MT_HW_REV1 ? 512 : 128;
+ 		mt7996_mcu_set_rro(dev, UNI_RRO_SET_FLUSH_TIMEOUT, timeout);
+-		mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 1);
+-		mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 0);
+-	} else {
+-		mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 3);
+-		mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 1);
+ 	}
+ 
+ 	mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
+@@ -620,9 +643,22 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 	if (phy)
+ 		return 0;
+ 
+-	if (is_mt7996(&dev->mt76) && band == MT_BAND2 && dev->hif2) {
+-		hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
+-		wed = &dev->mt76.mmio.wed_hif2;
++	if (is_mt7996(&dev->mt76) && dev->hif2) {
++		switch (dev->option_type) {
++		case 2:
++			/* eagle + 7988d */
++			if (band == MT_BAND1) {
++				hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
++				wed = &dev->mt76.mmio.wed_hif2;
++			}
++			break;
++		default:
++			if (band == MT_BAND2) {
++				hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
++				wed = &dev->mt76.mmio.wed_hif2;
++			}
++			break;
++		}
+ 	}
+ 
+ 	mphy = mt76_alloc_phy(&dev->mt76, sizeof(*phy), &mt7996_ops, band);
+@@ -1060,6 +1096,9 @@ int mt7996_get_chip_sku(struct mt7996_dev *dev)
+ static int mt7996_init_hardware(struct mt7996_dev *dev)
+ {
+ 	int ret, idx;
++	struct device_node *np = dev->mt76.dev->of_node;
++
++	of_property_read_u32(np, "option_type", &dev->option_type);
+ 
+ 	mt76_wr(dev, MT_INT_SOURCE_CSR, ~0);
+ 	if (is_mt7992(&dev->mt76)) {
+diff --git a/mt7996/main.c b/mt7996/main.c
+index d26c55ed..534a476a 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1610,8 +1610,19 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+ 
+-	if (phy != &dev->phy && phy->mt76->band_idx == MT_BAND2)
+-		wed = &dev->mt76.mmio.wed_hif2;
++	if (phy != &dev->phy && dev->hif2) {
++		switch (dev->option_type) {
++		case 2:
++			/* eagle + 7988d */
++			if (phy->mt76->band_idx == MT_BAND1)
++				wed = &dev->mt76.mmio.wed_hif2;
++			break;
++		default:
++			if (phy->mt76->band_idx == MT_BAND2)
++				wed = &dev->mt76.mmio.wed_hif2;
++			break;
++		}
++	}
+ 
+ 	if (!mtk_wed_device_active(wed))
+ 		return -ENODEV;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index fb1d1d84..d17b169a 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -8,6 +8,7 @@
+ 
+ #include <linux/interrupt.h>
+ #include <linux/ktime.h>
++#include <linux/pci.h>
+ #include "../mt76_connac.h"
+ #include "regs.h"
+ 
+@@ -350,6 +351,8 @@ struct mt7996_hif {
+ 	struct device *dev;
+ 	void __iomem *regs;
+ 	int irq;
++	enum pci_bus_speed speed;
++	enum pcie_link_width width;
+ };
+ 
+ struct mt7996_scs_ctrl {
+@@ -580,6 +583,8 @@ struct mt7996_dev {
+ 	u8 eeprom_mode;
+ 	u32 bg_nxt_freq;
+ 
++	u32 option_type;
++
+ 	bool ibf;
+ 	u8 fw_debug_wm;
+ 	u8 fw_debug_wa;
+diff --git a/mt7996/pci.c b/mt7996/pci.c
+index f0d3f199..24d69d4d 100644
+--- a/mt7996/pci.c
++++ b/mt7996/pci.c
+@@ -5,7 +5,6 @@
+ 
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+-#include <linux/pci.h>
+ 
+ #include "mt7996.h"
+ #include "mac.h"
+@@ -93,6 +92,7 @@ static int mt7996_pci_hif2_probe(struct pci_dev *pdev)
+ 	hif->dev = &pdev->dev;
+ 	hif->regs = pcim_iomap_table(pdev)[0];
+ 	hif->irq = pdev->irq;
++	pcie_bandwidth_available(pdev, NULL, &hif->speed, &hif->width);
+ 	spin_lock_bh(&hif_lock);
+ 	list_add(&hif->list, &hif_list);
+ 	spin_unlock_bh(&hif_lock);
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index a001d9fd..a0e4b3e1 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -435,6 +435,7 @@ enum offs_rev {
+ #define WF_WFDMA0_GLO_CFG_EXT0			MT_WFDMA0(0x2b0)
+ #define WF_WFDMA0_GLO_CFG_EXT0_RX_WB_RXD	BIT(18)
+ #define WF_WFDMA0_GLO_CFG_EXT0_WED_MERGE_MODE	BIT(14)
++#define WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK	GENMASK(27, 24)
+ 
+ #define WF_WFDMA0_GLO_CFG_EXT1			MT_WFDMA0(0x2b4)
+ #define WF_WFDMA0_GLO_CFG_EXT1_CALC_MODE	BIT(31)
+@@ -454,6 +455,7 @@ enum offs_rev {
+ 
+ #define MT_WFDMA_HOST_CONFIG			MT_WFDMA_EXT_CSR(0x30)
+ #define MT_WFDMA_HOST_CONFIG_PDMA_BAND		BIT(0)
++#define MT_WFDMA_HOST_CONFIG_BAND0_PCIE1	BIT(20)
+ #define MT_WFDMA_HOST_CONFIG_BAND1_PCIE1	BIT(21)
+ #define MT_WFDMA_HOST_CONFIG_BAND2_PCIE1	BIT(22)
+ 
+@@ -463,6 +465,9 @@ enum offs_rev {
+ #define MT_WFDMA_AXI_R2A_CTRL			MT_WFDMA_EXT_CSR(0x500)
+ #define MT_WFDMA_AXI_R2A_CTRL_OUTSTAND_MASK	GENMASK(4, 0)
+ 
++#define MT_WFDMA_AXI_R2A_CTRL2			MT_WFDMA_EXT_CSR(0x508)
++#define MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK	GENMASK(31, 28)
++
+ #define MT_PCIE_RECOG_ID			0xd7090
+ #define MT_PCIE_RECOG_ID_MASK			GENMASK(30, 0)
+ #define MT_PCIE_RECOG_ID_SEM			BIT(31)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0082-wifi-mt76-mt7996-implement-and-switch-to-hw-scan-cal.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0082-wifi-mt76-mt7996-implement-and-switch-to-hw-scan-cal.patch
deleted file mode 100644
index c76593a..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0082-wifi-mt76-mt7996-implement-and-switch-to-hw-scan-cal.patch
+++ /dev/null
@@ -1,451 +0,0 @@
-From 8a1c29f6a9b99d2f3d699fe6100704349406fc4b Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Fri, 3 Nov 2023 21:44:45 +0800
-Subject: [PATCH 082/116] wifi: mt76: mt7996: implement and switch to hw scan
- callbacks
-
-To support MLO, hw_scan callbacks are mandatory. However, the
-firmware of AP-segment doesn't support hw_scan commands, so we need
-to implement it in the driver.
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-If the cfg80211_scan_request contains an unicast BSSID, the probe
-request should be unicast.
-This works for ML probe request, which should be unicast.
-
-Co-developed-by: Peter Chiu <chui-hao.chiu@mediatek.com>
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
-Co-developed-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mac80211.c      |   3 +-
- mt76.h          |   2 +
- mt7996/init.c   |   5 ++
- mt7996/mac.c    | 141 ++++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/main.c   |  95 ++++++++++++++++++++++++++++----
- mt7996/mcu.c    |   3 +-
- mt7996/mt7996.h |  11 +++-
- 7 files changed, 248 insertions(+), 12 deletions(-)
-
-diff --git a/mac80211.c b/mac80211.c
-index 3c2c2af..0ad3f25 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -821,7 +821,7 @@ bool mt76_has_tx_pending(struct mt76_phy *phy)
- }
- EXPORT_SYMBOL_GPL(mt76_has_tx_pending);
- 
--static struct mt76_channel_state *
-+struct mt76_channel_state *
- mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c)
- {
- 	struct mt76_sband *msband;
-@@ -837,6 +837,7 @@ mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c)
- 	idx = c - &msband->sband.channels[0];
- 	return &msband->chan[idx];
- }
-+EXPORT_SYMBOL_GPL(mt76_channel_state);
- 
- void mt76_update_survey_active_time(struct mt76_phy *phy, ktime_t time)
- {
-diff --git a/mt76.h b/mt76.h
-index 28defd4..8ca7907 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -1477,6 +1477,8 @@ void mt76_release_buffered_frames(struct ieee80211_hw *hw,
- 				  enum ieee80211_frame_release_type reason,
- 				  bool more_data);
- bool mt76_has_tx_pending(struct mt76_phy *phy);
-+struct mt76_channel_state *
-+mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c);
- void mt76_set_channel(struct mt76_phy *phy);
- void mt76_update_survey(struct mt76_phy *phy);
- void mt76_update_survey_active_time(struct mt76_phy *phy, ktime_t time);
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 768979e..9523568 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -457,6 +457,9 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
- 
- 	wiphy->available_antennas_rx = phy->mt76->antenna_mask;
- 	wiphy->available_antennas_tx = phy->mt76->antenna_mask;
-+
-+	wiphy->max_scan_ssids = 4;
-+	wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
- }
- 
- static void
-@@ -677,6 +680,7 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
- 	mphy->dev->phys[band] = mphy;
- 
- 	INIT_DELAYED_WORK(&mphy->mac_work, mt7996_mac_work);
-+	INIT_DELAYED_WORK(&phy->scan_work, mt7996_scan_work);
- 
- 	ret = mt7996_eeprom_parse_hw_cap(dev, phy);
- 	if (ret)
-@@ -1574,6 +1578,7 @@ int mt7996_register_device(struct mt7996_dev *dev)
- 	dev->mt76.phy.priv = &dev->phy;
- 	INIT_WORK(&dev->rc_work, mt7996_mac_sta_rc_work);
- 	INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7996_mac_work);
-+	INIT_DELAYED_WORK(&dev->phy.scan_work, mt7996_scan_work);
- 	INIT_DELAYED_WORK(&dev->scs_work, mt7996_mcu_scs_sta_poll);
- 	INIT_LIST_HEAD(&dev->sta_rc_list);
- 	INIT_LIST_HEAD(&dev->twt_list);
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 8c44442..4e9dd2c 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -2693,3 +2693,144 @@ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
- 	dev->twt.table_mask &= ~BIT(flow->table_id);
- 	dev->twt.n_agrt--;
- }
-+
-+static void
-+mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
-+		       const u8 *dst)
-+{
-+	struct ieee80211_hw *hw = phy->mt76->hw;
-+	struct cfg80211_scan_request *req = phy->scan_req;
-+	struct ieee80211_vif *vif = phy->scan_vif;
-+	struct mt7996_vif *mvif;
-+	struct mt76_wcid *wcid;
-+	struct ieee80211_tx_info *info;
-+	struct sk_buff *skb;
-+
-+	if (!req || !vif)
-+		return;
-+
-+	mvif = (struct mt7996_vif *)vif->drv_priv;
-+	wcid = &mvif->sta.wcid;
-+
-+	skb = ieee80211_probereq_get(hw, vif->addr,
-+				     ssid->ssid, ssid->ssid_len, req->ie_len);
-+	if (!skb)
-+		return;
-+
-+	if (is_unicast_ether_addr(dst)) {
-+		struct ieee80211_hdr_3addr *hdr =
-+			(struct ieee80211_hdr_3addr *)skb->data;
-+		memcpy(hdr->addr1, dst, ETH_ALEN);
-+		memcpy(hdr->addr3, dst, ETH_ALEN);
-+	}
-+
-+	info = IEEE80211_SKB_CB(skb);
-+	if (req->no_cck)
-+		info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
-+
-+	if (req->ie_len)
-+		skb_put_data(skb, req->ie, req->ie_len);
-+
-+	skb_set_queue_mapping(skb, IEEE80211_AC_VO);
-+
-+	rcu_read_lock();
-+	if (!ieee80211_tx_prepare_skb(hw, vif, skb,
-+				      phy->scan_chan->band,
-+				      NULL)) {
-+		rcu_read_unlock();
-+		ieee80211_free_txskb(hw, skb);
-+		return;
-+	}
-+
-+	local_bh_disable();
-+	mt76_tx(phy->mt76, NULL, wcid, skb);
-+	local_bh_enable();
-+
-+	rcu_read_unlock();
-+}
-+
-+void mt7996_scan_complete(struct mt7996_phy *phy, bool aborted)
-+{
-+	struct cfg80211_scan_info info = {
-+		.aborted = aborted,
-+	};
-+
-+	ieee80211_scan_completed(phy->mt76->hw, &info);
-+	phy->scan_chan = NULL;
-+	phy->scan_req = NULL;
-+	phy->scan_vif = NULL;
-+	clear_bit(MT76_SCANNING, &phy->mt76->state);
-+}
-+
-+static void mt7996_scan_check_sta(void *data, struct ieee80211_sta *sta)
-+{
-+	bool *has_sta = data;
-+
-+	if (*has_sta)
-+		return;
-+	*has_sta = true;
-+}
-+
-+void mt7996_scan_work(struct work_struct *work)
-+{
-+	struct mt7996_phy *phy = container_of(work, struct mt7996_phy, scan_work.work);
-+	struct ieee80211_hw *hw = phy->mt76->hw;
-+	struct cfg80211_scan_request *req = phy->scan_req;
-+	struct cfg80211_chan_def chandef = {};
-+	int duration;
-+	bool has_sta = false, active_scan = false;
-+
-+	mutex_lock(&phy->dev->mt76.mutex);
-+	if (phy->scan_chan_idx >= req->n_channels) {
-+		mt7996_scan_complete(phy, false);
-+		mutex_unlock(&phy->dev->mt76.mutex);
-+
-+		mt7996_set_channel(phy, &hw->conf.chandef);
-+
-+		return;
-+	}
-+
-+	ieee80211_iterate_stations_atomic(hw, mt7996_scan_check_sta, &has_sta);
-+
-+	/* go back to operating channel */
-+	if (has_sta && phy->scan_chan) {
-+		phy->scan_chan = NULL;
-+		mutex_unlock(&phy->dev->mt76.mutex);
-+
-+		mt7996_set_channel(phy, &phy->mt76->chandef);
-+
-+		ieee80211_queue_delayed_work(hw, &phy->scan_work, HZ / 10);
-+
-+		return;
-+	}
-+
-+	wiphy_info(hw->wiphy, "hw scan %d MHz\n",
-+		   req->channels[phy->scan_chan_idx]->center_freq);
-+
-+	phy->scan_chan = req->channels[phy->scan_chan_idx++];
-+
-+	if (!req->n_ssids ||
-+	    (phy->scan_chan->flags & (IEEE80211_CHAN_NO_IR |
-+				      IEEE80211_CHAN_RADAR))) {
-+		duration = HZ / 9; /* ~110 ms */
-+	} else {
-+		duration = HZ / 16; /* ~60 ms */
-+		active_scan = true;
-+	}
-+
-+	cfg80211_chandef_create(&chandef, phy->scan_chan, NL80211_CHAN_HT20);
-+	mutex_unlock(&phy->dev->mt76.mutex);
-+
-+	mt7996_set_channel(phy, &chandef);
-+
-+	if (active_scan) {
-+		int i;
-+
-+		mutex_lock(&phy->dev->mt76.mutex);
-+		for (i = 0; i < req->n_ssids; i++)
-+			mt7996_scan_send_probe(phy, &req->ssids[i], req->bssid);
-+		mutex_unlock(&phy->dev->mt76.mutex);
-+	}
-+
-+	ieee80211_queue_delayed_work(hw, &phy->scan_work, duration);
-+}
-diff --git a/mt7996/main.c b/mt7996/main.c
-index c9e8108..5c1735e 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -312,6 +312,8 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
- 	int idx = msta->wcid.idx;
- 
-+	cancel_delayed_work_sync(&phy->scan_work);
-+
- 	mt7996_mcu_add_sta(dev, vif, NULL, false, false);
- 	mt7996_mcu_add_bss_info(phy, vif, false);
- 
-@@ -323,6 +325,10 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
- 	rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
- 
- 	mutex_lock(&dev->mt76.mutex);
-+
-+	if (test_bit(MT76_SCANNING, &phy->mt76->state))
-+		mt7996_scan_complete(phy, true);
-+
- 	dev->mt76.vif_mask &= ~BIT_ULL(mvif->mt76.idx);
- 	phy->omac_mask &= ~BIT_ULL(mvif->mt76.omac_idx);
- 	mutex_unlock(&dev->mt76.mutex);
-@@ -335,7 +341,33 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
- 	mt76_wcid_cleanup(&dev->mt76, &msta->wcid);
- }
- 
--int mt7996_set_channel(struct mt7996_phy *phy)
-+static void ___mt7996_set_channel(struct mt7996_phy *phy,
-+				 struct cfg80211_chan_def *chandef)
-+{
-+	struct mt76_dev *mdev = phy->mt76->dev;
-+	struct mt76_phy *mphy = phy->mt76;
-+	bool offchannel = phy->scan_chan != NULL;
-+	int timeout = HZ / 5;
-+
-+	wait_event_timeout(mdev->tx_wait, !mt76_has_tx_pending(mphy), timeout);
-+	mt76_update_survey(mphy);
-+
-+	if (mphy->chandef.chan->center_freq != chandef->chan->center_freq ||
-+	    mphy->chandef.width != chandef->width)
-+		mphy->dfs_state = MT_DFS_STATE_UNKNOWN;
-+
-+	mphy->chandef = *chandef;
-+	mphy->chan_state = mt76_channel_state(mphy, chandef->chan);
-+
-+	if (!offchannel)
-+		mphy->main_chan = chandef->chan;
-+
-+	if (chandef->chan != mphy->main_chan)
-+		memset(mphy->chan_state, 0, sizeof(*mphy->chan_state));
-+}
-+
-+static int __mt7996_set_channel(struct mt7996_phy *phy,
-+				struct cfg80211_chan_def *chandef)
- {
- 	struct mt7996_dev *dev = phy->dev;
- 	int ret;
-@@ -345,7 +377,7 @@ int mt7996_set_channel(struct mt7996_phy *phy)
- 	mutex_lock(&dev->mt76.mutex);
- 	set_bit(MT76_RESET, &phy->mt76->state);
- 
--	mt76_set_channel(phy->mt76);
-+	___mt7996_set_channel(phy, chandef);
- 
- 	if (dev->cal) {
- 		ret = mt7996_mcu_apply_tx_dpd(phy);
-@@ -381,6 +413,19 @@ out:
- 	return ret;
- }
- 
-+int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef)
-+{
-+	int ret;
-+
-+	ieee80211_stop_queues(phy->mt76->hw);
-+	ret = __mt7996_set_channel(phy, chandef);
-+	if (ret)
-+		return ret;
-+	ieee80211_wake_queues(phy->mt76->hw);
-+
-+	return 0;
-+}
-+
- static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
- 			  struct ieee80211_vif *vif, struct ieee80211_sta *sta,
- 			  struct ieee80211_key_conf *key)
-@@ -477,11 +522,7 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
- 		if (ret)
- 			return ret;
- 
--		ieee80211_stop_queues(hw);
--		ret = mt7996_set_channel(phy);
--		if (ret)
--			return ret;
--		ieee80211_wake_queues(hw);
-+		mt7996_set_channel(phy, &hw->conf.chandef);
- 	}
- 
- 	if (changed & (IEEE80211_CONF_CHANGE_POWER |
-@@ -1648,6 +1689,42 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
- 
- #endif
- 
-+static int
-+mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-+	       struct ieee80211_scan_request *hw_req)
-+{
-+	struct cfg80211_scan_request *req = &hw_req->req;
-+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+
-+	mutex_lock(&phy->dev->mt76.mutex);
-+	if (WARN_ON(phy->scan_req || phy->scan_chan)) {
-+		mutex_unlock(&phy->dev->mt76.mutex);
-+		return -EBUSY;
-+	}
-+
-+	set_bit(MT76_SCANNING, &phy->mt76->state);
-+	phy->scan_req = req;
-+	phy->scan_vif = vif;
-+	phy->scan_chan_idx = 0;
-+	mutex_unlock(&phy->dev->mt76.mutex);
-+
-+	ieee80211_queue_delayed_work(hw, &phy->scan_work, 0);
-+
-+	return 0;
-+}
-+
-+static void
-+mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
-+{
-+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+
-+	cancel_delayed_work_sync(&phy->scan_work);
-+
-+	mutex_lock(&phy->dev->mt76.mutex);
-+	mt7996_scan_complete(phy, true);
-+	mutex_unlock(&phy->dev->mt76.mutex);
-+}
-+
- const struct ieee80211_ops mt7996_ops = {
- 	.tx = mt7996_tx,
- 	.start = mt7996_start,
-@@ -1666,8 +1743,8 @@ const struct ieee80211_ops mt7996_ops = {
- 	.ampdu_action = mt7996_ampdu_action,
- 	.set_rts_threshold = mt7996_set_rts_threshold,
- 	.wake_tx_queue = mt76_wake_tx_queue,
--	.sw_scan_start = mt76_sw_scan,
--	.sw_scan_complete = mt76_sw_scan_complete,
-+	.hw_scan = mt7996_hw_scan,
-+	.cancel_hw_scan = mt7996_cancel_hw_scan,
- 	.release_buffered_frames = mt76_release_buffered_frames,
- 	.get_txpower = mt76_get_txpower,
- 	.channel_switch_beacon = mt7996_channel_switch_beacon,
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index e38a507..9f2d125 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -3775,7 +3775,8 @@ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag)
- 	if (phy->mt76->hw->conf.flags & IEEE80211_CONF_MONITOR)
- 		req.switch_reason = CH_SWITCH_NORMAL;
- 	else if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL ||
--		 phy->mt76->hw->conf.flags & IEEE80211_CONF_IDLE)
-+		 phy->mt76->hw->conf.flags & IEEE80211_CONF_IDLE ||
-+		 phy->scan_chan)
- 		req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD;
- 	else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef,
- 					  NL80211_IFTYPE_AP))
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index d03d3d9..fbf1e83 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -458,6 +458,13 @@ struct mt7996_phy {
- 	u8 pp_mode;
- 	u16 punct_bitmap;
- 
-+	/* for hw_scan */
-+	struct delayed_work scan_work;
-+	struct ieee80211_channel *scan_chan;
-+	struct cfg80211_scan_request *scan_req;
-+	struct ieee80211_vif *scan_vif;
-+	int scan_chan_idx;
-+
- 	struct mt7996_scs_ctrl scs_ctrl;
- 	u32 red_drop;
- 
-@@ -806,7 +813,7 @@ int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
- 			    struct ieee80211_he_obss_pd *he_obss_pd);
- int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- 			     struct ieee80211_sta *sta, bool changed);
--int mt7996_set_channel(struct mt7996_phy *phy);
-+int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef);
- int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag);
- int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif);
- int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
-@@ -964,6 +971,8 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
- 			 struct sk_buff *skb, u32 *info);
- bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len);
- void mt7996_stats_work(struct work_struct *work);
-+void mt7996_scan_work(struct work_struct *work);
-+void mt7996_scan_complete(struct mt7996_phy *phy, bool aborted);
- int mt76_dfs_start_rdd(struct mt7996_dev *dev, bool force);
- int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy);
- void mt7996_set_stream_he_eht_caps(struct mt7996_phy *phy);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0083-mtk-mt76-mt7996-support-enable-disable-thermal-prote.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0083-mtk-mt76-mt7996-support-enable-disable-thermal-prote.patch
new file mode 100644
index 0000000..f6331d2
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0083-mtk-mt76-mt7996-support-enable-disable-thermal-prote.patch
@@ -0,0 +1,115 @@
+From 49d072013ef5a88ca6040d18085885773967d5d4 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 27 Jul 2023 19:35:32 +0800
+Subject: [PATCH 083/199] mtk: mt76: mt7996: support enable/disable thermal
+ protection mechanism
+
+This commit adds a new debugfs thermal_enable to enable/disable thermal
+protection mechanism. The purpose of this commit is for autotest to
+verify thermal protection mechanism.
+
+[Usage]
+Enable thermal protection: echo 1 > thermal_enable
+Disable thermal protection: echo 0 > thermal_enable
+
+Please note that if you re-enable thermal protection mechanism, all the
+configuration values will be retained from the exising configuration,
+rather than using the default values.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt7996/main.c        |  1 +
+ mt7996/mt7996.h      |  1 +
+ mt7996/mtk_debugfs.c | 45 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 47 insertions(+)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 534a476a..8c93297a 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -91,6 +91,7 @@ int mt7996_run(struct ieee80211_hw *hw)
+ #ifdef CONFIG_MTK_DEBUG
+ 	phy->sr_enable = true;
+ 	phy->enhanced_sr_enable = true;
++	phy->thermal_protection_enable = true;
+ 
+ 	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
+ 						dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index d17b169a..6e96194b 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -489,6 +489,7 @@ struct mt7996_phy {
+ #ifdef CONFIG_MTK_DEBUG
+ 	bool sr_enable:1;
+ 	bool enhanced_sr_enable:1;
++	bool thermal_protection_enable:1;
+ #endif
+ };
+ 
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 275beb48..66ab74d2 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -3128,6 +3128,49 @@ mt7996_show_rro_mib(struct seq_file *s, void *data)
+ 	return 0;
+ }
+ 
++static int
++mt7996_thermal_enable_get(void *data, u64 *enable)
++{
++	struct mt7996_phy *phy = data;
++
++	*enable = phy->thermal_protection_enable;
++
++	return 0;
++}
++
++static int
++mt7996_thermal_enable_set(void *data, u64 action)
++{
++	struct mt7996_phy *phy = data;
++	int ret;
++	u8 throttling;
++
++	if (action > 1)
++		return -EINVAL;
++
++	if (!!action == phy->thermal_protection_enable)
++		return 0;
++
++	ret = mt7996_mcu_set_thermal_protect(phy, !!action);
++	if (ret)
++		return ret;
++
++	if (!!!action)
++		goto out;
++
++	throttling = MT7996_THERMAL_THROTTLE_MAX - phy->cdev_state;
++	ret = mt7996_mcu_set_thermal_throttling(phy, throttling);
++	if (ret)
++		return ret;
++
++out:
++	phy->thermal_protection_enable = !!action;
++
++	return 0;
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_thermal_enable, mt7996_thermal_enable_get,
++			 mt7996_thermal_enable_set, "%lld\n");
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+@@ -3234,6 +3277,8 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ 					    mt7996_show_rro_mib);
+ 	}
+ 
++	debugfs_create_file("thermal_enable", 0600, dir, phy, &fops_thermal_enable);
++
+ 	return 0;
+ }
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0083-wifi-mt76-mt7996-implement-and-switch-to-chanctx-cal.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0083-wifi-mt76-mt7996-implement-and-switch-to-chanctx-cal.patch
deleted file mode 100644
index 0e99f39..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0083-wifi-mt76-mt7996-implement-and-switch-to-chanctx-cal.patch
+++ /dev/null
@@ -1,383 +0,0 @@
-From d225140776409dcf5b526034682768803e551dbb Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Mon, 6 Nov 2023 16:17:11 +0800
-Subject: [PATCH 083/116] wifi: mt76: mt7996: implement and switch to chanctx
- callbacks
-
-To support MLO, chanctx callbacks are mandatory, since one VIF (MLD) could
-operate on multiple channels (links).
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Co-developed-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/init.c   |  22 ++++++
- mt7996/mac.c    |  10 ++-
- mt7996/main.c   | 178 ++++++++++++++++++++++++++++++++++++++++++++----
- mt7996/mcu.c    |   2 +-
- mt7996/mt7996.h |  17 +++++
- 5 files changed, 211 insertions(+), 18 deletions(-)
-
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 9523568..6eeec3b 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -382,6 +382,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
- 
- 	hw->sta_data_size = sizeof(struct mt7996_sta);
- 	hw->vif_data_size = sizeof(struct mt7996_vif);
-+	hw->chanctx_data_size = sizeof(struct mt7996_chanctx);
- 
- 	wiphy->iface_combinations = if_comb;
- 	wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
-@@ -417,6 +418,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
- 	ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD);
- 	ieee80211_hw_set(hw, WANT_MONITOR_VIF);
- 	ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
-+	ieee80211_hw_set(hw, CHANCTX_STA_CSA);
- 
- 	hw->max_tx_fragments = 4;
- 
-@@ -637,6 +639,22 @@ static int mt7996_vow_init(struct mt7996_phy *phy)
- 	return mt7996_mcu_set_vow_feature_ctrl(phy);
- }
- 
-+static void mt7996_init_chanctx(struct mt7996_phy *phy)
-+{
-+	struct ieee80211_supported_band *sband;
-+	struct ieee80211_channel *chan;
-+
-+	if (phy->mt76->band_idx == MT_BAND2)
-+		sband = &phy->mt76->sband_6g.sband;
-+	else if (phy->mt76->band_idx == MT_BAND1)
-+		sband = &phy->mt76->sband_5g.sband;
-+	else
-+		sband = &phy->mt76->sband_2g.sband;
-+
-+	chan = &sband->channels[0];
-+	cfg80211_chandef_create(&phy->mt76->chandef, chan, NL80211_CHAN_HT20);
-+}
-+
- static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
- 			       enum mt76_band_id band)
- {
-@@ -720,6 +738,8 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
- 	if (ret)
- 		goto error;
- 
-+	mt7996_init_chanctx(phy);
-+
- 	ret = mt7996_thermal_init(phy);
- 	if (ret)
- 		goto error;
-@@ -1609,6 +1629,8 @@ int mt7996_register_device(struct mt7996_dev *dev)
- 	if (ret)
- 		return ret;
- 
-+	mt7996_init_chanctx(&dev->phy);
-+
- 	ret = mt7996_thermal_init(&dev->phy);
- 	if (ret)
- 		return ret;
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 4e9dd2c..0879c77 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -2785,7 +2785,10 @@ void mt7996_scan_work(struct work_struct *work)
- 		mt7996_scan_complete(phy, false);
- 		mutex_unlock(&phy->dev->mt76.mutex);
- 
--		mt7996_set_channel(phy, &hw->conf.chandef);
-+		if (phy->chanctx)
-+			mt7996_set_channel(phy, &phy->chanctx->chandef);
-+		else
-+			mt7996_set_channel(phy, &phy->mt76->chandef);
- 
- 		return;
- 	}
-@@ -2797,7 +2800,10 @@ void mt7996_scan_work(struct work_struct *work)
- 		phy->scan_chan = NULL;
- 		mutex_unlock(&phy->dev->mt76.mutex);
- 
--		mt7996_set_channel(phy, &phy->mt76->chandef);
-+		if (phy->chanctx)
-+			mt7996_set_channel(phy, &phy->chanctx->chandef);
-+		else
-+			mt7996_set_channel(phy, &phy->mt76->chandef);
- 
- 		ieee80211_queue_delayed_work(hw, &phy->scan_work, HZ / 10);
- 
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 5c1735e..dff0f1f 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -76,6 +76,11 @@ int mt7996_run(struct ieee80211_hw *hw)
- 	if (ret)
- 		goto out;
- 
-+	/* set a parking channel */
-+	ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
-+	if (ret)
-+		goto out;
-+
- 	ret = mt7996_mcu_set_thermal_throttling(phy, MT7996_THERMAL_THROTTLE_MAX);
- 	if (ret)
- 		goto out;
-@@ -510,21 +515,6 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
- 	int ret;
- 
--	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
--		if (!mt76_testmode_enabled(phy->mt76) && !phy->mt76->test.bf_en) {
--			ret = mt7996_mcu_edcca_enable(phy, true);
--			if (ret)
--				return ret;
--		}
--
--		ret = mt7996_mcu_set_pp_en(phy, PP_USR_MODE,
--					   phy->mt76->chandef.punctured);
--		if (ret)
--			return ret;
--
--		mt7996_set_channel(phy, &hw->conf.chandef);
--	}
--
- 	if (changed & (IEEE80211_CONF_CHANGE_POWER |
- 		       IEEE80211_CONF_CHANGE_CHANNEL)) {
- 		ret = mt7996_mcu_set_txpower_sku(phy);
-@@ -1725,6 +1715,158 @@ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
- 	mutex_unlock(&phy->dev->mt76.mutex);
- }
- 
-+static int
-+mt7996_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
-+{
-+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
-+	int ret;
-+
-+	wiphy_info(hw->wiphy, "%s: add %u\n", __func__, conf->def.chan->hw_value);
-+	mutex_lock(&phy->dev->mt76.mutex);
-+
-+	if (ctx->assigned) {
-+		mutex_unlock(&phy->dev->mt76.mutex);
-+		return -ENOSPC;
-+	}
-+
-+	ctx->assigned = true;
-+	ctx->chandef = conf->def;
-+	ctx->phy = phy;
-+	if (phy->chanctx) {
-+		mutex_unlock(&phy->dev->mt76.mutex);
-+		return 0;
-+	}
-+
-+	phy->chanctx = ctx;
-+	mutex_unlock(&phy->dev->mt76.mutex);
-+
-+	if (!mt76_testmode_enabled(phy->mt76) && !phy->mt76->test.bf_en) {
-+		ret = mt7996_mcu_edcca_enable(phy, true);
-+		if (ret)
-+			return ret;
-+	}
-+
-+	ret = mt7996_mcu_set_pp_en(phy, PP_USR_MODE, ctx->chandef.punctured);
-+	if (ret)
-+		return ret;
-+
-+	return mt7996_set_channel(phy, &ctx->chandef);
-+}
-+
-+static void
-+mt7996_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
-+{
-+	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
-+	struct mt7996_phy *phy = ctx->phy;
-+
-+	wiphy_info(hw->wiphy, "%s: remove %u\n", __func__, conf->def.chan->hw_value);
-+	cancel_delayed_work_sync(&phy->scan_work);
-+	cancel_delayed_work_sync(&phy->mt76->mac_work);
-+
-+	mutex_lock(&phy->dev->mt76.mutex);
-+	ctx->assigned = false;
-+	if (ctx == phy->chanctx)
-+		phy->chanctx = NULL;
-+	mutex_unlock(&phy->dev->mt76.mutex);
-+}
-+
-+static void
-+mt7996_change_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf,
-+		      u32 changed)
-+{
-+	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
-+	struct mt7996_phy *phy = ctx->phy;
-+
-+	wiphy_info(hw->wiphy, "%s: change %u, 0x%x\n", __func__, conf->def.chan->hw_value, changed);
-+	if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH) {
-+		ctx->chandef = conf->def;
-+
-+		mt7996_set_channel(phy, &ctx->chandef);
-+	}
-+}
-+
-+static int
-+mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-+			  struct ieee80211_bss_conf *link_conf,
-+			  struct ieee80211_chanctx_conf *conf)
-+{
-+	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
-+	struct mt7996_phy *phy = ctx->phy;
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+
-+	wiphy_info(hw->wiphy, "Assign VIF (addr: %pM, type: %d, link_id: %d) to channel context: %d MHz\n",
-+		    vif->addr, vif->type, link_conf->link_id,
-+		    conf->def.chan->center_freq);
-+
-+	mutex_lock(&phy->dev->mt76.mutex);
-+
-+	mvif->chanctx = ctx;
-+	ctx->nbss_assigned++;
-+
-+	mutex_unlock(&phy->dev->mt76.mutex);
-+
-+	return 0;
-+}
-+
-+static void
-+mt7996_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-+			    struct ieee80211_bss_conf *link_conf,
-+			    struct ieee80211_chanctx_conf *conf)
-+{
-+	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
-+	struct mt7996_phy *phy = ctx->phy;
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+
-+	wiphy_info(hw->wiphy, "Remove VIF (addr: %pM, type: %d, link_id: %d) from channel context: %d MHz\n",
-+		   vif->addr, vif->type, link_conf->link_id,
-+		   conf->def.chan->center_freq);
-+	cancel_delayed_work_sync(&phy->scan_work);
-+
-+	mutex_lock(&phy->dev->mt76.mutex);
-+
-+	if (test_bit(MT76_SCANNING, &phy->mt76->state))
-+		mt7996_scan_complete(phy, true);
-+
-+	mvif->chanctx = NULL;
-+	ctx->nbss_assigned--;
-+
-+	mutex_unlock(&phy->dev->mt76.mutex);
-+}
-+
-+static int
-+mt7996_switch_vif_chanctx(struct ieee80211_hw *hw,
-+			  struct ieee80211_vif_chanctx_switch *vifs,
-+			  int n_vifs,
-+			  enum ieee80211_chanctx_switch_mode mode)
-+{
-+	struct mt7996_chanctx *old_ctx = mt7996_chanctx_get(vifs->old_ctx);
-+	struct mt7996_chanctx *new_ctx = mt7996_chanctx_get(vifs->new_ctx);
-+	struct mt7996_phy *phy = old_ctx->phy;
-+
-+	wiphy_info(hw->wiphy, "%s: old=%d, new=%d\n", __func__, vifs->old_ctx->def.chan->hw_value, vifs->new_ctx->def.chan->hw_value);
-+
-+	if (new_ctx->nbss_assigned && phy->chanctx == new_ctx) {
-+		new_ctx->nbss_assigned += n_vifs;
-+		return 0;
-+	}
-+
-+	if (WARN_ON(old_ctx != phy->chanctx))
-+		return -EINVAL;
-+
-+	mutex_lock(&phy->dev->mt76.mutex);
-+
-+	phy->chanctx = new_ctx;
-+	new_ctx->assigned = true;
-+	new_ctx->chandef = vifs->new_ctx->def;
-+	new_ctx->phy = phy;
-+	new_ctx->nbss_assigned += n_vifs;
-+
-+	mutex_unlock(&phy->dev->mt76.mutex);
-+
-+	return mt7996_set_channel(phy, &new_ctx->chandef);
-+}
-+
- const struct ieee80211_ops mt7996_ops = {
- 	.tx = mt7996_tx,
- 	.start = mt7996_start,
-@@ -1775,4 +1917,10 @@ const struct ieee80211_ops mt7996_ops = {
- 	.net_fill_forward_path = mt7996_net_fill_forward_path,
- 	.net_setup_tc = mt76_wed_net_setup_tc,
- #endif
-+	.add_chanctx = mt7996_add_chanctx,
-+	.remove_chanctx = mt7996_remove_chanctx,
-+	.change_chanctx = mt7996_change_chanctx,
-+	.assign_vif_chanctx = mt7996_assign_vif_chanctx,
-+	.unassign_vif_chanctx = mt7996_unassign_vif_chanctx,
-+	.switch_vif_chanctx = mt7996_switch_vif_chanctx,
- };
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 9f2d125..5319cc3 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -5309,7 +5309,7 @@ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap)
- 		.bitmap = cpu_to_le16(bitmap),
- 	};
- 
--	if (phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ ||
-+	if (phy->chanctx->chandef.chan->band == NL80211_BAND_2GHZ ||
- 	    mode > PP_USR_MODE)
- 		return 0;
- 
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index fbf1e83..84aafb4 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -332,6 +332,8 @@ struct mt7996_vif {
- 
- 	struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
- 	struct cfg80211_bitrate_mask bitrate_mask;
-+
-+	struct mt7996_chanctx *chanctx;
- };
- 
- /* crash-dump */
-@@ -421,6 +423,14 @@ struct mt7996_rro_ba_session {
- 	u32 last_in_rxtime :12;
- };
- 
-+struct mt7996_chanctx {
-+	struct cfg80211_chan_def chandef;
-+	struct mt7996_phy *phy;
-+
-+	bool assigned;
-+	u8 nbss_assigned;
-+};
-+
- struct mt7996_phy {
- 	struct mt76_phy *mt76;
- 	struct mt7996_dev *dev;
-@@ -464,6 +474,7 @@ struct mt7996_phy {
- 	struct cfg80211_scan_request *scan_req;
- 	struct ieee80211_vif *scan_vif;
- 	int scan_chan_idx;
-+	struct mt7996_chanctx *chanctx;
- 
- 	struct mt7996_scs_ctrl scs_ctrl;
- 	u32 red_drop;
-@@ -752,6 +763,12 @@ mt7996_get_background_radar_cap(struct mt7996_dev *dev)
- 	return 1;
- }
- 
-+static inline struct mt7996_chanctx *
-+mt7996_chanctx_get(struct ieee80211_chanctx_conf *ctx)
-+{
-+	return (struct mt7996_chanctx *)&ctx->drv_priv;
-+}
-+
- extern const struct ieee80211_ops mt7996_ops;
- extern struct pci_driver mt7996_pci_driver;
- extern struct pci_driver mt7996_hif_driver;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0084-mtk-mt76-mt7996-support-thermal-recal-debug-command.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0084-mtk-mt76-mt7996-support-thermal-recal-debug-command.patch
new file mode 100644
index 0000000..e560caf
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0084-mtk-mt76-mt7996-support-thermal-recal-debug-command.patch
@@ -0,0 +1,116 @@
+From 857348ab99cd5ba83f73c4da02c70f2004b6f10b Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 4 Jan 2024 19:53:37 +0800
+Subject: [PATCH 084/199] mtk: mt76: mt7996: support thermal recal debug
+ command
+
+Add support thermal recal debug command.
+
+Usage:
+$ echo val > debugfs/thermal_recal
+
+The val can be the following values:
+0 = disable
+1 = enable
+2 = manual trigger
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt76_connac_mcu.h    |  1 +
+ mt7996/mt7996.h      |  1 +
+ mt7996/mtk_debugfs.c | 17 +++++++++++++++++
+ mt7996/mtk_mcu.c     | 21 +++++++++++++++++++++
+ 4 files changed, 40 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index c8eedf36..1589a716 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1297,6 +1297,7 @@ enum {
+ 	MCU_UNI_CMD_TESTMODE_TRX_PARAM = 0x42,
+ 	MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
+ 	MCU_UNI_CMD_PRECAL_RESULT = 0x47,
++	MCU_UNI_CMD_THERMAL_CAL = 0x4c,
+ 	MCU_UNI_CMD_RRO = 0x57,
+ 	MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
+ 	MCU_UNI_CMD_PER_STA_INFO = 0x6d,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 6e96194b..0e6b7595 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1033,6 +1033,7 @@ void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type);
+ void mt7996_tm_update_channel(struct mt7996_phy *phy);
+ 
+ int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val);
++int mt7996_mcu_thermal_debug(struct mt7996_dev *dev, u8 mode, u8 action);
+ #endif
+ 
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 66ab74d2..cc1224ed 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -3171,6 +3171,22 @@ out:
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_thermal_enable, mt7996_thermal_enable_get,
+ 			 mt7996_thermal_enable_set, "%lld\n");
+ 
++static int
++mt7996_thermal_recal_set(void *data, u64 val)
++{
++#define THERMAL_DEBUG_OPERATION_MANUAL_TRIGGER 2
++#define THERMAL_DEBUG_MODE_RECAL 1
++	struct mt7996_dev *dev = data;
++
++	if (val > THERMAL_DEBUG_OPERATION_MANUAL_TRIGGER)
++		return -EINVAL;
++
++	return mt7996_mcu_thermal_debug(dev, THERMAL_DEBUG_MODE_RECAL, val);
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_thermal_recal, NULL,
++			 mt7996_thermal_recal_set, "%llu\n");
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+@@ -3278,6 +3294,7 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ 	}
+ 
+ 	debugfs_create_file("thermal_enable", 0600, dir, phy, &fops_thermal_enable);
++	debugfs_create_file("thermal_recal", 0200, dir, dev, &fops_thermal_recal);
+ 
+ 	return 0;
+ }
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index 967ee874..809181e0 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -1343,4 +1343,25 @@ int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val)
+ 				 sizeof(req), true);
+ }
+ 
++int mt7996_mcu_thermal_debug(struct mt7996_dev *dev, u8 mode, u8 action)
++{
++	struct {
++		u8 __rsv1[4];
++
++		__le16 tag;
++		__le16 len;
++
++		u8 mode;
++		u8 action;
++		u8 __rsv2[2];
++	} __packed req = {
++		.tag = cpu_to_le16(mode),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.mode = mode,
++		.action = action,
++	};
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(THERMAL_CAL), &req,
++	                         sizeof(req), true);
++}
+ #endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0084-wifi-mt76-mt7996-use-.sta_state-to-replace-.sta_add-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0084-wifi-mt76-mt7996-use-.sta_state-to-replace-.sta_add-.patch
deleted file mode 100644
index cc84e17..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0084-wifi-mt76-mt7996-use-.sta_state-to-replace-.sta_add-.patch
+++ /dev/null
@@ -1,146 +0,0 @@
-From 21ea1ffbe885bbaba0266f0b835cf58574cecc5f Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Wed, 8 Nov 2023 18:52:26 +0800
-Subject: [PATCH 084/116] wifi: mt76: mt7996: use .sta_state to replace
- .sta_add and .sta_remove
-
-MAC80211 mostly uses MLD address through TX path, and leaves the header
-translation procedure to driver. To perform address translation, driver
-needs to get the setup link address at early stage (i.e., state 1),
-however, when using .sta_add/.sta_remove callbacks, driver can only get
-the link address at state 3, so it's necessary to switch to .sta_state
-callback to meet this requirement.
-
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/main.c   | 53 ++++++++++++++++++++-----------------------------
- mt7996/mmio.c   |  1 +
- mt7996/mt7996.h |  2 ++
- 3 files changed, 24 insertions(+), 32 deletions(-)
-
-diff --git a/mt7996/main.c b/mt7996/main.c
-index dff0f1f..9eb4c5b 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -784,7 +784,7 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	u8 band_idx = mvif->phy->mt76->band_idx;
--	int ret, idx;
-+	int idx;
- 
- #ifdef CONFIG_MTK_VENDOR
- 	struct mt7996_phy *phy = &dev->phy;
-@@ -802,23 +802,10 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 	msta->wcid.phy_idx = band_idx;
- 	msta->wcid.tx_info |= MT_WCID_TX_INFO_SET;
- 
--	ewma_avg_signal_init(&msta->avg_ack_signal);
--
--	mt7996_mac_wtbl_update(dev, idx,
--			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
--
- #ifdef CONFIG_MTK_VENDOR
- 	mt7996_vendor_amnt_sta_remove(mvif->phy, sta);
- #endif
- 
--	ret = mt7996_mcu_add_sta(dev, vif, sta, true, true);
--	if (ret)
--		return ret;
--
--	ret = mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
--	if (ret)
--		return ret;
--
- #ifdef CONFIG_MTK_VENDOR
- 	switch (band_idx) {
- 	case MT_BAND1:
-@@ -839,6 +826,25 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 	return 0;
- }
- 
-+void mt7996_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
-+			  struct ieee80211_sta *sta)
-+{
-+	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
-+	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+
-+	mutex_lock(&dev->mt76.mutex);
-+
-+	mt7996_mac_wtbl_update(dev, msta->wcid.idx,
-+			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
-+
-+	mt7996_mcu_add_sta(dev, vif, sta, true, true);
-+	mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
-+
-+	ewma_avg_signal_init(&msta->avg_ack_signal);
-+
-+	mutex_unlock(&dev->mt76.mutex);
-+}
-+
- void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 			   struct ieee80211_sta *sta)
- {
-@@ -958,22 +964,6 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	return ret;
- }
- 
--static int
--mt7996_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
--	       struct ieee80211_sta *sta)
--{
--	return mt76_sta_state(hw, vif, sta, IEEE80211_STA_NOTEXIST,
--			      IEEE80211_STA_NONE);
--}
--
--static int
--mt7996_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
--		  struct ieee80211_sta *sta)
--{
--	return mt76_sta_state(hw, vif, sta, IEEE80211_STA_NONE,
--			      IEEE80211_STA_NOTEXIST);
--}
--
- static int
- mt7996_get_stats(struct ieee80211_hw *hw,
- 		 struct ieee80211_low_level_stats *stats)
-@@ -1877,8 +1867,7 @@ const struct ieee80211_ops mt7996_ops = {
- 	.conf_tx = mt7996_conf_tx,
- 	.configure_filter = mt7996_configure_filter,
- 	.bss_info_changed = mt7996_bss_info_changed,
--	.sta_add = mt7996_sta_add,
--	.sta_remove = mt7996_sta_remove,
-+	.sta_state = mt76_sta_state,
- 	.sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
- 	.sta_rc_update = mt7996_sta_rc_update,
- 	.set_key = mt7996_set_key,
-diff --git a/mt7996/mmio.c b/mt7996/mmio.c
-index 6028182..6abbcb6 100644
---- a/mt7996/mmio.c
-+++ b/mt7996/mmio.c
-@@ -655,6 +655,7 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
- 		.rx_check = mt7996_rx_check,
- 		.rx_poll_complete = mt7996_rx_poll_complete,
- 		.sta_add = mt7996_mac_sta_add,
-+		.sta_assoc = mt7996_mac_sta_assoc,
- 		.sta_remove = mt7996_mac_sta_remove,
- 		.update_survey = mt7996_update_channel,
- 	};
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 84aafb4..2b266d1 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -966,6 +966,8 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
- void mt7996_mac_set_coverage_class(struct mt7996_phy *phy);
- int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 		       struct ieee80211_sta *sta);
-+void mt7996_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
-+			  struct ieee80211_sta *sta);
- void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 			   struct ieee80211_sta *sta);
- void mt7996_mac_work(struct work_struct *work);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0085-mtk-mt76-mt7996-add-kite-two-pcie-with-two-wed-suppo.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0085-mtk-mt76-mt7996-add-kite-two-pcie-with-two-wed-suppo.patch
new file mode 100644
index 0000000..ddb26a5
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0085-mtk-mt76-mt7996-add-kite-two-pcie-with-two-wed-suppo.patch
@@ -0,0 +1,326 @@
+From 343d81213790ba2a9928ee104067f5a18bbeacca Mon Sep 17 00:00:00 2001
+From: Rex Lu <rex.lu@mediatek.com>
+Date: Tue, 19 Mar 2024 13:16:12 +0800
+Subject: [PATCH 085/199] mtk: mt76: mt7996: add kite two pcie with two wed
+ support
+
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+---
+ mt7996/dma.c  | 68 ++++++++++++++++++++++++++++++++++++++-------------
+ mt7996/init.c | 54 +++++++++++++++++++++++-----------------
+ mt7996/main.c |  5 ++--
+ mt7996/mmio.c | 15 ++++++++++--
+ mt7996/pci.c  |  5 ++--
+ mt7996/regs.h |  1 +
+ 6 files changed, 101 insertions(+), 47 deletions(-)
+
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index 3dc0e8a1..a2490fa7 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -108,8 +108,8 @@ static void mt7996_dma_config(struct mt7996_dev *dev)
+ 	}
+ 
+ 	/* data tx queue */
+-	TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
+ 	if (is_mt7996(&dev->mt76)) {
++		TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
+ 		if (dev->hif2) {
+ 			if (dev->option_type == 2) {
+ 				/*  bn1:ring21 bn2:ring19 */
+@@ -125,7 +125,15 @@ static void mt7996_dma_config(struct mt7996_dev *dev)
+ 			TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
+ 		}
+ 	} else {
+-		TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
++		if (dev->hif2) {
++			/*  bn0:ring18 bn1:ring21 */
++			TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
++			TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND2, MT7996_TXQ_BAND2);
++		} else {
++			/* single pcie bn0:ring18 bn1:ring19 */
++			TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
++			TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
++		}
+ 	}
+ 
+ 	/* mcu tx queue */
+@@ -285,8 +293,11 @@ void mt7996_dma_start(struct mt7996_dev *dev, bool reset, bool wed_reset)
+ 	if (mt7996_band_valid(dev, MT_BAND0))
+ 		irq_mask |= MT_INT_BAND0_RX_DONE;
+ 
+-	if (mt7996_band_valid(dev, MT_BAND1))
++	if (mt7996_band_valid(dev, MT_BAND1)) {
+ 		irq_mask |= MT_INT_BAND1_RX_DONE;
++		if (is_mt7992(&dev->mt76) && dev->hif2)
++			irq_mask |= MT_INT_RX_TXFREE_BAND1_EXT;
++	}
+ 
+ 	if (mt7996_band_valid(dev, MT_BAND2))
+ 		irq_mask |= MT_INT_BAND2_RX_DONE;
+@@ -379,27 +390,46 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ 			   MT_WFDMA_HOST_CONFIG_BAND1_PCIE1 |
+ 			   MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
+ 
+-		if (dev->option_type == 2)
+-			mt76_set(dev, MT_WFDMA_HOST_CONFIG,
+-				 MT_WFDMA_HOST_CONFIG_BAND0_PCIE1 |
+-				 MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
+-		else
+-			mt76_set(dev, MT_WFDMA_HOST_CONFIG,
+-				 MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
+-
+-		if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
+-		    is_mt7992(&dev->mt76)) {
++		switch (dev->option_type) {
++		case 2:
++			/* eagle + 7988d */
++			if (is_mt7996(&dev->mt76))
++				mt76_set(dev, MT_WFDMA_HOST_CONFIG,
++					 MT_WFDMA_HOST_CONFIG_BAND0_PCIE1 |
++					 MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
++			else
++				mt76_set(dev, MT_WFDMA_HOST_CONFIG,
++					 MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
++			break;
++		case 3:
+ 			mt76_set(dev, MT_WFDMA_HOST_CONFIG,
+-				 MT_WFDMA_HOST_CONFIG_PDMA_BAND |
+-				 MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
++				 MT_WFDMA_HOST_CONFIG_BAND0_PCIE1);
++
++			break;
++		default:
++			if (is_mt7996(&dev->mt76))
++				mt76_set(dev, MT_WFDMA_HOST_CONFIG,
++					 MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
++			else
++				mt76_set(dev, MT_WFDMA_HOST_CONFIG,
++					 MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
++			break;
+ 		}
+ 
+ 		/* AXI read outstanding number */
+ 		mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL,
+ 			 MT_WFDMA_AXI_R2A_CTRL_OUTSTAND_MASK, 0x14);
+ 
+-		if (dev->hif2->speed < PCIE_SPEED_8_0GT ||
+-		    (dev->hif2->speed == PCIE_SPEED_8_0GT && dev->hif2->width < 2)) {
++		if (dev->hif2->speed < PCIE_SPEED_5_0GT ||
++		    (dev->hif2->speed == PCIE_SPEED_5_0GT && dev->hif2->width < 2)) {
++			mt76_rmw(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs,
++				 WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK,
++				 FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, 0x1));
++			mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL2,
++				 MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK,
++				 FIELD_PREP(MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK, 0x1));
++		} else if (dev->hif2->speed < PCIE_SPEED_8_0GT ||
++			   (dev->hif2->speed == PCIE_SPEED_8_0GT && dev->hif2->width < 2)) {
+ 			mt76_rmw(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs,
+ 				 WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK,
+ 				 FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, 0x3));
+@@ -648,6 +678,10 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 
+ 		/* tx free notify event from WA for mt7992 band1 */
+ 		rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND1_WA) + hif1_ofs;
++		if (mtk_wed_device_active(wed_hif2)) {
++			dev->mt76.q_rx[MT_RXQ_BAND1_WA].flags = MT_WED_Q_TXFREE;
++			dev->mt76.q_rx[MT_RXQ_BAND1_WA].wed = wed_hif2;
++		}
+ 		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND1_WA],
+ 				       MT_RXQ_ID(MT_RXQ_BAND1_WA),
+ 				       MT7996_RX_MCU_RING_SIZE,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 910ffccf..1e4e6e78 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -527,39 +527,46 @@ void mt7996_mac_init(struct mt7996_dev *dev)
+ 	}
+ 
+ 	/* rro module init */
++	rx_path_type = is_mt7996(&dev->mt76) ? 2 : 7;
++	rro_bypass = is_mt7996(&dev->mt76) ? 1 : 2;
++	txfree_path = is_mt7996(&dev->mt76) ? 0: 1;
++
+ 	switch (dev->option_type) {
+ 	case 2:
+-		/* eagle + 7988d */
+-		rx_path_type = 3;
+-		rro_bypass = dev->has_rro ? 1 : 3;
+-		txfree_path = dev->has_rro ? 0 : 1;
++		if (is_mt7996(&dev->mt76)) {
++			/* eagle + 7988d */
++			rx_path_type = 3;
++			rro_bypass = 1;
++			txfree_path = 0;
++		}
+ 		break;
+ 	case 3:
+-		/* eagle + Airoha */
+-		rx_path_type = 6;
+-		rro_bypass = dev->has_rro ? 1 : 3;
+-		txfree_path = dev->has_rro ? 0 : 1;
++		/* Airoha */
++		if (is_mt7996(&dev->mt76)) {
++			rx_path_type = 6;
++			rro_bypass = 1;
++			txfree_path = 0;
++		} else {
++			rx_path_type = 8;
++			rro_bypass = 2;
++			txfree_path = 1;
++		}
+ 		break;
+ 	case 4:
+-		/* Bollinger */
+-		rx_path_type = 2;
+-		rro_bypass = dev->has_rro ? 1 : 3;
+-		txfree_path = dev->has_rro ? 0 : 1;
++		if (is_mt7996(&dev->mt76)) {
++			/* Bollinger */
++			rx_path_type = 2;
++			rro_bypass = 1;
++			txfree_path = 0;
++		}
+ 		break;
+ 	default:
+-		if (is_mt7996(&dev->mt76))
+-			rx_path_type = 2;
+-		else
+-			rx_path_type = 7;
+-
+-		rro_bypass = dev->has_rro ? 1 : 3;
+-		txfree_path = dev->has_rro ? 0 : 1;
+ 		break;
+ 	}
+ 
+ 	mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, dev->hif2 ? rx_path_type : 0);
+-	mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, rro_bypass);
+-	mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, txfree_path);
++	mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, dev->has_rro ? rro_bypass : 3);
++	mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, dev->has_rro ? txfree_path : 1);
+ 
+ 	if (dev->has_rro) {
+ 		u16 timeout;
+@@ -643,7 +650,7 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 	if (phy)
+ 		return 0;
+ 
+-	if (is_mt7996(&dev->mt76) && dev->hif2) {
++	if (dev->hif2) {
+ 		switch (dev->option_type) {
+ 		case 2:
+ 			/* eagle + 7988d */
+@@ -653,7 +660,8 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 			}
+ 			break;
+ 		default:
+-			if (band == MT_BAND2) {
++			if ((is_mt7996(&dev->mt76) && band == MT_BAND2) ||
++			    (is_mt7992(&dev->mt76) && band == MT_BAND1)) {
+ 				hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
+ 				wed = &dev->mt76.mmio.wed_hif2;
+ 			}
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 8c93297a..a37aac43 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1611,7 +1611,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+ 
+-	if (phy != &dev->phy && dev->hif2) {
++	if (dev->hif2) {
+ 		switch (dev->option_type) {
+ 		case 2:
+ 			/* eagle + 7988d */
+@@ -1619,7 +1619,8 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ 				wed = &dev->mt76.mmio.wed_hif2;
+ 			break;
+ 		default:
+-			if (phy->mt76->band_idx == MT_BAND2)
++			if ((is_mt7996(&dev->mt76) && phy->mt76->band_idx == MT_BAND2) ||
++			    (is_mt7992(&dev->mt76) && phy->mt76->band_idx == MT_BAND1))
+ 				wed = &dev->mt76.mmio.wed_hif2;
+ 			break;
+ 		}
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index 4baae0e9..d4739fea 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -336,10 +336,16 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ 					     MT_TXQ_RING_BASE(0) +
+ 					     MT7996_TXQ_BAND2 * MT_RING_SIZE;
+ 		if (dev->has_rro) {
++			u8 rxq_id = is_mt7996(&dev->mt76) ?
++				    MT7996_RXQ_TXFREE2 : MT7996_RXQ_MCU_WA_EXT;
++
+ 			wed->wlan.wpdma_txfree = wed->wlan.phy_base + hif1_ofs +
+ 						 MT_RXQ_RING_BASE(0) +
+-						 MT7996_RXQ_TXFREE2 * MT_RING_SIZE;
+-			wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_EXT) - 1;
++						 rxq_id * MT_RING_SIZE;
++			if (is_mt7996(&dev->mt76))
++				wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_EXT) - 1;
++			else
++				wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_BAND1_EXT) - 1;
+ 		} else {
+ 			wed->wlan.wpdma_txfree = wed->wlan.phy_base + hif1_ofs +
+ 						 MT_RXQ_RING_BASE(0) +
+@@ -423,6 +429,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ 						 MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE;
+ 		}
+ 		dev->mt76.rx_token_size = MT7996_TOKEN_SIZE + wed->wlan.rx_npkt;
++		if(dev->hif2 && is_mt7992(&dev->mt76))
++			wed->wlan.id = 0x7992;
+ 	}
+ 
+ 	wed->wlan.nbuf = MT7996_TOKEN_SIZE;
+@@ -553,6 +561,9 @@ static void mt7996_irq_tasklet(struct tasklet_struct *t)
+ 
+ 		if (intr1 & MT_INT_RX_DONE_BAND2_EXT)
+ 			napi_schedule(&dev->mt76.napi[MT_RXQ_BAND2]);
++
++		if (is_mt7992(&dev->mt76) && (intr1 & MT_INT_RX_TXFREE_BAND1_EXT))
++			napi_schedule(&dev->mt76.napi[MT_RXQ_BAND1_WA]);
+ 	}
+ 
+ 	if (mtk_wed_device_active(wed)) {
+diff --git a/mt7996/pci.c b/mt7996/pci.c
+index 24d69d4d..382b6a89 100644
+--- a/mt7996/pci.c
++++ b/mt7996/pci.c
+@@ -110,7 +110,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ 	int irq, ret;
+ 	struct mt76_dev *mdev;
+ 
+-	hif2_enable |= (id->device == 0x7990 || id->device == 0x7991);
++	hif2_enable |= (id->device == 0x7990 || id->device == 0x7991 || id->device == 0x799a);
+ 
+ 	ret = pcim_enable_device(pdev);
+ 	if (ret)
+@@ -171,8 +171,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ 		hif2_dev = container_of(hif2->dev, struct pci_dev, dev);
+ 		ret = 0;
+ 
+-		if (is_mt7996(&dev->mt76))
+-			ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &irq);
++		ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &irq);
+ 
+ 		if (ret < 0)
+ 			goto free_wed_or_irq_vector;
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index a0e4b3e1..e1893517 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -525,6 +525,7 @@ enum offs_rev {
+ #define MT_INT_RX_TXFREE_MAIN			BIT(17)
+ #define MT_INT_RX_TXFREE_BAND1			BIT(15)
+ #define MT_INT_RX_TXFREE_TRI			BIT(15)
++#define MT_INT_RX_TXFREE_BAND1_EXT		BIT(19) /* for mt7992 two PCIE*/
+ #define MT_INT_RX_DONE_BAND2_EXT		BIT(23)
+ #define MT_INT_RX_TXFREE_EXT			BIT(26)
+ #define MT_INT_MCU_CMD				BIT(29)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0085-wifi-mt76-mt7996-switch-to-per-link-data-structure-o.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0085-wifi-mt76-mt7996-switch-to-per-link-data-structure-o.patch
deleted file mode 100644
index f035710..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0085-wifi-mt76-mt7996-switch-to-per-link-data-structure-o.patch
+++ /dev/null
@@ -1,2320 +0,0 @@
-From a8c22741f0614f90922e3dcac3a94666e29e4682 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Fri, 24 Nov 2023 11:31:55 +0800
-Subject: [PATCH 085/116] wifi: mt76: mt7996: switch to per-link data structure
- of vif
-
-Introduce struct mt7996_bss_conf, data structure for per-link BSS.
-Note that mt7996_vif now represents a legacy or MLD device.
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/debugfs.c  |  10 +-
- mt7996/init.c     |   4 +-
- mt7996/mac.c      |  25 ++-
- mt7996/main.c     | 269 ++++++++++++++++++-----------
- mt7996/mcu.c      | 429 +++++++++++++++++++++++-----------------------
- mt7996/mt7996.h   |  72 +++++---
- mt7996/testmode.c |  17 +-
- 7 files changed, 463 insertions(+), 363 deletions(-)
-
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index 8377586..06015d6 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -739,7 +739,7 @@ static void
- mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta)
- {
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
--	struct mt7996_dev *dev = msta->vif->phy->dev;
-+	struct mt7996_dev *dev = msta->vif->deflink.phy->dev;
- 	struct seq_file *s = data;
- 	u8 ac;
- 
-@@ -759,7 +759,7 @@ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta)
- 				      GENMASK(11, 0));
- 		seq_printf(s, "\tSTA %pM wcid %d: AC%d%d queued:%d\n",
- 			   sta->addr, msta->wcid.idx,
--			   msta->vif->mt76.wmm_idx, ac, qlen);
-+			   msta->vif->deflink.mt76.wmm_idx, ac, qlen);
- 	}
- }
- 
-@@ -1023,7 +1023,7 @@ mt7996_atf_enable_set(void *data, u64 val)
- 	int ret;
- 
- 	vow->max_deficit = val ? 64 : 1;
--	ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
-+	ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
- 	if (ret)
- 		return ret;
- 
-@@ -1055,7 +1055,7 @@ mt7996_airtime_read(struct seq_file *s, void *data)
- 
- 		msta = container_of(wcid, struct mt7996_sta, wcid);
- 		sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
--		vif = &msta->vif->mt76;
-+		vif = &msta->vif->deflink.mt76;
- 		stats = &wcid->stats;
- 
- 		seq_printf(s, "%pM WCID: %hu BandIdx: %hhu OmacIdx: 0x%hhx\t"
-@@ -1230,7 +1230,7 @@ static ssize_t mt7996_sta_fixed_rate_set(struct file *file,
- #define LONG_PREAMBLE 1
- 	struct ieee80211_sta *sta = file->private_data;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
--	struct mt7996_dev *dev = msta->vif->phy->dev;
-+	struct mt7996_dev *dev = msta->vif->deflink.phy->dev;
- 	struct ra_rate phy = {};
- 	char buf[100];
- 	int ret;
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 6eeec3b..381e129 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -628,11 +628,11 @@ static int mt7996_vow_init(struct mt7996_phy *phy)
- 	vow->drr_quantum[6] = VOW_DRR_QUANTUM_L6;
- 	vow->drr_quantum[7] = VOW_DRR_QUANTUM_L7;
- 
--	ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
-+	ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
- 	if (ret)
- 		return ret;
- 
--	ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL);
-+	ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, NULL, VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL);
- 	if (ret)
- 		return ret;
- 
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 0879c77..63b16f9 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -897,8 +897,9 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
- 
- 	if (vif) {
- 		struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+		struct mt7996_bss_conf *mconf = &mvif->deflink;
- 
--		txp->fw.bss_idx = mvif->mt76.idx;
-+		txp->fw.bss_idx = mconf->mt76.idx;
- 	}
- 
- 	txp->fw.token = cpu_to_le16(id);
-@@ -1518,12 +1519,15 @@ static void
- mt7996_update_vif_beacon(void *priv, u8 *mac, struct ieee80211_vif *vif)
- {
- 	struct ieee80211_hw *hw = priv;
-+	struct ieee80211_bss_conf *conf = &vif->bss_conf;
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_bss_conf *mconf = &mvif->deflink;
- 
- 	switch (vif->type) {
- 	case NL80211_IFTYPE_MESH_POINT:
- 	case NL80211_IFTYPE_ADHOC:
- 	case NL80211_IFTYPE_AP:
--		mt7996_mcu_add_beacon(hw, vif, vif->bss_conf.enable_beacon);
-+		mt7996_mcu_add_beacon(hw, conf, mconf, conf->enable_beacon);
- 		break;
- 	default:
- 		break;
-@@ -2237,6 +2241,8 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
- 	struct mt7996_dev *dev = container_of(work, struct mt7996_dev, rc_work);
- 	struct ieee80211_sta *sta;
- 	struct ieee80211_vif *vif;
-+	struct ieee80211_bss_conf *conf;
-+	struct mt7996_bss_conf *mconf;
- 	struct mt7996_sta *msta;
- 	u32 changed;
- 	LIST_HEAD(list);
-@@ -2253,14 +2259,16 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
- 
- 		sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
- 		vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv);
-+		conf = &vif->bss_conf;
-+		mconf = &msta->vif->deflink;
- 
- 		if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED |
- 			       IEEE80211_RC_NSS_CHANGED |
- 			       IEEE80211_RC_BW_CHANGED))
--			mt7996_mcu_add_rate_ctrl(dev, vif, sta, true);
-+			mt7996_mcu_add_rate_ctrl(dev, conf, mconf, sta, true);
- 
- 		if (changed & IEEE80211_RC_SMPS_CHANGED)
--			mt7996_mcu_set_fixed_field(dev, vif, sta, NULL,
-+			mt7996_mcu_set_fixed_field(dev, mconf, sta, NULL,
- 						   RATE_PARAM_MMPS_UPDATE);
- 
- 		spin_lock_bh(&dev->mt76.sta_poll_lock);
-@@ -2643,7 +2651,7 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
- 
- 		flow->sched = true;
- 		flow->start_tsf = mt7996_mac_twt_sched_list_add(dev, flow);
--		curr_tsf = __mt7996_get_tsf(hw, msta->vif);
-+		curr_tsf = __mt7996_get_tsf(hw, &msta->vif->deflink);
- 		div_u64_rem(curr_tsf - flow->start_tsf, interval, &rem);
- 		flow_tsf = curr_tsf + interval - rem;
- 		twt_agrt->twt = cpu_to_le64(flow_tsf);
-@@ -2652,7 +2660,8 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
- 	}
- 	flow->tsf = le64_to_cpu(twt_agrt->twt);
- 
--	if (mt7996_mcu_twt_agrt_update(dev, msta->vif, flow, MCU_TWT_AGRT_ADD))
-+	if (mt7996_mcu_twt_agrt_update(dev, &msta->vif->deflink, flow,
-+				       MCU_TWT_AGRT_ADD))
- 		goto unlock;
- 
- 	setup_cmd = TWT_SETUP_CMD_ACCEPT;
-@@ -2674,6 +2683,7 @@ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
- 				  u8 flowid)
- {
- 	struct mt7996_twt_flow *flow;
-+	struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
- 
- 	lockdep_assert_held(&dev->mt76.mutex);
- 
-@@ -2684,8 +2694,7 @@ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
- 		return;
- 
- 	flow = &msta->twt.flow[flowid];
--	if (mt7996_mcu_twt_agrt_update(dev, msta->vif, flow,
--				       MCU_TWT_AGRT_DELETE))
-+	if (mt7996_mcu_twt_agrt_update(dev, mconf, flow, MCU_TWT_AGRT_DELETE))
- 		return;
- 
- 	list_del_init(&flow->list);
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 9eb4c5b..bc4863d 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -205,29 +205,30 @@ static int get_omac_idx(enum nl80211_iftype type, u64 mask)
- 	return -1;
- }
- 
--static void mt7996_init_bitrate_mask(struct ieee80211_vif *vif)
-+static void mt7996_init_bitrate_mask(struct mt7996_bss_conf *mconf)
- {
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	int i;
- 
--	for (i = 0; i < ARRAY_SIZE(mvif->bitrate_mask.control); i++) {
--		mvif->bitrate_mask.control[i].gi = NL80211_TXRATE_DEFAULT_GI;
--		mvif->bitrate_mask.control[i].he_gi = 0xff;
--		mvif->bitrate_mask.control[i].he_ltf = 0xff;
--		mvif->bitrate_mask.control[i].legacy = GENMASK(31, 0);
--		memset(mvif->bitrate_mask.control[i].ht_mcs, 0xff,
--		       sizeof(mvif->bitrate_mask.control[i].ht_mcs));
--		memset(mvif->bitrate_mask.control[i].vht_mcs, 0xff,
--		       sizeof(mvif->bitrate_mask.control[i].vht_mcs));
--		memset(mvif->bitrate_mask.control[i].he_mcs, 0xff,
--		       sizeof(mvif->bitrate_mask.control[i].he_mcs));
-+	for (i = 0; i < ARRAY_SIZE(mconf->bitrate_mask.control); i++) {
-+		mconf->bitrate_mask.control[i].gi = NL80211_TXRATE_DEFAULT_GI;
-+		mconf->bitrate_mask.control[i].he_gi = 0xff;
-+		mconf->bitrate_mask.control[i].he_ltf = 0xff;
-+		mconf->bitrate_mask.control[i].legacy = GENMASK(31, 0);
-+		memset(mconf->bitrate_mask.control[i].ht_mcs, 0xff,
-+		       sizeof(mconf->bitrate_mask.control[i].ht_mcs));
-+		memset(mconf->bitrate_mask.control[i].vht_mcs, 0xff,
-+		       sizeof(mconf->bitrate_mask.control[i].vht_mcs));
-+		memset(mconf->bitrate_mask.control[i].he_mcs, 0xff,
-+		       sizeof(mconf->bitrate_mask.control[i].he_mcs));
- 	}
- }
- 
- static int mt7996_add_interface(struct ieee80211_hw *hw,
- 				struct ieee80211_vif *vif)
- {
-+	struct ieee80211_bss_conf *conf = &vif->bss_conf;
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_bss_conf *mconf = &mvif->deflink;
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
- 	struct mt76_txq *mtxq;
-@@ -240,8 +241,8 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
- 	    is_zero_ether_addr(vif->addr))
- 		phy->monitor_vif = vif;
- 
--	mvif->mt76.idx = __ffs64(~dev->mt76.vif_mask);
--	if (mvif->mt76.idx >= mt7996_max_interface_num(dev)) {
-+	mconf->mt76.idx = __ffs64(~dev->mt76.vif_mask);
-+	if (mconf->mt76.idx >= mt7996_max_interface_num(dev)) {
- 		ret = -ENOSPC;
- 		goto out;
- 	}
-@@ -251,19 +252,21 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
- 		ret = -ENOSPC;
- 		goto out;
- 	}
--	mvif->mt76.omac_idx = idx;
--	mvif->phy = phy;
--	mvif->mt76.band_idx = band_idx;
--	mvif->mt76.wmm_idx = vif->type != NL80211_IFTYPE_AP;
--
--	ret = mt7996_mcu_add_dev_info(phy, vif, true);
-+	mconf->mt76.omac_idx = idx;
-+	mconf->vif = mvif;
-+	mconf->phy = phy;
-+	mconf->mt76.band_idx = band_idx;
-+	mconf->mt76.wmm_idx = vif->type != NL80211_IFTYPE_AP;
-+	mvif->dev = dev;
-+
-+	ret = mt7996_mcu_add_dev_info(phy, conf, mconf, true);
- 	if (ret)
- 		goto out;
- 
--	dev->mt76.vif_mask |= BIT_ULL(mvif->mt76.idx);
--	phy->omac_mask |= BIT_ULL(mvif->mt76.omac_idx);
-+	dev->mt76.vif_mask |= BIT_ULL(mconf->mt76.idx);
-+	phy->omac_mask |= BIT_ULL(mconf->mt76.omac_idx);
- 
--	idx = MT7996_WTBL_RESERVED - mvif->mt76.idx;
-+	idx = MT7996_WTBL_RESERVED - mconf->mt76.idx;
- 
- 	INIT_LIST_HEAD(&mvif->sta.rc_list);
- 	INIT_LIST_HEAD(&mvif->sta.wcid.poll_list);
-@@ -283,24 +286,25 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
- 	}
- 
- 	if (vif->type != NL80211_IFTYPE_AP &&
--	    (!mvif->mt76.omac_idx || mvif->mt76.omac_idx > 3))
-+	    (!mconf->mt76.omac_idx || mconf->mt76.omac_idx > 3))
- 		vif->offload_flags = 0;
- 	vif->offload_flags |= IEEE80211_OFFLOAD_ENCAP_4ADDR;
- 
- 	if (phy->mt76->chandef.chan->band != NL80211_BAND_2GHZ)
--		mvif->mt76.basic_rates_idx = MT7996_BASIC_RATES_TBL + 4;
-+		mconf->mt76.basic_rates_idx = MT7996_BASIC_RATES_TBL + 4;
- 	else
--		mvif->mt76.basic_rates_idx = MT7996_BASIC_RATES_TBL;
-+		mconf->mt76.basic_rates_idx = MT7996_BASIC_RATES_TBL;
- 
--	mt7996_init_bitrate_mask(vif);
-+	mt7996_init_bitrate_mask(mconf);
- 
--	mt7996_mcu_add_bss_info(phy, vif, true);
-+	mt7996_mcu_add_bss_info(phy, conf, mconf, true);
- 	/* defer the first STA_REC of BMC entry to BSS_CHANGED_BSSID for STA
- 	 * interface, since firmware only records BSSID when the entry is new
- 	 */
- 	if (vif->type != NL80211_IFTYPE_STATION)
--		mt7996_mcu_add_sta(dev, vif, NULL, true, true);
-+		mt7996_mcu_add_sta(dev, conf, mconf, NULL, true, true);
- 	rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid);
-+	rcu_assign_pointer(mvif->link[0], mconf);
- 
- out:
- 	mutex_unlock(&dev->mt76.mutex);
-@@ -311,7 +315,9 @@ out:
- static void mt7996_remove_interface(struct ieee80211_hw *hw,
- 				    struct ieee80211_vif *vif)
- {
-+	struct ieee80211_bss_conf *conf;
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_bss_conf *mconf;
- 	struct mt7996_sta *msta = &mvif->sta;
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-@@ -319,24 +325,22 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
- 
- 	cancel_delayed_work_sync(&phy->scan_work);
- 
--	mt7996_mcu_add_sta(dev, vif, NULL, false, false);
--	mt7996_mcu_add_bss_info(phy, vif, false);
-+	mutex_lock(&dev->mt76.mutex);
-+
-+	conf = link_conf_dereference_protected(vif, 0);
-+	mconf = mconf_dereference_protected(mvif, 0);
-+	mt7996_mcu_add_sta(dev, conf, mconf, NULL, false, false);
-+	mt7996_mcu_add_bss_info(phy, conf, mconf, false);
- 
- 	if (vif == phy->monitor_vif)
- 		phy->monitor_vif = NULL;
- 
--	mt7996_mcu_add_dev_info(phy, vif, false);
-+	mt7996_mcu_add_dev_info(phy, conf, mconf, false);
- 
- 	rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
- 
--	mutex_lock(&dev->mt76.mutex);
--
--	if (test_bit(MT76_SCANNING, &phy->mt76->state))
--		mt7996_scan_complete(phy, true);
--
--	dev->mt76.vif_mask &= ~BIT_ULL(mvif->mt76.idx);
--	phy->omac_mask &= ~BIT_ULL(mvif->mt76.omac_idx);
--	mutex_unlock(&dev->mt76.mutex);
-+	dev->mt76.vif_mask &= ~BIT_ULL(mconf->mt76.idx);
-+	phy->omac_mask &= ~BIT_ULL(mconf->mt76.omac_idx);
- 
- 	spin_lock_bh(&dev->mt76.sta_poll_lock);
- 	if (!list_empty(&msta->wcid.poll_list))
-@@ -344,6 +348,9 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
- 	spin_unlock_bh(&dev->mt76.sta_poll_lock);
- 
- 	mt76_wcid_cleanup(&dev->mt76, &msta->wcid);
-+	rcu_assign_pointer(mvif->link[0], NULL);
-+
-+	mutex_unlock(&dev->mt76.mutex);
- }
- 
- static void ___mt7996_set_channel(struct mt7996_phy *phy,
-@@ -441,6 +448,8 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
- 	struct mt7996_sta *msta = sta ? (struct mt7996_sta *)sta->drv_priv :
- 				  &mvif->sta;
- 	struct mt76_wcid *wcid = &msta->wcid;
-+	struct mt7996_bss_conf *mconf;
-+	struct ieee80211_bss_conf *conf;
- 	u8 *wcid_keyidx = &wcid->hw_key_idx;
- 	int idx = key->keyidx;
- 	int err = 0;
-@@ -482,9 +491,11 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
--	if (cmd == SET_KEY && !sta && !mvif->mt76.cipher) {
--		mvif->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher);
--		mt7996_mcu_add_bss_info(phy, vif, true);
-+	conf = link_conf_dereference_protected(vif, 0);
-+	mconf = mconf_dereference_protected(mvif, 0);
-+	if (cmd == SET_KEY && !sta && !mconf->mt76.cipher) {
-+		mconf->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher);
-+		mt7996_mcu_add_bss_info(phy, conf, mconf, true);
- 	}
- 
- 	if (cmd == SET_KEY) {
-@@ -498,9 +509,9 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
- 	mt76_wcid_key_setup(&dev->mt76, wcid, key);
- 
- 	if (key->keyidx == 6 || key->keyidx == 7)
--		err = mt7996_mcu_bcn_prot_enable(dev, vif, key);
-+		err = mt7996_mcu_bcn_prot_enable(dev, conf, mconf, key);
- 	else
--		err = mt7996_mcu_add_key(&dev->mt76, vif, key,
-+		err = mt7996_mcu_add_key(&dev->mt76, mconf, key,
- 					 MCU_WMWA_UNI_CMD(STA_REC_UPDATE),
- 					 &msta->wcid, cmd);
- out:
-@@ -547,7 +558,9 @@ mt7996_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	       unsigned int link_id, u16 queue,
- 	       const struct ieee80211_tx_queue_params *params)
- {
-+	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_bss_conf *mconf;
- 	static const u8 mq_to_aci[] = {
- 		[IEEE80211_AC_VO] = 3,
- 		[IEEE80211_AC_VI] = 2,
-@@ -555,10 +568,15 @@ mt7996_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 		[IEEE80211_AC_BK] = 1,
- 	};
- 
-+	mutex_lock(&dev->mt76.mutex);
-+	mconf = mconf_dereference_protected(mvif, 0);
-+
- 	/* firmware uses access class index */
--	mvif->queue_params[mq_to_aci[queue]] = *params;
-+	mconf->queue_params[mq_to_aci[queue]] = *params;
- 	/* no need to update right away, we'll get BSS_CHANGED_QOS */
- 
-+	mutex_unlock(&dev->mt76.mutex);
-+
- 	return 0;
- }
- 
-@@ -619,22 +637,20 @@ static void mt7996_configure_filter(struct ieee80211_hw *hw,
- }
- 
- static void
--mt7996_update_bss_color(struct ieee80211_hw *hw,
--			struct ieee80211_vif *vif,
-+mt7996_update_bss_color(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-+			struct mt7996_bss_conf *mconf,
- 			struct cfg80211_he_bss_color *bss_color)
- {
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 
- 	switch (vif->type) {
- 	case NL80211_IFTYPE_AP: {
--		struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--
--		if (mvif->mt76.omac_idx > HW_BSSID_MAX)
-+		if (mconf->mt76.omac_idx > HW_BSSID_MAX)
- 			return;
- 		fallthrough;
- 	}
- 	case NL80211_IFTYPE_STATION:
--		mt7996_mcu_update_bss_color(dev, vif, bss_color);
-+		mt7996_mcu_update_bss_color(dev, mconf, bss_color);
- 		break;
- 	default:
- 		break;
-@@ -642,16 +658,15 @@ mt7996_update_bss_color(struct ieee80211_hw *hw,
- }
- 
- static u8
--mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
--		       bool beacon, bool mcast)
-+mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
-+		       struct mt7996_bss_conf *mconf, bool beacon, bool mcast)
- {
--	struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
--	struct mt76_phy *mphy = hw->priv;
-+	struct mt76_phy *mphy = mconf->phy->mt76;
- 	u16 rate;
- 	u8 i, idx;
- 
--	rate = mt76_connac2_mac_tx_rate_val(mphy, vif, beacon, mcast);
-+	rate = mt76_connac2_mac_tx_rate_val(mphy, conf->vif, beacon, mcast);
- 
- 	if (beacon) {
- 		struct mt7996_phy *phy = mphy->priv;
-@@ -672,23 +687,22 @@ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 		if ((mt76_rates[i].hw_value & GENMASK(7, 0)) == idx)
- 			return MT7996_BASIC_RATES_TBL + 2 * i;
- 
--	return mvif->basic_rates_idx;
-+	return mconf->mt76.basic_rates_idx;
- }
- 
- static void
--mt7996_update_mu_group(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
--		       struct ieee80211_bss_conf *info)
-+mt7996_update_mu_group(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
-+		       struct mt7996_bss_conf *mconf)
- {
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
--	u8 band = mvif->mt76.band_idx;
-+	u8 band = mconf->mt76.band_idx;
- 	u32 *mu;
- 
--	mu = (u32 *)info->mu_group.membership;
-+	mu = (u32 *)conf->mu_group.membership;
- 	mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_VLD0(band), mu[0]);
- 	mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_VLD1(band), mu[1]);
- 
--	mu = (u32 *)info->mu_group.position;
-+	mu = (u32 *)conf->mu_group.position;
- 	mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_POS0(band), mu[0]);
- 	mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_POS1(band), mu[1]);
- 	mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_POS2(band), mu[2]);
-@@ -700,20 +714,22 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
- 				    struct ieee80211_bss_conf *info,
- 				    u64 changed)
- {
--	struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_bss_conf *mconf;
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
-+	mconf = mconf_dereference_protected(mvif, 0);
- 	/* station mode uses BSSID to map the wlan entry to a peer,
- 	 * and then peer references bss_info_rfch to set bandwidth cap.
- 	 */
- 	if ((changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) ||
- 	    (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) ||
- 	    (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon)) {
--		mt7996_mcu_add_bss_info(phy, vif, true);
--		mt7996_mcu_add_sta(dev, vif, NULL, true,
-+		mt7996_mcu_add_bss_info(phy, info, mconf, true);
-+		mt7996_mcu_add_sta(dev, info, mconf, NULL, true,
- 				   !!(changed & BSS_CHANGED_BSSID));
- 	}
- 
-@@ -725,42 +741,42 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
- 
- 		if (slottime != phy->slottime) {
- 			phy->slottime = slottime;
--			mt7996_mcu_set_timing(phy, vif);
-+			mt7996_mcu_set_timing(phy, mconf);
- 		}
- 	}
- 
- 	if (changed & BSS_CHANGED_MCAST_RATE)
--		mvif->mcast_rates_idx =
--			mt7996_get_rates_table(hw, vif, false, true);
-+		mconf->mt76.mcast_rates_idx =
-+			mt7996_get_rates_table(hw, info, mconf, false, true);
- 
- 	if (changed & BSS_CHANGED_BASIC_RATES)
--		mvif->basic_rates_idx =
--			mt7996_get_rates_table(hw, vif, false, false);
-+		mconf->mt76.basic_rates_idx =
-+			mt7996_get_rates_table(hw, info, mconf, false, false);
- 
- 	/* ensure that enable txcmd_mode after bss_info */
- 	if (changed & (BSS_CHANGED_QOS | BSS_CHANGED_BEACON_ENABLED))
--		mt7996_mcu_set_tx(dev, vif);
-+		mt7996_mcu_set_tx(dev, mconf);
- 
- 	if (changed & BSS_CHANGED_HE_OBSS_PD)
--		mt7996_mcu_add_obss_spr(phy, vif, &info->he_obss_pd);
-+		mt7996_mcu_add_obss_spr(phy, mconf, &info->he_obss_pd);
- 
- 	if (changed & BSS_CHANGED_HE_BSS_COLOR)
--		mt7996_update_bss_color(hw, vif, &info->he_bss_color);
-+		mt7996_update_bss_color(hw, vif, mconf, &info->he_bss_color);
- 
- 	if (changed & (BSS_CHANGED_BEACON |
- 		       BSS_CHANGED_BEACON_ENABLED)) {
--		mvif->beacon_rates_idx =
--			mt7996_get_rates_table(hw, vif, true, false);
-+		mconf->mt76.beacon_rates_idx =
-+			mt7996_get_rates_table(hw, info, mconf, true, false);
- 
--		mt7996_mcu_add_beacon(hw, vif, info->enable_beacon);
-+		mt7996_mcu_add_beacon(hw, info, mconf, info->enable_beacon);
- 	}
- 
- 	if (changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP |
- 		       BSS_CHANGED_FILS_DISCOVERY))
--		mt7996_mcu_beacon_inband_discov(dev, vif, changed);
-+		mt7996_mcu_beacon_inband_discov(dev, info, mconf, changed);
- 
- 	if (changed & BSS_CHANGED_MU_GROUPS)
--		mt7996_update_mu_group(hw, vif, info);
-+		mt7996_update_mu_group(hw, info, mconf);
- 
- 	mutex_unlock(&dev->mt76.mutex);
- }
-@@ -771,9 +787,14 @@ mt7996_channel_switch_beacon(struct ieee80211_hw *hw,
- 			     struct cfg80211_chan_def *chandef)
- {
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_bss_conf *mconf;
-+	struct ieee80211_bss_conf *conf;
- 
- 	mutex_lock(&dev->mt76.mutex);
--	mt7996_mcu_add_beacon(hw, vif, true);
-+	mconf = mconf_dereference_protected(mvif, 0);
-+	conf = link_conf_dereference_protected(vif, 0);
-+	mt7996_mcu_add_beacon(hw, conf, mconf, true);
- 	mutex_unlock(&dev->mt76.mutex);
- }
- 
-@@ -783,7 +804,8 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	u8 band_idx = mvif->phy->mt76->band_idx;
-+	struct mt7996_bss_conf *mconf = mconf_dereference_protected(mvif, 0);
-+	u8 band_idx = mconf->phy->mt76->band_idx;
- 	int idx;
- 
- #ifdef CONFIG_MTK_VENDOR
-@@ -803,7 +825,7 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 	msta->wcid.tx_info |= MT_WCID_TX_INFO_SET;
- 
- #ifdef CONFIG_MTK_VENDOR
--	mt7996_vendor_amnt_sta_remove(mvif->phy, sta);
-+	mt7996_vendor_amnt_sta_remove(mconf->phy, sta);
- #endif
- 
- #ifdef CONFIG_MTK_VENDOR
-@@ -830,15 +852,20 @@ void mt7996_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 			  struct ieee80211_sta *sta)
- {
- 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_bss_conf *mconf;
-+	struct ieee80211_bss_conf *conf;
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
- 	mt7996_mac_wtbl_update(dev, msta->wcid.idx,
- 			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
- 
--	mt7996_mcu_add_sta(dev, vif, sta, true, true);
--	mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
-+	conf = link_conf_dereference_protected(vif, 0);
-+	mconf = mconf_dereference_protected(mvif, 0);
-+	mt7996_mcu_add_sta(dev, conf, mconf, sta, true, true);
-+	mt7996_mcu_add_rate_ctrl(dev, conf, mconf, sta, false);
- 
- 	ewma_avg_signal_init(&msta->avg_ack_signal);
- 
-@@ -849,10 +876,15 @@ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 			   struct ieee80211_sta *sta)
- {
- 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_bss_conf *mconf;
-+	struct ieee80211_bss_conf *conf;
- 	int i;
- 
--	mt7996_mcu_add_sta(dev, vif, sta, false, false);
-+	conf = link_conf_dereference_protected(vif, 0);
-+	mconf = mconf_dereference_protected(mvif, 0);
-+	mt7996_mcu_add_sta(dev, conf, mconf, sta, false, false);
- 
- 	mt7996_mac_wtbl_update(dev, msta->wcid.idx,
- 			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
-@@ -984,7 +1016,7 @@ mt7996_get_stats(struct ieee80211_hw *hw,
- 	return 0;
- }
- 
--u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif)
-+u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_bss_conf *mconf)
- {
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-@@ -996,8 +1028,8 @@ u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif)
- 
- 	lockdep_assert_held(&dev->mt76.mutex);
- 
--	n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
--					       : mvif->mt76.omac_idx;
-+	n = mconf->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
-+					       : mconf->mt76.omac_idx;
- 	/* TSF software read */
- 	mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE,
- 		 MT_LPON_TCR_SW_READ);
-@@ -1012,10 +1044,12 @@ mt7996_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
- {
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+	struct mt7996_bss_conf *mconf;
- 	u64 ret;
- 
- 	mutex_lock(&dev->mt76.mutex);
--	ret = __mt7996_get_tsf(hw, mvif);
-+	mconf = mconf_dereference_protected(mvif, 0);
-+	ret = __mt7996_get_tsf(hw, mconf);
- 	mutex_unlock(&dev->mt76.mutex);
- 
- 	return ret;
-@@ -1028,6 +1062,7 @@ mt7996_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct mt7996_bss_conf *mconf;
- 	union {
- 		u64 t64;
- 		u32 t32[2];
-@@ -1036,8 +1071,9 @@ mt7996_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
--	n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
--					       : mvif->mt76.omac_idx;
-+	mconf = mconf_dereference_protected(mvif, 0);
-+	n = mconf->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
-+					       : mconf->mt76.omac_idx;
- 	mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]);
- 	mt76_wr(dev, MT_LPON_UTTR1(phy->mt76->band_idx), tsf.t32[1]);
- 	/* TSF software overwrite */
-@@ -1054,6 +1090,7 @@ mt7996_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct mt7996_bss_conf *mconf;
- 	union {
- 		u64 t64;
- 		u32 t32[2];
-@@ -1062,8 +1099,9 @@ mt7996_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
--	n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
--					       : mvif->mt76.omac_idx;
-+	mconf = mconf_dereference_protected(mvif, 0);
-+	n = mconf->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
-+					       : mconf->mt76.omac_idx;
- 	mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]);
- 	mt76_wr(dev, MT_LPON_UTTR1(phy->mt76->band_idx), tsf.t32[1]);
- 	/* TSF software adjust*/
-@@ -1179,7 +1217,7 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
- static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta)
- {
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
--	struct mt7996_dev *dev = msta->vif->phy->dev;
-+	struct mt7996_dev *dev = msta->vif->dev;
- 	u32 *changed = data;
- 
- 	spin_lock_bh(&dev->mt76.sta_poll_lock);
-@@ -1215,9 +1253,13 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
- 	struct mt7996_dev *dev = phy->dev;
-+	struct mt7996_bss_conf *mconf;
- 	u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED;
- 
--	mvif->bitrate_mask = *mask;
-+	mutex_lock(&dev->mt76.mutex);
-+	mconf = mconf_dereference_protected(mvif, 0);
-+	mconf->bitrate_mask = *mask;
-+	mutex_unlock(&dev->mt76.mutex);
- 
- 	/* if multiple rates across different preambles are given we can
- 	 * reconfigure this info with all peers using sta_rec command with
-@@ -1239,14 +1281,20 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw,
- 				 bool enabled)
- {
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_bss_conf *mconf;
-+
-+	mutex_lock(&dev->mt76.mutex);
-+	mconf = mconf_dereference_protected(mvif, 0);
- 
- 	if (enabled)
- 		set_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags);
- 	else
- 		clear_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags);
- 
--	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta);
-+	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, sta);
-+	mutex_unlock(&dev->mt76.mutex);
- }
- 
- static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw,
-@@ -1255,14 +1303,20 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw,
- 					 bool enabled)
- {
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_bss_conf *mconf;
-+
-+	mutex_lock(&dev->mt76.mutex);
-+	mconf = mconf_dereference_protected(mvif, 0);
- 
- 	if (enabled)
- 		set_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
- 	else
- 		clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
- 
--	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta);
-+	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, sta);
-+	mutex_unlock(&dev->mt76.mutex);
- }
- 
- static const char mt7996_gstrings_stats[][ETH_GSTRING_LEN] = {
-@@ -1395,7 +1449,7 @@ static void mt7996_ethtool_worker(void *wi_data, struct ieee80211_sta *sta)
- 	struct mt76_ethtool_worker_info *wi = wi_data;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- 
--	if (msta->vif->mt76.idx != wi->idx)
-+	if (msta->vif->deflink.mt76.idx != wi->idx)
- 		return;
- 
- 	mt76_ethtool_worker(wi, &msta->wcid.stats, true);
-@@ -1409,15 +1463,17 @@ void mt7996_get_et_stats(struct ieee80211_hw *hw,
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_bss_conf *mconf;
- 	struct mt76_mib_stats *mib = &phy->mib;
- 	struct mt76_ethtool_worker_info wi = {
- 		.data = data,
--		.idx = mvif->mt76.idx,
- 	};
- 	/* See mt7996_ampdu_stat_read_phy, etc */
- 	int i, ei = 0;
- 
- 	mutex_lock(&dev->mt76.mutex);
-+	mconf = mconf_dereference_protected(mvif, 0);
-+	wi.idx = mconf->mt76.idx,
- 
- 	mt7996_mac_update_stats(phy);
- 
-@@ -1623,6 +1679,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
- 			     struct net_device_path *path)
- {
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_bss_conf *mconf = &mvif->deflink;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-@@ -1652,7 +1709,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
- 	path->type = DEV_PATH_MTK_WDMA;
- 	path->dev = ctx->dev;
- 	path->mtk_wdma.wdma_idx = wed->wdma_idx;
--	path->mtk_wdma.bss = mvif->mt76.idx;
-+	path->mtk_wdma.bss = mconf->mt76.idx;
- 	path->mtk_wdma.queue = 0;
- 	path->mtk_wdma.wcid = msta->wcid.idx;
- 
-@@ -1784,6 +1841,7 @@ mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
- 	struct mt7996_phy *phy = ctx->phy;
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_bss_conf *mconf;
- 
- 	wiphy_info(hw->wiphy, "Assign VIF (addr: %pM, type: %d, link_id: %d) to channel context: %d MHz\n",
- 		    vif->addr, vif->type, link_conf->link_id,
-@@ -1791,7 +1849,8 @@ mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 
- 	mutex_lock(&phy->dev->mt76.mutex);
- 
--	mvif->chanctx = ctx;
-+	mconf = mconf_dereference_protected(mvif, 0);
-+	mconf->chanctx = ctx;
- 	ctx->nbss_assigned++;
- 
- 	mutex_unlock(&phy->dev->mt76.mutex);
-@@ -1807,6 +1866,7 @@ mt7996_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
- 	struct mt7996_phy *phy = ctx->phy;
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_bss_conf *mconf;
- 
- 	wiphy_info(hw->wiphy, "Remove VIF (addr: %pM, type: %d, link_id: %d) from channel context: %d MHz\n",
- 		   vif->addr, vif->type, link_conf->link_id,
-@@ -1818,7 +1878,8 @@ mt7996_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	if (test_bit(MT76_SCANNING, &phy->mt76->state))
- 		mt7996_scan_complete(phy, true);
- 
--	mvif->chanctx = NULL;
-+	mconf = mconf_dereference_protected(mvif, 0);
-+	mconf->chanctx = NULL;
- 	ctx->nbss_assigned--;
- 
- 	mutex_unlock(&phy->dev->mt76.mutex);
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 5319cc3..ebd3192 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -117,12 +117,12 @@ mt7996_mcu_get_sta_nss(u16 mcs_map)
- }
- 
- static void
--mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs,
--			  u16 mcs_map)
-+mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta,
-+			  struct mt7996_bss_conf *mconf,
-+			  __le16 *he_mcs, u16 mcs_map)
- {
--	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
--	enum nl80211_band band = msta->vif->phy->mt76->chandef.chan->band;
--	const u16 *mask = msta->vif->bitrate_mask.control[band].he_mcs;
-+	enum nl80211_band band = mconf->phy->mt76->chandef.chan->band;
-+	const u16 *mask = mconf->bitrate_mask.control[band].he_mcs;
- 	int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
- 
- 	for (nss = 0; nss < max_nss; nss++) {
-@@ -922,8 +922,7 @@ mt7996_mcu_add_uni_tlv(struct sk_buff *skb, u16 tag, u16 len)
- }
- 
- static void
--mt7996_mcu_bss_rfch_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
--			struct mt7996_phy *phy)
-+mt7996_mcu_bss_rfch_tlv(struct sk_buff *skb, struct mt7996_phy *phy)
- {
- 	static const u8 rlm_ch_band[] = {
- 		[NL80211_BAND_2GHZ] = 1,
-@@ -953,8 +952,7 @@ mt7996_mcu_bss_rfch_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
- }
- 
- static void
--mt7996_mcu_bss_ra_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
--		      struct mt7996_phy *phy)
-+mt7996_mcu_bss_ra_tlv(struct sk_buff *skb)
- {
- 	struct bss_ra_tlv *ra;
- 	struct tlv *tlv;
-@@ -966,7 +964,7 @@ mt7996_mcu_bss_ra_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
- }
- 
- static void
--mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
-+mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
- 		      struct mt7996_phy *phy)
- {
- #define DEFAULT_HE_PE_DURATION		4
-@@ -975,16 +973,16 @@ mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
- 	struct bss_info_uni_he *he;
- 	struct tlv *tlv;
- 
--	cap = mt76_connac_get_he_phy_cap(phy->mt76, vif);
-+	cap = mt76_connac_get_he_phy_cap(phy->mt76, conf->vif);
- 
- 	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he));
- 
- 	he = (struct bss_info_uni_he *)tlv;
--	he->he_pe_duration = vif->bss_conf.htc_trig_based_pkt_ext;
-+	he->he_pe_duration = conf->htc_trig_based_pkt_ext;
- 	if (!he->he_pe_duration)
- 		he->he_pe_duration = DEFAULT_HE_PE_DURATION;
- 
--	he->he_rts_thres = cpu_to_le16(vif->bss_conf.frame_time_rts_th);
-+	he->he_rts_thres = cpu_to_le16(conf->frame_time_rts_th);
- 	if (!he->he_rts_thres)
- 		he->he_rts_thres = cpu_to_le16(DEFAULT_HE_DURATION_RTS_THRES);
- 
-@@ -994,13 +992,13 @@ mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
- }
- 
- static void
--mt7996_mcu_bss_mbssid_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
-+mt7996_mcu_bss_mbssid_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
- 			  struct mt7996_phy *phy, int enable)
- {
- 	struct bss_info_uni_mbssid *mbssid;
- 	struct tlv *tlv;
- 
--	if (!vif->bss_conf.bssid_indicator && enable)
-+	if (!conf->bssid_indicator && enable)
- 		return;
- 
- 	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_11V_MBSSID, sizeof(*mbssid));
-@@ -1008,23 +1006,21 @@ mt7996_mcu_bss_mbssid_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
- 	mbssid = (struct bss_info_uni_mbssid *)tlv;
- 
- 	if (enable) {
--		mbssid->max_indicator = vif->bss_conf.bssid_indicator;
--		mbssid->mbss_idx = vif->bss_conf.bssid_index;
-+		mbssid->max_indicator = conf->bssid_indicator;
-+		mbssid->mbss_idx = conf->bssid_index;
- 		mbssid->tx_bss_omac_idx = 0;
- 	}
- }
- 
- static void
--mt7996_mcu_bss_bmc_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
-+mt7996_mcu_bss_bmc_tlv(struct sk_buff *skb, struct mt7996_bss_conf *mconf,
- 		       struct mt7996_phy *phy)
- {
--	struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
- 	struct bss_rate_tlv *bmc;
- 	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
- 	enum nl80211_band band = chandef->chan->band;
- 	struct tlv *tlv;
--	u8 idx = mvif->mcast_rates_idx ?
--		 mvif->mcast_rates_idx : mvif->basic_rates_idx;
-+	u8 idx = mconf->mt76.mcast_rates_idx ?: mconf->mt76.basic_rates_idx;
- 
- 	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_RATE, sizeof(*bmc));
- 
-@@ -1048,9 +1044,9 @@ mt7996_mcu_bss_txcmd_tlv(struct sk_buff *skb, bool en)
- }
- 
- static void
--mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
-+mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
-+		       struct mt7996_bss_conf *mconf)
- {
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct bss_mld_tlv *mld;
- 	struct tlv *tlv;
- 
-@@ -1058,33 +1054,30 @@ mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
- 
- 	mld = (struct bss_mld_tlv *)tlv;
- 	mld->group_mld_id = 0xff;
--	mld->own_mld_id = mvif->mt76.idx;
-+	mld->own_mld_id = mconf->mt76.idx;
- 	mld->remap_idx = 0xff;
- }
- 
- static void
--mt7996_mcu_bss_sec_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
-+mt7996_mcu_bss_sec_tlv(struct sk_buff *skb, struct mt7996_bss_conf *mconf)
- {
--	struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
- 	struct bss_sec_tlv *sec;
- 	struct tlv *tlv;
- 
- 	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_SEC, sizeof(*sec));
- 
- 	sec = (struct bss_sec_tlv *)tlv;
--	sec->cipher = mvif->cipher;
-+	sec->cipher = mconf->mt76.cipher;
- }
- 
- static int
--mt7996_mcu_muar_config(struct mt7996_phy *phy, struct ieee80211_vif *vif,
--		       bool bssid, bool enable)
-+mt7996_mcu_muar_config(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
-+		       struct mt7996_bss_conf *mconf, bool bssid, bool enable)
- {
- #define UNI_MUAR_ENTRY 2
- 	struct mt7996_dev *dev = phy->dev;
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	u32 idx = mvif->mt76.omac_idx - REPEATER_BSSID_START;
--	const u8 *addr = vif->addr;
--
-+	u32 idx = mconf->mt76.omac_idx - REPEATER_BSSID_START;
-+	const u8 *addr = bssid ? conf->bssid : conf->vif->addr;
- 	struct {
- 		struct {
- 			u8 band;
-@@ -1109,9 +1102,6 @@ mt7996_mcu_muar_config(struct mt7996_phy *phy, struct ieee80211_vif *vif,
- 		.entry_add = true,
- 	};
- 
--	if (bssid)
--		addr = vif->bss_conf.bssid;
--
- 	if (enable)
- 		memcpy(req.addr, addr, ETH_ALEN);
- 
-@@ -1120,10 +1110,8 @@ mt7996_mcu_muar_config(struct mt7996_phy *phy, struct ieee80211_vif *vif,
- }
- 
- static void
--mt7996_mcu_bss_ifs_timing_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
-+mt7996_mcu_bss_ifs_timing_tlv(struct sk_buff *skb, struct mt7996_phy *phy)
- {
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	struct mt7996_phy *phy = mvif->phy;
- 	struct bss_ifs_time_tlv *ifs_time;
- 	struct tlv *tlv;
- 	bool is_2ghz = phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ;
-@@ -1149,12 +1137,13 @@ mt7996_mcu_bss_ifs_timing_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
- 
- static int
- mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
--			 struct ieee80211_vif *vif,
-+			 struct ieee80211_bss_conf *conf,
-+			 struct mt7996_bss_conf *mconf,
- 			 struct ieee80211_sta *sta,
- 			 struct mt76_phy *phy, u16 wlan_idx,
- 			 bool enable)
- {
--	struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
-+	struct ieee80211_vif *vif = conf->vif;
- 	struct cfg80211_chan_def *chandef = &phy->chandef;
- 	struct mt76_connac_bss_basic_tlv *bss;
- 	u32 type = CONNECTION_INFRA_AP;
-@@ -1171,8 +1160,7 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
- 		if (enable) {
- 			rcu_read_lock();
- 			if (!sta)
--				sta = ieee80211_find_sta(vif,
--							 vif->bss_conf.bssid);
-+				sta = ieee80211_find_sta(vif, conf->bssid);
- 			/* TODO: enable BSS_INFO_UAPSD & BSS_INFO_PM */
- 			if (sta) {
- 				struct mt76_wcid *wcid;
-@@ -1195,18 +1183,17 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
- 	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_BASIC, sizeof(*bss));
- 
- 	bss = (struct mt76_connac_bss_basic_tlv *)tlv;
--	bss->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int);
--	bss->dtim_period = vif->bss_conf.dtim_period;
- 	bss->bmc_tx_wlan_idx = cpu_to_le16(wlan_idx);
- 	bss->sta_idx = cpu_to_le16(sta_wlan_idx);
- 	bss->conn_type = cpu_to_le32(type);
--	bss->omac_idx = mvif->omac_idx;
--	bss->band_idx = mvif->band_idx;
--	bss->wmm_idx = mvif->wmm_idx;
-+	bss->omac_idx = mconf->mt76.omac_idx;
-+	bss->band_idx = mconf->mt76.band_idx;
-+	bss->wmm_idx = mconf->mt76.wmm_idx;
- 	bss->conn_state = !enable;
- 	bss->active = enable;
- 
--	idx = mvif->omac_idx > EXT_BSSID_START ? HW_BSSID_0 : mvif->omac_idx;
-+	idx = mconf->mt76.omac_idx > EXT_BSSID_START ? HW_BSSID_0 :
-+						       mconf->mt76.omac_idx;
- 	bss->hw_bss_idx = idx;
- 
- 	if (vif->type == NL80211_IFTYPE_MONITOR) {
-@@ -1219,9 +1206,9 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
- 		return 0;
- 	}
- 
--	memcpy(bss->bssid, vif->bss_conf.bssid, ETH_ALEN);
--	bss->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int);
--	bss->dtim_period = vif->bss_conf.dtim_period;
-+	memcpy(bss->bssid, conf->bssid, ETH_ALEN);
-+	bss->bcn_interval = cpu_to_le16(conf->beacon_int);
-+	bss->dtim_period = conf->dtim_period;
- 	bss->phymode = mt76_connac_get_phy_mode(phy, vif,
- 						chandef->chan->band, NULL);
- 	bss->phymode_ext = mt76_connac_get_phy_mode_ext(phy, vif,
-@@ -1248,63 +1235,64 @@ __mt7996_mcu_alloc_bss_req(struct mt76_dev *dev, struct mt76_vif *mvif, int len)
- }
- 
- int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
--			    struct ieee80211_vif *vif, int enable)
-+			    struct ieee80211_bss_conf *conf,
-+			    struct mt7996_bss_conf *mconf, int enable)
- {
-+	struct ieee80211_vif *vif = conf->vif;
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_dev *dev = phy->dev;
- 	struct sk_buff *skb;
- 
--	if (mvif->mt76.omac_idx >= REPEATER_BSSID_START) {
--		mt7996_mcu_muar_config(phy, vif, false, enable);
--		mt7996_mcu_muar_config(phy, vif, true, enable);
-+	if (mconf->mt76.omac_idx >= REPEATER_BSSID_START) {
-+		mt7996_mcu_muar_config(phy, conf, mconf, false, enable);
-+		mt7996_mcu_muar_config(phy, conf, mconf, true, enable);
- 	}
- 
--	skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
-+	skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76,
- 					 MT7996_BSS_UPDATE_MAX_SIZE);
- 	if (IS_ERR(skb))
- 		return PTR_ERR(skb);
- 
- 	/* bss_basic must be first */
--	mt7996_mcu_bss_basic_tlv(skb, vif, NULL, phy->mt76,
-+	mt7996_mcu_bss_basic_tlv(skb, conf, mconf, NULL, phy->mt76,
- 				 mvif->sta.wcid.idx, enable);
--	mt7996_mcu_bss_sec_tlv(skb, vif);
-+	mt7996_mcu_bss_sec_tlv(skb, mconf);
- 
- 	if (vif->type == NL80211_IFTYPE_MONITOR)
- 		goto out;
- 
- 	if (enable) {
--		mt7996_mcu_bss_rfch_tlv(skb, vif, phy);
--		mt7996_mcu_bss_bmc_tlv(skb, vif, phy);
--		mt7996_mcu_bss_ra_tlv(skb, vif, phy);
-+		mt7996_mcu_bss_rfch_tlv(skb, phy);
-+		mt7996_mcu_bss_bmc_tlv(skb, mconf, phy);
-+		mt7996_mcu_bss_ra_tlv(skb);
- 		mt7996_mcu_bss_txcmd_tlv(skb, true);
--		mt7996_mcu_bss_ifs_timing_tlv(skb, vif);
-+		mt7996_mcu_bss_ifs_timing_tlv(skb, phy);
- 
--		if (vif->bss_conf.he_support)
--			mt7996_mcu_bss_he_tlv(skb, vif, phy);
-+		if (conf->he_support)
-+			mt7996_mcu_bss_he_tlv(skb, conf, phy);
- 
- 		/* this tag is necessary no matter if the vif is MLD */
--		mt7996_mcu_bss_mld_tlv(skb, vif);
-+		mt7996_mcu_bss_mld_tlv(skb, vif, mconf);
- 	}
- 
--	mt7996_mcu_bss_mbssid_tlv(skb, vif, phy, enable);
-+	mt7996_mcu_bss_mbssid_tlv(skb, conf, phy, enable);
- 
- out:
- 	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
- 				     MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
- }
- 
--int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif)
-+int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct mt7996_bss_conf *mconf)
- {
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_dev *dev = phy->dev;
- 	struct sk_buff *skb;
- 
--	skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
-+	skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76,
- 					 MT7996_BSS_UPDATE_MAX_SIZE);
- 	if (IS_ERR(skb))
- 		return PTR_ERR(skb);
- 
--	mt7996_mcu_bss_ifs_timing_tlv(skb, vif);
-+	mt7996_mcu_bss_ifs_timing_tlv(skb, phy);
- 
- 	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
- 				     MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
-@@ -1346,12 +1334,12 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
- 			 bool enable)
- {
- 	struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv;
--	struct mt7996_vif *mvif = msta->vif;
-+	struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
- 
- 	if (enable && !params->amsdu)
- 		msta->wcid.amsdu = false;
- 
--	return mt7996_mcu_sta_ba(dev, &mvif->mt76, params, enable, true);
-+	return mt7996_mcu_sta_ba(dev, &mconf->mt76, params, enable, true);
- }
- 
- int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
-@@ -1359,13 +1347,14 @@ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
- 			 bool enable)
- {
- 	struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv;
--	struct mt7996_vif *mvif = msta->vif;
-+	struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
- 
--	return mt7996_mcu_sta_ba(dev, &mvif->mt76, params, enable, false);
-+	return mt7996_mcu_sta_ba(dev, &mconf->mt76, params, enable, false);
- }
- 
- static void
--mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
-+mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
-+		      struct mt7996_bss_conf *mconf,
- 		      struct ieee80211_sta *sta)
- {
- 	struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
-@@ -1386,9 +1375,9 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
- 		he->he_phy_cap[i] = elem->phy_cap_info[i];
- 	}
- 
--	if (vif->type == NL80211_IFTYPE_AP &&
-+	if (conf->vif->type == NL80211_IFTYPE_AP &&
- 	    (elem->phy_cap_info[1] & IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD))
--		u8p_replace_bits(&he->he_phy_cap[1], vif->bss_conf.he_ldpc,
-+		u8p_replace_bits(&he->he_phy_cap[1], conf->he_ldpc,
- 				 IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD);
- 
- 	mcs_map = sta->deflink.he_cap.he_mcs_nss_supp;
-@@ -1396,16 +1385,16 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
- 	case IEEE80211_STA_RX_BW_160:
- 		if (elem->phy_cap_info[0] &
- 		    IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)
--			mt7996_mcu_set_sta_he_mcs(sta,
-+			mt7996_mcu_set_sta_he_mcs(sta, mconf,
- 						  &he->max_nss_mcs[CMD_HE_MCS_BW8080],
- 						  le16_to_cpu(mcs_map.rx_mcs_80p80));
- 
--		mt7996_mcu_set_sta_he_mcs(sta,
-+		mt7996_mcu_set_sta_he_mcs(sta, mconf,
- 					  &he->max_nss_mcs[CMD_HE_MCS_BW160],
- 					  le16_to_cpu(mcs_map.rx_mcs_160));
- 		fallthrough;
- 	default:
--		mt7996_mcu_set_sta_he_mcs(sta,
-+		mt7996_mcu_set_sta_he_mcs(sta, mconf,
- 					  &he->max_nss_mcs[CMD_HE_MCS_BW80],
- 					  le16_to_cpu(mcs_map.rx_mcs_80));
- 		break;
-@@ -1496,7 +1485,7 @@ mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
- 	struct tlv *tlv;
- #ifdef CONFIG_MTK_VENDOR
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
--	struct mt7996_phy *phy = (struct mt7996_phy *)msta->vif->phy;
-+	struct mt7996_phy *phy = (struct mt7996_phy *)msta->vif->deflink.phy;
- #endif
- 
- 	/* For 6G band, this tlv is necessary to let hw work normally */
-@@ -1553,25 +1542,26 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- 
- static void
- mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
--			struct ieee80211_vif *vif, struct ieee80211_sta *sta)
-+			struct ieee80211_bss_conf *conf,
-+			struct mt7996_bss_conf *mconf,
-+			struct ieee80211_sta *sta)
- {
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	struct mt7996_phy *phy = mvif->phy;
-+	struct mt7996_phy *phy = mconf->phy;
- 	struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
- 	struct sta_rec_muru *muru;
- 	struct tlv *tlv;
- 
--	if (vif->type != NL80211_IFTYPE_STATION &&
--	    vif->type != NL80211_IFTYPE_AP)
-+	if (conf->vif->type != NL80211_IFTYPE_STATION &&
-+	    conf->vif->type != NL80211_IFTYPE_AP)
- 		return;
- 
- 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MURU, sizeof(*muru));
- 
- 	muru = (struct sta_rec_muru *)tlv;
--	muru->cfg.mimo_dl_en = (vif->bss_conf.eht_mu_beamformer ||
--				vif->bss_conf.he_mu_beamformer ||
--				vif->bss_conf.vht_mu_beamformer ||
--				vif->bss_conf.vht_mu_beamformee) &&
-+	muru->cfg.mimo_dl_en = (conf->eht_mu_beamformer ||
-+				conf->he_mu_beamformer ||
-+				conf->vht_mu_beamformer ||
-+				conf->vht_mu_beamformee) &&
- 			       !!(phy->muru_onoff & MUMIMO_DL);
- 	muru->cfg.mimo_ul_en = !!(phy->muru_onoff & MUMIMO_UL);
- 	muru->cfg.ofdma_dl_en = !!(phy->muru_onoff & OFDMA_DL);
-@@ -1612,13 +1602,14 @@ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- }
- 
- static inline bool
--mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif,
-+mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
-+			struct mt7996_bss_conf *mconf,
- 			struct ieee80211_sta *sta, bool bfee)
- {
- 	int sts = hweight16(phy->mt76->chainmask);
- 
--	if (vif->type != NL80211_IFTYPE_STATION &&
--	    vif->type != NL80211_IFTYPE_AP)
-+	if (conf->vif->type != NL80211_IFTYPE_STATION &&
-+	    conf->vif->type != NL80211_IFTYPE_AP)
- 		return false;
- 
- 	if (!bfee && sts < 2)
-@@ -1629,10 +1620,10 @@ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif,
- 		struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem;
- 
- 		if (bfee)
--			return vif->bss_conf.eht_su_beamformee &&
-+			return conf->eht_su_beamformee &&
- 			       EHT_PHY(CAP0_SU_BEAMFORMEE, pe->phy_cap_info[0]);
- 		else
--			return vif->bss_conf.eht_su_beamformer &&
-+			return conf->eht_su_beamformer &&
- 			       EHT_PHY(CAP0_SU_BEAMFORMER, pe->phy_cap_info[0]);
- 	}
- 
-@@ -1640,10 +1631,10 @@ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif,
- 		struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem;
- 
- 		if (bfee)
--			return vif->bss_conf.he_su_beamformee &&
-+			return conf->he_su_beamformee &&
- 			       HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]);
- 		else
--			return vif->bss_conf.he_su_beamformer &&
-+			return conf->he_su_beamformer &&
- 			       HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]);
- 	}
- 
-@@ -1651,10 +1642,10 @@ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif,
- 		u32 cap = sta->deflink.vht_cap.cap;
- 
- 		if (bfee)
--			return vif->bss_conf.vht_su_beamformee &&
-+			return conf->vht_su_beamformee &&
- 			       (cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE);
- 		else
--			return vif->bss_conf.vht_su_beamformer &&
-+			return conf->vht_su_beamformer &&
- 			       (cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE);
- 	}
- 
-@@ -1850,10 +1841,10 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
- 
- static void
- mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
--			struct ieee80211_vif *vif, struct ieee80211_sta *sta)
-+			struct ieee80211_bss_conf *conf, struct mt7996_bss_conf *mconf,
-+			struct ieee80211_sta *sta)
- {
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	struct mt7996_phy *phy = mvif->phy;
-+	struct mt7996_phy *phy = mconf->phy;
- 	int tx_ant = hweight16(phy->mt76->chainmask) - 1;
- 	struct sta_rec_bf *bf;
- 	struct tlv *tlv;
-@@ -1868,7 +1859,7 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- 	if (!(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he))
- 		return;
- 
--	ebf = mt7996_is_ebf_supported(phy, vif, sta, false);
-+	ebf = mt7996_is_ebf_supported(phy, conf, mconf, sta, false);
- 	if (!ebf && !dev->ibf)
- 		return;
- 
-@@ -1880,9 +1871,9 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- 	 * ht: iBF only, since mac80211 lacks of eBF support
- 	 */
- 	if (sta->deflink.eht_cap.has_eht && ebf)
--		mt7996_mcu_sta_bfer_eht(sta, vif, phy, bf);
-+		mt7996_mcu_sta_bfer_eht(sta, conf->vif, phy, bf);
- 	else if (sta->deflink.he_cap.has_he && ebf)
--		mt7996_mcu_sta_bfer_he(sta, vif, phy, bf);
-+		mt7996_mcu_sta_bfer_he(sta, conf->vif, phy, bf);
- 	else if (sta->deflink.vht_cap.vht_supported)
- 		mt7996_mcu_sta_bfer_vht(sta, phy, bf, ebf);
- 	else if (sta->deflink.ht_cap.ht_supported)
-@@ -1921,10 +1912,10 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- 
- static void
- mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
--			struct ieee80211_vif *vif, struct ieee80211_sta *sta)
-+			struct ieee80211_bss_conf *conf,
-+			struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta)
- {
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	struct mt7996_phy *phy = mvif->phy;
-+	struct mt7996_phy *phy = mconf->phy;
- 	int tx_ant = hweight8(phy->mt76->antenna_mask) - 1;
- 	struct sta_rec_bfee *bfee;
- 	struct tlv *tlv;
-@@ -1933,7 +1924,7 @@ mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- 	if (!(sta->deflink.vht_cap.vht_supported || sta->deflink.he_cap.has_he))
- 		return;
- 
--	if (!mt7996_is_ebf_supported(phy, vif, sta, true))
-+	if (!mt7996_is_ebf_supported(phy, conf, mconf, sta, true))
- 		return;
- 
- 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BFEE, sizeof(*bfee));
-@@ -2055,17 +2046,17 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
- 				     MCU_WM_UNI_CMD(RA), true);
- }
- 
--int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif,
-+int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
-+			       struct mt7996_bss_conf *mconf,
- 			       struct ieee80211_sta *sta, void *data, u32 field)
- {
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- 	struct sta_phy_uni *phy = data;
- 	struct sta_rec_ra_fixed_uni *ra;
- 	struct sk_buff *skb;
- 	struct tlv *tlv;
- 
--	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
-+	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
- 					      &msta->wcid,
- 					      MT7996_STA_UPDATE_MAX_SIZE);
- 	if (IS_ERR(skb))
-@@ -2097,12 +2088,13 @@ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif
- }
- 
- static int
--mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif,
-+mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev,
-+			       struct ieee80211_bss_conf *conf,
-+			       struct mt7996_bss_conf *mconf,
- 			       struct ieee80211_sta *sta)
- {
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	struct cfg80211_chan_def *chandef = &mvif->phy->mt76->chandef;
--	struct cfg80211_bitrate_mask *mask = &mvif->bitrate_mask;
-+	struct cfg80211_chan_def *chandef = &mconf->phy->mt76->chandef;
-+	struct cfg80211_bitrate_mask *mask = &mconf->bitrate_mask;
- 	enum nl80211_band band = chandef->chan->band;
- 	struct sta_phy_uni phy = {};
- 	int ret, nrates = 0;
-@@ -2144,7 +2136,7 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif
- 
- 	/* fixed single rate */
- 	if (nrates == 1) {
--		ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy,
-+		ret = mt7996_mcu_set_fixed_field(dev, mconf, sta, &phy,
- 						 RATE_PARAM_FIXED_MCS);
- 		if (ret)
- 			return ret;
-@@ -2166,7 +2158,7 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif
- 		else
- 			mt76_rmw_field(dev, addr, GENMASK(15, 12), phy.sgi);
- 
--		ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy,
-+		ret = mt7996_mcu_set_fixed_field(dev, mconf, sta, &phy,
- 						 RATE_PARAM_FIXED_GI);
- 		if (ret)
- 			return ret;
-@@ -2174,7 +2166,7 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif
- 
- 	/* fixed HE_LTF */
- 	if (mask->control[band].he_ltf != GENMASK(7, 0)) {
--		ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy,
-+		ret = mt7996_mcu_set_fixed_field(dev, mconf, sta, &phy,
- 						 RATE_PARAM_FIXED_HE_LTF);
- 		if (ret)
- 			return ret;
-@@ -2185,13 +2177,14 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif
- 
- static void
- mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
--			     struct ieee80211_vif *vif, struct ieee80211_sta *sta)
-+			     struct ieee80211_bss_conf *conf,
-+			     struct mt7996_bss_conf *mconf,
-+			     struct ieee80211_sta *sta)
- {
- #define INIT_RCPI 180
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	struct mt76_phy *mphy = mvif->phy->mt76;
-+	struct mt76_phy *mphy = mconf->phy->mt76;
- 	struct cfg80211_chan_def *chandef = &mphy->chandef;
--	struct cfg80211_bitrate_mask *mask = &mvif->bitrate_mask;
-+	struct cfg80211_bitrate_mask *mask = &mconf->bitrate_mask;
- 	enum nl80211_band band = chandef->chan->band;
- 	struct sta_rec_ra_uni *ra;
- 	struct tlv *tlv;
-@@ -2203,7 +2196,7 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
- 
- 	ra->valid = true;
- 	ra->auto_rate = true;
--	ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, sta);
-+	ra->phy_mode = mt76_connac_get_phy_mode(mphy, conf->vif, band, sta);
- 	ra->channel = chandef->chan->hw_value;
- 	ra->bw = (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_320) ?
- 		 CMD_CBW_320MHZ : sta->deflink.bandwidth;
-@@ -2242,7 +2235,7 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
- 			cap |= STA_CAP_TX_STBC;
- 		if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
- 			cap |= STA_CAP_RX_STBC;
--		if (vif->bss_conf.ht_ldpc &&
-+		if (conf->ht_ldpc &&
- 		    (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING))
- 			cap |= STA_CAP_LDPC;
- 
-@@ -2268,7 +2261,7 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
- 			cap |= STA_CAP_VHT_TX_STBC;
- 		if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1)
- 			cap |= STA_CAP_VHT_RX_STBC;
--		if (vif->bss_conf.vht_ldpc &&
-+		if (conf->vht_ldpc &&
- 		    (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC))
- 			cap |= STA_CAP_VHT_LDPC;
- 
-@@ -2289,15 +2282,16 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
- 	memset(ra->rx_rcpi, INIT_RCPI, sizeof(ra->rx_rcpi));
- }
- 
--int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
-+int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
-+			     struct ieee80211_bss_conf *conf,
-+			     struct mt7996_bss_conf *mconf,
- 			     struct ieee80211_sta *sta, bool changed)
- {
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- 	struct sk_buff *skb;
- 	int ret;
- 
--	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
-+	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
- 					      &msta->wcid,
- 					      MT7996_STA_UPDATE_MAX_SIZE);
- 	if (IS_ERR(skb))
-@@ -2308,26 +2302,27 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- 	 * update sta_rec_he here.
- 	 */
- 	if (changed)
--		mt7996_mcu_sta_he_tlv(skb, vif, sta);
-+		mt7996_mcu_sta_he_tlv(skb, conf, mconf, sta);
- 
- 	/* sta_rec_ra accommodates BW, NSS and only MCS range format
- 	 * i.e 0-{7,8,9} for VHT.
- 	 */
--	mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, vif, sta);
-+	mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, conf, mconf, sta);
- 
- 	ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
- 				    MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
- 	if (ret)
- 		return ret;
- 
--	return mt7996_mcu_add_rate_ctrl_fixed(dev, vif, sta);
-+	return mt7996_mcu_add_rate_ctrl_fixed(dev, conf, mconf, sta);
- }
- 
- static int
--mt7996_mcu_sta_init_vow(struct mt7996_phy *phy, struct mt7996_sta *msta)
-+mt7996_mcu_sta_init_vow(struct mt7996_bss_conf *mconf, struct mt7996_sta *msta)
- {
-+	struct mt7996_phy *phy = mconf->phy;
- 	struct mt7996_vow_sta_ctrl *vow = &msta->vow;
--	u8 omac_idx = msta->vif->mt76.omac_idx;
-+	u8 omac_idx = mconf->mt76.omac_idx;
- 	int ret;
- 
- 	/* Assignment of STA BSS group index aligns FW.
-@@ -2344,20 +2339,22 @@ mt7996_mcu_sta_init_vow(struct mt7996_phy *phy, struct mt7996_sta *msta)
- 	vow->drr_quantum[IEEE80211_AC_BE] = VOW_DRR_QUANTUM_IDX2;
- 	vow->drr_quantum[IEEE80211_AC_BK] = VOW_DRR_QUANTUM_IDX2;
- 
--	ret = mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_BSS_GROUP);
-+	ret = mt7996_mcu_set_vow_drr_ctrl(phy, mconf, msta, VOW_DRR_CTRL_STA_BSS_GROUP);
- 	if (ret)
- 		return ret;
- 
--	ret = mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_PAUSE);
-+	ret = mt7996_mcu_set_vow_drr_ctrl(phy, mconf, msta, VOW_DRR_CTRL_STA_PAUSE);
- 	if (ret)
- 		return ret;
- 
--	return mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_ALL);
-+	return mt7996_mcu_set_vow_drr_ctrl(phy, mconf, msta, VOW_DRR_CTRL_STA_ALL);
- }
- 
--int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
--		       struct ieee80211_sta *sta, bool enable, bool newly)
-+int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
-+		       struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta,
-+		       bool enable, bool newly)
- {
-+	struct ieee80211_vif *vif = conf->vif;
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_sta *msta;
- 	struct sk_buff *skb;
-@@ -2365,7 +2362,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- 
- 	msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta;
- 
--	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
-+	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
- 					      &msta->wcid,
- 					      MT7996_STA_UPDATE_MAX_SIZE);
- 	if (IS_ERR(skb))
-@@ -2387,7 +2384,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- 		/* starec hdrt mode */
- 		mt7996_mcu_sta_hdrt_tlv(dev, skb);
- 		/* starec bfer */
--		mt7996_mcu_sta_bfer_tlv(dev, skb, vif, sta);
-+		mt7996_mcu_sta_bfer_tlv(dev, skb, conf, mconf, sta);
- 		/* starec ht */
- 		mt7996_mcu_sta_ht_tlv(skb, sta);
- 		/* starec vht */
-@@ -2397,18 +2394,18 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- 		/* starec amsdu */
- 		mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, sta);
- 		/* starec he */
--		mt7996_mcu_sta_he_tlv(skb, vif, sta);
-+		mt7996_mcu_sta_he_tlv(skb, conf, mconf, sta);
- 		/* starec he 6g*/
- 		mt7996_mcu_sta_he_6g_tlv(skb, sta);
- 		/* starec eht */
- 		mt7996_mcu_sta_eht_tlv(skb, sta);
- 		/* starec muru */
--		mt7996_mcu_sta_muru_tlv(dev, skb, vif, sta);
-+		mt7996_mcu_sta_muru_tlv(dev, skb, conf, mconf, sta);
- 		/* starec bfee */
--		mt7996_mcu_sta_bfee_tlv(dev, skb, vif, sta);
-+		mt7996_mcu_sta_bfee_tlv(dev, skb, conf, mconf, sta);
- 	}
- 
--	ret = mt7996_mcu_sta_init_vow(mvif->phy, msta);
-+	ret = mt7996_mcu_sta_init_vow(mconf, msta);
- 	if (ret) {
- 		dev_kfree_skb(skb);
- 		return ret;
-@@ -2463,16 +2460,15 @@ mt7996_mcu_sta_key_tlv(struct mt76_wcid *wcid,
- 	return 0;
- }
- 
--int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
-+int mt7996_mcu_add_key(struct mt76_dev *dev, struct mt7996_bss_conf *mconf,
- 		       struct ieee80211_key_conf *key, int mcu_cmd,
- 		       struct mt76_wcid *wcid, enum set_key_cmd cmd)
- {
--	struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
- 	struct sk_buff *skb;
- 	int ret;
- 
--	skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid,
--					      MT7996_STA_UPDATE_MAX_SIZE);
-+	skb = __mt76_connac_mcu_alloc_sta_req(dev, (struct mt76_vif *)mconf,
-+					      wcid, MT7996_STA_UPDATE_MAX_SIZE);
- 	if (IS_ERR(skb))
- 		return PTR_ERR(skb);
- 
-@@ -2483,17 +2479,18 @@ int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
- 	return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true);
- }
- 
--static int mt7996_mcu_get_pn(struct mt7996_dev *dev, struct ieee80211_vif *vif,
--			     u8 *pn)
-+static int mt7996_mcu_get_pn(struct mt7996_dev *dev,
-+			     struct ieee80211_bss_conf *conf,
-+			     struct mt7996_bss_conf *mconf, u8 *pn)
- {
- #define TSC_TYPE_BIGTK_PN 2
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)conf->vif->drv_priv;
- 	struct sta_rec_pn_info *pn_info;
- 	struct sk_buff *skb, *rskb;
- 	struct tlv *tlv;
- 	int ret;
- 
--	skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, &mvif->sta.wcid);
-+	skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76, &mvif->sta.wcid);
- 	if (IS_ERR(skb))
- 		return PTR_ERR(skb);
- 
-@@ -2517,10 +2514,11 @@ static int mt7996_mcu_get_pn(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- 	return 0;
- }
- 
--int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif,
-+int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev,
-+			       struct ieee80211_bss_conf *conf,
-+			       struct mt7996_bss_conf *mconf,
- 			       struct ieee80211_key_conf *key)
- {
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_mcu_bcn_prot_tlv *bcn_prot;
- 	struct sk_buff *skb;
- 	struct tlv *tlv;
-@@ -2529,7 +2527,7 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif
- 		  sizeof(struct mt7996_mcu_bcn_prot_tlv);
- 	int ret;
- 
--	skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76, len);
-+	skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76, len);
- 	if (IS_ERR(skb))
- 		return PTR_ERR(skb);
- 
-@@ -2537,7 +2535,7 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif
- 
- 	bcn_prot = (struct mt7996_mcu_bcn_prot_tlv *)tlv;
- 
--	ret = mt7996_mcu_get_pn(dev, vif, pn);
-+	ret = mt7996_mcu_get_pn(dev, conf, mconf, pn);
- 	if (ret) {
- 		dev_kfree_skb(skb);
- 		return ret;
-@@ -2570,10 +2568,10 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif
- 				     MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
- }
- int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
--			    struct ieee80211_vif *vif, bool enable)
-+			    struct ieee80211_bss_conf *conf,
-+			    struct mt7996_bss_conf *mconf, bool enable)
- {
- 	struct mt7996_dev *dev = phy->dev;
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct {
- 		struct req_hdr {
- 			u8 omac_idx;
-@@ -2589,8 +2587,8 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
- 		} __packed tlv;
- 	} data = {
- 		.hdr = {
--			.omac_idx = mvif->mt76.omac_idx,
--			.band_idx = mvif->mt76.band_idx,
-+			.omac_idx = mconf->mt76.omac_idx,
-+			.band_idx = mconf->mt76.band_idx,
- 		},
- 		.tlv = {
- 			.tag = cpu_to_le16(DEV_INFO_ACTIVE),
-@@ -2599,16 +2597,16 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
- 		},
- 	};
- 
--	if (mvif->mt76.omac_idx >= REPEATER_BSSID_START)
--		return mt7996_mcu_muar_config(phy, vif, false, enable);
-+	if (mconf->mt76.omac_idx >= REPEATER_BSSID_START)
-+		return mt7996_mcu_muar_config(phy, conf, mconf, false, enable);
- 
--	memcpy(data.tlv.omac_addr, vif->addr, ETH_ALEN);
-+	memcpy(data.tlv.omac_addr, conf->addr, ETH_ALEN);
- 	return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(DEV_INFO_UPDATE),
- 				 &data, sizeof(data), true);
- }
- 
- static void
--mt7996_mcu_beacon_cntdwn(struct ieee80211_vif *vif, struct sk_buff *rskb,
-+mt7996_mcu_beacon_cntdwn(struct ieee80211_bss_conf *conf, struct sk_buff *rskb,
- 			 struct sk_buff *skb,
- 			 struct ieee80211_mutable_offsets *offs)
- {
-@@ -2619,7 +2617,7 @@ mt7996_mcu_beacon_cntdwn(struct ieee80211_vif *vif, struct sk_buff *rskb,
- 	if (!offs->cntdwn_counter_offs[0])
- 		return;
- 
--	tag = vif->bss_conf.csa_active ? UNI_BSS_INFO_BCN_CSA : UNI_BSS_INFO_BCN_BCC;
-+	tag = conf->csa_active ? UNI_BSS_INFO_BCN_CSA : UNI_BSS_INFO_BCN_BCC;
- 
- 	tlv = mt7996_mcu_add_uni_tlv(rskb, tag, sizeof(*info));
- 
-@@ -2629,14 +2627,15 @@ mt7996_mcu_beacon_cntdwn(struct ieee80211_vif *vif, struct sk_buff *rskb,
- 
- static void
- mt7996_mcu_beacon_mbss(struct sk_buff *rskb, struct sk_buff *skb,
--		       struct ieee80211_vif *vif, struct bss_bcn_content_tlv *bcn,
-+		       struct ieee80211_bss_conf *conf,
-+		       struct bss_bcn_content_tlv *bcn,
- 		       struct ieee80211_mutable_offsets *offs)
- {
- 	struct bss_bcn_mbss_tlv *mbss;
- 	const struct element *elem;
- 	struct tlv *tlv;
- 
--	if (!vif->bss_conf.bssid_indicator)
-+	if (!conf->bssid_indicator)
- 		return;
- 
- 	tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_MBSSID, sizeof(*mbss));
-@@ -2681,7 +2680,7 @@ mt7996_mcu_beacon_mbss(struct sk_buff *rskb, struct sk_buff *skb,
- }
- 
- static void
--mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_vif *vif,
-+mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
- 		       struct sk_buff *rskb, struct sk_buff *skb,
- 		       struct bss_bcn_content_tlv *bcn,
- 		       struct ieee80211_mutable_offsets *offs)
-@@ -2695,9 +2694,9 @@ mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- 	if (offs->cntdwn_counter_offs[0]) {
- 		u16 offset = offs->cntdwn_counter_offs[0];
- 
--		if (vif->bss_conf.csa_active)
-+		if (conf->csa_active)
- 			bcn->csa_ie_pos = cpu_to_le16(offset - 4);
--		if (vif->bss_conf.color_change_active)
-+		if (conf->color_change_active)
- 			bcn->bcc_ie_pos = cpu_to_le16(offset - 3);
- 	}
- 
-@@ -2709,11 +2708,11 @@ mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- }
- 
- int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
--			  struct ieee80211_vif *vif, int en)
-+			  struct ieee80211_bss_conf *conf,
-+			  struct mt7996_bss_conf *mconf, int en)
- {
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct ieee80211_mutable_offsets offs;
- 	struct ieee80211_tx_info *info;
- 	struct sk_buff *skb, *rskb;
-@@ -2721,15 +2720,15 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
- 	struct bss_bcn_content_tlv *bcn;
- 	int len;
- 
--	if (vif->bss_conf.nontransmitted)
-+	if (conf->nontransmitted)
- 		return 0;
- 
--	rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
-+	rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76,
- 					  MT7996_MAX_BSS_OFFLOAD_SIZE);
- 	if (IS_ERR(rskb))
- 		return PTR_ERR(rskb);
- 
--	skb = ieee80211_beacon_get_template(hw, vif, &offs, 0);
-+	skb = ieee80211_beacon_get_template(hw, conf->vif, &offs, 0);
- 	if (!skb) {
- 		dev_kfree_skb(rskb);
- 		return -EINVAL;
-@@ -2752,9 +2751,9 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
- 	if (!en)
- 		goto out;
- 
--	mt7996_mcu_beacon_cont(dev, vif, rskb, skb, bcn, &offs);
--	mt7996_mcu_beacon_mbss(rskb, skb, vif, bcn, &offs);
--	mt7996_mcu_beacon_cntdwn(vif, rskb, skb, &offs);
-+	mt7996_mcu_beacon_cont(dev, conf, rskb, skb, bcn, &offs);
-+	mt7996_mcu_beacon_mbss(rskb, skb, conf, bcn, &offs);
-+	mt7996_mcu_beacon_cntdwn(conf, rskb, skb, &offs);
- out:
- 	dev_kfree_skb(skb);
- 	return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb,
-@@ -2762,14 +2761,15 @@ out:
- }
- 
- int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
--				    struct ieee80211_vif *vif, u32 changed)
-+				    struct ieee80211_bss_conf *conf,
-+				    struct mt7996_bss_conf *mconf, u32 changed)
- {
- #define OFFLOAD_TX_MODE_SU	BIT(0)
- #define OFFLOAD_TX_MODE_MU	BIT(1)
- 	struct ieee80211_hw *hw = mt76_hw(dev);
-+	struct ieee80211_vif *vif = conf->vif;
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	struct cfg80211_chan_def *chandef = &mvif->phy->mt76->chandef;
-+	struct cfg80211_chan_def *chandef = &mconf->phy->mt76->chandef;
- 	enum nl80211_band band = chandef->chan->band;
- 	struct mt76_wcid *wcid = &dev->mt76.global_wcid;
- 	struct bss_inband_discovery_tlv *discov;
-@@ -2779,20 +2779,20 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
- 	u8 *buf, interval;
- 	int len;
- 
--	if (vif->bss_conf.nontransmitted)
-+	if (conf->nontransmitted)
- 		return 0;
- 
--	rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
-+	rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76,
- 					  MT7996_MAX_BSS_OFFLOAD_SIZE);
- 	if (IS_ERR(rskb))
- 		return PTR_ERR(rskb);
- 
- 	if (changed & BSS_CHANGED_FILS_DISCOVERY) {
--		interval = vif->bss_conf.fils_discovery.max_interval;
-+		interval = conf->fils_discovery.max_interval;
- 		skb = ieee80211_get_fils_discovery_tmpl(hw, vif);
- 	} else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP &&
--		   vif->bss_conf.unsol_bcast_probe_resp_interval) {
--		interval = vif->bss_conf.unsol_bcast_probe_resp_interval;
-+		   conf->unsol_bcast_probe_resp_interval) {
-+		interval = conf->unsol_bcast_probe_resp_interval;
- 		skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif);
- 	}
- 
-@@ -3456,7 +3456,7 @@ int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans)
- 				     MCU_WM_UNI_CMD(RX_HDR_TRANS), true);
- }
- 
--int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif)
-+int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf)
- {
- #define MCU_EDCA_AC_PARAM	0
- #define WMM_AIFS_SET		BIT(0)
-@@ -3465,12 +3465,11 @@ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif)
- #define WMM_TXOP_SET		BIT(3)
- #define WMM_PARAM_SET		(WMM_AIFS_SET | WMM_CW_MIN_SET | \
- 				 WMM_CW_MAX_SET | WMM_TXOP_SET)
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct {
- 		u8 bss_idx;
- 		u8 __rsv[3];
- 	} __packed hdr = {
--		.bss_idx = mvif->mt76.idx,
-+		.bss_idx = mconf->mt76.idx,
- 	};
- 	struct sk_buff *skb;
- 	int len = sizeof(hdr) + IEEE80211_NUM_ACS * sizeof(struct edca);
-@@ -3483,7 +3482,7 @@ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif)
- 	skb_put_data(skb, &hdr, sizeof(hdr));
- 
- 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
--		struct ieee80211_tx_queue_params *q = &mvif->queue_params[ac];
-+		struct ieee80211_tx_queue_params *q = &mconf->queue_params[ac];
- 		struct edca *e;
- 		struct tlv *tlv;
- 
-@@ -4496,12 +4495,12 @@ mt7996_mcu_set_obss_spr_pd(struct mt7996_phy *phy,
- }
- 
- static int
--mt7996_mcu_set_obss_spr_siga(struct mt7996_phy *phy, struct ieee80211_vif *vif,
-+mt7996_mcu_set_obss_spr_siga(struct mt7996_phy *phy,
-+			     struct mt7996_bss_conf *mconf,
- 			     struct ieee80211_he_obss_pd *he_obss_pd)
- {
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_dev *dev = phy->dev;
--	u8 omac = mvif->mt76.omac_idx;
-+	u8 omac = mconf->mt76.omac_idx;
- 	struct {
- 		u8 band_idx;
- 		u8 __rsv[3];
-@@ -4573,7 +4572,8 @@ mt7996_mcu_set_obss_spr_bitmap(struct mt7996_phy *phy,
- 				 sizeof(req), true);
- }
- 
--int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
-+int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy,
-+			    struct mt7996_bss_conf *mconf,
- 			    struct ieee80211_he_obss_pd *he_obss_pd)
- {
- 	int ret;
-@@ -4607,7 +4607,7 @@ int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
- 		return ret;
- 
- 	/* Set SR prohibit */
--	ret = mt7996_mcu_set_obss_spr_siga(phy, vif, he_obss_pd);
-+	ret = mt7996_mcu_set_obss_spr_siga(phy, mconf, he_obss_pd);
- 	if (ret)
- 		return ret;
- 
-@@ -4615,16 +4615,16 @@ int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
- 	return mt7996_mcu_set_obss_spr_bitmap(phy, he_obss_pd);
- }
- 
--int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct ieee80211_vif *vif,
-+int mt7996_mcu_update_bss_color(struct mt7996_dev *dev,
-+				struct mt7996_bss_conf *mconf,
- 				struct cfg80211_he_bss_color *he_bss_color)
- {
- 	int len = sizeof(struct bss_req_hdr) + sizeof(struct bss_color_tlv);
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct bss_color_tlv *bss_color;
- 	struct sk_buff *skb;
- 	struct tlv *tlv;
- 
--	skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76, len);
-+	skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76, len);
- 	if (IS_ERR(skb))
- 		return PTR_ERR(skb);
- 
-@@ -4643,7 +4643,7 @@ int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct ieee80211_vif *vi
- #define TWT_AGRT_PROTECT	BIT(2)
- 
- int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
--			       struct mt7996_vif *mvif,
-+			       struct mt7996_bss_conf *mconf,
- 			       struct mt7996_twt_flow *flow,
- 			       int cmd)
- {
-@@ -4674,12 +4674,12 @@ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
- 		.len = cpu_to_le16(sizeof(req) - 4),
- 		.tbl_idx = flow->table_id,
- 		.cmd = cmd,
--		.own_mac_idx = mvif->mt76.omac_idx,
-+		.own_mac_idx = mconf->mt76.omac_idx,
- 		.flowid = flow->id,
- 		.peer_id = cpu_to_le16(flow->wcid),
- 		.duration = flow->duration,
--		.bss = mvif->mt76.idx,
--		.bss_idx = mvif->mt76.idx,
-+		.bss = mconf->mt76.idx,
-+		.bss_idx = mconf->mt76.idx,
- 		.start_tsf = cpu_to_le64(flow->tsf),
- 		.mantissa = flow->mantissa,
- 		.exponent = flow->exp,
-@@ -4810,15 +4810,15 @@ int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev, bool disable
- 
- int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
- 				     struct ieee80211_vif *vif,
-+				     struct mt7996_bss_conf *mconf,
- 				     struct ieee80211_sta *sta)
- {
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_sta *msta;
- 	struct sk_buff *skb;
- 
--	msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta;
-+	msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mconf->vif->sta;
- 
--	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
-+	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
- 					      &msta->wcid,
- 					      MT7996_STA_UPDATE_MAX_SIZE);
- 	if (IS_ERR(skb))
-@@ -5462,8 +5462,9 @@ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
- 				 &req, sizeof(req), false);
- }
- 
--int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
--	                        enum vow_drr_ctrl_id id)
-+int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy,
-+				struct mt7996_bss_conf *mconf,
-+				struct mt7996_sta *msta, enum vow_drr_ctrl_id id)
- {
- 	struct mt7996_vow_sta_ctrl *vow = msta ? &msta->vow : NULL;
- 	u32 val = 0;
-@@ -5489,9 +5490,9 @@ int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
- 		.len = cpu_to_le16(sizeof(req) - 4),
- 		.wlan_idx = cpu_to_le16(msta ? msta->wcid.idx : 0),
- 		.band_idx = phy->mt76->band_idx,
--		.wmm_idx = msta ? msta->vif->mt76.wmm_idx : 0,
-+		.wmm_idx = msta ? mconf->mt76.wmm_idx : 0,
- 		.ctrl_id = cpu_to_le32(id),
--		.omac_idx = msta ? msta->vif->mt76.omac_idx : 0
-+		.omac_idx = msta ? mconf->mt76.omac_idx : 0
- 	};
- 
- 	switch (id) {
-@@ -5679,7 +5680,7 @@ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
- {
- 	u8 mode, val;
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	struct mt7996_phy *phy =  mvif->phy;
-+	struct mt7996_phy *phy =  mvif->deflink.phy;
- 
- 	mode = FIELD_GET(RATE_CFG_MODE, *((u32 *)data));
- 	val = FIELD_GET(RATE_CFG_VAL, *((u32 *)data));
-@@ -5710,11 +5711,11 @@ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
- void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
- {
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	struct ieee80211_hw *hw = mvif->phy->mt76->hw;
-+	struct ieee80211_hw *hw = mvif->deflink.phy->mt76->hw;
- 	u8 val = *((u8 *)data);
- 
- 	vif->bss_conf.enable_beacon = val;
- 
--	mt7996_mcu_add_beacon(hw, vif, val);
-+	mt7996_mcu_add_beacon(hw, &vif->bss_conf, &mvif->deflink, val);
- }
- #endif
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 2b266d1..6b03ee1 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -324,18 +324,25 @@ struct mt7996_sta {
- 	struct mt7996_vow_sta_ctrl vow;
- };
- 
--struct mt7996_vif {
-+struct mt7996_bss_conf {
- 	struct mt76_vif mt76; /* must be first */
- 
--	struct mt7996_sta sta;
-+	struct mt7996_vif *vif;
- 	struct mt7996_phy *phy;
--
- 	struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
- 	struct cfg80211_bitrate_mask bitrate_mask;
- 
- 	struct mt7996_chanctx *chanctx;
- };
- 
-+struct mt7996_vif {
-+	struct mt7996_bss_conf deflink;
-+	struct mt7996_bss_conf __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
-+
-+	struct mt7996_sta sta;
-+	struct mt7996_dev *dev;
-+};
-+
- /* crash-dump */
- struct mt7996_crash_data {
- 	guid_t guid;
-@@ -769,6 +776,13 @@ mt7996_chanctx_get(struct ieee80211_chanctx_conf *ctx)
- 	return (struct mt7996_chanctx *)&ctx->drv_priv;
- }
- 
-+static inline struct mt7996_bss_conf *
-+mconf_dereference_protected(struct mt7996_vif *mvif, u8 link_id)
-+{
-+	return rcu_dereference_protected(mvif->link[link_id],
-+					 lockdep_is_held(&mvif->dev->mt76.mutex));
-+}
-+
- extern const struct ieee80211_ops mt7996_ops;
- extern struct pci_driver mt7996_pci_driver;
- extern struct pci_driver mt7996_hif_driver;
-@@ -779,7 +793,7 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
- void mt7996_wfsys_reset(struct mt7996_dev *dev);
- void mt7996_rro_hw_init(struct mt7996_dev *dev);
- irqreturn_t mt7996_irq_handler(int irq, void *dev_instance);
--u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
-+u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_bss_conf *mconf);
- int mt7996_register_device(struct mt7996_dev *dev);
- void mt7996_unregister_device(struct mt7996_dev *dev);
- const char *mt7996_eeprom_name(struct mt7996_dev *dev);
-@@ -805,37 +819,47 @@ int mt7996_run(struct ieee80211_hw *hw);
- int mt7996_mcu_init(struct mt7996_dev *dev);
- int mt7996_mcu_init_firmware(struct mt7996_dev *dev);
- int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
--			       struct mt7996_vif *mvif,
-+			       struct mt7996_bss_conf *mconf,
- 			       struct mt7996_twt_flow *flow,
- 			       int cmd);
- int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
--			    struct ieee80211_vif *vif, bool enable);
-+			    struct ieee80211_bss_conf *conf,
-+			    struct mt7996_bss_conf *mconf, bool enable);
- int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
--			    struct ieee80211_vif *vif, int enable);
--int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
--		       struct ieee80211_sta *sta, bool enable, bool newly);
-+			    struct ieee80211_bss_conf *conf,
-+			    struct mt7996_bss_conf *mconf, int enable);
-+int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
-+		       struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta,
-+		       bool enable, bool newly);
- int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
- 			 struct ieee80211_ampdu_params *params,
- 			 bool add);
- int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
- 			 struct ieee80211_ampdu_params *params,
- 			 bool add);
--int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct ieee80211_vif *vif,
-+int mt7996_mcu_update_bss_color(struct mt7996_dev *dev,
-+				struct mt7996_bss_conf *mconf,
- 				struct cfg80211_he_bss_color *he_bss_color);
--int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
--			  int enable);
-+int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
-+			  struct ieee80211_bss_conf *conf,
-+			  struct mt7996_bss_conf *mconf, int en);
- int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
--				    struct ieee80211_vif *vif, u32 changed);
--int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
-+				    struct ieee80211_bss_conf *conf,
-+				    struct mt7996_bss_conf *mconf, u32 changed);
-+int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy,
-+			    struct mt7996_bss_conf *mconf,
- 			    struct ieee80211_he_obss_pd *he_obss_pd);
--int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
-+int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
-+			     struct ieee80211_bss_conf *conf,
-+			     struct mt7996_bss_conf *mconf,
- 			     struct ieee80211_sta *sta, bool changed);
- int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef);
- int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag);
--int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif);
-+int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf);
- int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
- 				   void *data, u16 version);
--int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif,
-+int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
-+			       struct mt7996_bss_conf *mconf,
- 			       struct ieee80211_sta *sta, void *data, u32 field);
- int mt7996_mcu_set_eeprom(struct mt7996_dev *dev);
- int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf);
-@@ -850,7 +874,7 @@ int mt7996_mcu_set_radar_th(struct mt7996_dev *dev, int index,
- 			    const struct mt7996_dfs_pattern *pattern);
- int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable);
- int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val);
--int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif);
-+int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct mt7996_bss_conf *mconf);
- int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch);
- int mt7996_mcu_get_temperature(struct mt7996_phy *phy);
- int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state);
-@@ -890,8 +914,9 @@ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
- int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
- void mt7996_mcu_scs_sta_poll(struct work_struct *work);
- int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable);
--int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
--	                        enum vow_drr_ctrl_id id);
-+int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy,
-+				struct mt7996_bss_conf *mconf,
-+				struct mt7996_sta *msta, enum vow_drr_ctrl_id id);
- int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy);
- void mt7996_mcu_wmm_pbc_work(struct work_struct *work);
- 
-@@ -1000,13 +1025,16 @@ void mt7996_update_channel(struct mt76_phy *mphy);
- int mt7996_init_debugfs(struct mt7996_phy *phy);
- void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int len);
- bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len);
--int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
-+int mt7996_mcu_add_key(struct mt76_dev *dev, struct mt7996_bss_conf *mconf,
- 		       struct ieee80211_key_conf *key, int mcu_cmd,
- 		       struct mt76_wcid *wcid, enum set_key_cmd cmd);
--int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif,
-+int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev,
-+			       struct ieee80211_bss_conf *conf,
-+			       struct mt7996_bss_conf *mconf,
- 			       struct ieee80211_key_conf *key);
- int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
- 				     struct ieee80211_vif *vif,
-+				     struct mt7996_bss_conf *mconf,
- 				     struct ieee80211_sta *sta);
- int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode);
- int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap);
-diff --git a/mt7996/testmode.c b/mt7996/testmode.c
-index 784a8be..bf55b43 100644
---- a/mt7996/testmode.c
-+++ b/mt7996/testmode.c
-@@ -223,6 +223,7 @@ static void
- mt7996_tm_init(struct mt7996_phy *phy, bool en)
- {
- 	struct mt7996_dev *dev = phy->dev;
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)phy->monitor_vif->drv_priv;
- 	u8 rf_test_mode = en ? RF_OPER_RF_TEST : RF_OPER_NORMAL;
- 
- 	if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
-@@ -234,8 +235,8 @@ mt7996_tm_init(struct mt7996_phy *phy, bool en)
- 
- 	mt7996_tm_rf_switch_mode(dev, rf_test_mode);
- 
--	mt7996_mcu_add_bss_info(phy, phy->monitor_vif, en);
--	mt7996_mcu_add_sta(dev, phy->monitor_vif, NULL, en, false);
-+	mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, en);
-+	mt7996_mcu_add_sta(dev, &phy->monitor_vif->bss_conf, &mvif->deflink, NULL, en, false);
- 
- 	mt7996_tm_set(dev, SET_ID(BAND_IDX), phy->mt76->band_idx);
- 
-@@ -1179,13 +1180,13 @@ mt7996_tm_txbf_init(struct mt7996_phy *phy, u16 *val)
- 	mt7996_tm_set_mac_addr(dev, td->addr[2], SET_ID(BSSID));
- 
- 	/* bss idx & omac idx should be set to band idx for ibf cal */
--	mvif->mt76.idx = band_idx;
--	dev->mt76.vif_mask |= BIT_ULL(mvif->mt76.idx);
--	mvif->mt76.omac_idx = band_idx;
--	phy->omac_mask |= BIT_ULL(mvif->mt76.omac_idx);
-+	mvif->deflink.mt76.idx = band_idx;
-+	dev->mt76.vif_mask |= BIT_ULL(mvif->deflink.mt76.idx);
-+	mvif->deflink.mt76.omac_idx = band_idx;
-+	phy->omac_mask |= BIT_ULL(mvif->deflink.mt76.omac_idx);
- 
--	mt7996_mcu_add_dev_info(phy, phy->monitor_vif, true);
--	mt7996_mcu_add_bss_info(phy, phy->monitor_vif, true);
-+	mt7996_mcu_add_dev_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, true);
-+	mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, true);
- 
- 	if (td->ibf) {
- 		if (td->is_txbf_dut) {
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0086-mtk-mt76-add-support-to-enable-index-FW-log-for-Cons.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0086-mtk-mt76-add-support-to-enable-index-FW-log-for-Cons.patch
new file mode 100644
index 0000000..58d48b8
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0086-mtk-mt76-add-support-to-enable-index-FW-log-for-Cons.patch
@@ -0,0 +1,628 @@
+From 575f1d50cf1917a498582fa07f352c7723f01e8d Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Thu, 11 Jan 2024 08:55:13 +0800
+Subject: [PATCH 086/199] mtk: mt76: add support to enable index FW log for
+ ConsysPlanet
+
+Add support to enable and record index FW log, which is the input for ConsysPlanet, via mt76-test command.
+
+Usage:
+1. Foreground logging
+	1) Start: mt76-test phy0 idxlog
+	2) Stop: Ctrl + C
+2. Background logging
+	1) Start: mt76-test phy0 idxlog &
+	2) Stop: killall mt76-test
+3. Logging with FW Parser
+	1) Start Ethernet recording of FW Parser.
+	2) Start: mt76-test phy0 idxlog <PC's IP Address>
+	3) Stop: Ctrl + C
+	4) Stop FW Parser.
+
+Log Files
+- FW Log: FW text message
+	- Location: /tmp/log/clog_(timestamp)/WIFI_FW_(timestamp).clog
+- Driver Log: log message printed at driver layer
+	- Location: /tmp/log/clog_(timestamp)/WIFI_KERNEL_(timestamp).clog
+
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ mt7996/debugfs.c  |  90 +++++++++++++++++--
+ mt7996/mac.c      |  10 ++-
+ mt7996/mcu.c      |  34 +++++++-
+ mt7996/mcu.h      |   4 +-
+ mt7996/mt7996.h   |   3 +
+ tools/fwlog.c     | 218 ++++++++++++++++++++++++++++++++++------------
+ tools/main.c      |   2 +
+ tools/mt76-test.h |   3 +
+ 8 files changed, 295 insertions(+), 69 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 8f8608fa..7395f6df 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -430,8 +430,8 @@ create_buf_file_cb(const char *filename, struct dentry *parent, umode_t mode,
+ {
+ 	struct dentry *f;
+ 
+-	f = debugfs_create_file("fwlog_data", mode, parent, buf,
+-				&relay_file_operations);
++	f = debugfs_create_file(filename[0] == 'f' ? "fwlog_data" : "idxlog_data",
++	                        mode, parent, buf, &relay_file_operations);
+ 	if (IS_ERR(f))
+ 		return NULL;
+ 
+@@ -522,6 +522,53 @@ mt7996_fw_debug_bin_get(void *data, u64 *val)
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_bin, mt7996_fw_debug_bin_get,
+ 			 mt7996_fw_debug_bin_set, "%lld\n");
+ 
++static int
++mt7996_idxlog_enable_get(void *data, u64 *val)
++{
++	struct mt7996_dev *dev = data;
++
++	*val = dev->idxlog_enable;
++
++	return 0;
++}
++
++static int
++mt7996_idxlog_enable_set(void *data, u64 val)
++{
++	static struct rchan_callbacks relay_cb = {
++		.create_buf_file = create_buf_file_cb,
++		.remove_buf_file = remove_buf_file_cb,
++	};
++	struct mt7996_dev *dev = data;
++
++	if (dev->idxlog_enable == !!val)
++		return 0;
++
++	if (!dev->relay_idxlog) {
++		dev->relay_idxlog = relay_open("idxlog_data", dev->debugfs_dir,
++		                               1500, 512, &relay_cb, NULL);
++		if (!dev->relay_idxlog)
++			return -ENOMEM;
++	}
++
++	dev->idxlog_enable = !!val;
++
++	if (val) {
++		int ret = mt7996_mcu_fw_time_sync(&dev->mt76);
++		if (ret)
++			return ret;
++
++		/* Reset relay channel only when it is not being written to. */
++		relay_reset(dev->relay_idxlog);
++	}
++
++	return mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM,
++	                                val ? MCU_FW_LOG_RELAY_IDX : 0);
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_idxlog_enable, mt7996_idxlog_enable_get,
++	                 mt7996_idxlog_enable_set, "%llu\n");
++
+ static int
+ mt7996_fw_util_wa_show(struct seq_file *file, void *data)
+ {
+@@ -1029,6 +1076,7 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ 	debugfs_create_file("fw_debug_wm", 0600, dir, dev, &fops_fw_debug_wm);
+ 	debugfs_create_file("fw_debug_wa", 0600, dir, dev, &fops_fw_debug_wa);
+ 	debugfs_create_file("fw_debug_bin", 0600, dir, dev, &fops_fw_debug_bin);
++	debugfs_create_file("idxlog_enable", 0600, dir, dev, &fops_idxlog_enable);
+ 	/* TODO: wm fw cpu utilization */
+ 	debugfs_create_file("fw_util_wa", 0400, dir, dev,
+ 			    &mt7996_fw_util_wa_fops);
+@@ -1095,6 +1143,32 @@ mt7996_debugfs_write_fwlog(struct mt7996_dev *dev, const void *hdr, int hdrlen,
+ 	spin_unlock_irqrestore(&lock, flags);
+ }
+ 
++static void
++mt7996_debugfs_write_idxlog(struct mt7996_dev *dev, const void *data, int len)
++{
++	static DEFINE_SPINLOCK(lock);
++	unsigned long flags;
++	void *dest;
++
++	if (!dev->relay_idxlog)
++		return;
++
++	spin_lock_irqsave(&lock, flags);
++
++	dest = relay_reserve(dev->relay_idxlog, len + 4);
++	if (!dest)
++		dev_err(dev->mt76.dev, "Failed to reserve slot in %s\n",
++		        dev->relay_idxlog->base_filename);
++	else {
++		*(u32 *)dest = len;
++		dest += 4;
++		memcpy(dest, data, len);
++		relay_flush(dev->relay_idxlog);
++	}
++
++	spin_unlock_irqrestore(&lock, flags);
++}
++
+ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int len)
+ {
+ 	struct {
+@@ -1119,11 +1193,15 @@ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int
+ 
+ bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len)
+ {
+-	if (get_unaligned_le32(data) != FW_BIN_LOG_MAGIC)
+-		return false;
++	bool is_fwlog = get_unaligned_le32(data) == FW_BIN_LOG_MAGIC;
+ 
+-	if (dev->relay_fwlog)
+-		mt7996_debugfs_write_fwlog(dev, NULL, 0, data, len);
++	if (is_fwlog) {
++		if (dev->relay_fwlog)
++			mt7996_debugfs_write_fwlog(dev, NULL, 0, data, len);
++	} else if (dev->relay_idxlog)
++		mt7996_debugfs_write_idxlog(dev, data, len);
++	else
++		return false;
+ 
+ 	return true;
+ }
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 099e97ef..564f080d 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2279,11 +2279,9 @@ void mt7996_mac_work(struct work_struct *work)
+ 	mutex_lock(&mdev->mutex);
+ 
+ 	mt76_update_survey(mphy);
+-	if (++mphy->mac_work_count == 5) {
++	if (++mphy->mac_work_count % 5 == 0) {
+ 		int i;
+ 
+-		mphy->mac_work_count = 0;
+-
+ 		mt7996_mac_update_stats(phy);
+ 
+ 		/* Update DEV-wise information only in
+@@ -2302,6 +2300,12 @@ void mt7996_mac_work(struct work_struct *work)
+ 				if (mt7996_mcu_wa_cmd(phy->dev, MCU_WA_PARAM_CMD(QUERY), MCU_WA_PARAM_BSS_ACQ_PKT_CNT,
+ 				                      BSS_ACQ_PKT_CNT_BSS_BITMAP_ALL | BSS_ACQ_PKT_CNT_READ_CLR, 0))
+ 					dev_err(mdev->dev, "Failed to query per-AC-queue packet counts.\n");
++
++				if (mphy->mac_work_count == 100) {
++					if (phy->dev->idxlog_enable && mt7996_mcu_fw_time_sync(mdev))
++						dev_err(mdev->dev, "Failed to synchronize time with FW.\n");
++					mphy->mac_work_count = 0;
++				}
+ 			} else if (mt7996_band_valid(phy->dev, i) &&
+ 			           test_bit(MT76_STATE_RUNNING, &mdev->phys[i]->state))
+ 				break;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 28cedb60..8cc907f9 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -400,6 +400,7 @@ static void
+ mt7996_mcu_rx_log_message(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+ #define UNI_EVENT_FW_LOG_FORMAT 0
++#define UNI_EVENT_FW_LOG_MEMORY	1
+ 	struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data;
+ 	const char *data = (char *)&rxd[1] + 4, *type;
+ 	struct tlv *tlv = (struct tlv *)data;
+@@ -411,7 +412,8 @@ mt7996_mcu_rx_log_message(struct mt7996_dev *dev, struct sk_buff *skb)
+ 		goto out;
+ 	}
+ 
+-	if (le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_FORMAT)
++	if (le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_FORMAT &&
++	    le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_MEMORY)
+ 		return;
+ 
+ 	data += sizeof(*tlv) + 4;
+@@ -3187,6 +3189,36 @@ int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level)
+ 				 sizeof(data), false);
+ }
+ 
++int mt7996_mcu_fw_time_sync(struct mt76_dev *dev)
++{
++	struct {
++		u8 _rsv[4];
++
++		__le16 tag;
++		__le16 len;
++		__le32 sec;
++		__le32 usec;
++	} data = {
++		.tag = cpu_to_le16(UNI_WSYS_CONFIG_FW_TIME_SYNC),
++		.len = cpu_to_le16(sizeof(data) - 4),
++	};
++	struct timespec64 ts;
++	struct tm tm;
++
++	ktime_get_real_ts64(&ts);
++	data.sec = cpu_to_le32((u32)ts.tv_sec);
++	data.usec = cpu_to_le32((u32)(ts.tv_nsec / 1000));
++
++	/* Dump synchronized time for ConsysPlanet to parse. */
++	time64_to_tm(ts.tv_sec, 0, &tm);
++	dev_info(dev->dev, "%ld-%02d-%02d %02d:%02d:%02d.%ld UTC\n",
++	        tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
++	        tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec / 1000);
++
++	return mt76_mcu_send_msg(dev, MCU_WM_UNI_CMD(WSYS_CONFIG), &data,
++	                         sizeof(data), true);
++}
++
+ static int mt7996_mcu_set_mwds(struct mt7996_dev *dev, bool enabled)
+ {
+ 	struct {
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 8ffb16f8..b15796dc 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -351,7 +351,8 @@ enum {
+ 	MCU_FW_LOG_WM,
+ 	MCU_FW_LOG_WA,
+ 	MCU_FW_LOG_TO_HOST,
+-	MCU_FW_LOG_RELAY = 16
++	MCU_FW_LOG_RELAY = 16,
++	MCU_FW_LOG_RELAY_IDX = 40
+ };
+ 
+ enum {
+@@ -944,6 +945,7 @@ enum {
+ 	UNI_WSYS_CONFIG_FW_LOG_CTRL,
+ 	UNI_WSYS_CONFIG_FW_DBG_CTRL,
+ 	UNI_CMD_CERT_CFG = 6,
++	UNI_WSYS_CONFIG_FW_TIME_SYNC, /* UNI_CMD_FW_TIME_SYNC in FW */
+ };
+ 
+ enum {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 0e6b7595..d5162885 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -592,9 +592,11 @@ struct mt7996_dev {
+ 	u8 fw_debug_bin;
+ 	u16 fw_debug_seq;
+ 	bool fw_debug_muru_disable;
++	bool idxlog_enable;
+ 
+ 	struct dentry *debugfs_dir;
+ 	struct rchan *relay_fwlog;
++	struct rchan *relay_idxlog;
+ 
+ 	void *cal;
+ 	u32 cur_prek_offset;
+@@ -846,6 +848,7 @@ int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3);
+ int mt7996_mcu_red_config(struct mt7996_dev *dev, bool enable);
+ int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl);
+ int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level);
++int mt7996_mcu_fw_time_sync(struct mt76_dev *dev);
+ int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
+ void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ void mt7996_mcu_exit(struct mt7996_dev *dev);
+diff --git a/tools/fwlog.c b/tools/fwlog.c
+index 3c6a61d7..0e2de2dc 100644
+--- a/tools/fwlog.c
++++ b/tools/fwlog.c
+@@ -29,10 +29,9 @@ static const char *debugfs_path(const char *phyname, const char *file)
+ static int mt76_set_fwlog_en(const char *phyname, bool en, char *val)
+ {
+ 	FILE *f = fopen(debugfs_path(phyname, "fw_debug_bin"), "w");
+-
+ 	if (!f) {
+-		fprintf(stderr, "Could not open fw_debug_bin file\n");
+-		return 1;
++		perror("fopen");
++		return -1;
+ 	}
+ 
+ 	if (en && val)
+@@ -47,6 +46,21 @@ static int mt76_set_fwlog_en(const char *phyname, bool en, char *val)
+ 	return 0;
+ }
+ 
++static int mt76_set_idxlog_enable(const char *phyname, bool enable)
++{
++	FILE *f = fopen(debugfs_path(phyname, "idxlog_enable"), "w");
++	if (!f) {
++		perror("fopen");
++		return -1;
++	}
++
++	fprintf(f, "%hhu", enable);
++
++	fclose(f);
++
++	return 0;
++}
++
+ int read_retry(int fd, void *buf, int len)
+ {
+ 	int out_len = 0;
+@@ -80,105 +94,193 @@ static void handle_signal(int sig)
+ 	done = true;
+ }
+ 
+-int mt76_fwlog(const char *phyname, int argc, char **argv)
++static int mt76_log_socket(struct sockaddr_in *remote, char *ip)
+ {
+-#define BUF_SIZE 1504
+ 	struct sockaddr_in local = {
+ 		.sin_family = AF_INET,
+ 		.sin_addr.s_addr = INADDR_ANY,
+ 	};
+-	struct sockaddr_in remote = {
+-		.sin_family = AF_INET,
+-		.sin_port = htons(55688),
+-	};
+-	char *buf = calloc(BUF_SIZE, sizeof(char));
+-	int ret = 0;
+-	/* int yes = 1; */
+-	int s, fd;
+-
+-	if (argc < 1) {
+-		fprintf(stderr, "need destination address\n");
+-		return 1;
+-	}
++	int s, ret;
+ 
+-	if (!inet_aton(argv[0], &remote.sin_addr)) {
+-		fprintf(stderr, "invalid destination address\n");
+-		return 1;
++	remote->sin_family = AF_INET;
++	remote->sin_port = htons(55688);
++	if (!inet_aton(ip, &remote->sin_addr)) {
++		fprintf(stderr, "Invalid destination IP address: %s\n", ip);
++		return -EINVAL;
+ 	}
+ 
+ 	s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ 	if (s < 0) {
+ 		perror("socket");
+-		return 1;
++		return s;
+ 	}
+ 
+-	/* setsockopt(s, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes)); */
+-	if (bind(s, (struct sockaddr *)&local, sizeof(local)) < 0) {
++	ret = bind(s, (struct sockaddr *)&local, sizeof(local));
++	if (ret) {
+ 		perror("bind");
+-		return 1;
++		close(s);
++		return ret;
+ 	}
+ 
+-	if (mt76_set_fwlog_en(phyname, true, argv[1]))
+-		return 1;
++	return s;
++}
++
++static int mt76_log_relay(int in_fd, int out_fd, struct sockaddr_in *remote)
++{
++	char *buf = malloc(FWLOG_BUF_SIZE);
++	int ret = 0;
+ 
+-	fd = open(debugfs_path(phyname, "fwlog_data"), O_RDONLY);
+-	if (fd < 0) {
+-		fprintf(stderr, "Could not open fwlog_data file: %s\n", strerror(errno));
+-		ret = 1;
+-		goto out;
++	if (!buf) {
++		perror("malloc");
++		return -ENOMEM;
+ 	}
+ 
+ 	signal(SIGTERM, handle_signal);
+ 	signal(SIGINT, handle_signal);
+ 	signal(SIGQUIT, handle_signal);
+ 
+-	while (1) {
++	while (!done) {
+ 		struct pollfd pfd = {
+-			.fd = fd,
+-			.events = POLLIN | POLLHUP | POLLERR,
++			.fd = in_fd,
++			.events = POLLIN,
+ 		};
+ 		uint32_t len;
+-		int r;
+-
+-		if (done)
+-			break;
++		int rc;
+ 
+ 		poll(&pfd, 1, -1);
+ 
+-		r = read_retry(fd, &len, sizeof(len));
+-		if (r < 0)
++		rc = read_retry(in_fd, &len, sizeof(len));
++		if (rc < 0) {
++			if (!done) {
++				fprintf(stderr, "Failed to read relay file.\n");
++				ret = -1;
++			}
+ 			break;
+-
+-		if (!r)
++		}
++		if (!rc)
+ 			continue;
+ 
+-		if (len > BUF_SIZE) {
+-			fprintf(stderr, "Length error: %d > %d\n", len, BUF_SIZE);
+-			ret = 1;
++		if (len > FWLOG_BUF_SIZE) {
++			fprintf(stderr, "Log size was too large: %u bytes\n", len);
++			ret = -ENOMEM;
+ 			break;
+ 		}
+ 
+-		if (done)
++		rc = read_retry(in_fd, buf, len);
++		if (rc < 0) {
++			if (!done) {
++				fprintf(stderr, "Failed to read relay file.\n");
++				ret = -1;
++			}
+ 			break;
+-
+-		r = read_retry(fd, buf, len);
+-		if (done)
++		}
++		if (rc != len) {
++			fprintf(stderr, "Expected log size: %u bytes\n", len);
++			fprintf(stderr, "Read log size: %u bytes\n", rc);
++			ret = -EIO;
+ 			break;
++		}
+ 
+-		if (r != len) {
+-			fprintf(stderr, "Short read: %d < %d\n", r, len);
+-			ret = 1;
++		if (remote)
++			rc = sendto(out_fd, buf, len, 0, (struct sockaddr *)remote, sizeof(*remote));
++		else
++			rc = write(out_fd, buf, len);
++		if (rc < 0) {
++			perror("sendto/write");
++			ret = -1;
+ 			break;
+ 		}
++	}
++
++	free(buf);
++
++	return ret;
++}
++
++int mt76_fwlog(const char *phyname, int argc, char **argv)
++{
++	struct sockaddr_in remote;
++	int in_fd, out_fd, ret;
++
++	if (argc < 1) {
++		fprintf(stderr, "need destination address\n");
++		return -EINVAL;
++	}
++
++	out_fd = mt76_log_socket(&remote, argv[0]);
++	if (out_fd < 0)
++		return out_fd;
++
++	ret = mt76_set_fwlog_en(phyname, true, argv[1]);
++	if (ret)
++		goto close;
+ 
+-		/* send buf */
+-		sendto(s, buf, len, 0, (struct sockaddr *)&remote, sizeof(remote));
++	in_fd = open(debugfs_path(phyname, "fwlog_data"), O_RDONLY);
++	if (in_fd < 0) {
++		perror("open");
++		goto disable;
+ 	}
+ 
+-	close(fd);
++	if (mt76_log_relay(in_fd, out_fd, &remote))
++		fprintf(stderr, "Failed to relay FW log.\n");
+ 
+-out:
+-	mt76_set_fwlog_en(phyname, false, NULL);
++	close(in_fd);
++disable:
++	ret = mt76_set_fwlog_en(phyname, false, NULL);
++close:
++	close(out_fd);
++
++	return ret;
++}
++
++int mt76_idxlog(const char *phyname, int argc, char **argv)
++{
++#define IDXLOG_FILE_PATH	"/tmp/log/WIFI_FW.clog"
++	struct sockaddr_in remote;
++	int in_fd, out_fd, ret;
++
++	if (argc) {
++		out_fd = mt76_log_socket(&remote, argv[0]);
++		if (out_fd < 0)
++			return out_fd;
++	} else {
++		out_fd = open(IDXLOG_FILE_PATH, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR);
++		if (out_fd < 0) {
++			perror("open");
++			return -1;
++		}
++	}
++
++	ret = mt76_set_idxlog_enable(phyname, true);
++	if (ret)
++		goto close;
++
++	in_fd = open(debugfs_path(phyname, "idxlog_data"), O_RDONLY);
++	if (in_fd < 0) {
++		perror("open");
++		goto disable;
++	}
++
++	if (mt76_log_relay(in_fd, out_fd, argc ? &remote : NULL))
++		fprintf(stderr, "Failed to relay index log.\n");
++
++	close(in_fd);
++disable:
++	ret = mt76_set_idxlog_enable(phyname, false);
++close:
++	close(out_fd);
++
++	if (argc)
++		system("timestamp=$(date +\"%y%m%d_%H%M%S\");"
++		       "clog_dir=/tmp/log/clog_${timestamp};"
++		       "mkdir ${clog_dir};"
++		       "dmesg > ${clog_dir}/WIFI_KERNEL_${timestamp}.clog");
++	else
++		system("timestamp=$(date +\"%y%m%d_%H%M%S\");"
++		       "clog_dir=/tmp/log/clog_${timestamp};"
++		       "mkdir ${clog_dir};"
++		       "mv /tmp/log/WIFI_FW.clog ${clog_dir}/WIFI_FW_${timestamp}.clog;"
++		       "dmesg > ${clog_dir}/WIFI_KERNEL_${timestamp}.clog");
+ 
+ 	return ret;
+ }
+diff --git a/tools/main.c b/tools/main.c
+index 699a9eea..e9e25567 100644
+--- a/tools/main.c
++++ b/tools/main.c
+@@ -198,6 +198,8 @@ int main(int argc, char **argv)
+ 		ret = mt76_eeprom(phy, argc, argv);
+ 	else if (!strcmp(cmd, "fwlog"))
+ 		ret = mt76_fwlog(phyname, argc, argv);
++	else if (!strcmp(cmd, "idxlog"))
++		ret = mt76_idxlog(phyname, argc, argv);
+ 	else
+ 		usage();
+ 
+diff --git a/tools/mt76-test.h b/tools/mt76-test.h
+index d2fafa86..b9d508c5 100644
+--- a/tools/mt76-test.h
++++ b/tools/mt76-test.h
+@@ -22,6 +22,8 @@
+ #define EEPROM_FILE_PATH_FMT	"/tmp/mt76-test-%s"
+ #define EEPROM_PART_SIZE	20480
+ 
++#define FWLOG_BUF_SIZE	1504
++
+ struct nl_msg;
+ struct nlattr;
+ 
+@@ -61,5 +63,6 @@ extern unsigned char *eeprom_data;
+ void usage(void);
+ int mt76_eeprom(int phy, int argc, char **argv);
+ int mt76_fwlog(const char *phyname, int argc, char **argv);
++int mt76_idxlog(const char *phyname, int argc, char **argv);
+ 
+ #endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0086-wifi-mt76-mt7996-switch-to-per-link-data-structure-o.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0086-wifi-mt76-mt7996-switch-to-per-link-data-structure-o.patch
deleted file mode 100644
index 7256cb2..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0086-wifi-mt76-mt7996-switch-to-per-link-data-structure-o.patch
+++ /dev/null
@@ -1,2430 +0,0 @@
-From 88111ec93e8bfd47f020965dd075849bd2233e40 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Mon, 27 Nov 2023 10:43:34 +0800
-Subject: [PATCH 086/116] wifi: mt76: mt7996: switch to per-link data structure
- of sta
-
-Introduce struct mt7996_link_sta, data structure for per-link STA.
-Note that mt7996_sta now represents a peer legacy or MLD device.
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7615/mcu.c      |   4 +-
- mt76_connac_mcu.c |  18 +-
- mt76_connac_mcu.h |   7 +-
- mt7915/mcu.c      |   4 +-
- mt7925/mcu.c      |   4 +-
- mt7996/debugfs.c  |  20 ++-
- mt7996/mac.c      | 109 ++++++------
- mt7996/main.c     | 223 +++++++++++++++---------
- mt7996/mcu.c      | 430 ++++++++++++++++++++++++----------------------
- mt7996/mt7996.h   |  44 +++--
- mt7996/testmode.c |   6 +-
- 11 files changed, 482 insertions(+), 387 deletions(-)
-
-diff --git a/mt7615/mcu.c b/mt7615/mcu.c
-index a931066..8f4f203 100644
---- a/mt7615/mcu.c
-+++ b/mt7615/mcu.c
-@@ -862,8 +862,8 @@ mt7615_mcu_wtbl_sta_add(struct mt7615_phy *phy, struct ieee80211_vif *vif,
- 		else
- 			mvif->sta_added = true;
- 	}
--	mt76_connac_mcu_sta_basic_tlv(&dev->mt76, sskb, vif, sta, enable,
--				      new_entry);
-+	mt76_connac_mcu_sta_basic_tlv(&dev->mt76, sskb, vif, &sta->deflink,
-+				      enable, new_entry);
- 	if (enable && sta)
- 		mt76_connac_mcu_sta_tlv(phy->mt76, sskb, sta, vif, 0,
- 					MT76_STA_INFO_STATE_ASSOC);
-diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
-index 0c7b693..d83d314 100644
---- a/mt76_connac_mcu.c
-+++ b/mt76_connac_mcu.c
-@@ -371,9 +371,10 @@ EXPORT_SYMBOL_GPL(mt76_connac_mcu_bss_omac_tlv);
- 
- void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
- 				   struct ieee80211_vif *vif,
--				   struct ieee80211_sta *sta,
-+				   struct ieee80211_link_sta *link_sta,
- 				   bool enable, bool newly)
- {
-+	struct ieee80211_sta *sta = link_sta ? link_sta->sta : NULL;
- 	struct sta_rec_basic *basic;
- 	struct tlv *tlv;
- 	int conn_type;
-@@ -431,7 +432,7 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
- 		break;
- 	}
- 
--	memcpy(basic->peer_addr, sta->addr, ETH_ALEN);
-+	memcpy(basic->peer_addr, link_sta->addr, ETH_ALEN);
- 	basic->qos = sta->wme;
- }
- EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_basic_tlv);
-@@ -1055,7 +1056,7 @@ int mt76_connac_mcu_sta_cmd(struct mt76_phy *phy,
- 		return PTR_ERR(skb);
- 
- 	if (info->sta || !info->offload_fw)
--		mt76_connac_mcu_sta_basic_tlv(dev, skb, info->vif, info->sta,
-+		mt76_connac_mcu_sta_basic_tlv(dev, skb, info->vif, &info->sta->deflink,
- 					      info->enable, info->newly);
- 	if (info->sta && info->enable)
- 		mt76_connac_mcu_sta_tlv(phy, skb, info->sta,
-@@ -1306,7 +1307,8 @@ int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif,
- EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_ba);
- 
- u8 mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif,
--			    enum nl80211_band band, struct ieee80211_sta *sta)
-+			    enum nl80211_band band,
-+			    struct ieee80211_link_sta *link_sta)
- {
- 	struct mt76_dev *dev = phy->dev;
- 	const struct ieee80211_sta_he_cap *he_cap;
-@@ -1317,10 +1319,10 @@ u8 mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif,
- 	if (is_connac_v1(dev))
- 		return 0x38;
- 
--	if (sta) {
--		ht_cap = &sta->deflink.ht_cap;
--		vht_cap = &sta->deflink.vht_cap;
--		he_cap = &sta->deflink.he_cap;
-+	if (link_sta) {
-+		ht_cap = &link_sta->ht_cap;
-+		vht_cap = &link_sta->vht_cap;
-+		he_cap = &link_sta->he_cap;
- 	} else {
- 		struct ieee80211_supported_band *sband;
- 
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index c19f8e0..740b9f5 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1910,8 +1910,8 @@ int mt76_connac_mcu_set_channel_domain(struct mt76_phy *phy);
- int mt76_connac_mcu_set_vif_ps(struct mt76_dev *dev, struct ieee80211_vif *vif);
- void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
- 				   struct ieee80211_vif *vif,
--				   struct ieee80211_sta *sta, bool enable,
--				   bool newly);
-+				   struct ieee80211_link_sta *link_sta,
-+				   bool enable, bool newly);
- void mt76_connac_mcu_wtbl_generic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
- 				      struct ieee80211_vif *vif,
- 				      struct ieee80211_sta *sta, void *sta_wtbl,
-@@ -2019,7 +2019,8 @@ mt76_connac_get_he_phy_cap(struct mt76_phy *phy, struct ieee80211_vif *vif);
- const struct ieee80211_sta_eht_cap *
- mt76_connac_get_eht_phy_cap(struct mt76_phy *phy, struct ieee80211_vif *vif);
- u8 mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif,
--			    enum nl80211_band band, struct ieee80211_sta *sta);
-+			    enum nl80211_band band,
-+			    struct ieee80211_link_sta *link_sta);
- u8 mt76_connac_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_vif *vif,
- 				enum nl80211_band band);
- 
-diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 363ea0e..cb2e3b2 100644
---- a/mt7915/mcu.c
-+++ b/mt7915/mcu.c
-@@ -1504,7 +1504,7 @@ mt7915_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7915_dev *dev,
- 
- 	ra->valid = true;
- 	ra->auto_rate = true;
--	ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, sta);
-+	ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, &sta->deflink);
- 	ra->channel = chandef->chan->hw_value;
- 	ra->bw = sta->deflink.bandwidth;
- 	ra->phy.bw = sta->deflink.bandwidth;
-@@ -1669,7 +1669,7 @@ int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif,
- 		return PTR_ERR(skb);
- 
- 	/* starec basic */
--	mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, vif, sta, enable,
-+	mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, vif, &sta->deflink, enable,
- 				      !rcu_access_pointer(dev->mt76.wcid[msta->wcid.idx]));
- 	if (!enable)
- 		goto out;
-diff --git a/mt7925/mcu.c b/mt7925/mcu.c
-index 652a9ac..80e1828 100644
---- a/mt7925/mcu.c
-+++ b/mt7925/mcu.c
-@@ -1626,7 +1626,7 @@ mt7925_mcu_sta_cmd(struct mt76_phy *phy,
- 		return PTR_ERR(skb);
- 
- 	if (info->sta || !info->offload_fw)
--		mt76_connac_mcu_sta_basic_tlv(dev, skb, info->vif, info->sta,
-+		mt76_connac_mcu_sta_basic_tlv(dev, skb, info->vif, &info->sta->deflink,
- 					      info->enable, info->newly);
- 	if (info->sta && info->enable) {
- 		mt7925_mcu_sta_phy_tlv(skb, info->vif, info->sta);
-@@ -2092,7 +2092,7 @@ mt7925_mcu_bss_basic_tlv(struct sk_buff *skb,
- 		basic_req->nonht_basic_phy = cpu_to_le16(PHY_TYPE_OFDM_INDEX);
- 
- 	memcpy(basic_req->bssid, vif->bss_conf.bssid, ETH_ALEN);
--	basic_req->phymode = mt76_connac_get_phy_mode(phy, vif, band, sta);
-+	basic_req->phymode = mt76_connac_get_phy_mode(phy, vif, band, &sta->deflink);
- 	basic_req->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int);
- 	basic_req->dtim_period = vif->bss_conf.dtim_period;
- 	basic_req->bmc_tx_wlan_idx = cpu_to_le16(wlan_idx);
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index 06015d6..56e2192 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -739,14 +739,15 @@ static void
- mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta)
- {
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_link_sta *mlink = &msta->deflink;
- 	struct mt7996_dev *dev = msta->vif->deflink.phy->dev;
- 	struct seq_file *s = data;
- 	u8 ac;
- 
- 	for (ac = 0; ac < 4; ac++) {
- 		u32 qlen, ctrl, val;
--		u32 idx = msta->wcid.idx >> 5;
--		u8 offs = msta->wcid.idx & GENMASK(4, 0);
-+		u32 idx = mlink->wcid.idx >> 5;
-+		u8 offs = mlink->wcid.idx & GENMASK(4, 0);
- 
- 		ctrl = BIT(31) | BIT(11) | (ac << 24);
- 		val = mt76_rr(dev, MT_PLE_AC_QEMPTY(ac, idx));
-@@ -754,11 +755,11 @@ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta)
- 		if (val & BIT(offs))
- 			continue;
- 
--		mt76_wr(dev, MT_FL_Q0_CTRL, ctrl | msta->wcid.idx);
-+		mt76_wr(dev, MT_FL_Q0_CTRL, ctrl | mlink->wcid.idx);
- 		qlen = mt76_get_field(dev, MT_FL_Q3_CTRL,
- 				      GENMASK(11, 0));
- 		seq_printf(s, "\tSTA %pM wcid %d: AC%d%d queued:%d\n",
--			   sta->addr, msta->wcid.idx,
-+			   sta->addr, mlink->wcid.idx,
- 			   msta->vif->deflink.mt76.wmm_idx, ac, qlen);
- 	}
- }
-@@ -1041,7 +1042,7 @@ mt7996_airtime_read(struct seq_file *s, void *data)
- 	struct mt76_dev *mdev = &dev->mt76;
- 	struct mt76_sta_stats *stats;
- 	struct ieee80211_sta *sta;
--	struct mt7996_sta *msta;
-+	struct mt7996_link_sta *mlink;
- 	struct mt76_wcid *wcid;
- 	struct mt76_vif *vif;
- 	u16 i;
-@@ -1053,9 +1054,9 @@ mt7996_airtime_read(struct seq_file *s, void *data)
- 		if (!wcid || !wcid->sta)
- 			continue;
- 
--		msta = container_of(wcid, struct mt7996_sta, wcid);
--		sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
--		vif = &msta->vif->deflink.mt76;
-+		mlink = container_of(wcid, struct mt7996_link_sta, wcid);
-+		sta = container_of((void *)mlink->sta, struct ieee80211_sta, drv_priv);
-+		vif = &mlink->sta->vif->deflink.mt76;
- 		stats = &wcid->stats;
- 
- 		seq_printf(s, "%pM WCID: %hu BandIdx: %hhu OmacIdx: 0x%hhx\t"
-@@ -1230,6 +1231,7 @@ static ssize_t mt7996_sta_fixed_rate_set(struct file *file,
- #define LONG_PREAMBLE 1
- 	struct ieee80211_sta *sta = file->private_data;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_link_sta *mlink = &msta->deflink;
- 	struct mt7996_dev *dev = msta->vif->deflink.phy->dev;
- 	struct ra_rate phy = {};
- 	char buf[100];
-@@ -1265,7 +1267,7 @@ static ssize_t mt7996_sta_fixed_rate_set(struct file *file,
- 		goto out;
- 	}
- 
--	phy.wlan_idx = cpu_to_le16(msta->wcid.idx);
-+	phy.wlan_idx = cpu_to_le16(mlink->wcid.idx);
- 	phy.gi = cpu_to_le16(gi);
- 	phy.ltf = cpu_to_le16(ltf);
- 	phy.ldpc = phy.ldpc ? 7 : 0;
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 63b16f9..3cff43d 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -54,7 +54,7 @@ static const struct mt7996_dfs_radar_spec jp_radar_specs = {
- static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev,
- 					    u16 idx, bool unicast)
- {
--	struct mt7996_sta *sta;
-+	struct mt7996_link_sta *mlink;
- 	struct mt76_wcid *wcid;
- 
- 	if (idx >= ARRAY_SIZE(dev->mt76.wcid))
-@@ -67,11 +67,11 @@ static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev,
- 	if (!wcid->sta)
- 		return NULL;
- 
--	sta = container_of(wcid, struct mt7996_sta, wcid);
--	if (!sta->vif)
-+	mlink = container_of(wcid, struct mt7996_link_sta, wcid);
-+	if (!mlink->sta->vif)
- 		return NULL;
- 
--	return &sta->vif->sta.wcid;
-+	return &mlink->wcid;
- }
- 
- bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask)
-@@ -92,12 +92,11 @@ u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw)
- }
- 
- void mt7996_mac_enable_rtscts(struct mt7996_dev *dev,
--			      struct ieee80211_vif *vif, bool enable)
-+			      struct mt7996_link_sta *mlink, bool enable)
- {
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	u32 addr;
- 
--	addr = mt7996_mac_wtbl_lmac_addr(dev, mvif->sta.wcid.idx, 5);
-+	addr = mt7996_mac_wtbl_lmac_addr(dev, mlink->wcid.idx, 5);
- 	if (enable)
- 		mt76_set(dev, addr, BIT(5));
- 	else
-@@ -349,7 +348,7 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
- 	__le16 fc = 0;
- 	int idx;
- 	u8 hw_aggr = false;
--	struct mt7996_sta *msta = NULL;
-+	struct mt7996_link_sta *mlink = NULL;
- 
- 	hw_aggr = status->aggr;
- 	memset(status, 0, sizeof(*status));
-@@ -380,10 +379,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
- 	status->wcid = mt7996_rx_get_wcid(dev, idx, unicast);
- 
- 	if (status->wcid) {
--		msta = container_of(status->wcid, struct mt7996_sta, wcid);
-+		mlink = container_of(status->wcid, struct mt7996_link_sta, wcid);
- 		spin_lock_bh(&dev->mt76.sta_poll_lock);
--		if (list_empty(&msta->wcid.poll_list))
--			list_add_tail(&msta->wcid.poll_list,
-+		if (list_empty(&mlink->wcid.poll_list))
-+			list_add_tail(&mlink->wcid.poll_list,
- 				      &dev->mt76.sta_poll_list);
- 		spin_unlock_bh(&dev->mt76.sta_poll_lock);
- 	}
-@@ -592,7 +591,7 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
- #endif
- 	} else {
- 		status->flag |= RX_FLAG_8023;
--		mt7996_wed_check_ppe(dev, &dev->mt76.q_rx[q], msta, skb,
-+		mt7996_wed_check_ppe(dev, &dev->mt76.q_rx[q], mlink ? mlink->sta : NULL, skb,
- 				     *info);
- 	}
- 
-@@ -942,6 +941,7 @@ static void
- mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb)
- {
- 	struct mt7996_sta *msta;
-+	struct mt7996_link_sta *mlink;
- 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- 	bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
- 	u16 fc, tid;
-@@ -970,7 +970,8 @@ mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb)
- 		return;
- 
- 	msta = (struct mt7996_sta *)sta->drv_priv;
--	if (!test_and_set_bit(tid, &msta->wcid.ampdu_state))
-+	mlink = rcu_dereference(msta->link[0]);
-+	if (!test_and_set_bit(tid, &mlink->wcid.ampdu_state))
- 		ieee80211_start_tx_ba_session(sta, tid, 0);
- }
- 
-@@ -1048,7 +1049,7 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
- 		 */
- 		info = le32_to_cpu(*cur_info);
- 		if (info & MT_TXFREE_INFO_PAIR) {
--			struct mt7996_sta *msta;
-+			struct mt7996_link_sta *mlink;
- 			u16 idx;
- 
- 			idx = FIELD_GET(MT_TXFREE_INFO_WLAN_ID, info);
-@@ -1057,10 +1058,10 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
- 			if (!sta)
- 				continue;
- 
--			msta = container_of(wcid, struct mt7996_sta, wcid);
-+			mlink = container_of(wcid, struct mt7996_link_sta, wcid);
- 			spin_lock_bh(&mdev->sta_poll_lock);
--			if (list_empty(&msta->wcid.poll_list))
--				list_add_tail(&msta->wcid.poll_list,
-+			if (list_empty(&mlink->wcid.poll_list))
-+				list_add_tail(&mlink->wcid.poll_list,
- 					      &mdev->sta_poll_list);
- 			spin_unlock_bh(&mdev->sta_poll_lock);
- 			continue;
-@@ -1265,7 +1266,7 @@ out:
- 
- static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
- {
--	struct mt7996_sta *msta = NULL;
-+	struct mt7996_link_sta *mlink;
- 	struct mt76_wcid *wcid;
- 	__le32 *txs_data = data;
- 	u16 wcidx;
-@@ -1286,16 +1287,15 @@ static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
- 	if (!wcid)
- 		goto out;
- 
--	msta = container_of(wcid, struct mt7996_sta, wcid);
--
- 	mt7996_mac_add_txs_skb(dev, wcid, pid, txs_data);
- 
- 	if (!wcid->sta)
- 		goto out;
- 
-+	mlink = container_of(wcid, struct mt7996_link_sta, wcid);
- 	spin_lock_bh(&dev->mt76.sta_poll_lock);
--	if (list_empty(&msta->wcid.poll_list))
--		list_add_tail(&msta->wcid.poll_list, &dev->mt76.sta_poll_list);
-+	if (list_empty(&mlink->wcid.poll_list))
-+		list_add_tail(&mlink->wcid.poll_list, &dev->mt76.sta_poll_list);
- 	spin_unlock_bh(&dev->mt76.sta_poll_lock);
- 
- out:
-@@ -2242,8 +2242,9 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
- 	struct ieee80211_sta *sta;
- 	struct ieee80211_vif *vif;
- 	struct ieee80211_bss_conf *conf;
-+	struct ieee80211_link_sta *link_sta;
- 	struct mt7996_bss_conf *mconf;
--	struct mt7996_sta *msta;
-+	struct mt7996_link_sta *mlink;
- 	u32 changed;
- 	LIST_HEAD(list);
- 
-@@ -2251,24 +2252,25 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
- 	list_splice_init(&dev->sta_rc_list, &list);
- 
- 	while (!list_empty(&list)) {
--		msta = list_first_entry(&list, struct mt7996_sta, rc_list);
--		list_del_init(&msta->rc_list);
--		changed = msta->changed;
--		msta->changed = 0;
-+		mlink = list_first_entry(&list, struct mt7996_link_sta, rc_list);
-+		list_del_init(&mlink->rc_list);
-+		changed = mlink->changed;
-+		mlink->changed = 0;
- 		spin_unlock_bh(&dev->mt76.sta_poll_lock);
- 
--		sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
--		vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv);
-+		sta = container_of((void *)mlink->sta, struct ieee80211_sta, drv_priv);
-+		link_sta = &sta->deflink;
-+		vif = container_of((void *)mlink->sta->vif, struct ieee80211_vif, drv_priv);
- 		conf = &vif->bss_conf;
--		mconf = &msta->vif->deflink;
-+		mconf = &mlink->sta->vif->deflink;
- 
- 		if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED |
- 			       IEEE80211_RC_NSS_CHANGED |
- 			       IEEE80211_RC_BW_CHANGED))
--			mt7996_mcu_add_rate_ctrl(dev, conf, mconf, sta, true);
-+			mt7996_mcu_add_rate_ctrl(dev, conf, mconf, link_sta, mlink, true);
- 
- 		if (changed & IEEE80211_RC_SMPS_CHANGED)
--			mt7996_mcu_set_fixed_field(dev, mconf, sta, NULL,
-+			mt7996_mcu_set_fixed_field(dev, mconf, link_sta, mlink, NULL,
- 						   RATE_PARAM_MMPS_UPDATE);
- 
- 		spin_lock_bh(&dev->mt76.sta_poll_lock);
-@@ -2561,7 +2563,7 @@ static int mt7996_mac_check_twt_req(struct ieee80211_twt_setup *twt)
- }
- 
- static bool
--mt7996_mac_twt_param_equal(struct mt7996_sta *msta,
-+mt7996_mac_twt_param_equal(struct mt7996_link_sta *mlink,
- 			   struct ieee80211_twt_params *twt_agrt)
- {
- 	u16 type = le16_to_cpu(twt_agrt->req_type);
-@@ -2572,10 +2574,10 @@ mt7996_mac_twt_param_equal(struct mt7996_sta *msta,
- 	for (i = 0; i < MT7996_MAX_STA_TWT_AGRT; i++) {
- 		struct mt7996_twt_flow *f;
- 
--		if (!(msta->twt.flowid_mask & BIT(i)))
-+		if (!(mlink->twt.flowid_mask & BIT(i)))
- 			continue;
- 
--		f = &msta->twt.flow[i];
-+		f = &mlink->twt.flow[i];
- 		if (f->duration == twt_agrt->min_twt_dur &&
- 		    f->mantissa == twt_agrt->mantissa &&
- 		    f->exp == exp &&
-@@ -2594,6 +2596,7 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
- {
- 	enum ieee80211_twt_setup_cmd setup_cmd = TWT_SETUP_CMD_REJECT;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_link_sta *mlink;
- 	struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
- 	u16 req_type = le16_to_cpu(twt_agrt->req_type);
- 	enum ieee80211_twt_setup_cmd sta_setup_cmd;
-@@ -2605,11 +2608,12 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
- 		goto out;
- 
- 	mutex_lock(&dev->mt76.mutex);
-+	mlink = mlink_dereference_protected(msta, 0);
- 
- 	if (dev->twt.n_agrt == MT7996_MAX_TWT_AGRT)
- 		goto unlock;
- 
--	if (hweight8(msta->twt.flowid_mask) == ARRAY_SIZE(msta->twt.flow))
-+	if (hweight8(mlink->twt.flowid_mask) == ARRAY_SIZE(mlink->twt.flow))
- 		goto unlock;
- 
- 	if (twt_agrt->min_twt_dur < MT7996_MIN_TWT_DUR) {
-@@ -2618,10 +2622,10 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
- 		goto unlock;
- 	}
- 
--	if (mt7996_mac_twt_param_equal(msta, twt_agrt))
-+	if (mt7996_mac_twt_param_equal(mlink, twt_agrt))
- 		goto unlock;
- 
--	flowid = ffs(~msta->twt.flowid_mask) - 1;
-+	flowid = ffs(~mlink->twt.flowid_mask) - 1;
- 	twt_agrt->req_type &= ~cpu_to_le16(IEEE80211_TWT_REQTYPE_FLOWID);
- 	twt_agrt->req_type |= le16_encode_bits(flowid,
- 					       IEEE80211_TWT_REQTYPE_FLOWID);
-@@ -2630,10 +2634,10 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
- 	exp = FIELD_GET(IEEE80211_TWT_REQTYPE_WAKE_INT_EXP, req_type);
- 	sta_setup_cmd = FIELD_GET(IEEE80211_TWT_REQTYPE_SETUP_CMD, req_type);
- 
--	flow = &msta->twt.flow[flowid];
-+	flow = &mlink->twt.flow[flowid];
- 	memset(flow, 0, sizeof(*flow));
- 	INIT_LIST_HEAD(&flow->list);
--	flow->wcid = msta->wcid.idx;
-+	flow->wcid = mlink->wcid.idx;
- 	flow->table_id = table_id;
- 	flow->id = flowid;
- 	flow->duration = twt_agrt->min_twt_dur;
-@@ -2651,7 +2655,7 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
- 
- 		flow->sched = true;
- 		flow->start_tsf = mt7996_mac_twt_sched_list_add(dev, flow);
--		curr_tsf = __mt7996_get_tsf(hw, &msta->vif->deflink);
-+		curr_tsf = __mt7996_get_tsf(hw, &mlink->sta->vif->deflink);
- 		div_u64_rem(curr_tsf - flow->start_tsf, interval, &rem);
- 		flow_tsf = curr_tsf + interval - rem;
- 		twt_agrt->twt = cpu_to_le64(flow_tsf);
-@@ -2660,13 +2664,13 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
- 	}
- 	flow->tsf = le64_to_cpu(twt_agrt->twt);
- 
--	if (mt7996_mcu_twt_agrt_update(dev, &msta->vif->deflink, flow,
-+	if (mt7996_mcu_twt_agrt_update(dev, &mlink->sta->vif->deflink, flow,
- 				       MCU_TWT_AGRT_ADD))
- 		goto unlock;
- 
- 	setup_cmd = TWT_SETUP_CMD_ACCEPT;
- 	dev->twt.table_mask |= BIT(table_id);
--	msta->twt.flowid_mask |= BIT(flowid);
-+	mlink->twt.flowid_mask |= BIT(flowid);
- 	dev->twt.n_agrt++;
- 
- unlock:
-@@ -2679,26 +2683,25 @@ out:
- }
- 
- void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
--				  struct mt7996_sta *msta,
--				  u8 flowid)
-+				  struct mt7996_link_sta *mlink, u8 flowid)
- {
- 	struct mt7996_twt_flow *flow;
--	struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
-+	struct mt7996_bss_conf *mconf = mconf_dereference_protected(mlink->sta->vif, 0);
- 
- 	lockdep_assert_held(&dev->mt76.mutex);
- 
--	if (flowid >= ARRAY_SIZE(msta->twt.flow))
-+	if (flowid >= ARRAY_SIZE(mlink->twt.flow))
- 		return;
- 
--	if (!(msta->twt.flowid_mask & BIT(flowid)))
-+	if (!(mlink->twt.flowid_mask & BIT(flowid)))
- 		return;
- 
--	flow = &msta->twt.flow[flowid];
-+	flow = &mlink->twt.flow[flowid];
- 	if (mt7996_mcu_twt_agrt_update(dev, mconf, flow, MCU_TWT_AGRT_DELETE))
- 		return;
- 
- 	list_del_init(&flow->list);
--	msta->twt.flowid_mask &= ~BIT(flowid);
-+	mlink->twt.flowid_mask &= ~BIT(flowid);
- 	dev->twt.table_mask &= ~BIT(flow->table_id);
- 	dev->twt.n_agrt--;
- }
-@@ -2711,7 +2714,7 @@ mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
- 	struct cfg80211_scan_request *req = phy->scan_req;
- 	struct ieee80211_vif *vif = phy->scan_vif;
- 	struct mt7996_vif *mvif;
--	struct mt76_wcid *wcid;
-+	struct mt7996_link_sta *mlink;
- 	struct ieee80211_tx_info *info;
- 	struct sk_buff *skb;
- 
-@@ -2719,7 +2722,6 @@ mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
- 		return;
- 
- 	mvif = (struct mt7996_vif *)vif->drv_priv;
--	wcid = &mvif->sta.wcid;
- 
- 	skb = ieee80211_probereq_get(hw, vif->addr,
- 				     ssid->ssid, ssid->ssid_len, req->ie_len);
-@@ -2752,7 +2754,8 @@ mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
- 	}
- 
- 	local_bh_disable();
--	mt76_tx(phy->mt76, NULL, wcid, skb);
-+	mlink = rcu_dereference(mvif->sta.link[0]);
-+	mt76_tx(phy->mt76, NULL, &mlink->wcid, skb);
- 	local_bh_enable();
- 
- 	rcu_read_unlock();
-diff --git a/mt7996/main.c b/mt7996/main.c
-index bc4863d..4e5ba58 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -229,6 +229,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
- 	struct ieee80211_bss_conf *conf = &vif->bss_conf;
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_bss_conf *mconf = &mvif->deflink;
-+	struct mt7996_link_sta *mlink = &mvif->sta.deflink;
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
- 	struct mt76_txq *mtxq;
-@@ -268,14 +269,15 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
- 
- 	idx = MT7996_WTBL_RESERVED - mconf->mt76.idx;
- 
--	INIT_LIST_HEAD(&mvif->sta.rc_list);
--	INIT_LIST_HEAD(&mvif->sta.wcid.poll_list);
--	mvif->sta.wcid.idx = idx;
--	mvif->sta.wcid.phy_idx = band_idx;
--	mvif->sta.wcid.hw_key_idx = -1;
--	mvif->sta.wcid.tx_info |= MT_WCID_TX_INFO_SET;
--	mvif->sta.vif = mvif;
--	mt76_wcid_init(&mvif->sta.wcid);
-+	INIT_LIST_HEAD(&mlink->rc_list);
-+	INIT_LIST_HEAD(&mlink->wcid.poll_list);
-+	mlink->wcid.idx = idx;
-+	mlink->wcid.phy_idx = band_idx;
-+	mlink->wcid.hw_key_idx = -1;
-+	mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
-+	mlink->sta = &mvif->sta;
-+	mlink->sta->vif = mvif;
-+	mt76_wcid_init(&mlink->wcid);
- 
- 	mt7996_mac_wtbl_update(dev, idx,
- 			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
-@@ -297,14 +299,15 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
- 
- 	mt7996_init_bitrate_mask(mconf);
- 
--	mt7996_mcu_add_bss_info(phy, conf, mconf, true);
-+	mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, true);
- 	/* defer the first STA_REC of BMC entry to BSS_CHANGED_BSSID for STA
- 	 * interface, since firmware only records BSSID when the entry is new
- 	 */
- 	if (vif->type != NL80211_IFTYPE_STATION)
--		mt7996_mcu_add_sta(dev, conf, mconf, NULL, true, true);
--	rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid);
-+		mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, true, true);
-+	rcu_assign_pointer(dev->mt76.wcid[idx], &mlink->wcid);
- 	rcu_assign_pointer(mvif->link[0], mconf);
-+	rcu_assign_pointer(mvif->sta.link[0], mlink);
- 
- out:
- 	mutex_unlock(&dev->mt76.mutex);
-@@ -318,10 +321,10 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
- 	struct ieee80211_bss_conf *conf;
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_bss_conf *mconf;
--	struct mt7996_sta *msta = &mvif->sta;
-+	struct mt7996_link_sta *mlink = &mvif->sta.deflink;
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
--	int idx = msta->wcid.idx;
-+	int idx = mlink->wcid.idx;
- 
- 	cancel_delayed_work_sync(&phy->scan_work);
- 
-@@ -329,8 +332,8 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
- 
- 	conf = link_conf_dereference_protected(vif, 0);
- 	mconf = mconf_dereference_protected(mvif, 0);
--	mt7996_mcu_add_sta(dev, conf, mconf, NULL, false, false);
--	mt7996_mcu_add_bss_info(phy, conf, mconf, false);
-+	mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, false, false);
-+	mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, false);
- 
- 	if (vif == phy->monitor_vif)
- 		phy->monitor_vif = NULL;
-@@ -343,12 +346,13 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
- 	phy->omac_mask &= ~BIT_ULL(mconf->mt76.omac_idx);
- 
- 	spin_lock_bh(&dev->mt76.sta_poll_lock);
--	if (!list_empty(&msta->wcid.poll_list))
--		list_del_init(&msta->wcid.poll_list);
-+	if (!list_empty(&mlink->wcid.poll_list))
-+		list_del_init(&mlink->wcid.poll_list);
- 	spin_unlock_bh(&dev->mt76.sta_poll_lock);
- 
--	mt76_wcid_cleanup(&dev->mt76, &msta->wcid);
-+	mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
- 	rcu_assign_pointer(mvif->link[0], NULL);
-+	rcu_assign_pointer(mvif->sta.link[0], NULL);
- 
- 	mutex_unlock(&dev->mt76.mutex);
- }
-@@ -447,10 +451,10 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_sta *msta = sta ? (struct mt7996_sta *)sta->drv_priv :
- 				  &mvif->sta;
--	struct mt76_wcid *wcid = &msta->wcid;
- 	struct mt7996_bss_conf *mconf;
-+	struct mt7996_link_sta *mlink;
- 	struct ieee80211_bss_conf *conf;
--	u8 *wcid_keyidx = &wcid->hw_key_idx;
-+	u8 *wcid_keyidx;
- 	int idx = key->keyidx;
- 	int err = 0;
- 
-@@ -464,6 +468,12 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
- 	    !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
- 		return -EOPNOTSUPP;
- 
-+	mutex_lock(&dev->mt76.mutex);
-+	conf = link_conf_dereference_protected(vif, 0);
-+	mconf = mconf_dereference_protected(mvif, 0);
-+	mlink = mlink_dereference_protected(msta, 0);
-+	wcid_keyidx = &mlink->wcid.hw_key_idx;
-+
- 	/* fall back to sw encryption for unsupported ciphers */
- 	switch (key->cipher) {
- 	case WLAN_CIPHER_SUITE_TKIP:
-@@ -486,16 +496,13 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
- 	case WLAN_CIPHER_SUITE_WEP40:
- 	case WLAN_CIPHER_SUITE_WEP104:
- 	default:
-+		mutex_unlock(&dev->mt76.mutex);
- 		return -EOPNOTSUPP;
- 	}
- 
--	mutex_lock(&dev->mt76.mutex);
--
--	conf = link_conf_dereference_protected(vif, 0);
--	mconf = mconf_dereference_protected(mvif, 0);
- 	if (cmd == SET_KEY && !sta && !mconf->mt76.cipher) {
- 		mconf->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher);
--		mt7996_mcu_add_bss_info(phy, conf, mconf, true);
-+		mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, true);
- 	}
- 
- 	if (cmd == SET_KEY) {
-@@ -506,14 +513,14 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
- 		goto out;
- 	}
- 
--	mt76_wcid_key_setup(&dev->mt76, wcid, key);
-+	mt76_wcid_key_setup(&dev->mt76, &mlink->wcid, key);
- 
- 	if (key->keyidx == 6 || key->keyidx == 7)
--		err = mt7996_mcu_bcn_prot_enable(dev, conf, mconf, key);
-+		err = mt7996_mcu_bcn_prot_enable(dev, conf, mconf, mlink, key);
- 	else
- 		err = mt7996_mcu_add_key(&dev->mt76, mconf, key,
- 					 MCU_WMWA_UNI_CMD(STA_REC_UPDATE),
--					 &msta->wcid, cmd);
-+					 &mlink->wcid, cmd);
- out:
- 	mutex_unlock(&dev->mt76.mutex);
- 
-@@ -716,25 +723,27 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
- {
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_bss_conf *mconf;
-+	struct mt7996_link_sta *mlink;
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
- 	mconf = mconf_dereference_protected(mvif, 0);
-+	mlink = mlink_dereference_protected(&mvif->sta, 0);
- 	/* station mode uses BSSID to map the wlan entry to a peer,
- 	 * and then peer references bss_info_rfch to set bandwidth cap.
- 	 */
- 	if ((changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) ||
- 	    (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) ||
- 	    (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon)) {
--		mt7996_mcu_add_bss_info(phy, info, mconf, true);
--		mt7996_mcu_add_sta(dev, info, mconf, NULL, true,
-+		mt7996_mcu_add_bss_info(phy, info, mconf, mlink, true);
-+		mt7996_mcu_add_sta(dev, info, mconf, NULL, mlink, true,
- 				   !!(changed & BSS_CHANGED_BSSID));
- 	}
- 
- 	if (changed & BSS_CHANGED_ERP_CTS_PROT)
--		mt7996_mac_enable_rtscts(dev, vif, info->use_cts_prot);
-+		mt7996_mac_enable_rtscts(dev, mlink, info->use_cts_prot);
- 
- 	if (changed & BSS_CHANGED_ERP_SLOT) {
- 		int slottime = info->use_short_slot ? 9 : 20;
-@@ -805,6 +814,7 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_bss_conf *mconf = mconf_dereference_protected(mvif, 0);
-+	struct mt7996_link_sta *mlink = &msta->deflink;
- 	u8 band_idx = mconf->phy->mt76->band_idx;
- 	int idx;
- 
-@@ -816,13 +826,16 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 	if (idx < 0)
- 		return -ENOSPC;
- 
--	INIT_LIST_HEAD(&msta->rc_list);
--	INIT_LIST_HEAD(&msta->wcid.poll_list);
-+	INIT_LIST_HEAD(&mlink->rc_list);
-+	INIT_LIST_HEAD(&mlink->wcid.poll_list);
- 	msta->vif = mvif;
--	msta->wcid.sta = 1;
--	msta->wcid.idx = idx;
--	msta->wcid.phy_idx = band_idx;
--	msta->wcid.tx_info |= MT_WCID_TX_INFO_SET;
-+	mlink->wcid.sta = 1;
-+	mlink->wcid.idx = idx;
-+	mlink->wcid.phy_idx = band_idx;
-+	mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
-+	mlink->sta = msta;
-+
-+	rcu_assign_pointer(msta->link[0], mlink);
- 
- #ifdef CONFIG_MTK_VENDOR
- 	mt7996_vendor_amnt_sta_remove(mconf->phy, sta);
-@@ -855,19 +868,25 @@ void mt7996_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- 	struct mt7996_bss_conf *mconf;
-+	struct mt7996_link_sta *mlink;
- 	struct ieee80211_bss_conf *conf;
-+	struct ieee80211_link_sta *link_sta;
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
--	mt7996_mac_wtbl_update(dev, msta->wcid.idx,
--			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
--
- 	conf = link_conf_dereference_protected(vif, 0);
- 	mconf = mconf_dereference_protected(mvif, 0);
--	mt7996_mcu_add_sta(dev, conf, mconf, sta, true, true);
--	mt7996_mcu_add_rate_ctrl(dev, conf, mconf, sta, false);
-+	link_sta = link_sta_dereference_protected(sta, 0);
-+	mlink = mlink_dereference_protected(msta, 0);
- 
--	ewma_avg_signal_init(&msta->avg_ack_signal);
-+	mt7996_mac_wtbl_update(dev, mlink->wcid.idx,
-+			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
-+
-+	mt7996_mcu_add_sta(dev, conf, mconf, link_sta, mlink, true, true);
-+	mt7996_mcu_add_rate_ctrl(dev, conf, mconf, link_sta, mlink, false);
-+	mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
-+
-+	ewma_avg_signal_init(&mlink->avg_ack_signal);
- 
- 	mutex_unlock(&dev->mt76.mutex);
- }
-@@ -879,25 +898,31 @@ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- 	struct mt7996_bss_conf *mconf;
-+	struct mt7996_link_sta *mlink;
- 	struct ieee80211_bss_conf *conf;
-+	struct ieee80211_link_sta *link_sta;
- 	int i;
- 
- 	conf = link_conf_dereference_protected(vif, 0);
- 	mconf = mconf_dereference_protected(mvif, 0);
--	mt7996_mcu_add_sta(dev, conf, mconf, sta, false, false);
-+	link_sta = link_sta_dereference_protected(sta, 0);
-+	mlink = mlink_dereference_protected(msta, 0);
-+	mt7996_mcu_add_sta(dev, conf, mconf, link_sta, mlink, false, false);
- 
--	mt7996_mac_wtbl_update(dev, msta->wcid.idx,
-+	mt7996_mac_wtbl_update(dev, mlink->wcid.idx,
- 			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
- 
--	for (i = 0; i < ARRAY_SIZE(msta->twt.flow); i++)
--		mt7996_mac_twt_teardown_flow(dev, msta, i);
-+	for (i = 0; i < ARRAY_SIZE(mlink->twt.flow); i++)
-+		mt7996_mac_twt_teardown_flow(dev, mlink, i);
- 
- 	spin_lock_bh(&mdev->sta_poll_lock);
--	if (!list_empty(&msta->wcid.poll_list))
--		list_del_init(&msta->wcid.poll_list);
--	if (!list_empty(&msta->rc_list))
--		list_del_init(&msta->rc_list);
-+	if (!list_empty(&mlink->wcid.poll_list))
-+		list_del_init(&mlink->wcid.poll_list);
-+	if (!list_empty(&mlink->rc_list))
-+		list_del_init(&mlink->rc_list);
- 	spin_unlock_bh(&mdev->sta_poll_lock);
-+
-+	rcu_assign_pointer(msta->link[0], NULL);
- }
- 
- static void mt7996_tx(struct ieee80211_hw *hw,
-@@ -909,19 +934,22 @@ static void mt7996_tx(struct ieee80211_hw *hw,
- 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- 	struct ieee80211_vif *vif = info->control.vif;
- 	struct mt76_wcid *wcid = &dev->mt76.global_wcid;
-+	struct mt7996_link_sta *mlink;
- 
- 	if (control->sta) {
--		struct mt7996_sta *sta;
-+		struct mt7996_sta *msta;
- 
--		sta = (struct mt7996_sta *)control->sta->drv_priv;
--		wcid = &sta->wcid;
-+		msta = (struct mt7996_sta *)control->sta->drv_priv;
-+		mlink = rcu_dereference(msta->link[0]);
-+		wcid = &mlink->wcid;
- 	}
- 
- 	if (vif && !control->sta) {
- 		struct mt7996_vif *mvif;
- 
- 		mvif = (struct mt7996_vif *)vif->drv_priv;
--		wcid = &mvif->sta.wcid;
-+		mlink = rcu_dereference(mvif->sta.link[0]);
-+		wcid = &mlink->wcid;
- 	}
- 
- 	mt76_tx(mphy, control->sta, wcid, skb);
-@@ -948,6 +976,7 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	struct ieee80211_sta *sta = params->sta;
- 	struct ieee80211_txq *txq = sta->txq[params->tid];
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_link_sta *mlink;
- 	u16 tid = params->tid;
- 	u16 ssn = params->ssn;
- 	struct mt76_txq *mtxq;
-@@ -959,14 +988,15 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	mtxq = (struct mt76_txq *)txq->drv_priv;
- 
- 	mutex_lock(&dev->mt76.mutex);
-+	mlink = mlink_dereference_protected(msta, 0);
- 	switch (action) {
- 	case IEEE80211_AMPDU_RX_START:
--		mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, ssn,
-+		mt76_rx_aggr_start(&dev->mt76, &mlink->wcid, tid, ssn,
- 				   params->buf_size);
- 		ret = mt7996_mcu_add_rx_ba(dev, params, true);
- 		break;
- 	case IEEE80211_AMPDU_RX_STOP:
--		mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid);
-+		mt76_rx_aggr_stop(&dev->mt76, &mlink->wcid, tid);
- 		ret = mt7996_mcu_add_rx_ba(dev, params, false);
- 		break;
- 	case IEEE80211_AMPDU_TX_OPERATIONAL:
-@@ -977,16 +1007,16 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	case IEEE80211_AMPDU_TX_STOP_FLUSH:
- 	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
- 		mtxq->aggr = false;
--		clear_bit(tid, &msta->wcid.ampdu_state);
-+		clear_bit(tid, &mlink->wcid.ampdu_state);
- 		ret = mt7996_mcu_add_tx_ba(dev, params, false);
- 		break;
- 	case IEEE80211_AMPDU_TX_START:
--		set_bit(tid, &msta->wcid.ampdu_state);
-+		set_bit(tid, &mlink->wcid.ampdu_state);
- 		ret = IEEE80211_AMPDU_TX_START_IMMEDIATE;
- 		break;
- 	case IEEE80211_AMPDU_TX_STOP_CONT:
- 		mtxq->aggr = false;
--		clear_bit(tid, &msta->wcid.ampdu_state);
-+		clear_bit(tid, &mlink->wcid.ampdu_state);
- 		ret = mt7996_mcu_add_tx_ba(dev, params, false);
- 		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
- 		break;
-@@ -1165,10 +1195,19 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
- 				  struct ieee80211_sta *sta,
- 				  struct station_info *sinfo)
- {
-+	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
--	struct rate_info *txrate = &msta->wcid.rate;
-+	struct mt7996_link_sta *mlink;
-+	struct rate_info *txrate;
- 
-+	/* TODO: support per-link rate report */
-+	mutex_lock(&dev->mt76.mutex);
-+	mlink = mlink_dereference_protected(msta, 0);
-+	if (!mlink)
-+		goto out;
-+
-+	txrate = &mlink->wcid.rate;
- 	if (txrate->legacy || txrate->flags) {
- 		if (txrate->legacy) {
- 			sinfo->txrate.legacy = txrate->legacy;
-@@ -1187,44 +1226,52 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
- 	sinfo->txrate.flags = txrate->flags;
- 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
- 
--	sinfo->tx_failed = msta->wcid.stats.tx_failed;
-+	sinfo->tx_failed = mlink->wcid.stats.tx_failed;
- 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
- 
--	sinfo->tx_retries = msta->wcid.stats.tx_retries;
-+	sinfo->tx_retries = mlink->wcid.stats.tx_retries;
- 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES);
- 
--	sinfo->ack_signal = (s8)msta->ack_signal;
-+	sinfo->ack_signal = (s8)mlink->ack_signal;
- 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL);
- 
--	sinfo->avg_ack_signal = -(s8)ewma_avg_signal_read(&msta->avg_ack_signal);
-+	sinfo->avg_ack_signal = -(s8)ewma_avg_signal_read(&mlink->avg_ack_signal);
- 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG);
- 
- 	if (mtk_wed_device_active(&phy->dev->mt76.mmio.wed)) {
--		sinfo->tx_bytes = msta->wcid.stats.tx_bytes;
-+		sinfo->tx_bytes = mlink->wcid.stats.tx_bytes;
- 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES64);
- 
--		sinfo->rx_bytes = msta->wcid.stats.rx_bytes;
-+		sinfo->rx_bytes = mlink->wcid.stats.rx_bytes;
- 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES64);
- 
--		sinfo->tx_packets = msta->wcid.stats.tx_packets;
-+		sinfo->tx_packets = mlink->wcid.stats.tx_packets;
- 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);
- 
--		sinfo->rx_packets = msta->wcid.stats.rx_packets;
-+		sinfo->rx_packets = mlink->wcid.stats.rx_packets;
- 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS);
- 	}
-+out:
-+	mutex_unlock(&dev->mt76.mutex);
- }
- 
- static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta)
- {
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_link_sta *mlink;
- 	struct mt7996_dev *dev = msta->vif->dev;
- 	u32 *changed = data;
- 
-+	rcu_read_lock();
-+	mlink = rcu_dereference(msta->link[0]);
-+
- 	spin_lock_bh(&dev->mt76.sta_poll_lock);
--	msta->changed |= *changed;
--	if (list_empty(&msta->rc_list))
--		list_add_tail(&msta->rc_list, &dev->sta_rc_list);
-+	mlink->changed |= *changed;
-+	if (list_empty(&mlink->rc_list))
-+		list_add_tail(&mlink->rc_list, &dev->sta_rc_list);
- 	spin_unlock_bh(&dev->mt76.sta_poll_lock);
-+
-+	rcu_read_unlock();
- }
- 
- static void mt7996_sta_rc_update(struct ieee80211_hw *hw,
-@@ -1238,7 +1285,7 @@ static void mt7996_sta_rc_update(struct ieee80211_hw *hw,
- 
- 	if (!msta->vif) {
- 		dev_warn(dev->mt76.dev, "Un-initialized STA %pM wcid %d in rc_work\n",
--			 sta->addr, msta->wcid.idx);
-+			 sta->addr, msta->deflink.wcid.idx);
- 		return;
- 	}
- 
-@@ -1284,16 +1331,18 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw,
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- 	struct mt7996_bss_conf *mconf;
-+	struct mt7996_link_sta *mlink;
- 
- 	mutex_lock(&dev->mt76.mutex);
- 	mconf = mconf_dereference_protected(mvif, 0);
-+	mlink = mlink_dereference_protected(msta, 0);
- 
- 	if (enabled)
--		set_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags);
-+		set_bit(MT_WCID_FLAG_4ADDR, &mlink->wcid.flags);
- 	else
--		clear_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags);
-+		clear_bit(MT_WCID_FLAG_4ADDR, &mlink->wcid.flags);
- 
--	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, sta);
-+	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
- 	mutex_unlock(&dev->mt76.mutex);
- }
- 
-@@ -1306,16 +1355,18 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw,
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- 	struct mt7996_bss_conf *mconf;
-+	struct mt7996_link_sta *mlink;
- 
- 	mutex_lock(&dev->mt76.mutex);
- 	mconf = mconf_dereference_protected(mvif, 0);
-+	mlink = mlink_dereference_protected(msta, 0);
- 
- 	if (enabled)
--		set_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
-+		set_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
- 	else
--		clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
-+		clear_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
- 
--	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, sta);
-+	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
- 	mutex_unlock(&dev->mt76.mutex);
- }
- 
-@@ -1448,11 +1499,12 @@ static void mt7996_ethtool_worker(void *wi_data, struct ieee80211_sta *sta)
- {
- 	struct mt76_ethtool_worker_info *wi = wi_data;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_link_sta *mlink = &msta->deflink;
- 
- 	if (msta->vif->deflink.mt76.idx != wi->idx)
- 		return;
- 
--	mt76_ethtool_worker(wi, &msta->wcid.stats, true);
-+	mt76_ethtool_worker(wi, &mlink->wcid.stats, true);
- }
- 
- static
-@@ -1555,10 +1607,12 @@ mt7996_twt_teardown_request(struct ieee80211_hw *hw,
- 			    u8 flowid)
- {
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_link_sta *mlink;
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 
- 	mutex_lock(&dev->mt76.mutex);
--	mt7996_mac_twt_teardown_flow(dev, msta, flowid);
-+	mlink = mlink_dereference_protected(msta, 0);
-+	mt7996_mac_twt_teardown_flow(dev, mlink, flowid);
- 	mutex_unlock(&dev->mt76.mutex);
- }
- 
-@@ -1681,6 +1735,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_bss_conf *mconf = &mvif->deflink;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_link_sta *mlink = &msta->deflink;
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
- 	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
-@@ -1703,7 +1758,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
- 	if (!mtk_wed_device_active(wed))
- 		return -ENODEV;
- 
--	if (msta->wcid.idx > MT7996_WTBL_STA)
-+	if (mlink->wcid.idx > MT7996_WTBL_STA)
- 		return -EIO;
- 
- 	path->type = DEV_PATH_MTK_WDMA;
-@@ -1711,11 +1766,11 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
- 	path->mtk_wdma.wdma_idx = wed->wdma_idx;
- 	path->mtk_wdma.bss = mconf->mt76.idx;
- 	path->mtk_wdma.queue = 0;
--	path->mtk_wdma.wcid = msta->wcid.idx;
-+	path->mtk_wdma.wcid = mlink->wcid.idx;
- 
- 	if (ieee80211_hw_check(hw, SUPPORTS_AMSDU_IN_AMPDU) &&
- 	    mtk_wed_is_amsdu_supported(wed))
--		path->mtk_wdma.amsdu = msta->wcid.amsdu;
-+		path->mtk_wdma.amsdu = mlink->wcid.amsdu;
- 	else
- 		path->mtk_wdma.amsdu = 0;
- 
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index ebd3192..cab4a0a 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -117,13 +117,13 @@ mt7996_mcu_get_sta_nss(u16 mcs_map)
- }
- 
- static void
--mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta,
-+mt7996_mcu_set_sta_he_mcs(struct ieee80211_link_sta *link_sta,
- 			  struct mt7996_bss_conf *mconf,
- 			  __le16 *he_mcs, u16 mcs_map)
- {
- 	enum nl80211_band band = mconf->phy->mt76->chandef.chan->band;
- 	const u16 *mask = mconf->bitrate_mask.control[band].he_mcs;
--	int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
-+	int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss;
- 
- 	for (nss = 0; nss < max_nss; nss++) {
- 		int mcs;
-@@ -166,11 +166,11 @@ mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta,
- }
- 
- static void
--mt7996_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs,
--			   const u16 *mask)
-+mt7996_mcu_set_sta_vht_mcs(struct ieee80211_link_sta *link_sta,
-+			   __le16 *vht_mcs, const u16 *mask)
- {
--	u16 mcs, mcs_map = le16_to_cpu(sta->deflink.vht_cap.vht_mcs.rx_mcs_map);
--	int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
-+	u16 mcs, mcs_map = le16_to_cpu(link_sta->vht_cap.vht_mcs.rx_mcs_map);
-+	int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss;
- 
- 	for (nss = 0; nss < max_nss; nss++, mcs_map >>= 2) {
- 		switch (mcs_map & 0x3) {
-@@ -192,13 +192,13 @@ mt7996_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs,
- }
- 
- static void
--mt7996_mcu_set_sta_ht_mcs(struct ieee80211_sta *sta, u8 *ht_mcs,
-+mt7996_mcu_set_sta_ht_mcs(struct ieee80211_link_sta *link_sta, u8 *ht_mcs,
- 			  const u8 *mask)
- {
--	int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
-+	int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss;
- 
- 	for (nss = 0; nss < max_nss; nss++)
--		ht_mcs[nss] = sta->deflink.ht_cap.mcs.rx_mask[nss] & mask[nss];
-+		ht_mcs[nss] = link_sta->ht_cap.mcs.rx_mask[nss] & mask[nss];
- }
- 
- static int
-@@ -531,14 +531,14 @@ static inline void __mt7996_stat_to_netdev(struct mt76_phy *mphy,
- 					   u32 tx_bytes, u32 rx_bytes,
- 					   u32 tx_packets, u32 rx_packets)
- {
--	struct mt7996_sta *msta;
-+	struct mt7996_link_sta *mlink;
- 	struct ieee80211_vif *vif;
- 	struct wireless_dev *wdev;
- 
- 	if (wiphy_ext_feature_isset(mphy->hw->wiphy,
- 				    NL80211_EXT_FEATURE_STAS_COUNT)) {
--		msta = container_of(wcid, struct mt7996_sta, wcid);
--		vif = container_of((void *)msta->vif, struct ieee80211_vif,
-+		mlink = container_of(wcid, struct mt7996_link_sta, wcid);
-+		vif = container_of((void *)mlink->sta->vif, struct ieee80211_vif,
- 				   drv_priv);
- 		wdev = ieee80211_vif_to_wdev(vif);
- 
-@@ -1236,10 +1236,10 @@ __mt7996_mcu_alloc_bss_req(struct mt76_dev *dev, struct mt76_vif *mvif, int len)
- 
- int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
- 			    struct ieee80211_bss_conf *conf,
--			    struct mt7996_bss_conf *mconf, int enable)
-+			    struct mt7996_bss_conf *mconf,
-+			    struct mt7996_link_sta *mlink, int enable)
- {
- 	struct ieee80211_vif *vif = conf->vif;
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_dev *dev = phy->dev;
- 	struct sk_buff *skb;
- 
-@@ -1255,7 +1255,7 @@ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
- 
- 	/* bss_basic must be first */
- 	mt7996_mcu_bss_basic_tlv(skb, conf, mconf, NULL, phy->mt76,
--				 mvif->sta.wcid.idx, enable);
-+				 mlink->wcid.idx, enable);
- 	mt7996_mcu_bss_sec_tlv(skb, mconf);
- 
- 	if (vif->type == NL80211_IFTYPE_MONITOR)
-@@ -1335,9 +1335,10 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
- {
- 	struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv;
- 	struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
-+	struct mt7996_link_sta *mlink = mlink_dereference_protected(msta, 0);
- 
- 	if (enable && !params->amsdu)
--		msta->wcid.amsdu = false;
-+		mlink->wcid.amsdu = false;
- 
- 	return mt7996_mcu_sta_ba(dev, &mconf->mt76, params, enable, true);
- }
-@@ -1355,15 +1356,15 @@ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
- static void
- mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
- 		      struct mt7996_bss_conf *mconf,
--		      struct ieee80211_sta *sta)
-+		      struct ieee80211_link_sta *link_sta)
- {
--	struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
-+	struct ieee80211_he_cap_elem *elem = &link_sta->he_cap.he_cap_elem;
- 	struct ieee80211_he_mcs_nss_supp mcs_map;
- 	struct sta_rec_he_v2 *he;
- 	struct tlv *tlv;
- 	int i = 0;
- 
--	if (!sta->deflink.he_cap.has_he)
-+	if (!link_sta->he_cap.has_he)
- 		return;
- 
- 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_V2, sizeof(*he));
-@@ -1380,21 +1381,21 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
- 		u8p_replace_bits(&he->he_phy_cap[1], conf->he_ldpc,
- 				 IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD);
- 
--	mcs_map = sta->deflink.he_cap.he_mcs_nss_supp;
--	switch (sta->deflink.bandwidth) {
-+	mcs_map = link_sta->he_cap.he_mcs_nss_supp;
-+	switch (link_sta->bandwidth) {
- 	case IEEE80211_STA_RX_BW_160:
- 		if (elem->phy_cap_info[0] &
- 		    IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)
--			mt7996_mcu_set_sta_he_mcs(sta, mconf,
-+			mt7996_mcu_set_sta_he_mcs(link_sta, mconf,
- 						  &he->max_nss_mcs[CMD_HE_MCS_BW8080],
- 						  le16_to_cpu(mcs_map.rx_mcs_80p80));
- 
--		mt7996_mcu_set_sta_he_mcs(sta, mconf,
-+		mt7996_mcu_set_sta_he_mcs(link_sta, mconf,
- 					  &he->max_nss_mcs[CMD_HE_MCS_BW160],
- 					  le16_to_cpu(mcs_map.rx_mcs_160));
- 		fallthrough;
- 	default:
--		mt7996_mcu_set_sta_he_mcs(sta, mconf,
-+		mt7996_mcu_set_sta_he_mcs(link_sta, mconf,
- 					  &he->max_nss_mcs[CMD_HE_MCS_BW80],
- 					  le16_to_cpu(mcs_map.rx_mcs_80));
- 		break;
-@@ -1404,24 +1405,25 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
- }
- 
- static void
--mt7996_mcu_sta_he_6g_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
-+mt7996_mcu_sta_he_6g_tlv(struct sk_buff *skb,
-+			 struct ieee80211_link_sta *link_sta)
- {
- 	struct sta_rec_he_6g_capa *he_6g;
- 	struct tlv *tlv;
- 
--	if (!sta->deflink.he_6ghz_capa.capa)
-+	if (!link_sta->he_6ghz_capa.capa)
- 		return;
- 
- 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_6G, sizeof(*he_6g));
- 
- 	he_6g = (struct sta_rec_he_6g_capa *)tlv;
--	he_6g->capa = sta->deflink.he_6ghz_capa.capa;
-+	he_6g->capa = link_sta->he_6ghz_capa.capa;
- }
- 
- static void
--mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
-+mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta)
- {
--	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv;
- 	struct ieee80211_vif *vif = container_of((void *)msta->vif,
- 						 struct ieee80211_vif, drv_priv);
- 	struct ieee80211_eht_mcs_nss_supp *mcs_map;
-@@ -1429,11 +1431,11 @@ mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
- 	struct sta_rec_eht *eht;
- 	struct tlv *tlv;
- 
--	if (!sta->deflink.eht_cap.has_eht)
-+	if (!link_sta->eht_cap.has_eht)
- 		return;
- 
--	mcs_map = &sta->deflink.eht_cap.eht_mcs_nss_supp;
--	elem = &sta->deflink.eht_cap.eht_cap_elem;
-+	mcs_map = &link_sta->eht_cap.eht_mcs_nss_supp;
-+	elem = &link_sta->eht_cap.eht_cap_elem;
- 
- 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT, sizeof(*eht));
- 
-@@ -1444,7 +1446,7 @@ mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
- 	eht->phy_cap_ext = cpu_to_le64(elem->phy_cap_info[8]);
- 
- 	if (vif->type != NL80211_IFTYPE_STATION &&
--	    (sta->deflink.he_cap.he_cap_elem.phy_cap_info[0] &
-+	    (link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
- 	     (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
- 	      IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
- 	      IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
-@@ -1460,44 +1462,44 @@ mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
- }
- 
- static void
--mt7996_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
-+mt7996_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta)
- {
- 	struct sta_rec_ht_uni *ht;
- 	struct tlv *tlv;
- 
--	if (!sta->deflink.ht_cap.ht_supported)
-+	if (!link_sta->ht_cap.ht_supported)
- 		return;
- 
- 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht));
- 
- 	ht = (struct sta_rec_ht_uni *)tlv;
--	ht->ht_cap = cpu_to_le16(sta->deflink.ht_cap.cap);
--	ht->ampdu_param = u8_encode_bits(sta->deflink.ht_cap.ampdu_factor,
-+	ht->ht_cap = cpu_to_le16(link_sta->ht_cap.cap);
-+	ht->ampdu_param = u8_encode_bits(link_sta->ht_cap.ampdu_factor,
- 					 IEEE80211_HT_AMPDU_PARM_FACTOR) |
--			  u8_encode_bits(sta->deflink.ht_cap.ampdu_density,
-+			  u8_encode_bits(link_sta->ht_cap.ampdu_density,
- 					 IEEE80211_HT_AMPDU_PARM_DENSITY);
- }
- 
- static void
--mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
-+mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta)
- {
- 	struct sta_rec_vht *vht;
- 	struct tlv *tlv;
- #ifdef CONFIG_MTK_VENDOR
--	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv;
- 	struct mt7996_phy *phy = (struct mt7996_phy *)msta->vif->deflink.phy;
- #endif
- 
- 	/* For 6G band, this tlv is necessary to let hw work normally */
--	if (!sta->deflink.he_6ghz_capa.capa && !sta->deflink.vht_cap.vht_supported)
-+	if (!link_sta->he_6ghz_capa.capa && !link_sta->vht_cap.vht_supported)
- 		return;
- 
- 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht));
- 
- 	vht = (struct sta_rec_vht *)tlv;
--	vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap);
--	vht->vht_rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map;
--	vht->vht_tx_mcs_map = sta->deflink.vht_cap.vht_mcs.tx_mcs_map;
-+	vht->vht_cap = cpu_to_le32(link_sta->vht_cap.cap);
-+	vht->vht_rx_mcs_map = link_sta->vht_cap.vht_mcs.rx_mcs_map;
-+	vht->vht_tx_mcs_map = link_sta->vht_cap.vht_mcs.tx_mcs_map;
- #ifdef CONFIG_MTK_VENDOR
- 	vht->rts_bw_sig = phy->rts_bw_sig;
- #endif
-@@ -1505,9 +1507,10 @@ mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
- 
- static void
- mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
--			 struct ieee80211_vif *vif, struct ieee80211_sta *sta)
-+			 struct ieee80211_vif *vif,
-+			 struct ieee80211_link_sta *link_sta,
-+			 struct mt7996_link_sta *mlink)
- {
--	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- 	struct sta_rec_amsdu *amsdu;
- 	struct tlv *tlv;
- 
-@@ -1516,16 +1519,16 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- 	    vif->type != NL80211_IFTYPE_AP)
- 		return;
- 
--	if (!sta->deflink.agg.max_amsdu_len)
-+	if (!link_sta->agg.max_amsdu_len)
- 		return;
- 
- 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu));
- 	amsdu = (struct sta_rec_amsdu *)tlv;
- 	amsdu->max_amsdu_num = 8;
- 	amsdu->amsdu_en = true;
--	msta->wcid.amsdu = true;
-+	mlink->wcid.amsdu = true;
- 
--	switch (sta->deflink.agg.max_amsdu_len) {
-+	switch (link_sta->agg.max_amsdu_len) {
- 	case IEEE80211_MAX_MPDU_LEN_VHT_11454:
- 		amsdu->max_mpdu_size =
- 			IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
-@@ -1544,10 +1547,10 @@ static void
- mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- 			struct ieee80211_bss_conf *conf,
- 			struct mt7996_bss_conf *mconf,
--			struct ieee80211_sta *sta)
-+			struct ieee80211_link_sta *link_sta)
- {
- 	struct mt7996_phy *phy = mconf->phy;
--	struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
-+	struct ieee80211_he_cap_elem *elem = &link_sta->he_cap.he_cap_elem;
- 	struct sta_rec_muru *muru;
- 	struct tlv *tlv;
- 
-@@ -1567,11 +1570,11 @@ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- 	muru->cfg.ofdma_dl_en = !!(phy->muru_onoff & OFDMA_DL);
- 	muru->cfg.ofdma_ul_en = !!(phy->muru_onoff & OFDMA_UL);
- 
--	if (sta->deflink.vht_cap.vht_supported)
-+	if (link_sta->vht_cap.vht_supported)
- 		muru->mimo_dl.vht_mu_bfee =
--			!!(sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
-+			!!(link_sta->vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
- 
--	if (!sta->deflink.he_cap.has_he)
-+	if (!link_sta->he_cap.has_he)
- 		return;
- 
- 	muru->mimo_dl.partial_bw_dl_mimo =
-@@ -1604,7 +1607,7 @@ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- static inline bool
- mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
- 			struct mt7996_bss_conf *mconf,
--			struct ieee80211_sta *sta, bool bfee)
-+			struct ieee80211_link_sta *link_sta, bool bfee)
- {
- 	int sts = hweight16(phy->mt76->chainmask);
- 
-@@ -1615,8 +1618,8 @@ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
- 	if (!bfee && sts < 2)
- 		return false;
- 
--	if (sta->deflink.eht_cap.has_eht) {
--		struct ieee80211_sta_eht_cap *pc = &sta->deflink.eht_cap;
-+	if (link_sta->eht_cap.has_eht) {
-+		struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap;
- 		struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem;
- 
- 		if (bfee)
-@@ -1627,8 +1630,8 @@ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
- 			       EHT_PHY(CAP0_SU_BEAMFORMER, pe->phy_cap_info[0]);
- 	}
- 
--	if (sta->deflink.he_cap.has_he) {
--		struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem;
-+	if (link_sta->he_cap.has_he) {
-+		struct ieee80211_he_cap_elem *pe = &link_sta->he_cap.he_cap_elem;
- 
- 		if (bfee)
- 			return conf->he_su_beamformee &&
-@@ -1638,8 +1641,8 @@ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
- 			       HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]);
- 	}
- 
--	if (sta->deflink.vht_cap.vht_supported) {
--		u32 cap = sta->deflink.vht_cap.cap;
-+	if (link_sta->vht_cap.vht_supported) {
-+		u32 cap = link_sta->vht_cap.cap;
- 
- 		if (bfee)
- 			return conf->vht_su_beamformee &&
-@@ -1662,10 +1665,10 @@ mt7996_mcu_sta_sounding_rate(struct sta_rec_bf *bf)
- }
- 
- static void
--mt7996_mcu_sta_bfer_ht(struct ieee80211_sta *sta, struct mt7996_phy *phy,
--		       struct sta_rec_bf *bf)
-+mt7996_mcu_sta_bfer_ht(struct ieee80211_link_sta *link_sta,
-+		       struct mt7996_phy *phy, struct sta_rec_bf *bf)
- {
--	struct ieee80211_mcs_info *mcs = &sta->deflink.ht_cap.mcs;
-+	struct ieee80211_mcs_info *mcs = &link_sta->ht_cap.mcs;
- 	u8 n = 0;
- 
- 	bf->tx_mode = MT_PHY_TYPE_HT;
-@@ -1687,10 +1690,11 @@ mt7996_mcu_sta_bfer_ht(struct ieee80211_sta *sta, struct mt7996_phy *phy,
- }
- 
- static void
--mt7996_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7996_phy *phy,
--			struct sta_rec_bf *bf, bool explicit)
-+mt7996_mcu_sta_bfer_vht(struct ieee80211_link_sta *link_sta,
-+			struct mt7996_phy *phy, struct sta_rec_bf *bf,
-+			bool explicit)
- {
--	struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap;
-+	struct ieee80211_sta_vht_cap *pc = &link_sta->vht_cap;
- 	struct ieee80211_sta_vht_cap *vc = &phy->mt76->sband_5g.sband.vht_cap;
- 	u16 mcs_map = le16_to_cpu(pc->vht_mcs.rx_mcs_map);
- 	u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map);
-@@ -1711,23 +1715,24 @@ mt7996_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7996_phy *phy,
- 		bf->ncol = min_t(u8, nss_mcs, bf->nrow);
- 		bf->ibf_ncol = bf->ncol;
- 
--		if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
-+		if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160)
- 			bf->nrow = 1;
- 	} else {
- 		bf->nrow = tx_ant;
- 		bf->ncol = min_t(u8, nss_mcs, bf->nrow);
- 		bf->ibf_ncol = nss_mcs;
- 
--		if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
-+		if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160)
- 			bf->ibf_nrow = 1;
- 	}
- }
- 
- static void
--mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
--		       struct mt7996_phy *phy, struct sta_rec_bf *bf)
-+mt7996_mcu_sta_bfer_he(struct ieee80211_link_sta *link_sta,
-+		       struct ieee80211_vif *vif, struct mt7996_phy *phy,
-+		       struct sta_rec_bf *bf)
- {
--	struct ieee80211_sta_he_cap *pc = &sta->deflink.he_cap;
-+	struct ieee80211_sta_he_cap *pc = &link_sta->he_cap;
- 	struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem;
- 	const struct ieee80211_sta_he_cap *vc =
- 		mt76_connac_get_he_phy_cap(phy->mt76, vif);
-@@ -1752,7 +1757,7 @@ mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
- 	bf->ncol = min_t(u8, nss_mcs, bf->nrow);
- 	bf->ibf_ncol = bf->ncol;
- 
--	if (sta->deflink.bandwidth != IEEE80211_STA_RX_BW_160)
-+	if (link_sta->bandwidth != IEEE80211_STA_RX_BW_160)
- 		return;
- 
- 	/* go over for 160MHz and 80p80 */
-@@ -1784,10 +1789,11 @@ mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
- }
- 
- static void
--mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
--			struct mt7996_phy *phy, struct sta_rec_bf *bf)
-+mt7996_mcu_sta_bfer_eht(struct ieee80211_link_sta *link_sta,
-+			struct ieee80211_vif *vif, struct mt7996_phy *phy,
-+			struct sta_rec_bf *bf)
- {
--	struct ieee80211_sta_eht_cap *pc = &sta->deflink.eht_cap;
-+	struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap;
- 	struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem;
- 	struct ieee80211_eht_mcs_nss_supp *eht_nss = &pc->eht_mcs_nss_supp;
- 	const struct ieee80211_sta_eht_cap *vc =
-@@ -1810,10 +1816,10 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
- 	bf->ncol = min_t(u8, nss_mcs, bf->nrow);
- 	bf->ibf_ncol = bf->ncol;
- 
--	if (sta->deflink.bandwidth < IEEE80211_STA_RX_BW_160)
-+	if (link_sta->bandwidth < IEEE80211_STA_RX_BW_160)
- 		return;
- 
--	switch (sta->deflink.bandwidth) {
-+	switch (link_sta->bandwidth) {
- 	case IEEE80211_STA_RX_BW_160:
- 		snd_dim = EHT_PHY(CAP2_SOUNDING_DIM_160MHZ_MASK, ve->phy_cap_info[2]);
- 		sts = EHT_PHY(CAP1_BEAMFORMEE_SS_160MHZ_MASK, pe->phy_cap_info[1]);
-@@ -1842,7 +1848,7 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
- static void
- mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- 			struct ieee80211_bss_conf *conf, struct mt7996_bss_conf *mconf,
--			struct ieee80211_sta *sta)
-+			struct ieee80211_link_sta *link_sta)
- {
- 	struct mt7996_phy *phy = mconf->phy;
- 	int tx_ant = hweight16(phy->mt76->chainmask) - 1;
-@@ -1856,10 +1862,10 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- 	};
- 	bool ebf;
- 
--	if (!(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he))
-+	if (!(link_sta->ht_cap.ht_supported || link_sta->he_cap.has_he))
- 		return;
- 
--	ebf = mt7996_is_ebf_supported(phy, conf, mconf, sta, false);
-+	ebf = mt7996_is_ebf_supported(phy, conf, mconf, link_sta, false);
- 	if (!ebf && !dev->ibf)
- 		return;
- 
-@@ -1870,23 +1876,23 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- 	 * vht: support eBF and iBF
- 	 * ht: iBF only, since mac80211 lacks of eBF support
- 	 */
--	if (sta->deflink.eht_cap.has_eht && ebf)
--		mt7996_mcu_sta_bfer_eht(sta, conf->vif, phy, bf);
--	else if (sta->deflink.he_cap.has_he && ebf)
--		mt7996_mcu_sta_bfer_he(sta, conf->vif, phy, bf);
--	else if (sta->deflink.vht_cap.vht_supported)
--		mt7996_mcu_sta_bfer_vht(sta, phy, bf, ebf);
--	else if (sta->deflink.ht_cap.ht_supported)
--		mt7996_mcu_sta_bfer_ht(sta, phy, bf);
-+	if (link_sta->eht_cap.has_eht && ebf)
-+		mt7996_mcu_sta_bfer_eht(link_sta, conf->vif, phy, bf);
-+	else if (link_sta->he_cap.has_he && ebf)
-+		mt7996_mcu_sta_bfer_he(link_sta, conf->vif, phy, bf);
-+	else if (link_sta->vht_cap.vht_supported)
-+		mt7996_mcu_sta_bfer_vht(link_sta, phy, bf, ebf);
-+	else if (link_sta->ht_cap.ht_supported)
-+		mt7996_mcu_sta_bfer_ht(link_sta, phy, bf);
- 	else
- 		return;
- 
- 	bf->bf_cap = ebf ? ebf : dev->ibf << 1;
--	bf->bw = sta->deflink.bandwidth;
--	bf->ibf_dbw = sta->deflink.bandwidth;
-+	bf->bw = link_sta->bandwidth;
-+	bf->ibf_dbw = link_sta->bandwidth;
- 	bf->ibf_nrow = tx_ant;
- 
--	if (!ebf && sta->deflink.bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol)
-+	if (!ebf && link_sta->bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol)
- 		bf->ibf_timeout = 0x48;
- 	else
- 		bf->ibf_timeout = 0x18;
-@@ -1896,7 +1902,7 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- 	else
- 		bf->mem_20m = matrix[bf->nrow][bf->ncol];
- 
--	switch (sta->deflink.bandwidth) {
-+	switch (link_sta->bandwidth) {
- 	case IEEE80211_STA_RX_BW_160:
- 	case IEEE80211_STA_RX_BW_80:
- 		bf->mem_total = bf->mem_20m * 2;
-@@ -1913,7 +1919,8 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- static void
- mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- 			struct ieee80211_bss_conf *conf,
--			struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta)
-+			struct mt7996_bss_conf *mconf,
-+			struct ieee80211_link_sta *link_sta)
- {
- 	struct mt7996_phy *phy = mconf->phy;
- 	int tx_ant = hweight8(phy->mt76->antenna_mask) - 1;
-@@ -1921,22 +1928,22 @@ mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- 	struct tlv *tlv;
- 	u8 nrow = 0;
- 
--	if (!(sta->deflink.vht_cap.vht_supported || sta->deflink.he_cap.has_he))
-+	if (!(link_sta->vht_cap.vht_supported || link_sta->he_cap.has_he))
- 		return;
- 
--	if (!mt7996_is_ebf_supported(phy, conf, mconf, sta, true))
-+	if (!mt7996_is_ebf_supported(phy, conf, mconf, link_sta, true))
- 		return;
- 
- 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BFEE, sizeof(*bfee));
- 	bfee = (struct sta_rec_bfee *)tlv;
- 
--	if (sta->deflink.he_cap.has_he) {
--		struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem;
-+	if (link_sta->he_cap.has_he) {
-+		struct ieee80211_he_cap_elem *pe = &link_sta->he_cap.he_cap_elem;
- 
- 		nrow = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK,
- 			      pe->phy_cap_info[5]);
--	} else if (sta->deflink.vht_cap.vht_supported) {
--		struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap;
-+	} else if (link_sta->vht_cap.vht_supported) {
-+		struct ieee80211_sta_vht_cap *pc = &link_sta->vht_cap;
- 
- 		nrow = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK,
- 				 pc->cap);
-@@ -1973,25 +1980,24 @@ mt7996_mcu_sta_hdrt_tlv(struct mt7996_dev *dev, struct sk_buff *skb)
- static void
- mt7996_mcu_sta_hdr_trans_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- 			     struct ieee80211_vif *vif,
--			     struct ieee80211_sta *sta)
-+			     struct mt7996_link_sta *mlink)
- {
- 	struct sta_rec_hdr_trans *hdr_trans;
--	struct mt76_wcid *wcid;
-+	struct mt76_wcid *wcid = &mlink->wcid;
- 	struct tlv *tlv;
- 
- 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HDR_TRANS, sizeof(*hdr_trans));
- 	hdr_trans = (struct sta_rec_hdr_trans *)tlv;
- 	hdr_trans->dis_rx_hdr_tran = true;
- 
-+	if (!wcid->sta)
-+		return;
-+
- 	if (vif->type == NL80211_IFTYPE_STATION)
- 		hdr_trans->to_ds = true;
- 	else
- 		hdr_trans->from_ds = true;
- 
--	if (!sta)
--		return;
--
--	wcid = (struct mt76_wcid *)sta->drv_priv;
- 	hdr_trans->dis_rx_hdr_tran = !test_bit(MT_WCID_FLAG_HDR_TRANS, &wcid->flags);
- 	if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) {
- 		hdr_trans->to_ds = true;
-@@ -2048,16 +2054,17 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
- 
- int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
- 			       struct mt7996_bss_conf *mconf,
--			       struct ieee80211_sta *sta, void *data, u32 field)
-+			       struct ieee80211_link_sta *link_sta,
-+			       struct mt7996_link_sta *mlink, void *data,
-+			       u32 field)
- {
--	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- 	struct sta_phy_uni *phy = data;
- 	struct sta_rec_ra_fixed_uni *ra;
- 	struct sk_buff *skb;
- 	struct tlv *tlv;
- 
- 	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
--					      &msta->wcid,
-+					      &mlink->wcid,
- 					      MT7996_STA_UPDATE_MAX_SIZE);
- 	if (IS_ERR(skb))
- 		return PTR_ERR(skb);
-@@ -2076,7 +2083,7 @@ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
- 			ra->phy = *phy;
- 		break;
- 	case RATE_PARAM_MMPS_UPDATE:
--		ra->mmps_mode = mt7996_mcu_get_mmps_mode(sta->deflink.smps_mode);
-+		ra->mmps_mode = mt7996_mcu_get_mmps_mode(link_sta->smps_mode);
- 		break;
- 	default:
- 		break;
-@@ -2091,7 +2098,8 @@ static int
- mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev,
- 			       struct ieee80211_bss_conf *conf,
- 			       struct mt7996_bss_conf *mconf,
--			       struct ieee80211_sta *sta)
-+			       struct ieee80211_link_sta *link_sta,
-+			       struct mt7996_link_sta *mlink)
- {
- 	struct cfg80211_chan_def *chandef = &mconf->phy->mt76->chandef;
- 	struct cfg80211_bitrate_mask *mask = &mconf->bitrate_mask;
-@@ -2115,11 +2123,11 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev,
- 		}								\
- 	} while (0)
- 
--	if (sta->deflink.he_cap.has_he) {
-+	if (link_sta->he_cap.has_he) {
- 		__sta_phy_bitrate_mask_check(he_mcs, he_gi, 0, 1);
--	} else if (sta->deflink.vht_cap.vht_supported) {
-+	} else if (link_sta->vht_cap.vht_supported) {
- 		__sta_phy_bitrate_mask_check(vht_mcs, gi, 0, 0);
--	} else if (sta->deflink.ht_cap.ht_supported) {
-+	} else if (link_sta->ht_cap.ht_supported) {
- 		__sta_phy_bitrate_mask_check(ht_mcs, gi, 1, 0);
- 	} else {
- 		nrates = hweight32(mask->control[band].legacy);
-@@ -2136,8 +2144,8 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev,
- 
- 	/* fixed single rate */
- 	if (nrates == 1) {
--		ret = mt7996_mcu_set_fixed_field(dev, mconf, sta, &phy,
--						 RATE_PARAM_FIXED_MCS);
-+		ret = mt7996_mcu_set_fixed_field(dev, mconf, link_sta, mlink,
-+						 &phy, RATE_PARAM_FIXED_MCS);
- 		if (ret)
- 			return ret;
- 	}
-@@ -2145,29 +2153,28 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev,
- 	/* fixed GI */
- 	if (mask->control[band].gi != NL80211_TXRATE_DEFAULT_GI ||
- 	    mask->control[band].he_gi != GENMASK(7, 0)) {
--		struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- 		u32 addr;
- 
- 		/* firmware updates only TXCMD but doesn't take WTBL into
- 		 * account, so driver should update here to reflect the
- 		 * actual txrate hardware sends out.
- 		 */
--		addr = mt7996_mac_wtbl_lmac_addr(dev, msta->wcid.idx, 7);
--		if (sta->deflink.he_cap.has_he)
-+		addr = mt7996_mac_wtbl_lmac_addr(dev, mlink->wcid.idx, 7);
-+		if (link_sta->he_cap.has_he)
- 			mt76_rmw_field(dev, addr, GENMASK(31, 24), phy.sgi);
- 		else
- 			mt76_rmw_field(dev, addr, GENMASK(15, 12), phy.sgi);
- 
--		ret = mt7996_mcu_set_fixed_field(dev, mconf, sta, &phy,
--						 RATE_PARAM_FIXED_GI);
-+		ret = mt7996_mcu_set_fixed_field(dev, mconf, link_sta, mlink,
-+						 &phy, RATE_PARAM_FIXED_GI);
- 		if (ret)
- 			return ret;
- 	}
- 
- 	/* fixed HE_LTF */
- 	if (mask->control[band].he_ltf != GENMASK(7, 0)) {
--		ret = mt7996_mcu_set_fixed_field(dev, mconf, sta, &phy,
--						 RATE_PARAM_FIXED_HE_LTF);
-+		ret = mt7996_mcu_set_fixed_field(dev, mconf, link_sta, mlink,
-+						 &phy, RATE_PARAM_FIXED_HE_LTF);
- 		if (ret)
- 			return ret;
- 	}
-@@ -2179,7 +2186,7 @@ static void
- mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
- 			     struct ieee80211_bss_conf *conf,
- 			     struct mt7996_bss_conf *mconf,
--			     struct ieee80211_sta *sta)
-+			     struct ieee80211_link_sta *link_sta)
- {
- #define INIT_RCPI 180
- 	struct mt76_phy *mphy = mconf->phy->mt76;
-@@ -2188,20 +2195,20 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
- 	enum nl80211_band band = chandef->chan->band;
- 	struct sta_rec_ra_uni *ra;
- 	struct tlv *tlv;
--	u32 supp_rate = sta->deflink.supp_rates[band];
--	u32 cap = sta->wme ? STA_CAP_WMM : 0;
-+	u32 supp_rate = link_sta->supp_rates[band];
-+	u32 cap = link_sta->sta->wme ? STA_CAP_WMM : 0;
- 
- 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra));
- 	ra = (struct sta_rec_ra_uni *)tlv;
- 
- 	ra->valid = true;
- 	ra->auto_rate = true;
--	ra->phy_mode = mt76_connac_get_phy_mode(mphy, conf->vif, band, sta);
-+	ra->phy_mode = mt76_connac_get_phy_mode(mphy, conf->vif, band, link_sta);
- 	ra->channel = chandef->chan->hw_value;
--	ra->bw = (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_320) ?
--		 CMD_CBW_320MHZ : sta->deflink.bandwidth;
-+	ra->bw = (link_sta->bandwidth == IEEE80211_STA_RX_BW_320) ?
-+		 CMD_CBW_320MHZ : link_sta->bandwidth;
- 	ra->phy.bw = ra->bw;
--	ra->mmps_mode = mt7996_mcu_get_mmps_mode(sta->deflink.smps_mode);
-+	ra->mmps_mode = mt7996_mcu_get_mmps_mode(link_sta->smps_mode);
- 
- 	if (supp_rate) {
- 		supp_rate &= mask->control[band].legacy;
-@@ -2221,60 +2228,60 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
- 		}
- 	}
- 
--	if (sta->deflink.ht_cap.ht_supported) {
-+	if (link_sta->ht_cap.ht_supported) {
- 		ra->supp_mode |= MODE_HT;
--		ra->af = sta->deflink.ht_cap.ampdu_factor;
--		ra->ht_gf = !!(sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD);
-+		ra->af = link_sta->ht_cap.ampdu_factor;
-+		ra->ht_gf = !!(link_sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD);
- 
- 		cap |= STA_CAP_HT;
--		if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
-+		if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
- 			cap |= STA_CAP_SGI_20;
--		if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40)
-+		if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40)
- 			cap |= STA_CAP_SGI_40;
--		if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)
-+		if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)
- 			cap |= STA_CAP_TX_STBC;
--		if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
-+		if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
- 			cap |= STA_CAP_RX_STBC;
- 		if (conf->ht_ldpc &&
--		    (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING))
-+		    (link_sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING))
- 			cap |= STA_CAP_LDPC;
- 
--		mt7996_mcu_set_sta_ht_mcs(sta, ra->ht_mcs,
-+		mt7996_mcu_set_sta_ht_mcs(link_sta, ra->ht_mcs,
- 					  mask->control[band].ht_mcs);
- 		ra->supp_ht_mcs = *(__le32 *)ra->ht_mcs;
- 	}
- 
--	if (sta->deflink.vht_cap.vht_supported) {
-+	if (link_sta->vht_cap.vht_supported) {
- 		u8 af;
- 
- 		ra->supp_mode |= MODE_VHT;
- 		af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK,
--			       sta->deflink.vht_cap.cap);
-+			       link_sta->vht_cap.cap);
- 		ra->af = max_t(u8, ra->af, af);
- 
- 		cap |= STA_CAP_VHT;
--		if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80)
-+		if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80)
- 			cap |= STA_CAP_VHT_SGI_80;
--		if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160)
-+		if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160)
- 			cap |= STA_CAP_VHT_SGI_160;
--		if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC)
-+		if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC)
- 			cap |= STA_CAP_VHT_TX_STBC;
--		if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1)
-+		if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1)
- 			cap |= STA_CAP_VHT_RX_STBC;
- 		if (conf->vht_ldpc &&
--		    (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC))
-+		    (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC))
- 			cap |= STA_CAP_VHT_LDPC;
- 
--		mt7996_mcu_set_sta_vht_mcs(sta, ra->supp_vht_mcs,
-+		mt7996_mcu_set_sta_vht_mcs(link_sta, ra->supp_vht_mcs,
- 					   mask->control[band].vht_mcs);
- 	}
- 
--	if (sta->deflink.he_cap.has_he) {
-+	if (link_sta->he_cap.has_he) {
- 		ra->supp_mode |= MODE_HE;
- 		cap |= STA_CAP_HE;
- 
--		if (sta->deflink.he_6ghz_capa.capa)
--			ra->af = le16_get_bits(sta->deflink.he_6ghz_capa.capa,
-+		if (link_sta->he_6ghz_capa.capa)
-+			ra->af = le16_get_bits(link_sta->he_6ghz_capa.capa,
- 					       IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP);
- 	}
- 	ra->sta_cap = cpu_to_le32(cap);
-@@ -2285,14 +2292,14 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
- int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
- 			     struct ieee80211_bss_conf *conf,
- 			     struct mt7996_bss_conf *mconf,
--			     struct ieee80211_sta *sta, bool changed)
-+			     struct ieee80211_link_sta *link_sta,
-+			     struct mt7996_link_sta *mlink, bool changed)
- {
--	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- 	struct sk_buff *skb;
- 	int ret;
- 
- 	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
--					      &msta->wcid,
-+					      &mlink->wcid,
- 					      MT7996_STA_UPDATE_MAX_SIZE);
- 	if (IS_ERR(skb))
- 		return PTR_ERR(skb);
-@@ -2302,26 +2309,27 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
- 	 * update sta_rec_he here.
- 	 */
- 	if (changed)
--		mt7996_mcu_sta_he_tlv(skb, conf, mconf, sta);
-+		mt7996_mcu_sta_he_tlv(skb, conf, mconf, link_sta);
- 
- 	/* sta_rec_ra accommodates BW, NSS and only MCS range format
- 	 * i.e 0-{7,8,9} for VHT.
- 	 */
--	mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, conf, mconf, sta);
-+	mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, conf, mconf, link_sta);
- 
- 	ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
- 				    MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
- 	if (ret)
- 		return ret;
- 
--	return mt7996_mcu_add_rate_ctrl_fixed(dev, conf, mconf, sta);
-+	return mt7996_mcu_add_rate_ctrl_fixed(dev, conf, mconf, link_sta, mlink);
- }
- 
- static int
--mt7996_mcu_sta_init_vow(struct mt7996_bss_conf *mconf, struct mt7996_sta *msta)
-+mt7996_mcu_sta_init_vow(struct mt7996_bss_conf *mconf,
-+			struct mt7996_link_sta *mlink)
- {
- 	struct mt7996_phy *phy = mconf->phy;
--	struct mt7996_vow_sta_ctrl *vow = &msta->vow;
-+	struct mt7996_vow_sta_ctrl *vow = &mlink->vow;
- 	u8 omac_idx = mconf->mt76.omac_idx;
- 	int ret;
- 
-@@ -2339,73 +2347,70 @@ mt7996_mcu_sta_init_vow(struct mt7996_bss_conf *mconf, struct mt7996_sta *msta)
- 	vow->drr_quantum[IEEE80211_AC_BE] = VOW_DRR_QUANTUM_IDX2;
- 	vow->drr_quantum[IEEE80211_AC_BK] = VOW_DRR_QUANTUM_IDX2;
- 
--	ret = mt7996_mcu_set_vow_drr_ctrl(phy, mconf, msta, VOW_DRR_CTRL_STA_BSS_GROUP);
-+	ret = mt7996_mcu_set_vow_drr_ctrl(phy, mconf, mlink, VOW_DRR_CTRL_STA_BSS_GROUP);
- 	if (ret)
- 		return ret;
- 
--	ret = mt7996_mcu_set_vow_drr_ctrl(phy, mconf, msta, VOW_DRR_CTRL_STA_PAUSE);
-+	ret = mt7996_mcu_set_vow_drr_ctrl(phy, mconf, mlink, VOW_DRR_CTRL_STA_PAUSE);
- 	if (ret)
- 		return ret;
- 
--	return mt7996_mcu_set_vow_drr_ctrl(phy, mconf, msta, VOW_DRR_CTRL_STA_ALL);
-+	return mt7996_mcu_set_vow_drr_ctrl(phy, mconf, mlink, VOW_DRR_CTRL_STA_ALL);
- }
- 
- int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
--		       struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta,
--		       bool enable, bool newly)
-+		       struct mt7996_bss_conf *mconf,
-+		       struct ieee80211_link_sta *link_sta,
-+		       struct mt7996_link_sta *mlink, bool enable, bool newly)
- {
- 	struct ieee80211_vif *vif = conf->vif;
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	struct mt7996_sta *msta;
- 	struct sk_buff *skb;
- 	int ret;
- 
--	msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta;
--
- 	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
--					      &msta->wcid,
-+					      &mlink->wcid,
- 					      MT7996_STA_UPDATE_MAX_SIZE);
- 	if (IS_ERR(skb))
- 		return PTR_ERR(skb);
- 
- 	/* starec basic */
--	mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, vif, sta, enable, newly);
-+	mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, vif, link_sta, enable, newly);
- 
- 	if (!enable)
- 		goto out;
- 
- 	/* starec hdr trans */
--	mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, sta);
-+	mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, mlink);
- 	/* starec tx proc */
- 	mt7996_mcu_sta_tx_proc_tlv(skb);
- 
- 	/* tag order is in accordance with firmware dependency. */
--	if (sta) {
-+	if (link_sta) {
- 		/* starec hdrt mode */
- 		mt7996_mcu_sta_hdrt_tlv(dev, skb);
- 		/* starec bfer */
--		mt7996_mcu_sta_bfer_tlv(dev, skb, conf, mconf, sta);
-+		mt7996_mcu_sta_bfer_tlv(dev, skb, conf, mconf, link_sta);
- 		/* starec ht */
--		mt7996_mcu_sta_ht_tlv(skb, sta);
-+		mt7996_mcu_sta_ht_tlv(skb, link_sta);
- 		/* starec vht */
--		mt7996_mcu_sta_vht_tlv(skb, sta);
-+		mt7996_mcu_sta_vht_tlv(skb, link_sta);
- 		/* starec uapsd */
--		mt76_connac_mcu_sta_uapsd(skb, vif, sta);
-+		mt76_connac_mcu_sta_uapsd(skb, vif, link_sta->sta);
- 		/* starec amsdu */
--		mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, sta);
-+		mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, link_sta, mlink);
- 		/* starec he */
--		mt7996_mcu_sta_he_tlv(skb, conf, mconf, sta);
-+		mt7996_mcu_sta_he_tlv(skb, conf, mconf, link_sta);
- 		/* starec he 6g*/
--		mt7996_mcu_sta_he_6g_tlv(skb, sta);
-+		mt7996_mcu_sta_he_6g_tlv(skb, link_sta);
- 		/* starec eht */
--		mt7996_mcu_sta_eht_tlv(skb, sta);
-+		mt7996_mcu_sta_eht_tlv(skb, link_sta);
- 		/* starec muru */
--		mt7996_mcu_sta_muru_tlv(dev, skb, conf, mconf, sta);
-+		mt7996_mcu_sta_muru_tlv(dev, skb, conf, mconf, link_sta);
- 		/* starec bfee */
--		mt7996_mcu_sta_bfee_tlv(dev, skb, conf, mconf, sta);
-+		mt7996_mcu_sta_bfee_tlv(dev, skb, conf, mconf, link_sta);
- 	}
- 
--	ret = mt7996_mcu_sta_init_vow(mconf, msta);
-+	ret = mt7996_mcu_sta_init_vow(mconf, mlink);
- 	if (ret) {
- 		dev_kfree_skb(skb);
- 		return ret;
-@@ -2481,16 +2486,16 @@ int mt7996_mcu_add_key(struct mt76_dev *dev, struct mt7996_bss_conf *mconf,
- 
- static int mt7996_mcu_get_pn(struct mt7996_dev *dev,
- 			     struct ieee80211_bss_conf *conf,
--			     struct mt7996_bss_conf *mconf, u8 *pn)
-+			     struct mt7996_bss_conf *mconf,
-+			     struct mt7996_link_sta *mlink, u8 *pn)
- {
- #define TSC_TYPE_BIGTK_PN 2
--	struct mt7996_vif *mvif = (struct mt7996_vif *)conf->vif->drv_priv;
- 	struct sta_rec_pn_info *pn_info;
- 	struct sk_buff *skb, *rskb;
- 	struct tlv *tlv;
- 	int ret;
- 
--	skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76, &mvif->sta.wcid);
-+	skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76, &mlink->wcid);
- 	if (IS_ERR(skb))
- 		return PTR_ERR(skb);
- 
-@@ -2517,6 +2522,7 @@ static int mt7996_mcu_get_pn(struct mt7996_dev *dev,
- int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev,
- 			       struct ieee80211_bss_conf *conf,
- 			       struct mt7996_bss_conf *mconf,
-+			       struct mt7996_link_sta *mlink,
- 			       struct ieee80211_key_conf *key)
- {
- 	struct mt7996_mcu_bcn_prot_tlv *bcn_prot;
-@@ -2535,7 +2541,7 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev,
- 
- 	bcn_prot = (struct mt7996_mcu_bcn_prot_tlv *)tlv;
- 
--	ret = mt7996_mcu_get_pn(dev, conf, mconf, pn);
-+	ret = mt7996_mcu_get_pn(dev, conf, mconf, mlink, pn);
- 	if (ret) {
- 		dev_kfree_skb(skb);
- 		return ret;
-@@ -4811,21 +4817,18 @@ int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev, bool disable
- int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
- 				     struct ieee80211_vif *vif,
- 				     struct mt7996_bss_conf *mconf,
--				     struct ieee80211_sta *sta)
-+				     struct mt7996_link_sta *mlink)
- {
--	struct mt7996_sta *msta;
- 	struct sk_buff *skb;
- 
--	msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mconf->vif->sta;
--
- 	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
--					      &msta->wcid,
-+					      &mlink->wcid,
- 					      MT7996_STA_UPDATE_MAX_SIZE);
- 	if (IS_ERR(skb))
- 		return PTR_ERR(skb);
- 
- 	/* starec hdr trans */
--	mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, sta);
-+	mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, mlink);
- 	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
- 				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
- }
-@@ -5014,7 +5017,7 @@ int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
- 	switch (tag) {
- 	case UNI_PER_STA_RSSI:
- 		for (i = 0; i < sta_num; ++i) {
--			struct mt7996_sta *msta;
-+			struct mt7996_link_sta *mlink;
- 			struct mt76_phy *phy;
- 			s8 rssi[4];
- 			u8 *rcpi;
-@@ -5028,10 +5031,10 @@ int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
- 				rssi[2] = to_rssi(MT_PRXV_RCPI0, rcpi[2]);
- 				rssi[3] = to_rssi(MT_PRXV_RCPI0, rcpi[3]);
- 
--				msta = container_of(wcid, struct mt7996_sta, wcid);
--				phy = msta->vif->phy->mt76;
--				msta->ack_signal = mt76_rx_signal(phy->antenna_mask, rssi);
--				ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal);
-+				mlink = container_of(wcid, struct mt7996_link_sta, wcid);
-+				phy = mlink->sta->vif->deflink.phy->mt76;
-+				mlink->ack_signal = mt76_rx_signal(phy->antenna_mask, rssi);
-+				ewma_avg_signal_add(&mlink->avg_ack_signal, -mlink->ack_signal);
- 			} else {
- 				ret = -EINVAL;
- 				dev_err(dev->dev, "Failed to update RSSI for "
-@@ -5069,7 +5072,7 @@ int mt7996_mcu_get_rssi(struct mt76_dev *dev)
- {
- 	u16 sta_list[PER_STA_INFO_MAX_NUM];
- 	LIST_HEAD(sta_poll_list);
--	struct mt7996_sta *msta;
-+	struct mt7996_link_sta *mlink;
- 	int i, ret;
- 	bool empty = false;
- 
-@@ -5089,13 +5092,13 @@ int mt7996_mcu_get_rssi(struct mt76_dev *dev)
- 				empty = true;
- 				break;
- 			}
--			msta = list_first_entry(&sta_poll_list,
--			                        struct mt7996_sta,
-+			mlink = list_first_entry(&sta_poll_list,
-+			                        struct mt7996_link_sta,
- 			                        wcid.poll_list);
--			list_del_init(&msta->wcid.poll_list);
-+			list_del_init(&mlink->wcid.poll_list);
- 			spin_unlock_bh(&dev->sta_poll_lock);
- 
--			sta_list[i] = msta->wcid.idx;
-+			sta_list[i] = mlink->wcid.idx;
- 		}
- 
- 		ret = mt7996_mcu_get_per_sta_info(dev, UNI_PER_STA_RSSI,
-@@ -5385,10 +5388,18 @@ int mt7996_mcu_set_scs_stats(struct mt7996_phy *phy)
- void mt7996_sta_rssi_work(void *data, struct ieee80211_sta *sta)
- {
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_link_sta *mlink;
- 	struct mt7996_phy *poll_phy = (struct mt7996_phy *) data;
- 
--	if (poll_phy->scs_ctrl.sta_min_rssi > msta->ack_signal)
--		poll_phy->scs_ctrl.sta_min_rssi = msta->ack_signal;
-+	mutex_lock(&poll_phy->dev->mt76.mutex);
-+	mlink = mlink_dereference_protected(msta, 0);
-+	if (!mlink)
-+		goto out;
-+
-+	if (poll_phy->scs_ctrl.sta_min_rssi > mlink->ack_signal)
-+		poll_phy->scs_ctrl.sta_min_rssi = mlink->ack_signal;
-+out:
-+	mutex_unlock(&poll_phy->dev->mt76.mutex);
- }
- 
- void mt7996_mcu_scs_sta_poll(struct work_struct *work)
-@@ -5464,9 +5475,10 @@ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
- 
- int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy,
- 				struct mt7996_bss_conf *mconf,
--				struct mt7996_sta *msta, enum vow_drr_ctrl_id id)
-+				struct mt7996_link_sta *mlink,
-+				enum vow_drr_ctrl_id id)
- {
--	struct mt7996_vow_sta_ctrl *vow = msta ? &msta->vow : NULL;
-+	struct mt7996_vow_sta_ctrl *vow = mlink ? &mlink->vow : NULL;
- 	u32 val = 0;
- 	struct {
- 		u8 __rsv1[4];
-@@ -5488,11 +5500,11 @@ int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy,
- 	} __packed req = {
- 		.tag = cpu_to_le16(UNI_VOW_DRR_CTRL),
- 		.len = cpu_to_le16(sizeof(req) - 4),
--		.wlan_idx = cpu_to_le16(msta ? msta->wcid.idx : 0),
-+		.wlan_idx = cpu_to_le16(mlink ? mlink->wcid.idx : 0),
- 		.band_idx = phy->mt76->band_idx,
--		.wmm_idx = msta ? mconf->mt76.wmm_idx : 0,
-+		.wmm_idx = mlink ? mconf->mt76.wmm_idx : 0,
- 		.ctrl_id = cpu_to_le32(id),
--		.omac_idx = msta ? mconf->mt76.omac_idx : 0
-+		.omac_idx = mlink ? mconf->mt76.omac_idx : 0
- 	};
- 
- 	switch (id) {
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 6b03ee1..3701720 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -302,10 +302,10 @@ struct mt7996_vow_sta_ctrl {
- 	u8 drr_quantum[IEEE80211_NUM_ACS];
- };
- 
--struct mt7996_sta {
-+struct mt7996_link_sta {
- 	struct mt76_wcid wcid; /* must be first */
- 
--	struct mt7996_vif *vif;
-+	struct mt7996_sta *sta;
- 
- 	struct list_head rc_list;
- 
-@@ -324,6 +324,13 @@ struct mt7996_sta {
- 	struct mt7996_vow_sta_ctrl vow;
- };
- 
-+struct mt7996_sta {
-+	struct mt7996_link_sta deflink;
-+	struct mt7996_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
-+
-+	struct mt7996_vif *vif;
-+};
-+
- struct mt7996_bss_conf {
- 	struct mt76_vif mt76; /* must be first */
- 
-@@ -783,6 +790,13 @@ mconf_dereference_protected(struct mt7996_vif *mvif, u8 link_id)
- 					 lockdep_is_held(&mvif->dev->mt76.mutex));
- }
- 
-+static inline struct mt7996_link_sta *
-+mlink_dereference_protected(struct mt7996_sta *msta, u8 link_id)
-+{
-+	return rcu_dereference_protected(msta->link[link_id],
-+					 lockdep_is_held(&msta->vif->dev->mt76.mutex));
-+}
-+
- extern const struct ieee80211_ops mt7996_ops;
- extern struct pci_driver mt7996_pci_driver;
- extern struct pci_driver mt7996_hif_driver;
-@@ -827,10 +841,12 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
- 			    struct mt7996_bss_conf *mconf, bool enable);
- int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
- 			    struct ieee80211_bss_conf *conf,
--			    struct mt7996_bss_conf *mconf, int enable);
-+			    struct mt7996_bss_conf *mconf,
-+			    struct mt7996_link_sta *mlink, int enable);
- int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
--		       struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta,
--		       bool enable, bool newly);
-+		       struct mt7996_bss_conf *mconf,
-+		       struct ieee80211_link_sta *link_sta,
-+		       struct mt7996_link_sta *mlink, bool enable, bool newly);
- int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
- 			 struct ieee80211_ampdu_params *params,
- 			 bool add);
-@@ -852,7 +868,8 @@ int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy,
- int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
- 			     struct ieee80211_bss_conf *conf,
- 			     struct mt7996_bss_conf *mconf,
--			     struct ieee80211_sta *sta, bool changed);
-+			     struct ieee80211_link_sta *link_sta,
-+			     struct mt7996_link_sta *mlink, bool changed);
- int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef);
- int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag);
- int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf);
-@@ -860,7 +877,9 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
- 				   void *data, u16 version);
- int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
- 			       struct mt7996_bss_conf *mconf,
--			       struct ieee80211_sta *sta, void *data, u32 field);
-+			       struct ieee80211_link_sta *link_sta,
-+			       struct mt7996_link_sta *mlink, void *data,
-+			       u32 field);
- int mt7996_mcu_set_eeprom(struct mt7996_dev *dev);
- int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf);
- int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num);
-@@ -916,7 +935,8 @@ void mt7996_mcu_scs_sta_poll(struct work_struct *work);
- int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable);
- int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy,
- 				struct mt7996_bss_conf *mconf,
--				struct mt7996_sta *msta, enum vow_drr_ctrl_id id);
-+				struct mt7996_link_sta *mlink,
-+				enum vow_drr_ctrl_id id);
- int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy);
- void mt7996_mcu_wmm_pbc_work(struct work_struct *work);
- 
-@@ -983,7 +1003,7 @@ void mt7996_mac_reset_counters(struct mt7996_phy *phy);
- void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy);
- void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band);
- void mt7996_mac_enable_rtscts(struct mt7996_dev *dev,
--			      struct ieee80211_vif *vif, bool enable);
-+			      struct mt7996_link_sta *mlink, bool enable);
- void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
- 			   struct sk_buff *skb, struct mt76_wcid *wcid,
- 			   struct ieee80211_key_conf *key, int pid,
-@@ -1001,8 +1021,7 @@ void mt7996_mac_dump_work(struct work_struct *work);
- void mt7996_mac_sta_rc_work(struct work_struct *work);
- void mt7996_mac_update_stats(struct mt7996_phy *phy);
- void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
--				  struct mt7996_sta *msta,
--				  u8 flowid);
-+				  struct mt7996_link_sta *mlink, u8 flowid);
- void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
- 			      struct ieee80211_sta *sta,
- 			      struct ieee80211_twt_setup *twt);
-@@ -1031,11 +1050,12 @@ int mt7996_mcu_add_key(struct mt76_dev *dev, struct mt7996_bss_conf *mconf,
- int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev,
- 			       struct ieee80211_bss_conf *conf,
- 			       struct mt7996_bss_conf *mconf,
-+			       struct mt7996_link_sta *mlink,
- 			       struct ieee80211_key_conf *key);
- int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
- 				     struct ieee80211_vif *vif,
- 				     struct mt7996_bss_conf *mconf,
--				     struct ieee80211_sta *sta);
-+				     struct mt7996_link_sta *mlink);
- int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode);
- int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap);
- #ifdef CONFIG_MAC80211_DEBUGFS
-diff --git a/mt7996/testmode.c b/mt7996/testmode.c
-index bf55b43..ba17f94 100644
---- a/mt7996/testmode.c
-+++ b/mt7996/testmode.c
-@@ -235,8 +235,8 @@ mt7996_tm_init(struct mt7996_phy *phy, bool en)
- 
- 	mt7996_tm_rf_switch_mode(dev, rf_test_mode);
- 
--	mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, en);
--	mt7996_mcu_add_sta(dev, &phy->monitor_vif->bss_conf, &mvif->deflink, NULL, en, false);
-+	mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, &mvif->sta.deflink, en);
-+	mt7996_mcu_add_sta(dev, &phy->monitor_vif->bss_conf, &mvif->deflink, NULL, &mvif->sta.deflink, en, false);
- 
- 	mt7996_tm_set(dev, SET_ID(BAND_IDX), phy->mt76->band_idx);
- 
-@@ -1186,7 +1186,7 @@ mt7996_tm_txbf_init(struct mt7996_phy *phy, u16 *val)
- 	phy->omac_mask |= BIT_ULL(mvif->deflink.mt76.omac_idx);
- 
- 	mt7996_mcu_add_dev_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, true);
--	mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, true);
-+	mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, &mvif->sta.deflink, true);
- 
- 	if (td->ibf) {
- 		if (td->is_txbf_dut) {
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0087-mtk-mt76-mt7996-implement-and-switch-to-hw-scan-call.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0087-mtk-mt76-mt7996-implement-and-switch-to-hw-scan-call.patch
new file mode 100644
index 0000000..452ba1f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0087-mtk-mt76-mt7996-implement-and-switch-to-hw-scan-call.patch
@@ -0,0 +1,451 @@
+From c2213afbe5f0c0b36ba7fb19c2bd9f04cea01cc9 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 3 Nov 2023 21:44:45 +0800
+Subject: [PATCH 087/199] mtk: mt76: mt7996: implement and switch to hw scan
+ callbacks
+
+To support MLO, hw_scan callbacks are mandatory. However, the
+firmware of AP-segment doesn't support hw_scan commands, so we need
+to implement it in the driver.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+If the cfg80211_scan_request contains an unicast BSSID, the probe
+request should be unicast.
+This works for ML probe request, which should be unicast.
+
+Co-developed-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Co-developed-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mac80211.c      |   3 +-
+ mt76.h          |   2 +
+ mt7996/init.c   |   5 ++
+ mt7996/mac.c    | 141 ++++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/main.c   |  95 ++++++++++++++++++++++++++++----
+ mt7996/mcu.c    |   3 +-
+ mt7996/mt7996.h |  11 +++-
+ 7 files changed, 248 insertions(+), 12 deletions(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index 3e054d9d..032a13a5 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -820,7 +820,7 @@ bool mt76_has_tx_pending(struct mt76_phy *phy)
+ }
+ EXPORT_SYMBOL_GPL(mt76_has_tx_pending);
+ 
+-static struct mt76_channel_state *
++struct mt76_channel_state *
+ mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c)
+ {
+ 	struct mt76_sband *msband;
+@@ -836,6 +836,7 @@ mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c)
+ 	idx = c - &msband->sband.channels[0];
+ 	return &msband->chan[idx];
+ }
++EXPORT_SYMBOL_GPL(mt76_channel_state);
+ 
+ void mt76_update_survey_active_time(struct mt76_phy *phy, ktime_t time)
+ {
+diff --git a/mt76.h b/mt76.h
+index 8f3541e2..6a7752ef 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -1469,6 +1469,8 @@ void mt76_release_buffered_frames(struct ieee80211_hw *hw,
+ 				  enum ieee80211_frame_release_type reason,
+ 				  bool more_data);
+ bool mt76_has_tx_pending(struct mt76_phy *phy);
++struct mt76_channel_state *
++mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c);
+ void mt76_set_channel(struct mt76_phy *phy);
+ void mt76_update_survey(struct mt76_phy *phy);
+ void mt76_update_survey_active_time(struct mt76_phy *phy, ktime_t time);
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 1e4e6e78..2b396d94 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -459,6 +459,9 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ 
+ 	wiphy->available_antennas_rx = phy->mt76->antenna_mask;
+ 	wiphy->available_antennas_tx = phy->mt76->antenna_mask;
++
++	wiphy->max_scan_ssids = 4;
++	wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+ }
+ 
+ static void
+@@ -679,6 +682,7 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 	mphy->dev->phys[band] = mphy;
+ 
+ 	INIT_DELAYED_WORK(&mphy->mac_work, mt7996_mac_work);
++	INIT_DELAYED_WORK(&phy->scan_work, mt7996_scan_work);
+ 
+ 	ret = mt7996_eeprom_parse_hw_cap(dev, phy);
+ 	if (ret)
+@@ -1574,6 +1578,7 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ 	dev->mt76.phy.priv = &dev->phy;
+ 	INIT_WORK(&dev->rc_work, mt7996_mac_sta_rc_work);
+ 	INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7996_mac_work);
++	INIT_DELAYED_WORK(&dev->phy.scan_work, mt7996_scan_work);
+ 	INIT_DELAYED_WORK(&dev->scs_work, mt7996_mcu_scs_sta_poll);
+ 	INIT_LIST_HEAD(&dev->sta_rc_list);
+ 	INIT_LIST_HEAD(&dev->twt_list);
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 564f080d..7578cbb0 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2693,3 +2693,144 @@ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
+ 	dev->twt.table_mask &= ~BIT(flow->table_id);
+ 	dev->twt.n_agrt--;
+ }
++
++static void
++mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
++		       const u8 *dst)
++{
++	struct ieee80211_hw *hw = phy->mt76->hw;
++	struct cfg80211_scan_request *req = phy->scan_req;
++	struct ieee80211_vif *vif = phy->scan_vif;
++	struct mt7996_vif *mvif;
++	struct mt76_wcid *wcid;
++	struct ieee80211_tx_info *info;
++	struct sk_buff *skb;
++
++	if (!req || !vif)
++		return;
++
++	mvif = (struct mt7996_vif *)vif->drv_priv;
++	wcid = &mvif->sta.wcid;
++
++	skb = ieee80211_probereq_get(hw, vif->addr,
++				     ssid->ssid, ssid->ssid_len, req->ie_len);
++	if (!skb)
++		return;
++
++	if (is_unicast_ether_addr(dst)) {
++		struct ieee80211_hdr_3addr *hdr =
++			(struct ieee80211_hdr_3addr *)skb->data;
++		memcpy(hdr->addr1, dst, ETH_ALEN);
++		memcpy(hdr->addr3, dst, ETH_ALEN);
++	}
++
++	info = IEEE80211_SKB_CB(skb);
++	if (req->no_cck)
++		info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
++
++	if (req->ie_len)
++		skb_put_data(skb, req->ie, req->ie_len);
++
++	skb_set_queue_mapping(skb, IEEE80211_AC_VO);
++
++	rcu_read_lock();
++	if (!ieee80211_tx_prepare_skb(hw, vif, skb,
++				      phy->scan_chan->band,
++				      NULL)) {
++		rcu_read_unlock();
++		ieee80211_free_txskb(hw, skb);
++		return;
++	}
++
++	local_bh_disable();
++	mt76_tx(phy->mt76, NULL, wcid, skb);
++	local_bh_enable();
++
++	rcu_read_unlock();
++}
++
++void mt7996_scan_complete(struct mt7996_phy *phy, bool aborted)
++{
++	struct cfg80211_scan_info info = {
++		.aborted = aborted,
++	};
++
++	ieee80211_scan_completed(phy->mt76->hw, &info);
++	phy->scan_chan = NULL;
++	phy->scan_req = NULL;
++	phy->scan_vif = NULL;
++	clear_bit(MT76_SCANNING, &phy->mt76->state);
++}
++
++static void mt7996_scan_check_sta(void *data, struct ieee80211_sta *sta)
++{
++	bool *has_sta = data;
++
++	if (*has_sta)
++		return;
++	*has_sta = true;
++}
++
++void mt7996_scan_work(struct work_struct *work)
++{
++	struct mt7996_phy *phy = container_of(work, struct mt7996_phy, scan_work.work);
++	struct ieee80211_hw *hw = phy->mt76->hw;
++	struct cfg80211_scan_request *req = phy->scan_req;
++	struct cfg80211_chan_def chandef = {};
++	int duration;
++	bool has_sta = false, active_scan = false;
++
++	mutex_lock(&phy->dev->mt76.mutex);
++	if (phy->scan_chan_idx >= req->n_channels) {
++		mt7996_scan_complete(phy, false);
++		mutex_unlock(&phy->dev->mt76.mutex);
++
++		mt7996_set_channel(phy, &hw->conf.chandef);
++
++		return;
++	}
++
++	ieee80211_iterate_stations_atomic(hw, mt7996_scan_check_sta, &has_sta);
++
++	/* go back to operating channel */
++	if (has_sta && phy->scan_chan) {
++		phy->scan_chan = NULL;
++		mutex_unlock(&phy->dev->mt76.mutex);
++
++		mt7996_set_channel(phy, &phy->mt76->chandef);
++
++		ieee80211_queue_delayed_work(hw, &phy->scan_work, HZ / 10);
++
++		return;
++	}
++
++	wiphy_info(hw->wiphy, "hw scan %d MHz\n",
++		   req->channels[phy->scan_chan_idx]->center_freq);
++
++	phy->scan_chan = req->channels[phy->scan_chan_idx++];
++
++	if (!req->n_ssids ||
++	    (phy->scan_chan->flags & (IEEE80211_CHAN_NO_IR |
++				      IEEE80211_CHAN_RADAR))) {
++		duration = HZ / 9; /* ~110 ms */
++	} else {
++		duration = HZ / 16; /* ~60 ms */
++		active_scan = true;
++	}
++
++	cfg80211_chandef_create(&chandef, phy->scan_chan, NL80211_CHAN_HT20);
++	mutex_unlock(&phy->dev->mt76.mutex);
++
++	mt7996_set_channel(phy, &chandef);
++
++	if (active_scan) {
++		int i;
++
++		mutex_lock(&phy->dev->mt76.mutex);
++		for (i = 0; i < req->n_ssids; i++)
++			mt7996_scan_send_probe(phy, &req->ssids[i], req->bssid);
++		mutex_unlock(&phy->dev->mt76.mutex);
++	}
++
++	ieee80211_queue_delayed_work(hw, &phy->scan_work, duration);
++}
+diff --git a/mt7996/main.c b/mt7996/main.c
+index a37aac43..c640fb61 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -312,6 +312,8 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	int idx = msta->wcid.idx;
+ 
++	cancel_delayed_work_sync(&phy->scan_work);
++
+ 	mt7996_mcu_add_sta(dev, vif, NULL, false, false);
+ 	mt7996_mcu_add_bss_info(phy, vif, false);
+ 
+@@ -323,6 +325,10 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ 	rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
+ 
+ 	mutex_lock(&dev->mt76.mutex);
++
++	if (test_bit(MT76_SCANNING, &phy->mt76->state))
++		mt7996_scan_complete(phy, true);
++
+ 	dev->mt76.vif_mask &= ~BIT_ULL(mvif->mt76.idx);
+ 	phy->omac_mask &= ~BIT_ULL(mvif->mt76.omac_idx);
+ 	mutex_unlock(&dev->mt76.mutex);
+@@ -335,7 +341,33 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ 	mt76_wcid_cleanup(&dev->mt76, &msta->wcid);
+ }
+ 
+-int mt7996_set_channel(struct mt7996_phy *phy)
++static void ___mt7996_set_channel(struct mt7996_phy *phy,
++				 struct cfg80211_chan_def *chandef)
++{
++	struct mt76_dev *mdev = phy->mt76->dev;
++	struct mt76_phy *mphy = phy->mt76;
++	bool offchannel = phy->scan_chan != NULL;
++	int timeout = HZ / 5;
++
++	wait_event_timeout(mdev->tx_wait, !mt76_has_tx_pending(mphy), timeout);
++	mt76_update_survey(mphy);
++
++	if (mphy->chandef.chan->center_freq != chandef->chan->center_freq ||
++	    mphy->chandef.width != chandef->width)
++		mphy->dfs_state = MT_DFS_STATE_UNKNOWN;
++
++	mphy->chandef = *chandef;
++	mphy->chan_state = mt76_channel_state(mphy, chandef->chan);
++
++	if (!offchannel)
++		mphy->main_chan = chandef->chan;
++
++	if (chandef->chan != mphy->main_chan)
++		memset(mphy->chan_state, 0, sizeof(*mphy->chan_state));
++}
++
++static int __mt7996_set_channel(struct mt7996_phy *phy,
++				struct cfg80211_chan_def *chandef)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+ 	int ret;
+@@ -345,7 +377,7 @@ int mt7996_set_channel(struct mt7996_phy *phy)
+ 	mutex_lock(&dev->mt76.mutex);
+ 	set_bit(MT76_RESET, &phy->mt76->state);
+ 
+-	mt76_set_channel(phy->mt76);
++	___mt7996_set_channel(phy, chandef);
+ 
+ 	if (dev->cal) {
+ 		ret = mt7996_mcu_apply_tx_dpd(phy);
+@@ -385,6 +417,19 @@ out:
+ 	return ret;
+ }
+ 
++int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef)
++{
++	int ret;
++
++	ieee80211_stop_queues(phy->mt76->hw);
++	ret = __mt7996_set_channel(phy, chandef);
++	if (ret)
++		return ret;
++	ieee80211_wake_queues(phy->mt76->hw);
++
++	return 0;
++}
++
+ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ 			  struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ 			  struct ieee80211_key_conf *key)
+@@ -481,11 +526,7 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
+ 		if (ret)
+ 			return ret;
+ 
+-		ieee80211_stop_queues(hw);
+-		ret = mt7996_set_channel(phy);
+-		if (ret)
+-			return ret;
+-		ieee80211_wake_queues(hw);
++		mt7996_set_channel(phy, &hw->conf.chandef);
+ 	}
+ 
+ 	if (changed & (IEEE80211_CONF_CHANGE_POWER |
+@@ -1652,6 +1693,42 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ 
+ #endif
+ 
++static int
++mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++	       struct ieee80211_scan_request *hw_req)
++{
++	struct cfg80211_scan_request *req = &hw_req->req;
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++
++	mutex_lock(&phy->dev->mt76.mutex);
++	if (WARN_ON(phy->scan_req || phy->scan_chan)) {
++		mutex_unlock(&phy->dev->mt76.mutex);
++		return -EBUSY;
++	}
++
++	set_bit(MT76_SCANNING, &phy->mt76->state);
++	phy->scan_req = req;
++	phy->scan_vif = vif;
++	phy->scan_chan_idx = 0;
++	mutex_unlock(&phy->dev->mt76.mutex);
++
++	ieee80211_queue_delayed_work(hw, &phy->scan_work, 0);
++
++	return 0;
++}
++
++static void
++mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
++{
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++
++	cancel_delayed_work_sync(&phy->scan_work);
++
++	mutex_lock(&phy->dev->mt76.mutex);
++	mt7996_scan_complete(phy, true);
++	mutex_unlock(&phy->dev->mt76.mutex);
++}
++
+ const struct ieee80211_ops mt7996_ops = {
+ 	.add_chanctx = ieee80211_emulate_add_chanctx,
+ 	.remove_chanctx = ieee80211_emulate_remove_chanctx,
+@@ -1674,8 +1751,8 @@ const struct ieee80211_ops mt7996_ops = {
+ 	.ampdu_action = mt7996_ampdu_action,
+ 	.set_rts_threshold = mt7996_set_rts_threshold,
+ 	.wake_tx_queue = mt76_wake_tx_queue,
+-	.sw_scan_start = mt76_sw_scan,
+-	.sw_scan_complete = mt76_sw_scan_complete,
++	.hw_scan = mt7996_hw_scan,
++	.cancel_hw_scan = mt7996_cancel_hw_scan,
+ 	.release_buffered_frames = mt76_release_buffered_frames,
+ 	.get_txpower = mt76_get_txpower,
+ 	.channel_switch_beacon = mt7996_channel_switch_beacon,
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 8cc907f9..214c157c 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3778,7 +3778,8 @@ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag)
+ 	if (phy->mt76->hw->conf.flags & IEEE80211_CONF_MONITOR)
+ 		req.switch_reason = CH_SWITCH_NORMAL;
+ 	else if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL ||
+-		 phy->mt76->hw->conf.flags & IEEE80211_CONF_IDLE)
++		 phy->mt76->hw->conf.flags & IEEE80211_CONF_IDLE ||
++		 phy->scan_chan)
+ 		req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD;
+ 	else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef,
+ 					  NL80211_IFTYPE_AP))
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index d5162885..f94cb496 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -459,6 +459,13 @@ struct mt7996_phy {
+ 	u8 pp_mode;
+ 	u16 punct_bitmap;
+ 
++	/* for hw_scan */
++	struct delayed_work scan_work;
++	struct ieee80211_channel *scan_chan;
++	struct cfg80211_scan_request *scan_req;
++	struct ieee80211_vif *scan_vif;
++	int scan_chan_idx;
++
+ 	struct mt7996_scs_ctrl scs_ctrl;
+ 	u32 red_drop;
+ 
+@@ -807,7 +814,7 @@ int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ 			    struct ieee80211_he_obss_pd *he_obss_pd);
+ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 			     struct ieee80211_sta *sta, bool changed);
+-int mt7996_set_channel(struct mt7996_phy *phy);
++int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef);
+ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag);
+ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif);
+ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
+@@ -965,6 +972,8 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+ 			 struct sk_buff *skb, u32 *info);
+ bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len);
+ void mt7996_stats_work(struct work_struct *work);
++void mt7996_scan_work(struct work_struct *work);
++void mt7996_scan_complete(struct mt7996_phy *phy, bool aborted);
+ int mt76_dfs_start_rdd(struct mt7996_dev *dev, bool force);
+ int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy);
+ void mt7996_set_stream_he_eht_caps(struct mt7996_phy *phy);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0087-wifi-mt76-extend-wcid-and-sta-flow-for-MLO-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0087-wifi-mt76-extend-wcid-and-sta-flow-for-MLO-support.patch
deleted file mode 100644
index c1cf6ec..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0087-wifi-mt76-extend-wcid-and-sta-flow-for-MLO-support.patch
+++ /dev/null
@@ -1,91 +0,0 @@
-From e0f1ba7574dc2a68c51a3a1d9f926dbebfbefc55 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Wed, 29 Nov 2023 11:04:50 +0800
-Subject: [PATCH 087/116] wifi: mt76: extend wcid and sta flow for MLO support
-
-Add link related info to wcid, and split sta connection flow of common
-parts for MLO supported chipsets.
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mac80211.c | 12 +++++++++++-
- mt76.h     |  7 ++++++-
- 2 files changed, 17 insertions(+), 2 deletions(-)
-
-diff --git a/mac80211.c b/mac80211.c
-index 0ad3f25..4e96b39 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -1067,6 +1067,10 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
- 		     sizeof(mstat.chain_signal));
- 	memcpy(status->chain_signal, mstat.chain_signal,
- 	       sizeof(mstat.chain_signal));
-+	if (mstat.wcid) {
-+		status->link_valid = mstat.wcid->link_valid;
-+		status->link_id = mstat.wcid->link_id;
-+	}
- 
- 	*sta = wcid_to_sta(mstat.wcid);
- 	*hw = mt76_phy_hw(dev, mstat.phy_idx);
-@@ -1375,6 +1379,9 @@ mt76_sta_add(struct mt76_phy *phy, struct ieee80211_vif *vif,
- 	if (ret)
- 		goto out;
- 
-+	if (phy->hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO)
-+		goto out;
-+
- 	for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
- 		struct mt76_txq *mtxq;
- 
-@@ -1404,12 +1411,15 @@ void __mt76_sta_remove(struct mt76_dev *dev, struct ieee80211_vif *vif,
- 	struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv;
- 	int i, idx = wcid->idx;
- 
--	for (i = 0; i < ARRAY_SIZE(wcid->aggr); i++)
-+	for (i = 0; !sta->valid_links && i < ARRAY_SIZE(wcid->aggr); i++)
- 		mt76_rx_aggr_stop(dev, wcid, i);
- 
- 	if (dev->drv->sta_remove)
- 		dev->drv->sta_remove(dev, vif, sta);
- 
-+	if (sta->valid_links)
-+		return;
-+
- 	mt76_wcid_cleanup(dev, wcid);
- 
- 	mt76_wcid_mask_clear(dev->wcid_mask, idx);
-diff --git a/mt76.h b/mt76.h
-index 8ca7907..c3ab96a 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -381,6 +381,9 @@ struct mt76_wcid {
- 	u8 sta:1;
- 	u8 amsdu:1;
- 	u8 phy_idx:2;
-+	u8 link_id:4;
-+	bool link_valid;
-+	struct mt76_wcid *def_wcid;
- 
- 	u8 rx_check_pn;
- 	u8 rx_key_pn[IEEE80211_NUM_TIDS + 1][6];
-@@ -1366,11 +1369,13 @@ mtxq_to_txq(struct mt76_txq *mtxq)
- static inline struct ieee80211_sta *
- wcid_to_sta(struct mt76_wcid *wcid)
- {
--	void *ptr = wcid;
-+	void *ptr;
- 
- 	if (!wcid || !wcid->sta)
- 		return NULL;
- 
-+	ptr = wcid->def_wcid ?: wcid;
-+
- 	return container_of(ptr, struct ieee80211_sta, drv_priv);
- }
- 
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0088-mtk-mt76-mt7996-implement-and-switch-to-chanctx-call.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0088-mtk-mt76-mt7996-implement-and-switch-to-chanctx-call.patch
new file mode 100644
index 0000000..be4a016
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0088-mtk-mt76-mt7996-implement-and-switch-to-chanctx-call.patch
@@ -0,0 +1,383 @@
+From f51442617a3575a1af73981d95316811e8488e3f Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 6 Nov 2023 16:17:11 +0800
+Subject: [PATCH 088/199] mtk: mt76: mt7996: implement and switch to chanctx
+ callbacks
+
+To support MLO, chanctx callbacks are mandatory, since one VIF (MLD) could
+operate on multiple channels (links).
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/init.c   |  22 ++++++
+ mt7996/mac.c    |  10 ++-
+ mt7996/main.c   | 178 ++++++++++++++++++++++++++++++++++++++++++++----
+ mt7996/mcu.c    |   2 +-
+ mt7996/mt7996.h |  17 +++++
+ 5 files changed, 211 insertions(+), 18 deletions(-)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 2b396d94..d18db618 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -384,6 +384,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ 
+ 	hw->sta_data_size = sizeof(struct mt7996_sta);
+ 	hw->vif_data_size = sizeof(struct mt7996_vif);
++	hw->chanctx_data_size = sizeof(struct mt7996_chanctx);
+ 
+ 	wiphy->iface_combinations = if_comb;
+ 	wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
+@@ -419,6 +420,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ 	ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD);
+ 	ieee80211_hw_set(hw, WANT_MONITOR_VIF);
+ 	ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
++	ieee80211_hw_set(hw, CHANCTX_STA_CSA);
+ 
+ 	hw->max_tx_fragments = 4;
+ 
+@@ -639,6 +641,22 @@ static int mt7996_vow_init(struct mt7996_phy *phy)
+ 	return mt7996_mcu_set_vow_feature_ctrl(phy);
+ }
+ 
++static void mt7996_init_chanctx(struct mt7996_phy *phy)
++{
++	struct ieee80211_supported_band *sband;
++	struct ieee80211_channel *chan;
++
++	if (phy->mt76->band_idx == MT_BAND2)
++		sband = &phy->mt76->sband_6g.sband;
++	else if (phy->mt76->band_idx == MT_BAND1)
++		sband = &phy->mt76->sband_5g.sband;
++	else
++		sband = &phy->mt76->sband_2g.sband;
++
++	chan = &sband->channels[0];
++	cfg80211_chandef_create(&phy->mt76->chandef, chan, NL80211_CHAN_HT20);
++}
++
+ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 			       enum mt76_band_id band)
+ {
+@@ -722,6 +740,8 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 	if (ret)
+ 		goto error;
+ 
++	mt7996_init_chanctx(phy);
++
+ 	ret = mt7996_thermal_init(phy);
+ 	if (ret)
+ 		goto error;
+@@ -1609,6 +1629,8 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ 	if (ret)
+ 		return ret;
+ 
++	mt7996_init_chanctx(&dev->phy);
++
+ 	ret = mt7996_thermal_init(&dev->phy);
+ 	if (ret)
+ 		return ret;
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 7578cbb0..fc6e383b 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2785,7 +2785,10 @@ void mt7996_scan_work(struct work_struct *work)
+ 		mt7996_scan_complete(phy, false);
+ 		mutex_unlock(&phy->dev->mt76.mutex);
+ 
+-		mt7996_set_channel(phy, &hw->conf.chandef);
++		if (phy->chanctx)
++			mt7996_set_channel(phy, &phy->chanctx->chandef);
++		else
++			mt7996_set_channel(phy, &phy->mt76->chandef);
+ 
+ 		return;
+ 	}
+@@ -2797,7 +2800,10 @@ void mt7996_scan_work(struct work_struct *work)
+ 		phy->scan_chan = NULL;
+ 		mutex_unlock(&phy->dev->mt76.mutex);
+ 
+-		mt7996_set_channel(phy, &phy->mt76->chandef);
++		if (phy->chanctx)
++			mt7996_set_channel(phy, &phy->chanctx->chandef);
++		else
++			mt7996_set_channel(phy, &phy->mt76->chandef);
+ 
+ 		ieee80211_queue_delayed_work(hw, &phy->scan_work, HZ / 10);
+ 
+diff --git a/mt7996/main.c b/mt7996/main.c
+index c640fb61..f7910abe 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -76,6 +76,11 @@ int mt7996_run(struct ieee80211_hw *hw)
+ 	if (ret)
+ 		goto out;
+ 
++	/* set a parking channel */
++	ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
++	if (ret)
++		goto out;
++
+ 	ret = mt7996_mcu_set_thermal_throttling(phy, MT7996_THERMAL_THROTTLE_MAX);
+ 	if (ret)
+ 		goto out;
+@@ -514,21 +519,6 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	int ret;
+ 
+-	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+-		if (!mt76_testmode_enabled(phy->mt76) && !phy->mt76->test.bf_en) {
+-			ret = mt7996_mcu_edcca_enable(phy, true);
+-			if (ret)
+-				return ret;
+-		}
+-
+-		ret = mt7996_mcu_set_pp_en(phy, PP_USR_MODE,
+-					   phy->mt76->chandef.punctured);
+-		if (ret)
+-			return ret;
+-
+-		mt7996_set_channel(phy, &hw->conf.chandef);
+-	}
+-
+ 	if (changed & (IEEE80211_CONF_CHANGE_POWER |
+ 		       IEEE80211_CONF_CHANGE_CHANNEL)) {
+ 		ret = mt7996_mcu_set_txpower_sku(phy);
+@@ -1729,6 +1719,158 @@ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ 	mutex_unlock(&phy->dev->mt76.mutex);
+ }
+ 
++static int
++mt7996_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
++{
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
++	int ret;
++
++	wiphy_info(hw->wiphy, "%s: add %u\n", __func__, conf->def.chan->hw_value);
++	mutex_lock(&phy->dev->mt76.mutex);
++
++	if (ctx->assigned) {
++		mutex_unlock(&phy->dev->mt76.mutex);
++		return -ENOSPC;
++	}
++
++	ctx->assigned = true;
++	ctx->chandef = conf->def;
++	ctx->phy = phy;
++	if (phy->chanctx) {
++		mutex_unlock(&phy->dev->mt76.mutex);
++		return 0;
++	}
++
++	phy->chanctx = ctx;
++	mutex_unlock(&phy->dev->mt76.mutex);
++
++	if (!mt76_testmode_enabled(phy->mt76) && !phy->mt76->test.bf_en) {
++		ret = mt7996_mcu_edcca_enable(phy, true);
++		if (ret)
++			return ret;
++	}
++
++	ret = mt7996_mcu_set_pp_en(phy, PP_USR_MODE, ctx->chandef.punctured);
++	if (ret)
++		return ret;
++
++	return mt7996_set_channel(phy, &ctx->chandef);
++}
++
++static void
++mt7996_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
++{
++	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
++	struct mt7996_phy *phy = ctx->phy;
++
++	wiphy_info(hw->wiphy, "%s: remove %u\n", __func__, conf->def.chan->hw_value);
++	cancel_delayed_work_sync(&phy->scan_work);
++	cancel_delayed_work_sync(&phy->mt76->mac_work);
++
++	mutex_lock(&phy->dev->mt76.mutex);
++	ctx->assigned = false;
++	if (ctx == phy->chanctx)
++		phy->chanctx = NULL;
++	mutex_unlock(&phy->dev->mt76.mutex);
++}
++
++static void
++mt7996_change_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf,
++		      u32 changed)
++{
++	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
++	struct mt7996_phy *phy = ctx->phy;
++
++	wiphy_info(hw->wiphy, "%s: change %u, 0x%x\n", __func__, conf->def.chan->hw_value, changed);
++	if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH) {
++		ctx->chandef = conf->def;
++
++		mt7996_set_channel(phy, &ctx->chandef);
++	}
++}
++
++static int
++mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++			  struct ieee80211_bss_conf *link_conf,
++			  struct ieee80211_chanctx_conf *conf)
++{
++	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
++	struct mt7996_phy *phy = ctx->phy;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++
++	wiphy_info(hw->wiphy, "Assign VIF (addr: %pM, type: %d, link_id: %d) to channel context: %d MHz\n",
++		    vif->addr, vif->type, link_conf->link_id,
++		    conf->def.chan->center_freq);
++
++	mutex_lock(&phy->dev->mt76.mutex);
++
++	mvif->chanctx = ctx;
++	ctx->nbss_assigned++;
++
++	mutex_unlock(&phy->dev->mt76.mutex);
++
++	return 0;
++}
++
++static void
++mt7996_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++			    struct ieee80211_bss_conf *link_conf,
++			    struct ieee80211_chanctx_conf *conf)
++{
++	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
++	struct mt7996_phy *phy = ctx->phy;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++
++	wiphy_info(hw->wiphy, "Remove VIF (addr: %pM, type: %d, link_id: %d) from channel context: %d MHz\n",
++		   vif->addr, vif->type, link_conf->link_id,
++		   conf->def.chan->center_freq);
++	cancel_delayed_work_sync(&phy->scan_work);
++
++	mutex_lock(&phy->dev->mt76.mutex);
++
++	if (test_bit(MT76_SCANNING, &phy->mt76->state))
++		mt7996_scan_complete(phy, true);
++
++	mvif->chanctx = NULL;
++	ctx->nbss_assigned--;
++
++	mutex_unlock(&phy->dev->mt76.mutex);
++}
++
++static int
++mt7996_switch_vif_chanctx(struct ieee80211_hw *hw,
++			  struct ieee80211_vif_chanctx_switch *vifs,
++			  int n_vifs,
++			  enum ieee80211_chanctx_switch_mode mode)
++{
++	struct mt7996_chanctx *old_ctx = mt7996_chanctx_get(vifs->old_ctx);
++	struct mt7996_chanctx *new_ctx = mt7996_chanctx_get(vifs->new_ctx);
++	struct mt7996_phy *phy = old_ctx->phy;
++
++	wiphy_info(hw->wiphy, "%s: old=%d, new=%d\n", __func__, vifs->old_ctx->def.chan->hw_value, vifs->new_ctx->def.chan->hw_value);
++
++	if (new_ctx->nbss_assigned && phy->chanctx == new_ctx) {
++		new_ctx->nbss_assigned += n_vifs;
++		return 0;
++	}
++
++	if (WARN_ON(old_ctx != phy->chanctx))
++		return -EINVAL;
++
++	mutex_lock(&phy->dev->mt76.mutex);
++
++	phy->chanctx = new_ctx;
++	new_ctx->assigned = true;
++	new_ctx->chandef = vifs->new_ctx->def;
++	new_ctx->phy = phy;
++	new_ctx->nbss_assigned += n_vifs;
++
++	mutex_unlock(&phy->dev->mt76.mutex);
++
++	return mt7996_set_channel(phy, &new_ctx->chandef);
++}
++
+ const struct ieee80211_ops mt7996_ops = {
+ 	.add_chanctx = ieee80211_emulate_add_chanctx,
+ 	.remove_chanctx = ieee80211_emulate_remove_chanctx,
+@@ -1783,4 +1925,10 @@ const struct ieee80211_ops mt7996_ops = {
+ 	.net_fill_forward_path = mt7996_net_fill_forward_path,
+ 	.net_setup_tc = mt76_wed_net_setup_tc,
+ #endif
++	.add_chanctx = mt7996_add_chanctx,
++	.remove_chanctx = mt7996_remove_chanctx,
++	.change_chanctx = mt7996_change_chanctx,
++	.assign_vif_chanctx = mt7996_assign_vif_chanctx,
++	.unassign_vif_chanctx = mt7996_unassign_vif_chanctx,
++	.switch_vif_chanctx = mt7996_switch_vif_chanctx,
+ };
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 214c157c..7faf0916 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -5296,7 +5296,7 @@ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap)
+ 		.bitmap = cpu_to_le16(bitmap),
+ 	};
+ 
+-	if (phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ ||
++	if (phy->chanctx->chandef.chan->band == NL80211_BAND_2GHZ ||
+ 	    mode > PP_USR_MODE)
+ 		return 0;
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index f94cb496..3597bbbf 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -333,6 +333,8 @@ struct mt7996_vif {
+ 
+ 	struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
+ 	struct cfg80211_bitrate_mask bitrate_mask;
++
++	struct mt7996_chanctx *chanctx;
+ };
+ 
+ /* crash-dump */
+@@ -422,6 +424,14 @@ struct mt7996_rro_ba_session {
+ 	u32 last_in_rxtime :12;
+ };
+ 
++struct mt7996_chanctx {
++	struct cfg80211_chan_def chandef;
++	struct mt7996_phy *phy;
++
++	bool assigned;
++	u8 nbss_assigned;
++};
++
+ struct mt7996_phy {
+ 	struct mt76_phy *mt76;
+ 	struct mt7996_dev *dev;
+@@ -465,6 +475,7 @@ struct mt7996_phy {
+ 	struct cfg80211_scan_request *scan_req;
+ 	struct ieee80211_vif *scan_vif;
+ 	int scan_chan_idx;
++	struct mt7996_chanctx *chanctx;
+ 
+ 	struct mt7996_scs_ctrl scs_ctrl;
+ 	u32 red_drop;
+@@ -753,6 +764,12 @@ mt7996_get_background_radar_cap(struct mt7996_dev *dev)
+ 	return 1;
+ }
+ 
++static inline struct mt7996_chanctx *
++mt7996_chanctx_get(struct ieee80211_chanctx_conf *ctx)
++{
++	return (struct mt7996_chanctx *)&ctx->drv_priv;
++}
++
+ extern const struct ieee80211_ops mt7996_ops;
+ extern struct pci_driver mt7996_pci_driver;
+ extern struct pci_driver mt7996_hif_driver;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0088-wifi-mt76-mt7996-enable-MLO-capability.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0088-wifi-mt76-mt7996-enable-MLO-capability.patch
deleted file mode 100644
index 9fbb44a..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0088-wifi-mt76-mt7996-enable-MLO-capability.patch
+++ /dev/null
@@ -1,108 +0,0 @@
-From ebe94f1d3aa73deefe0057ae93974a06b55db1ce Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 30 Nov 2023 16:31:17 +0800
-Subject: [PATCH 088/116] wifi: mt76: mt7996: enable MLO capability
-
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/eeprom.c |  6 ++++++
- mt7996/init.c   | 40 ++++++++++++++++++++++++++++++++++++++--
- 2 files changed, 44 insertions(+), 2 deletions(-)
-
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 51455d8..0393e93 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -387,6 +387,12 @@ static int mt7996_eeprom_parse_band_config(struct mt7996_phy *phy)
- 		break;
- 	}
- 
-+	/* TODO: for MLO, we enable all band capabilities */
-+	phy->mt76->cap.has_2ghz = true;
-+	phy->mt76->cap.has_5ghz = true;
-+	if (is_mt7996(&phy->dev->mt76))
-+		phy->mt76->cap.has_6ghz = true;
-+
- 	return ret;
- }
- 
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 381e129..c6eb6a5 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -34,16 +34,45 @@ static const struct ieee80211_iface_combination if_comb[] = {
- 		.limits = if_limits,
- 		.n_limits = ARRAY_SIZE(if_limits),
- 		.max_interfaces = MT7996_MAX_INTERFACES,
--		.num_different_channels = 1,
-+		.num_different_channels = 3,
- 		.beacon_int_infra_match = true,
-+		/*
- 		.radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
- 				       BIT(NL80211_CHAN_WIDTH_20) |
- 				       BIT(NL80211_CHAN_WIDTH_40) |
- 				       BIT(NL80211_CHAN_WIDTH_80) |
- 				       BIT(NL80211_CHAN_WIDTH_160),
-+		*/
- 	}
- };
- 
-+static const u8 mt7996_if_types_ext_capa[] = {
-+	[0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
-+	[7] = WLAN_EXT_CAPA8_OPMODE_NOTIF,
-+};
-+
-+static const struct wiphy_iftype_ext_capab mt7996_iftypes_ext_capa[] = {
-+	{
-+		.iftype = NL80211_IFTYPE_STATION,
-+		.extended_capabilities = mt7996_if_types_ext_capa,
-+		.extended_capabilities_mask = mt7996_if_types_ext_capa,
-+		.extended_capabilities_len = sizeof(mt7996_if_types_ext_capa),
-+		.mld_capa_and_ops = 2,
-+	},
-+	{
-+		.iftype = NL80211_IFTYPE_AP,
-+		.extended_capabilities = mt7996_if_types_ext_capa,
-+		.extended_capabilities_mask = mt7996_if_types_ext_capa,
-+		.extended_capabilities_len = sizeof(mt7996_if_types_ext_capa),
-+		.mld_capa_and_ops = 2,
-+		/* the max number of simultaneous links is defined as the
-+		 * maximum number of affiliated APs minus 1.
-+		 * mt7996 could have 3 links in an MLD AP, so currently
-+		 * hardcode it to 2.
-+		 */
-+	},
-+};
-+
- static ssize_t mt7996_thermal_temp_show(struct device *dev,
- 					struct device_attribute *attr,
- 					char *buf)
-@@ -417,8 +446,9 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
- 	ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD);
- 	ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD);
- 	ieee80211_hw_set(hw, WANT_MONITOR_VIF);
--	ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
-+	// ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
- 	ieee80211_hw_set(hw, CHANCTX_STA_CSA);
-+	ieee80211_hw_set(hw, CONNECTION_MONITOR);
- 
- 	hw->max_tx_fragments = 4;
- 
-@@ -462,6 +492,12 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
- 
- 	wiphy->max_scan_ssids = 4;
- 	wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
-+
-+	/* enable MLO support */
-+	wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO;
-+	wiphy->iftype_ext_capab = mt7996_iftypes_ext_capa;
-+	wiphy->num_iftype_ext_capab = ARRAY_SIZE(mt7996_iftypes_ext_capa);
-+	wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE;
- }
- 
- static void
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0089-mtk-mt76-mt7996-use-.sta_state-to-replace-.sta_add-a.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0089-mtk-mt76-mt7996-use-.sta_state-to-replace-.sta_add-a.patch
new file mode 100644
index 0000000..e6cbe6c
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0089-mtk-mt76-mt7996-use-.sta_state-to-replace-.sta_add-a.patch
@@ -0,0 +1,146 @@
+From 29120502dfe63ff9cde7c6445b34019fd9729cd4 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 8 Nov 2023 18:52:26 +0800
+Subject: [PATCH 089/199] mtk: mt76: mt7996: use .sta_state to replace .sta_add
+ and .sta_remove
+
+MAC80211 mostly uses MLD address through TX path, and leaves the header
+translation procedure to driver. To perform address translation, driver
+needs to get the setup link address at early stage (i.e., state 1),
+however, when using .sta_add/.sta_remove callbacks, driver can only get
+the link address at state 3, so it's necessary to switch to .sta_state
+callback to meet this requirement.
+
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/main.c   | 53 ++++++++++++++++++++-----------------------------
+ mt7996/mmio.c   |  1 +
+ mt7996/mt7996.h |  2 ++
+ 3 files changed, 24 insertions(+), 32 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index f7910abe..0580b9ff 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -788,7 +788,7 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	u8 band_idx = mvif->phy->mt76->band_idx;
+-	int ret, idx;
++	int idx;
+ 
+ #ifdef CONFIG_MTK_VENDOR
+ 	struct mt7996_phy *phy = &dev->phy;
+@@ -806,23 +806,10 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 	msta->wcid.phy_idx = band_idx;
+ 	msta->wcid.tx_info |= MT_WCID_TX_INFO_SET;
+ 
+-	ewma_avg_signal_init(&msta->avg_ack_signal);
+-
+-	mt7996_mac_wtbl_update(dev, idx,
+-			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+-
+ #ifdef CONFIG_MTK_VENDOR
+ 	mt7996_vendor_amnt_sta_remove(mvif->phy, sta);
+ #endif
+ 
+-	ret = mt7996_mcu_add_sta(dev, vif, sta, true, true);
+-	if (ret)
+-		return ret;
+-
+-	ret = mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
+-	if (ret)
+-		return ret;
+-
+ #ifdef CONFIG_MTK_VENDOR
+ 	switch (band_idx) {
+ 	case MT_BAND1:
+@@ -843,6 +830,25 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 	return 0;
+ }
+ 
++void mt7996_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
++			  struct ieee80211_sta *sta)
++{
++	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++
++	mutex_lock(&dev->mt76.mutex);
++
++	mt7996_mac_wtbl_update(dev, msta->wcid.idx,
++			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
++
++	mt7996_mcu_add_sta(dev, vif, sta, true, true);
++	mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
++
++	ewma_avg_signal_init(&msta->avg_ack_signal);
++
++	mutex_unlock(&dev->mt76.mutex);
++}
++
+ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 			   struct ieee80211_sta *sta)
+ {
+@@ -962,22 +968,6 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	return ret;
+ }
+ 
+-static int
+-mt7996_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+-	       struct ieee80211_sta *sta)
+-{
+-	return mt76_sta_state(hw, vif, sta, IEEE80211_STA_NOTEXIST,
+-			      IEEE80211_STA_NONE);
+-}
+-
+-static int
+-mt7996_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+-		  struct ieee80211_sta *sta)
+-{
+-	return mt76_sta_state(hw, vif, sta, IEEE80211_STA_NONE,
+-			      IEEE80211_STA_NOTEXIST);
+-}
+-
+ static int
+ mt7996_get_stats(struct ieee80211_hw *hw,
+ 		 struct ieee80211_low_level_stats *stats)
+@@ -1885,8 +1875,7 @@ const struct ieee80211_ops mt7996_ops = {
+ 	.conf_tx = mt7996_conf_tx,
+ 	.configure_filter = mt7996_configure_filter,
+ 	.bss_info_changed = mt7996_bss_info_changed,
+-	.sta_add = mt7996_sta_add,
+-	.sta_remove = mt7996_sta_remove,
++	.sta_state = mt76_sta_state,
+ 	.sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
+ 	.sta_rc_update = mt7996_sta_rc_update,
+ 	.set_key = mt7996_set_key,
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index d4739fea..b94155c4 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -655,6 +655,7 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
+ 		.rx_check = mt7996_rx_check,
+ 		.rx_poll_complete = mt7996_rx_poll_complete,
+ 		.sta_add = mt7996_mac_sta_add,
++		.sta_assoc = mt7996_mac_sta_assoc,
+ 		.sta_remove = mt7996_mac_sta_remove,
+ 		.update_survey = mt7996_update_channel,
+ 	};
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 3597bbbf..75073a1b 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -967,6 +967,8 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ void mt7996_mac_set_coverage_class(struct mt7996_phy *phy);
+ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 		       struct ieee80211_sta *sta);
++void mt7996_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
++			  struct ieee80211_sta *sta);
+ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 			   struct ieee80211_sta *sta);
+ void mt7996_mac_work(struct work_struct *work);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0089-wifi-mt76-mt7996-support-multi-link-vif-links-and-ML.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0089-wifi-mt76-mt7996-support-multi-link-vif-links-and-ML.patch
deleted file mode 100644
index c4d110d..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0089-wifi-mt76-mt7996-support-multi-link-vif-links-and-ML.patch
+++ /dev/null
@@ -1,590 +0,0 @@
-From 9b5284be6784cf32222425a9cbbd9622db38aafd Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 23 Nov 2023 18:22:11 +0800
-Subject: [PATCH 089/116] wifi: mt76: mt7996: support multi-link vif links and
- MLO bss callbacks
-
-Rework add/remove interface functions to add/remove bss_conf functions,
-and also switch to callbacks for MLO bss.
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/main.c   | 298 +++++++++++++++++++++++++++++++++++++++---------
- mt7996/mcu.c    |  29 +++--
- mt7996/mt7996.h |   9 ++
- 3 files changed, 270 insertions(+), 66 deletions(-)
-
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 4e5ba58..9fbb86c 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -205,6 +205,38 @@ static int get_omac_idx(enum nl80211_iftype type, u64 mask)
- 	return -1;
- }
- 
-+static int get_own_mld_idx(u64 mask, bool group_mld)
-+{
-+	u8 start, end;
-+	int i;
-+
-+	if (group_mld) {
-+		start = 0;
-+		end = 15;
-+	} else {
-+		start = 16;
-+		end = 63;
-+	}
-+
-+	i = get_free_idx(mask, start, end);
-+	if (i)
-+		return i - 1;
-+
-+	return -1;
-+}
-+
-+static int get_mld_remap_idx(u64 mask)
-+{
-+	u8 start = 0, end = 15;
-+	int i;
-+
-+	i = get_free_idx(mask, start, end);
-+	if (i)
-+		return i - 1;
-+
-+	return -1;
-+}
-+
- static void mt7996_init_bitrate_mask(struct mt7996_bss_conf *mconf)
- {
- 	int i;
-@@ -223,48 +255,108 @@ static void mt7996_init_bitrate_mask(struct mt7996_bss_conf *mconf)
- 	}
- }
- 
--static int mt7996_add_interface(struct ieee80211_hw *hw,
--				struct ieee80211_vif *vif)
-+static void mt7996_remove_bss_conf(struct ieee80211_vif *vif,
-+				   struct ieee80211_bss_conf *conf,
-+				   struct mt7996_bss_conf *mconf)
- {
--	struct ieee80211_bss_conf *conf = &vif->bss_conf;
-+	struct mt7996_phy *phy = mconf->phy;
-+	struct mt7996_dev *dev = phy->dev;
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	struct mt7996_bss_conf *mconf = &mvif->deflink;
--	struct mt7996_link_sta *mlink = &mvif->sta.deflink;
--	struct mt7996_dev *dev = mt7996_hw_dev(hw);
--	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	u8 link_id = conf->link_id;
-+	struct mt7996_link_sta *mlink =
-+		mlink_dereference_protected(&mvif->sta, link_id);
-+
-+	if (!mlink)
-+		return;
-+
-+	mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, false, false);
-+	mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, false);
-+	mt7996_mcu_add_dev_info(phy, conf, mconf, false);
-+
-+	rcu_assign_pointer(dev->mt76.wcid[mlink->wcid.idx], NULL);
-+	rcu_assign_pointer(mvif->link[link_id], NULL);
-+	rcu_assign_pointer(mvif->sta.link[link_id], NULL);
-+
-+	dev->mt76.vif_mask &= ~BIT_ULL(mconf->mt76.idx);
-+	dev->mld_id_mask &= ~BIT_ULL(mconf->own_mld_id);
-+	phy->omac_mask &= ~BIT_ULL(mconf->mt76.omac_idx);
-+
-+	spin_lock_bh(&dev->mt76.sta_poll_lock);
-+	if (!list_empty(&mlink->wcid.poll_list))
-+		list_del_init(&mlink->wcid.poll_list);
-+	spin_unlock_bh(&dev->mt76.sta_poll_lock);
-+
-+	mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
-+
-+	if (mlink != &mvif->sta.deflink)
-+		kfree(mlink);
-+
-+	if (mconf != &mvif->deflink)
-+		kfree(mconf);
-+}
-+
-+static int mt7996_add_bss_conf(struct mt7996_phy *phy,
-+			       struct ieee80211_vif *vif,
-+			       struct ieee80211_bss_conf *conf)
-+{
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_dev *dev = phy->dev;
-+	struct mt7996_bss_conf *mconf;
-+	struct mt7996_link_sta *mlink;
- 	struct mt76_txq *mtxq;
- 	u8 band_idx = phy->mt76->band_idx;
--	int idx, ret = 0;
--
--	mutex_lock(&dev->mt76.mutex);
-+	u8 link_id = conf->link_id;
-+	int idx, ret;
- 
--	if (vif->type == NL80211_IFTYPE_MONITOR &&
--	    is_zero_ether_addr(vif->addr))
--		phy->monitor_vif = vif;
-+	if (conf != &vif->bss_conf) {
-+		mconf = kzalloc(sizeof(*mconf), GFP_KERNEL);
-+		if (!mconf)
-+			return -ENOMEM;
-+	} else {
-+		mconf = &mvif->deflink;
-+	}
- 
- 	mconf->mt76.idx = __ffs64(~dev->mt76.vif_mask);
- 	if (mconf->mt76.idx >= mt7996_max_interface_num(dev)) {
- 		ret = -ENOSPC;
--		goto out;
-+		goto error;
- 	}
- 
- 	idx = get_omac_idx(vif->type, phy->omac_mask);
- 	if (idx < 0) {
- 		ret = -ENOSPC;
--		goto out;
-+		goto error;
-+	}
-+
-+	mconf->own_mld_id = get_own_mld_idx(dev->mld_id_mask, false);
-+	if (mconf->own_mld_id < 0) {
-+		ret = -ENOSPC;
-+		goto error;
- 	}
-+
- 	mconf->mt76.omac_idx = idx;
- 	mconf->vif = mvif;
- 	mconf->phy = phy;
- 	mconf->mt76.band_idx = band_idx;
- 	mconf->mt76.wmm_idx = vif->type != NL80211_IFTYPE_AP;
--	mvif->dev = dev;
-+	mconf->link_id = link_id;
- 
- 	ret = mt7996_mcu_add_dev_info(phy, conf, mconf, true);
- 	if (ret)
--		goto out;
-+		goto error;
-+
-+	if (ieee80211_vif_is_mld(vif)) {
-+		mlink = kzalloc(sizeof(*mlink), GFP_KERNEL);
-+		if (!mlink) {
-+			ret = -ENOMEM;
-+			goto error;
-+		}
-+	} else {
-+		mlink = &mvif->sta.deflink;
-+	}
- 
- 	dev->mt76.vif_mask |= BIT_ULL(mconf->mt76.idx);
-+	dev->mld_id_mask |= BIT_ULL(mconf->own_mld_id);
- 	phy->omac_mask |= BIT_ULL(mconf->mt76.omac_idx);
- 
- 	idx = MT7996_WTBL_RESERVED - mconf->mt76.idx;
-@@ -275,6 +367,9 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
- 	mlink->wcid.phy_idx = band_idx;
- 	mlink->wcid.hw_key_idx = -1;
- 	mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
-+	mlink->wcid.def_wcid = &mvif->sta.deflink.wcid;
-+	mlink->wcid.link_id = link_id;
-+	mlink->wcid.link_valid = ieee80211_vif_is_mld(vif);
- 	mlink->sta = &mvif->sta;
- 	mlink->sta->vif = mvif;
- 	mt76_wcid_init(&mlink->wcid);
-@@ -296,7 +391,6 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
- 		mconf->mt76.basic_rates_idx = MT7996_BASIC_RATES_TBL + 4;
- 	else
- 		mconf->mt76.basic_rates_idx = MT7996_BASIC_RATES_TBL;
--
- 	mt7996_init_bitrate_mask(mconf);
- 
- 	mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, true);
-@@ -306,10 +400,32 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
- 	if (vif->type != NL80211_IFTYPE_STATION)
- 		mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, true, true);
- 	rcu_assign_pointer(dev->mt76.wcid[idx], &mlink->wcid);
--	rcu_assign_pointer(mvif->link[0], mconf);
--	rcu_assign_pointer(mvif->sta.link[0], mlink);
-+	rcu_assign_pointer(mvif->link[link_id], mconf);
-+	rcu_assign_pointer(mvif->sta.link[link_id], mlink);
- 
--out:
-+	return 0;
-+error:
-+	mt7996_remove_bss_conf(vif, conf, mconf);
-+	return ret;
-+}
-+
-+static int mt7996_add_interface(struct ieee80211_hw *hw,
-+			        struct ieee80211_vif *vif)
-+{
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	int ret = 0;
-+
-+	mutex_lock(&dev->mt76.mutex);
-+	if (vif->type == NL80211_IFTYPE_MONITOR &&
-+	    is_zero_ether_addr(vif->addr))
-+		phy->monitor_vif = vif;
-+
-+	mvif->dev = dev;
-+	mvif->sta.vif = mvif;
-+
-+	ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
- 	mutex_unlock(&dev->mt76.mutex);
- 
- 	return ret;
-@@ -321,38 +437,23 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
- 	struct ieee80211_bss_conf *conf;
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_bss_conf *mconf;
--	struct mt7996_link_sta *mlink = &mvif->sta.deflink;
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
--	int idx = mlink->wcid.idx;
- 
- 	cancel_delayed_work_sync(&phy->scan_work);
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
--	conf = link_conf_dereference_protected(vif, 0);
--	mconf = mconf_dereference_protected(mvif, 0);
--	mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, false, false);
--	mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, false);
-+	if (test_bit(MT76_SCANNING, &phy->mt76->state))
-+		mt7996_scan_complete(phy, true);
- 
- 	if (vif == phy->monitor_vif)
- 		phy->monitor_vif = NULL;
- 
--	mt7996_mcu_add_dev_info(phy, conf, mconf, false);
--
--	rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
--
--	dev->mt76.vif_mask &= ~BIT_ULL(mconf->mt76.idx);
--	phy->omac_mask &= ~BIT_ULL(mconf->mt76.omac_idx);
--
--	spin_lock_bh(&dev->mt76.sta_poll_lock);
--	if (!list_empty(&mlink->wcid.poll_list))
--		list_del_init(&mlink->wcid.poll_list);
--	spin_unlock_bh(&dev->mt76.sta_poll_lock);
-+	conf = link_conf_dereference_protected(vif, 0);
-+	mconf = mconf_dereference_protected(mvif, 0);
- 
--	mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
--	rcu_assign_pointer(mvif->link[0], NULL);
--	rcu_assign_pointer(mvif->sta.link[0], NULL);
-+	mt7996_remove_bss_conf(vif, conf, mconf);
- 
- 	mutex_unlock(&dev->mt76.mutex);
- }
-@@ -716,10 +817,31 @@ mt7996_update_mu_group(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
- 	mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_POS3(band), mu[3]);
- }
- 
--static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
--				    struct ieee80211_vif *vif,
--				    struct ieee80211_bss_conf *info,
--				    u64 changed)
-+static void mt7996_vif_cfg_changed(struct ieee80211_hw *hw,
-+				   struct ieee80211_vif *vif, u64 changed)
-+{
-+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+
-+	mutex_lock(&dev->mt76.mutex);
-+
-+	if (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) {
-+		struct ieee80211_bss_conf *conf = &vif->bss_conf;
-+		struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+		struct mt7996_bss_conf *mconf = mconf_dereference_protected(mvif, 0);
-+		struct mt7996_link_sta *mlink = mlink_dereference_protected(&mvif->sta, 0);
-+
-+		mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, true);
-+		mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, true, false);
-+	}
-+
-+	mutex_unlock(&dev->mt76.mutex);
-+}
-+
-+static void mt7996_link_info_changed(struct ieee80211_hw *hw,
-+				     struct ieee80211_vif *vif,
-+				     struct ieee80211_bss_conf *info,
-+				     u64 changed)
- {
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_bss_conf *mconf;
-@@ -735,7 +857,6 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
- 	 * and then peer references bss_info_rfch to set bandwidth cap.
- 	 */
- 	if ((changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) ||
--	    (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) ||
- 	    (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon)) {
- 		mt7996_mcu_add_bss_info(phy, info, mconf, mlink, true);
- 		mt7996_mcu_add_sta(dev, info, mconf, NULL, mlink, true,
-@@ -1078,7 +1199,7 @@ mt7996_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
- 	u64 ret;
- 
- 	mutex_lock(&dev->mt76.mutex);
--	mconf = mconf_dereference_protected(mvif, 0);
-+	mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
- 	ret = __mt7996_get_tsf(hw, mconf);
- 	mutex_unlock(&dev->mt76.mutex);
- 
-@@ -1101,7 +1222,7 @@ mt7996_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
--	mconf = mconf_dereference_protected(mvif, 0);
-+	mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
- 	n = mconf->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
- 					       : mconf->mt76.omac_idx;
- 	mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]);
-@@ -1129,7 +1250,7 @@ mt7996_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
--	mconf = mconf_dereference_protected(mvif, 0);
-+	mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
- 	n = mconf->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
- 					       : mconf->mt76.omac_idx;
- 	mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]);
-@@ -1304,7 +1425,7 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED;
- 
- 	mutex_lock(&dev->mt76.mutex);
--	mconf = mconf_dereference_protected(mvif, 0);
-+	mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
- 	mconf->bitrate_mask = *mask;
- 	mutex_unlock(&dev->mt76.mutex);
- 
-@@ -1524,7 +1645,7 @@ void mt7996_get_et_stats(struct ieee80211_hw *hw,
- 	int i, ei = 0;
- 
- 	mutex_lock(&dev->mt76.mutex);
--	mconf = mconf_dereference_protected(mvif, 0);
-+	mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
- 	wi.idx = mconf->mt76.idx,
- 
- 	mt7996_mac_update_stats(phy);
-@@ -1897,6 +2018,8 @@ mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	struct mt7996_phy *phy = ctx->phy;
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_bss_conf *mconf;
-+	u8 link_id = link_conf->link_id;
-+	int ret;
- 
- 	wiphy_info(hw->wiphy, "Assign VIF (addr: %pM, type: %d, link_id: %d) to channel context: %d MHz\n",
- 		    vif->addr, vif->type, link_conf->link_id,
-@@ -1904,10 +2027,24 @@ mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 
- 	mutex_lock(&phy->dev->mt76.mutex);
- 
--	mconf = mconf_dereference_protected(mvif, 0);
-+	/* remove first */
-+	if (rcu_access_pointer(mvif->link[link_id]))
-+		mt7996_remove_bss_conf(vif, link_conf,
-+				       mconf_dereference_protected(mvif, link_id));
-+
-+	ret = mt7996_add_bss_conf(phy, vif, link_conf);
-+	if (ret) {
-+		mutex_unlock(&phy->dev->mt76.mutex);
-+		return ret;
-+	}
-+
-+	mconf = mconf_dereference_protected(mvif, link_id);
- 	mconf->chanctx = ctx;
- 	ctx->nbss_assigned++;
- 
-+	if (mt7996_hw_phy(hw) == phy)
-+		mvif->master_link_id = link_id;
-+
- 	mutex_unlock(&phy->dev->mt76.mutex);
- 
- 	return 0;
-@@ -1933,7 +2070,7 @@ mt7996_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	if (test_bit(MT76_SCANNING, &phy->mt76->state))
- 		mt7996_scan_complete(phy, true);
- 
--	mconf = mconf_dereference_protected(mvif, 0);
-+	mconf = mconf_dereference_protected(mvif, link_conf->link_id);
- 	mconf->chanctx = NULL;
- 	ctx->nbss_assigned--;
- 
-@@ -1973,6 +2110,57 @@ mt7996_switch_vif_chanctx(struct ieee80211_hw *hw,
- 	return mt7996_set_channel(phy, &new_ctx->chandef);
- }
- 
-+static int
-+mt7996_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-+			u16 old_links, u16 new_links,
-+			struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS])
-+{
-+	struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	unsigned long rem = old_links & ~new_links;
-+	unsigned int link_id;
-+	int ret = 0;
-+
-+	if (old_links == new_links)
-+		return 0;
-+
-+	mutex_lock(&dev->mt76.mutex);
-+
-+	/* check if there's legacy bss needed to be removed */
-+	if (rcu_access_pointer(mvif->link[0]) == &mvif->deflink)
-+		rem |= BIT(0);
-+	/* remove first */
-+	for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
-+		struct mt7996_bss_conf *mconf =
-+			mconf_dereference_protected(mvif, link_id);
-+
-+		if (!mconf)
-+			continue;
-+
-+		mt7996_remove_bss_conf(vif, old[link_id], mconf);
-+	}
-+
-+	if (!old_links) {
-+		mvif->group_mld_id = get_own_mld_idx(dev->mld_id_mask, true);
-+		dev->mld_id_mask |= BIT_ULL(mvif->group_mld_id);
-+
-+		mvif->mld_remap_id = get_mld_remap_idx(dev->mld_remap_id_mask);
-+		dev->mld_remap_id_mask |= BIT_ULL(mvif->mld_remap_id);
-+	}
-+
-+	/* fallback to non-MLO interface */
-+	if (!new_links) {
-+		ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
-+		dev->mld_id_mask &= ~BIT_ULL(mvif->group_mld_id);
-+		dev->mld_remap_id_mask &= ~BIT_ULL(mvif->mld_remap_id);
-+	}
-+
-+	mutex_unlock(&dev->mt76.mutex);
-+
-+	return ret;
-+}
-+
- const struct ieee80211_ops mt7996_ops = {
- 	.tx = mt7996_tx,
- 	.start = mt7996_start,
-@@ -1982,7 +2170,8 @@ const struct ieee80211_ops mt7996_ops = {
- 	.config = mt7996_config,
- 	.conf_tx = mt7996_conf_tx,
- 	.configure_filter = mt7996_configure_filter,
--	.bss_info_changed = mt7996_bss_info_changed,
-+	.vif_cfg_changed = mt7996_vif_cfg_changed,
-+	.link_info_changed = mt7996_link_info_changed,
- 	.sta_state = mt76_sta_state,
- 	.sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
- 	.sta_rc_update = mt7996_sta_rc_update,
-@@ -2028,4 +2217,5 @@ const struct ieee80211_ops mt7996_ops = {
- 	.assign_vif_chanctx = mt7996_assign_vif_chanctx,
- 	.unassign_vif_chanctx = mt7996_unassign_vif_chanctx,
- 	.switch_vif_chanctx = mt7996_switch_vif_chanctx,
-+	.change_vif_links = mt7996_change_vif_links,
- };
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index cab4a0a..2a17299 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -1047,15 +1047,23 @@ static void
- mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
- 		       struct mt7996_bss_conf *mconf)
- {
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct bss_mld_tlv *mld;
- 	struct tlv *tlv;
- 
- 	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_MLD, sizeof(*mld));
--
- 	mld = (struct bss_mld_tlv *)tlv;
--	mld->group_mld_id = 0xff;
--	mld->own_mld_id = mconf->mt76.idx;
--	mld->remap_idx = 0xff;
-+
-+	if (ieee80211_vif_is_mld(vif)) {
-+		mld->group_mld_id = mvif->group_mld_id;
-+		mld->remap_idx = mvif->mld_remap_id;
-+		memcpy(mld->mac_addr, vif->addr, ETH_ALEN);
-+	} else {
-+		mld->group_mld_id = 0xff;
-+		mld->remap_idx = 0xff;
-+	}
-+
-+	mld->own_mld_id = mconf->own_mld_id;
- }
- 
- static void
-@@ -1136,13 +1144,11 @@ mt7996_mcu_bss_ifs_timing_tlv(struct sk_buff *skb, struct mt7996_phy *phy)
- }
- 
- static int
--mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
--			 struct ieee80211_bss_conf *conf,
--			 struct mt7996_bss_conf *mconf,
--			 struct ieee80211_sta *sta,
--			 struct mt76_phy *phy, u16 wlan_idx,
--			 bool enable)
-+mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
-+			 struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta,
-+			 u16 wlan_idx, bool enable)
- {
-+	struct mt76_phy *phy = mconf->phy->mt76;
- 	struct ieee80211_vif *vif = conf->vif;
- 	struct cfg80211_chan_def *chandef = &phy->chandef;
- 	struct mt76_connac_bss_basic_tlv *bss;
-@@ -1254,8 +1260,7 @@ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
- 		return PTR_ERR(skb);
- 
- 	/* bss_basic must be first */
--	mt7996_mcu_bss_basic_tlv(skb, conf, mconf, NULL, phy->mt76,
--				 mlink->wcid.idx, enable);
-+	mt7996_mcu_bss_basic_tlv(skb, conf, mconf, NULL, mlink->wcid.idx, enable);
- 	mt7996_mcu_bss_sec_tlv(skb, mconf);
- 
- 	if (vif->type == NL80211_IFTYPE_MONITOR)
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 3701720..ed8c82e 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -340,6 +340,9 @@ struct mt7996_bss_conf {
- 	struct cfg80211_bitrate_mask bitrate_mask;
- 
- 	struct mt7996_chanctx *chanctx;
-+
-+	u8 link_id;
-+	u8 own_mld_id;
- };
- 
- struct mt7996_vif {
-@@ -348,6 +351,10 @@ struct mt7996_vif {
- 
- 	struct mt7996_sta sta;
- 	struct mt7996_dev *dev;
-+
-+	u8 master_link_id;
-+	u8 group_mld_id;
-+	u8 mld_remap_id;
- };
- 
- /* crash-dump */
-@@ -549,6 +556,8 @@ struct mt7996_dev {
- 	u16 chainmask;
- 	u8 chainshift[__MT_MAX_BAND];
- 	u32 hif_idx;
-+	u64 mld_id_mask;
-+	u64 mld_remap_id_mask;
- 
- 	struct work_struct init_work;
- 	struct work_struct rc_work;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0090-mtk-mt76-mt7996-switch-to-per-link-data-structure-of.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0090-mtk-mt76-mt7996-switch-to-per-link-data-structure-of.patch
new file mode 100644
index 0000000..d7ad06b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0090-mtk-mt76-mt7996-switch-to-per-link-data-structure-of.patch
@@ -0,0 +1,2320 @@
+From 737e6a82f962447ef85055ebb32c07568fc8c722 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 24 Nov 2023 11:31:55 +0800
+Subject: [PATCH 090/199] mtk: mt76: mt7996: switch to per-link data structure
+ of vif
+
+Introduce struct mt7996_bss_conf, data structure for per-link BSS.
+Note that mt7996_vif now represents a legacy or MLD device.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/debugfs.c  |  10 +-
+ mt7996/init.c     |   4 +-
+ mt7996/mac.c      |  25 ++-
+ mt7996/main.c     | 269 ++++++++++++++++++-----------
+ mt7996/mcu.c      | 429 +++++++++++++++++++++++-----------------------
+ mt7996/mt7996.h   |  72 +++++---
+ mt7996/testmode.c |  17 +-
+ 7 files changed, 463 insertions(+), 363 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 7395f6df..5d4d69ea 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -726,7 +726,7 @@ static void
+ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta)
+ {
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+-	struct mt7996_dev *dev = msta->vif->phy->dev;
++	struct mt7996_dev *dev = msta->vif->deflink.phy->dev;
+ 	struct seq_file *s = data;
+ 	u8 ac;
+ 
+@@ -746,7 +746,7 @@ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta)
+ 				      GENMASK(11, 0));
+ 		seq_printf(s, "\tSTA %pM wcid %d: AC%d%d queued:%d\n",
+ 			   sta->addr, msta->wcid.idx,
+-			   msta->vif->mt76.wmm_idx, ac, qlen);
++			   msta->vif->deflink.mt76.wmm_idx, ac, qlen);
+ 	}
+ }
+ 
+@@ -1010,7 +1010,7 @@ mt7996_atf_enable_set(void *data, u64 val)
+ 	int ret;
+ 
+ 	vow->max_deficit = val ? 64 : 1;
+-	ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
++	ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
+ 	if (ret)
+ 		return ret;
+ 
+@@ -1042,7 +1042,7 @@ mt7996_airtime_read(struct seq_file *s, void *data)
+ 
+ 		msta = container_of(wcid, struct mt7996_sta, wcid);
+ 		sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
+-		vif = &msta->vif->mt76;
++		vif = &msta->vif->deflink.mt76;
+ 		stats = &wcid->stats;
+ 
+ 		seq_printf(s, "%pM WCID: %hu BandIdx: %hhu OmacIdx: 0x%hhx\t"
+@@ -1217,7 +1217,7 @@ static ssize_t mt7996_sta_fixed_rate_set(struct file *file,
+ #define LONG_PREAMBLE 1
+ 	struct ieee80211_sta *sta = file->private_data;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+-	struct mt7996_dev *dev = msta->vif->phy->dev;
++	struct mt7996_dev *dev = msta->vif->deflink.phy->dev;
+ 	struct ra_rate phy = {};
+ 	char buf[100];
+ 	int ret;
+diff --git a/mt7996/init.c b/mt7996/init.c
+index d18db618..86bb0661 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -630,11 +630,11 @@ static int mt7996_vow_init(struct mt7996_phy *phy)
+ 	vow->drr_quantum[6] = VOW_DRR_QUANTUM_L6;
+ 	vow->drr_quantum[7] = VOW_DRR_QUANTUM_L7;
+ 
+-	ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
++	ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, NULL, VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND);
+ 	if (ret)
+ 		return ret;
+ 
+-	ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL);
++	ret = mt7996_mcu_set_vow_drr_ctrl(phy, NULL, NULL, VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL);
+ 	if (ret)
+ 		return ret;
+ 
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index fc6e383b..5fbcfc74 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -900,8 +900,9 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 
+ 	if (vif) {
+ 		struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++		struct mt7996_bss_conf *mconf = &mvif->deflink;
+ 
+-		txp->fw.bss_idx = mvif->mt76.idx;
++		txp->fw.bss_idx = mconf->mt76.idx;
+ 	}
+ 
+ 	txp->fw.token = cpu_to_le16(id);
+@@ -1515,12 +1516,15 @@ static void
+ mt7996_update_vif_beacon(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ {
+ 	struct ieee80211_hw *hw = priv;
++	struct ieee80211_bss_conf *conf = &vif->bss_conf;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_bss_conf *mconf = &mvif->deflink;
+ 
+ 	switch (vif->type) {
+ 	case NL80211_IFTYPE_MESH_POINT:
+ 	case NL80211_IFTYPE_ADHOC:
+ 	case NL80211_IFTYPE_AP:
+-		mt7996_mcu_add_beacon(hw, vif, vif->bss_conf.enable_beacon);
++		mt7996_mcu_add_beacon(hw, conf, mconf, conf->enable_beacon);
+ 		break;
+ 	default:
+ 		break;
+@@ -2237,6 +2241,8 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
+ 	struct mt7996_dev *dev = container_of(work, struct mt7996_dev, rc_work);
+ 	struct ieee80211_sta *sta;
+ 	struct ieee80211_vif *vif;
++	struct ieee80211_bss_conf *conf;
++	struct mt7996_bss_conf *mconf;
+ 	struct mt7996_sta *msta;
+ 	u32 changed;
+ 	LIST_HEAD(list);
+@@ -2253,14 +2259,16 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
+ 
+ 		sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
+ 		vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv);
++		conf = &vif->bss_conf;
++		mconf = &msta->vif->deflink;
+ 
+ 		if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED |
+ 			       IEEE80211_RC_NSS_CHANGED |
+ 			       IEEE80211_RC_BW_CHANGED))
+-			mt7996_mcu_add_rate_ctrl(dev, vif, sta, true);
++			mt7996_mcu_add_rate_ctrl(dev, conf, mconf, sta, true);
+ 
+ 		if (changed & IEEE80211_RC_SMPS_CHANGED)
+-			mt7996_mcu_set_fixed_field(dev, vif, sta, NULL,
++			mt7996_mcu_set_fixed_field(dev, mconf, sta, NULL,
+ 						   RATE_PARAM_MMPS_UPDATE);
+ 
+ 		spin_lock_bh(&dev->mt76.sta_poll_lock);
+@@ -2643,7 +2651,7 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+ 
+ 		flow->sched = true;
+ 		flow->start_tsf = mt7996_mac_twt_sched_list_add(dev, flow);
+-		curr_tsf = __mt7996_get_tsf(hw, msta->vif);
++		curr_tsf = __mt7996_get_tsf(hw, &msta->vif->deflink);
+ 		div_u64_rem(curr_tsf - flow->start_tsf, interval, &rem);
+ 		flow_tsf = curr_tsf + interval - rem;
+ 		twt_agrt->twt = cpu_to_le64(flow_tsf);
+@@ -2652,7 +2660,8 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+ 	}
+ 	flow->tsf = le64_to_cpu(twt_agrt->twt);
+ 
+-	if (mt7996_mcu_twt_agrt_update(dev, msta->vif, flow, MCU_TWT_AGRT_ADD))
++	if (mt7996_mcu_twt_agrt_update(dev, &msta->vif->deflink, flow,
++				       MCU_TWT_AGRT_ADD))
+ 		goto unlock;
+ 
+ 	setup_cmd = TWT_SETUP_CMD_ACCEPT;
+@@ -2674,6 +2683,7 @@ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
+ 				  u8 flowid)
+ {
+ 	struct mt7996_twt_flow *flow;
++	struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
+ 
+ 	lockdep_assert_held(&dev->mt76.mutex);
+ 
+@@ -2684,8 +2694,7 @@ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
+ 		return;
+ 
+ 	flow = &msta->twt.flow[flowid];
+-	if (mt7996_mcu_twt_agrt_update(dev, msta->vif, flow,
+-				       MCU_TWT_AGRT_DELETE))
++	if (mt7996_mcu_twt_agrt_update(dev, mconf, flow, MCU_TWT_AGRT_DELETE))
+ 		return;
+ 
+ 	list_del_init(&flow->list);
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 0580b9ff..8a32ec69 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -205,29 +205,30 @@ static int get_omac_idx(enum nl80211_iftype type, u64 mask)
+ 	return -1;
+ }
+ 
+-static void mt7996_init_bitrate_mask(struct ieee80211_vif *vif)
++static void mt7996_init_bitrate_mask(struct mt7996_bss_conf *mconf)
+ {
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	int i;
+ 
+-	for (i = 0; i < ARRAY_SIZE(mvif->bitrate_mask.control); i++) {
+-		mvif->bitrate_mask.control[i].gi = NL80211_TXRATE_DEFAULT_GI;
+-		mvif->bitrate_mask.control[i].he_gi = 0xff;
+-		mvif->bitrate_mask.control[i].he_ltf = 0xff;
+-		mvif->bitrate_mask.control[i].legacy = GENMASK(31, 0);
+-		memset(mvif->bitrate_mask.control[i].ht_mcs, 0xff,
+-		       sizeof(mvif->bitrate_mask.control[i].ht_mcs));
+-		memset(mvif->bitrate_mask.control[i].vht_mcs, 0xff,
+-		       sizeof(mvif->bitrate_mask.control[i].vht_mcs));
+-		memset(mvif->bitrate_mask.control[i].he_mcs, 0xff,
+-		       sizeof(mvif->bitrate_mask.control[i].he_mcs));
++	for (i = 0; i < ARRAY_SIZE(mconf->bitrate_mask.control); i++) {
++		mconf->bitrate_mask.control[i].gi = NL80211_TXRATE_DEFAULT_GI;
++		mconf->bitrate_mask.control[i].he_gi = 0xff;
++		mconf->bitrate_mask.control[i].he_ltf = 0xff;
++		mconf->bitrate_mask.control[i].legacy = GENMASK(31, 0);
++		memset(mconf->bitrate_mask.control[i].ht_mcs, 0xff,
++		       sizeof(mconf->bitrate_mask.control[i].ht_mcs));
++		memset(mconf->bitrate_mask.control[i].vht_mcs, 0xff,
++		       sizeof(mconf->bitrate_mask.control[i].vht_mcs));
++		memset(mconf->bitrate_mask.control[i].he_mcs, 0xff,
++		       sizeof(mconf->bitrate_mask.control[i].he_mcs));
+ 	}
+ }
+ 
+ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ 				struct ieee80211_vif *vif)
+ {
++	struct ieee80211_bss_conf *conf = &vif->bss_conf;
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_bss_conf *mconf = &mvif->deflink;
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mt76_txq *mtxq;
+@@ -240,8 +241,8 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ 	    is_zero_ether_addr(vif->addr))
+ 		phy->monitor_vif = vif;
+ 
+-	mvif->mt76.idx = __ffs64(~dev->mt76.vif_mask);
+-	if (mvif->mt76.idx >= mt7996_max_interface_num(dev)) {
++	mconf->mt76.idx = __ffs64(~dev->mt76.vif_mask);
++	if (mconf->mt76.idx >= mt7996_max_interface_num(dev)) {
+ 		ret = -ENOSPC;
+ 		goto out;
+ 	}
+@@ -251,19 +252,21 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ 		ret = -ENOSPC;
+ 		goto out;
+ 	}
+-	mvif->mt76.omac_idx = idx;
+-	mvif->phy = phy;
+-	mvif->mt76.band_idx = band_idx;
+-	mvif->mt76.wmm_idx = vif->type == NL80211_IFTYPE_AP ? 0 : 3;
+-
+-	ret = mt7996_mcu_add_dev_info(phy, vif, true);
++	mconf->mt76.omac_idx = idx;
++	mconf->vif = mvif;
++	mconf->phy = phy;
++	mconf->mt76.band_idx = band_idx;
++	mconf->mt76.wmm_idx = vif->type == NL80211_IFTYPE_AP ? 0 : 3;
++	mvif->dev = dev;
++
++	ret = mt7996_mcu_add_dev_info(phy, conf, mconf, true);
+ 	if (ret)
+ 		goto out;
+ 
+-	dev->mt76.vif_mask |= BIT_ULL(mvif->mt76.idx);
+-	phy->omac_mask |= BIT_ULL(mvif->mt76.omac_idx);
++	dev->mt76.vif_mask |= BIT_ULL(mconf->mt76.idx);
++	phy->omac_mask |= BIT_ULL(mconf->mt76.omac_idx);
+ 
+-	idx = MT7996_WTBL_RESERVED - mvif->mt76.idx;
++	idx = MT7996_WTBL_RESERVED - mconf->mt76.idx;
+ 
+ 	INIT_LIST_HEAD(&mvif->sta.rc_list);
+ 	INIT_LIST_HEAD(&mvif->sta.wcid.poll_list);
+@@ -283,24 +286,25 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ 	}
+ 
+ 	if (vif->type != NL80211_IFTYPE_AP &&
+-	    (!mvif->mt76.omac_idx || mvif->mt76.omac_idx > 3))
++	    (!mconf->mt76.omac_idx || mconf->mt76.omac_idx > 3))
+ 		vif->offload_flags = 0;
+ 	vif->offload_flags |= IEEE80211_OFFLOAD_ENCAP_4ADDR;
+ 
+ 	if (phy->mt76->chandef.chan->band != NL80211_BAND_2GHZ)
+-		mvif->mt76.basic_rates_idx = MT7996_BASIC_RATES_TBL + 4;
++		mconf->mt76.basic_rates_idx = MT7996_BASIC_RATES_TBL + 4;
+ 	else
+-		mvif->mt76.basic_rates_idx = MT7996_BASIC_RATES_TBL;
++		mconf->mt76.basic_rates_idx = MT7996_BASIC_RATES_TBL;
+ 
+-	mt7996_init_bitrate_mask(vif);
++	mt7996_init_bitrate_mask(mconf);
+ 
+-	mt7996_mcu_add_bss_info(phy, vif, true);
++	mt7996_mcu_add_bss_info(phy, conf, mconf, true);
+ 	/* defer the first STA_REC of BMC entry to BSS_CHANGED_BSSID for STA
+ 	 * interface, since firmware only records BSSID when the entry is new
+ 	 */
+ 	if (vif->type != NL80211_IFTYPE_STATION)
+-		mt7996_mcu_add_sta(dev, vif, NULL, true, true);
++		mt7996_mcu_add_sta(dev, conf, mconf, NULL, true, true);
+ 	rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid);
++	rcu_assign_pointer(mvif->link[0], mconf);
+ 
+ out:
+ 	mutex_unlock(&dev->mt76.mutex);
+@@ -311,7 +315,9 @@ out:
+ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ 				    struct ieee80211_vif *vif)
+ {
++	struct ieee80211_bss_conf *conf;
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_bss_conf *mconf;
+ 	struct mt7996_sta *msta = &mvif->sta;
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+@@ -319,24 +325,22 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ 
+ 	cancel_delayed_work_sync(&phy->scan_work);
+ 
+-	mt7996_mcu_add_sta(dev, vif, NULL, false, false);
+-	mt7996_mcu_add_bss_info(phy, vif, false);
++	mutex_lock(&dev->mt76.mutex);
++
++	conf = link_conf_dereference_protected(vif, 0);
++	mconf = mconf_dereference_protected(mvif, 0);
++	mt7996_mcu_add_sta(dev, conf, mconf, NULL, false, false);
++	mt7996_mcu_add_bss_info(phy, conf, mconf, false);
+ 
+ 	if (vif == phy->monitor_vif)
+ 		phy->monitor_vif = NULL;
+ 
+-	mt7996_mcu_add_dev_info(phy, vif, false);
++	mt7996_mcu_add_dev_info(phy, conf, mconf, false);
+ 
+ 	rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
+ 
+-	mutex_lock(&dev->mt76.mutex);
+-
+-	if (test_bit(MT76_SCANNING, &phy->mt76->state))
+-		mt7996_scan_complete(phy, true);
+-
+-	dev->mt76.vif_mask &= ~BIT_ULL(mvif->mt76.idx);
+-	phy->omac_mask &= ~BIT_ULL(mvif->mt76.omac_idx);
+-	mutex_unlock(&dev->mt76.mutex);
++	dev->mt76.vif_mask &= ~BIT_ULL(mconf->mt76.idx);
++	phy->omac_mask &= ~BIT_ULL(mconf->mt76.omac_idx);
+ 
+ 	spin_lock_bh(&dev->mt76.sta_poll_lock);
+ 	if (!list_empty(&msta->wcid.poll_list))
+@@ -344,6 +348,9 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ 	spin_unlock_bh(&dev->mt76.sta_poll_lock);
+ 
+ 	mt76_wcid_cleanup(&dev->mt76, &msta->wcid);
++	rcu_assign_pointer(mvif->link[0], NULL);
++
++	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
+ static void ___mt7996_set_channel(struct mt7996_phy *phy,
+@@ -445,6 +452,8 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ 	struct mt7996_sta *msta = sta ? (struct mt7996_sta *)sta->drv_priv :
+ 				  &mvif->sta;
+ 	struct mt76_wcid *wcid = &msta->wcid;
++	struct mt7996_bss_conf *mconf;
++	struct ieee80211_bss_conf *conf;
+ 	u8 *wcid_keyidx = &wcid->hw_key_idx;
+ 	int idx = key->keyidx;
+ 	int err = 0;
+@@ -486,9 +495,11 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+-	if (cmd == SET_KEY && !sta && !mvif->mt76.cipher) {
+-		mvif->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher);
+-		mt7996_mcu_add_bss_info(phy, vif, true);
++	conf = link_conf_dereference_protected(vif, 0);
++	mconf = mconf_dereference_protected(mvif, 0);
++	if (cmd == SET_KEY && !sta && !mconf->mt76.cipher) {
++		mconf->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher);
++		mt7996_mcu_add_bss_info(phy, conf, mconf, true);
+ 	}
+ 
+ 	if (cmd == SET_KEY) {
+@@ -502,9 +513,9 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ 	mt76_wcid_key_setup(&dev->mt76, wcid, key);
+ 
+ 	if (key->keyidx == 6 || key->keyidx == 7)
+-		err = mt7996_mcu_bcn_prot_enable(dev, vif, key);
++		err = mt7996_mcu_bcn_prot_enable(dev, conf, mconf, key);
+ 	else
+-		err = mt7996_mcu_add_key(&dev->mt76, vif, key,
++		err = mt7996_mcu_add_key(&dev->mt76, mconf, key,
+ 					 MCU_WMWA_UNI_CMD(STA_REC_UPDATE),
+ 					 &msta->wcid, cmd);
+ out:
+@@ -551,7 +562,9 @@ mt7996_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	       unsigned int link_id, u16 queue,
+ 	       const struct ieee80211_tx_queue_params *params)
+ {
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_bss_conf *mconf;
+ 	static const u8 mq_to_aci[] = {
+ 		[IEEE80211_AC_VO] = 3,
+ 		[IEEE80211_AC_VI] = 2,
+@@ -559,10 +572,15 @@ mt7996_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 		[IEEE80211_AC_BK] = 1,
+ 	};
+ 
++	mutex_lock(&dev->mt76.mutex);
++	mconf = mconf_dereference_protected(mvif, 0);
++
+ 	/* firmware uses access class index */
+-	mvif->queue_params[mq_to_aci[queue]] = *params;
++	mconf->queue_params[mq_to_aci[queue]] = *params;
+ 	/* no need to update right away, we'll get BSS_CHANGED_QOS */
+ 
++	mutex_unlock(&dev->mt76.mutex);
++
+ 	return 0;
+ }
+ 
+@@ -623,22 +641,20 @@ static void mt7996_configure_filter(struct ieee80211_hw *hw,
+ }
+ 
+ static void
+-mt7996_update_bss_color(struct ieee80211_hw *hw,
+-			struct ieee80211_vif *vif,
++mt7996_update_bss_color(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++			struct mt7996_bss_conf *mconf,
+ 			struct cfg80211_he_bss_color *bss_color)
+ {
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 
+ 	switch (vif->type) {
+ 	case NL80211_IFTYPE_AP: {
+-		struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-
+-		if (mvif->mt76.omac_idx > HW_BSSID_MAX)
++		if (mconf->mt76.omac_idx > HW_BSSID_MAX)
+ 			return;
+ 		fallthrough;
+ 	}
+ 	case NL80211_IFTYPE_STATION:
+-		mt7996_mcu_update_bss_color(dev, vif, bss_color);
++		mt7996_mcu_update_bss_color(dev, mconf, bss_color);
+ 		break;
+ 	default:
+ 		break;
+@@ -646,16 +662,15 @@ mt7996_update_bss_color(struct ieee80211_hw *hw,
+ }
+ 
+ static u8
+-mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+-		       bool beacon, bool mcast)
++mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
++		       struct mt7996_bss_conf *mconf, bool beacon, bool mcast)
+ {
+-	struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+-	struct mt76_phy *mphy = hw->priv;
++	struct mt76_phy *mphy = mconf->phy->mt76;
+ 	u16 rate;
+ 	u8 i, idx;
+ 
+-	rate = mt76_connac2_mac_tx_rate_val(mphy, vif, beacon, mcast);
++	rate = mt76_connac2_mac_tx_rate_val(mphy, conf->vif, beacon, mcast);
+ 
+ 	if (beacon) {
+ 		struct mt7996_phy *phy = mphy->priv;
+@@ -676,23 +691,22 @@ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 		if ((mt76_rates[i].hw_value & GENMASK(7, 0)) == idx)
+ 			return MT7996_BASIC_RATES_TBL + 2 * i;
+ 
+-	return mvif->basic_rates_idx;
++	return mconf->mt76.basic_rates_idx;
+ }
+ 
+ static void
+-mt7996_update_mu_group(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+-		       struct ieee80211_bss_conf *info)
++mt7996_update_mu_group(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
++		       struct mt7996_bss_conf *mconf)
+ {
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+-	u8 band = mvif->mt76.band_idx;
++	u8 band = mconf->mt76.band_idx;
+ 	u32 *mu;
+ 
+-	mu = (u32 *)info->mu_group.membership;
++	mu = (u32 *)conf->mu_group.membership;
+ 	mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_VLD0(band), mu[0]);
+ 	mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_VLD1(band), mu[1]);
+ 
+-	mu = (u32 *)info->mu_group.position;
++	mu = (u32 *)conf->mu_group.position;
+ 	mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_POS0(band), mu[0]);
+ 	mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_POS1(band), mu[1]);
+ 	mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_POS2(band), mu[2]);
+@@ -704,20 +718,22 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
+ 				    struct ieee80211_bss_conf *info,
+ 				    u64 changed)
+ {
+-	struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_bss_conf *mconf;
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
++	mconf = mconf_dereference_protected(mvif, 0);
+ 	/* station mode uses BSSID to map the wlan entry to a peer,
+ 	 * and then peer references bss_info_rfch to set bandwidth cap.
+ 	 */
+ 	if ((changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) ||
+ 	    (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) ||
+ 	    (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon)) {
+-		mt7996_mcu_add_bss_info(phy, vif, true);
+-		mt7996_mcu_add_sta(dev, vif, NULL, true,
++		mt7996_mcu_add_bss_info(phy, info, mconf, true);
++		mt7996_mcu_add_sta(dev, info, mconf, NULL, true,
+ 				   !!(changed & BSS_CHANGED_BSSID));
+ 	}
+ 
+@@ -729,42 +745,42 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
+ 
+ 		if (slottime != phy->slottime) {
+ 			phy->slottime = slottime;
+-			mt7996_mcu_set_timing(phy, vif);
++			mt7996_mcu_set_timing(phy, mconf);
+ 		}
+ 	}
+ 
+ 	if (changed & BSS_CHANGED_MCAST_RATE)
+-		mvif->mcast_rates_idx =
+-			mt7996_get_rates_table(hw, vif, false, true);
++		mconf->mt76.mcast_rates_idx =
++			mt7996_get_rates_table(hw, info, mconf, false, true);
+ 
+ 	if (changed & BSS_CHANGED_BASIC_RATES)
+-		mvif->basic_rates_idx =
+-			mt7996_get_rates_table(hw, vif, false, false);
++		mconf->mt76.basic_rates_idx =
++			mt7996_get_rates_table(hw, info, mconf, false, false);
+ 
+ 	/* ensure that enable txcmd_mode after bss_info */
+ 	if (changed & (BSS_CHANGED_QOS | BSS_CHANGED_BEACON_ENABLED))
+-		mt7996_mcu_set_tx(dev, vif);
++		mt7996_mcu_set_tx(dev, mconf);
+ 
+ 	if (changed & BSS_CHANGED_HE_OBSS_PD)
+-		mt7996_mcu_add_obss_spr(phy, vif, &info->he_obss_pd);
++		mt7996_mcu_add_obss_spr(phy, mconf, &info->he_obss_pd);
+ 
+ 	if (changed & BSS_CHANGED_HE_BSS_COLOR)
+-		mt7996_update_bss_color(hw, vif, &info->he_bss_color);
++		mt7996_update_bss_color(hw, vif, mconf, &info->he_bss_color);
+ 
+ 	if (changed & (BSS_CHANGED_BEACON |
+ 		       BSS_CHANGED_BEACON_ENABLED)) {
+-		mvif->beacon_rates_idx =
+-			mt7996_get_rates_table(hw, vif, true, false);
++		mconf->mt76.beacon_rates_idx =
++			mt7996_get_rates_table(hw, info, mconf, true, false);
+ 
+-		mt7996_mcu_add_beacon(hw, vif, info->enable_beacon);
++		mt7996_mcu_add_beacon(hw, info, mconf, info->enable_beacon);
+ 	}
+ 
+ 	if (changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP |
+ 		       BSS_CHANGED_FILS_DISCOVERY))
+-		mt7996_mcu_beacon_inband_discov(dev, vif, changed);
++		mt7996_mcu_beacon_inband_discov(dev, info, mconf, changed);
+ 
+ 	if (changed & BSS_CHANGED_MU_GROUPS)
+-		mt7996_update_mu_group(hw, vif, info);
++		mt7996_update_mu_group(hw, info, mconf);
+ 
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+@@ -775,9 +791,14 @@ mt7996_channel_switch_beacon(struct ieee80211_hw *hw,
+ 			     struct cfg80211_chan_def *chandef)
+ {
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_bss_conf *mconf;
++	struct ieee80211_bss_conf *conf;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+-	mt7996_mcu_add_beacon(hw, vif, true);
++	mconf = mconf_dereference_protected(mvif, 0);
++	conf = link_conf_dereference_protected(vif, 0);
++	mt7996_mcu_add_beacon(hw, conf, mconf, true);
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
+@@ -787,7 +808,8 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	u8 band_idx = mvif->phy->mt76->band_idx;
++	struct mt7996_bss_conf *mconf = mconf_dereference_protected(mvif, 0);
++	u8 band_idx = mconf->phy->mt76->band_idx;
+ 	int idx;
+ 
+ #ifdef CONFIG_MTK_VENDOR
+@@ -807,7 +829,7 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 	msta->wcid.tx_info |= MT_WCID_TX_INFO_SET;
+ 
+ #ifdef CONFIG_MTK_VENDOR
+-	mt7996_vendor_amnt_sta_remove(mvif->phy, sta);
++	mt7996_vendor_amnt_sta_remove(mconf->phy, sta);
+ #endif
+ 
+ #ifdef CONFIG_MTK_VENDOR
+@@ -834,15 +856,20 @@ void mt7996_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 			  struct ieee80211_sta *sta)
+ {
+ 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_bss_conf *mconf;
++	struct ieee80211_bss_conf *conf;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+ 	mt7996_mac_wtbl_update(dev, msta->wcid.idx,
+ 			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+ 
+-	mt7996_mcu_add_sta(dev, vif, sta, true, true);
+-	mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
++	conf = link_conf_dereference_protected(vif, 0);
++	mconf = mconf_dereference_protected(mvif, 0);
++	mt7996_mcu_add_sta(dev, conf, mconf, sta, true, true);
++	mt7996_mcu_add_rate_ctrl(dev, conf, mconf, sta, false);
+ 
+ 	ewma_avg_signal_init(&msta->avg_ack_signal);
+ 
+@@ -853,10 +880,15 @@ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 			   struct ieee80211_sta *sta)
+ {
+ 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_bss_conf *mconf;
++	struct ieee80211_bss_conf *conf;
+ 	int i;
+ 
+-	mt7996_mcu_add_sta(dev, vif, sta, false, false);
++	conf = link_conf_dereference_protected(vif, 0);
++	mconf = mconf_dereference_protected(mvif, 0);
++	mt7996_mcu_add_sta(dev, conf, mconf, sta, false, false);
+ 
+ 	mt7996_mac_wtbl_update(dev, msta->wcid.idx,
+ 			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+@@ -988,7 +1020,7 @@ mt7996_get_stats(struct ieee80211_hw *hw,
+ 	return 0;
+ }
+ 
+-u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif)
++u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_bss_conf *mconf)
+ {
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+@@ -1000,8 +1032,8 @@ u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif)
+ 
+ 	lockdep_assert_held(&dev->mt76.mutex);
+ 
+-	n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
+-					       : mvif->mt76.omac_idx;
++	n = mconf->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
++					       : mconf->mt76.omac_idx;
+ 	/* TSF software read */
+ 	mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE,
+ 		 MT_LPON_TCR_SW_READ);
+@@ -1016,10 +1048,12 @@ mt7996_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ {
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mt7996_bss_conf *mconf;
+ 	u64 ret;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+-	ret = __mt7996_get_tsf(hw, mvif);
++	mconf = mconf_dereference_protected(mvif, 0);
++	ret = __mt7996_get_tsf(hw, mconf);
+ 	mutex_unlock(&dev->mt76.mutex);
+ 
+ 	return ret;
+@@ -1032,6 +1066,7 @@ mt7996_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct mt7996_bss_conf *mconf;
+ 	union {
+ 		u64 t64;
+ 		u32 t32[2];
+@@ -1040,8 +1075,9 @@ mt7996_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+-	n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
+-					       : mvif->mt76.omac_idx;
++	mconf = mconf_dereference_protected(mvif, 0);
++	n = mconf->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
++					       : mconf->mt76.omac_idx;
+ 	mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]);
+ 	mt76_wr(dev, MT_LPON_UTTR1(phy->mt76->band_idx), tsf.t32[1]);
+ 	/* TSF software overwrite */
+@@ -1058,6 +1094,7 @@ mt7996_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct mt7996_bss_conf *mconf;
+ 	union {
+ 		u64 t64;
+ 		u32 t32[2];
+@@ -1066,8 +1103,9 @@ mt7996_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+-	n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
+-					       : mvif->mt76.omac_idx;
++	mconf = mconf_dereference_protected(mvif, 0);
++	n = mconf->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
++					       : mconf->mt76.omac_idx;
+ 	mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]);
+ 	mt76_wr(dev, MT_LPON_UTTR1(phy->mt76->band_idx), tsf.t32[1]);
+ 	/* TSF software adjust*/
+@@ -1183,7 +1221,7 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
+ static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta)
+ {
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+-	struct mt7996_dev *dev = msta->vif->phy->dev;
++	struct mt7996_dev *dev = msta->vif->dev;
+ 	u32 *changed = data;
+ 
+ 	spin_lock_bh(&dev->mt76.sta_poll_lock);
+@@ -1219,9 +1257,13 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mt7996_dev *dev = phy->dev;
++	struct mt7996_bss_conf *mconf;
+ 	u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED;
+ 
+-	mvif->bitrate_mask = *mask;
++	mutex_lock(&dev->mt76.mutex);
++	mconf = mconf_dereference_protected(mvif, 0);
++	mconf->bitrate_mask = *mask;
++	mutex_unlock(&dev->mt76.mutex);
+ 
+ 	/* if multiple rates across different preambles are given we can
+ 	 * reconfigure this info with all peers using sta_rec command with
+@@ -1243,14 +1285,20 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw,
+ 				 bool enabled)
+ {
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_bss_conf *mconf;
++
++	mutex_lock(&dev->mt76.mutex);
++	mconf = mconf_dereference_protected(mvif, 0);
+ 
+ 	if (enabled)
+ 		set_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags);
+ 	else
+ 		clear_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags);
+ 
+-	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta);
++	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, sta);
++	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
+ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw,
+@@ -1259,14 +1307,20 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw,
+ 					 bool enabled)
+ {
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_bss_conf *mconf;
++
++	mutex_lock(&dev->mt76.mutex);
++	mconf = mconf_dereference_protected(mvif, 0);
+ 
+ 	if (enabled)
+ 		set_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
+ 	else
+ 		clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
+ 
+-	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta);
++	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, sta);
++	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
+ static const char mt7996_gstrings_stats[][ETH_GSTRING_LEN] = {
+@@ -1399,7 +1453,7 @@ static void mt7996_ethtool_worker(void *wi_data, struct ieee80211_sta *sta)
+ 	struct mt76_ethtool_worker_info *wi = wi_data;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 
+-	if (msta->vif->mt76.idx != wi->idx)
++	if (msta->vif->deflink.mt76.idx != wi->idx)
+ 		return;
+ 
+ 	mt76_ethtool_worker(wi, &msta->wcid.stats, true);
+@@ -1413,15 +1467,17 @@ void mt7996_get_et_stats(struct ieee80211_hw *hw,
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_bss_conf *mconf;
+ 	struct mt76_mib_stats *mib = &phy->mib;
+ 	struct mt76_ethtool_worker_info wi = {
+ 		.data = data,
+-		.idx = mvif->mt76.idx,
+ 	};
+ 	/* See mt7996_ampdu_stat_read_phy, etc */
+ 	int i, ei = 0;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
++	mconf = mconf_dereference_protected(mvif, 0);
++	wi.idx = mconf->mt76.idx,
+ 
+ 	mt7996_mac_update_stats(phy);
+ 
+@@ -1627,6 +1683,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ 			     struct net_device_path *path)
+ {
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_bss_conf *mconf = &mvif->deflink;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+@@ -1656,7 +1713,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ 	path->type = DEV_PATH_MTK_WDMA;
+ 	path->dev = ctx->dev;
+ 	path->mtk_wdma.wdma_idx = wed->wdma_idx;
+-	path->mtk_wdma.bss = mvif->mt76.idx;
++	path->mtk_wdma.bss = mconf->mt76.idx;
+ 	path->mtk_wdma.queue = 0;
+ 	path->mtk_wdma.wcid = msta->wcid.idx;
+ 
+@@ -1788,6 +1845,7 @@ mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
+ 	struct mt7996_phy *phy = ctx->phy;
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_bss_conf *mconf;
+ 
+ 	wiphy_info(hw->wiphy, "Assign VIF (addr: %pM, type: %d, link_id: %d) to channel context: %d MHz\n",
+ 		    vif->addr, vif->type, link_conf->link_id,
+@@ -1795,7 +1853,8 @@ mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 
+ 	mutex_lock(&phy->dev->mt76.mutex);
+ 
+-	mvif->chanctx = ctx;
++	mconf = mconf_dereference_protected(mvif, 0);
++	mconf->chanctx = ctx;
+ 	ctx->nbss_assigned++;
+ 
+ 	mutex_unlock(&phy->dev->mt76.mutex);
+@@ -1811,6 +1870,7 @@ mt7996_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
+ 	struct mt7996_phy *phy = ctx->phy;
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_bss_conf *mconf;
+ 
+ 	wiphy_info(hw->wiphy, "Remove VIF (addr: %pM, type: %d, link_id: %d) from channel context: %d MHz\n",
+ 		   vif->addr, vif->type, link_conf->link_id,
+@@ -1822,7 +1882,8 @@ mt7996_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	if (test_bit(MT76_SCANNING, &phy->mt76->state))
+ 		mt7996_scan_complete(phy, true);
+ 
+-	mvif->chanctx = NULL;
++	mconf = mconf_dereference_protected(mvif, 0);
++	mconf->chanctx = NULL;
+ 	ctx->nbss_assigned--;
+ 
+ 	mutex_unlock(&phy->dev->mt76.mutex);
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 7faf0916..1e0fca2d 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -117,12 +117,12 @@ mt7996_mcu_get_sta_nss(u16 mcs_map)
+ }
+ 
+ static void
+-mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs,
+-			  u16 mcs_map)
++mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta,
++			  struct mt7996_bss_conf *mconf,
++			  __le16 *he_mcs, u16 mcs_map)
+ {
+-	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+-	enum nl80211_band band = msta->vif->phy->mt76->chandef.chan->band;
+-	const u16 *mask = msta->vif->bitrate_mask.control[band].he_mcs;
++	enum nl80211_band band = mconf->phy->mt76->chandef.chan->band;
++	const u16 *mask = mconf->bitrate_mask.control[band].he_mcs;
+ 	int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
+ 
+ 	for (nss = 0; nss < max_nss; nss++) {
+@@ -922,8 +922,7 @@ mt7996_mcu_add_uni_tlv(struct sk_buff *skb, u16 tag, u16 len)
+ }
+ 
+ static void
+-mt7996_mcu_bss_rfch_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+-			struct mt7996_phy *phy)
++mt7996_mcu_bss_rfch_tlv(struct sk_buff *skb, struct mt7996_phy *phy)
+ {
+ 	static const u8 rlm_ch_band[] = {
+ 		[NL80211_BAND_2GHZ] = 1,
+@@ -953,8 +952,7 @@ mt7996_mcu_bss_rfch_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ }
+ 
+ static void
+-mt7996_mcu_bss_ra_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+-		      struct mt7996_phy *phy)
++mt7996_mcu_bss_ra_tlv(struct sk_buff *skb)
+ {
+ 	struct bss_ra_tlv *ra;
+ 	struct tlv *tlv;
+@@ -966,7 +964,7 @@ mt7996_mcu_bss_ra_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ }
+ 
+ static void
+-mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
++mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
+ 		      struct mt7996_phy *phy)
+ {
+ #define DEFAULT_HE_PE_DURATION		4
+@@ -975,16 +973,16 @@ mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ 	struct bss_info_uni_he *he;
+ 	struct tlv *tlv;
+ 
+-	cap = mt76_connac_get_he_phy_cap(phy->mt76, vif);
++	cap = mt76_connac_get_he_phy_cap(phy->mt76, conf->vif);
+ 
+ 	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he));
+ 
+ 	he = (struct bss_info_uni_he *)tlv;
+-	he->he_pe_duration = vif->bss_conf.htc_trig_based_pkt_ext;
++	he->he_pe_duration = conf->htc_trig_based_pkt_ext;
+ 	if (!he->he_pe_duration)
+ 		he->he_pe_duration = DEFAULT_HE_PE_DURATION;
+ 
+-	he->he_rts_thres = cpu_to_le16(vif->bss_conf.frame_time_rts_th);
++	he->he_rts_thres = cpu_to_le16(conf->frame_time_rts_th);
+ 	if (!he->he_rts_thres)
+ 		he->he_rts_thres = cpu_to_le16(DEFAULT_HE_DURATION_RTS_THRES);
+ 
+@@ -994,13 +992,13 @@ mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ }
+ 
+ static void
+-mt7996_mcu_bss_mbssid_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
++mt7996_mcu_bss_mbssid_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
+ 			  struct mt7996_phy *phy, int enable)
+ {
+ 	struct bss_info_uni_mbssid *mbssid;
+ 	struct tlv *tlv;
+ 
+-	if (!vif->bss_conf.bssid_indicator && enable)
++	if (!conf->bssid_indicator && enable)
+ 		return;
+ 
+ 	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_11V_MBSSID, sizeof(*mbssid));
+@@ -1008,23 +1006,21 @@ mt7996_mcu_bss_mbssid_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ 	mbssid = (struct bss_info_uni_mbssid *)tlv;
+ 
+ 	if (enable) {
+-		mbssid->max_indicator = vif->bss_conf.bssid_indicator;
+-		mbssid->mbss_idx = vif->bss_conf.bssid_index;
++		mbssid->max_indicator = conf->bssid_indicator;
++		mbssid->mbss_idx = conf->bssid_index;
+ 		mbssid->tx_bss_omac_idx = 0;
+ 	}
+ }
+ 
+ static void
+-mt7996_mcu_bss_bmc_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
++mt7996_mcu_bss_bmc_tlv(struct sk_buff *skb, struct mt7996_bss_conf *mconf,
+ 		       struct mt7996_phy *phy)
+ {
+-	struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+ 	struct bss_rate_tlv *bmc;
+ 	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
+ 	enum nl80211_band band = chandef->chan->band;
+ 	struct tlv *tlv;
+-	u8 idx = mvif->mcast_rates_idx ?
+-		 mvif->mcast_rates_idx : mvif->basic_rates_idx;
++	u8 idx = mconf->mt76.mcast_rates_idx ?: mconf->mt76.basic_rates_idx;
+ 
+ 	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_RATE, sizeof(*bmc));
+ 
+@@ -1048,9 +1044,9 @@ mt7996_mcu_bss_txcmd_tlv(struct sk_buff *skb, bool en)
+ }
+ 
+ static void
+-mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
++mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
++		       struct mt7996_bss_conf *mconf)
+ {
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct bss_mld_tlv *mld;
+ 	struct tlv *tlv;
+ 
+@@ -1058,33 +1054,30 @@ mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
+ 
+ 	mld = (struct bss_mld_tlv *)tlv;
+ 	mld->group_mld_id = 0xff;
+-	mld->own_mld_id = mvif->mt76.idx;
++	mld->own_mld_id = mconf->mt76.idx;
+ 	mld->remap_idx = 0xff;
+ }
+ 
+ static void
+-mt7996_mcu_bss_sec_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
++mt7996_mcu_bss_sec_tlv(struct sk_buff *skb, struct mt7996_bss_conf *mconf)
+ {
+-	struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+ 	struct bss_sec_tlv *sec;
+ 	struct tlv *tlv;
+ 
+ 	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_SEC, sizeof(*sec));
+ 
+ 	sec = (struct bss_sec_tlv *)tlv;
+-	sec->cipher = mvif->cipher;
++	sec->cipher = mconf->mt76.cipher;
+ }
+ 
+ static int
+-mt7996_mcu_muar_config(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+-		       bool bssid, bool enable)
++mt7996_mcu_muar_config(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
++		       struct mt7996_bss_conf *mconf, bool bssid, bool enable)
+ {
+ #define UNI_MUAR_ENTRY 2
+ 	struct mt7996_dev *dev = phy->dev;
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	u32 idx = mvif->mt76.omac_idx - REPEATER_BSSID_START;
+-	const u8 *addr = vif->addr;
+-
++	u32 idx = mconf->mt76.omac_idx - REPEATER_BSSID_START;
++	const u8 *addr = bssid ? conf->bssid : conf->vif->addr;
+ 	struct {
+ 		struct {
+ 			u8 band;
+@@ -1109,9 +1102,6 @@ mt7996_mcu_muar_config(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ 		.entry_add = true,
+ 	};
+ 
+-	if (bssid)
+-		addr = vif->bss_conf.bssid;
+-
+ 	if (enable)
+ 		memcpy(req.addr, addr, ETH_ALEN);
+ 
+@@ -1120,10 +1110,8 @@ mt7996_mcu_muar_config(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ }
+ 
+ static void
+-mt7996_mcu_bss_ifs_timing_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
++mt7996_mcu_bss_ifs_timing_tlv(struct sk_buff *skb, struct mt7996_phy *phy)
+ {
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct mt7996_phy *phy = mvif->phy;
+ 	struct bss_ifs_time_tlv *ifs_time;
+ 	struct tlv *tlv;
+ 	bool is_2ghz = phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ;
+@@ -1149,12 +1137,13 @@ mt7996_mcu_bss_ifs_timing_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
+ 
+ static int
+ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
+-			 struct ieee80211_vif *vif,
++			 struct ieee80211_bss_conf *conf,
++			 struct mt7996_bss_conf *mconf,
+ 			 struct ieee80211_sta *sta,
+ 			 struct mt76_phy *phy, u16 wlan_idx,
+ 			 bool enable)
+ {
+-	struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
++	struct ieee80211_vif *vif = conf->vif;
+ 	struct cfg80211_chan_def *chandef = &phy->chandef;
+ 	struct mt76_connac_bss_basic_tlv *bss;
+ 	u32 type = CONNECTION_INFRA_AP;
+@@ -1171,8 +1160,7 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
+ 		if (enable) {
+ 			rcu_read_lock();
+ 			if (!sta)
+-				sta = ieee80211_find_sta(vif,
+-							 vif->bss_conf.bssid);
++				sta = ieee80211_find_sta(vif, conf->bssid);
+ 			/* TODO: enable BSS_INFO_UAPSD & BSS_INFO_PM */
+ 			if (sta) {
+ 				struct mt76_wcid *wcid;
+@@ -1195,18 +1183,17 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
+ 	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_BASIC, sizeof(*bss));
+ 
+ 	bss = (struct mt76_connac_bss_basic_tlv *)tlv;
+-	bss->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int);
+-	bss->dtim_period = vif->bss_conf.dtim_period;
+ 	bss->bmc_tx_wlan_idx = cpu_to_le16(wlan_idx);
+ 	bss->sta_idx = cpu_to_le16(sta_wlan_idx);
+ 	bss->conn_type = cpu_to_le32(type);
+-	bss->omac_idx = mvif->omac_idx;
+-	bss->band_idx = mvif->band_idx;
+-	bss->wmm_idx = mvif->wmm_idx;
++	bss->omac_idx = mconf->mt76.omac_idx;
++	bss->band_idx = mconf->mt76.band_idx;
++	bss->wmm_idx = mconf->mt76.wmm_idx;
+ 	bss->conn_state = !enable;
+ 	bss->active = enable;
+ 
+-	idx = mvif->omac_idx > EXT_BSSID_START ? HW_BSSID_0 : mvif->omac_idx;
++	idx = mconf->mt76.omac_idx > EXT_BSSID_START ? HW_BSSID_0 :
++						       mconf->mt76.omac_idx;
+ 	bss->hw_bss_idx = idx;
+ 
+ 	if (vif->type == NL80211_IFTYPE_MONITOR) {
+@@ -1219,9 +1206,9 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
+ 		return 0;
+ 	}
+ 
+-	memcpy(bss->bssid, vif->bss_conf.bssid, ETH_ALEN);
+-	bss->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int);
+-	bss->dtim_period = vif->bss_conf.dtim_period;
++	memcpy(bss->bssid, conf->bssid, ETH_ALEN);
++	bss->bcn_interval = cpu_to_le16(conf->beacon_int);
++	bss->dtim_period = conf->dtim_period;
+ 	bss->phymode = mt76_connac_get_phy_mode(phy, vif,
+ 						chandef->chan->band, NULL);
+ 	bss->phymode_ext = mt76_connac_get_phy_mode_ext(phy, vif,
+@@ -1248,63 +1235,64 @@ __mt7996_mcu_alloc_bss_req(struct mt76_dev *dev, struct mt76_vif *mvif, int len)
+ }
+ 
+ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
+-			    struct ieee80211_vif *vif, int enable)
++			    struct ieee80211_bss_conf *conf,
++			    struct mt7996_bss_conf *mconf, int enable)
+ {
++	struct ieee80211_vif *vif = conf->vif;
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_dev *dev = phy->dev;
+ 	struct sk_buff *skb;
+ 
+-	if (mvif->mt76.omac_idx >= REPEATER_BSSID_START) {
+-		mt7996_mcu_muar_config(phy, vif, false, enable);
+-		mt7996_mcu_muar_config(phy, vif, true, enable);
++	if (mconf->mt76.omac_idx >= REPEATER_BSSID_START) {
++		mt7996_mcu_muar_config(phy, conf, mconf, false, enable);
++		mt7996_mcu_muar_config(phy, conf, mconf, true, enable);
+ 	}
+ 
+-	skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
++	skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76,
+ 					 MT7996_BSS_UPDATE_MAX_SIZE);
+ 	if (IS_ERR(skb))
+ 		return PTR_ERR(skb);
+ 
+ 	/* bss_basic must be first */
+-	mt7996_mcu_bss_basic_tlv(skb, vif, NULL, phy->mt76,
++	mt7996_mcu_bss_basic_tlv(skb, conf, mconf, NULL, phy->mt76,
+ 				 mvif->sta.wcid.idx, enable);
+-	mt7996_mcu_bss_sec_tlv(skb, vif);
++	mt7996_mcu_bss_sec_tlv(skb, mconf);
+ 
+ 	if (vif->type == NL80211_IFTYPE_MONITOR)
+ 		goto out;
+ 
+ 	if (enable) {
+-		mt7996_mcu_bss_rfch_tlv(skb, vif, phy);
+-		mt7996_mcu_bss_bmc_tlv(skb, vif, phy);
+-		mt7996_mcu_bss_ra_tlv(skb, vif, phy);
++		mt7996_mcu_bss_rfch_tlv(skb, phy);
++		mt7996_mcu_bss_bmc_tlv(skb, mconf, phy);
++		mt7996_mcu_bss_ra_tlv(skb);
+ 		mt7996_mcu_bss_txcmd_tlv(skb, true);
+-		mt7996_mcu_bss_ifs_timing_tlv(skb, vif);
++		mt7996_mcu_bss_ifs_timing_tlv(skb, phy);
+ 
+-		if (vif->bss_conf.he_support)
+-			mt7996_mcu_bss_he_tlv(skb, vif, phy);
++		if (conf->he_support)
++			mt7996_mcu_bss_he_tlv(skb, conf, phy);
+ 
+ 		/* this tag is necessary no matter if the vif is MLD */
+-		mt7996_mcu_bss_mld_tlv(skb, vif);
++		mt7996_mcu_bss_mld_tlv(skb, vif, mconf);
+ 	}
+ 
+-	mt7996_mcu_bss_mbssid_tlv(skb, vif, phy, enable);
++	mt7996_mcu_bss_mbssid_tlv(skb, conf, phy, enable);
+ 
+ out:
+ 	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ 				     MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
+ }
+ 
+-int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif)
++int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct mt7996_bss_conf *mconf)
+ {
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_dev *dev = phy->dev;
+ 	struct sk_buff *skb;
+ 
+-	skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
++	skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76,
+ 					 MT7996_BSS_UPDATE_MAX_SIZE);
+ 	if (IS_ERR(skb))
+ 		return PTR_ERR(skb);
+ 
+-	mt7996_mcu_bss_ifs_timing_tlv(skb, vif);
++	mt7996_mcu_bss_ifs_timing_tlv(skb, phy);
+ 
+ 	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ 				     MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
+@@ -1346,12 +1334,12 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ 			 bool enable)
+ {
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv;
+-	struct mt7996_vif *mvif = msta->vif;
++	struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
+ 
+ 	if (enable && !params->amsdu)
+ 		msta->wcid.amsdu = false;
+ 
+-	return mt7996_mcu_sta_ba(dev, &mvif->mt76, params, enable, true);
++	return mt7996_mcu_sta_ba(dev, &mconf->mt76, params, enable, true);
+ }
+ 
+ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
+@@ -1359,13 +1347,14 @@ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
+ 			 bool enable)
+ {
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv;
+-	struct mt7996_vif *mvif = msta->vif;
++	struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
+ 
+-	return mt7996_mcu_sta_ba(dev, &mvif->mt76, params, enable, false);
++	return mt7996_mcu_sta_ba(dev, &mconf->mt76, params, enable, false);
+ }
+ 
+ static void
+-mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
++mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
++		      struct mt7996_bss_conf *mconf,
+ 		      struct ieee80211_sta *sta)
+ {
+ 	struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
+@@ -1386,9 +1375,9 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ 		he->he_phy_cap[i] = elem->phy_cap_info[i];
+ 	}
+ 
+-	if (vif->type == NL80211_IFTYPE_AP &&
++	if (conf->vif->type == NL80211_IFTYPE_AP &&
+ 	    (elem->phy_cap_info[1] & IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD))
+-		u8p_replace_bits(&he->he_phy_cap[1], vif->bss_conf.he_ldpc,
++		u8p_replace_bits(&he->he_phy_cap[1], conf->he_ldpc,
+ 				 IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD);
+ 
+ 	mcs_map = sta->deflink.he_cap.he_mcs_nss_supp;
+@@ -1396,16 +1385,16 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ 	case IEEE80211_STA_RX_BW_160:
+ 		if (elem->phy_cap_info[0] &
+ 		    IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)
+-			mt7996_mcu_set_sta_he_mcs(sta,
++			mt7996_mcu_set_sta_he_mcs(sta, mconf,
+ 						  &he->max_nss_mcs[CMD_HE_MCS_BW8080],
+ 						  le16_to_cpu(mcs_map.rx_mcs_80p80));
+ 
+-		mt7996_mcu_set_sta_he_mcs(sta,
++		mt7996_mcu_set_sta_he_mcs(sta, mconf,
+ 					  &he->max_nss_mcs[CMD_HE_MCS_BW160],
+ 					  le16_to_cpu(mcs_map.rx_mcs_160));
+ 		fallthrough;
+ 	default:
+-		mt7996_mcu_set_sta_he_mcs(sta,
++		mt7996_mcu_set_sta_he_mcs(sta, mconf,
+ 					  &he->max_nss_mcs[CMD_HE_MCS_BW80],
+ 					  le16_to_cpu(mcs_map.rx_mcs_80));
+ 		break;
+@@ -1496,7 +1485,7 @@ mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ 	struct tlv *tlv;
+ #ifdef CONFIG_MTK_VENDOR
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+-	struct mt7996_phy *phy = (struct mt7996_phy *)msta->vif->phy;
++	struct mt7996_phy *phy = (struct mt7996_phy *)msta->vif->deflink.phy;
+ #endif
+ 
+ 	/* For 6G band, this tlv is necessary to let hw work normally */
+@@ -1553,25 +1542,26 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 
+ static void
+ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+-			struct ieee80211_vif *vif, struct ieee80211_sta *sta)
++			struct ieee80211_bss_conf *conf,
++			struct mt7996_bss_conf *mconf,
++			struct ieee80211_sta *sta)
+ {
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct mt7996_phy *phy = mvif->phy;
++	struct mt7996_phy *phy = mconf->phy;
+ 	struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
+ 	struct sta_rec_muru *muru;
+ 	struct tlv *tlv;
+ 
+-	if (vif->type != NL80211_IFTYPE_STATION &&
+-	    vif->type != NL80211_IFTYPE_AP)
++	if (conf->vif->type != NL80211_IFTYPE_STATION &&
++	    conf->vif->type != NL80211_IFTYPE_AP)
+ 		return;
+ 
+ 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MURU, sizeof(*muru));
+ 
+ 	muru = (struct sta_rec_muru *)tlv;
+-	muru->cfg.mimo_dl_en = (vif->bss_conf.eht_mu_beamformer ||
+-				vif->bss_conf.he_mu_beamformer ||
+-				vif->bss_conf.vht_mu_beamformer ||
+-				vif->bss_conf.vht_mu_beamformee) &&
++	muru->cfg.mimo_dl_en = (conf->eht_mu_beamformer ||
++				conf->he_mu_beamformer ||
++				conf->vht_mu_beamformer ||
++				conf->vht_mu_beamformee) &&
+ 			       !!(phy->muru_onoff & MUMIMO_DL);
+ 	muru->cfg.mimo_ul_en = !!(phy->muru_onoff & MUMIMO_UL);
+ 	muru->cfg.ofdma_dl_en = !!(phy->muru_onoff & OFDMA_DL);
+@@ -1612,13 +1602,14 @@ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ }
+ 
+ static inline bool
+-mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif,
++mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
++			struct mt7996_bss_conf *mconf,
+ 			struct ieee80211_sta *sta, bool bfee)
+ {
+ 	int sts = hweight16(phy->mt76->chainmask);
+ 
+-	if (vif->type != NL80211_IFTYPE_STATION &&
+-	    vif->type != NL80211_IFTYPE_AP)
++	if (conf->vif->type != NL80211_IFTYPE_STATION &&
++	    conf->vif->type != NL80211_IFTYPE_AP)
+ 		return false;
+ 
+ 	if (!bfee && sts < 2)
+@@ -1629,10 +1620,10 @@ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ 		struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem;
+ 
+ 		if (bfee)
+-			return vif->bss_conf.eht_su_beamformee &&
++			return conf->eht_su_beamformee &&
+ 			       EHT_PHY(CAP0_SU_BEAMFORMER, pe->phy_cap_info[0]);
+ 		else
+-			return vif->bss_conf.eht_su_beamformer &&
++			return conf->eht_su_beamformer &&
+ 			       EHT_PHY(CAP0_SU_BEAMFORMEE, pe->phy_cap_info[0]);
+ 	}
+ 
+@@ -1640,10 +1631,10 @@ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ 		struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem;
+ 
+ 		if (bfee)
+-			return vif->bss_conf.he_su_beamformee &&
++			return conf->he_su_beamformee &&
+ 			       HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]);
+ 		else
+-			return vif->bss_conf.he_su_beamformer &&
++			return conf->he_su_beamformer &&
+ 			       HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]);
+ 	}
+ 
+@@ -1651,10 +1642,10 @@ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ 		u32 cap = sta->deflink.vht_cap.cap;
+ 
+ 		if (bfee)
+-			return vif->bss_conf.vht_su_beamformee &&
++			return conf->vht_su_beamformee &&
+ 			       (cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE);
+ 		else
+-			return vif->bss_conf.vht_su_beamformer &&
++			return conf->vht_su_beamformer &&
+ 			       (cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE);
+ 	}
+ 
+@@ -1850,10 +1841,10 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+ 
+ static void
+ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+-			struct ieee80211_vif *vif, struct ieee80211_sta *sta)
++			struct ieee80211_bss_conf *conf, struct mt7996_bss_conf *mconf,
++			struct ieee80211_sta *sta)
+ {
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct mt7996_phy *phy = mvif->phy;
++	struct mt7996_phy *phy = mconf->phy;
+ 	int tx_ant = hweight16(phy->mt76->chainmask) - 1;
+ 	struct sta_rec_bf *bf;
+ 	struct tlv *tlv;
+@@ -1868,7 +1859,7 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 	if (!(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he))
+ 		return;
+ 
+-	ebf = mt7996_is_ebf_supported(phy, vif, sta, false);
++	ebf = mt7996_is_ebf_supported(phy, conf, mconf, sta, false);
+ 	if (!ebf && !dev->ibf)
+ 		return;
+ 
+@@ -1880,9 +1871,9 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 	 * ht: iBF only, since mac80211 lacks of eBF support
+ 	 */
+ 	if (sta->deflink.eht_cap.has_eht && ebf)
+-		mt7996_mcu_sta_bfer_eht(sta, vif, phy, bf);
++		mt7996_mcu_sta_bfer_eht(sta, conf->vif, phy, bf);
+ 	else if (sta->deflink.he_cap.has_he && ebf)
+-		mt7996_mcu_sta_bfer_he(sta, vif, phy, bf);
++		mt7996_mcu_sta_bfer_he(sta, conf->vif, phy, bf);
+ 	else if (sta->deflink.vht_cap.vht_supported)
+ 		mt7996_mcu_sta_bfer_vht(sta, phy, bf, ebf);
+ 	else if (sta->deflink.ht_cap.ht_supported)
+@@ -1921,10 +1912,10 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 
+ static void
+ mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+-			struct ieee80211_vif *vif, struct ieee80211_sta *sta)
++			struct ieee80211_bss_conf *conf,
++			struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta)
+ {
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct mt7996_phy *phy = mvif->phy;
++	struct mt7996_phy *phy = mconf->phy;
+ 	int tx_ant = hweight8(phy->mt76->antenna_mask) - 1;
+ 	struct sta_rec_bfee *bfee;
+ 	struct tlv *tlv;
+@@ -1933,7 +1924,7 @@ mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 	if (!(sta->deflink.vht_cap.vht_supported || sta->deflink.he_cap.has_he))
+ 		return;
+ 
+-	if (!mt7996_is_ebf_supported(phy, vif, sta, true))
++	if (!mt7996_is_ebf_supported(phy, conf, mconf, sta, true))
+ 		return;
+ 
+ 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BFEE, sizeof(*bfee));
+@@ -2055,17 +2046,17 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
+ 				     MCU_WM_UNI_CMD(RA), true);
+ }
+ 
+-int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
++			       struct mt7996_bss_conf *mconf,
+ 			       struct ieee80211_sta *sta, void *data, u32 field)
+ {
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	struct sta_phy_uni *phy = data;
+ 	struct sta_rec_ra_fixed_uni *ra;
+ 	struct sk_buff *skb;
+ 	struct tlv *tlv;
+ 
+-	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
++	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
+ 					      &msta->wcid,
+ 					      MT7996_STA_UPDATE_MAX_SIZE);
+ 	if (IS_ERR(skb))
+@@ -2097,12 +2088,13 @@ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif
+ }
+ 
+ static int
+-mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev,
++			       struct ieee80211_bss_conf *conf,
++			       struct mt7996_bss_conf *mconf,
+ 			       struct ieee80211_sta *sta)
+ {
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct cfg80211_chan_def *chandef = &mvif->phy->mt76->chandef;
+-	struct cfg80211_bitrate_mask *mask = &mvif->bitrate_mask;
++	struct cfg80211_chan_def *chandef = &mconf->phy->mt76->chandef;
++	struct cfg80211_bitrate_mask *mask = &mconf->bitrate_mask;
+ 	enum nl80211_band band = chandef->chan->band;
+ 	struct sta_phy_uni phy = {};
+ 	int ret, nrates = 0;
+@@ -2144,7 +2136,7 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif
+ 
+ 	/* fixed single rate */
+ 	if (nrates == 1) {
+-		ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy,
++		ret = mt7996_mcu_set_fixed_field(dev, mconf, sta, &phy,
+ 						 RATE_PARAM_FIXED_MCS);
+ 		if (ret)
+ 			return ret;
+@@ -2166,7 +2158,7 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif
+ 		else
+ 			mt76_rmw_field(dev, addr, GENMASK(15, 12), phy.sgi);
+ 
+-		ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy,
++		ret = mt7996_mcu_set_fixed_field(dev, mconf, sta, &phy,
+ 						 RATE_PARAM_FIXED_GI);
+ 		if (ret)
+ 			return ret;
+@@ -2174,7 +2166,7 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif
+ 
+ 	/* fixed HE_LTF */
+ 	if (mask->control[band].he_ltf != GENMASK(7, 0)) {
+-		ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy,
++		ret = mt7996_mcu_set_fixed_field(dev, mconf, sta, &phy,
+ 						 RATE_PARAM_FIXED_HE_LTF);
+ 		if (ret)
+ 			return ret;
+@@ -2185,13 +2177,14 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif
+ 
+ static void
+ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
+-			     struct ieee80211_vif *vif, struct ieee80211_sta *sta)
++			     struct ieee80211_bss_conf *conf,
++			     struct mt7996_bss_conf *mconf,
++			     struct ieee80211_sta *sta)
+ {
+ #define INIT_RCPI 180
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct mt76_phy *mphy = mvif->phy->mt76;
++	struct mt76_phy *mphy = mconf->phy->mt76;
+ 	struct cfg80211_chan_def *chandef = &mphy->chandef;
+-	struct cfg80211_bitrate_mask *mask = &mvif->bitrate_mask;
++	struct cfg80211_bitrate_mask *mask = &mconf->bitrate_mask;
+ 	enum nl80211_band band = chandef->chan->band;
+ 	struct sta_rec_ra_uni *ra;
+ 	struct tlv *tlv;
+@@ -2203,7 +2196,7 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
+ 
+ 	ra->valid = true;
+ 	ra->auto_rate = true;
+-	ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, &sta->deflink);
++	ra->phy_mode = mt76_connac_get_phy_mode(mphy, conf->vif, band, &sta->deflink);
+ 	ra->channel = chandef->chan->hw_value;
+ 	ra->bw = (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_320) ?
+ 		 CMD_CBW_320MHZ : sta->deflink.bandwidth;
+@@ -2242,7 +2235,7 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
+ 			cap |= STA_CAP_TX_STBC;
+ 		if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
+ 			cap |= STA_CAP_RX_STBC;
+-		if (vif->bss_conf.ht_ldpc &&
++		if (conf->ht_ldpc &&
+ 		    (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING))
+ 			cap |= STA_CAP_LDPC;
+ 
+@@ -2268,7 +2261,7 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
+ 			cap |= STA_CAP_VHT_TX_STBC;
+ 		if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1)
+ 			cap |= STA_CAP_VHT_RX_STBC;
+-		if (vif->bss_conf.vht_ldpc &&
++		if (conf->vht_ldpc &&
+ 		    (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC))
+ 			cap |= STA_CAP_VHT_LDPC;
+ 
+@@ -2289,15 +2282,16 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
+ 	memset(ra->rx_rcpi, INIT_RCPI, sizeof(ra->rx_rcpi));
+ }
+ 
+-int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
++			     struct ieee80211_bss_conf *conf,
++			     struct mt7996_bss_conf *mconf,
+ 			     struct ieee80211_sta *sta, bool changed)
+ {
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	struct sk_buff *skb;
+ 	int ret;
+ 
+-	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
++	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
+ 					      &msta->wcid,
+ 					      MT7996_STA_UPDATE_MAX_SIZE);
+ 	if (IS_ERR(skb))
+@@ -2308,26 +2302,27 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 	 * update sta_rec_he here.
+ 	 */
+ 	if (changed)
+-		mt7996_mcu_sta_he_tlv(skb, vif, sta);
++		mt7996_mcu_sta_he_tlv(skb, conf, mconf, sta);
+ 
+ 	/* sta_rec_ra accommodates BW, NSS and only MCS range format
+ 	 * i.e 0-{7,8,9} for VHT.
+ 	 */
+-	mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, vif, sta);
++	mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, conf, mconf, sta);
+ 
+ 	ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ 				    MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+ 	if (ret)
+ 		return ret;
+ 
+-	return mt7996_mcu_add_rate_ctrl_fixed(dev, vif, sta);
++	return mt7996_mcu_add_rate_ctrl_fixed(dev, conf, mconf, sta);
+ }
+ 
+ static int
+-mt7996_mcu_sta_init_vow(struct mt7996_phy *phy, struct mt7996_sta *msta)
++mt7996_mcu_sta_init_vow(struct mt7996_bss_conf *mconf, struct mt7996_sta *msta)
+ {
++	struct mt7996_phy *phy = mconf->phy;
+ 	struct mt7996_vow_sta_ctrl *vow = &msta->vow;
+-	u8 omac_idx = msta->vif->mt76.omac_idx;
++	u8 omac_idx = mconf->mt76.omac_idx;
+ 	int ret;
+ 
+ 	/* Assignment of STA BSS group index aligns FW.
+@@ -2344,20 +2339,22 @@ mt7996_mcu_sta_init_vow(struct mt7996_phy *phy, struct mt7996_sta *msta)
+ 	vow->drr_quantum[IEEE80211_AC_BE] = VOW_DRR_QUANTUM_IDX2;
+ 	vow->drr_quantum[IEEE80211_AC_BK] = VOW_DRR_QUANTUM_IDX2;
+ 
+-	ret = mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_BSS_GROUP);
++	ret = mt7996_mcu_set_vow_drr_ctrl(phy, mconf, msta, VOW_DRR_CTRL_STA_BSS_GROUP);
+ 	if (ret)
+ 		return ret;
+ 
+-	ret = mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_PAUSE);
++	ret = mt7996_mcu_set_vow_drr_ctrl(phy, mconf, msta, VOW_DRR_CTRL_STA_PAUSE);
+ 	if (ret)
+ 		return ret;
+ 
+-	return mt7996_mcu_set_vow_drr_ctrl(phy, msta, VOW_DRR_CTRL_STA_ALL);
++	return mt7996_mcu_set_vow_drr_ctrl(phy, mconf, msta, VOW_DRR_CTRL_STA_ALL);
+ }
+ 
+-int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+-		       struct ieee80211_sta *sta, bool enable, bool newly)
++int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
++		       struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta,
++		       bool enable, bool newly)
+ {
++	struct ieee80211_vif *vif = conf->vif;
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct ieee80211_link_sta *link_sta;
+ 	struct mt7996_sta *msta;
+@@ -2367,7 +2364,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 	msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta;
+ 	link_sta = sta ? &sta->deflink : NULL;
+ 
+-	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
++	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
+ 					      &msta->wcid,
+ 					      MT7996_STA_UPDATE_MAX_SIZE);
+ 	if (IS_ERR(skb))
+@@ -2390,7 +2387,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 		/* starec hdrt mode */
+ 		mt7996_mcu_sta_hdrt_tlv(dev, skb);
+ 		/* starec bfer */
+-		mt7996_mcu_sta_bfer_tlv(dev, skb, vif, sta);
++		mt7996_mcu_sta_bfer_tlv(dev, skb, conf, mconf, sta);
+ 		/* starec ht */
+ 		mt7996_mcu_sta_ht_tlv(skb, sta);
+ 		/* starec vht */
+@@ -2400,18 +2397,18 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 		/* starec amsdu */
+ 		mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, sta);
+ 		/* starec he */
+-		mt7996_mcu_sta_he_tlv(skb, vif, sta);
++		mt7996_mcu_sta_he_tlv(skb, conf, mconf, sta);
+ 		/* starec he 6g*/
+ 		mt7996_mcu_sta_he_6g_tlv(skb, sta);
+ 		/* starec eht */
+ 		mt7996_mcu_sta_eht_tlv(skb, sta);
+ 		/* starec muru */
+-		mt7996_mcu_sta_muru_tlv(dev, skb, vif, sta);
++		mt7996_mcu_sta_muru_tlv(dev, skb, conf, mconf, sta);
+ 		/* starec bfee */
+-		mt7996_mcu_sta_bfee_tlv(dev, skb, vif, sta);
++		mt7996_mcu_sta_bfee_tlv(dev, skb, conf, mconf, sta);
+ 	}
+ 
+-	ret = mt7996_mcu_sta_init_vow(mvif->phy, msta);
++	ret = mt7996_mcu_sta_init_vow(mconf, msta);
+ 	if (ret) {
+ 		dev_kfree_skb(skb);
+ 		return ret;
+@@ -2466,16 +2463,15 @@ mt7996_mcu_sta_key_tlv(struct mt76_wcid *wcid,
+ 	return 0;
+ }
+ 
+-int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_add_key(struct mt76_dev *dev, struct mt7996_bss_conf *mconf,
+ 		       struct ieee80211_key_conf *key, int mcu_cmd,
+ 		       struct mt76_wcid *wcid, enum set_key_cmd cmd)
+ {
+-	struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+ 	struct sk_buff *skb;
+ 	int ret;
+ 
+-	skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid,
+-					      MT7996_STA_UPDATE_MAX_SIZE);
++	skb = __mt76_connac_mcu_alloc_sta_req(dev, (struct mt76_vif *)mconf,
++					      wcid, MT7996_STA_UPDATE_MAX_SIZE);
+ 	if (IS_ERR(skb))
+ 		return PTR_ERR(skb);
+ 
+@@ -2486,17 +2482,18 @@ int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
+ 	return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true);
+ }
+ 
+-static int mt7996_mcu_get_pn(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+-			     u8 *pn)
++static int mt7996_mcu_get_pn(struct mt7996_dev *dev,
++			     struct ieee80211_bss_conf *conf,
++			     struct mt7996_bss_conf *mconf, u8 *pn)
+ {
+ #define TSC_TYPE_BIGTK_PN 2
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)conf->vif->drv_priv;
+ 	struct sta_rec_pn_info *pn_info;
+ 	struct sk_buff *skb, *rskb;
+ 	struct tlv *tlv;
+ 	int ret;
+ 
+-	skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, &mvif->sta.wcid);
++	skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76, &mvif->sta.wcid);
+ 	if (IS_ERR(skb))
+ 		return PTR_ERR(skb);
+ 
+@@ -2520,10 +2517,11 @@ static int mt7996_mcu_get_pn(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 	return 0;
+ }
+ 
+-int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev,
++			       struct ieee80211_bss_conf *conf,
++			       struct mt7996_bss_conf *mconf,
+ 			       struct ieee80211_key_conf *key)
+ {
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_mcu_bcn_prot_tlv *bcn_prot;
+ 	struct sk_buff *skb;
+ 	struct tlv *tlv;
+@@ -2532,7 +2530,7 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif
+ 		  sizeof(struct mt7996_mcu_bcn_prot_tlv);
+ 	int ret;
+ 
+-	skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76, len);
++	skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76, len);
+ 	if (IS_ERR(skb))
+ 		return PTR_ERR(skb);
+ 
+@@ -2540,7 +2538,7 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif
+ 
+ 	bcn_prot = (struct mt7996_mcu_bcn_prot_tlv *)tlv;
+ 
+-	ret = mt7996_mcu_get_pn(dev, vif, pn);
++	ret = mt7996_mcu_get_pn(dev, conf, mconf, pn);
+ 	if (ret) {
+ 		dev_kfree_skb(skb);
+ 		return ret;
+@@ -2573,10 +2571,10 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif
+ 				     MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
+ }
+ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
+-			    struct ieee80211_vif *vif, bool enable)
++			    struct ieee80211_bss_conf *conf,
++			    struct mt7996_bss_conf *mconf, bool enable)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct {
+ 		struct req_hdr {
+ 			u8 omac_idx;
+@@ -2592,8 +2590,8 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
+ 		} __packed tlv;
+ 	} data = {
+ 		.hdr = {
+-			.omac_idx = mvif->mt76.omac_idx,
+-			.band_idx = mvif->mt76.band_idx,
++			.omac_idx = mconf->mt76.omac_idx,
++			.band_idx = mconf->mt76.band_idx,
+ 		},
+ 		.tlv = {
+ 			.tag = cpu_to_le16(DEV_INFO_ACTIVE),
+@@ -2602,16 +2600,16 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
+ 		},
+ 	};
+ 
+-	if (mvif->mt76.omac_idx >= REPEATER_BSSID_START)
+-		return mt7996_mcu_muar_config(phy, vif, false, enable);
++	if (mconf->mt76.omac_idx >= REPEATER_BSSID_START)
++		return mt7996_mcu_muar_config(phy, conf, mconf, false, enable);
+ 
+-	memcpy(data.tlv.omac_addr, vif->addr, ETH_ALEN);
++	memcpy(data.tlv.omac_addr, conf->addr, ETH_ALEN);
+ 	return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(DEV_INFO_UPDATE),
+ 				 &data, sizeof(data), true);
+ }
+ 
+ static void
+-mt7996_mcu_beacon_cntdwn(struct ieee80211_vif *vif, struct sk_buff *rskb,
++mt7996_mcu_beacon_cntdwn(struct ieee80211_bss_conf *conf, struct sk_buff *rskb,
+ 			 struct sk_buff *skb,
+ 			 struct ieee80211_mutable_offsets *offs)
+ {
+@@ -2622,7 +2620,7 @@ mt7996_mcu_beacon_cntdwn(struct ieee80211_vif *vif, struct sk_buff *rskb,
+ 	if (!offs->cntdwn_counter_offs[0])
+ 		return;
+ 
+-	tag = vif->bss_conf.csa_active ? UNI_BSS_INFO_BCN_CSA : UNI_BSS_INFO_BCN_BCC;
++	tag = conf->csa_active ? UNI_BSS_INFO_BCN_CSA : UNI_BSS_INFO_BCN_BCC;
+ 
+ 	tlv = mt7996_mcu_add_uni_tlv(rskb, tag, sizeof(*info));
+ 
+@@ -2632,14 +2630,15 @@ mt7996_mcu_beacon_cntdwn(struct ieee80211_vif *vif, struct sk_buff *rskb,
+ 
+ static void
+ mt7996_mcu_beacon_mbss(struct sk_buff *rskb, struct sk_buff *skb,
+-		       struct ieee80211_vif *vif, struct bss_bcn_content_tlv *bcn,
++		       struct ieee80211_bss_conf *conf,
++		       struct bss_bcn_content_tlv *bcn,
+ 		       struct ieee80211_mutable_offsets *offs)
+ {
+ 	struct bss_bcn_mbss_tlv *mbss;
+ 	const struct element *elem;
+ 	struct tlv *tlv;
+ 
+-	if (!vif->bss_conf.bssid_indicator)
++	if (!conf->bssid_indicator)
+ 		return;
+ 
+ 	tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_MBSSID, sizeof(*mbss));
+@@ -2684,7 +2683,7 @@ mt7996_mcu_beacon_mbss(struct sk_buff *rskb, struct sk_buff *skb,
+ }
+ 
+ static void
+-mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ 		       struct sk_buff *rskb, struct sk_buff *skb,
+ 		       struct bss_bcn_content_tlv *bcn,
+ 		       struct ieee80211_mutable_offsets *offs)
+@@ -2698,9 +2697,9 @@ mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 	if (offs->cntdwn_counter_offs[0]) {
+ 		u16 offset = offs->cntdwn_counter_offs[0];
+ 
+-		if (vif->bss_conf.csa_active)
++		if (conf->csa_active)
+ 			bcn->csa_ie_pos = cpu_to_le16(offset - 4);
+-		if (vif->bss_conf.color_change_active)
++		if (conf->color_change_active)
+ 			bcn->bcc_ie_pos = cpu_to_le16(offset - 3);
+ 	}
+ 
+@@ -2712,11 +2711,11 @@ mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ }
+ 
+ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+-			  struct ieee80211_vif *vif, int en)
++			  struct ieee80211_bss_conf *conf,
++			  struct mt7996_bss_conf *mconf, int en)
+ {
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct ieee80211_mutable_offsets offs;
+ 	struct ieee80211_tx_info *info;
+ 	struct sk_buff *skb, *rskb;
+@@ -2724,15 +2723,15 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ 	struct bss_bcn_content_tlv *bcn;
+ 	int len;
+ 
+-	if (vif->bss_conf.nontransmitted)
++	if (conf->nontransmitted)
+ 		return 0;
+ 
+-	rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
++	rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76,
+ 					  MT7996_MAX_BSS_OFFLOAD_SIZE);
+ 	if (IS_ERR(rskb))
+ 		return PTR_ERR(rskb);
+ 
+-	skb = ieee80211_beacon_get_template(hw, vif, &offs, 0);
++	skb = ieee80211_beacon_get_template(hw, conf->vif, &offs, 0);
+ 	if (!skb) {
+ 		dev_kfree_skb(rskb);
+ 		return -EINVAL;
+@@ -2755,9 +2754,9 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ 	if (!en)
+ 		goto out;
+ 
+-	mt7996_mcu_beacon_cont(dev, vif, rskb, skb, bcn, &offs);
+-	mt7996_mcu_beacon_mbss(rskb, skb, vif, bcn, &offs);
+-	mt7996_mcu_beacon_cntdwn(vif, rskb, skb, &offs);
++	mt7996_mcu_beacon_cont(dev, conf, rskb, skb, bcn, &offs);
++	mt7996_mcu_beacon_mbss(rskb, skb, conf, bcn, &offs);
++	mt7996_mcu_beacon_cntdwn(conf, rskb, skb, &offs);
+ out:
+ 	dev_kfree_skb(skb);
+ 	return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb,
+@@ -2765,14 +2764,15 @@ out:
+ }
+ 
+ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+-				    struct ieee80211_vif *vif, u32 changed)
++				    struct ieee80211_bss_conf *conf,
++				    struct mt7996_bss_conf *mconf, u32 changed)
+ {
+ #define OFFLOAD_TX_MODE_SU	BIT(0)
+ #define OFFLOAD_TX_MODE_MU	BIT(1)
+ 	struct ieee80211_hw *hw = mt76_hw(dev);
++	struct ieee80211_vif *vif = conf->vif;
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct cfg80211_chan_def *chandef = &mvif->phy->mt76->chandef;
++	struct cfg80211_chan_def *chandef = &mconf->phy->mt76->chandef;
+ 	enum nl80211_band band = chandef->chan->band;
+ 	struct mt76_wcid *wcid = &dev->mt76.global_wcid;
+ 	struct bss_inband_discovery_tlv *discov;
+@@ -2782,20 +2782,20 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+ 	u8 *buf, interval;
+ 	int len;
+ 
+-	if (vif->bss_conf.nontransmitted)
++	if (conf->nontransmitted)
+ 		return 0;
+ 
+-	rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
++	rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76,
+ 					  MT7996_MAX_BSS_OFFLOAD_SIZE);
+ 	if (IS_ERR(rskb))
+ 		return PTR_ERR(rskb);
+ 
+ 	if (changed & BSS_CHANGED_FILS_DISCOVERY) {
+-		interval = vif->bss_conf.fils_discovery.max_interval;
++		interval = conf->fils_discovery.max_interval;
+ 		skb = ieee80211_get_fils_discovery_tmpl(hw, vif);
+ 	} else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP &&
+-		   vif->bss_conf.unsol_bcast_probe_resp_interval) {
+-		interval = vif->bss_conf.unsol_bcast_probe_resp_interval;
++		   conf->unsol_bcast_probe_resp_interval) {
++		interval = conf->unsol_bcast_probe_resp_interval;
+ 		skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif);
+ 	}
+ 
+@@ -3459,7 +3459,7 @@ int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans)
+ 				     MCU_WM_UNI_CMD(RX_HDR_TRANS), true);
+ }
+ 
+-int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif)
++int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf)
+ {
+ #define MCU_EDCA_AC_PARAM	0
+ #define WMM_AIFS_SET		BIT(0)
+@@ -3468,12 +3468,11 @@ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif)
+ #define WMM_TXOP_SET		BIT(3)
+ #define WMM_PARAM_SET		(WMM_AIFS_SET | WMM_CW_MIN_SET | \
+ 				 WMM_CW_MAX_SET | WMM_TXOP_SET)
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct {
+ 		u8 bss_idx;
+ 		u8 __rsv[3];
+ 	} __packed hdr = {
+-		.bss_idx = mvif->mt76.idx,
++		.bss_idx = mconf->mt76.idx,
+ 	};
+ 	struct sk_buff *skb;
+ 	int len = sizeof(hdr) + IEEE80211_NUM_ACS * sizeof(struct edca);
+@@ -3486,7 +3485,7 @@ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif)
+ 	skb_put_data(skb, &hdr, sizeof(hdr));
+ 
+ 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+-		struct ieee80211_tx_queue_params *q = &mvif->queue_params[ac];
++		struct ieee80211_tx_queue_params *q = &mconf->queue_params[ac];
+ 		struct edca *e;
+ 		struct tlv *tlv;
+ 
+@@ -4499,12 +4498,12 @@ mt7996_mcu_set_obss_spr_pd(struct mt7996_phy *phy,
+ }
+ 
+ static int
+-mt7996_mcu_set_obss_spr_siga(struct mt7996_phy *phy, struct ieee80211_vif *vif,
++mt7996_mcu_set_obss_spr_siga(struct mt7996_phy *phy,
++			     struct mt7996_bss_conf *mconf,
+ 			     struct ieee80211_he_obss_pd *he_obss_pd)
+ {
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_dev *dev = phy->dev;
+-	u8 omac = mvif->mt76.omac_idx;
++	u8 omac = mconf->mt76.omac_idx;
+ 	struct {
+ 		u8 band_idx;
+ 		u8 __rsv[3];
+@@ -4576,7 +4575,8 @@ mt7996_mcu_set_obss_spr_bitmap(struct mt7996_phy *phy,
+ 				 sizeof(req), true);
+ }
+ 
+-int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
++int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy,
++			    struct mt7996_bss_conf *mconf,
+ 			    struct ieee80211_he_obss_pd *he_obss_pd)
+ {
+ 	int ret;
+@@ -4610,7 +4610,7 @@ int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ 		return ret;
+ 
+ 	/* Set SR prohibit */
+-	ret = mt7996_mcu_set_obss_spr_siga(phy, vif, he_obss_pd);
++	ret = mt7996_mcu_set_obss_spr_siga(phy, mconf, he_obss_pd);
+ 	if (ret)
+ 		return ret;
+ 
+@@ -4618,16 +4618,16 @@ int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ 	return mt7996_mcu_set_obss_spr_bitmap(phy, he_obss_pd);
+ }
+ 
+-int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_update_bss_color(struct mt7996_dev *dev,
++				struct mt7996_bss_conf *mconf,
+ 				struct cfg80211_he_bss_color *he_bss_color)
+ {
+ 	int len = sizeof(struct bss_req_hdr) + sizeof(struct bss_color_tlv);
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct bss_color_tlv *bss_color;
+ 	struct sk_buff *skb;
+ 	struct tlv *tlv;
+ 
+-	skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76, len);
++	skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76, len);
+ 	if (IS_ERR(skb))
+ 		return PTR_ERR(skb);
+ 
+@@ -4646,7 +4646,7 @@ int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct ieee80211_vif *vi
+ #define TWT_AGRT_PROTECT	BIT(2)
+ 
+ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
+-			       struct mt7996_vif *mvif,
++			       struct mt7996_bss_conf *mconf,
+ 			       struct mt7996_twt_flow *flow,
+ 			       int cmd)
+ {
+@@ -4677,12 +4677,12 @@ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
+ 		.len = cpu_to_le16(sizeof(req) - 4),
+ 		.tbl_idx = flow->table_id,
+ 		.cmd = cmd,
+-		.own_mac_idx = mvif->mt76.omac_idx,
++		.own_mac_idx = mconf->mt76.omac_idx,
+ 		.flowid = flow->id,
+ 		.peer_id = cpu_to_le16(flow->wcid),
+ 		.duration = flow->duration,
+-		.bss = mvif->mt76.idx,
+-		.bss_idx = mvif->mt76.idx,
++		.bss = mconf->mt76.idx,
++		.bss_idx = mconf->mt76.idx,
+ 		.start_tsf = cpu_to_le64(flow->tsf),
+ 		.mantissa = flow->mantissa,
+ 		.exponent = flow->exp,
+@@ -4813,15 +4813,15 @@ int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev, bool disable
+ 
+ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ 				     struct ieee80211_vif *vif,
++				     struct mt7996_bss_conf *mconf,
+ 				     struct ieee80211_sta *sta)
+ {
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_sta *msta;
+ 	struct sk_buff *skb;
+ 
+-	msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta;
++	msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mconf->vif->sta;
+ 
+-	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
++	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
+ 					      &msta->wcid,
+ 					      MT7996_STA_UPDATE_MAX_SIZE);
+ 	if (IS_ERR(skb))
+@@ -5449,8 +5449,9 @@ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
+ 				 &req, sizeof(req), false);
+ }
+ 
+-int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
+-	                        enum vow_drr_ctrl_id id)
++int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy,
++				struct mt7996_bss_conf *mconf,
++				struct mt7996_sta *msta, enum vow_drr_ctrl_id id)
+ {
+ 	struct mt7996_vow_sta_ctrl *vow = msta ? &msta->vow : NULL;
+ 	u32 val = 0;
+@@ -5476,9 +5477,9 @@ int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
+ 		.len = cpu_to_le16(sizeof(req) - 4),
+ 		.wlan_idx = cpu_to_le16(msta ? msta->wcid.idx : 0),
+ 		.band_idx = phy->mt76->band_idx,
+-		.wmm_idx = msta ? msta->vif->mt76.wmm_idx : 0,
++		.wmm_idx = msta ? mconf->mt76.wmm_idx : 0,
+ 		.ctrl_id = cpu_to_le32(id),
+-		.omac_idx = msta ? msta->vif->mt76.omac_idx : 0
++		.omac_idx = msta ? mconf->mt76.omac_idx : 0
+ 	};
+ 
+ 	switch (id) {
+@@ -5666,7 +5667,7 @@ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ {
+ 	u8 mode, val;
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct mt7996_phy *phy =  mvif->phy;
++	struct mt7996_phy *phy =  mvif->deflink.phy;
+ 
+ 	mode = FIELD_GET(RATE_CFG_MODE, *((u32 *)data));
+ 	val = FIELD_GET(RATE_CFG_VAL, *((u32 *)data));
+@@ -5697,11 +5698,11 @@ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ {
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct ieee80211_hw *hw = mvif->phy->mt76->hw;
++	struct ieee80211_hw *hw = mvif->deflink.phy->mt76->hw;
+ 	u8 val = *((u8 *)data);
+ 
+ 	vif->bss_conf.enable_beacon = val;
+ 
+-	mt7996_mcu_add_beacon(hw, vif, val);
++	mt7996_mcu_add_beacon(hw, &vif->bss_conf, &mvif->deflink, val);
+ }
+ #endif
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 75073a1b..46964079 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -325,18 +325,25 @@ struct mt7996_sta {
+ 	struct mt7996_vow_sta_ctrl vow;
+ };
+ 
+-struct mt7996_vif {
++struct mt7996_bss_conf {
+ 	struct mt76_vif mt76; /* must be first */
+ 
+-	struct mt7996_sta sta;
++	struct mt7996_vif *vif;
+ 	struct mt7996_phy *phy;
+-
+ 	struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
+ 	struct cfg80211_bitrate_mask bitrate_mask;
+ 
+ 	struct mt7996_chanctx *chanctx;
+ };
+ 
++struct mt7996_vif {
++	struct mt7996_bss_conf deflink;
++	struct mt7996_bss_conf __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
++
++	struct mt7996_sta sta;
++	struct mt7996_dev *dev;
++};
++
+ /* crash-dump */
+ struct mt7996_crash_data {
+ 	guid_t guid;
+@@ -770,6 +777,13 @@ mt7996_chanctx_get(struct ieee80211_chanctx_conf *ctx)
+ 	return (struct mt7996_chanctx *)&ctx->drv_priv;
+ }
+ 
++static inline struct mt7996_bss_conf *
++mconf_dereference_protected(struct mt7996_vif *mvif, u8 link_id)
++{
++	return rcu_dereference_protected(mvif->link[link_id],
++					 lockdep_is_held(&mvif->dev->mt76.mutex));
++}
++
+ extern const struct ieee80211_ops mt7996_ops;
+ extern struct pci_driver mt7996_pci_driver;
+ extern struct pci_driver mt7996_hif_driver;
+@@ -780,7 +794,7 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
+ void mt7996_wfsys_reset(struct mt7996_dev *dev);
+ void mt7996_rro_hw_init(struct mt7996_dev *dev);
+ irqreturn_t mt7996_irq_handler(int irq, void *dev_instance);
+-u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
++u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_bss_conf *mconf);
+ int mt7996_register_device(struct mt7996_dev *dev);
+ void mt7996_unregister_device(struct mt7996_dev *dev);
+ const char *mt7996_eeprom_name(struct mt7996_dev *dev);
+@@ -806,37 +820,47 @@ int mt7996_run(struct ieee80211_hw *hw);
+ int mt7996_mcu_init(struct mt7996_dev *dev);
+ int mt7996_mcu_init_firmware(struct mt7996_dev *dev);
+ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
+-			       struct mt7996_vif *mvif,
++			       struct mt7996_bss_conf *mconf,
+ 			       struct mt7996_twt_flow *flow,
+ 			       int cmd);
+ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
+-			    struct ieee80211_vif *vif, bool enable);
++			    struct ieee80211_bss_conf *conf,
++			    struct mt7996_bss_conf *mconf, bool enable);
+ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
+-			    struct ieee80211_vif *vif, int enable);
+-int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+-		       struct ieee80211_sta *sta, bool enable, bool newly);
++			    struct ieee80211_bss_conf *conf,
++			    struct mt7996_bss_conf *mconf, int enable);
++int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
++		       struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta,
++		       bool enable, bool newly);
+ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ 			 struct ieee80211_ampdu_params *params,
+ 			 bool add);
+ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
+ 			 struct ieee80211_ampdu_params *params,
+ 			 bool add);
+-int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_update_bss_color(struct mt7996_dev *dev,
++				struct mt7996_bss_conf *mconf,
+ 				struct cfg80211_he_bss_color *he_bss_color);
+-int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+-			  int enable);
++int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
++			  struct ieee80211_bss_conf *conf,
++			  struct mt7996_bss_conf *mconf, int en);
+ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+-				    struct ieee80211_vif *vif, u32 changed);
+-int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
++				    struct ieee80211_bss_conf *conf,
++				    struct mt7996_bss_conf *mconf, u32 changed);
++int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy,
++			    struct mt7996_bss_conf *mconf,
+ 			    struct ieee80211_he_obss_pd *he_obss_pd);
+-int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
++			     struct ieee80211_bss_conf *conf,
++			     struct mt7996_bss_conf *mconf,
+ 			     struct ieee80211_sta *sta, bool changed);
+ int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef);
+ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag);
+-int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif);
++int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf);
+ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
+ 				   void *data, u16 version);
+-int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
++			       struct mt7996_bss_conf *mconf,
+ 			       struct ieee80211_sta *sta, void *data, u32 field);
+ int mt7996_mcu_set_eeprom(struct mt7996_dev *dev);
+ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf);
+@@ -851,7 +875,7 @@ int mt7996_mcu_set_radar_th(struct mt7996_dev *dev, int index,
+ 			    const struct mt7996_dfs_pattern *pattern);
+ int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable);
+ int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val);
+-int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif);
++int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct mt7996_bss_conf *mconf);
+ int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch);
+ int mt7996_mcu_get_temperature(struct mt7996_phy *phy);
+ int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state);
+@@ -891,8 +915,9 @@ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
+ void mt7996_mcu_scs_sta_poll(struct work_struct *work);
+ int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable);
+-int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy, struct mt7996_sta *msta,
+-	                        enum vow_drr_ctrl_id id);
++int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy,
++				struct mt7996_bss_conf *mconf,
++				struct mt7996_sta *msta, enum vow_drr_ctrl_id id);
+ int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy);
+ void mt7996_mcu_wmm_pbc_work(struct work_struct *work);
+ 
+@@ -1001,13 +1026,16 @@ void mt7996_update_channel(struct mt76_phy *mphy);
+ int mt7996_init_debugfs(struct mt7996_phy *phy);
+ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int len);
+ bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len);
+-int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_add_key(struct mt76_dev *dev, struct mt7996_bss_conf *mconf,
+ 		       struct ieee80211_key_conf *key, int mcu_cmd,
+ 		       struct mt76_wcid *wcid, enum set_key_cmd cmd);
+-int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev,
++			       struct ieee80211_bss_conf *conf,
++			       struct mt7996_bss_conf *mconf,
+ 			       struct ieee80211_key_conf *key);
+ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ 				     struct ieee80211_vif *vif,
++				     struct mt7996_bss_conf *mconf,
+ 				     struct ieee80211_sta *sta);
+ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode);
+ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap);
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index 784a8bea..bf55b430 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -223,6 +223,7 @@ static void
+ mt7996_tm_init(struct mt7996_phy *phy, bool en)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)phy->monitor_vif->drv_priv;
+ 	u8 rf_test_mode = en ? RF_OPER_RF_TEST : RF_OPER_NORMAL;
+ 
+ 	if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
+@@ -234,8 +235,8 @@ mt7996_tm_init(struct mt7996_phy *phy, bool en)
+ 
+ 	mt7996_tm_rf_switch_mode(dev, rf_test_mode);
+ 
+-	mt7996_mcu_add_bss_info(phy, phy->monitor_vif, en);
+-	mt7996_mcu_add_sta(dev, phy->monitor_vif, NULL, en, false);
++	mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, en);
++	mt7996_mcu_add_sta(dev, &phy->monitor_vif->bss_conf, &mvif->deflink, NULL, en, false);
+ 
+ 	mt7996_tm_set(dev, SET_ID(BAND_IDX), phy->mt76->band_idx);
+ 
+@@ -1179,13 +1180,13 @@ mt7996_tm_txbf_init(struct mt7996_phy *phy, u16 *val)
+ 	mt7996_tm_set_mac_addr(dev, td->addr[2], SET_ID(BSSID));
+ 
+ 	/* bss idx & omac idx should be set to band idx for ibf cal */
+-	mvif->mt76.idx = band_idx;
+-	dev->mt76.vif_mask |= BIT_ULL(mvif->mt76.idx);
+-	mvif->mt76.omac_idx = band_idx;
+-	phy->omac_mask |= BIT_ULL(mvif->mt76.omac_idx);
++	mvif->deflink.mt76.idx = band_idx;
++	dev->mt76.vif_mask |= BIT_ULL(mvif->deflink.mt76.idx);
++	mvif->deflink.mt76.omac_idx = band_idx;
++	phy->omac_mask |= BIT_ULL(mvif->deflink.mt76.omac_idx);
+ 
+-	mt7996_mcu_add_dev_info(phy, phy->monitor_vif, true);
+-	mt7996_mcu_add_bss_info(phy, phy->monitor_vif, true);
++	mt7996_mcu_add_dev_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, true);
++	mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, true);
+ 
+ 	if (td->ibf) {
+ 		if (td->is_txbf_dut) {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0090-wifi-mt76-mt7996-support-multi-link-sta-links-and-ML.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0090-wifi-mt76-mt7996-support-multi-link-sta-links-and-ML.patch
deleted file mode 100644
index b4ea4c0..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0090-wifi-mt76-mt7996-support-multi-link-sta-links-and-ML.patch
+++ /dev/null
@@ -1,604 +0,0 @@
-From 707513db7b37788147180eda7215eb23561d579f Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Wed, 29 Nov 2023 10:12:39 +0800
-Subject: [PATCH 090/116] wifi: mt76: mt7996: support multi-link sta links and
- MLO sta callbacks
-
-Rework add_sta functions to add_link_sta functions, and support
-.change_sta_links callback.
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt76_connac_mcu.h |   2 +
- mt7996/main.c     | 309 ++++++++++++++++++++++++++++++++++++----------
- mt7996/mcu.c      | 117 ++++++++++++++++++
- mt7996/mcu.h      |  29 +++++
- mt7996/mt7996.h   |   7 ++
- 5 files changed, 398 insertions(+), 66 deletions(-)
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 740b9f5..728ffb3 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -814,7 +814,9 @@ enum {
- 	STA_REC_HE_6G = 0x17,
- 	STA_REC_HE_V2 = 0x19,
- 	STA_REC_MLD = 0x20,
-+	STA_REC_EHT_MLD = 0x21,
- 	STA_REC_EHT = 0x22,
-+	STA_REC_MLD_TEARDOWN = 0x23,
- 	STA_REC_PN_INFO = 0x26,
- 	STA_REC_KEY_V3 = 0x27,
- 	STA_REC_HDRT = 0x28,
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 9fbb86c..37e065f 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -928,42 +928,234 @@ mt7996_channel_switch_beacon(struct ieee80211_hw *hw,
- 	mutex_unlock(&dev->mt76.mutex);
- }
- 
-+static void mt7996_remove_link_sta(struct mt7996_dev *dev,
-+				   struct ieee80211_bss_conf *conf,
-+				   struct mt7996_bss_conf *mconf,
-+				   struct ieee80211_link_sta *link_sta,
-+				   struct mt7996_link_sta *mlink)
-+{
-+	struct ieee80211_sta *sta = link_sta->sta;
-+	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	int i;
-+
-+	if (!mlink)
-+		return;
-+
-+	for (i = 0; i < ARRAY_SIZE(mlink->wcid.aggr); i++)
-+			mt76_rx_aggr_stop(&dev->mt76, &mlink->wcid, i);
-+
-+	if (sta->mlo)
-+		mt7996_mcu_teardown_mld_sta(dev, mconf, mlink);
-+	else
-+		mt7996_mcu_add_sta(dev, conf, mconf, link_sta, mlink, false, false);
-+
-+	mt7996_mac_wtbl_update(dev, mlink->wcid.idx,
-+			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
-+
-+	for (i = 0; i < ARRAY_SIZE(mlink->twt.flow); i++)
-+		mt7996_mac_twt_teardown_flow(dev, mlink, i);
-+
-+	rcu_assign_pointer(mlink->sta->link[mlink->wcid.link_id], NULL);
-+
-+	spin_lock_bh(&dev->mt76.sta_poll_lock);
-+	if (!list_empty(&mlink->wcid.poll_list))
-+		list_del_init(&mlink->wcid.poll_list);
-+	if (!list_empty(&mlink->rc_list))
-+		list_del_init(&mlink->rc_list);
-+	spin_unlock_bh(&dev->mt76.sta_poll_lock);
-+
-+	/* TODO: update primary link */
-+	if (sta->valid_links) {
-+		if (mlink->wcid.link_id == msta->pri_link)
-+			msta->pri_link = msta->sec_link;
-+
-+		if (sta->valid_links & ~(BIT(msta->pri_link)))
-+			msta->sec_link = __ffs(sta->valid_links & ~(BIT(msta->pri_link)));
-+		else
-+			msta->sec_link = msta->pri_link;
-+	}
-+
-+	mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
-+	mt76_wcid_mask_clear(dev->mt76.wcid_mask, mlink->wcid.idx);
-+	mt76_wcid_mask_clear(dev->mt76.wcid_phy_mask, mlink->wcid.idx);
-+
-+	if (mlink != &msta->deflink)
-+		kfree(mlink);
-+}
-+
-+static int mt7996_add_link_sta(struct mt7996_dev *dev,
-+			       struct ieee80211_bss_conf *conf,
-+			       struct mt7996_bss_conf *mconf,
-+			       struct ieee80211_link_sta *link_sta, bool assoc)
-+{
-+	struct ieee80211_sta *sta = link_sta->sta;
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)conf->vif->drv_priv;
-+	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	u8 link_id = link_sta->link_id;
-+	struct mt7996_link_sta *mlink = NULL;
-+	int idx, ret;
-+
-+	if (!rcu_access_pointer(msta->link[link_id])) {
-+		idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
-+		if (idx < 0)
-+			return -ENOSPC;
-+
-+		if (sta->mlo) {
-+			mlink = kzalloc(sizeof(*mlink), GFP_KERNEL);
-+			if (!mlink)
-+				return -ENOMEM;
-+		} else {
-+			mlink = &msta->deflink;
-+		}
-+
-+		INIT_LIST_HEAD(&mlink->rc_list);
-+		INIT_LIST_HEAD(&mlink->wcid.poll_list);
-+		msta->vif = mvif;
-+		mlink->wcid.sta = 1;
-+		mlink->wcid.idx = idx;
-+		mlink->wcid.phy_idx = mconf->phy->mt76->band_idx;
-+		mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
-+		mlink->wcid.def_wcid = &msta->deflink.wcid;
-+		mlink->sta = msta;
-+		if (sta->valid_links) {
-+			mlink->wcid.link_valid = true;
-+			mlink->wcid.link_id = link_id;
-+			if (sta->valid_links & ~(BIT(msta->pri_link)))
-+				msta->sec_link = __ffs(sta->valid_links &
-+						       ~(BIT(msta->pri_link)));
-+			else
-+				msta->sec_link = msta->pri_link;
-+		}
-+
-+		rcu_assign_pointer(msta->link[link_id], mlink);
-+
-+		ewma_signal_init(&mlink->wcid.rssi);
-+		if (mconf->phy->mt76->band_idx == MT_BAND1)
-+			mt76_wcid_mask_set(dev->mt76.wcid_phy_mask, idx);
-+		rcu_assign_pointer(dev->mt76.wcid[idx], &mlink->wcid);
-+		mt76_wcid_init(&mlink->wcid);
-+	}
-+
-+	if (!assoc)
-+		return 0;
-+
-+	if (!mlink)
-+		mlink = mlink_dereference_protected(msta, link_id);
-+	mt7996_mac_wtbl_update(dev, mlink->wcid.idx,
-+			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
-+
-+	ret = mt7996_mcu_add_sta(dev, conf, mconf, link_sta, mlink, true, true);
-+	if (ret)
-+		goto error;
-+
-+	ret = mt7996_mcu_add_rate_ctrl(dev, conf, mconf, link_sta, mlink, false);
-+	if (ret)
-+		goto error;
-+
-+	ewma_avg_signal_init(&mlink->avg_ack_signal);
-+
-+	return 0;
-+error:
-+	mt7996_remove_link_sta(dev, conf, mconf, link_sta, mlink);
-+	return ret;
-+}
-+
-+static void
-+mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
-+			    struct ieee80211_sta *sta, unsigned long rem)
-+{
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	unsigned int link_id;
-+
-+	for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
-+		struct mt7996_bss_conf *mconf =
-+			mconf_dereference_protected(mvif, link_id);
-+		struct mt7996_link_sta *mlink =
-+			mlink_dereference_protected(msta, link_id);
-+		struct ieee80211_bss_conf *conf =
-+			link_conf_dereference_protected(vif, link_id);
-+		struct ieee80211_link_sta *link_sta =
-+			link_sta_dereference_protected(sta, link_id);
-+
-+		mt7996_remove_link_sta(dev, conf, mconf, link_sta, mlink);
-+	}
-+}
-+
-+static int
-+mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
-+			 struct ieee80211_sta *sta, unsigned long add,
-+			 bool assoc)
-+{
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_link_sta *mlink;
-+	unsigned int link_id;
-+	int i, ret;
-+
-+	for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
-+		struct mt7996_bss_conf *mconf =
-+			mconf_dereference_protected(mvif, link_id);
-+		struct ieee80211_bss_conf *conf =
-+			link_conf_dereference_protected(vif, link_id);
-+		struct ieee80211_link_sta *link_sta =
-+			link_sta_dereference_protected(sta, link_id);
-+
-+		ret = mt7996_add_link_sta(dev, conf, mconf, link_sta, assoc);
-+		if (ret)
-+			goto error;
-+	}
-+
-+	if (!assoc)
-+		return 0;
-+
-+	mlink = mlink_dereference_protected(msta, msta->pri_link);
-+	for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
-+		struct mt76_txq *mtxq;
-+
-+		if (!sta->txq[i])
-+			continue;
-+		mtxq = (struct mt76_txq *)sta->txq[i]->drv_priv;
-+		mtxq->wcid = mlink->wcid.idx;
-+	}
-+
-+	ret = mt7996_mcu_add_mld_sta(dev, vif, sta, add);
-+	if (ret)
-+		goto error;
-+
-+	return 0;
-+error:
-+	mt7996_mac_sta_remove_links(dev, vif, sta, add);
-+	return ret;
-+}
-+
- int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 		       struct ieee80211_sta *sta)
- {
- 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
--	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	struct mt7996_bss_conf *mconf = mconf_dereference_protected(mvif, 0);
--	struct mt7996_link_sta *mlink = &msta->deflink;
--	u8 band_idx = mconf->phy->mt76->band_idx;
--	int idx;
-+	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_bss_conf *mconf;
-+	u8 link_id = sta->valid_links ? __ffs(sta->valid_links) : 0;
-+	unsigned long add = BIT(link_id);
-+	int ret;
- 
- #ifdef CONFIG_MTK_VENDOR
- 	struct mt7996_phy *phy = &dev->phy;
- #endif
- 
--	idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
--	if (idx < 0)
--		return -ENOSPC;
--
--	INIT_LIST_HEAD(&mlink->rc_list);
--	INIT_LIST_HEAD(&mlink->wcid.poll_list);
--	msta->vif = mvif;
--	mlink->wcid.sta = 1;
--	mlink->wcid.idx = idx;
--	mlink->wcid.phy_idx = band_idx;
--	mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
--	mlink->sta = msta;
--
--	rcu_assign_pointer(msta->link[0], mlink);
-+	msta->pri_link = link_id;
-+	ret = mt7996_mac_sta_add_links(dev, vif, sta, add, false);
-+	if (ret)
-+		return ret;
- 
- #ifdef CONFIG_MTK_VENDOR
-+	mconf = mconf_dereference_protected(mvif, link_id);
- 	mt7996_vendor_amnt_sta_remove(mconf->phy, sta);
- #endif
- 
- #ifdef CONFIG_MTK_VENDOR
--	switch (band_idx) {
-+	switch (mconf->phy->mt76->band_idx) {
- 	case MT_BAND1:
- 		phy = mt7996_phy2(dev);
- 		break;
-@@ -986,28 +1178,11 @@ void mt7996_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 			  struct ieee80211_sta *sta)
- {
- 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
--	struct mt7996_bss_conf *mconf;
--	struct mt7996_link_sta *mlink;
--	struct ieee80211_bss_conf *conf;
--	struct ieee80211_link_sta *link_sta;
-+	unsigned long add = sta->valid_links ?: BIT(0);
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
--	conf = link_conf_dereference_protected(vif, 0);
--	mconf = mconf_dereference_protected(mvif, 0);
--	link_sta = link_sta_dereference_protected(sta, 0);
--	mlink = mlink_dereference_protected(msta, 0);
--
--	mt7996_mac_wtbl_update(dev, mlink->wcid.idx,
--			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
--
--	mt7996_mcu_add_sta(dev, conf, mconf, link_sta, mlink, true, true);
--	mt7996_mcu_add_rate_ctrl(dev, conf, mconf, link_sta, mlink, false);
--	mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
--
--	ewma_avg_signal_init(&mlink->avg_ack_signal);
-+	mt7996_mac_sta_add_links(dev, vif, sta, add, true);
- 
- 	mutex_unlock(&dev->mt76.mutex);
- }
-@@ -1016,34 +1191,9 @@ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 			   struct ieee80211_sta *sta)
- {
- 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
--	struct mt7996_bss_conf *mconf;
--	struct mt7996_link_sta *mlink;
--	struct ieee80211_bss_conf *conf;
--	struct ieee80211_link_sta *link_sta;
--	int i;
--
--	conf = link_conf_dereference_protected(vif, 0);
--	mconf = mconf_dereference_protected(mvif, 0);
--	link_sta = link_sta_dereference_protected(sta, 0);
--	mlink = mlink_dereference_protected(msta, 0);
--	mt7996_mcu_add_sta(dev, conf, mconf, link_sta, mlink, false, false);
--
--	mt7996_mac_wtbl_update(dev, mlink->wcid.idx,
--			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
--
--	for (i = 0; i < ARRAY_SIZE(mlink->twt.flow); i++)
--		mt7996_mac_twt_teardown_flow(dev, mlink, i);
-+	unsigned long rem = sta->valid_links ?: BIT(0);
- 
--	spin_lock_bh(&mdev->sta_poll_lock);
--	if (!list_empty(&mlink->wcid.poll_list))
--		list_del_init(&mlink->wcid.poll_list);
--	if (!list_empty(&mlink->rc_list))
--		list_del_init(&mlink->rc_list);
--	spin_unlock_bh(&mdev->sta_poll_lock);
--
--	rcu_assign_pointer(msta->link[0], NULL);
-+	mt7996_mac_sta_remove_links(dev, vif, sta, rem);
- }
- 
- static void mt7996_tx(struct ieee80211_hw *hw,
-@@ -2161,6 +2311,32 @@ mt7996_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	return ret;
- }
- 
-+static int
-+mt7996_change_sta_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-+			struct ieee80211_sta *sta, u16 old_links, u16 new_links)
-+{
-+	struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+	unsigned long add = new_links & ~old_links;
-+	unsigned long rem = old_links & ~new_links;
-+	int ret = 0;
-+
-+	mutex_lock(&dev->mt76.mutex);
-+
-+	if (rem)
-+		mt7996_mac_sta_remove_links(dev, vif, sta, rem);
-+
-+	ret = mt7996_mac_sta_add_links(dev, vif, sta, add, false);
-+	if (ret)
-+		goto remove;
-+
-+	goto out;
-+remove:
-+	mt7996_mac_sta_remove_links(dev, vif, sta, add);
-+out:
-+	mutex_unlock(&dev->mt76.mutex);
-+	return ret;
-+}
-+
- const struct ieee80211_ops mt7996_ops = {
- 	.tx = mt7996_tx,
- 	.start = mt7996_start,
-@@ -2218,4 +2394,5 @@ const struct ieee80211_ops mt7996_ops = {
- 	.unassign_vif_chanctx = mt7996_unassign_vif_chanctx,
- 	.switch_vif_chanctx = mt7996_switch_vif_chanctx,
- 	.change_vif_links = mt7996_change_vif_links,
-+	.change_sta_links = mt7996_change_sta_links,
- };
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 2a17299..16352d1 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -2425,6 +2425,123 @@ out:
- 				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
- }
- 
-+static void
-+mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
-+			     struct ieee80211_sta *sta)
-+{
-+	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct sta_rec_mld_setup *mld_setup;
-+	struct mld_setup_link *mld_setup_link;
-+	struct mt7996_link_sta *mlink;
-+	struct mt7996_bss_conf *mconf;
-+	struct tlv *tlv;
-+	unsigned long valid_links = sta->valid_links;
-+	unsigned int link_id;
-+
-+	mlink = mlink_dereference_protected(msta, msta->pri_link);
-+	if (!mlink)
-+		return;
-+
-+	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MLD,
-+				      sizeof(*mld_setup) +
-+				      sizeof(struct mld_setup_link) *
-+					     hweight16(sta->valid_links));
-+
-+	mld_setup = (struct sta_rec_mld_setup *)tlv;
-+	memcpy(mld_setup->mld_addr, sta->addr, ETH_ALEN);
-+	mld_setup->setup_wcid = cpu_to_le16(mlink->wcid.idx);
-+	mld_setup->primary_id = cpu_to_le16(mlink->wcid.idx);
-+	if (msta->sec_link != msta->pri_link) {
-+		mlink = mlink_dereference_protected(msta, msta->sec_link);
-+		if (!mlink)
-+			return;
-+	}
-+	mld_setup->seconed_id = cpu_to_le16(mlink->wcid.idx);
-+	mld_setup->link_num = hweight16(sta->valid_links);
-+
-+	mld_setup_link = (struct mld_setup_link *)mld_setup->link_info;
-+	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
-+		mlink = mlink_dereference_protected(msta, link_id);
-+		mconf = mconf_dereference_protected(msta->vif, link_id);
-+
-+		mld_setup_link->wcid = cpu_to_le16(mlink->wcid.idx);
-+		mld_setup_link->bss_idx = mconf->mt76.idx;
-+		mld_setup_link++;
-+	}
-+}
-+
-+static void
-+mt7996_mcu_sta_eht_mld_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
-+			   struct ieee80211_sta *sta)
-+{
-+	struct sta_rec_eht_mld *eht_mld;
-+	struct tlv *tlv;
-+	int i;
-+
-+	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT_MLD, sizeof(*eht_mld));
-+	eht_mld = (struct sta_rec_eht_mld *)tlv;
-+
-+	for (i = 0; i < ARRAY_SIZE(eht_mld->str_cap); i++)
-+		eht_mld->str_cap[i] = 0x7;
-+	/* TODO:
-+	eht_mld->nsep = ;
-+	eht_mld->eml_cap = cpu_to_le16()
-+	*/
-+}
-+
-+int mt7996_mcu_add_mld_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
-+			   struct ieee80211_sta *sta, unsigned long add)
-+{
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	unsigned int link_id;
-+
-+	if (!sta->mlo)
-+		return 0;
-+
-+	for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
-+		struct mt7996_bss_conf *mconf =
-+			mconf_dereference_protected(mvif, link_id);
-+		struct mt7996_link_sta *mlink =
-+			mlink_dereference_protected(msta, link_id);
-+		struct sk_buff *skb;
-+		int ret;
-+
-+		skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
-+						      &mlink->wcid,
-+						      MT7996_STA_UPDATE_MAX_SIZE);
-+		if (IS_ERR(skb))
-+			return PTR_ERR(skb);
-+		/* starec mld setup */
-+		mt7996_mcu_sta_mld_setup_tlv(dev, skb, sta);
-+		/* starec eht mld */
-+		mt7996_mcu_sta_eht_mld_tlv(dev, skb, sta);
-+		ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
-+					    MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
-+		if (ret)
-+			return ret;
-+	}
-+	return 0;
-+}
-+int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev,
-+				struct mt7996_bss_conf *mconf,
-+				struct mt7996_link_sta *mlink)
-+{
-+	struct sk_buff *skb;
-+
-+	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76,
-+					      &mconf->mt76,
-+					      &mlink->wcid,
-+					      MT7996_STA_UPDATE_MAX_SIZE);
-+	if (IS_ERR(skb))
-+		return PTR_ERR(skb);
-+
-+	mt76_connac_mcu_add_tlv(skb, STA_REC_MLD_TEARDOWN, sizeof(struct tlv));
-+
-+	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
-+				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
-+}
-+
- static int
- mt7996_mcu_sta_key_tlv(struct mt76_wcid *wcid,
- 		       struct sk_buff *skb,
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 814072e..ee36cf5 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -698,6 +698,35 @@ struct sta_rec_hdr_trans {
- 	u8 mesh;
- } __packed;
- 
-+struct sta_rec_mld_setup {
-+	__le16 tag;
-+	__le16 len;
-+	u8 mld_addr[ETH_ALEN];
-+	__le16 primary_id;
-+	__le16 seconed_id;
-+	__le16 setup_wcid;
-+	u8 link_num;
-+	u8 info;
-+	u8 __rsv[2];
-+	u8 link_info[];
-+} __packed;
-+
-+struct mld_setup_link {
-+	__le16 wcid;
-+	u8 bss_idx;
-+	u8 __rsv[1];
-+} __packed;
-+
-+struct sta_rec_eht_mld {
-+	__le16 tag;
-+	__le16 len;
-+	u8 nsep;
-+	u8 __rsv1[2];
-+	u8 str_cap[__MT_MAX_BAND];
-+	__le16 eml_cap;
-+	u8 __rsv2[4];
-+} __packed;
-+
- struct hdr_trans_en {
- 	__le16 tag;
- 	__le16 len;
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index ed8c82e..913aff1 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -329,6 +329,8 @@ struct mt7996_sta {
- 	struct mt7996_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
- 
- 	struct mt7996_vif *vif;
-+	u8 pri_link;
-+	u8 sec_link;
- };
- 
- struct mt7996_bss_conf {
-@@ -856,6 +858,9 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
- 		       struct mt7996_bss_conf *mconf,
- 		       struct ieee80211_link_sta *link_sta,
- 		       struct mt7996_link_sta *mlink, bool enable, bool newly);
-+int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev,
-+				struct mt7996_bss_conf *mconf,
-+				struct mt7996_link_sta *mlink);
- int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
- 			 struct ieee80211_ampdu_params *params,
- 			 bool add);
-@@ -879,6 +884,8 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
- 			     struct mt7996_bss_conf *mconf,
- 			     struct ieee80211_link_sta *link_sta,
- 			     struct mt7996_link_sta *mlink, bool changed);
-+int mt7996_mcu_add_mld_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
-+			   struct ieee80211_sta *sta, unsigned long add);
- int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef);
- int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag);
- int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0091-mtk-mt76-mt7996-switch-to-per-link-data-structure-of.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0091-mtk-mt76-mt7996-switch-to-per-link-data-structure-of.patch
new file mode 100644
index 0000000..8dcbc38
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0091-mtk-mt76-mt7996-switch-to-per-link-data-structure-of.patch
@@ -0,0 +1,2279 @@
+From bb417654387169098f02664031be303d1f1a7f16 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 27 Nov 2023 10:43:34 +0800
+Subject: [PATCH 091/199] mtk: mt76: mt7996: switch to per-link data structure
+ of sta
+
+Introduce struct mt7996_link_sta, data structure for per-link STA.
+Note that mt7996_sta now represents a peer legacy or MLD device.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/debugfs.c  |  20 ++-
+ mt7996/mac.c      | 109 ++++++------
+ mt7996/main.c     | 223 +++++++++++++++---------
+ mt7996/mcu.c      | 430 ++++++++++++++++++++++++----------------------
+ mt7996/mt7996.h   |  44 +++--
+ mt7996/testmode.c |   6 +-
+ 6 files changed, 461 insertions(+), 371 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 5d4d69ea..62658dbc 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -726,14 +726,15 @@ static void
+ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta)
+ {
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_link_sta *mlink = &msta->deflink;
+ 	struct mt7996_dev *dev = msta->vif->deflink.phy->dev;
+ 	struct seq_file *s = data;
+ 	u8 ac;
+ 
+ 	for (ac = 0; ac < 4; ac++) {
+ 		u32 qlen, ctrl, val;
+-		u32 idx = msta->wcid.idx >> 5;
+-		u8 offs = msta->wcid.idx & GENMASK(4, 0);
++		u32 idx = mlink->wcid.idx >> 5;
++		u8 offs = mlink->wcid.idx & GENMASK(4, 0);
+ 
+ 		ctrl = BIT(31) | BIT(11) | (ac << 24);
+ 		val = mt76_rr(dev, MT_PLE_AC_QEMPTY(ac, idx));
+@@ -741,11 +742,11 @@ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta)
+ 		if (val & BIT(offs))
+ 			continue;
+ 
+-		mt76_wr(dev, MT_FL_Q0_CTRL, ctrl | msta->wcid.idx);
++		mt76_wr(dev, MT_FL_Q0_CTRL, ctrl | mlink->wcid.idx);
+ 		qlen = mt76_get_field(dev, MT_FL_Q3_CTRL,
+ 				      GENMASK(11, 0));
+ 		seq_printf(s, "\tSTA %pM wcid %d: AC%d%d queued:%d\n",
+-			   sta->addr, msta->wcid.idx,
++			   sta->addr, mlink->wcid.idx,
+ 			   msta->vif->deflink.mt76.wmm_idx, ac, qlen);
+ 	}
+ }
+@@ -1028,7 +1029,7 @@ mt7996_airtime_read(struct seq_file *s, void *data)
+ 	struct mt76_dev *mdev = &dev->mt76;
+ 	struct mt76_sta_stats *stats;
+ 	struct ieee80211_sta *sta;
+-	struct mt7996_sta *msta;
++	struct mt7996_link_sta *mlink;
+ 	struct mt76_wcid *wcid;
+ 	struct mt76_vif *vif;
+ 	u16 i;
+@@ -1040,9 +1041,9 @@ mt7996_airtime_read(struct seq_file *s, void *data)
+ 		if (!wcid || !wcid->sta)
+ 			continue;
+ 
+-		msta = container_of(wcid, struct mt7996_sta, wcid);
+-		sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
+-		vif = &msta->vif->deflink.mt76;
++		mlink = container_of(wcid, struct mt7996_link_sta, wcid);
++		sta = container_of((void *)mlink->sta, struct ieee80211_sta, drv_priv);
++		vif = &mlink->sta->vif->deflink.mt76;
+ 		stats = &wcid->stats;
+ 
+ 		seq_printf(s, "%pM WCID: %hu BandIdx: %hhu OmacIdx: 0x%hhx\t"
+@@ -1217,6 +1218,7 @@ static ssize_t mt7996_sta_fixed_rate_set(struct file *file,
+ #define LONG_PREAMBLE 1
+ 	struct ieee80211_sta *sta = file->private_data;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_link_sta *mlink = &msta->deflink;
+ 	struct mt7996_dev *dev = msta->vif->deflink.phy->dev;
+ 	struct ra_rate phy = {};
+ 	char buf[100];
+@@ -1252,7 +1254,7 @@ static ssize_t mt7996_sta_fixed_rate_set(struct file *file,
+ 		goto out;
+ 	}
+ 
+-	phy.wlan_idx = cpu_to_le16(msta->wcid.idx);
++	phy.wlan_idx = cpu_to_le16(mlink->wcid.idx);
+ 	phy.gi = cpu_to_le16(gi);
+ 	phy.ltf = cpu_to_le16(ltf);
+ 	phy.ldpc = phy.ldpc ? 7 : 0;
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 5fbcfc74..fbe6c0bd 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -54,7 +54,7 @@ static const struct mt7996_dfs_radar_spec jp_radar_specs = {
+ static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev,
+ 					    u16 idx, bool unicast)
+ {
+-	struct mt7996_sta *sta;
++	struct mt7996_link_sta *mlink;
+ 	struct mt76_wcid *wcid;
+ 
+ 	if (idx >= ARRAY_SIZE(dev->mt76.wcid))
+@@ -67,11 +67,11 @@ static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev,
+ 	if (!wcid->sta)
+ 		return NULL;
+ 
+-	sta = container_of(wcid, struct mt7996_sta, wcid);
+-	if (!sta->vif)
++	mlink = container_of(wcid, struct mt7996_link_sta, wcid);
++	if (!mlink->sta->vif)
+ 		return NULL;
+ 
+-	return &sta->vif->sta.wcid;
++	return &mlink->wcid;
+ }
+ 
+ bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask)
+@@ -92,12 +92,11 @@ u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw)
+ }
+ 
+ void mt7996_mac_enable_rtscts(struct mt7996_dev *dev,
+-			      struct ieee80211_vif *vif, bool enable)
++			      struct mt7996_link_sta *mlink, bool enable)
+ {
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	u32 addr;
+ 
+-	addr = mt7996_mac_wtbl_lmac_addr(dev, mvif->sta.wcid.idx, 5);
++	addr = mt7996_mac_wtbl_lmac_addr(dev, mlink->wcid.idx, 5);
+ 	if (enable)
+ 		mt76_set(dev, addr, BIT(5));
+ 	else
+@@ -349,7 +348,7 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ 	__le16 fc = 0;
+ 	int idx;
+ 	u8 hw_aggr = false;
+-	struct mt7996_sta *msta = NULL;
++	struct mt7996_link_sta *mlink = NULL;
+ 
+ 	hw_aggr = status->aggr;
+ 	memset(status, 0, sizeof(*status));
+@@ -378,10 +377,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ 	status->wcid = mt7996_rx_get_wcid(dev, idx, unicast);
+ 
+ 	if (status->wcid) {
+-		msta = container_of(status->wcid, struct mt7996_sta, wcid);
++		mlink = container_of(status->wcid, struct mt7996_link_sta, wcid);
+ 		spin_lock_bh(&dev->mt76.sta_poll_lock);
+-		if (list_empty(&msta->wcid.poll_list))
+-			list_add_tail(&msta->wcid.poll_list,
++		if (list_empty(&mlink->wcid.poll_list))
++			list_add_tail(&mlink->wcid.poll_list,
+ 				      &dev->mt76.sta_poll_list);
+ 		spin_unlock_bh(&dev->mt76.sta_poll_lock);
+ 	}
+@@ -586,7 +585,7 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ #endif
+ 	} else {
+ 		status->flag |= RX_FLAG_8023;
+-		mt7996_wed_check_ppe(dev, &dev->mt76.q_rx[q], msta, skb,
++		mt7996_wed_check_ppe(dev, &dev->mt76.q_rx[q], mlink ? mlink->sta : NULL, skb,
+ 				     *info);
+ 	}
+ 
+@@ -945,6 +944,7 @@ static void
+ mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb)
+ {
+ 	struct mt7996_sta *msta;
++	struct mt7996_link_sta *mlink;
+ 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ 	bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
+ 	u16 fc, tid;
+@@ -973,7 +973,8 @@ mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb)
+ 		return;
+ 
+ 	msta = (struct mt7996_sta *)sta->drv_priv;
+-	if (!test_and_set_bit(tid, &msta->wcid.ampdu_state))
++	mlink = rcu_dereference(msta->link[0]);
++	if (!test_and_set_bit(tid, &mlink->wcid.ampdu_state))
+ 		ieee80211_start_tx_ba_session(sta, tid, 0);
+ }
+ 
+@@ -1051,7 +1052,7 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ 		 */
+ 		info = le32_to_cpu(*cur_info);
+ 		if (info & MT_TXFREE_INFO_PAIR) {
+-			struct mt7996_sta *msta;
++			struct mt7996_link_sta *mlink;
+ 			u16 idx;
+ 
+ 			idx = FIELD_GET(MT_TXFREE_INFO_WLAN_ID, info);
+@@ -1060,10 +1061,10 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ 			if (!sta)
+ 				continue;
+ 
+-			msta = container_of(wcid, struct mt7996_sta, wcid);
++			mlink = container_of(wcid, struct mt7996_link_sta, wcid);
+ 			spin_lock_bh(&mdev->sta_poll_lock);
+-			if (list_empty(&msta->wcid.poll_list))
+-				list_add_tail(&msta->wcid.poll_list,
++			if (list_empty(&mlink->wcid.poll_list))
++				list_add_tail(&mlink->wcid.poll_list,
+ 					      &mdev->sta_poll_list);
+ 			spin_unlock_bh(&mdev->sta_poll_lock);
+ 			continue;
+@@ -1268,7 +1269,7 @@ out:
+ 
+ static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
+ {
+-	struct mt7996_sta *msta = NULL;
++	struct mt7996_link_sta *mlink;
+ 	struct mt76_wcid *wcid;
+ 	__le32 *txs_data = data;
+ 	u16 wcidx;
+@@ -1289,16 +1290,15 @@ static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
+ 	if (!wcid)
+ 		goto out;
+ 
+-	msta = container_of(wcid, struct mt7996_sta, wcid);
+-
+ 	mt7996_mac_add_txs_skb(dev, wcid, pid, txs_data);
+ 
+ 	if (!wcid->sta)
+ 		goto out;
+ 
++	mlink = container_of(wcid, struct mt7996_link_sta, wcid);
+ 	spin_lock_bh(&dev->mt76.sta_poll_lock);
+-	if (list_empty(&msta->wcid.poll_list))
+-		list_add_tail(&msta->wcid.poll_list, &dev->mt76.sta_poll_list);
++	if (list_empty(&mlink->wcid.poll_list))
++		list_add_tail(&mlink->wcid.poll_list, &dev->mt76.sta_poll_list);
+ 	spin_unlock_bh(&dev->mt76.sta_poll_lock);
+ 
+ out:
+@@ -2242,8 +2242,9 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
+ 	struct ieee80211_sta *sta;
+ 	struct ieee80211_vif *vif;
+ 	struct ieee80211_bss_conf *conf;
++	struct ieee80211_link_sta *link_sta;
+ 	struct mt7996_bss_conf *mconf;
+-	struct mt7996_sta *msta;
++	struct mt7996_link_sta *mlink;
+ 	u32 changed;
+ 	LIST_HEAD(list);
+ 
+@@ -2251,24 +2252,25 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
+ 	list_splice_init(&dev->sta_rc_list, &list);
+ 
+ 	while (!list_empty(&list)) {
+-		msta = list_first_entry(&list, struct mt7996_sta, rc_list);
+-		list_del_init(&msta->rc_list);
+-		changed = msta->changed;
+-		msta->changed = 0;
++		mlink = list_first_entry(&list, struct mt7996_link_sta, rc_list);
++		list_del_init(&mlink->rc_list);
++		changed = mlink->changed;
++		mlink->changed = 0;
+ 		spin_unlock_bh(&dev->mt76.sta_poll_lock);
+ 
+-		sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
+-		vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv);
++		sta = container_of((void *)mlink->sta, struct ieee80211_sta, drv_priv);
++		link_sta = &sta->deflink;
++		vif = container_of((void *)mlink->sta->vif, struct ieee80211_vif, drv_priv);
+ 		conf = &vif->bss_conf;
+-		mconf = &msta->vif->deflink;
++		mconf = &mlink->sta->vif->deflink;
+ 
+ 		if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED |
+ 			       IEEE80211_RC_NSS_CHANGED |
+ 			       IEEE80211_RC_BW_CHANGED))
+-			mt7996_mcu_add_rate_ctrl(dev, conf, mconf, sta, true);
++			mt7996_mcu_add_rate_ctrl(dev, conf, mconf, link_sta, mlink, true);
+ 
+ 		if (changed & IEEE80211_RC_SMPS_CHANGED)
+-			mt7996_mcu_set_fixed_field(dev, mconf, sta, NULL,
++			mt7996_mcu_set_fixed_field(dev, mconf, link_sta, mlink, NULL,
+ 						   RATE_PARAM_MMPS_UPDATE);
+ 
+ 		spin_lock_bh(&dev->mt76.sta_poll_lock);
+@@ -2561,7 +2563,7 @@ static int mt7996_mac_check_twt_req(struct ieee80211_twt_setup *twt)
+ }
+ 
+ static bool
+-mt7996_mac_twt_param_equal(struct mt7996_sta *msta,
++mt7996_mac_twt_param_equal(struct mt7996_link_sta *mlink,
+ 			   struct ieee80211_twt_params *twt_agrt)
+ {
+ 	u16 type = le16_to_cpu(twt_agrt->req_type);
+@@ -2572,10 +2574,10 @@ mt7996_mac_twt_param_equal(struct mt7996_sta *msta,
+ 	for (i = 0; i < MT7996_MAX_STA_TWT_AGRT; i++) {
+ 		struct mt7996_twt_flow *f;
+ 
+-		if (!(msta->twt.flowid_mask & BIT(i)))
++		if (!(mlink->twt.flowid_mask & BIT(i)))
+ 			continue;
+ 
+-		f = &msta->twt.flow[i];
++		f = &mlink->twt.flow[i];
+ 		if (f->duration == twt_agrt->min_twt_dur &&
+ 		    f->mantissa == twt_agrt->mantissa &&
+ 		    f->exp == exp &&
+@@ -2594,6 +2596,7 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+ {
+ 	enum ieee80211_twt_setup_cmd setup_cmd = TWT_SETUP_CMD_REJECT;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_link_sta *mlink;
+ 	struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
+ 	u16 req_type = le16_to_cpu(twt_agrt->req_type);
+ 	enum ieee80211_twt_setup_cmd sta_setup_cmd;
+@@ -2605,11 +2608,12 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+ 		goto out;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
++	mlink = mlink_dereference_protected(msta, 0);
+ 
+ 	if (dev->twt.n_agrt == MT7996_MAX_TWT_AGRT)
+ 		goto unlock;
+ 
+-	if (hweight8(msta->twt.flowid_mask) == ARRAY_SIZE(msta->twt.flow))
++	if (hweight8(mlink->twt.flowid_mask) == ARRAY_SIZE(mlink->twt.flow))
+ 		goto unlock;
+ 
+ 	if (twt_agrt->min_twt_dur < MT7996_MIN_TWT_DUR) {
+@@ -2618,10 +2622,10 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+ 		goto unlock;
+ 	}
+ 
+-	if (mt7996_mac_twt_param_equal(msta, twt_agrt))
++	if (mt7996_mac_twt_param_equal(mlink, twt_agrt))
+ 		goto unlock;
+ 
+-	flowid = ffs(~msta->twt.flowid_mask) - 1;
++	flowid = ffs(~mlink->twt.flowid_mask) - 1;
+ 	twt_agrt->req_type &= ~cpu_to_le16(IEEE80211_TWT_REQTYPE_FLOWID);
+ 	twt_agrt->req_type |= le16_encode_bits(flowid,
+ 					       IEEE80211_TWT_REQTYPE_FLOWID);
+@@ -2630,10 +2634,10 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+ 	exp = FIELD_GET(IEEE80211_TWT_REQTYPE_WAKE_INT_EXP, req_type);
+ 	sta_setup_cmd = FIELD_GET(IEEE80211_TWT_REQTYPE_SETUP_CMD, req_type);
+ 
+-	flow = &msta->twt.flow[flowid];
++	flow = &mlink->twt.flow[flowid];
+ 	memset(flow, 0, sizeof(*flow));
+ 	INIT_LIST_HEAD(&flow->list);
+-	flow->wcid = msta->wcid.idx;
++	flow->wcid = mlink->wcid.idx;
+ 	flow->table_id = table_id;
+ 	flow->id = flowid;
+ 	flow->duration = twt_agrt->min_twt_dur;
+@@ -2651,7 +2655,7 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+ 
+ 		flow->sched = true;
+ 		flow->start_tsf = mt7996_mac_twt_sched_list_add(dev, flow);
+-		curr_tsf = __mt7996_get_tsf(hw, &msta->vif->deflink);
++		curr_tsf = __mt7996_get_tsf(hw, &mlink->sta->vif->deflink);
+ 		div_u64_rem(curr_tsf - flow->start_tsf, interval, &rem);
+ 		flow_tsf = curr_tsf + interval - rem;
+ 		twt_agrt->twt = cpu_to_le64(flow_tsf);
+@@ -2660,13 +2664,13 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+ 	}
+ 	flow->tsf = le64_to_cpu(twt_agrt->twt);
+ 
+-	if (mt7996_mcu_twt_agrt_update(dev, &msta->vif->deflink, flow,
++	if (mt7996_mcu_twt_agrt_update(dev, &mlink->sta->vif->deflink, flow,
+ 				       MCU_TWT_AGRT_ADD))
+ 		goto unlock;
+ 
+ 	setup_cmd = TWT_SETUP_CMD_ACCEPT;
+ 	dev->twt.table_mask |= BIT(table_id);
+-	msta->twt.flowid_mask |= BIT(flowid);
++	mlink->twt.flowid_mask |= BIT(flowid);
+ 	dev->twt.n_agrt++;
+ 
+ unlock:
+@@ -2679,26 +2683,25 @@ out:
+ }
+ 
+ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
+-				  struct mt7996_sta *msta,
+-				  u8 flowid)
++				  struct mt7996_link_sta *mlink, u8 flowid)
+ {
+ 	struct mt7996_twt_flow *flow;
+-	struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
++	struct mt7996_bss_conf *mconf = mconf_dereference_protected(mlink->sta->vif, 0);
+ 
+ 	lockdep_assert_held(&dev->mt76.mutex);
+ 
+-	if (flowid >= ARRAY_SIZE(msta->twt.flow))
++	if (flowid >= ARRAY_SIZE(mlink->twt.flow))
+ 		return;
+ 
+-	if (!(msta->twt.flowid_mask & BIT(flowid)))
++	if (!(mlink->twt.flowid_mask & BIT(flowid)))
+ 		return;
+ 
+-	flow = &msta->twt.flow[flowid];
++	flow = &mlink->twt.flow[flowid];
+ 	if (mt7996_mcu_twt_agrt_update(dev, mconf, flow, MCU_TWT_AGRT_DELETE))
+ 		return;
+ 
+ 	list_del_init(&flow->list);
+-	msta->twt.flowid_mask &= ~BIT(flowid);
++	mlink->twt.flowid_mask &= ~BIT(flowid);
+ 	dev->twt.table_mask &= ~BIT(flow->table_id);
+ 	dev->twt.n_agrt--;
+ }
+@@ -2711,7 +2714,7 @@ mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
+ 	struct cfg80211_scan_request *req = phy->scan_req;
+ 	struct ieee80211_vif *vif = phy->scan_vif;
+ 	struct mt7996_vif *mvif;
+-	struct mt76_wcid *wcid;
++	struct mt7996_link_sta *mlink;
+ 	struct ieee80211_tx_info *info;
+ 	struct sk_buff *skb;
+ 
+@@ -2719,7 +2722,6 @@ mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
+ 		return;
+ 
+ 	mvif = (struct mt7996_vif *)vif->drv_priv;
+-	wcid = &mvif->sta.wcid;
+ 
+ 	skb = ieee80211_probereq_get(hw, vif->addr,
+ 				     ssid->ssid, ssid->ssid_len, req->ie_len);
+@@ -2752,7 +2754,8 @@ mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
+ 	}
+ 
+ 	local_bh_disable();
+-	mt76_tx(phy->mt76, NULL, wcid, skb);
++	mlink = rcu_dereference(mvif->sta.link[0]);
++	mt76_tx(phy->mt76, NULL, &mlink->wcid, skb);
+ 	local_bh_enable();
+ 
+ 	rcu_read_unlock();
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 8a32ec69..e071c5fa 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -229,6 +229,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ 	struct ieee80211_bss_conf *conf = &vif->bss_conf;
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_bss_conf *mconf = &mvif->deflink;
++	struct mt7996_link_sta *mlink = &mvif->sta.deflink;
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mt76_txq *mtxq;
+@@ -268,14 +269,15 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ 
+ 	idx = MT7996_WTBL_RESERVED - mconf->mt76.idx;
+ 
+-	INIT_LIST_HEAD(&mvif->sta.rc_list);
+-	INIT_LIST_HEAD(&mvif->sta.wcid.poll_list);
+-	mvif->sta.wcid.idx = idx;
+-	mvif->sta.wcid.phy_idx = band_idx;
+-	mvif->sta.wcid.hw_key_idx = -1;
+-	mvif->sta.wcid.tx_info |= MT_WCID_TX_INFO_SET;
+-	mvif->sta.vif = mvif;
+-	mt76_wcid_init(&mvif->sta.wcid);
++	INIT_LIST_HEAD(&mlink->rc_list);
++	INIT_LIST_HEAD(&mlink->wcid.poll_list);
++	mlink->wcid.idx = idx;
++	mlink->wcid.phy_idx = band_idx;
++	mlink->wcid.hw_key_idx = -1;
++	mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
++	mlink->sta = &mvif->sta;
++	mlink->sta->vif = mvif;
++	mt76_wcid_init(&mlink->wcid);
+ 
+ 	mt7996_mac_wtbl_update(dev, idx,
+ 			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+@@ -297,14 +299,15 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ 
+ 	mt7996_init_bitrate_mask(mconf);
+ 
+-	mt7996_mcu_add_bss_info(phy, conf, mconf, true);
++	mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, true);
+ 	/* defer the first STA_REC of BMC entry to BSS_CHANGED_BSSID for STA
+ 	 * interface, since firmware only records BSSID when the entry is new
+ 	 */
+ 	if (vif->type != NL80211_IFTYPE_STATION)
+-		mt7996_mcu_add_sta(dev, conf, mconf, NULL, true, true);
+-	rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid);
++		mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, true, true);
++	rcu_assign_pointer(dev->mt76.wcid[idx], &mlink->wcid);
+ 	rcu_assign_pointer(mvif->link[0], mconf);
++	rcu_assign_pointer(mvif->sta.link[0], mlink);
+ 
+ out:
+ 	mutex_unlock(&dev->mt76.mutex);
+@@ -318,10 +321,10 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ 	struct ieee80211_bss_conf *conf;
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_bss_conf *mconf;
+-	struct mt7996_sta *msta = &mvif->sta;
++	struct mt7996_link_sta *mlink = &mvif->sta.deflink;
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+-	int idx = msta->wcid.idx;
++	int idx = mlink->wcid.idx;
+ 
+ 	cancel_delayed_work_sync(&phy->scan_work);
+ 
+@@ -329,8 +332,8 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ 
+ 	conf = link_conf_dereference_protected(vif, 0);
+ 	mconf = mconf_dereference_protected(mvif, 0);
+-	mt7996_mcu_add_sta(dev, conf, mconf, NULL, false, false);
+-	mt7996_mcu_add_bss_info(phy, conf, mconf, false);
++	mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, false, false);
++	mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, false);
+ 
+ 	if (vif == phy->monitor_vif)
+ 		phy->monitor_vif = NULL;
+@@ -343,12 +346,13 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ 	phy->omac_mask &= ~BIT_ULL(mconf->mt76.omac_idx);
+ 
+ 	spin_lock_bh(&dev->mt76.sta_poll_lock);
+-	if (!list_empty(&msta->wcid.poll_list))
+-		list_del_init(&msta->wcid.poll_list);
++	if (!list_empty(&mlink->wcid.poll_list))
++		list_del_init(&mlink->wcid.poll_list);
+ 	spin_unlock_bh(&dev->mt76.sta_poll_lock);
+ 
+-	mt76_wcid_cleanup(&dev->mt76, &msta->wcid);
++	mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
+ 	rcu_assign_pointer(mvif->link[0], NULL);
++	rcu_assign_pointer(mvif->sta.link[0], NULL);
+ 
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+@@ -451,10 +455,10 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_sta *msta = sta ? (struct mt7996_sta *)sta->drv_priv :
+ 				  &mvif->sta;
+-	struct mt76_wcid *wcid = &msta->wcid;
+ 	struct mt7996_bss_conf *mconf;
++	struct mt7996_link_sta *mlink;
+ 	struct ieee80211_bss_conf *conf;
+-	u8 *wcid_keyidx = &wcid->hw_key_idx;
++	u8 *wcid_keyidx;
+ 	int idx = key->keyidx;
+ 	int err = 0;
+ 
+@@ -468,6 +472,12 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ 	    !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+ 		return -EOPNOTSUPP;
+ 
++	mutex_lock(&dev->mt76.mutex);
++	conf = link_conf_dereference_protected(vif, 0);
++	mconf = mconf_dereference_protected(mvif, 0);
++	mlink = mlink_dereference_protected(msta, 0);
++	wcid_keyidx = &mlink->wcid.hw_key_idx;
++
+ 	/* fall back to sw encryption for unsupported ciphers */
+ 	switch (key->cipher) {
+ 	case WLAN_CIPHER_SUITE_TKIP:
+@@ -490,16 +500,13 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ 	case WLAN_CIPHER_SUITE_WEP40:
+ 	case WLAN_CIPHER_SUITE_WEP104:
+ 	default:
++		mutex_unlock(&dev->mt76.mutex);
+ 		return -EOPNOTSUPP;
+ 	}
+ 
+-	mutex_lock(&dev->mt76.mutex);
+-
+-	conf = link_conf_dereference_protected(vif, 0);
+-	mconf = mconf_dereference_protected(mvif, 0);
+ 	if (cmd == SET_KEY && !sta && !mconf->mt76.cipher) {
+ 		mconf->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher);
+-		mt7996_mcu_add_bss_info(phy, conf, mconf, true);
++		mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, true);
+ 	}
+ 
+ 	if (cmd == SET_KEY) {
+@@ -510,14 +517,14 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ 		goto out;
+ 	}
+ 
+-	mt76_wcid_key_setup(&dev->mt76, wcid, key);
++	mt76_wcid_key_setup(&dev->mt76, &mlink->wcid, key);
+ 
+ 	if (key->keyidx == 6 || key->keyidx == 7)
+-		err = mt7996_mcu_bcn_prot_enable(dev, conf, mconf, key);
++		err = mt7996_mcu_bcn_prot_enable(dev, conf, mconf, mlink, key);
+ 	else
+ 		err = mt7996_mcu_add_key(&dev->mt76, mconf, key,
+ 					 MCU_WMWA_UNI_CMD(STA_REC_UPDATE),
+-					 &msta->wcid, cmd);
++					 &mlink->wcid, cmd);
+ out:
+ 	mutex_unlock(&dev->mt76.mutex);
+ 
+@@ -720,25 +727,27 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
+ {
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_bss_conf *mconf;
++	struct mt7996_link_sta *mlink;
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+ 	mconf = mconf_dereference_protected(mvif, 0);
++	mlink = mlink_dereference_protected(&mvif->sta, 0);
+ 	/* station mode uses BSSID to map the wlan entry to a peer,
+ 	 * and then peer references bss_info_rfch to set bandwidth cap.
+ 	 */
+ 	if ((changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) ||
+ 	    (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) ||
+ 	    (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon)) {
+-		mt7996_mcu_add_bss_info(phy, info, mconf, true);
+-		mt7996_mcu_add_sta(dev, info, mconf, NULL, true,
++		mt7996_mcu_add_bss_info(phy, info, mconf, mlink, true);
++		mt7996_mcu_add_sta(dev, info, mconf, NULL, mlink, true,
+ 				   !!(changed & BSS_CHANGED_BSSID));
+ 	}
+ 
+ 	if (changed & BSS_CHANGED_ERP_CTS_PROT)
+-		mt7996_mac_enable_rtscts(dev, vif, info->use_cts_prot);
++		mt7996_mac_enable_rtscts(dev, mlink, info->use_cts_prot);
+ 
+ 	if (changed & BSS_CHANGED_ERP_SLOT) {
+ 		int slottime = info->use_short_slot ? 9 : 20;
+@@ -809,6 +818,7 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_bss_conf *mconf = mconf_dereference_protected(mvif, 0);
++	struct mt7996_link_sta *mlink = &msta->deflink;
+ 	u8 band_idx = mconf->phy->mt76->band_idx;
+ 	int idx;
+ 
+@@ -820,13 +830,16 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 	if (idx < 0)
+ 		return -ENOSPC;
+ 
+-	INIT_LIST_HEAD(&msta->rc_list);
+-	INIT_LIST_HEAD(&msta->wcid.poll_list);
++	INIT_LIST_HEAD(&mlink->rc_list);
++	INIT_LIST_HEAD(&mlink->wcid.poll_list);
+ 	msta->vif = mvif;
+-	msta->wcid.sta = 1;
+-	msta->wcid.idx = idx;
+-	msta->wcid.phy_idx = band_idx;
+-	msta->wcid.tx_info |= MT_WCID_TX_INFO_SET;
++	mlink->wcid.sta = 1;
++	mlink->wcid.idx = idx;
++	mlink->wcid.phy_idx = band_idx;
++	mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
++	mlink->sta = msta;
++
++	rcu_assign_pointer(msta->link[0], mlink);
+ 
+ #ifdef CONFIG_MTK_VENDOR
+ 	mt7996_vendor_amnt_sta_remove(mconf->phy, sta);
+@@ -859,19 +872,25 @@ void mt7996_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	struct mt7996_bss_conf *mconf;
++	struct mt7996_link_sta *mlink;
+ 	struct ieee80211_bss_conf *conf;
++	struct ieee80211_link_sta *link_sta;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+-	mt7996_mac_wtbl_update(dev, msta->wcid.idx,
+-			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+-
+ 	conf = link_conf_dereference_protected(vif, 0);
+ 	mconf = mconf_dereference_protected(mvif, 0);
+-	mt7996_mcu_add_sta(dev, conf, mconf, sta, true, true);
+-	mt7996_mcu_add_rate_ctrl(dev, conf, mconf, sta, false);
++	link_sta = link_sta_dereference_protected(sta, 0);
++	mlink = mlink_dereference_protected(msta, 0);
+ 
+-	ewma_avg_signal_init(&msta->avg_ack_signal);
++	mt7996_mac_wtbl_update(dev, mlink->wcid.idx,
++			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
++
++	mt7996_mcu_add_sta(dev, conf, mconf, link_sta, mlink, true, true);
++	mt7996_mcu_add_rate_ctrl(dev, conf, mconf, link_sta, mlink, false);
++	mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
++
++	ewma_avg_signal_init(&mlink->avg_ack_signal);
+ 
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+@@ -883,25 +902,31 @@ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	struct mt7996_bss_conf *mconf;
++	struct mt7996_link_sta *mlink;
+ 	struct ieee80211_bss_conf *conf;
++	struct ieee80211_link_sta *link_sta;
+ 	int i;
+ 
+ 	conf = link_conf_dereference_protected(vif, 0);
+ 	mconf = mconf_dereference_protected(mvif, 0);
+-	mt7996_mcu_add_sta(dev, conf, mconf, sta, false, false);
++	link_sta = link_sta_dereference_protected(sta, 0);
++	mlink = mlink_dereference_protected(msta, 0);
++	mt7996_mcu_add_sta(dev, conf, mconf, link_sta, mlink, false, false);
+ 
+-	mt7996_mac_wtbl_update(dev, msta->wcid.idx,
++	mt7996_mac_wtbl_update(dev, mlink->wcid.idx,
+ 			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+ 
+-	for (i = 0; i < ARRAY_SIZE(msta->twt.flow); i++)
+-		mt7996_mac_twt_teardown_flow(dev, msta, i);
++	for (i = 0; i < ARRAY_SIZE(mlink->twt.flow); i++)
++		mt7996_mac_twt_teardown_flow(dev, mlink, i);
+ 
+ 	spin_lock_bh(&mdev->sta_poll_lock);
+-	if (!list_empty(&msta->wcid.poll_list))
+-		list_del_init(&msta->wcid.poll_list);
+-	if (!list_empty(&msta->rc_list))
+-		list_del_init(&msta->rc_list);
++	if (!list_empty(&mlink->wcid.poll_list))
++		list_del_init(&mlink->wcid.poll_list);
++	if (!list_empty(&mlink->rc_list))
++		list_del_init(&mlink->rc_list);
+ 	spin_unlock_bh(&mdev->sta_poll_lock);
++
++	rcu_assign_pointer(msta->link[0], NULL);
+ }
+ 
+ static void mt7996_tx(struct ieee80211_hw *hw,
+@@ -913,19 +938,22 @@ static void mt7996_tx(struct ieee80211_hw *hw,
+ 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ 	struct ieee80211_vif *vif = info->control.vif;
+ 	struct mt76_wcid *wcid = &dev->mt76.global_wcid;
++	struct mt7996_link_sta *mlink;
+ 
+ 	if (control->sta) {
+-		struct mt7996_sta *sta;
++		struct mt7996_sta *msta;
+ 
+-		sta = (struct mt7996_sta *)control->sta->drv_priv;
+-		wcid = &sta->wcid;
++		msta = (struct mt7996_sta *)control->sta->drv_priv;
++		mlink = rcu_dereference(msta->link[0]);
++		wcid = &mlink->wcid;
+ 	}
+ 
+ 	if (vif && !control->sta) {
+ 		struct mt7996_vif *mvif;
+ 
+ 		mvif = (struct mt7996_vif *)vif->drv_priv;
+-		wcid = &mvif->sta.wcid;
++		mlink = rcu_dereference(mvif->sta.link[0]);
++		wcid = &mlink->wcid;
+ 	}
+ 
+ 	mt76_tx(mphy, control->sta, wcid, skb);
+@@ -952,6 +980,7 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	struct ieee80211_sta *sta = params->sta;
+ 	struct ieee80211_txq *txq = sta->txq[params->tid];
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_link_sta *mlink;
+ 	u16 tid = params->tid;
+ 	u16 ssn = params->ssn;
+ 	struct mt76_txq *mtxq;
+@@ -963,14 +992,15 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	mtxq = (struct mt76_txq *)txq->drv_priv;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
++	mlink = mlink_dereference_protected(msta, 0);
+ 	switch (action) {
+ 	case IEEE80211_AMPDU_RX_START:
+-		mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, ssn,
++		mt76_rx_aggr_start(&dev->mt76, &mlink->wcid, tid, ssn,
+ 				   params->buf_size);
+ 		ret = mt7996_mcu_add_rx_ba(dev, params, true);
+ 		break;
+ 	case IEEE80211_AMPDU_RX_STOP:
+-		mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid);
++		mt76_rx_aggr_stop(&dev->mt76, &mlink->wcid, tid);
+ 		ret = mt7996_mcu_add_rx_ba(dev, params, false);
+ 		break;
+ 	case IEEE80211_AMPDU_TX_OPERATIONAL:
+@@ -981,16 +1011,16 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ 	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+ 		mtxq->aggr = false;
+-		clear_bit(tid, &msta->wcid.ampdu_state);
++		clear_bit(tid, &mlink->wcid.ampdu_state);
+ 		ret = mt7996_mcu_add_tx_ba(dev, params, false);
+ 		break;
+ 	case IEEE80211_AMPDU_TX_START:
+-		set_bit(tid, &msta->wcid.ampdu_state);
++		set_bit(tid, &mlink->wcid.ampdu_state);
+ 		ret = IEEE80211_AMPDU_TX_START_IMMEDIATE;
+ 		break;
+ 	case IEEE80211_AMPDU_TX_STOP_CONT:
+ 		mtxq->aggr = false;
+-		clear_bit(tid, &msta->wcid.ampdu_state);
++		clear_bit(tid, &mlink->wcid.ampdu_state);
+ 		ret = mt7996_mcu_add_tx_ba(dev, params, false);
+ 		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ 		break;
+@@ -1169,10 +1199,19 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
+ 				  struct ieee80211_sta *sta,
+ 				  struct station_info *sinfo)
+ {
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+-	struct rate_info *txrate = &msta->wcid.rate;
++	struct mt7996_link_sta *mlink;
++	struct rate_info *txrate;
+ 
++	/* TODO: support per-link rate report */
++	mutex_lock(&dev->mt76.mutex);
++	mlink = mlink_dereference_protected(msta, 0);
++	if (!mlink)
++		goto out;
++
++	txrate = &mlink->wcid.rate;
+ 	if (txrate->legacy || txrate->flags) {
+ 		if (txrate->legacy) {
+ 			sinfo->txrate.legacy = txrate->legacy;
+@@ -1191,44 +1230,52 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
+ 	sinfo->txrate.flags = txrate->flags;
+ 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
+ 
+-	sinfo->tx_failed = msta->wcid.stats.tx_failed;
++	sinfo->tx_failed = mlink->wcid.stats.tx_failed;
+ 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
+ 
+-	sinfo->tx_retries = msta->wcid.stats.tx_retries;
++	sinfo->tx_retries = mlink->wcid.stats.tx_retries;
+ 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES);
+ 
+-	sinfo->ack_signal = (s8)msta->ack_signal;
++	sinfo->ack_signal = (s8)mlink->ack_signal;
+ 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL);
+ 
+-	sinfo->avg_ack_signal = -(s8)ewma_avg_signal_read(&msta->avg_ack_signal);
++	sinfo->avg_ack_signal = -(s8)ewma_avg_signal_read(&mlink->avg_ack_signal);
+ 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG);
+ 
+ 	if (mtk_wed_device_active(&phy->dev->mt76.mmio.wed)) {
+-		sinfo->tx_bytes = msta->wcid.stats.tx_bytes;
++		sinfo->tx_bytes = mlink->wcid.stats.tx_bytes;
+ 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES64);
+ 
+-		sinfo->rx_bytes = msta->wcid.stats.rx_bytes;
++		sinfo->rx_bytes = mlink->wcid.stats.rx_bytes;
+ 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES64);
+ 
+-		sinfo->tx_packets = msta->wcid.stats.tx_packets;
++		sinfo->tx_packets = mlink->wcid.stats.tx_packets;
+ 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);
+ 
+-		sinfo->rx_packets = msta->wcid.stats.rx_packets;
++		sinfo->rx_packets = mlink->wcid.stats.rx_packets;
+ 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS);
+ 	}
++out:
++	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
+ static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta)
+ {
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_link_sta *mlink;
+ 	struct mt7996_dev *dev = msta->vif->dev;
+ 	u32 *changed = data;
+ 
++	rcu_read_lock();
++	mlink = rcu_dereference(msta->link[0]);
++
+ 	spin_lock_bh(&dev->mt76.sta_poll_lock);
+-	msta->changed |= *changed;
+-	if (list_empty(&msta->rc_list))
+-		list_add_tail(&msta->rc_list, &dev->sta_rc_list);
++	mlink->changed |= *changed;
++	if (list_empty(&mlink->rc_list))
++		list_add_tail(&mlink->rc_list, &dev->sta_rc_list);
+ 	spin_unlock_bh(&dev->mt76.sta_poll_lock);
++
++	rcu_read_unlock();
+ }
+ 
+ static void mt7996_sta_rc_update(struct ieee80211_hw *hw,
+@@ -1242,7 +1289,7 @@ static void mt7996_sta_rc_update(struct ieee80211_hw *hw,
+ 
+ 	if (!msta->vif) {
+ 		dev_warn(dev->mt76.dev, "Un-initialized STA %pM wcid %d in rc_work\n",
+-			 sta->addr, msta->wcid.idx);
++			 sta->addr, msta->deflink.wcid.idx);
+ 		return;
+ 	}
+ 
+@@ -1288,16 +1335,18 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw,
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	struct mt7996_bss_conf *mconf;
++	struct mt7996_link_sta *mlink;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 	mconf = mconf_dereference_protected(mvif, 0);
++	mlink = mlink_dereference_protected(msta, 0);
+ 
+ 	if (enabled)
+-		set_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags);
++		set_bit(MT_WCID_FLAG_4ADDR, &mlink->wcid.flags);
+ 	else
+-		clear_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags);
++		clear_bit(MT_WCID_FLAG_4ADDR, &mlink->wcid.flags);
+ 
+-	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, sta);
++	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
+@@ -1310,16 +1359,18 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw,
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	struct mt7996_bss_conf *mconf;
++	struct mt7996_link_sta *mlink;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 	mconf = mconf_dereference_protected(mvif, 0);
++	mlink = mlink_dereference_protected(msta, 0);
+ 
+ 	if (enabled)
+-		set_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
++		set_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
+ 	else
+-		clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
++		clear_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
+ 
+-	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, sta);
++	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
+@@ -1452,11 +1503,12 @@ static void mt7996_ethtool_worker(void *wi_data, struct ieee80211_sta *sta)
+ {
+ 	struct mt76_ethtool_worker_info *wi = wi_data;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_link_sta *mlink = &msta->deflink;
+ 
+ 	if (msta->vif->deflink.mt76.idx != wi->idx)
+ 		return;
+ 
+-	mt76_ethtool_worker(wi, &msta->wcid.stats, true);
++	mt76_ethtool_worker(wi, &mlink->wcid.stats, true);
+ }
+ 
+ static
+@@ -1559,10 +1611,12 @@ mt7996_twt_teardown_request(struct ieee80211_hw *hw,
+ 			    u8 flowid)
+ {
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_link_sta *mlink;
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+-	mt7996_mac_twt_teardown_flow(dev, msta, flowid);
++	mlink = mlink_dereference_protected(msta, 0);
++	mt7996_mac_twt_teardown_flow(dev, mlink, flowid);
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
+@@ -1685,6 +1739,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_bss_conf *mconf = &mvif->deflink;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_link_sta *mlink = &msta->deflink;
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+@@ -1707,7 +1762,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ 	if (!mtk_wed_device_active(wed))
+ 		return -ENODEV;
+ 
+-	if (msta->wcid.idx > MT7996_WTBL_STA)
++	if (mlink->wcid.idx > MT7996_WTBL_STA)
+ 		return -EIO;
+ 
+ 	path->type = DEV_PATH_MTK_WDMA;
+@@ -1715,11 +1770,11 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ 	path->mtk_wdma.wdma_idx = wed->wdma_idx;
+ 	path->mtk_wdma.bss = mconf->mt76.idx;
+ 	path->mtk_wdma.queue = 0;
+-	path->mtk_wdma.wcid = msta->wcid.idx;
++	path->mtk_wdma.wcid = mlink->wcid.idx;
+ 
+ 	if (ieee80211_hw_check(hw, SUPPORTS_AMSDU_IN_AMPDU) &&
+ 	    mtk_wed_is_amsdu_supported(wed))
+-		path->mtk_wdma.amsdu = msta->wcid.amsdu;
++		path->mtk_wdma.amsdu = mlink->wcid.amsdu;
+ 	else
+ 		path->mtk_wdma.amsdu = 0;
+ 
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 1e0fca2d..20b84c93 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -117,13 +117,13 @@ mt7996_mcu_get_sta_nss(u16 mcs_map)
+ }
+ 
+ static void
+-mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta,
++mt7996_mcu_set_sta_he_mcs(struct ieee80211_link_sta *link_sta,
+ 			  struct mt7996_bss_conf *mconf,
+ 			  __le16 *he_mcs, u16 mcs_map)
+ {
+ 	enum nl80211_band band = mconf->phy->mt76->chandef.chan->band;
+ 	const u16 *mask = mconf->bitrate_mask.control[band].he_mcs;
+-	int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
++	int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss;
+ 
+ 	for (nss = 0; nss < max_nss; nss++) {
+ 		int mcs;
+@@ -166,11 +166,11 @@ mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta,
+ }
+ 
+ static void
+-mt7996_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs,
+-			   const u16 *mask)
++mt7996_mcu_set_sta_vht_mcs(struct ieee80211_link_sta *link_sta,
++			   __le16 *vht_mcs, const u16 *mask)
+ {
+-	u16 mcs, mcs_map = le16_to_cpu(sta->deflink.vht_cap.vht_mcs.rx_mcs_map);
+-	int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
++	u16 mcs, mcs_map = le16_to_cpu(link_sta->vht_cap.vht_mcs.rx_mcs_map);
++	int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss;
+ 
+ 	for (nss = 0; nss < max_nss; nss++, mcs_map >>= 2) {
+ 		switch (mcs_map & 0x3) {
+@@ -192,13 +192,13 @@ mt7996_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs,
+ }
+ 
+ static void
+-mt7996_mcu_set_sta_ht_mcs(struct ieee80211_sta *sta, u8 *ht_mcs,
++mt7996_mcu_set_sta_ht_mcs(struct ieee80211_link_sta *link_sta, u8 *ht_mcs,
+ 			  const u8 *mask)
+ {
+-	int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
++	int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss;
+ 
+ 	for (nss = 0; nss < max_nss; nss++)
+-		ht_mcs[nss] = sta->deflink.ht_cap.mcs.rx_mask[nss] & mask[nss];
++		ht_mcs[nss] = link_sta->ht_cap.mcs.rx_mask[nss] & mask[nss];
+ }
+ 
+ static int
+@@ -531,14 +531,14 @@ static inline void __mt7996_stat_to_netdev(struct mt76_phy *mphy,
+ 					   u32 tx_bytes, u32 rx_bytes,
+ 					   u32 tx_packets, u32 rx_packets)
+ {
+-	struct mt7996_sta *msta;
++	struct mt7996_link_sta *mlink;
+ 	struct ieee80211_vif *vif;
+ 	struct wireless_dev *wdev;
+ 
+ 	if (wiphy_ext_feature_isset(mphy->hw->wiphy,
+ 				    NL80211_EXT_FEATURE_STAS_COUNT)) {
+-		msta = container_of(wcid, struct mt7996_sta, wcid);
+-		vif = container_of((void *)msta->vif, struct ieee80211_vif,
++		mlink = container_of(wcid, struct mt7996_link_sta, wcid);
++		vif = container_of((void *)mlink->sta->vif, struct ieee80211_vif,
+ 				   drv_priv);
+ 		wdev = ieee80211_vif_to_wdev(vif);
+ 
+@@ -1236,10 +1236,10 @@ __mt7996_mcu_alloc_bss_req(struct mt76_dev *dev, struct mt76_vif *mvif, int len)
+ 
+ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
+ 			    struct ieee80211_bss_conf *conf,
+-			    struct mt7996_bss_conf *mconf, int enable)
++			    struct mt7996_bss_conf *mconf,
++			    struct mt7996_link_sta *mlink, int enable)
+ {
+ 	struct ieee80211_vif *vif = conf->vif;
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_dev *dev = phy->dev;
+ 	struct sk_buff *skb;
+ 
+@@ -1255,7 +1255,7 @@ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
+ 
+ 	/* bss_basic must be first */
+ 	mt7996_mcu_bss_basic_tlv(skb, conf, mconf, NULL, phy->mt76,
+-				 mvif->sta.wcid.idx, enable);
++				 mlink->wcid.idx, enable);
+ 	mt7996_mcu_bss_sec_tlv(skb, mconf);
+ 
+ 	if (vif->type == NL80211_IFTYPE_MONITOR)
+@@ -1335,9 +1335,10 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ {
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv;
+ 	struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
++	struct mt7996_link_sta *mlink = mlink_dereference_protected(msta, 0);
+ 
+ 	if (enable && !params->amsdu)
+-		msta->wcid.amsdu = false;
++		mlink->wcid.amsdu = false;
+ 
+ 	return mt7996_mcu_sta_ba(dev, &mconf->mt76, params, enable, true);
+ }
+@@ -1355,15 +1356,15 @@ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
+ static void
+ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
+ 		      struct mt7996_bss_conf *mconf,
+-		      struct ieee80211_sta *sta)
++		      struct ieee80211_link_sta *link_sta)
+ {
+-	struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
++	struct ieee80211_he_cap_elem *elem = &link_sta->he_cap.he_cap_elem;
+ 	struct ieee80211_he_mcs_nss_supp mcs_map;
+ 	struct sta_rec_he_v2 *he;
+ 	struct tlv *tlv;
+ 	int i = 0;
+ 
+-	if (!sta->deflink.he_cap.has_he)
++	if (!link_sta->he_cap.has_he)
+ 		return;
+ 
+ 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_V2, sizeof(*he));
+@@ -1380,21 +1381,21 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
+ 		u8p_replace_bits(&he->he_phy_cap[1], conf->he_ldpc,
+ 				 IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD);
+ 
+-	mcs_map = sta->deflink.he_cap.he_mcs_nss_supp;
+-	switch (sta->deflink.bandwidth) {
++	mcs_map = link_sta->he_cap.he_mcs_nss_supp;
++	switch (link_sta->bandwidth) {
+ 	case IEEE80211_STA_RX_BW_160:
+ 		if (elem->phy_cap_info[0] &
+ 		    IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)
+-			mt7996_mcu_set_sta_he_mcs(sta, mconf,
++			mt7996_mcu_set_sta_he_mcs(link_sta, mconf,
+ 						  &he->max_nss_mcs[CMD_HE_MCS_BW8080],
+ 						  le16_to_cpu(mcs_map.rx_mcs_80p80));
+ 
+-		mt7996_mcu_set_sta_he_mcs(sta, mconf,
++		mt7996_mcu_set_sta_he_mcs(link_sta, mconf,
+ 					  &he->max_nss_mcs[CMD_HE_MCS_BW160],
+ 					  le16_to_cpu(mcs_map.rx_mcs_160));
+ 		fallthrough;
+ 	default:
+-		mt7996_mcu_set_sta_he_mcs(sta, mconf,
++		mt7996_mcu_set_sta_he_mcs(link_sta, mconf,
+ 					  &he->max_nss_mcs[CMD_HE_MCS_BW80],
+ 					  le16_to_cpu(mcs_map.rx_mcs_80));
+ 		break;
+@@ -1404,24 +1405,25 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
+ }
+ 
+ static void
+-mt7996_mcu_sta_he_6g_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
++mt7996_mcu_sta_he_6g_tlv(struct sk_buff *skb,
++			 struct ieee80211_link_sta *link_sta)
+ {
+ 	struct sta_rec_he_6g_capa *he_6g;
+ 	struct tlv *tlv;
+ 
+-	if (!sta->deflink.he_6ghz_capa.capa)
++	if (!link_sta->he_6ghz_capa.capa)
+ 		return;
+ 
+ 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_6G, sizeof(*he_6g));
+ 
+ 	he_6g = (struct sta_rec_he_6g_capa *)tlv;
+-	he_6g->capa = sta->deflink.he_6ghz_capa.capa;
++	he_6g->capa = link_sta->he_6ghz_capa.capa;
+ }
+ 
+ static void
+-mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
++mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta)
+ {
+-	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv;
+ 	struct ieee80211_vif *vif = container_of((void *)msta->vif,
+ 						 struct ieee80211_vif, drv_priv);
+ 	struct ieee80211_eht_mcs_nss_supp *mcs_map;
+@@ -1429,11 +1431,11 @@ mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ 	struct sta_rec_eht *eht;
+ 	struct tlv *tlv;
+ 
+-	if (!sta->deflink.eht_cap.has_eht)
++	if (!link_sta->eht_cap.has_eht)
+ 		return;
+ 
+-	mcs_map = &sta->deflink.eht_cap.eht_mcs_nss_supp;
+-	elem = &sta->deflink.eht_cap.eht_cap_elem;
++	mcs_map = &link_sta->eht_cap.eht_mcs_nss_supp;
++	elem = &link_sta->eht_cap.eht_cap_elem;
+ 
+ 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT, sizeof(*eht));
+ 
+@@ -1444,7 +1446,7 @@ mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ 	eht->phy_cap_ext = cpu_to_le64(elem->phy_cap_info[8]);
+ 
+ 	if (vif->type != NL80211_IFTYPE_STATION &&
+-	    (sta->deflink.he_cap.he_cap_elem.phy_cap_info[0] &
++	    (link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
+ 	     (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
+ 	      IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
+ 	      IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
+@@ -1460,44 +1462,44 @@ mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ }
+ 
+ static void
+-mt7996_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
++mt7996_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta)
+ {
+ 	struct sta_rec_ht_uni *ht;
+ 	struct tlv *tlv;
+ 
+-	if (!sta->deflink.ht_cap.ht_supported)
++	if (!link_sta->ht_cap.ht_supported)
+ 		return;
+ 
+ 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht));
+ 
+ 	ht = (struct sta_rec_ht_uni *)tlv;
+-	ht->ht_cap = cpu_to_le16(sta->deflink.ht_cap.cap);
+-	ht->ampdu_param = u8_encode_bits(sta->deflink.ht_cap.ampdu_factor,
++	ht->ht_cap = cpu_to_le16(link_sta->ht_cap.cap);
++	ht->ampdu_param = u8_encode_bits(link_sta->ht_cap.ampdu_factor,
+ 					 IEEE80211_HT_AMPDU_PARM_FACTOR) |
+-			  u8_encode_bits(sta->deflink.ht_cap.ampdu_density,
++			  u8_encode_bits(link_sta->ht_cap.ampdu_density,
+ 					 IEEE80211_HT_AMPDU_PARM_DENSITY);
+ }
+ 
+ static void
+-mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
++mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta)
+ {
+ 	struct sta_rec_vht *vht;
+ 	struct tlv *tlv;
+ #ifdef CONFIG_MTK_VENDOR
+-	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv;
+ 	struct mt7996_phy *phy = (struct mt7996_phy *)msta->vif->deflink.phy;
+ #endif
+ 
+ 	/* For 6G band, this tlv is necessary to let hw work normally */
+-	if (!sta->deflink.he_6ghz_capa.capa && !sta->deflink.vht_cap.vht_supported)
++	if (!link_sta->he_6ghz_capa.capa && !link_sta->vht_cap.vht_supported)
+ 		return;
+ 
+ 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht));
+ 
+ 	vht = (struct sta_rec_vht *)tlv;
+-	vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap);
+-	vht->vht_rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map;
+-	vht->vht_tx_mcs_map = sta->deflink.vht_cap.vht_mcs.tx_mcs_map;
++	vht->vht_cap = cpu_to_le32(link_sta->vht_cap.cap);
++	vht->vht_rx_mcs_map = link_sta->vht_cap.vht_mcs.rx_mcs_map;
++	vht->vht_tx_mcs_map = link_sta->vht_cap.vht_mcs.tx_mcs_map;
+ #ifdef CONFIG_MTK_VENDOR
+ 	vht->rts_bw_sig = phy->rts_bw_sig;
+ #endif
+@@ -1505,9 +1507,10 @@ mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ 
+ static void
+ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+-			 struct ieee80211_vif *vif, struct ieee80211_sta *sta)
++			 struct ieee80211_vif *vif,
++			 struct ieee80211_link_sta *link_sta,
++			 struct mt7996_link_sta *mlink)
+ {
+-	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	struct sta_rec_amsdu *amsdu;
+ 	struct tlv *tlv;
+ 
+@@ -1516,16 +1519,16 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 	    vif->type != NL80211_IFTYPE_AP)
+ 		return;
+ 
+-	if (!sta->deflink.agg.max_amsdu_len)
++	if (!link_sta->agg.max_amsdu_len)
+ 		return;
+ 
+ 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu));
+ 	amsdu = (struct sta_rec_amsdu *)tlv;
+ 	amsdu->max_amsdu_num = 8;
+ 	amsdu->amsdu_en = true;
+-	msta->wcid.amsdu = true;
++	mlink->wcid.amsdu = true;
+ 
+-	switch (sta->deflink.agg.max_amsdu_len) {
++	switch (link_sta->agg.max_amsdu_len) {
+ 	case IEEE80211_MAX_MPDU_LEN_VHT_11454:
+ 		amsdu->max_mpdu_size =
+ 			IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
+@@ -1544,10 +1547,10 @@ static void
+ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 			struct ieee80211_bss_conf *conf,
+ 			struct mt7996_bss_conf *mconf,
+-			struct ieee80211_sta *sta)
++			struct ieee80211_link_sta *link_sta)
+ {
+ 	struct mt7996_phy *phy = mconf->phy;
+-	struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
++	struct ieee80211_he_cap_elem *elem = &link_sta->he_cap.he_cap_elem;
+ 	struct sta_rec_muru *muru;
+ 	struct tlv *tlv;
+ 
+@@ -1567,11 +1570,11 @@ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 	muru->cfg.ofdma_dl_en = !!(phy->muru_onoff & OFDMA_DL);
+ 	muru->cfg.ofdma_ul_en = !!(phy->muru_onoff & OFDMA_UL);
+ 
+-	if (sta->deflink.vht_cap.vht_supported)
++	if (link_sta->vht_cap.vht_supported)
+ 		muru->mimo_dl.vht_mu_bfee =
+-			!!(sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
++			!!(link_sta->vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
+ 
+-	if (!sta->deflink.he_cap.has_he)
++	if (!link_sta->he_cap.has_he)
+ 		return;
+ 
+ 	muru->mimo_dl.partial_bw_dl_mimo =
+@@ -1604,7 +1607,7 @@ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ static inline bool
+ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
+ 			struct mt7996_bss_conf *mconf,
+-			struct ieee80211_sta *sta, bool bfee)
++			struct ieee80211_link_sta *link_sta, bool bfee)
+ {
+ 	int sts = hweight16(phy->mt76->chainmask);
+ 
+@@ -1615,8 +1618,8 @@ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
+ 	if (!bfee && sts < 2)
+ 		return false;
+ 
+-	if (sta->deflink.eht_cap.has_eht) {
+-		struct ieee80211_sta_eht_cap *pc = &sta->deflink.eht_cap;
++	if (link_sta->eht_cap.has_eht) {
++		struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap;
+ 		struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem;
+ 
+ 		if (bfee)
+@@ -1627,8 +1630,8 @@ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
+ 			       EHT_PHY(CAP0_SU_BEAMFORMEE, pe->phy_cap_info[0]);
+ 	}
+ 
+-	if (sta->deflink.he_cap.has_he) {
+-		struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem;
++	if (link_sta->he_cap.has_he) {
++		struct ieee80211_he_cap_elem *pe = &link_sta->he_cap.he_cap_elem;
+ 
+ 		if (bfee)
+ 			return conf->he_su_beamformee &&
+@@ -1638,8 +1641,8 @@ mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
+ 			       HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]);
+ 	}
+ 
+-	if (sta->deflink.vht_cap.vht_supported) {
+-		u32 cap = sta->deflink.vht_cap.cap;
++	if (link_sta->vht_cap.vht_supported) {
++		u32 cap = link_sta->vht_cap.cap;
+ 
+ 		if (bfee)
+ 			return conf->vht_su_beamformee &&
+@@ -1662,10 +1665,10 @@ mt7996_mcu_sta_sounding_rate(struct sta_rec_bf *bf)
+ }
+ 
+ static void
+-mt7996_mcu_sta_bfer_ht(struct ieee80211_sta *sta, struct mt7996_phy *phy,
+-		       struct sta_rec_bf *bf)
++mt7996_mcu_sta_bfer_ht(struct ieee80211_link_sta *link_sta,
++		       struct mt7996_phy *phy, struct sta_rec_bf *bf)
+ {
+-	struct ieee80211_mcs_info *mcs = &sta->deflink.ht_cap.mcs;
++	struct ieee80211_mcs_info *mcs = &link_sta->ht_cap.mcs;
+ 	u8 n = 0;
+ 
+ 	bf->tx_mode = MT_PHY_TYPE_HT;
+@@ -1687,10 +1690,11 @@ mt7996_mcu_sta_bfer_ht(struct ieee80211_sta *sta, struct mt7996_phy *phy,
+ }
+ 
+ static void
+-mt7996_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7996_phy *phy,
+-			struct sta_rec_bf *bf, bool explicit)
++mt7996_mcu_sta_bfer_vht(struct ieee80211_link_sta *link_sta,
++			struct mt7996_phy *phy, struct sta_rec_bf *bf,
++			bool explicit)
+ {
+-	struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap;
++	struct ieee80211_sta_vht_cap *pc = &link_sta->vht_cap;
+ 	struct ieee80211_sta_vht_cap *vc = &phy->mt76->sband_5g.sband.vht_cap;
+ 	u16 mcs_map = le16_to_cpu(pc->vht_mcs.rx_mcs_map);
+ 	u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map);
+@@ -1711,23 +1715,24 @@ mt7996_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7996_phy *phy,
+ 		bf->ncol = min_t(u8, nss_mcs, bf->nrow);
+ 		bf->ibf_ncol = bf->ncol;
+ 
+-		if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
++		if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160)
+ 			bf->nrow = 1;
+ 	} else {
+ 		bf->nrow = tx_ant;
+ 		bf->ncol = min_t(u8, nss_mcs, bf->nrow);
+ 		bf->ibf_ncol = nss_mcs;
+ 
+-		if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
++		if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160)
+ 			bf->ibf_nrow = 1;
+ 	}
+ }
+ 
+ static void
+-mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+-		       struct mt7996_phy *phy, struct sta_rec_bf *bf)
++mt7996_mcu_sta_bfer_he(struct ieee80211_link_sta *link_sta,
++		       struct ieee80211_vif *vif, struct mt7996_phy *phy,
++		       struct sta_rec_bf *bf)
+ {
+-	struct ieee80211_sta_he_cap *pc = &sta->deflink.he_cap;
++	struct ieee80211_sta_he_cap *pc = &link_sta->he_cap;
+ 	struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem;
+ 	const struct ieee80211_sta_he_cap *vc =
+ 		mt76_connac_get_he_phy_cap(phy->mt76, vif);
+@@ -1752,7 +1757,7 @@ mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+ 	bf->ncol = min_t(u8, nss_mcs, bf->nrow);
+ 	bf->ibf_ncol = bf->ncol;
+ 
+-	if (sta->deflink.bandwidth != IEEE80211_STA_RX_BW_160)
++	if (link_sta->bandwidth != IEEE80211_STA_RX_BW_160)
+ 		return;
+ 
+ 	/* go over for 160MHz and 80p80 */
+@@ -1784,10 +1789,11 @@ mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+ }
+ 
+ static void
+-mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+-			struct mt7996_phy *phy, struct sta_rec_bf *bf)
++mt7996_mcu_sta_bfer_eht(struct ieee80211_link_sta *link_sta,
++			struct ieee80211_vif *vif, struct mt7996_phy *phy,
++			struct sta_rec_bf *bf)
+ {
+-	struct ieee80211_sta_eht_cap *pc = &sta->deflink.eht_cap;
++	struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap;
+ 	struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem;
+ 	struct ieee80211_eht_mcs_nss_supp *eht_nss = &pc->eht_mcs_nss_supp;
+ 	const struct ieee80211_sta_eht_cap *vc =
+@@ -1810,10 +1816,10 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+ 	bf->ncol = min_t(u8, nss_mcs, bf->nrow);
+ 	bf->ibf_ncol = bf->ncol;
+ 
+-	if (sta->deflink.bandwidth < IEEE80211_STA_RX_BW_160)
++	if (link_sta->bandwidth < IEEE80211_STA_RX_BW_160)
+ 		return;
+ 
+-	switch (sta->deflink.bandwidth) {
++	switch (link_sta->bandwidth) {
+ 	case IEEE80211_STA_RX_BW_160:
+ 		snd_dim = EHT_PHY(CAP2_SOUNDING_DIM_160MHZ_MASK, ve->phy_cap_info[2]);
+ 		sts = EHT_PHY(CAP1_BEAMFORMEE_SS_160MHZ_MASK, pe->phy_cap_info[1]);
+@@ -1842,7 +1848,7 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+ static void
+ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 			struct ieee80211_bss_conf *conf, struct mt7996_bss_conf *mconf,
+-			struct ieee80211_sta *sta)
++			struct ieee80211_link_sta *link_sta)
+ {
+ 	struct mt7996_phy *phy = mconf->phy;
+ 	int tx_ant = hweight16(phy->mt76->chainmask) - 1;
+@@ -1856,10 +1862,10 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 	};
+ 	bool ebf;
+ 
+-	if (!(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he))
++	if (!(link_sta->ht_cap.ht_supported || link_sta->he_cap.has_he))
+ 		return;
+ 
+-	ebf = mt7996_is_ebf_supported(phy, conf, mconf, sta, false);
++	ebf = mt7996_is_ebf_supported(phy, conf, mconf, link_sta, false);
+ 	if (!ebf && !dev->ibf)
+ 		return;
+ 
+@@ -1870,23 +1876,23 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 	 * vht: support eBF and iBF
+ 	 * ht: iBF only, since mac80211 lacks of eBF support
+ 	 */
+-	if (sta->deflink.eht_cap.has_eht && ebf)
+-		mt7996_mcu_sta_bfer_eht(sta, conf->vif, phy, bf);
+-	else if (sta->deflink.he_cap.has_he && ebf)
+-		mt7996_mcu_sta_bfer_he(sta, conf->vif, phy, bf);
+-	else if (sta->deflink.vht_cap.vht_supported)
+-		mt7996_mcu_sta_bfer_vht(sta, phy, bf, ebf);
+-	else if (sta->deflink.ht_cap.ht_supported)
+-		mt7996_mcu_sta_bfer_ht(sta, phy, bf);
++	if (link_sta->eht_cap.has_eht && ebf)
++		mt7996_mcu_sta_bfer_eht(link_sta, conf->vif, phy, bf);
++	else if (link_sta->he_cap.has_he && ebf)
++		mt7996_mcu_sta_bfer_he(link_sta, conf->vif, phy, bf);
++	else if (link_sta->vht_cap.vht_supported)
++		mt7996_mcu_sta_bfer_vht(link_sta, phy, bf, ebf);
++	else if (link_sta->ht_cap.ht_supported)
++		mt7996_mcu_sta_bfer_ht(link_sta, phy, bf);
+ 	else
+ 		return;
+ 
+ 	bf->bf_cap = ebf ? ebf : dev->ibf << 1;
+-	bf->bw = sta->deflink.bandwidth;
+-	bf->ibf_dbw = sta->deflink.bandwidth;
++	bf->bw = link_sta->bandwidth;
++	bf->ibf_dbw = link_sta->bandwidth;
+ 	bf->ibf_nrow = tx_ant;
+ 
+-	if (!ebf && sta->deflink.bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol)
++	if (!ebf && link_sta->bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol)
+ 		bf->ibf_timeout = 0x48;
+ 	else
+ 		bf->ibf_timeout = 0x18;
+@@ -1896,7 +1902,7 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 	else
+ 		bf->mem_20m = matrix[bf->nrow][bf->ncol];
+ 
+-	switch (sta->deflink.bandwidth) {
++	switch (link_sta->bandwidth) {
+ 	case IEEE80211_STA_RX_BW_160:
+ 	case IEEE80211_STA_RX_BW_80:
+ 		bf->mem_total = bf->mem_20m * 2;
+@@ -1913,7 +1919,8 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ static void
+ mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 			struct ieee80211_bss_conf *conf,
+-			struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta)
++			struct mt7996_bss_conf *mconf,
++			struct ieee80211_link_sta *link_sta)
+ {
+ 	struct mt7996_phy *phy = mconf->phy;
+ 	int tx_ant = hweight8(phy->mt76->antenna_mask) - 1;
+@@ -1921,22 +1928,22 @@ mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 	struct tlv *tlv;
+ 	u8 nrow = 0;
+ 
+-	if (!(sta->deflink.vht_cap.vht_supported || sta->deflink.he_cap.has_he))
++	if (!(link_sta->vht_cap.vht_supported || link_sta->he_cap.has_he))
+ 		return;
+ 
+-	if (!mt7996_is_ebf_supported(phy, conf, mconf, sta, true))
++	if (!mt7996_is_ebf_supported(phy, conf, mconf, link_sta, true))
+ 		return;
+ 
+ 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BFEE, sizeof(*bfee));
+ 	bfee = (struct sta_rec_bfee *)tlv;
+ 
+-	if (sta->deflink.he_cap.has_he) {
+-		struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem;
++	if (link_sta->he_cap.has_he) {
++		struct ieee80211_he_cap_elem *pe = &link_sta->he_cap.he_cap_elem;
+ 
+ 		nrow = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK,
+ 			      pe->phy_cap_info[5]);
+-	} else if (sta->deflink.vht_cap.vht_supported) {
+-		struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap;
++	} else if (link_sta->vht_cap.vht_supported) {
++		struct ieee80211_sta_vht_cap *pc = &link_sta->vht_cap;
+ 
+ 		nrow = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK,
+ 				 pc->cap);
+@@ -1973,25 +1980,24 @@ mt7996_mcu_sta_hdrt_tlv(struct mt7996_dev *dev, struct sk_buff *skb)
+ static void
+ mt7996_mcu_sta_hdr_trans_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 			     struct ieee80211_vif *vif,
+-			     struct ieee80211_sta *sta)
++			     struct mt7996_link_sta *mlink)
+ {
+ 	struct sta_rec_hdr_trans *hdr_trans;
+-	struct mt76_wcid *wcid;
++	struct mt76_wcid *wcid = &mlink->wcid;
+ 	struct tlv *tlv;
+ 
+ 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HDR_TRANS, sizeof(*hdr_trans));
+ 	hdr_trans = (struct sta_rec_hdr_trans *)tlv;
+ 	hdr_trans->dis_rx_hdr_tran = true;
+ 
++	if (!wcid->sta)
++		return;
++
+ 	if (vif->type == NL80211_IFTYPE_STATION)
+ 		hdr_trans->to_ds = true;
+ 	else
+ 		hdr_trans->from_ds = true;
+ 
+-	if (!sta)
+-		return;
+-
+-	wcid = (struct mt76_wcid *)sta->drv_priv;
+ 	hdr_trans->dis_rx_hdr_tran = !test_bit(MT_WCID_FLAG_HDR_TRANS, &wcid->flags);
+ 	if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) {
+ 		hdr_trans->to_ds = true;
+@@ -2048,16 +2054,17 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
+ 
+ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
+ 			       struct mt7996_bss_conf *mconf,
+-			       struct ieee80211_sta *sta, void *data, u32 field)
++			       struct ieee80211_link_sta *link_sta,
++			       struct mt7996_link_sta *mlink, void *data,
++			       u32 field)
+ {
+-	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	struct sta_phy_uni *phy = data;
+ 	struct sta_rec_ra_fixed_uni *ra;
+ 	struct sk_buff *skb;
+ 	struct tlv *tlv;
+ 
+ 	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
+-					      &msta->wcid,
++					      &mlink->wcid,
+ 					      MT7996_STA_UPDATE_MAX_SIZE);
+ 	if (IS_ERR(skb))
+ 		return PTR_ERR(skb);
+@@ -2076,7 +2083,7 @@ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
+ 			ra->phy = *phy;
+ 		break;
+ 	case RATE_PARAM_MMPS_UPDATE:
+-		ra->mmps_mode = mt7996_mcu_get_mmps_mode(sta->deflink.smps_mode);
++		ra->mmps_mode = mt7996_mcu_get_mmps_mode(link_sta->smps_mode);
+ 		break;
+ 	default:
+ 		break;
+@@ -2091,7 +2098,8 @@ static int
+ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev,
+ 			       struct ieee80211_bss_conf *conf,
+ 			       struct mt7996_bss_conf *mconf,
+-			       struct ieee80211_sta *sta)
++			       struct ieee80211_link_sta *link_sta,
++			       struct mt7996_link_sta *mlink)
+ {
+ 	struct cfg80211_chan_def *chandef = &mconf->phy->mt76->chandef;
+ 	struct cfg80211_bitrate_mask *mask = &mconf->bitrate_mask;
+@@ -2115,11 +2123,11 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev,
+ 		}								\
+ 	} while (0)
+ 
+-	if (sta->deflink.he_cap.has_he) {
++	if (link_sta->he_cap.has_he) {
+ 		__sta_phy_bitrate_mask_check(he_mcs, he_gi, 0, 1);
+-	} else if (sta->deflink.vht_cap.vht_supported) {
++	} else if (link_sta->vht_cap.vht_supported) {
+ 		__sta_phy_bitrate_mask_check(vht_mcs, gi, 0, 0);
+-	} else if (sta->deflink.ht_cap.ht_supported) {
++	} else if (link_sta->ht_cap.ht_supported) {
+ 		__sta_phy_bitrate_mask_check(ht_mcs, gi, 1, 0);
+ 	} else {
+ 		nrates = hweight32(mask->control[band].legacy);
+@@ -2136,8 +2144,8 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev,
+ 
+ 	/* fixed single rate */
+ 	if (nrates == 1) {
+-		ret = mt7996_mcu_set_fixed_field(dev, mconf, sta, &phy,
+-						 RATE_PARAM_FIXED_MCS);
++		ret = mt7996_mcu_set_fixed_field(dev, mconf, link_sta, mlink,
++						 &phy, RATE_PARAM_FIXED_MCS);
+ 		if (ret)
+ 			return ret;
+ 	}
+@@ -2145,29 +2153,28 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev,
+ 	/* fixed GI */
+ 	if (mask->control[band].gi != NL80211_TXRATE_DEFAULT_GI ||
+ 	    mask->control[band].he_gi != GENMASK(7, 0)) {
+-		struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 		u32 addr;
+ 
+ 		/* firmware updates only TXCMD but doesn't take WTBL into
+ 		 * account, so driver should update here to reflect the
+ 		 * actual txrate hardware sends out.
+ 		 */
+-		addr = mt7996_mac_wtbl_lmac_addr(dev, msta->wcid.idx, 7);
+-		if (sta->deflink.he_cap.has_he)
++		addr = mt7996_mac_wtbl_lmac_addr(dev, mlink->wcid.idx, 7);
++		if (link_sta->he_cap.has_he)
+ 			mt76_rmw_field(dev, addr, GENMASK(31, 24), phy.sgi);
+ 		else
+ 			mt76_rmw_field(dev, addr, GENMASK(15, 12), phy.sgi);
+ 
+-		ret = mt7996_mcu_set_fixed_field(dev, mconf, sta, &phy,
+-						 RATE_PARAM_FIXED_GI);
++		ret = mt7996_mcu_set_fixed_field(dev, mconf, link_sta, mlink,
++						 &phy, RATE_PARAM_FIXED_GI);
+ 		if (ret)
+ 			return ret;
+ 	}
+ 
+ 	/* fixed HE_LTF */
+ 	if (mask->control[band].he_ltf != GENMASK(7, 0)) {
+-		ret = mt7996_mcu_set_fixed_field(dev, mconf, sta, &phy,
+-						 RATE_PARAM_FIXED_HE_LTF);
++		ret = mt7996_mcu_set_fixed_field(dev, mconf, link_sta, mlink,
++						 &phy, RATE_PARAM_FIXED_HE_LTF);
+ 		if (ret)
+ 			return ret;
+ 	}
+@@ -2179,7 +2186,7 @@ static void
+ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
+ 			     struct ieee80211_bss_conf *conf,
+ 			     struct mt7996_bss_conf *mconf,
+-			     struct ieee80211_sta *sta)
++			     struct ieee80211_link_sta *link_sta)
+ {
+ #define INIT_RCPI 180
+ 	struct mt76_phy *mphy = mconf->phy->mt76;
+@@ -2188,20 +2195,20 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
+ 	enum nl80211_band band = chandef->chan->band;
+ 	struct sta_rec_ra_uni *ra;
+ 	struct tlv *tlv;
+-	u32 supp_rate = sta->deflink.supp_rates[band];
+-	u32 cap = sta->wme ? STA_CAP_WMM : 0;
++	u32 supp_rate = link_sta->supp_rates[band];
++	u32 cap = link_sta->sta->wme ? STA_CAP_WMM : 0;
+ 
+ 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra));
+ 	ra = (struct sta_rec_ra_uni *)tlv;
+ 
+ 	ra->valid = true;
+ 	ra->auto_rate = true;
+-	ra->phy_mode = mt76_connac_get_phy_mode(mphy, conf->vif, band, &sta->deflink);
++	ra->phy_mode = mt76_connac_get_phy_mode(mphy, conf->vif, band, link_sta);
+ 	ra->channel = chandef->chan->hw_value;
+-	ra->bw = (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_320) ?
+-		 CMD_CBW_320MHZ : sta->deflink.bandwidth;
++	ra->bw = (link_sta->bandwidth == IEEE80211_STA_RX_BW_320) ?
++		 CMD_CBW_320MHZ : link_sta->bandwidth;
+ 	ra->phy.bw = ra->bw;
+-	ra->mmps_mode = mt7996_mcu_get_mmps_mode(sta->deflink.smps_mode);
++	ra->mmps_mode = mt7996_mcu_get_mmps_mode(link_sta->smps_mode);
+ 
+ 	if (supp_rate) {
+ 		supp_rate &= mask->control[band].legacy;
+@@ -2221,60 +2228,60 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
+ 		}
+ 	}
+ 
+-	if (sta->deflink.ht_cap.ht_supported) {
++	if (link_sta->ht_cap.ht_supported) {
+ 		ra->supp_mode |= MODE_HT;
+-		ra->af = sta->deflink.ht_cap.ampdu_factor;
+-		ra->ht_gf = !!(sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD);
++		ra->af = link_sta->ht_cap.ampdu_factor;
++		ra->ht_gf = !!(link_sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD);
+ 
+ 		cap |= STA_CAP_HT;
+-		if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
++		if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
+ 			cap |= STA_CAP_SGI_20;
+-		if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40)
++		if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40)
+ 			cap |= STA_CAP_SGI_40;
+-		if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)
++		if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)
+ 			cap |= STA_CAP_TX_STBC;
+-		if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
++		if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
+ 			cap |= STA_CAP_RX_STBC;
+ 		if (conf->ht_ldpc &&
+-		    (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING))
++		    (link_sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING))
+ 			cap |= STA_CAP_LDPC;
+ 
+-		mt7996_mcu_set_sta_ht_mcs(sta, ra->ht_mcs,
++		mt7996_mcu_set_sta_ht_mcs(link_sta, ra->ht_mcs,
+ 					  mask->control[band].ht_mcs);
+ 		ra->supp_ht_mcs = *(__le32 *)ra->ht_mcs;
+ 	}
+ 
+-	if (sta->deflink.vht_cap.vht_supported) {
++	if (link_sta->vht_cap.vht_supported) {
+ 		u8 af;
+ 
+ 		ra->supp_mode |= MODE_VHT;
+ 		af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK,
+-			       sta->deflink.vht_cap.cap);
++			       link_sta->vht_cap.cap);
+ 		ra->af = max_t(u8, ra->af, af);
+ 
+ 		cap |= STA_CAP_VHT;
+-		if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80)
++		if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80)
+ 			cap |= STA_CAP_VHT_SGI_80;
+-		if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160)
++		if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160)
+ 			cap |= STA_CAP_VHT_SGI_160;
+-		if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC)
++		if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC)
+ 			cap |= STA_CAP_VHT_TX_STBC;
+-		if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1)
++		if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1)
+ 			cap |= STA_CAP_VHT_RX_STBC;
+ 		if (conf->vht_ldpc &&
+-		    (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC))
++		    (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC))
+ 			cap |= STA_CAP_VHT_LDPC;
+ 
+-		mt7996_mcu_set_sta_vht_mcs(sta, ra->supp_vht_mcs,
++		mt7996_mcu_set_sta_vht_mcs(link_sta, ra->supp_vht_mcs,
+ 					   mask->control[band].vht_mcs);
+ 	}
+ 
+-	if (sta->deflink.he_cap.has_he) {
++	if (link_sta->he_cap.has_he) {
+ 		ra->supp_mode |= MODE_HE;
+ 		cap |= STA_CAP_HE;
+ 
+-		if (sta->deflink.he_6ghz_capa.capa)
+-			ra->af = le16_get_bits(sta->deflink.he_6ghz_capa.capa,
++		if (link_sta->he_6ghz_capa.capa)
++			ra->af = le16_get_bits(link_sta->he_6ghz_capa.capa,
+ 					       IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP);
+ 	}
+ 	ra->sta_cap = cpu_to_le32(cap);
+@@ -2285,14 +2292,14 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
+ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
+ 			     struct ieee80211_bss_conf *conf,
+ 			     struct mt7996_bss_conf *mconf,
+-			     struct ieee80211_sta *sta, bool changed)
++			     struct ieee80211_link_sta *link_sta,
++			     struct mt7996_link_sta *mlink, bool changed)
+ {
+-	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	struct sk_buff *skb;
+ 	int ret;
+ 
+ 	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
+-					      &msta->wcid,
++					      &mlink->wcid,
+ 					      MT7996_STA_UPDATE_MAX_SIZE);
+ 	if (IS_ERR(skb))
+ 		return PTR_ERR(skb);
+@@ -2302,26 +2309,27 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
+ 	 * update sta_rec_he here.
+ 	 */
+ 	if (changed)
+-		mt7996_mcu_sta_he_tlv(skb, conf, mconf, sta);
++		mt7996_mcu_sta_he_tlv(skb, conf, mconf, link_sta);
+ 
+ 	/* sta_rec_ra accommodates BW, NSS and only MCS range format
+ 	 * i.e 0-{7,8,9} for VHT.
+ 	 */
+-	mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, conf, mconf, sta);
++	mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, conf, mconf, link_sta);
+ 
+ 	ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ 				    MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+ 	if (ret)
+ 		return ret;
+ 
+-	return mt7996_mcu_add_rate_ctrl_fixed(dev, conf, mconf, sta);
++	return mt7996_mcu_add_rate_ctrl_fixed(dev, conf, mconf, link_sta, mlink);
+ }
+ 
+ static int
+-mt7996_mcu_sta_init_vow(struct mt7996_bss_conf *mconf, struct mt7996_sta *msta)
++mt7996_mcu_sta_init_vow(struct mt7996_bss_conf *mconf,
++			struct mt7996_link_sta *mlink)
+ {
+ 	struct mt7996_phy *phy = mconf->phy;
+-	struct mt7996_vow_sta_ctrl *vow = &msta->vow;
++	struct mt7996_vow_sta_ctrl *vow = &mlink->vow;
+ 	u8 omac_idx = mconf->mt76.omac_idx;
+ 	int ret;
+ 
+@@ -2339,33 +2347,28 @@ mt7996_mcu_sta_init_vow(struct mt7996_bss_conf *mconf, struct mt7996_sta *msta)
+ 	vow->drr_quantum[IEEE80211_AC_BE] = VOW_DRR_QUANTUM_IDX2;
+ 	vow->drr_quantum[IEEE80211_AC_BK] = VOW_DRR_QUANTUM_IDX2;
+ 
+-	ret = mt7996_mcu_set_vow_drr_ctrl(phy, mconf, msta, VOW_DRR_CTRL_STA_BSS_GROUP);
++	ret = mt7996_mcu_set_vow_drr_ctrl(phy, mconf, mlink, VOW_DRR_CTRL_STA_BSS_GROUP);
+ 	if (ret)
+ 		return ret;
+ 
+-	ret = mt7996_mcu_set_vow_drr_ctrl(phy, mconf, msta, VOW_DRR_CTRL_STA_PAUSE);
++	ret = mt7996_mcu_set_vow_drr_ctrl(phy, mconf, mlink, VOW_DRR_CTRL_STA_PAUSE);
+ 	if (ret)
+ 		return ret;
+ 
+-	return mt7996_mcu_set_vow_drr_ctrl(phy, mconf, msta, VOW_DRR_CTRL_STA_ALL);
++	return mt7996_mcu_set_vow_drr_ctrl(phy, mconf, mlink, VOW_DRR_CTRL_STA_ALL);
+ }
+ 
+ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+-		       struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta,
+-		       bool enable, bool newly)
++		       struct mt7996_bss_conf *mconf,
++		       struct ieee80211_link_sta *link_sta,
++		       struct mt7996_link_sta *mlink, bool enable, bool newly)
+ {
+ 	struct ieee80211_vif *vif = conf->vif;
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct ieee80211_link_sta *link_sta;
+-	struct mt7996_sta *msta;
+ 	struct sk_buff *skb;
+ 	int ret;
+ 
+-	msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta;
+-	link_sta = sta ? &sta->deflink : NULL;
+-
+ 	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
+-					      &msta->wcid,
++					      &mlink->wcid,
+ 					      MT7996_STA_UPDATE_MAX_SIZE);
+ 	if (IS_ERR(skb))
+ 		return PTR_ERR(skb);
+@@ -2378,37 +2381,37 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ 		goto out;
+ 
+ 	/* starec hdr trans */
+-	mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, sta);
++	mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, mlink);
+ 	/* starec tx proc */
+ 	mt7996_mcu_sta_tx_proc_tlv(skb);
+ 
+ 	/* tag order is in accordance with firmware dependency. */
+-	if (sta) {
++	if (link_sta) {
+ 		/* starec hdrt mode */
+ 		mt7996_mcu_sta_hdrt_tlv(dev, skb);
+ 		/* starec bfer */
+-		mt7996_mcu_sta_bfer_tlv(dev, skb, conf, mconf, sta);
++		mt7996_mcu_sta_bfer_tlv(dev, skb, conf, mconf, link_sta);
+ 		/* starec ht */
+-		mt7996_mcu_sta_ht_tlv(skb, sta);
++		mt7996_mcu_sta_ht_tlv(skb, link_sta);
+ 		/* starec vht */
+-		mt7996_mcu_sta_vht_tlv(skb, sta);
++		mt7996_mcu_sta_vht_tlv(skb, link_sta);
+ 		/* starec uapsd */
+-		mt76_connac_mcu_sta_uapsd(skb, vif, sta);
++		mt76_connac_mcu_sta_uapsd(skb, vif, link_sta->sta);
+ 		/* starec amsdu */
+-		mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, sta);
++		mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, link_sta, mlink);
+ 		/* starec he */
+-		mt7996_mcu_sta_he_tlv(skb, conf, mconf, sta);
++		mt7996_mcu_sta_he_tlv(skb, conf, mconf, link_sta);
+ 		/* starec he 6g*/
+-		mt7996_mcu_sta_he_6g_tlv(skb, sta);
++		mt7996_mcu_sta_he_6g_tlv(skb, link_sta);
+ 		/* starec eht */
+-		mt7996_mcu_sta_eht_tlv(skb, sta);
++		mt7996_mcu_sta_eht_tlv(skb, link_sta);
+ 		/* starec muru */
+-		mt7996_mcu_sta_muru_tlv(dev, skb, conf, mconf, sta);
++		mt7996_mcu_sta_muru_tlv(dev, skb, conf, mconf, link_sta);
+ 		/* starec bfee */
+-		mt7996_mcu_sta_bfee_tlv(dev, skb, conf, mconf, sta);
++		mt7996_mcu_sta_bfee_tlv(dev, skb, conf, mconf, link_sta);
+ 	}
+ 
+-	ret = mt7996_mcu_sta_init_vow(mconf, msta);
++	ret = mt7996_mcu_sta_init_vow(mconf, mlink);
+ 	if (ret) {
+ 		dev_kfree_skb(skb);
+ 		return ret;
+@@ -2484,16 +2487,16 @@ int mt7996_mcu_add_key(struct mt76_dev *dev, struct mt7996_bss_conf *mconf,
+ 
+ static int mt7996_mcu_get_pn(struct mt7996_dev *dev,
+ 			     struct ieee80211_bss_conf *conf,
+-			     struct mt7996_bss_conf *mconf, u8 *pn)
++			     struct mt7996_bss_conf *mconf,
++			     struct mt7996_link_sta *mlink, u8 *pn)
+ {
+ #define TSC_TYPE_BIGTK_PN 2
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)conf->vif->drv_priv;
+ 	struct sta_rec_pn_info *pn_info;
+ 	struct sk_buff *skb, *rskb;
+ 	struct tlv *tlv;
+ 	int ret;
+ 
+-	skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76, &mvif->sta.wcid);
++	skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76, &mlink->wcid);
+ 	if (IS_ERR(skb))
+ 		return PTR_ERR(skb);
+ 
+@@ -2520,6 +2523,7 @@ static int mt7996_mcu_get_pn(struct mt7996_dev *dev,
+ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev,
+ 			       struct ieee80211_bss_conf *conf,
+ 			       struct mt7996_bss_conf *mconf,
++			       struct mt7996_link_sta *mlink,
+ 			       struct ieee80211_key_conf *key)
+ {
+ 	struct mt7996_mcu_bcn_prot_tlv *bcn_prot;
+@@ -2538,7 +2542,7 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev,
+ 
+ 	bcn_prot = (struct mt7996_mcu_bcn_prot_tlv *)tlv;
+ 
+-	ret = mt7996_mcu_get_pn(dev, conf, mconf, pn);
++	ret = mt7996_mcu_get_pn(dev, conf, mconf, mlink, pn);
+ 	if (ret) {
+ 		dev_kfree_skb(skb);
+ 		return ret;
+@@ -4814,21 +4818,18 @@ int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev, bool disable
+ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ 				     struct ieee80211_vif *vif,
+ 				     struct mt7996_bss_conf *mconf,
+-				     struct ieee80211_sta *sta)
++				     struct mt7996_link_sta *mlink)
+ {
+-	struct mt7996_sta *msta;
+ 	struct sk_buff *skb;
+ 
+-	msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mconf->vif->sta;
+-
+ 	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
+-					      &msta->wcid,
++					      &mlink->wcid,
+ 					      MT7996_STA_UPDATE_MAX_SIZE);
+ 	if (IS_ERR(skb))
+ 		return PTR_ERR(skb);
+ 
+ 	/* starec hdr trans */
+-	mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, sta);
++	mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, mlink);
+ 	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ 				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+ }
+@@ -5017,7 +5018,7 @@ int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
+ 	switch (tag) {
+ 	case UNI_PER_STA_RSSI:
+ 		for (i = 0; i < sta_num; ++i) {
+-			struct mt7996_sta *msta;
++			struct mt7996_link_sta *mlink;
+ 			struct mt76_phy *phy;
+ 			s8 rssi[4];
+ 			u8 *rcpi;
+@@ -5031,10 +5032,10 @@ int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
+ 				rssi[2] = to_rssi(MT_PRXV_RCPI0, rcpi[2]);
+ 				rssi[3] = to_rssi(MT_PRXV_RCPI0, rcpi[3]);
+ 
+-				msta = container_of(wcid, struct mt7996_sta, wcid);
+-				phy = msta->vif->phy->mt76;
+-				msta->ack_signal = mt76_rx_signal(phy->antenna_mask, rssi);
+-				ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal);
++				mlink = container_of(wcid, struct mt7996_link_sta, wcid);
++				phy = mlink->sta->vif->deflink.phy->mt76;
++				mlink->ack_signal = mt76_rx_signal(phy->antenna_mask, rssi);
++				ewma_avg_signal_add(&mlink->avg_ack_signal, -mlink->ack_signal);
+ 			} else {
+ 				ret = -EINVAL;
+ 				dev_err(dev->dev, "Failed to update RSSI for "
+@@ -5056,7 +5057,7 @@ int mt7996_mcu_get_rssi(struct mt76_dev *dev)
+ {
+ 	u16 sta_list[PER_STA_INFO_MAX_NUM];
+ 	LIST_HEAD(sta_poll_list);
+-	struct mt7996_sta *msta;
++	struct mt7996_link_sta *mlink;
+ 	int i, ret;
+ 	bool empty = false;
+ 
+@@ -5076,13 +5077,13 @@ int mt7996_mcu_get_rssi(struct mt76_dev *dev)
+ 				empty = true;
+ 				break;
+ 			}
+-			msta = list_first_entry(&sta_poll_list,
+-			                        struct mt7996_sta,
++			mlink = list_first_entry(&sta_poll_list,
++			                        struct mt7996_link_sta,
+ 			                        wcid.poll_list);
+-			list_del_init(&msta->wcid.poll_list);
++			list_del_init(&mlink->wcid.poll_list);
+ 			spin_unlock_bh(&dev->sta_poll_lock);
+ 
+-			sta_list[i] = msta->wcid.idx;
++			sta_list[i] = mlink->wcid.idx;
+ 		}
+ 
+ 		ret = mt7996_mcu_get_per_sta_info(dev, UNI_PER_STA_RSSI,
+@@ -5372,10 +5373,18 @@ int mt7996_mcu_set_scs_stats(struct mt7996_phy *phy)
+ void mt7996_sta_rssi_work(void *data, struct ieee80211_sta *sta)
+ {
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_link_sta *mlink;
+ 	struct mt7996_phy *poll_phy = (struct mt7996_phy *) data;
+ 
+-	if (poll_phy->scs_ctrl.sta_min_rssi > msta->ack_signal)
+-		poll_phy->scs_ctrl.sta_min_rssi = msta->ack_signal;
++	mutex_lock(&poll_phy->dev->mt76.mutex);
++	mlink = mlink_dereference_protected(msta, 0);
++	if (!mlink)
++		goto out;
++
++	if (poll_phy->scs_ctrl.sta_min_rssi > mlink->ack_signal)
++		poll_phy->scs_ctrl.sta_min_rssi = mlink->ack_signal;
++out:
++	mutex_unlock(&poll_phy->dev->mt76.mutex);
+ }
+ 
+ void mt7996_mcu_scs_sta_poll(struct work_struct *work)
+@@ -5451,9 +5460,10 @@ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
+ 
+ int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy,
+ 				struct mt7996_bss_conf *mconf,
+-				struct mt7996_sta *msta, enum vow_drr_ctrl_id id)
++				struct mt7996_link_sta *mlink,
++				enum vow_drr_ctrl_id id)
+ {
+-	struct mt7996_vow_sta_ctrl *vow = msta ? &msta->vow : NULL;
++	struct mt7996_vow_sta_ctrl *vow = mlink ? &mlink->vow : NULL;
+ 	u32 val = 0;
+ 	struct {
+ 		u8 __rsv1[4];
+@@ -5475,11 +5485,11 @@ int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy,
+ 	} __packed req = {
+ 		.tag = cpu_to_le16(UNI_VOW_DRR_CTRL),
+ 		.len = cpu_to_le16(sizeof(req) - 4),
+-		.wlan_idx = cpu_to_le16(msta ? msta->wcid.idx : 0),
++		.wlan_idx = cpu_to_le16(mlink ? mlink->wcid.idx : 0),
+ 		.band_idx = phy->mt76->band_idx,
+-		.wmm_idx = msta ? mconf->mt76.wmm_idx : 0,
++		.wmm_idx = mlink ? mconf->mt76.wmm_idx : 0,
+ 		.ctrl_id = cpu_to_le32(id),
+-		.omac_idx = msta ? mconf->mt76.omac_idx : 0
++		.omac_idx = mlink ? mconf->mt76.omac_idx : 0
+ 	};
+ 
+ 	switch (id) {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 46964079..1732ff36 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -303,10 +303,10 @@ struct mt7996_vow_sta_ctrl {
+ 	u8 drr_quantum[IEEE80211_NUM_ACS];
+ };
+ 
+-struct mt7996_sta {
++struct mt7996_link_sta {
+ 	struct mt76_wcid wcid; /* must be first */
+ 
+-	struct mt7996_vif *vif;
++	struct mt7996_sta *sta;
+ 
+ 	struct list_head rc_list;
+ 
+@@ -325,6 +325,13 @@ struct mt7996_sta {
+ 	struct mt7996_vow_sta_ctrl vow;
+ };
+ 
++struct mt7996_sta {
++	struct mt7996_link_sta deflink;
++	struct mt7996_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
++
++	struct mt7996_vif *vif;
++};
++
+ struct mt7996_bss_conf {
+ 	struct mt76_vif mt76; /* must be first */
+ 
+@@ -784,6 +791,13 @@ mconf_dereference_protected(struct mt7996_vif *mvif, u8 link_id)
+ 					 lockdep_is_held(&mvif->dev->mt76.mutex));
+ }
+ 
++static inline struct mt7996_link_sta *
++mlink_dereference_protected(struct mt7996_sta *msta, u8 link_id)
++{
++	return rcu_dereference_protected(msta->link[link_id],
++					 lockdep_is_held(&msta->vif->dev->mt76.mutex));
++}
++
+ extern const struct ieee80211_ops mt7996_ops;
+ extern struct pci_driver mt7996_pci_driver;
+ extern struct pci_driver mt7996_hif_driver;
+@@ -828,10 +842,12 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
+ 			    struct mt7996_bss_conf *mconf, bool enable);
+ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
+ 			    struct ieee80211_bss_conf *conf,
+-			    struct mt7996_bss_conf *mconf, int enable);
++			    struct mt7996_bss_conf *mconf,
++			    struct mt7996_link_sta *mlink, int enable);
+ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+-		       struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta,
+-		       bool enable, bool newly);
++		       struct mt7996_bss_conf *mconf,
++		       struct ieee80211_link_sta *link_sta,
++		       struct mt7996_link_sta *mlink, bool enable, bool newly);
+ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ 			 struct ieee80211_ampdu_params *params,
+ 			 bool add);
+@@ -853,7 +869,8 @@ int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy,
+ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
+ 			     struct ieee80211_bss_conf *conf,
+ 			     struct mt7996_bss_conf *mconf,
+-			     struct ieee80211_sta *sta, bool changed);
++			     struct ieee80211_link_sta *link_sta,
++			     struct mt7996_link_sta *mlink, bool changed);
+ int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef);
+ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag);
+ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf);
+@@ -861,7 +878,9 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
+ 				   void *data, u16 version);
+ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
+ 			       struct mt7996_bss_conf *mconf,
+-			       struct ieee80211_sta *sta, void *data, u32 field);
++			       struct ieee80211_link_sta *link_sta,
++			       struct mt7996_link_sta *mlink, void *data,
++			       u32 field);
+ int mt7996_mcu_set_eeprom(struct mt7996_dev *dev);
+ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf);
+ int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num);
+@@ -917,7 +936,8 @@ void mt7996_mcu_scs_sta_poll(struct work_struct *work);
+ int mt7996_mcu_set_band_confg(struct mt7996_phy *phy, u16 option, bool enable);
+ int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy,
+ 				struct mt7996_bss_conf *mconf,
+-				struct mt7996_sta *msta, enum vow_drr_ctrl_id id);
++				struct mt7996_link_sta *mlink,
++				enum vow_drr_ctrl_id id);
+ int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy);
+ void mt7996_mcu_wmm_pbc_work(struct work_struct *work);
+ 
+@@ -984,7 +1004,7 @@ void mt7996_mac_reset_counters(struct mt7996_phy *phy);
+ void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy);
+ void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band);
+ void mt7996_mac_enable_rtscts(struct mt7996_dev *dev,
+-			      struct ieee80211_vif *vif, bool enable);
++			      struct mt7996_link_sta *mlink, bool enable);
+ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ 			   struct sk_buff *skb, struct mt76_wcid *wcid,
+ 			   struct ieee80211_key_conf *key, int pid,
+@@ -1002,8 +1022,7 @@ void mt7996_mac_dump_work(struct work_struct *work);
+ void mt7996_mac_sta_rc_work(struct work_struct *work);
+ void mt7996_mac_update_stats(struct mt7996_phy *phy);
+ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
+-				  struct mt7996_sta *msta,
+-				  u8 flowid);
++				  struct mt7996_link_sta *mlink, u8 flowid);
+ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+ 			      struct ieee80211_sta *sta,
+ 			      struct ieee80211_twt_setup *twt);
+@@ -1032,11 +1051,12 @@ int mt7996_mcu_add_key(struct mt76_dev *dev, struct mt7996_bss_conf *mconf,
+ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev,
+ 			       struct ieee80211_bss_conf *conf,
+ 			       struct mt7996_bss_conf *mconf,
++			       struct mt7996_link_sta *mlink,
+ 			       struct ieee80211_key_conf *key);
+ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ 				     struct ieee80211_vif *vif,
+ 				     struct mt7996_bss_conf *mconf,
+-				     struct ieee80211_sta *sta);
++				     struct mt7996_link_sta *mlink);
+ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode);
+ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap);
+ #ifdef CONFIG_MAC80211_DEBUGFS
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index bf55b430..ba17f947 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -235,8 +235,8 @@ mt7996_tm_init(struct mt7996_phy *phy, bool en)
+ 
+ 	mt7996_tm_rf_switch_mode(dev, rf_test_mode);
+ 
+-	mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, en);
+-	mt7996_mcu_add_sta(dev, &phy->monitor_vif->bss_conf, &mvif->deflink, NULL, en, false);
++	mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, &mvif->sta.deflink, en);
++	mt7996_mcu_add_sta(dev, &phy->monitor_vif->bss_conf, &mvif->deflink, NULL, &mvif->sta.deflink, en, false);
+ 
+ 	mt7996_tm_set(dev, SET_ID(BAND_IDX), phy->mt76->band_idx);
+ 
+@@ -1186,7 +1186,7 @@ mt7996_tm_txbf_init(struct mt7996_phy *phy, u16 *val)
+ 	phy->omac_mask |= BIT_ULL(mvif->deflink.mt76.omac_idx);
+ 
+ 	mt7996_mcu_add_dev_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, true);
+-	mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, true);
++	mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, &mvif->sta.deflink, true);
+ 
+ 	if (td->ibf) {
+ 		if (td->is_txbf_dut) {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0091-wifi-mt76-mt7996-introduce-mt7996_band_phy-for-ch-ba.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0091-wifi-mt76-mt7996-introduce-mt7996_band_phy-for-ch-ba.patch
deleted file mode 100644
index 45d16d3..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0091-wifi-mt76-mt7996-introduce-mt7996_band_phy-for-ch-ba.patch
+++ /dev/null
@@ -1,271 +0,0 @@
-From 5aa4150e28fdef37bedc7231913a17c260b7aef0 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Fri, 1 Dec 2023 16:01:53 +0800
-Subject: [PATCH 091/116] wifi: mt76: mt7996: introduce mt7996_band_phy() for
- ch band and phy mapping
-
-For MLO devices, one ieee80211_hw can be mapped to several bands, and
-thus several mt76_phy. Add mt7996_band_phy() to temporarily do the
-mapping.
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/main.c   | 149 +++++++++++++++++++++++++++++-------------------
- mt7996/mt7996.h |  22 +++++++
- 2 files changed, 112 insertions(+), 59 deletions(-)
-
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 37e065f..e443761 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -41,9 +41,8 @@ static void mt7996_testmode_disable_all(struct mt7996_dev *dev)
- int mt7996_run(struct ieee80211_hw *hw)
- {
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
--	struct mt7996_phy *phy = mt7996_hw_phy(hw);
- 	bool running;
--	int ret;
-+	int band, ret;
- 
- 	running = mt7996_dev_running(dev);
- 	if (!running) {
-@@ -62,66 +61,76 @@ int mt7996_run(struct ieee80211_hw *hw)
- 
- 	mt7996_testmode_disable_all(dev);
- 
--	mt7996_mac_enable_nf(dev, phy->mt76->band_idx);
-+	for (band = 0; band < NUM_NL80211_BANDS; band++) {
-+		struct mt7996_phy *phy;
- 
--	ret = mt7996_mcu_set_rts_thresh(phy, 0x92b);
--	if (ret)
--		goto out;
-+		if (!hw->wiphy->bands[band])
-+			continue;
- 
--	ret = mt7996_mcu_set_radio_en(phy, true);
--	if (ret)
--		goto out;
-+		phy = mt7996_band_phy(hw, band);
- 
--	ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_RX_PATH);
--	if (ret)
--		goto out;
-+		if (!phy || test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
-+			continue;
- 
--	/* set a parking channel */
--	ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
--	if (ret)
--		goto out;
-+		mt7996_mac_enable_nf(dev, phy->mt76->band_idx);
- 
--	ret = mt7996_mcu_set_thermal_throttling(phy, MT7996_THERMAL_THROTTLE_MAX);
--	if (ret)
--		goto out;
-+		ret = mt7996_mcu_set_rts_thresh(phy, 0x92b);
-+		if (ret)
-+			goto out;
- 
--	ret = mt7996_mcu_set_thermal_protect(phy, true);
--	if (ret)
--		goto out;
-+		ret = mt7996_mcu_set_radio_en(phy, true);
-+		if (ret)
-+			goto out;
- 
--	ret = mt7996_mcu_set_scs(phy, SCS_ENABLE);
--	if (ret)
--		goto out;
-+		ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_RX_PATH);
-+		if (ret)
-+			goto out;
- 
--#ifdef CONFIG_MTK_DEBUG
--	phy->sr_enable = true;
--	phy->enhanced_sr_enable = true;
--	phy->thermal_protection_enable = true;
-+		/* set a parking channel */
-+		ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
-+		if (ret)
-+			goto out;
- 
--	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
--						dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
-+		ret = mt7996_mcu_set_thermal_throttling(phy, MT7996_THERMAL_THROTTLE_MAX);
-+		if (ret)
-+			goto out;
-+
-+		ret = mt7996_mcu_set_thermal_protect(phy, true);
-+		if (ret)
-+			goto out;
-+
-+		ret = mt7996_mcu_set_scs(phy, SCS_ENABLE);
-+		if (ret)
-+			goto out;
- 
--	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
--						dev->dbg.sku_disable ? 0 : phy->sku_path_en);
-+#ifdef CONFIG_MTK_DEBUG
-+		phy->sr_enable = true;
-+		phy->enhanced_sr_enable = true;
-+		phy->thermal_protection_enable = true;
-+		ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
-+						   dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
-+
-+		ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
-+						   dev->dbg.sku_disable ? 0 : phy->sku_path_en);
- #else
--	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
--						phy->sku_limit_en);
--	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
--						phy->sku_path_en);
-+		ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
-+						   phy->sku_limit_en);
-+		ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
-+						   phy->sku_path_en);
- #endif
--	if (ret)
--		goto out;
--
--	set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
-+		if (ret)
-+			goto out;
- 
--	ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
--				     MT7996_WATCHDOG_TIME);
-+		set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
- 
--	ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
-+		ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
-+					     MT7996_WATCHDOG_TIME);
- 
--	if (!running)
--		mt7996_mac_reset_counters(phy);
-+		if (!running)
-+			mt7996_mac_reset_counters(phy);
-+	}
- 
-+	ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
- out:
- 	return ret;
- }
-@@ -143,18 +152,29 @@ static int mt7996_start(struct ieee80211_hw *hw)
- static void mt7996_stop(struct ieee80211_hw *hw)
- {
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
--	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	int band;
- 
--	cancel_delayed_work_sync(&phy->mt76->mac_work);
- 	cancel_delayed_work_sync(&dev->scs_work);
- 
--	mutex_lock(&dev->mt76.mutex);
-+	for (band = 0; band < NUM_NL80211_BANDS; band++) {
-+		struct mt7996_phy *phy;
- 
--	mt7996_mcu_set_radio_en(phy, false);
-+		if (!hw->wiphy->bands[band])
-+			continue;
- 
--	clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
-+		phy = mt7996_band_phy(hw, band);
- 
--	mutex_unlock(&dev->mt76.mutex);
-+		if (!phy || !test_bit(MT76_STATE_RUNNING, &phy->mt76->state) ||
-+		    (phy->chanctx && phy->chanctx->nbss_assigned))
-+			continue;
-+
-+		cancel_delayed_work_sync(&phy->mt76->mac_work);
-+
-+		mutex_lock(&dev->mt76.mutex);
-+		mt7996_mcu_set_radio_en(phy, false);
-+		clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
-+		mutex_unlock(&dev->mt76.mutex);
-+	}
- }
- 
- static inline int get_free_idx(u32 mask, u8 start, u8 end)
-@@ -2057,7 +2077,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	       struct ieee80211_scan_request *hw_req)
- {
- 	struct cfg80211_scan_request *req = &hw_req->req;
--	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct mt7996_phy *phy = mt7996_band_phy(hw, req->channels[0]->band);
- 
- 	mutex_lock(&phy->dev->mt76.mutex);
- 	if (WARN_ON(phy->scan_req || phy->scan_chan)) {
-@@ -2079,19 +2099,30 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- static void
- mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
- {
--	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	int band;
- 
--	cancel_delayed_work_sync(&phy->scan_work);
-+	for (band = 0; band < NUM_NL80211_BANDS; band++) {
-+		struct mt7996_phy *phy;
- 
--	mutex_lock(&phy->dev->mt76.mutex);
--	mt7996_scan_complete(phy, true);
--	mutex_unlock(&phy->dev->mt76.mutex);
-+		if (!hw->wiphy->bands[band])
-+			continue;
-+
-+		phy = mt7996_band_phy(hw, band);
-+		if (!(test_bit(MT76_SCANNING, &phy->mt76->state)))
-+			continue;
-+
-+		cancel_delayed_work_sync(&phy->scan_work);
-+
-+		mutex_lock(&phy->dev->mt76.mutex);
-+		mt7996_scan_complete(phy, true);
-+		mutex_unlock(&phy->dev->mt76.mutex);
-+	}
- }
- 
- static int
- mt7996_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
- {
--	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct mt7996_phy *phy = mt7996_band_phy(hw, conf->def.chan->band);
- 	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
- 	int ret;
- 
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 913aff1..4c090ba 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -788,6 +788,28 @@ mt7996_get_background_radar_cap(struct mt7996_dev *dev)
- 	return 1;
- }
- 
-+static inline struct mt7996_phy *
-+mt7996_band_phy(struct ieee80211_hw *hw, enum nl80211_band band)
-+{
-+	struct mt76_phy *phy = hw->priv;
-+
-+	if (!(hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO))
-+		return phy->priv;
-+
-+	/* TODO: mlo: temporarily hardcode */
-+	if (band == NL80211_BAND_6GHZ)
-+		phy = phy->dev->phys[MT_BAND2];
-+	else if (band == NL80211_BAND_5GHZ)
-+		phy = phy->dev->phys[MT_BAND1];
-+	else
-+		phy = phy->dev->phys[MT_BAND0];
-+
-+	if (!phy)
-+		phy = hw->priv;
-+
-+	return phy->priv;
-+}
-+
- static inline struct mt7996_chanctx *
- mt7996_chanctx_get(struct ieee80211_chanctx_conf *ctx)
- {
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0092-mtk-mt76-extend-wcid-and-sta-flow-for-MLO-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0092-mtk-mt76-extend-wcid-and-sta-flow-for-MLO-support.patch
new file mode 100644
index 0000000..5ca3404
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0092-mtk-mt76-extend-wcid-and-sta-flow-for-MLO-support.patch
@@ -0,0 +1,50 @@
+From f9ceb3601873e92e296383b3f8f124cf7d84e6fe Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 29 Nov 2023 11:04:50 +0800
+Subject: [PATCH 092/199] mtk: mt76: extend wcid and sta flow for MLO support
+
+Add link related info to wcid, and split sta connection flow of common
+parts for MLO supported chipsets.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mac80211.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index 032a13a5..49834afe 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -1368,6 +1368,9 @@ mt76_sta_add(struct mt76_phy *phy, struct ieee80211_vif *vif,
+ 	if (ret)
+ 		goto out;
+ 
++	if (phy->hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO)
++		goto out;
++
+ 	for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
+ 		struct mt76_txq *mtxq;
+ 
+@@ -1397,12 +1400,15 @@ void __mt76_sta_remove(struct mt76_dev *dev, struct ieee80211_vif *vif,
+ 	struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv;
+ 	int i, idx = wcid->idx;
+ 
+-	for (i = 0; i < ARRAY_SIZE(wcid->aggr); i++)
++	for (i = 0; !sta->valid_links && i < ARRAY_SIZE(wcid->aggr); i++)
+ 		mt76_rx_aggr_stop(dev, wcid, i);
+ 
+ 	if (dev->drv->sta_remove)
+ 		dev->drv->sta_remove(dev, vif, sta);
+ 
++	if (sta->valid_links)
++		return;
++
+ 	mt76_wcid_cleanup(dev, wcid);
+ 
+ 	mt76_wcid_mask_clear(dev->wcid_mask, idx);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0092-wifi-mt76-mt7996-rework-ieee80211_ops-callbacks-for-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0092-wifi-mt76-mt7996-rework-ieee80211_ops-callbacks-for-.patch
deleted file mode 100644
index 13bff79..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0092-wifi-mt76-mt7996-rework-ieee80211_ops-callbacks-for-.patch
+++ /dev/null
@@ -1,501 +0,0 @@
-From 31581304bf759bc9d85325c233731aaa5667d311 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Fri, 1 Dec 2023 17:26:43 +0800
-Subject: [PATCH 092/116] wifi: mt76: mt7996: rework ieee80211_ops callbacks
- for link consideration
-
-Extend ieee80211 callback functions to support multi-link operation.
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/main.c | 313 ++++++++++++++++++++++++++++++++------------------
- 1 file changed, 204 insertions(+), 109 deletions(-)
-
-diff --git a/mt7996/main.c b/mt7996/main.c
-index e443761..f5658d4 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -568,7 +568,6 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
- 			  struct ieee80211_key_conf *key)
- {
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
--	struct mt7996_phy *phy = mt7996_hw_phy(hw);
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_sta *msta = sta ? (struct mt7996_sta *)sta->drv_priv :
- 				  &mvif->sta;
-@@ -578,70 +577,77 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
- 	u8 *wcid_keyidx;
- 	int idx = key->keyidx;
- 	int err = 0;
-+	unsigned long add;
-+	unsigned int link_id;
- 
--	/* The hardware does not support per-STA RX GTK, fallback
--	 * to software mode for these.
--	 */
--	if ((vif->type == NL80211_IFTYPE_ADHOC ||
--	     vif->type == NL80211_IFTYPE_MESH_POINT) &&
--	    (key->cipher == WLAN_CIPHER_SUITE_TKIP ||
--	     key->cipher == WLAN_CIPHER_SUITE_CCMP) &&
--	    !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
--		return -EOPNOTSUPP;
-+	if (key->link_id >= 0) {
-+		add = BIT(key->link_id);
-+	} else {
-+		if (sta)
-+			add = sta->valid_links ?: BIT(0);
-+		else
-+			add = vif->valid_links ?: BIT(0);
-+	}
- 
- 	mutex_lock(&dev->mt76.mutex);
--	conf = link_conf_dereference_protected(vif, 0);
--	mconf = mconf_dereference_protected(mvif, 0);
--	mlink = mlink_dereference_protected(msta, 0);
--	wcid_keyidx = &mlink->wcid.hw_key_idx;
--
--	/* fall back to sw encryption for unsupported ciphers */
--	switch (key->cipher) {
--	case WLAN_CIPHER_SUITE_TKIP:
--	case WLAN_CIPHER_SUITE_CCMP:
--	case WLAN_CIPHER_SUITE_CCMP_256:
--	case WLAN_CIPHER_SUITE_GCMP:
--	case WLAN_CIPHER_SUITE_GCMP_256:
--	case WLAN_CIPHER_SUITE_SMS4:
--		break;
--	case WLAN_CIPHER_SUITE_AES_CMAC:
--	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
--	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
--	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
--		if (key->keyidx == 6 || key->keyidx == 7) {
--			wcid_keyidx = &wcid->hw_key_idx2;
--			key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE;
-+
-+	for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
-+		conf = link_conf_dereference_protected(vif, link_id);
-+		mconf = mconf_dereference_protected(mvif, link_id);
-+		mlink = mlink_dereference_protected(msta, link_id);
-+		wcid_keyidx = &mlink->wcid.hw_key_idx;
-+
-+		if (!conf || !mconf || !mlink)
-+			continue;
-+
-+		/* fall back to sw encryption for unsupported ciphers */
-+		switch (key->cipher) {
-+		case WLAN_CIPHER_SUITE_TKIP:
-+		case WLAN_CIPHER_SUITE_CCMP:
-+		case WLAN_CIPHER_SUITE_CCMP_256:
-+		case WLAN_CIPHER_SUITE_GCMP:
-+		case WLAN_CIPHER_SUITE_GCMP_256:
-+		case WLAN_CIPHER_SUITE_SMS4:
- 			break;
-+		case WLAN_CIPHER_SUITE_AES_CMAC:
-+		case WLAN_CIPHER_SUITE_BIP_CMAC_256:
-+		case WLAN_CIPHER_SUITE_BIP_GMAC_128:
-+		case WLAN_CIPHER_SUITE_BIP_GMAC_256:
-+			if (key->keyidx == 6 || key->keyidx == 7) {
-+				wcid_keyidx = &mlink->wcid.hw_key_idx2;
-+				key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE;
-+				break;
-+			}
-+			fallthrough;
-+		case WLAN_CIPHER_SUITE_WEP40:
-+		case WLAN_CIPHER_SUITE_WEP104:
-+		default:
-+			mutex_unlock(&dev->mt76.mutex);
-+			return -EOPNOTSUPP;
- 		}
--		fallthrough;
--	case WLAN_CIPHER_SUITE_WEP40:
--	case WLAN_CIPHER_SUITE_WEP104:
--	default:
--		mutex_unlock(&dev->mt76.mutex);
--		return -EOPNOTSUPP;
--	}
- 
--	if (cmd == SET_KEY && !sta && !mconf->mt76.cipher) {
--		mconf->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher);
--		mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, true);
--	}
-+		if (cmd == SET_KEY && !sta && !mconf->mt76.cipher) {
-+			mconf->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher);
-+			mt7996_mcu_add_bss_info(mconf->phy, conf, mconf, mlink, true);
-+		}
- 
--	if (cmd == SET_KEY) {
--		*wcid_keyidx = idx;
--	} else {
--		if (idx == *wcid_keyidx)
--			*wcid_keyidx = -1;
--		goto out;
--	}
-+		if (cmd == SET_KEY) {
-+			*wcid_keyidx = idx;
-+		} else {
-+			if (idx == *wcid_keyidx)
-+				*wcid_keyidx = -1;
-+			goto out;
-+		}
- 
--	mt76_wcid_key_setup(&dev->mt76, &mlink->wcid, key);
-+		mt76_wcid_key_setup(&dev->mt76, &mlink->wcid, key);
- 
--	if (key->keyidx == 6 || key->keyidx == 7)
--		err = mt7996_mcu_bcn_prot_enable(dev, conf, mconf, mlink, key);
--	else
--		err = mt7996_mcu_add_key(&dev->mt76, mconf, key,
--					 MCU_WMWA_UNI_CMD(STA_REC_UPDATE),
--					 &mlink->wcid, cmd);
-+		if (key->keyidx == 6 || key->keyidx == 7)
-+			err = mt7996_mcu_bcn_prot_enable(dev, conf, mconf, mlink, key);
-+		else
-+			err = mt7996_mcu_add_key(&dev->mt76, mconf, key,
-+						 MCU_WMWA_UNI_CMD(STA_REC_UPDATE),
-+						 &mlink->wcid, cmd);
-+	}
- out:
- 	mutex_unlock(&dev->mt76.mutex);
- 
-@@ -697,7 +703,11 @@ mt7996_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	};
- 
- 	mutex_lock(&dev->mt76.mutex);
--	mconf = mconf_dereference_protected(mvif, 0);
-+	mconf = mconf_dereference_protected(mvif, link_id);
-+	if (!mconf) {
-+		mutex_unlock(&dev->mt76.mutex);
-+		return -EINVAL;
-+	}
- 
- 	/* firmware uses access class index */
- 	mconf->queue_params[mq_to_aci[queue]] = *params;
-@@ -840,19 +850,26 @@ mt7996_update_mu_group(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
- static void mt7996_vif_cfg_changed(struct ieee80211_hw *hw,
- 				   struct ieee80211_vif *vif, u64 changed)
- {
--	struct mt7996_phy *phy = mt7996_hw_phy(hw);
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
- 	if (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) {
--		struct ieee80211_bss_conf *conf = &vif->bss_conf;
- 		struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--		struct mt7996_bss_conf *mconf = mconf_dereference_protected(mvif, 0);
--		struct mt7996_link_sta *mlink = mlink_dereference_protected(&mvif->sta, 0);
--
--		mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, true);
--		mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, true, false);
-+		unsigned long valid_links = vif->valid_links ?: BIT(0);
-+		unsigned int link_id;
-+
-+		for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
-+			struct ieee80211_bss_conf *conf =
-+				link_conf_dereference_protected(vif, link_id);
-+			struct mt7996_bss_conf *mconf =
-+				mconf_dereference_protected(mvif, link_id);
-+			struct mt7996_link_sta *mlink =
-+				mlink_dereference_protected(&mvif->sta, link_id);
-+
-+			mt7996_mcu_add_bss_info(mconf->phy, conf, mconf, mlink, true);
-+			mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, true, false);
-+		}
- 	}
- 
- 	mutex_unlock(&dev->mt76.mutex);
-@@ -871,8 +888,13 @@ static void mt7996_link_info_changed(struct ieee80211_hw *hw,
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
--	mconf = mconf_dereference_protected(mvif, 0);
--	mlink = mlink_dereference_protected(&mvif->sta, 0);
-+	mconf = mconf_dereference_protected(mvif, info->link_id);
-+	mlink = mlink_dereference_protected(&mvif->sta, info->link_id);
-+	if (!mconf || !mlink)
-+		goto out;
-+
-+	if (mconf->phy)
-+		phy = mconf->phy;
- 	/* station mode uses BSSID to map the wlan entry to a peer,
- 	 * and then peer references bss_info_rfch to set bandwidth cap.
- 	 */
-@@ -928,6 +950,7 @@ static void mt7996_link_info_changed(struct ieee80211_hw *hw,
- 	if (changed & BSS_CHANGED_MU_GROUPS)
- 		mt7996_update_mu_group(hw, info, mconf);
- 
-+out:
- 	mutex_unlock(&dev->mt76.mutex);
- }
- 
-@@ -938,13 +961,22 @@ mt7996_channel_switch_beacon(struct ieee80211_hw *hw,
- {
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	struct mt7996_bss_conf *mconf;
--	struct ieee80211_bss_conf *conf;
-+	struct mt7996_phy *phy = mt7996_band_phy(hw, chandef->chan->band);
-+	unsigned long valid_links = vif->valid_links ?: BIT(0);
-+	unsigned int link_id;
- 
- 	mutex_lock(&dev->mt76.mutex);
--	mconf = mconf_dereference_protected(mvif, 0);
--	conf = link_conf_dereference_protected(vif, 0);
--	mt7996_mcu_add_beacon(hw, conf, mconf, true);
-+	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
-+		struct mt7996_bss_conf *mconf =
-+			mconf_dereference_protected(mvif, link_id);
-+		struct ieee80211_bss_conf *conf =
-+			link_conf_dereference_protected(vif, link_id);
-+
-+		if (!mconf || phy != mconf->phy)
-+			continue;
-+
-+		mt7996_mcu_add_beacon(hw, conf, mconf, true);
-+	}
- 	mutex_unlock(&dev->mt76.mutex);
- }
- 
-@@ -1216,34 +1248,74 @@ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 	mt7996_mac_sta_remove_links(dev, vif, sta, rem);
- }
- 
-+static void
-+mt7996_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-+			  struct ieee80211_sta *sta)
-+{
-+	struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	unsigned long rem = sta->valid_links ?: BIT(0);
-+	unsigned int link_id;
-+
-+	mutex_lock(&dev->mt76.mutex);
-+	spin_lock_bh(&dev->mt76.status_lock);
-+	for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
-+		struct mt7996_link_sta *mlink =
-+			mlink_dereference_protected(msta, link_id);
-+
-+		rcu_assign_pointer(dev->mt76.wcid[mlink->wcid.idx], NULL);
-+	}
-+	spin_unlock_bh(&dev->mt76.status_lock);
-+	mutex_unlock(&dev->mt76.mutex);
-+}
-+
- static void mt7996_tx(struct ieee80211_hw *hw,
- 		      struct ieee80211_tx_control *control,
- 		      struct sk_buff *skb)
- {
--	struct mt7996_dev *dev = mt7996_hw_dev(hw);
--	struct mt76_phy *mphy = hw->priv;
-+	struct mt76_phy *mphy;
- 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- 	struct ieee80211_vif *vif = info->control.vif;
--	struct mt76_wcid *wcid = &dev->mt76.global_wcid;
--	struct mt7996_link_sta *mlink;
-+	struct mt76_wcid *wcid;
-+	struct mt7996_vif *mvif;
-+	struct mt7996_sta *msta;
- 
- 	if (control->sta) {
--		struct mt7996_sta *msta;
--
- 		msta = (struct mt7996_sta *)control->sta->drv_priv;
--		mlink = rcu_dereference(msta->link[0]);
--		wcid = &mlink->wcid;
-+		mvif = msta->vif;
-+	} else if (vif) {
-+		mvif = (struct mt7996_vif *)vif->drv_priv;
-+		msta = &mvif->sta;
- 	}
- 
--	if (vif && !control->sta) {
--		struct mt7996_vif *mvif;
-+	rcu_read_lock();
-+	if (mvif && msta) {
-+		struct mt7996_bss_conf *mconf;
-+		struct mt7996_link_sta *mlink;
-+
-+		u8 link_id = u32_get_bits(info->control.flags,
-+					  IEEE80211_TX_CTRL_MLO_LINK);
- 
--		mvif = (struct mt7996_vif *)vif->drv_priv;
--		mlink = rcu_dereference(mvif->sta.link[0]);
-+		if (link_id >= IEEE80211_LINK_UNSPECIFIED)
-+			link_id = mvif->master_link_id;
-+
-+		mconf = rcu_dereference(mvif->link[link_id]);
-+		mlink = rcu_dereference(msta->link[link_id]);
-+
-+		if (!mconf || !mlink)
-+			goto unlock;
-+
-+		mphy = mconf->phy->mt76;
- 		wcid = &mlink->wcid;
-+	} else {
-+		struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+
-+		mphy = hw->priv;
-+		wcid = &dev->mt76.global_wcid;
- 	}
- 
- 	mt76_tx(mphy, control->sta, wcid, skb);
-+	rcu_read_unlock();
- }
- 
- static int mt7996_set_rts_threshold(struct ieee80211_hw *hw, u32 val)
-@@ -1279,7 +1351,7 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	mtxq = (struct mt76_txq *)txq->drv_priv;
- 
- 	mutex_lock(&dev->mt76.mutex);
--	mlink = mlink_dereference_protected(msta, 0);
-+	mlink = mlink_dereference_protected(msta, msta->pri_link);
- 	switch (action) {
- 	case IEEE80211_AMPDU_RX_START:
- 		mt76_rx_aggr_start(&dev->mt76, &mlink->wcid, tid, ssn,
-@@ -1494,7 +1566,7 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
- 
- 	/* TODO: support per-link rate report */
- 	mutex_lock(&dev->mt76.mutex);
--	mlink = mlink_dereference_protected(msta, 0);
-+	mlink = mlink_dereference_protected(msta, msta->pri_link);
- 	if (!mlink)
- 		goto out;
- 
-@@ -1554,7 +1626,7 @@ static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta)
- 	u32 *changed = data;
- 
- 	rcu_read_lock();
--	mlink = rcu_dereference(msta->link[0]);
-+	mlink = rcu_dereference(msta->link[msta->pri_link]);
- 
- 	spin_lock_bh(&dev->mt76.sta_poll_lock);
- 	mlink->changed |= *changed;
-@@ -1621,19 +1693,26 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw,
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
--	struct mt7996_bss_conf *mconf;
--	struct mt7996_link_sta *mlink;
-+	unsigned long valid_links = sta->valid_links ?: BIT(0);
-+	unsigned int link_id;
- 
- 	mutex_lock(&dev->mt76.mutex);
--	mconf = mconf_dereference_protected(mvif, 0);
--	mlink = mlink_dereference_protected(msta, 0);
-+	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
-+		struct mt7996_bss_conf *mconf =
-+			mconf_dereference_protected(mvif, link_id);
-+		struct mt7996_link_sta *mlink =
-+			mlink_dereference_protected(msta, link_id);
- 
--	if (enabled)
--		set_bit(MT_WCID_FLAG_4ADDR, &mlink->wcid.flags);
--	else
--		clear_bit(MT_WCID_FLAG_4ADDR, &mlink->wcid.flags);
-+		if (!mconf || !mlink)
-+			continue;
-+
-+		if (enabled)
-+			set_bit(MT_WCID_FLAG_4ADDR, &mlink->wcid.flags);
-+		else
-+			clear_bit(MT_WCID_FLAG_4ADDR, &mlink->wcid.flags);
- 
--	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
-+		mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
-+	}
- 	mutex_unlock(&dev->mt76.mutex);
- }
- 
-@@ -1645,19 +1724,26 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw,
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
--	struct mt7996_bss_conf *mconf;
--	struct mt7996_link_sta *mlink;
-+	unsigned long valid_links = sta->valid_links ?: BIT(0);
-+	unsigned int link_id;
- 
- 	mutex_lock(&dev->mt76.mutex);
--	mconf = mconf_dereference_protected(mvif, 0);
--	mlink = mlink_dereference_protected(msta, 0);
-+	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
-+		struct mt7996_bss_conf *mconf =
-+			mconf_dereference_protected(mvif, link_id);
-+		struct mt7996_link_sta *mlink =
-+			mlink_dereference_protected(msta, link_id);
- 
--	if (enabled)
--		set_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
--	else
--		clear_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
-+		if (!mconf || !mlink)
-+			continue;
-+
-+		if (enabled)
-+			set_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
-+		else
-+			clear_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
- 
--	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
-+		mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
-+	}
- 	mutex_unlock(&dev->mt76.mutex);
- }
- 
-@@ -1790,9 +1876,13 @@ static void mt7996_ethtool_worker(void *wi_data, struct ieee80211_sta *sta)
- {
- 	struct mt76_ethtool_worker_info *wi = wi_data;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
--	struct mt7996_link_sta *mlink = &msta->deflink;
-+	struct mt7996_link_sta *mlink;
-+	struct mt7996_bss_conf *mconf;
-+
-+	mlink = mlink_dereference_protected(msta, msta->pri_link);
-+	mconf = mconf_dereference_protected(msta->vif, msta->pri_link);
- 
--	if (msta->vif->deflink.mt76.idx != wi->idx)
-+	if (mconf->mt76.idx != wi->idx)
- 		return;
- 
- 	mt76_ethtool_worker(wi, &mlink->wcid.stats, true);
-@@ -2024,12 +2114,13 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
- 			     struct net_device_path *path)
- {
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	struct mt7996_bss_conf *mconf = &mvif->deflink;
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
--	struct mt7996_link_sta *mlink = &msta->deflink;
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
- 	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
-+	struct mt7996_bss_conf *mconf;
-+	struct mt7996_link_sta *mlink;
-+	u8 link_id;
- 
- 	if (dev->hif2) {
- 		switch (dev->option_type) {
-@@ -2049,6 +2140,10 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
- 	if (!mtk_wed_device_active(wed))
- 		return -ENODEV;
- 
-+	link_id = msta->pri_link;
-+	mconf = rcu_dereference(mvif->link[link_id]);
-+	mlink = rcu_dereference(msta->link[link_id]);
-+
- 	if (mlink->wcid.idx > MT7996_WTBL_STA)
- 		return -EIO;
- 
-@@ -2380,7 +2475,7 @@ const struct ieee80211_ops mt7996_ops = {
- 	.vif_cfg_changed = mt7996_vif_cfg_changed,
- 	.link_info_changed = mt7996_link_info_changed,
- 	.sta_state = mt76_sta_state,
--	.sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
-+	.sta_pre_rcu_remove = mt7996_sta_pre_rcu_remove,
- 	.sta_rc_update = mt7996_sta_rc_update,
- 	.set_key = mt7996_set_key,
- 	.ampdu_action = mt7996_ampdu_action,
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0093-mtk-mt76-mt7996-enable-MLO-capability.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0093-mtk-mt76-mt7996-enable-MLO-capability.patch
new file mode 100644
index 0000000..ee007e2
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0093-mtk-mt76-mt7996-enable-MLO-capability.patch
@@ -0,0 +1,104 @@
+From 208cd15d45360f69c2811bed72b2715c83632b3b Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 30 Nov 2023 16:31:17 +0800
+Subject: [PATCH 093/199] mtk: mt76: mt7996: enable MLO capability
+
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/eeprom.c |  6 ++++++
+ mt7996/init.c   | 38 ++++++++++++++++++++++++++++++++++++--
+ 2 files changed, 42 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index c4714982..cd93a3c2 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -390,6 +390,12 @@ static int mt7996_eeprom_parse_band_config(struct mt7996_phy *phy)
+ 		break;
+ 	}
+ 
++	/* TODO: for MLO, we enable all band capabilities */
++	phy->mt76->cap.has_2ghz = true;
++	phy->mt76->cap.has_5ghz = true;
++	if (is_mt7996(&phy->dev->mt76))
++		phy->mt76->cap.has_6ghz = true;
++
+ 	return ret;
+ }
+ 
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 86bb0661..2fe869c9 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -35,7 +35,7 @@ static const struct ieee80211_iface_combination if_comb[] = {
+ 		.limits = if_limits,
+ 		.n_limits = ARRAY_SIZE(if_limits),
+ 		.max_interfaces = MT7996_MAX_INTERFACES,
+-		.num_different_channels = 1,
++		.num_different_channels = 3,
+ 		.beacon_int_infra_match = true,
+ 		.radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+ 				       BIT(NL80211_CHAN_WIDTH_20) |
+@@ -46,6 +46,33 @@ static const struct ieee80211_iface_combination if_comb[] = {
+ 	}
+ };
+ 
++static const u8 mt7996_if_types_ext_capa[] = {
++	[0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
++	[7] = WLAN_EXT_CAPA8_OPMODE_NOTIF,
++};
++
++static const struct wiphy_iftype_ext_capab mt7996_iftypes_ext_capa[] = {
++	{
++		.iftype = NL80211_IFTYPE_STATION,
++		.extended_capabilities = mt7996_if_types_ext_capa,
++		.extended_capabilities_mask = mt7996_if_types_ext_capa,
++		.extended_capabilities_len = sizeof(mt7996_if_types_ext_capa),
++		.mld_capa_and_ops = 2,
++	},
++	{
++		.iftype = NL80211_IFTYPE_AP,
++		.extended_capabilities = mt7996_if_types_ext_capa,
++		.extended_capabilities_mask = mt7996_if_types_ext_capa,
++		.extended_capabilities_len = sizeof(mt7996_if_types_ext_capa),
++		.mld_capa_and_ops = 2,
++		/* the max number of simultaneous links is defined as the
++		 * maximum number of affiliated APs minus 1.
++		 * mt7996 could have 3 links in an MLD AP, so currently
++		 * hardcode it to 2.
++		 */
++	},
++};
++
+ static ssize_t mt7996_thermal_temp_show(struct device *dev,
+ 					struct device_attribute *attr,
+ 					char *buf)
+@@ -419,8 +446,9 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ 	ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD);
+ 	ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD);
+ 	ieee80211_hw_set(hw, WANT_MONITOR_VIF);
+-	ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
++	// ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
+ 	ieee80211_hw_set(hw, CHANCTX_STA_CSA);
++	ieee80211_hw_set(hw, CONNECTION_MONITOR);
+ 
+ 	hw->max_tx_fragments = 4;
+ 
+@@ -464,6 +492,12 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ 
+ 	wiphy->max_scan_ssids = 4;
+ 	wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
++
++	/* enable MLO support */
++	wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO;
++	wiphy->iftype_ext_capab = mt7996_iftypes_ext_capa;
++	wiphy->num_iftype_ext_capab = ARRAY_SIZE(mt7996_iftypes_ext_capa);
++	wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE;
+ }
+ 
+ static void
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0093-wifi-mt76-mt7996-rework-TXD-for-multi-link-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0093-wifi-mt76-mt7996-rework-TXD-for-multi-link-support.patch
deleted file mode 100644
index 6509b04..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0093-wifi-mt76-mt7996-rework-TXD-for-multi-link-support.patch
+++ /dev/null
@@ -1,203 +0,0 @@
-From 59f38b9ffa5320f9d80c0752d8ffc15dcf0114c5 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Mon, 4 Dec 2023 11:25:54 +0800
-Subject: [PATCH 093/116] wifi: mt76: mt7996: rework TXD for multi-link support
-
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/mac.c    | 88 +++++++++++++++++++++++++++++++++++--------------
- mt7996/mt7996.h |  9 +++++
- 2 files changed, 73 insertions(+), 24 deletions(-)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 3cff43d..0fa3266 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -621,9 +621,8 @@ mt7996_mac_write_txwi_8023(struct mt7996_dev *dev, __le32 *txwi,
- 	u32 val;
- 
- 	if (wcid->sta) {
--		struct ieee80211_sta *sta;
-+		struct ieee80211_sta *sta = wcid_to_sta(wcid);
- 
--		sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv);
- 		wmm = sta->wme;
- 	}
- 
-@@ -724,6 +723,10 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
- 		txwi[3] |= cpu_to_le32(val);
- 		txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU);
- 	}
-+
-+	if (ieee80211_vif_is_mld(info->control.vif) &&
-+	    (multicast || unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE))))
-+		txwi[5] |= cpu_to_le32(MT_TXD5_FL);
- }
- 
- void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
-@@ -733,10 +736,12 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
- {
- 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- 	struct ieee80211_vif *vif = info->control.vif;
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_bss_conf *mconf;
- 	u8 band_idx = (info->hw_queue & MT_TX_HW_QUEUE_PHY) >> 2;
- 	u8 p_fmt, q_idx, omac_idx = 0, wmm_idx = 0;
-+	u8 link_id;
- 	bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
--	struct mt76_vif *mvif;
- 	u16 tx_count = 15;
- 	u32 val;
- 	bool inband_disc = !!(changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP |
-@@ -744,11 +749,16 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
- 	bool beacon = !!(changed & (BSS_CHANGED_BEACON |
- 				    BSS_CHANGED_BEACON_ENABLED)) && (!inband_disc);
- 
--	mvif = vif ? (struct mt76_vif *)vif->drv_priv : NULL;
--	if (mvif) {
--		omac_idx = mvif->omac_idx;
--		wmm_idx = mvif->wmm_idx;
--		band_idx = mvif->band_idx;
-+	if (likely(wcid != &dev->mt76.global_wcid))
-+		link_id = wcid->link_id;
-+	else
-+		link_id = u32_get_bits(info->control.flags, IEEE80211_TX_CTRL_MLO_LINK);
-+
-+	mconf = rcu_dereference(mvif->link[link_id]);
-+	if (mconf) {
-+		omac_idx = mconf->mt76.omac_idx;
-+		wmm_idx = mconf->mt76.wmm_idx;
-+		band_idx = mconf->mt76.band_idx;
- 	}
- 
- 	if (inband_disc) {
-@@ -795,7 +805,10 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
- 		val |= MT_TXD5_TX_STATUS_HOST;
- 	txwi[5] = cpu_to_le32(val);
- 
--	val = MT_TXD6_DIS_MAT | MT_TXD6_DAS;
-+	val = MT_TXD6_DAS;
-+	if ((q_idx >= MT_LMAC_ALTX0 && q_idx <= MT_LMAC_BCN0))
-+		val |= MT_TXD6_DIS_MAT;
-+
- 	if (is_mt7996(&dev->mt76))
- 		val |= FIELD_PREP(MT_TXD6_MSDU_CNT, 1);
- 	else
-@@ -814,16 +827,18 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
- 			     is_multicast_ether_addr(hdr->addr1);
- 		u8 idx = MT7996_BASIC_RATES_TBL;
- 
--		if (mvif) {
--			if (mcast && mvif->mcast_rates_idx)
--				idx = mvif->mcast_rates_idx;
--			else if (beacon && mvif->beacon_rates_idx)
--				idx = mvif->beacon_rates_idx;
-+		if (mconf) {
-+			if (mcast && mconf->mt76.mcast_rates_idx)
-+				idx = mconf->mt76.mcast_rates_idx;
-+			else if (beacon && mconf->mt76.beacon_rates_idx)
-+				idx = mconf->mt76.beacon_rates_idx;
- 			else
--				idx = mvif->basic_rates_idx;
-+				idx = mconf->mt76.basic_rates_idx;
- 		}
- 
- 		val = FIELD_PREP(MT_TXD6_TX_RATE, idx) | MT_TXD6_FIXED_BW;
-+		if (mcast)
-+			val |= MT_TXD6_DIS_MAT;
- 		txwi[6] |= cpu_to_le32(val);
- 		txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE);
- 	}
-@@ -839,17 +854,48 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
- 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb);
- 	struct ieee80211_key_conf *key = info->control.hw_key;
- 	struct ieee80211_vif *vif = info->control.vif;
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_sta *msta;
-+	struct mt7996_bss_conf *mconf;
- 	struct mt76_connac_txp_common *txp;
- 	struct mt76_txwi_cache *t;
- 	int id, i, pid, nbuf = tx_info->nbuf - 1;
- 	bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
- 	u8 *txwi = (u8 *)txwi_ptr;
-+	u8 link_id;
- 
- 	if (unlikely(tx_info->skb->len <= ETH_HLEN))
- 		return -EINVAL;
- 
--	if (!wcid)
--		wcid = &dev->mt76.global_wcid;
-+	if (WARN_ON(!wcid))
-+		return -EINVAL;
-+
-+	msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta;
-+	if (ieee80211_is_data_qos(hdr->frame_control) && sta->mlo) {
-+		if (unlikely(tx_info->skb->protocol == cpu_to_be16(ETH_P_PAE))) {
-+			link_id = msta->pri_link;
-+		} else {
-+			u8 tid = tx_info->skb->priority & IEEE80211_QOS_CTL_TID_MASK;
-+
-+			link_id = (tid % 2) ? msta->sec_link : msta->pri_link;
-+		}
-+	} else {
-+		link_id = u32_get_bits(info->control.flags, IEEE80211_TX_CTRL_MLO_LINK);
-+
-+		if (link_id == IEEE80211_LINK_UNSPECIFIED || (sta && !sta->mlo))
-+			link_id = wcid->link_id;
-+	}
-+
-+	if (link_id != wcid->link_id) {
-+		struct mt7996_link_sta *mlink = rcu_dereference(msta->link[link_id]);
-+
-+		if (mlink)
-+			wcid = &mlink->wcid;
-+	}
-+
-+	mconf = rcu_dereference(mvif->link[wcid->link_id]);
-+	if (!mconf)
-+		return -ENOLINK;
- 
- 	t = (struct mt76_txwi_cache *)(txwi + mdev->drv->txwi_size);
- 	t->skb = tx_info->skb;
-@@ -894,13 +940,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
- 	if (!is_8023 && ieee80211_is_mgmt(hdr->frame_control))
- 		txp->fw.flags |= cpu_to_le16(MT_CT_INFO_MGMT_FRAME);
- 
--	if (vif) {
--		struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--		struct mt7996_bss_conf *mconf = &mvif->deflink;
--
--		txp->fw.bss_idx = mconf->mt76.idx;
--	}
--
-+	txp->fw.bss_idx = mconf->mt76.idx;
- 	txp->fw.token = cpu_to_le16(id);
- 	txp->fw.rept_wds_wcid = cpu_to_le16(sta ? wcid->idx : 0xfff);
- 
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 4c090ba..21a95c1 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -830,6 +830,15 @@ mlink_dereference_protected(struct mt7996_sta *msta, u8 link_id)
- 					 lockdep_is_held(&msta->vif->dev->mt76.mutex));
- }
- 
-+static inline struct mt7996_link_sta *
-+wcid_to_mlink(struct mt76_wcid *wcid)
-+{
-+	if (!wcid)
-+		return NULL;
-+
-+	return container_of(wcid, struct mt7996_link_sta, wcid);
-+}
-+
- extern const struct ieee80211_ops mt7996_ops;
- extern struct pci_driver mt7996_pci_driver;
- extern struct pci_driver mt7996_hif_driver;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0094-mtk-mt76-mt7996-support-multi-link-vif-links-and-MLO.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0094-mtk-mt76-mt7996-support-multi-link-vif-links-and-MLO.patch
new file mode 100644
index 0000000..db2ebfa
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0094-mtk-mt76-mt7996-support-multi-link-vif-links-and-MLO.patch
@@ -0,0 +1,587 @@
+From db6f06e9685bd4297e87b10b6f2550b4430e3b0a Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 23 Nov 2023 18:22:11 +0800
+Subject: [PATCH 094/199] mtk: mt76: mt7996: support multi-link vif links and
+ MLO bss callbacks
+
+Rework add/remove interface functions to add/remove bss_conf functions,
+and also switch to callbacks for MLO bss.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/main.c   | 295 +++++++++++++++++++++++++++++++++++++++---------
+ mt7996/mcu.c    |  29 +++--
+ mt7996/mt7996.h |   9 ++
+ 3 files changed, 267 insertions(+), 66 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index e071c5fa..3abae2fe 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -205,6 +205,38 @@ static int get_omac_idx(enum nl80211_iftype type, u64 mask)
+ 	return -1;
+ }
+ 
++static int get_own_mld_idx(u64 mask, bool group_mld)
++{
++	u8 start, end;
++	int i;
++
++	if (group_mld) {
++		start = 0;
++		end = 15;
++	} else {
++		start = 16;
++		end = 63;
++	}
++
++	i = get_free_idx(mask, start, end);
++	if (i)
++		return i - 1;
++
++	return -1;
++}
++
++static int get_mld_remap_idx(u64 mask)
++{
++	u8 start = 0, end = 15;
++	int i;
++
++	i = get_free_idx(mask, start, end);
++	if (i)
++		return i - 1;
++
++	return -1;
++}
++
+ static void mt7996_init_bitrate_mask(struct mt7996_bss_conf *mconf)
+ {
+ 	int i;
+@@ -223,48 +255,108 @@ static void mt7996_init_bitrate_mask(struct mt7996_bss_conf *mconf)
+ 	}
+ }
+ 
+-static int mt7996_add_interface(struct ieee80211_hw *hw,
+-				struct ieee80211_vif *vif)
++static void mt7996_remove_bss_conf(struct ieee80211_vif *vif,
++				   struct ieee80211_bss_conf *conf,
++				   struct mt7996_bss_conf *mconf)
+ {
+-	struct ieee80211_bss_conf *conf = &vif->bss_conf;
++	struct mt7996_phy *phy = mconf->phy;
++	struct mt7996_dev *dev = phy->dev;
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct mt7996_bss_conf *mconf = &mvif->deflink;
+-	struct mt7996_link_sta *mlink = &mvif->sta.deflink;
+-	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+-	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	u8 link_id = conf->link_id;
++	struct mt7996_link_sta *mlink =
++		mlink_dereference_protected(&mvif->sta, link_id);
++
++	if (!mlink)
++		return;
++
++	mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, false, false);
++	mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, false);
++	mt7996_mcu_add_dev_info(phy, conf, mconf, false);
++
++	rcu_assign_pointer(dev->mt76.wcid[mlink->wcid.idx], NULL);
++	rcu_assign_pointer(mvif->link[link_id], NULL);
++	rcu_assign_pointer(mvif->sta.link[link_id], NULL);
++
++	dev->mt76.vif_mask &= ~BIT_ULL(mconf->mt76.idx);
++	dev->mld_id_mask &= ~BIT_ULL(mconf->own_mld_id);
++	phy->omac_mask &= ~BIT_ULL(mconf->mt76.omac_idx);
++
++	spin_lock_bh(&dev->mt76.sta_poll_lock);
++	if (!list_empty(&mlink->wcid.poll_list))
++		list_del_init(&mlink->wcid.poll_list);
++	spin_unlock_bh(&dev->mt76.sta_poll_lock);
++
++	mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
++
++	if (mlink != &mvif->sta.deflink)
++		kfree(mlink);
++
++	if (mconf != &mvif->deflink)
++		kfree(mconf);
++}
++
++static int mt7996_add_bss_conf(struct mt7996_phy *phy,
++			       struct ieee80211_vif *vif,
++			       struct ieee80211_bss_conf *conf)
++{
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_dev *dev = phy->dev;
++	struct mt7996_bss_conf *mconf;
++	struct mt7996_link_sta *mlink;
+ 	struct mt76_txq *mtxq;
+ 	u8 band_idx = phy->mt76->band_idx;
+-	int idx, ret = 0;
+-
+-	mutex_lock(&dev->mt76.mutex);
++	u8 link_id = conf->link_id;
++	int idx, ret;
+ 
+-	if (vif->type == NL80211_IFTYPE_MONITOR &&
+-	    is_zero_ether_addr(vif->addr))
+-		phy->monitor_vif = vif;
++	if (conf != &vif->bss_conf) {
++		mconf = kzalloc(sizeof(*mconf), GFP_KERNEL);
++		if (!mconf)
++			return -ENOMEM;
++	} else {
++		mconf = &mvif->deflink;
++	}
+ 
+ 	mconf->mt76.idx = __ffs64(~dev->mt76.vif_mask);
+ 	if (mconf->mt76.idx >= mt7996_max_interface_num(dev)) {
+ 		ret = -ENOSPC;
+-		goto out;
++		goto error;
+ 	}
+ 
+ 	idx = get_omac_idx(vif->type, phy->omac_mask);
+ 	if (idx < 0) {
+ 		ret = -ENOSPC;
+-		goto out;
++		goto error;
++	}
++
++	mconf->own_mld_id = get_own_mld_idx(dev->mld_id_mask, false);
++	if (mconf->own_mld_id < 0) {
++		ret = -ENOSPC;
++		goto error;
+ 	}
++
+ 	mconf->mt76.omac_idx = idx;
+ 	mconf->vif = mvif;
+ 	mconf->phy = phy;
+ 	mconf->mt76.band_idx = band_idx;
+ 	mconf->mt76.wmm_idx = vif->type == NL80211_IFTYPE_AP ? 0 : 3;
+-	mvif->dev = dev;
++	mconf->link_id = link_id;
+ 
+ 	ret = mt7996_mcu_add_dev_info(phy, conf, mconf, true);
+ 	if (ret)
+-		goto out;
++		goto error;
++
++	if (ieee80211_vif_is_mld(vif)) {
++		mlink = kzalloc(sizeof(*mlink), GFP_KERNEL);
++		if (!mlink) {
++			ret = -ENOMEM;
++			goto error;
++		}
++	} else {
++		mlink = &mvif->sta.deflink;
++	}
+ 
+ 	dev->mt76.vif_mask |= BIT_ULL(mconf->mt76.idx);
++	dev->mld_id_mask |= BIT_ULL(mconf->own_mld_id);
+ 	phy->omac_mask |= BIT_ULL(mconf->mt76.omac_idx);
+ 
+ 	idx = MT7996_WTBL_RESERVED - mconf->mt76.idx;
+@@ -275,6 +367,9 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ 	mlink->wcid.phy_idx = band_idx;
+ 	mlink->wcid.hw_key_idx = -1;
+ 	mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
++	mlink->wcid.def_wcid = &mvif->sta.deflink.wcid;
++	mlink->wcid.link_id = link_id;
++	mlink->wcid.link_valid = ieee80211_vif_is_mld(vif);
+ 	mlink->sta = &mvif->sta;
+ 	mlink->sta->vif = mvif;
+ 	mt76_wcid_init(&mlink->wcid);
+@@ -296,7 +391,6 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ 		mconf->mt76.basic_rates_idx = MT7996_BASIC_RATES_TBL + 4;
+ 	else
+ 		mconf->mt76.basic_rates_idx = MT7996_BASIC_RATES_TBL;
+-
+ 	mt7996_init_bitrate_mask(mconf);
+ 
+ 	mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, true);
+@@ -306,10 +400,32 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ 	if (vif->type != NL80211_IFTYPE_STATION)
+ 		mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, true, true);
+ 	rcu_assign_pointer(dev->mt76.wcid[idx], &mlink->wcid);
+-	rcu_assign_pointer(mvif->link[0], mconf);
+-	rcu_assign_pointer(mvif->sta.link[0], mlink);
++	rcu_assign_pointer(mvif->link[link_id], mconf);
++	rcu_assign_pointer(mvif->sta.link[link_id], mlink);
+ 
+-out:
++	return 0;
++error:
++	mt7996_remove_bss_conf(vif, conf, mconf);
++	return ret;
++}
++
++static int mt7996_add_interface(struct ieee80211_hw *hw,
++			        struct ieee80211_vif *vif)
++{
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	int ret = 0;
++
++	mutex_lock(&dev->mt76.mutex);
++	if (vif->type == NL80211_IFTYPE_MONITOR &&
++	    is_zero_ether_addr(vif->addr))
++		phy->monitor_vif = vif;
++
++	mvif->dev = dev;
++	mvif->sta.vif = mvif;
++
++	ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
+ 	mutex_unlock(&dev->mt76.mutex);
+ 
+ 	return ret;
+@@ -321,38 +437,23 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ 	struct ieee80211_bss_conf *conf;
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_bss_conf *mconf;
+-	struct mt7996_link_sta *mlink = &mvif->sta.deflink;
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+-	int idx = mlink->wcid.idx;
+ 
+ 	cancel_delayed_work_sync(&phy->scan_work);
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+-	conf = link_conf_dereference_protected(vif, 0);
+-	mconf = mconf_dereference_protected(mvif, 0);
+-	mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, false, false);
+-	mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, false);
++	if (test_bit(MT76_SCANNING, &phy->mt76->state))
++		mt7996_scan_complete(phy, true);
+ 
+ 	if (vif == phy->monitor_vif)
+ 		phy->monitor_vif = NULL;
+ 
+-	mt7996_mcu_add_dev_info(phy, conf, mconf, false);
+-
+-	rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
+-
+-	dev->mt76.vif_mask &= ~BIT_ULL(mconf->mt76.idx);
+-	phy->omac_mask &= ~BIT_ULL(mconf->mt76.omac_idx);
+-
+-	spin_lock_bh(&dev->mt76.sta_poll_lock);
+-	if (!list_empty(&mlink->wcid.poll_list))
+-		list_del_init(&mlink->wcid.poll_list);
+-	spin_unlock_bh(&dev->mt76.sta_poll_lock);
++	conf = link_conf_dereference_protected(vif, 0);
++	mconf = mconf_dereference_protected(mvif, 0);
+ 
+-	mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
+-	rcu_assign_pointer(mvif->link[0], NULL);
+-	rcu_assign_pointer(mvif->sta.link[0], NULL);
++	mt7996_remove_bss_conf(vif, conf, mconf);
+ 
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+@@ -720,10 +821,31 @@ mt7996_update_mu_group(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
+ 	mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_POS3(band), mu[3]);
+ }
+ 
+-static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
+-				    struct ieee80211_vif *vif,
+-				    struct ieee80211_bss_conf *info,
+-				    u64 changed)
++static void mt7996_vif_cfg_changed(struct ieee80211_hw *hw,
++				   struct ieee80211_vif *vif, u64 changed)
++{
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++
++	mutex_lock(&dev->mt76.mutex);
++
++	if (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) {
++		struct ieee80211_bss_conf *conf = &vif->bss_conf;
++		struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++		struct mt7996_bss_conf *mconf = mconf_dereference_protected(mvif, 0);
++		struct mt7996_link_sta *mlink = mlink_dereference_protected(&mvif->sta, 0);
++
++		mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, true);
++		mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, true, false);
++	}
++
++	mutex_unlock(&dev->mt76.mutex);
++}
++
++static void mt7996_link_info_changed(struct ieee80211_hw *hw,
++				     struct ieee80211_vif *vif,
++				     struct ieee80211_bss_conf *info,
++				     u64 changed)
+ {
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_bss_conf *mconf;
+@@ -739,7 +861,6 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
+ 	 * and then peer references bss_info_rfch to set bandwidth cap.
+ 	 */
+ 	if ((changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) ||
+-	    (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) ||
+ 	    (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon)) {
+ 		mt7996_mcu_add_bss_info(phy, info, mconf, mlink, true);
+ 		mt7996_mcu_add_sta(dev, info, mconf, NULL, mlink, true,
+@@ -1082,7 +1203,7 @@ mt7996_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ 	u64 ret;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+-	mconf = mconf_dereference_protected(mvif, 0);
++	mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
+ 	ret = __mt7996_get_tsf(hw, mconf);
+ 	mutex_unlock(&dev->mt76.mutex);
+ 
+@@ -1105,7 +1226,7 @@ mt7996_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+-	mconf = mconf_dereference_protected(mvif, 0);
++	mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
+ 	n = mconf->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
+ 					       : mconf->mt76.omac_idx;
+ 	mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]);
+@@ -1133,7 +1254,7 @@ mt7996_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+-	mconf = mconf_dereference_protected(mvif, 0);
++	mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
+ 	n = mconf->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
+ 					       : mconf->mt76.omac_idx;
+ 	mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]);
+@@ -1308,7 +1429,7 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+-	mconf = mconf_dereference_protected(mvif, 0);
++	mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
+ 	mconf->bitrate_mask = *mask;
+ 	mutex_unlock(&dev->mt76.mutex);
+ 
+@@ -1528,7 +1649,7 @@ void mt7996_get_et_stats(struct ieee80211_hw *hw,
+ 	int i, ei = 0;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+-	mconf = mconf_dereference_protected(mvif, 0);
++	mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
+ 	wi.idx = mconf->mt76.idx,
+ 
+ 	mt7996_mac_update_stats(phy);
+@@ -1901,6 +2022,8 @@ mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	struct mt7996_phy *phy = ctx->phy;
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_bss_conf *mconf;
++	u8 link_id = link_conf->link_id;
++	int ret;
+ 
+ 	wiphy_info(hw->wiphy, "Assign VIF (addr: %pM, type: %d, link_id: %d) to channel context: %d MHz\n",
+ 		    vif->addr, vif->type, link_conf->link_id,
+@@ -1908,10 +2031,24 @@ mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 
+ 	mutex_lock(&phy->dev->mt76.mutex);
+ 
+-	mconf = mconf_dereference_protected(mvif, 0);
++	/* remove first */
++	if (rcu_access_pointer(mvif->link[link_id]))
++		mt7996_remove_bss_conf(vif, link_conf,
++				       mconf_dereference_protected(mvif, link_id));
++
++	ret = mt7996_add_bss_conf(phy, vif, link_conf);
++	if (ret) {
++		mutex_unlock(&phy->dev->mt76.mutex);
++		return ret;
++	}
++
++	mconf = mconf_dereference_protected(mvif, link_id);
+ 	mconf->chanctx = ctx;
+ 	ctx->nbss_assigned++;
+ 
++	if (mt7996_hw_phy(hw) == phy)
++		mvif->master_link_id = link_id;
++
+ 	mutex_unlock(&phy->dev->mt76.mutex);
+ 
+ 	return 0;
+@@ -1937,7 +2074,7 @@ mt7996_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	if (test_bit(MT76_SCANNING, &phy->mt76->state))
+ 		mt7996_scan_complete(phy, true);
+ 
+-	mconf = mconf_dereference_protected(mvif, 0);
++	mconf = mconf_dereference_protected(mvif, link_conf->link_id);
+ 	mconf->chanctx = NULL;
+ 	ctx->nbss_assigned--;
+ 
+@@ -1977,6 +2114,54 @@ mt7996_switch_vif_chanctx(struct ieee80211_hw *hw,
+ 	return mt7996_set_channel(phy, &new_ctx->chandef);
+ }
+ 
++static int
++mt7996_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++			u16 old_links, u16 new_links,
++			struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS])
++{
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	unsigned long rem = old_links & ~new_links;
++	unsigned int link_id;
++	int ret = 0;
++
++	if (old_links == new_links)
++		return 0;
++
++	mutex_lock(&dev->mt76.mutex);
++
++	/* remove first */
++	for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
++		struct mt7996_bss_conf *mconf =
++			mconf_dereference_protected(mvif, link_id);
++
++		if (!mconf)
++			continue;
++
++		mt7996_remove_bss_conf(vif, old[link_id], mconf);
++	}
++
++	if (!old_links) {
++		mvif->group_mld_id = get_own_mld_idx(dev->mld_id_mask, true);
++		dev->mld_id_mask |= BIT_ULL(mvif->group_mld_id);
++
++		mvif->mld_remap_id = get_mld_remap_idx(dev->mld_remap_id_mask);
++		dev->mld_remap_id_mask |= BIT_ULL(mvif->mld_remap_id);
++	}
++
++	/* fallback to non-MLO interface */
++	if (!new_links) {
++		ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
++		dev->mld_id_mask &= ~BIT_ULL(mvif->group_mld_id);
++		dev->mld_remap_id_mask &= ~BIT_ULL(mvif->mld_remap_id);
++	}
++
++	mutex_unlock(&dev->mt76.mutex);
++
++	return ret;
++}
++
+ const struct ieee80211_ops mt7996_ops = {
+ 	.add_chanctx = ieee80211_emulate_add_chanctx,
+ 	.remove_chanctx = ieee80211_emulate_remove_chanctx,
+@@ -1990,7 +2175,8 @@ const struct ieee80211_ops mt7996_ops = {
+ 	.config = mt7996_config,
+ 	.conf_tx = mt7996_conf_tx,
+ 	.configure_filter = mt7996_configure_filter,
+-	.bss_info_changed = mt7996_bss_info_changed,
++	.vif_cfg_changed = mt7996_vif_cfg_changed,
++	.link_info_changed = mt7996_link_info_changed,
+ 	.sta_state = mt76_sta_state,
+ 	.sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
+ 	.sta_rc_update = mt7996_sta_rc_update,
+@@ -2036,4 +2222,5 @@ const struct ieee80211_ops mt7996_ops = {
+ 	.assign_vif_chanctx = mt7996_assign_vif_chanctx,
+ 	.unassign_vif_chanctx = mt7996_unassign_vif_chanctx,
+ 	.switch_vif_chanctx = mt7996_switch_vif_chanctx,
++	.change_vif_links = mt7996_change_vif_links,
+ };
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 20b84c93..d3e244ee 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1047,15 +1047,23 @@ static void
+ mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ 		       struct mt7996_bss_conf *mconf)
+ {
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct bss_mld_tlv *mld;
+ 	struct tlv *tlv;
+ 
+ 	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_MLD, sizeof(*mld));
+-
+ 	mld = (struct bss_mld_tlv *)tlv;
+-	mld->group_mld_id = 0xff;
+-	mld->own_mld_id = mconf->mt76.idx;
+-	mld->remap_idx = 0xff;
++
++	if (ieee80211_vif_is_mld(vif)) {
++		mld->group_mld_id = mvif->group_mld_id;
++		mld->remap_idx = mvif->mld_remap_id;
++		memcpy(mld->mac_addr, vif->addr, ETH_ALEN);
++	} else {
++		mld->group_mld_id = 0xff;
++		mld->remap_idx = 0xff;
++	}
++
++	mld->own_mld_id = mconf->own_mld_id;
+ }
+ 
+ static void
+@@ -1136,13 +1144,11 @@ mt7996_mcu_bss_ifs_timing_tlv(struct sk_buff *skb, struct mt7996_phy *phy)
+ }
+ 
+ static int
+-mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
+-			 struct ieee80211_bss_conf *conf,
+-			 struct mt7996_bss_conf *mconf,
+-			 struct ieee80211_sta *sta,
+-			 struct mt76_phy *phy, u16 wlan_idx,
+-			 bool enable)
++mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
++			 struct mt7996_bss_conf *mconf, struct ieee80211_sta *sta,
++			 u16 wlan_idx, bool enable)
+ {
++	struct mt76_phy *phy = mconf->phy->mt76;
+ 	struct ieee80211_vif *vif = conf->vif;
+ 	struct cfg80211_chan_def *chandef = &phy->chandef;
+ 	struct mt76_connac_bss_basic_tlv *bss;
+@@ -1254,8 +1260,7 @@ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
+ 		return PTR_ERR(skb);
+ 
+ 	/* bss_basic must be first */
+-	mt7996_mcu_bss_basic_tlv(skb, conf, mconf, NULL, phy->mt76,
+-				 mlink->wcid.idx, enable);
++	mt7996_mcu_bss_basic_tlv(skb, conf, mconf, NULL, mlink->wcid.idx, enable);
+ 	mt7996_mcu_bss_sec_tlv(skb, mconf);
+ 
+ 	if (vif->type == NL80211_IFTYPE_MONITOR)
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 1732ff36..09b1088e 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -341,6 +341,9 @@ struct mt7996_bss_conf {
+ 	struct cfg80211_bitrate_mask bitrate_mask;
+ 
+ 	struct mt7996_chanctx *chanctx;
++
++	u8 link_id;
++	u8 own_mld_id;
+ };
+ 
+ struct mt7996_vif {
+@@ -349,6 +352,10 @@ struct mt7996_vif {
+ 
+ 	struct mt7996_sta sta;
+ 	struct mt7996_dev *dev;
++
++	u8 master_link_id;
++	u8 group_mld_id;
++	u8 mld_remap_id;
+ };
+ 
+ /* crash-dump */
+@@ -550,6 +557,8 @@ struct mt7996_dev {
+ 	u16 chainmask;
+ 	u8 chainshift[__MT_MAX_BAND];
+ 	u32 hif_idx;
++	u64 mld_id_mask;
++	u64 mld_remap_id_mask;
+ 
+ 	struct work_struct init_work;
+ 	struct work_struct rc_work;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0094-wifi-mt76-mt7996-rework-TXS-for-multi-link-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0094-wifi-mt76-mt7996-rework-TXS-for-multi-link-support.patch
deleted file mode 100644
index 8e6acb1..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0094-wifi-mt76-mt7996-rework-TXS-for-multi-link-support.patch
+++ /dev/null
@@ -1,120 +0,0 @@
-From 13cf1c4b8027b2747c067d1bdd2f927f8c266b31 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Mon, 4 Dec 2023 11:57:38 +0800
-Subject: [PATCH 094/116] wifi: mt76: mt7996: rework TXS for multi-link support
-
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/mac.c    |  9 +++++----
- mt7996/main.c   |  1 +
- mt7996/mt7996.h | 28 ++++++++++++++++++++++++++++
- 3 files changed, 34 insertions(+), 4 deletions(-)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 0fa3266..65431c7 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1192,7 +1192,7 @@ mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid,
- 		struct ieee80211_sta *sta;
- 		u8 tid;
- 
--		sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv);
-+		sta = wcid_to_sta(wcid);
- 		tid = FIELD_GET(MT_TXS0_TID, txs);
- 		ieee80211_refresh_tx_agg_session_timer(sta, tid);
- 	}
-@@ -1310,9 +1310,10 @@ static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
- 	struct mt76_wcid *wcid;
- 	__le32 *txs_data = data;
- 	u16 wcidx;
--	u8 pid;
-+	u8 band, pid;
- 
- 	wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID);
-+	band = le32_get_bits(txs_data[2], MT_TXS2_BAND);
- 	pid = le32_get_bits(txs_data[3], MT_TXS3_PID);
- 
- 	if (pid < MT_PACKET_ID_NO_SKB)
-@@ -1323,7 +1324,7 @@ static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
- 
- 	rcu_read_lock();
- 
--	wcid = rcu_dereference(dev->mt76.wcid[wcidx]);
-+	wcid = mt7996_get_link_wcid(dev, wcidx, band);
- 	if (!wcid)
- 		goto out;
- 
-@@ -1332,7 +1333,7 @@ static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
- 	if (!wcid->sta)
- 		goto out;
- 
--	mlink = container_of(wcid, struct mt7996_link_sta, wcid);
-+	mlink = wcid_to_mlink(wcid);
- 	spin_lock_bh(&dev->mt76.sta_poll_lock);
- 	if (list_empty(&mlink->wcid.poll_list))
- 		list_add_tail(&mlink->wcid.poll_list, &dev->mt76.sta_poll_list);
-diff --git a/mt7996/main.c b/mt7996/main.c
-index f5658d4..a94955c 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -2317,6 +2317,7 @@ mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	mconf = mconf_dereference_protected(mvif, link_id);
- 	mconf->chanctx = ctx;
- 	ctx->nbss_assigned++;
-+	mvif->band_to_link[phy->mt76->band_idx] = link_id;
- 
- 	if (mt7996_hw_phy(hw) == phy)
- 		mvif->master_link_id = link_id;
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 21a95c1..c6ca00f 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -357,6 +357,8 @@ struct mt7996_vif {
- 	u8 master_link_id;
- 	u8 group_mld_id;
- 	u8 mld_remap_id;
-+
-+	u8 band_to_link[__MT_MAX_BAND];
- };
- 
- /* crash-dump */
-@@ -839,6 +841,32 @@ wcid_to_mlink(struct mt76_wcid *wcid)
- 	return container_of(wcid, struct mt7996_link_sta, wcid);
- }
- 
-+static inline struct mt76_wcid *
-+mt7996_get_link_wcid(struct mt7996_dev *dev, u16 idx, u8 band_idx)
-+{
-+	struct mt7996_link_sta *mlink;
-+	struct mt76_wcid *wcid;
-+	u8 link_id;
-+
-+	if (!idx || idx >= ARRAY_SIZE(dev->mt76.wcid))
-+		return NULL;
-+
-+	wcid = rcu_dereference(dev->mt76.wcid[idx]);
-+	if (!wcid)
-+		return NULL;
-+
-+	if (wcid->phy_idx == band_idx)
-+		return wcid;
-+
-+	mlink = wcid_to_mlink(wcid);
-+	link_id = mlink->sta->vif->band_to_link[band_idx];
-+	mlink = rcu_dereference(mlink->sta->link[link_id]);
-+	if (!mlink)
-+		return wcid;
-+
-+	return &mlink->wcid;
-+}
-+
- extern const struct ieee80211_ops mt7996_ops;
- extern struct pci_driver mt7996_pci_driver;
- extern struct pci_driver mt7996_hif_driver;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0095-mtk-mt76-mt7996-support-multi-link-sta-links-and-MLO.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0095-mtk-mt76-mt7996-support-multi-link-sta-links-and-MLO.patch
new file mode 100644
index 0000000..1338a68
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0095-mtk-mt76-mt7996-support-multi-link-sta-links-and-MLO.patch
@@ -0,0 +1,589 @@
+From 9fb46d0e65f5f37975a71ad433db8290ee586566 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 29 Nov 2023 10:12:39 +0800
+Subject: [PATCH 095/199] mtk: mt76: mt7996: support multi-link sta links and
+ MLO sta callbacks
+
+Rework add_sta functions to add_link_sta functions, and support
+.change_sta_links callback.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/main.c   | 309 +++++++++++++++++++++++++++++++++++++-----------
+ mt7996/mcu.c    | 117 ++++++++++++++++++
+ mt7996/mcu.h    |  29 +++++
+ mt7996/mt7996.h |   7 ++
+ 4 files changed, 396 insertions(+), 66 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 3abae2fe..e850942f 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -932,42 +932,234 @@ mt7996_channel_switch_beacon(struct ieee80211_hw *hw,
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
++static void mt7996_remove_link_sta(struct mt7996_dev *dev,
++				   struct ieee80211_bss_conf *conf,
++				   struct mt7996_bss_conf *mconf,
++				   struct ieee80211_link_sta *link_sta,
++				   struct mt7996_link_sta *mlink)
++{
++	struct ieee80211_sta *sta = link_sta->sta;
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	int i;
++
++	if (!mlink)
++		return;
++
++	for (i = 0; i < ARRAY_SIZE(mlink->wcid.aggr); i++)
++			mt76_rx_aggr_stop(&dev->mt76, &mlink->wcid, i);
++
++	if (sta->mlo)
++		mt7996_mcu_teardown_mld_sta(dev, mconf, mlink);
++	else
++		mt7996_mcu_add_sta(dev, conf, mconf, link_sta, mlink, false, false);
++
++	mt7996_mac_wtbl_update(dev, mlink->wcid.idx,
++			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
++
++	for (i = 0; i < ARRAY_SIZE(mlink->twt.flow); i++)
++		mt7996_mac_twt_teardown_flow(dev, mlink, i);
++
++	rcu_assign_pointer(mlink->sta->link[mlink->wcid.link_id], NULL);
++
++	spin_lock_bh(&dev->mt76.sta_poll_lock);
++	if (!list_empty(&mlink->wcid.poll_list))
++		list_del_init(&mlink->wcid.poll_list);
++	if (!list_empty(&mlink->rc_list))
++		list_del_init(&mlink->rc_list);
++	spin_unlock_bh(&dev->mt76.sta_poll_lock);
++
++	/* TODO: update primary link */
++	if (sta->valid_links) {
++		if (mlink->wcid.link_id == msta->pri_link)
++			msta->pri_link = msta->sec_link;
++
++		if (sta->valid_links & ~(BIT(msta->pri_link)))
++			msta->sec_link = __ffs(sta->valid_links & ~(BIT(msta->pri_link)));
++		else
++			msta->sec_link = msta->pri_link;
++	}
++
++	mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
++	mt76_wcid_mask_clear(dev->mt76.wcid_mask, mlink->wcid.idx);
++	mt76_wcid_mask_clear(dev->mt76.wcid_phy_mask, mlink->wcid.idx);
++
++	if (mlink != &msta->deflink)
++		kfree(mlink);
++}
++
++static int mt7996_add_link_sta(struct mt7996_dev *dev,
++			       struct ieee80211_bss_conf *conf,
++			       struct mt7996_bss_conf *mconf,
++			       struct ieee80211_link_sta *link_sta, bool assoc)
++{
++	struct ieee80211_sta *sta = link_sta->sta;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)conf->vif->drv_priv;
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	u8 link_id = link_sta->link_id;
++	struct mt7996_link_sta *mlink = NULL;
++	int idx, ret;
++
++	if (!rcu_access_pointer(msta->link[link_id])) {
++		idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
++		if (idx < 0)
++			return -ENOSPC;
++
++		if (sta->mlo) {
++			mlink = kzalloc(sizeof(*mlink), GFP_KERNEL);
++			if (!mlink)
++				return -ENOMEM;
++		} else {
++			mlink = &msta->deflink;
++		}
++
++		INIT_LIST_HEAD(&mlink->rc_list);
++		INIT_LIST_HEAD(&mlink->wcid.poll_list);
++		msta->vif = mvif;
++		mlink->wcid.sta = 1;
++		mlink->wcid.idx = idx;
++		mlink->wcid.phy_idx = mconf->phy->mt76->band_idx;
++		mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
++		mlink->wcid.def_wcid = &msta->deflink.wcid;
++		mlink->sta = msta;
++		if (sta->valid_links) {
++			mlink->wcid.link_valid = true;
++			mlink->wcid.link_id = link_id;
++			if (sta->valid_links & ~(BIT(msta->pri_link)))
++				msta->sec_link = __ffs(sta->valid_links &
++						       ~(BIT(msta->pri_link)));
++			else
++				msta->sec_link = msta->pri_link;
++		}
++
++		rcu_assign_pointer(msta->link[link_id], mlink);
++
++		ewma_signal_init(&mlink->wcid.rssi);
++		if (mconf->phy->mt76->band_idx == MT_BAND1)
++			mt76_wcid_mask_set(dev->mt76.wcid_phy_mask, idx);
++		rcu_assign_pointer(dev->mt76.wcid[idx], &mlink->wcid);
++		mt76_wcid_init(&mlink->wcid);
++	}
++
++	if (!assoc)
++		return 0;
++
++	if (!mlink)
++		mlink = mlink_dereference_protected(msta, link_id);
++	mt7996_mac_wtbl_update(dev, mlink->wcid.idx,
++			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
++
++	ret = mt7996_mcu_add_sta(dev, conf, mconf, link_sta, mlink, true, true);
++	if (ret)
++		goto error;
++
++	ret = mt7996_mcu_add_rate_ctrl(dev, conf, mconf, link_sta, mlink, false);
++	if (ret)
++		goto error;
++
++	ewma_avg_signal_init(&mlink->avg_ack_signal);
++
++	return 0;
++error:
++	mt7996_remove_link_sta(dev, conf, mconf, link_sta, mlink);
++	return ret;
++}
++
++static void
++mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++			    struct ieee80211_sta *sta, unsigned long rem)
++{
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	unsigned int link_id;
++
++	for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
++		struct mt7996_bss_conf *mconf =
++			mconf_dereference_protected(mvif, link_id);
++		struct mt7996_link_sta *mlink =
++			mlink_dereference_protected(msta, link_id);
++		struct ieee80211_bss_conf *conf =
++			link_conf_dereference_protected(vif, link_id);
++		struct ieee80211_link_sta *link_sta =
++			link_sta_dereference_protected(sta, link_id);
++
++		mt7996_remove_link_sta(dev, conf, mconf, link_sta, mlink);
++	}
++}
++
++static int
++mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++			 struct ieee80211_sta *sta, unsigned long add,
++			 bool assoc)
++{
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_link_sta *mlink;
++	unsigned int link_id;
++	int i, ret;
++
++	for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
++		struct mt7996_bss_conf *mconf =
++			mconf_dereference_protected(mvif, link_id);
++		struct ieee80211_bss_conf *conf =
++			link_conf_dereference_protected(vif, link_id);
++		struct ieee80211_link_sta *link_sta =
++			link_sta_dereference_protected(sta, link_id);
++
++		ret = mt7996_add_link_sta(dev, conf, mconf, link_sta, assoc);
++		if (ret)
++			goto error;
++	}
++
++	if (!assoc)
++		return 0;
++
++	mlink = mlink_dereference_protected(msta, msta->pri_link);
++	for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
++		struct mt76_txq *mtxq;
++
++		if (!sta->txq[i])
++			continue;
++		mtxq = (struct mt76_txq *)sta->txq[i]->drv_priv;
++		mtxq->wcid = mlink->wcid.idx;
++	}
++
++	ret = mt7996_mcu_add_mld_sta(dev, vif, sta, add);
++	if (ret)
++		goto error;
++
++	return 0;
++error:
++	mt7996_mac_sta_remove_links(dev, vif, sta, add);
++	return ret;
++}
++
+ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 		       struct ieee80211_sta *sta)
+ {
+ 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+-	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct mt7996_bss_conf *mconf = mconf_dereference_protected(mvif, 0);
+-	struct mt7996_link_sta *mlink = &msta->deflink;
+-	u8 band_idx = mconf->phy->mt76->band_idx;
+-	int idx;
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_bss_conf *mconf;
++	u8 link_id = sta->valid_links ? __ffs(sta->valid_links) : 0;
++	unsigned long add = BIT(link_id);
++	int ret;
+ 
+ #ifdef CONFIG_MTK_VENDOR
+ 	struct mt7996_phy *phy = &dev->phy;
+ #endif
+ 
+-	idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
+-	if (idx < 0)
+-		return -ENOSPC;
+-
+-	INIT_LIST_HEAD(&mlink->rc_list);
+-	INIT_LIST_HEAD(&mlink->wcid.poll_list);
+-	msta->vif = mvif;
+-	mlink->wcid.sta = 1;
+-	mlink->wcid.idx = idx;
+-	mlink->wcid.phy_idx = band_idx;
+-	mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
+-	mlink->sta = msta;
+-
+-	rcu_assign_pointer(msta->link[0], mlink);
++	msta->pri_link = link_id;
++	ret = mt7996_mac_sta_add_links(dev, vif, sta, add, false);
++	if (ret)
++		return ret;
+ 
+ #ifdef CONFIG_MTK_VENDOR
++	mconf = mconf_dereference_protected(mvif, link_id);
+ 	mt7996_vendor_amnt_sta_remove(mconf->phy, sta);
+ #endif
+ 
+ #ifdef CONFIG_MTK_VENDOR
+-	switch (band_idx) {
++	switch (mconf->phy->mt76->band_idx) {
+ 	case MT_BAND1:
+ 		phy = mt7996_phy2(dev);
+ 		break;
+@@ -990,28 +1182,11 @@ void mt7996_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 			  struct ieee80211_sta *sta)
+ {
+ 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+-	struct mt7996_bss_conf *mconf;
+-	struct mt7996_link_sta *mlink;
+-	struct ieee80211_bss_conf *conf;
+-	struct ieee80211_link_sta *link_sta;
++	unsigned long add = sta->valid_links ?: BIT(0);
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+-	conf = link_conf_dereference_protected(vif, 0);
+-	mconf = mconf_dereference_protected(mvif, 0);
+-	link_sta = link_sta_dereference_protected(sta, 0);
+-	mlink = mlink_dereference_protected(msta, 0);
+-
+-	mt7996_mac_wtbl_update(dev, mlink->wcid.idx,
+-			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+-
+-	mt7996_mcu_add_sta(dev, conf, mconf, link_sta, mlink, true, true);
+-	mt7996_mcu_add_rate_ctrl(dev, conf, mconf, link_sta, mlink, false);
+-	mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET;
+-
+-	ewma_avg_signal_init(&mlink->avg_ack_signal);
++	mt7996_mac_sta_add_links(dev, vif, sta, add, true);
+ 
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+@@ -1020,34 +1195,9 @@ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 			   struct ieee80211_sta *sta)
+ {
+ 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+-	struct mt7996_bss_conf *mconf;
+-	struct mt7996_link_sta *mlink;
+-	struct ieee80211_bss_conf *conf;
+-	struct ieee80211_link_sta *link_sta;
+-	int i;
+-
+-	conf = link_conf_dereference_protected(vif, 0);
+-	mconf = mconf_dereference_protected(mvif, 0);
+-	link_sta = link_sta_dereference_protected(sta, 0);
+-	mlink = mlink_dereference_protected(msta, 0);
+-	mt7996_mcu_add_sta(dev, conf, mconf, link_sta, mlink, false, false);
+-
+-	mt7996_mac_wtbl_update(dev, mlink->wcid.idx,
+-			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+-
+-	for (i = 0; i < ARRAY_SIZE(mlink->twt.flow); i++)
+-		mt7996_mac_twt_teardown_flow(dev, mlink, i);
++	unsigned long rem = sta->valid_links ?: BIT(0);
+ 
+-	spin_lock_bh(&mdev->sta_poll_lock);
+-	if (!list_empty(&mlink->wcid.poll_list))
+-		list_del_init(&mlink->wcid.poll_list);
+-	if (!list_empty(&mlink->rc_list))
+-		list_del_init(&mlink->rc_list);
+-	spin_unlock_bh(&mdev->sta_poll_lock);
+-
+-	rcu_assign_pointer(msta->link[0], NULL);
++	mt7996_mac_sta_remove_links(dev, vif, sta, rem);
+ }
+ 
+ static void mt7996_tx(struct ieee80211_hw *hw,
+@@ -2162,6 +2312,32 @@ mt7996_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	return ret;
+ }
+ 
++static int
++mt7996_change_sta_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++			struct ieee80211_sta *sta, u16 old_links, u16 new_links)
++{
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	unsigned long add = new_links & ~old_links;
++	unsigned long rem = old_links & ~new_links;
++	int ret = 0;
++
++	mutex_lock(&dev->mt76.mutex);
++
++	if (rem)
++		mt7996_mac_sta_remove_links(dev, vif, sta, rem);
++
++	ret = mt7996_mac_sta_add_links(dev, vif, sta, add, false);
++	if (ret)
++		goto remove;
++
++	goto out;
++remove:
++	mt7996_mac_sta_remove_links(dev, vif, sta, add);
++out:
++	mutex_unlock(&dev->mt76.mutex);
++	return ret;
++}
++
+ const struct ieee80211_ops mt7996_ops = {
+ 	.add_chanctx = ieee80211_emulate_add_chanctx,
+ 	.remove_chanctx = ieee80211_emulate_remove_chanctx,
+@@ -2223,4 +2399,5 @@ const struct ieee80211_ops mt7996_ops = {
+ 	.unassign_vif_chanctx = mt7996_unassign_vif_chanctx,
+ 	.switch_vif_chanctx = mt7996_switch_vif_chanctx,
+ 	.change_vif_links = mt7996_change_vif_links,
++	.change_sta_links = mt7996_change_sta_links,
+ };
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index d3e244ee..7138230e 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2426,6 +2426,123 @@ out:
+ 				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+ }
+ 
++static void
++mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
++			     struct ieee80211_sta *sta)
++{
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct sta_rec_mld_setup *mld_setup;
++	struct mld_setup_link *mld_setup_link;
++	struct mt7996_link_sta *mlink;
++	struct mt7996_bss_conf *mconf;
++	struct tlv *tlv;
++	unsigned long valid_links = sta->valid_links;
++	unsigned int link_id;
++
++	mlink = mlink_dereference_protected(msta, msta->pri_link);
++	if (!mlink)
++		return;
++
++	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MLD,
++				      sizeof(*mld_setup) +
++				      sizeof(struct mld_setup_link) *
++					     hweight16(sta->valid_links));
++
++	mld_setup = (struct sta_rec_mld_setup *)tlv;
++	memcpy(mld_setup->mld_addr, sta->addr, ETH_ALEN);
++	mld_setup->setup_wcid = cpu_to_le16(mlink->wcid.idx);
++	mld_setup->primary_id = cpu_to_le16(mlink->wcid.idx);
++	if (msta->sec_link != msta->pri_link) {
++		mlink = mlink_dereference_protected(msta, msta->sec_link);
++		if (!mlink)
++			return;
++	}
++	mld_setup->seconed_id = cpu_to_le16(mlink->wcid.idx);
++	mld_setup->link_num = hweight16(sta->valid_links);
++
++	mld_setup_link = (struct mld_setup_link *)mld_setup->link_info;
++	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++		mlink = mlink_dereference_protected(msta, link_id);
++		mconf = mconf_dereference_protected(msta->vif, link_id);
++
++		mld_setup_link->wcid = cpu_to_le16(mlink->wcid.idx);
++		mld_setup_link->bss_idx = mconf->mt76.idx;
++		mld_setup_link++;
++	}
++}
++
++static void
++mt7996_mcu_sta_eht_mld_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
++			   struct ieee80211_sta *sta)
++{
++	struct sta_rec_eht_mld *eht_mld;
++	struct tlv *tlv;
++	int i;
++
++	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT_MLD, sizeof(*eht_mld));
++	eht_mld = (struct sta_rec_eht_mld *)tlv;
++
++	for (i = 0; i < ARRAY_SIZE(eht_mld->str_cap); i++)
++		eht_mld->str_cap[i] = 0x7;
++	/* TODO:
++	eht_mld->nsep = ;
++	eht_mld->eml_cap = cpu_to_le16()
++	*/
++}
++
++int mt7996_mcu_add_mld_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++			   struct ieee80211_sta *sta, unsigned long add)
++{
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	unsigned int link_id;
++
++	if (!sta->mlo)
++		return 0;
++
++	for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
++		struct mt7996_bss_conf *mconf =
++			mconf_dereference_protected(mvif, link_id);
++		struct mt7996_link_sta *mlink =
++			mlink_dereference_protected(msta, link_id);
++		struct sk_buff *skb;
++		int ret;
++
++		skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
++						      &mlink->wcid,
++						      MT7996_STA_UPDATE_MAX_SIZE);
++		if (IS_ERR(skb))
++			return PTR_ERR(skb);
++		/* starec mld setup */
++		mt7996_mcu_sta_mld_setup_tlv(dev, skb, sta);
++		/* starec eht mld */
++		mt7996_mcu_sta_eht_mld_tlv(dev, skb, sta);
++		ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
++					    MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
++		if (ret)
++			return ret;
++	}
++	return 0;
++}
++int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev,
++				struct mt7996_bss_conf *mconf,
++				struct mt7996_link_sta *mlink)
++{
++	struct sk_buff *skb;
++
++	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76,
++					      &mconf->mt76,
++					      &mlink->wcid,
++					      MT7996_STA_UPDATE_MAX_SIZE);
++	if (IS_ERR(skb))
++		return PTR_ERR(skb);
++
++	mt76_connac_mcu_add_tlv(skb, STA_REC_MLD_OFF, sizeof(struct tlv));
++
++	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
++				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
++}
++
+ static int
+ mt7996_mcu_sta_key_tlv(struct mt76_wcid *wcid,
+ 		       struct sk_buff *skb,
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index b15796dc..23d44cd3 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -692,6 +692,35 @@ struct sta_rec_hdr_trans {
+ 	u8 mesh;
+ } __packed;
+ 
++struct sta_rec_mld_setup {
++	__le16 tag;
++	__le16 len;
++	u8 mld_addr[ETH_ALEN];
++	__le16 primary_id;
++	__le16 seconed_id;
++	__le16 setup_wcid;
++	u8 link_num;
++	u8 info;
++	u8 __rsv[2];
++	u8 link_info[];
++} __packed;
++
++struct mld_setup_link {
++	__le16 wcid;
++	u8 bss_idx;
++	u8 __rsv[1];
++} __packed;
++
++struct sta_rec_eht_mld {
++	__le16 tag;
++	__le16 len;
++	u8 nsep;
++	u8 __rsv1[2];
++	u8 str_cap[__MT_MAX_BAND];
++	__le16 eml_cap;
++	u8 __rsv2[4];
++} __packed;
++
+ struct hdr_trans_en {
+ 	__le16 tag;
+ 	__le16 len;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 09b1088e..4775e1f8 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -330,6 +330,8 @@ struct mt7996_sta {
+ 	struct mt7996_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
+ 
+ 	struct mt7996_vif *vif;
++	u8 pri_link;
++	u8 sec_link;
+ };
+ 
+ struct mt7996_bss_conf {
+@@ -857,6 +859,9 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ 		       struct mt7996_bss_conf *mconf,
+ 		       struct ieee80211_link_sta *link_sta,
+ 		       struct mt7996_link_sta *mlink, bool enable, bool newly);
++int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev,
++				struct mt7996_bss_conf *mconf,
++				struct mt7996_link_sta *mlink);
+ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ 			 struct ieee80211_ampdu_params *params,
+ 			 bool add);
+@@ -880,6 +885,8 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
+ 			     struct mt7996_bss_conf *mconf,
+ 			     struct ieee80211_link_sta *link_sta,
+ 			     struct mt7996_link_sta *mlink, bool changed);
++int mt7996_mcu_add_mld_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++			   struct ieee80211_sta *sta, unsigned long add);
+ int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef);
+ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag);
+ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0095-wifi-mt76-mt7996-rework-RXD-for-multi-link-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0095-wifi-mt76-mt7996-rework-RXD-for-multi-link-support.patch
deleted file mode 100644
index 3d24613..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0095-wifi-mt76-mt7996-rework-RXD-for-multi-link-support.patch
+++ /dev/null
@@ -1,64 +0,0 @@
-From eeabbc93450223a64bfbb812c2dd938219e58bc2 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Mon, 4 Dec 2023 14:50:47 +0800
-Subject: [PATCH 095/116] wifi: mt76: mt7996: rework RXD for multi-link support
-
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/mac.c | 27 ++-------------------------
- 1 file changed, 2 insertions(+), 25 deletions(-)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 65431c7..a32b416 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -51,29 +51,6 @@ static const struct mt7996_dfs_radar_spec jp_radar_specs = {
- 	},
- };
- 
--static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev,
--					    u16 idx, bool unicast)
--{
--	struct mt7996_link_sta *mlink;
--	struct mt76_wcid *wcid;
--
--	if (idx >= ARRAY_SIZE(dev->mt76.wcid))
--		return NULL;
--
--	wcid = rcu_dereference(dev->mt76.wcid[idx]);
--	if (unicast || !wcid)
--		return wcid;
--
--	if (!wcid->sta)
--		return NULL;
--
--	mlink = container_of(wcid, struct mt7996_link_sta, wcid);
--	if (!mlink->sta->vif)
--		return NULL;
--
--	return &mlink->wcid;
--}
--
- bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask)
- {
- 	mt76_rmw(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX,
-@@ -376,10 +353,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
- 
- 	unicast = FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) == MT_RXD3_NORMAL_U2M;
- 	idx = FIELD_GET(MT_RXD1_NORMAL_WLAN_IDX, rxd1);
--	status->wcid = mt7996_rx_get_wcid(dev, idx, unicast);
-+	status->wcid = mt7996_get_link_wcid(dev, idx, band_idx);
- 
- 	if (status->wcid) {
--		mlink = container_of(status->wcid, struct mt7996_link_sta, wcid);
-+		mlink = wcid_to_mlink(status->wcid);
- 		spin_lock_bh(&dev->mt76.sta_poll_lock);
- 		if (list_empty(&mlink->wcid.poll_list))
- 			list_add_tail(&mlink->wcid.poll_list,
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0096-mtk-mt76-mt7996-introduce-mt7996_band_phy-for-ch-ban.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0096-mtk-mt76-mt7996-introduce-mt7996_band_phy-for-ch-ban.patch
new file mode 100644
index 0000000..4a5208f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0096-mtk-mt76-mt7996-introduce-mt7996_band_phy-for-ch-ban.patch
@@ -0,0 +1,271 @@
+From 05433210ece4bbeee342650a8c4ea98c8e1c823c Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 1 Dec 2023 16:01:53 +0800
+Subject: [PATCH 096/199] mtk: mt76: mt7996: introduce mt7996_band_phy() for ch
+ band and phy mapping
+
+For MLO devices, one ieee80211_hw can be mapped to several bands, and
+thus several mt76_phy. Add mt7996_band_phy() to temporarily do the
+mapping.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/main.c   | 149 +++++++++++++++++++++++++++++-------------------
+ mt7996/mt7996.h |  22 +++++++
+ 2 files changed, 112 insertions(+), 59 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index e850942f..ab99f1cf 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -41,9 +41,8 @@ static void mt7996_testmode_disable_all(struct mt7996_dev *dev)
+ int mt7996_run(struct ieee80211_hw *hw)
+ {
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+-	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	bool running;
+-	int ret;
++	int band, ret;
+ 
+ 	running = mt7996_dev_running(dev);
+ 	if (!running) {
+@@ -62,66 +61,76 @@ int mt7996_run(struct ieee80211_hw *hw)
+ 
+ 	mt7996_testmode_disable_all(dev);
+ 
+-	mt7996_mac_enable_nf(dev, phy->mt76->band_idx);
++	for (band = 0; band < NUM_NL80211_BANDS; band++) {
++		struct mt7996_phy *phy;
+ 
+-	ret = mt7996_mcu_set_rts_thresh(phy, 0x92b);
+-	if (ret)
+-		goto out;
++		if (!hw->wiphy->bands[band])
++			continue;
+ 
+-	ret = mt7996_mcu_set_radio_en(phy, true);
+-	if (ret)
+-		goto out;
++		phy = mt7996_band_phy(hw, band);
+ 
+-	ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_RX_PATH);
+-	if (ret)
+-		goto out;
++		if (!phy || test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
++			continue;
+ 
+-	/* set a parking channel */
+-	ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
+-	if (ret)
+-		goto out;
++		mt7996_mac_enable_nf(dev, phy->mt76->band_idx);
+ 
+-	ret = mt7996_mcu_set_thermal_throttling(phy, MT7996_THERMAL_THROTTLE_MAX);
+-	if (ret)
+-		goto out;
++		ret = mt7996_mcu_set_rts_thresh(phy, 0x92b);
++		if (ret)
++			goto out;
+ 
+-	ret = mt7996_mcu_set_thermal_protect(phy, true);
+-	if (ret)
+-		goto out;
++		ret = mt7996_mcu_set_radio_en(phy, true);
++		if (ret)
++			goto out;
+ 
+-	ret = mt7996_mcu_set_scs(phy, SCS_ENABLE);
+-	if (ret)
+-		goto out;
++		ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_RX_PATH);
++		if (ret)
++			goto out;
+ 
+-#ifdef CONFIG_MTK_DEBUG
+-	phy->sr_enable = true;
+-	phy->enhanced_sr_enable = true;
+-	phy->thermal_protection_enable = true;
++		/* set a parking channel */
++		ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
++		if (ret)
++			goto out;
++
++		ret = mt7996_mcu_set_thermal_throttling(phy, MT7996_THERMAL_THROTTLE_MAX);
++		if (ret)
++			goto out;
+ 
+-	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
+-						dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
++		ret = mt7996_mcu_set_thermal_protect(phy, true);
++		if (ret)
++			goto out;
++
++		ret = mt7996_mcu_set_scs(phy, SCS_ENABLE);
++		if (ret)
++			goto out;
+ 
+-	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
+-						dev->dbg.sku_disable ? 0 : phy->sku_path_en);
++#ifdef CONFIG_MTK_DEBUG
++		phy->sr_enable = true;
++		phy->enhanced_sr_enable = true;
++		phy->thermal_protection_enable = true;
++		ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
++						   dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
++
++		ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
++						   dev->dbg.sku_disable ? 0 : phy->sku_path_en);
+ #else
+-	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
+-						phy->sku_limit_en);
+-	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
+-						phy->sku_path_en);
++		ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
++						   phy->sku_limit_en);
++		ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
++						   phy->sku_path_en);
+ #endif
+-	if (ret)
+-		goto out;
+-
+-	set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
++		if (ret)
++			goto out;
+ 
+-	ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
+-				     MT7996_WATCHDOG_TIME);
++		set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+ 
+-	ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
++		ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
++					     MT7996_WATCHDOG_TIME);
+ 
+-	if (!running)
+-		mt7996_mac_reset_counters(phy);
++		if (!running)
++			mt7996_mac_reset_counters(phy);
++	}
+ 
++	ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
+ out:
+ 	return ret;
+ }
+@@ -143,18 +152,29 @@ static int mt7996_start(struct ieee80211_hw *hw)
+ static void mt7996_stop(struct ieee80211_hw *hw)
+ {
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+-	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	int band;
+ 
+-	cancel_delayed_work_sync(&phy->mt76->mac_work);
+ 	cancel_delayed_work_sync(&dev->scs_work);
+ 
+-	mutex_lock(&dev->mt76.mutex);
++	for (band = 0; band < NUM_NL80211_BANDS; band++) {
++		struct mt7996_phy *phy;
+ 
+-	mt7996_mcu_set_radio_en(phy, false);
++		if (!hw->wiphy->bands[band])
++			continue;
+ 
+-	clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
++		phy = mt7996_band_phy(hw, band);
+ 
+-	mutex_unlock(&dev->mt76.mutex);
++		if (!phy || !test_bit(MT76_STATE_RUNNING, &phy->mt76->state) ||
++		    (phy->chanctx && phy->chanctx->nbss_assigned))
++			continue;
++
++		cancel_delayed_work_sync(&phy->mt76->mac_work);
++
++		mutex_lock(&dev->mt76.mutex);
++		mt7996_mcu_set_radio_en(phy, false);
++		clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
++		mutex_unlock(&dev->mt76.mutex);
++	}
+ }
+ 
+ static inline int get_free_idx(u32 mask, u8 start, u8 end)
+@@ -2061,7 +2081,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	       struct ieee80211_scan_request *hw_req)
+ {
+ 	struct cfg80211_scan_request *req = &hw_req->req;
+-	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct mt7996_phy *phy = mt7996_band_phy(hw, req->channels[0]->band);
+ 
+ 	mutex_lock(&phy->dev->mt76.mutex);
+ 	if (WARN_ON(phy->scan_req || phy->scan_chan)) {
+@@ -2083,19 +2103,30 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ static void
+ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ {
+-	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	int band;
+ 
+-	cancel_delayed_work_sync(&phy->scan_work);
++	for (band = 0; band < NUM_NL80211_BANDS; band++) {
++		struct mt7996_phy *phy;
+ 
+-	mutex_lock(&phy->dev->mt76.mutex);
+-	mt7996_scan_complete(phy, true);
+-	mutex_unlock(&phy->dev->mt76.mutex);
++		if (!hw->wiphy->bands[band])
++			continue;
++
++		phy = mt7996_band_phy(hw, band);
++		if (!(test_bit(MT76_SCANNING, &phy->mt76->state)))
++			continue;
++
++		cancel_delayed_work_sync(&phy->scan_work);
++
++		mutex_lock(&phy->dev->mt76.mutex);
++		mt7996_scan_complete(phy, true);
++		mutex_unlock(&phy->dev->mt76.mutex);
++	}
+ }
+ 
+ static int
+ mt7996_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
+ {
+-	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct mt7996_phy *phy = mt7996_band_phy(hw, conf->def.chan->band);
+ 	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
+ 	int ret;
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 4775e1f8..d5b61a2e 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -789,6 +789,28 @@ mt7996_get_background_radar_cap(struct mt7996_dev *dev)
+ 	return 1;
+ }
+ 
++static inline struct mt7996_phy *
++mt7996_band_phy(struct ieee80211_hw *hw, enum nl80211_band band)
++{
++	struct mt76_phy *phy = hw->priv;
++
++	if (!(hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO))
++		return phy->priv;
++
++	/* TODO: mlo: temporarily hardcode */
++	if (band == NL80211_BAND_6GHZ)
++		phy = phy->dev->phys[MT_BAND2];
++	else if (band == NL80211_BAND_5GHZ)
++		phy = phy->dev->phys[MT_BAND1];
++	else
++		phy = phy->dev->phys[MT_BAND0];
++
++	if (!phy)
++		phy = hw->priv;
++
++	return phy->priv;
++}
++
+ static inline struct mt7996_chanctx *
+ mt7996_chanctx_get(struct ieee80211_chanctx_conf *ctx)
+ {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0096-wifi-mt76-mt7996-rework-mac-functions-for-multi-link.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0096-wifi-mt76-mt7996-rework-mac-functions-for-multi-link.patch
deleted file mode 100644
index fcb59e6..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0096-wifi-mt76-mt7996-rework-mac-functions-for-multi-link.patch
+++ /dev/null
@@ -1,249 +0,0 @@
-From 0bd2c0dd6b87178561f38f27cd0be5009b56d43c Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Mon, 4 Dec 2023 18:31:02 +0800
-Subject: [PATCH 096/116] wifi: mt76: mt7996: rework mac functions for
- multi-link support
-
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/mac.c | 91 +++++++++++++++++++++++++++++++++++++---------------
- 1 file changed, 65 insertions(+), 26 deletions(-)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index a32b416..31b9166 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -85,10 +85,11 @@ static int mt7996_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap)
- {
- 	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
- 	struct ethhdr *eth_hdr = (struct ethhdr *)(skb->data + hdr_gap);
--	struct mt7996_sta *msta = (struct mt7996_sta *)status->wcid;
-+	struct mt7996_link_sta *mlink = (struct mt7996_link_sta *)status->wcid;
- 	__le32 *rxd = (__le32 *)skb->data;
- 	struct ieee80211_sta *sta;
- 	struct ieee80211_vif *vif;
-+	struct ieee80211_bss_conf *conf;
- 	struct ieee80211_hdr hdr;
- 	u16 frame_control;
- 
-@@ -99,11 +100,14 @@ static int mt7996_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap)
- 	if (!(le32_to_cpu(rxd[1]) & MT_RXD1_NORMAL_GROUP_4))
- 		return -EINVAL;
- 
--	if (!msta || !msta->vif)
-+	if (!mlink->sta || !mlink->sta->vif)
- 		return -EINVAL;
- 
--	sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
--	vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv);
-+	sta = wcid_to_sta(status->wcid);
-+	vif = container_of((void *)mlink->sta->vif, struct ieee80211_vif, drv_priv);
-+	conf = rcu_dereference(vif->link_conf[mlink->wcid.link_id]);
-+	if (unlikely(!conf))
-+		return -ENOLINK;
- 
- 	/* store the info from RXD and ethhdr to avoid being overridden */
- 	frame_control = le32_get_bits(rxd[8], MT_RXD8_FRAME_CONTROL);
-@@ -116,7 +120,7 @@ static int mt7996_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap)
- 	switch (frame_control & (IEEE80211_FCTL_TODS |
- 				 IEEE80211_FCTL_FROMDS)) {
- 	case 0:
--		ether_addr_copy(hdr.addr3, vif->bss_conf.bssid);
-+		ether_addr_copy(hdr.addr3, conf->bssid);
- 		break;
- 	case IEEE80211_FCTL_FROMDS:
- 		ether_addr_copy(hdr.addr3, eth_hdr->h_source);
-@@ -955,15 +959,21 @@ u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id)
- }
- 
- static void
--mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb)
-+mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb,
-+		     struct mt76_wcid *wcid)
- {
- 	struct mt7996_sta *msta;
- 	struct mt7996_link_sta *mlink;
-+	struct ieee80211_link_sta *link_sta;
- 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- 	bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
- 	u16 fc, tid;
- 
--	if (!sta || !(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he))
-+	link_sta = rcu_dereference(sta->link[wcid->link_id]);
-+	if (!link_sta)
-+		return;
-+
-+	if (!sta->mlo && !(link_sta->ht_cap.ht_supported || link_sta->he_cap.has_he))
- 		return;
- 
- 	tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
-@@ -987,17 +997,17 @@ mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb)
- 		return;
- 
- 	msta = (struct mt7996_sta *)sta->drv_priv;
--	mlink = rcu_dereference(msta->link[0]);
-+	mlink = rcu_dereference(msta->link[msta->pri_link]);
- 	if (!test_and_set_bit(tid, &mlink->wcid.ampdu_state))
- 		ieee80211_start_tx_ba_session(sta, tid, 0);
- }
- 
- static void
- mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t,
--		 struct ieee80211_sta *sta, struct list_head *free_list)
-+		 struct ieee80211_sta *sta, struct mt76_wcid *wcid,
-+		 struct list_head *free_list)
- {
- 	struct mt76_dev *mdev = &dev->mt76;
--	struct mt76_wcid *wcid;
- 	__le32 *txwi;
- 	u16 wcid_idx;
- 
-@@ -1007,11 +1017,10 @@ mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t,
- 
- 	txwi = (__le32 *)mt76_get_txwi_ptr(mdev, t);
- 	if (sta) {
--		wcid = (struct mt76_wcid *)sta->drv_priv;
- 		wcid_idx = wcid->idx;
- 
- 		if (likely(t->skb->protocol != cpu_to_be16(ETH_P_PAE)))
--			mt7996_tx_check_aggr(sta, t->skb);
-+			mt7996_tx_check_aggr(sta, t->skb, wcid);
- 	} else {
- 		wcid_idx = le32_get_bits(txwi[9], MT_TXD9_WLAN_IDX);
- 	}
-@@ -1066,7 +1075,9 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
- 		 */
- 		info = le32_to_cpu(*cur_info);
- 		if (info & MT_TXFREE_INFO_PAIR) {
--			struct mt7996_link_sta *mlink;
-+			struct mt7996_sta *msta;
-+			unsigned long valid_links;
-+			unsigned int link_id;
- 			u16 idx;
- 
- 			idx = FIELD_GET(MT_TXFREE_INFO_WLAN_ID, info);
-@@ -1075,11 +1086,17 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
- 			if (!sta)
- 				continue;
- 
--			mlink = container_of(wcid, struct mt7996_link_sta, wcid);
-+			valid_links = sta->valid_links ?: BIT(0);
-+			msta = (struct mt7996_sta *)sta->drv_priv;
-+			/* for MLD STA, add all link's wcid to sta_poll_list */
- 			spin_lock_bh(&mdev->sta_poll_lock);
--			if (list_empty(&mlink->wcid.poll_list))
--				list_add_tail(&mlink->wcid.poll_list,
--					      &mdev->sta_poll_list);
-+			for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
-+				struct mt7996_link_sta *mlink =
-+					rcu_dereference(msta->link[link_id]);
-+
-+				if (list_empty(&mlink->wcid.poll_list))
-+					list_add_tail(&mlink->wcid.poll_list, &mdev->sta_poll_list);
-+			}
- 			spin_unlock_bh(&mdev->sta_poll_lock);
- 			continue;
- 		} else if (info & MT_TXFREE_INFO_HEADER) {
-@@ -1115,7 +1132,8 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
- 			if (!txwi)
- 				continue;
- 
--			mt7996_txwi_free(dev, txwi, sta, &free_list);
-+			mt7996_txwi_free(dev, txwi, sta, wcid, &free_list);
-+			txwi->jiffies = 0;
- 		}
- 	}
- 
-@@ -1537,19 +1555,29 @@ static void
- mt7996_update_vif_beacon(void *priv, u8 *mac, struct ieee80211_vif *vif)
- {
- 	struct ieee80211_hw *hw = priv;
--	struct ieee80211_bss_conf *conf = &vif->bss_conf;
-+	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	struct mt7996_bss_conf *mconf = &mvif->deflink;
-+	unsigned long update = vif->valid_links ?: BIT(0);
-+	unsigned int link_id;
- 
-+	mutex_lock(&dev->mt76.mutex);
- 	switch (vif->type) {
- 	case NL80211_IFTYPE_MESH_POINT:
- 	case NL80211_IFTYPE_ADHOC:
- 	case NL80211_IFTYPE_AP:
--		mt7996_mcu_add_beacon(hw, conf, mconf, conf->enable_beacon);
-+		for_each_set_bit(link_id, &update, IEEE80211_MLD_MAX_NUM_LINKS) {
-+			struct mt7996_bss_conf *mconf =
-+					mconf_dereference_protected(mvif, link_id);
-+			struct ieee80211_bss_conf *conf =
-+					link_conf_dereference_protected(vif, link_id);
-+
-+			mt7996_mcu_add_beacon(hw, conf, mconf, conf->enable_beacon);
-+		}
- 		break;
- 	default:
- 		break;
- 	}
-+	mutex_unlock(&dev->mt76.mutex);
- }
- 
- static void
-@@ -1585,7 +1613,7 @@ void mt7996_tx_token_put(struct mt7996_dev *dev)
- 
- 	spin_lock_bh(&dev->mt76.token_lock);
- 	idr_for_each_entry(&dev->mt76.token, txwi, id) {
--		mt7996_txwi_free(dev, txwi, NULL, NULL);
-+		mt7996_txwi_free(dev, txwi, NULL, NULL, NULL);
- 		dev->mt76.token_count--;
- 	}
- 	spin_unlock_bh(&dev->mt76.token_lock);
-@@ -2266,21 +2294,31 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
- 	u32 changed;
- 	LIST_HEAD(list);
- 
-+	rcu_read_lock();
- 	spin_lock_bh(&dev->mt76.sta_poll_lock);
- 	list_splice_init(&dev->sta_rc_list, &list);
- 
- 	while (!list_empty(&list)) {
-+		u8 link_id;
-+
- 		mlink = list_first_entry(&list, struct mt7996_link_sta, rc_list);
-+		link_id = mlink->wcid.link_id;
-+
- 		list_del_init(&mlink->rc_list);
- 		changed = mlink->changed;
- 		mlink->changed = 0;
- 		spin_unlock_bh(&dev->mt76.sta_poll_lock);
- 
--		sta = container_of((void *)mlink->sta, struct ieee80211_sta, drv_priv);
--		link_sta = &sta->deflink;
- 		vif = container_of((void *)mlink->sta->vif, struct ieee80211_vif, drv_priv);
--		conf = &vif->bss_conf;
--		mconf = &mlink->sta->vif->deflink;
-+		conf = rcu_dereference(vif->link_conf[link_id]);
-+		mconf = rcu_dereference(mlink->sta->vif->link[link_id]);
-+		sta = wcid_to_sta(&mlink->wcid);
-+		link_sta = rcu_dereference(sta->link[link_id]);
-+
-+		if (unlikely(!conf || !mconf || !link_sta)) {
-+			spin_lock_bh(&dev->mt76.sta_poll_lock);
-+			continue;
-+		}
- 
- 		if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED |
- 			       IEEE80211_RC_NSS_CHANGED |
-@@ -2295,6 +2333,7 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
- 	}
- 
- 	spin_unlock_bh(&dev->mt76.sta_poll_lock);
-+	rcu_read_unlock();
- }
- 
- void mt7996_mac_work(struct work_struct *work)
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0097-mtk-mt76-mt7996-rework-ieee80211_ops-callbacks-for-l.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0097-mtk-mt76-mt7996-rework-ieee80211_ops-callbacks-for-l.patch
new file mode 100644
index 0000000..5e2b865
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0097-mtk-mt76-mt7996-rework-ieee80211_ops-callbacks-for-l.patch
@@ -0,0 +1,501 @@
+From 874f7bc5d6481d84d3d38f0a598ca791dc9755d8 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 1 Dec 2023 17:26:43 +0800
+Subject: [PATCH 097/199] mtk: mt76: mt7996: rework ieee80211_ops callbacks for
+ link consideration
+
+Extend ieee80211 callback functions to support multi-link operation.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/main.c | 313 ++++++++++++++++++++++++++++++++------------------
+ 1 file changed, 204 insertions(+), 109 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index ab99f1cf..6148c834 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -572,7 +572,6 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ 			  struct ieee80211_key_conf *key)
+ {
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+-	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_sta *msta = sta ? (struct mt7996_sta *)sta->drv_priv :
+ 				  &mvif->sta;
+@@ -582,70 +581,77 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ 	u8 *wcid_keyidx;
+ 	int idx = key->keyidx;
+ 	int err = 0;
++	unsigned long add;
++	unsigned int link_id;
+ 
+-	/* The hardware does not support per-STA RX GTK, fallback
+-	 * to software mode for these.
+-	 */
+-	if ((vif->type == NL80211_IFTYPE_ADHOC ||
+-	     vif->type == NL80211_IFTYPE_MESH_POINT) &&
+-	    (key->cipher == WLAN_CIPHER_SUITE_TKIP ||
+-	     key->cipher == WLAN_CIPHER_SUITE_CCMP) &&
+-	    !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+-		return -EOPNOTSUPP;
++	if (key->link_id >= 0) {
++		add = BIT(key->link_id);
++	} else {
++		if (sta)
++			add = sta->valid_links ?: BIT(0);
++		else
++			add = vif->valid_links ?: BIT(0);
++	}
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+-	conf = link_conf_dereference_protected(vif, 0);
+-	mconf = mconf_dereference_protected(mvif, 0);
+-	mlink = mlink_dereference_protected(msta, 0);
+-	wcid_keyidx = &mlink->wcid.hw_key_idx;
+-
+-	/* fall back to sw encryption for unsupported ciphers */
+-	switch (key->cipher) {
+-	case WLAN_CIPHER_SUITE_TKIP:
+-	case WLAN_CIPHER_SUITE_CCMP:
+-	case WLAN_CIPHER_SUITE_CCMP_256:
+-	case WLAN_CIPHER_SUITE_GCMP:
+-	case WLAN_CIPHER_SUITE_GCMP_256:
+-	case WLAN_CIPHER_SUITE_SMS4:
+-		break;
+-	case WLAN_CIPHER_SUITE_AES_CMAC:
+-	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+-	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+-	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+-		if (key->keyidx == 6 || key->keyidx == 7) {
+-			wcid_keyidx = &wcid->hw_key_idx2;
+-			key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE;
++
++	for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
++		conf = link_conf_dereference_protected(vif, link_id);
++		mconf = mconf_dereference_protected(mvif, link_id);
++		mlink = mlink_dereference_protected(msta, link_id);
++		wcid_keyidx = &mlink->wcid.hw_key_idx;
++
++		if (!conf || !mconf || !mlink)
++			continue;
++
++		/* fall back to sw encryption for unsupported ciphers */
++		switch (key->cipher) {
++		case WLAN_CIPHER_SUITE_TKIP:
++		case WLAN_CIPHER_SUITE_CCMP:
++		case WLAN_CIPHER_SUITE_CCMP_256:
++		case WLAN_CIPHER_SUITE_GCMP:
++		case WLAN_CIPHER_SUITE_GCMP_256:
++		case WLAN_CIPHER_SUITE_SMS4:
+ 			break;
++		case WLAN_CIPHER_SUITE_AES_CMAC:
++		case WLAN_CIPHER_SUITE_BIP_CMAC_256:
++		case WLAN_CIPHER_SUITE_BIP_GMAC_128:
++		case WLAN_CIPHER_SUITE_BIP_GMAC_256:
++			if (key->keyidx == 6 || key->keyidx == 7) {
++				wcid_keyidx = &mlink->wcid.hw_key_idx2;
++				key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE;
++				break;
++			}
++			fallthrough;
++		case WLAN_CIPHER_SUITE_WEP40:
++		case WLAN_CIPHER_SUITE_WEP104:
++		default:
++			mutex_unlock(&dev->mt76.mutex);
++			return -EOPNOTSUPP;
+ 		}
+-		fallthrough;
+-	case WLAN_CIPHER_SUITE_WEP40:
+-	case WLAN_CIPHER_SUITE_WEP104:
+-	default:
+-		mutex_unlock(&dev->mt76.mutex);
+-		return -EOPNOTSUPP;
+-	}
+ 
+-	if (cmd == SET_KEY && !sta && !mconf->mt76.cipher) {
+-		mconf->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher);
+-		mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, true);
+-	}
++		if (cmd == SET_KEY && !sta && !mconf->mt76.cipher) {
++			mconf->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher);
++			mt7996_mcu_add_bss_info(mconf->phy, conf, mconf, mlink, true);
++		}
+ 
+-	if (cmd == SET_KEY) {
+-		*wcid_keyidx = idx;
+-	} else {
+-		if (idx == *wcid_keyidx)
+-			*wcid_keyidx = -1;
+-		goto out;
+-	}
++		if (cmd == SET_KEY) {
++			*wcid_keyidx = idx;
++		} else {
++			if (idx == *wcid_keyidx)
++				*wcid_keyidx = -1;
++			goto out;
++		}
+ 
+-	mt76_wcid_key_setup(&dev->mt76, &mlink->wcid, key);
++		mt76_wcid_key_setup(&dev->mt76, &mlink->wcid, key);
+ 
+-	if (key->keyidx == 6 || key->keyidx == 7)
+-		err = mt7996_mcu_bcn_prot_enable(dev, conf, mconf, mlink, key);
+-	else
+-		err = mt7996_mcu_add_key(&dev->mt76, mconf, key,
+-					 MCU_WMWA_UNI_CMD(STA_REC_UPDATE),
+-					 &mlink->wcid, cmd);
++		if (key->keyidx == 6 || key->keyidx == 7)
++			err = mt7996_mcu_bcn_prot_enable(dev, conf, mconf, mlink, key);
++		else
++			err = mt7996_mcu_add_key(&dev->mt76, mconf, key,
++						 MCU_WMWA_UNI_CMD(STA_REC_UPDATE),
++						 &mlink->wcid, cmd);
++	}
+ out:
+ 	mutex_unlock(&dev->mt76.mutex);
+ 
+@@ -701,7 +707,11 @@ mt7996_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	};
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+-	mconf = mconf_dereference_protected(mvif, 0);
++	mconf = mconf_dereference_protected(mvif, link_id);
++	if (!mconf) {
++		mutex_unlock(&dev->mt76.mutex);
++		return -EINVAL;
++	}
+ 
+ 	/* firmware uses access class index */
+ 	mconf->queue_params[mq_to_aci[queue]] = *params;
+@@ -844,19 +854,26 @@ mt7996_update_mu_group(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
+ static void mt7996_vif_cfg_changed(struct ieee80211_hw *hw,
+ 				   struct ieee80211_vif *vif, u64 changed)
+ {
+-	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+ 	if (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) {
+-		struct ieee80211_bss_conf *conf = &vif->bss_conf;
+ 		struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-		struct mt7996_bss_conf *mconf = mconf_dereference_protected(mvif, 0);
+-		struct mt7996_link_sta *mlink = mlink_dereference_protected(&mvif->sta, 0);
+-
+-		mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, true);
+-		mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, true, false);
++		unsigned long valid_links = vif->valid_links ?: BIT(0);
++		unsigned int link_id;
++
++		for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++			struct ieee80211_bss_conf *conf =
++				link_conf_dereference_protected(vif, link_id);
++			struct mt7996_bss_conf *mconf =
++				mconf_dereference_protected(mvif, link_id);
++			struct mt7996_link_sta *mlink =
++				mlink_dereference_protected(&mvif->sta, link_id);
++
++			mt7996_mcu_add_bss_info(mconf->phy, conf, mconf, mlink, true);
++			mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, true, false);
++		}
+ 	}
+ 
+ 	mutex_unlock(&dev->mt76.mutex);
+@@ -875,8 +892,13 @@ static void mt7996_link_info_changed(struct ieee80211_hw *hw,
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+-	mconf = mconf_dereference_protected(mvif, 0);
+-	mlink = mlink_dereference_protected(&mvif->sta, 0);
++	mconf = mconf_dereference_protected(mvif, info->link_id);
++	mlink = mlink_dereference_protected(&mvif->sta, info->link_id);
++	if (!mconf || !mlink)
++		goto out;
++
++	if (mconf->phy)
++		phy = mconf->phy;
+ 	/* station mode uses BSSID to map the wlan entry to a peer,
+ 	 * and then peer references bss_info_rfch to set bandwidth cap.
+ 	 */
+@@ -932,6 +954,7 @@ static void mt7996_link_info_changed(struct ieee80211_hw *hw,
+ 	if (changed & BSS_CHANGED_MU_GROUPS)
+ 		mt7996_update_mu_group(hw, info, mconf);
+ 
++out:
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
+@@ -942,13 +965,22 @@ mt7996_channel_switch_beacon(struct ieee80211_hw *hw,
+ {
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct mt7996_bss_conf *mconf;
+-	struct ieee80211_bss_conf *conf;
++	struct mt7996_phy *phy = mt7996_band_phy(hw, chandef->chan->band);
++	unsigned long valid_links = vif->valid_links ?: BIT(0);
++	unsigned int link_id;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+-	mconf = mconf_dereference_protected(mvif, 0);
+-	conf = link_conf_dereference_protected(vif, 0);
+-	mt7996_mcu_add_beacon(hw, conf, mconf, true);
++	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++		struct mt7996_bss_conf *mconf =
++			mconf_dereference_protected(mvif, link_id);
++		struct ieee80211_bss_conf *conf =
++			link_conf_dereference_protected(vif, link_id);
++
++		if (!mconf || phy != mconf->phy)
++			continue;
++
++		mt7996_mcu_add_beacon(hw, conf, mconf, true);
++	}
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
+@@ -1220,34 +1252,74 @@ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 	mt7996_mac_sta_remove_links(dev, vif, sta, rem);
+ }
+ 
++static void
++mt7996_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++			  struct ieee80211_sta *sta)
++{
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	unsigned long rem = sta->valid_links ?: BIT(0);
++	unsigned int link_id;
++
++	mutex_lock(&dev->mt76.mutex);
++	spin_lock_bh(&dev->mt76.status_lock);
++	for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
++		struct mt7996_link_sta *mlink =
++			mlink_dereference_protected(msta, link_id);
++
++		rcu_assign_pointer(dev->mt76.wcid[mlink->wcid.idx], NULL);
++	}
++	spin_unlock_bh(&dev->mt76.status_lock);
++	mutex_unlock(&dev->mt76.mutex);
++}
++
+ static void mt7996_tx(struct ieee80211_hw *hw,
+ 		      struct ieee80211_tx_control *control,
+ 		      struct sk_buff *skb)
+ {
+-	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+-	struct mt76_phy *mphy = hw->priv;
++	struct mt76_phy *mphy;
+ 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ 	struct ieee80211_vif *vif = info->control.vif;
+-	struct mt76_wcid *wcid = &dev->mt76.global_wcid;
+-	struct mt7996_link_sta *mlink;
++	struct mt76_wcid *wcid;
++	struct mt7996_vif *mvif;
++	struct mt7996_sta *msta;
+ 
+ 	if (control->sta) {
+-		struct mt7996_sta *msta;
+-
+ 		msta = (struct mt7996_sta *)control->sta->drv_priv;
+-		mlink = rcu_dereference(msta->link[0]);
+-		wcid = &mlink->wcid;
++		mvif = msta->vif;
++	} else if (vif) {
++		mvif = (struct mt7996_vif *)vif->drv_priv;
++		msta = &mvif->sta;
+ 	}
+ 
+-	if (vif && !control->sta) {
+-		struct mt7996_vif *mvif;
++	rcu_read_lock();
++	if (mvif && msta) {
++		struct mt7996_bss_conf *mconf;
++		struct mt7996_link_sta *mlink;
++
++		u8 link_id = u32_get_bits(info->control.flags,
++					  IEEE80211_TX_CTRL_MLO_LINK);
+ 
+-		mvif = (struct mt7996_vif *)vif->drv_priv;
+-		mlink = rcu_dereference(mvif->sta.link[0]);
++		if (link_id >= IEEE80211_LINK_UNSPECIFIED)
++			link_id = mvif->master_link_id;
++
++		mconf = rcu_dereference(mvif->link[link_id]);
++		mlink = rcu_dereference(msta->link[link_id]);
++
++		if (!mconf || !mlink)
++			goto unlock;
++
++		mphy = mconf->phy->mt76;
+ 		wcid = &mlink->wcid;
++	} else {
++		struct mt7996_dev *dev = mt7996_hw_dev(hw);
++
++		mphy = hw->priv;
++		wcid = &dev->mt76.global_wcid;
+ 	}
+ 
+ 	mt76_tx(mphy, control->sta, wcid, skb);
++	rcu_read_unlock();
+ }
+ 
+ static int mt7996_set_rts_threshold(struct ieee80211_hw *hw, u32 val)
+@@ -1283,7 +1355,7 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	mtxq = (struct mt76_txq *)txq->drv_priv;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+-	mlink = mlink_dereference_protected(msta, 0);
++	mlink = mlink_dereference_protected(msta, msta->pri_link);
+ 	switch (action) {
+ 	case IEEE80211_AMPDU_RX_START:
+ 		mt76_rx_aggr_start(&dev->mt76, &mlink->wcid, tid, ssn,
+@@ -1498,7 +1570,7 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
+ 
+ 	/* TODO: support per-link rate report */
+ 	mutex_lock(&dev->mt76.mutex);
+-	mlink = mlink_dereference_protected(msta, 0);
++	mlink = mlink_dereference_protected(msta, msta->pri_link);
+ 	if (!mlink)
+ 		goto out;
+ 
+@@ -1558,7 +1630,7 @@ static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta)
+ 	u32 *changed = data;
+ 
+ 	rcu_read_lock();
+-	mlink = rcu_dereference(msta->link[0]);
++	mlink = rcu_dereference(msta->link[msta->pri_link]);
+ 
+ 	spin_lock_bh(&dev->mt76.sta_poll_lock);
+ 	mlink->changed |= *changed;
+@@ -1625,19 +1697,26 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw,
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+-	struct mt7996_bss_conf *mconf;
+-	struct mt7996_link_sta *mlink;
++	unsigned long valid_links = sta->valid_links ?: BIT(0);
++	unsigned int link_id;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+-	mconf = mconf_dereference_protected(mvif, 0);
+-	mlink = mlink_dereference_protected(msta, 0);
++	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++		struct mt7996_bss_conf *mconf =
++			mconf_dereference_protected(mvif, link_id);
++		struct mt7996_link_sta *mlink =
++			mlink_dereference_protected(msta, link_id);
+ 
+-	if (enabled)
+-		set_bit(MT_WCID_FLAG_4ADDR, &mlink->wcid.flags);
+-	else
+-		clear_bit(MT_WCID_FLAG_4ADDR, &mlink->wcid.flags);
++		if (!mconf || !mlink)
++			continue;
++
++		if (enabled)
++			set_bit(MT_WCID_FLAG_4ADDR, &mlink->wcid.flags);
++		else
++			clear_bit(MT_WCID_FLAG_4ADDR, &mlink->wcid.flags);
+ 
+-	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
++		mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
++	}
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
+@@ -1649,19 +1728,26 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw,
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+-	struct mt7996_bss_conf *mconf;
+-	struct mt7996_link_sta *mlink;
++	unsigned long valid_links = sta->valid_links ?: BIT(0);
++	unsigned int link_id;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+-	mconf = mconf_dereference_protected(mvif, 0);
+-	mlink = mlink_dereference_protected(msta, 0);
++	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++		struct mt7996_bss_conf *mconf =
++			mconf_dereference_protected(mvif, link_id);
++		struct mt7996_link_sta *mlink =
++			mlink_dereference_protected(msta, link_id);
+ 
+-	if (enabled)
+-		set_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
+-	else
+-		clear_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
++		if (!mconf || !mlink)
++			continue;
++
++		if (enabled)
++			set_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
++		else
++			clear_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
+ 
+-	mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
++		mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
++	}
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
+@@ -1794,9 +1880,13 @@ static void mt7996_ethtool_worker(void *wi_data, struct ieee80211_sta *sta)
+ {
+ 	struct mt76_ethtool_worker_info *wi = wi_data;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+-	struct mt7996_link_sta *mlink = &msta->deflink;
++	struct mt7996_link_sta *mlink;
++	struct mt7996_bss_conf *mconf;
++
++	mlink = mlink_dereference_protected(msta, msta->pri_link);
++	mconf = mconf_dereference_protected(msta->vif, msta->pri_link);
+ 
+-	if (msta->vif->deflink.mt76.idx != wi->idx)
++	if (mconf->mt76.idx != wi->idx)
+ 		return;
+ 
+ 	mt76_ethtool_worker(wi, &mlink->wcid.stats, true);
+@@ -2028,12 +2118,13 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ 			     struct net_device_path *path)
+ {
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct mt7996_bss_conf *mconf = &mvif->deflink;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+-	struct mt7996_link_sta *mlink = &msta->deflink;
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
++	struct mt7996_bss_conf *mconf;
++	struct mt7996_link_sta *mlink;
++	u8 link_id;
+ 
+ 	if (dev->hif2) {
+ 		switch (dev->option_type) {
+@@ -2053,6 +2144,10 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ 	if (!mtk_wed_device_active(wed))
+ 		return -ENODEV;
+ 
++	link_id = msta->pri_link;
++	mconf = rcu_dereference(mvif->link[link_id]);
++	mlink = rcu_dereference(msta->link[link_id]);
++
+ 	if (mlink->wcid.idx > MT7996_WTBL_STA)
+ 		return -EIO;
+ 
+@@ -2385,7 +2480,7 @@ const struct ieee80211_ops mt7996_ops = {
+ 	.vif_cfg_changed = mt7996_vif_cfg_changed,
+ 	.link_info_changed = mt7996_link_info_changed,
+ 	.sta_state = mt76_sta_state,
+-	.sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
++	.sta_pre_rcu_remove = mt7996_sta_pre_rcu_remove,
+ 	.sta_rc_update = mt7996_sta_rc_update,
+ 	.set_key = mt7996_set_key,
+ 	.ampdu_action = mt7996_ampdu_action,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0097-wifi-mt76-connac-rework-mcu-functions-for-multi-link.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0097-wifi-mt76-connac-rework-mcu-functions-for-multi-link.patch
deleted file mode 100644
index c4caeb7..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0097-wifi-mt76-connac-rework-mcu-functions-for-multi-link.patch
+++ /dev/null
@@ -1,259 +0,0 @@
-From c83a1297e94b7b05b1acb8245347ed6443f8b50e Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 7 Dec 2023 15:39:03 +0800
-Subject: [PATCH 097/116] wifi: mt76: connac: rework mcu functions for
- multi-link support
-
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7615/mcu.c      |  4 +--
- mt76_connac_mcu.c | 11 ++++----
- mt76_connac_mcu.h |  2 +-
- mt7915/mcu.c      |  3 +-
- mt7925/mcu.c      |  3 +-
- mt7996/mcu.c      | 70 +++++++++++++++++++++++++++++++++--------------
- 6 files changed, 63 insertions(+), 30 deletions(-)
-
-diff --git a/mt7615/mcu.c b/mt7615/mcu.c
-index 8f4f203..e72dd65 100644
---- a/mt7615/mcu.c
-+++ b/mt7615/mcu.c
-@@ -862,8 +862,8 @@ mt7615_mcu_wtbl_sta_add(struct mt7615_phy *phy, struct ieee80211_vif *vif,
- 		else
- 			mvif->sta_added = true;
- 	}
--	mt76_connac_mcu_sta_basic_tlv(&dev->mt76, sskb, vif, &sta->deflink,
--				      enable, new_entry);
-+	mt76_connac_mcu_sta_basic_tlv(&dev->mt76, sskb, &vif->bss_conf,
-+				      &sta->deflink, enable, new_entry);
- 	if (enable && sta)
- 		mt76_connac_mcu_sta_tlv(phy->mt76, sskb, sta, vif, 0,
- 					MT76_STA_INFO_STATE_ASSOC);
-diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
-index d83d314..9ea7471 100644
---- a/mt76_connac_mcu.c
-+++ b/mt76_connac_mcu.c
-@@ -370,10 +370,11 @@ void mt76_connac_mcu_bss_omac_tlv(struct sk_buff *skb,
- EXPORT_SYMBOL_GPL(mt76_connac_mcu_bss_omac_tlv);
- 
- void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
--				   struct ieee80211_vif *vif,
-+				   struct ieee80211_bss_conf *conf,
- 				   struct ieee80211_link_sta *link_sta,
- 				   bool enable, bool newly)
- {
-+	struct ieee80211_vif *vif = conf->vif;
- 	struct ieee80211_sta *sta = link_sta ? link_sta->sta : NULL;
- 	struct sta_rec_basic *basic;
- 	struct tlv *tlv;
-@@ -394,10 +395,9 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
- 
- 	if (!sta) {
- 		basic->conn_type = cpu_to_le32(CONNECTION_INFRA_BC);
--
- 		if (vif->type == NL80211_IFTYPE_STATION &&
--		    !is_zero_ether_addr(vif->bss_conf.bssid)) {
--			memcpy(basic->peer_addr, vif->bss_conf.bssid, ETH_ALEN);
-+		    conf && !is_zero_ether_addr(conf->bssid)) {
-+			memcpy(basic->peer_addr, conf->bssid, ETH_ALEN);
- 			basic->aid = cpu_to_le16(vif->cfg.aid);
- 		} else {
- 			eth_broadcast_addr(basic->peer_addr);
-@@ -1056,7 +1056,8 @@ int mt76_connac_mcu_sta_cmd(struct mt76_phy *phy,
- 		return PTR_ERR(skb);
- 
- 	if (info->sta || !info->offload_fw)
--		mt76_connac_mcu_sta_basic_tlv(dev, skb, info->vif, &info->sta->deflink,
-+		mt76_connac_mcu_sta_basic_tlv(dev, skb, &info->vif->bss_conf,
-+					      &info->sta->deflink,
- 					      info->enable, info->newly);
- 	if (info->sta && info->enable)
- 		mt76_connac_mcu_sta_tlv(phy, skb, info->sta,
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 728ffb3..b93b5e0 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1911,7 +1911,7 @@ mt76_connac_mcu_add_tlv(struct sk_buff *skb, int tag, int len)
- int mt76_connac_mcu_set_channel_domain(struct mt76_phy *phy);
- int mt76_connac_mcu_set_vif_ps(struct mt76_dev *dev, struct ieee80211_vif *vif);
- void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
--				   struct ieee80211_vif *vif,
-+				   struct ieee80211_bss_conf *conf,
- 				   struct ieee80211_link_sta *link_sta,
- 				   bool enable, bool newly);
- void mt76_connac_mcu_wtbl_generic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
-diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index cb2e3b2..d2294e5 100644
---- a/mt7915/mcu.c
-+++ b/mt7915/mcu.c
-@@ -1669,7 +1669,8 @@ int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif,
- 		return PTR_ERR(skb);
- 
- 	/* starec basic */
--	mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, vif, &sta->deflink, enable,
-+	mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, &vif->bss_conf,
-+				      &sta->deflink, enable,
- 				      !rcu_access_pointer(dev->mt76.wcid[msta->wcid.idx]));
- 	if (!enable)
- 		goto out;
-diff --git a/mt7925/mcu.c b/mt7925/mcu.c
-index 80e1828..a814b2a 100644
---- a/mt7925/mcu.c
-+++ b/mt7925/mcu.c
-@@ -1626,7 +1626,8 @@ mt7925_mcu_sta_cmd(struct mt76_phy *phy,
- 		return PTR_ERR(skb);
- 
- 	if (info->sta || !info->offload_fw)
--		mt76_connac_mcu_sta_basic_tlv(dev, skb, info->vif, &info->sta->deflink,
-+		mt76_connac_mcu_sta_basic_tlv(dev, skb, &info->vif->bss_conf,
-+					      &info->sta->deflink,
- 					      info->enable, info->newly);
- 	if (info->sta && info->enable) {
- 		mt7925_mcu_sta_phy_tlv(skb, info->vif, info->sta);
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 16352d1..7f31dd9 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -1169,10 +1169,12 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
- 				sta = ieee80211_find_sta(vif, conf->bssid);
- 			/* TODO: enable BSS_INFO_UAPSD & BSS_INFO_PM */
- 			if (sta) {
--				struct mt76_wcid *wcid;
-+				struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+				struct mt7996_link_sta *mlink;
- 
--				wcid = (struct mt76_wcid *)sta->drv_priv;
--				sta_wlan_idx = wcid->idx;
-+				mlink = rcu_dereference(msta->link[conf->link_id]);
-+				if (mlink)
-+					sta_wlan_idx = mlink->wcid.idx;
- 			}
- 			rcu_read_unlock();
- 		}
-@@ -1306,9 +1308,8 @@ int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct mt7996_bss_conf *mconf)
- static int
- mt7996_mcu_sta_ba(struct mt7996_dev *dev, struct mt76_vif *mvif,
- 		  struct ieee80211_ampdu_params *params,
--		  bool enable, bool tx)
-+		  struct mt76_wcid *wcid, bool enable, bool tx)
- {
--	struct mt76_wcid *wcid = (struct mt76_wcid *)params->sta->drv_priv;
- 	struct sta_rec_ba_uni *ba;
- 	struct sk_buff *skb;
- 	struct tlv *tlv;
-@@ -1338,24 +1339,53 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
- 			 struct ieee80211_ampdu_params *params,
- 			 bool enable)
- {
--	struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv;
--	struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
--	struct mt7996_link_sta *mlink = mlink_dereference_protected(msta, 0);
-+	struct ieee80211_sta *sta = params->sta;
-+	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	unsigned long valid_links = sta->valid_links ?: BIT(0);
-+	unsigned int link_id;
-+
-+	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
-+		struct mt7996_link_sta *mlink =
-+			mlink_dereference_protected(msta, link_id);
-+		struct mt7996_bss_conf *mconf =
-+			mconf_dereference_protected(msta->vif, link_id);
-+		int ret;
- 
--	if (enable && !params->amsdu)
--		mlink->wcid.amsdu = false;
-+		if (enable && !params->amsdu)
-+			mlink->wcid.amsdu = false;
- 
--	return mt7996_mcu_sta_ba(dev, &mconf->mt76, params, enable, true);
-+		ret = mt7996_mcu_sta_ba(dev, &mconf->mt76, params,
-+					&mlink->wcid, enable, true);
-+		if (ret)
-+			return ret;
-+	}
-+
-+	return 0;
- }
- 
- int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
- 			 struct ieee80211_ampdu_params *params,
- 			 bool enable)
- {
--	struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv;
--	struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
-+	struct ieee80211_sta *sta = params->sta;
-+	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	unsigned long valid_links = sta->valid_links ?: BIT(0);
-+	unsigned int link_id;
- 
--	return mt7996_mcu_sta_ba(dev, &mconf->mt76, params, enable, false);
-+	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
-+		struct mt7996_link_sta *mlink =
-+			mlink_dereference_protected(msta, link_id);
-+		struct mt7996_bss_conf *mconf =
-+			mconf_dereference_protected(msta->vif, link_id);
-+		int ret;
-+
-+		ret = mt7996_mcu_sta_ba(dev, &mconf->mt76, params, &mlink->wcid,
-+					enable, false);
-+		if (ret)
-+			return ret;
-+	}
-+
-+	return 0;
- }
- 
- static void
-@@ -2379,7 +2409,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
- 		return PTR_ERR(skb);
- 
- 	/* starec basic */
--	mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, vif, link_sta, enable, newly);
-+	mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, conf, link_sta, enable, newly);
- 
- 	if (!enable)
- 		goto out;
-@@ -2840,7 +2870,7 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
- 			  struct mt7996_bss_conf *mconf, int en)
- {
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
--	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct mt7996_phy *phy = mconf->phy;
- 	struct ieee80211_mutable_offsets offs;
- 	struct ieee80211_tx_info *info;
- 	struct sk_buff *skb, *rskb;
-@@ -2856,7 +2886,7 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
- 	if (IS_ERR(rskb))
- 		return PTR_ERR(rskb);
- 
--	skb = ieee80211_beacon_get_template(hw, conf->vif, &offs, 0);
-+	skb = ieee80211_beacon_get_template(hw, conf->vif, &offs, conf->link_id);
- 	if (!skb) {
- 		dev_kfree_skb(rskb);
- 		return -EINVAL;
-@@ -2894,9 +2924,9 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
- {
- #define OFFLOAD_TX_MODE_SU	BIT(0)
- #define OFFLOAD_TX_MODE_MU	BIT(1)
--	struct ieee80211_hw *hw = mt76_hw(dev);
- 	struct ieee80211_vif *vif = conf->vif;
--	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_phy *phy = mconf->phy;
- 	struct cfg80211_chan_def *chandef = &mconf->phy->mt76->chandef;
- 	enum nl80211_band band = chandef->chan->band;
- 	struct mt76_wcid *wcid = &dev->mt76.global_wcid;
-@@ -5154,7 +5184,7 @@ int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
- 				rssi[3] = to_rssi(MT_PRXV_RCPI0, rcpi[3]);
- 
- 				mlink = container_of(wcid, struct mt7996_link_sta, wcid);
--				phy = mlink->sta->vif->deflink.phy->mt76;
-+				phy = dev->phys[wcid->phy_idx];
- 				mlink->ack_signal = mt76_rx_signal(phy->antenna_mask, rssi);
- 				ewma_avg_signal_add(&mlink->avg_ack_signal, -mlink->ack_signal);
- 			} else {
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0098-mtk-mt76-mt7996-rework-TXD-for-multi-link-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0098-mtk-mt76-mt7996-rework-TXD-for-multi-link-support.patch
new file mode 100644
index 0000000..120bcda
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0098-mtk-mt76-mt7996-rework-TXD-for-multi-link-support.patch
@@ -0,0 +1,203 @@
+From 993bb57ca7933d2de1e8b77693e5d006e6968c4c Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 4 Dec 2023 11:25:54 +0800
+Subject: [PATCH 098/199] mtk: mt76: mt7996: rework TXD for multi-link support
+
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mac.c    | 88 +++++++++++++++++++++++++++++++++++--------------
+ mt7996/mt7996.h |  9 +++++
+ 2 files changed, 73 insertions(+), 24 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index fbe6c0bd..b0ad48dd 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -615,9 +615,8 @@ mt7996_mac_write_txwi_8023(struct mt7996_dev *dev, __le32 *txwi,
+ 	u32 val;
+ 
+ 	if (wcid->sta) {
+-		struct ieee80211_sta *sta;
++		struct ieee80211_sta *sta = wcid_to_sta(wcid);
+ 
+-		sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv);
+ 		wmm = sta->wme;
+ 	}
+ 
+@@ -727,6 +726,10 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
+ 		txwi[3] |= cpu_to_le32(val);
+ 		txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU);
+ 	}
++
++	if (ieee80211_vif_is_mld(info->control.vif) &&
++	    (multicast || unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE))))
++		txwi[5] |= cpu_to_le32(MT_TXD5_FL);
+ }
+ 
+ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+@@ -736,10 +739,12 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ {
+ 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ 	struct ieee80211_vif *vif = info->control.vif;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_bss_conf *mconf;
+ 	u8 band_idx = (info->hw_queue & MT_TX_HW_QUEUE_PHY) >> 2;
+ 	u8 p_fmt, q_idx, omac_idx = 0, wmm_idx = 0;
++	u8 link_id;
+ 	bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
+-	struct mt76_vif *mvif;
+ 	u16 tx_count = 15;
+ 	u32 val;
+ 	bool inband_disc = !!(changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP |
+@@ -747,11 +752,16 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ 	bool beacon = !!(changed & (BSS_CHANGED_BEACON |
+ 				    BSS_CHANGED_BEACON_ENABLED)) && (!inband_disc);
+ 
+-	mvif = vif ? (struct mt76_vif *)vif->drv_priv : NULL;
+-	if (mvif) {
+-		omac_idx = mvif->omac_idx;
+-		wmm_idx = mvif->wmm_idx;
+-		band_idx = mvif->band_idx;
++	if (likely(wcid != &dev->mt76.global_wcid))
++		link_id = wcid->link_id;
++	else
++		link_id = u32_get_bits(info->control.flags, IEEE80211_TX_CTRL_MLO_LINK);
++
++	mconf = rcu_dereference(mvif->link[link_id]);
++	if (mconf) {
++		omac_idx = mconf->mt76.omac_idx;
++		wmm_idx = mconf->mt76.wmm_idx;
++		band_idx = mconf->mt76.band_idx;
+ 	}
+ 
+ 	if (inband_disc) {
+@@ -798,7 +808,10 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ 		val |= MT_TXD5_TX_STATUS_HOST;
+ 	txwi[5] = cpu_to_le32(val);
+ 
+-	val = MT_TXD6_DIS_MAT | MT_TXD6_DAS;
++	val = MT_TXD6_DAS;
++	if ((q_idx >= MT_LMAC_ALTX0 && q_idx <= MT_LMAC_BCN0))
++		val |= MT_TXD6_DIS_MAT;
++
+ 	if (is_mt7996(&dev->mt76))
+ 		val |= FIELD_PREP(MT_TXD6_MSDU_CNT, 1);
+ 	else
+@@ -817,16 +830,18 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ 			     is_multicast_ether_addr(hdr->addr1);
+ 		u8 idx = MT7996_BASIC_RATES_TBL;
+ 
+-		if (mvif) {
+-			if (mcast && mvif->mcast_rates_idx)
+-				idx = mvif->mcast_rates_idx;
+-			else if (beacon && mvif->beacon_rates_idx)
+-				idx = mvif->beacon_rates_idx;
++		if (mconf) {
++			if (mcast && mconf->mt76.mcast_rates_idx)
++				idx = mconf->mt76.mcast_rates_idx;
++			else if (beacon && mconf->mt76.beacon_rates_idx)
++				idx = mconf->mt76.beacon_rates_idx;
+ 			else
+-				idx = mvif->basic_rates_idx;
++				idx = mconf->mt76.basic_rates_idx;
+ 		}
+ 
+ 		val = FIELD_PREP(MT_TXD6_TX_RATE, idx) | MT_TXD6_FIXED_BW;
++		if (mcast)
++			val |= MT_TXD6_DIS_MAT;
+ 		txwi[6] |= cpu_to_le32(val);
+ 		txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE);
+ 	}
+@@ -842,17 +857,48 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb);
+ 	struct ieee80211_key_conf *key = info->control.hw_key;
+ 	struct ieee80211_vif *vif = info->control.vif;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_sta *msta;
++	struct mt7996_bss_conf *mconf;
+ 	struct mt76_connac_txp_common *txp;
+ 	struct mt76_txwi_cache *t;
+ 	int id, i, pid, nbuf = tx_info->nbuf - 1;
+ 	bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
+ 	u8 *txwi = (u8 *)txwi_ptr;
++	u8 link_id;
+ 
+ 	if (unlikely(tx_info->skb->len <= ETH_HLEN))
+ 		return -EINVAL;
+ 
+-	if (!wcid)
+-		wcid = &dev->mt76.global_wcid;
++	if (WARN_ON(!wcid))
++		return -EINVAL;
++
++	msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta;
++	if (ieee80211_is_data_qos(hdr->frame_control) && sta->mlo) {
++		if (unlikely(tx_info->skb->protocol == cpu_to_be16(ETH_P_PAE))) {
++			link_id = msta->pri_link;
++		} else {
++			u8 tid = tx_info->skb->priority & IEEE80211_QOS_CTL_TID_MASK;
++
++			link_id = (tid % 2) ? msta->sec_link : msta->pri_link;
++		}
++	} else {
++		link_id = u32_get_bits(info->control.flags, IEEE80211_TX_CTRL_MLO_LINK);
++
++		if (link_id == IEEE80211_LINK_UNSPECIFIED || (sta && !sta->mlo))
++			link_id = wcid->link_id;
++	}
++
++	if (link_id != wcid->link_id) {
++		struct mt7996_link_sta *mlink = rcu_dereference(msta->link[link_id]);
++
++		if (mlink)
++			wcid = &mlink->wcid;
++	}
++
++	mconf = rcu_dereference(mvif->link[wcid->link_id]);
++	if (!mconf)
++		return -ENOLINK;
+ 
+ 	t = (struct mt76_txwi_cache *)(txwi + mdev->drv->txwi_size);
+ 	t->skb = tx_info->skb;
+@@ -897,13 +943,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 	if (!is_8023 && ieee80211_is_mgmt(hdr->frame_control))
+ 		txp->fw.flags |= cpu_to_le16(MT_CT_INFO_MGMT_FRAME);
+ 
+-	if (vif) {
+-		struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-		struct mt7996_bss_conf *mconf = &mvif->deflink;
+-
+-		txp->fw.bss_idx = mconf->mt76.idx;
+-	}
+-
++	txp->fw.bss_idx = mconf->mt76.idx;
+ 	txp->fw.token = cpu_to_le16(id);
+ 	txp->fw.rept_wds_wcid = cpu_to_le16(sta ? wcid->idx : 0xfff);
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index d5b61a2e..72988c9e 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -831,6 +831,15 @@ mlink_dereference_protected(struct mt7996_sta *msta, u8 link_id)
+ 					 lockdep_is_held(&msta->vif->dev->mt76.mutex));
+ }
+ 
++static inline struct mt7996_link_sta *
++wcid_to_mlink(struct mt76_wcid *wcid)
++{
++	if (!wcid)
++		return NULL;
++
++	return container_of(wcid, struct mt7996_link_sta, wcid);
++}
++
+ extern const struct ieee80211_ops mt7996_ops;
+ extern struct pci_driver mt7996_pci_driver;
+ extern struct pci_driver mt7996_hif_driver;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0098-wifi-mt76-connac-rework-connac-helpers.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0098-wifi-mt76-connac-rework-connac-helpers.patch
deleted file mode 100644
index 02d240f..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0098-wifi-mt76-connac-rework-connac-helpers.patch
+++ /dev/null
@@ -1,174 +0,0 @@
-From 056ff64bf4b942ff77e1b5931cdabd236f9d0f83 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Mon, 11 Dec 2023 18:45:00 +0800
-Subject: [PATCH 098/116] wifi: mt76: connac: rework connac helpers
-
-Rework connac helpers related to rate and phymode.
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt76_connac.h     |  2 +-
- mt76_connac_mac.c | 17 ++++++++---------
- mt76_connac_mcu.c |  6 +++---
- mt76_connac_mcu.h |  2 +-
- mt7925/main.c     |  2 +-
- mt7996/main.c     |  2 +-
- mt7996/mcu.c      |  2 +-
- 7 files changed, 16 insertions(+), 17 deletions(-)
-
-diff --git a/mt76_connac.h b/mt76_connac.h
-index 445d0f0..f7766a9 100644
---- a/mt76_connac.h
-+++ b/mt76_connac.h
-@@ -427,7 +427,7 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
- 				 struct ieee80211_key_conf *key, int pid,
- 				 enum mt76_txq_id qid, u32 changed);
- u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy,
--				 struct ieee80211_vif *vif,
-+				 struct ieee80211_bss_conf *conf,
- 				 bool beacon, bool mcast);
- bool mt76_connac2_mac_fill_txs(struct mt76_dev *dev, struct mt76_wcid *wcid,
- 			       __le32 *txs_data);
-diff --git a/mt76_connac_mac.c b/mt76_connac_mac.c
-index b841bf6..c1e9ba0 100644
---- a/mt76_connac_mac.c
-+++ b/mt76_connac_mac.c
-@@ -291,12 +291,11 @@ EXPORT_SYMBOL_GPL(mt76_connac_init_tx_queues);
- })
- 
- u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy,
--				 struct ieee80211_vif *vif,
-+				 struct ieee80211_bss_conf *conf,
- 				 bool beacon, bool mcast)
- {
--	struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
--	struct cfg80211_chan_def *chandef = mvif->ctx ?
--					    &mvif->ctx->def : &mphy->chandef;
-+	struct ieee80211_vif *vif = conf->vif;
-+	struct cfg80211_chan_def *chandef = &mphy->chandef;
- 	u8 nss = 0, mode = 0, band = chandef->chan->band;
- 	int rateidx = 0, mcast_rate;
- 
-@@ -304,14 +303,14 @@ u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy,
- 		goto legacy;
- 
- 	if (is_mt7921(mphy->dev)) {
--		rateidx = ffs(vif->bss_conf.basic_rates) - 1;
-+		rateidx = ffs(conf->basic_rates) - 1;
- 		goto legacy;
- 	}
- 
- 	if (beacon) {
- 		struct cfg80211_bitrate_mask *mask;
- 
--		mask = &vif->bss_conf.beacon_tx_rate;
-+		mask = &conf->beacon_tx_rate;
- 
- 		__bitrate_mask_check(he_mcs, HE_SU);
- 		__bitrate_mask_check(vht_mcs, VHT);
-@@ -323,11 +322,11 @@ u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy,
- 		}
- 	}
- 
--	mcast_rate = vif->bss_conf.mcast_rate[band];
-+	mcast_rate = conf->mcast_rate[band];
- 	if (mcast && mcast_rate > 0)
- 		rateidx = mcast_rate - 1;
- 	else
--		rateidx = ffs(vif->bss_conf.basic_rates) - 1;
-+		rateidx = ffs(conf->basic_rates) - 1;
- 
- legacy:
- 	rateidx = mt76_calculate_default_rate(mphy, vif, rateidx);
-@@ -561,7 +560,7 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
- 		struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- 		bool multicast = ieee80211_is_data(hdr->frame_control) &&
- 				 is_multicast_ether_addr(hdr->addr1);
--		u16 rate = mt76_connac2_mac_tx_rate_val(mphy, vif, beacon,
-+		u16 rate = mt76_connac2_mac_tx_rate_val(mphy, &vif->bss_conf, beacon,
- 							multicast);
- 		u32 val = MT_TXD6_FIXED_BW;
- 
-diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
-index 9ea7471..71f3d30 100644
---- a/mt76_connac_mcu.c
-+++ b/mt76_connac_mcu.c
-@@ -1361,7 +1361,7 @@ u8 mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif,
- }
- EXPORT_SYMBOL_GPL(mt76_connac_get_phy_mode);
- 
--u8 mt76_connac_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_vif *vif,
-+u8 mt76_connac_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_bss_conf *conf,
- 				enum nl80211_band band)
- {
- 	const struct ieee80211_sta_eht_cap *eht_cap;
-@@ -1372,9 +1372,9 @@ u8 mt76_connac_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_vif *vif,
- 		mode |= PHY_MODE_AX_6G;
- 
- 	sband = phy->hw->wiphy->bands[band];
--	eht_cap = ieee80211_get_eht_iftype_cap(sband, vif->type);
-+	eht_cap = ieee80211_get_eht_iftype_cap(sband, conf->vif->type);
- 
--	if (!eht_cap || !eht_cap->has_eht || !vif->bss_conf.eht_support)
-+	if (!eht_cap || !eht_cap->has_eht || !conf->eht_support)
- 		return mode;
- 
- 	switch (band) {
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index b93b5e0..4d0fd4b 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -2023,7 +2023,7 @@ mt76_connac_get_eht_phy_cap(struct mt76_phy *phy, struct ieee80211_vif *vif);
- u8 mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif,
- 			    enum nl80211_band band,
- 			    struct ieee80211_link_sta *link_sta);
--u8 mt76_connac_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_vif *vif,
-+u8 mt76_connac_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_bss_conf *conf,
- 				enum nl80211_band band);
- 
- int mt76_connac_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
-diff --git a/mt7925/main.c b/mt7925/main.c
-index 1f07ec5..6d7ec77 100644
---- a/mt7925/main.c
-+++ b/mt7925/main.c
-@@ -675,7 +675,7 @@ mt7925_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	u16 rate;
- 	u8 i, idx, ht;
- 
--	rate = mt76_connac2_mac_tx_rate_val(mphy, vif, beacon, mcast);
-+	rate = mt76_connac2_mac_tx_rate_val(mphy, &vif->bss_conf, beacon, mcast);
- 	ht = FIELD_GET(MT_TX_RATE_MODE, rate) > MT_PHY_TYPE_OFDM;
- 
- 	if (beacon && ht) {
-diff --git a/mt7996/main.c b/mt7996/main.c
-index a94955c..0245b6b 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -804,7 +804,7 @@ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
- 	u16 rate;
- 	u8 i, idx;
- 
--	rate = mt76_connac2_mac_tx_rate_val(mphy, conf->vif, beacon, mcast);
-+	rate = mt76_connac2_mac_tx_rate_val(mphy, conf, beacon, mcast);
- 
- 	if (beacon) {
- 		struct mt7996_phy *phy = mphy->priv;
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 7f31dd9..f3d162a 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -1219,7 +1219,7 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
- 	bss->dtim_period = conf->dtim_period;
- 	bss->phymode = mt76_connac_get_phy_mode(phy, vif,
- 						chandef->chan->band, NULL);
--	bss->phymode_ext = mt76_connac_get_phy_mode_ext(phy, vif,
-+	bss->phymode_ext = mt76_connac_get_phy_mode_ext(phy, conf,
- 							chandef->chan->band);
- 
- 	return 0;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0099-mtk-mt76-mt7996-rework-TXS-for-multi-link-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0099-mtk-mt76-mt7996-rework-TXS-for-multi-link-support.patch
new file mode 100644
index 0000000..6ee0a2b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0099-mtk-mt76-mt7996-rework-TXS-for-multi-link-support.patch
@@ -0,0 +1,112 @@
+From 907d640dec0609331c193539094c3620ae848543 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 4 Dec 2023 11:57:38 +0800
+Subject: [PATCH 099/199] mtk: mt76: mt7996: rework TXS for multi-link support
+
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+The wcidx and band idx in the TXS are sometimes mismatched
+since FW will select a link to TX based on its algorithm.
+That is, the wcidx in the TXS would be the one
+registered by the driver rather than the actual TXed wcidx.
+However, the band idx in the TXS is the actual TXed band.
+Therefore, we should get the driver-registered wcid in order
+to notify the driver that the packet has been acked; otherwise,
+the driver will be unable to match the TXed packet and its TXS.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mac.c    |  4 ++--
+ mt7996/main.c   |  1 +
+ mt7996/mt7996.h | 31 +++++++++++++++++++++++++++++++
+ 3 files changed, 34 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index b0ad48dd..c910f353 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1195,7 +1195,7 @@ mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid,
+ 		struct ieee80211_sta *sta;
+ 		u8 tid;
+ 
+-		sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv);
++		sta = wcid_to_sta(wcid);
+ 		tid = FIELD_GET(MT_TXS0_TID, txs);
+ 		ieee80211_refresh_tx_agg_session_timer(sta, tid);
+ 	}
+@@ -1335,7 +1335,7 @@ static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
+ 	if (!wcid->sta)
+ 		goto out;
+ 
+-	mlink = container_of(wcid, struct mt7996_link_sta, wcid);
++	mlink = wcid_to_mlink(wcid);
+ 	spin_lock_bh(&dev->mt76.sta_poll_lock);
+ 	if (list_empty(&mlink->wcid.poll_list))
+ 		list_add_tail(&mlink->wcid.poll_list, &dev->mt76.sta_poll_list);
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 6148c834..65e244da 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -2321,6 +2321,7 @@ mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	mconf = mconf_dereference_protected(mvif, link_id);
+ 	mconf->chanctx = ctx;
+ 	ctx->nbss_assigned++;
++	mvif->band_to_link[phy->mt76->band_idx] = link_id;
+ 
+ 	if (mt7996_hw_phy(hw) == phy)
+ 		mvif->master_link_id = link_id;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 72988c9e..b7e623cb 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -358,6 +358,8 @@ struct mt7996_vif {
+ 	u8 master_link_id;
+ 	u8 group_mld_id;
+ 	u8 mld_remap_id;
++
++	u8 band_to_link[__MT_MAX_BAND];
+ };
+ 
+ /* crash-dump */
+@@ -840,6 +842,35 @@ wcid_to_mlink(struct mt76_wcid *wcid)
+ 	return container_of(wcid, struct mt7996_link_sta, wcid);
+ }
+ 
++static inline struct mt76_wcid *
++mt7996_get_link_wcid(struct mt7996_dev *dev, u16 idx, u8 band_idx)
++{
++	struct mt7996_link_sta *mlink;
++	struct mt76_wcid *wcid;
++	u8 link_id;
++
++	if (!idx || idx >= ARRAY_SIZE(dev->mt76.wcid))
++		return NULL;
++
++	if (!mt7996_band_valid(dev, band_idx))
++		return NULL;
++
++	wcid = rcu_dereference(dev->mt76.wcid[idx]);
++	if (!wcid)
++		return NULL;
++
++	if (wcid->phy_idx == band_idx)
++		return wcid;
++
++	mlink = wcid_to_mlink(wcid);
++	link_id = mlink->sta->vif->band_to_link[band_idx];
++	mlink = rcu_dereference(mlink->sta->link[link_id]);
++	if (!mlink)
++		return wcid;
++
++	return &mlink->wcid;
++}
++
+ extern const struct ieee80211_ops mt7996_ops;
+ extern struct pci_driver mt7996_pci_driver;
+ extern struct pci_driver mt7996_hif_driver;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0099-wifi-mt76-mt7996-handle-mapping-for-hw-and-phy.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0099-wifi-mt76-mt7996-handle-mapping-for-hw-and-phy.patch
deleted file mode 100644
index e1391b5..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0099-wifi-mt76-mt7996-handle-mapping-for-hw-and-phy.patch
+++ /dev/null
@@ -1,218 +0,0 @@
-From b359a84d5cb94c2856285a0d82422a6b232f7f70 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Tue, 5 Dec 2023 13:56:51 +0800
-Subject: [PATCH 099/116] wifi: mt76: mt7996: handle mapping for hw and phy
-
-We've used mt7996_band_phy() to do mapping from ieee80211_hw to mt7996_phy,
-and this patch is a temporal workaround for opposite direction.
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mac80211.c    | 11 ++++++++++-
- mt76.h        | 10 ++++++++++
- mt7996/mac.c  |  7 +++++--
- mt7996/main.c | 41 ++++++++++++++++++++++++++++++-----------
- 4 files changed, 55 insertions(+), 14 deletions(-)
-
-diff --git a/mac80211.c b/mac80211.c
-index 4e96b39..de2c00d 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -824,9 +824,13 @@ EXPORT_SYMBOL_GPL(mt76_has_tx_pending);
- struct mt76_channel_state *
- mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c)
- {
-+	struct mt76_phy *ori_phy = phy;
- 	struct mt76_sband *msband;
- 	int idx;
- 
-+	if (phy->main_phy)
-+		phy = phy->main_phy;
-+begin:
- 	if (c->band == NL80211_BAND_2GHZ)
- 		msband = &phy->sband_2g;
- 	else if (c->band == NL80211_BAND_6GHZ)
-@@ -835,6 +839,11 @@ mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c)
- 		msband = &phy->sband_5g;
- 
- 	idx = c - &msband->sband.channels[0];
-+	/* TODO: mlo: this is a temp solution, need to come up with a more clever one */
-+	if (idx < 0 || idx >= msband->sband.n_channels) {
-+		phy = ori_phy;
-+		goto begin;
-+	}
- 	return &msband->chan[idx];
- }
- EXPORT_SYMBOL_GPL(mt76_channel_state);
-@@ -1073,7 +1082,7 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
- 	}
- 
- 	*sta = wcid_to_sta(mstat.wcid);
--	*hw = mt76_phy_hw(dev, mstat.phy_idx);
-+	*hw = mt76_main_hw(dev->phys[mstat.phy_idx]);
- 
- 	if ((mstat.flag & RX_FLAG_8023) || ieee80211_is_data_qos(hdr->frame_control)) {
- 		struct mt76_phy *phy = mt76_dev_phy(dev, mstat.phy_idx);
-diff --git a/mt76.h b/mt76.h
-index c3ab96a..ebfd379 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -831,6 +831,7 @@ struct mt76_vif {
- struct mt76_phy {
- 	struct ieee80211_hw *hw;
- 	struct mt76_dev *dev;
-+	struct mt76_phy *main_phy;
- 	void *priv;
- 
- 	unsigned long state;
-@@ -1322,6 +1323,15 @@ mt76_phy_hw(struct mt76_dev *dev, u8 phy_idx)
- 	return mt76_dev_phy(dev, phy_idx)->hw;
- }
- 
-+static inline struct ieee80211_hw *
-+mt76_main_hw(struct mt76_phy *phy)
-+{
-+	if (phy->main_phy)
-+		return mt76_dev_phy(phy->dev, phy->main_phy->band_idx)->hw;
-+
-+	return mt76_dev_phy(phy->dev, phy->band_idx)->hw;
-+}
-+
- static inline u8 *
- mt76_get_txwi_ptr(struct mt76_dev *dev, struct mt76_txwi_cache *t)
- {
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 31b9166..cd25ea6 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -2383,7 +2383,10 @@ void mt7996_mac_work(struct work_struct *work)
- 
- 	mt76_tx_status_check(mdev, false);
- 
--	ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
-+	if (mphy->main_phy && !test_bit(MT76_STATE_RUNNING, &mphy->main_phy->state))
-+		return;
-+
-+	ieee80211_queue_delayed_work(mt76_main_hw(mphy), &mphy->mac_work,
- 				     MT7996_WATCHDOG_TIME);
- }
- 
-@@ -2802,7 +2805,7 @@ mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
- 	skb_set_queue_mapping(skb, IEEE80211_AC_VO);
- 
- 	rcu_read_lock();
--	if (!ieee80211_tx_prepare_skb(hw, vif, skb,
-+	if (!ieee80211_tx_prepare_skb(mt76_main_hw(phy->mt76), vif, skb,
- 				      phy->scan_chan->band,
- 				      NULL)) {
- 		rcu_read_unlock();
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 0245b6b..34ff96d 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -173,6 +173,7 @@ static void mt7996_stop(struct ieee80211_hw *hw)
- 		mutex_lock(&dev->mt76.mutex);
- 		mt7996_mcu_set_radio_en(phy, false);
- 		clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
-+		phy->mt76->main_phy = NULL;
- 		mutex_unlock(&dev->mt76.mutex);
- 	}
- }
-@@ -541,9 +542,10 @@ out:
- 	clear_bit(MT76_RESET, &phy->mt76->state);
- 	mutex_unlock(&dev->mt76.mutex);
- 
--	mt76_txq_schedule_all(phy->mt76);
-+	if (phy->mt76 == phy->mt76->main_phy)
-+		mt76_txq_schedule_all(phy->mt76);
- 
--	ieee80211_queue_delayed_work(phy->mt76->hw,
-+	ieee80211_queue_delayed_work(mt76_main_hw(phy->mt76),
- 				     &phy->mt76->mac_work,
- 				     MT7996_WATCHDOG_TIME);
- 
-@@ -554,11 +556,11 @@ int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef
- {
- 	int ret;
- 
--	ieee80211_stop_queues(phy->mt76->hw);
-+	ieee80211_stop_queues(mt76_main_hw(phy->mt76));
- 	ret = __mt7996_set_channel(phy, chandef);
- 	if (ret)
- 		return ret;
--	ieee80211_wake_queues(phy->mt76->hw);
-+	ieee80211_wake_queues(mt76_main_hw(phy->mt76));
- 
- 	return 0;
- }
-@@ -731,6 +733,7 @@ static void mt7996_configure_filter(struct ieee80211_hw *hw,
- 			MT_WF_RFCR1_DROP_CFEND |
- 			MT_WF_RFCR1_DROP_CFACK;
- 	u32 flags = 0;
-+	u8 band;
- 
- #define MT76_FILTER(_flag, _hw) do {					\
- 		flags |= *total_flags & FIF_##_flag;			\
-@@ -764,12 +767,26 @@ static void mt7996_configure_filter(struct ieee80211_hw *hw,
- 			     MT_WF_RFCR_DROP_NDPA);
- 
- 	*total_flags = flags;
--	mt76_wr(dev, MT_WF_RFCR(phy->mt76->band_idx), phy->rxfilter);
- 
--	if (*total_flags & FIF_CONTROL)
--		mt76_clear(dev, MT_WF_RFCR1(phy->mt76->band_idx), ctl_flags);
--	else
--		mt76_set(dev, MT_WF_RFCR1(phy->mt76->band_idx), ctl_flags);
-+	/* configure rx filter to all affliated phy */
-+	for (band = 0; band < __MT_MAX_BAND; band++) {
-+		struct mt7996_phy *tmp;
-+
-+		if (!hw->wiphy->bands[band])
-+			continue;
-+
-+		tmp = dev->mt76.phys[band]->priv;
-+		if (tmp->mt76->main_phy != phy->mt76)
-+			continue;
-+
-+		tmp->rxfilter = phy->rxfilter;
-+		mt76_wr(dev, MT_WF_RFCR(tmp->mt76->band_idx), phy->rxfilter);
-+
-+		if (*total_flags & FIF_CONTROL)
-+			mt76_clear(dev, MT_WF_RFCR1(tmp->mt76->band_idx), ctl_flags);
-+		else
-+			mt76_set(dev, MT_WF_RFCR1(tmp->mt76->band_idx), ctl_flags);
-+	}
- 
- 	mutex_unlock(&dev->mt76.mutex);
- }
-@@ -2186,7 +2203,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	phy->scan_chan_idx = 0;
- 	mutex_unlock(&phy->dev->mt76.mutex);
- 
--	ieee80211_queue_delayed_work(hw, &phy->scan_work, 0);
-+	ieee80211_queue_delayed_work(mt76_main_hw(phy->mt76), &phy->scan_work, 0);
- 
- 	return 0;
- }
-@@ -2203,7 +2220,8 @@ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
- 			continue;
- 
- 		phy = mt7996_band_phy(hw, band);
--		if (!(test_bit(MT76_SCANNING, &phy->mt76->state)))
-+		if (!(test_bit(MT76_SCANNING, &phy->mt76->state) &&
-+		      phy->mt76->main_phy == hw->priv))
- 			continue;
- 
- 		cancel_delayed_work_sync(&phy->scan_work);
-@@ -2224,6 +2242,7 @@ mt7996_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
- 	wiphy_info(hw->wiphy, "%s: add %u\n", __func__, conf->def.chan->hw_value);
- 	mutex_lock(&phy->dev->mt76.mutex);
- 
-+	phy->mt76->main_phy = hw->priv;
- 	if (ctx->assigned) {
- 		mutex_unlock(&phy->dev->mt76.mutex);
- 		return -ENOSPC;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0100-mtk-mt76-mt7996-rework-RXD-for-multi-link-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0100-mtk-mt76-mt7996-rework-RXD-for-multi-link-support.patch
new file mode 100644
index 0000000..2581dc2
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0100-mtk-mt76-mt7996-rework-RXD-for-multi-link-support.patch
@@ -0,0 +1,64 @@
+From ec6837fa5ce9c356989cd027ff518d013beaa665 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 4 Dec 2023 14:50:47 +0800
+Subject: [PATCH 100/199] mtk: mt76: mt7996: rework RXD for multi-link support
+
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mac.c | 27 ++-------------------------
+ 1 file changed, 2 insertions(+), 25 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index c910f353..8d6a153c 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -51,29 +51,6 @@ static const struct mt7996_dfs_radar_spec jp_radar_specs = {
+ 	},
+ };
+ 
+-static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev,
+-					    u16 idx, bool unicast)
+-{
+-	struct mt7996_link_sta *mlink;
+-	struct mt76_wcid *wcid;
+-
+-	if (idx >= ARRAY_SIZE(dev->mt76.wcid))
+-		return NULL;
+-
+-	wcid = rcu_dereference(dev->mt76.wcid[idx]);
+-	if (unicast || !wcid)
+-		return wcid;
+-
+-	if (!wcid->sta)
+-		return NULL;
+-
+-	mlink = container_of(wcid, struct mt7996_link_sta, wcid);
+-	if (!mlink->sta->vif)
+-		return NULL;
+-
+-	return &mlink->wcid;
+-}
+-
+ bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask)
+ {
+ 	mt76_rmw(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX,
+@@ -374,10 +351,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ 
+ 	unicast = FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) == MT_RXD3_NORMAL_U2M;
+ 	idx = FIELD_GET(MT_RXD1_NORMAL_WLAN_IDX, rxd1);
+-	status->wcid = mt7996_rx_get_wcid(dev, idx, unicast);
++	status->wcid = mt7996_get_link_wcid(dev, idx, band_idx);
+ 
+ 	if (status->wcid) {
+-		mlink = container_of(status->wcid, struct mt7996_link_sta, wcid);
++		mlink = wcid_to_mlink(status->wcid);
+ 		spin_lock_bh(&dev->mt76.sta_poll_lock);
+ 		if (list_empty(&mlink->wcid.poll_list))
+ 			list_add_tail(&mlink->wcid.poll_list,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0100-wifi-mt76-mt7996-handle-mapping-for-hw-and-vif.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0100-wifi-mt76-mt7996-handle-mapping-for-hw-and-vif.patch
deleted file mode 100644
index 6fa4af2..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0100-wifi-mt76-mt7996-handle-mapping-for-hw-and-vif.patch
+++ /dev/null
@@ -1,222 +0,0 @@
-From 556174db2cbb957fdc977ecfcef3b20a3dcd670b Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Fri, 8 Dec 2023 18:08:13 +0800
-Subject: [PATCH 100/116] wifi: mt76: mt7996: handle mapping for hw and vif
-
-We have several temporal workarounds for ieee80211_hw and mt76_phy
-mappings. For legacy MBSS cases, we also need a method to do the
-hw and vif mappings.
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- dma.c           |  2 +-
- mac80211.c      |  2 +-
- mt76.h          | 14 ++++++++++++--
- mt7996/mac.c    | 24 +++++++++++++++++++++++-
- mt7996/main.c   |  1 +
- mt7996/mcu.c    |  4 ++--
- mt7996/mmio.c   |  1 +
- mt7996/mt7996.h |  3 +++
- tx.c            |  4 ++--
- 9 files changed, 46 insertions(+), 9 deletions(-)
-
-diff --git a/dma.c b/dma.c
-index 38701c7..3f1fb6c 100644
---- a/dma.c
-+++ b/dma.c
-@@ -685,7 +685,7 @@ free:
- 
- free_skb:
- 	status.skb = tx_info.skb;
--	hw = mt76_tx_status_get_hw(dev, tx_info.skb);
-+	hw = mt76_tx_status_get_hw(dev, tx_info.skb, wcid);
- 	spin_lock_bh(&dev->rx_lock);
- 	ieee80211_tx_status_ext(hw, &status);
- 	spin_unlock_bh(&dev->rx_lock);
-diff --git a/mac80211.c b/mac80211.c
-index de2c00d..1dec143 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -1520,7 +1520,7 @@ void mt76_wcid_cleanup(struct mt76_dev *dev, struct mt76_wcid *wcid)
- 	spin_unlock_bh(&phy->tx_lock);
- 
- 	while ((skb = __skb_dequeue(&list)) != NULL) {
--		hw = mt76_tx_status_get_hw(dev, skb);
-+		hw = mt76_tx_status_get_hw(dev, skb, wcid);
- 		ieee80211_free_txskb(hw, skb);
- 	}
- }
-diff --git a/mt76.h b/mt76.h
-index ebfd379..1d57e21 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -556,6 +556,9 @@ struct mt76_driver_ops {
- 
- 	void (*sta_remove)(struct mt76_dev *dev, struct ieee80211_vif *vif,
- 			   struct ieee80211_sta *sta);
-+
-+	void (*get_hw)(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 phy_idx,
-+		       struct ieee80211_hw **hw);
- };
- 
- struct mt76_channel_state {
-@@ -1601,14 +1604,21 @@ static inline void mt76_testmode_reset(struct mt76_phy *phy, bool disable)
- 
- /* internal */
- static inline struct ieee80211_hw *
--mt76_tx_status_get_hw(struct mt76_dev *dev, struct sk_buff *skb)
-+mt76_tx_status_get_hw(struct mt76_dev *dev, struct sk_buff *skb,
-+		      struct mt76_wcid *wcid)
- {
- 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- 	u8 phy_idx = (info->hw_queue & MT_TX_HW_QUEUE_PHY) >> 2;
--	struct ieee80211_hw *hw = mt76_phy_hw(dev, phy_idx);
-+	struct ieee80211_hw *hw;
- 
- 	info->hw_queue &= ~MT_TX_HW_QUEUE_PHY;
- 
-+	if (dev->drv->get_hw) {
-+		dev->drv->get_hw(dev, wcid, phy_idx, &hw);
-+	} else {
-+		hw = mt76_phy_hw(dev, phy_idx);
-+	}
-+
- 	return hw;
- }
- 
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index cd25ea6..6ca3e06 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -2846,13 +2846,23 @@ static void mt7996_scan_check_sta(void *data, struct ieee80211_sta *sta)
- void mt7996_scan_work(struct work_struct *work)
- {
- 	struct mt7996_phy *phy = container_of(work, struct mt7996_phy, scan_work.work);
--	struct ieee80211_hw *hw = phy->mt76->hw;
-+	struct ieee80211_vif *vif = phy->scan_vif;
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct ieee80211_hw *hw = mvif->hw;
- 	struct cfg80211_scan_request *req = phy->scan_req;
- 	struct cfg80211_chan_def chandef = {};
- 	int duration;
- 	bool has_sta = false, active_scan = false;
- 
- 	mutex_lock(&phy->dev->mt76.mutex);
-+	/* don't let non-MLD AP scan other bands */
-+	if (vif->type != NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
-+	    phy != mt7996_hw_phy(hw)) {
-+		mt7996_scan_complete(phy, false);
-+		mutex_unlock(&phy->dev->mt76.mutex);
-+		return;
-+	}
-+
- 	if (phy->scan_chan_idx >= req->n_channels) {
- 		mt7996_scan_complete(phy, false);
- 		mutex_unlock(&phy->dev->mt76.mutex);
-@@ -2912,3 +2922,15 @@ void mt7996_scan_work(struct work_struct *work)
- 
- 	ieee80211_queue_delayed_work(hw, &phy->scan_work, duration);
- }
-+
-+void mt7996_get_hw(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 phy_idx,
-+		   struct ieee80211_hw **hw)
-+{
-+	struct mt7996_link_sta *mlink = wcid_to_mlink(wcid);
-+
-+	if (mlink) {
-+		*hw = mlink->sta->vif->hw;
-+	} else {
-+		*hw = mt76_phy_hw(dev, phy_idx);
-+	}
-+}
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 34ff96d..ddfde58 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -444,6 +444,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
- 		phy->monitor_vif = vif;
- 
- 	mvif->dev = dev;
-+	mvif->hw = hw;
- 	mvif->sta.vif = mvif;
- 
- 	ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index f3d162a..2a09ed1 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -2947,11 +2947,11 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
- 
- 	if (changed & BSS_CHANGED_FILS_DISCOVERY) {
- 		interval = conf->fils_discovery.max_interval;
--		skb = ieee80211_get_fils_discovery_tmpl(hw, vif);
-+		skb = ieee80211_get_fils_discovery_tmpl(mvif->hw, vif);
- 	} else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP &&
- 		   conf->unsol_bcast_probe_resp_interval) {
- 		interval = conf->unsol_bcast_probe_resp_interval;
--		skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif);
-+		skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(mvif->hw, vif);
- 	}
- 
- 	if (!skb) {
-diff --git a/mt7996/mmio.c b/mt7996/mmio.c
-index 6abbcb6..6bebdd8 100644
---- a/mt7996/mmio.c
-+++ b/mt7996/mmio.c
-@@ -658,6 +658,7 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
- 		.sta_assoc = mt7996_mac_sta_assoc,
- 		.sta_remove = mt7996_mac_sta_remove,
- 		.update_survey = mt7996_update_channel,
-+		.get_hw = mt7996_get_hw,
- 	};
- 	struct mt7996_dev *dev;
- 	struct mt76_dev *mdev;
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index c6ca00f..dc0a31d 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -353,6 +353,7 @@ struct mt7996_vif {
- 
- 	struct mt7996_sta sta;
- 	struct mt7996_dev *dev;
-+	struct ieee80211_hw *hw;
- 
- 	u8 master_link_id;
- 	u8 group_mld_id;
-@@ -897,6 +898,8 @@ int mt7996_init_tx_queues(struct mt7996_phy *phy, int idx,
- void mt7996_init_txpower(struct mt7996_phy *phy);
- int mt7996_txbf_init(struct mt7996_dev *dev);
- int mt7996_get_chip_sku(struct mt7996_dev *dev);
-+void mt7996_get_hw(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 phy_idx,
-+		   struct ieee80211_hw **hw);
- void mt7996_reset(struct mt7996_dev *dev);
- void mt7996_coredump(struct mt7996_dev *dev, u8 state);
- int mt7996_run(struct ieee80211_hw *hw);
-diff --git a/tx.c b/tx.c
-index e279506..6580833 100644
---- a/tx.c
-+++ b/tx.c
-@@ -76,7 +76,7 @@ mt76_tx_status_unlock(struct mt76_dev *dev, struct sk_buff_head *list)
- 			}
- 		}
- 
--		hw = mt76_tx_status_get_hw(dev, skb);
-+		hw = mt76_tx_status_get_hw(dev, skb, wcid);
- 		spin_lock_bh(&dev->rx_lock);
- 		ieee80211_tx_status_ext(hw, &status);
- 		spin_unlock_bh(&dev->rx_lock);
-@@ -272,7 +272,7 @@ void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *
- 	if (cb->pktid < MT_PACKET_ID_FIRST) {
- 		struct ieee80211_rate_status rs = {};
- 
--		hw = mt76_tx_status_get_hw(dev, skb);
-+		hw = mt76_tx_status_get_hw(dev, skb, wcid);
- 		status.sta = wcid_to_sta(wcid);
- 		if (status.sta && (wcid->rate.flags || wcid->rate.legacy)) {
- 			rs.rate_idx = wcid->rate;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0101-mtk-mt76-mt7996-rework-mac-functions-for-multi-link-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0101-mtk-mt76-mt7996-rework-mac-functions-for-multi-link-.patch
new file mode 100644
index 0000000..1e50891
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0101-mtk-mt76-mt7996-rework-mac-functions-for-multi-link-.patch
@@ -0,0 +1,249 @@
+From c5aaee0305b927c4e3a5e39bca3d19d3325b2ee8 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 4 Dec 2023 18:31:02 +0800
+Subject: [PATCH 101/199] mtk: mt76: mt7996: rework mac functions for
+ multi-link support
+
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mac.c | 91 +++++++++++++++++++++++++++++++++++++---------------
+ 1 file changed, 65 insertions(+), 26 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 8d6a153c..6d0506fc 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -85,10 +85,11 @@ static int mt7996_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap)
+ {
+ 	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+ 	struct ethhdr *eth_hdr = (struct ethhdr *)(skb->data + hdr_gap);
+-	struct mt7996_sta *msta = (struct mt7996_sta *)status->wcid;
++	struct mt7996_link_sta *mlink = (struct mt7996_link_sta *)status->wcid;
+ 	__le32 *rxd = (__le32 *)skb->data;
+ 	struct ieee80211_sta *sta;
+ 	struct ieee80211_vif *vif;
++	struct ieee80211_bss_conf *conf;
+ 	struct ieee80211_hdr hdr;
+ 	u16 frame_control;
+ 
+@@ -99,11 +100,14 @@ static int mt7996_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap)
+ 	if (!(le32_to_cpu(rxd[1]) & MT_RXD1_NORMAL_GROUP_4))
+ 		return -EINVAL;
+ 
+-	if (!msta || !msta->vif)
++	if (!mlink->sta || !mlink->sta->vif)
+ 		return -EINVAL;
+ 
+-	sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
+-	vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv);
++	sta = wcid_to_sta(status->wcid);
++	vif = container_of((void *)mlink->sta->vif, struct ieee80211_vif, drv_priv);
++	conf = rcu_dereference(vif->link_conf[mlink->wcid.link_id]);
++	if (unlikely(!conf))
++		return -ENOLINK;
+ 
+ 	/* store the info from RXD and ethhdr to avoid being overridden */
+ 	frame_control = le32_get_bits(rxd[8], MT_RXD8_FRAME_CONTROL);
+@@ -116,7 +120,7 @@ static int mt7996_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap)
+ 	switch (frame_control & (IEEE80211_FCTL_TODS |
+ 				 IEEE80211_FCTL_FROMDS)) {
+ 	case 0:
+-		ether_addr_copy(hdr.addr3, vif->bss_conf.bssid);
++		ether_addr_copy(hdr.addr3, conf->bssid);
+ 		break;
+ 	case IEEE80211_FCTL_FROMDS:
+ 		ether_addr_copy(hdr.addr3, eth_hdr->h_source);
+@@ -958,15 +962,21 @@ u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id)
+ }
+ 
+ static void
+-mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb)
++mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb,
++		     struct mt76_wcid *wcid)
+ {
+ 	struct mt7996_sta *msta;
+ 	struct mt7996_link_sta *mlink;
++	struct ieee80211_link_sta *link_sta;
+ 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ 	bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
+ 	u16 fc, tid;
+ 
+-	if (!sta || !(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he))
++	link_sta = rcu_dereference(sta->link[wcid->link_id]);
++	if (!link_sta)
++		return;
++
++	if (!sta->mlo && !(link_sta->ht_cap.ht_supported || link_sta->he_cap.has_he))
+ 		return;
+ 
+ 	tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
+@@ -990,17 +1000,17 @@ mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb)
+ 		return;
+ 
+ 	msta = (struct mt7996_sta *)sta->drv_priv;
+-	mlink = rcu_dereference(msta->link[0]);
++	mlink = rcu_dereference(msta->link[msta->pri_link]);
+ 	if (!test_and_set_bit(tid, &mlink->wcid.ampdu_state))
+ 		ieee80211_start_tx_ba_session(sta, tid, 0);
+ }
+ 
+ static void
+ mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t,
+-		 struct ieee80211_sta *sta, struct list_head *free_list)
++		 struct ieee80211_sta *sta, struct mt76_wcid *wcid,
++		 struct list_head *free_list)
+ {
+ 	struct mt76_dev *mdev = &dev->mt76;
+-	struct mt76_wcid *wcid;
+ 	__le32 *txwi;
+ 	u16 wcid_idx;
+ 
+@@ -1010,11 +1020,10 @@ mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t,
+ 
+ 	txwi = (__le32 *)mt76_get_txwi_ptr(mdev, t);
+ 	if (sta) {
+-		wcid = (struct mt76_wcid *)sta->drv_priv;
+ 		wcid_idx = wcid->idx;
+ 
+ 		if (likely(t->skb->protocol != cpu_to_be16(ETH_P_PAE)))
+-			mt7996_tx_check_aggr(sta, t->skb);
++			mt7996_tx_check_aggr(sta, t->skb, wcid);
+ 	} else {
+ 		wcid_idx = le32_get_bits(txwi[9], MT_TXD9_WLAN_IDX);
+ 	}
+@@ -1069,7 +1078,9 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ 		 */
+ 		info = le32_to_cpu(*cur_info);
+ 		if (info & MT_TXFREE_INFO_PAIR) {
+-			struct mt7996_link_sta *mlink;
++			struct mt7996_sta *msta;
++			unsigned long valid_links;
++			unsigned int link_id;
+ 			u16 idx;
+ 
+ 			idx = FIELD_GET(MT_TXFREE_INFO_WLAN_ID, info);
+@@ -1078,11 +1089,17 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ 			if (!sta)
+ 				continue;
+ 
+-			mlink = container_of(wcid, struct mt7996_link_sta, wcid);
++			valid_links = sta->valid_links ?: BIT(0);
++			msta = (struct mt7996_sta *)sta->drv_priv;
++			/* for MLD STA, add all link's wcid to sta_poll_list */
+ 			spin_lock_bh(&mdev->sta_poll_lock);
+-			if (list_empty(&mlink->wcid.poll_list))
+-				list_add_tail(&mlink->wcid.poll_list,
+-					      &mdev->sta_poll_list);
++			for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++				struct mt7996_link_sta *mlink =
++					rcu_dereference(msta->link[link_id]);
++
++				if (list_empty(&mlink->wcid.poll_list))
++					list_add_tail(&mlink->wcid.poll_list, &mdev->sta_poll_list);
++			}
+ 			spin_unlock_bh(&mdev->sta_poll_lock);
+ 			continue;
+ 		} else if (info & MT_TXFREE_INFO_HEADER) {
+@@ -1118,7 +1135,8 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ 			if (!txwi)
+ 				continue;
+ 
+-			mt7996_txwi_free(dev, txwi, sta, &free_list);
++			mt7996_txwi_free(dev, txwi, sta, wcid, &free_list);
++			txwi->jiffies = 0;
+ 		}
+ 	}
+ 
+@@ -1533,19 +1551,29 @@ static void
+ mt7996_update_vif_beacon(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ {
+ 	struct ieee80211_hw *hw = priv;
+-	struct ieee80211_bss_conf *conf = &vif->bss_conf;
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct mt7996_bss_conf *mconf = &mvif->deflink;
++	unsigned long update = vif->valid_links ?: BIT(0);
++	unsigned int link_id;
+ 
++	mutex_lock(&dev->mt76.mutex);
+ 	switch (vif->type) {
+ 	case NL80211_IFTYPE_MESH_POINT:
+ 	case NL80211_IFTYPE_ADHOC:
+ 	case NL80211_IFTYPE_AP:
+-		mt7996_mcu_add_beacon(hw, conf, mconf, conf->enable_beacon);
++		for_each_set_bit(link_id, &update, IEEE80211_MLD_MAX_NUM_LINKS) {
++			struct mt7996_bss_conf *mconf =
++					mconf_dereference_protected(mvif, link_id);
++			struct ieee80211_bss_conf *conf =
++					link_conf_dereference_protected(vif, link_id);
++
++			mt7996_mcu_add_beacon(hw, conf, mconf, conf->enable_beacon);
++		}
+ 		break;
+ 	default:
+ 		break;
+ 	}
++	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
+ static void
+@@ -1581,7 +1609,7 @@ void mt7996_tx_token_put(struct mt7996_dev *dev)
+ 
+ 	spin_lock_bh(&dev->mt76.token_lock);
+ 	idr_for_each_entry(&dev->mt76.token, txwi, id) {
+-		mt7996_txwi_free(dev, txwi, NULL, NULL);
++		mt7996_txwi_free(dev, txwi, NULL, NULL, NULL);
+ 		dev->mt76.token_count--;
+ 	}
+ 	spin_unlock_bh(&dev->mt76.token_lock);
+@@ -2265,21 +2293,31 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
+ 	u32 changed;
+ 	LIST_HEAD(list);
+ 
++	rcu_read_lock();
+ 	spin_lock_bh(&dev->mt76.sta_poll_lock);
+ 	list_splice_init(&dev->sta_rc_list, &list);
+ 
+ 	while (!list_empty(&list)) {
++		u8 link_id;
++
+ 		mlink = list_first_entry(&list, struct mt7996_link_sta, rc_list);
++		link_id = mlink->wcid.link_id;
++
+ 		list_del_init(&mlink->rc_list);
+ 		changed = mlink->changed;
+ 		mlink->changed = 0;
+ 		spin_unlock_bh(&dev->mt76.sta_poll_lock);
+ 
+-		sta = container_of((void *)mlink->sta, struct ieee80211_sta, drv_priv);
+-		link_sta = &sta->deflink;
+ 		vif = container_of((void *)mlink->sta->vif, struct ieee80211_vif, drv_priv);
+-		conf = &vif->bss_conf;
+-		mconf = &mlink->sta->vif->deflink;
++		conf = rcu_dereference(vif->link_conf[link_id]);
++		mconf = rcu_dereference(mlink->sta->vif->link[link_id]);
++		sta = wcid_to_sta(&mlink->wcid);
++		link_sta = rcu_dereference(sta->link[link_id]);
++
++		if (unlikely(!conf || !mconf || !link_sta)) {
++			spin_lock_bh(&dev->mt76.sta_poll_lock);
++			continue;
++		}
+ 
+ 		if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED |
+ 			       IEEE80211_RC_NSS_CHANGED |
+@@ -2294,6 +2332,7 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
+ 	}
+ 
+ 	spin_unlock_bh(&dev->mt76.sta_poll_lock);
++	rcu_read_unlock();
+ }
+ 
+ void mt7996_mac_work(struct work_struct *work)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0101-wifi-mt76-mt7996-rework-scanning-parts-for-MLD-STA-s.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0101-wifi-mt76-mt7996-rework-scanning-parts-for-MLD-STA-s.patch
deleted file mode 100644
index c79b078..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0101-wifi-mt76-mt7996-rework-scanning-parts-for-MLD-STA-s.patch
+++ /dev/null
@@ -1,153 +0,0 @@
-From 1cad504914e594c629dd2b5a5b67ff58afd307e6 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Wed, 13 Dec 2023 16:58:31 +0800
-Subject: [PATCH 101/116] wifi: mt76: mt7996: rework scanning parts for MLD STA
- support
-
-During the first scanning, the STA VIF is still a legacy interface,
-and at the moment it doesn't know if it's MLD or not. So do dome tricks
-here to let the legacy interface be abled to scan all bands.
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/mac.c  | 34 +++++++++++++++++++++++++---------
- mt7996/main.c | 17 +++++++++++++++++
- 2 files changed, 42 insertions(+), 9 deletions(-)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 6ca3e06..d6d1c07 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -2770,23 +2770,40 @@ static void
- mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
- 		       const u8 *dst)
- {
--	struct ieee80211_hw *hw = phy->mt76->hw;
- 	struct cfg80211_scan_request *req = phy->scan_req;
- 	struct ieee80211_vif *vif = phy->scan_vif;
-+	struct ieee80211_bss_conf *conf;
- 	struct mt7996_vif *mvif;
- 	struct mt7996_link_sta *mlink;
- 	struct ieee80211_tx_info *info;
-+	struct ieee80211_hw *hw;
- 	struct sk_buff *skb;
-+	unsigned long valid_links;
-+	unsigned int link_id;
- 
- 	if (!req || !vif)
- 		return;
- 
-+	valid_links = vif->valid_links ?: BIT(0);
- 	mvif = (struct mt7996_vif *)vif->drv_priv;
-+	hw = mvif->hw;
-+
-+	rcu_read_lock();
-+
-+	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
-+		conf = rcu_dereference(vif->link_conf[link_id]);
-+		mlink = rcu_dereference(mvif->sta.link[link_id]);
-+		if (mlink->wcid.phy_idx != phy->mt76->band_idx)
-+			continue;
-+	}
- 
--	skb = ieee80211_probereq_get(hw, vif->addr,
-+	if (unlikely(!conf))
-+		goto unlock;
-+
-+	skb = ieee80211_probereq_get(hw, conf->addr,
- 				     ssid->ssid, ssid->ssid_len, req->ie_len);
- 	if (!skb)
--		return;
-+		goto unlock;
- 
- 	if (is_unicast_ether_addr(dst)) {
- 		struct ieee80211_hdr_3addr *hdr =
-@@ -2804,30 +2821,29 @@ mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
- 
- 	skb_set_queue_mapping(skb, IEEE80211_AC_VO);
- 
--	rcu_read_lock();
--	if (!ieee80211_tx_prepare_skb(mt76_main_hw(phy->mt76), vif, skb,
--				      phy->scan_chan->band,
--				      NULL)) {
-+	if (!ieee80211_tx_prepare_skb(hw, vif, skb,
-+				      phy->scan_chan->band, NULL)) {
- 		rcu_read_unlock();
- 		ieee80211_free_txskb(hw, skb);
- 		return;
- 	}
- 
- 	local_bh_disable();
--	mlink = rcu_dereference(mvif->sta.link[0]);
- 	mt76_tx(phy->mt76, NULL, &mlink->wcid, skb);
- 	local_bh_enable();
- 
-+unlock:
- 	rcu_read_unlock();
- }
- 
- void mt7996_scan_complete(struct mt7996_phy *phy, bool aborted)
- {
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)phy->scan_vif->drv_priv;
- 	struct cfg80211_scan_info info = {
- 		.aborted = aborted,
- 	};
- 
--	ieee80211_scan_completed(phy->mt76->hw, &info);
-+	ieee80211_scan_completed(mvif->hw, &info);
- 	phy->scan_chan = NULL;
- 	phy->scan_req = NULL;
- 	phy->scan_vif = NULL;
-diff --git a/mt7996/main.c b/mt7996/main.c
-index ddfde58..ef08563 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -2191,6 +2191,8 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- {
- 	struct cfg80211_scan_request *req = &hw_req->req;
- 	struct mt7996_phy *phy = mt7996_band_phy(hw, req->channels[0]->band);
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	int ret;
- 
- 	mutex_lock(&phy->dev->mt76.mutex);
- 	if (WARN_ON(phy->scan_req || phy->scan_chan)) {
-@@ -2202,6 +2204,17 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	phy->scan_req = req;
- 	phy->scan_vif = vif;
- 	phy->scan_chan_idx = 0;
-+	if (vif->type == NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
-+	    (phy->mt76 != mvif->deflink.phy->mt76)) {
-+		phy->mt76->main_phy = hw->priv;
-+		mt7996_remove_bss_conf(vif, &vif->bss_conf, &mvif->deflink);
-+
-+		ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
-+		if (ret) {
-+			mutex_unlock(&phy->dev->mt76.mutex);
-+			return ret;
-+		}
-+	}
- 	mutex_unlock(&phy->dev->mt76.mutex);
- 
- 	ieee80211_queue_delayed_work(mt76_main_hw(phy->mt76), &phy->scan_work, 0);
-@@ -2212,6 +2225,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- static void
- mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
- {
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	int band;
- 
- 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
-@@ -2229,6 +2243,9 @@ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
- 
- 		mutex_lock(&phy->dev->mt76.mutex);
- 		mt7996_scan_complete(phy, true);
-+		if (vif->type == NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
-+		    (phy->mt76 != mvif->deflink.phy->mt76))
-+			phy->mt76->main_phy = NULL;
- 		mutex_unlock(&phy->dev->mt76.mutex);
- 	}
- }
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0102-mtk-mt76-rework-mcu-functions-for-multi-link-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0102-mtk-mt76-rework-mcu-functions-for-multi-link-support.patch
new file mode 100644
index 0000000..d6cf909
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0102-mtk-mt76-rework-mcu-functions-for-multi-link-support.patch
@@ -0,0 +1,212 @@
+From 06755ac0b2b1c2e38ce8fbb6da62f966ec7d219a Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 7 Dec 2023 15:39:03 +0800
+Subject: [PATCH 102/199] mtk: mt76: rework mcu functions for multi-link
+ support
+
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt76_connac_mcu.c | 10 +++----
+ mt76_connac_mcu.h |  2 +-
+ mt7996/mcu.c      | 70 +++++++++++++++++++++++++++++++++--------------
+ 3 files changed, 56 insertions(+), 26 deletions(-)
+
+diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
+index 33cbf4c2..de4e0019 100644
+--- a/mt76_connac_mcu.c
++++ b/mt76_connac_mcu.c
+@@ -370,10 +370,11 @@ void mt76_connac_mcu_bss_omac_tlv(struct sk_buff *skb,
+ EXPORT_SYMBOL_GPL(mt76_connac_mcu_bss_omac_tlv);
+ 
+ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+-				   struct ieee80211_vif *vif,
++				   struct ieee80211_bss_conf *conf,
+ 				   struct ieee80211_link_sta *link_sta,
+ 				   bool enable, bool newly)
+ {
++	struct ieee80211_vif *vif = conf->vif;
+ 	struct sta_rec_basic *basic;
+ 	struct tlv *tlv;
+ 	int conn_type;
+@@ -393,10 +394,9 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+ 
+ 	if (!link_sta) {
+ 		basic->conn_type = cpu_to_le32(CONNECTION_INFRA_BC);
+-
+ 		if (vif->type == NL80211_IFTYPE_STATION &&
+-		    !is_zero_ether_addr(vif->bss_conf.bssid)) {
+-			memcpy(basic->peer_addr, vif->bss_conf.bssid, ETH_ALEN);
++		    conf && !is_zero_ether_addr(conf->bssid)) {
++			memcpy(basic->peer_addr, conf->bssid, ETH_ALEN);
+ 			basic->aid = cpu_to_le16(vif->cfg.aid);
+ 		} else {
+ 			eth_broadcast_addr(basic->peer_addr);
+@@ -1059,7 +1059,7 @@ int mt76_connac_mcu_sta_cmd(struct mt76_phy *phy,
+ 
+ 	link_sta = info->sta ? &info->sta->deflink : NULL;
+ 	if (info->sta || !info->offload_fw)
+-		mt76_connac_mcu_sta_basic_tlv(dev, skb, info->vif,
++		mt76_connac_mcu_sta_basic_tlv(dev, skb, &info->vif->bss_conf,
+ 					      link_sta, info->enable,
+ 					      info->newly);
+ 	if (info->sta && info->enable)
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 1589a716..226e9a94 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1923,7 +1923,7 @@ mt76_connac_mcu_add_tlv(struct sk_buff *skb, int tag, int len)
+ int mt76_connac_mcu_set_channel_domain(struct mt76_phy *phy);
+ int mt76_connac_mcu_set_vif_ps(struct mt76_dev *dev, struct ieee80211_vif *vif);
+ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+-				   struct ieee80211_vif *vif,
++				   struct ieee80211_bss_conf *conf,
+ 				   struct ieee80211_link_sta *link_sta,
+ 				   bool enable, bool newly);
+ void mt76_connac_mcu_wtbl_generic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 7138230e..1f320a7b 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1169,10 +1169,12 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
+ 				sta = ieee80211_find_sta(vif, conf->bssid);
+ 			/* TODO: enable BSS_INFO_UAPSD & BSS_INFO_PM */
+ 			if (sta) {
+-				struct mt76_wcid *wcid;
++				struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++				struct mt7996_link_sta *mlink;
+ 
+-				wcid = (struct mt76_wcid *)sta->drv_priv;
+-				sta_wlan_idx = wcid->idx;
++				mlink = rcu_dereference(msta->link[conf->link_id]);
++				if (mlink)
++					sta_wlan_idx = mlink->wcid.idx;
+ 			}
+ 			rcu_read_unlock();
+ 		}
+@@ -1306,9 +1308,8 @@ int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct mt7996_bss_conf *mconf)
+ static int
+ mt7996_mcu_sta_ba(struct mt7996_dev *dev, struct mt76_vif *mvif,
+ 		  struct ieee80211_ampdu_params *params,
+-		  bool enable, bool tx)
++		  struct mt76_wcid *wcid, bool enable, bool tx)
+ {
+-	struct mt76_wcid *wcid = (struct mt76_wcid *)params->sta->drv_priv;
+ 	struct sta_rec_ba_uni *ba;
+ 	struct sk_buff *skb;
+ 	struct tlv *tlv;
+@@ -1338,24 +1339,53 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ 			 struct ieee80211_ampdu_params *params,
+ 			 bool enable)
+ {
+-	struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv;
+-	struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
+-	struct mt7996_link_sta *mlink = mlink_dereference_protected(msta, 0);
++	struct ieee80211_sta *sta = params->sta;
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	unsigned long valid_links = sta->valid_links ?: BIT(0);
++	unsigned int link_id;
++
++	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++		struct mt7996_link_sta *mlink =
++			mlink_dereference_protected(msta, link_id);
++		struct mt7996_bss_conf *mconf =
++			mconf_dereference_protected(msta->vif, link_id);
++		int ret;
+ 
+-	if (enable && !params->amsdu)
+-		mlink->wcid.amsdu = false;
++		if (enable && !params->amsdu)
++			mlink->wcid.amsdu = false;
+ 
+-	return mt7996_mcu_sta_ba(dev, &mconf->mt76, params, enable, true);
++		ret = mt7996_mcu_sta_ba(dev, &mconf->mt76, params,
++					&mlink->wcid, enable, true);
++		if (ret)
++			return ret;
++	}
++
++	return 0;
+ }
+ 
+ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
+ 			 struct ieee80211_ampdu_params *params,
+ 			 bool enable)
+ {
+-	struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv;
+-	struct mt7996_bss_conf *mconf = mconf_dereference_protected(msta->vif, 0);
++	struct ieee80211_sta *sta = params->sta;
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	unsigned long valid_links = sta->valid_links ?: BIT(0);
++	unsigned int link_id;
+ 
+-	return mt7996_mcu_sta_ba(dev, &mconf->mt76, params, enable, false);
++	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++		struct mt7996_link_sta *mlink =
++			mlink_dereference_protected(msta, link_id);
++		struct mt7996_bss_conf *mconf =
++			mconf_dereference_protected(msta->vif, link_id);
++		int ret;
++
++		ret = mt7996_mcu_sta_ba(dev, &mconf->mt76, params, &mlink->wcid,
++					enable, false);
++		if (ret)
++			return ret;
++	}
++
++	return 0;
+ }
+ 
+ static void
+@@ -2379,7 +2409,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ 		return PTR_ERR(skb);
+ 
+ 	/* starec basic */
+-	mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, vif, link_sta,
++	mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, conf, link_sta,
+ 				      enable, newly);
+ 
+ 	if (!enable)
+@@ -2841,7 +2871,7 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ 			  struct mt7996_bss_conf *mconf, int en)
+ {
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+-	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct mt7996_phy *phy = mconf->phy;
+ 	struct ieee80211_mutable_offsets offs;
+ 	struct ieee80211_tx_info *info;
+ 	struct sk_buff *skb, *rskb;
+@@ -2857,7 +2887,7 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ 	if (IS_ERR(rskb))
+ 		return PTR_ERR(rskb);
+ 
+-	skb = ieee80211_beacon_get_template(hw, conf->vif, &offs, 0);
++	skb = ieee80211_beacon_get_template(hw, conf->vif, &offs, conf->link_id);
+ 	if (!skb) {
+ 		dev_kfree_skb(rskb);
+ 		return -EINVAL;
+@@ -2895,9 +2925,9 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+ {
+ #define OFFLOAD_TX_MODE_SU	BIT(0)
+ #define OFFLOAD_TX_MODE_MU	BIT(1)
+-	struct ieee80211_hw *hw = mt76_hw(dev);
+ 	struct ieee80211_vif *vif = conf->vif;
+-	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_phy *phy = mconf->phy;
+ 	struct cfg80211_chan_def *chandef = &mconf->phy->mt76->chandef;
+ 	enum nl80211_band band = chandef->chan->band;
+ 	struct mt76_wcid *wcid = &dev->mt76.global_wcid;
+@@ -5155,7 +5185,7 @@ int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
+ 				rssi[3] = to_rssi(MT_PRXV_RCPI0, rcpi[3]);
+ 
+ 				mlink = container_of(wcid, struct mt7996_link_sta, wcid);
+-				phy = mlink->sta->vif->deflink.phy->mt76;
++				phy = dev->phys[wcid->phy_idx];
+ 				mlink->ack_signal = mt76_rx_signal(phy->antenna_mask, rssi);
+ 				ewma_avg_signal_add(&mlink->avg_ack_signal, -mlink->ack_signal);
+ 			} else {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0102-wifi-mt76-mt7996-implement-mld-address-translation.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0102-wifi-mt76-mt7996-implement-mld-address-translation.patch
deleted file mode 100644
index d4e5cae..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0102-wifi-mt76-mt7996-implement-mld-address-translation.patch
+++ /dev/null
@@ -1,125 +0,0 @@
-From fe38a0bccd1cf3ddb1736d79048b6ae9b17b94d0 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 7 Dec 2023 16:31:56 +0800
-Subject: [PATCH 102/116] wifi: mt76: mt7996: implement mld address translation
-
-Do the MLD to link address translation for EAPOL and management frames
-in driver.
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Co-developed-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/mac.c  | 20 ++++++++++++++++++++
- mt7996/main.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++---
- 2 files changed, 66 insertions(+), 3 deletions(-)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index d6d1c07..3141fe4 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -895,6 +895,26 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
- 		mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, key,
- 				      pid, qid, 0);
- 
-+	/* translate addr3 of EAPOL by driver */
-+	if (unlikely(tx_info->skb->protocol == cpu_to_be16(ETH_P_PAE)) && sta->mlo) {
-+		if (ether_addr_equal(vif->addr, hdr->addr3)) {
-+			struct ieee80211_bss_conf *conf;
-+
-+			conf = rcu_dereference(vif->link_conf[wcid->link_id]);
-+			if (unlikely(!conf))
-+				return -ENOLINK;
-+
-+			memcpy(hdr->addr3, conf->addr, ETH_ALEN);
-+		} else if (ether_addr_equal(sta->addr, hdr->addr3)) {
-+			struct ieee80211_link_sta *link_sta;
-+
-+			link_sta = rcu_dereference(sta->link[wcid->link_id]);
-+			memcpy(hdr->addr3, link_sta->addr, ETH_ALEN);
-+		}
-+
-+		pr_info("EAPOL: a1=%pM, a2=%pM, a3=%pM\n", hdr->addr1, hdr->addr2, hdr->addr3);
-+	}
-+
- 	txp = (struct mt76_connac_txp_common *)(txwi + MT_TXD_SIZE);
- 	for (i = 0; i < nbuf; i++) {
- 		u16 len;
-diff --git a/mt7996/main.c b/mt7996/main.c
-index ef08563..61c4d2d 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -1308,14 +1308,56 @@ static void mt7996_tx(struct ieee80211_hw *hw,
- 
- 	rcu_read_lock();
- 	if (mvif && msta) {
-+		struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- 		struct mt7996_bss_conf *mconf;
- 		struct mt7996_link_sta *mlink;
--
- 		u8 link_id = u32_get_bits(info->control.flags,
- 					  IEEE80211_TX_CTRL_MLO_LINK);
-+		struct ieee80211_sta *sta = ieee80211_find_sta(vif, hdr->addr1);
-+
-+		if (link_id >= IEEE80211_LINK_UNSPECIFIED) {
-+			if (sta) {
-+				struct mt7996_sta *peer;
-+
-+				peer = (struct mt7996_sta *)sta->drv_priv;
-+				link_id = peer->pri_link;
-+			} else {
-+				link_id = mvif->master_link_id;
-+			}
-+		}
- 
--		if (link_id >= IEEE80211_LINK_UNSPECIFIED)
--			link_id = mvif->master_link_id;
-+		/* translate mld addr to link addr */
-+		if (ieee80211_vif_is_mld(vif)) {
-+			struct ieee80211_bss_conf *conf;
-+			if (sta) {
-+				struct ieee80211_link_sta *link_sta =
-+					rcu_dereference(sta->link[link_id]);
-+
-+				if (!link_sta) {
-+					mlo_dbg(mt7996_hw_phy(mvif->hw), "request TX on invalid link_id=%u, use primary link (id=%u) instead.\n",
-+						      link_id, msta->pri_link);
-+					link_id = msta->pri_link;
-+					link_sta = rcu_dereference(sta->link[link_id]);
-+
-+					if (!link_sta) {
-+						mlo_dbg(mt7996_hw_phy(mvif->hw), "primary link became invalid, give up the TX\n");
-+						goto unlock;
-+					}
-+				}
-+
-+				memcpy(hdr->addr1, link_sta->addr, ETH_ALEN);
-+				if (ether_addr_equal(sta->addr, hdr->addr3))
-+					memcpy(hdr->addr3, link_sta->addr, ETH_ALEN);
-+			}
-+
-+			conf = rcu_dereference(vif->link_conf[link_id]);
-+			if (unlikely(!conf))
-+				goto unlock;
-+
-+			memcpy(hdr->addr2, conf->addr, ETH_ALEN);
-+			if (ether_addr_equal(vif->addr, hdr->addr3))
-+				memcpy(hdr->addr3, conf->addr, ETH_ALEN);
-+		}
- 
- 		mconf = rcu_dereference(mvif->link[link_id]);
- 		mlink = rcu_dereference(msta->link[link_id]);
-@@ -1333,6 +1375,7 @@ static void mt7996_tx(struct ieee80211_hw *hw,
- 	}
- 
- 	mt76_tx(mphy, control->sta, wcid, skb);
-+unlock:
- 	rcu_read_unlock();
- }
- 
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0103-mtk-mt76-rework-connac-helpers.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0103-mtk-mt76-rework-connac-helpers.patch
new file mode 100644
index 0000000..59c3a83
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0103-mtk-mt76-rework-connac-helpers.patch
@@ -0,0 +1,174 @@
+From 7ee1d3297a47a4e1d14fef747bee991dadd6298b Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 11 Dec 2023 18:45:00 +0800
+Subject: [PATCH 103/199] mtk: mt76: rework connac helpers
+
+Rework connac helpers related to rate and phymode.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt76_connac.h     |  2 +-
+ mt76_connac_mac.c | 17 ++++++++---------
+ mt76_connac_mcu.c |  6 +++---
+ mt76_connac_mcu.h |  2 +-
+ mt7925/main.c     |  2 +-
+ mt7996/main.c     |  2 +-
+ mt7996/mcu.c      |  2 +-
+ 7 files changed, 16 insertions(+), 17 deletions(-)
+
+diff --git a/mt76_connac.h b/mt76_connac.h
+index 445d0f0a..f7766a98 100644
+--- a/mt76_connac.h
++++ b/mt76_connac.h
+@@ -427,7 +427,7 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
+ 				 struct ieee80211_key_conf *key, int pid,
+ 				 enum mt76_txq_id qid, u32 changed);
+ u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy,
+-				 struct ieee80211_vif *vif,
++				 struct ieee80211_bss_conf *conf,
+ 				 bool beacon, bool mcast);
+ bool mt76_connac2_mac_fill_txs(struct mt76_dev *dev, struct mt76_wcid *wcid,
+ 			       __le32 *txs_data);
+diff --git a/mt76_connac_mac.c b/mt76_connac_mac.c
+index b841bf62..c1e9ba0f 100644
+--- a/mt76_connac_mac.c
++++ b/mt76_connac_mac.c
+@@ -291,12 +291,11 @@ EXPORT_SYMBOL_GPL(mt76_connac_init_tx_queues);
+ })
+ 
+ u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy,
+-				 struct ieee80211_vif *vif,
++				 struct ieee80211_bss_conf *conf,
+ 				 bool beacon, bool mcast)
+ {
+-	struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+-	struct cfg80211_chan_def *chandef = mvif->ctx ?
+-					    &mvif->ctx->def : &mphy->chandef;
++	struct ieee80211_vif *vif = conf->vif;
++	struct cfg80211_chan_def *chandef = &mphy->chandef;
+ 	u8 nss = 0, mode = 0, band = chandef->chan->band;
+ 	int rateidx = 0, mcast_rate;
+ 
+@@ -304,14 +303,14 @@ u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy,
+ 		goto legacy;
+ 
+ 	if (is_mt7921(mphy->dev)) {
+-		rateidx = ffs(vif->bss_conf.basic_rates) - 1;
++		rateidx = ffs(conf->basic_rates) - 1;
+ 		goto legacy;
+ 	}
+ 
+ 	if (beacon) {
+ 		struct cfg80211_bitrate_mask *mask;
+ 
+-		mask = &vif->bss_conf.beacon_tx_rate;
++		mask = &conf->beacon_tx_rate;
+ 
+ 		__bitrate_mask_check(he_mcs, HE_SU);
+ 		__bitrate_mask_check(vht_mcs, VHT);
+@@ -323,11 +322,11 @@ u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy,
+ 		}
+ 	}
+ 
+-	mcast_rate = vif->bss_conf.mcast_rate[band];
++	mcast_rate = conf->mcast_rate[band];
+ 	if (mcast && mcast_rate > 0)
+ 		rateidx = mcast_rate - 1;
+ 	else
+-		rateidx = ffs(vif->bss_conf.basic_rates) - 1;
++		rateidx = ffs(conf->basic_rates) - 1;
+ 
+ legacy:
+ 	rateidx = mt76_calculate_default_rate(mphy, vif, rateidx);
+@@ -561,7 +560,7 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
+ 		struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ 		bool multicast = ieee80211_is_data(hdr->frame_control) &&
+ 				 is_multicast_ether_addr(hdr->addr1);
+-		u16 rate = mt76_connac2_mac_tx_rate_val(mphy, vif, beacon,
++		u16 rate = mt76_connac2_mac_tx_rate_val(mphy, &vif->bss_conf, beacon,
+ 							multicast);
+ 		u32 val = MT_TXD6_FIXED_BW;
+ 
+diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
+index de4e0019..ab7cf4a6 100644
+--- a/mt76_connac_mcu.c
++++ b/mt76_connac_mcu.c
+@@ -1366,7 +1366,7 @@ u8 mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif,
+ }
+ EXPORT_SYMBOL_GPL(mt76_connac_get_phy_mode);
+ 
+-u8 mt76_connac_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_vif *vif,
++u8 mt76_connac_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_bss_conf *conf,
+ 				enum nl80211_band band)
+ {
+ 	const struct ieee80211_sta_eht_cap *eht_cap;
+@@ -1377,9 +1377,9 @@ u8 mt76_connac_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_vif *vif,
+ 		mode |= PHY_MODE_AX_6G;
+ 
+ 	sband = phy->hw->wiphy->bands[band];
+-	eht_cap = ieee80211_get_eht_iftype_cap(sband, vif->type);
++	eht_cap = ieee80211_get_eht_iftype_cap(sband, conf->vif->type);
+ 
+-	if (!eht_cap || !eht_cap->has_eht || !vif->bss_conf.eht_support)
++	if (!eht_cap || !eht_cap->has_eht || !conf->eht_support)
+ 		return mode;
+ 
+ 	switch (band) {
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 226e9a94..f6a0d328 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -2036,7 +2036,7 @@ mt76_connac_get_eht_phy_cap(struct mt76_phy *phy, struct ieee80211_vif *vif);
+ u8 mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif,
+ 			    enum nl80211_band band,
+ 			    struct ieee80211_link_sta *sta);
+-u8 mt76_connac_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_vif *vif,
++u8 mt76_connac_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_bss_conf *conf,
+ 				enum nl80211_band band);
+ 
+ int mt76_connac_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
+diff --git a/mt7925/main.c b/mt7925/main.c
+index 8c0768bf..c1b1cd8a 100644
+--- a/mt7925/main.c
++++ b/mt7925/main.c
+@@ -793,7 +793,7 @@ mt7925_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	u16 rate;
+ 	u8 i, idx, ht;
+ 
+-	rate = mt76_connac2_mac_tx_rate_val(mphy, vif, beacon, mcast);
++	rate = mt76_connac2_mac_tx_rate_val(mphy, &vif->bss_conf, beacon, mcast);
+ 	ht = FIELD_GET(MT_TX_RATE_MODE, rate) > MT_PHY_TYPE_OFDM;
+ 
+ 	if (beacon && ht) {
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 65e244da..1ea81d62 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -808,7 +808,7 @@ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
+ 	u16 rate;
+ 	u8 i, idx;
+ 
+-	rate = mt76_connac2_mac_tx_rate_val(mphy, conf->vif, beacon, mcast);
++	rate = mt76_connac2_mac_tx_rate_val(mphy, conf, beacon, mcast);
+ 
+ 	if (beacon) {
+ 		struct mt7996_phy *phy = mphy->priv;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 1f320a7b..bf7231cd 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1219,7 +1219,7 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
+ 	bss->dtim_period = conf->dtim_period;
+ 	bss->phymode = mt76_connac_get_phy_mode(phy, vif,
+ 						chandef->chan->band, NULL);
+-	bss->phymode_ext = mt76_connac_get_phy_mode_ext(phy, vif,
++	bss->phymode_ext = mt76_connac_get_phy_mode_ext(phy, conf,
+ 							chandef->chan->band);
+ 
+ 	return 0;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0103-wifi-mt76-mt7996-use-BSS_CHANGED_TXPOWER-for-txpower.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0103-wifi-mt76-mt7996-use-BSS_CHANGED_TXPOWER-for-txpower.patch
deleted file mode 100644
index ab0aacb..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0103-wifi-mt76-mt7996-use-BSS_CHANGED_TXPOWER-for-txpower.patch
+++ /dev/null
@@ -1,106 +0,0 @@
-From dd18695ef0e8a86e7ab7560ef2c07faf2ef381d7 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Tue, 27 Feb 2024 18:07:11 +0800
-Subject: [PATCH 103/116] wifi: mt76: mt7996: use BSS_CHANGED_TXPOWER for
- txpower setting
-
-This is a preliminary patch to add MLO support for mt7996 chipsets.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/main.c     | 12 +++---------
- mt7996/mcu.c      |  5 +++--
- mt7996/mt7996.h   |  3 ++-
- mt7996/testmode.c |  2 +-
- 4 files changed, 9 insertions(+), 13 deletions(-)
-
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 61c4d2d..2d0dc16 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -661,14 +661,6 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
- {
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
--	int ret;
--
--	if (changed & (IEEE80211_CONF_CHANGE_POWER |
--		       IEEE80211_CONF_CHANGE_CHANNEL)) {
--		ret = mt7996_mcu_set_txpower_sku(phy);
--		if (ret)
--			return ret;
--	}
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
-@@ -968,6 +960,9 @@ static void mt7996_link_info_changed(struct ieee80211_hw *hw,
- 	if (changed & BSS_CHANGED_MU_GROUPS)
- 		mt7996_update_mu_group(hw, info, mconf);
- 
-+	if (changed & BSS_CHANGED_TXPOWER)
-+		mt7996_mcu_set_txpower_sku(phy, info);
-+
- out:
- 	mutex_unlock(&dev->mt76.mutex);
- }
-@@ -1607,7 +1602,6 @@ mt7996_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
- 	mt76_set_stream_caps(phy->mt76, true);
- 	mt7996_set_stream_vht_txbf_caps(phy);
- 	mt7996_set_stream_he_eht_caps(phy);
--	mt7996_mcu_set_txpower_sku(phy);
- 
- 	mutex_unlock(&dev->mt76.mutex);
- 
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 2a09ed1..025a00c 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -5325,7 +5325,8 @@ mt7996_update_max_txpower_cur(struct mt7996_phy *phy, int tx_power)
- 		mphy->txpower_cur = e2p_power_limit;
- }
- 
--int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
-+int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
-+			       struct ieee80211_bss_conf *conf)
- {
- #define TX_POWER_LIMIT_TABLE_RATE	0
- #define TX_POWER_LIMIT_TABLE_PATH	1
-@@ -5354,7 +5355,7 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
- 
- 	if (hw->conf.power_level == INT_MIN)
- 		hw->conf.power_level = 127;
--	txpower_limit = mt7996_get_power_bound(phy, hw->conf.power_level);
-+	txpower_limit = mt7996_get_power_bound(phy, conf->txpower);
- 
- 	if (phy->sku_limit_en) {
- 		txpower_limit = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index dc0a31d..39aa3ee 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -976,7 +976,8 @@ int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch);
- int mt7996_mcu_get_temperature(struct mt7996_phy *phy);
- int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state);
- int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable);
--int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy);
-+int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
-+			       struct ieee80211_bss_conf *conf);
- int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
- 		       u8 rx_sel, u8 val);
- int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev,
-diff --git a/mt7996/testmode.c b/mt7996/testmode.c
-index ba17f94..0565ebc 100644
---- a/mt7996/testmode.c
-+++ b/mt7996/testmode.c
-@@ -1830,7 +1830,7 @@ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
- 		mt7996_tm_update_channel(phy);
- 		mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(SKU_POWER_LIMIT), td->sku_en);
- 		mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(BACKOFF_POWER_LIMIT), td->sku_en);
--		mt7996_mcu_set_txpower_sku(phy);
-+		mt7996_mcu_set_txpower_sku(phy, &phy->monitor_vif->bss_conf);
- 	}
- 	if (changed & BIT(TM_CHANGED_TX_LENGTH)) {
- 		mt7996_tm_set(dev, SET_ID(TX_LEN), td->tx_mpdu_len);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0104-mtk-mt76-mt7996-handle-mapping-for-hw-and-phy.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0104-mtk-mt76-mt7996-handle-mapping-for-hw-and-phy.patch
new file mode 100644
index 0000000..4d17271
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0104-mtk-mt76-mt7996-handle-mapping-for-hw-and-phy.patch
@@ -0,0 +1,218 @@
+From 55f4ab5ef60b32bb44395dc818b0dbc55fb7d81a Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 5 Dec 2023 13:56:51 +0800
+Subject: [PATCH 104/199] mtk: mt76: mt7996: handle mapping for hw and phy
+
+We've used mt7996_band_phy() to do mapping from ieee80211_hw to mt7996_phy,
+and this patch is a temporal workaround for opposite direction.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mac80211.c    | 11 ++++++++++-
+ mt76.h        | 10 ++++++++++
+ mt7996/mac.c  |  7 +++++--
+ mt7996/main.c | 41 ++++++++++++++++++++++++++++++-----------
+ 4 files changed, 55 insertions(+), 14 deletions(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index 49834afe..bf7ead01 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -823,9 +823,13 @@ EXPORT_SYMBOL_GPL(mt76_has_tx_pending);
+ struct mt76_channel_state *
+ mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c)
+ {
++	struct mt76_phy *ori_phy = phy;
+ 	struct mt76_sband *msband;
+ 	int idx;
+ 
++	if (phy->main_phy)
++		phy = phy->main_phy;
++begin:
+ 	if (c->band == NL80211_BAND_2GHZ)
+ 		msband = &phy->sband_2g;
+ 	else if (c->band == NL80211_BAND_6GHZ)
+@@ -834,6 +838,11 @@ mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c)
+ 		msband = &phy->sband_5g;
+ 
+ 	idx = c - &msband->sband.channels[0];
++	/* TODO: mlo: this is a temp solution, need to come up with a more clever one */
++	if (idx < 0 || idx >= msband->sband.n_channels) {
++		phy = ori_phy;
++		goto begin;
++	}
+ 	return &msband->chan[idx];
+ }
+ EXPORT_SYMBOL_GPL(mt76_channel_state);
+@@ -1073,7 +1082,7 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
+ 	}
+ 
+ 	*sta = wcid_to_sta(mstat.wcid);
+-	*hw = mt76_phy_hw(dev, mstat.phy_idx);
++	*hw = mt76_main_hw(dev->phys[mstat.phy_idx]);
+ }
+ 
+ static void
+diff --git a/mt76.h b/mt76.h
+index 6a7752ef..f037284a 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -829,6 +829,7 @@ struct mt76_vif {
+ struct mt76_phy {
+ 	struct ieee80211_hw *hw;
+ 	struct mt76_dev *dev;
++	struct mt76_phy *main_phy;
+ 	void *priv;
+ 
+ 	unsigned long state;
+@@ -1308,6 +1309,15 @@ mt76_phy_hw(struct mt76_dev *dev, u8 phy_idx)
+ 	return mt76_dev_phy(dev, phy_idx)->hw;
+ }
+ 
++static inline struct ieee80211_hw *
++mt76_main_hw(struct mt76_phy *phy)
++{
++	if (phy->main_phy)
++		return mt76_dev_phy(phy->dev, phy->main_phy->band_idx)->hw;
++
++	return mt76_dev_phy(phy->dev, phy->band_idx)->hw;
++}
++
+ static inline u8 *
+ mt76_get_txwi_ptr(struct mt76_dev *dev, struct mt76_txwi_cache *t)
+ {
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 6d0506fc..c0c0df2f 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2382,7 +2382,10 @@ void mt7996_mac_work(struct work_struct *work)
+ 
+ 	mt76_tx_status_check(mdev, false);
+ 
+-	ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
++	if (mphy->main_phy && !test_bit(MT76_STATE_RUNNING, &mphy->main_phy->state))
++		return;
++
++	ieee80211_queue_delayed_work(mt76_main_hw(mphy), &mphy->mac_work,
+ 				     MT7996_WATCHDOG_TIME);
+ }
+ 
+@@ -2801,7 +2804,7 @@ mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
+ 	skb_set_queue_mapping(skb, IEEE80211_AC_VO);
+ 
+ 	rcu_read_lock();
+-	if (!ieee80211_tx_prepare_skb(hw, vif, skb,
++	if (!ieee80211_tx_prepare_skb(mt76_main_hw(phy->mt76), vif, skb,
+ 				      phy->scan_chan->band,
+ 				      NULL)) {
+ 		rcu_read_unlock();
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 1ea81d62..331dd4d4 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -173,6 +173,7 @@ static void mt7996_stop(struct ieee80211_hw *hw)
+ 		mutex_lock(&dev->mt76.mutex);
+ 		mt7996_mcu_set_radio_en(phy, false);
+ 		clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
++		phy->mt76->main_phy = NULL;
+ 		mutex_unlock(&dev->mt76.mutex);
+ 	}
+ }
+@@ -545,9 +546,10 @@ out:
+ 	clear_bit(MT76_RESET, &phy->mt76->state);
+ 	mutex_unlock(&dev->mt76.mutex);
+ 
+-	mt76_txq_schedule_all(phy->mt76);
++	if (phy->mt76 == phy->mt76->main_phy)
++		mt76_txq_schedule_all(phy->mt76);
+ 
+-	ieee80211_queue_delayed_work(phy->mt76->hw,
++	ieee80211_queue_delayed_work(mt76_main_hw(phy->mt76),
+ 				     &phy->mt76->mac_work,
+ 				     MT7996_WATCHDOG_TIME);
+ 
+@@ -558,11 +560,11 @@ int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef
+ {
+ 	int ret;
+ 
+-	ieee80211_stop_queues(phy->mt76->hw);
++	ieee80211_stop_queues(mt76_main_hw(phy->mt76));
+ 	ret = __mt7996_set_channel(phy, chandef);
+ 	if (ret)
+ 		return ret;
+-	ieee80211_wake_queues(phy->mt76->hw);
++	ieee80211_wake_queues(mt76_main_hw(phy->mt76));
+ 
+ 	return 0;
+ }
+@@ -735,6 +737,7 @@ static void mt7996_configure_filter(struct ieee80211_hw *hw,
+ 			MT_WF_RFCR1_DROP_CFEND |
+ 			MT_WF_RFCR1_DROP_CFACK;
+ 	u32 flags = 0;
++	u8 band;
+ 
+ #define MT76_FILTER(_flag, _hw) do {					\
+ 		flags |= *total_flags & FIF_##_flag;			\
+@@ -768,12 +771,26 @@ static void mt7996_configure_filter(struct ieee80211_hw *hw,
+ 			     MT_WF_RFCR_DROP_NDPA);
+ 
+ 	*total_flags = flags;
+-	mt76_wr(dev, MT_WF_RFCR(phy->mt76->band_idx), phy->rxfilter);
+ 
+-	if (*total_flags & FIF_CONTROL)
+-		mt76_clear(dev, MT_WF_RFCR1(phy->mt76->band_idx), ctl_flags);
+-	else
+-		mt76_set(dev, MT_WF_RFCR1(phy->mt76->band_idx), ctl_flags);
++	/* configure rx filter to all affliated phy */
++	for (band = 0; band < __MT_MAX_BAND; band++) {
++		struct mt7996_phy *tmp;
++
++		if (!hw->wiphy->bands[band])
++			continue;
++
++		tmp = dev->mt76.phys[band]->priv;
++		if (tmp->mt76->main_phy != phy->mt76)
++			continue;
++
++		tmp->rxfilter = phy->rxfilter;
++		mt76_wr(dev, MT_WF_RFCR(tmp->mt76->band_idx), phy->rxfilter);
++
++		if (*total_flags & FIF_CONTROL)
++			mt76_clear(dev, MT_WF_RFCR1(tmp->mt76->band_idx), ctl_flags);
++		else
++			mt76_set(dev, MT_WF_RFCR1(tmp->mt76->band_idx), ctl_flags);
++	}
+ 
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+@@ -2190,7 +2207,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	phy->scan_chan_idx = 0;
+ 	mutex_unlock(&phy->dev->mt76.mutex);
+ 
+-	ieee80211_queue_delayed_work(hw, &phy->scan_work, 0);
++	ieee80211_queue_delayed_work(mt76_main_hw(phy->mt76), &phy->scan_work, 0);
+ 
+ 	return 0;
+ }
+@@ -2207,7 +2224,8 @@ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ 			continue;
+ 
+ 		phy = mt7996_band_phy(hw, band);
+-		if (!(test_bit(MT76_SCANNING, &phy->mt76->state)))
++		if (!(test_bit(MT76_SCANNING, &phy->mt76->state) &&
++		      phy->mt76->main_phy == hw->priv))
+ 			continue;
+ 
+ 		cancel_delayed_work_sync(&phy->scan_work);
+@@ -2228,6 +2246,7 @@ mt7996_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
+ 	wiphy_info(hw->wiphy, "%s: add %u\n", __func__, conf->def.chan->hw_value);
+ 	mutex_lock(&phy->dev->mt76.mutex);
+ 
++	phy->mt76->main_phy = hw->priv;
+ 	if (ctx->assigned) {
+ 		mutex_unlock(&phy->dev->mt76.mutex);
+ 		return -ENOSPC;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0104-wifi-mt76-mt7996-temp-support-for-single-wiphy.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0104-wifi-mt76-mt7996-temp-support-for-single-wiphy.patch
deleted file mode 100644
index 9f92af3..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0104-wifi-mt76-mt7996-temp-support-for-single-wiphy.patch
+++ /dev/null
@@ -1,436 +0,0 @@
-From b8584db9dd73a4686872e75f5919184e4cb109d0 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 28 Mar 2024 18:50:04 +0800
-Subject: [PATCH 104/116] wifi: mt76: mt7996: temp support for single wiphy
-
-Add temporal single wiphy for simultaneously supporting MLD and legacy
-interfaces.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mac80211.c      | 11 +------
- mt76.h          | 11 +------
- mt7996/eeprom.c |  6 ----
- mt7996/init.c   | 23 ++++++++++---
- mt7996/mac.c    |  5 +--
- mt7996/main.c   | 88 ++++++++++++++++++++++++++++++-------------------
- mt7996/mt7996.h | 14 ++++----
- 7 files changed, 81 insertions(+), 77 deletions(-)
-
-diff --git a/mac80211.c b/mac80211.c
-index 1dec143..8b9f3fd 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -824,13 +824,9 @@ EXPORT_SYMBOL_GPL(mt76_has_tx_pending);
- struct mt76_channel_state *
- mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c)
- {
--	struct mt76_phy *ori_phy = phy;
- 	struct mt76_sband *msband;
- 	int idx;
- 
--	if (phy->main_phy)
--		phy = phy->main_phy;
--begin:
- 	if (c->band == NL80211_BAND_2GHZ)
- 		msband = &phy->sband_2g;
- 	else if (c->band == NL80211_BAND_6GHZ)
-@@ -839,11 +835,6 @@ begin:
- 		msband = &phy->sband_5g;
- 
- 	idx = c - &msband->sband.channels[0];
--	/* TODO: mlo: this is a temp solution, need to come up with a more clever one */
--	if (idx < 0 || idx >= msband->sband.n_channels) {
--		phy = ori_phy;
--		goto begin;
--	}
- 	return &msband->chan[idx];
- }
- EXPORT_SYMBOL_GPL(mt76_channel_state);
-@@ -1082,7 +1073,7 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
- 	}
- 
- 	*sta = wcid_to_sta(mstat.wcid);
--	*hw = mt76_main_hw(dev->phys[mstat.phy_idx]);
-+	*hw = mt76_phy_hw(dev, mstat.phy_idx);
- 
- 	if ((mstat.flag & RX_FLAG_8023) || ieee80211_is_data_qos(hdr->frame_control)) {
- 		struct mt76_phy *phy = mt76_dev_phy(dev, mstat.phy_idx);
-diff --git a/mt76.h b/mt76.h
-index 1d57e21..0452e5d 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -833,8 +833,8 @@ struct mt76_vif {
- 
- struct mt76_phy {
- 	struct ieee80211_hw *hw;
-+	struct ieee80211_hw *ori_hw;
- 	struct mt76_dev *dev;
--	struct mt76_phy *main_phy;
- 	void *priv;
- 
- 	unsigned long state;
-@@ -1326,15 +1326,6 @@ mt76_phy_hw(struct mt76_dev *dev, u8 phy_idx)
- 	return mt76_dev_phy(dev, phy_idx)->hw;
- }
- 
--static inline struct ieee80211_hw *
--mt76_main_hw(struct mt76_phy *phy)
--{
--	if (phy->main_phy)
--		return mt76_dev_phy(phy->dev, phy->main_phy->band_idx)->hw;
--
--	return mt76_dev_phy(phy->dev, phy->band_idx)->hw;
--}
--
- static inline u8 *
- mt76_get_txwi_ptr(struct mt76_dev *dev, struct mt76_txwi_cache *t)
- {
-diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 0393e93..51455d8 100644
---- a/mt7996/eeprom.c
-+++ b/mt7996/eeprom.c
-@@ -387,12 +387,6 @@ static int mt7996_eeprom_parse_band_config(struct mt7996_phy *phy)
- 		break;
- 	}
- 
--	/* TODO: for MLO, we enable all band capabilities */
--	phy->mt76->cap.has_2ghz = true;
--	phy->mt76->cap.has_5ghz = true;
--	if (is_mt7996(&phy->dev->mt76))
--		phy->mt76->cap.has_6ghz = true;
--
- 	return ret;
- }
- 
-diff --git a/mt7996/init.c b/mt7996/init.c
-index c6eb6a5..f374119 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -18,13 +18,13 @@ static const struct ieee80211_iface_limit if_limits[] = {
- 		.max = 1,
- 		.types = BIT(NL80211_IFTYPE_ADHOC)
- 	}, {
--		.max = 16,
-+		.max = 16 * 3,
- 		.types = BIT(NL80211_IFTYPE_AP)
- #ifdef CONFIG_MAC80211_MESH
- 			 | BIT(NL80211_IFTYPE_MESH_POINT)
- #endif
- 	}, {
--		.max = MT7996_MAX_INTERFACES,
-+		.max = MT7996_MAX_INTERFACES * 3,
- 		.types = BIT(NL80211_IFTYPE_STATION)
- 	}
- };
-@@ -33,7 +33,7 @@ static const struct ieee80211_iface_combination if_comb[] = {
- 	{
- 		.limits = if_limits,
- 		.n_limits = ARRAY_SIZE(if_limits),
--		.max_interfaces = MT7996_MAX_INTERFACES,
-+		.max_interfaces = MT7996_MAX_INTERFACES * 3,
- 		.num_different_channels = 3,
- 		.beacon_int_infra_match = true,
- 		/*
-@@ -795,6 +795,10 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
- 		mtk_wed_device_start(&dev->mt76.mmio.wed_hif2, MT_INT_TX_RX_DONE_EXT);
- 	}
- 
-+	/* TODO: FIXME: force to use single wiphy, need to rework init flow */
-+	phy->mt76->ori_hw = mphy->hw;
-+	mphy->hw = dev->phy.mt76->hw;
-+
- 	return 0;
- 
- error:
-@@ -811,6 +815,9 @@ mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
- 	if (!phy)
- 		return;
- 
-+	/* TODO: FIXME: temp for single wiphy support */
-+	phy->mt76->hw = phy->mt76->ori_hw;
-+
- 	mt7996_unregister_thermal(phy);
- 
- 	mphy = phy->dev->mt76.phys[band];
-@@ -1679,6 +1686,12 @@ int mt7996_register_device(struct mt7996_dev *dev)
- 	if (ret)
- 		return ret;
- 
-+	hw->wiphy->bands[NL80211_BAND_2GHZ] = &dev->phy.mt76->sband_2g.sband;
-+	if (mt7996_phy2(dev))
-+		hw->wiphy->bands[NL80211_BAND_5GHZ] = &mt7996_phy2(dev)->mt76->sband_5g.sband;
-+	if (mt7996_phy3(dev))
-+		hw->wiphy->bands[NL80211_BAND_6GHZ] = &mt7996_phy3(dev)->mt76->sband_6g.sband;
-+
- 	ieee80211_queue_work(mt76_hw(dev), &dev->init_work);
- 
- 	dev->recovery.hw_init_done = true;
-@@ -1708,11 +1721,11 @@ error:
- void mt7996_unregister_device(struct mt7996_dev *dev)
- {
- 	cancel_work_sync(&dev->wed_rro.work);
--	mt7996_unregister_phy(mt7996_phy3(dev), MT_BAND2);
--	mt7996_unregister_phy(mt7996_phy2(dev), MT_BAND1);
- 	mt7996_unregister_thermal(&dev->phy);
- 	mt7996_coredump_unregister(dev);
- 	mt76_unregister_device(&dev->mt76);
-+	mt7996_unregister_phy(mt7996_phy2(dev), MT_BAND1);
-+	mt7996_unregister_phy(mt7996_phy3(dev), MT_BAND2);
- 	mt7996_wed_rro_free(dev);
- 	mt7996_mcu_exit(dev);
- 	mt7996_tx_token_put(dev);
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 3141fe4..e6db176 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -2403,10 +2403,7 @@ void mt7996_mac_work(struct work_struct *work)
- 
- 	mt76_tx_status_check(mdev, false);
- 
--	if (mphy->main_phy && !test_bit(MT76_STATE_RUNNING, &mphy->main_phy->state))
--		return;
--
--	ieee80211_queue_delayed_work(mt76_main_hw(mphy), &mphy->mac_work,
-+	ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
- 				     MT7996_WATCHDOG_TIME);
- }
- 
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 2d0dc16..b0ac649 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -140,6 +140,10 @@ static int mt7996_start(struct ieee80211_hw *hw)
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	int ret;
- 
-+	/* only allow settings from hw0 */
-+	if (hw != dev->phy.mt76->hw)
-+		return -1;
-+
- 	flush_work(&dev->init_work);
- 
- 	mutex_lock(&dev->mt76.mutex);
-@@ -154,6 +158,10 @@ static void mt7996_stop(struct ieee80211_hw *hw)
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	int band;
- 
-+	/* only allow settings from hw0 */
-+	if (hw != dev->phy.mt76->hw)
-+		return;
-+
- 	cancel_delayed_work_sync(&dev->scs_work);
- 
- 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
-@@ -173,7 +181,6 @@ static void mt7996_stop(struct ieee80211_hw *hw)
- 		mutex_lock(&dev->mt76.mutex);
- 		mt7996_mcu_set_radio_en(phy, false);
- 		clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
--		phy->mt76->main_phy = NULL;
- 		mutex_unlock(&dev->mt76.mutex);
- 	}
- }
-@@ -446,8 +453,11 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
- 	mvif->dev = dev;
- 	mvif->hw = hw;
- 	mvif->sta.vif = mvif;
-+	/* TODO: temporaily set this to prevent some crashes */
-+	mvif->deflink.phy = phy;
- 
--	ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
-+	if (vif->type == NL80211_IFTYPE_STATION)
-+		ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
- 	mutex_unlock(&dev->mt76.mutex);
- 
- 	return ret;
-@@ -543,10 +553,9 @@ out:
- 	clear_bit(MT76_RESET, &phy->mt76->state);
- 	mutex_unlock(&dev->mt76.mutex);
- 
--	if (phy->mt76 == phy->mt76->main_phy)
--		mt76_txq_schedule_all(phy->mt76);
-+	mt76_txq_schedule_all(phy->mt76);
- 
--	ieee80211_queue_delayed_work(mt76_main_hw(phy->mt76),
-+	ieee80211_queue_delayed_work(phy->mt76->hw,
- 				     &phy->mt76->mac_work,
- 				     MT7996_WATCHDOG_TIME);
- 
-@@ -557,11 +566,11 @@ int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef
- {
- 	int ret;
- 
--	ieee80211_stop_queues(mt76_main_hw(phy->mt76));
-+	ieee80211_stop_queues(phy->mt76->hw);
- 	ret = __mt7996_set_channel(phy, chandef);
- 	if (ret)
- 		return ret;
--	ieee80211_wake_queues(mt76_main_hw(phy->mt76));
-+	ieee80211_wake_queues(phy->mt76->hw);
- 
- 	return 0;
- }
-@@ -769,9 +778,6 @@ static void mt7996_configure_filter(struct ieee80211_hw *hw,
- 			continue;
- 
- 		tmp = dev->mt76.phys[band]->priv;
--		if (tmp->mt76->main_phy != phy->mt76)
--			continue;
--
- 		tmp->rxfilter = phy->rxfilter;
- 		mt76_wr(dev, MT_WF_RFCR(tmp->mt76->band_idx), phy->rxfilter);
- 
-@@ -1576,9 +1582,11 @@ static int
- mt7996_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
- {
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
--	struct mt7996_phy *phy = mt7996_hw_phy(hw);
--	int max_nss = hweight8(hw->wiphy->available_antennas_tx);
--	u8 band_idx = phy->mt76->band_idx, shift = dev->chainshift[band_idx];
-+	int band, max_nss = hweight8(hw->wiphy->available_antennas_tx);
-+
-+	/* only allow settings from hw0 */
-+	if (hw != dev->phy.mt76->hw)
-+		return 0;
- 
- 	if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss)
- 		return -EINVAL;
-@@ -1588,20 +1596,34 @@ mt7996_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
--	phy->mt76->antenna_mask = tx_ant;
-+	for (band = 0; band < NUM_NL80211_BANDS; band++) {
-+		struct mt7996_phy *phy;
-+		u8 band_idx, shift;
-+
-+		if (!hw->wiphy->bands[band])
-+			continue;
- 
--	/* restore to the origin chainmask which might have auxiliary path */
--	if (hweight8(tx_ant) == max_nss && band_idx < MT_BAND2)
--		phy->mt76->chainmask = ((dev->chainmask >> shift) &
--					(BIT(dev->chainshift[band_idx + 1] - shift) - 1)) << shift;
--	else if (hweight8(tx_ant) == max_nss)
--		phy->mt76->chainmask = (dev->chainmask >> shift) << shift;
--	else
--		phy->mt76->chainmask = tx_ant << shift;
-+		phy = mt7996_band_phy(hw, band);
-+		if (!phy)
-+			continue;
- 
--	mt76_set_stream_caps(phy->mt76, true);
--	mt7996_set_stream_vht_txbf_caps(phy);
--	mt7996_set_stream_he_eht_caps(phy);
-+		phy->mt76->antenna_mask = tx_ant;
-+		band_idx = phy->mt76->band_idx;
-+		shift = dev->chainshift[band_idx];
-+
-+		/* restore to the origin chainmask which might have auxiliary path */
-+		if (hweight8(tx_ant) == max_nss && band_idx < MT_BAND2)
-+			phy->mt76->chainmask = ((dev->chainmask >> shift) &
-+						(BIT(dev->chainshift[band_idx + 1] - shift) - 1)) << shift;
-+		else if (hweight8(tx_ant) == max_nss)
-+			phy->mt76->chainmask = (dev->chainmask >> shift) << shift;
-+		else
-+			phy->mt76->chainmask = tx_ant << shift;
-+
-+		mt76_set_stream_caps(phy->mt76, true);
-+		mt7996_set_stream_vht_txbf_caps(phy);
-+		mt7996_set_stream_he_eht_caps(phy);
-+	}
- 
- 	mutex_unlock(&dev->mt76.mutex);
- 
-@@ -2243,7 +2265,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	phy->scan_chan_idx = 0;
- 	if (vif->type == NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
- 	    (phy->mt76 != mvif->deflink.phy->mt76)) {
--		phy->mt76->main_phy = hw->priv;
-+		// phy->mt76->main_phy = hw->priv;
- 		mt7996_remove_bss_conf(vif, &vif->bss_conf, &mvif->deflink);
- 
- 		ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
-@@ -2254,7 +2276,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	}
- 	mutex_unlock(&phy->dev->mt76.mutex);
- 
--	ieee80211_queue_delayed_work(mt76_main_hw(phy->mt76), &phy->scan_work, 0);
-+	ieee80211_queue_delayed_work(phy->mt76->hw, &phy->scan_work, 0);
- 
- 	return 0;
- }
-@@ -2262,7 +2284,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- static void
- mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
- {
--	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	// struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 	int band;
- 
- 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
-@@ -2272,17 +2294,16 @@ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
- 			continue;
- 
- 		phy = mt7996_band_phy(hw, band);
--		if (!(test_bit(MT76_SCANNING, &phy->mt76->state) &&
--		      phy->mt76->main_phy == hw->priv))
-+		if (!test_bit(MT76_SCANNING, &phy->mt76->state))
- 			continue;
- 
- 		cancel_delayed_work_sync(&phy->scan_work);
- 
- 		mutex_lock(&phy->dev->mt76.mutex);
- 		mt7996_scan_complete(phy, true);
--		if (vif->type == NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
--		    (phy->mt76 != mvif->deflink.phy->mt76))
--			phy->mt76->main_phy = NULL;
-+		// if (vif->type == NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
-+		//     (phy->mt76 != mvif->deflink.phy->mt76))
-+		// 	phy->mt76->main_phy = NULL;
- 		mutex_unlock(&phy->dev->mt76.mutex);
- 	}
- }
-@@ -2297,7 +2318,6 @@ mt7996_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
- 	wiphy_info(hw->wiphy, "%s: add %u\n", __func__, conf->def.chan->hw_value);
- 	mutex_lock(&phy->dev->mt76.mutex);
- 
--	phy->mt76->main_phy = hw->priv;
- 	if (ctx->assigned) {
- 		mutex_unlock(&phy->dev->mt76.mutex);
- 		return -ENOSPC;
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 39aa3ee..0444ae5 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -794,21 +794,19 @@ mt7996_get_background_radar_cap(struct mt7996_dev *dev)
- static inline struct mt7996_phy *
- mt7996_band_phy(struct ieee80211_hw *hw, enum nl80211_band band)
- {
--	struct mt76_phy *phy = hw->priv;
--
--	if (!(hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO))
--		return phy->priv;
-+	struct mt76_dev *dev = hw->priv;
-+	struct mt76_phy *phy;
- 
- 	/* TODO: mlo: temporarily hardcode */
- 	if (band == NL80211_BAND_6GHZ)
--		phy = phy->dev->phys[MT_BAND2];
-+		phy = dev->phys[MT_BAND2];
- 	else if (band == NL80211_BAND_5GHZ)
--		phy = phy->dev->phys[MT_BAND1];
-+		phy = dev->phys[MT_BAND1];
- 	else
--		phy = phy->dev->phys[MT_BAND0];
-+		phy = dev->phys[MT_BAND0];
- 
- 	if (!phy)
--		phy = hw->priv;
-+		return NULL;
- 
- 	return phy->priv;
- }
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0105-mtk-mt76-mt7996-handle-mapping-for-hw-and-vif.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0105-mtk-mt76-mt7996-handle-mapping-for-hw-and-vif.patch
new file mode 100644
index 0000000..8848e51
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0105-mtk-mt76-mt7996-handle-mapping-for-hw-and-vif.patch
@@ -0,0 +1,222 @@
+From 1172c92c354a6816dc112dae33ec473682f627a6 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 8 Dec 2023 18:08:13 +0800
+Subject: [PATCH 105/199] mtk: mt76: mt7996: handle mapping for hw and vif
+
+We have several temporal workarounds for ieee80211_hw and mt76_phy
+mappings. For legacy MBSS cases, we also need a method to do the
+hw and vif mappings.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ dma.c           |  2 +-
+ mac80211.c      |  2 +-
+ mt76.h          | 14 ++++++++++++--
+ mt7996/mac.c    | 24 +++++++++++++++++++++++-
+ mt7996/main.c   |  1 +
+ mt7996/mcu.c    |  4 ++--
+ mt7996/mmio.c   |  1 +
+ mt7996/mt7996.h |  3 +++
+ tx.c            |  4 ++--
+ 9 files changed, 46 insertions(+), 9 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index 38701c71..3f1fb6c2 100644
+--- a/dma.c
++++ b/dma.c
+@@ -685,7 +685,7 @@ free:
+ 
+ free_skb:
+ 	status.skb = tx_info.skb;
+-	hw = mt76_tx_status_get_hw(dev, tx_info.skb);
++	hw = mt76_tx_status_get_hw(dev, tx_info.skb, wcid);
+ 	spin_lock_bh(&dev->rx_lock);
+ 	ieee80211_tx_status_ext(hw, &status);
+ 	spin_unlock_bh(&dev->rx_lock);
+diff --git a/mac80211.c b/mac80211.c
+index bf7ead01..5f0c310f 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -1509,7 +1509,7 @@ void mt76_wcid_cleanup(struct mt76_dev *dev, struct mt76_wcid *wcid)
+ 	spin_unlock_bh(&phy->tx_lock);
+ 
+ 	while ((skb = __skb_dequeue(&list)) != NULL) {
+-		hw = mt76_tx_status_get_hw(dev, skb);
++		hw = mt76_tx_status_get_hw(dev, skb, wcid);
+ 		ieee80211_free_txskb(hw, skb);
+ 	}
+ }
+diff --git a/mt76.h b/mt76.h
+index f037284a..c51df8e5 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -554,6 +554,9 @@ struct mt76_driver_ops {
+ 
+ 	void (*sta_remove)(struct mt76_dev *dev, struct ieee80211_vif *vif,
+ 			   struct ieee80211_sta *sta);
++
++	void (*get_hw)(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 phy_idx,
++		       struct ieee80211_hw **hw);
+ };
+ 
+ struct mt76_channel_state {
+@@ -1588,14 +1591,21 @@ static inline void mt76_testmode_reset(struct mt76_phy *phy, bool disable)
+ 
+ /* internal */
+ static inline struct ieee80211_hw *
+-mt76_tx_status_get_hw(struct mt76_dev *dev, struct sk_buff *skb)
++mt76_tx_status_get_hw(struct mt76_dev *dev, struct sk_buff *skb,
++		      struct mt76_wcid *wcid)
+ {
+ 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ 	u8 phy_idx = (info->hw_queue & MT_TX_HW_QUEUE_PHY) >> 2;
+-	struct ieee80211_hw *hw = mt76_phy_hw(dev, phy_idx);
++	struct ieee80211_hw *hw;
+ 
+ 	info->hw_queue &= ~MT_TX_HW_QUEUE_PHY;
+ 
++	if (dev->drv->get_hw) {
++		dev->drv->get_hw(dev, wcid, phy_idx, &hw);
++	} else {
++		hw = mt76_phy_hw(dev, phy_idx);
++	}
++
+ 	return hw;
+ }
+ 
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index c0c0df2f..5417e741 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2845,13 +2845,23 @@ static void mt7996_scan_check_sta(void *data, struct ieee80211_sta *sta)
+ void mt7996_scan_work(struct work_struct *work)
+ {
+ 	struct mt7996_phy *phy = container_of(work, struct mt7996_phy, scan_work.work);
+-	struct ieee80211_hw *hw = phy->mt76->hw;
++	struct ieee80211_vif *vif = phy->scan_vif;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct ieee80211_hw *hw = mvif->hw;
+ 	struct cfg80211_scan_request *req = phy->scan_req;
+ 	struct cfg80211_chan_def chandef = {};
+ 	int duration;
+ 	bool has_sta = false, active_scan = false;
+ 
+ 	mutex_lock(&phy->dev->mt76.mutex);
++	/* don't let non-MLD AP scan other bands */
++	if (vif->type != NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
++	    phy != mt7996_hw_phy(hw)) {
++		mt7996_scan_complete(phy, false);
++		mutex_unlock(&phy->dev->mt76.mutex);
++		return;
++	}
++
+ 	if (phy->scan_chan_idx >= req->n_channels) {
+ 		mt7996_scan_complete(phy, false);
+ 		mutex_unlock(&phy->dev->mt76.mutex);
+@@ -2911,3 +2921,15 @@ void mt7996_scan_work(struct work_struct *work)
+ 
+ 	ieee80211_queue_delayed_work(hw, &phy->scan_work, duration);
+ }
++
++void mt7996_get_hw(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 phy_idx,
++		   struct ieee80211_hw **hw)
++{
++	struct mt7996_link_sta *mlink = wcid_to_mlink(wcid);
++
++	if (mlink) {
++		*hw = mlink->sta->vif->hw;
++	} else {
++		*hw = mt76_phy_hw(dev, phy_idx);
++	}
++}
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 331dd4d4..1251213e 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -444,6 +444,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ 		phy->monitor_vif = vif;
+ 
+ 	mvif->dev = dev;
++	mvif->hw = hw;
+ 	mvif->sta.vif = mvif;
+ 
+ 	ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index bf7231cd..8a04035e 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2948,11 +2948,11 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+ 
+ 	if (changed & BSS_CHANGED_FILS_DISCOVERY) {
+ 		interval = conf->fils_discovery.max_interval;
+-		skb = ieee80211_get_fils_discovery_tmpl(hw, vif);
++		skb = ieee80211_get_fils_discovery_tmpl(mvif->hw, vif);
+ 	} else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP &&
+ 		   conf->unsol_bcast_probe_resp_interval) {
+ 		interval = conf->unsol_bcast_probe_resp_interval;
+-		skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif);
++		skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(mvif->hw, vif);
+ 	}
+ 
+ 	if (!skb) {
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index b94155c4..58db5204 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -658,6 +658,7 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
+ 		.sta_assoc = mt7996_mac_sta_assoc,
+ 		.sta_remove = mt7996_mac_sta_remove,
+ 		.update_survey = mt7996_update_channel,
++		.get_hw = mt7996_get_hw,
+ 	};
+ 	struct mt7996_dev *dev;
+ 	struct mt76_dev *mdev;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index b7e623cb..3b24643a 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -354,6 +354,7 @@ struct mt7996_vif {
+ 
+ 	struct mt7996_sta sta;
+ 	struct mt7996_dev *dev;
++	struct ieee80211_hw *hw;
+ 
+ 	u8 master_link_id;
+ 	u8 group_mld_id;
+@@ -901,6 +902,8 @@ int mt7996_init_tx_queues(struct mt7996_phy *phy, int idx,
+ void mt7996_init_txpower(struct mt7996_phy *phy);
+ int mt7996_txbf_init(struct mt7996_dev *dev);
+ int mt7996_get_chip_sku(struct mt7996_dev *dev);
++void mt7996_get_hw(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 phy_idx,
++		   struct ieee80211_hw **hw);
+ void mt7996_reset(struct mt7996_dev *dev);
+ void mt7996_coredump(struct mt7996_dev *dev, u8 state);
+ int mt7996_run(struct ieee80211_hw *hw);
+diff --git a/tx.c b/tx.c
+index e2795067..6580833e 100644
+--- a/tx.c
++++ b/tx.c
+@@ -76,7 +76,7 @@ mt76_tx_status_unlock(struct mt76_dev *dev, struct sk_buff_head *list)
+ 			}
+ 		}
+ 
+-		hw = mt76_tx_status_get_hw(dev, skb);
++		hw = mt76_tx_status_get_hw(dev, skb, wcid);
+ 		spin_lock_bh(&dev->rx_lock);
+ 		ieee80211_tx_status_ext(hw, &status);
+ 		spin_unlock_bh(&dev->rx_lock);
+@@ -272,7 +272,7 @@ void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *
+ 	if (cb->pktid < MT_PACKET_ID_FIRST) {
+ 		struct ieee80211_rate_status rs = {};
+ 
+-		hw = mt76_tx_status_get_hw(dev, skb);
++		hw = mt76_tx_status_get_hw(dev, skb, wcid);
+ 		status.sta = wcid_to_sta(wcid);
+ 		if (status.sta && (wcid->rate.flags || wcid->rate.legacy)) {
+ 			rs.rate_idx = wcid->rate;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0105-wifi-mt76-mt7996-implement-ieee80211_ops-for-link-de.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0105-wifi-mt76-mt7996-implement-ieee80211_ops-for-link-de.patch
deleted file mode 100644
index 708a27e..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0105-wifi-mt76-mt7996-implement-ieee80211_ops-for-link-de.patch
+++ /dev/null
@@ -1,157 +0,0 @@
-From 79961bfcb2ebe3368d6d1ddf8b09cbf370c855a8 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Fri, 29 Dec 2023 18:37:41 +0800
-Subject: [PATCH 105/116] wifi: mt76: mt7996: implement ieee80211_ops for link
- debugfs
-
-Add .link_sta_add_debugfs and .link_add_debugfs.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/debugfs.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/mac.c     |  4 +--
- mt7996/main.c    |  2 ++
- mt7996/mcu.c     |  6 +++--
- mt7996/mt7996.h  |  5 ++++
- 5 files changed, 78 insertions(+), 4 deletions(-)
-
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index 56e2192..26927ed 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -1307,4 +1307,69 @@ void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	debugfs_create_file("hw-queues", 0400, dir, sta, &mt7996_queues_fops);
- }
- 
-+static int
-+mt7996_link_sta_info_show(struct seq_file *file, void *data)
-+{
-+	struct ieee80211_link_sta *link_sta = file->private;
-+	struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv;
-+	struct mt7996_link_sta *mlink;
-+	struct mt7996_dev *dev = msta->vif->dev;
-+	struct rate_info *r;
-+
-+	mutex_lock(&dev->mt76.mutex);
-+
-+	mlink = mlink_dereference_protected(msta, link_sta->link_id);
-+	r = &mlink->wcid.rate;
-+	seq_printf(file, "tx rate: flags=0x%x,legacy=%u,mcs=%u,nss=%u,bw=%u,he_gi=%u,he_dcm=%u,he_ru_alloc=%u,eht_gi=%u,eht_ru_alloc=%u\n",
-+		   r->flags, r->legacy, r->mcs, r->nss, r->bw, r->he_gi, r->he_dcm, r->he_ru_alloc, r->eht_gi, r->eht_ru_alloc);
-+	seq_printf(file, "tx_bytes=%llu\n", mlink->wcid.stats.tx_bytes);
-+	seq_printf(file, "rx_bytes=%llu\n", mlink->wcid.stats.rx_bytes);
-+	seq_printf(file, "tx_airtime=%llu\n", mlink->wcid.stats.tx_airtime);
-+	seq_printf(file, "rx_airtime=%llu\n", mlink->wcid.stats.rx_airtime);
-+
-+	mutex_unlock(&dev->mt76.mutex);
-+
-+	return 0;
-+}
-+DEFINE_SHOW_ATTRIBUTE(mt7996_link_sta_info);
-+
-+void mt7996_link_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-+				 struct ieee80211_link_sta *link_sta,
-+				 struct dentry *dir)
-+{
-+	debugfs_create_file("link_sta_info", 0600, dir, link_sta,
-+			    &mt7996_link_sta_info_fops);
-+}
-+
-+static int
-+mt7996_link_info_show(struct seq_file *file, void *data)
-+{
-+	struct ieee80211_bss_conf *conf = file->private;
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)conf->vif->drv_priv;
-+	struct mt7996_sta *msta = &mvif->sta;
-+	struct mt7996_bss_conf *mconf;
-+	struct mt7996_link_sta *mlink;
-+	struct mt7996_dev *dev = mvif->dev;
-+	struct rate_info *r;
-+
-+	mutex_lock(&dev->mt76.mutex);
-+
-+	mconf = mconf_dereference_protected(mvif, conf->link_id);
-+	mlink = mlink_dereference_protected(msta, conf->link_id);
-+	r = &mlink->wcid.rate;
-+	seq_printf(file, "band mapping=%u\n", mconf->phy->mt76->band_idx);
-+	seq_printf(file, "tx rate: flags=0x%x,legacy=%u,mcs=%u,nss=%u,bw=%u,he_gi=%u,he_dcm=%u,he_ru_alloc=%u,eht_gi=%u,eht_ru_alloc=%u\n",
-+		   r->flags, r->legacy, r->mcs, r->nss, r->bw, r->he_gi, r->he_dcm, r->he_ru_alloc, r->eht_gi, r->eht_ru_alloc);
-+
-+	mutex_unlock(&dev->mt76.mutex);
-+
-+	return 0;
-+}
-+DEFINE_SHOW_ATTRIBUTE(mt7996_link_info);
-+
-+void mt7996_link_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-+			     struct ieee80211_bss_conf *link_conf, struct dentry *dir)
-+{
-+	debugfs_create_file("link_info", 0600, dir, link_conf, &mt7996_link_info_fops);
-+}
- #endif
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index e6db176..5967b6a 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -2379,10 +2379,10 @@ void mt7996_mac_work(struct work_struct *work)
- 				mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_RATE);
- 				mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_AIRTIME);
- 				mt7996_mcu_get_rssi(mdev);
--				if (mtk_wed_device_active(&mdev->mmio.wed)) {
-+				// if (mtk_wed_device_active(&mdev->mmio.wed)) {
- 					mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_ADM_STAT);
- 					mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_MSDU_COUNT);
--				}
-+				// }
- 
- 				if (mt7996_mcu_wa_cmd(phy->dev, MCU_WA_PARAM_CMD(QUERY), MCU_WA_PARAM_BSS_ACQ_PKT_CNT,
- 				                      BSS_ACQ_PKT_CNT_BSS_BITMAP_ALL | BSS_ACQ_PKT_CNT_READ_CLR, 0))
-diff --git a/mt7996/main.c b/mt7996/main.c
-index b0ac649..3e70d86 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -2602,6 +2602,8 @@ const struct ieee80211_ops mt7996_ops = {
- 	CFG80211_TESTMODE_DUMP(mt76_testmode_dump)
- #ifdef CONFIG_MAC80211_DEBUGFS
- 	.sta_add_debugfs = mt7996_sta_add_debugfs,
-+	.link_sta_add_debugfs = mt7996_link_sta_add_debugfs,
-+	// .link_add_debugfs = mt7996_link_add_debugfs,
- #endif
- 	.set_radar_background = mt7996_set_radar_background,
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 025a00c..1387a52 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -616,8 +616,10 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- 			wcid->stats.tx_packets += tx_packets;
- 			wcid->stats.rx_packets += rx_packets;
- 
--			__mt7996_stat_to_netdev(mphy, wcid, 0, 0,
--						tx_packets, rx_packets);
-+			if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
-+				__mt7996_stat_to_netdev(mphy, wcid, 0, 0,
-+							tx_packets, rx_packets);
-+			}
- 			break;
- 		case UNI_ALL_STA_TXRX_AIRTIME:
- 			wlan_idx = le16_to_cpu(res->airtime[i].wlan_idx);
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 0444ae5..dc3cacc 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -1138,6 +1138,11 @@ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap);
- #ifdef CONFIG_MAC80211_DEBUGFS
- void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 			    struct ieee80211_sta *sta, struct dentry *dir);
-+void mt7996_link_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-+				 struct ieee80211_link_sta *link_sta,
-+				 struct dentry *dir);
-+void mt7996_link_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-+			     struct ieee80211_bss_conf *link_conf, struct dentry *dir);
- #endif
- int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
- 			 bool hif2, int *irq);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0106-mtk-mt76-mt7996-rework-scanning-parts-for-MLD-STA-su.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0106-mtk-mt76-mt7996-rework-scanning-parts-for-MLD-STA-su.patch
new file mode 100644
index 0000000..99899b8
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0106-mtk-mt76-mt7996-rework-scanning-parts-for-MLD-STA-su.patch
@@ -0,0 +1,184 @@
+From b05defbe38b85f93a74c115830c06d975c8cb67e Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 13 Dec 2023 16:58:31 +0800
+Subject: [PATCH 106/199] mtk: mt76: mt7996: rework scanning parts for MLD STA
+ support
+
+During the first scanning, the STA VIF is still a legacy interface,
+and at the moment it doesn't know if it's MLD or not. So do dome tricks
+here to let the legacy interface be abled to scan all bands.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mac.c  | 44 +++++++++++++++++++++++---------------------
+ mt7996/main.c | 35 ++++++++++++++++++++++++++++++++++-
+ 2 files changed, 57 insertions(+), 22 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 5417e741..36d2e0b3 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2769,23 +2769,40 @@ static void
+ mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
+ 		       const u8 *dst)
+ {
+-	struct ieee80211_hw *hw = phy->mt76->hw;
+ 	struct cfg80211_scan_request *req = phy->scan_req;
+ 	struct ieee80211_vif *vif = phy->scan_vif;
++	struct ieee80211_bss_conf *conf;
+ 	struct mt7996_vif *mvif;
+ 	struct mt7996_link_sta *mlink;
+ 	struct ieee80211_tx_info *info;
++	struct ieee80211_hw *hw;
+ 	struct sk_buff *skb;
++	unsigned long valid_links;
++	unsigned int link_id;
+ 
+ 	if (!req || !vif)
+ 		return;
+ 
++	valid_links = vif->valid_links ?: BIT(0);
+ 	mvif = (struct mt7996_vif *)vif->drv_priv;
++	hw = mvif->hw;
++
++	rcu_read_lock();
++
++	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++		conf = rcu_dereference(vif->link_conf[link_id]);
++		mlink = rcu_dereference(mvif->sta.link[link_id]);
++		if (mlink->wcid.phy_idx != phy->mt76->band_idx)
++			continue;
++	}
++
++	if (unlikely(!conf))
++		goto unlock;
+ 
+-	skb = ieee80211_probereq_get(hw, vif->addr,
++	skb = ieee80211_probereq_get(hw, conf->addr,
+ 				     ssid->ssid, ssid->ssid_len, req->ie_len);
+ 	if (!skb)
+-		return;
++		goto unlock;
+ 
+ 	if (is_unicast_ether_addr(dst)) {
+ 		struct ieee80211_hdr_3addr *hdr =
+@@ -2803,36 +2820,21 @@ mt7996_scan_send_probe(struct mt7996_phy *phy, struct cfg80211_ssid *ssid,
+ 
+ 	skb_set_queue_mapping(skb, IEEE80211_AC_VO);
+ 
+-	rcu_read_lock();
+-	if (!ieee80211_tx_prepare_skb(mt76_main_hw(phy->mt76), vif, skb,
+-				      phy->scan_chan->band,
+-				      NULL)) {
++	if (!ieee80211_tx_prepare_skb(hw, vif, skb,
++				      phy->scan_chan->band, NULL)) {
+ 		rcu_read_unlock();
+ 		ieee80211_free_txskb(hw, skb);
+ 		return;
+ 	}
+ 
+ 	local_bh_disable();
+-	mlink = rcu_dereference(mvif->sta.link[0]);
+ 	mt76_tx(phy->mt76, NULL, &mlink->wcid, skb);
+ 	local_bh_enable();
+ 
++unlock:
+ 	rcu_read_unlock();
+ }
+ 
+-void mt7996_scan_complete(struct mt7996_phy *phy, bool aborted)
+-{
+-	struct cfg80211_scan_info info = {
+-		.aborted = aborted,
+-	};
+-
+-	ieee80211_scan_completed(phy->mt76->hw, &info);
+-	phy->scan_chan = NULL;
+-	phy->scan_req = NULL;
+-	phy->scan_vif = NULL;
+-	clear_bit(MT76_SCANNING, &phy->mt76->state);
+-}
+-
+ static void mt7996_scan_check_sta(void *data, struct ieee80211_sta *sta)
+ {
+ 	bool *has_sta = data;
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 1251213e..9c5e31b7 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -2189,12 +2189,28 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ 
+ #endif
+ 
++void mt7996_scan_complete(struct mt7996_phy *phy, bool aborted)
++{
++	struct mt7996_vif *mvif = (struct mt7996_vif *)phy->scan_vif->drv_priv;
++	struct cfg80211_scan_info info = {
++		.aborted = aborted,
++	};
++
++	ieee80211_scan_completed(mvif->hw, &info);
++	phy->scan_chan = NULL;
++	phy->scan_req = NULL;
++	phy->scan_vif = NULL;
++	clear_bit(MT76_SCANNING, &phy->mt76->state);
++}
++
+ static int
+ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	       struct ieee80211_scan_request *hw_req)
+ {
+ 	struct cfg80211_scan_request *req = &hw_req->req;
+ 	struct mt7996_phy *phy = mt7996_band_phy(hw, req->channels[0]->band);
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	int ret;
+ 
+ 	mutex_lock(&phy->dev->mt76.mutex);
+ 	if (WARN_ON(phy->scan_req || phy->scan_chan)) {
+@@ -2202,10 +2218,23 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 		return -EBUSY;
+ 	}
+ 
+-	set_bit(MT76_SCANNING, &phy->mt76->state);
+ 	phy->scan_req = req;
+ 	phy->scan_vif = vif;
+ 	phy->scan_chan_idx = 0;
++
++	if (vif->type == NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
++	    (phy->mt76 != mvif->deflink.phy->mt76)) {
++		phy->mt76->main_phy = hw->priv;
++		mt7996_remove_bss_conf(vif, &vif->bss_conf, &mvif->deflink);
++
++		ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
++		if (ret) {
++			mutex_unlock(&phy->dev->mt76.mutex);
++			return ret;
++		}
++	}
++
++	set_bit(MT76_SCANNING, &phy->mt76->state);
+ 	mutex_unlock(&phy->dev->mt76.mutex);
+ 
+ 	ieee80211_queue_delayed_work(mt76_main_hw(phy->mt76), &phy->scan_work, 0);
+@@ -2216,6 +2245,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ static void
+ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ {
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	int band;
+ 
+ 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
+@@ -2233,6 +2263,9 @@ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ 
+ 		mutex_lock(&phy->dev->mt76.mutex);
+ 		mt7996_scan_complete(phy, true);
++		if (vif->type == NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
++		    (phy->mt76 != mvif->deflink.phy->mt76))
++			phy->mt76->main_phy = NULL;
+ 		mutex_unlock(&phy->dev->mt76.mutex);
+ 	}
+ }
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0106-mtk-wifi-mt76-mt7996-support-multi-link-channel-swit.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0106-mtk-wifi-mt76-mt7996-support-multi-link-channel-swit.patch
deleted file mode 100644
index c57d3c8..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0106-mtk-wifi-mt76-mt7996-support-multi-link-channel-swit.patch
+++ /dev/null
@@ -1,169 +0,0 @@
-From 4969672ddd07d512af13af7622a8c941ddc21d53 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 19 Jan 2024 14:04:03 +0800
-Subject: [PATCH 106/116] mtk: wifi: mt76: mt7996: support multi-link channel
- switch
-
-mtk: wifi: mt76: mt7996: remove the limitation of radar detect width for mlo
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-mtk: wifi: mt76: mt7996: start and finalize channel switch on link basis
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-mtk: wifi: mt76: mt7996: fix DFS RDD init issue
-
-1. Add radar enabled flag in mt76_phy since hw->conf.radar_enabled
-is only used for non-chanctx driver.
-2. Add IEEE80211_CHANCTX_CHANGE_RADAR flag in change_chanctx for RDD
-DFS state update.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-mtk: wifi: mt76: mt7996: fix background radar using wrong phy for mld ap
-
-mt7996_hw_phy will be phy0 for 3 link mld ap
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mac80211.c    |  2 +-
- mt76.h        |  1 +
- mt7996/init.c |  2 --
- mt7996/main.c | 12 +++++++++---
- mt7996/mcu.c  | 14 +++++++++++---
- 5 files changed, 22 insertions(+), 9 deletions(-)
-
-diff --git a/mac80211.c b/mac80211.c
-index 8b9f3fd..c2b82fb 100644
---- a/mac80211.c
-+++ b/mac80211.c
-@@ -1798,7 +1798,7 @@ enum mt76_dfs_state mt76_phy_dfs_state(struct mt76_phy *phy)
- 	    test_bit(MT76_SCANNING, &phy->state))
- 		return MT_DFS_STATE_DISABLED;
- 
--	if (!hw->conf.radar_enabled) {
-+	if (!phy->radar_enabled) {
- 		if ((hw->conf.flags & IEEE80211_CONF_MONITOR) &&
- 		    (phy->chandef.chan->flags & IEEE80211_CHAN_RADAR))
- 			return MT_DFS_STATE_ACTIVE;
-diff --git a/mt76.h b/mt76.h
-index 0452e5d..d88d552 100644
---- a/mt76.h
-+++ b/mt76.h
-@@ -849,6 +849,7 @@ struct mt76_phy {
- 
- 	struct mt76_channel_state *chan_state;
- 	enum mt76_dfs_state dfs_state;
-+	bool radar_enabled;
- 	ktime_t survey_time;
- 
- 	u32 aggr_stats[32];
-diff --git a/mt7996/init.c b/mt7996/init.c
-index f374119..0dee659 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -36,13 +36,11 @@ static const struct ieee80211_iface_combination if_comb[] = {
- 		.max_interfaces = MT7996_MAX_INTERFACES * 3,
- 		.num_different_channels = 3,
- 		.beacon_int_infra_match = true,
--		/*
- 		.radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
- 				       BIT(NL80211_CHAN_WIDTH_20) |
- 				       BIT(NL80211_CHAN_WIDTH_40) |
- 				       BIT(NL80211_CHAN_WIDTH_80) |
- 				       BIT(NL80211_CHAN_WIDTH_160),
--		*/
- 	}
- };
- 
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 3e70d86..8e44da7 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -2133,7 +2133,7 @@ static int
- mt7996_set_radar_background(struct ieee80211_hw *hw,
- 			    struct cfg80211_chan_def *chandef)
- {
--	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct mt7996_phy *phy = mt7996_band_phy(hw, NL80211_BAND_5GHZ);
- 	struct mt7996_dev *dev = phy->dev;
- 	int ret = -EINVAL;
- 	bool running;
-@@ -2332,6 +2332,7 @@ mt7996_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
- 	}
- 
- 	phy->chanctx = ctx;
-+	phy->mt76->radar_enabled = conf->radar_enabled;
- 	mutex_unlock(&phy->dev->mt76.mutex);
- 
- 	if (!mt76_testmode_enabled(phy->mt76) && !phy->mt76->test.bf_en) {
-@@ -2359,8 +2360,10 @@ mt7996_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *co
- 
- 	mutex_lock(&phy->dev->mt76.mutex);
- 	ctx->assigned = false;
--	if (ctx == phy->chanctx)
-+	if (ctx == phy->chanctx) {
- 		phy->chanctx = NULL;
-+		phy->mt76->radar_enabled = false;
-+	}
- 	mutex_unlock(&phy->dev->mt76.mutex);
- }
- 
-@@ -2372,8 +2375,10 @@ mt7996_change_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *co
- 	struct mt7996_phy *phy = ctx->phy;
- 
- 	wiphy_info(hw->wiphy, "%s: change %u, 0x%x\n", __func__, conf->def.chan->hw_value, changed);
--	if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH) {
-+	if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH ||
-+	    changed & IEEE80211_CHANCTX_CHANGE_RADAR) {
- 		ctx->chandef = conf->def;
-+		phy->mt76->radar_enabled = conf->radar_enabled;
- 
- 		mt7996_set_channel(phy, &ctx->chandef);
- 	}
-@@ -2471,6 +2476,7 @@ mt7996_switch_vif_chanctx(struct ieee80211_hw *hw,
- 	mutex_lock(&phy->dev->mt76.mutex);
- 
- 	phy->chanctx = new_ctx;
-+	phy->mt76->radar_enabled = vifs->new_ctx->radar_enabled;
- 	new_ctx->assigned = true;
- 	new_ctx->chandef = vifs->new_ctx->def;
- 	new_ctx->phy = phy;
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 1387a52..e18a553 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -357,10 +357,18 @@ int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3)
- static void
- mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
- {
--	if (!vif->bss_conf.csa_active || vif->type == NL80211_IFTYPE_STATION)
-+	struct mt76_phy *mphy = (struct mt76_phy *)priv;
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct ieee80211_bss_conf *link_conf;
-+	int link_id, band_idx = mphy->band_idx;
-+
-+	link_id = mvif->band_to_link[band_idx];
-+	link_conf = rcu_dereference(vif->link_conf[link_id]);
-+
-+	if (!link_conf || !link_conf->csa_active || vif->type == NL80211_IFTYPE_STATION)
- 		return;
- 
--	ieee80211_csa_finish(vif, 0);
-+	ieee80211_csa_finish(vif, link_id);
- }
- 
- static void
-@@ -475,7 +483,7 @@ mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb)
- 		case UNI_EVENT_IE_COUNTDOWN_CSA:
- 			ieee80211_iterate_active_interfaces_atomic(mphy->hw,
- 					IEEE80211_IFACE_ITER_RESUME_ALL,
--					mt7996_mcu_csa_finish, mphy->hw);
-+					mt7996_mcu_csa_finish, mphy);
- 			break;
- 		case UNI_EVENT_IE_COUNTDOWN_BCC:
- 			ieee80211_iterate_active_interfaces_atomic(mphy->hw,
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0107-mtk-mt76-mt7996-hw_scan-ACS-channel-time-too-long-on.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0107-mtk-mt76-mt7996-hw_scan-ACS-channel-time-too-long-on.patch
deleted file mode 100644
index 54f4647..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0107-mtk-mt76-mt7996-hw_scan-ACS-channel-time-too-long-on.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-From 919a124e164b3c1ba5fe4fde2ad48e0ef3961d17 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Thu, 22 Feb 2024 11:09:10 +0800
-Subject: [PATCH 107/116] mtk: mt76: mt7996: hw_scan: ACS channel time too long
- on duty channel
-
-This problem happens in SW scan and was already fixed.
-(https://gerrit.mediatek.inc/c/gateway/WiFi7/mac80211/mt76/+/8312969)
-
-This commit applys same solution for HW scan.
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- mt7996/main.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 8e44da7..233bf2a 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -497,6 +497,7 @@ static void ___mt7996_set_channel(struct mt7996_phy *phy,
- 	struct mt76_phy *mphy = phy->mt76;
- 	bool offchannel = phy->scan_chan != NULL;
- 	int timeout = HZ / 5;
-+	unsigned long was_scanning = ieee80211_get_scanning(mphy->hw);
- 
- 	wait_event_timeout(mdev->tx_wait, !mt76_has_tx_pending(mphy), timeout);
- 	mt76_update_survey(mphy);
-@@ -511,7 +512,7 @@ static void ___mt7996_set_channel(struct mt7996_phy *phy,
- 	if (!offchannel)
- 		mphy->main_chan = chandef->chan;
- 
--	if (chandef->chan != mphy->main_chan)
-+	if (chandef->chan != mphy->main_chan || was_scanning)
- 		memset(mphy->chan_state, 0, sizeof(*mphy->chan_state));
- }
- 
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0107-mtk-mt76-mt7996-implement-mld-address-translation.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0107-mtk-mt76-mt7996-implement-mld-address-translation.patch
new file mode 100644
index 0000000..0062ff5
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0107-mtk-mt76-mt7996-implement-mld-address-translation.patch
@@ -0,0 +1,125 @@
+From b864635477ddfa6095f9268c94e4ab9410c51f43 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 7 Dec 2023 16:31:56 +0800
+Subject: [PATCH 107/199] mtk: mt76: mt7996: implement mld address translation
+
+Do the MLD to link address translation for EAPOL and management frames
+in driver.
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Co-developed-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mac.c  | 20 ++++++++++++++++++++
+ mt7996/main.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++---
+ 2 files changed, 66 insertions(+), 3 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 36d2e0b3..a306ad71 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -898,6 +898,26 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 		mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, key,
+ 				      pid, qid, 0);
+ 
++	/* translate addr3 of EAPOL by driver */
++	if (unlikely(tx_info->skb->protocol == cpu_to_be16(ETH_P_PAE)) && sta->mlo) {
++		if (ether_addr_equal(vif->addr, hdr->addr3)) {
++			struct ieee80211_bss_conf *conf;
++
++			conf = rcu_dereference(vif->link_conf[wcid->link_id]);
++			if (unlikely(!conf))
++				return -ENOLINK;
++
++			memcpy(hdr->addr3, conf->addr, ETH_ALEN);
++		} else if (ether_addr_equal(sta->addr, hdr->addr3)) {
++			struct ieee80211_link_sta *link_sta;
++
++			link_sta = rcu_dereference(sta->link[wcid->link_id]);
++			memcpy(hdr->addr3, link_sta->addr, ETH_ALEN);
++		}
++
++		pr_info("EAPOL: a1=%pM, a2=%pM, a3=%pM\n", hdr->addr1, hdr->addr2, hdr->addr3);
++	}
++
+ 	txp = (struct mt76_connac_txp_common *)(txwi + MT_TXD_SIZE);
+ 	for (i = 0; i < nbuf; i++) {
+ 		u16 len;
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 9c5e31b7..d897a361 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1312,14 +1312,56 @@ static void mt7996_tx(struct ieee80211_hw *hw,
+ 
+ 	rcu_read_lock();
+ 	if (mvif && msta) {
++		struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ 		struct mt7996_bss_conf *mconf;
+ 		struct mt7996_link_sta *mlink;
+-
+ 		u8 link_id = u32_get_bits(info->control.flags,
+ 					  IEEE80211_TX_CTRL_MLO_LINK);
++		struct ieee80211_sta *sta = ieee80211_find_sta(vif, hdr->addr1);
++
++		if (link_id >= IEEE80211_LINK_UNSPECIFIED) {
++			if (sta) {
++				struct mt7996_sta *peer;
++
++				peer = (struct mt7996_sta *)sta->drv_priv;
++				link_id = peer->pri_link;
++			} else {
++				link_id = mvif->master_link_id;
++			}
++		}
+ 
+-		if (link_id >= IEEE80211_LINK_UNSPECIFIED)
+-			link_id = mvif->master_link_id;
++		/* translate mld addr to link addr */
++		if (ieee80211_vif_is_mld(vif)) {
++			struct ieee80211_bss_conf *conf;
++			if (sta) {
++				struct ieee80211_link_sta *link_sta =
++					rcu_dereference(sta->link[link_id]);
++
++				if (!link_sta) {
++					mlo_dbg(mt7996_hw_phy(mvif->hw), "request TX on invalid link_id=%u, use primary link (id=%u) instead.\n",
++						      link_id, msta->pri_link);
++					link_id = msta->pri_link;
++					link_sta = rcu_dereference(sta->link[link_id]);
++
++					if (!link_sta) {
++						mlo_dbg(mt7996_hw_phy(mvif->hw), "primary link became invalid, give up the TX\n");
++						goto unlock;
++					}
++				}
++
++				memcpy(hdr->addr1, link_sta->addr, ETH_ALEN);
++				if (ether_addr_equal(sta->addr, hdr->addr3))
++					memcpy(hdr->addr3, link_sta->addr, ETH_ALEN);
++			}
++
++			conf = rcu_dereference(vif->link_conf[link_id]);
++			if (unlikely(!conf))
++				goto unlock;
++
++			memcpy(hdr->addr2, conf->addr, ETH_ALEN);
++			if (ether_addr_equal(vif->addr, hdr->addr3))
++				memcpy(hdr->addr3, conf->addr, ETH_ALEN);
++		}
+ 
+ 		mconf = rcu_dereference(mvif->link[link_id]);
+ 		mlink = rcu_dereference(msta->link[link_id]);
+@@ -1337,6 +1379,7 @@ static void mt7996_tx(struct ieee80211_hw *hw,
+ 	}
+ 
+ 	mt76_tx(mphy, control->sta, wcid, skb);
++unlock:
+ 	rcu_read_unlock();
+ }
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0108-mtk-mt76-mt7996-use-BSS_CHANGED_TXPOWER-for-txpower-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0108-mtk-mt76-mt7996-use-BSS_CHANGED_TXPOWER-for-txpower-.patch
new file mode 100644
index 0000000..624ea79
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0108-mtk-mt76-mt7996-use-BSS_CHANGED_TXPOWER-for-txpower-.patch
@@ -0,0 +1,106 @@
+From f0996fb23e5b67edf0e25d31c90f938b7c9759c6 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 27 Feb 2024 18:07:11 +0800
+Subject: [PATCH 108/199] mtk: mt76: mt7996: use BSS_CHANGED_TXPOWER for
+ txpower setting
+
+This is a preliminary patch to add MLO support for mt7996 chipsets.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/main.c     | 12 +++---------
+ mt7996/mcu.c      |  5 +++--
+ mt7996/mt7996.h   |  3 ++-
+ mt7996/testmode.c |  2 +-
+ 4 files changed, 9 insertions(+), 13 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index d897a361..fd0df974 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -665,14 +665,6 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
+ {
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+-	int ret;
+-
+-	if (changed & (IEEE80211_CONF_CHANGE_POWER |
+-		       IEEE80211_CONF_CHANGE_CHANNEL)) {
+-		ret = mt7996_mcu_set_txpower_sku(phy);
+-		if (ret)
+-			return ret;
+-	}
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+@@ -972,6 +964,9 @@ static void mt7996_link_info_changed(struct ieee80211_hw *hw,
+ 	if (changed & BSS_CHANGED_MU_GROUPS)
+ 		mt7996_update_mu_group(hw, info, mconf);
+ 
++	if (changed & BSS_CHANGED_TXPOWER)
++		mt7996_mcu_set_txpower_sku(phy, info);
++
+ out:
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+@@ -1611,7 +1606,6 @@ mt7996_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
+ 	mt76_set_stream_caps(phy->mt76, true);
+ 	mt7996_set_stream_vht_txbf_caps(phy);
+ 	mt7996_set_stream_he_eht_caps(phy);
+-	mt7996_mcu_set_txpower_sku(phy);
+ 
+ 	mutex_unlock(&dev->mt76.mutex);
+ 
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 8a04035e..bf128637 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -5310,7 +5310,8 @@ mt7996_update_max_txpower_cur(struct mt7996_phy *phy, int tx_power)
+ 		mphy->txpower_cur = e2p_power_limit;
+ }
+ 
+-int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
++int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
++			       struct ieee80211_bss_conf *conf)
+ {
+ #define TX_POWER_LIMIT_TABLE_RATE	0
+ #define TX_POWER_LIMIT_TABLE_PATH	1
+@@ -5339,7 +5340,7 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
+ 
+ 	if (hw->conf.power_level == INT_MIN)
+ 		hw->conf.power_level = 127;
+-	txpower_limit = mt7996_get_power_bound(phy, hw->conf.power_level);
++	txpower_limit = mt7996_get_power_bound(phy, conf->txpower);
+ 
+ 	if (phy->sku_limit_en) {
+ 		txpower_limit = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 3b24643a..fbb85177 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -980,7 +980,8 @@ int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch);
+ int mt7996_mcu_get_temperature(struct mt7996_phy *phy);
+ int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state);
+ int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable);
+-int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy);
++int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
++			       struct ieee80211_bss_conf *conf);
+ int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
+ 		       u8 rx_sel, u8 val);
+ int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev,
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index ba17f947..0565ebc9 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -1830,7 +1830,7 @@ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
+ 		mt7996_tm_update_channel(phy);
+ 		mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(SKU_POWER_LIMIT), td->sku_en);
+ 		mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(BACKOFF_POWER_LIMIT), td->sku_en);
+-		mt7996_mcu_set_txpower_sku(phy);
++		mt7996_mcu_set_txpower_sku(phy, &phy->monitor_vif->bss_conf);
+ 	}
+ 	if (changed & BIT(TM_CHANGED_TX_LENGTH)) {
+ 		mt7996_tm_set(dev, SET_ID(TX_LEN), td->tx_mpdu_len);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0108-wifi-mt76-mt7996-add-beacon-monitoring-in-driver-for.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0108-wifi-mt76-mt7996-add-beacon-monitoring-in-driver-for.patch
deleted file mode 100644
index cf2d487..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0108-wifi-mt76-mt7996-add-beacon-monitoring-in-driver-for.patch
+++ /dev/null
@@ -1,201 +0,0 @@
-From e7f7d5d9d5d110e7550ee2cf6fb1e6bcf6455976 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Tue, 20 Feb 2024 10:08:01 +0800
-Subject: [PATCH 108/116] wifi: mt76: mt7996: add beacon monitoring in driver
- for mlo
-
-Add beacon monitoring in driver since mac80211 does not
-support connect monitoring if WIPHY_FLAG_SUPPORTS_MLO is set.
-(IEEE80211_HW_CONNECTION_MONITOR should be set)
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/mac.c    | 56 +++++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/main.c   | 50 +++++++++++++++++++++++++++++++++++++++++++
- mt7996/mt7996.h |  7 +++++++
- 3 files changed, 113 insertions(+)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 5967b6a..2a45fc0 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -565,6 +565,21 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
- 			 */
- 			if (ieee80211_has_a4(fc) && is_mesh && status->amsdu)
- 				*qos &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
-+		} else if (ieee80211_is_beacon(fc)) {
-+			struct ieee80211_hw *hw = phy->mt76->hw;
-+			struct ieee80211_sta *sta;
-+			struct mt7996_sta *msta;
-+			unsigned int link_id;
-+
-+			sta = ieee80211_find_sta_by_link_addrs(hw, hdr->addr2, NULL, &link_id);
-+			if (!sta)
-+				sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr2, NULL);
-+
-+			if (sta) {
-+				msta = (struct mt7996_sta *)sta->drv_priv;
-+				if (msta && msta->vif)
-+					msta->vif->beacon_received_time[band_idx] = jiffies;
-+			}
- 		}
- #ifdef CONFIG_MTK_VENDOR
- 		if (phy->amnt_ctrl.enable && !ieee80211_is_beacon(fc))
-@@ -2956,6 +2971,47 @@ void mt7996_scan_work(struct work_struct *work)
- 	ieee80211_queue_delayed_work(hw, &phy->scan_work, duration);
- }
- 
-+void mt7996_beacon_mon_work(struct work_struct *work)
-+{
-+	struct mt7996_vif *mvif = container_of(work, struct mt7996_vif, beacon_mon_work.work);
-+	struct ieee80211_vif *vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv);
-+	struct ieee80211_hw *hw = mvif->hw;
-+	struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+	unsigned long next_time = ULONG_MAX, valid_links = vif->valid_links ?: BIT(0);
-+	unsigned int link_id;
-+
-+	mutex_lock(&dev->mt76.mutex);
-+
-+	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
-+		struct ieee80211_bss_conf *conf;
-+		struct mt7996_bss_conf *mconf;
-+		struct mt7996_phy *phy;
-+		unsigned long timeout, loss_duration;
-+
-+		conf = link_conf_dereference_protected(vif, link_id);
-+		mconf = mconf_dereference_protected(mvif, link_id);
-+		if (!conf || !mconf)
-+			continue;
-+
-+		phy = mconf->phy;
-+		loss_duration = msecs_to_jiffies(MT7996_MAX_BEACON_LOSS * conf->beacon_int);
-+		timeout = mvif->beacon_received_time[phy->mt76->band_idx] + loss_duration;
-+		if (time_after_eq(jiffies, timeout)) {
-+			mutex_unlock(&dev->mt76.mutex);
-+			wiphy_info(hw->wiphy,
-+				   "link %d: detected beacon loss, start disconnecting\n",
-+				   link_id);
-+			/* TODO: disconnect single link & handle link reconfiguration for MLD */
-+			ieee80211_connection_loss(vif);
-+			return;
-+		}
-+		next_time = min(next_time, timeout - jiffies);
-+	}
-+	mutex_unlock(&dev->mt76.mutex);
-+
-+	ieee80211_queue_delayed_work(hw, &mvif->beacon_mon_work, next_time);
-+}
-+
- void mt7996_get_hw(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 phy_idx,
- 		   struct ieee80211_hw **hw)
- {
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 233bf2a..d1f2c25 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -450,6 +450,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
- 	    is_zero_ether_addr(vif->addr))
- 		phy->monitor_vif = vif;
- 
-+	INIT_DELAYED_WORK(&mvif->beacon_mon_work, mt7996_beacon_mon_work);
- 	mvif->dev = dev;
- 	mvif->hw = hw;
- 	mvif->sta.vif = mvif;
-@@ -2565,6 +2566,54 @@ out:
- 	return ret;
- }
- 
-+static void
-+mt7996_event_callback(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-+		      const struct ieee80211_event *event)
-+{
-+	struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+
-+	mutex_lock(&dev->mt76.mutex);
-+
-+	switch (event->type) {
-+	case MLME_EVENT:
-+		if (event->u.mlme.data == ASSOC_EVENT &&
-+		    event->u.mlme.status == MLME_SUCCESS) {
-+			struct ieee80211_bss_conf *conf;
-+			struct mt7996_bss_conf *mconf;
-+			struct mt7996_phy *phy;
-+			unsigned long cur, valid_links = vif->valid_links ?: BIT(0);
-+			unsigned int link_id;
-+			int next_time = INT_MAX;
-+
-+			cur = jiffies;
-+			for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
-+				conf = link_conf_dereference_protected(vif, link_id);
-+				mconf = mconf_dereference_protected(mvif, link_id);
-+				if (conf && mconf) {
-+					phy = mconf->phy;
-+					mvif->beacon_received_time[phy->mt76->band_idx] = cur;
-+					next_time = min(next_time,
-+							MT7996_MAX_BEACON_LOSS *
-+							conf->beacon_int);
-+				}
-+			}
-+
-+			ieee80211_queue_delayed_work(hw, &mvif->beacon_mon_work,
-+						     msecs_to_jiffies(next_time));
-+			break;
-+		}
-+
-+		cancel_delayed_work_sync(&mvif->beacon_mon_work);
-+		break;
-+	default:
-+		break;
-+	}
-+
-+	mutex_unlock(&dev->mt76.mutex);
-+	return;
-+}
-+
- const struct ieee80211_ops mt7996_ops = {
- 	.tx = mt7996_tx,
- 	.start = mt7996_start,
-@@ -2617,6 +2666,7 @@ const struct ieee80211_ops mt7996_ops = {
- 	.net_fill_forward_path = mt7996_net_fill_forward_path,
- 	.net_setup_tc = mt76_wed_net_setup_tc,
- #endif
-+	.event_callback = mt7996_event_callback,
- 	.add_chanctx = mt7996_add_chanctx,
- 	.remove_chanctx = mt7996_remove_chanctx,
- 	.change_chanctx = mt7996_change_chanctx,
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index dc3cacc..0412d73 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -128,6 +128,8 @@
- 
- #define to_rssi(field, rcpi)	((FIELD_GET(field, rcpi) - 220) / 2)
- 
-+#define MT7996_MAX_BEACON_LOSS		50
-+
- struct mt7996_vif;
- struct mt7996_sta;
- struct mt7996_dfs_pulse;
-@@ -360,6 +362,10 @@ struct mt7996_vif {
- 	u8 mld_remap_id;
- 
- 	u8 band_to_link[__MT_MAX_BAND];
-+
-+	/* for beacon monitoring */
-+	struct delayed_work beacon_mon_work;
-+	unsigned long beacon_received_time[__MT_MAX_BAND];
- };
- 
- /* crash-dump */
-@@ -1113,6 +1119,7 @@ bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len);
- void mt7996_stats_work(struct work_struct *work);
- void mt7996_scan_work(struct work_struct *work);
- void mt7996_scan_complete(struct mt7996_phy *phy, bool aborted);
-+void mt7996_beacon_mon_work(struct work_struct *work);
- int mt76_dfs_start_rdd(struct mt7996_dev *dev, bool force);
- int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy);
- void mt7996_set_stream_he_eht_caps(struct mt7996_phy *phy);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0109-mtk-mt76-mt7996-temp-support-for-single-wiphy.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0109-mtk-mt76-mt7996-temp-support-for-single-wiphy.patch
new file mode 100644
index 0000000..1f5c5df
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0109-mtk-mt76-mt7996-temp-support-for-single-wiphy.patch
@@ -0,0 +1,458 @@
+From dd73bdd0c88aa8f1fcf133d903d257286911047c Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 28 Mar 2024 18:50:04 +0800
+Subject: [PATCH 109/199] mtk: mt76: mt7996: temp support for single wiphy
+
+Add temporal single wiphy for simultaneously supporting MLD and legacy
+interfaces.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mac80211.c      |  9 -----
+ mt76.h          | 11 +-----
+ mt7996/eeprom.c |  6 ----
+ mt7996/init.c   | 25 +++++++++----
+ mt7996/mac.c    |  5 +--
+ mt7996/main.c   | 96 ++++++++++++++++++++++++++++++-------------------
+ mt7996/mt7996.h | 14 ++++----
+ 7 files changed, 86 insertions(+), 80 deletions(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index 5f0c310f..bc463437 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -823,13 +823,9 @@ EXPORT_SYMBOL_GPL(mt76_has_tx_pending);
+ struct mt76_channel_state *
+ mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c)
+ {
+-	struct mt76_phy *ori_phy = phy;
+ 	struct mt76_sband *msband;
+ 	int idx;
+ 
+-	if (phy->main_phy)
+-		phy = phy->main_phy;
+-begin:
+ 	if (c->band == NL80211_BAND_2GHZ)
+ 		msband = &phy->sband_2g;
+ 	else if (c->band == NL80211_BAND_6GHZ)
+@@ -838,11 +834,6 @@ begin:
+ 		msband = &phy->sband_5g;
+ 
+ 	idx = c - &msband->sband.channels[0];
+-	/* TODO: mlo: this is a temp solution, need to come up with a more clever one */
+-	if (idx < 0 || idx >= msband->sband.n_channels) {
+-		phy = ori_phy;
+-		goto begin;
+-	}
+ 	return &msband->chan[idx];
+ }
+ EXPORT_SYMBOL_GPL(mt76_channel_state);
+diff --git a/mt76.h b/mt76.h
+index c51df8e5..b1f22e6a 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -831,8 +831,8 @@ struct mt76_vif {
+ 
+ struct mt76_phy {
+ 	struct ieee80211_hw *hw;
++	struct ieee80211_hw *ori_hw;
+ 	struct mt76_dev *dev;
+-	struct mt76_phy *main_phy;
+ 	void *priv;
+ 
+ 	unsigned long state;
+@@ -1312,15 +1312,6 @@ mt76_phy_hw(struct mt76_dev *dev, u8 phy_idx)
+ 	return mt76_dev_phy(dev, phy_idx)->hw;
+ }
+ 
+-static inline struct ieee80211_hw *
+-mt76_main_hw(struct mt76_phy *phy)
+-{
+-	if (phy->main_phy)
+-		return mt76_dev_phy(phy->dev, phy->main_phy->band_idx)->hw;
+-
+-	return mt76_dev_phy(phy->dev, phy->band_idx)->hw;
+-}
+-
+ static inline u8 *
+ mt76_get_txwi_ptr(struct mt76_dev *dev, struct mt76_txwi_cache *t)
+ {
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index cd93a3c2..c4714982 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -390,12 +390,6 @@ static int mt7996_eeprom_parse_band_config(struct mt7996_phy *phy)
+ 		break;
+ 	}
+ 
+-	/* TODO: for MLO, we enable all band capabilities */
+-	phy->mt76->cap.has_2ghz = true;
+-	phy->mt76->cap.has_5ghz = true;
+-	if (is_mt7996(&phy->dev->mt76))
+-		phy->mt76->cap.has_6ghz = true;
+-
+ 	return ret;
+ }
+ 
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 2fe869c9..f75aa568 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -19,13 +19,13 @@ static const struct ieee80211_iface_limit if_limits[] = {
+ 		.max = 1,
+ 		.types = BIT(NL80211_IFTYPE_ADHOC)
+ 	}, {
+-		.max = 16,
++		.max = 16 * 3,
+ 		.types = BIT(NL80211_IFTYPE_AP)
+ #ifdef CONFIG_MAC80211_MESH
+ 			 | BIT(NL80211_IFTYPE_MESH_POINT)
+ #endif
+ 	}, {
+-		.max = MT7996_MAX_INTERFACES,
++		.max = MT7996_MAX_INTERFACES * 3,
+ 		.types = BIT(NL80211_IFTYPE_STATION)
+ 	}
+ };
+@@ -34,7 +34,7 @@ static const struct ieee80211_iface_combination if_comb[] = {
+ 	{
+ 		.limits = if_limits,
+ 		.n_limits = ARRAY_SIZE(if_limits),
+-		.max_interfaces = MT7996_MAX_INTERFACES,
++		.max_interfaces = MT7996_MAX_INTERFACES * 3,
+ 		.num_different_channels = 3,
+ 		.beacon_int_infra_match = true,
+ 		.radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+@@ -417,7 +417,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ 	wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
+ 	wiphy->reg_notifier = mt7996_regd_notifier;
+ 	wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+-	wiphy->mbssid_max_interfaces = 16;
++	wiphy->mbssid_max_interfaces = 16 * 3;
+ 
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BSS_COLOR);
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
+@@ -795,6 +795,10 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 		mtk_wed_device_start(&dev->mt76.mmio.wed_hif2, MT_INT_TX_RX_DONE_EXT);
+ 	}
+ 
++	/* TODO: FIXME: force to use single wiphy, need to rework init flow */
++	phy->mt76->ori_hw = mphy->hw;
++	mphy->hw = dev->phy.mt76->hw;
++
+ 	return 0;
+ 
+ error:
+@@ -811,6 +815,9 @@ mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
+ 	if (!phy)
+ 		return;
+ 
++	/* TODO: FIXME: temp for single wiphy support */
++	phy->mt76->hw = phy->mt76->ori_hw;
++
+ 	mt7996_unregister_thermal(phy);
+ 
+ 	mphy = phy->dev->mt76.phys[band];
+@@ -1677,6 +1684,12 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ 	if (ret)
+ 		return ret;
+ 
++	hw->wiphy->bands[NL80211_BAND_2GHZ] = &dev->phy.mt76->sband_2g.sband;
++	if (mt7996_phy2(dev))
++		hw->wiphy->bands[NL80211_BAND_5GHZ] = &mt7996_phy2(dev)->mt76->sband_5g.sband;
++	if (mt7996_phy3(dev))
++		hw->wiphy->bands[NL80211_BAND_6GHZ] = &mt7996_phy3(dev)->mt76->sband_6g.sband;
++
+ 	ieee80211_queue_work(mt76_hw(dev), &dev->init_work);
+ 
+ 	dev->recovery.hw_init_done = true;
+@@ -1706,11 +1719,11 @@ error:
+ void mt7996_unregister_device(struct mt7996_dev *dev)
+ {
+ 	cancel_work_sync(&dev->wed_rro.work);
+-	mt7996_unregister_phy(mt7996_phy3(dev), MT_BAND2);
+-	mt7996_unregister_phy(mt7996_phy2(dev), MT_BAND1);
+ 	mt7996_unregister_thermal(&dev->phy);
+ 	mt7996_coredump_unregister(dev);
+ 	mt76_unregister_device(&dev->mt76);
++	mt7996_unregister_phy(mt7996_phy2(dev), MT_BAND1);
++	mt7996_unregister_phy(mt7996_phy3(dev), MT_BAND2);
+ 	mt7996_wed_rro_free(dev);
+ 	mt7996_mcu_exit(dev);
+ 	mt7996_tx_token_put(dev);
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index a306ad71..24df5bb5 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2402,10 +2402,7 @@ void mt7996_mac_work(struct work_struct *work)
+ 
+ 	mt76_tx_status_check(mdev, false);
+ 
+-	if (mphy->main_phy && !test_bit(MT76_STATE_RUNNING, &mphy->main_phy->state))
+-		return;
+-
+-	ieee80211_queue_delayed_work(mt76_main_hw(mphy), &mphy->mac_work,
++	ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
+ 				     MT7996_WATCHDOG_TIME);
+ }
+ 
+diff --git a/mt7996/main.c b/mt7996/main.c
+index fd0df974..7424bebf 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -140,6 +140,10 @@ static int mt7996_start(struct ieee80211_hw *hw)
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	int ret;
+ 
++	/* only allow settings from hw0 */
++	if (hw != dev->phy.mt76->hw)
++		return -1;
++
+ 	flush_work(&dev->init_work);
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+@@ -154,6 +158,10 @@ static void mt7996_stop(struct ieee80211_hw *hw)
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	int band;
+ 
++	/* only allow settings from hw0 */
++	if (hw != dev->phy.mt76->hw)
++		return;
++
+ 	cancel_delayed_work_sync(&dev->scs_work);
+ 
+ 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
+@@ -173,7 +181,6 @@ static void mt7996_stop(struct ieee80211_hw *hw)
+ 		mutex_lock(&dev->mt76.mutex);
+ 		mt7996_mcu_set_radio_en(phy, false);
+ 		clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+-		phy->mt76->main_phy = NULL;
+ 		mutex_unlock(&dev->mt76.mutex);
+ 	}
+ }
+@@ -280,16 +287,18 @@ static void mt7996_remove_bss_conf(struct ieee80211_vif *vif,
+ 				   struct ieee80211_bss_conf *conf,
+ 				   struct mt7996_bss_conf *mconf)
+ {
+-	struct mt7996_phy *phy = mconf->phy;
+-	struct mt7996_dev *dev = phy->dev;
++	struct mt7996_phy *phy;
++	struct mt7996_dev *dev;
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	u8 link_id = conf->link_id;
+ 	struct mt7996_link_sta *mlink =
+ 		mlink_dereference_protected(&mvif->sta, link_id);
+ 
+-	if (!mlink)
++	if (!mlink || !mconf)
+ 		return;
+ 
++	phy = mconf->phy;
++	dev = phy->dev;
+ 	mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, false, false);
+ 	mt7996_mcu_add_bss_info(phy, conf, mconf, mlink, false);
+ 	mt7996_mcu_add_dev_info(phy, conf, mconf, false);
+@@ -446,8 +455,11 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ 	mvif->dev = dev;
+ 	mvif->hw = hw;
+ 	mvif->sta.vif = mvif;
++	/* TODO: temporaily set this to prevent some crashes */
++	mvif->deflink.phy = phy;
+ 
+-	ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
++	if (vif->type == NL80211_IFTYPE_STATION)
++		ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
+ 	mutex_unlock(&dev->mt76.mutex);
+ 
+ 	return ret;
+@@ -547,10 +559,9 @@ out:
+ 	clear_bit(MT76_RESET, &phy->mt76->state);
+ 	mutex_unlock(&dev->mt76.mutex);
+ 
+-	if (phy->mt76 == phy->mt76->main_phy)
+-		mt76_txq_schedule_all(phy->mt76);
++	mt76_txq_schedule_all(phy->mt76);
+ 
+-	ieee80211_queue_delayed_work(mt76_main_hw(phy->mt76),
++	ieee80211_queue_delayed_work(phy->mt76->hw,
+ 				     &phy->mt76->mac_work,
+ 				     MT7996_WATCHDOG_TIME);
+ 
+@@ -561,11 +572,11 @@ int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef
+ {
+ 	int ret;
+ 
+-	ieee80211_stop_queues(mt76_main_hw(phy->mt76));
++	ieee80211_stop_queues(phy->mt76->hw);
+ 	ret = __mt7996_set_channel(phy, chandef);
+ 	if (ret)
+ 		return ret;
+-	ieee80211_wake_queues(mt76_main_hw(phy->mt76));
++	ieee80211_wake_queues(phy->mt76->hw);
+ 
+ 	return 0;
+ }
+@@ -773,9 +784,6 @@ static void mt7996_configure_filter(struct ieee80211_hw *hw,
+ 			continue;
+ 
+ 		tmp = dev->mt76.phys[band]->priv;
+-		if (tmp->mt76->main_phy != phy->mt76)
+-			continue;
+-
+ 		tmp->rxfilter = phy->rxfilter;
+ 		mt76_wr(dev, MT_WF_RFCR(tmp->mt76->band_idx), phy->rxfilter);
+ 
+@@ -1580,9 +1588,11 @@ static int
+ mt7996_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
+ {
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+-	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+-	int max_nss = hweight8(hw->wiphy->available_antennas_tx);
+-	u8 band_idx = phy->mt76->band_idx, shift = dev->chainshift[band_idx];
++	int band, max_nss = hweight8(hw->wiphy->available_antennas_tx);
++
++	/* only allow settings from hw0 */
++	if (hw != dev->phy.mt76->hw)
++		return 0;
+ 
+ 	if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss)
+ 		return -EINVAL;
+@@ -1592,20 +1602,34 @@ mt7996_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+-	phy->mt76->antenna_mask = tx_ant;
++	for (band = 0; band < NUM_NL80211_BANDS; band++) {
++		struct mt7996_phy *phy;
++		u8 band_idx, shift;
+ 
+-	/* restore to the origin chainmask which might have auxiliary path */
+-	if (hweight8(tx_ant) == max_nss && band_idx < MT_BAND2)
+-		phy->mt76->chainmask = ((dev->chainmask >> shift) &
+-					(BIT(dev->chainshift[band_idx + 1] - shift) - 1)) << shift;
+-	else if (hweight8(tx_ant) == max_nss)
+-		phy->mt76->chainmask = (dev->chainmask >> shift) << shift;
+-	else
+-		phy->mt76->chainmask = tx_ant << shift;
++		if (!hw->wiphy->bands[band])
++			continue;
++
++		phy = mt7996_band_phy(hw, band);
++		if (!phy)
++			continue;
++
++		phy->mt76->antenna_mask = tx_ant;
++		band_idx = phy->mt76->band_idx;
++		shift = dev->chainshift[band_idx];
++
++		/* restore to the origin chainmask which might have auxiliary path */
++		if (hweight8(tx_ant) == max_nss && band_idx < MT_BAND2)
++			phy->mt76->chainmask = ((dev->chainmask >> shift) &
++						(BIT(dev->chainshift[band_idx + 1] - shift) - 1)) << shift;
++		else if (hweight8(tx_ant) == max_nss)
++			phy->mt76->chainmask = (dev->chainmask >> shift) << shift;
++		else
++			phy->mt76->chainmask = tx_ant << shift;
+ 
+-	mt76_set_stream_caps(phy->mt76, true);
+-	mt7996_set_stream_vht_txbf_caps(phy);
+-	mt7996_set_stream_he_eht_caps(phy);
++		mt76_set_stream_caps(phy->mt76, true);
++		mt7996_set_stream_vht_txbf_caps(phy);
++		mt7996_set_stream_he_eht_caps(phy);
++	}
+ 
+ 	mutex_unlock(&dev->mt76.mutex);
+ 
+@@ -2261,7 +2285,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 
+ 	if (vif->type == NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
+ 	    (phy->mt76 != mvif->deflink.phy->mt76)) {
+-		phy->mt76->main_phy = hw->priv;
++		// phy->mt76->main_phy = hw->priv;
+ 		mt7996_remove_bss_conf(vif, &vif->bss_conf, &mvif->deflink);
+ 
+ 		ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
+@@ -2274,7 +2298,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	set_bit(MT76_SCANNING, &phy->mt76->state);
+ 	mutex_unlock(&phy->dev->mt76.mutex);
+ 
+-	ieee80211_queue_delayed_work(mt76_main_hw(phy->mt76), &phy->scan_work, 0);
++	ieee80211_queue_delayed_work(phy->mt76->hw, &phy->scan_work, 0);
+ 
+ 	return 0;
+ }
+@@ -2282,7 +2306,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ static void
+ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ {
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	// struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	int band;
+ 
+ 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
+@@ -2292,17 +2316,16 @@ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ 			continue;
+ 
+ 		phy = mt7996_band_phy(hw, band);
+-		if (!(test_bit(MT76_SCANNING, &phy->mt76->state) &&
+-		      phy->mt76->main_phy == hw->priv))
++		if (!test_bit(MT76_SCANNING, &phy->mt76->state))
+ 			continue;
+ 
+ 		cancel_delayed_work_sync(&phy->scan_work);
+ 
+ 		mutex_lock(&phy->dev->mt76.mutex);
+ 		mt7996_scan_complete(phy, true);
+-		if (vif->type == NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
+-		    (phy->mt76 != mvif->deflink.phy->mt76))
+-			phy->mt76->main_phy = NULL;
++		// if (vif->type == NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
++		//     (phy->mt76 != mvif->deflink.phy->mt76))
++		// 	phy->mt76->main_phy = NULL;
+ 		mutex_unlock(&phy->dev->mt76.mutex);
+ 	}
+ }
+@@ -2317,7 +2340,6 @@ mt7996_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
+ 	wiphy_info(hw->wiphy, "%s: add %u\n", __func__, conf->def.chan->hw_value);
+ 	mutex_lock(&phy->dev->mt76.mutex);
+ 
+-	phy->mt76->main_phy = hw->priv;
+ 	if (ctx->assigned) {
+ 		mutex_unlock(&phy->dev->mt76.mutex);
+ 		return -ENOSPC;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index fbb85177..9a47ad04 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -795,21 +795,19 @@ mt7996_get_background_radar_cap(struct mt7996_dev *dev)
+ static inline struct mt7996_phy *
+ mt7996_band_phy(struct ieee80211_hw *hw, enum nl80211_band band)
+ {
+-	struct mt76_phy *phy = hw->priv;
+-
+-	if (!(hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO))
+-		return phy->priv;
++	struct mt76_dev *dev = hw->priv;
++	struct mt76_phy *phy;
+ 
+ 	/* TODO: mlo: temporarily hardcode */
+ 	if (band == NL80211_BAND_6GHZ)
+-		phy = phy->dev->phys[MT_BAND2];
++		phy = dev->phys[MT_BAND2];
+ 	else if (band == NL80211_BAND_5GHZ)
+-		phy = phy->dev->phys[MT_BAND1];
++		phy = dev->phys[MT_BAND1];
+ 	else
+-		phy = phy->dev->phys[MT_BAND0];
++		phy = dev->phys[MT_BAND0];
+ 
+ 	if (!phy)
+-		phy = hw->priv;
++		return NULL;
+ 
+ 	return phy->priv;
+ }
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0109-mtk-wifi-mt76-mt7996-support-band_idx-option-for-set.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0109-mtk-wifi-mt76-mt7996-support-band_idx-option-for-set.patch
deleted file mode 100644
index 18dc66b..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0109-mtk-wifi-mt76-mt7996-support-band_idx-option-for-set.patch
+++ /dev/null
@@ -1,178 +0,0 @@
-From ef9c473ce6e0e65c915225af9d8400dbebb7ae67 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Mon, 4 Mar 2024 16:21:16 +0800
-Subject: [PATCH 109/116] mtk: wifi: mt76: mt7996: support band_idx option for
- set_mu/get_mu vendor command
-
-The vendor command set_mu and get_mu should be executed with band_idx.
-With band_idx, driver can access the corrsponding phy by band_idx.
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- mt7996/mcu.c    | 26 +++++++++++++++++++-------
- mt7996/mcu.h    |  1 +
- mt7996/vendor.c | 40 +++++++++++++++++++++++++++++++++++-----
- mt7996/vendor.h |  1 +
- 4 files changed, 56 insertions(+), 12 deletions(-)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index e18a553..a71d92f 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -5853,12 +5853,23 @@ int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy)
- #ifdef CONFIG_MTK_VENDOR
- void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
- {
--	u8 mode, val;
-+	u8 mode, val, band_idx;
- 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
--	struct mt7996_phy *phy =  mvif->deflink.phy;
-+	struct mt7996_phy *phy;
-+	struct mt76_phy *mphy;
- 
- 	mode = FIELD_GET(RATE_CFG_MODE, *((u32 *)data));
- 	val = FIELD_GET(RATE_CFG_VAL, *((u32 *)data));
-+	band_idx = FIELD_GET(RATE_CFG_BAND_IDX, *((u32 *)data));
-+
-+	if (!mt7996_band_valid(mvif->dev, band_idx))
-+		goto error;
-+
-+	mphy = mvif->dev->mt76.phys[band_idx];
-+	if (!mphy)
-+		goto error;
-+
-+	phy = (struct mt7996_phy *)mphy->priv;
- 
- 	switch (mode) {
- 	case RATE_PARAM_FIXED_OFDMA:
-@@ -5874,13 +5885,14 @@ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
- 			phy->muru_onoff = MUMIMO_UL;
- 		break;
- 	case RATE_PARAM_AUTO_MU:
--		if (val < 0 || val > 15) {
--			printk("Wrong value! The value is between 0-15.\n");
--			break;
--		}
--		phy->muru_onoff = val;
-+		phy->muru_onoff = val & GENMASK(3, 0);
- 		break;
- 	}
-+
-+	return;
-+error:
-+	dev_err(mvif->dev->mt76.dev, "Invalid band_idx to config\n");
-+	return;
- }
- 
- void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index ee36cf5..3d5a0c3 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -888,6 +888,7 @@ enum {
- #endif
- };
- 
-+#define RATE_CFG_BAND_IDX	GENMASK(17, 16)
- #define RATE_CFG_MODE	GENMASK(15, 8)
- #define RATE_CFG_VAL	GENMASK(7, 0)
- 
-diff --git a/mt7996/vendor.c b/mt7996/vendor.c
-index 31688c3..64ef551 100644
---- a/mt7996/vendor.c
-+++ b/mt7996/vendor.c
-@@ -16,6 +16,7 @@ mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
- 	[MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
- 	[MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
- 	[MTK_VENDOR_ATTR_MU_CTRL_STRUCT] = {.type = NLA_BINARY },
-+	[MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX] = {.type = NLA_U8 },
- };
- 
- static const struct nla_policy
-@@ -135,7 +136,7 @@ static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
- 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
- 	struct mt7996_muru *muru;
- 	int err;
--	u8 val8;
-+	u8 val8, band_idx;
- 	u32 val32 = 0;
- 
- 	err = nla_parse(tb, MTK_VENDOR_ATTR_MU_CTRL_MAX, data, data_len,
-@@ -143,10 +144,13 @@ static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
- 	if (err)
- 		return err;
- 
--	if (tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF]) {
-+	if (tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF] &&
-+	    tb[MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX]) {
- 		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF]);
-+		band_idx = nla_get_u8(tb[MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX]);
- 		val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_AUTO_MU) |
--			 FIELD_PREP(RATE_CFG_VAL, val8);
-+			 FIELD_PREP(RATE_CFG_VAL, val8) |
-+			 FIELD_PREP(RATE_CFG_BAND_IDX, band_idx);
- 		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
- 							   mt7996_set_wireless_vif, &val32);
- 	} else if (tb[MTK_VENDOR_ATTR_MU_CTRL_STRUCT]) {
-@@ -168,18 +172,44 @@ mt7996_vendor_mu_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
- 			   unsigned long *storage)
- {
- 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
--	struct mt7996_phy *phy = mt7996_hw_phy(hw);
--	int len = 0;
-+	struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+	struct mt7996_phy *phy;
-+	struct mt76_phy *mphy;
-+	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_MU_CTRL];
-+	int len = 0, err;
-+	u8 band_idx;
- 
- 	if (*storage == 1)
- 		return -ENOENT;
- 	*storage = 1;
- 
-+	err = nla_parse(tb, MTK_VENDOR_ATTR_MU_CTRL_MAX, data, data_len,
-+			mu_ctrl_policy, NULL);
-+	if (err)
-+		return err;
-+
-+	if (!tb[MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX])
-+		return -EINVAL;
-+
-+	band_idx = nla_get_u8(tb[MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX]);
-+	if (!mt7996_band_valid(dev, band_idx))
-+		goto error;
-+
-+	mphy = dev->mt76.phys[band_idx];
-+	if (!mphy)
-+		goto error;
-+
-+	phy = (struct mt7996_phy *)mphy->priv;
-+
- 	if (nla_put_u8(skb, MTK_VENDOR_ATTR_MU_CTRL_DUMP, phy->muru_onoff))
- 		return -ENOMEM;
- 	len += 1;
- 
- 	return len;
-+
-+error:
-+	dev_err(dev->mt76.dev, "Invalid band idx to dump\n");
-+	return -EINVAL;
- }
- 
- void mt7996_set_wireless_rts_sigta(struct ieee80211_hw *hw, u8 value) {
-diff --git a/mt7996/vendor.h b/mt7996/vendor.h
-index 0d1ef32..3234677 100644
---- a/mt7996/vendor.h
-+++ b/mt7996/vendor.h
-@@ -68,6 +68,7 @@ enum mtk_vendor_attr_mu_ctrl {
- 	MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
- 	MTK_VENDOR_ATTR_MU_CTRL_DUMP,
- 	MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
-+	MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX,
- 
- 	/* keep last */
- 	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0110-mtk-mt76-mt7996-implement-ieee80211_ops-for-link-deb.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0110-mtk-mt76-mt7996-implement-ieee80211_ops-for-link-deb.patch
new file mode 100644
index 0000000..718b6e8
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0110-mtk-mt76-mt7996-implement-ieee80211_ops-for-link-deb.patch
@@ -0,0 +1,295 @@
+From 4ac5eeaefbbc6353b211d703e83ebfdc98c9bc9b Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 29 Dec 2023 18:37:41 +0800
+Subject: [PATCH 110/199] mtk: mt76: mt7996: implement ieee80211_ops for link
+ debugfs
+
+Add .link_sta_add_debugfs and .link_add_debugfs for per-link STA and BSS
+info.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ mt7996/debugfs.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mac.c     |   4 +-
+ mt7996/main.c    |   2 +
+ mt7996/mcu.c     |   6 +-
+ mt7996/mt7996.h  |   5 ++
+ 5 files changed, 214 insertions(+), 4 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 62658dbc..fe8fea5d 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -1294,4 +1294,205 @@ void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	debugfs_create_file("hw-queues", 0400, dir, sta, &mt7996_queues_fops);
+ }
+ 
++static void
++mt7996_parse_rate(struct rate_info *rate, char *buf, size_t size)
++{
++	u32 bitrate = cfg80211_calculate_bitrate(rate);
++	bool legacy = false;
++	char *pos = buf;
++	enum {
++		GI_0_4,
++		GI_0_8,
++		GI_1_6,
++		GI_3_2
++	} gi = GI_0_8;
++
++	pos += snprintf(pos, size - (pos - buf), "%u.%u Mbit/s",
++			bitrate / 10, bitrate % 10);
++
++	if (rate->flags & RATE_INFO_FLAGS_MCS) {
++		pos += snprintf(pos, size - (pos - buf), " HT");
++
++		if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
++			gi = GI_0_4;
++	} else if (rate->flags & RATE_INFO_FLAGS_VHT_MCS) {
++		pos += snprintf(pos, size - (pos - buf), " VHT");
++
++		if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
++			gi = GI_0_4;
++	} else if (rate->flags & RATE_INFO_FLAGS_HE_MCS) {
++		pos += snprintf(pos, size - (pos - buf), " HE");
++
++		if (rate->he_gi == NL80211_RATE_INFO_HE_GI_1_6)
++			gi = GI_1_6;
++		else if (rate->he_gi == NL80211_RATE_INFO_HE_GI_3_2)
++			gi = GI_3_2;
++	} else if (rate->flags & RATE_INFO_FLAGS_EHT_MCS) {
++		pos += snprintf(pos, size - (pos - buf), " EHT");
++
++		if (rate->eht_gi == NL80211_RATE_INFO_EHT_GI_1_6)
++			gi = GI_1_6;
++		else if (rate->eht_gi == NL80211_RATE_INFO_EHT_GI_3_2)
++			gi = GI_3_2;
++	} else {
++		pos += snprintf(pos, size - (pos - buf), " Legacy");
++		legacy = true;
++	}
++
++	switch (rate->bw) {
++	case RATE_INFO_BW_20:
++		pos += snprintf(pos, size - (pos - buf), " 20MHz");
++		break;
++	case RATE_INFO_BW_40:
++		pos += snprintf(pos, size - (pos - buf), " 40MHz");
++		break;
++	case RATE_INFO_BW_80:
++		pos += snprintf(pos, size - (pos - buf), " 80MHz");
++		break;
++	case RATE_INFO_BW_160:
++		pos += snprintf(pos, size - (pos - buf), " 160MHz");
++		break;
++	case RATE_INFO_BW_320:
++		pos += snprintf(pos, size - (pos - buf), " 320MHz");
++		break;
++	case RATE_INFO_BW_HE_RU:
++		if (rate->he_ru_alloc == NL80211_RATE_INFO_HE_RU_ALLOC_106) {
++			pos += snprintf(pos, size - (pos - buf), " 106-tone RU");
++			break;
++		}
++		fallthrough;
++	default:
++		pos += snprintf(pos, size - (pos - buf), " (Unknown BW)");
++	}
++
++	if (!legacy) {
++		pos += snprintf(pos, size - (pos - buf), " MCS %hhu", rate->mcs);
++		pos += snprintf(pos, size - (pos - buf), " NSS %hhu", rate->nss);
++	}
++
++	switch (gi) {
++	case GI_0_4:
++		pos += snprintf(pos, size - (pos - buf), " GI 0.4us");
++		break;
++	case GI_0_8:
++		pos += snprintf(pos, size - (pos - buf), " GI 0.8us");
++		break;
++	case GI_1_6:
++		pos += snprintf(pos, size - (pos - buf), " GI 1.6us");
++		break;
++	default:
++		pos += snprintf(pos, size - (pos - buf), " GI 3.2us");
++		break;
++	}
++}
++
++static int
++mt7996_link_sta_info_show(struct seq_file *file, void *data)
++{
++	struct ieee80211_link_sta *link_sta = file->private;
++	struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv;
++	struct mt7996_link_sta *mlink;
++	struct mt76_sta_stats *stats;
++	struct mt76_wcid *wcid;
++	char buf[100];
++
++	mutex_lock(&msta->vif->dev->mt76.mutex);
++
++	mlink = mlink_dereference_protected(msta, link_sta->link_id);
++	if (!mlink) {
++		mutex_unlock(&msta->vif->dev->mt76.mutex);
++		return -EINVAL;
++	}
++	wcid = &mlink->wcid;
++	stats = &wcid->stats;
++
++	seq_printf(file, "WCID: %hu\n", wcid->idx);
++	seq_printf(file, "Link ID: %hhu\n", link_sta->link_id);
++	seq_printf(file, "Link Address: %pM\n", link_sta->addr);
++	seq_printf(file, "Status:\n");
++	seq_printf(file, "\tRSSI: %d [%hhd, %hhd, %hhd, %hhd] dBm\n",
++		   mlink->signal, mlink->chain_signal[0], mlink->chain_signal[1],
++		   mlink->chain_signal[2], mlink->chain_signal[3]);
++	seq_printf(file, "\tACK RSSI: %d [%hhd, %hhd, %hhd, %hhd] dBm\n",
++		   mlink->ack_signal, mlink->chain_ack_signal[0],
++		   mlink->chain_ack_signal[1], mlink->chain_ack_signal[2],
++		   mlink->chain_ack_signal[3]);
++	seq_printf(file, "\tACK SNR: [%hhd, %hhd, %hhd, %hhd] dBm\n",
++		   mlink->chain_ack_snr[0], mlink->chain_ack_snr[1],
++		   mlink->chain_ack_snr[2], mlink->chain_ack_snr[3]);
++	seq_printf(file, "Rate:\n");
++
++	mt7996_parse_rate(&wcid->rate, buf, sizeof(buf));
++	seq_printf(file, "\tTX: %s\n", buf);
++	mt7996_parse_rate(&wcid->rx_rate, buf, sizeof(buf));
++	seq_printf(file, "\tRX: %s\n", buf);
++
++	seq_printf(file, "Statistics:\n");
++	seq_printf(file, "\tTX:\n");
++	seq_printf(file, "\t\tBytes: %llu\n", stats->tx_bytes);
++	seq_printf(file, "\t\tMSDU Count: %u\n", stats->tx_packets);
++	seq_printf(file, "\t\tMPDU Count: %u\n", stats->tx_mpdus);
++	seq_printf(file, "\t\tMPDU Fails: %u (PER: %u.%u%%)\n", stats->tx_failed,
++		   stats->tx_mpdus ? stats->tx_failed * 1000 / stats->tx_mpdus / 10 : 0,
++		   stats->tx_mpdus ? stats->tx_failed * 1000 / stats->tx_mpdus % 10 : 0);
++	seq_printf(file, "\t\tMPDU Retries: %u\n", stats->tx_retries);
++	seq_printf(file, "\t\tAirtime: %llu (unit: 1.024 us)\n", stats->tx_airtime);
++	seq_printf(file, "\tRX:\n");
++	seq_printf(file, "\t\tBytes: %llu\n", stats->rx_bytes);
++	seq_printf(file, "\t\tMPDU Count: %u\n", stats->rx_mpdus);
++	seq_printf(file, "\t\tMPDU FCS Errors: %u (PER: %u.%u%%)\n", stats->rx_fcs_err,
++		   stats->rx_mpdus ? stats->rx_fcs_err * 1000 / stats->rx_mpdus / 10 : 0,
++		   stats->rx_mpdus ? stats->rx_fcs_err * 1000 / stats->rx_mpdus % 10 : 0);
++	seq_printf(file, "\t\tAirtime: %llu (unit: 1.024 us)\n", stats->rx_airtime);
++
++	mutex_unlock(&msta->vif->dev->mt76.mutex);
++
++	return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(mt7996_link_sta_info);
++
++void mt7996_link_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++				 struct ieee80211_link_sta *link_sta,
++				 struct dentry *dir)
++{
++	debugfs_create_file("link_sta_info", 0400, dir, link_sta,
++			    &mt7996_link_sta_info_fops);
++}
++
++static int
++mt7996_link_info_show(struct seq_file *file, void *data)
++{
++	struct ieee80211_bss_conf *conf = file->private;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)conf->vif->drv_priv;
++	struct mt7996_sta *msta = &mvif->sta;
++	struct mt7996_bss_conf *mconf;
++	struct mt7996_link_sta *mlink;
++	struct mt7996_dev *dev = mvif->dev;
++	struct rate_info *r;
++
++	mutex_lock(&dev->mt76.mutex);
++
++	mconf = mconf_dereference_protected(mvif, conf->link_id);
++	mlink = mlink_dereference_protected(msta, conf->link_id);
++	if (!mconf || !mlink) {
++		mutex_unlock(&dev->mt76.mutex);
++		return -EINVAL;
++	}
++
++	r = &mlink->wcid.rate;
++	seq_printf(file, "band mapping=%u\n", mconf->phy->mt76->band_idx);
++	seq_printf(file, "tx rate: flags=0x%x,legacy=%u,mcs=%u,nss=%u,bw=%u,he_gi=%u,he_dcm=%u,he_ru_alloc=%u,eht_gi=%u,eht_ru_alloc=%u\n",
++		   r->flags, r->legacy, r->mcs, r->nss, r->bw, r->he_gi, r->he_dcm, r->he_ru_alloc, r->eht_gi, r->eht_ru_alloc);
++
++	mutex_unlock(&dev->mt76.mutex);
++
++	return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(mt7996_link_info);
++
++void mt7996_link_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++			     struct ieee80211_bss_conf *link_conf, struct dentry *dir)
++{
++	debugfs_create_file("link_info", 0600, dir, link_conf, &mt7996_link_info_fops);
++}
+ #endif
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 24df5bb5..3b3aa01b 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2378,10 +2378,10 @@ void mt7996_mac_work(struct work_struct *work)
+ 				mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_RATE);
+ 				mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_AIRTIME);
+ 				mt7996_mcu_get_rssi(mdev);
+-				if (mtk_wed_device_active(&mdev->mmio.wed)) {
++				// if (mtk_wed_device_active(&mdev->mmio.wed)) {
+ 					mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_ADM_STAT);
+ 					mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_MSDU_COUNT);
+-				}
++				// }
+ 
+ 				if (mt7996_mcu_wa_cmd(phy->dev, MCU_WA_PARAM_CMD(QUERY), MCU_WA_PARAM_BSS_ACQ_PKT_CNT,
+ 				                      BSS_ACQ_PKT_CNT_BSS_BITMAP_ALL | BSS_ACQ_PKT_CNT_READ_CLR, 0))
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 7424bebf..17f4901f 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -2625,6 +2625,8 @@ const struct ieee80211_ops mt7996_ops = {
+ 	CFG80211_TESTMODE_DUMP(mt76_testmode_dump)
+ #ifdef CONFIG_MAC80211_DEBUGFS
+ 	.sta_add_debugfs = mt7996_sta_add_debugfs,
++	.link_sta_add_debugfs = mt7996_link_sta_add_debugfs,
++	// .link_add_debugfs = mt7996_link_add_debugfs,
+ #endif
+ 	.set_radar_background = mt7996_set_radar_background,
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index bf128637..2884af9d 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -616,8 +616,10 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 			wcid->stats.tx_packets += tx_packets;
+ 			wcid->stats.rx_packets += rx_packets;
+ 
+-			__mt7996_stat_to_netdev(mphy, wcid, 0, 0,
+-						tx_packets, rx_packets);
++			if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
++				__mt7996_stat_to_netdev(mphy, wcid, 0, 0,
++							tx_packets, rx_packets);
++			}
+ 			break;
+ 		case UNI_ALL_STA_TXRX_AIRTIME:
+ 			wlan_idx = le16_to_cpu(res->airtime[i].wlan_idx);
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 9a47ad04..294c46f6 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1142,6 +1142,11 @@ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap);
+ #ifdef CONFIG_MAC80211_DEBUGFS
+ void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 			    struct ieee80211_sta *sta, struct dentry *dir);
++void mt7996_link_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++				 struct ieee80211_link_sta *link_sta,
++				 struct dentry *dir);
++void mt7996_link_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++			     struct ieee80211_bss_conf *link_conf, struct dentry *dir);
+ #endif
+ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ 			 bool hif2, int *irq);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0110-mtk-wifi-mt76-mt7996-tmp-disable-VOW.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0110-mtk-wifi-mt76-mt7996-tmp-disable-VOW.patch
deleted file mode 100644
index 7d6fb6f..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0110-mtk-wifi-mt76-mt7996-tmp-disable-VOW.patch
+++ /dev/null
@@ -1,59 +0,0 @@
-From 2611bc7e80596500702dd3eef9d34014d00869b3 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Wed, 20 Mar 2024 22:56:44 +0800
-Subject: [PATCH 110/116] mtk: wifi: mt76: mt7996: tmp disable VOW
-
-FW will return failed when legacy 5G station connects after legacy 2G
-station, need to check.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- mt7996/mcu.c | 6 +++++-
- 1 file changed, 5 insertions(+), 1 deletion(-)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index a71d92f..895ec3e 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -2369,6 +2369,7 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
- 	return mt7996_mcu_add_rate_ctrl_fixed(dev, conf, mconf, link_sta, mlink);
- }
- 
-+#if 0
- static int
- mt7996_mcu_sta_init_vow(struct mt7996_bss_conf *mconf,
- 			struct mt7996_link_sta *mlink)
-@@ -2402,6 +2403,7 @@ mt7996_mcu_sta_init_vow(struct mt7996_bss_conf *mconf,
- 
- 	return mt7996_mcu_set_vow_drr_ctrl(phy, mconf, mlink, VOW_DRR_CTRL_STA_ALL);
- }
-+#endif
- 
- int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
- 		       struct mt7996_bss_conf *mconf,
-@@ -2410,7 +2412,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
- {
- 	struct ieee80211_vif *vif = conf->vif;
- 	struct sk_buff *skb;
--	int ret;
-+	// int ret;
- 
- 	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
- 					      &mlink->wcid,
-@@ -2455,11 +2457,13 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
- 		mt7996_mcu_sta_bfee_tlv(dev, skb, conf, mconf, link_sta);
- 	}
- 
-+#if 0
- 	ret = mt7996_mcu_sta_init_vow(mconf, mlink);
- 	if (ret) {
- 		dev_kfree_skb(skb);
- 		return ret;
- 	}
-+#endif
- out:
- 	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
- 				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0111-mtk-mt76-mt7996-support-multi-link-channel-switch.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0111-mtk-mt76-mt7996-support-multi-link-channel-switch.patch
new file mode 100644
index 0000000..3f696d9
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0111-mtk-mt76-mt7996-support-multi-link-channel-switch.patch
@@ -0,0 +1,143 @@
+From 56f1336c11dd0627e8e79951c16713cc35463733 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 19 Jan 2024 14:04:03 +0800
+Subject: [PATCH 111/199] mtk: mt76: mt7996: support multi-link channel switch
+
+mtk: wifi: mt76: mt7996: remove the limitation of radar detect width for mlo
+
+mtk: wifi: mt76: mt7996: start and finalize channel switch on link basis
+
+mtk: wifi: mt76: mt7996: fix DFS RDD init issue
+
+1. Add radar enabled flag in mt76_phy since hw->conf.radar_enabled
+is only used for non-chanctx driver.
+2. Add IEEE80211_CHANCTX_CHANGE_RADAR flag in change_chanctx for RDD
+DFS state update.
+
+mtk: wifi: mt76: mt7996: fix background radar using wrong phy for mld ap
+
+mt7996_hw_phy will be phy0 for 3 link mld ap
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mac80211.c    |  2 +-
+ mt76.h        |  1 +
+ mt7996/main.c | 12 +++++++++---
+ mt7996/mcu.c  | 14 +++++++++++---
+ 4 files changed, 22 insertions(+), 7 deletions(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index bc463437..5e4935f2 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -1787,7 +1787,7 @@ enum mt76_dfs_state mt76_phy_dfs_state(struct mt76_phy *phy)
+ 	    test_bit(MT76_SCANNING, &phy->state))
+ 		return MT_DFS_STATE_DISABLED;
+ 
+-	if (!hw->conf.radar_enabled) {
++	if (!phy->radar_enabled) {
+ 		if ((hw->conf.flags & IEEE80211_CONF_MONITOR) &&
+ 		    (phy->chandef.chan->flags & IEEE80211_CHAN_RADAR))
+ 			return MT_DFS_STATE_ACTIVE;
+diff --git a/mt76.h b/mt76.h
+index b1f22e6a..c6dda4a8 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -847,6 +847,7 @@ struct mt76_phy {
+ 
+ 	struct mt76_channel_state *chan_state;
+ 	enum mt76_dfs_state dfs_state;
++	bool radar_enabled;
+ 	ktime_t survey_time;
+ 
+ 	u32 aggr_stats[32];
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 17f4901f..7012aa96 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -2139,7 +2139,7 @@ static int
+ mt7996_set_radar_background(struct ieee80211_hw *hw,
+ 			    struct cfg80211_chan_def *chandef)
+ {
+-	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct mt7996_phy *phy = mt7996_band_phy(hw, NL80211_BAND_5GHZ);
+ 	struct mt7996_dev *dev = phy->dev;
+ 	int ret = -EINVAL;
+ 	bool running;
+@@ -2354,6 +2354,7 @@ mt7996_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
+ 	}
+ 
+ 	phy->chanctx = ctx;
++	phy->mt76->radar_enabled = conf->radar_enabled;
+ 	mutex_unlock(&phy->dev->mt76.mutex);
+ 
+ 	if (!mt76_testmode_enabled(phy->mt76) && !phy->mt76->test.bf_en) {
+@@ -2381,8 +2382,10 @@ mt7996_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *co
+ 
+ 	mutex_lock(&phy->dev->mt76.mutex);
+ 	ctx->assigned = false;
+-	if (ctx == phy->chanctx)
++	if (ctx == phy->chanctx) {
+ 		phy->chanctx = NULL;
++		phy->mt76->radar_enabled = false;
++	}
+ 	mutex_unlock(&phy->dev->mt76.mutex);
+ }
+ 
+@@ -2394,8 +2397,10 @@ mt7996_change_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *co
+ 	struct mt7996_phy *phy = ctx->phy;
+ 
+ 	wiphy_info(hw->wiphy, "%s: change %u, 0x%x\n", __func__, conf->def.chan->hw_value, changed);
+-	if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH) {
++	if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH ||
++	    changed & IEEE80211_CHANCTX_CHANGE_RADAR) {
+ 		ctx->chandef = conf->def;
++		phy->mt76->radar_enabled = conf->radar_enabled;
+ 
+ 		mt7996_set_channel(phy, &ctx->chandef);
+ 	}
+@@ -2493,6 +2498,7 @@ mt7996_switch_vif_chanctx(struct ieee80211_hw *hw,
+ 	mutex_lock(&phy->dev->mt76.mutex);
+ 
+ 	phy->chanctx = new_ctx;
++	phy->mt76->radar_enabled = vifs->new_ctx->radar_enabled;
+ 	new_ctx->assigned = true;
+ 	new_ctx->chandef = vifs->new_ctx->def;
+ 	new_ctx->phy = phy;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 2884af9d..7b0d631e 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -357,10 +357,18 @@ int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3)
+ static void
+ mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ {
+-	if (!vif->bss_conf.csa_active || vif->type == NL80211_IFTYPE_STATION)
++	struct mt76_phy *mphy = (struct mt76_phy *)priv;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct ieee80211_bss_conf *link_conf;
++	int link_id, band_idx = mphy->band_idx;
++
++	link_id = mvif->band_to_link[band_idx];
++	link_conf = rcu_dereference(vif->link_conf[link_id]);
++
++	if (!link_conf || !link_conf->csa_active || vif->type == NL80211_IFTYPE_STATION)
+ 		return;
+ 
+-	ieee80211_csa_finish(vif, 0);
++	ieee80211_csa_finish(vif, link_id);
+ }
+ 
+ static void
+@@ -475,7 +483,7 @@ mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb)
+ 		case UNI_EVENT_IE_COUNTDOWN_CSA:
+ 			ieee80211_iterate_active_interfaces_atomic(mphy->hw,
+ 					IEEE80211_IFACE_ITER_RESUME_ALL,
+-					mt7996_mcu_csa_finish, mphy->hw);
++					mt7996_mcu_csa_finish, mphy);
+ 			break;
+ 		case UNI_EVENT_IE_COUNTDOWN_BCC:
+ 			ieee80211_iterate_active_interfaces_atomic(mphy->hw,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0111-mtk-wifi-mt76-mt7996-enable-ampdu-limit-to-avoid-BA-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0111-mtk-wifi-mt76-mt7996-enable-ampdu-limit-to-avoid-BA-.patch
deleted file mode 100644
index 4c4f50d..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0111-mtk-wifi-mt76-mt7996-enable-ampdu-limit-to-avoid-BA-.patch
+++ /dev/null
@@ -1,186 +0,0 @@
-From 20695a924111dee64fb9fa5d8c4996f2333b4aee Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Mon, 1 Apr 2024 17:00:21 +0800
-Subject: [PATCH 111/116] mtk: wifi: mt76: mt7996: enable ampdu limit to avoid
- BA bound issue
-
-[Description]
-When the station is MTK device and the peak is higher than 15G, the PPS
-would exceed HW-RRO's bandwidth and lead to Rx fifo full and PER. When
-a link occurs PER, it may occupy SSN and the other two bands are not
-able to transmit.
-
-Limit AMPDU to 512 when satisify all of the following conditions
-1. BA winsize is 1024.
-2. At least one link use BW320 and its spatial stream is larger
-than 3.
-3. At least one link use BW160 and its spatial stream is larger
-than 3.
-
-By limiting AMPDU to 512, it can solve this issue.
-1. Reduce PPS so we can avoid Rx fifo full due to HW-RRO.
-2. If a bind occupy SSN, the other two bands can use the SSN
-between 512 to 1024.
-
-[Release-log]
-N/A
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- mt76_connac_mcu.h |  1 +
- mt7996/mcu.c      | 86 +++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/mcu.h      |  8 +++++
- 3 files changed, 95 insertions(+)
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 4d0fd4b..1c8e503 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -821,6 +821,7 @@ enum {
- 	STA_REC_KEY_V3 = 0x27,
- 	STA_REC_HDRT = 0x28,
- 	STA_REC_HDR_TRANS = 0x2B,
-+	STA_REC_TX_CAP = 0x2f,
- 	STA_REC_MAX_NUM
- };
- 
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 895ec3e..3f51c28 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -1344,6 +1344,85 @@ mt7996_mcu_sta_ba(struct mt7996_dev *dev, struct mt76_vif *mvif,
- 				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
- }
- 
-+static int
-+mt7996_mcu_sta_tx_cap(struct mt7996_dev *dev, struct mt76_vif *mvif,
-+		      struct mt76_wcid *wcid)
-+{
-+	struct sta_rec_tx_cap *tx_cap;
-+	struct sk_buff *skb;
-+	struct tlv *tlv;
-+
-+	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, mvif, wcid,
-+					      MT7996_STA_UPDATE_MAX_SIZE);
-+	if (IS_ERR(skb))
-+		return PTR_ERR(skb);
-+
-+	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_TX_CAP, sizeof(*tx_cap));
-+
-+	tx_cap = (struct sta_rec_tx_cap *)tlv;
-+	tx_cap->ampdu_limit_en = true;
-+
-+	dev_info(dev->mt76.dev, "%s: limit wcid %d ampdu to 512\n", __func__, wcid->idx);
-+
-+	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
-+				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
-+}
-+
-+static bool mt7996_check_limit_ampdu_en(struct ieee80211_ampdu_params *params) {
-+	struct ieee80211_sta *sta = params->sta;
-+	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	unsigned long valid_links = sta->valid_links ?: BIT(0);
-+	unsigned int link_id;
-+	bool BW320 = false, BW160 = false;
-+
-+	if (params->buf_size < 1024)
-+		return false;
-+
-+	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
-+		struct ieee80211_link_sta __rcu *link =
-+			link_sta_dereference_protected(sta, link_id);
-+		struct mt7996_bss_conf *mconf =
-+			mconf_dereference_protected(msta->vif, link_id);
-+		struct mt76_phy *phy = mconf->phy->mt76;
-+		struct ieee80211_eht_mcs_nss_supp_bw *ss = NULL;
-+		u8 sta_bw, ap_nss, sta_nss;
-+
-+		switch (phy->chandef.width) {
-+		case NL80211_CHAN_WIDTH_160:
-+			if (link->bandwidth >= IEEE80211_STA_RX_BW_160) {
-+				ss = &link->eht_cap.eht_mcs_nss_supp.bw._160;
-+				sta_bw = NL80211_CHAN_WIDTH_160;
-+			}
-+			break;
-+		case NL80211_CHAN_WIDTH_320:
-+			if (link->bandwidth == IEEE80211_STA_RX_BW_320) {
-+				ss = &link->eht_cap.eht_mcs_nss_supp.bw._320;
-+				sta_bw = NL80211_CHAN_WIDTH_320;
-+			}
-+			break;
-+		default:
-+			break;
-+		}
-+
-+		if (!ss)
-+			continue;
-+
-+		ap_nss = hweight8(phy->antenna_mask);
-+		sta_nss = max(u8_get_bits(ss->rx_tx_mcs11_max_nss, IEEE80211_EHT_MCS_NSS_RX),
-+			      u8_get_bits(ss->rx_tx_mcs13_max_nss, IEEE80211_EHT_MCS_NSS_RX));
-+
-+		if (min(ap_nss, sta_nss) <= 2)
-+			continue;
-+
-+		if (sta_bw == NL80211_CHAN_WIDTH_160)
-+			BW160 = true;
-+		else if (sta_bw == NL80211_CHAN_WIDTH_320)
-+			BW320 = true;
-+	}
-+
-+	return BW320 && BW160;
-+}
-+
- /** starec & wtbl **/
- int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
- 			 struct ieee80211_ampdu_params *params,
-@@ -1353,6 +1432,7 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- 	unsigned long valid_links = sta->valid_links ?: BIT(0);
- 	unsigned int link_id;
-+	bool limit_ampdu_en = mt7996_check_limit_ampdu_en(params);
- 
- 	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
- 		struct mt7996_link_sta *mlink =
-@@ -1368,6 +1448,12 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
- 					&mlink->wcid, enable, true);
- 		if (ret)
- 			return ret;
-+
-+		if (limit_ampdu_en) {
-+			ret = mt7996_mcu_sta_tx_cap(dev, &mconf->mt76, &mlink->wcid);
-+			if (ret)
-+				return ret;
-+		}
- 	}
- 
- 	return 0;
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 3d5a0c3..a1ac18f 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -579,6 +579,13 @@ struct sta_rec_ba_uni {
- 	u8 __rsv[3];
- } __packed;
- 
-+struct sta_rec_tx_cap {
-+	__le16 tag;
-+	__le16 len;
-+	u8 ampdu_limit_en;
-+	u8 rsv[3];
-+} __packed;
-+
- struct sta_rec_eht {
- 	__le16 tag;
- 	__le16 len;
-@@ -945,6 +952,7 @@ enum {
- 					 sizeof(struct sta_rec_eht) +		\
- 					 sizeof(struct sta_rec_hdrt) +		\
- 					 sizeof(struct sta_rec_hdr_trans) +	\
-+					 sizeof(struct sta_rec_tx_cap) +	\
- 					 sizeof(struct tlv))
- 
- #define MT7996_MAX_BEACON_SIZE		1338
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0112-mtk-mt76-mt7996-ACS-channel-time-too-long-on-duty-ch.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0112-mtk-mt76-mt7996-ACS-channel-time-too-long-on-duty-ch.patch
new file mode 100644
index 0000000..a9b41f9
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0112-mtk-mt76-mt7996-ACS-channel-time-too-long-on-duty-ch.patch
@@ -0,0 +1,40 @@
+From 43de0e905d116dc16cf7cacadeddcc352695913d Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 22 Feb 2024 11:09:10 +0800
+Subject: [PATCH 112/199] mtk: mt76: mt7996: ACS channel time too long on duty
+ channel
+
+This problem happens in SW scan and was already fixed.
+(https://gerrit.mediatek.inc/c/gateway/WiFi7/mac80211/mt76/+/8312969)
+
+This commit applys same solution for HW scan.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ mt7996/main.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 7012aa96..c4eba315 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -499,6 +499,7 @@ static void ___mt7996_set_channel(struct mt7996_phy *phy,
+ 	struct mt76_phy *mphy = phy->mt76;
+ 	bool offchannel = phy->scan_chan != NULL;
+ 	int timeout = HZ / 5;
++	unsigned long was_scanning = ieee80211_get_scanning(mphy->hw);
+ 
+ 	wait_event_timeout(mdev->tx_wait, !mt76_has_tx_pending(mphy), timeout);
+ 	mt76_update_survey(mphy);
+@@ -513,7 +514,7 @@ static void ___mt7996_set_channel(struct mt7996_phy *phy,
+ 	if (!offchannel)
+ 		mphy->main_chan = chandef->chan;
+ 
+-	if (chandef->chan != mphy->main_chan)
++	if (chandef->chan != mphy->main_chan || was_scanning)
+ 		memset(mphy->chan_state, 0, sizeof(*mphy->chan_state));
+ }
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0112-wifi-mt76-mt7996-Fix-get_txpower-wrong-result-in-sin.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0112-wifi-mt76-mt7996-Fix-get_txpower-wrong-result-in-sin.patch
deleted file mode 100644
index 5471917..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0112-wifi-mt76-mt7996-Fix-get_txpower-wrong-result-in-sin.patch
+++ /dev/null
@@ -1,63 +0,0 @@
-From e993c3e5aecb0d86609ebda85608bf84547b7ca7 Mon Sep 17 00:00:00 2001
-From: Allen Ye <allen.ye@mediatek.com>
-Date: Mon, 8 Apr 2024 16:56:09 +0800
-Subject: [PATCH 112/116] wifi: mt76: mt7996: Fix get_txpower wrong result in
- single wiphy and legacy mode
-
-Fix get_txpower wrong result in single wiphy and legacy mode.
-ieee80211_hw is get from wiphy0, so we need to get correct phy from vif.
-
-Temporarily use link 0 bss due to mac80211 didn't pass link id here.
----
- mt7996/main.c | 28 +++++++++++++++++++++++++++-
- 1 file changed, 27 insertions(+), 1 deletion(-)
-
-diff --git a/mt7996/main.c b/mt7996/main.c
-index d1f2c25..dcb0b07 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -975,6 +975,32 @@ out:
- 	mutex_unlock(&dev->mt76.mutex);
- }
- 
-+int mt7996_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-+		       int *dbm)
-+{
-+	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
-+	struct mt7996_bss_conf *mconf;
-+	struct mt7996_dev *dev = mt7996_hw_dev(hw);
-+	struct mt76_phy *mphy;
-+	int delta;
-+
-+	mutex_lock(&dev->mt76.mutex);
-+	mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
-+	if (!mconf || !mconf->phy) {
-+		*dbm = 0;
-+		goto out;
-+	}
-+
-+	mphy = mconf->phy->mt76;
-+
-+	delta = mt76_tx_power_nss_delta(hweight16(mphy->chainmask));
-+
-+	*dbm = DIV_ROUND_UP(mphy->txpower_cur + delta, 2);
-+out:
-+	mutex_unlock(&dev->mt76.mutex);
-+	return 0;
-+}
-+
- static void
- mt7996_channel_switch_beacon(struct ieee80211_hw *hw,
- 			     struct ieee80211_vif *vif,
-@@ -2635,7 +2661,7 @@ const struct ieee80211_ops mt7996_ops = {
- 	.hw_scan = mt7996_hw_scan,
- 	.cancel_hw_scan = mt7996_cancel_hw_scan,
- 	.release_buffered_frames = mt76_release_buffered_frames,
--	.get_txpower = mt76_get_txpower,
-+	.get_txpower = mt7996_get_txpower,
- 	.channel_switch_beacon = mt7996_channel_switch_beacon,
- 	.get_stats = mt7996_get_stats,
- 	.get_et_sset_count = mt7996_get_et_sset_count,
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0113-mtk-mt76-mt7996-add-beacon-monitoring-in-driver-for-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0113-mtk-mt76-mt7996-add-beacon-monitoring-in-driver-for-.patch
new file mode 100644
index 0000000..3989bb4
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0113-mtk-mt76-mt7996-add-beacon-monitoring-in-driver-for-.patch
@@ -0,0 +1,433 @@
+From c3e35c17c5b42dd2332864d6bed15398f47d76eb Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 20 Feb 2024 10:08:01 +0800
+Subject: [PATCH 113/199] mtk: mt76: mt7996: add beacon monitoring in driver
+ for mlo
+
+Add beacon monitoring in driver since mac80211 does not
+support connect monitoring if WIPHY_FLAG_SUPPORTS_MLO is set.
+(IEEE80211_HW_CONNECTION_MONITOR should be set)
+
+Add probing mechanism (send nullfunc) when beacon loss is detected.
+Note that probe request is not implemented yet.
+Fix beacon monitor deadlock issue between the mutex lock of
+mt7996_event_callback & mt7996_beacon_mon_work
+
+fix call trace during STA wifi down/up
+remove chanctx will be called before canceling beacon mon work
+Therefore, check phy->chanctx exist or not
+
+1. Refactor beacon monitor to mld level.
+2. Trigger disconnection only when all valid links are lost.
+3. Add temporary resume link monitor mechanism by simply receiving beacon
+from the lost link.
+4. Change from cancel_delayed_work_sync to cancel_delayed_work.
+The beacon work will canceled only when sta is disconnected or disabled,
+so it is unnecessary to finish the work.
+5. Add address sanity check before sending nullfunc.
+6. Add cancel_delayed_work in more place.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/mac.c    | 217 +++++++++++++++++++++++++++++++++++++++++++++++-
+ mt7996/main.c   |  61 ++++++++++++++
+ mt7996/mt7996.h |  13 +++
+ 3 files changed, 290 insertions(+), 1 deletion(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 3b3aa01b..803f7ce3 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -559,6 +559,32 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ 			 */
+ 			if (ieee80211_has_a4(fc) && is_mesh && status->amsdu)
+ 				*qos &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
++		} else if (ieee80211_is_beacon(fc)) {
++			struct ieee80211_hw *hw = phy->mt76->hw;
++			struct ieee80211_sta *sta;
++			struct mt7996_sta *msta;
++			unsigned int link_id = 0;
++
++			sta = ieee80211_find_sta_by_link_addrs(hw, hdr->addr2, NULL, &link_id);
++			if (!sta)
++				sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr2, NULL);
++
++			if (sta) {
++				msta = (struct mt7996_sta *)sta->drv_priv;
++				if (msta && msta->vif) {
++					msta->vif->beacon_received_time[band_idx] = jiffies;
++					/* FIXME: This is a temporary workaround.
++					 * Lost links should be resumed via TTLM or
++					 * link reconfig.
++					 */
++					if (msta->vif->lost_links & BIT(link_id)) {
++						msta->vif->lost_links &= ~BIT(link_id);
++						wiphy_info(hw->wiphy,
++							   "link %d: resume beacon monitoring\n",
++							   link_id);
++					}
++				}
++			}
+ 		}
+ #ifdef CONFIG_MTK_VENDOR
+ 		if (phy->amnt_ctrl.enable && !ieee80211_is_beacon(fc))
+@@ -709,7 +735,8 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
+ 	}
+ 
+ 	if (ieee80211_vif_is_mld(info->control.vif) &&
+-	    (multicast || unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE))))
++	    (multicast || unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE)) ||
++	     info->flags & IEEE80211_TX_CTL_INJECTED))
+ 		txwi[5] |= cpu_to_le32(MT_TXD5_FL);
+ }
+ 
+@@ -1194,6 +1221,10 @@ mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid,
+ 	if (le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT) == 0) {
+ 		skb = mt76_tx_status_skb_get(mdev, wcid, pid, &list);
+ 		if (skb) {
++			struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
++			struct mt7996_link_sta *mlink = wcid_to_mlink(wcid);
++			struct mt7996_vif *mvif;
++
+ 			info = IEEE80211_SKB_CB(skb);
+ 			if (!(txs & MT_TXS0_ACK_ERROR_MASK))
+ 				info->flags |= IEEE80211_TX_STAT_ACK;
+@@ -1203,6 +1234,18 @@ mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid,
+ 				!!(info->flags & IEEE80211_TX_STAT_ACK);
+ 
+ 			info->status.rates[0].idx = -1;
++
++			/* connection monitoring */
++			if (mlink && mlink->sta)
++				mvif = mlink->sta->vif;
++			if (ieee80211_is_nullfunc(hdr->frame_control) && mvif &&
++			    mvif->probe[wcid->phy_idx] == (void *)skb &&
++			    info->flags & IEEE80211_TX_STAT_ACK) {
++				/* reset beacon monitoring */
++				mvif->probe[wcid->phy_idx] = NULL;
++				mvif->beacon_received_time[wcid->phy_idx] = jiffies;
++				mvif->probe_send_count[wcid->phy_idx] = 0;
++			}
+ 		}
+ 	}
+ 
+@@ -2941,6 +2984,178 @@ void mt7996_scan_work(struct work_struct *work)
+ 	ieee80211_queue_delayed_work(hw, &phy->scan_work, duration);
+ }
+ 
++static int
++mt7996_beacon_mon_send_probe(struct mt7996_phy *phy, struct mt7996_vif *mvif,
++			     struct ieee80211_bss_conf *conf, unsigned int link_id)
++{
++	struct ieee80211_vif *vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv);
++	struct ieee80211_hw *hw = mvif->hw;
++	struct mt7996_link_sta *mlink;
++	struct ieee80211_tx_info *info;
++	struct sk_buff *skb;
++	int ret = 0, band_idx = phy->mt76->band_idx;
++	int band;
++
++	rcu_read_lock();
++
++	mlink = rcu_dereference(mvif->sta.link[link_id]);
++	if (!mlink || mlink->wcid.phy_idx != band_idx) {
++		ret = -EINVAL;
++		goto unlock;
++	}
++
++	if (!ieee80211_hw_check(hw, REPORTS_TX_ACK_STATUS)) {
++		/* probe request is not supported yet */
++		ret = -EOPNOTSUPP;
++		goto unlock;
++	}
++
++	/* FIXME: bss conf should not be all-zero before beacon mon work is canecled */
++	if (!is_valid_ether_addr(conf->bssid) ||
++	    !is_valid_ether_addr(conf->addr)) {
++		/* invalid address */
++		ret = -EINVAL;
++		goto unlock;
++	}
++
++	skb = ieee80211_nullfunc_get(hw, vif, link_id, false);
++	if (!skb) {
++		ret = -ENOMEM;
++		goto unlock;
++	}
++
++	info = IEEE80211_SKB_CB(skb);
++	/* frame injected by driver */
++	info->flags |= (IEEE80211_TX_CTL_REQ_TX_STATUS |
++			IEEE80211_TX_CTL_INJECTED |
++			IEEE80211_TX_CTL_NO_PS_BUFFER);
++	if (ieee80211_vif_is_mld(vif))
++		info->control.flags |= u32_encode_bits(link_id, IEEE80211_TX_CTRL_MLO_LINK);
++
++	if (phy->chanctx)
++		band = phy->chanctx->chandef.chan->band;
++	else
++		band = phy->mt76->chandef.chan->band;
++
++	skb_set_queue_mapping(skb, IEEE80211_AC_VO);
++	if (!ieee80211_tx_prepare_skb(hw, vif, skb, band, NULL)) {
++		rcu_read_unlock();
++		ieee80211_free_txskb(hw, skb);
++		return -EINVAL;
++	}
++
++	local_bh_disable();
++	mt76_tx(phy->mt76, NULL, &mlink->wcid, skb);
++	local_bh_enable();
++
++	mvif->probe[band_idx] = (void *)skb;
++	mvif->probe_send_count[band_idx]++;
++	mvif->probe_send_time[band_idx] = jiffies;
++
++unlock:
++	rcu_read_unlock();
++	return ret;
++}
++
++void mt7996_beacon_mon_work(struct work_struct *work)
++{
++	struct mt7996_vif *mvif = container_of(work, struct mt7996_vif, beacon_mon_work.work);
++	struct ieee80211_vif *vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv);
++	struct ieee80211_hw *hw = mvif->hw;
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	unsigned long next_time = ULONG_MAX, valid_links = vif->valid_links ?: BIT(0);
++	unsigned int link_id;
++	enum monitor_state {
++		MON_STATE_BEACON_MON,
++		MON_STATE_SEND_PROBE,
++		MON_STATE_LINK_LOST,
++		MON_STATE_DISCONN,
++	};
++
++	mutex_lock(&dev->mt76.mutex);
++
++	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++		struct ieee80211_bss_conf *conf;
++		struct mt7996_bss_conf *mconf;
++		struct mt7996_phy *phy;
++		unsigned long timeout, loss_duration;
++		int band_idx;
++		enum monitor_state state = MON_STATE_BEACON_MON;
++
++		conf = link_conf_dereference_protected(vif, link_id);
++		mconf = mconf_dereference_protected(mvif, link_id);
++		if (!conf || !mconf)
++			continue;
++
++		/* skip lost links */
++		if (mvif->lost_links & BIT(link_id))
++			continue;
++
++		phy = mconf->phy;
++		band_idx = phy->mt76->band_idx;
++		if (mvif->probe[band_idx]) {
++			loss_duration = msecs_to_jiffies(MT7996_MAX_PROBE_TIMEOUT);
++			timeout = mvif->probe_send_time[band_idx] + loss_duration;
++			if (time_after_eq(jiffies, timeout)) {
++				if (mvif->probe_send_count[band_idx] == MT7996_MAX_PROBE_TRIES)
++					state = MON_STATE_LINK_LOST;
++				else
++					state = MON_STATE_SEND_PROBE;
++			}
++		} else {
++			loss_duration = msecs_to_jiffies(MT7996_MAX_BEACON_LOSS *
++							 conf->beacon_int);
++			timeout = mvif->beacon_received_time[band_idx] + loss_duration;
++			if (time_after_eq(jiffies, timeout)) {
++				wiphy_info(hw->wiphy, "link %d: detect %d beacon loss\n",
++					   link_id, MT7996_MAX_BEACON_LOSS);
++				state = MON_STATE_SEND_PROBE;
++			}
++		}
++
++		switch (state) {
++		case MON_STATE_BEACON_MON:
++			break;
++		case MON_STATE_SEND_PROBE:
++			if (!mt7996_beacon_mon_send_probe(phy, mvif, conf, link_id)) {
++				timeout = MT7996_MAX_PROBE_TIMEOUT +
++					  mvif->probe_send_time[band_idx];
++				wiphy_info(hw->wiphy,
++					   "link %d: send nullfunc to AP %pM, try %d/%d\n",
++					   link_id, conf->bssid,
++					   mvif->probe_send_count[band_idx],
++					   MT7996_MAX_PROBE_TRIES);
++				break;
++			}
++			fallthrough;
++		case MON_STATE_LINK_LOST:
++			mvif->lost_links |= BIT(link_id);
++			wiphy_info(hw->wiphy,
++				   "link %d: %s to AP %pM, stop monitoring the lost link\n",
++				   link_id,
++				   state == MON_STATE_LINK_LOST ? "No ack for nullfunc frame" :
++								  "Failed to send nullfunc frame",
++				   conf->bssid);
++			mvif->probe[band_idx] = NULL;
++			mvif->probe_send_count[band_idx] = 0;
++			/* TODO: disable single link TX via TTLM/link reconfig for MLD */
++			if (mvif->lost_links != valid_links)
++				break;
++			fallthrough;
++		case MON_STATE_DISCONN:
++		default:
++			mutex_unlock(&dev->mt76.mutex);
++			wiphy_info(hw->wiphy, "all links are lost, disconnecting\n");
++			ieee80211_connection_loss(vif);
++			return;
++		}
++		next_time = min(next_time, timeout - jiffies);
++	}
++	mutex_unlock(&dev->mt76.mutex);
++
++	ieee80211_queue_delayed_work(hw, &mvif->beacon_mon_work, next_time);
++}
++
+ void mt7996_get_hw(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 phy_idx,
+ 		   struct ieee80211_hw **hw)
+ {
+diff --git a/mt7996/main.c b/mt7996/main.c
+index c4eba315..155318b3 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -452,6 +452,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ 	    is_zero_ether_addr(vif->addr))
+ 		phy->monitor_vif = vif;
+ 
++	INIT_DELAYED_WORK(&mvif->beacon_mon_work, mt7996_beacon_mon_work);
+ 	mvif->dev = dev;
+ 	mvif->hw = hw;
+ 	mvif->sta.vif = mvif;
+@@ -475,6 +476,7 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 
+ 	cancel_delayed_work_sync(&phy->scan_work);
++	cancel_delayed_work(&mvif->beacon_mon_work);
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+@@ -1146,6 +1148,9 @@ mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	unsigned int link_id;
+ 
++	if (!ieee80211_vif_is_mld(vif) || rem == sta->valid_links)
++		cancel_delayed_work(&mvif->beacon_mon_work);
++
+ 	for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
+ 		struct mt7996_bss_conf *mconf =
+ 			mconf_dereference_protected(mvif, link_id);
+@@ -2584,6 +2589,61 @@ out:
+ 	return ret;
+ }
+ 
++static void
++mt7996_event_callback(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++		      const struct ieee80211_event *event)
++{
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	int i;
++
++	switch (event->type) {
++	case MLME_EVENT:
++		if (event->u.mlme.data == ASSOC_EVENT &&
++		    event->u.mlme.status == MLME_SUCCESS) {
++			struct ieee80211_bss_conf *conf;
++			struct mt7996_bss_conf *mconf;
++			struct mt7996_phy *phy;
++			unsigned long cur, valid_links = vif->valid_links ?: BIT(0);
++			unsigned int link_id;
++			int next_time = INT_MAX;
++
++			mutex_lock(&dev->mt76.mutex);
++			cur = jiffies;
++			for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++				conf = link_conf_dereference_protected(vif, link_id);
++				mconf = mconf_dereference_protected(mvif, link_id);
++
++				if (!conf || !mconf)
++					continue;
++
++				phy = mconf->phy;
++				mvif->beacon_received_time[phy->mt76->band_idx] = cur;
++				next_time = min(next_time,
++						MT7996_MAX_BEACON_LOSS *
++						conf->beacon_int);
++			}
++
++			ieee80211_queue_delayed_work(hw, &mvif->beacon_mon_work,
++						     msecs_to_jiffies(next_time));
++			mutex_unlock(&dev->mt76.mutex);
++			break;
++		}
++
++		mutex_lock(&dev->mt76.mutex);
++		memset(mvif->probe_send_count, 0, sizeof(mvif->probe_send_count));
++		for (i = 0; i < __MT_MAX_BAND; i++)
++			mvif->probe[i] = NULL;
++		mvif->lost_links = 0;
++		mutex_unlock(&dev->mt76.mutex);
++
++		cancel_delayed_work(&mvif->beacon_mon_work);
++		break;
++	default:
++		break;
++	}
++}
++
+ const struct ieee80211_ops mt7996_ops = {
+ 	.add_chanctx = ieee80211_emulate_add_chanctx,
+ 	.remove_chanctx = ieee80211_emulate_remove_chanctx,
+@@ -2640,6 +2700,7 @@ const struct ieee80211_ops mt7996_ops = {
+ 	.net_fill_forward_path = mt7996_net_fill_forward_path,
+ 	.net_setup_tc = mt76_wed_net_setup_tc,
+ #endif
++	.event_callback = mt7996_event_callback,
+ 	.add_chanctx = mt7996_add_chanctx,
+ 	.remove_chanctx = mt7996_remove_chanctx,
+ 	.change_chanctx = mt7996_change_chanctx,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 294c46f6..40ee8949 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -129,6 +129,10 @@
+ 
+ #define to_rssi(field, rcpi)	((FIELD_GET(field, rcpi) - 220) / 2)
+ 
++#define MT7996_MAX_BEACON_LOSS		20
++#define MT7996_MAX_PROBE_TIMEOUT	500
++#define MT7996_MAX_PROBE_TRIES		2
++
+ struct mt7996_vif;
+ struct mt7996_sta;
+ struct mt7996_dfs_pulse;
+@@ -361,6 +365,14 @@ struct mt7996_vif {
+ 	u8 mld_remap_id;
+ 
+ 	u8 band_to_link[__MT_MAX_BAND];
++
++	/* for beacon monitoring */
++	struct delayed_work beacon_mon_work;
++	unsigned long beacon_received_time[__MT_MAX_BAND];
++	u16 lost_links;
++	void *probe[__MT_MAX_BAND];
++	unsigned long probe_send_time[__MT_MAX_BAND];
++	int probe_send_count[__MT_MAX_BAND];
+ };
+ 
+ /* crash-dump */
+@@ -1117,6 +1129,7 @@ bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len);
+ void mt7996_stats_work(struct work_struct *work);
+ void mt7996_scan_work(struct work_struct *work);
+ void mt7996_scan_complete(struct mt7996_phy *phy, bool aborted);
++void mt7996_beacon_mon_work(struct work_struct *work);
+ int mt76_dfs_start_rdd(struct mt7996_dev *dev, bool force);
+ int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy);
+ void mt7996_set_stream_he_eht_caps(struct mt7996_phy *phy);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0113-mtk-wifi-mt76-mt7996-add-beacon_int_min_gcd-to-suppo.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0113-mtk-wifi-mt76-mt7996-add-beacon_int_min_gcd-to-suppo.patch
deleted file mode 100644
index 273a130..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0113-mtk-wifi-mt76-mt7996-add-beacon_int_min_gcd-to-suppo.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From fde0acbc9ad0c869da1a075c408946ba1e7b91d1 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Fri, 19 Apr 2024 11:01:21 +0800
-Subject: [PATCH 113/116] mtk: wifi: mt76: mt7996: add beacon_int_min_gcd to
- support different beacon interval
-
-When beacon_int_min_gcd is zero, all beacon intervals for different
-interfaces should be same. If beacon_int_min_gcd is larger than zero,
-all beacon intervals for different interfaces should be larger or
-equal than beacon_int_min_gcd.
-
-Without this patch, set beacon fail when different interfaces use
-different beacon interval.
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- mt7996/init.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 0dee659..3af9d02 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -41,6 +41,7 @@ static const struct ieee80211_iface_combination if_comb[] = {
- 				       BIT(NL80211_CHAN_WIDTH_40) |
- 				       BIT(NL80211_CHAN_WIDTH_80) |
- 				       BIT(NL80211_CHAN_WIDTH_160),
-+		.beacon_int_min_gcd = 100,
- 	}
- };
- 
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0114-mtk-mt76-mt7996-support-band_idx-option-for-set_mu-g.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0114-mtk-mt76-mt7996-support-band_idx-option-for-set_mu-g.patch
new file mode 100644
index 0000000..014431b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0114-mtk-mt76-mt7996-support-band_idx-option-for-set_mu-g.patch
@@ -0,0 +1,178 @@
+From efb6ebd878b7926828f0315622fcbdf98abe6b6b Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 4 Mar 2024 16:21:16 +0800
+Subject: [PATCH 114/199] mtk: mt76: mt7996: support band_idx option for
+ set_mu/get_mu vendor command
+
+The vendor command set_mu and get_mu should be executed with band_idx.
+With band_idx, driver can access the corrsponding phy by band_idx.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt7996/mcu.c    | 26 +++++++++++++++++++-------
+ mt7996/mcu.h    |  1 +
+ mt7996/vendor.c | 40 +++++++++++++++++++++++++++++++++++-----
+ mt7996/vendor.h |  1 +
+ 4 files changed, 56 insertions(+), 12 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 7b0d631e..598f32ba 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -5838,12 +5838,23 @@ int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy)
+ #ifdef CONFIG_MTK_VENDOR
+ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ {
+-	u8 mode, val;
++	u8 mode, val, band_idx;
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct mt7996_phy *phy =  mvif->deflink.phy;
++	struct mt7996_phy *phy;
++	struct mt76_phy *mphy;
+ 
+ 	mode = FIELD_GET(RATE_CFG_MODE, *((u32 *)data));
+ 	val = FIELD_GET(RATE_CFG_VAL, *((u32 *)data));
++	band_idx = FIELD_GET(RATE_CFG_BAND_IDX, *((u32 *)data));
++
++	if (!mt7996_band_valid(mvif->dev, band_idx))
++		goto error;
++
++	mphy = mvif->dev->mt76.phys[band_idx];
++	if (!mphy)
++		goto error;
++
++	phy = (struct mt7996_phy *)mphy->priv;
+ 
+ 	switch (mode) {
+ 	case RATE_PARAM_FIXED_OFDMA:
+@@ -5859,13 +5870,14 @@ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ 			phy->muru_onoff |= MUMIMO_UL;
+ 		break;
+ 	case RATE_PARAM_AUTO_MU:
+-		if (val < 0 || val > 15) {
+-			printk("Wrong value! The value is between 0-15.\n");
+-			break;
+-		}
+-		phy->muru_onoff = val;
++		phy->muru_onoff = val & GENMASK(3, 0);
+ 		break;
+ 	}
++
++	return;
++error:
++	dev_err(mvif->dev->mt76.dev, "Invalid band_idx to config\n");
++	return;
+ }
+ 
+ void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 23d44cd3..9903d88e 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -882,6 +882,7 @@ enum {
+ #endif
+ };
+ 
++#define RATE_CFG_BAND_IDX	GENMASK(17, 16)
+ #define RATE_CFG_MODE	GENMASK(15, 8)
+ #define RATE_CFG_VAL	GENMASK(7, 0)
+ 
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index 31688c37..64ef5515 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -16,6 +16,7 @@ mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
+ 	[MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_MU_CTRL_STRUCT] = {.type = NLA_BINARY },
++	[MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX] = {.type = NLA_U8 },
+ };
+ 
+ static const struct nla_policy
+@@ -135,7 +136,7 @@ static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mt7996_muru *muru;
+ 	int err;
+-	u8 val8;
++	u8 val8, band_idx;
+ 	u32 val32 = 0;
+ 
+ 	err = nla_parse(tb, MTK_VENDOR_ATTR_MU_CTRL_MAX, data, data_len,
+@@ -143,10 +144,13 @@ static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
+ 	if (err)
+ 		return err;
+ 
+-	if (tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF]) {
++	if (tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF] &&
++	    tb[MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX]) {
+ 		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF]);
++		band_idx = nla_get_u8(tb[MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX]);
+ 		val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_AUTO_MU) |
+-			 FIELD_PREP(RATE_CFG_VAL, val8);
++			 FIELD_PREP(RATE_CFG_VAL, val8) |
++			 FIELD_PREP(RATE_CFG_BAND_IDX, band_idx);
+ 		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+ 							   mt7996_set_wireless_vif, &val32);
+ 	} else if (tb[MTK_VENDOR_ATTR_MU_CTRL_STRUCT]) {
+@@ -168,18 +172,44 @@ mt7996_vendor_mu_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
+ 			   unsigned long *storage)
+ {
+ 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+-	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+-	int len = 0;
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mt7996_phy *phy;
++	struct mt76_phy *mphy;
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_MU_CTRL];
++	int len = 0, err;
++	u8 band_idx;
+ 
+ 	if (*storage == 1)
+ 		return -ENOENT;
+ 	*storage = 1;
+ 
++	err = nla_parse(tb, MTK_VENDOR_ATTR_MU_CTRL_MAX, data, data_len,
++			mu_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	if (!tb[MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX])
++		return -EINVAL;
++
++	band_idx = nla_get_u8(tb[MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX]);
++	if (!mt7996_band_valid(dev, band_idx))
++		goto error;
++
++	mphy = dev->mt76.phys[band_idx];
++	if (!mphy)
++		goto error;
++
++	phy = (struct mt7996_phy *)mphy->priv;
++
+ 	if (nla_put_u8(skb, MTK_VENDOR_ATTR_MU_CTRL_DUMP, phy->muru_onoff))
+ 		return -ENOMEM;
+ 	len += 1;
+ 
+ 	return len;
++
++error:
++	dev_err(dev->mt76.dev, "Invalid band idx to dump\n");
++	return -EINVAL;
+ }
+ 
+ void mt7996_set_wireless_rts_sigta(struct ieee80211_hw *hw, u8 value) {
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index 0d1ef322..32346775 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -68,6 +68,7 @@ enum mtk_vendor_attr_mu_ctrl {
+ 	MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
+ 	MTK_VENDOR_ATTR_MU_CTRL_DUMP,
+ 	MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
++	MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0114-mtk-wifi-mt76-mt7996-Add-connac3-csi-feature.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0114-mtk-wifi-mt76-mt7996-Add-connac3-csi-feature.patch
deleted file mode 100644
index 15ec86c..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0114-mtk-wifi-mt76-mt7996-Add-connac3-csi-feature.patch
+++ /dev/null
@@ -1,1089 +0,0 @@
-From e8ef34a080c0c243559a63d029376436ddf11ac8 Mon Sep 17 00:00:00 2001
-From: mtk20656 <chank.chen@mediatek.com>
-Date: Sat, 20 Jan 2024 12:03:24 +0800
-Subject: [PATCH 114/116] mtk: wifi: mt76: mt7996: Add connac3 csi feature.
-
-1. format align to wifi6.
-2. add bw320 support.
-3. add active mode.
-
-Signed-off-by: mtk20656 <chank.chen@mediatek.com>
----
- mt76_connac_mcu.h |   2 +
- mt7996/init.c     |  22 +++
- mt7996/main.c     |   3 +
- mt7996/mcu.c      | 465 ++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/mcu.h      | 105 +++++++++++
- mt7996/mt7996.h   |  55 ++++++
- mt7996/vendor.c   | 208 +++++++++++++++++++++
- mt7996/vendor.h   |  50 +++++
- 8 files changed, 910 insertions(+)
-
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 1c8e503..daaf87f 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1053,6 +1053,7 @@ enum {
- 	MCU_UNI_EVENT_THERMAL = 0x35,
- 	MCU_UNI_EVENT_NIC_CAPAB = 0x43,
- 	MCU_UNI_EVENT_TESTMODE_CTRL = 0x46,
-+	MCU_UNI_EVENT_CSI_REPORT = 0x4A,
- 	MCU_UNI_EVENT_WED_RRO = 0x57,
- 	MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
- 	MCU_UNI_EVENT_ALL_STA_INFO = 0x6e,
-@@ -1290,6 +1291,7 @@ enum {
- 	MCU_UNI_CMD_TESTMODE_TRX_PARAM = 0x42,
- 	MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
- 	MCU_UNI_CMD_PRECAL_RESULT = 0x47,
-+	MCU_UNI_CMD_CSI_CTRL = 0x4A,
- 	MCU_UNI_CMD_THERMAL_CAL = 0x4c,
- 	MCU_UNI_CMD_RRO = 0x57,
- 	MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 3af9d02..ff72aab 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -806,6 +806,24 @@ error:
- 	return ret;
- }
- 
-+#ifdef CONFIG_MTK_VENDOR
-+static int mt7996_unregister_csi(struct mt7996_phy *phy)
-+{
-+	struct csi_data *c, *tmp_c;
-+
-+	spin_lock_bh(&phy->csi.lock);
-+	phy->csi.enable = 0;
-+
-+	list_for_each_entry_safe(c, tmp_c, &phy->csi.list, node) {
-+		list_del(&c->node);
-+		kfree(c);
-+	}
-+	spin_unlock_bh(&phy->csi.lock);
-+
-+	return 0;
-+}
-+#endif
-+
- static void
- mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
- {
-@@ -817,6 +835,10 @@ mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
- 	/* TODO: FIXME: temp for single wiphy support */
- 	phy->mt76->hw = phy->mt76->ori_hw;
- 
-+#ifdef CONFIG_MTK_VENDOR
-+	mt7996_unregister_csi(phy);
-+#endif
-+
- 	mt7996_unregister_thermal(phy);
- 
- 	mphy = phy->dev->mt76.phys[band];
-diff --git a/mt7996/main.c b/mt7996/main.c
-index dcb0b07..35f2495 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -1292,6 +1292,9 @@ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
- 	unsigned long rem = sta->valid_links ?: BIT(0);
- 
-+#ifdef CONFIG_MTK_VENDOR
-+	mt7996_mcu_set_csi(&dev->phy, 2, 8, 1, 0, sta->addr);
-+#endif
- 	mt7996_mac_sta_remove_links(dev, vif, sta, rem);
- }
- 
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 3f51c28..6c9ec31 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -653,6 +653,263 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
- 	}
- }
- 
-+static int
-+csi_integrate_segment_data(struct mt7996_phy *phy, struct csi_data *csi)
-+{
-+	struct csi_data *csi_temp = NULL;
-+
-+	if (csi->segment_num == 0 && csi->remain_last == 0)
-+		return CSI_CHAIN_COMPLETE;
-+	else if (csi->segment_num == 0 && csi->remain_last == 1) {
-+		memcpy(&phy->csi.buffered_csi,
-+		       csi, sizeof(struct csi_data));
-+
-+		return CSI_CHAIN_SEGMENT_FIRST;
-+	} else if (csi->segment_num != 0) {
-+		csi_temp = &phy->csi.buffered_csi;
-+		if (csi->chain_info != csi_temp->chain_info ||
-+		csi->segment_num != (csi_temp->segment_num + 1))
-+			return CSI_CHAIN_SEGMENT_ERR;
-+
-+		memcpy(&csi_temp->data_i[csi_temp->data_num],
-+		       csi->data_i, csi->data_num * sizeof(s16));
-+
-+		memcpy(&csi_temp->data_q[csi_temp->data_num],
-+		       csi->data_q, csi->data_num * sizeof(s16));
-+
-+		csi_temp->data_num += csi->data_num;
-+		csi_temp->segment_num = csi->segment_num;
-+		csi_temp->remain_last = csi->remain_last;
-+
-+		if (csi->remain_last == 0)
-+			return CSI_CHAIN_SEGMENT_LAST;
-+		else if (csi->remain_last == 1)
-+			return CSI_CHAIN_SEGMENT_MIDDLE;
-+	}
-+
-+	return CSI_CHAIN_ERR;
-+}
-+
-+static int
-+mt7996_mcu_csi_report_data(struct mt7996_phy *phy, u8 *tlv_buf, u32 len)
-+{
-+	int ret, i;
-+	struct csi_data *current_csi;
-+	struct csi_data *target_csi;
-+	struct csi_tlv *tlv_data;
-+	u8 *buf_tmp;
-+	u32 rx_info, tx_rx_idx;
-+	u32 buf_len_last, offset;
-+
-+	buf_tmp = tlv_buf;
-+	buf_len_last = len;
-+	offset = sizeof(((struct csi_tlv *)0)->basic);
-+
-+	current_csi = kzalloc(sizeof(*current_csi), GFP_KERNEL);
-+	if (!current_csi)
-+		return -ENOMEM;
-+
-+	while (buf_len_last >= offset) {
-+		u32 tag, len;
-+		s16 *data_tmp = NULL;
-+
-+		tlv_data = (struct csi_tlv *)buf_tmp;
-+		tag = le32_to_cpu(tlv_data->basic.tag);
-+		len = le32_to_cpu(tlv_data->basic.len);
-+
-+		switch (tag) {
-+		case CSI_EVENT_FW_VER:
-+			current_csi->fw_ver = le32_to_cpu(tlv_data->info);
-+			break;
-+		case CSI_EVENT_CBW:
-+			current_csi->ch_bw = le32_to_cpu(tlv_data->info);
-+			break;
-+		case CSI_EVENT_RSSI:
-+			current_csi->rssi = le32_to_cpu(tlv_data->info);
-+			break;
-+		case CSI_EVENT_SNR:
-+			current_csi->snr = le32_to_cpu(tlv_data->info);
-+			break;
-+		case CSI_EVENT_BAND:
-+			current_csi->band = le32_to_cpu(tlv_data->info);
-+
-+			if (current_csi->band != phy->mt76->band_idx) {
-+				kfree(current_csi);
-+				return -EINVAL;
-+			}
-+
-+			break;
-+		case CSI_EVENT_CSI_NUM:
-+			current_csi->data_num = le32_to_cpu(tlv_data->info);
-+
-+			if (current_csi->data_num > CSI_BW80_DATA_COUNT) {
-+				kfree(current_csi);
-+				return -EINVAL;
-+			}
-+
-+			break;
-+		case CSI_EVENT_CSI_I_DATA:
-+			if (len != sizeof(s16) * current_csi->data_num) {
-+				kfree(current_csi);
-+				return -EINVAL;
-+			}
-+
-+			data_tmp = tlv_data->data;
-+			for (i = 0; i < current_csi->data_num; i++)
-+				current_csi->data_i[i] = le16_to_cpu(*(data_tmp + i));
-+			break;
-+		case CSI_EVENT_CSI_Q_DATA:
-+			if (len != sizeof(s16) * current_csi->data_num) {
-+				kfree(current_csi);
-+				return -EINVAL;
-+			}
-+
-+			data_tmp = tlv_data->data;
-+			for (i = 0; i < current_csi->data_num; i++)
-+				current_csi->data_q[i] = le16_to_cpu(*(data_tmp + i));
-+			break;
-+		case CSI_EVENT_DBW:
-+			current_csi->data_bw = le32_to_cpu(tlv_data->info);
-+			break;
-+		case CSI_EVENT_CH_IDX:
-+			current_csi->pri_ch_idx = le32_to_cpu(tlv_data->info);
-+			break;
-+		case CSI_EVENT_TA:
-+			memcpy(current_csi->ta, tlv_data->mac, ETH_ALEN);
-+			break;
-+		case CSI_EVENT_EXTRA_INFO:
-+			current_csi->ext_info = le32_to_cpu(tlv_data->info);
-+			break;
-+		case CSI_EVENT_RX_MODE:
-+			rx_info = le32_to_cpu(tlv_data->info);
-+			current_csi->rx_mode = u32_get_bits(rx_info, GENMASK(15, 0));
-+			current_csi->rx_rate = u32_get_bits(rx_info, GENMASK(31, 16));
-+			break;
-+		case CSI_EVENT_H_IDX:
-+			current_csi->chain_info = le32_to_cpu(tlv_data->info);
-+			break;
-+		case CSI_EVENT_TX_RX_IDX:
-+			tx_rx_idx = le32_to_cpu(tlv_data->info);
-+			current_csi->tx_idx = u32_get_bits(tx_rx_idx, GENMASK(31, 16));
-+			current_csi->rx_idx = u32_get_bits(tx_rx_idx, GENMASK(15, 0));
-+			break;
-+		case CSI_EVENT_TS:
-+			current_csi->ts = le32_to_cpu(tlv_data->info);
-+
-+			if (phy->csi.interval &&
-+				current_csi->ts < phy->csi.last_record + phy->csi.interval) {
-+				kfree(current_csi);
-+				return 0;
-+			}
-+
-+			break;
-+		case CSI_EVENT_PKT_SN:
-+			current_csi->pkt_sn = le32_to_cpu(tlv_data->info);
-+			break;
-+		case CSI_EVENT_BW_SEG:
-+			current_csi->segment_num = le32_to_cpu(tlv_data->info);
-+			break;
-+		case CSI_EVENT_REMAIN_LAST:
-+			current_csi->remain_last = le32_to_cpu(tlv_data->info);
-+			break;
-+		case CSI_EVENT_TR_STREAM:
-+			current_csi->tr_stream = le32_to_cpu(tlv_data->info);
-+			break;
-+		default:
-+			break;
-+		};
-+
-+		buf_len_last -= (offset + len);
-+
-+		if (buf_len_last >= offset)
-+			buf_tmp += (offset + len);
-+	}
-+
-+	/* integret the bw80 segment */
-+	if (current_csi->ch_bw >= CSI_BW80) {
-+		ret = csi_integrate_segment_data(phy, current_csi);
-+
-+		switch (ret) {
-+		case CSI_CHAIN_ERR:
-+		case CSI_CHAIN_SEGMENT_ERR:
-+			kfree(current_csi);
-+			return -EINVAL;
-+			break;
-+		case CSI_CHAIN_SEGMENT_FIRST:
-+		case CSI_CHAIN_SEGMENT_MIDDLE:
-+			kfree(current_csi);
-+			return 0;
-+			break;
-+		case CSI_CHAIN_COMPLETE:
-+			target_csi = current_csi;
-+			break;
-+		case CSI_CHAIN_SEGMENT_LAST:
-+			target_csi = current_csi;
-+			memcpy(target_csi, &phy->csi.buffered_csi, sizeof(struct csi_data));
-+			memset(&phy->csi.buffered_csi, 0, sizeof(struct csi_data));
-+			break;
-+		default:
-+			break;
-+		}
-+	} else {
-+		target_csi = current_csi;
-+	}
-+
-+	/* put the csi data into list */
-+	INIT_LIST_HEAD(&target_csi->node);
-+	spin_lock_bh(&phy->csi.lock);
-+
-+	if (!phy->csi.enable) {
-+		kfree(target_csi);
-+		goto out;
-+	}
-+
-+	list_add_tail(&target_csi->node, &phy->csi.list);
-+	phy->csi.count++;
-+
-+	if (phy->csi.count > CSI_MAX_BUF_NUM) {
-+		struct csi_data *old;
-+
-+		old = list_first_entry(&phy->csi.list,
-+				       struct csi_data, node);
-+
-+		list_del(&old->node);
-+		kfree(old);
-+		phy->csi.count--;
-+	}
-+
-+	if (target_csi->chain_info & BIT(15)) /* last chain */
-+		phy->csi.last_record = target_csi->ts;
-+
-+out:
-+	spin_unlock_bh(&phy->csi.lock);
-+	return 0;
-+}
-+
-+void
-+mt7996_mcu_csi_report_event(struct mt7996_dev *dev, struct sk_buff *skb)
-+{
-+	struct mt7996_mcu_csi_event *event;
-+	struct mt76_phy *mphy;
-+	struct mt7996_phy *phy;
-+
-+	event = (struct mt7996_mcu_csi_event *)skb->data;
-+
-+	mphy = dev->mt76.phys[event->band_idx];
-+	if (!mphy)
-+		return;
-+
-+	phy = mphy->priv;
-+
-+	switch (le16_to_cpu(event->tag)) {
-+	case UNI_EVENT_CSI_DATA:
-+		mt7996_mcu_csi_report_data(phy, event->tlv_buf, le16_to_cpu(event->len) - 4);
-+		break;
-+	default:
-+		break;
-+	}
-+}
-+
- static void
- mt7996_mcu_rx_thermal_notify(struct mt7996_dev *dev, struct sk_buff *skb)
- {
-@@ -896,6 +1153,11 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
- 	case MCU_UNI_EVENT_BF:
- 		mt7996_mcu_rx_bf_event(dev, skb);
- 		break;
-+#endif
-+#ifdef CONFIG_MTK_VENDOR
-+	case MCU_UNI_EVENT_CSI_REPORT:
-+		mt7996_mcu_csi_report_event(dev, skb);
-+		break;
- #endif
- 	default:
- 		break;
-@@ -5995,4 +6257,207 @@ void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
- 
- 	mt7996_mcu_add_beacon(hw, &vif->bss_conf, &mvif->deflink, val);
- }
-+
-+static int mt7996_mcu_set_csi_enable(struct mt7996_phy *phy, u16 tag)
-+{
-+	struct {
-+		u8 band;
-+		u8 rsv1[3];
-+
-+		__le16 tag;
-+		__le16 len;
-+	} __packed req = {
-+		.band = phy->mt76->band_idx,
-+		.tag = cpu_to_le16(tag),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+	};
-+
-+	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
-+				sizeof(req), false);
-+}
-+
-+static int mt7996_mcu_set_csi_frame_type(struct mt7996_phy *phy, u16 tag, u8 type_idx, u32 type)
-+{
-+	struct {
-+		u8 band;
-+		u8 rsv1[3];
-+
-+		__le16 tag;
-+		__le16 len;
-+		u8 frame_type_idx;
-+		u8 frame_type;
-+		u8 rsv2[2];
-+	} __packed req = {
-+		.band = phy->mt76->band_idx,
-+		.tag = cpu_to_le16(tag),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.frame_type_idx = type_idx,
-+		.frame_type = type,
-+	};
-+
-+	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
-+				sizeof(req), false);
-+}
-+
-+static int mt7996_mcu_set_csi_chain_filter(struct mt7996_phy *phy, u16 tag, u8 func, u32 value)
-+{
-+	struct {
-+		u8 band;
-+		u8 rsv1[3];
-+
-+		__le16 tag;
-+		__le16 len;
-+		u8 function;
-+		u8 chain_value;
-+		u8 rsv2[2];
-+	} __packed req = {
-+		.band = phy->mt76->band_idx,
-+		.tag = cpu_to_le16(tag),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.function = func,
-+		.chain_value = value,
-+	};
-+
-+	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
-+				sizeof(req), false);
-+}
-+
-+static int mt7996_mcu_set_csi_sta_filter(struct mt7996_phy *phy, u16 tag, u32 op, u8 *sta_mac)
-+{
-+	struct {
-+		u8 band;
-+		u8 rsv1[3];
-+
-+		__le16 tag;
-+		__le16 len;
-+		u8 operation;
-+		u8 rsv2[1];
-+		u8 mac[6];
-+	} __packed req = {
-+		.band = phy->mt76->band_idx,
-+		.tag = cpu_to_le16(tag),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.operation = op,
-+	};
-+
-+	memcpy(req.mac, sta_mac, ETH_ALEN);
-+
-+	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
-+				sizeof(req), false);
-+}
-+
-+static int mt7996_mcu_set_csi_active_mode(struct mt7996_phy *phy, u16 tag,
-+					  u32 interval, u8 frame_idx, u8 subframe_idx, u32 bitmap)
-+{
-+	struct {
-+		u8 band;
-+		u8 rsv1[3];
-+
-+		__le16 tag;
-+		__le16 len;
-+		__le16 interval; /* uint: ms */
-+		u8 frame_type_idx;
-+		u8 subframe_type_idx;
-+		__le32 bitmap; /* sta wcid bitmap */
-+		u8 rsv2[4];
-+	} __packed req = {
-+		.band = phy->mt76->band_idx,
-+		.tag = cpu_to_le16(tag),
-+		.len = cpu_to_le16(sizeof(req) - 4),
-+		.interval = cpu_to_le16(interval),
-+		.frame_type_idx = frame_idx,
-+		.subframe_type_idx = subframe_idx,
-+		.bitmap = cpu_to_le32(bitmap),
-+	};
-+
-+	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
-+				sizeof(req), false);
-+}
-+
-+void mt7996_csi_wcid_bitmap_update(void *data, struct ieee80211_sta *sta)
-+{
-+	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-+	struct mt7996_phy *phy = msta->vif->deflink.phy;
-+	struct csi_bitmap_info_update *sta_info = (struct csi_bitmap_info_update *)data;
-+	u16 wcid = 0;
-+
-+#define CSI_ACTIVE_MODE_ADD 1
-+#define CSI_ACTIVE_MODE_REMOVE 0
-+
-+	if (!memcmp(sta_info->addr, sta->addr, ETH_ALEN)) {
-+		wcid = msta->deflink.wcid.idx;
-+
-+		/* active mode: only support station with wcid less than 32 */
-+		if (wcid > 32)
-+			return;
-+
-+		if (sta_info->action == CSI_ACTIVE_MODE_ADD)
-+			phy->csi.active_bitmap |= BIT(wcid);
-+		else if (sta_info->action == CSI_ACTIVE_MODE_REMOVE)
-+			phy->csi.active_bitmap &= ~(BIT(wcid));
-+	}
-+}
-+
-+int mt7996_mcu_set_csi(struct mt7996_phy *phy, u8 mode,
-+			u8 cfg, u8 v1, u32 v2, u8 *mac_addr)
-+{
-+	switch (mode) {
-+	case CSI_CONTROL_MODE_STOP:
-+		return mt7996_mcu_set_csi_enable(phy, UNI_CMD_CSI_STOP);
-+	case CSI_CONTROL_MODE_START:
-+		return mt7996_mcu_set_csi_enable(phy, UNI_CMD_CSI_START);
-+	case CSI_CONTROL_MODE_SET:
-+		switch (cfg) {
-+		case CSI_CONFIG_FRAME_TYPE:
-+			if (v2 > 255)
-+				return -EINVAL;
-+
-+			return mt7996_mcu_set_csi_frame_type(phy,
-+					UNI_CMD_CSI_SET_FRAME_TYPE, v1, v2);
-+		case CSI_CONFIG_CHAIN_FILTER:
-+			if (v2 > 255)
-+				return -EINVAL;
-+
-+			return mt7996_mcu_set_csi_chain_filter(phy,
-+					UNI_CMD_CSI_SET_CHAIN_FILTER, v1, v2);
-+		case CSI_CONFIG_STA_FILTER:
-+			if (!is_valid_ether_addr(mac_addr))
-+				return -EINVAL;
-+
-+			if (v2 > 255)
-+				return -EINVAL;
-+
-+			return mt7996_mcu_set_csi_sta_filter(phy,
-+					UNI_CMD_CSI_SET_STA_FILTER, v2, mac_addr);
-+		case CSI_CONFIG_ACTIVE_MODE:
-+			if (is_valid_ether_addr(mac_addr)) {
-+				struct csi_bitmap_info_update sta_info;
-+
-+				if (v2 > 255)
-+					return -EINVAL;
-+
-+				memcpy(sta_info.addr, mac_addr, ETH_ALEN);
-+				sta_info.action = v2;
-+
-+				ieee80211_iterate_stations_atomic(phy->mt76->hw,
-+								mt7996_csi_wcid_bitmap_update, &sta_info);
-+				return 0;
-+			} else {
-+				u8 frame_type = v1 & 0x3;
-+				u8 frame_subtype = (v1 & 0x3c) >> 2;
-+
-+					/* active mode: max interval is 3000ms */
-+					if (v2 > 3000)
-+						return -EINVAL;
-+
-+				return mt7996_mcu_set_csi_active_mode(phy, UNI_CMD_CSI_SET_ACTIVE_MODE,
-+						v2, frame_type, frame_subtype, phy->csi.active_bitmap);
-+			}
-+		default:
-+			return -EINVAL;
-+		}
-+	default:
-+		return -EINVAL;
-+	}
-+}
- #endif
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index a1ac18f..dc93fab 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -1159,4 +1159,109 @@ struct fixed_rate_table_ctrl {
- 	u8 _rsv2;
- } __packed;
- 
-+#ifdef CONFIG_MTK_VENDOR
-+struct mt7996_mcu_csi_event {
-+	struct mt7996_mcu_rxd rxd;
-+
-+	u8 band_idx;
-+	u8 _rsv[3];
-+
-+	__le16 tag;
-+	__le16 len;
-+	u8 tlv_buf[0];
-+};
-+
-+enum UNI_EVENT_CSI_TAG_T {
-+	UNI_EVENT_CSI_DATA = 0,
-+	UNI_EVENT_CSI_MAX_NUM
-+};
-+
-+struct csi_tlv {
-+	struct {
-+		__le32 tag;
-+		__le32 len;
-+	} basic;
-+	union {
-+		u8 mac[ETH_ALEN];
-+		__le32 info;
-+		s16 data[0];
-+	};
-+} __packed;
-+
-+struct csi_bitmap_info_update {
-+	u8 action;
-+	u8 addr[ETH_ALEN];
-+};
-+
-+#define CSI_MAX_BUF_NUM	3000
-+
-+enum CSI_EVENT_TLV_TAG {
-+	CSI_EVENT_FW_VER,
-+	CSI_EVENT_CBW,
-+	CSI_EVENT_RSSI,
-+	CSI_EVENT_SNR,
-+	CSI_EVENT_BAND,
-+	CSI_EVENT_CSI_NUM,
-+	CSI_EVENT_CSI_I_DATA,
-+	CSI_EVENT_CSI_Q_DATA,
-+	CSI_EVENT_DBW,
-+	CSI_EVENT_CH_IDX,
-+	CSI_EVENT_TA,
-+	CSI_EVENT_EXTRA_INFO,
-+	CSI_EVENT_RX_MODE,
-+	CSI_EVENT_RSVD1,
-+	CSI_EVENT_RSVD2,
-+	CSI_EVENT_RSVD3,
-+	CSI_EVENT_RSVD4,
-+	CSI_EVENT_H_IDX,
-+	CSI_EVENT_TX_RX_IDX,
-+	CSI_EVENT_TS,
-+	CSI_EVENT_PKT_SN,
-+	CSI_EVENT_BW_SEG,
-+	CSI_EVENT_REMAIN_LAST,
-+	CSI_EVENT_TR_STREAM,
-+	CSI_EVENT_TLV_TAG_NUM,
-+};
-+
-+enum CSI_CHAIN_TYPE {
-+	CSI_CHAIN_ERR,
-+	CSI_CHAIN_COMPLETE,
-+	CSI_CHAIN_SEGMENT_FIRST,
-+	CSI_CHAIN_SEGMENT_MIDDLE,
-+	CSI_CHAIN_SEGMENT_LAST,
-+	CSI_CHAIN_SEGMENT_ERR,
-+};
-+
-+enum CSI_CONTROL_MODE_T {
-+	CSI_CONTROL_MODE_STOP,
-+	CSI_CONTROL_MODE_START,
-+	CSI_CONTROL_MODE_SET,
-+	CSI_CONTROL_MODE_NUM
-+};
-+
-+enum CSI_CONFIG_ITEM_T {
-+	CSI_CONFIG_RSVD1,
-+	CSI_CONFIG_WF,
-+	CSI_CONFIG_RSVD2,
-+	CSI_CONFIG_FRAME_TYPE,
-+	CSI_CONFIG_TX_PATH,
-+	CSI_CONFIG_OUTPUT_FORMAT,
-+	CSI_CONFIG_INFO,
-+	CSI_CONFIG_CHAIN_FILTER,
-+	CSI_CONFIG_STA_FILTER,
-+	CSI_CONFIG_ACTIVE_MODE,
-+	CSI_CONFIG_ITEM_NUM
-+};
-+
-+/* CSI config Tag */
-+enum UNI_CMD_CSI_TAG_T {
-+	UNI_CMD_CSI_STOP = 0,
-+	UNI_CMD_CSI_START = 1,
-+	UNI_CMD_CSI_SET_FRAME_TYPE = 2,
-+	UNI_CMD_CSI_SET_CHAIN_FILTER = 3,
-+	UNI_CMD_CSI_SET_STA_FILTER = 4,
-+	UNI_CMD_CSI_SET_ACTIVE_MODE = 5,
-+};
-+#endif
-+
- #endif
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 0412d73..0d537fb 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -438,6 +438,47 @@ struct mt7996_air_monitor_ctrl {
- 	struct mt7996_air_monitor_group group[MT7996_AIR_MONITOR_MAX_GROUP];
- 	struct mt7996_air_monitor_entry entry[MT7996_AIR_MONITOR_MAX_ENTRY];
- };
-+
-+enum {
-+	CSI_BW20,
-+	CSI_BW40,
-+	CSI_BW80,
-+	CSI_BW160,
-+	CSI_BW320
-+};
-+
-+#define CSI_BW20_DATA_COUNT	64
-+#define CSI_BW40_DATA_COUNT	128
-+#define CSI_BW80_DATA_COUNT	256
-+#define CSI_BW160_DATA_COUNT	512
-+#define CSI_BW320_DATA_COUNT	1024
-+
-+struct csi_data {
-+	u8 fw_ver;
-+	u8 ch_bw;
-+	u16 data_num;
-+	s16 data_i[CSI_BW320_DATA_COUNT];
-+	s16 data_q[CSI_BW320_DATA_COUNT];
-+	u8 band;
-+	s8 rssi;
-+	u8 snr;
-+	u32 ts;
-+	u8 data_bw;
-+	u8 pri_ch_idx;
-+	u8 ta[ETH_ALEN];
-+	u32 ext_info;
-+	u16 rx_mode;
-+	u16 rx_rate;
-+	u32 chain_info;
-+	u16 tx_idx;
-+	u16 rx_idx;
-+	u32 segment_num;
-+	u8 remain_last;
-+	u16 pkt_sn;
-+	u8 tr_stream;
-+
-+	struct list_head node;
-+};
- #endif
- 
- struct mt7996_rro_ba_session {
-@@ -534,6 +575,18 @@ struct mt7996_phy {
- 	u8 rts_bw_sig;
- 	spinlock_t amnt_lock;
- 	struct mt7996_air_monitor_ctrl amnt_ctrl;
-+
-+	struct {
-+		struct list_head list;
-+		spinlock_t lock;
-+		u32 count;
-+		bool enable;
-+
-+		struct csi_data buffered_csi;
-+		u32 active_bitmap;
-+		u32 interval;
-+		u32 last_record;
-+	} csi;
- #endif
- #ifdef CONFIG_MTK_DEBUG
- 	bool sr_enable:1;
-@@ -1166,6 +1219,8 @@ void mt7996_mcu_set_mimo(struct mt7996_phy *phy);
- int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val);
- int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data);
- void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
-+int mt7996_mcu_set_csi(struct mt7996_phy *phy, u8 mode,
-+		       u8 cfg, u8 v1, u32 v2, u8 *mac_addr);
- #endif
- 
- int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
-diff --git a/mt7996/vendor.c b/mt7996/vendor.c
-index 64ef551..07c0c36 100644
---- a/mt7996/vendor.c
-+++ b/mt7996/vendor.c
-@@ -119,6 +119,18 @@ beacon_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BEACON_CTRL] = {
- 	[MTK_VENDOR_ATTR_BEACON_CTRL_MODE] = { .type = NLA_U8 },
- };
- 
-+static const struct nla_policy
-+csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
-+	[MTK_VENDOR_ATTR_CSI_CTRL_CFG] = {.type = NLA_NESTED },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2] = { .type = NLA_U32 },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR] = { .type = NLA_NESTED },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM] = { .type = NLA_U16 },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_DATA] = { .type = NLA_NESTED },
-+};
-+
- struct mt7996_amnt_data {
- 	u8 idx;
- 	u8 addr[ETH_ALEN];
-@@ -1000,7 +1012,188 @@ static int mt7996_vendor_beacon_ctrl(struct wiphy *wiphy,
- 
- 	return 0;
- }
-+static int mt7996_vendor_csi_ctrl(struct wiphy *wiphy,
-+				  struct wireless_dev *wdev,
-+				  const void *data,
-+				  int data_len)
-+{
-+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_CSI_CTRL];
-+	int err;
-+
-+	err = nla_parse(tb, MTK_VENDOR_ATTR_CSI_CTRL_MAX, data, data_len,
-+			csi_ctrl_policy, NULL);
-+	if (err)
-+		return err;
-+
-+	if (tb[MTK_VENDOR_ATTR_CSI_CTRL_CFG]) {
-+		u8 mode = 0, type = 0, v1 = 0;
-+		u32 v2 = 0;
-+		u8 mac_addr[ETH_ALEN] = {};
-+		struct nlattr *cur;
-+		int rem;
-+
-+		nla_for_each_nested(cur, tb[MTK_VENDOR_ATTR_CSI_CTRL_CFG], rem) {
-+			switch (nla_type(cur)) {
-+			case MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE:
-+				mode = nla_get_u8(cur);
-+				break;
-+			case MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE:
-+				type = nla_get_u8(cur);
-+				break;
-+			case MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1:
-+				v1 = nla_get_u8(cur);
-+				break;
-+			case MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2:
-+				v2 = nla_get_u32(cur);
-+				break;
-+			default:
-+				return -EINVAL;
-+			};
-+		}
-+
-+		if (tb[MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR]) {
-+			u8 idx = 0;
-+
-+			nla_for_each_nested(cur, tb[MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR], rem) {
-+				mac_addr[idx++] = nla_get_u8(cur);
-+			}
-+		}
-+
-+		err = mt7996_mcu_set_csi(phy, mode, type, v1, v2, mac_addr);
-+		if (err < 0)
-+			return err;
-+
-+		spin_lock_bh(&phy->csi.lock);
- 
-+		phy->csi.enable = !!mode;
-+
-+		/* clean up old csi stats */
-+		if ((mode == CSI_CONTROL_MODE_STOP || mode == CSI_CONTROL_MODE_SET)
-+			&& !list_empty(&phy->csi.list)) {
-+			struct csi_data *c, *tmp_c;
-+
-+			list_for_each_entry_safe(c, tmp_c, &phy->csi.list, node) {
-+				list_del(&c->node);
-+				kfree(c);
-+				phy->csi.count--;
-+			}
-+		} else if (mode == CSI_CONTROL_MODE_START) {
-+			phy->csi.last_record = 0;
-+		}
-+
-+		spin_unlock_bh(&phy->csi.lock);
-+
-+		if (mode == CSI_CONTROL_MODE_SET && type == CSI_CONFIG_STA_FILTER && v1 == 2)
-+			phy->csi.interval = v2;
-+	}
-+
-+	return 0;
-+}
-+
-+static int
-+mt7996_vendor_csi_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
-+			    struct sk_buff *skb, const void *data, int data_len,
-+			    unsigned long *storage)
-+{
-+#define RESERVED_SET	BIT(31)
-+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-+	struct mt7996_phy *phy = mt7996_hw_phy(hw);
-+	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {0};
-+	int err = 0;
-+
-+	if (*storage & RESERVED_SET) {
-+		if ((*storage & GENMASK(15, 0)) == 0)
-+			return -ENOENT;
-+		(*storage)--;
-+	}
-+
-+	if (data) {
-+		err = nla_parse(tb, MTK_VENDOR_ATTR_CSI_CTRL_MAX, data, data_len,
-+				csi_ctrl_policy, NULL);
-+		if (err)
-+			return err;
-+	}
-+
-+	if (!(*storage & RESERVED_SET) && tb[MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM]) {
-+		*storage = nla_get_u16(tb[MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM]);
-+		*storage |= RESERVED_SET;
-+	}
-+
-+	spin_lock_bh(&phy->csi.lock);
-+
-+	if (!list_empty(&phy->csi.list)) {
-+		struct csi_data *csi;
-+		void *a, *b;
-+		int i;
-+
-+		csi = list_first_entry(&phy->csi.list, struct csi_data, node);
-+
-+		a = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_CTRL_DATA);
-+		if (!a)
-+			goto out;
-+
-+		if (nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_VER, 1) ||
-+		    nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_RSSI, csi->rssi) ||
-+		    nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_SNR, csi->snr) ||
-+		    nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_BW, csi->data_bw) ||
-+		    nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_CH_IDX, csi->pri_ch_idx) ||
-+		    nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_MODE, csi->rx_mode))
-+			goto out;
-+
-+		if (nla_put_u16(skb, MTK_VENDOR_ATTR_CSI_DATA_TX_ANT, csi->tx_idx) ||
-+		    nla_put_u16(skb, MTK_VENDOR_ATTR_CSI_DATA_RX_ANT, csi->rx_idx))
-+			goto out;
-+
-+		if (nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_INFO, csi->ext_info) ||
-+		    nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO, csi->chain_info) ||
-+		    nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_TS, csi->ts))
-+			goto out;
-+
-+		b = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_DATA_TA);
-+		if (!b)
-+			goto out;
-+
-+		for (i = 0; i < ARRAY_SIZE(csi->ta); i++)
-+			if (nla_put_u8(skb, i, csi->ta[i]))
-+				goto out;
-+		nla_nest_end(skb, b);
-+
-+		if (nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_NUM, csi->data_num))
-+			goto out;
-+
-+		b = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_DATA_I);
-+		if (!b)
-+			goto out;
-+
-+		for (i = 0; i < csi->data_num; i++)
-+			if (nla_put_u16(skb, i, csi->data_i[i]))
-+				goto out;
-+		nla_nest_end(skb, b);
-+
-+		b = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_DATA_Q);
-+		if (!b)
-+			goto out;
-+
-+		for (i = 0; i < csi->data_num; i++)
-+			if (nla_put_u16(skb, i, csi->data_q[i]))
-+				goto out;
-+		nla_nest_end(skb, b);
-+
-+		nla_nest_end(skb, a);
-+
-+		list_del(&csi->node);
-+		kfree(csi);
-+		phy->csi.count--;
-+
-+		err = phy->csi.count;
-+	}
-+out:
-+	spin_unlock_bh(&phy->csi.lock);
-+
-+	return err;
-+}
- 
- static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- 	{
-@@ -1129,6 +1322,18 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
- 		.policy = beacon_ctrl_policy,
- 		.maxattr = MTK_VENDOR_ATTR_BEACON_CTRL_MAX,
- 	},
-+	{
-+		.info = {
-+			.vendor_id = MTK_NL80211_VENDOR_ID,
-+			.subcmd = MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL,
-+		},
-+		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
-+			 WIPHY_VENDOR_CMD_NEED_RUNNING,
-+		.doit = mt7996_vendor_csi_ctrl,
-+		.dumpit = mt7996_vendor_csi_ctrl_dump,
-+		.policy = csi_ctrl_policy,
-+		.maxattr = MTK_VENDOR_ATTR_CSI_CTRL_MAX,
-+	},
- };
- 
- void mt7996_vendor_register(struct mt7996_phy *phy)
-@@ -1136,6 +1341,9 @@ void mt7996_vendor_register(struct mt7996_phy *phy)
- 	phy->mt76->hw->wiphy->vendor_commands = mt7996_vendor_commands;
- 	phy->mt76->hw->wiphy->n_vendor_commands = ARRAY_SIZE(mt7996_vendor_commands);
- 
-+	INIT_LIST_HEAD(&phy->csi.list);
-+	spin_lock_init(&phy->csi.lock);
-+
- 	spin_lock_init(&phy->amnt_lock);
- }
- #endif
-diff --git a/mt7996/vendor.h b/mt7996/vendor.h
-index 3234677..abe3fde 100644
---- a/mt7996/vendor.h
-+++ b/mt7996/vendor.h
-@@ -7,6 +7,7 @@
- 
- enum mtk_nl80211_vendor_subcmds {
- 	MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
-+	MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
- 	MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
- 	MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
- 	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
-@@ -240,6 +241,55 @@ enum mtk_vendor_attr_beacon_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
- };
- 
-+enum mtk_vendor_attr_csi_ctrl {
-+	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_CSI_CTRL_CFG,
-+	MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE,
-+	MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE,
-+	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
-+	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
-+	MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
-+
-+	MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
-+
-+	MTK_VENDOR_ATTR_CSI_CTRL_DATA,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_CSI_CTRL,
-+	MTK_VENDOR_ATTR_CSI_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_CSI_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_csi_data {
-+	MTK_VENDOR_ATTR_CSI_DATA_UNSPEC,
-+	MTK_VENDOR_ATTR_CSI_DATA_PAD,
-+
-+	MTK_VENDOR_ATTR_CSI_DATA_VER,
-+	MTK_VENDOR_ATTR_CSI_DATA_TS,
-+	MTK_VENDOR_ATTR_CSI_DATA_RSSI,
-+	MTK_VENDOR_ATTR_CSI_DATA_SNR,
-+	MTK_VENDOR_ATTR_CSI_DATA_BW,
-+	MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
-+	MTK_VENDOR_ATTR_CSI_DATA_TA,
-+	MTK_VENDOR_ATTR_CSI_DATA_NUM,
-+	MTK_VENDOR_ATTR_CSI_DATA_I,
-+	MTK_VENDOR_ATTR_CSI_DATA_Q,
-+	MTK_VENDOR_ATTR_CSI_DATA_INFO,
-+	MTK_VENDOR_ATTR_CSI_DATA_RSVD1,
-+	MTK_VENDOR_ATTR_CSI_DATA_RSVD2,
-+	MTK_VENDOR_ATTR_CSI_DATA_RSVD3,
-+	MTK_VENDOR_ATTR_CSI_DATA_RSVD4,
-+	MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
-+	MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
-+	MTK_VENDOR_ATTR_CSI_DATA_MODE,
-+	MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_CSI_DATA,
-+	MTK_VENDOR_ATTR_CSI_DATA_MAX =
-+		NUM_MTK_VENDOR_ATTRS_CSI_DATA - 1
-+};
- #endif
- 
- #endif
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0115-mtk-mt76-mt7996-tmp-disable-VOW.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0115-mtk-mt76-mt7996-tmp-disable-VOW.patch
new file mode 100644
index 0000000..dfb76e0
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0115-mtk-mt76-mt7996-tmp-disable-VOW.patch
@@ -0,0 +1,59 @@
+From 3ba122ebb3462395d26d4f8f55dab510a49f5b5e Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 20 Mar 2024 22:56:44 +0800
+Subject: [PATCH 115/199] mtk: mt76: mt7996: tmp disable VOW
+
+FW will return failed when legacy 5G station connects after legacy 2G
+station, need to check.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mcu.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 598f32ba..340b6637 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2369,6 +2369,7 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
+ 	return mt7996_mcu_add_rate_ctrl_fixed(dev, conf, mconf, link_sta, mlink);
+ }
+ 
++#if 0
+ static int
+ mt7996_mcu_sta_init_vow(struct mt7996_bss_conf *mconf,
+ 			struct mt7996_link_sta *mlink)
+@@ -2402,6 +2403,7 @@ mt7996_mcu_sta_init_vow(struct mt7996_bss_conf *mconf,
+ 
+ 	return mt7996_mcu_set_vow_drr_ctrl(phy, mconf, mlink, VOW_DRR_CTRL_STA_ALL);
+ }
++#endif
+ 
+ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ 		       struct mt7996_bss_conf *mconf,
+@@ -2410,7 +2412,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ {
+ 	struct ieee80211_vif *vif = conf->vif;
+ 	struct sk_buff *skb;
+-	int ret;
++	// int ret;
+ 
+ 	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
+ 					      &mlink->wcid,
+@@ -2456,11 +2458,13 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ 		mt7996_mcu_sta_bfee_tlv(dev, skb, conf, mconf, link_sta);
+ 	}
+ 
++#if 0
+ 	ret = mt7996_mcu_sta_init_vow(mconf, mlink);
+ 	if (ret) {
+ 		dev_kfree_skb(skb);
+ 		return ret;
+ 	}
++#endif
+ out:
+ 	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ 				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0115-mtk-wifi-mt76-mt7996-add-more-debug-info-for-MLO.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0115-mtk-wifi-mt76-mt7996-add-more-debug-info-for-MLO.patch
deleted file mode 100644
index fad0b87..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0115-mtk-wifi-mt76-mt7996-add-more-debug-info-for-MLO.patch
+++ /dev/null
@@ -1,2140 +0,0 @@
-From 9f8261c0ffa9a1bbe4f810ce39c70c1f8954c60d Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Mon, 22 Apr 2024 11:06:48 +0800
-Subject: [PATCH 115/116] mtk: wifi: mt76: mt7996: add more debug info for MLO
-
----
- mt76_connac_mcu.c    |    1 +
- mt76_connac_mcu.h    |    1 +
- mt7996/debugfs.c     |   34 +-
- mt7996/mac.c         |   15 +
- mt7996/main.c        |   14 +
- mt7996/mcu.c         |   18 +
- mt7996/mcu.h         |    8 +
- mt7996/mt7996.h      |   20 +
- mt7996/mtk_debug.h   |  660 +++++++++++++++++++++++++
- mt7996/mtk_debugfs.c | 1085 ++++++++++++++++++++++++++++++++++++++++++
- 10 files changed, 1847 insertions(+), 9 deletions(-)
-
-diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
-index 71f3d30..a4924c4 100644
---- a/mt76_connac_mcu.c
-+++ b/mt76_connac_mcu.c
-@@ -433,6 +433,7 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
- 	}
- 
- 	memcpy(basic->peer_addr, link_sta->addr, ETH_ALEN);
-+	pr_info("%s: link %u addr [%pM]\n", __func__, link_sta->link_id, basic->peer_addr);
- 	basic->qos = sta->wme;
- }
- EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_basic_tlv);
-diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index daaf87f..27bff39 100644
---- a/mt76_connac_mcu.h
-+++ b/mt76_connac_mcu.h
-@@ -1287,6 +1287,7 @@ enum {
- 	MCU_UNI_CMD_THERMAL = 0x35,
- 	MCU_UNI_CMD_VOW = 0x37,
- 	MCU_UNI_CMD_PP = 0x38,
-+	MCU_UNI_CMD_MEC = 0x3a,
- 	MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
- 	MCU_UNI_CMD_TESTMODE_TRX_PARAM = 0x42,
- 	MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
-diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index 26927ed..77349c2 100644
---- a/mt7996/debugfs.c
-+++ b/mt7996/debugfs.c
-@@ -314,15 +314,17 @@ mt7996_fw_debug_wm_set(void *data, u64 val)
- 		DEBUG_CMD_RPT_TRIG,
- 		DEBUG_SPL,
- 		DEBUG_RPT_RX,
--		DEBUG_RPT_RA = 68,
- 		DEBUG_IDS_SND = 84,
-+		DEBUG_IDS_BSRP,
-+		DEBUG_IDS_TPUT_MON,
- 		DEBUG_IDS_PP = 93,
--		DEBUG_IDS_RA = 94,
--		DEBUG_IDS_BF = 95,
--		DEBUG_IDS_SR = 96,
--		DEBUG_IDS_RU = 97,
--		DEBUG_IDS_MUMIMO = 98,
--		DEBUG_IDS_ERR_LOG = 101,
-+		DEBUG_IDS_RA,
-+		DEBUG_IDS_BF,
-+		DEBUG_IDS_SR,
-+		DEBUG_IDS_RU,
-+		DEBUG_IDS_MUMIMO,
-+		DEBUG_IDS_MLO = 100,
-+		DEBUG_IDS_ERR_LOG,
- 	};
- 	u8 debug_category[] = {
- 		DEBUG_TXCMD,
-@@ -330,14 +332,16 @@ mt7996_fw_debug_wm_set(void *data, u64 val)
- 		DEBUG_CMD_RPT_TRIG,
- 		DEBUG_SPL,
- 		DEBUG_RPT_RX,
--		DEBUG_RPT_RA,
- 		DEBUG_IDS_SND,
-+		DEBUG_IDS_BSRP,
-+		DEBUG_IDS_TPUT_MON,
- 		DEBUG_IDS_PP,
- 		DEBUG_IDS_RA,
- 		DEBUG_IDS_BF,
- 		DEBUG_IDS_SR,
- 		DEBUG_IDS_RU,
- 		DEBUG_IDS_MUMIMO,
-+		DEBUG_IDS_MLO,
- 		DEBUG_IDS_ERR_LOG,
- 	};
- 	bool tx, rx, en;
-@@ -372,7 +376,8 @@ mt7996_fw_debug_wm_set(void *data, u64 val)
- 		if (ret)
- 			return ret;
- 
--		if (debug_category[i] == DEBUG_IDS_SND && en) {
-+		if ((debug_category[i] == DEBUG_TXCMD ||
-+		     debug_category[i] == DEBUG_IDS_SND) && en) {
- 			ret = mt7996_mcu_fw_dbg_ctrl(dev, debug_category[i], 2);
- 			if (ret)
- 				return ret;
-@@ -506,6 +511,14 @@ mt7996_fw_debug_bin_set(void *data, u64 val)
- 	if (ret)
- 		return ret;
- 
-+#ifdef CONFIG_MTK_DEBUG
-+	dev->dbg.dump_mcu_pkt = val & BIT(4) ? true : false;
-+	dev->dbg.dump_txd = val & BIT(5) ? true : false;
-+	dev->dbg.dump_tx_pkt = val & BIT(6) ? true : false;
-+	dev->dbg.dump_rx_pkt = val & BIT(7) ? true : false;
-+	dev->dbg.dump_rx_raw = val & BIT(8) ? true : false;
-+#endif
-+
- 	return mt7996_fw_debug_wm_set(dev, dev->fw_debug_wm);
- }
- 
-@@ -1208,6 +1221,9 @@ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int
- bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len)
- {
- 	bool is_fwlog = get_unaligned_le32(data) == FW_BIN_LOG_MAGIC;
-+#ifdef CONFIG_MTK_DEBUG
-+	is_fwlog |= get_unaligned_le32(data) == PKT_BIN_DEBUG_MAGIC;
-+#endif
- 
- 	if (is_fwlog) {
- 		if (dev->relay_fwlog)
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 2a45fc0..a8cf24c 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -331,6 +331,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
- 	u8 hw_aggr = false;
- 	struct mt7996_link_sta *mlink = NULL;
- 
-+#ifdef CONFIG_MTK_DEBUG
-+	if (dev->dbg.dump_rx_raw)
-+		mt7996_packet_log_to_host(dev, skb->data, skb->len, PKT_BIN_DEBUG_RX_RAW, 0);
-+#endif
- 	hw_aggr = status->aggr;
- 	memset(status, 0, sizeof(*status));
- 
-@@ -511,6 +515,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
- 	}
- 
- 	hdr_gap = (u8 *)rxd - skb->data + 2 * remove_pad;
-+#ifdef CONFIG_MTK_DEBUG
-+	if (dev->dbg.dump_rx_pkt)
-+		mt7996_packet_log_to_host(dev, skb->data, skb->len, PKT_BIN_DEBUG_RX, hdr_gap);
-+#endif
- 	if (hdr_trans && ieee80211_has_morefrags(fc)) {
- 		if (mt7996_reverse_frag0_hdr_trans(skb, hdr_gap))
- 			return -EINVAL;
-@@ -967,6 +975,13 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
- 	tx_info->buf[1].skip_unmap = true;
- 	tx_info->nbuf = MT_CT_DMA_BUF_NUM;
- 
-+#ifdef CONFIG_MTK_DEBUG
-+	if (dev->dbg.dump_txd)
-+		mt7996_packet_log_to_host(dev, txwi, MT_TXD_SIZE, PKT_BIN_DEBUG_TXD, 0);
-+	if (dev->dbg.dump_tx_pkt)
-+		mt7996_packet_log_to_host(dev, t->skb->data, t->skb->len, PKT_BIN_DEBUG_TX, 0);
-+#endif
-+
- 	return 0;
- }
- 
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 35f2495..2cc0c32 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -431,6 +431,9 @@ static int mt7996_add_bss_conf(struct mt7996_phy *phy,
- 	rcu_assign_pointer(mvif->link[link_id], mconf);
- 	rcu_assign_pointer(mvif->sta.link[link_id], mlink);
- 
-+	mlo_dbg(phy, "bss_idx=%u, link_id=%u, wcid=%u\n",
-+		mconf->mt76.idx, mconf->link_id, mlink->wcid.idx);
-+
- 	return 0;
- error:
- 	mt7996_remove_bss_conf(vif, conf, mconf);
-@@ -603,6 +606,11 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
- 			add = vif->valid_links ?: BIT(0);
- 	}
- 
-+	mlo_dbg(mt7996_hw_phy(hw), "cipher = 0x%x, icv_len = %u, iv_len = %u, hw_key_idx = %u, keyidx = %d, flags = 0x%x, link_id = %d, keylen = %u\n",
-+		     key->cipher, key->icv_len, key->iv_len, key->hw_key_idx, key->keyidx, key->flags, key->link_id, key->keylen);
-+	// print_hex_dump(KERN_INFO , "", DUMP_PREFIX_OFFSET, 16, 1, key->key, key->keylen, false);
-+	mlo_dbg(mt7996_hw_phy(hw), "add=%lx, valid_links=%x, active_links=%x\n", add, vif->valid_links, vif->active_links);
-+
- 	mutex_lock(&dev->mt76.mutex);
- 
- 	for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
-@@ -1133,6 +1141,8 @@ static int mt7996_add_link_sta(struct mt7996_dev *dev,
- 			mt76_wcid_mask_set(dev->mt76.wcid_phy_mask, idx);
- 		rcu_assign_pointer(dev->mt76.wcid[idx], &mlink->wcid);
- 		mt76_wcid_init(&mlink->wcid);
-+
-+		mlo_dbg(mconf->phy, "wcid=%u, link_id=%u, link_addr=%pM, pri_link=%u, sec_link=%u\n", mlink->wcid.idx, link_id, link_sta->addr, msta->pri_link, msta->sec_link);
- 	}
- 
- 	if (!assoc)
-@@ -1167,6 +1177,7 @@ mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- 	unsigned int link_id;
- 
-+	mlo_dbg(mt7996_hw_phy(mvif->hw), "rem=%lu\n", rem);
- 	for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
- 		struct mt7996_bss_conf *mconf =
- 			mconf_dereference_protected(mvif, link_id);
-@@ -1192,6 +1203,7 @@ mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- 	unsigned int link_id;
- 	int i, ret;
- 
-+	mlo_dbg(mt7996_hw_phy(mvif->hw), "add=%lu, assoc=%d\n", add, assoc);
- 	for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
- 		struct mt7996_bss_conf *mconf =
- 			mconf_dereference_protected(mvif, link_id);
-@@ -2530,6 +2542,7 @@ mt7996_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	unsigned int link_id;
- 	int ret = 0;
- 
-+	mlo_dbg(phy, "old=%u, new=%u\n", old_links, new_links);
- 	if (old_links == new_links)
- 		return 0;
- 
-@@ -2578,6 +2591,7 @@ mt7996_change_sta_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 	unsigned long rem = old_links & ~new_links;
- 	int ret = 0;
- 
-+	mlo_dbg(mt7996_hw_phy(hw), "old=%u, new=%u\n", old_links, new_links);
- 	mutex_lock(&dev->mt76.mutex);
- 
- 	if (rem)
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 6c9ec31..fa17f73 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -333,6 +333,10 @@ mt7996_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
- 		mcu_txd->s2d_index = MCU_S2D_H2N;
- 
- exit:
-+#ifdef CONFIG_MTK_DEBUG
-+	if (dev->dbg.dump_mcu_pkt)
-+		mt7996_packet_log_to_host(dev, skb->data, skb->len, PKT_BIN_DEBUG_MCU, 0);
-+#endif
- 	if (wait_seq)
- 		*wait_seq = seq;
- 
-@@ -1336,6 +1340,8 @@ mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
- 	}
- 
- 	mld->own_mld_id = mconf->own_mld_id;
-+	pr_info("%s: group_mld_id=%d own_mld_id=%d remap_idx=%d mld->addr[%pM]\n",
-+		__func__, mld->group_mld_id,  mld->own_mld_id, mld->remap_idx, mld->mac_addr);
- }
- 
- static void
-@@ -1487,6 +1493,10 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
- 	}
- 
- 	memcpy(bss->bssid, conf->bssid, ETH_ALEN);
-+
-+	mlo_dbg(mconf->phy, "omac_idx=%d band_idx=%d wmm_idx=%d bss->bssid=%pM enable=%d\n",
-+		bss->omac_idx, bss->band_idx, bss->wmm_idx, bss->bssid, enable);
-+
- 	bss->bcn_interval = cpu_to_le16(conf->beacon_int);
- 	bss->dtim_period = conf->dtim_period;
- 	bss->phymode = mt76_connac_get_phy_mode(phy, vif,
-@@ -2770,6 +2780,8 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
- 
- 	/* starec basic */
- 	mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, conf, link_sta, enable, newly);
-+	mlo_dbg(mconf->phy, "link=%u, newly=%d, en=%d\n",
-+		mlink->wcid.link_id, newly, enable);
- 
- 	if (!enable)
- 		goto out;
-@@ -2852,12 +2864,16 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- 	mld_setup->link_num = hweight16(sta->valid_links);
- 
- 	mld_setup_link = (struct mld_setup_link *)mld_setup->link_info;
-+	mlo_dbg(mt7996_hw_phy(mlink->sta->vif->hw), "pri_link(%u) primary_id(%d) seconed_id(%d) wcid(%d), link_num(%d), mld_addr[%pM]\n",
-+		msta->pri_link, mld_setup->primary_id, mld_setup->seconed_id, mld_setup->setup_wcid, mld_setup->link_num, mld_setup->mld_addr);
- 	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
- 		mlink = mlink_dereference_protected(msta, link_id);
- 		mconf = mconf_dereference_protected(msta->vif, link_id);
- 
- 		mld_setup_link->wcid = cpu_to_le16(mlink->wcid.idx);
- 		mld_setup_link->bss_idx = mconf->mt76.idx;
-+		mlo_dbg(mt7996_hw_phy(mlink->sta->vif->hw), "link_id(%d) wcid(%d) bss_idx(%d)\n",
-+		link_id, mld_setup_link->wcid, mld_setup_link->bss_idx);
- 		mld_setup_link++;
- 	}
- }
-@@ -3121,6 +3137,8 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
- 		return mt7996_mcu_muar_config(phy, conf, mconf, false, enable);
- 
- 	memcpy(data.tlv.omac_addr, conf->addr, ETH_ALEN);
-+	mlo_dbg(phy, "omac=%u, band=%u, addr=%pM, en=%d\n",
-+		data.hdr.omac_idx,data.hdr.band_idx, data.tlv.omac_addr, enable);
- 	return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(DEV_INFO_UPDATE),
- 				 &data, sizeof(data), true);
- }
-diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index dc93fab..84d961d 100644
---- a/mt7996/mcu.h
-+++ b/mt7996/mcu.h
-@@ -1035,6 +1035,14 @@ enum {
- 	UNI_RRO_SET_FLUSH_TIMEOUT
- };
- 
-+enum {
-+	UNI_MEC_READ_INFO = 0,
-+	UNI_MEC_AMSDU_ALGO_EN_STA,
-+	UNI_MEC_AMSDU_PARA_STA,
-+	UNI_MEC_AMSDU_ALGO_THRESHOLD,
-+	UNI_MEC_IFAC_SPEED,
-+};
-+
- enum{
- 	UNI_CMD_SR_ENABLE = 0x1,
- 	UNI_CMD_SR_ENABLE_SD,
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 0d537fb..de7cf1e 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -730,6 +730,13 @@ struct mt7996_dev {
- 		u8 fw_dbg_lv;
- 		u32 bcn_total_cnt[__MT_MAX_BAND];
- 		u32 sid;
-+
-+		bool dump_mcu_pkt:1;
-+		bool dump_txd:1;
-+		bool dump_tx_pkt:1;
-+		bool dump_rx_pkt:1;
-+		bool dump_rx_raw:1;
-+		u32 token_idx;
- 	} dbg;
- 	const struct mt7996_dbg_reg_desc *dbg_reg;
- #endif
-@@ -925,6 +932,8 @@ mt7996_get_link_wcid(struct mt7996_dev *dev, u16 idx, u8 band_idx)
- 	return &mlink->wcid;
- }
- 
-+#define mlo_dbg(phy, fmt, ...)   wiphy_info(phy->mt76->hw->wiphy, "%s: " fmt, __func__, ##__VA_ARGS__)
-+
- extern const struct ieee80211_ops mt7996_ops;
- extern struct pci_driver mt7996_pci_driver;
- extern struct pci_driver mt7996_hif_driver;
-@@ -1256,6 +1265,17 @@ void mt7996_tm_update_channel(struct mt7996_phy *phy);
- 
- int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val);
- int mt7996_mcu_thermal_debug(struct mt7996_dev *dev, u8 mode, u8 action);
-+
-+#define PKT_BIN_DEBUG_MAGIC	0xc8763123
-+enum {
-+	PKT_BIN_DEBUG_MCU,
-+	PKT_BIN_DEBUG_TXD,
-+	PKT_BIN_DEBUG_TX,
-+	PKT_BIN_DEBUG_RX,
-+	PKT_BIN_DEBUG_RX_RAW,
-+};
-+
-+void mt7996_packet_log_to_host(struct mt7996_dev *dev, const void *data, int len, int type, int des_len);
- #endif
- 
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
-diff --git a/mt7996/mtk_debug.h b/mt7996/mtk_debug.h
-index da2a607..8299023 100644
---- a/mt7996/mtk_debug.h
-+++ b/mt7996/mtk_debug.h
-@@ -2288,4 +2288,664 @@ enum cipher_suit {
- #define WTBL_RATE_STBC_OFFSET          	 	14
- #endif
- 
-+struct bin_debug_hdr {
-+	__le32 magic_num;
-+	__le16 serial_id;
-+	__le16 msg_type;
-+	__le16 len;
-+	__le16 des_len;	/* descriptor len for rxd */
-+} __packed;
-+
-+enum umac_port {
-+	ENUM_UMAC_HIF_PORT_0         = 0,
-+	ENUM_UMAC_CPU_PORT_1         = 1,
-+	ENUM_UMAC_LMAC_PORT_2        = 2,
-+	ENUM_PLE_CTRL_PSE_PORT_3     = 3,
-+	ENUM_UMAC_PSE_PLE_PORT_TOTAL_NUM = 4
-+};
-+
-+/* N9 MCU QUEUE LIST */
-+enum umac_cpu_port_queue_idx {
-+	ENUM_UMAC_CTX_Q_0 = 0,
-+	ENUM_UMAC_CTX_Q_1 = 1,
-+	ENUM_UMAC_CTX_Q_2 = 2,
-+	ENUM_UMAC_CTX_Q_3 = 3,
-+	ENUM_UMAC_CRX     = 0,
-+	ENUM_UMAC_CIF_QUEUE_TOTAL_NUM = 4
-+};
-+
-+/* LMAC PLE For PSE Control P3 */
-+enum umac_ple_ctrl_port3_queue_idx {
-+	ENUM_UMAC_PLE_CTRL_P3_Q_0X1E            = 0x1e,
-+	ENUM_UMAC_PLE_CTRL_P3_Q_0X1F            = 0x1f,
-+	ENUM_UMAC_PLE_CTRL_P3_TOTAL_NUM         = 2
-+};
-+
-+/* PSE PLE QUEUE */
-+#define CR_NUM_OF_AC_MT7996	34
-+#define CR_NUM_OF_AC_MT7992	17
-+struct bmac_queue_info {
-+	char *QueueName;
-+	u32 Portid;
-+	u32 Queueid;
-+	u32 tgid;
-+};
-+
-+struct bmac_queue_info_t {
-+	char *QueueName;
-+	u32 Portid;
-+	u32 Queueid;
-+};
-+
-+#define WF_DRR_TOP_BASE                                        0x820c8800
-+#define WF_DRR_TOP_SBRR_ADDR                                   (WF_DRR_TOP_BASE + 0x00E0) // 88E0
-+#define WF_DRR_TOP_TWT_STA_MAP00_ADDR                          (WF_DRR_TOP_BASE + 0x0100) // 8900
-+#define WF_DRR_TOP_TWT_STA_MAP_EXT_00_ADDR                     (WF_DRR_TOP_BASE + 0x0180) // 8980
-+#define WF_DRR_TOP_AC0_STATION_PAUSE00_ADDR                    (WF_DRR_TOP_BASE + 0x0200) // 8A00
-+#define WF_DRR_TOP_AC0_STATION_PAUSE_EXT_00_ADDR               (WF_DRR_TOP_BASE + 0x0280) // 8A80
-+#define WF_DRR_TOP_AC1_STATION_PAUSE00_ADDR                    (WF_DRR_TOP_BASE + 0x0300) // 8B00
-+#define WF_DRR_TOP_AC1_STATION_PAUSE_EXT_00_ADDR               (WF_DRR_TOP_BASE + 0x0380) // 8B80
-+#define WF_DRR_TOP_AC2_STATION_PAUSE00_ADDR                    (WF_DRR_TOP_BASE + 0x0400) // 8C00
-+#define WF_DRR_TOP_AC2_STATION_PAUSE_EXT_00_ADDR               (WF_DRR_TOP_BASE + 0x0480) // 8C80
-+#define WF_DRR_TOP_AC3_STATION_PAUSE00_ADDR                    (WF_DRR_TOP_BASE + 0x0500) // 8D00
-+#define WF_DRR_TOP_AC3_STATION_PAUSE_EXT_00_ADDR               (WF_DRR_TOP_BASE + 0x0580) // 8D80
-+
-+#define WF_DRR_TOP_SBRR_TARGET_BAND_MASK                       0x00000003                // TARGET_BAND[1..0]
-+/* PLE AMSDU */
-+#define WF_PLE_TOP_BASE                                        0x820c0000
-+
-+#define WF_PLE_TOP_AMSDU_PACK_1_MSDU_CNT_ADDR                  (WF_PLE_TOP_BASE + 0x10e0) // 10E0
-+#define WF_PLE_TOP_AMSDU_PACK_2_MSDU_CNT_ADDR                  (WF_PLE_TOP_BASE + 0x10e4) // 10E4
-+#define WF_PLE_TOP_AMSDU_PACK_3_MSDU_CNT_ADDR                  (WF_PLE_TOP_BASE + 0x10e8) // 10E8
-+#define WF_PLE_TOP_AMSDU_PACK_4_MSDU_CNT_ADDR                  (WF_PLE_TOP_BASE + 0x10ec) // 10EC
-+#define WF_PLE_TOP_AMSDU_PACK_5_MSDU_CNT_ADDR                  (WF_PLE_TOP_BASE + 0x10f0) // 10F0
-+#define WF_PLE_TOP_AMSDU_PACK_6_MSDU_CNT_ADDR                  (WF_PLE_TOP_BASE + 0x10f4) // 10F4
-+#define WF_PLE_TOP_AMSDU_PACK_7_MSDU_CNT_ADDR                  (WF_PLE_TOP_BASE + 0x10f8) // 10F8
-+#define WF_PLE_TOP_AMSDU_PACK_8_MSDU_CNT_ADDR                  (WF_PLE_TOP_BASE + 0x10fc) // 10FC
-+#define WF_PLE_TOP_AMSDU_PACK_NUM                              8
-+
-+/* PLE */
-+#define WF_PLE_TOP_PBUF_CTRL_ADDR                              (WF_PLE_TOP_BASE + 0x04) // 0004
-+
-+#define WF_PLE_TOP_PG_HIF_GROUP_ADDR                           (WF_PLE_TOP_BASE + 0x0c) // 000C
-+#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_ADDR                     (WF_PLE_TOP_BASE + 0x10) // 0010
-+#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_ADDR                     (WF_PLE_TOP_BASE + 0x14) // 0014
-+#define WF_PLE_TOP_PG_CPU_GROUP_ADDR                           (WF_PLE_TOP_BASE + 0x18) // 0018
-+#define WF_PLE_TOP_QUEUE_EMPTY_ADDR                            (WF_PLE_TOP_BASE + 0x360) // 0360
-+
-+#define WF_PLE_TOP_DIS_STA_MAP0_ADDR                           (WF_PLE_TOP_BASE + 0x100) // 0100
-+#define WF_PLE_TOP_DIS_STA_MAP1_ADDR                           (WF_PLE_TOP_BASE + 0x104) // 0104
-+#define WF_PLE_TOP_DIS_STA_MAP2_ADDR                           (WF_PLE_TOP_BASE + 0x108) // 0108
-+#define WF_PLE_TOP_DIS_STA_MAP3_ADDR                           (WF_PLE_TOP_BASE + 0x10c) // 010C
-+#define WF_PLE_TOP_DIS_STA_MAP4_ADDR                           (WF_PLE_TOP_BASE + 0x110) // 0110
-+#define WF_PLE_TOP_DIS_STA_MAP5_ADDR                           (WF_PLE_TOP_BASE + 0x114) // 0114
-+#define WF_PLE_TOP_DIS_STA_MAP6_ADDR                           (WF_PLE_TOP_BASE + 0x118) // 0118
-+#define WF_PLE_TOP_DIS_STA_MAP7_ADDR                           (WF_PLE_TOP_BASE + 0x11c) // 011C
-+#define WF_PLE_TOP_DIS_STA_MAP8_ADDR                           (WF_PLE_TOP_BASE + 0x120) // 0120
-+
-+#define WF_PLE_TOP_TXCMD_QUEUE_EMPTY_ADDR                      (WF_PLE_TOP_BASE + 0x378) // 0378
-+#define WF_PLE_TOP_NATIVE_TXCMD_QUEUE_EMPTY_ADDR               (WF_PLE_TOP_BASE + 0x37c) // 037C
-+#define WF_PLE_TOP_BN1_TXCMD_QUEUE_EMPTY_ADDR                  (WF_PLE_TOP_BASE + 0x388) // 0388
-+#define WF_PLE_TOP_BN1_NATIVE_TXCMD_QUEUE_EMPTY_ADDR           (WF_PLE_TOP_BASE + 0x38c) // 038C
-+#define WF_PLE_TOP_BN2_TXCMD_QUEUE_EMPTY_ADDR                  (WF_PLE_TOP_BASE + 0x398) // 0398
-+#define WF_PLE_TOP_BN2_NATIVE_TXCMD_QUEUE_EMPTY_ADDR           (WF_PLE_TOP_BASE + 0x39c) // 039C
-+
-+#define WF_PLE_TOP_FREEPG_CNT_ADDR                             (WF_PLE_TOP_BASE + 0x3a0) // 03A0
-+#define WF_PLE_TOP_FREEPG_HEAD_TAIL_ADDR                       (WF_PLE_TOP_BASE + 0x3a4) // 03A4
-+#define WF_PLE_TOP_HIF_PG_INFO_ADDR                            (WF_PLE_TOP_BASE + 0x3a8) // 03A8
-+#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_ADDR                      (WF_PLE_TOP_BASE + 0x3ac) // 03AC
-+#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_ADDR                      (WF_PLE_TOP_BASE + 0x3b0) // 03B0
-+#define WF_PLE_TOP_CPU_PG_INFO_ADDR                            (WF_PLE_TOP_BASE + 0x3b4) // 03B4
-+
-+#define WF_PLE_TOP_FL_QUE_CTRL_0_ADDR                          (WF_PLE_TOP_BASE + 0x3e0) // 03E0
-+#define WF_PLE_TOP_FL_QUE_CTRL_1_ADDR                          (WF_PLE_TOP_BASE + 0x3e4) // 03E4
-+#define WF_PLE_TOP_FL_QUE_CTRL_2_ADDR                          (WF_PLE_TOP_BASE + 0x3e8) // 03E8
-+#define WF_PLE_TOP_FL_QUE_CTRL_3_ADDR                          (WF_PLE_TOP_BASE + 0x3ec) // 03EC
-+
-+#define WF_PLE_TOP_AC0_QUEUE_EMPTY0_ADDR                       (WF_PLE_TOP_BASE + 0x600) // 0600
-+#define WF_PLE_TOP_AC0_QUEUE_EMPTY1_ADDR                       (WF_PLE_TOP_BASE + 0x604) // 0604
-+#define WF_PLE_TOP_AC0_QUEUE_EMPTY2_ADDR                       (WF_PLE_TOP_BASE + 0x608) // 0608
-+#define WF_PLE_TOP_AC0_QUEUE_EMPTY3_ADDR                       (WF_PLE_TOP_BASE + 0x60c) // 060C
-+#define WF_PLE_TOP_AC0_QUEUE_EMPTY4_ADDR                       (WF_PLE_TOP_BASE + 0x610) // 0610
-+#define WF_PLE_TOP_AC0_QUEUE_EMPTY5_ADDR                       (WF_PLE_TOP_BASE + 0x614) // 0614
-+#define WF_PLE_TOP_AC0_QUEUE_EMPTY6_ADDR                       (WF_PLE_TOP_BASE + 0x618) // 0618
-+#define WF_PLE_TOP_AC0_QUEUE_EMPTY7_ADDR                       (WF_PLE_TOP_BASE + 0x61c) // 061C
-+#define WF_PLE_TOP_AC0_QUEUE_EMPTY8_ADDR                       (WF_PLE_TOP_BASE + 0x620) // 0620
-+#define WF_PLE_TOP_AC0_QUEUE_EMPTY_EXT0_ADDR                   (WF_PLE_TOP_BASE + 0x680) // 0680
-+
-+#define WF_PLE_TOP_AC1_QUEUE_EMPTY0_ADDR                       (WF_PLE_TOP_BASE + 0x700) // 0700
-+#define WF_PLE_TOP_AC1_QUEUE_EMPTY1_ADDR                       (WF_PLE_TOP_BASE + 0x704) // 0704
-+#define WF_PLE_TOP_AC1_QUEUE_EMPTY2_ADDR                       (WF_PLE_TOP_BASE + 0x708) // 0708
-+#define WF_PLE_TOP_AC1_QUEUE_EMPTY3_ADDR                       (WF_PLE_TOP_BASE + 0x70c) // 070C
-+#define WF_PLE_TOP_AC1_QUEUE_EMPTY4_ADDR                       (WF_PLE_TOP_BASE + 0x710) // 0710
-+#define WF_PLE_TOP_AC1_QUEUE_EMPTY5_ADDR                       (WF_PLE_TOP_BASE + 0x714) // 0714
-+#define WF_PLE_TOP_AC1_QUEUE_EMPTY6_ADDR                       (WF_PLE_TOP_BASE + 0x718) // 0718
-+#define WF_PLE_TOP_AC1_QUEUE_EMPTY7_ADDR                       (WF_PLE_TOP_BASE + 0x71c) // 071C
-+#define WF_PLE_TOP_AC1_QUEUE_EMPTY8_ADDR                       (WF_PLE_TOP_BASE + 0x720) // 0720
-+#define WF_PLE_TOP_AC1_QUEUE_EMPTY_EXT0_ADDR                   (WF_PLE_TOP_BASE + 0x780) // 0780
-+
-+#define WF_PLE_TOP_AC2_QUEUE_EMPTY0_ADDR                       (WF_PLE_TOP_BASE + 0x800) // 0800
-+#define WF_PLE_TOP_AC2_QUEUE_EMPTY1_ADDR                       (WF_PLE_TOP_BASE + 0x804) // 0804
-+#define WF_PLE_TOP_AC2_QUEUE_EMPTY2_ADDR                       (WF_PLE_TOP_BASE + 0x808) // 0808
-+#define WF_PLE_TOP_AC2_QUEUE_EMPTY3_ADDR                       (WF_PLE_TOP_BASE + 0x80c) // 080C
-+#define WF_PLE_TOP_AC2_QUEUE_EMPTY4_ADDR                       (WF_PLE_TOP_BASE + 0x810) // 0810
-+#define WF_PLE_TOP_AC2_QUEUE_EMPTY5_ADDR                       (WF_PLE_TOP_BASE + 0x814) // 0814
-+#define WF_PLE_TOP_AC2_QUEUE_EMPTY6_ADDR                       (WF_PLE_TOP_BASE + 0x818) // 0818
-+#define WF_PLE_TOP_AC2_QUEUE_EMPTY7_ADDR                       (WF_PLE_TOP_BASE + 0x81c) // 081C
-+#define WF_PLE_TOP_AC2_QUEUE_EMPTY8_ADDR                       (WF_PLE_TOP_BASE + 0x820) // 0820
-+#define WF_PLE_TOP_AC2_QUEUE_EMPTY_EXT0_ADDR                   (WF_PLE_TOP_BASE + 0x880) // 0880
-+
-+#define WF_PLE_TOP_AC3_QUEUE_EMPTY0_ADDR                       (WF_PLE_TOP_BASE + 0x900) // 0900
-+#define WF_PLE_TOP_AC3_QUEUE_EMPTY1_ADDR                       (WF_PLE_TOP_BASE + 0x904) // 0904
-+#define WF_PLE_TOP_AC3_QUEUE_EMPTY2_ADDR                       (WF_PLE_TOP_BASE + 0x908) // 0908
-+#define WF_PLE_TOP_AC3_QUEUE_EMPTY3_ADDR                       (WF_PLE_TOP_BASE + 0x90c) // 090C
-+#define WF_PLE_TOP_AC3_QUEUE_EMPTY4_ADDR                       (WF_PLE_TOP_BASE + 0x910) // 0910
-+#define WF_PLE_TOP_AC3_QUEUE_EMPTY5_ADDR                       (WF_PLE_TOP_BASE + 0x914) // 0914
-+#define WF_PLE_TOP_AC3_QUEUE_EMPTY6_ADDR                       (WF_PLE_TOP_BASE + 0x918) // 0918
-+#define WF_PLE_TOP_AC3_QUEUE_EMPTY7_ADDR                       (WF_PLE_TOP_BASE + 0x91c) // 091C
-+#define WF_PLE_TOP_AC3_QUEUE_EMPTY8_ADDR                       (WF_PLE_TOP_BASE + 0x920) // 0920
-+#define WF_PLE_TOP_AC3_QUEUE_EMPTY_EXT0_ADDR                   (WF_PLE_TOP_BASE + 0x980) // 0980
-+
-+#define WF_PLE_TOP_QUEUE_EMPTY_ALL_AC_EMPTY_ADDR               WF_PLE_TOP_QUEUE_EMPTY_ADDR
-+#define WF_PLE_TOP_QUEUE_EMPTY_ALL_AC_EMPTY_MASK               0x01000000                // ALL_AC_EMPTY[24]
-+#define WF_PLE_TOP_QUEUE_EMPTY_ALL_AC_EMPTY_SHFT               24
-+
-+#define WF_PLE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_ADDR                WF_PLE_TOP_PBUF_CTRL_ADDR
-+#define WF_PLE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_MASK                0x80000000                // PAGE_SIZE_CFG[31]
-+#define WF_PLE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_SHFT                31
-+#define WF_PLE_TOP_PBUF_CTRL_PBUF_OFFSET_ADDR                  WF_PLE_TOP_PBUF_CTRL_ADDR
-+#define WF_PLE_TOP_PBUF_CTRL_PBUF_OFFSET_MASK                  0x07FE0000                // PBUF_OFFSET[26..17]
-+#define WF_PLE_TOP_PBUF_CTRL_PBUF_OFFSET_SHFT                  17
-+#define WF_PLE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_ADDR               WF_PLE_TOP_PBUF_CTRL_ADDR
-+#define WF_PLE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_MASK               0x00001FFF                // TOTAL_PAGE_NUM[12..0]
-+#define WF_PLE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_SHFT               0
-+
-+#define WF_PLE_TOP_FREEPG_CNT_FFA_CNT_ADDR                     WF_PLE_TOP_FREEPG_CNT_ADDR
-+#define WF_PLE_TOP_FREEPG_CNT_FFA_CNT_MASK                     0x1FFF0000                // FFA_CNT[28..16]
-+#define WF_PLE_TOP_FREEPG_CNT_FFA_CNT_SHFT                     16
-+#define WF_PLE_TOP_FREEPG_CNT_FREEPG_CNT_ADDR                  WF_PLE_TOP_FREEPG_CNT_ADDR
-+#define WF_PLE_TOP_FREEPG_CNT_FREEPG_CNT_MASK                  0x00001FFF                // FREEPG_CNT[12..0]
-+#define WF_PLE_TOP_FREEPG_CNT_FREEPG_CNT_SHFT                  0
-+
-+#define WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_ADDR           WF_PLE_TOP_FREEPG_HEAD_TAIL_ADDR
-+#define WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_MASK           0x1FFF0000                // FREEPG_TAIL[28..16]
-+#define WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_SHFT           16
-+#define WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_ADDR           WF_PLE_TOP_FREEPG_HEAD_TAIL_ADDR
-+#define WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_MASK           0x00001FFF                // FREEPG_HEAD[12..0]
-+#define WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_SHFT           0
-+
-+#define WF_PLE_TOP_PG_HIF_GROUP_HIF_MAX_QUOTA_ADDR             WF_PLE_TOP_PG_HIF_GROUP_ADDR
-+#define WF_PLE_TOP_PG_HIF_GROUP_HIF_MAX_QUOTA_MASK             0x1FFF0000                // HIF_MAX_QUOTA[28..16]
-+#define WF_PLE_TOP_PG_HIF_GROUP_HIF_MAX_QUOTA_SHFT             16
-+#define WF_PLE_TOP_PG_HIF_GROUP_HIF_MIN_QUOTA_ADDR             WF_PLE_TOP_PG_HIF_GROUP_ADDR
-+#define WF_PLE_TOP_PG_HIF_GROUP_HIF_MIN_QUOTA_MASK             0x00001FFF                // HIF_MIN_QUOTA[12..0]
-+#define WF_PLE_TOP_PG_HIF_GROUP_HIF_MIN_QUOTA_SHFT             0
-+
-+#define WF_PLE_TOP_HIF_PG_INFO_HIF_SRC_CNT_ADDR                WF_PLE_TOP_HIF_PG_INFO_ADDR
-+#define WF_PLE_TOP_HIF_PG_INFO_HIF_SRC_CNT_MASK                0x1FFF0000                // HIF_SRC_CNT[28..16]
-+#define WF_PLE_TOP_HIF_PG_INFO_HIF_SRC_CNT_SHFT                16
-+#define WF_PLE_TOP_HIF_PG_INFO_HIF_RSV_CNT_ADDR                WF_PLE_TOP_HIF_PG_INFO_ADDR
-+#define WF_PLE_TOP_HIF_PG_INFO_HIF_RSV_CNT_MASK                0x00001FFF                // HIF_RSV_CNT[12..0]
-+#define WF_PLE_TOP_HIF_PG_INFO_HIF_RSV_CNT_SHFT                0
-+
-+#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MAX_QUOTA_ADDR WF_PLE_TOP_PG_HIF_WMTXD_GROUP_ADDR
-+#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MAX_QUOTA_MASK 0x1FFF0000                // HIF_WMTXD_MAX_QUOTA[28..16]
-+#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MAX_QUOTA_SHFT 16
-+#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MIN_QUOTA_ADDR WF_PLE_TOP_PG_HIF_WMTXD_GROUP_ADDR
-+#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MIN_QUOTA_MASK 0x00001FFF                // HIF_WMTXD_MIN_QUOTA[12..0]
-+#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MIN_QUOTA_SHFT 0
-+
-+#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_SRC_CNT_ADDR    WF_PLE_TOP_HIF_WMTXD_PG_INFO_ADDR
-+#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_SRC_CNT_MASK    0x1FFF0000                // HIF_WMTXD_SRC_CNT[28..16]
-+#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_SRC_CNT_SHFT    16
-+#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_RSV_CNT_ADDR    WF_PLE_TOP_HIF_WMTXD_PG_INFO_ADDR
-+#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_RSV_CNT_MASK    0x00001FFF                // HIF_WMTXD_RSV_CNT[12..0]
-+#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_RSV_CNT_SHFT    0
-+
-+#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MAX_QUOTA_ADDR WF_PLE_TOP_PG_HIF_TXCMD_GROUP_ADDR
-+#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MAX_QUOTA_MASK 0x1FFF0000                // HIF_TXCMD_MAX_QUOTA[28..16]
-+#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MAX_QUOTA_SHFT 16
-+#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MIN_QUOTA_ADDR WF_PLE_TOP_PG_HIF_TXCMD_GROUP_ADDR
-+#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MIN_QUOTA_MASK 0x00001FFF                // HIF_TXCMD_MIN_QUOTA[12..0]
-+#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MIN_QUOTA_SHFT 0
-+
-+#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_SRC_CNT_ADDR    WF_PLE_TOP_HIF_TXCMD_PG_INFO_ADDR
-+#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_SRC_CNT_MASK    0x1FFF0000                // HIF_TXCMD_SRC_CNT[28..16]
-+#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_SRC_CNT_SHFT    16
-+#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_RSV_CNT_ADDR    WF_PLE_TOP_HIF_TXCMD_PG_INFO_ADDR
-+#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_RSV_CNT_MASK    0x00001FFF                // HIF_TXCMD_RSV_CNT[12..0]
-+#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_RSV_CNT_SHFT    0
-+
-+#define WF_PLE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_ADDR             WF_PLE_TOP_PG_CPU_GROUP_ADDR
-+#define WF_PLE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_MASK             0x1FFF0000                // CPU_MAX_QUOTA[28..16]
-+#define WF_PLE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_SHFT             16
-+#define WF_PLE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_ADDR             WF_PLE_TOP_PG_CPU_GROUP_ADDR
-+#define WF_PLE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_MASK             0x00001FFF                // CPU_MIN_QUOTA[12..0]
-+#define WF_PLE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_SHFT             0
-+
-+#define WF_PLE_TOP_CPU_PG_INFO_CPU_SRC_CNT_ADDR                WF_PLE_TOP_CPU_PG_INFO_ADDR
-+#define WF_PLE_TOP_CPU_PG_INFO_CPU_SRC_CNT_MASK                0x1FFF0000                // CPU_SRC_CNT[28..16]
-+#define WF_PLE_TOP_CPU_PG_INFO_CPU_SRC_CNT_SHFT                16
-+#define WF_PLE_TOP_CPU_PG_INFO_CPU_RSV_CNT_ADDR                WF_PLE_TOP_CPU_PG_INFO_ADDR
-+#define WF_PLE_TOP_CPU_PG_INFO_CPU_RSV_CNT_MASK                0x00001FFF                // CPU_RSV_CNT[12..0]
-+#define WF_PLE_TOP_CPU_PG_INFO_CPU_RSV_CNT_SHFT                0
-+
-+#define WF_PLE_TOP_FL_QUE_CTRL_0_EXECUTE_ADDR                  WF_PLE_TOP_FL_QUE_CTRL_0_ADDR
-+#define WF_PLE_TOP_FL_QUE_CTRL_0_EXECUTE_MASK                  0x80000000                // EXECUTE[31]
-+#define WF_PLE_TOP_FL_QUE_CTRL_0_EXECUTE_SHFT                  31
-+#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_ADDR                WF_PLE_TOP_FL_QUE_CTRL_0_ADDR
-+#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_MASK                0x7F000000                // Q_BUF_QID[30..24]
-+#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_SHFT                24
-+#define WF_PLE_TOP_FL_QUE_CTRL_0_FL_BUFFER_ADDR_ADDR           WF_PLE_TOP_FL_QUE_CTRL_0_ADDR
-+#define WF_PLE_TOP_FL_QUE_CTRL_0_FL_BUFFER_ADDR_MASK           0x00FFF000                // FL_BUFFER_ADDR[23..12]
-+#define WF_PLE_TOP_FL_QUE_CTRL_0_FL_BUFFER_ADDR_SHFT           12
-+#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_WLANID_ADDR             WF_PLE_TOP_FL_QUE_CTRL_0_ADDR
-+#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_WLANID_MASK             0x00000FFF                // Q_BUF_WLANID[11..0]
-+#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_WLANID_SHFT             0
-+
-+#define WF_PLE_TOP_FL_QUE_CTRL_1_Q_BUF_TGID_ADDR               WF_PLE_TOP_FL_QUE_CTRL_1_ADDR
-+#define WF_PLE_TOP_FL_QUE_CTRL_1_Q_BUF_TGID_MASK               0xC0000000                // Q_BUF_TGID[31..30]
-+#define WF_PLE_TOP_FL_QUE_CTRL_1_Q_BUF_TGID_SHFT               30
-+#define WF_PLE_TOP_FL_QUE_CTRL_1_Q_BUF_PID_ADDR                WF_PLE_TOP_FL_QUE_CTRL_1_ADDR
-+#define WF_PLE_TOP_FL_QUE_CTRL_1_Q_BUF_PID_MASK                0x30000000                // Q_BUF_PID[29..28]
-+#define WF_PLE_TOP_FL_QUE_CTRL_1_Q_BUF_PID_SHFT                28
-+
-+#define WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_ADDR           WF_PLE_TOP_FL_QUE_CTRL_2_ADDR
-+#define WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_MASK           0x1FFF0000                // QUEUE_TAIL_FID[28..16]
-+#define WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_SHFT           16
-+#define WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_ADDR           WF_PLE_TOP_FL_QUE_CTRL_2_ADDR
-+#define WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_MASK           0x00001FFF                // QUEUE_HEAD_FID[12..0]
-+#define WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_SHFT           0
-+
-+#define WF_PLE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_ADDR            WF_PLE_TOP_FL_QUE_CTRL_3_ADDR
-+#define WF_PLE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_MASK            0x00001FFF                // QUEUE_PKT_NUM[12..0]
-+#define WF_PLE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_SHFT            0
-+
-+#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_TGID_ADDR               WF_PLE_TOP_FL_QUE_CTRL_0_ADDR
-+#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_TGID_MASK               0x00300000                // Q_BUF_TGID[21..20]
-+#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_TGID_SHFT               20
-+#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_ADDR                WF_PLE_TOP_FL_QUE_CTRL_0_ADDR
-+#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_MASK                0x00030000                // Q_BUF_PID[17..16]
-+#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_SHFT                16
-+/* PSE */
-+#define WF_PSE_TOP_BASE                                        0x820c8000
-+
-+#define WF_PSE_TOP_PBUF_CTRL_ADDR                              (WF_PSE_TOP_BASE + 0x04) // 8004
-+#define WF_PSE_TOP_QUEUE_EMPTY_ADDR                            (WF_PSE_TOP_BASE + 0xB0) // 80B0
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_ADDR                          (WF_PSE_TOP_BASE + 0xBC) // 80BC
-+#define WF_PSE_TOP_PG_HIF0_GROUP_ADDR                          (WF_PSE_TOP_BASE + 0x110) // 8110
-+#define WF_PSE_TOP_PG_HIF1_GROUP_ADDR                          (WF_PSE_TOP_BASE + 0x114) // 8114
-+#define WF_PSE_TOP_PG_CPU_GROUP_ADDR                           (WF_PSE_TOP_BASE + 0x118) // 8118
-+#define WF_PSE_TOP_PG_PLE_GROUP_ADDR                           (WF_PSE_TOP_BASE + 0x11C) // 811C
-+#define WF_PSE_TOP_PG_PLE1_GROUP_ADDR                          (WF_PSE_TOP_BASE + 0x120) // 8120
-+#define WF_PSE_TOP_PG_LMAC0_GROUP_ADDR                         (WF_PSE_TOP_BASE + 0x124) // 8124
-+#define WF_PSE_TOP_PG_LMAC1_GROUP_ADDR                         (WF_PSE_TOP_BASE + 0x128) // 8128
-+#define WF_PSE_TOP_PG_LMAC2_GROUP_ADDR                         (WF_PSE_TOP_BASE + 0x12C) // 812C
-+#define WF_PSE_TOP_PG_LMAC3_GROUP_ADDR                         (WF_PSE_TOP_BASE + 0x130) // 8130
-+#define WF_PSE_TOP_PG_MDP_GROUP_ADDR                           (WF_PSE_TOP_BASE + 0x134) // 8134
-+#define WF_PSE_TOP_PG_MDP2_GROUP_ADDR                          (WF_PSE_TOP_BASE + 0x13C) // 813C
-+#define WF_PSE_TOP_PG_HIF2_GROUP_ADDR                          (WF_PSE_TOP_BASE + 0x140) // 8140
-+#define WF_PSE_TOP_PG_MDP3_GROUP_ADDR                          (WF_PSE_TOP_BASE + 0x144) // 8144
-+#define WF_PSE_TOP_HIF0_PG_INFO_ADDR                           (WF_PSE_TOP_BASE + 0x150) // 8150
-+#define WF_PSE_TOP_HIF1_PG_INFO_ADDR                           (WF_PSE_TOP_BASE + 0x154) // 8154
-+#define WF_PSE_TOP_CPU_PG_INFO_ADDR                            (WF_PSE_TOP_BASE + 0x158) // 8158
-+#define WF_PSE_TOP_PLE_PG_INFO_ADDR                            (WF_PSE_TOP_BASE + 0x15C) // 815C
-+#define WF_PSE_TOP_PLE1_PG_INFO_ADDR                           (WF_PSE_TOP_BASE + 0x160) // 8160
-+#define WF_PSE_TOP_LMAC0_PG_INFO_ADDR                          (WF_PSE_TOP_BASE + 0x164) // 8164
-+#define WF_PSE_TOP_LMAC1_PG_INFO_ADDR                          (WF_PSE_TOP_BASE + 0x168) // 8168
-+#define WF_PSE_TOP_LMAC2_PG_INFO_ADDR                          (WF_PSE_TOP_BASE + 0x16C) // 816C
-+#define WF_PSE_TOP_LMAC3_PG_INFO_ADDR                          (WF_PSE_TOP_BASE + 0x170) // 8170
-+#define WF_PSE_TOP_MDP_PG_INFO_ADDR                            (WF_PSE_TOP_BASE + 0x174) // 8174
-+#define WF_PSE_TOP_MDP2_PG_INFO_ADDR                           (WF_PSE_TOP_BASE + 0x17C) // 817C
-+#define WF_PSE_TOP_HIF2_PG_INFO_ADDR                           (WF_PSE_TOP_BASE + 0x180) // 8180
-+#define WF_PSE_TOP_MDP3_PG_INFO_ADDR                           (WF_PSE_TOP_BASE + 0x184) // 8184
-+#define WF_PSE_TOP_FL_QUE_CTRL_0_ADDR                          (WF_PSE_TOP_BASE + 0x1B0) // 81B0
-+#define WF_PSE_TOP_FL_QUE_CTRL_1_ADDR                          (WF_PSE_TOP_BASE + 0x1B4) // 81B4
-+#define WF_PSE_TOP_FL_QUE_CTRL_2_ADDR                          (WF_PSE_TOP_BASE + 0x1B8) // 81B8
-+#define WF_PSE_TOP_FL_QUE_CTRL_3_ADDR                          (WF_PSE_TOP_BASE + 0x1BC) // 81BC
-+#define WF_PSE_TOP_FREEPG_CNT_ADDR                             (WF_PSE_TOP_BASE + 0x3A0) // 83A0
-+#define WF_PSE_TOP_FREEPG_HEAD_TAIL_ADDR                       (WF_PSE_TOP_BASE + 0x3A4) // 83A4
-+
-+#define WF_PSE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_ADDR                WF_PSE_TOP_PBUF_CTRL_ADDR
-+#define WF_PSE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_MASK                0x80000000                // PAGE_SIZE_CFG[31]
-+#define WF_PSE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_SHFT                31
-+#define WF_PSE_TOP_PBUF_CTRL_PBUF_OFFSET_ADDR                  WF_PSE_TOP_PBUF_CTRL_ADDR
-+#define WF_PSE_TOP_PBUF_CTRL_PBUF_OFFSET_MASK                  0x07FE0000                // PBUF_OFFSET[26..17]
-+#define WF_PSE_TOP_PBUF_CTRL_PBUF_OFFSET_SHFT                  17
-+#define WF_PSE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_ADDR               WF_PSE_TOP_PBUF_CTRL_ADDR
-+#define WF_PSE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_MASK               0x00001FFF                // TOTAL_PAGE_NUM[12..0]
-+#define WF_PSE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_SHFT               0
-+
-+#define WF_PSE_TOP_QUEUE_EMPTY_RLS_Q_EMTPY_ADDR                WF_PSE_TOP_QUEUE_EMPTY_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_RLS_Q_EMTPY_MASK                0x80000000                // RLS_Q_EMTPY[31]
-+#define WF_PSE_TOP_QUEUE_EMPTY_RLS_Q_EMTPY_SHFT                31
-+#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q4_EMPTY_ADDR               WF_PSE_TOP_QUEUE_EMPTY_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q4_EMPTY_MASK               0x10000000                // CPU_Q4_EMPTY[28]
-+#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q4_EMPTY_SHFT               28
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC1_QUEUE_EMPTY_ADDR     WF_PSE_TOP_QUEUE_EMPTY_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC1_QUEUE_EMPTY_MASK     0x08000000                // MDP_RXIOC1_QUEUE_EMPTY[27]
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC1_QUEUE_EMPTY_SHFT     27
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC1_QUEUE_EMPTY_ADDR     WF_PSE_TOP_QUEUE_EMPTY_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC1_QUEUE_EMPTY_MASK     0x04000000                // MDP_TXIOC1_QUEUE_EMPTY[26]
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC1_QUEUE_EMPTY_SHFT     26
-+#define WF_PSE_TOP_QUEUE_EMPTY_SEC_TX1_QUEUE_EMPTY_ADDR        WF_PSE_TOP_QUEUE_EMPTY_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_SEC_TX1_QUEUE_EMPTY_MASK        0x02000000                // SEC_TX1_QUEUE_EMPTY[25]
-+#define WF_PSE_TOP_QUEUE_EMPTY_SEC_TX1_QUEUE_EMPTY_SHFT        25
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TX1_QUEUE_EMPTY_ADDR        WF_PSE_TOP_QUEUE_EMPTY_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TX1_QUEUE_EMPTY_MASK        0x01000000                // MDP_TX1_QUEUE_EMPTY[24]
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TX1_QUEUE_EMPTY_SHFT        24
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC_QUEUE_EMPTY_ADDR      WF_PSE_TOP_QUEUE_EMPTY_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC_QUEUE_EMPTY_MASK      0x00800000                // MDP_RXIOC_QUEUE_EMPTY[23]
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC_QUEUE_EMPTY_SHFT      23
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC_QUEUE_EMPTY_ADDR      WF_PSE_TOP_QUEUE_EMPTY_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC_QUEUE_EMPTY_MASK      0x00400000                // MDP_TXIOC_QUEUE_EMPTY[22]
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC_QUEUE_EMPTY_SHFT      22
-+#define WF_PSE_TOP_QUEUE_EMPTY_SFD_PARK_QUEUE_EMPTY_ADDR       WF_PSE_TOP_QUEUE_EMPTY_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_SFD_PARK_QUEUE_EMPTY_MASK       0x00200000                // SFD_PARK_QUEUE_EMPTY[21]
-+#define WF_PSE_TOP_QUEUE_EMPTY_SFD_PARK_QUEUE_EMPTY_SHFT       21
-+#define WF_PSE_TOP_QUEUE_EMPTY_SEC_RX_QUEUE_EMPTY_ADDR         WF_PSE_TOP_QUEUE_EMPTY_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_SEC_RX_QUEUE_EMPTY_MASK         0x00100000                // SEC_RX_QUEUE_EMPTY[20]
-+#define WF_PSE_TOP_QUEUE_EMPTY_SEC_RX_QUEUE_EMPTY_SHFT         20
-+#define WF_PSE_TOP_QUEUE_EMPTY_SEC_TX_QUEUE_EMPTY_ADDR         WF_PSE_TOP_QUEUE_EMPTY_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_SEC_TX_QUEUE_EMPTY_MASK         0x00080000                // SEC_TX_QUEUE_EMPTY[19]
-+#define WF_PSE_TOP_QUEUE_EMPTY_SEC_TX_QUEUE_EMPTY_SHFT         19
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RX_QUEUE_EMPTY_ADDR         WF_PSE_TOP_QUEUE_EMPTY_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RX_QUEUE_EMPTY_MASK         0x00040000                // MDP_RX_QUEUE_EMPTY[18]
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RX_QUEUE_EMPTY_SHFT         18
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TX_QUEUE_EMPTY_ADDR         WF_PSE_TOP_QUEUE_EMPTY_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TX_QUEUE_EMPTY_MASK         0x00020000                // MDP_TX_QUEUE_EMPTY[17]
-+#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TX_QUEUE_EMPTY_SHFT         17
-+#define WF_PSE_TOP_QUEUE_EMPTY_LMAC_TX_QUEUE_EMPTY_ADDR        WF_PSE_TOP_QUEUE_EMPTY_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_LMAC_TX_QUEUE_EMPTY_MASK        0x00010000                // LMAC_TX_QUEUE_EMPTY[16]
-+#define WF_PSE_TOP_QUEUE_EMPTY_LMAC_TX_QUEUE_EMPTY_SHFT        16
-+
-+#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q3_EMPTY_ADDR               WF_PSE_TOP_QUEUE_EMPTY_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q3_EMPTY_MASK               0x00000008                // CPU_Q3_EMPTY[3]
-+#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q3_EMPTY_SHFT               3
-+#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q2_EMPTY_ADDR               WF_PSE_TOP_QUEUE_EMPTY_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q2_EMPTY_MASK               0x00000004                // CPU_Q2_EMPTY[2]
-+#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q2_EMPTY_SHFT               2
-+#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q1_EMPTY_ADDR               WF_PSE_TOP_QUEUE_EMPTY_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q1_EMPTY_MASK               0x00000002                // CPU_Q1_EMPTY[1]
-+#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q1_EMPTY_SHFT               1
-+#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q0_EMPTY_ADDR               WF_PSE_TOP_QUEUE_EMPTY_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q0_EMPTY_MASK               0x00000001                // CPU_Q0_EMPTY[0]
-+#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q0_EMPTY_SHFT               0
-+
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_13_EMPTY_ADDR             WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_13_EMPTY_MASK             0x20000000                // HIF_13_EMPTY[29]
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_13_EMPTY_SHFT             29
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_12_EMPTY_ADDR             WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_12_EMPTY_MASK             0x10000000                // HIF_12_EMPTY[28]
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_12_EMPTY_SHFT             28
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_11_EMPTY_ADDR             WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_11_EMPTY_MASK             0x08000000                // HIF_11_EMPTY[27]
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_11_EMPTY_SHFT             27
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_10_EMPTY_ADDR             WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_10_EMPTY_MASK             0x04000000                // HIF_10_EMPTY[26]
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_10_EMPTY_SHFT             26
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_9_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_9_EMPTY_MASK              0x02000000                // HIF_9_EMPTY[25]
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_9_EMPTY_SHFT              25
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_8_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_8_EMPTY_MASK              0x01000000                // HIF_8_EMPTY[24]
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_8_EMPTY_SHFT              24
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_7_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_7_EMPTY_MASK              0x00800000                // HIF_7_EMPTY[23]
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_7_EMPTY_SHFT              23
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_6_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_6_EMPTY_MASK              0x00400000                // HIF_6_EMPTY[22]
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_6_EMPTY_SHFT              22
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_5_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_5_EMPTY_MASK              0x00200000                // HIF_5_EMPTY[21]
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_5_EMPTY_SHFT              21
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_4_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_4_EMPTY_MASK              0x00100000                // HIF_4_EMPTY[20]
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_4_EMPTY_SHFT              20
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_3_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_3_EMPTY_MASK              0x00080000                // HIF_3_EMPTY[19]
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_3_EMPTY_SHFT              19
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_2_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_2_EMPTY_MASK              0x00040000                // HIF_2_EMPTY[18]
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_2_EMPTY_SHFT              18
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_1_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_1_EMPTY_MASK              0x00020000                // HIF_1_EMPTY[17]
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_1_EMPTY_SHFT              17
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_0_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_0_EMPTY_MASK              0x00010000                // HIF_0_EMPTY[16]
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_0_EMPTY_SHFT              16
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC3_QUEUE_EMPTY_ADDR   WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC3_QUEUE_EMPTY_MASK   0x00008000                // MDP_RXIOC3_QUEUE_EMPTY[15]
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC3_QUEUE_EMPTY_SHFT   15
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC2_QUEUE_EMPTY_ADDR   WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC2_QUEUE_EMPTY_MASK   0x00000800                // MDP_RXIOC2_QUEUE_EMPTY[11]
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC2_QUEUE_EMPTY_SHFT   11
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TXIOC2_QUEUE_EMPTY_ADDR   WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TXIOC2_QUEUE_EMPTY_MASK   0x00000400                // MDP_TXIOC2_QUEUE_EMPTY[10]
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TXIOC2_QUEUE_EMPTY_SHFT   10
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_SEC_TX2_QUEUE_EMPTY_ADDR      WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_SEC_TX2_QUEUE_EMPTY_MASK      0x00000200                // SEC_TX2_QUEUE_EMPTY[9]
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_SEC_TX2_QUEUE_EMPTY_SHFT      9
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TX2_QUEUE_EMPTY_ADDR      WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TX2_QUEUE_EMPTY_MASK      0x00000100                // MDP_TX2_QUEUE_EMPTY[8]
-+#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TX2_QUEUE_EMPTY_SHFT      8
-+
-+#define WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MAX_QUOTA_ADDR           WF_PSE_TOP_PG_HIF0_GROUP_ADDR
-+#define WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MAX_QUOTA_MASK           0x1FFF0000                // HIF0_MAX_QUOTA[28..16]
-+#define WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MAX_QUOTA_SHFT           16
-+#define WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MIN_QUOTA_ADDR           WF_PSE_TOP_PG_HIF0_GROUP_ADDR
-+#define WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MIN_QUOTA_MASK           0x00001FFF                // HIF0_MIN_QUOTA[12..0]
-+#define WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MIN_QUOTA_SHFT           0
-+
-+
-+#define WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MAX_QUOTA_ADDR           WF_PSE_TOP_PG_HIF1_GROUP_ADDR
-+#define WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MAX_QUOTA_MASK           0x1FFF0000                // HIF1_MAX_QUOTA[28..16]
-+#define WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MAX_QUOTA_SHFT           16
-+#define WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MIN_QUOTA_ADDR           WF_PSE_TOP_PG_HIF1_GROUP_ADDR
-+#define WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MIN_QUOTA_MASK           0x00001FFF                // HIF1_MIN_QUOTA[12..0]
-+#define WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MIN_QUOTA_SHFT           0
-+
-+#define WF_PSE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_ADDR             WF_PSE_TOP_PG_CPU_GROUP_ADDR
-+#define WF_PSE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_MASK             0x1FFF0000                // CPU_MAX_QUOTA[28..16]
-+#define WF_PSE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_SHFT             16
-+#define WF_PSE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_ADDR             WF_PSE_TOP_PG_CPU_GROUP_ADDR
-+#define WF_PSE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_MASK             0x00001FFF                // CPU_MIN_QUOTA[12..0]
-+#define WF_PSE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_SHFT             0
-+
-+#define WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_ADDR             WF_PSE_TOP_PG_PLE_GROUP_ADDR
-+#define WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_MASK             0x1FFF0000                // PLE_MAX_QUOTA[28..16]
-+#define WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_SHFT             16
-+#define WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_ADDR             WF_PSE_TOP_PG_PLE_GROUP_ADDR
-+#define WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_MASK             0x00001FFF                // PLE_MIN_QUOTA[12..0]
-+#define WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_SHFT             0
-+
-+#define WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MAX_QUOTA_ADDR         WF_PSE_TOP_PG_LMAC0_GROUP_ADDR
-+#define WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MAX_QUOTA_MASK         0x1FFF0000                // LMAC0_MAX_QUOTA[28..16]
-+#define WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MAX_QUOTA_SHFT         16
-+#define WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MIN_QUOTA_ADDR         WF_PSE_TOP_PG_LMAC0_GROUP_ADDR
-+#define WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MIN_QUOTA_MASK         0x00001FFF                // LMAC0_MIN_QUOTA[12..0]
-+#define WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MIN_QUOTA_SHFT         0
-+
-+#define WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MAX_QUOTA_ADDR         WF_PSE_TOP_PG_LMAC1_GROUP_ADDR
-+#define WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MAX_QUOTA_MASK         0x1FFF0000                // LMAC1_MAX_QUOTA[28..16]
-+#define WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MAX_QUOTA_SHFT         16
-+#define WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MIN_QUOTA_ADDR         WF_PSE_TOP_PG_LMAC1_GROUP_ADDR
-+#define WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MIN_QUOTA_MASK         0x00001FFF                // LMAC1_MIN_QUOTA[12..0]
-+#define WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MIN_QUOTA_SHFT         0
-+
-+#define WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MAX_QUOTA_ADDR         WF_PSE_TOP_PG_LMAC2_GROUP_ADDR
-+#define WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MAX_QUOTA_MASK         0x1FFF0000                // LMAC2_MAX_QUOTA[28..16]
-+#define WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MAX_QUOTA_SHFT         16
-+#define WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MIN_QUOTA_ADDR         WF_PSE_TOP_PG_LMAC2_GROUP_ADDR
-+#define WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MIN_QUOTA_MASK         0x00001FFF                // LMAC2_MIN_QUOTA[12..0]
-+#define WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MIN_QUOTA_SHFT         0
-+
-+#define WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MAX_QUOTA_ADDR         WF_PSE_TOP_PG_LMAC3_GROUP_ADDR
-+#define WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MAX_QUOTA_MASK         0x1FFF0000                // LMAC3_MAX_QUOTA[28..16]
-+#define WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MAX_QUOTA_SHFT         16
-+#define WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MIN_QUOTA_ADDR         WF_PSE_TOP_PG_LMAC3_GROUP_ADDR
-+#define WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MIN_QUOTA_MASK         0x00001FFF                // LMAC3_MIN_QUOTA[12..0]
-+#define WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MIN_QUOTA_SHFT         0
-+
-+#define WF_PSE_TOP_PG_MDP_GROUP_MDP_MAX_QUOTA_ADDR             WF_PSE_TOP_PG_MDP_GROUP_ADDR
-+#define WF_PSE_TOP_PG_MDP_GROUP_MDP_MAX_QUOTA_MASK             0x1FFF0000                // MDP_MAX_QUOTA[28..16]
-+#define WF_PSE_TOP_PG_MDP_GROUP_MDP_MAX_QUOTA_SHFT             16
-+#define WF_PSE_TOP_PG_MDP_GROUP_MDP_MIN_QUOTA_ADDR             WF_PSE_TOP_PG_MDP_GROUP_ADDR
-+#define WF_PSE_TOP_PG_MDP_GROUP_MDP_MIN_QUOTA_MASK             0x00001FFF                // MDP_MIN_QUOTA[12..0]
-+#define WF_PSE_TOP_PG_MDP_GROUP_MDP_MIN_QUOTA_SHFT             0
-+
-+#define WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MAX_QUOTA_ADDR           WF_PSE_TOP_PG_MDP2_GROUP_ADDR
-+#define WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MAX_QUOTA_MASK           0x1FFF0000                // MDP2_MAX_QUOTA[28..16]
-+#define WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MAX_QUOTA_SHFT           16
-+#define WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MIN_QUOTA_ADDR           WF_PSE_TOP_PG_MDP2_GROUP_ADDR
-+#define WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MIN_QUOTA_MASK           0x00001FFF                // MDP2_MIN_QUOTA[12..0]
-+#define WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MIN_QUOTA_SHFT           0
-+
-+#define WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MAX_QUOTA_ADDR           WF_PSE_TOP_PG_HIF2_GROUP_ADDR
-+#define WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MAX_QUOTA_MASK           0x1FFF0000                // HIF2_MAX_QUOTA[28..16]
-+#define WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MAX_QUOTA_SHFT           16
-+#define WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MIN_QUOTA_ADDR           WF_PSE_TOP_PG_HIF2_GROUP_ADDR
-+#define WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MIN_QUOTA_MASK           0x00001FFF                // HIF2_MIN_QUOTA[12..0]
-+#define WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MIN_QUOTA_SHFT           0
-+
-+#define WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MAX_QUOTA_ADDR           WF_PSE_TOP_PG_MDP3_GROUP_ADDR
-+#define WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MAX_QUOTA_MASK           0x1FFF0000                // MDP3_MAX_QUOTA[28..16]
-+#define WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MAX_QUOTA_SHFT           16
-+#define WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MIN_QUOTA_ADDR           WF_PSE_TOP_PG_MDP3_GROUP_ADDR
-+#define WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MIN_QUOTA_MASK           0x00001FFF                // MDP3_MIN_QUOTA[12..0]
-+#define WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MIN_QUOTA_SHFT           0
-+
-+#define WF_PSE_TOP_HIF0_PG_INFO_HIF0_SRC_CNT_ADDR              WF_PSE_TOP_HIF0_PG_INFO_ADDR
-+#define WF_PSE_TOP_HIF0_PG_INFO_HIF0_SRC_CNT_MASK              0x1FFF0000                // HIF0_SRC_CNT[28..16]
-+#define WF_PSE_TOP_HIF0_PG_INFO_HIF0_SRC_CNT_SHFT              16
-+#define WF_PSE_TOP_HIF0_PG_INFO_HIF0_RSV_CNT_ADDR              WF_PSE_TOP_HIF0_PG_INFO_ADDR
-+#define WF_PSE_TOP_HIF0_PG_INFO_HIF0_RSV_CNT_MASK              0x00001FFF                // HIF0_RSV_CNT[12..0]
-+#define WF_PSE_TOP_HIF0_PG_INFO_HIF0_RSV_CNT_SHFT              0
-+
-+#define WF_PSE_TOP_HIF1_PG_INFO_HIF1_SRC_CNT_ADDR              WF_PSE_TOP_HIF1_PG_INFO_ADDR
-+#define WF_PSE_TOP_HIF1_PG_INFO_HIF1_SRC_CNT_MASK              0x1FFF0000                // HIF1_SRC_CNT[28..16]
-+#define WF_PSE_TOP_HIF1_PG_INFO_HIF1_SRC_CNT_SHFT              16
-+#define WF_PSE_TOP_HIF1_PG_INFO_HIF1_RSV_CNT_ADDR              WF_PSE_TOP_HIF1_PG_INFO_ADDR
-+#define WF_PSE_TOP_HIF1_PG_INFO_HIF1_RSV_CNT_MASK              0x00001FFF                // HIF1_RSV_CNT[12..0]
-+#define WF_PSE_TOP_HIF1_PG_INFO_HIF1_RSV_CNT_SHFT              0
-+
-+#define WF_PSE_TOP_CPU_PG_INFO_CPU_SRC_CNT_ADDR                WF_PSE_TOP_CPU_PG_INFO_ADDR
-+#define WF_PSE_TOP_CPU_PG_INFO_CPU_SRC_CNT_MASK                0x1FFF0000                // CPU_SRC_CNT[28..16]
-+#define WF_PSE_TOP_CPU_PG_INFO_CPU_SRC_CNT_SHFT                16
-+#define WF_PSE_TOP_CPU_PG_INFO_CPU_RSV_CNT_ADDR                WF_PSE_TOP_CPU_PG_INFO_ADDR
-+#define WF_PSE_TOP_CPU_PG_INFO_CPU_RSV_CNT_MASK                0x00001FFF                // CPU_RSV_CNT[12..0]
-+#define WF_PSE_TOP_CPU_PG_INFO_CPU_RSV_CNT_SHFT                0
-+
-+#define WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_ADDR                WF_PSE_TOP_PLE_PG_INFO_ADDR
-+#define WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_MASK                0x1FFF0000                // PLE_SRC_CNT[28..16]
-+#define WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_SHFT                16
-+#define WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_ADDR                WF_PSE_TOP_PLE_PG_INFO_ADDR
-+#define WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_MASK                0x00001FFF                // PLE_RSV_CNT[12..0]
-+#define WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_SHFT                0
-+
-+#define WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_SRC_CNT_ADDR            WF_PSE_TOP_LMAC0_PG_INFO_ADDR
-+#define WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_SRC_CNT_MASK            0x1FFF0000                // LMAC0_SRC_CNT[28..16]
-+#define WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_SRC_CNT_SHFT            16
-+#define WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_RSV_CNT_ADDR            WF_PSE_TOP_LMAC0_PG_INFO_ADDR
-+#define WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_RSV_CNT_MASK            0x00001FFF                // LMAC0_RSV_CNT[12..0]
-+#define WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_RSV_CNT_SHFT            0
-+
-+#define WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_SRC_CNT_ADDR            WF_PSE_TOP_LMAC1_PG_INFO_ADDR
-+#define WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_SRC_CNT_MASK            0x1FFF0000                // LMAC1_SRC_CNT[28..16]
-+#define WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_SRC_CNT_SHFT            16
-+#define WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_RSV_CNT_ADDR            WF_PSE_TOP_LMAC1_PG_INFO_ADDR
-+#define WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_RSV_CNT_MASK            0x00001FFF                // LMAC1_RSV_CNT[12..0]
-+#define WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_RSV_CNT_SHFT            0
-+
-+#define WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_SRC_CNT_ADDR            WF_PSE_TOP_LMAC2_PG_INFO_ADDR
-+#define WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_SRC_CNT_MASK            0x1FFF0000                // LMAC2_SRC_CNT[28..16]
-+#define WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_SRC_CNT_SHFT            16
-+#define WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_RSV_CNT_ADDR            WF_PSE_TOP_LMAC2_PG_INFO_ADDR
-+#define WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_RSV_CNT_MASK            0x00001FFF                // LMAC2_RSV_CNT[12..0]
-+#define WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_RSV_CNT_SHFT            0
-+
-+#define WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_SRC_CNT_ADDR            WF_PSE_TOP_LMAC3_PG_INFO_ADDR
-+#define WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_SRC_CNT_MASK            0x1FFF0000                // LMAC3_SRC_CNT[28..16]
-+#define WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_SRC_CNT_SHFT            16
-+#define WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_RSV_CNT_ADDR            WF_PSE_TOP_LMAC3_PG_INFO_ADDR
-+#define WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_RSV_CNT_MASK            0x00001FFF                // LMAC3_RSV_CNT[12..0]
-+#define WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_RSV_CNT_SHFT            0
-+
-+#define WF_PSE_TOP_MDP_PG_INFO_MDP_SRC_CNT_ADDR                WF_PSE_TOP_MDP_PG_INFO_ADDR
-+#define WF_PSE_TOP_MDP_PG_INFO_MDP_SRC_CNT_MASK                0x1FFF0000                // MDP_SRC_CNT[28..16]
-+#define WF_PSE_TOP_MDP_PG_INFO_MDP_SRC_CNT_SHFT                16
-+#define WF_PSE_TOP_MDP_PG_INFO_MDP_RSV_CNT_ADDR                WF_PSE_TOP_MDP_PG_INFO_ADDR
-+#define WF_PSE_TOP_MDP_PG_INFO_MDP_RSV_CNT_MASK                0x00001FFF                // MDP_RSV_CNT[12..0]
-+#define WF_PSE_TOP_MDP_PG_INFO_MDP_RSV_CNT_SHFT                0
-+
-+#define WF_PSE_TOP_MDP2_PG_INFO_MDP2_SRC_CNT_ADDR              WF_PSE_TOP_MDP2_PG_INFO_ADDR
-+#define WF_PSE_TOP_MDP2_PG_INFO_MDP2_SRC_CNT_MASK              0x1FFF0000                // MDP2_SRC_CNT[28..16]
-+#define WF_PSE_TOP_MDP2_PG_INFO_MDP2_SRC_CNT_SHFT              16
-+#define WF_PSE_TOP_MDP2_PG_INFO_MDP2_RSV_CNT_ADDR              WF_PSE_TOP_MDP2_PG_INFO_ADDR
-+#define WF_PSE_TOP_MDP2_PG_INFO_MDP2_RSV_CNT_MASK              0x00001FFF                // MDP2_RSV_CNT[12..0]
-+#define WF_PSE_TOP_MDP2_PG_INFO_MDP2_RSV_CNT_SHFT              0
-+
-+#define WF_PSE_TOP_HIF2_PG_INFO_HIF2_SRC_CNT_ADDR              WF_PSE_TOP_HIF2_PG_INFO_ADDR
-+#define WF_PSE_TOP_HIF2_PG_INFO_HIF2_SRC_CNT_MASK              0x1FFF0000                // HIF2_SRC_CNT[28..16]
-+#define WF_PSE_TOP_HIF2_PG_INFO_HIF2_SRC_CNT_SHFT              16
-+#define WF_PSE_TOP_HIF2_PG_INFO_HIF2_RSV_CNT_ADDR              WF_PSE_TOP_HIF2_PG_INFO_ADDR
-+#define WF_PSE_TOP_HIF2_PG_INFO_HIF2_RSV_CNT_MASK              0x00001FFF                // HIF2_RSV_CNT[12..0]
-+#define WF_PSE_TOP_HIF2_PG_INFO_HIF2_RSV_CNT_SHFT              0
-+
-+#define WF_PSE_TOP_MDP3_PG_INFO_MDP3_SRC_CNT_ADDR              WF_PSE_TOP_MDP3_PG_INFO_ADDR
-+#define WF_PSE_TOP_MDP3_PG_INFO_MDP3_SRC_CNT_MASK              0x1FFF0000                // MDP3_SRC_CNT[28..16]
-+#define WF_PSE_TOP_MDP3_PG_INFO_MDP3_SRC_CNT_SHFT              16
-+#define WF_PSE_TOP_MDP3_PG_INFO_MDP3_RSV_CNT_ADDR              WF_PSE_TOP_MDP3_PG_INFO_ADDR
-+#define WF_PSE_TOP_MDP3_PG_INFO_MDP3_RSV_CNT_MASK              0x00001FFF                // MDP3_RSV_CNT[12..0]
-+#define WF_PSE_TOP_MDP3_PG_INFO_MDP3_RSV_CNT_SHFT              0
-+
-+#define WF_PSE_TOP_FL_QUE_CTRL_0_EXECUTE_ADDR                  WF_PSE_TOP_FL_QUE_CTRL_0_ADDR
-+#define WF_PSE_TOP_FL_QUE_CTRL_0_EXECUTE_MASK                  0x80000000                // EXECUTE[31]
-+#define WF_PSE_TOP_FL_QUE_CTRL_0_EXECUTE_SHFT                  31
-+#define WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_ADDR                WF_PSE_TOP_FL_QUE_CTRL_0_ADDR
-+#define WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_MASK                0x7F000000                // Q_BUF_QID[30..24]
-+#define WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_SHFT                24
-+#define WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_SHFT                16
-+
-+#define WF_PSE_TOP_FL_QUE_CTRL_1_Q_BUF_PID_ADDR                WF_PSE_TOP_FL_QUE_CTRL_1_ADDR
-+#define WF_PSE_TOP_FL_QUE_CTRL_1_Q_BUF_PID_MASK                0x30000000                // Q_BUF_PID[29..28]
-+#define WF_PSE_TOP_FL_QUE_CTRL_1_Q_BUF_PID_SHFT                28
-+
-+#define WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_ADDR           WF_PSE_TOP_FL_QUE_CTRL_2_ADDR
-+#define WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_MASK           0x1FFF0000                // QUEUE_TAIL_FID[28..16]
-+#define WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_SHFT           16
-+#define WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_ADDR           WF_PSE_TOP_FL_QUE_CTRL_2_ADDR
-+#define WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_MASK           0x00001FFF                // QUEUE_HEAD_FID[12..0]
-+#define WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_SHFT           0
-+
-+#define WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PAGE_NUM_ADDR           WF_PSE_TOP_FL_QUE_CTRL_3_ADDR
-+#define WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PAGE_NUM_MASK           0x00FFF000                // QUEUE_PAGE_NUM[23..12]
-+#define WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PAGE_NUM_SHFT           12
-+#define WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_ADDR            WF_PSE_TOP_FL_QUE_CTRL_3_ADDR
-+#define WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_MASK            0x00001FFF                // QUEUE_PKT_NUM[12..0]
-+#define WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_SHFT            0
-+
-+#define WF_PSE_TOP_FREEPG_CNT_FFA_CNT_ADDR                     WF_PSE_TOP_FREEPG_CNT_ADDR
-+#define WF_PSE_TOP_FREEPG_CNT_FFA_CNT_MASK                     0x1FFF0000                // FFA_CNT[28..16]
-+#define WF_PSE_TOP_FREEPG_CNT_FFA_CNT_SHFT                     16
-+#define WF_PSE_TOP_FREEPG_CNT_FREEPG_CNT_ADDR                  WF_PSE_TOP_FREEPG_CNT_ADDR
-+#define WF_PSE_TOP_FREEPG_CNT_FREEPG_CNT_MASK                  0x00001FFF                // FREEPG_CNT[12..0]
-+#define WF_PSE_TOP_FREEPG_CNT_FREEPG_CNT_SHFT                  0
-+
-+#define WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_ADDR           WF_PSE_TOP_FREEPG_HEAD_TAIL_ADDR
-+#define WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_MASK           0x1FFF0000                // FREEPG_TAIL[28..16]
-+#define WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_SHFT           16
-+#define WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_ADDR           WF_PSE_TOP_FREEPG_HEAD_TAIL_ADDR
-+#define WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_MASK           0x00001FFF                // FREEPG_HEAD[12..0]
-+#define WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_SHFT           0
-+
-+/* RXD */
-+enum {
-+	BMAC_GROUP_VLD_1 = 0x01,
-+	BMAC_GROUP_VLD_2 = 0x02,
-+	BMAC_GROUP_VLD_3 = 0x04,
-+	BMAC_GROUP_VLD_4 = 0x08,
-+	BMAC_GROUP_VLD_5 = 0x10,
-+};
-+
- #endif
-diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index c787551..6640448 100644
---- a/mt7996/mtk_debugfs.c
-+++ b/mt7996/mtk_debugfs.c
-@@ -3228,6 +3228,1072 @@ mt7996_thermal_recal_set(void *data, u64 val)
- DEFINE_DEBUGFS_ATTRIBUTE(fops_thermal_recal, NULL,
- 			 mt7996_thermal_recal_set, "%llu\n");
- 
-+static int
-+mt7996_reset_counter(void *data, u64 val)
-+{
-+	struct mt7996_dev *dev = data;
-+	struct mt76_dev *mdev = &dev->mt76;
-+	struct mt76_wcid *wcid;
-+	int ret;
-+
-+	/* Reset read-clear counters in FW and WTBL. */
-+	ret = mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_ADM_STAT);
-+	if (ret)
-+		return ret;
-+
-+	ret = mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_MSDU_COUNT);
-+	if (ret)
-+		return ret;
-+
-+	ret = mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_AIRTIME);
-+	if (ret)
-+		return ret;
-+
-+	ret = mt7996_mcu_get_per_sta_info(mdev, UNI_PER_STA_TX_CNT, 1, &dev->wlan_idx);
-+	if (ret)
-+		return ret;
-+
-+	/* Reset counters in MT76. */
-+	rcu_read_lock();
-+	wcid = rcu_dereference(dev->mt76.wcid[dev->wlan_idx]);
-+	if (wcid)
-+		memset(&wcid->stats, 0, sizeof(struct mt76_sta_stats));
-+	else
-+		ret = -EINVAL;
-+	rcu_read_unlock();
-+
-+	return ret;
-+}
-+DEFINE_DEBUGFS_ATTRIBUTE(fops_reset_counter, NULL, mt7996_reset_counter, "%llu\n");
-+
-+static int
-+mt7996_per_read(struct seq_file *s, void *data)
-+{
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	u16 wlan_idx = dev->wlan_idx;
-+	struct mt76_wcid *wcid;
-+	u32 total, failed;
-+	int ret;
-+
-+	ret = mt7996_mcu_get_per_sta_info(&dev->mt76, UNI_PER_STA_TX_CNT, 1, &wlan_idx);
-+	if (ret)
-+		return ret;
-+
-+	rcu_read_lock();
-+	wcid = rcu_dereference(dev->mt76.wcid[wlan_idx]);
-+	if (wcid) {
-+		total = wcid->stats.tx_total_mpdu_cnt;
-+		failed = wcid->stats.tx_failed_mpdu_cnt;
-+		seq_printf(s, "WCID %hu\tTxTotalMpduCount: %u\tTxFailedMpduCount: %u\tPER: %u.%u%%\n",
-+		           wlan_idx, total, failed,
-+		           total ? failed * 1000 / total / 10 : 0,
-+		           total ? failed * 1000 / total % 10 : 0);
-+	} else {
-+		ret = -EINVAL;
-+	}
-+	rcu_read_unlock();
-+
-+	return ret;
-+}
-+
-+void mt7996_packet_log_to_host(struct mt7996_dev *dev, const void *data, int len, int type, int des_len)
-+{
-+	struct bin_debug_hdr *hdr;
-+	char *buf;
-+
-+	if (len > 1500 - sizeof(*hdr))
-+	len = 1500 - sizeof(*hdr);
-+
-+	buf = kzalloc(sizeof(*hdr) + len, GFP_KERNEL);
-+	if (!buf)
-+		return;
-+
-+	hdr = (struct bin_debug_hdr *)buf;
-+	hdr->magic_num = cpu_to_le32(PKT_BIN_DEBUG_MAGIC);
-+	hdr->serial_id = cpu_to_le16(dev->fw_debug_seq++);
-+	hdr->msg_type = cpu_to_le16(type);
-+	hdr->len = cpu_to_le16(len);
-+	hdr->des_len = cpu_to_le16(des_len);
-+
-+	memcpy(buf + sizeof(*hdr), data, len);
-+
-+	mt7996_debugfs_rx_log(dev, buf, sizeof(*hdr) + len);
-+	kfree(buf);
-+}
-+
-+static int mt7996_rx_token_read(struct seq_file *s, void *data)
-+{
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	int id, count = 0;
-+	struct mt76_rxwi_cache *r;
-+
-+	seq_printf(s, "Rx cut through token:\n");
-+	spin_lock_bh(&dev->mt76.rx_token_lock);
-+	idr_for_each_entry(&dev->mt76.rx_token, r, id) {
-+		count++;
-+	}
-+	seq_printf(s, "\ttotal:%8d used:%8d\n",
-+		   dev->mt76.rx_token_size, count);
-+	spin_unlock_bh(&dev->mt76.rx_token_lock);
-+
-+	return 0;
-+}
-+
-+/* AMSDU SETTING */
-+static ssize_t mt7996_amsdu_algo_write(struct file *file,
-+				   const char __user *user_buf,
-+				   size_t count,
-+				   loff_t *ppos)
-+{
-+	struct mt7996_dev *dev = file->private_data;
-+	char buf[100];
-+	int ret;
-+	struct {
-+		u8 _rsv[4];
-+
-+		u16 tag;
-+		u16 len;
-+
-+		u16 wlan_idx;
-+		u8 algo_en;
-+		u8 rsv[1];
-+	} __packed data = {
-+		.tag = cpu_to_le16(UNI_MEC_AMSDU_ALGO_EN_STA),
-+		.len = cpu_to_le16(sizeof(data) - 4),
-+	};
-+
-+	if (count >= sizeof(buf))
-+		return -EINVAL;
-+
-+	if (copy_from_user(buf, user_buf, count))
-+		return -EFAULT;
-+
-+	if (count && buf[count - 1] == '\n')
-+		buf[count - 1] = '\0';
-+	else
-+		buf[count] = '\0';
-+
-+	if (sscanf(buf, "%hu %hhu", &data.wlan_idx, &data.algo_en) != 2)
-+		return -EINVAL;
-+
-+	if (data.wlan_idx >= mt7996_wtbl_size(dev))
-+		return -EINVAL;
-+
-+	ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MEC), &data,
-+				sizeof(data), true);
-+	if (ret)
-+		return -EINVAL;
-+
-+	return count;
-+}
-+static const struct file_operations fops_amsdu_algo = {
-+	.write = mt7996_amsdu_algo_write,
-+	.read = NULL,
-+	.open = simple_open,
-+	.llseek = default_llseek,
-+};
-+
-+static ssize_t mt7996_amsdu_para_write(struct file *file,
-+				   const char __user *user_buf,
-+				   size_t count,
-+				   loff_t *ppos)
-+{
-+	struct mt7996_dev *dev = file->private_data;
-+	char buf[100];
-+	int ret;
-+	struct {
-+		u8 _rsv[4];
-+
-+		u16 tag;
-+		u16 len;
-+
-+		u16 wlan_idx;
-+		u8  amsdu_en;
-+		u8  num;
-+		u16 lenth;
-+		u8  rsv[2];
-+	} __packed data = {
-+		.tag = cpu_to_le16(UNI_MEC_AMSDU_PARA_STA),
-+		.len = cpu_to_le16(sizeof(data) - 4),
-+	};
-+
-+	if (count >= sizeof(buf))
-+		return -EINVAL;
-+
-+	if (copy_from_user(buf, user_buf, count))
-+		return -EFAULT;
-+
-+	if (count && buf[count - 1] == '\n')
-+		buf[count - 1] = '\0';
-+	else
-+		buf[count] = '\0';
-+
-+	if (sscanf(buf, "%hu %hhu %hhu %hu", &data.wlan_idx, &data.amsdu_en, &data.num, &data.lenth) != 4)
-+		return -EINVAL;
-+
-+	if (data.wlan_idx >= mt7996_wtbl_size(dev))
-+		return -EINVAL;
-+
-+	ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MEC), &data,
-+			  sizeof(data), true);
-+	if (ret)
-+		return -EINVAL;
-+
-+	return count;
-+}
-+static const struct file_operations fops_amsdu_para = {
-+	.write = mt7996_amsdu_para_write,
-+	.read = NULL,
-+	.open = simple_open,
-+	.llseek = default_llseek,
-+};
-+
-+static int mt7996_amsdu_info_read(struct seq_file *s, void *data)
-+{
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	u32 amsdu_cnt[WF_PLE_TOP_AMSDU_PACK_NUM] = {0}, total_cnt;
-+	u8 i;
-+
-+	seq_printf(s, "HW A-MSDU Information:\n");
-+
-+	for (total_cnt = 0, i = 0; i < WF_PLE_TOP_AMSDU_PACK_NUM; ++i) {
-+		amsdu_cnt[i] = mt76_rr(dev, WF_PLE_TOP_AMSDU_PACK_1_MSDU_CNT_ADDR + i * 4);
-+		total_cnt += amsdu_cnt[i];
-+	}
-+
-+	for (i = 0; i < WF_PLE_TOP_AMSDU_PACK_NUM; ++i) {
-+		seq_printf(s, "# of HW A-MSDU containing %hhu MSDU: 0x%x",
-+		           i + 1, amsdu_cnt[i]);
-+		seq_printf(s, "\t(%u.%u%%)\n",
-+		           total_cnt ? amsdu_cnt[i] * 1000 / total_cnt / 10 : 0,
-+		           total_cnt ? amsdu_cnt[i] * 1000 / total_cnt % 10 : 0);
-+	}
-+
-+	return 0;
-+}
-+
-+/* PSE INFO */
-+static struct bmac_queue_info_t pse_queue_empty_info[] = {
-+	{"CPU Q0",  ENUM_UMAC_CPU_PORT_1,     ENUM_UMAC_CTX_Q_0},
-+	{"CPU Q1",  ENUM_UMAC_CPU_PORT_1,     ENUM_UMAC_CTX_Q_1},
-+	{"CPU Q2",  ENUM_UMAC_CPU_PORT_1,     ENUM_UMAC_CTX_Q_2},
-+	{"CPU Q3",  ENUM_UMAC_CPU_PORT_1,     ENUM_UMAC_CTX_Q_3},
-+	{NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, /* 4~7 not defined */
-+	{NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
-+	{NULL, 0, 0}, {NULL, 0, 0},  /* 14~15 not defined */
-+	{"LMAC Q",  ENUM_UMAC_LMAC_PORT_2,    0},
-+	{"MDP TX Q0", ENUM_UMAC_LMAC_PORT_2, 1},
-+	{"MDP RX Q", ENUM_UMAC_LMAC_PORT_2, 2},
-+	{"SEC TX Q0", ENUM_UMAC_LMAC_PORT_2, 3},
-+	{"SEC RX Q", ENUM_UMAC_LMAC_PORT_2, 4},
-+	{"SFD_PARK Q", ENUM_UMAC_LMAC_PORT_2, 5},
-+	{"MDP_TXIOC Q0", ENUM_UMAC_LMAC_PORT_2, 6},
-+	{"MDP_RXIOC Q0", ENUM_UMAC_LMAC_PORT_2, 7},
-+	{"MDP TX Q1", ENUM_UMAC_LMAC_PORT_2, 0x11},
-+	{"SEC TX Q1", ENUM_UMAC_LMAC_PORT_2, 0x13},
-+	{"MDP_TXIOC Q1", ENUM_UMAC_LMAC_PORT_2, 0x16},
-+	{"MDP_RXIOC Q1", ENUM_UMAC_LMAC_PORT_2, 0x17},
-+	{"CPU Q3",  ENUM_UMAC_CPU_PORT_1,     4},
-+	{NULL, 0, 0}, {NULL, 0, 0},
-+	{"RLS Q",  ENUM_PLE_CTRL_PSE_PORT_3, ENUM_UMAC_PLE_CTRL_P3_Q_0X1F}
-+};
-+
-+static struct bmac_queue_info_t pse_queue_empty2_info[] = {
-+	{"MDP_TDPIOC Q0", ENUM_UMAC_LMAC_PORT_2, 0x8},
-+	{"MDP_RDPIOC Q0", ENUM_UMAC_LMAC_PORT_2, 0x9},
-+	{"MDP_TDPIOC Q1", ENUM_UMAC_LMAC_PORT_2, 0x18},
-+	{"MDP_RDPIOC Q1", ENUM_UMAC_LMAC_PORT_2, 0x19},
-+	{"MDP_TDPIOC Q2", ENUM_UMAC_LMAC_PORT_2, 0x28},
-+	{"MDP_RDPIOC Q2", ENUM_UMAC_LMAC_PORT_2, 0x29},
-+	{NULL, 0, 0},
-+	{"MDP_RDPIOC Q3", ENUM_UMAC_LMAC_PORT_2, 0x39},
-+	{"MDP TX Q2", ENUM_UMAC_LMAC_PORT_2, 0x21},
-+	{"SEC TX Q2", ENUM_UMAC_LMAC_PORT_2, 0x23},
-+	{"MDP_TXIOC Q2", ENUM_UMAC_LMAC_PORT_2, 0x26},
-+	{"MDP_RXIOC Q2", ENUM_UMAC_LMAC_PORT_2, 0x27},
-+	{NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
-+	{"MDP_RXIOC Q3", ENUM_UMAC_LMAC_PORT_2, 0x37},
-+	{"HIF Q0", ENUM_UMAC_HIF_PORT_0,    0},
-+	{"HIF Q1", ENUM_UMAC_HIF_PORT_0,    1},
-+	{"HIF Q2", ENUM_UMAC_HIF_PORT_0,    2},
-+	{"HIF Q3", ENUM_UMAC_HIF_PORT_0,    3},
-+	{"HIF Q4", ENUM_UMAC_HIF_PORT_0,    4},
-+	{"HIF Q5", ENUM_UMAC_HIF_PORT_0,    5},
-+	{"HIF Q6", ENUM_UMAC_HIF_PORT_0,    6},
-+	{"HIF Q7", ENUM_UMAC_HIF_PORT_0,    7},
-+	{"HIF Q8", ENUM_UMAC_HIF_PORT_0,    8},
-+	{"HIF Q9", ENUM_UMAC_HIF_PORT_0,    9},
-+	{"HIF Q10", ENUM_UMAC_HIF_PORT_0,    10},
-+	{"HIF Q11", ENUM_UMAC_HIF_PORT_0,    11},
-+	{"HIF Q12", ENUM_UMAC_HIF_PORT_0,    12},
-+	{"HIF Q13", ENUM_UMAC_HIF_PORT_0,    13},
-+	{NULL, 0, 0}, {NULL, 0, 0}
-+};
-+
-+static int
-+mt7996_pseinfo_read(struct seq_file *s, void *data)
-+{
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	u32 pse_buf_ctrl, pg_sz, pg_num;
-+	u32 pse_stat[2], pg_flow_ctrl[28] = {0};
-+	u32 fpg_cnt, ffa_cnt, fpg_head, fpg_tail;
-+	u32 max_q, min_q, rsv_pg, used_pg;
-+	int i;
-+
-+	pse_buf_ctrl = mt76_rr(dev, WF_PSE_TOP_PBUF_CTRL_ADDR);
-+	pse_stat[0] = mt76_rr(dev, WF_PSE_TOP_QUEUE_EMPTY_ADDR);
-+	pse_stat[1] = mt76_rr(dev, WF_PSE_TOP_QUEUE_EMPTY_1_ADDR);
-+	pg_flow_ctrl[0] = mt76_rr(dev, WF_PSE_TOP_FREEPG_CNT_ADDR);
-+	pg_flow_ctrl[1] = mt76_rr(dev, WF_PSE_TOP_FREEPG_HEAD_TAIL_ADDR);
-+	pg_flow_ctrl[2] = mt76_rr(dev, WF_PSE_TOP_PG_HIF0_GROUP_ADDR);
-+	pg_flow_ctrl[3] = mt76_rr(dev, WF_PSE_TOP_HIF0_PG_INFO_ADDR);
-+	pg_flow_ctrl[4] = mt76_rr(dev, WF_PSE_TOP_PG_HIF1_GROUP_ADDR);
-+	pg_flow_ctrl[5] = mt76_rr(dev, WF_PSE_TOP_HIF1_PG_INFO_ADDR);
-+	pg_flow_ctrl[6] = mt76_rr(dev, WF_PSE_TOP_PG_CPU_GROUP_ADDR);
-+	pg_flow_ctrl[7] = mt76_rr(dev, WF_PSE_TOP_CPU_PG_INFO_ADDR);
-+	pg_flow_ctrl[8] = mt76_rr(dev, WF_PSE_TOP_PG_LMAC0_GROUP_ADDR);
-+	pg_flow_ctrl[9] = mt76_rr(dev, WF_PSE_TOP_LMAC0_PG_INFO_ADDR);
-+	pg_flow_ctrl[10] = mt76_rr(dev, WF_PSE_TOP_PG_LMAC1_GROUP_ADDR);
-+	pg_flow_ctrl[11] = mt76_rr(dev, WF_PSE_TOP_LMAC1_PG_INFO_ADDR);
-+	pg_flow_ctrl[12] = mt76_rr(dev, WF_PSE_TOP_PG_LMAC2_GROUP_ADDR);
-+	pg_flow_ctrl[13] = mt76_rr(dev, WF_PSE_TOP_LMAC2_PG_INFO_ADDR);
-+	pg_flow_ctrl[14] = mt76_rr(dev, WF_PSE_TOP_PG_PLE_GROUP_ADDR);
-+	pg_flow_ctrl[15] = mt76_rr(dev, WF_PSE_TOP_PLE_PG_INFO_ADDR);
-+	pg_flow_ctrl[16] = mt76_rr(dev, WF_PSE_TOP_PG_LMAC3_GROUP_ADDR);
-+	pg_flow_ctrl[17] = mt76_rr(dev, WF_PSE_TOP_LMAC3_PG_INFO_ADDR);
-+	pg_flow_ctrl[18] = mt76_rr(dev, WF_PSE_TOP_PG_MDP_GROUP_ADDR);
-+	pg_flow_ctrl[19] = mt76_rr(dev, WF_PSE_TOP_MDP_PG_INFO_ADDR);
-+	pg_flow_ctrl[20] = mt76_rr(dev, WF_PSE_TOP_PG_PLE1_GROUP_ADDR);
-+	pg_flow_ctrl[21] = mt76_rr(dev, WF_PSE_TOP_PLE1_PG_INFO_ADDR);
-+	pg_flow_ctrl[22] = mt76_rr(dev, WF_PSE_TOP_PG_MDP2_GROUP_ADDR);
-+	pg_flow_ctrl[23] = mt76_rr(dev, WF_PSE_TOP_MDP2_PG_INFO_ADDR);
-+	if (mt7996_band_valid(dev, MT_BAND2)) {
-+		pg_flow_ctrl[24] = mt76_rr(dev, WF_PSE_TOP_PG_MDP3_GROUP_ADDR);
-+		pg_flow_ctrl[25] = mt76_rr(dev, WF_PSE_TOP_MDP3_PG_INFO_ADDR);
-+	}
-+	pg_flow_ctrl[26] = mt76_rr(dev, WF_PSE_TOP_PG_HIF2_GROUP_ADDR);
-+	pg_flow_ctrl[27] = mt76_rr(dev, WF_PSE_TOP_HIF2_PG_INFO_ADDR);
-+	/* Configuration Info */
-+	seq_printf(s, "PSE Configuration Info:\n");
-+	seq_printf(s, "\tPacket Buffer Control: 0x%08x\n", pse_buf_ctrl);
-+	pg_sz = (pse_buf_ctrl & WF_PSE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_MASK) >> WF_PSE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_SHFT;
-+	seq_printf(s, "\t\tPage Size=%d(%d bytes per page)\n", pg_sz, (pg_sz == 1 ? 256 : 128));
-+	seq_printf(s, "\t\tPage Offset=%d(in unit of 64KB)\n",
-+			 (pse_buf_ctrl & WF_PSE_TOP_PBUF_CTRL_PBUF_OFFSET_MASK) >> WF_PSE_TOP_PBUF_CTRL_PBUF_OFFSET_SHFT);
-+	pg_num = (pse_buf_ctrl & WF_PSE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_MASK) >> WF_PSE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_SHFT;
-+	seq_printf(s, "\t\tTotal page numbers=%d pages\n", pg_num);
-+	/* Page Flow Control */
-+	seq_printf(s, "PSE Page Flow Control:\n");
-+	seq_printf(s, "\tFree page counter: 0x%08x\n", pg_flow_ctrl[0]);
-+	fpg_cnt = (pg_flow_ctrl[0] & WF_PSE_TOP_FREEPG_CNT_FREEPG_CNT_MASK) >> WF_PSE_TOP_FREEPG_CNT_FREEPG_CNT_SHFT;
-+	seq_printf(s, "\t\tThe toal page number of free=0x%03x\n", fpg_cnt);
-+	ffa_cnt = (pg_flow_ctrl[0] & WF_PSE_TOP_FREEPG_CNT_FFA_CNT_MASK) >> WF_PSE_TOP_FREEPG_CNT_FFA_CNT_SHFT;
-+	seq_printf(s, "\t\tThe free page numbers of free for all=0x%03x\n", ffa_cnt);
-+	seq_printf(s, "\tFree page head and tail: 0x%08x\n", pg_flow_ctrl[1]);
-+	fpg_head = (pg_flow_ctrl[1] & WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_MASK) >> WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_SHFT;
-+	fpg_tail = (pg_flow_ctrl[1] & WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_MASK) >> WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_SHFT;
-+	seq_printf(s, "\t\tThe tail/head page of free page list=0x%03x/0x%03x\n", fpg_tail, fpg_head);
-+	seq_printf(s, "\tReserved page counter of HIF0 group: 0x%08x\n", pg_flow_ctrl[2]);
-+	seq_printf(s, "\tHIF0 group page status: 0x%08x\n", pg_flow_ctrl[3]);
-+	min_q = (pg_flow_ctrl[2] & WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MIN_QUOTA_SHFT;
-+	max_q = (pg_flow_ctrl[2] & WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MAX_QUOTA_SHFT;
-+	seq_printf(s, "\t\tThe max/min quota pages of HIF0 group=0x%03x/0x%03x\n", max_q, min_q);
-+	rsv_pg = (pg_flow_ctrl[3] & WF_PSE_TOP_HIF0_PG_INFO_HIF0_RSV_CNT_MASK) >> WF_PSE_TOP_HIF0_PG_INFO_HIF0_RSV_CNT_SHFT;
-+	used_pg = (pg_flow_ctrl[3] & WF_PSE_TOP_HIF0_PG_INFO_HIF0_SRC_CNT_MASK) >> WF_PSE_TOP_HIF0_PG_INFO_HIF0_SRC_CNT_SHFT;
-+	seq_printf(s, "\t\tThe used/reserved pages of HIF0 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
-+	seq_printf(s, "\tReserved page counter of HIF1 group: 0x%08x\n", pg_flow_ctrl[4]);
-+	seq_printf(s, "\tHIF1 group page status: 0x%08x\n", pg_flow_ctrl[5]);
-+	min_q = (pg_flow_ctrl[4] & WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MIN_QUOTA_SHFT;
-+	max_q = (pg_flow_ctrl[4] & WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MAX_QUOTA_SHFT;
-+	seq_printf(s, "\t\tThe max/min quota pages of HIF1 group=0x%03x/0x%03x\n", max_q, min_q);
-+	rsv_pg = (pg_flow_ctrl[5] & WF_PSE_TOP_HIF1_PG_INFO_HIF1_RSV_CNT_MASK) >> WF_PSE_TOP_HIF1_PG_INFO_HIF1_RSV_CNT_SHFT;
-+	used_pg = (pg_flow_ctrl[5] & WF_PSE_TOP_HIF1_PG_INFO_HIF1_SRC_CNT_MASK) >> WF_PSE_TOP_HIF1_PG_INFO_HIF1_SRC_CNT_SHFT;
-+	seq_printf(s, "\t\tThe used/reserved pages of HIF1 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
-+	seq_printf(s, "\tReserved page counter of HIF2 group: 0x%08x\n", pg_flow_ctrl[26]);
-+	seq_printf(s, "\tHIF2 group page status: 0x%08x\n", pg_flow_ctrl[27]);
-+	min_q = (pg_flow_ctrl[26] & WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MIN_QUOTA_SHFT;
-+	max_q = (pg_flow_ctrl[26] & WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MAX_QUOTA_SHFT;
-+	seq_printf(s, "\t\tThe max/min quota pages of HIF2 group=0x%03x/0x%03x\n", max_q, min_q);
-+	rsv_pg = (pg_flow_ctrl[27] & WF_PSE_TOP_HIF2_PG_INFO_HIF2_RSV_CNT_MASK) >> WF_PSE_TOP_HIF2_PG_INFO_HIF2_RSV_CNT_SHFT;
-+	used_pg = (pg_flow_ctrl[27] & WF_PSE_TOP_HIF2_PG_INFO_HIF2_SRC_CNT_MASK) >> WF_PSE_TOP_HIF2_PG_INFO_HIF2_SRC_CNT_SHFT;
-+	seq_printf(s, "\t\tThe used/reserved pages of HIF2 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
-+	seq_printf(s, "\tReserved page counter of CPU group: 0x%08x\n", pg_flow_ctrl[6]);
-+	seq_printf(s, "\tCPU group page status: 0x%08x\n", pg_flow_ctrl[7]);
-+	min_q = (pg_flow_ctrl[6] & WF_PSE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_SHFT;
-+	max_q = (pg_flow_ctrl[6] & WF_PSE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_SHFT;
-+	seq_printf(s, "\t\tThe max/min quota pages of CPU group=0x%03x/0x%03x\n", max_q, min_q);
-+	rsv_pg = (pg_flow_ctrl[7] & WF_PSE_TOP_CPU_PG_INFO_CPU_RSV_CNT_MASK) >> WF_PSE_TOP_CPU_PG_INFO_CPU_RSV_CNT_SHFT;
-+	used_pg = (pg_flow_ctrl[7] & WF_PSE_TOP_CPU_PG_INFO_CPU_SRC_CNT_MASK) >> WF_PSE_TOP_CPU_PG_INFO_CPU_SRC_CNT_SHFT;
-+	seq_printf(s, "\t\tThe used/reserved pages of CPU group=0x%03x/0x%03x\n", used_pg, rsv_pg);
-+	seq_printf(s, "\tReserved page counter of LMAC0 group: 0x%08x\n", pg_flow_ctrl[8]);
-+	seq_printf(s, "\tLMAC0 group page status: 0x%08x\n", pg_flow_ctrl[9]);
-+	min_q = (pg_flow_ctrl[8] & WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MIN_QUOTA_SHFT;
-+	max_q = (pg_flow_ctrl[8] & WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MAX_QUOTA_SHFT;
-+	seq_printf(s, "\t\tThe max/min quota pages of LMAC0 group=0x%03x/0x%03x\n", max_q, min_q);
-+	rsv_pg = (pg_flow_ctrl[9] & WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_RSV_CNT_MASK) >> WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_RSV_CNT_SHFT;
-+	used_pg = (pg_flow_ctrl[9] & WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_SRC_CNT_MASK) >> WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_SRC_CNT_SHFT;
-+	seq_printf(s, "\t\tThe used/reserved pages of LMAC0 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
-+	seq_printf(s, "\tReserved page counter of LMAC1 group: 0x%08x\n", pg_flow_ctrl[10]);
-+	seq_printf(s, "\tLMAC1 group page status: 0x%08x\n", pg_flow_ctrl[11]);
-+	min_q = (pg_flow_ctrl[10] & WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MIN_QUOTA_SHFT;
-+	max_q = (pg_flow_ctrl[10] & WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MAX_QUOTA_SHFT;
-+	seq_printf(s, "\t\tThe max/min quota pages of LMAC1 group=0x%03x/0x%03x\n", max_q, min_q);
-+	rsv_pg = (pg_flow_ctrl[11] & WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_RSV_CNT_MASK) >> WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_RSV_CNT_SHFT;
-+	used_pg = (pg_flow_ctrl[11] & WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_SRC_CNT_MASK) >> WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_SRC_CNT_SHFT;
-+	seq_printf(s, "\t\tThe used/reserved pages of LMAC1 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
-+	seq_printf(s, "\tReserved page counter of LMAC2 group: 0x%08x\n", pg_flow_ctrl[11]);
-+	seq_printf(s, "\tLMAC2 group page status: 0x%08x\n", pg_flow_ctrl[12]);
-+	min_q = (pg_flow_ctrl[12] & WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MIN_QUOTA_SHFT;
-+	max_q = (pg_flow_ctrl[12] & WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MAX_QUOTA_SHFT;
-+	seq_printf(s, "\t\tThe max/min quota pages of LMAC2 group=0x%03x/0x%03x\n", max_q, min_q);
-+	rsv_pg = (pg_flow_ctrl[13] & WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_RSV_CNT_MASK) >> WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_RSV_CNT_SHFT;
-+	used_pg = (pg_flow_ctrl[13] & WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_SRC_CNT_MASK) >> WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_SRC_CNT_SHFT;
-+	seq_printf(s, "\t\tThe used/reserved pages of LMAC2 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
-+
-+	seq_printf(s, "\tReserved page counter of LMAC3 group: 0x%08x\n", pg_flow_ctrl[16]);
-+	seq_printf(s, "\tLMAC3 group page status: 0x%08x\n", pg_flow_ctrl[17]);
-+	min_q = (pg_flow_ctrl[16] & WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MIN_QUOTA_SHFT;
-+	max_q = (pg_flow_ctrl[16] & WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MAX_QUOTA_SHFT;
-+	seq_printf(s, "\t\tThe max/min quota pages of LMAC3 group=0x%03x/0x%03x\n", max_q, min_q);
-+	rsv_pg = (pg_flow_ctrl[17] & WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_RSV_CNT_MASK) >> WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_RSV_CNT_SHFT;
-+	used_pg = (pg_flow_ctrl[17] & WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_SRC_CNT_MASK) >> WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_SRC_CNT_SHFT;
-+	seq_printf(s, "\t\tThe used/reserved pages of LMAC3 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
-+
-+	seq_printf(s, "\tReserved page counter of PLE group: 0x%08x\n", pg_flow_ctrl[14]);
-+	seq_printf(s, "\tPLE group page status: 0x%08x\n", pg_flow_ctrl[15]);
-+	min_q = (pg_flow_ctrl[14] & WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_SHFT;
-+	max_q = (pg_flow_ctrl[14] & WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_SHFT;
-+	seq_printf(s, "\t\tThe max/min quota pages of PLE group=0x%03x/0x%03x\n", max_q, min_q);
-+	rsv_pg = (pg_flow_ctrl[15] & WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_MASK) >> WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_SHFT;
-+	used_pg = (pg_flow_ctrl[15] & WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_MASK) >> WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_SHFT;
-+	seq_printf(s, "\t\tThe used/reserved pages of PLE group=0x%03x/0x%03x\n", used_pg, rsv_pg);
-+
-+	seq_printf(s, "\tReserved page counter of PLE1 group: 0x%08x\n", pg_flow_ctrl[14]);
-+	seq_printf(s, "\tPLE1 group page status: 0x%08x\n", pg_flow_ctrl[15]);
-+	min_q = (pg_flow_ctrl[20] & WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_SHFT;
-+	max_q = (pg_flow_ctrl[20] & WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_SHFT;
-+	seq_printf(s, "\t\tThe max/min quota pages of PLE1 group=0x%03x/0x%03x\n", max_q, min_q);
-+	rsv_pg = (pg_flow_ctrl[21] & WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_MASK) >> WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_SHFT;
-+	used_pg = (pg_flow_ctrl[21] & WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_MASK) >> WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_SHFT;
-+	seq_printf(s, "\t\tThe used/reserved pages of PLE1 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
-+
-+	seq_printf(s, "\tReserved page counter of MDP group: 0x%08x\n", pg_flow_ctrl[18]);
-+	seq_printf(s, "\tMDP group page status: 0x%08x\n", pg_flow_ctrl[19]);
-+	min_q = (pg_flow_ctrl[18] & WF_PSE_TOP_PG_MDP_GROUP_MDP_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_MDP_GROUP_MDP_MIN_QUOTA_SHFT;
-+	max_q = (pg_flow_ctrl[18] & WF_PSE_TOP_PG_MDP_GROUP_MDP_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_MDP_GROUP_MDP_MAX_QUOTA_SHFT;
-+	seq_printf(s, "\t\tThe max/min quota pages of MDP group=0x%03x/0x%03x\n", max_q, min_q);
-+	rsv_pg = (pg_flow_ctrl[19] & WF_PSE_TOP_MDP_PG_INFO_MDP_RSV_CNT_MASK) >> WF_PSE_TOP_MDP_PG_INFO_MDP_RSV_CNT_SHFT;
-+	used_pg = (pg_flow_ctrl[19] & WF_PSE_TOP_MDP_PG_INFO_MDP_SRC_CNT_MASK) >> WF_PSE_TOP_MDP_PG_INFO_MDP_SRC_CNT_SHFT;
-+	seq_printf(s, "\t\tThe used/reserved pages of MDP group=0x%03x/0x%03x\n", used_pg, rsv_pg);
-+	seq_printf(s, "\tReserved page counter of MDP2 group: 0x%08x\n", pg_flow_ctrl[22]);
-+	seq_printf(s, "\tMDP2 group page status: 0x%08x\n", pg_flow_ctrl[23]);
-+	min_q = (pg_flow_ctrl[22] & WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MIN_QUOTA_SHFT;
-+	max_q = (pg_flow_ctrl[22] & WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MAX_QUOTA_SHFT;
-+	seq_printf(s, "\t\tThe max/min quota pages of MDP2 group=0x%03x/0x%03x\n", max_q, min_q);
-+	rsv_pg = (pg_flow_ctrl[23] & WF_PSE_TOP_MDP2_PG_INFO_MDP2_RSV_CNT_MASK) >> WF_PSE_TOP_MDP2_PG_INFO_MDP2_RSV_CNT_SHFT;
-+	used_pg = (pg_flow_ctrl[23] & WF_PSE_TOP_MDP2_PG_INFO_MDP2_SRC_CNT_MASK) >> WF_PSE_TOP_MDP2_PG_INFO_MDP2_SRC_CNT_SHFT;
-+	seq_printf(s, "\t\tThe used/reserved pages of MDP2 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
-+	if (mt7996_band_valid(dev, MT_BAND2)) {
-+		seq_printf(s, "\tReserved page counter of MDP3 group: 0x%08x\n", pg_flow_ctrl[24]);
-+		seq_printf(s, "\tMDP3 group page status: 0x%08x\n", pg_flow_ctrl[25]);
-+		min_q = (pg_flow_ctrl[24] & WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MIN_QUOTA_SHFT;
-+		max_q = (pg_flow_ctrl[24] & WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MAX_QUOTA_SHFT;
-+		seq_printf(s, "\t\tThe max/min quota pages of MDP3 group=0x%03x/0x%03x\n", max_q, min_q);
-+		rsv_pg = (pg_flow_ctrl[25] & WF_PSE_TOP_MDP3_PG_INFO_MDP3_RSV_CNT_MASK) >> WF_PSE_TOP_MDP3_PG_INFO_MDP3_RSV_CNT_SHFT;
-+		used_pg = (pg_flow_ctrl[25] & WF_PSE_TOP_MDP3_PG_INFO_MDP3_SRC_CNT_MASK) >> WF_PSE_TOP_MDP3_PG_INFO_MDP3_SRC_CNT_SHFT;
-+		seq_printf(s, "\t\tThe used/reserved pages of MDP3 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
-+	}
-+	/* Queue Empty Status */
-+	seq_printf(s, "PSE Queue Empty Status:\n");
-+	seq_printf(s, "\tQUEUE_EMPTY: 0x%08x, QUEUE_EMPTY2: 0x%08x\n", pse_stat[0], pse_stat[1]);
-+	seq_printf(s, "\t\tCPU Q0/1/2/3/4 empty=%d/%d/%d/%d/%d\n",
-+			  (pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_CPU_Q0_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_CPU_Q0_EMPTY_SHFT,
-+			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_CPU_Q1_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_CPU_Q1_EMPTY_SHFT),
-+			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_CPU_Q2_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_CPU_Q2_EMPTY_SHFT),
-+			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_CPU_Q3_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_CPU_Q3_EMPTY_SHFT),
-+			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_CPU_Q4_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_CPU_Q4_EMPTY_SHFT));
-+	seq_printf(s, "\t\tHIF Q0/1/2/3/4/5/6/7/8/9/10/11/12/13 empty=%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d\n",
-+			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_0_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_0_EMPTY_SHFT),
-+			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_1_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_1_EMPTY_SHFT),
-+			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_2_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_2_EMPTY_SHFT),
-+			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_3_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_3_EMPTY_SHFT),
-+			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_4_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_4_EMPTY_SHFT),
-+			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_5_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_5_EMPTY_SHFT),
-+			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_6_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_6_EMPTY_SHFT),
-+			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_7_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_7_EMPTY_SHFT),
-+			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_8_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_8_EMPTY_SHFT),
-+			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_9_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_9_EMPTY_SHFT),
-+			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_10_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_10_EMPTY_SHFT),
-+			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_11_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_11_EMPTY_SHFT),
-+			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_12_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_12_EMPTY_SHFT),
-+			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_13_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_13_EMPTY_SHFT));
-+	seq_printf(s, "\t\tLMAC TX Q empty=%d\n",
-+			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_LMAC_TX_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_LMAC_TX_QUEUE_EMPTY_SHFT));
-+	seq_printf(s, "\t\tMDP TX Q0/Q1/Q2/RX Q empty=%d/%d/%d/%d\n",
-+			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_TX_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_TX_QUEUE_EMPTY_SHFT),
-+			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_TX1_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_TX1_QUEUE_EMPTY_SHFT),
-+			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TX2_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TX2_QUEUE_EMPTY_SHFT),
-+			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_RX_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_RX_QUEUE_EMPTY_SHFT));
-+	seq_printf(s, "\t\tSEC TX Q0/Q1/Q2/RX Q empty=%d/%d/%d/%d\n",
-+			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_SEC_TX_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_SEC_TX_QUEUE_EMPTY_SHFT),
-+			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_SEC_TX1_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_SEC_TX1_QUEUE_EMPTY_SHFT),
-+			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_SEC_TX2_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_SEC_TX2_QUEUE_EMPTY_SHFT),
-+			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_SEC_RX_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_SEC_RX_QUEUE_EMPTY_SHFT));
-+	seq_printf(s, "\t\tSFD PARK Q empty=%d\n",
-+			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_SFD_PARK_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_SFD_PARK_QUEUE_EMPTY_SHFT));
-+	seq_printf(s, "\t\tMDP TXIOC Q0/Q1/Q2 empty=%d/%d/%d\n",
-+			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC_QUEUE_EMPTY_SHFT),
-+			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC1_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC1_QUEUE_EMPTY_SHFT),
-+			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TXIOC2_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TXIOC2_QUEUE_EMPTY_SHFT));
-+	seq_printf(s, "\t\tMDP RXIOC Q0/Q1/Q2/Q3 empty=%d/%d/%d/%d\n",
-+			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC_QUEUE_EMPTY_SHFT),
-+			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC1_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC1_QUEUE_EMPTY_SHFT),
-+			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC2_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC2_QUEUE_EMPTY_SHFT),
-+			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC3_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC3_QUEUE_EMPTY_SHFT));
-+	seq_printf(s, "\t\tRLS Q empty=%d\n",
-+			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_RLS_Q_EMTPY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_RLS_Q_EMTPY_SHFT));
-+	seq_printf(s, "Nonempty Q info:\n");
-+
-+	for (i = 0; i < 31; i++) {
-+		if (((pse_stat[0] & (0x1 << i)) >> i) == 0) {
-+			u32 hfid, tfid, pktcnt, fl_que_ctrl[3] = {0};
-+
-+			if (pse_queue_empty_info[i].QueueName != NULL) {
-+				seq_printf(s, "\t%s: ", pse_queue_empty_info[i].QueueName);
-+				fl_que_ctrl[0] |= WF_PSE_TOP_FL_QUE_CTRL_0_EXECUTE_MASK;
-+				fl_que_ctrl[0] |= (pse_queue_empty_info[i].Portid << WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_SHFT);
-+				fl_que_ctrl[0] |= (pse_queue_empty_info[i].Queueid << WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_SHFT);
-+			} else
-+				continue;
-+
-+			fl_que_ctrl[0] |= (0x1 << 31);
-+			mt76_wr(dev, WF_PSE_TOP_FL_QUE_CTRL_0_ADDR, fl_que_ctrl[0]);
-+			fl_que_ctrl[1] = mt76_rr(dev, WF_PSE_TOP_FL_QUE_CTRL_2_ADDR);
-+			fl_que_ctrl[2] = mt76_rr(dev, WF_PSE_TOP_FL_QUE_CTRL_3_ADDR);
-+			hfid = (fl_que_ctrl[1] & WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_MASK) >> WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_SHFT;
-+			tfid = (fl_que_ctrl[1] & WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_MASK) >> WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_SHFT;
-+			pktcnt = (fl_que_ctrl[2] & WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_MASK) >> WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_SHFT;
-+			seq_printf(s, "tail/head fid = 0x%03x/0x%03x, pkt cnt = 0x%03x\n",
-+					  tfid, hfid, pktcnt);
-+		}
-+	}
-+
-+	for (i = 0; i < 31; i++) {
-+		if (((pse_stat[1] & (0x1 << i)) >> i) == 0) {
-+			u32 hfid, tfid, pktcnt, fl_que_ctrl[3] = {0};
-+
-+			if (pse_queue_empty2_info[i].QueueName != NULL) {
-+				seq_printf(s, "\t%s: ", pse_queue_empty2_info[i].QueueName);
-+				fl_que_ctrl[0] |= WF_PSE_TOP_FL_QUE_CTRL_0_EXECUTE_MASK;
-+				fl_que_ctrl[0] |= (pse_queue_empty2_info[i].Portid << WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_SHFT);
-+				fl_que_ctrl[0] |= (pse_queue_empty2_info[i].Queueid << WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_SHFT);
-+			} else
-+				continue;
-+
-+			fl_que_ctrl[0] |= (0x1 << 31);
-+			mt76_wr(dev, WF_PSE_TOP_FL_QUE_CTRL_0_ADDR, fl_que_ctrl[0]);
-+			fl_que_ctrl[1] = mt76_rr(dev, WF_PSE_TOP_FL_QUE_CTRL_2_ADDR);
-+			fl_que_ctrl[2] = mt76_rr(dev, WF_PSE_TOP_FL_QUE_CTRL_3_ADDR);
-+			hfid = (fl_que_ctrl[1] & WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_MASK) >> WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_SHFT;
-+			tfid = (fl_que_ctrl[1] & WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_MASK) >> WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_SHFT;
-+			pktcnt = (fl_que_ctrl[2] & WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_MASK) >> WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_SHFT;
-+			seq_printf(s, "tail/head fid = 0x%03x/0x%03x, pkt cnt = 0x%03x\n",
-+					  tfid, hfid, pktcnt);
-+		}
-+	}
-+
-+	return 0;
-+}
-+
-+/* PLE INFO */
-+static char *sta_ctrl_reg[] = {"ENABLE", "DISABLE", "PAUSE", "TWT_PAUSE"};
-+static struct bmac_queue_info ple_queue_empty_info[] = {
-+	{"CPU Q0",  ENUM_UMAC_CPU_PORT_1,     ENUM_UMAC_CTX_Q_0, 0},
-+	{"CPU Q1",  ENUM_UMAC_CPU_PORT_1,     ENUM_UMAC_CTX_Q_1, 0},
-+	{"CPU Q2",  ENUM_UMAC_CPU_PORT_1,     ENUM_UMAC_CTX_Q_2, 0},
-+	{"CPU Q3",  ENUM_UMAC_CPU_PORT_1,     ENUM_UMAC_CTX_Q_3, 0},
-+	{"ALTX Q0", ENUM_UMAC_LMAC_PORT_2,    0x10, 0},
-+	{"BMC Q0",  ENUM_UMAC_LMAC_PORT_2,    0x11, 0},
-+	{"BCN Q0",  ENUM_UMAC_LMAC_PORT_2,    0x12, 0},
-+	{"PSMP Q0", ENUM_UMAC_LMAC_PORT_2,    0x13, 0},
-+	{"ALTX Q1", ENUM_UMAC_LMAC_PORT_2,    0x10, 1},
-+	{"BMC Q1",  ENUM_UMAC_LMAC_PORT_2,    0x11, 1},
-+	{"BCN Q1",  ENUM_UMAC_LMAC_PORT_2,    0x12, 1},
-+	{"PSMP Q1", ENUM_UMAC_LMAC_PORT_2,    0x13, 1},
-+	{"ALTX Q2", ENUM_UMAC_LMAC_PORT_2,    0x10, 2},
-+	{"BMC Q2",  ENUM_UMAC_LMAC_PORT_2,    0x11, 2},
-+	{"BCN Q2",  ENUM_UMAC_LMAC_PORT_2,    0x12, 2},
-+	{"PSMP Q2", ENUM_UMAC_LMAC_PORT_2,    0x13, 2},
-+	{"NAF Q",   ENUM_UMAC_LMAC_PORT_2,    0x18, 0},
-+	{"NBCN Q",  ENUM_UMAC_LMAC_PORT_2,    0x19, 0},
-+	{NULL, 0, 0, 0}, {NULL, 0, 0, 0}, /* 18, 19 not defined */
-+	{"FIXFID Q", ENUM_UMAC_LMAC_PORT_2, 0x1a, 0},
-+	{NULL, 0, 0, 0}, {NULL, 0, 0, 0}, {NULL, 0, 0, 0}, {NULL, 0, 0, 0}, {NULL, 0, 0, 0},
-+	{NULL, 0, 0, 0}, {NULL, 0, 0, 0},
-+	{"RLS4 Q",   ENUM_PLE_CTRL_PSE_PORT_3, 0x7c, 0},
-+	{"RLS3 Q",   ENUM_PLE_CTRL_PSE_PORT_3, 0x7d, 0},
-+	{"RLS2 Q",   ENUM_PLE_CTRL_PSE_PORT_3, 0x7e, 0},
-+	{"RLS Q",  ENUM_PLE_CTRL_PSE_PORT_3, 0x7f, 0}
-+};
-+
-+static struct bmac_queue_info_t ple_txcmd_queue_empty_info[__MT_MAX_BAND][32] = {
-+	{{"AC00Q", ENUM_UMAC_LMAC_PORT_2, 0x40},
-+	 {"AC01Q", ENUM_UMAC_LMAC_PORT_2, 0x41},
-+	 {"AC02Q", ENUM_UMAC_LMAC_PORT_2, 0x42},
-+	 {"AC03Q", ENUM_UMAC_LMAC_PORT_2, 0x43},
-+	 {"AC10Q", ENUM_UMAC_LMAC_PORT_2, 0x44},
-+	 {"AC11Q", ENUM_UMAC_LMAC_PORT_2, 0x45},
-+	 {"AC12Q", ENUM_UMAC_LMAC_PORT_2, 0x46},
-+	 {"AC13Q", ENUM_UMAC_LMAC_PORT_2, 0x47},
-+	 {"AC20Q", ENUM_UMAC_LMAC_PORT_2, 0x48},
-+	 {"AC21Q", ENUM_UMAC_LMAC_PORT_2, 0x49},
-+	 {"AC22Q", ENUM_UMAC_LMAC_PORT_2, 0x4a},
-+	 {"AC23Q", ENUM_UMAC_LMAC_PORT_2, 0x4b},
-+	 {"AC30Q", ENUM_UMAC_LMAC_PORT_2, 0x4c},
-+	 {"AC31Q", ENUM_UMAC_LMAC_PORT_2, 0x4d},
-+	 {"AC32Q", ENUM_UMAC_LMAC_PORT_2, 0x4e},
-+	 {"AC33Q", ENUM_UMAC_LMAC_PORT_2, 0x4f},
-+	 {"ALTX Q0", ENUM_UMAC_LMAC_PORT_2, 0x70},
-+	 {"TF Q0", ENUM_UMAC_LMAC_PORT_2, 0x71},
-+	 {"TWT TSF-TF Q0", ENUM_UMAC_LMAC_PORT_2, 0x72},
-+	 {"TWT DL Q0", ENUM_UMAC_LMAC_PORT_2, 0x73},
-+	 {"TWT UL Q0", ENUM_UMAC_LMAC_PORT_2, 0x74},
-+	 {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
-+	 {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
-+	 {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}},
-+
-+	{{"AC00Q", ENUM_UMAC_LMAC_PORT_2, 0x50},
-+	 {"AC01Q", ENUM_UMAC_LMAC_PORT_2, 0x51},
-+	 {"AC02Q", ENUM_UMAC_LMAC_PORT_2, 0x52},
-+	 {"AC03Q", ENUM_UMAC_LMAC_PORT_2, 0x53},
-+	 {"AC10Q", ENUM_UMAC_LMAC_PORT_2, 0x54},
-+	 {"AC11Q", ENUM_UMAC_LMAC_PORT_2, 0x55},
-+	 {"AC12Q", ENUM_UMAC_LMAC_PORT_2, 0x56},
-+	 {"AC13Q", ENUM_UMAC_LMAC_PORT_2, 0x57},
-+	 {"AC20Q", ENUM_UMAC_LMAC_PORT_2, 0x58},
-+	 {"AC21Q", ENUM_UMAC_LMAC_PORT_2, 0x59},
-+	 {"AC22Q", ENUM_UMAC_LMAC_PORT_2, 0x5a},
-+	 {"AC23Q", ENUM_UMAC_LMAC_PORT_2, 0x5b},
-+	 {"AC30Q", ENUM_UMAC_LMAC_PORT_2, 0x5c},
-+	 {"AC31Q", ENUM_UMAC_LMAC_PORT_2, 0x5d},
-+	 {"AC32Q", ENUM_UMAC_LMAC_PORT_2, 0x5e},
-+	 {"AC33Q", ENUM_UMAC_LMAC_PORT_2, 0x5f},
-+	 {"ALTX Q0", ENUM_UMAC_LMAC_PORT_2, 0x75},
-+	 {"TF Q0", ENUM_UMAC_LMAC_PORT_2, 0x76},
-+	 {"TWT TSF-TF Q0", ENUM_UMAC_LMAC_PORT_2, 0x77},
-+	 {"TWT DL Q0", ENUM_UMAC_LMAC_PORT_2, 0x78},
-+	 {"TWT UL Q0", ENUM_UMAC_LMAC_PORT_2, 0x79},
-+	 {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
-+	 {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
-+	 {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}},
-+
-+	{{"AC00Q", ENUM_UMAC_LMAC_PORT_2, 0x60},
-+	 {"AC01Q", ENUM_UMAC_LMAC_PORT_2, 0x61},
-+	 {"AC02Q", ENUM_UMAC_LMAC_PORT_2, 0x62},
-+	 {"AC03Q", ENUM_UMAC_LMAC_PORT_2, 0x63},
-+	 {"AC10Q", ENUM_UMAC_LMAC_PORT_2, 0x64},
-+	 {"AC11Q", ENUM_UMAC_LMAC_PORT_2, 0x65},
-+	 {"AC12Q", ENUM_UMAC_LMAC_PORT_2, 0x66},
-+	 {"AC13Q", ENUM_UMAC_LMAC_PORT_2, 0x67},
-+	 {"AC20Q", ENUM_UMAC_LMAC_PORT_2, 0x68},
-+	 {"AC21Q", ENUM_UMAC_LMAC_PORT_2, 0x69},
-+	 {"AC22Q", ENUM_UMAC_LMAC_PORT_2, 0x6a},
-+	 {"AC23Q", ENUM_UMAC_LMAC_PORT_2, 0x6b},
-+	 {"AC30Q", ENUM_UMAC_LMAC_PORT_2, 0x6c},
-+	 {"AC31Q", ENUM_UMAC_LMAC_PORT_2, 0x6d},
-+	 {"AC32Q", ENUM_UMAC_LMAC_PORT_2, 0x6e},
-+	 {"AC33Q", ENUM_UMAC_LMAC_PORT_2, 0x6f},
-+	 {"ALTX Q0", ENUM_UMAC_LMAC_PORT_2, 0x7a},
-+	 {"TF Q0", ENUM_UMAC_LMAC_PORT_2, 0x7b},
-+	 {"TWT TSF-TF Q0", ENUM_UMAC_LMAC_PORT_2, 0x7c},
-+	 {"TWT DL Q0", ENUM_UMAC_LMAC_PORT_2, 0x7d},
-+	 {"TWT UL Q0", ENUM_UMAC_LMAC_PORT_2, 0x7e},
-+	 {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
-+	 {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
-+	 {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}}
-+};
-+
-+static size_t
-+ple_cr_num_of_ac(struct mt76_dev *dev)
-+{
-+	switch (mt76_chip(dev)) {
-+	case 0x7990:
-+		return CR_NUM_OF_AC_MT7996;
-+	case 0x7992:
-+	default:
-+		return CR_NUM_OF_AC_MT7992;
-+	}
-+}
-+
-+static void
-+mt7996_show_ple_pg_info(struct mt7996_dev *dev, struct seq_file *s)
-+{
-+	u32 val[2];
-+
-+	seq_printf(s, "PLE Configuration Info:\n");
-+
-+	val[0] = mt76_rr(dev, WF_PLE_TOP_PBUF_CTRL_ADDR);
-+	seq_printf(s, "\tPacket Buffer Control: 0x%08x\n", val[0]);
-+	seq_printf(s, "\t\tPage size: %u bytes\n",
-+		   u32_get_bits(val[0], WF_PLE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_MASK) ? 128 : 64);
-+	seq_printf(s, "\t\tPacket buffer offset: %u (unit: 2KB)\n",
-+		   u32_get_bits(val[0], WF_PLE_TOP_PBUF_CTRL_PBUF_OFFSET_MASK));
-+	seq_printf(s, "\t\tTotal number of pages: %u pages\n",
-+		   u32_get_bits(val[0], WF_PLE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_MASK));
-+
-+	seq_printf(s, "PLE Page Flow Control:\n");
-+
-+	val[0] = mt76_rr(dev, WF_PLE_TOP_FREEPG_CNT_ADDR);
-+	val[1] = mt76_rr(dev, WF_PLE_TOP_FREEPG_HEAD_TAIL_ADDR);
-+	seq_printf(s, "\tFree Page Counter: 0x%08x\n", val[0]);
-+	seq_printf(s, "\tFree Page Head and Tail: 0x%08x\n", val[1]);
-+	seq_printf(s, "\t\tNumber of free pages: 0x%04x\n",
-+		   u32_get_bits(val[0], WF_PLE_TOP_FREEPG_CNT_FREEPG_CNT_MASK));
-+	seq_printf(s, "\t\tNumber of unassigned pages: 0x%04x\n",
-+		   u32_get_bits(val[0], WF_PLE_TOP_FREEPG_CNT_FFA_CNT_MASK));
-+	seq_printf(s, "\t\tFID of tail/head free page: 0x%04x/0x%04x\n",
-+		   u32_get_bits(val[1], WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_MASK),
-+		   u32_get_bits(val[1], WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_MASK));
-+
-+	val[0] = mt76_rr(dev, WF_PLE_TOP_PG_HIF_GROUP_ADDR);
-+	val[1] = mt76_rr(dev, WF_PLE_TOP_HIF_PG_INFO_ADDR);
-+	seq_printf(s, "\tReserved Page Counter of HIF Group: 0x%08x\n", val[0]);
-+	seq_printf(s, "\tHIF Group Page Status: 0x%08x\n", val[1]);
-+	seq_printf(s, "\t\tMax/min page quota for HIF group: 0x%04x/0x%04x\n",
-+		   u32_get_bits(val[0], WF_PLE_TOP_PG_HIF_GROUP_HIF_MAX_QUOTA_MASK),
-+		   u32_get_bits(val[0], WF_PLE_TOP_PG_HIF_GROUP_HIF_MIN_QUOTA_MASK));
-+	seq_printf(s, "\t\tUsed/free page count for HIF group: 0x%04x/0x%04x\n",
-+		   u32_get_bits(val[1], WF_PLE_TOP_HIF_PG_INFO_HIF_SRC_CNT_MASK),
-+		   u32_get_bits(val[1], WF_PLE_TOP_HIF_PG_INFO_HIF_RSV_CNT_MASK));
-+
-+	val[0] = mt76_rr(dev, WF_PLE_TOP_PG_HIF_WMTXD_GROUP_ADDR);
-+	val[1] = mt76_rr(dev, WF_PLE_TOP_HIF_WMTXD_PG_INFO_ADDR);
-+	seq_printf(s, "\tReserved Page Counter of HIF WMCPU TXD Group: 0x%08x\n", val[0]);
-+	seq_printf(s, "\tHIF WMCPU TXD Group Page Status: 0x%08x\n", val[1]);
-+	seq_printf(s, "\t\tMax/min page quota for HIF WMCPU TXD group: 0x%04x/0x%04x\n",
-+		   u32_get_bits(val[0], WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MAX_QUOTA_MASK),
-+		   u32_get_bits(val[0], WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MIN_QUOTA_MASK));
-+	seq_printf(s, "\t\tUsed/free page count for HIF WMCPU TXD group: 0x%04x/0x%04x\n",
-+		   u32_get_bits(val[1], WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_SRC_CNT_MASK),
-+		   u32_get_bits(val[1], WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_RSV_CNT_MASK));
-+
-+	val[0] = mt76_rr(dev, WF_PLE_TOP_PG_HIF_TXCMD_GROUP_ADDR);
-+	val[1] = mt76_rr(dev, WF_PLE_TOP_HIF_TXCMD_PG_INFO_ADDR);
-+	seq_printf(s, "\tReserved Page Counter of HIF TXCMD Group: 0x%08x\n", val[0]);
-+	seq_printf(s, "\tHIF TXCMD Group Page Status: 0x%08x\n", val[1]);
-+	seq_printf(s, "\t\tMax/min page quota for HIF TXCMD group: 0x%04x/0x%04x\n",
-+		   u32_get_bits(val[0], WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MAX_QUOTA_MASK),
-+		   u32_get_bits(val[0], WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MIN_QUOTA_MASK));
-+	seq_printf(s, "\t\tUsed/free page count for HIF TXCMD group: 0x%04x/0x%04x\n",
-+		   u32_get_bits(val[1], WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_SRC_CNT_MASK),
-+		   u32_get_bits(val[1], WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_RSV_CNT_MASK));
-+
-+	val[0] = mt76_rr(dev, WF_PLE_TOP_PG_CPU_GROUP_ADDR);
-+	val[1] = mt76_rr(dev, WF_PLE_TOP_CPU_PG_INFO_ADDR);
-+	seq_printf(s, "\tReserved Page Counter of CPU Group: 0x%08x\n", val[0]);
-+	seq_printf(s, "\tCPU Group Page Status: 0x%08x\n", val[1]);
-+	seq_printf(s, "\t\tMax/min page quota for CPU group: 0x%04x/0x%04x\n",
-+		   u32_get_bits(val[0], WF_PLE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_MASK),
-+		   u32_get_bits(val[0], WF_PLE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_MASK));
-+	seq_printf(s, "\t\tUsed/free page count for CPU group: 0x%04x/0x%04x\n",
-+		   u32_get_bits(val[1], WF_PLE_TOP_CPU_PG_INFO_CPU_SRC_CNT_MASK),
-+		   u32_get_bits(val[1], WF_PLE_TOP_CPU_PG_INFO_CPU_RSV_CNT_MASK));
-+}
-+
-+static void
-+mt7996_get_ple_acq_stat(struct mt7996_dev *dev, unsigned long *ple_stat)
-+{
-+	u32 i, addr;
-+	size_t cr_num_of_ac = ple_cr_num_of_ac(&dev->mt76);
-+
-+	ple_stat[0] = mt76_rr(dev, WF_PLE_TOP_QUEUE_EMPTY_ADDR);
-+
-+	/* Legacy */
-+	addr = WF_PLE_TOP_AC0_QUEUE_EMPTY0_ADDR;
-+	for (i = 1; i <= cr_num_of_ac; i++, addr += 4) {
-+		if (i == cr_num_of_ac && is_mt7992(&dev->mt76))
-+			ple_stat[i] = mt76_rr(dev, WF_PLE_TOP_AC0_QUEUE_EMPTY_EXT0_ADDR);
-+		else
-+			ple_stat[i] = mt76_rr(dev, addr);
-+	}
-+
-+	addr = WF_PLE_TOP_AC1_QUEUE_EMPTY0_ADDR;
-+	for (; i <= cr_num_of_ac * 2; i++, addr += 4) {
-+		if (i == cr_num_of_ac * 2 && is_mt7992(&dev->mt76))
-+			ple_stat[i] = mt76_rr(dev, WF_PLE_TOP_AC1_QUEUE_EMPTY_EXT0_ADDR);
-+		else
-+			ple_stat[i] = mt76_rr(dev, addr);
-+	}
-+
-+	addr = WF_PLE_TOP_AC2_QUEUE_EMPTY0_ADDR;
-+	for (; i <= cr_num_of_ac * 3; i++, addr += 4) {
-+		if (i == cr_num_of_ac * 3 && is_mt7992(&dev->mt76))
-+			ple_stat[i] = mt76_rr(dev, WF_PLE_TOP_AC2_QUEUE_EMPTY_EXT0_ADDR);
-+		else
-+			ple_stat[i] = mt76_rr(dev, addr);
-+	}
-+
-+	addr = WF_PLE_TOP_AC3_QUEUE_EMPTY0_ADDR;
-+	for (; i <= cr_num_of_ac * 4; i++, addr += 4) {
-+		if (i == cr_num_of_ac * 4 && is_mt7992(&dev->mt76))
-+			ple_stat[i] = mt76_rr(dev, WF_PLE_TOP_AC3_QUEUE_EMPTY_EXT0_ADDR);
-+		else
-+			ple_stat[i] = mt76_rr(dev, addr);
-+	}
-+}
-+
-+static void
-+mt7996_get_sta_pause(struct mt7996_dev *dev, u8 band, u32 *sta_pause, u32 *twt_pause)
-+{
-+	u32 i, addr;
-+	size_t cr_num_of_ac = ple_cr_num_of_ac(&dev->mt76);
-+
-+	/* switch to target band */
-+	mt76_wr(dev, WF_DRR_TOP_SBRR_ADDR, u32_encode_bits(band, WF_DRR_TOP_SBRR_TARGET_BAND_MASK));
-+
-+	/* Legacy */
-+	addr = WF_DRR_TOP_AC0_STATION_PAUSE00_ADDR;
-+	for (i = 0; i < cr_num_of_ac; i++, addr += 4) {
-+		if (i == cr_num_of_ac - 1 && is_mt7992(&dev->mt76))
-+			sta_pause[i] = mt76_rr(dev, WF_DRR_TOP_AC0_STATION_PAUSE_EXT_00_ADDR);
-+		else
-+			sta_pause[i] = mt76_rr(dev, addr);
-+	}
-+
-+	addr = WF_DRR_TOP_AC1_STATION_PAUSE00_ADDR;
-+	for (; i < cr_num_of_ac * 2; i++, addr += 4) {
-+		if (i == cr_num_of_ac * 2 - 1 && is_mt7992(&dev->mt76))
-+			sta_pause[i] = mt76_rr(dev, WF_DRR_TOP_AC1_STATION_PAUSE_EXT_00_ADDR);
-+		else
-+			sta_pause[i] = mt76_rr(dev, addr);
-+	}
-+
-+	addr = WF_DRR_TOP_AC2_STATION_PAUSE00_ADDR;
-+	for (; i < cr_num_of_ac * 3; i++, addr += 4) {
-+		if (i == cr_num_of_ac * 3 - 1 && is_mt7992(&dev->mt76))
-+			sta_pause[i] = mt76_rr(dev, WF_DRR_TOP_AC2_STATION_PAUSE_EXT_00_ADDR);
-+		else
-+			sta_pause[i] = mt76_rr(dev, addr);
-+	}
-+
-+	addr = WF_DRR_TOP_AC3_STATION_PAUSE00_ADDR;
-+	for (; i < cr_num_of_ac * 4; i++, addr += 4) {
-+		if (i == cr_num_of_ac * 4 - 1 && is_mt7992(&dev->mt76))
-+			sta_pause[i] = mt76_rr(dev, WF_DRR_TOP_AC3_STATION_PAUSE_EXT_00_ADDR);
-+		else
-+			sta_pause[i] = mt76_rr(dev, addr);
-+	}
-+
-+	/* TWT */
-+	addr = WF_DRR_TOP_TWT_STA_MAP00_ADDR;
-+	for (i = 0; i < cr_num_of_ac; i++, addr += 4) {
-+		if (i == cr_num_of_ac - 1 && is_mt7992(&dev->mt76))
-+			twt_pause[i] = mt76_rr(dev, WF_DRR_TOP_TWT_STA_MAP_EXT_00_ADDR);
-+		else
-+			twt_pause[i] = mt76_rr(dev, addr);
-+	}
-+}
-+
-+static void
-+mt7996_get_ple_queue_info(struct mt7996_dev *dev, u32 pid, u32 qid, u32 tgid,
-+			  u16 wlan_idx, u16 *hfid, u16 *tfid, u16 *pktcnt)
-+{
-+	u32 val = WF_PLE_TOP_FL_QUE_CTRL_0_EXECUTE_MASK |
-+		  u32_encode_bits(pid, WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_MASK) |
-+		  u32_encode_bits(tgid, WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_TGID_MASK) |
-+		  u32_encode_bits(qid, WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_MASK) |
-+		  u32_encode_bits(wlan_idx, WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_WLANID_MASK);
-+	mt76_wr(dev, WF_PLE_TOP_FL_QUE_CTRL_0_ADDR, val);
-+
-+	val = mt76_rr(dev, WF_PLE_TOP_FL_QUE_CTRL_2_ADDR);
-+	*hfid = u32_get_bits(val, WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_MASK);
-+	*tfid = u32_get_bits(val, WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_MASK);
-+
-+	val = mt76_rr(dev, WF_PLE_TOP_FL_QUE_CTRL_3_ADDR);
-+	*pktcnt = u32_get_bits(val, WF_PLE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_MASK);
-+}
-+
-+static void
-+mt7996_show_sta_acq_info(struct seq_file *s, unsigned long *ple_stat,
-+			 u32 *sta_pause, u32 *twt_sta_pause)
-+{
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	size_t cr_num_of_ac = ple_cr_num_of_ac(&dev->mt76);
-+	size_t cr_num_of_all_ac = cr_num_of_ac * IEEE80211_NUM_ACS;
-+	int i, j;
-+
-+	for (j = 0; j < cr_num_of_all_ac; j++) { /* show AC Q info */
-+		for (i = 0; i < 32; i++) {
-+			if (!test_bit(i, &ple_stat[j + 1])) {
-+				u16 hfid, tfid, pktcnt, wlan_idx = i + (j % cr_num_of_ac) * 32;
-+				u8 wmmidx, ctrl = 0, acq_idx = j / cr_num_of_ac;
-+				struct mt7996_link_sta *mlink;
-+				struct mt76_wcid *wcid;
-+				size_t idx;
-+
-+				if (wlan_idx >= MT76_N_WCIDS) {
-+					seq_printf(s, "Error: WCID %hu exceeded threshold.\n", wlan_idx);
-+					continue;
-+				}
-+				wcid = rcu_dereference(dev->mt76.wcid[wlan_idx]);
-+				if (!wcid) {
-+					seq_printf(s, "Error: STA %hu does not exist.\n", wlan_idx);
-+					continue;
-+				}
-+				mlink = container_of(wcid, struct mt7996_link_sta, wcid);
-+				wmmidx = mlink->sta->vif->deflink.mt76.wmm_idx;
-+
-+				seq_printf(s, "\tSTA%hu AC%hhu: ", wlan_idx, acq_idx);
-+				mt7996_get_ple_queue_info(dev, ENUM_UMAC_LMAC_PORT_2, acq_idx,
-+							  0, wlan_idx, &hfid, &tfid, &pktcnt);
-+				seq_printf(s, "tail/head fid = 0x%04x/0x%04x, pkt cnt = 0x%04x",
-+					   tfid, hfid, pktcnt);
-+
-+				idx = wcid->phy_idx * cr_num_of_all_ac + j;
-+				if (sta_pause[idx] & BIT(i))
-+					ctrl = 2;
-+
-+				idx = wcid->phy_idx * cr_num_of_ac + j % cr_num_of_ac;
-+				if (twt_sta_pause[idx] & BIT(i))
-+					ctrl = 3;
-+
-+				seq_printf(s, ", ctrl = %s (wmmidx=%hhu, band=%hhu)\n",
-+					   sta_ctrl_reg[ctrl], wmmidx, wcid->phy_idx);
-+			}
-+		}
-+	}
-+}
-+
-+static void
-+mt7996_show_txcmdq_info(struct seq_file *s)
-+{
-+	const u32 txcmd_queue_empty_addr[__MT_MAX_BAND][2] = {
-+		[MT_BAND0] = {WF_PLE_TOP_TXCMD_QUEUE_EMPTY_ADDR,
-+			      WF_PLE_TOP_NATIVE_TXCMD_QUEUE_EMPTY_ADDR},
-+		[MT_BAND1] = {WF_PLE_TOP_BN1_TXCMD_QUEUE_EMPTY_ADDR,
-+			      WF_PLE_TOP_BN1_NATIVE_TXCMD_QUEUE_EMPTY_ADDR},
-+		[MT_BAND2] = {WF_PLE_TOP_BN2_TXCMD_QUEUE_EMPTY_ADDR,
-+			      WF_PLE_TOP_BN2_NATIVE_TXCMD_QUEUE_EMPTY_ADDR}
-+	};
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	u8 band;
-+
-+	for (band = MT_BAND0; band < __MT_MAX_BAND; ++band) {
-+		unsigned long txcmdq_stat, native_txcmdq_stat;
-+		int i;
-+
-+		if (!dev->mt76.phys[band])
-+			continue;
-+
-+		txcmdq_stat = mt76_rr(dev, txcmd_queue_empty_addr[band][0]);
-+		native_txcmdq_stat = mt76_rr(dev, txcmd_queue_empty_addr[band][1]);
-+
-+		seq_printf(s, "Band%hhu Non-native/native TXCMD Queue Empty: 0x%08lx/0x%08lx\n",
-+			   band, txcmdq_stat, native_txcmdq_stat);
-+
-+		for (i = 0; i < 32 ; i++) {
-+			if (!test_bit(i, &native_txcmdq_stat)) {
-+				struct bmac_queue_info_t *queue = &ple_txcmd_queue_empty_info[band][i];
-+				u16 hfid, tfid, pktcnt;
-+
-+				if (!queue->QueueName)
-+					continue;
-+
-+				seq_printf(s, "\t%s: ", queue->QueueName);
-+				mt7996_get_ple_queue_info(dev, queue->Portid, queue->Queueid,
-+							  0, 0, &hfid, &tfid, &pktcnt);
-+				seq_printf(s, "tail/head fid = 0x%04x/0x%04x, pkt cnt = 0x%04x\n",
-+					   tfid, hfid, pktcnt);
-+			}
-+		}
-+	}
-+}
-+
-+static int
-+mt7996_pleinfo_read(struct seq_file *s, void *data)
-+{
-+	struct mt7996_dev *dev = dev_get_drvdata(s->private);
-+	size_t cr_num_of_ac = ple_cr_num_of_ac(&dev->mt76);
-+	size_t cr_num_of_all_ac = cr_num_of_ac * IEEE80211_NUM_ACS;
-+	u32 *sta_pause, *twt_sta_pause;
-+	unsigned long *ple_stat;
-+	int i, j, ret = 0;
-+
-+	ple_stat = kzalloc((cr_num_of_all_ac + 1) * sizeof(unsigned long), GFP_KERNEL);
-+	if (!ple_stat)
-+		return -ENOMEM;
-+
-+	sta_pause = kzalloc(__MT_MAX_BAND * cr_num_of_all_ac * sizeof(u32), GFP_KERNEL);
-+	if (!sta_pause) {
-+		ret = -ENOMEM;
-+		goto out;
-+	}
-+
-+	twt_sta_pause = kzalloc(__MT_MAX_BAND * cr_num_of_ac * sizeof(u32), GFP_KERNEL);
-+	if (!twt_sta_pause) {
-+		ret = -ENOMEM;
-+		goto out;
-+	}
-+
-+	mt7996_show_ple_pg_info(dev, s);
-+	mt7996_get_ple_acq_stat(dev, ple_stat);
-+
-+	for (i = MT_BAND0; i < __MT_MAX_BAND; i++) {
-+		if (dev->mt76.phys[i])
-+			mt7996_get_sta_pause(dev, i,
-+					     sta_pause + i * cr_num_of_all_ac,
-+					     twt_sta_pause + i * cr_num_of_ac);
-+	}
-+
-+	if ((ple_stat[0] & WF_PLE_TOP_QUEUE_EMPTY_ALL_AC_EMPTY_MASK) == 0) {
-+		for (j = 0; j < cr_num_of_all_ac; j++) {
-+			if (j % cr_num_of_ac == 0)
-+				seq_printf(s, "\n\tSTA in nonempty AC%ld TXD queue: ", j / cr_num_of_ac);
-+
-+			for (i = 0; i < 32; i++) {
-+				if (!test_bit(i, &ple_stat[j + 1]))
-+					seq_printf(s, "%lu ", i + (j % cr_num_of_ac) * 32);
-+			}
-+		}
-+		seq_printf(s, "\n");
-+	}
-+
-+	seq_printf(s, "Nonempty TXD Queue Info:\n");
-+
-+	for (i = 0; i < 32; i++) {
-+		if (!test_bit(i, &ple_stat[0])) {
-+			struct bmac_queue_info *queue = &ple_queue_empty_info[i];
-+			u16 hfid, tfid, pktcnt;
-+
-+			if (!queue->QueueName)
-+				continue;
-+
-+			seq_printf(s, "\t%s: ", queue->QueueName);
-+			mt7996_get_ple_queue_info(dev, queue->Portid, queue->Queueid,
-+						  queue->tgid, 0, &hfid, &tfid, &pktcnt);
-+			seq_printf(s, "tail/head fid = 0x%04x/0x%04x, pkt cnt = 0x%04x\n",
-+				   tfid, hfid, pktcnt);
-+		}
-+	}
-+
-+	mt7996_show_sta_acq_info(s, ple_stat, sta_pause, twt_sta_pause);
-+	mt7996_show_txcmdq_info(s);
-+
-+	kfree(twt_sta_pause);
-+out:
-+	kfree(sta_pause);
-+	kfree(ple_stat);
-+	return ret;
-+}
-+
-+/* DRR */
-+static int
-+mt7996_drr_info(struct seq_file *s, void *data)
-+{
-+	/* TODO: Wait MIB counter API implement complete */
-+	return 0;
-+}
-+
- int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- {
- 	struct mt7996_dev *dev = phy->dev;
-@@ -3337,6 +4403,25 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
- 
- 	debugfs_create_file("thermal_enable", 0600, dir, phy, &fops_thermal_enable);
- 	debugfs_create_file("thermal_recal", 0200, dir, dev, &fops_thermal_recal);
-+	debugfs_create_file("reset_counter", 0200, dir, dev, &fops_reset_counter);
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "per", dir, mt7996_per_read);
-+
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "drr_info", dir,
-+				    mt7996_drr_info);
-+
-+	debugfs_create_u32("token_idx", 0600, dir, &dev->dbg.token_idx);
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "rx_token", dir,
-+				    mt7996_rx_token_read);
-+
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "ple_info", dir,
-+				    mt7996_pleinfo_read);
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "pse_info", dir,
-+				    mt7996_pseinfo_read);
-+	/* amsdu */
-+	debugfs_create_file("amsdu_algo", 0600, dir, dev, &fops_amsdu_algo);
-+	debugfs_create_file("amsdu_para", 0600, dir, dev, &fops_amsdu_para);
-+	debugfs_create_devm_seqfile(dev->mt76.dev, "amsdu_info", dir,
-+	                            mt7996_amsdu_info_read);
- 
- 	return 0;
- }
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0116-mtk-mt76-mt7996-enable-ampdu-limit-to-avoid-BA-bound.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0116-mtk-mt76-mt7996-enable-ampdu-limit-to-avoid-BA-bound.patch
new file mode 100644
index 0000000..ea0d36e
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0116-mtk-mt76-mt7996-enable-ampdu-limit-to-avoid-BA-bound.patch
@@ -0,0 +1,186 @@
+From af0b14e475466b4f353c20af228c381c695ec3f9 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 1 Apr 2024 17:00:21 +0800
+Subject: [PATCH 116/199] mtk: mt76: mt7996: enable ampdu limit to avoid BA
+ bound issue
+
+[Description]
+When the station is MTK device and the peak is higher than 15G, the PPS
+would exceed HW-RRO's bandwidth and lead to Rx fifo full and PER. When
+a link occurs PER, it may occupy SSN and the other two bands are not
+able to transmit.
+
+Limit AMPDU to 512 when satisify all of the following conditions
+1. BA winsize is 1024.
+2. At least one link use BW320 and its spatial stream is larger
+than 3.
+3. At least one link use BW160 and its spatial stream is larger
+than 3.
+
+By limiting AMPDU to 512, it can solve this issue.
+1. Reduce PPS so we can avoid Rx fifo full due to HW-RRO.
+2. If a bind occupy SSN, the other two bands can use the SSN
+between 512 to 1024.
+
+[Release-log]
+N/A
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt76_connac_mcu.h |  1 +
+ mt7996/mcu.c      | 86 +++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h      |  8 +++++
+ 3 files changed, 95 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index f6a0d328..6e3324f5 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -829,6 +829,7 @@ enum {
+ 	STA_REC_KEY_V3 = 0x27,
+ 	STA_REC_HDRT = 0x28,
+ 	STA_REC_HDR_TRANS = 0x2B,
++	STA_REC_TX_CAP = 0x2f,
+ 	STA_REC_MAX_NUM
+ };
+ 
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 340b6637..f97acfb2 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1344,6 +1344,85 @@ mt7996_mcu_sta_ba(struct mt7996_dev *dev, struct mt76_vif *mvif,
+ 				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+ }
+ 
++static int
++mt7996_mcu_sta_tx_cap(struct mt7996_dev *dev, struct mt76_vif *mvif,
++		      struct mt76_wcid *wcid)
++{
++	struct sta_rec_tx_cap *tx_cap;
++	struct sk_buff *skb;
++	struct tlv *tlv;
++
++	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, mvif, wcid,
++					      MT7996_STA_UPDATE_MAX_SIZE);
++	if (IS_ERR(skb))
++		return PTR_ERR(skb);
++
++	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_TX_CAP, sizeof(*tx_cap));
++
++	tx_cap = (struct sta_rec_tx_cap *)tlv;
++	tx_cap->ampdu_limit_en = true;
++
++	dev_info(dev->mt76.dev, "%s: limit wcid %d ampdu to 512\n", __func__, wcid->idx);
++
++	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
++				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
++}
++
++static bool mt7996_check_limit_ampdu_en(struct ieee80211_ampdu_params *params) {
++	struct ieee80211_sta *sta = params->sta;
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	unsigned long valid_links = sta->valid_links ?: BIT(0);
++	unsigned int link_id;
++	bool BW320 = false, BW160 = false;
++
++	if (params->buf_size < 1024)
++		return false;
++
++	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++		struct ieee80211_link_sta __rcu *link =
++			link_sta_dereference_protected(sta, link_id);
++		struct mt7996_bss_conf *mconf =
++			mconf_dereference_protected(msta->vif, link_id);
++		struct mt76_phy *phy = mconf->phy->mt76;
++		struct ieee80211_eht_mcs_nss_supp_bw *ss = NULL;
++		u8 sta_bw, ap_nss, sta_nss;
++
++		switch (phy->chandef.width) {
++		case NL80211_CHAN_WIDTH_160:
++			if (link->bandwidth >= IEEE80211_STA_RX_BW_160) {
++				ss = &link->eht_cap.eht_mcs_nss_supp.bw._160;
++				sta_bw = NL80211_CHAN_WIDTH_160;
++			}
++			break;
++		case NL80211_CHAN_WIDTH_320:
++			if (link->bandwidth == IEEE80211_STA_RX_BW_320) {
++				ss = &link->eht_cap.eht_mcs_nss_supp.bw._320;
++				sta_bw = NL80211_CHAN_WIDTH_320;
++			}
++			break;
++		default:
++			break;
++		}
++
++		if (!ss)
++			continue;
++
++		ap_nss = hweight8(phy->antenna_mask);
++		sta_nss = max(u8_get_bits(ss->rx_tx_mcs11_max_nss, IEEE80211_EHT_MCS_NSS_RX),
++			      u8_get_bits(ss->rx_tx_mcs13_max_nss, IEEE80211_EHT_MCS_NSS_RX));
++
++		if (min(ap_nss, sta_nss) <= 2)
++			continue;
++
++		if (sta_bw == NL80211_CHAN_WIDTH_160)
++			BW160 = true;
++		else if (sta_bw == NL80211_CHAN_WIDTH_320)
++			BW320 = true;
++	}
++
++	return BW320 && BW160;
++}
++
+ /** starec & wtbl **/
+ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ 			 struct ieee80211_ampdu_params *params,
+@@ -1353,6 +1432,7 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	unsigned long valid_links = sta->valid_links ?: BIT(0);
+ 	unsigned int link_id;
++	bool limit_ampdu_en = mt7996_check_limit_ampdu_en(params);
+ 
+ 	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ 		struct mt7996_link_sta *mlink =
+@@ -1368,6 +1448,12 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ 					&mlink->wcid, enable, true);
+ 		if (ret)
+ 			return ret;
++
++		if (limit_ampdu_en) {
++			ret = mt7996_mcu_sta_tx_cap(dev, &mconf->mt76, &mlink->wcid);
++			if (ret)
++				return ret;
++		}
+ 	}
+ 
+ 	return 0;
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 9903d88e..f9f04680 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -573,6 +573,13 @@ struct sta_rec_ba_uni {
+ 	u8 __rsv[3];
+ } __packed;
+ 
++struct sta_rec_tx_cap {
++	__le16 tag;
++	__le16 len;
++	u8 ampdu_limit_en;
++	u8 rsv[3];
++} __packed;
++
+ struct sta_rec_eht {
+ 	__le16 tag;
+ 	__le16 len;
+@@ -939,6 +946,7 @@ enum {
+ 					 sizeof(struct sta_rec_eht) +		\
+ 					 sizeof(struct sta_rec_hdrt) +		\
+ 					 sizeof(struct sta_rec_hdr_trans) +	\
++					 sizeof(struct sta_rec_tx_cap) +	\
+ 					 sizeof(struct tlv))
+ 
+ #define MT7996_MAX_BEACON_SIZE		1338
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0116-mtk-wifi-mt76-mt7996-remain-multiple-wiphy-model-for.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0116-mtk-wifi-mt76-mt7996-remain-multiple-wiphy-model-for.patch
deleted file mode 100644
index 351cb74..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0116-mtk-wifi-mt76-mt7996-remain-multiple-wiphy-model-for.patch
+++ /dev/null
@@ -1,111 +0,0 @@
-From 81a4701e1fb249ce062c5d17e6c670265ef0f05e Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 22 Apr 2024 16:49:48 +0800
-Subject: [PATCH 116/116] mtk: wifi: mt76: mt7996: remain multiple wiphy model
- for testmode
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/init.c   | 13 ++++++++-----
- mt7996/main.c   | 14 ++++++++++----
- mt7996/mt7996.h |  9 ++++-----
- 3 files changed, 22 insertions(+), 14 deletions(-)
-
-diff --git a/mt7996/init.c b/mt7996/init.c
-index ff72aab..58944c6 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -794,9 +794,11 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
- 		mtk_wed_device_start(&dev->mt76.mmio.wed_hif2, MT_INT_TX_RX_DONE_EXT);
- 	}
- 
--	/* TODO: FIXME: force to use single wiphy, need to rework init flow */
--	phy->mt76->ori_hw = mphy->hw;
--	mphy->hw = dev->phy.mt76->hw;
-+	/* TODO: FIXME: force to use single wiphy for normal mode, need to rework init flow */
-+	if (!dev->testmode_enable) {
-+		phy->mt76->ori_hw = mphy->hw;
-+		mphy->hw = dev->phy.mt76->hw;
-+	}
- 
- 	return 0;
- 
-@@ -832,8 +834,9 @@ mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
- 	if (!phy)
- 		return;
- 
--	/* TODO: FIXME: temp for single wiphy support */
--	phy->mt76->hw = phy->mt76->ori_hw;
-+	/* TODO: FIXME: temp for normal mode single wiphy support */
-+	if (!phy->dev->testmode_enable)
-+		phy->mt76->hw = phy->mt76->ori_hw;
- 
- #ifdef CONFIG_MTK_VENDOR
- 	mt7996_unregister_csi(phy);
-diff --git a/mt7996/main.c b/mt7996/main.c
-index 2cc0c32..9738e76 100644
---- a/mt7996/main.c
-+++ b/mt7996/main.c
-@@ -140,8 +140,8 @@ static int mt7996_start(struct ieee80211_hw *hw)
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	int ret;
- 
--	/* only allow settings from hw0 */
--	if (hw != dev->phy.mt76->hw)
-+	/* only allow settings from hw0 for normal mode */
-+	if (!dev->testmode_enable && hw != dev->phy.mt76->hw)
- 		return -1;
- 
- 	flush_work(&dev->init_work);
-@@ -158,8 +158,8 @@ static void mt7996_stop(struct ieee80211_hw *hw)
- 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
- 	int band;
- 
--	/* only allow settings from hw0 */
--	if (hw != dev->phy.mt76->hw)
-+	/* only allow settings from hw0 for normal mode */
-+	if (!dev->testmode_enable && hw != dev->phy.mt76->hw)
- 		return;
- 
- 	cancel_delayed_work_sync(&dev->scs_work);
-@@ -453,6 +453,12 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
- 	    is_zero_ether_addr(vif->addr))
- 		phy->monitor_vif = vif;
- 
-+	if (dev->testmode_enable && vif->type != NL80211_IFTYPE_MONITOR) {
-+		mutex_unlock(&dev->mt76.mutex);
-+		dev_err(dev->mt76.dev, "Only monitor interface is allowed in testmode\n");
-+		return -EINVAL;
-+	}
-+
- 	INIT_DELAYED_WORK(&mvif->beacon_mon_work, mt7996_beacon_mon_work);
- 	mvif->dev = dev;
- 	mvif->hw = hw;
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index de7cf1e..45f8af1 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -860,16 +860,15 @@ mt7996_get_background_radar_cap(struct mt7996_dev *dev)
- static inline struct mt7996_phy *
- mt7996_band_phy(struct ieee80211_hw *hw, enum nl80211_band band)
- {
--	struct mt76_dev *dev = hw->priv;
--	struct mt76_phy *phy;
-+	struct mt76_phy *phy = hw->priv;
- 
- 	/* TODO: mlo: temporarily hardcode */
- 	if (band == NL80211_BAND_6GHZ)
--		phy = dev->phys[MT_BAND2];
-+		phy = phy->dev->phys[MT_BAND2];
- 	else if (band == NL80211_BAND_5GHZ)
--		phy = dev->phys[MT_BAND1];
-+		phy = phy->dev->phys[MT_BAND1];
- 	else
--		phy = dev->phys[MT_BAND0];
-+		phy = phy->dev->phys[MT_BAND0];
- 
- 	if (!phy)
- 		return NULL;
--- 
-2.18.0
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0117-mtk-mt76-mt7996-Fix-get_txpower-wrong-result-in-sing.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0117-mtk-mt76-mt7996-Fix-get_txpower-wrong-result-in-sing.patch
new file mode 100644
index 0000000..082b6a7
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0117-mtk-mt76-mt7996-Fix-get_txpower-wrong-result-in-sing.patch
@@ -0,0 +1,64 @@
+From cae948de0db9841b932562c2283331155ff182f0 Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Mon, 8 Apr 2024 16:56:09 +0800
+Subject: [PATCH 117/199] mtk: mt76: mt7996: Fix get_txpower wrong result in
+ single wiphy and legacy mode
+
+Fix get_txpower wrong result in single wiphy and legacy mode.
+ieee80211_hw is get from wiphy0, so we need to get correct phy from vif.
+
+Temporarily use link 0 bss due to mac80211 didn't pass link id here.
+
+---
+ mt7996/main.c | 28 +++++++++++++++++++++++++++-
+ 1 file changed, 27 insertions(+), 1 deletion(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 155318b3..7e56f4b0 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -982,6 +982,32 @@ out:
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
++int mt7996_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++		       int *dbm)
++{
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_bss_conf *mconf;
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mt76_phy *mphy;
++	int delta;
++
++	mutex_lock(&dev->mt76.mutex);
++	mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
++	if (!mconf || !mconf->phy) {
++		*dbm = 0;
++		goto out;
++	}
++
++	mphy = mconf->phy->mt76;
++
++	delta = mt76_tx_power_nss_delta(hweight16(mphy->chainmask));
++
++	*dbm = DIV_ROUND_UP(mphy->txpower_cur + delta, 2);
++out:
++	mutex_unlock(&dev->mt76.mutex);
++	return 0;
++}
++
+ static void
+ mt7996_channel_switch_beacon(struct ieee80211_hw *hw,
+ 			     struct ieee80211_vif *vif,
+@@ -2669,7 +2695,7 @@ const struct ieee80211_ops mt7996_ops = {
+ 	.hw_scan = mt7996_hw_scan,
+ 	.cancel_hw_scan = mt7996_cancel_hw_scan,
+ 	.release_buffered_frames = mt76_release_buffered_frames,
+-	.get_txpower = mt76_get_txpower,
++	.get_txpower = mt7996_get_txpower,
+ 	.channel_switch_beacon = mt7996_channel_switch_beacon,
+ 	.get_stats = mt7996_get_stats,
+ 	.get_et_sset_count = mt7996_get_et_sset_count,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0118-mtk-mt76-mt7996-Add-connac3-csi-feature.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0118-mtk-mt76-mt7996-Add-connac3-csi-feature.patch
new file mode 100644
index 0000000..27c74b7
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0118-mtk-mt76-mt7996-Add-connac3-csi-feature.patch
@@ -0,0 +1,1132 @@
+From 3b007db9f040c663de6308f0cb1b9b603f1811c5 Mon Sep 17 00:00:00 2001
+From: mtk20656 <chank.chen@mediatek.com>
+Date: Sat, 20 Jan 2024 12:03:24 +0800
+Subject: [PATCH 118/199] mtk: mt76: mt7996: Add connac3 csi feature.
+
+1. format align to wifi6.
+2. add bw320 support.
+3. add active mode.
+
+Fix csi bug with single wiphy design.
+
+Signed-off-by: mtk20656 <chank.chen@mediatek.com>
+---
+ mt76_connac_mcu.h |   2 +
+ mt7996/init.c     |  22 +++
+ mt7996/main.c     |   3 +
+ mt7996/mcu.c      | 465 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h      | 105 +++++++++++
+ mt7996/mt7996.h   |  55 ++++++
+ mt7996/vendor.c   | 247 ++++++++++++++++++++++++
+ mt7996/vendor.h   |  52 ++++++
+ 8 files changed, 951 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 6e3324f5..1852e512 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1061,6 +1061,7 @@ enum {
+ 	MCU_UNI_EVENT_THERMAL = 0x35,
+ 	MCU_UNI_EVENT_NIC_CAPAB = 0x43,
+ 	MCU_UNI_EVENT_TESTMODE_CTRL = 0x46,
++	MCU_UNI_EVENT_CSI_REPORT = 0x4A,
+ 	MCU_UNI_EVENT_WED_RRO = 0x57,
+ 	MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
+ 	MCU_UNI_EVENT_ALL_STA_INFO = 0x6e,
+@@ -1298,6 +1299,7 @@ enum {
+ 	MCU_UNI_CMD_TESTMODE_TRX_PARAM = 0x42,
+ 	MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
+ 	MCU_UNI_CMD_PRECAL_RESULT = 0x47,
++	MCU_UNI_CMD_CSI_CTRL = 0x4A,
+ 	MCU_UNI_CMD_THERMAL_CAL = 0x4c,
+ 	MCU_UNI_CMD_RRO = 0x57,
+ 	MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index f75aa568..a1941869 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -807,6 +807,24 @@ error:
+ 	return ret;
+ }
+ 
++#ifdef CONFIG_MTK_VENDOR
++static int mt7996_unregister_csi(struct mt7996_phy *phy)
++{
++	struct csi_data *c, *tmp_c;
++
++	spin_lock_bh(&phy->csi.lock);
++	phy->csi.enable = 0;
++
++	list_for_each_entry_safe(c, tmp_c, &phy->csi.list, node) {
++		list_del(&c->node);
++		kfree(c);
++	}
++	spin_unlock_bh(&phy->csi.lock);
++
++	return 0;
++}
++#endif
++
+ static void
+ mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
+ {
+@@ -818,6 +836,10 @@ mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
+ 	/* TODO: FIXME: temp for single wiphy support */
+ 	phy->mt76->hw = phy->mt76->ori_hw;
+ 
++#ifdef CONFIG_MTK_VENDOR
++	mt7996_unregister_csi(phy);
++#endif
++
+ 	mt7996_unregister_thermal(phy);
+ 
+ 	mphy = phy->dev->mt76.phys[band];
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 7e56f4b0..b94af2f6 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1302,6 +1302,9 @@ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+ 	unsigned long rem = sta->valid_links ?: BIT(0);
+ 
++#ifdef CONFIG_MTK_VENDOR
++	mt7996_mcu_set_csi(&dev->phy, 2, 8, 1, 0, sta->addr);
++#endif
+ 	mt7996_mac_sta_remove_links(dev, vif, sta, rem);
+ }
+ 
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index f97acfb2..c59dbbea 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -653,6 +653,263 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	}
+ }
+ 
++static int
++csi_integrate_segment_data(struct mt7996_phy *phy, struct csi_data *csi)
++{
++	struct csi_data *csi_temp = NULL;
++
++	if (csi->segment_num == 0 && csi->remain_last == 0)
++		return CSI_CHAIN_COMPLETE;
++	else if (csi->segment_num == 0 && csi->remain_last == 1) {
++		memcpy(&phy->csi.buffered_csi,
++		       csi, sizeof(struct csi_data));
++
++		return CSI_CHAIN_SEGMENT_FIRST;
++	} else if (csi->segment_num != 0) {
++		csi_temp = &phy->csi.buffered_csi;
++		if (csi->chain_info != csi_temp->chain_info ||
++		csi->segment_num != (csi_temp->segment_num + 1))
++			return CSI_CHAIN_SEGMENT_ERR;
++
++		memcpy(&csi_temp->data_i[csi_temp->data_num],
++		       csi->data_i, csi->data_num * sizeof(s16));
++
++		memcpy(&csi_temp->data_q[csi_temp->data_num],
++		       csi->data_q, csi->data_num * sizeof(s16));
++
++		csi_temp->data_num += csi->data_num;
++		csi_temp->segment_num = csi->segment_num;
++		csi_temp->remain_last = csi->remain_last;
++
++		if (csi->remain_last == 0)
++			return CSI_CHAIN_SEGMENT_LAST;
++		else if (csi->remain_last == 1)
++			return CSI_CHAIN_SEGMENT_MIDDLE;
++	}
++
++	return CSI_CHAIN_ERR;
++}
++
++static int
++mt7996_mcu_csi_report_data(struct mt7996_phy *phy, u8 *tlv_buf, u32 len)
++{
++	int ret, i;
++	struct csi_data *current_csi;
++	struct csi_data *target_csi;
++	struct csi_tlv *tlv_data;
++	u8 *buf_tmp;
++	u32 rx_info, tx_rx_idx;
++	u32 buf_len_last, offset;
++
++	buf_tmp = tlv_buf;
++	buf_len_last = len;
++	offset = sizeof(((struct csi_tlv *)0)->basic);
++
++	current_csi = kzalloc(sizeof(*current_csi), GFP_KERNEL);
++	if (!current_csi)
++		return -ENOMEM;
++
++	while (buf_len_last >= offset) {
++		u32 tag, len;
++		s16 *data_tmp = NULL;
++
++		tlv_data = (struct csi_tlv *)buf_tmp;
++		tag = le32_to_cpu(tlv_data->basic.tag);
++		len = le32_to_cpu(tlv_data->basic.len);
++
++		switch (tag) {
++		case CSI_EVENT_FW_VER:
++			current_csi->fw_ver = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_CBW:
++			current_csi->ch_bw = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_RSSI:
++			current_csi->rssi = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_SNR:
++			current_csi->snr = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_BAND:
++			current_csi->band = le32_to_cpu(tlv_data->info);
++
++			if (current_csi->band != phy->mt76->band_idx) {
++				kfree(current_csi);
++				return -EINVAL;
++			}
++
++			break;
++		case CSI_EVENT_CSI_NUM:
++			current_csi->data_num = le32_to_cpu(tlv_data->info);
++
++			if (current_csi->data_num > CSI_BW80_DATA_COUNT) {
++				kfree(current_csi);
++				return -EINVAL;
++			}
++
++			break;
++		case CSI_EVENT_CSI_I_DATA:
++			if (len != sizeof(s16) * current_csi->data_num) {
++				kfree(current_csi);
++				return -EINVAL;
++			}
++
++			data_tmp = tlv_data->data;
++			for (i = 0; i < current_csi->data_num; i++)
++				current_csi->data_i[i] = le16_to_cpu(*(data_tmp + i));
++			break;
++		case CSI_EVENT_CSI_Q_DATA:
++			if (len != sizeof(s16) * current_csi->data_num) {
++				kfree(current_csi);
++				return -EINVAL;
++			}
++
++			data_tmp = tlv_data->data;
++			for (i = 0; i < current_csi->data_num; i++)
++				current_csi->data_q[i] = le16_to_cpu(*(data_tmp + i));
++			break;
++		case CSI_EVENT_DBW:
++			current_csi->data_bw = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_CH_IDX:
++			current_csi->pri_ch_idx = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_TA:
++			memcpy(current_csi->ta, tlv_data->mac, ETH_ALEN);
++			break;
++		case CSI_EVENT_EXTRA_INFO:
++			current_csi->ext_info = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_RX_MODE:
++			rx_info = le32_to_cpu(tlv_data->info);
++			current_csi->rx_mode = u32_get_bits(rx_info, GENMASK(15, 0));
++			current_csi->rx_rate = u32_get_bits(rx_info, GENMASK(31, 16));
++			break;
++		case CSI_EVENT_H_IDX:
++			current_csi->chain_info = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_TX_RX_IDX:
++			tx_rx_idx = le32_to_cpu(tlv_data->info);
++			current_csi->tx_idx = u32_get_bits(tx_rx_idx, GENMASK(31, 16));
++			current_csi->rx_idx = u32_get_bits(tx_rx_idx, GENMASK(15, 0));
++			break;
++		case CSI_EVENT_TS:
++			current_csi->ts = le32_to_cpu(tlv_data->info);
++
++			if (phy->csi.interval &&
++				current_csi->ts < phy->csi.last_record + phy->csi.interval) {
++				kfree(current_csi);
++				return 0;
++			}
++
++			break;
++		case CSI_EVENT_PKT_SN:
++			current_csi->pkt_sn = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_BW_SEG:
++			current_csi->segment_num = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_REMAIN_LAST:
++			current_csi->remain_last = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_TR_STREAM:
++			current_csi->tr_stream = le32_to_cpu(tlv_data->info);
++			break;
++		default:
++			break;
++		};
++
++		buf_len_last -= (offset + len);
++
++		if (buf_len_last >= offset)
++			buf_tmp += (offset + len);
++	}
++
++	/* integret the bw80 segment */
++	if (current_csi->ch_bw >= CSI_BW80) {
++		ret = csi_integrate_segment_data(phy, current_csi);
++
++		switch (ret) {
++		case CSI_CHAIN_ERR:
++		case CSI_CHAIN_SEGMENT_ERR:
++			kfree(current_csi);
++			return -EINVAL;
++			break;
++		case CSI_CHAIN_SEGMENT_FIRST:
++		case CSI_CHAIN_SEGMENT_MIDDLE:
++			kfree(current_csi);
++			return 0;
++			break;
++		case CSI_CHAIN_COMPLETE:
++			target_csi = current_csi;
++			break;
++		case CSI_CHAIN_SEGMENT_LAST:
++			target_csi = current_csi;
++			memcpy(target_csi, &phy->csi.buffered_csi, sizeof(struct csi_data));
++			memset(&phy->csi.buffered_csi, 0, sizeof(struct csi_data));
++			break;
++		default:
++			break;
++		}
++	} else {
++		target_csi = current_csi;
++	}
++
++	/* put the csi data into list */
++	INIT_LIST_HEAD(&target_csi->node);
++	spin_lock_bh(&phy->csi.lock);
++
++	if (!phy->csi.enable) {
++		kfree(target_csi);
++		goto out;
++	}
++
++	list_add_tail(&target_csi->node, &phy->csi.list);
++	phy->csi.count++;
++
++	if (phy->csi.count > CSI_MAX_BUF_NUM) {
++		struct csi_data *old;
++
++		old = list_first_entry(&phy->csi.list,
++				       struct csi_data, node);
++
++		list_del(&old->node);
++		kfree(old);
++		phy->csi.count--;
++	}
++
++	if (target_csi->chain_info & BIT(15)) /* last chain */
++		phy->csi.last_record = target_csi->ts;
++
++out:
++	spin_unlock_bh(&phy->csi.lock);
++	return 0;
++}
++
++void
++mt7996_mcu_csi_report_event(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++	struct mt7996_mcu_csi_event *event;
++	struct mt76_phy *mphy;
++	struct mt7996_phy *phy;
++
++	event = (struct mt7996_mcu_csi_event *)skb->data;
++
++	mphy = dev->mt76.phys[event->band_idx];
++	if (!mphy)
++		return;
++
++	phy = mphy->priv;
++
++	switch (le16_to_cpu(event->tag)) {
++	case UNI_EVENT_CSI_DATA:
++		mt7996_mcu_csi_report_data(phy, event->tlv_buf, le16_to_cpu(event->len) - 4);
++		break;
++	default:
++		break;
++	}
++}
++
+ static void
+ mt7996_mcu_rx_thermal_notify(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+@@ -896,6 +1153,11 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	case MCU_UNI_EVENT_BF:
+ 		mt7996_mcu_rx_bf_event(dev, skb);
+ 		break;
++#endif
++#ifdef CONFIG_MTK_VENDOR
++	case MCU_UNI_EVENT_CSI_REPORT:
++		mt7996_mcu_csi_report_event(dev, skb);
++		break;
+ #endif
+ 	default:
+ 		break;
+@@ -5980,4 +6242,207 @@ void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ 
+ 	mt7996_mcu_add_beacon(hw, &vif->bss_conf, &mvif->deflink, val);
+ }
++
++static int mt7996_mcu_set_csi_enable(struct mt7996_phy *phy, u16 tag)
++{
++	struct {
++		u8 band;
++		u8 rsv1[3];
++
++		__le16 tag;
++		__le16 len;
++	} __packed req = {
++		.band = phy->mt76->band_idx,
++		.tag = cpu_to_le16(tag),
++		.len = cpu_to_le16(sizeof(req) - 4),
++	};
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
++				sizeof(req), false);
++}
++
++static int mt7996_mcu_set_csi_frame_type(struct mt7996_phy *phy, u16 tag, u8 type_idx, u32 type)
++{
++	struct {
++		u8 band;
++		u8 rsv1[3];
++
++		__le16 tag;
++		__le16 len;
++		u8 frame_type_idx;
++		u8 frame_type;
++		u8 rsv2[2];
++	} __packed req = {
++		.band = phy->mt76->band_idx,
++		.tag = cpu_to_le16(tag),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.frame_type_idx = type_idx,
++		.frame_type = type,
++	};
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
++				sizeof(req), false);
++}
++
++static int mt7996_mcu_set_csi_chain_filter(struct mt7996_phy *phy, u16 tag, u8 func, u32 value)
++{
++	struct {
++		u8 band;
++		u8 rsv1[3];
++
++		__le16 tag;
++		__le16 len;
++		u8 function;
++		u8 chain_value;
++		u8 rsv2[2];
++	} __packed req = {
++		.band = phy->mt76->band_idx,
++		.tag = cpu_to_le16(tag),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.function = func,
++		.chain_value = value,
++	};
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
++				sizeof(req), false);
++}
++
++static int mt7996_mcu_set_csi_sta_filter(struct mt7996_phy *phy, u16 tag, u32 op, u8 *sta_mac)
++{
++	struct {
++		u8 band;
++		u8 rsv1[3];
++
++		__le16 tag;
++		__le16 len;
++		u8 operation;
++		u8 rsv2[1];
++		u8 mac[6];
++	} __packed req = {
++		.band = phy->mt76->band_idx,
++		.tag = cpu_to_le16(tag),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.operation = op,
++	};
++
++	memcpy(req.mac, sta_mac, ETH_ALEN);
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
++				sizeof(req), false);
++}
++
++static int mt7996_mcu_set_csi_active_mode(struct mt7996_phy *phy, u16 tag,
++					  u32 interval, u8 frame_idx, u8 subframe_idx, u32 bitmap)
++{
++	struct {
++		u8 band;
++		u8 rsv1[3];
++
++		__le16 tag;
++		__le16 len;
++		__le16 interval; /* uint: ms */
++		u8 frame_type_idx;
++		u8 subframe_type_idx;
++		__le32 bitmap; /* sta wcid bitmap */
++		u8 rsv2[4];
++	} __packed req = {
++		.band = phy->mt76->band_idx,
++		.tag = cpu_to_le16(tag),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.interval = cpu_to_le16(interval),
++		.frame_type_idx = frame_idx,
++		.subframe_type_idx = subframe_idx,
++		.bitmap = cpu_to_le32(bitmap),
++	};
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
++				sizeof(req), false);
++}
++
++void mt7996_csi_wcid_bitmap_update(void *data, struct ieee80211_sta *sta)
++{
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_phy *phy = msta->vif->deflink.phy;
++	struct csi_bitmap_info_update *sta_info = (struct csi_bitmap_info_update *)data;
++	u16 wcid = 0;
++
++#define CSI_ACTIVE_MODE_ADD 1
++#define CSI_ACTIVE_MODE_REMOVE 0
++
++	if (!memcmp(sta_info->addr, sta->addr, ETH_ALEN)) {
++		wcid = msta->deflink.wcid.idx;
++
++		/* active mode: only support station with wcid less than 32 */
++		if (wcid > 32)
++			return;
++
++		if (sta_info->action == CSI_ACTIVE_MODE_ADD)
++			phy->csi.active_bitmap |= BIT(wcid);
++		else if (sta_info->action == CSI_ACTIVE_MODE_REMOVE)
++			phy->csi.active_bitmap &= ~(BIT(wcid));
++	}
++}
++
++int mt7996_mcu_set_csi(struct mt7996_phy *phy, u8 mode,
++			u8 cfg, u8 v1, u32 v2, u8 *mac_addr)
++{
++	switch (mode) {
++	case CSI_CONTROL_MODE_STOP:
++		return mt7996_mcu_set_csi_enable(phy, UNI_CMD_CSI_STOP);
++	case CSI_CONTROL_MODE_START:
++		return mt7996_mcu_set_csi_enable(phy, UNI_CMD_CSI_START);
++	case CSI_CONTROL_MODE_SET:
++		switch (cfg) {
++		case CSI_CONFIG_FRAME_TYPE:
++			if (v2 > 255)
++				return -EINVAL;
++
++			return mt7996_mcu_set_csi_frame_type(phy,
++					UNI_CMD_CSI_SET_FRAME_TYPE, v1, v2);
++		case CSI_CONFIG_CHAIN_FILTER:
++			if (v2 > 255)
++				return -EINVAL;
++
++			return mt7996_mcu_set_csi_chain_filter(phy,
++					UNI_CMD_CSI_SET_CHAIN_FILTER, v1, v2);
++		case CSI_CONFIG_STA_FILTER:
++			if (!is_valid_ether_addr(mac_addr))
++				return -EINVAL;
++
++			if (v2 > 255)
++				return -EINVAL;
++
++			return mt7996_mcu_set_csi_sta_filter(phy,
++					UNI_CMD_CSI_SET_STA_FILTER, v2, mac_addr);
++		case CSI_CONFIG_ACTIVE_MODE:
++			if (is_valid_ether_addr(mac_addr)) {
++				struct csi_bitmap_info_update sta_info;
++
++				if (v2 > 255)
++					return -EINVAL;
++
++				memcpy(sta_info.addr, mac_addr, ETH_ALEN);
++				sta_info.action = v2;
++
++				ieee80211_iterate_stations_atomic(phy->mt76->hw,
++								mt7996_csi_wcid_bitmap_update, &sta_info);
++				return 0;
++			} else {
++				u8 frame_type = v1 & 0x3;
++				u8 frame_subtype = (v1 & 0x3c) >> 2;
++
++					/* active mode: max interval is 3000ms */
++					if (v2 > 3000)
++						return -EINVAL;
++
++				return mt7996_mcu_set_csi_active_mode(phy, UNI_CMD_CSI_SET_ACTIVE_MODE,
++						v2, frame_type, frame_subtype, phy->csi.active_bitmap);
++			}
++		default:
++			return -EINVAL;
++		}
++	default:
++		return -EINVAL;
++	}
++}
+ #endif
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index f9f04680..42e9f525 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -1153,4 +1153,109 @@ struct fixed_rate_table_ctrl {
+ 	u8 _rsv2;
+ } __packed;
+ 
++#ifdef CONFIG_MTK_VENDOR
++struct mt7996_mcu_csi_event {
++	struct mt7996_mcu_rxd rxd;
++
++	u8 band_idx;
++	u8 _rsv[3];
++
++	__le16 tag;
++	__le16 len;
++	u8 tlv_buf[0];
++};
++
++enum UNI_EVENT_CSI_TAG_T {
++	UNI_EVENT_CSI_DATA = 0,
++	UNI_EVENT_CSI_MAX_NUM
++};
++
++struct csi_tlv {
++	struct {
++		__le32 tag;
++		__le32 len;
++	} basic;
++	union {
++		u8 mac[ETH_ALEN];
++		__le32 info;
++		s16 data[0];
++	};
++} __packed;
++
++struct csi_bitmap_info_update {
++	u8 action;
++	u8 addr[ETH_ALEN];
++};
++
++#define CSI_MAX_BUF_NUM	3000
++
++enum CSI_EVENT_TLV_TAG {
++	CSI_EVENT_FW_VER,
++	CSI_EVENT_CBW,
++	CSI_EVENT_RSSI,
++	CSI_EVENT_SNR,
++	CSI_EVENT_BAND,
++	CSI_EVENT_CSI_NUM,
++	CSI_EVENT_CSI_I_DATA,
++	CSI_EVENT_CSI_Q_DATA,
++	CSI_EVENT_DBW,
++	CSI_EVENT_CH_IDX,
++	CSI_EVENT_TA,
++	CSI_EVENT_EXTRA_INFO,
++	CSI_EVENT_RX_MODE,
++	CSI_EVENT_RSVD1,
++	CSI_EVENT_RSVD2,
++	CSI_EVENT_RSVD3,
++	CSI_EVENT_RSVD4,
++	CSI_EVENT_H_IDX,
++	CSI_EVENT_TX_RX_IDX,
++	CSI_EVENT_TS,
++	CSI_EVENT_PKT_SN,
++	CSI_EVENT_BW_SEG,
++	CSI_EVENT_REMAIN_LAST,
++	CSI_EVENT_TR_STREAM,
++	CSI_EVENT_TLV_TAG_NUM,
++};
++
++enum CSI_CHAIN_TYPE {
++	CSI_CHAIN_ERR,
++	CSI_CHAIN_COMPLETE,
++	CSI_CHAIN_SEGMENT_FIRST,
++	CSI_CHAIN_SEGMENT_MIDDLE,
++	CSI_CHAIN_SEGMENT_LAST,
++	CSI_CHAIN_SEGMENT_ERR,
++};
++
++enum CSI_CONTROL_MODE_T {
++	CSI_CONTROL_MODE_STOP,
++	CSI_CONTROL_MODE_START,
++	CSI_CONTROL_MODE_SET,
++	CSI_CONTROL_MODE_NUM
++};
++
++enum CSI_CONFIG_ITEM_T {
++	CSI_CONFIG_RSVD1,
++	CSI_CONFIG_WF,
++	CSI_CONFIG_RSVD2,
++	CSI_CONFIG_FRAME_TYPE,
++	CSI_CONFIG_TX_PATH,
++	CSI_CONFIG_OUTPUT_FORMAT,
++	CSI_CONFIG_INFO,
++	CSI_CONFIG_CHAIN_FILTER,
++	CSI_CONFIG_STA_FILTER,
++	CSI_CONFIG_ACTIVE_MODE,
++	CSI_CONFIG_ITEM_NUM
++};
++
++/* CSI config Tag */
++enum UNI_CMD_CSI_TAG_T {
++	UNI_CMD_CSI_STOP = 0,
++	UNI_CMD_CSI_START = 1,
++	UNI_CMD_CSI_SET_FRAME_TYPE = 2,
++	UNI_CMD_CSI_SET_CHAIN_FILTER = 3,
++	UNI_CMD_CSI_SET_STA_FILTER = 4,
++	UNI_CMD_CSI_SET_ACTIVE_MODE = 5,
++};
++#endif
++
+ #endif
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 40ee8949..f0492078 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -445,6 +445,47 @@ struct mt7996_air_monitor_ctrl {
+ 	struct mt7996_air_monitor_group group[MT7996_AIR_MONITOR_MAX_GROUP];
+ 	struct mt7996_air_monitor_entry entry[MT7996_AIR_MONITOR_MAX_ENTRY];
+ };
++
++enum {
++	CSI_BW20,
++	CSI_BW40,
++	CSI_BW80,
++	CSI_BW160,
++	CSI_BW320
++};
++
++#define CSI_BW20_DATA_COUNT	64
++#define CSI_BW40_DATA_COUNT	128
++#define CSI_BW80_DATA_COUNT	256
++#define CSI_BW160_DATA_COUNT	512
++#define CSI_BW320_DATA_COUNT	1024
++
++struct csi_data {
++	u8 fw_ver;
++	u8 ch_bw;
++	u16 data_num;
++	s16 data_i[CSI_BW320_DATA_COUNT];
++	s16 data_q[CSI_BW320_DATA_COUNT];
++	u8 band;
++	s8 rssi;
++	u8 snr;
++	u32 ts;
++	u8 data_bw;
++	u8 pri_ch_idx;
++	u8 ta[ETH_ALEN];
++	u32 ext_info;
++	u16 rx_mode;
++	u16 rx_rate;
++	u32 chain_info;
++	u16 tx_idx;
++	u16 rx_idx;
++	u32 segment_num;
++	u8 remain_last;
++	u16 pkt_sn;
++	u8 tr_stream;
++
++	struct list_head node;
++};
+ #endif
+ 
+ struct mt7996_rro_ba_session {
+@@ -541,6 +582,18 @@ struct mt7996_phy {
+ 	u8 rts_bw_sig;
+ 	spinlock_t amnt_lock;
+ 	struct mt7996_air_monitor_ctrl amnt_ctrl;
++
++	struct {
++		struct list_head list;
++		spinlock_t lock;
++		u32 count;
++		bool enable;
++
++		struct csi_data buffered_csi;
++		u32 active_bitmap;
++		u32 interval;
++		u32 last_record;
++	} csi;
+ #endif
+ #ifdef CONFIG_MTK_DEBUG
+ 	bool sr_enable:1;
+@@ -1176,6 +1229,8 @@ void mt7996_mcu_set_mimo(struct mt7996_phy *phy);
+ int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val);
+ int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data);
+ void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
++int mt7996_mcu_set_csi(struct mt7996_phy *phy, u8 mode,
++		       u8 cfg, u8 v1, u32 v2, u8 *mac_addr);
+ #endif
+ 
+ int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index 64ef5515..84b50ab2 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -119,6 +119,19 @@ beacon_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BEACON_CTRL] = {
+ 	[MTK_VENDOR_ATTR_BEACON_CTRL_MODE] = { .type = NLA_U8 },
+ };
+ 
++static const struct nla_policy
++csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
++	[MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG] = {.type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2] = { .type = NLA_U32 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR] = { .type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM] = { .type = NLA_U16 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_DATA] = { .type = NLA_NESTED },
++};
++
+ struct mt7996_amnt_data {
+ 	u8 idx;
+ 	u8 addr[ETH_ALEN];
+@@ -1000,7 +1013,226 @@ static int mt7996_vendor_beacon_ctrl(struct wiphy *wiphy,
+ 
+ 	return 0;
+ }
++static int mt7996_vendor_csi_ctrl(struct wiphy *wiphy,
++				  struct wireless_dev *wdev,
++				  const void *data,
++				  int data_len)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mt7996_phy *phy;
++	struct mt76_phy *mphy;
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_CSI_CTRL];
++	u8 band_idx = 0;
++	int err;
++
++	err = nla_parse(tb, MTK_VENDOR_ATTR_CSI_CTRL_MAX, data, data_len,
++			csi_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	if (tb[MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX])
++		band_idx = nla_get_u8(tb[MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX]);
++
++	if (!mt7996_band_valid(dev, band_idx))
++		goto error;
++
++	mphy = dev->mt76.phys[band_idx];
++	if (!mphy)
++		goto error;
++
++	phy = (struct mt7996_phy *)mphy->priv;
++	if (!phy)
++		goto error;
++
++	if (tb[MTK_VENDOR_ATTR_CSI_CTRL_CFG]) {
++		u8 mode = 0, type = 0, v1 = 0;
++		u32 v2 = 0;
++		u8 mac_addr[ETH_ALEN] = {};
++		struct nlattr *cur;
++		int rem;
++
++		nla_for_each_nested(cur, tb[MTK_VENDOR_ATTR_CSI_CTRL_CFG], rem) {
++			switch (nla_type(cur)) {
++			case MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE:
++				mode = nla_get_u8(cur);
++				break;
++			case MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE:
++				type = nla_get_u8(cur);
++				break;
++			case MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1:
++				v1 = nla_get_u8(cur);
++				break;
++			case MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2:
++				v2 = nla_get_u32(cur);
++				break;
++			default:
++				return -EINVAL;
++			};
++		}
++
++		if (tb[MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR]) {
++			u8 idx = 0;
++
++			nla_for_each_nested(cur, tb[MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR], rem) {
++				mac_addr[idx++] = nla_get_u8(cur);
++			}
++		}
++
++		err = mt7996_mcu_set_csi(phy, mode, type, v1, v2, mac_addr);
++		if (err < 0)
++			return err;
++
++		spin_lock_bh(&phy->csi.lock);
++
++		phy->csi.enable = !!mode;
++
++		/* clean up old csi stats */
++		if ((mode == CSI_CONTROL_MODE_STOP || mode == CSI_CONTROL_MODE_SET)
++			&& !list_empty(&phy->csi.list)) {
++			struct csi_data *c, *tmp_c;
++
++			list_for_each_entry_safe(c, tmp_c, &phy->csi.list, node) {
++				list_del(&c->node);
++				kfree(c);
++				phy->csi.count--;
++			}
++		} else if (mode == CSI_CONTROL_MODE_START) {
++			phy->csi.last_record = 0;
++		}
++
++		spin_unlock_bh(&phy->csi.lock);
++
++		if (mode == CSI_CONTROL_MODE_SET && type == CSI_CONFIG_STA_FILTER && v1 == 2)
++			phy->csi.interval = v2;
++	}
++
++	return 0;
++
++error:
++	dev_err(dev->mt76.dev, "Invalid band idx: %d\n", band_idx);
++	return -EINVAL;
++}
++
++static int
++mt7996_vendor_csi_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
++			    struct sk_buff *skb, const void *data, int data_len,
++			    unsigned long *storage)
++{
++#define RESERVED_SET	BIT(31)
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mt7996_phy *phy;
++	struct mt76_phy *mphy;
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {0};
++	u8 band_idx = 0;
++	int err = 0;
++
++	if (*storage & RESERVED_SET) {
++		if ((*storage & GENMASK(15, 0)) == 0)
++			return -ENOENT;
++		(*storage)--;
++	}
++
++	if (data) {
++		err = nla_parse(tb, MTK_VENDOR_ATTR_CSI_CTRL_MAX, data, data_len,
++				csi_ctrl_policy, NULL);
++		if (err)
++			return err;
++	}
++
++	if (tb[MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX])
++		band_idx = nla_get_u8(tb[MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX]);
++
++	if (!mt7996_band_valid(dev, band_idx))
++		return -EINVAL;
+ 
++	mphy = dev->mt76.phys[band_idx];
++	if (!mphy)
++		return -EINVAL;
++
++	phy = (struct mt7996_phy *)mphy->priv;
++	if (!phy)
++		return -EINVAL;
++
++	if (!(*storage & RESERVED_SET) && tb[MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM]) {
++		*storage = nla_get_u16(tb[MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM]);
++		*storage |= RESERVED_SET;
++	}
++
++	spin_lock_bh(&phy->csi.lock);
++
++	if (!list_empty(&phy->csi.list)) {
++		struct csi_data *csi;
++		void *a, *b;
++		int i;
++
++		csi = list_first_entry(&phy->csi.list, struct csi_data, node);
++
++		a = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_CTRL_DATA);
++		if (!a)
++			goto out;
++
++		if (nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_VER, 1) ||
++		    nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_RSSI, csi->rssi) ||
++		    nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_SNR, csi->snr) ||
++		    nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_BW, csi->data_bw) ||
++		    nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_CH_IDX, csi->pri_ch_idx) ||
++		    nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_MODE, csi->rx_mode))
++			goto out;
++
++		if (nla_put_u16(skb, MTK_VENDOR_ATTR_CSI_DATA_TX_ANT, csi->tx_idx) ||
++		    nla_put_u16(skb, MTK_VENDOR_ATTR_CSI_DATA_RX_ANT, csi->rx_idx))
++			goto out;
++
++		if (nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_INFO, csi->ext_info) ||
++		    nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO, csi->chain_info) ||
++		    nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_TS, csi->ts))
++			goto out;
++
++		b = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_DATA_TA);
++		if (!b)
++			goto out;
++
++		for (i = 0; i < ARRAY_SIZE(csi->ta); i++)
++			if (nla_put_u8(skb, i, csi->ta[i]))
++				goto out;
++		nla_nest_end(skb, b);
++
++		if (nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_NUM, csi->data_num))
++			goto out;
++
++		b = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_DATA_I);
++		if (!b)
++			goto out;
++
++		for (i = 0; i < csi->data_num; i++)
++			if (nla_put_u16(skb, i, csi->data_i[i]))
++				goto out;
++		nla_nest_end(skb, b);
++
++		b = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_DATA_Q);
++		if (!b)
++			goto out;
++
++		for (i = 0; i < csi->data_num; i++)
++			if (nla_put_u16(skb, i, csi->data_q[i]))
++				goto out;
++		nla_nest_end(skb, b);
++
++		nla_nest_end(skb, a);
++
++		list_del(&csi->node);
++		kfree(csi);
++		phy->csi.count--;
++
++		err = phy->csi.count;
++	}
++out:
++	spin_unlock_bh(&phy->csi.lock);
++
++	return err;
++}
+ 
+ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 	{
+@@ -1129,6 +1361,18 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 		.policy = beacon_ctrl_policy,
+ 		.maxattr = MTK_VENDOR_ATTR_BEACON_CTRL_MAX,
+ 	},
++	{
++		.info = {
++			.vendor_id = MTK_NL80211_VENDOR_ID,
++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++			 WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.doit = mt7996_vendor_csi_ctrl,
++		.dumpit = mt7996_vendor_csi_ctrl_dump,
++		.policy = csi_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_CSI_CTRL_MAX,
++	},
+ };
+ 
+ void mt7996_vendor_register(struct mt7996_phy *phy)
+@@ -1136,6 +1380,9 @@ void mt7996_vendor_register(struct mt7996_phy *phy)
+ 	phy->mt76->hw->wiphy->vendor_commands = mt7996_vendor_commands;
+ 	phy->mt76->hw->wiphy->n_vendor_commands = ARRAY_SIZE(mt7996_vendor_commands);
+ 
++	INIT_LIST_HEAD(&phy->csi.list);
++	spin_lock_init(&phy->csi.lock);
++
+ 	spin_lock_init(&phy->amnt_lock);
+ }
+ #endif
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index 32346775..834b3d08 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -7,6 +7,7 @@
+ 
+ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
++	MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
+ 	MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
+ 	MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
+ 	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
+@@ -240,6 +241,57 @@ enum mtk_vendor_attr_beacon_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_csi_ctrl {
++	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
++	MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_DATA,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_CSI_CTRL,
++	MTK_VENDOR_ATTR_CSI_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_CSI_CTRL - 1
++};
++
++enum mtk_vendor_attr_csi_data {
++	MTK_VENDOR_ATTR_CSI_DATA_UNSPEC,
++	MTK_VENDOR_ATTR_CSI_DATA_PAD,
++
++	MTK_VENDOR_ATTR_CSI_DATA_VER,
++	MTK_VENDOR_ATTR_CSI_DATA_TS,
++	MTK_VENDOR_ATTR_CSI_DATA_RSSI,
++	MTK_VENDOR_ATTR_CSI_DATA_SNR,
++	MTK_VENDOR_ATTR_CSI_DATA_BW,
++	MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
++	MTK_VENDOR_ATTR_CSI_DATA_TA,
++	MTK_VENDOR_ATTR_CSI_DATA_NUM,
++	MTK_VENDOR_ATTR_CSI_DATA_I,
++	MTK_VENDOR_ATTR_CSI_DATA_Q,
++	MTK_VENDOR_ATTR_CSI_DATA_INFO,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD1,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD2,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD3,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD4,
++	MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
++	MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
++	MTK_VENDOR_ATTR_CSI_DATA_MODE,
++	MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_CSI_DATA,
++	MTK_VENDOR_ATTR_CSI_DATA_MAX =
++		NUM_MTK_VENDOR_ATTRS_CSI_DATA - 1
++};
+ #endif
+ 
+ #endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0119-mtk-mt76-mt7996-add-more-debug-info-for-MLO.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0119-mtk-mt76-mt7996-add-more-debug-info-for-MLO.patch
new file mode 100644
index 0000000..772fb37
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0119-mtk-mt76-mt7996-add-more-debug-info-for-MLO.patch
@@ -0,0 +1,2228 @@
+From 9fe070ee29d4d63044a27782e643cd3dad8a8669 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 22 Apr 2024 11:06:48 +0800
+Subject: [PATCH 119/199] mtk: mt76: mt7996: add more debug info for MLO
+
+---
+ mt76_connac_mcu.c    |    1 +
+ mt76_connac_mcu.h    |    1 +
+ mt7996/debugfs.c     |   35 +-
+ mt7996/mac.c         |   15 +
+ mt7996/main.c        |   14 +
+ mt7996/mcu.c         |   27 ++
+ mt7996/mcu.h         |    8 +
+ mt7996/mt7996.h      |   22 +
+ mt7996/mtk_debug.h   |  660 ++++++++++++++++++++++++++
+ mt7996/mtk_debugfs.c | 1050 ++++++++++++++++++++++++++++++++++++++++++
+ tools/fwlog.c        |   40 +-
+ tools/main.c         |    1 +
+ 12 files changed, 1856 insertions(+), 18 deletions(-)
+
+diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
+index ab7cf4a6..8d1036e1 100644
+--- a/mt76_connac_mcu.c
++++ b/mt76_connac_mcu.c
+@@ -432,6 +432,7 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+ 	}
+ 
+ 	memcpy(basic->peer_addr, link_sta->addr, ETH_ALEN);
++	pr_info("%s: link %u addr [%pM]\n", __func__, link_sta->link_id, basic->peer_addr);
+ 	basic->qos = link_sta->sta->wme;
+ }
+ EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_basic_tlv);
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 1852e512..870417f3 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1295,6 +1295,7 @@ enum {
+ 	MCU_UNI_CMD_THERMAL = 0x35,
+ 	MCU_UNI_CMD_VOW = 0x37,
+ 	MCU_UNI_CMD_PP = 0x38,
++	MCU_UNI_CMD_MEC = 0x3a,
+ 	MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
+ 	MCU_UNI_CMD_TESTMODE_TRX_PARAM = 0x42,
+ 	MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index fe8fea5d..63b8887d 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -314,15 +314,17 @@ mt7996_fw_debug_wm_set(void *data, u64 val)
+ 		DEBUG_CMD_RPT_TRIG,
+ 		DEBUG_SPL,
+ 		DEBUG_RPT_RX,
+-		DEBUG_RPT_RA = 68,
+ 		DEBUG_IDS_SND = 84,
++		DEBUG_IDS_BSRP,
++		DEBUG_IDS_TPUT_MON,
+ 		DEBUG_IDS_PP = 93,
+-		DEBUG_IDS_RA = 94,
+-		DEBUG_IDS_BF = 95,
+-		DEBUG_IDS_SR = 96,
+-		DEBUG_IDS_RU = 97,
+-		DEBUG_IDS_MUMIMO = 98,
+-		DEBUG_IDS_ERR_LOG = 101,
++		DEBUG_IDS_RA,
++		DEBUG_IDS_BF,
++		DEBUG_IDS_SR,
++		DEBUG_IDS_RU,
++		DEBUG_IDS_MUMIMO,
++		DEBUG_IDS_MLO = 100,
++		DEBUG_IDS_ERR_LOG,
+ 	};
+ 	u8 debug_category[] = {
+ 		DEBUG_TXCMD,
+@@ -330,14 +332,16 @@ mt7996_fw_debug_wm_set(void *data, u64 val)
+ 		DEBUG_CMD_RPT_TRIG,
+ 		DEBUG_SPL,
+ 		DEBUG_RPT_RX,
+-		DEBUG_RPT_RA,
+ 		DEBUG_IDS_SND,
++		DEBUG_IDS_BSRP,
++		DEBUG_IDS_TPUT_MON,
+ 		DEBUG_IDS_PP,
+ 		DEBUG_IDS_RA,
+ 		DEBUG_IDS_BF,
+ 		DEBUG_IDS_SR,
+ 		DEBUG_IDS_RU,
+ 		DEBUG_IDS_MUMIMO,
++		DEBUG_IDS_MLO,
+ 		DEBUG_IDS_ERR_LOG,
+ 	};
+ 	bool tx, rx, en;
+@@ -372,7 +376,8 @@ mt7996_fw_debug_wm_set(void *data, u64 val)
+ 		if (ret)
+ 			return ret;
+ 
+-		if (debug_category[i] == DEBUG_IDS_SND && en) {
++		if ((debug_category[i] == DEBUG_TXCMD ||
++		     debug_category[i] == DEBUG_IDS_SND) && en) {
+ 			ret = mt7996_mcu_fw_dbg_ctrl(dev, debug_category[i], 2);
+ 			if (ret)
+ 				return ret;
+@@ -506,6 +511,15 @@ mt7996_fw_debug_bin_set(void *data, u64 val)
+ 	if (ret)
+ 		return ret;
+ 
++#ifdef CONFIG_MTK_DEBUG
++	dev->dbg.dump_mcu_pkt = !!(val & BIT(4));
++	dev->dbg.dump_txd = !!(val & BIT(5));
++	dev->dbg.dump_tx_pkt = !!(val & BIT(6));
++	dev->dbg.dump_rx_pkt = !!(val & BIT(7));
++	dev->dbg.dump_rx_raw = !!(val & BIT(8));
++	dev->dbg.dump_mcu_event = !!(val & BIT(9));
++#endif
++
+ 	return mt7996_fw_debug_wm_set(dev, dev->fw_debug_wm);
+ }
+ 
+@@ -1195,6 +1209,9 @@ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int
+ bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len)
+ {
+ 	bool is_fwlog = get_unaligned_le32(data) == FW_BIN_LOG_MAGIC;
++#ifdef CONFIG_MTK_DEBUG
++	is_fwlog |= get_unaligned_le32(data) == PKT_BIN_DEBUG_MAGIC;
++#endif
+ 
+ 	if (is_fwlog) {
+ 		if (dev->relay_fwlog)
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 803f7ce3..6462c64c 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -331,6 +331,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ 	u8 hw_aggr = false;
+ 	struct mt7996_link_sta *mlink = NULL;
+ 
++#ifdef CONFIG_MTK_DEBUG
++	if (dev->dbg.dump_rx_raw)
++		mt7996_packet_log_to_host(dev, skb->data, skb->len, PKT_BIN_DEBUG_RX_RAW, 0);
++#endif
+ 	hw_aggr = status->aggr;
+ 	memset(status, 0, sizeof(*status));
+ 
+@@ -505,6 +509,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ 	}
+ 
+ 	hdr_gap = (u8 *)rxd - skb->data + 2 * remove_pad;
++#ifdef CONFIG_MTK_DEBUG
++	if (dev->dbg.dump_rx_pkt)
++		mt7996_packet_log_to_host(dev, skb->data, skb->len, PKT_BIN_DEBUG_RX, hdr_gap);
++#endif
+ 	if (hdr_trans && ieee80211_has_morefrags(fc)) {
+ 		if (mt7996_reverse_frag0_hdr_trans(skb, hdr_gap))
+ 			return -EINVAL;
+@@ -982,6 +990,13 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 	tx_info->buf[1].skip_unmap = true;
+ 	tx_info->nbuf = MT_CT_DMA_BUF_NUM;
+ 
++#ifdef CONFIG_MTK_DEBUG
++	if (dev->dbg.dump_txd)
++		mt7996_packet_log_to_host(dev, txwi, MT_TXD_SIZE, PKT_BIN_DEBUG_TXD, 0);
++	if (dev->dbg.dump_tx_pkt)
++		mt7996_packet_log_to_host(dev, t->skb->data, t->skb->len, PKT_BIN_DEBUG_TX, 0);
++#endif
++
+ 	return 0;
+ }
+ 
+diff --git a/mt7996/main.c b/mt7996/main.c
+index b94af2f6..fa9486e4 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -433,6 +433,9 @@ static int mt7996_add_bss_conf(struct mt7996_phy *phy,
+ 	rcu_assign_pointer(mvif->link[link_id], mconf);
+ 	rcu_assign_pointer(mvif->sta.link[link_id], mlink);
+ 
++	mlo_dbg(phy, "bss_idx=%u, link_id=%u, wcid=%u\n",
++		mconf->mt76.idx, mconf->link_id, mlink->wcid.idx);
++
+ 	return 0;
+ error:
+ 	mt7996_remove_bss_conf(vif, conf, mconf);
+@@ -610,6 +613,11 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ 			add = vif->valid_links ?: BIT(0);
+ 	}
+ 
++	mlo_dbg(mt7996_hw_phy(hw), "cipher = 0x%x, icv_len = %u, iv_len = %u, hw_key_idx = %u, keyidx = %d, flags = 0x%x, link_id = %d, keylen = %u\n",
++		     key->cipher, key->icv_len, key->iv_len, key->hw_key_idx, key->keyidx, key->flags, key->link_id, key->keylen);
++	// print_hex_dump(KERN_INFO , "", DUMP_PREFIX_OFFSET, 16, 1, key->key, key->keylen, false);
++	mlo_dbg(mt7996_hw_phy(hw), "add=%lx, valid_links=%x, active_links=%x\n", add, vif->valid_links, vif->active_links);
++
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+ 	for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
+@@ -1140,6 +1148,8 @@ static int mt7996_add_link_sta(struct mt7996_dev *dev,
+ 			mt76_wcid_mask_set(dev->mt76.wcid_phy_mask, idx);
+ 		rcu_assign_pointer(dev->mt76.wcid[idx], &mlink->wcid);
+ 		mt76_wcid_init(&mlink->wcid);
++
++		mlo_dbg(mconf->phy, "wcid=%u, link_id=%u, link_addr=%pM, pri_link=%u, sec_link=%u\n", mlink->wcid.idx, link_id, link_sta->addr, msta->pri_link, msta->sec_link);
+ 	}
+ 
+ 	if (!assoc)
+@@ -1177,6 +1187,7 @@ mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 	if (!ieee80211_vif_is_mld(vif) || rem == sta->valid_links)
+ 		cancel_delayed_work(&mvif->beacon_mon_work);
+ 
++	mlo_dbg(mt7996_hw_phy(mvif->hw), "rem=%lu\n", rem);
+ 	for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
+ 		struct mt7996_bss_conf *mconf =
+ 			mconf_dereference_protected(mvif, link_id);
+@@ -1202,6 +1213,7 @@ mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 	unsigned int link_id;
+ 	int i, ret;
+ 
++	mlo_dbg(mt7996_hw_phy(mvif->hw), "add=%lu, assoc=%d\n", add, assoc);
+ 	for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
+ 		struct mt7996_bss_conf *mconf =
+ 			mconf_dereference_protected(mvif, link_id);
+@@ -2556,6 +2568,7 @@ mt7996_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	unsigned int link_id;
+ 	int ret = 0;
+ 
++	mlo_dbg(phy, "old=%u, new=%u\n", old_links, new_links);
+ 	if (old_links == new_links)
+ 		return 0;
+ 
+@@ -2601,6 +2614,7 @@ mt7996_change_sta_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	unsigned long rem = old_links & ~new_links;
+ 	int ret = 0;
+ 
++	mlo_dbg(mt7996_hw_phy(hw), "old=%u, new=%u\n", old_links, new_links);
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+ 	if (rem)
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index c59dbbea..0b02b76b 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -333,6 +333,10 @@ mt7996_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
+ 		mcu_txd->s2d_index = MCU_S2D_H2N;
+ 
+ exit:
++#ifdef CONFIG_MTK_DEBUG
++	if (dev->dbg.dump_mcu_pkt)
++		mt7996_packet_log_to_host(dev, skb->data, skb->len, PKT_BIN_DEBUG_MCU, 0);
++#endif
+ 	if (wait_seq)
+ 		*wait_seq = seq;
+ 
+@@ -1120,6 +1124,10 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+ 	struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data;
+ 
++#ifdef CONFIG_MTK_DEBUG
++	if (dev->dbg.dump_mcu_event)
++		mt7996_packet_log_to_host(dev, skb->data, skb->len, PKT_BIN_DEBUG_MCU_EVENT, 0);
++#endif
+ 	switch (rxd->eid) {
+ 	case MCU_UNI_EVENT_FW_LOG_2_HOST:
+ 		mt7996_mcu_rx_log_message(dev, skb);
+@@ -1336,6 +1344,8 @@ mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ 	}
+ 
+ 	mld->own_mld_id = mconf->own_mld_id;
++	pr_info("%s: group_mld_id=%d own_mld_id=%d remap_idx=%d mld->addr[%pM]\n",
++		__func__, mld->group_mld_id,  mld->own_mld_id, mld->remap_idx, mld->mac_addr);
+ }
+ 
+ static void
+@@ -1487,6 +1497,10 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
+ 	}
+ 
+ 	memcpy(bss->bssid, conf->bssid, ETH_ALEN);
++
++	mlo_dbg(mconf->phy, "omac_idx=%d band_idx=%d wmm_idx=%d bss->bssid=%pM enable=%d\n",
++		bss->omac_idx, bss->band_idx, bss->wmm_idx, bss->bssid, enable);
++
+ 	bss->bcn_interval = cpu_to_le16(conf->beacon_int);
+ 	bss->dtim_period = conf->dtim_period;
+ 	bss->phymode = mt76_connac_get_phy_mode(phy, vif,
+@@ -2771,6 +2785,8 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ 	/* starec basic */
+ 	mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, conf, link_sta,
+ 				      enable, newly);
++	mlo_dbg(mconf->phy, "link=%u, newly=%d, en=%d\n",
++		mlink->wcid.link_id, newly, enable);
+ 
+ 	if (!enable)
+ 		goto out;
+@@ -2853,12 +2869,16 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 	mld_setup->link_num = hweight16(sta->valid_links);
+ 
+ 	mld_setup_link = (struct mld_setup_link *)mld_setup->link_info;
++	mlo_dbg(mt7996_hw_phy(mlink->sta->vif->hw), "pri_link(%u) primary_id(%d) seconed_id(%d) wcid(%d), link_num(%d), mld_addr[%pM]\n",
++		msta->pri_link, mld_setup->primary_id, mld_setup->seconed_id, mld_setup->setup_wcid, mld_setup->link_num, mld_setup->mld_addr);
+ 	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ 		mlink = mlink_dereference_protected(msta, link_id);
+ 		mconf = mconf_dereference_protected(msta->vif, link_id);
+ 
+ 		mld_setup_link->wcid = cpu_to_le16(mlink->wcid.idx);
+ 		mld_setup_link->bss_idx = mconf->mt76.idx;
++		mlo_dbg(mt7996_hw_phy(mlink->sta->vif->hw), "link_id(%d) wcid(%d) bss_idx(%d)\n",
++		link_id, mld_setup_link->wcid, mld_setup_link->bss_idx);
+ 		mld_setup_link++;
+ 	}
+ }
+@@ -3122,6 +3142,8 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
+ 		return mt7996_mcu_muar_config(phy, conf, mconf, false, enable);
+ 
+ 	memcpy(data.tlv.omac_addr, conf->addr, ETH_ALEN);
++	mlo_dbg(phy, "omac=%u, band=%u, addr=%pM, en=%d\n",
++		data.hdr.omac_idx,data.hdr.band_idx, data.tlv.omac_addr, enable);
+ 	return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(DEV_INFO_UPDATE),
+ 				 &data, sizeof(data), true);
+ }
+@@ -3226,6 +3248,11 @@ mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ 			      BSS_CHANGED_BEACON);
+ 
+ 	memcpy(buf + MT_TXD_SIZE, skb->data, skb->len);
++
++	if (dev->dbg.dump_txd)
++		mt7996_packet_log_to_host(dev, (__le32 *)buf, MT_TXD_SIZE, PKT_BIN_DEBUG_TXD, 0);
++	if (dev->dbg.dump_tx_pkt)
++		mt7996_packet_log_to_host(dev, skb->data, skb->len, PKT_BIN_DEBUG_TX, 0);
+ }
+ 
+ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 42e9f525..5e217719 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -1029,6 +1029,14 @@ enum {
+ 	UNI_RRO_SET_FLUSH_TIMEOUT
+ };
+ 
++enum {
++	UNI_MEC_READ_INFO = 0,
++	UNI_MEC_AMSDU_ALGO_EN_STA,
++	UNI_MEC_AMSDU_PARA_STA,
++	UNI_MEC_AMSDU_ALGO_THRESHOLD,
++	UNI_MEC_IFAC_SPEED,
++};
++
+ enum{
+ 	UNI_CMD_SR_ENABLE = 0x1,
+ 	UNI_CMD_SR_ENABLE_SD,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index f0492078..ff13d00e 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -737,6 +737,14 @@ struct mt7996_dev {
+ 		u8 fw_dbg_lv;
+ 		u32 bcn_total_cnt[__MT_MAX_BAND];
+ 		u32 sid;
++
++		bool dump_mcu_pkt:1;
++		bool dump_mcu_event:1;
++		bool dump_txd:1;
++		bool dump_tx_pkt:1;
++		bool dump_rx_pkt:1;
++		bool dump_rx_raw:1;
++		u32 token_idx;
+ 	} dbg;
+ 	const struct mt7996_dbg_reg_desc *dbg_reg;
+ #endif
+@@ -935,6 +943,8 @@ mt7996_get_link_wcid(struct mt7996_dev *dev, u16 idx, u8 band_idx)
+ 	return &mlink->wcid;
+ }
+ 
++#define mlo_dbg(phy, fmt, ...)   wiphy_info(phy->mt76->hw->wiphy, "%s: " fmt, __func__, ##__VA_ARGS__)
++
+ extern const struct ieee80211_ops mt7996_ops;
+ extern struct pci_driver mt7996_pci_driver;
+ extern struct pci_driver mt7996_hif_driver;
+@@ -1266,6 +1276,18 @@ void mt7996_tm_update_channel(struct mt7996_phy *phy);
+ 
+ int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val);
+ int mt7996_mcu_thermal_debug(struct mt7996_dev *dev, u8 mode, u8 action);
++
++#define PKT_BIN_DEBUG_MAGIC	0xc8763123
++enum {
++	PKT_BIN_DEBUG_MCU,
++	PKT_BIN_DEBUG_TXD,
++	PKT_BIN_DEBUG_TX,
++	PKT_BIN_DEBUG_RX,
++	PKT_BIN_DEBUG_RX_RAW,
++	PKT_BIN_DEBUG_MCU_EVENT,
++};
++
++void mt7996_packet_log_to_host(struct mt7996_dev *dev, const void *data, int len, int type, int des_len);
+ #endif
+ 
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mtk_debug.h b/mt7996/mtk_debug.h
+index da2a6072..82990239 100644
+--- a/mt7996/mtk_debug.h
++++ b/mt7996/mtk_debug.h
+@@ -2288,4 +2288,664 @@ enum cipher_suit {
+ #define WTBL_RATE_STBC_OFFSET          	 	14
+ #endif
+ 
++struct bin_debug_hdr {
++	__le32 magic_num;
++	__le16 serial_id;
++	__le16 msg_type;
++	__le16 len;
++	__le16 des_len;	/* descriptor len for rxd */
++} __packed;
++
++enum umac_port {
++	ENUM_UMAC_HIF_PORT_0         = 0,
++	ENUM_UMAC_CPU_PORT_1         = 1,
++	ENUM_UMAC_LMAC_PORT_2        = 2,
++	ENUM_PLE_CTRL_PSE_PORT_3     = 3,
++	ENUM_UMAC_PSE_PLE_PORT_TOTAL_NUM = 4
++};
++
++/* N9 MCU QUEUE LIST */
++enum umac_cpu_port_queue_idx {
++	ENUM_UMAC_CTX_Q_0 = 0,
++	ENUM_UMAC_CTX_Q_1 = 1,
++	ENUM_UMAC_CTX_Q_2 = 2,
++	ENUM_UMAC_CTX_Q_3 = 3,
++	ENUM_UMAC_CRX     = 0,
++	ENUM_UMAC_CIF_QUEUE_TOTAL_NUM = 4
++};
++
++/* LMAC PLE For PSE Control P3 */
++enum umac_ple_ctrl_port3_queue_idx {
++	ENUM_UMAC_PLE_CTRL_P3_Q_0X1E            = 0x1e,
++	ENUM_UMAC_PLE_CTRL_P3_Q_0X1F            = 0x1f,
++	ENUM_UMAC_PLE_CTRL_P3_TOTAL_NUM         = 2
++};
++
++/* PSE PLE QUEUE */
++#define CR_NUM_OF_AC_MT7996	34
++#define CR_NUM_OF_AC_MT7992	17
++struct bmac_queue_info {
++	char *QueueName;
++	u32 Portid;
++	u32 Queueid;
++	u32 tgid;
++};
++
++struct bmac_queue_info_t {
++	char *QueueName;
++	u32 Portid;
++	u32 Queueid;
++};
++
++#define WF_DRR_TOP_BASE                                        0x820c8800
++#define WF_DRR_TOP_SBRR_ADDR                                   (WF_DRR_TOP_BASE + 0x00E0) // 88E0
++#define WF_DRR_TOP_TWT_STA_MAP00_ADDR                          (WF_DRR_TOP_BASE + 0x0100) // 8900
++#define WF_DRR_TOP_TWT_STA_MAP_EXT_00_ADDR                     (WF_DRR_TOP_BASE + 0x0180) // 8980
++#define WF_DRR_TOP_AC0_STATION_PAUSE00_ADDR                    (WF_DRR_TOP_BASE + 0x0200) // 8A00
++#define WF_DRR_TOP_AC0_STATION_PAUSE_EXT_00_ADDR               (WF_DRR_TOP_BASE + 0x0280) // 8A80
++#define WF_DRR_TOP_AC1_STATION_PAUSE00_ADDR                    (WF_DRR_TOP_BASE + 0x0300) // 8B00
++#define WF_DRR_TOP_AC1_STATION_PAUSE_EXT_00_ADDR               (WF_DRR_TOP_BASE + 0x0380) // 8B80
++#define WF_DRR_TOP_AC2_STATION_PAUSE00_ADDR                    (WF_DRR_TOP_BASE + 0x0400) // 8C00
++#define WF_DRR_TOP_AC2_STATION_PAUSE_EXT_00_ADDR               (WF_DRR_TOP_BASE + 0x0480) // 8C80
++#define WF_DRR_TOP_AC3_STATION_PAUSE00_ADDR                    (WF_DRR_TOP_BASE + 0x0500) // 8D00
++#define WF_DRR_TOP_AC3_STATION_PAUSE_EXT_00_ADDR               (WF_DRR_TOP_BASE + 0x0580) // 8D80
++
++#define WF_DRR_TOP_SBRR_TARGET_BAND_MASK                       0x00000003                // TARGET_BAND[1..0]
++/* PLE AMSDU */
++#define WF_PLE_TOP_BASE                                        0x820c0000
++
++#define WF_PLE_TOP_AMSDU_PACK_1_MSDU_CNT_ADDR                  (WF_PLE_TOP_BASE + 0x10e0) // 10E0
++#define WF_PLE_TOP_AMSDU_PACK_2_MSDU_CNT_ADDR                  (WF_PLE_TOP_BASE + 0x10e4) // 10E4
++#define WF_PLE_TOP_AMSDU_PACK_3_MSDU_CNT_ADDR                  (WF_PLE_TOP_BASE + 0x10e8) // 10E8
++#define WF_PLE_TOP_AMSDU_PACK_4_MSDU_CNT_ADDR                  (WF_PLE_TOP_BASE + 0x10ec) // 10EC
++#define WF_PLE_TOP_AMSDU_PACK_5_MSDU_CNT_ADDR                  (WF_PLE_TOP_BASE + 0x10f0) // 10F0
++#define WF_PLE_TOP_AMSDU_PACK_6_MSDU_CNT_ADDR                  (WF_PLE_TOP_BASE + 0x10f4) // 10F4
++#define WF_PLE_TOP_AMSDU_PACK_7_MSDU_CNT_ADDR                  (WF_PLE_TOP_BASE + 0x10f8) // 10F8
++#define WF_PLE_TOP_AMSDU_PACK_8_MSDU_CNT_ADDR                  (WF_PLE_TOP_BASE + 0x10fc) // 10FC
++#define WF_PLE_TOP_AMSDU_PACK_NUM                              8
++
++/* PLE */
++#define WF_PLE_TOP_PBUF_CTRL_ADDR                              (WF_PLE_TOP_BASE + 0x04) // 0004
++
++#define WF_PLE_TOP_PG_HIF_GROUP_ADDR                           (WF_PLE_TOP_BASE + 0x0c) // 000C
++#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_ADDR                     (WF_PLE_TOP_BASE + 0x10) // 0010
++#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_ADDR                     (WF_PLE_TOP_BASE + 0x14) // 0014
++#define WF_PLE_TOP_PG_CPU_GROUP_ADDR                           (WF_PLE_TOP_BASE + 0x18) // 0018
++#define WF_PLE_TOP_QUEUE_EMPTY_ADDR                            (WF_PLE_TOP_BASE + 0x360) // 0360
++
++#define WF_PLE_TOP_DIS_STA_MAP0_ADDR                           (WF_PLE_TOP_BASE + 0x100) // 0100
++#define WF_PLE_TOP_DIS_STA_MAP1_ADDR                           (WF_PLE_TOP_BASE + 0x104) // 0104
++#define WF_PLE_TOP_DIS_STA_MAP2_ADDR                           (WF_PLE_TOP_BASE + 0x108) // 0108
++#define WF_PLE_TOP_DIS_STA_MAP3_ADDR                           (WF_PLE_TOP_BASE + 0x10c) // 010C
++#define WF_PLE_TOP_DIS_STA_MAP4_ADDR                           (WF_PLE_TOP_BASE + 0x110) // 0110
++#define WF_PLE_TOP_DIS_STA_MAP5_ADDR                           (WF_PLE_TOP_BASE + 0x114) // 0114
++#define WF_PLE_TOP_DIS_STA_MAP6_ADDR                           (WF_PLE_TOP_BASE + 0x118) // 0118
++#define WF_PLE_TOP_DIS_STA_MAP7_ADDR                           (WF_PLE_TOP_BASE + 0x11c) // 011C
++#define WF_PLE_TOP_DIS_STA_MAP8_ADDR                           (WF_PLE_TOP_BASE + 0x120) // 0120
++
++#define WF_PLE_TOP_TXCMD_QUEUE_EMPTY_ADDR                      (WF_PLE_TOP_BASE + 0x378) // 0378
++#define WF_PLE_TOP_NATIVE_TXCMD_QUEUE_EMPTY_ADDR               (WF_PLE_TOP_BASE + 0x37c) // 037C
++#define WF_PLE_TOP_BN1_TXCMD_QUEUE_EMPTY_ADDR                  (WF_PLE_TOP_BASE + 0x388) // 0388
++#define WF_PLE_TOP_BN1_NATIVE_TXCMD_QUEUE_EMPTY_ADDR           (WF_PLE_TOP_BASE + 0x38c) // 038C
++#define WF_PLE_TOP_BN2_TXCMD_QUEUE_EMPTY_ADDR                  (WF_PLE_TOP_BASE + 0x398) // 0398
++#define WF_PLE_TOP_BN2_NATIVE_TXCMD_QUEUE_EMPTY_ADDR           (WF_PLE_TOP_BASE + 0x39c) // 039C
++
++#define WF_PLE_TOP_FREEPG_CNT_ADDR                             (WF_PLE_TOP_BASE + 0x3a0) // 03A0
++#define WF_PLE_TOP_FREEPG_HEAD_TAIL_ADDR                       (WF_PLE_TOP_BASE + 0x3a4) // 03A4
++#define WF_PLE_TOP_HIF_PG_INFO_ADDR                            (WF_PLE_TOP_BASE + 0x3a8) // 03A8
++#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_ADDR                      (WF_PLE_TOP_BASE + 0x3ac) // 03AC
++#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_ADDR                      (WF_PLE_TOP_BASE + 0x3b0) // 03B0
++#define WF_PLE_TOP_CPU_PG_INFO_ADDR                            (WF_PLE_TOP_BASE + 0x3b4) // 03B4
++
++#define WF_PLE_TOP_FL_QUE_CTRL_0_ADDR                          (WF_PLE_TOP_BASE + 0x3e0) // 03E0
++#define WF_PLE_TOP_FL_QUE_CTRL_1_ADDR                          (WF_PLE_TOP_BASE + 0x3e4) // 03E4
++#define WF_PLE_TOP_FL_QUE_CTRL_2_ADDR                          (WF_PLE_TOP_BASE + 0x3e8) // 03E8
++#define WF_PLE_TOP_FL_QUE_CTRL_3_ADDR                          (WF_PLE_TOP_BASE + 0x3ec) // 03EC
++
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY0_ADDR                       (WF_PLE_TOP_BASE + 0x600) // 0600
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY1_ADDR                       (WF_PLE_TOP_BASE + 0x604) // 0604
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY2_ADDR                       (WF_PLE_TOP_BASE + 0x608) // 0608
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY3_ADDR                       (WF_PLE_TOP_BASE + 0x60c) // 060C
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY4_ADDR                       (WF_PLE_TOP_BASE + 0x610) // 0610
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY5_ADDR                       (WF_PLE_TOP_BASE + 0x614) // 0614
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY6_ADDR                       (WF_PLE_TOP_BASE + 0x618) // 0618
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY7_ADDR                       (WF_PLE_TOP_BASE + 0x61c) // 061C
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY8_ADDR                       (WF_PLE_TOP_BASE + 0x620) // 0620
++#define WF_PLE_TOP_AC0_QUEUE_EMPTY_EXT0_ADDR                   (WF_PLE_TOP_BASE + 0x680) // 0680
++
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY0_ADDR                       (WF_PLE_TOP_BASE + 0x700) // 0700
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY1_ADDR                       (WF_PLE_TOP_BASE + 0x704) // 0704
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY2_ADDR                       (WF_PLE_TOP_BASE + 0x708) // 0708
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY3_ADDR                       (WF_PLE_TOP_BASE + 0x70c) // 070C
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY4_ADDR                       (WF_PLE_TOP_BASE + 0x710) // 0710
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY5_ADDR                       (WF_PLE_TOP_BASE + 0x714) // 0714
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY6_ADDR                       (WF_PLE_TOP_BASE + 0x718) // 0718
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY7_ADDR                       (WF_PLE_TOP_BASE + 0x71c) // 071C
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY8_ADDR                       (WF_PLE_TOP_BASE + 0x720) // 0720
++#define WF_PLE_TOP_AC1_QUEUE_EMPTY_EXT0_ADDR                   (WF_PLE_TOP_BASE + 0x780) // 0780
++
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY0_ADDR                       (WF_PLE_TOP_BASE + 0x800) // 0800
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY1_ADDR                       (WF_PLE_TOP_BASE + 0x804) // 0804
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY2_ADDR                       (WF_PLE_TOP_BASE + 0x808) // 0808
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY3_ADDR                       (WF_PLE_TOP_BASE + 0x80c) // 080C
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY4_ADDR                       (WF_PLE_TOP_BASE + 0x810) // 0810
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY5_ADDR                       (WF_PLE_TOP_BASE + 0x814) // 0814
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY6_ADDR                       (WF_PLE_TOP_BASE + 0x818) // 0818
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY7_ADDR                       (WF_PLE_TOP_BASE + 0x81c) // 081C
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY8_ADDR                       (WF_PLE_TOP_BASE + 0x820) // 0820
++#define WF_PLE_TOP_AC2_QUEUE_EMPTY_EXT0_ADDR                   (WF_PLE_TOP_BASE + 0x880) // 0880
++
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY0_ADDR                       (WF_PLE_TOP_BASE + 0x900) // 0900
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY1_ADDR                       (WF_PLE_TOP_BASE + 0x904) // 0904
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY2_ADDR                       (WF_PLE_TOP_BASE + 0x908) // 0908
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY3_ADDR                       (WF_PLE_TOP_BASE + 0x90c) // 090C
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY4_ADDR                       (WF_PLE_TOP_BASE + 0x910) // 0910
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY5_ADDR                       (WF_PLE_TOP_BASE + 0x914) // 0914
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY6_ADDR                       (WF_PLE_TOP_BASE + 0x918) // 0918
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY7_ADDR                       (WF_PLE_TOP_BASE + 0x91c) // 091C
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY8_ADDR                       (WF_PLE_TOP_BASE + 0x920) // 0920
++#define WF_PLE_TOP_AC3_QUEUE_EMPTY_EXT0_ADDR                   (WF_PLE_TOP_BASE + 0x980) // 0980
++
++#define WF_PLE_TOP_QUEUE_EMPTY_ALL_AC_EMPTY_ADDR               WF_PLE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PLE_TOP_QUEUE_EMPTY_ALL_AC_EMPTY_MASK               0x01000000                // ALL_AC_EMPTY[24]
++#define WF_PLE_TOP_QUEUE_EMPTY_ALL_AC_EMPTY_SHFT               24
++
++#define WF_PLE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_ADDR                WF_PLE_TOP_PBUF_CTRL_ADDR
++#define WF_PLE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_MASK                0x80000000                // PAGE_SIZE_CFG[31]
++#define WF_PLE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_SHFT                31
++#define WF_PLE_TOP_PBUF_CTRL_PBUF_OFFSET_ADDR                  WF_PLE_TOP_PBUF_CTRL_ADDR
++#define WF_PLE_TOP_PBUF_CTRL_PBUF_OFFSET_MASK                  0x07FE0000                // PBUF_OFFSET[26..17]
++#define WF_PLE_TOP_PBUF_CTRL_PBUF_OFFSET_SHFT                  17
++#define WF_PLE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_ADDR               WF_PLE_TOP_PBUF_CTRL_ADDR
++#define WF_PLE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_MASK               0x00001FFF                // TOTAL_PAGE_NUM[12..0]
++#define WF_PLE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_SHFT               0
++
++#define WF_PLE_TOP_FREEPG_CNT_FFA_CNT_ADDR                     WF_PLE_TOP_FREEPG_CNT_ADDR
++#define WF_PLE_TOP_FREEPG_CNT_FFA_CNT_MASK                     0x1FFF0000                // FFA_CNT[28..16]
++#define WF_PLE_TOP_FREEPG_CNT_FFA_CNT_SHFT                     16
++#define WF_PLE_TOP_FREEPG_CNT_FREEPG_CNT_ADDR                  WF_PLE_TOP_FREEPG_CNT_ADDR
++#define WF_PLE_TOP_FREEPG_CNT_FREEPG_CNT_MASK                  0x00001FFF                // FREEPG_CNT[12..0]
++#define WF_PLE_TOP_FREEPG_CNT_FREEPG_CNT_SHFT                  0
++
++#define WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_ADDR           WF_PLE_TOP_FREEPG_HEAD_TAIL_ADDR
++#define WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_MASK           0x1FFF0000                // FREEPG_TAIL[28..16]
++#define WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_SHFT           16
++#define WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_ADDR           WF_PLE_TOP_FREEPG_HEAD_TAIL_ADDR
++#define WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_MASK           0x00001FFF                // FREEPG_HEAD[12..0]
++#define WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_SHFT           0
++
++#define WF_PLE_TOP_PG_HIF_GROUP_HIF_MAX_QUOTA_ADDR             WF_PLE_TOP_PG_HIF_GROUP_ADDR
++#define WF_PLE_TOP_PG_HIF_GROUP_HIF_MAX_QUOTA_MASK             0x1FFF0000                // HIF_MAX_QUOTA[28..16]
++#define WF_PLE_TOP_PG_HIF_GROUP_HIF_MAX_QUOTA_SHFT             16
++#define WF_PLE_TOP_PG_HIF_GROUP_HIF_MIN_QUOTA_ADDR             WF_PLE_TOP_PG_HIF_GROUP_ADDR
++#define WF_PLE_TOP_PG_HIF_GROUP_HIF_MIN_QUOTA_MASK             0x00001FFF                // HIF_MIN_QUOTA[12..0]
++#define WF_PLE_TOP_PG_HIF_GROUP_HIF_MIN_QUOTA_SHFT             0
++
++#define WF_PLE_TOP_HIF_PG_INFO_HIF_SRC_CNT_ADDR                WF_PLE_TOP_HIF_PG_INFO_ADDR
++#define WF_PLE_TOP_HIF_PG_INFO_HIF_SRC_CNT_MASK                0x1FFF0000                // HIF_SRC_CNT[28..16]
++#define WF_PLE_TOP_HIF_PG_INFO_HIF_SRC_CNT_SHFT                16
++#define WF_PLE_TOP_HIF_PG_INFO_HIF_RSV_CNT_ADDR                WF_PLE_TOP_HIF_PG_INFO_ADDR
++#define WF_PLE_TOP_HIF_PG_INFO_HIF_RSV_CNT_MASK                0x00001FFF                // HIF_RSV_CNT[12..0]
++#define WF_PLE_TOP_HIF_PG_INFO_HIF_RSV_CNT_SHFT                0
++
++#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MAX_QUOTA_ADDR WF_PLE_TOP_PG_HIF_WMTXD_GROUP_ADDR
++#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MAX_QUOTA_MASK 0x1FFF0000                // HIF_WMTXD_MAX_QUOTA[28..16]
++#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MAX_QUOTA_SHFT 16
++#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MIN_QUOTA_ADDR WF_PLE_TOP_PG_HIF_WMTXD_GROUP_ADDR
++#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MIN_QUOTA_MASK 0x00001FFF                // HIF_WMTXD_MIN_QUOTA[12..0]
++#define WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MIN_QUOTA_SHFT 0
++
++#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_SRC_CNT_ADDR    WF_PLE_TOP_HIF_WMTXD_PG_INFO_ADDR
++#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_SRC_CNT_MASK    0x1FFF0000                // HIF_WMTXD_SRC_CNT[28..16]
++#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_SRC_CNT_SHFT    16
++#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_RSV_CNT_ADDR    WF_PLE_TOP_HIF_WMTXD_PG_INFO_ADDR
++#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_RSV_CNT_MASK    0x00001FFF                // HIF_WMTXD_RSV_CNT[12..0]
++#define WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_RSV_CNT_SHFT    0
++
++#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MAX_QUOTA_ADDR WF_PLE_TOP_PG_HIF_TXCMD_GROUP_ADDR
++#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MAX_QUOTA_MASK 0x1FFF0000                // HIF_TXCMD_MAX_QUOTA[28..16]
++#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MAX_QUOTA_SHFT 16
++#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MIN_QUOTA_ADDR WF_PLE_TOP_PG_HIF_TXCMD_GROUP_ADDR
++#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MIN_QUOTA_MASK 0x00001FFF                // HIF_TXCMD_MIN_QUOTA[12..0]
++#define WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MIN_QUOTA_SHFT 0
++
++#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_SRC_CNT_ADDR    WF_PLE_TOP_HIF_TXCMD_PG_INFO_ADDR
++#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_SRC_CNT_MASK    0x1FFF0000                // HIF_TXCMD_SRC_CNT[28..16]
++#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_SRC_CNT_SHFT    16
++#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_RSV_CNT_ADDR    WF_PLE_TOP_HIF_TXCMD_PG_INFO_ADDR
++#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_RSV_CNT_MASK    0x00001FFF                // HIF_TXCMD_RSV_CNT[12..0]
++#define WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_RSV_CNT_SHFT    0
++
++#define WF_PLE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_ADDR             WF_PLE_TOP_PG_CPU_GROUP_ADDR
++#define WF_PLE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_MASK             0x1FFF0000                // CPU_MAX_QUOTA[28..16]
++#define WF_PLE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_SHFT             16
++#define WF_PLE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_ADDR             WF_PLE_TOP_PG_CPU_GROUP_ADDR
++#define WF_PLE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_MASK             0x00001FFF                // CPU_MIN_QUOTA[12..0]
++#define WF_PLE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_SHFT             0
++
++#define WF_PLE_TOP_CPU_PG_INFO_CPU_SRC_CNT_ADDR                WF_PLE_TOP_CPU_PG_INFO_ADDR
++#define WF_PLE_TOP_CPU_PG_INFO_CPU_SRC_CNT_MASK                0x1FFF0000                // CPU_SRC_CNT[28..16]
++#define WF_PLE_TOP_CPU_PG_INFO_CPU_SRC_CNT_SHFT                16
++#define WF_PLE_TOP_CPU_PG_INFO_CPU_RSV_CNT_ADDR                WF_PLE_TOP_CPU_PG_INFO_ADDR
++#define WF_PLE_TOP_CPU_PG_INFO_CPU_RSV_CNT_MASK                0x00001FFF                // CPU_RSV_CNT[12..0]
++#define WF_PLE_TOP_CPU_PG_INFO_CPU_RSV_CNT_SHFT                0
++
++#define WF_PLE_TOP_FL_QUE_CTRL_0_EXECUTE_ADDR                  WF_PLE_TOP_FL_QUE_CTRL_0_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_0_EXECUTE_MASK                  0x80000000                // EXECUTE[31]
++#define WF_PLE_TOP_FL_QUE_CTRL_0_EXECUTE_SHFT                  31
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_ADDR                WF_PLE_TOP_FL_QUE_CTRL_0_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_MASK                0x7F000000                // Q_BUF_QID[30..24]
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_SHFT                24
++#define WF_PLE_TOP_FL_QUE_CTRL_0_FL_BUFFER_ADDR_ADDR           WF_PLE_TOP_FL_QUE_CTRL_0_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_0_FL_BUFFER_ADDR_MASK           0x00FFF000                // FL_BUFFER_ADDR[23..12]
++#define WF_PLE_TOP_FL_QUE_CTRL_0_FL_BUFFER_ADDR_SHFT           12
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_WLANID_ADDR             WF_PLE_TOP_FL_QUE_CTRL_0_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_WLANID_MASK             0x00000FFF                // Q_BUF_WLANID[11..0]
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_WLANID_SHFT             0
++
++#define WF_PLE_TOP_FL_QUE_CTRL_1_Q_BUF_TGID_ADDR               WF_PLE_TOP_FL_QUE_CTRL_1_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_1_Q_BUF_TGID_MASK               0xC0000000                // Q_BUF_TGID[31..30]
++#define WF_PLE_TOP_FL_QUE_CTRL_1_Q_BUF_TGID_SHFT               30
++#define WF_PLE_TOP_FL_QUE_CTRL_1_Q_BUF_PID_ADDR                WF_PLE_TOP_FL_QUE_CTRL_1_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_1_Q_BUF_PID_MASK                0x30000000                // Q_BUF_PID[29..28]
++#define WF_PLE_TOP_FL_QUE_CTRL_1_Q_BUF_PID_SHFT                28
++
++#define WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_ADDR           WF_PLE_TOP_FL_QUE_CTRL_2_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_MASK           0x1FFF0000                // QUEUE_TAIL_FID[28..16]
++#define WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_SHFT           16
++#define WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_ADDR           WF_PLE_TOP_FL_QUE_CTRL_2_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_MASK           0x00001FFF                // QUEUE_HEAD_FID[12..0]
++#define WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_SHFT           0
++
++#define WF_PLE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_ADDR            WF_PLE_TOP_FL_QUE_CTRL_3_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_MASK            0x00001FFF                // QUEUE_PKT_NUM[12..0]
++#define WF_PLE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_SHFT            0
++
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_TGID_ADDR               WF_PLE_TOP_FL_QUE_CTRL_0_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_TGID_MASK               0x00300000                // Q_BUF_TGID[21..20]
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_TGID_SHFT               20
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_ADDR                WF_PLE_TOP_FL_QUE_CTRL_0_ADDR
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_MASK                0x00030000                // Q_BUF_PID[17..16]
++#define WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_SHFT                16
++/* PSE */
++#define WF_PSE_TOP_BASE                                        0x820c8000
++
++#define WF_PSE_TOP_PBUF_CTRL_ADDR                              (WF_PSE_TOP_BASE + 0x04) // 8004
++#define WF_PSE_TOP_QUEUE_EMPTY_ADDR                            (WF_PSE_TOP_BASE + 0xB0) // 80B0
++#define WF_PSE_TOP_QUEUE_EMPTY_1_ADDR                          (WF_PSE_TOP_BASE + 0xBC) // 80BC
++#define WF_PSE_TOP_PG_HIF0_GROUP_ADDR                          (WF_PSE_TOP_BASE + 0x110) // 8110
++#define WF_PSE_TOP_PG_HIF1_GROUP_ADDR                          (WF_PSE_TOP_BASE + 0x114) // 8114
++#define WF_PSE_TOP_PG_CPU_GROUP_ADDR                           (WF_PSE_TOP_BASE + 0x118) // 8118
++#define WF_PSE_TOP_PG_PLE_GROUP_ADDR                           (WF_PSE_TOP_BASE + 0x11C) // 811C
++#define WF_PSE_TOP_PG_PLE1_GROUP_ADDR                          (WF_PSE_TOP_BASE + 0x120) // 8120
++#define WF_PSE_TOP_PG_LMAC0_GROUP_ADDR                         (WF_PSE_TOP_BASE + 0x124) // 8124
++#define WF_PSE_TOP_PG_LMAC1_GROUP_ADDR                         (WF_PSE_TOP_BASE + 0x128) // 8128
++#define WF_PSE_TOP_PG_LMAC2_GROUP_ADDR                         (WF_PSE_TOP_BASE + 0x12C) // 812C
++#define WF_PSE_TOP_PG_LMAC3_GROUP_ADDR                         (WF_PSE_TOP_BASE + 0x130) // 8130
++#define WF_PSE_TOP_PG_MDP_GROUP_ADDR                           (WF_PSE_TOP_BASE + 0x134) // 8134
++#define WF_PSE_TOP_PG_MDP2_GROUP_ADDR                          (WF_PSE_TOP_BASE + 0x13C) // 813C
++#define WF_PSE_TOP_PG_HIF2_GROUP_ADDR                          (WF_PSE_TOP_BASE + 0x140) // 8140
++#define WF_PSE_TOP_PG_MDP3_GROUP_ADDR                          (WF_PSE_TOP_BASE + 0x144) // 8144
++#define WF_PSE_TOP_HIF0_PG_INFO_ADDR                           (WF_PSE_TOP_BASE + 0x150) // 8150
++#define WF_PSE_TOP_HIF1_PG_INFO_ADDR                           (WF_PSE_TOP_BASE + 0x154) // 8154
++#define WF_PSE_TOP_CPU_PG_INFO_ADDR                            (WF_PSE_TOP_BASE + 0x158) // 8158
++#define WF_PSE_TOP_PLE_PG_INFO_ADDR                            (WF_PSE_TOP_BASE + 0x15C) // 815C
++#define WF_PSE_TOP_PLE1_PG_INFO_ADDR                           (WF_PSE_TOP_BASE + 0x160) // 8160
++#define WF_PSE_TOP_LMAC0_PG_INFO_ADDR                          (WF_PSE_TOP_BASE + 0x164) // 8164
++#define WF_PSE_TOP_LMAC1_PG_INFO_ADDR                          (WF_PSE_TOP_BASE + 0x168) // 8168
++#define WF_PSE_TOP_LMAC2_PG_INFO_ADDR                          (WF_PSE_TOP_BASE + 0x16C) // 816C
++#define WF_PSE_TOP_LMAC3_PG_INFO_ADDR                          (WF_PSE_TOP_BASE + 0x170) // 8170
++#define WF_PSE_TOP_MDP_PG_INFO_ADDR                            (WF_PSE_TOP_BASE + 0x174) // 8174
++#define WF_PSE_TOP_MDP2_PG_INFO_ADDR                           (WF_PSE_TOP_BASE + 0x17C) // 817C
++#define WF_PSE_TOP_HIF2_PG_INFO_ADDR                           (WF_PSE_TOP_BASE + 0x180) // 8180
++#define WF_PSE_TOP_MDP3_PG_INFO_ADDR                           (WF_PSE_TOP_BASE + 0x184) // 8184
++#define WF_PSE_TOP_FL_QUE_CTRL_0_ADDR                          (WF_PSE_TOP_BASE + 0x1B0) // 81B0
++#define WF_PSE_TOP_FL_QUE_CTRL_1_ADDR                          (WF_PSE_TOP_BASE + 0x1B4) // 81B4
++#define WF_PSE_TOP_FL_QUE_CTRL_2_ADDR                          (WF_PSE_TOP_BASE + 0x1B8) // 81B8
++#define WF_PSE_TOP_FL_QUE_CTRL_3_ADDR                          (WF_PSE_TOP_BASE + 0x1BC) // 81BC
++#define WF_PSE_TOP_FREEPG_CNT_ADDR                             (WF_PSE_TOP_BASE + 0x3A0) // 83A0
++#define WF_PSE_TOP_FREEPG_HEAD_TAIL_ADDR                       (WF_PSE_TOP_BASE + 0x3A4) // 83A4
++
++#define WF_PSE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_ADDR                WF_PSE_TOP_PBUF_CTRL_ADDR
++#define WF_PSE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_MASK                0x80000000                // PAGE_SIZE_CFG[31]
++#define WF_PSE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_SHFT                31
++#define WF_PSE_TOP_PBUF_CTRL_PBUF_OFFSET_ADDR                  WF_PSE_TOP_PBUF_CTRL_ADDR
++#define WF_PSE_TOP_PBUF_CTRL_PBUF_OFFSET_MASK                  0x07FE0000                // PBUF_OFFSET[26..17]
++#define WF_PSE_TOP_PBUF_CTRL_PBUF_OFFSET_SHFT                  17
++#define WF_PSE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_ADDR               WF_PSE_TOP_PBUF_CTRL_ADDR
++#define WF_PSE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_MASK               0x00001FFF                // TOTAL_PAGE_NUM[12..0]
++#define WF_PSE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_SHFT               0
++
++#define WF_PSE_TOP_QUEUE_EMPTY_RLS_Q_EMTPY_ADDR                WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_RLS_Q_EMTPY_MASK                0x80000000                // RLS_Q_EMTPY[31]
++#define WF_PSE_TOP_QUEUE_EMPTY_RLS_Q_EMTPY_SHFT                31
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q4_EMPTY_ADDR               WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q4_EMPTY_MASK               0x10000000                // CPU_Q4_EMPTY[28]
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q4_EMPTY_SHFT               28
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC1_QUEUE_EMPTY_ADDR     WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC1_QUEUE_EMPTY_MASK     0x08000000                // MDP_RXIOC1_QUEUE_EMPTY[27]
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC1_QUEUE_EMPTY_SHFT     27
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC1_QUEUE_EMPTY_ADDR     WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC1_QUEUE_EMPTY_MASK     0x04000000                // MDP_TXIOC1_QUEUE_EMPTY[26]
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC1_QUEUE_EMPTY_SHFT     26
++#define WF_PSE_TOP_QUEUE_EMPTY_SEC_TX1_QUEUE_EMPTY_ADDR        WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_SEC_TX1_QUEUE_EMPTY_MASK        0x02000000                // SEC_TX1_QUEUE_EMPTY[25]
++#define WF_PSE_TOP_QUEUE_EMPTY_SEC_TX1_QUEUE_EMPTY_SHFT        25
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TX1_QUEUE_EMPTY_ADDR        WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TX1_QUEUE_EMPTY_MASK        0x01000000                // MDP_TX1_QUEUE_EMPTY[24]
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TX1_QUEUE_EMPTY_SHFT        24
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC_QUEUE_EMPTY_ADDR      WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC_QUEUE_EMPTY_MASK      0x00800000                // MDP_RXIOC_QUEUE_EMPTY[23]
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC_QUEUE_EMPTY_SHFT      23
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC_QUEUE_EMPTY_ADDR      WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC_QUEUE_EMPTY_MASK      0x00400000                // MDP_TXIOC_QUEUE_EMPTY[22]
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC_QUEUE_EMPTY_SHFT      22
++#define WF_PSE_TOP_QUEUE_EMPTY_SFD_PARK_QUEUE_EMPTY_ADDR       WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_SFD_PARK_QUEUE_EMPTY_MASK       0x00200000                // SFD_PARK_QUEUE_EMPTY[21]
++#define WF_PSE_TOP_QUEUE_EMPTY_SFD_PARK_QUEUE_EMPTY_SHFT       21
++#define WF_PSE_TOP_QUEUE_EMPTY_SEC_RX_QUEUE_EMPTY_ADDR         WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_SEC_RX_QUEUE_EMPTY_MASK         0x00100000                // SEC_RX_QUEUE_EMPTY[20]
++#define WF_PSE_TOP_QUEUE_EMPTY_SEC_RX_QUEUE_EMPTY_SHFT         20
++#define WF_PSE_TOP_QUEUE_EMPTY_SEC_TX_QUEUE_EMPTY_ADDR         WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_SEC_TX_QUEUE_EMPTY_MASK         0x00080000                // SEC_TX_QUEUE_EMPTY[19]
++#define WF_PSE_TOP_QUEUE_EMPTY_SEC_TX_QUEUE_EMPTY_SHFT         19
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RX_QUEUE_EMPTY_ADDR         WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RX_QUEUE_EMPTY_MASK         0x00040000                // MDP_RX_QUEUE_EMPTY[18]
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_RX_QUEUE_EMPTY_SHFT         18
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TX_QUEUE_EMPTY_ADDR         WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TX_QUEUE_EMPTY_MASK         0x00020000                // MDP_TX_QUEUE_EMPTY[17]
++#define WF_PSE_TOP_QUEUE_EMPTY_MDP_TX_QUEUE_EMPTY_SHFT         17
++#define WF_PSE_TOP_QUEUE_EMPTY_LMAC_TX_QUEUE_EMPTY_ADDR        WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_LMAC_TX_QUEUE_EMPTY_MASK        0x00010000                // LMAC_TX_QUEUE_EMPTY[16]
++#define WF_PSE_TOP_QUEUE_EMPTY_LMAC_TX_QUEUE_EMPTY_SHFT        16
++
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q3_EMPTY_ADDR               WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q3_EMPTY_MASK               0x00000008                // CPU_Q3_EMPTY[3]
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q3_EMPTY_SHFT               3
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q2_EMPTY_ADDR               WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q2_EMPTY_MASK               0x00000004                // CPU_Q2_EMPTY[2]
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q2_EMPTY_SHFT               2
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q1_EMPTY_ADDR               WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q1_EMPTY_MASK               0x00000002                // CPU_Q1_EMPTY[1]
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q1_EMPTY_SHFT               1
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q0_EMPTY_ADDR               WF_PSE_TOP_QUEUE_EMPTY_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q0_EMPTY_MASK               0x00000001                // CPU_Q0_EMPTY[0]
++#define WF_PSE_TOP_QUEUE_EMPTY_CPU_Q0_EMPTY_SHFT               0
++
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_13_EMPTY_ADDR             WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_13_EMPTY_MASK             0x20000000                // HIF_13_EMPTY[29]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_13_EMPTY_SHFT             29
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_12_EMPTY_ADDR             WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_12_EMPTY_MASK             0x10000000                // HIF_12_EMPTY[28]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_12_EMPTY_SHFT             28
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_11_EMPTY_ADDR             WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_11_EMPTY_MASK             0x08000000                // HIF_11_EMPTY[27]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_11_EMPTY_SHFT             27
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_10_EMPTY_ADDR             WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_10_EMPTY_MASK             0x04000000                // HIF_10_EMPTY[26]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_10_EMPTY_SHFT             26
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_9_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_9_EMPTY_MASK              0x02000000                // HIF_9_EMPTY[25]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_9_EMPTY_SHFT              25
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_8_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_8_EMPTY_MASK              0x01000000                // HIF_8_EMPTY[24]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_8_EMPTY_SHFT              24
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_7_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_7_EMPTY_MASK              0x00800000                // HIF_7_EMPTY[23]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_7_EMPTY_SHFT              23
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_6_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_6_EMPTY_MASK              0x00400000                // HIF_6_EMPTY[22]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_6_EMPTY_SHFT              22
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_5_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_5_EMPTY_MASK              0x00200000                // HIF_5_EMPTY[21]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_5_EMPTY_SHFT              21
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_4_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_4_EMPTY_MASK              0x00100000                // HIF_4_EMPTY[20]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_4_EMPTY_SHFT              20
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_3_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_3_EMPTY_MASK              0x00080000                // HIF_3_EMPTY[19]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_3_EMPTY_SHFT              19
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_2_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_2_EMPTY_MASK              0x00040000                // HIF_2_EMPTY[18]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_2_EMPTY_SHFT              18
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_1_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_1_EMPTY_MASK              0x00020000                // HIF_1_EMPTY[17]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_1_EMPTY_SHFT              17
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_0_EMPTY_ADDR              WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_0_EMPTY_MASK              0x00010000                // HIF_0_EMPTY[16]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_HIF_0_EMPTY_SHFT              16
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC3_QUEUE_EMPTY_ADDR   WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC3_QUEUE_EMPTY_MASK   0x00008000                // MDP_RXIOC3_QUEUE_EMPTY[15]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC3_QUEUE_EMPTY_SHFT   15
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC2_QUEUE_EMPTY_ADDR   WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC2_QUEUE_EMPTY_MASK   0x00000800                // MDP_RXIOC2_QUEUE_EMPTY[11]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC2_QUEUE_EMPTY_SHFT   11
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TXIOC2_QUEUE_EMPTY_ADDR   WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TXIOC2_QUEUE_EMPTY_MASK   0x00000400                // MDP_TXIOC2_QUEUE_EMPTY[10]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TXIOC2_QUEUE_EMPTY_SHFT   10
++#define WF_PSE_TOP_QUEUE_EMPTY_1_SEC_TX2_QUEUE_EMPTY_ADDR      WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_SEC_TX2_QUEUE_EMPTY_MASK      0x00000200                // SEC_TX2_QUEUE_EMPTY[9]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_SEC_TX2_QUEUE_EMPTY_SHFT      9
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TX2_QUEUE_EMPTY_ADDR      WF_PSE_TOP_QUEUE_EMPTY_1_ADDR
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TX2_QUEUE_EMPTY_MASK      0x00000100                // MDP_TX2_QUEUE_EMPTY[8]
++#define WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TX2_QUEUE_EMPTY_SHFT      8
++
++#define WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MAX_QUOTA_ADDR           WF_PSE_TOP_PG_HIF0_GROUP_ADDR
++#define WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MAX_QUOTA_MASK           0x1FFF0000                // HIF0_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MAX_QUOTA_SHFT           16
++#define WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MIN_QUOTA_ADDR           WF_PSE_TOP_PG_HIF0_GROUP_ADDR
++#define WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MIN_QUOTA_MASK           0x00001FFF                // HIF0_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MIN_QUOTA_SHFT           0
++
++
++#define WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MAX_QUOTA_ADDR           WF_PSE_TOP_PG_HIF1_GROUP_ADDR
++#define WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MAX_QUOTA_MASK           0x1FFF0000                // HIF1_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MAX_QUOTA_SHFT           16
++#define WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MIN_QUOTA_ADDR           WF_PSE_TOP_PG_HIF1_GROUP_ADDR
++#define WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MIN_QUOTA_MASK           0x00001FFF                // HIF1_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MIN_QUOTA_SHFT           0
++
++#define WF_PSE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_ADDR             WF_PSE_TOP_PG_CPU_GROUP_ADDR
++#define WF_PSE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_MASK             0x1FFF0000                // CPU_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_SHFT             16
++#define WF_PSE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_ADDR             WF_PSE_TOP_PG_CPU_GROUP_ADDR
++#define WF_PSE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_MASK             0x00001FFF                // CPU_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_SHFT             0
++
++#define WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_ADDR             WF_PSE_TOP_PG_PLE_GROUP_ADDR
++#define WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_MASK             0x1FFF0000                // PLE_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_SHFT             16
++#define WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_ADDR             WF_PSE_TOP_PG_PLE_GROUP_ADDR
++#define WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_MASK             0x00001FFF                // PLE_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_SHFT             0
++
++#define WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MAX_QUOTA_ADDR         WF_PSE_TOP_PG_LMAC0_GROUP_ADDR
++#define WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MAX_QUOTA_MASK         0x1FFF0000                // LMAC0_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MAX_QUOTA_SHFT         16
++#define WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MIN_QUOTA_ADDR         WF_PSE_TOP_PG_LMAC0_GROUP_ADDR
++#define WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MIN_QUOTA_MASK         0x00001FFF                // LMAC0_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MIN_QUOTA_SHFT         0
++
++#define WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MAX_QUOTA_ADDR         WF_PSE_TOP_PG_LMAC1_GROUP_ADDR
++#define WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MAX_QUOTA_MASK         0x1FFF0000                // LMAC1_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MAX_QUOTA_SHFT         16
++#define WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MIN_QUOTA_ADDR         WF_PSE_TOP_PG_LMAC1_GROUP_ADDR
++#define WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MIN_QUOTA_MASK         0x00001FFF                // LMAC1_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MIN_QUOTA_SHFT         0
++
++#define WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MAX_QUOTA_ADDR         WF_PSE_TOP_PG_LMAC2_GROUP_ADDR
++#define WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MAX_QUOTA_MASK         0x1FFF0000                // LMAC2_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MAX_QUOTA_SHFT         16
++#define WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MIN_QUOTA_ADDR         WF_PSE_TOP_PG_LMAC2_GROUP_ADDR
++#define WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MIN_QUOTA_MASK         0x00001FFF                // LMAC2_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MIN_QUOTA_SHFT         0
++
++#define WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MAX_QUOTA_ADDR         WF_PSE_TOP_PG_LMAC3_GROUP_ADDR
++#define WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MAX_QUOTA_MASK         0x1FFF0000                // LMAC3_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MAX_QUOTA_SHFT         16
++#define WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MIN_QUOTA_ADDR         WF_PSE_TOP_PG_LMAC3_GROUP_ADDR
++#define WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MIN_QUOTA_MASK         0x00001FFF                // LMAC3_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MIN_QUOTA_SHFT         0
++
++#define WF_PSE_TOP_PG_MDP_GROUP_MDP_MAX_QUOTA_ADDR             WF_PSE_TOP_PG_MDP_GROUP_ADDR
++#define WF_PSE_TOP_PG_MDP_GROUP_MDP_MAX_QUOTA_MASK             0x1FFF0000                // MDP_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_MDP_GROUP_MDP_MAX_QUOTA_SHFT             16
++#define WF_PSE_TOP_PG_MDP_GROUP_MDP_MIN_QUOTA_ADDR             WF_PSE_TOP_PG_MDP_GROUP_ADDR
++#define WF_PSE_TOP_PG_MDP_GROUP_MDP_MIN_QUOTA_MASK             0x00001FFF                // MDP_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_MDP_GROUP_MDP_MIN_QUOTA_SHFT             0
++
++#define WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MAX_QUOTA_ADDR           WF_PSE_TOP_PG_MDP2_GROUP_ADDR
++#define WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MAX_QUOTA_MASK           0x1FFF0000                // MDP2_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MAX_QUOTA_SHFT           16
++#define WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MIN_QUOTA_ADDR           WF_PSE_TOP_PG_MDP2_GROUP_ADDR
++#define WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MIN_QUOTA_MASK           0x00001FFF                // MDP2_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MIN_QUOTA_SHFT           0
++
++#define WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MAX_QUOTA_ADDR           WF_PSE_TOP_PG_HIF2_GROUP_ADDR
++#define WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MAX_QUOTA_MASK           0x1FFF0000                // HIF2_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MAX_QUOTA_SHFT           16
++#define WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MIN_QUOTA_ADDR           WF_PSE_TOP_PG_HIF2_GROUP_ADDR
++#define WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MIN_QUOTA_MASK           0x00001FFF                // HIF2_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MIN_QUOTA_SHFT           0
++
++#define WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MAX_QUOTA_ADDR           WF_PSE_TOP_PG_MDP3_GROUP_ADDR
++#define WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MAX_QUOTA_MASK           0x1FFF0000                // MDP3_MAX_QUOTA[28..16]
++#define WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MAX_QUOTA_SHFT           16
++#define WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MIN_QUOTA_ADDR           WF_PSE_TOP_PG_MDP3_GROUP_ADDR
++#define WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MIN_QUOTA_MASK           0x00001FFF                // MDP3_MIN_QUOTA[12..0]
++#define WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MIN_QUOTA_SHFT           0
++
++#define WF_PSE_TOP_HIF0_PG_INFO_HIF0_SRC_CNT_ADDR              WF_PSE_TOP_HIF0_PG_INFO_ADDR
++#define WF_PSE_TOP_HIF0_PG_INFO_HIF0_SRC_CNT_MASK              0x1FFF0000                // HIF0_SRC_CNT[28..16]
++#define WF_PSE_TOP_HIF0_PG_INFO_HIF0_SRC_CNT_SHFT              16
++#define WF_PSE_TOP_HIF0_PG_INFO_HIF0_RSV_CNT_ADDR              WF_PSE_TOP_HIF0_PG_INFO_ADDR
++#define WF_PSE_TOP_HIF0_PG_INFO_HIF0_RSV_CNT_MASK              0x00001FFF                // HIF0_RSV_CNT[12..0]
++#define WF_PSE_TOP_HIF0_PG_INFO_HIF0_RSV_CNT_SHFT              0
++
++#define WF_PSE_TOP_HIF1_PG_INFO_HIF1_SRC_CNT_ADDR              WF_PSE_TOP_HIF1_PG_INFO_ADDR
++#define WF_PSE_TOP_HIF1_PG_INFO_HIF1_SRC_CNT_MASK              0x1FFF0000                // HIF1_SRC_CNT[28..16]
++#define WF_PSE_TOP_HIF1_PG_INFO_HIF1_SRC_CNT_SHFT              16
++#define WF_PSE_TOP_HIF1_PG_INFO_HIF1_RSV_CNT_ADDR              WF_PSE_TOP_HIF1_PG_INFO_ADDR
++#define WF_PSE_TOP_HIF1_PG_INFO_HIF1_RSV_CNT_MASK              0x00001FFF                // HIF1_RSV_CNT[12..0]
++#define WF_PSE_TOP_HIF1_PG_INFO_HIF1_RSV_CNT_SHFT              0
++
++#define WF_PSE_TOP_CPU_PG_INFO_CPU_SRC_CNT_ADDR                WF_PSE_TOP_CPU_PG_INFO_ADDR
++#define WF_PSE_TOP_CPU_PG_INFO_CPU_SRC_CNT_MASK                0x1FFF0000                // CPU_SRC_CNT[28..16]
++#define WF_PSE_TOP_CPU_PG_INFO_CPU_SRC_CNT_SHFT                16
++#define WF_PSE_TOP_CPU_PG_INFO_CPU_RSV_CNT_ADDR                WF_PSE_TOP_CPU_PG_INFO_ADDR
++#define WF_PSE_TOP_CPU_PG_INFO_CPU_RSV_CNT_MASK                0x00001FFF                // CPU_RSV_CNT[12..0]
++#define WF_PSE_TOP_CPU_PG_INFO_CPU_RSV_CNT_SHFT                0
++
++#define WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_ADDR                WF_PSE_TOP_PLE_PG_INFO_ADDR
++#define WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_MASK                0x1FFF0000                // PLE_SRC_CNT[28..16]
++#define WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_SHFT                16
++#define WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_ADDR                WF_PSE_TOP_PLE_PG_INFO_ADDR
++#define WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_MASK                0x00001FFF                // PLE_RSV_CNT[12..0]
++#define WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_SHFT                0
++
++#define WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_SRC_CNT_ADDR            WF_PSE_TOP_LMAC0_PG_INFO_ADDR
++#define WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_SRC_CNT_MASK            0x1FFF0000                // LMAC0_SRC_CNT[28..16]
++#define WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_SRC_CNT_SHFT            16
++#define WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_RSV_CNT_ADDR            WF_PSE_TOP_LMAC0_PG_INFO_ADDR
++#define WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_RSV_CNT_MASK            0x00001FFF                // LMAC0_RSV_CNT[12..0]
++#define WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_RSV_CNT_SHFT            0
++
++#define WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_SRC_CNT_ADDR            WF_PSE_TOP_LMAC1_PG_INFO_ADDR
++#define WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_SRC_CNT_MASK            0x1FFF0000                // LMAC1_SRC_CNT[28..16]
++#define WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_SRC_CNT_SHFT            16
++#define WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_RSV_CNT_ADDR            WF_PSE_TOP_LMAC1_PG_INFO_ADDR
++#define WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_RSV_CNT_MASK            0x00001FFF                // LMAC1_RSV_CNT[12..0]
++#define WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_RSV_CNT_SHFT            0
++
++#define WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_SRC_CNT_ADDR            WF_PSE_TOP_LMAC2_PG_INFO_ADDR
++#define WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_SRC_CNT_MASK            0x1FFF0000                // LMAC2_SRC_CNT[28..16]
++#define WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_SRC_CNT_SHFT            16
++#define WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_RSV_CNT_ADDR            WF_PSE_TOP_LMAC2_PG_INFO_ADDR
++#define WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_RSV_CNT_MASK            0x00001FFF                // LMAC2_RSV_CNT[12..0]
++#define WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_RSV_CNT_SHFT            0
++
++#define WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_SRC_CNT_ADDR            WF_PSE_TOP_LMAC3_PG_INFO_ADDR
++#define WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_SRC_CNT_MASK            0x1FFF0000                // LMAC3_SRC_CNT[28..16]
++#define WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_SRC_CNT_SHFT            16
++#define WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_RSV_CNT_ADDR            WF_PSE_TOP_LMAC3_PG_INFO_ADDR
++#define WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_RSV_CNT_MASK            0x00001FFF                // LMAC3_RSV_CNT[12..0]
++#define WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_RSV_CNT_SHFT            0
++
++#define WF_PSE_TOP_MDP_PG_INFO_MDP_SRC_CNT_ADDR                WF_PSE_TOP_MDP_PG_INFO_ADDR
++#define WF_PSE_TOP_MDP_PG_INFO_MDP_SRC_CNT_MASK                0x1FFF0000                // MDP_SRC_CNT[28..16]
++#define WF_PSE_TOP_MDP_PG_INFO_MDP_SRC_CNT_SHFT                16
++#define WF_PSE_TOP_MDP_PG_INFO_MDP_RSV_CNT_ADDR                WF_PSE_TOP_MDP_PG_INFO_ADDR
++#define WF_PSE_TOP_MDP_PG_INFO_MDP_RSV_CNT_MASK                0x00001FFF                // MDP_RSV_CNT[12..0]
++#define WF_PSE_TOP_MDP_PG_INFO_MDP_RSV_CNT_SHFT                0
++
++#define WF_PSE_TOP_MDP2_PG_INFO_MDP2_SRC_CNT_ADDR              WF_PSE_TOP_MDP2_PG_INFO_ADDR
++#define WF_PSE_TOP_MDP2_PG_INFO_MDP2_SRC_CNT_MASK              0x1FFF0000                // MDP2_SRC_CNT[28..16]
++#define WF_PSE_TOP_MDP2_PG_INFO_MDP2_SRC_CNT_SHFT              16
++#define WF_PSE_TOP_MDP2_PG_INFO_MDP2_RSV_CNT_ADDR              WF_PSE_TOP_MDP2_PG_INFO_ADDR
++#define WF_PSE_TOP_MDP2_PG_INFO_MDP2_RSV_CNT_MASK              0x00001FFF                // MDP2_RSV_CNT[12..0]
++#define WF_PSE_TOP_MDP2_PG_INFO_MDP2_RSV_CNT_SHFT              0
++
++#define WF_PSE_TOP_HIF2_PG_INFO_HIF2_SRC_CNT_ADDR              WF_PSE_TOP_HIF2_PG_INFO_ADDR
++#define WF_PSE_TOP_HIF2_PG_INFO_HIF2_SRC_CNT_MASK              0x1FFF0000                // HIF2_SRC_CNT[28..16]
++#define WF_PSE_TOP_HIF2_PG_INFO_HIF2_SRC_CNT_SHFT              16
++#define WF_PSE_TOP_HIF2_PG_INFO_HIF2_RSV_CNT_ADDR              WF_PSE_TOP_HIF2_PG_INFO_ADDR
++#define WF_PSE_TOP_HIF2_PG_INFO_HIF2_RSV_CNT_MASK              0x00001FFF                // HIF2_RSV_CNT[12..0]
++#define WF_PSE_TOP_HIF2_PG_INFO_HIF2_RSV_CNT_SHFT              0
++
++#define WF_PSE_TOP_MDP3_PG_INFO_MDP3_SRC_CNT_ADDR              WF_PSE_TOP_MDP3_PG_INFO_ADDR
++#define WF_PSE_TOP_MDP3_PG_INFO_MDP3_SRC_CNT_MASK              0x1FFF0000                // MDP3_SRC_CNT[28..16]
++#define WF_PSE_TOP_MDP3_PG_INFO_MDP3_SRC_CNT_SHFT              16
++#define WF_PSE_TOP_MDP3_PG_INFO_MDP3_RSV_CNT_ADDR              WF_PSE_TOP_MDP3_PG_INFO_ADDR
++#define WF_PSE_TOP_MDP3_PG_INFO_MDP3_RSV_CNT_MASK              0x00001FFF                // MDP3_RSV_CNT[12..0]
++#define WF_PSE_TOP_MDP3_PG_INFO_MDP3_RSV_CNT_SHFT              0
++
++#define WF_PSE_TOP_FL_QUE_CTRL_0_EXECUTE_ADDR                  WF_PSE_TOP_FL_QUE_CTRL_0_ADDR
++#define WF_PSE_TOP_FL_QUE_CTRL_0_EXECUTE_MASK                  0x80000000                // EXECUTE[31]
++#define WF_PSE_TOP_FL_QUE_CTRL_0_EXECUTE_SHFT                  31
++#define WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_ADDR                WF_PSE_TOP_FL_QUE_CTRL_0_ADDR
++#define WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_MASK                0x7F000000                // Q_BUF_QID[30..24]
++#define WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_SHFT                24
++#define WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_SHFT                16
++
++#define WF_PSE_TOP_FL_QUE_CTRL_1_Q_BUF_PID_ADDR                WF_PSE_TOP_FL_QUE_CTRL_1_ADDR
++#define WF_PSE_TOP_FL_QUE_CTRL_1_Q_BUF_PID_MASK                0x30000000                // Q_BUF_PID[29..28]
++#define WF_PSE_TOP_FL_QUE_CTRL_1_Q_BUF_PID_SHFT                28
++
++#define WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_ADDR           WF_PSE_TOP_FL_QUE_CTRL_2_ADDR
++#define WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_MASK           0x1FFF0000                // QUEUE_TAIL_FID[28..16]
++#define WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_SHFT           16
++#define WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_ADDR           WF_PSE_TOP_FL_QUE_CTRL_2_ADDR
++#define WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_MASK           0x00001FFF                // QUEUE_HEAD_FID[12..0]
++#define WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_SHFT           0
++
++#define WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PAGE_NUM_ADDR           WF_PSE_TOP_FL_QUE_CTRL_3_ADDR
++#define WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PAGE_NUM_MASK           0x00FFF000                // QUEUE_PAGE_NUM[23..12]
++#define WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PAGE_NUM_SHFT           12
++#define WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_ADDR            WF_PSE_TOP_FL_QUE_CTRL_3_ADDR
++#define WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_MASK            0x00001FFF                // QUEUE_PKT_NUM[12..0]
++#define WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_SHFT            0
++
++#define WF_PSE_TOP_FREEPG_CNT_FFA_CNT_ADDR                     WF_PSE_TOP_FREEPG_CNT_ADDR
++#define WF_PSE_TOP_FREEPG_CNT_FFA_CNT_MASK                     0x1FFF0000                // FFA_CNT[28..16]
++#define WF_PSE_TOP_FREEPG_CNT_FFA_CNT_SHFT                     16
++#define WF_PSE_TOP_FREEPG_CNT_FREEPG_CNT_ADDR                  WF_PSE_TOP_FREEPG_CNT_ADDR
++#define WF_PSE_TOP_FREEPG_CNT_FREEPG_CNT_MASK                  0x00001FFF                // FREEPG_CNT[12..0]
++#define WF_PSE_TOP_FREEPG_CNT_FREEPG_CNT_SHFT                  0
++
++#define WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_ADDR           WF_PSE_TOP_FREEPG_HEAD_TAIL_ADDR
++#define WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_MASK           0x1FFF0000                // FREEPG_TAIL[28..16]
++#define WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_SHFT           16
++#define WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_ADDR           WF_PSE_TOP_FREEPG_HEAD_TAIL_ADDR
++#define WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_MASK           0x00001FFF                // FREEPG_HEAD[12..0]
++#define WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_SHFT           0
++
++/* RXD */
++enum {
++	BMAC_GROUP_VLD_1 = 0x01,
++	BMAC_GROUP_VLD_2 = 0x02,
++	BMAC_GROUP_VLD_3 = 0x04,
++	BMAC_GROUP_VLD_4 = 0x08,
++	BMAC_GROUP_VLD_5 = 0x10,
++};
++
+ #endif
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index cc1224ed..c7f5b56e 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -3187,6 +3187,1038 @@ mt7996_thermal_recal_set(void *data, u64 val)
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_thermal_recal, NULL,
+ 			 mt7996_thermal_recal_set, "%llu\n");
+ 
++static int
++mt7996_reset_counter(void *data, u64 val)
++{
++	struct mt7996_dev *dev = data;
++	struct mt76_dev *mdev = &dev->mt76;
++	struct mt76_wcid *wcid;
++	int ret;
++
++	/* Reset read-clear counters in FW and WTBL. */
++	ret = mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_ADM_STAT);
++	if (ret)
++		return ret;
++
++	ret = mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_MSDU_COUNT);
++	if (ret)
++		return ret;
++
++	ret = mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_AIRTIME);
++	if (ret)
++		return ret;
++
++	/* Reset counters in MT76. */
++	rcu_read_lock();
++	wcid = rcu_dereference(dev->mt76.wcid[dev->wlan_idx]);
++	if (wcid)
++		memset(&wcid->stats, 0, sizeof(struct mt76_sta_stats));
++	else
++		ret = -EINVAL;
++	rcu_read_unlock();
++
++	return ret;
++}
++DEFINE_DEBUGFS_ATTRIBUTE(fops_reset_counter, NULL, mt7996_reset_counter, "%llu\n");
++
++void mt7996_packet_log_to_host(struct mt7996_dev *dev, const void *data, int len, int type, int des_len)
++{
++	struct bin_debug_hdr *hdr;
++	char *buf;
++
++	if (len > 1500 - sizeof(*hdr))
++	len = 1500 - sizeof(*hdr);
++
++	buf = kzalloc(sizeof(*hdr) + len, GFP_KERNEL);
++	if (!buf)
++		return;
++
++	hdr = (struct bin_debug_hdr *)buf;
++	hdr->magic_num = cpu_to_le32(PKT_BIN_DEBUG_MAGIC);
++	hdr->serial_id = cpu_to_le16(dev->fw_debug_seq++);
++	hdr->msg_type = cpu_to_le16(type);
++	hdr->len = cpu_to_le16(len);
++	hdr->des_len = cpu_to_le16(des_len);
++
++	memcpy(buf + sizeof(*hdr), data, len);
++
++	mt7996_debugfs_rx_log(dev, buf, sizeof(*hdr) + len);
++	kfree(buf);
++}
++
++static int mt7996_rx_token_read(struct seq_file *s, void *data)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	int id, count = 0;
++	struct mt76_rxwi_cache *r;
++
++	seq_printf(s, "Rx cut through token:\n");
++	spin_lock_bh(&dev->mt76.rx_token_lock);
++	idr_for_each_entry(&dev->mt76.rx_token, r, id) {
++		count++;
++	}
++	seq_printf(s, "\ttotal:%8d used:%8d\n",
++		   dev->mt76.rx_token_size, count);
++	spin_unlock_bh(&dev->mt76.rx_token_lock);
++
++	return 0;
++}
++
++/* AMSDU SETTING */
++static ssize_t mt7996_amsdu_algo_write(struct file *file,
++				   const char __user *user_buf,
++				   size_t count,
++				   loff_t *ppos)
++{
++	struct mt7996_dev *dev = file->private_data;
++	char buf[100];
++	int ret;
++	struct {
++		u8 _rsv[4];
++
++		u16 tag;
++		u16 len;
++
++		u16 wlan_idx;
++		u8 algo_en;
++		u8 rsv[1];
++	} __packed data = {
++		.tag = cpu_to_le16(UNI_MEC_AMSDU_ALGO_EN_STA),
++		.len = cpu_to_le16(sizeof(data) - 4),
++	};
++
++	if (count >= sizeof(buf))
++		return -EINVAL;
++
++	if (copy_from_user(buf, user_buf, count))
++		return -EFAULT;
++
++	if (count && buf[count - 1] == '\n')
++		buf[count - 1] = '\0';
++	else
++		buf[count] = '\0';
++
++	if (sscanf(buf, "%hu %hhu", &data.wlan_idx, &data.algo_en) != 2)
++		return -EINVAL;
++
++	if (data.wlan_idx >= mt7996_wtbl_size(dev))
++		return -EINVAL;
++
++	ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MEC), &data,
++				sizeof(data), true);
++	if (ret)
++		return -EINVAL;
++
++	return count;
++}
++static const struct file_operations fops_amsdu_algo = {
++	.write = mt7996_amsdu_algo_write,
++	.read = NULL,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t mt7996_amsdu_para_write(struct file *file,
++				   const char __user *user_buf,
++				   size_t count,
++				   loff_t *ppos)
++{
++	struct mt7996_dev *dev = file->private_data;
++	char buf[100];
++	int ret;
++	struct {
++		u8 _rsv[4];
++
++		u16 tag;
++		u16 len;
++
++		u16 wlan_idx;
++		u8  amsdu_en;
++		u8  num;
++		u16 lenth;
++		u8  rsv[2];
++	} __packed data = {
++		.tag = cpu_to_le16(UNI_MEC_AMSDU_PARA_STA),
++		.len = cpu_to_le16(sizeof(data) - 4),
++	};
++
++	if (count >= sizeof(buf))
++		return -EINVAL;
++
++	if (copy_from_user(buf, user_buf, count))
++		return -EFAULT;
++
++	if (count && buf[count - 1] == '\n')
++		buf[count - 1] = '\0';
++	else
++		buf[count] = '\0';
++
++	if (sscanf(buf, "%hu %hhu %hhu %hu", &data.wlan_idx, &data.amsdu_en, &data.num, &data.lenth) != 4)
++		return -EINVAL;
++
++	if (data.wlan_idx >= mt7996_wtbl_size(dev))
++		return -EINVAL;
++
++	ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MEC), &data,
++			  sizeof(data), true);
++	if (ret)
++		return -EINVAL;
++
++	return count;
++}
++static const struct file_operations fops_amsdu_para = {
++	.write = mt7996_amsdu_para_write,
++	.read = NULL,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static int mt7996_hw_amsdu_info_read(struct seq_file *s, void *data)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	u32 amsdu_cnt[WF_PLE_TOP_AMSDU_PACK_NUM] = {0}, total_cnt;
++	u8 i;
++
++	seq_printf(s, "HW A-MSDU Information:\n");
++
++	for (total_cnt = 0, i = 0; i < WF_PLE_TOP_AMSDU_PACK_NUM; ++i) {
++		amsdu_cnt[i] = mt76_rr(dev, WF_PLE_TOP_AMSDU_PACK_1_MSDU_CNT_ADDR + i * 4);
++		total_cnt += amsdu_cnt[i];
++	}
++
++	for (i = 0; i < WF_PLE_TOP_AMSDU_PACK_NUM; ++i) {
++		seq_printf(s, "# of HW A-MSDU containing %hhu MSDU: 0x%x",
++		           i + 1, amsdu_cnt[i]);
++		seq_printf(s, "\t(%u.%u%%)\n",
++		           total_cnt ? amsdu_cnt[i] * 1000 / total_cnt / 10 : 0,
++		           total_cnt ? amsdu_cnt[i] * 1000 / total_cnt % 10 : 0);
++	}
++
++	return 0;
++}
++
++/* PSE INFO */
++static struct bmac_queue_info_t pse_queue_empty_info[] = {
++	{"CPU Q0",  ENUM_UMAC_CPU_PORT_1,     ENUM_UMAC_CTX_Q_0},
++	{"CPU Q1",  ENUM_UMAC_CPU_PORT_1,     ENUM_UMAC_CTX_Q_1},
++	{"CPU Q2",  ENUM_UMAC_CPU_PORT_1,     ENUM_UMAC_CTX_Q_2},
++	{"CPU Q3",  ENUM_UMAC_CPU_PORT_1,     ENUM_UMAC_CTX_Q_3},
++	{NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, /* 4~7 not defined */
++	{NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
++	{NULL, 0, 0}, {NULL, 0, 0},  /* 14~15 not defined */
++	{"LMAC Q",  ENUM_UMAC_LMAC_PORT_2,    0},
++	{"MDP TX Q0", ENUM_UMAC_LMAC_PORT_2, 1},
++	{"MDP RX Q", ENUM_UMAC_LMAC_PORT_2, 2},
++	{"SEC TX Q0", ENUM_UMAC_LMAC_PORT_2, 3},
++	{"SEC RX Q", ENUM_UMAC_LMAC_PORT_2, 4},
++	{"SFD_PARK Q", ENUM_UMAC_LMAC_PORT_2, 5},
++	{"MDP_TXIOC Q0", ENUM_UMAC_LMAC_PORT_2, 6},
++	{"MDP_RXIOC Q0", ENUM_UMAC_LMAC_PORT_2, 7},
++	{"MDP TX Q1", ENUM_UMAC_LMAC_PORT_2, 0x11},
++	{"SEC TX Q1", ENUM_UMAC_LMAC_PORT_2, 0x13},
++	{"MDP_TXIOC Q1", ENUM_UMAC_LMAC_PORT_2, 0x16},
++	{"MDP_RXIOC Q1", ENUM_UMAC_LMAC_PORT_2, 0x17},
++	{"CPU Q3",  ENUM_UMAC_CPU_PORT_1,     4},
++	{NULL, 0, 0}, {NULL, 0, 0},
++	{"RLS Q",  ENUM_PLE_CTRL_PSE_PORT_3, ENUM_UMAC_PLE_CTRL_P3_Q_0X1F}
++};
++
++static struct bmac_queue_info_t pse_queue_empty2_info[] = {
++	{"MDP_TDPIOC Q0", ENUM_UMAC_LMAC_PORT_2, 0x8},
++	{"MDP_RDPIOC Q0", ENUM_UMAC_LMAC_PORT_2, 0x9},
++	{"MDP_TDPIOC Q1", ENUM_UMAC_LMAC_PORT_2, 0x18},
++	{"MDP_RDPIOC Q1", ENUM_UMAC_LMAC_PORT_2, 0x19},
++	{"MDP_TDPIOC Q2", ENUM_UMAC_LMAC_PORT_2, 0x28},
++	{"MDP_RDPIOC Q2", ENUM_UMAC_LMAC_PORT_2, 0x29},
++	{NULL, 0, 0},
++	{"MDP_RDPIOC Q3", ENUM_UMAC_LMAC_PORT_2, 0x39},
++	{"MDP TX Q2", ENUM_UMAC_LMAC_PORT_2, 0x21},
++	{"SEC TX Q2", ENUM_UMAC_LMAC_PORT_2, 0x23},
++	{"MDP_TXIOC Q2", ENUM_UMAC_LMAC_PORT_2, 0x26},
++	{"MDP_RXIOC Q2", ENUM_UMAC_LMAC_PORT_2, 0x27},
++	{NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
++	{"MDP_RXIOC Q3", ENUM_UMAC_LMAC_PORT_2, 0x37},
++	{"HIF Q0", ENUM_UMAC_HIF_PORT_0,    0},
++	{"HIF Q1", ENUM_UMAC_HIF_PORT_0,    1},
++	{"HIF Q2", ENUM_UMAC_HIF_PORT_0,    2},
++	{"HIF Q3", ENUM_UMAC_HIF_PORT_0,    3},
++	{"HIF Q4", ENUM_UMAC_HIF_PORT_0,    4},
++	{"HIF Q5", ENUM_UMAC_HIF_PORT_0,    5},
++	{"HIF Q6", ENUM_UMAC_HIF_PORT_0,    6},
++	{"HIF Q7", ENUM_UMAC_HIF_PORT_0,    7},
++	{"HIF Q8", ENUM_UMAC_HIF_PORT_0,    8},
++	{"HIF Q9", ENUM_UMAC_HIF_PORT_0,    9},
++	{"HIF Q10", ENUM_UMAC_HIF_PORT_0,    10},
++	{"HIF Q11", ENUM_UMAC_HIF_PORT_0,    11},
++	{"HIF Q12", ENUM_UMAC_HIF_PORT_0,    12},
++	{"HIF Q13", ENUM_UMAC_HIF_PORT_0,    13},
++	{NULL, 0, 0}, {NULL, 0, 0}
++};
++
++static int
++mt7996_pseinfo_read(struct seq_file *s, void *data)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	u32 pse_buf_ctrl, pg_sz, pg_num;
++	u32 pse_stat[2], pg_flow_ctrl[28] = {0};
++	u32 fpg_cnt, ffa_cnt, fpg_head, fpg_tail;
++	u32 max_q, min_q, rsv_pg, used_pg;
++	int i;
++
++	pse_buf_ctrl = mt76_rr(dev, WF_PSE_TOP_PBUF_CTRL_ADDR);
++	pse_stat[0] = mt76_rr(dev, WF_PSE_TOP_QUEUE_EMPTY_ADDR);
++	pse_stat[1] = mt76_rr(dev, WF_PSE_TOP_QUEUE_EMPTY_1_ADDR);
++	pg_flow_ctrl[0] = mt76_rr(dev, WF_PSE_TOP_FREEPG_CNT_ADDR);
++	pg_flow_ctrl[1] = mt76_rr(dev, WF_PSE_TOP_FREEPG_HEAD_TAIL_ADDR);
++	pg_flow_ctrl[2] = mt76_rr(dev, WF_PSE_TOP_PG_HIF0_GROUP_ADDR);
++	pg_flow_ctrl[3] = mt76_rr(dev, WF_PSE_TOP_HIF0_PG_INFO_ADDR);
++	pg_flow_ctrl[4] = mt76_rr(dev, WF_PSE_TOP_PG_HIF1_GROUP_ADDR);
++	pg_flow_ctrl[5] = mt76_rr(dev, WF_PSE_TOP_HIF1_PG_INFO_ADDR);
++	pg_flow_ctrl[6] = mt76_rr(dev, WF_PSE_TOP_PG_CPU_GROUP_ADDR);
++	pg_flow_ctrl[7] = mt76_rr(dev, WF_PSE_TOP_CPU_PG_INFO_ADDR);
++	pg_flow_ctrl[8] = mt76_rr(dev, WF_PSE_TOP_PG_LMAC0_GROUP_ADDR);
++	pg_flow_ctrl[9] = mt76_rr(dev, WF_PSE_TOP_LMAC0_PG_INFO_ADDR);
++	pg_flow_ctrl[10] = mt76_rr(dev, WF_PSE_TOP_PG_LMAC1_GROUP_ADDR);
++	pg_flow_ctrl[11] = mt76_rr(dev, WF_PSE_TOP_LMAC1_PG_INFO_ADDR);
++	pg_flow_ctrl[12] = mt76_rr(dev, WF_PSE_TOP_PG_LMAC2_GROUP_ADDR);
++	pg_flow_ctrl[13] = mt76_rr(dev, WF_PSE_TOP_LMAC2_PG_INFO_ADDR);
++	pg_flow_ctrl[14] = mt76_rr(dev, WF_PSE_TOP_PG_PLE_GROUP_ADDR);
++	pg_flow_ctrl[15] = mt76_rr(dev, WF_PSE_TOP_PLE_PG_INFO_ADDR);
++	pg_flow_ctrl[16] = mt76_rr(dev, WF_PSE_TOP_PG_LMAC3_GROUP_ADDR);
++	pg_flow_ctrl[17] = mt76_rr(dev, WF_PSE_TOP_LMAC3_PG_INFO_ADDR);
++	pg_flow_ctrl[18] = mt76_rr(dev, WF_PSE_TOP_PG_MDP_GROUP_ADDR);
++	pg_flow_ctrl[19] = mt76_rr(dev, WF_PSE_TOP_MDP_PG_INFO_ADDR);
++	pg_flow_ctrl[20] = mt76_rr(dev, WF_PSE_TOP_PG_PLE1_GROUP_ADDR);
++	pg_flow_ctrl[21] = mt76_rr(dev, WF_PSE_TOP_PLE1_PG_INFO_ADDR);
++	pg_flow_ctrl[22] = mt76_rr(dev, WF_PSE_TOP_PG_MDP2_GROUP_ADDR);
++	pg_flow_ctrl[23] = mt76_rr(dev, WF_PSE_TOP_MDP2_PG_INFO_ADDR);
++	if (mt7996_band_valid(dev, MT_BAND2)) {
++		pg_flow_ctrl[24] = mt76_rr(dev, WF_PSE_TOP_PG_MDP3_GROUP_ADDR);
++		pg_flow_ctrl[25] = mt76_rr(dev, WF_PSE_TOP_MDP3_PG_INFO_ADDR);
++	}
++	pg_flow_ctrl[26] = mt76_rr(dev, WF_PSE_TOP_PG_HIF2_GROUP_ADDR);
++	pg_flow_ctrl[27] = mt76_rr(dev, WF_PSE_TOP_HIF2_PG_INFO_ADDR);
++	/* Configuration Info */
++	seq_printf(s, "PSE Configuration Info:\n");
++	seq_printf(s, "\tPacket Buffer Control: 0x%08x\n", pse_buf_ctrl);
++	pg_sz = (pse_buf_ctrl & WF_PSE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_MASK) >> WF_PSE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_SHFT;
++	seq_printf(s, "\t\tPage Size=%d(%d bytes per page)\n", pg_sz, (pg_sz == 1 ? 256 : 128));
++	seq_printf(s, "\t\tPage Offset=%d(in unit of 64KB)\n",
++			 (pse_buf_ctrl & WF_PSE_TOP_PBUF_CTRL_PBUF_OFFSET_MASK) >> WF_PSE_TOP_PBUF_CTRL_PBUF_OFFSET_SHFT);
++	pg_num = (pse_buf_ctrl & WF_PSE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_MASK) >> WF_PSE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_SHFT;
++	seq_printf(s, "\t\tTotal page numbers=%d pages\n", pg_num);
++	/* Page Flow Control */
++	seq_printf(s, "PSE Page Flow Control:\n");
++	seq_printf(s, "\tFree page counter: 0x%08x\n", pg_flow_ctrl[0]);
++	fpg_cnt = (pg_flow_ctrl[0] & WF_PSE_TOP_FREEPG_CNT_FREEPG_CNT_MASK) >> WF_PSE_TOP_FREEPG_CNT_FREEPG_CNT_SHFT;
++	seq_printf(s, "\t\tThe toal page number of free=0x%03x\n", fpg_cnt);
++	ffa_cnt = (pg_flow_ctrl[0] & WF_PSE_TOP_FREEPG_CNT_FFA_CNT_MASK) >> WF_PSE_TOP_FREEPG_CNT_FFA_CNT_SHFT;
++	seq_printf(s, "\t\tThe free page numbers of free for all=0x%03x\n", ffa_cnt);
++	seq_printf(s, "\tFree page head and tail: 0x%08x\n", pg_flow_ctrl[1]);
++	fpg_head = (pg_flow_ctrl[1] & WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_MASK) >> WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_SHFT;
++	fpg_tail = (pg_flow_ctrl[1] & WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_MASK) >> WF_PSE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_SHFT;
++	seq_printf(s, "\t\tThe tail/head page of free page list=0x%03x/0x%03x\n", fpg_tail, fpg_head);
++	seq_printf(s, "\tReserved page counter of HIF0 group: 0x%08x\n", pg_flow_ctrl[2]);
++	seq_printf(s, "\tHIF0 group page status: 0x%08x\n", pg_flow_ctrl[3]);
++	min_q = (pg_flow_ctrl[2] & WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MIN_QUOTA_SHFT;
++	max_q = (pg_flow_ctrl[2] & WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_HIF0_GROUP_HIF0_MAX_QUOTA_SHFT;
++	seq_printf(s, "\t\tThe max/min quota pages of HIF0 group=0x%03x/0x%03x\n", max_q, min_q);
++	rsv_pg = (pg_flow_ctrl[3] & WF_PSE_TOP_HIF0_PG_INFO_HIF0_RSV_CNT_MASK) >> WF_PSE_TOP_HIF0_PG_INFO_HIF0_RSV_CNT_SHFT;
++	used_pg = (pg_flow_ctrl[3] & WF_PSE_TOP_HIF0_PG_INFO_HIF0_SRC_CNT_MASK) >> WF_PSE_TOP_HIF0_PG_INFO_HIF0_SRC_CNT_SHFT;
++	seq_printf(s, "\t\tThe used/reserved pages of HIF0 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++	seq_printf(s, "\tReserved page counter of HIF1 group: 0x%08x\n", pg_flow_ctrl[4]);
++	seq_printf(s, "\tHIF1 group page status: 0x%08x\n", pg_flow_ctrl[5]);
++	min_q = (pg_flow_ctrl[4] & WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MIN_QUOTA_SHFT;
++	max_q = (pg_flow_ctrl[4] & WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_HIF1_GROUP_HIF1_MAX_QUOTA_SHFT;
++	seq_printf(s, "\t\tThe max/min quota pages of HIF1 group=0x%03x/0x%03x\n", max_q, min_q);
++	rsv_pg = (pg_flow_ctrl[5] & WF_PSE_TOP_HIF1_PG_INFO_HIF1_RSV_CNT_MASK) >> WF_PSE_TOP_HIF1_PG_INFO_HIF1_RSV_CNT_SHFT;
++	used_pg = (pg_flow_ctrl[5] & WF_PSE_TOP_HIF1_PG_INFO_HIF1_SRC_CNT_MASK) >> WF_PSE_TOP_HIF1_PG_INFO_HIF1_SRC_CNT_SHFT;
++	seq_printf(s, "\t\tThe used/reserved pages of HIF1 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++	seq_printf(s, "\tReserved page counter of HIF2 group: 0x%08x\n", pg_flow_ctrl[26]);
++	seq_printf(s, "\tHIF2 group page status: 0x%08x\n", pg_flow_ctrl[27]);
++	min_q = (pg_flow_ctrl[26] & WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MIN_QUOTA_SHFT;
++	max_q = (pg_flow_ctrl[26] & WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_HIF2_GROUP_HIF2_MAX_QUOTA_SHFT;
++	seq_printf(s, "\t\tThe max/min quota pages of HIF2 group=0x%03x/0x%03x\n", max_q, min_q);
++	rsv_pg = (pg_flow_ctrl[27] & WF_PSE_TOP_HIF2_PG_INFO_HIF2_RSV_CNT_MASK) >> WF_PSE_TOP_HIF2_PG_INFO_HIF2_RSV_CNT_SHFT;
++	used_pg = (pg_flow_ctrl[27] & WF_PSE_TOP_HIF2_PG_INFO_HIF2_SRC_CNT_MASK) >> WF_PSE_TOP_HIF2_PG_INFO_HIF2_SRC_CNT_SHFT;
++	seq_printf(s, "\t\tThe used/reserved pages of HIF2 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++	seq_printf(s, "\tReserved page counter of CPU group: 0x%08x\n", pg_flow_ctrl[6]);
++	seq_printf(s, "\tCPU group page status: 0x%08x\n", pg_flow_ctrl[7]);
++	min_q = (pg_flow_ctrl[6] & WF_PSE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_SHFT;
++	max_q = (pg_flow_ctrl[6] & WF_PSE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_SHFT;
++	seq_printf(s, "\t\tThe max/min quota pages of CPU group=0x%03x/0x%03x\n", max_q, min_q);
++	rsv_pg = (pg_flow_ctrl[7] & WF_PSE_TOP_CPU_PG_INFO_CPU_RSV_CNT_MASK) >> WF_PSE_TOP_CPU_PG_INFO_CPU_RSV_CNT_SHFT;
++	used_pg = (pg_flow_ctrl[7] & WF_PSE_TOP_CPU_PG_INFO_CPU_SRC_CNT_MASK) >> WF_PSE_TOP_CPU_PG_INFO_CPU_SRC_CNT_SHFT;
++	seq_printf(s, "\t\tThe used/reserved pages of CPU group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++	seq_printf(s, "\tReserved page counter of LMAC0 group: 0x%08x\n", pg_flow_ctrl[8]);
++	seq_printf(s, "\tLMAC0 group page status: 0x%08x\n", pg_flow_ctrl[9]);
++	min_q = (pg_flow_ctrl[8] & WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MIN_QUOTA_SHFT;
++	max_q = (pg_flow_ctrl[8] & WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC0_GROUP_LMAC0_MAX_QUOTA_SHFT;
++	seq_printf(s, "\t\tThe max/min quota pages of LMAC0 group=0x%03x/0x%03x\n", max_q, min_q);
++	rsv_pg = (pg_flow_ctrl[9] & WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_RSV_CNT_MASK) >> WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_RSV_CNT_SHFT;
++	used_pg = (pg_flow_ctrl[9] & WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_SRC_CNT_MASK) >> WF_PSE_TOP_LMAC0_PG_INFO_LMAC0_SRC_CNT_SHFT;
++	seq_printf(s, "\t\tThe used/reserved pages of LMAC0 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++	seq_printf(s, "\tReserved page counter of LMAC1 group: 0x%08x\n", pg_flow_ctrl[10]);
++	seq_printf(s, "\tLMAC1 group page status: 0x%08x\n", pg_flow_ctrl[11]);
++	min_q = (pg_flow_ctrl[10] & WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MIN_QUOTA_SHFT;
++	max_q = (pg_flow_ctrl[10] & WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC1_GROUP_LMAC1_MAX_QUOTA_SHFT;
++	seq_printf(s, "\t\tThe max/min quota pages of LMAC1 group=0x%03x/0x%03x\n", max_q, min_q);
++	rsv_pg = (pg_flow_ctrl[11] & WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_RSV_CNT_MASK) >> WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_RSV_CNT_SHFT;
++	used_pg = (pg_flow_ctrl[11] & WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_SRC_CNT_MASK) >> WF_PSE_TOP_LMAC1_PG_INFO_LMAC1_SRC_CNT_SHFT;
++	seq_printf(s, "\t\tThe used/reserved pages of LMAC1 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++	seq_printf(s, "\tReserved page counter of LMAC2 group: 0x%08x\n", pg_flow_ctrl[11]);
++	seq_printf(s, "\tLMAC2 group page status: 0x%08x\n", pg_flow_ctrl[12]);
++	min_q = (pg_flow_ctrl[12] & WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MIN_QUOTA_SHFT;
++	max_q = (pg_flow_ctrl[12] & WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC2_GROUP_LMAC2_MAX_QUOTA_SHFT;
++	seq_printf(s, "\t\tThe max/min quota pages of LMAC2 group=0x%03x/0x%03x\n", max_q, min_q);
++	rsv_pg = (pg_flow_ctrl[13] & WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_RSV_CNT_MASK) >> WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_RSV_CNT_SHFT;
++	used_pg = (pg_flow_ctrl[13] & WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_SRC_CNT_MASK) >> WF_PSE_TOP_LMAC2_PG_INFO_LMAC2_SRC_CNT_SHFT;
++	seq_printf(s, "\t\tThe used/reserved pages of LMAC2 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++
++	seq_printf(s, "\tReserved page counter of LMAC3 group: 0x%08x\n", pg_flow_ctrl[16]);
++	seq_printf(s, "\tLMAC3 group page status: 0x%08x\n", pg_flow_ctrl[17]);
++	min_q = (pg_flow_ctrl[16] & WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MIN_QUOTA_SHFT;
++	max_q = (pg_flow_ctrl[16] & WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_LMAC3_GROUP_LMAC3_MAX_QUOTA_SHFT;
++	seq_printf(s, "\t\tThe max/min quota pages of LMAC3 group=0x%03x/0x%03x\n", max_q, min_q);
++	rsv_pg = (pg_flow_ctrl[17] & WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_RSV_CNT_MASK) >> WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_RSV_CNT_SHFT;
++	used_pg = (pg_flow_ctrl[17] & WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_SRC_CNT_MASK) >> WF_PSE_TOP_LMAC3_PG_INFO_LMAC3_SRC_CNT_SHFT;
++	seq_printf(s, "\t\tThe used/reserved pages of LMAC3 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++
++	seq_printf(s, "\tReserved page counter of PLE group: 0x%08x\n", pg_flow_ctrl[14]);
++	seq_printf(s, "\tPLE group page status: 0x%08x\n", pg_flow_ctrl[15]);
++	min_q = (pg_flow_ctrl[14] & WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_SHFT;
++	max_q = (pg_flow_ctrl[14] & WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_SHFT;
++	seq_printf(s, "\t\tThe max/min quota pages of PLE group=0x%03x/0x%03x\n", max_q, min_q);
++	rsv_pg = (pg_flow_ctrl[15] & WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_MASK) >> WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_SHFT;
++	used_pg = (pg_flow_ctrl[15] & WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_MASK) >> WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_SHFT;
++	seq_printf(s, "\t\tThe used/reserved pages of PLE group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++
++	seq_printf(s, "\tReserved page counter of PLE1 group: 0x%08x\n", pg_flow_ctrl[14]);
++	seq_printf(s, "\tPLE1 group page status: 0x%08x\n", pg_flow_ctrl[15]);
++	min_q = (pg_flow_ctrl[20] & WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_PLE_GROUP_PLE_MIN_QUOTA_SHFT;
++	max_q = (pg_flow_ctrl[20] & WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_PLE_GROUP_PLE_MAX_QUOTA_SHFT;
++	seq_printf(s, "\t\tThe max/min quota pages of PLE1 group=0x%03x/0x%03x\n", max_q, min_q);
++	rsv_pg = (pg_flow_ctrl[21] & WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_MASK) >> WF_PSE_TOP_PLE_PG_INFO_PLE_RSV_CNT_SHFT;
++	used_pg = (pg_flow_ctrl[21] & WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_MASK) >> WF_PSE_TOP_PLE_PG_INFO_PLE_SRC_CNT_SHFT;
++	seq_printf(s, "\t\tThe used/reserved pages of PLE1 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++
++	seq_printf(s, "\tReserved page counter of MDP group: 0x%08x\n", pg_flow_ctrl[18]);
++	seq_printf(s, "\tMDP group page status: 0x%08x\n", pg_flow_ctrl[19]);
++	min_q = (pg_flow_ctrl[18] & WF_PSE_TOP_PG_MDP_GROUP_MDP_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_MDP_GROUP_MDP_MIN_QUOTA_SHFT;
++	max_q = (pg_flow_ctrl[18] & WF_PSE_TOP_PG_MDP_GROUP_MDP_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_MDP_GROUP_MDP_MAX_QUOTA_SHFT;
++	seq_printf(s, "\t\tThe max/min quota pages of MDP group=0x%03x/0x%03x\n", max_q, min_q);
++	rsv_pg = (pg_flow_ctrl[19] & WF_PSE_TOP_MDP_PG_INFO_MDP_RSV_CNT_MASK) >> WF_PSE_TOP_MDP_PG_INFO_MDP_RSV_CNT_SHFT;
++	used_pg = (pg_flow_ctrl[19] & WF_PSE_TOP_MDP_PG_INFO_MDP_SRC_CNT_MASK) >> WF_PSE_TOP_MDP_PG_INFO_MDP_SRC_CNT_SHFT;
++	seq_printf(s, "\t\tThe used/reserved pages of MDP group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++	seq_printf(s, "\tReserved page counter of MDP2 group: 0x%08x\n", pg_flow_ctrl[22]);
++	seq_printf(s, "\tMDP2 group page status: 0x%08x\n", pg_flow_ctrl[23]);
++	min_q = (pg_flow_ctrl[22] & WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MIN_QUOTA_SHFT;
++	max_q = (pg_flow_ctrl[22] & WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_MDP2_GROUP_MDP2_MAX_QUOTA_SHFT;
++	seq_printf(s, "\t\tThe max/min quota pages of MDP2 group=0x%03x/0x%03x\n", max_q, min_q);
++	rsv_pg = (pg_flow_ctrl[23] & WF_PSE_TOP_MDP2_PG_INFO_MDP2_RSV_CNT_MASK) >> WF_PSE_TOP_MDP2_PG_INFO_MDP2_RSV_CNT_SHFT;
++	used_pg = (pg_flow_ctrl[23] & WF_PSE_TOP_MDP2_PG_INFO_MDP2_SRC_CNT_MASK) >> WF_PSE_TOP_MDP2_PG_INFO_MDP2_SRC_CNT_SHFT;
++	seq_printf(s, "\t\tThe used/reserved pages of MDP2 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++	if (mt7996_band_valid(dev, MT_BAND2)) {
++		seq_printf(s, "\tReserved page counter of MDP3 group: 0x%08x\n", pg_flow_ctrl[24]);
++		seq_printf(s, "\tMDP3 group page status: 0x%08x\n", pg_flow_ctrl[25]);
++		min_q = (pg_flow_ctrl[24] & WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MIN_QUOTA_MASK) >> WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MIN_QUOTA_SHFT;
++		max_q = (pg_flow_ctrl[24] & WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MAX_QUOTA_MASK) >> WF_PSE_TOP_PG_MDP3_GROUP_MDP3_MAX_QUOTA_SHFT;
++		seq_printf(s, "\t\tThe max/min quota pages of MDP3 group=0x%03x/0x%03x\n", max_q, min_q);
++		rsv_pg = (pg_flow_ctrl[25] & WF_PSE_TOP_MDP3_PG_INFO_MDP3_RSV_CNT_MASK) >> WF_PSE_TOP_MDP3_PG_INFO_MDP3_RSV_CNT_SHFT;
++		used_pg = (pg_flow_ctrl[25] & WF_PSE_TOP_MDP3_PG_INFO_MDP3_SRC_CNT_MASK) >> WF_PSE_TOP_MDP3_PG_INFO_MDP3_SRC_CNT_SHFT;
++		seq_printf(s, "\t\tThe used/reserved pages of MDP3 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
++	}
++	/* Queue Empty Status */
++	seq_printf(s, "PSE Queue Empty Status:\n");
++	seq_printf(s, "\tQUEUE_EMPTY: 0x%08x, QUEUE_EMPTY2: 0x%08x\n", pse_stat[0], pse_stat[1]);
++	seq_printf(s, "\t\tCPU Q0/1/2/3/4 empty=%d/%d/%d/%d/%d\n",
++			  (pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_CPU_Q0_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_CPU_Q0_EMPTY_SHFT,
++			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_CPU_Q1_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_CPU_Q1_EMPTY_SHFT),
++			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_CPU_Q2_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_CPU_Q2_EMPTY_SHFT),
++			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_CPU_Q3_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_CPU_Q3_EMPTY_SHFT),
++			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_CPU_Q4_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_CPU_Q4_EMPTY_SHFT));
++	seq_printf(s, "\t\tHIF Q0/1/2/3/4/5/6/7/8/9/10/11/12/13 empty=%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d\n",
++			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_0_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_0_EMPTY_SHFT),
++			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_1_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_1_EMPTY_SHFT),
++			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_2_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_2_EMPTY_SHFT),
++			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_3_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_3_EMPTY_SHFT),
++			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_4_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_4_EMPTY_SHFT),
++			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_5_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_5_EMPTY_SHFT),
++			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_6_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_6_EMPTY_SHFT),
++			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_7_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_7_EMPTY_SHFT),
++			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_8_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_8_EMPTY_SHFT),
++			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_9_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_9_EMPTY_SHFT),
++			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_10_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_10_EMPTY_SHFT),
++			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_11_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_11_EMPTY_SHFT),
++			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_12_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_12_EMPTY_SHFT),
++			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_HIF_13_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_HIF_13_EMPTY_SHFT));
++	seq_printf(s, "\t\tLMAC TX Q empty=%d\n",
++			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_LMAC_TX_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_LMAC_TX_QUEUE_EMPTY_SHFT));
++	seq_printf(s, "\t\tMDP TX Q0/Q1/Q2/RX Q empty=%d/%d/%d/%d\n",
++			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_TX_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_TX_QUEUE_EMPTY_SHFT),
++			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_TX1_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_TX1_QUEUE_EMPTY_SHFT),
++			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TX2_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TX2_QUEUE_EMPTY_SHFT),
++			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_RX_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_RX_QUEUE_EMPTY_SHFT));
++	seq_printf(s, "\t\tSEC TX Q0/Q1/Q2/RX Q empty=%d/%d/%d/%d\n",
++			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_SEC_TX_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_SEC_TX_QUEUE_EMPTY_SHFT),
++			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_SEC_TX1_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_SEC_TX1_QUEUE_EMPTY_SHFT),
++			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_SEC_TX2_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_SEC_TX2_QUEUE_EMPTY_SHFT),
++			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_SEC_RX_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_SEC_RX_QUEUE_EMPTY_SHFT));
++	seq_printf(s, "\t\tSFD PARK Q empty=%d\n",
++			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_SFD_PARK_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_SFD_PARK_QUEUE_EMPTY_SHFT));
++	seq_printf(s, "\t\tMDP TXIOC Q0/Q1/Q2 empty=%d/%d/%d\n",
++			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC_QUEUE_EMPTY_SHFT),
++			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC1_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_TXIOC1_QUEUE_EMPTY_SHFT),
++			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TXIOC2_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_MDP_TXIOC2_QUEUE_EMPTY_SHFT));
++	seq_printf(s, "\t\tMDP RXIOC Q0/Q1/Q2/Q3 empty=%d/%d/%d/%d\n",
++			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC_QUEUE_EMPTY_SHFT),
++			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC1_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_MDP_RXIOC1_QUEUE_EMPTY_SHFT),
++			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC2_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC2_QUEUE_EMPTY_SHFT),
++			  ((pse_stat[1] & WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC3_QUEUE_EMPTY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_1_MDP_RXIOC3_QUEUE_EMPTY_SHFT));
++	seq_printf(s, "\t\tRLS Q empty=%d\n",
++			  ((pse_stat[0] & WF_PSE_TOP_QUEUE_EMPTY_RLS_Q_EMTPY_MASK) >> WF_PSE_TOP_QUEUE_EMPTY_RLS_Q_EMTPY_SHFT));
++	seq_printf(s, "Nonempty Q info:\n");
++
++	for (i = 0; i < 31; i++) {
++		if (((pse_stat[0] & (0x1 << i)) >> i) == 0) {
++			u32 hfid, tfid, pktcnt, fl_que_ctrl[3] = {0};
++
++			if (pse_queue_empty_info[i].QueueName != NULL) {
++				seq_printf(s, "\t%s: ", pse_queue_empty_info[i].QueueName);
++				fl_que_ctrl[0] |= WF_PSE_TOP_FL_QUE_CTRL_0_EXECUTE_MASK;
++				fl_que_ctrl[0] |= (pse_queue_empty_info[i].Portid << WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_SHFT);
++				fl_que_ctrl[0] |= (pse_queue_empty_info[i].Queueid << WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_SHFT);
++			} else
++				continue;
++
++			fl_que_ctrl[0] |= (0x1 << 31);
++			mt76_wr(dev, WF_PSE_TOP_FL_QUE_CTRL_0_ADDR, fl_que_ctrl[0]);
++			fl_que_ctrl[1] = mt76_rr(dev, WF_PSE_TOP_FL_QUE_CTRL_2_ADDR);
++			fl_que_ctrl[2] = mt76_rr(dev, WF_PSE_TOP_FL_QUE_CTRL_3_ADDR);
++			hfid = (fl_que_ctrl[1] & WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_MASK) >> WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_SHFT;
++			tfid = (fl_que_ctrl[1] & WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_MASK) >> WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_SHFT;
++			pktcnt = (fl_que_ctrl[2] & WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_MASK) >> WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_SHFT;
++			seq_printf(s, "tail/head fid = 0x%03x/0x%03x, pkt cnt = 0x%03x\n",
++					  tfid, hfid, pktcnt);
++		}
++	}
++
++	for (i = 0; i < 31; i++) {
++		if (((pse_stat[1] & (0x1 << i)) >> i) == 0) {
++			u32 hfid, tfid, pktcnt, fl_que_ctrl[3] = {0};
++
++			if (pse_queue_empty2_info[i].QueueName != NULL) {
++				seq_printf(s, "\t%s: ", pse_queue_empty2_info[i].QueueName);
++				fl_que_ctrl[0] |= WF_PSE_TOP_FL_QUE_CTRL_0_EXECUTE_MASK;
++				fl_que_ctrl[0] |= (pse_queue_empty2_info[i].Portid << WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_SHFT);
++				fl_que_ctrl[0] |= (pse_queue_empty2_info[i].Queueid << WF_PSE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_SHFT);
++			} else
++				continue;
++
++			fl_que_ctrl[0] |= (0x1 << 31);
++			mt76_wr(dev, WF_PSE_TOP_FL_QUE_CTRL_0_ADDR, fl_que_ctrl[0]);
++			fl_que_ctrl[1] = mt76_rr(dev, WF_PSE_TOP_FL_QUE_CTRL_2_ADDR);
++			fl_que_ctrl[2] = mt76_rr(dev, WF_PSE_TOP_FL_QUE_CTRL_3_ADDR);
++			hfid = (fl_que_ctrl[1] & WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_MASK) >> WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_SHFT;
++			tfid = (fl_que_ctrl[1] & WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_MASK) >> WF_PSE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_SHFT;
++			pktcnt = (fl_que_ctrl[2] & WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_MASK) >> WF_PSE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_SHFT;
++			seq_printf(s, "tail/head fid = 0x%03x/0x%03x, pkt cnt = 0x%03x\n",
++					  tfid, hfid, pktcnt);
++		}
++	}
++
++	return 0;
++}
++
++/* PLE INFO */
++static char *sta_ctrl_reg[] = {"ENABLE", "DISABLE", "PAUSE", "TWT_PAUSE"};
++static struct bmac_queue_info ple_queue_empty_info[] = {
++	{"CPU Q0",  ENUM_UMAC_CPU_PORT_1,     ENUM_UMAC_CTX_Q_0, 0},
++	{"CPU Q1",  ENUM_UMAC_CPU_PORT_1,     ENUM_UMAC_CTX_Q_1, 0},
++	{"CPU Q2",  ENUM_UMAC_CPU_PORT_1,     ENUM_UMAC_CTX_Q_2, 0},
++	{"CPU Q3",  ENUM_UMAC_CPU_PORT_1,     ENUM_UMAC_CTX_Q_3, 0},
++	{"ALTX Q0", ENUM_UMAC_LMAC_PORT_2,    0x10, 0},
++	{"BMC Q0",  ENUM_UMAC_LMAC_PORT_2,    0x11, 0},
++	{"BCN Q0",  ENUM_UMAC_LMAC_PORT_2,    0x12, 0},
++	{"PSMP Q0", ENUM_UMAC_LMAC_PORT_2,    0x13, 0},
++	{"ALTX Q1", ENUM_UMAC_LMAC_PORT_2,    0x10, 1},
++	{"BMC Q1",  ENUM_UMAC_LMAC_PORT_2,    0x11, 1},
++	{"BCN Q1",  ENUM_UMAC_LMAC_PORT_2,    0x12, 1},
++	{"PSMP Q1", ENUM_UMAC_LMAC_PORT_2,    0x13, 1},
++	{"ALTX Q2", ENUM_UMAC_LMAC_PORT_2,    0x10, 2},
++	{"BMC Q2",  ENUM_UMAC_LMAC_PORT_2,    0x11, 2},
++	{"BCN Q2",  ENUM_UMAC_LMAC_PORT_2,    0x12, 2},
++	{"PSMP Q2", ENUM_UMAC_LMAC_PORT_2,    0x13, 2},
++	{"NAF Q",   ENUM_UMAC_LMAC_PORT_2,    0x18, 0},
++	{"NBCN Q",  ENUM_UMAC_LMAC_PORT_2,    0x19, 0},
++	{NULL, 0, 0, 0}, {NULL, 0, 0, 0}, /* 18, 19 not defined */
++	{"FIXFID Q", ENUM_UMAC_LMAC_PORT_2, 0x1a, 0},
++	{NULL, 0, 0, 0}, {NULL, 0, 0, 0}, {NULL, 0, 0, 0}, {NULL, 0, 0, 0}, {NULL, 0, 0, 0},
++	{NULL, 0, 0, 0}, {NULL, 0, 0, 0},
++	{"RLS4 Q",   ENUM_PLE_CTRL_PSE_PORT_3, 0x7c, 0},
++	{"RLS3 Q",   ENUM_PLE_CTRL_PSE_PORT_3, 0x7d, 0},
++	{"RLS2 Q",   ENUM_PLE_CTRL_PSE_PORT_3, 0x7e, 0},
++	{"RLS Q",  ENUM_PLE_CTRL_PSE_PORT_3, 0x7f, 0}
++};
++
++static struct bmac_queue_info_t ple_txcmd_queue_empty_info[__MT_MAX_BAND][32] = {
++	{{"AC00Q", ENUM_UMAC_LMAC_PORT_2, 0x40},
++	 {"AC01Q", ENUM_UMAC_LMAC_PORT_2, 0x41},
++	 {"AC02Q", ENUM_UMAC_LMAC_PORT_2, 0x42},
++	 {"AC03Q", ENUM_UMAC_LMAC_PORT_2, 0x43},
++	 {"AC10Q", ENUM_UMAC_LMAC_PORT_2, 0x44},
++	 {"AC11Q", ENUM_UMAC_LMAC_PORT_2, 0x45},
++	 {"AC12Q", ENUM_UMAC_LMAC_PORT_2, 0x46},
++	 {"AC13Q", ENUM_UMAC_LMAC_PORT_2, 0x47},
++	 {"AC20Q", ENUM_UMAC_LMAC_PORT_2, 0x48},
++	 {"AC21Q", ENUM_UMAC_LMAC_PORT_2, 0x49},
++	 {"AC22Q", ENUM_UMAC_LMAC_PORT_2, 0x4a},
++	 {"AC23Q", ENUM_UMAC_LMAC_PORT_2, 0x4b},
++	 {"AC30Q", ENUM_UMAC_LMAC_PORT_2, 0x4c},
++	 {"AC31Q", ENUM_UMAC_LMAC_PORT_2, 0x4d},
++	 {"AC32Q", ENUM_UMAC_LMAC_PORT_2, 0x4e},
++	 {"AC33Q", ENUM_UMAC_LMAC_PORT_2, 0x4f},
++	 {"ALTX Q0", ENUM_UMAC_LMAC_PORT_2, 0x70},
++	 {"TF Q0", ENUM_UMAC_LMAC_PORT_2, 0x71},
++	 {"TWT TSF-TF Q0", ENUM_UMAC_LMAC_PORT_2, 0x72},
++	 {"TWT DL Q0", ENUM_UMAC_LMAC_PORT_2, 0x73},
++	 {"TWT UL Q0", ENUM_UMAC_LMAC_PORT_2, 0x74},
++	 {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
++	 {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
++	 {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}},
++
++	{{"AC00Q", ENUM_UMAC_LMAC_PORT_2, 0x50},
++	 {"AC01Q", ENUM_UMAC_LMAC_PORT_2, 0x51},
++	 {"AC02Q", ENUM_UMAC_LMAC_PORT_2, 0x52},
++	 {"AC03Q", ENUM_UMAC_LMAC_PORT_2, 0x53},
++	 {"AC10Q", ENUM_UMAC_LMAC_PORT_2, 0x54},
++	 {"AC11Q", ENUM_UMAC_LMAC_PORT_2, 0x55},
++	 {"AC12Q", ENUM_UMAC_LMAC_PORT_2, 0x56},
++	 {"AC13Q", ENUM_UMAC_LMAC_PORT_2, 0x57},
++	 {"AC20Q", ENUM_UMAC_LMAC_PORT_2, 0x58},
++	 {"AC21Q", ENUM_UMAC_LMAC_PORT_2, 0x59},
++	 {"AC22Q", ENUM_UMAC_LMAC_PORT_2, 0x5a},
++	 {"AC23Q", ENUM_UMAC_LMAC_PORT_2, 0x5b},
++	 {"AC30Q", ENUM_UMAC_LMAC_PORT_2, 0x5c},
++	 {"AC31Q", ENUM_UMAC_LMAC_PORT_2, 0x5d},
++	 {"AC32Q", ENUM_UMAC_LMAC_PORT_2, 0x5e},
++	 {"AC33Q", ENUM_UMAC_LMAC_PORT_2, 0x5f},
++	 {"ALTX Q0", ENUM_UMAC_LMAC_PORT_2, 0x75},
++	 {"TF Q0", ENUM_UMAC_LMAC_PORT_2, 0x76},
++	 {"TWT TSF-TF Q0", ENUM_UMAC_LMAC_PORT_2, 0x77},
++	 {"TWT DL Q0", ENUM_UMAC_LMAC_PORT_2, 0x78},
++	 {"TWT UL Q0", ENUM_UMAC_LMAC_PORT_2, 0x79},
++	 {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
++	 {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
++	 {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}},
++
++	{{"AC00Q", ENUM_UMAC_LMAC_PORT_2, 0x60},
++	 {"AC01Q", ENUM_UMAC_LMAC_PORT_2, 0x61},
++	 {"AC02Q", ENUM_UMAC_LMAC_PORT_2, 0x62},
++	 {"AC03Q", ENUM_UMAC_LMAC_PORT_2, 0x63},
++	 {"AC10Q", ENUM_UMAC_LMAC_PORT_2, 0x64},
++	 {"AC11Q", ENUM_UMAC_LMAC_PORT_2, 0x65},
++	 {"AC12Q", ENUM_UMAC_LMAC_PORT_2, 0x66},
++	 {"AC13Q", ENUM_UMAC_LMAC_PORT_2, 0x67},
++	 {"AC20Q", ENUM_UMAC_LMAC_PORT_2, 0x68},
++	 {"AC21Q", ENUM_UMAC_LMAC_PORT_2, 0x69},
++	 {"AC22Q", ENUM_UMAC_LMAC_PORT_2, 0x6a},
++	 {"AC23Q", ENUM_UMAC_LMAC_PORT_2, 0x6b},
++	 {"AC30Q", ENUM_UMAC_LMAC_PORT_2, 0x6c},
++	 {"AC31Q", ENUM_UMAC_LMAC_PORT_2, 0x6d},
++	 {"AC32Q", ENUM_UMAC_LMAC_PORT_2, 0x6e},
++	 {"AC33Q", ENUM_UMAC_LMAC_PORT_2, 0x6f},
++	 {"ALTX Q0", ENUM_UMAC_LMAC_PORT_2, 0x7a},
++	 {"TF Q0", ENUM_UMAC_LMAC_PORT_2, 0x7b},
++	 {"TWT TSF-TF Q0", ENUM_UMAC_LMAC_PORT_2, 0x7c},
++	 {"TWT DL Q0", ENUM_UMAC_LMAC_PORT_2, 0x7d},
++	 {"TWT UL Q0", ENUM_UMAC_LMAC_PORT_2, 0x7e},
++	 {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
++	 {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0},
++	 {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}}
++};
++
++static size_t
++ple_cr_num_of_ac(struct mt76_dev *dev)
++{
++	switch (mt76_chip(dev)) {
++	case 0x7990:
++		return CR_NUM_OF_AC_MT7996;
++	case 0x7992:
++	default:
++		return CR_NUM_OF_AC_MT7992;
++	}
++}
++
++static void
++mt7996_show_ple_pg_info(struct mt7996_dev *dev, struct seq_file *s)
++{
++	u32 val[2];
++
++	seq_printf(s, "PLE Configuration Info:\n");
++
++	val[0] = mt76_rr(dev, WF_PLE_TOP_PBUF_CTRL_ADDR);
++	seq_printf(s, "\tPacket Buffer Control: 0x%08x\n", val[0]);
++	seq_printf(s, "\t\tPage size: %u bytes\n",
++		   u32_get_bits(val[0], WF_PLE_TOP_PBUF_CTRL_PAGE_SIZE_CFG_MASK) ? 128 : 64);
++	seq_printf(s, "\t\tPacket buffer offset: %u (unit: 2KB)\n",
++		   u32_get_bits(val[0], WF_PLE_TOP_PBUF_CTRL_PBUF_OFFSET_MASK));
++	seq_printf(s, "\t\tTotal number of pages: %u pages\n",
++		   u32_get_bits(val[0], WF_PLE_TOP_PBUF_CTRL_TOTAL_PAGE_NUM_MASK));
++
++	seq_printf(s, "PLE Page Flow Control:\n");
++
++	val[0] = mt76_rr(dev, WF_PLE_TOP_FREEPG_CNT_ADDR);
++	val[1] = mt76_rr(dev, WF_PLE_TOP_FREEPG_HEAD_TAIL_ADDR);
++	seq_printf(s, "\tFree Page Counter: 0x%08x\n", val[0]);
++	seq_printf(s, "\tFree Page Head and Tail: 0x%08x\n", val[1]);
++	seq_printf(s, "\t\tNumber of free pages: 0x%04x\n",
++		   u32_get_bits(val[0], WF_PLE_TOP_FREEPG_CNT_FREEPG_CNT_MASK));
++	seq_printf(s, "\t\tNumber of unassigned pages: 0x%04x\n",
++		   u32_get_bits(val[0], WF_PLE_TOP_FREEPG_CNT_FFA_CNT_MASK));
++	seq_printf(s, "\t\tFID of tail/head free page: 0x%04x/0x%04x\n",
++		   u32_get_bits(val[1], WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_TAIL_MASK),
++		   u32_get_bits(val[1], WF_PLE_TOP_FREEPG_HEAD_TAIL_FREEPG_HEAD_MASK));
++
++	val[0] = mt76_rr(dev, WF_PLE_TOP_PG_HIF_GROUP_ADDR);
++	val[1] = mt76_rr(dev, WF_PLE_TOP_HIF_PG_INFO_ADDR);
++	seq_printf(s, "\tReserved Page Counter of HIF Group: 0x%08x\n", val[0]);
++	seq_printf(s, "\tHIF Group Page Status: 0x%08x\n", val[1]);
++	seq_printf(s, "\t\tMax/min page quota for HIF group: 0x%04x/0x%04x\n",
++		   u32_get_bits(val[0], WF_PLE_TOP_PG_HIF_GROUP_HIF_MAX_QUOTA_MASK),
++		   u32_get_bits(val[0], WF_PLE_TOP_PG_HIF_GROUP_HIF_MIN_QUOTA_MASK));
++	seq_printf(s, "\t\tUsed/free page count for HIF group: 0x%04x/0x%04x\n",
++		   u32_get_bits(val[1], WF_PLE_TOP_HIF_PG_INFO_HIF_SRC_CNT_MASK),
++		   u32_get_bits(val[1], WF_PLE_TOP_HIF_PG_INFO_HIF_RSV_CNT_MASK));
++
++	val[0] = mt76_rr(dev, WF_PLE_TOP_PG_HIF_WMTXD_GROUP_ADDR);
++	val[1] = mt76_rr(dev, WF_PLE_TOP_HIF_WMTXD_PG_INFO_ADDR);
++	seq_printf(s, "\tReserved Page Counter of HIF WMCPU TXD Group: 0x%08x\n", val[0]);
++	seq_printf(s, "\tHIF WMCPU TXD Group Page Status: 0x%08x\n", val[1]);
++	seq_printf(s, "\t\tMax/min page quota for HIF WMCPU TXD group: 0x%04x/0x%04x\n",
++		   u32_get_bits(val[0], WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MAX_QUOTA_MASK),
++		   u32_get_bits(val[0], WF_PLE_TOP_PG_HIF_WMTXD_GROUP_HIF_WMTXD_MIN_QUOTA_MASK));
++	seq_printf(s, "\t\tUsed/free page count for HIF WMCPU TXD group: 0x%04x/0x%04x\n",
++		   u32_get_bits(val[1], WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_SRC_CNT_MASK),
++		   u32_get_bits(val[1], WF_PLE_TOP_HIF_WMTXD_PG_INFO_HIF_WMTXD_RSV_CNT_MASK));
++
++	val[0] = mt76_rr(dev, WF_PLE_TOP_PG_HIF_TXCMD_GROUP_ADDR);
++	val[1] = mt76_rr(dev, WF_PLE_TOP_HIF_TXCMD_PG_INFO_ADDR);
++	seq_printf(s, "\tReserved Page Counter of HIF TXCMD Group: 0x%08x\n", val[0]);
++	seq_printf(s, "\tHIF TXCMD Group Page Status: 0x%08x\n", val[1]);
++	seq_printf(s, "\t\tMax/min page quota for HIF TXCMD group: 0x%04x/0x%04x\n",
++		   u32_get_bits(val[0], WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MAX_QUOTA_MASK),
++		   u32_get_bits(val[0], WF_PLE_TOP_PG_HIF_TXCMD_GROUP_HIF_TXCMD_MIN_QUOTA_MASK));
++	seq_printf(s, "\t\tUsed/free page count for HIF TXCMD group: 0x%04x/0x%04x\n",
++		   u32_get_bits(val[1], WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_SRC_CNT_MASK),
++		   u32_get_bits(val[1], WF_PLE_TOP_HIF_TXCMD_PG_INFO_HIF_TXCMD_RSV_CNT_MASK));
++
++	val[0] = mt76_rr(dev, WF_PLE_TOP_PG_CPU_GROUP_ADDR);
++	val[1] = mt76_rr(dev, WF_PLE_TOP_CPU_PG_INFO_ADDR);
++	seq_printf(s, "\tReserved Page Counter of CPU Group: 0x%08x\n", val[0]);
++	seq_printf(s, "\tCPU Group Page Status: 0x%08x\n", val[1]);
++	seq_printf(s, "\t\tMax/min page quota for CPU group: 0x%04x/0x%04x\n",
++		   u32_get_bits(val[0], WF_PLE_TOP_PG_CPU_GROUP_CPU_MAX_QUOTA_MASK),
++		   u32_get_bits(val[0], WF_PLE_TOP_PG_CPU_GROUP_CPU_MIN_QUOTA_MASK));
++	seq_printf(s, "\t\tUsed/free page count for CPU group: 0x%04x/0x%04x\n",
++		   u32_get_bits(val[1], WF_PLE_TOP_CPU_PG_INFO_CPU_SRC_CNT_MASK),
++		   u32_get_bits(val[1], WF_PLE_TOP_CPU_PG_INFO_CPU_RSV_CNT_MASK));
++}
++
++static void
++mt7996_get_ple_acq_stat(struct mt7996_dev *dev, unsigned long *ple_stat)
++{
++	u32 i, addr;
++	size_t cr_num_of_ac = ple_cr_num_of_ac(&dev->mt76);
++
++	ple_stat[0] = mt76_rr(dev, WF_PLE_TOP_QUEUE_EMPTY_ADDR);
++
++	/* Legacy */
++	addr = WF_PLE_TOP_AC0_QUEUE_EMPTY0_ADDR;
++	for (i = 1; i <= cr_num_of_ac; i++, addr += 4) {
++		if (i == cr_num_of_ac && is_mt7992(&dev->mt76))
++			ple_stat[i] = mt76_rr(dev, WF_PLE_TOP_AC0_QUEUE_EMPTY_EXT0_ADDR);
++		else
++			ple_stat[i] = mt76_rr(dev, addr);
++	}
++
++	addr = WF_PLE_TOP_AC1_QUEUE_EMPTY0_ADDR;
++	for (; i <= cr_num_of_ac * 2; i++, addr += 4) {
++		if (i == cr_num_of_ac * 2 && is_mt7992(&dev->mt76))
++			ple_stat[i] = mt76_rr(dev, WF_PLE_TOP_AC1_QUEUE_EMPTY_EXT0_ADDR);
++		else
++			ple_stat[i] = mt76_rr(dev, addr);
++	}
++
++	addr = WF_PLE_TOP_AC2_QUEUE_EMPTY0_ADDR;
++	for (; i <= cr_num_of_ac * 3; i++, addr += 4) {
++		if (i == cr_num_of_ac * 3 && is_mt7992(&dev->mt76))
++			ple_stat[i] = mt76_rr(dev, WF_PLE_TOP_AC2_QUEUE_EMPTY_EXT0_ADDR);
++		else
++			ple_stat[i] = mt76_rr(dev, addr);
++	}
++
++	addr = WF_PLE_TOP_AC3_QUEUE_EMPTY0_ADDR;
++	for (; i <= cr_num_of_ac * 4; i++, addr += 4) {
++		if (i == cr_num_of_ac * 4 && is_mt7992(&dev->mt76))
++			ple_stat[i] = mt76_rr(dev, WF_PLE_TOP_AC3_QUEUE_EMPTY_EXT0_ADDR);
++		else
++			ple_stat[i] = mt76_rr(dev, addr);
++	}
++}
++
++static void
++mt7996_get_sta_pause(struct mt7996_dev *dev, u8 band, u32 *sta_pause, u32 *twt_pause)
++{
++	u32 i, addr;
++	size_t cr_num_of_ac = ple_cr_num_of_ac(&dev->mt76);
++
++	/* switch to target band */
++	mt76_wr(dev, WF_DRR_TOP_SBRR_ADDR, u32_encode_bits(band, WF_DRR_TOP_SBRR_TARGET_BAND_MASK));
++
++	/* Legacy */
++	addr = WF_DRR_TOP_AC0_STATION_PAUSE00_ADDR;
++	for (i = 0; i < cr_num_of_ac; i++, addr += 4) {
++		if (i == cr_num_of_ac - 1 && is_mt7992(&dev->mt76))
++			sta_pause[i] = mt76_rr(dev, WF_DRR_TOP_AC0_STATION_PAUSE_EXT_00_ADDR);
++		else
++			sta_pause[i] = mt76_rr(dev, addr);
++	}
++
++	addr = WF_DRR_TOP_AC1_STATION_PAUSE00_ADDR;
++	for (; i < cr_num_of_ac * 2; i++, addr += 4) {
++		if (i == cr_num_of_ac * 2 - 1 && is_mt7992(&dev->mt76))
++			sta_pause[i] = mt76_rr(dev, WF_DRR_TOP_AC1_STATION_PAUSE_EXT_00_ADDR);
++		else
++			sta_pause[i] = mt76_rr(dev, addr);
++	}
++
++	addr = WF_DRR_TOP_AC2_STATION_PAUSE00_ADDR;
++	for (; i < cr_num_of_ac * 3; i++, addr += 4) {
++		if (i == cr_num_of_ac * 3 - 1 && is_mt7992(&dev->mt76))
++			sta_pause[i] = mt76_rr(dev, WF_DRR_TOP_AC2_STATION_PAUSE_EXT_00_ADDR);
++		else
++			sta_pause[i] = mt76_rr(dev, addr);
++	}
++
++	addr = WF_DRR_TOP_AC3_STATION_PAUSE00_ADDR;
++	for (; i < cr_num_of_ac * 4; i++, addr += 4) {
++		if (i == cr_num_of_ac * 4 - 1 && is_mt7992(&dev->mt76))
++			sta_pause[i] = mt76_rr(dev, WF_DRR_TOP_AC3_STATION_PAUSE_EXT_00_ADDR);
++		else
++			sta_pause[i] = mt76_rr(dev, addr);
++	}
++
++	/* TWT */
++	addr = WF_DRR_TOP_TWT_STA_MAP00_ADDR;
++	for (i = 0; i < cr_num_of_ac; i++, addr += 4) {
++		if (i == cr_num_of_ac - 1 && is_mt7992(&dev->mt76))
++			twt_pause[i] = mt76_rr(dev, WF_DRR_TOP_TWT_STA_MAP_EXT_00_ADDR);
++		else
++			twt_pause[i] = mt76_rr(dev, addr);
++	}
++}
++
++static void
++mt7996_get_ple_queue_info(struct mt7996_dev *dev, u32 pid, u32 qid, u32 tgid,
++			  u16 wlan_idx, u16 *hfid, u16 *tfid, u16 *pktcnt)
++{
++	u32 val = WF_PLE_TOP_FL_QUE_CTRL_0_EXECUTE_MASK |
++		  u32_encode_bits(pid, WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_PID_MASK) |
++		  u32_encode_bits(tgid, WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_TGID_MASK) |
++		  u32_encode_bits(qid, WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_QID_MASK) |
++		  u32_encode_bits(wlan_idx, WF_PLE_TOP_FL_QUE_CTRL_0_Q_BUF_WLANID_MASK);
++	mt76_wr(dev, WF_PLE_TOP_FL_QUE_CTRL_0_ADDR, val);
++
++	val = mt76_rr(dev, WF_PLE_TOP_FL_QUE_CTRL_2_ADDR);
++	*hfid = u32_get_bits(val, WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_HEAD_FID_MASK);
++	*tfid = u32_get_bits(val, WF_PLE_TOP_FL_QUE_CTRL_2_QUEUE_TAIL_FID_MASK);
++
++	val = mt76_rr(dev, WF_PLE_TOP_FL_QUE_CTRL_3_ADDR);
++	*pktcnt = u32_get_bits(val, WF_PLE_TOP_FL_QUE_CTRL_3_QUEUE_PKT_NUM_MASK);
++}
++
++static void
++mt7996_show_sta_acq_info(struct seq_file *s, unsigned long *ple_stat,
++			 u32 *sta_pause, u32 *twt_sta_pause)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	size_t cr_num_of_ac = ple_cr_num_of_ac(&dev->mt76);
++	size_t cr_num_of_all_ac = cr_num_of_ac * IEEE80211_NUM_ACS;
++	int i, j;
++
++	for (j = 0; j < cr_num_of_all_ac; j++) { /* show AC Q info */
++		for (i = 0; i < 32; i++) {
++			if (!test_bit(i, &ple_stat[j + 1])) {
++				u16 hfid, tfid, pktcnt, wlan_idx = i + (j % cr_num_of_ac) * 32;
++				u8 wmmidx, ctrl = 0, acq_idx = j / cr_num_of_ac;
++				struct mt7996_link_sta *mlink;
++				struct mt76_wcid *wcid;
++				size_t idx;
++
++				if (wlan_idx >= MT76_N_WCIDS) {
++					seq_printf(s, "Error: WCID %hu exceeded threshold.\n", wlan_idx);
++					continue;
++				}
++				wcid = rcu_dereference(dev->mt76.wcid[wlan_idx]);
++				if (!wcid) {
++					seq_printf(s, "Error: STA %hu does not exist.\n", wlan_idx);
++					continue;
++				}
++				mlink = container_of(wcid, struct mt7996_link_sta, wcid);
++				wmmidx = mlink->sta->vif->deflink.mt76.wmm_idx;
++
++				seq_printf(s, "\tSTA%hu AC%hhu: ", wlan_idx, acq_idx);
++				mt7996_get_ple_queue_info(dev, ENUM_UMAC_LMAC_PORT_2, acq_idx,
++							  0, wlan_idx, &hfid, &tfid, &pktcnt);
++				seq_printf(s, "tail/head fid = 0x%04x/0x%04x, pkt cnt = 0x%04x",
++					   tfid, hfid, pktcnt);
++
++				idx = wcid->phy_idx * cr_num_of_all_ac + j;
++				if (sta_pause[idx] & BIT(i))
++					ctrl = 2;
++
++				idx = wcid->phy_idx * cr_num_of_ac + j % cr_num_of_ac;
++				if (twt_sta_pause[idx] & BIT(i))
++					ctrl = 3;
++
++				seq_printf(s, ", ctrl = %s (wmmidx=%hhu, band=%hhu)\n",
++					   sta_ctrl_reg[ctrl], wmmidx, wcid->phy_idx);
++			}
++		}
++	}
++}
++
++static void
++mt7996_show_txcmdq_info(struct seq_file *s)
++{
++	const u32 txcmd_queue_empty_addr[__MT_MAX_BAND][2] = {
++		[MT_BAND0] = {WF_PLE_TOP_TXCMD_QUEUE_EMPTY_ADDR,
++			      WF_PLE_TOP_NATIVE_TXCMD_QUEUE_EMPTY_ADDR},
++		[MT_BAND1] = {WF_PLE_TOP_BN1_TXCMD_QUEUE_EMPTY_ADDR,
++			      WF_PLE_TOP_BN1_NATIVE_TXCMD_QUEUE_EMPTY_ADDR},
++		[MT_BAND2] = {WF_PLE_TOP_BN2_TXCMD_QUEUE_EMPTY_ADDR,
++			      WF_PLE_TOP_BN2_NATIVE_TXCMD_QUEUE_EMPTY_ADDR}
++	};
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	u8 band;
++
++	for (band = MT_BAND0; band < __MT_MAX_BAND; ++band) {
++		unsigned long txcmdq_stat, native_txcmdq_stat;
++		int i;
++
++		if (!dev->mt76.phys[band])
++			continue;
++
++		txcmdq_stat = mt76_rr(dev, txcmd_queue_empty_addr[band][0]);
++		native_txcmdq_stat = mt76_rr(dev, txcmd_queue_empty_addr[band][1]);
++
++		seq_printf(s, "Band%hhu Non-native/native TXCMD Queue Empty: 0x%08lx/0x%08lx\n",
++			   band, txcmdq_stat, native_txcmdq_stat);
++
++		for (i = 0; i < 32 ; i++) {
++			if (!test_bit(i, &native_txcmdq_stat)) {
++				struct bmac_queue_info_t *queue = &ple_txcmd_queue_empty_info[band][i];
++				u16 hfid, tfid, pktcnt;
++
++				if (!queue->QueueName)
++					continue;
++
++				seq_printf(s, "\t%s: ", queue->QueueName);
++				mt7996_get_ple_queue_info(dev, queue->Portid, queue->Queueid,
++							  0, 0, &hfid, &tfid, &pktcnt);
++				seq_printf(s, "tail/head fid = 0x%04x/0x%04x, pkt cnt = 0x%04x\n",
++					   tfid, hfid, pktcnt);
++			}
++		}
++	}
++}
++
++static int
++mt7996_pleinfo_read(struct seq_file *s, void *data)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	size_t cr_num_of_ac = ple_cr_num_of_ac(&dev->mt76);
++	size_t cr_num_of_all_ac = cr_num_of_ac * IEEE80211_NUM_ACS;
++	u32 *sta_pause, *twt_sta_pause;
++	unsigned long *ple_stat;
++	int i, j, ret = 0;
++
++	ple_stat = kzalloc((cr_num_of_all_ac + 1) * sizeof(unsigned long), GFP_KERNEL);
++	if (!ple_stat)
++		return -ENOMEM;
++
++	sta_pause = kzalloc(__MT_MAX_BAND * cr_num_of_all_ac * sizeof(u32), GFP_KERNEL);
++	if (!sta_pause) {
++		ret = -ENOMEM;
++		goto out;
++	}
++
++	twt_sta_pause = kzalloc(__MT_MAX_BAND * cr_num_of_ac * sizeof(u32), GFP_KERNEL);
++	if (!twt_sta_pause) {
++		ret = -ENOMEM;
++		goto out;
++	}
++
++	mt7996_show_ple_pg_info(dev, s);
++	mt7996_get_ple_acq_stat(dev, ple_stat);
++
++	for (i = MT_BAND0; i < __MT_MAX_BAND; i++) {
++		if (dev->mt76.phys[i])
++			mt7996_get_sta_pause(dev, i,
++					     sta_pause + i * cr_num_of_all_ac,
++					     twt_sta_pause + i * cr_num_of_ac);
++	}
++
++	if ((ple_stat[0] & WF_PLE_TOP_QUEUE_EMPTY_ALL_AC_EMPTY_MASK) == 0) {
++		for (j = 0; j < cr_num_of_all_ac; j++) {
++			if (j % cr_num_of_ac == 0)
++				seq_printf(s, "\n\tSTA in nonempty AC%ld TXD queue: ", j / cr_num_of_ac);
++
++			for (i = 0; i < 32; i++) {
++				if (!test_bit(i, &ple_stat[j + 1]))
++					seq_printf(s, "%lu ", i + (j % cr_num_of_ac) * 32);
++			}
++		}
++		seq_printf(s, "\n");
++	}
++
++	seq_printf(s, "Nonempty TXD Queue Info:\n");
++
++	for (i = 0; i < 32; i++) {
++		if (!test_bit(i, &ple_stat[0])) {
++			struct bmac_queue_info *queue = &ple_queue_empty_info[i];
++			u16 hfid, tfid, pktcnt;
++
++			if (!queue->QueueName)
++				continue;
++
++			seq_printf(s, "\t%s: ", queue->QueueName);
++			mt7996_get_ple_queue_info(dev, queue->Portid, queue->Queueid,
++						  queue->tgid, 0, &hfid, &tfid, &pktcnt);
++			seq_printf(s, "tail/head fid = 0x%04x/0x%04x, pkt cnt = 0x%04x\n",
++				   tfid, hfid, pktcnt);
++		}
++	}
++
++	mt7996_show_sta_acq_info(s, ple_stat, sta_pause, twt_sta_pause);
++	mt7996_show_txcmdq_info(s);
++
++	kfree(twt_sta_pause);
++out:
++	kfree(sta_pause);
++	kfree(ple_stat);
++	return ret;
++}
++
++/* DRR */
++static int
++mt7996_drr_info(struct seq_file *s, void *data)
++{
++	/* TODO: Wait MIB counter API implement complete */
++	return 0;
++}
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+@@ -3295,6 +4327,24 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ 
+ 	debugfs_create_file("thermal_enable", 0600, dir, phy, &fops_thermal_enable);
+ 	debugfs_create_file("thermal_recal", 0200, dir, dev, &fops_thermal_recal);
++	debugfs_create_file("reset_counter", 0200, dir, dev, &fops_reset_counter);
++
++	debugfs_create_devm_seqfile(dev->mt76.dev, "drr_info", dir,
++				    mt7996_drr_info);
++
++	debugfs_create_u32("token_idx", 0600, dir, &dev->dbg.token_idx);
++	debugfs_create_devm_seqfile(dev->mt76.dev, "rx_token", dir,
++				    mt7996_rx_token_read);
++
++	debugfs_create_devm_seqfile(dev->mt76.dev, "ple_info", dir,
++				    mt7996_pleinfo_read);
++	debugfs_create_devm_seqfile(dev->mt76.dev, "pse_info", dir,
++				    mt7996_pseinfo_read);
++	/* amsdu */
++	debugfs_create_file("amsdu_algo", 0600, dir, dev, &fops_amsdu_algo);
++	debugfs_create_file("amsdu_para", 0600, dir, dev, &fops_amsdu_para);
++	debugfs_create_devm_seqfile(dev->mt76.dev, "hw_amsdu_info", dir,
++	                            mt7996_hw_amsdu_info_read);
+ 
+ 	return 0;
+ }
+diff --git a/tools/fwlog.c b/tools/fwlog.c
+index 0e2de2dc..8d163e1d 100644
+--- a/tools/fwlog.c
++++ b/tools/fwlog.c
+@@ -94,7 +94,7 @@ static void handle_signal(int sig)
+ 	done = true;
+ }
+ 
+-static int mt76_log_socket(struct sockaddr_in *remote, char *ip)
++static int mt76_log_socket(struct sockaddr_in *remote, char *ip, unsigned short port)
+ {
+ 	struct sockaddr_in local = {
+ 		.sin_family = AF_INET,
+@@ -103,7 +103,7 @@ static int mt76_log_socket(struct sockaddr_in *remote, char *ip)
+ 	int s, ret;
+ 
+ 	remote->sin_family = AF_INET;
+-	remote->sin_port = htons(55688);
++	remote->sin_port = htons(port);
+ 	if (!inet_aton(ip, &remote->sin_addr)) {
+ 		fprintf(stderr, "Invalid destination IP address: %s\n", ip);
+ 		return -EINVAL;
+@@ -201,15 +201,37 @@ int mt76_fwlog(const char *phyname, int argc, char **argv)
+ {
+ 	struct sockaddr_in remote;
+ 	int in_fd, out_fd, ret;
++	char dev_ip[16] = {};
++	unsigned short port;
+ 
+-	if (argc < 1) {
+-		fprintf(stderr, "need destination address\n");
++	if (argc < 2) {
++		fprintf(stderr, "need destination address and fw_debug_bin\n");
+ 		return -EINVAL;
+ 	}
+ 
+-	out_fd = mt76_log_socket(&remote, argv[0]);
+-	if (out_fd < 0)
+-		return out_fd;
++
++	if (argc == 2) {
++		/* support ip:port format */
++		if (strchr(argv[0], ':')) {
++			sscanf(argv[0], "%[^:]:%hu", dev_ip, &port);
++		} else {
++			strncpy(dev_ip, argv[0], sizeof(dev_ip));
++			port = 55688;
++		}
++
++		out_fd = mt76_log_socket(&remote, dev_ip, port);
++		if (out_fd < 0)
++			return out_fd;
++	} else if (argc == 3) {
++		out_fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR);
++		if (out_fd < 0) {
++			perror("open");
++			return out_fd;
++		}
++	} else {
++		fprintf(stderr, "Too many arguments.\n");
++		return -EINVAL;
++	}
+ 
+ 	ret = mt76_set_fwlog_en(phyname, true, argv[1]);
+ 	if (ret)
+@@ -221,7 +243,7 @@ int mt76_fwlog(const char *phyname, int argc, char **argv)
+ 		goto disable;
+ 	}
+ 
+-	if (mt76_log_relay(in_fd, out_fd, &remote))
++	if (mt76_log_relay(in_fd, out_fd, argc == 3 ? NULL : &remote))
+ 		fprintf(stderr, "Failed to relay FW log.\n");
+ 
+ 	close(in_fd);
+@@ -240,7 +262,7 @@ int mt76_idxlog(const char *phyname, int argc, char **argv)
+ 	int in_fd, out_fd, ret;
+ 
+ 	if (argc) {
+-		out_fd = mt76_log_socket(&remote, argv[0]);
++		out_fd = mt76_log_socket(&remote, argv[0], 55688);
+ 		if (out_fd < 0)
+ 			return out_fd;
+ 	} else {
+diff --git a/tools/main.c b/tools/main.c
+index e9e25567..dcbb9b26 100644
+--- a/tools/main.c
++++ b/tools/main.c
+@@ -49,6 +49,7 @@ void usage(void)
+ 		"eeprom set <addr>=<val> [...]",
+ 		"eeprom changes",
+ 		"eeprom reset",
++		"fwlog <ip> <fw_debug_bin input> <fwlog name>",
+ 	};
+ 	int i;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0120-mtk-mt76-add-internal-debug-tool.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0120-mtk-mt76-add-internal-debug-tool.patch
new file mode 100644
index 0000000..99adad3
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0120-mtk-mt76-add-internal-debug-tool.patch
@@ -0,0 +1,1904 @@
+From a63950272ed7934e2777155cb8ea06bb82d75c33 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 22 Apr 2024 12:22:05 +0800
+Subject: [PATCH 120/199] mtk: mt76: add internal debug tool
+
+Add the following DebugFS knobs:
+- reset_counter: reset TX/RX statistical counters in FW, WTBL, and MT76.
+- per: show PER, which is calculated using MPDU-based statistics from CMDRPT-TX.
+
+Sources of statistics stored in Eagle mt76_sta_stats are summarized below.
+<tx_bytes> source: CMDRPT-TX
+<tx_packets> unit: MSDU. source: WTBL.
+<tx_retries> unit: MPDU. source: TX-Free-Done Event
+<tx_failed> unit: MPDU. source: TX-Free-Done Event
+<tx_total_mpdu_cnt> unit: MPDU. source: CMDRPT-TX
+<tx_failed_mpdu_cnt> unit: MPDU. source: CMDRPT-TX
+<rx_bytes> source: RXRPT
+<rx_packets> unit: MSDU. source: WTBL.
+<rx_errors> Not used.
+<rx_drops> Not used.
+
+Add token pending time
+
+Refactor DebugFS knob amsdu_info to read unambiguous CR addresses for HW-AMSDU information.
+
+Remove the duplicate function in mtk_debugfs.c & mtk_debug_i.c
+Only enable mt7996_mcu_fw_log_2_host function in mcu.c
+
+mtk: wifi: mt76: mt7996: add more ids support for eagle and kite
+
+IDS is the internal debug commands for firmware debug usage. This
+debugfs will be called only when we use chihuahua tool. Since MT7990 and
+MT7992 use the same firmware branch. This commit change some ids idx and
+support mode ids options as below:
+
+1. set fw_dbg=2:62 for MUCOP
+2. set fw_dbg=1:85 for BSRP
+3. set fw_dbg=1:86 for Tput Monitor
+4. set fw_dbg=1:100 for MLO
+5. set fw_dbg=1:101 for ERROR Log
+
+mtk: wifi: mt76: mt7996: revise DebugFS command ple_info to show correct TXCMD queue information
+
+Each band has its own set of TXCMD queues in PLE module.
+However, the original codebase only specifies one shared set of queues with wrong queue indices.
+
+mtk: wifi: mt76: mt7992: revise DebugFS command ple_info to accommodate Kite
+
+Because Kite only supports 512 STAs, the number of AC_QUEUE_EMPTY CRs is less than that of Eagle.
+Consequently, some related macros have to be revised to prevent reading wrong CRs.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/Makefile        |   3 +-
+ mt7996/debugfs.c       |   6 +-
+ mt7996/init.c          |   4 +
+ mt7996/mac.c           |   2 +
+ mt7996/mt7996.h        |  12 +
+ mt7996/mtk_debug_i.h   | 987 +++++++++++++++++++++++++++++++++++++++++
+ mt7996/mtk_debugfs_i.c | 720 ++++++++++++++++++++++++++++++
+ 7 files changed, 1732 insertions(+), 2 deletions(-)
+ create mode 100644 mt7996/mtk_debug_i.h
+ create mode 100644 mt7996/mtk_debugfs_i.c
+
+diff --git a/mt7996/Makefile b/mt7996/Makefile
+index 6643c7a3..49ec9154 100644
+--- a/mt7996/Makefile
++++ b/mt7996/Makefile
+@@ -1,4 +1,5 @@
+ # SPDX-License-Identifier: ISC
++EXTRA_CFLAGS += -Werror
+ EXTRA_CFLAGS += -DCONFIG_MT76_LEDS
+ EXTRA_CFLAGS += -DCONFIG_MTK_DEBUG
+ EXTRA_CFLAGS += -DCONFIG_MTK_VENDOR
+@@ -11,4 +12,4 @@ mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
+ mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o
+ mt7996e-$(CONFIG_NL80211_TESTMODE) += testmode.o
+ 
+-mt7996e-y += mtk_debugfs.o mtk_mcu.o
++mt7996e-y += mtk_debugfs.o mtk_mcu.o mtk_debugfs_i.o
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 63b8887d..2c553cf3 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -1118,8 +1118,12 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ 	debugfs_create_file("fw_debug_muru_disable", 0600, dir, dev,
+ 			    &fops_fw_debug_muru_disable);
+ 
+-	if (phy == &dev->phy)
++	if (phy == &dev->phy) {
+ 		dev->debugfs_dir = dir;
++#ifdef CONFIG_MTK_DEBUG
++		mt7996_mtk_init_debugfs_internal(phy, dir);
++#endif
++	}
+ 
+ #ifdef CONFIG_MTK_DEBUG
+ 	debugfs_create_u16("wlan_idx", 0600, dir, &dev->wlan_idx);
+diff --git a/mt7996/init.c b/mt7996/init.c
+index a1941869..057d20db 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -886,6 +886,10 @@ void mt7996_rro_hw_init(struct mt7996_dev *dev)
+ 		mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE0,
+ 			dev->wed_rro.addr_elem[0].phy_addr);
+ 	} else {
++		INIT_LIST_HEAD(&dev->wed_rro.pg_addr_cache);
++		for (i = 0; i < MT7996_RRO_MSDU_PG_HASH_SIZE; i++)
++			INIT_LIST_HEAD(&dev->wed_rro.pg_hash_head[i]);
++
+ 		/* TODO: remove line after WM has set */
+ 		mt76_clear(dev, WF_RRO_AXI_MST_CFG, WF_RRO_AXI_MST_CFG_DIDX_OK);
+ 
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 6462c64c..c6816ab5 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -334,6 +334,7 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ #ifdef CONFIG_MTK_DEBUG
+ 	if (dev->dbg.dump_rx_raw)
+ 		mt7996_packet_log_to_host(dev, skb->data, skb->len, PKT_BIN_DEBUG_RX_RAW, 0);
++	mt7996_dump_bmac_rxd_info(dev, rxd);
+ #endif
+ 	hw_aggr = status->aggr;
+ 	memset(status, 0, sizeof(*status));
+@@ -995,6 +996,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 		mt7996_packet_log_to_host(dev, txwi, MT_TXD_SIZE, PKT_BIN_DEBUG_TXD, 0);
+ 	if (dev->dbg.dump_tx_pkt)
+ 		mt7996_packet_log_to_host(dev, t->skb->data, t->skb->len, PKT_BIN_DEBUG_TX, 0);
++	mt7996_dump_bmac_txd_info(NULL, dev, (__le32 *)txwi, true, false);
+ #endif
+ 
+ 	return 0;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index ff13d00e..ef544957 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -101,6 +101,7 @@
+ 
+ #define MT7996_BUILD_TIME_LEN		24
+ 
++#define MT7996_RRO_MSDU_PG_HASH_SIZE	127
+ #define MT7996_RRO_MAX_SESSION		1024
+ #define MT7996_RRO_WINDOW_MAX_LEN	1024
+ #define MT7996_RRO_ADDR_ELEM_LEN	128
+@@ -688,6 +689,9 @@ struct mt7996_dev {
+ 		struct work_struct work;
+ 		struct list_head poll_list;
+ 		spinlock_t lock;
++
++		struct list_head pg_addr_cache;
++		struct list_head pg_hash_head[MT7996_RRO_MSDU_PG_HASH_SIZE];
+ 	} wed_rro;
+ 
+ 	bool testmode_enable;
+@@ -744,7 +748,11 @@ struct mt7996_dev {
+ 		bool dump_tx_pkt:1;
+ 		bool dump_rx_pkt:1;
+ 		bool dump_rx_raw:1;
++		u8 dump_ple_txd;
+ 		u32 token_idx;
++		u32 rxd_read_cnt;
++		u32 txd_read_cnt;
++		u32 fid_idx;
+ 	} dbg;
+ 	const struct mt7996_dbg_reg_desc *dbg_reg;
+ #endif
+@@ -1288,6 +1296,10 @@ enum {
+ };
+ 
+ void mt7996_packet_log_to_host(struct mt7996_dev *dev, const void *data, int len, int type, int des_len);
++void mt7996_dump_bmac_rxd_info(struct mt7996_dev *dev, __le32 *rxd);
++void mt7996_dump_bmac_txd_info(struct seq_file *s, struct mt7996_dev *dev,
++			       __le32 *txd, bool is_hif_txd, bool dump_txp);
++int mt7996_mtk_init_debugfs_internal(struct mt7996_phy *phy, struct dentry *dir);
+ #endif
+ 
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mtk_debug_i.h b/mt7996/mtk_debug_i.h
+new file mode 100644
+index 00000000..d3756fa2
+--- /dev/null
++++ b/mt7996/mtk_debug_i.h
+@@ -0,0 +1,987 @@
++#ifndef __MTK_DEBUG_I_H
++#define __MTK_DEBUG_I_H
++
++#ifdef CONFIG_MTK_DEBUG
++
++// DW0
++#define WF_RX_DESCRIPTOR_RX_BYTE_COUNT_DW                                   0
++#define WF_RX_DESCRIPTOR_RX_BYTE_COUNT_ADDR                                 0
++#define WF_RX_DESCRIPTOR_RX_BYTE_COUNT_MASK                                 0x0000ffff // 15- 0
++#define WF_RX_DESCRIPTOR_RX_BYTE_COUNT_SHIFT                                0
++#define WF_RX_DESCRIPTOR_PACKET_TYPE_DW                                     0
++#define WF_RX_DESCRIPTOR_PACKET_TYPE_ADDR                                   0
++#define WF_RX_DESCRIPTOR_PACKET_TYPE_MASK                                   0xf8000000 // 31-27
++#define WF_RX_DESCRIPTOR_PACKET_TYPE_SHIFT                                  27
++// DW1
++#define WF_RX_DESCRIPTOR_MLD_ID_DW                                          1
++#define WF_RX_DESCRIPTOR_MLD_ID_ADDR                                        4
++#define WF_RX_DESCRIPTOR_MLD_ID_MASK                                        0x00000fff // 11- 0
++#define WF_RX_DESCRIPTOR_MLD_ID_SHIFT                                       0
++#define WF_RX_DESCRIPTOR_GROUP_VLD_DW                                       1
++#define WF_RX_DESCRIPTOR_GROUP_VLD_ADDR                                     4
++#define WF_RX_DESCRIPTOR_GROUP_VLD_MASK                                     0x001f0000 // 20-16
++#define WF_RX_DESCRIPTOR_GROUP_VLD_SHIFT                                    16
++#define WF_RX_DESCRIPTOR_KID_DW                                             1
++#define WF_RX_DESCRIPTOR_KID_ADDR                                           4
++#define WF_RX_DESCRIPTOR_KID_MASK                                           0x00600000 // 22-21
++#define WF_RX_DESCRIPTOR_KID_SHIFT                                          21
++#define WF_RX_DESCRIPTOR_CM_DW                                              1
++#define WF_RX_DESCRIPTOR_CM_ADDR                                            4
++#define WF_RX_DESCRIPTOR_CM_MASK                                            0x00800000 // 23-23
++#define WF_RX_DESCRIPTOR_CM_SHIFT                                           23
++#define WF_RX_DESCRIPTOR_CLM_DW                                             1
++#define WF_RX_DESCRIPTOR_CLM_ADDR                                           4
++#define WF_RX_DESCRIPTOR_CLM_MASK                                           0x01000000 // 24-24
++#define WF_RX_DESCRIPTOR_CLM_SHIFT                                          24
++#define WF_RX_DESCRIPTOR_I_DW                                               1
++#define WF_RX_DESCRIPTOR_I_ADDR                                             4
++#define WF_RX_DESCRIPTOR_I_MASK                                             0x02000000 // 25-25
++#define WF_RX_DESCRIPTOR_I_SHIFT                                            25
++#define WF_RX_DESCRIPTOR_T_DW                                               1
++#define WF_RX_DESCRIPTOR_T_ADDR                                             4
++#define WF_RX_DESCRIPTOR_T_MASK                                             0x04000000 // 26-26
++#define WF_RX_DESCRIPTOR_T_SHIFT                                            26
++#define WF_RX_DESCRIPTOR_BN_DW                                              1
++#define WF_RX_DESCRIPTOR_BN_ADDR                                            4
++#define WF_RX_DESCRIPTOR_BN_MASK                                            0x18000000 // 28-27
++#define WF_RX_DESCRIPTOR_BN_SHIFT                                           27
++#define WF_RX_DESCRIPTOR_BIPN_FAIL_DW                                       1
++#define WF_RX_DESCRIPTOR_BIPN_FAIL_ADDR                                     4
++#define WF_RX_DESCRIPTOR_BIPN_FAIL_MASK                                     0x20000000 // 29-29
++#define WF_RX_DESCRIPTOR_BIPN_FAIL_SHIFT                                    29
++// DW2
++#define WF_RX_DESCRIPTOR_BSSID_DW                                           2
++#define WF_RX_DESCRIPTOR_BSSID_ADDR                                         8
++#define WF_RX_DESCRIPTOR_BSSID_MASK                                         0x0000003f //  5- 0
++#define WF_RX_DESCRIPTOR_BSSID_SHIFT                                        0
++#define WF_RX_DESCRIPTOR_H_DW                                               2
++#define WF_RX_DESCRIPTOR_H_ADDR                                             8
++#define WF_RX_DESCRIPTOR_H_MASK                                             0x00000080 //  7- 7
++#define WF_RX_DESCRIPTOR_H_SHIFT                                            7
++#define WF_RX_DESCRIPTOR_HEADER_LENGTH_DW                                   2
++#define WF_RX_DESCRIPTOR_HEADER_LENGTH_ADDR                                 8
++#define WF_RX_DESCRIPTOR_HEADER_LENGTH_MASK                                 0x00001f00 // 12- 8
++#define WF_RX_DESCRIPTOR_HEADER_LENGTH_SHIFT                                8
++#define WF_RX_DESCRIPTOR_HO_DW                                              2
++#define WF_RX_DESCRIPTOR_HO_ADDR                                            8
++#define WF_RX_DESCRIPTOR_HO_MASK                                            0x0000e000 // 15-13
++#define WF_RX_DESCRIPTOR_HO_SHIFT                                           13
++#define WF_RX_DESCRIPTOR_SEC_MODE_DW                                        2
++#define WF_RX_DESCRIPTOR_SEC_MODE_ADDR                                      8
++#define WF_RX_DESCRIPTOR_SEC_MODE_MASK                                      0x001f0000 // 20-16
++#define WF_RX_DESCRIPTOR_SEC_MODE_SHIFT                                     16
++#define WF_RX_DESCRIPTOR_MUBAR_DW                                           2
++#define WF_RX_DESCRIPTOR_MUBAR_ADDR                                         8
++#define WF_RX_DESCRIPTOR_MUBAR_MASK                                         0x00200000 // 21-21
++#define WF_RX_DESCRIPTOR_MUBAR_SHIFT                                        21
++#define WF_RX_DESCRIPTOR_SWBIT_DW                                           2
++#define WF_RX_DESCRIPTOR_SWBIT_ADDR                                         8
++#define WF_RX_DESCRIPTOR_SWBIT_MASK                                         0x00400000 // 22-22
++#define WF_RX_DESCRIPTOR_SWBIT_SHIFT                                        22
++#define WF_RX_DESCRIPTOR_DAF_DW                                             2
++#define WF_RX_DESCRIPTOR_DAF_ADDR                                           8
++#define WF_RX_DESCRIPTOR_DAF_MASK                                           0x00800000 // 23-23
++#define WF_RX_DESCRIPTOR_DAF_SHIFT                                          23
++#define WF_RX_DESCRIPTOR_EL_DW                                              2
++#define WF_RX_DESCRIPTOR_EL_ADDR                                            8
++#define WF_RX_DESCRIPTOR_EL_MASK                                            0x01000000 // 24-24
++#define WF_RX_DESCRIPTOR_EL_SHIFT                                           24
++#define WF_RX_DESCRIPTOR_HTF_DW                                             2
++#define WF_RX_DESCRIPTOR_HTF_ADDR                                           8
++#define WF_RX_DESCRIPTOR_HTF_MASK                                           0x02000000 // 25-25
++#define WF_RX_DESCRIPTOR_HTF_SHIFT                                          25
++#define WF_RX_DESCRIPTOR_INTF_DW                                            2
++#define WF_RX_DESCRIPTOR_INTF_ADDR                                          8
++#define WF_RX_DESCRIPTOR_INTF_MASK                                          0x04000000 // 26-26
++#define WF_RX_DESCRIPTOR_INTF_SHIFT                                         26
++#define WF_RX_DESCRIPTOR_FRAG_DW                                            2
++#define WF_RX_DESCRIPTOR_FRAG_ADDR                                          8
++#define WF_RX_DESCRIPTOR_FRAG_MASK                                          0x08000000 // 27-27
++#define WF_RX_DESCRIPTOR_FRAG_SHIFT                                         27
++#define WF_RX_DESCRIPTOR_NUL_DW                                             2
++#define WF_RX_DESCRIPTOR_NUL_ADDR                                           8
++#define WF_RX_DESCRIPTOR_NUL_MASK                                           0x10000000 // 28-28
++#define WF_RX_DESCRIPTOR_NUL_SHIFT                                          28
++#define WF_RX_DESCRIPTOR_NDATA_DW                                           2
++#define WF_RX_DESCRIPTOR_NDATA_ADDR                                         8
++#define WF_RX_DESCRIPTOR_NDATA_MASK                                         0x20000000 // 29-29
++#define WF_RX_DESCRIPTOR_NDATA_SHIFT                                        29
++#define WF_RX_DESCRIPTOR_NAMP_DW                                            2
++#define WF_RX_DESCRIPTOR_NAMP_ADDR                                          8
++#define WF_RX_DESCRIPTOR_NAMP_MASK                                          0x40000000 // 30-30
++#define WF_RX_DESCRIPTOR_NAMP_SHIFT                                         30
++#define WF_RX_DESCRIPTOR_BF_RPT_DW                                          2
++#define WF_RX_DESCRIPTOR_BF_RPT_ADDR                                        8
++#define WF_RX_DESCRIPTOR_BF_RPT_MASK                                        0x80000000 // 31-31
++#define WF_RX_DESCRIPTOR_BF_RPT_SHIFT                                       31
++// DW3
++#define WF_RX_DESCRIPTOR_RXV_SN_DW                                          3
++#define WF_RX_DESCRIPTOR_RXV_SN_ADDR                                        12
++#define WF_RX_DESCRIPTOR_RXV_SN_MASK                                        0x000000ff //  7- 0
++#define WF_RX_DESCRIPTOR_RXV_SN_SHIFT                                       0
++#define WF_RX_DESCRIPTOR_CH_FREQUENCY_DW                                    3
++#define WF_RX_DESCRIPTOR_CH_FREQUENCY_ADDR                                  12
++#define WF_RX_DESCRIPTOR_CH_FREQUENCY_MASK                                  0x0000ff00 // 15- 8
++#define WF_RX_DESCRIPTOR_CH_FREQUENCY_SHIFT                                 8
++#define WF_RX_DESCRIPTOR_A1_TYPE_DW                                         3
++#define WF_RX_DESCRIPTOR_A1_TYPE_ADDR                                       12
++#define WF_RX_DESCRIPTOR_A1_TYPE_MASK                                       0x00030000 // 17-16
++#define WF_RX_DESCRIPTOR_A1_TYPE_SHIFT                                      16
++#define WF_RX_DESCRIPTOR_HTC_DW                                             3
++#define WF_RX_DESCRIPTOR_HTC_ADDR                                           12
++#define WF_RX_DESCRIPTOR_HTC_MASK                                           0x00040000 // 18-18
++#define WF_RX_DESCRIPTOR_HTC_SHIFT                                          18
++#define WF_RX_DESCRIPTOR_TCL_DW                                             3
++#define WF_RX_DESCRIPTOR_TCL_ADDR                                           12
++#define WF_RX_DESCRIPTOR_TCL_MASK                                           0x00080000 // 19-19
++#define WF_RX_DESCRIPTOR_TCL_SHIFT                                          19
++#define WF_RX_DESCRIPTOR_BBM_DW                                             3
++#define WF_RX_DESCRIPTOR_BBM_ADDR                                           12
++#define WF_RX_DESCRIPTOR_BBM_MASK                                           0x00100000 // 20-20
++#define WF_RX_DESCRIPTOR_BBM_SHIFT                                          20
++#define WF_RX_DESCRIPTOR_BU_DW                                              3
++#define WF_RX_DESCRIPTOR_BU_ADDR                                            12
++#define WF_RX_DESCRIPTOR_BU_MASK                                            0x00200000 // 21-21
++#define WF_RX_DESCRIPTOR_BU_SHIFT                                           21
++#define WF_RX_DESCRIPTOR_CO_ANT_DW                                          3
++#define WF_RX_DESCRIPTOR_CO_ANT_ADDR                                        12
++#define WF_RX_DESCRIPTOR_CO_ANT_MASK                                        0x00400000 // 22-22
++#define WF_RX_DESCRIPTOR_CO_ANT_SHIFT                                       22
++#define WF_RX_DESCRIPTOR_BF_CQI_DW                                          3
++#define WF_RX_DESCRIPTOR_BF_CQI_ADDR                                        12
++#define WF_RX_DESCRIPTOR_BF_CQI_MASK                                        0x00800000 // 23-23
++#define WF_RX_DESCRIPTOR_BF_CQI_SHIFT                                       23
++#define WF_RX_DESCRIPTOR_FC_DW                                              3
++#define WF_RX_DESCRIPTOR_FC_ADDR                                            12
++#define WF_RX_DESCRIPTOR_FC_MASK                                            0x01000000 // 24-24
++#define WF_RX_DESCRIPTOR_FC_SHIFT                                           24
++#define WF_RX_DESCRIPTOR_VLAN_DW                                            3
++#define WF_RX_DESCRIPTOR_VLAN_ADDR                                          12
++#define WF_RX_DESCRIPTOR_VLAN_MASK                                          0x80000000 // 31-31
++#define WF_RX_DESCRIPTOR_VLAN_SHIFT                                         31
++// DW4
++#define WF_RX_DESCRIPTOR_PF_DW                                              4
++#define WF_RX_DESCRIPTOR_PF_ADDR                                            16
++#define WF_RX_DESCRIPTOR_PF_MASK                                            0x00000003 //  1- 0
++#define WF_RX_DESCRIPTOR_PF_SHIFT                                           0
++#define WF_RX_DESCRIPTOR_MAC_DW                                             4
++#define WF_RX_DESCRIPTOR_MAC_ADDR                                           16
++#define WF_RX_DESCRIPTOR_MAC_MASK                                           0x00000004 //  2- 2
++#define WF_RX_DESCRIPTOR_MAC_SHIFT                                          2
++#define WF_RX_DESCRIPTOR_TID_DW                                             4
++#define WF_RX_DESCRIPTOR_TID_ADDR                                           16
++#define WF_RX_DESCRIPTOR_TID_MASK                                           0x00000078 //  6- 3
++#define WF_RX_DESCRIPTOR_TID_SHIFT                                          3
++#define WF_RX_DESCRIPTOR_ETHER_TYPE_OFFSET_DW                               4
++#define WF_RX_DESCRIPTOR_ETHER_TYPE_OFFSET_ADDR                             16
++#define WF_RX_DESCRIPTOR_ETHER_TYPE_OFFSET_MASK                             0x00003f80 // 13- 7
++#define WF_RX_DESCRIPTOR_ETHER_TYPE_OFFSET_SHIFT                            7
++#define WF_RX_DESCRIPTOR_IP_DW                                              4
++#define WF_RX_DESCRIPTOR_IP_ADDR                                            16
++#define WF_RX_DESCRIPTOR_IP_MASK                                            0x00004000 // 14-14
++#define WF_RX_DESCRIPTOR_IP_SHIFT                                           14
++#define WF_RX_DESCRIPTOR_UT_DW                                              4
++#define WF_RX_DESCRIPTOR_UT_ADDR                                            16
++#define WF_RX_DESCRIPTOR_UT_MASK                                            0x00008000 // 15-15
++#define WF_RX_DESCRIPTOR_UT_SHIFT                                           15
++#define WF_RX_DESCRIPTOR_PSE_FID_DW                                         4
++#define WF_RX_DESCRIPTOR_PSE_FID_ADDR                                       16
++#define WF_RX_DESCRIPTOR_PSE_FID_MASK                                       0x0fff0000 // 27-16
++#define WF_RX_DESCRIPTOR_PSE_FID_SHIFT                                      16
++// DW5
++// DW6
++#define WF_RX_DESCRIPTOR_CLS_BITMAP_31_0__DW                                6
++#define WF_RX_DESCRIPTOR_CLS_BITMAP_31_0__ADDR                              24
++#define WF_RX_DESCRIPTOR_CLS_BITMAP_31_0__MASK                              0xffffffff // 31- 0
++#define WF_RX_DESCRIPTOR_CLS_BITMAP_31_0__SHIFT                             0
++// DW7
++#define WF_RX_DESCRIPTOR_CLS_BITMAP_33_32__DW                               7
++#define WF_RX_DESCRIPTOR_CLS_BITMAP_33_32__ADDR                             28
++#define WF_RX_DESCRIPTOR_CLS_BITMAP_33_32__MASK                             0x00000003 //  1- 0
++#define WF_RX_DESCRIPTOR_CLS_BITMAP_33_32__SHIFT                            0
++#define WF_RX_DESCRIPTOR_DP_DW                                              7
++#define WF_RX_DESCRIPTOR_DP_ADDR                                            28
++#define WF_RX_DESCRIPTOR_DP_MASK                                            0x00080000 // 19-19
++#define WF_RX_DESCRIPTOR_DP_SHIFT                                           19
++#define WF_RX_DESCRIPTOR_CLS_DW                                             7
++#define WF_RX_DESCRIPTOR_CLS_ADDR                                           28
++#define WF_RX_DESCRIPTOR_CLS_MASK                                           0x00100000 // 20-20
++#define WF_RX_DESCRIPTOR_CLS_SHIFT                                          20
++#define WF_RX_DESCRIPTOR_OFLD_DW                                            7
++#define WF_RX_DESCRIPTOR_OFLD_ADDR                                          28
++#define WF_RX_DESCRIPTOR_OFLD_MASK                                          0x00600000 // 22-21
++#define WF_RX_DESCRIPTOR_OFLD_SHIFT                                         21
++#define WF_RX_DESCRIPTOR_MGC_DW                                             7
++#define WF_RX_DESCRIPTOR_MGC_ADDR                                           28
++#define WF_RX_DESCRIPTOR_MGC_MASK                                           0x00800000 // 23-23
++#define WF_RX_DESCRIPTOR_MGC_SHIFT                                          23
++#define WF_RX_DESCRIPTOR_WOL_DW                                             7
++#define WF_RX_DESCRIPTOR_WOL_ADDR                                           28
++#define WF_RX_DESCRIPTOR_WOL_MASK                                           0x1f000000 // 28-24
++#define WF_RX_DESCRIPTOR_WOL_SHIFT                                          24
++#define WF_RX_DESCRIPTOR_PF_MODE_DW                                         7
++#define WF_RX_DESCRIPTOR_PF_MODE_ADDR                                       28
++#define WF_RX_DESCRIPTOR_PF_MODE_MASK                                       0x20000000 // 29-29
++#define WF_RX_DESCRIPTOR_PF_MODE_SHIFT                                      29
++#define WF_RX_DESCRIPTOR_PF_STS_DW                                          7
++#define WF_RX_DESCRIPTOR_PF_STS_ADDR                                        28
++#define WF_RX_DESCRIPTOR_PF_STS_MASK                                        0xc0000000 // 31-30
++#define WF_RX_DESCRIPTOR_PF_STS_SHIFT                                       30
++// DW8
++#define WF_RX_DESCRIPTOR_FRAME_CONTROL_FIELD_DW                             8
++#define WF_RX_DESCRIPTOR_FRAME_CONTROL_FIELD_ADDR                           32
++#define WF_RX_DESCRIPTOR_FRAME_CONTROL_FIELD_MASK                           0x0000ffff // 15- 0
++#define WF_RX_DESCRIPTOR_FRAME_CONTROL_FIELD_SHIFT                          0
++#define WF_RX_DESCRIPTOR_PEER_MLD_ADDRESS_15_0__DW                          8
++#define WF_RX_DESCRIPTOR_PEER_MLD_ADDRESS_15_0__ADDR                        32
++#define WF_RX_DESCRIPTOR_PEER_MLD_ADDRESS_15_0__MASK                        0xffff0000 // 31-16
++#define WF_RX_DESCRIPTOR_PEER_MLD_ADDRESS_15_0__SHIFT                       16
++// DW9
++#define WF_RX_DESCRIPTOR_PEER_MLD_ADDRESS_47_16__DW                         9
++#define WF_RX_DESCRIPTOR_PEER_MLD_ADDRESS_47_16__ADDR                       36
++#define WF_RX_DESCRIPTOR_PEER_MLD_ADDRESS_47_16__MASK                       0xffffffff // 31- 0
++#define WF_RX_DESCRIPTOR_PEER_MLD_ADDRESS_47_16__SHIFT                      0
++// DW10
++#define WF_RX_DESCRIPTOR_FRAGMENT_NUMBER_DW                                 10
++#define WF_RX_DESCRIPTOR_FRAGMENT_NUMBER_ADDR                               40
++#define WF_RX_DESCRIPTOR_FRAGMENT_NUMBER_MASK                               0x0000000f //  3- 0
++#define WF_RX_DESCRIPTOR_FRAGMENT_NUMBER_SHIFT                              0
++#define WF_RX_DESCRIPTOR_SEQUENCE_NUMBER_DW                                 10
++#define WF_RX_DESCRIPTOR_SEQUENCE_NUMBER_ADDR                               40
++#define WF_RX_DESCRIPTOR_SEQUENCE_NUMBER_MASK                               0x0000fff0 // 15- 4
++#define WF_RX_DESCRIPTOR_SEQUENCE_NUMBER_SHIFT                              4
++#define WF_RX_DESCRIPTOR_QOS_CONTROL_FIELD_DW                               10
++#define WF_RX_DESCRIPTOR_QOS_CONTROL_FIELD_ADDR                             40
++#define WF_RX_DESCRIPTOR_QOS_CONTROL_FIELD_MASK                             0xffff0000 // 31-16
++#define WF_RX_DESCRIPTOR_QOS_CONTROL_FIELD_SHIFT                            16
++// DW11
++#define WF_RX_DESCRIPTOR_HT_CONTROL_FIELD_DW                                11
++#define WF_RX_DESCRIPTOR_HT_CONTROL_FIELD_ADDR                              44
++#define WF_RX_DESCRIPTOR_HT_CONTROL_FIELD_MASK                              0xffffffff // 31- 0
++#define WF_RX_DESCRIPTOR_HT_CONTROL_FIELD_SHIFT                             0
++// DW12
++#define WF_RX_DESCRIPTOR_PN_31_0__DW                                        12
++#define WF_RX_DESCRIPTOR_PN_31_0__ADDR                                      48
++#define WF_RX_DESCRIPTOR_PN_31_0__MASK                                      0xffffffff // 31- 0
++#define WF_RX_DESCRIPTOR_PN_31_0__SHIFT                                     0
++// DW13
++#define WF_RX_DESCRIPTOR_PN_63_32__DW                                       13
++#define WF_RX_DESCRIPTOR_PN_63_32__ADDR                                     52
++#define WF_RX_DESCRIPTOR_PN_63_32__MASK                                     0xffffffff // 31- 0
++#define WF_RX_DESCRIPTOR_PN_63_32__SHIFT                                    0
++// DW14
++#define WF_RX_DESCRIPTOR_PN_95_64__DW                                       14
++#define WF_RX_DESCRIPTOR_PN_95_64__ADDR                                     56
++#define WF_RX_DESCRIPTOR_PN_95_64__MASK                                     0xffffffff // 31- 0
++#define WF_RX_DESCRIPTOR_PN_95_64__SHIFT                                    0
++// DW15
++#define WF_RX_DESCRIPTOR_PN_127_96__DW                                      15
++#define WF_RX_DESCRIPTOR_PN_127_96__ADDR                                    60
++#define WF_RX_DESCRIPTOR_PN_127_96__MASK                                    0xffffffff // 31- 0
++#define WF_RX_DESCRIPTOR_PN_127_96__SHIFT                                   0
++// DW16
++#define WF_RX_DESCRIPTOR_TIMESTAMP_DW                                       16
++#define WF_RX_DESCRIPTOR_TIMESTAMP_ADDR                                     64
++#define WF_RX_DESCRIPTOR_TIMESTAMP_MASK                                     0xffffffff // 31- 0
++#define WF_RX_DESCRIPTOR_TIMESTAMP_SHIFT                                    0
++// DW17
++#define WF_RX_DESCRIPTOR_CRC_DW                                             17
++#define WF_RX_DESCRIPTOR_CRC_ADDR                                           68
++#define WF_RX_DESCRIPTOR_CRC_MASK                                           0xffffffff // 31- 0
++#define WF_RX_DESCRIPTOR_CRC_SHIFT                                          0
++// DW18
++// DW19
++// DW20
++#define WF_RX_DESCRIPTOR_P_RXV_DW                                           20
++#define WF_RX_DESCRIPTOR_P_RXV_ADDR                                         80
++#define WF_RX_DESCRIPTOR_P_RXV_MASK                                         0xffffffff // 31- 0
++#define WF_RX_DESCRIPTOR_P_RXV_SHIFT                                        0
++// DW21
++// DO NOT process repeat field(p_rxv)
++// DW22
++#define WF_RX_DESCRIPTOR_DBW_DW                                             22
++#define WF_RX_DESCRIPTOR_DBW_ADDR                                           88
++#define WF_RX_DESCRIPTOR_DBW_MASK                                           0x00000007 //  2- 0
++#define WF_RX_DESCRIPTOR_DBW_SHIFT                                          0
++#define WF_RX_DESCRIPTOR_GI_DW                                              22
++#define WF_RX_DESCRIPTOR_GI_ADDR                                            88
++#define WF_RX_DESCRIPTOR_GI_MASK                                            0x00000018 //  4- 3
++#define WF_RX_DESCRIPTOR_GI_SHIFT                                           3
++#define WF_RX_DESCRIPTOR_DCM_DW                                             22
++#define WF_RX_DESCRIPTOR_DCM_ADDR                                           88
++#define WF_RX_DESCRIPTOR_DCM_MASK                                           0x00000020 //  5- 5
++#define WF_RX_DESCRIPTOR_DCM_SHIFT                                          5
++#define WF_RX_DESCRIPTOR_NUM_RX_DW                                          22
++#define WF_RX_DESCRIPTOR_NUM_RX_ADDR                                        88
++#define WF_RX_DESCRIPTOR_NUM_RX_MASK                                        0x000001c0 //  8- 6
++#define WF_RX_DESCRIPTOR_NUM_RX_SHIFT                                       6
++#define WF_RX_DESCRIPTOR_STBC_DW                                            22
++#define WF_RX_DESCRIPTOR_STBC_ADDR                                          88
++#define WF_RX_DESCRIPTOR_STBC_MASK                                          0x00000600 // 10- 9
++#define WF_RX_DESCRIPTOR_STBC_SHIFT                                         9
++#define WF_RX_DESCRIPTOR_TX_MODE_DW                                         22
++#define WF_RX_DESCRIPTOR_TX_MODE_ADDR                                       88
++#define WF_RX_DESCRIPTOR_TX_MODE_MASK                                       0x00007800 // 14-11
++#define WF_RX_DESCRIPTOR_TX_MODE_SHIFT                                      11
++// DW23
++#define WF_RX_DESCRIPTOR_RCPI_DW                                            23
++#define WF_RX_DESCRIPTOR_RCPI_ADDR                                          92
++#define WF_RX_DESCRIPTOR_RCPI_MASK                                          0xffffffff // 31- 0
++#define WF_RX_DESCRIPTOR_RCPI_SHIFT                                         0
++// DW24
++#define WF_RX_DESCRIPTOR_C_RXV_DW                                           24
++#define WF_RX_DESCRIPTOR_C_RXV_ADDR                                         96
++#define WF_RX_DESCRIPTOR_C_RXV_MASK                                         0xffffffff // 31- 0
++#define WF_RX_DESCRIPTOR_C_RXV_SHIFT                                        0
++// DW25
++// DO NOT process repeat field(c_rxv)
++// DW26
++// DO NOT process repeat field(c_rxv)
++// DW27
++// DO NOT process repeat field(c_rxv)
++// DW28
++// DO NOT process repeat field(c_rxv)
++// DW29
++// DO NOT process repeat field(c_rxv)
++// DW30
++// DO NOT process repeat field(c_rxv)
++// DW31
++// DO NOT process repeat field(c_rxv)
++// DW32
++// DO NOT process repeat field(c_rxv)
++// DW33
++// DO NOT process repeat field(c_rxv)
++// DW34
++// DO NOT process repeat field(c_rxv)
++// DW35
++// DO NOT process repeat field(c_rxv)
++// DW36
++// DO NOT process repeat field(c_rxv)
++// DW37
++// DO NOT process repeat field(c_rxv)
++// DW38
++// DO NOT process repeat field(c_rxv)
++// DW39
++// DO NOT process repeat field(c_rxv)
++// DW40
++// DO NOT process repeat field(c_rxv)
++// DW41
++// DO NOT process repeat field(c_rxv)
++// DW42
++// DO NOT process repeat field(c_rxv)
++// DW43
++// DO NOT process repeat field(c_rxv)
++// DW44
++// DO NOT process repeat field(c_rxv)
++// DW45
++// DO NOT process repeat field(c_rxv)
++// DW46
++// DW47
++
++/* TXD */
++// DW0
++#define WF_TX_DESCRIPTOR_TX_BYTE_COUNT_DW                                   0
++#define WF_TX_DESCRIPTOR_TX_BYTE_COUNT_ADDR                                 0
++#define WF_TX_DESCRIPTOR_TX_BYTE_COUNT_MASK                                 0x0000ffff // 15- 0
++#define WF_TX_DESCRIPTOR_TX_BYTE_COUNT_SHIFT                                0
++#define WF_TX_DESCRIPTOR_ETHER_TYPE_OFFSET_DW                               0
++#define WF_TX_DESCRIPTOR_ETHER_TYPE_OFFSET_ADDR                             0
++#define WF_TX_DESCRIPTOR_ETHER_TYPE_OFFSET_MASK                             0x007f0000 // 22-16
++#define WF_TX_DESCRIPTOR_ETHER_TYPE_OFFSET_SHIFT                            16
++#define WF_TX_DESCRIPTOR_PKT_FT_DW                                          0
++#define WF_TX_DESCRIPTOR_PKT_FT_ADDR                                        0
++#define WF_TX_DESCRIPTOR_PKT_FT_MASK                                        0x01800000 // 24-23
++#define WF_TX_DESCRIPTOR_PKT_FT_SHIFT                                       23
++#define WF_TX_DESCRIPTOR_Q_IDX_DW                                           0
++#define WF_TX_DESCRIPTOR_Q_IDX_ADDR                                         0
++#define WF_TX_DESCRIPTOR_Q_IDX_MASK                                         0xfe000000 // 31-25
++#define WF_TX_DESCRIPTOR_Q_IDX_SHIFT                                        25
++// DW1
++#define WF_TX_DESCRIPTOR_MLD_ID_DW                                          1
++#define WF_TX_DESCRIPTOR_MLD_ID_ADDR                                        4
++#define WF_TX_DESCRIPTOR_MLD_ID_MASK                                        0x00000fff // 11- 0
++#define WF_TX_DESCRIPTOR_MLD_ID_SHIFT                                       0
++#define WF_TX_DESCRIPTOR_TGID_DW                                            1
++#define WF_TX_DESCRIPTOR_TGID_ADDR                                          4
++#define WF_TX_DESCRIPTOR_TGID_MASK                                          0x00003000 // 13-12
++#define WF_TX_DESCRIPTOR_TGID_SHIFT                                         12
++#define WF_TX_DESCRIPTOR_HF_DW                                              1
++#define WF_TX_DESCRIPTOR_HF_ADDR                                            4
++#define WF_TX_DESCRIPTOR_HF_MASK                                            0x0000c000 // 15-14
++#define WF_TX_DESCRIPTOR_HF_SHIFT                                           14
++#define WF_TX_DESCRIPTOR_HEADER_LENGTH_DW                                   1
++#define WF_TX_DESCRIPTOR_HEADER_LENGTH_ADDR                                 4
++#define WF_TX_DESCRIPTOR_HEADER_LENGTH_MASK                                 0x001f0000 // 20-16
++#define WF_TX_DESCRIPTOR_HEADER_LENGTH_SHIFT                                16
++#define WF_TX_DESCRIPTOR_MRD_DW                                             1
++#define WF_TX_DESCRIPTOR_MRD_ADDR                                           4
++#define WF_TX_DESCRIPTOR_MRD_MASK                                           0x00010000 // 16-16
++#define WF_TX_DESCRIPTOR_MRD_SHIFT                                          16
++#define WF_TX_DESCRIPTOR_EOSP_DW                                            1
++#define WF_TX_DESCRIPTOR_EOSP_ADDR                                          4
++#define WF_TX_DESCRIPTOR_EOSP_MASK                                          0x00020000 // 17-17
++#define WF_TX_DESCRIPTOR_EOSP_SHIFT                                         17
++#define WF_TX_DESCRIPTOR_EOSP_DW                                            1
++#define WF_TX_DESCRIPTOR_EOSP_ADDR                                          4
++#define WF_TX_DESCRIPTOR_EOSP_MASK                                          0x00020000 // 17-17
++#define WF_TX_DESCRIPTOR_EOSP_SHIFT                                         17
++#define WF_TX_DESCRIPTOR_AMS_DW                                             1
++#define WF_TX_DESCRIPTOR_AMS_ADDR                                           4
++#define WF_TX_DESCRIPTOR_AMS_MASK                                           0x00040000 // 18-18
++#define WF_TX_DESCRIPTOR_AMS_SHIFT                                          18
++#define WF_TX_DESCRIPTOR_RMVL_DW                                            1
++#define WF_TX_DESCRIPTOR_RMVL_ADDR                                          4
++#define WF_TX_DESCRIPTOR_RMVL_MASK                                          0x00040000 // 18-18
++#define WF_TX_DESCRIPTOR_RMVL_SHIFT                                         18
++#define WF_TX_DESCRIPTOR_VLAN_DW                                            1
++#define WF_TX_DESCRIPTOR_VLAN_ADDR                                          4
++#define WF_TX_DESCRIPTOR_VLAN_MASK                                          0x00080000 // 19-19
++#define WF_TX_DESCRIPTOR_VLAN_SHIFT                                         19
++#define WF_TX_DESCRIPTOR_ETYP_DW                                            1
++#define WF_TX_DESCRIPTOR_ETYP_ADDR                                          4
++#define WF_TX_DESCRIPTOR_ETYP_MASK                                          0x00100000 // 20-20
++#define WF_TX_DESCRIPTOR_ETYP_SHIFT                                         20
++#define WF_TX_DESCRIPTOR_TID_MGMT_TYPE_DW                                   1
++#define WF_TX_DESCRIPTOR_TID_MGMT_TYPE_ADDR                                 4
++#define WF_TX_DESCRIPTOR_TID_MGMT_TYPE_MASK                                 0x01e00000 // 24-21
++#define WF_TX_DESCRIPTOR_TID_MGMT_TYPE_SHIFT                                21
++#define WF_TX_DESCRIPTOR_OM_DW                                              1
++#define WF_TX_DESCRIPTOR_OM_ADDR                                            4
++#define WF_TX_DESCRIPTOR_OM_MASK                                            0x7e000000 // 30-25
++#define WF_TX_DESCRIPTOR_OM_SHIFT                                           25
++#define WF_TX_DESCRIPTOR_FR_DW                                              1
++#define WF_TX_DESCRIPTOR_FR_ADDR                                            4
++#define WF_TX_DESCRIPTOR_FR_MASK                                            0x80000000 // 31-31
++#define WF_TX_DESCRIPTOR_FR_SHIFT                                           31
++// DW2
++#define WF_TX_DESCRIPTOR_SUBTYPE_DW                                         2
++#define WF_TX_DESCRIPTOR_SUBTYPE_ADDR                                       8
++#define WF_TX_DESCRIPTOR_SUBTYPE_MASK                                       0x0000000f //  3- 0
++#define WF_TX_DESCRIPTOR_SUBTYPE_SHIFT                                      0
++#define WF_TX_DESCRIPTOR_FTYPE_DW                                           2
++#define WF_TX_DESCRIPTOR_FTYPE_ADDR                                         8
++#define WF_TX_DESCRIPTOR_FTYPE_MASK                                         0x00000030 //  5- 4
++#define WF_TX_DESCRIPTOR_FTYPE_SHIFT                                        4
++#define WF_TX_DESCRIPTOR_BF_TYPE_DW                                         2
++#define WF_TX_DESCRIPTOR_BF_TYPE_ADDR                                       8
++#define WF_TX_DESCRIPTOR_BF_TYPE_MASK                                       0x000000c0 //  7- 6
++#define WF_TX_DESCRIPTOR_BF_TYPE_SHIFT                                      6
++#define WF_TX_DESCRIPTOR_OM_MAP_DW                                          2
++#define WF_TX_DESCRIPTOR_OM_MAP_ADDR                                        8
++#define WF_TX_DESCRIPTOR_OM_MAP_MASK                                        0x00000100 //  8- 8
++#define WF_TX_DESCRIPTOR_OM_MAP_SHIFT                                       8
++#define WF_TX_DESCRIPTOR_RTS_DW                                             2
++#define WF_TX_DESCRIPTOR_RTS_ADDR                                           8
++#define WF_TX_DESCRIPTOR_RTS_MASK                                           0x00000200 //  9- 9
++#define WF_TX_DESCRIPTOR_RTS_SHIFT                                          9
++#define WF_TX_DESCRIPTOR_HEADER_PADDING_DW                                  2
++#define WF_TX_DESCRIPTOR_HEADER_PADDING_ADDR                                8
++#define WF_TX_DESCRIPTOR_HEADER_PADDING_MASK                                0x00000c00 // 11-10
++#define WF_TX_DESCRIPTOR_HEADER_PADDING_SHIFT                               10
++#define WF_TX_DESCRIPTOR_DU_DW                                              2
++#define WF_TX_DESCRIPTOR_DU_ADDR                                            8
++#define WF_TX_DESCRIPTOR_DU_MASK                                            0x00001000 // 12-12
++#define WF_TX_DESCRIPTOR_DU_SHIFT                                           12
++#define WF_TX_DESCRIPTOR_HE_DW                                              2
++#define WF_TX_DESCRIPTOR_HE_ADDR                                            8
++#define WF_TX_DESCRIPTOR_HE_MASK                                            0x00002000 // 13-13
++#define WF_TX_DESCRIPTOR_HE_SHIFT                                           13
++#define WF_TX_DESCRIPTOR_FRAG_DW                                            2
++#define WF_TX_DESCRIPTOR_FRAG_ADDR                                          8
++#define WF_TX_DESCRIPTOR_FRAG_MASK                                          0x0000c000 // 15-14
++#define WF_TX_DESCRIPTOR_FRAG_SHIFT                                         14
++#define WF_TX_DESCRIPTOR_REMAINING_TX_TIME_DW                               2
++#define WF_TX_DESCRIPTOR_REMAINING_TX_TIME_ADDR                             8
++#define WF_TX_DESCRIPTOR_REMAINING_TX_TIME_MASK                             0x03ff0000 // 25-16
++#define WF_TX_DESCRIPTOR_REMAINING_TX_TIME_SHIFT                            16
++#define WF_TX_DESCRIPTOR_POWER_OFFSET_DW                                    2
++#define WF_TX_DESCRIPTOR_POWER_OFFSET_ADDR                                  8
++#define WF_TX_DESCRIPTOR_POWER_OFFSET_MASK                                  0xfc000000 // 31-26
++#define WF_TX_DESCRIPTOR_POWER_OFFSET_SHIFT                                 26
++// DW3
++#define WF_TX_DESCRIPTOR_NA_DW                                              3
++#define WF_TX_DESCRIPTOR_NA_ADDR                                            12
++#define WF_TX_DESCRIPTOR_NA_MASK                                            0x00000001 //  0- 0
++#define WF_TX_DESCRIPTOR_NA_SHIFT                                           0
++#define WF_TX_DESCRIPTOR_PF_DW                                              3
++#define WF_TX_DESCRIPTOR_PF_ADDR                                            12
++#define WF_TX_DESCRIPTOR_PF_MASK                                            0x00000002 //  1- 1
++#define WF_TX_DESCRIPTOR_PF_SHIFT                                           1
++#define WF_TX_DESCRIPTOR_EMRD_DW                                            3
++#define WF_TX_DESCRIPTOR_EMRD_ADDR                                          12
++#define WF_TX_DESCRIPTOR_EMRD_MASK                                          0x00000004 //  2- 2
++#define WF_TX_DESCRIPTOR_EMRD_SHIFT                                         2
++#define WF_TX_DESCRIPTOR_EEOSP_DW                                           3
++#define WF_TX_DESCRIPTOR_EEOSP_ADDR                                         12
++#define WF_TX_DESCRIPTOR_EEOSP_MASK                                         0x00000008 //  3- 3
++#define WF_TX_DESCRIPTOR_EEOSP_SHIFT                                        3
++#define WF_TX_DESCRIPTOR_BM_DW                                              3
++#define WF_TX_DESCRIPTOR_BM_ADDR                                            12
++#define WF_TX_DESCRIPTOR_BM_MASK                                            0x00000010 //  4- 4
++#define WF_TX_DESCRIPTOR_BM_SHIFT                                           4
++#define WF_TX_DESCRIPTOR_HW_AMSDU_CAP_DW                                    3
++#define WF_TX_DESCRIPTOR_HW_AMSDU_CAP_ADDR                                  12
++#define WF_TX_DESCRIPTOR_HW_AMSDU_CAP_MASK                                  0x00000020 //  5- 5
++#define WF_TX_DESCRIPTOR_HW_AMSDU_CAP_SHIFT                                 5
++#define WF_TX_DESCRIPTOR_TX_COUNT_DW                                        3
++#define WF_TX_DESCRIPTOR_TX_COUNT_ADDR                                      12
++#define WF_TX_DESCRIPTOR_TX_COUNT_MASK                                      0x000007c0 // 10- 6
++#define WF_TX_DESCRIPTOR_TX_COUNT_SHIFT                                     6
++#define WF_TX_DESCRIPTOR_REMAINING_TX_COUNT_DW                              3
++#define WF_TX_DESCRIPTOR_REMAINING_TX_COUNT_ADDR                            12
++#define WF_TX_DESCRIPTOR_REMAINING_TX_COUNT_MASK                            0x0000f800 // 15-11
++#define WF_TX_DESCRIPTOR_REMAINING_TX_COUNT_SHIFT                           11
++#define WF_TX_DESCRIPTOR_SN_DW                                              3
++#define WF_TX_DESCRIPTOR_SN_ADDR                                            12
++#define WF_TX_DESCRIPTOR_SN_MASK                                            0x0fff0000 // 27-16
++#define WF_TX_DESCRIPTOR_SN_SHIFT                                           16
++#define WF_TX_DESCRIPTOR_BA_DIS_DW                                          3
++#define WF_TX_DESCRIPTOR_BA_DIS_ADDR                                        12
++#define WF_TX_DESCRIPTOR_BA_DIS_MASK                                        0x10000000 // 28-28
++#define WF_TX_DESCRIPTOR_BA_DIS_SHIFT                                       28
++#define WF_TX_DESCRIPTOR_PM_DW                                              3
++#define WF_TX_DESCRIPTOR_PM_ADDR                                            12
++#define WF_TX_DESCRIPTOR_PM_MASK                                            0x20000000 // 29-29
++#define WF_TX_DESCRIPTOR_PM_SHIFT                                           29
++#define WF_TX_DESCRIPTOR_PN_VLD_DW                                          3
++#define WF_TX_DESCRIPTOR_PN_VLD_ADDR                                        12
++#define WF_TX_DESCRIPTOR_PN_VLD_MASK                                        0x40000000 // 30-30
++#define WF_TX_DESCRIPTOR_PN_VLD_SHIFT                                       30
++#define WF_TX_DESCRIPTOR_SN_VLD_DW                                          3
++#define WF_TX_DESCRIPTOR_SN_VLD_ADDR                                        12
++#define WF_TX_DESCRIPTOR_SN_VLD_MASK                                        0x80000000 // 31-31
++#define WF_TX_DESCRIPTOR_SN_VLD_SHIFT                                       31
++// DW4
++#define WF_TX_DESCRIPTOR_PN_31_0__DW                                        4
++#define WF_TX_DESCRIPTOR_PN_31_0__ADDR                                      16
++#define WF_TX_DESCRIPTOR_PN_31_0__MASK                                      0xffffffff // 31- 0
++#define WF_TX_DESCRIPTOR_PN_31_0__SHIFT                                     0
++// DW5
++#define WF_TX_DESCRIPTOR_PID_DW                                             5
++#define WF_TX_DESCRIPTOR_PID_ADDR                                           20
++#define WF_TX_DESCRIPTOR_PID_MASK                                           0x000000ff //  7- 0
++#define WF_TX_DESCRIPTOR_PID_SHIFT                                          0
++#define WF_TX_DESCRIPTOR_TXSFM_DW                                           5
++#define WF_TX_DESCRIPTOR_TXSFM_ADDR                                         20
++#define WF_TX_DESCRIPTOR_TXSFM_MASK                                         0x00000100 //  8- 8
++#define WF_TX_DESCRIPTOR_TXSFM_SHIFT                                        8
++#define WF_TX_DESCRIPTOR_TXS2M_DW                                           5
++#define WF_TX_DESCRIPTOR_TXS2M_ADDR                                         20
++#define WF_TX_DESCRIPTOR_TXS2M_MASK                                         0x00000200 //  9- 9
++#define WF_TX_DESCRIPTOR_TXS2M_SHIFT                                        9
++#define WF_TX_DESCRIPTOR_TXS2H_DW                                           5
++#define WF_TX_DESCRIPTOR_TXS2H_ADDR                                         20
++#define WF_TX_DESCRIPTOR_TXS2H_MASK                                         0x00000400 // 10-10
++#define WF_TX_DESCRIPTOR_TXS2H_SHIFT                                        10
++#define WF_TX_DESCRIPTOR_FBCZ_DW                                            5
++#define WF_TX_DESCRIPTOR_FBCZ_ADDR                                          20
++#define WF_TX_DESCRIPTOR_FBCZ_MASK                                          0x00001000 // 12-12
++#define WF_TX_DESCRIPTOR_FBCZ_SHIFT                                         12
++#define WF_TX_DESCRIPTOR_BYPASS_RBB_DW                                      5
++#define WF_TX_DESCRIPTOR_BYPASS_RBB_ADDR                                    20
++#define WF_TX_DESCRIPTOR_BYPASS_RBB_MASK                                    0x00002000 // 13-13
++#define WF_TX_DESCRIPTOR_BYPASS_RBB_SHIFT                                   13
++#define WF_TX_DESCRIPTOR_BYPASS_TBB_DW                                      5
++#define WF_TX_DESCRIPTOR_BYPASS_TBB_ADDR                                    20
++#define WF_TX_DESCRIPTOR_BYPASS_TBB_MASK                                    0x00004000 // 14-14
++#define WF_TX_DESCRIPTOR_BYPASS_TBB_SHIFT                                   14
++#define WF_TX_DESCRIPTOR_FL_DW                                              5
++#define WF_TX_DESCRIPTOR_FL_ADDR                                            20
++#define WF_TX_DESCRIPTOR_FL_MASK                                            0x00008000 // 15-15
++#define WF_TX_DESCRIPTOR_FL_SHIFT                                           15
++#define WF_TX_DESCRIPTOR_PN_47_32__DW                                       5
++#define WF_TX_DESCRIPTOR_PN_47_32__ADDR                                     20
++#define WF_TX_DESCRIPTOR_PN_47_32__MASK                                     0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_PN_47_32__SHIFT                                    16
++// DW6
++#define WF_TX_DESCRIPTOR_AMSDU_CAP_UTXB_DW                                  6
++#define WF_TX_DESCRIPTOR_AMSDU_CAP_UTXB_ADDR                                24
++#define WF_TX_DESCRIPTOR_AMSDU_CAP_UTXB_MASK                                0x00000002 //  1- 1
++#define WF_TX_DESCRIPTOR_AMSDU_CAP_UTXB_SHIFT                               1
++#define WF_TX_DESCRIPTOR_DAS_DW                                             6
++#define WF_TX_DESCRIPTOR_DAS_ADDR                                           24
++#define WF_TX_DESCRIPTOR_DAS_MASK                                           0x00000004 //  2- 2
++#define WF_TX_DESCRIPTOR_DAS_SHIFT                                          2
++#define WF_TX_DESCRIPTOR_DIS_MAT_DW                                         6
++#define WF_TX_DESCRIPTOR_DIS_MAT_ADDR                                       24
++#define WF_TX_DESCRIPTOR_DIS_MAT_MASK                                       0x00000008 //  3- 3
++#define WF_TX_DESCRIPTOR_DIS_MAT_SHIFT                                      3
++#define WF_TX_DESCRIPTOR_MSDU_COUNT_DW                                      6
++#define WF_TX_DESCRIPTOR_MSDU_COUNT_ADDR                                    24
++#define WF_TX_DESCRIPTOR_MSDU_COUNT_MASK                                    0x000003f0 //  9- 4
++#define WF_TX_DESCRIPTOR_MSDU_COUNT_SHIFT                                   4
++#define WF_TX_DESCRIPTOR_TIMESTAMP_OFFSET_IDX_DW                            6
++#define WF_TX_DESCRIPTOR_TIMESTAMP_OFFSET_IDX_ADDR                          24
++#define WF_TX_DESCRIPTOR_TIMESTAMP_OFFSET_IDX_MASK                          0x00007c00 // 14-10
++#define WF_TX_DESCRIPTOR_TIMESTAMP_OFFSET_IDX_SHIFT                         10
++#define WF_TX_DESCRIPTOR_TIMESTAMP_OFFSET_EN_DW                             6
++#define WF_TX_DESCRIPTOR_TIMESTAMP_OFFSET_EN_ADDR                           24
++#define WF_TX_DESCRIPTOR_TIMESTAMP_OFFSET_EN_MASK                           0x00008000 // 15-15
++#define WF_TX_DESCRIPTOR_TIMESTAMP_OFFSET_EN_SHIFT                          15
++#define WF_TX_DESCRIPTOR_FIXED_RATE_IDX_DW                                  6
++#define WF_TX_DESCRIPTOR_FIXED_RATE_IDX_ADDR                                24
++#define WF_TX_DESCRIPTOR_FIXED_RATE_IDX_MASK                                0x003f0000 // 21-16
++#define WF_TX_DESCRIPTOR_FIXED_RATE_IDX_SHIFT                               16
++#define WF_TX_DESCRIPTOR_BW_DW                                              6
++#define WF_TX_DESCRIPTOR_BW_ADDR                                            24
++#define WF_TX_DESCRIPTOR_BW_MASK                                            0x03c00000 // 25-22
++#define WF_TX_DESCRIPTOR_BW_SHIFT                                           22
++#define WF_TX_DESCRIPTOR_VTA_DW                                             6
++#define WF_TX_DESCRIPTOR_VTA_ADDR                                           24
++#define WF_TX_DESCRIPTOR_VTA_MASK                                           0x10000000 // 28-28
++#define WF_TX_DESCRIPTOR_VTA_SHIFT                                          28
++#define WF_TX_DESCRIPTOR_SRC_DW                                             6
++#define WF_TX_DESCRIPTOR_SRC_ADDR                                           24
++#define WF_TX_DESCRIPTOR_SRC_MASK                                           0xc0000000 // 31-30
++#define WF_TX_DESCRIPTOR_SRC_SHIFT                                          30
++// DW7
++#define WF_TX_DESCRIPTOR_SW_TX_TIME_DW                                      7
++#define WF_TX_DESCRIPTOR_SW_TX_TIME_ADDR                                    28
++#define WF_TX_DESCRIPTOR_SW_TX_TIME_MASK                                    0x000003ff //  9- 0
++#define WF_TX_DESCRIPTOR_SW_TX_TIME_SHIFT                                   0
++#define WF_TX_DESCRIPTOR_UT_DW                                              7
++#define WF_TX_DESCRIPTOR_UT_ADDR                                            28
++#define WF_TX_DESCRIPTOR_UT_MASK                                            0x00008000 // 15-15
++#define WF_TX_DESCRIPTOR_UT_SHIFT                                           15
++#define WF_TX_DESCRIPTOR_CTXD_CNT_DW                                        7
++#define WF_TX_DESCRIPTOR_CTXD_CNT_ADDR                                      28
++#define WF_TX_DESCRIPTOR_CTXD_CNT_MASK                                      0x03c00000 // 25-22
++#define WF_TX_DESCRIPTOR_CTXD_CNT_SHIFT                                     22
++#define WF_TX_DESCRIPTOR_CTXD_DW                                            7
++#define WF_TX_DESCRIPTOR_CTXD_ADDR                                          28
++#define WF_TX_DESCRIPTOR_CTXD_MASK                                          0x04000000 // 26-26
++#define WF_TX_DESCRIPTOR_CTXD_SHIFT                                         26
++#define WF_TX_DESCRIPTOR_HM_DW                                              7
++#define WF_TX_DESCRIPTOR_HM_ADDR                                            28
++#define WF_TX_DESCRIPTOR_HM_MASK                                            0x08000000 // 27-27
++#define WF_TX_DESCRIPTOR_HM_SHIFT                                           27
++#define WF_TX_DESCRIPTOR_DP_DW                                              7
++#define WF_TX_DESCRIPTOR_DP_ADDR                                            28
++#define WF_TX_DESCRIPTOR_DP_MASK                                            0x10000000 // 28-28
++#define WF_TX_DESCRIPTOR_DP_SHIFT                                           28
++#define WF_TX_DESCRIPTOR_IP_DW                                              7
++#define WF_TX_DESCRIPTOR_IP_ADDR                                            28
++#define WF_TX_DESCRIPTOR_IP_MASK                                            0x20000000 // 29-29
++#define WF_TX_DESCRIPTOR_IP_SHIFT                                           29
++#define WF_TX_DESCRIPTOR_TXD_LEN_DW                                         7
++#define WF_TX_DESCRIPTOR_TXD_LEN_ADDR                                       28
++#define WF_TX_DESCRIPTOR_TXD_LEN_MASK                                       0xc0000000 // 31-30
++#define WF_TX_DESCRIPTOR_TXD_LEN_SHIFT                                      30
++// DW8
++#define WF_TX_DESCRIPTOR_MSDU0_DW                                           8
++#define WF_TX_DESCRIPTOR_MSDU0_ADDR                                         32
++#define WF_TX_DESCRIPTOR_MSDU0_MASK                                         0x0000ffff // 15- 0
++#define WF_TX_DESCRIPTOR_MSDU0_SHIFT                                        0
++#define WF_TX_DESCRIPTOR_MSDU1_DW                                           8
++#define WF_TX_DESCRIPTOR_MSDU1_ADDR                                         32
++#define WF_TX_DESCRIPTOR_MSDU1_MASK                                         0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_MSDU1_SHIFT                                        16
++// DW9
++#define WF_TX_DESCRIPTOR_MSDU2_DW                                           9
++#define WF_TX_DESCRIPTOR_MSDU2_ADDR                                         36
++#define WF_TX_DESCRIPTOR_MSDU2_MASK                                         0x0000ffff // 15- 0
++#define WF_TX_DESCRIPTOR_MSDU2_SHIFT                                        0
++#define WF_TX_DESCRIPTOR_MSDU3_DW                                           9
++#define WF_TX_DESCRIPTOR_MSDU3_ADDR                                         36
++#define WF_TX_DESCRIPTOR_MSDU3_MASK                                         0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_MSDU3_SHIFT                                        16
++// DW10
++#define WF_TX_DESCRIPTOR_TXP0_DW                                            10
++#define WF_TX_DESCRIPTOR_TXP0_ADDR                                          40
++#define WF_TX_DESCRIPTOR_TXP0_MASK                                          0xffffffff // 31- 0
++#define WF_TX_DESCRIPTOR_TXP0_SHIFT                                         0
++// DW11
++// DO NOT process repeat field(txp[0])
++#define WF_TX_DESCRIPTOR_TXP1_DW                                            11
++#define WF_TX_DESCRIPTOR_TXP1_ADDR                                          44
++#define WF_TX_DESCRIPTOR_TXP1_MASK                                          0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_TXP1_SHIFT                                         16
++// DW12
++// DO NOT process repeat field(txp[1])
++// DW13
++#define WF_TX_DESCRIPTOR_TXP2_DW                                            13
++#define WF_TX_DESCRIPTOR_TXP2_ADDR                                          52
++#define WF_TX_DESCRIPTOR_TXP2_MASK                                          0xffffffff // 31- 0
++#define WF_TX_DESCRIPTOR_TXP2_SHIFT                                         0
++// DW14
++// DO NOT process repeat field(txp[2])
++#define WF_TX_DESCRIPTOR_TXP3_DW                                            14
++#define WF_TX_DESCRIPTOR_TXP3_ADDR                                          56
++#define WF_TX_DESCRIPTOR_TXP3_MASK                                          0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_TXP3_SHIFT                                         16
++// DW15
++// DO NOT process repeat field(txp[3])
++// DW16
++#define WF_TX_DESCRIPTOR_MSDU4_DW                                           16
++#define WF_TX_DESCRIPTOR_MSDU4_ADDR                                         64
++#define WF_TX_DESCRIPTOR_MSDU4_MASK                                         0x0000ffff // 15- 0
++#define WF_TX_DESCRIPTOR_MSDU4_SHIFT                                        0
++#define WF_TX_DESCRIPTOR_MSDU5_DW                                           16
++#define WF_TX_DESCRIPTOR_MSDU5_ADDR                                         64
++#define WF_TX_DESCRIPTOR_MSDU5_MASK                                         0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_MSDU5_SHIFT                                        16
++// DW17
++#define WF_TX_DESCRIPTOR_MSDU6_DW                                           17
++#define WF_TX_DESCRIPTOR_MSDU6_ADDR                                         68
++#define WF_TX_DESCRIPTOR_MSDU6_MASK                                         0x0000ffff // 15- 0
++#define WF_TX_DESCRIPTOR_MSDU6_SHIFT                                        0
++#define WF_TX_DESCRIPTOR_MSDU7_DW                                           17
++#define WF_TX_DESCRIPTOR_MSDU7_ADDR                                         68
++#define WF_TX_DESCRIPTOR_MSDU7_MASK                                         0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_MSDU7_SHIFT                                        16
++// DW18
++#define WF_TX_DESCRIPTOR_TXP4_DW                                            18
++#define WF_TX_DESCRIPTOR_TXP4_ADDR                                          72
++#define WF_TX_DESCRIPTOR_TXP4_MASK                                          0xffffffff // 31- 0
++#define WF_TX_DESCRIPTOR_TXP4_SHIFT                                         0
++// DW19
++// DO NOT process repeat field(txp[4])
++#define WF_TX_DESCRIPTOR_TXP5_DW                                            19
++#define WF_TX_DESCRIPTOR_TXP5_ADDR                                          76
++#define WF_TX_DESCRIPTOR_TXP5_MASK                                          0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_TXP5_SHIFT                                         16
++// DW20
++// DO NOT process repeat field(txp[5])
++// DW21
++#define WF_TX_DESCRIPTOR_TXP6_DW                                            21
++#define WF_TX_DESCRIPTOR_TXP6_ADDR                                          84
++#define WF_TX_DESCRIPTOR_TXP6_MASK                                          0xffffffff // 31- 0
++#define WF_TX_DESCRIPTOR_TXP6_SHIFT                                         0
++// DW22
++// DO NOT process repeat field(txp[6])
++#define WF_TX_DESCRIPTOR_TXP7_DW                                            22
++#define WF_TX_DESCRIPTOR_TXP7_ADDR                                          88
++#define WF_TX_DESCRIPTOR_TXP7_MASK                                          0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_TXP7_SHIFT                                         16
++// DW23
++// DO NOT process repeat field(txp[7])
++// DW24
++#define WF_TX_DESCRIPTOR_TXP8_DW                                            24
++#define WF_TX_DESCRIPTOR_TXP8_ADDR                                          96
++#define WF_TX_DESCRIPTOR_TXP8_MASK                                          0xffffffff // 31- 0
++#define WF_TX_DESCRIPTOR_TXP8_SHIFT                                         0
++// DW25
++// DO NOT process repeat field(txp[8])
++#define WF_TX_DESCRIPTOR_TXP9_DW                                            25
++#define WF_TX_DESCRIPTOR_TXP9_ADDR                                          100
++#define WF_TX_DESCRIPTOR_TXP9_MASK                                          0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_TXP9_SHIFT                                         16
++// DW26
++// DO NOT process repeat field(txp[9])
++// DW27
++#define WF_TX_DESCRIPTOR_TXP10_DW                                           27
++#define WF_TX_DESCRIPTOR_TXP10_ADDR                                         108
++#define WF_TX_DESCRIPTOR_TXP10_MASK                                         0xffffffff // 31- 0
++#define WF_TX_DESCRIPTOR_TXP10_SHIFT                                        0
++// DW28
++// DO NOT process repeat field(txp[10])
++#define WF_TX_DESCRIPTOR_TXP11_DW                                           28
++#define WF_TX_DESCRIPTOR_TXP11_ADDR                                         112
++#define WF_TX_DESCRIPTOR_TXP11_MASK                                         0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_TXP11_SHIFT                                        16
++// DW29
++// DO NOT process repeat field(txp[11])
++// DW30
++#define WF_TX_DESCRIPTOR_TXP12_DW                                           30
++#define WF_TX_DESCRIPTOR_TXP12_ADDR                                         120
++#define WF_TX_DESCRIPTOR_TXP12_MASK                                         0xffffffff // 31- 0
++#define WF_TX_DESCRIPTOR_TXP12_SHIFT                                        0
++// DW31
++// DO NOT process repeat field(txp[12])
++#define WF_TX_DESCRIPTOR_TXP13_DW                                           31
++#define WF_TX_DESCRIPTOR_TXP13_ADDR                                         124
++#define WF_TX_DESCRIPTOR_TXP13_MASK                                         0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_TXP13_SHIFT                                        16
++// DW32
++// DO NOT process repeat field(txp[13])
++// DW33
++#define WF_TX_DESCRIPTOR_TXP14_DW                                           33
++#define WF_TX_DESCRIPTOR_TXP14_ADDR                                         132
++#define WF_TX_DESCRIPTOR_TXP14_MASK                                         0xffffffff // 31- 0
++#define WF_TX_DESCRIPTOR_TXP14_SHIFT                                        0
++// DW34
++// DO NOT process repeat field(txp[14])
++#define WF_TX_DESCRIPTOR_TXP15_DW                                           34
++#define WF_TX_DESCRIPTOR_TXP15_ADDR                                         136
++#define WF_TX_DESCRIPTOR_TXP15_MASK                                         0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_TXP15_SHIFT                                        16
++// DW35
++// DO NOT process repeat field(txp[15])
++// DW36
++#define WF_TX_DESCRIPTOR_TXP16_DW                                           36
++#define WF_TX_DESCRIPTOR_TXP16_ADDR                                         144
++#define WF_TX_DESCRIPTOR_TXP16_MASK                                         0xffffffff // 31- 0
++#define WF_TX_DESCRIPTOR_TXP16_SHIFT                                        0
++// DW37
++// DO NOT process repeat field(txp[16])
++#define WF_TX_DESCRIPTOR_TXP17_DW                                           37
++#define WF_TX_DESCRIPTOR_TXP17_ADDR                                         148
++#define WF_TX_DESCRIPTOR_TXP17_MASK                                         0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_TXP17_SHIFT                                        16
++// DW38
++// DO NOT process repeat field(txp[17])
++// DW39
++#define WF_TX_DESCRIPTOR_TXP18_DW                                           39
++#define WF_TX_DESCRIPTOR_TXP18_ADDR                                         156
++#define WF_TX_DESCRIPTOR_TXP18_MASK                                         0xffffffff // 31- 0
++#define WF_TX_DESCRIPTOR_TXP18_SHIFT                                        0
++// DW40
++// DO NOT process repeat field(txp[18])
++#define WF_TX_DESCRIPTOR_TXP19_DW                                           40
++#define WF_TX_DESCRIPTOR_TXP19_ADDR                                         160
++#define WF_TX_DESCRIPTOR_TXP19_MASK                                         0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_TXP19_SHIFT                                        16
++// DW41
++// DO NOT process repeat field(txp[19])
++// DW42
++#define WF_TX_DESCRIPTOR_TXP20_DW                                           42
++#define WF_TX_DESCRIPTOR_TXP20_ADDR                                         168
++#define WF_TX_DESCRIPTOR_TXP20_MASK                                         0xffffffff // 31- 0
++#define WF_TX_DESCRIPTOR_TXP20_SHIFT                                        0
++// DW43
++// DO NOT process repeat field(txp[20])
++#define WF_TX_DESCRIPTOR_TXP21_DW                                           43
++#define WF_TX_DESCRIPTOR_TXP21_ADDR                                         172
++#define WF_TX_DESCRIPTOR_TXP21_MASK                                         0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_TXP21_SHIFT                                        16
++// DW44
++// DO NOT process repeat field(txp[21])
++// DW45
++#define WF_TX_DESCRIPTOR_TXP22_DW                                           45
++#define WF_TX_DESCRIPTOR_TXP22_ADDR                                         180
++#define WF_TX_DESCRIPTOR_TXP22_MASK                                         0xffffffff // 31- 0
++#define WF_TX_DESCRIPTOR_TXP22_SHIFT                                        0
++// DW46
++// DO NOT process repeat field(txp[22])
++#define WF_TX_DESCRIPTOR_TXP23_DW                                           46
++#define WF_TX_DESCRIPTOR_TXP23_ADDR                                         184
++#define WF_TX_DESCRIPTOR_TXP23_MASK                                         0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_TXP23_SHIFT                                        16
++// DW47
++// DO NOT process repeat field(txp[23])
++// DW48
++#define WF_TX_DESCRIPTOR_TXP24_DW                                           48
++#define WF_TX_DESCRIPTOR_TXP24_ADDR                                         192
++#define WF_TX_DESCRIPTOR_TXP24_MASK                                         0xffffffff // 31- 0
++#define WF_TX_DESCRIPTOR_TXP24_SHIFT                                        0
++// DW49
++// DO NOT process repeat field(txp[24])
++#define WF_TX_DESCRIPTOR_TXP25_DW                                           49
++#define WF_TX_DESCRIPTOR_TXP25_ADDR                                         196
++#define WF_TX_DESCRIPTOR_TXP25_MASK                                         0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_TXP25_SHIFT                                        16
++// DW50
++// DO NOT process repeat field(txp[25])
++// DW51
++#define WF_TX_DESCRIPTOR_TXP26_DW                                           51
++#define WF_TX_DESCRIPTOR_TXP26_ADDR                                         204
++#define WF_TX_DESCRIPTOR_TXP26_MASK                                         0xffffffff // 31- 0
++#define WF_TX_DESCRIPTOR_TXP26_SHIFT                                        0
++// DW52
++// DO NOT process repeat field(txp[26])
++#define WF_TX_DESCRIPTOR_TXP27_DW                                           52
++#define WF_TX_DESCRIPTOR_TXP27_ADDR                                         208
++#define WF_TX_DESCRIPTOR_TXP27_MASK                                         0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_TXP27_SHIFT                                        16
++// DW53
++// DO NOT process repeat field(txp[27])
++// DW54
++#define WF_TX_DESCRIPTOR_TXP28_DW                                           54
++#define WF_TX_DESCRIPTOR_TXP28_ADDR                                         216
++#define WF_TX_DESCRIPTOR_TXP28_MASK                                         0xffffffff // 31- 0
++#define WF_TX_DESCRIPTOR_TXP28_SHIFT                                        0
++// DW55
++// DO NOT process repeat field(txp[28])
++#define WF_TX_DESCRIPTOR_TXP29_DW                                           55
++#define WF_TX_DESCRIPTOR_TXP29_ADDR                                         220
++#define WF_TX_DESCRIPTOR_TXP29_MASK                                         0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_TXP29_SHIFT                                        16
++// DW56
++// DO NOT process repeat field(txp[29])
++// DW57
++#define WF_TX_DESCRIPTOR_TXP30_DW                                           57
++#define WF_TX_DESCRIPTOR_TXP30_ADDR                                         228
++#define WF_TX_DESCRIPTOR_TXP30_MASK                                         0xffffffff // 31- 0
++#define WF_TX_DESCRIPTOR_TXP30_SHIFT                                        0
++// DW58
++// DO NOT process repeat field(txp[30])
++#define WF_TX_DESCRIPTOR_TXP31_DW                                           58
++#define WF_TX_DESCRIPTOR_TXP31_ADDR                                         232
++#define WF_TX_DESCRIPTOR_TXP31_MASK                                         0xffff0000 // 31-16
++#define WF_TX_DESCRIPTOR_TXP31_SHIFT                                        16
++// DW59
++// DO NOT process repeat field(txp[31])
++
++/* TXP PAO */
++#define HIF_TXP_V2_SIZE (24 * 4)
++/* DW0 */
++#define HIF_TXD_VERSION_SHIFT 19
++#define HIF_TXD_VERSION_MASK 0x00780000
++
++/* DW8 */
++#define HIF_TXP_PRIORITY_SHIFT 0
++#define HIF_TXP_PRIORITY_MASK 0x00000001
++#define HIF_TXP_FIXED_RATE_SHIFT 1
++#define HIF_TXP_FIXED_RATE_MASK 0x00000002
++#define HIF_TXP_TCP_SHIFT 2
++#define HIF_TXP_TCP_MASK 0x00000004
++#define HIF_TXP_NON_CIPHER_SHIFT 3
++#define HIF_TXP_NON_CIPHER_MASK 0x00000008
++#define HIF_TXP_VLAN_SHIFT 4
++#define HIF_TXP_VLAN_MASK 0x00000010
++#define HIF_TXP_BC_MC_FLAG_SHIFT 5
++#define HIF_TXP_BC_MC_FLAG_MASK 0x00000060
++#define HIF_TXP_FR_HOST_SHIFT 7
++#define HIF_TXP_FR_HOST_MASK 0x00000080
++#define HIF_TXP_ETYPE_SHIFT 8
++#define HIF_TXP_ETYPE_MASK 0x00000100
++#define HIF_TXP_TXP_AMSDU_SHIFT 9
++#define HIF_TXP_TXP_AMSDU_MASK 0x00000200
++#define HIF_TXP_TXP_MC_CLONE_SHIFT 10
++#define HIF_TXP_TXP_MC_CLONE_MASK 0x00000400
++#define HIF_TXP_TOKEN_ID_SHIFT 16
++#define HIF_TXP_TOKEN_ID_MASK 0xffff0000
++
++/* DW9 */
++#define HIF_TXP_BSS_IDX_SHIFT 0
++#define HIF_TXP_BSS_IDX_MASK 0x000000ff
++#define HIF_TXP_USER_PRIORITY_SHIFT 8
++#define HIF_TXP_USER_PRIORITY_MASK 0x0000ff00
++#define HIF_TXP_BUF_NUM_SHIFT 16
++#define HIF_TXP_BUF_NUM_MASK 0x001f0000
++#define HIF_TXP_MSDU_CNT_SHIFT 21
++#define HIF_TXP_MSDU_CNT_MASK 0x03e00000
++#define HIF_TXP_SRC_SHIFT 26
++#define HIF_TXP_SRC_MASK 0x0c000000
++
++/* DW10 */
++#define HIF_TXP_ETH_TYPE_SHIFT 0
++#define HIF_TXP_ETH_TYPE_MASK 0x0000ffff
++#define HIF_TXP_WLAN_IDX_SHIFT 16
++#define HIF_TXP_WLAN_IDX_MASK 0x0fff0000
++
++/* DW11 */
++#define HIF_TXP_PPE_INFO_SHIFT 0
++#define HIF_TXP_PPE_INFO_MASK 0xffffffff
++
++/* DW12 - DW31 */
++#define HIF_TXP_BUF_PTR0_L_SHIFT 0
++#define HIF_TXP_BUF_PTR0_L_MASK 0xffffffff
++#define HIF_TXP_BUF_LEN0_SHIFT 0
++#define HIF_TXP_BUF_LEN0_MASK 0x00000fff
++#define HIF_TXP_BUF_PTR0_H_SHIFT 12
++#define HIF_TXP_BUF_PTR0_H_MASK 0x0000f000
++#define HIF_TXP_BUF_LEN1_SHIFT 16
++#define HIF_TXP_BUF_LEN1_MASK 0x0fff0000
++#define HIF_TXP_BUF_PTR1_H_SHIFT 28
++#define HIF_TXP_BUF_PTR1_H_MASK 0xf0000000
++#define HIF_TXP_BUF_PTR1_L_SHIFT 0
++#define HIF_TXP_BUF_PTR1_L_MASK 0xffffffff
++
++/* DW31 */
++#define HIF_TXP_ML_SHIFT 16
++#define HIF_TXP_ML_MASK 0xffff0000
++
++#endif
++
++#endif
+diff --git a/mt7996/mtk_debugfs_i.c b/mt7996/mtk_debugfs_i.c
+new file mode 100644
+index 00000000..ea412cd5
+--- /dev/null
++++ b/mt7996/mtk_debugfs_i.c
+@@ -0,0 +1,720 @@
++#include <linux/inet.h>
++#include "mt7996.h"
++#include "../mt76.h"
++#include "mcu.h"
++#include "mac.h"
++#include "eeprom.h"
++#include "mtk_debug.h"
++#include "mtk_debug_i.h"
++#include "mtk_mcu.h"
++
++#ifdef CONFIG_MTK_DEBUG
++
++#define info_or_seq_printf(seq, fmt, ...)	do {	\
++	if (seq)					\
++		seq_printf(seq, fmt, ##__VA_ARGS__);	\
++	else						\
++		pr_info(fmt, ##__VA_ARGS__);		\
++} while (0)
++
++static void info_or_seq_hex_dump(struct seq_file *seq, int prefix_type,
++				 int rowsize, int groupsize, const void *buf,
++				 size_t len, bool ascii)
++{
++	if (seq)
++		seq_hex_dump(seq, "", prefix_type, rowsize, groupsize,
++			     buf, len, ascii);
++	else
++		print_hex_dump(KERN_INFO, "", prefix_type,
++			       rowsize, groupsize, buf, len, ascii);
++}
++
++//bmac dump mac txp
++static void mt7996_dump_bmac_mac_txp_info(struct seq_file *s, struct mt7996_dev *dev,
++					  __le32 *txp)
++{
++	struct mt7996_txp_token {
++		__le16 msdu[4];
++	} *msdu;
++	struct mt7996_txp_ptr {
++		__le32 addr1;
++		__le32 addr_info;
++		__le32 addr2;
++	} *ptr;
++	int i = 0;
++
++	for (i = 0; i < 12; i = i+2 ) {
++		if (i == 0 || i == 4) {
++			msdu = (struct mt7996_txp_token *) txp;
++			info_or_seq_printf(s, "msdu token(%d-%d)=%ld %ld %ld %ld (0x%08x-0x%08x)\n", i, i+3,
++				(msdu->msdu[0] & GENMASK(14, 0)),
++				(msdu->msdu[1] & GENMASK(14, 0)),
++				(msdu->msdu[2] & GENMASK(14, 0)),
++				(msdu->msdu[3] & GENMASK(14, 0)), *txp, *(txp+1));
++			txp = txp + 2;
++		}
++		ptr = (struct mt7996_txp_ptr *) txp;
++		info_or_seq_printf(s, "ptr%02d : addr(0x%08x) len(%ld) addr_h(%02lx) SRC(%d) ML(%d) \n",
++			i, ptr->addr1,
++			FIELD_GET(GENMASK(11, 0), ptr->addr_info),
++			FIELD_GET(GENMASK(13, 12), ptr->addr_info),
++			!!(ptr->addr_info & BIT(14)),
++			!!(ptr->addr_info & BIT(15)));
++		info_or_seq_printf(s, "ptr%02d : addr(0x%08x) len(%ld) addr_h(%02lx) SRC(%d) ML(%d) \n",
++			i+1, ptr->addr2,
++			FIELD_GET(GENMASK(27, 16), ptr->addr_info),
++			FIELD_GET(GENMASK(29, 28), ptr->addr_info),
++			!!(ptr->addr_info & BIT(30)),
++			!!(ptr->addr_info & BIT(31)));
++		txp = txp + 3;
++	}
++}
++
++//bmac dump hif txp
++void mt7996_dump_bmac_hif_txp_info(struct seq_file *s, struct mt7996_dev *dev,
++				   __le32 *txp, u32 hif_txp_ver)
++{
++	int i, j = 0;
++	u32 dw;
++
++	info_or_seq_printf(s, "txp raw data: size=%d\n", HIF_TXP_V2_SIZE);
++	info_or_seq_hex_dump(s, DUMP_PREFIX_OFFSET, 16, 1, (u8 *)txp, HIF_TXP_V2_SIZE, false);
++
++	info_or_seq_printf(s, "BMAC_TXP Fields:\n");
++
++	/* dw0 */
++	if (hif_txp_ver == 2) {
++		dw = le32_to_cpu(txp[0]);
++		info_or_seq_printf(s, "HIF_TXP_PRIORITY = %d\n",
++				GET_FIELD(HIF_TXP_PRIORITY, dw));
++		info_or_seq_printf(s, "HIF_TXP_FIXED_RATE = %d\n",
++				GET_FIELD(HIF_TXP_FIXED_RATE, dw));
++		info_or_seq_printf(s, "HIF_TXP_TCP = %d\n",
++				GET_FIELD(HIF_TXP_TCP, dw));
++		info_or_seq_printf(s, "HIF_TXP_NON_CIPHER = %d\n",
++				GET_FIELD(HIF_TXP_NON_CIPHER, dw));
++		info_or_seq_printf(s, "HIF_TXP_VLAN = %d\n",
++				GET_FIELD(HIF_TXP_VLAN, dw));
++		info_or_seq_printf(s, "HIF_TXP_BC_MC_FLAG = %d\n",
++				GET_FIELD(HIF_TXP_BC_MC_FLAG, dw));
++		info_or_seq_printf(s, "HIF_TXP_FR_HOST = %d\n",
++				GET_FIELD(HIF_TXP_FR_HOST, dw));
++		info_or_seq_printf(s, "HIF_TXP_ETYPE = %d\n",
++				GET_FIELD(HIF_TXP_ETYPE, dw));
++		info_or_seq_printf(s, "HIF_TXP_TXP_AMSDU = %d\n",
++				GET_FIELD(HIF_TXP_TXP_AMSDU, dw));
++		info_or_seq_printf(s, "HIF_TXP_TXP_MC_CLONE = %d\n",
++				GET_FIELD(HIF_TXP_TXP_MC_CLONE, dw));
++		info_or_seq_printf(s, "HIF_TXP_TOKEN_ID = %d\n",
++				GET_FIELD(HIF_TXP_TOKEN_ID, dw));
++
++		/* dw1 */
++		dw = le32_to_cpu(txp[1]);
++		info_or_seq_printf(s, "HIF_TXP_BSS_IDX = %d\n",
++				GET_FIELD(HIF_TXP_BSS_IDX, dw));
++		info_or_seq_printf(s, "HIF_TXP_USER_PRIORITY = %d\n",
++				GET_FIELD(HIF_TXP_USER_PRIORITY, dw));
++		info_or_seq_printf(s, "HIF_TXP_BUF_NUM = %d\n",
++				GET_FIELD(HIF_TXP_BUF_NUM, dw));
++		info_or_seq_printf(s, "HIF_TXP_MSDU_CNT = %d\n",
++				GET_FIELD(HIF_TXP_MSDU_CNT, dw));
++		info_or_seq_printf(s, "HIF_TXP_SRC = %d\n",
++				GET_FIELD(HIF_TXP_SRC, dw));
++
++		/* dw2 */
++		dw = le32_to_cpu(txp[2]);
++		info_or_seq_printf(s, "HIF_TXP_ETH_TYPE(network-endian) = 0x%x\n",
++				GET_FIELD(HIF_TXP_ETH_TYPE, dw));
++		info_or_seq_printf(s, "HIF_TXP_WLAN_IDX = %d\n",
++				GET_FIELD(HIF_TXP_WLAN_IDX, dw));
++
++		/* dw3 */
++		dw = le32_to_cpu(txp[3]);
++		info_or_seq_printf(s, "HIF_TXP_PPE_INFO = 0x%x\n",
++				GET_FIELD(HIF_TXP_PPE_INFO, dw));
++
++		for (i = 0; i < 13; i++) {
++			if (i % 2 == 0) {
++				info_or_seq_printf(s, "HIF_TXP_BUF_PTR%d_L = 0x%x\n",
++						i, GET_FIELD(HIF_TXP_BUF_PTR0_L,
++						le32_to_cpu(txp[4 + j])));
++				j++;
++				info_or_seq_printf(s, "HIF_TXP_BUF_LEN%d = %d\n",
++						i, GET_FIELD(HIF_TXP_BUF_LEN0, le32_to_cpu(txp[4 + j])));
++				info_or_seq_printf(s, "HIF_TXP_BUF_PTR%d_H = 0x%x\n",
++						i, GET_FIELD(HIF_TXP_BUF_PTR0_H, le32_to_cpu(txp[4 + j])));
++				if (i <= 10) {
++					info_or_seq_printf(s, "HIF_TXP_BUF_LEN%d = %d\n",
++							i + 1, GET_FIELD(HIF_TXP_BUF_LEN1, le32_to_cpu(txp[4 + j])));
++					info_or_seq_printf(s, "HIF_TXP_BUF_PTR%d_H = 0x%x\n",
++							i + 1, GET_FIELD(HIF_TXP_BUF_PTR1_H, le32_to_cpu(txp[4 + j])));
++				}
++				j++;
++			} else {
++				info_or_seq_printf(s, "HIF_TXP_BUF_PTR%d_L = 0x%x\n",
++					i, GET_FIELD(HIF_TXP_BUF_PTR1_L,
++					le32_to_cpu(txp[4 + j])));
++				j++;
++			}
++		}
++
++		info_or_seq_printf(s, "ml = 0x%x\n",
++			GET_FIELD(HIF_TXP_ML, le32_to_cpu(txp[23])));
++	} else {
++		struct mt76_connac_txp_common *txp_v1 = (struct mt76_connac_txp_common *)txp;
++
++		info_or_seq_printf(s, "FLAGS = (%04x)\n", txp_v1->fw.flags);
++
++		info_or_seq_printf(s, "MSDU = %d\n", txp_v1->fw.token);
++
++		info_or_seq_printf(s, "BSS_IDX = %d\n", txp_v1->fw.bss_idx);
++
++		info_or_seq_printf(s, "WCID = %d\n",txp_v1->fw.rept_wds_wcid);
++
++		info_or_seq_printf(s, "MSDU_CNT = %d\n", txp_v1->fw.nbuf);
++
++		for (i = 0; i < MT_TXP_MAX_BUF_NUM; i++)
++			info_or_seq_printf(s, "ptr%02d : addr(0x%08x) len(%d)\n", i, le32_to_cpu(txp_v1->fw.buf[i]),
++				le16_to_cpu(txp_v1->fw.len[i]));
++	}
++}
++
++/* bmac txd dump */
++void mt7996_dump_bmac_txd_info(struct seq_file *s, struct mt7996_dev *dev,
++			       __le32 *txd, bool is_hif_txd, bool dump_txp)
++{
++	u32 hif_txp_ver = 0;
++
++	/* dump stop */
++	if (!dev->dbg.txd_read_cnt)
++		return;
++
++	/* force dump */
++	if (dev->dbg.txd_read_cnt > 8)
++		dev->dbg.txd_read_cnt = 8;
++
++	/* dump txd_read_cnt times */
++	if (dev->dbg.txd_read_cnt != 8)
++		dev->dbg.txd_read_cnt--;
++
++	info_or_seq_printf(s, "txd raw data: size=%d\n", MT_TXD_SIZE);
++	info_or_seq_hex_dump(s, DUMP_PREFIX_OFFSET, 16, 1, (u8 *)txd, MT_TXD_SIZE, false);
++
++	info_or_seq_printf(s, "BMAC_TXD Fields:\n");
++	/* dw0 */
++	if (is_hif_txd) {
++		hif_txp_ver = FIELD_GET(GENMASK(22, 19), txd[0]);
++		info_or_seq_printf(s, "HIF TXD VER = %d\n", hif_txp_ver);
++	}
++	info_or_seq_printf(s, "TX_BYTE_COUNT = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_TX_BYTE_COUNT, txd[0]));
++	info_or_seq_printf(s, "ETHER_TYPE_OFFSET(word) = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_ETHER_TYPE_OFFSET, txd[0]));
++	info_or_seq_printf(s, "PKT_FT = %d%s%s%s%s\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_PKT_FT, txd[0]),
++			GET_FIELD(WF_TX_DESCRIPTOR_PKT_FT, txd[0]) == 0 ? "(ct)" : "",
++			GET_FIELD(WF_TX_DESCRIPTOR_PKT_FT, txd[0]) == 1 ? "(s&f)" : "",
++			GET_FIELD(WF_TX_DESCRIPTOR_PKT_FT, txd[0]) == 2 ? "(cmd)" : "",
++			GET_FIELD(WF_TX_DESCRIPTOR_PKT_FT, txd[0]) == 3 ? "(redirect)" : "");
++	info_or_seq_printf(s, "Q_IDX = %d%s%s%s\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_Q_IDX, txd[0]),
++			GET_FIELD(WF_TX_DESCRIPTOR_Q_IDX, txd[0]) == 0x10 ? "(ALTX)" : "",
++			GET_FIELD(WF_TX_DESCRIPTOR_Q_IDX, txd[0]) == 0x11 ? "(BMC)" : "",
++			GET_FIELD(WF_TX_DESCRIPTOR_Q_IDX, txd[0]) == 0x12 ? "(BCN)" : "");
++
++	/* dw1 */
++	info_or_seq_printf(s, "MLD_ID = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_MLD_ID, txd[1]));
++	info_or_seq_printf(s, "TGID = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_TGID, txd[1]));
++	info_or_seq_printf(s, "HF = %d%s%s%s%s\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_HF, txd[1]),
++			GET_FIELD(WF_TX_DESCRIPTOR_HF, txd[1]) == 0 ? "(eth/802.3)" : "",
++			GET_FIELD(WF_TX_DESCRIPTOR_HF, txd[1]) == 1 ? "(cmd)" : "",
++			GET_FIELD(WF_TX_DESCRIPTOR_HF, txd[1]) == 2 ? "(802.11)" : "",
++			GET_FIELD(WF_TX_DESCRIPTOR_HF, txd[1]) == 3 ? "(802.11 enhanced" : "");
++	info_or_seq_printf(s, "802.11 HEADER_LENGTH = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_HF, txd[1]) == 2 ?
++			GET_FIELD(WF_TX_DESCRIPTOR_HEADER_LENGTH, txd[1]) : 0);
++	info_or_seq_printf(s, "MRD = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_HF, txd[1]) == 0 ?
++			GET_FIELD(WF_TX_DESCRIPTOR_MRD, txd[1]) : 0);
++	info_or_seq_printf(s, "EOSP = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_HF, txd[1]) == 0 ?
++			GET_FIELD(WF_TX_DESCRIPTOR_EOSP, txd[1]) : 0);
++	info_or_seq_printf(s, "AMS = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_HF, txd[1]) == 3 ?
++			GET_FIELD(WF_TX_DESCRIPTOR_AMS, txd[1]) : 0);
++	info_or_seq_printf(s, "RMVL = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_HF, txd[1]) == 0 ?
++			GET_FIELD(WF_TX_DESCRIPTOR_RMVL, txd[1]): 0);
++	info_or_seq_printf(s, "VLAN = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_HF, txd[1]) == 0 ?
++			GET_FIELD(WF_TX_DESCRIPTOR_VLAN, txd[1]) : 0);
++	info_or_seq_printf(s, "ETYP = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_HF, txd[1]) == 0 ?
++			GET_FIELD(WF_TX_DESCRIPTOR_ETYP, txd[1]) : 0);
++	info_or_seq_printf(s, "TID_MGMT_TYPE = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_TID_MGMT_TYPE, txd[1]));
++	info_or_seq_printf(s, "OM = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_OM, txd[1]));
++	info_or_seq_printf(s, "FR = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_FR, txd[1]));
++
++	/* dw2 */
++	info_or_seq_printf(s, "SUBTYPE = %d%s%s%s%s\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_SUBTYPE, txd[2]),
++			(GET_FIELD(WF_TX_DESCRIPTOR_FTYPE, txd[2]) == 0) &&
++			(GET_FIELD(WF_TX_DESCRIPTOR_SUBTYPE, txd[2]) == 13) ?
++			"(action)" : "",
++			(GET_FIELD(WF_TX_DESCRIPTOR_FTYPE, txd[2]) == 1) &&
++			(GET_FIELD(WF_TX_DESCRIPTOR_SUBTYPE, txd[2]) == 8) ?
++			"(bar)" : "",
++			(GET_FIELD(WF_TX_DESCRIPTOR_FTYPE, txd[2]) == 2) &&
++			(GET_FIELD(WF_TX_DESCRIPTOR_SUBTYPE, txd[2]) == 4) ?
++			"(null)" : "",
++			(GET_FIELD(WF_TX_DESCRIPTOR_FTYPE, txd[2]) == 2) &&
++			(GET_FIELD(WF_TX_DESCRIPTOR_SUBTYPE, txd[2]) == 12) ?
++			"(qos null)" : "");
++
++	info_or_seq_printf(s, "FTYPE = %d%s%s%s\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_FTYPE, txd[2]),
++			GET_FIELD(WF_TX_DESCRIPTOR_FTYPE, txd[2]) == 0 ? "(mgmt)" : "",
++			GET_FIELD(WF_TX_DESCRIPTOR_FTYPE, txd[2]) == 1 ? "(ctl)" : "",
++			GET_FIELD(WF_TX_DESCRIPTOR_FTYPE, txd[2]) == 2 ? "(data)" : "");
++	info_or_seq_printf(s, "BF_TYPE = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_BF_TYPE, txd[2]));
++	info_or_seq_printf(s, "OM_MAP = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_OM_MAP, txd[2]));
++	info_or_seq_printf(s, "RTS = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_RTS, txd[2]));
++	info_or_seq_printf(s, "HEADER_PADDING = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_HEADER_PADDING, txd[2]));
++	info_or_seq_printf(s, "DU = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_DU, txd[2]));
++	info_or_seq_printf(s, "HE = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_HE, txd[2]));
++	info_or_seq_printf(s, "FRAG = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_FRAG, txd[2]));
++	info_or_seq_printf(s, "REMAINING_TX_TIME = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_REMAINING_TX_TIME, txd[2]));
++	info_or_seq_printf(s, "POWER_OFFSET = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_POWER_OFFSET, txd[2]));
++
++	/* dw3 */
++	info_or_seq_printf(s, "NA = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_NA, txd[3]));
++	info_or_seq_printf(s, "PF = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_PF, txd[3]));
++	info_or_seq_printf(s, "EMRD = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_EMRD, txd[3]));
++	info_or_seq_printf(s, "EEOSP = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_EEOSP, txd[3]));
++	info_or_seq_printf(s, "BM = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_BM, txd[3]));
++	info_or_seq_printf(s, "HW_AMSDU_CAP = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_HW_AMSDU_CAP, txd[3]));
++	info_or_seq_printf(s, "TX_COUNT = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_TX_COUNT, txd[3]));
++	info_or_seq_printf(s, "REMAINING_TX_COUNT = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_REMAINING_TX_COUNT, txd[3]));
++	info_or_seq_printf(s, "SN = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_SN, txd[3]));
++	info_or_seq_printf(s, "BA_DIS = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_BA_DIS, txd[3]));
++	info_or_seq_printf(s, "PM = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_PM, txd[3]));
++	info_or_seq_printf(s, "PN_VLD = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_PN_VLD, txd[3]));
++	info_or_seq_printf(s, "SN_VLD = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_SN_VLD, txd[3]));
++
++	/* dw4 */
++	info_or_seq_printf(s, "PN_31_0 = 0x%x\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_PN_31_0_, txd[4]));
++
++	/* dw5 */
++	info_or_seq_printf(s, "PID = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_PID, txd[5]));
++	info_or_seq_printf(s, "TXSFM = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_TXSFM, txd[5]));
++	info_or_seq_printf(s, "TXS2M = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_TXS2M, txd[5]));
++	info_or_seq_printf(s, "TXS2H = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_TXS2H, txd[5]));
++	info_or_seq_printf(s, "FBCZ = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_FBCZ, txd[5]));
++	info_or_seq_printf(s, "BYPASS_RBB = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_BYPASS_RBB, txd[5]));
++
++	info_or_seq_printf(s, "FL = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_FL, txd[5]));
++	info_or_seq_printf(s, "PN_47_32 = 0x%x\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_PN_47_32_, txd[5]));
++
++	/* dw6 */
++	info_or_seq_printf(s, "AMSDU_CAP_UTXB = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_AMSDU_CAP_UTXB, txd[6]));
++	info_or_seq_printf(s, "DAS = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_DAS, txd[6]));
++	info_or_seq_printf(s, "DIS_MAT = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_DIS_MAT, txd[6]));
++	info_or_seq_printf(s, "MSDU_COUNT = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_MSDU_COUNT, txd[6]));
++	info_or_seq_printf(s, "TIMESTAMP_OFFSET = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_TIMESTAMP_OFFSET_IDX, txd[6]));
++	info_or_seq_printf(s, "FIXED_RATE_IDX = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_FIXED_RATE_IDX, txd[6]));
++	info_or_seq_printf(s, "BW = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_BW, txd[6]));
++	info_or_seq_printf(s, "VTA = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_VTA, txd[6]));
++	info_or_seq_printf(s, "SRC = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_SRC, txd[6]));
++
++	/* dw7 */
++	info_or_seq_printf(s, "SW_TX_TIME(unit:65536ns) = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_SW_TX_TIME , txd[7]));
++	info_or_seq_printf(s, "UT = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_UT, txd[7]));
++	info_or_seq_printf(s, "CTXD_CNT = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_CTXD_CNT, txd[7]));
++	info_or_seq_printf(s, "HM = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_HM, txd[7]));
++	info_or_seq_printf(s, "DP = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_DP, txd[7]));
++	info_or_seq_printf(s, "IP = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_IP, txd[7]));
++	info_or_seq_printf(s, "TXD_LEN = %d\n",
++			GET_FIELD(WF_TX_DESCRIPTOR_TXD_LEN, txd[7]));
++
++	if (dump_txp) {
++		__le32 *txp = txd + 8;
++
++		if (is_hif_txd)
++			mt7996_dump_bmac_hif_txp_info(s, dev, txp, hif_txp_ver);
++		else
++			mt7996_dump_bmac_mac_txp_info(s, dev, txp);
++	}
++}
++
++static void
++mt7996_dump_mac_fid(struct seq_file *s, struct mt7996_dev *dev, u32 fid, bool is_ple)
++{
++#define PLE_MEM_SIZE	 128
++#define PSE_MEM_SIZE	 256
++	 u8 data[PSE_MEM_SIZE] = {0};
++	 u32 addr = 0;
++	 int i = 0, cr_cnt = PSE_MEM_SIZE;
++	 u32 *ptr = (u32 *) data;
++
++	 if (is_ple) {
++		cr_cnt = PLE_MEM_SIZE;
++		seq_printf(s, "dump ple: fid = 0x%08x\n", fid);
++	 } else {
++		seq_printf(s, "dump pse: fid = 0x%08x\n", fid);
++	 }
++
++	 for (i = 0; i < cr_cnt; i = i + 4) {
++		if (is_ple)
++			addr = (0xa << 28 | fid << 15) + i;
++		else
++			addr = (0xb << 28 | fid << 15) + i;
++		*ptr = mt76_rr(dev, addr);
++		ptr++;
++	 }
++
++	 seq_printf(s, "raw data: size=%d\n", cr_cnt);
++
++	 seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 16, 1, (u8 *)data, cr_cnt, false);
++	 /* dump one txd info */
++	 if (is_ple) {
++		 dev->dbg.txd_read_cnt = 1;
++		 mt7996_dump_bmac_txd_info(s, dev, (__le32 *)&data[0], false, true);
++	 }
++}
++
++static int
++mt7996_ple_fid_read(struct seq_file *s, void *data) {
++	 struct mt7996_dev *dev = dev_get_drvdata(s->private);
++
++	 mt7996_dump_mac_fid(s, dev, dev->dbg.fid_idx, true);
++	 return 0;
++}
++
++static int
++mt7996_pse_fid_read(struct seq_file *s, void *data) {
++	 struct mt7996_dev *dev = dev_get_drvdata(s->private);
++
++	 mt7996_dump_mac_fid(s, dev, dev->dbg.fid_idx, false);
++	 return 0;
++}
++
++void mt7996_dump_bmac_rxd_info(struct mt7996_dev *dev, __le32 *rxd)
++{
++	/* dump stop */
++	if (!dev->dbg.rxd_read_cnt)
++		return;
++
++	/* force dump */
++	if (dev->dbg.rxd_read_cnt > 8)
++		dev->dbg.rxd_read_cnt = 8;
++
++	/* dump txd_read_cnt times */
++	if (dev->dbg.rxd_read_cnt != 8)
++		dev->dbg.rxd_read_cnt--;
++
++	printk("rxd raw data: size=%d\n", MT_TXD_SIZE);
++	print_hex_dump(KERN_ERR , "", DUMP_PREFIX_OFFSET, 16, 1, (u8 *)rxd, 96, false);
++
++	printk("BMAC_RXD Fields:\n");
++
++	/* group0 */
++	/* dw0 */
++	printk("RX_BYTE_COUNT = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_RX_BYTE_COUNT, le32_to_cpu(rxd[0])));
++	printk("PACKET_TYPE = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_PACKET_TYPE, le32_to_cpu(rxd[0])));
++
++	/* dw1 */
++	printk("MLD_ID = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_MLD_ID, le32_to_cpu(rxd[1])));
++	printk("GROUP_VLD = 0x%x%s%s%s%s%s\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_GROUP_VLD, le32_to_cpu(rxd[1])),
++			GET_FIELD(WF_RX_DESCRIPTOR_GROUP_VLD, le32_to_cpu(rxd[1]))
++			& BMAC_GROUP_VLD_1 ? "[group1]" : "",
++			GET_FIELD(WF_RX_DESCRIPTOR_GROUP_VLD, le32_to_cpu(rxd[1]))
++			& BMAC_GROUP_VLD_2 ? "[group2]" : "",
++			GET_FIELD(WF_RX_DESCRIPTOR_GROUP_VLD, le32_to_cpu(rxd[1]))
++			& BMAC_GROUP_VLD_3 ? "[group3]" : "",
++			GET_FIELD(WF_RX_DESCRIPTOR_GROUP_VLD, le32_to_cpu(rxd[1]))
++			& BMAC_GROUP_VLD_4 ? "[group4]" : "",
++			GET_FIELD(WF_RX_DESCRIPTOR_GROUP_VLD, le32_to_cpu(rxd[1]))
++			& BMAC_GROUP_VLD_5 ? "[group5]" : "");
++	printk("KID = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_KID, le32_to_cpu(rxd[1])));
++	printk("CM = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_CM, le32_to_cpu(rxd[1])));
++	printk("CLM = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_CLM, le32_to_cpu(rxd[1])));
++	printk("I = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_I, le32_to_cpu(rxd[1])));
++	printk("T = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_T, le32_to_cpu(rxd[1])));
++	printk("BN = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_BN, le32_to_cpu(rxd[1])));
++	printk("BIPN_FAIL = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_BIPN_FAIL, le32_to_cpu(rxd[1])));
++
++	/* dw2 */
++	printk("BSSID = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_BSSID, le32_to_cpu(rxd[2])));
++	printk("H = %d%s\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_H, le32_to_cpu(rxd[2])),
++			GET_FIELD(WF_RX_DESCRIPTOR_H, le32_to_cpu(rxd[2])) == 0 ?
++			"802.11 frame" : "eth/802.3 frame");
++	printk("HEADER_LENGTH(word) = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_HEADER_LENGTH, le32_to_cpu(rxd[2])));
++	printk("HO(word) = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_HO, le32_to_cpu(rxd[2])));
++	printk("SEC_MODE = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_SEC_MODE, le32_to_cpu(rxd[2])));
++	printk("MUBAR = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_MUBAR, le32_to_cpu(rxd[2])));
++	printk("SWBIT = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_SWBIT, le32_to_cpu(rxd[2])));
++	printk("DAF = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_DAF, le32_to_cpu(rxd[2])));
++	printk("EL = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_EL, le32_to_cpu(rxd[2])));
++	printk("HTF = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_HTF, le32_to_cpu(rxd[2])));
++	printk("INTF = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_INTF, le32_to_cpu(rxd[2])));
++	printk("FRAG = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_FRAG, le32_to_cpu(rxd[2])));
++	printk("NUL = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_NUL, le32_to_cpu(rxd[2])));
++	printk("NDATA = %d%s\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_NDATA, le32_to_cpu(rxd[2])),
++			GET_FIELD(WF_RX_DESCRIPTOR_NDATA, le32_to_cpu(rxd[2])) == 0 ?
++			"[data frame]" : "[mgmt/ctl frame]");
++	printk("NAMP = %d%s\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_NAMP, le32_to_cpu(rxd[2])),
++			GET_FIELD(WF_RX_DESCRIPTOR_NAMP, le32_to_cpu(rxd[2])) == 0 ?
++			"[ampdu frame]" : "[mpdu frame]");
++	printk("BF_RPT = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_BF_RPT, le32_to_cpu(rxd[2])));
++
++	/* dw3 */
++	printk("RXV_SN = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_RXV_SN, le32_to_cpu(rxd[3])));
++	printk("CH_FREQUENCY = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_CH_FREQUENCY, le32_to_cpu(rxd[3])));
++	printk("A1_TYPE = %d%s%s%s%s\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_A1_TYPE, le32_to_cpu(rxd[3])),
++			GET_FIELD(WF_RX_DESCRIPTOR_A1_TYPE, le32_to_cpu(rxd[3])) == 0 ?
++			"[reserved]" : "",
++			GET_FIELD(WF_RX_DESCRIPTOR_A1_TYPE, le32_to_cpu(rxd[3])) == 1 ?
++			"[uc2me]" : "",
++			GET_FIELD(WF_RX_DESCRIPTOR_A1_TYPE, le32_to_cpu(rxd[3])) == 2 ?
++			"[mc]" : "",
++			GET_FIELD(WF_RX_DESCRIPTOR_A1_TYPE, le32_to_cpu(rxd[3])) == 3 ?
++			"[bc]" : "");
++	printk("HTC = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_HTC, le32_to_cpu(rxd[3])));
++	printk("TCL = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_TCL, le32_to_cpu(rxd[3])));
++	printk("BBM = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_BBM, le32_to_cpu(rxd[3])));
++	printk("BU = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_BU, le32_to_cpu(rxd[3])));
++	printk("CO_ANT = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_CO_ANT, le32_to_cpu(rxd[3])));
++	printk("BF_CQI = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_BF_CQI, le32_to_cpu(rxd[3])));
++	printk("FC = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_FC, le32_to_cpu(rxd[3])));
++	printk("VLAN = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_VLAN, le32_to_cpu(rxd[3])));
++
++	/* dw4 */
++	printk("PF = %d%s%s%s%s\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_PF, le32_to_cpu(rxd[4])),
++			GET_FIELD(WF_RX_DESCRIPTOR_PF, le32_to_cpu(rxd[4])) == 0 ?
++			"[msdu]" : "",
++			GET_FIELD(WF_RX_DESCRIPTOR_PF, le32_to_cpu(rxd[4])) == 1 ?
++			"[final amsdu]" : "",
++			GET_FIELD(WF_RX_DESCRIPTOR_PF, le32_to_cpu(rxd[4])) == 2 ?
++			"[middle amsdu]" : "",
++			GET_FIELD(WF_RX_DESCRIPTOR_PF, le32_to_cpu(rxd[4])) == 3 ?
++			"[first amsdu]" : "");
++	printk("MAC = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_MAC, le32_to_cpu(rxd[4])));
++	printk("TID = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_TID, le32_to_cpu(rxd[4])));
++	printk("ETHER_TYPE_OFFSET = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_ETHER_TYPE_OFFSET, le32_to_cpu(rxd[4])));
++	printk("IP = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_IP, le32_to_cpu(rxd[4])));
++	printk("UT = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_UT, le32_to_cpu(rxd[4])));
++	printk("PSE_FID = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_PSE_FID, le32_to_cpu(rxd[4])));
++
++	/* group4 */
++	/* dw0 */
++	printk("FRAME_CONTROL_FIELD = 0x%x\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_GROUP_VLD, le32_to_cpu(rxd[1]))
++			& BMAC_GROUP_VLD_4 ?
++			GET_FIELD(WF_RX_DESCRIPTOR_FRAME_CONTROL_FIELD, le32_to_cpu(rxd[8])) : 0);
++	printk("PEER_MLD_ADDRESS_15_0 = 0x%x\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_GROUP_VLD, le32_to_cpu(rxd[1]))
++			& BMAC_GROUP_VLD_4 ?
++			GET_FIELD(WF_RX_DESCRIPTOR_PEER_MLD_ADDRESS_15_0_,
++			le32_to_cpu(rxd[8])) : 0);
++
++	/* dw1 */
++	printk("PEER_MLD_ADDRESS_47_16 = 0x%x\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_GROUP_VLD, le32_to_cpu(rxd[1]))
++			& BMAC_GROUP_VLD_4 ?
++			GET_FIELD(WF_RX_DESCRIPTOR_PEER_MLD_ADDRESS_47_16_,
++			le32_to_cpu(rxd[9])) : 0);
++
++	/* dw2 */
++	printk("FRAGMENT_NUMBER = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_GROUP_VLD, le32_to_cpu(rxd[1]))
++			& BMAC_GROUP_VLD_4 ?
++			GET_FIELD(WF_RX_DESCRIPTOR_FRAGMENT_NUMBER,
++			le32_to_cpu(rxd[10])) : 0);
++	printk("SEQUENCE_NUMBER = %d\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_GROUP_VLD, le32_to_cpu(rxd[1]))
++			& BMAC_GROUP_VLD_4 ?
++			GET_FIELD(WF_RX_DESCRIPTOR_SEQUENCE_NUMBER,
++			le32_to_cpu(rxd[10])) : 0);
++	printk("QOS_CONTROL_FIELD = 0x%x\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_GROUP_VLD, le32_to_cpu(rxd[1]))
++			& BMAC_GROUP_VLD_4 ?
++			GET_FIELD(WF_RX_DESCRIPTOR_QOS_CONTROL_FIELD,
++			le32_to_cpu(rxd[10])) : 0);
++
++	/* dw3 */
++	printk("HT_CONTROL_FIELD = 0x%x\n",
++			GET_FIELD(WF_RX_DESCRIPTOR_GROUP_VLD, le32_to_cpu(rxd[1]))
++			& BMAC_GROUP_VLD_4 ?
++			GET_FIELD(WF_RX_DESCRIPTOR_HT_CONTROL_FIELD,
++			le32_to_cpu(rxd[11])) : 0);
++}
++
++static int mt7996_token_txd_read(struct seq_file *s, void *data)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	struct mt76_txwi_cache *t;
++	u8* txwi;
++
++	seq_printf(s, "\n");
++	spin_lock_bh(&dev->mt76.token_lock);
++
++	t = idr_find(&dev->mt76.token, dev->dbg.token_idx);
++	if (t != NULL) {
++		struct mt76_dev *mdev = &dev->mt76;
++		txwi = ((u8*)(t)) - (mdev->drv->txwi_size);
++		/* dump one txd info */
++		dev->dbg.txd_read_cnt = 1;
++		mt7996_dump_bmac_txd_info(s, dev, (__le32 *)txwi, true, true);
++		seq_printf(s, "\n");
++		seq_printf(s, "[SKB]\n");
++		seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 16, 1, (u8 *)t->skb->data, t->skb->len, false);
++		seq_printf(s, "\n");
++	}
++	spin_unlock_bh(&dev->mt76.token_lock);
++	return 0;
++}
++
++static int mt7996_rx_msdu_pg_read(struct seq_file *s, void *data)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	struct list_head *p;
++	int i, count = 0, total = 0;
++
++	seq_printf(s, "Rx Msdu page:\n");
++	spin_lock(&dev->wed_rro.lock);
++	for (i = 0; i < MT7996_RRO_MSDU_PG_HASH_SIZE; i++) {
++		list_for_each(p, &dev->wed_rro.pg_hash_head[i]) {
++			count++;
++		}
++	}
++
++	total = count;
++	list_for_each(p, &dev->wed_rro.pg_addr_cache) {
++		total++;
++	}
++	seq_printf(s, "\ttotal:%8d used:%8d\n", total, count);
++	spin_unlock(&dev->wed_rro.lock);
++
++	return 0;
++}
++
++int mt7996_mtk_init_debugfs_internal(struct mt7996_phy *phy, struct dentry *dir)
++{
++	struct mt7996_dev *dev = phy->dev;
++
++	debugfs_create_devm_seqfile(dev->mt76.dev, "token_txd", dir,
++				    mt7996_token_txd_read);
++	debugfs_create_u32("txd_dump", 0600, dir, &dev->dbg.txd_read_cnt);
++	debugfs_create_u32("rxd_dump", 0600, dir, &dev->dbg.rxd_read_cnt);
++	debugfs_create_devm_seqfile(dev->mt76.dev, "rx_msdu_pg", dir,
++				    mt7996_rx_msdu_pg_read);
++
++	/* ple/pse fid raw data dump */
++	debugfs_create_u32("fid_idx", 0600, dir, &dev->dbg.fid_idx);
++	debugfs_create_devm_seqfile(dev->mt76.dev, "ple_fid", dir,
++				    mt7996_ple_fid_read);
++	debugfs_create_devm_seqfile(dev->mt76.dev, "pse_fid", dir,
++				    mt7996_pse_fid_read);
++
++	debugfs_create_u8("dump_ple_txd", 0600, dir, &dev->dbg.dump_ple_txd);
++	return 0;
++}
++
++#endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0121-mtk-mt76-mt7996-add-linux-tracing-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0121-mtk-mt76-mt7996-add-linux-tracing-support.patch
new file mode 100644
index 0000000..70831fd
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0121-mtk-mt76-mt7996-add-linux-tracing-support.patch
@@ -0,0 +1,394 @@
+From 5ef5041a7200a4e3877df4c3533bd3c5e2038615 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 7 Sep 2022 12:13:20 +0800
+Subject: [PATCH 121/199] mtk: mt76: mt7996: add linux tracing support
+
+Add static tracepoint support for besra.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ agg-rx.c              |   2 +
+ mac80211.c            |   3 +
+ mt7996/Makefile       |   2 +-
+ mt7996/mac.c          |   6 ++
+ mt7996/mcu.c          |   8 +++
+ mt7996/mt7996_trace.h | 141 ++++++++++++++++++++++++++++++++++++++++++
+ mt7996/trace.c        |  12 ++++
+ trace.h               |  58 +++++++++++++++++
+ 8 files changed, 231 insertions(+), 1 deletion(-)
+ create mode 100644 mt7996/mt7996_trace.h
+ create mode 100644 mt7996/trace.c
+
+diff --git a/agg-rx.c b/agg-rx.c
+index 07c386c7..b48943c4 100644
+--- a/agg-rx.c
++++ b/agg-rx.c
+@@ -3,6 +3,7 @@
+  * Copyright (C) 2018 Felix Fietkau <nbd@nbd.name>
+  */
+ #include "mt76.h"
++#include "trace.h"
+ 
+ static unsigned long mt76_aggr_tid_to_timeo(u8 tidno)
+ {
+@@ -187,6 +188,7 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
+ 	seqno = status->seqno;
+ 	size = tid->size;
+ 	sn_less = ieee80211_sn_less(seqno, head);
++	trace_mt76_rx_aggr_reorder(tid->dev, wcid, head, seqno, sn_less);
+ 
+ 	if (!tid->started) {
+ 		if (sn_less)
+diff --git a/mac80211.c b/mac80211.c
+index 5e4935f2..dc91e827 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -5,6 +5,7 @@
+ #include <linux/sched.h>
+ #include <linux/of.h>
+ #include "mt76.h"
++#include "trace.h"
+ 
+ static const struct ieee80211_channel mt76_channels_2ghz[] = {
+ 	CHAN2G(1, 2412),
+@@ -1307,6 +1308,7 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
+ 
+ 		mt76_check_ccmp_pn(skb);
+ 		skb_shinfo(skb)->frag_list = NULL;
++		trace_mt76_rx_complete(dev, (struct mt76_rx_status *)skb->cb, 0);
+ 		mt76_rx_convert(dev, skb, &hw, &sta);
+ 		ieee80211_rx_list(hw, sta, skb, &list);
+ 
+@@ -1316,6 +1318,7 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
+ 			nskb = nskb->next;
+ 			skb->next = NULL;
+ 
++			trace_mt76_rx_complete(dev, (struct mt76_rx_status *)skb->cb, 1);
+ 			mt76_rx_convert(dev, skb, &hw, &sta);
+ 			ieee80211_rx_list(hw, sta, skb, &list);
+ 		}
+diff --git a/mt7996/Makefile b/mt7996/Makefile
+index 49ec9154..936edc61 100644
+--- a/mt7996/Makefile
++++ b/mt7996/Makefile
+@@ -12,4 +12,4 @@ mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
+ mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o
+ mt7996e-$(CONFIG_NL80211_TESTMODE) += testmode.o
+ 
+-mt7996e-y += mtk_debugfs.o mtk_mcu.o mtk_debugfs_i.o
++mt7996e-y += mtk_debugfs.o mtk_mcu.o mtk_debugfs_i.o trace.o
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index c6816ab5..6ba07156 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -11,6 +11,7 @@
+ #include "mac.h"
+ #include "mcu.h"
+ #include "vendor.h"
++#include "mt7996_trace.h"
+ 
+ static const struct mt7996_dfs_radar_spec etsi_radar_specs = {
+ 	.pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 },
+@@ -344,6 +345,8 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ 	phy = mphy->priv;
+ 	status->phy_idx = mphy->band_idx;
+ 
++	trace_mt7996_fill_rx(phy, skb->data, skb->len);
++
+ 	if (!test_bit(MT76_STATE_RUNNING, &mphy->state))
+ 		return -EINVAL;
+ 
+@@ -609,6 +612,8 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ 	    !(status->flag & RX_FLAG_8023))
+ 		mt76_connac3_mac_decode_he_radiotap(skb, rxv, mode);
+ 
++	trace_mt7996_fill_rx_done(phy, status->seqno, hdr_gap);
++
+ 	if (!status->wcid || !ieee80211_is_data_qos(fc) || hw_aggr)
+ 		return 0;
+ 
+@@ -998,6 +1003,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 		mt7996_packet_log_to_host(dev, t->skb->data, t->skb->len, PKT_BIN_DEBUG_TX, 0);
+ 	mt7996_dump_bmac_txd_info(NULL, dev, (__le32 *)txwi, true, false);
+ #endif
++	trace_mt7996_tx_prepare(dev, wcid, qid, txwi, t->skb->data, t->skb->len);
+ 
+ 	return 0;
+ }
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 0b02b76b..1cc08a30 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -10,6 +10,7 @@
+ #include "mcu.h"
+ #include "mac.h"
+ #include "eeprom.h"
++#include "mt7996_trace.h"
+ 
+ #define fw_name(_dev, name, ...)	({			\
+ 	char *_fw;						\
+@@ -305,6 +306,9 @@ mt7996_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
+ 		else if (cmd & __MCU_CMD_FIELD_WM)
+ 			uni_txd->s2d_index = MCU_S2D_H2N;
+ 
++		trace_mt7996_mcu_cmd(dev, 1, uni_txd->cid, 0,
++				    skb->data, skb->len);
++
+ 		goto exit;
+ 	}
+ 
+@@ -332,6 +336,8 @@ mt7996_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
+ 	else
+ 		mcu_txd->s2d_index = MCU_S2D_H2N;
+ 
++	trace_mt7996_mcu_cmd(dev, 0, mcu_txd->cid, mcu_txd->ext_cid,
++			    skb->data, skb->len);
+ exit:
+ #ifdef CONFIG_MTK_DEBUG
+ 	if (dev->dbg.dump_mcu_pkt)
+@@ -1177,6 +1183,8 @@ void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+ 	struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data;
+ 
++	trace_mt7996_mcu_event(dev, rxd->option & MCU_UNI_CMD_UNSOLICITED_EVENT,
++			      rxd->eid, rxd->ext_eid, skb->data, skb->len);
+ 	if (rxd->option & MCU_UNI_CMD_UNSOLICITED_EVENT) {
+ 		mt7996_mcu_uni_rx_unsolicited_event(dev, skb);
+ 		return;
+diff --git a/mt7996/mt7996_trace.h b/mt7996/mt7996_trace.h
+new file mode 100644
+index 00000000..5fa73482
+--- /dev/null
++++ b/mt7996/mt7996_trace.h
+@@ -0,0 +1,141 @@
++/* SPDX-License-Identifier: ISC */
++/*
++ * Copyright (C) 2022 MediaTek Inc.
++ */
++
++#if !defined(__MT7996_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
++#define __MT7996_TRACE_H
++
++#include <linux/types.h>
++#include <linux/tracepoint.h>
++#include "mt7996.h"
++
++#undef TRACE_SYSTEM
++#define TRACE_SYSTEM mt7996
++
++#define MAXNAME		32
++#define DEV_ENTRY	__array(char, wiphy_name, 32)
++#define DEV_ASSIGN(_w)	strlcpy(__entry->wiphy_name, wiphy_name(_w), MAXNAME)
++#define DEV_PR_FMT	"%s"
++#define DEV_PR_ARG	__entry->wiphy_name
++
++DECLARE_EVENT_CLASS(mt7996_mcu_debug,
++	TP_PROTO(struct mt7996_dev *dev, bool uni, u8 id, u8 ext_id,
++		 const void *data, size_t len),
++	TP_ARGS(dev, uni, id, ext_id, data, len),
++	TP_STRUCT__entry(
++		__field(bool, uni)
++		__field(u8, id)
++		__field(u8, ext_id)
++		__field(size_t, len)
++		__dynamic_array(u8, data, len)
++	),
++	TP_fast_assign(
++		__entry->uni = uni;
++		__entry->id = id;
++		__entry->ext_id = ext_id;
++		__entry->len = len;
++		memcpy(__get_dynamic_array(data), data, len);
++	),
++	TP_printk(
++		"uni: %d, id: %u, ext_id: %u, len: %zu",
++		__entry->uni,
++		__entry->id,
++		__entry->ext_id,
++		__entry->len
++	)
++);
++
++DEFINE_EVENT(mt7996_mcu_debug, mt7996_mcu_cmd,
++	TP_PROTO(struct mt7996_dev *dev, bool uni, u8 id, u8 ext_id,
++		 const void *data, size_t len),
++	TP_ARGS(dev, uni, id, ext_id, data, len)
++);
++
++DEFINE_EVENT(mt7996_mcu_debug, mt7996_mcu_event,
++	TP_PROTO(struct mt7996_dev *dev, bool uni, u8 id, u8 ext_id,
++		 const void *data, size_t len),
++	TP_ARGS(dev, uni, id, ext_id, data, len)
++);
++
++TRACE_EVENT(mt7996_tx_prepare,
++	TP_PROTO(struct mt7996_dev *dev, struct mt76_wcid *wcid, enum mt76_txq_id qid,
++		 const void *txwi, const void *data, size_t len),
++	TP_ARGS(dev, wcid, qid, txwi, data, len),
++
++	TP_STRUCT__entry(
++		DEV_ENTRY
++		__field(u16, wcid)
++		__field(u8, qid)
++		__array(u8, txwi, MT_TXD_SIZE)
++		__field(size_t, len)
++		__dynamic_array(u8, data, len)
++	),
++
++	TP_fast_assign(
++		DEV_ASSIGN(dev->mt76.phys[wcid->phy_idx]->hw->wiphy);
++		__entry->wcid = wcid->idx;
++		__entry->qid = qid;
++		memcpy(__entry->txwi, txwi, MT_TXD_SIZE);
++		__entry->len = len;
++		memcpy(__get_dynamic_array(data), data, len);
++	),
++
++	TP_printk(
++		DEV_PR_FMT " wcid: %u, qid: %u, len: %zu",
++		DEV_PR_ARG, __entry->wcid, __entry->qid, __entry->len
++	)
++);
++
++TRACE_EVENT(mt7996_fill_rx,
++	TP_PROTO(struct mt7996_phy *phy, const void *data, size_t len),
++	TP_ARGS(phy, data, len),
++
++	TP_STRUCT__entry(
++		DEV_ENTRY
++		__field(size_t, len)
++		__dynamic_array(u8, data, len)
++	),
++
++	TP_fast_assign(
++		DEV_ASSIGN(phy->mt76->hw->wiphy);
++		__entry->len = len;
++		memcpy(__get_dynamic_array(data), data, len);
++	),
++
++	TP_printk(
++		DEV_PR_FMT " len: %zu",
++		DEV_PR_ARG, __entry->len
++	)
++);
++
++TRACE_EVENT(mt7996_fill_rx_done,
++	TP_PROTO(struct mt7996_phy *phy, u16 seqno, u16 hdr_gap),
++	TP_ARGS(phy, seqno, hdr_gap),
++
++	TP_STRUCT__entry(
++		DEV_ENTRY
++		__field(u16, seqno)
++		__field(u16, hdr_gap)
++	),
++
++	TP_fast_assign(
++		DEV_ASSIGN(phy->mt76->hw->wiphy);
++		__entry->seqno = seqno;
++		__entry->hdr_gap = hdr_gap;
++	),
++
++	TP_printk(
++		DEV_PR_FMT " seqno: %u, hdr_gap: %u",
++		DEV_PR_ARG, __entry->seqno, __entry->hdr_gap
++	)
++);
++
++#endif
++
++#undef TRACE_INCLUDE_PATH
++#define TRACE_INCLUDE_PATH ./mt7996
++#undef TRACE_INCLUDE_FILE
++#define TRACE_INCLUDE_FILE mt7996_trace
++
++#include <trace/define_trace.h>
+diff --git a/mt7996/trace.c b/mt7996/trace.c
+new file mode 100644
+index 00000000..ba36f79a
+--- /dev/null
++++ b/mt7996/trace.c
+@@ -0,0 +1,12 @@
++/* SPDX-License-Identifier: ISC */
++/*
++ * Copyright (C) 2022 MediaTek Inc.
++ */
++
++#include <linux/module.h>
++
++#ifndef __CHECKER__
++#define CREATE_TRACE_POINTS
++#include "mt7996_trace.h"
++
++#endif
+diff --git a/trace.h b/trace.h
+index c3d0ef8e..57c94c6b 100644
+--- a/trace.h
++++ b/trace.h
+@@ -101,6 +101,64 @@ DEFINE_EVENT(dev_txid_evt, mac_txdone,
+ 	TP_ARGS(dev, wcid, pktid)
+ );
+ 
++TRACE_EVENT(mt76_rx_complete,
++	TP_PROTO(struct mt76_dev *dev, struct mt76_rx_status *status, bool sub),
++	TP_ARGS(dev, status, sub),
++
++	TP_STRUCT__entry(
++		DEV_ENTRY
++		__field(u16, seqno)
++		__field(u16, wcid)
++		__field(u8, first_amsdu)
++		__field(bool, sub)
++	),
++
++	TP_fast_assign(
++		strlcpy(__entry->wiphy_name,
++			wiphy_name(mt76_dev_phy(dev, status->phy_idx)->hw->wiphy),
++			MAXNAME);
++		__entry->seqno = status->seqno;
++		__entry->wcid = status->wcid ? status->wcid->idx : 0;
++		__entry->first_amsdu = status->first_amsdu;
++		__entry->sub = sub;
++	),
++
++	TP_printk(
++		DEV_PR_FMT " seqno: %u, wcid: %u, first_amsdu: %x, sub: %d",
++		DEV_PR_ARG, __entry->seqno, __entry->wcid, __entry->first_amsdu,
++		__entry->sub
++	)
++);
++
++TRACE_EVENT(mt76_rx_aggr_reorder,
++	TP_PROTO(struct mt76_dev *dev, struct mt76_wcid *wcid, u16 _head, u16 seqno, bool sn_less),
++	TP_ARGS(dev, wcid, _head, seqno, sn_less),
++
++	TP_STRUCT__entry(
++		DEV_ENTRY
++		__field(u16, wcid)
++		__field(u16, _head)
++		__field(u16, seqno)
++		__field(bool, sn_less)
++	),
++
++	TP_fast_assign(
++		strlcpy(__entry->wiphy_name,
++			wiphy_name(dev->phys[wcid->phy_idx]->hw->wiphy),
++			MAXNAME);
++		__entry->wcid = wcid->idx;
++		__entry->_head = _head;
++		__entry->seqno = seqno;
++		__entry->sn_less = sn_less;
++	),
++
++	TP_printk(
++		DEV_PR_FMT " wcid: %u, head: %u, seqno: %u, sn_less: %d",
++		DEV_PR_ARG, __entry->wcid, __entry->_head, __entry->seqno,
++		__entry->sn_less
++	)
++);
++
+ #endif
+ 
+ #undef TRACE_INCLUDE_PATH
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0122-mtk-mt76-temp-changes-for-SQC-period.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0122-mtk-mt76-temp-changes-for-SQC-period.patch
new file mode 100644
index 0000000..335413f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0122-mtk-mt76-temp-changes-for-SQC-period.patch
@@ -0,0 +1,225 @@
+From 59bc821a3e6440f117f8e864e468a1c297be482a Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Fri, 9 Jun 2023 14:11:30 +0800
+Subject: [PATCH 122/199] mtk: mt76: temp changes for SQC period
+
+mtk: wifi: mt76: mt7996: For SQC test Disable single sku
+
+During SQC testing, disable single sku to prevent max power from
+being restricted by regdb setting
+
+NOTE: This patch is only for apply during SQC and will be
+removed after SQC.
+
+mtk: wifi: mt76: mt7996: Enable WM/WA UART log during the SQC period
+
+Enable WM/WA UART log by default during the SQC period
+
+mtk: wifi: mt76: mt7996: add SER flow log for debug during the SQC period.
+
+Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
+Signed-off-by: mtk27745 <rex.lu@mediatek.com>
+---
+ mt7996/dma.c         | 39 +++++++++++++++++++++++++++++++++++++++
+ mt7996/mac.c         | 29 +++++++++++++++++++++++++++++
+ mt7996/mcu.c         |  4 ++--
+ mt7996/mtk_debugfs.c |  1 +
+ 4 files changed, 71 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index a2490fa7..bbc3814d 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -800,11 +800,23 @@ void mt7996_dma_reset(struct mt7996_dev *dev, bool force)
+ 	for (i = 0; i < __MT_MCUQ_MAX; i++)
+ 		mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true);
+ 
++	if (!force)
++		dev_info(dev->mt76.dev,"%s L1 SER tx queue clean up done.",
++			 wiphy_name(dev->mt76.hw->wiphy));
++
+ 	mt76_for_each_q_rx(&dev->mt76, i)
+ 		mt76_queue_rx_cleanup(dev, &dev->mt76.q_rx[i]);
+ 
++	if (!force)
++		dev_info(dev->mt76.dev,"%s L1 SER rx queue clean up done.",
++			 wiphy_name(dev->mt76.hw->wiphy));
++
+ 	mt76_tx_status_check(&dev->mt76, true);
+ 
++	if (!force)
++		dev_info(dev->mt76.dev,"%s L1 SER mt76_tx_status_check done.",
++			 wiphy_name(dev->mt76.hw->wiphy));
++
+ 	/* reset wfsys */
+ 	if (force)
+ 		mt7996_wfsys_reset(dev);
+@@ -815,7 +827,15 @@ void mt7996_dma_reset(struct mt7996_dev *dev, bool force)
+ 	if (mtk_wed_device_active(&dev->mt76.mmio.wed))
+ 		mtk_wed_device_dma_reset(&dev->mt76.mmio.wed);
+ 
++	if (!force)
++		dev_info(dev->mt76.dev,"%s L1 SER wed dma reset done.",
++			 wiphy_name(dev->mt76.hw->wiphy));
++
+ 	mt7996_dma_disable(dev, force);
++
++	if (!force)
++		dev_info(dev->mt76.dev,"%s L1 SER dma disable done.",
++			 wiphy_name(dev->mt76.hw->wiphy));
+ 	mt76_wed_dma_reset(&dev->mt76);
+ 
+ 	/* reset hw queues */
+@@ -827,9 +847,16 @@ void mt7996_dma_reset(struct mt7996_dev *dev, bool force)
+ 			mt76_dma_reset_tx_queue(&dev->mt76, phy3->q_tx[i]);
+ 	}
+ 
++	if (!force)
++		dev_info(dev->mt76.dev,"%s L1 SER dma tx queue reset done.",
++			 wiphy_name(dev->mt76.hw->wiphy));
++
+ 	for (i = 0; i < __MT_MCUQ_MAX; i++)
+ 		mt76_queue_reset(dev, dev->mt76.q_mcu[i], true);
+ 
++	if (!force)
++		dev_info(dev->mt76.dev,"%s L1 SER mcu queue reset done.",
++			 wiphy_name(dev->mt76.hw->wiphy));
+ 	mt76_for_each_q_rx(&dev->mt76, i) {
+ 		if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
+ 			if (mt76_queue_is_wed_rro(&dev->mt76.q_rx[i]) ||
+@@ -843,6 +870,10 @@ void mt7996_dma_reset(struct mt7996_dev *dev, bool force)
+ 		mt76_queue_reset(dev, &dev->mt76.q_rx[i], true);
+ 	}
+ 
++	if (!force)
++		dev_info(dev->mt76.dev,"%s L1 SER rx queue reset done.",
++			 wiphy_name(dev->mt76.hw->wiphy));
++
+ 	mt76_tx_status_check(&dev->mt76, true);
+ 
+ 	mt76_for_each_q_rx(&dev->mt76, i) {
+@@ -854,7 +885,15 @@ void mt7996_dma_reset(struct mt7996_dev *dev, bool force)
+ 		mt76_queue_rx_reset(dev, i);
+ 	}
+ 
++	if (!force)
++		dev_info(dev->mt76.dev,"%s L1 SER rx queue rx reset done.",
++			 wiphy_name(dev->mt76.hw->wiphy));
++
+ 	mt7996_dma_enable(dev, !force);
++
++	if (!force)
++		dev_info(dev->mt76.dev,"%s L1 SER dma enable done.",
++			 wiphy_name(dev->mt76.hw->wiphy));
+ }
+ 
+ void mt7996_dma_cleanup(struct mt7996_dev *dev)
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 6ba07156..fc83cea1 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1980,6 +1980,9 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 	if (phy3)
+ 		ieee80211_stop_queues(phy3->mt76->hw);
+ 
++	dev_info(dev->mt76.dev,"%s L1 SER queue stop done.",
++		 wiphy_name(dev->mt76.hw->wiphy));
++
+ 	set_bit(MT76_RESET, &dev->mphy.state);
+ 	set_bit(MT76_MCU_RESET, &dev->mphy.state);
+ 	if (phy2)
+@@ -1989,6 +1992,10 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 	wake_up(&dev->mt76.mcu.wait);
+ 
+ 	mt76_worker_disable(&dev->mt76.tx_worker);
++
++	dev_info(dev->mt76.dev,"%s L1 SER disable tx_work done.",
++		 wiphy_name(dev->mt76.hw->wiphy));
++
+ 	mt76_for_each_q_rx(&dev->mt76, i) {
+ 		if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
+ 		    mt76_queue_is_wed_rro(&dev->mt76.q_rx[i]))
+@@ -1998,14 +2005,30 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 	}
+ 	napi_disable(&dev->mt76.tx_napi);
+ 
++	dev_info(dev->mt76.dev,"%s L1 SER napi disable done.",
++		 wiphy_name(dev->mt76.hw->wiphy));
++
+ 	mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED);
+ 
++	dev_info(dev->mt76.dev,"%s L1 SER dma stop done.",
++		 wiphy_name(dev->mt76.hw->wiphy));
++
+ 	if (mt7996_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) {
+ 		mt7996_dma_reset(dev, false);
+ 
++		dev_info(dev->mt76.dev,"%s L1 SER dma reset done.",
++			wiphy_name(dev->mt76.hw->wiphy));
++
+ 		mt7996_tx_token_put(dev);
++
++		dev_info(dev->mt76.dev,"%s L1 SER token put done.",
++			wiphy_name(dev->mt76.hw->wiphy));
++
+ 		idr_init(&dev->mt76.token);
+ 
++		dev_info(dev->mt76.dev,"%s L1 SER idr init done.",
++			wiphy_name(dev->mt76.hw->wiphy));
++
+ 		mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_INIT);
+ 		mt7996_wait_reset_state(dev, MT_MCU_CMD_RECOVERY_DONE);
+ 	}
+@@ -2016,6 +2039,9 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 	/* enable DMA Tx/Tx and interrupt */
+ 	mt7996_dma_start(dev, false, false);
+ 
++	dev_info(dev->mt76.dev,"%s L1 SER dma start done.",
++		 wiphy_name(dev->mt76.hw->wiphy));
++
+ 	if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
+ 		u32 wed_irq_mask = MT_INT_RRO_RX_DONE | MT_INT_TX_DONE_BAND2 |
+ 				   dev->mt76.mmio.irqmask;
+@@ -2042,6 +2068,9 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 				     MT_INT_TX_RX_DONE_EXT);
+ 	}
+ 
++	dev_info(dev->mt76.dev,"%s L1 SER wed start done.",
++		 wiphy_name(dev->mt76.hw->wiphy));
++
+ 	clear_bit(MT76_MCU_RESET, &dev->mphy.state);
+ 	clear_bit(MT76_RESET, &dev->mphy.state);
+ 	if (phy2)
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 1cc08a30..a88beb36 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3850,11 +3850,11 @@ int mt7996_mcu_init_firmware(struct mt7996_dev *dev)
+ 		return ret;
+ 
+ 	set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
+-	ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM, 0);
++	ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM, 1);
+ 	if (ret)
+ 		return ret;
+ 
+-	ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WA, 0);
++	ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WA, 1);
+ 	if (ret)
+ 		return ret;
+ 
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index c7f5b56e..0820e0d0 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -4298,6 +4298,7 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ 	debugfs_create_file("red", 0200, dir, dev, &fops_red_config);
+ 	debugfs_create_file("vow_drr_dbg", 0200, dir, dev, &fops_vow_drr_dbg);
+ 
++	dev->dbg.sku_disable = true; /* For SQC */
+ 	debugfs_create_u8("sku_disable", 0600, dir, &dev->dbg.sku_disable);
+ 	debugfs_create_file("scs_enable", 0200, dir, phy, &fops_scs_enable);
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0123-mtk-mt76-mt7996-remain-multiple-wiphy-model-for-test.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0123-mtk-mt76-mt7996-remain-multiple-wiphy-model-for-test.patch
new file mode 100644
index 0000000..7c5f0e4
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0123-mtk-mt76-mt7996-remain-multiple-wiphy-model-for-test.patch
@@ -0,0 +1,111 @@
+From f4f602099ba8cfdf81172f1d3789d810439cc174 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 22 Apr 2024 16:49:48 +0800
+Subject: [PATCH 123/199] mtk: mt76: mt7996: remain multiple wiphy model for
+ testmode
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/init.c   | 13 ++++++++-----
+ mt7996/main.c   | 14 ++++++++++----
+ mt7996/mt7996.h |  9 ++++-----
+ 3 files changed, 22 insertions(+), 14 deletions(-)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 057d20db..9da12f07 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -795,9 +795,11 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 		mtk_wed_device_start(&dev->mt76.mmio.wed_hif2, MT_INT_TX_RX_DONE_EXT);
+ 	}
+ 
+-	/* TODO: FIXME: force to use single wiphy, need to rework init flow */
+-	phy->mt76->ori_hw = mphy->hw;
+-	mphy->hw = dev->phy.mt76->hw;
++	/* TODO: FIXME: force to use single wiphy for normal mode, need to rework init flow */
++	if (!dev->testmode_enable) {
++		phy->mt76->ori_hw = mphy->hw;
++		mphy->hw = dev->phy.mt76->hw;
++	}
+ 
+ 	return 0;
+ 
+@@ -833,8 +835,9 @@ mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
+ 	if (!phy)
+ 		return;
+ 
+-	/* TODO: FIXME: temp for single wiphy support */
+-	phy->mt76->hw = phy->mt76->ori_hw;
++	/* TODO: FIXME: temp for normal mode single wiphy support */
++	if (!phy->dev->testmode_enable)
++		phy->mt76->hw = phy->mt76->ori_hw;
+ 
+ #ifdef CONFIG_MTK_VENDOR
+ 	mt7996_unregister_csi(phy);
+diff --git a/mt7996/main.c b/mt7996/main.c
+index fa9486e4..f52bb2f9 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -140,8 +140,8 @@ static int mt7996_start(struct ieee80211_hw *hw)
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	int ret;
+ 
+-	/* only allow settings from hw0 */
+-	if (hw != dev->phy.mt76->hw)
++	/* only allow settings from hw0 for normal mode */
++	if (!dev->testmode_enable && hw != dev->phy.mt76->hw)
+ 		return -1;
+ 
+ 	flush_work(&dev->init_work);
+@@ -158,8 +158,8 @@ static void mt7996_stop(struct ieee80211_hw *hw)
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	int band;
+ 
+-	/* only allow settings from hw0 */
+-	if (hw != dev->phy.mt76->hw)
++	/* only allow settings from hw0 for normal mode */
++	if (!dev->testmode_enable && hw != dev->phy.mt76->hw)
+ 		return;
+ 
+ 	cancel_delayed_work_sync(&dev->scs_work);
+@@ -455,6 +455,12 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ 	    is_zero_ether_addr(vif->addr))
+ 		phy->monitor_vif = vif;
+ 
++	if (dev->testmode_enable && vif->type != NL80211_IFTYPE_MONITOR) {
++		mutex_unlock(&dev->mt76.mutex);
++		dev_err(dev->mt76.dev, "Only monitor interface is allowed in testmode\n");
++		return -EINVAL;
++	}
++
+ 	INIT_DELAYED_WORK(&mvif->beacon_mon_work, mt7996_beacon_mon_work);
+ 	mvif->dev = dev;
+ 	mvif->hw = hw;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index ef544957..827c24bf 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -876,16 +876,15 @@ mt7996_get_background_radar_cap(struct mt7996_dev *dev)
+ static inline struct mt7996_phy *
+ mt7996_band_phy(struct ieee80211_hw *hw, enum nl80211_band band)
+ {
+-	struct mt76_dev *dev = hw->priv;
+-	struct mt76_phy *phy;
++	struct mt76_phy *phy = hw->priv;
+ 
+ 	/* TODO: mlo: temporarily hardcode */
+ 	if (band == NL80211_BAND_6GHZ)
+-		phy = dev->phys[MT_BAND2];
++		phy = phy->dev->phys[MT_BAND2];
+ 	else if (band == NL80211_BAND_5GHZ)
+-		phy = dev->phys[MT_BAND1];
++		phy = phy->dev->phys[MT_BAND1];
+ 	else
+-		phy = dev->phys[MT_BAND0];
++		phy = phy->dev->phys[MT_BAND0];
+ 
+ 	if (!phy)
+ 		return NULL;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0124-mtk-mt76-mt7996-enable-ibf-capability-for-mt7992.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0124-mtk-mt76-mt7996-enable-ibf-capability-for-mt7992.patch
new file mode 100644
index 0000000..14ec534
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0124-mtk-mt76-mt7996-enable-ibf-capability-for-mt7992.patch
@@ -0,0 +1,42 @@
+From a0cf73079cf52808d6b4196d433d62aebd751c16 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Tue, 23 Apr 2024 09:19:25 +0800
+Subject: [PATCH 124/199] mtk: mt76: mt7996: enable ibf capability for mt7992
+
+For the specific sku of mt7992, it supports both ibf and ebf
+functionality. The firmware algorithm may decide which type is better
+according to the station's beamform capability.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt7996/mcu.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index a88beb36..28123fb7 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2265,6 +2265,8 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 			struct ieee80211_bss_conf *conf, struct mt7996_bss_conf *mconf,
+ 			struct ieee80211_link_sta *link_sta)
+ {
++#define EBF_MODE	BIT(0)
++#define IBF_MODE	BIT(1)
+ 	struct mt7996_phy *phy = mconf->phy;
+ 	int tx_ant = hweight16(phy->mt76->chainmask) - 1;
+ 	struct sta_rec_bf *bf;
+@@ -2302,7 +2304,10 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 	else
+ 		return;
+ 
+-	bf->bf_cap = ebf ? ebf : dev->ibf << 1;
++	bf->bf_cap = ebf ? EBF_MODE : (dev->ibf ? IBF_MODE : 0);
++	if (is_mt7992(&dev->mt76) &&
++	    tx_ant == hweight8(phy->mt76->hw->wiphy->available_antennas_tx))
++		bf->bf_cap |= IBF_MODE;
+ 	bf->bw = link_sta->bandwidth;
+ 	bf->ibf_dbw = link_sta->bandwidth;
+ 	bf->ibf_nrow = tx_ant;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0125-mtk-mt76-remove-the-limitation-for-legacy-AP-sacn.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0125-mtk-mt76-remove-the-limitation-for-legacy-AP-sacn.patch
new file mode 100644
index 0000000..cc53fd1
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0125-mtk-mt76-remove-the-limitation-for-legacy-AP-sacn.patch
@@ -0,0 +1,38 @@
+From 6ae8ff7998348080caa661a1e31c36febd3443ee Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 2 May 2024 15:40:21 +0800
+Subject: [PATCH 125/199] mtk: mt76: remove the limitation for legacy AP sacn
+
+The limitation was used to prevent resource conflict in multiple wiphys
+architecture. It becomes single wiphy and the limitation seems to be
+useless.
+
+Furthermore, legacy APs need to scan due to features like ACS, so remove
+the limitations
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ mt7996/mac.c | 7 -------
+ 1 file changed, 7 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index fc83cea1..bba16975 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2968,13 +2968,6 @@ void mt7996_scan_work(struct work_struct *work)
+ 	bool has_sta = false, active_scan = false;
+ 
+ 	mutex_lock(&phy->dev->mt76.mutex);
+-	/* don't let non-MLD AP scan other bands */
+-	if (vif->type != NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
+-	    phy != mt7996_hw_phy(hw)) {
+-		mt7996_scan_complete(phy, false);
+-		mutex_unlock(&phy->dev->mt76.mutex);
+-		return;
+-	}
+ 
+ 	if (phy->scan_chan_idx >= req->n_channels) {
+ 		mt7996_scan_complete(phy, false);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0126-mtk-mt76-add-support-for-get_survey-in-single-wiphy-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0126-mtk-mt76-add-support-for-get_survey-in-single-wiphy-.patch
new file mode 100644
index 0000000..34d5ad6
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0126-mtk-mt76-add-support-for-get_survey-in-single-wiphy-.patch
@@ -0,0 +1,72 @@
+From 749af4a20ff5d2fb111346a1251fc2aab471986c Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 2 May 2024 15:22:22 +0800
+Subject: [PATCH 126/199] mtk: mt76: add support for get_survey in single wiphy
+ architecture
+
+---
+ mac80211.c | 45 +++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 35 insertions(+), 10 deletions(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index dc91e827..589c37af 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -912,19 +912,44 @@ int mt76_get_survey(struct ieee80211_hw *hw, int idx,
+ 	if (idx == 0 && dev->drv->update_survey)
+ 		mt76_update_survey(phy);
+ 
+-	if (idx >= phy->sband_2g.sband.n_channels +
+-		   phy->sband_5g.sband.n_channels) {
+-		idx -= (phy->sband_2g.sband.n_channels +
+-			phy->sband_5g.sband.n_channels);
+-		sband = &phy->sband_6g;
+-	} else if (idx >= phy->sband_2g.sband.n_channels) {
+-		idx -= phy->sband_2g.sband.n_channels;
+-		sband = &phy->sband_5g;
++	if (phy->hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO) {
++		struct mt76_phy *phy0, *phy1, *phy2;
++
++		phy0 = dev->phys[MT_BAND0];
++		phy1 = dev->phys[MT_BAND1];
++		phy2 = dev->phys[MT_BAND2];
++
++		/* TODO: mlo: temporarily hardcode */
++		/* FIXME only works on Eagle & Kite */
++		if (idx >= phy0->sband_2g.sband.n_channels +
++		    phy1->sband_5g.sband.n_channels) {
++			idx -= (phy0->sband_2g.sband.n_channels +
++				phy1->sband_5g.sband.n_channels);
++			sband = phy2 ? &phy2->sband_6g : NULL;
++			phy = phy2;
++		} else if (idx >= phy0->sband_2g.sband.n_channels) {
++			idx -= phy0->sband_2g.sband.n_channels;
++			sband = &phy1->sband_5g;
++			phy = phy1;
++		} else {
++			sband = &phy0->sband_2g;
++			phy = phy0;
++		}
+ 	} else {
+-		sband = &phy->sband_2g;
++		if (idx >= phy->sband_2g.sband.n_channels +
++		    phy->sband_5g.sband.n_channels) {
++			idx -= (phy->sband_2g.sband.n_channels +
++				phy->sband_5g.sband.n_channels);
++			sband = &phy->sband_6g;
++		} else if (idx >= phy->sband_2g.sband.n_channels) {
++			idx -= phy->sband_2g.sband.n_channels;
++			sband = &phy->sband_5g;
++		} else {
++			sband = &phy->sband_2g;
++		}
+ 	}
+ 
+-	if (idx >= sband->sband.n_channels) {
++	if (!sband || idx >= sband->sband.n_channels) {
+ 		ret = -ENOENT;
+ 		goto out;
+ 	}
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0127-mtk-mt76-mt7996-add-critical-update-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0127-mtk-mt76-mt7996-add-critical-update-support.patch
new file mode 100644
index 0000000..66e1603
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0127-mtk-mt76-mt7996-add-critical-update-support.patch
@@ -0,0 +1,395 @@
+From 8a5fc9a07ebf08ffdd31d05beaa2d399b67f6a10 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 23 Apr 2024 15:22:24 +0800
+Subject: [PATCH 127/199] mtk: mt76: mt7996: add critical update support
+
+Add critical update support
+modification: wmm configuration
+inclusion: channel switch
+(affiliated link's per-STA profile CSA/eCSA countdown is included)
+
+Fix the CU flag countdown issue of the non-CU link as reported from cert.
+1. Avoid setting the CSA beacon twice during channel switch.
+2. Raise the bypass_seq_bitmap up for the non-CU link.
+
+Reset the beacon when switching channels during CAC
+Otherwise, the FW will not be able to perform the CSA countdown.
+Also, modify the bpcc check since channel switch will add the bpcc twice
+(before CSA and after CSA).
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt76_connac_mcu.h |   2 +
+ mt7996/main.c     | 107 +++++++++++++++++++++++++++++++++-------------
+ mt7996/mcu.c      |  99 ++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h      |  26 ++++++++++-
+ mt7996/mt7996.h   |   4 ++
+ 5 files changed, 208 insertions(+), 30 deletions(-)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 870417f3..57cae8ae 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1380,6 +1380,8 @@ enum {
+ 	UNI_BSS_INFO_OFFLOAD = 25,
+ 	UNI_BSS_INFO_MLD = 26,
+ 	UNI_BSS_INFO_PM_DISABLE = 27,
++	UNI_BSS_INFO_BCN_CRIT_UPDATE = 32,
++	UNI_BSS_INFO_BCN_STA_PROF_CSA = 37,
+ };
+ 
+ enum {
+diff --git a/mt7996/main.c b/mt7996/main.c
+index f52bb2f9..0d25d763 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -370,6 +370,7 @@ static int mt7996_add_bss_conf(struct mt7996_phy *phy,
+ 	mconf->mt76.band_idx = band_idx;
+ 	mconf->mt76.wmm_idx = vif->type == NL80211_IFTYPE_AP ? 0 : 3;
+ 	mconf->link_id = link_id;
++	mconf->bpcc = 0;
+ 
+ 	ret = mt7996_mcu_add_dev_info(phy, conf, mconf, true);
+ 	if (ret)
+@@ -979,7 +980,9 @@ static void mt7996_link_info_changed(struct ieee80211_hw *hw,
+ 		mconf->mt76.beacon_rates_idx =
+ 			mt7996_get_rates_table(hw, info, mconf, true, false);
+ 
+-		mt7996_mcu_add_beacon(hw, info, mconf, info->enable_beacon);
++		/* The CSA beacon will be set in channel_switch_beacon */
++		if (!info->csa_active)
++			mt7996_mcu_add_beacon(hw, info, mconf, info->enable_beacon);
+ 	}
+ 
+ 	if (changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP |
+@@ -1030,21 +1033,45 @@ mt7996_channel_switch_beacon(struct ieee80211_hw *hw,
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_phy *phy = mt7996_band_phy(hw, chandef->chan->band);
+-	unsigned long valid_links = vif->valid_links ?: BIT(0);
++	struct ieee80211_bss_conf *conf;
++	struct mt7996_bss_conf *mconf;
++	u16 valid_links = vif->valid_links ?: BIT(0);
+ 	unsigned int link_id;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+-	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
+-		struct mt7996_bss_conf *mconf =
+-			mconf_dereference_protected(mvif, link_id);
+-		struct ieee80211_bss_conf *conf =
+-			link_conf_dereference_protected(vif, link_id);
++	link_id = mvif->band_to_link[phy->mt76->band_idx];
+ 
+-		if (!mconf || phy != mconf->phy)
+-			continue;
++	if (!mvif->cs_ready_links)
++		mvif->cs_link_id = link_id;
++
++	mvif->cs_ready_links |= BIT(link_id);
++	if (mvif->cs_ready_links != valid_links)
++		goto out;
++
++	link_id = mvif->cs_link_id;
++	do {
++		valid_links &= ~BIT(link_id);
++		mconf = mconf_dereference_protected(mvif, link_id);
++		conf = link_conf_dereference_protected(vif, link_id);
++		if (!conf || !mconf)
++			goto fail;
++
++		/* Reset the beacon when switching channels during CAC */
++		if (link_id == mvif->cs_link_id &&
++		    !cfg80211_reg_can_beacon(hw->wiphy, &phy->mt76->chandef, vif->type))
++			mt7996_mcu_add_beacon(hw, conf, mconf, false);
+ 
+ 		mt7996_mcu_add_beacon(hw, conf, mconf, true);
+-	}
++		link_id = ffs(valid_links) - 1;
++	} while (valid_links);
++
++out:
++	mutex_unlock(&dev->mt76.mutex);
++	return;
++fail:
++	mvif->cs_ready_links = 0;
++	mvif->cs_link_id = IEEE80211_LINK_UNSPECIFIED;
++	dev_err(dev->mt76.dev, "link %d: failed to switch beacon\n", link_id);
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
+@@ -2534,32 +2561,54 @@ mt7996_switch_vif_chanctx(struct ieee80211_hw *hw,
+ 			  int n_vifs,
+ 			  enum ieee80211_chanctx_switch_mode mode)
+ {
+-	struct mt7996_chanctx *old_ctx = mt7996_chanctx_get(vifs->old_ctx);
+-	struct mt7996_chanctx *new_ctx = mt7996_chanctx_get(vifs->new_ctx);
+-	struct mt7996_phy *phy = old_ctx->phy;
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mt7996_chanctx *old_ctx;
++	struct mt7996_chanctx *new_ctx;
++	struct mt7996_phy *phy;
++	int i, ret = 0;
+ 
+-	wiphy_info(hw->wiphy, "%s: old=%d, new=%d\n", __func__, vifs->old_ctx->def.chan->hw_value, vifs->new_ctx->def.chan->hw_value);
++	for (i = 0; i < n_vifs; i++) {
++		if (vifs[i].old_ctx == vifs[i].new_ctx)
++			continue;
+ 
+-	if (new_ctx->nbss_assigned && phy->chanctx == new_ctx) {
+-		new_ctx->nbss_assigned += n_vifs;
+-		return 0;
+-	}
++		wiphy_info(hw->wiphy, "%s: old=%d, new=%d\n",
++			   __func__, vifs[i].old_ctx->def.chan->hw_value,
++			   vifs[i].new_ctx->def.chan->hw_value);
+ 
+-	if (WARN_ON(old_ctx != phy->chanctx))
+-		return -EINVAL;
++		mutex_lock(&dev->mt76.mutex);
+ 
+-	mutex_lock(&phy->dev->mt76.mutex);
++		old_ctx = mt7996_chanctx_get(vifs[i].old_ctx);
++		new_ctx = mt7996_chanctx_get(vifs[i].new_ctx);
++		phy = old_ctx->phy;
+ 
+-	phy->chanctx = new_ctx;
+-	phy->mt76->radar_enabled = vifs->new_ctx->radar_enabled;
+-	new_ctx->assigned = true;
+-	new_ctx->chandef = vifs->new_ctx->def;
+-	new_ctx->phy = phy;
+-	new_ctx->nbss_assigned += n_vifs;
++		if (new_ctx->nbss_assigned && phy->chanctx == new_ctx) {
++			new_ctx->nbss_assigned++;
++			mutex_unlock(&dev->mt76.mutex);
++			continue;
++		}
+ 
+-	mutex_unlock(&phy->dev->mt76.mutex);
++		if (WARN_ON(old_ctx != phy->chanctx)) {
++			ret = -EINVAL;
++			mutex_unlock(&dev->mt76.mutex);
++			goto out;
++		}
++
++		phy->chanctx = new_ctx;
++		phy->mt76->radar_enabled = vifs[i].new_ctx->radar_enabled;
++		new_ctx->assigned = true;
++		new_ctx->chandef = vifs[i].new_ctx->def;
++		new_ctx->phy = phy;
++		new_ctx->nbss_assigned++;
++
++		mutex_unlock(&dev->mt76.mutex);
++
++		ret = mt7996_set_channel(phy, &new_ctx->chandef);
++		if (ret)
++			goto out;
++	}
+ 
+-	return mt7996_set_channel(phy, &new_ctx->chandef);
++out:
++	return ret;
+ }
+ 
+ static int
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 28123fb7..ea69b66f 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -370,6 +370,7 @@ mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ 	struct mt76_phy *mphy = (struct mt76_phy *)priv;
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct ieee80211_bss_conf *link_conf;
++	unsigned long valid_links = vif->valid_links ?: BIT(0);
+ 	int link_id, band_idx = mphy->band_idx;
+ 
+ 	link_id = mvif->band_to_link[band_idx];
+@@ -378,7 +379,16 @@ mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ 	if (!link_conf || !link_conf->csa_active || vif->type == NL80211_IFTYPE_STATION)
+ 		return;
+ 
++	pr_info("%s: link_id=%d\n", __func__, link_id);
++	mvif->cs_ready_links = 0;
++	mvif->cs_link_id = IEEE80211_LINK_UNSPECIFIED;
+ 	ieee80211_csa_finish(vif, link_id);
++	/* remove CSA for affiliated links */
++	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++		if (link_id == link_conf->link_id)
++			continue;
++		ieee80211_csa_finish(vif, link_id);
++	}
+ }
+ 
+ static void
+@@ -3235,6 +3245,93 @@ mt7996_mcu_beacon_mbss(struct sk_buff *rskb, struct sk_buff *skb,
+ 	}
+ }
+ 
++static bool
++mt7996_mcu_beacon_is_cu_link(struct sk_buff *skb, struct mt7996_bss_conf *mconf,
++			     u16 tail_offset)
++{
++	const struct element *elem;
++	u8 *beacon_tail = skb->data + tail_offset;
++	bool has_ml_ie = false;
++	int bpcc;
++
++	for_each_element_extid(elem, WLAN_EID_EXT_EHT_MULTI_LINK,
++			       beacon_tail, skb->len - tail_offset)
++		if (ieee80211_mle_type_ok(elem->data + 1,
++					  IEEE80211_ML_CONTROL_TYPE_BASIC,
++					  elem->datalen - 1)) {
++			has_ml_ie = true;
++			break;
++		}
++
++	if (!has_ml_ie)
++		return false;
++
++	bpcc = ieee80211_mle_get_bss_param_ch_cnt(elem->data + 1);
++	if (bpcc < 0 || bpcc == mconf->bpcc)
++		return false;
++
++	mconf->bpcc = bpcc;
++
++	return true;
++}
++
++static void
++mt7996_mcu_beacon_crit_update(struct sk_buff *rskb, struct sk_buff *skb,
++			      struct ieee80211_bss_conf *conf,
++			      struct mt7996_bss_conf *mconf,
++			      struct ieee80211_mutable_offsets *offs)
++{
++	struct ieee80211_mgmt *mgmt = (void *)skb->data;
++	struct bss_bcn_crit_update_tlv *crit;
++	struct tlv *tlv;
++
++	if (!ieee80211_vif_is_mld(conf->vif) ||
++	    !(mgmt->u.beacon.capab_info & WLAN_CAPABILITY_PBCC))
++		return;
++
++	tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_CRIT_UPDATE, sizeof(*crit));
++
++	/* TODO: Support 11vMBSS */
++	crit = (struct bss_bcn_crit_update_tlv *)tlv;
++	crit->bss_bitmap = cpu_to_le32(BIT(0));
++	/* The beacon of the CU link should be set in sequence
++	 * to ensure it appears in the air before the beacon of
++	 * the non-CU link.
++	 */
++	if (!mt7996_mcu_beacon_is_cu_link(skb, mconf, offs->tim_offset))
++		crit->bypass_seq_bitmap = cpu_to_le32(BIT(0));
++	else
++		crit->bypass_seq_bitmap = cpu_to_le32(0);
++	crit->tim_ie_pos[0] = cpu_to_le16(offs->tim_offset);
++	crit->cap_info_ie_pos[0] = cpu_to_le16(offsetof(struct ieee80211_mgmt,
++							u.beacon.capab_info));
++}
++
++static void
++mt7996_mcu_beacon_sta_prof_csa(struct sk_buff *rskb,
++			       struct ieee80211_bss_conf *conf,
++			       struct ieee80211_mutable_offsets *offs)
++{
++	struct ieee80211_vif *vif = conf->vif;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_bss_conf *cs_mconf;
++	struct bss_bcn_sta_prof_cntdwn_tlv *sta_prof;
++	struct tlv *tlv;
++
++	if (!ieee80211_vif_is_mld(vif) || !offs->sta_prof_cntdwn_offs[0])
++		return;
++
++	cs_mconf = mconf_dereference_protected(mvif, mvif->cs_link_id);
++	if (!cs_mconf)
++		return;
++
++	tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_STA_PROF_CSA, sizeof(*sta_prof));
++
++	sta_prof = (struct bss_bcn_sta_prof_cntdwn_tlv *)tlv;
++	sta_prof->sta_prof_csa_offs = cpu_to_le16(offs->sta_prof_cntdwn_offs[0] - 4);
++	sta_prof->cs_bss_idx = cs_mconf->mt76.idx;
++}
++
+ static void
+ mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ 		       struct sk_buff *rskb, struct sk_buff *skb,
+@@ -3315,6 +3412,8 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ 	mt7996_mcu_beacon_cont(dev, conf, rskb, skb, bcn, &offs);
+ 	mt7996_mcu_beacon_mbss(rskb, skb, conf, bcn, &offs);
+ 	mt7996_mcu_beacon_cntdwn(conf, rskb, skb, &offs);
++	mt7996_mcu_beacon_sta_prof_csa(rskb, conf, &offs);
++	mt7996_mcu_beacon_crit_update(rskb, skb, conf, mconf, &offs);
+ out:
+ 	dev_kfree_skb(skb);
+ 	return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb,
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 5e217719..c39dcc3c 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -503,6 +503,27 @@ struct bss_bcn_mbss_tlv {
+ 	__le16 offset[MAX_BEACON_NUM];
+ } __packed __aligned(4);
+ 
++struct bss_bcn_crit_update_tlv {
++	__le16 tag;
++	__le16 len;
++	__le32 bss_bitmap;
++	/* Bypass the beacon sequence handling in firmware for the
++	 * BSSes in the bitmap. If the flag is set for a BSS, then the
++	 * firmware will not set the beacon of the BSS in sequence.
++	 */
++	__le32 bypass_seq_bitmap;
++	__le16 tim_ie_pos[32];
++	__le16 cap_info_ie_pos[32];
++} __packed;
++
++struct bss_bcn_sta_prof_cntdwn_tlv {
++	__le16 tag;
++	__le16 len;
++	__le16 sta_prof_csa_offs;
++	u8 cs_bss_idx;
++	u8 pkt_content[9];
++} __packed;
++
+ struct bss_txcmd_tlv {
+ 	__le16 tag;
+ 	__le16 len;
+@@ -954,7 +975,10 @@ enum {
+ 					 sizeof(struct bss_bcn_content_tlv) +	\
+ 					 4 + MT_TXD_SIZE +			\
+ 					 sizeof(struct bss_bcn_cntdwn_tlv) +	\
+-					 sizeof(struct bss_bcn_mbss_tlv))
++					 sizeof(struct bss_bcn_mbss_tlv) +	\
++					 sizeof(struct bss_bcn_crit_update_tlv) +	\
++					 sizeof(struct bss_bcn_sta_prof_cntdwn_tlv))	\
++
+ #define MT7996_MAX_BSS_OFFLOAD_SIZE	(MT7996_MAX_BEACON_SIZE +		\
+ 					 MT7996_BEACON_UPDATE_SIZE)
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 827c24bf..78226f7b 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -351,6 +351,7 @@ struct mt7996_bss_conf {
+ 
+ 	u8 link_id;
+ 	u8 own_mld_id;
++	u8 bpcc;
+ };
+ 
+ struct mt7996_vif {
+@@ -365,6 +366,9 @@ struct mt7996_vif {
+ 	u8 group_mld_id;
+ 	u8 mld_remap_id;
+ 
++	u8 cs_link_id;
++	u16 cs_ready_links;
++
+ 	u8 band_to_link[__MT_MAX_BAND];
+ 
+ 	/* for beacon monitoring */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0128-mtk-mt76-mt7996-Add-support-for-EMLSR.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0128-mtk-mt76-mt7996-Add-support-for-EMLSR.patch
new file mode 100644
index 0000000..8c0318f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0128-mtk-mt76-mt7996-Add-support-for-EMLSR.patch
@@ -0,0 +1,299 @@
+From 060df153443363c797ddfe40e3190c931bc4879a Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+Date: Tue, 7 May 2024 15:47:23 +0800
+Subject: [PATCH 128/199] mtk: mt76: mt7996: Add support for EMLSR
+
+1. Register the EMLSR capability
+2. Set the EML capability of the station to firmware
+3. Process the EML Operating Mode Notification frame
+
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ mt76_connac_mcu.h |  8 ++++++
+ mt7996/init.c     |  1 +
+ mt7996/mcu.c      | 60 +++++++++++++++++++++++++++++++++++++++++++-
+ mt7996/mt7996.h   | 19 ++++++++++++++
+ mt7996/vendor.c   | 64 +++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/vendor.h   | 15 +++++++++++
+ 6 files changed, 166 insertions(+), 1 deletion(-)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 57cae8ae..d72337d4 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -624,6 +624,13 @@ struct sta_rec_tx_proc {
+ 	__le32 flag;
+ } __packed;
+ 
++struct sta_rec_eml_op {
++	__le16 tag;
++	__le16 len;
++	u8 bitmap;
++	u8 link_ant_num[3];
++} __packed;
++
+ /* wtbl_rec */
+ 
+ struct wtbl_req_hdr {
+@@ -828,6 +835,7 @@ enum {
+ 	STA_REC_PN_INFO = 0x26,
+ 	STA_REC_KEY_V3 = 0x27,
+ 	STA_REC_HDRT = 0x28,
++	STA_REC_EML_OP = 0x29,
+ 	STA_REC_HDR_TRANS = 0x2B,
+ 	STA_REC_TX_CAP = 0x2f,
+ 	STA_REC_MAX_NUM
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 9da12f07..2e624def 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -64,6 +64,7 @@ static const struct wiphy_iftype_ext_capab mt7996_iftypes_ext_capa[] = {
+ 		.extended_capabilities = mt7996_if_types_ext_capa,
+ 		.extended_capabilities_mask = mt7996_if_types_ext_capa,
+ 		.extended_capabilities_len = sizeof(mt7996_if_types_ext_capa),
++		.eml_capabilities = IEEE80211_EML_CAP_EMLSR_SUPP,
+ 		.mld_capa_and_ops = 2,
+ 		/* the max number of simultaneous links is defined as the
+ 		 * maximum number of affiliated APs minus 1.
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index ea69b66f..2a2e6116 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2919,9 +2919,10 @@ mt7996_mcu_sta_eht_mld_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 
+ 	for (i = 0; i < ARRAY_SIZE(eht_mld->str_cap); i++)
+ 		eht_mld->str_cap[i] = 0x7;
++
++	eht_mld->eml_cap = cpu_to_le16(sta->eml_capa);
+ 	/* TODO:
+ 	eht_mld->nsep = ;
+-	eht_mld->eml_cap = cpu_to_le16()
+ 	*/
+ }
+ 
+@@ -6326,6 +6327,63 @@ int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy)
+ 	                         &req, sizeof(req), true);
+ }
+ 
++int mt7996_mcu_set_eml_omn(struct ieee80211_hw *hw,
++			   struct ieee80211_vif *vif,
++			   u8 link_id,
++			   struct ieee80211_sta *sta,
++			   struct mt7996_eml_omn *eml_omn)
++{
++#define EML_OMN_CONTROL_EMLSR_MODE	0x01
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_link_sta *mlink;
++	struct mt7996_bss_conf *mconf, *mconf_link;
++	struct sta_rec_eml_op *eml_op;
++	struct sk_buff *skb;
++	struct tlv *tlv;
++
++	mlink = mlink_dereference_protected(msta, link_id);
++	mconf = mconf_dereference_protected(mvif, link_id);
++
++	if (!mlink || !mconf)
++			return -EINVAL;
++
++	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76,
++					      &mconf->mt76,
++					      &mlink->wcid,
++					      MT7996_STA_UPDATE_MAX_SIZE);
++
++	if (IS_ERR(skb))
++		return PTR_ERR(skb);
++
++	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EML_OP, sizeof(*eml_op));
++	eml_op = (struct sta_rec_eml_op *) tlv;
++	eml_op->bitmap = 0;
++
++	if (eml_omn->control & EML_OMN_CONTROL_EMLSR_MODE) {
++		unsigned long bitmap = (unsigned long) le16_to_cpu(eml_omn->bitmap);
++		unsigned int linkid;
++
++		for_each_set_bit(linkid, &bitmap, IEEE80211_MLD_MAX_NUM_LINKS) {
++			mconf_link = mconf_dereference_protected(mvif, linkid);
++
++			if (!mconf_link)
++				continue;
++
++			eml_op->bitmap |= BIT(mconf_link->phy->mt76->band_idx);
++		}
++	}
++
++	mlo_dbg(mconf->phy, "link:%u, wcid:%d, control:%x, mode:%d, bmp:%x\n",
++		mlink->wcid.link_id, mlink->wcid.idx, eml_omn->control,
++		!!(eml_omn->control & EML_OMN_CONTROL_EMLSR_MODE),
++		eml_op->bitmap);
++
++	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
++			MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
++}
++
+ #ifdef CONFIG_MTK_VENDOR
+ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 78226f7b..c60262ec 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -410,6 +410,23 @@ enum {
+ 	SCS_ENABLE,
+ };
+ 
++struct mt7996_eml_omn {
++	u8 dialog_token;
++	u8 control;
++	__le16 bitmap;
++	union {
++		struct {
++			u8 emlsr_para_update;
++		} emlsr_info;
++		struct {
++			u8 mcs_map_count_control;
++			u8 mcs_map_bw80[3];
++			u8 mcs_map_bw160[3];
++			u8 mcs_map_bw320[3];
++		} emlmr_info;
++	} u;
++} __packed;
++
+ struct mt7996_wed_rro_addr {
+ 	u32 head_low;
+ 	u32 head_high : 4;
+@@ -1226,6 +1243,8 @@ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ 				     struct mt7996_link_sta *mlink);
+ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode);
+ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap);
++int mt7996_mcu_set_eml_omn(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 link_id,
++			   struct ieee80211_sta *sta, struct mt7996_eml_omn *eml_omn);
+ #ifdef CONFIG_MAC80211_DEBUGFS
+ void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 			    struct ieee80211_sta *sta, struct dentry *dir);
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index 84b50ab2..585c4e28 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -119,6 +119,13 @@ beacon_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BEACON_CTRL] = {
+ 	[MTK_VENDOR_ATTR_BEACON_CTRL_MODE] = { .type = NLA_U8 },
+ };
+ 
++static const struct nla_policy
++eml_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EML_CTRL] = {
++	[MTK_VENDOR_ATTR_EML_LINK_ID] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EML_STA_ADDR] = { .type = NLA_BINARY },
++	[MTK_VENDOR_ATTR_EML_CTRL_STRUCT] = { .type = NLA_BINARY },
++};
++
+ static const struct nla_policy
+ csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
+ 	[MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX] = { .type = NLA_U8 },
+@@ -1013,6 +1020,52 @@ static int mt7996_vendor_beacon_ctrl(struct wiphy *wiphy,
+ 
+ 	return 0;
+ }
++
++static int mt7996_vendor_eml_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
++				  const void *data, int data_len)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct ieee80211_vif *vif = wdev_to_ieee80211_vif(wdev);
++	struct ieee80211_sta *sta;
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_EML_CTRL];
++	struct mt7996_eml_omn *eml_omn;
++	u8 sta_addr[ETH_ALEN], link_id;
++	int err;
++
++	if (!ieee80211_vif_is_mld(vif))
++		return -EINVAL;
++
++	err = nla_parse(tb, MTK_VENDOR_ATTR_EML_CTRL_MAX, data, data_len,
++			eml_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	if (!tb[MTK_VENDOR_ATTR_EML_LINK_ID] || !tb[MTK_VENDOR_ATTR_EML_STA_ADDR])
++		return -EINVAL;
++
++	link_id = nla_get_u8(tb[MTK_VENDOR_ATTR_EML_LINK_ID]);
++
++	if (link_id >= IEEE80211_LINK_UNSPECIFIED)
++		return -EINVAL;
++
++	nla_memcpy(sta_addr, tb[MTK_VENDOR_ATTR_EML_STA_ADDR], ETH_ALEN);
++	sta = ieee80211_find_sta_by_ifaddr(hw, sta_addr, NULL);
++
++	if (!sta)
++		return -EINVAL;
++
++	if (tb[MTK_VENDOR_ATTR_EML_CTRL_STRUCT]) {
++		eml_omn = kzalloc(sizeof(struct mt7996_eml_omn), GFP_KERNEL);
++		nla_memcpy(eml_omn, tb[MTK_VENDOR_ATTR_EML_CTRL_STRUCT],
++			   sizeof(struct mt7996_eml_omn));
++
++		err = mt7996_mcu_set_eml_omn(hw, vif, link_id, sta, eml_omn);
++		kfree(eml_omn);
++	}
++
++	return err;
++}
++
+ static int mt7996_vendor_csi_ctrl(struct wiphy *wiphy,
+ 				  struct wireless_dev *wdev,
+ 				  const void *data,
+@@ -1373,6 +1426,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 		.policy = csi_ctrl_policy,
+ 		.maxattr = MTK_VENDOR_ATTR_CSI_CTRL_MAX,
+ 	},
++	{
++		.info = {
++			.vendor_id = MTK_NL80211_VENDOR_ID,
++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_EML_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++			 WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.doit = mt7996_vendor_eml_ctrl,
++		.policy = eml_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_EML_CTRL_MAX,
++	},
+ };
+ 
+ void mt7996_vendor_register(struct mt7996_phy *phy)
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index 834b3d08..f6fcb623 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -18,6 +18,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
+ 	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
+ 	MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
++	MTK_NL80211_VENDOR_SUBCMD_EML_CTRL = 0xd3,
+ };
+ 
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -241,6 +242,20 @@ enum mtk_vendor_attr_beacon_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_eml_ctrl {
++
++	MTK_VENDOR_ATTR_EML_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_EML_LINK_ID,
++	MTK_VENDOR_ATTR_EML_STA_ADDR,
++	MTK_VENDOR_ATTR_EML_CTRL_STRUCT,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_EML_CTRL,
++	MTK_VENDOR_ATTR_EML_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_EML_CTRL - 1
++};
++
+ enum mtk_vendor_attr_csi_ctrl {
+ 	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0129-mtk-mt76-mt7996-add-max-mpdu-len-capability.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0129-mtk-mt76-mt7996-add-max-mpdu-len-capability.patch
new file mode 100644
index 0000000..36cbb69
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0129-mtk-mt76-mt7996-add-max-mpdu-len-capability.patch
@@ -0,0 +1,31 @@
+From 77894f28fb40126d8ffa927787e4258cac597191 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Thu, 9 May 2024 11:16:10 +0800
+Subject: [PATCH 129/199] mtk: mt76: mt7996: add max mpdu len capability
+
+Set max mpdu len to 11454 according to hardware capability.
+Without this patch, the max ampdu length would be 3895 and hurt performance.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/init.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 2e624def..6e20a174 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -1519,7 +1519,9 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
+ 
+ 	eht_cap_elem->mac_cap_info[0] =
+ 		IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS |
+-		IEEE80211_EHT_MAC_CAP0_OM_CONTROL;
++		IEEE80211_EHT_MAC_CAP0_OM_CONTROL |
++		u8_encode_bits(IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_11454,
++			       IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK);
+ 
+ 	eht_cap_elem->phy_cap_info[0] =
+ 		IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI |
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0130-mtk-mt76-mt7996-add-correct-bss_conf-for-legacy-AP-s.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0130-mtk-mt76-mt7996-add-correct-bss_conf-for-legacy-AP-s.patch
new file mode 100644
index 0000000..14ff6d2
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0130-mtk-mt76-mt7996-add-correct-bss_conf-for-legacy-AP-s.patch
@@ -0,0 +1,63 @@
+From ee730e834f37a0fa3a70bdce358ef3a163b46399 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 10 May 2024 13:35:56 +0800
+Subject: [PATCH 130/199] mtk: mt76: mt7996: add correct bss_conf for legacy AP
+ scan
+
+If the legacy AP interface is added but not yet started, its bss_conf
+wlould be phy0. Problem happens when scan is triggered with scan request
+contining channels not belong to phy0. In other word, it is invalid to
+set channel that does not belong to current bss_conf.
+
+This commit adds correct bss_conf for legacy AP so that setting channel
+can be successful.
+
+Now AP's bss_conf is not added until chanctx being assigned, so legacy AP
+might receive scan request without bss_conf being assigned. (such as ACS)
+
+This commit changes the way bss_conf being checked before scan. If the
+bss_conf exists but belongs to a different phy than the scanning one, it's
+removed. And if the bss_conf is not exist, or it just be removed, it's
+added and assigned to the scanning phy.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ mt7996/main.c | 21 +++++++++++++--------
+ 1 file changed, 13 insertions(+), 8 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 0d25d763..990a38a7 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -2363,15 +2363,20 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	phy->scan_vif = vif;
+ 	phy->scan_chan_idx = 0;
+ 
+-	if (vif->type == NL80211_IFTYPE_STATION && !ieee80211_vif_is_mld(vif) &&
+-	    (phy->mt76 != mvif->deflink.phy->mt76)) {
+-		// phy->mt76->main_phy = hw->priv;
+-		mt7996_remove_bss_conf(vif, &vif->bss_conf, &mvif->deflink);
++	if (!ieee80211_vif_is_mld(vif)) {
++		struct mt7996_bss_conf *mconf = mconf_dereference_protected(mvif, 0);
+ 
+-		ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
+-		if (ret) {
+-			mutex_unlock(&phy->dev->mt76.mutex);
+-			return ret;
++		if (mconf && mconf->phy != phy) {
++			mt7996_remove_bss_conf(vif, &vif->bss_conf, &mvif->deflink);
++			mconf = NULL;
++		}
++
++		if (!mconf) {
++			ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
++			if (ret) {
++				mutex_unlock(&phy->dev->mt76.mutex);
++				return ret;
++			}
+ 		}
+ 	}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0131-mtk-mt76-mt7996-fix-set-beacon-mcu-command.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0131-mtk-mt76-mt7996-fix-set-beacon-mcu-command.patch
new file mode 100644
index 0000000..b35ad4b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0131-mtk-mt76-mt7996-fix-set-beacon-mcu-command.patch
@@ -0,0 +1,78 @@
+From d3c4bfceae589098cb6cba5a3bb9125144df29fb Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 14 May 2024 10:52:43 +0800
+Subject: [PATCH 131/199] mtk: mt76: mt7996: fix set beacon mcu command
+
+When stopping AP, mac80211 frees beacon template before it calls
+driver's stop_ap operation. In other words, on the path of stopping
+AP, ieee80211_beacon_get_template() must returns NULL in
+mt7996_mcu_add_beacon(). In such case mt7996 immediately returns
+-EINVAL without telling FW to disable the beacon.
+
+This commit refactors mt7996_mcu_add_beacon() so that FW can be
+correctly informed when disabling AP interface.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ mt7996/mcu.c | 26 +++++++++++++++-----------
+ 1 file changed, 15 insertions(+), 11 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 2a2e6116..0a0df745 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3377,7 +3377,7 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ 	struct sk_buff *skb, *rskb;
+ 	struct tlv *tlv;
+ 	struct bss_bcn_content_tlv *bcn;
+-	int len;
++	int len, extra_len = 0;
+ 
+ 	if (conf->nontransmitted)
+ 		return 0;
+@@ -3388,28 +3388,32 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ 		return PTR_ERR(rskb);
+ 
+ 	skb = ieee80211_beacon_get_template(hw, conf->vif, &offs, conf->link_id);
+-	if (!skb) {
++	if (en && !skb) {
+ 		dev_kfree_skb(rskb);
+ 		return -EINVAL;
+ 	}
+ 
+-	if (skb->len > MT7996_MAX_BEACON_SIZE) {
+-		dev_err(dev->mt76.dev, "Bcn size limit exceed\n");
+-		dev_kfree_skb(rskb);
+-		dev_kfree_skb(skb);
+-		return -EINVAL;
+-	}
++	if (skb) {
++		if (skb->len > MT7996_MAX_BEACON_SIZE) {
++			dev_err(dev->mt76.dev, "Bcn size limit exceed\n");
++			dev_kfree_skb(rskb);
++			dev_kfree_skb(skb);
++			return -EINVAL;
++		}
+ 
+-	info = IEEE80211_SKB_CB(skb);
+-	info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->mt76->band_idx);
++		extra_len = skb->len;
++	}
+ 
+-	len = ALIGN(sizeof(*bcn) + MT_TXD_SIZE + skb->len, 4);
++	len = ALIGN(sizeof(*bcn) + MT_TXD_SIZE + extra_len, 4);
+ 	tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_CONTENT, len);
+ 	bcn = (struct bss_bcn_content_tlv *)tlv;
+ 	bcn->enable = en;
+ 	if (!en)
+ 		goto out;
+ 
++	info = IEEE80211_SKB_CB(skb);
++	info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->mt76->band_idx);
++
+ 	mt7996_mcu_beacon_cont(dev, conf, rskb, skb, bcn, &offs);
+ 	mt7996_mcu_beacon_mbss(rskb, skb, conf, bcn, &offs);
+ 	mt7996_mcu_beacon_cntdwn(conf, rskb, skb, &offs);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0132-mtk-mt76-fix-incorrect-setting-of-antenna-capability.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0132-mtk-mt76-fix-incorrect-setting-of-antenna-capability.patch
new file mode 100644
index 0000000..344d28f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0132-mtk-mt76-fix-incorrect-setting-of-antenna-capability.patch
@@ -0,0 +1,33 @@
+From c7dfcaacdd6ae91fa75d2dc9c9efb7db4254c4a6 Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Tue, 14 May 2024 14:12:28 +0800
+Subject: [PATCH 132/199] mtk: mt76: fix incorrect setting of antenna
+ capability
+
+Due to current implementation of single-wiphy architecture, only one antenna capability can be set for all PHYs via mt7996_set_antenna().
+Therefore, if antenna capabilities of PHYs are different, some may be set wrong.
+Thus, temporarily make mt7996_set_antenna() dummy.
+
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ mt7996/main.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 990a38a7..0a87cfad 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1670,6 +1670,10 @@ mt7996_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	int band, max_nss = hweight8(hw->wiphy->available_antennas_tx);
+ 
++	/* TODO: set antenna based on capability of each band. */
++	dev_warn(dev->mt76.dev, "%s: temporarily not supported.\n", __func__);
++	return 0;
++
+ 	/* only allow settings from hw0 */
+ 	if (hw != dev->phy.mt76->hw)
+ 		return 0;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0133-mtk-mt76-mt7996-fix-stop_tx_ba_session-warning.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0133-mtk-mt76-mt7996-fix-stop_tx_ba_session-warning.patch
new file mode 100644
index 0000000..f58b151
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0133-mtk-mt76-mt7996-fix-stop_tx_ba_session-warning.patch
@@ -0,0 +1,81 @@
+From 8bb932b534480f0025fdbd57270b194ed7738e2d Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 15 May 2024 11:10:17 +0800
+Subject: [PATCH 133/199] mtk: mt76: mt7996: fix stop_tx_ba_session warning
+
+---
+ mt7996/main.c | 11 +++++++----
+ mt7996/mcu.c  |  6 ++++++
+ 2 files changed, 13 insertions(+), 4 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 0a87cfad..c7ec01f2 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1079,7 +1079,8 @@ static void mt7996_remove_link_sta(struct mt7996_dev *dev,
+ 				   struct ieee80211_bss_conf *conf,
+ 				   struct mt7996_bss_conf *mconf,
+ 				   struct ieee80211_link_sta *link_sta,
+-				   struct mt7996_link_sta *mlink)
++				   struct mt7996_link_sta *mlink,
++				   bool last_link)
+ {
+ 	struct ieee80211_sta *sta = link_sta->sta;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+@@ -1091,7 +1092,7 @@ static void mt7996_remove_link_sta(struct mt7996_dev *dev,
+ 	for (i = 0; i < ARRAY_SIZE(mlink->wcid.aggr); i++)
+ 			mt76_rx_aggr_stop(&dev->mt76, &mlink->wcid, i);
+ 
+-	if (sta->mlo)
++	if (sta->mlo && last_link)
+ 		mt7996_mcu_teardown_mld_sta(dev, mconf, mlink);
+ 	else
+ 		mt7996_mcu_add_sta(dev, conf, mconf, link_sta, mlink, false, false);
+@@ -1205,7 +1206,8 @@ static int mt7996_add_link_sta(struct mt7996_dev *dev,
+ 
+ 	return 0;
+ error:
+-	mt7996_remove_link_sta(dev, conf, mconf, link_sta, mlink);
++	mt7996_remove_link_sta(dev, conf, mconf, link_sta, mlink,
++			       hweight16(sta->valid_links) <= 1);
+ 	return ret;
+ }
+ 
+@@ -1230,8 +1232,9 @@ mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 			link_conf_dereference_protected(vif, link_id);
+ 		struct ieee80211_link_sta *link_sta =
+ 			link_sta_dereference_protected(sta, link_id);
++		bool last_link = rem == sta->valid_links && link_id == __fls(rem);
+ 
+-		mt7996_remove_link_sta(dev, conf, mconf, link_sta, mlink);
++		mt7996_remove_link_sta(dev, conf, mconf, link_sta, mlink, last_link);
+ 	}
+ }
+ 
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 0a0df745..48ff11d9 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1735,6 +1735,9 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ 			mconf_dereference_protected(msta->vif, link_id);
+ 		int ret;
+ 
++		if (!mlink || !mconf)
++			continue;
++
+ 		if (enable && !params->amsdu)
+ 			mlink->wcid.amsdu = false;
+ 
+@@ -1769,6 +1772,9 @@ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
+ 			mconf_dereference_protected(msta->vif, link_id);
+ 		int ret;
+ 
++		if (!mlink || !mconf)
++			continue;
++
+ 		ret = mt7996_mcu_sta_ba(dev, &mconf->mt76, params, &mlink->wcid,
+ 					enable, false);
+ 		if (ret)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0134-mtk-mt76-mt7996-do-software-link-addr-translation-fo.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0134-mtk-mt76-mt7996-do-software-link-addr-translation-fo.patch
new file mode 100644
index 0000000..746cedc
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0134-mtk-mt76-mt7996-do-software-link-addr-translation-fo.patch
@@ -0,0 +1,96 @@
+From 17651c073f135c837ca0b8d0b3e9ab8c4beef67f Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 15 May 2024 17:47:33 +0800
+Subject: [PATCH 134/199] mtk: mt76: mt7996: do software link addr translation
+ for EAPOL
+
+Previously, we do HW link address translation for EAPOL addr1 and addr2,
+but SW link address translation for EAPOL addr3 due to incompatibility
+between HW converting rules and 802.11 EAPOL frames.
+
+This patch adds support for doing pure SW link address translation for
+EAPOL, to get rid of ambiguity and could also help on debugging EAPOL
+timeout issues.
+
+Note that dma_sync_single_for_cpu/dma_sync_single_for_device is
+necessary to sync the changes of address to DMA.
+
+Assign EAPOL's SA/DA to MLD address.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mac.c | 46 +++++++++++++++++++++++++++++++---------------
+ 1 file changed, 31 insertions(+), 15 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index bba16975..020203ec 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -831,7 +831,8 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ 	txwi[5] = cpu_to_le32(val);
+ 
+ 	val = MT_TXD6_DAS;
+-	if ((q_idx >= MT_LMAC_ALTX0 && q_idx <= MT_LMAC_BCN0))
++	if ((q_idx >= MT_LMAC_ALTX0 && q_idx <= MT_LMAC_BCN0) ||
++	    unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE)))
+ 		val |= MT_TXD6_DIS_MAT;
+ 
+ 	if (is_mt7996(&dev->mt76))
+@@ -939,23 +940,38 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 		mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, key,
+ 				      pid, qid, 0);
+ 
+-	/* translate addr3 of EAPOL by driver */
++	/* Since the rules of HW MLD address translation are not fully compatible
++	 * with 802.11 EAPOL frame, we do the translation by software
++	 */
+ 	if (unlikely(tx_info->skb->protocol == cpu_to_be16(ETH_P_PAE)) && sta->mlo) {
+-		if (ether_addr_equal(vif->addr, hdr->addr3)) {
+-			struct ieee80211_bss_conf *conf;
+-
+-			conf = rcu_dereference(vif->link_conf[wcid->link_id]);
+-			if (unlikely(!conf))
+-				return -ENOLINK;
+-
+-			memcpy(hdr->addr3, conf->addr, ETH_ALEN);
+-		} else if (ether_addr_equal(sta->addr, hdr->addr3)) {
+-			struct ieee80211_link_sta *link_sta;
+-
+-			link_sta = rcu_dereference(sta->link[wcid->link_id]);
+-			memcpy(hdr->addr3, link_sta->addr, ETH_ALEN);
++		struct ieee80211_bss_conf *conf;
++		struct ieee80211_link_sta *link_sta;
++		__le16 fc = hdr->frame_control;
++
++		conf = rcu_dereference(vif->link_conf[wcid->link_id]);
++		link_sta = rcu_dereference(sta->link[wcid->link_id]);
++		if (!conf || !link_sta)
++			return -ENOLINK;
++
++		dma_sync_single_for_cpu(mdev->dma_dev, tx_info->buf[1].addr,
++					tx_info->buf[1].len, DMA_TO_DEVICE);
++
++		memcpy(hdr->addr1, link_sta->addr, ETH_ALEN);
++		memcpy(hdr->addr2, conf->addr, ETH_ALEN);
++
++		/* EAPOL's SA/DA need to be MLD address in MLO */
++		if (ieee80211_has_a4(fc)) {
++			memcpy(hdr->addr3, sta->addr, ETH_ALEN);
++			memcpy(hdr->addr4, vif->addr, ETH_ALEN);
++		} else if (ieee80211_has_tods(fc)) {
++			memcpy(hdr->addr3, sta->addr, ETH_ALEN);
++		} else if (ieee80211_has_fromds(fc)) {
++			memcpy(hdr->addr3, vif->addr, ETH_ALEN);
+ 		}
+ 
++		dma_sync_single_for_device(mdev->dma_dev, tx_info->buf[1].addr,
++					   tx_info->buf[1].len, DMA_TO_DEVICE);
++
+ 		pr_info("EAPOL: a1=%pM, a2=%pM, a3=%pM\n", hdr->addr1, hdr->addr2, hdr->addr3);
+ 	}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0135-mtk-mt76-mt7996-add-per-band-debugfs-folder.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0135-mtk-mt76-mt7996-add-per-band-debugfs-folder.patch
new file mode 100644
index 0000000..d82f9b3
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0135-mtk-mt76-mt7996-add-per-band-debugfs-folder.patch
@@ -0,0 +1,146 @@
+From 84c119a7ad5969f445513f9c5759f8b041620e1f Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Thu, 16 May 2024 18:03:19 +0800
+Subject: [PATCH 135/199] mtk: mt76: mt7996: add per-band debugfs folder
+
+Add per-band debugfs folder and  move upstream debugfs knob to it.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/debugfs.c | 43 +++++++++++++++++++++++++++++++------------
+ mt7996/init.c    | 14 +++++++++-----
+ mt7996/mt7996.h  |  3 ++-
+ 3 files changed, 42 insertions(+), 18 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 2c553cf3..9d74f659 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -1073,21 +1073,49 @@ mt7996_airtime_read(struct seq_file *s, void *data)
+ 	return 0;
+ }
+ 
+-int mt7996_init_debugfs(struct mt7996_phy *phy)
++int mt7996_init_band_debugfs(struct mt7996_phy *phy)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+ 	struct dentry *dir;
++	char dir_name[10];
+ 
+-	dir = mt76_register_debugfs_fops(phy->mt76, NULL);
++	if (!dev->debugfs_dir)
++		return -EINVAL;
++
++	snprintf(dir_name, sizeof(dir_name), "band%d", phy->mt76->band_idx);
++
++	dir = debugfs_create_dir(dir_name, dev->debugfs_dir);
+ 	if (!dir)
+ 		return -ENOMEM;
++
+ 	debugfs_create_file("hw-queues", 0400, dir, phy,
+ 			    &mt7996_hw_queues_fops);
+ 	debugfs_create_file("xmit-queues", 0400, dir, phy,
+ 			    &mt7996_xmit_queues_fops);
+-	debugfs_create_file("tx_stats", 0400, dir, phy, &mt7996_tx_stats_fops);
+ 	debugfs_create_file("sys_recovery", 0600, dir, phy,
+ 			    &mt7996_sys_recovery_ops);
++	debugfs_create_file("atf_enable", 0600, dir, phy, &fops_atf_enable);
++	debugfs_create_file("tx_stats", 0400, dir, phy, &mt7996_tx_stats_fops);
++	if (phy->mt76->cap.has_5ghz) {
++		debugfs_create_u32("dfs_hw_pattern", 0400, dir,
++				   &dev->hw_pattern);
++		debugfs_create_file("radar_trigger", 0200, dir, dev,
++				    &fops_radar_trigger);
++		debugfs_create_devm_seqfile(dev->mt76.dev, "rdd_monitor", dir,
++					    mt7996_rdd_monitor);
++	}
++
++	return 0;
++}
++
++int mt7996_init_dev_debugfs(struct mt7996_phy *phy)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct dentry *dir;
++
++	dir = mt76_register_debugfs_fops(phy->mt76, NULL);
++	if (!dir)
++		return -ENOMEM;
+ 	debugfs_create_file("fw_debug_wm", 0600, dir, dev, &fops_fw_debug_wm);
+ 	debugfs_create_file("fw_debug_wa", 0600, dir, dev, &fops_fw_debug_wa);
+ 	debugfs_create_file("fw_debug_bin", 0600, dir, dev, &fops_fw_debug_bin);
+@@ -1103,18 +1131,9 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ 	debugfs_create_file("otp", 0400, dir, dev, &mt7996_efuse_ops);
+ 	debugfs_create_devm_seqfile(dev->mt76.dev, "vow_info", dir,
+ 	                            mt7996_vow_info_read);
+-	debugfs_create_file("atf_enable", 0600, dir, phy, &fops_atf_enable);
+ 	debugfs_create_devm_seqfile(dev->mt76.dev, "airtime", dir,
+ 	                            mt7996_airtime_read);
+ 
+-	if (phy->mt76->cap.has_5ghz) {
+-		debugfs_create_u32("dfs_hw_pattern", 0400, dir,
+-				   &dev->hw_pattern);
+-		debugfs_create_file("radar_trigger", 0200, dir, dev,
+-				    &fops_radar_trigger);
+-		debugfs_create_devm_seqfile(dev->mt76.dev, "rdd_monitor", dir,
+-					    mt7996_rdd_monitor);
+-	}
+ 	debugfs_create_file("fw_debug_muru_disable", 0600, dir, dev,
+ 			    &fops_fw_debug_muru_disable);
+ 
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 6e20a174..01795404 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -787,7 +787,7 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 			goto error;
+ 	}
+ 
+-	ret = mt7996_init_debugfs(phy);
++	ret = mt7996_init_band_debugfs(phy);
+ 	if (ret)
+ 		goto error;
+ 
+@@ -1708,6 +1708,14 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ 	if (ret)
+ 		return ret;
+ 
++	ret = mt7996_init_dev_debugfs(&dev->phy);
++	if (ret)
++		goto error;
++
++	ret = mt7996_init_band_debugfs(&dev->phy);
++	if (ret)
++		goto error;
++
+ 	ret = mt7996_register_phy(dev, mt7996_phy2(dev), MT_BAND1);
+ 	if (ret)
+ 		return ret;
+@@ -1732,10 +1740,6 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ 			goto error;
+ 	}
+ 
+-	ret = mt7996_init_debugfs(&dev->phy);
+-	if (ret)
+-		goto error;
+-
+ 	ret = mt7996_coredump_register(dev);
+ 	if (ret)
+ 		goto error;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index c60262ec..08a43e61 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1226,7 +1226,8 @@ int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy);
+ void mt7996_set_stream_he_eht_caps(struct mt7996_phy *phy);
+ void mt7996_set_stream_vht_txbf_caps(struct mt7996_phy *phy);
+ void mt7996_update_channel(struct mt76_phy *mphy);
+-int mt7996_init_debugfs(struct mt7996_phy *phy);
++int mt7996_init_dev_debugfs(struct mt7996_phy *phy);
++int mt7996_init_band_debugfs(struct mt7996_phy *phy);
+ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int len);
+ bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len);
+ int mt7996_mcu_add_key(struct mt76_dev *dev, struct mt7996_bss_conf *mconf,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0136-mtk-mt76-mt7996-move-internal-debugfs-knob-to-per-ba.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0136-mtk-mt76-mt7996-move-internal-debugfs-knob-to-per-ba.patch
new file mode 100644
index 0000000..b8a705e
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0136-mtk-mt76-mt7996-move-internal-debugfs-knob-to-per-ba.patch
@@ -0,0 +1,270 @@
+From 729686d815e03189a23d2f0a987289eb34ac7676 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Thu, 16 May 2024 18:04:28 +0800
+Subject: [PATCH 136/199] mtk: mt76: mt7996: move internal debugfs knob to
+ per-band folder
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/debugfs.c     |   5 +-
+ mt7996/mt7996.h      |   3 +-
+ mt7996/mtk_debugfs.c | 117 ++++++++++++++-----------------------------
+ 3 files changed, 44 insertions(+), 81 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 9d74f659..71dc0449 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -1105,6 +1105,9 @@ int mt7996_init_band_debugfs(struct mt7996_phy *phy)
+ 					    mt7996_rdd_monitor);
+ 	}
+ 
++#ifdef CONFIG_MTK_DEBUG
++	mt7996_mtk_init_band_debugfs(phy, dir);
++#endif
+ 	return 0;
+ }
+ 
+@@ -1146,7 +1149,7 @@ int mt7996_init_dev_debugfs(struct mt7996_phy *phy)
+ 
+ #ifdef CONFIG_MTK_DEBUG
+ 	debugfs_create_u16("wlan_idx", 0600, dir, &dev->wlan_idx);
+-	mt7996_mtk_init_debugfs(phy, dir);
++	mt7996_mtk_init_dev_debugfs(dev, dir);
+ #endif
+ 
+ 	return 0;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 08a43e61..cb35a927 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1286,7 +1286,8 @@ enum edcca_bw_id {
+ };
+ 
+ #ifdef CONFIG_MTK_DEBUG
+-int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
++void mt7996_mtk_init_dev_debugfs(struct mt7996_dev *dev, struct dentry *dir);
++void mt7996_mtk_init_band_debugfs(struct mt7996_phy *phy, struct dentry *dir);
+ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
+ int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set);
+ void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb);
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 0820e0d0..59c6db73 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -14,13 +14,14 @@
+ #ifdef CONFIG_MTK_DEBUG
+ 
+ /* AGG INFO */
+-static int
+-mt7996_agginfo_read_per_band(struct seq_file *s, int band_idx)
++static int mt7996_agginfo_show(struct seq_file *s, void *data)
+ {
+-	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	struct mt7996_phy *phy = s->private;
++	struct mt7996_dev *dev = phy->dev;
+ 	u64 total_burst, total_ampdu, ampdu_cnt[16];
+ 	u32 value, idx, row_idx, col_idx, start_range, agg_rang_sel[16], burst_cnt[16], band_offset = 0;
+ 	u8 partial_str[16] = {}, full_str[64] = {};
++	u8 band_idx = phy->mt76->band_idx;
+ 
+ 	switch (band_idx) {
+ 	case 0:
+@@ -204,24 +205,7 @@ mt7996_agginfo_read_per_band(struct seq_file *s, int band_idx)
+ 
+ 	return 0;
+ }
+-
+-static int mt7996_agginfo_read_band0(struct seq_file *s, void *data)
+-{
+-	mt7996_agginfo_read_per_band(s, MT_BAND0);
+-	return 0;
+-}
+-
+-static int mt7996_agginfo_read_band1(struct seq_file *s, void *data)
+-{
+-	mt7996_agginfo_read_per_band(s, MT_BAND1);
+-	return 0;
+-}
+-
+-static int mt7996_agginfo_read_band2(struct seq_file *s, void *data)
+-{
+-	mt7996_agginfo_read_per_band(s, MT_BAND2);
+-	return 0;
+-}
++DEFINE_SHOW_ATTRIBUTE(mt7996_agginfo);
+ 
+ /* AMSDU INFO */
+ static int mt7996_amsdu_result_read(struct seq_file *s, void *data)
+@@ -704,10 +688,12 @@ static int mt7996_trinfo_read(struct seq_file *s, void *data)
+ }
+ 
+ /* MIB INFO */
+-static int mt7996_mibinfo_read_per_band(struct seq_file *s, int band_idx)
++static int mt7996_mibinfo_show(struct seq_file *s, void *data)
+ {
+ #define BSS_NUM	4
+-	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	struct mt7996_phy *phy = s->private;
++	struct mt7996_dev *dev = phy->dev;
++	u8 band_idx = phy->mt76->band_idx;
+ 	u8 bss_nums = BSS_NUM;
+ 	u32 idx;
+ 	u32 mac_val, band_offset = 0, band_offset_umib = 0;
+@@ -920,24 +906,7 @@ static int mt7996_mibinfo_read_per_band(struct seq_file *s, int band_idx)
+ 
+ 	return 0;
+ }
+-
+-static int mt7996_mibinfo_band0(struct seq_file *s, void *data)
+-{
+-	mt7996_mibinfo_read_per_band(s, MT_BAND0);
+-	return 0;
+-}
+-
+-static int mt7996_mibinfo_band1(struct seq_file *s, void *data)
+-{
+-	mt7996_mibinfo_read_per_band(s, MT_BAND1);
+-	return 0;
+-}
+-
+-static int mt7996_mibinfo_band2(struct seq_file *s, void *data)
+-{
+-	mt7996_mibinfo_read_per_band(s, MT_BAND2);
+-	return 0;
+-}
++DEFINE_SHOW_ATTRIBUTE(mt7996_mibinfo);
+ 
+ /* WTBL INFO */
+ static int
+@@ -3036,9 +3005,9 @@ static const struct file_operations fops_muru_fixed_group_rate = {
+ 
+ static int mt7996_muru_prot_thr_set(void *data, u64 val)
+ {
+-	struct mt7996_phy *phy = data;
++	struct mt7996_dev *dev = data;
+ 
+-	return mt7996_mcu_muru_set_prot_frame_thr(phy->dev, (u32)val);
++	return mt7996_mcu_muru_set_prot_frame_thr(dev, (u32)val);
+ }
+ 
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_muru_prot_thr, NULL,
+@@ -4219,9 +4188,32 @@ mt7996_drr_info(struct seq_file *s, void *data)
+ 	return 0;
+ }
+ 
+-int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
++void mt7996_mtk_init_band_debugfs(struct mt7996_phy *phy, struct dentry *dir)
++{
++	/* agg */
++	debugfs_create_file("agginfo", 0400, dir, phy, &mt7996_agginfo_fops);
++	debugfs_create_file("mibinfo", 0400, dir, phy, &mt7996_mibinfo_fops);
++	debugfs_create_file("txpower_level", 0600, dir, phy, &fops_txpower_level);
++	debugfs_create_file("txpower_info", 0600, dir, phy, &mt7996_txpower_info_fops);
++	debugfs_create_file("txpower_sku", 0600, dir, phy, &mt7996_txpower_sku_fops);
++	debugfs_create_file("txpower_path", 0600, dir, phy, &mt7996_txpower_path_fops);
++
++	debugfs_create_file("sr_enable", 0600, dir, phy, &fops_sr_enable);
++	debugfs_create_file("sr_enhanced_enable", 0600, dir, phy, &fops_sr_enhanced_enable);
++	debugfs_create_file("sr_stats", 0400, dir, phy, &mt7996_sr_stats_fops);
++	debugfs_create_file("sr_scene_cond", 0400, dir, phy, &mt7996_sr_scene_cond_fops);
++
++	debugfs_create_file("bf_txsnd_info", 0600, dir, phy, &fops_bf_txsnd_info);
++	debugfs_create_file("bf_starec_read", 0600, dir, phy, &fops_starec_bf_read);
++	debugfs_create_file("bf_fbk_rpt", 0600, dir, phy, &fops_bf_fbk_rpt);
++	debugfs_create_file("pfmu_tag_read", 0600, dir, phy, &fops_bf_pfmu_tag_read);
++
++	debugfs_create_file("thermal_enable", 0600, dir, phy, &fops_thermal_enable);
++	debugfs_create_file("scs_enable", 0200, dir, phy, &fops_scs_enable);
++}
++
++void mt7996_mtk_init_dev_debugfs(struct mt7996_dev *dev, struct dentry *dir)
+ {
+-	struct mt7996_dev *dev = phy->dev;
+ 	u32 device_id = (dev->mt76.rev) >> 16;
+ 	int i = 0;
+ 	static const struct mt7996_dbg_reg_desc dbg_reg_s[] = {
+@@ -4246,13 +4238,6 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ 		WTBL_LMAC_DW9 = WTBL_LMAC_DW9_7992;
+ 	}
+ 
+-	/* agg */
+-	debugfs_create_devm_seqfile(dev->mt76.dev, "agg_info0", dir,
+-				    mt7996_agginfo_read_band0);
+-	debugfs_create_devm_seqfile(dev->mt76.dev, "agg_info1", dir,
+-				    mt7996_agginfo_read_band1);
+-	debugfs_create_devm_seqfile(dev->mt76.dev, "agg_info2", dir,
+-				    mt7996_agginfo_read_band2);
+ 	/* amsdu */
+ 	debugfs_create_devm_seqfile(dev->mt76.dev, "amsdu_info", dir,
+ 				    mt7996_amsdu_result_read);
+@@ -4270,24 +4255,12 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ 	debugfs_create_devm_seqfile(dev->mt76.dev, "fw_wm_info", dir,
+ 				    mt7996_fw_wm_info_read);
+ 
+-	debugfs_create_devm_seqfile(dev->mt76.dev, "mib_info0", dir,
+-				    mt7996_mibinfo_band0);
+-	debugfs_create_devm_seqfile(dev->mt76.dev, "mib_info1", dir,
+-				    mt7996_mibinfo_band1);
+-	debugfs_create_devm_seqfile(dev->mt76.dev, "mib_info2", dir,
+-				    mt7996_mibinfo_band2);
+-
+ 	debugfs_create_devm_seqfile(dev->mt76.dev, "sta_info", dir,
+ 				    mt7996_sta_info);
+ 
+ 	debugfs_create_devm_seqfile(dev->mt76.dev, "tr_info", dir,
+ 				    mt7996_trinfo_read);
+ 
+-	debugfs_create_file("txpower_level", 0600, dir, phy, &fops_txpower_level);
+-	debugfs_create_file("txpower_info", 0600, dir, phy, &mt7996_txpower_info_fops);
+-	debugfs_create_file("txpower_sku", 0600, dir, phy, &mt7996_txpower_sku_fops);
+-	debugfs_create_file("txpower_path", 0600, dir, phy, &mt7996_txpower_path_fops);
+-
+ 	debugfs_create_devm_seqfile(dev->mt76.dev, "eeprom_mode", dir,
+ 				    mt7996_show_eeprom_mode);
+ 
+@@ -4300,23 +4273,12 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ 
+ 	dev->dbg.sku_disable = true; /* For SQC */
+ 	debugfs_create_u8("sku_disable", 0600, dir, &dev->dbg.sku_disable);
+-	debugfs_create_file("scs_enable", 0200, dir, phy, &fops_scs_enable);
+-
+-	debugfs_create_file("sr_enable", 0600, dir, phy, &fops_sr_enable);
+-	debugfs_create_file("sr_enhanced_enable", 0600, dir, phy, &fops_sr_enhanced_enable);
+-	debugfs_create_file("sr_stats", 0400, dir, phy, &mt7996_sr_stats_fops);
+-	debugfs_create_file("sr_scene_cond", 0400, dir, phy, &mt7996_sr_scene_cond_fops);
+ 
++	debugfs_create_file("muru_prot_thr", 0200, dir, dev, &fops_muru_prot_thr);
+ 	debugfs_create_file("muru_fixed_rate_enable", 0600, dir, dev,
+ 			    &fops_muru_fixed_rate_enable);
+ 	debugfs_create_file("muru_fixed_group_rate", 0600, dir, dev,
+ 			    &fops_muru_fixed_group_rate);
+-	debugfs_create_file("bf_txsnd_info", 0600, dir, phy, &fops_bf_txsnd_info);
+-	debugfs_create_file("bf_starec_read", 0600, dir, phy, &fops_starec_bf_read);
+-	debugfs_create_file("bf_fbk_rpt", 0600, dir, phy, &fops_bf_fbk_rpt);
+-	debugfs_create_file("pfmu_tag_read", 0600, dir, phy, &fops_bf_pfmu_tag_read);
+-
+-	debugfs_create_file("muru_prot_thr", 0200, dir, phy, &fops_muru_prot_thr);
+ 
+ 	if (dev->has_rro) {
+ 		debugfs_create_u32("rro_sid", 0600, dir, &dev->dbg.sid);
+@@ -4326,7 +4288,6 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ 					    mt7996_show_rro_mib);
+ 	}
+ 
+-	debugfs_create_file("thermal_enable", 0600, dir, phy, &fops_thermal_enable);
+ 	debugfs_create_file("thermal_recal", 0200, dir, dev, &fops_thermal_recal);
+ 	debugfs_create_file("reset_counter", 0200, dir, dev, &fops_reset_counter);
+ 
+@@ -4346,8 +4307,6 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ 	debugfs_create_file("amsdu_para", 0600, dir, dev, &fops_amsdu_para);
+ 	debugfs_create_devm_seqfile(dev->mt76.dev, "hw_amsdu_info", dir,
+ 	                            mt7996_hw_amsdu_info_read);
+-
+-	return 0;
+ }
+ 
+ #endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0137-mtk-mt76-mt7996-refactor-amsdu-debugfs.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0137-mtk-mt76-mt7996-refactor-amsdu-debugfs.patch
new file mode 100644
index 0000000..a05b8ca
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0137-mtk-mt76-mt7996-refactor-amsdu-debugfs.patch
@@ -0,0 +1,114 @@
+From 48926fde96602e41c02879d7a020f4b24d332f75 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Fri, 17 May 2024 17:34:03 +0800
+Subject: [PATCH 137/199] mtk: mt76: mt7996: refactor amsdu debugfs
+
+1. Remove hw_amsdu_info which is duplicated with amsdu_info.
+
+2. The amsdu_info cannot read CR directly because the CR is read-clear.
+   If amsdu_info read CR directly, the CR would be cleared and the
+   mt7996_mac_work cannot get correct value.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/mtk_debugfs.c | 63 ++++++++++++++++----------------------------
+ 1 file changed, 22 insertions(+), 41 deletions(-)
+
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 59c6db73..a7cbde3e 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -210,28 +210,35 @@ DEFINE_SHOW_ATTRIBUTE(mt7996_agginfo);
+ /* AMSDU INFO */
+ static int mt7996_amsdu_result_read(struct seq_file *s, void *data)
+ {
+-#define HW_MSDU_CNT_ADDR 0xf400
+-#define HW_MSDU_NUM_MAX 33
+ 	struct mt7996_dev *dev = dev_get_drvdata(s->private);
+-	u32 ple_stat[HW_MSDU_NUM_MAX] = {0}, total_amsdu = 0;
+-	u8 i;
++	struct mt7996_phy *phy = &dev->phy;
++	struct mt76_mib_stats *mib = &phy->mib;
++	static u32 tx_amsdu_last[MT76_MAX_AMSDU_NUM] = {0};
++	static u32 tx_amsdu_cnt_last = 0;
++	u32 tx_amsdu, tx_amsdu_cnt, ratio;
++	int i;
+ 
+-	for (i = 0; i < HW_MSDU_NUM_MAX; i++)
+-		ple_stat[i] = mt76_rr(dev, HW_MSDU_CNT_ADDR + i * 0x04);
++	mutex_lock(&dev->mt76.mutex);
+ 
+-	seq_printf(s, "TXD counter status of MSDU:\n");
++	mt7996_mac_update_stats(phy);
+ 
+-	for (i = 0; i < HW_MSDU_NUM_MAX; i++)
+-		total_amsdu += ple_stat[i];
++	tx_amsdu_cnt = mib->tx_amsdu_cnt - tx_amsdu_cnt_last;
+ 
+-	for (i = 0; i < HW_MSDU_NUM_MAX; i++) {
+-		seq_printf(s, "AMSDU pack count of %d MSDU in TXD: 0x%x ", i, ple_stat[i]);
+-		if (total_amsdu != 0)
+-			seq_printf(s, "(%d%%)\n", ple_stat[i] * 100 / total_amsdu);
+-		else
+-			seq_printf(s, "\n");
++	seq_puts(s, "Tx MSDU statistics:\n");
++	for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) {
++		tx_amsdu = mib->tx_amsdu[i] - tx_amsdu_last[i];
++		ratio = tx_amsdu_cnt ? tx_amsdu * 100 / tx_amsdu_cnt : 0;
++
++		seq_printf(s, "AMSDU pack count of %d MSDU in TXD: %8d (%3d%%)\n",
++			   i + 1, tx_amsdu, ratio);
++
++		tx_amsdu_last[i] = mib->tx_amsdu[i];
+ 	}
+ 
++	tx_amsdu_cnt_last = mib->tx_amsdu_cnt;
++
++	mutex_unlock(&dev->mt76.mutex);
++
+ 	return 0;
+ }
+ 
+@@ -3342,30 +3349,6 @@ static const struct file_operations fops_amsdu_para = {
+ 	.llseek = default_llseek,
+ };
+ 
+-static int mt7996_hw_amsdu_info_read(struct seq_file *s, void *data)
+-{
+-	struct mt7996_dev *dev = dev_get_drvdata(s->private);
+-	u32 amsdu_cnt[WF_PLE_TOP_AMSDU_PACK_NUM] = {0}, total_cnt;
+-	u8 i;
+-
+-	seq_printf(s, "HW A-MSDU Information:\n");
+-
+-	for (total_cnt = 0, i = 0; i < WF_PLE_TOP_AMSDU_PACK_NUM; ++i) {
+-		amsdu_cnt[i] = mt76_rr(dev, WF_PLE_TOP_AMSDU_PACK_1_MSDU_CNT_ADDR + i * 4);
+-		total_cnt += amsdu_cnt[i];
+-	}
+-
+-	for (i = 0; i < WF_PLE_TOP_AMSDU_PACK_NUM; ++i) {
+-		seq_printf(s, "# of HW A-MSDU containing %hhu MSDU: 0x%x",
+-		           i + 1, amsdu_cnt[i]);
+-		seq_printf(s, "\t(%u.%u%%)\n",
+-		           total_cnt ? amsdu_cnt[i] * 1000 / total_cnt / 10 : 0,
+-		           total_cnt ? amsdu_cnt[i] * 1000 / total_cnt % 10 : 0);
+-	}
+-
+-	return 0;
+-}
+-
+ /* PSE INFO */
+ static struct bmac_queue_info_t pse_queue_empty_info[] = {
+ 	{"CPU Q0",  ENUM_UMAC_CPU_PORT_1,     ENUM_UMAC_CTX_Q_0},
+@@ -4305,8 +4288,6 @@ void mt7996_mtk_init_dev_debugfs(struct mt7996_dev *dev, struct dentry *dir)
+ 	/* amsdu */
+ 	debugfs_create_file("amsdu_algo", 0600, dir, dev, &fops_amsdu_algo);
+ 	debugfs_create_file("amsdu_para", 0600, dir, dev, &fops_amsdu_para);
+-	debugfs_create_devm_seqfile(dev->mt76.dev, "hw_amsdu_info", dir,
+-	                            mt7996_hw_amsdu_info_read);
+ }
+ 
+ #endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0138-mtk-mt76-mt7996-trigger-channel-calibration-for-DFS-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0138-mtk-mt76-mt7996-trigger-channel-calibration-for-DFS-.patch
new file mode 100644
index 0000000..6160525
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0138-mtk-mt76-mt7996-trigger-channel-calibration-for-DFS-.patch
@@ -0,0 +1,135 @@
+From 29f282a0ca7e594da0b23db9429c0588408a708e Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 17 May 2024 14:49:50 +0800
+Subject: [PATCH 138/199] mtk: mt76: mt7996: trigger channel calibration for
+ DFS link after sta is associated
+
+Trigger channel calibration (set channel with switch reason = NORMAL)
+for DFS link after STA is associated.
+Without this patch, 5G link might have high PER during T.P. test
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/main.c     | 15 +++++++++++----
+ mt7996/mcu.c      |  7 ++++---
+ mt7996/mt7996.h   |  2 +-
+ mt7996/testmode.c |  4 ++--
+ 4 files changed, 18 insertions(+), 10 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index c7ec01f2..40418f7e 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -82,12 +82,12 @@ int mt7996_run(struct ieee80211_hw *hw)
+ 		if (ret)
+ 			goto out;
+ 
+-		ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_RX_PATH);
++		ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_RX_PATH, false);
+ 		if (ret)
+ 			goto out;
+ 
+ 		/* set a parking channel */
+-		ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
++		ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH, false);
+ 		if (ret)
+ 			goto out;
+ 
+@@ -554,11 +554,11 @@ static int __mt7996_set_channel(struct mt7996_phy *phy,
+ 		goto out;
+ 	}
+ 
+-	ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
++	ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH, false);
+ 	if (ret)
+ 		goto out;
+ 
+-	ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_RX_PATH);
++	ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_RX_PATH, false);
+ 	if (ret)
+ 		goto out;
+ 
+@@ -2732,6 +2732,13 @@ mt7996_event_callback(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 				next_time = min(next_time,
+ 						MT7996_MAX_BEACON_LOSS *
+ 						conf->beacon_int);
++
++				/* trigger calibration for DFS link */
++				if (!cfg80211_reg_can_beacon(hw->wiphy,
++							     &mconf->chanctx->chandef,
++							     NL80211_IFTYPE_AP))
++					mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH,
++								 true);
+ 			}
+ 
+ 			ieee80211_queue_delayed_work(hw, &mvif->beacon_mon_work,
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 48ff11d9..3a950a90 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -4397,7 +4397,7 @@ int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy,
+ 				  0, region);
+ }
+ 
+-int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag)
++int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag, bool sta)
+ {
+ 	static const u8 ch_band[] = {
+ 		[NL80211_BAND_2GHZ] = 0,
+@@ -4408,6 +4408,8 @@ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag)
+ 	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
+ 	int freq1 = chandef->center_freq1;
+ 	u8 band_idx = phy->mt76->band_idx;
++	enum nl80211_iftype iftype = sta ? NL80211_IFTYPE_STATION :
++					   NL80211_IFTYPE_AP;
+ 	struct {
+ 		/* fixed field */
+ 		u8 __rsv[4];
+@@ -4448,8 +4450,7 @@ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag)
+ 		 phy->mt76->hw->conf.flags & IEEE80211_CONF_IDLE ||
+ 		 phy->scan_chan)
+ 		req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD;
+-	else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef,
+-					  NL80211_IFTYPE_AP))
++	else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef, iftype))
+ 		req.switch_reason = CH_SWITCH_DFS;
+ 	else
+ 		req.switch_reason = CH_SWITCH_NORMAL;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index cb35a927..259845a1 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1054,7 +1054,7 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
+ int mt7996_mcu_add_mld_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 			   struct ieee80211_sta *sta, unsigned long add);
+ int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef);
+-int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag);
++int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag, bool sta);
+ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf);
+ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
+ 				   void *data, u16 version);
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index 0565ebc9..8b35d125 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -544,7 +544,7 @@ mt7996_tm_dpd_prek_send_req(struct mt7996_phy *phy, struct mt7996_tm_req *req,
+ 
+ 		/* set channel switch reason */
+ 		mphy->hw->conf.flags |= IEEE80211_CONF_OFFCHANNEL;
+-		mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
++		mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH, false);
+ 
+ 		ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), req,
+ 					sizeof(*req), false);
+@@ -558,7 +558,7 @@ out:
+ 	mphy->hw->conf.flags &= ~IEEE80211_CONF_OFFCHANNEL;
+ 	memcpy(chandef, &chandef_backup, sizeof(struct cfg80211_chan_def));
+ 	memcpy(chandef->chan, &chan_backup, sizeof(struct ieee80211_channel));
+-	mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
++	mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH, false);
+ 
+ 	return ret;
+ }
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0139-mtk-mt76-mt7996-do-not-remove-bss_info-and-starec-wh.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0139-mtk-mt76-mt7996-do-not-remove-bss_info-and-starec-wh.patch
new file mode 100644
index 0000000..30c8b37
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0139-mtk-mt76-mt7996-do-not-remove-bss_info-and-starec-wh.patch
@@ -0,0 +1,55 @@
+From 525b850e3140488b346601070adbeb5fd163af67 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Wed, 22 May 2024 18:22:51 +0800
+Subject: [PATCH 139/199] mtk: mt76: mt7996: do not remove bss_info and starec
+ when assign_vif_chanctx
+
+When STA interface re-connect from rootAP, it would unsign/assign vif
+chanctx for both AP and station interface. If remove/re-allocate
+bss_info and starec for AP interface, the WTBL and GTK of AP would
+be cleared. But the hostapd does not remove interface so it would not
+set key again.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/main.c | 17 +++++++++++++----
+ 1 file changed, 13 insertions(+), 4 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 40418f7e..2a2112f1 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -338,6 +338,9 @@ static int mt7996_add_bss_conf(struct mt7996_phy *phy,
+ 	u8 link_id = conf->link_id;
+ 	int idx, ret;
+ 
++	if (rcu_access_pointer(mvif->link[link_id]))
++		return 0;
++
+ 	if (conf != &vif->bss_conf) {
+ 		mconf = kzalloc(sizeof(*mconf), GFP_KERNEL);
+ 		if (!mconf)
+@@ -2516,10 +2519,16 @@ mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 
+ 	mutex_lock(&phy->dev->mt76.mutex);
+ 
+-	/* remove first */
+-	if (rcu_access_pointer(mvif->link[link_id]))
+-		mt7996_remove_bss_conf(vif, link_conf,
+-				       mconf_dereference_protected(mvif, link_id));
++	mconf = mconf_dereference_protected(mvif, link_id);
++
++	/* Remove bss conf when change non-MLO interface to MLO interface */
++	if (ieee80211_vif_is_mld(vif) && mconf == &mvif->deflink)
++		mt7996_remove_bss_conf(vif, link_conf, mconf);
++	else if (mconf && phy != mconf->phy)
++		dev_err(phy->dev->mt76.dev,
++			"%s: error: change link[%d] from phy%d to phy%d",
++			__func__, link_id, mconf->phy->mt76->band_idx,
++			phy->mt76->band_idx);
+ 
+ 	ret = mt7996_add_bss_conf(phy, vif, link_conf);
+ 	if (ret) {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0140-mtk-mt76-mt7996-remove-chanctx-in-mt7996_bss_conf.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0140-mtk-mt76-mt7996-remove-chanctx-in-mt7996_bss_conf.patch
new file mode 100644
index 0000000..d7ff224
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0140-mtk-mt76-mt7996-remove-chanctx-in-mt7996_bss_conf.patch
@@ -0,0 +1,71 @@
+From c684d5b236f67ffb278ffe9c0836df16a5c0eeb7 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Tue, 28 May 2024 09:33:11 +0800
+Subject: [PATCH 140/199] mtk: mt76: mt7996: remove chanctx in mt7996_bss_conf
+
+Different vif under the same phy cannot use different chanctx.
+So it is better to maintain the chanctx in mt7996_phy.
+Instead of mconf->chanctx, get the chanctx by mconf->phy->chanctx.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/main.c   | 8 +-------
+ mt7996/mt7996.h | 2 --
+ 2 files changed, 1 insertion(+), 9 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 2a2112f1..4f3b0070 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -2536,8 +2536,6 @@ mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 		return ret;
+ 	}
+ 
+-	mconf = mconf_dereference_protected(mvif, link_id);
+-	mconf->chanctx = ctx;
+ 	ctx->nbss_assigned++;
+ 	mvif->band_to_link[phy->mt76->band_idx] = link_id;
+ 
+@@ -2556,8 +2554,6 @@ mt7996_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ {
+ 	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
+ 	struct mt7996_phy *phy = ctx->phy;
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct mt7996_bss_conf *mconf;
+ 
+ 	wiphy_info(hw->wiphy, "Remove VIF (addr: %pM, type: %d, link_id: %d) from channel context: %d MHz\n",
+ 		   vif->addr, vif->type, link_conf->link_id,
+@@ -2569,8 +2565,6 @@ mt7996_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	if (test_bit(MT76_SCANNING, &phy->mt76->state))
+ 		mt7996_scan_complete(phy, true);
+ 
+-	mconf = mconf_dereference_protected(mvif, link_conf->link_id);
+-	mconf->chanctx = NULL;
+ 	ctx->nbss_assigned--;
+ 
+ 	mutex_unlock(&phy->dev->mt76.mutex);
+@@ -2744,7 +2738,7 @@ mt7996_event_callback(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 
+ 				/* trigger calibration for DFS link */
+ 				if (!cfg80211_reg_can_beacon(hw->wiphy,
+-							     &mconf->chanctx->chandef,
++							     &phy->chanctx->chandef,
+ 							     NL80211_IFTYPE_AP))
+ 					mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH,
+ 								 true);
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 259845a1..8f084651 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -347,8 +347,6 @@ struct mt7996_bss_conf {
+ 	struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
+ 	struct cfg80211_bitrate_mask bitrate_mask;
+ 
+-	struct mt7996_chanctx *chanctx;
+-
+ 	u8 link_id;
+ 	u8 own_mld_id;
+ 	u8 bpcc;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0141-mtk-mt76-mt7996-temporarily-disable-EPCS.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0141-mtk-mt76-mt7996-temporarily-disable-EPCS.patch
new file mode 100644
index 0000000..98d1f3a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0141-mtk-mt76-mt7996-temporarily-disable-EPCS.patch
@@ -0,0 +1,28 @@
+From 268b885a43ad602ac8166ba9464d6b2673045ae1 Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Tue, 28 May 2024 15:58:57 +0800
+Subject: [PATCH 141/199] mtk: mt76: mt7996: temporarily disable EPCS
+
+EPCS is not yet ready, so do not claim to support it.
+
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ mt7996/init.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 01795404..7cf3a37e 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -1518,7 +1518,7 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
+ 	eht_cap->has_eht = true;
+ 
+ 	eht_cap_elem->mac_cap_info[0] =
+-		IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS |
++		// IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS |
+ 		IEEE80211_EHT_MAC_CAP0_OM_CONTROL |
+ 		u8_encode_bits(IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_11454,
+ 			       IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0142-mtk-mt76-mt7996-fix-kite-can-t-handle-11v-beacon-on-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0142-mtk-mt76-mt7996-fix-kite-can-t-handle-11v-beacon-on-.patch
new file mode 100644
index 0000000..8aabd07
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0142-mtk-mt76-mt7996-fix-kite-can-t-handle-11v-beacon-on-.patch
@@ -0,0 +1,37 @@
+From ab5d7deb92a8180b4d236da52863bfe9b7327dca Mon Sep 17 00:00:00 2001
+From: Rex Lu <rex.lu@mediatek.com>
+Date: Thu, 30 May 2024 17:39:38 +0800
+Subject: [PATCH 142/199] mtk: mt76: mt7996: fix kite can't handle 11v beacon
+ on sta side
+
+this hw flag SUPPORTS_MULTI_BSSID need to set. otherwise cfg80211_parse_mbssid_data will not handle 11v mbss beacon
+
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+---
+ mt7996/init.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 7cf3a37e..cc696a69 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -48,6 +48,7 @@ static const struct ieee80211_iface_combination if_comb[] = {
+ 
+ static const u8 mt7996_if_types_ext_capa[] = {
+ 	[0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
++	[2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT,
+ 	[7] = WLAN_EXT_CAPA8_OPMODE_NOTIF,
+ };
+ 
+@@ -447,7 +448,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ 	ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD);
+ 	ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD);
+ 	ieee80211_hw_set(hw, WANT_MONITOR_VIF);
+-	// ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
++	ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
+ 	ieee80211_hw_set(hw, CHANCTX_STA_CSA);
+ 	ieee80211_hw_set(hw, CONNECTION_MONITOR);
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0143-mtk-mt76-mt7996-add-post-channel-switch-for-DFS-chan.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0143-mtk-mt76-mt7996-add-post-channel-switch-for-DFS-chan.patch
new file mode 100644
index 0000000..cd106e8
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0143-mtk-mt76-mt7996-add-post-channel-switch-for-DFS-chan.patch
@@ -0,0 +1,67 @@
+From 7b2e80bf62f10469992b8e08c801e9b64f7c775d Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 21 Feb 2024 13:41:23 +0800
+Subject: [PATCH 143/199] mtk: mt76: mt7996: add post channel switch for DFS
+ channel switching
+
+Add post channel switch callback for DFS channel switch support
+After CAC, we need to set channel again for DFS RDD (notify to change
+state from cac to active)
+Add IEEE80211_HW_HANDLE_QUIET_CSA flag to avoid stopping the entire vif TX
+queue when csa_blocked_tx is raised especially for DFS channel switch.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/init.c |  1 +
+ mt7996/main.c | 16 ++++++++++++++++
+ 2 files changed, 17 insertions(+)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index cc696a69..0ee2acfb 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -451,6 +451,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ 	ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
+ 	ieee80211_hw_set(hw, CHANCTX_STA_CSA);
+ 	ieee80211_hw_set(hw, CONNECTION_MONITOR);
++	ieee80211_hw_set(hw, HANDLES_QUIET_CSA);
+ 
+ 	hw->max_tx_fragments = 4;
+ 
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 4f3b0070..c8259b86 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1078,6 +1078,21 @@ fail:
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
++static int
++mt7996_post_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++			   struct ieee80211_bss_conf *link_conf)
++{
++	struct cfg80211_chan_def *chandef = &link_conf->chanreq.oper;
++	struct mt7996_phy *phy = mt7996_band_phy(hw, chandef->chan->band);
++	int ret;
++
++	ret = cfg80211_chandef_dfs_required(hw->wiphy, chandef, NL80211_IFTYPE_AP);
++	if (ret <= 0)
++		return ret;
++
++	return mt7996_set_channel(phy, chandef);
++}
++
+ static void mt7996_remove_link_sta(struct mt7996_dev *dev,
+ 				   struct ieee80211_bss_conf *conf,
+ 				   struct mt7996_bss_conf *mconf,
+@@ -2791,6 +2806,7 @@ const struct ieee80211_ops mt7996_ops = {
+ 	.release_buffered_frames = mt76_release_buffered_frames,
+ 	.get_txpower = mt7996_get_txpower,
+ 	.channel_switch_beacon = mt7996_channel_switch_beacon,
++	.post_channel_switch = mt7996_post_channel_switch,
+ 	.get_stats = mt7996_get_stats,
+ 	.get_et_sset_count = mt7996_get_et_sset_count,
+ 	.get_et_stats = mt7996_get_et_stats,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0144-mtk-mt76-mt7996-update-testmode-bf-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0144-mtk-mt76-mt7996-update-testmode-bf-support.patch
new file mode 100644
index 0000000..e6b3a8c
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0144-mtk-mt76-mt7996-update-testmode-bf-support.patch
@@ -0,0 +1,342 @@
+From 90a87564fc0a2fd18c73b290548e6ad299f4b3c9 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 3 Jun 2024 16:40:38 +0800
+Subject: [PATCH 144/199] mtk: mt76: mt7996: update testmode bf support
+
+Fix bssid & omac idx to band idx when testmode is enabled
+
+Add support for per-packet bw & primary channel selection index configuration
+This is used for ibf calibaration of group 9 ~ 13 in Kite
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt76.h            |  3 +++
+ mt7996/main.c     | 29 +++++++++++++++-------
+ mt7996/testmode.c | 63 ++++++++++++++++++++++++++++++++---------------
+ testmode.c        | 15 ++++++++---
+ testmode.h        |  6 +++++
+ tools/fields.c    | 16 ++++++++++--
+ 6 files changed, 98 insertions(+), 34 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index c6dda4a8..f544814e 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -782,6 +782,9 @@ struct mt76_testmode_data {
+ 	bool ibf;
+ 	bool ebf;
+ 
++	u8 tx_pkt_bw;
++	u8 tx_pri_sel;
++
+ 	u32 freq_offset;
+ 
+ 	u8 tx_power[4];
+diff --git a/mt7996/main.c b/mt7996/main.c
+index c8259b86..21457a61 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -349,16 +349,27 @@ static int mt7996_add_bss_conf(struct mt7996_phy *phy,
+ 		mconf = &mvif->deflink;
+ 	}
+ 
+-	mconf->mt76.idx = __ffs64(~dev->mt76.vif_mask);
+-	if (mconf->mt76.idx >= mt7996_max_interface_num(dev)) {
+-		ret = -ENOSPC;
+-		goto error;
+-	}
++	if (!dev->testmode_enable) {
++		mconf->mt76.idx = __ffs64(~dev->mt76.vif_mask);
++		if (mconf->mt76.idx >= mt7996_max_interface_num(dev)) {
++			ret = -ENOSPC;
++			goto error;
++		}
+ 
+-	idx = get_omac_idx(vif->type, phy->omac_mask);
+-	if (idx < 0) {
+-		ret = -ENOSPC;
+-		goto error;
++		idx = get_omac_idx(vif->type, phy->omac_mask);
++		if (idx < 0) {
++			ret = -ENOSPC;
++			goto error;
++		}
++	} else {
++		/* bss idx & omac idx should be set to band idx for ibf cal */
++		if (dev->mt76.vif_mask & BIT_ULL(band_idx) ||
++		    phy->omac_mask & BIT_ULL(band_idx)) {
++			ret = -ENOSPC;
++			goto error;
++		}
++		mconf->mt76.idx = band_idx;
++		idx = band_idx;
+ 	}
+ 
+ 	mconf->own_mld_id = get_own_mld_idx(dev->mld_id_mask, false);
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index 8b35d125..b956915e 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -235,8 +235,10 @@ mt7996_tm_init(struct mt7996_phy *phy, bool en)
+ 
+ 	mt7996_tm_rf_switch_mode(dev, rf_test_mode);
+ 
+-	mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, &mvif->sta.deflink, en);
+-	mt7996_mcu_add_sta(dev, &phy->monitor_vif->bss_conf, &mvif->deflink, NULL, &mvif->sta.deflink, en, false);
++	mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf,
++				&mvif->deflink, &mvif->sta.deflink, en);
++	mt7996_mcu_add_sta(dev, &phy->monitor_vif->bss_conf, &mvif->deflink,
++			   NULL, &mvif->sta.deflink, en, false);
+ 
+ 	mt7996_tm_set(dev, SET_ID(BAND_IDX), phy->mt76->band_idx);
+ 
+@@ -252,9 +254,11 @@ mt7996_tm_update_channel(struct mt7996_phy *phy)
+ {
+ #define CHAN_FREQ_BW_80P80_TAG		(SET_ID(CHAN_FREQ) | BIT(16))
+ 	struct mt7996_dev *dev = phy->dev;
++	struct mt76_testmode_data *td = &phy->mt76->test;
+ 	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
+ 	struct ieee80211_channel *chan = chandef->chan;
+-	u8 width = chandef->width;
++	u8 dbw, width = chandef->width, pri_sel = 0;
++	int width_mhz;
+ 	static const u8 ch_band[] = {
+ 		[NL80211_BAND_2GHZ] = 0,
+ 		[NL80211_BAND_5GHZ] = 1,
+@@ -274,18 +278,37 @@ mt7996_tm_update_channel(struct mt7996_phy *phy)
+ 		mt7996_tm_set(dev, CHAN_FREQ_BW_80P80_TAG, chandef->center_freq2 * 1000);
+ 	}
+ 
+-	/* TODO: define per-packet bw */
+-	/* per-packet bw */
+-	mt7996_tm_set(dev, SET_ID(DBW), mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW));
++	width_mhz = mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_MHZ);
++
++	/* data (per-packet) bw */
++	dbw = width;
++	if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_PKT_BW)) {
++		int pkt_bw_mhz = mt7996_tm_bw_mapping(td->tx_pkt_bw, BW_MAP_NL_TO_MHZ);
++
++		if (pkt_bw_mhz > width_mhz) {
++			dev_info(dev->mt76.dev,
++				 "per-packet bw cannot exceed system bw, use %d MHz instead\n",
++				 width_mhz);
++			td->tx_pkt_bw = width;
++		}
++		dbw = td->tx_pkt_bw;
++	}
++	mt7996_tm_set(dev, SET_ID(DBW), mt7996_tm_bw_mapping(dbw, BW_MAP_NL_TO_FW));
+ 
+ 	/* control channel selection index */
+-	mt7996_tm_set(dev, SET_ID(PRIMARY_CH), 0);
++	if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_PRI_SEL)) {
++		if (td->tx_pri_sel > width_mhz / 20 - 1) {
++			dev_info(dev->mt76.dev,
++				 "Invalid primary channel selection index, use 0 instead\n");
++			td->tx_pri_sel = 0;
++		}
++		pri_sel = td->tx_pri_sel;
++	}
++	mt7996_tm_set(dev, SET_ID(PRIMARY_CH), pri_sel);
+ 	mt7996_tm_set(dev, SET_ID(BAND), ch_band[chan->band]);
+ 
+ 	/* trigger switch channel calibration */
+ 	mt7996_tm_set(dev, SET_ID(CHAN_FREQ), chandef->center_freq1 * 1000);
+-
+-	// TODO: update power limit table
+ }
+ 
+ static void
+@@ -1179,14 +1202,9 @@ mt7996_tm_txbf_init(struct mt7996_phy *phy, u16 *val)
+ 	mt7996_tm_set_mac_addr(dev, td->addr[1], SET_ID(SA));
+ 	mt7996_tm_set_mac_addr(dev, td->addr[2], SET_ID(BSSID));
+ 
+-	/* bss idx & omac idx should be set to band idx for ibf cal */
+-	mvif->deflink.mt76.idx = band_idx;
+-	dev->mt76.vif_mask |= BIT_ULL(mvif->deflink.mt76.idx);
+-	mvif->deflink.mt76.omac_idx = band_idx;
+-	phy->omac_mask |= BIT_ULL(mvif->deflink.mt76.omac_idx);
+-
+ 	mt7996_mcu_add_dev_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, true);
+-	mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf, &mvif->deflink, &mvif->sta.deflink, true);
++	mt7996_mcu_add_bss_info(phy, &phy->monitor_vif->bss_conf,
++				&mvif->deflink, &mvif->sta.deflink, true);
+ 
+ 	if (td->ibf) {
+ 		if (td->is_txbf_dut) {
+@@ -1360,7 +1378,8 @@ mt7996_tm_add_txbf_sta(struct mt7996_phy *phy, u8 pfmu_idx, u8 nr, u8 nc, bool e
+ 			.tx_mode = mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY),
+ 		},
+ 	};
+-	u8 ndp_rate, ndpa_rate, rept_poll_rate, bf_bw;
++	u8 ndp_rate, ndpa_rate, rept_poll_rate;
++	u8 bf_bw = phy->mt76->chandef.width;
+ 
+ 	if ((td->tx_rate_mode == MT76_TM_TX_MODE_HE_SU ||
+ 	     td->tx_rate_mode == MT76_TM_TX_MODE_EHT_SU) && !td->ibf) {
+@@ -1390,11 +1409,12 @@ mt7996_tm_add_txbf_sta(struct mt7996_phy *phy, u8 pfmu_idx, u8 nr, u8 nc, bool e
+ 		}
+ 	}
+ 
+-	bf_bw = mt7996_tm_bw_mapping(phy->mt76->chandef.width, BW_MAP_NL_TO_BF);
+ 	req.bf.ndp_rate = ndp_rate;
+ 	req.bf.ndpa_rate = ndpa_rate;
+ 	req.bf.rept_poll_rate = rept_poll_rate;
+-	req.bf.bw = bf_bw;
++	if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_PKT_BW))
++		bf_bw = td->tx_pkt_bw;
++	req.bf.bw = mt7996_tm_bw_mapping(bf_bw, BW_MAP_NL_TO_BF);
+ 	req.bf.tx_mode = (td->tx_rate_mode == MT76_TM_TX_MODE_EHT_SU) ? 0xf : req.bf.tx_mode;
+ 
+ 	if (ebf) {
+@@ -1421,6 +1441,7 @@ mt7996_tm_txbf_profile_update(struct mt7996_phy *phy, u16 *val, bool ebf)
+ 	struct mt7996_dev *dev = phy->dev;
+ 	struct mt7996_pfmu_tag *tag = dev->test.txbf_pfmu_tag;
+ 	u8 rate, pfmu_idx = val[0], nc = val[2], nr;
++	u8 dbw = phy->mt76->chandef.width;
+ 	int ret;
+ 	bool is_atenl = val[5];
+ 
+@@ -1439,7 +1460,9 @@ mt7996_tm_txbf_profile_update(struct mt7996_phy *phy, u16 *val, bool ebf)
+ 	tag->t1.nr = nr;
+ 	tag->t1.nc = nc;
+ 	tag->t1.invalid_prof = true;
+-	tag->t1.data_bw = mt7996_tm_bw_mapping(phy->mt76->chandef.width, BW_MAP_NL_TO_BF);
++	if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_PKT_BW))
++		dbw = td->tx_pkt_bw;
++	tag->t1.data_bw = mt7996_tm_bw_mapping(dbw, BW_MAP_NL_TO_BF);
+ 	tag->t2.se_idx = td->tx_spe_idx;
+ 
+ 	if (ebf) {
+diff --git a/testmode.c b/testmode.c
+index f1d162ce..f7237cc0 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -25,6 +25,8 @@ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
+ 	[MT76_TM_ATTR_TX_DUTY_CYCLE] = { .type = NLA_U8 },
+ 	[MT76_TM_ATTR_TX_IPG] = { .type = NLA_U32 },
+ 	[MT76_TM_ATTR_TX_TIME] = { .type = NLA_U32 },
++	[MT76_TM_ATTR_TX_PKT_BW] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_TX_PRI_SEL] = { .type = NLA_U8 },
+ 	[MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
+ 	[MT76_TM_ATTR_DRV_DATA] = { .type = NLA_NESTED },
+ 	[MT76_TM_ATTR_OFF_CH_SCAN_CH] = { .type = NLA_U8 },
+@@ -567,12 +569,15 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 			   &td->tx_duty_cycle, 0, 99) ||
+ 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_POWER_CONTROL],
+ 			   &td->tx_power_control, 0, 1) ||
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_PKT_BW], &td->tx_pkt_bw,
++			   NL80211_CHAN_WIDTH_20_NOHT, NL80211_CHAN_WIDTH_320) ||
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_PRI_SEL], &td->tx_pri_sel, 0, 15) ||
+ 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_AID], &td->aid, 0, 16) ||
+ 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_CH], &td->offchan_ch, 36, 196) ||
+ 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH], &td->offchan_center_ch,
+ 			   36, 196) ||
+-	    mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_BW],
+-			   &td->offchan_bw, NL80211_CHAN_WIDTH_20_NOHT, NL80211_CHAN_WIDTH_160) ||
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_BW], &td->offchan_bw,
++			   NL80211_CHAN_WIDTH_20_NOHT, NL80211_CHAN_WIDTH_160) ||
+ 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_IPI_THRESHOLD], &td->ipi_threshold, 0, 10) ||
+ 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_IPI_RESET], &td->ipi_reset, 0, 1))
+ 		goto out;
+@@ -616,7 +621,7 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 
+ 			err = mt76_tm_get_u8(cur, &td->tx_power[idx++], 0, 63);
+ 			if (err)
+-				return err;
++				goto out;
+ 		}
+ 	}
+ 
+@@ -823,6 +828,10 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ 	     nla_put_u32(msg, MT76_TM_ATTR_TX_TIME, td->tx_time)) ||
+ 	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER_CONTROL) &&
+ 	     nla_put_u8(msg, MT76_TM_ATTR_TX_POWER_CONTROL, td->tx_power_control)) ||
++	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_PKT_BW) &&
++	     nla_put_u8(msg, MT76_TM_ATTR_TX_PKT_BW, td->tx_pkt_bw)) ||
++	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_PRI_SEL) &&
++	     nla_put_u8(msg, MT76_TM_ATTR_TX_PRI_SEL, td->tx_pri_sel)) ||
+ 	    (mt76_testmode_param_present(td, MT76_TM_ATTR_FREQ_OFFSET) &&
+ 	     nla_put_u32(msg, MT76_TM_ATTR_FREQ_OFFSET, td->freq_offset)))
+ 		goto out;
+diff --git a/testmode.h b/testmode.h
+index bda7624a..ed2068e5 100644
+--- a/testmode.h
++++ b/testmode.h
+@@ -37,6 +37,9 @@
+  * @MT76_TM_ATTR_TX_POWER_CONTROL: enable tx power control (u8)
+  * @MT76_TM_ATTR_TX_POWER: per-antenna tx power array (nested, u8 attrs)
+  *
++ * @MT76_TM_ATTR_TX_PKT_BW: per-packet data bandwidth (u8)
++ * @MT76_TM_ATTR_TX_PRI_SEL: primary channel selection index (u8)
++ *
+  * @MT76_TM_ATTR_FREQ_OFFSET: RF frequency offset (u32)
+  *
+  * @MT76_TM_ATTR_STATS: statistics (nested, see &enum mt76_testmode_stats_attr)
+@@ -104,6 +107,9 @@ enum mt76_testmode_attr {
+ 	MT76_TM_ATTR_TX_POWER_CONTROL,
+ 	MT76_TM_ATTR_TX_POWER,
+ 
++	MT76_TM_ATTR_TX_PKT_BW,
++	MT76_TM_ATTR_TX_PRI_SEL,
++
+ 	MT76_TM_ATTR_FREQ_OFFSET,
+ 
+ 	MT76_TM_ATTR_STATS,
+diff --git a/tools/fields.c b/tools/fields.c
+index f793d1a5..8b372602 100644
+--- a/tools/fields.c
++++ b/tools/fields.c
+@@ -35,13 +35,21 @@ static const char * const testmode_tx_mode[] = {
+ 	[MT76_TM_TX_MODE_EHT_MU] = "eht_mu",
+ };
+ 
+-static const char * const testmode_offchan_bw[] = {
++static const char * const testmode_bw[] = {
+ 	[NL80211_CHAN_WIDTH_20_NOHT] = "NOHT",
+ 	[NL80211_CHAN_WIDTH_20] = "20",
+ 	[NL80211_CHAN_WIDTH_40] = "40",
+ 	[NL80211_CHAN_WIDTH_80] = "80",
+ 	[NL80211_CHAN_WIDTH_80P80] = "80p80",
+ 	[NL80211_CHAN_WIDTH_160] = "160",
++	[NL80211_CHAN_WIDTH_5] = "5",
++	[NL80211_CHAN_WIDTH_10] = "10",
++	[NL80211_CHAN_WIDTH_1] = "1",
++	[NL80211_CHAN_WIDTH_2] = "2",
++	[NL80211_CHAN_WIDTH_4] = "4",
++	[NL80211_CHAN_WIDTH_8] = "8",
++	[NL80211_CHAN_WIDTH_16] = "16",
++	[NL80211_CHAN_WIDTH_320] = "320",
+ };
+ 
+ static const char * const testmode_txbf_act[] = {
+@@ -430,6 +438,8 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
+ 	FIELD(u8, TX_POWER_CONTROL, "tx_power_control"),
+ 	FIELD_ARRAY(u8, TX_POWER, "tx_power"),
+ 	FIELD(u8, TX_ANTENNA, "tx_antenna"),
++	FIELD_ENUM(TX_PKT_BW, "tx_pkt_bw", testmode_bw),
++	FIELD(u8, TX_PRI_SEL, "tx_pri_sel"),
+ 	FIELD(u32, FREQ_OFFSET, "freq_offset"),
+ 	FIELD(u8, AID, "aid"),
+ 	FIELD(u8, RU_ALLOC, "ru_alloc"),
+@@ -438,7 +448,7 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
+ 	FIELD_ARRAY(u16_hex, TXBF_PARAM, "txbf_param"),
+ 	FIELD(u8, OFF_CH_SCAN_CH, "offchan_ch"),
+ 	FIELD(u8, OFF_CH_SCAN_CENTER_CH, "offchan_center_ch"),
+-	FIELD_ENUM(OFF_CH_SCAN_BW, "offchan_bw", testmode_offchan_bw),
++	FIELD_ENUM(OFF_CH_SCAN_BW, "offchan_bw", testmode_bw),
+ 	FIELD(u8, IPI_THRESHOLD, "ipi_threshold"),
+ 	FIELD(u32, IPI_PERIOD, "ipi_period"),
+ 	FIELD(u8, IPI_RESET, "ipi_reset"),
+@@ -469,6 +479,8 @@ static struct nla_policy testdata_policy[NUM_MT76_TM_ATTRS] = {
+ 	[MT76_TM_ATTR_TX_POWER_CONTROL] = { .type = NLA_U8 },
+ 	[MT76_TM_ATTR_TX_ANTENNA] = { .type = NLA_U8 },
+ 	[MT76_TM_ATTR_TX_SPE_IDX] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_TX_PKT_BW] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_TX_PRI_SEL] = { .type = NLA_U8 },
+ 	[MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
+ 	[MT76_TM_ATTR_AID] = { .type = NLA_U8 },
+ 	[MT76_TM_ATTR_RU_ALLOC] = { .type = NLA_U8 },
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0145-mtk-mt76-mt7996-add-mlo-related-debugfs-knob.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0145-mtk-mt76-mt7996-add-mlo-related-debugfs-knob.patch
new file mode 100644
index 0000000..a72a37c
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0145-mtk-mt76-mt7996-add-mlo-related-debugfs-knob.patch
@@ -0,0 +1,190 @@
+From 37fd1cdf8fb3c77948c858e7bb258cd02b3cac31 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 27 May 2024 19:06:04 +0800
+Subject: [PATCH 145/199] mtk: mt76: mt7996: add mlo related debugfs knob
+
+Add the following debugfs knob
+Per-bss link info
+	- /sys/kernel/debug/ieee80211/phy0/<interface>/mt76_link_info
+Per-station link info
+	- /sys/kernel/debug/ieee80211/phy0/<interface>/stations/<mac address>/mt76_link_info
+
+Add TX MSDU failed, retried counts, and PER to mt76_links_info DebugFS knob.
+Remove MSDU statistics from link_sta_info DebugFS knob, since MSDUs are MLD-wise, instead of link-wise, handled.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ mt7996/debugfs.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++-
+ mt7996/main.c    |   1 +
+ mt7996/mt7996.h  |   1 +
+ 3 files changed, 119 insertions(+), 1 deletion(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 71dc0449..6eb395c4 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -1330,11 +1330,128 @@ mt7996_queues_show(struct seq_file *s, void *data)
+ 
+ DEFINE_SHOW_ATTRIBUTE(mt7996_queues);
+ 
++static int
++mt7996_sta_links_info_show(struct seq_file *s, void *data)
++{
++	struct ieee80211_sta *sta = s->private;
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	u64 tx_cnt = 0, tx_fails = 0, tx_retries = 0, rx_cnt = 0;
++	struct mt7996_dev *dev = msta->vif->dev;
++	unsigned long valid_links;
++	u8 link_id;
++
++	seq_printf(s, "primary link, link ID = %d\n", msta->pri_link);
++	seq_printf(s, "secondary link, link ID = %d\n", msta->sec_link);
++	seq_printf(s, "valid links = 0x%x\n", sta->valid_links);
++
++	mutex_lock(&dev->mt76.mutex);
++	valid_links = sta->valid_links ?: BIT(0);
++	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++		struct mt7996_link_sta *mlink =
++			mlink_dereference_protected(msta, link_id);
++		struct mt76_wcid *wcid;
++
++		if (!mlink)
++			continue;
++
++		wcid = &mlink->wcid;
++
++		tx_cnt += wcid->stats.tx_packets;
++		tx_fails += wcid->stats.tx_packets_failed;
++		tx_retries += wcid->stats.tx_packets_retried;
++		rx_cnt += wcid->stats.rx_packets;
++
++		seq_printf(s, "link%d: wcid=%d, phy=%d, link_valid=%d\n",
++			    wcid->link_id, wcid->idx, wcid->phy_idx, wcid->link_valid);
++	}
++	mutex_unlock(&dev->mt76.mutex);
++
++	/* PER may be imprecise, because MSDU total and failed counts
++	 * are updated at different times.
++	 */
++	seq_printf(s, "TX MSDU Count: %llu\n", tx_cnt);
++	seq_printf(s, "TX MSDU Fails: %llu (PER: %llu.%llu%%)\n", tx_fails,
++		   tx_cnt ? tx_fails * 1000 / tx_cnt / 10 : 0,
++		   tx_cnt ? tx_fails * 1000 / tx_cnt % 10 : 0);
++	seq_printf(s, "TX MSDU Retries: %llu\n", tx_retries);
++	seq_printf(s, "RX MSDU Count: %llu\n", rx_cnt);
++
++	return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(mt7996_sta_links_info);
++
+ void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 			    struct ieee80211_sta *sta, struct dentry *dir)
+ {
+ 	debugfs_create_file("fixed_rate", 0600, dir, sta, &fops_fixed_rate);
+ 	debugfs_create_file("hw-queues", 0400, dir, sta, &mt7996_queues_fops);
++	debugfs_create_file("mt76_links_info", 0400, dir, sta,
++			    &mt7996_sta_links_info_fops);
++}
++
++static int
++mt7996_vif_links_info_show(struct seq_file *s, void *data)
++{
++	struct ieee80211_vif *vif = s->private;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_dev *dev = mvif->dev;
++	struct mt7996_bss_conf *mconf;
++	struct mt7996_link_sta *mlink;
++	unsigned long valid_links;
++	u8 link_id, i;
++
++	static const char* width_to_bw[] = {
++		[NL80211_CHAN_WIDTH_40] = "40",
++		[NL80211_CHAN_WIDTH_80] = "80",
++		[NL80211_CHAN_WIDTH_80P80] = "80+80",
++		[NL80211_CHAN_WIDTH_160] = "160",
++		[NL80211_CHAN_WIDTH_5] = "5",
++		[NL80211_CHAN_WIDTH_10] = "10",
++		[NL80211_CHAN_WIDTH_20] = "20",
++		[NL80211_CHAN_WIDTH_20_NOHT] = "20_NOHT",
++		[NL80211_CHAN_WIDTH_320] = "320",
++	};
++
++	seq_printf(s, "master link id = %d\n", mvif->master_link_id);
++	seq_printf(s, "group mld id = %d\n", mvif->group_mld_id);
++	seq_printf(s, "mld remap id = %d\n", mvif->mld_remap_id);
++
++	seq_printf(s, "valid links = 0x%x\n", vif->valid_links);
++	for (i = 0; i < __MT_MAX_BAND; i++)
++		seq_printf(s, "band%d_link_id = %d\n", i, mvif->band_to_link[i]);
++
++	mutex_lock(&dev->mt76.mutex);
++	valid_links = vif->valid_links ?: BIT(0);
++	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++		mconf = mconf_dereference_protected(mvif, link_id);
++		mlink = mlink_dereference_protected(&mvif->sta, link_id);
++
++		if (!mconf || !mlink)
++			continue;
++
++		seq_printf(s, "- link[%02d]: bss_idx = %d, wcid = %d\n",
++			   mconf->link_id, mconf->mt76.idx, mlink->wcid.idx);
++		seq_printf(s, "            omac_idx = %d, own_mld_id=%d\n",
++			   mconf->mt76.omac_idx, mconf->own_mld_id);
++
++		if (!mconf->phy->chanctx)
++			continue;
++
++		seq_printf(s, "            band_idx=%d, channel=%d, bw%s\n",
++			   mconf->mt76.band_idx,
++			   mconf->phy->chanctx->chandef.chan->hw_value,
++			   width_to_bw[mconf->phy->chanctx->chandef.width]);
++	}
++	mutex_unlock(&dev->mt76.mutex);
++
++	return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(mt7996_vif_links_info);
++
++void mt7996_vif_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
++{
++	debugfs_create_file("mt76_links_info", 0400, vif->debugfs_dir, vif,
++			    &mt7996_vif_links_info_fops);
+ }
+ 
+ static void
+@@ -1473,7 +1590,6 @@ mt7996_link_sta_info_show(struct seq_file *file, void *data)
+ 	seq_printf(file, "Statistics:\n");
+ 	seq_printf(file, "\tTX:\n");
+ 	seq_printf(file, "\t\tBytes: %llu\n", stats->tx_bytes);
+-	seq_printf(file, "\t\tMSDU Count: %u\n", stats->tx_packets);
+ 	seq_printf(file, "\t\tMPDU Count: %u\n", stats->tx_mpdus);
+ 	seq_printf(file, "\t\tMPDU Fails: %u (PER: %u.%u%%)\n", stats->tx_failed,
+ 		   stats->tx_mpdus ? stats->tx_failed * 1000 / stats->tx_mpdus / 10 : 0,
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 21457a61..a85fa935 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -2841,6 +2841,7 @@ const struct ieee80211_ops mt7996_ops = {
+ 	.sta_add_debugfs = mt7996_sta_add_debugfs,
+ 	.link_sta_add_debugfs = mt7996_link_sta_add_debugfs,
+ 	// .link_add_debugfs = mt7996_link_add_debugfs,
++	.vif_add_debugfs = mt7996_vif_add_debugfs,
+ #endif
+ 	.set_radar_background = mt7996_set_radar_background,
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 8f084651..b5869bb8 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1247,6 +1247,7 @@ int mt7996_mcu_set_eml_omn(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u
+ #ifdef CONFIG_MAC80211_DEBUGFS
+ void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 			    struct ieee80211_sta *sta, struct dentry *dir);
++void mt7996_vif_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+ void mt7996_link_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 				 struct ieee80211_link_sta *link_sta,
+ 				 struct dentry *dir);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0146-mtk-mt76-mt7996-add-debugfs-knob-to-show-mlo-related.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0146-mtk-mt76-mt7996-add-debugfs-knob-to-show-mlo-related.patch
new file mode 100644
index 0000000..a116432
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0146-mtk-mt76-mt7996-add-debugfs-knob-to-show-mlo-related.patch
@@ -0,0 +1,203 @@
+From e35c211ac14b2189475f945ca9025e444d825c97 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Wed, 29 May 2024 18:45:50 +0800
+Subject: [PATCH 146/199] mtk: mt76: mt7996: add debugfs knob to show mlo
+ related table
+
+Add the following debugfs knob
+- /sys/kernel/debug/ieee80211/phy0/mt76/mat_table
+- /sys/kernel/debug/ieee80211/phy0/mt76/band0/agg_table
+- /sys/kernel/debug/ieee80211/phy0/mt76/band0/rmac_table
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/debugfs.c       |  3 +-
+ mt7996/mt7996.h        |  3 +-
+ mt7996/mtk_debug_i.h   | 24 +++++++++++
+ mt7996/mtk_debugfs_i.c | 92 +++++++++++++++++++++++++++++++++++++++++-
+ 4 files changed, 119 insertions(+), 3 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 6eb395c4..968174e6 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -1107,6 +1107,7 @@ int mt7996_init_band_debugfs(struct mt7996_phy *phy)
+ 
+ #ifdef CONFIG_MTK_DEBUG
+ 	mt7996_mtk_init_band_debugfs(phy, dir);
++	mt7996_mtk_init_band_debugfs_internal(phy, dir);
+ #endif
+ 	return 0;
+ }
+@@ -1143,7 +1144,7 @@ int mt7996_init_dev_debugfs(struct mt7996_phy *phy)
+ 	if (phy == &dev->phy) {
+ 		dev->debugfs_dir = dir;
+ #ifdef CONFIG_MTK_DEBUG
+-		mt7996_mtk_init_debugfs_internal(phy, dir);
++		mt7996_mtk_init_dev_debugfs_internal(phy, dir);
+ #endif
+ 	}
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index b5869bb8..96d8aae9 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1322,7 +1322,8 @@ void mt7996_packet_log_to_host(struct mt7996_dev *dev, const void *data, int len
+ void mt7996_dump_bmac_rxd_info(struct mt7996_dev *dev, __le32 *rxd);
+ void mt7996_dump_bmac_txd_info(struct seq_file *s, struct mt7996_dev *dev,
+ 			       __le32 *txd, bool is_hif_txd, bool dump_txp);
+-int mt7996_mtk_init_debugfs_internal(struct mt7996_phy *phy, struct dentry *dir);
++int mt7996_mtk_init_dev_debugfs_internal(struct mt7996_phy *phy, struct dentry *dir);
++int mt7996_mtk_init_band_debugfs_internal(struct mt7996_phy *phy, struct dentry *dir);
+ #endif
+ 
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mtk_debug_i.h b/mt7996/mtk_debug_i.h
+index d3756fa2..cec8d57e 100644
+--- a/mt7996/mtk_debug_i.h
++++ b/mt7996/mtk_debug_i.h
+@@ -982,6 +982,30 @@
+ #define HIF_TXP_ML_SHIFT 16
+ #define HIF_TXP_ML_MASK 0xffff0000
+ 
++/* UWTBL */
++#define MT_WF_UWTBL_BASE		0x820c4000
++#define MT_WF_UWTBL(ofs)		(MT_WF_UWTBL_BASE + (ofs))
++
++#define MT_WF_UWTBL_ITCR		MT_WF_UWTBL(0x130)
++#define MT_WF_UWTBL_ITCR0		MT_WF_UWTBL(0x138)
++#define MT_WF_UWTBL_ITCR1		MT_WF_UWTBL(0x13c)
++
++#define MT_WF_UWTBL_ITCR_SET		BIT(31)
++#define MT_WF_UWTBL_ITCR_INDEX		GENMASK(5, 0)
++
++/* RMAC */
++#define MT_WF_RMAC_SRAM_DATA0(_band)	MT_WF_RMAC(_band, 0x210)
++#define MT_WF_RMAC_SRAM_DATA1(_band)	MT_WF_RMAC(_band, 0x214)
++#define MT_WF_RMAC_SRAM_BITMAP0(_band)	MT_WF_RMAC(_band, 0x220)
++#define MT_WF_RMAC_SRAM_BITMAP1(_band)	MT_WF_RMAC(_band, 0x224)
++#define MT_WF_RMAC_MEM_CTRL(_band)	MT_WF_RMAC(_band, 0x228)
++
++#define MT_WF_RMAC_MEM_CRTL_TRIG	BIT(31)
++#define MT_WF_RMAC_MEM_CRTL_TDX		GENMASK(7, 0)
++
++/* AGG */
++#define MT_AGG_REMAP_CTRL(_band)	MT_WF_AGG(_band, 0x094)
++#define MT_AGG_REMAP_CTRL_OM_REMAP	GENMASK(5, 0)
+ #endif
+ 
+ #endif
+diff --git a/mt7996/mtk_debugfs_i.c b/mt7996/mtk_debugfs_i.c
+index ea412cd5..839c3e31 100644
+--- a/mt7996/mtk_debugfs_i.c
++++ b/mt7996/mtk_debugfs_i.c
+@@ -695,7 +695,86 @@ static int mt7996_rx_msdu_pg_read(struct seq_file *s, void *data)
+ 	return 0;
+ }
+ 
+-int mt7996_mtk_init_debugfs_internal(struct mt7996_phy *phy, struct dentry *dir)
++static int
++mt7996_mat_table_show(struct seq_file *s, void *data)
++{
++#define MT_MAX_MAT_TABLE_SIZE	63
++	struct mt7996_dev *dev = s->private;
++	int i;
++
++	for (i = 0; i < MT_MAX_MAT_TABLE_SIZE; i++) {
++		u32 req = MT_WF_UWTBL_ITCR_SET |
++			  u32_encode_bits(i, MT_WF_UWTBL_ITCR_INDEX);
++		u32 dw[2];
++		u8 *addr = (u8 *)dw;
++
++		mt76_wr(dev, MT_WF_UWTBL_ITCR, req);
++		dw[0] = mt76_rr(dev, MT_WF_UWTBL_ITCR0);
++		dw[1] = mt76_rr(dev, MT_WF_UWTBL_ITCR1);
++
++		if (dw[0] || dw[1])
++			seq_printf(s, "own_mld_id%d\tAddr: %pM\n", i, addr);
++	}
++	return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(mt7996_mat_table);
++
++static int
++mt7996_rmac_table_show(struct seq_file *s, void *data)
++{
++	struct mt7996_phy *phy = s->private;
++	struct mt7996_dev *dev = phy->dev;
++	unsigned long usage_bitmap[2] = {0};
++	int i, j;
++	u8 band = phy->mt76->band_idx;
++
++	usage_bitmap[0] = (unsigned long)mt76_rr(dev, MT_WF_RMAC_SRAM_BITMAP0(band));
++	usage_bitmap[1] = (unsigned long)mt76_rr(dev, MT_WF_RMAC_SRAM_BITMAP1(band));
++
++	for (i = 0; i < 2; i++) {
++		for_each_set_bit(j, &usage_bitmap[i], 32) {
++			u32 req = MT_WF_RMAC_MEM_CRTL_TRIG |
++				  u32_encode_bits(i * 32 + j, MT_WF_RMAC_MEM_CRTL_TDX);
++			u32 dw[2];
++			u8 *addr = (u8 *)dw;
++
++			mt76_wr(dev, MT_WF_RMAC_MEM_CTRL(band), req);
++			dw[0] = mt76_rr(dev, MT_WF_RMAC_SRAM_DATA0(band));
++			dw[1] = mt76_rr(dev, MT_WF_RMAC_SRAM_DATA1(band));
++
++			seq_printf(s, "omac_idx%d\tAddr: %pM\n", i * 32 + j, addr);
++		}
++	}
++
++	return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(mt7996_rmac_table);
++
++static int
++mt7996_agg_table_show(struct seq_file *s, void *data)
++{
++	struct mt7996_phy *phy = s->private;
++	struct mt7996_dev *dev = phy->dev;
++	int i, j;
++	u8 band = phy->mt76->band_idx;
++
++	for (i = 0; i < 4; i++) {
++		u32 value = mt76_rr(dev, MT_AGG_REMAP_CTRL(band) + 4 * i);
++
++		for (j = 0; j < 4; j++) {
++			u8 shift = 8 * j;
++			u32 mask = MT_AGG_REMAP_CTRL_OM_REMAP << shift;
++
++			seq_printf(s, "idx%d: %d\n", i * 4 + j,
++				      (value & mask) >> shift);
++		}
++	}
++
++	return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(mt7996_agg_table);
++
++int mt7996_mtk_init_dev_debugfs_internal(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+ 
+@@ -714,7 +793,18 @@ int mt7996_mtk_init_debugfs_internal(struct mt7996_phy *phy, struct dentry *dir)
+ 				    mt7996_pse_fid_read);
+ 
+ 	debugfs_create_u8("dump_ple_txd", 0600, dir, &dev->dbg.dump_ple_txd);
++
++	/* MLO related Table */
++	debugfs_create_file("mat_table", 0400, dir, dev, &mt7996_mat_table_fops);
+ 	return 0;
+ }
+ 
++int mt7996_mtk_init_band_debugfs_internal(struct mt7996_phy *phy, struct dentry *dir)
++{
++	/* MLO related Table */
++	debugfs_create_file("rmac_table", 0400, dir, phy, &mt7996_rmac_table_fops);
++	debugfs_create_file("agg_table", 0400, dir, phy, &mt7996_agg_table_fops);
++
++	return 0;
++}
+ #endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0147-mtk-mt76-mt7996-add-debugfs-knob-to-set-agc.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0147-mtk-mt76-mt7996-add-debugfs-knob-to-set-agc.patch
new file mode 100644
index 0000000..41861e6
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0147-mtk-mt76-mt7996-add-debugfs-knob-to-set-agc.patch
@@ -0,0 +1,212 @@
+From 8facea1f5154f7a3d246456e0e9e1d16872779a8 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Fri, 31 May 2024 10:55:03 +0800
+Subject: [PATCH 147/199] mtk: mt76: mt7996: add debugfs knob to set agc
+
+Add the following debugfs knob
+- /sys/kernel/debug/ieee80211/phy0/mt76/mlo_agc_tx
+- /sys/kernel/debug/ieee80211/phy0/mt76/mlo_agc_trig
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt76_connac_mcu.h      |   1 +
+ mt7996/mcu.h           |  20 ++++++++
+ mt7996/mt7996.h        |   1 +
+ mt7996/mtk_debugfs_i.c | 103 +++++++++++++++++++++++++++++++++++++++++
+ mt7996/mtk_mcu.c       |   6 +++
+ 5 files changed, 131 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index d72337d4..f7b1f0d0 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1312,6 +1312,7 @@ enum {
+ 	MCU_UNI_CMD_THERMAL_CAL = 0x4c,
+ 	MCU_UNI_CMD_RRO = 0x57,
+ 	MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
++	MCU_UNI_CMD_MLO = 0x59,
+ 	MCU_UNI_CMD_PER_STA_INFO = 0x6d,
+ 	MCU_UNI_CMD_ALL_STA_INFO = 0x6e,
+ 	MCU_UNI_CMD_ASSERT_DUMP = 0x6f,
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index c39dcc3c..389aab63 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -1157,6 +1157,26 @@ enum {
+ 	UNI_CMD_SCS_ENABLE,
+ };
+ 
++enum {
++	UNI_CMD_MLO_AGC_TX = 4,
++	UNI_CMD_MLO_AGC_TRIG = 5,
++};
++
++struct mt7996_mlo_agc_set {
++	u8 rsv[4];
++
++	__le16 tag;
++	__le16 len;
++
++	u8 mld_id;
++	u8 link_id;
++	u8 ac;
++	u8 disp_pol;
++	u8 ratio;
++	u8 order;
++	__le16 mgf;
++} __packed;
++
+ #define MT7996_PATCH_SEC		GENMASK(31, 24)
+ #define MT7996_PATCH_SCRAMBLE_KEY	GENMASK(15, 8)
+ #define MT7996_PATCH_AES_KEY		GENMASK(7, 0)
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 96d8aae9..cb6a5753 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1324,6 +1324,7 @@ void mt7996_dump_bmac_txd_info(struct seq_file *s, struct mt7996_dev *dev,
+ 			       __le32 *txd, bool is_hif_txd, bool dump_txp);
+ int mt7996_mtk_init_dev_debugfs_internal(struct mt7996_phy *phy, struct dentry *dir);
+ int mt7996_mtk_init_band_debugfs_internal(struct mt7996_phy *phy, struct dentry *dir);
++int mt7996_mcu_mlo_agc(struct mt7996_dev *dev, const void *data, int len);
+ #endif
+ 
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/mt7996/mtk_debugfs_i.c b/mt7996/mtk_debugfs_i.c
+index 839c3e31..852d1b12 100644
+--- a/mt7996/mtk_debugfs_i.c
++++ b/mt7996/mtk_debugfs_i.c
+@@ -774,6 +774,106 @@ mt7996_agg_table_show(struct seq_file *s, void *data)
+ }
+ DEFINE_SHOW_ATTRIBUTE(mt7996_agg_table);
+ 
++static ssize_t mt7996_mlo_agc_tx_set(struct file *file,
++				     const char __user *user_buf,
++				     size_t count, loff_t *ppos)
++{
++	struct mt7996_dev *dev = file->private_data;
++	struct mt7996_mlo_agc_set req;
++	char buf[100];
++	int ret;
++	u16 mgf;
++
++	memset(&req, 0, sizeof(req));
++
++	if (count >= sizeof(buf))
++		return -EINVAL;
++
++	if (copy_from_user(buf, user_buf, count))
++		return -EFAULT;
++
++	if (count && buf[count - 1] == '\n')
++		buf[count - 1] = '\0';
++	else
++		buf[count] = '\0';
++
++	if (sscanf(buf, "%hhu %hhu %hhu %hhu %hu %hhu %hhu",
++		   &req.mld_id, &req.link_id, &req.ac, &req.disp_pol,
++		   &mgf, &req.ratio, &req.order) != 7) {
++		dev_warn(dev->mt76.dev,
++			 "format: [MldRecIdx] [Link] [Ac] [DispPol] [MGF] [Ratio] [Order]\n");
++		goto out;
++	}
++
++	req.tag = cpu_to_le16(UNI_CMD_MLO_AGC_TX);
++	req.len = cpu_to_le16(sizeof(req) - 4);
++	req.mgf = cpu_to_le16(mgf);
++
++	ret = mt7996_mcu_mlo_agc(dev, &req, sizeof(req));
++	if (ret)
++		return -EFAULT;
++
++out:
++	return count;
++}
++
++static const struct file_operations fops_mlo_agc_tx = {
++	.write = mt7996_mlo_agc_tx_set,
++	.open = simple_open,
++	.owner = THIS_MODULE,
++	.llseek = default_llseek,
++};
++
++static ssize_t mt7996_mlo_agc_trig_set(struct file *file,
++				       const char __user *user_buf,
++				       size_t count, loff_t *ppos)
++{
++	struct mt7996_dev *dev = file->private_data;
++	struct mt7996_mlo_agc_set req;
++	char buf[100];
++	int ret;
++	u16 mgf;
++
++	memset(&req, 0, sizeof(req));
++
++	if (count >= sizeof(buf))
++		return -EINVAL;
++
++	if (copy_from_user(buf, user_buf, count))
++		return -EFAULT;
++
++	if (count && buf[count - 1] == '\n')
++		buf[count - 1] = '\0';
++	else
++		buf[count] = '\0';
++
++	if (sscanf(buf, "%hhu %hhu %hhu %hhu %hu %hhu",
++		   &req.mld_id, &req.link_id, &req.ac, &req.disp_pol,
++		   &mgf, &req.ratio) != 6) {
++		dev_warn(dev->mt76.dev,
++			 "format: [MldRecIdx] [Link] [Ac] [DispPol] [MGF] [Ratio]\n");
++		goto out;
++	}
++
++	req.tag = cpu_to_le16(UNI_CMD_MLO_AGC_TRIG);
++	req.len = cpu_to_le16(sizeof(req) - 4);
++	req.mgf = cpu_to_le16(mgf);
++
++	ret = mt7996_mcu_mlo_agc(dev, &req, sizeof(req));
++	if (ret)
++		return -EFAULT;
++
++out:
++	return count;
++}
++
++static const struct file_operations fops_mlo_agc_trig = {
++	.write = mt7996_mlo_agc_trig_set,
++	.open = simple_open,
++	.owner = THIS_MODULE,
++	.llseek = default_llseek,
++};
++
+ int mt7996_mtk_init_dev_debugfs_internal(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+@@ -796,6 +896,9 @@ int mt7996_mtk_init_dev_debugfs_internal(struct mt7996_phy *phy, struct dentry *
+ 
+ 	/* MLO related Table */
+ 	debugfs_create_file("mat_table", 0400, dir, dev, &mt7996_mat_table_fops);
++	debugfs_create_file("mlo_agc_tx", 0200, dir, dev, &fops_mlo_agc_tx);
++	debugfs_create_file("mlo_agc_trig", 0200, dir, dev, &fops_mlo_agc_trig);
++
+ 	return 0;
+ }
+ 
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index 809181e0..82e3f721 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -1364,4 +1364,10 @@ int mt7996_mcu_thermal_debug(struct mt7996_dev *dev, u8 mode, u8 action)
+ 	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(THERMAL_CAL), &req,
+ 	                         sizeof(req), true);
+ }
++
++int mt7996_mcu_mlo_agc(struct mt7996_dev *dev, const void *data, int len)
++{
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MLO), data,
++	                        len, true);
++}
+ #endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0148-mtk-mt76-mt7996-set-unused-band-to-UNSPECIFIED-in-ba.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0148-mtk-mt76-mt7996-set-unused-band-to-UNSPECIFIED-in-ba.patch
new file mode 100644
index 0000000..1e80e00
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0148-mtk-mt76-mt7996-set-unused-band-to-UNSPECIFIED-in-ba.patch
@@ -0,0 +1,66 @@
+From 946c2a0cd923e411009c541184978747947ebb5a Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Wed, 5 Jun 2024 10:11:13 +0800
+Subject: [PATCH 148/199] mtk: mt76: mt7996: set unused band to UNSPECIFIED in
+ band_to_link
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/main.c   | 4 ++++
+ mt7996/mcu.c    | 3 +++
+ mt7996/mt7996.h | 3 +++
+ 3 files changed, 10 insertions(+)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index a85fa935..b6231dff 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -482,6 +482,8 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ 	mvif->sta.vif = mvif;
+ 	/* TODO: temporaily set this to prevent some crashes */
+ 	mvif->deflink.phy = phy;
++	memset(mvif->band_to_link, IEEE80211_LINK_UNSPECIFIED,
++	       sizeof(mvif->band_to_link));
+ 
+ 	if (vif->type == NL80211_IFTYPE_STATION)
+ 		ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
+@@ -1054,6 +1056,8 @@ mt7996_channel_switch_beacon(struct ieee80211_hw *hw,
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 	link_id = mvif->band_to_link[phy->mt76->band_idx];
++	if (link_id == IEEE80211_LINK_UNSPECIFIED)
++		goto out;
+ 
+ 	if (!mvif->cs_ready_links)
+ 		mvif->cs_link_id = link_id;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 3a950a90..40ba5b43 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -374,6 +374,9 @@ mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ 	int link_id, band_idx = mphy->band_idx;
+ 
+ 	link_id = mvif->band_to_link[band_idx];
++	if (link_id == IEEE80211_LINK_UNSPECIFIED)
++		return;
++
+ 	link_conf = rcu_dereference(vif->link_conf[link_id]);
+ 
+ 	if (!link_conf || !link_conf->csa_active || vif->type == NL80211_IFTYPE_STATION)
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index cb6a5753..dc0209de 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -962,6 +962,9 @@ mt7996_get_link_wcid(struct mt7996_dev *dev, u16 idx, u8 band_idx)
+ 
+ 	mlink = wcid_to_mlink(wcid);
+ 	link_id = mlink->sta->vif->band_to_link[band_idx];
++	if (link_id == IEEE80211_LINK_UNSPECIFIED)
++		return wcid;
++
+ 	mlink = rcu_dereference(mlink->sta->link[link_id]);
+ 	if (!mlink)
+ 		return wcid;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0149-mtk-mt76-mt7996-support-per-link-report-of-bss-color.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0149-mtk-mt76-mt7996-support-per-link-report-of-bss-color.patch
new file mode 100644
index 0000000..9e2f7e6
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0149-mtk-mt76-mt7996-support-per-link-report-of-bss-color.patch
@@ -0,0 +1,53 @@
+From bd2464e8ba51bbb00a1b6aabf12041431d57289b Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 5 Jun 2024 19:11:13 +0800
+Subject: [PATCH 149/199] mtk: mt76: mt7996: support per-link report of bss
+ color change
+
+ieee80211_color_change_finish() has been extended for per-link support.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mcu.c | 17 ++++++++++++++---
+ 1 file changed, 14 insertions(+), 3 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 40ba5b43..db388e66 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -472,10 +472,21 @@ out:
+ static void
+ mt7996_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ {
+-	if (!vif->bss_conf.color_change_active || vif->type == NL80211_IFTYPE_STATION)
++	struct mt76_phy *mphy = (struct mt76_phy *)priv;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct ieee80211_bss_conf *link_conf;
++	u8 link_id;
++
++	link_id = mvif->band_to_link[mphy->band_idx];
++	if (link_id == IEEE80211_LINK_UNSPECIFIED)
++		return;
++
++	link_conf = rcu_dereference(vif->link_conf[link_id]);
++	if (!link_conf || !link_conf->color_change_active ||
++	    vif->type == NL80211_IFTYPE_STATION)
+ 		return;
+ 
+-	ieee80211_color_change_finish(vif);
++	ieee80211_color_change_finish(vif, link_id);
+ }
+ 
+ static void
+@@ -511,7 +522,7 @@ mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb)
+ 		case UNI_EVENT_IE_COUNTDOWN_BCC:
+ 			ieee80211_iterate_active_interfaces_atomic(mphy->hw,
+ 					IEEE80211_IFACE_ITER_RESUME_ALL,
+-					mt7996_mcu_cca_finish, mphy->hw);
++					mt7996_mcu_cca_finish, mphy);
+ 			break;
+ 		}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0150-mtk-mt76-mt7996-Fix-inconsistent-QoS-mapping-between.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0150-mtk-mt76-mt7996-Fix-inconsistent-QoS-mapping-between.patch
new file mode 100644
index 0000000..f185fed
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0150-mtk-mt76-mt7996-Fix-inconsistent-QoS-mapping-between.patch
@@ -0,0 +1,191 @@
+From ac20b6e9a96c45213a2bbccb051d16e4408f2969 Mon Sep 17 00:00:00 2001
+From: Rex Lu <rex.lu@mediatek.com>
+Date: Fri, 7 Jun 2024 13:51:25 +0800
+Subject: [PATCH 150/199] mtk: mt76: mt7996: Fix inconsistent QoS mapping
+ between SW and HW
+
+Fix inconsistent QoS mapping between SW and HW.
+Specifically, the mapping from IP DSCP to IEEE 802.11 user priority may be customized.
+Therefore, driver needs to pass the mapping to HW, so that the QoS type of traffic can be mapped in a consistent manner for both SW and HW paths.
+
+Refactor mt7996_mcu_set_qos_map function.
+1. According to fw logic, it will fill the dscp value 0~63 correspond tid.
+for example:
+The last set is 48-56 in the default qos map from hostapd.
+And it will set tid 7. but 57-63 in original function will not fill any value,
+so it will set zero to fw. Once the value of dscp is 57. mac80211 will correspond to tid 7.
+and fw will correspond to tid 0.
+
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+---
+ mt76_connac_mcu.h |  1 +
+ mt7996/main.c     | 31 ++++++++++++++++++++++++++
+ mt7996/mcu.c      | 55 +++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mt7996.h   |  8 ++++++-
+ 4 files changed, 94 insertions(+), 1 deletion(-)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index f7b1f0d0..0244b5aa 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1262,6 +1262,7 @@ enum {
+ 	MCU_EXT_CMD_GROUP_PRE_CAL_INFO = 0xab,
+ 	MCU_EXT_CMD_DPD_PRE_CAL_INFO = 0xac,
+ 	MCU_EXT_CMD_PHY_STAT_INFO = 0xad,
++	MCU_EXT_CMD_SET_QOS_MAP = 0xb4,
+ };
+ 
+ enum {
+diff --git a/mt7996/main.c b/mt7996/main.c
+index b6231dff..3c12bd52 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -2365,6 +2365,9 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ 
+ 	ctx->dev = NULL;
+ 
++	if (path->mtk_wdma.amsdu)
++		path->mtk_wdma.tid = mvif->qos_map[path->mtk_wdma.tid >> 2];
++
+ 	return 0;
+ }
+ 
+@@ -2794,6 +2797,33 @@ mt7996_event_callback(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	}
+ }
+ 
++static int
++mt7996_set_qos_map(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++		   struct cfg80211_qos_map *qos_map)
++{
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	unsigned long valid_links = vif->valid_links ?: BIT(0);
++	unsigned int link_id;
++	int ret = 0;
++
++	mutex_lock(&dev->mt76.mutex);
++	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++		struct mt7996_bss_conf *mconf =
++			mconf_dereference_protected(mvif, link_id);
++
++		if (!mconf)
++			continue;
++
++		ret = mt7996_mcu_set_qos_map(dev, mconf, qos_map);
++		if(ret)
++			break;
++	}
++	mutex_unlock(&dev->mt76.mutex);
++
++	return ret;
++}
++
+ const struct ieee80211_ops mt7996_ops = {
+ 	.add_chanctx = ieee80211_emulate_add_chanctx,
+ 	.remove_chanctx = ieee80211_emulate_remove_chanctx,
+@@ -2861,4 +2891,5 @@ const struct ieee80211_ops mt7996_ops = {
+ 	.switch_vif_chanctx = mt7996_switch_vif_chanctx,
+ 	.change_vif_links = mt7996_change_vif_links,
+ 	.change_sta_links = mt7996_change_sta_links,
++	.set_qos_map = mt7996_set_qos_map,
+ };
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index db388e66..51f15928 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -6667,4 +6667,59 @@ int mt7996_mcu_set_csi(struct mt7996_phy *phy, u8 mode,
+ 		return -EINVAL;
+ 	}
+ }
++
++int mt7996_mcu_set_qos_map(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf,
++			   struct cfg80211_qos_map *usr_qos_map)
++{
++	struct {
++		u8 bss_idx;
++		u8 qos_map_enable;
++		u8 __rsv[2];
++		s8 qos_map[IP_DSCP_NUM];
++	} __packed req = {
++		.bss_idx = mconf->mt76.idx,
++		.qos_map_enable = true,
++	};
++	s8 i;
++
++	/* Default QoS map, defined in section 2.3 of RFC8325.
++	 * Three most significant bits of DSCP are used as UP.
++	 */
++	for (i = 0; i < IP_DSCP_NUM; ++i)
++		req.qos_map[i] = i >> 3;
++
++	/* Recommended QoS map, defined in section 4 of RFC8325.
++	 * Used in cfg80211_classify8021d since kernel v6.8.
++	 */
++	req.qos_map[10] = req.qos_map[12] = req.qos_map[14] = req.qos_map[16] = 0;
++	req.qos_map[18] = req.qos_map[20] = req.qos_map[22] = 3;
++	req.qos_map[24] = 4;
++	req.qos_map[40] = 5;
++	req.qos_map[44] = req.qos_map[46] = 6;
++	req.qos_map[48] = 7;
++
++	/* User-defined QoS map */
++	if (usr_qos_map) {
++		for (i = 0; i < IEEE80211_NUM_UPS; ++i) {
++			u8 low = usr_qos_map->up[i].low;
++			u8 high = usr_qos_map->up[i].high;
++
++			if (low < IP_DSCP_NUM && high < IP_DSCP_NUM && low <= high)
++				memset(req.qos_map + low, i, high - low + 1);
++		}
++
++		for (i = 0; i < usr_qos_map->num_des; ++i) {
++			u8 dscp = usr_qos_map->dscp_exception[i].dscp;
++			u8 up = usr_qos_map->dscp_exception[i].up;
++
++			if (dscp < IP_DSCP_NUM && up < IEEE80211_NUM_UPS)
++				req.qos_map[dscp] = up;
++		}
++	}
++
++	memcpy(mconf->vif->qos_map, req.qos_map, IP_DSCP_NUM);
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(SET_QOS_MAP), &req,
++				 sizeof(req), false);
++}
+ #endif
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index dc0209de..302ac7bf 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -134,6 +134,8 @@
+ #define MT7996_MAX_PROBE_TIMEOUT	500
+ #define MT7996_MAX_PROBE_TRIES		2
+ 
++#define IP_DSCP_NUM			64
++
+ struct mt7996_vif;
+ struct mt7996_sta;
+ struct mt7996_dfs_pulse;
+@@ -376,6 +378,9 @@ struct mt7996_vif {
+ 	void *probe[__MT_MAX_BAND];
+ 	unsigned long probe_send_time[__MT_MAX_BAND];
+ 	int probe_send_count[__MT_MAX_BAND];
++
++	/* QoS map support */
++	u8 qos_map[IP_DSCP_NUM];
+ };
+ 
+ /* crash-dump */
+@@ -1333,5 +1338,6 @@ int mt7996_mcu_mlo_agc(struct mt7996_dev *dev, const void *data, int len);
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ int mt7996_dma_rro_init(struct mt7996_dev *dev);
+ #endif /* CONFIG_NET_MEDIATEK_SOC_WED */
+-
++int mt7996_mcu_set_qos_map(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf,
++			   struct cfg80211_qos_map *usr_qos_map);
+ #endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0151-mtk-mt76-mt7996-add-support-for-MLD-interface-to-sca.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0151-mtk-mt76-mt7996-add-support-for-MLD-interface-to-sca.patch
new file mode 100644
index 0000000..630fdb9
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0151-mtk-mt76-mt7996-add-support-for-MLD-interface-to-sca.patch
@@ -0,0 +1,144 @@
+From 8a5ed33fbb5d297d74acfc6cc75ee358501aed18 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 30 May 2024 15:26:34 +0800
+Subject: [PATCH 151/199] mtk: mt76: mt7996: add support for MLD interface to
+ scan
+
+Before queueing a HW scan work, mt7996_hw_scan() should make sure that
+there is mt7996_bss_conf on the scanning phy.
+
+For MLD interface scan, we first iterate all links to find the
+mt7996_bss_conf with target phy. A mt7996_bss_conf not being found
+implies the link is invalid, for which an AP interface should NOT scan
+while a STA interface should. Here we use the default link for STA scan,
+and delete it after the scan is completed.
+
+Note that AP's mt7996_bss_conf is added when the link is assigned to
+a chanctx, meaning that the operating channel is already determined.
+However, if the AP uses ACS to decide link's operating channel,
+hostapd adds the link in mac80211 and triggers a scan, while
+mt7996_bss_conf is not yet allocated. In such a case we also use the
+default link to scan, and delete it once the scan is finished.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ mt7996/main.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 77 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 3c12bd52..a70fc4d3 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -2375,10 +2375,24 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ 
+ void mt7996_scan_complete(struct mt7996_phy *phy, bool aborted)
+ {
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)phy->scan_vif->drv_priv;
++	struct ieee80211_vif *vif = phy->scan_vif;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct cfg80211_scan_info info = {
+ 		.aborted = aborted,
+ 	};
++	int i;
++
++	if (ieee80211_vif_is_mld(vif)) {
++		struct mt7996_bss_conf *mconf;
++
++		for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
++			mconf = mconf_dereference_protected(mvif, i);
++
++			if (mconf && mconf->phy == phy && mconf == &mvif->deflink)
++				mt7996_remove_bss_conf(vif, &vif->bss_conf,
++						       &mvif->deflink);
++		}
++	}
+ 
+ 	ieee80211_scan_completed(mvif->hw, &info);
+ 	phy->scan_chan = NULL;
+@@ -2394,6 +2408,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	struct cfg80211_scan_request *req = &hw_req->req;
+ 	struct mt7996_phy *phy = mt7996_band_phy(hw, req->channels[0]->band);
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_bss_conf *mconf;
+ 	int ret;
+ 
+ 	mutex_lock(&phy->dev->mt76.mutex);
+@@ -2407,7 +2422,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	phy->scan_chan_idx = 0;
+ 
+ 	if (!ieee80211_vif_is_mld(vif)) {
+-		struct mt7996_bss_conf *mconf = mconf_dereference_protected(mvif, 0);
++		mconf = mconf_dereference_protected(mvif, 0);
+ 
+ 		if (mconf && mconf->phy != phy) {
+ 			mt7996_remove_bss_conf(vif, &vif->bss_conf, &mvif->deflink);
+@@ -2421,6 +2436,66 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 				return ret;
+ 			}
+ 		}
++	} else {
++		struct ieee80211_bss_conf *link_conf;
++		unsigned long valid_links = vif->valid_links;
++		unsigned int link_id;
++		bool found = false;
++
++		for_each_set_bit(link_id, &valid_links,
++				 IEEE80211_MLD_MAX_NUM_LINKS) {
++			mconf = mconf_dereference_protected(mvif, link_id);
++			if (mconf && mconf->phy == phy) {
++				found = true;
++				break;
++			}
++
++			link_conf = link_conf_dereference_protected(vif, link_id);
++			if (link_conf && !mconf) {
++				/* The link is added in mac80211, but not yet
++				 * initialized and assigned to a chanctx.
++				 * Here we use the default link to perform scan.
++				 */
++				memcpy(&vif->bss_conf, link_conf, sizeof(struct ieee80211_bss_conf));
++				ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
++				found = true;
++				break;
++			}
++		}
++
++		if (!found) {
++			if (vif->type != NL80211_IFTYPE_STATION) {
++				/* Only allowed STA MLD to scan full-band when
++				 * there is no valid link on the band.
++				 * (For example, when connecting by 2 links
++				 * (2+5 GHz), an AP MLD is not allowed to scan
++				 * full-band (2+5+6 GHz), while a STA MLD is.)
++				 */
++				mutex_unlock(&phy->dev->mt76.mutex);
++				mt7996_scan_complete(phy, 0);
++				return 0;
++			}
++
++			/* Try to find an empty link, which is later used to scan. */
++			for (link_id = 0;
++			     link_id < IEEE80211_MLD_MAX_NUM_LINKS;
++			     link_id++) {
++				if (!rcu_access_pointer(mvif->link[link_id]))
++					break;
++			}
++
++			if (link_id == IEEE80211_MLD_MAX_NUM_LINKS) {
++				mutex_unlock(&phy->dev->mt76.mutex);
++				return -ENOLINK;
++			}
++
++			vif->bss_conf.link_id = link_id;
++			ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
++			if (ret) {
++				mutex_unlock(&phy->dev->mt76.mutex);
++				return ret;
++			}
++		}
+ 	}
+ 
+ 	set_bit(MT76_SCANNING, &phy->mt76->state);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0152-mtk-mt76-mt7996-add-per-link-txpower-config.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0152-mtk-mt76-mt7996-add-per-link-txpower-config.patch
new file mode 100644
index 0000000..7dc0c24
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0152-mtk-mt76-mt7996-add-per-link-txpower-config.patch
@@ -0,0 +1,35 @@
+From e2fe59ea3398bb2c2b00d2333aaa707aed44b37a Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 11 Jun 2024 17:04:00 +0800
+Subject: [PATCH 152/199] mtk: mt76: mt7996: add per-link txpower config
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/main.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index a70fc4d3..19a1c7b3 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1016,7 +1016,7 @@ out:
+ }
+ 
+ int mt7996_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+-		       int *dbm)
++		       unsigned int link_id, int *dbm)
+ {
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_bss_conf *mconf;
+@@ -1025,7 +1025,7 @@ int mt7996_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	int delta;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+-	mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
++	mconf = mconf_dereference_protected(mvif, link_id);
+ 	if (!mconf || !mconf->phy) {
+ 		*dbm = 0;
+ 		goto out;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0153-mtk-mt76-mt7996-update-TX-RX-rates-via-MCU-command.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0153-mtk-mt76-mt7996-update-TX-RX-rates-via-MCU-command.patch
new file mode 100644
index 0000000..343129f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0153-mtk-mt76-mt7996-update-TX-RX-rates-via-MCU-command.patch
@@ -0,0 +1,414 @@
+From 06c0ba03cc9ed39bee42b10a1e22f9aceeb57ecd Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Fri, 7 Jun 2024 10:44:45 +0800
+Subject: [PATCH 153/199] mtk: mt76: mt7996: update TX/RX rates via MCU command
+
+Update TX/RX rates via MCU command to address following issues:
+1. TX rate was originally updated via TXS. However in MLO connection, WCID from TXS may not represent the actually used link.
+2. RX rate was originally updated via RXD. However, there is no RXD when HW path is taken.
+
+Original TX-rate update via TXS is removed.
+Still, RX-rate update via RXD is not removed, because mac80211 requires driver to provide such information for each received frame.
+
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ mt76.h        |   1 +
+ mt7996/mac.c  | 117 ++++++----------------------------------
+ mt7996/main.c |  28 ++++------
+ mt7996/mcu.c  | 146 +++++++++++++++++++++++++++++++++++++++++++++-----
+ 4 files changed, 161 insertions(+), 131 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index f544814e..a0fc8b1a 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -370,6 +370,7 @@ struct mt76_wcid {
+ 	int inactive_count;
+ 
+ 	struct rate_info rate;
++	struct rate_info rx_rate;
+ 	unsigned long ampdu_state;
+ 
+ 	u16 idx;
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 020203ec..484b679b 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1242,15 +1242,12 @@ mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid,
+ 		       int pid, __le32 *txs_data)
+ {
+ 	struct mt76_sta_stats *stats = &wcid->stats;
+-	struct ieee80211_supported_band *sband;
+ 	struct mt76_dev *mdev = &dev->mt76;
+-	struct mt76_phy *mphy;
+ 	struct ieee80211_tx_info *info;
+ 	struct sk_buff_head list;
+-	struct rate_info rate = {};
+ 	struct sk_buff *skb = NULL;
+-	bool cck = false;
+-	u32 txrate, txs, mode, stbc;
++	u32 txrate, txs;
++	u8 mode, bw, mcs, nss;
+ 
+ 	txs = le32_to_cpu(txs_data[0]);
+ 
+@@ -1298,105 +1295,23 @@ mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid,
+ 	}
+ 
+ 	txrate = FIELD_GET(MT_TXS0_TX_RATE, txs);
+-
+-	rate.mcs = FIELD_GET(MT_TX_RATE_IDX, txrate);
+-	rate.nss = FIELD_GET(MT_TX_RATE_NSS, txrate) + 1;
+-	stbc = le32_get_bits(txs_data[3], MT_TXS3_RATE_STBC);
+-
+-	if (stbc && rate.nss > 1)
+-		rate.nss >>= 1;
+-
+-	if (rate.nss - 1 < ARRAY_SIZE(stats->tx_nss))
+-		stats->tx_nss[rate.nss - 1]++;
+-	if (rate.mcs < ARRAY_SIZE(stats->tx_mcs))
+-		stats->tx_mcs[rate.mcs]++;
++	bw = FIELD_GET(MT_TXS0_BW, txs);
+ 
+ 	mode = FIELD_GET(MT_TX_RATE_MODE, txrate);
+-	switch (mode) {
+-	case MT_PHY_TYPE_CCK:
+-		cck = true;
+-		fallthrough;
+-	case MT_PHY_TYPE_OFDM:
+-		mphy = mt76_dev_phy(mdev, wcid->phy_idx);
+-
+-		if (mphy->chandef.chan->band == NL80211_BAND_5GHZ)
+-			sband = &mphy->sband_5g.sband;
+-		else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ)
+-			sband = &mphy->sband_6g.sband;
+-		else
+-			sband = &mphy->sband_2g.sband;
+-
+-		rate.mcs = mt76_get_rate(mphy->dev, sband, rate.mcs, cck);
+-		rate.legacy = sband->bitrates[rate.mcs].bitrate;
+-		break;
+-	case MT_PHY_TYPE_HT:
+-	case MT_PHY_TYPE_HT_GF:
+-		if (rate.mcs > 31)
+-			goto out;
+-
+-		rate.flags = RATE_INFO_FLAGS_MCS;
+-		if (wcid->rate.flags & RATE_INFO_FLAGS_SHORT_GI)
+-			rate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+-		break;
+-	case MT_PHY_TYPE_VHT:
+-		if (rate.mcs > 9)
+-			goto out;
+-
+-		rate.flags = RATE_INFO_FLAGS_VHT_MCS;
+-		if (wcid->rate.flags & RATE_INFO_FLAGS_SHORT_GI)
+-			rate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+-		break;
+-	case MT_PHY_TYPE_HE_SU:
+-	case MT_PHY_TYPE_HE_EXT_SU:
+-	case MT_PHY_TYPE_HE_TB:
+-	case MT_PHY_TYPE_HE_MU:
+-		if (rate.mcs > 11)
+-			goto out;
+-
+-		rate.he_gi = wcid->rate.he_gi;
+-		rate.he_dcm = FIELD_GET(MT_TX_RATE_DCM, txrate);
+-		rate.flags = RATE_INFO_FLAGS_HE_MCS;
+-		break;
+-	case MT_PHY_TYPE_EHT_SU:
+-	case MT_PHY_TYPE_EHT_TRIG:
+-	case MT_PHY_TYPE_EHT_MU:
+-		if (rate.mcs > 13)
+-			goto out;
+-
+-		rate.eht_gi = wcid->rate.eht_gi;
+-		rate.flags = RATE_INFO_FLAGS_EHT_MCS;
+-		break;
+-	default:
+-		goto out;
+-	}
+-
+-	stats->tx_mode[mode]++;
++	mcs = FIELD_GET(MT_TX_RATE_IDX, txrate);
++	nss = FIELD_GET(MT_TX_RATE_NSS, txrate) + 1;
++	if (le32_get_bits(txs_data[3], MT_TXS3_RATE_STBC) && nss > 1)
++		nss >>= 1;
++
++	if (nss - 1 < ARRAY_SIZE(stats->tx_nss))
++		stats->tx_nss[nss - 1]++;
++	if (mcs < ARRAY_SIZE(stats->tx_mcs))
++		stats->tx_mcs[mcs]++;
++	if (mode < ARRAY_SIZE(stats->tx_mode))
++		stats->tx_mode[mode]++;
++	if (bw < ARRAY_SIZE(stats->tx_bw))
++		stats->tx_bw[bw]++;
+ 
+-	switch (FIELD_GET(MT_TXS0_BW, txs)) {
+-	case IEEE80211_STA_RX_BW_320:
+-		rate.bw = RATE_INFO_BW_320;
+-		stats->tx_bw[4]++;
+-		break;
+-	case IEEE80211_STA_RX_BW_160:
+-		rate.bw = RATE_INFO_BW_160;
+-		stats->tx_bw[3]++;
+-		break;
+-	case IEEE80211_STA_RX_BW_80:
+-		rate.bw = RATE_INFO_BW_80;
+-		stats->tx_bw[2]++;
+-		break;
+-	case IEEE80211_STA_RX_BW_40:
+-		rate.bw = RATE_INFO_BW_40;
+-		stats->tx_bw[1]++;
+-		break;
+-	default:
+-		rate.bw = RATE_INFO_BW_20;
+-		stats->tx_bw[0]++;
+-		break;
+-	}
+-	wcid->rate = rate;
+-
+-out:
+ 	if (skb)
+ 		mt76_tx_status_skb_done(mdev, skb, &list);
+ 	mt76_tx_status_unlock(mdev, &list);
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 19a1c7b3..fa60b889 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1765,32 +1765,24 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	struct mt7996_link_sta *mlink;
+-	struct rate_info *txrate;
++	struct rate_info *rate;
+ 
+-	/* TODO: support per-link rate report */
+ 	mutex_lock(&dev->mt76.mutex);
+ 	mlink = mlink_dereference_protected(msta, msta->pri_link);
+ 	if (!mlink)
+ 		goto out;
+ 
+-	txrate = &mlink->wcid.rate;
+-	if (txrate->legacy || txrate->flags) {
+-		if (txrate->legacy) {
+-			sinfo->txrate.legacy = txrate->legacy;
+-		} else {
+-			sinfo->txrate.mcs = txrate->mcs;
+-			sinfo->txrate.nss = txrate->nss;
+-			sinfo->txrate.bw = txrate->bw;
+-			sinfo->txrate.he_gi = txrate->he_gi;
+-			sinfo->txrate.he_dcm = txrate->he_dcm;
+-			sinfo->txrate.he_ru_alloc = txrate->he_ru_alloc;
+-			sinfo->txrate.eht_gi = txrate->eht_gi;
+-		}
+-		sinfo->txrate.flags = txrate->flags;
++	rate = &mlink->wcid.rate;
++	if (rate->legacy || rate->flags) {
++		sinfo->txrate = *rate;
+ 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
+ 	}
+-	sinfo->txrate.flags = txrate->flags;
+-	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
++
++	rate = &mlink->wcid.rx_rate;
++	if (rate->legacy || rate->flags) {
++		sinfo->rxrate = *rate;
++		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
++	}
+ 
+ 	sinfo->tx_failed = mlink->wcid.stats.tx_failed;
+ 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 51f15928..8fd9d450 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -532,42 +532,164 @@ mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb)
+ }
+ 
+ static int
+-mt7996_mcu_update_tx_gi(struct rate_info *rate, struct all_sta_trx_rate *mcu_rate)
++mt7996_mcu_update_rate(struct rate_info *rate, struct ieee80211_supported_band *sband,
++		       u8 mode, u8 bw, u8 mcs, u8 nss, u8 stbc, u8 gi)
+ {
+-	switch (mcu_rate->tx_mode) {
++	struct rate_info tmp_rate = {};
++
++	tmp_rate.mcs = mcs;
++	tmp_rate.nss = (stbc && nss > 1) ? nss / 2 : nss;
++
++	switch (mode) {
+ 	case MT_PHY_TYPE_CCK:
+ 	case MT_PHY_TYPE_OFDM:
++		if (mcs >= sband->n_bitrates)
++			return -EINVAL;
++
++		tmp_rate.legacy = sband->bitrates[mcs].bitrate;
+ 		break;
+ 	case MT_PHY_TYPE_HT:
+ 	case MT_PHY_TYPE_HT_GF:
++		if (mcs > 31)
++			return -EINVAL;
++
++		tmp_rate.flags |= RATE_INFO_FLAGS_MCS;
++		if (gi)
++			tmp_rate.flags |= RATE_INFO_FLAGS_SHORT_GI;
++		break;
+ 	case MT_PHY_TYPE_VHT:
+-		if (mcu_rate->tx_gi)
+-			rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
+-		else
+-			rate->flags &= ~RATE_INFO_FLAGS_SHORT_GI;
++		if (mcs > 9)
++			return -EINVAL;
++
++		tmp_rate.flags |= RATE_INFO_FLAGS_VHT_MCS;
++		if (gi)
++			tmp_rate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ 		break;
+ 	case MT_PHY_TYPE_HE_SU:
+ 	case MT_PHY_TYPE_HE_EXT_SU:
+ 	case MT_PHY_TYPE_HE_TB:
+ 	case MT_PHY_TYPE_HE_MU:
+-		if (mcu_rate->tx_gi > NL80211_RATE_INFO_HE_GI_3_2)
++		tmp_rate.mcs = mcs & GENMASK(3, 0);
++		if (tmp_rate.mcs > 11 || gi > NL80211_RATE_INFO_HE_GI_3_2)
+ 			return -EINVAL;
+-		rate->he_gi = mcu_rate->tx_gi;
++
++		tmp_rate.flags |= RATE_INFO_FLAGS_HE_MCS;
++		tmp_rate.he_gi = gi;
++		tmp_rate.he_dcm = mcs & MT_PRXV_TX_DCM;
+ 		break;
+ 	case MT_PHY_TYPE_EHT_SU:
+ 	case MT_PHY_TYPE_EHT_TRIG:
+ 	case MT_PHY_TYPE_EHT_MU:
+-		if (mcu_rate->tx_gi > NL80211_RATE_INFO_EHT_GI_3_2)
++		tmp_rate.mcs = mcs & GENMASK(3, 0);
++		if (tmp_rate.mcs > 13 || gi > NL80211_RATE_INFO_EHT_GI_3_2)
+ 			return -EINVAL;
+-		rate->eht_gi = mcu_rate->tx_gi;
++
++		tmp_rate.flags |= RATE_INFO_FLAGS_EHT_MCS;
++		tmp_rate.eht_gi = gi;
+ 		break;
+ 	default:
+ 		return -EINVAL;
+ 	}
+ 
++	switch (bw) {
++	case IEEE80211_STA_RX_BW_20:
++		tmp_rate.bw = RATE_INFO_BW_20;
++		break;
++	case IEEE80211_STA_RX_BW_40:
++		tmp_rate.bw = RATE_INFO_BW_40;
++		break;
++	case IEEE80211_STA_RX_BW_80:
++		tmp_rate.bw = RATE_INFO_BW_80;
++		break;
++	case IEEE80211_STA_RX_BW_160:
++		tmp_rate.bw = RATE_INFO_BW_160;
++		break;
++	case IEEE80211_STA_RX_BW_320:
++		tmp_rate.bw = RATE_INFO_BW_320;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	if (mode == MT_PHY_TYPE_HE_EXT_SU && mcs & MT_PRXV_TX_ER_SU_106T) {
++		tmp_rate.bw = RATE_INFO_BW_HE_RU;
++		tmp_rate.he_ru_alloc = NL80211_RATE_INFO_HE_RU_ALLOC_106;
++	}
++	*rate = tmp_rate;
++
+ 	return 0;
+ }
+ 
++static int
++mt7996_mcu_update_trx_rates(struct mt76_wcid *wcid, struct all_sta_trx_rate *mcu_rate)
++{
++	struct mt7996_link_sta *mlink = container_of(wcid, struct mt7996_link_sta, wcid);
++	struct mt76_dev *dev = &mlink->sta->vif->dev->mt76;
++	struct mt76_phy *phy = mt76_dev_phy(dev, wcid->phy_idx);
++	struct ieee80211_supported_band *sband = NULL;
++	bool cck;
++	int ret;
++
++	/* TX rate */
++	cck = false;
++
++	switch (mcu_rate->tx_mode) {
++	case MT_PHY_TYPE_CCK:
++		cck = true;
++		fallthrough;
++	case MT_PHY_TYPE_OFDM:
++		if (phy->chandef.chan->band == NL80211_BAND_2GHZ) {
++			sband = &phy->sband_2g.sband;
++			if (!cck)
++				mcu_rate->tx_mcs += 4;
++		} else if (phy->chandef.chan->band == NL80211_BAND_5GHZ)
++			sband = &phy->sband_5g.sband;
++		else
++			sband = &phy->sband_6g.sband;
++		break;
++	case MT_PHY_TYPE_HT:
++	case MT_PHY_TYPE_HT_GF:
++		mcu_rate->tx_mcs += ((mcu_rate->tx_nss - 1) << 3);
++		break;
++	default:
++		break;
++	}
++
++	ret = mt7996_mcu_update_rate(&wcid->rate, sband, mcu_rate->tx_mode,
++				     mcu_rate->tx_bw, mcu_rate->tx_mcs,
++				     mcu_rate->tx_nss, mcu_rate->tx_stbc,
++				     mcu_rate->tx_gi);
++	if (ret)
++		return ret;
++
++	/* RX rate */
++	cck = false;
++
++	switch (mcu_rate->rx_mode) {
++	case MT_PHY_TYPE_CCK:
++		cck = true;
++		fallthrough;
++	case MT_PHY_TYPE_OFDM:
++		if (phy->chandef.chan->band == NL80211_BAND_2GHZ)
++			sband = &phy->sband_2g.sband;
++		else if (phy->chandef.chan->band == NL80211_BAND_5GHZ)
++			sband = &phy->sband_5g.sband;
++		else
++			sband = &phy->sband_6g.sband;
++
++		mcu_rate->rx_rate = mt76_get_rate(dev, sband, mcu_rate->rx_rate, cck);
++		break;
++	default:
++		break;
++	}
++
++	ret = mt7996_mcu_update_rate(&wcid->rx_rate, sband, mcu_rate->rx_mode,
++				     mcu_rate->rx_bw, mcu_rate->rx_rate,
++				     mcu_rate->rx_nsts + 1, mcu_rate->rx_stbc,
++				     mcu_rate->rx_gi);
++	return ret;
++}
++
+ static inline void __mt7996_stat_to_netdev(struct mt76_phy *mphy,
+ 					   struct mt76_wcid *wcid,
+ 					   u32 tx_bytes, u32 rx_bytes,
+@@ -618,8 +740,8 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 			if (!wcid)
+ 				break;
+ 
+-			if (mt7996_mcu_update_tx_gi(&wcid->rate, &res->rate[i]))
+-				dev_err(dev->mt76.dev, "Failed to update TX GI\n");
++			if (mt7996_mcu_update_trx_rates(wcid, &res->rate[i]))
++				dev_err(dev->mt76.dev, "Failed to update TX/RX rates.\n");
+ 			break;
+ 		case UNI_ALL_STA_TXRX_ADM_STAT:
+ 			wlan_idx = le16_to_cpu(res->adm_stat[i].wlan_idx);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0154-mtk-mt76-mt7996-add-link-information-when-dump-stati.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0154-mtk-mt76-mt7996-add-link-information-when-dump-stati.patch
new file mode 100644
index 0000000..057f0c4
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0154-mtk-mt76-mt7996-add-link-information-when-dump-stati.patch
@@ -0,0 +1,119 @@
+From 4eda4a37c2d5fee71c5f7b4d4a3b5baa57252c14 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Tue, 11 Jun 2024 18:09:57 +0800
+Subject: [PATCH 154/199] mtk: mt76: mt7996: add link information when dump
+ station
+
+Report following per-link information to mac80211:
+- RSSI
+- RX rate
+- TX/RX byte counts
+- TX MPDU failed/retried counts
+- TX/RX airtime
+- per-link per-antenna average data-frame RSSI to mac80211.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ mt7996/main.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 78 insertions(+)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index fa60b889..8c162987 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1813,6 +1813,83 @@ out:
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
++static void mt7996_sta_link_statistics(struct ieee80211_hw *hw,
++				       struct ieee80211_vif *vif,
++				       struct ieee80211_sta *sta,
++				       unsigned int link_id,
++				       struct station_link_info *linfo)
++{
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_link_sta *mlink;
++	struct mt7996_bss_conf *mconf;
++	struct mt76_sta_stats *stats;
++	int i;
++
++	mutex_lock(&dev->mt76.mutex);
++	mlink = mlink_dereference_protected(msta, link_id);
++	if (!mlink)
++		goto out;
++	stats = &mlink->wcid.stats;
++
++	mconf = mconf_dereference_protected(mvif, link_id);
++	if (!mconf)
++		goto out;
++
++	linfo->signal = (s8)mlink->signal;
++	linfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
++
++	linfo->chains = mconf->phy->mt76->antenna_mask;
++	memcpy(linfo->chain_signal, mlink->chain_signal, IEEE80211_MAX_CHAINS);
++	linfo->filled |= BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL);
++
++	linfo->signal_avg = -(s8)ewma_avg_signal_read(&mlink->signal_avg);
++	linfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);
++
++	for (i = 0; i < IEEE80211_MAX_CHAINS; ++i)
++		linfo->chain_signal_avg[i] = -(s8)ewma_avg_signal_read(mlink->chain_signal_avg + i);
++	linfo->filled |= BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG);
++
++	linfo->ack_signal = (s8)mlink->ack_signal;
++	linfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL);
++
++	linfo->avg_ack_signal = -(s8)ewma_avg_signal_read(&mlink->avg_ack_signal);
++	linfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG);
++
++	linfo->txrate = mlink->wcid.rate;
++	linfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
++
++	linfo->rxrate = mlink->wcid.rx_rate;
++	linfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
++
++	linfo->tx_bytes = stats->tx_bytes;
++	linfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES64);
++
++	linfo->rx_bytes = stats->rx_bytes;
++	linfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES64);
++
++	linfo->tx_failed = stats->tx_failed;
++	linfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
++
++	linfo->tx_retries = stats->tx_retries;
++	linfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES);
++
++	linfo->rx_mpdu_count = stats->rx_mpdus;
++	linfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_MPDUS);
++
++	linfo->fcs_err_count = stats->rx_fcs_err;
++	linfo->filled |= BIT_ULL(NL80211_STA_INFO_FCS_ERROR_COUNT);
++
++	linfo->tx_duration = stats->tx_airtime;
++	linfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_DURATION);
++
++	linfo->rx_duration = stats->rx_airtime;
++	linfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DURATION);
++out:
++	mutex_unlock(&dev->mt76.mutex);
++}
++
+ static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta)
+ {
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+@@ -2932,6 +3009,7 @@ const struct ieee80211_ops mt7996_ops = {
+ 	.set_bitrate_mask = mt7996_set_bitrate_mask,
+ 	.set_coverage_class = mt7996_set_coverage_class,
+ 	.sta_statistics = mt7996_sta_statistics,
++	.sta_link_statistics = mt7996_sta_link_statistics,
+ 	.sta_set_4addr = mt7996_sta_set_4addr,
+ 	.sta_set_decap_offload = mt7996_sta_set_decap_offload,
+ 	.add_twt_setup = mt7996_mac_add_twt_setup,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0155-mtk-mt76-mt7996-add-per-link-RX-MPDU-statistics.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0155-mtk-mt76-mt7996-add-per-link-RX-MPDU-statistics.patch
new file mode 100644
index 0000000..e35f69e
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0155-mtk-mt76-mt7996-add-per-link-RX-MPDU-statistics.patch
@@ -0,0 +1,96 @@
+From c7dcf0304c6cd1d73b1caf2ce0b35f7913d2b69e Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Tue, 23 Jul 2024 16:37:57 +0800
+Subject: [PATCH 155/199] mtk: mt76: mt7996: add per-link RX MPDU statistics
+
+Add per-link RX MPDU total/failed counts.
+
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ mt76.h            |  2 ++
+ mt76_connac_mcu.h |  2 ++
+ mt7996/mac.c      |  1 +
+ mt7996/mcu.c      | 10 ++++++++++
+ mt7996/mcu.h      |  7 +++++++
+ 5 files changed, 22 insertions(+)
+
+diff --git a/mt76.h b/mt76.h
+index a0fc8b1a..f67f0658 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -336,6 +336,8 @@ struct mt76_sta_stats {
+ 	/* WED RX */
+ 	u64 rx_bytes;
+ 	u32 rx_packets;
++	u32 rx_mpdus;
++	u32 rx_fcs_err;
+ 	u32 rx_errors;
+ 	u32 rx_drops;
+ 	u64 rx_airtime;
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 0244b5aa..8de91f62 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1421,6 +1421,8 @@ enum UNI_ALL_STA_INFO_TAG {
+ 	UNI_ALL_STA_DATA_TX_RETRY_COUNT,
+ 	UNI_ALL_STA_GI_MODE,
+ 	UNI_ALL_STA_TXRX_MSDU_COUNT,
++	UNI_ALL_STA_TXOP_ACCESS_DELAY,
++	UNI_ALL_STA_RX_MPDU_COUNT,
+ 	UNI_ALL_STA_MAX_NUM
+ };
+ 
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 484b679b..a0406700 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2408,6 +2408,7 @@ void mt7996_mac_work(struct work_struct *work)
+ 					mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_ADM_STAT);
+ 					mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_MSDU_COUNT);
+ 				// }
++				mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_RX_MPDU_COUNT);
+ 
+ 				if (mt7996_mcu_wa_cmd(phy->dev, MCU_WA_PARAM_CMD(QUERY), MCU_WA_PARAM_BSS_ACQ_PKT_CNT,
+ 				                      BSS_ACQ_PKT_CNT_BSS_BITMAP_ALL | BSS_ACQ_PKT_CNT_READ_CLR, 0))
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 8fd9d450..be6b985f 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -803,6 +803,16 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 				                               tx_airtime, rx_airtime);
+ 			}
+ 			break;
++		case UNI_ALL_STA_RX_MPDU_COUNT:
++			wlan_idx = le16_to_cpu(res->rx_mpdu_cnt[i].wlan_idx);
++			wcid = rcu_dereference(dev->mt76.wcid[wlan_idx]);
++			if (!wcid)
++				break;
++
++			wcid->stats.rx_mpdus += le32_to_cpu(res->rx_mpdu_cnt[i].total);
++			wcid->stats.rx_fcs_err += le32_to_cpu(res->rx_mpdu_cnt[i].total) -
++						  le32_to_cpu(res->rx_mpdu_cnt[i].success);
++			break;
+ 		default:
+ 			break;
+ 		}
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 389aab63..737f426d 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -268,6 +268,13 @@ struct mt7996_mcu_all_sta_info_event {
+ 			__le32 tx[IEEE80211_NUM_ACS];
+ 			__le32 rx[IEEE80211_NUM_ACS];
+ 		} __packed, airtime);
++
++		DECLARE_FLEX_ARRAY(struct {
++			__le16 wlan_idx;
++			u8 rsv[2];
++			__le32 total;
++			__le32 success;
++		} __packed, rx_mpdu_cnt);
+ 	} __packed;
+ } __packed;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0156-mtk-mt76-mt7996-support-link_id-for-mt7996_set_bitra.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0156-mtk-mt76-mt7996-support-link_id-for-mt7996_set_bitra.patch
new file mode 100644
index 0000000..476fb7b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0156-mtk-mt76-mt7996-support-link_id-for-mt7996_set_bitra.patch
@@ -0,0 +1,120 @@
+From c310822a0a28c43c4bf4234e76d82e4d1fb47a96 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Fri, 7 Jun 2024 13:23:55 +0800
+Subject: [PATCH 156/199] mtk: mt76: mt7996: support link_id for
+ mt7996_set_bitrate_mask
+
+Add support new argument link_id for set_bitrate_mask within
+ieee80211_ops.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt7996/main.c   | 34 ++++++++++++++++++++++++++--------
+ mt7996/mt7996.h |  5 +++++
+ 2 files changed, 31 insertions(+), 8 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 8c162987..71a42199 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1895,17 +1895,21 @@ static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta)
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	struct mt7996_link_sta *mlink;
+ 	struct mt7996_dev *dev = msta->vif->dev;
+-	u32 *changed = data;
++	struct mt7996_sta_rc_work_data *wd = data;
+ 
+ 	rcu_read_lock();
+-	mlink = rcu_dereference(msta->link[msta->pri_link]);
++	mlink = rcu_dereference(msta->link[wd->link_id]);
++
++	if (!mlink)
++		goto unlock;
+ 
+ 	spin_lock_bh(&dev->mt76.sta_poll_lock);
+-	mlink->changed |= *changed;
++	mlink->changed |= wd->changed;
+ 	if (list_empty(&mlink->rc_list))
+ 		list_add_tail(&mlink->rc_list, &dev->sta_rc_list);
+ 	spin_unlock_bh(&dev->mt76.sta_poll_lock);
+ 
++unlock:
+ 	rcu_read_unlock();
+ }
+ 
+@@ -1917,6 +1921,10 @@ static void mt7996_sta_rc_update(struct ieee80211_hw *hw,
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mt7996_dev *dev = phy->dev;
++	struct mt7996_sta_rc_work_data data = {
++		.link_id = msta->pri_link,
++		.changed = changed,
++	};
+ 
+ 	if (!msta->vif) {
+ 		dev_warn(dev->mt76.dev, "Un-initialized STA %pM wcid %d in rc_work\n",
+@@ -1924,22 +1932,32 @@ static void mt7996_sta_rc_update(struct ieee80211_hw *hw,
+ 		return;
+ 	}
+ 
+-	mt7996_sta_rc_work(&changed, sta);
++	mt7996_sta_rc_work(&data, sta);
+ 	ieee80211_queue_work(hw, &dev->rc_work);
+ }
+ 
+ static int
+ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+-			const struct cfg80211_bitrate_mask *mask)
++			const struct cfg80211_bitrate_mask *mask,
++			unsigned int link_id)
+ {
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mt7996_dev *dev = phy->dev;
+ 	struct mt7996_bss_conf *mconf;
+-	u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED;
++	struct mt7996_sta_rc_work_data data = {
++		.link_id = link_id,
++		.changed = IEEE80211_RC_SUPP_RATES_CHANGED,
++	};
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+-	mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
++	mconf = mconf_dereference_protected(mvif, link_id);
++
++	if (!mconf) {
++		mutex_unlock(&dev->mt76.mutex);
++		return -EINVAL;
++	}
++
+ 	mconf->bitrate_mask = *mask;
+ 	mutex_unlock(&dev->mt76.mutex);
+ 
+@@ -1951,7 +1969,7 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	 * - multiple rates: if it's not in range format i.e 0-{7,8,9} for VHT
+ 	 * then multiple MCS setting (MCS 4,5,6) is not supported.
+ 	 */
+-	ieee80211_iterate_stations_atomic(hw, mt7996_sta_rc_work, &changed);
++	ieee80211_iterate_stations_atomic(hw, mt7996_sta_rc_work, &data);
+ 	ieee80211_queue_work(hw, &dev->rc_work);
+ 
+ 	return 0;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 302ac7bf..49789441 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -444,6 +444,11 @@ struct mt7996_wed_rro_session_id {
+ 	u16 id;
+ };
+ 
++struct mt7996_sta_rc_work_data {
++	unsigned int link_id;
++	u32 changed;
++};
++
+ #ifdef CONFIG_MTK_VENDOR
+ #define MT7996_AIR_MONITOR_MAX_ENTRY	16
+ #define MT7996_AIR_MONITOR_MAX_GROUP	(MT7996_AIR_MONITOR_MAX_ENTRY >> 1)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0157-mtk-mt76-mt7996-add-per-radio-antenna-config.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0157-mtk-mt76-mt7996-add-per-radio-antenna-config.patch
new file mode 100644
index 0000000..5e3b87a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0157-mtk-mt76-mt7996-add-per-radio-antenna-config.patch
@@ -0,0 +1,254 @@
+From 467122289f7c31285d68ae819f22a8bf7b2272ae Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 11 Jun 2024 17:04:22 +0800
+Subject: [PATCH 157/199] mtk: mt76: mt7996: add per-radio antenna config
+
+Add per-radio antenna config
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mac80211.c      | 19 ++++++++++++++++---
+ mt76.h          |  3 ++-
+ mt7996/eeprom.c |  3 +++
+ mt7996/init.c   | 26 ++++++++++++++------------
+ mt7996/main.c   | 15 ++++++---------
+ mt7996/mcu.c    |  3 ++-
+ mt7996/mt7996.h |  7 ++++---
+ 7 files changed, 47 insertions(+), 29 deletions(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index 589c37af..10267019 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -431,8 +431,8 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw)
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS);
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AQL);
+ 
+-	wiphy->available_antennas_tx = phy->antenna_mask;
+-	wiphy->available_antennas_rx = phy->antenna_mask;
++	wiphy->available_antennas_tx[phy->cur_band] = phy->antenna_mask;
++	wiphy->available_antennas_rx[phy->cur_band] = phy->antenna_mask;
+ 
+ 	wiphy->sar_capa = &mt76_sar_capa;
+ 	phy->frp = devm_kcalloc(dev->dev, wiphy->sar_capa->num_freq_ranges,
+@@ -1710,12 +1710,25 @@ void mt76_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ }
+ EXPORT_SYMBOL_GPL(mt76_sw_scan_complete);
+ 
+-int mt76_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
++int mt76_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant, int band)
+ {
+ 	struct mt76_phy *phy = hw->priv;
+ 	struct mt76_dev *dev = phy->dev;
++	int band_idx;
+ 
+ 	mutex_lock(&dev->mutex);
++	phy = NULL;
++	for (band_idx = 0; band_idx < __MT_MAX_BAND; band_idx++)
++		if (dev->phys[band_idx] && dev->phys[band_idx]->cur_band == band) {
++			phy = dev->phys[band_idx];
++			break;
++		}
++
++	if (!phy) {
++		mutex_unlock(&dev->mutex);
++		return -EINVAL;
++	}
++
+ 	*tx_ant = phy->antenna_mask;
+ 	*rx_ant = phy->antenna_mask;
+ 	mutex_unlock(&dev->mutex);
+diff --git a/mt76.h b/mt76.h
+index f67f0658..728740ef 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -862,6 +862,7 @@ struct mt76_phy {
+ 	struct mt76_sband sband_2g;
+ 	struct mt76_sband sband_5g;
+ 	struct mt76_sband sband_6g;
++	enum nl80211_band cur_band;
+ 
+ 	u8 macaddr[ETH_ALEN];
+ 
+@@ -1540,7 +1541,7 @@ int mt76_get_sar_power(struct mt76_phy *phy,
+ void mt76_csa_check(struct mt76_dev *dev);
+ void mt76_csa_finish(struct mt76_dev *dev);
+ 
+-int mt76_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant);
++int mt76_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant, int band);
+ int mt76_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set);
+ void mt76_insert_ccmp_hdr(struct sk_buff *skb, u8 key_id);
+ int mt76_get_rate(struct mt76_dev *dev,
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index c4714982..e8e1d85a 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -378,12 +378,15 @@ static int mt7996_eeprom_parse_band_config(struct mt7996_phy *phy)
+ 	switch (val) {
+ 	case MT_EE_BAND_SEL_2GHZ:
+ 		phy->mt76->cap.has_2ghz = true;
++		phy->mt76->cur_band = NL80211_BAND_2GHZ;
+ 		break;
+ 	case MT_EE_BAND_SEL_5GHZ:
+ 		phy->mt76->cap.has_5ghz = true;
++		phy->mt76->cur_band = NL80211_BAND_5GHZ;
+ 		break;
+ 	case MT_EE_BAND_SEL_6GHZ:
+ 		phy->mt76->cap.has_6ghz = true;
++		phy->mt76->cur_band = NL80211_BAND_6GHZ;
+ 		break;
+ 	default:
+ 		ret = -EINVAL;
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 0ee2acfb..55eb32cb 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -392,8 +392,10 @@ static void
+ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ {
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct mt76_phy *mphy = phy->mt76;
+ 	struct mt76_dev *mdev = &phy->dev->mt76;
+ 	struct wiphy *wiphy = hw->wiphy;
++	struct wiphy *single_wiphy = mdev->phy.hw->wiphy;
+ 	u16 max_subframes = phy->dev->has_eht ? IEEE80211_MAX_AMPDU_BUF_EHT :
+ 						IEEE80211_MAX_AMPDU_BUF_HE;
+ 
+@@ -455,25 +457,25 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ 
+ 	hw->max_tx_fragments = 4;
+ 
+-	if (phy->mt76->cap.has_2ghz) {
+-		phy->mt76->sband_2g.sband.ht_cap.cap |=
++	if (mphy->cap.has_2ghz) {
++		mphy->sband_2g.sband.ht_cap.cap |=
+ 			IEEE80211_HT_CAP_LDPC_CODING |
+ 			IEEE80211_HT_CAP_MAX_AMSDU;
+-		phy->mt76->sband_2g.sband.ht_cap.ampdu_density =
++		mphy->sband_2g.sband.ht_cap.ampdu_density =
+ 			IEEE80211_HT_MPDU_DENSITY_2;
+ 	}
+ 
+-	if (phy->mt76->cap.has_5ghz) {
+-		phy->mt76->sband_5g.sband.ht_cap.cap |=
++	if (mphy->cap.has_5ghz) {
++		mphy->sband_5g.sband.ht_cap.cap |=
+ 			IEEE80211_HT_CAP_LDPC_CODING |
+ 			IEEE80211_HT_CAP_MAX_AMSDU;
+ 
+-		phy->mt76->sband_5g.sband.vht_cap.cap |=
++		mphy->sband_5g.sband.vht_cap.cap |=
+ 			IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
+ 			IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK |
+ 			IEEE80211_VHT_CAP_SHORT_GI_160 |
+ 			IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+-		phy->mt76->sband_5g.sband.ht_cap.ampdu_density =
++		mphy->sband_5g.sband.ht_cap.ampdu_density =
+ 			IEEE80211_HT_MPDU_DENSITY_1;
+ 
+ 		ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);
+@@ -481,17 +483,17 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ 
+ 	/* init led callbacks */
+ 	if (IS_ENABLED(CONFIG_MT76_LEDS)) {
+-		phy->mt76->leds.cdev.brightness_set = mt7996_led_set_brightness;
+-		phy->mt76->leds.cdev.blink_set = mt7996_led_set_blink;
++		mphy->leds.cdev.brightness_set = mt7996_led_set_brightness;
++		mphy->leds.cdev.blink_set = mt7996_led_set_blink;
+ 	}
+ 
+-	mt76_set_stream_caps(phy->mt76, true);
++	mt76_set_stream_caps(mphy, true);
+ 	mt7996_set_stream_vht_txbf_caps(phy);
+ 	mt7996_set_stream_he_eht_caps(phy);
+ 	mt7996_init_txpower(phy);
+ 
+-	wiphy->available_antennas_rx = phy->mt76->antenna_mask;
+-	wiphy->available_antennas_tx = phy->mt76->antenna_mask;
++	single_wiphy->available_antennas_rx[mphy->cur_band] = mphy->antenna_mask;
++	single_wiphy->available_antennas_tx[mphy->cur_band] = mphy->antenna_mask;
+ 
+ 	wiphy->max_scan_ssids = 4;
+ 	wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 71a42199..b59f72d0 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1701,14 +1701,11 @@ mt7996_set_coverage_class(struct ieee80211_hw *hw, s16 coverage_class)
+ }
+ 
+ static int
+-mt7996_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
++mt7996_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant, int band)
+ {
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+-	int band, max_nss = hweight8(hw->wiphy->available_antennas_tx);
+-
+-	/* TODO: set antenna based on capability of each band. */
+-	dev_warn(dev->mt76.dev, "%s: temporarily not supported.\n", __func__);
+-	return 0;
++	int max_nss = hweight8(hw->wiphy->available_antennas_tx[band]);
++	enum nl80211_band bandid;
+ 
+ 	/* only allow settings from hw0 */
+ 	if (hw != dev->phy.mt76->hw)
+@@ -1722,14 +1719,14 @@ mt7996_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+-	for (band = 0; band < NUM_NL80211_BANDS; band++) {
++	for (bandid = 0; bandid < NUM_NL80211_BANDS; bandid++) {
+ 		struct mt7996_phy *phy;
+ 		u8 band_idx, shift;
+ 
+-		if (!hw->wiphy->bands[band])
++		if (band != bandid || !hw->wiphy->bands[bandid])
+ 			continue;
+ 
+-		phy = mt7996_band_phy(hw, band);
++		phy = mt7996_band_phy(hw, bandid);
+ 		if (!phy)
+ 			continue;
+ 
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index be6b985f..6af45467 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2430,6 +2430,7 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ #define EBF_MODE	BIT(0)
+ #define IBF_MODE	BIT(1)
+ 	struct mt7996_phy *phy = mconf->phy;
++	struct wiphy *wiphy = phy->mt76->hw->wiphy;
+ 	int tx_ant = hweight16(phy->mt76->chainmask) - 1;
+ 	struct sta_rec_bf *bf;
+ 	struct tlv *tlv;
+@@ -2468,7 +2469,7 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 
+ 	bf->bf_cap = ebf ? EBF_MODE : (dev->ibf ? IBF_MODE : 0);
+ 	if (is_mt7992(&dev->mt76) &&
+-	    tx_ant == hweight8(phy->mt76->hw->wiphy->available_antennas_tx))
++	    tx_ant == hweight8(wiphy->available_antennas_tx[phy->mt76->cur_band]))
+ 		bf->bf_cap |= IBF_MODE;
+ 	bf->bw = link_sta->bandwidth;
+ 	bf->ibf_dbw = link_sta->bandwidth;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 49789441..05bdc437 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1173,9 +1173,10 @@ void mt7996_memcpy_fromio(struct mt7996_dev *dev, void *buf, u32 offset,
+ 
+ static inline u16 mt7996_rx_chainmask(struct mt7996_phy *phy)
+ {
+-	int max_nss = hweight8(phy->mt76->hw->wiphy->available_antennas_tx);
+-	int cur_nss = hweight8(phy->mt76->antenna_mask);
+-	u16 tx_chainmask = phy->mt76->chainmask;
++	struct mt76_phy *mphy = phy->mt76;
++	int cur_nss = hweight8(mphy->antenna_mask);
++	int max_nss = hweight8(mphy->hw->wiphy->available_antennas_rx[mphy->cur_band]);
++	u16 tx_chainmask = mphy->chainmask;
+ 
+ 	if (cur_nss != max_nss)
+ 		return tx_chainmask;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0158-mtk-mt76-mt7996-rework-debug-prints.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0158-mtk-mt76-mt7996-rework-debug-prints.patch
new file mode 100644
index 0000000..8ed1049
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0158-mtk-mt76-mt7996-rework-debug-prints.patch
@@ -0,0 +1,375 @@
+From 4a9819d5ac8cef5b85c089698467053713619f86 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 19 Jun 2024 12:04:46 +0800
+Subject: [PATCH 158/199] mtk: mt76: mt7996: rework debug prints
+
+Trim debug messages, and move some of them (especially mcu cmd) into
+tracing log.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt76_connac_mcu.c |  1 -
+ mt7996/mac.c      |  3 ---
+ mt7996/main.c     | 66 ++++++++++++++++++++++++++++-------------------
+ mt7996/mcu.c      | 36 +++++++++++++-------------
+ mt7996/mt7996.h   | 10 ++++++-
+ 5 files changed, 67 insertions(+), 49 deletions(-)
+
+diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
+index 8d1036e1..ab7cf4a6 100644
+--- a/mt76_connac_mcu.c
++++ b/mt76_connac_mcu.c
+@@ -432,7 +432,6 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+ 	}
+ 
+ 	memcpy(basic->peer_addr, link_sta->addr, ETH_ALEN);
+-	pr_info("%s: link %u addr [%pM]\n", __func__, link_sta->link_id, basic->peer_addr);
+ 	basic->qos = link_sta->sta->wme;
+ }
+ EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_basic_tlv);
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index a0406700..960b02dd 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2930,9 +2930,6 @@ void mt7996_scan_work(struct work_struct *work)
+ 		return;
+ 	}
+ 
+-	wiphy_info(hw->wiphy, "hw scan %d MHz\n",
+-		   req->channels[phy->scan_chan_idx]->center_freq);
+-
+ 	phy->scan_chan = req->channels[phy->scan_chan_idx++];
+ 
+ 	if (!req->n_ssids ||
+diff --git a/mt7996/main.c b/mt7996/main.c
+index b59f72d0..cff2c527 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -297,6 +297,9 @@ static void mt7996_remove_bss_conf(struct ieee80211_vif *vif,
+ 	if (!mlink || !mconf)
+ 		return;
+ 
++	mt76_vif_dbg(vif, "band=%u, bss_idx=%u, link_id=%u, wcid=%u\n",
++		     mconf->phy->mt76->band_idx, mconf->mt76.idx, mconf->link_id, mlink->wcid.idx);
++
+ 	phy = mconf->phy;
+ 	dev = phy->dev;
+ 	mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, false, false);
+@@ -448,8 +451,8 @@ static int mt7996_add_bss_conf(struct mt7996_phy *phy,
+ 	rcu_assign_pointer(mvif->link[link_id], mconf);
+ 	rcu_assign_pointer(mvif->sta.link[link_id], mlink);
+ 
+-	mlo_dbg(phy, "bss_idx=%u, link_id=%u, wcid=%u\n",
+-		mconf->mt76.idx, mconf->link_id, mlink->wcid.idx);
++	mt76_vif_dbg(vif, "band=%u, bss_idx=%u, link_id=%u, wcid=%u\n",
++		     phy->mt76->band_idx, mconf->mt76.idx, mconf->link_id, mlink->wcid.idx);
+ 
+ 	return 0;
+ error:
+@@ -636,10 +639,11 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ 			add = vif->valid_links ?: BIT(0);
+ 	}
+ 
+-	mlo_dbg(mt7996_hw_phy(hw), "cipher = 0x%x, icv_len = %u, iv_len = %u, hw_key_idx = %u, keyidx = %d, flags = 0x%x, link_id = %d, keylen = %u\n",
+-		     key->cipher, key->icv_len, key->iv_len, key->hw_key_idx, key->keyidx, key->flags, key->link_id, key->keylen);
+-	// print_hex_dump(KERN_INFO , "", DUMP_PREFIX_OFFSET, 16, 1, key->key, key->keylen, false);
+-	mlo_dbg(mt7996_hw_phy(hw), "add=%lx, valid_links=%x, active_links=%x\n", add, vif->valid_links, vif->active_links);
++	if (sta)
++		mt76_trace(vif, "keyidx=%d, link_bitmap=0x%lx (STA %pM)\n",
++			   key->keyidx, add, sta->addr);
++	else
++		mt76_trace(vif, "keyidx=%d, link_bitmap=0x%lx\n", key->keyidx, add);
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+@@ -1216,7 +1220,8 @@ static int mt7996_add_link_sta(struct mt7996_dev *dev,
+ 		rcu_assign_pointer(dev->mt76.wcid[idx], &mlink->wcid);
+ 		mt76_wcid_init(&mlink->wcid);
+ 
+-		mlo_dbg(mconf->phy, "wcid=%u, link_id=%u, link_addr=%pM, pri_link=%u, sec_link=%u\n", mlink->wcid.idx, link_id, link_sta->addr, msta->pri_link, msta->sec_link);
++		mt76_vif_dbg(conf->vif, "STA %pM, wcid=%u, link_id=%u (%pM), pri_link=%u, sec_link=%u\n",
++		     sta->addr, mlink->wcid.idx, link_id, link_sta->addr, msta->pri_link, msta->sec_link);
+ 	}
+ 
+ 	if (!assoc)
+@@ -1255,7 +1260,7 @@ mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 	if (!ieee80211_vif_is_mld(vif) || rem == sta->valid_links)
+ 		cancel_delayed_work(&mvif->beacon_mon_work);
+ 
+-	mlo_dbg(mt7996_hw_phy(mvif->hw), "rem=%lu\n", rem);
++	mt76_vif_dbg(vif, "removed_links=0x%lx\n", rem);
+ 	for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
+ 		struct mt7996_bss_conf *mconf =
+ 			mconf_dereference_protected(mvif, link_id);
+@@ -1282,7 +1287,7 @@ mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 	unsigned int link_id;
+ 	int i, ret;
+ 
+-	mlo_dbg(mt7996_hw_phy(mvif->hw), "add=%lu, assoc=%d\n", add, assoc);
++	mt76_vif_dbg(vif, "added_links=0x%lx, assoc=%d\n", add, assoc);
+ 	for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
+ 		struct mt7996_bss_conf *mconf =
+ 			mconf_dereference_protected(mvif, link_id);
+@@ -1291,6 +1296,9 @@ mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 		struct ieee80211_link_sta *link_sta =
+ 			link_sta_dereference_protected(sta, link_id);
+ 
++		if (!mconf || !conf || !link_sta)
++			continue;
++
+ 		ret = mt7996_add_link_sta(dev, conf, mconf, link_sta, assoc);
+ 		if (ret)
+ 			goto error;
+@@ -1457,13 +1465,13 @@ static void mt7996_tx(struct ieee80211_hw *hw,
+ 					rcu_dereference(sta->link[link_id]);
+ 
+ 				if (!link_sta) {
+-					mlo_dbg(mt7996_hw_phy(mvif->hw), "request TX on invalid link_id=%u, use primary link (id=%u) instead.\n",
+-						      link_id, msta->pri_link);
++					mt76_vif_dbg(vif, "request TX on invalid link_id=%u, use primary link (id=%u) instead.\n",
++						     link_id, msta->pri_link);
+ 					link_id = msta->pri_link;
+ 					link_sta = rcu_dereference(sta->link[link_id]);
+ 
+ 					if (!link_sta) {
+-						mlo_dbg(mt7996_hw_phy(mvif->hw), "primary link became invalid, give up the TX\n");
++						mt76_vif_dbg(vif, "primary link became invalid, give up the TX\n");
+ 						goto unlock;
+ 					}
+ 				}
+@@ -2495,6 +2503,7 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	struct mt7996_bss_conf *mconf;
+ 	int ret;
+ 
++	mt76_vif_dbg(vif, "trigger scan on mt76 band %u\n", phy->mt76->band_idx);
+ 	mutex_lock(&phy->dev->mt76.mutex);
+ 	if (WARN_ON(phy->scan_req || phy->scan_chan)) {
+ 		mutex_unlock(&phy->dev->mt76.mutex);
+@@ -2624,7 +2633,7 @@ mt7996_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
+ 	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
+ 	int ret;
+ 
+-	wiphy_info(hw->wiphy, "%s: add %u\n", __func__, conf->def.chan->hw_value);
++	mt76_dbg(hw, "add %u on mt76 band %d\n", conf->def.chan->hw_value, phy->mt76->band_idx);
+ 	mutex_lock(&phy->dev->mt76.mutex);
+ 
+ 	if (ctx->assigned) {
+@@ -2663,7 +2672,7 @@ mt7996_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *co
+ 	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
+ 	struct mt7996_phy *phy = ctx->phy;
+ 
+-	wiphy_info(hw->wiphy, "%s: remove %u\n", __func__, conf->def.chan->hw_value);
++	mt76_dbg(hw, "remove %u\n", conf->def.chan->hw_value);
+ 	cancel_delayed_work_sync(&phy->scan_work);
+ 	cancel_delayed_work_sync(&phy->mt76->mac_work);
+ 
+@@ -2683,13 +2692,14 @@ mt7996_change_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *co
+ 	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
+ 	struct mt7996_phy *phy = ctx->phy;
+ 
+-	wiphy_info(hw->wiphy, "%s: change %u, 0x%x\n", __func__, conf->def.chan->hw_value, changed);
+ 	if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH ||
+ 	    changed & IEEE80211_CHANCTX_CHANGE_RADAR) {
+ 		ctx->chandef = conf->def;
+ 		phy->mt76->radar_enabled = conf->radar_enabled;
+ 
+ 		mt7996_set_channel(phy, &ctx->chandef);
++
++		mt76_dbg(hw, "change to %u, 0x%x\n", conf->def.chan->hw_value, changed);
+ 	}
+ }
+ 
+@@ -2705,9 +2715,8 @@ mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	u8 link_id = link_conf->link_id;
+ 	int ret;
+ 
+-	wiphy_info(hw->wiphy, "Assign VIF (addr: %pM, type: %d, link_id: %d) to channel context: %d MHz\n",
+-		    vif->addr, vif->type, link_conf->link_id,
+-		    conf->def.chan->center_freq);
++	mt76_vif_dbg(vif, "assign link_id %u to %d MHz\n", link_conf->link_id,
++		     conf->def.chan->center_freq);
+ 
+ 	mutex_lock(&phy->dev->mt76.mutex);
+ 
+@@ -2747,9 +2756,8 @@ mt7996_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
+ 	struct mt7996_phy *phy = ctx->phy;
+ 
+-	wiphy_info(hw->wiphy, "Remove VIF (addr: %pM, type: %d, link_id: %d) from channel context: %d MHz\n",
+-		   vif->addr, vif->type, link_conf->link_id,
+-		   conf->def.chan->center_freq);
++	mt76_vif_dbg(vif, "remove link %u from %d MHz\n",
++		     link_conf->link_id, conf->def.chan->center_freq);
+ 	cancel_delayed_work_sync(&phy->scan_work);
+ 
+ 	mutex_lock(&phy->dev->mt76.mutex);
+@@ -2778,9 +2786,15 @@ mt7996_switch_vif_chanctx(struct ieee80211_hw *hw,
+ 		if (vifs[i].old_ctx == vifs[i].new_ctx)
+ 			continue;
+ 
+-		wiphy_info(hw->wiphy, "%s: old=%d, new=%d\n",
+-			   __func__, vifs[i].old_ctx->def.chan->hw_value,
+-			   vifs[i].new_ctx->def.chan->hw_value);
++		mt76_vif_dbg(vifs[i].vif,
++			     "chan=%d->%d, width=%d->%d, punct_bitmap=0x%04x->0x%04x, link=%u\n",
++			     vifs[i].old_ctx->def.chan->hw_value,
++			     vifs[i].new_ctx->def.chan->hw_value,
++			     vifs[i].old_ctx->def.width,
++			     vifs[i].new_ctx->def.width,
++			     vifs[i].old_ctx->def.punctured,
++			     vifs[i].new_ctx->def.punctured,
++			     vifs[i].link_conf->link_id);
+ 
+ 		mutex_lock(&dev->mt76.mutex);
+ 
+@@ -2830,7 +2844,7 @@ mt7996_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	unsigned int link_id;
+ 	int ret = 0;
+ 
+-	mlo_dbg(phy, "old=%u, new=%u\n", old_links, new_links);
++	mt76_vif_dbg(vif, "old=0x%x, new=0x%x\n", old_links, new_links);
+ 	if (old_links == new_links)
+ 		return 0;
+ 
+@@ -2876,7 +2890,7 @@ mt7996_change_sta_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	unsigned long rem = old_links & ~new_links;
+ 	int ret = 0;
+ 
+-	mlo_dbg(mt7996_hw_phy(hw), "old=%u, new=%u\n", old_links, new_links);
++	mt76_vif_dbg(vif, "STA %pM old=0x%x, new=0x%x\n", sta->addr, old_links, new_links);
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+ 	if (rem)
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 6af45467..f848e3b7 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -382,7 +382,6 @@ mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ 	if (!link_conf || !link_conf->csa_active || vif->type == NL80211_IFTYPE_STATION)
+ 		return;
+ 
+-	pr_info("%s: link_id=%d\n", __func__, link_id);
+ 	mvif->cs_ready_links = 0;
+ 	mvif->cs_link_id = IEEE80211_LINK_UNSPECIFIED;
+ 	ieee80211_csa_finish(vif, link_id);
+@@ -1508,8 +1507,8 @@ mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ 	}
+ 
+ 	mld->own_mld_id = mconf->own_mld_id;
+-	pr_info("%s: group_mld_id=%d own_mld_id=%d remap_idx=%d mld->addr[%pM]\n",
+-		__func__, mld->group_mld_id,  mld->own_mld_id, mld->remap_idx, mld->mac_addr);
++	mt76_trace(vif, "group_mld_id=%d, own_mld_id=%d, remap_idx=%d, mld->addr[%pM]\n",
++		   mld->group_mld_id,  mld->own_mld_id, mld->remap_idx, mld->mac_addr);
+ }
+ 
+ static void
+@@ -1662,8 +1661,8 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *conf,
+ 
+ 	memcpy(bss->bssid, conf->bssid, ETH_ALEN);
+ 
+-	mlo_dbg(mconf->phy, "omac_idx=%d band_idx=%d wmm_idx=%d bss->bssid=%pM enable=%d\n",
+-		bss->omac_idx, bss->band_idx, bss->wmm_idx, bss->bssid, enable);
++	mt76_trace(vif, "band=%d, omac=%d, wmm_idx=%d, bssid=%pM, link=%d, en=%d\n",
++		   bss->band_idx, bss->omac_idx, bss->wmm_idx, bss->bssid, conf->link_id, enable);
+ 
+ 	bss->bcn_interval = cpu_to_le16(conf->beacon_int);
+ 	bss->dtim_period = conf->dtim_period;
+@@ -1802,8 +1801,6 @@ mt7996_mcu_sta_tx_cap(struct mt7996_dev *dev, struct mt76_vif *mvif,
+ 	tx_cap = (struct sta_rec_tx_cap *)tlv;
+ 	tx_cap->ampdu_limit_en = true;
+ 
+-	dev_info(dev->mt76.dev, "%s: limit wcid %d ampdu to 512\n", __func__, wcid->idx);
+-
+ 	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ 				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+ }
+@@ -2961,8 +2958,8 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ 	/* starec basic */
+ 	mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, conf, link_sta,
+ 				      enable, newly);
+-	mlo_dbg(mconf->phy, "link=%u, newly=%d, en=%d\n",
+-		mlink->wcid.link_id, newly, enable);
++	mt76_trace(vif, "link=%u, wcid=%u, newly=%d, en=%d\n",
++		   mlink->wcid.link_id, mlink->wcid.idx, newly, enable);
+ 
+ 	if (!enable)
+ 		goto out;
+@@ -3022,6 +3019,8 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 	struct tlv *tlv;
+ 	unsigned long valid_links = sta->valid_links;
+ 	unsigned int link_id;
++	struct ieee80211_vif *vif = container_of((void *)msta->vif, struct ieee80211_vif,
++						 drv_priv);
+ 
+ 	mlink = mlink_dereference_protected(msta, msta->pri_link);
+ 	if (!mlink)
+@@ -3045,15 +3044,16 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 	mld_setup->link_num = hweight16(sta->valid_links);
+ 
+ 	mld_setup_link = (struct mld_setup_link *)mld_setup->link_info;
+-	mlo_dbg(mt7996_hw_phy(mlink->sta->vif->hw), "pri_link(%u) primary_id(%d) seconed_id(%d) wcid(%d), link_num(%d), mld_addr[%pM]\n",
+-		msta->pri_link, mld_setup->primary_id, mld_setup->seconed_id, mld_setup->setup_wcid, mld_setup->link_num, mld_setup->mld_addr);
++	mt76_trace(vif, "STA %pM pri_link=%u, pri_wcid=%u, sec_link=%u, sec_wcid=%u\n",
++		   sta->addr, msta->pri_link, le16_to_cpu(mld_setup->primary_id),
++		   msta->sec_link, le16_to_cpu(mld_setup->seconed_id));
+ 	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ 		mlink = mlink_dereference_protected(msta, link_id);
+ 		mconf = mconf_dereference_protected(msta->vif, link_id);
+ 
+ 		mld_setup_link->wcid = cpu_to_le16(mlink->wcid.idx);
+ 		mld_setup_link->bss_idx = mconf->mt76.idx;
+-		mlo_dbg(mt7996_hw_phy(mlink->sta->vif->hw), "link_id(%d) wcid(%d) bss_idx(%d)\n",
++		mt76_trace(vif, "link_id(%d) wcid(%d) bss_idx(%d)\n",
+ 		link_id, mld_setup_link->wcid, mld_setup_link->bss_idx);
+ 		mld_setup_link++;
+ 	}
+@@ -3319,8 +3319,8 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
+ 		return mt7996_mcu_muar_config(phy, conf, mconf, false, enable);
+ 
+ 	memcpy(data.tlv.omac_addr, conf->addr, ETH_ALEN);
+-	mlo_dbg(phy, "omac=%u, band=%u, addr=%pM, en=%d\n",
+-		data.hdr.omac_idx,data.hdr.band_idx, data.tlv.omac_addr, enable);
++	mt76_trace(conf->vif, "band=%u, omac=%u, addr=%pM, en=%d\n",
++		   data.hdr.band_idx, data.hdr.omac_idx, data.tlv.omac_addr, enable);
+ 	return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(DEV_INFO_UPDATE),
+ 				 &data, sizeof(data), true);
+ }
+@@ -6533,10 +6533,10 @@ int mt7996_mcu_set_eml_omn(struct ieee80211_hw *hw,
+ 		}
+ 	}
+ 
+-	mlo_dbg(mconf->phy, "link:%u, wcid:%d, control:%x, mode:%d, bmp:%x\n",
+-		mlink->wcid.link_id, mlink->wcid.idx, eml_omn->control,
+-		!!(eml_omn->control & EML_OMN_CONTROL_EMLSR_MODE),
+-		eml_op->bitmap);
++	mt76_vif_dbg(vif, "link:%u, wcid:%d, control:%x, mode:%d, bmp:%x\n",
++		     mlink->wcid.link_id, mlink->wcid.idx, eml_omn->control,
++		     !!(eml_omn->control & EML_OMN_CONTROL_EMLSR_MODE),
++		     eml_op->bitmap);
+ 
+ 	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ 			MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 05bdc437..612e849c 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -982,7 +982,15 @@ mt7996_get_link_wcid(struct mt7996_dev *dev, u16 idx, u8 band_idx)
+ 	return &mlink->wcid;
+ }
+ 
+-#define mlo_dbg(phy, fmt, ...)   wiphy_info(phy->mt76->hw->wiphy, "%s: " fmt, __func__, ##__VA_ARGS__)
++#define mt76_dbg(hw, fmt, ...) wiphy_info(hw->wiphy, "%s: " fmt, __func__, ##__VA_ARGS__)
++#define mt76_vif_dbg(vif, fmt, ...)				\
++	pr_info("%s: %s: " fmt,					\
++		ieee80211_vif_to_wdev(vif)->netdev->name,	\
++		__func__, ##__VA_ARGS__)
++#define mt76_trace(vif, fmt, ...)				\
++	trace_printk("(%s) " fmt,				\
++		     ieee80211_vif_to_wdev(vif)->netdev->name,	\
++		     ##__VA_ARGS__)
+ 
+ extern const struct ieee80211_ops mt7996_ops;
+ extern struct pci_driver mt7996_pci_driver;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0159-mtk-mt76-mt7996-support-configure-trigger-frame-type.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0159-mtk-mt76-mt7996-support-configure-trigger-frame-type.patch
new file mode 100644
index 0000000..4c0667b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0159-mtk-mt76-mt7996-support-configure-trigger-frame-type.patch
@@ -0,0 +1,88 @@
+From f3d7abe8218a285c098f88267034f5625fe3df3a Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Tue, 7 May 2024 10:00:09 +0800
+Subject: [PATCH 159/199] mtk: mt76: mt7996: support configure trigger frame
+ type
+
+Support receiving an nl80211 subcmd
+MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_VARIANT_TYPE, which is used to
+configure the trigger frame type by sending mcu commands. The input
+value can be 0 or 1. The former is for HE variant trigger frame and the
+latter is for EHT variant trigger frame.
+
+The purpose of this commit is for WiFi 7 R1 cert UL-RU and UL-MU test
+cases.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt7996/mtk_mcu.c | 1 +
+ mt7996/mtk_mcu.h | 1 +
+ mt7996/vendor.c  | 8 ++++++++
+ mt7996/vendor.h  | 1 +
+ 4 files changed, 11 insertions(+)
+
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index 82e3f721..74f185c2 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -1041,6 +1041,7 @@ error:
+  * SET_TRIG_TYPE (0xC9)
+  * SET_20M_DYN_ALGO (0xCA)
+  * SET_CERT_MU_EDCA_OVERRIDE (0xCD)
++ * SET_TRIG_VARIANT (0xD5)
+  */
+ int mt7996_mcu_set_muru_cmd(struct mt7996_dev *dev, u16 action, int val)
+ {
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+index 2cffc893..8ba261a7 100644
+--- a/mt7996/mtk_mcu.h
++++ b/mt7996/mtk_mcu.h
+@@ -133,6 +133,7 @@ enum {
+ 	UNI_CMD_MURU_SET_20M_DYN_ALGO = 0xCA,
+ 	UNI_CMD_MURU_PROT_FRAME_THR = 0xCC,
+ 	UNI_CMD_MURU_SET_CERT_MU_EDCA_OVERRIDE,
++	UNI_CMD_MURU_SET_TRIG_VARIANT = 0xD5,
+ };
+ 
+ struct bf_pfmu_tag {
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index 585c4e28..54eb2686 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -107,6 +107,7 @@ rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
+ 	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE] = { .type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY] = { .type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_VARIANT_TYPE] = { .type = NLA_U8 },
+ };
+ 
+ static const struct nla_policy
+@@ -907,6 +908,13 @@ static int mt7996_vendor_rfeature_ctrl(struct wiphy *wiphy,
+ 		default:
+ 			return 0;
+ 		}
++	} else if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_VARIANT_TYPE]) {
++		u8 trig_var;
++
++		trig_var = nla_get_u8(tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_VARIANT_TYPE]);
++
++		return mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_TRIG_VARIANT,
++					       trig_var);
+ 	}
+ 
+ 	return 0;
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index f6fcb623..bd9579d5 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -93,6 +93,7 @@ enum mtk_vendor_attr_rfeature_ctrl {
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_VARIANT_TYPE,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0160-mtk-mt76-mt7996-support-configure-coding-type-for-wi.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0160-mtk-mt76-mt7996-support-configure-coding-type-for-wi.patch
new file mode 100644
index 0000000..b3e6aa7
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0160-mtk-mt76-mt7996-support-configure-coding-type-for-wi.patch
@@ -0,0 +1,236 @@
+From 750c99749949677a385774085cce922986eda7a2 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Fri, 7 Jun 2024 13:24:04 +0800
+Subject: [PATCH 160/199] mtk: mt76: mt7996: support configure coding type for
+ wifi7 r1 cert
+
+This commit includes two changes for WiFi7 cert fix rate test cases.
+First, support receiving an nl80211 subcmd
+MTK_VENDOR_ATTR_RFEATURE_CTRL_CODING_TYPE, which is used to
+configure the encoding type by sending mcu commands. The input
+value can be 0, 1 or 8.
+
+Second, if we fix partial rate by two different user space commands,
+the first command will be useless due to the design of mcu commands.
+Some mcu commands could not be sent when we run several user space
+commands to fix partial rate. To address this issue, we utilize variable
+cert_mode to determine whether we are currently using multiple user
+space commands to fix the partial rate.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt7996/mac.c     | 10 ++++++++++
+ mt7996/mcu.c     |  7 +++++++
+ mt7996/mcu.h     |  1 +
+ mt7996/mt7996.h  |  5 ++++-
+ mt7996/mtk_mcu.c | 41 +++++++++++++++++++++++++++++++++++++++++
+ mt7996/vendor.c  | 18 +++++++++++++++++-
+ mt7996/vendor.h  |  2 ++
+ 7 files changed, 82 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 960b02dd..c59cc7cc 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2373,6 +2373,16 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
+ 		if (changed & IEEE80211_RC_SMPS_CHANGED)
+ 			mt7996_mcu_set_fixed_field(dev, mconf, link_sta, mlink, NULL,
+ 						   RATE_PARAM_MMPS_UPDATE);
++#ifdef CONFIG_MTK_VENDOR
++		if (changed & IEEE80211_RC_CODING_TYPE_CHANGED) {
++			struct sta_phy_uni phy = {
++				.ldpc = dev->coding_type,
++			};
++
++			mt7996_mcu_set_fixed_field(dev, mconf, link_sta, mlink, &phy,
++						   RATE_PARAM_FIXED_ENCODING);
++		}
++#endif
+ 
+ 		spin_lock_bh(&dev->mt76.sta_poll_lock);
+ 	}
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index f848e3b7..eeff5e9e 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2659,6 +2659,7 @@ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
+ 	case RATE_PARAM_FIXED_MCS:
+ 	case RATE_PARAM_FIXED_GI:
+ 	case RATE_PARAM_FIXED_HE_LTF:
++	case RATE_PARAM_FIXED_ENCODING:
+ 		if (phy)
+ 			ra->phy = *phy;
+ 		break;
+@@ -2884,6 +2885,12 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
+ 	if (IS_ERR(skb))
+ 		return PTR_ERR(skb);
+ 
++#ifdef CONFIG_MTK_VENDOR
++	if (changed && dev->cert_mode == 2)
++		return mt7996_mcu_add_rate_ctrl_fixed(dev, conf, mconf,
++						      link_sta, mlink);
++#endif
++
+ 	/* firmware rc algorithm refers to sta_rec_he for HE control.
+ 	 * once dev->rc_work changes the settings driver should also
+ 	 * update sta_rec_he here.
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 737f426d..9b29dc29 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -909,6 +909,7 @@ enum {
+ 	RATE_PARAM_FIXED_HE_LTF = 7,
+ 	RATE_PARAM_FIXED_MCS,
+ 	RATE_PARAM_FIXED_GI = 11,
++	RATE_PARAM_FIXED_ENCODING,
+ 	RATE_PARAM_AUTO = 20,
+ #ifdef CONFIG_MTK_VENDOR
+ 	RATE_PARAM_FIXED_MIMO = 30,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 612e849c..ae792719 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -516,6 +516,8 @@ struct csi_data {
+ 
+ 	struct list_head node;
+ };
++
++int mt7996_set_coding_type(struct ieee80211_hw *hw, u8 coding_type, u8 link_id);
+ #endif
+ 
+ struct mt7996_rro_ba_session {
+@@ -786,7 +788,8 @@ struct mt7996_dev {
+ 	const struct mt7996_dbg_reg_desc *dbg_reg;
+ #endif
+ #ifdef CONFIG_MTK_VENDOR
+-	bool cert_mode;
++	u8 cert_mode;
++	u8 coding_type;
+ #endif
+ 
+ #if defined CONFIG_NL80211_TESTMODE || defined CONFIG_MTK_DEBUG
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index 74f185c2..7cf4b2be 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -1372,3 +1372,44 @@ int mt7996_mcu_mlo_agc(struct mt7996_dev *dev, const void *data, int len)
+ 	                        len, true);
+ }
+ #endif
++
++#ifdef CONFIG_MTK_VENDOR
++
++static void mt7996_sta_coding_type_work(void *data, struct ieee80211_sta *sta)
++{
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_link_sta *mlink;
++	struct mt7996_dev *dev = msta->vif->dev;
++	u8 *link_id = data;
++
++	rcu_read_lock();
++	mlink = rcu_dereference(msta->link[*link_id]);
++
++	if (!mlink)
++		goto unlock;
++
++	spin_lock_bh(&dev->mt76.sta_poll_lock);
++	mlink->changed |= IEEE80211_RC_CODING_TYPE_CHANGED;
++	if (list_empty(&mlink->rc_list))
++		list_add_tail(&mlink->rc_list, &dev->sta_rc_list);
++
++	spin_unlock_bh(&dev->mt76.sta_poll_lock);
++
++unlock:
++	rcu_read_unlock();
++}
++
++int mt7996_set_coding_type(struct ieee80211_hw *hw, u8 coding_type, u8 link_id)
++{
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct mt7996_dev *dev = phy->dev;
++
++	dev->coding_type = coding_type;
++
++	/* Not support set all stations under different MLD interface */
++	ieee80211_iterate_stations_atomic(hw, mt7996_sta_coding_type_work, &link_id);
++	ieee80211_queue_work(hw, &dev->rc_work);
++
++	return 0;
++}
++#endif
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index 54eb2686..e929f812 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -108,6 +108,8 @@ rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
+ 	[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY] = { .type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_VARIANT_TYPE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_CODING_TYPE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_LINK_ID] = { .type = NLA_U8 },
+ };
+ 
+ static const struct nla_policy
+@@ -864,16 +866,24 @@ static int mt7996_vendor_rfeature_ctrl(struct wiphy *wiphy,
+ {
+ 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct ieee80211_vif *vif = wdev_to_ieee80211_vif(wdev);
+ 	struct mt7996_dev *dev = phy->dev;
+ 	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL];
+ 	int err;
+ 	u32 val;
++	u8 link_id = 0;
+ 
+ 	err = nla_parse(tb, MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX, data, data_len,
+ 			rfeature_ctrl_policy, NULL);
+ 	if (err)
+ 		return err;
+ 
++	if (ieee80211_vif_is_mld(vif) && tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_LINK_ID]) {
++		link_id = nla_get_u8(tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_LINK_ID]);
++		if (link_id >= IEEE80211_LINK_UNSPECIFIED)
++			return -EINVAL;
++	}
++
+ 	val = CAPI_RFEATURE_CHANGED;
+ 
+ 	if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG]) {
+@@ -915,6 +925,12 @@ static int mt7996_vendor_rfeature_ctrl(struct wiphy *wiphy,
+ 
+ 		return mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_TRIG_VARIANT,
+ 					       trig_var);
++	} else if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_CODING_TYPE]) {
++		u8 coding_type;
++
++		coding_type = nla_get_u8(tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_CODING_TYPE]);
++
++		return mt7996_set_coding_type(hw, coding_type, link_id);
+ 	}
+ 
+ 	return 0;
+@@ -971,7 +987,7 @@ static int mt7996_vendor_wireless_ctrl(struct wiphy *wiphy,
+ 	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]) {
+ 		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]);
+ 		dev->cert_mode = val8;
+-		mt7996_mcu_set_cert(phy, val8);
++		mt7996_mcu_set_cert(phy, !!val8);
+ 		mt7996_mcu_set_bypass_smthint(phy, val8);
+ 	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU]) {
+ 		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU]);
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index bd9579d5..5608a3b4 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -94,6 +94,8 @@ enum mtk_vendor_attr_rfeature_ctrl {
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF,
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_VARIANT_TYPE,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_CODING_TYPE,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_LINK_ID,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0161-mtk-mt76-mt7996-record-RSSI-and-SNR.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0161-mtk-mt76-mt7996-record-RSSI-and-SNR.patch
new file mode 100644
index 0000000..cea127b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0161-mtk-mt76-mt7996-record-RSSI-and-SNR.patch
@@ -0,0 +1,235 @@
+From b823f8980320ec23f5a95a1eadd6d93425f1c070 Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Thu, 13 Jun 2024 17:27:03 +0800
+Subject: [PATCH 161/199] mtk: mt76: mt7996: record RSSI and SNR
+
+RSSI and SNR information were incomplete.
+- RSSI: per-antenna ACK-frame RSSI was not reported.
+- SNR: unavailable.
+Therefore, get and record these signal statuses in order to show them for debugging purposes.
+
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ mt7996/mac.c    |  9 +++++-
+ mt7996/mcu.c    | 76 ++++++++++++++++++++++++++++++-------------------
+ mt7996/mcu.h    |  9 +++++-
+ mt7996/mt7996.h |  8 +++++-
+ 4 files changed, 70 insertions(+), 32 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index c59cc7cc..017e3465 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -493,6 +493,13 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ 		status->chain_signal[2] = to_rssi(MT_PRXV_RCPI2, v3);
+ 		status->chain_signal[3] = to_rssi(MT_PRXV_RCPI3, v3);
+ 
++		if (mlink) {
++			memcpy(mlink->chain_signal, status->chain_signal,
++			       IEEE80211_MAX_CHAINS);
++			mlink->signal = mt76_rx_signal(mphy->antenna_mask,
++						       mlink->chain_signal);
++		}
++
+ 		/* RXD Group 5 - C-RXV */
+ 		if (rxd1 & MT_RXD1_NORMAL_GROUP_5) {
+ 			rxd += 24;
+@@ -2413,7 +2420,7 @@ void mt7996_mac_work(struct work_struct *work)
+ 			if (i == mphy->band_idx) {
+ 				mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_RATE);
+ 				mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_AIRTIME);
+-				mt7996_mcu_get_rssi(mdev);
++				mt7996_mcu_get_signal_status(mdev);
+ 				// if (mtk_wed_device_active(&mdev->mmio.wed)) {
+ 					mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_ADM_STAT);
+ 					mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_MSDU_COUNT);
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index eeff5e9e..54295414 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -5791,14 +5791,15 @@ int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u16 val)
+ }
+ 
+ int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
+-	                        u16 sta_num, u16 *sta_list)
++				u16 sta_num, u16 *sta_list)
+ {
+ #define PER_STA_INFO_MAX_NUM	90
+ 	struct mt7996_mcu_per_sta_info_event *res;
++	struct mt7996_link_sta *mlink;
+ 	struct mt76_wcid *wcid;
+ 	struct sk_buff *skb;
++	int i, j, ret;
+ 	u16 wlan_idx;
+-	int i, ret;
+ 	struct {
+ 		u8 __rsv1;
+ 		u8 unsolicit;
+@@ -5837,23 +5838,18 @@ int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
+ 	switch (tag) {
+ 	case UNI_PER_STA_RSSI:
+ 		for (i = 0; i < sta_num; ++i) {
+-			struct mt7996_link_sta *mlink;
+-			struct mt76_phy *phy;
+-			s8 rssi[4];
+-			u8 *rcpi;
+-
+ 			wlan_idx = le16_to_cpu(res->rssi[i].wlan_idx);
+ 			wcid = rcu_dereference(dev->wcid[wlan_idx]);
+-			if (wcid) {
+-				rcpi = res->rssi[i].rcpi;
+-				rssi[0] = to_rssi(MT_PRXV_RCPI0, rcpi[0]);
+-				rssi[1] = to_rssi(MT_PRXV_RCPI0, rcpi[1]);
+-				rssi[2] = to_rssi(MT_PRXV_RCPI0, rcpi[2]);
+-				rssi[3] = to_rssi(MT_PRXV_RCPI0, rcpi[3]);
+-
+-				mlink = container_of(wcid, struct mt7996_link_sta, wcid);
+-				phy = dev->phys[wcid->phy_idx];
+-				mlink->ack_signal = mt76_rx_signal(phy->antenna_mask, rssi);
++			mlink = wcid_to_mlink(wcid);
++			if (mlink) {
++				struct mt76_phy *phy = dev->phys[wcid->phy_idx];
++				u8 *rcpi = res->rssi[i].rcpi;
++
++				for (j = 0; j < IEEE80211_MAX_CHAINS; ++j)
++					mlink->chain_ack_signal[j] = to_rssi(MT_PRXV_RCPI0, rcpi[j]);
++
++				mlink->ack_signal = mt76_rx_signal(phy->antenna_mask,
++								   mlink->chain_ack_signal);
+ 				ewma_avg_signal_add(&mlink->avg_ack_signal, -mlink->ack_signal);
+ 			} else {
+ 				ret = -EINVAL;
+@@ -5862,6 +5858,21 @@ int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
+ 			}
+ 		}
+ 		break;
++	case UNI_PER_STA_SNR:
++		for (i = 0; i < sta_num; ++i) {
++			wlan_idx = le16_to_cpu(res->snr[i].wlan_idx);
++			wcid = rcu_dereference(dev->wcid[wlan_idx]);
++			mlink = wcid_to_mlink(wcid);
++			if (mlink)
++				memcpy(mlink->chain_ack_snr, res->snr[i].val,
++				       IEEE80211_MAX_CHAINS);
++			else {
++				ret = -EINVAL;
++				dev_err(dev->dev, "Failed to update SNR for "
++				                  "invalid WCID: %hu\n", wlan_idx);
++			}
++		}
++		break;
+ 	default:
+ 		ret = -EINVAL;
+ 		dev_err(dev->dev, "Unknown UNI_PER_STA_INFO_TAG: %d\n", tag);
+@@ -5872,7 +5883,7 @@ out:
+ 	return ret;
+ }
+ 
+-int mt7996_mcu_get_rssi(struct mt76_dev *dev)
++int mt7996_mcu_get_signal_status(struct mt76_dev *dev)
+ {
+ 	u16 sta_list[PER_STA_INFO_MAX_NUM];
+ 	LIST_HEAD(sta_poll_list);
+@@ -5897,8 +5908,8 @@ int mt7996_mcu_get_rssi(struct mt76_dev *dev)
+ 				break;
+ 			}
+ 			mlink = list_first_entry(&sta_poll_list,
+-			                        struct mt7996_link_sta,
+-			                        wcid.poll_list);
++						 struct mt7996_link_sta,
++						 wcid.poll_list);
+ 			list_del_init(&mlink->wcid.poll_list);
+ 			spin_unlock_bh(&dev->sta_poll_lock);
+ 
+@@ -5906,16 +5917,23 @@ int mt7996_mcu_get_rssi(struct mt76_dev *dev)
+ 		}
+ 
+ 		ret = mt7996_mcu_get_per_sta_info(dev, UNI_PER_STA_RSSI,
+-		                                  i, sta_list);
+-		if (ret) {
+-			/* Add STAs, whose RSSI has not been updated,
+-			 * back to polling list.
+-			 */
+-			spin_lock_bh(&dev->sta_poll_lock);
+-			list_splice(&sta_poll_list, &dev->sta_poll_list);
+-			spin_unlock_bh(&dev->sta_poll_lock);
++						  i, sta_list);
++		if (ret)
+ 			break;
+-		}
++
++		ret = mt7996_mcu_get_per_sta_info(dev, UNI_PER_STA_SNR,
++						  i, sta_list);
++		if (ret)
++			break;
++	}
++
++	if (ret) {
++		/* Add STAs, whose signal statuses have not been updated,
++		 * back to polling list.
++		 */
++		spin_lock_bh(&dev->sta_poll_lock);
++		list_splice(&sta_poll_list, &dev->sta_poll_list);
++		spin_unlock_bh(&dev->sta_poll_lock);
+ 	}
+ 
+ 	return ret;
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 9b29dc29..20eaf20d 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -202,7 +202,13 @@ struct mt7996_mcu_mib {
+ struct per_sta_rssi {
+ 	__le16 wlan_idx;
+ 	u8 __rsv[2];
+-	u8 rcpi[4];
++	u8 rcpi[IEEE80211_MAX_CHAINS];
++} __packed;
++
++struct per_sta_snr {
++	__le16 wlan_idx;
++	u8 __rsv[2];
++	s8 val[IEEE80211_MAX_CHAINS];
+ } __packed;
+ 
+ struct mt7996_mcu_per_sta_info_event {
+@@ -213,6 +219,7 @@ struct mt7996_mcu_per_sta_info_event {
+ 
+ 	union {
+ 		struct per_sta_rssi rssi[0];
++		struct per_sta_snr snr[0];
+ 	};
+ } __packed;
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index ae792719..8d27ae5d 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -317,9 +317,15 @@ struct mt7996_link_sta {
+ 
+ 	struct list_head rc_list;
+ 
++	s8 chain_signal[IEEE80211_MAX_CHAINS];
++	int signal;
++
++	s8 chain_ack_signal[IEEE80211_MAX_CHAINS];
+ 	int ack_signal;
+ 	struct ewma_avg_signal avg_ack_signal;
+ 
++	s8 chain_ack_snr[IEEE80211_MAX_CHAINS];
++
+ 	unsigned long changed;
+ 
+ 	struct mt76_connac_sta_key_conf bip;
+@@ -1126,7 +1132,7 @@ void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ void mt7996_mcu_exit(struct mt7996_dev *dev);
+ int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
+ 	                        u16 sta_num, u16 *sta_list);
+-int mt7996_mcu_get_rssi(struct mt76_dev *dev);
++int mt7996_mcu_get_signal_status(struct mt76_dev *dev);
+ int mt7996_mcu_get_all_sta_info(struct mt76_dev *dev, u16 tag);
+ int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
+ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0162-mtk-mt76-mt7996-support-find-the-mt7996_phy-by-link_.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0162-mtk-mt76-mt7996-support-find-the-mt7996_phy-by-link_.patch
new file mode 100644
index 0000000..239e33b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0162-mtk-mt76-mt7996-support-find-the-mt7996_phy-by-link_.patch
@@ -0,0 +1,183 @@
+From a2a53b70dec62aa8f7afb02d135c2af76423e1e8 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 20 Jun 2024 10:55:07 +0800
+Subject: [PATCH 162/199] mtk: mt76: mt7996: support find the mt7996_phy by
+ link_id for vendor command
+
+Add support to find the corresponding phy by link_id. This commit is for
+the wifi7 r1 cert UL OFDMA case. If we want to force sending trigger
+frame on the specific band, we need to fill band idx in the mcu cmd.
+That is why we need link id to find the corresponding phy.
+
+Fix the way to find the band_idx if WiFi7 AP is non-MLD mode.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt7996/mt7996.h      |  5 +++--
+ mt7996/mtk_debugfs.c |  2 +-
+ mt7996/mtk_mcu.c     | 22 ++++++++++++----------
+ mt7996/vendor.c      | 19 ++++++++++++++-----
+ 4 files changed, 30 insertions(+), 18 deletions(-)
+
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 8d27ae5d..42c1e287 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1326,11 +1326,12 @@ int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx, boo
+ void mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ int mt7996_mcu_set_muru_fixed_rate_enable(struct mt7996_dev *dev, u8 action, int val);
+ int mt7996_mcu_set_muru_fixed_rate_parameter(struct mt7996_dev *dev, u8 action, void *para);
+-int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para);
++int mt7996_mcu_set_txbf_snd_info(struct mt7996_dev *dev, void *para);
+ int mt7996_mcu_set_muru_cmd(struct mt7996_dev *dev, u16 action, int val);
+ int mt7996_mcu_muru_set_prot_frame_thr(struct mt7996_dev *dev, u32 val);
+ int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val);
+-int mt7996_mcu_set_rfeature_trig_type(struct mt7996_phy *phy, u8 enable, u8 trig_type);
++int mt7996_mcu_set_rfeature_trig_type(struct mt7996_dev *dev, u8 band_idx,
++				      u8 enable, u8 trig_type);
+ void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type);
+ void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 ofdma_user_cnt);
+ void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type);
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index a7cbde3e..9bd35c91 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2929,7 +2929,7 @@ mt7996_bf_txsnd_info_set(struct file *file,
+ 	else
+ 		buf[count] = '\0';
+ 
+-	ret = mt7996_mcu_set_txbf_snd_info(phy, buf);
++	ret = mt7996_mcu_set_txbf_snd_info(phy->dev, buf);
+ 
+ 	if (ret) return -EFAULT;
+ 
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index 7cf4b2be..c87daf8e 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -352,7 +352,7 @@ int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx, boo
+ 	return mt76_mcu_skb_send_msg(&phy->dev->mt76, skb, MCU_WM_UNI_CMD(BF), false);
+ }
+ 
+-int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para)
++int mt7996_mcu_set_txbf_snd_info(struct mt7996_dev *dev, void *para)
+ {
+ 	char *buf = (char *)para;
+ 	__le16 input[5] = {0};
+@@ -365,7 +365,7 @@ int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para)
+ 
+ 	memset(&hdr, 0, sizeof(hdr));
+ 
+-	skb = mt76_mcu_msg_alloc(&phy->dev->mt76, NULL, len);
++	skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len);
+ 	if (!skb)
+ 		return -ENOMEM;
+ 
+@@ -428,7 +428,7 @@ int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para)
+ 		return -EINVAL;
+ 	}
+ 
+-	return mt76_mcu_skb_send_msg(&phy->dev->mt76, skb, MCU_WM_UNI_CMD(BF), false);
++	return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(BF), false);
+ }
+ 
+ static inline void
+@@ -1112,10 +1112,9 @@ int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val)
+ 				 true);
+ }
+ 
+-int mt7996_mcu_set_bsrp_ctrl(struct mt7996_phy *phy, u16 interval,
++int mt7996_mcu_set_bsrp_ctrl(struct mt7996_dev *dev, u8 band_idx, u16 interval,
+ 			     u16 ru_alloc, u32 trig_type, u8 trig_flow, u8 ext_cmd)
+ {
+-	struct mt7996_dev *dev = phy->dev;
+ 	struct {
+ 		u8 _rsv[4];
+ 
+@@ -1141,13 +1140,16 @@ int mt7996_mcu_set_bsrp_ctrl(struct mt7996_phy *phy, u16 interval,
+ 			       GENMASK(2, 0) : GENMASK(1, 0),
+ 	};
+ 
++	if (!mt7996_band_valid(dev, band_idx))
++		return -EINVAL;
++
+ 	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
+ 				 sizeof(req), false);
+ }
+ 
+-int mt7996_mcu_set_rfeature_trig_type(struct mt7996_phy *phy, u8 enable, u8 trig_type)
++int mt7996_mcu_set_rfeature_trig_type(struct mt7996_dev *dev, u8 band_idx,
++				      u8 enable, u8 trig_type)
+ {
+-	struct mt7996_dev *dev = phy->dev;
+ 	int ret = 0;
+ 	char buf[] = "01:00:00:1B";
+ 
+@@ -1159,14 +1161,14 @@ int mt7996_mcu_set_rfeature_trig_type(struct mt7996_phy *phy, u8 enable, u8 trig
+ 
+ 	switch (trig_type) {
+ 	case CAPI_BASIC:
+-		return mt7996_mcu_set_bsrp_ctrl(phy, 5, 67, 0, 0, enable);
++		return mt7996_mcu_set_bsrp_ctrl(dev, band_idx, 5, 67, 0, 0, enable);
+ 	case CAPI_BRP:
+-		return mt7996_mcu_set_txbf_snd_info(phy, buf);
++		return mt7996_mcu_set_txbf_snd_info(dev, buf);
+ 	case CAPI_MU_BAR:
+ 		return mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY,
+ 					       MU_DL_ACK_POLICY_MU_BAR);
+ 	case CAPI_BSRP:
+-		return mt7996_mcu_set_bsrp_ctrl(phy, 5, 67, 4, 0, enable);
++		return mt7996_mcu_set_bsrp_ctrl(dev, band_idx, 5, 67, 4, 0, enable);
+ 	default:
+ 		return 0;
+ 	}
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index e929f812..ed7c1322 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -866,12 +866,13 @@ static int mt7996_vendor_rfeature_ctrl(struct wiphy *wiphy,
+ {
+ 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+-	struct ieee80211_vif *vif = wdev_to_ieee80211_vif(wdev);
+ 	struct mt7996_dev *dev = phy->dev;
++	struct ieee80211_vif *vif = wdev_to_ieee80211_vif(wdev);
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_bss_conf *mconf;
+ 	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL];
+ 	int err;
+-	u32 val;
+-	u8 link_id = 0;
++	u8 band_idx, link_id = 0;
+ 
+ 	err = nla_parse(tb, MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX, data, data_len,
+ 			rfeature_ctrl_policy, NULL);
+@@ -884,7 +885,15 @@ static int mt7996_vendor_rfeature_ctrl(struct wiphy *wiphy,
+ 			return -EINVAL;
+ 	}
+ 
+-	val = CAPI_RFEATURE_CHANGED;
++	rcu_read_lock();
++	mconf = rcu_dereference(mvif->link[link_id]);
++	if (!mconf || !mconf->phy) {
++		rcu_read_unlock();
++		return -EINVAL;
++	}
++
++	band_idx = mconf->phy->mt76->band_idx;
++	rcu_read_unlock();
+ 
+ 	if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG]) {
+ 		u8 enable, trig_type;
+@@ -904,7 +913,7 @@ static int mt7996_vendor_rfeature_ctrl(struct wiphy *wiphy,
+ 			};
+ 		}
+ 
+-		err = mt7996_mcu_set_rfeature_trig_type(phy, enable, trig_type);
++		err = mt7996_mcu_set_rfeature_trig_type(dev, band_idx, enable, trig_type);
+ 		if (err)
+ 			return err;
+ 	} else if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY]) {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0163-mtk-mt76-mt7996-workaround-for-get_tsf-crash-issue.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0163-mtk-mt76-mt7996-workaround-for-get_tsf-crash-issue.patch
new file mode 100644
index 0000000..3093ae7
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0163-mtk-mt76-mt7996-workaround-for-get_tsf-crash-issue.patch
@@ -0,0 +1,40 @@
+From 2788ac3b4b7c0484faea6207a86e37c827262c89 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 24 Jun 2024 11:09:50 +0800
+Subject: [PATCH 163/199] mtk: mt76: mt7996: workaround for get_tsf crash issue
+
+---
+ mt7996/main.c | 15 ++++++++++++---
+ 1 file changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index cff2c527..443b3962 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1630,11 +1630,20 @@ mt7996_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_bss_conf *mconf;
+-	u64 ret;
++	u64 ret = -1ULL;
++	int i;
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+-	mconf = mconf_dereference_protected(mvif, mvif->master_link_id);
+-	ret = __mt7996_get_tsf(hw, mconf);
++	/* FIXME workaround for preventing kernel crash during ACS
++	 * (i.e., link 0 is doing ACS while link 1 queries tsf)
++	 */
++	for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
++		mconf = mconf_dereference_protected(mvif, i);
++		if (mconf)
++			break;
++	}
++	if (mconf)
++		ret = __mt7996_get_tsf(hw, mconf);
+ 	mutex_unlock(&dev->mt76.mutex);
+ 
+ 	return ret;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0164-mtk-mt76-add-debugfs-for-tx-drop-counters.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0164-mtk-mt76-add-debugfs-for-tx-drop-counters.patch
new file mode 100644
index 0000000..af71cc8
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0164-mtk-mt76-add-debugfs-for-tx-drop-counters.patch
@@ -0,0 +1,401 @@
+From a56b55265b3e813f7b803b242094a9a9a77cd3a4 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Thu, 13 Jun 2024 17:47:13 +0800
+Subject: [PATCH 164/199] mtk: mt76: add debugfs for tx drop counters
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ dma.c                | 22 +++++++++---
+ mac80211.c           |  2 ++
+ mt76.h               | 27 +++++++++++++++
+ mt7996/mac.c         | 20 ++++++++---
+ mt7996/main.c        | 10 ++++--
+ mt7996/mtk_debugfs.c | 80 ++++++++++++++++++++++++++++++++++++++++++++
+ tx.c                 | 12 +++++++
+ 7 files changed, 161 insertions(+), 12 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index 3f1fb6c2..0dae40e2 100644
+--- a/dma.c
++++ b/dma.c
+@@ -612,12 +612,16 @@ mt76_dma_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q,
+ 	dma_addr_t addr;
+ 	u8 *txwi;
+ 
+-	if (test_bit(MT76_RESET, &phy->state))
++	if (test_bit(MT76_RESET, &phy->state)) {
++		phy->tx_dbg_stats.tx_drop[MT_TX_DROP_RESET_STATE]++;
+ 		goto free_skb;
++	}
+ 
+ 	t = mt76_get_txwi(dev);
+-	if (!t)
++	if (!t) {
++		dev->tx_dbg_stats.tx_drop[MT_TX_DROP_GET_TXWI_FAIL]++;
+ 		goto free_skb;
++	}
+ 
+ 	txwi = mt76_get_txwi_ptr(dev, t);
+ 
+@@ -627,8 +631,10 @@ mt76_dma_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q,
+ 
+ 	len = skb_headlen(skb);
+ 	addr = dma_map_single(dev->dma_dev, skb->data, len, DMA_TO_DEVICE);
+-	if (unlikely(dma_mapping_error(dev->dma_dev, addr)))
++	if (unlikely(dma_mapping_error(dev->dma_dev, addr))) {
++		dev->tx_dbg_stats.tx_drop[MT_TX_DROP_DMA_FAIL]++;
+ 		goto free;
++	}
+ 
+ 	tx_info.buf[n].addr = t->dma_addr;
+ 	tx_info.buf[n++].len = dev->drv->txwi_size;
+@@ -636,13 +642,17 @@ mt76_dma_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q,
+ 	tx_info.buf[n++].len = len;
+ 
+ 	skb_walk_frags(skb, iter) {
+-		if (n == ARRAY_SIZE(tx_info.buf))
++		if (n == ARRAY_SIZE(tx_info.buf)) {
++			dev->tx_dbg_stats.tx_drop[MT_TX_DROP_AGG_EXCEEDED]++;
+ 			goto unmap;
++		}
+ 
+ 		addr = dma_map_single(dev->dma_dev, iter->data, iter->len,
+ 				      DMA_TO_DEVICE);
+-		if (unlikely(dma_mapping_error(dev->dma_dev, addr)))
++		if (unlikely(dma_mapping_error(dev->dma_dev, addr))) {
++			dev->tx_dbg_stats.tx_drop[MT_TX_DROP_DMA_FAIL]++;
+ 			goto unmap;
++		}
+ 
+ 		tx_info.buf[n].addr = addr;
+ 		tx_info.buf[n++].len = iter->len;
+@@ -651,6 +661,7 @@ mt76_dma_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q,
+ 
+ 	if (q->queued + (tx_info.nbuf + 1) / 2 >= q->ndesc - 1) {
+ 		ret = -ENOMEM;
++		phy->tx_dbg_stats.tx_drop[MT_TX_DROP_RING_FULL]++;
+ 		goto unmap;
+ 	}
+ 
+@@ -662,6 +673,7 @@ mt76_dma_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q,
+ 	if (ret < 0)
+ 		goto unmap;
+ 
++	phy->tx_dbg_stats.tx_to_hw++;
+ 	return mt76_dma_add_buf(dev, q, tx_info.buf, tx_info.nbuf,
+ 				tx_info.info, tx_info.skb, t);
+ 
+diff --git a/mac80211.c b/mac80211.c
+index 10267019..5402366e 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -417,6 +417,7 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw)
+ 
+ 	INIT_LIST_HEAD(&phy->tx_list);
+ 	spin_lock_init(&phy->tx_lock);
++	spin_lock_init(&phy->tx_dbg_stats.lock);
+ 
+ 	SET_IEEE80211_DEV(hw, dev->dev);
+ 	SET_IEEE80211_PERM_ADDR(hw, phy->macaddr);
+@@ -597,6 +598,7 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
+ 	spin_lock_init(&dev->lock);
+ 	spin_lock_init(&dev->cc_lock);
+ 	spin_lock_init(&dev->status_lock);
++	spin_lock_init(&dev->tx_dbg_stats.lock);
+ 	mutex_init(&dev->mutex);
+ 	init_waitqueue_head(&dev->tx_wait);
+ 
+diff --git a/mt76.h b/mt76.h
+index 728740ef..ee118ee5 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -835,6 +835,31 @@ struct mt76_vif {
+ 	struct ieee80211_chanctx_conf *ctx;
+ };
+ 
++enum {
++	MT_TX_DROP_IN_TESTMODE,
++	MT_TX_DROP_WCID_NOT_INIT,
++	MT_TX_DROP_STOPPED_QUEUE,
++	MT_TX_DROP_RESET_STATE,
++	MT_TX_DROP_GET_TXWI_FAIL,
++	MT_TX_DROP_DMA_FAIL,
++	MT_TX_DROP_AGG_EXCEEDED,
++	MT_TX_DROP_RING_FULL,
++	MT_TX_DROP_INVALID_SKB,
++	MT_TX_DROP_GET_TOKEN_FAIL,
++	MT_TX_DROP_ADDR_TRANS_FAIL,
++	MT_TX_DROP_INVALID_WCID,
++	MT_TX_DROP_INVALID_LINK,
++	MT_TX_DROP_MAX,
++};
++
++struct mt76_tx_debug {
++	u32 tx_from_mac80211;
++	u32 tx_to_hw;
++
++	u32 tx_drop[MT_TX_DROP_MAX];
++	spinlock_t lock;
++};
++
+ struct mt76_phy {
+ 	struct ieee80211_hw *hw;
+ 	struct ieee80211_hw *ori_hw;
+@@ -891,6 +916,7 @@ struct mt76_phy {
+ 		bool al;
+ 		u8 pin;
+ 	} leds;
++	struct mt76_tx_debug tx_dbg_stats;
+ };
+ 
+ struct mt76_dev {
+@@ -995,6 +1021,7 @@ struct mt76_dev {
+ 	};
+ 
+ 	const char *bin_file_name;
++	struct mt76_tx_debug tx_dbg_stats;
+ };
+ 
+ #define MT76_MAX_AMSDU_NUM 8
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 017e3465..0f282f16 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -897,11 +897,15 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 	u8 *txwi = (u8 *)txwi_ptr;
+ 	u8 link_id;
+ 
+-	if (unlikely(tx_info->skb->len <= ETH_HLEN))
++	if (unlikely(tx_info->skb->len <= ETH_HLEN)) {
++		mdev->tx_dbg_stats.tx_drop[MT_TX_DROP_INVALID_SKB]++;
+ 		return -EINVAL;
++	}
+ 
+-	if (WARN_ON(!wcid))
++	if (WARN_ON(!wcid)) {
++		mdev->tx_dbg_stats.tx_drop[MT_TX_DROP_INVALID_WCID]++;
+ 		return -EINVAL;
++	}
+ 
+ 	msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta;
+ 	if (ieee80211_is_data_qos(hdr->frame_control) && sta->mlo) {
+@@ -927,15 +931,19 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 	}
+ 
+ 	mconf = rcu_dereference(mvif->link[wcid->link_id]);
+-	if (!mconf)
++	if (!mconf) {
++		mdev->tx_dbg_stats.tx_drop[MT_TX_DROP_INVALID_LINK]++;
+ 		return -ENOLINK;
++	}
+ 
+ 	t = (struct mt76_txwi_cache *)(txwi + mdev->drv->txwi_size);
+ 	t->skb = tx_info->skb;
+ 
+ 	id = mt76_token_consume(mdev, &t);
+-	if (id < 0)
++	if (id < 0) {
++		mdev->tx_dbg_stats.tx_drop[MT_TX_DROP_GET_TOKEN_FAIL]++;
+ 		return id;
++	}
+ #ifdef CONFIG_MTK_DEBUG
+ 	t->jiffies = jiffies;
+ #endif
+@@ -957,8 +965,10 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 
+ 		conf = rcu_dereference(vif->link_conf[wcid->link_id]);
+ 		link_sta = rcu_dereference(sta->link[wcid->link_id]);
+-		if (!conf || !link_sta)
++		if (!conf || !link_sta) {
++			mdev->tx_dbg_stats.tx_drop[MT_TX_DROP_INVALID_LINK]++;
+ 			return -ENOLINK;
++		}
+ 
+ 		dma_sync_single_for_cpu(mdev->dma_dev, tx_info->buf[1].addr,
+ 					tx_info->buf[1].len, DMA_TO_DEVICE);
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 443b3962..553345e8 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1423,11 +1423,13 @@ static void mt7996_tx(struct ieee80211_hw *hw,
+ 		      struct sk_buff *skb)
+ {
+ 	struct mt76_phy *mphy;
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ 	struct ieee80211_vif *vif = info->control.vif;
+ 	struct mt76_wcid *wcid;
+ 	struct mt7996_vif *mvif;
+ 	struct mt7996_sta *msta;
++	bool addr_trans_success = false;
+ 
+ 	if (control->sta) {
+ 		msta = (struct mt7996_sta *)control->sta->drv_priv;
+@@ -1499,14 +1501,18 @@ static void mt7996_tx(struct ieee80211_hw *hw,
+ 		mphy = mconf->phy->mt76;
+ 		wcid = &mlink->wcid;
+ 	} else {
+-		struct mt7996_dev *dev = mt7996_hw_dev(hw);
+-
+ 		mphy = hw->priv;
+ 		wcid = &dev->mt76.global_wcid;
+ 	}
+ 
++	addr_trans_success = true;
+ 	mt76_tx(mphy, control->sta, wcid, skb);
+ unlock:
++	if (!addr_trans_success) {
++		spin_lock_bh(&dev->mt76.tx_dbg_stats.lock);
++		dev->mt76.tx_dbg_stats.tx_drop[MT_TX_DROP_ADDR_TRANS_FAIL]++;
++		spin_unlock_bh(&dev->mt76.tx_dbg_stats.lock);
++	}
+ 	rcu_read_unlock();
+ }
+ 
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 9bd35c91..759b9d8f 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -4163,6 +4163,83 @@ out:
+ 	return ret;
+ }
+ 
++static int
++mt7996_tx_drop_show(struct seq_file *s, void *data)
++{
++	struct mt7996_dev *dev = s->private;
++	struct mt76_dev *mdev = &dev->mt76;
++	struct mt76_tx_debug *dev_stats = &mdev->tx_dbg_stats;
++	struct mt76_tx_debug *phy_stats[__MT_MAX_BAND];
++	int i = 0;
++
++	seq_printf(s, "\t\t\t\t       dev");
++	for (i = 0; i < __MT_MAX_BAND; i++) {
++		seq_printf(s, "       Band%d", i);
++		if (mdev->phys[i]) {
++			phy_stats[i] = &mdev->phys[i]->tx_dbg_stats;
++		} else {
++			phy_stats[i] = kzalloc(sizeof(struct mt76_tx_debug),
++					       GFP_KERNEL);
++			if (!phy_stats[i])
++				goto out;
++		}
++
++	}
++	seq_printf(s, "       total\n");
++
++	seq_printf(s, "%-30s%12d%12d%12d%12d%12d\n", "Receive from mac80211",
++		       dev_stats->tx_from_mac80211,
++		       phy_stats[0]->tx_from_mac80211,
++		       phy_stats[1]->tx_from_mac80211,
++		       phy_stats[2]->tx_from_mac80211,
++		       dev_stats->tx_from_mac80211 +
++		       phy_stats[0]->tx_from_mac80211 +
++		       phy_stats[1]->tx_from_mac80211 +
++		       phy_stats[2]->tx_from_mac80211);
++	seq_printf(s, "%-30s%12d%12d%12d%12d%12d\n\n", "Send to hw",
++		       dev_stats->tx_to_hw,
++		       phy_stats[0]->tx_to_hw,
++		       phy_stats[1]->tx_to_hw,
++		       phy_stats[2]->tx_to_hw,
++		       dev_stats->tx_to_hw +
++		       phy_stats[0]->tx_to_hw +
++		       phy_stats[1]->tx_to_hw +
++		       phy_stats[2]->tx_to_hw);
++#define __pr(t) seq_printf(s, "Drop due to %-18s%12d%12d%12d%12d%12d\n",\
++			   #t, dev_stats->tx_drop[MT_TX_DROP_##t],	\
++			   phy_stats[0]->tx_drop[MT_TX_DROP_##t],	\
++			   phy_stats[1]->tx_drop[MT_TX_DROP_##t],	\
++			   phy_stats[2]->tx_drop[MT_TX_DROP_##t],	\
++			   dev_stats->tx_drop[MT_TX_DROP_##t] +		\
++			   phy_stats[0]->tx_drop[MT_TX_DROP_##t] + 	\
++			   phy_stats[1]->tx_drop[MT_TX_DROP_##t] +	\
++			   phy_stats[2]->tx_drop[MT_TX_DROP_##t])
++
++	__pr(IN_TESTMODE);
++	__pr(WCID_NOT_INIT);
++	__pr(STOPPED_QUEUE);
++	__pr(RESET_STATE);
++	__pr(GET_TXWI_FAIL);
++	__pr(DMA_FAIL);
++	__pr(AGG_EXCEEDED);
++	__pr(RING_FULL);
++	__pr(INVALID_SKB);
++	__pr(GET_TOKEN_FAIL);
++	__pr(ADDR_TRANS_FAIL);
++	__pr(INVALID_WCID);
++	__pr(INVALID_LINK);
++
++#undef __pr
++out:
++	for (i = 0; i < __MT_MAX_BAND; i++) {
++		if (!mdev->phys[i] && phy_stats[i])
++			kfree(phy_stats[i]);
++	}
++
++	return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(mt7996_tx_drop);
++
+ /* DRR */
+ static int
+ mt7996_drr_info(struct seq_file *s, void *data)
+@@ -4288,6 +4365,9 @@ void mt7996_mtk_init_dev_debugfs(struct mt7996_dev *dev, struct dentry *dir)
+ 	/* amsdu */
+ 	debugfs_create_file("amsdu_algo", 0600, dir, dev, &fops_amsdu_algo);
+ 	debugfs_create_file("amsdu_para", 0600, dir, dev, &fops_amsdu_para);
++
++	/* Drop counters */
++	debugfs_create_file("tx_drop_stats", 0400, dir, dev, &mt7996_tx_drop_fops);
+ }
+ 
+ #endif
+diff --git a/tx.c b/tx.c
+index 6580833e..5e6e433f 100644
+--- a/tx.c
++++ b/tx.c
+@@ -331,8 +331,14 @@ mt76_tx(struct mt76_phy *phy, struct ieee80211_sta *sta,
+ {
+ 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ 
++	spin_lock_bh(&phy->tx_dbg_stats.lock);
++	phy->tx_dbg_stats.tx_from_mac80211++;
++	spin_unlock_bh(&phy->tx_dbg_stats.lock);
+ 	if (mt76_testmode_enabled(phy)) {
+ 		ieee80211_free_txskb(phy->hw, skb);
++		spin_lock_bh(&phy->tx_dbg_stats.lock);
++		phy->tx_dbg_stats.tx_drop[MT_TX_DROP_IN_TESTMODE]++;
++		spin_unlock_bh(&phy->tx_dbg_stats.lock);
+ 		return;
+ 	}
+ 
+@@ -349,6 +355,9 @@ mt76_tx(struct mt76_phy *phy, struct ieee80211_sta *sta,
+ 		dev_warn(phy->dev->dev, "Un-initialized STA %pM wcid %d in mt76_tx\n",
+ 			 sta->addr, wcid->idx);
+ 
++		spin_lock_bh(&phy->tx_dbg_stats.lock);
++		phy->tx_dbg_stats.tx_drop[MT_TX_DROP_WCID_NOT_INIT]++;
++		spin_unlock_bh(&phy->tx_dbg_stats.lock);
+ 		ieee80211_free_txskb(phy->hw, skb);
+ 		return;
+ 	}
+@@ -380,6 +389,8 @@ mt76_txq_dequeue(struct mt76_phy *phy, struct mt76_txq *mtxq)
+ 	info = IEEE80211_SKB_CB(skb);
+ 	info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->band_idx);
+ 
++	phy->dev->tx_dbg_stats.tx_from_mac80211++;
++
+ 	return skb;
+ }
+ 
+@@ -617,6 +628,7 @@ mt76_txq_schedule_pending_wcid(struct mt76_phy *phy, struct mt76_wcid *wcid)
+ 		q = phy->q_tx[qid];
+ 		if (mt76_txq_stopped(q)) {
+ 			ret = -1;
++			phy->tx_dbg_stats.tx_drop[MT_TX_DROP_STOPPED_QUEUE]++;
+ 			break;
+ 		}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0165-mtk-mt76-add-debugfs-for-rx-drop-counters.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0165-mtk-mt76-add-debugfs-for-rx-drop-counters.patch
new file mode 100644
index 0000000..925e4bd
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0165-mtk-mt76-add-debugfs-for-rx-drop-counters.patch
@@ -0,0 +1,499 @@
+From 99a027c431faf039052d0d157807469231f037dc Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Thu, 23 May 2024 02:33:47 +0800
+Subject: [PATCH 165/199] mtk: mt76: add debugfs for rx drop counters
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ agg-rx.c             |  9 +++++
+ dma.c                | 40 +++++++++++++++++-----
+ dma.h                | 14 ++++----
+ mac80211.c           | 15 ++++++++-
+ mt76.h               | 39 ++++++++++++++++++++++
+ mt7996/mac.c         | 13 ++++++++
+ mt7996/mtk_debugfs.c | 79 ++++++++++++++++++++++++++++++++++++++++++++
+ 7 files changed, 193 insertions(+), 16 deletions(-)
+
+diff --git a/agg-rx.c b/agg-rx.c
+index b48943c4..9875baa8 100644
+--- a/agg-rx.c
++++ b/agg-rx.c
+@@ -152,6 +152,7 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
+ 	struct mt76_wcid *wcid = status->wcid;
+ 	struct ieee80211_sta *sta;
+ 	struct mt76_rx_tid *tid;
++	struct mt76_phy *phy;
+ 	bool sn_less;
+ 	u16 seqno, head, size, idx;
+ 	u8 tidno = status->qos_ctl & IEEE80211_QOS_CTL_TID_MASK;
+@@ -178,6 +179,8 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
+ 	if (!tid)
+ 		return;
+ 
++	phy = mt76_dev_phy(tid->dev, wcid->phy_idx);
++
+ 	status->flag |= RX_FLAG_DUP_VALIDATED;
+ 	spin_lock_bh(&tid->lock);
+ 
+@@ -200,6 +203,9 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
+ 	if (sn_less) {
+ 		__skb_unlink(skb, frames);
+ 		dev_kfree_skb(skb);
++		spin_lock_bh(&phy->rx_dbg_stats.lock);
++		phy->rx_dbg_stats.rx_drop[MT_RX_DROP_AGG_SN_LESS]++;
++		spin_unlock_bh(&phy->rx_dbg_stats.lock);
+ 		goto out;
+ 	}
+ 
+@@ -226,6 +232,9 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
+ 	/* Discard if the current slot is already in use */
+ 	if (tid->reorder_buf[idx]) {
+ 		dev_kfree_skb(skb);
++		spin_lock_bh(&phy->rx_dbg_stats.lock);
++		phy->rx_dbg_stats.rx_drop[MT_RX_DROP_AGG_DUP]++;
++		spin_unlock_bh(&phy->rx_dbg_stats.lock);
+ 		goto out;
+ 	}
+ 
+diff --git a/dma.c b/dma.c
+index 0dae40e2..81e76191 100644
+--- a/dma.c
++++ b/dma.c
+@@ -251,13 +251,16 @@ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
+ 	if (mt76_queue_is_wed_rx(q)) {
+ 		if (!rxwi) {
+ 			rxwi = mt76_get_rxwi(dev);
+-			if (!rxwi)
++			if (!rxwi) {
++				q->rx_drop[MT_RX_DROP_DMAD_GET_RXWI_FAIL]++;
+ 				return -ENOMEM;
++			}
+ 		}
+ 
+ 		rx_token = mt76_rx_token_consume(dev, data, rxwi, buf->addr);
+ 		if (rx_token < 0) {
+ 			mt76_put_rxwi(dev, rxwi);
++			q->rx_drop[MT_RX_DROP_DMAD_GET_TOKEN_FAIL]++;
+ 			return -ENOMEM;
+ 		}
+ 
+@@ -428,6 +431,7 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ 	struct mt76_desc *desc = &q->desc[idx];
+ 	u32 ctrl, desc_info, buf1;
+ 	void *buf = e->buf;
++	int reason;
+ 
+ 	if (mt76_queue_is_wed_rro_ind(q))
+ 		goto done;
+@@ -443,7 +447,9 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ 		*info = desc_info;
+ 
+ 	buf1 = le32_to_cpu(desc->buf1);
+-	mt76_dma_should_drop_buf(drop, ctrl, buf1, desc_info);
++	reason = mt76_dma_should_drop_buf(drop, ctrl, buf1, desc_info);
++	if (drop && *drop && reason >= 0)
++		q->rx_drop[reason]++;
+ 
+ 	if (mt76_queue_is_wed_rx(q)) {
+ 		u32 id, find = 0;
+@@ -467,13 +473,17 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ 			}
+ 
+ 			spin_unlock_bh(&dev->rx_token_lock);
+-			if (!find)
++			if (!find) {
++				q->rx_drop[MT_RX_DROP_DMAD_ADDR_NOT_FOUND]++;
+ 				return NULL;
++			}
+ 		}
+ 
+ 		r = mt76_rx_token_release(dev, token);
+-		if (!r)
++		if (!r) {
++			q->rx_drop[MT_RX_DROP_DMAD_TOKEN_NOT_FOUND]++;
+ 			return NULL;
++		}
+ 
+ 		dma_unmap_single(dev->dma_dev, r->dma_addr,
+ 				 SKB_WITH_OVERHEAD(q->buf_size),
+@@ -489,8 +499,10 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ 			struct mt76_queue_buf qbuf;
+ 
+ 			buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC | GFP_DMA32);
+-			if (!buf)
++			if (!buf) {
++				q->rx_drop[MT_RX_DROP_DMAD_NOMEM]++;
+ 				return NULL;
++			}
+ 
+ 			memcpy(buf, r->ptr, SKB_WITH_OVERHEAD(q->buf_size));
+ 
+@@ -500,6 +512,7 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ 			if (unlikely(dma_mapping_error(dev->dma_dev, r->dma_addr))) {
+ 				skb_free_frag(r->ptr);
+ 				mt76_put_rxwi(dev, r);
++				q->rx_drop[MT_RX_DROP_DMAD_DMA_MAPPING_FAIL]++;
+ 				return NULL;
+ 			}
+ 
+@@ -517,8 +530,11 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ 			}
+ 		}
+ 
+-		if (drop)
++		if (drop) {
+ 			*drop |= !!(buf1 & MT_DMA_CTL_WO_DROP);
++			if (buf1 & MT_DMA_CTL_WO_DROP)
++				q->rx_drop[MT_RX_DROP_DMAD_WO_FRAG]++;
++		}
+ 	} else {
+ 		dma_unmap_single(dev->dma_dev, e->dma_addr[0],
+ 				 SKB_WITH_OVERHEAD(q->buf_size),
+@@ -892,6 +908,7 @@ mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
+ 
+ 		skb_add_rx_frag(skb, nr_frags, page, offset, len, q->buf_size);
+ 	} else {
++		q->rx_drop[MT_RX_DROP_FRAG]++;
+ 		skb_free_frag(data);
+ 	}
+ 
+@@ -899,10 +916,12 @@ mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
+ 		return;
+ 
+ 	q->rx_head = NULL;
+-	if (nr_frags < ARRAY_SIZE(shinfo->frags))
++	if (nr_frags < ARRAY_SIZE(shinfo->frags)) {
+ 		dev->drv->rx_skb(dev, q - dev->q_rx, skb, &info);
+-	else
++	} else {
++		q->rx_drop[MT_RX_DROP_FRAG]++;
+ 		dev_kfree_skb(skb);
++	}
+ }
+ 
+ static int
+@@ -947,6 +966,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+ 			data_len = SKB_WITH_OVERHEAD(q->buf_size);
+ 
+ 		if (data_len < len + q->buf_offset) {
++			q->rx_drop[MT_RX_DROP_FRAG]++;
+ 			dev_kfree_skb(q->rx_head);
+ 			q->rx_head = NULL;
+ 			goto free_frag;
+@@ -963,8 +983,10 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+ 			goto free_frag;
+ 
+ 		skb = build_skb(data, q->buf_size);
+-		if (!skb)
++		if (!skb) {
++			q->rx_drop[MT_RX_DROP_BUILD_SKB_FAIL]++;
+ 			goto free_frag;
++		}
+ 
+ 		skb_reserve(skb, q->buf_offset);
+ 
+diff --git a/dma.h b/dma.h
+index 3a8c2e55..718122d5 100644
+--- a/dma.h
++++ b/dma.h
+@@ -93,27 +93,29 @@ mt76_dma_reset_tx_queue(struct mt76_dev *dev, struct mt76_queue *q)
+ 		mt76_wed_dma_setup(dev, q, true);
+ }
+ 
+-static inline void
++static inline int
+ mt76_dma_should_drop_buf(bool *drop, u32 ctrl, u32 buf1, u32 info)
+ {
+ 	if (!drop)
+-		return;
++		return -1;
+ 
+ 	*drop = !!(ctrl & (MT_DMA_CTL_TO_HOST_A | MT_DMA_CTL_DROP));
+ 	if (!(ctrl & MT_DMA_CTL_VER_MASK))
+-		return;
++		return MT_RX_DROP_DMAD_WO_DROP;
+ 
+ 	switch (FIELD_GET(MT_DMA_WED_IND_REASON, buf1)) {
+ 	case MT_DMA_WED_IND_REASON_REPEAT:
+ 		*drop = true;
+-		break;
++		return MT_RX_DROP_DMAD_RRO_REPEAT;
+ 	case MT_DMA_WED_IND_REASON_OLDPKT:
+ 		*drop = !(info & MT_DMA_INFO_DMA_FRAG);
+-		break;
++		return MT_RX_DROP_DMAD_RRO_OLDPKT;
+ 	default:
+ 		*drop = !!(ctrl & MT_DMA_CTL_PN_CHK_FAIL);
+-		break;
++		return MT_RX_DROP_DMAD_RRO_PN_CHK_FAIL;
+ 	}
++
++	return -1;
+ }
+ 
+ #endif
+diff --git a/mac80211.c b/mac80211.c
+index 5402366e..d5f842db 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -418,6 +418,7 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw)
+ 	INIT_LIST_HEAD(&phy->tx_list);
+ 	spin_lock_init(&phy->tx_lock);
+ 	spin_lock_init(&phy->tx_dbg_stats.lock);
++	spin_lock_init(&phy->rx_dbg_stats.lock);
+ 
+ 	SET_IEEE80211_DEV(hw, dev->dev);
+ 	SET_IEEE80211_PERM_ADDR(hw, phy->macaddr);
+@@ -755,6 +756,9 @@ static void mt76_rx_release_amsdu(struct mt76_phy *phy, enum mt76_rxq_id q)
+ 		}
+ 
+ 		if (ether_addr_equal(skb->data + offset, rfc1042_header)) {
++			spin_lock_bh(&phy->rx_dbg_stats.lock);
++			phy->rx_dbg_stats.rx_drop[MT_RX_DROP_RFC_PKT]++;
++			spin_unlock_bh(&phy->rx_dbg_stats.lock);
+ 			dev_kfree_skb(skb);
+ 			return;
+ 		}
+@@ -792,6 +796,9 @@ void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb)
+ 
+ 	if (!test_bit(MT76_STATE_RUNNING, &phy->state)) {
+ 		dev_kfree_skb(skb);
++		spin_lock_bh(&phy->rx_dbg_stats.lock);
++		phy->rx_dbg_stats.rx_drop[MT_RX_DROP_STATE_ERR]++;
++		spin_unlock_bh(&phy->rx_dbg_stats.lock);
+ 		return;
+ 	}
+ 
+@@ -1055,6 +1062,7 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
+ {
+ 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+ 	struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb);
++	struct mt76_phy *phy;
+ 	struct mt76_rx_status mstat;
+ 
+ 	mstat = *((struct mt76_rx_status *)skb->cb);
+@@ -1101,7 +1109,12 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
+ 	}
+ 
+ 	*sta = wcid_to_sta(mstat.wcid);
+-	*hw = mt76_main_hw(dev->phys[mstat.phy_idx]);
++	*hw = mt76_phy_hw(dev, mstat.phy_idx);
++
++	phy = mt76_dev_phy(dev, mstat.phy_idx);
++	spin_lock_bh(&phy->rx_dbg_stats.lock);
++	phy->rx_dbg_stats.rx_to_mac80211++;
++	spin_unlock_bh(&phy->rx_dbg_stats.lock);
+ }
+ 
+ static void
+diff --git a/mt76.h b/mt76.h
+index ee118ee5..f2d12b89 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -187,6 +187,34 @@ enum mt76_dfs_state {
+ 	MT_DFS_STATE_ACTIVE,
+ };
+ 
++enum {
++	/* Per dev counters*/
++	MT_RX_DROP_DMAD_RRO_REPEAT,
++	MT_RX_DROP_DMAD_RRO_OLDPKT,
++	MT_RX_DROP_DMAD_RRO_PN_CHK_FAIL,
++	MT_RX_DROP_DMAD_WO_FRAG,
++	MT_RX_DROP_DMAD_WO_DROP,
++	MT_RX_DROP_DMAD_ADDR_NOT_FOUND,
++	MT_RX_DROP_DMAD_TOKEN_NOT_FOUND,
++	MT_RX_DROP_DMAD_GET_TOKEN_FAIL,
++	MT_RX_DROP_DMAD_GET_RXWI_FAIL,
++	MT_RX_DROP_DMAD_NOMEM,
++	MT_RX_DROP_DMAD_DMA_MAPPING_FAIL,
++	MT_RX_DROP_FRAG,
++	MT_RX_DROP_BUILD_SKB_FAIL,
++
++	MT_RX_DROP_PER_Q_MAX,
++
++	/* Per phy counters */
++	MT_RX_DROP_RXD_ERR = 0,
++	MT_RX_DROP_STATE_ERR,
++	MT_RX_DROP_RFC_PKT,
++	MT_RX_DROP_AGG_SN_LESS,
++	MT_RX_DROP_AGG_DUP,
++
++	MT_RX_DROP_PER_PHY_MAX,
++};
++
+ struct mt76_queue_buf {
+ 	dma_addr_t addr;
+ 	u16 len:15,
+@@ -255,6 +283,8 @@ struct mt76_queue {
+ 	dma_addr_t desc_dma;
+ 	struct sk_buff *rx_head;
+ 	struct page_frag_cache rx_page;
++
++	u32 rx_drop[MT_RX_DROP_PER_Q_MAX];
+ };
+ 
+ struct mt76_mcu_ops {
+@@ -860,6 +890,14 @@ struct mt76_tx_debug {
+ 	spinlock_t lock;
+ };
+ 
++struct mt76_rx_debug {
++	u32 rx_from_hw;
++	u32 rx_to_mac80211;
++
++	u32 rx_drop[MT_RX_DROP_PER_PHY_MAX];
++	spinlock_t lock;
++};
++
+ struct mt76_phy {
+ 	struct ieee80211_hw *hw;
+ 	struct ieee80211_hw *ori_hw;
+@@ -917,6 +955,7 @@ struct mt76_phy {
+ 		u8 pin;
+ 	} leds;
+ 	struct mt76_tx_debug tx_dbg_stats;
++	struct mt76_rx_debug rx_dbg_stats;
+ };
+ 
+ struct mt76_dev {
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 0f282f16..2f37d31c 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1410,9 +1410,11 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+ 			 struct sk_buff *skb, u32 *info)
+ {
+ 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
++	struct mt76_phy *phy;
+ 	__le32 *rxd = (__le32 *)skb->data;
+ 	__le32 *end = (__le32 *)&skb->data[skb->len];
+ 	enum rx_pkt_type type;
++	u8 band_idx;
+ 
+ 	type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE);
+ 	if (type != PKT_TYPE_NORMAL) {
+@@ -1447,12 +1449,23 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+ 		dev_kfree_skb(skb);
+ 		break;
+ 	case PKT_TYPE_NORMAL:
++		band_idx = le32_get_bits(rxd[1], MT_RXD1_NORMAL_BAND_IDX);
++		phy = mt76_dev_phy(mdev, band_idx);
++		spin_lock_bh(&phy->rx_dbg_stats.lock);
++		phy->rx_dbg_stats.rx_from_hw++;
++		spin_unlock_bh(&phy->rx_dbg_stats.lock);
++
+ 		if (!mt7996_mac_fill_rx(dev, q, skb, info)) {
+ 			mt76_rx(&dev->mt76, q, skb);
+ 			return;
+ 		}
+ 		fallthrough;
+ 	default:
++		band_idx = le32_get_bits(rxd[1], MT_RXD1_NORMAL_BAND_IDX);
++		phy = mt76_dev_phy(mdev, band_idx);
++		spin_lock_bh(&phy->rx_dbg_stats.lock);
++		phy->rx_dbg_stats.rx_drop[MT_RX_DROP_RXD_ERR]++;
++		spin_unlock_bh(&phy->rx_dbg_stats.lock);
+ 		dev_kfree_skb(skb);
+ 		break;
+ 	}
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 759b9d8f..b16ea5fe 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -4240,6 +4240,84 @@ out:
+ }
+ DEFINE_SHOW_ATTRIBUTE(mt7996_tx_drop);
+ 
++static int
++mt7996_rx_drop_show(struct seq_file *s, void *data)
++{
++	struct mt7996_dev *dev = s->private;
++	struct mt76_dev *mdev = &dev->mt76;
++	struct mt76_rx_debug *stats[__MT_MAX_BAND];
++	struct mt76_queue *q[2];
++	int i = 0;
++
++	q[0] = &mdev->q_rx[MT_RXQ_MAIN];
++	q[1] = is_mt7996(mdev) ? &mdev->q_rx[MT_RXQ_BAND2] :
++				 &mdev->q_rx[MT_RXQ_BAND1];
++
++	seq_printf(s, "\t\t\t\t   ");
++	for (i = 0; i < 2; i++) {
++		seq_printf(s, "        RXQ%d", q[i]->hw_idx);
++	}
++	seq_printf(s, "\n");
++
++#define __pr(t) seq_printf(s, "Drop due to %-22s%12d%12d\n", #t, \
++			   q[0]->rx_drop[MT_RX_DROP_##t],	\
++			   q[1]->rx_drop[MT_RX_DROP_##t]);
++	__pr(DMAD_RRO_REPEAT);
++	__pr(DMAD_RRO_OLDPKT);
++	__pr(DMAD_RRO_PN_CHK_FAIL);
++	__pr(DMAD_WO_FRAG);
++	__pr(DMAD_WO_DROP);
++	__pr(DMAD_ADDR_NOT_FOUND);
++	__pr(DMAD_TOKEN_NOT_FOUND);
++	__pr(DMAD_GET_TOKEN_FAIL);
++	__pr(DMAD_GET_RXWI_FAIL);
++	__pr(DMAD_NOMEM);
++	__pr(DMAD_DMA_MAPPING_FAIL);
++	__pr(FRAG);
++	__pr(BUILD_SKB_FAIL);
++#undef __pr
++
++	seq_printf(s, "\n\t\t\t\t   ");
++	for (i = 0; i < __MT_MAX_BAND; i++) {
++		seq_printf(s, "       Band%d", i);
++		if (mdev->phys[i]) {
++			stats[i] = &mdev->phys[i]->rx_dbg_stats;
++		} else {
++			stats[i] = kzalloc(sizeof(struct mt76_rx_debug),
++					       GFP_KERNEL);
++			if (!stats[i])
++				goto out;
++		}
++	}
++	seq_printf(s, "\n");
++	seq_printf(s, "%-35s%12d%12d%12d\n", "Receive from hw",
++		       stats[MT_BAND0]->rx_from_hw,
++		       stats[MT_BAND1]->rx_from_hw,
++		       stats[MT_BAND2]->rx_from_hw);
++	seq_printf(s, "%-35s%12d%12d%12d\n\n", "Send to mac80211",
++		       stats[MT_BAND0]->rx_to_mac80211,
++		       stats[MT_BAND1]->rx_to_mac80211,
++		       stats[MT_BAND2]->rx_to_mac80211);
++#define __pr(t) seq_printf(s, "Drop due to %-22s%12d%12d%12d\n", #t, \
++			   stats[MT_BAND0]->rx_drop[MT_RX_DROP_##t],	\
++			   stats[MT_BAND1]->rx_drop[MT_RX_DROP_##t],	\
++			   stats[MT_BAND2]->rx_drop[MT_RX_DROP_##t])
++	__pr(RXD_ERR);
++	__pr(STATE_ERR);
++	__pr(RFC_PKT);
++	__pr(AGG_SN_LESS);
++	__pr(AGG_DUP);
++#undef __pr
++
++out:
++	for (i = 0; i < __MT_MAX_BAND; i++) {
++		if (!mdev->phys[i] && stats[i])
++			kfree(stats[i]);
++	}
++
++	return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(mt7996_rx_drop);
+ /* DRR */
+ static int
+ mt7996_drr_info(struct seq_file *s, void *data)
+@@ -4368,6 +4446,7 @@ void mt7996_mtk_init_dev_debugfs(struct mt7996_dev *dev, struct dentry *dir)
+ 
+ 	/* Drop counters */
+ 	debugfs_create_file("tx_drop_stats", 0400, dir, dev, &mt7996_tx_drop_fops);
++	debugfs_create_file("rx_drop_stats", 0400, dir, dev, &mt7996_rx_drop_fops);
+ }
+ 
+ #endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0166-mtk-mt76-mt7996-add-support-for-remain-on-channel-op.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0166-mtk-mt76-mt7996-add-support-for-remain-on-channel-op.patch
new file mode 100644
index 0000000..000a95f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0166-mtk-mt76-mt7996-add-support-for-remain-on-channel-op.patch
@@ -0,0 +1,308 @@
+From 17fa174e1a5da3fc24454f6b5e4e014b93b0165d Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 7 Jun 2024 15:24:00 +0800
+Subject: [PATCH 166/199] mtk: mt76: mt7996: add support for remain-on-channel
+ operation
+
+Remain-on-channel operation allows interface to temporarily leave the
+operating channel and go to another channel for listening or TX skbs.
+It is not allowed to conduct a scan and a remain-on-channel at the same
+time, since they both require a bss/link to jump to another channel.
+
+The constrains for interfaces about remain-on-channel operation:
+1. The legacy interfaces can only remain on channels that is in the same
+   band with the original channel.
+2. The MLD AP can only remain on channels that in the same band with one
+   of its operating links.
+3. The MLd STA can remain on full-band channels even if it does not have
+   connection on that band. In such case, the default link is used.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ mt7996/init.c   |   4 ++
+ mt7996/main.c   | 174 +++++++++++++++++++++++++++++++++++++++++++++++-
+ mt7996/mt7996.h |   5 ++
+ 3 files changed, 181 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 55eb32cb..5f38374f 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -498,6 +498,8 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ 	wiphy->max_scan_ssids = 4;
+ 	wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+ 
++	wiphy->max_remain_on_channel_duration = 5000;
++
+ 	/* enable MLO support */
+ 	wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO;
+ 	wiphy->iftype_ext_capab = mt7996_iftypes_ext_capa;
+@@ -740,6 +742,7 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 
+ 	INIT_DELAYED_WORK(&mphy->mac_work, mt7996_mac_work);
+ 	INIT_DELAYED_WORK(&phy->scan_work, mt7996_scan_work);
++	INIT_DELAYED_WORK(&phy->roc_complete_work, mt7996_roc_complete_work);
+ 
+ 	ret = mt7996_eeprom_parse_hw_cap(dev, phy);
+ 	if (ret)
+@@ -1676,6 +1679,7 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ 	INIT_WORK(&dev->rc_work, mt7996_mac_sta_rc_work);
+ 	INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7996_mac_work);
+ 	INIT_DELAYED_WORK(&dev->phy.scan_work, mt7996_scan_work);
++	INIT_DELAYED_WORK(&dev->phy.roc_complete_work, mt7996_roc_complete_work);
+ 	INIT_DELAYED_WORK(&dev->scs_work, mt7996_mcu_scs_sta_poll);
+ 	INIT_LIST_HEAD(&dev->sta_rc_list);
+ 	INIT_LIST_HEAD(&dev->twt_list);
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 553345e8..35a77609 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -505,6 +505,7 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 
+ 	cancel_delayed_work_sync(&phy->scan_work);
++	cancel_delayed_work_sync(&phy->roc_complete_work);
+ 	cancel_delayed_work(&mvif->beacon_mon_work);
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+@@ -528,7 +529,7 @@ static void ___mt7996_set_channel(struct mt7996_phy *phy,
+ {
+ 	struct mt76_dev *mdev = phy->mt76->dev;
+ 	struct mt76_phy *mphy = phy->mt76;
+-	bool offchannel = phy->scan_chan != NULL;
++	bool offchannel = phy->scan_chan != NULL || phy->roc_chan != NULL;
+ 	int timeout = HZ / 5;
+ 	unsigned long was_scanning = ieee80211_get_scanning(mphy->hw);
+ 
+@@ -2520,7 +2521,8 @@ mt7996_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 
+ 	mt76_vif_dbg(vif, "trigger scan on mt76 band %u\n", phy->mt76->band_idx);
+ 	mutex_lock(&phy->dev->mt76.mutex);
+-	if (WARN_ON(phy->scan_req || phy->scan_chan)) {
++	if (WARN_ON(phy->scan_req || phy->scan_chan ||
++		    test_bit(MT76_STATE_ROC, &phy->mt76->state))) {
+ 		mutex_unlock(&phy->dev->mt76.mutex);
+ 		return -EBUSY;
+ 	}
+@@ -2641,6 +2643,170 @@ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+ 	}
+ }
+ 
++void mt7996_roc_complete_work(struct work_struct *work)
++{
++	struct cfg80211_chan_def *chandef;
++	struct ieee80211_vif *vif;
++	struct ieee80211_hw *hw;
++	struct mt7996_vif *mvif;
++	struct mt7996_phy *phy;
++	int i;
++
++	phy = container_of(work, struct mt7996_phy, roc_complete_work.work);
++	vif = phy->roc_vif;
++	mvif = (struct mt7996_vif *)vif->drv_priv;
++	hw = mvif->hw;
++
++	mutex_lock(&phy->dev->mt76.mutex);
++
++	if (!test_bit(MT76_STATE_ROC, &phy->mt76->state)) {
++		mutex_unlock(&phy->dev->mt76.mutex);
++		return;
++	}
++
++	phy->roc_vif = NULL;
++	phy->roc_chan = NULL;
++	clear_bit(MT76_STATE_ROC, &phy->mt76->state);
++
++	if (ieee80211_vif_is_mld(vif)) {
++		struct mt7996_bss_conf *mconf;
++
++		for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
++			mconf = mconf_dereference_protected(mvif, i);
++
++			if (mconf && mconf->phy == phy && mconf == &mvif->deflink) {
++				mt7996_remove_bss_conf(vif, &vif->bss_conf,
++						       &mvif->deflink);
++				break;
++			}
++		}
++	}
++
++	mutex_unlock(&phy->dev->mt76.mutex);
++
++	chandef = phy->chanctx ? &phy->chanctx->chandef : &phy->mt76->chandef;
++	wiphy_info(hw->wiphy, "finish roc work, go back to freq=%u\n",
++			chandef->chan->center_freq);
++	mt7996_set_channel(phy, chandef);
++	ieee80211_remain_on_channel_expired(hw);
++}
++
++static int
++mt7996_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++			 struct ieee80211_channel *chan, int duration,
++			 enum ieee80211_roc_type type)
++{
++	struct cfg80211_chan_def chandef = {};
++	struct mt7996_bss_conf *mconf;
++	struct mt7996_vif *mvif;
++	struct mt7996_phy *phy;
++	int ret;
++
++	if (!chan)
++		return -EINVAL;
++
++	phy = mt7996_band_phy(hw, chan->band);
++
++	if (!phy)
++		return -EINVAL;
++
++	mvif = (struct mt7996_vif *)vif->drv_priv;
++	mutex_lock(&phy->dev->mt76.mutex);
++
++	if (test_bit(MT76_STATE_ROC, &phy->mt76->state) ||
++	    test_bit(MT76_SCANNING, &phy->mt76->state)) {
++		ret = -EBUSY;
++		goto error_unlock;
++	}
++
++	if (!ieee80211_vif_is_mld(vif)) {
++		mconf = mconf_dereference_protected(mvif, 0);
++		if (!mconf || mconf->phy != phy) {
++			ret = -EINVAL;
++			goto error_unlock;
++		}
++	} else {
++		unsigned long valid_links = vif->valid_links;
++		unsigned int link_id;
++		bool found = false;
++
++		for_each_set_bit(link_id, &valid_links,
++				 IEEE80211_MLD_MAX_NUM_LINKS) {
++			mconf = mconf_dereference_protected(mvif, link_id);
++			if (mconf && mconf->phy == phy) {
++				found = true;
++				break;
++			}
++		}
++
++		if (!found) {
++			if (vif->type != NL80211_IFTYPE_STATION) {
++				ret = -ENOLINK;
++				goto error_unlock;
++			}
++
++			/* Try to find an empty link, which is later used to scan. */
++			for (link_id = 0;
++			     link_id < IEEE80211_MLD_MAX_NUM_LINKS;
++			     link_id++) {
++				if (!rcu_access_pointer(mvif->link[link_id]))
++					break;
++			}
++
++			if (link_id == IEEE80211_MLD_MAX_NUM_LINKS) {
++				ret = -ENOLINK;
++				goto error_unlock;
++			}
++
++			vif->bss_conf.link_id = link_id;
++			ret = mt7996_add_bss_conf(phy, vif, &vif->bss_conf);
++			if (ret)
++				goto error_unlock;
++		}
++
++	}
++	wiphy_info(hw->wiphy, "start roc work on freq=%u\n",
++			chan->center_freq);
++
++	set_bit(MT76_STATE_ROC, &phy->mt76->state);
++	phy->roc_vif = vif;
++	phy->roc_chan = chan;
++	mutex_unlock(&phy->dev->mt76.mutex);
++
++	cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
++	mt7996_set_channel(phy, &chandef);
++	ieee80211_ready_on_channel(hw);
++
++	ieee80211_queue_delayed_work(phy->mt76->hw, &phy->roc_complete_work,
++				     msecs_to_jiffies(duration));
++
++	return 0;
++
++error_unlock:
++	mutex_unlock(&phy->dev->mt76.mutex);
++	return ret;
++}
++
++static int
++mt7996_cancel_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
++{
++	int band;
++	struct mt7996_phy *phy;
++
++	for (band = 0; band < NUM_NL80211_BANDS; band++) {
++		if (!hw->wiphy->bands[band])
++			continue;
++
++		phy = mt7996_band_phy(hw, band);
++		if (!phy || !test_bit(MT76_STATE_ROC, &phy->mt76->state))
++			continue;
++
++		cancel_delayed_work_sync(&phy->roc_complete_work);
++		ieee80211_queue_delayed_work(phy->mt76->hw, &phy->roc_complete_work, 0);
++	}
++	return 0;
++}
++
+ static int
+ mt7996_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
+ {
+@@ -2689,6 +2855,7 @@ mt7996_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *co
+ 
+ 	mt76_dbg(hw, "remove %u\n", conf->def.chan->hw_value);
+ 	cancel_delayed_work_sync(&phy->scan_work);
++	cancel_delayed_work_sync(&phy->roc_complete_work);
+ 	cancel_delayed_work_sync(&phy->mt76->mac_work);
+ 
+ 	mutex_lock(&phy->dev->mt76.mutex);
+@@ -2774,6 +2941,7 @@ mt7996_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	mt76_vif_dbg(vif, "remove link %u from %d MHz\n",
+ 		     link_conf->link_id, conf->def.chan->center_freq);
+ 	cancel_delayed_work_sync(&phy->scan_work);
++	cancel_delayed_work_sync(&phy->roc_complete_work);
+ 
+ 	mutex_lock(&phy->dev->mt76.mutex);
+ 
+@@ -3036,6 +3204,8 @@ const struct ieee80211_ops mt7996_ops = {
+ 	.wake_tx_queue = mt76_wake_tx_queue,
+ 	.hw_scan = mt7996_hw_scan,
+ 	.cancel_hw_scan = mt7996_cancel_hw_scan,
++	.remain_on_channel = mt7996_remain_on_channel,
++	.cancel_remain_on_channel = mt7996_cancel_remain_on_channel,
+ 	.release_buffered_frames = mt76_release_buffered_frames,
+ 	.get_txpower = mt7996_get_txpower,
+ 	.channel_switch_beacon = mt7996_channel_switch_beacon,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 42c1e287..3ee8156c 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -597,6 +597,10 @@ struct mt7996_phy {
+ 	struct mt7996_scs_ctrl scs_ctrl;
+ 	u32 red_drop;
+ 
++	struct delayed_work roc_complete_work;
++	struct ieee80211_vif *roc_vif;
++	struct ieee80211_channel *roc_chan;
++
+ 	bool sku_limit_en;
+ 	bool sku_path_en;
+ 
+@@ -1249,6 +1253,7 @@ bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len);
+ void mt7996_stats_work(struct work_struct *work);
+ void mt7996_scan_work(struct work_struct *work);
+ void mt7996_scan_complete(struct mt7996_phy *phy, bool aborted);
++void mt7996_roc_complete_work(struct work_struct *work);
+ void mt7996_beacon_mon_work(struct work_struct *work);
+ int mt76_dfs_start_rdd(struct mt7996_dev *dev, bool force);
+ int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0167-mtk-mt76-mt7996-rework-the-setting-flow-of-starec-RA.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0167-mtk-mt76-mt7996-rework-the-setting-flow-of-starec-RA.patch
new file mode 100644
index 0000000..29f9510
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0167-mtk-mt76-mt7996-rework-the-setting-flow-of-starec-RA.patch
@@ -0,0 +1,291 @@
+From 6b95665d9e183c522e051a18177afe8b91ea817c Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 26 Jun 2024 19:09:46 +0800
+Subject: [PATCH 167/199] mtk: mt76: mt7996: rework the setting flow of starec
+ RA and MLD tags
+
+1. STA_REC_RA and STA_REC_MLD tags need to be set when newly is true,
+   so that FW can correctly set non-setup link(s) to powersave mode when
+   the peer is a MLSR MLD STA. (reported by Peter)
+
+2. This patch also tries to fix random EAPOL timeout issue.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/main.c   |   4 --
+ mt7996/mcu.c    | 187 +++++++++++++++++++++---------------------------
+ mt7996/mcu.h    |   3 +
+ mt7996/mt7996.h |   2 -
+ 4 files changed, 83 insertions(+), 113 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 35a77609..b5d45d19 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1318,10 +1318,6 @@ mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 		mtxq->wcid = mlink->wcid.idx;
+ 	}
+ 
+-	ret = mt7996_mcu_add_mld_sta(dev, vif, sta, add);
+-	if (ret)
+-		goto error;
+-
+ 	return 0;
+ error:
+ 	mt7996_mac_sta_remove_links(dev, vif, sta, add);
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 54295414..9b3c6e3d 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2911,6 +2911,77 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
+ 	return mt7996_mcu_add_rate_ctrl_fixed(dev, conf, mconf, link_sta, mlink);
+ }
+ 
++static void
++mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
++			     struct ieee80211_sta *sta, unsigned long valid_links)
++{
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct sta_rec_mld_setup *mld_setup;
++	struct mld_setup_link *mld_setup_link;
++	struct mt7996_link_sta *mlink;
++	struct mt7996_bss_conf *mconf;
++	struct tlv *tlv;
++	unsigned int link_id;
++	struct ieee80211_vif *vif = container_of((void *)msta->vif, struct ieee80211_vif,
++						 drv_priv);
++
++	mlink = mlink_dereference_protected(msta, msta->pri_link);
++	if (!mlink)
++		return;
++
++	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MLD,
++				      sizeof(*mld_setup) +
++				      sizeof(struct mld_setup_link) *
++					     hweight16(valid_links));
++
++	mld_setup = (struct sta_rec_mld_setup *)tlv;
++	memcpy(mld_setup->mld_addr, sta->addr, ETH_ALEN);
++	mld_setup->setup_wcid = cpu_to_le16(mlink->wcid.idx);
++	mld_setup->primary_id = cpu_to_le16(mlink->wcid.idx);
++	if (msta->sec_link != msta->pri_link) {
++		mlink = mlink_dereference_protected(msta, msta->sec_link);
++		if (!mlink)
++			return;
++	}
++	mld_setup->seconed_id = cpu_to_le16(mlink->wcid.idx);
++	mld_setup->link_num = hweight16(valid_links);
++
++	mld_setup_link = (struct mld_setup_link *)mld_setup->link_info;
++	mt76_trace(vif, "STA %pM pri_link=%u, pri_wcid=%u, sec_link=%u, sec_wcid=%u\n",
++		   sta->addr, msta->pri_link, le16_to_cpu(mld_setup->primary_id),
++		   msta->sec_link, le16_to_cpu(mld_setup->seconed_id));
++	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++		mlink = mlink_dereference_protected(msta, link_id);
++		mconf = mconf_dereference_protected(msta->vif, link_id);
++
++		mld_setup_link->wcid = cpu_to_le16(mlink->wcid.idx);
++		mld_setup_link->bss_idx = mconf->mt76.idx;
++		mt76_trace(vif, "link_id(%d) wcid(%d) bss_idx(%d)\n",
++		link_id, mld_setup_link->wcid, mld_setup_link->bss_idx);
++		mld_setup_link++;
++	}
++}
++
++static void
++mt7996_mcu_sta_eht_mld_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
++			   struct ieee80211_sta *sta)
++{
++	struct sta_rec_eht_mld *eht_mld;
++	struct tlv *tlv;
++	int i;
++
++	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT_MLD, sizeof(*eht_mld));
++	eht_mld = (struct sta_rec_eht_mld *)tlv;
++
++	for (i = 0; i < ARRAY_SIZE(eht_mld->str_cap); i++)
++		eht_mld->str_cap[i] = 0x7;
++
++	eht_mld->eml_cap = cpu_to_le16(sta->eml_capa);
++	/* TODO:
++	eht_mld->nsep = ;
++	*/
++}
++
+ #if 0
+ static int
+ mt7996_mcu_sta_init_vow(struct mt7996_bss_conf *mconf,
+@@ -2978,6 +3049,8 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ 
+ 	/* tag order is in accordance with firmware dependency. */
+ 	if (link_sta) {
++		struct ieee80211_sta *sta = link_sta->sta;
++
+ 		/* starec hdrt mode */
+ 		mt7996_mcu_sta_hdrt_tlv(dev, skb);
+ 		/* starec bfer */
+@@ -2987,7 +3060,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ 		/* starec vht */
+ 		mt7996_mcu_sta_vht_tlv(skb, link_sta);
+ 		/* starec uapsd */
+-		mt76_connac_mcu_sta_uapsd(skb, vif, link_sta->sta);
++		mt76_connac_mcu_sta_uapsd(skb, vif, sta);
+ 		/* starec amsdu */
+ 		mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, link_sta, mlink);
+ 		/* starec he */
+@@ -3000,6 +3073,12 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ 		mt7996_mcu_sta_muru_tlv(dev, skb, conf, mconf, link_sta);
+ 		/* starec bfee */
+ 		mt7996_mcu_sta_bfee_tlv(dev, skb, conf, mconf, link_sta);
++
++		if (sta->mlo) {
++			/* starec mld setup */
++			mt7996_mcu_sta_mld_setup_tlv(dev, skb, sta, sta->valid_links);
++			mt7996_mcu_sta_eht_mld_tlv(dev, skb, sta);
++		}
+ 	}
+ 
+ #if 0
+@@ -3014,112 +3093,6 @@ out:
+ 				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+ }
+ 
+-static void
+-mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+-			     struct ieee80211_sta *sta)
+-{
+-	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+-	struct sta_rec_mld_setup *mld_setup;
+-	struct mld_setup_link *mld_setup_link;
+-	struct mt7996_link_sta *mlink;
+-	struct mt7996_bss_conf *mconf;
+-	struct tlv *tlv;
+-	unsigned long valid_links = sta->valid_links;
+-	unsigned int link_id;
+-	struct ieee80211_vif *vif = container_of((void *)msta->vif, struct ieee80211_vif,
+-						 drv_priv);
+-
+-	mlink = mlink_dereference_protected(msta, msta->pri_link);
+-	if (!mlink)
+-		return;
+-
+-	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MLD,
+-				      sizeof(*mld_setup) +
+-				      sizeof(struct mld_setup_link) *
+-					     hweight16(sta->valid_links));
+-
+-	mld_setup = (struct sta_rec_mld_setup *)tlv;
+-	memcpy(mld_setup->mld_addr, sta->addr, ETH_ALEN);
+-	mld_setup->setup_wcid = cpu_to_le16(mlink->wcid.idx);
+-	mld_setup->primary_id = cpu_to_le16(mlink->wcid.idx);
+-	if (msta->sec_link != msta->pri_link) {
+-		mlink = mlink_dereference_protected(msta, msta->sec_link);
+-		if (!mlink)
+-			return;
+-	}
+-	mld_setup->seconed_id = cpu_to_le16(mlink->wcid.idx);
+-	mld_setup->link_num = hweight16(sta->valid_links);
+-
+-	mld_setup_link = (struct mld_setup_link *)mld_setup->link_info;
+-	mt76_trace(vif, "STA %pM pri_link=%u, pri_wcid=%u, sec_link=%u, sec_wcid=%u\n",
+-		   sta->addr, msta->pri_link, le16_to_cpu(mld_setup->primary_id),
+-		   msta->sec_link, le16_to_cpu(mld_setup->seconed_id));
+-	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
+-		mlink = mlink_dereference_protected(msta, link_id);
+-		mconf = mconf_dereference_protected(msta->vif, link_id);
+-
+-		mld_setup_link->wcid = cpu_to_le16(mlink->wcid.idx);
+-		mld_setup_link->bss_idx = mconf->mt76.idx;
+-		mt76_trace(vif, "link_id(%d) wcid(%d) bss_idx(%d)\n",
+-		link_id, mld_setup_link->wcid, mld_setup_link->bss_idx);
+-		mld_setup_link++;
+-	}
+-}
+-
+-static void
+-mt7996_mcu_sta_eht_mld_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+-			   struct ieee80211_sta *sta)
+-{
+-	struct sta_rec_eht_mld *eht_mld;
+-	struct tlv *tlv;
+-	int i;
+-
+-	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT_MLD, sizeof(*eht_mld));
+-	eht_mld = (struct sta_rec_eht_mld *)tlv;
+-
+-	for (i = 0; i < ARRAY_SIZE(eht_mld->str_cap); i++)
+-		eht_mld->str_cap[i] = 0x7;
+-
+-	eht_mld->eml_cap = cpu_to_le16(sta->eml_capa);
+-	/* TODO:
+-	eht_mld->nsep = ;
+-	*/
+-}
+-
+-int mt7996_mcu_add_mld_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+-			   struct ieee80211_sta *sta, unsigned long add)
+-{
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+-	unsigned int link_id;
+-
+-	if (!sta->mlo)
+-		return 0;
+-
+-	for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
+-		struct mt7996_bss_conf *mconf =
+-			mconf_dereference_protected(mvif, link_id);
+-		struct mt7996_link_sta *mlink =
+-			mlink_dereference_protected(msta, link_id);
+-		struct sk_buff *skb;
+-		int ret;
+-
+-		skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
+-						      &mlink->wcid,
+-						      MT7996_STA_UPDATE_MAX_SIZE);
+-		if (IS_ERR(skb))
+-			return PTR_ERR(skb);
+-		/* starec mld setup */
+-		mt7996_mcu_sta_mld_setup_tlv(dev, skb, sta);
+-		/* starec eht mld */
+-		mt7996_mcu_sta_eht_mld_tlv(dev, skb, sta);
+-		ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
+-					    MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+-		if (ret)
+-			return ret;
+-	}
+-	return 0;
+-}
+ int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev,
+ 				struct mt7996_bss_conf *mconf,
+ 				struct mt7996_link_sta *mlink)
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 20eaf20d..05b29c90 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -983,6 +983,9 @@ enum {
+ 					 sizeof(struct sta_rec_hdrt) +		\
+ 					 sizeof(struct sta_rec_hdr_trans) +	\
+ 					 sizeof(struct sta_rec_tx_cap) +	\
++					 sizeof(struct sta_rec_mld_setup) +	\
++					 sizeof(struct mld_setup_link) * 3 +	\
++					 sizeof(struct sta_rec_eht_mld) +	\
+ 					 sizeof(struct tlv))
+ 
+ #define MT7996_MAX_BEACON_SIZE		1338
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 3ee8156c..1e507b1c 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1083,8 +1083,6 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
+ 			     struct mt7996_bss_conf *mconf,
+ 			     struct ieee80211_link_sta *link_sta,
+ 			     struct mt7996_link_sta *mlink, bool changed);
+-int mt7996_mcu_add_mld_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+-			   struct ieee80211_sta *sta, unsigned long add);
+ int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef);
+ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag, bool sta);
+ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0168-mtk-mt76-mt7996-Fix-NULL-pointer-crash-when-mac-tx-f.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0168-mtk-mt76-mt7996-Fix-NULL-pointer-crash-when-mac-tx-f.patch
new file mode 100644
index 0000000..e14d128
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0168-mtk-mt76-mt7996-Fix-NULL-pointer-crash-when-mac-tx-f.patch
@@ -0,0 +1,27 @@
+From 8f1b477c5734b3e46dd8d6795c784ef58d553d74 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Thu, 27 Jun 2024 14:54:38 +0800
+Subject: [PATCH 168/199] mtk: mt76: mt7996: Fix NULL pointer crash when mac tx
+ free with a msta that has been removed.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ mt7996/mac.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 2f37d31c..12cc60cb 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1200,7 +1200,7 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ 				struct mt7996_link_sta *mlink =
+ 					rcu_dereference(msta->link[link_id]);
+ 
+-				if (list_empty(&mlink->wcid.poll_list))
++				if (mlink && list_empty(&mlink->wcid.poll_list))
+ 					list_add_tail(&mlink->wcid.poll_list, &mdev->sta_poll_list);
+ 			}
+ 			spin_unlock_bh(&mdev->sta_poll_lock);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0169-mtk-mt76-mt7996-update-adie-efuse-merge-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0169-mtk-mt76-mt7996-update-adie-efuse-merge-support.patch
new file mode 100644
index 0000000..8def00e
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0169-mtk-mt76-mt7996-update-adie-efuse-merge-support.patch
@@ -0,0 +1,376 @@
+From a6bd94f22e1b58c4248e433889071b4e0bd1f283 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 14 Jun 2024 10:14:31 +0800
+Subject: [PATCH 169/199] mtk: mt76: mt7996: update adie efuse merge support
+
+Refactor efuse merge due to FW supporting the efuse merge mcu command
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/eeprom.c | 144 -----------------------------------------
+ mt7996/mcu.c    | 167 +++++++++++++++++++++++++++++++++++++++++++++++-
+ mt7996/mcu.h    |   1 +
+ 3 files changed, 167 insertions(+), 145 deletions(-)
+
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index e8e1d85a..fb0f2953 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -531,146 +531,6 @@ fail:
+ 	return ret;
+ }
+ 
+-static int mt7996_apply_cal_free_data(struct mt7996_dev *dev)
+-{
+-#define MT_EE_CAL_FREE_MAX_SIZE		30
+-#define MT_EE_7977BN_OFFSET		(0x1200 - 0x500)
+-#define MT_EE_END_OFFSET		0xffff
+-	enum adie_type {
+-		ADIE_7975,
+-		ADIE_7976,
+-		ADIE_7977,
+-		ADIE_7978,
+-		ADIE_7979,
+-	};
+-	static const u16 adie_offs_list[][MT_EE_CAL_FREE_MAX_SIZE] = {
+-		[ADIE_7975] = {0x5cd, 0x5cf, 0x5d1, 0x5d3, 0x6c0, 0x6c1, 0x6c2, 0x6c3,
+-			       0x7a1, 0x7a6, 0x7a8, 0x7aa, -1},
+-		[ADIE_7976] = {0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x55, 0x57, 0x59,
+-			       0x70, 0x71, 0x790, 0x791, 0x794, 0x795, 0x7a6, 0x7a8, 0x7aa, -1},
+-		[ADIE_7977] = {0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x55, 0x57, 0x59,
+-			       0x69, 0x6a, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, -1},
+-		[ADIE_7978] = {0x91, 0x95, 0x100, 0x102, 0x104, 0x106, 0x107,
+-			       0x108, 0x109, 0x10a, 0x10b, 0x10c, 0x10e, 0x110, -1},
+-		[ADIE_7979] = {0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x55, 0x57, 0x59,
+-			       0x69, 0x6a, 0x7a, 0x7b, 0x7c, 0x7e, 0x80, -1},
+-	};
+-	static const u16 eep_offs_list[][MT_EE_CAL_FREE_MAX_SIZE] = {
+-		[ADIE_7975] = {0x451, 0x453, 0x455, 0x457, 0x44c, 0x44d, 0x44e, 0x44f,
+-			       0xba1, 0xba6, 0xba8, 0xbaa, -1},
+-		[ADIE_7976] = {0x44c, 0x44d, 0x44e, 0x44f, 0x450,
+-			       0x451, 0x453, 0x455, 0x457, 0x459,
+-			       0x470, 0x471, 0xb90, 0xb91, 0xb94, 0xb95,
+-			       0xba6, 0xba8, 0xbaa, -1},
+-		[ADIE_7977] = {0x124c, 0x124d, 0x124e, 0x124f, 0x1250,
+-			       0x1251, 0x1253, 0x1255, 0x1257, 0x1259,
+-			       0x1269, 0x126a, 0x127a, 0x127b, 0x127c, 0x127d, 0x127e, -1},
+-		[ADIE_7978] = {0xb91, 0xb95, 0x480, 0x482, 0x484, 0x486, 0x487, 0x488, 0x489,
+-			       0x48a, 0x48b, 0x48c, 0x48e, 0x490, -1},
+-		[ADIE_7979] = {0x124c, 0x124d, 0x124e, 0x124f, 0x1250, 0x1251,
+-			       0x1253, 0x1255, 0x1257, 0x1259, 0x1269, 0x126a,
+-			       0x127a, 0x127b, 0x127c, 0x127e, 0x1280, -1},
+-	};
+-	static const u16 adie_base_7996[] = {
+-		0x400, 0x1e00, 0x1200
+-	};
+-	static const u16 adie_base_7992[] = {
+-		0x400, 0x1200, 0x0
+-	};
+-	static const u16 *adie_offs[__MT_MAX_BAND];
+-	static const u16 *eep_offs[__MT_MAX_BAND];
+-	static const u16 *adie_base;
+-	u8 *eeprom = dev->mt76.eeprom.data;
+-	u8 buf[MT7996_EEPROM_BLOCK_SIZE];
+-	int adie_id, band, i, ret;
+-
+-	switch (mt76_chip(&dev->mt76)) {
+-	case 0x7990:
+-		adie_base = adie_base_7996;
+-		/* adie 0 */
+-		if (dev->fem_type == MT7996_FEM_INT && dev->chip_sku != MT7996_SKU_233)
+-			adie_id = ADIE_7975;
+-		else
+-			adie_id = ADIE_7976;
+-		adie_offs[0] = adie_offs_list[adie_id];
+-		eep_offs[0] = eep_offs_list[adie_id];
+-
+-		/* adie 1 */
+-		if (dev->chip_sku == MT7996_SKU_444) {
+-			adie_offs[1] = adie_offs_list[ADIE_7977];
+-			eep_offs[1] = eep_offs_list[ADIE_7977];
+-		}
+-
+-		/* adie 2 */
+-		adie_offs[2] = adie_offs_list[ADIE_7977];
+-		eep_offs[2] = eep_offs_list[ADIE_7977];
+-		break;
+-	case 0x7992:
+-		adie_base = adie_base_7992;
+-		/* adie 0 */
+-		if (dev->chip_sku == MT7992_SKU_44 &&
+-		    dev->fem_type != MT7996_FEM_EXT)
+-			adie_id = ADIE_7975;
+-		else if (dev->chip_sku == MT7992_SKU_24)
+-			adie_id = ADIE_7978;
+-		else
+-			adie_id = ADIE_7976;
+-		adie_offs[0] = adie_offs_list[adie_id];
+-		eep_offs[0] = eep_offs_list[adie_id];
+-
+-		/* adie 1 */
+-		if (dev->chip_sku == MT7992_SKU_44 &&
+-		    dev->fem_type != MT7996_FEM_INT)
+-			adie_id = ADIE_7977;
+-		else if (dev->chip_sku != MT7992_SKU_23)
+-			adie_id = ADIE_7979;
+-		else
+-			break;
+-		adie_offs[1] = adie_offs_list[adie_id];
+-		eep_offs[1] = eep_offs_list[adie_id];
+-		break;
+-	default:
+-		return -EINVAL;
+-	}
+-
+-	for (band = 0; band < __MT_MAX_BAND; band++) {
+-		u16 adie_offset, eep_offset;
+-		u32 block_num, prev_block_num = -1;
+-
+-		if (!adie_offs[band])
+-			continue;
+-
+-		for (i = 0; i < MT_EE_CAL_FREE_MAX_SIZE; i++) {
+-			adie_offset = adie_offs[band][i] + adie_base[band];
+-			eep_offset = eep_offs[band][i];
+-			block_num = adie_offset / MT7996_EEPROM_BLOCK_SIZE;
+-
+-			if (adie_offs[band][i] == MT_EE_END_OFFSET)
+-				break;
+-
+-			if (is_mt7996(&dev->mt76) && dev->chip_sku == MT7996_SKU_444 &&
+-			    band == MT_BAND1)
+-				eep_offset -= MT_EE_7977BN_OFFSET;
+-
+-			if (prev_block_num != block_num) {
+-				ret = mt7996_mcu_get_eeprom(dev, adie_offset, buf);
+-				if (ret) {
+-					if (ret != -EINVAL)
+-						return ret;
+-
+-					prev_block_num = -1;
+-					continue;
+-				}
+-			}
+-
+-			eeprom[eep_offset] = buf[adie_offset % MT7996_EEPROM_BLOCK_SIZE];
+-			prev_block_num = block_num;
+-		}
+-	}
+-
+-	return 0;
+-}
+-
+ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ {
+ 	int ret;
+@@ -685,10 +545,6 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ 
+ 	mt7996_eeprom_load_precal(dev);
+ 
+-	ret = mt7996_apply_cal_free_data(dev);
+-	if (ret)
+-		return ret;
+-
+ 	ret = mt7996_eeprom_parse_hw_cap(dev, &dev->phy);
+ 	if (ret < 0)
+ 		return ret;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 9b3c6e3d..08c92eb3 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -4595,7 +4595,151 @@ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag, bool sta)
+ 				 &req, sizeof(req), true);
+ }
+ 
+-int mt7996_mcu_set_eeprom(struct mt7996_dev *dev)
++static int mt7996_mcu_set_cal_free_data(struct mt7996_dev *dev)
++{
++#define MT_EE_CAL_FREE_MAX_SIZE		30
++#define MT_EE_7977BN_OFFSET		(0x1200 - 0x500)
++#define MT_EE_END_OFFSET		0xffff
++	enum adie_type {
++		ADIE_7975,
++		ADIE_7976,
++		ADIE_7977,
++		ADIE_7978,
++		ADIE_7979,
++	};
++	static const u16 adie_offs_list[][MT_EE_CAL_FREE_MAX_SIZE] = {
++		[ADIE_7975] = {0x5cd, 0x5cf, 0x5d1, 0x5d3, 0x6c0, 0x6c1, 0x6c2, 0x6c3,
++			       0x7a1, 0x7a6, 0x7a8, 0x7aa, -1},
++		[ADIE_7976] = {0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x55, 0x57, 0x59,
++			       0x70, 0x71, 0x790, 0x791, 0x794, 0x795, 0x7a6, 0x7a8, 0x7aa, -1},
++		[ADIE_7977] = {0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x55, 0x57, 0x59,
++			       0x69, 0x6a, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, -1},
++		[ADIE_7978] = {0x91, 0x95, 0x100, 0x102, 0x104, 0x106, 0x107,
++			       0x108, 0x109, 0x10a, 0x10b, 0x10c, 0x10e, 0x110, -1},
++		[ADIE_7979] = {0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x55, 0x57, 0x59,
++			       0x69, 0x6a, 0x7a, 0x7b, 0x7c, 0x7e, 0x80, -1},
++	};
++	static const u16 eep_offs_list[][MT_EE_CAL_FREE_MAX_SIZE] = {
++		[ADIE_7975] = {0x451, 0x453, 0x455, 0x457, 0x44c, 0x44d, 0x44e, 0x44f,
++			       0xba1, 0xba6, 0xba8, 0xbaa, -1},
++		[ADIE_7976] = {0x44c, 0x44d, 0x44e, 0x44f, 0x450,
++			       0x451, 0x453, 0x455, 0x457, 0x459,
++			       0x470, 0x471, 0xb90, 0xb91, 0xb94, 0xb95,
++			       0xba6, 0xba8, 0xbaa, -1},
++		[ADIE_7977] = {0x124c, 0x124d, 0x124e, 0x124f, 0x1250,
++			       0x1251, 0x1253, 0x1255, 0x1257, 0x1259,
++			       0x1269, 0x126a, 0x127a, 0x127b, 0x127c, 0x127d, 0x127e, -1},
++		[ADIE_7978] = {0xb91, 0xb95, 0x480, 0x482, 0x484, 0x486, 0x487, 0x488, 0x489,
++			       0x48a, 0x48b, 0x48c, 0x48e, 0x490, -1},
++		[ADIE_7979] = {0x124c, 0x124d, 0x124e, 0x124f, 0x1250, 0x1251,
++			       0x1253, 0x1255, 0x1257, 0x1259, 0x1269, 0x126a,
++			       0x127a, 0x127b, 0x127c, 0x127e, 0x1280, -1},
++	};
++	static const u16 adie_base_7996[] = {
++		0x400, 0x1e00, 0x1200
++	};
++	static const u16 adie_base_7992[] = {
++		0x400, 0x1200, 0x0
++	};
++	static const u16 *adie_offs[__MT_MAX_BAND];
++	static const u16 *eep_offs[__MT_MAX_BAND];
++	static const u16 *adie_base;
++	int adie_id, band, i, ret;
++
++	switch (mt76_chip(&dev->mt76)) {
++	case 0x7990:
++		adie_base = adie_base_7996;
++		/* adie 0 */
++		if (dev->fem_type == MT7996_FEM_INT && dev->chip_sku != MT7996_SKU_233)
++			adie_id = ADIE_7975;
++		else
++			adie_id = ADIE_7976;
++		adie_offs[0] = adie_offs_list[adie_id];
++		eep_offs[0] = eep_offs_list[adie_id];
++
++		/* adie 1 */
++		if (dev->chip_sku == MT7996_SKU_444) {
++			adie_offs[1] = adie_offs_list[ADIE_7977];
++			eep_offs[1] = eep_offs_list[ADIE_7977];
++		}
++
++		/* adie 2 */
++		adie_offs[2] = adie_offs_list[ADIE_7977];
++		eep_offs[2] = eep_offs_list[ADIE_7977];
++		break;
++	case 0x7992:
++		adie_base = adie_base_7992;
++		/* adie 0 */
++		if (dev->chip_sku == MT7992_SKU_44 &&
++		    dev->fem_type != MT7996_FEM_EXT)
++			adie_id = ADIE_7975;
++		else if (dev->chip_sku == MT7992_SKU_24)
++			adie_id = ADIE_7978;
++		else
++			adie_id = ADIE_7976;
++		adie_offs[0] = adie_offs_list[adie_id];
++		eep_offs[0] = eep_offs_list[adie_id];
++
++		/* adie 1 */
++		if (dev->chip_sku == MT7992_SKU_44 &&
++		    dev->fem_type != MT7996_FEM_INT)
++			adie_id = ADIE_7977;
++		else if (dev->chip_sku != MT7992_SKU_23)
++			adie_id = ADIE_7979;
++		else
++			break;
++		adie_offs[1] = adie_offs_list[adie_id];
++		eep_offs[1] = eep_offs_list[adie_id];
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	for (band = 0; band < __MT_MAX_BAND; band++) {
++		struct {
++			/* fixed field */
++			u8 __rsv[4];
++
++			__le16 tag;
++			__le16 len;
++			__le16 adie_offset;
++			__le16 eep_offset;
++			__le16 count;
++			u8 rsv[2];
++		} __packed req = {
++			.tag = cpu_to_le16(UNI_EFUSE_PATCH),
++			.len = cpu_to_le16(sizeof(req) - 4),
++			.count = cpu_to_le16(1),
++		};
++		u16 adie_offset, eep_offset;
++
++		if (!adie_offs[band])
++			continue;
++
++		for (i = 0; i < MT_EE_CAL_FREE_MAX_SIZE; i++) {
++			adie_offset = adie_offs[band][i] + adie_base[band];
++			eep_offset = eep_offs[band][i];
++
++			if (adie_offs[band][i] == MT_EE_END_OFFSET)
++				break;
++
++			if (is_mt7996(&dev->mt76) && dev->chip_sku == MT7996_SKU_444 &&
++			    band == MT_BAND1)
++				eep_offset -= MT_EE_7977BN_OFFSET;
++
++			req.eep_offset = cpu_to_le16(eep_offset);
++			req.adie_offset = cpu_to_le16(adie_offset);
++			ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(EFUSE_CTRL),
++						&req, sizeof(req), true);
++			if (ret)
++				return ret;
++		}
++	}
++
++	return 0;
++}
++
++int mt7996_mcu_set_eeprom_flash(struct mt7996_dev *dev)
+ {
+ #define MAX_PAGE_IDX_MASK	GENMASK(7, 5)
+ #define PAGE_IDX_MASK		GENMASK(4, 2)
+@@ -4640,6 +4784,27 @@ int mt7996_mcu_set_eeprom(struct mt7996_dev *dev)
+ 	return 0;
+ }
+ 
++int mt7996_mcu_set_eeprom(struct mt7996_dev *dev)
++{
++	struct mt7996_mcu_eeprom req = {
++		.tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.buffer_mode = EE_MODE_EFUSE,
++		.format = EE_FORMAT_WHOLE
++	};
++	int ret;
++
++	if (dev->flash_mode)
++		ret = mt7996_mcu_set_eeprom_flash(dev);
++	else
++		ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(EFUSE_CTRL),
++					&req, sizeof(req), true);
++	if (ret)
++		return ret;
++
++	return mt7996_mcu_set_cal_free_data(dev);
++}
++
+ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf)
+ {
+ 	struct mt7996_mcu_eeprom_info req = {
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 05b29c90..d99e9a60 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -1038,6 +1038,7 @@ enum {
+ 	UNI_EFUSE_BUFFER_MODE,
+ 	UNI_EFUSE_FREE_BLOCK,
+ 	UNI_EFUSE_BUFFER_RD,
++	UNI_EFUSE_PATCH,
+ };
+ 
+ enum {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0170-mtk-mt76-mt7996-support-handle-link_id-in-ap_wireles.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0170-mtk-mt76-mt7996-support-handle-link_id-in-ap_wireles.patch
new file mode 100644
index 0000000..f98bc73
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0170-mtk-mt76-mt7996-support-handle-link_id-in-ap_wireles.patch
@@ -0,0 +1,327 @@
+From e7b25f87fa76aef0603209472187e9cadfbaada6 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Fri, 14 Jun 2024 14:54:44 +0800
+Subject: [PATCH 170/199] mtk: mt76: mt7996: support handle link_id in
+ ap_wireless vendor cmd
+
+Add support handle link_id in ap_wireless vendor command. The link_id is
+used to find the corresponding phy and its band_idx. The band_idx is the
+critical information required by some mcu commands.
+
+This commit also refactor some functions, changing the parameter from
+phy to dev since phy is not required.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt7996/mt7996.h  | 12 +++++------
+ mt7996/mtk_mcu.c | 53 +++++++++++++++++++++++++++++++-----------------
+ mt7996/vendor.c  | 46 ++++++++++++++++++++++++++++-------------
+ mt7996/vendor.h  |  6 +-----
+ 4 files changed, 73 insertions(+), 44 deletions(-)
+
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 1e507b1c..00cd0b61 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1300,8 +1300,8 @@ int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy,
+ 				  struct ieee80211_sta *sta);
+ void mt7996_set_wireless_amsdu(struct ieee80211_hw *hw, u8 en);
+ void mt7996_mcu_set_mimo(struct mt7996_phy *phy);
+-int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val);
+-int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data);
++int mt7996_set_muru_cfg(struct mt7996_dev *dev, u8 action, u8 val);
++int mt7996_mcu_set_muru_cfg(struct mt7996_dev *dev, void *data);
+ void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
+ int mt7996_mcu_set_csi(struct mt7996_phy *phy, u8 mode,
+ 		       u8 cfg, u8 v1, u32 v2, u8 *mac_addr);
+@@ -1332,12 +1332,12 @@ int mt7996_mcu_set_muru_fixed_rate_parameter(struct mt7996_dev *dev, u8 action,
+ int mt7996_mcu_set_txbf_snd_info(struct mt7996_dev *dev, void *para);
+ int mt7996_mcu_set_muru_cmd(struct mt7996_dev *dev, u16 action, int val);
+ int mt7996_mcu_muru_set_prot_frame_thr(struct mt7996_dev *dev, u32 val);
+-int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val);
++int mt7996_mcu_set_bypass_smthint(struct mt7996_dev *dev, u8 band_idx, u8 val);
+ int mt7996_mcu_set_rfeature_trig_type(struct mt7996_dev *dev, u8 band_idx,
+ 				      u8 enable, u8 trig_type);
+-void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type);
+-void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 ofdma_user_cnt);
+-void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type);
++void mt7996_mcu_set_ppdu_tx_type(struct mt7996_dev *dev, u8 ppdu_type);
++void mt7996_mcu_set_nusers_ofdma(struct mt7996_dev *dev, u8 band_idx, u8 ofdma_user_cnt);
++void mt7996_mcu_set_cert(struct mt7996_dev *dev);
+ void mt7996_tm_update_channel(struct mt7996_phy *phy);
+ 
+ int mt7996_mcu_set_vow_drr_dbg(struct mt7996_dev *dev, u32 val);
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index c87daf8e..8bacf29d 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -1082,11 +1082,10 @@ int mt7996_mcu_muru_set_prot_frame_thr(struct mt7996_dev *dev, u32 val)
+ 				 false);
+ }
+ 
+-int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val)
++int mt7996_mcu_set_bypass_smthint(struct mt7996_dev *dev, u8 band_idx, u8 val)
+ {
+ #define BF_PHY_SMTH_INT_BYPASS 0
+ #define BYPASS_VAL 1
+-	struct mt7996_dev *dev = phy->dev;
+ 	struct {
+ 		u8 _rsv[4];
+ 
+@@ -1101,11 +1100,11 @@ int mt7996_mcu_set_bypass_smthint(struct mt7996_phy *phy, u8 val)
+ 		.tag = cpu_to_le16(BF_CFG_PHY),
+ 		.len = cpu_to_le16(sizeof(data) - 4),
+ 		.action = BF_PHY_SMTH_INT_BYPASS,
+-		.band_idx = phy->mt76->band_idx,
++		.band_idx = band_idx,
+ 		.smthintbypass = val,
+ 	};
+ 
+-	if (val != BYPASS_VAL)
++	if (val != BYPASS_VAL || !mt7996_band_valid(dev, band_idx))
+ 		return -EINVAL;
+ 
+ 	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &data, sizeof(data),
+@@ -1174,9 +1173,8 @@ int mt7996_mcu_set_rfeature_trig_type(struct mt7996_dev *dev, u8 band_idx,
+ 	}
+ }
+ 
+-int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data)
++int mt7996_mcu_set_muru_cfg(struct mt7996_dev *dev, void *data)
+ {
+-	struct mt7996_dev *dev = phy->dev;
+ 	struct mt7996_muru *muru;
+ 	struct {
+ 		u8 _rsv[4];
+@@ -1202,7 +1200,7 @@ int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data)
+ 				 sizeof(req), false);
+ }
+ 
+-int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val)
++int mt7996_set_muru_cfg(struct mt7996_dev *dev, u8 action, u8 val)
+ {
+ 	struct mt7996_muru *muru;
+ 	struct mt7996_muru_dl *dl;
+@@ -1222,7 +1220,7 @@ int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val)
+ 		comm->sch_type = MURU_OFDMA_SCH_TYPE_DL;
+ 		muru->cfg_comm = cpu_to_le32(MURU_COMM_SET);
+ 		muru->cfg_dl = cpu_to_le32(MURU_FIXED_DL_TOTAL_USER_CNT);
+-		ret = mt7996_mcu_set_muru_cfg(phy, muru);
++		ret = mt7996_mcu_set_muru_cfg(dev, muru);
+ 		break;
+ 	case MU_CTRL_UL_USER_CNT:
+ 		ul->user_num = val;
+@@ -1230,7 +1228,7 @@ int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val)
+ 		comm->sch_type = MURU_OFDMA_SCH_TYPE_UL;
+ 		muru->cfg_comm = cpu_to_le32(MURU_COMM_SET);
+ 		muru->cfg_ul = cpu_to_le32(MURU_FIXED_UL_TOTAL_USER_CNT);
+-		ret = mt7996_mcu_set_muru_cfg(phy, muru);
++		ret = mt7996_mcu_set_muru_cfg(dev, muru);
+ 		break;
+ 	default:
+ 		break;
+@@ -1240,16 +1238,15 @@ int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val)
+ 	return ret;
+ }
+ 
+-void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type)
++void mt7996_mcu_set_ppdu_tx_type(struct mt7996_dev *dev, u8 ppdu_type)
+ {
+-	struct mt7996_dev *dev = phy->dev;
+ 	int enable_su;
+ 
+ 	switch (ppdu_type) {
+ 	case CAPI_SU:
+ 		enable_su = 1;
+ 		mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SUTX_CTRL, enable_su);
+-		mt7996_set_muru_cfg(phy, MU_CTRL_DL_USER_CNT, 0);
++		mt7996_set_muru_cfg(dev, MU_CTRL_DL_USER_CNT, 0);
+ 		break;
+ 	case CAPI_MU:
+ 		enable_su = 0;
+@@ -1260,16 +1257,35 @@ void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type)
+ 	}
+ }
+ 
+-void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 user_cnt)
++void mt7996_mcu_set_nusers_ofdma(struct mt7996_dev *dev, u8 band_idx, u8 user_cnt)
+ {
+-	struct mt7996_dev *dev = phy->dev;
++	struct mt76_phy *mphy;
++	struct mt7996_phy *phy;
+ 	int enable_su = 0;
++	u8 type;
++
++	if (!mt7996_band_valid(dev, band_idx)) {
++		dev_err(dev->mt76.dev, "Invalid band_idx\n");
++		return;
++	}
++
++	mphy = dev->mt76.phys[band_idx];
++	if (!mphy)
++		return;
++
++	phy = (struct mt7996_phy *)mphy->priv;
+ 
+ 	mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SUTX_CTRL, enable_su);
+-	mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY, MU_DL_ACK_POLICY_SU_BAR);
++	mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_MUDL_ACK_POLICY,
++				MU_DL_ACK_POLICY_SU_BAR);
+ 	mt7996_mcu_muru_set_prot_frame_thr(dev, 9999);
+ 
+-	mt7996_set_muru_cfg(phy, type, user_cnt);
++	if (phy->muru_onoff & OFDMA_UL)
++		type = MU_CTRL_UL_USER_CNT;
++	else
++		type = MU_CTRL_DL_USER_CNT;
++
++	mt7996_set_muru_cfg(dev, type, user_cnt);
+ }
+ 
+ void mt7996_mcu_set_mimo(struct mt7996_phy *phy)
+@@ -1300,9 +1316,8 @@ void mt7996_mcu_set_mimo(struct mt7996_phy *phy)
+ 	mt7996_mcu_set_muru_cmd(dev, UNI_CMD_MURU_SET_FORCE_MU, force_mu);
+ }
+ 
+-void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type)
++void mt7996_mcu_set_cert(struct mt7996_dev *dev)
+ {
+-	struct mt7996_dev *dev = phy->dev;
+ 	struct {
+ 		u8 _rsv[4];
+ 
+@@ -1313,7 +1328,7 @@ void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type)
+ 	} __packed req = {
+ 		.tag = cpu_to_le16(UNI_CMD_CERT_CFG),
+ 		.len = cpu_to_le16(sizeof(req) - 4),
+-		.action = type, /* 1: CAPI Enable */
++		.action = !!dev->cert_mode, /* 1: CAPI Enable */
+ 	};
+ 
+ 	mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(WSYS_CONFIG), &req,
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index ed7c1322..33c682c0 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -31,6 +31,7 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
+ 	[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA] = {.type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO] = {.type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE] = {.type = NLA_U16 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_LINK_ID] = {.type = NLA_U8 },
+ };
+ 
+ static const struct nla_policy
+@@ -182,7 +183,7 @@ static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
+ 		nla_memcpy(muru, tb[MTK_VENDOR_ATTR_MU_CTRL_STRUCT],
+ 			   sizeof(struct mt7996_muru));
+ 
+-		err = mt7996_mcu_set_muru_cfg(phy, muru);
++		err = mt7996_mcu_set_muru_cfg(phy->dev, muru);
+ 		kfree(muru);
+ 	}
+ 
+@@ -953,9 +954,12 @@ static int mt7996_vendor_wireless_ctrl(struct wiphy *wiphy,
+ 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mt7996_dev *dev = phy->dev;
++	struct ieee80211_vif *vif = wdev_to_ieee80211_vif(wdev);
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_bss_conf *mconf;
+ 	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL];
+ 	int err;
+-	u8 val8;
++	u8 val8, band_idx, link_id = 0;
+ 	u16 val16;
+ 	u32 val32;
+ 
+@@ -964,12 +968,28 @@ static int mt7996_vendor_wireless_ctrl(struct wiphy *wiphy,
+ 	if (err)
+ 		return err;
+ 
+-	val32 = CAPI_WIRELESS_CHANGED;
++	if (ieee80211_vif_is_mld(vif) && tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_LINK_ID]) {
++		link_id = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_LINK_ID]);
++
++		if (link_id >= IEEE80211_LINK_UNSPECIFIED)
++			return -EINVAL;
++	}
++
++	rcu_read_lock();
++	mconf = rcu_dereference(mvif->link[link_id]);
++	if (!mconf || !mconf->phy) {
++		rcu_read_unlock();
++		return -EINVAL;
++	}
++
++	band_idx = mconf->phy->mt76->band_idx;
++	rcu_read_unlock();
+ 
+ 	if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA]) {
+ 		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA]);
+-		val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_FIXED_OFDMA) |
+-			 FIELD_PREP(RATE_CFG_VAL, val8);
++		val32 = FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_FIXED_OFDMA) |
++			FIELD_PREP(RATE_CFG_VAL, val8) |
++			FIELD_PREP(RATE_CFG_BAND_IDX, band_idx);
+ 		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+ 			mt7996_set_wireless_vif, &val32);
+ 		if (val8 == 3) /* DL20and80 */
+@@ -980,24 +1000,22 @@ static int mt7996_vendor_wireless_ctrl(struct wiphy *wiphy,
+ 		hw->max_rx_aggregation_subframes = val16;
+ 	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE]) {
+ 		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE]);
+-		mt7996_mcu_set_ppdu_tx_type(phy, val8);
++		mt7996_mcu_set_ppdu_tx_type(dev, val8);
+ 	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA]) {
+ 		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA]);
+-		if (phy->muru_onoff & OFDMA_UL)
+-			mt7996_mcu_set_nusers_ofdma(phy, MU_CTRL_UL_USER_CNT, val8);
+-		else
+-			mt7996_mcu_set_nusers_ofdma(phy, MU_CTRL_DL_USER_CNT, val8);
++		mt7996_mcu_set_nusers_ofdma(dev, band_idx, val8);
+ 	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO]) {
+ 		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO]);
+-		val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_FIXED_MIMO) |
+-			 FIELD_PREP(RATE_CFG_VAL, val8);
++		val32 = FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_FIXED_MIMO) |
++			FIELD_PREP(RATE_CFG_VAL, val8) |
++			FIELD_PREP(RATE_CFG_BAND_IDX, band_idx);
+ 		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+ 			mt7996_set_wireless_vif, &val32);
+ 	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]) {
+ 		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]);
+ 		dev->cert_mode = val8;
+-		mt7996_mcu_set_cert(phy, !!val8);
+-		mt7996_mcu_set_bypass_smthint(phy, val8);
++		mt7996_mcu_set_cert(dev);
++		mt7996_mcu_set_bypass_smthint(dev, band_idx, val8);
+ 	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU]) {
+ 		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU]);
+ 		mt7996_set_wireless_amsdu(hw, val8);
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index 5608a3b4..714f0b3e 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -78,11 +78,6 @@ enum mtk_vendor_attr_mu_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
+ };
+ 
+-enum mtk_capi_control_changed {
+-	CAPI_RFEATURE_CHANGED = BIT(16),
+-	CAPI_WIRELESS_CHANGED = BIT(17),
+-};
+-
+ enum mtk_vendor_attr_rfeature_ctrl {
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
+ 
+@@ -117,6 +112,7 @@ enum mtk_vendor_attr_wireless_ctrl {
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT = 9,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_MU_EDCA, /* reserve */
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_LINK_ID,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0171-mtk-mt76-mt7996-fix-incorrect-indexing-of-MIB-FW-eve.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0171-mtk-mt76-mt7996-fix-incorrect-indexing-of-MIB-FW-eve.patch
new file mode 100644
index 0000000..03262f4
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0171-mtk-mt76-mt7996-fix-incorrect-indexing-of-MIB-FW-eve.patch
@@ -0,0 +1,99 @@
+From 96ef7f1ebdc990ecd0e547710c0929aa7d885a68 Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Fri, 28 Jun 2024 18:03:41 +0800
+Subject: [PATCH 171/199] mtk: mt76: mt7996: fix incorrect indexing of MIB FW
+ event
+
+When porting channel state reporting from Wi-Fi 6 to Wi-Fi 7 codebase, indexing of FW event of MIB data was not properly handled.
+
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ mt7996/mcu.c | 45 +++++++++++++++++++++++++++++----------------
+ 1 file changed, 29 insertions(+), 16 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 08c92eb3..bb4f6170 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -5106,6 +5106,13 @@ int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap)
+ 
+ int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch)
+ {
++	enum {
++		IDX_TX_TIME,
++		IDX_RX_TIME,
++		IDX_OBSS_AIRTIME,
++		IDX_NON_WIFI_TIME,
++		IDX_NUM
++	};
+ 	struct {
+ 		struct {
+ 			u8 band;
+@@ -5115,16 +5122,15 @@ int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch)
+ 			__le16 tag;
+ 			__le16 len;
+ 			__le32 offs;
+-		} data[4];
++		} data[IDX_NUM];
+ 	} __packed req = {
+ 		.hdr.band = phy->mt76->band_idx,
+ 	};
+-	/* strict order */
+ 	static const u32 offs[] = {
+-		UNI_MIB_TX_TIME,
+-		UNI_MIB_RX_TIME,
+-		UNI_MIB_OBSS_AIRTIME,
+-		UNI_MIB_NON_WIFI_TIME,
++		[IDX_TX_TIME] = UNI_MIB_TX_TIME,
++		[IDX_RX_TIME] = UNI_MIB_RX_TIME,
++		[IDX_OBSS_AIRTIME] = UNI_MIB_OBSS_AIRTIME,
++		[IDX_NON_WIFI_TIME] = UNI_MIB_NON_WIFI_TIME,
+ 	};
+ 	struct mt76_channel_state *state = phy->mt76->chan_state;
+ 	struct mt76_channel_state *state_ts = &phy->state_ts;
+@@ -5133,7 +5139,7 @@ int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch)
+ 	struct sk_buff *skb;
+ 	int i, ret;
+ 
+-	for (i = 0; i < 4; i++) {
++	for (i = 0; i < IDX_NUM; i++) {
+ 		req.data[i].tag = cpu_to_le16(UNI_CMD_MIB_DATA);
+ 		req.data[i].len = cpu_to_le16(sizeof(req.data[i]));
+ 		req.data[i].offs = cpu_to_le32(offs[i]);
+@@ -5152,17 +5158,24 @@ int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch)
+ 		goto out;
+ 
+ #define __res_u64(s) le64_to_cpu(res[s].data)
+-	state->cc_tx += __res_u64(1) - state_ts->cc_tx;
+-	state->cc_bss_rx += __res_u64(2) - state_ts->cc_bss_rx;
+-	state->cc_rx += __res_u64(2) + __res_u64(3) - state_ts->cc_rx;
+-	state->cc_busy += __res_u64(0) + __res_u64(1) + __res_u64(2) + __res_u64(3) -
++	state->cc_tx += __res_u64(IDX_TX_TIME) - state_ts->cc_tx;
++	state->cc_bss_rx += __res_u64(IDX_RX_TIME) - state_ts->cc_bss_rx;
++	state->cc_rx += __res_u64(IDX_RX_TIME) +
++			__res_u64(IDX_OBSS_AIRTIME) -
++			state_ts->cc_rx;
++	state->cc_busy += __res_u64(IDX_TX_TIME) +
++			  __res_u64(IDX_RX_TIME) +
++			  __res_u64(IDX_OBSS_AIRTIME) +
++			  __res_u64(IDX_NON_WIFI_TIME) -
+ 			  state_ts->cc_busy;
+-
+ out:
+-	state_ts->cc_tx = __res_u64(1);
+-	state_ts->cc_bss_rx = __res_u64(2);
+-	state_ts->cc_rx = __res_u64(2) + __res_u64(3);
+-	state_ts->cc_busy = __res_u64(0) + __res_u64(1) + __res_u64(2) + __res_u64(3);
++	state_ts->cc_tx = __res_u64(IDX_TX_TIME);
++	state_ts->cc_bss_rx = __res_u64(IDX_RX_TIME);
++	state_ts->cc_rx = __res_u64(IDX_RX_TIME) + __res_u64(IDX_OBSS_AIRTIME);
++	state_ts->cc_busy = __res_u64(IDX_TX_TIME) +
++			    __res_u64(IDX_RX_TIME) +
++			    __res_u64(IDX_OBSS_AIRTIME) +
++			    __res_u64(IDX_NON_WIFI_TIME);
+ #undef __res_u64
+ 
+ 	dev_kfree_skb(skb);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0172-mtk-mt76-mt7996-support-muru-dbg-info-debug-commands.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0172-mtk-mt76-mt7996-support-muru-dbg-info-debug-commands.patch
new file mode 100644
index 0000000..9cda995
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0172-mtk-mt76-mt7996-support-muru-dbg-info-debug-commands.patch
@@ -0,0 +1,82 @@
+From 75b59026efecf9c1f6a9486537566121ea3f9055 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 3 Jun 2024 15:18:05 +0800
+Subject: [PATCH 172/199] mtk: mt76: mt7996: support muru dbg info debug
+ commands
+
+Support enable muru debug functionality by debugfs.
+Usage:
+$ echo <item>-<val> > /sys/kernel/debug/ieee80211/phy0/mt76/muru_dbg
+
+The purpose of this commit is for WiFi 7 R1 cert UL-MU test cases.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt7996/mtk_debugfs.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 44 insertions(+)
+
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index b16ea5fe..b698c68a 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -4326,6 +4326,48 @@ mt7996_drr_info(struct seq_file *s, void *data)
+ 	return 0;
+ }
+ 
++static ssize_t mt7996_muru_dbg_info_set(struct file *file,
++					const char __user *user_buf,
++					size_t count, loff_t *ppos)
++{
++	struct mt7996_dev *dev = file->private_data;
++	char buf[10];
++	u16 item;
++	u8 val;
++	int ret;
++
++	if (count >= sizeof(buf))
++		return -EINVAL;
++
++	if (copy_from_user(buf, user_buf, count))
++		return -EFAULT;
++
++	if (count && buf[count - 1] == '\n')
++		buf[count - 1] = '\0';
++	else
++		buf[count] = '\0';
++
++	if (sscanf(buf, "%hu-%hhu", &item, &val) != 2) {
++		dev_warn(dev->mt76.dev,"format: item-value\n");
++		return -EINVAL;
++	}
++
++	ret = mt7996_mcu_muru_dbg_info(dev, item, val);
++	if (ret) {
++		dev_warn(dev->mt76.dev, "Fail to send mcu cmd.\n");
++		return -EFAULT;
++	}
++
++	return count;
++}
++
++static const struct file_operations fops_muru_dbg_info = {
++	.write = mt7996_muru_dbg_info_set,
++	.open = simple_open,
++	.owner = THIS_MODULE,
++	.llseek = default_llseek,
++};
++
+ void mt7996_mtk_init_band_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ 	/* agg */
+@@ -4447,6 +4489,8 @@ void mt7996_mtk_init_dev_debugfs(struct mt7996_dev *dev, struct dentry *dir)
+ 	/* Drop counters */
+ 	debugfs_create_file("tx_drop_stats", 0400, dir, dev, &mt7996_tx_drop_fops);
+ 	debugfs_create_file("rx_drop_stats", 0400, dir, dev, &mt7996_rx_drop_fops);
++
++	debugfs_create_file("muru_dbg", 0200, dir, dev, &fops_muru_dbg_info);
+ }
+ 
+ #endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0173-mtk-mt76-mt7996-add-kite-if_comb.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0173-mtk-mt76-mt7996-add-kite-if_comb.patch
new file mode 100644
index 0000000..e41cb4b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0173-mtk-mt76-mt7996-add-kite-if_comb.patch
@@ -0,0 +1,68 @@
+From 4e90d97cde027ac38e6f88649d4e028c96999e2e Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 3 Jul 2024 10:34:39 +0800
+Subject: [PATCH 173/199] mtk: mt76: mt7996: add kite if_comb
+
+Add Kite (dual band) if_comb
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/init.c | 33 ++++++++++++++++++++++++++++++---
+ 1 file changed, 30 insertions(+), 3 deletions(-)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 5f38374f..f923ce66 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -46,6 +46,22 @@ static const struct ieee80211_iface_combination if_comb[] = {
+ 	}
+ };
+ 
++static const struct ieee80211_iface_combination if_comb_7992[] = {
++	{
++		.limits = if_limits,
++		.n_limits = ARRAY_SIZE(if_limits),
++		.max_interfaces = MT7996_MAX_INTERFACES * 2,
++		.num_different_channels = 2,
++		.beacon_int_infra_match = true,
++		.radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
++				       BIT(NL80211_CHAN_WIDTH_20) |
++				       BIT(NL80211_CHAN_WIDTH_40) |
++				       BIT(NL80211_CHAN_WIDTH_80) |
++				       BIT(NL80211_CHAN_WIDTH_160),
++		.beacon_int_min_gcd = 100,
++	}
++};
++
+ static const u8 mt7996_if_types_ext_capa[] = {
+ 	[0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
+ 	[2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT,
+@@ -417,11 +433,22 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ 	hw->vif_data_size = sizeof(struct mt7996_vif);
+ 	hw->chanctx_data_size = sizeof(struct mt7996_chanctx);
+ 
+-	wiphy->iface_combinations = if_comb;
+-	wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
++	switch (mt76_chip(mdev)) {
++	case 0x7990:
++		wiphy->iface_combinations = if_comb;
++		wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
++		wiphy->mbssid_max_interfaces = 16 * 3;
++		break;
++	case 0x7992:
++	default:
++		wiphy->iface_combinations = if_comb_7992;
++		wiphy->n_iface_combinations = ARRAY_SIZE(if_comb_7992);
++		wiphy->mbssid_max_interfaces = 16 * 2;
++		break;
++	}
++
+ 	wiphy->reg_notifier = mt7996_regd_notifier;
+ 	wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+-	wiphy->mbssid_max_interfaces = 16 * 3;
+ 
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BSS_COLOR);
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0174-mtk-mt76-mt7996-change-source-of-per-WCID-TX-MPDU-st.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0174-mtk-mt76-mt7996-change-source-of-per-WCID-TX-MPDU-st.patch
new file mode 100644
index 0000000..9d80ebd
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0174-mtk-mt76-mt7996-change-source-of-per-WCID-TX-MPDU-st.patch
@@ -0,0 +1,162 @@
+From ddada5f035de7b87e15de8a87ba1c2491d87edfd Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Mon, 17 Jun 2024 17:16:19 +0800
+Subject: [PATCH 174/199] mtk: mt76: mt7996: change source of per-WCID TX MPDU
+ statistics
+
+Change source of per-WCID TX MPDU statistics from TX-free-done event to PPDU TXS, because WCID from TX-free-done event may not represent the actually used link.
+
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ mt76.h             |  1 +
+ mt76_connac3_mac.h |  5 +++++
+ mt7996/mac.c       | 50 +++++++++++++++++++++++++---------------------
+ 3 files changed, 33 insertions(+), 23 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index f2d12b89..feb7bf16 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -360,6 +360,7 @@ struct mt76_sta_stats {
+ 	u64 tx_bytes;
+ 	/* WED TX */
+ 	u32 tx_packets;		/* unit: MSDU */
++	u32 tx_mpdus;
+ 	u32 tx_retries;
+ 	u32 tx_failed;
+ 	u64 tx_airtime;
+diff --git a/mt76_connac3_mac.h b/mt76_connac3_mac.h
+index db0c29e6..ad8392cd 100644
+--- a/mt76_connac3_mac.h
++++ b/mt76_connac3_mac.h
+@@ -204,6 +204,11 @@ enum tx_frag_idx {
+ 	MT_TX_FRAG_LAST
+ };
+ 
++enum {
++	MT_TXS_MPDU_FMT = 0,
++	MT_TXS_PPDU_FMT = 2,
++};
++
+ #define MT_CT_INFO_APPLY_TXD		BIT(0)
+ #define MT_CT_INFO_COPY_HOST_TXD_ALL	BIT(1)
+ #define MT_CT_INFO_MGMT_FRAME		BIT(2)
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 12cc60cb..97cd5e23 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1206,19 +1206,9 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ 			spin_unlock_bh(&mdev->sta_poll_lock);
+ 			continue;
+ 		} else if (info & MT_TXFREE_INFO_HEADER) {
+-			u32 tx_retries = 0, tx_failed = 0;
+-
+ 			if (!wcid)
+ 				continue;
+ 
+-			tx_retries =
+-				FIELD_GET(MT_TXFREE_INFO_COUNT, info) - 1;
+-			tx_failed = tx_retries +
+-				!!FIELD_GET(MT_TXFREE_INFO_STAT, info);
+-
+-			wcid->stats.tx_retries += tx_retries;
+-			wcid->stats.tx_failed += tx_failed;
+-
+ 			if (FIELD_GET(MT_TXFREE_INFO_STAT, info) == 2) {
+ 				struct mt7996_phy *mphy =
+ 					__mt7996_phy(dev, wcid->phy_idx);
+@@ -1256,9 +1246,10 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ 
+ static bool
+ mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid,
+-		       int pid, __le32 *txs_data)
++		       struct mt76_wcid *link_wcid, int pid, __le32 *txs_data)
+ {
+-	struct mt76_sta_stats *stats = &wcid->stats;
++	u8 fmt = le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT);
++	struct mt76_sta_stats *stats = &link_wcid->stats;
+ 	struct mt76_dev *mdev = &dev->mt76;
+ 	struct ieee80211_tx_info *info;
+ 	struct sk_buff_head list;
+@@ -1270,8 +1261,9 @@ mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid,
+ 
+ 	mt76_tx_status_lock(mdev, &list);
+ 
+-	/* only report MPDU TXS */
+-	if (le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT) == 0) {
++	switch (fmt) {
++	case MT_TXS_MPDU_FMT:
++		/* Only report MPDU TXS to mac80211. */
+ 		skb = mt76_tx_status_skb_get(mdev, wcid, pid, &list);
+ 		if (skb) {
+ 			struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+@@ -1300,6 +1292,15 @@ mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid,
+ 				mvif->probe_send_count[wcid->phy_idx] = 0;
+ 			}
+ 		}
++		break;
++	case MT_TXS_PPDU_FMT:
++		stats->tx_mpdus += le32_get_bits(txs_data[5], MT_TXS5_MPDU_TX_CNT);
++		stats->tx_failed += le32_get_bits(txs_data[6], MT_TXS6_MPDU_FAIL_CNT);
++		stats->tx_retries += le32_get_bits(txs_data[7], MT_TXS7_MPDU_RETRY_CNT);
++		break;
++	default:
++		dev_err(mdev->dev, "Unknown TXS format: %hhu\n", fmt);
++		goto unlock;
+ 	}
+ 
+ 	if (mtk_wed_device_active(&dev->mt76.mmio.wed) && wcid->sta) {
+@@ -1331,6 +1332,7 @@ mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid,
+ 
+ 	if (skb)
+ 		mt76_tx_status_skb_done(mdev, skb, &list);
++unlock:
+ 	mt76_tx_status_unlock(mdev, &list);
+ 
+ 	return !!skb;
+@@ -1338,13 +1340,13 @@ mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid,
+ 
+ static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
+ {
+-	struct mt7996_link_sta *mlink;
+-	struct mt76_wcid *wcid;
++	struct mt76_wcid *wcid, *link_wcid;
+ 	__le32 *txs_data = data;
+ 	u16 wcidx;
+-	u8 pid;
++	u8 band, pid;
+ 
+ 	wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID);
++	band = le32_get_bits(txs_data[2], MT_TXS2_BAND);
+ 	pid = le32_get_bits(txs_data[3], MT_TXS3_PID);
+ 
+ 	if (pid < MT_PACKET_ID_NO_SKB)
+@@ -1359,17 +1361,19 @@ static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
+ 	if (!wcid)
+ 		goto out;
+ 
+-	mt7996_mac_add_txs_skb(dev, wcid, pid, txs_data);
++	link_wcid = mt7996_get_link_wcid(dev, wcidx, band);
++	if (!link_wcid)
++		goto out;
++
++	mt7996_mac_add_txs_skb(dev, wcid, link_wcid, pid, txs_data);
+ 
+-	if (!wcid->sta)
++	if (!link_wcid->sta)
+ 		goto out;
+ 
+-	mlink = wcid_to_mlink(wcid);
+ 	spin_lock_bh(&dev->mt76.sta_poll_lock);
+-	if (list_empty(&mlink->wcid.poll_list))
+-		list_add_tail(&mlink->wcid.poll_list, &dev->mt76.sta_poll_list);
++	if (list_empty(&link_wcid->poll_list))
++		list_add_tail(&link_wcid->poll_list, &dev->mt76.sta_poll_list);
+ 	spin_unlock_bh(&dev->mt76.sta_poll_lock);
+-
+ out:
+ 	rcu_read_unlock();
+ }
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0175-mtk-mt76-mt7996-update-preamble-puncture-support-for.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0175-mtk-mt76-mt7996-update-preamble-puncture-support-for.patch
new file mode 100644
index 0000000..2896b57
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0175-mtk-mt76-mt7996-update-preamble-puncture-support-for.patch
@@ -0,0 +1,301 @@
+From 88307d7d8ce98e7d072f6f4592fe2c5c0162080d Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Tue, 28 May 2024 15:31:39 +0800
+Subject: [PATCH 175/199] mtk: mt76: mt7996: update preamble puncture support
+ for mt7996
+
+Add pp station mode support. Current fw only support one bitmap on a band.
+For extender mode, fw only consider that extender and root AP are both in
+fw mode and detect the same bitmap. So that extender AP/STA can use the
+same bitmap setting.
+Current PP_DSCB_CTRL cmd only use bitmap value in fw implementation.
+
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ mt76_connac_mcu.h |   1 +
+ mt7996/main.c     |  22 ++++++++++
+ mt7996/mcu.c      | 107 +++++++++++++++++++++++++++++++++++++++++++++-
+ mt7996/mcu.h      |  34 +++++++++++++++
+ mt7996/mt7996.h   |   2 +
+ 5 files changed, 165 insertions(+), 1 deletion(-)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 8de91f62..c1becf7e 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1071,6 +1071,7 @@ enum {
+ 	MCU_UNI_EVENT_TESTMODE_CTRL = 0x46,
+ 	MCU_UNI_EVENT_CSI_REPORT = 0x4A,
+ 	MCU_UNI_EVENT_WED_RRO = 0x57,
++	MCU_UNI_EVENT_PP = 0x5a,
+ 	MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
+ 	MCU_UNI_EVENT_ALL_STA_INFO = 0x6e,
+ };
+diff --git a/mt7996/main.c b/mt7996/main.c
+index b5d45d19..032ef5f3 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1241,6 +1241,12 @@ static int mt7996_add_link_sta(struct mt7996_dev *dev,
+ 	if (ret)
+ 		goto error;
+ 
++	if (link_sta->eht_cap.has_eht && conf->vif->type == NL80211_IFTYPE_STATION) {
++		ret = mt7996_mcu_set_pp_sta_dscb(mconf->phy, &conf->chanreq.oper, mconf->mt76.omac_idx);
++		if (ret)
++			goto error;
++	}
++
+ 	ewma_avg_signal_init(&mlink->avg_ack_signal);
+ 
+ 	return 0;
+@@ -2840,6 +2846,10 @@ mt7996_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
+ 	if (ret)
+ 		return ret;
+ 
++	ret = mt7996_mcu_set_pp_alg_ctrl(phy, PP_ALG_SET_TIMER);
++	if (ret)
++		return ret;
++
+ 	return mt7996_set_channel(phy, &ctx->chandef);
+ }
+ 
+@@ -2960,6 +2970,7 @@ mt7996_switch_vif_chanctx(struct ieee80211_hw *hw,
+ 	struct mt7996_chanctx *new_ctx;
+ 	struct mt7996_phy *phy;
+ 	int i, ret = 0;
++	u8 omac_idx;
+ 
+ 	for (i = 0; i < n_vifs; i++) {
+ 		if (vifs[i].old_ctx == vifs[i].new_ctx)
+@@ -3002,6 +3013,17 @@ mt7996_switch_vif_chanctx(struct ieee80211_hw *hw,
+ 
+ 		mutex_unlock(&dev->mt76.mutex);
+ 
++		if (vifs->vif->type == NL80211_IFTYPE_AP && phy->pp_mode == PP_USR_MODE)
++			ret = mt7996_mcu_set_pp_en(phy, PP_USR_MODE,
++						   new_ctx->chandef.punctured);
++		else if (vifs->vif->type == NL80211_IFTYPE_STATION) {
++			omac_idx = get_omac_idx(NL80211_IFTYPE_STATION, phy->omac_mask);
++			ret = mt7996_mcu_set_pp_sta_dscb(phy, &new_ctx->chandef, omac_idx);
++		}
++
++		if (ret)
++			goto out;
++
+ 		ret = mt7996_set_channel(phy, &new_ctx->chandef);
+ 		if (ret)
+ 			goto out;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index bb4f6170..adb71bf5 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1280,6 +1280,39 @@ mt7996_mcu_wed_rro_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	}
+ }
+ 
++static void
++mt7996_mcu_pp_event(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++	struct mt7996_mcu_pp_basic_event *event;
++	struct mt7996_mcu_pp_dscb_event *dscb_event;
++	struct mt7996_phy *phy;
++	struct mt76_phy *mphy;
++	u16 report_bitmap;
++
++	event = (struct mt7996_mcu_pp_basic_event *)skb->data;
++
++	switch (le16_to_cpu(event->tag)) {
++	case UNI_EVENT_STATIC_PP_TAG_CSA_DSCB_IE:
++	case UNI_EVENT_STATIC_PP_TAG_DSCB_IE:
++		if (!mt7996_band_valid(dev, event->band_idx))
++			return;
++
++		mphy = mt76_dev_phy(&dev->mt76, event->band_idx);
++		phy = mphy->priv;
++
++		dscb_event = (struct mt7996_mcu_pp_dscb_event *)event;
++		report_bitmap = le16_to_cpu(dscb_event->punct_bitmap);
++
++		if (phy->punct_bitmap == report_bitmap)
++			return;
++
++		if (phy->pp_mode == PP_FW_MODE)
++			phy->punct_bitmap = report_bitmap;
++
++		break;
++	}
++}
++
+ static void
+ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+@@ -1305,6 +1338,9 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	case MCU_UNI_EVENT_WED_RRO:
+ 		mt7996_mcu_wed_rro_event(dev, skb);
+ 		break;
++	case MCU_UNI_EVENT_PP:
++		mt7996_mcu_pp_event(dev, skb);
++		break;
+ #ifdef CONFIG_MTK_DEBUG
+ 	case MCU_UNI_EVENT_SR:
+ 		mt7996_mcu_rx_sr_event(dev, skb);
+@@ -6274,7 +6310,8 @@ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap)
+ 		u8 force_bitmap_ctrl;
+ 		u8 auto_mode;
+ 		__le16 bitmap;
+-		u8 _rsv2[2];
++		u8 csa_enable;
++		u8 _rsv2;
+ 	} __packed req = {
+ 		.tag = cpu_to_le16(UNI_CMD_PP_EN_CTRL),
+ 		.len = cpu_to_le16(sizeof(req) - 4),
+@@ -6284,6 +6321,7 @@ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap)
+ 		.force_bitmap_ctrl = (mode == PP_USR_MODE) ? 2 : 0,
+ 		.auto_mode = pp_auto,
+ 		.bitmap = cpu_to_le16(bitmap),
++		.csa_enable = false,
+ 	};
+ 
+ 	if (phy->chanctx->chandef.chan->band == NL80211_BAND_2GHZ ||
+@@ -6300,6 +6338,73 @@ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap)
+ 				 &req, sizeof(req), false);
+ }
+ 
++int mt7996_mcu_set_pp_sta_dscb(struct mt7996_phy *phy,
++			       struct cfg80211_chan_def *chandef,
++			       u8 omac_idx)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct {
++		u8 _rsv1[4];
++
++		__le16 tag;
++		__le16 len;
++		u8 band_idx;
++		u8 omac_idx;
++		u8 eht_op_present;
++		u8 dscb_present;
++		__le16 dscb;
++		u8 ctrl;
++		u8 ccfs0;
++		u8 ccfs1;
++		u8 rsv2[3];
++	} __packed req = {
++		.tag = cpu_to_le16(UNI_CMD_PP_DSCB_CTRL),
++		.len = cpu_to_le16(sizeof(req) - 4),
++
++		.band_idx = phy->mt76->band_idx,
++		.omac_idx = omac_idx,
++		.eht_op_present = true,
++		.dscb_present = !!chandef->punctured,
++		.dscb = cpu_to_le16(chandef->punctured),
++		.ctrl = 0,
++		.ccfs0 = ieee80211_frequency_to_channel(chandef->center_freq1),
++		.ccfs1 = ieee80211_frequency_to_channel(chandef->center_freq1),
++	};
++
++	if (phy->chanctx->chandef.chan->band == NL80211_BAND_2GHZ ||
++	    phy->punct_bitmap == chandef->punctured)
++		return 0;
++
++	switch (chandef->width) {
++	case NL80211_CHAN_WIDTH_320:
++		req.ctrl |= IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ;
++		if (chandef->chan->hw_value < req.ccfs1)
++			req.ccfs0 -= 16;
++		else
++			req.ccfs0 += 16;
++		break;
++	case NL80211_CHAN_WIDTH_160:
++		req.ctrl |= IEEE80211_EHT_OPER_CHAN_WIDTH_160MHZ;
++		if (chandef->chan->hw_value < req.ccfs1)
++			req.ccfs0 -= 8;
++		else
++			req.ccfs0 += 8;
++		break;
++	case NL80211_CHAN_WIDTH_80:
++		req.ctrl |= IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ;
++		req.ccfs0 = 0;
++		break;
++	default:
++		return 0;
++		break;
++	}
++
++	phy->punct_bitmap = cpu_to_le16(chandef->punctured);
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(PP),
++				 &req, sizeof(req), false);
++}
++
+ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index d99e9a60..011b6c7a 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -1162,6 +1162,7 @@ enum {
+ 
+ enum {
+ 	UNI_CMD_PP_EN_CTRL,
++	UNI_CMD_PP_DSCB_CTRL,
+ };
+ 
+ enum pp_mode {
+@@ -1170,6 +1171,39 @@ enum pp_mode {
+ 	PP_USR_MODE,
+ };
+ 
++enum {
++	UNI_EVENT_PP_TAG_ALG_CTRL = 1,
++	UNI_EVENT_STATIC_PP_TAG_DSCB_IE,
++	UNI_EVENT_STATIC_PP_TAG_CSA_DSCB_IE,
++	UNI_EVENT_PP_SHOW_INFO,
++};
++
++struct mt7996_mcu_pp_basic_event {
++	struct mt7996_mcu_rxd rxd;
++
++	u8 __rsv1[4];
++
++	__le16 tag;
++	__le16 len;
++	u8 band_idx;
++	u8 __rsv2[3];
++} __packed;
++
++struct mt7996_mcu_pp_dscb_event {
++	struct mt7996_mcu_rxd rxd;
++
++	u8 __rsv1[4];
++
++	__le16 tag;
++	__le16 len;
++	u8 band_idx;
++	u8 omac_idx;
++	u8 new_dscb;
++	u8 __rsv2;
++	__le16 punct_bitmap;
++	u8 __rsv3[2];
++} __packed;
++
+ enum {
+ 	UNI_CMD_SCS_SEND_DATA,
+ 	UNI_CMD_SCS_SET_PD_THR_RANGE = 2,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 00cd0b61..f31d3f36 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1276,6 +1276,8 @@ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ 				     struct mt7996_link_sta *mlink);
+ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode);
+ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap);
++int mt7996_mcu_set_pp_sta_dscb(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef,
++			       u8 omac_idx);
+ int mt7996_mcu_set_eml_omn(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 link_id,
+ 			   struct ieee80211_sta *sta, struct mt7996_eml_omn *eml_omn);
+ #ifdef CONFIG_MAC80211_DEBUGFS
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0176-mtk-mt76-Add-dynamic-pp-vendor-and-debug-pp-algo-cmd.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0176-mtk-mt76-Add-dynamic-pp-vendor-and-debug-pp-algo-cmd.patch
new file mode 100644
index 0000000..7b2b8de
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0176-mtk-mt76-Add-dynamic-pp-vendor-and-debug-pp-algo-cmd.patch
@@ -0,0 +1,425 @@
+From a2ca9b9f2be2bd336db4c3e6a2f6693d6e2236b4 Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Tue, 28 May 2024 15:39:06 +0800
+Subject: [PATCH 176/199] mtk: mt76: Add dynamic pp vendor and debug pp algo
+ cmd support
+
+Add dynamic pp vendor and debug pp algo cmd support.
+1. Add support channel switch with a punct bitmap.
+2. Add pp event for fw mode and trigger a channel switch by hostapd.
+3. Add pp algo dump cmd to get current fw punct bitmap and mode.
+
+When extender sta have not connected to root ap and the chandef is null.
+mt76 just ignore the event. Once the sta connect to ap, the sta part would
+send a new fw cmd to update the pp bitmap.
+
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ mt7996/mcu.c         |  70 ++++++++++++++++++++++++++++-
+ mt7996/mcu.h         |  38 ++++++++++++++++
+ mt7996/mt7996.h      |   2 +
+ mt7996/mtk_debugfs.c |  14 ++++++
+ mt7996/vendor.c      | 103 +++++++++++++++++++++++++++++++------------
+ mt7996/vendor.h      |   8 +++-
+ 6 files changed, 205 insertions(+), 30 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index adb71bf5..00bc675e 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1280,6 +1280,39 @@ mt7996_mcu_wed_rro_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	}
+ }
+ 
++void
++mt7996_dump_pp_statistic_event(struct mt7996_dev *dev,
++			 struct mt7996_mcu_pp_alg_ctrl_event *event)
++{
++	u32 unit_time = le32_to_cpu(event->pp_timer_intv);
++
++	dev_info(dev->mt76.dev, "band idx = %u\n", le32_to_cpu(event->band_idx));
++	dev_info(dev->mt76.dev, "x2 value = %u\n", le32_to_cpu(event->thr_x2_value));
++	dev_info(dev->mt76.dev, "x2 shift = %u\n", le32_to_cpu(event->thr_x2_shift));
++	dev_info(dev->mt76.dev, "x3 value = %u\n", le32_to_cpu(event->thr_x3_value));
++	dev_info(dev->mt76.dev, "x3 shift = %u\n", le32_to_cpu(event->thr_x3_shift));
++	dev_info(dev->mt76.dev, "x4 value = %u\n", le32_to_cpu(event->thr_x4_value));
++	dev_info(dev->mt76.dev, "x4 shift = %u\n", le32_to_cpu(event->thr_x4_shift));
++	dev_info(dev->mt76.dev, "x5 value = %u\n", le32_to_cpu(event->thr_x5_value));
++	dev_info(dev->mt76.dev, "x5 shift = %u\n", le32_to_cpu(event->thr_x5_shift));
++	dev_info(dev->mt76.dev, "x6 value = %u\n", le32_to_cpu(event->thr_x6_value));
++	dev_info(dev->mt76.dev, "x6 shift = %u\n", le32_to_cpu(event->thr_x6_shift));
++	dev_info(dev->mt76.dev, "x7 value = %u\n", le32_to_cpu(event->thr_x7_value));
++	dev_info(dev->mt76.dev, "x7 shift = %u\n", le32_to_cpu(event->thr_x7_shift));
++	dev_info(dev->mt76.dev, "x8 value = %u\n", le32_to_cpu(event->thr_x8_value));
++	dev_info(dev->mt76.dev, "x8 shift = %u\n", le32_to_cpu(event->thr_x8_shift));
++	dev_info(dev->mt76.dev, "sw_pp_time = %u (Unit: %u ms)\n",
++		 le32_to_cpu(event->sw_pp_time), unit_time);
++	dev_info(dev->mt76.dev, "hw_pp_time = %u (Unit: %u ms)\n",
++		 le32_to_cpu(event->hw_pp_time), unit_time);
++	dev_info(dev->mt76.dev, "no_pp_time = %u (Unit: %u ms)\n",
++		 le32_to_cpu(event->no_pp_time), unit_time);
++	dev_info(dev->mt76.dev, "auto_bw_time = %u (Unit: %u ms)\n",
++		 le32_to_cpu(event->auto_bw_time), unit_time);
++	dev_info(dev->mt76.dev, "punct_bitmap = 0x%04x\n",
++		 le16_to_cpu(event->punct_bitmap));
++}
++
+ static void
+ mt7996_mcu_pp_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+@@ -1306,9 +1339,13 @@ mt7996_mcu_pp_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 		if (phy->punct_bitmap == report_bitmap)
+ 			return;
+ 
+-		if (phy->pp_mode == PP_FW_MODE)
++		if (phy->pp_mode == PP_FW_MODE) {
+ 			phy->punct_bitmap = report_bitmap;
+-
++			mt7996_vendor_pp_bitmap_update(phy, report_bitmap);
++		}
++		break;
++	case UNI_EVENT_PP_TAG_ALG_CTRL:
++		mt7996_dump_pp_statistic_event(dev, (struct mt7996_mcu_pp_alg_ctrl_event *)event);
+ 		break;
+ 	}
+ }
+@@ -6405,6 +6442,35 @@ int mt7996_mcu_set_pp_sta_dscb(struct mt7996_phy *phy,
+ 				 &req, sizeof(req), false);
+ }
+ 
++int mt7996_mcu_set_pp_alg_ctrl(struct mt7996_phy *phy, u8 action)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct {
++		u8 _rsv1[4];
++
++		__le16 tag;
++		__le16 len;
++
++		__le32 pp_timer_intv;
++		__le32 rsv2[14];
++		u8 band_idx;
++		u8 pp_action;
++		u8 reset;
++		u8 _rsv3;
++	} __packed req = {
++		.tag = cpu_to_le16(UNI_CMD_PP_ALG_CTRL),
++		.len = cpu_to_le16(sizeof(req) - 4),
++
++		.pp_timer_intv = action == PP_ALG_SET_TIMER ? 2000 : 0,
++		.band_idx = phy->mt76->band_idx,
++		.pp_action = action,
++		.reset = 0,
++	};
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(PP),
++				 &req, sizeof(req), false);
++}
++
+ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 011b6c7a..01eb0ea1 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -1162,6 +1162,7 @@ enum {
+ 
+ enum {
+ 	UNI_CMD_PP_EN_CTRL,
++	UNI_CMD_PP_ALG_CTRL,
+ 	UNI_CMD_PP_DSCB_CTRL,
+ };
+ 
+@@ -1171,6 +1172,11 @@ enum pp_mode {
+ 	PP_USR_MODE,
+ };
+ 
++enum pp_alg_action {
++	PP_ALG_SET_TIMER,
++	PP_ALG_GET_STATISTICS = 2,
++};
++
+ enum {
+ 	UNI_EVENT_PP_TAG_ALG_CTRL = 1,
+ 	UNI_EVENT_STATIC_PP_TAG_DSCB_IE,
+@@ -1204,6 +1210,38 @@ struct mt7996_mcu_pp_dscb_event {
+ 	u8 __rsv3[2];
+ } __packed;
+ 
++struct mt7996_mcu_pp_alg_ctrl_event {
++	struct mt7996_mcu_rxd rxd;
++
++	u8 __rsv1[4];
++
++	__le16 tag;
++	__le16 len;
++
++	__le32 pp_timer_intv;
++	__le32 thr_x2_value;
++	__le32 thr_x2_shift;
++	__le32 thr_x3_value;
++	__le32 thr_x3_shift;
++	__le32 thr_x4_value;
++	__le32 thr_x4_shift;
++	__le32 thr_x5_value;
++	__le32 thr_x5_shift;
++	__le32 thr_x6_value;
++	__le32 thr_x6_shift;
++	__le32 thr_x7_value;
++	__le32 thr_x7_shift;
++	__le32 thr_x8_value;
++	__le32 thr_x8_shift;
++	__le32 sw_pp_time;
++	__le32 hw_pp_time;
++	__le32 no_pp_time;
++	__le32 auto_bw_time;
++	u8 band_idx;
++	u8 __rsv2;
++	__le16 punct_bitmap;
++} __packed;
++
+ enum {
+ 	UNI_CMD_SCS_SEND_DATA,
+ 	UNI_CMD_SCS_SET_PD_THR_RANGE = 2,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index f31d3f36..74c32827 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1278,6 +1278,7 @@ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode);
+ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap);
+ int mt7996_mcu_set_pp_sta_dscb(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef,
+ 			       u8 omac_idx);
++int mt7996_mcu_set_pp_alg_ctrl(struct mt7996_phy *phy, u8 action);
+ int mt7996_mcu_set_eml_omn(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 link_id,
+ 			   struct ieee80211_sta *sta, struct mt7996_eml_omn *eml_omn);
+ #ifdef CONFIG_MAC80211_DEBUGFS
+@@ -1307,6 +1308,7 @@ int mt7996_mcu_set_muru_cfg(struct mt7996_dev *dev, void *data);
+ void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
+ int mt7996_mcu_set_csi(struct mt7996_phy *phy, u8 mode,
+ 		       u8 cfg, u8 v1, u32 v2, u8 *mac_addr);
++int mt7996_vendor_pp_bitmap_update(struct mt7996_phy *phy, u16 bitmap);
+ #endif
+ 
+ int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index b698c68a..093f3c69 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -4368,6 +4368,18 @@ static const struct file_operations fops_muru_dbg_info = {
+ 	.llseek = default_llseek,
+ };
+ 
++static int mt7996_pp_alg_show(struct seq_file *s, void *data)
++{
++	struct mt7996_phy *phy = s->private;
++	struct mt7996_dev *dev = phy->dev;
++
++	dev_info(dev->mt76.dev, "pp_mode = %d\n", phy->pp_mode);
++	mt7996_mcu_set_pp_alg_ctrl(phy, PP_ALG_GET_STATISTICS);
++
++	return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(mt7996_pp_alg);
++
+ void mt7996_mtk_init_band_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ 	/* agg */
+@@ -4390,6 +4402,8 @@ void mt7996_mtk_init_band_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ 
+ 	debugfs_create_file("thermal_enable", 0600, dir, phy, &fops_thermal_enable);
+ 	debugfs_create_file("scs_enable", 0200, dir, phy, &fops_scs_enable);
++
++	debugfs_create_file("pp_alg", 0200, dir, phy, &mt7996_pp_alg_fops);
+ }
+ 
+ void mt7996_mtk_init_dev_debugfs(struct mt7996_dev *dev, struct dentry *dir)
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index 33c682c0..d7973c5e 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -96,7 +96,9 @@ ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
+ static struct nla_policy
+ pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
+ 	[MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
+-	[MTK_VENDOR_ATTR_PP_BAND_IDX] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_PP_LINK_ID] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_PP_BITMAP] = { .type = NLA_U16 },
++	[MTK_VENDOR_ATTR_PP_CURR_FREQ] = { .type = NLA_U32 },
+ };
+ 
+ static const struct nla_policy
+@@ -805,31 +807,44 @@ static int mt7996_vendor_pp_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
+ 				 const void *data, int data_len)
+ {
+ 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct ieee80211_vif *vif = wdev_to_ieee80211_vif(wdev);
+ 	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_PP_CTRL];
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_phy *phy;
+-	struct mt76_phy *mphy;
+ 	struct cfg80211_chan_def *chandef;
++	struct mt7996_bss_conf *mconf;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	int err;
+-	u8 val8, band_idx = 0;
++	u8 mode = 0, link_id = 0;
++	u16 punct_bitmap = 0;
+ 
+ 	err = nla_parse(tb, MTK_VENDOR_ATTR_PP_CTRL_MAX, data, data_len,
+ 			pp_ctrl_policy, NULL);
+ 
+-	if (tb[MTK_VENDOR_ATTR_PP_BAND_IDX]) {
+-		band_idx = nla_get_u8(tb[MTK_VENDOR_ATTR_PP_BAND_IDX]);
+-	}
++	if (tb[MTK_VENDOR_ATTR_PP_MODE])
++		mode = nla_get_u8(tb[MTK_VENDOR_ATTR_PP_MODE]);
++	else
++		return -EINVAL;
+ 
+-	if (!mt7996_band_valid(dev, band_idx))
+-		goto error;
++	if (ieee80211_vif_is_mld(vif) && tb[MTK_VENDOR_ATTR_PP_LINK_ID]) {
++		link_id = nla_get_u8(tb[MTK_VENDOR_ATTR_PP_LINK_ID]);
++		if (link_id >= IEEE80211_LINK_UNSPECIFIED)
++			return -EINVAL;
++	}
+ 
+-	mphy = dev->mt76.phys[band_idx];
+-	if (!mphy)
++	rcu_read_lock();
++	mconf = rcu_dereference(mvif->link[link_id]);
++	if (!mconf) {
++		rcu_read_unlock();
+ 		goto error;
++	}
+ 
+-	phy = (struct mt7996_phy *)mphy->priv;
+-	if (!phy)
++	phy = mconf->phy;
++	if (!phy) {
++		rcu_read_unlock();
+ 		goto error;
++	}
++	rcu_read_unlock();
+ 
+ 	chandef = &phy->chanctx->chandef;
+ 	if (!chandef)
+@@ -838,28 +853,53 @@ static int mt7996_vendor_pp_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
+ 	if (chandef->chan->band == NL80211_BAND_2GHZ)
+ 		return 0;
+ 
+-	if (tb[MTK_VENDOR_ATTR_PP_MODE]) {
+-		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_PP_MODE]);
+-		switch (val8) {
+-		case PP_DISABLE:
+-		case PP_FW_MODE:
+-			err = mt7996_mcu_set_pp_en(phy, val8, 0);
+-			break;
+-		case PP_USR_MODE:
+-			/* handled by add_chanctx */
+-			err = 0;
+-			break;
+-		default:
+-			err = -EINVAL;
+-		}
++	switch (mode) {
++	case PP_USR_MODE:
++		if (tb[MTK_VENDOR_ATTR_PP_BITMAP])
++			punct_bitmap = nla_get_u16(tb[MTK_VENDOR_ATTR_PP_BITMAP]);
++		fallthrough;
++	case PP_FW_MODE:
++	case PP_DISABLE:
++		err = mt7996_mcu_set_pp_en(phy, mode, punct_bitmap);
++		break;
++	default:
++		return -EINVAL;
+ 	}
+ 
+ 	return err;
+ error:
+-	dev_err(dev->mt76.dev, "Invalid band idx: %d\n", band_idx);
++	dev_err(dev->mt76.dev, "Invalid link id: %d\n", link_id);
+ 	return -EINVAL;
+ }
+ 
++int mt7996_vendor_pp_bitmap_update(struct mt7996_phy *phy, u16 bitmap)
++{
++	struct sk_buff *skb;
++	struct mt76_phy *mphy = phy->mt76;
++	struct cfg80211_chan_def *chandef = &phy->chanctx->chandef;
++
++	if (!chandef)
++		return 0;
++
++	skb = cfg80211_vendor_event_alloc(mphy->hw->wiphy, NULL, 20,
++					  MTK_NL80211_VENDOR_EVENT_PP_BMP_UPDATE,
++					  GFP_ATOMIC);
++
++	if (!skb)
++		return -ENOMEM;
++
++	if (nla_put_u16(skb, MTK_VENDOR_ATTR_PP_BITMAP, bitmap) ||
++	    nla_put_u32(skb, MTK_VENDOR_ATTR_PP_CURR_FREQ,
++			chandef->chan->center_freq)) {
++		dev_kfree_skb(skb);
++		return -ENOMEM;
++	}
++
++	cfg80211_vendor_event(skb, GFP_ATOMIC);
++
++	return 0;
++}
++
+ static int mt7996_vendor_rfeature_ctrl(struct wiphy *wiphy,
+ 				       struct wireless_dev *wdev,
+ 				       const void *data,
+@@ -1490,10 +1530,19 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 	},
+ };
+ 
++static const struct nl80211_vendor_cmd_info mt7996_vendor_events[] = {
++	[MTK_NL80211_VENDOR_EVENT_PP_BMP_UPDATE] = {
++		.vendor_id = MTK_NL80211_VENDOR_ID,
++		.subcmd = MTK_NL80211_VENDOR_EVENT_PP_BMP_UPDATE,
++	},
++};
++
+ void mt7996_vendor_register(struct mt7996_phy *phy)
+ {
+ 	phy->mt76->hw->wiphy->vendor_commands = mt7996_vendor_commands;
+ 	phy->mt76->hw->wiphy->n_vendor_commands = ARRAY_SIZE(mt7996_vendor_commands);
++	phy->mt76->hw->wiphy->vendor_events = mt7996_vendor_events;
++	phy->mt76->hw->wiphy->n_vendor_events = ARRAY_SIZE(mt7996_vendor_events);
+ 
+ 	INIT_LIST_HEAD(&phy->csi.list);
+ 	spin_lock_init(&phy->csi.lock);
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index 714f0b3e..ca4f00ad 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -21,6 +21,10 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_EML_CTRL = 0xd3,
+ };
+ 
++enum mtk_nl80211_vendor_events {
++	MTK_NL80211_VENDOR_EVENT_PP_BMP_UPDATE = 0x5,
++};
++
+ enum mtk_vendor_attr_edcca_ctrl {
+ 	MTK_VENDOR_ATTR_EDCCA_THRESHOLD_INVALID = 0,
+ 
+@@ -222,7 +226,9 @@ enum mtk_vendor_attr_pp_ctrl {
+ 	MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
+ 
+ 	MTK_VENDOR_ATTR_PP_MODE,
+-	MTK_VENDOR_ATTR_PP_BAND_IDX,
++	MTK_VENDOR_ATTR_PP_LINK_ID,
++	MTK_VENDOR_ATTR_PP_BITMAP,
++	MTK_VENDOR_ATTR_PP_CURR_FREQ,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_PP_CTRL,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0177-mtk-mt76-mt7996-disable-MAT-and-set-force-link-for-4.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0177-mtk-mt76-mt7996-disable-MAT-and-set-force-link-for-4.patch
new file mode 100644
index 0000000..64389e3
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0177-mtk-mt76-mt7996-disable-MAT-and-set-force-link-for-4.patch
@@ -0,0 +1,40 @@
+From 554f2dd335deaf511b9261603a3e6c62f1183639 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 8 Jul 2024 11:05:31 +0800
+Subject: [PATCH 177/199] mtk: mt76: mt7996: disable MAT and set force-link for
+ 4-addr NULL func data frame
+
+This is a temporary solution to solve a WDS connection problem.
+
+We found that WDS connection occasionally failed because of dropping
+of the 4-addr NULL func data frame, which is used by STA to enstablish
+WDS conection. It seems like the dropping is originated from the STA
+side HW's incorrect address translation.
+
+Disabling MAT can prevent the connection problem.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ mt7996/mac.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 97cd5e23..a8985a3b 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -759,6 +759,12 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
+ 	    (multicast || unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE)) ||
+ 	     info->flags & IEEE80211_TX_CTL_INJECTED))
+ 		txwi[5] |= cpu_to_le32(MT_TXD5_FL);
++
++	if (unlikely(ieee80211_is_nullfunc(fc)) && ieee80211_has_a4(fc) &&
++	    ieee80211_vif_is_mld(info->control.vif)) {
++		txwi[5] |= cpu_to_le32(MT_TXD5_FL);
++		txwi[6] |= cpu_to_le32(MT_TXD6_DIS_MAT);
++	}
+ }
+ 
+ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0178-mtk-mt76-mt7996-add-per-STA-TX-MSDU-failed-and-retri.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0178-mtk-mt76-mt7996-add-per-STA-TX-MSDU-failed-and-retri.patch
new file mode 100644
index 0000000..eb0dbbc
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0178-mtk-mt76-mt7996-add-per-STA-TX-MSDU-failed-and-retri.patch
@@ -0,0 +1,249 @@
+From c9cc947b2922dde53c71ce871ac869039d740999 Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Thu, 4 Jul 2024 14:00:16 +0800
+Subject: [PATCH 178/199] mtk: mt76: mt7996: add per-STA TX MSDU failed and
+ retried counts
+
+Record per-STA TX MSDU failed and retried counts for debugging.
+
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ mt76.h            |  2 ++
+ mt76_connac_mcu.h |  3 ++
+ mt7996/mac.c      | 39 ++++++++++++++++++++++++-
+ mt7996/mcu.c      | 74 +++++++++++------------------------------------
+ mt7996/mcu.h      |  8 +++++
+ mt7996/mt7996.h   |  1 -
+ 6 files changed, 68 insertions(+), 59 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index feb7bf16..fa4a3e70 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -360,6 +360,8 @@ struct mt76_sta_stats {
+ 	u64 tx_bytes;
+ 	/* WED TX */
+ 	u32 tx_packets;		/* unit: MSDU */
++	u32 tx_packets_retried;
++	u32 tx_packets_failed;
+ 	u32 tx_mpdus;
+ 	u32 tx_retries;
+ 	u32 tx_failed;
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index c1becf7e..545de4ae 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1402,6 +1402,8 @@ enum {
+ 	UNI_OFFLOAD_OFFLOAD_BMC_RPY_DETECT,
+ };
+ 
++#define PER_STA_INFO_MAX_NUM	90
++
+ enum UNI_PER_STA_INFO_TAG {
+ 	UNI_PER_STA_RSSI,
+ 	UNI_PER_STA_CONTENTION_RX_RATE,
+@@ -1411,6 +1413,7 @@ enum UNI_PER_STA_INFO_TAG {
+ 	UNI_PER_STA_TX_CNT,
+ 	UNI_PER_STA_TID_SN_GET,
+ 	UNI_PER_STA_TID_SN_SET,
++	UNI_PER_STA_PKT_CNT,
+ 	UNI_PER_STA_MAX_NUM
+ };
+ 
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index a8985a3b..fae24cad 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2431,6 +2431,43 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
+ 	rcu_read_unlock();
+ }
+ 
++static int mt7996_mac_sta_poll(struct mt76_dev *dev)
++{
++	u16 sta_list[PER_STA_INFO_MAX_NUM];
++	struct mt7996_link_sta *mlink;
++	int i, ret;
++
++	spin_lock_bh(&dev->sta_poll_lock);
++	for (i = 0; i < PER_STA_INFO_MAX_NUM; ++i) {
++		if (list_empty(&dev->sta_poll_list))
++			break;
++
++		mlink = list_first_entry(&dev->sta_poll_list,
++					 struct mt7996_link_sta,
++					 wcid.poll_list);
++		list_del_init(&mlink->wcid.poll_list);
++		sta_list[i] = mlink->wcid.idx;
++	}
++	spin_unlock_bh(&dev->sta_poll_lock);
++
++	if (i == 0)
++		return 0;
++
++	ret = mt7996_mcu_get_per_sta_info(dev, UNI_PER_STA_RSSI, i, sta_list);
++	if (ret)
++		dev_err(dev->dev, "Failed to update RSSI of polled STAs.\n");
++
++	ret = mt7996_mcu_get_per_sta_info(dev, UNI_PER_STA_SNR, i, sta_list);
++	if (ret)
++		dev_err(dev->dev, "Failed to update SNR of polled STAs.\n");
++
++	ret = mt7996_mcu_get_per_sta_info(dev, UNI_PER_STA_PKT_CNT, i, sta_list);
++	if (ret)
++		dev_err(dev->dev, "Failed to update MSDU counts of polled STAs.\n");
++
++	return ret;
++}
++
+ void mt7996_mac_work(struct work_struct *work)
+ {
+ 	struct mt76_phy *mphy = (struct mt76_phy *)container_of(work, struct mt76_phy,
+@@ -2453,7 +2490,7 @@ void mt7996_mac_work(struct work_struct *work)
+ 			if (i == mphy->band_idx) {
+ 				mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_RATE);
+ 				mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_AIRTIME);
+-				mt7996_mcu_get_signal_status(mdev);
++				mt7996_mac_sta_poll(mdev);
+ 				// if (mtk_wed_device_active(&mdev->mmio.wed)) {
+ 					mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_ADM_STAT);
+ 					mt7996_mcu_get_all_sta_info(mdev, UNI_ALL_STA_TXRX_MSDU_COUNT);
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 00bc675e..857d2826 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -6017,7 +6017,6 @@ int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u16 val)
+ int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
+ 				u16 sta_num, u16 *sta_list)
+ {
+-#define PER_STA_INFO_MAX_NUM	90
+ 	struct mt7996_mcu_per_sta_info_event *res;
+ 	struct mt7996_link_sta *mlink;
+ 	struct mt76_wcid *wcid;
+@@ -6097,6 +6096,23 @@ int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
+ 			}
+ 		}
+ 		break;
++	case UNI_PER_STA_PKT_CNT:
++		for (i = 0; i < sta_num; ++i) {
++			wlan_idx = le16_to_cpu(res->msdu_cnt[i].wlan_idx);
++			wcid = rcu_dereference(dev->wcid[wlan_idx]);
++			if (wcid) {
++				u32 retries = le32_to_cpu(res->msdu_cnt[i].tx_retries),
++				    drops = le32_to_cpu(res->msdu_cnt[i].tx_drops);
++
++				wcid->stats.tx_packets_retried += retries;
++				wcid->stats.tx_packets_failed += retries + drops;
++			} else {
++				ret = -EINVAL;
++				dev_err(dev->dev, "Failed to update MSDU counts for "
++						  "invalid WCID: %hu\n", wlan_idx);
++			}
++		}
++		break;
+ 	default:
+ 		ret = -EINVAL;
+ 		dev_err(dev->dev, "Unknown UNI_PER_STA_INFO_TAG: %d\n", tag);
+@@ -6107,62 +6123,6 @@ out:
+ 	return ret;
+ }
+ 
+-int mt7996_mcu_get_signal_status(struct mt76_dev *dev)
+-{
+-	u16 sta_list[PER_STA_INFO_MAX_NUM];
+-	LIST_HEAD(sta_poll_list);
+-	struct mt7996_link_sta *mlink;
+-	int i, ret;
+-	bool empty = false;
+-
+-	spin_lock_bh(&dev->sta_poll_lock);
+-	list_splice_init(&dev->sta_poll_list, &sta_poll_list);
+-	spin_unlock_bh(&dev->sta_poll_lock);
+-
+-	while (!empty) {
+-		for (i = 0; i < PER_STA_INFO_MAX_NUM; ++i) {
+-			spin_lock_bh(&dev->sta_poll_lock);
+-			if (list_empty(&sta_poll_list)) {
+-				spin_unlock_bh(&dev->sta_poll_lock);
+-
+-				if (i == 0)
+-					return 0;
+-
+-				empty = true;
+-				break;
+-			}
+-			mlink = list_first_entry(&sta_poll_list,
+-						 struct mt7996_link_sta,
+-						 wcid.poll_list);
+-			list_del_init(&mlink->wcid.poll_list);
+-			spin_unlock_bh(&dev->sta_poll_lock);
+-
+-			sta_list[i] = mlink->wcid.idx;
+-		}
+-
+-		ret = mt7996_mcu_get_per_sta_info(dev, UNI_PER_STA_RSSI,
+-						  i, sta_list);
+-		if (ret)
+-			break;
+-
+-		ret = mt7996_mcu_get_per_sta_info(dev, UNI_PER_STA_SNR,
+-						  i, sta_list);
+-		if (ret)
+-			break;
+-	}
+-
+-	if (ret) {
+-		/* Add STAs, whose signal statuses have not been updated,
+-		 * back to polling list.
+-		 */
+-		spin_lock_bh(&dev->sta_poll_lock);
+-		list_splice(&sta_poll_list, &dev->sta_poll_list);
+-		spin_unlock_bh(&dev->sta_poll_lock);
+-	}
+-
+-	return ret;
+-}
+-
+ int mt7996_mcu_get_all_sta_info(struct mt76_dev *dev, u16 tag)
+ {
+ 	struct {
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 01eb0ea1..739e357c 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -211,6 +211,13 @@ struct per_sta_snr {
+ 	s8 val[IEEE80211_MAX_CHAINS];
+ } __packed;
+ 
++struct per_sta_msdu_cnt {
++	__le16 wlan_idx;
++	u8 __rsv[2];
++	__le32 tx_drops;
++	__le32 tx_retries;
++} __packed;
++
+ struct mt7996_mcu_per_sta_info_event {
+ 	u8 __rsv[4];
+ 
+@@ -220,6 +227,7 @@ struct mt7996_mcu_per_sta_info_event {
+ 	union {
+ 		struct per_sta_rssi rssi[0];
+ 		struct per_sta_snr snr[0];
++		struct per_sta_msdu_cnt msdu_cnt[0];
+ 	};
+ } __packed;
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 74c32827..8b00b05b 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1134,7 +1134,6 @@ void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ void mt7996_mcu_exit(struct mt7996_dev *dev);
+ int mt7996_mcu_get_per_sta_info(struct mt76_dev *dev, u16 tag,
+ 	                        u16 sta_num, u16 *sta_list);
+-int mt7996_mcu_get_signal_status(struct mt76_dev *dev);
+ int mt7996_mcu_get_all_sta_info(struct mt76_dev *dev, u16 tag);
+ int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
+ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0179-mtk-mt76-mt7996-fill-in-sn-into-txd-for-MLD-multicas.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0179-mtk-mt76-mt7996-fill-in-sn-into-txd-for-MLD-multicas.patch
new file mode 100644
index 0000000..15c49bd
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0179-mtk-mt76-mt7996-fill-in-sn-into-txd-for-MLD-multicas.patch
@@ -0,0 +1,42 @@
+From 112a2fd7458a1ee1a23b236c024bbc858b9d935e Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 15 Jul 2024 11:42:47 +0800
+Subject: [PATCH 179/199] mtk: mt76: mt7996: fill in sn into txd for MLD
+ multicast packet
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/mac.c | 9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index fae24cad..c91c550d 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -681,6 +681,7 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
+ 	bool multicast = is_multicast_ether_addr(hdr->addr1);
+ 	u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
+ 	__le16 fc = hdr->frame_control, sc = hdr->seq_ctrl;
++	u16 seqno = le16_to_cpu(sc);
+ 	u8 fc_type, fc_stype;
+ 	u32 val;
+ 
+@@ -739,9 +740,13 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
+ 		txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT);
+ 	}
+ 
+-	if (info->flags & IEEE80211_TX_CTL_INJECTED) {
+-		u16 seqno = le16_to_cpu(sc);
++	if (ieee80211_vif_is_mld(info->control.vif) && multicast) {
++		val = MT_TXD3_SN_VALID |
++		      FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno));
++		txwi[3] |= cpu_to_le32(val);
++	}
+ 
++	if (info->flags & IEEE80211_TX_CTL_INJECTED) {
+ 		if (ieee80211_is_back_req(hdr->frame_control)) {
+ 			struct ieee80211_bar *bar;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0180-mtk-mt76-mt7996-fix-potential-null-pointer.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0180-mtk-mt76-mt7996-fix-potential-null-pointer.patch
new file mode 100644
index 0000000..d82d6f0
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0180-mtk-mt76-mt7996-fix-potential-null-pointer.patch
@@ -0,0 +1,91 @@
+From 0468bbdacaddcec089088bbb16e0d4b402d52aaa Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 9 Jul 2024 14:54:39 +0800
+Subject: [PATCH 180/199] mtk: mt76: mt7996: fix potential null pointer
+
+Fix more parts that might have null pointer access.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mac.c  |  3 +++
+ mt7996/main.c | 10 ++++++++++
+ mt7996/mcu.c  |  3 +++
+ 3 files changed, 16 insertions(+)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index c91c550d..657a19c1 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1115,6 +1115,9 @@ mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb,
+ 
+ 	msta = (struct mt7996_sta *)sta->drv_priv;
+ 	mlink = rcu_dereference(msta->link[msta->pri_link]);
++	if (!mlink)
++		return;
++
+ 	if (!test_and_set_bit(tid, &mlink->wcid.ampdu_state))
+ 		ieee80211_start_tx_ba_session(sta, tid, 0);
+ }
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 032ef5f3..ff0b9c0e 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -518,9 +518,12 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ 
+ 	conf = link_conf_dereference_protected(vif, 0);
+ 	mconf = mconf_dereference_protected(mvif, 0);
++	if (!mconf || !conf)
++		goto out;
+ 
+ 	mt7996_remove_bss_conf(vif, conf, mconf);
+ 
++out:
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
+@@ -928,6 +931,9 @@ static void mt7996_vif_cfg_changed(struct ieee80211_hw *hw,
+ 			struct mt7996_link_sta *mlink =
+ 				mlink_dereference_protected(&mvif->sta, link_id);
+ 
++			if (!conf || !mconf || !mlink)
++				continue;
++
+ 			mt7996_mcu_add_bss_info(mconf->phy, conf, mconf, mlink, true);
+ 			mt7996_mcu_add_sta(dev, conf, mconf, NULL, mlink, true, false);
+ 		}
+@@ -1279,6 +1285,8 @@ mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 			link_sta_dereference_protected(sta, link_id);
+ 		bool last_link = rem == sta->valid_links && link_id == __fls(rem);
+ 
++		if (!mconf || !mlink || !conf || !link_sta)
++			continue;
+ 		mt7996_remove_link_sta(dev, conf, mconf, link_sta, mlink, last_link);
+ 	}
+ }
+@@ -1415,6 +1423,8 @@ mt7996_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 		struct mt7996_link_sta *mlink =
+ 			mlink_dereference_protected(msta, link_id);
+ 
++		if (!mlink)
++			continue;
+ 		rcu_assign_pointer(dev->mt76.wcid[mlink->wcid.idx], NULL);
+ 	}
+ 	spin_unlock_bh(&dev->mt76.status_lock);
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 857d2826..4310d35b 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3027,6 +3027,9 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 		mlink = mlink_dereference_protected(msta, link_id);
+ 		mconf = mconf_dereference_protected(msta->vif, link_id);
+ 
++		if (!mlink || !mconf)
++			continue;
++
+ 		mld_setup_link->wcid = cpu_to_le16(mlink->wcid.idx);
+ 		mld_setup_link->bss_idx = mconf->mt76.idx;
+ 		mt76_trace(vif, "link_id(%d) wcid(%d) bss_idx(%d)\n",
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0181-mtk-mt76-mt7996-Fix-legacy-action-frame-wrong-addres.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0181-mtk-mt76-mt7996-Fix-legacy-action-frame-wrong-addres.patch
new file mode 100644
index 0000000..e6819c5
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0181-mtk-mt76-mt7996-Fix-legacy-action-frame-wrong-addres.patch
@@ -0,0 +1,50 @@
+From 6d35d91b55b55134d3fca5a9f913121b2b8058eb Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Wed, 17 Jul 2024 11:36:11 +0800
+Subject: [PATCH 181/199] mtk: mt76: mt7996: Fix legacy action frame wrong
+ address translation
+
+For non-associated STA send unicast ANQP request, AP should use unicast
+to response, so mt76 sould tell fw not translate frames of this type.
+
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ mt7996/mac.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 657a19c1..e571bbfa 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -671,7 +671,8 @@ mt7996_mac_write_txwi_8023(struct mt7996_dev *dev, __le32 *txwi,
+ 
+ static void
+ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
+-			    struct sk_buff *skb, struct ieee80211_key_conf *key)
++			    struct sk_buff *skb, struct ieee80211_key_conf *key,
++			    struct mt76_wcid *wcid)
+ {
+ 	struct mt76_phy *mphy =
+ 		mt76_dev_phy(&dev->mt76, le32_get_bits(txwi[1], MT_TXD1_TGID));
+@@ -770,6 +771,9 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
+ 		txwi[5] |= cpu_to_le32(MT_TXD5_FL);
+ 		txwi[6] |= cpu_to_le32(MT_TXD6_DIS_MAT);
+ 	}
++
++	if (!wcid->sta && ieee80211_is_action(fc))
++		txwi[6] |= cpu_to_le32(MT_TXD6_DIS_MAT);
+ }
+ 
+ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+@@ -863,7 +867,7 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ 	if (is_8023)
+ 		mt7996_mac_write_txwi_8023(dev, txwi, skb, wcid);
+ 	else
+-		mt7996_mac_write_txwi_80211(dev, txwi, skb, key);
++		mt7996_mac_write_txwi_80211(dev, txwi, skb, key, wcid);
+ 
+ 	if (txwi[1] & cpu_to_le32(MT_TXD1_FIXED_RATE)) {
+ 		struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0182-mtk-mt76-mt7996-add-AP-affiliated-link-removal-suppo.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0182-mtk-mt76-mt7996-add-AP-affiliated-link-removal-suppo.patch
new file mode 100644
index 0000000..b35da7b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0182-mtk-mt76-mt7996-add-AP-affiliated-link-removal-suppo.patch
@@ -0,0 +1,564 @@
+From e61df790cc5ef66271a12e14af49f48ad097f7a6 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 31 May 2024 18:14:59 +0800
+Subject: [PATCH 182/199] mtk: mt76: mt7996: add AP affiliated link removal
+ support
+
+Add support for ap link removal of MLD reconfiguration.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt76_connac_mcu.h |   3 +
+ mt7996/main.c     |  68 ++++++++++++---
+ mt7996/mcu.c      | 217 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h      |  89 ++++++++++++++++++-
+ mt7996/mt7996.h   |   2 +
+ 5 files changed, 367 insertions(+), 12 deletions(-)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 545de4ae..fd1bf1d1 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1074,6 +1074,7 @@ enum {
+ 	MCU_UNI_EVENT_PP = 0x5a,
+ 	MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
+ 	MCU_UNI_EVENT_ALL_STA_INFO = 0x6e,
++	MCU_UNI_EVENT_MLD = 0x81,
+ };
+ 
+ #define MCU_UNI_CMD_EVENT			BIT(1)
+@@ -1319,6 +1320,7 @@ enum {
+ 	MCU_UNI_CMD_ALL_STA_INFO = 0x6e,
+ 	MCU_UNI_CMD_ASSERT_DUMP = 0x6f,
+ 	MCU_UNI_CMD_PTA_3WIRE_CTRL = 0x78,
++	MCU_UNI_CMD_MLD = 0x82,
+ };
+ 
+ enum {
+@@ -1393,6 +1395,7 @@ enum {
+ 	UNI_BSS_INFO_PM_DISABLE = 27,
+ 	UNI_BSS_INFO_BCN_CRIT_UPDATE = 32,
+ 	UNI_BSS_INFO_BCN_STA_PROF_CSA = 37,
++	UNI_BSS_INFO_BCN_ML_RECONF = 38,
+ };
+ 
+ enum {
+diff --git a/mt7996/main.c b/mt7996/main.c
+index ff0b9c0e..fc6f4ef9 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -319,6 +319,21 @@ static void mt7996_remove_bss_conf(struct ieee80211_vif *vif,
+ 		list_del_init(&mlink->wcid.poll_list);
+ 	spin_unlock_bh(&dev->mt76.sta_poll_lock);
+ 
++	/* reassign a new bss wcid if the previous one was removed */
++	if (vif->txq && ieee80211_vif_is_mld(vif) &&
++	    hweight16(vif->valid_links) > 1) {
++		struct mt76_txq *mtxq = (struct mt76_txq *)vif->txq->drv_priv;
++
++		if (mtxq->wcid == mlink->wcid.idx) {
++			u8 new_link = __ffs(vif->valid_links & ~BIT(link_id));
++			struct mt7996_link_sta *new_mlink =
++				mlink_dereference_protected(&mvif->sta, new_link);
++
++			if (new_mlink)
++				mtxq->wcid = new_mlink->wcid.idx;
++		}
++	}
++
+ 	mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
+ 
+ 	if (mlink != &mvif->sta.deflink)
+@@ -425,7 +440,7 @@ static int mt7996_add_bss_conf(struct mt7996_phy *phy,
+ 	mt7996_mac_wtbl_update(dev, idx,
+ 			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+ 
+-	if (vif->txq) {
++	if (vif->txq && hweight16(vif->valid_links) <= 1) {
+ 		mtxq = (struct mt76_txq *)vif->txq->drv_priv;
+ 		mtxq->wcid = idx;
+ 	}
+@@ -1128,6 +1143,8 @@ static void mt7996_remove_link_sta(struct mt7996_dev *dev,
+ {
+ 	struct ieee80211_sta *sta = link_sta->sta;
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	u16 valid_links = sta->valid_links;
++	bool pri_changed;
+ 	int i;
+ 
+ 	if (!mlink)
+@@ -1148,6 +1165,7 @@ static void mt7996_remove_link_sta(struct mt7996_dev *dev,
+ 		mt7996_mac_twt_teardown_flow(dev, mlink, i);
+ 
+ 	rcu_assign_pointer(mlink->sta->link[mlink->wcid.link_id], NULL);
++	rcu_assign_pointer(dev->mt76.wcid[mlink->wcid.idx], NULL);
+ 
+ 	spin_lock_bh(&dev->mt76.sta_poll_lock);
+ 	if (!list_empty(&mlink->wcid.poll_list))
+@@ -1156,17 +1174,41 @@ static void mt7996_remove_link_sta(struct mt7996_dev *dev,
+ 		list_del_init(&mlink->rc_list);
+ 	spin_unlock_bh(&dev->mt76.sta_poll_lock);
+ 
+-	/* TODO: update primary link */
+-	if (sta->valid_links) {
+-		if (mlink->wcid.link_id == msta->pri_link)
+-			msta->pri_link = msta->sec_link;
++	valid_links &= ~BIT(mlink->wcid.link_id);
++	if (!valid_links)
++		goto done;
+ 
+-		if (sta->valid_links & ~(BIT(msta->pri_link)))
+-			msta->sec_link = __ffs(sta->valid_links & ~(BIT(msta->pri_link)));
+-		else
+-			msta->sec_link = msta->pri_link;
++	/* update primary and secondary link */
++	pri_changed = mlink->wcid.link_id == msta->pri_link;
++	if (pri_changed)
++		msta->pri_link = msta->sec_link;
++
++	if (valid_links & ~(BIT(msta->pri_link)))
++		msta->sec_link = __ffs(valid_links & ~(BIT(msta->pri_link)));
++	else
++		msta->sec_link = msta->pri_link;
++
++	if (pri_changed) {
++		struct mt7996_link_sta *mlink_new =
++			mlink_dereference_protected(msta, msta->pri_link);
++
++		if (!mlink_new)
++			goto done;
++
++		mlink_new->wcid.ampdu_state = mlink->wcid.ampdu_state;
++		for (i = 0; i < ARRAY_SIZE(mlink->wcid.aggr); i++)
++			rcu_assign_pointer(mlink_new->wcid.aggr[i], mlink->wcid.aggr[i]);
++		for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
++			struct mt76_txq *mtxq;
++
++			if (!sta->txq[i])
++				continue;
++			mtxq = (struct mt76_txq *)sta->txq[i]->drv_priv;
++			mtxq->wcid = mlink_new->wcid.idx;
++		}
+ 	}
+ 
++done:
+ 	mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
+ 	mt76_wcid_mask_clear(dev->mt76.wcid_mask, mlink->wcid.idx);
+ 	mt76_wcid_mask_clear(dev->mt76.wcid_phy_mask, mlink->wcid.idx);
+@@ -3104,9 +3146,15 @@ mt7996_change_sta_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	mt76_vif_dbg(vif, "STA %pM old=0x%x, new=0x%x\n", sta->addr, old_links, new_links);
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+-	if (rem)
++	if (rem) {
+ 		mt7996_mac_sta_remove_links(dev, vif, sta, rem);
+ 
++		/* Todo: update hw info of MLD STA */
++		/* ret = mt7996_mcu_add_mld_sta(dev, vif, sta, new_links); */
++		/* if (ret) */
++		/* 	goto remove; */
++	}
++
+ 	ret = mt7996_mac_sta_add_links(dev, vif, sta, add, false);
+ 	if (ret)
+ 		goto remove;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 4310d35b..662e4f02 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1350,6 +1350,48 @@ mt7996_mcu_pp_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	}
+ }
+ 
++static void
++mt7996_mcu_mld_reconf_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
++{
++	struct mt7996_mld_event_data *data = priv;
++	struct mt7996_mcu_mld_ap_reconf_event *reconf = (void *)data->data;
++
++	if (!ether_addr_equal(vif->addr, data->mld_addr))
++		return;
++
++	ieee80211_links_removed(vif, le16_to_cpu(reconf->link_bitmap));
++}
++
++static void
++mt7996_mcu_mld_event(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++	struct mt7996_mcu_mld_event *event = (void *)skb->data;
++	struct mt7996_mld_event_data data = {};
++	struct tlv *tlv;
++	int len;
++
++	memcpy(data.mld_addr, event->mld_addr, ETH_ALEN);
++	skb_pull(skb, sizeof(*event));
++	tlv = (struct tlv *)skb->data;
++	len = skb->len;
++
++	while (len > 0 && le16_to_cpu(tlv->len) <= len) {
++		switch (le16_to_cpu(tlv->tag)) {
++		case UNI_EVENT_MLD_RECONF_AP_REM_TIMER:
++			data.data = (u8 *)tlv;
++			ieee80211_iterate_active_interfaces_atomic(dev->mt76.hw,
++					IEEE80211_IFACE_ITER_RESUME_ALL,
++					mt7996_mcu_mld_reconf_finish, &data);
++			break;
++		default:
++			break;
++		}
++
++		len -= le16_to_cpu(tlv->len);
++		tlv = (struct tlv *)((u8 *)(tlv) + le16_to_cpu(tlv->len));
++	}
++}
++
+ static void
+ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+@@ -1378,6 +1420,9 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	case MCU_UNI_EVENT_PP:
+ 		mt7996_mcu_pp_event(dev, skb);
+ 		break;
++	case MCU_UNI_EVENT_MLD:
++		mt7996_mcu_mld_event(dev, skb);
++		break;
+ #ifdef CONFIG_MTK_DEBUG
+ 	case MCU_UNI_EVENT_SR:
+ 		mt7996_mcu_rx_sr_event(dev, skb);
+@@ -3169,6 +3214,44 @@ out:
+ 				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+ }
+ 
++int mt7996_mcu_add_mld_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++			   struct ieee80211_sta *sta, unsigned long add)
++{
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	u8 link_id;
++
++	if (!sta->mlo)
++		return 0;
++
++	for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
++		struct mt7996_bss_conf *mconf =
++			mconf_dereference_protected(mvif, link_id);
++		struct mt7996_link_sta *mlink =
++			mlink_dereference_protected(msta, link_id);
++		struct sk_buff *skb;
++		int ret;
++
++		if (!mconf || !mlink)
++			continue;
++
++		skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
++						      &mlink->wcid,
++						      MT7996_STA_UPDATE_MAX_SIZE);
++		if (IS_ERR(skb))
++			return PTR_ERR(skb);
++		/* starec mld setup */
++		mt7996_mcu_sta_mld_setup_tlv(dev, skb, sta, add);
++		/* starec eht mld */
++		mt7996_mcu_sta_eht_mld_tlv(dev, skb, sta);
++		ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
++					    MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
++		if (ret)
++			return ret;
++	}
++	return 0;
++}
++
+ int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev,
+ 				struct mt7996_bss_conf *mconf,
+ 				struct mt7996_link_sta *mlink)
+@@ -3381,6 +3464,51 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
+ 				 &data, sizeof(data), true);
+ }
+ 
++static int
++mt7996_mcu_mld_reconf(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++		      u16 removed_links, u16 *removal_count)
++{
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mld_req_hdr hdr = { .mld_idx = 0xff };
++	struct mld_reconf_timer *rt;
++	struct sk_buff *skb;
++	struct tlv *tlv;
++	int len = sizeof(hdr) + sizeof(*rt);
++	unsigned long rem = removed_links;
++	u8 link_id;
++
++	memcpy(hdr.mld_addr, vif->addr, ETH_ALEN);
++
++	skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len);
++	if (!skb)
++		return -ENOMEM;
++
++	skb_put_data(skb, &hdr, sizeof(hdr));
++
++	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_CMD_MLD_RECONF_AP_REM_TIMER, sizeof(*rt));
++	rt = (struct mld_reconf_timer *)tlv;
++	rt->link_bitmap = cpu_to_le16(removed_links);
++
++	for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
++		struct ieee80211_bss_conf *conf =
++			link_conf_dereference_protected(vif, link_id);
++		struct mt7996_bss_conf *mconf =
++			mconf_dereference_protected(mvif, link_id);
++		u8 band_idx;
++		u16 to_sec;
++
++		if (!conf || !mconf)
++			continue;
++
++		band_idx = mconf->phy->mt76->band_idx;
++		to_sec = conf->beacon_int * removal_count[link_id] / 1000;
++		rt->to_sec[band_idx] = cpu_to_le16(to_sec);
++		rt->bss_idx[band_idx] = mconf->mt76.idx;
++	}
++
++	return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(MLD), true);
++}
++
+ static void
+ mt7996_mcu_beacon_cntdwn(struct ieee80211_bss_conf *conf, struct sk_buff *rskb,
+ 			 struct sk_buff *skb,
+@@ -3575,6 +3703,94 @@ mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ 		mt7996_packet_log_to_host(dev, skb->data, skb->len, PKT_BIN_DEBUG_TX, 0);
+ }
+ 
++static void
++mt7996_mcu_beacon_ml_reconf(struct mt7996_dev *dev,
++			    struct ieee80211_bss_conf *conf,
++			    struct sk_buff *rskb, struct sk_buff *skb,
++			    struct ieee80211_mutable_offsets *offs)
++{
++	struct mt7996_vif *mvif = (struct mt7996_vif *)conf->vif->drv_priv;
++	struct bss_bcn_ml_reconf_tlv *reconf;
++	struct bss_bcn_ml_reconf_offset *reconf_offs;
++	const struct element *elem, *sub;
++	struct tlv *tlv;
++	u16 removal_offs[IEEE80211_MLD_MAX_NUM_LINKS] = {};
++	u16 removal_count[IEEE80211_MLD_MAX_NUM_LINKS] = {};
++	u16 tail_offset = offs->tim_offset + offs->tim_length;
++	unsigned long removed_links = 0;
++	bool has_reconf = false;
++	u8 link_id, *beacon_tail = skb->data + tail_offset;
++
++	if (!ieee80211_vif_is_mld(conf->vif))
++		return;
++
++	/* TODO: currently manually parse reconf info directly from the IE, it
++	 * is expected to be passed from upper layer in the future.
++	 */
++	for_each_element_extid(elem, WLAN_EID_EXT_EHT_MULTI_LINK,
++			       beacon_tail, skb->len - tail_offset) {
++		if (ieee80211_mle_type_ok(elem->data + 1,
++					  IEEE80211_ML_CONTROL_TYPE_RECONF,
++					  elem->datalen - 1)) {
++			has_reconf = true;
++			break;
++		}
++	}
++
++	if (!has_reconf)
++		return;
++
++	for_each_mle_subelement(sub, elem->data + 1, elem->datalen - 1) {
++		struct ieee80211_mle_per_sta_profile *prof = (void *)sub->data;
++		u8 *pos = prof->variable;
++		u16 control;
++
++		if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE)
++			continue;
++
++		if (!ieee80211_mle_reconf_sta_prof_size_ok(sub->data,
++							   sub->datalen))
++			return;
++
++		control = le16_to_cpu(prof->control);
++		link_id = control & IEEE80211_MLE_STA_RECONF_CONTROL_LINK_ID;
++
++		removed_links |= BIT(link_id);
++
++		if (control & IEEE80211_MLE_STA_RECONF_CONTROL_STA_MAC_ADDR_PRESENT)
++			pos += 6;
++
++		if (control & IEEE80211_MLE_STA_RECONF_CONTROL_AP_REM_TIMER_PRESENT) {
++			removal_offs[link_id] = pos - skb->data;
++			removal_count[link_id] = le16_to_cpu(*(__le16 *)pos);
++		}
++	}
++
++	if (!removed_links)
++		return;
++
++	/* the first link to be removed */
++	if (conf->link_id == ffs(removed_links) - 1)
++		mt7996_mcu_mld_reconf(dev, conf->vif, removed_links, removal_count);
++
++	tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_ML_RECONF,
++				     sizeof(*reconf) +
++				     sizeof(*reconf_offs) * hweight16(removed_links));
++	reconf = (struct bss_bcn_ml_reconf_tlv *)tlv;
++	reconf->reconf_count = hweight16(removed_links);
++
++	reconf_offs = (struct bss_bcn_ml_reconf_offset *)reconf->offset;
++	for_each_set_bit(link_id, &removed_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++		struct mt7996_bss_conf *mconf =
++			mconf_dereference_protected(mvif, link_id);
++
++		reconf_offs->ap_removal_timer_offs =
++			cpu_to_le16(removal_offs[link_id]);
++		reconf_offs->bss_idx = mconf->mt76.idx;
++		reconf_offs++;
++	}
++}
++
+ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ 			  struct ieee80211_bss_conf *conf,
+ 			  struct mt7996_bss_conf *mconf, int en)
+@@ -3628,6 +3844,7 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ 	mt7996_mcu_beacon_cntdwn(conf, rskb, skb, &offs);
+ 	mt7996_mcu_beacon_sta_prof_csa(rskb, conf, &offs);
+ 	mt7996_mcu_beacon_crit_update(rskb, skb, conf, mconf, &offs);
++	mt7996_mcu_beacon_ml_reconf(dev, conf, rskb, skb, &offs);
+ out:
+ 	dev_kfree_skb(skb);
+ 	return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb,
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 739e357c..7c559d2b 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -546,6 +546,20 @@ struct bss_bcn_sta_prof_cntdwn_tlv {
+ 	u8 pkt_content[9];
+ } __packed;
+ 
++struct bss_bcn_ml_reconf_tlv {
++	__le16 tag;
++	__le16 len;
++	u8 reconf_count;
++	u8 rsv[3];
++	u8 offset[];
++} __packed;
++
++struct bss_bcn_ml_reconf_offset {
++	__le16 ap_removal_timer_offs;
++	u8 bss_idx;
++	u8 rsv;
++} __packed;
++
+ struct bss_txcmd_tlv {
+ 	__le16 tag;
+ 	__le16 len;
+@@ -594,6 +608,17 @@ struct bss_mld_tlv {
+ 	u8 __rsv[3];
+ } __packed;
+ 
++struct bss_mld_link_op_tlv {
++	__le16 tag;
++	__le16 len;
++	u8 group_mld_id;
++	u8 own_mld_id;
++	u8 mac_addr[ETH_ALEN];
++	u8 remap_idx;
++	u8 link_operation;
++	u8 rsv[2];
++} __packed;
++
+ struct sta_rec_ht_uni {
+ 	__le16 tag;
+ 	__le16 len;
+@@ -1003,8 +1028,9 @@ enum {
+ 					 sizeof(struct bss_bcn_cntdwn_tlv) +	\
+ 					 sizeof(struct bss_bcn_mbss_tlv) +	\
+ 					 sizeof(struct bss_bcn_crit_update_tlv) +	\
+-					 sizeof(struct bss_bcn_sta_prof_cntdwn_tlv))	\
+-
++					 sizeof(struct bss_bcn_sta_prof_cntdwn_tlv) +	\
++					 sizeof(struct bss_bcn_ml_reconf_tlv) +	\
++					 3 * sizeof(struct bss_bcn_ml_reconf_offset))
+ #define MT7996_MAX_BSS_OFFLOAD_SIZE	(MT7996_MAX_BEACON_SIZE +		\
+ 					 MT7996_BEACON_UPDATE_SIZE)
+ 
+@@ -1105,6 +1131,65 @@ enum {
+ 	UNI_CMD_THERMAL_PROTECT_DUTY_CONFIG,
+ };
+ 
++struct mld_req_hdr {
++	u8 ver;
++	u8 mld_addr[ETH_ALEN];
++	u8 mld_idx;
++	u8 flag;
++	u8 rsv[3];
++	u8 buf[];
++} __packed;
++
++struct mld_reconf_timer {
++	__le16 tag;
++	__le16 len;
++	__le16 link_bitmap;
++	__le16 to_sec[__MT_MAX_BAND]; /* timeout of reconf (second) */
++	u8 bss_idx[__MT_MAX_BAND];
++	u8 rsv;
++} __packed;
++
++struct mld_reconf_stop_link {
++	__le16 tag;
++	__le16 len;
++	__le16 link_bitmap;
++	u8 rsv[2];
++	u8 bss_idx[16];
++} __packed;
++
++enum {
++	UNI_CMD_MLD_RECONF_AP_REM_TIMER = 0x03,
++};
++
++struct mt7996_mcu_mld_event {
++	struct mt7996_mcu_rxd rxd;
++
++	/* fixed field */
++	u8 ver;
++	u8 mld_addr[ETH_ALEN];
++	u8 mld_idx;
++	u8 rsv[4];
++	/* tlv */
++	u8 buf[];
++} __packed;
++
++struct mt7996_mld_event_data {
++	u8 mld_addr[ETH_ALEN];
++	u8 *data;
++};
++
++struct mt7996_mcu_mld_ap_reconf_event {
++	__le16 tag;
++	__le16 len;
++	__le16 link_bitmap;
++	u8 bss_idx[3];
++	u8 rsv[3];
++} __packed;
++
++enum {
++	UNI_EVENT_MLD_RECONF_AP_REM_TIMER = 0x04,
++};
++
+ struct tx_power_ctrl {
+ 	u8 _rsv[4];
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 8b00b05b..6f9f82b7 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1083,6 +1083,8 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
+ 			     struct mt7996_bss_conf *mconf,
+ 			     struct ieee80211_link_sta *link_sta,
+ 			     struct mt7996_link_sta *mlink, bool changed);
++int mt7996_mcu_add_mld_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++			   struct ieee80211_sta *sta, unsigned long add);
+ int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef);
+ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag, bool sta);
+ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0183-mtk-mt76-mt7996-add-support-for-AP-A-TTLM.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0183-mtk-mt76-mt7996-add-support-for-AP-A-TTLM.patch
new file mode 100644
index 0000000..62f46d7
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0183-mtk-mt76-mt7996-add-support-for-AP-A-TTLM.patch
@@ -0,0 +1,356 @@
+From 35f9165b9c803d17bdf96668001f64461b9fa3e2 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 28 Jun 2024 13:45:15 +0800
+Subject: [PATCH 183/199] mtk: mt76: mt7996: add support for AP A-TTLM
+
+This add support for AP A-TTLM support by following actions
+1. request at2lm resource to FW and get switch time TSF value
+2. handle 2 events from FW and send notification to higher layer:
+   switch time erxpired and AT2LM end
+3. provide TTLM offset to FW if TTLM needs count down.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ mt76_connac_mcu.h |   1 +
+ mt7996/main.c     |  14 +++++
+ mt7996/mcu.c      | 152 +++++++++++++++++++++++++++++++++++++++++++++-
+ mt7996/mcu.h      |  46 ++++++++++++++
+ mt7996/mt7996.h   |   2 +
+ 5 files changed, 214 insertions(+), 1 deletion(-)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index fd1bf1d1..01dbe0e5 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1396,6 +1396,7 @@ enum {
+ 	UNI_BSS_INFO_BCN_CRIT_UPDATE = 32,
+ 	UNI_BSS_INFO_BCN_STA_PROF_CSA = 37,
+ 	UNI_BSS_INFO_BCN_ML_RECONF = 38,
++	UNI_BSS_INFO_BCN_ATTLM = 39,
+ };
+ 
+ enum {
+diff --git a/mt7996/main.c b/mt7996/main.c
+index fc6f4ef9..0e5e9a51 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -3167,6 +3167,19 @@ out:
+ 	return ret;
+ }
+ 
++static int
++mt7996_set_attlm(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++		 u16 disabled_links, u16 switch_time, u32 duration)
++{
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	int ret;
++
++	mutex_lock(&dev->mt76.mutex);
++	ret = mt7996_mcu_mld_set_attlm(dev, vif, disabled_links, switch_time, duration);
++	mutex_unlock(&dev->mt76.mutex);
++	return ret;
++}
++
+ static void
+ mt7996_event_callback(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 		      const struct ieee80211_event *event)
+@@ -3327,4 +3340,5 @@ const struct ieee80211_ops mt7996_ops = {
+ 	.change_vif_links = mt7996_change_vif_links,
+ 	.change_sta_links = mt7996_change_sta_links,
+ 	.set_qos_map = mt7996_set_qos_map,
++	.set_attlm = mt7996_set_attlm,
+ };
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 662e4f02..ce2168e8 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1362,6 +1362,22 @@ mt7996_mcu_mld_reconf_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ 	ieee80211_links_removed(vif, le16_to_cpu(reconf->link_bitmap));
+ }
+ 
++static void
++mt7996_mcu_mld_attlm_event(void *priv, u8 *mac, struct ieee80211_vif *vif)
++{
++	struct mt7996_mld_event_data *data = priv;
++	struct mt7996_mcu_mld_attlm_timeout_event *ttlm = (void *)data->data;
++
++	if (!ether_addr_equal(vif->addr, data->mld_addr))
++		return;
++
++	/*
++	 * TODO: Remap the FW event type to MAC80211 event type.
++	 * For now, we align it because this is a proprietary implementation.
++	 */
++	ieee80211_attlm_notify(vif, 0, ttlm->event_type, GFP_ATOMIC);
++}
++
+ static void
+ mt7996_mcu_mld_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+@@ -1376,9 +1392,15 @@ mt7996_mcu_mld_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	len = skb->len;
+ 
+ 	while (len > 0 && le16_to_cpu(tlv->len) <= len) {
++		data.data = (u8 *)tlv;
++
+ 		switch (le16_to_cpu(tlv->tag)) {
++		case UNI_EVENT_MLD_ATTLM_TIMEOUT:
++			ieee80211_iterate_active_interfaces_atomic(dev->mt76.hw,
++					IEEE80211_IFACE_ITER_RESUME_ALL,
++					mt7996_mcu_mld_attlm_event, &data);
++			break;
+ 		case UNI_EVENT_MLD_RECONF_AP_REM_TIMER:
+-			data.data = (u8 *)tlv;
+ 			ieee80211_iterate_active_interfaces_atomic(dev->mt76.hw,
+ 					IEEE80211_IFACE_ITER_RESUME_ALL,
+ 					mt7996_mcu_mld_reconf_finish, &data);
+@@ -3509,6 +3531,91 @@ mt7996_mcu_mld_reconf(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 	return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(MLD), true);
+ }
+ 
++int mt7996_mcu_mld_set_attlm(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++			     u16 disabled_links, u16 switch_time, u32 duration)
++{
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mld_req_hdr hdr = { .mld_idx = 0xff };
++	struct mld_attlm_req *req;
++	struct mt7996_mcu_mld_attlm_resp_event *resp;
++	struct sk_buff *skb, *rskb;
++	struct tlv *tlv;
++	int len = sizeof(hdr) + sizeof(*req), ret;
++	unsigned long valid_disabled_links =
++			(unsigned long) vif->valid_links & disabled_links;
++	u8 link_id;
++	bool bss_idx_set = false;
++
++	memcpy(hdr.mld_addr, vif->addr, ETH_ALEN);
++
++	skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len);
++	if (!skb)
++		return -ENOMEM;
++
++	skb_put_data(skb, &hdr, sizeof(hdr));
++	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_CMD_MLD_ATTLM_RES_REQ, sizeof(*req));
++	req = (struct mld_attlm_req *)tlv;
++
++	req->attlm_idx = 0;
++	req->mst_timer = 1;
++	req->e_timer = 1;
++	req->mst_timer_adv_time = cpu_to_le16(50);
++	req->e_timer_adv_time = cpu_to_le16(0);
++	req->mst_duration = cpu_to_le32(switch_time * USEC_PER_MSEC);
++	req->e_duration = cpu_to_le32(duration * USEC_PER_MSEC);
++	req->disabled_link_bitmap = cpu_to_le16(valid_disabled_links);
++	for_each_set_bit(link_id, &valid_disabled_links,
++			 IEEE80211_MLD_MAX_NUM_LINKS) {
++		struct mt7996_bss_conf *mconf =
++				mconf_dereference_protected(mvif, link_id);
++
++		if (!mconf)
++			continue;
++
++		if (!bss_idx_set) {
++			req->bss_idx = mconf->mt76.idx;
++			bss_idx_set = true;
++		}
++
++		req->disabled_bss_idx[link_id] = mconf->mt76.idx;
++	}
++
++	if (!bss_idx_set) {
++		dev_kfree_skb(skb);
++		return -ENOLINK;
++	}
++
++	ret = mt76_mcu_skb_send_and_get_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(MLD),
++					    true, &rskb);
++
++	if (ret)
++		return ret;
++
++	skb_pull(rskb, sizeof(struct mt7996_mcu_mld_event) - sizeof(struct mt7996_mcu_rxd));
++	resp = (struct mt7996_mcu_mld_attlm_resp_event *)rskb->data;
++	switch(le16_to_cpu(resp->tag)) {
++	case UNI_EVENT_MLD_ATTLM_RES_RSP: {
++		u32 tsf_0, tsf_1;
++		u64 switch_time_tsf;
++		u16 switch_time_tsf_tu;
++
++		tsf_0 = le32_to_cpu(resp->switch_time_tsf[0]);
++		tsf_1 = le32_to_cpu(resp->switch_time_tsf[1]);
++		switch_time_tsf = (u64)tsf_0 + ((u64)tsf_1 << 32);
++		switch_time_tsf_tu = (u16)u64_get_bits(switch_time_tsf,
++						   GENMASK_ULL(25, 10));
++		ieee80211_attlm_notify(vif, switch_time_tsf_tu,
++				       NL80211_ATTLM_STARTED, GFP_KERNEL);
++		break;
++	}
++	default:
++		break;
++	}
++
++	dev_kfree_skb(rskb);
++	return ret;
++}
++
+ static void
+ mt7996_mcu_beacon_cntdwn(struct ieee80211_bss_conf *conf, struct sk_buff *rskb,
+ 			 struct sk_buff *skb,
+@@ -3791,6 +3898,48 @@ mt7996_mcu_beacon_ml_reconf(struct mt7996_dev *dev,
+ 	}
+ }
+ 
++static void
++mt7996_mcu_beacon_ttlm(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
++		       struct sk_buff *rskb, struct sk_buff *skb,
++		       struct ieee80211_mutable_offsets *offs)
++{
++	u16 offset = 0, tail_offset = offs->tim_offset + offs->tim_length;
++	struct bss_bcn_attlm_offset_tlv *attlm_offset;
++	u8 *beacon_tail = skb->data + tail_offset;
++	const struct element *elem;
++	struct ieee80211_ttlm_elem *ttlm;
++	bool cntdown_ttlm = false;
++	struct tlv *tlv;
++
++	if (!ieee80211_vif_is_mld(conf->vif))
++		return;
++
++	for_each_element_extid(elem, WLAN_EID_EXT_TID_TO_LINK_MAPPING,
++			       beacon_tail, skb->len - tail_offset) {
++		if (ieee80211_tid_to_link_map_size_ok(elem->data + 1,
++						      elem->datalen - 1)) {
++			ttlm = (struct ieee80211_ttlm_elem *)elem->data + 1;
++			if (!(ttlm->control &
++			      IEEE80211_TTLM_CONTROL_SWITCH_TIME_PRESENT) &&
++			    (ttlm->control &
++			     IEEE80211_TTLM_CONTROL_EXPECTED_DUR_PRESENT)) {
++				offset = (u8 *)elem - skb->data;
++				cntdown_ttlm = true;
++				break;
++			}
++		}
++	}
++
++	if (!cntdown_ttlm)
++		return;
++
++	tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_ATTLM,
++				     sizeof(*attlm_offset));
++	attlm_offset = (struct bss_bcn_attlm_offset_tlv *)tlv;
++	attlm_offset->valid_id_bitmap = BIT(0);
++	attlm_offset->offset = cpu_to_le16(offset);
++}
++
+ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ 			  struct ieee80211_bss_conf *conf,
+ 			  struct mt7996_bss_conf *mconf, int en)
+@@ -3845,6 +3994,7 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ 	mt7996_mcu_beacon_sta_prof_csa(rskb, conf, &offs);
+ 	mt7996_mcu_beacon_crit_update(rskb, skb, conf, mconf, &offs);
+ 	mt7996_mcu_beacon_ml_reconf(dev, conf, rskb, skb, &offs);
++	mt7996_mcu_beacon_ttlm(dev, conf, rskb, skb, &offs);
+ out:
+ 	dev_kfree_skb(skb);
+ 	return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb,
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 7c559d2b..0f2695eb 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -560,6 +560,14 @@ struct bss_bcn_ml_reconf_offset {
+ 	u8 rsv;
+ } __packed;
+ 
++struct bss_bcn_attlm_offset_tlv {
++	__le16 tag;
++	__le16 len;
++	u8 valid_id_bitmap;
++	u8 rsv;
++	__le16 offset;
++} __packed;
++
+ struct bss_txcmd_tlv {
+ 	__le16 tag;
+ 	__le16 len;
+@@ -1140,6 +1148,22 @@ struct mld_req_hdr {
+ 	u8 buf[];
+ } __packed;
+ 
++struct mld_attlm_req {
++	__le16 tag;
++	__le16 len;
++	u8 attlm_idx;
++	u8 bss_idx;
++	u8 mst_timer;
++	u8 e_timer;
++	__le16 mst_timer_adv_time;
++	__le16 e_timer_adv_time;
++	__le32 mst_duration;
++	__le32 e_duration;
++	__le16 disabled_link_bitmap;
++	u8 disabled_bss_idx[16];
++	u8 rsv[2];
++} __packed;
++
+ struct mld_reconf_timer {
+ 	__le16 tag;
+ 	__le16 len;
+@@ -1158,6 +1182,7 @@ struct mld_reconf_stop_link {
+ } __packed;
+ 
+ enum {
++	UNI_CMD_MLD_ATTLM_RES_REQ = 0x02,
+ 	UNI_CMD_MLD_RECONF_AP_REM_TIMER = 0x03,
+ };
+ 
+@@ -1178,6 +1203,25 @@ struct mt7996_mld_event_data {
+ 	u8 *data;
+ };
+ 
++struct mt7996_mcu_mld_attlm_resp_event {
++	__le16 tag;
++	__le16 len;
++	u8 status;
++	u8 attlm_idx;
++	u8 bss_idx;
++	u8 rsv;
++	__le32 switch_time_tsf[2];
++	__le32 end_tsf[2];
++} __packed;
++
++struct mt7996_mcu_mld_attlm_timeout_event {
++	__le16 tag;
++	__le16 len;
++	u8 attlm_idx;
++	u8 event_type;
++	u8 rsv[2];
++} __packed;
++
+ struct mt7996_mcu_mld_ap_reconf_event {
+ 	__le16 tag;
+ 	__le16 len;
+@@ -1187,6 +1231,8 @@ struct mt7996_mcu_mld_ap_reconf_event {
+ } __packed;
+ 
+ enum {
++	UNI_EVENT_MLD_ATTLM_RES_RSP = 0x02,
++	UNI_EVENT_MLD_ATTLM_TIMEOUT = 0x03,
+ 	UNI_EVENT_MLD_RECONF_AP_REM_TIMER = 0x04,
+ };
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 6f9f82b7..2ee46583 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1154,6 +1154,8 @@ int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_phy *phy,
+ 				enum vow_drr_ctrl_id id);
+ int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy);
+ void mt7996_mcu_wmm_pbc_work(struct work_struct *work);
++int mt7996_mcu_mld_set_attlm(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++			     u16 disabled_links, u16 switch_time, u32 duration);
+ 
+ static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
+ {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0184-mtk-mt76-mt7996-leave-ps-when-4-address-is-establish.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0184-mtk-mt76-mt7996-leave-ps-when-4-address-is-establish.patch
new file mode 100644
index 0000000..de63b5e
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0184-mtk-mt76-mt7996-leave-ps-when-4-address-is-establish.patch
@@ -0,0 +1,108 @@
+From 68822b5e5ef48ad096a57a2366b231b7ee9af9c6 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Thu, 18 Jul 2024 10:29:22 +0800
+Subject: [PATCH 184/199] mtk: mt76: mt7996: leave ps when 4 address is
+ established
+
+Because the 4 address non-amsdu packet does not have bssid field, the
+hardware cannot get the bssid. Without bssid, the station's are not able
+leave PS mode due to HW design. Wake up non-setup link when receiving
+4 address null data to prevent this issue.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt76_connac_mcu.h |  1 +
+ mt7996/main.c     |  3 +++
+ mt7996/mcu.c      | 18 ++++++++++++++++++
+ mt7996/mcu.h      |  6 ++++++
+ mt7996/mt7996.h   |  2 ++
+ 5 files changed, 30 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 01dbe0e5..eb738f4f 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -838,6 +838,7 @@ enum {
+ 	STA_REC_EML_OP = 0x29,
+ 	STA_REC_HDR_TRANS = 0x2B,
+ 	STA_REC_TX_CAP = 0x2f,
++	STA_REC_PS_LEAVE = 0x45,
+ 	STA_REC_MAX_NUM
+ };
+ 
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 0e5e9a51..8398151d 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -2077,6 +2077,9 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw,
+ 			clear_bit(MT_WCID_FLAG_4ADDR, &mlink->wcid.flags);
+ 
+ 		mt7996_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
++
++		if (msta->pri_link != link_id && is_mt7996(&dev->mt76))
++			mt7996_mcu_ps_leave(dev, mconf, mlink);
+ 	}
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index ce2168e8..0af0ca82 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -6247,6 +6247,24 @@ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ 				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+ }
+ 
++int mt7996_mcu_ps_leave(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf,
++			struct mt7996_link_sta *mlink)
++{
++	struct sk_buff *skb;
++
++	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
++					      &mlink->wcid,
++					      MT7996_STA_UPDATE_MAX_SIZE);
++	if (IS_ERR(skb))
++		return PTR_ERR(skb);
++
++	mt76_connac_mcu_add_tlv(skb, STA_REC_PS_LEAVE,
++				sizeof(struct sta_rec_ps_leave));
++
++	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
++				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
++}
++
+ int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx,
+ 				    u16 rate_idx, bool beacon)
+ {
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 0f2695eb..ffa574b8 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -775,6 +775,12 @@ struct sta_rec_hdr_trans {
+ 	u8 mesh;
+ } __packed;
+ 
++struct sta_rec_ps_leave {
++	__le16 tag;
++	__le16 len;
++	u8 __rsv[4];
++} __packed;
++
+ struct sta_rec_mld_setup {
+ 	__le16 tag;
+ 	__le16 len;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 2ee46583..c1823759 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1277,6 +1277,8 @@ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ 				     struct ieee80211_vif *vif,
+ 				     struct mt7996_bss_conf *mconf,
+ 				     struct mt7996_link_sta *mlink);
++int mt7996_mcu_ps_leave(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf,
++			struct mt7996_link_sta *mlink);
+ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode);
+ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap);
+ int mt7996_mcu_set_pp_sta_dscb(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0185-mtk-mt76-mt7996-add-debugfs-knob-to-set-and-dump-txo.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0185-mtk-mt76-mt7996-add-debugfs-knob-to-set-and-dump-txo.patch
new file mode 100644
index 0000000..222353c
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0185-mtk-mt76-mt7996-add-debugfs-knob-to-set-and-dump-txo.patch
@@ -0,0 +1,156 @@
+From 32991b1ffa0cefe38be1adc97c2effb4a27bcc28 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Wed, 17 Jul 2024 16:57:24 +0800
+Subject: [PATCH 185/199] mtk: mt76: mt7996: add debugfs knob to set and dump
+ txop
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/mtk_debug_i.h   |   8 ++++
+ mt7996/mtk_debugfs_i.c | 104 +++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 112 insertions(+)
+
+diff --git a/mt7996/mtk_debug_i.h b/mt7996/mtk_debug_i.h
+index cec8d57e..01023e16 100644
+--- a/mt7996/mtk_debug_i.h
++++ b/mt7996/mtk_debug_i.h
+@@ -1006,6 +1006,14 @@
+ /* AGG */
+ #define MT_AGG_REMAP_CTRL(_band)	MT_WF_AGG(_band, 0x094)
+ #define MT_AGG_REMAP_CTRL_OM_REMAP	GENMASK(5, 0)
++
++/* TMAC */
++#define MT_WF_TMAC_WMM0_OFFSET		0x0c4
++#define MT_WF_TMAC_WMM1_OFFSET		0x364
++#define MT_WF_TMAC_WMM2_OFFSET		0x36c
++#define MT_WF_TMAC_WMM3_OFFSET		0x374
++#define MT_WF_TMAC_WMM_TXOP_MASK	GENMASK(31, 16)
++#define MT_WF_TMAC_WMM_TXOP_SHIFT	16
+ #endif
+ 
+ #endif
+diff --git a/mt7996/mtk_debugfs_i.c b/mt7996/mtk_debugfs_i.c
+index 852d1b12..c9224eee 100644
+--- a/mt7996/mtk_debugfs_i.c
++++ b/mt7996/mtk_debugfs_i.c
+@@ -824,6 +824,109 @@ static const struct file_operations fops_mlo_agc_tx = {
+ 	.llseek = default_llseek,
+ };
+ 
++static ssize_t mt7996_be_txop_set(struct file *file,
++			       const char __user *user_buf,
++			       size_t count, loff_t *ppos)
++{
++	struct mt7996_dev *dev = file->private_data;
++	char buf[100], role[4];
++	u32 ofs;
++	u16 txop, decimal;
++	int i = 0;
++
++	if (count >= sizeof(buf))
++		return -EINVAL;
++
++	if (copy_from_user(buf, user_buf, count))
++		return -EFAULT;
++
++	if (count && buf[count - 1] == '\n')
++		buf[count - 1] = '\0';
++	else
++		buf[count] = '\0';
++
++	if (sscanf(buf, "%3s %hu.%hu", role, &txop, &decimal) != 3)
++		goto err;
++
++	if (!strncmp(role, "ap", 2))
++		ofs = MT_WF_TMAC_WMM0_OFFSET;
++	else if (!strncmp(role, "sta", 3))
++		ofs = MT_WF_TMAC_WMM3_OFFSET;
++	else
++		goto err;
++
++	/* Change unit to 32 us */
++	txop = (txop * 1000 + decimal * 100 + 16) >> 5;
++
++	for (i = 0; i < __MT_MAX_BAND; i++) {
++		if (!dev->mt76.phys[i])
++			continue;
++
++		mt76_rmw(dev, MT_WF_TMAC(i, ofs), MT_WF_TMAC_WMM_TXOP_MASK,
++			 txop << MT_WF_TMAC_WMM_TXOP_SHIFT);
++	}
++
++	return count;
++err:
++	dev_warn(dev->mt76.dev,
++		 "format: [ap|sta] [tx_queue_data2_burst]\n");
++	return -EINVAL;
++}
++
++static ssize_t mt7996_be_txop_dump(struct file *file, char __user *user_buf,
++				size_t count, loff_t *ppos)
++{
++	struct mt7996_dev *dev = file->private_data;
++	static const size_t size = 2048;
++	int len = 0, i, ret;
++	char *buf;
++	enum {
++		AP,
++		STA,
++		MAX_IF_TYPE,
++	};
++
++	buf = kzalloc(size, GFP_KERNEL);
++	if (!buf)
++		return -ENOMEM;
++
++	len += scnprintf(buf + len, size - len, "Band\tAP (WMM0)\t\tSTA (WMM3)\n");
++
++	for (i = 0; i < __MT_MAX_BAND; i++) {
++		u32 txop[MAX_IF_TYPE], tx_burst[MAX_IF_TYPE];
++
++		if (!dev->mt76.phys[i])
++			continue;
++
++#define MT7996_READ_TXOP(role, base)						\
++do {										\
++	txop[role] = mt76_rr(dev, MT_WF_TMAC(i, base));				\
++	tx_burst[role] = u32_get_bits(txop[role], MT_WF_TMAC_WMM_TXOP_MASK);	\
++	tx_burst[role] = tx_burst[role] ? ((tx_burst[role] << 5) - 16) / 100 : 0;\
++} while (0)
++		MT7996_READ_TXOP(AP, MT_WF_TMAC_WMM0_OFFSET);
++		MT7996_READ_TXOP(STA, MT_WF_TMAC_WMM3_OFFSET);
++#undef MT7996_READ_TXOP
++
++		len += scnprintf(buf + len, size - len,
++			"%d\t0x%08x (%1u.%1u) \t0x%08x (%1u.%1u)\n", i,
++			txop[AP], tx_burst[AP] / 10, tx_burst[AP] % 10,
++			txop[STA], tx_burst[STA] / 10, tx_burst[STA] % 10);
++	}
++	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
++
++	kfree(buf);
++	return ret;
++}
++
++static const struct file_operations fops_mt7996_txop = {
++	.write = mt7996_be_txop_set,
++	.read = mt7996_be_txop_dump,
++	.open = simple_open,
++	.owner = THIS_MODULE,
++	.llseek = default_llseek,
++};
++
+ static ssize_t mt7996_mlo_agc_trig_set(struct file *file,
+ 				       const char __user *user_buf,
+ 				       size_t count, loff_t *ppos)
+@@ -893,6 +996,7 @@ int mt7996_mtk_init_dev_debugfs_internal(struct mt7996_phy *phy, struct dentry *
+ 				    mt7996_pse_fid_read);
+ 
+ 	debugfs_create_u8("dump_ple_txd", 0600, dir, &dev->dbg.dump_ple_txd);
++	debugfs_create_file("txop", 0600, dir, dev, &fops_mt7996_txop);
+ 
+ 	/* MLO related Table */
+ 	debugfs_create_file("mat_table", 0400, dir, dev, &mt7996_mat_table_fops);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0186-mtk-mt76-mt7996-add-mcu-command-to-set-bssid-mapping.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0186-mtk-mt76-mt7996-add-mcu-command-to-set-bssid-mapping.patch
new file mode 100644
index 0000000..1530414
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0186-mtk-mt76-mt7996-add-mcu-command-to-set-bssid-mapping.patch
@@ -0,0 +1,107 @@
+From 073ba6d0288ea53059ec280b7789a2cbf25ba03f Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 22 Jul 2024 10:47:45 +0800
+Subject: [PATCH 186/199] mtk: mt76: mt7996: add mcu command to set bssid
+ mapping address
+
+When receiving 4 address non-amsdu packet, there is no bssid in the address
+field. Set mcu command to use A1 as bssid when receiving 4 address non-amsdu
+packet.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/init.c   |  7 +++++++
+ mt7996/mcu.c    | 26 ++++++++++++++++++++++++++
+ mt7996/mcu.h    |  1 +
+ mt7996/mt7996.h |  1 +
+ 4 files changed, 35 insertions(+)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index f923ce66..25f772af 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -539,6 +539,9 @@ mt7996_mac_init_band(struct mt7996_dev *dev, u8 band)
+ {
+ 	u32 mask, set;
+ 
++	if (!mt7996_band_valid(dev, band))
++		return;
++
+ 	/* clear estimated value of EIFS for Rx duration & OBSS time */
+ 	mt76_wr(dev, MT_WF_RMAC_RSVD0(band), MT_WF_RMAC_RSVD0_EIFS_CLR);
+ 
+@@ -566,6 +569,10 @@ mt7996_mac_init_band(struct mt7996_dev *dev, u8 band)
+ 	 * MT_AGG_ACR_PPDU_TXS2H (PPDU format) even though ACR bit is set.
+ 	 */
+ 	mt76_set(dev, MT_AGG_ACR4(band), MT_AGG_ACR_PPDU_TXS2H);
++
++
++	if (!is_mt7996(&dev->mt76))
++		mt7996_mcu_set_bssid_mapping_addr(&dev->mt76, band);
+ }
+ 
+ static void mt7996_mac_init_basic_rates(struct mt7996_dev *dev)
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 0af0ca82..ead9ff4f 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -6117,6 +6117,32 @@ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
+ 				 &req, sizeof(req), true);
+ }
+ 
++int mt7996_mcu_set_bssid_mapping_addr(struct mt76_dev *dev, u8 band_idx)
++{
++	enum {
++		BSSID_MAPPING_ADDR1,
++		BSSID_MAPPING_ADDR2,
++		BSSID_MAPPING_ADDR3,
++	};
++	struct {
++		u8 band_idx;
++		u8 _rsv1[3];
++
++		__le16 tag;
++		__le16 len;
++		u8 addr;
++		u8 _rsv2[3];
++	} __packed req = {
++		.band_idx = band_idx,
++		.tag = cpu_to_le16(UNI_BAND_CONFIG_BSSID_MAPPING_ADDR),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.addr = BSSID_MAPPING_ADDR1,
++	};
++
++	return mt76_mcu_send_msg(dev, MCU_WM_UNI_CMD(BAND_CONFIG),
++				 &req, sizeof(req), true);
++}
++
+ int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val)
+ {
+ 	struct {
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index ffa574b8..5d4625c8 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -1067,6 +1067,7 @@ enum {
+ 	UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08,
+ 	UNI_BAND_CONFIG_RTS_SIGTA_EN = 0x09,
+ 	UNI_BAND_CONFIG_DIS_SECCH_CCA_DET = 0x0a,
++	UNI_BAND_CONFIG_BSSID_MAPPING_ADDR = 0x12,
+ };
+ 
+ enum {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index c1823759..e1e1160a 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1108,6 +1108,7 @@ int mt7996_mcu_set_radar_th(struct mt7996_dev *dev, int index,
+ 			    const struct mt7996_dfs_pattern *pattern);
+ int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable);
+ int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val);
++int mt7996_mcu_set_bssid_mapping_addr(struct mt76_dev *dev, u8 band);
+ int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct mt7996_bss_conf *mconf);
+ int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch);
+ int mt7996_mcu_get_temperature(struct mt7996_phy *phy);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0187-mtk-mt76-mt7996-Temporary-fix-init-txpwoer-for-singl.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0187-mtk-mt76-mt7996-Temporary-fix-init-txpwoer-for-singl.patch
new file mode 100644
index 0000000..2941c99
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0187-mtk-mt76-mt7996-Temporary-fix-init-txpwoer-for-singl.patch
@@ -0,0 +1,51 @@
+From 3cc44ab9e52f4c5543dcf0e0dc7cd6364aa36b3d Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Mon, 22 Jul 2024 19:33:07 +0800
+Subject: [PATCH 187/199] mtk: mt76: mt7996: Temporary fix init txpwoer for
+ single wiphy
+
+This patch can be removed after the real single wiphy finished.
+
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ mt7996/init.c | 21 +++++++++++++++------
+ 1 file changed, 15 insertions(+), 6 deletions(-)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 25f772af..e0cbcaaa 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -373,15 +373,24 @@ static void __mt7996_init_txpower(struct mt7996_phy *phy,
+ 
+ void mt7996_init_txpower(struct mt7996_phy *phy)
+ {
++	struct ieee80211_hw *hw;
++	struct mt7996_phy *phy_2g, *phy_5g, *phy_6g;
++
+ 	if (!phy)
+ 		return;
+ 
+-	if (phy->mt76->cap.has_2ghz)
+-		__mt7996_init_txpower(phy, &phy->mt76->sband_2g.sband);
+-	if (phy->mt76->cap.has_5ghz)
+-		__mt7996_init_txpower(phy, &phy->mt76->sband_5g.sband);
+-	if (phy->mt76->cap.has_6ghz)
+-		__mt7996_init_txpower(phy, &phy->mt76->sband_6g.sband);
++	hw = phy->mt76->hw;
++	/* FIXME refactor after single wiphy multiple radios merged */
++	phy_2g = mt7996_band_phy(hw, NL80211_BAND_2GHZ);
++	phy_5g = mt7996_band_phy(hw, NL80211_BAND_5GHZ);
++	phy_6g = mt7996_band_phy(hw, NL80211_BAND_6GHZ);
++
++	if (phy_2g && phy_2g->mt76->cap.has_2ghz)
++		__mt7996_init_txpower(phy_2g, &phy_2g->mt76->sband_2g.sband);
++	if (phy_5g && phy_5g->mt76->cap.has_5ghz)
++		__mt7996_init_txpower(phy_5g, &phy_5g->mt76->sband_5g.sband);
++	if (phy_6g && phy_6g->mt76->cap.has_6ghz)
++		__mt7996_init_txpower(phy_6g, &phy_6g->mt76->sband_6g.sband);
+ }
+ 
+ static void
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0188-mtk-mt76-mt7996-Add-lpi-support-with-sku_idx-and-enh.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0188-mtk-mt76-mt7996-Add-lpi-support-with-sku_idx-and-enh.patch
new file mode 100644
index 0000000..64f6af0
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0188-mtk-mt76-mt7996-Add-lpi-support-with-sku_idx-and-enh.patch
@@ -0,0 +1,647 @@
+From 5a12177572a884d35a44d9988aa8777ef4bc52d2 Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Fri, 12 Jul 2024 16:58:57 +0800
+Subject: [PATCH 188/199] mtk: mt76: mt7996: Add lpi support with sku_idx and
+ enhancement
+
+Add lpi support with sku_idx and enhancement.
+1. Add sku index for lpi sku table
+2. Add lpi psd limit for psd country and compensate power by fw.
+3. Add lpi mode mamangement from enhancement that use 1T to transmit mgmt
+from in 80 MHz.
+
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ eeprom.c             |  13 +++--
+ mt76.h               |   7 ++-
+ mt76_connac3_mac.h   |  12 +++++
+ mt7996/init.c        |   2 +-
+ mt7996/mac.c         |   5 ++
+ mt7996/main.c        |   5 +-
+ mt7996/mcu.c         | 120 +++++++++++++++++++++++++++++++++++++++----
+ mt7996/mcu.h         |   1 +
+ mt7996/mt7996.h      |   3 +-
+ mt7996/mtk_debugfs.c |   3 +-
+ mt7996/testmode.c    |   2 +-
+ mt7996/testmode.h    |  12 -----
+ mt7996/vendor.c      | 116 +++++++++++++++++++++++++++++++++++++++++
+ mt7996/vendor.h      |  15 ++++++
+ 14 files changed, 286 insertions(+), 30 deletions(-)
+
+diff --git a/eeprom.c b/eeprom.c
+index 3da94926..888e2d92 100644
+--- a/eeprom.c
++++ b/eeprom.c
+@@ -218,8 +218,9 @@ static bool mt76_string_prop_find(struct property *prop, const char *str)
+ }
+ 
+ struct device_node *
+-mt76_find_power_limits_node(struct mt76_dev *dev)
++mt76_find_power_limits_node(struct mt76_phy *phy)
+ {
++	struct mt76_dev *dev = phy->dev;
+ 	struct device_node *np = dev->dev->of_node;
+ 	const char *const region_names[] = {
+ 		[NL80211_DFS_UNSET] = "ww",
+@@ -229,6 +230,7 @@ mt76_find_power_limits_node(struct mt76_dev *dev)
+ 	};
+ 	struct device_node *cur, *fallback = NULL;
+ 	const char *region_name = NULL;
++	char index[4] = {0};
+ 
+ 	if (dev->region < ARRAY_SIZE(region_names))
+ 		region_name = region_names[dev->region];
+@@ -237,15 +239,20 @@ mt76_find_power_limits_node(struct mt76_dev *dev)
+ 	if (!np)
+ 		return NULL;
+ 
++	snprintf(index, sizeof(index), "%d", phy->sku_idx);
+ 	for_each_child_of_node(np, cur) {
+ 		struct property *country = of_find_property(cur, "country", NULL);
+ 		struct property *regd = of_find_property(cur, "regdomain", NULL);
++		struct property *sku_index = of_find_property(cur, "sku-index", NULL);
+ 
+ 		if (!country && !regd) {
+ 			fallback = cur;
+ 			continue;
+ 		}
+ 
++		if (phy->sku_idx && !mt76_string_prop_find(sku_index, index))
++			continue;
++
+ 		if (mt76_string_prop_find(country, dev->alpha2) ||
+ 		    mt76_string_prop_find(regd, region_name)) {
+ 			of_node_put(np);
+@@ -322,7 +329,7 @@ mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const __be32 *data,
+ 
+ 	for (i = 0; i < pwr_len; i++) {
+ 		pwr[i] = min_t(s8, target_power,
+-			       be32_to_cpu(data[i]) + nss_delta);
++			       (s8)be32_to_cpu(data[i]) + nss_delta);
+ 		*max_power = max(*max_power, pwr[i]);
+ 	}
+ }
+@@ -387,7 +394,7 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
+ 	if (!IS_ENABLED(CONFIG_OF))
+ 		return target_power;
+ 
+-	np = mt76_find_power_limits_node(dev);
++	np = mt76_find_power_limits_node(phy);
+ 	if (!np)
+ 		return target_power;
+ 
+diff --git a/mt76.h b/mt76.h
+index fa4a3e70..1858f542 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -933,6 +933,7 @@ struct mt76_phy {
+ 	u8 macaddr[ETH_ALEN];
+ 
+ 	int txpower_cur;
++	u8 sku_idx;
+ 	u8 antenna_mask;
+ 	u16 chainmask;
+ 
+@@ -1047,6 +1048,10 @@ struct mt76_dev {
+ 
+ 	u32 rxfilter;
+ 
++	bool lpi_psd;
++	bool lpi_bcn_enhance;
++	bool mgmt_pwr_enhance;
++
+ #ifdef CONFIG_NL80211_TESTMODE
+ 	const struct mt76_testmode_ops *test_ops;
+ 	struct {
+@@ -1810,7 +1815,7 @@ mt76_mcu_skb_send_msg(struct mt76_dev *dev, struct sk_buff *skb, int cmd,
+ void mt76_set_irq_mask(struct mt76_dev *dev, u32 addr, u32 clear, u32 set);
+ 
+ struct device_node *
+-mt76_find_power_limits_node(struct mt76_dev *dev);
++mt76_find_power_limits_node(struct mt76_phy *phy);
+ struct device_node *
+ mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan);
+ 
+diff --git a/mt76_connac3_mac.h b/mt76_connac3_mac.h
+index ad8392cd..7bc41047 100644
+--- a/mt76_connac3_mac.h
++++ b/mt76_connac3_mac.h
+@@ -209,6 +209,18 @@ enum {
+ 	MT_TXS_PPDU_FMT = 2,
+ };
+ 
++/* BW defined in FW hal_cal_flow_rom.h */
++enum {
++	FW_CDBW_20MHZ,
++	FW_CDBW_40MHZ,
++	FW_CDBW_80MHZ,
++	FW_CDBW_160MHZ,
++	FW_CDBW_320MHZ,
++	FW_CDBW_5MHZ,
++	FW_CDBW_10MHZ,
++	FW_CDBW_8080MHZ,
++};
++
+ #define MT_CT_INFO_APPLY_TXD		BIT(0)
+ #define MT_CT_INFO_COPY_HOST_TXD_ALL	BIT(1)
+ #define MT_CT_INFO_MGMT_FRAME		BIT(2)
+diff --git a/mt7996/init.c b/mt7996/init.c
+index e0cbcaaa..f1b9b0e0 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -347,7 +347,7 @@ static void __mt7996_init_txpower(struct mt7996_phy *phy,
+ 
+ 	phy->sku_limit_en = true;
+ 	phy->sku_path_en = true;
+-	np = mt76_find_power_limits_node(&dev->mt76);
++	np = mt76_find_power_limits_node(phy->mt76);
+ 	for (i = 0; i < sband->n_channels; i++) {
+ 		struct ieee80211_channel *chan = &sband->channels[i];
+ 		int target_power = mt7996_eeprom_get_target_power(dev, chan);
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index e571bbfa..32f5b859 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -885,8 +885,13 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ 		}
+ 
+ 		val = FIELD_PREP(MT_TXD6_TX_RATE, idx) | MT_TXD6_FIXED_BW;
++
+ 		if (mcast)
+ 			val |= MT_TXD6_DIS_MAT;
++		if (dev->mt76.phys[band_idx]->cap.has_6ghz &&
++		    dev->mt76.lpi_bcn_enhance &&
++		    ieee80211_is_mgmt(hdr->frame_control))
++			val |= FIELD_PREP(MT_TXD6_BW, FW_CDBW_80MHZ);
+ 		txwi[6] |= cpu_to_le32(val);
+ 		txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE);
+ 	}
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 8398151d..4b48a870 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -878,6 +878,7 @@ static u8
+ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
+ 		       struct mt7996_bss_conf *mconf, bool beacon, bool mcast)
+ {
++#define FR_RATE_IDX_OFDM_6M 0x004b
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt76_phy *mphy = mconf->phy->mt76;
+ 	u16 rate;
+@@ -890,6 +891,8 @@ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
+ 
+ 		if (dev->cert_mode && phy->mt76->band_idx == MT_BAND2)
+ 			rate = 0x0200;
++		else if (mphy->dev->lpi_bcn_enhance)
++			rate = FR_RATE_IDX_OFDM_6M;
+ 
+ 		/* odd index for driver, even index for firmware */
+ 		idx = MT7996_BEACON_RATES_TBL + 2 * phy->mt76->band_idx;
+@@ -1035,7 +1038,7 @@ static void mt7996_link_info_changed(struct ieee80211_hw *hw,
+ 		mt7996_update_mu_group(hw, info, mconf);
+ 
+ 	if (changed & BSS_CHANGED_TXPOWER)
+-		mt7996_mcu_set_txpower_sku(phy, info);
++		mt7996_mcu_set_txpower_sku(phy, info->txpower);
+ 
+ out:
+ 	mutex_unlock(&dev->mt76.mutex);
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index ead9ff4f..0439157c 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -6310,7 +6310,7 @@ int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx,
+ 
+ 	if (beacon) {
+ 		req.spe_idx_sel = SPE_IXD_SELECT_TXD;
+-		req.spe_idx = 24 + band_idx;
++		req.spe_idx = dev->mt76.mgmt_pwr_enhance ? 0 : 24 + band_idx;
+ 		phy->beacon_rate = rate_idx;
+ 	} else {
+ 		req.spe_idx_sel = SPE_IXD_SELECT_BMC_WTBL;
+@@ -6593,14 +6593,29 @@ mt7996_update_max_txpower_cur(struct mt7996_phy *phy, int tx_power)
+ 		mphy->txpower_cur = e2p_power_limit;
+ }
+ 
++bool mt7996_is_psd_country(char *country)
++{
++	char psd_country_list[][3] = {"US", "KR", "BR", "CL", "MY", ""};
++	int i;
++
++	if (strlen(country) != 2)
++		return 0;
++
++	for (i = 0; psd_country_list[i][0] != '\0'; i++) {
++		if (!strncmp(country, psd_country_list[i], 2))
++			return 1;
++	}
++
++	return 0;
++}
++
+ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
+-			       struct ieee80211_bss_conf *conf)
++			       int txpower_setting)
+ {
+ #define TX_POWER_LIMIT_TABLE_RATE	0
+ #define TX_POWER_LIMIT_TABLE_PATH	1
+ 	struct mt7996_dev *dev = phy->dev;
+ 	struct mt76_phy *mphy = phy->mt76;
+-	struct ieee80211_hw *hw = mphy->hw;
+ 	struct tx_power_limit_table_ctrl {
+ 		u8 __rsv1[4];
+ 
+@@ -6621,9 +6636,9 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
+ 	struct sk_buff *skb;
+ 	int i, ret, txpower_limit;
+ 
+-	if (hw->conf.power_level == INT_MIN)
+-		hw->conf.power_level = 127;
+-	txpower_limit = mt7996_get_power_bound(phy, conf->txpower);
++	if (txpower_setting == INT_MIN || txpower_setting > 127)
++		txpower_setting = 127;
++	txpower_limit = mt7996_get_power_bound(phy, txpower_setting);
+ 
+ 	if (phy->sku_limit_en) {
+ 		txpower_limit = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
+@@ -6642,7 +6657,36 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
+ 	skb_put_data(skb, &req, sizeof(req));
+ 	/* cck and ofdm */
+ 	skb_put_data(skb, &la.cck, sizeof(la.cck));
+-	skb_put_data(skb, &la.ofdm, sizeof(la.ofdm));
++
++	/* FW would compensate for PSD countries
++	 * driver doesn't need to do it
++	 */
++	if (phy->mt76->cap.has_6ghz && mphy->dev->lpi_psd &&
++	    !mt7996_is_psd_country(dev->mt76.alpha2)) {
++		switch (mphy->chandef.width) {
++		case NL80211_CHAN_WIDTH_20:
++			skb_put_data(skb, &la.eht[3], sizeof(la.ofdm));
++			break;
++		case NL80211_CHAN_WIDTH_40:
++			skb_put_data(skb, &la.eht[4], sizeof(la.ofdm));
++			break;
++		case NL80211_CHAN_WIDTH_80:
++			skb_put_data(skb, &la.eht[5], sizeof(la.ofdm));
++			break;
++		case NL80211_CHAN_WIDTH_160:
++			skb_put_data(skb, &la.eht[6], sizeof(la.ofdm));
++			break;
++		case NL80211_CHAN_WIDTH_320:
++			skb_put_data(skb, &la.eht[7], sizeof(la.ofdm));
++			break;
++		default:
++			skb_put_data(skb, &la.ofdm, sizeof(la.ofdm));
++			break;
++		}
++	} else {
++		skb_put_data(skb, &la.ofdm, sizeof(la.ofdm));
++	}
++
+ 	/* ht20 */
+ 	skb_put_data(skb, &la.mcs[0], 8);
+ 	/* ht40 */
+@@ -6679,8 +6723,41 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
+ 
+ 	skb_put_data(skb, &req, sizeof(req));
+ 	skb_put_data(skb, &la_path.cck, sizeof(la_path.cck));
+-	skb_put_data(skb, &la_path.ofdm, sizeof(la_path.ofdm));
+-	skb_put_data(skb, &la_path.ofdm_bf, sizeof(la_path.ofdm_bf));
++
++	/* FW would NOT compensate in the case of BF backoff table
++	 * driver needs to compensate for LPI PSD
++	 */
++	if (phy->mt76->cap.has_6ghz && mphy->dev->lpi_psd) {
++		switch (mphy->chandef.width) {
++		case NL80211_CHAN_WIDTH_20:
++			skb_put_data(skb, &la_path.ru[5], sizeof(la_path.ofdm));
++			skb_put_data(skb, &la_path.ru_bf[5], sizeof(la_path.ofdm_bf));
++			break;
++		case NL80211_CHAN_WIDTH_40:
++			skb_put_data(skb, &la_path.ru[6], sizeof(la_path.ofdm));
++			skb_put_data(skb, &la_path.ru_bf[6], sizeof(la_path.ofdm_bf));
++			break;
++		case NL80211_CHAN_WIDTH_80:
++			skb_put_data(skb, &la_path.ru[8], sizeof(la_path.ofdm));
++			skb_put_data(skb, &la_path.ru_bf[8], sizeof(la_path.ofdm_bf));
++			break;
++		case NL80211_CHAN_WIDTH_160:
++			skb_put_data(skb, &la_path.ru[11], sizeof(la_path.ofdm));
++			skb_put_data(skb, &la_path.ru_bf[11], sizeof(la_path.ofdm_bf));
++			break;
++		case NL80211_CHAN_WIDTH_320:
++			skb_put_data(skb, &la_path.ru[15], sizeof(la_path.ofdm));
++			skb_put_data(skb, &la_path.ru_bf[15], sizeof(la_path.ofdm_bf));
++			break;
++		default:
++			skb_put_data(skb, &la_path.ofdm, sizeof(la_path.ofdm));
++			skb_put_data(skb, &la_path.ofdm_bf, sizeof(la_path.ofdm_bf));
++			break;
++		}
++	} else {
++		skb_put_data(skb, &la_path.ofdm, sizeof(la_path.ofdm));
++		skb_put_data(skb, &la_path.ofdm_bf, sizeof(la_path.ofdm_bf));
++	}
+ 
+ 	for (i = 0; i < 32; i++) {
+ 		bool bf = i % 2;
+@@ -6694,6 +6771,31 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
+ 				     MCU_WM_UNI_CMD(TXPOWER), true);
+ }
+ 
++int mt7996_mcu_set_lpi_psd(struct mt7996_phy *phy, u8 enable)
++{
++	struct mt7996_dev *dev = phy->dev;
++
++	struct {
++		u8 band_idx;
++		u8 _rsv[3];
++
++		__le16 tag;
++		__le16 len;
++		u8 lpi_enable;
++		u8 psd_limit;
++		u8 _rsv2[2];
++	} __packed req = {
++		.band_idx = phy->mt76->band_idx,
++		.tag = cpu_to_le16(UNI_BAND_CONFIG_LPI_CTRL),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.lpi_enable = enable,
++		.psd_limit = enable ? mt7996_is_psd_country(dev->mt76.alpha2) : 0,
++	};
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
++				 &req, sizeof(req), false);
++}
++
+ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode)
+ {
+ 	__le32 cp_mode;
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 5d4625c8..a5818f95 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -1067,6 +1067,7 @@ enum {
+ 	UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08,
+ 	UNI_BAND_CONFIG_RTS_SIGTA_EN = 0x09,
+ 	UNI_BAND_CONFIG_DIS_SECCH_CCA_DET = 0x0a,
++	UNI_BAND_CONFIG_LPI_CTRL = 0x0d,
+ 	UNI_BAND_CONFIG_BSSID_MAPPING_ADDR = 0x12,
+ };
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index e1e1160a..67ac7a91 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1115,7 +1115,7 @@ int mt7996_mcu_get_temperature(struct mt7996_phy *phy);
+ int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state);
+ int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable);
+ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
+-			       struct ieee80211_bss_conf *conf);
++			       int txpower_setting);
+ int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
+ 		       u8 rx_sel, u8 val);
+ int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev,
+@@ -1315,6 +1315,7 @@ void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
+ int mt7996_mcu_set_csi(struct mt7996_phy *phy, u8 mode,
+ 		       u8 cfg, u8 v1, u32 v2, u8 *mac_addr);
+ int mt7996_vendor_pp_bitmap_update(struct mt7996_phy *phy, u16 bitmap);
++int mt7996_mcu_set_lpi_psd(struct mt7996_phy *phy, u8 enable);
+ #endif
+ 
+ int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 093f3c69..fa8de2db 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2509,7 +2509,7 @@ mt7996_get_txpower_info(struct file *file, char __user *user_buf,
+ 	len += scnprintf(buf + len, size - len,
+ 			 "    Theraml Compensation Value: %d\n",
+ 			 basic_info->thermal_compensate_value);
+-	np = mt76_find_power_limits_node(phy->mt76->dev);
++	np = mt76_find_power_limits_node(phy->mt76);
+ 	len += scnprintf(buf + len, size - len,
+ 			 "    RegDB:  %s\n",
+ 			 !np ? "enable" : "disable");
+@@ -4505,6 +4505,7 @@ void mt7996_mtk_init_dev_debugfs(struct mt7996_dev *dev, struct dentry *dir)
+ 	debugfs_create_file("rx_drop_stats", 0400, dir, dev, &mt7996_rx_drop_fops);
+ 
+ 	debugfs_create_file("muru_dbg", 0200, dir, dev, &fops_muru_dbg_info);
++	debugfs_create_bool("mgmt_pwr_enhance", 0600, dir, &dev->mt76.mgmt_pwr_enhance);
+ }
+ 
+ #endif
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index b956915e..cf9ec9ac 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -1853,7 +1853,7 @@ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
+ 		mt7996_tm_update_channel(phy);
+ 		mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(SKU_POWER_LIMIT), td->sku_en);
+ 		mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(BACKOFF_POWER_LIMIT), td->sku_en);
+-		mt7996_mcu_set_txpower_sku(phy, &phy->monitor_vif->bss_conf);
++		mt7996_mcu_set_txpower_sku(phy, phy->monitor_vif->bss_conf.txpower);
+ 	}
+ 	if (changed & BIT(TM_CHANGED_TX_LENGTH)) {
+ 		mt7996_tm_set(dev, SET_ID(TX_LEN), td->tx_mpdu_len);
+diff --git a/mt7996/testmode.h b/mt7996/testmode.h
+index ba1767ae..5c720da7 100644
+--- a/mt7996/testmode.h
++++ b/mt7996/testmode.h
+@@ -15,18 +15,6 @@ enum {
+ 	TM_CBW_320MHZ = 12,
+ };
+ 
+-/* BW defined in FW hal_cal_flow_rom.h */
+-enum {
+-	FW_CDBW_20MHZ,
+-	FW_CDBW_40MHZ,
+-	FW_CDBW_80MHZ,
+-	FW_CDBW_160MHZ,
+-	FW_CDBW_320MHZ,
+-	FW_CDBW_5MHZ,
+-	FW_CDBW_10MHZ,
+-	FW_CDBW_8080MHZ,
+-};
+-
+ enum {
+ 	BF_CDBW_20MHZ,
+ 	BF_CDBW_40MHZ,
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index d7973c5e..e75c163e 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -145,6 +145,14 @@ csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
+ 	[MTK_VENDOR_ATTR_CSI_CTRL_DATA] = { .type = NLA_NESTED },
+ };
+ 
++static struct nla_policy
++txpower_ctrl_policy[NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL] = {
++	[MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_PSD] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID] = { .type = NLA_U8 },
++};
++
+ struct mt7996_amnt_data {
+ 	u8 idx;
+ 	u8 addr[ETH_ALEN];
+@@ -1378,6 +1386,103 @@ out:
+ 	return err;
+ }
+ 
++static int mt7996_vendor_txpower_ctrl(struct wiphy *wiphy,
++				      struct wireless_dev *wdev,
++				      const void *data,
++				      int data_len)
++{
++#define FR_RATE_IDX_OFDM_6M 0x004b
++	struct mt7996_dev *dev;
++	struct mt7996_phy *phy;
++	struct mt76_phy *mphy;
++	struct ieee80211_vif *vif = wdev_to_ieee80211_vif(wdev);
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_bss_conf *mconf;
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL];
++	struct mt76_power_limits la = {};
++	struct mt76_power_path_limits la_path = {};
++	int err, current_txpower, delta;
++	u8 val, link_id = 0, idx;
++
++	err = nla_parse(tb, MTK_VENDOR_ATTR_TXPOWER_CTRL_MAX, data, data_len,
++			txpower_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++
++	if (ieee80211_vif_is_mld(vif) && tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID]) {
++		link_id = nla_get_u8(tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID]);
++
++		if (link_id >= IEEE80211_LINK_UNSPECIFIED)
++			return -EINVAL;
++	}
++
++	rcu_read_lock();
++	mconf = rcu_dereference(mvif->link[link_id]);
++	if (!mconf || !mconf->phy) {
++		rcu_read_unlock();
++		return -EINVAL;
++	}
++
++	phy = mconf->phy;
++	rcu_read_unlock();
++
++	mphy = phy->mt76;
++
++	if (mphy->cap.has_6ghz &&
++	    tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_PSD]) {
++		val = nla_get_u8(tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_PSD]);
++		mphy->dev->lpi_psd = val;
++
++		err = mt7996_mcu_set_lpi_psd(phy, val);
++		if (err)
++			return err;
++	}
++
++	if (tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX]) {
++		mphy->sku_idx = nla_get_u8(tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX]);
++
++		if (mt76_find_power_limits_node(mphy) == NULL)
++			mphy->sku_idx = 0;
++
++		phy->sku_limit_en = true;
++		phy->sku_path_en = true;
++		mt76_get_rate_power_limits(mphy, mphy->chandef.chan, &la, &la_path, 127);
++		if (!la_path.ofdm[0])
++			phy->sku_path_en = false;
++
++		dev = phy->dev;
++		err = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
++						   dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
++		if (err)
++			return err;
++		err = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
++						   dev->dbg.sku_disable ? 0 : phy->sku_path_en);
++		if (err)
++			return err;
++	}
++
++	if (mphy->cap.has_6ghz &&
++	    tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE]) {
++		val = nla_get_u8(tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE]);
++		mphy->dev->lpi_bcn_enhance = val;
++		idx = MT7996_BEACON_RATES_TBL + 2 * phy->mt76->band_idx;
++
++		err = mt7996_mcu_set_fixed_rate_table(phy, idx, FR_RATE_IDX_OFDM_6M, true);
++		if (err)
++			return err;
++	}
++
++	delta = mt76_tx_power_nss_delta(hweight16(mphy->chainmask));
++	current_txpower = DIV_ROUND_UP(mphy->txpower_cur + delta, 2);
++
++	err = mt7996_mcu_set_txpower_sku(phy, current_txpower);
++	if (err)
++		return err;
++
++	return 0;
++}
++
+ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 	{
+ 		.info = {
+@@ -1528,6 +1633,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 		.policy = eml_ctrl_policy,
+ 		.maxattr = MTK_VENDOR_ATTR_EML_CTRL_MAX,
+ 	},
++	{
++		.info = {
++			.vendor_id = MTK_NL80211_VENDOR_ID,
++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++			 WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.doit = mt7996_vendor_txpower_ctrl,
++		.policy = txpower_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_TXPOWER_CTRL_MAX,
++	},
+ };
+ 
+ static const struct nl80211_vendor_cmd_info mt7996_vendor_events[] = {
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index ca4f00ad..515f77a6 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -18,6 +18,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
+ 	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
+ 	MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
++	MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL = 0xce,
+ 	MTK_NL80211_VENDOR_SUBCMD_EML_CTRL = 0xd3,
+ };
+ 
+@@ -312,6 +313,20 @@ enum mtk_vendor_attr_csi_data {
+ 	MTK_VENDOR_ATTR_CSI_DATA_MAX =
+ 		NUM_MTK_VENDOR_ATTRS_CSI_DATA - 1
+ };
++
++enum mtk_vendor_attr_txpower_ctrl {
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_PSD,
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX,
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE,
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL,
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL - 1
++};
+ #endif
+ 
+ #endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0189-mtk-mt76-mt7996-Add-Triggered-Uplink-Access-Optimiza.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0189-mtk-mt76-mt7996-Add-Triggered-Uplink-Access-Optimiza.patch
new file mode 100644
index 0000000..16d8cae
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0189-mtk-mt76-mt7996-Add-Triggered-Uplink-Access-Optimiza.patch
@@ -0,0 +1,309 @@
+From c21cbc5f70b521cf05abb63c63d9a753076adb24 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Tue, 2 Jul 2024 10:06:26 +0800
+Subject: [PATCH 189/199] mtk: mt76: mt7996: Add Triggered Uplink Access
+ Optimization support
+
+Add TUAO feature, which is a subset of SCS procedure support.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt7996/init.c    | 25 ++++++++++-----
+ mt7996/mt7996.h  |  3 ++
+ mt7996/mtk_mcu.c | 49 ++++++++++++++++++++++++++++++
+ mt7996/mtk_mcu.h |  7 +++++
+ mt7996/vendor.c  | 79 ++++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/vendor.h  | 17 +++++++++++
+ 6 files changed, 173 insertions(+), 7 deletions(-)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index f1b9b0e0..2f43c0ff 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -62,7 +62,14 @@ static const struct ieee80211_iface_combination if_comb_7992[] = {
+ 	}
+ };
+ 
+-static const u8 mt7996_if_types_ext_capa[] = {
++static const u8 mt7996_if_types_ext_capa_ap[] = {
++	[0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
++	[2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT,
++	[6] = WLAN_EXT_CAPA7_SCS_SUPPORT,
++	[7] = WLAN_EXT_CAPA8_OPMODE_NOTIF,
++};
++
++static const u8 mt7996_if_types_ext_capa_sta[] = {
+ 	[0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
+ 	[2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT,
+ 	[7] = WLAN_EXT_CAPA8_OPMODE_NOTIF,
+@@ -71,16 +78,16 @@ static const u8 mt7996_if_types_ext_capa[] = {
+ static const struct wiphy_iftype_ext_capab mt7996_iftypes_ext_capa[] = {
+ 	{
+ 		.iftype = NL80211_IFTYPE_STATION,
+-		.extended_capabilities = mt7996_if_types_ext_capa,
+-		.extended_capabilities_mask = mt7996_if_types_ext_capa,
+-		.extended_capabilities_len = sizeof(mt7996_if_types_ext_capa),
++		.extended_capabilities = mt7996_if_types_ext_capa_sta,
++		.extended_capabilities_mask = mt7996_if_types_ext_capa_sta,
++		.extended_capabilities_len = sizeof(mt7996_if_types_ext_capa_sta),
+ 		.mld_capa_and_ops = 2,
+ 	},
+ 	{
+ 		.iftype = NL80211_IFTYPE_AP,
+-		.extended_capabilities = mt7996_if_types_ext_capa,
+-		.extended_capabilities_mask = mt7996_if_types_ext_capa,
+-		.extended_capabilities_len = sizeof(mt7996_if_types_ext_capa),
++		.extended_capabilities = mt7996_if_types_ext_capa_ap,
++		.extended_capabilities_mask = mt7996_if_types_ext_capa_ap,
++		.extended_capabilities_len = sizeof(mt7996_if_types_ext_capa_ap),
+ 		.eml_capabilities = IEEE80211_EML_CAP_EMLSR_SUPP,
+ 		.mld_capa_and_ops = 2,
+ 		/* the max number of simultaneous links is defined as the
+@@ -1573,6 +1580,10 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
+ 		u8_encode_bits(IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_11454,
+ 			       IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK);
+ 
++	if (iftype == NL80211_IFTYPE_AP)
++		eht_cap_elem->mac_cap_info[0] |=
++			IEEE80211_EHT_MAC_CAP0_SCS_TRAFFIC_DESC;
++
+ 	eht_cap_elem->phy_cap_info[0] =
+ 		IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI |
+ 		IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER |
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 67ac7a91..fa884316 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -524,6 +524,9 @@ struct csi_data {
+ };
+ 
+ int mt7996_set_coding_type(struct ieee80211_hw *hw, u8 coding_type, u8 link_id);
++
++int mt7996_mcu_set_muru_qos_cfg(struct mt7996_dev *dev, u16 wlan_idx, u8 dir,
++				u8 scs_id, u8 req_type, u8 *qos_ie, u8 qos_ie_len);
+ #endif
+ 
+ struct mt7996_rro_ba_session {
+diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
+index 8bacf29d..9ec4bc9a 100644
+--- a/mt7996/mtk_mcu.c
++++ b/mt7996/mtk_mcu.c
+@@ -1429,4 +1429,53 @@ int mt7996_set_coding_type(struct ieee80211_hw *hw, u8 coding_type, u8 link_id)
+ 
+ 	return 0;
+ }
++
++int mt7996_mcu_set_muru_qos_cfg(struct mt7996_dev *dev, u16 wlan_idx, u8 dir,
++				u8 scs_id, u8 req_type, u8 *qos_ie, u8 qos_ie_len)
++{
++#define QOS_FLAG_UPDATE 20
++#define QOS_FLAG_DELETE 21
++
++	struct {
++		u8 _rsv[4];
++
++		__le16 tag;
++		__le16 len;
++
++		__le32 qos_flag;
++		__le16 wlan_idx;
++		u8 __rsv2[12];
++		u8 dir;
++		u8 _rsv3[4];
++		u8 scs_id;
++		u8 qos_ie[44];
++	} __packed req = {
++		.tag = cpu_to_le16(UNI_CMD_MURU_SET_QOS_CFG),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.wlan_idx = cpu_to_le16(wlan_idx),
++		.scs_id = scs_id,
++	};
++
++	switch (req_type) {
++	case SCS_REQ_TYPE_ADD:
++	case SCS_REQ_TYPE_CHANGE:
++		req.qos_flag = cpu_to_le32(QOS_FLAG_UPDATE);
++		req.dir = dir;
++
++		if (qos_ie_len > sizeof(req.qos_ie))
++			return -EINVAL;
++
++		memcpy(req.qos_ie, qos_ie, qos_ie_len);
++		break;
++	case SCS_REQ_TYPE_REMOVE:
++		req.qos_flag = cpu_to_le32(QOS_FLAG_DELETE);
++		break;
++	default:
++		dev_err(dev->mt76.dev, "Unsupported req_type %u\n", req_type);
++		return -EINVAL;
++	}
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
++				 sizeof(req), true);
++}
+ #endif
+diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
+index 8ba261a7..711903e0 100644
+--- a/mt7996/mtk_mcu.h
++++ b/mt7996/mtk_mcu.h
+@@ -134,6 +134,7 @@ enum {
+ 	UNI_CMD_MURU_PROT_FRAME_THR = 0xCC,
+ 	UNI_CMD_MURU_SET_CERT_MU_EDCA_OVERRIDE,
+ 	UNI_CMD_MURU_SET_TRIG_VARIANT = 0xD5,
++	UNI_CMD_MURU_SET_QOS_CFG = 0xFE,
+ };
+ 
+ struct bf_pfmu_tag {
+@@ -1165,4 +1166,10 @@ enum {
+ 			  VOW_DRR_DBG_PRN)
+ #endif
+ 
++enum {
++	SCS_REQ_TYPE_ADD,
++	SCS_REQ_TYPE_REMOVE,
++	SCS_REQ_TYPE_CHANGE,
++};
++
+ #endif
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index e75c163e..e13a148a 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -132,6 +132,17 @@ eml_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EML_CTRL] = {
+ 	[MTK_VENDOR_ATTR_EML_CTRL_STRUCT] = { .type = NLA_BINARY },
+ };
+ 
++
++static const struct nla_policy
++scs_ctrl_policy[NUM_MTK_VENDOR_ATTRS_SCS_CTRL] = {
++	[MTK_VENDOR_ATTR_SCS_ID] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_SCS_REQ_TYPE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_SCS_DIR] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_SCS_QOS_IE] = { .type = NLA_BINARY },
++	[MTK_VENDOR_ATTR_SCS_MAC_ADDR] = NLA_POLICY_ETH_ADDR,
++	[MTK_VENDOR_ATTR_SCS_LINK_ID] = { .type = NLA_U8 },
++};
++
+ static const struct nla_policy
+ csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
+ 	[MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX] = { .type = NLA_U8 },
+@@ -1164,6 +1175,63 @@ static int mt7996_vendor_eml_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev
+ 
+ 	return err;
+ }
++static int mt7996_vendor_scs_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
++				  const void *data, int data_len)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct ieee80211_sta *sta;
++	struct mt7996_sta *msta;
++	struct mt7996_link_sta *mlink;
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_SCS_CTRL];
++	u8 sta_addr[ETH_ALEN];
++	u8 scs_id, req_type, dir, link_id, qos_ie_len;
++	u8 *qos_ie = NULL;
++	int err;
++
++	err = nla_parse(tb, MTK_VENDOR_ATTR_SCS_CTRL_MAX, data, data_len,
++			scs_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	if (!tb[MTK_VENDOR_ATTR_SCS_ID] || !tb[MTK_VENDOR_ATTR_SCS_REQ_TYPE] ||
++	    !tb[MTK_VENDOR_ATTR_SCS_MAC_ADDR] || !tb[MTK_VENDOR_ATTR_SCS_LINK_ID])
++		return -EINVAL;
++
++	scs_id = nla_get_u8(tb[MTK_VENDOR_ATTR_SCS_ID]);
++	req_type = nla_get_u8(tb[MTK_VENDOR_ATTR_SCS_REQ_TYPE]);
++	nla_memcpy(sta_addr, tb[MTK_VENDOR_ATTR_SCS_MAC_ADDR], ETH_ALEN);
++	link_id = nla_get_u8(tb[MTK_VENDOR_ATTR_SCS_LINK_ID]);
++
++	sta = ieee80211_find_sta_by_ifaddr(hw, sta_addr, NULL);
++	if (!sta)
++		return -EINVAL;
++
++	msta = (struct mt7996_sta *)sta->drv_priv;
++	mlink = mlink_dereference_protected(msta, link_id);
++	if (!mlink)
++		return -EINVAL;
++
++	if (req_type == SCS_REQ_TYPE_ADD || req_type == SCS_REQ_TYPE_CHANGE) {
++		if (!tb[MTK_VENDOR_ATTR_SCS_DIR] || !tb[MTK_VENDOR_ATTR_SCS_QOS_IE])
++			return -EINVAL;
++
++		dir = nla_get_u8(tb[MTK_VENDOR_ATTR_SCS_DIR]);
++		qos_ie_len = nla_len(tb[MTK_VENDOR_ATTR_SCS_QOS_IE]);
++		qos_ie = kzalloc(qos_ie_len, GFP_KERNEL);
++		if (!qos_ie)
++			return -ENOMEM;
++
++		nla_memcpy(qos_ie, tb[MTK_VENDOR_ATTR_SCS_QOS_IE], qos_ie_len);
++	}
++
++	err = mt7996_mcu_set_muru_qos_cfg(dev, mlink->wcid.idx, dir, scs_id,
++					  req_type, qos_ie, qos_ie_len);
++
++	kfree(qos_ie);
++
++	return err;
++}
+ 
+ static int mt7996_vendor_csi_ctrl(struct wiphy *wiphy,
+ 				  struct wireless_dev *wdev,
+@@ -1644,6 +1712,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 		.policy = txpower_ctrl_policy,
+ 		.maxattr = MTK_VENDOR_ATTR_TXPOWER_CTRL_MAX,
+ 	},
++	{
++		.info = {
++			.vendor_id = MTK_NL80211_VENDOR_ID,
++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_SCS_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++			 WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.doit = mt7996_vendor_scs_ctrl,
++		.policy = scs_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_SCS_CTRL_MAX,
++	},
+ };
+ 
+ static const struct nl80211_vendor_cmd_info mt7996_vendor_events[] = {
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index 515f77a6..71800590 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -19,6 +19,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
+ 	MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
+ 	MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL = 0xce,
++	MTK_NL80211_VENDOR_SUBCMD_SCS_CTRL = 0xd0,
+ 	MTK_NL80211_VENDOR_SUBCMD_EML_CTRL = 0xd3,
+ };
+ 
+@@ -262,6 +263,22 @@ enum mtk_vendor_attr_eml_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_EML_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_scs_ctrl {
++	MTK_VENDOR_ATTR_SCS_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_SCS_ID,
++	MTK_VENDOR_ATTR_SCS_REQ_TYPE,
++	MTK_VENDOR_ATTR_SCS_DIR,
++	MTK_VENDOR_ATTR_SCS_QOS_IE,
++	MTK_VENDOR_ATTR_SCS_MAC_ADDR,
++	MTK_VENDOR_ATTR_SCS_LINK_ID,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_SCS_CTRL,
++	MTK_VENDOR_ATTR_SCS_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_SCS_CTRL - 1
++};
++
+ enum mtk_vendor_attr_csi_ctrl {
+ 	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0190-mtk-mt76-mt7996-add-per-band-token-limit.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0190-mtk-mt76-mt7996-add-per-band-token-limit.patch
new file mode 100644
index 0000000..78737b8
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0190-mtk-mt76-mt7996-add-per-band-token-limit.patch
@@ -0,0 +1,200 @@
+From 08bab29bc70a7b21150167f2ae502d8e5538c743 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Tue, 30 Jul 2024 19:49:39 +0800
+Subject: [PATCH 190/199] mtk: mt76: mt7996: add per-band token limit
+
+Add a threshold for per-band token count.
+The bands use the same token pool so a band cannot transmit if
+the other band occupy too many tokens. With this patch, we can
+prevent a band from interfering with the other band.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt76.h               |  6 +++++-
+ mt7996/init.c        |  2 ++
+ mt7996/mac.c         |  5 ++++-
+ mt7996/mt7996.h      |  1 +
+ mt7996/mtk_debugfs.c | 14 ++++++++++++--
+ tx.c                 | 19 ++++++++++++++++---
+ 6 files changed, 40 insertions(+), 7 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index 1858f542..e5e5529d 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -462,6 +462,7 @@ struct mt76_txwi_cache {
+ 	dma_addr_t dma_addr;
+ 
+ 	unsigned long jiffies;
++	u8 phy_idx;
+ 
+ 	struct sk_buff *skb;
+ };
+@@ -960,6 +961,7 @@ struct mt76_phy {
+ 	} leds;
+ 	struct mt76_tx_debug tx_dbg_stats;
+ 	struct mt76_rx_debug rx_dbg_stats;
++	int tokens;
+ };
+ 
+ struct mt76_dev {
+@@ -1009,6 +1011,7 @@ struct mt76_dev {
+ 	u16 wed_token_count;
+ 	u16 token_count;
+ 	u16 token_size;
++	u16 token_threshold;
+ 
+ 	spinlock_t rx_token_lock;
+ 	struct idr rx_token;
+@@ -1879,7 +1882,8 @@ static inline bool mt76_queue_is_wed_rx(struct mt76_queue *q)
+ 
+ struct mt76_txwi_cache *
+ mt76_token_release(struct mt76_dev *dev, int token, bool *wake);
+-int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi);
++int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi,
++		       u8 phy_idx);
+ void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked);
+ struct mt76_rxwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token);
+ int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 2f43c0ff..bad4b1b7 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -1770,6 +1770,8 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ 	if (ret)
+ 		return ret;
+ 
++	dev->mt76.token_threshold = MT7996_PER_BAND_TOKEN_SIZE;
++
+ 	ret = mt7996_init_dev_debugfs(&dev->phy);
+ 	if (ret)
+ 		goto error;
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 32f5b859..f9344f28 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -959,7 +959,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 	t = (struct mt76_txwi_cache *)(txwi + mdev->drv->txwi_size);
+ 	t->skb = tx_info->skb;
+ 
+-	id = mt76_token_consume(mdev, &t);
++	id = mt76_token_consume(mdev, &t, mconf->mt76.band_idx);
+ 	if (id < 0) {
+ 		mdev->tx_dbg_stats.tx_drop[MT_TX_DROP_GET_TOKEN_FAIL]++;
+ 		return id;
+@@ -1683,8 +1683,11 @@ void mt7996_tx_token_put(struct mt7996_dev *dev)
+ 
+ 	spin_lock_bh(&dev->mt76.token_lock);
+ 	idr_for_each_entry(&dev->mt76.token, txwi, id) {
++		struct mt76_phy *phy = mt76_dev_phy(&dev->mt76, txwi->phy_idx);
++
+ 		mt7996_txwi_free(dev, txwi, NULL, NULL, NULL);
+ 		dev->mt76.token_count--;
++		phy->tokens--;
+ 	}
+ 	spin_unlock_bh(&dev->mt76.token_lock);
+ 	idr_destroy(&dev->mt76.token);
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index fa884316..04832b0c 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -76,6 +76,7 @@
+ #define MT7996_TOKEN_SIZE		16384
+ #define MT7996_HW_TOKEN_SIZE		8192
+ #define MT7996_SW_TOKEN_SIZE		15360
++#define MT7996_PER_BAND_TOKEN_SIZE	5120
+ 
+ #define MT7996_CFEND_RATE_DEFAULT	0x49	/* OFDM 24M */
+ #define MT7996_CFEND_RATE_11B		0x03	/* 11B LP, 11M */
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index fa8de2db..a4703b07 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2401,7 +2401,7 @@ static int mt7996_sta_info(struct seq_file *s, void *data)
+ static int mt7996_token_read(struct seq_file *s, void *data)
+ {
+ 	struct mt7996_dev *dev = dev_get_drvdata(s->private);
+-	int msdu_id;
++	int msdu_id, i;
+ 	struct mt76_txwi_cache *txwi;
+ 
+ 	seq_printf(s, "Token from host:\n");
+@@ -2410,8 +2410,18 @@ static int mt7996_token_read(struct seq_file *s, void *data)
+ 		seq_printf(s, "%4d (pending time %u ms)\n", msdu_id,
+ 			   jiffies_to_msecs(jiffies - txwi->jiffies));
+ 	}
+-	spin_unlock_bh(&dev->mt76.token_lock);
+ 	seq_printf(s, "\n");
++	for (i = 0; i < __MT_MAX_BAND; i++) {
++		struct mt76_phy *phy = mt76_dev_phy(&dev->mt76, i);
++
++		if (!mt7996_band_valid(dev, i))
++			continue;
++
++		seq_printf(s, "Band%u consume: %d, free: %d total: %d\n",
++			   i, phy->tokens, dev->mt76.token_threshold - phy->tokens,
++			   dev->mt76.token_threshold);
++	}
++	spin_unlock_bh(&dev->mt76.token_lock);
+ 
+ 	return 0;
+ }
+diff --git a/tx.c b/tx.c
+index 5e6e433f..c965f0e3 100644
+--- a/tx.c
++++ b/tx.c
+@@ -837,20 +837,29 @@ void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked)
+ }
+ EXPORT_SYMBOL_GPL(__mt76_set_tx_blocked);
+ 
+-int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi)
++int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi,
++		       u8 phy_idx)
+ {
+-	int token, start = 0;
++	int token = -EINVAL, start = 0;
++	struct mt76_phy *phy = mt76_dev_phy(dev, phy_idx);
+ 
+ 	if (mtk_wed_device_active(&dev->mmio.wed))
+ 		start = dev->mmio.wed.wlan.nbuf;
+ 
+ 	spin_lock_bh(&dev->token_lock);
+ 
++	if (phy->tokens > dev->token_threshold)
++		goto out;
++
+ 	token = idr_alloc(&dev->token, *ptxwi, start, start + dev->token_size,
+ 			  GFP_ATOMIC);
+-	if (token >= start)
++	if (token >= start) {
+ 		dev->token_count++;
+ 
++		(*ptxwi)->phy_idx = phy_idx;
++		phy->tokens++;
++	}
++
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ 	if (mtk_wed_device_active(&dev->mmio.wed) &&
+ 	    token >= dev->mmio.wed.wlan.token_start)
+@@ -860,6 +869,7 @@ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi)
+ 	if (dev->token_count >= dev->token_size - MT76_TOKEN_FREE_THR)
+ 		__mt76_set_tx_blocked(dev, true);
+ 
++out:
+ 	spin_unlock_bh(&dev->token_lock);
+ 
+ 	return token;
+@@ -893,7 +903,10 @@ mt76_token_release(struct mt76_dev *dev, int token, bool *wake)
+ 
+ 	txwi = idr_remove(&dev->token, token);
+ 	if (txwi) {
++		struct mt76_phy *phy = mt76_dev_phy(dev, txwi->phy_idx);
++
+ 		dev->token_count--;
++		phy->tokens--;
+ 
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ 		if (mtk_wed_device_active(&dev->mmio.wed) &&
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0191-mtk-mt76-sync-with-upstream-changes.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0191-mtk-mt76-sync-with-upstream-changes.patch
new file mode 100644
index 0000000..5b27fbc
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0191-mtk-mt76-sync-with-upstream-changes.patch
@@ -0,0 +1,40 @@
+From ae1bd00dbac3464137f2f595b935babc3bea0137 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 31 Jul 2024 11:19:53 +0800
+Subject: [PATCH 191/199] mtk: mt76: sync with upstream changes
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/main.c | 2 +-
+ mt7996/mcu.c  | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 4b48a870..59353603 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -153,7 +153,7 @@ static int mt7996_start(struct ieee80211_hw *hw)
+ 	return ret;
+ }
+ 
+-static void mt7996_stop(struct ieee80211_hw *hw)
++static void mt7996_stop(struct ieee80211_hw *hw, bool suspend)
+ {
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	int band;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 0439157c..03c671f6 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -421,7 +421,7 @@ mt7996_mcu_rx_radar_detected(struct mt7996_dev *dev, struct sk_buff *skb)
+ 						&dev->rdd2_chandef,
+ 						GFP_ATOMIC);
+ 	} else {
+-		ieee80211_radar_detected(mphy->hw);
++		ieee80211_radar_detected(mphy->hw, NULL);
+ 	}
+ 	dev->hw_pattern++;
+ }
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0192-mtk-mt76-mt7996-record-per-antenna-average-data-fram.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0192-mtk-mt76-mt7996-record-per-antenna-average-data-fram.patch
new file mode 100644
index 0000000..bdf7dfa
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0192-mtk-mt76-mt7996-record-per-antenna-average-data-fram.patch
@@ -0,0 +1,76 @@
+From a73d892b561967d17374cc3e04ec2b8d097062b4 Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Thu, 1 Aug 2024 09:11:02 +0800
+Subject: [PATCH 192/199] mtk: mt76: mt7996: record per-antenna average
+ data-frame RSSI
+
+Record per-antenna average data-frame RSSI.
+
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ mt7996/mac.c    | 7 +++++++
+ mt7996/main.c   | 5 ++++-
+ mt7996/mt7996.h | 2 ++
+ 3 files changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index f9344f28..27b4e99f 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -494,10 +494,17 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
+ 		status->chain_signal[3] = to_rssi(MT_PRXV_RCPI3, v3);
+ 
+ 		if (mlink) {
++			int i;
++
+ 			memcpy(mlink->chain_signal, status->chain_signal,
+ 			       IEEE80211_MAX_CHAINS);
+ 			mlink->signal = mt76_rx_signal(mphy->antenna_mask,
+ 						       mlink->chain_signal);
++
++			for (i = 0; i < IEEE80211_MAX_CHAINS; ++i)
++				ewma_avg_signal_add(mlink->chain_signal_avg + i,
++						    -mlink->chain_signal[i]);
++			ewma_avg_signal_add(&mlink->signal_avg, -mlink->signal);
+ 		}
+ 
+ 		/* RXD Group 5 - C-RXV */
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 59353603..29b2583d 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1230,7 +1230,7 @@ static int mt7996_add_link_sta(struct mt7996_dev *dev,
+ 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ 	u8 link_id = link_sta->link_id;
+ 	struct mt7996_link_sta *mlink = NULL;
+-	int idx, ret;
++	int idx, ret, i;
+ 
+ 	if (!rcu_access_pointer(msta->link[link_id])) {
+ 		idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
+@@ -1298,6 +1298,9 @@ static int mt7996_add_link_sta(struct mt7996_dev *dev,
+ 			goto error;
+ 	}
+ 
++	for (i = 0; i < IEEE80211_MAX_CHAINS; ++i)
++		ewma_avg_signal_init(mlink->chain_signal_avg + i);
++	ewma_avg_signal_init(&mlink->signal_avg);
+ 	ewma_avg_signal_init(&mlink->avg_ack_signal);
+ 
+ 	return 0;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 04832b0c..65a88e58 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -320,6 +320,8 @@ struct mt7996_link_sta {
+ 
+ 	s8 chain_signal[IEEE80211_MAX_CHAINS];
+ 	int signal;
++	struct ewma_avg_signal chain_signal_avg[IEEE80211_MAX_CHAINS];
++	struct ewma_avg_signal signal_avg;
+ 
+ 	s8 chain_ack_signal[IEEE80211_MAX_CHAINS];
+ 	int ack_signal;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0193-mtk-mt76-mt7996-remove-default-bss_conf-when-link-be.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0193-mtk-mt76-mt7996-remove-default-bss_conf-when-link-be.patch
new file mode 100644
index 0000000..4a10a4e
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0193-mtk-mt76-mt7996-remove-default-bss_conf-when-link-be.patch
@@ -0,0 +1,31 @@
+From c163bc7222a263f342dbef0568f2bd09044135be Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 5 Aug 2024 14:54:43 +0800
+Subject: [PATCH 193/199] mtk: mt76: mt7996: remove default bss_conf when link
+ becomes MLD
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ mt7996/main.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 29b2583d..76dbb2f3 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -3124,6 +3124,12 @@ mt7996_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	}
+ 
+ 	if (!old_links) {
++		struct mt7996_bss_conf *mconf =
++			mconf_dereference_protected(mvif, 0);
++
++		if (ieee80211_vif_is_mld(vif) && mconf == &mvif->deflink)
++			mt7996_remove_bss_conf(vif, &vif->bss_conf, mconf);
++
+ 		mvif->group_mld_id = get_own_mld_idx(dev->mld_id_mask, true);
+ 		dev->mld_id_mask |= BIT_ULL(mvif->group_mld_id);
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0194-mtk-mt76-mt7996-pass-vif-cfg.assoc-to-mt7996_mac_sta.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0194-mtk-mt76-mt7996-pass-vif-cfg.assoc-to-mt7996_mac_sta.patch
new file mode 100644
index 0000000..8d59550
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0194-mtk-mt76-mt7996-pass-vif-cfg.assoc-to-mt7996_mac_sta.patch
@@ -0,0 +1,40 @@
+From a1517eb6d1c93a2dc4155916497fbe27c3a21b9d Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 5 Aug 2024 14:55:10 +0800
+Subject: [PATCH 194/199] mtk: mt76: mt7996: pass vif->cfg.assoc to
+ mt7996_mac_sta_add_links
+
+Link management functions might leverage mt7996_change_sta_links to
+update sta's links, so the 'assoc' argument of mt7996_mac_sta_add_links()
+should be dynamic decieded by vif->cfg.assoc.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ mt7996/main.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 76dbb2f3..6826f319 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -3157,6 +3157,7 @@ mt7996_change_sta_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	unsigned long add = new_links & ~old_links;
+ 	unsigned long rem = old_links & ~new_links;
+ 	int ret = 0;
++	bool assoc;
+ 
+ 	mt76_vif_dbg(vif, "STA %pM old=0x%x, new=0x%x\n", sta->addr, old_links, new_links);
+ 	mutex_lock(&dev->mt76.mutex);
+@@ -3170,7 +3171,8 @@ mt7996_change_sta_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 		/* 	goto remove; */
+ 	}
+ 
+-	ret = mt7996_mac_sta_add_links(dev, vif, sta, add, false);
++	assoc = vif->type == NL80211_IFTYPE_STATION ? vif->cfg.assoc : false;
++	ret = mt7996_mac_sta_add_links(dev, vif, sta, add, assoc);
+ 	if (ret)
+ 		goto remove;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0195-mtk-mt76-mt7996-separate-hwrro-from-wed.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0195-mtk-mt76-mt7996-separate-hwrro-from-wed.patch
new file mode 100644
index 0000000..29f0504
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0195-mtk-mt76-mt7996-separate-hwrro-from-wed.patch
@@ -0,0 +1,1190 @@
+From 8b65a89860eb0fcd914382b4297efa262f4c158c Mon Sep 17 00:00:00 2001
+From: Rex Lu <rex.lu@mediatek.com>
+Date: Tue, 6 Aug 2024 10:06:10 +0800
+Subject: [PATCH 195/199] mtk: mt76: mt7996: separate hwrro from wed
+
+1. separate hwrro from wed
+2. support mt7996/mt7992 run hwrro 3.0 without wed
+
+Signed-off-by: Rex Lu <rex.lu@mediatek.com>
+---
+ dma.c           |  78 ++++++++--
+ dma.h           |   6 +-
+ mac80211.c      |   5 +
+ mt76.h          |  19 ++-
+ mt7996/dma.c    |  77 +++++++---
+ mt7996/init.c   |  60 ++++----
+ mt7996/mac.c    | 388 +++++++++++++++++++++++++++++++++++++++++++++++-
+ mt7996/mmio.c   |   2 +
+ mt7996/mt7996.h |  53 +++++++
+ mt7996/pci.c    |   4 +
+ mt7996/regs.h   |   1 +
+ 11 files changed, 617 insertions(+), 76 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index 81e76191..7598823a 100644
+--- a/dma.c
++++ b/dma.c
+@@ -231,7 +231,7 @@ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
+ 	struct mt76_queue_entry *entry = &q->entry[q->head];
+ 	struct mt76_desc *desc;
+ 	int idx = q->head;
+-	u32 buf1 = 0, ctrl;
++	u32 buf1 = 0, ctrl, info = 0;
+ 	int rx_token;
+ 
+ 	if (mt76_queue_is_wed_rro_ind(q)) {
+@@ -248,7 +248,7 @@ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
+ 	buf1 = FIELD_PREP(MT_DMA_CTL_SDP0_H, buf->addr >> 32);
+ #endif
+ 
+-	if (mt76_queue_is_wed_rx(q)) {
++	if (mt76_queue_is_wed_rx(q)  || mt76_queue_is_wed_rro_data(q)) {
+ 		if (!rxwi) {
+ 			rxwi = mt76_get_rxwi(dev);
+ 			if (!rxwi) {
+@@ -266,12 +266,24 @@ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
+ 
+ 		buf1 |= FIELD_PREP(MT_DMA_CTL_TOKEN, rx_token);
+ 		ctrl |= MT_DMA_CTL_TO_HOST;
++		rxwi->qid = q - dev->q_rx;
++	}
++
++	if (mt76_queue_is_wed_rro_msdu_pg(q)) {
++		if (dev->drv->rx_rro_fill_msdu_pg(dev, q, buf->addr, data))
++			return	-ENOMEM;
++	}
++
++	if (q->flags & MT_QFLAG_WED_RRO_EN) {
++		info |= FIELD_PREP(MT_DMA_MAGIC_MASK, q->magic_cnt);
++		if ((q->head + 1) == q->ndesc)
++			q->magic_cnt = (q->magic_cnt + 1) % MT_DMA_MAGIC_CNT;
+ 	}
+ 
+ 	WRITE_ONCE(desc->buf0, cpu_to_le32(buf->addr));
+ 	WRITE_ONCE(desc->buf1, cpu_to_le32(buf1));
+ 	WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
+-	WRITE_ONCE(desc->info, 0);
++	WRITE_ONCE(desc->info, cpu_to_le32(info));
+ 
+ done:
+ 	entry->dma_addr[0] = buf->addr;
+@@ -433,7 +445,7 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ 	void *buf = e->buf;
+ 	int reason;
+ 
+-	if (mt76_queue_is_wed_rro_ind(q))
++	if (mt76_queue_is_wed_rro(q))
+ 		goto done;
+ 
+ 	ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
+@@ -558,15 +570,28 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
+ 
+ 	if (mt76_queue_is_wed_rro_data(q) ||
+ 	    mt76_queue_is_wed_rro_msdu_pg(q))
+-		return NULL;
++		goto done;
++
++	if (mt76_queue_is_wed_rro_ind(q)) {
++		struct mt76_wed_rro_ind *cmd;
+ 
+-	if (!mt76_queue_is_wed_rro_ind(q)) {
++		if (flush)
++			goto done;
++
++		cmd = q->entry[idx].buf;
++		if (cmd->magic_cnt != q->magic_cnt)
++			return NULL;
++
++		if (q->tail == q->ndesc - 1)
++			q->magic_cnt = (q->magic_cnt + 1) % MT_DMA_WED_IND_CMD_CNT;
++	} else {
+ 		if (flush)
+ 			q->desc[idx].ctrl |= cpu_to_le32(MT_DMA_CTL_DMA_DONE);
+ 		else if (!(q->desc[idx].ctrl & cpu_to_le32(MT_DMA_CTL_DMA_DONE)))
+ 			return NULL;
+ 	}
+ 
++done:
+ 	q->tail = (q->tail + 1) % q->ndesc;
+ 	q->queued--;
+ 
+@@ -750,8 +775,8 @@ int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
+ 			break;
+ 		}
+ 
+-		qbuf.addr = addr + offset;
+ done:
++		qbuf.addr = addr + offset;
+ 		qbuf.len = len - offset;
+ 		qbuf.skip_unmap = false;
+ 		if (mt76_dma_add_rx_buf(dev, q, &qbuf, buf, NULL) < 0) {
+@@ -856,7 +881,7 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
+ 
+ 	spin_unlock_bh(&q->lock);
+ 
+-	if (mt76_queue_is_wed_rx(q))
++	if (mt76_queue_is_wed_rx(q) || mt76_queue_is_wed_rro(q))
+ 		return;
+ 
+ 	if (!q->rx_page.va)
+@@ -934,8 +959,9 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+ 	bool allow_direct = !mt76_queue_is_wed_rx(q);
+ 	bool more;
+ 
+-	if (IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED) &&
+-	    mt76_queue_is_wed_tx_free(q)) {
++	if ((q->flags & MT_QFLAG_WED_RRO_EN) ||
++	    (IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED) &&
++	    mt76_queue_is_wed_tx_free(q))) {
+ 		dma_idx = Q_READ(q, dma_idx);
+ 		check_ddone = true;
+ 	}
+@@ -957,6 +983,14 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+ 		if (!data)
+ 			break;
+ 
++		if (mt76_queue_is_wed_rro_ind(q) && dev->drv->rx_rro_ind_process)
++			dev->drv->rx_rro_ind_process(dev, data);
++
++		if (mt76_queue_is_wed_rro(q)) {
++			done++;
++			continue;
++		}
++
+ 		if (drop || (len == 0))
+ 			goto free_frag;
+ 
+@@ -1037,11 +1071,18 @@ int mt76_dma_rx_poll(struct napi_struct *napi, int budget)
+ EXPORT_SYMBOL_GPL(mt76_dma_rx_poll);
+ 
+ static int
+-mt76_dma_init(struct mt76_dev *dev,
++__mt76_dma_init(struct mt76_dev *dev, enum mt76_rxq_id qid,
+ 	      int (*poll)(struct napi_struct *napi, int budget))
+ {
+ 	int i;
+ 
++	if (qid < __MT_RXQ_MAX && dev->q_rx[qid].ndesc) {
++		netif_napi_add(&dev->napi_dev, &dev->napi[qid], poll);
++		mt76_dma_rx_fill(dev, &dev->q_rx[qid], false);
++		napi_enable(&dev->napi[qid]);
++		return 0;
++	}
++
+ 	init_dummy_netdev(&dev->napi_dev);
+ 	init_dummy_netdev(&dev->tx_napi_dev);
+ 	snprintf(dev->napi_dev.name, sizeof(dev->napi_dev.name), "%s",
+@@ -1063,6 +1104,20 @@ mt76_dma_init(struct mt76_dev *dev,
+ 	return 0;
+ }
+ 
++static int
++mt76_dma_rx_queue_init(struct mt76_dev *dev, enum mt76_rxq_id qid,
++	      int (*poll)(struct napi_struct *napi, int budget))
++{
++	return __mt76_dma_init(dev, qid, poll);
++}
++
++static int
++mt76_dma_init(struct mt76_dev *dev,
++	      int (*poll)(struct napi_struct *napi, int budget))
++{
++	return __mt76_dma_init(dev, __MT_RXQ_MAX, poll);
++}
++
+ static const struct mt76_queue_ops mt76_dma_ops = {
+ 	.init = mt76_dma_init,
+ 	.alloc = mt76_dma_alloc_queue,
+@@ -1070,6 +1125,7 @@ static const struct mt76_queue_ops mt76_dma_ops = {
+ 	.tx_queue_skb_raw = mt76_dma_tx_queue_skb_raw,
+ 	.tx_queue_skb = mt76_dma_tx_queue_skb,
+ 	.tx_cleanup = mt76_dma_tx_cleanup,
++	.rx_init = mt76_dma_rx_queue_init,
+ 	.rx_cleanup = mt76_dma_rx_cleanup,
+ 	.rx_reset = mt76_dma_rx_reset,
+ 	.kick = mt76_dma_kick_queue,
+diff --git a/dma.h b/dma.h
+index 718122d5..393be98a 100644
+--- a/dma.h
++++ b/dma.h
+@@ -31,8 +31,12 @@
+ #define MT_DMA_CTL_PN_CHK_FAIL		BIT(13)
+ #define MT_DMA_CTL_VER_MASK		BIT(7)
+ 
+-#define MT_DMA_RRO_EN		BIT(13)
++#define MT_DMA_SDP0			GENMASK(15, 0)
++#define MT_DMA_TOKEN_ID			GENMASK(31, 16)
++#define MT_DMA_MAGIC_MASK		GENMASK(31, 28)
++#define MT_DMA_RRO_EN			BIT(13)
+ 
++#define MT_DMA_MAGIC_CNT		16
+ #define MT_DMA_WED_IND_CMD_CNT		8
+ #define MT_DMA_WED_IND_REASON		GENMASK(15, 12)
+ 
+diff --git a/mac80211.c b/mac80211.c
+index d5f842db..d81bf667 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -732,6 +732,7 @@ static void mt76_rx_release_amsdu(struct mt76_phy *phy, enum mt76_rxq_id q)
+ 	struct sk_buff *skb = phy->rx_amsdu[q].head;
+ 	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+ 	struct mt76_dev *dev = phy->dev;
++	struct mt76_queue *rxq = &dev->q_rx[q];
+ 
+ 	phy->rx_amsdu[q].head = NULL;
+ 	phy->rx_amsdu[q].tail = NULL;
+@@ -763,6 +764,10 @@ static void mt76_rx_release_amsdu(struct mt76_phy *phy, enum mt76_rxq_id q)
+ 			return;
+ 		}
+ 	}
++
++	if (mt76_queue_is_wed_rro_data(rxq))
++		q = MT_RXQ_RRO_IND;
++
+ 	__skb_queue_tail(&dev->rx_skb[q], skb);
+ }
+ 
+diff --git a/mt76.h b/mt76.h
+index e5e5529d..58e9b0b7 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -276,6 +276,7 @@ struct mt76_queue {
+ 
+ 	u8 buf_offset;
+ 	u16 flags;
++	u8 magic_cnt;
+ 
+ 	struct mtk_wed_device *wed;
+ 	u32 wed_regs;
+@@ -329,6 +330,9 @@ struct mt76_queue_ops {
+ 	void (*tx_cleanup)(struct mt76_dev *dev, struct mt76_queue *q,
+ 			   bool flush);
+ 
++	int (*rx_init)(struct mt76_dev *dev, enum mt76_rxq_id qid,
++		       int (*poll)(struct napi_struct *napi, int budget));
++
+ 	void (*rx_cleanup)(struct mt76_dev *dev, struct mt76_queue *q);
+ 
+ 	void (*kick)(struct mt76_dev *dev, struct mt76_queue *q);
+@@ -472,6 +476,7 @@ struct mt76_rxwi_cache {
+ 	dma_addr_t dma_addr;
+ 
+ 	void *ptr;
++	u8 qid;
+ };
+ 
+ struct mt76_rx_tid {
+@@ -578,6 +583,10 @@ struct mt76_driver_ops {
+ 	void (*rx_skb)(struct mt76_dev *dev, enum mt76_rxq_id q,
+ 		       struct sk_buff *skb, u32 *info);
+ 
++	void (*rx_rro_ind_process)(struct mt76_dev *dev, void *data);
++	int (*rx_rro_fill_msdu_pg)(struct mt76_dev *dev, struct mt76_queue *q,
++				   dma_addr_t p, void *data);
++
+ 	void (*rx_poll_complete)(struct mt76_dev *dev, enum mt76_rxq_id q);
+ 
+ 	void (*sta_ps)(struct mt76_dev *dev, struct ieee80211_sta *sta,
+@@ -1306,6 +1315,7 @@ static inline int mt76_wed_dma_setup(struct mt76_dev *dev, struct mt76_queue *q,
+ #define mt76_tx_queue_skb(dev, ...)	(dev)->mt76.queue_ops->tx_queue_skb(&((dev)->mphy), __VA_ARGS__)
+ #define mt76_queue_rx_reset(dev, ...)	(dev)->mt76.queue_ops->rx_reset(&((dev)->mt76), __VA_ARGS__)
+ #define mt76_queue_tx_cleanup(dev, ...)	(dev)->mt76.queue_ops->tx_cleanup(&((dev)->mt76), __VA_ARGS__)
++#define mt76_queue_rx_init(dev, ...)	(dev)->mt76.queue_ops->rx_init(&((dev)->mt76), __VA_ARGS__)
+ #define mt76_queue_rx_cleanup(dev, ...)	(dev)->mt76.queue_ops->rx_cleanup(&((dev)->mt76), __VA_ARGS__)
+ #define mt76_queue_kick(dev, ...)	(dev)->mt76.queue_ops->kick(&((dev)->mt76), __VA_ARGS__)
+ #define mt76_queue_reset(dev, ...)	(dev)->mt76.queue_ops->reset_q(&((dev)->mt76), __VA_ARGS__)
+@@ -1871,13 +1881,8 @@ static inline bool mt76_queue_is_wed_rro_msdu_pg(struct mt76_queue *q)
+ 
+ static inline bool mt76_queue_is_wed_rx(struct mt76_queue *q)
+ {
+-	if (!(q->flags & MT_QFLAG_WED))
+-		return false;
+-
+-	return FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX ||
+-	       mt76_queue_is_wed_rro_ind(q) || mt76_queue_is_wed_rro_data(q) ||
+-	       mt76_queue_is_wed_rro_msdu_pg(q);
+-
++	return (q->flags & MT_QFLAG_WED) &&
++	       FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX;
+ }
+ 
+ struct mt76_txwi_cache *
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index bbc3814d..e4b0af29 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -300,7 +300,7 @@ void mt7996_dma_start(struct mt7996_dev *dev, bool reset, bool wed_reset)
+ 	}
+ 
+ 	if (mt7996_band_valid(dev, MT_BAND2))
+-		irq_mask |= MT_INT_BAND2_RX_DONE;
++		irq_mask |= MT_INT_BAND2_RX_DONE | MT_INT_TX_RX_DONE_EXT;
+ 
+ 	if (mtk_wed_device_active(wed) && wed_reset) {
+ 		u32 wed_irq_mask = irq_mask;
+@@ -465,7 +465,6 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ 	mt7996_dma_start(dev, reset, true);
+ }
+ 
+-#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ int mt7996_dma_rro_init(struct mt7996_dev *dev)
+ {
+ 	struct mt76_dev *mdev = &dev->mt76;
+@@ -474,7 +473,9 @@ int mt7996_dma_rro_init(struct mt7996_dev *dev)
+ 
+ 	/* ind cmd */
+ 	mdev->q_rx[MT_RXQ_RRO_IND].flags = MT_WED_RRO_Q_IND;
+-	mdev->q_rx[MT_RXQ_RRO_IND].wed = &mdev->mmio.wed;
++	if (mtk_wed_device_active(&mdev->mmio.wed) &&
++	    mtk_wed_get_rx_capa(&mdev->mmio.wed))
++		mdev->q_rx[MT_RXQ_RRO_IND].wed = &mdev->mmio.wed;
+ 	ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_RRO_IND],
+ 			       MT_RXQ_ID(MT_RXQ_RRO_IND),
+ 			       MT7996_RX_RING_SIZE,
+@@ -485,7 +486,9 @@ int mt7996_dma_rro_init(struct mt7996_dev *dev)
+ 	/* rx msdu page queue for band0 */
+ 	mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND0].flags =
+ 		MT_WED_RRO_Q_MSDU_PG(0) | MT_QFLAG_WED_RRO_EN;
+-	mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND0].wed = &mdev->mmio.wed;
++	if (mtk_wed_device_active(&mdev->mmio.wed) &&
++	    mtk_wed_get_rx_capa(&mdev->mmio.wed))
++		mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND0].wed = &mdev->mmio.wed;
+ 	ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND0],
+ 			       MT_RXQ_ID(MT_RXQ_MSDU_PAGE_BAND0),
+ 			       MT7996_RX_RING_SIZE,
+@@ -498,7 +501,9 @@ int mt7996_dma_rro_init(struct mt7996_dev *dev)
+ 		/* rx msdu page queue for band1 */
+ 		mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND1].flags =
+ 			MT_WED_RRO_Q_MSDU_PG(1) | MT_QFLAG_WED_RRO_EN;
+-		mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND1].wed = &mdev->mmio.wed;
++		if (mtk_wed_device_active(&mdev->mmio.wed) &&
++		    mtk_wed_get_rx_capa(&mdev->mmio.wed))
++			mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND1].wed = &mdev->mmio.wed;
+ 		ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND1],
+ 				       MT_RXQ_ID(MT_RXQ_MSDU_PAGE_BAND1),
+ 				       MT7996_RX_RING_SIZE,
+@@ -512,7 +517,9 @@ int mt7996_dma_rro_init(struct mt7996_dev *dev)
+ 		/* rx msdu page queue for band2 */
+ 		mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND2].flags =
+ 			MT_WED_RRO_Q_MSDU_PG(2) | MT_QFLAG_WED_RRO_EN;
+-		mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND2].wed = &mdev->mmio.wed;
++		if (mtk_wed_device_active(&mdev->mmio.wed) &&
++		    mtk_wed_get_rx_capa(&mdev->mmio.wed))
++			mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND2].wed = &mdev->mmio.wed;
+ 		ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND2],
+ 				       MT_RXQ_ID(MT_RXQ_MSDU_PAGE_BAND2),
+ 				       MT7996_RX_RING_SIZE,
+@@ -522,15 +529,37 @@ int mt7996_dma_rro_init(struct mt7996_dev *dev)
+ 			return ret;
+ 	}
+ 
+-	irq_mask = mdev->mmio.irqmask | MT_INT_RRO_RX_DONE |
+-		   MT_INT_TX_DONE_BAND2;
+-	mt76_wr(dev, MT_INT_MASK_CSR, irq_mask);
+-	mtk_wed_device_start_hw_rro(&mdev->mmio.wed, irq_mask, false);
+-	mt7996_irq_enable(dev, irq_mask);
++
++
++	if (mtk_wed_device_active(&mdev->mmio.wed)) {
++		irq_mask = mdev->mmio.irqmask | MT_INT_RRO_RX_DONE |
++			   MT_INT_TX_DONE_BAND2;
++
++		if (mtk_wed_get_rx_capa(&mdev->mmio.wed))
++			irq_mask &= ~MT_INT_RX_DONE_RRO_IND;
++
++		mt76_wr(dev, MT_INT_MASK_CSR, irq_mask);
++		mtk_wed_device_start_hw_rro(&mdev->mmio.wed, irq_mask, false);
++		mt7996_irq_enable(dev, irq_mask);
++	} else {
++		if (is_mt7996(&dev->mt76)) {
++			mt76_queue_rx_init(dev, MT_RXQ_TXFREE_BAND0, mt76_dma_rx_poll);
++			mt76_queue_rx_init(dev, MT_RXQ_TXFREE_BAND2, mt76_dma_rx_poll);
++			mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND1, mt76_dma_rx_poll);
++			mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND2, mt76_dma_rx_poll);
++		}
++		mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND0, mt76_dma_rx_poll);
++		mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND1, mt76_dma_rx_poll);
++		if (mt7996_band_valid(dev, MT_BAND2))
++			mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND2, mt76_dma_rx_poll);
++		mt76_queue_rx_init(dev, MT_RXQ_RRO_IND, mt76_dma_rx_poll);
++		mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND0, mt76_dma_rx_poll);
++
++		mt7996_irq_enable(dev, MT_INT_RRO_RX_DONE);
++	}
+ 
+ 	return 0;
+ }
+-#endif /* CONFIG_NET_MEDIATEK_SOC_WED */
+ 
+ int mt7996_dma_init(struct mt7996_dev *dev)
+ {
+@@ -691,12 +720,12 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 			return ret;
+ 	}
+ 
+-	if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed) &&
+-	    dev->has_rro) {
++	if (dev->has_rro) {
+ 		/* rx rro data queue for band0 */
+ 		dev->mt76.q_rx[MT_RXQ_RRO_BAND0].flags =
+ 			MT_WED_RRO_Q_DATA(0) | MT_QFLAG_WED_RRO_EN;
+-		dev->mt76.q_rx[MT_RXQ_RRO_BAND0].wed = wed;
++		if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed))
++			dev->mt76.q_rx[MT_RXQ_RRO_BAND0].wed = wed;
+ 		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_RRO_BAND0],
+ 				       MT_RXQ_ID(MT_RXQ_RRO_BAND0),
+ 				       MT7996_RX_RING_SIZE,
+@@ -708,7 +737,8 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 		if (is_mt7992(&dev->mt76)) {
+ 			dev->mt76.q_rx[MT_RXQ_RRO_BAND1].flags =
+ 				MT_WED_RRO_Q_DATA(1) | MT_QFLAG_WED_RRO_EN;
+-			dev->mt76.q_rx[MT_RXQ_RRO_BAND1].wed = wed;
++			if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed))
++				dev->mt76.q_rx[MT_RXQ_RRO_BAND1].wed = wed;
+ 			ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_RRO_BAND1],
+ 					       MT_RXQ_ID(MT_RXQ_RRO_BAND1),
+ 					       MT7996_RX_RING_SIZE,
+@@ -718,9 +748,10 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 				return ret;
+ 		} else {
+ 			/* tx free notify event from WA for band0 */
+-			dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
+-			dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed;
+-
++			if (mtk_wed_device_active(wed)) {
++				dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
++				dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed;
++			}
+ 			ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0],
+ 					       MT_RXQ_ID(MT_RXQ_TXFREE_BAND0),
+ 					       MT7996_RX_MCU_RING_SIZE,
+@@ -734,7 +765,8 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 			/* rx rro data queue for band2 */
+ 			dev->mt76.q_rx[MT_RXQ_RRO_BAND2].flags =
+ 				MT_WED_RRO_Q_DATA(1) | MT_QFLAG_WED_RRO_EN;
+-			dev->mt76.q_rx[MT_RXQ_RRO_BAND2].wed = wed;
++			if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed))
++				dev->mt76.q_rx[MT_RXQ_RRO_BAND2].wed = wed;
+ 			ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_RRO_BAND2],
+ 					       MT_RXQ_ID(MT_RXQ_RRO_BAND2),
+ 					       MT7996_RX_RING_SIZE,
+@@ -811,6 +843,11 @@ void mt7996_dma_reset(struct mt7996_dev *dev, bool force)
+ 		dev_info(dev->mt76.dev,"%s L1 SER rx queue clean up done.",
+ 			 wiphy_name(dev->mt76.hw->wiphy));
+ 
++	if (dev->has_rro && !mtk_wed_device_active(&dev->mt76.mmio.wed)) {
++		mt7996_rro_msdu_pg_free(dev);
++		mt7996_rx_token_put(dev);
++	}
++
+ 	mt76_tx_status_check(&dev->mt76, true);
+ 
+ 	if (!force)
+diff --git a/mt7996/init.c b/mt7996/init.c
+index bad4b1b7..0e647356 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -931,7 +931,6 @@ void mt7996_wfsys_reset(struct mt7996_dev *dev)
+ 
+ void mt7996_rro_hw_init(struct mt7996_dev *dev)
+ {
+-#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ 	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+ 	u32 reg = MT_RRO_ADDR_ELEM_SEG_ADDR0;
+ 	int i;
+@@ -939,6 +938,10 @@ void mt7996_rro_hw_init(struct mt7996_dev *dev)
+ 	if (!dev->has_rro)
+ 		return;
+ 
++	INIT_LIST_HEAD(&dev->wed_rro.pg_addr_cache);
++	for (i = 0; i < MT7996_RRO_MSDU_PG_HASH_SIZE; i++)
++		INIT_LIST_HEAD(&dev->wed_rro.pg_hash_head[i]);
++
+ 	if (is_mt7992(&dev->mt76)) {
+ 		/* set emul 3.0 function */
+ 		mt76_wr(dev, MT_RRO_3_0_EMU_CONF,
+@@ -947,9 +950,6 @@ void mt7996_rro_hw_init(struct mt7996_dev *dev)
+ 		mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE0,
+ 			dev->wed_rro.addr_elem[0].phy_addr);
+ 	} else {
+-		INIT_LIST_HEAD(&dev->wed_rro.pg_addr_cache);
+-		for (i = 0; i < MT7996_RRO_MSDU_PG_HASH_SIZE; i++)
+-			INIT_LIST_HEAD(&dev->wed_rro.pg_hash_head[i]);
+ 
+ 		/* TODO: remove line after WM has set */
+ 		mt76_clear(dev, WF_RRO_AXI_MST_CFG, WF_RRO_AXI_MST_CFG_DIDX_OK);
+@@ -972,18 +972,24 @@ void mt7996_rro_hw_init(struct mt7996_dev *dev)
+ 		mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE1,
+ 			MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE);
+ 	}
+-	wed->wlan.ind_cmd.win_size = ffs(MT7996_RRO_WINDOW_MAX_LEN) - 6;
+-	if (is_mt7996(&dev->mt76))
+-		wed->wlan.ind_cmd.particular_sid = MT7996_RRO_MAX_SESSION;
+-	else
+-		wed->wlan.ind_cmd.particular_sid = 1;
+-	wed->wlan.ind_cmd.particular_se_phys = dev->wed_rro.session.phy_addr;
+-	wed->wlan.ind_cmd.se_group_nums = MT7996_RRO_ADDR_ELEM_LEN;
+-	wed->wlan.ind_cmd.ack_sn_addr = MT_RRO_ACK_SN_CTRL;
+ 
+-	mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE0, 0x15010e00);
+-	mt76_set(dev, MT_RRO_IND_CMD_SIGNATURE_BASE1,
+-		 MT_RRO_IND_CMD_SIGNATURE_BASE1_EN);
++	if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed)) {
++		wed->wlan.ind_cmd.win_size = ffs(MT7996_RRO_WINDOW_MAX_LEN) - 6;
++		if (is_mt7996(&dev->mt76))
++			wed->wlan.ind_cmd.particular_sid = MT7996_RRO_MAX_SESSION;
++		else
++			wed->wlan.ind_cmd.particular_sid = 1;
++		wed->wlan.ind_cmd.particular_se_phys = dev->wed_rro.session.phy_addr;
++		wed->wlan.ind_cmd.se_group_nums = MT7996_RRO_ADDR_ELEM_LEN;
++		wed->wlan.ind_cmd.ack_sn_addr = MT_RRO_ACK_SN_CTRL;
++
++		mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE0, 0x15010e00);
++		mt76_set(dev, MT_RRO_IND_CMD_SIGNATURE_BASE1,
++			MT_RRO_IND_CMD_SIGNATURE_BASE1_EN);
++	} else {
++		mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE0, 0);
++		mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE1, 0);
++	}
+ 
+ 	/* particular session configure */
+ 	/* use max session idx + 1 as particular session id */
+@@ -1012,12 +1018,10 @@ void mt7996_rro_hw_init(struct mt7996_dev *dev)
+ 	mt76_wr(dev, MT_RRO_HOST_INT_ENA,
+ 		MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA);
+ 
+-#endif
+ }
+ 
+ static int mt7996_wed_rro_init(struct mt7996_dev *dev)
+ {
+-#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ 	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+ 	struct mt7996_wed_rro_addr *addr;
+ 	void *ptr;
+@@ -1026,9 +1030,6 @@ static int mt7996_wed_rro_init(struct mt7996_dev *dev)
+ 	if (!dev->has_rro)
+ 		return 0;
+ 
+-	if (!mtk_wed_device_active(wed))
+-		return 0;
+-
+ 	for (i = 0; i < ARRAY_SIZE(dev->wed_rro.ba_bitmap); i++) {
+ 		ptr = dmam_alloc_coherent(dev->mt76.dma_dev,
+ 					  MT7996_RRO_BA_BITMAP_CR_SIZE,
+@@ -1059,9 +1060,8 @@ static int mt7996_wed_rro_init(struct mt7996_dev *dev)
+ 			addr->signature = 0xff;
+ 			addr++;
+ 		}
+-
+-		wed->wlan.ind_cmd.addr_elem_phys[i] =
+-			dev->wed_rro.addr_elem[i].phy_addr;
++		if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed))
++			wed->wlan.ind_cmd.addr_elem_phys[i] = dev->wed_rro.addr_elem[i].phy_addr;
+ 	}
+ 
+ 	for (i = 0; i < MT7996_RRO_MSDU_PG_CR_CNT; i++) {
+@@ -1093,22 +1093,15 @@ static int mt7996_wed_rro_init(struct mt7996_dev *dev)
+ 	mt7996_rro_hw_init(dev);
+ 
+ 	return mt7996_dma_rro_init(dev);
+-#else
+-	return 0;
+-#endif
+ }
+ 
+ static void mt7996_wed_rro_free(struct mt7996_dev *dev)
+ {
+-#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ 	int i;
+ 
+ 	if (!dev->has_rro)
+ 		return;
+ 
+-	if (!mtk_wed_device_active(&dev->mt76.mmio.wed))
+-		return;
+-
+ 	for (i = 0; i < ARRAY_SIZE(dev->wed_rro.ba_bitmap); i++) {
+ 		if (!dev->wed_rro.ba_bitmap[i].ptr)
+ 			continue;
+@@ -1138,12 +1131,10 @@ static void mt7996_wed_rro_free(struct mt7996_dev *dev)
+ 			   sizeof(struct mt7996_wed_rro_addr),
+ 			   dev->wed_rro.session.ptr,
+ 			   dev->wed_rro.session.phy_addr);
+-#endif
+ }
+ 
+ static void mt7996_wed_rro_work(struct work_struct *work)
+ {
+-#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ 	struct mt7996_dev *dev;
+ 	LIST_HEAD(list);
+ 
+@@ -1186,7 +1177,6 @@ reset:
+ out:
+ 		kfree(e);
+ 	}
+-#endif
+ }
+ 
+ int mt7996_get_chip_sku(struct mt7996_dev *dev)
+@@ -1828,6 +1818,10 @@ void mt7996_unregister_device(struct mt7996_dev *dev)
+ 	mt7996_mcu_exit(dev);
+ 	mt7996_tx_token_put(dev);
+ 	mt7996_dma_cleanup(dev);
++	if (dev->has_rro && !mtk_wed_device_active(&dev->mt76.mmio.wed)) {
++		mt7996_rro_msdu_pg_free(dev);
++		mt7996_rx_token_put(dev);
++	}
+ 	tasklet_disable(&dev->mt76.irq_tasklet);
+ 
+ 	mt76_free_device(&dev->mt76);
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 27b4e99f..c0f282d1 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1505,6 +1505,387 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+ 	}
+ }
+ 
++static struct mt7996_msdu_pg_addr *
++mt7996_alloc_pg_addr(struct mt7996_dev *dev)
++{
++	struct mt7996_msdu_pg_addr *p;
++	int size;
++
++	size = L1_CACHE_ALIGN(sizeof(*p));
++	p = kzalloc(size, GFP_ATOMIC);
++	if (!p)
++		return NULL;
++
++	INIT_LIST_HEAD(&p->list);
++
++	return p;
++}
++
++static struct mt7996_msdu_pg_addr *
++__mt7996_get_pg_addr(struct mt7996_dev *dev)
++{
++	struct mt7996_msdu_pg_addr *p = NULL;
++
++	spin_lock(&dev->wed_rro.lock);
++	if (!list_empty(&dev->wed_rro.pg_addr_cache)) {
++		p = list_first_entry(&dev->wed_rro.pg_addr_cache,
++				     struct mt7996_msdu_pg_addr,
++				     list);
++		if (p)
++			list_del(&p->list);
++	}
++	spin_unlock(&dev->wed_rro.lock);
++
++	return p;
++}
++
++struct mt7996_msdu_pg_addr *
++mt7996_get_pg_addr(struct mt7996_dev *dev)
++{
++	struct mt7996_msdu_pg_addr *p = __mt7996_get_pg_addr(dev);
++
++	if (p)
++		return p;
++
++	return mt7996_alloc_pg_addr(dev);
++}
++
++static void
++mt7996_put_pg_addr(struct mt7996_dev *dev,
++		struct mt7996_msdu_pg_addr *p)
++{
++	if (!p)
++		return;
++
++	if (p->buf) {
++		skb_free_frag(p->buf);
++		p->buf = NULL;
++	}
++
++	spin_lock(&dev->wed_rro.lock);
++	list_add(&p->list, &dev->wed_rro.pg_addr_cache);
++	spin_unlock(&dev->wed_rro.lock);
++}
++
++static void
++mt7996_free_pg_addr(struct mt7996_dev *dev)
++{
++	struct mt7996_msdu_pg_addr *pg_addr;
++
++	local_bh_disable();
++	while ((pg_addr = __mt7996_get_pg_addr(dev)) != NULL) {
++		if (pg_addr->buf) {
++			skb_free_frag(pg_addr->buf);
++			pg_addr->buf = NULL;
++		}
++		kfree(pg_addr);
++	}
++	local_bh_enable();
++}
++
++static u32
++mt7996_rro_msdu_pg_hash(dma_addr_t pa)
++{
++	u32 sum = 0;
++	u16 i = 0;
++
++	while (pa != 0) {
++		sum += (u32) ((pa & 0xff) + i) % MT7996_RRO_MSDU_PG_HASH_SIZE;
++		pa >>= 8;
++		i += 13;
++	}
++
++	return sum % MT7996_RRO_MSDU_PG_HASH_SIZE;
++}
++
++static struct mt7996_msdu_pg_addr *
++mt7996_rro_msdu_pg_search(struct mt7996_dev *dev, dma_addr_t pa)
++{
++	struct mt7996_msdu_pg_addr *pg_addr, *tmp;
++	u32 hash_idx =  mt7996_rro_msdu_pg_hash(pa);
++	struct list_head *head;
++	u8 found = 0;
++
++	spin_lock(&dev->wed_rro.lock);
++	head = &dev->wed_rro.pg_hash_head[hash_idx];
++	list_for_each_entry_safe(pg_addr, tmp, head, list) {
++		if (pg_addr->dma_addr == pa) {
++			list_del(&pg_addr->list);
++			found = 1;
++			break;
++		}
++	}
++	spin_unlock(&dev->wed_rro.lock);
++
++	return (found == 1) ? pg_addr : NULL;
++}
++
++void mt7996_rro_msdu_pg_free(struct mt7996_dev *dev)
++{
++	struct mt7996_msdu_pg_addr *pg_addr, *tmp;
++	struct list_head *head;
++	u32 i;
++
++	local_bh_disable();
++	for (i = 0; i < MT7996_RRO_MSDU_PG_HASH_SIZE; i++) {
++		head = &dev->wed_rro.pg_hash_head[i];
++		list_for_each_entry_safe(pg_addr, tmp, head, list) {
++			list_del_init(&pg_addr->list);
++			dma_unmap_single(dev->mt76.dma_dev, pg_addr->dma_addr,
++					 SKB_WITH_OVERHEAD(pg_addr->q->buf_size),
++					 DMA_FROM_DEVICE);
++			if (pg_addr->buf) {
++				skb_free_frag(pg_addr->buf);
++				pg_addr->buf = NULL;
++			}
++			kfree(pg_addr);
++		}
++	}
++	local_bh_enable();
++
++	mt7996_free_pg_addr(dev);
++
++	mt76_for_each_q_rx(&dev->mt76, i) {
++		struct mt76_queue *q = &dev->mt76.q_rx[i];
++		struct page *page;
++
++		if (mt76_queue_is_wed_rro_msdu_pg(q)) {
++			if (!q->rx_page.va)
++				continue;
++
++			page = virt_to_page(q->rx_page.va);
++			__page_frag_cache_drain(page, q->rx_page.pagecnt_bias);
++			memset(&q->rx_page, 0, sizeof(q->rx_page));
++		}
++	}
++}
++
++void mt7996_rx_token_put(struct mt7996_dev *dev)
++{
++	struct mt76_queue *q;
++	struct page *page;
++	int i;
++
++	for (i = 0; i < dev->mt76.rx_token_size; i++) {
++		struct mt76_rxwi_cache *r;
++
++		r = mt76_rx_token_release(&dev->mt76, i);
++		if (!r || !r->ptr)
++			continue;
++
++		q = &dev->mt76.q_rx[r->qid];
++		dma_unmap_single(dev->mt76.dma_dev, r->dma_addr,
++				 SKB_WITH_OVERHEAD(q->buf_size),
++				 DMA_FROM_DEVICE);
++		skb_free_frag(r->ptr);
++		r->dma_addr = 0;
++		r->ptr = NULL;
++
++		mt76_put_rxwi(&dev->mt76, r);
++	}
++
++	mt76_for_each_q_rx(&dev->mt76, i) {
++		struct mt76_queue *q = &dev->mt76.q_rx[i];
++
++		if (mt76_queue_is_wed_rro_data(q)) {
++			if (!q->rx_page.va)
++				continue;
++
++			page = virt_to_page(q->rx_page.va);
++			__page_frag_cache_drain(page, q->rx_page.pagecnt_bias);
++			memset(&q->rx_page, 0, sizeof(q->rx_page));
++		}
++	}
++
++	mt76_free_pending_rxwi(&dev->mt76);
++}
++
++int mt7996_rro_fill_msdu_page(struct mt76_dev *mdev, struct mt76_queue *q,
++			 dma_addr_t p, void *data)
++{
++	struct mt7996_msdu_pg_addr *pg_addr;
++	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
++	struct mt7996_msdu_pg *pg = data;
++	u32 hash_idx;
++
++	pg->owner = 1;
++	pg_addr = mt7996_get_pg_addr(dev);
++	if (!pg_addr)
++		return -ENOMEM;
++
++	pg_addr->buf = data;
++	pg_addr->dma_addr = p;
++	pg_addr->q = q;
++	hash_idx = mt7996_rro_msdu_pg_hash(pg_addr->dma_addr);
++
++	spin_lock(&dev->wed_rro.lock);
++	list_add_tail(&pg_addr->list,
++		      &dev->wed_rro.pg_hash_head[hash_idx]);
++	spin_unlock(&dev->wed_rro.lock);
++
++	return 0;
++}
++
++static struct mt7996_wed_rro_addr *
++mt7996_rro_get_addr_elem(struct mt7996_dev *dev, u16 seid, u16 sn)
++{
++	u32 idx;
++	void *addr;
++
++	if (seid == MT7996_RRO_MAX_SESSION) {
++		addr = dev->wed_rro.session.ptr;
++		idx = sn % MT7996_RRO_WINDOW_MAX_LEN;
++	} else {
++		addr = dev->wed_rro.addr_elem[seid/ MT7996_RRO_BA_BITMAP_SESSION_SIZE].ptr;
++		idx = (seid % MT7996_RRO_BA_BITMAP_SESSION_SIZE) * MT7996_RRO_WINDOW_MAX_LEN
++			+ (sn % MT7996_RRO_WINDOW_MAX_LEN);
++	}
++	return addr + idx * sizeof(struct mt7996_wed_rro_addr);
++}
++
++void mt7996_rro_rx_process(struct mt76_dev *mdev, void *data)
++{
++	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
++	struct mt76_wed_rro_ind *cmd = (struct mt76_wed_rro_ind *)data;
++	struct mt76_rxwi_cache *r;
++	struct mt76_rx_status *status;
++	struct mt76_queue *q;
++	struct mt7996_wed_rro_addr *elem;
++	struct mt7996_msdu_pg_addr *pg_addr = NULL;
++	struct mt7996_msdu_pg *pg = NULL;
++	struct mt7996_rro_hif *rxd;
++	struct sk_buff *skb;
++	dma_addr_t msdu_pg_pa;
++	int len, data_len, i, j, sn;
++	void *buf;
++	u8 more, qid;
++	u32 info = 0;
++
++	for (i = 0; i < cmd->ind_cnt; i++) {
++		sn = (cmd->start_sn + i) & GENMASK(11, 0);
++		elem = mt7996_rro_get_addr_elem(dev, cmd->se_id, sn);
++		if (elem->signature != (sn / MT7996_RRO_WINDOW_MAX_LEN)) {
++			elem->signature = 0xff;
++			goto update_ack_sn;
++		}
++
++		msdu_pg_pa = elem->head_high;
++		msdu_pg_pa <<= 32;
++		msdu_pg_pa |= elem->head_low;
++
++		for (j = 0; j < elem->count; j++) {
++			if (pg_addr == NULL) {
++				pg_addr = mt7996_rro_msdu_pg_search(dev, msdu_pg_pa);
++
++				if (pg_addr == NULL) {
++					dev_info(mdev->dev, "pg_addr(%llx) search fail\n",
++						 msdu_pg_pa);
++					continue;
++				}
++
++				dma_unmap_single(mdev->dma_dev, pg_addr->dma_addr,
++						 SKB_WITH_OVERHEAD(pg_addr->q->buf_size),
++						 DMA_FROM_DEVICE);
++
++				pg = (struct mt7996_msdu_pg *) pg_addr->buf;
++			}
++
++			rxd = &pg->rxd[j % MT7996_MAX_HIF_RXD_IN_PG];
++			more = !rxd->ls;
++			len = rxd->sdl;
++
++			r = mt76_rx_token_release(mdev, rxd->rx_token_id);
++			if (!r)
++				goto next_page_chk;
++
++			qid = r->qid;
++			buf = r->ptr;
++			q = &mdev->q_rx[qid];
++			dma_unmap_single(mdev->dma_dev, r->dma_addr,
++					 SKB_WITH_OVERHEAD(q->buf_size),
++					 DMA_FROM_DEVICE);
++			r->dma_addr = 0;
++			r->ptr = NULL;
++			mt76_put_rxwi(mdev, r);
++			if (!buf)
++				goto next_page_chk;
++
++			if (q->rx_head)
++				data_len = q->buf_size;
++			else
++				data_len = SKB_WITH_OVERHEAD(q->buf_size);
++
++			if (data_len < len + q->buf_offset) {
++				dev_kfree_skb(q->rx_head);
++				skb_free_frag(buf);
++				q->rx_head = NULL;
++				goto next_page_chk;
++			}
++
++			if (q->rx_head) {
++				/* TDO: fragment error, skip handle */
++				//mt76_add_fragment(mdev, q, buf, len, more, info);
++				skb_free_frag(buf);
++				if (!more) {
++					dev_kfree_skb(q->rx_head);
++					q->rx_head = NULL;
++				}
++				goto next_page_chk;
++			}
++
++			if (!more && !mt7996_rx_check(mdev, buf, len))
++				goto next_page_chk;
++
++			skb = build_skb(buf, q->buf_size);
++			if (!skb)
++				goto next_page_chk;
++
++			skb_reserve(skb, q->buf_offset);
++			__skb_put(skb, len);
++
++			if (cmd->ind_reason == 1 || cmd->ind_reason == 2) {
++				dev_kfree_skb(skb);
++				goto next_page_chk;
++			}
++
++			if (more) {
++				q->rx_head = skb;
++				goto next_page_chk;
++			}
++
++			status = (struct mt76_rx_status *)skb->cb;
++			if (cmd->se_id != MT7996_RRO_MAX_SESSION)
++				status->aggr = true;
++
++			mt7996_queue_rx_skb(mdev, qid, skb, &info);
++
++next_page_chk:
++			if ((j + 1) % MT7996_MAX_HIF_RXD_IN_PG == 0) {
++				msdu_pg_pa = pg->next_pg_h;
++				msdu_pg_pa <<= 32;
++				msdu_pg_pa |= pg->next_pg_l;
++				mt7996_put_pg_addr(dev, pg_addr);
++				pg_addr = NULL;
++			}
++		}
++update_ack_sn:
++		if ((i + 1) % 4 == 0)
++			mt76_wr(dev, MT_RRO_ACK_SN_CTRL,
++				FIELD_PREP(MT_RRO_ACK_SN_CTRL_SESSION_MASK, cmd->se_id) |
++				FIELD_PREP(MT_RRO_ACK_SN_CTRL_SN_MASK, sn));
++		if (pg_addr) {
++			mt7996_put_pg_addr(dev, pg_addr);
++			pg_addr = NULL;
++		}
++	}
++
++	/* update ack_sn for remaining addr_elem */
++	if (i % 4 != 0)
++		mt76_wr(dev, MT_RRO_ACK_SN_CTRL,
++			FIELD_PREP(MT_RRO_ACK_SN_CTRL_SESSION_MASK, cmd->se_id) |
++			FIELD_PREP(MT_RRO_ACK_SN_CTRL_SN_MASK, sn));
++}
++
+ void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+@@ -2040,6 +2421,9 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 	dev_info(dev->mt76.dev,"%s L1 SER dma start done.",
+ 		 wiphy_name(dev->mt76.hw->wiphy));
+ 
++	if (is_mt7992(&dev->mt76) && dev->has_rro)
++		mt76_wr(dev, MT_RRO_3_0_EMU_CONF, MT_RRO_3_0_EMU_CONF_EN_MASK);
++
+ 	if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
+ 		u32 wed_irq_mask = MT_INT_RRO_RX_DONE | MT_INT_TX_DONE_BAND2 |
+ 				   dev->mt76.mmio.irqmask;
+@@ -2049,10 +2433,6 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 
+ 		mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask);
+ 
+-		if (is_mt7992(&dev->mt76) && dev->has_rro)
+-			mt76_wr(dev, MT_RRO_3_0_EMU_CONF,
+-				MT_RRO_3_0_EMU_CONF_EN_MASK);
+-
+ 		mtk_wed_device_start_hw_rro(&dev->mt76.mmio.wed, wed_irq_mask,
+ 					    true);
+ 
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index 58db5204..4d9cb9ff 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -654,6 +654,8 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
+ 		.rx_skb = mt7996_queue_rx_skb,
+ 		.rx_check = mt7996_rx_check,
+ 		.rx_poll_complete = mt7996_rx_poll_complete,
++		.rx_rro_ind_process = mt7996_rro_rx_process,
++		.rx_rro_fill_msdu_pg = mt7996_rro_fill_msdu_page,
+ 		.sta_add = mt7996_mac_sta_add,
+ 		.sta_assoc = mt7996_mac_sta_assoc,
+ 		.sta_remove = mt7996_mac_sta_remove,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 65a88e58..3aedaf5a 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -102,6 +102,7 @@
+ 
+ #define MT7996_BUILD_TIME_LEN		24
+ 
++#define MT7996_MAX_HIF_RXD_IN_PG	5
+ #define MT7996_RRO_MSDU_PG_HASH_SIZE	127
+ #define MT7996_RRO_MAX_SESSION		1024
+ #define MT7996_RRO_WINDOW_MAX_LEN	1024
+@@ -532,6 +533,33 @@ int mt7996_mcu_set_muru_qos_cfg(struct mt7996_dev *dev, u16 wlan_idx, u8 dir,
+ 				u8 scs_id, u8 req_type, u8 *qos_ie, u8 qos_ie_len);
+ #endif
+ 
++struct mt7996_rro_hif {
++	u32 rx_blk_base_l;
++	u32 rx_blk_base_h: 4;
++	u32 eth_hdr_ofst : 7;
++	u32 rsv          : 1;
++	u32 ring_no      : 2;
++	u32 dst_sel      : 2;
++	u32 sdl          :14;
++	u32 ls           : 1;
++	u32 rsv2         : 1;
++	u32 pn_31_0;
++	u32 pn_47_32     :16;
++	u32 cs_status    : 4;
++	u32 cs_type      : 4;
++	u32 c            : 1;
++	u32 f            : 1;
++	u32 un           : 1;
++	u32 rsv3         : 1;
++	u32 is_fc_data   : 1;
++	u32 uc           : 1;
++	u32 mc           : 1;
++	u32 bc           : 1;
++	u16 rx_token_id;
++	u16 rsv4;
++	u32 rsv5;
++};
++
+ struct mt7996_rro_ba_session {
+ 	u32 ack_sn         :12;
+ 	u32 win_sz         :3;
+@@ -547,6 +575,26 @@ struct mt7996_rro_ba_session {
+ 	u32 last_in_rxtime :12;
+ };
+ 
++struct mt7996_rro_ba_session_elem {
++	struct list_head poll_list;
++	u16 session_id;
++};
++
++struct mt7996_msdu_pg {
++	struct mt7996_rro_hif rxd[MT7996_MAX_HIF_RXD_IN_PG];
++	u32 next_pg_l;
++	u32 next_pg_h	: 4;
++	u32 rsv		:27;
++	u32 owner	: 1;
++};
++
++struct mt7996_msdu_pg_addr {
++	struct list_head list;
++	dma_addr_t dma_addr;
++	struct mt76_queue *q;
++	void *buf;
++};
++
+ struct mt7996_chanctx {
+ 	struct cfg80211_chan_def chandef;
+ 	struct mt7996_phy *phy;
+@@ -1257,6 +1305,11 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ void mt7996_tx_token_put(struct mt7996_dev *dev);
+ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+ 			 struct sk_buff *skb, u32 *info);
++void mt7996_rx_token_put(struct mt7996_dev *dev);
++void mt7996_rro_msdu_pg_free(struct mt7996_dev *dev);
++void mt7996_rro_rx_process(struct mt76_dev *mdev, void *data);
++int mt7996_rro_fill_msdu_page(struct mt76_dev *mdev, struct mt76_queue *q,
++			      dma_addr_t p, void *data);
+ bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len);
+ void mt7996_stats_work(struct work_struct *work);
+ void mt7996_scan_work(struct work_struct *work);
+diff --git a/mt7996/pci.c b/mt7996/pci.c
+index 382b6a89..a010680f 100644
+--- a/mt7996/pci.c
++++ b/mt7996/pci.c
+@@ -13,6 +13,9 @@
+ static bool hif2_enable = false;
+ module_param(hif2_enable, bool, 0644);
+ 
++static bool rro_enable = false;
++module_param(rro_enable, bool, 0644);
++
+ static LIST_HEAD(hif_list);
+ static DEFINE_SPINLOCK(hif_lock);
+ static u32 hif_idx;
+@@ -140,6 +143,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ 	if (IS_ERR(dev))
+ 		return PTR_ERR(dev);
+ 
++	dev->has_rro = rro_enable;
+ 	mdev = &dev->mt76;
+ 	mt7996_wfsys_reset(dev);
+ 	hif2 = mt7996_pci_init_hif2(pdev);
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index e1893517..0ea055ba 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -561,6 +561,7 @@ enum offs_rev {
+ #define MT_INT_RRO_RX_DONE			(MT_INT_RX(MT_RXQ_RRO_BAND0) |		\
+ 						 MT_INT_RX(MT_RXQ_RRO_BAND1) |		\
+ 						 MT_INT_RX(MT_RXQ_RRO_BAND2) |		\
++						 MT_INT_RX(MT_RXQ_RRO_IND) |		\
+ 						 MT_INT_RX(MT_RXQ_MSDU_PAGE_BAND0) |	\
+ 						 MT_INT_RX(MT_RXQ_MSDU_PAGE_BAND1) |	\
+ 						 MT_INT_RX(MT_RXQ_MSDU_PAGE_BAND2))
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0196-mtk-mt76-mt7996-ignore-vif.dormant_links-in-mt7996_c.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0196-mtk-mt76-mt7996-ignore-vif.dormant_links-in-mt7996_c.patch
new file mode 100644
index 0000000..f6d2576
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0196-mtk-mt76-mt7996-ignore-vif.dormant_links-in-mt7996_c.patch
@@ -0,0 +1,36 @@
+From bdbcaf57e4e62a9ff8425677384e3da49497c170 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 5 Aug 2024 14:56:19 +0800
+Subject: [PATCH 196/199] mtk: mt76: mt7996: ignore vif.dormant_links in
+ mt7996_change_vif_links
+
+The dormant links are disabled for reasons like TTLM, and might become
+active in the near future, so we do not remove their bss_conf here.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ mt7996/main.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 6826f319..3892fd27 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -3102,11 +3102,12 @@ mt7996_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	unsigned long rem = old_links & ~new_links;
++	unsigned long rem = old_links & ~new_links & ~vif->dormant_links;
+ 	unsigned int link_id;
+ 	int ret = 0;
+ 
+-	mt76_vif_dbg(vif, "old=0x%x, new=0x%x\n", old_links, new_links);
++	mt76_vif_dbg(vif, "old=0x%x, new=0x%x, dormant=0x%x\n",
++		     old_links, new_links, vif->dormant_links);
+ 	if (old_links == new_links)
+ 		return 0;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0197-mtk-mt76-mt7996-add-Adv-TTLM-support-for-STA.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0197-mtk-mt76-mt7996-add-Adv-TTLM-support-for-STA.patch
new file mode 100644
index 0000000..04f64a9
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0197-mtk-mt76-mt7996-add-Adv-TTLM-support-for-STA.patch
@@ -0,0 +1,169 @@
+From aea601b0b41e150228aa152da2930778f0e10942 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 5 Aug 2024 15:03:55 +0800
+Subject: [PATCH 197/199] mtk: mt76: mt7996: add Adv-TTLM support for STA
+
+1. add the handling for valid_link and TTLM changing in vif_cfg_changed
+   callback.
+2. send peer-mld request for default mapping and Adv-TTLM. Neg-TTLM will
+   be supported in further commit.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ mt76_connac_mcu.h |  1 +
+ mt7996/main.c     |  3 +++
+ mt7996/mcu.c      | 64 +++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h      | 22 ++++++++++++++++
+ mt7996/mt7996.h   |  2 ++
+ 5 files changed, 92 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index eb738f4f..97d4c2ad 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1322,6 +1322,7 @@ enum {
+ 	MCU_UNI_CMD_ASSERT_DUMP = 0x6f,
+ 	MCU_UNI_CMD_PTA_3WIRE_CTRL = 0x78,
+ 	MCU_UNI_CMD_MLD = 0x82,
++	MCU_UNI_CMD_PEER_MLD = 0x83,
+ };
+ 
+ enum {
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 3892fd27..7be2a38a 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -957,6 +957,9 @@ static void mt7996_vif_cfg_changed(struct ieee80211_hw *hw,
+ 		}
+ 	}
+ 
++	if (changed & BSS_CHANGED_MLD_VALID_LINKS)
++		mt7996_mcu_peer_mld_ttlm_req(dev, vif, changed);
++
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 03c671f6..7096b8a0 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3616,6 +3616,70 @@ int mt7996_mcu_mld_set_attlm(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 	return ret;
+ }
+ 
++int mt7996_mcu_peer_mld_ttlm_req(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++				 u64 changed)
++{
++	struct mt7996_link_sta *mlink;
++	struct ieee80211_sta *sta;
++	struct mt7996_sta *msta;
++	struct peer_mld_req_hdr hdr = { .mld_idx = 0xff };
++	struct peer_mld_ttlm_req *req;
++	struct sk_buff *skb;
++	struct tlv *tlv;
++	int len = sizeof(hdr) + sizeof(*req);
++	unsigned long valid_links = (unsigned long)vif->valid_links;
++	u8 link_id;
++
++	if (vif->type != NL80211_IFTYPE_STATION)
++		return 0;
++
++	rcu_read_lock();
++	sta = ieee80211_find_sta(vif, vif->cfg.ap_addr);
++	if (!sta) {
++		rcu_read_unlock();
++		return -EINVAL;
++	}
++
++	memcpy(hdr.peer_mld_addr, sta->addr, ETH_ALEN);
++	msta = (struct mt7996_sta *)sta->drv_priv;
++
++	skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len);
++	if (!skb)
++		return -ENOMEM;
++
++	skb_put_data(skb, &hdr, sizeof(hdr));
++	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_CMD_PEER_MLD_TTLM_REQ,
++				     sizeof(*req));
++	req = (struct peer_mld_ttlm_req *)tlv;
++
++	memcpy(req->mld_addr, vif->addr, ETH_ALEN);
++	req->enabled_link_bitmap = cpu_to_le16(vif->valid_links);
++	for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++		mlink = mlink_dereference_protected(msta, link_id);
++		if (!mlink)
++			continue;
++
++		req->link_to_wcid[link_id] = cpu_to_le16(mlink->wcid.idx);
++
++		if (changed & BSS_CHANGED_MLD_ADV_TTLM) {
++			/* skip TTLM-disabled links */
++			if (vif->adv_ttlm.active &&
++			    !(vif->adv_ttlm.map & BIT(link_id)))
++				continue;
++
++			req->dl_tid_map[link_id] = 0xff;
++			req->ul_tid_map[link_id] = 0xff;
++		}
++
++		/* TODO apply negotiated TTLM */
++	}
++
++	rcu_read_unlock();
++
++	return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(PEER_MLD),
++				     true);
++}
++
+ static void
+ mt7996_mcu_beacon_cntdwn(struct ieee80211_bss_conf *conf, struct sk_buff *rskb,
+ 			 struct sk_buff *skb,
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index a5818f95..dab4700e 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -1244,6 +1244,28 @@ enum {
+ 	UNI_EVENT_MLD_RECONF_AP_REM_TIMER = 0x04,
+ };
+ 
++struct peer_mld_req_hdr {
++	u8 ver;
++	u8 peer_mld_addr[ETH_ALEN];
++	u8 mld_idx;
++	u8 rsv[4];
++	u8 buf[];
++} __packed;
++
++struct peer_mld_ttlm_req {
++	__le16 tag;
++	__le16 len;
++	u8 mld_addr[ETH_ALEN];
++	__le16 enabled_link_bitmap;
++	__le16 link_to_wcid[IEEE80211_MLD_MAX_NUM_LINKS + 1];
++	u8 dl_tid_map[IEEE80211_MLD_MAX_NUM_LINKS + 1];
++	u8 ul_tid_map[IEEE80211_MLD_MAX_NUM_LINKS + 1];
++} __packed;
++
++enum {
++	UNI_CMD_PEER_MLD_TTLM_REQ = 0x0,
++};
++
+ struct tx_power_ctrl {
+ 	u8 _rsv[4];
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 3aedaf5a..7866c706 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1211,6 +1211,8 @@ int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy);
+ void mt7996_mcu_wmm_pbc_work(struct work_struct *work);
+ int mt7996_mcu_mld_set_attlm(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 			     u16 disabled_links, u16 switch_time, u32 duration);
++int mt7996_mcu_peer_mld_ttlm_req(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++				 u64 changed);
+ 
+ static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
+ {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0198-mtk-mt76-mt7996-Add-AFC-and-lpi-power-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0198-mtk-mt76-mt7996-Add-AFC-and-lpi-power-support.patch
new file mode 100644
index 0000000..347774e
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0198-mtk-mt76-mt7996-Add-AFC-and-lpi-power-support.patch
@@ -0,0 +1,722 @@
+From bdbfbe8865914946cbce6c901224785cf5cd7d3c Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Thu, 1 Aug 2024 11:25:03 +0800
+Subject: [PATCH 198/199] mtk: mt76: mt7996: Add AFC and lpi power support
+
+This patch receiving and storing the power table from hostapd vendor cmd.
+The power table would be use to compare the sku table in standard power
+mode every time when setting power and mt76 would set the minimum values
+between afc and sku table to fw.
+The AFC table format is like below:
+col\row  bw20  bw40  ...  ru26  ...  ru3472
+chan 1
+chan 6
+...
+chan 233
+To compare the afc and sku table, mt76 will find the power list of current
+channel in AFC table. And mtk sku table design serveral rates for each
+bandwidth, so a power value of afc table could compare with serveral
+rates in mtk sku table.
+
+ - Add a new vendor attibute lpi mode, due to the lpi is tuntime decided by AFC
+not like wifi6 switch lpi and standard power should reload interface.
+
+Add dump afc table and information.
+Add the bf on value offset logic. The offset is antenna/beamform gain
+offset = 10 * (log(num of ant) - log(num of NSS)).
+
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ mt76.h               |   2 +
+ mt7996/mac.c         |   2 +-
+ mt7996/main.c        |   2 +-
+ mt7996/mcu.c         | 186 ++++++++++++++++++++++++++++++++++++++++---
+ mt7996/mcu.h         | 139 ++++++++++++++++++++++++++++++++
+ mt7996/mt7996.h      |   2 +
+ mt7996/mtk_debugfs.c |  37 +++++++++
+ mt7996/vendor.c      |  81 +++++++++++++++----
+ mt7996/vendor.h      |   3 +
+ 9 files changed, 429 insertions(+), 25 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index 58e9b0b7..4b473123 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -1063,6 +1063,8 @@ struct mt76_dev {
+ 	bool lpi_psd;
+ 	bool lpi_bcn_enhance;
+ 	bool mgmt_pwr_enhance;
++	bool lpi_mode;
++	s8 **afc_power_table;
+ 
+ #ifdef CONFIG_NL80211_TESTMODE
+ 	const struct mt76_testmode_ops *test_ops;
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index c0f282d1..60223bea 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -896,7 +896,7 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ 		if (mcast)
+ 			val |= MT_TXD6_DIS_MAT;
+ 		if (dev->mt76.phys[band_idx]->cap.has_6ghz &&
+-		    dev->mt76.lpi_bcn_enhance &&
++		    dev->mt76.lpi_mode && dev->mt76.lpi_bcn_enhance &&
+ 		    ieee80211_is_mgmt(hdr->frame_control))
+ 			val |= FIELD_PREP(MT_TXD6_BW, FW_CDBW_80MHZ);
+ 		txwi[6] |= cpu_to_le32(val);
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 7be2a38a..58e02083 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -891,7 +891,7 @@ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
+ 
+ 		if (dev->cert_mode && phy->mt76->band_idx == MT_BAND2)
+ 			rate = 0x0200;
+-		else if (mphy->dev->lpi_bcn_enhance)
++		else if (mphy->dev->lpi_mode && mphy->dev->lpi_bcn_enhance)
+ 			rate = FR_RATE_IDX_OFDM_6M;
+ 
+ 		/* odd index for driver, even index for firmware */
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 7096b8a0..48bdbf73 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -6374,7 +6374,8 @@ int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx,
+ 
+ 	if (beacon) {
+ 		req.spe_idx_sel = SPE_IXD_SELECT_TXD;
+-		req.spe_idx = dev->mt76.mgmt_pwr_enhance ? 0 : 24 + band_idx;
++		req.spe_idx = dev->mt76.lpi_mode && dev->mt76.mgmt_pwr_enhance ?
++			0 : 24 + band_idx;
+ 		phy->beacon_rate = rate_idx;
+ 	} else {
+ 		req.spe_idx_sel = SPE_IXD_SELECT_BMC_WTBL;
+@@ -6657,6 +6658,126 @@ mt7996_update_max_txpower_cur(struct mt7996_phy *phy, int tx_power)
+ 		mphy->txpower_cur = e2p_power_limit;
+ }
+ 
++static int mt7996_afc_update_power_limit(struct mt7996_dev *dev,
++					 struct ieee80211_channel *chan,
++					 struct mt76_power_limits *la,
++					 struct mt76_power_path_limits *la_path,
++					 int *tx_power,
++					 struct cfg80211_chan_def *chandef)
++{
++	s8 *power_list, bw320_offset, target_power;
++	int table_idx, i, bw, mcs, ru, eht, path;
++	s8 bf_offset_ofdm[] = {6, 10, 12, 14};
++	s8 bf_offset[] = {0, 6, 10, 12, 14, 0, 4, 6, 8, 0, 3, 5, 0, 2, 0};
++
++	table_idx = chan->hw_value / 4;
++	if (table_idx < 0 || table_idx > MAX_CHANNEL_NUM_6G ||
++	    !dev->mt76.afc_power_table[table_idx])
++		return -EINVAL;
++
++	power_list = dev->mt76.afc_power_table[table_idx];
++
++	switch (chan->center_freq) {
++	case 31:
++	case 95:
++	case 159:
++		bw320_offset = 1;
++		break;
++	case 63:
++	case 127:
++	case 191:
++		bw320_offset = 2;
++		break;
++	default:
++		bw320_offset = 0;
++		break;
++	}
++
++	if (chandef) {
++		switch (chandef->width) {
++		case NL80211_CHAN_WIDTH_20:
++			target_power = power_list[afc_power_bw20];
++			break;
++		case NL80211_CHAN_WIDTH_40:
++			target_power = power_list[afc_power_bw40];
++			break;
++		case NL80211_CHAN_WIDTH_80:
++			target_power = power_list[afc_power_bw80];
++			break;
++		case NL80211_CHAN_WIDTH_160:
++			target_power = power_list[afc_power_bw160];
++			break;
++		case NL80211_CHAN_WIDTH_320:
++			if (bw320_offset == 1)
++				target_power = power_list[afc_power_bw320_1];
++			else
++				target_power = power_list[afc_power_bw320_2];
++			break;
++		default:
++			break;
++		}
++		*tx_power = min_t(int, *tx_power, target_power);
++	}
++
++	target_power = min_t(s8, (s8)*tx_power, power_list[afc_power_bw20]);
++	for (i = 0; i < sizeof(la->cck); i++)
++		la->cck[i] = min_t(s8, la->cck[i], power_list[afc_power_bw20]);
++	for (i = 0; i < sizeof(la->ofdm); i++)
++		la->ofdm[i] = min_t(s8, la->ofdm[i], target_power);
++
++	for (i = 0; i < sizeof(la_path->cck); i++)
++		la_path->cck[i] = min_t(s8, la_path->cck[i], power_list[afc_power_bw20]);
++	for (i = 0; i < sizeof(la_path->ofdm); i++)
++		la_path->ofdm[i] = min_t(s8, la_path->ofdm[i], target_power);
++	for (i = 0; i < sizeof(la_path->ofdm_bf); i++) {
++		la_path->ofdm_bf[i] =
++			min_t(s8, la_path->ofdm_bf[i],
++			      target_power - bf_offset_ofdm[i]);
++	}
++
++	for (bw = afc_power_bw20; bw < afc_power_table_num; bw++) {
++		if ((bw == afc_power_bw320_1 && bw320_offset == 2) ||
++		    (bw == afc_power_bw320_2 && bw320_offset == 1))
++			continue;
++
++		if (power_list[bw] == AFC_INVALID_POWER)
++			continue;
++
++		/* Negative index means doesn't need to update powers of the type. */
++		if (mt7996_get_bw_power_table_idx(bw, &mcs, &ru, &eht, &path))
++			return -EINVAL;
++
++		if (mcs >= 0) {
++			for (i = 0; i < sizeof(la->mcs[0]); i++)
++				la->mcs[mcs][i] =
++					min_t(s8, la->mcs[mcs][i], power_list[bw]);
++		}
++
++		if (ru >= 0) {
++			for (i = 0; i < sizeof(la->ru[0]); i++)
++				la->ru[ru][i] = min_t(s8, la->ru[ru][i], power_list[bw]);
++		}
++
++		if (eht >= 0) {
++			for (i = 0; i < sizeof(la->eht[0]); i++)
++				la->eht[eht][i] =
++					min_t(s8, la->eht[eht][i], power_list[bw]);
++		}
++
++		if (path >= 0) {
++			for (i = 0; i < sizeof(la_path->ru[0]); i++) {
++				la_path->ru[path][i] =
++					min_t(s8, la_path->ru[path][i], power_list[bw]);
++				la_path->ru_bf[path][i] =
++					min_t(s8, la_path->ru_bf[path][i],
++					      power_list[bw] - bf_offset[i]);
++			}
++		}
++	}
++
++	return 0;
++}
++
+ bool mt7996_is_psd_country(char *country)
+ {
+ 	char psd_country_list[][3] = {"US", "KR", "BR", "CL", "MY", ""};
+@@ -6704,15 +6825,22 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
+ 		txpower_setting = 127;
+ 	txpower_limit = mt7996_get_power_bound(phy, txpower_setting);
+ 
+-	if (phy->sku_limit_en) {
+-		txpower_limit = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
+-							   &la, &la_path, txpower_limit);
+-		mt7996_update_max_txpower_cur(phy, txpower_limit);
+-	} else {
++	if (!phy->sku_limit_en) {
+ 		mt7996_update_max_txpower_cur(phy, txpower_limit);
+ 		return 0;
+ 	}
+ 
++	txpower_limit = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
++						   &la, &la_path, txpower_limit);
++	if(phy->mt76->cap.has_6ghz && dev->mt76.afc_power_table) {
++		ret = mt7996_afc_update_power_limit(dev, mphy->main_chan, &la, &la_path,
++						    &txpower_limit, &mphy->chandef);
++		if (ret)
++			return ret;
++	}
++
++	mt7996_update_max_txpower_cur(phy, txpower_limit);
++
+ 	skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
+ 				 sizeof(req) + MT7996_SKU_PATH_NUM);
+ 	if (!skb)
+@@ -6725,7 +6853,7 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
+ 	/* FW would compensate for PSD countries
+ 	 * driver doesn't need to do it
+ 	 */
+-	if (phy->mt76->cap.has_6ghz && mphy->dev->lpi_psd &&
++	if (phy->mt76->cap.has_6ghz && mphy->dev->lpi_mode && mphy->dev->lpi_psd &&
+ 	    !mt7996_is_psd_country(dev->mt76.alpha2)) {
+ 		switch (mphy->chandef.width) {
+ 		case NL80211_CHAN_WIDTH_20:
+@@ -6791,7 +6919,7 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
+ 	/* FW would NOT compensate in the case of BF backoff table
+ 	 * driver needs to compensate for LPI PSD
+ 	 */
+-	if (phy->mt76->cap.has_6ghz && mphy->dev->lpi_psd) {
++	if (phy->mt76->cap.has_6ghz && mphy->dev->lpi_mode && mphy->dev->lpi_psd) {
+ 		switch (mphy->chandef.width) {
+ 		case NL80211_CHAN_WIDTH_20:
+ 			skb_put_data(skb, &la_path.ru[5], sizeof(la_path.ofdm));
+@@ -6853,13 +6981,53 @@ int mt7996_mcu_set_lpi_psd(struct mt7996_phy *phy, u8 enable)
+ 		.tag = cpu_to_le16(UNI_BAND_CONFIG_LPI_CTRL),
+ 		.len = cpu_to_le16(sizeof(req) - 4),
+ 		.lpi_enable = enable,
+-		.psd_limit = enable ? mt7996_is_psd_country(dev->mt76.alpha2) : 0,
++		.psd_limit = enable && dev->mt76.lpi_mode ?
++				mt7996_is_psd_country(dev->mt76.alpha2) : 0,
+ 	};
+ 
+ 	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
+ 				 &req, sizeof(req), false);
+ }
+ 
++int mt7996_alloc_afc_table(struct mt7996_dev *dev)
++{
++	struct mt76_dev *mdev = &dev->mt76;
++	int i;
++
++	mdev->afc_power_table =
++		(s8**)devm_kzalloc(dev->mt76.dev,
++				   MAX_CHANNEL_NUM_6G * sizeof(s8*),
++				   GFP_KERNEL);
++
++	if (!mdev->afc_power_table)
++		return -ENOMEM;
++
++	for (i = 0; i < MAX_CHANNEL_NUM_6G; i++) {
++		mdev->afc_power_table[i] =
++			(s8*)devm_kzalloc(dev->mt76.dev,
++					  afc_power_table_num * sizeof(s8),
++					  GFP_KERNEL);
++		if (!mdev->afc_power_table[i]) {
++			mt7996_free_afc_table(dev);
++			return -ENOMEM;
++		}
++	}
++	return 0;
++}
++
++void mt7996_free_afc_table(struct mt7996_dev *dev)
++{
++	struct mt76_dev *mdev = &dev->mt76;
++	int i;
++
++	if (mdev->afc_power_table) {
++		for (i = 0; i < MAX_CHANNEL_NUM_6G; i++)
++			devm_kfree(mdev->dev, mdev->afc_power_table[i]);
++		devm_kfree(mdev->dev, mdev->afc_power_table);
++	}
++	mdev->afc_power_table = NULL;
++}
++
+ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode)
+ {
+ 	__le32 cp_mode;
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index dab4700e..33ba3774 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -1295,6 +1295,145 @@ enum {
+ 	UNI_TXPOWER_SHOW_INFO = 7,
+ };
+ 
++#define MAX_CHANNEL_NUM_6G 59
++#define AFC_INVALID_POWER 127
++enum afc_table_info {
++	afc_power_bw20,
++	afc_power_bw40,
++	afc_power_bw80,
++	afc_power_bw160,
++	afc_power_bw320_1,
++	afc_power_bw320_2,
++	afc_power_ru26,
++	afc_power_ru52,
++	afc_power_ru78,
++	afc_power_ru106,
++	afc_power_ru132,
++	afc_power_ru726,
++	afc_power_ru1480,
++	afc_power_ru1772,
++	afc_power_ru2476,
++	afc_power_ru2988,
++	afc_power_ru3472,
++	afc_power_table_num,
++};
++
++static inline int mt7996_get_bw_power_table_idx(int bw, int *mcs, int *ru, int *eht,
++						int *path)
++{
++	switch (bw) {
++	case afc_power_bw20:
++		*mcs = 0;
++		*ru = 3;
++		*eht = 3;
++		*path = 5;
++		break;
++	case afc_power_bw40:
++		*mcs = 1;
++		*ru = 4;
++		*eht = 4;
++		*path = 6;
++		break;
++	case afc_power_bw80:
++		*mcs = 2;
++		*ru = 5;
++		*eht = 5;
++		*path = 8;
++		break;
++	case afc_power_bw160:
++		*mcs = 3;
++		*ru = 6;
++		*eht = 6;
++		*path = 11;
++		break;
++	case afc_power_bw320_1:
++		*mcs = -1;
++		*ru = -1;
++		*eht = 7;
++		*path = 15;
++		break;
++	case afc_power_bw320_2:
++		*mcs = -1;
++		*ru = -1;
++		*eht = 7;
++		*path = 15;
++		break;
++	case afc_power_ru26:
++		*mcs = -1;
++		*ru = 0;
++		*eht = 0;
++		*path = 0;
++		break;
++	case afc_power_ru52:
++		*mcs = -1;
++		*ru = 1;
++		*eht = 1;
++		*path = 1;
++		break;
++	case afc_power_ru78:
++		*mcs = -1;
++		*ru = -1;
++		*eht = 8;
++		*path = 2;
++		break;
++	case afc_power_ru106:
++		*mcs = -1;
++		*ru = 2;
++		*eht = 2;
++		*path = 3;
++		break;
++	case afc_power_ru132:
++		*mcs = -1;
++		*ru = -1;
++		*eht = 9;
++		*path = 4;
++		break;
++	case afc_power_ru726:
++		*mcs = -1;
++		*ru = -1;
++		*eht = 10;
++		*path = 7;
++		break;
++	case afc_power_ru1480:
++		*mcs = -1;
++		*ru = -1;
++		*eht = 11;
++		*path = 9;
++		break;
++	case afc_power_ru1772:
++		*mcs = -1;
++		*ru = -1;
++		*eht = 12;
++		*path = 10;
++		break;
++	case afc_power_ru2476:
++		*mcs = -1;
++		*ru = -1;
++		*eht = 13;
++		*path = 12;
++		break;
++	case afc_power_ru2988:
++		*mcs = -1;
++		*ru = -1;
++		*eht = 14;
++		*path = 13;
++		break;
++	case afc_power_ru3472:
++		*mcs = -1;
++		*ru = -1;
++		*eht = 15;
++		*path = 14;
++		break;
++	default:
++		*mcs = -1;
++		*ru = -1;
++		*eht = -1;
++		*path = -1;
++		return -EINVAL;
++	}
++	return 0;
++}
++
+ enum {
+ 	UNI_CMD_ACCESS_REG_BASIC = 0x0,
+ 	UNI_CMD_ACCESS_RF_REG_BASIC,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 7866c706..e8571e9f 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1170,6 +1170,8 @@ int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state);
+ int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable);
+ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
+ 			       int txpower_setting);
++int mt7996_alloc_afc_table(struct mt7996_dev *dev);
++void mt7996_free_afc_table(struct mt7996_dev *dev);
+ int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
+ 		       u8 rx_sel, u8 val);
+ int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev,
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index a4703b07..63e63a33 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2523,6 +2523,11 @@ mt7996_get_txpower_info(struct file *file, char __user *user_buf,
+ 	len += scnprintf(buf + len, size - len,
+ 			 "    RegDB:  %s\n",
+ 			 !np ? "enable" : "disable");
++	len += scnprintf(buf + len, size - len,
++			 "    sku_index:  %d\n", phy->mt76->sku_idx);
++	len += scnprintf(buf + len, size - len,
++			 "    lpi:  %s\n",
++			 phy->mt76->dev->lpi_mode ? "enable" : "disable");
+ 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ 
+ out:
+@@ -4390,6 +4395,37 @@ static int mt7996_pp_alg_show(struct seq_file *s, void *data)
+ }
+ DEFINE_SHOW_ATTRIBUTE(mt7996_pp_alg);
+ 
++static int mt7996_afc_table_show(struct seq_file *s, void *data)
++{
++	struct mt7996_dev *dev = s->private;
++
++	char str[200] = {0}, *pos;
++	char *end = str + sizeof(str);
++	int i, j;
++
++	if (!dev->mt76.afc_power_table || !dev->mt76.afc_power_table[0]) {
++		seq_printf(s, "afc table doesn't exist.\n");
++		return 0;
++	}
++
++	seq_printf(s, "bw/ru :    20    40    80   160  320-1 320-2   26    52    78   "
++		   "106   132   726  1480  1772  2476  2988  3472\n");
++	for(i = 0; i < MAX_CHANNEL_NUM_6G; i ++) {
++		pos = str;
++		for (j = 0; j < afc_power_table_num; j ++) {
++			pos += snprintf(pos, end - pos, "%5d ",
++					dev->mt76.afc_power_table[i][j]);
++		}
++		seq_printf(s, "ch %3d: %s\n", i * 4 + 1, str);
++		memset(str, 0, sizeof(str));
++	}
++	seq_printf(s, "Unit : 0.5 dBm\n");
++	seq_printf(s, "NOTE : power of the table is translated to single path.\n");
++
++	return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(mt7996_afc_table);
++
+ void mt7996_mtk_init_band_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ 	/* agg */
+@@ -4516,6 +4552,7 @@ void mt7996_mtk_init_dev_debugfs(struct mt7996_dev *dev, struct dentry *dir)
+ 
+ 	debugfs_create_file("muru_dbg", 0200, dir, dev, &fops_muru_dbg_info);
+ 	debugfs_create_bool("mgmt_pwr_enhance", 0600, dir, &dev->mt76.mgmt_pwr_enhance);
++	debugfs_create_file("afc_table", 0200, dir, dev, &mt7996_afc_table_fops);
+ }
+ 
+ #endif
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index e13a148a..867c277d 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -162,6 +162,8 @@ txpower_ctrl_policy[NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL] = {
+ 	[MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX] = { .type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE] = { .type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE] = { .type = NLA_BINARY },
++	[MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI] = { .type = NLA_U8 },
+ };
+ 
+ struct mt7996_amnt_data {
+@@ -1454,6 +1456,34 @@ out:
+ 	return err;
+ }
+ 
++static int mt7996_parse_afc_table(struct mt7996_dev *dev, struct nlattr *tb, int delta)
++{
++	int ch, bw, err = 0;
++	struct mt76_dev *mdev = &dev->mt76;
++	s8 **table;
++
++	if (!mdev->afc_power_table)
++		err = mt7996_alloc_afc_table(dev);
++
++	if (err) {
++		mt7996_free_afc_table(dev);
++		return err;
++	}
++
++	table = nla_data(tb);
++
++	for (ch = 0; ch < MAX_CHANNEL_NUM_6G; ch++) {
++		memcpy(mdev->afc_power_table[ch], table[ch],
++			afc_power_table_num * sizeof(s8));
++		for (bw = 0; bw < afc_power_table_num; bw++)
++			if (mdev->afc_power_table[ch][bw] != AFC_INVALID_POWER)
++				mdev->afc_power_table[ch][bw] -= delta;
++	}
++
++	return 0;
++}
++
++
+ static int mt7996_vendor_txpower_ctrl(struct wiphy *wiphy,
+ 				      struct wireless_dev *wdev,
+ 				      const void *data,
+@@ -1463,12 +1493,13 @@ static int mt7996_vendor_txpower_ctrl(struct wiphy *wiphy,
+ 	struct mt7996_dev *dev;
+ 	struct mt7996_phy *phy;
+ 	struct mt76_phy *mphy;
++	struct mt76_dev *mdev;
+ 	struct ieee80211_vif *vif = wdev_to_ieee80211_vif(wdev);
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt7996_bss_conf *mconf;
+-	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL];
+-	struct mt76_power_limits la = {};
+-	struct mt76_power_path_limits la_path = {};
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL], *table;
++	struct mt76_power_limits *la;
++	struct mt76_power_path_limits *la_path;
+ 	int err, current_txpower, delta;
+ 	u8 val, link_id = 0, idx;
+ 
+@@ -1496,6 +1527,14 @@ static int mt7996_vendor_txpower_ctrl(struct wiphy *wiphy,
+ 	rcu_read_unlock();
+ 
+ 	mphy = phy->mt76;
++	mdev = mphy->dev;
++	delta = mt76_tx_power_nss_delta(hweight16(mphy->chainmask));
++
++	if (mphy->cap.has_6ghz &&
++	    tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI]) {
++		val = nla_get_u8(tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI]);
++		mphy->dev->lpi_mode = !!val;
++	}
+ 
+ 	if (mphy->cap.has_6ghz &&
+ 	    tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_PSD]) {
+@@ -1504,7 +1543,7 @@ static int mt7996_vendor_txpower_ctrl(struct wiphy *wiphy,
+ 
+ 		err = mt7996_mcu_set_lpi_psd(phy, val);
+ 		if (err)
+-			return err;
++			goto out;
+ 	}
+ 
+ 	if (tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX]) {
+@@ -1515,19 +1554,22 @@ static int mt7996_vendor_txpower_ctrl(struct wiphy *wiphy,
+ 
+ 		phy->sku_limit_en = true;
+ 		phy->sku_path_en = true;
+-		mt76_get_rate_power_limits(mphy, mphy->chandef.chan, &la, &la_path, 127);
+-		if (!la_path.ofdm[0])
++		la = kzalloc(sizeof(struct mt76_power_limits), GFP_KERNEL);
++		la_path = kzalloc(sizeof(struct mt76_power_path_limits), GFP_KERNEL);
++
++		mt76_get_rate_power_limits(mphy, mphy->chandef.chan, la, la_path, 127);
++		if (!la_path->ofdm[0])
+ 			phy->sku_path_en = false;
+ 
+ 		dev = phy->dev;
+ 		err = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
+ 						   dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
+ 		if (err)
+-			return err;
++			goto out;
+ 		err = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
+ 						   dev->dbg.sku_disable ? 0 : phy->sku_path_en);
+ 		if (err)
+-			return err;
++			goto out;
+ 	}
+ 
+ 	if (mphy->cap.has_6ghz &&
+@@ -1536,19 +1578,30 @@ static int mt7996_vendor_txpower_ctrl(struct wiphy *wiphy,
+ 		mphy->dev->lpi_bcn_enhance = val;
+ 		idx = MT7996_BEACON_RATES_TBL + 2 * phy->mt76->band_idx;
+ 
+-		err = mt7996_mcu_set_fixed_rate_table(phy, idx, FR_RATE_IDX_OFDM_6M, true);
++		err = mt7996_mcu_set_fixed_rate_table(phy, idx, FR_RATE_IDX_OFDM_6M,
++						      true);
+ 		if (err)
+-			return err;
++			goto out;
++	}
++
++	if (mphy->cap.has_6ghz) {
++		table = tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE];
++		if (table) {
++			err = mt7996_parse_afc_table(dev, table, delta);
++			if (err)
++				goto out;
++		} else
++			mt7996_free_afc_table(dev);
+ 	}
+ 
+-	delta = mt76_tx_power_nss_delta(hweight16(mphy->chainmask));
+ 	current_txpower = DIV_ROUND_UP(mphy->txpower_cur + delta, 2);
+ 
+ 	err = mt7996_mcu_set_txpower_sku(phy, current_txpower);
+-	if (err)
+-		return err;
+ 
+-	return 0;
++out:
++	kfree(la);
++	kfree(la_path);
++	return err;
+ }
+ 
+ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index 71800590..cd84a492 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -338,12 +338,15 @@ enum mtk_vendor_attr_txpower_ctrl {
+ 	MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX,
+ 	MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE,
+ 	MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID,
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE,
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL,
+ 	MTK_VENDOR_ATTR_TXPOWER_CTRL_MAX =
+ 		NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL - 1
+ };
++
+ #endif
+ 
+ #endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0199-mtk-mt76-do-not-report-ACK-when-TXS-is-lost.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0199-mtk-mt76-do-not-report-ACK-when-TXS-is-lost.patch
new file mode 100644
index 0000000..d99c18f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0199-mtk-mt76-do-not-report-ACK-when-TXS-is-lost.patch
@@ -0,0 +1,27 @@
+From 3526fd4180ee45d66f634aa3bef3d86e7ab5bc82 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 13 Aug 2024 18:55:00 +0800
+Subject: [PATCH 199/199] mtk: mt76: do not report ACK when TXS is lost
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ tx.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/tx.c b/tx.c
+index c965f0e3..7712a32e 100644
+--- a/tx.c
++++ b/tx.c
+@@ -100,7 +100,8 @@ __mt76_tx_status_skb_done(struct mt76_dev *dev, struct sk_buff *skb, u8 flags,
+ 		return;
+ 
+ 	/* Tx status can be unreliable. if it fails, mark the frame as ACKed */
+-	if (flags & MT_TX_CB_TXS_FAILED) {
++	if ((flags & MT_TX_CB_TXS_FAILED) &&
++	    (dev->drv->drv_flags & MT_DRV_SW_RX_AIRTIME)) {
+ 		info->status.rates[0].count = 0;
+ 		info->status.rates[0].idx = -1;
+ 		info->flags |= IEEE80211_TX_STAT_ACK;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/patches.inc b/recipes-wifi/linux-mt76/files/patches-3.x/patches.inc
index e9b246c..4654ec4 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/patches.inc
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/patches.inc
@@ -1,119 +1,202 @@
 #patch patches (come from openwrt/lede/target/linux/mediatek)
 SRC_URI_append = " \
     file://0001-mtk-Revert-wifi-mt76-mt7996-fill-txd-by-host-driver.patch \
-    file://0002-bp-sync-upstream-changes.patch \
-    file://0003-wifi-mt76-mt7996-use-hweight16-to-get-correct-tx_ant.patch \
-    file://0004-mtk-wifi-mt76-mt7996-fix-MBSS.patch \
-    file://0005-wifi-mt76-mt7996-fix-HE-and-EHT-phy-cap.patch \
-    file://0006-mtk-wifi-mt76-mt7996-adjust-Beamformee-SS-capability.patch \
-    file://0007-wifi-mt76-mt7992-adjust-beamform-mcu-cmd-configurati.patch \
-    file://0008-mtk-wifi-mt76-mt7996-add-preamble-puncture-support-f.patch \
-    file://0009-mtk-wifi-mt76-mt7996-add-driver-support-for-wpa3-ocv.patch \
-    file://0010-mtk-wifi-mt76-mt7996-enable-ser-query.patch \
-    file://0011-mtk-wifi-mt76-mt7996-set-key-flag-IEEE80211_KEY_FLAG.patch \
-    file://0012-mtk-wifi-mt76-mt7996-Fix-TGax-HE-4.51.1_24G-fail.patch \
-    file://0013-mtk-wifi-mt76-mt7996-add-support-for-different-varia.patch \
-    file://0014-mtk-wifi-mt76-mt7996-ACS-channel-time-too-long-on-du.patch \
-    file://0015-mtk-wifi-mt76-mt7996-Fixed-null-pointer-dereference-.patch \
-    file://0016-mtk-wifi-mt76-add-sanity-check-to-prevent-kernel-cra.patch \
-    file://0017-mtk-wifi-mt76-mt7996-add-firmware-WA-s-coredump.patch \
-    file://0018-mtk-wifi-mt76-mt7996-for-build-pass.patch \
-    file://0019-mtk-wifi-mt76-mt7996-add-debug-tool.patch \
-    file://0020-mtk-wifi-mt76-mt7996-add-check-for-hostapd-config-he.patch \
-    file://0021-mtk-wifi-mt76-testmode-add-basic-testmode-support.patch \
-    file://0022-mtk-wifi-mt76-testmode-add-testmode-pre-calibration-.patch \
-    file://0023-mtk-wifi-mt76-mt7996-add-normal-mode-pre-calibration.patch \
-    file://0024-mtk-wifi-mt76-mt7996-enable-SCS-feature-for-mt7996-d.patch \
-    file://0025-mtk-wifi-mt76-mt7996-add-txpower-support.patch \
-    file://0026-mtk-wifi-mt76-mt7996-add-binfile-mode-support.patch \
-    file://0027-mtk-wifi-mt76-testmode-add-testmode-ZWDFS-verificati.patch \
-    file://0028-mtk-wifi-mt76-mt7996-support-eagle-ZWDFS-on-iFEM.patch \
-    file://0029-mtk-wifi-mt76-mt7996-refactor-eeprom-loading-flow-fo.patch \
-    file://0030-mtk-wifi-mt76-mt7996-add-vendor-commands-support.patch \
-    file://0031-mtk-wifi-mt76-mt7996-add-debugfs-for-fw-coredump.patch \
-    file://0032-mtk-wifi-mt76-mt7996-Add-mt7992-coredump-support.patch \
-    file://0033-mtk-wifi-mt76-mt7996-add-support-for-runtime-set-in-.patch \
-    file://0034-mtk-wifi-mt76-mt7996-add-support-spatial-reuse-debug.patch \
-    file://0035-mtk-wifi-mt76-mt7996-Establish-BA-in-VO-queue.patch \
-    file://0036-mtk-wifi-mt76-mt7996-report-tx-and-rx-byte-to-tpt_le.patch \
-    file://0037-mtk-wifi-mt76-mt7996-support-dup-wtbl.patch \
-    file://0038-mtk-wifi-mt76-try-more-times-when-send-message-timeo.patch \
-    file://0039-mtk-wifi-mt76-mt7996-add-SER-overlap-handle.patch \
-    file://0040-mtk-wifi-mt76-mt7996-kite-default-1-pcie-setting.patch \
-    file://0041-mtk-wifi-mt76-mt7996-add-debugfs-knob-for-rx_counter.patch \
-    file://0042-mtk-wifi-mt76-mt7996-support-BF-MIMO-debug-commands.patch \
-    file://0043-mtk-wifi-mt76-mt7996-add-build-the-following-MURU-mc.patch \
-    file://0044-mtk-wifi-mt76-mt7996-add-cert-patch.patch \
-    file://0045-mtk-wifi-mt76-testmode-add-testmode-bf-support.patch \
-    file://0046-mtk-wifi-mt76-mt7996-add-zwdfs-cert-mode.patch \
-    file://0047-mtk-wifi-mt76-testmode-add-channel-68-96.patch \
-    file://0048-mtk-wifi-mt76-testmode-add-kite-testmode-support.patch \
-    file://0049-mtk-wifi-mt76-mt7996-assign-DEAUTH-to-ALTX-queue-for.patch \
-    file://0050-mtk-wifi-mt76-mt7996-add-no_beacon-vendor-command-fo.patch \
-    file://0051-mtk-wifi-mt76-mt7996-add-adie-efuse-merge-support.patch \
-    file://0052-mtk-wifi-mt7996-add-Eagle-2adie-TBTC-BE14000-support.patch \
-    file://0053-mtk-wifi-mt76-mt7996-add-background-radar-hw-cap-che.patch \
-    file://0054-mtk-wifi-mt76-mt7996-add-fallback-in-case-of-missing.patch \
-    file://0055-mtk-wifi-mt76-mt7996-add-kite-part-number-support.patch \
-    file://0056-mtk-wifi-mt76-revert-page_poll-for-kernel-5.4.patch \
-    file://0057-mtk-wifi-mt76-rework-wed-rx-flow.patch \
-    file://0058-mtk-wifi-mt76-wed-change-wed-token-init-size-to-adap.patch \
-    file://0059-mtk-wifi-mt76-add-random-early-drop-support.patch \
-    file://0060-mtk-wifi-mt76-mt7996-reset-addr_elem-when-delete-ba.patch \
-    file://0061-mtk-wifi-mt76-wed-change-pcie0-R5-to-pcie1-to-get-6G.patch \
-    file://0062-mtk-wifi-mt76-add-SER-support-for-wed3.0.patch \
-    file://0063-mtk-wifi-mt76-mt7915-wed-find-rx-token-by-physical-a.patch \
-    file://0064-mtk-wifi-mt76-mt7996-add-dma-mask-limitation.patch \
-    file://0065-mtk-wifi-mt76-mt7996-add-per-bss-statistic-info.patch \
-    file://0066-mtk-wifi-mt76-mt7996-do-not-report-netdev-stats-on-m.patch \
-    file://0067-mtk-wifi-mt76-mt7996-add-support-for-HW-ATF.patch \
-    file://0068-mtk-wifi-mt76-mt7996-wed-add-SER0.5-support-w-wed3.0.patch \
-    file://0069-mtk-wifi-mt76-mt7996-support-backaward-compatiable.patch \
-    file://0070-mtk-wifi-mt76-mt7996-wed-add-wed-support-for-mt7992.patch \
-    file://0071-mtk-wifi-mt76-mt7992-wed-add-2pcie-one-wed-support.patch \
-    file://0072-mtk-wifi-mt76-mt7996-Remove-wed-rro-ring-add-napi-at.patch \
-    file://0073-mtk-wifi-mt76-mt7996-Remove-wed_stop-during-L1-SER.patch \
-    file://0074-mtk-wifi-mt76-mt7996-Refactor-rro-del-ba-command-for.patch \
-    file://0075-mtk-wifi-mt76-mt7996-get-airtime-and-RSSI-via-MCU-co.patch \
-    file://0076-mtk-wifi-mt76-mt7996-add-support-for-WMM-PBC-configu.patch \
-    file://0077-mtk-wifi-mt76-mt7996-eagle-support-extra-option_type.patch \
-    file://0078-mtk-wifi-mt76-mt7996-support-enable-disable-thermal-.patch \
-    file://0079-mtk-wifi-mt76-mt7996-support-thermal-recal-debug-com.patch \
-    file://0080-mtk-wifi-mt76-mt7996-add-kite-two-pcie-with-two-wed-.patch \
-    file://0081-mtk-wifi-mt76-mt7992-add-support-to-enable-index-FW-.patch \
-    file://0082-wifi-mt76-mt7996-implement-and-switch-to-hw-scan-cal.patch \
-    file://0083-wifi-mt76-mt7996-implement-and-switch-to-chanctx-cal.patch \
-    file://0084-wifi-mt76-mt7996-use-.sta_state-to-replace-.sta_add-.patch \
-    file://0085-wifi-mt76-mt7996-switch-to-per-link-data-structure-o.patch \
-    file://0086-wifi-mt76-mt7996-switch-to-per-link-data-structure-o.patch \
-    file://0087-wifi-mt76-extend-wcid-and-sta-flow-for-MLO-support.patch \
-    file://0088-wifi-mt76-mt7996-enable-MLO-capability.patch \
-    file://0089-wifi-mt76-mt7996-support-multi-link-vif-links-and-ML.patch \
-    file://0090-wifi-mt76-mt7996-support-multi-link-sta-links-and-ML.patch \
-    file://0091-wifi-mt76-mt7996-introduce-mt7996_band_phy-for-ch-ba.patch \
-    file://0092-wifi-mt76-mt7996-rework-ieee80211_ops-callbacks-for-.patch \
-    file://0093-wifi-mt76-mt7996-rework-TXD-for-multi-link-support.patch \
-    file://0094-wifi-mt76-mt7996-rework-TXS-for-multi-link-support.patch \
-    file://0095-wifi-mt76-mt7996-rework-RXD-for-multi-link-support.patch \
-    file://0096-wifi-mt76-mt7996-rework-mac-functions-for-multi-link.patch \
-    file://0097-wifi-mt76-connac-rework-mcu-functions-for-multi-link.patch \
-    file://0098-wifi-mt76-connac-rework-connac-helpers.patch \
-    file://0099-wifi-mt76-mt7996-handle-mapping-for-hw-and-phy.patch \
-    file://0100-wifi-mt76-mt7996-handle-mapping-for-hw-and-vif.patch \
-    file://0101-wifi-mt76-mt7996-rework-scanning-parts-for-MLD-STA-s.patch \
-    file://0102-wifi-mt76-mt7996-implement-mld-address-translation.patch \
-    file://0103-wifi-mt76-mt7996-use-BSS_CHANGED_TXPOWER-for-txpower.patch \
-    file://0104-wifi-mt76-mt7996-temp-support-for-single-wiphy.patch \
-    file://0105-wifi-mt76-mt7996-implement-ieee80211_ops-for-link-de.patch \
-    file://0106-mtk-wifi-mt76-mt7996-support-multi-link-channel-swit.patch \
-    file://0107-mtk-mt76-mt7996-hw_scan-ACS-channel-time-too-long-on.patch \
-    file://0108-wifi-mt76-mt7996-add-beacon-monitoring-in-driver-for.patch \
-    file://0109-mtk-wifi-mt76-mt7996-support-band_idx-option-for-set.patch \
-    file://0110-mtk-wifi-mt76-mt7996-tmp-disable-VOW.patch \
-    file://0111-mtk-wifi-mt76-mt7996-enable-ampdu-limit-to-avoid-BA-.patch \
-    file://0112-wifi-mt76-mt7996-Fix-get_txpower-wrong-result-in-sin.patch \
-    file://0113-mtk-wifi-mt76-mt7996-add-beacon_int_min_gcd-to-suppo.patch \
-    file://0114-mtk-wifi-mt76-mt7996-Add-connac3-csi-feature.patch \
-    file://0115-mtk-wifi-mt76-mt7996-add-more-debug-info-for-MLO.patch \
-    file://0116-mtk-wifi-mt76-mt7996-remain-multiple-wiphy-model-for.patch \
+    file://0002-mtk-mt76-mt7996-use-hweight16-to-get-correct-tx_ant.patch \
+    file://0003-mtk-mt76-mt7996-fix-MBSS.patch \
+    file://0004-mtk-mt76-mt7996-fix-HE-and-EHT-phy-cap.patch \
+    file://0005-mtk-mt76-mt7996-adjust-Beamformee-SS-capability.patch \
+    file://0006-mtk-mt76-mt7996-add-support-for-IEEE-802.11-fragment.patch \
+    file://0007-mtk-mt76-mt7996-set-rx-path-when-channel-switch.patch \
+    file://0008-mtk-mt76-mt7996-set-station-s-wmm-index-to-3.patch \
+    file://0009-mtk-mt76-mt7996-fix-rxd-checksum-offload-offset.patch \
+    file://0010-mtk-mt76-mt7996-fix-EHT-Beamforming-capability-check.patch \
+    file://0011-mtk-mt76-mt7996-fix-amsdu-information.patch \
+    file://0012-mtk-mt76-mt7996-add-beacon_int_min_gcd-to-support-di.patch \
+    file://0013-mtk-mt76-adjust-beamform-mcu-cmd-configuration-for-m.patch \
+    file://0014-mtk-mt76-mt7996-add-preamble-puncture-support-for-mt.patch \
+    file://0015-mtk-mt76-mt7996-add-driver-support-for-wpa3-ocv-and-.patch \
+    file://0016-mtk-mt76-mt7996-enable-ser-query.patch \
+    file://0017-mtk-mt76-mt7996-set-key-flag-IEEE80211_KEY_FLAG_GENE.patch \
+    file://0018-mtk-mt76-mt7996-Fix-TGax-HE-4.51.1_24G-fail.patch \
+    file://0019-mtk-mt76-mt7996-add-support-for-different-variants.patch \
+    file://0020-mtk-mt76-mt7996-ACS-channel-time-too-long-on-duty-ch.patch \
+    file://0021-mtk-mt76-mt7996-Fixed-null-pointer-dereference-issue.patch \
+    file://0022-mtk-mt76-add-sanity-check-to-prevent-kernel-crash.patch \
+    file://0023-mtk-mt76-mt7996-add-firmware-WA-s-coredump.patch \
+    file://0024-mtk-mt76-mt7996-for-build-pass.patch \
+    file://0025-mtk-mt76-mt7996-add-debug-tool.patch \
+    file://0026-mtk-mt76-mt7996-add-check-for-hostapd-config-he_ldpc.patch \
+    file://0027-mtk-mt76-mt7996-add-basic-testmode-support.patch \
+    file://0028-mtk-mt76-mt7996-add-testmode-pre-calibration-support.patch \
+    file://0029-mtk-mt76-mt7996-add-normal-mode-pre-calibration-supp.patch \
+    file://0030-mtk-mt76-mt7996-enable-SCS-feature-for-mt7996-driver.patch \
+    file://0031-mtk-mt76-mt7996-add-txpower-support.patch \
+    file://0032-mtk-mt76-mt7996-add-binfile-mode-support.patch \
+    file://0033-mtk-mt76-mt7996-add-testmode-ZWDFS-verification-supp.patch \
+    file://0034-mtk-mt76-mt7996-support-eagle-ZWDFS-on-iFEM.patch \
+    file://0035-mtk-mt76-mt7996-refactor-eeprom-loading-flow-for-sku.patch \
+    file://0036-mtk-mt76-mt7996-add-vendor-commands-support.patch \
+    file://0037-mtk-mt76-mt7996-add-debugfs-for-fw-coredump.patch \
+    file://0038-mtk-mt76-mt7996-Add-mt7992-coredump-support.patch \
+    file://0039-mtk-mt76-mt7996-add-support-for-runtime-set-in-band-.patch \
+    file://0040-mtk-mt76-mt7996-add-support-spatial-reuse-debug-comm.patch \
+    file://0041-mtk-mt76-mt7996-Establish-BA-in-VO-queue.patch \
+    file://0042-mtk-mt76-mt7996-report-tx-and-rx-byte-to-tpt_led.patch \
+    file://0043-mtk-mt76-mt7996-support-dup-wtbl.patch \
+    file://0044-mtk-mt76-try-more-times-when-send-message-timeout.patch \
+    file://0045-mtk-mt76-mt7996-add-SER-overlap-handle.patch \
+    file://0046-mtk-mt76-mt7996-kite-default-1-pcie-setting.patch \
+    file://0047-mtk-mt76-mt7996-support-BF-MIMO-debug-commands.patch \
+    file://0048-mtk-mt76-mt7996-add-build-the-following-MURU-mcu-com.patch \
+    file://0049-mtk-mt76-mt7996-add-cert-patch.patch \
+    file://0050-mtk-mt76-mt7996-add-testmode-bf-support.patch \
+    file://0051-mtk-mt76-mt7996-add-zwdfs-cert-mode.patch \
+    file://0052-mtk-mt76-mt7996-add-channel-68-96.patch \
+    file://0053-mtk-mt76-mt7996-add-kite-testmode-support.patch \
+    file://0054-mtk-mt76-mt7996-assign-DEAUTH-to-ALTX-queue-for-CERT.patch \
+    file://0055-mtk-mt76-mt7996-add-no_beacon-vendor-command-for-cer.patch \
+    file://0056-mtk-mt76-mt7996-add-adie-efuse-merge-support.patch \
+    file://0057-mtk-mt76-mt7996-add-Eagle-2adie-TBTC-BE14000-support.patch \
+    file://0058-mtk-mt76-mt7996-add-background-radar-hw-cap-check.patch \
+    file://0059-mtk-mt76-mt7996-add-fallback-in-case-of-missing-prec.patch \
+    file://0060-mtk-mt76-mt7996-add-kite-part-number-support.patch \
+    file://0061-mtk-wifi-mt76-revert-page_poll-for-kernel-5.4.patch \
+    file://0062-mtk-mt76-rework-wed-rx-flow.patch \
+    file://0063-mtk-mt76-change-wed-token-init-size-to-adapt-wed3.0.patch \
+    file://0064-mtk-mt76-add-random-early-drop-support.patch \
+    file://0065-mtk-mt76-mt7996-reset-addr_elem-when-delete-ba.patch \
+    file://0066-mtk-mt76-change-pcie0-R5-to-pcie1-to-get-6G-ICS.patch \
+    file://0067-mtk-mt76-add-SER-support-for-wed3.0.patch \
+    file://0068-mtk-mt76-find-rx-token-by-physical-address.patch \
+    file://0069-mtk-mt76-mt7996-add-dma-mask-limitation.patch \
+    file://0070-mtk-mt76-mt7996-add-per-bss-statistic-info.patch \
+    file://0071-mtk-mt76-mt7996-do-not-report-netdev-stats-on-monito.patch \
+    file://0072-mtk-mt76-mt7996-add-support-for-HW-ATF.patch \
+    file://0073-mtk-mt76-mt7996-add-SER0.5-support-w-wed3.0.patch \
+    file://0074-mtk-mt76-mt7996-support-backaward-compatiable.patch \
+    file://0075-mtk-mt76-mt7996-add-wed-support-for-mt7992.patch \
+    file://0076-mtk-mt76-add-2pcie-one-wed-support.patch \
+    file://0077-mtk-mt76-mt7996-Remove-wed-rro-ring-add-napi-at-init.patch \
+    file://0078-mtk-mt76-mt7996-Remove-wed_stop-during-L1-SER.patch \
+    file://0079-mtk-mt76-mt7996-Refactor-rro-del-ba-command-format.patch \
+    file://0080-mtk-mt76-mt7996-get-airtime-and-RSSI-via-MCU-command.patch \
+    file://0081-mtk-mt76-mt7996-add-support-for-WMM-PBC-configuratio.patch \
+    file://0082-mtk-mt76-mt7996-eagle-support-extra-option_type.patch \
+    file://0083-mtk-mt76-mt7996-support-enable-disable-thermal-prote.patch \
+    file://0084-mtk-mt76-mt7996-support-thermal-recal-debug-command.patch \
+    file://0085-mtk-mt76-mt7996-add-kite-two-pcie-with-two-wed-suppo.patch \
+    file://0086-mtk-mt76-add-support-to-enable-index-FW-log-for-Cons.patch \
+    file://0087-mtk-mt76-mt7996-implement-and-switch-to-hw-scan-call.patch \
+    file://0088-mtk-mt76-mt7996-implement-and-switch-to-chanctx-call.patch \
+    file://0089-mtk-mt76-mt7996-use-.sta_state-to-replace-.sta_add-a.patch \
+    file://0090-mtk-mt76-mt7996-switch-to-per-link-data-structure-of.patch \
+    file://0091-mtk-mt76-mt7996-switch-to-per-link-data-structure-of.patch \
+    file://0092-mtk-mt76-extend-wcid-and-sta-flow-for-MLO-support.patch \
+    file://0093-mtk-mt76-mt7996-enable-MLO-capability.patch \
+    file://0094-mtk-mt76-mt7996-support-multi-link-vif-links-and-MLO.patch \
+    file://0095-mtk-mt76-mt7996-support-multi-link-sta-links-and-MLO.patch \
+    file://0096-mtk-mt76-mt7996-introduce-mt7996_band_phy-for-ch-ban.patch \
+    file://0097-mtk-mt76-mt7996-rework-ieee80211_ops-callbacks-for-l.patch \
+    file://0098-mtk-mt76-mt7996-rework-TXD-for-multi-link-support.patch \
+    file://0099-mtk-mt76-mt7996-rework-TXS-for-multi-link-support.patch \
+    file://0100-mtk-mt76-mt7996-rework-RXD-for-multi-link-support.patch \
+    file://0101-mtk-mt76-mt7996-rework-mac-functions-for-multi-link-.patch \
+    file://0102-mtk-mt76-rework-mcu-functions-for-multi-link-support.patch \
+    file://0103-mtk-mt76-rework-connac-helpers.patch \
+    file://0104-mtk-mt76-mt7996-handle-mapping-for-hw-and-phy.patch \
+    file://0105-mtk-mt76-mt7996-handle-mapping-for-hw-and-vif.patch \
+    file://0106-mtk-mt76-mt7996-rework-scanning-parts-for-MLD-STA-su.patch \
+    file://0107-mtk-mt76-mt7996-implement-mld-address-translation.patch \
+    file://0108-mtk-mt76-mt7996-use-BSS_CHANGED_TXPOWER-for-txpower-.patch \
+    file://0109-mtk-mt76-mt7996-temp-support-for-single-wiphy.patch \
+    file://0110-mtk-mt76-mt7996-implement-ieee80211_ops-for-link-deb.patch \
+    file://0111-mtk-mt76-mt7996-support-multi-link-channel-switch.patch \
+    file://0112-mtk-mt76-mt7996-ACS-channel-time-too-long-on-duty-ch.patch \
+    file://0113-mtk-mt76-mt7996-add-beacon-monitoring-in-driver-for-.patch \
+    file://0114-mtk-mt76-mt7996-support-band_idx-option-for-set_mu-g.patch \
+    file://0115-mtk-mt76-mt7996-tmp-disable-VOW.patch \
+    file://0116-mtk-mt76-mt7996-enable-ampdu-limit-to-avoid-BA-bound.patch \
+    file://0117-mtk-mt76-mt7996-Fix-get_txpower-wrong-result-in-sing.patch \
+    file://0118-mtk-mt76-mt7996-Add-connac3-csi-feature.patch \
+    file://0119-mtk-mt76-mt7996-add-more-debug-info-for-MLO.patch \
+    file://0120-mtk-mt76-add-internal-debug-tool.patch \
+    file://0121-mtk-mt76-mt7996-add-linux-tracing-support.patch \
+    file://0122-mtk-mt76-temp-changes-for-SQC-period.patch \
+    file://0123-mtk-mt76-mt7996-remain-multiple-wiphy-model-for-test.patch \
+    file://0124-mtk-mt76-mt7996-enable-ibf-capability-for-mt7992.patch \
+    file://0125-mtk-mt76-remove-the-limitation-for-legacy-AP-sacn.patch \
+    file://0126-mtk-mt76-add-support-for-get_survey-in-single-wiphy-.patch \
+    file://0127-mtk-mt76-mt7996-add-critical-update-support.patch \
+    file://0128-mtk-mt76-mt7996-Add-support-for-EMLSR.patch \
+    file://0129-mtk-mt76-mt7996-add-max-mpdu-len-capability.patch \
+    file://0130-mtk-mt76-mt7996-add-correct-bss_conf-for-legacy-AP-s.patch \
+    file://0131-mtk-mt76-mt7996-fix-set-beacon-mcu-command.patch \
+    file://0132-mtk-mt76-fix-incorrect-setting-of-antenna-capability.patch \
+    file://0133-mtk-mt76-mt7996-fix-stop_tx_ba_session-warning.patch \
+    file://0134-mtk-mt76-mt7996-do-software-link-addr-translation-fo.patch \
+    file://0135-mtk-mt76-mt7996-add-per-band-debugfs-folder.patch \
+    file://0136-mtk-mt76-mt7996-move-internal-debugfs-knob-to-per-ba.patch \
+    file://0137-mtk-mt76-mt7996-refactor-amsdu-debugfs.patch \
+    file://0138-mtk-mt76-mt7996-trigger-channel-calibration-for-DFS-.patch \
+    file://0139-mtk-mt76-mt7996-do-not-remove-bss_info-and-starec-wh.patch \
+    file://0140-mtk-mt76-mt7996-remove-chanctx-in-mt7996_bss_conf.patch \
+    file://0141-mtk-mt76-mt7996-temporarily-disable-EPCS.patch \
+    file://0142-mtk-mt76-mt7996-fix-kite-can-t-handle-11v-beacon-on-.patch \
+    file://0143-mtk-mt76-mt7996-add-post-channel-switch-for-DFS-chan.patch \
+    file://0144-mtk-mt76-mt7996-update-testmode-bf-support.patch \
+    file://0145-mtk-mt76-mt7996-add-mlo-related-debugfs-knob.patch \
+    file://0146-mtk-mt76-mt7996-add-debugfs-knob-to-show-mlo-related.patch \
+    file://0147-mtk-mt76-mt7996-add-debugfs-knob-to-set-agc.patch \
+    file://0148-mtk-mt76-mt7996-set-unused-band-to-UNSPECIFIED-in-ba.patch \
+    file://0149-mtk-mt76-mt7996-support-per-link-report-of-bss-color.patch \
+    file://0150-mtk-mt76-mt7996-Fix-inconsistent-QoS-mapping-between.patch \
+    file://0151-mtk-mt76-mt7996-add-support-for-MLD-interface-to-sca.patch \
+    file://0152-mtk-mt76-mt7996-add-per-link-txpower-config.patch \
+    file://0153-mtk-mt76-mt7996-update-TX-RX-rates-via-MCU-command.patch \
+    file://0154-mtk-mt76-mt7996-add-link-information-when-dump-stati.patch \
+    file://0155-mtk-mt76-mt7996-add-per-link-RX-MPDU-statistics.patch \
+    file://0156-mtk-mt76-mt7996-support-link_id-for-mt7996_set_bitra.patch \
+    file://0157-mtk-mt76-mt7996-add-per-radio-antenna-config.patch \
+    file://0158-mtk-mt76-mt7996-rework-debug-prints.patch \
+    file://0159-mtk-mt76-mt7996-support-configure-trigger-frame-type.patch \
+    file://0160-mtk-mt76-mt7996-support-configure-coding-type-for-wi.patch \
+    file://0161-mtk-mt76-mt7996-record-RSSI-and-SNR.patch \
+    file://0162-mtk-mt76-mt7996-support-find-the-mt7996_phy-by-link_.patch \
+    file://0163-mtk-mt76-mt7996-workaround-for-get_tsf-crash-issue.patch \
+    file://0164-mtk-mt76-add-debugfs-for-tx-drop-counters.patch \
+    file://0165-mtk-mt76-add-debugfs-for-rx-drop-counters.patch \
+    file://0166-mtk-mt76-mt7996-add-support-for-remain-on-channel-op.patch \
+    file://0167-mtk-mt76-mt7996-rework-the-setting-flow-of-starec-RA.patch \
+    file://0168-mtk-mt76-mt7996-Fix-NULL-pointer-crash-when-mac-tx-f.patch \
+    file://0169-mtk-mt76-mt7996-update-adie-efuse-merge-support.patch \
+    file://0170-mtk-mt76-mt7996-support-handle-link_id-in-ap_wireles.patch \
+    file://0171-mtk-mt76-mt7996-fix-incorrect-indexing-of-MIB-FW-eve.patch \
+    file://0172-mtk-mt76-mt7996-support-muru-dbg-info-debug-commands.patch \
+    file://0173-mtk-mt76-mt7996-add-kite-if_comb.patch \
+    file://0174-mtk-mt76-mt7996-change-source-of-per-WCID-TX-MPDU-st.patch \
+    file://0175-mtk-mt76-mt7996-update-preamble-puncture-support-for.patch \
+    file://0176-mtk-mt76-Add-dynamic-pp-vendor-and-debug-pp-algo-cmd.patch \
+    file://0177-mtk-mt76-mt7996-disable-MAT-and-set-force-link-for-4.patch \
+    file://0178-mtk-mt76-mt7996-add-per-STA-TX-MSDU-failed-and-retri.patch \
+    file://0179-mtk-mt76-mt7996-fill-in-sn-into-txd-for-MLD-multicas.patch \
+    file://0180-mtk-mt76-mt7996-fix-potential-null-pointer.patch \
+    file://0181-mtk-mt76-mt7996-Fix-legacy-action-frame-wrong-addres.patch \
+    file://0182-mtk-mt76-mt7996-add-AP-affiliated-link-removal-suppo.patch \
+    file://0183-mtk-mt76-mt7996-add-support-for-AP-A-TTLM.patch \
+    file://0184-mtk-mt76-mt7996-leave-ps-when-4-address-is-establish.patch \
+    file://0185-mtk-mt76-mt7996-add-debugfs-knob-to-set-and-dump-txo.patch \
+    file://0186-mtk-mt76-mt7996-add-mcu-command-to-set-bssid-mapping.patch \
+    file://0187-mtk-mt76-mt7996-Temporary-fix-init-txpwoer-for-singl.patch \
+    file://0188-mtk-mt76-mt7996-Add-lpi-support-with-sku_idx-and-enh.patch \
+    file://0189-mtk-mt76-mt7996-Add-Triggered-Uplink-Access-Optimiza.patch \
+    file://0190-mtk-mt76-mt7996-add-per-band-token-limit.patch \
+    file://0191-mtk-mt76-sync-with-upstream-changes.patch \
+    file://0192-mtk-mt76-mt7996-record-per-antenna-average-data-fram.patch \
+    file://0193-mtk-mt76-mt7996-remove-default-bss_conf-when-link-be.patch \
+    file://0194-mtk-mt76-mt7996-pass-vif-cfg.assoc-to-mt7996_mac_sta.patch \
+    file://0195-mtk-mt76-mt7996-separate-hwrro-from-wed.patch \
+    file://0196-mtk-mt76-mt7996-ignore-vif.dormant_links-in-mt7996_c.patch \
+    file://0197-mtk-mt76-mt7996-add-Adv-TTLM-support-for-STA.patch \
+    file://0198-mtk-mt76-mt7996-Add-AFC-and-lpi-power-support.patch \
+    file://0199-mtk-mt76-do-not-report-ACK-when-TXS-is-lost.patch \
     "
diff --git a/recipes-wifi/linux-mt76/files/patches/0001-wifi-mt76-fix-incorrect-HE-TX-GI-report.patch b/recipes-wifi/linux-mt76/files/patches/0001-wifi-mt76-fix-incorrect-HE-TX-GI-report.patch
index 41c0721..01fb995 100644
--- a/recipes-wifi/linux-mt76/files/patches/0001-wifi-mt76-fix-incorrect-HE-TX-GI-report.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0001-wifi-mt76-fix-incorrect-HE-TX-GI-report.patch
@@ -1,4 +1,4 @@
-From 4621fb4257ade6a4639be6ab8f785e4d8e3bba43 Mon Sep 17 00:00:00 2001
+From 67edc0d71c271793b5ab04338abedaab41b8586e Mon Sep 17 00:00:00 2001
 From: Evelyn Tsai <evelyn.tsai@mediatek.com>
 Date: Thu, 18 May 2023 18:11:37 +0800
 Subject: [PATCH 01/21] wifi: mt76: fix incorrect HE TX GI report
@@ -17,7 +17,7 @@
  7 files changed, 282 insertions(+), 22 deletions(-)
 
 diff --git a/mt76.h b/mt76.h
-index 2cbea73..92acba9 100644
+index 7dd59db3..bf0770a8 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -282,12 +282,16 @@ struct mt76_queue_ops {
@@ -38,10 +38,10 @@
  	MT_PHY_TYPE_HE_EXT_SU,
  	MT_PHY_TYPE_HE_TB,
 diff --git a/mt7915/init.c b/mt7915/init.c
-index eee1879..edf83c4 100644
+index f1ef965f..888dbf82 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
-@@ -673,6 +673,8 @@ mt7915_register_ext_phy(struct mt7915_dev *dev, struct mt7915_phy *phy)
+@@ -674,6 +674,8 @@ mt7915_register_ext_phy(struct mt7915_dev *dev, struct mt7915_phy *phy)
  	struct mt76_phy *mphy = phy->mt76;
  	int ret;
  
@@ -50,7 +50,7 @@
  	INIT_DELAYED_WORK(&mphy->mac_work, mt7915_mac_work);
  
  	mt7915_eeprom_parse_hw_cap(dev, phy);
-@@ -1206,6 +1208,8 @@ int mt7915_register_device(struct mt7915_dev *dev)
+@@ -1204,6 +1206,8 @@ int mt7915_register_device(struct mt7915_dev *dev)
  	dev->phy.dev = dev;
  	dev->phy.mt76 = &dev->mt76.phy;
  	dev->mt76.phy.priv = &dev->phy;
@@ -60,7 +60,7 @@
  	INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7915_mac_work);
  	INIT_LIST_HEAD(&dev->sta_rc_list);
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index 8008ce3..b915201 100644
+index 8008ce3f..b9152018 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -180,15 +180,7 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev)
@@ -188,7 +188,7 @@
  
  	mt76_tx_status_check(mphy->dev, false);
 diff --git a/mt7915/main.c b/mt7915/main.c
-index b16a633..e61041d 100644
+index 2624edbb..b2a6278f 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -752,6 +752,7 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
@@ -220,7 +220,7 @@
  
  static void mt7915_tx(struct ieee80211_hw *hw,
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index fe54a2f..7df2162 100644
+index 18ba20cf..9774bcce 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -3793,6 +3793,167 @@ out:
@@ -392,7 +392,7 @@
  				struct cfg80211_he_bss_color *he_bss_color)
  {
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index b41ac4a..8f36546 100644
+index b41ac4aa..8f365461 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -152,6 +152,61 @@ struct mt7915_mcu_eeprom_info {
@@ -466,7 +466,7 @@
 +};
  #endif
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index a30d08e..aee30c7 100644
+index a30d08eb..aee30c73 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -137,6 +137,7 @@ struct mt7915_sta {
diff --git a/recipes-wifi/linux-mt76/files/patches/0002-wifi-mt76-mt7915-add-pc-stack-dump-for-WM-s-coredump.patch b/recipes-wifi/linux-mt76/files/patches/0002-wifi-mt76-mt7915-add-pc-stack-dump-for-WM-s-coredump.patch
index 5d62e29..a235b66 100644
--- a/recipes-wifi/linux-mt76/files/patches/0002-wifi-mt76-mt7915-add-pc-stack-dump-for-WM-s-coredump.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0002-wifi-mt76-mt7915-add-pc-stack-dump-for-WM-s-coredump.patch
@@ -1,4 +1,4 @@
-From c680e5830a6f55b930c2e2e6fdd47f7c957fb656 Mon Sep 17 00:00:00 2001
+From 044f6b5284b02b6f41e91d0e8bfb5a022b43cbf9 Mon Sep 17 00:00:00 2001
 From: Bo Jiao <Bo.Jiao@mediatek.com>
 Date: Mon, 22 May 2023 13:49:37 +0800
 Subject: [PATCH 02/21] wifi: mt76: mt7915: add pc stack dump for WM's
@@ -16,7 +16,7 @@
  7 files changed, 207 insertions(+), 71 deletions(-)
 
 diff --git a/mt76.h b/mt76.h
-index 92acba9..ee14425 100644
+index bf0770a8..4fb07877 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -32,6 +32,8 @@
@@ -52,7 +52,7 @@
  	struct net_device napi_dev;
  	struct net_device tx_napi_dev;
 diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
-index b35acf8..1ea9798 100644
+index 162c57fb..4baaaacf 100644
 --- a/mt76_connac_mcu.c
 +++ b/mt76_connac_mcu.c
 @@ -2941,6 +2941,9 @@ int mt76_connac2_load_ram(struct mt76_dev *dev, const char *fw_wm,
@@ -86,7 +86,7 @@
  		struct mt76_connac2_patch_sec *sec;
  		u32 len, addr, mode;
 diff --git a/mt7915/coredump.c b/mt7915/coredump.c
-index 5daf225..298c1ca 100644
+index 5daf2258..298c1cad 100644
 --- a/mt7915/coredump.c
 +++ b/mt7915/coredump.c
 @@ -7,7 +7,7 @@
@@ -414,7 +414,7 @@
  }
  
 diff --git a/mt7915/coredump.h b/mt7915/coredump.h
-index 709f8e9..809ccbd 100644
+index 709f8e9c..809ccbdf 100644
 --- a/mt7915/coredump.h
 +++ b/mt7915/coredump.h
 @@ -4,6 +4,7 @@
@@ -514,7 +514,7 @@
  	return NULL;
  }
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index b915201..0f6b806 100644
+index b9152018..0f6b8067 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -1597,28 +1597,31 @@ void mt7915_mac_reset_work(struct work_struct *work)
@@ -595,7 +595,7 @@
  }
  
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index aee30c7..5cd2b33 100644
+index aee30c73..5cd2b334 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -286,7 +286,7 @@ struct mt7915_dev {
@@ -608,7 +608,7 @@
  #endif
  
 diff --git a/mt7915/regs.h b/mt7915/regs.h
-index 89ac8e6..7515b23 100644
+index 89ac8e67..7515b23f 100644
 --- a/mt7915/regs.h
 +++ b/mt7915/regs.h
 @@ -1219,4 +1219,24 @@ enum offs_rev {
diff --git a/recipes-wifi/linux-mt76/files/patches/0003-wifi-mt76-mt7915-move-temperature-margin-check-to-mt.patch b/recipes-wifi/linux-mt76/files/patches/0003-wifi-mt76-mt7915-move-temperature-margin-check-to-mt.patch
index 4f3c97a..dbb5c3f 100644
--- a/recipes-wifi/linux-mt76/files/patches/0003-wifi-mt76-mt7915-move-temperature-margin-check-to-mt.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0003-wifi-mt76-mt7915-move-temperature-margin-check-to-mt.patch
@@ -1,4 +1,4 @@
-From 2825cf9ff77fa1065dac5c7a129aad9e8edecdf3 Mon Sep 17 00:00:00 2001
+From c4f870c030edebe27120d87364a213c9f8f7089c Mon Sep 17 00:00:00 2001
 From: Howard Hsu <howard-yh.hsu@mediatek.com>
 Date: Thu, 13 Jul 2023 15:50:00 +0800
 Subject: [PATCH 03/21] wifi: mt76: mt7915: move temperature margin check to
@@ -17,10 +17,10 @@
  2 files changed, 5 insertions(+), 5 deletions(-)
 
 diff --git a/mt7915/init.c b/mt7915/init.c
-index edf83c4..9fe0524 100644
+index 888dbf82..3b352fe9 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
-@@ -83,12 +83,13 @@ static ssize_t mt7915_thermal_temp_store(struct device *dev,
+@@ -84,12 +84,13 @@ static ssize_t mt7915_thermal_temp_store(struct device *dev,
  	mutex_lock(&phy->dev->mt76.mutex);
  	val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 60, 130);
  
@@ -38,7 +38,7 @@
  		return -EINVAL;
  	}
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 7df2162..6e9970c 100644
+index 9774bcce..dcf8782a 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -3226,8 +3226,7 @@ int mt7915_mcu_set_thermal_protect(struct mt7915_phy *phy)
diff --git a/recipes-wifi/linux-mt76/files/patches/0004-wifi-mt76-mt7915-fix-txpower-issues.patch b/recipes-wifi/linux-mt76/files/patches/0004-wifi-mt76-mt7915-fix-txpower-issues.patch
index a9a5f48..a93ff89 100644
--- a/recipes-wifi/linux-mt76/files/patches/0004-wifi-mt76-mt7915-fix-txpower-issues.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0004-wifi-mt76-mt7915-fix-txpower-issues.patch
@@ -1,4 +1,4 @@
-From 47f124ba1718259555245eb9c3ad3fb2b4fd0a67 Mon Sep 17 00:00:00 2001
+From 349e821372153fddd6abcd295e50753e5f040f1f 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 04/21] wifi: mt76: mt7915: fix txpower issues
@@ -10,7 +10,7 @@
  3 files changed, 28 insertions(+), 23 deletions(-)
 
 diff --git a/eeprom.c b/eeprom.c
-index 0bc66cc..ecd09c0 100644
+index 0bc66cc1..ecd09c03 100644
 --- a/eeprom.c
 +++ b/eeprom.c
 @@ -343,7 +343,7 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
@@ -23,7 +23,7 @@
  
  	if (!mcs_rates)
 diff --git a/mt7915/debugfs.c b/mt7915/debugfs.c
-index 5780138..894e2cd 100644
+index 57801388..894e2cd7 100644
 --- a/mt7915/debugfs.c
 +++ b/mt7915/debugfs.c
 @@ -951,9 +951,9 @@ mt7915_xmit_queues_show(struct seq_file *file, void *data)
@@ -107,7 +107,7 @@
  	reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_TPC_CTRL_STAT(band) :
  	      MT_WF_PHY_TPC_CTRL_STAT_MT7916(band);
 diff --git a/mt7915/main.c b/mt7915/main.c
-index e61041d..1903db4 100644
+index b2a6278f..a2ad918d 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -1080,6 +1080,7 @@ mt7915_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
diff --git a/recipes-wifi/linux-mt76/files/patches/0005-wifi-mt76-mt7915-Fixed-null-pointer-dereference-issu.patch b/recipes-wifi/linux-mt76/files/patches/0005-wifi-mt76-mt7915-Fixed-null-pointer-dereference-issu.patch
index 07a8d7b..81601e4 100644
--- a/recipes-wifi/linux-mt76/files/patches/0005-wifi-mt76-mt7915-Fixed-null-pointer-dereference-issu.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0005-wifi-mt76-mt7915-Fixed-null-pointer-dereference-issu.patch
@@ -1,4 +1,4 @@
-From dc0b3bc81859846e1f7bab33eca1212753b4ec1d Mon Sep 17 00:00:00 2001
+From 7b49a07b4440843e3a85c268d3526b410ebf061e Mon Sep 17 00:00:00 2001
 From: MeiChia Chiu <meichia.chiu@mediatek.com>
 Date: Thu, 26 Oct 2023 21:11:05 +0800
 Subject: [PATCH 05/21] wifi: mt76: mt7915: Fixed null pointer dereference
@@ -17,7 +17,7 @@
  1 file changed, 7 insertions(+)
 
 diff --git a/mt7915/main.c b/mt7915/main.c
-index 1903db4..61a1dbb 100644
+index a2ad918d..ec2360c3 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -1170,9 +1170,16 @@ static void mt7915_sta_rc_update(struct ieee80211_hw *hw,
diff --git a/recipes-wifi/linux-mt76/files/patches/0006-wifi-mt76-ACS-channel-time-too-long-on-duty-channel.patch b/recipes-wifi/linux-mt76/files/patches/0006-wifi-mt76-ACS-channel-time-too-long-on-duty-channel.patch
index 4bf49da..ac20c29 100644
--- a/recipes-wifi/linux-mt76/files/patches/0006-wifi-mt76-ACS-channel-time-too-long-on-duty-channel.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0006-wifi-mt76-ACS-channel-time-too-long-on-duty-channel.patch
@@ -1,4 +1,4 @@
-From e6a8b61c776db565c532515af002156da38f2f48 Mon Sep 17 00:00:00 2001
+From b6f567e8d4223c309134df14c577f0adee2044bf Mon Sep 17 00:00:00 2001
 From: Evelyn Tsai <evelyn.tsai@mediatek.com>
 Date: Sat, 18 Nov 2023 07:36:45 +0800
 Subject: [PATCH 06/21] wifi: mt76: ACS channel time too long on duty channel
@@ -26,10 +26,10 @@
  1 file changed, 2 insertions(+), 1 deletion(-)
 
 diff --git a/mac80211.c b/mac80211.c
-index e7b763b..bc20f60 100644
+index 94e85ed9..aee6f1e7 100644
 --- a/mac80211.c
 +++ b/mac80211.c
-@@ -927,6 +927,7 @@ void mt76_set_channel(struct mt76_phy *phy)
+@@ -928,6 +928,7 @@ void mt76_set_channel(struct mt76_phy *phy)
  	struct cfg80211_chan_def *chandef = &hw->conf.chandef;
  	bool offchannel = hw->conf.flags & IEEE80211_CONF_OFFCHANNEL;
  	int timeout = HZ / 5;
@@ -37,7 +37,7 @@
  
  	wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(phy), timeout);
  	mt76_update_survey(phy);
-@@ -941,7 +942,7 @@ void mt76_set_channel(struct mt76_phy *phy)
+@@ -942,7 +943,7 @@ void mt76_set_channel(struct mt76_phy *phy)
  	if (!offchannel)
  		phy->main_chan = chandef->chan;
  
diff --git a/recipes-wifi/linux-mt76/files/patches/0007-wifi-mt76-mt7915-add-post-channel-switch-for-DFS-cha.patch b/recipes-wifi/linux-mt76/files/patches/0007-wifi-mt76-mt7915-add-post-channel-switch-for-DFS-cha.patch
index 8dbeb72..c40f2ae 100644
--- a/recipes-wifi/linux-mt76/files/patches/0007-wifi-mt76-mt7915-add-post-channel-switch-for-DFS-cha.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0007-wifi-mt76-mt7915-add-post-channel-switch-for-DFS-cha.patch
@@ -1,4 +1,4 @@
-From 96ee52ffd24c36443d93df103b2a856832a41443 Mon Sep 17 00:00:00 2001
+From d5a9af18ab8f2c8c6a46c10ef99f2a50e08ae9c3 Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Thu, 16 Nov 2023 14:41:54 +0800
 Subject: [PATCH 07/21] wifi: mt76: mt7915: add post channel switch for DFS
@@ -10,7 +10,7 @@
  1 file changed, 22 insertions(+)
 
 diff --git a/mt7915/main.c b/mt7915/main.c
-index 61a1dbb..71e0d55 100644
+index ec2360c3..0d24e74c 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -736,6 +736,27 @@ mt7915_channel_switch_beacon(struct ieee80211_hw *hw,
@@ -41,7 +41,7 @@
  int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
  		       struct ieee80211_sta *sta)
  {
-@@ -1701,6 +1722,7 @@ const struct ieee80211_ops mt7915_ops = {
+@@ -1705,6 +1726,7 @@ const struct ieee80211_ops mt7915_ops = {
  	.get_txpower = mt76_get_txpower,
  	.set_sar_specs = mt7915_set_sar_specs,
  	.channel_switch_beacon = mt7915_channel_switch_beacon,
diff --git a/recipes-wifi/linux-mt76/files/patches/0008-wifi-mt76-mt7915-add-support-for-realtime-Rx-rate-up.patch b/recipes-wifi/linux-mt76/files/patches/0008-wifi-mt76-mt7915-add-support-for-realtime-Rx-rate-up.patch
index 7e82038..a5a0241 100644
--- a/recipes-wifi/linux-mt76/files/patches/0008-wifi-mt76-mt7915-add-support-for-realtime-Rx-rate-up.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0008-wifi-mt76-mt7915-add-support-for-realtime-Rx-rate-up.patch
@@ -1,4 +1,4 @@
-From 308339254c28af264d9c64f1bc3e29a996d5da12 Mon Sep 17 00:00:00 2001
+From 2b85ea7a8f85c36709442d5ebab72bded582d5cd Mon Sep 17 00:00:00 2001
 From: "Henry.Yen" <henry.yen@mediatek.com>
 Date: Mon, 8 Jan 2024 17:19:01 +0800
 Subject: [PATCH 08/21] wifi: mt76: mt7915: add support for realtime Rx rate
@@ -19,7 +19,7 @@
  2 files changed, 7 insertions(+), 1 deletion(-)
 
 diff --git a/mt76_connac.h b/mt76_connac.h
-index 91987bd..4871857 100644
+index 445d0f0a..5028e49a 100644
 --- a/mt76_connac.h
 +++ b/mt76_connac.h
 @@ -260,6 +260,12 @@ static inline bool is_connac_v1(struct mt76_dev *dev)
@@ -36,7 +36,7 @@
  {
  	switch (mt76_chip(dev)) {
 diff --git a/mt7915/main.c b/mt7915/main.c
-index 71e0d55..5d31f5a 100644
+index 0d24e74c..645d9779 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -1118,7 +1118,7 @@ static void mt7915_sta_statistics(struct ieee80211_hw *hw,
diff --git a/recipes-wifi/linux-mt76/files/patches/0009-wifi-mt76-mt7915-remove-redundant-argument-in-add_be.patch b/recipes-wifi/linux-mt76/files/patches/0009-wifi-mt76-mt7915-remove-redundant-argument-in-add_be.patch
index 815103c..9125ae3 100644
--- a/recipes-wifi/linux-mt76/files/patches/0009-wifi-mt76-mt7915-remove-redundant-argument-in-add_be.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0009-wifi-mt76-mt7915-remove-redundant-argument-in-add_be.patch
@@ -1,4 +1,4 @@
-From 963b2b1d3955ccf2cf4806790c9184e5dfc3e3ea Mon Sep 17 00:00:00 2001
+From bc85ec5a69b6e57eab15c50b54e0603a0829d102 Mon Sep 17 00:00:00 2001
 From: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
 Date: Wed, 24 Jan 2024 15:04:33 +0800
 Subject: [PATCH 09/21] wifi: mt76: mt7915: remove redundant argument in
@@ -15,7 +15,7 @@
  4 files changed, 5 insertions(+), 7 deletions(-)
 
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index 0f6b806..ada3a7f 100644
+index 0f6b8067..ada3a7f4 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -1285,8 +1285,7 @@ mt7915_update_vif_beacon(void *priv, u8 *mac, struct ieee80211_vif *vif)
@@ -29,7 +29,7 @@
  	default:
  		break;
 diff --git a/mt7915/main.c b/mt7915/main.c
-index 5d31f5a..9eeca39 100644
+index 645d9779..f0491d66 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -659,7 +659,7 @@ static void mt7915_bss_info_changed(struct ieee80211_hw *hw,
@@ -51,7 +51,7 @@
  }
  
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 6e9970c..84ffe07 100644
+index dcf8782a..a71562fb 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -1970,8 +1970,7 @@ mt7915_mcu_add_inband_discov(struct mt7915_dev *dev, struct ieee80211_vif *vif,
@@ -65,7 +65,7 @@
  	struct mt7915_dev *dev = mt7915_hw_dev(hw);
  	struct mt7915_phy *phy = mt7915_hw_phy(hw);
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 5cd2b33..e1801d5 100644
+index 5cd2b334..e1801d5b 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -461,7 +461,7 @@ int mt7915_mcu_update_bss_color(struct mt7915_dev *dev, struct ieee80211_vif *vi
diff --git a/recipes-wifi/linux-mt76/files/patches/0010-wifi-mt76-mt7915-add-support-for-WMM-PBC-configurati.patch b/recipes-wifi/linux-mt76/files/patches/0010-wifi-mt76-mt7915-add-support-for-WMM-PBC-configurati.patch
index e508cd1..04fe37e 100644
--- a/recipes-wifi/linux-mt76/files/patches/0010-wifi-mt76-mt7915-add-support-for-WMM-PBC-configurati.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0010-wifi-mt76-mt7915-add-support-for-WMM-PBC-configurati.patch
@@ -1,4 +1,4 @@
-From 4d8bb032a13e467479bf852a7c72c693814bc11b Mon Sep 17 00:00:00 2001
+From 664d47ddcc7ed90b5d2d73b2040759bf0bf75714 Mon Sep 17 00:00:00 2001
 From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
 Date: Mon, 29 Jan 2024 11:28:41 +0800
 Subject: [PATCH 10/21] wifi: mt76: mt7915: add support for WMM PBC
@@ -14,10 +14,10 @@
  6 files changed, 127 insertions(+)
 
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 67be14d..1dd8244 100644
+index 6873ce14..46dcd1c6 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
-@@ -1025,6 +1025,7 @@ enum {
+@@ -1026,6 +1026,7 @@ enum {
  	MCU_EXT_EVENT_ASSERT_DUMP = 0x23,
  	MCU_EXT_EVENT_RDD_REPORT = 0x3a,
  	MCU_EXT_EVENT_CSA_NOTIFY = 0x4f,
@@ -25,7 +25,7 @@
  	MCU_EXT_EVENT_WA_TX_STAT = 0x74,
  	MCU_EXT_EVENT_BCC_NOTIFY = 0x75,
  	MCU_EXT_EVENT_MURU_CTRL = 0x9f,
-@@ -1220,6 +1221,7 @@ enum {
+@@ -1222,6 +1223,7 @@ enum {
  	MCU_EXT_CMD_TXDPD_CAL = 0x60,
  	MCU_EXT_CMD_CAL_CACHE = 0x67,
  	MCU_EXT_CMD_RED_ENABLE = 0x68,
@@ -34,10 +34,10 @@
  	MCU_EXT_CMD_SET_RADAR_TH = 0x7c,
  	MCU_EXT_CMD_SET_RDD_PATTERN = 0x7d,
 diff --git a/mt7915/init.c b/mt7915/init.c
-index 9fe0524..f81a2f2 100644
+index 3b352fe9..46762827 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
-@@ -1221,6 +1221,8 @@ int mt7915_register_device(struct mt7915_dev *dev)
+@@ -1219,6 +1219,8 @@ int mt7915_register_device(struct mt7915_dev *dev)
  	INIT_WORK(&dev->dump_work, mt7915_mac_dump_work);
  	mutex_init(&dev->dump_mutex);
  
@@ -47,7 +47,7 @@
  
  	phy2 = mt7915_alloc_ext_phy(dev);
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index ada3a7f..e167e7b 100644
+index ada3a7f4..e167e7b6 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -2032,6 +2032,8 @@ void mt7915_mac_work(struct work_struct *work)
@@ -79,7 +79,7 @@
  
  	if (++phy->stats_work_count == 10) {
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 84ffe07..446c512 100644
+index a71562fb..9126e62f 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -354,6 +354,93 @@ mt7915_mcu_rx_bcc_notify(struct mt7915_dev *dev, struct sk_buff *skb)
@@ -187,7 +187,7 @@
  		break;
  	}
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index 8f36546..fa0847d 100644
+index 8f365461..fa0847d5 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -329,10 +329,25 @@ enum {
@@ -217,7 +217,7 @@
  };
  
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index e1801d5..89156f3 100644
+index e1801d5b..89156f35 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -326,6 +326,9 @@ struct mt7915_dev {
diff --git a/recipes-wifi/linux-mt76/files/patches/0011-wifi-mt76-fix-tx-statistics-about-tx-retry-and-tx-fa.patch b/recipes-wifi/linux-mt76/files/patches/0011-wifi-mt76-fix-tx-statistics-about-tx-retry-and-tx-fa.patch
index 9837869..238b452 100644
--- a/recipes-wifi/linux-mt76/files/patches/0011-wifi-mt76-fix-tx-statistics-about-tx-retry-and-tx-fa.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0011-wifi-mt76-fix-tx-statistics-about-tx-retry-and-tx-fa.patch
@@ -1,4 +1,4 @@
-From 99458375f8a646403eed7c259d05ff3e47e892b6 Mon Sep 17 00:00:00 2001
+From 1587828e06491339654d20dea3382749fde143ed Mon Sep 17 00:00:00 2001
 From: Peter Chiu <chui-hao.chiu@mediatek.com>
 Date: Mon, 29 Jan 2024 11:02:06 +0800
 Subject: [PATCH 11/21] wifi: mt76: fix tx statistics about tx retry and tx
@@ -13,7 +13,7 @@
  2 files changed, 1 insertion(+), 4 deletions(-)
 
 diff --git a/mt76_connac_mac.c b/mt76_connac_mac.c
-index b841bf6..630c640 100644
+index b841bf62..630c6402 100644
 --- a/mt76_connac_mac.c
 +++ b/mt76_connac_mac.c
 @@ -716,9 +716,6 @@ bool mt76_connac2_mac_add_txs_skb(struct mt76_dev *dev, struct mt76_wcid *wcid,
@@ -27,7 +27,7 @@
  	skb = mt76_tx_status_skb_get(dev, wcid, pid, &list);
  	if (skb) {
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index e167e7b..a5d0b09 100644
+index e167e7b6..a5d0b096 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -1021,7 +1021,7 @@ static void mt7915_mac_add_txs(struct mt7915_dev *dev, void *data)
diff --git a/recipes-wifi/linux-mt76/files/patches/0012-wifi-mt76-add-sanity-check-to-prevent-kernel-crash.patch b/recipes-wifi/linux-mt76/files/patches/0012-wifi-mt76-add-sanity-check-to-prevent-kernel-crash.patch
index d225e02..576cf69 100644
--- a/recipes-wifi/linux-mt76/files/patches/0012-wifi-mt76-add-sanity-check-to-prevent-kernel-crash.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0012-wifi-mt76-add-sanity-check-to-prevent-kernel-crash.patch
@@ -1,4 +1,4 @@
-From 8bcee1da876907d67de94317dcd343842a54921f Mon Sep 17 00:00:00 2001
+From 65092f531e1319ed6ddb25e982393eddccb781b5 Mon Sep 17 00:00:00 2001
 From: Peter Chiu <chui-hao.chiu@mediatek.com>
 Date: Mon, 29 Jan 2024 15:33:24 +0800
 Subject: [PATCH 12/21] wifi: mt76: add sanity check to prevent kernel crash
@@ -12,7 +12,7 @@
  1 file changed, 8 insertions(+)
 
 diff --git a/tx.c b/tx.c
-index 5cf6ede..ab42f69 100644
+index 5cf6edee..ab42f69b 100644
 --- a/tx.c
 +++ b/tx.c
 @@ -345,6 +345,14 @@ mt76_tx(struct mt76_phy *phy, struct ieee80211_sta *sta,
diff --git a/recipes-wifi/linux-mt76/files/patches/0013-wifi-mt76-mt7915-limit-per-band-token-count.patch b/recipes-wifi/linux-mt76/files/patches/0013-wifi-mt76-mt7915-limit-per-band-token-count.patch
index 6d59422..167f36c 100644
--- a/recipes-wifi/linux-mt76/files/patches/0013-wifi-mt76-mt7915-limit-per-band-token-count.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0013-wifi-mt76-mt7915-limit-per-band-token-count.patch
@@ -1,4 +1,4 @@
-From ed564a0fe9b656b67eafc74c63aa747b43c49580 Mon Sep 17 00:00:00 2001
+From a2f8deaaf6a97b0157e49ec476b003ef1dd234f8 Mon Sep 17 00:00:00 2001
 From: Peter Chiu <chui-hao.chiu@mediatek.com>
 Date: Mon, 29 Jan 2024 15:33:24 +0800
 Subject: [PATCH 13/21] wifi: mt76: mt7915: limit per-band token count
@@ -20,7 +20,7 @@
  9 files changed, 41 insertions(+), 7 deletions(-)
 
 diff --git a/mt76.h b/mt76.h
-index ee14425..b83456b 100644
+index 4fb07877..05ee568c 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -407,6 +407,8 @@ struct mt76_txwi_cache {
@@ -49,7 +49,7 @@
  
  	spinlock_t rx_token_lock;
  	struct idr rx_token;
-@@ -1662,7 +1667,8 @@ static inline bool mt76_queue_is_wed_rx(struct mt76_queue *q)
+@@ -1674,7 +1679,8 @@ static inline bool mt76_queue_is_wed_rx(struct mt76_queue *q)
  
  struct mt76_txwi_cache *
  mt76_token_release(struct mt76_dev *dev, int token, bool *wake);
@@ -60,7 +60,7 @@
  struct mt76_txwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token);
  int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
 diff --git a/mt76_connac_mac.c b/mt76_connac_mac.c
-index 630c640..a92c261 100644
+index 630c6402..a92c261d 100644
 --- a/mt76_connac_mac.c
 +++ b/mt76_connac_mac.c
 @@ -1178,6 +1178,8 @@ void mt76_connac2_tx_token_put(struct mt76_dev *dev)
@@ -73,10 +73,10 @@
  	spin_unlock_bh(&dev->token_lock);
  	idr_destroy(&dev->token);
 diff --git a/mt7915/init.c b/mt7915/init.c
-index f81a2f2..3ec9eab 100644
+index 46762827..ea9bc735 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
-@@ -1225,6 +1225,8 @@ int mt7915_register_device(struct mt7915_dev *dev)
+@@ -1223,6 +1223,8 @@ int mt7915_register_device(struct mt7915_dev *dev)
  
  	dev->dbdc_support = mt7915_band_config(dev);
  
@@ -85,7 +85,7 @@
  	phy2 = mt7915_alloc_ext_phy(dev);
  	if (IS_ERR(phy2))
  		return PTR_ERR(phy2);
-@@ -1257,6 +1259,7 @@ int mt7915_register_device(struct mt7915_dev *dev)
+@@ -1255,6 +1257,7 @@ int mt7915_register_device(struct mt7915_dev *dev)
  	}
  
  	dev->recovery.hw_init_done = true;
@@ -94,7 +94,7 @@
  	ret = mt7915_init_debugfs(&dev->phy);
  	if (ret)
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index a5d0b09..4604a68 100644
+index a5d0b096..4604a682 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -738,6 +738,7 @@ int mt7915_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
@@ -115,7 +115,7 @@
  		return id;
  
 diff --git a/mt7921/pci_mac.c b/mt7921/pci_mac.c
-index 031ba9a..4c69c55 100644
+index 031ba9aa..4c69c55c 100644
 --- a/mt7921/pci_mac.c
 +++ b/mt7921/pci_mac.c
 @@ -27,7 +27,7 @@ int mt7921e_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
@@ -128,7 +128,7 @@
  		return id;
  
 diff --git a/mt7925/pci_mac.c b/mt7925/pci_mac.c
-index 9fca887..f1d615c 100644
+index 9fca8879..f1d615c0 100644
 --- a/mt7925/pci_mac.c
 +++ b/mt7925/pci_mac.c
 @@ -27,7 +27,7 @@ int mt7925e_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
@@ -141,10 +141,10 @@
  		return id;
  
 diff --git a/mt7996/init.c b/mt7996/init.c
-index 9aa97e4..7549a10 100644
+index 283df84f..d191a7b7 100644
 --- a/mt7996/init.c
 +++ b/mt7996/init.c
-@@ -634,6 +634,8 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+@@ -635,6 +635,8 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
  		mtk_wed_device_start(&dev->mt76.mmio.wed_hif2, irq_mask);
  	}
  
@@ -171,7 +171,7 @@
  	if (ret)
  		goto error;
 diff --git a/mt7996/mac.c b/mt7996/mac.c
-index bc7111a..aa19120 100644
+index bc7111a7..aa19120b 100644
 --- a/mt7996/mac.c
 +++ b/mt7996/mac.c
 @@ -922,6 +922,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
@@ -192,7 +192,7 @@
  		return id;
  
 diff --git a/tx.c b/tx.c
-index ab42f69..0fdf7d8 100644
+index ab42f69b..0fdf7d83 100644
 --- a/tx.c
 +++ b/tx.c
 @@ -825,16 +825,30 @@ void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked)
diff --git a/recipes-wifi/linux-mt76/files/patches/0014-wifi-mt76-mt7915-update-power-on-sequence.patch b/recipes-wifi/linux-mt76/files/patches/0014-wifi-mt76-mt7915-update-power-on-sequence.patch
index d12aa99..5f53feb 100644
--- a/recipes-wifi/linux-mt76/files/patches/0014-wifi-mt76-mt7915-update-power-on-sequence.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0014-wifi-mt76-mt7915-update-power-on-sequence.patch
@@ -1,4 +1,4 @@
-From 24535c7fc954e8122a792a28bd7c8fd77887c4b8 Mon Sep 17 00:00:00 2001
+From 55dd46f8caed3d8baa6819d884a1e82c496083f5 Mon Sep 17 00:00:00 2001
 From: Peter Chiu <chui-hao.chiu@mediatek.com>
 Date: Thu, 14 Mar 2024 17:55:12 +0800
 Subject: [PATCH 14/21] wifi: mt76: mt7915: update power on sequence
@@ -13,7 +13,7 @@
  3 files changed, 48 insertions(+), 2 deletions(-)
 
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 89156f3..74cd8ca 100644
+index 89156f35..74cd8caf 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -329,6 +329,7 @@ struct mt7915_dev {
@@ -25,7 +25,7 @@
  
  enum {
 diff --git a/mt7915/regs.h b/mt7915/regs.h
-index 7515b23..3452a7e 100644
+index 7515b23f..3452a7e9 100644
 --- a/mt7915/regs.h
 +++ b/mt7915/regs.h
 @@ -775,6 +775,7 @@ enum offs_rev {
@@ -45,7 +45,7 @@
  /* ADIE */
  #define MT_ADIE_CHIP_ID			0x02c
 diff --git a/mt7915/soc.c b/mt7915/soc.c
-index 92d8d71..bb3468a 100644
+index 92d8d710..bb3468a9 100644
 --- a/mt7915/soc.c
 +++ b/mt7915/soc.c
 @@ -260,6 +260,7 @@ static int mt7986_wmac_consys_lockup(struct mt7915_dev *dev, bool enable)
diff --git a/recipes-wifi/linux-mt76/files/patches/0015-wifi-mt76-mt7915-add-support-for-IEEE-802.11-fragmen.patch b/recipes-wifi/linux-mt76/files/patches/0015-wifi-mt76-mt7915-add-support-for-IEEE-802.11-fragmen.patch
index 067716d..c28e51e 100644
--- a/recipes-wifi/linux-mt76/files/patches/0015-wifi-mt76-mt7915-add-support-for-IEEE-802.11-fragmen.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0015-wifi-mt76-mt7915-add-support-for-IEEE-802.11-fragmen.patch
@@ -1,4 +1,4 @@
-From 9be5cee24e12846873cc8b2f7b3166043f47a8dc Mon Sep 17 00:00:00 2001
+From 22225104ac30af79661bf46c04e8c9523c2d22fd Mon Sep 17 00:00:00 2001
 From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
 Date: Wed, 3 Apr 2024 14:05:59 +0800
 Subject: [PATCH 15/21] wifi: mt76: mt7915: add support for IEEE 802.11
@@ -13,7 +13,7 @@
  2 files changed, 16 insertions(+), 1 deletion(-)
 
 diff --git a/mt76_connac2_mac.h b/mt76_connac2_mac.h
-index 5f13211..eb47653 100644
+index 5f132115..eb476536 100644
 --- a/mt76_connac2_mac.h
 +++ b/mt76_connac2_mac.h
 @@ -355,4 +355,11 @@ enum tx_port_idx {
@@ -29,7 +29,7 @@
 +
  #endif /* __MT76_CONNAC2_MAC_H */
 diff --git a/mt76_connac_mac.c b/mt76_connac_mac.c
-index a92c261..170ef36 100644
+index a92c261d..170ef367 100644
 --- a/mt76_connac_mac.c
 +++ b/mt76_connac_mac.c
 @@ -391,6 +391,7 @@ mt76_connac2_mac_write_txwi_80211(struct mt76_dev *dev, __le32 *txwi,
diff --git a/recipes-wifi/linux-mt76/files/patches/0016-wifi-mt76-mt7915-add-dummy-HW-offload-of-IEEE-802.11.patch b/recipes-wifi/linux-mt76/files/patches/0016-wifi-mt76-mt7915-add-dummy-HW-offload-of-IEEE-802.11.patch
index 78d9d87..d1a11c4 100644
--- a/recipes-wifi/linux-mt76/files/patches/0016-wifi-mt76-mt7915-add-dummy-HW-offload-of-IEEE-802.11.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0016-wifi-mt76-mt7915-add-dummy-HW-offload-of-IEEE-802.11.patch
@@ -1,4 +1,4 @@
-From b8a7bf6451dd612803743b69209672186bb09f6f Mon Sep 17 00:00:00 2001
+From 9db10864ac1dbdc802e563fc6d8752c8b275c302 Mon Sep 17 00:00:00 2001
 From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
 Date: Wed, 17 Apr 2024 10:47:08 +0800
 Subject: [PATCH 16/21] wifi: mt76: mt7915: add dummy HW offload of IEEE 802.11
@@ -14,10 +14,10 @@
  2 files changed, 8 insertions(+)
 
 diff --git a/mt7915/init.c b/mt7915/init.c
-index 3ec9eab..19a68c5 100644
+index ea9bc735..470b198a 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
-@@ -398,6 +398,7 @@ mt7915_init_wiphy(struct mt7915_phy *phy)
+@@ -399,6 +399,7 @@ mt7915_init_wiphy(struct mt7915_phy *phy)
  	ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD);
  	ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
  	ieee80211_hw_set(hw, WANT_MONITOR_VIF);
@@ -26,7 +26,7 @@
  	hw->max_tx_fragments = 4;
  
 diff --git a/mt7915/main.c b/mt7915/main.c
-index 9eeca39..65a3ce0 100644
+index f0491d66..f4673c8d 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -1614,6 +1614,12 @@ mt7915_twt_teardown_request(struct ieee80211_hw *hw,
@@ -42,7 +42,7 @@
  static int
  mt7915_set_radar_background(struct ieee80211_hw *hw,
  			    struct cfg80211_chan_def *chandef)
-@@ -1741,6 +1747,7 @@ const struct ieee80211_ops mt7915_ops = {
+@@ -1745,6 +1751,7 @@ const struct ieee80211_ops mt7915_ops = {
  	.sta_set_decap_offload = mt7915_sta_set_decap_offload,
  	.add_twt_setup = mt7915_mac_add_twt_setup,
  	.twt_teardown_request = mt7915_twt_teardown_request,
diff --git a/recipes-wifi/linux-mt76/files/patches/0017-wifi-mt76-mt7915-fix-rx-filter-setting-for-bfee-func.patch b/recipes-wifi/linux-mt76/files/patches/0017-wifi-mt76-mt7915-fix-rx-filter-setting-for-bfee-func.patch
index d4ad3a7..706b107 100644
--- a/recipes-wifi/linux-mt76/files/patches/0017-wifi-mt76-mt7915-fix-rx-filter-setting-for-bfee-func.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0017-wifi-mt76-mt7915-fix-rx-filter-setting-for-bfee-func.patch
@@ -1,4 +1,4 @@
-From f7a49dfa1f56157ea7d0d433fa6a06b12a98f46b Mon Sep 17 00:00:00 2001
+From d0d40f91383444dce590d39157d05f4922102db3 Mon Sep 17 00:00:00 2001
 From: Howard Hsu <howard-yh.hsu@mediatek.com>
 Date: Fri, 12 Apr 2024 11:33:08 +0800
 Subject: [PATCH 17/21] wifi: mt76: mt7915: fix rx filter setting for bfee
@@ -14,7 +14,7 @@
  1 file changed, 1 insertion(+), 2 deletions(-)
 
 diff --git a/mt7915/main.c b/mt7915/main.c
-index 65a3ce0..5ed84bc 100644
+index f4673c8d..f40a9007 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -564,8 +564,7 @@ static void mt7915_configure_filter(struct ieee80211_hw *hw,
diff --git a/recipes-wifi/linux-mt76/files/patches/0018-wifi-mt76-mt7915-fix-inconsistent-QoS-mapping-betwee.patch b/recipes-wifi/linux-mt76/files/patches/0018-wifi-mt76-mt7915-fix-inconsistent-QoS-mapping-betwee.patch
index c871c60..6eed8bd 100644
--- a/recipes-wifi/linux-mt76/files/patches/0018-wifi-mt76-mt7915-fix-inconsistent-QoS-mapping-betwee.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0018-wifi-mt76-mt7915-fix-inconsistent-QoS-mapping-betwee.patch
@@ -1,4 +1,4 @@
-From 5fd6278fa4d62c140f40fe2d7ae0bd86074b2d36 Mon Sep 17 00:00:00 2001
+From 6e6fb69bc4f57d622fae76d8d5a3102b8e98e10f Mon Sep 17 00:00:00 2001
 From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
 Date: Thu, 25 Apr 2024 17:17:13 +0800
 Subject: [PATCH] wifi: mt76: mt7915: fix inconsistent QoS mapping between SW
@@ -10,11 +10,11 @@
 Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
 ---
  mt76_connac_mcu.h |  1 +
- mt7915/main.c     | 44 ++++++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 45 insertions(+)
+ mt7915/main.c     | 51 +++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 52 insertions(+)
 
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 8899eea..5c25f1a 100644
+index 46dcd1c..e0255a2 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
 @@ -1238,6 +1238,7 @@ enum {
@@ -26,15 +26,15 @@
  
  enum {
 diff --git a/mt7915/main.c b/mt7915/main.c
-index 5ed84bc..be11e4f 100644
+index f40a900..f82d0b1 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
-@@ -1619,6 +1619,49 @@ mt7915_set_frag_threshold(struct ieee80211_hw *hw, u32 val)
+@@ -1619,6 +1619,56 @@ mt7915_set_frag_threshold(struct ieee80211_hw *hw, u32 val)
  	return 0;
  }
  
 +static int
-+mt7915_set_qos_map(struct ieee80211_vif *vif, struct cfg80211_qos_map *qos_map)
++mt7915_set_qos_map(struct ieee80211_vif *vif, struct cfg80211_qos_map *usr_qos_map)
 +{
 +#define IP_DSCP_NUM	64
 +	struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
@@ -45,18 +45,25 @@
 +		s8 qos_map[IP_DSCP_NUM];
 +	} __packed req = {
 +		.bss_idx = mvif->mt76.idx,
-+		.qos_map_enable = qos_map ? true : false,
++		.qos_map_enable = usr_qos_map ? true : false,
 +	};
 +
 +	/* Prevent access to members of mt7915_vif before its initialization. */
 +	if (!mvif->phy)
 +		return -EPERM;
 +
-+	if (qos_map) {
-+		struct cfg80211_dscp_exception *exception = qos_map->dscp_exception;
-+		struct cfg80211_dscp_range *range = qos_map->up;
++	if (usr_qos_map) {
++		struct cfg80211_dscp_exception *exception = usr_qos_map->dscp_exception;
++		struct cfg80211_dscp_range *range = usr_qos_map->up;
 +		s8 i;
 +
++		/* Default QoS map, defined in section 2.3 of RFC8325.
++		 * Three most significant bits of DSCP are used as UP.
++		 */
++		for (i = 0; i < IP_DSCP_NUM; ++i)
++			req.qos_map[i] = i >> 3;
++
++		/* User-defined QoS map */
 +		for (i = 0; i < IEEE80211_NUM_UPS; ++i) {
 +			u8 low = range[i].low, high = range[i].high;
 +
@@ -64,7 +71,7 @@
 +				memset(req.qos_map + low, i, high - low + 1);
 +		}
 +
-+		for (i = 0; i < qos_map->num_des; ++i) {
++		for (i = 0; i < usr_qos_map->num_des; ++i) {
 +			u8 dscp = exception[i].dscp, up = exception[i].up;
 +
 +			if (dscp < IP_DSCP_NUM && up < IEEE80211_NUM_UPS)
@@ -79,7 +86,7 @@
  static int
  mt7915_set_radar_background(struct ieee80211_hw *hw,
  			    struct cfg80211_chan_def *chandef)
-@@ -1747,6 +1790,7 @@ const struct ieee80211_ops mt7915_ops = {
+@@ -1751,6 +1801,7 @@ const struct ieee80211_ops mt7915_ops = {
  	.add_twt_setup = mt7915_mac_add_twt_setup,
  	.twt_teardown_request = mt7915_twt_teardown_request,
  	.set_frag_threshold = mt7915_set_frag_threshold,
@@ -88,5 +95,5 @@
  	CFG80211_TESTMODE_DUMP(mt76_testmode_dump)
  #ifdef CONFIG_MAC80211_DEBUGFS
 -- 
-2.18.0
+2.45.2
 
diff --git a/recipes-wifi/linux-mt76/files/patches/0019-wifi-mt76-mt7915-adjust-rx-filter.patch b/recipes-wifi/linux-mt76/files/patches/0019-wifi-mt76-mt7915-adjust-rx-filter.patch
index beeeaf9..aebc44f 100644
--- a/recipes-wifi/linux-mt76/files/patches/0019-wifi-mt76-mt7915-adjust-rx-filter.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0019-wifi-mt76-mt7915-adjust-rx-filter.patch
@@ -1,4 +1,4 @@
-From 304c98810a9e68eccae433c570d1fc7c1063a641 Mon Sep 17 00:00:00 2001
+From d189b10ff8b7cd3d6fdabde17e8476bfe66466cc Mon Sep 17 00:00:00 2001
 From: Howard Hsu <howard-yh.hsu@mediatek.com>
 Date: Fri, 19 Apr 2024 15:43:23 +0800
 Subject: [PATCH 19/21] wifi: mt76: mt7915: adjust rx filter
@@ -13,7 +13,7 @@
  1 file changed, 7 insertions(+), 4 deletions(-)
 
 diff --git a/mt7915/main.c b/mt7915/main.c
-index c6880ca..3783849 100644
+index 3a8b9404..0d2614e1 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -489,7 +489,8 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed)
diff --git a/recipes-wifi/linux-mt76/files/patches/0020-wifi-mt76-mt7915-add-additional-chain-signal-info-to.patch b/recipes-wifi/linux-mt76/files/patches/0020-wifi-mt76-mt7915-add-additional-chain-signal-info-to.patch
index 76573f5..7b3b464 100644
--- a/recipes-wifi/linux-mt76/files/patches/0020-wifi-mt76-mt7915-add-additional-chain-signal-info-to.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0020-wifi-mt76-mt7915-add-additional-chain-signal-info-to.patch
@@ -1,4 +1,4 @@
-From 6fb263d127c9ef802b4256e95ccea2d2faa047b4 Mon Sep 17 00:00:00 2001
+From cab458a1d2d91784aa28bacfd0b6649fd5c3f740 Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Wed, 20 Sep 2023 11:10:57 +0800
 Subject: [PATCH 20/21] wifi: mt76: mt7915: add additional chain signal info to
@@ -10,7 +10,7 @@
  1 file changed, 1 insertion(+), 1 deletion(-)
 
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index 4604a68..95c794a 100644
+index 4604a682..95c794a3 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -437,7 +437,7 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb,
diff --git a/recipes-wifi/linux-mt76/files/patches/0021-wifi-mt76-mt7915-remove-unnecessary-register-setting.patch b/recipes-wifi/linux-mt76/files/patches/0021-wifi-mt76-mt7915-remove-unnecessary-register-setting.patch
index 1f9d540..b318b8c 100644
--- a/recipes-wifi/linux-mt76/files/patches/0021-wifi-mt76-mt7915-remove-unnecessary-register-setting.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0021-wifi-mt76-mt7915-remove-unnecessary-register-setting.patch
@@ -1,7 +1,8 @@
-From 1af0fe3345f26594c78e91681997ad3a1838fc7c Mon Sep 17 00:00:00 2001
+From 9976288a87664bacf514902869be38215da1d8b1 Mon Sep 17 00:00:00 2001
 From: Henry Yen <henry.yen@mediatek.com>
 Date: Wed, 6 Mar 2024 12:42:06 +0800
-Subject: [PATCH] wifi: mt76: mt7915: remove unnecessary register settings
+Subject: [PATCH 21/21] wifi: mt76: mt7915: remove unnecessary register
+ settings
 
 Remove unnecessary register settings from the driver layer,
 and let firmware take over the configuration control.
@@ -13,10 +14,10 @@
  2 files changed, 1 insertion(+), 72 deletions(-)
 
 diff --git a/mt7915/init.c b/mt7915/init.c
-index 19a68c5..15ae27a 100644
+index 470b198a..84c69a88 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
-@@ -475,30 +475,6 @@ mt7915_mac_init_band(struct mt7915_dev *dev, u8 band)
+@@ -476,30 +476,6 @@ mt7915_mac_init_band(struct mt7915_dev *dev, u8 band)
  {
  	u32 mask, set;
  
@@ -47,7 +48,7 @@
  	/* mt7915: disable rx rate report by default due to hw issues */
  	mt76_clear(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN);
  
-@@ -601,23 +577,17 @@ mt7915_init_led_mux(struct mt7915_dev *dev)
+@@ -602,23 +578,17 @@ mt7915_init_led_mux(struct mt7915_dev *dev)
  void mt7915_mac_init(struct mt7915_dev *dev)
  {
  	int i;
@@ -72,7 +73,7 @@
  		mt7915_mac_wtbl_update(dev, i,
  				       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index 95c794a..1c5ab41 100644
+index 95c794a3..1c5ab41f 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -1149,61 +1149,20 @@ void mt7915_mac_reset_counters(struct mt7915_phy *phy)
diff --git a/recipes-wifi/linux-mt76/files/patches/0022-wifi-mt76-mt7915-add-PID-to-only-report-data-frame-T.patch b/recipes-wifi/linux-mt76/files/patches/0022-wifi-mt76-mt7915-add-PID-to-only-report-data-frame-T.patch
new file mode 100644
index 0000000..a7bd453
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches/0022-wifi-mt76-mt7915-add-PID-to-only-report-data-frame-T.patch
@@ -0,0 +1,115 @@
+From 15d44f1c050949ec23da85167bef1163b4a8ddf2 Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Fri, 2 Aug 2024 14:22:45 +0800
+Subject: [PATCH] wifi: mt76: mt7915: add PID to only report data-frame TX rate
+
+According to definition of NL80211_STA_INFO_RX_BITRATE, mac80211 implicitly only expects data-frame rates to be reported.
+In MT76, TX rate is updated using TXS, and PID in TXS is possibly useful for distinguishing between data and management frames.
+Originally, MT_PACKET_ID_NO_SKB was used for data frames (when WED is disabled) and some management frames.
+In order to differentiate between them, MT_PACKET_ID_SW_DATA is added.
+And TX rate is updated only when TXS is associated with either SW-path or HW-path data frame.
+
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ mt76.h            | 3 ++-
+ mt76_connac.h     | 2 +-
+ mt76_connac_mac.c | 7 +++++--
+ mt7915/mac.c      | 2 +-
+ tx.c              | 9 ++++-----
+ 5 files changed, 13 insertions(+), 10 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index 05ee568..bdc9a9b 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -443,7 +443,8 @@ struct mt76_rx_tid {
+ #define MT_PACKET_ID_NO_ACK		0
+ #define MT_PACKET_ID_NO_SKB		1
+ #define MT_PACKET_ID_WED		2
+-#define MT_PACKET_ID_FIRST		3
++#define MT_PACKET_ID_SW_DATA		3
++#define MT_PACKET_ID_FIRST		4
+ #define MT_PACKET_ID_HAS_RATE		BIT(7)
+ /* This is timer for when to give up when waiting for TXS callback,
+  * with starting time being the time at which the DMA_DONE callback
+diff --git a/mt76_connac.h b/mt76_connac.h
+index 5028e49..355b506 100644
+--- a/mt76_connac.h
++++ b/mt76_connac.h
+@@ -436,7 +436,7 @@ u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy,
+ 				 struct ieee80211_vif *vif,
+ 				 bool beacon, bool mcast);
+ bool mt76_connac2_mac_fill_txs(struct mt76_dev *dev, struct mt76_wcid *wcid,
+-			       __le32 *txs_data);
++			       int pid, __le32 *txs_data);
+ bool mt76_connac2_mac_add_txs_skb(struct mt76_dev *dev, struct mt76_wcid *wcid,
+ 				  int pid, __le32 *txs_data);
+ void mt76_connac2_mac_decode_he_radiotap(struct mt76_dev *dev,
+diff --git a/mt76_connac_mac.c b/mt76_connac_mac.c
+index 170ef36..4d24cc6 100644
+--- a/mt76_connac_mac.c
++++ b/mt76_connac_mac.c
+@@ -594,7 +594,7 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
+ EXPORT_SYMBOL_GPL(mt76_connac2_mac_write_txwi);
+ 
+ bool mt76_connac2_mac_fill_txs(struct mt76_dev *dev, struct mt76_wcid *wcid,
+-			       __le32 *txs_data)
++			       int pid, __le32 *txs_data)
+ {
+ 	struct mt76_sta_stats *stats = &wcid->stats;
+ 	struct ieee80211_supported_band *sband;
+@@ -628,6 +628,9 @@ bool mt76_connac2_mac_fill_txs(struct mt76_dev *dev, struct mt76_wcid *wcid,
+ 		}
+ 	}
+ 
++	if (pid != MT_PACKET_ID_WED && pid != MT_PACKET_ID_SW_DATA)
++		return true;
++
+ 	txrate = FIELD_GET(MT_TXS0_TX_RATE, txs);
+ 
+ 	rate.mcs = FIELD_GET(MT_TX_RATE_IDX, txrate);
+@@ -737,7 +740,7 @@ bool mt76_connac2_mac_add_txs_skb(struct mt76_dev *dev, struct mt76_wcid *wcid,
+ 			!!(info->flags & IEEE80211_TX_STAT_ACK);
+ 		info->status.rates[0].idx = -1;
+ 
+-		mt76_connac2_mac_fill_txs(dev, wcid, txs_data);
++		mt76_connac2_mac_fill_txs(dev, wcid, pid, txs_data);
+ 		mt76_tx_status_skb_done(dev, skb, &list);
+ 	}
+ 	mt76_tx_status_unlock(dev, &list);
+diff --git a/mt7915/mac.c b/mt7915/mac.c
+index 1c5ab41..9c56f10 100644
+--- a/mt7915/mac.c
++++ b/mt7915/mac.c
+@@ -1023,7 +1023,7 @@ static void mt7915_mac_add_txs(struct mt7915_dev *dev, void *data)
+ 	msta = container_of(wcid, struct mt7915_sta, wcid);
+ 
+ 	if (le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT) == MT_TXS_PPDU_FMT)
+-		mt76_connac2_mac_fill_txs(&dev->mt76, wcid, txs_data);
++		mt76_connac2_mac_fill_txs(&dev->mt76, wcid, pid, txs_data);
+ 	else
+ 		mt76_connac2_mac_add_txs_skb(&dev->mt76, wcid, pid, txs_data);
+ 
+diff --git a/tx.c b/tx.c
+index 0fdf7d8..d6054ab 100644
+--- a/tx.c
++++ b/tx.c
+@@ -136,11 +136,10 @@ mt76_tx_status_skb_add(struct mt76_dev *dev, struct mt76_wcid *wcid,
+ 
+ 	if (!(info->flags & (IEEE80211_TX_CTL_REQ_TX_STATUS |
+ 			     IEEE80211_TX_CTL_RATE_CTRL_PROBE))) {
+-		if (mtk_wed_device_active(&dev->mmio.wed) &&
+-		    ((info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) ||
+-		     ieee80211_is_data(hdr->frame_control)))
+-			return MT_PACKET_ID_WED;
+-
++		if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP ||
++		    ieee80211_is_data(hdr->frame_control))
++			return mtk_wed_device_active(&dev->mmio.wed) ? MT_PACKET_ID_WED
++								     : MT_PACKET_ID_SW_DATA;
+ 		return MT_PACKET_ID_NO_SKB;
+ 	}
+ 
+-- 
+2.45.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches/0999-wifi-mt76-mt7915-build-pass-for-Linux-Kernel-5.4-fix.patch b/recipes-wifi/linux-mt76/files/patches/0999-wifi-mt76-mt7915-build-pass-for-Linux-Kernel-5.4-fix.patch
index 53a0fc9..1f9477c 100644
--- a/recipes-wifi/linux-mt76/files/patches/0999-wifi-mt76-mt7915-build-pass-for-Linux-Kernel-5.4-fix.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0999-wifi-mt76-mt7915-build-pass-for-Linux-Kernel-5.4-fix.patch
@@ -1,7 +1,7 @@
-From e3da91079dacc07f8e011f4b469e70bd78a93636 Mon Sep 17 00:00:00 2001
+From 4cdf382808962e209db2575b74d5fde2d2e7ac72 Mon Sep 17 00:00:00 2001
 From: Evelyn Tsai <evelyn.tsai@mediatek.com>
 Date: Sat, 1 Apr 2023 08:18:17 +0800
-Subject: [PATCH 0999/1051] wifi: mt76: mt7915: build pass for Linux Kernel 5.4
+Subject: [PATCH 0999/1052] wifi: mt76: mt7915: build pass for Linux Kernel 5.4
  fixes
 
 ---
@@ -23,7 +23,7 @@
  15 files changed, 123 insertions(+), 232 deletions(-)
 
 diff --git a/debugfs.c b/debugfs.c
-index c4649ba..1c8328d 100644
+index c4649ba0..1c8328d5 100644
 --- a/debugfs.c
 +++ b/debugfs.c
 @@ -33,8 +33,10 @@ mt76_napi_threaded_set(void *data, u64 val)
@@ -38,7 +38,7 @@
  	return 0;
  }
 diff --git a/dma.c b/dma.c
-index f4f88c4..ccdd564 100644
+index f4f88c44..ccdd5646 100644
 --- a/dma.c
 +++ b/dma.c
 @@ -178,7 +178,7 @@ mt76_free_pending_rxwi(struct mt76_dev *dev)
@@ -240,7 +240,7 @@
  
  	if (mtk_wed_device_active(&dev->mmio.wed))
 diff --git a/dma.h b/dma.h
-index 1de5a2b..619dc0f 100644
+index 1de5a2b2..619dc0fe 100644
 --- a/dma.h
 +++ b/dma.h
 @@ -79,8 +79,7 @@ enum mt76_dma_wed_ind_reason {
@@ -254,7 +254,7 @@
  			    bool reset_idx);
  void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q);
 diff --git a/eeprom.c b/eeprom.c
-index ecd09c0..a267397 100644
+index ecd09c03..a2673978 100644
 --- a/eeprom.c
 +++ b/eeprom.c
 @@ -163,9 +163,15 @@ void
@@ -275,10 +275,10 @@
  	if (!is_valid_ether_addr(phy->macaddr)) {
  		eth_random_addr(phy->macaddr);
 diff --git a/mac80211.c b/mac80211.c
-index bc20f60..b30a74e 100644
+index aee6f1e7..4d50bfae 100644
 --- a/mac80211.c
 +++ b/mac80211.c
-@@ -577,47 +577,6 @@ void mt76_unregister_phy(struct mt76_phy *phy)
+@@ -578,47 +578,6 @@ void mt76_unregister_phy(struct mt76_phy *phy)
  }
  EXPORT_SYMBOL_GPL(mt76_unregister_phy);
  
@@ -326,7 +326,7 @@
  struct mt76_dev *
  mt76_alloc_device(struct device *pdev, unsigned int size,
  		  const struct ieee80211_ops *ops,
-@@ -1817,21 +1776,6 @@ void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi,
+@@ -1818,21 +1777,6 @@ void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi,
  }
  EXPORT_SYMBOL_GPL(mt76_ethtool_worker);
  
@@ -349,7 +349,7 @@
  {
  	struct ieee80211_hw *hw = phy->hw;
 diff --git a/mcu.c b/mcu.c
-index a8cafa3..fa4b054 100644
+index a8cafa39..fa4b0544 100644
 --- a/mcu.c
 +++ b/mcu.c
 @@ -4,6 +4,7 @@
@@ -361,7 +361,7 @@
  struct sk_buff *
  __mt76_mcu_msg_alloc(struct mt76_dev *dev, const void *data,
 diff --git a/mt76.h b/mt76.h
-index b83456b..a07c7df 100644
+index 05ee568c..063fc364 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -15,11 +15,6 @@
@@ -393,7 +393,7 @@
  void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi,
  			 struct mt76_sta_stats *stats, bool eht);
  int mt76_skb_adjust_pad(struct sk_buff *skb, int pad);
-@@ -1673,25 +1667,6 @@ void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked);
+@@ -1685,25 +1679,6 @@ void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked);
  struct mt76_txwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token);
  int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
  			  struct mt76_txwi_cache *r, dma_addr_t phys);
@@ -420,7 +420,7 @@
  static inline void mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked)
  {
 diff --git a/mt7615/mcu.c b/mt7615/mcu.c
-index ae34d01..c9444c6 100644
+index c807bd8d..a9310660 100644
 --- a/mt7615/mcu.c
 +++ b/mt7615/mcu.c
 @@ -10,6 +10,7 @@
@@ -432,7 +432,7 @@
  static bool prefer_offload_fw = true;
  module_param(prefer_offload_fw, bool, 0644);
 diff --git a/mt76_connac.h b/mt76_connac.h
-index 4871857..8e7068c 100644
+index 5028e49a..5356c52c 100644
 --- a/mt76_connac.h
 +++ b/mt76_connac.h
 @@ -56,7 +56,6 @@ enum {
@@ -452,7 +452,7 @@
  
  	if (chandef->width >= ARRAY_SIZE(width_to_bw))
 diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
-index 1ea9798..a8f097d 100644
+index 4baaaacf..4e84f8d2 100644
 --- a/mt76_connac_mcu.c
 +++ b/mt76_connac_mcu.c
 @@ -4,6 +4,7 @@
@@ -524,10 +524,10 @@
  #define DEFAULT_HE_DURATION_RTS_THRES	1023
  static void
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 0936c1c..99cdd1b 100644
+index e0255a23..abc57f18 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
-@@ -1985,12 +1985,8 @@ void mt76_connac_mcu_reg_wr(struct mt76_dev *dev, u32 offset, u32 val);
+@@ -1992,12 +1992,8 @@ void mt76_connac_mcu_reg_wr(struct mt76_dev *dev, u32 offset, u32 val);
  
  const struct ieee80211_sta_he_cap *
  mt76_connac_get_he_phy_cap(struct mt76_phy *phy, struct ieee80211_vif *vif);
@@ -541,7 +541,7 @@
  int mt76_connac_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
  			    struct mt76_connac_sta_key_conf *sta_key_conf,
 diff --git a/mt7915/main.c b/mt7915/main.c
-index 3783849..1da1d32 100644
+index 0d2614e1..a3facc9a 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -1440,22 +1440,20 @@ void mt7915_get_et_strings(struct ieee80211_hw *hw,
@@ -599,7 +599,7 @@
  
  static void
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 64d2710..91a1031 100644
+index 9126e62f..34323577 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -6,6 +6,7 @@
@@ -611,10 +611,10 @@
  #define fw_name(_dev, name, ...)	({			\
  	char *_fw;						\
 diff --git a/usb.c b/usb.c
-index dc690d1..058f2d1 100644
+index 58ff0682..0ca3b069 100644
 --- a/usb.c
 +++ b/usb.c
-@@ -319,27 +319,29 @@ mt76u_set_endpoints(struct usb_interface *intf,
+@@ -318,27 +318,29 @@ mt76u_set_endpoints(struct usb_interface *intf,
  
  static int
  mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76_queue *q, struct urb *urb,
@@ -649,7 +649,7 @@
  		urb->num_sgs = i;
  	}
  
-@@ -352,16 +354,15 @@ mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76_queue *q, struct urb *urb,
+@@ -351,16 +353,15 @@ mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76_queue *q, struct urb *urb,
  
  static int
  mt76u_refill_rx(struct mt76_dev *dev, struct mt76_queue *q,
@@ -669,7 +669,7 @@
  
  	return urb->transfer_buffer ? 0 : -ENOMEM;
  }
-@@ -399,7 +400,7 @@ mt76u_rx_urb_alloc(struct mt76_dev *dev, struct mt76_queue *q,
+@@ -398,7 +399,7 @@ mt76u_rx_urb_alloc(struct mt76_dev *dev, struct mt76_queue *q,
  	if (err)
  		return err;
  
@@ -678,7 +678,7 @@
  }
  
  static void mt76u_urb_free(struct urb *urb)
-@@ -407,10 +408,10 @@ static void mt76u_urb_free(struct urb *urb)
+@@ -406,10 +407,10 @@ static void mt76u_urb_free(struct urb *urb)
  	int i;
  
  	for (i = 0; i < urb->num_sgs; i++)
@@ -691,7 +691,7 @@
  
  	usb_free_urb(urb);
  }
-@@ -546,8 +547,6 @@ mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb,
+@@ -545,8 +546,6 @@ mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb,
  		len -= data_len;
  		nsgs++;
  	}
@@ -700,7 +700,7 @@
  	dev->drv->rx_skb(dev, MT_RXQ_MAIN, skb, NULL);
  
  	return nsgs;
-@@ -613,7 +612,7 @@ mt76u_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
+@@ -612,7 +611,7 @@ mt76u_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
  
  		count = mt76u_process_rx_entry(dev, urb, q->buf_size);
  		if (count > 0) {
@@ -709,7 +709,7 @@
  			if (err < 0)
  				break;
  		}
-@@ -664,10 +663,6 @@ mt76u_alloc_rx_queue(struct mt76_dev *dev, enum mt76_rxq_id qid)
+@@ -663,10 +662,6 @@ mt76u_alloc_rx_queue(struct mt76_dev *dev, enum mt76_rxq_id qid)
  	struct mt76_queue *q = &dev->q_rx[qid];
  	int i, err;
  
@@ -720,7 +720,7 @@
  	spin_lock_init(&q->lock);
  	q->entry = devm_kcalloc(dev->dev,
  				MT_NUM_RX_ENTRIES, sizeof(*q->entry),
-@@ -696,6 +691,7 @@ EXPORT_SYMBOL_GPL(mt76u_alloc_mcu_queue);
+@@ -695,6 +690,7 @@ EXPORT_SYMBOL_GPL(mt76u_alloc_mcu_queue);
  static void
  mt76u_free_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
  {
@@ -728,7 +728,7 @@
  	int i;
  
  	for (i = 0; i < q->ndesc; i++) {
-@@ -705,8 +701,13 @@ mt76u_free_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
+@@ -704,8 +700,13 @@ mt76u_free_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
  		mt76u_urb_free(q->entry[i].urb);
  		q->entry[i].urb = NULL;
  	}
@@ -745,7 +745,7 @@
  
  static void mt76u_free_rx(struct mt76_dev *dev)
 diff --git a/wed.c b/wed.c
-index f89e453..f7a3f1b 100644
+index f89e4537..f7a3f1b3 100644
 --- a/wed.c
 +++ b/wed.c
 @@ -9,8 +9,12 @@
diff --git a/recipes-wifi/linux-mt76/files/patches/1000-wifi-mt76-mt7915-add-mtk-internal-debug-tools-for-mt.patch b/recipes-wifi/linux-mt76/files/patches/1000-wifi-mt76-mt7915-add-mtk-internal-debug-tools-for-mt.patch
index 646d0bf..2af1061 100644
--- a/recipes-wifi/linux-mt76/files/patches/1000-wifi-mt76-mt7915-add-mtk-internal-debug-tools-for-mt.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1000-wifi-mt76-mt7915-add-mtk-internal-debug-tools-for-mt.patch
@@ -1,7 +1,8 @@
-From 57ca0074490c096cf61f5857ceb233f9a763cf82 Mon Sep 17 00:00:00 2001
+From 3f35505b3bbdfe9e381dbaa1c2450a7e7e461989 Mon Sep 17 00:00:00 2001
 From: Shayne Chen <shayne.chen@mediatek.com>
 Date: Wed, 22 Jun 2022 10:39:47 +0800
-Subject: [PATCH] wifi: mt76: mt7915: add mtk internal debug tools for mt76
+Subject: [PATCH 01/70] wifi: mt76: mt7915: add mtk internal debug tools for
+ mt76
 
 ---
  mt76_connac_mcu.h     |    6 +
@@ -271,7 +272,7 @@
  }
  
 diff --git a/mt7915/main.c b/mt7915/main.c
-index 417002f..4d8cb1c 100644
+index b19b433..aa4b3ae 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -73,7 +73,11 @@ int mt7915_run(struct ieee80211_hw *hw)
@@ -481,7 +482,7 @@
  #endif
 diff --git a/mt7915/mt7915_debug.h b/mt7915/mt7915_debug.h
 new file mode 100644
-index 0000000..1ec8de9
+index 0000000..2f9f3da
 --- /dev/null
 +++ b/mt7915/mt7915_debug.h
 @@ -0,0 +1,1442 @@
@@ -850,8 +851,8 @@
 +	[DBG_PSE_FREEPG_CNT]		= { DBG_INVALID_BASE, 0x380},
 +	[DBG_PSE_FREEPG_HEAD_TAIL]	= { DBG_INVALID_BASE, 0x384},
 +	[DBG_PSE_HIF0_PG_INFO]		= { DBG_INVALID_BASE, 0x150},
-+	[DBG_PSE_PG_HIF1_GROUP]		= { DBG_INVALID_BASE, 0x154},
-+	[DBG_PSE_HIF1_PG_INFO]		= { DBG_INVALID_BASE, 0x160},
++	[DBG_PSE_PG_HIF1_GROUP]		= { DBG_INVALID_BASE, 0x114},
++	[DBG_PSE_HIF1_PG_INFO]		= { DBG_INVALID_BASE, 0x154},
 +	[DBG_PSE_PG_CPU_GROUP]		= { DBG_INVALID_BASE, 0x118},
 +	[DBG_PSE_CPU_PG_INFO]		= { DBG_INVALID_BASE, 0x158},
 +	[DBG_PSE_PG_PLE_GROUP]		= { DBG_INVALID_BASE, 0x11c},
@@ -863,7 +864,7 @@
 +	[DBG_PSE_PG_LMAC2_GROUP]	= { DBG_INVALID_BASE, 0x12c},
 +	[DBG_PSE_LMAC2_PG_INFO]		= { DBG_INVALID_BASE, 0x16c},
 +	[DBG_PSE_PG_LMAC3_GROUP]	= { DBG_INVALID_BASE, 0x130},
-+	[DBG_PSE_LMAC3_PG_INFO]		= { DBG_INVALID_BASE, 0x17c},
++	[DBG_PSE_LMAC3_PG_INFO]		= { DBG_INVALID_BASE, 0x170},
 +	[DBG_PSE_PG_MDP_GROUP]		= { DBG_INVALID_BASE, 0x134},
 +	[DBG_PSE_MDP_PG_INFO]		= { DBG_INVALID_BASE, 0x174},
 +	[DBG_PSE_PG_PLE1_GROUP]		= { DBG_INVALID_BASE, 0x120},
@@ -1383,17 +1384,17 @@
 +#define MT_DBG_PSE_MDP_PG_INFO_MDP_SRC_CNT_MASK		GENMASK(27, 16)
 +#define MT_DBG_PSE_MDP_PG_INFO_MDP_RSV_CNT_MASK		GENMASK(11, 0)
 +
-+#define MT_DBG_PSE_FL_QUE_CTRL_0_ADDR			MT_DBG_PLE(0x1b0)
++#define MT_DBG_PSE_FL_QUE_CTRL_0_ADDR			MT_DBG_PSE(0x1b0)
 +#define MT_DBG_PSE_FL_QUE_CTRL_0_EXECUTE_MASK	   	BIT(31)
 +#define MT_DBG_PSE_FL_QUE_CTRL_0_Q_BUF_QID_SHFT         24
 +#define MT_DBG_PSE_FL_QUE_CTRL_0_Q_BUF_PID_SHFT         10
 +#define MT_DBG_PSE_FL_QUE_CTRL_0_Q_BUF_WLANID_MASK    	GENMASK(9, 0)
 +
-+#define MT_DBG_PSE_FL_QUE_CTRL_2_ADDR			MT_DBG_PLE(0x1b8)
++#define MT_DBG_PSE_FL_QUE_CTRL_2_ADDR			MT_DBG_PSE(0x1b8)
 +#define MT_DBG_PSE_FL_QUE_CTRL_2_QUEUE_TAIL_FID_MASK    GENMASK(27, 16)
 +#define MT_DBG_PSE_FL_QUE_CTRL_2_QUEUE_HEAD_FID_MASK    GENMASK(11, 0)
 +
-+#define MT_DBG_PSE_FL_QUE_CTRL_3_ADDR			MT_DBG_PLE(0x1bc)
++#define MT_DBG_PSE_FL_QUE_CTRL_3_ADDR			MT_DBG_PSE(0x1bc)
 +#define MT_DBG_PSE_FL_QUE_CTRL_3_QUEUE_PKT_NUM_MASK    	GENMASK(11, 0)
 +
 +
@@ -1929,7 +1930,7 @@
 +#endif
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
 new file mode 100644
-index 0000000..665d8bd
+index 0000000..06cedaa
 --- /dev/null
 +++ b/mt7915/mtk_debugfs.c
 @@ -0,0 +1,3750 @@
@@ -3746,8 +3747,8 @@
 +	rsv_pg = FIELD_GET(MT_DBG_PSE_LMAC1_PG_INFO_LMAC1_RSV_CNT_MASK, pg_flow_ctrl[11]);
 +	used_pg = FIELD_GET(MT_DBG_PSE_LMAC1_PG_INFO_LMAC1_SRC_CNT_MASK, pg_flow_ctrl[11]);
 +	seq_printf(s, "\t\tThe used/reserved pages of LMAC1 group=0x%03x/0x%03x\n", used_pg, rsv_pg);
-+	seq_printf(s, "\tReserved page counter of LMAC2 group(0x82068180): 0x%08x\n", pg_flow_ctrl[11]);
-+	seq_printf(s, "\tLMAC2 group page status(0x82068184): 0x%08x\n", pg_flow_ctrl[12]);
++	seq_printf(s, "\tReserved page counter of LMAC2 group(0x82068180): 0x%08x\n", pg_flow_ctrl[12]);
++	seq_printf(s, "\tLMAC2 group page status(0x82068184): 0x%08x\n", pg_flow_ctrl[13]);
 +	min_q = FIELD_GET(MT_DBG_PSE_PG_LMAC2_GROUP_LMAC2_MIN_QUOTA_MASK, pg_flow_ctrl[12]);
 +	max_q = FIELD_GET(MT_DBG_PSE_PG_LMAC2_GROUP_LMAC2_MAX_QUOTA_MASK, pg_flow_ctrl[12]);
 +	seq_printf(s, "\t\tThe max/min quota pages of LMAC2 group=0x%03x/0x%03x\n", max_q, min_q);
@@ -3773,8 +3774,8 @@
 +	used_pg = FIELD_GET(MT_DBG_PSE_PLE_PG_INFO_PLE_SRC_CNT_MASK, pg_flow_ctrl[15]);
 +	seq_printf(s, "\t\tThe used/reserved pages of PLE group=0x%03x/0x%03x\n", used_pg, rsv_pg);
 +
-+	seq_printf(s, "\tReserved page counter of PLE1 group(0x82068168): 0x%08x\n", pg_flow_ctrl[14]);
-+	seq_printf(s, "\tPLE1 group page status(0x8206816c): 0x%08x\n", pg_flow_ctrl[15]);
++	seq_printf(s, "\tReserved page counter of PLE1 group(0x82068168): 0x%08x\n", pg_flow_ctrl[20]);
++	seq_printf(s, "\tPLE1 group page status(0x8206816c): 0x%08x\n", pg_flow_ctrl[21]);
 +	min_q = FIELD_GET(MT_DBG_PSE_PG_PLE_GROUP_PLE_MIN_QUOTA_MASK, pg_flow_ctrl[20]);
 +	max_q = FIELD_GET(MT_DBG_PSE_PG_PLE_GROUP_PLE_MAX_QUOTA_MASK, pg_flow_ctrl[20]);
 +	seq_printf(s, "\t\tThe max/min quota pages of PLE1 group=0x%03x/0x%03x\n", max_q, min_q);
@@ -4859,7 +4860,7 @@
 +	struct mt76_dev *mdev = NULL;
 +	int i;
 +
-+	seq_printf(s, "Version: 2.2.24.5\n");
++	seq_printf(s, "Version: 2.2.24.7\n");
 +
 +	if (!test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state))
 +		return 0;
@@ -5873,5 +5874,5 @@
  	return ret;
  }
 -- 
-2.18.0
+2.45.2
 
diff --git a/recipes-wifi/linux-mt76/files/patches/1001-wifi-mt76-mt7915-csi-implement-csi-support.patch b/recipes-wifi/linux-mt76/files/patches/1001-wifi-mt76-mt7915-csi-implement-csi-support.patch
index 115bb84..1e84172 100644
--- a/recipes-wifi/linux-mt76/files/patches/1001-wifi-mt76-mt7915-csi-implement-csi-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1001-wifi-mt76-mt7915-csi-implement-csi-support.patch
@@ -1,7 +1,7 @@
-From c5e9e8836065c1651ab7b84a0d30953c45c7a35d Mon Sep 17 00:00:00 2001
+From 594116fbcff4d30156c7207244633ad3c1bf9ec1 Mon Sep 17 00:00:00 2001
 From: Bo Jiao <Bo.Jiao@mediatek.com>
 Date: Mon, 6 Jun 2022 20:13:02 +0800
-Subject: [PATCH] wifi: mt76: mt7915: csi: implement csi support
+Subject: [PATCH 1001/1052] wifi: mt76: mt7915: csi: implement csi support
 
 ---
  mt76_connac_mcu.h |   2 +
@@ -19,10 +19,10 @@
  create mode 100644 mt7915/vendor.h
 
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index a8690cd..cda7559 100644
+index 01d6598a..ad63596d 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
-@@ -1029,6 +1029,7 @@ enum {
+@@ -1030,6 +1030,7 @@ enum {
  	MCU_EXT_EVENT_WA_TX_STAT = 0x74,
  	MCU_EXT_EVENT_BCC_NOTIFY = 0x75,
  	MCU_EXT_EVENT_MURU_CTRL = 0x9f,
@@ -30,7 +30,7 @@
  };
  
  /* unified event table */
-@@ -1243,6 +1244,7 @@ enum {
+@@ -1245,6 +1246,7 @@ enum {
  	MCU_EXT_CMD_DPD_PRE_CAL_INFO = 0xac,
  	MCU_EXT_CMD_PHY_STAT_INFO = 0xad,
  	MCU_EXT_CMD_SET_QOS_MAP = 0xb4,
@@ -39,7 +39,7 @@
  
  enum {
 diff --git a/mt7915/Makefile b/mt7915/Makefile
-index fd71141..65129b4 100644
+index fd711416..65129b4f 100644
 --- a/mt7915/Makefile
 +++ b/mt7915/Makefile
 @@ -1,10 +1,10 @@
@@ -56,7 +56,7 @@
  mt7915e-$(CONFIG_NL80211_TESTMODE) += testmode.o
  mt7915e-$(CONFIG_MT798X_WMAC) += soc.o
 diff --git a/mt7915/debugfs.c b/mt7915/debugfs.c
-index 2661386..909df24 100644
+index 26613869..909df246 100644
 --- a/mt7915/debugfs.c
 +++ b/mt7915/debugfs.c
 @@ -1241,6 +1241,51 @@ mt7915_rf_regval_set(void *data, u64 val)
@@ -122,10 +122,10 @@
  	if (!ext_phy)
  		dev->debugfs_dir = dir;
 diff --git a/mt7915/init.c b/mt7915/init.c
-index 19a68c5..1200405 100644
+index 84c69a88..ac15bc53 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
-@@ -697,6 +697,14 @@ mt7915_register_ext_phy(struct mt7915_dev *dev, struct mt7915_phy *phy)
+@@ -668,6 +668,14 @@ mt7915_register_ext_phy(struct mt7915_dev *dev, struct mt7915_phy *phy)
  	/* init wiphy according to mphy and phy */
  	mt7915_init_wiphy(phy);
  
@@ -140,7 +140,7 @@
  	ret = mt76_register_phy(mphy, true, mt76_rates,
  				ARRAY_SIZE(mt76_rates));
  	if (ret)
-@@ -1178,6 +1186,28 @@ void mt7915_set_stream_he_caps(struct mt7915_phy *phy)
+@@ -1146,6 +1154,28 @@ void mt7915_set_stream_he_caps(struct mt7915_phy *phy)
  	}
  }
  
@@ -169,7 +169,7 @@
  static void mt7915_unregister_ext_phy(struct mt7915_dev *dev)
  {
  	struct mt7915_phy *phy = mt7915_ext_phy(dev);
-@@ -1186,6 +1216,10 @@ static void mt7915_unregister_ext_phy(struct mt7915_dev *dev)
+@@ -1154,6 +1184,10 @@ static void mt7915_unregister_ext_phy(struct mt7915_dev *dev)
  	if (!phy)
  		return;
  
@@ -180,7 +180,7 @@
  	mt7915_unregister_thermal(phy);
  	mt76_unregister_phy(mphy);
  	ieee80211_free_hw(mphy->hw);
-@@ -1198,6 +1232,10 @@ static void mt7915_stop_hardware(struct mt7915_dev *dev)
+@@ -1166,6 +1200,10 @@ static void mt7915_stop_hardware(struct mt7915_dev *dev)
  	mt7915_dma_cleanup(dev);
  	tasklet_disable(&dev->mt76.irq_tasklet);
  
@@ -191,7 +191,7 @@
  	if (is_mt798x(&dev->mt76))
  		mt7986_wmac_disable(dev);
  }
-@@ -1242,6 +1280,14 @@ int mt7915_register_device(struct mt7915_dev *dev)
+@@ -1210,6 +1248,14 @@ int mt7915_register_device(struct mt7915_dev *dev)
  	dev->mt76.test_ops = &mt7915_testmode_ops;
  #endif
  
@@ -207,7 +207,7 @@
  				   ARRAY_SIZE(mt76_rates));
  	if (ret)
 diff --git a/mt7915/main.c b/mt7915/main.c
-index 5523031..f3c3b7e 100644
+index fc9a464a..12bb0b39 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -810,6 +810,19 @@ void mt7915_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
@@ -231,7 +231,7 @@
  	mt7915_mcu_add_sta(dev, vif, sta, false);
  
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 37b1505..33dab71 100644
+index ff7f81b0..44765b1f 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -40,6 +40,10 @@ static bool sr_scene_detect = true;
@@ -459,7 +459,7 @@
  int mt7915_dbg_mcu_wa_cmd(struct mt7915_dev *dev, int cmd, u32 a1, u32 a2, u32 a3, bool wait_resp)
  {
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index 9ae0f07..f32d525 100644
+index 9ae0f07a..f32d5256 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -604,4 +604,78 @@ mt7915_get_power_bound(struct mt7915_phy *phy, s8 txpower)
@@ -542,10 +542,10 @@
 +
  #endif
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index d2224dc..4696171 100644
+index 58c0bf99..1316f93d 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
-@@ -197,6 +197,57 @@ struct mt7915_hif {
+@@ -195,6 +195,57 @@ struct mt7915_hif {
  	int irq;
  };
  
@@ -603,7 +603,7 @@
  struct mt7915_phy {
  	struct mt76_phy *mt76;
  	struct mt7915_dev *dev;
-@@ -245,6 +296,25 @@ struct mt7915_phy {
+@@ -243,6 +294,25 @@ struct mt7915_phy {
  		u8 spe_idx;
  	} test;
  #endif
@@ -629,7 +629,7 @@
  };
  
  #ifdef MTK_DEBUG
-@@ -649,6 +719,14 @@ void mt7915_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+@@ -646,6 +716,14 @@ void mt7915_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
  int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr,
  			 bool pci, int *irq);
  
@@ -646,7 +646,7 @@
  int mt7915_dbg_mcu_wa_cmd(struct mt7915_dev *dev, int cmd, u32 a1, u32 a2, u32 a3, bool wait_resp);
 diff --git a/mt7915/vendor.c b/mt7915/vendor.c
 new file mode 100644
-index 0000000..0476202
+index 00000000..92496513
 --- /dev/null
 +++ b/mt7915/vendor.c
 @@ -0,0 +1,606 @@
@@ -1258,7 +1258,7 @@
 +}
 diff --git a/mt7915/vendor.h b/mt7915/vendor.h
 new file mode 100644
-index 0000000..d2b90aa
+index 00000000..d2b90aa0
 --- /dev/null
 +++ b/mt7915/vendor.h
 @@ -0,0 +1,75 @@
diff --git a/recipes-wifi/linux-mt76/files/patches/1002-wifi-mt76-mt7915-air-monitor-support.patch b/recipes-wifi/linux-mt76/files/patches/1002-wifi-mt76-mt7915-air-monitor-support.patch
index 1bbc85f..06612ed 100644
--- a/recipes-wifi/linux-mt76/files/patches/1002-wifi-mt76-mt7915-air-monitor-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1002-wifi-mt76-mt7915-air-monitor-support.patch
@@ -1,7 +1,7 @@
-From 703dc34c3f03017062f03a74add408ece14914a9 Mon Sep 17 00:00:00 2001
+From 3ae462a794ae2a8b613c23aa6179cf5c7b2f7b3c Mon Sep 17 00:00:00 2001
 From: Bo Jiao <Bo.Jiao@mediatek.com>
 Date: Tue, 11 Jan 2022 12:03:23 +0800
-Subject: [PATCH] wifi: mt76: mt7915: air monitor support
+Subject: [PATCH 1002/1052] wifi: mt76: mt7915: air monitor support
 
 ---
  mt76_connac_mcu.h |   2 +
@@ -13,10 +13,10 @@
  6 files changed, 441 insertions(+)
 
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index cda7559..3aa4e59 100644
+index ad63596d..cd6db774 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
-@@ -1243,6 +1243,8 @@ enum {
+@@ -1245,6 +1245,8 @@ enum {
  	MCU_EXT_CMD_GROUP_PRE_CAL_INFO = 0xab,
  	MCU_EXT_CMD_DPD_PRE_CAL_INFO = 0xac,
  	MCU_EXT_CMD_PHY_STAT_INFO = 0xad,
@@ -26,7 +26,7 @@
  	MCU_EXT_CMD_CSI_CTRL = 0xc2,
  };
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index d99864f..e38905a 100644
+index 8268c19a..778f04f7 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -531,6 +531,10 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb,
@@ -41,7 +41,7 @@
  		status->flag |= RX_FLAG_8023;
  		mt7915_wed_check_ppe(dev, &dev->mt76.q_rx[q], msta, skb,
 diff --git a/mt7915/main.c b/mt7915/main.c
-index f3c3b7e..707afcc 100644
+index 12bb0b39..aca3e9c0 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -800,6 +800,9 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
@@ -55,10 +55,10 @@
  }
  
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 4696171..1acf938 100644
+index 1316f93d..bb39a53a 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
-@@ -234,6 +234,7 @@ struct csi_data {
+@@ -232,6 +232,7 @@ struct csi_data {
  
  	struct list_head node;
  };
@@ -66,7 +66,7 @@
  struct csi_mac_filter {
  	struct list_head node;
  
-@@ -246,6 +247,33 @@ struct csi_mac_filter {
+@@ -244,6 +245,33 @@ struct csi_mac_filter {
  #define SHOW_CSI_MAC 2
  
  #define MAX_CSI_MAC_NUM 10
@@ -100,7 +100,7 @@
  #endif
  
  struct mt7915_phy {
-@@ -314,6 +342,8 @@ struct mt7915_phy {
+@@ -312,6 +340,8 @@ struct mt7915_phy {
  		u32 interval;
  		u32 last_record;
  	} csi;
@@ -109,7 +109,7 @@
  #endif
  };
  
-@@ -725,6 +755,9 @@ int mt7915_mcu_set_csi(struct mt7915_phy *phy, u8 mode,
+@@ -722,6 +752,9 @@ int mt7915_mcu_set_csi(struct mt7915_phy *phy, u8 mode,
  		       u8 cfg, u8 v1, u32 v2, u8 *mac_addr, u32 sta_interval);
  struct csi_mac_filter *mt7915_csi_mac_filter_find(struct mt7915_phy *phy, u8 *addr);
  void mt7915_csi_mac_filter_clear(struct mt7915_phy *phy);
@@ -120,7 +120,7 @@
  
  #ifdef MTK_DEBUG
 diff --git a/mt7915/vendor.c b/mt7915/vendor.c
-index 0476202..4c11ed1 100644
+index 92496513..fb32cd6d 100644
 --- a/mt7915/vendor.c
 +++ b/mt7915/vendor.c
 @@ -584,6 +584,355 @@ out:
@@ -499,7 +499,7 @@
  };
  
 diff --git a/mt7915/vendor.h b/mt7915/vendor.h
-index d2b90aa..429b25b 100644
+index d2b90aa0..429b25b7 100644
 --- a/mt7915/vendor.h
 +++ b/mt7915/vendor.h
 @@ -5,6 +5,7 @@
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 490e884..7bf27a4 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,7 +1,7 @@
-From 76e8a5bbc80cf7612256c7783555bba11dbf5b8b Mon Sep 17 00:00:00 2001
+From 6ec4565becc5364a689b346496c7caaef98f83e9 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/1051] wifi: mt76: mt7915: add support for muru_onoff via
+Subject: [PATCH 1003/1052] wifi: mt76: mt7915: add support for muru_onoff via
 
 ---
  mt7915/init.c        |  1 +
@@ -12,10 +12,10 @@
  5 files changed, 50 insertions(+), 2 deletions(-)
 
 diff --git a/mt7915/init.c b/mt7915/init.c
-index 3f06360..6fa3a51 100644
+index ac15bc53..18c6ef7e 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
-@@ -363,6 +363,7 @@ mt7915_init_wiphy(struct mt7915_phy *phy)
+@@ -364,6 +364,7 @@ mt7915_init_wiphy(struct mt7915_phy *phy)
  		IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US;
  
  	phy->slottime = 9;
@@ -24,7 +24,7 @@
  	hw->sta_data_size = sizeof(struct mt7915_sta);
  	hw->vif_data_size = sizeof(struct mt7915_vif);
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 3f35ba3..5aaa3f0 100644
+index 44765b1f..6867635f 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -965,6 +965,7 @@ mt7915_mcu_sta_muru_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
@@ -57,7 +57,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 f32d525..f44146e 100644
+index f32d5256..f44146ed 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -678,4 +678,10 @@ enum CSI_CHAIN_TYPE {
@@ -72,10 +72,10 @@
 +
  #endif
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 57a9da3..95722cb 100644
+index bb39a53a..14107de3 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
-@@ -291,6 +291,8 @@ struct mt7915_phy {
+@@ -302,6 +302,8 @@ struct mt7915_phy {
  	u32 rx_ampdu_ts;
  	u32 ampdu_ref;
  
@@ -85,7 +85,7 @@
  	struct mt76_channel_state state_ts;
  
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index 62d3a99..2f55a84 100644
+index 665d8bd4..0beb3644 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -2554,6 +2554,38 @@ static int mt7915_token_txd_read(struct seq_file *s, void *data)
diff --git a/recipes-wifi/linux-mt76/files/patches/1004-wifi-mt76-mt7915-certification-patches.patch b/recipes-wifi/linux-mt76/files/patches/1004-wifi-mt76-mt7915-certification-patches.patch
index b9fac6d..7024d1f 100644
--- a/recipes-wifi/linux-mt76/files/patches/1004-wifi-mt76-mt7915-certification-patches.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1004-wifi-mt76-mt7915-certification-patches.patch
@@ -1,7 +1,7 @@
-From 9196ca94b27491c6841a3a1a46afb8557f82e62d Mon Sep 17 00:00:00 2001
+From 230a5ba21216bb292f2fb1b0cf2236e2e14de2ca Mon Sep 17 00:00:00 2001
 From: MeiChia Chiu <meichia.chiu@mediatek.com>
 Date: Mon, 6 Jun 2022 20:15:51 +0800
-Subject: [PATCH 1004/1051] wifi: mt76: mt7915: certification patches
+Subject: [PATCH 1004/1052] wifi: mt76: mt7915: certification patches
 
 ---
  mt76_connac_mcu.h    |   1 +
@@ -16,10 +16,10 @@
  9 files changed, 955 insertions(+), 5 deletions(-)
 
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 3aa4e59..d62b7df 100644
+index cd6db774..bd28cc50 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
-@@ -1246,6 +1246,7 @@ enum {
+@@ -1248,6 +1248,7 @@ enum {
  	/* for vendor csi and air monitor */
  	MCU_EXT_CMD_SMESH_CTRL = 0xae,
  	MCU_EXT_CMD_SET_QOS_MAP = 0xb4,
@@ -28,7 +28,7 @@
  };
  
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index 778f04f..2fd1d1f 100644
+index 778f04f7..2fd1d1fb 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -8,6 +8,7 @@
@@ -76,7 +76,7 @@
  			       IEEE80211_RC_NSS_CHANGED |
  			       IEEE80211_RC_BW_CHANGED))
 diff --git a/mt7915/main.c b/mt7915/main.c
-index 9fd65f0..5c483b3 100644
+index aca3e9c0..09e1a83b 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -771,6 +771,9 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
@@ -107,7 +107,7 @@
  
  void mt7915_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 5aaa3f0..aaac73a 100644
+index 6867635f..c819a3be 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -4402,6 +4402,472 @@ mt7915_mcu_report_csi(struct mt7915_dev *dev, struct sk_buff *skb)
@@ -584,7 +584,7 @@
  
  #ifdef MTK_DEBUG
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index f44146e..eef2fc0 100644
+index f44146ed..eef2fc00 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -486,10 +486,14 @@ enum {
@@ -818,10 +818,10 @@
  
  #endif
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 95722cb..ea309bf 100644
+index 14107de3..cf49ac86 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
-@@ -735,6 +735,19 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr,
+@@ -749,6 +749,19 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr,
  			 bool pci, int *irq);
  
  #ifdef CONFIG_MTK_VENDOR
@@ -842,7 +842,7 @@
  int mt7915_mcu_set_csi(struct mt7915_phy *phy, u8 mode,
  		       u8 cfg, u8 v1, u32 v2, u8 *mac_addr, u32 sta_interval);
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index 2f55a84..84f8fae 100644
+index 0beb3644..54daa736 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -2560,7 +2560,8 @@ static int mt7915_muru_onoff_get(void *data, u64 *val)
@@ -867,11 +867,11 @@
  	}
  
 diff --git a/mt7915/vendor.c b/mt7915/vendor.c
-index c964b14..7a71894 100644
+index fb32cd6d..e4317af3 100644
 --- a/mt7915/vendor.c
 +++ b/mt7915/vendor.c
-@@ -23,6 +23,29 @@ csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
- 	[MTK_VENDOR_ATTR_CSI_CTRL_DATA] = { .type = NLA_NESTED },
+@@ -24,6 +24,29 @@ csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
+ 	[MTK_VENDOR_ATTR_CSI_CTRL_DUMP_MAC_FILTER] = { .type = NLA_NESTED },
  };
  
 +static const struct nla_policy
@@ -900,7 +900,7 @@
  struct csi_null_tone {
  	u8 start;
  	u8 end;
-@@ -797,6 +820,149 @@ mt7915_vendor_amnt_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
+@@ -933,6 +956,149 @@ mt7915_vendor_amnt_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
  	return len + 1;
  }
  
@@ -1050,7 +1050,7 @@
  static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
  	{
  		.info = {
-@@ -821,6 +987,28 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
+@@ -957,6 +1123,28 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
  		.dumpit = mt7915_vendor_amnt_ctrl_dump,
  		.policy = amnt_ctrl_policy,
  		.maxattr = MTK_VENDOR_ATTR_AMNT_CTRL_MAX,
@@ -1080,7 +1080,7 @@
  };
  
 diff --git a/mt7915/vendor.h b/mt7915/vendor.h
-index 1863eee..1a18cae 100644
+index 429b25b7..1a0139d7 100644
 --- a/mt7915/vendor.h
 +++ b/mt7915/vendor.h
 @@ -7,6 +7,48 @@
diff --git a/recipes-wifi/linux-mt76/files/patches/1005-wifi-mt76-mt7915-add-mt76-vendor-muru-onoff-command.patch b/recipes-wifi/linux-mt76/files/patches/1005-wifi-mt76-mt7915-add-mt76-vendor-muru-onoff-command.patch
index 9eb1db4..d110348 100644
--- a/recipes-wifi/linux-mt76/files/patches/1005-wifi-mt76-mt7915-add-mt76-vendor-muru-onoff-command.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1005-wifi-mt76-mt7915-add-mt76-vendor-muru-onoff-command.patch
@@ -1,7 +1,7 @@
-From 78cd3b046f6f037e2b78557860e8c14796c40a00 Mon Sep 17 00:00:00 2001
+From d6ae847c0cea20f781970198a2aed4dc7e4daffa Mon Sep 17 00:00:00 2001
 From: Evelyn Tsai <evelyn.tsai@mediatek.com>
 Date: Tue, 4 Apr 2023 02:27:44 +0800
-Subject: [PATCH 1005/1051] wifi: mt76: mt7915: add mt76 vendor muru onoff
+Subject: [PATCH 1005/1052] wifi: mt76: mt7915: add mt76 vendor muru onoff
  command
 
 ---
@@ -12,7 +12,7 @@
  4 files changed, 63 insertions(+)
 
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index aaac73a..8bb7185 100644
+index c819a3be..cd5363c6 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -4422,6 +4422,13 @@ void mt7915_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
@@ -30,7 +30,7 @@
  }
  
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index eef2fc0..8650053 100644
+index eef2fc00..86500536 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -493,6 +493,7 @@ enum {
@@ -42,10 +42,10 @@
  };
  
 diff --git a/mt7915/vendor.c b/mt7915/vendor.c
-index 7a71894..a8b1fa8 100644
+index e4317af3..cf09b513 100644
 --- a/mt7915/vendor.c
 +++ b/mt7915/vendor.c
-@@ -35,6 +35,11 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
+@@ -36,6 +36,11 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
  	[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
  };
  
@@ -57,7 +57,7 @@
  static const struct nla_policy
  rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
  	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI] = {.type = NLA_U8 },
-@@ -963,6 +968,33 @@ static int mt7915_vendor_wireless_ctrl(struct wiphy *wiphy,
+@@ -1099,6 +1104,33 @@ static int mt7915_vendor_wireless_ctrl(struct wiphy *wiphy,
  	return 0;
  }
  
@@ -91,7 +91,7 @@
  static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
  	{
  		.info = {
-@@ -1009,6 +1041,17 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
+@@ -1145,6 +1177,17 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
  		.doit = mt7915_vendor_wireless_ctrl,
  		.policy = wireless_ctrl_policy,
  		.maxattr = MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX,
@@ -110,7 +110,7 @@
  };
  
 diff --git a/mt7915/vendor.h b/mt7915/vendor.h
-index 1a18cae..a4a9180 100644
+index 1a0139d7..9cb67551 100644
 --- a/mt7915/vendor.h
 +++ b/mt7915/vendor.h
 @@ -9,6 +9,7 @@ enum mtk_nl80211_vendor_subcmds {
diff --git a/recipes-wifi/linux-mt76/files/patches/1006-wifi-mt76-mt7915-drop-undefined-action-frame.patch b/recipes-wifi/linux-mt76/files/patches/1006-wifi-mt76-mt7915-drop-undefined-action-frame.patch
index 5a66be9..0182c4f 100644
--- a/recipes-wifi/linux-mt76/files/patches/1006-wifi-mt76-mt7915-drop-undefined-action-frame.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1006-wifi-mt76-mt7915-drop-undefined-action-frame.patch
@@ -1,14 +1,14 @@
-From 2c33bfa0c261aaf9263bfef4b811a97bc72e20d5 Mon Sep 17 00:00:00 2001
+From d48d6a0643e5be590744db4173e14c87f4945390 Mon Sep 17 00:00:00 2001
 From: Peter Chiu <chui-hao.chiu@mediatek.com>
 Date: Thu, 14 Apr 2022 15:18:02 +0800
-Subject: [PATCH 1006/1051] wifi: mt76: mt7915: drop undefined action frame
+Subject: [PATCH 1006/1052] wifi: mt76: mt7915: drop undefined action frame
 
 ---
  mt7915/mac.c | 6 ++++++
  1 file changed, 6 insertions(+)
 
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index 2fd1d1f..4177d6a 100644
+index 2fd1d1fb..4177d6a3 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -744,6 +744,8 @@ int mt7915_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
diff --git a/recipes-wifi/linux-mt76/files/patches/1007-wifi-mt76-testmode-rework-testmode-init-registers.patch b/recipes-wifi/linux-mt76/files/patches/1007-wifi-mt76-testmode-rework-testmode-init-registers.patch
index fa485ce..0f0e9d5 100644
--- a/recipes-wifi/linux-mt76/files/patches/1007-wifi-mt76-testmode-rework-testmode-init-registers.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1007-wifi-mt76-testmode-rework-testmode-init-registers.patch
@@ -1,7 +1,7 @@
-From 2a6f2256321718cb48b28b0d1a6113dc195de03a Mon Sep 17 00:00:00 2001
+From bbbac8090c06fd110f3770d99e7993017101cfd0 Mon Sep 17 00:00:00 2001
 From: Shayne Chen <shayne.chen@mediatek.com>
 Date: Mon, 6 Jun 2022 19:46:26 +0800
-Subject: [PATCH 1007/1051] wifi: mt76: testmode: rework testmode init
+Subject: [PATCH 1007/1052] wifi: mt76: testmode: rework testmode init
  registers
 
 ---
@@ -18,10 +18,10 @@
  10 files changed, 164 insertions(+), 35 deletions(-)
 
 diff --git a/mac80211.c b/mac80211.c
-index b30a74e..3f5c2ed 100644
+index 4d50bfae..9ebb2746 100644
 --- a/mac80211.c
 +++ b/mac80211.c
-@@ -804,7 +804,8 @@ void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb)
+@@ -805,7 +805,8 @@ void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb)
  	}
  
  #ifdef CONFIG_NL80211_TESTMODE
@@ -32,7 +32,7 @@
  		if (status->flag & RX_FLAG_FAILED_FCS_CRC)
  			phy->test.rx_stats.fcs_error[q]++;
 diff --git a/mt76.h b/mt76.h
-index a07c7df..fe5b136 100644
+index 063fc364..329e4d7a 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -709,6 +709,8 @@ struct mt76_testmode_ops {
@@ -62,10 +62,10 @@
  };
  
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index d62b7df..22d477f 100644
+index bd28cc50..f5edeef6 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
-@@ -1239,6 +1239,7 @@ enum {
+@@ -1241,6 +1241,7 @@ enum {
  	MCU_EXT_CMD_OFFCH_SCAN_CTRL = 0x9a,
  	MCU_EXT_CMD_SET_RDD_TH = 0x9d,
  	MCU_EXT_CMD_MURU_CTRL = 0x9f,
@@ -74,7 +74,7 @@
  	MCU_EXT_CMD_GROUP_PRE_CAL_INFO = 0xab,
  	MCU_EXT_CMD_DPD_PRE_CAL_INFO = 0xac,
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index 8650053..7653b5e 100644
+index 86500536..7653b5e2 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -9,6 +9,7 @@
@@ -86,7 +86,7 @@
  	MCU_ATE_CLEAN_TXQUEUE = 0x1c,
  };
 diff --git a/mt7915/mmio.c b/mt7915/mmio.c
-index 6004d64..694fc1b 100644
+index d6ecd698..02b4714c 100644
 --- a/mt7915/mmio.c
 +++ b/mt7915/mmio.c
 @@ -120,6 +120,7 @@ static const u32 mt7986_reg[] = {
@@ -106,7 +106,7 @@
  	[TMAC_ODTR]		= 0x0cc,
  	[TMAC_ATCR]		= 0x00c,
 diff --git a/mt7915/regs.h b/mt7915/regs.h
-index 3452a7e..8bb6a9f 100644
+index 3452a7e9..8bb6a9f2 100644
 --- a/mt7915/regs.h
 +++ b/mt7915/regs.h
 @@ -48,6 +48,7 @@ enum reg_rev {
@@ -154,7 +154,7 @@
  #define MT_AGG_ACR0(_band)		MT_WF_AGG(_band, __OFFS(AGG_ACR0))
  #define MT_AGG_ACR_CFEND_RATE		GENMASK(13, 0)
 diff --git a/mt7915/testmode.c b/mt7915/testmode.c
-index 0d76ae3..4693919 100644
+index 0d76ae31..46939191 100644
 --- a/mt7915/testmode.c
 +++ b/mt7915/testmode.c
 @@ -30,7 +30,7 @@ struct reg_band {
@@ -379,7 +379,7 @@
  
  const struct mt76_testmode_ops mt7915_testmode_ops = {
 diff --git a/mt7915/testmode.h b/mt7915/testmode.h
-index 5573ac3..a1c54c8 100644
+index 5573ac30..a1c54c89 100644
 --- a/mt7915/testmode.h
 +++ b/mt7915/testmode.h
 @@ -33,6 +33,12 @@ struct mt7915_tm_clean_txq {
@@ -430,7 +430,7 @@
 +
  #endif
 diff --git a/testmode.c b/testmode.c
-index ca4fecc..9e05b86 100644
+index ca4feccf..9e05b862 100644
 --- a/testmode.c
 +++ b/testmode.c
 @@ -448,8 +448,7 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
@@ -454,7 +454,7 @@
  		return -EMSGSIZE;
  
 diff --git a/testmode.h b/testmode.h
-index 5e2792d..8961326 100644
+index 5e2792d8..89613266 100644
 --- a/testmode.h
 +++ b/testmode.h
 @@ -101,6 +101,8 @@ enum mt76_testmode_attr {
diff --git a/recipes-wifi/linux-mt76/files/patches/1008-wifi-mt76-testmode-additional-supports.patch b/recipes-wifi/linux-mt76/files/patches/1008-wifi-mt76-testmode-additional-supports.patch
index 4023904..470e6e4 100644
--- a/recipes-wifi/linux-mt76/files/patches/1008-wifi-mt76-testmode-additional-supports.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1008-wifi-mt76-testmode-additional-supports.patch
@@ -1,7 +1,7 @@
-From aba0454fa24cdc1f0650375d4e3264778c3756ab Mon Sep 17 00:00:00 2001
+From d2bc22b6da2b5bbc7d4c441a1a6b8c55c3fc7462 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 1008/1051] wifi: mt76: testmode: additional supports
+Subject: [PATCH 1008/1052] wifi: mt76: testmode: additional supports
 
 Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
@@ -29,7 +29,7 @@
  20 files changed, 2073 insertions(+), 169 deletions(-)
 
 diff --git a/dma.c b/dma.c
-index ccdd564..bc8afcf 100644
+index ccdd5646..bc8afcff 100644
 --- a/dma.c
 +++ b/dma.c
 @@ -614,8 +614,7 @@ free:
@@ -43,7 +43,7 @@
  #endif
  
 diff --git a/mac80211.c b/mac80211.c
-index 3f5c2ed..305cae7 100644
+index 9ebb2746..d6b70374 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 fe5b136..a7d424f 100644
+index 329e4d7a..62205051 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -707,6 +707,21 @@ struct mt76_testmode_ops {
@@ -246,7 +246,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 a8f097d..44cd646 100644
+index 4e84f8d2..2acd7efb 100644
 --- a/mt76_connac_mcu.c
 +++ b/mt76_connac_mcu.c
 @@ -407,6 +407,7 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
@@ -268,10 +268,10 @@
  		return;
  
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 22d477f..0f408d9 100644
+index f5edeef6..152b4aaa 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
-@@ -1023,6 +1023,7 @@ enum {
+@@ -1024,6 +1024,7 @@ enum {
  	MCU_EXT_EVENT_FW_LOG_2_HOST = 0x13,
  	MCU_EXT_EVENT_THERMAL_PROTECT = 0x22,
  	MCU_EXT_EVENT_ASSERT_DUMP = 0x23,
@@ -279,7 +279,7 @@
  	MCU_EXT_EVENT_RDD_REPORT = 0x3a,
  	MCU_EXT_EVENT_CSA_NOTIFY = 0x4f,
  	MCU_EXT_EVENT_BSS_ACQ_PKT_CNT = 0x52,
-@@ -1246,6 +1247,7 @@ enum {
+@@ -1248,6 +1249,7 @@ enum {
  	MCU_EXT_CMD_PHY_STAT_INFO = 0xad,
  	/* for vendor csi and air monitor */
  	MCU_EXT_CMD_SMESH_CTRL = 0xae,
@@ -288,7 +288,7 @@
  	MCU_EXT_CMD_CERT_CFG = 0xb7,
  	MCU_EXT_CMD_CSI_CTRL = 0xc2,
 diff --git a/mt7915/eeprom.c b/mt7915/eeprom.c
-index bfdbc15..f4876fe 100644
+index bfdbc15a..f4876fe9 100644
 --- a/mt7915/eeprom.c
 +++ b/mt7915/eeprom.c
 @@ -142,7 +142,7 @@ static int mt7915_eeprom_load(struct mt7915_dev *dev)
@@ -301,10 +301,10 @@
  				return ret;
  		}
 diff --git a/mt7915/init.c b/mt7915/init.c
-index 6fa3a51..0994b8a 100644
+index 18c6ef7e..0c58ab7b 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
-@@ -692,7 +692,7 @@ static void mt7915_init_work(struct work_struct *work)
+@@ -700,7 +700,7 @@ static void mt7915_init_work(struct work_struct *work)
  	struct mt7915_dev *dev = container_of(work, struct mt7915_dev,
  				 init_work);
  
@@ -314,7 +314,7 @@
  	mt7915_txbf_init(dev);
  }
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index 4177d6a..b05e163 100644
+index 4177d6a3..b05e1630 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -586,6 +586,7 @@ mt7915_mac_fill_rx_vector(struct mt7915_dev *dev, struct sk_buff *skb)
@@ -404,7 +404,7 @@
  		goto out;
  
 diff --git a/mt7915/main.c b/mt7915/main.c
-index 5c483b3..aebfda8 100644
+index 09e1a83b..2e566710 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -238,7 +238,7 @@ static int mt7915_add_interface(struct ieee80211_hw *hw,
@@ -417,7 +417,7 @@
  		mvif->mt76.wmm_idx += 2;
  
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 8bb7185..8e3ae93 100644
+index cd5363c6..b5177e30 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -478,6 +478,11 @@ mt7915_mcu_rx_ext_event(struct mt7915_dev *dev, struct sk_buff *skb)
@@ -498,7 +498,7 @@
  
  	return 0;
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index 7653b5e..c791c7f 100644
+index 7653b5e2..c791c7fa 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -8,10 +8,15 @@
@@ -556,7 +556,7 @@
  
  enum {
 diff --git a/mt7915/mmio.c b/mt7915/mmio.c
-index 694fc1b..222e2cf 100644
+index 02b4714c..730cd338 100644
 --- a/mt7915/mmio.c
 +++ b/mt7915/mmio.c
 @@ -134,6 +134,7 @@ static const u32 mt7915_offs[] = {
@@ -576,10 +576,10 @@
  	[AGG_PCR0]		= 0x040,
  	[AGG_ACR0]		= 0x054,
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index ea309bf..9cc7f3a 100644
+index cf49ac86..bc77a61a 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
-@@ -306,11 +306,15 @@ struct mt7915_phy {
+@@ -317,11 +317,15 @@ struct mt7915_phy {
  
  		s32 last_freq_offset;
  		u8 last_rcpi[4];
@@ -595,7 +595,7 @@
  	} test;
  #endif
  
-@@ -423,6 +427,14 @@ struct mt7915_dev {
+@@ -438,6 +442,14 @@ struct mt7915_dev {
  	void __iomem *dcm;
  	void __iomem *sku;
  
@@ -610,7 +610,7 @@
  #ifdef MTK_DEBUG
  	u16 wlan_idx;
  	struct {
-@@ -603,8 +615,8 @@ int mt7915_mcu_set_fixed_rate_ctrl(struct mt7915_dev *dev,
+@@ -618,8 +630,8 @@ int mt7915_mcu_set_fixed_rate_ctrl(struct mt7915_dev *dev,
  				   struct ieee80211_vif *vif,
  				   struct ieee80211_sta *sta,
  				   void *data, u32 field);
@@ -621,16 +621,16 @@
  int mt7915_mcu_get_eeprom_free_block(struct mt7915_dev *dev, u8 *block_num);
  int mt7915_mcu_set_mac(struct mt7915_dev *dev, int band, bool enable,
  		       bool hdr_trans);
-@@ -643,6 +655,7 @@ int mt7915_mcu_fw_log_2_host(struct mt7915_dev *dev, u8 type, u8 ctrl);
+@@ -658,6 +670,7 @@ int mt7915_mcu_fw_log_2_host(struct mt7915_dev *dev, u8 type, u8 ctrl);
  int mt7915_mcu_fw_dbg_ctrl(struct mt7915_dev *dev, u32 module, u8 level);
  void mt7915_mcu_rx_event(struct mt7915_dev *dev, struct sk_buff *skb);
  void mt7915_mcu_exit(struct mt7915_dev *dev);
 +int mt7915_tm_txbf_status_read(struct mt7915_dev *dev, struct sk_buff *skb);
  void mt7915_mcu_wmm_pbc_work(struct work_struct *work);
- int mt7915_mcu_set_qos_map(struct mt7915_dev *dev, struct ieee80211_vif *vif);
  
+ static inline u16 mt7915_wtbl_size(struct mt7915_dev *dev)
 diff --git a/mt7915/regs.h b/mt7915/regs.h
-index 8bb6a9f..1236da9 100644
+index 8bb6a9f2..1236da91 100644
 --- a/mt7915/regs.h
 +++ b/mt7915/regs.h
 @@ -62,6 +62,7 @@ enum offs_rev {
@@ -651,7 +651,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 4693919..32dc85c 100644
+index 46939191..32dc85cd 100644
 --- a/mt7915/testmode.c
 +++ b/mt7915/testmode.c
 @@ -9,6 +9,10 @@
@@ -2111,7 +2111,7 @@
 +	.set_eeprom = mt7915_tm_set_eeprom,
  };
 diff --git a/mt7915/testmode.h b/mt7915/testmode.h
-index a1c54c8..eb0e043 100644
+index a1c54c89..eb0e0432 100644
 --- a/mt7915/testmode.h
 +++ b/mt7915/testmode.h
 @@ -4,6 +4,8 @@
@@ -2426,7 +2426,7 @@
 +
  #endif
 diff --git a/testmode.c b/testmode.c
-index 9e05b86..7587047 100644
+index 9e05b862..75870478 100644
 --- a/testmode.c
 +++ b/testmode.c
 @@ -8,6 +8,7 @@ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
@@ -2919,7 +2919,7 @@
  
  	if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER)) {
 diff --git a/testmode.h b/testmode.h
-index 8961326..7a68625 100644
+index 89613266..7a686250 100644
 --- a/testmode.h
 +++ b/testmode.h
 @@ -6,6 +6,8 @@
@@ -3056,7 +3056,7 @@
 +
  #endif
 diff --git a/tools/fields.c b/tools/fields.c
-index e3f6908..406ba77 100644
+index e3f69089..406ba77c 100644
 --- a/tools/fields.c
 +++ b/tools/fields.c
 @@ -10,6 +10,7 @@ static const char * const testmode_state[] = {
@@ -3235,7 +3235,7 @@
  };
  
 diff --git a/tx.c b/tx.c
-index 0fdf7d8..db0d4df 100644
+index 0fdf7d83..db0d4df5 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 *
diff --git a/recipes-wifi/linux-mt76/files/patches/1009-wifi-mt76-testmode-add-pre-cal-support.patch b/recipes-wifi/linux-mt76/files/patches/1009-wifi-mt76-testmode-add-pre-cal-support.patch
index 4a716f1..991c4e4 100644
--- a/recipes-wifi/linux-mt76/files/patches/1009-wifi-mt76-testmode-add-pre-cal-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1009-wifi-mt76-testmode-add-pre-cal-support.patch
@@ -1,7 +1,7 @@
-From 9d04f2c9a6204160cf75dbd1970dee1db3b75d01 Mon Sep 17 00:00:00 2001
+From fbbb77d3e06f0dd3f4ed27e8bb5ff113fe3c77fc Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Wed, 31 Aug 2022 20:06:52 +0800
-Subject: [PATCH] wifi: mt76: testmode: add pre-cal support
+Subject: [PATCH 1009/1052] wifi: mt76: testmode: add pre-cal support
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 ---
@@ -18,7 +18,7 @@
  10 files changed, 507 insertions(+), 6 deletions(-)
 
 diff --git a/eeprom.c b/eeprom.c
-index a267397..3625b16 100644
+index a2673978..3625b169 100644
 --- a/eeprom.c
 +++ b/eeprom.c
 @@ -94,8 +94,10 @@ int mt76_get_of_data_from_mtd(struct mt76_dev *dev, void *eep, int offset, int l
@@ -35,7 +35,7 @@
  
  out_put_node:
 diff --git a/mt76.h b/mt76.h
-index a7d424f..20577af 100644
+index 62205051..d8c2a515 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -708,6 +708,7 @@ struct mt76_testmode_ops {
@@ -47,10 +47,10 @@
  
  struct mt76_testmode_entry_data {
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 0972010..b75d340 100644
+index 152b4aaa..cf88b674 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
-@@ -1019,6 +1019,7 @@ enum {
+@@ -1020,6 +1020,7 @@ enum {
  
  /* ext event table */
  enum {
@@ -59,7 +59,7 @@
  	MCU_EXT_EVENT_FW_LOG_2_HOST = 0x13,
  	MCU_EXT_EVENT_THERMAL_PROTECT = 0x22,
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 233411c..ad58e3b 100644
+index b5177e30..d4291e24 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -482,6 +482,9 @@ mt7915_mcu_rx_ext_event(struct mt7915_dev *dev, struct sk_buff *skb)
@@ -73,10 +73,10 @@
  	case MCU_EXT_EVENT_BSS_ACQ_PKT_CNT:
  		mt7915_mcu_rx_bss_acq_pkt_cnt(dev, skb);
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 1846e2f..dd2e80b 100644
+index bc77a61a..ca385b66 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
-@@ -654,6 +654,7 @@ int mt7915_mcu_fw_dbg_ctrl(struct mt7915_dev *dev, u32 module, u8 level);
+@@ -671,6 +671,7 @@ int mt7915_mcu_fw_dbg_ctrl(struct mt7915_dev *dev, u32 module, u8 level);
  void mt7915_mcu_rx_event(struct mt7915_dev *dev, struct sk_buff *skb);
  void mt7915_mcu_exit(struct mt7915_dev *dev);
  int mt7915_tm_txbf_status_read(struct mt7915_dev *dev, struct sk_buff *skb);
@@ -85,7 +85,7 @@
  
  static inline u16 mt7915_wtbl_size(struct mt7915_dev *dev)
 diff --git a/mt7915/testmode.c b/mt7915/testmode.c
-index 32dc85c..4b34430 100644
+index 32dc85cd..4b344303 100644
 --- a/mt7915/testmode.c
 +++ b/mt7915/testmode.c
 @@ -5,6 +5,7 @@
@@ -560,7 +560,7 @@
 +	.dump_precal = mt7915_tm_dump_precal,
  };
 diff --git a/mt7915/testmode.h b/mt7915/testmode.h
-index eb0e043..7569826 100644
+index eb0e0432..75698261 100644
 --- a/mt7915/testmode.h
 +++ b/mt7915/testmode.h
 @@ -81,6 +81,11 @@ struct tm_tx_cont {
@@ -628,7 +628,7 @@
  	TAM_ARB_OP_MODE_NORMAL = 1,
  	TAM_ARB_OP_MODE_TEST,
 diff --git a/testmode.c b/testmode.c
-index 7587047..132267e 100644
+index 75870478..132267eb 100644
 --- a/testmode.c
 +++ b/testmode.c
 @@ -771,6 +771,18 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
@@ -661,7 +661,7 @@
  	    (nla_put_string(msg, MT76_TM_ATTR_MTD_PART, dev->test_mtd.name) ||
  	     nla_put_u32(msg, MT76_TM_ATTR_MTD_OFFSET, dev->test_mtd.offset)))
 diff --git a/testmode.h b/testmode.h
-index 7a68625..e4c1b52 100644
+index 7a686250..e4c1b521 100644
 --- a/testmode.h
 +++ b/testmode.h
 @@ -19,6 +19,7 @@
@@ -717,7 +717,7 @@
  
  	/* keep last */
 diff --git a/tools/fields.c b/tools/fields.c
-index 406ba77..27801db 100644
+index 406ba77c..27801dbe 100644
 --- a/tools/fields.c
 +++ b/tools/fields.c
 @@ -11,6 +11,14 @@ static const char * const testmode_state[] = {
diff --git a/recipes-wifi/linux-mt76/files/patches/1010-wifi-mt76-testmode-add-iBF-command-mode-support.patch b/recipes-wifi/linux-mt76/files/patches/1010-wifi-mt76-testmode-add-iBF-command-mode-support.patch
index 7713db3..edbae40 100644
--- a/recipes-wifi/linux-mt76/files/patches/1010-wifi-mt76-testmode-add-iBF-command-mode-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1010-wifi-mt76-testmode-add-iBF-command-mode-support.patch
@@ -1,7 +1,7 @@
-From ccf1ac17ba7b6c4cabe8f4e6557a57f66e9fbc7b Mon Sep 17 00:00:00 2001
+From a78a0de5858eba651ca9630dc8970c947b190b28 Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Mon, 12 Sep 2022 18:16:54 +0800
-Subject: [PATCH 1010/1051] wifi: mt76: testmode: add iBF command mode support
+Subject: [PATCH 1010/1052] wifi: mt76: testmode: add iBF command mode support
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 ---
@@ -12,7 +12,7 @@
  4 files changed, 85 insertions(+), 7 deletions(-)
 
 diff --git a/mt7915/testmode.c b/mt7915/testmode.c
-index 4b34430..453319e 100644
+index 4b344303..453319e1 100644
 --- a/mt7915/testmode.c
 +++ b/mt7915/testmode.c
 @@ -722,6 +722,7 @@ mt7915_tm_txbf_profile_update(struct mt7915_phy *phy, u16 *val, bool ebf)
@@ -104,7 +104,7 @@
  		mt7915_tm_set_tx_len(phy, tx_time);
  
 diff --git a/testmode.c b/testmode.c
-index 070b296..b1986ad 100644
+index 132267eb..06b10a36 100644
 --- a/testmode.c
 +++ b/testmode.c
 @@ -535,6 +535,42 @@ out:
@@ -163,7 +163,7 @@
  		nla_for_each_nested(cur, tb[MT76_TM_ATTR_TXBF_PARAM], rem) {
  			if (nla_len(cur) != 2 ||
 diff --git a/testmode.h b/testmode.h
-index e4c1b52..1d7aef8 100644
+index e4c1b521..1d7aef86 100644
 --- a/testmode.h
 +++ b/testmode.h
 @@ -285,8 +285,10 @@ enum mt76_testmode_txbf_act {
@@ -178,7 +178,7 @@
  
  	/* keep last */
 diff --git a/tools/fields.c b/tools/fields.c
-index 27801db..b0ee84d 100644
+index 27801dbe..b0ee84d2 100644
 --- a/tools/fields.c
 +++ b/tools/fields.c
 @@ -32,6 +32,20 @@ static const char * const testmode_tx_mode[] = {
diff --git a/recipes-wifi/linux-mt76/files/patches/1011-wifi-mt76-testmode-add-ZWDFS-test-mode-support.patch b/recipes-wifi/linux-mt76/files/patches/1011-wifi-mt76-testmode-add-ZWDFS-test-mode-support.patch
index 3c95bdb..056c2b1 100644
--- a/recipes-wifi/linux-mt76/files/patches/1011-wifi-mt76-testmode-add-ZWDFS-test-mode-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1011-wifi-mt76-testmode-add-ZWDFS-test-mode-support.patch
@@ -1,7 +1,7 @@
-From d1999bcb10d6f3b0d97bad2ba47ea3bae1b38a52 Mon Sep 17 00:00:00 2001
+From 12f1e58ae28f7e12f298d213a0cfe13b751082df Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Thu, 27 Oct 2022 17:42:07 +0800
-Subject: [PATCH] wifi: mt76: testmode: add ZWDFS test mode support
+Subject: [PATCH 1011/1052] wifi: mt76: testmode: add ZWDFS test mode support
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 ---
@@ -18,7 +18,7 @@
  10 files changed, 508 insertions(+), 1 deletion(-)
 
 diff --git a/mt76.h b/mt76.h
-index 5dfae96..65ec039 100644
+index d8c2a515..15106812 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -793,6 +793,15 @@ struct mt76_testmode_data {
@@ -38,7 +38,7 @@
  
  struct mt76_vif {
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 6060eec..09d41b5 100644
+index cf88b674..40fa9fc8 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
 @@ -1243,6 +1243,7 @@ enum {
@@ -58,7 +58,7 @@
  
  enum {
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 17a23e3..3ba91a6 100644
+index d4291e24..500ecbbb 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -2759,6 +2759,7 @@ mt7915_mcu_background_chain_ctrl(struct mt7915_phy *phy,
@@ -139,7 +139,7 @@
 +	return 0;
 +}
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index c791c7f..066246b 100644
+index c791c7fa..066246bb 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -698,6 +698,52 @@ enum CSI_CHAIN_TYPE {
@@ -196,7 +196,7 @@
  #define OFDMA_DL                       BIT(0)
  #define OFDMA_UL                       BIT(1)
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index ca385b6..00f5544 100644
+index ca385b66..00f55443 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -306,6 +306,7 @@ struct mt7915_phy {
@@ -218,7 +218,7 @@
  int mt7915_mtk_init_debugfs(struct mt7915_phy *phy, struct dentry *dir);
  int mt7915_dbg_mcu_wa_cmd(struct mt7915_dev *dev, int cmd, u32 a1, u32 a2, u32 a3, bool wait_resp);
 diff --git a/mt7915/regs.h b/mt7915/regs.h
-index 1236da9..7e9b76b 100644
+index 1236da91..7e9b76b0 100644
 --- a/mt7915/regs.h
 +++ b/mt7915/regs.h
 @@ -1211,6 +1211,8 @@ enum offs_rev {
@@ -231,7 +231,7 @@
  #define MT_WF_PHY_BASE			0x83080000
  #define MT_WF_PHY(ofs)			(MT_WF_PHY_BASE + (ofs))
 diff --git a/mt7915/testmode.c b/mt7915/testmode.c
-index 453319e..caa3590 100644
+index 453319e1..caa35906 100644
 --- a/mt7915/testmode.c
 +++ b/mt7915/testmode.c
 @@ -14,6 +14,12 @@ enum {
@@ -558,7 +558,7 @@
  
  static int
 diff --git a/testmode.c b/testmode.c
-index 06b10a3..d8fc5d6 100644
+index 06b10a36..d8fc5d6f 100644
 --- a/testmode.c
 +++ b/testmode.c
 @@ -26,6 +26,13 @@ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
@@ -622,7 +622,7 @@
  	     nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) ||
  	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA) &&
 diff --git a/testmode.h b/testmode.h
-index 1d7aef8..b39cf51 100644
+index 1d7aef86..b39cf511 100644
 --- a/testmode.h
 +++ b/testmode.h
 @@ -64,6 +64,20 @@
@@ -690,7 +690,7 @@
 +
  #endif
 diff --git a/tools/fields.c b/tools/fields.c
-index b0ee84d..e2cf4b9 100644
+index b0ee84d2..e2cf4b92 100644
 --- a/tools/fields.c
 +++ b/tools/fields.c
 @@ -46,6 +46,14 @@ static const char * const testmode_txbf_act[] = {
diff --git a/recipes-wifi/linux-mt76/files/patches/1012-wifi-mt76-testmode-add-iBF-eBF-cal-and-cert-commands.patch b/recipes-wifi/linux-mt76/files/patches/1012-wifi-mt76-testmode-add-iBF-eBF-cal-and-cert-commands.patch
index d1a98ab..5795382 100644
--- a/recipes-wifi/linux-mt76/files/patches/1012-wifi-mt76-testmode-add-iBF-eBF-cal-and-cert-commands.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1012-wifi-mt76-testmode-add-iBF-eBF-cal-and-cert-commands.patch
@@ -1,8 +1,8 @@
-From 5cdc71e26586cf3a314a0971a83a00181c3e305b Mon Sep 17 00:00:00 2001
+From d46a2b30adcdc8bca0f2a5012cd7bd4a3d59051b 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] wifi: mt76: testmode: add iBF/eBF cal and cert commands with
- golden
+Subject: [PATCH 1012/1052] wifi: mt76: testmode: add iBF/eBF cal and cert
+ commands with golden
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 ---
@@ -25,7 +25,7 @@
  16 files changed, 859 insertions(+), 325 deletions(-)
 
 diff --git a/mt76.h b/mt76.h
-index 65ec039..4408ba8 100644
+index 15106812..9597f564 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -755,6 +755,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 44cd646..15e61c9 100644
+index 2acd7efb..0ce7ecdc 100644
 --- a/mt76_connac_mcu.c
 +++ b/mt76_connac_mcu.c
 @@ -2688,6 +2688,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 b05e163..dc75ff1 100644
+index b05e1630..dc75ff1f 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -737,8 +737,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 8ec508d..b0e11f1 100644
+index 2e566710..4a541188 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 3ba91a6..0b5b3ae 100644
+index 500ecbbb..6f05a081 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 066246b..de17c57 100644
+index 066246bb..de17c579 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -546,10 +546,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 222e2cf..ddf1b72 100644
+index 730cd338..ed15d711 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 00f5544..ef92b2e 100644
+index 00f55443..ef92b2ea 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -325,7 +325,6 @@ struct mt7915_phy {
@@ -523,7 +523,7 @@
 +
  #endif
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index 54daa73..02fe61a 100644
+index 54daa736..02fe61a3 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -2888,6 +2888,36 @@ mt7915_txpower_level_set(void *data, u64 val)
@@ -576,7 +576,7 @@
  
  	return 0;
 diff --git a/mt7915/mtk_mcu.c b/mt7915/mtk_mcu.c
-index 143dae2..7a2d28c 100644
+index 143dae26..7a2d28c7 100644
 --- a/mt7915/mtk_mcu.c
 +++ b/mt7915/mtk_mcu.c
 @@ -1,9 +1,10 @@
@@ -840,7 +840,7 @@
 +}
 +#endif
 diff --git a/mt7915/regs.h b/mt7915/regs.h
-index 7e9b76b..4d05e39 100644
+index 7e9b76b0..4d05e391 100644
 --- a/mt7915/regs.h
 +++ b/mt7915/regs.h
 @@ -61,6 +61,7 @@ enum offs_rev {
@@ -862,7 +862,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 caa3590..faf6014 100644
+index caa35906..faf60146 100644
 --- a/mt7915/testmode.c
 +++ b/mt7915/testmode.c
 @@ -55,6 +55,8 @@ struct reg_band {
@@ -1706,7 +1706,7 @@
  	rateval =  mode << 6 | rate_idx;
  	tx_cont->rateval = cpu_to_le16(rateval);
 diff --git a/mt7915/testmode.h b/mt7915/testmode.h
-index 7569826..5aba13c 100644
+index 75698261..5aba13cf 100644
 --- a/mt7915/testmode.h
 +++ b/mt7915/testmode.h
 @@ -311,137 +311,7 @@ struct mt7915_tm_muru {
@@ -1850,7 +1850,7 @@
  
  #endif
 diff --git a/testmode.c b/testmode.c
-index d8fc5d6..56b9205 100644
+index d8fc5d6f..56b92059 100644
 --- a/testmode.c
 +++ b/testmode.c
 @@ -196,6 +196,7 @@ mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len,
@@ -1862,7 +1862,7 @@
  	memcpy(hdr->addr2, addr[1], ETH_ALEN);
  	memcpy(hdr->addr3, addr[2], ETH_ALEN);
 diff --git a/testmode.h b/testmode.h
-index b39cf51..20fab3e 100644
+index b39cf511..20fab3ec 100644
 --- a/testmode.h
 +++ b/testmode.h
 @@ -303,7 +303,10 @@ enum mt76_testmode_cfg {
@@ -1890,7 +1890,7 @@
  	/* keep last */
  	NUM_MT76_TM_TXBF_ACT,
 diff --git a/tools/fields.c b/tools/fields.c
-index e2cf4b9..027b8cd 100644
+index e2cf4b92..027b8cdb 100644
 --- a/tools/fields.c
 +++ b/tools/fields.c
 @@ -33,7 +33,10 @@ static const char * const testmode_tx_mode[] = {
diff --git a/recipes-wifi/linux-mt76/files/patches/1013-wifi-mt76-connac-airtime-fairness-feature-off-in-mac.patch b/recipes-wifi/linux-mt76/files/patches/1013-wifi-mt76-connac-airtime-fairness-feature-off-in-mac.patch
index 420358a..7879b85 100644
--- a/recipes-wifi/linux-mt76/files/patches/1013-wifi-mt76-connac-airtime-fairness-feature-off-in-mac.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1013-wifi-mt76-connac-airtime-fairness-feature-off-in-mac.patch
@@ -1,7 +1,7 @@
-From fa9a7d0143157226ec02bdfa64fa9e313f8b87ba Mon Sep 17 00:00:00 2001
+From 9e7d1c03b7944cd7a0641c68c9515f400e662451 Mon Sep 17 00:00:00 2001
 From: Evelyn Tsai <evelyn.tsai@mediatek.com>
 Date: Fri, 6 May 2022 15:58:42 +0800
-Subject: [PATCH 1013/1051] wifi: mt76: connac: airtime fairness feature off in
+Subject: [PATCH 1013/1052] wifi: mt76: connac: airtime fairness feature off in
  mac80211
 
 ---
@@ -9,7 +9,7 @@
  1 file changed, 1 deletion(-)
 
 diff --git a/mac80211.c b/mac80211.c
-index 305cae7..f9dfdf8 100644
+index d6b70374..750a642a 100644
 --- a/mac80211.c
 +++ b/mac80211.c
 @@ -451,7 +451,6 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw)
diff --git a/recipes-wifi/linux-mt76/files/patches/1014-wifi-mt76-mt7915-add-phy-capability-vendor-command.patch b/recipes-wifi/linux-mt76/files/patches/1014-wifi-mt76-mt7915-add-phy-capability-vendor-command.patch
index 2e16650..3357c0d 100644
--- a/recipes-wifi/linux-mt76/files/patches/1014-wifi-mt76-mt7915-add-phy-capability-vendor-command.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1014-wifi-mt76-mt7915-add-phy-capability-vendor-command.patch
@@ -1,7 +1,8 @@
-From 3982aaa49810c8f0b2c754bd6c7a2d2b21c45407 Mon Sep 17 00:00:00 2001
+From ea8636284d9278b7053ecedb02045be768d90957 Mon Sep 17 00:00:00 2001
 From: Yi-Chia Hsieh <Yi-Chia.Hsieh@mediatek.com>
 Date: Tue, 12 Jul 2022 10:04:35 -0700
-Subject: [PATCH] wifi: mt76: mt7915: add phy capability vendor command
+Subject: [PATCH 1014/1052] wifi: mt76: mt7915: add phy capability vendor
+ command
 
 ---
  mt7915/mt7915.h |  1 +
@@ -10,7 +11,7 @@
  3 files changed, 79 insertions(+)
 
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 1c1b3a1..f8ae363 100644
+index ef92b2ea..64cfa2e0 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -11,6 +11,7 @@
@@ -22,7 +23,7 @@
  #define MT7916_WTBL_SIZE		544
  #define MT7915_WTBL_RESERVED		(mt7915_wtbl_size(dev) - 1)
 diff --git a/mt7915/vendor.c b/mt7915/vendor.c
-index 1beb603..e6cd79f 100644
+index cf09b513..9e8d2442 100644
 --- a/mt7915/vendor.c
 +++ b/mt7915/vendor.c
 @@ -52,6 +52,18 @@ rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
@@ -99,7 +100,7 @@
  };
  
 diff --git a/mt7915/vendor.h b/mt7915/vendor.h
-index 9cb6755..e58884c 100644
+index 9cb67551..e58884ce 100644
 --- a/mt7915/vendor.h
 +++ b/mt7915/vendor.h
 @@ -10,6 +10,7 @@ enum mtk_nl80211_vendor_subcmds {
diff --git a/recipes-wifi/linux-mt76/files/patches/1015-wifi-mt76-mt7915-add-vendor-subcmd-EDCCA-ctrl-enable.patch b/recipes-wifi/linux-mt76/files/patches/1015-wifi-mt76-mt7915-add-vendor-subcmd-EDCCA-ctrl-enable.patch
index c8d1472..a8230fb 100644
--- a/recipes-wifi/linux-mt76/files/patches/1015-wifi-mt76-mt7915-add-vendor-subcmd-EDCCA-ctrl-enable.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1015-wifi-mt76-mt7915-add-vendor-subcmd-EDCCA-ctrl-enable.patch
@@ -1,7 +1,7 @@
-From d49feab75f9fc7ffc5fd8b817b11c80251ec720c Mon Sep 17 00:00:00 2001
+From 31ecfaed704ad082d532a32ae5abd045f5d2f339 Mon Sep 17 00:00:00 2001
 From: Howard Hsu <howard-yh.hsu@mediatek.com>
 Date: Fri, 24 Jun 2022 11:15:45 +0800
-Subject: [PATCH] wifi: mt76: mt7915: add vendor subcmd EDCCA ctrl
+Subject: [PATCH 1015/1052] wifi: mt76: mt7915: add vendor subcmd EDCCA ctrl
  enable/threshold/compensation
 
 ---
@@ -15,7 +15,7 @@
  7 files changed, 264 insertions(+), 1 deletion(-)
 
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 09d41b5..10b6133 100644
+index 40fa9fc8..a85179b8 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
 @@ -1254,6 +1254,7 @@ enum {
@@ -27,7 +27,7 @@
  	MCU_EXT_CMD_IPI_HIST_SCAN = 0xc5,
  };
 diff --git a/mt7915/main.c b/mt7915/main.c
-index b0e11f1..b1d7383 100644
+index 4a541188..de2f9098 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -479,6 +479,9 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed)
@@ -41,7 +41,7 @@
  		ret = mt7915_set_channel(phy);
  		if (ret)
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 0b5b3ae..0e04fc2 100644
+index 6f05a081..f83d969c 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -5037,3 +5037,75 @@ int mt7915_mcu_ipi_hist_scan(struct mt7915_phy *phy, void *data, u8 mode, bool w
@@ -121,7 +121,7 @@
 +	return 0;
 +}
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index de17c57..1682c11 100644
+index de17c579..1682c117 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -1128,6 +1128,27 @@ enum {
@@ -153,7 +153,7 @@
  
  #endif
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 64cfa2e..141c151 100644
+index 64cfa2e0..141c151e 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -789,7 +789,8 @@ void mt7915_vendor_amnt_fill_rx(struct mt7915_phy *phy, struct sk_buff *skb);
@@ -167,7 +167,7 @@
  int mt7915_mcu_ipi_hist_scan(struct mt7915_phy *phy, void *data, u8 mode, bool wait_resp);
  
 diff --git a/mt7915/vendor.c b/mt7915/vendor.c
-index 9e8d244..aaa0cf1 100644
+index 9e8d2442..aaa0cf1d 100644
 --- a/mt7915/vendor.c
 +++ b/mt7915/vendor.c
 @@ -64,6 +64,24 @@ phy_capa_dump_policy[NUM_MTK_VENDOR_ATTRS_PHY_CAPA_DUMP] = {
@@ -324,7 +324,7 @@
  };
  
 diff --git a/mt7915/vendor.h b/mt7915/vendor.h
-index e58884c..c8d30b5 100644
+index e58884ce..c8d30b52 100644
 --- a/mt7915/vendor.h
 +++ b/mt7915/vendor.h
 @@ -3,6 +3,7 @@
diff --git a/recipes-wifi/linux-mt76/files/patches/1016-wifi-mt76-mt7915-implement-bin-file-mode.patch b/recipes-wifi/linux-mt76/files/patches/1016-wifi-mt76-mt7915-implement-bin-file-mode.patch
index 956433c..9800bd2 100644
--- a/recipes-wifi/linux-mt76/files/patches/1016-wifi-mt76-mt7915-implement-bin-file-mode.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1016-wifi-mt76-mt7915-implement-bin-file-mode.patch
@@ -1,7 +1,7 @@
-From c4c1b1bf2d0630188f3e283d7f3856d0fb0700c6 Mon Sep 17 00:00:00 2001
+From 6aa933c1f0af48d3703f500dce4514b62d9d2121 Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Thu, 7 Jul 2022 11:09:59 +0800
-Subject: [PATCH 1016/1051] wifi: mt76: mt7915: implement bin file mode
+Subject: [PATCH 1016/1052] wifi: mt76: mt7915: implement bin file mode
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
@@ -16,7 +16,7 @@
  7 files changed, 144 insertions(+), 4 deletions(-)
 
 diff --git a/eeprom.c b/eeprom.c
-index 3625b16..9d029c0 100644
+index 3625b169..9d029c04 100644
 --- a/eeprom.c
 +++ b/eeprom.c
 @@ -161,6 +161,31 @@ static int mt76_get_of_eeprom(struct mt76_dev *dev, void *eep, int len)
@@ -52,7 +52,7 @@
  mt76_eeprom_override(struct mt76_phy *phy)
  {
 diff --git a/mt76.h b/mt76.h
-index 1455144..fbcdfa9 100644
+index 9597f564..253a564f 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -983,6 +983,9 @@ struct mt76_dev {
@@ -74,7 +74,7 @@
  struct mt76_queue *
  mt76_init_queue(struct mt76_dev *dev, int qid, int idx, int n_desc,
 diff --git a/mt7915/eeprom.c b/mt7915/eeprom.c
-index f4876fe..c8b1c18 100644
+index f4876fe9..c8b1c18e 100644
 --- a/mt7915/eeprom.c
 +++ b/mt7915/eeprom.c
 @@ -5,6 +5,30 @@
@@ -196,7 +196,7 @@
  		if (ret)
  			return ret;
 diff --git a/mt7915/eeprom.h b/mt7915/eeprom.h
-index 509fb43..99101f9 100644
+index 509fb43d..99101f91 100644
 --- a/mt7915/eeprom.h
 +++ b/mt7915/eeprom.h
 @@ -109,6 +109,13 @@ enum mt7915_sku_rate_group {
@@ -214,10 +214,10 @@
  mt7915_get_channel_group_5g(int channel, bool is_7976)
  {
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index ea450ae..28403c9 100644
+index 141c151e..2fb8e2fb 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
-@@ -399,6 +399,8 @@ struct mt7915_dev {
+@@ -414,6 +414,8 @@ struct mt7915_dev {
  
  	bool dbdc_support;
  	bool flash_mode;
@@ -226,7 +226,7 @@
  	bool muru_debug;
  	bool ibf;
  
-@@ -786,6 +788,7 @@ void mt7915_dump_tmac_info(u8 *tmac_info);
+@@ -802,6 +804,7 @@ void mt7915_dump_tmac_info(u8 *tmac_info);
  int mt7915_mcu_set_txpower_level(struct mt7915_phy *phy, u8 drop_level);
  void mt7915_packet_log_to_host(struct mt7915_dev *dev, const void *data, int len, int type, int des_len);
  int mt7915_mcu_set_amsdu_algo(struct mt7915_dev *dev, u16 wcid, u8 enable);
@@ -235,7 +235,7 @@
  #define PKT_BIN_DEBUG_MAGIC	0xc8763123
  enum {
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index 9e490ad..17577fd 100644
+index 02fe61a3..76f4849e 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -3,6 +3,7 @@
@@ -304,7 +304,7 @@
  }
  #endif
 diff --git a/testmode.h b/testmode.h
-index 20fab3e..91d1e86 100644
+index 20fab3ec..91d1e867 100644
 --- a/testmode.h
 +++ b/testmode.h
 @@ -17,7 +17,7 @@
diff --git a/recipes-wifi/linux-mt76/files/patches/1017-wifi-mt76-mt7915-Add-mu-dump-support.patch b/recipes-wifi/linux-mt76/files/patches/1017-wifi-mt76-mt7915-Add-mu-dump-support.patch
index 30998f6..8e1378e 100644
--- a/recipes-wifi/linux-mt76/files/patches/1017-wifi-mt76-mt7915-Add-mu-dump-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1017-wifi-mt76-mt7915-Add-mu-dump-support.patch
@@ -1,7 +1,7 @@
-From fdac49c9616d72b03e0ba8ab1f17d777db8876cf Mon Sep 17 00:00:00 2001
+From 0623b3b87fe33c6ad8037aa01cf63b2a1ab2274f Mon Sep 17 00:00:00 2001
 From: TomLiu <tomml.liu@mediatek.com>
 Date: Thu, 11 Aug 2022 18:09:45 -0700
-Subject: [PATCH 1017/1051] wifi: mt76: mt7915: Add mu dump support
+Subject: [PATCH 1017/1052] wifi: mt76: mt7915: Add mu dump support
 
 ---
  mt7915/vendor.c | 24 ++++++++++++++++++++++++
@@ -9,10 +9,10 @@
  2 files changed, 25 insertions(+)
 
 diff --git a/mt7915/vendor.c b/mt7915/vendor.c
-index 3a58684..ac6f637 100644
+index aaa0cf1d..eb0b380d 100644
 --- a/mt7915/vendor.c
 +++ b/mt7915/vendor.c
-@@ -38,6 +38,7 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
+@@ -39,6 +39,7 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
  static const struct nla_policy
  mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
  	[MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
@@ -20,7 +20,7 @@
  };
  
  static const struct nla_policy
-@@ -1025,6 +1026,28 @@ static int mt7915_vendor_mu_ctrl(struct wiphy *wiphy,
+@@ -1161,6 +1162,28 @@ static int mt7915_vendor_mu_ctrl(struct wiphy *wiphy,
  	return 0;
  }
  
@@ -49,7 +49,7 @@
  static int
  mt7915_vendor_phy_capa_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
  			     struct sk_buff *skb, const void *data, int data_len,
-@@ -1211,6 +1234,7 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
+@@ -1347,6 +1370,7 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
  		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
  			WIPHY_VENDOR_CMD_NEED_RUNNING,
  		.doit = mt7915_vendor_mu_ctrl,
@@ -58,7 +58,7 @@
  		.maxattr = MTK_VENDOR_ATTR_MU_CTRL_MAX,
  	},
 diff --git a/mt7915/vendor.h b/mt7915/vendor.h
-index 284994a..8c2a996 100644
+index c8d30b52..c61ba260 100644
 --- a/mt7915/vendor.h
 +++ b/mt7915/vendor.h
 @@ -73,6 +73,7 @@ enum mtk_vendor_attr_mu_ctrl {
diff --git a/recipes-wifi/linux-mt76/files/patches/1018-wifi-mt76-mt7915-add-vendor-subcmd-three-wire-PTA-ct.patch b/recipes-wifi/linux-mt76/files/patches/1018-wifi-mt76-mt7915-add-vendor-subcmd-three-wire-PTA-ct.patch
index 0b9b72a..90266c6 100644
--- a/recipes-wifi/linux-mt76/files/patches/1018-wifi-mt76-mt7915-add-vendor-subcmd-three-wire-PTA-ct.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1018-wifi-mt76-mt7915-add-vendor-subcmd-three-wire-PTA-ct.patch
@@ -1,7 +1,7 @@
-From 14f9e5f7f36380a64d158e31f60956ddec016245 Mon Sep 17 00:00:00 2001
+From a2610e02282fb1825cdc2d76ebff9e979a6a977c Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Fri, 28 Oct 2022 10:15:56 +0800
-Subject: [PATCH 1018/1051] wifi: mt76: mt7915: add vendor subcmd three wire
+Subject: [PATCH 1018/1052] wifi: mt76: mt7915: add vendor subcmd three wire
  (PTA) ctrl
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
@@ -15,10 +15,10 @@
  6 files changed, 111 insertions(+), 29 deletions(-)
 
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 012f9be..7e12c05 100644
+index a85179b8..e7eb6a93 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
-@@ -1251,7 +1251,7 @@ enum {
+@@ -1253,7 +1253,7 @@ enum {
  	MCU_EXT_CMD_SMESH_CTRL = 0xae,
  	MCU_EXT_CMD_RX_STAT_USER_CTRL = 0xb3,
  	MCU_EXT_CMD_SET_QOS_MAP = 0xb4,
@@ -28,7 +28,7 @@
  	MCU_EXT_CMD_CSI_CTRL = 0xc2,
  	MCU_EXT_CMD_IPI_HIST_SCAN = 0xc5,
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index badc831..89b35e8 100644
+index f83d969c..3e4239f6 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -4736,37 +4736,33 @@ void mt7915_mcu_set_dynalgo(struct mt7915_phy *phy, u8 enable)
@@ -93,7 +93,7 @@
  
  void mt7915_mcu_set_bypass_smthint(struct mt7915_phy *phy, u8 val)
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index 1682c11..1b0bd06 100644
+index 1682c117..1b0bd06b 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -916,6 +916,35 @@ struct mt7915_mcu_rdd_ipi_scan {
@@ -133,10 +133,10 @@
  #define OFDMA_DL                       BIT(0)
  #define OFDMA_UL                       BIT(1)
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 28403c9..d9a885d 100644
+index 2fb8e2fb..6027e7f7 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
-@@ -767,6 +767,7 @@ void mt7915_mcu_set_mimo(struct mt7915_phy *phy, u8 direction);
+@@ -781,6 +781,7 @@ void mt7915_mcu_set_mimo(struct mt7915_phy *phy, u8 direction);
  void mt7915_mcu_set_dynalgo(struct mt7915_phy *phy, u8 enable);
  int mt7915_mcu_set_mu_edca(struct mt7915_phy *phy, u8 val);
  void mt7915_mcu_set_cert(struct mt7915_phy *phy, u8 type);
@@ -145,10 +145,10 @@
  void mt7915_vendor_register(struct mt7915_phy *phy);
  int mt7915_mcu_set_csi(struct mt7915_phy *phy, u8 mode,
 diff --git a/mt7915/vendor.c b/mt7915/vendor.c
-index ac6f637..eeac18d 100644
+index eb0b380d..54b89030 100644
 --- a/mt7915/vendor.c
 +++ b/mt7915/vendor.c
-@@ -41,6 +41,11 @@ mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
+@@ -42,6 +42,11 @@ mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
  	[MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
  };
  
@@ -160,7 +160,7 @@
  static const struct nla_policy
  rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
  	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI] = {.type = NLA_U8 },
-@@ -992,7 +997,7 @@ static int mt7915_vendor_wireless_ctrl(struct wiphy *wiphy,
+@@ -1128,7 +1133,7 @@ static int mt7915_vendor_wireless_ctrl(struct wiphy *wiphy,
  			mt7915_set_wireless_vif, &val32);
  	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]) {
  		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]);
@@ -169,7 +169,7 @@
  		mt7915_mcu_set_bypass_smthint(phy, val8); /* Cert bypass smooth interpolation */
  	}
  
-@@ -1136,6 +1141,7 @@ static int mt7915_vendor_edcca_ctrl(struct wiphy *wiphy,
+@@ -1272,6 +1277,7 @@ static int mt7915_vendor_edcca_ctrl(struct wiphy *wiphy,
  	return 0;
  }
  
@@ -177,7 +177,7 @@
  static int
  mt7915_vendor_edcca_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
  			     struct sk_buff *skb, const void *data, int data_len,
-@@ -1179,6 +1185,31 @@ mt7915_vendor_edcca_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
+@@ -1315,6 +1321,31 @@ mt7915_vendor_edcca_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
  	return len;
  }
  
@@ -209,7 +209,7 @@
  static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
  	{
  		.info = {
-@@ -1260,6 +1291,17 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
+@@ -1396,6 +1427,17 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
  		.dumpit = mt7915_vendor_edcca_ctrl_dump,
  		.policy = edcca_ctrl_policy,
  		.maxattr = MTK_VENDOR_ATTR_EDCCA_CTRL_MAX,
@@ -228,7 +228,7 @@
  };
  
 diff --git a/mt7915/vendor.h b/mt7915/vendor.h
-index 8c2a996..e61a6aa 100644
+index c61ba260..ddde0cc0 100644
 --- a/mt7915/vendor.h
 +++ b/mt7915/vendor.h
 @@ -13,6 +13,7 @@ enum mtk_nl80211_vendor_subcmds {
diff --git a/recipes-wifi/linux-mt76/files/patches/1019-wifi-mt76-mt7915-add-ibf-control-vendor-cmd.patch b/recipes-wifi/linux-mt76/files/patches/1019-wifi-mt76-mt7915-add-ibf-control-vendor-cmd.patch
index 60b7d81..cfd1574 100644
--- a/recipes-wifi/linux-mt76/files/patches/1019-wifi-mt76-mt7915-add-ibf-control-vendor-cmd.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1019-wifi-mt76-mt7915-add-ibf-control-vendor-cmd.patch
@@ -1,7 +1,7 @@
-From b523d127286292b2915eda127d35001ee67ab461 Mon Sep 17 00:00:00 2001
+From 95a5898db592cf183d3275e67a511f3ad5c2f541 Mon Sep 17 00:00:00 2001
 From: mtk27835 <shurong.wen@mediatek.com>
 Date: Wed, 7 Sep 2022 14:01:29 -0700
-Subject: [PATCH 1019/1051] wifi: mt76: mt7915: add ibf control vendor cmd
+Subject: [PATCH 1019/1052] wifi: mt76: mt7915: add ibf control vendor cmd
 
 Signed-off-by: mtk27835 <shurong.wen@mediatek.com>
 ---
@@ -10,10 +10,10 @@
  2 files changed, 89 insertions(+), 1 deletion(-)
 
 diff --git a/mt7915/vendor.c b/mt7915/vendor.c
-index eeac18d..a21cbce 100644
+index 54b89030..b4facaaa 100644
 --- a/mt7915/vendor.c
 +++ b/mt7915/vendor.c
-@@ -87,6 +87,11 @@ edcca_dump_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP] = {
+@@ -88,6 +88,11 @@ edcca_dump_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP] = {
         [MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL] = { .type = NLA_U8 },
  };
  
@@ -25,7 +25,7 @@
  struct csi_null_tone {
  	u8 start;
  	u8 end;
-@@ -1209,6 +1214,54 @@ static int mt7915_vendor_3wire_ctrl(struct wiphy *wiphy,
+@@ -1345,6 +1350,54 @@ static int mt7915_vendor_3wire_ctrl(struct wiphy *wiphy,
  	return mt7915_mcu_set_cfg(phy, CFGINFO_3WIRE_EN_CFG, three_wire_mode);
  }
  
@@ -80,7 +80,7 @@
  
  static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
  	{
-@@ -1302,6 +1355,18 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
+@@ -1438,6 +1491,18 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
  		.doit = mt7915_vendor_3wire_ctrl,
  		.policy = three_wire_ctrl_policy,
  		.maxattr = MTK_VENDOR_ATTR_3WIRE_CTRL_MAX,
@@ -100,7 +100,7 @@
  };
  
 diff --git a/mt7915/vendor.h b/mt7915/vendor.h
-index e61a6aa..876edf3 100644
+index ddde0cc0..24f35c8e 100644
 --- a/mt7915/vendor.h
 +++ b/mt7915/vendor.h
 @@ -13,7 +13,8 @@ enum mtk_nl80211_vendor_subcmds {
@@ -113,7 +113,7 @@
  };
  
  
-@@ -225,4 +226,26 @@ enum mtk_vendor_attr_phy_capa_dump {
+@@ -238,4 +239,26 @@ enum mtk_vendor_attr_phy_capa_dump {
  		NUM_MTK_VENDOR_ATTRS_PHY_CAPA_DUMP - 1
  };
  
diff --git a/recipes-wifi/linux-mt76/files/patches/1020-wifi-mt76-mt7915-add-cal-free-data-merge-support.patch b/recipes-wifi/linux-mt76/files/patches/1020-wifi-mt76-mt7915-add-cal-free-data-merge-support.patch
index 7b56f68..a16e386 100644
--- a/recipes-wifi/linux-mt76/files/patches/1020-wifi-mt76-mt7915-add-cal-free-data-merge-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1020-wifi-mt76-mt7915-add-cal-free-data-merge-support.patch
@@ -1,7 +1,7 @@
-From 4f16df4de5a3b2d9080a7c07bfc1f0496de442cd Mon Sep 17 00:00:00 2001
+From 1c8aa13277994e9d4bedcc2399bdcbd73d3612ef Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Thu, 30 Mar 2023 15:12:37 +0800
-Subject: [PATCH] wifi: mt76: mt7915: add cal free data merge support
+Subject: [PATCH 1020/1052] wifi: mt76: mt7915: add cal free data merge support
 
 1. add basic cal free data support
 2. add E3 low yield rate workaround for panther E3 with 7976 adie
@@ -19,7 +19,7 @@
  5 files changed, 250 insertions(+), 6 deletions(-)
 
 diff --git a/mt7915/debugfs.c b/mt7915/debugfs.c
-index 909df24..c369296 100644
+index 909df246..c3692969 100644
 --- a/mt7915/debugfs.c
 +++ b/mt7915/debugfs.c
 @@ -1286,6 +1286,46 @@ static const struct file_operations mt7915_csi_ops = {
@@ -78,7 +78,7 @@
  	debugfs_create_file("csi_stats", 0400, dir, phy, &mt7915_csi_ops);
  #endif
 diff --git a/mt7915/eeprom.c b/mt7915/eeprom.c
-index c8b1c18..6133c20 100644
+index c8b1c18e..6133c200 100644
 --- a/mt7915/eeprom.c
 +++ b/mt7915/eeprom.c
 @@ -48,8 +48,13 @@ static int mt7915_eeprom_load_precal(struct mt7915_dev *dev)
@@ -309,7 +309,7 @@
  	memcpy(dev->mphy.macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR,
  	       ETH_ALEN);
 diff --git a/mt7915/eeprom.h b/mt7915/eeprom.h
-index 99101f9..70fca0b 100644
+index 99101f91..70fca0b3 100644
 --- a/mt7915/eeprom.h
 +++ b/mt7915/eeprom.h
 @@ -68,6 +68,8 @@ enum mt7915_eeprom_field {
@@ -322,7 +322,7 @@
  	MT7976_ONE_ADIE_DBDC = 0x7,
  	MT7975_ONE_ADIE	= 0x8,
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index cf622de..7bc5182 100644
+index 3e4239f6..85112791 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -2968,6 +2968,7 @@ int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset, u8 *read_buf)
@@ -353,10 +353,10 @@
  	dev_kfree_skb(skb);
  
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 3fe901d..5ba6986 100644
+index 6027e7f7..7d068e1e 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
-@@ -581,6 +581,7 @@ u32 mt7915_wed_init_buf(void *ptr, dma_addr_t phys, int token_id);
+@@ -579,6 +579,7 @@ u32 mt7915_wed_init_buf(void *ptr, dma_addr_t phys, int token_id);
  
  int mt7915_register_device(struct mt7915_dev *dev);
  void mt7915_unregister_device(struct mt7915_dev *dev);
diff --git a/recipes-wifi/linux-mt76/files/patches/1021-wifi-mt76-mt7915-support-on-off-SW-ACI-through-debug.patch b/recipes-wifi/linux-mt76/files/patches/1021-wifi-mt76-mt7915-support-on-off-SW-ACI-through-debug.patch
index 11f05a7..610fecd 100644
--- a/recipes-wifi/linux-mt76/files/patches/1021-wifi-mt76-mt7915-support-on-off-SW-ACI-through-debug.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1021-wifi-mt76-mt7915-support-on-off-SW-ACI-through-debug.patch
@@ -1,7 +1,7 @@
-From 615af047639896e1d92edf7c18322a1a6d993e92 Mon Sep 17 00:00:00 2001
+From cf95b91353888672b8fcf839592760de4d5f93ac Mon Sep 17 00:00:00 2001
 From: Evelyn Tsai <evelyn.tsai@mediatek.com>
 Date: Fri, 14 Oct 2022 11:15:13 +0800
-Subject: [PATCH 1021/1051] wifi: mt76: mt7915: support on off SW ACI through
+Subject: [PATCH 1021/1052] wifi: mt76: mt7915: support on off SW ACI through
  debugfs
 
 Signed-off-by: Evelyn Tsai <evelyn.tsai@mediatek.com>
@@ -11,10 +11,10 @@
  2 files changed, 22 insertions(+)
 
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 7e12c05..94fcf32 100644
+index e7eb6a93..49c3f1aa 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
-@@ -1253,6 +1253,7 @@ enum {
+@@ -1255,6 +1255,7 @@ enum {
  	MCU_EXT_CMD_SET_QOS_MAP = 0xb4,
  	MCU_EXT_CMD_SET_CFG = 0xb7,
  	MCU_EXT_CMD_EDCCA = 0xba,
@@ -23,7 +23,7 @@
  	MCU_EXT_CMD_IPI_HIST_SCAN = 0xc5,
  };
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index 17577fd..8f2f496 100644
+index 76f4849e..3e84d753 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -3770,6 +3770,25 @@ static int mt7915_show_eeprom_mode(struct seq_file *s, void *data)
diff --git a/recipes-wifi/linux-mt76/files/patches/1022-wifi-mt76-mt7915-add-bf-backoff-limit-table-support.patch b/recipes-wifi/linux-mt76/files/patches/1022-wifi-mt76-mt7915-add-bf-backoff-limit-table-support.patch
index 62eec89..100d631 100644
--- a/recipes-wifi/linux-mt76/files/patches/1022-wifi-mt76-mt7915-add-bf-backoff-limit-table-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1022-wifi-mt76-mt7915-add-bf-backoff-limit-table-support.patch
@@ -1,7 +1,7 @@
-From 942b504ff8a04d6faf2023418946be2167bd67d0 Mon Sep 17 00:00:00 2001
+From 94af1c45316f8510f60afd0348918345757f1c20 Mon Sep 17 00:00:00 2001
 From: Shayne Chen <shayne.chen@mediatek.com>
 Date: Mon, 5 Dec 2022 18:21:51 +0800
-Subject: [PATCH 1022/1051] wifi: mt76: mt7915: add bf backoff limit table
+Subject: [PATCH 1022/1052] wifi: mt76: mt7915: add bf backoff limit table
  support
 
 Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
@@ -18,7 +18,7 @@
  9 files changed, 275 insertions(+), 54 deletions(-)
 
 diff --git a/debugfs.c b/debugfs.c
-index 1c8328d..a626f7c 100644
+index 1c8328d5..a626f7cf 100644
 --- a/debugfs.c
 +++ b/debugfs.c
 @@ -95,9 +95,9 @@ void mt76_seq_puts_array(struct seq_file *file, const char *str,
@@ -34,7 +34,7 @@
  }
  EXPORT_SYMBOL_GPL(mt76_seq_puts_array);
 diff --git a/eeprom.c b/eeprom.c
-index 9d029c0..aa33e7b 100644
+index 9d029c04..aa33e7b5 100644
 --- a/eeprom.c
 +++ b/eeprom.c
 @@ -336,9 +336,10 @@ mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const __be32 *data,
@@ -115,7 +115,7 @@
  	return max_power;
  }
 diff --git a/mt76.h b/mt76.h
-index fbcdfa9..54f805e 100644
+index 253a564f..580320fd 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -1089,6 +1089,14 @@ struct mt76_power_limits {
@@ -134,7 +134,7 @@
  
  struct mt76_ethtool_worker_info {
 diff --git a/mt7915/debugfs.c b/mt7915/debugfs.c
-index 5c08910..fa1d2ac 100644
+index c3692969..3830a735 100644
 --- a/mt7915/debugfs.c
 +++ b/mt7915/debugfs.c
 @@ -1020,7 +1020,7 @@ mt7915_rate_txpower_get(struct file *file, char __user *user_buf,
@@ -234,7 +234,7 @@
  static int
  mt7915_twt_stats(struct seq_file *s, void *data)
  {
-@@ -1310,7 +1373,9 @@ int mt7915_init_debugfs(struct mt7915_phy *phy)
+@@ -1355,7 +1418,9 @@ int mt7915_init_debugfs(struct mt7915_phy *phy)
  	debugfs_create_file("implicit_txbf", 0600, dir, dev,
  			    &fops_implicit_txbf);
  	debugfs_create_file("txpower_sku", 0400, dir, phy,
@@ -246,10 +246,10 @@
  				    mt7915_twt_stats);
  	debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval);
 diff --git a/mt7915/init.c b/mt7915/init.c
-index 0994b8a..f548d09 100644
+index 0c58ab7b..a9cb496b 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
-@@ -284,6 +284,8 @@ static void __mt7915_init_txpower(struct mt7915_phy *phy,
+@@ -285,6 +285,8 @@ static void __mt7915_init_txpower(struct mt7915_phy *phy,
  	int pwr_delta = mt7915_eeprom_get_power_delta(dev, sband->band);
  	struct mt76_power_limits limits;
  
@@ -258,7 +258,7 @@
  	for (i = 0; i < sband->n_channels; i++) {
  		struct ieee80211_channel *chan = &sband->channels[i];
  		u32 target_power = 0;
-@@ -300,6 +302,11 @@ static void __mt7915_init_txpower(struct mt7915_phy *phy,
+@@ -301,6 +303,11 @@ static void __mt7915_init_txpower(struct mt7915_phy *phy,
  		target_power = mt76_get_rate_power_limits(phy->mt76, chan,
  							  &limits,
  							  target_power);
@@ -271,7 +271,7 @@
  		target_power = DIV_ROUND_UP(target_power, 2);
  		chan->max_power = min_t(int, chan->max_reg_power,
 diff --git a/mt7915/main.c b/mt7915/main.c
-index f927c5a..4e1a430 100644
+index de2f9098..4a5a0155 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -73,11 +73,7 @@ int mt7915_run(struct ieee80211_hw *hw)
@@ -288,7 +288,7 @@
  		goto out;
  
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 372168d..4225e97 100644
+index 85112791..1b5ef875 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -3422,7 +3422,8 @@ int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy,
@@ -536,7 +536,7 @@
  	return mt76_mcu_send_msg(&dev->mt76,
  				 MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req,
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index 1b0bd06..94eff26 100644
+index 1b0bd06b..94eff268 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -517,12 +517,18 @@ enum {
@@ -559,7 +559,7 @@
  	SPR_ENABLE = 0x1,
  	SPR_ENABLE_SD = 0x3,
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index d32b12f..c595d81 100644
+index 7d068e1e..437cfb63 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -72,6 +72,7 @@
@@ -570,7 +570,7 @@
  
  #define MT7915_MAX_TWT_AGRT		16
  #define MT7915_MAX_STA_TWT_AGRT		8
-@@ -302,6 +303,9 @@ struct mt7915_phy {
+@@ -313,6 +314,9 @@ struct mt7915_phy {
  	struct list_head stats_list;
  	spinlock_t stats_lock;
  
@@ -580,7 +580,7 @@
  #ifdef CONFIG_NL80211_TESTMODE
  	struct {
  		u32 *reg_backup;
-@@ -628,9 +632,10 @@ int mt7915_mcu_set_mac(struct mt7915_dev *dev, int band, bool enable,
+@@ -643,9 +647,10 @@ int mt7915_mcu_set_mac(struct mt7915_dev *dev, int band, bool enable,
  int mt7915_mcu_set_test_param(struct mt7915_dev *dev, u8 param, bool test_mode,
  			      u8 en);
  int mt7915_mcu_set_ser(struct mt7915_dev *dev, u8 action, u8 set, u8 band);
diff --git a/recipes-wifi/linux-mt76/files/patches/1023-wifi-mt76-mt7915-amsdu-set-and-get-control.patch b/recipes-wifi/linux-mt76/files/patches/1023-wifi-mt76-mt7915-amsdu-set-and-get-control.patch
index c95aa26..72fd7b9 100644
--- a/recipes-wifi/linux-mt76/files/patches/1023-wifi-mt76-mt7915-amsdu-set-and-get-control.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1023-wifi-mt76-mt7915-amsdu-set-and-get-control.patch
@@ -1,7 +1,7 @@
-From 0f64ab075028815e91162e78ba308d5022b89c85 Mon Sep 17 00:00:00 2001
+From 71155a316facf65574dc5a03cc6c70a824c3d220 Mon Sep 17 00:00:00 2001
 From: TomLiu <tomml.liu@mediatek.com>
 Date: Wed, 14 Dec 2022 00:44:07 -0800
-Subject: [PATCH 1023/1051] wifi: mt76: mt7915: amsdu set and get control
+Subject: [PATCH 1023/1052] wifi: mt76: mt7915: amsdu set and get control
 
 ---
  mt7915/mac.c    |  7 +++++++
@@ -11,7 +11,7 @@
  4 files changed, 50 insertions(+)
 
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index dc75ff1..e14b3fd 100644
+index dc75ff1f..e14b3fdd 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -1994,6 +1994,13 @@ static void mt7915_mac_sta_stats_work(struct mt7915_phy *phy)
@@ -29,10 +29,10 @@
  void mt7915_capi_sta_rc_work(void *data, struct ieee80211_sta *sta)
  {
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index c595d81..804d554 100644
+index 437cfb63..82d374eb 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
-@@ -761,6 +761,7 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr,
+@@ -775,6 +775,7 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr,
  			 bool pci, int *irq);
  
  #ifdef CONFIG_MTK_VENDOR
@@ -41,10 +41,10 @@
  void mt7915_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
  void mt7915_mcu_set_rfeature_starec(void *data, struct mt7915_dev *dev,
 diff --git a/mt7915/vendor.c b/mt7915/vendor.c
-index a21cbce..e25a0ce 100644
+index b4facaaa..4848e0a1 100644
 --- a/mt7915/vendor.c
 +++ b/mt7915/vendor.c
-@@ -31,10 +31,16 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
+@@ -32,10 +32,16 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
  	[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA] = {.type = NLA_U8 },
  	[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO] = {.type = NLA_U8 },
  	[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE] = {.type = NLA_U16 },
@@ -61,7 +61,7 @@
  static const struct nla_policy
  mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
  	[MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
-@@ -1004,11 +1010,34 @@ static int mt7915_vendor_wireless_ctrl(struct wiphy *wiphy,
+@@ -1140,11 +1146,34 @@ static int mt7915_vendor_wireless_ctrl(struct wiphy *wiphy,
  		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]);
  		mt7915_mcu_set_cfg(phy, CFGINFO_CERT_CFG, val8); /* Cert Enable for OMI */
  		mt7915_mcu_set_bypass_smthint(phy, val8); /* Cert bypass smooth interpolation */
@@ -96,7 +96,7 @@
  static int mt7915_vendor_mu_ctrl(struct wiphy *wiphy,
  				  struct wireless_dev *wdev,
  				  const void *data,
-@@ -1307,6 +1336,7 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
+@@ -1443,6 +1472,7 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
  		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
  			WIPHY_VENDOR_CMD_NEED_RUNNING,
  		.doit = mt7915_vendor_wireless_ctrl,
@@ -105,7 +105,7 @@
  		.maxattr = MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX,
  	},
 diff --git a/mt7915/vendor.h b/mt7915/vendor.h
-index 876edf3..7c4e914 100644
+index 24f35c8e..8a13b3a3 100644
 --- a/mt7915/vendor.h
 +++ b/mt7915/vendor.h
 @@ -75,6 +75,7 @@ enum mtk_vendor_attr_wireless_ctrl {
diff --git a/recipes-wifi/linux-mt76/files/patches/1024-wifi-mt76-mt7915-Add-vendor-command-attribute-for-RT.patch b/recipes-wifi/linux-mt76/files/patches/1024-wifi-mt76-mt7915-Add-vendor-command-attribute-for-RT.patch
index 7951f6c..153f1e3 100644
--- a/recipes-wifi/linux-mt76/files/patches/1024-wifi-mt76-mt7915-Add-vendor-command-attribute-for-RT.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1024-wifi-mt76-mt7915-Add-vendor-command-attribute-for-RT.patch
@@ -1,7 +1,7 @@
-From 0b4edb9d0fa52ebe9bf4a1c1f152515296afc657 Mon Sep 17 00:00:00 2001
+From fbbc0bb4591648f645ed1b0b43f77e9922ecdfdd Mon Sep 17 00:00:00 2001
 From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
 Date: Tue, 24 Jan 2023 14:32:08 +0800
-Subject: [PATCH 1024/1051] wifi: mt76: mt7915: Add vendor command attribute
+Subject: [PATCH 1024/1052] wifi: mt76: mt7915: Add vendor command attribute
  for RTS BW signaling.
 
 Signed-off-by: himanshu.goyal <himanshu.goyal@mediatek.com>
@@ -13,7 +13,7 @@
  4 files changed, 20 insertions(+)
 
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 4225e97..2e6eefc 100644
+index 1b5ef875..ca18acfb 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -4863,6 +4863,12 @@ int mt7915_mcu_set_cfg(struct mt7915_phy *phy, u8 cfg_info, u8 type)
@@ -30,7 +30,7 @@
  		tlv_len = sizeof(struct three_wire_cfg);
  		req.three_wire.tag = cpu_to_le16(cfg_info);
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index 94eff26..6ebcce0 100644
+index 94eff268..6ebcce0d 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -936,6 +936,13 @@ struct three_wire_cfg {
@@ -62,10 +62,10 @@
  };
  
 diff --git a/mt7915/vendor.c b/mt7915/vendor.c
-index e25a0ce..8370216 100644
+index 4848e0a1..35891d49 100644
 --- a/mt7915/vendor.c
 +++ b/mt7915/vendor.c
-@@ -34,6 +34,7 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
+@@ -35,6 +35,7 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
  	[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU] = {.type = NLA_U8 },
  	[MTK_VENDOR_ATTR_WIRELESS_CTRL_MU_EDCA] = {.type = NLA_U8 },
  	[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
@@ -73,7 +73,7 @@
  };
  
  static const struct nla_policy
-@@ -1013,6 +1014,9 @@ static int mt7915_vendor_wireless_ctrl(struct wiphy *wiphy,
+@@ -1149,6 +1150,9 @@ static int mt7915_vendor_wireless_ctrl(struct wiphy *wiphy,
  	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU]) {
  		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU]);
  		mt7915_set_wireless_amsdu(hw, val8);
@@ -84,7 +84,7 @@
  
  	return 0;
 diff --git a/mt7915/vendor.h b/mt7915/vendor.h
-index 7c4e914..3672420 100644
+index 8a13b3a3..f91ad695 100644
 --- a/mt7915/vendor.h
 +++ b/mt7915/vendor.h
 @@ -77,6 +77,7 @@ enum mtk_vendor_attr_wireless_ctrl {
diff --git a/recipes-wifi/linux-mt76/files/patches/1025-wifi-mt76-mt7915-add-vendor-cmd-to-get-available-col.patch b/recipes-wifi/linux-mt76/files/patches/1025-wifi-mt76-mt7915-add-vendor-cmd-to-get-available-col.patch
index 9522fb0..dc85870 100644
--- a/recipes-wifi/linux-mt76/files/patches/1025-wifi-mt76-mt7915-add-vendor-cmd-to-get-available-col.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1025-wifi-mt76-mt7915-add-vendor-cmd-to-get-available-col.patch
@@ -1,7 +1,7 @@
-From 399d30ba0e363d9c2f8a438fe2ed2154e6a7a02f Mon Sep 17 00:00:00 2001
+From a48f497ea998ba30f4ee8ab7e5c41e0eb7738119 Mon Sep 17 00:00:00 2001
 From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
 Date: Thu, 26 Jan 2023 08:50:47 +0800
-Subject: [PATCH 1025/1051] wifi: mt76: mt7915: add vendor cmd to get available
+Subject: [PATCH 1025/1052] wifi: mt76: mt7915: add vendor cmd to get available
  color bitmap
 
 Add a vendor cmd to notify user space available color bitmap.
@@ -14,10 +14,10 @@
  2 files changed, 48 insertions(+)
 
 diff --git a/mt7915/vendor.c b/mt7915/vendor.c
-index 8370216..9a26f7f 100644
+index 35891d49..df08704b 100644
 --- a/mt7915/vendor.c
 +++ b/mt7915/vendor.c
-@@ -99,6 +99,11 @@ ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
+@@ -100,6 +100,11 @@ ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
  	[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
  };
  
@@ -29,7 +29,7 @@
  struct csi_null_tone {
  	u8 start;
  	u8 end;
-@@ -1295,6 +1300,27 @@ mt7915_vendor_ibf_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
+@@ -1431,6 +1436,27 @@ mt7915_vendor_ibf_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
  	return 1;
  }
  
@@ -57,7 +57,7 @@
  
  static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
  	{
-@@ -1401,6 +1427,17 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
+@@ -1537,6 +1563,17 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
  		.dumpit = mt7915_vendor_ibf_ctrl_dump,
  		.policy = ibf_ctrl_policy,
  		.maxattr = MTK_VENDOR_ATTR_IBF_CTRL_MAX,
@@ -76,7 +76,7 @@
  };
  
 diff --git a/mt7915/vendor.h b/mt7915/vendor.h
-index 3672420..bd1c617 100644
+index f91ad695..5b8d99bf 100644
 --- a/mt7915/vendor.h
 +++ b/mt7915/vendor.h
 @@ -15,6 +15,7 @@ enum mtk_nl80211_vendor_subcmds {
@@ -87,7 +87,7 @@
  };
  
  
-@@ -261,4 +262,14 @@ enum mtk_vendor_attr_ibf_dump {
+@@ -274,4 +275,14 @@ enum mtk_vendor_attr_ibf_dump {
  		NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
  };
  
diff --git a/recipes-wifi/linux-mt76/files/patches/1026-wifi-mt76-mt7915-disable-SW-ACI-by-default.patch b/recipes-wifi/linux-mt76/files/patches/1026-wifi-mt76-mt7915-disable-SW-ACI-by-default.patch
index e96992e..fc382a9 100644
--- a/recipes-wifi/linux-mt76/files/patches/1026-wifi-mt76-mt7915-disable-SW-ACI-by-default.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1026-wifi-mt76-mt7915-disable-SW-ACI-by-default.patch
@@ -1,7 +1,7 @@
-From 7f50bd85cb6e13254fa6e72667e1b34599c64f27 Mon Sep 17 00:00:00 2001
+From 252b23b1e979d3b44ca148407d0166c9b618c9e2 Mon Sep 17 00:00:00 2001
 From: Howard Hsu <howard-yh.hsu@mediatek.com>
 Date: Fri, 24 Feb 2023 16:29:42 +0800
-Subject: [PATCH] wifi: mt76: mt7915: disable SW-ACI by default
+Subject: [PATCH 1026/1052] wifi: mt76: mt7915: disable SW-ACI by default
 
 Support to enable/disable SW-ACI by module parameter "sw_aci_enable".
 SW-ACI feature is disable by default.
@@ -13,7 +13,7 @@
  4 files changed, 29 insertions(+), 9 deletions(-)
 
 diff --git a/mt7915/main.c b/mt7915/main.c
-index 9fa01e9..05076b3 100644
+index 4a5a0155..75042189 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -8,6 +8,10 @@
@@ -39,7 +39,7 @@
  
  	if (phy != &dev->phy) {
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 8e895b3..236f666 100644
+index ca18acfb..7a7b3bac 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -5222,3 +5222,18 @@ int mt7915_mcu_get_edcca(struct mt7915_phy *phy, u8 mode, s8 *value)
@@ -62,7 +62,7 @@
 +				 sizeof(req), NULL);
 +}
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 82d374e..3fa3257 100644
+index 82d374eb..3fa32574 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -801,6 +801,7 @@ int mt7915_vendor_amnt_sta_remove(struct mt7915_phy *phy,
@@ -74,7 +74,7 @@
  int mt7915_mcu_ipi_hist_scan(struct mt7915_phy *phy, void *data, u8 mode, bool wait_resp);
  
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index 3e84d75..d932360 100644
+index 3e84d753..d9323603 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -3773,16 +3773,12 @@ static int mt7915_show_eeprom_mode(struct seq_file *s, void *data)
diff --git a/recipes-wifi/linux-mt76/files/patches/1027-wifi-mt76-mt7915-add-muru-user-number-debug-command.patch b/recipes-wifi/linux-mt76/files/patches/1027-wifi-mt76-mt7915-add-muru-user-number-debug-command.patch
index b0a287b..0cfc758 100644
--- a/recipes-wifi/linux-mt76/files/patches/1027-wifi-mt76-mt7915-add-muru-user-number-debug-command.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1027-wifi-mt76-mt7915-add-muru-user-number-debug-command.patch
@@ -1,7 +1,7 @@
-From 37b9de2d5d24fbdb33192977ce5d33d0ad7f2c0a Mon Sep 17 00:00:00 2001
+From ffe8743a0ee8310def4abad0968fee55cf409ea6 Mon Sep 17 00:00:00 2001
 From: MeiChia Chiu <meichia.chiu@mediatek.com>
 Date: Thu, 27 Apr 2023 15:37:33 +0800
-Subject: [PATCH 1027/1051] wifi: mt76: mt7915: add muru user number debug
+Subject: [PATCH 1027/1052] wifi: mt76: mt7915: add muru user number debug
  command
 
 ---
@@ -11,10 +11,10 @@
  3 files changed, 17 insertions(+), 1 deletion(-)
 
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 498674d..24dc022 100644
+index 3fa32574..c745b31d 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
-@@ -647,6 +647,7 @@ int mt7915_mcu_set_pulse_th(struct mt7915_dev *dev,
+@@ -662,6 +662,7 @@ int mt7915_mcu_set_pulse_th(struct mt7915_dev *dev,
  int mt7915_mcu_set_radar_th(struct mt7915_dev *dev, int index,
  			    const struct mt7915_dfs_pattern *pattern);
  int mt7915_mcu_set_muru_ctrl(struct mt7915_dev *dev, u32 cmd, u32 val);
@@ -23,10 +23,10 @@
  int mt7915_mcu_apply_tx_dpd(struct mt7915_phy *phy);
  int mt7915_mcu_get_chan_mib_info(struct mt7915_phy *phy, bool chan_switch);
 diff --git a/mt7915/vendor.c b/mt7915/vendor.c
-index 9a26f7f..432d750 100644
+index df08704b..6446439f 100644
 --- a/mt7915/vendor.c
 +++ b/mt7915/vendor.c
-@@ -46,6 +46,8 @@ static const struct nla_policy
+@@ -47,6 +47,8 @@ static const struct nla_policy
  mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
  	[MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
  	[MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
@@ -35,7 +35,7 @@
  };
  
  static const struct nla_policy
-@@ -1053,9 +1055,10 @@ static int mt7915_vendor_mu_ctrl(struct wiphy *wiphy,
+@@ -1189,9 +1191,10 @@ static int mt7915_vendor_mu_ctrl(struct wiphy *wiphy,
  				  int data_len)
  {
  	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
@@ -47,7 +47,7 @@
  	u32 val32 = 0;
  
  	err = nla_parse(tb, MTK_VENDOR_ATTR_MU_CTRL_MAX, data, data_len,
-@@ -1069,6 +1072,16 @@ static int mt7915_vendor_mu_ctrl(struct wiphy *wiphy,
+@@ -1205,6 +1208,16 @@ static int mt7915_vendor_mu_ctrl(struct wiphy *wiphy,
  			 FIELD_PREP(RATE_CFG_VAL, val8);
  		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
  			mt7915_set_wireless_vif, &val32);
@@ -65,7 +65,7 @@
  
  	return 0;
 diff --git a/mt7915/vendor.h b/mt7915/vendor.h
-index bd1c617..03d1660 100644
+index 5b8d99bf..11ccd0d8 100644
 --- a/mt7915/vendor.h
 +++ b/mt7915/vendor.h
 @@ -103,6 +103,8 @@ enum mtk_vendor_attr_mu_ctrl {
diff --git a/recipes-wifi/linux-mt76/files/patches/1028-wifi-mt76-mt7915-add-debugfs-for-fw-coredump.patch b/recipes-wifi/linux-mt76/files/patches/1028-wifi-mt76-mt7915-add-debugfs-for-fw-coredump.patch
index a2d4af1..650f7de 100644
--- a/recipes-wifi/linux-mt76/files/patches/1028-wifi-mt76-mt7915-add-debugfs-for-fw-coredump.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1028-wifi-mt76-mt7915-add-debugfs-for-fw-coredump.patch
@@ -1,7 +1,7 @@
-From ef06e880955373d91da964fe8bda86e12277ec55 Mon Sep 17 00:00:00 2001
+From 15cd113a03288b74f6486c898378d61b2204b09e Mon Sep 17 00:00:00 2001
 From: Bo Jiao <Bo.Jiao@mediatek.com>
 Date: Mon, 22 May 2023 15:30:21 +0800
-Subject: [PATCH 1028/1051] wifi: mt76: mt7915: add debugfs for fw coredump.
+Subject: [PATCH 1028/1052] wifi: mt76: mt7915: add debugfs for fw coredump.
 
 Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
 ---
@@ -12,7 +12,7 @@
  4 files changed, 58 insertions(+), 9 deletions(-)
 
 diff --git a/mt7915/debugfs.c b/mt7915/debugfs.c
-index fa1d2ac..3044557 100644
+index 3830a735..06c62dd3 100644
 --- a/mt7915/debugfs.c
 +++ b/mt7915/debugfs.c
 @@ -82,8 +82,10 @@ mt7915_sys_recovery_set(struct file *file, const char __user *user_buf,
@@ -67,7 +67,7 @@
  	/* SER statistics */
  	desc += scnprintf(buff + desc, bufsz - desc,
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index e14b3fd..35e97f8 100644
+index e14b3fdd..35e97f88 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -1687,10 +1687,34 @@ void mt7915_mac_dump_work(struct work_struct *work)
@@ -117,7 +117,7 @@
  	}
  
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index 6ebcce0..035ad97 100644
+index 6ebcce0d..035ad97d 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -760,8 +760,12 @@ enum {
@@ -135,7 +135,7 @@
  	SER_ENABLE = 2,
  	SER_RECOVER
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 24dc022..6317051 100644
+index c745b31d..ef51d6e2 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -91,6 +91,13 @@ struct mt7915_sta;
@@ -152,7 +152,7 @@
  enum mt7915_txq_id {
  	MT7915_TXQ_FWDL = 16,
  	MT7915_TXQ_MCU_WM,
-@@ -389,6 +396,7 @@ struct mt7915_dev {
+@@ -404,6 +411,7 @@ struct mt7915_dev {
  
  	/* protects coredump data */
  	struct mutex dump_mutex;
@@ -160,7 +160,7 @@
  #ifdef CONFIG_DEV_COREDUMP
  	struct {
  		struct mt7915_crash_data *crash_data[__MT76_RAM_TYPE_MAX];
-@@ -585,6 +593,7 @@ int mt7915_txbf_init(struct mt7915_dev *dev);
+@@ -600,6 +608,7 @@ int mt7915_txbf_init(struct mt7915_dev *dev);
  void mt7915_init_txpower(struct mt7915_phy *phy);
  int mt7915_init_vif(struct mt7915_phy *phy, struct ieee80211_vif *vif, bool bf_en);
  void mt7915_reset(struct mt7915_dev *dev);
diff --git a/recipes-wifi/linux-mt76/files/patches/1029-wifi-mt76-mt7915-remove-BW160-support.patch b/recipes-wifi/linux-mt76/files/patches/1029-wifi-mt76-mt7915-remove-BW160-support.patch
index 81d26e8..ab2f5d5 100644
--- a/recipes-wifi/linux-mt76/files/patches/1029-wifi-mt76-mt7915-remove-BW160-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1029-wifi-mt76-mt7915-remove-BW160-support.patch
@@ -1,7 +1,7 @@
-From 7c0b8498964ddc1708cd5fd949e70d70d7cc7207 Mon Sep 17 00:00:00 2001
+From 1ffedbe219a388f3b76a717cbf2d42e6ba9b25f2 Mon Sep 17 00:00:00 2001
 From: MeiChia Chiu <meichia.chiu@mediatek.com>
 Date: Wed, 24 May 2023 22:35:54 +0800
-Subject: [PATCH 1029/1051] wifi: mt76: mt7915: remove BW160 support
+Subject: [PATCH 1029/1052] wifi: mt76: mt7915: remove BW160 support
 
 Remove BW160 capability in mt7915.
 ---
@@ -9,10 +9,10 @@
  1 file changed, 6 insertions(+), 20 deletions(-)
 
 diff --git a/mt7915/init.c b/mt7915/init.c
-index f548d09..20d6efd 100644
+index a9cb496b..a77078f7 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
-@@ -440,11 +440,6 @@ mt7915_init_wiphy(struct mt7915_phy *phy)
+@@ -441,11 +441,6 @@ mt7915_init_wiphy(struct mt7915_phy *phy)
  			vht_cap->cap |=
  				IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 |
  				IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
@@ -24,7 +24,7 @@
  		} else {
  			phy->mt76->sband_5g.sband.ht_cap.ampdu_density =
  				IEEE80211_HT_MPDU_DENSITY_2;
-@@ -865,13 +860,9 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_phy *phy,
+@@ -873,13 +868,9 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_phy *phy,
  	int sts = hweight8(phy->mt76->chainmask);
  	u8 c, sts_160 = sts;
  
@@ -41,7 +41,7 @@
  
  #ifdef CONFIG_MAC80211_MESH
  	if (vif == NL80211_IFTYPE_MESH_POINT)
-@@ -955,15 +946,10 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band,
+@@ -963,15 +954,10 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band,
  	int i, idx = 0, nss = hweight8(phy->mt76->antenna_mask);
  	u16 mcs_map = 0;
  	u16 mcs_map_160 = 0;
diff --git a/recipes-wifi/linux-mt76/files/patches/1030-wifi-mt76-mt7915-add-txpower-info-dump-support.patch b/recipes-wifi/linux-mt76/files/patches/1030-wifi-mt76-mt7915-add-txpower-info-dump-support.patch
index b0f9ac0..c7b206b 100644
--- a/recipes-wifi/linux-mt76/files/patches/1030-wifi-mt76-mt7915-add-txpower-info-dump-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1030-wifi-mt76-mt7915-add-txpower-info-dump-support.patch
@@ -1,7 +1,7 @@
-From dc97f22df64689fe6d5cf9e410437c8d9510a41c Mon Sep 17 00:00:00 2001
+From ac55beffc7c153e47a63dd889a80f90656afa5a4 Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Tue, 11 Jul 2023 17:06:04 +0800
-Subject: [PATCH 1030/1051] wifi: mt76: mt7915: add txpower info dump support
+Subject: [PATCH 1030/1052] wifi: mt76: mt7915: add txpower info dump support
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 ---
@@ -11,7 +11,7 @@
  3 files changed, 91 insertions(+), 1 deletion(-)
 
 diff --git a/mt7915/debugfs.c b/mt7915/debugfs.c
-index 3044557..24e88f7 100644
+index 06c62dd3..223f9a37 100644
 --- a/mt7915/debugfs.c
 +++ b/mt7915/debugfs.c
 @@ -1259,6 +1259,91 @@ mt7915_txpower_path_show(struct seq_file *file, void *data)
@@ -106,7 +106,7 @@
  static int
  mt7915_twt_stats(struct seq_file *s, void *data)
  {
-@@ -1388,6 +1473,8 @@ int mt7915_init_debugfs(struct mt7915_phy *phy)
+@@ -1433,6 +1518,8 @@ int mt7915_init_debugfs(struct mt7915_phy *phy)
  			    &mt7915_txpower_fops);
  	debugfs_create_file("txpower_path", 0400, dir, phy,
  			    &mt7915_txpower_path_fops);
@@ -116,7 +116,7 @@
  				    mt7915_twt_stats);
  	debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval);
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 7039933..92c0a1e 100644
+index 7a7b3bac..34d36e50 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -3624,6 +3624,8 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len,
@@ -129,7 +129,7 @@
  
  	dev_kfree_skb(skb);
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index 035ad97..3089fb6 100644
+index 035ad97d..3089fb64 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -525,7 +525,8 @@ enum {
diff --git a/recipes-wifi/linux-mt76/files/patches/1031-wifi-mt76-mt7915-report-tx-and-rx-byte-to-tpt_led-wh.patch b/recipes-wifi/linux-mt76/files/patches/1031-wifi-mt76-mt7915-report-tx-and-rx-byte-to-tpt_led-wh.patch
index ac73e2d..2f9f4d1 100644
--- a/recipes-wifi/linux-mt76/files/patches/1031-wifi-mt76-mt7915-report-tx-and-rx-byte-to-tpt_led-wh.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1031-wifi-mt76-mt7915-report-tx-and-rx-byte-to-tpt_led-wh.patch
@@ -1,7 +1,7 @@
-From d2be9fd8f4889bfde955ab9aeb865d4359e0edd5 Mon Sep 17 00:00:00 2001
+From 693f54f4538631b84be41befd324f642182d37ca Mon Sep 17 00:00:00 2001
 From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
 Date: Fri, 23 Jun 2023 06:06:21 +0800
-Subject: [PATCH 1031/1051] wifi: mt76: mt7915: report tx and rx byte to
+Subject: [PATCH 1031/1052] wifi: mt76: mt7915: report tx and rx byte to
  tpt_led when wed is enabled
 
 Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
@@ -11,7 +11,7 @@
  2 files changed, 11 insertions(+), 4 deletions(-)
 
 diff --git a/mt76_connac_mac.c b/mt76_connac_mac.c
-index 170ef36..6eeea97 100644
+index 170ef367..6eeea971 100644
 --- a/mt76_connac_mac.c
 +++ b/mt76_connac_mac.c
 @@ -605,9 +605,15 @@ bool mt76_connac2_mac_fill_txs(struct mt76_dev *dev, struct mt76_wcid *wcid,
@@ -42,7 +42,7 @@
  			sband = &mphy->sband_5g.sband;
  		else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ)
 diff --git a/mt7915/mmio.c b/mt7915/mmio.c
-index ddf1b72..437a9b0 100644
+index ed15d711..b1b219ce 100644
 --- a/mt7915/mmio.c
 +++ b/mt7915/mmio.c
 @@ -588,6 +588,7 @@ static void mt7915_mmio_wed_update_rx_stats(struct mtk_wed_device *wed,
diff --git a/recipes-wifi/linux-mt76/files/patches/1032-wifi-mt76-mt7915-Establish-BA-in-VO-queue.patch b/recipes-wifi/linux-mt76/files/patches/1032-wifi-mt76-mt7915-Establish-BA-in-VO-queue.patch
index 9b232bf..a62749d 100644
--- a/recipes-wifi/linux-mt76/files/patches/1032-wifi-mt76-mt7915-Establish-BA-in-VO-queue.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1032-wifi-mt76-mt7915-Establish-BA-in-VO-queue.patch
@@ -1,14 +1,14 @@
-From 372460c5ed7a903c5cc5f814d588df5811df850e Mon Sep 17 00:00:00 2001
+From fcfa851c3dd6782c382dc1890f2623fdc0858f73 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 1032/1051] wifi: mt76: mt7915: Establish BA in VO queue
+Subject: [PATCH 1032/1052] 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 6eeea97..c23d266 100644
+index 6eeea971..c23d266e 100644
 --- a/mt76_connac_mac.c
 +++ b/mt76_connac_mac.c
 @@ -1123,8 +1123,6 @@ void mt76_connac2_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi)
diff --git a/recipes-wifi/linux-mt76/files/patches/1033-wifi-mt76-mt7915-Disable-RegDB-when-enable-single-sk.patch b/recipes-wifi/linux-mt76/files/patches/1033-wifi-mt76-mt7915-Disable-RegDB-when-enable-single-sk.patch
index dc975a6..4ca8bff 100644
--- a/recipes-wifi/linux-mt76/files/patches/1033-wifi-mt76-mt7915-Disable-RegDB-when-enable-single-sk.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1033-wifi-mt76-mt7915-Disable-RegDB-when-enable-single-sk.patch
@@ -1,7 +1,7 @@
-From 1de98ecb4505e3f99759caf4e9df9e263dfa403a Mon Sep 17 00:00:00 2001
+From 6eb114c04f79a4e1642a6743e9895cb8038a4630 Mon Sep 17 00:00:00 2001
 From: "Allen.Ye" <allen.ye@mediatek.com>
 Date: Fri, 11 Aug 2023 16:46:53 +0800
-Subject: [PATCH 1033/1051] wifi: mt76: mt7915: Disable RegDB when enable
+Subject: [PATCH 1033/1052] wifi: mt76: mt7915: Disable RegDB when enable
  single sku
 
 ---
@@ -11,7 +11,7 @@
  3 files changed, 57 insertions(+), 11 deletions(-)
 
 diff --git a/mt7915/debugfs.c b/mt7915/debugfs.c
-index 24e88f7..502b493 100644
+index 223f9a37..2c1e1bea 100644
 --- a/mt7915/debugfs.c
 +++ b/mt7915/debugfs.c
 @@ -1020,10 +1020,16 @@ mt7915_rate_txpower_get(struct file *file, char __user *user_buf,
@@ -40,15 +40,15 @@
 -	      MT_WF_PHY_TPC_CTRL_STAT_MT7916(band);
 +	reg = is_mt7915(&dev->mt76) ? MT_WF_IRPI_TPC_CTRL_STAT(band) :
 +	      MT_WF_IRPI_TPC_CTRL_STAT_MT7916(band);
- 
--	len += scnprintf(buf + len, sz - len, "\nTx power (bbp)  : %6ld\n",
--			 mt76_get_field(dev, reg, MT_WF_PHY_TPC_POWER));
++
 +	len += scnprintf(buf + len, sz - len, "\nTx power (bbp)  : %6ld [0.5 dBm]\n",
 +			 mt76_get_field(dev, reg, MT_WF_IRPI_TPC_POWER));
 +
 +	len += scnprintf(buf + len, sz - len, "RegDB maximum power:\t%d [dBm]\n",
 +			 chan->max_reg_power);
-+
+ 
+-	len += scnprintf(buf + len, sz - len, "\nTx power (bbp)  : %6ld\n",
+-			 mt76_get_field(dev, reg, MT_WF_PHY_TPC_POWER));
 +	if (chan->band == NL80211_BAND_2GHZ)
 +		sband = phy->mt76->sband_2g.sband;
 +	else if (chan->band == NL80211_BAND_5GHZ)
@@ -103,10 +103,10 @@
  	return ret;
  }
 diff --git a/mt7915/init.c b/mt7915/init.c
-index 20d6efd..4fa48fb 100644
+index a77078f7..6f616b54 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
-@@ -283,9 +283,11 @@ static void __mt7915_init_txpower(struct mt7915_phy *phy,
+@@ -284,9 +284,11 @@ static void __mt7915_init_txpower(struct mt7915_phy *phy,
  	int nss_delta = mt76_tx_power_nss_delta(n_chains);
  	int pwr_delta = mt7915_eeprom_get_power_delta(dev, sband->band);
  	struct mt76_power_limits limits;
@@ -118,7 +118,7 @@
  	for (i = 0; i < sband->n_channels; i++) {
  		struct ieee80211_channel *chan = &sband->channels[i];
  		u32 target_power = 0;
-@@ -309,8 +311,13 @@ static void __mt7915_init_txpower(struct mt7915_phy *phy,
+@@ -310,8 +312,13 @@ static void __mt7915_init_txpower(struct mt7915_phy *phy,
  
  		target_power += nss_delta;
  		target_power = DIV_ROUND_UP(target_power, 2);
@@ -135,7 +135,7 @@
  	}
  }
 diff --git a/mt7915/regs.h b/mt7915/regs.h
-index 4d05e39..ca355d1 100644
+index 4d05e391..ca355d14 100644
 --- a/mt7915/regs.h
 +++ b/mt7915/regs.h
 @@ -1215,6 +1215,10 @@ enum offs_rev {
diff --git a/recipes-wifi/linux-mt76/files/patches/1034-wifi-mt76-mt7915-enable-the-mac80211-hw-bmc-ps-buffe.patch b/recipes-wifi/linux-mt76/files/patches/1034-wifi-mt76-mt7915-enable-the-mac80211-hw-bmc-ps-buffe.patch
index 92a60ce..89f875e 100644
--- a/recipes-wifi/linux-mt76/files/patches/1034-wifi-mt76-mt7915-enable-the-mac80211-hw-bmc-ps-buffe.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1034-wifi-mt76-mt7915-enable-the-mac80211-hw-bmc-ps-buffe.patch
@@ -1,7 +1,7 @@
-From fd4211f53ab7973c1875881dcdaa12e252b95670 Mon Sep 17 00:00:00 2001
+From 18fd1219383a2d2f2b0881d87ca8a1552e273bf1 Mon Sep 17 00:00:00 2001
 From: Evelyn Tsai <evelyn.tsai@mediatek.com>
 Date: Thu, 24 Aug 2023 03:01:27 +0800
-Subject: [PATCH 1034/1051] wifi: mt76: mt7915: enable the mac80211 hw bmc ps
+Subject: [PATCH 1034/1052] wifi: mt76: mt7915: enable the mac80211 hw bmc ps
  buffer function.
 
 ---
@@ -9,10 +9,10 @@
  1 file changed, 1 insertion(+)
 
 diff --git a/mt7915/init.c b/mt7915/init.c
-index 4fa48fb..0f8a772 100644
+index 6f616b54..c4685f21 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
-@@ -413,6 +413,7 @@ mt7915_init_wiphy(struct mt7915_phy *phy)
+@@ -414,6 +414,7 @@ mt7915_init_wiphy(struct mt7915_phy *phy)
  	ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD);
  	ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
  	ieee80211_hw_set(hw, WANT_MONITOR_VIF);
diff --git a/recipes-wifi/linux-mt76/files/patches/1035-wifi-mt76-update-debugfs-knob-for-tx-tokens.patch b/recipes-wifi/linux-mt76/files/patches/1035-wifi-mt76-update-debugfs-knob-for-tx-tokens.patch
index cd4a4bb..b41c81d 100644
--- a/recipes-wifi/linux-mt76/files/patches/1035-wifi-mt76-update-debugfs-knob-for-tx-tokens.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1035-wifi-mt76-update-debugfs-knob-for-tx-tokens.patch
@@ -1,7 +1,7 @@
-From cf2a6fb7145d4083bd06558fcad669f91517b6ad Mon Sep 17 00:00:00 2001
+From c324616943e4a51d5d288a26bdbb330be11d2d8f Mon Sep 17 00:00:00 2001
 From: Evelyn Tsai <evelyn.tsai@mediatek.com>
 Date: Thu, 24 Aug 2023 03:01:27 +0800
-Subject: [PATCH 1035/1051] wifi: mt76: update debugfs knob for tx tokens
+Subject: [PATCH 1035/1052] wifi: mt76: update debugfs knob for tx tokens
 
 1. dump token pending time
 2. dump per-band token counts
@@ -14,7 +14,7 @@
  3 files changed, 22 insertions(+), 5 deletions(-)
 
 diff --git a/mt76.h b/mt76.h
-index 54f805e..cff22f5 100644
+index 580320fd..16b76b48 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -403,6 +403,7 @@ struct mt76_txwi_cache {
@@ -26,7 +26,7 @@
  	union {
  		struct sk_buff *skb;
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index 35e97f8..a731446 100644
+index 35e97f88..a7314465 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -811,6 +811,8 @@ int mt7915_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
@@ -39,7 +39,7 @@
  	mt7915_mac_write_txwi(mdev, txwi_ptr, tx_info->skb, wcid, pid, key,
  			      qid, 0);
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index ad7abda..5ce2b93 100644
+index d9323603..2cc0b2d8 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -2203,17 +2203,31 @@ static int mt7915_mibinfo_band1(struct seq_file *s, void *data)
diff --git a/recipes-wifi/linux-mt76/files/patches/1036-wifi-mt76-mt7915-support-enable-disable-spatial-reus.patch b/recipes-wifi/linux-mt76/files/patches/1036-wifi-mt76-mt7915-support-enable-disable-spatial-reus.patch
index e9f3b10..98ae91e 100644
--- a/recipes-wifi/linux-mt76/files/patches/1036-wifi-mt76-mt7915-support-enable-disable-spatial-reus.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1036-wifi-mt76-mt7915-support-enable-disable-spatial-reus.patch
@@ -1,7 +1,7 @@
-From 93a4a8d162937bd5c49aac52d158fe8bda71731a Mon Sep 17 00:00:00 2001
+From 8e5290c521da92f931b169e1b2dccc7125e17253 Mon Sep 17 00:00:00 2001
 From: Howard Hsu <howard-yh.hsu@mediatek.com>
 Date: Tue, 5 Sep 2023 20:17:19 +0800
-Subject: [PATCH 1036/1051] wifi: mt76: mt7915: support enable/disable spatial
+Subject: [PATCH 1036/1052] wifi: mt76: mt7915: support enable/disable spatial
  reuse through debugfs
 
 Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
@@ -12,7 +12,7 @@
  3 files changed, 14 insertions(+), 2 deletions(-)
 
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 92c0a1e..ef9f0ce 100644
+index 34d36e50..40d94c4f 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -3756,8 +3756,7 @@ int mt7915_mcu_set_txbf(struct mt7915_dev *dev, u8 action)
@@ -26,10 +26,10 @@
  	struct mt7915_dev *dev = phy->dev;
  	struct mt7915_mcu_sr_ctrl req = {
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 6317051..3ec65a7 100644
+index ef51d6e2..bc0f313c 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
-@@ -798,6 +798,7 @@ int mt7915_mcu_get_edcca(struct mt7915_phy *phy, u8 mode, s8 *value);
+@@ -814,6 +814,7 @@ int mt7915_mcu_get_edcca(struct mt7915_phy *phy, u8 mode, s8 *value);
  int mt7915_mcu_sw_aci_set(struct mt7915_dev *dev, bool val);
  int mt7915_mcu_ipi_hist_ctrl(struct mt7915_phy *phy, void *data, u8 cmd, bool wait_resp);
  int mt7915_mcu_ipi_hist_scan(struct mt7915_phy *phy, void *data, u8 mode, bool wait_resp);
@@ -38,7 +38,7 @@
  #ifdef MTK_DEBUG
  int mt7915_mtk_init_debugfs(struct mt7915_phy *phy, struct dentry *dir);
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index 5ce2b93..b653c34 100644
+index 2cc0b2d8..c00184f8 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -3799,6 +3799,17 @@ mt7915_sw_aci_set(void *data, u64 val)
diff --git a/recipes-wifi/linux-mt76/files/patches/1037-wifi-mt76-mt7915-add-debug-log-for-SER-flow.patch b/recipes-wifi/linux-mt76/files/patches/1037-wifi-mt76-mt7915-add-debug-log-for-SER-flow.patch
index 966e47d..d3fd14d 100644
--- a/recipes-wifi/linux-mt76/files/patches/1037-wifi-mt76-mt7915-add-debug-log-for-SER-flow.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1037-wifi-mt76-mt7915-add-debug-log-for-SER-flow.patch
@@ -1,7 +1,7 @@
-From 664596920de78c1aa9d1c3dc0e07018ca5c7d43d Mon Sep 17 00:00:00 2001
+From 50046dd2dad794942f0f28fda6ede3b90261e0be Mon Sep 17 00:00:00 2001
 From: Bo Jiao <Bo.Jiao@mediatek.com>
 Date: Mon, 11 Sep 2023 17:11:24 +0800
-Subject: [PATCH 1037/1051] wifi: mt76: mt7915: add debug log for SER flow.
+Subject: [PATCH 1037/1052] wifi: mt76: mt7915: add debug log for SER flow.
 
 Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
 ---
@@ -9,7 +9,7 @@
  1 file changed, 9 insertions(+)
 
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index a731446..c421447 100644
+index a7314465..c421447c 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -1531,6 +1531,9 @@ void mt7915_mac_reset_work(struct work_struct *work)
diff --git a/recipes-wifi/linux-mt76/files/patches/1038-wifi-mt76-mt7915-add-debuffs-knob-for-protect-thresh.patch b/recipes-wifi/linux-mt76/files/patches/1038-wifi-mt76-mt7915-add-debuffs-knob-for-protect-thresh.patch
index b38bc29..a367ad8 100644
--- a/recipes-wifi/linux-mt76/files/patches/1038-wifi-mt76-mt7915-add-debuffs-knob-for-protect-thresh.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1038-wifi-mt76-mt7915-add-debuffs-knob-for-protect-thresh.patch
@@ -1,7 +1,7 @@
-From bbcd1a6d3d84c1cb1a82fe996bf825c11878289c Mon Sep 17 00:00:00 2001
+From 5fa70ee3f31733b9ddee67fdc78fe637df251aca Mon Sep 17 00:00:00 2001
 From: Peter Chiu <chui-hao.chiu@mediatek.com>
 Date: Mon, 2 Oct 2023 14:00:13 +0800
-Subject: [PATCH 1038/1051] wifi: mt76: mt7915: add debuffs knob for protect
+Subject: [PATCH 1038/1052] wifi: mt76: mt7915: add debuffs knob for protect
  threshold
 
 ---
@@ -10,10 +10,10 @@
  2 files changed, 12 insertions(+)
 
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 3ec65a7..3dc99f8 100644
+index bc0f313c..183d59da 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
-@@ -778,6 +778,7 @@ void mt7915_mcu_set_rfeature_starec(void *data, struct mt7915_dev *dev,
+@@ -792,6 +792,7 @@ void mt7915_mcu_set_rfeature_starec(void *data, struct mt7915_dev *dev,
  		       struct ieee80211_vif *vif, struct ieee80211_sta *sta);
  int mt7915_mcu_set_rfeature_trig_type(struct mt7915_phy *phy, u8 enable, u8 trig_type);
  int mt7915_mcu_set_mu_dl_ack_policy(struct mt7915_phy *phy, u8 policy_num);
@@ -22,7 +22,7 @@
  void mt7915_mcu_set_nusers_ofdma(struct mt7915_phy *phy, u8 type, u8 ofdma_user_cnt);
  void mt7915_mcu_set_mimo(struct mt7915_phy *phy, u8 direction);
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index b653c34..dad5ed7 100644
+index c00184f8..c8efd266 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -2852,6 +2852,16 @@ static int mt7915_sta_tx_amsdu_set(void *data, u64 tx_amsdu)
diff --git a/recipes-wifi/linux-mt76/files/patches/1039-wifi-mt76-mt7915-add-mt7981-efuse-variants-support.patch b/recipes-wifi/linux-mt76/files/patches/1039-wifi-mt76-mt7915-add-mt7981-efuse-variants-support.patch
index 2260e5f..39612b0 100644
--- a/recipes-wifi/linux-mt76/files/patches/1039-wifi-mt76-mt7915-add-mt7981-efuse-variants-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1039-wifi-mt76-mt7915-add-mt7981-efuse-variants-support.patch
@@ -1,7 +1,7 @@
-From 51410d3e134142548c1432c0ea5c07635d8646a5 Mon Sep 17 00:00:00 2001
+From 90dfcdd20e564dc3b82342141dfd6917899b7b66 Mon Sep 17 00:00:00 2001
 From: "Henry.Yen" <henry.yen@mediatek.com>
 Date: Mon, 11 Dec 2023 16:01:55 +0800
-Subject: [PATCH 1039/1051] wifi: mt76: mt7915 add mt7981 efuse variants
+Subject: [PATCH 1039/1052] wifi: mt76: mt7915 add mt7981 efuse variants
  support
 
 ---
@@ -10,7 +10,7 @@
  2 files changed, 28 insertions(+), 1 deletion(-)
 
 diff --git a/mt7915/eeprom.c b/mt7915/eeprom.c
-index 6133c20..df5e396 100644
+index 6133c200..df5e396c 100644
 --- a/mt7915/eeprom.c
 +++ b/mt7915/eeprom.c
 @@ -193,6 +193,21 @@ static int mt7915_eeprom_load(struct mt7915_dev *dev)
@@ -50,10 +50,10 @@
  	mt7915_eeprom_parse_band_config(phy);
  
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 3dc99f8..3170aca 100644
+index 183d59da..c8f9ed52 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
-@@ -409,6 +409,7 @@ struct mt7915_dev {
+@@ -424,6 +424,7 @@ struct mt7915_dev {
  
  	u32 hw_pattern;
  
@@ -61,7 +61,7 @@
  	bool dbdc_support;
  	bool flash_mode;
  	bool bin_file_mode;
-@@ -684,7 +685,11 @@ void mt7915_tm_rf_test_event(struct mt7915_dev *dev, struct sk_buff *skb);
+@@ -698,7 +699,11 @@ void mt7915_tm_rf_test_event(struct mt7915_dev *dev, struct sk_buff *skb);
  
  static inline u16 mt7915_wtbl_size(struct mt7915_dev *dev)
  {
diff --git a/recipes-wifi/linux-mt76/files/patches/1040-wifi-mt76-mt7915-support-scs-feature.patch b/recipes-wifi/linux-mt76/files/patches/1040-wifi-mt76-mt7915-support-scs-feature.patch
index e826d19..0f39a4a 100644
--- a/recipes-wifi/linux-mt76/files/patches/1040-wifi-mt76-mt7915-support-scs-feature.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1040-wifi-mt76-mt7915-support-scs-feature.patch
@@ -1,7 +1,7 @@
-From 28300199dbd301df08b570d527aeb76adac50343 Mon Sep 17 00:00:00 2001
+From 5ba9ebc9e86610960d798ff1103350d2d3453a90 Mon Sep 17 00:00:00 2001
 From: Howard Hsu <howard-yh.hsu@mediatek.com>
 Date: Wed, 6 Dec 2023 08:53:03 +0800
-Subject: [PATCH 1040/1041] wifi: mt76: mt7915: support scs feature
+Subject: [PATCH 1040/1052] wifi: mt76: mt7915: support scs feature
 
 Add support scs feature for connac2 codebase. This commit includes three
 parts.
@@ -29,7 +29,7 @@
  9 files changed, 188 insertions(+)
 
 diff --git a/mt76.h b/mt76.h
-index 86e4a60..8450d3b 100644
+index 16b76b48..43f3c282 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -311,6 +311,7 @@ struct mt76_sta_stats {
@@ -49,7 +49,7 @@
  
  enum mt76_wcid_flags {
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index dc2a47d..8b7fe87 100644
+index 49c3f1aa..febe3ed4 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
 @@ -1238,6 +1238,7 @@ enum {
@@ -61,10 +61,10 @@
  	MCU_EXT_CMD_FW_DBG_CTRL = 0x95,
  	MCU_EXT_CMD_OFFCH_SCAN_CTRL = 0x9a,
 diff --git a/mt7915/init.c b/mt7915/init.c
-index 4dbb84a..ec61911 100644
+index c4685f21..a26e0d69 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
-@@ -1219,6 +1219,7 @@ int mt7915_register_device(struct mt7915_dev *dev)
+@@ -1222,6 +1222,7 @@ int mt7915_register_device(struct mt7915_dev *dev)
  	spin_lock_init(&dev->phy.stats_lock);
  	INIT_WORK(&dev->rc_work, mt7915_mac_sta_rc_work);
  	INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7915_mac_work);
@@ -73,7 +73,7 @@
  	INIT_LIST_HEAD(&dev->twt_list);
  
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index c421447..fb98940 100644
+index c421447c..fb989405 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -1463,6 +1463,8 @@ mt7915_mac_full_reset(struct mt7915_dev *dev)
@@ -116,7 +116,7 @@
  		 wiphy_name(dev->mt76.hw->wiphy));
  }
 diff --git a/mt7915/main.c b/mt7915/main.c
-index 05076b3..ba009e6 100644
+index 75042189..04301300 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -89,12 +89,24 @@ int mt7915_run(struct ieee80211_hw *hw)
@@ -153,7 +153,7 @@
  		mt7915_mcu_set_mac(dev, dev->phy.mt76->band_idx, false, false);
  	}
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 052ec99..297ded7 100644
+index 40d94c4f..fcbe4da4 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -5238,3 +5238,121 @@ int mt7915_mcu_sw_aci_set(struct mt7915_dev *dev, bool val)
@@ -279,7 +279,7 @@
 +		ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
 +}
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index 3089fb6..742a785 100644
+index 3089fb64..742a7855 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -1200,4 +1200,8 @@ struct mt7915_mcu_edcca_info {
@@ -292,7 +292,7 @@
 +};
  #endif
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index c8f9ed5..6b27be9 100644
+index c8f9ed52..6b27be9c 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -283,6 +283,15 @@ struct mt7915_air_monitor_ctrl {
@@ -338,7 +338,7 @@
  #ifdef MTK_DEBUG
  int mt7915_mtk_init_debugfs(struct mt7915_phy *phy, struct dentry *dir);
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index c8efd26..e60dc85 100644
+index c8efd266..e60dc850 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -3820,6 +3820,29 @@ mt7915_sr_enable_set(void *data, u64 val)
diff --git a/recipes-wifi/linux-mt76/files/patches/1041-wifi-mt76-mt7915-support-thermal-recal-debug-commnad.patch b/recipes-wifi/linux-mt76/files/patches/1041-wifi-mt76-mt7915-support-thermal-recal-debug-commnad.patch
index 4576a2c..51ad6bc 100644
--- a/recipes-wifi/linux-mt76/files/patches/1041-wifi-mt76-mt7915-support-thermal-recal-debug-commnad.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1041-wifi-mt76-mt7915-support-thermal-recal-debug-commnad.patch
@@ -1,7 +1,7 @@
-From 9fd80f6b724ba3f7272da8856e3b3d07d04dadb8 Mon Sep 17 00:00:00 2001
+From 71385726a0af02c6cb650a6be60511e6f0f1b3a4 Mon Sep 17 00:00:00 2001
 From: Howard Hsu <howard-yh.hsu@mediatek.com>
 Date: Thu, 21 Dec 2023 20:35:36 +0800
-Subject: [PATCH 1041/1041] wifi: mt76: mt7915: support thermal recal debug
+Subject: [PATCH 1041/1052] wifi: mt76: mt7915: support thermal recal debug
  commnad
 
 Add thermal recal debug command:
@@ -21,7 +21,7 @@
  4 files changed, 35 insertions(+)
 
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 8b7fe87..c34c6e6 100644
+index febe3ed4..8a0f5bea 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
 @@ -1234,6 +1234,7 @@ enum {
@@ -33,7 +33,7 @@
  	MCU_EXT_CMD_SET_RDD_PATTERN = 0x7d,
  	MCU_EXT_CMD_MWDS_SUPPORT = 0x80,
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 297ded7..b66e5ea 100644
+index fcbe4da4..2b653bfb 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -5356,3 +5356,18 @@ void mt7915_mcu_scs_sta_poll(struct work_struct *work)
@@ -56,7 +56,7 @@
 +				 sizeof(req), true);
 +}
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 6b27be9..496ccd9 100644
+index 6b27be9c..496ccd94 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -835,6 +835,7 @@ int mt7915_mcu_ipi_hist_scan(struct mt7915_phy *phy, void *data, u8 mode, bool w
@@ -68,7 +68,7 @@
  #ifdef MTK_DEBUG
  int mt7915_mtk_init_debugfs(struct mt7915_phy *phy, struct dentry *dir);
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index e60dc85..0677495 100644
+index e60dc850..0677495c 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -3843,6 +3843,22 @@ mt7915_scs_enable_set(void *data, u64 val)
diff --git a/recipes-wifi/linux-mt76/files/patches/1042-wifi-mt76-mt7915-Add-support-for-lpi-and-duplicate-m.patch b/recipes-wifi/linux-mt76/files/patches/1042-wifi-mt76-mt7915-Add-support-for-lpi-and-duplicate-m.patch
index 2508bd1..4dbc453 100644
--- a/recipes-wifi/linux-mt76/files/patches/1042-wifi-mt76-mt7915-Add-support-for-lpi-and-duplicate-m.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1042-wifi-mt76-mt7915-Add-support-for-lpi-and-duplicate-m.patch
@@ -1,7 +1,7 @@
-From 15b2679217535569b2c27dad231ad2c38857a3d2 Mon Sep 17 00:00:00 2001
+From 45dbb4f20ca056bd56f8c723a10d0f5662d92f75 Mon Sep 17 00:00:00 2001
 From: Allen Ye <allen.ye@mediatek.com>
 Date: Fri, 15 Dec 2023 14:03:11 +0800
-Subject: [PATCH 1042/1051] wifi: mt76: mt7915: Add support for lpi and
+Subject: [PATCH 1042/1052] wifi: mt76: mt7915: Add support for lpi and
  duplicate mode
 
 Add support lpi and duplicate mode.
@@ -31,7 +31,7 @@
  12 files changed, 210 insertions(+), 17 deletions(-)
 
 diff --git a/eeprom.c b/eeprom.c
-index aa33e7b..261d65a 100644
+index aa33e7b5..261d65ad 100644
 --- a/eeprom.c
 +++ b/eeprom.c
 @@ -224,8 +224,9 @@ static bool mt76_string_prop_find(struct property *prop, const char *str)
@@ -95,7 +95,7 @@
  		return target_power;
  
 diff --git a/mt76.h b/mt76.h
-index 7ffba7d..a7a8ece 100644
+index 43f3c282..a18b3e57 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -853,6 +853,9 @@ struct mt76_phy {
@@ -118,7 +118,7 @@
  mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan);
  
 diff --git a/mt76_connac2_mac.h b/mt76_connac2_mac.h
-index eb47653..49ba39f 100644
+index eb476536..49ba39ff 100644
 --- a/mt76_connac2_mac.h
 +++ b/mt76_connac2_mac.h
 @@ -355,6 +355,13 @@ enum tx_port_idx {
@@ -136,7 +136,7 @@
  	MT_TX_FRAG_NONE,
  	MT_TX_FRAG_FIRST,
 diff --git a/mt76_connac_mac.c b/mt76_connac_mac.c
-index c23d266..d170815 100644
+index c23d266e..d1708152 100644
 --- a/mt76_connac_mac.c
 +++ b/mt76_connac_mac.c
 @@ -572,7 +572,8 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
@@ -161,10 +161,10 @@
  
  		txwi[7] &= ~cpu_to_le32(MT_TXD7_HW_AMSDU);
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index e445046..4f87e25 100644
+index 8a0f5bea..3148526a 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
-@@ -1258,6 +1258,7 @@ enum {
+@@ -1260,6 +1260,7 @@ enum {
  	MCU_EXT_CMD_SWLNA_ACI_CTRL = 0xc0,
  	MCU_EXT_CMD_CSI_CTRL = 0xc2,
  	MCU_EXT_CMD_IPI_HIST_SCAN = 0xc5,
@@ -173,7 +173,7 @@
  
  enum {
 diff --git a/mt7915/debugfs.c b/mt7915/debugfs.c
-index 502b493..b2a4ff4 100644
+index 2c1e1bea..f031b54c 100644
 --- a/mt7915/debugfs.c
 +++ b/mt7915/debugfs.c
 @@ -1297,7 +1297,6 @@ mt7915_txpower_info_show(struct seq_file *file, void *data)
@@ -194,10 +194,10 @@
  
  out:
 diff --git a/mt7915/init.c b/mt7915/init.c
-index 2196d5f..9932266 100644
+index a26e0d69..e8a6fcda 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
-@@ -287,7 +287,7 @@ static void __mt7915_init_txpower(struct mt7915_phy *phy,
+@@ -288,7 +288,7 @@ static void __mt7915_init_txpower(struct mt7915_phy *phy,
  
  	phy->sku_limit_en = true;
  	phy->sku_path_en = true;
@@ -206,7 +206,7 @@
  	for (i = 0; i < sband->n_channels; i++) {
  		struct ieee80211_channel *chan = &sband->channels[i];
  		u32 target_power = 0;
-@@ -331,8 +331,10 @@ void mt7915_init_txpower(struct mt7915_phy *phy)
+@@ -332,8 +332,10 @@ void mt7915_init_txpower(struct mt7915_phy *phy)
  		__mt7915_init_txpower(phy, &phy->mt76->sband_2g.sband);
  	if (phy->mt76->cap.has_5ghz)
  		__mt7915_init_txpower(phy, &phy->mt76->sband_5g.sband);
@@ -219,7 +219,7 @@
  
  static void
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 87409a4..196cc33 100644
+index 2b653bfb..0fba0a6d 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -1522,7 +1522,8 @@ mt7915_mcu_set_spe_idx(struct mt7915_dev *dev, struct ieee80211_vif *vif,
@@ -355,10 +355,10 @@
  			      u8 en)
  {
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 2981e5a..d524fbf 100644
+index 496ccd94..74008d4f 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
-@@ -810,6 +810,7 @@ int mt7915_mcu_set_csi(struct mt7915_phy *phy, u8 mode,
+@@ -826,6 +826,7 @@ void mt7915_csi_mac_filter_clear(struct mt7915_phy *phy);
  void mt7915_vendor_amnt_fill_rx(struct mt7915_phy *phy, struct sk_buff *skb);
  int mt7915_vendor_amnt_sta_remove(struct mt7915_phy *phy,
  				  struct ieee80211_sta *sta);
@@ -367,7 +367,7 @@
  int mt7915_mcu_set_edcca(struct mt7915_phy *phy, int mode, u8 *value, s8 compensation);
  int mt7915_mcu_get_edcca(struct mt7915_phy *phy, u8 mode, s8 *value);
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index 53294c1..352b8e9 100644
+index 0677495c..3291aafc 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -3945,6 +3945,7 @@ int mt7915_mtk_init_debugfs(struct mt7915_phy *phy, struct dentry *dir)
@@ -379,10 +379,10 @@
  	debugfs_create_devm_seqfile(dev->mt76.dev, "eeprom_mode", dir,
  				    mt7915_show_eeprom_mode);
 diff --git a/mt7915/vendor.c b/mt7915/vendor.c
-index 432d750..566fec0 100644
+index 6446439f..a9e87a17 100644
 --- a/mt7915/vendor.c
 +++ b/mt7915/vendor.c
-@@ -106,6 +106,13 @@ bss_color_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL] = {
+@@ -107,6 +107,13 @@ bss_color_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL] = {
  	[MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP] = { .type = NLA_U64 },
  };
  
@@ -396,7 +396,7 @@
  struct csi_null_tone {
  	u8 start;
  	u8 end;
-@@ -1335,6 +1342,63 @@ mt7915_vendor_bss_color_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev
+@@ -1471,6 +1478,63 @@ mt7915_vendor_bss_color_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev
  	return len;
  }
  
@@ -460,7 +460,7 @@
  static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
  	{
  		.info = {
-@@ -1451,6 +1515,17 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
+@@ -1587,6 +1651,17 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
  		.dumpit = mt7915_vendor_bss_color_ctrl_dump,
  		.policy = bss_color_ctrl_policy,
  		.maxattr = MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX,
@@ -479,7 +479,7 @@
  };
  
 diff --git a/mt7915/vendor.h b/mt7915/vendor.h
-index 03d1660..5b8a1fb 100644
+index 11ccd0d8..3040007f 100644
 --- a/mt7915/vendor.h
 +++ b/mt7915/vendor.h
 @@ -16,6 +16,7 @@ enum mtk_nl80211_vendor_subcmds {
@@ -490,7 +490,7 @@
  };
  
  
-@@ -274,4 +275,18 @@ enum mtk_vendor_attr_bss_color_ctrl {
+@@ -287,4 +288,18 @@ enum mtk_vendor_attr_bss_color_ctrl {
  	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX =
  		NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
  };
diff --git a/recipes-wifi/linux-mt76/files/patches/1043-wifi-mt76-testmode-add-cheetah-support.patch b/recipes-wifi/linux-mt76/files/patches/1043-wifi-mt76-testmode-add-cheetah-support.patch
index fcc126c..c568929 100644
--- a/recipes-wifi/linux-mt76/files/patches/1043-wifi-mt76-testmode-add-cheetah-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1043-wifi-mt76-testmode-add-cheetah-support.patch
@@ -1,7 +1,7 @@
-From 6adb4d938ebedcb3e842640678b8aa510ee0b088 Mon Sep 17 00:00:00 2001
+From 3eec1f57a5ad5fd0322ac69360cac993ef9f8902 Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Tue, 31 Oct 2023 16:29:13 +0800
-Subject: [PATCH 1043/1051] wifi: mt76: testmode: add cheetah support
+Subject: [PATCH 1043/1052] wifi: mt76: testmode: add cheetah support
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 ---
@@ -9,7 +9,7 @@
  1 file changed, 5 insertions(+), 4 deletions(-)
 
 diff --git a/mt7915/testmode.c b/mt7915/testmode.c
-index faf6014..ecd6271 100644
+index faf60146..ecd62712 100644
 --- a/mt7915/testmode.c
 +++ b/mt7915/testmode.c
 @@ -2151,7 +2151,7 @@ mt7915_tm_group_prek(struct mt7915_phy *phy, enum mt76_testmode_state state)
diff --git a/recipes-wifi/linux-mt76/files/patches/1044-wifi-mt76-mt7915-add-no_beacon-vendor-command-for-ce.patch b/recipes-wifi/linux-mt76/files/patches/1044-wifi-mt76-mt7915-add-no_beacon-vendor-command-for-ce.patch
index 0f1deb4..bcc0450 100644
--- a/recipes-wifi/linux-mt76/files/patches/1044-wifi-mt76-mt7915-add-no_beacon-vendor-command-for-ce.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1044-wifi-mt76-mt7915-add-no_beacon-vendor-command-for-ce.patch
@@ -1,7 +1,7 @@
-From 2a8762458470dcbeb3619d6833b7a072b3f57c4d Mon Sep 17 00:00:00 2001
+From e91a2d40ac3cb7c76827b894c03e31e577944f68 Mon Sep 17 00:00:00 2001
 From: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
 Date: Wed, 24 Jan 2024 14:39:14 +0800
-Subject: [PATCH 1044/1051] wifi: mt76: mt7915: add no_beacon vendor command
+Subject: [PATCH 1044/1052] wifi: mt76: mt7915: add no_beacon vendor command
  for cert
 
 Add the vendor command to disable/enable beacon
@@ -21,7 +21,7 @@
  4 files changed, 65 insertions(+), 1 deletion(-)
 
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 196cc33..fda1efe 100644
+index 0fba0a6d..b1cb7747 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -5101,6 +5101,17 @@ int mt7915_mcu_set_rfeature_trig_type(struct mt7915_phy *phy, u8 enable, u8 trig
@@ -43,10 +43,10 @@
  
  #ifdef MTK_DEBUG
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index d524fbf..223e034 100644
+index 74008d4f..8b2e360e 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
-@@ -811,6 +811,7 @@ void mt7915_vendor_amnt_fill_rx(struct mt7915_phy *phy, struct sk_buff *skb);
+@@ -827,6 +827,7 @@ void mt7915_vendor_amnt_fill_rx(struct mt7915_phy *phy, struct sk_buff *skb);
  int mt7915_vendor_amnt_sta_remove(struct mt7915_phy *phy,
  				  struct ieee80211_sta *sta);
  int mt7915_mcu_set_lpi(struct mt7915_phy *phy, bool en);
@@ -55,10 +55,10 @@
  int mt7915_mcu_set_edcca(struct mt7915_phy *phy, int mode, u8 *value, s8 compensation);
  int mt7915_mcu_get_edcca(struct mt7915_phy *phy, u8 mode, s8 *value);
 diff --git a/mt7915/vendor.c b/mt7915/vendor.c
-index 566fec0..6154d1a 100644
+index a9e87a17..309480d1 100644
 --- a/mt7915/vendor.c
 +++ b/mt7915/vendor.c
-@@ -113,6 +113,11 @@ txpower_ctrl_policy[NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL] = {
+@@ -114,6 +114,11 @@ txpower_ctrl_policy[NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL] = {
  	[MTK_VENDOR_ATTR_TXPOWER_CTRL_BCN_DUP] = { .type = NLA_U8 },
  };
  
@@ -70,7 +70,7 @@
  struct csi_null_tone {
  	u8 start;
  	u8 end;
-@@ -1399,6 +1404,30 @@ static int mt7915_vendor_txpower_ctrl(struct wiphy *wiphy,
+@@ -1535,6 +1540,30 @@ static int mt7915_vendor_txpower_ctrl(struct wiphy *wiphy,
  	return 0;
  }
  
@@ -101,7 +101,7 @@
  static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
  	{
  		.info = {
-@@ -1526,7 +1555,18 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
+@@ -1662,7 +1691,18 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
  		.doit = mt7915_vendor_txpower_ctrl,
  		.policy = txpower_ctrl_policy,
  		.maxattr = MTK_VENDOR_ATTR_TXPOWER_CTRL_MAX,
@@ -122,7 +122,7 @@
  
  void mt7915_vendor_register(struct mt7915_phy *phy)
 diff --git a/mt7915/vendor.h b/mt7915/vendor.h
-index 5b8a1fb..661d636 100644
+index 3040007f..bcde5d3c 100644
 --- a/mt7915/vendor.h
 +++ b/mt7915/vendor.h
 @@ -16,6 +16,7 @@ enum mtk_nl80211_vendor_subcmds {
@@ -133,7 +133,7 @@
  	MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL = 0xce,
  };
  
-@@ -289,4 +290,15 @@ enum mtk_vendor_attr_txpower_ctrl {
+@@ -302,4 +303,15 @@ enum mtk_vendor_attr_txpower_ctrl {
  		NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL - 1
  };
  
diff --git a/recipes-wifi/linux-mt76/files/patches/1045-wifi-mt76-mt7915-support-spatial-reuse-debug-command.patch b/recipes-wifi/linux-mt76/files/patches/1045-wifi-mt76-mt7915-support-spatial-reuse-debug-command.patch
index d30d6be..495efa1 100644
--- a/recipes-wifi/linux-mt76/files/patches/1045-wifi-mt76-mt7915-support-spatial-reuse-debug-command.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1045-wifi-mt76-mt7915-support-spatial-reuse-debug-command.patch
@@ -1,7 +1,7 @@
-From e64321af1c112f7a5f4c72a23fe96573b7f14a1f Mon Sep 17 00:00:00 2001
+From 40e0945208ca6569cc032b59f935223b8fac19f4 Mon Sep 17 00:00:00 2001
 From: Howard Hsu <howard-yh.hsu@mediatek.com>
 Date: Thu, 15 Feb 2024 11:16:16 +0800
-Subject: [PATCH 1045/1051] wifi: mt76: mt7915: support spatial reuse debug
+Subject: [PATCH 1045/1052] wifi: mt76: mt7915: support spatial reuse debug
  commands
 
 Support 3 spatial reuse debug commands:
@@ -19,10 +19,10 @@
  5 files changed, 191 insertions(+), 4 deletions(-)
 
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 4f87e25..292ef0f 100644
+index 3148526a..d8b001f1 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
-@@ -1031,6 +1031,7 @@ enum {
+@@ -1032,6 +1032,7 @@ enum {
  	MCU_EXT_EVENT_WA_TX_STAT = 0x74,
  	MCU_EXT_EVENT_BCC_NOTIFY = 0x75,
  	MCU_EXT_EVENT_MURU_CTRL = 0x9f,
@@ -31,7 +31,7 @@
  };
  
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index fda1efe..cb78961 100644
+index b1cb7747..e505664e 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -452,6 +452,91 @@ mt7915_mcu_rx_bss_acq_pkt_cnt(struct mt7915_dev *dev, struct sk_buff * skb)
@@ -158,7 +158,7 @@
  		return 0;
  
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index 742a785..f476767 100644
+index 742a7855..f4767671 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -42,6 +42,45 @@ struct mt7915_mcu_thermal_notify {
@@ -233,10 +233,10 @@
  	THERMAL_PROTECT_PARAMETER_CTRL,
  	THERMAL_PROTECT_BASIC_INFO,
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 223e034..a6901dd 100644
+index 8b2e360e..10d48495 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
-@@ -356,6 +356,9 @@ struct mt7915_phy {
+@@ -371,6 +371,9 @@ struct mt7915_phy {
  	struct mt7915_air_monitor_ctrl amnt_ctrl;
  #endif
  	struct mt7915_scs_ctrl scs_ctrl;
@@ -247,7 +247,7 @@
  
  #ifdef MTK_DEBUG
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index 352b8e9..0f72f30 100644
+index 3291aafc..c8dd569c 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -3809,16 +3809,34 @@ mt7915_sw_aci_set(void *data, u64 val)
diff --git a/recipes-wifi/linux-mt76/files/patches/1046-wifi-mt76-try-more-times-when-send-message-timeout.patch b/recipes-wifi/linux-mt76/files/patches/1046-wifi-mt76-try-more-times-when-send-message-timeout.patch
index 8416215..cec5940 100644
--- a/recipes-wifi/linux-mt76/files/patches/1046-wifi-mt76-try-more-times-when-send-message-timeout.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1046-wifi-mt76-try-more-times-when-send-message-timeout.patch
@@ -1,10 +1,9 @@
-From 526b34aec94a2d244441a844c84862e35f1162a5 Mon Sep 17 00:00:00 2001
+From 73508da4263fcc10cd059fd89ef4017f9725324d Mon Sep 17 00:00:00 2001
 From: Bo Jiao <Bo.Jiao@mediatek.com>
 Date: Thu, 7 Mar 2024 11:13:45 +0800
-Subject: [PATCH 1046/1051] wifi: mt76: try more times when send message
+Subject: [PATCH 1046/1052] wifi: mt76: try more times when send message
  timeout.
 
-CR-Id: WCNCR00334773
 Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
 ---
  dma.c        |  7 ++++--
@@ -13,7 +12,7 @@
  3 files changed, 64 insertions(+), 52 deletions(-)
 
 diff --git a/dma.c b/dma.c
-index bc8afcf..133a50d 100644
+index bc8afcff..133a50dc 100644
 --- a/dma.c
 +++ b/dma.c
 @@ -504,9 +504,12 @@ mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, struct mt76_queue *q,
@@ -40,7 +39,7 @@
  
  static int
 diff --git a/mcu.c b/mcu.c
-index fa4b054..de185cc 100644
+index fa4b0544..de185cc9 100644
 --- a/mcu.c
 +++ b/mcu.c
 @@ -4,6 +4,7 @@
@@ -133,7 +132,7 @@
  
  	return ret;
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index fb98940..b3c9163 100644
+index fb989405..b3c91633 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -1348,12 +1348,6 @@ mt7915_mac_restart(struct mt7915_dev *dev)
diff --git a/recipes-wifi/linux-mt76/files/patches/1047-wifi-mt76-mt7915-add-SER-overlap-handle.patch b/recipes-wifi/linux-mt76/files/patches/1047-wifi-mt76-mt7915-add-SER-overlap-handle.patch
index b8314fd..d7f7244 100644
--- a/recipes-wifi/linux-mt76/files/patches/1047-wifi-mt76-mt7915-add-SER-overlap-handle.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1047-wifi-mt76-mt7915-add-SER-overlap-handle.patch
@@ -1,9 +1,8 @@
-From 0f8d644d9c7ab28e9e4da56151987a3e4be1f55f Mon Sep 17 00:00:00 2001
+From 1b93f1495d5f28a7620add2ebea0f691ff6e8a0d Mon Sep 17 00:00:00 2001
 From: Bo Jiao <Bo.Jiao@mediatek.com>
 Date: Tue, 6 Feb 2024 14:46:59 +0800
-Subject: [PATCH 1047/1051] wifi: mt76: mt7915: add SER overlap handle
+Subject: [PATCH 1047/1052] wifi: mt76: mt7915: add SER overlap handle
 
-CR-ID: WCNCR00355921
 Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
 ---
  dma.c        |  3 ++-
@@ -14,7 +13,7 @@
  5 files changed, 34 insertions(+), 3 deletions(-)
 
 diff --git a/dma.c b/dma.c
-index 133a50d..100d2af 100644
+index 133a50dc..100d2aff 100644
 --- a/dma.c
 +++ b/dma.c
 @@ -506,7 +506,8 @@ mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, struct mt76_queue *q,
@@ -28,7 +27,7 @@
  		goto error;
  	}
 diff --git a/mcu.c b/mcu.c
-index de185cc..1bc94e8 100644
+index de185cc9..1bc94e85 100644
 --- a/mcu.c
 +++ b/mcu.c
 @@ -42,7 +42,9 @@ struct sk_buff *mt76_mcu_get_response(struct mt76_dev *dev,
@@ -53,7 +52,7 @@
  			dev_err(dev->dev, "send message %08x timeout, try again(%d).\n",
  				cmd, (MT76_MSG_MAX_RETRY_CNT - retry_cnt));
 diff --git a/mt76.h b/mt76.h
-index a7a8ece..e6482e5 100644
+index a18b3e57..49ddaade 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -478,6 +478,14 @@ enum {
@@ -80,7 +79,7 @@
  	u32 wcid_mask[DIV_ROUND_UP(MT76_N_WCIDS, 32)];
  	u32 wcid_phy_mask[DIV_ROUND_UP(MT76_N_WCIDS, 32)];
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index b3c9163..c84b957 100644
+index b3c91633..c84b9573 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -1538,6 +1538,7 @@ void mt7915_mac_reset_work(struct work_struct *work)
@@ -116,7 +115,7 @@
  	wake_up(&dev->reset_wait);
  }
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index cb78961..f890063 100644
+index e505664e..272db06a 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -198,6 +198,13 @@ mt7915_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
diff --git a/recipes-wifi/linux-mt76/files/patches/1048-wifi-mt76-mt7915-add-background-radar-hw-cap-check.patch b/recipes-wifi/linux-mt76/files/patches/1048-wifi-mt76-mt7915-add-background-radar-hw-cap-check.patch
index 17e299e..ec0a323 100644
--- a/recipes-wifi/linux-mt76/files/patches/1048-wifi-mt76-mt7915-add-background-radar-hw-cap-check.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1048-wifi-mt76-mt7915-add-background-radar-hw-cap-check.patch
@@ -1,7 +1,7 @@
-From 3ad316d5a0d9caa9a07597ac59df0663daf3e319 Mon Sep 17 00:00:00 2001
+From 04882a33afbee2fd86cdb244a0faf0748f163b31 Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Mon, 4 Mar 2024 11:29:06 +0800
-Subject: [PATCH 1048/1051] wifi: mt76: mt7915: add background radar hw cap
+Subject: [PATCH 1048/1052] wifi: mt76: mt7915: add background radar hw cap
  check
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
@@ -12,7 +12,7 @@
  3 files changed, 39 insertions(+), 3 deletions(-)
 
 diff --git a/mt7915/debugfs.c b/mt7915/debugfs.c
-index b2a4ff4..03daf44 100644
+index f031b54c..3fd1654c 100644
 --- a/mt7915/debugfs.c
 +++ b/mt7915/debugfs.c
 @@ -459,6 +459,11 @@ mt7915_rdd_monitor(struct seq_file *s, void *data)
@@ -28,7 +28,7 @@
  		ret = -EINVAL;
  		goto out;
 diff --git a/mt7915/eeprom.h b/mt7915/eeprom.h
-index 70fca0b..adeee10 100644
+index 70fca0b3..adeee104 100644
 --- a/mt7915/eeprom.h
 +++ b/mt7915/eeprom.h
 @@ -55,6 +55,7 @@ enum mt7915_eeprom_field {
@@ -76,10 +76,10 @@
  
  #endif
 diff --git a/mt7915/init.c b/mt7915/init.c
-index 9932266..dc1b34d 100644
+index e8a6fcda..03c86ae0 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
-@@ -404,9 +404,10 @@ mt7915_init_wiphy(struct mt7915_phy *phy)
+@@ -405,9 +405,10 @@ mt7915_init_wiphy(struct mt7915_phy *phy)
  	if (!is_mt7915(&dev->mt76))
  		wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_STA_TX_PWR);
  
diff --git a/recipes-wifi/linux-mt76/files/patches/1049-wifi-mt76-mt7915-add-foolproof-mechanism-for-ZWDFS-d.patch b/recipes-wifi/linux-mt76/files/patches/1049-wifi-mt76-mt7915-add-foolproof-mechanism-for-ZWDFS-d.patch
index 8aaa151..e626535 100644
--- a/recipes-wifi/linux-mt76/files/patches/1049-wifi-mt76-mt7915-add-foolproof-mechanism-for-ZWDFS-d.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1049-wifi-mt76-mt7915-add-foolproof-mechanism-for-ZWDFS-d.patch
@@ -1,7 +1,7 @@
-From 1de92bb3d5a816769f306322ae3c213adbacc6f1 Mon Sep 17 00:00:00 2001
+From 7f45a6881d88e92180fe2b162800b8334d005a5f Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Wed, 6 Mar 2024 11:30:34 +0800
-Subject: [PATCH 1049/1051] wifi: mt76: mt7915: add foolproof mechanism for
+Subject: [PATCH 1049/1052] wifi: mt76: mt7915: add foolproof mechanism for
  ZWDFS during radar detected & triggered
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
@@ -11,7 +11,7 @@
  2 files changed, 8 insertions(+)
 
 diff --git a/mt7915/debugfs.c b/mt7915/debugfs.c
-index 03daf44..d8ca90a 100644
+index 3fd1654c..522e2999 100644
 --- a/mt7915/debugfs.c
 +++ b/mt7915/debugfs.c
 @@ -231,6 +231,11 @@ mt7915_radar_trigger(void *data, u64 val)
@@ -27,7 +27,7 @@
  				       val, 0, 0);
  }
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index f890063..853a50e 100644
+index 272db06a..250b05ec 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -302,6 +302,9 @@ mt7915_mcu_rx_radar_detected(struct mt7915_dev *dev, struct sk_buff *skb)
diff --git a/recipes-wifi/linux-mt76/files/patches/1050-mtk-wifi-mt76-mt7915-assign-DEAUTH-to-ALTX-queue-for.patch b/recipes-wifi/linux-mt76/files/patches/1050-mtk-wifi-mt76-mt7915-assign-DEAUTH-to-ALTX-queue-for.patch
index 2c5f484..05e22ba 100644
--- a/recipes-wifi/linux-mt76/files/patches/1050-mtk-wifi-mt76-mt7915-assign-DEAUTH-to-ALTX-queue-for.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1050-mtk-wifi-mt76-mt7915-assign-DEAUTH-to-ALTX-queue-for.patch
@@ -1,7 +1,7 @@
-From 236bc42c90160e3526d8901f3d983ebc76316ee2 Mon Sep 17 00:00:00 2001
+From 2ed70c162a13b4b4af3bfc7a806e8cfab29126f7 Mon Sep 17 00:00:00 2001
 From: Michael-CY Lee <michael-cy.lee@mediatek.com>
 Date: Tue, 19 Mar 2024 08:35:26 +0800
-Subject: [PATCH 1050/1051] mtk: wifi: mt76: mt7915: assign DEAUTH to ALTX
+Subject: [PATCH 1050/1052] mtk: wifi: mt76: mt7915: assign DEAUTH to ALTX
  queue for CERT
 
 Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
@@ -10,7 +10,7 @@
  1 file changed, 10 insertions(+)
 
 diff --git a/mt76_connac_mac.c b/mt76_connac_mac.c
-index d170815..21dadb5 100644
+index d1708152..21dadb55 100644
 --- a/mt76_connac_mac.c
 +++ b/mt76_connac_mac.c
 @@ -385,6 +385,8 @@ mt76_connac2_mac_write_txwi_80211(struct mt76_dev *dev, __le32 *txwi,
diff --git a/recipes-wifi/linux-mt76/files/patches/1051-wifi-mt76-mt7915-set-channel-after-sta-is-associated.patch b/recipes-wifi/linux-mt76/files/patches/1051-wifi-mt76-mt7915-set-channel-after-sta-is-associated.patch
index b969ef3..eabad84 100644
--- a/recipes-wifi/linux-mt76/files/patches/1051-wifi-mt76-mt7915-set-channel-after-sta-is-associated.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1051-wifi-mt76-mt7915-set-channel-after-sta-is-associated.patch
@@ -1,7 +1,7 @@
-From 29807a01daba2d3042a76b2427fbb24e1da71b28 Mon Sep 17 00:00:00 2001
+From 1d18008ab9d67f318932ed993103bd46d9f0215d Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Thu, 21 Mar 2024 16:52:34 +0800
-Subject: [PATCH 1051/1051] wifi: mt76: mt7915: set channel after sta is
+Subject: [PATCH 1051/1052] wifi: mt76: mt7915: set channel after sta is
  associated to adjust switch reason
 
 when sta is associated to AP operating in DFS channel, a channel
@@ -16,7 +16,7 @@
  1 file changed, 26 insertions(+)
 
 diff --git a/mt7915/main.c b/mt7915/main.c
-index da998e1..c97974a 100644
+index 04301300..eab45737 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -794,6 +794,31 @@ out:
@@ -51,7 +51,7 @@
  int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
  		       struct ieee80211_sta *sta)
  {
-@@ -1784,6 +1809,7 @@ const struct ieee80211_ops mt7915_ops = {
+@@ -1835,6 +1860,7 @@ const struct ieee80211_ops mt7915_ops = {
  	.set_sar_specs = mt7915_set_sar_specs,
  	.channel_switch_beacon = mt7915_channel_switch_beacon,
  	.post_channel_switch = mt7915_post_channel_switch,
diff --git a/recipes-wifi/linux-mt76/files/patches/1052-wifi-mt76-mt7915-Clear-private-driver-data-in-case-o.patch b/recipes-wifi/linux-mt76/files/patches/1052-wifi-mt76-mt7915-Clear-private-driver-data-in-case-o.patch
index 2f6e452..a9ff608 100644
--- a/recipes-wifi/linux-mt76/files/patches/1052-wifi-mt76-mt7915-Clear-private-driver-data-in-case-o.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1052-wifi-mt76-mt7915-Clear-private-driver-data-in-case-o.patch
@@ -1,8 +1,8 @@
-From f92bc600352365982f4c674de61454dfdcba6bcd Mon Sep 17 00:00:00 2001
+From 903edd4667682289fca8cb7fac0f407c34598e5c Mon Sep 17 00:00:00 2001
 From: Rex Lu <rex.lu@mediatek.com>
 Date: Fri, 5 Jul 2024 17:50:29 +0800
-Subject: [PATCH] wifi: mt76: mt7915: Clear private driver data in case of
- reuse
+Subject: [PATCH 1052/1052] wifi: mt76: mt7915: Clear private driver data in
+ case of reuse
 
 Signed-off-by: Rex Lu <rex.lu@mediatek.com>
 ---
@@ -10,7 +10,7 @@
  1 file changed, 3 insertions(+)
 
 diff --git a/mt7915/main.c b/mt7915/main.c
-index 6912ea7..4a4c372 100644
+index eab45737..8c498683 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -230,6 +230,9 @@ int mt7915_init_vif(struct mt7915_phy *phy, struct ieee80211_vif *vif, bool bf_e
diff --git a/recipes-wifi/linux-mt76/files/patches/2000-wifi-mt76-mt7915-wed-add-wed-tx-support.patch b/recipes-wifi/linux-mt76/files/patches/2000-wifi-mt76-mt7915-wed-add-wed-tx-support.patch
index 1d5e567..0653704 100644
--- a/recipes-wifi/linux-mt76/files/patches/2000-wifi-mt76-mt7915-wed-add-wed-tx-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches/2000-wifi-mt76-mt7915-wed-add-wed-tx-support.patch
@@ -1,4 +1,4 @@
-From 3af8d78c5f9622276367d9e71921fe25f9d211b5 Mon Sep 17 00:00:00 2001
+From aba91915c426953a680062a8e3528b7ca90054e9 Mon Sep 17 00:00:00 2001
 From: Sujuan Chen <sujuan.chen@mediatek.com>
 Date: Fri, 25 Nov 2022 10:38:53 +0800
 Subject: [PATCH 2000/2015] wifi: mt76: mt7915: wed: add wed tx support
@@ -14,7 +14,7 @@
  6 files changed, 14 insertions(+), 8 deletions(-)
 
 diff --git a/mt76_connac.h b/mt76_connac.h
-index 8e7068c..e1d6ca2 100644
+index 5356c52c..93856347 100644
 --- a/mt76_connac.h
 +++ b/mt76_connac.h
 @@ -130,6 +130,7 @@ struct mt76_connac_sta_key_conf {
@@ -26,7 +26,7 @@
  struct mt76_connac_fw_txp {
  	__le16 flags;
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index c84b957..1c8b873 100644
+index c84b9573..1c8b8732 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -878,9 +878,9 @@ u32 mt7915_wed_init_buf(void *ptr, dma_addr_t phys, int token_id)
@@ -67,10 +67,10 @@
  
  static void
 diff --git a/mt7915/main.c b/mt7915/main.c
-index c97974a..ed7ade1 100644
+index 8c498683..ff0337a6 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
-@@ -1761,14 +1761,14 @@ mt7915_net_fill_forward_path(struct ieee80211_hw *hw,
+@@ -1816,14 +1816,14 @@ mt7915_net_fill_forward_path(struct ieee80211_hw *hw,
  	if (!mtk_wed_device_active(wed))
  		return -ENODEV;
  
@@ -88,7 +88,7 @@
  
  	ctx->dev = NULL;
 diff --git a/mt7915/mmio.c b/mt7915/mmio.c
-index 437a9b0..91100f1 100644
+index b1b219ce..53648212 100644
 --- a/mt7915/mmio.c
 +++ b/mt7915/mmio.c
 @@ -13,7 +13,7 @@
@@ -109,7 +109,7 @@
  	ret = dma_set_mask(wed->dev, DMA_BIT_MASK(32));
  	if (ret)
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index a6901dd..2dce288 100644
+index 10d48495..d93ab970 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -62,7 +62,7 @@
@@ -122,7 +122,7 @@
  
  #define MT7915_CFEND_RATE_DEFAULT	0x49	/* OFDM 24M */
 diff --git a/wed.c b/wed.c
-index f7a3f1b..47c81a2 100644
+index f7a3f1b3..47c81a28 100644
 --- a/wed.c
 +++ b/wed.c
 @@ -187,7 +187,7 @@ void mt76_wed_offload_disable(struct mtk_wed_device *wed)
diff --git a/recipes-wifi/linux-mt76/files/patches/2001-wifi-mt76-mt7915-wed-add-wds-support-when-wed-is-ena.patch b/recipes-wifi/linux-mt76/files/patches/2001-wifi-mt76-mt7915-wed-add-wds-support-when-wed-is-ena.patch
index c8e6888..35edec4 100644
--- a/recipes-wifi/linux-mt76/files/patches/2001-wifi-mt76-mt7915-wed-add-wds-support-when-wed-is-ena.patch
+++ b/recipes-wifi/linux-mt76/files/patches/2001-wifi-mt76-mt7915-wed-add-wds-support-when-wed-is-ena.patch
@@ -1,4 +1,4 @@
-From 0454c032c589da59ef4e44654d2f650e273e215a Mon Sep 17 00:00:00 2001
+From 2845e9a6b55d63d970541c8322eec387bfa252e4 Mon Sep 17 00:00:00 2001
 From: Sujuan Chen <sujuan.chen@mediatek.com>
 Date: Tue, 13 Dec 2022 17:51:26 +0800
 Subject: [PATCH 2001/2015] wifi: mt76: mt7915: wed: add wds support when wed
@@ -15,7 +15,7 @@
  6 files changed, 82 insertions(+), 10 deletions(-)
 
 diff --git a/mt76.h b/mt76.h
-index e6482e5..6168758 100644
+index 49ddaade..411a9576 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -78,6 +78,12 @@ enum mt76_wed_type {
@@ -32,10 +32,10 @@
  	u32 (*rr)(struct mt76_dev *dev, u32 offset);
  	void (*wr)(struct mt76_dev *dev, u32 offset, u32 val);
 diff --git a/mt7915/main.c b/mt7915/main.c
-index ed7ade1..6276230 100644
+index ff0337a6..778c73e9 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
-@@ -831,8 +831,15 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+@@ -834,8 +834,15 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
  #endif
  	int ret, idx;
  	u32 addr;
@@ -52,7 +52,7 @@
  	if (idx < 0)
  		return -ENOSPC;
  
-@@ -1323,6 +1330,13 @@ static void mt7915_sta_set_4addr(struct ieee80211_hw *hw,
+@@ -1335,6 +1342,13 @@ static void mt7915_sta_set_4addr(struct ieee80211_hw *hw,
  	else
  		clear_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags);
  
@@ -66,7 +66,7 @@
  	mt76_connac_mcu_wtbl_update_hdr_trans(&dev->mt76, vif, sta);
  }
  
-@@ -1768,8 +1782,12 @@ mt7915_net_fill_forward_path(struct ieee80211_hw *hw,
+@@ -1823,8 +1837,12 @@ mt7915_net_fill_forward_path(struct ieee80211_hw *hw,
  	path->dev = ctx->dev;
  	path->mtk_wdma.wdma_idx = wed->wdma_idx;
  	path->mtk_wdma.bss = mvif->mt76.idx;
@@ -81,7 +81,7 @@
  	ctx->dev = NULL;
  
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 853a50e..4390f42 100644
+index 250b05ec..7927a523 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -2588,10 +2588,18 @@ int mt7915_mcu_init_firmware(struct mt7915_dev *dev)
@@ -108,7 +108,7 @@
  	ret = mt7915_mcu_set_mwds(dev, 1);
  	if (ret)
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index f476767..52baaa7 100644
+index f4767671..52baaa73 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -392,6 +392,7 @@ enum {
@@ -120,7 +120,7 @@
  	MCU_WA_PARAM_RED_SHOW_STA = 0xf,
  	MCU_WA_PARAM_RED_TARGET_DELAY = 0x10,
 diff --git a/util.c b/util.c
-index fc76c66..61b2d30 100644
+index d6c01a2d..995d7880 100644
 --- a/util.c
 +++ b/util.c
 @@ -42,9 +42,14 @@ bool ____mt76_poll_msec(struct mt76_dev *dev, u32 offset, u32 mask, u32 val,
@@ -188,7 +188,7 @@
  int mt76_get_min_avg_rssi(struct mt76_dev *dev, bool ext_phy)
  {
 diff --git a/util.h b/util.h
-index 260965d..99b7263 100644
+index 260965dd..99b7263c 100644
 --- a/util.h
 +++ b/util.h
 @@ -27,7 +27,12 @@ enum {
diff --git a/recipes-wifi/linux-mt76/files/patches/2002-wifi-mt76-mt7915-wed-add-fill-receive-path-to-report.patch b/recipes-wifi/linux-mt76/files/patches/2002-wifi-mt76-mt7915-wed-add-fill-receive-path-to-report.patch
index be6d9da..6e78804 100644
--- a/recipes-wifi/linux-mt76/files/patches/2002-wifi-mt76-mt7915-wed-add-fill-receive-path-to-report.patch
+++ b/recipes-wifi/linux-mt76/files/patches/2002-wifi-mt76-mt7915-wed-add-fill-receive-path-to-report.patch
@@ -1,4 +1,4 @@
-From 21586d3a17ba293d3d80bd93fb404729647820bd Mon Sep 17 00:00:00 2001
+From 0f848ffb35cc45beb156069d3856b14414e9a6ee Mon Sep 17 00:00:00 2001
 From: Evelyn Tsai <evelyn.tsai@mediatek.com>
 Date: Fri, 19 May 2023 07:05:22 +0800
 Subject: [PATCH 2002/2015] wifi: mt76: mt7915: wed: add fill receive path to
@@ -10,10 +10,10 @@
  1 file changed, 18 insertions(+)
 
 diff --git a/mt7915/main.c b/mt7915/main.c
-index 6276230..fbb2aff 100644
+index 778c73e9..abafa5e4 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
-@@ -1798,6 +1798,23 @@ mt7915_net_fill_forward_path(struct ieee80211_hw *hw,
+@@ -1848,6 +1848,23 @@ mt7915_net_fill_forward_path(struct ieee80211_hw *hw,
  
  	return 0;
  }
@@ -37,7 +37,7 @@
  #endif
  
  const struct ieee80211_ops mt7915_ops = {
-@@ -1855,6 +1872,7 @@ const struct ieee80211_ops mt7915_ops = {
+@@ -1910,6 +1927,7 @@ const struct ieee80211_ops mt7915_ops = {
  	.set_radar_background = mt7915_set_radar_background,
  #ifdef CONFIG_NET_MEDIATEK_SOC_WED
  	.net_fill_forward_path = mt7915_net_fill_forward_path,
diff --git a/recipes-wifi/linux-mt76/files/patches/2003-wifi-mt76-mt7915-wed-find-rx-token-by-physical-addre.patch b/recipes-wifi/linux-mt76/files/patches/2003-wifi-mt76-mt7915-wed-find-rx-token-by-physical-addre.patch
index f2e3d14..ef36792 100644
--- a/recipes-wifi/linux-mt76/files/patches/2003-wifi-mt76-mt7915-wed-find-rx-token-by-physical-addre.patch
+++ b/recipes-wifi/linux-mt76/files/patches/2003-wifi-mt76-mt7915-wed-find-rx-token-by-physical-addre.patch
@@ -1,4 +1,4 @@
-From 5a0028b27ba80cb34d905a668e335fb2c55685d1 Mon Sep 17 00:00:00 2001
+From 6d6ae068e478b101556ae723c23533220a8daeb5 Mon Sep 17 00:00:00 2001
 From: Sujuan Chen <sujuan.chen@mediatek.com>
 Date: Fri, 25 Nov 2022 14:32:35 +0800
 Subject: [PATCH 2003/2015] wifi: mt76: mt7915: wed: find rx token by physical
@@ -13,7 +13,7 @@
  1 file changed, 24 insertions(+), 1 deletion(-)
 
 diff --git a/dma.c b/dma.c
-index 100d2af..185c6f1 100644
+index 100d2aff..185c6f12 100644
 --- a/dma.c
 +++ b/dma.c
 @@ -444,9 +444,32 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
diff --git a/recipes-wifi/linux-mt76/files/patches/2004-wifi-mt76-mt7915-wed-HW-ATF-support-for-mt7986.patch b/recipes-wifi/linux-mt76/files/patches/2004-wifi-mt76-mt7915-wed-HW-ATF-support-for-mt7986.patch
index f9bf272..65b6630 100644
--- a/recipes-wifi/linux-mt76/files/patches/2004-wifi-mt76-mt7915-wed-HW-ATF-support-for-mt7986.patch
+++ b/recipes-wifi/linux-mt76/files/patches/2004-wifi-mt76-mt7915-wed-HW-ATF-support-for-mt7986.patch
@@ -1,4 +1,4 @@
-From 08b72ac5e48038ff9c6a13adeb5273c07ba6e5a6 Mon Sep 17 00:00:00 2001
+From 065fd51b334f21e004e8a0e03e4d90a4e8265d98 Mon Sep 17 00:00:00 2001
 From: Lian Chen <lian.chen@mediatek.com>
 Date: Mon, 7 Nov 2022 14:47:44 +0800
 Subject: [PATCH 2004/2015] wifi: mt76: mt7915: wed: HW ATF support for mt7986
@@ -16,10 +16,10 @@
  7 files changed, 796 insertions(+), 3 deletions(-)
 
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 292ef0f..3786e69 100644
+index d8b001f1..1721f9b5 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
-@@ -1209,6 +1209,7 @@ enum {
+@@ -1211,6 +1211,7 @@ enum {
  	MCU_EXT_CMD_THERMAL_CTRL = 0x2c,
  	MCU_EXT_CMD_WTBL_UPDATE = 0x32,
  	MCU_EXT_CMD_SET_DRR_CTRL = 0x36,
@@ -27,7 +27,7 @@
  	MCU_EXT_CMD_SET_RDD_CTRL = 0x3a,
  	MCU_EXT_CMD_ATE_CTRL = 0x3d,
  	MCU_EXT_CMD_PROTECT_CTRL = 0x3e,
-@@ -1218,6 +1219,7 @@ enum {
+@@ -1220,6 +1221,7 @@ enum {
  	MCU_EXT_CMD_MUAR_UPDATE = 0x48,
  	MCU_EXT_CMD_BCN_OFFLOAD = 0x49,
  	MCU_EXT_CMD_RX_AIRTIME_CTRL = 0x4a,
@@ -36,7 +36,7 @@
  	MCU_EXT_CMD_EFUSE_FREE_BLOCK = 0x4f,
  	MCU_EXT_CMD_TX_POWER_FEATURE_CTRL = 0x58,
 diff --git a/mt7915/debugfs.c b/mt7915/debugfs.c
-index d8ca90a..3ae4aca 100644
+index 522e2999..576ec1a5 100644
 --- a/mt7915/debugfs.c
 +++ b/mt7915/debugfs.c
 @@ -12,6 +12,10 @@
@@ -409,7 +409,7 @@
  static int
  mt7915_radar_trigger(void *data, u64 val)
  {
-@@ -1526,6 +1882,7 @@ int mt7915_init_debugfs(struct mt7915_phy *phy)
+@@ -1571,6 +1927,7 @@ int mt7915_init_debugfs(struct mt7915_phy *phy)
  	debugfs_create_devm_seqfile(dev->mt76.dev, "twt_stats", dir,
  				    mt7915_twt_stats);
  	debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval);
@@ -418,10 +418,10 @@
  	if (!dev->dbdc_support || phy->mt76->band_idx) {
  		debugfs_create_u32("dfs_hw_pattern", 0400, dir,
 diff --git a/mt7915/init.c b/mt7915/init.c
-index dc1b34d..f38c8a1 100644
+index 03c86ae0..7f210669 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
-@@ -588,9 +588,64 @@ mt7915_init_led_mux(struct mt7915_dev *dev)
+@@ -589,9 +589,64 @@ mt7915_init_led_mux(struct mt7915_dev *dev)
  	}
  }
  
@@ -484,9 +484,9 @@
  	int i;
 +	struct wiphy *wiphy = dev->phy.mt76->hw->wiphy;
  
- 	if (!is_mt7915(&dev->mt76))
- 		mt76_clear(dev, MT_MDP_DCR2, MT_MDP_DCR2_RX_TRANS_SHORT);
-@@ -604,6 +659,9 @@ void mt7915_mac_init(struct mt7915_dev *dev)
+ 	/* config pse qid6 wfdma port selection */
+ 	if (!is_mt7915(&dev->mt76) && dev->hif2)
+@@ -610,6 +665,9 @@ void mt7915_mac_init(struct mt7915_dev *dev)
  		mt7915_mac_init_band(dev, i);
  
  	mt7915_init_led_mux(dev);
@@ -497,7 +497,7 @@
  
  int mt7915_txbf_init(struct mt7915_dev *dev)
 diff --git a/mt7915/main.c b/mt7915/main.c
-index fbb2aff..feb2c89 100644
+index abafa5e4..ecfd9307 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -226,6 +226,7 @@ int mt7915_init_vif(struct mt7915_phy *phy, struct ieee80211_vif *vif, bool bf_e
@@ -508,7 +508,7 @@
  	struct mt76_txq *mtxq;
  	bool ext_phy = phy != &dev->phy;
  	int idx, ret = 0;
-@@ -288,6 +289,9 @@ int mt7915_init_vif(struct mt7915_phy *phy, struct ieee80211_vif *vif, bool bf_e
+@@ -291,6 +292,9 @@ int mt7915_init_vif(struct mt7915_phy *phy, struct ieee80211_vif *vif, bool bf_e
  	mt7915_mcu_add_sta(dev, vif, NULL, true);
  	rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid);
  
@@ -518,7 +518,7 @@
  	return ret;
  }
  
-@@ -826,6 +830,7 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+@@ -829,6 +833,7 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
  	struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
  	struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
  	bool ext_phy = mvif->phy != &dev->phy;
@@ -526,7 +526,7 @@
  #ifdef CONFIG_MTK_VENDOR
  	struct mt7915_phy *phy = ext_phy ? mt7915_ext_phy(dev) : &dev->phy;
  #endif
-@@ -876,6 +881,15 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+@@ -879,6 +884,15 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
  	if (phy->muru_onoff & MUMIMO_DL_CERT)
  		mt7915_mcu_set_mimo(phy, 0);
  #endif
@@ -543,7 +543,7 @@
  }
  
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 4390f42..e6e3152 100644
+index 7927a523..570dd171 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -1866,7 +1866,7 @@ mt7915_mcu_add_group(struct mt7915_dev *dev, struct ieee80211_vif *vif,
@@ -746,7 +746,7 @@
  {
  #define MT_BF_PROCESSING	4
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 2dce288..1d0cfa1 100644
+index d93ab970..9ea30f1d 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -141,6 +141,58 @@ struct mt7915_twt_flow {
@@ -816,7 +816,7 @@
  };
  
  struct mt7915_vif_cap {
-@@ -492,6 +545,8 @@ struct mt7915_dev {
+@@ -507,6 +560,8 @@ struct mt7915_dev {
  #endif
  
  	struct delayed_work scs_work;
@@ -825,7 +825,7 @@
  
  	bool wmm_pbc_enable;
  	struct work_struct wmm_pbc_work;
-@@ -528,6 +583,15 @@ enum mt7915_rdd_cmd {
+@@ -543,6 +598,15 @@ enum mt7915_rdd_cmd {
  	RDD_IRQ_OFF,
  };
  
@@ -841,7 +841,7 @@
  static inline struct mt7915_phy *
  mt7915_hw_phy(struct ieee80211_hw *hw)
  {
-@@ -657,6 +721,11 @@ int mt7915_mcu_set_mac(struct mt7915_dev *dev, int band, bool enable,
+@@ -672,6 +736,11 @@ int mt7915_mcu_set_mac(struct mt7915_dev *dev, int band, bool enable,
  int mt7915_mcu_set_test_param(struct mt7915_dev *dev, u8 param, bool test_mode,
  			      u8 en);
  int mt7915_mcu_set_ser(struct mt7915_dev *dev, u8 action, u8 set, u8 band);
@@ -854,7 +854,7 @@
  int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy);
  int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len,
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index 0f72f30..14ea5bc 100644
+index c8dd569c..d6e92b12 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -1518,6 +1518,136 @@ static void chip_get_sta_pause(struct mt7915_dev *dev, u32 *sta_pause)
diff --git a/recipes-wifi/linux-mt76/files/patches/2005-wifi-mt76-mt7915-wed-add-rxwi-for-further-in-chip-rr.patch b/recipes-wifi/linux-mt76/files/patches/2005-wifi-mt76-mt7915-wed-add-rxwi-for-further-in-chip-rr.patch
index 053fc86..ae9b7c0 100644
--- a/recipes-wifi/linux-mt76/files/patches/2005-wifi-mt76-mt7915-wed-add-rxwi-for-further-in-chip-rr.patch
+++ b/recipes-wifi/linux-mt76/files/patches/2005-wifi-mt76-mt7915-wed-add-rxwi-for-further-in-chip-rr.patch
@@ -1,4 +1,4 @@
-From 4faced46403673e8089b9c4cc89d55f7d9fd5e6d Mon Sep 17 00:00:00 2001
+From b6184a94736ddea3277b1bea374686edb9aef6b4 Mon Sep 17 00:00:00 2001
 From: Sujuan Chen <sujuan.chen@mediatek.com>
 Date: Fri, 6 Jan 2023 18:18:50 +0800
 Subject: [PATCH 2005/2015] wifi: mt76: mt7915: wed: add rxwi for further in
@@ -17,7 +17,7 @@
  8 files changed, 87 insertions(+), 80 deletions(-)
 
 diff --git a/dma.c b/dma.c
-index 185c6f1..9cd97d2 100644
+index 185c6f12..9cd97d24 100644
 --- a/dma.c
 +++ b/dma.c
 @@ -64,17 +64,17 @@ mt76_alloc_txwi(struct mt76_dev *dev)
@@ -245,10 +245,10 @@
  unmap:
  	for (n--; n > 0; n--)
 diff --git a/mac80211.c b/mac80211.c
-index f9dfdf8..225b290 100644
+index 750a642a..e50c68f8 100644
 --- a/mac80211.c
 +++ b/mac80211.c
-@@ -618,7 +618,6 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
+@@ -619,7 +619,6 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
  	spin_lock_init(&dev->lock);
  	spin_lock_init(&dev->cc_lock);
  	spin_lock_init(&dev->status_lock);
@@ -256,7 +256,7 @@
  	mutex_init(&dev->mutex);
  	init_waitqueue_head(&dev->tx_wait);
  
-@@ -651,6 +650,7 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
+@@ -652,6 +651,7 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
  	INIT_LIST_HEAD(&dev->txwi_cache);
  	INIT_LIST_HEAD(&dev->rxwi_cache);
  	dev->token_size = dev->drv->token_size;
@@ -265,7 +265,7 @@
  	for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++)
  		skb_queue_head_init(&dev->rx_skb[i]);
 diff --git a/mt76.h b/mt76.h
-index 6168758..5e71267 100644
+index 411a9576..40884fd8 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -193,6 +193,7 @@ struct mt76_queue_entry {
@@ -323,7 +323,7 @@
  void mt76_free_pending_rxwi(struct mt76_dev *dev);
  void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
  		      struct napi_struct *napi);
-@@ -1819,9 +1825,9 @@ mt76_token_release(struct mt76_dev *dev, int token, bool *wake);
+@@ -1831,9 +1837,9 @@ mt76_token_release(struct mt76_dev *dev, int token, bool *wake);
  int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi,
  		       u8 phy_idx);
  void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked);
@@ -336,7 +336,7 @@
  static inline void mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked)
  {
 diff --git a/mt7915/dma.c b/mt7915/dma.c
-index 0baa82c..552410a 100644
+index 0baa82c8..552410aa 100644
 --- a/mt7915/dma.c
 +++ b/mt7915/dma.c
 @@ -512,7 +512,6 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2)
@@ -356,7 +356,7 @@
  		}
  
 diff --git a/mt7915/mmio.c b/mt7915/mmio.c
-index 91100f1..3391a94 100644
+index 53648212..baf35a53 100644
 --- a/mt7915/mmio.c
 +++ b/mt7915/mmio.c
 @@ -725,7 +725,7 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr,
@@ -377,7 +377,7 @@
  		.tx_complete_skb = mt76_connac_tx_complete_skb,
  		.rx_skb = mt7915_queue_rx_skb,
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 1d0cfa1..f5a7e1e 100644
+index 9ea30f1d..55af5c8c 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -64,6 +64,7 @@
@@ -389,7 +389,7 @@
  #define MT7915_CFEND_RATE_DEFAULT	0x49	/* OFDM 24M */
  #define MT7915_CFEND_RATE_11B		0x03	/* 11B LP, 11M */
 diff --git a/tx.c b/tx.c
-index db0d4df..92afbf5 100644
+index db0d4df5..92afbf5d 100644
 --- a/tx.c
 +++ b/tx.c
 @@ -864,16 +864,16 @@ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi,
@@ -434,7 +434,7 @@
  }
  EXPORT_SYMBOL_GPL(mt76_rx_token_release);
 diff --git a/wed.c b/wed.c
-index 47c81a2..c03b52f 100644
+index 47c81a28..c03b52f9 100644
 --- a/wed.c
 +++ b/wed.c
 @@ -16,18 +16,18 @@ void mt76_wed_release_rx_buf(struct mtk_wed_device *wed)
diff --git a/recipes-wifi/linux-mt76/files/patches/2006-wifi-mt76-add-debugfs-knob-to-show-packet-error-rate.patch b/recipes-wifi/linux-mt76/files/patches/2006-wifi-mt76-add-debugfs-knob-to-show-packet-error-rate.patch
index 57dba95..a0c7a95 100644
--- a/recipes-wifi/linux-mt76/files/patches/2006-wifi-mt76-add-debugfs-knob-to-show-packet-error-rate.patch
+++ b/recipes-wifi/linux-mt76/files/patches/2006-wifi-mt76-add-debugfs-knob-to-show-packet-error-rate.patch
@@ -1,4 +1,4 @@
-From 358136dd0d94d84efda5c04e7b392111adcb2895 Mon Sep 17 00:00:00 2001
+From 35000e804c9cad780ee340efba7cebf461366cd3 Mon Sep 17 00:00:00 2001
 From: Peter Chiu <chui-hao.chiu@mediatek.com>
 Date: Wed, 11 Jan 2023 10:56:27 +0800
 Subject: [PATCH 2006/2015] wifi: mt76: add debugfs knob to show packet error
@@ -15,7 +15,7 @@
  6 files changed, 194 insertions(+), 1 deletion(-)
 
 diff --git a/mt76.h b/mt76.h
-index 5e71267..0ab1d74 100644
+index 40884fd8..7100934a 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -321,8 +321,10 @@ struct mt76_sta_stats {
@@ -30,10 +30,10 @@
  	u64 rx_bytes;
  	u32 rx_packets;
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 3786e69..91294a8 100644
+index 1721f9b5..3d00961c 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
-@@ -1207,6 +1207,7 @@ enum {
+@@ -1209,6 +1209,7 @@ enum {
  	MCU_EXT_CMD_EDCA_UPDATE = 0x27,
  	MCU_EXT_CMD_DEV_INFO_UPDATE = 0x2A,
  	MCU_EXT_CMD_THERMAL_CTRL = 0x2c,
@@ -42,7 +42,7 @@
  	MCU_EXT_CMD_SET_DRR_CTRL = 0x36,
  	MCU_EXT_CMD_SET_FEATURE_CTRL = 0x38,
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index e6e3152..8a07360 100644
+index 570dd171..8733a07d 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -4565,6 +4565,114 @@ int mt7915_mcu_get_tx_rate(struct mt7915_phy *phy, u16 wcidx)
@@ -161,7 +161,7 @@
  				struct cfg80211_he_bss_color *he_bss_color)
  {
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index 52baaa7..ec7ad7d 100644
+index 52baaa73..ec7ad7db 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -854,7 +854,8 @@ mt7915_get_power_bound(struct mt7915_phy *phy, s8 txpower)
@@ -200,10 +200,10 @@
     CAPI_SU,
     CAPI_MU,
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index f5a7e1e..aafdafd 100644
+index 55af5c8c..731f5f3e 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
-@@ -755,6 +755,7 @@ int mt7915_mcu_get_rx_rate(struct mt7915_phy *phy, struct ieee80211_vif *vif,
+@@ -770,6 +770,7 @@ int mt7915_mcu_get_rx_rate(struct mt7915_phy *phy, struct ieee80211_vif *vif,
  int mt7915_mcu_rdd_background_enable(struct mt7915_phy *phy,
  				     struct cfg80211_chan_def *chandef);
  int mt7915_mcu_wed_wa_tx_stats(struct mt7915_dev *dev, u16 wcid);
@@ -212,7 +212,7 @@
  int mt7915_mcu_wa_cmd(struct mt7915_dev *dev, int cmd, u32 a1, u32 a2, u32 a3);
  int mt7915_mcu_fw_log_2_host(struct mt7915_dev *dev, u8 type, u8 ctrl);
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index 14ea5bc..c63bf90 100644
+index d6e92b12..d64a3aec 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -4027,6 +4027,66 @@ mt7915_sr_scene_cond_show(struct seq_file *file, void *data)
diff --git a/recipes-wifi/linux-mt76/files/patches/2007-wifi-mt76-mt7915-add-ctxd-support-for-mt7916.patch b/recipes-wifi/linux-mt76/files/patches/2007-wifi-mt76-mt7915-add-ctxd-support-for-mt7916.patch
index 4f182ca..8052770 100644
--- a/recipes-wifi/linux-mt76/files/patches/2007-wifi-mt76-mt7915-add-ctxd-support-for-mt7916.patch
+++ b/recipes-wifi/linux-mt76/files/patches/2007-wifi-mt76-mt7915-add-ctxd-support-for-mt7916.patch
@@ -1,4 +1,4 @@
-From c6929b147d890eeef315aabfd868860e1b8d3288 Mon Sep 17 00:00:00 2001
+From fac0ee814e10b01fc8536f4da0e2e7cec474b5d7 Mon Sep 17 00:00:00 2001
 From: "sujuan.chen" <sujuan.chen@mediatek.com>
 Date: Thu, 6 Apr 2023 17:50:52 +0800
 Subject: [PATCH 2007/2015] wifi: mt76: mt7915: add ctxd support for mt7916
@@ -10,7 +10,7 @@
  2 files changed, 35 insertions(+)
 
 diff --git a/mt7915/dma.c b/mt7915/dma.c
-index 552410a..4f9f5a3 100644
+index 552410aa..4f9f5a38 100644
 --- a/mt7915/dma.c
 +++ b/mt7915/dma.c
 @@ -435,6 +435,26 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2)
@@ -41,7 +41,7 @@
  	} else {
  		mt76_clear(dev, MT_WFDMA_HOST_CONFIG, MT_WFDMA_HOST_CONFIG_WED);
 diff --git a/mt7915/regs.h b/mt7915/regs.h
-index ca355d1..d4acefc 100644
+index ca355d14..d4acefca 100644
 --- a/mt7915/regs.h
 +++ b/mt7915/regs.h
 @@ -607,6 +607,7 @@ enum offs_rev {
diff --git a/recipes-wifi/linux-mt76/files/patches/2008-wifi-mt76-connac-wed-add-wed-rx-copy-skb.patch b/recipes-wifi/linux-mt76/files/patches/2008-wifi-mt76-connac-wed-add-wed-rx-copy-skb.patch
index c96e9cc..6705920 100644
--- a/recipes-wifi/linux-mt76/files/patches/2008-wifi-mt76-connac-wed-add-wed-rx-copy-skb.patch
+++ b/recipes-wifi/linux-mt76/files/patches/2008-wifi-mt76-connac-wed-add-wed-rx-copy-skb.patch
@@ -1,4 +1,4 @@
-From 0e3c9337e58e45f3e2ec3a00692dedc994c1bac8 Mon Sep 17 00:00:00 2001
+From cfad406a8e50de85347861782835596f599bfe59 Mon Sep 17 00:00:00 2001
 From: Evelyn Tsai <evelyn.tsai@mediatek.com>
 Date: Sun, 4 Feb 2024 17:52:44 +0800
 Subject: [PATCH 2008/2015] wifi: mt76: connac: wed: add wed rx copy skb
@@ -10,7 +10,7 @@
  2 files changed, 80 insertions(+), 28 deletions(-)
 
 diff --git a/dma.c b/dma.c
-index 9cd97d2..d17fc88 100644
+index 9cd97d24..d17fc88c 100644
 --- a/dma.c
 +++ b/dma.c
 @@ -225,10 +225,10 @@ void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q)
@@ -138,7 +138,7 @@
  
  static void
 diff --git a/wed.c b/wed.c
-index c03b52f..70e4057 100644
+index c03b52f9..70e40575 100644
 --- a/wed.c
 +++ b/wed.c
 @@ -9,12 +9,9 @@
diff --git a/recipes-wifi/linux-mt76/files/patches/2009-wifi-mt76-mt7915-enable-wa-log-to-uart.patch b/recipes-wifi/linux-mt76/files/patches/2009-wifi-mt76-mt7915-enable-wa-log-to-uart.patch
index 14ad66b..730a493 100644
--- a/recipes-wifi/linux-mt76/files/patches/2009-wifi-mt76-mt7915-enable-wa-log-to-uart.patch
+++ b/recipes-wifi/linux-mt76/files/patches/2009-wifi-mt76-mt7915-enable-wa-log-to-uart.patch
@@ -1,4 +1,4 @@
-From fc34c39c5e3484ab7d438dd0f5a701c5256cc643 Mon Sep 17 00:00:00 2001
+From 3be6f92d47cdfd1ab676c9728cc84560fd2cee91 Mon Sep 17 00:00:00 2001
 From: Peter Chiu <chui-hao.chiu@mediatek.com>
 Date: Fri, 8 Sep 2023 18:26:21 +0800
 Subject: [PATCH 2009/2015] wifi: mt76: mt7915: enable wa log to uart
@@ -9,7 +9,7 @@
  1 file changed, 5 insertions(+), 1 deletion(-)
 
 diff --git a/mt7915/debugfs.c b/mt7915/debugfs.c
-index 3ae4aca..76d1951 100644
+index 576ec1a5..03ccb51b 100644
 --- a/mt7915/debugfs.c
 +++ b/mt7915/debugfs.c
 @@ -951,7 +951,11 @@ mt7915_fw_debug_wa_set(void *data, u64 val)
diff --git a/recipes-wifi/linux-mt76/files/patches/2010-wifi-mt76-mt7915-add-error-message-when-driver-recei.patch b/recipes-wifi/linux-mt76/files/patches/2010-wifi-mt76-mt7915-add-error-message-when-driver-recei.patch
index 616fa20..8bccb66 100644
--- a/recipes-wifi/linux-mt76/files/patches/2010-wifi-mt76-mt7915-add-error-message-when-driver-recei.patch
+++ b/recipes-wifi/linux-mt76/files/patches/2010-wifi-mt76-mt7915-add-error-message-when-driver-recei.patch
@@ -1,4 +1,4 @@
-From 2cdf9a78e39cbb91519b09393ea438c49df5b594 Mon Sep 17 00:00:00 2001
+From 24b3e43ac4d858dd17013087e4ea0b4b86002079 Mon Sep 17 00:00:00 2001
 From: Peter Chiu <chui-hao.chiu@mediatek.com>
 Date: Fri, 8 Sep 2023 18:29:32 +0800
 Subject: [PATCH 2010/2015] wifi: mt76: mt7915: add error message when driver
@@ -10,7 +10,7 @@
  1 file changed, 6 insertions(+)
 
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index 1c8b873..3f907e6 100644
+index 1c8b8732..3f907e61 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -1007,6 +1007,12 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len)
diff --git a/recipes-wifi/linux-mt76/files/patches/2011-wifi-mt76-mt7915-wed-change-wed-token-init-size-to-a.patch b/recipes-wifi/linux-mt76/files/patches/2011-wifi-mt76-mt7915-wed-change-wed-token-init-size-to-a.patch
index 54bdd75..21e6647 100644
--- a/recipes-wifi/linux-mt76/files/patches/2011-wifi-mt76-mt7915-wed-change-wed-token-init-size-to-a.patch
+++ b/recipes-wifi/linux-mt76/files/patches/2011-wifi-mt76-mt7915-wed-change-wed-token-init-size-to-a.patch
@@ -1,4 +1,4 @@
-From a21e557d571d24801dbc7ebd403cefaec0b30f02 Mon Sep 17 00:00:00 2001
+From a1da16a0e8de7d47d6dbd43e2e677edd6285387a Mon Sep 17 00:00:00 2001
 From: "sujuan.chen" <sujuan.chen@mediatek.com>
 Date: Mon, 11 Sep 2023 17:57:32 +0800
 Subject: [PATCH 2011/2015] wifi: mt76: mt7915: wed: change wed token init size
@@ -16,7 +16,7 @@
  7 files changed, 33 insertions(+), 26 deletions(-)
 
 diff --git a/mt76.h b/mt76.h
-index 0ab1d74..5bf6ba6 100644
+index 7100934a..07dc6b34 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -29,6 +29,8 @@
@@ -29,7 +29,7 @@
  #define MT_QFLAG_WED_TYPE	GENMASK(4, 2)
  #define MT_QFLAG_WED		BIT(5)
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index 3f907e6..02f794d 100644
+index 3f907e61..02f794d4 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -940,7 +940,7 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len)
@@ -64,7 +64,7 @@
  					msdu);
  
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 8a07360..708e7cd 100644
+index 8733a07d..e856f37f 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -2509,6 +2509,9 @@ mt7915_mcu_init_rx_airtime(struct mt7915_dev *dev)
@@ -89,7 +89,7 @@
  
  	return mt76_mcu_send_msg(&dev->mt76, MCU_WA_PARAM_CMD(SET), &req,
 diff --git a/mt7915/mmio.c b/mt7915/mmio.c
-index 3391a94..6309dd9 100644
+index baf35a53..6ade056a 100644
 --- a/mt7915/mmio.c
 +++ b/mt7915/mmio.c
 @@ -695,11 +695,14 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr,
@@ -119,7 +119,7 @@
  	ret = dma_set_mask(wed->dev, DMA_BIT_MASK(32));
  	if (ret)
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index aafdafd..945c82e 100644
+index 731f5f3e..61500841 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -62,7 +62,9 @@
@@ -134,7 +134,7 @@
  #define MT7915_RX_TOKEN_SIZE		4096
  
 diff --git a/tx.c b/tx.c
-index 92afbf5..df2bb07 100644
+index 92afbf5d..df2bb07d 100644
 --- a/tx.c
 +++ b/tx.c
 @@ -828,7 +828,7 @@ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi,
@@ -189,7 +189,7 @@
  
  	if (dev->token_count < dev->token_size - MT76_TOKEN_FREE_THR &&
 diff --git a/wed.c b/wed.c
-index 70e4057..5ed681e 100644
+index 70e40575..5ed681ed 100644
 --- a/wed.c
 +++ b/wed.c
 @@ -118,7 +118,7 @@ int mt76_wed_offload_enable(struct mtk_wed_device *wed)
diff --git a/recipes-wifi/linux-mt76/files/patches/2012-wifi-mt76-mt7915-wed-add-per-bss-statistic-info.patch b/recipes-wifi/linux-mt76/files/patches/2012-wifi-mt76-mt7915-wed-add-per-bss-statistic-info.patch
index 9b86205..7854dec 100644
--- a/recipes-wifi/linux-mt76/files/patches/2012-wifi-mt76-mt7915-wed-add-per-bss-statistic-info.patch
+++ b/recipes-wifi/linux-mt76/files/patches/2012-wifi-mt76-mt7915-wed-add-per-bss-statistic-info.patch
@@ -1,7 +1,7 @@
-From 4e0a12871b8c134c99bd702f425c492ddc1f50b6 Mon Sep 17 00:00:00 2001
+From c9a8c7cdd9a5e042dc691fb70c24d4ee6832f297 Mon Sep 17 00:00:00 2001
 From: Evelyn Tsai <evelyn.tsai@mediatek.com>
 Date: Wed, 1 Nov 2023 07:50:08 +0800
-Subject: [PATCH 1/5] wifi: mt76: mt7915: wed: add per bss statistic info
+Subject: [PATCH 2012/2015] wifi: mt76: mt7915: wed: add per bss statistic info
 
 ---
  mt7915/init.c        |  1 +
@@ -14,10 +14,10 @@
  7 files changed, 81 insertions(+), 7 deletions(-)
 
 diff --git a/mt7915/init.c b/mt7915/init.c
-index f38c8a1..7bc8039 100644
+index 7f210669..26dc1a7a 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
-@@ -400,6 +400,7 @@ mt7915_init_wiphy(struct mt7915_phy *phy)
+@@ -401,6 +401,7 @@ mt7915_init_wiphy(struct mt7915_phy *phy)
  	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_DISCOVERY);
  	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT);
  	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
@@ -26,7 +26,7 @@
  	if (!is_mt7915(&dev->mt76))
  		wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_STA_TX_PWR);
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index 02f794d..0c12170 100644
+index 02f794d4..0c121700 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -1071,6 +1071,7 @@ static void mt7915_mac_add_txs(struct mt7915_dev *dev, void *data)
@@ -79,10 +79,10 @@
  		spin_lock_bh(&phy->stats_lock);
  	}
 diff --git a/mt7915/main.c b/mt7915/main.c
-index feb2c89..722635e 100644
+index ecfd9307..802d9e6f 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
-@@ -1219,6 +1219,9 @@ static void mt7915_sta_statistics(struct ieee80211_hw *hw,
+@@ -1231,6 +1231,9 @@ static void mt7915_sta_statistics(struct ieee80211_hw *hw,
  	struct rate_info *txrate = &msta->wcid.rate;
  	struct rate_info rxrate = {};
  
@@ -92,7 +92,7 @@
  	if (is_connac_v2(&phy->dev->mt76) &&
  	    !mt7915_mcu_get_rx_rate(phy, vif, sta, &rxrate)) {
  		sinfo->rxrate = rxrate;
-@@ -1247,7 +1250,7 @@ static void mt7915_sta_statistics(struct ieee80211_hw *hw,
+@@ -1259,7 +1262,7 @@ static void mt7915_sta_statistics(struct ieee80211_hw *hw,
  		sinfo->tx_bytes = msta->wcid.stats.tx_bytes;
  		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES64);
  
@@ -102,7 +102,7 @@
  			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);
  		}
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 708e7cd..342fe42 100644
+index e856f37f..a1b4afee 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -4750,7 +4750,8 @@ int mt7915_mcu_twt_agrt_update(struct mt7915_dev *dev,
@@ -153,7 +153,7 @@
  out:
  	dev_kfree_skb(skb);
 diff --git a/mt7915/mmio.c b/mt7915/mmio.c
-index 6309dd9..142f308 100644
+index 6ade056a..ab807369 100644
 --- a/mt7915/mmio.c
 +++ b/mt7915/mmio.c
 @@ -592,7 +592,7 @@ static void mt7915_mmio_wed_update_rx_stats(struct mtk_wed_device *wed,
@@ -199,10 +199,10 @@
  }
  
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 945c82e..6e7ed9e 100644
+index 61500841..5e7b7ebe 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
-@@ -756,7 +756,8 @@ int mt7915_mcu_get_rx_rate(struct mt7915_phy *phy, struct ieee80211_vif *vif,
+@@ -771,7 +771,8 @@ int mt7915_mcu_get_rx_rate(struct mt7915_phy *phy, struct ieee80211_vif *vif,
  			   struct ieee80211_sta *sta, struct rate_info *rate);
  int mt7915_mcu_rdd_background_enable(struct mt7915_phy *phy,
  				     struct cfg80211_chan_def *chandef);
@@ -213,7 +213,7 @@
  int mt7915_mcu_rf_regval(struct mt7915_dev *dev, u32 regidx, u32 *val, bool set);
  int mt7915_mcu_wa_cmd(struct mt7915_dev *dev, int cmd, u32 a1, u32 a2, u32 a3);
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index c63bf90..2c9f198 100644
+index d64a3aec..c4eb7368 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -4034,7 +4034,7 @@ static int mt7915_reset_counter(void *data, u64 val)
diff --git a/recipes-wifi/linux-mt76/files/patches/2013-wifi-mt76-add-debugfs-for-tx-drop-counters.patch b/recipes-wifi/linux-mt76/files/patches/2013-wifi-mt76-add-debugfs-for-tx-drop-counters.patch
index 8cb87e0..19fb831 100644
--- a/recipes-wifi/linux-mt76/files/patches/2013-wifi-mt76-add-debugfs-for-tx-drop-counters.patch
+++ b/recipes-wifi/linux-mt76/files/patches/2013-wifi-mt76-add-debugfs-for-tx-drop-counters.patch
@@ -1,7 +1,7 @@
-From d58cdc551155267c9c57606b0f8098dbcfbb75fc Mon Sep 17 00:00:00 2001
+From 86545609cc73f064e34e6783175b502623e08217 Mon Sep 17 00:00:00 2001
 From: Peter Chiu <chui-hao.chiu@mediatek.com>
 Date: Mon, 18 Mar 2024 14:16:34 +0800
-Subject: [PATCH 1/4] wifi: mt76: add debugfs for tx drop counters
+Subject: [PATCH 2013/2015] wifi: mt76: add debugfs for tx drop counters
 
 Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
 ---
@@ -13,7 +13,7 @@
  5 files changed, 99 insertions(+), 8 deletions(-)
 
 diff --git a/dma.c b/dma.c
-index d17fc88..da3e8bc 100644
+index d17fc88c..da3e8bc3 100644
 --- a/dma.c
 +++ b/dma.c
 @@ -612,13 +612,18 @@ mt76_dma_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q,
@@ -96,7 +96,7 @@
  }
  
 diff --git a/mt76.h b/mt76.h
-index da5875f..2d59091 100644
+index 07dc6b34..08f1a7ce 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -849,6 +849,27 @@ struct mt76_vif {
@@ -136,7 +136,7 @@
  
  struct mt76_dev {
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index 0c12170..1e2ef8c 100644
+index 0c121700..1e2ef8c0 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -782,9 +782,15 @@ int mt7915_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
@@ -176,7 +176,7 @@
  	t->jiffies = jiffies;
  
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index 2c9f198..1b2fbf9 100644
+index c4eb7368..abb09b76 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -4033,6 +4033,12 @@ static int mt7915_reset_counter(void *data, u64 val)
@@ -239,7 +239,7 @@
 +
  #endif
 diff --git a/tx.c b/tx.c
-index df2bb07..e4eb74b 100644
+index df2bb07d..e4eb74b7 100644
 --- a/tx.c
 +++ b/tx.c
 @@ -330,8 +330,10 @@ mt76_tx(struct mt76_phy *phy, struct ieee80211_sta *sta,
diff --git a/recipes-wifi/linux-mt76/files/patches/2014-wifi-mt76-add-debugfs-for-rx-drop-counters.patch b/recipes-wifi/linux-mt76/files/patches/2014-wifi-mt76-add-debugfs-for-rx-drop-counters.patch
index 8668aaf..248ddd3 100644
--- a/recipes-wifi/linux-mt76/files/patches/2014-wifi-mt76-add-debugfs-for-rx-drop-counters.patch
+++ b/recipes-wifi/linux-mt76/files/patches/2014-wifi-mt76-add-debugfs-for-rx-drop-counters.patch
@@ -1,7 +1,7 @@
-From dd3b663524af8f38dc1e2a04e1722f5b7d96d514 Mon Sep 17 00:00:00 2001
+From 8c618a9481eb07cd9699cb6266d34b6fd485f44a Mon Sep 17 00:00:00 2001
 From: Peter Chiu <chui-hao.chiu@mediatek.com>
 Date: Thu, 23 May 2024 02:33:47 +0800
-Subject: [PATCH 2/4] wifi: mt76: add debugfs for rx drop counters
+Subject: [PATCH 2014/2015] wifi: mt76: add debugfs for rx drop counters
 
 Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
 ---
@@ -15,7 +15,7 @@
  7 files changed, 140 insertions(+), 13 deletions(-)
 
 diff --git a/agg-rx.c b/agg-rx.c
-index 07c386c..97a963a 100644
+index 07c386c7..97a963ad 100644
 --- a/agg-rx.c
 +++ b/agg-rx.c
 @@ -151,6 +151,7 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
@@ -52,7 +52,7 @@
  	}
  
 diff --git a/dma.c b/dma.c
-index da3e8bc..782463f 100644
+index da3e8bc3..782463f6 100644
 --- a/dma.c
 +++ b/dma.c
 @@ -251,13 +251,16 @@ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
@@ -158,7 +158,7 @@
  		skb_reserve(skb, q->buf_offset);
  
 diff --git a/dma.h b/dma.h
-index 619dc0f..6b2ee7e 100644
+index 619dc0fe..6b2ee7ec 100644
 --- a/dma.h
 +++ b/dma.h
 @@ -92,27 +92,29 @@ mt76_dma_reset_tx_queue(struct mt76_dev *dev, struct mt76_queue *q)
@@ -198,7 +198,7 @@
  
  #endif
 diff --git a/mac80211.c b/mac80211.c
-index c31d130..6980fe7 100644
+index e50c68f8..e01ce59a 100644
 --- a/mac80211.c
 +++ b/mac80211.c
 @@ -775,6 +775,7 @@ static void mt76_rx_release_amsdu(struct mt76_phy *phy, enum mt76_rxq_id q)
@@ -236,7 +236,7 @@
  
  static void
 diff --git a/mt76.h b/mt76.h
-index 2d59091..11898b8 100644
+index 08f1a7ce..32327d3b 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -175,6 +175,33 @@ enum mt76_dfs_state {
@@ -305,7 +305,7 @@
  
  struct mt76_dev {
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index 1e2ef8c..195b5f6 100644
+index 1e2ef8c0..195b5f62 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -1175,9 +1175,11 @@ void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
@@ -342,7 +342,7 @@
  		break;
  	}
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index 1b2fbf9..a33a90d 100644
+index abb09b76..5413291a 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -4032,9 +4032,12 @@ static int mt7915_reset_counter(void *data, u64 val)
diff --git a/recipes-wifi/linux-mt76/files/patches/2015-wifi-mt76-mt7915-support-backaward-compatiable.patch b/recipes-wifi/linux-mt76/files/patches/2015-wifi-mt76-mt7915-support-backaward-compatiable.patch
index c7c6707..fbca3a6 100644
--- a/recipes-wifi/linux-mt76/files/patches/2015-wifi-mt76-mt7915-support-backaward-compatiable.patch
+++ b/recipes-wifi/linux-mt76/files/patches/2015-wifi-mt76-mt7915-support-backaward-compatiable.patch
@@ -1,7 +1,7 @@
-From eebe902614c5e2759492afcd1f84665d241077cb Mon Sep 17 00:00:00 2001
+From fd382c52114f01db7d28f60b3e8917b4c9dfd301 Mon Sep 17 00:00:00 2001
 From: Rex Lu <rex.lu@mediatek.com>
 Date: Mon, 11 Dec 2023 19:21:16 +0800
-Subject: [PATCH 3/4] wifi: mt76: mt7915: support backaward compatiable
+Subject: [PATCH 2015/2015] wifi: mt76: mt7915: support backaward compatiable
 
 ---
  mt7915/mmio.c | 4 ++--
@@ -9,7 +9,7 @@
  2 files changed, 3 insertions(+), 3 deletions(-)
 
 diff --git a/mt7915/mmio.c b/mt7915/mmio.c
-index 142f308..11db3ed 100644
+index ab807369..1ea91676 100644
 --- a/mt7915/mmio.c
 +++ b/mt7915/mmio.c
 @@ -697,7 +697,7 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr,
@@ -31,7 +31,7 @@
  
  	wed->wlan.nbuf = is_mt7915(&dev->mt76) ?
 diff --git a/wed.c b/wed.c
-index 5ed681e..652f59e 100644
+index 5ed681ed..652f59e1 100644
 --- a/wed.c
 +++ b/wed.c
 @@ -175,7 +175,7 @@ int mt76_wed_dma_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
diff --git a/recipes-wifi/linux-mt76/files/patches/9999-mt76-revert-for-backports-5.15-wireless-stack.patch b/recipes-wifi/linux-mt76/files/patches/9999-mt76-revert-for-backports-5.15-wireless-stack.patch
index 976378d..7e7fe6f 100644
--- a/recipes-wifi/linux-mt76/files/patches/9999-mt76-revert-for-backports-5.15-wireless-stack.patch
+++ b/recipes-wifi/linux-mt76/files/patches/9999-mt76-revert-for-backports-5.15-wireless-stack.patch
@@ -1,32 +1,32 @@
-From b797380172c9191f7de318ee2db475583d67ed0d Mon Sep 17 00:00:00 2001
+From 1d910139c69d3baae9937d8416bc172bcd07d160 Mon Sep 17 00:00:00 2001
 From: Evelyn Tsai <evelyn.tsai@mediatek.com>
 Date: Wed, 5 Apr 2023 08:29:19 +0800
-Subject: [PATCH 4/4] mt76: revert for backports-5.15 wireless stack
+Subject: [PATCH] mt76: revert for backports-5.15 wireless stack
 
 wifi: mt76: mt7915: add support for he ldpc control from hostapd
 wifi: mt76: only include mt76_connac3_mac for WiFi7 chipsets
 ---
  Makefile          |   6 +-
  dma.c             |   2 +-
- mac80211.c        |  15 +--
+ mac80211.c        |  19 ++--
  mt7615/dma.c      |   4 +-
  mt7615/main.c     |   6 +-
- mt7615/mcu.c      |   8 +-
+ mt7615/mcu.c      |   6 +-
  mt76_connac_mac.c |   2 +-
  mt76_connac_mcu.c | 119 ++++++++++------------
  mt76x02_mac.c     |   6 +-
  mt7915/debugfs.c  |   4 +-
  mt7915/dma.c      |   4 +-
- mt7915/init.c     |   3 +-
- mt7915/main.c     |  36 ++-----
- mt7915/mcu.c      | 246 ++++++++++++++++++++++++++++++----------------
+ mt7915/init.c     |  12 ++-
+ mt7915/main.c     |  40 ++------
+ mt7915/mcu.c      | 252 ++++++++++++++++++++++++++++++----------------
  mt7915/testmode.c |   8 +-
- tx.c              |  22 ++---
+ tx.c              |  22 ++--
  wed.c             |   1 +
- 17 files changed, 265 insertions(+), 227 deletions(-)
+ 17 files changed, 275 insertions(+), 238 deletions(-)
 
 diff --git a/Makefile b/Makefile
-index 5352d00..073e37d 100644
+index 5352d000..073e37d7 100644
 --- a/Makefile
 +++ b/Makefile
 @@ -31,7 +31,11 @@ mt76x02-lib-y := mt76x02_util.o mt76x02_mac.o mt76x02_mcu.o \
@@ -43,7 +43,7 @@
  mt792x-lib-y := mt792x_core.o mt792x_mac.o mt792x_trace.o \
  		mt792x_debugfs.o mt792x_dma.o
 diff --git a/dma.c b/dma.c
-index 782463f..39fafce 100644
+index 782463f6..39fafce0 100644
 --- a/dma.c
 +++ b/dma.c
 @@ -1052,7 +1052,7 @@ mt76_dma_init(struct mt76_dev *dev,
@@ -56,7 +56,7 @@
  		napi_enable(&dev->napi[i]);
  	}
 diff --git a/mac80211.c b/mac80211.c
-index 6980fe7..e9c2dc2 100644
+index e01ce59a..a41bf2f9 100644
 --- a/mac80211.c
 +++ b/mac80211.c
 @@ -1062,14 +1062,9 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
@@ -77,16 +77,18 @@
  	status->rate_idx = mstat.rate_idx;
  	status->nss = mstat.nss;
  	status->band = mstat.band;
-@@ -1592,7 +1587,7 @@ EXPORT_SYMBOL_GPL(mt76_get_sar_power);
+@@ -1592,8 +1587,8 @@ EXPORT_SYMBOL_GPL(mt76_get_sar_power);
  static void
  __mt76_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
  {
--	if (vif->bss_conf.csa_active && ieee80211_beacon_cntdwn_is_complete(vif))
+-	if (vif->bss_conf.csa_active && ieee80211_beacon_cntdwn_is_complete(vif, 0))
+-		ieee80211_csa_finish(vif, 0);
 +	if (vif->csa_active && ieee80211_beacon_cntdwn_is_complete(vif))
- 		ieee80211_csa_finish(vif);
++		ieee80211_csa_finish(vif);
  }
  
-@@ -1614,7 +1609,7 @@ __mt76_csa_check(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ void mt76_csa_finish(struct mt76_dev *dev)
+@@ -1614,10 +1609,10 @@ __mt76_csa_check(void *priv, u8 *mac, struct ieee80211_vif *vif)
  {
  	struct mt76_dev *dev = priv;
  
@@ -94,9 +96,13 @@
 +	if (!vif->csa_active)
  		return;
  
- 	dev->csa_complete |= ieee80211_beacon_cntdwn_is_complete(vif);
+-	dev->csa_complete |= ieee80211_beacon_cntdwn_is_complete(vif, 0);
++	dev->csa_complete |= ieee80211_beacon_cntdwn_is_complete(vif);
+ }
+ 
+ void mt76_csa_check(struct mt76_dev *dev)
 diff --git a/mt7615/dma.c b/mt7615/dma.c
-index e7135b2..6767c39 100644
+index e7135b2f..6767c39d 100644
 --- a/mt7615/dma.c
 +++ b/mt7615/dma.c
 @@ -282,8 +282,8 @@ int mt7615_dma_init(struct mt7615_dev *dev)
@@ -111,7 +117,7 @@
  
  	mt76_poll(dev, MT_WPDMA_GLO_CFG,
 diff --git a/mt7615/main.c b/mt7615/main.c
-index dab16b5..d32a752 100644
+index c27acaf0..1bdd703a 100644
 --- a/mt7615/main.c
 +++ b/mt7615/main.c
 @@ -473,7 +473,7 @@ static int mt7615_config(struct ieee80211_hw *hw, u32 changed)
@@ -142,18 +148,9 @@
  	if (changed & BSS_CHANGED_MU_GROUPS)
  		 mt7615_update_mu_group(hw, vif, info);
 diff --git a/mt7615/mcu.c b/mt7615/mcu.c
-index c9444c6..466f38e 100644
+index a9310660..c76594d6 100644
 --- a/mt7615/mcu.c
 +++ b/mt7615/mcu.c
-@@ -353,7 +353,7 @@ out:
- static void
- mt7615_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
- {
--	if (vif->bss_conf.csa_active)
-+	if (vif->csa_active)
- 		ieee80211_csa_finish(vif);
- }
- 
 @@ -699,7 +699,7 @@ mt7615_mcu_add_beacon_offload(struct mt7615_dev *dev,
  	if (!enable)
  		goto out;
@@ -182,7 +179,7 @@
  		.bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int),
  	};
 diff --git a/mt76_connac_mac.c b/mt76_connac_mac.c
-index 21dadb5..fbad20e 100644
+index 21dadb55..fbad20ec 100644
 --- a/mt76_connac_mac.c
 +++ b/mt76_connac_mac.c
 @@ -1132,7 +1132,7 @@ void mt76_connac2_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi)
@@ -195,7 +192,7 @@
  
  	tid = le32_get_bits(txwi[1], MT_TXD1_TID);
 diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
-index 15e61c9..dd23643 100644
+index 0ce7ecdc..aee9b5bf 100644
 --- a/mt76_connac_mcu.c
 +++ b/mt76_connac_mcu.c
 @@ -199,7 +199,7 @@ int mt76_connac_mcu_set_vif_ps(struct mt76_dev *dev, struct ieee80211_vif *vif)
@@ -578,7 +575,7 @@
  	return mt76_mcu_skb_send_msg(dev, skb, MCU_UNI_CMD(OFFLOAD), true);
  }
 diff --git a/mt76x02_mac.c b/mt76x02_mac.c
-index d5db6ff..fec3d10 100644
+index d5db6ffd..fec3d10d 100644
 --- a/mt76x02_mac.c
 +++ b/mt76x02_mac.c
 @@ -404,7 +404,7 @@ void mt76x02_mac_write_txwi(struct mt76x02_dev *dev, struct mt76x02_txwi *txwi,
@@ -603,10 +600,10 @@
  		if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
  			ba_size = 0;
 diff --git a/mt7915/debugfs.c b/mt7915/debugfs.c
-index 76d1951..e22e777 100644
+index 03ccb51b..72867ff7 100644
 --- a/mt7915/debugfs.c
 +++ b/mt7915/debugfs.c
-@@ -2052,8 +2052,8 @@ static ssize_t mt7915_sta_fixed_rate_set(struct file *file,
+@@ -2100,8 +2100,8 @@ static ssize_t mt7915_sta_fixed_rate_set(struct file *file,
  
  	phy.ldpc = (phy.bw || phy.ldpc) * GENMASK(2, 0);
  	for (i = 0; i <= phy.bw; i++) {
@@ -618,7 +615,7 @@
  	field = RATE_PARAM_FIXED;
  
 diff --git a/mt7915/dma.c b/mt7915/dma.c
-index 4f9f5a3..fde6a38 100644
+index 4f9f5a38..fde6a385 100644
 --- a/mt7915/dma.c
 +++ b/mt7915/dma.c
 @@ -596,8 +596,8 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2)
@@ -633,10 +630,10 @@
  
  	mt7915_dma_enable(dev, false);
 diff --git a/mt7915/init.c b/mt7915/init.c
-index 7bc8039..f1cacc1 100644
+index 26dc1a7a..e8e3b94a 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
-@@ -1155,8 +1155,7 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band,
+@@ -1163,8 +1163,7 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band,
  			mt76_connac_gen_ppe_thresh(he_cap->ppe_thres, nss);
  		} else {
  			he_cap_elem->phy_cap_info[9] |=
@@ -646,11 +643,41 @@
  		}
  
  		if (band == NL80211_BAND_6GHZ) {
+@@ -1198,7 +1197,8 @@ void mt7915_set_stream_he_caps(struct mt7915_phy *phy)
+ 		n = mt7915_init_he_caps(phy, NL80211_BAND_2GHZ, data);
+ 
+ 		band = &phy->mt76->sband_2g.sband;
+-		_ieee80211_set_sband_iftype_data(band, data, n);
++		band->iftype_data = data;
++		band->n_iftype_data = n;
+ 	}
+ 
+ 	if (phy->mt76->cap.has_5ghz) {
+@@ -1206,7 +1206,8 @@ void mt7915_set_stream_he_caps(struct mt7915_phy *phy)
+ 		n = mt7915_init_he_caps(phy, NL80211_BAND_5GHZ, data);
+ 
+ 		band = &phy->mt76->sband_5g.sband;
+-		_ieee80211_set_sband_iftype_data(band, data, n);
++		band->iftype_data = data;
++		band->n_iftype_data = n;
+ 	}
+ 
+ 	if (phy->mt76->cap.has_6ghz) {
+@@ -1214,7 +1215,8 @@ void mt7915_set_stream_he_caps(struct mt7915_phy *phy)
+ 		n = mt7915_init_he_caps(phy, NL80211_BAND_6GHZ, data);
+ 
+ 		band = &phy->mt76->sband_6g.sband;
+-		_ieee80211_set_sband_iftype_data(band, data, n);
++		band->iftype_data = data;
++		band->n_iftype_data = n;
+ 	}
+ }
+ 
 diff --git a/mt7915/main.c b/mt7915/main.c
-index 722635e..98efaf8 100644
+index 802d9e6f..93120b9f 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
-@@ -548,7 +548,7 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed)
+@@ -551,7 +551,7 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed)
  
  static int
  mt7915_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
@@ -659,7 +686,7 @@
  	       const struct ieee80211_tx_queue_params *params)
  {
  	struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
-@@ -650,7 +650,7 @@ mt7915_update_bss_color(struct ieee80211_hw *hw,
+@@ -653,7 +653,7 @@ mt7915_update_bss_color(struct ieee80211_hw *hw,
  static void mt7915_bss_info_changed(struct ieee80211_hw *hw,
  				    struct ieee80211_vif *vif,
  				    struct ieee80211_bss_conf *info,
@@ -668,7 +695,7 @@
  {
  	struct mt7915_phy *phy = mt7915_hw_phy(hw);
  	struct mt7915_dev *dev = mt7915_hw_dev(hw);
-@@ -666,7 +666,7 @@ static void mt7915_bss_info_changed(struct ieee80211_hw *hw,
+@@ -669,7 +669,7 @@ static void mt7915_bss_info_changed(struct ieee80211_hw *hw,
  	    vif->type == NL80211_IFTYPE_STATION)
  		set_bss_info = set_sta = !is_zero_ether_addr(info->bssid);
  	if (changed & BSS_CHANGED_ASSOC)
@@ -677,7 +704,7 @@
  	if (changed & BSS_CHANGED_BEACON_ENABLED &&
  	    vif->type != NL80211_IFTYPE_AP)
  		set_bss_info = set_sta = info->enable_beacon;
-@@ -714,27 +714,8 @@ static void mt7915_bss_info_changed(struct ieee80211_hw *hw,
+@@ -717,27 +717,8 @@ static void mt7915_bss_info_changed(struct ieee80211_hw *hw,
  	mutex_unlock(&dev->mt76.mutex);
  }
  
@@ -706,7 +733,7 @@
  {
  	struct mt7915_phy *phy = mt7915_hw_phy(hw);
  	struct mt7915_dev *dev = mt7915_hw_dev(hw);
-@@ -742,8 +723,6 @@ mt7915_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+@@ -745,8 +726,6 @@ mt7915_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
  
  	mutex_lock(&dev->mt76.mutex);
  
@@ -715,7 +742,7 @@
  	err = mt7915_mcu_add_bss_info(phy, vif, true);
  	if (err)
  		goto out;
-@@ -755,8 +734,7 @@ out:
+@@ -758,8 +737,7 @@ out:
  }
  
  static void
@@ -725,7 +752,7 @@
  {
  	struct mt7915_dev *dev = mt7915_hw_dev(hw);
  
-@@ -1379,10 +1357,10 @@ static int mt7915_sta_set_txpwr(struct ieee80211_hw *hw,
+@@ -1391,10 +1369,10 @@ static int mt7915_sta_set_txpwr(struct ieee80211_hw *hw,
  {
  	struct mt7915_phy *phy = mt7915_hw_phy(hw);
  	struct mt7915_dev *dev = mt7915_hw_dev(hw);
@@ -738,8 +765,19 @@
  		txpower = 0;
  
  	mutex_lock(&dev->mt76.mutex);
+@@ -1885,10 +1863,6 @@ mt7915_net_fill_receive_path(struct ieee80211_hw *hw,
+ #endif
+ 
+ const struct ieee80211_ops mt7915_ops = {
+-	.add_chanctx = ieee80211_emulate_add_chanctx,
+-	.remove_chanctx = ieee80211_emulate_remove_chanctx,
+-	.change_chanctx = ieee80211_emulate_change_chanctx,
+-	.switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx,
+ 	.tx = mt7915_tx,
+ 	.start = mt7915_start,
+ 	.stop = mt7915_stop,
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 7a67b52..34ea5bf 100644
+index a1b4afee..2c5943b4 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -67,7 +67,7 @@ mt7915_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs,
@@ -793,7 +831,7 @@
  }
  
  static int
-@@ -243,7 +243,7 @@ int mt7915_mcu_wa_cmd(struct mt7915_dev *dev, int cmd, u32 a1, u32 a2, u32 a3)
+@@ -243,10 +243,10 @@ int mt7915_mcu_wa_cmd(struct mt7915_dev *dev, int cmd, u32 a1, u32 a2, u32 a3)
  static void
  mt7915_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
  {
@@ -801,7 +839,11 @@
 +	if (!vif->csa_active || vif->type == NL80211_IFTYPE_STATION)
  		return;
  
+-	ieee80211_csa_finish(vif, 0);
++	ieee80211_csa_finish(vif);
+ }
+ 
- 	ieee80211_csa_finish(vif);
+ static void
 @@ -349,7 +349,7 @@ mt7915_mcu_rx_log_message(struct mt7915_dev *dev, struct sk_buff *skb)
  static void
  mt7915_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
@@ -811,6 +853,19 @@
  		return;
  
  	ieee80211_color_change_finish(vif);
+@@ -681,10 +681,10 @@ static bool mt7915_check_he_obss_narrow_bw_ru(struct ieee80211_hw *hw,
+ 		.tolerated = true,
+ 	};
+ 
+-	if (!(vif->bss_conf.chanreq.oper.chan->flags & IEEE80211_CHAN_RADAR))
++	if (!(vif->bss_conf.chandef.chan->flags & IEEE80211_CHAN_RADAR))
+ 		return false;
+ 
+-	cfg80211_bss_iter(hw->wiphy, &vif->bss_conf.chanreq.oper,
++	cfg80211_bss_iter(hw->wiphy, &vif->bss_conf.chandef,
+ 			  mt7915_check_he_obss_narrow_bw_ru_iter,
+ 			  &iter_data);
+ 
 @@ -945,13 +945,13 @@ mt7915_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta,
  		      struct ieee80211_vif *vif)
  {
@@ -1353,7 +1408,7 @@
  					len = sku_len[SKU_HE_RU242] * 4;
  				}
 diff --git a/mt7915/testmode.c b/mt7915/testmode.c
-index ecd6271..b2c442b 100644
+index ecd62712..b2c442b0 100644
 --- a/mt7915/testmode.c
 +++ b/mt7915/testmode.c
 @@ -418,12 +418,12 @@ mt7915_tm_entry_add(struct mt7915_phy *phy, u8 aid)
@@ -1374,7 +1429,7 @@
  	sta->wme = 1;
  
 diff --git a/tx.c b/tx.c
-index e4eb74b..a3654a9 100644
+index e4eb74b7..a3654a9b 100644
 --- a/tx.c
 +++ b/tx.c
 @@ -60,20 +60,15 @@ mt76_tx_status_unlock(struct mt76_dev *dev, struct sk_buff_head *list)
@@ -1430,7 +1485,7 @@
  		ieee80211_tx_status_ext(hw, &status);
  		spin_unlock_bh(&dev->rx_lock);
 diff --git a/wed.c b/wed.c
-index 652f59e..1c2ad30 100644
+index 652f59e1..1c2ad302 100644
 --- a/wed.c
 +++ b/wed.c
 @@ -80,6 +80,7 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
diff --git a/recipes-wifi/linux-mt76/files/patches/patches.inc b/recipes-wifi/linux-mt76/files/patches/patches.inc
index f5d6773..2dd33c4 100644
--- a/recipes-wifi/linux-mt76/files/patches/patches.inc
+++ b/recipes-wifi/linux-mt76/files/patches/patches.inc
@@ -21,6 +21,7 @@
     file://0019-wifi-mt76-mt7915-adjust-rx-filter.patch \
     file://0020-wifi-mt76-mt7915-add-additional-chain-signal-info-to.patch \
     file://0021-wifi-mt76-mt7915-remove-unnecessary-register-setting.patch \
+    file://0022-wifi-mt76-mt7915-add-PID-to-only-report-data-frame-T.patch \
     file://0999-wifi-mt76-mt7915-build-pass-for-Linux-Kernel-5.4-fix.patch \
     file://1000-wifi-mt76-mt7915-add-mtk-internal-debug-tools-for-mt.patch \
     file://1001-wifi-mt76-mt7915-csi-implement-csi-support.patch \
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp.bin
index 3203f47..ed3597b 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp_23.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp_23.bin
index 6bb8783..37511a9 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp_23.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp_23.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp_24.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp_24.bin
new file mode 100644
index 0000000..19aec4c
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_dsp_24.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch.bin
index 62b15ff..dfc722e 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch_23.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch_23.bin
index c6127eb..1fb990b 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch_23.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch_23.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch_24.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch_24.bin
new file mode 100644
index 0000000..6feaa4d
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_rom_patch_24.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa.bin
index 0ddfc82..3489c57 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa_23.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa_23.bin
index 54c9f8d..c15a207 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa_23.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa_23.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa_24.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa_24.bin
new file mode 100644
index 0000000..d6bedba
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wa_24.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm.bin
index ca62f15..542cd54 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_23.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_23.bin
index f645c9b..132beb0 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_23.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_23.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_24.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_24.bin
new file mode 100644
index 0000000..46b92ed
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_24.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm.bin
index 66cc70b..a6a4a71 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm_23.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm_23.bin
index 5643c9a..db10ce2 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm_23.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm_23.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm_24.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm_24.bin
new file mode 100644
index 0000000..91cd9ae
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7992_wm_tm_24.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_dsp.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_dsp.bin
index e9765d9..45fd63d 100755
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_dsp.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_dsp.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_eeprom_233_2i5i6i.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_eeprom_233_2i5i6i.bin
new file mode 100644
index 0000000..11eaa85
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_eeprom_233_2i5i6i.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch.bin
index 524024f..8538d92 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch_233.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch_233.bin
index 17a1475..f729c9a 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch_233.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch_233.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa.bin
index 3e04fe1..73a281b 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa_233.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa_233.bin
index bf1ff57..d8b85e0 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa_233.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa_233.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm.bin
index c26be71..8d5fd6e 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_233.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_233.bin
index c685756..95ab39f 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_233.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_233.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm.bin
index e275cbb..ce1e3f4 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm_233.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm_233.bin
index 04a5951..13684f1 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm_233.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm_233.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/linux-mt76_2.x.bb b/recipes-wifi/linux-mt76/linux-mt76_2.x.bb
index b61b5cf..b62aac1 100644
--- a/recipes-wifi/linux-mt76/linux-mt76_2.x.bb
+++ b/recipes-wifi/linux-mt76/linux-mt76_2.x.bb
@@ -42,7 +42,6 @@
     CONFIG_MAC80211_DEBUGFS=y \
     CONFIG_NL80211_TESTMODE=y \
     CONFIG_MT76_CONNAC_LIB=m \
-    CONFIG_MT7615_COMMON=m \
     CONFIG_MT7915E=m \
     CONFIG_MT798X_WMAC=y \
     "
@@ -72,11 +71,9 @@
 do_install() {
     # Module
     install -d ${D}/lib/modules/${KERNEL_VERSION}/updates/drivers/net/wireless/mediatek/mt76/
-    install -d ${D}/lib/modules/${KERNEL_VERSION}/updates/drivers/net/wireless/mediatek/mt76/mt7615/
     install -d ${D}/lib/modules/${KERNEL_VERSION}/updates/drivers/net/wireless/mediatek/mt76/mt7915/
     install -m 0644 ${B}/mt76.ko ${D}/lib/modules/${KERNEL_VERSION}/updates/drivers/net/wireless/mediatek/mt76/
     install -m 0644 ${B}/mt76-connac-lib.ko ${D}/lib/modules/${KERNEL_VERSION}/updates/drivers/net/wireless/mediatek/mt76/
-    install -m 0644 ${B}/mt7615/mt7615-common.ko ${D}/lib/modules/${KERNEL_VERSION}/updates/drivers/net/wireless/mediatek/mt76/mt7615/
     install -m 0644 ${B}/mt7915/mt7915e.ko ${D}/lib/modules/${KERNEL_VERSION}/updates/drivers/net/wireless/mediatek/mt76/mt7915/
 }
 
diff --git a/recipes-wifi/linux-mt76/linux-mt76_3.x.bb b/recipes-wifi/linux-mt76/linux-mt76_3.x.bb
index e34efdf..5cc2c71 100644
--- a/recipes-wifi/linux-mt76/linux-mt76_3.x.bb
+++ b/recipes-wifi/linux-mt76/linux-mt76_3.x.bb
@@ -25,7 +25,6 @@
 require files/patches-${PV}/patches.inc
 SRC_URI_append += " \
         file://5000-mt76-add-internal-wed_tiny-header-file.patch \
-        file://0118-fixup-wifi-mt76-mt7996-temp-support-for-single-wiphy.patch \
         "
 
 S = "${WORKDIR}/git"
diff --git a/recipes-wifi/linux-mt76/mt76-3x.inc b/recipes-wifi/linux-mt76/mt76-3x.inc
index f6dea8d..76d998a 100644
--- a/recipes-wifi/linux-mt76/mt76-3x.inc
+++ b/recipes-wifi/linux-mt76/mt76-3x.inc
@@ -1 +1 @@
-SRCREV_mt7988 = "513c131c6309712a51502870b041f45b4bd6a6d4"
+SRCREV_mt7988 = "6585a4353a51c0e4873b691bbcab251dffc7e2bb"
diff --git a/recipes-wifi/linux-mt76/mt76-test.bb b/recipes-wifi/linux-mt76/mt76-test.bb
index 35dd928..5a575a7 100644
--- a/recipes-wifi/linux-mt76/mt76-test.bb
+++ b/recipes-wifi/linux-mt76/mt76-test.bb
@@ -3,7 +3,7 @@
 LICENSE = "GPLv2"
 LIC_FILES_CHKSUM = "file://../COPYING;md5=c188eeeb69c0a05d0545816f1458a0c9"
 
-DEPENDS += "libnl-tiny"
+DEPENDS += "libnl-tiny linux-mac80211"
 
 inherit pkgconfig cmake
 
@@ -28,13 +28,12 @@
 FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
 
 
-CFLAGS_append = " -I=${includedir}/libnl-tiny "
+CFLAGS_append = " -I=${includedir}/libnl-tiny -I${STAGING_KERNEL_BUILDDIR}/usr/include/mac80211/uapi "
 
 S = "${WORKDIR}/git/tools"
 PATCH_SRC = "${@bb.utils.contains('DISTRO_FEATURES','wifi_eht','${WORKDIR}/patches-3.x','${WORKDIR}/patches',d)}"
 
 SRC_URI += "file://${@bb.utils.contains('DISTRO_FEATURES','wifi_eht','patches-3.x/','patches/',d)};apply=no"
-SRC_URI += "${@bb.utils.contains('DISTRO_FEATURES','wifi_eht','file://0118-fixup-wifi-mt76-mt7996-temp-support-for-single-wiphy.patch;subdir=${PATCH_SRC};apply=no','',d)}"
 
 do_mtk_patches() {
 	cd ${S}/../
diff --git a/recipes-wifi/linux-mt76/mt76.inc b/recipes-wifi/linux-mt76/mt76.inc
index 8f6a403..99ff551 100644
--- a/recipes-wifi/linux-mt76/mt76.inc
+++ b/recipes-wifi/linux-mt76/mt76.inc
@@ -1 +1 @@
-SRCREV ?= "513c131c6309712a51502870b041f45b4bd6a6d4"
+SRCREV ?= "3b47d9df427c4833605a172f2a8f0e0012b04c80"
diff --git a/recipes-wifi/wpa-supplicant/files/003-fix_wpa_supplicant_build_issue.patch b/recipes-wifi/wpa-supplicant/files/003-fix_wpa_supplicant_build_issue.patch
new file mode 100644
index 0000000..dc050a0
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/003-fix_wpa_supplicant_build_issue.patch
@@ -0,0 +1,73 @@
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index bd71083..9da8f8b 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1399,6 +1399,7 @@ int hostapd_drv_txpower_ctrl(struct hostapd_data *hapd)
+ {
+ 	s8 link_id = -1, sku_idx = hapd->iconf->sku_idx, ret = 0, i;
+ 	s8 **afc_power_table = NULL;
++	int lpi_mode = 0;
+ 
+ 	if (!hapd->driver || !hapd->driver->txpower_ctrl)
+ 		return 0;
+@@ -1407,6 +1408,7 @@ int hostapd_drv_txpower_ctrl(struct hostapd_data *hapd)
+ 		link_id = hapd->mld_link_id;
+ 
+ #ifdef CONFIG_AFC
++	lpi_mode = hapd->iface->afc.lpi_mode;
+ 	if (hapd->iface->current_mode->is_6ghz &&
+ 	    he_reg_is_sp(hapd->iface->conf->he_6ghz_reg_pwr_type) &&
+ 	    !hapd->iface->afc.lpi_mode) {
+@@ -1424,7 +1426,7 @@ int hostapd_drv_txpower_ctrl(struct hostapd_data *hapd)
+ 					 hapd->iconf->lpi_bcn_enhance,
+ 					 link_id,
+ 					 afc_power_table,
+-					 hapd->iface->afc.lpi_mode);
++					 lpi_mode);
+ #ifdef CONFIG_AFC
+ out:
+ 	if (afc_power_table)
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 0625bb7..643506f 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -869,7 +869,7 @@ static inline void hostap_afc_disable_channels(struct hostapd_iface *iface)
+ {
+ }
+ 
+-int hostapd_afc_translate_table(struct hostapd_iface *iface,
++static inline int hostapd_afc_translate_table(struct hostapd_iface *iface,
+ 				 s8 ***power_table)
+ {
+ 	return -EINVAL;
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index a08afb5..414d0df 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -15708,12 +15708,12 @@ static int nl80211_txpower_ctrl(void *priv, u8 lpi_psd, u8 sku_idx, u8 lpi_bcn_e
+ 
+ 	if (link_id > -1)
+ 		nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID, link_id);
+-
++#ifdef CONFIG_AFC
+ 	if (power_table && *power_table) {
+ 		nla_put(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE,
+ 		        MAX_CHANNEL_NUM_6G * afc_power_table_num, power_table);
+ 	}
+-
++#endif
+ 	nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI, lpi_mode);
+ 	nla_nest_end(msg, data);
+ 	ret = send_and_recv_cmd(drv, msg);
+diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
+index d2982cb..23a3e44 100644
+--- a/wpa_supplicant/Makefile
++++ b/wpa_supplicant/Makefile
+@@ -1082,6 +1082,7 @@ endif
+ ifdef CONFIG_IEEE80211BE
+ CONFIG_IEEE80211AX=y
+ CFLAGS += -DCONFIG_IEEE80211BE
++OBJS += ../src/ap/scs.o
+ endif
+ ifdef CONFIG_IEEE80211AX
+ CFLAGS += -DCONFIG_IEEE80211AX
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0001-ctrl_iface-create-link-based-hapd-control-sockets.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0001-ctrl_iface-create-link-based-hapd-control-sockets.patch
new file mode 100644
index 0000000..580179a
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0001-ctrl_iface-create-link-based-hapd-control-sockets.patch
@@ -0,0 +1,228 @@
+From 166ae43a8066cbc70d5d990cfd29cb6c4c2afc67 Mon Sep 17 00:00:00 2001
+From: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
+Date: Tue, 23 Apr 2024 11:01:23 +0530
+Subject: [PATCH 001/126] ctrl_iface: create link based hapd control sockets
+
+Create link based control sockets to access the link based commands
+through hostapd_cli. This will create the link interfaces in the name of
+wlan<X>_link<X>
+
+Example:
+To fetch link 0 status from wlan0, below command can be used -
+    $ hostapd_cli -i wlan0 -l 0 status
+
+On failure of link/interface selection, below error will be observed
+    $ hostapd_cli -i wlan0 -l 2 status
+    Failed to connect to hostapd - wpa_ctrl_open: No such file or directory
+
+Signed-off-by: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
+Co-developed-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ hostapd/ctrl_iface.c  | 16 ++++++++++++++--
+ hostapd/hostapd_cli.c | 30 ++++++++++++++++++++++++++++--
+ src/ap/hostapd.c      | 28 ++++++++++++++++++++++++++++
+ src/ap/hostapd.h      |  1 +
+ src/common/wpa_ctrl.h |  4 ++++
+ 5 files changed, 75 insertions(+), 4 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 39b9ef59d..3fa33be7a 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4687,18 +4687,26 @@ static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
+ {
+ 	char *buf;
+ 	size_t len;
++	char *ctrl_sock_iface;
++
++#ifdef CONFIG_IEEE80211BE
++	ctrl_sock_iface = hapd->ctrl_sock_iface;
++#else
++	ctrl_sock_iface = hapd->conf->iface;
++#endif /* CONFIG_IEEE80211BE */
+ 
+ 	if (hapd->conf->ctrl_interface == NULL)
+ 		return NULL;
+ 
+ 	len = os_strlen(hapd->conf->ctrl_interface) +
+-		os_strlen(hapd->conf->iface) + 2;
++		os_strlen(ctrl_sock_iface) + 2;
++
+ 	buf = os_malloc(len);
+ 	if (buf == NULL)
+ 		return NULL;
+ 
+ 	os_snprintf(buf, len, "%s/%s",
+-		    hapd->conf->ctrl_interface, hapd->conf->iface);
++		    hapd->conf->ctrl_interface, ctrl_sock_iface);
+ 	buf[len - 1] = '\0';
+ 	return buf;
+ }
+@@ -4869,7 +4877,11 @@ fail:
+ #endif /* ANDROID */
+ 
+ 	if (os_strlen(hapd->conf->ctrl_interface) + 1 +
++#ifdef CONFIG_IEEE80211BE
++	    os_strlen(hapd->ctrl_sock_iface) >= sizeof(addr.sun_path))
++#else
+ 	    os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path))
++#endif /* CONFIG_IEEE80211BE */
+ 		goto fail;
+ 
+ 	s = socket(PF_UNIX, SOCK_DGRAM, 0);
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index eb8a38350..f05a734fe 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -54,7 +54,11 @@ static void usage(void)
+ 	fprintf(stderr, "%s\n", hostapd_cli_version);
+ 	fprintf(stderr,
+ 		"\n"
++#ifdef CONFIG_IEEE80211BE
++		"usage: hostapd_cli [-p<path>] [-i<ifname>] [-l<link_id>] [-hvBr] "
++#else
+ 		"usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvBr] "
++#endif /* CONFIG_IEEE80211BE */
+ 		"[-a<path>] \\\n"
+ 		"                   [-P<pid file>] [-G<ping interval>] [command..]\n"
+ 		"\n"
+@@ -74,7 +78,12 @@ static void usage(void)
+ 		"   -B           run a daemon in the background\n"
+ 		"   -i<ifname>   Interface to listen on (default: first "
+ 		"interface found in the\n"
+-		"                socket path)\n\n");
++		"                socket path)\n"
++#ifdef CONFIG_IEEE80211BE
++		"   -l<link_id>  Link ID of the interface in case of Multi-Link\n"
++		"                Operation\n"
++#endif /* CONFIG_IEEE80211BE */
++		"\n");
+ 	print_help(stderr, NULL);
+ }
+ 
+@@ -2205,19 +2214,26 @@ static void hostapd_cli_action(struct wpa_ctrl *ctrl)
+ 	eloop_unregister_read_sock(fd);
+ }
+ 
+-
+ int main(int argc, char *argv[])
+ {
+ 	int warning_displayed = 0;
+ 	int c;
+ 	int daemonize = 0;
+ 	int reconnect = 0;
++#ifdef CONFIG_IEEE80211BE
++	int link_id = -1;
++	char buf[300];
++#endif /* CONFIG_IEEE80211BE */
+ 
+ 	if (os_program_init())
+ 		return -1;
+ 
+ 	for (;;) {
++#ifdef CONFIG_IEEE80211BE
++		c = getopt(argc, argv, "a:BhG:i:l:p:P:rs:v");
++#else
+ 		c = getopt(argc, argv, "a:BhG:i:p:P:rs:v");
++#endif /* CONFIG_IEEE80211BE */
+ 		if (c < 0)
+ 			break;
+ 		switch (c) {
+@@ -2252,6 +2268,16 @@ int main(int argc, char *argv[])
+ 		case 's':
+ 			client_socket_dir = optarg;
+ 			break;
++#ifdef CONFIG_IEEE80211BE
++		case 'l':
++			link_id = atoi(optarg);
++			os_memset(buf, '\0', sizeof(buf));
++			os_snprintf(buf, sizeof(buf), "%s_%s%d",
++				    ctrl_ifname, WPA_CTRL_IFACE_LINK_NAME, link_id);
++			os_free(ctrl_ifname);
++			ctrl_ifname = os_strdup(buf);
++			break;
++#endif /* CONFIG_IEEE80211BE */
+ 		default:
+ 			usage();
+ 			return -1;
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index a05de030d..c819c30cf 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -1808,12 +1808,37 @@ int hostapd_set_acl(struct hostapd_data *hapd)
+ }
+ 
+ 
++static void hostapd_set_ctrl_sock_iface(struct hostapd_data *hapd)
++{
++#ifdef CONFIG_IEEE80211BE
++	os_memset(hapd->ctrl_sock_iface, '\0',
++		  sizeof(hapd->ctrl_sock_iface));
++	os_strlcpy(hapd->ctrl_sock_iface, hapd->conf->iface,
++		   sizeof(hapd->ctrl_sock_iface));
++
++	if (hapd->conf->mld_ap) {
++		char buf[128];
++
++		os_memset(buf, '\0', sizeof(buf));
++		os_snprintf(buf, sizeof(buf), "%s_%s%d",
++			    hapd->conf->iface, WPA_CTRL_IFACE_LINK_NAME,
++			    hapd->mld_link_id);
++		os_memset(hapd->ctrl_sock_iface, '\0',
++			  sizeof(hapd->ctrl_sock_iface));
++		os_strlcpy(hapd->ctrl_sock_iface, buf, sizeof(buf));
++	}
++#endif /* CONFIG_IEEE80211BE */
++}
++
++
+ static int start_ctrl_iface_bss(struct hostapd_data *hapd)
+ {
+ 	if (!hapd->iface->interfaces ||
+ 	    !hapd->iface->interfaces->ctrl_iface_init)
+ 		return 0;
+ 
++	hostapd_set_ctrl_sock_iface(hapd);
++
+ 	if (hapd->iface->interfaces->ctrl_iface_init(hapd)) {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Failed to setup control interface for %s",
+@@ -1834,6 +1859,9 @@ static int start_ctrl_iface(struct hostapd_iface *iface)
+ 
+ 	for (i = 0; i < iface->num_bss; i++) {
+ 		struct hostapd_data *hapd = iface->bss[i];
++
++		hostapd_set_ctrl_sock_iface(hapd);
++
+ 		if (iface->interfaces->ctrl_iface_init(hapd)) {
+ 			wpa_printf(MSG_ERROR,
+ 				   "Failed to setup control interface for %s",
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index dcf395ca5..34a665562 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -476,6 +476,7 @@ struct hostapd_data {
+ 	struct hostapd_mld *mld;
+ 	struct dl_list link;
+ 	u8 mld_link_id;
++	char ctrl_sock_iface[IFNAMSIZ + 1];
+ #ifdef CONFIG_TESTING_OPTIONS
+ 	u8 eht_mld_link_removal_count;
+ #endif /* CONFIG_TESTING_OPTIONS */
+diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
+index f6142501e..865ac6d91 100644
+--- a/src/common/wpa_ctrl.h
++++ b/src/common/wpa_ctrl.h
+@@ -674,4 +674,8 @@ char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl);
+ }
+ #endif
+ 
++#ifdef CONFIG_IEEE80211BE
++#define WPA_CTRL_IFACE_LINK_NAME	"link"
++#endif /* CONFIG_IEEE80211BE */
++
+ #endif /* WPA_CTRL_H */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0001-hostapd-MLO-fix-for_each_mld_link-macro.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0001-hostapd-MLO-fix-for_each_mld_link-macro.patch
deleted file mode 100644
index c8e7f92..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0001-hostapd-MLO-fix-for_each_mld_link-macro.patch
+++ /dev/null
@@ -1,101 +0,0 @@
-From f5102b209870065c3e3719dd113892eafd4bb59e Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:31 +0530
-Subject: [PATCH 001/104] hostapd: MLO: fix for_each_mld_link macro
-
-Currently for_each_mld_link macro uses 3 nested for loops. Since now the
-affliated links are linked together via linked list, the logic can
-be improvised by using dl_list_for_each macro instead which uses one for
-loop.
-
-Modify for_each_mld_link macro to use dl_list_for_each instead.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/beacon.c   | 10 +---------
- src/ap/hostapd.h  | 17 +++--------------
- src/ap/sta_info.c |  4 +---
- 3 files changed, 5 insertions(+), 26 deletions(-)
-
-diff --git a/src/ap/beacon.c b/src/ap/beacon.c
-index 32865f667..195c7bbd9 100644
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -945,7 +945,6 @@ static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd,
- {
- 	struct probe_resp_params sta_info_params;
- 	struct hostapd_data *link;
--	unsigned int probed_mld_id, i, j;
- 
- 	params->mld_ap = NULL;
- 	params->mld_info = os_zalloc(sizeof(*params->mld_info));
-@@ -956,14 +955,7 @@ static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd,
- 		   "MLD: Got ML probe request with AP MLD ID %d for links %04x",
- 		   mld_id, links);
- 
--	/*
--	 * We want to include the AP MLD ID in the response if it was
--	 * included in the request.
--	 */
--	probed_mld_id = mld_id != -1 ? mld_id : hostapd_get_mld_id(hapd);
--
--	for_each_mld_link(link, i, j, hapd->iface->interfaces,
--			  probed_mld_id) {
-+	for_each_mld_link(link, hapd) {
- 		struct mld_link_info *link_info;
- 		size_t buflen;
- 		u8 mld_link_id = link->mld_link_id;
-diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
-index affe4f604..d12efb104 100644
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -817,19 +817,8 @@ struct hostapd_data * hostapd_mld_get_first_bss(struct hostapd_data *hapd);
- 
- bool hostapd_mld_is_first_bss(struct hostapd_data *hapd);
- 
--#define for_each_mld_link(_link, _bss_idx, _iface_idx, _ifaces, _mld_id) \
--	for (_iface_idx = 0;						\
--	     _iface_idx < (_ifaces)->count;				\
--	     _iface_idx++)						\
--		for (_bss_idx = 0;					\
--		     _bss_idx <						\
--			(_ifaces)->iface[_iface_idx]->num_bss;		\
--		     _bss_idx++)					\
--			for (_link =					\
--			     (_ifaces)->iface[_iface_idx]->bss[_bss_idx]; \
--			    _link && _link->conf->mld_ap &&		\
--				hostapd_get_mld_id(_link) == _mld_id;	\
--			    _link = NULL)
-+#define for_each_mld_link(partner, self) \
-+	dl_list_for_each(partner, &self->mld->links, struct hostapd_data, link)
- 
- #else /* CONFIG_IEEE80211BE */
- 
-@@ -838,7 +827,7 @@ static inline bool hostapd_mld_is_first_bss(struct hostapd_data *hapd)
- 	return true;
- }
- 
--#define for_each_mld_link(_link, _bss_idx, _iface_idx, _ifaces, _mld_id) \
-+#define for_each_mld_link(partner, self) \
- 	if (false)
- 
- #endif /* CONFIG_IEEE80211BE */
-diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
-index 122880a3d..2423ff189 100644
---- a/src/ap/sta_info.c
-+++ b/src/ap/sta_info.c
-@@ -1761,10 +1761,8 @@ static void ap_sta_remove_link_sta(struct hostapd_data *hapd,
- 				   struct sta_info *sta)
- {
- 	struct hostapd_data *tmp_hapd;
--	unsigned int i, j;
- 
--	for_each_mld_link(tmp_hapd, i, j, hapd->iface->interfaces,
--			  hostapd_get_mld_id(hapd)) {
-+	for_each_mld_link(tmp_hapd, hapd) {
- 		struct sta_info *tmp_sta;
- 
- 		if (hapd == tmp_hapd)
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0002-ctrl_iface-MLO-introduce-MLD-level-socket.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0002-ctrl_iface-MLO-introduce-MLD-level-socket.patch
new file mode 100644
index 0000000..448534e
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0002-ctrl_iface-MLO-introduce-MLD-level-socket.patch
@@ -0,0 +1,479 @@
+From 631a48c1a241bf9515d296eeea5d6060bef96cff Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Tue, 23 Apr 2024 11:01:24 +0530
+Subject: [PATCH 002/126] ctrl_iface: MLO: introduce MLD level socket
+
+With MLO, each link have socket created with "<ifname>_link<link id>" under
+the control interface directory.
+
+Introduce a MLD level socket - "<ifname>" as well under the same control
+interface directory. This socket can be used to pass the command to its
+partner links directly instead of using the link level socket. Link ID
+needs to be passed with the command.
+
+The structure of the command is -
+ "<COMMAND APPLICABALE FOR THE LINK> LINKID <link id>"
+
+Directory looks something like this -
+  $ ls /var/run/hostapd/
+    wlan0
+    wlan0_link0
+    wlan0_link1
+
+wlan0 here is the MLD level socket. Rest are each link level.
+
+This would also help to maintain backwards compatibility with applications
+which looks for <ifname> under the control interface directory.`
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ hostapd/ctrl_iface.c | 335 +++++++++++++++++++++++++++++++++++++++++++
+ hostapd/ctrl_iface.h |   4 +
+ hostapd/main.c       |   5 +
+ src/ap/hostapd.c     |  11 ++
+ src/ap/hostapd.h     |   6 +
+ 5 files changed, 361 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 3fa33be7a..5fe29147f 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4682,6 +4682,341 @@ done:
+ }
+ 
+ 
++#ifdef CONFIG_IEEE80211BE
++#ifndef CONFIG_CTRL_IFACE_UDP
++static int hostapd_mld_ctrl_iface_attach(struct hostapd_mld *mld,
++					 struct sockaddr_storage *from,
++					 socklen_t fromlen, const char *input)
++{
++	return ctrl_iface_attach(&mld->ctrl_dst, from, fromlen, input);
++}
++
++
++static int hostapd_mld_ctrl_iface_detach(struct hostapd_mld *mld,
++					 struct sockaddr_storage *from,
++					 socklen_t fromlen)
++{
++	return ctrl_iface_detach(&mld->ctrl_dst, from, fromlen);
++}
++
++
++static int hostapd_mld_ctrl_iface_receive_process(struct hostapd_mld *mld,
++						  char *buf, char *reply,
++						  int reply_size,
++						  struct sockaddr_storage *from,
++						  socklen_t fromlen)
++{
++	struct hostapd_data *link_hapd, *link_itr;
++	int reply_len, link_id = -1;
++	char *link_cmd;
++	bool found = false;
++
++	os_memcpy(reply, "OK\n", 3);
++	reply_len = 3;
++
++	/* Check if link id is provided in the command or not */
++	link_cmd = os_strstr(buf, "LINKID");
++	if (link_cmd) {
++		/* Trim the link id part now */
++		*(link_cmd - 1) = '\0';
++
++		link_cmd += 7;
++		link_id = atoi(link_cmd);
++
++		if (link_id < 0 || link_id >= 15) {
++			os_memcpy(reply, "INVALID LINK ID\n", 16);
++			reply_len = 16;
++			return reply_len;
++		}
++
++		link_hapd = mld->fbss;
++		if (!link_hapd) {
++			os_memcpy(reply, "NO LINKS ACTIVE\n", 16);
++			reply_len = 16;
++			return reply_len;
++		}
++
++		for_each_mld_link(link_itr, link_hapd) {
++			if (link_itr->mld_link_id == link_id) {
++				found = true;
++				break;
++			}
++		}
++
++		if (!found) {
++			os_memcpy(reply, "FAIL\n", 5);
++			reply_len = 5;
++			return reply_len;
++		}
++
++		link_hapd = link_itr;
++	} else {
++		link_hapd = mld->fbss;
++	}
++
++	if (os_strcmp(buf, "PING") == 0) {
++		os_memcpy(reply, "PONG\n", 5);
++		reply_len = 5;
++	} else if (os_strcmp(buf, "ATTACH") == 0) {
++		if (hostapd_mld_ctrl_iface_attach(mld, from, fromlen, NULL))
++			reply_len = -1;
++	} else if (os_strncmp(buf, "ATTACH ", 7) == 0) {
++		if (hostapd_mld_ctrl_iface_attach(mld, from, fromlen, buf + 7))
++			reply_len = -1;
++	} else if (os_strcmp(buf, "DETACH") == 0) {
++		if (hostapd_mld_ctrl_iface_detach(mld, from, fromlen))
++			reply_len = -1;
++	} else {
++		if (link_id == -1)
++			wpa_printf(MSG_DEBUG, "Link ID not provided, using first link BSS (if available)");
++
++		if (!link_hapd)
++			reply_len = -1;
++		else
++			reply_len =
++				hostapd_ctrl_iface_receive_process(link_hapd, buf,
++								   reply, reply_size,
++								   from, fromlen);
++	}
++
++	if (reply_len < 0) {
++		os_memcpy(reply, "FAIL\n", 5);
++		reply_len = 5;
++	}
++
++	return reply_len;
++}
++
++
++static void hostapd_mld_ctrl_iface_receive(int sock, void *eloop_ctx,
++					   void *sock_ctx)
++{
++	struct hostapd_mld *mld = eloop_ctx;
++	char buf[4096];
++	int res;
++	struct sockaddr_storage from;
++	socklen_t fromlen = sizeof(from);
++	char *reply, *pos = buf;
++	const int reply_size = 4096;
++	int reply_len;
++	int level = MSG_DEBUG;
++
++	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
++		       (struct sockaddr *) &from, &fromlen);
++	if (res < 0) {
++		wpa_printf(MSG_ERROR, "recvfrom(mld ctrl_iface): %s",
++			   strerror(errno));
++		return;
++	}
++	buf[res] = '\0';
++
++	reply = os_malloc(reply_size);
++	if (reply == NULL) {
++		if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
++			   fromlen) < 0) {
++			wpa_printf(MSG_DEBUG, "MLD CTRL: sendto failed: %s",
++				   strerror(errno));
++		}
++		return;
++	}
++
++	if (os_strcmp(pos, "PING") == 0)
++		level = MSG_EXCESSIVE;
++
++	wpa_hexdump_ascii(level, "RX MLD ctrl_iface", pos, res);
++
++	reply_len = hostapd_mld_ctrl_iface_receive_process(mld, pos,
++							   reply, reply_size,
++							   &from, fromlen);
++
++	if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
++		   fromlen) < 0) {
++		wpa_printf(MSG_DEBUG, "MLD CTRL: sendto failed: %s",
++			   strerror(errno));
++	}
++	os_free(reply);
++}
++
++
++static char * hostapd_mld_ctrl_iface_path(struct hostapd_mld *mld)
++{
++	char *buf;
++	size_t len;
++
++	if (!mld->ctrl_interface)
++		return NULL;
++
++	len = os_strlen(mld->ctrl_interface) + os_strlen(mld->name) + 2;
++
++	buf = os_malloc(len);
++	if (buf == NULL)
++		return NULL;
++
++	os_snprintf(buf, len, "%s/%s", mld->ctrl_interface, mld->name);
++	buf[len - 1] = '\0';
++	return buf;
++}
++#endif /* !CONFIG_CTRL_IFACE_UDP */
++
++
++int hostapd_mld_ctrl_iface_init(struct hostapd_mld *mld)
++{
++#ifndef CONFIG_CTRL_IFACE_UDP
++	struct sockaddr_un addr;
++	int s = -1;
++	char *fname = NULL;
++
++	if (!mld)
++		return -1;
++
++	if (mld->ctrl_sock > -1) {
++		wpa_printf(MSG_DEBUG, "MLD %s ctrl_iface already exists!",
++			   mld->name);
++		return 0;
++	}
++
++	dl_list_init(&mld->ctrl_dst);
++
++	if (mld->ctrl_interface == NULL)
++		return 0;
++
++	if (mkdir(mld->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
++		if (errno == EEXIST) {
++			wpa_printf(MSG_DEBUG, "Using existing control "
++				   "interface directory.");
++		} else {
++			wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s",
++				   strerror(errno));
++			goto fail;
++		}
++	}
++
++	if (os_strlen(mld->ctrl_interface) + 1 +
++	    os_strlen(mld->name) >= sizeof(addr.sun_path))
++		goto fail;
++
++	s = socket(PF_UNIX, SOCK_DGRAM, 0);
++	if (s < 0) {
++		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
++		goto fail;
++	}
++
++	os_memset(&addr, 0, sizeof(addr));
++#ifdef __FreeBSD__
++	addr.sun_len = sizeof(addr);
++#endif /* __FreeBSD__ */
++	addr.sun_family = AF_UNIX;
++
++	fname = hostapd_mld_ctrl_iface_path(mld);
++	if (fname == NULL)
++		goto fail;
++
++	os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
++
++	wpa_printf(MSG_DEBUG, "Setting up MLD %s ctrl_iface", mld->name);
++
++	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
++		wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
++			   strerror(errno));
++		if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
++			wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
++				   " allow connections - assuming it was left"
++				   "over from forced program termination");
++			if (unlink(fname) < 0) {
++				wpa_printf(MSG_ERROR,
++					   "Could not unlink existing ctrl_iface socket '%s': %s",
++					   fname, strerror(errno));
++				goto fail;
++			}
++			if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
++			    0) {
++				wpa_printf(MSG_ERROR,
++					   "hostapd-ctrl-iface: bind(PF_UNIX): %s",
++					   strerror(errno));
++				goto fail;
++			}
++			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
++				   "ctrl_iface socket '%s'", fname);
++		} else {
++			wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
++				   "be in use - cannot override it");
++			wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
++				   "not used anymore", fname);
++			os_free(fname);
++			fname = NULL;
++			goto fail;
++		}
++	}
++
++	if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
++		wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s",
++			   strerror(errno));
++		goto fail;
++	}
++	os_free(fname);
++
++	mld->ctrl_sock = s;
++
++	if (eloop_register_read_sock(s, hostapd_mld_ctrl_iface_receive, mld,
++				     NULL) < 0)
++		return -1;
++
++	return 0;
++
++fail:
++	if (s >= 0)
++		close(s);
++	if (fname) {
++		unlink(fname);
++		os_free(fname);
++	}
++	return -1;
++#endif /* !CONFIG_CTRL_IFACE_UDP */
++	return 0;
++}
++
++
++void hostapd_mld_ctrl_iface_deinit(struct hostapd_mld *mld)
++{
++#ifndef CONFIG_CTRL_IFACE_UDP
++	struct wpa_ctrl_dst *dst, *prev;
++
++	if (mld->ctrl_sock > -1) {
++		char *fname;
++		eloop_unregister_read_sock(mld->ctrl_sock);
++		close(mld->ctrl_sock);
++		mld->ctrl_sock = -1;
++
++		fname = hostapd_mld_ctrl_iface_path(mld);
++		if (fname)
++			unlink(fname);
++		os_free(fname);
++
++		if (mld->ctrl_interface &&
++		    rmdir(mld->ctrl_interface) < 0) {
++			if (errno == ENOTEMPTY) {
++				wpa_printf(MSG_DEBUG, "MLD Control interface "
++					   "directory not empty - leaving it "
++					   "behind");
++			} else {
++				wpa_printf(MSG_ERROR,
++					   "rmdir[ctrl_interface=%s]: %s",
++					   mld->ctrl_interface,
++					   strerror(errno));
++			}
++		}
++	}
++
++	dl_list_for_each_safe(dst, prev, &mld->ctrl_dst, struct wpa_ctrl_dst,
++			      list)
++		os_free(dst);
++#endif /* !CONFIG_CTRL_IFACE_UDP */
++
++	os_free(mld->ctrl_interface);
++}
++#endif /* CONFIG_IEEE80211BE */
++
++
+ #ifndef CONFIG_CTRL_IFACE_UDP
+ static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
+ {
+diff --git a/hostapd/ctrl_iface.h b/hostapd/ctrl_iface.h
+index 3341a66bd..ec5a95be7 100644
+--- a/hostapd/ctrl_iface.h
++++ b/hostapd/ctrl_iface.h
+@@ -14,6 +14,10 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd);
+ void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd);
+ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface);
+ void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface);
++#ifdef CONFIG_IEEE80211BE
++int hostapd_mld_ctrl_iface_init(struct hostapd_mld *mld);
++void hostapd_mld_ctrl_iface_deinit(struct hostapd_mld *mld);
++#endif /* CONFIG_IEEE80211BE */
+ #else /* CONFIG_NO_CTRL_IFACE */
+ static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
+ {
+diff --git a/hostapd/main.c b/hostapd/main.c
+index 00e02bb03..aa1f69812 100644
+--- a/hostapd/main.c
++++ b/hostapd/main.c
+@@ -748,6 +748,7 @@ static void hostapd_global_cleanup_mld(struct hapd_interfaces *interfaces)
+ 		if (!interfaces->mld[i])
+ 			continue;
+ 
++		interfaces->mld_ctrl_iface_deinit(interfaces->mld[i]);
+ 		os_free(interfaces->mld[i]);
+ 		interfaces->mld[i] = NULL;
+ 	}
+@@ -793,6 +794,10 @@ int main(int argc, char *argv[])
+ 	interfaces.global_iface_path = NULL;
+ 	interfaces.global_iface_name = NULL;
+ 	interfaces.global_ctrl_sock = -1;
++#ifdef CONFIG_IEEE80211BE
++	interfaces.mld_ctrl_iface_init = hostapd_mld_ctrl_iface_init;
++	interfaces.mld_ctrl_iface_deinit = hostapd_mld_ctrl_iface_deinit;
++#endif /* CONFIG_IEEE80211BE */
+ 	dl_list_init(&interfaces.global_ctrl_dst);
+ #ifdef CONFIG_ETH_P_OUI
+ 	dl_list_init(&interfaces.eth_p_oui);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index c819c30cf..36d48ae09 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -3093,9 +3093,18 @@ static void hostapd_bss_setup_multi_link(struct hostapd_data *hapd,
+ 
+ 	os_strlcpy(mld->name, conf->iface, sizeof(conf->iface));
+ 	dl_list_init(&mld->links);
++	mld->ctrl_sock = -1;
++	mld->ctrl_interface = os_strdup(hapd->conf->ctrl_interface);
+ 
+ 	wpa_printf(MSG_DEBUG, "AP MLD %s created", mld->name);
+ 
++	/*
++	 * Initialize MLD control interfaces early to allow external monitoring of
++	 * link setup operations.
++	 */
++	if (interfaces->mld_ctrl_iface_init(mld))
++		goto fail;
++
+ 	hapd->mld = mld;
+ 	hostapd_mld_ref_inc(mld);
+ 	hostapd_bss_alloc_link_id(hapd);
+@@ -3155,6 +3164,8 @@ static void hostapd_cleanup_unused_mlds(struct hapd_interfaces *interfaces)
+ 		if (!remove && !forced_remove)
+ 			continue;
+ 
++		interfaces->mld_ctrl_iface_deinit(mld);
++
+ 		wpa_printf(MSG_DEBUG, "AP MLD %s: Freed%s", mld->name,
+ 			   forced_remove ? " (forced)" : "");
+ 		os_free(mld);
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 34a665562..2ef63e5f2 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -97,6 +97,8 @@ struct hapd_interfaces {
+ #ifdef CONFIG_IEEE80211BE
+ 	struct hostapd_mld **mld;
+ 	size_t mld_count;
++	int (*mld_ctrl_iface_init)(struct hostapd_mld *mld);
++	void (*mld_ctrl_iface_deinit)(struct hostapd_mld *mld);
+ #endif /* CONFIG_IEEE80211BE */
+ };
+ 
+@@ -519,6 +521,10 @@ struct hostapd_mld {
+ 
+ 	struct hostapd_data *fbss;
+ 	struct dl_list links; /* List head of all affiliated links */
++
++	int ctrl_sock;
++	struct dl_list ctrl_dst;
++	char *ctrl_interface; /* directory for UNIX domain sockets */
+ };
+ 
+ #define HOSTAPD_MLD_MAX_REF_COUNT      0xFF
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0002-hostapd-MLO-frame-link-add-command-on-per-BSS-basis.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0002-hostapd-MLO-frame-link-add-command-on-per-BSS-basis.patch
deleted file mode 100644
index 86c2263..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0002-hostapd-MLO-frame-link-add-command-on-per-BSS-basis.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From 3ca32441ecd9d1a52f736d4a4fffdc24de629e90 Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:32 +0530
-Subject: [PATCH 002/104] hostapd: MLO: frame link add command on per BSS basis
-
-Currently function nl80211_link_add() creates the link add NL message on
-drv basis which in turn uses drv's first BSS always. In order to support
-link add for various other interfaces, use BSS handler to create the NL
-message.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/drivers/driver_nl80211.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 4949de577..042bc97a8 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13876,7 +13876,7 @@ static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr,
- 		}
- 	}
- 
--	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ADD_LINK);
-+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_ADD_LINK);
- 	if (!msg ||
- 	    nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
- 	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0003-hostapd_cli-MLO-pass-LINKID-in-the-command.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0003-hostapd_cli-MLO-pass-LINKID-in-the-command.patch
new file mode 100644
index 0000000..9b45d05
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0003-hostapd_cli-MLO-pass-LINKID-in-the-command.patch
@@ -0,0 +1,194 @@
+From 702fc9f42fc30acd7f956994f887d02eef3c3ade Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Tue, 23 Apr 2024 11:01:25 +0530
+Subject: [PATCH 003/126] hostapd_cli: MLO: pass 'LINKID' in the command
+
+MLD level socket can take 'LINKID <link id>'. Add changes to pass this via
+hostapd_cli. User needs to give "link_id=<link_id>" in post fix fashion in
+the command in order to pass this link_id from cli.
+
+For example -
+$ hostapd_cli -i wlan0 status link_id=0 | grep freq=
+freq=2437
+
+$ hostapd_cli -i wlan0
+...
+Interactive mode
+
+> ping
+PONG
+>
+> status link_id=0
+Command for 'LINKID 0'
+state=ENABLED
+phy=phy0
+freq=2437
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ hostapd/hostapd_cli.c | 39 +++++++++++++++++++++++++++++++
+ src/common/wpa_ctrl.c | 54 +++++++++++++++++++++++++++++++++++++++++++
+ src/common/wpa_ctrl.h |  3 +++
+ 3 files changed, 96 insertions(+)
+
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index f05a734fe..d69525502 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1962,6 +1962,45 @@ static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ 	} else if (count == 0) {
+ 		printf("Unknown command '%s'\n", argv[0]);
+ 	} else {
++#ifdef CONFIG_IEEE80211BE
++		char *pos, *end;
++		int i, j, link_id;
++		bool link_found = false;
++
++		wpa_ctrl_reset_mld_link(ctrl);
++		i = 0;
++
++		while (i < argc) {
++			pos = os_strstr(argv[i], "link_id=");
++			if (!pos) {
++				i++;
++				continue;
++			}
++
++			pos = pos + 8;
++			link_id = strtol(pos, &end, 10);
++
++			if (link_id < 0 || link_id >= 15) {
++				printf("Invalid link ID '%d'\n", link_id);
++				return;
++			}
++
++			link_found = true;
++
++			/* remove this link_id= from the arguements */
++			for (j = i + 1; j < argc; j++)
++				argv[j - 1] = argv[j];
++
++			argc--;
++			i = 0;
++		}
++
++		if (link_found) {
++			wpa_ctrl_set_mld_link(ctrl, link_id);
++			printf("Command for '%s'\n",
++			       wpa_ctrl_get_mld_link(ctrl));
++		}
++#endif /* CONFIG_IEEE80211BE */
+ 		match->handler(ctrl, argc - 1, &argv[1]);
+ 	}
+ }
+diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c
+index 7e197f094..d0c174c05 100644
+--- a/src/common/wpa_ctrl.c
++++ b/src/common/wpa_ctrl.c
+@@ -72,6 +72,13 @@ struct wpa_ctrl {
+ #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+ 	HANDLE pipe;
+ #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
++#ifdef CONFIG_IEEE80211BE
++	/* 'LINKID ' - 7 chars including space
++	 * 'XX' - Two chars max for link id
++	 * Total required 10 chars at least
++	 */
++	char link_id_str[10];
++#endif /* CONFIG_IEEE80211BE */
+ };
+ 
+ 
+@@ -488,6 +495,7 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
+ 	fd_set rfds;
+ 	const char *_cmd;
+ 	char *cmd_buf = NULL;
++	char *link_cmd_buf = NULL;
+ 	size_t _cmd_len;
+ 
+ #ifdef CONFIG_CTRL_IFACE_UDP
+@@ -510,6 +518,28 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
+ 		_cmd_len = cmd_len;
+ 	}
+ 
++#ifdef CONFIG_IEEE80211BE
++	if (os_strlen(ctrl->link_id_str)) {
++		char *pos;
++
++		_cmd_len = _cmd_len + 1 + os_strlen(ctrl->link_id_str);
++		link_cmd_buf = os_malloc(_cmd_len);
++		if (link_cmd_buf == NULL) {
++			if (cmd_buf)
++				os_free(cmd_buf);
++			return -1;
++		}
++
++		pos = link_cmd_buf;
++		os_strlcpy(pos, _cmd, _cmd_len);
++		pos += os_strlen(_cmd);
++		*pos++ = ' ';
++		os_memcpy(pos, ctrl->link_id_str, os_strlen(ctrl->link_id_str));
++		_cmd = link_cmd_buf;
++		wpa_ctrl_reset_mld_link(ctrl);
++	}
++#endif /* CONFIG_IEEE80211BE */
++
+ 	errno = 0;
+ 	started_at.sec = 0;
+ 	started_at.usec = 0;
+@@ -535,9 +565,11 @@ retry_send:
+ 		}
+ 	send_err:
+ 		os_free(cmd_buf);
++		os_free(link_cmd_buf);
+ 		return -1;
+ 	}
+ 	os_free(cmd_buf);
++	os_free(link_cmd_buf);
+ 
+ 	os_get_reltime(&ending_at);
+ 	ending_at.sec += 10;
+@@ -773,4 +805,26 @@ int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl)
+ 
+ #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+ 
++
++#ifdef CONFIG_IEEE80211BE
++void wpa_ctrl_reset_mld_link(struct wpa_ctrl *ctrl)
++{
++	os_memset(ctrl->link_id_str, '\0', sizeof(ctrl->link_id_str));
++}
++
++
++void wpa_ctrl_set_mld_link(struct wpa_ctrl *ctrl, int link_id)
++{
++	os_snprintf(ctrl->link_id_str, sizeof(ctrl->link_id_str),
++		    "LINKID %d", link_id);
++}
++
++
++char *wpa_ctrl_get_mld_link(struct wpa_ctrl *ctrl)
++{
++	return ctrl->link_id_str;
++}
++#endif /* CONFIG_IEEE80211BE */
++
++
+ #endif /* CONFIG_CTRL_IFACE */
+diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
+index 865ac6d91..d1ce1dd29 100644
+--- a/src/common/wpa_ctrl.h
++++ b/src/common/wpa_ctrl.h
+@@ -676,6 +676,9 @@ char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl);
+ 
+ #ifdef CONFIG_IEEE80211BE
+ #define WPA_CTRL_IFACE_LINK_NAME	"link"
++void wpa_ctrl_reset_mld_link(struct wpa_ctrl *ctrl);
++void wpa_ctrl_set_mld_link(struct wpa_ctrl *ctrl, int link_id);
++char *wpa_ctrl_get_mld_link(struct wpa_ctrl *ctrl);
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ #endif /* WPA_CTRL_H */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0003-nl80211-Print-the-interface-name-in-debug-during-lin.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0003-nl80211-Print-the-interface-name-in-debug-during-lin.patch
deleted file mode 100644
index 8dfcc5c..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0003-nl80211-Print-the-interface-name-in-debug-during-lin.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From 695a2dbff28bb259c2b4f8bfdfb9040f81ab7e90 Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:33 +0530
-Subject: [PATCH 003/104] nl80211: Print the interface name in debug during
- link add
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/drivers/driver_nl80211.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 042bc97a8..98948bfb1 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13900,8 +13900,8 @@ static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr,
- 	bss->valid_links |= BIT(link_id);
- 	bss->links[link_id].ctx = bss_ctx;
- 
--	wpa_printf(MSG_DEBUG, "nl80211: MLD: valid_links=0x%04x",
--		   bss->valid_links);
-+	wpa_printf(MSG_DEBUG, "nl80211: MLD: valid_links=0x%04x on %s",
-+		   bss->valid_links, bss->ifname);
- 	return 0;
- }
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0004-hostapd-MLO-send-link_id-on-sta_deauth.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0004-hostapd-MLO-send-link_id-on-sta_deauth.patch
deleted file mode 100644
index 5f56b72..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0004-hostapd-MLO-send-link_id-on-sta_deauth.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From 49a31ee63f482c930e001e2b6a13bf9261fcf5de Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:34 +0530
-Subject: [PATCH 004/104] hostapd: MLO: send link_id on sta_deauth()
-
-Function i802_sta_deauth() already has the link_id passed to it in its
-arguments. Use that to pass it down to send mlme handler.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/drivers/driver_nl80211.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 98948bfb1..e5fa22b59 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -8254,7 +8254,7 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
- 	return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
- 					    IEEE80211_HDRLEN +
- 					    sizeof(mgmt.u.deauth), 0, 0, 0, 0,
--					    0, NULL, 0, 0, -1);
-+					    0, NULL, 0, 0, link_id);
- }
- 
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0004-hostapd_cli-MLO-add-status-command-for-MLD-socket.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0004-hostapd_cli-MLO-add-status-command-for-MLD-socket.patch
new file mode 100644
index 0000000..08a7a11
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0004-hostapd_cli-MLO-add-status-command-for-MLD-socket.patch
@@ -0,0 +1,99 @@
+From 8e2802b52f5469e12dcb0bc38929f2721a2f0a83 Mon Sep 17 00:00:00 2001
+From: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Date: Tue, 23 Apr 2024 11:01:26 +0530
+Subject: [PATCH 004/126] hostapd_cli: MLO: add status command for MLD socket
+
+Add MLD level 'status' command. Currently each link level socket has got
+'status' command. When the same is passed on MLD level socket without any
+link id, it routes it to first BSS of the MLD if available. Handle this
+now properly.
+
+If link id is not passed then it will be treated as MLD level status
+command.
+
+$ hostapd_cli -i wlan0
+....
+Interactive mode
+
+> status
+name=wlan0
+mld_address=AA:BB:CC:DD:EE:FF
+num_links=2
+LINK INFORMATION
+link_id=0
+link_addr=AA:BB:CC:DD:EE:EE
+link_id=1
+link_addr=AA:BB:CC:DD:FF:FF
+
+Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+---
+ hostapd/ctrl_iface.c | 46 ++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 46 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 5fe29147f..a584d370e 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4700,6 +4700,49 @@ static int hostapd_mld_ctrl_iface_detach(struct hostapd_mld *mld,
+ }
+ 
+ 
++int hostapd_ctrl_mld_iface_status(struct hostapd_mld *mld, char *buf,
++				  size_t buflen)
++{
++	struct hostapd_data *link_hapd;
++	int len = 0, ret;
++
++	ret = os_snprintf(buf + len, buflen - len,
++			  "name=%s\n"
++			  "mld_address=" MACSTR "\n"
++			  "num_links=%d\n",
++			  mld->name, MAC2STR(mld->mld_addr), mld->num_links);
++	if (os_snprintf_error(buflen - len, ret))
++		return len;
++	len += ret;
++
++	if (!mld->fbss) {
++		ret = os_snprintf(buf + len, buflen - len,
++				  "\n No Link information present\n");
++		if (os_snprintf_error(buflen - len, ret))
++			return len;
++		len += ret;
++	}
++
++	ret = os_snprintf(buf + len, buflen - len,
++			 "LINK INFORMATION\n");
++	if (os_snprintf_error(buflen - len, ret))
++		return len;
++	len += ret;
++
++	dl_list_for_each(link_hapd, &mld->links, struct hostapd_data, link) {
++		ret = os_snprintf(buf + len, buflen - len,
++				 "link_id=%d\n"
++				 "link_addr=" MACSTR "\n",
++				 link_hapd->mld_link_id, MAC2STR(link_hapd->own_addr));
++		if (os_snprintf_error(buflen - len, ret))
++			return len;
++		len += ret;
++	}
++
++	return len;
++}
++
++
+ static int hostapd_mld_ctrl_iface_receive_process(struct hostapd_mld *mld,
+ 						  char *buf, char *reply,
+ 						  int reply_size,
+@@ -4766,6 +4809,9 @@ static int hostapd_mld_ctrl_iface_receive_process(struct hostapd_mld *mld,
+ 	} else if (os_strcmp(buf, "DETACH") == 0) {
+ 		if (hostapd_mld_ctrl_iface_detach(mld, from, fromlen))
+ 			reply_len = -1;
++	} else if (os_strcmp(buf, "STATUS") == 0 && link_id == -1){
++		reply_len = hostapd_ctrl_mld_iface_status(mld, reply,
++							  reply_size);
+ 	} else {
+ 		if (link_id == -1)
+ 			wpa_printf(MSG_DEBUG, "Link ID not provided, using first link BSS (if available)");
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0005-hostapd-MLO-handle-auth-assoc-on-link-address.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0005-hostapd-MLO-handle-auth-assoc-on-link-address.patch
deleted file mode 100644
index e8adbc0..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0005-hostapd-MLO-handle-auth-assoc-on-link-address.patch
+++ /dev/null
@@ -1,77 +0,0 @@
-From ec1fdb73853632e9a9003f8c59620d1e12f6d2d0 Mon Sep 17 00:00:00 2001
-From: Sriram R <quic_srirrama@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:35 +0530
-Subject: [PATCH 005/104] hostapd: MLO: handle auth/assoc on link address
-
-Modify authentication and association frames to be always sent with link
-address as A1 and A3 for ease of Tx status handling.
-
-Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/ieee802_11.c | 25 ++-----------------------
- 1 file changed, 2 insertions(+), 23 deletions(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 5a3132de4..b20300bab 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -416,14 +416,7 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
- 	struct wpabuf *ml_resp = NULL;
- 
- #ifdef CONFIG_IEEE80211BE
--	/*
--	 * Once a non-AP MLD is added to the driver, the addressing should use
--	 * the MLD MAC address. Thus, use the MLD address instead of translating
--	 * the addresses.
--	 */
- 	if (ap_sta_is_mld(hapd, sta)) {
--		sa = hapd->mld->mld_addr;
--
- 		ml_resp = hostapd_ml_auth_resp(hapd);
- 		if (!ml_resp)
- 			return -1;
-@@ -444,7 +437,7 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
- 					    WLAN_FC_STYPE_AUTH);
- 	os_memcpy(reply->da, dst, ETH_ALEN);
- 	os_memcpy(reply->sa, sa, ETH_ALEN);
--	os_memcpy(reply->bssid, bssid, ETH_ALEN);
-+	os_memcpy(reply->bssid, sa, ETH_ALEN);
- 
- 	reply->u.auth.auth_alg = host_to_le16(auth_alg);
- 	reply->u.auth.auth_transaction = host_to_le16(auth_transaction);
-@@ -3265,14 +3258,9 @@ static void handle_auth(struct hostapd_data *hapd,
- 	bssid = mgmt->bssid;
- 
- #ifdef CONFIG_IEEE80211BE
--	 /*
--	  * Once a non-AP MLD is added to the driver, the addressing should use
--	  * the MLD MAC address. It is the responsibility of the driver to
--	  * handle the translations.
--	  */
- 	if (ap_sta_is_mld(hapd, sta)) {
- 		dst = sta->addr;
--		bssid = hapd->mld->mld_addr;
-+		bssid = hapd->own_addr;
- 	}
- #endif /* CONFIG_IEEE80211BE */
- 
-@@ -4823,15 +4811,6 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
- 			     (reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
- 			      WLAN_FC_STYPE_ASSOC_RESP));
- 
--#ifdef CONFIG_IEEE80211BE
--	/*
--	 * Once a non-AP MLD is added to the driver, the addressing should use
--	 * MLD MAC address.
--	 */
--	if (ap_sta_is_mld(hapd, sta) && allow_mld_addr_trans)
--		sa = hapd->mld->mld_addr;
--#endif /* CONFIG_IEEE80211BE */
--
- 	os_memcpy(reply->da, addr, ETH_ALEN);
- 	os_memcpy(reply->sa, sa, ETH_ALEN);
- 	os_memcpy(reply->bssid, sa, ETH_ALEN);
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0005-hostapd-afcd-add-AFC-daemon-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0005-hostapd-afcd-add-AFC-daemon-support.patch
new file mode 100644
index 0000000..2955527
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0005-hostapd-afcd-add-AFC-daemon-support.patch
@@ -0,0 +1,615 @@
+From f25cbdd28ff46a7beedd07f575614da7c7d269d7 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 17 May 2024 11:50:25 +0200
+Subject: [PATCH 005/126] hostapd: afcd: add AFC daemon support
+
+Introduce Automated Frequency Coordination Daemon (AFCD) support
+for UNII-5 and UNII-7 6GHz bands.
+AFCD will be used by hostapd AFC client in order to forward the AFC
+request to the AFC coordinator and decouple AFC connection management
+from hostapd.
+AFC is required for Standard Power Devices (SPDs) to determine a lists
+of channels and EIRP/PSD powers that are available in the 6GHz spectrum.
+AFCD is tested with AFC DUT Test Harness [0].
+Add afc-reply.json as reference for replies from the AFC coordinator.
+
+[0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main
+
+Tested-by: Felix Fietkau <nbd@nbd.name>
+Tested-by: Allen Ye <allen.ye@mediatek.com>
+Tested-by: Krishna Chaitanya <chaitanya.mgit@gmail.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ afc/.gitignore    |   1 +
+ afc/Makefile      |  31 +++++
+ afc/afc-reply.txt | 219 +++++++++++++++++++++++++++++++++
+ afc/afcd.c        | 305 ++++++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 556 insertions(+)
+ create mode 100644 afc/.gitignore
+ create mode 100644 afc/Makefile
+ create mode 100644 afc/afc-reply.txt
+ create mode 100644 afc/afcd.c
+
+diff --git a/afc/.gitignore b/afc/.gitignore
+new file mode 100644
+index 000000000..8d8cca905
+--- /dev/null
++++ b/afc/.gitignore
+@@ -0,0 +1 @@
++afcd
+diff --git a/afc/Makefile b/afc/Makefile
+new file mode 100644
+index 000000000..a83bd01db
+--- /dev/null
++++ b/afc/Makefile
+@@ -0,0 +1,31 @@
++ALL=afcd
++
++include ../src/build.rules
++
++CFLAGS += -I../src/utils
++CFLAGS += -I../src
++
++OBJS=afcd.o
++OBJS += ../src/utils/common.o
++OBJS += ../src/utils/wpa_debug.o
++OBJS += ../src/utils/wpabuf.o
++
++ifndef CONFIG_OS
++ifdef CONFIG_NATIVE_WINDOWS
++CONFIG_OS=win32
++else
++CONFIG_OS=unix
++endif
++endif
++OBJS += ../src/utils/os_$(CONFIG_OS).o
++
++LIBS += -lcurl
++
++_OBJS_VAR := OBJS
++include ../src/objs.mk
++afcd: $(OBJS)
++	$(Q)$(LDO) $(LDFLAGS) -o afcd $(OBJS) $(LIBS)
++	@$(E) "  LD " $@
++
++clean: common-clean
++	rm -f core *~
+diff --git a/afc/afc-reply.txt b/afc/afc-reply.txt
+new file mode 100644
+index 000000000..aaa4f8956
+--- /dev/null
++++ b/afc/afc-reply.txt
+@@ -0,0 +1,219 @@
++HTTP/1.1 200 OK
++Content-Type: application/json
++Content-Length: 4843
++
++{
++   "availableSpectrumInquiryResponses":[
++      {
++         "availabilityExpireTime":"2023-02-23T12:53:18Z",
++         "availableChannelInfo":[
++            {
++               "channelCfi":[
++                  1,
++                  5,
++                  9,
++                  13,
++                  17,
++                  21,
++                  25,
++                  29,
++                  33,
++                  37,
++                  41,
++                  45,
++                  49,
++                  53,
++                  57,
++                  61,
++                  65,
++                  69,
++                  73,
++                  77,
++                  81,
++                  85,
++                  89,
++                  93,
++                  117,
++                  121,
++                  125,
++                  129,
++                  133,
++                  137,
++                  141,
++                  145,
++                  149,
++                  153,
++                  157,
++                  161,
++                  165,
++                  169,
++                  173,
++                  177,
++                  181
++               ],
++               "globalOperatingClass":131,
++               "maxEirp":[
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5
++               ]
++            },
++            {
++               "channelCfi":[
++                  3,
++                  11,
++                  19,
++                  27,
++                  35,
++                  43,
++                  51,
++                  59,
++                  67,
++                  75,
++                  83,
++                  91,
++                  123,
++                  131,
++                  139,
++                  147,
++                  155,
++                  163,
++                  171,
++                  179
++               ],
++               "globalOperatingClass":132,
++               "maxEirp":[
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5
++               ]
++            },
++            {
++               "channelCfi":[
++                  7,
++                  23,
++                  39,
++                  55,
++                  71,
++                  87,
++                  135,
++                  151,
++                  167
++               ],
++               "globalOperatingClass":133,
++               "maxEirp":[
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5,
++                  5
++               ]
++            },
++            {
++               "channelCfi":[
++                  15,
++                  47,
++                  79,
++                  143
++               ],
++               "globalOperatingClass":134,
++               "maxEirp":[
++                  5,
++                  5,
++                  5,
++                  5
++               ]
++            },
++            {
++               "channelCfi":[
++               ],
++               "globalOperatingClass":135,
++               "maxEirp":[
++               ]
++            }
++         ],
++         "availableFrequencyInfo":[
++            {
++               "frequencyRange":{
++                  "highFrequency":6425,
++                  "lowFrequency":5925
++               },
++               "maxPSD":3.98970004336019
++            },
++            {
++               "frequencyRange":{
++                  "highFrequency":6865,
++                  "lowFrequency":6525
++               },
++               "maxPSD":3.98970004336019
++            }
++         ],
++         "requestId":"11235813",
++         "response":{
++            "responseCode":0,
++            "shortDescription":"Success"
++         },
++         "rulesetId":"US_47_CFR_PART_15_SUBPART_E"
++      }
++   ],
++   "version":"1.1"
++}
+diff --git a/afc/afcd.c b/afc/afcd.c
+new file mode 100644
+index 000000000..2b99940ae
+--- /dev/null
++++ b/afc/afcd.c
+@@ -0,0 +1,305 @@
++/*
++ * Automated Frequency Coordination Daemon
++ * Copyright (c) 2024, Lorenzo Bianconi <lorenzo@kernel.org>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include <curl/curl.h>
++#include <sys/un.h>
++#include <sys/stat.h>
++
++#include "utils/includes.h"
++#include "utils/common.h"
++
++#define CURL_TIMEOUT	60
++#define AFCD_SOCK	"afcd.sock"
++
++struct curl_ctx {
++	char *buf;
++	size_t buf_len;
++};
++
++static volatile bool exiting;
++
++static char *path = "/var/run";
++static char *bearer_token;
++static char *url;
++static int port = 443;
++
++
++static size_t afcd_curl_cb_write(void *ptr, size_t size, size_t nmemb,
++				 void *userdata)
++{
++	struct curl_ctx *ctx = userdata;
++	char *buf;
++
++	buf = os_realloc(ctx->buf, ctx->buf_len + size * nmemb + 1);
++	if (!buf)
++		return 0;
++
++	ctx->buf = buf;
++	os_memcpy(buf + ctx->buf_len, ptr, size * nmemb);
++	buf[ctx->buf_len + size * nmemb] = '\0';
++	ctx->buf_len += size * nmemb;
++
++	return size * nmemb;
++}
++
++
++static int afcd_send_request(struct curl_ctx *ctx, unsigned char *request)
++{
++	struct curl_slist *headers = NULL, *tmp;
++	int ret = CURLE_FAILED_INIT;
++	CURL *curl;
++
++	wpa_printf(MSG_DEBUG, "Sending AFC request to %s", url);
++
++	curl_global_init(CURL_GLOBAL_ALL);
++	curl = curl_easy_init();
++	if (!curl)
++		goto out_global_cleanup;
++
++	headers  = curl_slist_append(headers, "Accept: application/json");
++	if (!headers)
++		goto out_easy_cleanup;
++
++	tmp = curl_slist_append(headers, "Content-Type: application/json");
++	if (!tmp)
++		goto out_slist_free_all;
++	headers = tmp;
++
++	tmp = curl_slist_append(headers, "charset: utf-8");
++	if (!tmp)
++		goto out_slist_free_all;
++	headers = tmp;
++
++	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
++	curl_easy_setopt(curl, CURLOPT_URL, url);
++	curl_easy_setopt(curl, CURLOPT_PORT, port);
++	curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
++	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
++			 afcd_curl_cb_write);
++	curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx);
++	curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcrp/0.1");
++	curl_easy_setopt(curl, CURLOPT_TIMEOUT, CURL_TIMEOUT);
++	curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
++	if (bearer_token)
++		curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, bearer_token);
++	curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BEARER);
++	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
++	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L);
++	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request);
++	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L);
++
++	ret = curl_easy_perform(curl);
++	if (ret != CURLE_OK)
++		wpa_printf(MSG_ERROR, "curl_easy_perform failed: %s",
++			   curl_easy_strerror(ret));
++
++out_slist_free_all:
++	curl_slist_free_all(headers);
++out_easy_cleanup:
++	curl_easy_cleanup(curl);
++out_global_cleanup:
++	curl_global_cleanup();
++
++	return ret == CURLE_OK ? 0 : -EINVAL;
++}
++
++
++static void handle_term(int sig)
++{
++	wpa_printf(MSG_ERROR, "Received signal %d", sig);
++	exiting = true;
++}
++
++
++static void usage(void)
++{
++	wpa_printf(MSG_ERROR,
++		   "%s:\n"
++		   "afcd -u<url> [-p<port>][-t<token>][-D<unix-sock dir>][-P<PID file>][-dB]",
++		   __func__);
++}
++
++
++#define BUFSIZE		8192
++static int afcd_server_run(void)
++{
++	size_t len = os_strlen(path) + 1 + os_strlen(AFCD_SOCK);
++	struct sockaddr_un addr = {
++		.sun_family = AF_UNIX,
++#ifdef __FreeBSD__
++		.sun_len = sizeof(addr),
++#endif /* __FreeBSD__ */
++	};
++	int sockfd, ret = 0;
++	char *fname = NULL;
++	unsigned char *buf;
++	fd_set read_set;
++
++	if (len >= sizeof(addr.sun_path))
++		return -EINVAL;
++
++	if (mkdir(path, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST)
++		return -EINVAL;
++
++	buf = os_malloc(BUFSIZE);
++	if (!buf)
++		return -ENOMEM;
++
++	fname = os_malloc(len + 1);
++	if (!fname) {
++		ret = -ENOMEM;
++		goto free_buf;
++	}
++
++	os_snprintf(fname, len + 1, "%s/%s", path, AFCD_SOCK);
++	fname[len] = '\0';
++	os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
++
++	sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
++	if (sockfd < 0) {
++		wpa_printf(MSG_ERROR, "Failed creating socket");
++		ret = -errno;
++		goto unlink;
++	}
++
++	if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
++		wpa_printf(MSG_ERROR, "Failed to bind socket");
++		ret = -errno;
++		goto close;
++	}
++
++	if (listen(sockfd, 10) < 0) {
++		wpa_printf(MSG_ERROR, "Failed to listen on socket");
++		ret = -errno;
++		goto close;
++	}
++
++	FD_ZERO(&read_set);
++	while (!exiting) {
++		socklen_t addr_len = sizeof(addr);
++		struct sockaddr_in6 c_addr;
++		struct timeval timeout = {
++			.tv_sec = 1,
++		};
++		struct curl_ctx ctx = {};
++		int fd;
++
++		FD_SET(sockfd, &read_set);
++		if (select(sockfd + 1, &read_set, NULL, NULL, &timeout) < 0) {
++			if (errno != EINTR) {
++				wpa_printf(MSG_ERROR,
++					   "Select failed on socket");
++				ret = -errno;
++				break;
++			}
++			continue;
++		}
++
++		if (!FD_ISSET(sockfd, &read_set))
++			continue;
++
++		fd = accept(sockfd, (struct sockaddr *)&c_addr,
++			    &addr_len);
++		if (fd < 0) {
++			if (errno != EINTR) {
++				wpa_printf(MSG_ERROR,
++					   "Failed accepting connections");
++				ret = -errno;
++				break;
++			}
++			continue;
++		}
++
++		os_memset(buf, 0, BUFSIZE);
++		if (recv(fd, buf, BUFSIZE - 1, 0) <= 0) {
++			close(fd);
++			continue;
++		}
++
++		wpa_printf(MSG_DEBUG, "Received request: %s", buf);
++		if (!afcd_send_request(&ctx, buf)) {
++			wpa_printf(MSG_DEBUG, "Received reply: %s", ctx.buf);
++			send(fd, ctx.buf, ctx.buf_len, MSG_NOSIGNAL);
++			free(ctx.buf);
++		}
++		close(fd);
++	}
++close:
++	close(sockfd);
++unlink:
++	unlink(fname);
++	os_free(fname);
++free_buf:
++	os_free(buf);
++
++	return ret;
++}
++
++
++int main(int argc, char **argv)
++{
++	bool daemonize = false;
++	char *pid_file = NULL;
++
++	if (os_program_init())
++		return -1;
++
++	for (;;) {
++		int c = getopt(argc, argv, "u:p:t:D:P:hdB");
++
++		if (c < 0)
++			break;
++
++		switch (c) {
++		case 'h':
++			usage();
++			return 0;
++		case 'B':
++			daemonize = true;
++			break;
++		case 'D':
++			path = optarg;
++			break;
++		case 'P':
++			os_free(pid_file);
++			pid_file = os_rel2abs_path(optarg);
++			break;
++		case 'u':
++			url = optarg;
++			break;
++		case 'p':
++			port = atoi(optarg);
++			break;
++		case 'd':
++			if (wpa_debug_level > 0)
++				wpa_debug_level--;
++			break;
++		case 't':
++			bearer_token = optarg;
++			break;
++		default:
++			usage();
++			return -EINVAL;
++		}
++	}
++
++	if (!url) {
++		usage();
++		return -EINVAL;
++	}
++
++	if (daemonize && os_daemonize(pid_file)) {
++		wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
++		return -EINVAL;
++	}
++
++	signal(SIGTERM, handle_term);
++	signal(SIGINT, handle_term);
++
++	return afcd_server_run();
++}
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0006-hostapd-MLO-reset-auth-state-machine-s-ML-info.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0006-hostapd-MLO-reset-auth-state-machine-s-ML-info.patch
deleted file mode 100644
index 2a7bacd..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0006-hostapd-MLO-reset-auth-state-machine-s-ML-info.patch
+++ /dev/null
@@ -1,93 +0,0 @@
-From 4be7c245a946016c41a69c7469e00d22aaa32a46 Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:36 +0530
-Subject: [PATCH 006/104] hostapd: MLO: reset auth state machine's ML info
-
-Currently auth state machine ML info is set only when the it is created
-newly. However, if the association is tried again, the state machine will
-exist already and hence the ML info will not be refreshed. This leads to
-an issue where if in the subsequent association request, the MLD info is
-different than old info then validation of it will fail.
-
-Fix this issue by refreshing the auth state machine's ML info every time
-association request is handled.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/ieee802_11.c | 32 ++++++++++++++++++--------------
- src/ap/wpa_auth.c   |  1 +
- 2 files changed, 19 insertions(+), 14 deletions(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index b20300bab..98398ccdd 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -4032,15 +4032,15 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
- 
- 	if (hapd->conf->wpa && wpa_ie) {
- 		enum wpa_validate_result res;
-+#ifdef CONFIG_IEEE80211BE
-+		struct mld_info *info = &sta->mld_info;
-+		bool init = false;
-+#endif /* CONFIG_IEEE80211BE */
- 
- 		wpa_ie -= 2;
- 		wpa_ie_len += 2;
- 
- 		if (!sta->wpa_sm) {
--#ifdef CONFIG_IEEE80211BE
--			struct mld_info *info = &sta->mld_info;
--#endif /* CONFIG_IEEE80211BE */
--
- 			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
- 							sta->addr,
- 							p2p_dev_addr);
-@@ -4050,19 +4050,23 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
- 					   "Failed to initialize RSN state machine");
- 				return WLAN_STATUS_UNSPECIFIED_FAILURE;
- 			}
--
- #ifdef CONFIG_IEEE80211BE
--			if (ap_sta_is_mld(hapd, sta)) {
--				wpa_printf(MSG_DEBUG,
--					   "MLD: Set ML info in RSN Authenticator");
--				wpa_auth_set_ml_info(sta->wpa_sm,
--						     hapd->mld->mld_addr,
--						     sta->mld_assoc_link_id,
--						     info);
--			}
--#endif /* CONFIG_IEEE80211BE */
-+			init = true;
- 		}
- 
-+		if (ap_sta_is_mld(hapd, sta)) {
-+			wpa_printf(MSG_DEBUG,
-+				   "MLD: %s ML info in RSN Authenticator",
-+				   init ? "Set" : "Reset");
-+			wpa_auth_set_ml_info(sta->wpa_sm,
-+					     hapd->mld->mld_addr,
-+					     sta->mld_assoc_link_id,
-+					     info);
-+		}
-+#else
-+		}
-+#endif /* CONFIG_IEEE80211BE */
-+
- 		wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
- 		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
- 					  hapd->iface->freq,
-diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
-index 01a10b23c..0d15c4209 100644
---- a/src/ap/wpa_auth.c
-+++ b/src/ap/wpa_auth.c
-@@ -6820,6 +6820,7 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
- 		return;
- 
- 	os_memset(sm->mld_links, 0, sizeof(sm->mld_links));
-+	sm->n_mld_affiliated_links = 0;
- 
- 	wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
- 			"MLD: Initialization");
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0006-hostapd-export-hostapd_is_usable_chans-utility-routi.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0006-hostapd-export-hostapd_is_usable_chans-utility-routi.patch
new file mode 100644
index 0000000..7dc2584
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0006-hostapd-export-hostapd_is_usable_chans-utility-routi.patch
@@ -0,0 +1,57 @@
+From ce1dd0c7ab931cc448bed621a12033fbc94f36f1 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 17 May 2024 11:50:26 +0200
+Subject: [PATCH 006/126] hostapd: export hostapd_is_usable_chans utility
+ routine
+
+This is a preliminary patch to introduce AFC support.
+
+Tested-by: Felix Fietkau <nbd@nbd.name>
+Tested-by: Allen Ye <allen.ye@mediatek.com>
+Tested-by: Krishna Chaitanya <chaitanya.mgit@gmail.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ src/ap/hw_features.c | 2 +-
+ src/ap/hw_features.h | 6 ++++++
+ 2 files changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index c4556603d..85e67080d 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -1008,7 +1008,7 @@ static bool hostapd_is_usable_punct_bitmap(struct hostapd_iface *iface)
+  * 0 = not usable
+  * -1 = not currently usable due to 6 GHz NO-IR
+  */
+-static int hostapd_is_usable_chans(struct hostapd_iface *iface)
++int hostapd_is_usable_chans(struct hostapd_iface *iface)
+ {
+ 	int secondary_freq;
+ 	struct hostapd_channel_data *pri_chan;
+diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
+index c682c6d20..eeffb1abd 100644
+--- a/src/ap/hw_features.h
++++ b/src/ap/hw_features.h
+@@ -30,6 +30,7 @@ void hostapd_stop_setup_timers(struct hostapd_iface *iface);
+ int hostapd_hw_skip_mode(struct hostapd_iface *iface,
+ 			 struct hostapd_hw_modes *mode);
+ int hostapd_determine_mode(struct hostapd_iface *iface);
++int hostapd_is_usable_chans(struct hostapd_iface *iface);
+ #else /* NEED_AP_MLME */
+ static inline void
+ hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
+@@ -103,6 +104,11 @@ static inline int hostapd_determine_mode(struct hostapd_iface *iface)
+ 	return 0;
+ }
+ 
++static inline int hostapd_is_usable_chans(struct hostapd_iface *iface)
++{
++	return 1;
++}
++
+ #endif /* NEED_AP_MLME */
+ 
+ #endif /* HW_FEATURES_H */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0007-hostapd-MLO-add-support-for-cohosted-ML-BSS.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0007-hostapd-MLO-add-support-for-cohosted-ML-BSS.patch
deleted file mode 100644
index 9afd8f4..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0007-hostapd-MLO-add-support-for-cohosted-ML-BSS.patch
+++ /dev/null
@@ -1,201 +0,0 @@
-From d56daa4ebdf544a30f30986097edd6d5f9b8674f Mon Sep 17 00:00:00 2001
-From: Sriram R <quic_srirrama@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:37 +0530
-Subject: [PATCH 007/104] hostapd: MLO: add support for cohosted ML BSS
-
-Currently MLO is being supported with an assumption of only single BSS per
-link in the hostapd conf file. This needs to be extended when cohosted ML
-BSS exist in the same config file.
-
-Extend the support for cohosted BSSes. This is required for MBSSID MLO
-support as well.
-
-Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- hostapd/main.c   | 38 +++++++-------------------
- src/ap/hostapd.c | 70 +++++++++++++++++++++++++++++++++++++++++++-----
- 2 files changed, 73 insertions(+), 35 deletions(-)
-
-diff --git a/hostapd/main.c b/hostapd/main.c
-index a43d3a5be..524a10274 100644
---- a/hostapd/main.c
-+++ b/hostapd/main.c
-@@ -158,6 +158,9 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
- 	struct hostapd_bss_config *conf = hapd->conf;
- 	u8 *b = conf->bssid;
- 	struct wpa_driver_capa capa;
-+#ifdef CONFIG_IEEE80211BE
-+	struct hostapd_data *h_hapd = NULL;
-+#endif /* CONFIG_IEEE80211BE */
- 
- 	if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) {
- 		wpa_printf(MSG_ERROR, "No hostapd driver wrapper available");
-@@ -165,35 +168,10 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
- 	}
- 
- #ifdef CONFIG_IEEE80211BE
--	for (i = 0; conf->mld_ap && i < iface->interfaces->count; i++) {
--		struct hostapd_iface *h = iface->interfaces->iface[i];
--		struct hostapd_data *h_hapd = h->bss[0];
--		struct hostapd_bss_config *hconf = h_hapd->conf;
--
--		if (h == iface) {
--			wpa_printf(MSG_DEBUG, "MLD: Skip own interface");
--			continue;
--		}
--
--		if (!hconf->mld_ap) {
--			wpa_printf(MSG_DEBUG,
--				   "MLD: Skip non-MLD");
--			continue;
--		}
--
--		if (!hostapd_is_ml_partner(hapd, h_hapd)) {
--			wpa_printf(MSG_DEBUG,
--				   "MLD: Skip non matching MLD vif name");
--			continue;
--		}
--
--		wpa_printf(MSG_DEBUG, "MLD: Found matching MLD interface");
--		if (!h_hapd->drv_priv) {
--			wpa_printf(MSG_DEBUG,
--				   "MLD: Matching MLD BSS not initialized yet");
--			continue;
--		}
-+	if (conf->mld_ap)
-+		h_hapd = hostapd_mld_get_first_bss(hapd);
- 
-+	if (h_hapd) {
- 		hapd->drv_priv = h_hapd->drv_priv;
- 		hapd->interface_added = h_hapd->interface_added;
- 
-@@ -214,6 +192,8 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
- 		}
- 
- 		hostapd_mld_add_link(hapd);
-+		wpa_printf(MSG_DEBUG, "Setup of non first link (%d) BSS of MLD %s",
-+			   hapd->mld_link_id, hapd->conf->iface);
- 
- 		goto setup_mld;
- 	}
-@@ -298,6 +278,8 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
- 			os_memcpy(hapd->own_addr, b, ETH_ALEN);
- 
- 		hostapd_mld_add_link(hapd);
-+		wpa_printf(MSG_DEBUG, "Setup of first link (%d) BSS of MLD %s",
-+			   hapd->mld_link_id, hapd->conf->iface);
- 	}
- 
- setup_mld:
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index f8cb6432d..ff1d8f9d0 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -1333,6 +1333,9 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
- 	char force_ifname[IFNAMSIZ];
- 	u8 if_addr[ETH_ALEN];
- 	int flush_old_stations = 1;
-+#ifdef CONFIG_IEEE80211BE
-+	struct hostapd_data *h_hapd = NULL;
-+#endif /* CONFIG_IEEE80211BE */
- 
- 	if (!hostapd_mld_is_first_bss(hapd))
- 		wpa_printf(MSG_DEBUG,
-@@ -1379,6 +1382,21 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
- 			} while (mac_in_conf(hapd->iconf, hapd->own_addr));
- 		}
- 
-+#ifdef CONFIG_IEEE80211BE
-+		if (conf->mld_ap) {
-+			h_hapd = hostapd_mld_get_first_bss(hapd);
-+
-+			if (h_hapd) {
-+				hapd->drv_priv = h_hapd->drv_priv;
-+				hapd->interface_added = h_hapd->interface_added;
-+				hostapd_mld_add_link(hapd);
-+				wpa_printf(MSG_DEBUG, "Setup of non first link (%d) BSS of MLD %s",
-+					   hapd->mld_link_id, hapd->conf->iface);
-+				goto setup_mld;
-+			}
-+		}
-+#endif /* CONFIG_IEEE80211BE */
-+
- 		hapd->interface_added = 1;
- 		if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
- 				   conf->iface, addr, hapd,
-@@ -1393,8 +1411,33 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
- 
- 		if (!addr)
- 			os_memcpy(hapd->own_addr, if_addr, ETH_ALEN);
-+
-+#ifdef CONFIG_IEEE80211BE
-+		if (hapd->conf->mld_ap) {
-+			wpa_printf(MSG_DEBUG, "Setup of first link (%d) BSS of MLD %s",
-+				   hapd->mld_link_id, hapd->conf->iface);
-+			os_memcpy(hapd->mld->mld_addr, hapd->own_addr, ETH_ALEN);
-+			hostapd_mld_add_link(hapd);
-+		}
- 	}
- 
-+setup_mld:
-+
-+	if (hapd->conf->mld_ap && !first) {
-+		wpa_printf(MSG_DEBUG,
-+			   "MLD: Set link_id=%u, mld_addr=" MACSTR
-+			   ", own_addr=" MACSTR,
-+			   hapd->mld_link_id, MAC2STR(hapd->mld->mld_addr),
-+			   MAC2STR(hapd->own_addr));
-+
-+		if (hostapd_drv_link_add(hapd, hapd->mld_link_id,
-+					 hapd->own_addr))
-+			return -1;
-+	}
-+#else
-+	}
-+#endif /* CONFIG_IEEE80211BE */
-+
- 	if (conf->wmm_enabled < 0)
- 		conf->wmm_enabled = hapd->iconf->ieee80211n |
- 			hapd->iconf->ieee80211ax;
-@@ -4679,17 +4722,30 @@ void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx)
- struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
- 					       u8 link_id)
- {
--	unsigned int i;
-+	struct hostapd_iface *iface;
-+	struct hostapd_data *bss;
-+	struct hostapd_bss_config *conf;
-+	unsigned int i, j;
- 
- 	for (i = 0; i < hapd->iface->interfaces->count; i++) {
--		struct hostapd_iface *h = hapd->iface->interfaces->iface[i];
--		struct hostapd_data *h_hapd = h->bss[0];
--
--		if (!hostapd_is_ml_partner(hapd, h_hapd))
-+		iface = hapd->iface->interfaces->iface[i];
-+		if (!iface)
- 			continue;
- 
--		if (h_hapd->mld_link_id == link_id)
--			return h_hapd;
-+		for (j = 0; j < iface->num_bss; j++) {
-+			bss = iface->bss[j];
-+			conf = bss->conf;
-+
-+			if (!conf->mld_ap ||
-+			    !hostapd_is_ml_partner(hapd, bss))
-+				continue;
-+
-+			if (!bss->drv_priv)
-+				continue;
-+
-+			if (bss->mld_link_id == link_id)
-+				return bss;
-+		}
- 	}
- 
- 	return NULL;
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0007-hostapd-ap-add-AFC-client-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0007-hostapd-ap-add-AFC-client-support.patch
new file mode 100644
index 0000000..0dce399
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0007-hostapd-ap-add-AFC-client-support.patch
@@ -0,0 +1,1650 @@
+From 3bd1d33a94f25337fd70df60ee5a42a60f95cba9 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 17 May 2024 11:50:27 +0200
+Subject: [PATCH 007/126] hostapd: ap: add AFC client support
+
+Introduce Automated Frequency Coordination (AFC) support for UNII-5 and
+UNII-7 6GHz bands.
+AFC client will connect to AFCD providing AP related parameter for AFC
+coordinator (e.g. geolocation, supported frequencies, ..).
+AFC is required for Standard Power Devices (SPDs) to determine a lists
+of channels and EIRP/PSD powers that are available in the 6GHz spectrum.
+AFC hostapd client is tested with AFC DUT Test Harness [0].
+
+[0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main
+
+Tested-by: Felix Fietkau <nbd@nbd.name>
+Tested-by: Allen Ye <allen.ye@mediatek.com>
+Tested-by: Krishna Chaitanya <chaitanya.mgit@gmail.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ hostapd/Makefile      |    8 +
+ hostapd/config_file.c |  261 +++++++++++
+ hostapd/defconfig     |    3 +
+ hostapd/hostapd.conf  |   42 ++
+ src/ap/afc.c          | 1041 +++++++++++++++++++++++++++++++++++++++++
+ src/ap/ap_config.c    |   16 +
+ src/ap/ap_config.h    |   47 ++
+ src/ap/hostapd.c      |   16 +
+ src/ap/hostapd.h      |   45 ++
+ src/ap/hw_features.c  |    2 +
+ 10 files changed, 1481 insertions(+)
+ create mode 100644 src/ap/afc.c
+
+diff --git a/hostapd/Makefile b/hostapd/Makefile
+index ca4439234..78171025e 100644
+--- a/hostapd/Makefile
++++ b/hostapd/Makefile
+@@ -104,6 +104,14 @@ CFLAGS += -DCONFIG_TAXONOMY
+ OBJS += ../src/ap/taxonomy.o
+ endif
+ 
++ifdef CONFIG_IEEE80211AX
++ifdef CONFIG_AFC
++CFLAGS += -DCONFIG_AFC
++OBJS += ../src/ap/afc.o
++LIBS += -ljson-c
++endif
++endif
++
+ ifdef CONFIG_MODULE_TESTS
+ CFLAGS += -DCONFIG_MODULE_TESTS
+ OBJS += hapd_module_tests.o
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 96f1b1749..a86621ed9 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -1281,6 +1281,190 @@ static int hostapd_parse_he_srg_bitmap(u8 *bitmap, char *val)
+ 	return 0;
+ }
+ 
++
++#ifdef CONFIG_AFC
++static int hostapd_afc_parse_cert_ids(struct hostapd_config *conf, char *pos)
++{
++	struct cert_id *c = NULL;
++	int i, count = 0;
++
++	while (pos && pos[0]) {
++		char *p;
++
++		c = os_realloc_array(c, count + 1, sizeof(*c));
++		if (!c)
++			return -ENOMEM;
++
++		i = count;
++		count++;
++
++		p = os_strchr(pos, ':');
++		if (!p)
++			goto error;
++
++		*p++ = '\0';
++		if (!p || !p[0])
++			goto error;
++
++		c[i].rulset = os_malloc(os_strlen(pos) + 1);
++		if (!c[i].rulset)
++			goto error;
++
++		os_strlcpy(c[i].rulset, pos, os_strlen(pos) + 1);
++		pos = p;
++		p = os_strchr(pos, ',');
++		if (p)
++			*p++ = '\0';
++
++		c[i].id = os_malloc(os_strlen(pos) + 1);
++		if (!c[i].id)
++			goto error;
++
++		os_strlcpy(c[i].id, pos, os_strlen(pos) + 1);
++		pos = p;
++	}
++
++	conf->afc.n_cert_ids = count;
++	conf->afc.cert_ids = c;
++
++	return 0;
++
++error:
++	for (i = 0; i < count; i++) {
++		os_free(c[i].rulset);
++		os_free(c[i].id);
++	}
++	os_free(c);
++
++	return -ENOMEM;
++}
++
++
++static int hostapd_afc_parse_position_data(struct afc_linear_polygon **data,
++					   unsigned int *n_linear_polygon_data,
++					   char *pos)
++{
++	struct afc_linear_polygon *d = NULL;
++	int i, count = 0;
++
++	while (pos && pos[0]) {
++		char *p, *end;
++
++		d = os_realloc_array(d, count + 1, sizeof(*d));
++		if (!d)
++			return -ENOMEM;
++
++		i = count;
++		count++;
++
++		p = os_strchr(pos, ':');
++		if (!p)
++			goto error;
++
++		*p++ = '\0';
++		if (!p || !p[0])
++			goto error;
++
++		d[i].longitude = strtod(pos, &end);
++		if (*end)
++			goto error;
++
++		pos = p;
++		p = os_strchr(pos, ',');
++		if (p)
++			*p++ = '\0';
++
++		d[i].latitude = strtod(pos, &end);
++		if (*end)
++			goto error;
++
++		pos = p;
++	}
++
++	*n_linear_polygon_data = count;
++	*data = d;
++
++	return 0;
++
++error:
++	os_free(d);
++	return -ENOMEM;
++}
++
++
++static int hostapd_afc_parse_freq_range(struct hostapd_config *conf, char *pos)
++{
++	struct afc_freq_range *f = NULL;
++	int i, count = 0;
++
++	while (pos && pos[0]) {
++		char *p;
++
++		f = os_realloc_array(f, count + 1, sizeof(*f));
++		if (!f)
++			return -ENOMEM;
++
++		i = count;
++		count++;
++
++		p = os_strchr(pos, ':');
++		if (!p)
++			goto error;
++
++		*p++ = '\0';
++		if (!p || !p[0])
++			goto error;
++
++		f[i].low_freq = atoi(pos);
++		pos = p;
++		p = os_strchr(pos, ',');
++		if (p)
++			*p++ = '\0';
++
++		f[i].high_freq = atoi(pos);
++		pos = p;
++	}
++
++	conf->afc.n_freq_range = count;
++	conf->afc.freq_range = f;
++
++	return 0;
++
++error:
++	os_free(f);
++	return -ENOMEM;
++}
++
++
++static int hostapd_afc_parse_op_class(struct hostapd_config *conf, char *pos)
++{
++	unsigned int *oc = NULL;
++	int i, count = 0;
++
++	while (pos && pos[0]) {
++		char *p;
++
++		oc = os_realloc_array(oc, count + 1, sizeof(*oc));
++		if (!oc)
++			return -ENOMEM;
++
++		i = count;
++		count++;
++
++		p = os_strchr(pos, ',');
++		if (p)
++			*p++ = '\0';
++
++		oc[i] = atoi(pos);
++		pos = p;
++	}
++
++	conf->afc.n_op_class = count;
++	conf->afc.op_class = oc;
++
++	return 0;
++}
++#endif /* CONFIG_AFC */
+ #endif /* CONFIG_IEEE80211AX */
+ 
+ 
+@@ -3955,6 +4139,83 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 			return 1;
+ 		}
+ 		bss->unsol_bcast_probe_resp_interval = val;
++#ifdef CONFIG_AFC
++	} else if (os_strcmp(buf, "afcd_sock") == 0) {
++		conf->afc.socket = os_malloc(os_strlen(pos) + 1);
++		if (!conf->afc.socket)
++			return 1;
++
++		os_strlcpy(conf->afc.socket, pos, os_strlen(pos) + 1);
++	} else if (os_strcmp(buf, "afc_request_version") == 0) {
++		conf->afc.request.version = os_malloc(os_strlen(pos) + 1);
++		if (!conf->afc.request.version)
++			return 1;
++
++		os_strlcpy(conf->afc.request.version, pos, os_strlen(pos) + 1);
++	} else if (os_strcmp(buf, "afc_request_id") == 0) {
++		conf->afc.request.id = os_malloc(os_strlen(pos) + 1);
++		if (!conf->afc.request.id)
++			return 1;
++
++		os_strlcpy(conf->afc.request.id, pos, os_strlen(pos) + 1);
++	} else if (os_strcmp(buf, "afc_serial_number") == 0) {
++		conf->afc.request.sn = os_malloc(os_strlen(pos) + 1);
++		if (!conf->afc.request.sn)
++			return 1;
++
++		os_strlcpy(conf->afc.request.sn, pos, os_strlen(pos) + 1);
++	} else if (os_strcmp(buf, "afc_cert_ids") == 0) {
++		if (hostapd_afc_parse_cert_ids(conf, pos))
++			return 1;
++	} else if (os_strcmp(buf, "afc_location_type") == 0) {
++		conf->afc.location.type = atoi(pos);
++		if (conf->afc.location.type != ELLIPSE &&
++		    conf->afc.location.type != LINEAR_POLYGON &&
++		    conf->afc.location.type != RADIAL_POLYGON)
++			return 1;
++	} else if (os_strcmp(buf, "afc_linear_polygon") == 0) {
++		if (hostapd_afc_parse_position_data(
++			&conf->afc.location.linear_polygon_data,
++			&conf->afc.location.n_linear_polygon_data,
++			pos))
++			return 1;
++	} else if (os_strcmp(buf, "afc_radial_polygon") == 0) {
++		if (hostapd_afc_parse_position_data(
++			(struct afc_linear_polygon **)
++			&conf->afc.location.radial_polygon_data,
++			&conf->afc.location.n_radial_polygon_data,
++			pos))
++			return 1;
++	} else if (os_strcmp(buf, "afc_major_axis") == 0) {
++		conf->afc.location.major_axis = atoi(pos);
++	} else if (os_strcmp(buf, "afc_minor_axis") == 0) {
++		conf->afc.location.minor_axis = atoi(pos);
++	} else if (os_strcmp(buf, "afc_orientation") == 0) {
++		conf->afc.location.orientation = atoi(pos);
++	} else if (os_strcmp(buf, "afc_height") == 0) {
++		char *end;
++
++		conf->afc.location.height = strtod(pos, &end);
++		if (*end)
++			return 1;
++	} else if (os_strcmp(buf, "afc_height_type") == 0) {
++		conf->afc.location.height_type = os_malloc(os_strlen(pos) + 1);
++		if (!conf->afc.location.height_type)
++			return 1;
++
++		os_strlcpy(conf->afc.location.height_type, pos,
++			   os_strlen(pos) + 1);
++	} else if (os_strcmp(buf, "afc_vertical_tolerance") == 0) {
++		conf->afc.location.vertical_tolerance = atoi(pos);
++	} else if (os_strcmp(buf, "afc_min_power") == 0) {
++		conf->afc.min_power = atoi(pos);
++	} else if (os_strcmp(buf, "afc_freq_range") == 0) {
++		if (hostapd_afc_parse_freq_range(conf, pos))
++			return 1;
++	} else if (os_strcmp(buf, "afc_op_class") == 0) {
++		if (hostapd_afc_parse_op_class(conf, pos))
++			return 1;
++#endif /* CONFIG_AFC */
+ 	} else if (os_strcmp(buf, "mbssid") == 0) {
+ 		int mbssid = atoi(pos);
+ 		if (mbssid < 0 || mbssid > ENHANCED_MBSSID_ENABLED) {
+diff --git a/hostapd/defconfig b/hostapd/defconfig
+index 550db697b..66bf894eb 100644
+--- a/hostapd/defconfig
++++ b/hostapd/defconfig
+@@ -425,3 +425,6 @@ CONFIG_DPP2=y
+ 
+ # Wi-Fi Aware unsynchronized service discovery (NAN USD)
+ #CONFIG_NAN_USD=y
++
++# Enable Automated Frequency Coordination for 6GHz outdoor
++#CONFIG_AFC=y
+diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
+index 93524cf5d..56442c69d 100644
+--- a/hostapd/hostapd.conf
++++ b/hostapd/hostapd.conf
+@@ -1030,6 +1030,48 @@ wmm_ac_vo_acm=0
+ # Valid range: 0..20 TUs; default is 0 (disabled)
+ #unsol_bcast_probe_resp_interval=0
+ 
++##### Automated Frequency Coordination for 6GHz UNII-5 and UNII-7 bands #######
++
++# AFC daemon connection socket
++#afcd_sock=/var/run/afcd.sock
++
++# AFC request identification parameters
++#afc_request_version=1.1
++#afc_request_id=11235813
++#afc_serial_number=abcdefg
++#afc_cert_ids=US_47_CFR_PART_15_SUBPART_E:CID000
++#
++# AFC location type:
++# 0 = ellipse
++# 1 = linear polygon
++# 2 = radial polygon
++#afc_location_type=0
++#
++# AFC ellipse or linear polygon coordinations
++#afc_linear_polygon=-122.984157:37.425056
++#
++# AFC radial polygon coordinations
++#afc_radial_polygon=118.8:92.76,76.44:87.456,98.56:123.33
++#
++# AFC ellipse major/minor axis and orientation
++#afc_major_axis=100
++#afc_minor_axis=50
++#afc_orientation=70
++#
++# AFC device elevation parameters
++#afc_height=3.0
++#afc_height_type=AGL
++#afc_vertical_tolerance=7
++#
++# AFC minimum desired TX power (dbm)
++#afc_min_power=24
++#
++# AFC request frequency ranges
++#afc_freq_range=5925:6425,6525:6875
++#
++# AFC request operation classes
++#afc_op_class=131,132,133,134,136
++
+ ##### IEEE 802.11be related configuration #####################################
+ 
+ #ieee80211be: Whether IEEE 802.11be (EHT) is enabled
+diff --git a/src/ap/afc.c b/src/ap/afc.c
+new file mode 100644
+index 000000000..cfee83fe7
+--- /dev/null
++++ b/src/ap/afc.c
+@@ -0,0 +1,1041 @@
++/*
++ * Automated Frequency Coordination
++ * Copyright (c) 2024, Lorenzo Bianconi <lorenzo@kernel.org>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include <json-c/json.h>
++#include <sys/un.h>
++#include <time.h>
++
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/eloop.h"
++#include "hostapd.h"
++#include "acs.h"
++#include "hw_features.h"
++
++#define HOSTAPD_AFC_RETRY_TIMEOUT	180
++#define HOSTAPD_AFC_TIMEOUT		86400 /* 24h */
++#define HOSTAPD_AFC_BUFSIZE		8192
++
++static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx);
++
++
++static struct json_object *
++hostapd_afc_build_location_request(struct hostapd_iface *iface)
++{
++	struct json_object *location_obj, *center_obj, *ellipse_obj;
++	struct json_object *elevation_obj, *str_obj;
++	struct hostapd_config *iconf = iface->conf;
++	bool is_ap_indoor = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type);
++
++	location_obj = json_object_new_object();
++	if (!location_obj)
++		return NULL;
++
++	if (iconf->afc.location.type != LINEAR_POLYGON) {
++		struct afc_linear_polygon *lp =
++			&iconf->afc.location.linear_polygon_data[0];
++
++		if (!lp)
++			goto error;
++
++		ellipse_obj = json_object_new_object();
++		if (!ellipse_obj)
++			goto error;
++
++		center_obj = json_object_new_object();
++		if (!center_obj)
++			goto error;
++
++		json_object_object_add(ellipse_obj, "center", center_obj);
++
++		str_obj = json_object_new_double(lp->longitude);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(center_obj, "longitude", str_obj);
++		str_obj = json_object_new_double(lp->latitude);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(center_obj, "latitude", str_obj);
++	}
++
++	switch (iconf->afc.location.type) {
++	case LINEAR_POLYGON: {
++		struct json_object *outer_boundary_obj;
++		int i;
++
++		outer_boundary_obj = json_object_new_object();
++		if (!outer_boundary_obj)
++			goto error;
++
++		json_object_object_add(location_obj, "linearPolygon",
++				       outer_boundary_obj);
++		ellipse_obj = json_object_new_array();
++		if (!ellipse_obj)
++			goto error;
++
++		json_object_object_add(outer_boundary_obj, "outerBoundary",
++				       ellipse_obj);
++		for (i = 0;
++		     i < iconf->afc.location.n_linear_polygon_data; i++) {
++			struct afc_linear_polygon *lp =
++				&iconf->afc.location.linear_polygon_data[i];
++
++			center_obj = json_object_new_object();
++			if (!center_obj)
++				goto error;
++
++			json_object_array_add(ellipse_obj, center_obj);
++			str_obj = json_object_new_double(lp->longitude);
++			if (!str_obj)
++				goto error;
++
++			json_object_object_add(center_obj, "longitude",
++					       str_obj);
++			str_obj = json_object_new_double(lp->latitude);
++			if (!str_obj)
++				goto error;
++
++			json_object_object_add(center_obj, "latitude",
++					       str_obj);
++		}
++		break;
++	}
++	case RADIAL_POLYGON: {
++		struct json_object *outer_boundary_obj;
++		int i;
++
++		json_object_object_add(location_obj, "radialPolygon",
++				       ellipse_obj);
++
++		outer_boundary_obj = json_object_new_array();
++		if (!outer_boundary_obj)
++			goto error;
++
++		json_object_object_add(ellipse_obj, "outerBoundary",
++				       outer_boundary_obj);
++		for (i = 0;
++		     i < iconf->afc.location.n_radial_polygon_data; i++) {
++			struct afc_radial_polygon *rp =
++				&iconf->afc.location.radial_polygon_data[i];
++			struct json_object *angle_obj;
++
++			angle_obj = json_object_new_object();
++			if (!angle_obj)
++				goto error;
++
++			json_object_array_add(outer_boundary_obj, angle_obj);
++
++			str_obj = json_object_new_double(rp->angle);
++			if (!str_obj)
++				goto error;
++
++			json_object_object_add(angle_obj, "angle", str_obj);
++			str_obj = json_object_new_double(rp->length);
++			if (!str_obj)
++				goto error;
++
++			json_object_object_add(angle_obj, "length", str_obj);
++		}
++		break;
++	}
++	case ELLIPSE:
++	default:
++		json_object_object_add(location_obj, "ellipse", ellipse_obj);
++
++		str_obj = json_object_new_int(iconf->afc.location.major_axis);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(ellipse_obj, "majorAxis", str_obj);
++		str_obj = json_object_new_int(iconf->afc.location.minor_axis);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(ellipse_obj, "minorAxis", str_obj);
++		str_obj = json_object_new_int(iconf->afc.location.orientation);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(ellipse_obj, "orientation", str_obj);
++		break;
++	}
++
++	elevation_obj = json_object_new_object();
++	if (!elevation_obj)
++		goto error;
++
++	json_object_object_add(location_obj, "elevation",
++			       elevation_obj);
++	str_obj = json_object_new_double(iconf->afc.location.height);
++	if (!str_obj)
++		goto error;
++
++	json_object_object_add(elevation_obj, "height", str_obj);
++	if (iconf->afc.location.height_type) {
++		str_obj = json_object_new_string(iconf->afc.location.height_type);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(elevation_obj, "heightType", str_obj);
++	}
++
++	str_obj = json_object_new_int(iconf->afc.location.vertical_tolerance);
++	if (!str_obj)
++		goto error;
++
++	json_object_object_add(elevation_obj, "verticalUncertainty",
++			       str_obj);
++	str_obj = json_object_new_int(is_ap_indoor);
++	if (!str_obj)
++		goto error;
++
++	json_object_object_add(location_obj, "indoorDeployment", str_obj);
++
++	return location_obj;
++
++error:
++	json_object_put(location_obj);
++	return NULL;
++}
++
++
++static struct json_object * hostapd_afc_get_opclass_chan_list(u8 op_class)
++{
++	struct json_object *chan_list_obj, *str_obj;
++	const struct oper_class_map *oper_class;
++	int chan_offset, chan;
++
++	oper_class = get_oper_class(NULL, op_class);
++	if (!oper_class)
++		return NULL;
++
++	chan_list_obj = json_object_new_array();
++	if (!chan_list_obj)
++		return NULL;
++
++	switch (op_class) {
++	case 132: /*  40MHz */
++		chan_offset = 2;
++		break;
++	case 133: /*  80MHz */
++		chan_offset = 6;
++		break;
++	case 134: /* 160MHz */
++		chan_offset = 14;
++		break;
++	default:
++		chan_offset = 0;
++		break;
++	}
++
++	for (chan = oper_class->min_chan; chan <= oper_class->max_chan;
++	     chan += oper_class->inc) {
++		str_obj = json_object_new_int(chan + chan_offset);
++		if (!str_obj) {
++			json_object_put(chan_list_obj);
++			return NULL;
++		}
++		json_object_array_add(chan_list_obj, str_obj);
++	}
++
++	return chan_list_obj;
++}
++
++
++static struct json_object *
++hostapd_afc_build_req_chan_list(struct hostapd_iface *iface)
++{
++	struct json_object *op_class_list_obj, *str_obj;
++	struct hostapd_config *iconf = iface->conf;
++	int i;
++
++	op_class_list_obj = json_object_new_array();
++	if (!op_class_list_obj)
++		return NULL;
++
++	for (i = 0; i < iconf->afc.n_op_class; i++) {
++		struct json_object *op_class_obj, *chan_list_obj;
++		u8 op_class = iconf->afc.op_class[i];
++
++		if (!is_6ghz_op_class(op_class))
++			continue;
++
++		op_class_obj = json_object_new_object();
++		if (!op_class_obj)
++			goto error;
++
++		json_object_array_add(op_class_list_obj, op_class_obj);
++		str_obj = json_object_new_int(op_class);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(op_class_obj, "globalOperatingClass",
++				       str_obj);
++
++		chan_list_obj = hostapd_afc_get_opclass_chan_list(op_class);
++		if (!chan_list_obj)
++			goto error;
++
++		json_object_object_add(op_class_obj, "channelCfi",
++				       chan_list_obj);
++	}
++
++	return op_class_list_obj;
++
++error:
++	json_object_put(op_class_list_obj);
++	return NULL;
++}
++
++
++static struct json_object *
++hostapd_afc_build_request(struct hostapd_iface *iface)
++{
++	struct json_object *l1_obj, *l2_obj, *la1_obj, *la2_obj;
++	struct json_object *s2_obj, *str_obj, *location_obj;
++	struct hostapd_config *iconf = iface->conf;
++	struct json_object *op_class_list_obj;
++	int i;
++
++	l1_obj = json_object_new_object();
++	if (!l1_obj)
++		return NULL;
++
++	if (iconf->afc.request.version) {
++		str_obj = json_object_new_string(iconf->afc.request.version);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(l1_obj, "version", str_obj);
++	}
++
++	la1_obj = json_object_new_array();
++	if (!la1_obj)
++		goto error;
++
++	json_object_object_add(l1_obj, "availableSpectrumInquiryRequests",
++			       la1_obj);
++	l2_obj = json_object_new_object();
++	if (!l2_obj)
++		goto error;
++
++	json_object_array_add(la1_obj, l2_obj);
++	if (iconf->afc.request.id) {
++		str_obj = json_object_new_string(iconf->afc.request.id);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(l2_obj, "requestId", str_obj);
++	}
++
++	s2_obj = json_object_new_object();
++	if (!s2_obj)
++		goto error;
++
++	json_object_object_add(l2_obj, "deviceDescriptor", s2_obj);
++	if (iconf->afc.request.sn) {
++		str_obj = json_object_new_string(iconf->afc.request.sn);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(s2_obj, "serialNumber", str_obj);
++	}
++
++	la2_obj = json_object_new_array();
++	if (!la2_obj)
++		goto error;
++
++	json_object_object_add(s2_obj, "certificationId", la2_obj);
++	for (i = 0; i < iconf->afc.n_cert_ids; i++) {
++		struct json_object *obj;
++
++		obj = json_object_new_object();
++		if (!obj)
++			goto error;
++
++		json_object_array_add(la2_obj, obj);
++		str_obj =
++			json_object_new_string(iconf->afc.cert_ids[i].rulset);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(obj, "rulesetId", str_obj);
++		str_obj = json_object_new_string(iconf->afc.cert_ids[i].id);
++		if (!str_obj)
++			goto error;
++
++		json_object_object_add(obj, "id", str_obj);
++	}
++
++	location_obj = hostapd_afc_build_location_request(iface);
++	if (!location_obj)
++		goto error;
++
++	json_object_object_add(l2_obj, "location", location_obj);
++	str_obj = json_object_new_int(iconf->afc.min_power);
++	if (!str_obj)
++		goto error;
++
++	json_object_object_add(l2_obj, "minDesiredPower", str_obj);
++
++	if (iconf->afc.n_freq_range) {
++		struct json_object *freq_obj;
++
++		freq_obj = json_object_new_array();
++		if (!freq_obj)
++			goto error;
++
++		json_object_object_add(l2_obj, "inquiredFrequencyRange",
++				       freq_obj);
++		for (i = 0; i < iconf->afc.n_freq_range; i++) {
++			struct afc_freq_range *fr = &iconf->afc.freq_range[i];
++			struct json_object *obj;
++
++			obj = json_object_new_object();
++			if (!obj)
++				goto error;
++
++			json_object_array_add(freq_obj, obj);
++			str_obj = json_object_new_int(fr->low_freq);
++			if (!str_obj)
++				goto error;
++
++			json_object_object_add(obj, "lowFrequency", str_obj);
++			str_obj = json_object_new_int(fr->high_freq);
++			if (!str_obj)
++				goto error;
++
++			json_object_object_add(obj, "highFrequency", str_obj);
++		}
++	}
++
++	op_class_list_obj = hostapd_afc_build_req_chan_list(iface);
++	if (!op_class_list_obj)
++		goto error;
++
++	json_object_object_add(l2_obj, "inquiredChannels", op_class_list_obj);
++
++	wpa_printf(MSG_DEBUG, "Pending AFC request: %s",
++		   json_object_get_string(l1_obj));
++
++	return l1_obj;
++
++error:
++	json_object_put(l1_obj);
++
++	return NULL;
++}
++
++
++static int
++hostad_afc_parse_available_freq_info(struct hostapd_iface *iface,
++				     struct json_object *reply_elem_obj)
++{
++	struct afc_freq_range_elem *f = NULL;
++	struct json_object *obj;
++	int i, count = 0;
++
++	if (!json_object_object_get_ex(reply_elem_obj,
++				       "availableFrequencyInfo", &obj))
++		return 0;
++
++	for (i = 0; i < json_object_array_length(obj); i++) {
++		struct json_object *range_elem_obj, *freq_range_obj;
++		struct json_object *high_freq_obj, *low_freq_obj;
++		struct json_object *max_psd_obj;
++
++		range_elem_obj = json_object_array_get_idx(obj, i);
++		if (!range_elem_obj)
++			continue;
++
++		if (!json_object_object_get_ex(range_elem_obj,
++					       "frequencyRange",
++					       &freq_range_obj))
++			continue;
++
++		if (!json_object_object_get_ex(freq_range_obj,
++					       "lowFrequency",
++					       &low_freq_obj))
++			continue;
++
++		if (!json_object_object_get_ex(freq_range_obj,
++					       "highFrequency",
++					       &high_freq_obj))
++			continue;
++
++		if (!json_object_object_get_ex(range_elem_obj, "maxPsd",
++					       &max_psd_obj) &&
++		    !json_object_object_get_ex(range_elem_obj, "maxPSD",
++					       &max_psd_obj))
++			continue;
++
++		f = os_realloc_array(f, count + 1, sizeof(*f));
++		if (!f)
++			return -ENOMEM;
++
++		f[count].low_freq = json_object_get_int(low_freq_obj);
++		f[count].high_freq = json_object_get_int(high_freq_obj);
++		f[count++].max_psd = json_object_get_int(max_psd_obj);
++	}
++	iface->afc.freq_range = f;
++	iface->afc.num_freq_range = count;
++
++	return 0;
++}
++
++
++static int hostad_afc_update_chan_info(struct afc_chan_info_elem **chan_list,
++				       int *chan_list_size, u8 op_class,
++				       int center_chan, int power)
++{
++	int num_low_subchan, ch, count = *chan_list_size;
++	struct afc_chan_info_elem *c = *chan_list;
++
++	switch (op_class) {
++	case 132: /*  40MHz */
++		num_low_subchan = 2;
++		break;
++	case 133: /*  80MHz */
++		num_low_subchan = 6;
++		break;
++	case 134: /* 160MHz */
++		num_low_subchan = 14;
++		break;
++	default:
++		num_low_subchan = 0;
++		break;
++	}
++
++	for (ch = center_chan - num_low_subchan;
++	     ch <= center_chan + num_low_subchan; ch += 4) {
++		int i;
++
++		for (i = 0; i < count; i++) {
++			if (c[i].chan == ch)
++				break;
++		}
++
++		if (i == count) {
++			c = os_realloc_array(c, count + 1, sizeof(*c));
++			if (!c)
++				return -ENOMEM;
++
++			c[count].chan = ch;
++			c[count++].power = power;
++		}
++	}
++
++	*chan_list_size = count;
++	*chan_list = c;
++
++	return 0;
++}
++
++
++static int
++hostad_afc_parse_available_chan_info(struct hostapd_iface *iface,
++				     struct json_object *reply_elem_obj)
++{
++	struct afc_chan_info_elem *c = NULL;
++	struct json_object *obj;
++	int i, count = 0;
++
++	if (!json_object_object_get_ex(reply_elem_obj,
++				       "availableChannelInfo", &obj))
++		return 0;
++
++	for (i = 0; i < json_object_array_length(obj); i++) {
++		struct json_object *range_elem_obj, *op_class_obj;
++		struct json_object *chan_cfi_obj, *max_eirp_obj;
++		int ch, op_class;
++
++		range_elem_obj = json_object_array_get_idx(obj, i);
++		if (!range_elem_obj)
++			continue;
++
++		if (!json_object_object_get_ex(range_elem_obj,
++					       "globalOperatingClass",
++					       &op_class_obj))
++			continue;
++
++		if (!json_object_object_get_ex(range_elem_obj, "maxEirp",
++					       &max_eirp_obj))
++			continue;
++
++		if (!json_object_object_get_ex(range_elem_obj, "channelCfi",
++					       &chan_cfi_obj))
++			continue;
++
++		op_class = json_object_get_int(op_class_obj);
++		for (ch = 0;
++		     ch < json_object_array_length(chan_cfi_obj); ch++) {
++			struct json_object *pwr_obj;
++			struct json_object *ch_obj;
++			int channel, power;
++
++			ch_obj = json_object_array_get_idx(chan_cfi_obj, ch);
++			if (!ch_obj)
++				continue;
++
++			pwr_obj = json_object_array_get_idx(max_eirp_obj, ch);
++			if (!pwr_obj)
++				continue;
++
++			channel = json_object_get_int(ch_obj);
++			power = json_object_get_int(pwr_obj);
++
++			hostad_afc_update_chan_info(&c, &count, op_class,
++						    channel, power);
++		}
++		iface->afc.chan_info_list = c;
++		iface->afc.num_chan_info = count;
++	}
++
++	return 0;
++}
++
++
++static int hostad_afc_get_timeout(struct json_object *obj)
++{
++	time_t t, now;
++	struct tm tm;
++
++	if (sscanf(json_object_get_string(obj), "%d-%d-%dT%d:%d:%dZ",
++		   &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour,
++		   &tm.tm_min, &tm.tm_sec) <= 0)
++		return HOSTAPD_AFC_TIMEOUT;
++
++	tm.tm_year -= 1900;
++	tm.tm_mon -= 1;
++	tm.tm_isdst = -1;
++	t = mktime(&tm);
++	time(&now);
++
++	return now > t ? HOSTAPD_AFC_RETRY_TIMEOUT : (t - now) * 80 / 100;
++}
++
++
++static int hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply)
++{
++	struct json_object *payload_obj, *reply_obj, *version_obj;
++	struct hostapd_config *iconf = iface->conf;
++	int i, request_timeout = -1, ret = -EINVAL;
++
++	wpa_printf(MSG_DEBUG, "Received AFC reply: %s", reply);
++	payload_obj = json_tokener_parse(reply);
++	if (!payload_obj) {
++		wpa_printf(MSG_ERROR, "Failed to parse AFC reply payload");
++		return -EINVAL;
++	}
++
++	if (!json_object_object_get_ex(payload_obj, "version", &version_obj)) {
++		wpa_printf(MSG_ERROR, "Missing version in AFC reply");
++		return -EINVAL;
++	}
++
++	if (iconf->afc.request.version &&
++	    os_strcmp(iconf->afc.request.version,
++		      json_object_get_string(version_obj))) {
++		wpa_printf(MSG_ERROR, "Mismatch in AFC reply version");
++		return -EINVAL;
++	}
++
++	if (!json_object_object_get_ex(payload_obj,
++				       "availableSpectrumInquiryResponses",
++				       &reply_obj)) {
++		wpa_printf(MSG_ERROR,
++			   "Missing availableSpectrumInquiry in AFC reply");
++		return -EINVAL;
++	}
++
++	for (i = 0; i < json_object_array_length(reply_obj); i++) {
++		struct json_object *reply_elem_obj, *obj, *status_obj;
++		int j, status = -EINVAL;
++
++		reply_elem_obj = json_object_array_get_idx(reply_obj, i);
++		if (!reply_elem_obj) {
++			wpa_printf(MSG_DEBUG,
++				   "Failed to get reply element at index %d",
++				   i);
++			continue;
++		}
++
++		if (!json_object_object_get_ex(reply_elem_obj, "requestId",
++					       &obj)) {
++			wpa_printf(MSG_DEBUG,
++				   "Missing requestId in reply element %d", i);
++			continue;
++		}
++
++		if (iconf->afc.request.id &&
++		    os_strcmp(iconf->afc.request.id,
++			      json_object_get_string(obj))) {
++			wpa_printf(MSG_DEBUG,
++				   "RequestId mismatch in reply element %d",
++				   i);
++			continue;
++		}
++
++		if (!json_object_object_get_ex(reply_elem_obj, "rulesetId",
++					       &obj)) {
++			wpa_printf(MSG_DEBUG,
++				   "Missing rulesetId in reply element %d", i);
++			continue;
++		}
++
++		for (j = 0; j < iconf->afc.n_cert_ids; j++) {
++			if (!os_strcmp(iconf->afc.cert_ids[j].rulset,
++				       json_object_get_string(obj)))
++				break;
++		}
++
++		if (j == iconf->afc.n_cert_ids) {
++			wpa_printf(MSG_DEBUG,
++				   "RulesetId mismatch in reply element %d",
++				   i);
++			continue;
++		}
++
++		if (!json_object_object_get_ex(reply_elem_obj, "response",
++					       &obj)) {
++			wpa_printf(MSG_DEBUG,
++				   "Missing response field in reply element %d",
++				   i);
++			continue;
++		}
++
++		if (json_object_object_get_ex(obj, "shortDescription",
++					      &status_obj))
++			wpa_printf(MSG_DEBUG, "AFC reply element %d: %s",
++				   i, json_object_get_string(status_obj));
++
++		if (json_object_object_get_ex(obj, "responseCode",
++					      &status_obj))
++			status = json_object_get_int(status_obj);
++
++		if (status < 0) {
++			wpa_printf(MSG_DEBUG,
++				   "Reply element %d invalid responseCode: %d",
++				   i, status);
++			continue;
++		}
++
++		if (hostad_afc_parse_available_freq_info(iface,
++							 reply_elem_obj) ||
++		    hostad_afc_parse_available_chan_info(iface,
++							 reply_elem_obj))
++			continue;
++
++		if (json_object_object_get_ex(reply_elem_obj,
++					      "availabilityExpireTime",
++					      &obj)) {
++			int timeout = hostad_afc_get_timeout(obj);
++
++			if (request_timeout < 0 || timeout < request_timeout)
++				request_timeout = timeout;
++		}
++
++		ret = status;
++	}
++
++	iface->afc.data_valid = true;
++	iface->afc.timeout = request_timeout;
++	if (iface->afc.timeout < 0)
++		iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
++
++	return ret;
++}
++
++
++static int hostapd_afc_send_receive(struct hostapd_iface *iface)
++{
++	struct hostapd_config *iconf = iface->conf;
++	json_object *request_obj = NULL;
++	struct timeval sock_timeout = {
++		.tv_sec = 5,
++	};
++	struct sockaddr_un addr = {
++		.sun_family = AF_UNIX,
++#ifdef __FreeBSD__
++		.sun_len = sizeof(addr),
++#endif /* __FreeBSD__ */
++	};
++	const char *request;
++	char *buf = NULL;
++	int sockfd, ret;
++	fd_set read_set;
++
++	if (iface->afc.data_valid) {
++		/* AFC data already downloaded from the server */
++		return 0;
++	}
++
++	if (!iconf->afc.socket) {
++		wpa_printf(MSG_ERROR, "Missing AFC socket string");
++		return -EINVAL;
++	}
++
++	iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
++	if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) {
++		wpa_printf(MSG_ERROR, "Malformed AFC socket string %s",
++			   iconf->afc.socket);
++		return -EINVAL;
++	}
++
++	os_strlcpy(addr.sun_path, iconf->afc.socket, sizeof(addr.sun_path));
++	sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
++	if (sockfd < 0) {
++		wpa_printf(MSG_ERROR, "Failed creating AFC socket");
++		return sockfd;
++	}
++
++	if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
++		wpa_printf(MSG_ERROR, "Failed connecting AFC socket");
++		ret = -EIO;
++		goto close_sock;
++	}
++
++	request_obj = hostapd_afc_build_request(iface);
++	if (!request_obj) {
++		ret = -ENOMEM;
++		goto close_sock;
++	}
++
++	request = json_object_to_json_string(request_obj);
++	if (send(sockfd, request, strlen(request), 0) < 0) {
++		wpa_printf(MSG_ERROR, "Failed sending AFC request");
++		ret = -EIO;
++		goto close_sock;
++	}
++
++	FD_ZERO(&read_set);
++	FD_SET(sockfd, &read_set);
++	if (select(sockfd + 1, &read_set, NULL, NULL, &sock_timeout) < 0) {
++		wpa_printf(MSG_ERROR, "Select failed on AFC socket");
++		ret = -errno;
++		goto close_sock;
++	}
++
++	if (!FD_ISSET(sockfd, &read_set)) {
++		ret = -EIO;
++		goto close_sock;
++	}
++
++	buf = os_zalloc(HOSTAPD_AFC_BUFSIZE);
++	if (!buf) {
++		ret = -ENOMEM;
++		goto close_sock;
++	}
++
++	ret = recv(sockfd, buf, HOSTAPD_AFC_BUFSIZE - 1, 0);
++	if (ret <= 0)
++		goto close_sock;
++
++	ret = hostapd_afc_parse_reply(iface, buf);
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed parsing AFC reply: %d", ret);
++close_sock:
++	os_free(buf);
++	json_object_put(request_obj);
++	close(sockfd);
++
++	return ret;
++}
++
++
++static bool hostapd_afc_has_usable_chans(struct hostapd_iface *iface)
++{
++	const struct oper_class_map *oper_class;
++	int ch;
++
++	oper_class = get_oper_class(NULL, iface->conf->op_class);
++	if (!oper_class)
++		return false;
++
++	for (ch = oper_class->min_chan; ch <= oper_class->max_chan;
++	     ch += oper_class->inc) {
++		struct hostapd_hw_modes *mode = iface->current_mode;
++		int i;
++
++		for (i = 0; i < mode->num_channels; i++) {
++			struct hostapd_channel_data *chan = &mode->channels[i];
++
++			if (chan->chan == ch &&
++			    !(chan->flag & HOSTAPD_CHAN_DISABLED))
++				return true;
++		}
++	}
++
++	return false;
++}
++
++
++int hostapd_afc_handle_request(struct hostapd_iface *iface)
++{
++	struct hostapd_config *iconf = iface->conf;
++	int ret;
++
++	/* AFC is required just for standard power AP */
++	if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
++		return 1;
++
++	if (!is_6ghz_op_class(iconf->op_class) || !is_6ghz_freq(iface->freq))
++		return 1;
++
++	if (iface->state == HAPD_IFACE_ACS)
++		return 1;
++
++	ret = hostapd_afc_send_receive(iface);
++	if (ret < 0) {
++		/*
++		 * If the connection to the AFCD failed, resched for a
++		 * future attempt.
++		 */
++		wpa_printf(MSG_ERROR, "AFC connection failed: %d", ret);
++		if (ret == -EIO)
++			ret = 0;
++		goto resched;
++	}
++
++	hostap_afc_disable_channels(iface);
++	if (!hostapd_afc_has_usable_chans(iface))
++		goto resched;
++
++	if (!hostapd_is_usable_chans(iface)) {
++		/* Trigger an ACS freq scan */
++		iconf->channel = 0;
++		iface->freq = 0;
++
++		if (acs_init(iface) != HOSTAPD_CHAN_ACS) {
++			wpa_printf(MSG_ERROR, "Could not start ACS");
++			ret = -EINVAL;
++		}
++	} else {
++		ret = 1;
++	}
++
++resched:
++	eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
++	eloop_register_timeout(iface->afc.timeout, 0,
++			       hostapd_afc_timeout_handler, iface, NULL);
++
++	return ret;
++}
++
++
++static void hostapd_afc_delete_data_from_server(struct hostapd_iface *iface)
++{
++	os_free(iface->afc.chan_info_list);
++	os_free(iface->afc.freq_range);
++
++	iface->afc.num_freq_range = 0;
++	iface->afc.num_chan_info = 0;
++
++	iface->afc.chan_info_list = NULL;
++	iface->afc.freq_range = NULL;
++
++	iface->afc.data_valid = false;
++}
++
++
++static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx)
++{
++	struct hostapd_iface *iface = eloop_ctx;
++	bool restart_iface = true;
++
++	hostapd_afc_delete_data_from_server(iface);
++	if (iface->state != HAPD_IFACE_ENABLED) {
++		/* Hostapd is not fully enabled yet, toggle the interface */
++		goto restart_interface;
++	}
++
++	if (hostapd_afc_send_receive(iface) < 0 ||
++	    hostapd_get_hw_features(iface)) {
++		restart_iface = false;
++		goto restart_interface;
++	}
++
++	if (hostapd_is_usable_chans(iface))
++		goto resched;
++
++	restart_iface = hostapd_afc_has_usable_chans(iface);
++	if (restart_iface) {
++		/* Trigger an ACS freq scan */
++		iface->conf->channel = 0;
++		iface->freq = 0;
++	}
++
++restart_interface:
++	hostapd_disable_iface(iface);
++	if (restart_iface)
++		hostapd_enable_iface(iface);
++resched:
++	eloop_register_timeout(iface->afc.timeout, 0,
++			       hostapd_afc_timeout_handler, iface, NULL);
++}
++
++
++void hostapd_afc_stop(struct hostapd_iface *iface)
++{
++	eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
++}
++
++
++void hostap_afc_disable_channels(struct hostapd_iface *iface)
++{
++	struct hostapd_hw_modes *mode = NULL;
++	int i;
++
++	for (i = 0; i < iface->num_hw_features; i++) {
++		mode = &iface->hw_features[i];
++		if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
++		    mode->is_6ghz)
++			break;
++	}
++
++	if (i == iface->num_hw_features)
++		return;
++
++	if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type))
++		return;
++
++	if (!iface->afc.data_valid)
++		return;
++
++	for (i = 0; i < mode->num_channels; i++) {
++		struct hostapd_channel_data *chan = &mode->channels[i];
++		int j;
++
++		if (!is_6ghz_freq(chan->freq))
++			continue;
++
++		for (j = 0; j < iface->afc.num_freq_range; j++) {
++			if (chan->freq >= iface->afc.freq_range[j].low_freq &&
++			    chan->freq <= iface->afc.freq_range[j].high_freq)
++				break;
++		}
++
++		if (j != iface->afc.num_freq_range)
++			continue;
++
++		for (j = 0; j < iface->afc.num_chan_info; j++) {
++			if (chan->chan == iface->afc.chan_info_list[j].chan)
++				break;
++		}
++
++		if (j != iface->afc.num_chan_info)
++			continue;
++
++		chan->flag |= HOSTAPD_CHAN_DISABLED;
++		wpa_printf(MSG_MSGDUMP,
++			   "Disabling freq=%d MHz (not allowed by AFC)",
++			   chan->freq);
++	}
++}
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index c6aa49610..9e34e029a 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -1047,6 +1047,22 @@ void hostapd_config_free(struct hostapd_config *conf)
+ #endif /* CONFIG_ACS */
+ 	wpabuf_free(conf->lci);
+ 	wpabuf_free(conf->civic);
++#ifdef CONFIG_AFC
++	os_free(conf->afc.socket);
++	os_free(conf->afc.request.version);
++	os_free(conf->afc.request.id);
++	os_free(conf->afc.request.sn);
++	for (i = 0; i < conf->afc.n_cert_ids; i++) {
++		os_free(conf->afc.cert_ids[i].rulset);
++		os_free(conf->afc.cert_ids[i].id);
++	}
++	os_free(conf->afc.cert_ids);
++	os_free(conf->afc.location.height_type);
++	os_free(conf->afc.location.linear_polygon_data);
++	os_free(conf->afc.location.radial_polygon_data);
++	os_free(conf->afc.freq_range);
++	os_free(conf->afc.op_class);
++#endif /* CONFIG_AFC */
+ 
+ 	os_free(conf);
+ }
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index d42076785..e6669e6a3 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1249,6 +1249,53 @@ struct hostapd_config {
+ 
+ 	/* Whether to enable TWT responder in HT and VHT modes */
+ 	bool ht_vht_twt_responder;
++
++#ifdef CONFIG_AFC
++	struct {
++		char *socket;
++		struct {
++			char *version;
++			char *id;
++			char *sn;
++		} request;
++		unsigned int n_cert_ids;
++		struct cert_id {
++			char *rulset;
++			char *id;
++		} *cert_ids;
++		struct {
++			enum afc_location_type {
++				ELLIPSE,
++				LINEAR_POLYGON,
++				RADIAL_POLYGON,
++			} type;
++			unsigned int n_linear_polygon_data;
++			struct afc_linear_polygon {
++				double longitude;
++				double latitude;
++			} *linear_polygon_data;
++			unsigned int n_radial_polygon_data;
++			struct afc_radial_polygon {
++				double length;
++				double angle;
++			} *radial_polygon_data;
++			int major_axis;
++			int minor_axis;
++			int orientation;
++			double height;
++			char *height_type;
++			int vertical_tolerance;
++		} location;
++		unsigned int n_freq_range;
++		struct afc_freq_range {
++			int low_freq;
++			int high_freq;
++		} *freq_range;
++		unsigned int n_op_class;
++		unsigned int *op_class;
++		int min_power;
++	} afc;
++#endif /* CONFIG_AFC */
+ };
+ 
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 36d48ae09..5a8cdc90e 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -715,6 +715,7 @@ void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
+ static void hostapd_cleanup_iface(struct hostapd_iface *iface)
+ {
+ 	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
++	hostapd_afc_stop(iface);
+ 	eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
+ 			     NULL);
+ 
+@@ -2559,6 +2560,16 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
+ 		}
+ #endif /* CONFIG_MESH */
+ 
++#ifdef CONFIG_IEEE80211AX
++		/* check AFC for 6GHz channels. */
++		res = hostapd_afc_handle_request(iface);
++		if (res <= 0) {
++			if (res < 0)
++				goto fail;
++			return res;
++		}
++#endif /* CONFIG_IEEE80211AX */
++
+ 		if (!delay_apply_cfg &&
+ 		    hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
+ 				     hapd->iconf->channel,
+@@ -2957,6 +2968,7 @@ void hostapd_interface_deinit(struct hostapd_iface *iface)
+ 
+ 	hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+ 
++	hostapd_afc_stop(iface);
+ 	eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+ 	iface->wait_channel_update = 0;
+ 	iface->is_no_ir = false;
+@@ -3030,6 +3042,10 @@ void hostapd_interface_free(struct hostapd_iface *iface)
+ 			   __func__, iface->bss[j]);
+ 		os_free(iface->bss[j]);
+ 	}
++#ifdef CONFIG_AFC
++	os_free(iface->afc.chan_info_list);
++	os_free(iface->afc.freq_range);
++#endif
+ 	hostapd_cleanup_iface(iface);
+ }
+ 
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 2ef63e5f2..d67a0afa0 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -718,9 +718,54 @@ struct hostapd_iface {
+ 	bool is_no_ir;
+ 
+ 	bool is_ch_switch_dfs; /* Channel switch from ACS to DFS */
++
++#ifdef CONFIG_AFC
++	struct {
++		int timeout;
++		unsigned int num_freq_range;
++		struct afc_freq_range_elem {
++			int low_freq;
++			int high_freq;
++			/**
++			 * max eirp power spectral density received from
++			 * the AFC coordinator for this band
++			 */
++			int max_psd;
++		} *freq_range;
++		unsigned int num_chan_info;
++		struct afc_chan_info_elem {
++			int chan;
++			/**
++			 * max eirp power received from the AFC coordinator
++			 * for this channel
++			 */
++			int power;
++		} *chan_info_list;
++		bool data_valid;
++	} afc;
++#endif /* CONFIG_AFC */
+ };
+ 
+ /* hostapd.c */
++#ifdef CONFIG_AFC
++int hostapd_afc_handle_request(struct hostapd_iface *iface);
++void hostapd_afc_stop(struct hostapd_iface *iface);
++void hostap_afc_disable_channels(struct hostapd_iface *iface);
++#else
++static inline int hostapd_afc_handle_request(struct hostapd_iface *iface)
++{
++	return 1;
++}
++
++static inline void hostapd_afc_stop(struct hostapd_iface *iface)
++{
++}
++
++static inline void hostap_afc_disable_channels(struct hostapd_iface *iface)
++{
++}
++#endif /* CONFIG_AFC */
++
+ int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+ 			       int (*cb)(struct hostapd_iface *iface,
+ 					 void *ctx), void *ctx);
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index 85e67080d..8aa0b3ab5 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -114,6 +114,8 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
+ 	iface->hw_features = modes;
+ 	iface->num_hw_features = num_modes;
+ 
++	hostap_afc_disable_channels(iface);
++
+ 	for (i = 0; i < num_modes; i++) {
+ 		struct hostapd_hw_modes *feature = &modes[i];
+ 		int dfs_enabled = hapd->iconf->ieee80211h &&
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0008-hostapd-MLO-extend-support-for-cohosted-ML-BSS.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0008-hostapd-MLO-extend-support-for-cohosted-ML-BSS.patch
deleted file mode 100644
index 42c4b67..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0008-hostapd-MLO-extend-support-for-cohosted-ML-BSS.patch
+++ /dev/null
@@ -1,368 +0,0 @@
-From 27dbd9d9796d656c8cf78d51d48162208080a987 Mon Sep 17 00:00:00 2001
-From: Sriram R <quic_srirrama@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:38 +0530
-Subject: [PATCH 008/104] hostapd: MLO: extend support for cohosted ML BSS
-
-Modify necessary helper apis to support multiple BSS support for MLO to
-make the changes scalable.
-
-Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/ieee802_11.c     | 116 ++++++++++++++++------------------------
- src/ap/ieee802_11_eht.c |  27 +++-------
- src/ap/wpa_auth_glue.c  |  52 +++++++++++-------
- 3 files changed, 89 insertions(+), 106 deletions(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 98398ccdd..26e3d8356 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -4567,7 +4567,7 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
- 				  bool offload)
- {
- #ifdef CONFIG_IEEE80211BE
--	unsigned int i, j;
-+	unsigned int i;
- 
- 	if (!hostapd_is_mld_ap(hapd))
- 		return 0;
-@@ -4582,25 +4582,25 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
- 		hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
- 
- 	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
--		struct hostapd_iface *iface = NULL;
-+		struct hostapd_data *bss = NULL;
- 		struct mld_link_info *link = &sta->mld_info.links[i];
-+		bool link_bss_found = false;
- 
- 		if (!link->valid)
- 			continue;
- 
--		for (j = 0; j < hapd->iface->interfaces->count; j++) {
--			iface = hapd->iface->interfaces->iface[j];
-+		for_each_mld_link(bss, hapd) {
-+			if (bss == hapd)
-+				continue;
- 
--			if (hapd->iface == iface)
-+			if (bss->mld_link_id != i)
- 				continue;
- 
--			if (hostapd_is_ml_partner(hapd, iface->bss[0]) &&
--			    i == iface->bss[0]->mld_link_id)
--				break;
-+			link_bss_found = true;
-+			break;
- 		}
- 
--		if (!iface || j == hapd->iface->interfaces->count ||
--		    TEST_FAIL()) {
-+		if (!link_bss_found || TEST_FAIL()) {
- 			wpa_printf(MSG_DEBUG,
- 				   "MLD: No link match for link_id=%u", i);
- 
-@@ -4613,7 +4613,7 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
- 			if (!offload)
- 				ieee80211_ml_build_assoc_resp(hapd, link);
- 		} else {
--			if (ieee80211_ml_process_link(iface->bss[0], sta, link,
-+			if (ieee80211_ml_process_link(bss, sta, link,
- 						      ies, ies_len, reassoc,
- 						      offload))
- 				return -1;
-@@ -5777,7 +5777,7 @@ static bool hostapd_ml_handle_disconnect(struct hostapd_data *hapd,
- #ifdef CONFIG_IEEE80211BE
- 	struct hostapd_data *assoc_hapd, *tmp_hapd;
- 	struct sta_info *assoc_sta;
--	unsigned int i, link_id;
-+	struct sta_info *tmp_sta;
- 
- 	if (!hostapd_is_mld_ap(hapd))
- 		return false;
-@@ -5790,45 +5790,27 @@ static bool hostapd_ml_handle_disconnect(struct hostapd_data *hapd,
- 	if (!assoc_sta)
- 		return false;
- 
--	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
--		for (i = 0; i < assoc_hapd->iface->interfaces->count; i++) {
--			struct sta_info *tmp_sta;
--
--			if (!assoc_sta->mld_info.links[link_id].valid)
--				continue;
-+	for_each_mld_link(tmp_hapd, assoc_hapd) {
-+		if (tmp_hapd == assoc_hapd)
-+			continue;
- 
--			tmp_hapd =
--				assoc_hapd->iface->interfaces->iface[i]->bss[0];
-+		if (!assoc_sta->mld_info.links[tmp_hapd->mld_link_id].valid)
-+			continue;
- 
--			if (!hostapd_is_ml_partner(assoc_hapd, tmp_hapd))
-+		for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
-+		     tmp_sta = tmp_sta->next) {
-+			if (tmp_sta->mld_assoc_link_id !=
-+			    assoc_sta->mld_assoc_link_id ||
-+			    tmp_sta->aid != assoc_sta->aid)
- 				continue;
- 
--			for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
--			     tmp_sta = tmp_sta->next) {
--				/*
--				 * Remove the station on which the association
--				 * was done only after all other link stations
--				 * are removed. Since there is only a single
--				 * station per struct hostapd_hapd with the
--				 * same association link simply break out from
--				 * the loop.
--				 */
--				if (tmp_sta == assoc_sta)
--					break;
--
--				if (tmp_sta->mld_assoc_link_id !=
--				    assoc_sta->mld_assoc_link_id ||
--				    tmp_sta->aid != assoc_sta->aid)
--					continue;
--
--				if (!disassoc)
--					hostapd_deauth_sta(tmp_hapd, tmp_sta,
--							   mgmt);
--				else
--					hostapd_disassoc_sta(tmp_hapd, tmp_sta,
--							     mgmt);
--				break;
--			}
-+			if (!disassoc)
-+				hostapd_deauth_sta(tmp_hapd, tmp_sta,
-+						   mgmt);
-+			else
-+				hostapd_disassoc_sta(tmp_hapd, tmp_sta,
-+						     mgmt);
-+			break;
- 		}
- 	}
- 
-@@ -6451,38 +6433,34 @@ static void hostapd_ml_handle_assoc_cb(struct hostapd_data *hapd,
- 				       struct sta_info *sta, bool ok)
- {
- #ifdef CONFIG_IEEE80211BE
--	unsigned int i, link_id;
-+	struct hostapd_data *tmp_hapd;
- 
- 	if (!hostapd_is_mld_ap(hapd))
- 		return;
- 
--	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
--		struct mld_link_info *link = &sta->mld_info.links[link_id];
-+	for_each_mld_link(tmp_hapd, hapd) {
-+		struct mld_link_info *link =
-+				&sta->mld_info.links[tmp_hapd->mld_link_id];
-+		struct sta_info *tmp_sta;
- 
--		if (!link->valid)
-+		if (tmp_hapd == hapd)
- 			continue;
- 
--		for (i = 0; i < hapd->iface->interfaces->count; i++) {
--			struct sta_info *tmp_sta;
--			struct hostapd_data *tmp_hapd =
--				hapd->iface->interfaces->iface[i]->bss[0];
-+		if (!link->valid)
-+			continue;
- 
--			if (!hostapd_is_ml_partner(tmp_hapd, hapd))
-+		for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
-+		     tmp_sta = tmp_sta->next) {
-+			if (tmp_sta == sta ||
-+			    tmp_sta->mld_assoc_link_id !=
-+			    sta->mld_assoc_link_id ||
-+			    tmp_sta->aid != sta->aid)
- 				continue;
- 
--			for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
--			     tmp_sta = tmp_sta->next) {
--				if (tmp_sta == sta ||
--				    tmp_sta->mld_assoc_link_id !=
--				    sta->mld_assoc_link_id ||
--				    tmp_sta->aid != sta->aid)
--					continue;
--
--				ieee80211_ml_link_sta_assoc_cb(tmp_hapd,
--							       tmp_sta, link,
--							       ok);
--				break;
--			}
-+			ieee80211_ml_link_sta_assoc_cb(tmp_hapd,
-+						       tmp_sta, link,
-+						       ok);
-+			break;
- 		}
- 	}
- #endif /* CONFIG_IEEE80211BE */
-diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
-index 7365057ad..353a4116e 100644
---- a/src/ap/ieee802_11_eht.c
-+++ b/src/ap/ieee802_11_eht.c
-@@ -1029,7 +1029,7 @@ const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
- static int hostapd_mld_validate_assoc_info(struct hostapd_data *hapd,
- 					   struct sta_info *sta)
- {
--	u8 i, link_id;
-+	u8 link_id;
- 	struct mld_info *info = &sta->mld_info;
- 
- 	if (!ap_sta_is_mld(hapd, sta)) {
-@@ -1049,31 +1049,20 @@ static int hostapd_mld_validate_assoc_info(struct hostapd_data *hapd,
- 	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- 		struct hostapd_data *other_hapd;
- 
--		if (!info->links[link_id].valid)
-+		if (!info->links[link_id].valid || link_id == hapd->mld_link_id)
- 			continue;
- 
--		for (i = 0; i < hapd->iface->interfaces->count; i++) {
--			other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
--
--			if (hapd == other_hapd)
--				continue;
--
--			if (hostapd_is_ml_partner(hapd, other_hapd) &&
--			    link_id == other_hapd->mld_link_id)
--				break;
--		}
--
--		if (i == hapd->iface->interfaces->count &&
--		    link_id != hapd->mld_link_id) {
-+		other_hapd = hostapd_mld_get_link_bss(hapd, link_id);
-+		if (!other_hapd) {
- 			wpa_printf(MSG_DEBUG, "MLD: Invalid link ID=%u",
- 				   link_id);
- 			return -1;
- 		}
- 
--		if (i < hapd->iface->interfaces->count)
--			os_memcpy(info->links[link_id].local_addr,
--				  other_hapd->own_addr,
--				  ETH_ALEN);
-+		os_memcpy(info->links[link_id].local_addr,
-+			  other_hapd->own_addr,
-+			  ETH_ALEN);
-+
- 	}
- 
- 	return 0;
-diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
-index 012f2b803..d3cd44695 100644
---- a/src/ap/wpa_auth_glue.c
-+++ b/src/ap/wpa_auth_glue.c
-@@ -1537,7 +1537,7 @@ static int hostapd_wpa_auth_get_ml_rsn_info(void *ctx,
- 					    struct wpa_auth_ml_rsn_info *info)
- {
- 	struct hostapd_data *hapd = ctx;
--	unsigned int i, j;
-+	unsigned int i;
- 
- 	wpa_printf(MSG_DEBUG, "WPA_AUTH: MLD: Get RSN info CB: n_mld_links=%u",
- 		   info->n_mld_links);
-@@ -1547,26 +1547,33 @@ static int hostapd_wpa_auth_get_ml_rsn_info(void *ctx,
- 
- 	for (i = 0; i < info->n_mld_links; i++) {
- 		unsigned int link_id = info->links[i].link_id;
-+		struct hostapd_data *bss = NULL;
-+		bool link_bss_found = false;
- 
- 		wpa_printf(MSG_DEBUG,
- 			   "WPA_AUTH: MLD: Get link RSN CB: link_id=%u",
- 			   link_id);
- 
--		for (j = 0; j < hapd->iface->interfaces->count; j++) {
--			struct hostapd_iface *iface =
--				hapd->iface->interfaces->iface[j];
-+		if (hapd->mld_link_id == link_id) {
-+			wpa_auth_ml_get_rsn_info(hapd->wpa_auth,
-+						 &info->links[i]);
-+			continue;
-+		}
- 
--			if (!hostapd_is_ml_partner(hapd, iface->bss[0]) ||
--			    link_id != iface->bss[0]->mld_link_id ||
--			    !iface->bss[0]->wpa_auth)
-+		for_each_mld_link(bss, hapd) {
-+			if (bss == hapd)
- 				continue;
- 
--			wpa_auth_ml_get_rsn_info(iface->bss[0]->wpa_auth,
-+			if (bss->mld_link_id != link_id)
-+				continue;
-+
-+			wpa_auth_ml_get_rsn_info(bss->wpa_auth,
- 						 &info->links[i]);
-+			link_bss_found = true;
- 			break;
- 		}
- 
--		if (j == hapd->iface->interfaces->count)
-+		if (!link_bss_found)
- 			wpa_printf(MSG_DEBUG,
- 				   "WPA_AUTH: MLD: link=%u not found", link_id);
- 	}
-@@ -1579,7 +1586,7 @@ static int hostapd_wpa_auth_get_ml_key_info(void *ctx,
- 					    struct wpa_auth_ml_key_info *info)
- {
- 	struct hostapd_data *hapd = ctx;
--	unsigned int i, j;
-+	unsigned int i;
- 
- 	wpa_printf(MSG_DEBUG, "WPA_AUTH: MLD: Get key info CB: n_mld_links=%u",
- 		   info->n_mld_links);
-@@ -1588,29 +1595,38 @@ static int hostapd_wpa_auth_get_ml_key_info(void *ctx,
- 		return -1;
- 
- 	for (i = 0; i < info->n_mld_links; i++) {
-+		struct hostapd_data *bss = NULL;
- 		u8 link_id = info->links[i].link_id;
-+		bool link_bss_found = false;
- 
- 		wpa_printf(MSG_DEBUG,
- 			   "WPA_AUTH: MLD: Get link info CB: link_id=%u",
- 			   link_id);
- 
--		for (j = 0; j < hapd->iface->interfaces->count; j++) {
--			struct hostapd_iface *iface =
--				hapd->iface->interfaces->iface[j];
-+		if (hapd->mld_link_id == link_id) {
-+			wpa_auth_ml_get_key_info(hapd->wpa_auth,
-+						 &info->links[i],
-+						 info->mgmt_frame_prot,
-+						 info->beacon_prot);
-+			continue;
-+		}
-+
-+		for_each_mld_link(bss, hapd) {
-+			if (bss == hapd)
-+				continue;
- 
--			if (!hostapd_is_ml_partner(hapd, iface->bss[0]) ||
--			    link_id != iface->bss[0]->mld_link_id ||
--			    !iface->bss[0]->wpa_auth)
-+			if (bss->mld_link_id != link_id)
- 				continue;
- 
--			wpa_auth_ml_get_key_info(iface->bss[0]->wpa_auth,
-+			wpa_auth_ml_get_key_info(bss->wpa_auth,
- 						 &info->links[i],
- 						 info->mgmt_frame_prot,
- 						 info->beacon_prot);
-+			link_bss_found = true;
- 			break;
- 		}
- 
--		if (j == hapd->iface->interfaces->count)
-+		if (!link_bss_found)
- 			wpa_printf(MSG_DEBUG,
- 				   "WPA_AUTH: MLD: link=%u not found", link_id);
- 	}
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0008-hostapd-update-TPE-IE-according-to-AFC.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0008-hostapd-update-TPE-IE-according-to-AFC.patch
new file mode 100644
index 0000000..c6752f7
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0008-hostapd-update-TPE-IE-according-to-AFC.patch
@@ -0,0 +1,166 @@
+From 4e29b0943aeb681e09dcd0e59ae3532246f44c3d Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 17 May 2024 11:50:28 +0200
+Subject: [PATCH 008/126] hostapd: update TPE IE according to AFC
+
+Update Transmit Power Envelope (TPE) IE according to the reply from AFC
+coordinator on UNII-5 or UNII-7 6GHz bands.
+
+Tested-by: Felix Fietkau <nbd@nbd.name>
+Tested-by: Allen Ye <allen.ye@mediatek.com>
+Tested-by: Krishna Chaitanya <chaitanya.mgit@gmail.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ src/ap/afc.c        | 37 +++++++++++++++++++++++++++++++++++
+ src/ap/hostapd.h    |  9 +++++++++
+ src/ap/ieee802_11.c | 47 ++++++++++++++++++++++++++++-----------------
+ 3 files changed, 75 insertions(+), 18 deletions(-)
+
+diff --git a/src/ap/afc.c b/src/ap/afc.c
+index cfee83fe7..361ecb575 100644
+--- a/src/ap/afc.c
++++ b/src/ap/afc.c
+@@ -1039,3 +1039,40 @@ void hostap_afc_disable_channels(struct hostapd_iface *iface)
+ 			   chan->freq);
+ 	}
+ }
++
++
++int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
++				       int *power)
++{
++	int i;
++
++	if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type))
++		return -EINVAL;
++
++	if (!iface->afc.data_valid)
++		return -EINVAL;
++
++	if (psd) {
++		for (i = 0; i < iface->afc.num_freq_range; i++) {
++			struct afc_freq_range_elem *f;
++
++			f = &iface->afc.freq_range[i];
++			if (iface->freq >= f->low_freq &&
++			    iface->freq <= f->high_freq) {
++				*power = 2 * f->max_psd;
++				return 0;
++			}
++		}
++	} else {
++		for (i = 0; i < iface->afc.num_chan_info; i++) {
++			struct afc_chan_info_elem *c;
++
++			c = &iface->afc.chan_info_list[i];
++			if (c->chan == iface->conf->channel) {
++				*power = 2 * c->power;
++				return 0;
++			}
++		}
++	}
++	return -EINVAL;
++}
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index d67a0afa0..996977fdf 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -748,10 +748,19 @@ struct hostapd_iface {
+ 
+ /* hostapd.c */
+ #ifdef CONFIG_AFC
++int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
++				       int *power);
+ int hostapd_afc_handle_request(struct hostapd_iface *iface);
+ void hostapd_afc_stop(struct hostapd_iface *iface);
+ void hostap_afc_disable_channels(struct hostapd_iface *iface);
+ #else
++static inline int
++hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
++				   int *power)
++{
++	return -EINVAL;
++}
++
+ static inline int hostapd_afc_handle_request(struct hostapd_iface *iface)
+ {
+ 	return 1;
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index fd1de5ebc..d8d82d737 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7110,42 +7110,53 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
+ 	 */
+ 	if (is_6ghz_op_class(iconf->op_class)) {
+ 		enum max_tx_pwr_interpretation tx_pwr_intrpn;
++		int err, max_eirp_psd, max_eirp_power;
+ 
+ 		/* Same Maximum Transmit Power for all 20 MHz bands */
+ 		tx_pwr_count = 0;
+ 		tx_pwr_intrpn = REGULATORY_CLIENT_EIRP_PSD;
+ 
+ 		/* Default Transmit Power Envelope for Global Operating Class */
+-		if (hapd->iconf->reg_def_cli_eirp_psd != -1)
+-			tx_pwr = hapd->iconf->reg_def_cli_eirp_psd;
+-		else
+-			tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
++		err = hostap_afc_get_chan_max_eirp_power(iface, true,
++							 &max_eirp_psd);
++		if (err < 0) {
++			if (hapd->iconf->reg_def_cli_eirp_psd != -1)
++				max_eirp_psd = hapd->iconf->reg_def_cli_eirp_psd;
++			else
++				max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
++		}
+ 
+ 		eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn,
+-					   REG_DEFAULT_CLIENT, tx_pwr);
++					   REG_DEFAULT_CLIENT, max_eirp_psd);
+ 
+ 		/* Indoor Access Point must include an additional TPE for
+ 		 * subordinate devices */
+ 		if (he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type)) {
+-			/* TODO: Extract PSD limits from channel data */
+-			if (hapd->iconf->reg_sub_cli_eirp_psd != -1)
+-				tx_pwr = hapd->iconf->reg_sub_cli_eirp_psd;
+-			else
+-				tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
++			if (err < 0) {
++				/* non-AFC connection */
++				if (hapd->iconf->reg_sub_cli_eirp_psd != -1)
++					max_eirp_psd = hapd->iconf->reg_sub_cli_eirp_psd;
++				else
++					max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
++			}
+ 			eid = hostapd_add_tpe_info(eid, tx_pwr_count,
+ 						   tx_pwr_intrpn,
+ 						   REG_SUBORDINATE_CLIENT,
+-						   tx_pwr);
++						   max_eirp_psd);
+ 		}
+ 
+-		if (iconf->reg_def_cli_eirp != -1 &&
+-		    he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
+-			eid = hostapd_add_tpe_info(
+-				eid, tx_pwr_count, REGULATORY_CLIENT_EIRP,
+-				REG_DEFAULT_CLIENT,
+-				hapd->iconf->reg_def_cli_eirp);
++		if (hostap_afc_get_chan_max_eirp_power(iface, false,
++						       &max_eirp_power)) {
++			max_eirp_power = iconf->reg_def_cli_eirp;
++			if (max_eirp_power == -1 ||
++			    !he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
++				return eid;
++		}
+ 
+-		return eid;
++		return hostapd_add_tpe_info(eid, tx_pwr_count,
++					    REGULATORY_CLIENT_EIRP,
++					    REG_DEFAULT_CLIENT,
++					    max_eirp_power);
+ 	}
+ #endif /* CONFIG_IEEE80211AX */
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0009-hostapd-MLO-pass-link_id-in-get_hapd_bssid-helper-fu.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0009-hostapd-MLO-pass-link_id-in-get_hapd_bssid-helper-fu.patch
deleted file mode 100644
index 6b9fc79..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0009-hostapd-MLO-pass-link_id-in-get_hapd_bssid-helper-fu.patch
+++ /dev/null
@@ -1,120 +0,0 @@
-From 30cd94f678f5f85703854812f0deb6467b37df5f Mon Sep 17 00:00:00 2001
-From: Sriram R <quic_srirrama@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:39 +0530
-Subject: [PATCH 009/104] hostapd: MLO: pass link_id in get_hapd_bssid helper
- function
-
-Currently get_hapd_bssid() function matches the given bssid in all bsses
-of its own iface. However with MLO, there is requirement to check its
-own partner BSS at least.
-
-Make changes to compare its link partners as well and if link id passed
-matches with the link id of the partner then return it.
-
-Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/drv_callbacks.c | 47 +++++++++++++++++++++++++-----------------
- 1 file changed, 28 insertions(+), 19 deletions(-)
-
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index 2d3206909..adac2d478 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -1750,7 +1750,7 @@ switch_link_hapd(struct hostapd_data *hapd, int link_id)
- #define HAPD_BROADCAST ((struct hostapd_data *) -1)
- 
- static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
--					    const u8 *bssid)
-+					    const u8 *bssid, int link_id)
- {
- 	size_t i;
- 
-@@ -1761,8 +1761,30 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
- 		return HAPD_BROADCAST;
- 
- 	for (i = 0; i < iface->num_bss; i++) {
-+#ifdef CONFIG_IEEE80211BE
-+		struct hostapd_data *hapd, *p_hapd;
-+
-+		hapd = iface->bss[i];
-+		if (ether_addr_equal(bssid, hapd->own_addr) ||
-+		    (hapd->conf->mld_ap &&
-+		     ether_addr_equal(bssid, hapd->mld->mld_addr) &&
-+		     link_id == hapd->mld_link_id)) {
-+			return hapd;
-+		} else if (hapd->conf->mld_ap) {
-+			for_each_mld_link(p_hapd, hapd) {
-+				if (p_hapd == hapd)
-+					continue;
-+
-+				if (ether_addr_equal(bssid, p_hapd->own_addr) ||
-+				    (ether_addr_equal(bssid, p_hapd->mld->mld_addr) &&
-+				     link_id == p_hapd->mld_link_id))
-+					return p_hapd;
-+			}
-+		}
-+#else
- 		if (ether_addr_equal(bssid, iface->bss[i]->own_addr))
- 			return iface->bss[i];
-+#endif /*CONFIG_IEEE80211BE */
- 	}
- 
- 	return NULL;
-@@ -1773,7 +1795,7 @@ static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd,
- 					const u8 *bssid, const u8 *addr,
- 					int wds)
- {
--	hapd = get_hapd_bssid(hapd->iface, bssid);
-+	hapd = get_hapd_bssid(hapd->iface, bssid, -1);
- 	if (hapd == NULL || hapd == HAPD_BROADCAST)
- 		return;
- 
-@@ -1813,14 +1835,7 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
- 	if (bssid == NULL)
- 		return 0;
- 
--#ifdef CONFIG_IEEE80211BE
--	if (hapd->conf->mld_ap &&
--	    ether_addr_equal(hapd->mld->mld_addr, bssid))
--		is_mld = true;
--#endif /* CONFIG_IEEE80211BE */
--
--	if (!is_mld)
--		hapd = get_hapd_bssid(iface, bssid);
-+	hapd = get_hapd_bssid(iface, bssid, rx_mgmt->link_id);
- 
- 	if (!hapd) {
- 		u16 fc = le_to_host16(hdr->frame_control);
-@@ -1872,17 +1887,11 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
- 	struct ieee80211_hdr *hdr;
- 	struct hostapd_data *orig_hapd, *tmp_hapd;
- 
--#ifdef CONFIG_IEEE80211BE
--	if (hapd->conf->mld_ap && link_id != -1) {
--		tmp_hapd = hostapd_mld_get_link_bss(hapd, link_id);
--		if (tmp_hapd)
--			hapd = tmp_hapd;
--	}
--#endif /* CONFIG_IEEE80211BE */
- 	orig_hapd = hapd;
- 
- 	hdr = (struct ieee80211_hdr *) buf;
--	tmp_hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
-+	hapd = switch_link_hapd(hapd, link_id);
-+	tmp_hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len), link_id);
- 	if (tmp_hapd) {
- 		hapd = tmp_hapd;
- #ifdef CONFIG_IEEE80211BE
-@@ -1899,7 +1908,7 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
- 		if (stype != WLAN_FC_STYPE_ACTION || len <= 25 ||
- 		    buf[24] != WLAN_ACTION_PUBLIC)
- 			return;
--		hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2);
-+		hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2, link_id);
- 		if (!hapd || hapd == HAPD_BROADCAST)
- 			return;
- 		/*
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0009-sync-2024-06-21-openwrt-trunk-src-folder.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0009-sync-2024-06-21-openwrt-trunk-src-folder.patch
new file mode 100644
index 0000000..66a6d8c
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0009-sync-2024-06-21-openwrt-trunk-src-folder.patch
@@ -0,0 +1,5140 @@
+From 2acddad81c876fda04bfd94e7d45dca4bc72a843 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 19 Jul 2024 15:23:02 +0800
+Subject: [PATCH 009/126] sync 2024-06-21 openwrt/trunk src folder
+
+Sync to 032d3fcf7a861b140435b6507b2b0b66361c92f8
+"hostapd: use strdup on string passed to hostapd_add_iface"
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ hostapd/radius.c           |  715 +++++++++++++
+ src/ap/ubus.c              | 2006 ++++++++++++++++++++++++++++++++++++
+ src/ap/ubus.h              |  157 +++
+ src/ap/ucode.c             |  817 +++++++++++++++
+ src/ap/ucode.h             |   54 +
+ src/utils/build_features.h |   65 ++
+ src/utils/ucode.c          |  502 +++++++++
+ src/utils/ucode.h          |   30 +
+ wpa_supplicant/ubus.c      |  280 +++++
+ wpa_supplicant/ubus.h      |   55 +
+ wpa_supplicant/ucode.c     |  299 ++++++
+ wpa_supplicant/ucode.h     |   49 +
+ 12 files changed, 5029 insertions(+)
+ create mode 100644 hostapd/radius.c
+ create mode 100644 src/ap/ubus.c
+ create mode 100644 src/ap/ubus.h
+ create mode 100644 src/ap/ucode.c
+ create mode 100644 src/ap/ucode.h
+ create mode 100644 src/utils/build_features.h
+ create mode 100644 src/utils/ucode.c
+ create mode 100644 src/utils/ucode.h
+ create mode 100644 wpa_supplicant/ubus.c
+ create mode 100644 wpa_supplicant/ubus.h
+ create mode 100644 wpa_supplicant/ucode.c
+ create mode 100644 wpa_supplicant/ucode.h
+
+diff --git a/hostapd/radius.c b/hostapd/radius.c
+new file mode 100644
+index 000000000..362a22c27
+--- /dev/null
++++ b/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/src/ap/ubus.c b/src/ap/ubus.c
+new file mode 100644
+index 000000000..8689494bc
+--- /dev/null
++++ b/src/ap/ubus.c
+@@ -0,0 +1,2006 @@
++/*
++ * 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);
++}
++
++
++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,
++				hostapd_get_punct_bitmap(hapd));
++
++	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;
++
++	if (!ctx)
++		return;
++
++	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/src/ap/ubus.h b/src/ap/ubus.h
+new file mode 100644
+index 000000000..22767d67e
+--- /dev/null
++++ b/src/ap/ubus.h
+@@ -0,0 +1,157 @@
++/*
++ * 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
++
++#include "sta_info.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;
++struct sta_info;
++
++#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/src/ap/ucode.c b/src/ap/ucode.c
+new file mode 100644
+index 000000000..68fb45088
+--- /dev/null
++++ b/src/ap/ucode.c
+@@ -0,0 +1,817 @@
++#include <sys/un.h>
++
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/ucode.h"
++#include "hostapd.h"
++#include "beacon.h"
++#include "hw_features.h"
++#include "ap_drv_ops.h"
++#include "dfs.h"
++#include "acs.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);
++	hapd->ucode.idx = wpa_ucode_registry_add(bss_registry, val);
++
++	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);
++	hapd->ucode.idx = wpa_ucode_registry_add(iface_registry, val);
++
++	return val;
++}
++
++static void
++hostapd_ucode_update_bss_list(struct hostapd_iface *iface, uc_value_t *if_bss, uc_value_t *bss)
++{
++	uc_value_t *list;
++	int i;
++
++	list = ucv_array_new(vm);
++	for (i = 0; iface->bss && i < iface->num_bss; i++) {
++		struct hostapd_data *hapd = iface->bss[i];
++		uc_value_t *val = hostapd_ucode_bss_get_uval(hapd);
++
++		ucv_array_set(list, i, ucv_get(ucv_string_new(hapd->conf->iface)));
++		ucv_object_add(bss, hapd->conf->iface, ucv_get(val));
++	}
++	ucv_object_add(if_bss, iface->phy, ucv_get(list));
++}
++
++static void
++hostapd_ucode_update_interfaces(void)
++{
++	uc_value_t *ifs = ucv_object_new(vm);
++	uc_value_t *if_bss = ucv_array_new(vm);
++	uc_value_t *bss = 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, if_bss, bss);
++	}
++
++	ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
++	ucv_object_add(ucv_prototype_get(global), "interface_bss", ucv_get(if_bss));
++	ucv_object_add(ucv_prototype_get(global), "bss", ucv_get(bss));
++	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);
++	char *data;
++	int ret;
++
++	if (ucv_type(iface) != UC_STRING)
++		return ucv_int64_new(-1);
++
++	data = strdup(ucv_string_get(iface));
++	ret = hostapd_add_iface(interfaces, data);
++	free(data);
++
++	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 struct hostapd_vlan *
++bss_conf_find_vlan(struct hostapd_bss_config *bss, int id)
++{
++	struct hostapd_vlan *vlan;
++
++	for (vlan = bss->vlan; vlan; vlan = vlan->next)
++		if (vlan->vlan_id == id)
++			return vlan;
++
++	return NULL;
++}
++
++static int
++bss_conf_rename_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
++		     const char *ifname)
++{
++	if (!strcmp(ifname, vlan->ifname))
++		return 0;
++
++	hostapd_drv_if_rename(hapd, WPA_IF_AP_VLAN, vlan->ifname, ifname);
++	os_strlcpy(vlan->ifname, ifname, sizeof(vlan->ifname));
++
++	return 0;
++}
++
++static int
++bss_reload_vlans(struct hostapd_data *hapd, struct hostapd_bss_config *bss)
++{
++	struct hostapd_bss_config *old_bss = hapd->conf;
++	struct hostapd_vlan *vlan, *vlan_new, *wildcard;
++	char ifname[IFNAMSIZ + 1], vlan_ifname[IFNAMSIZ + 1], *pos;
++	int ret;
++
++	vlan = bss_conf_find_vlan(old_bss, VLAN_ID_WILDCARD);
++	wildcard = bss_conf_find_vlan(bss, VLAN_ID_WILDCARD);
++	if (!!vlan != !!wildcard)
++		return -1;
++
++	if (vlan && wildcard && strcmp(vlan->ifname, wildcard->ifname) != 0)
++		strcpy(vlan->ifname, wildcard->ifname);
++	else
++		wildcard = NULL;
++
++	for (vlan = bss->vlan; vlan; vlan = vlan->next) {
++		if (vlan->vlan_id == VLAN_ID_WILDCARD ||
++		    vlan->dynamic_vlan > 0)
++			continue;
++
++		if (!bss_conf_find_vlan(old_bss, vlan->vlan_id))
++			return -1;
++	}
++
++	for (vlan = old_bss->vlan; vlan; vlan = vlan->next) {
++		if (vlan->vlan_id == VLAN_ID_WILDCARD)
++			continue;
++
++		if (vlan->dynamic_vlan == 0) {
++			vlan_new = bss_conf_find_vlan(bss, vlan->vlan_id);
++			if (!vlan_new)
++				return -1;
++
++			if (bss_conf_rename_vlan(hapd, vlan, vlan_new->ifname))
++				return -1;
++
++			continue;
++		}
++
++		if (!wildcard)
++			continue;
++
++		os_strlcpy(ifname, wildcard->ifname, sizeof(ifname));
++		pos = os_strchr(ifname, '#');
++		if (!pos)
++			return -1;
++
++		*pos++ = '\0';
++		ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s%d%s",
++				  ifname, vlan->vlan_id, pos);
++	        if (os_snprintf_error(sizeof(vlan_ifname), ret))
++			return -1;
++
++		if (bss_conf_rename_vlan(hapd, vlan, vlan_ifname))
++			return -1;
++	}
++
++	return 0;
++}
++
++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);
++	uc_value_t *files_only = uc_fn_arg(2);
++	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)
++		goto out;
++
++	if (idx > conf->num_bss || !conf->bss[idx])
++		goto free;
++
++	if (ucv_boolean_get(files_only)) {
++		struct hostapd_bss_config *bss = conf->bss[idx];
++		struct hostapd_bss_config *old_bss = hapd->conf;
++
++#define swap_field(name)				\
++	do {								\
++		void *ptr = old_bss->name;		\
++		old_bss->name = bss->name;		\
++		bss->name = ptr;				\
++	} while (0)
++
++		swap_field(ssid.wpa_psk_file);
++		ret = bss_reload_vlans(hapd, bss);
++		goto done;
++	}
++
++	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_setup_bss(hapd, hapd == iface->bss[0], true);
++	hostapd_ucode_update_interfaces();
++
++done:
++	ret = 0;
++free:
++	hostapd_config_free(conf);
++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)
++		return NULL;
++
++	iface = hapd->iface;
++	if (iface->num_bss == 1) {
++		wpa_printf(MSG_ERROR, "trying to delete last bss of an iface: %s\n", hapd->conf->iface);
++		return NULL;
++	}
++
++	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--;
++
++	iface->bss[0]->interface_added = 0;
++	hostapd_drv_set_first_bss(iface->bss[0]);
++	hapd->interface_added = 1;
++
++	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_interfaces();
++	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_interfaces();
++	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_iface_set_bss_order(uc_vm_t *vm, size_t nargs)
++{
++	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++	uc_value_t *bss_list = uc_fn_arg(0);
++	struct hostapd_data **new_bss;
++	struct hostapd_bss_config **new_conf;
++
++	if (!iface)
++		return NULL;
++
++	if (ucv_type(bss_list) != UC_ARRAY ||
++	    ucv_array_length(bss_list) != iface->num_bss)
++		return NULL;
++
++	new_bss = calloc(iface->num_bss, sizeof(*new_bss));
++	new_conf = calloc(iface->num_bss, sizeof(*new_conf));
++	for (size_t i = 0; i < iface->num_bss; i++) {
++		struct hostapd_data *bss;
++
++		bss = ucv_resource_data(ucv_array_get(bss_list, i), "hostapd.bss");
++		if (bss->iface != iface)
++			goto free;
++
++		for (size_t k = 0; k < i; k++)
++			if (new_bss[k] == bss)
++				goto free;
++
++		new_bss[i] = bss;
++		new_conf[i] = bss->conf;
++	}
++
++	new_bss[0]->interface_added = 0;
++	for (size_t i = 1; i < iface->num_bss; i++)
++		new_bss[i]->interface_added = 1;
++
++	free(iface->bss);
++	iface->bss = new_bss;
++
++	free(iface->conf->bss);
++	iface->conf->bss = new_conf;
++	iface->conf->num_bss = iface->num_bss;
++	hostapd_drv_set_first_bss(iface->bss[0]);
++
++	return ucv_boolean_new(true);
++
++free:
++	free(new_bss);
++	free(new_conf);
++	return NULL;
++}
++
++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);
++}
++
++static void
++uc_hostapd_disable_iface(struct hostapd_iface *iface)
++{
++	switch (iface->state) {
++	case HAPD_IFACE_DISABLED:
++		break;
++#ifdef CONFIG_ACS
++	case HAPD_IFACE_ACS:
++		acs_cleanup(iface);
++		iface->scan_cb = NULL;
++		/* fallthrough */
++#endif
++	default:
++		hostapd_disable_iface(iface);
++		break;
++	}
++}
++
++static uc_value_t *
++uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
++{
++	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++	int i;
++
++	if (!iface)
++		return NULL;
++
++	if (iface->state != HAPD_IFACE_ENABLED)
++		uc_hostapd_disable_iface(iface);
++
++	for (i = 0; i < iface->num_bss; i++) {
++		struct hostapd_data *hapd = iface->bss[i];
++
++		hostapd_drv_stop_ap(hapd);
++		hapd->beacon_set_done = 0;
++	}
++
++	return NULL;
++}
++
++static uc_value_t *
++uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
++{
++	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++	uc_value_t *info = uc_fn_arg(0);
++	struct hostapd_config *conf;
++	bool changed = false;
++	uint64_t intval;
++	int i;
++
++	if (!iface)
++		return NULL;
++
++	if (!info) {
++		iface->freq = 0;
++		goto out;
++	}
++
++	if (ucv_type(info) != UC_OBJECT)
++		return NULL;
++
++#define UPDATE_VAL(field, name)							\
++	if ((intval = ucv_int64_get(ucv_object_get(info, name, NULL))) &&	\
++		!errno && intval != conf->field) do {				\
++		conf->field = intval;						\
++		changed = true;							\
++	} while(0)
++
++	conf = iface->conf;
++	UPDATE_VAL(op_class, "op_class");
++	UPDATE_VAL(hw_mode, "hw_mode");
++	UPDATE_VAL(channel, "channel");
++	UPDATE_VAL(secondary_channel, "sec_channel");
++	if (!changed &&
++	    (iface->bss[0]->beacon_set_done ||
++	     iface->state == HAPD_IFACE_DFS))
++		return ucv_boolean_new(true);
++
++	intval = ucv_int64_get(ucv_object_get(info, "center_seg0_idx", NULL));
++	if (!errno)
++		hostapd_set_oper_centr_freq_seg0_idx(conf, intval);
++
++	intval = ucv_int64_get(ucv_object_get(info, "center_seg1_idx", NULL));
++	if (!errno)
++		hostapd_set_oper_centr_freq_seg1_idx(conf, intval);
++
++	intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
++	if (!errno)
++		hostapd_set_oper_chwidth(conf, intval);
++
++	intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL));
++	if (!errno)
++		iface->freq = intval;
++	else
++		iface->freq = 0;
++	conf->acs = 0;
++
++out:
++	switch (iface->state) {
++	case HAPD_IFACE_ENABLED:
++		if (!hostapd_is_dfs_required(iface) ||
++			hostapd_is_dfs_chan_available(iface))
++			break;
++		wpa_printf(MSG_INFO, "DFS CAC required on new channel, restart interface");
++		/* fallthrough */
++	default:
++		uc_hostapd_disable_iface(iface);
++		break;
++	}
++
++	if (conf->channel && !iface->freq)
++		iface->freq = hostapd_hw_get_freq(iface->bss[0], conf->channel);
++
++	if (iface->state != HAPD_IFACE_ENABLED) {
++		hostapd_enable_iface(iface);
++		return ucv_boolean_new(true);
++	}
++
++	for (i = 0; i < iface->num_bss; i++) {
++		struct hostapd_data *hapd = iface->bss[i];
++		int ret;
++
++		hapd->conf->start_disabled = 0;
++		hostapd_set_freq(hapd, conf->hw_mode, iface->freq,
++				 conf->channel,
++				 conf->enable_edmg,
++				 conf->edmg_channel,
++				 conf->ieee80211n,
++				 conf->ieee80211ac,
++				 conf->ieee80211ax,
++				 conf->ieee80211be,
++				 conf->secondary_channel,
++				 hostapd_get_oper_chwidth(conf),
++				 hostapd_get_oper_centr_freq_seg0_idx(conf),
++				 hostapd_get_oper_centr_freq_seg1_idx(conf));
++
++		ieee802_11_set_beacon(hapd);
++	}
++
++	return ucv_boolean_new(true);
++}
++
++static uc_value_t *
++uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
++{
++	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++	uc_value_t *info = uc_fn_arg(0);
++	struct hostapd_config *conf;
++	struct csa_settings csa = {};
++	uint64_t intval;
++	int i, ret = 0;
++
++	if (!iface || ucv_type(info) != UC_OBJECT)
++		return NULL;
++
++	conf = iface->conf;
++	if ((intval = ucv_int64_get(ucv_object_get(info, "csa_count", NULL))) && !errno)
++		csa.cs_count = intval;
++	if ((intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL))) && !errno)
++		csa.freq_params.sec_channel_offset = intval;
++
++	csa.freq_params.ht_enabled = conf->ieee80211n;
++	csa.freq_params.vht_enabled = conf->ieee80211ac;
++	csa.freq_params.he_enabled = conf->ieee80211ax;
++#ifdef CONFIG_IEEE80211BE
++	csa.freq_params.eht_enabled = conf->ieee80211be;
++#endif
++	intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
++	if (errno)
++		intval = hostapd_get_oper_chwidth(conf);
++	if (intval)
++		csa.freq_params.bandwidth = 40 << intval;
++	else
++		csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20;
++
++	if ((intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL))) && !errno)
++		csa.freq_params.freq = intval;
++	if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq1", NULL))) && !errno)
++		csa.freq_params.center_freq1 = intval;
++	if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
++		csa.freq_params.center_freq2 = intval;
++
++	for (i = 0; i < iface->num_bss; i++)
++		ret = hostapd_switch_channel(iface->bss[i], &csa);
++
++	return ucv_boolean_new(!ret);
++}
++
++static uc_value_t *
++uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
++{
++	struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
++	uc_value_t *ifname_arg = uc_fn_arg(0);
++	char prev_ifname[IFNAMSIZ + 1];
++	struct sta_info *sta;
++	const char *ifname;
++	int ret;
++
++	if (!hapd || ucv_type(ifname_arg) != UC_STRING)
++		return NULL;
++
++	os_strlcpy(prev_ifname, hapd->conf->iface, sizeof(prev_ifname));
++	ifname = ucv_string_get(ifname_arg);
++
++	hostapd_ubus_free_bss(hapd);
++	if (interfaces->ctrl_iface_deinit)
++		interfaces->ctrl_iface_deinit(hapd);
++
++	ret = hostapd_drv_if_rename(hapd, WPA_IF_AP_BSS, NULL, ifname);
++	if (ret)
++		goto out;
++
++	for (sta = hapd->sta_list; sta; sta = sta->next) {
++		char cur_name[IFNAMSIZ + 1], new_name[IFNAMSIZ + 1];
++
++		if (!(sta->flags & WLAN_STA_WDS) || sta->pending_wds_enable)
++			continue;
++
++		snprintf(cur_name, sizeof(cur_name), "%s.sta%d", prev_ifname, sta->aid);
++		snprintf(new_name, sizeof(new_name), "%s.sta%d", ifname, sta->aid);
++		hostapd_drv_if_rename(hapd, WPA_IF_AP_VLAN, cur_name, new_name);
++	}
++
++	if (!strncmp(hapd->conf->ssid.vlan, hapd->conf->iface, sizeof(hapd->conf->ssid.vlan)))
++		os_strlcpy(hapd->conf->ssid.vlan, ifname, sizeof(hapd->conf->ssid.vlan));
++	os_strlcpy(hapd->conf->iface, ifname, sizeof(hapd->conf->iface));
++	hostapd_ubus_add_bss(hapd);
++
++	hostapd_ucode_update_interfaces();
++out:
++	if (interfaces->ctrl_iface_init)
++		interfaces->ctrl_iface_init(hapd);
++
++	return ret ? NULL : ucv_boolean_new(true);
++}
++
++
++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 },
++		{ "freq_info", uc_wpa_freq_info },
++		{ "add_iface", uc_hostapd_add_iface },
++		{ "remove_iface", uc_hostapd_remove_iface },
++		{ "udebug_set", uc_wpa_udebug_set },
++	};
++	static const uc_function_list_t bss_fns[] = {
++		{ "ctrl", uc_hostapd_bss_ctrl },
++		{ "set_config", uc_hostapd_bss_set_config },
++		{ "rename", uc_hostapd_bss_rename },
++		{ "delete", uc_hostapd_bss_delete },
++	};
++	static const uc_function_list_t iface_fns[] = {
++		{ "set_bss_order", uc_hostapd_iface_set_bss_order },
++		{ "add_bss", uc_hostapd_iface_add_bss },
++		{ "stop", uc_hostapd_iface_stop },
++		{ "start", uc_hostapd_iface_start },
++		{ "switch_channel", uc_hostapd_iface_switch_channel },
++	};
++	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)
++{
++	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));
++	ucv_put(wpa_ucode_call(2));
++	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/src/ap/ucode.h b/src/ap/ucode.h
+new file mode 100644
+index 000000000..d00b78716
+--- /dev/null
++++ b/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);
++
++#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)
++{
++}
++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/src/utils/build_features.h b/src/utils/build_features.h
+new file mode 100644
+index 000000000..553769ece
+--- /dev/null
++++ b/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/src/utils/ucode.c b/src/utils/ucode.c
+new file mode 100644
+index 000000000..29c753c32
+--- /dev/null
++++ b/src/utils/ucode.c
+@@ -0,0 +1,502 @@
++#include <unistd.h>
++#include "ucode.h"
++#include "utils/eloop.h"
++#include "crypto/crypto.h"
++#include "crypto/sha1.h"
++#include "common/ieee802_11_common.h"
++#include <linux/netlink.h>
++#include <linux/genetlink.h>
++#include <linux/nl80211.h>
++#include <libubox/uloop.h>
++#include <ucode/compiler.h>
++#include <udebug.h>
++
++static uc_value_t *registry;
++static uc_vm_t vm;
++static struct uloop_timeout gc_timer;
++static struct udebug ud;
++static struct udebug_buf ud_log, ud_nl[3];
++static const struct udebug_buf_meta meta_log = {
++	.name = "wpa_log",
++	.format = UDEBUG_FORMAT_STRING,
++};
++static const struct udebug_buf_meta meta_nl_ll = {
++	.name = "wpa_nl_ctrl",
++	.format = UDEBUG_FORMAT_PACKET,
++	.sub_format = UDEBUG_DLT_NETLINK,
++};
++static const struct udebug_buf_meta meta_nl_tx = {
++	.name = "wpa_nl_tx",
++	.format = UDEBUG_FORMAT_PACKET,
++	.sub_format = UDEBUG_DLT_NETLINK,
++};
++#define UDEBUG_FLAG_RX_FRAME	(1ULL << 0)
++static const struct udebug_buf_flag rx_flags[] = {
++	{  "rx_frame", UDEBUG_FLAG_RX_FRAME },
++};
++static const struct udebug_buf_meta meta_nl_rx = {
++	.name = "wpa_nl_rx",
++	.format = UDEBUG_FORMAT_PACKET,
++	.sub_format = UDEBUG_DLT_NETLINK,
++	.flags = rx_flags,
++	.n_flags = ARRAY_SIZE(rx_flags),
++};
++static struct udebug_ubus_ring udebug_rings[] = {
++	{
++		.buf = &ud_log,
++		.meta = &meta_log,
++		.default_entries = 1024,
++		.default_size = 64 * 1024
++	},
++	{
++		.buf = &ud_nl[0],
++		.meta = &meta_nl_rx,
++		.default_entries = 1024,
++		.default_size = 256 * 1024,
++	},
++	{
++		.buf = &ud_nl[1],
++		.meta = &meta_nl_tx,
++		.default_entries = 1024,
++		.default_size = 64 * 1024,
++	},
++	{
++		.buf = &ud_nl[2],
++		.meta = &meta_nl_ll,
++		.default_entries = 1024,
++		.default_size = 32 * 1024,
++	}
++};
++char *udebug_service;
++struct udebug_ubus ud_ubus;
++
++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_freq_info(uc_vm_t *vm, size_t nargs)
++{
++	uc_value_t *freq = uc_fn_arg(0);
++	uc_value_t *sec = uc_fn_arg(1);
++	int width = ucv_uint64_get(uc_fn_arg(2));
++	int freq_val, center_idx, center_ofs;
++	enum oper_chan_width chanwidth;
++	enum hostapd_hw_mode hw_mode;
++	u8 op_class, channel, tmp_channel;
++	const char *modestr;
++	int sec_channel = 0;
++	uc_value_t *ret;
++
++	if (ucv_type(freq) != UC_INTEGER)
++		return NULL;
++
++	freq_val = ucv_int64_get(freq);
++	if (ucv_type(sec) == UC_INTEGER)
++		sec_channel = ucv_int64_get(sec);
++	else if (sec)
++		return NULL;
++	else if (freq_val > 4000)
++		sec_channel = (freq_val / 20) & 1 ? 1 : -1;
++	else
++		sec_channel = freq_val < 2442 ? 1 : -1;
++
++	if (sec_channel != -1 && sec_channel != 1 && sec_channel != 0)
++		return NULL;
++
++	switch (width) {
++	case 0:
++		chanwidth = CONF_OPER_CHWIDTH_USE_HT;
++		break;
++	case 1:
++		chanwidth = CONF_OPER_CHWIDTH_80MHZ;
++		break;
++	case 2:
++		chanwidth = CONF_OPER_CHWIDTH_160MHZ;
++		break;
++	default:
++		return NULL;
++	}
++
++	hw_mode = ieee80211_freq_to_channel_ext(freq_val, sec_channel,
++						chanwidth, &op_class, &channel);
++	switch (hw_mode) {
++	case HOSTAPD_MODE_IEEE80211B:
++		modestr = "b";
++		break;
++	case HOSTAPD_MODE_IEEE80211G:
++		modestr = "g";
++		break;
++	case HOSTAPD_MODE_IEEE80211A:
++		modestr = "a";
++		break;
++	case HOSTAPD_MODE_IEEE80211AD:
++		modestr = "ad";
++		break;
++	default:
++		return NULL;
++	}
++
++	ret = ucv_object_new(vm);
++	ucv_object_add(ret, "op_class", ucv_int64_new(op_class));
++	ucv_object_add(ret, "channel", ucv_int64_new(channel));
++	ucv_object_add(ret, "hw_mode", ucv_int64_new(hw_mode));
++	ucv_object_add(ret, "hw_mode_str", ucv_get(ucv_string_new(modestr)));
++	ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
++	ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
++
++	if (!sec_channel)
++		return ret;
++
++	if (freq_val >= 5900)
++		center_ofs = 0;
++	else if (freq_val >= 5745)
++		center_ofs = 20;
++	else
++		center_ofs = 35;
++	tmp_channel = channel - center_ofs;
++	tmp_channel &= ~((8 << width) - 1);
++	center_idx = tmp_channel + center_ofs + (4 << width) - 1;
++
++	if (freq_val < 3000)
++		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(0));
++	else
++		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(center_idx));
++	center_idx = (center_idx - channel) * 5 + freq_val;
++	ucv_object_add(ret, "center_freq1", ucv_int64_new(center_idx));
++
++out:
++	return ret;
++}
++
++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;
++}
++
++static void udebug_printf_hook(int level, const char *fmt, va_list ap)
++{
++	udebug_entry_init(&ud_log);
++	udebug_entry_vprintf(&ud_log, fmt, ap);
++	udebug_entry_add(&ud_log);
++}
++
++static void udebug_hexdump_hook(int level, const char *title,
++                const void *data, size_t len)
++{
++	char *buf;
++
++	udebug_entry_init(&ud_log);
++	udebug_entry_printf(&ud_log, "%s - hexdump:", title);
++	buf = udebug_entry_append(&ud_log, NULL, 3 * len);
++	for (size_t i = 0; i < len; i++)
++		buf += sprintf(buf, " %02x", *(uint8_t *)(data + i));
++	udebug_entry_add(&ud_log);
++}
++
++static void udebug_netlink_hook(int tx, const void *data, size_t len)
++{
++	struct {
++		uint16_t pkttype;
++		uint16_t arphdr;
++		uint16_t _pad[5];
++		uint16_t proto;
++	} hdr = {
++		.pkttype = host_to_be16(tx ? 7 : 6),
++		.arphdr = host_to_be16(824),
++		.proto = host_to_be16(16),
++	};
++	const struct nlmsghdr *nlh = data;
++	const struct genlmsghdr *gnlh = data + NLMSG_HDRLEN;
++	struct udebug_buf *buf = &ud_nl[!!tx];
++
++	if (nlh->nlmsg_type == 0x10)
++		buf = &ud_nl[2];
++	else if (!tx && gnlh->cmd == NL80211_CMD_FRAME &&
++	         !(udebug_buf_flags(buf) & UDEBUG_FLAG_RX_FRAME))
++		return;
++
++	if (!udebug_buf_valid(buf))
++		return;
++
++	udebug_entry_init(buf);
++	udebug_entry_append(buf, &hdr, sizeof(hdr));
++	udebug_entry_append(buf, data, len);
++	udebug_entry_add(buf);
++}
++
++static void
++wpa_udebug_config(struct udebug_ubus *ctx, struct blob_attr *data,
++		  bool enabled)
++{
++	udebug_ubus_apply_config(&ud, udebug_rings, ARRAY_SIZE(udebug_rings),
++				 data, enabled);
++
++	if (udebug_buf_valid(&ud_log)) {
++		wpa_printf_hook = udebug_printf_hook;
++		wpa_hexdump_hook = udebug_hexdump_hook;
++	} else {
++		wpa_printf_hook = NULL;
++		wpa_hexdump_hook = NULL;
++	}
++
++	if (udebug_buf_valid(&ud_nl[0]) ||
++	    udebug_buf_valid(&ud_nl[1]) ||
++	    udebug_buf_valid(&ud_nl[2]))
++		wpa_netlink_hook = udebug_netlink_hook;
++	else
++		wpa_netlink_hook = NULL;
++}
++
++uc_value_t *uc_wpa_udebug_set(uc_vm_t *vm, size_t nargs)
++{
++	uc_value_t *name = uc_fn_arg(0);
++	uc_value_t *ubus = uc_fn_arg(1);
++	static bool enabled = false;
++	struct ubus_context *ctx;
++	bool cur_en;
++
++	cur_en = ucv_type(name) == UC_STRING;
++	ctx = ucv_resource_data(ubus, "ubus.connection");
++	if (!ctx)
++		cur_en = false;
++
++	if (enabled == cur_en)
++		return ucv_boolean_new(true);
++
++	enabled = cur_en;
++	if (enabled) {
++		udebug_service = strdup(ucv_string_get(name));
++		udebug_init(&ud);
++		udebug_auto_connect(&ud, NULL);
++		udebug_ubus_init(&ud_ubus, ctx, udebug_service, wpa_udebug_config);
++	} else {
++		udebug_ubus_free(&ud_ubus);
++		for (size_t i = 0; i < ARRAY_SIZE(udebug_rings); i++)
++			if (udebug_buf_valid(udebug_rings[i].buf))
++				udebug_buf_free(udebug_rings[i].buf);
++		udebug_free(&ud);
++		free(udebug_service);
++	}
++
++	return ucv_boolean_new(true);
++}
++
++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;
++}
++
++int wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val)
++{
++	uc_value_t *data;
++	int i = 0;
++
++	while (ucv_array_get(reg, i))
++		i++;
++
++	ucv_array_set(reg, i, ucv_get(val));
++
++	return 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);
++	void **dataptr;
++
++	if (!val)
++		return NULL;
++
++	ucv_array_set(reg, idx - 1, NULL);
++	dataptr = ucv_resource_dataptr(val, NULL);
++	if (dataptr)
++		*dataptr = 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/src/utils/ucode.h b/src/utils/ucode.h
+new file mode 100644
+index 000000000..c083241e0
+--- /dev/null
++++ b/src/utils/ucode.h
+@@ -0,0 +1,30 @@
++#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);
++
++int wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val);
++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_udebug_set(uc_vm_t *vm, size_t nargs);
++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);
++uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs);
++
++#endif
+diff --git a/wpa_supplicant/ubus.c b/wpa_supplicant/ubus.c
+new file mode 100644
+index 000000000..1c477f0c0
+--- /dev/null
++++ b/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/wpa_supplicant/ubus.h b/wpa_supplicant/ubus.h
+new file mode 100644
+index 000000000..f6681cb26
+--- /dev/null
++++ b/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/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+new file mode 100644
+index 000000000..397f85bde
+--- /dev/null
++++ b/wpa_supplicant/ucode.c
+@@ -0,0 +1,299 @@
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/ucode.h"
++#include "drivers/driver.h"
++#include "ap/hostapd.h"
++#include "wpa_supplicant_i.h"
++#include "wps_supplicant.h"
++#include "bss.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_s->ucode.idx = wpa_ucode_registry_add(iface_registry, val);
++
++	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);
++}
++
++void wpas_ucode_update_state(struct wpa_supplicant *wpa_s)
++{
++	const char *state;
++	uc_value_t *val;
++
++	val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
++	if (!val)
++		return;
++
++	if (wpa_ucode_call_prepare("state"))
++		return;
++
++	state = wpa_supplicant_state_txt(wpa_s->wpa_state);
++	uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
++	uc_value_push(ucv_get(val));
++	uc_value_push(ucv_get(ucv_string_new(state)));
++	ucv_put(wpa_ucode_call(3));
++	ucv_gc(vm);
++}
++
++void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data)
++{
++	const char *state;
++	uc_value_t *val;
++
++	if (event != EVENT_CH_SWITCH_STARTED)
++		return;
++
++	val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
++	if (!val)
++		return;
++
++	if (wpa_ucode_call_prepare("event"))
++		return;
++
++	uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
++	uc_value_push(ucv_get(val));
++	uc_value_push(ucv_get(ucv_string_new(event_to_string(event))));
++	val = ucv_object_new(vm);
++	uc_value_push(ucv_get(val));
++
++	if (event == EVENT_CH_SWITCH_STARTED) {
++		ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count));
++		ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq));
++		ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
++		ucv_object_add(val, "center_freq1", ucv_int64_new(data->ch_switch.cf1));
++		ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
++	}
++
++	ucv_put(wpa_ucode_call(4));
++	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 *driver = ucv_object_get(info, "driver", NULL);
++	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);
++	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),
++	};
++
++	if (driver) {
++		const char *drvname;
++		if (ucv_type(driver) != UC_STRING)
++			goto out;
++
++		iface.driver = NULL;
++		drvname = ucv_string_get(driver);
++		for (int i = 0; wpa_drivers[i]; i++) {
++			if (!strcmp(drvname, wpa_drivers[i]->name))
++				iface.driver = wpa_drivers[i]->name;
++		}
++
++		if (!iface.driver)
++			goto out;
++	}
++
++	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);
++}
++
++static uc_value_t *
++uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
++{
++	struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
++	struct wpa_bss *bss;
++	uc_value_t *ret, *val;
++
++	if (!wpa_s)
++		return NULL;
++
++	ret = ucv_object_new(vm);
++
++	val = ucv_string_new(wpa_supplicant_state_txt(wpa_s->wpa_state));
++	ucv_object_add(ret, "state", ucv_get(val));
++
++	bss = wpa_s->current_bss;
++	if (bss) {
++		int sec_chan = 0;
++		const u8 *ie;
++
++		ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
++		if (ie && ie[1] >= 2) {
++			const struct ieee80211_ht_operation *ht_oper;
++			int sec;
++
++			ht_oper = (const void *) (ie + 2);
++			sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
++			if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
++				sec_chan = 1;
++			else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
++				sec_chan = -1;
++		}
++
++		ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
++		ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
++	}
++
++#ifdef CONFIG_MESH
++	if (wpa_s->ifmsh) {
++		struct hostapd_iface *ifmsh = wpa_s->ifmsh;
++
++		ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(ifmsh->conf->secondary_channel));
++		ucv_object_add(ret, "frequency", ucv_int64_new(ifmsh->freq));
++	}
++#endif
++
++	return 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 },
++		{ "udebug_set", uc_wpa_udebug_set },
++	};
++	static const uc_function_list_t iface_fns[] = {
++		{ "status", uc_wpas_iface_status },
++	};
++	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, "wpas.iface", iface_fns, NULL);
++
++	iface_registry = ucv_array_new(vm);
++	uc_vm_registry_set(vm, "wpas.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/wpa_supplicant/ucode.h b/wpa_supplicant/ucode.h
+new file mode 100644
+index 000000000..a429a0ed8
+--- /dev/null
++++ b/wpa_supplicant/ucode.h
+@@ -0,0 +1,49 @@
++#ifndef __WPAS_UCODE_H
++#define __WPAS_UCODE_H
++
++#include "utils/ucode.h"
++
++struct wpa_global;
++union wpa_event_data;
++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);
++void wpas_ucode_update_state(struct wpa_supplicant *wpa_s);
++void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data);
++#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)
++{
++}
++
++static inline void wpas_ucode_update_state(struct wpa_supplicant *wpa_s)
++{
++}
++
++static inline void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data)
++{
++}
++
++#endif
++
++#endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0010-hostapd-MLO-pass-ctx-in-mlme_event_mgmt.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0010-hostapd-MLO-pass-ctx-in-mlme_event_mgmt.patch
deleted file mode 100644
index de6b62e..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0010-hostapd-MLO-pass-ctx-in-mlme_event_mgmt.patch
+++ /dev/null
@@ -1,64 +0,0 @@
-From 33c9e4624040e1e0f331260c239fccdccbe52528 Mon Sep 17 00:00:00 2001
-From: Sriram R <quic_srirrama@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:40 +0530
-Subject: [PATCH 010/104] hostapd: MLO: pass ctx in mlme_event_mgmt()
-
-Add support to pass ctx in mlme_event_mgmt(). This will help in to route
-the event properly to link BSS.
-
-Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/drv_callbacks.c             | 2 +-
- src/drivers/driver.h               | 8 ++++++++
- src/drivers/driver_nl80211_event.c | 1 +
- 3 files changed, 10 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index adac2d478..3b24aa4f4 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -1810,8 +1810,8 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
- 	const u8 *bssid;
- 	struct hostapd_frame_info fi;
- 	int ret;
--	bool is_mld = false;
- 
-+	hapd = rx_mgmt->ctx ? rx_mgmt->ctx : hapd;
- 	hapd = switch_link_hapd(hapd, rx_mgmt->link_id);
- 	iface = hapd->iface;
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index d67c949b6..a7455ef6e 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -6345,6 +6345,14 @@ union wpa_event_data {
- 		 */
- 		void *drv_priv;
- 
-+		/**
-+		 * ctx - Pointer to store ctx of private BSS information
-+		 *
-+		 * If not set to NULL, this is used for forwarding the packet
-+		 * to right link BSS of ML BSS.
-+		 */
-+		void *ctx;
-+
- 		/**
- 		 * freq - Frequency (in MHz) on which the frame was received
- 		 */
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 51b27bd5e..1ca8b5bce 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -1367,6 +1367,7 @@ static void mlme_event_mgmt(struct i802_bss *bss,
- 	event.rx_mgmt.frame_len = len;
- 	event.rx_mgmt.ssi_signal = ssi_signal;
- 	event.rx_mgmt.drv_priv = bss;
-+	event.rx_mgmt.ctx = bss->ctx;
- 	event.rx_mgmt.link_id = link_id;
- 
- 	wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0010-sync-2024-08-13-openwrt-trunk-patches-folder.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0010-sync-2024-08-13-openwrt-trunk-patches-folder.patch
new file mode 100644
index 0000000..6fe601b
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0010-sync-2024-08-13-openwrt-trunk-patches-folder.patch
@@ -0,0 +1,14492 @@
+From 1a9b003e08a15df0e52604b991c1dd851a9bd265 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 19 Jul 2024 15:26:42 +0800
+Subject: [PATCH 010/126] sync 2024-08-13 openwrt/trunk patches folder
+
+Sync to e80520197c9ca7bced50d3605d6baba6dead6e35
+"hostapd: Add support for APuP"
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ hostapd/Makefile                          |  153 +-
+ hostapd/config_file.c                     |   42 +-
+ hostapd/ctrl_iface.c                      |    4 +
+ hostapd/defconfig                         |   15 +-
+ hostapd/hostapd_cli.c                     |   10 +-
+ hostapd/main.c                            |   21 +-
+ src/ap/acs.c                              |    5 +-
+ src/ap/airtime_policy.c                   |   15 +-
+ src/ap/ap_config.h                        |   37 +
+ src/ap/ap_drv_ops.c                       |   28 +-
+ src/ap/ap_drv_ops.h                       |   24 +-
+ src/ap/apup.c                             |  168 +
+ src/ap/apup.h                             |   24 +
+ src/ap/beacon.c                           |   16 +-
+ src/ap/ctrl_iface_ap.c                    |   45 +-
+ src/ap/dfs.c                              |   22 +-
+ src/ap/drv_callbacks.c                    |   16 +-
+ src/ap/hostapd.c                          |   59 +-
+ src/ap/hostapd.h                          |   41 +
+ src/ap/hw_features.c                      |    3 +-
+ src/ap/ieee802_11.c                       |   55 +-
+ src/ap/ieee802_11.h                       |    2 +
+ src/ap/ieee802_11_ht.c                    |   15 +
+ src/ap/ieee802_11_vht.c                   |   17 +
+ src/ap/ieee802_1x.c                       |    6 +
+ src/ap/rrm.c                              |   10 +
+ src/ap/sta_info.c                         |   35 +-
+ src/ap/sta_info.h                         |   16 +-
+ src/ap/ubus.c                             |   15 +
+ src/ap/ubus.h                             |    5 +
+ src/ap/ucode.c                            |   17 +
+ src/ap/ucode.h                            |    4 +
+ src/ap/vlan_full.c                        |    4 +
+ src/ap/vlan_init.c                        |    8 +-
+ src/ap/wnm_ap.c                           |   16 +-
+ src/ap/wpa_auth.c                         |    3 +-
+ src/ap/wpa_auth_glue.c                    |    9 +-
+ src/ap/wps_hostapd.c                      |    6 +-
+ src/ap/x_snoop.c                          |   22 +-
+ src/common/defs.h                         |    4 +
+ src/common/dpp_crypto.c                   |   10 +-
+ src/common/hw_features_common.c           |    1 +
+ src/common/ieee802_11_defs.h              |    2 +
+ src/common/sae.c                          |    7 +
+ src/common/wpa_ctrl.c                     |   10 +-
+ src/crypto/Makefile                       |  129 +-
+ src/crypto/crypto_mbedtls.c               | 4228 +++++++++++++++++++++
+ src/crypto/crypto_module_tests.c          |  134 +
+ src/crypto/tls_mbedtls.c                  | 3313 ++++++++++++++++
+ src/drivers/driver.h                      |   38 +-
+ src/drivers/driver_nl80211.c              |  174 +-
+ src/drivers/driver_nl80211_capa.c         |    4 +
+ src/drivers/driver_nl80211_event.c        |    5 +
+ src/drivers/driver_nl80211_scan.c         |    2 +-
+ src/drivers/drivers.c                     |    4 +
+ src/drivers/drivers.mak                   |    4 +-
+ src/drivers/rfkill.h                      |   16 +
+ src/radius/radius_client.c                |   34 +
+ src/radius/radius_client.h                |    2 +
+ src/radius/radius_das.c                   |  201 +-
+ src/radius/radius_das.h                   |    1 +
+ src/radius/radius_server.c                |   61 +-
+ src/rsn_supp/wpa.c                        |    3 +
+ src/tls/Makefile                          |   11 +
+ src/utils/eloop.c                         |   20 +-
+ src/utils/eloop.h                         |    8 +
+ src/utils/uloop.c                         |   64 +
+ src/utils/wpa_debug.c                     |   49 +-
+ src/utils/wpa_debug.h                     |   73 +-
+ tests/Makefile                            |   76 +-
+ tests/hwsim/example-hostapd.config        |    8 +-
+ tests/hwsim/example-wpa_supplicant.config |    9 +-
+ tests/hwsim/test_ap_eap.py                |  114 +-
+ tests/hwsim/test_ap_ft.py                 |    4 +-
+ tests/hwsim/test_authsrv.py               |    9 +-
+ tests/hwsim/test_dpp.py                   |   19 +-
+ tests/hwsim/test_erp.py                   |   16 +-
+ tests/hwsim/test_fils.py                  |    4 +
+ tests/hwsim/test_pmksa_cache.py           |    4 +-
+ tests/hwsim/test_sae.py                   |    7 +
+ tests/hwsim/test_suite_b.py               |    3 +
+ tests/hwsim/test_wpas_ctrl.py             |    2 +-
+ tests/hwsim/utils.py                      |    8 +-
+ tests/test-crypto_module.c                |   16 +
+ tests/test-https.c                        |   12 +-
+ tests/test-https_server.c                 |   12 +-
+ wpa_supplicant/Makefile                   |  143 +-
+ wpa_supplicant/ap.c                       |   28 +-
+ wpa_supplicant/config.c                   |   95 +
+ wpa_supplicant/config_file.c              |    8 +-
+ wpa_supplicant/config_ssid.h              |    5 +
+ wpa_supplicant/ctrl_iface.c               |   12 +-
+ wpa_supplicant/defconfig                  |    6 +-
+ wpa_supplicant/eapol_test.c               |   11 +
+ wpa_supplicant/events.c                   |    7 +-
+ wpa_supplicant/main.c                     |   14 +-
+ wpa_supplicant/mesh.c                     |    3 +
+ wpa_supplicant/wpa_cli.c                  |    9 +
+ wpa_supplicant/wpa_priv.c                 |    8 +-
+ wpa_supplicant/wpa_supplicant.c           |   76 +-
+ wpa_supplicant/wpa_supplicant_i.h         |    6 +
+ wpa_supplicant/wps_supplicant.c           |    3 +
+ wpa_supplicant/wps_supplicant.h           |    3 +-
+ 103 files changed, 9984 insertions(+), 401 deletions(-)
+ create mode 100644 src/ap/apup.c
+ create mode 100644 src/ap/apup.h
+ create mode 100644 src/crypto/crypto_mbedtls.c
+ create mode 100644 src/crypto/tls_mbedtls.c
+ create mode 100644 src/utils/uloop.c
+ create mode 100644 tests/test-crypto_module.c
+
+diff --git a/hostapd/Makefile b/hostapd/Makefile
+index 78171025e..8dc6e6216 100644
+--- a/hostapd/Makefile
++++ b/hostapd/Makefile
+@@ -1,6 +1,7 @@
+ ALL=hostapd hostapd_cli
+ CONFIG_FILE = .config
+ 
++-include $(if $(MULTICALL), ../wpa_supplicant/.config)
+ include ../src/build.rules
+ 
+ ifdef LIBS
+@@ -62,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
+@@ -174,6 +179,24 @@ OBJS += ../src/common/hw_features_common.o
+ 
+ OBJS += ../src/eapol_auth/eapol_auth_sm.o
+ 
++ifdef CONFIG_UBUS
++CFLAGS += -DUBUS_SUPPORT
++OBJS += ../src/ap/ubus.o
++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
+ CFLAGS += -O0 -fprofile-arcs -ftest-coverage -U_FORTIFY_SOURCE
+@@ -208,7 +231,8 @@ endif
+ 
+ ifdef CONFIG_NO_VLAN
+ CFLAGS += -DCONFIG_NO_VLAN
+-else
++endif
++ifneq ($(findstring CONFIG_NO_VLAN,$(CFLAGS)), CONFIG_NO_VLAN)
+ OBJS += ../src/ap/vlan_init.o
+ OBJS += ../src/ap/vlan_ifconfig.o
+ OBJS += ../src/ap/vlan.o
+@@ -228,6 +252,9 @@ endif
+ ifdef CONFIG_NO_CTRL_IFACE
+ CFLAGS += -DCONFIG_NO_CTRL_IFACE
+ else
++ifdef CONFIG_CTRL_IFACE_MIB
++CFLAGS += -DCONFIG_CTRL_IFACE_MIB
++endif
+ ifeq ($(CONFIG_CTRL_IFACE), udp)
+ CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+ else
+@@ -367,10 +394,14 @@ CFLAGS += -DCONFIG_MBO
+ OBJS += ../src/ap/mbo_ap.o
+ endif
+ 
++ifndef MULTICALL
++CFLAGS += -DNO_SUPPLICANT
++endif
++
+ include ../src/drivers/drivers.mak
+-OBJS += $(DRV_AP_OBJS)
+-CFLAGS += $(DRV_AP_CFLAGS)
+-LDFLAGS += $(DRV_AP_LDFLAGS)
++OBJS += $(sort $(DRV_AP_OBJS) $(if $(MULTICALL),$(DRV_WPA_OBJS)))
++CFLAGS += $(DRV_AP_CFLAGS) $(if $(MULTICALL),$(DRV_WPA_CFLAGS))
++LDFLAGS += $(DRV_AP_LDFLAGS) $(if $(MULTICALL),$(DRV_WPA_LDFLAGS))
+ LIBS += $(DRV_AP_LIBS)
+ 
+ ifdef CONFIG_L2_PACKET
+@@ -716,6 +747,7 @@ CFLAGS += -DCONFIG_TLSV12
+ endif
+ 
+ ifeq ($(CONFIG_TLS), wolfssl)
++CFLAGS += -DCONFIG_TLS_WOLFSSL
+ CONFIG_CRYPTO=wolfssl
+ ifdef TLS_FUNCS
+ OBJS += ../src/crypto/tls_wolfssl.o
+@@ -736,6 +768,7 @@ endif
+ endif
+ 
+ ifeq ($(CONFIG_TLS), openssl)
++CFLAGS += -DCONFIG_TLS_OPENSSL
+ CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
+ CONFIG_CRYPTO=openssl
+ ifdef TLS_FUNCS
+@@ -765,7 +798,39 @@ endif
+ CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
+ endif
+ 
++ifeq ($(CONFIG_TLS), mbedtls)
++CFLAGS += -DCONFIG_TLS_MBEDTLS
++ifndef CONFIG_CRYPTO
++CONFIG_CRYPTO=mbedtls
++endif
++ifdef TLS_FUNCS
++OBJS += ../src/crypto/tls_mbedtls.o
++LIBS += -lmbedtls
++ifndef CONFIG_DPP
++LIBS += -lmbedx509
++endif
++endif
++OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++HOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++SOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++ifeq ($(CONFIG_CRYPTO), mbedtls)
++ifdef CONFIG_DPP
++LIBS += -lmbedx509
++LIBS_h += -lmbedx509
++LIBS_n += -lmbedx509
++LIBS_s += -lmbedx509
++endif
++LIBS += -lmbedcrypto
++LIBS_h += -lmbedcrypto
++LIBS_n += -lmbedcrypto
++LIBS_s += -lmbedcrypto
++# XXX: create a config option?
++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
++endif
++endif
++
+ ifeq ($(CONFIG_TLS), gnutls)
++CFLAGS += -DCONFIG_TLS_GNUTLS
+ ifndef CONFIG_CRYPTO
+ # default to libgcrypt
+ CONFIG_CRYPTO=gnutls
+@@ -796,6 +861,7 @@ endif
+ endif
+ 
+ ifeq ($(CONFIG_TLS), internal)
++CFLAGS += -DCONFIG_TLS_INTERNAL
+ ifndef CONFIG_CRYPTO
+ CONFIG_CRYPTO=internal
+ endif
+@@ -874,6 +940,7 @@ endif
+ endif
+ 
+ ifeq ($(CONFIG_TLS), linux)
++CFLAGS += -DCONFIG_TLS_INTERNAL
+ OBJS += ../src/crypto/crypto_linux.o
+ ifdef TLS_FUNCS
+ OBJS += ../src/crypto/crypto_internal-rsa.o
+@@ -944,9 +1011,11 @@ endif
+ 
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-wrap.o
+ endif
+ endif
++endif
+ ifdef NEED_AES_EAX
+ AESOBJS += ../src/crypto/aes-eax.o
+ NEED_AES_CTR=y
+@@ -956,38 +1025,48 @@ AESOBJS += ../src/crypto/aes-siv.o
+ NEED_AES_CTR=y
+ endif
+ ifdef NEED_AES_CTR
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-ctr.o
+ endif
++endif
+ ifdef NEED_AES_ENCBLOCK
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-encblock.o
+ endif
++endif
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-omac1.o
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_UNWRAP
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ NEED_AES_DEC=y
+ AESOBJS += ../src/crypto/aes-unwrap.o
+ endif
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_CBC
+ NEED_AES_DEC=y
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-cbc.o
+ endif
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_DEC
+ ifdef CONFIG_INTERNAL_AES
+ AESOBJS += ../src/crypto/aes-internal-dec.o
+@@ -1002,12 +1081,16 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-prf.o
++endif
+ ifdef CONFIG_INTERNAL_SHA1
+ SHA1OBJS += ../src/crypto/sha1-internal.o
+ ifdef NEED_FIPS186_2_PRF
+@@ -1016,16 +1099,22 @@ endif
+ endif
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
+ endif
+ endif
++endif
+ ifdef NEED_T_PRF
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-tprf.o
+ endif
++endif
+ ifdef NEED_TLS_PRF
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-tlsprf.o
+ endif
+ endif
++endif
+ 
+ ifdef NEED_SHA1
+ OBJS += $(SHA1OBJS)
+@@ -1035,11 +1124,13 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/md5.o
+ endif
+ endif
+ endif
+ endif
++endif
+ 
+ ifdef NEED_MD5
+ ifdef CONFIG_INTERNAL_MD5
+@@ -1078,56 +1169,81 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256-prf.o
++endif
+ ifdef CONFIG_INTERNAL_SHA256
+ OBJS += ../src/crypto/sha256-internal.o
+ endif
+ ifdef NEED_TLS_PRF_SHA256
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256-tlsprf.o
+ endif
++endif
+ ifdef NEED_TLS_PRF_SHA384
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-tlsprf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA256_KDF
++CFLAGS += -DCONFIG_HMAC_SHA256_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256-kdf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA384_KDF
++CFLAGS += -DCONFIG_HMAC_SHA384_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-kdf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA512_KDF
++CFLAGS += -DCONFIG_HMAC_SHA512_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512-kdf.o
+ endif
++endif
+ ifdef NEED_SHA384
+ CFLAGS += -DCONFIG_SHA384
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-prf.o
+ endif
++endif
+ ifdef NEED_SHA512
+ CFLAGS += -DCONFIG_SHA512
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512-prf.o
+ endif
++endif
+ 
+ ifdef CONFIG_INTERNAL_SHA384
+ CFLAGS += -DCONFIG_INTERNAL_SHA384
+@@ -1172,11 +1288,13 @@ HOBJS += $(SHA1OBJS)
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ HOBJS += ../src/crypto/md5.o
+ endif
+ endif
+ endif
+ endif
++endif
+ 
+ ifdef CONFIG_RADIUS_SERVER
+ CFLAGS += -DRADIUS_SERVER
+@@ -1306,6 +1424,11 @@ ifdef CONFIG_NO_TKIP
+ CFLAGS += -DCONFIG_NO_TKIP
+ endif
+ 
++ifdef CONFIG_APUP
++CFLAGS += -DCONFIG_APUP
++OBJS += ../src/ap/apup.o
++endif
++
+ $(DESTDIR)$(BINDIR)/%: %
+ 	install -D $(<) $(@)
+ 
+@@ -1314,8 +1437,14 @@ install: $(addprefix $(DESTDIR)$(BINDIR)/,$(ALL))
+ _OBJS_VAR := OBJS
+ include ../src/objs.mk
+ 
++hostapd_multi.a: $(BCHECK) $(OBJS)
++	$(Q)$(CC) -c -o hostapd_multi.o -Dmain=hostapd_main $(CFLAGS) main.c
++	@$(E) "  CC " $<
++	@rm -f $@
++	@$(AR) cr $@ hostapd_multi.o $(OBJS)
++
+ hostapd: $(OBJS)
+-	$(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
++	+$(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
+ 	@$(E) "  LD " $@
+ 
+ ifdef CONFIG_WPA_TRACE
+@@ -1326,7 +1455,7 @@ _OBJS_VAR := OBJS_c
+ include ../src/objs.mk
+ 
+ hostapd_cli: $(OBJS_c)
+-	$(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
++	+$(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
+ 	@$(E) "  LD " $@
+ 
+ NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS)
+@@ -1350,7 +1479,9 @@ NOBJS += ../src/utils/trace.o
+ endif
+ 
+ HOBJS += hlr_auc_gw.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).o ../src/utils/wpabuf.o ../src/crypto/milenage.o
++ifneq ($(CONFIG_TLS), mbedtls)
+ HOBJS += ../src/crypto/aes-encblock.o
++endif
+ ifdef CONFIG_INTERNAL_AES
+ HOBJS += ../src/crypto/aes-internal.o
+ HOBJS += ../src/crypto/aes-internal-enc.o
+@@ -1373,13 +1504,17 @@ SOBJS += ../src/common/sae.o
+ SOBJS += ../src/common/sae_pk.o
+ SOBJS += ../src/common/dragonfly.o
+ SOBJS += $(AESOBJS)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SOBJS += ../src/crypto/sha256-prf.o
+ SOBJS += ../src/crypto/sha384-prf.o
+ SOBJS += ../src/crypto/sha512-prf.o
++endif
+ SOBJS += ../src/crypto/dh_groups.o
++ifneq ($(CONFIG_TLS), mbedtls)
+ SOBJS += ../src/crypto/sha256-kdf.o
+ SOBJS += ../src/crypto/sha384-kdf.o
+ SOBJS += ../src/crypto/sha512-kdf.o
++endif
+ 
+ _OBJS_VAR := NOBJS
+ include ../src/objs.mk
+@@ -1388,6 +1523,12 @@ include ../src/objs.mk
+ _OBJS_VAR := SOBJS
+ include ../src/objs.mk
+ 
++dump_cflags:
++	@printf "%s " "$(CFLAGS)"
++
++dump_ldflags:
++	@printf "%s " "$(LDFLAGS) $(LIBS) $(EXTRALIBS)"
++
+ nt_password_hash: $(NOBJS)
+ 	$(Q)$(CC) $(LDFLAGS) -o nt_password_hash $(NOBJS) $(LIBS_n)
+ 	@$(E) "  LD " $@
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index a86621ed9..f3968ec95 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -1229,6 +1229,8 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf,
+ 		conf->vht_capab |= VHT_CAP_RX_ANTENNA_PATTERN;
+ 	if (os_strstr(capab, "[TX-ANTENNA-PATTERN]"))
+ 		conf->vht_capab |= VHT_CAP_TX_ANTENNA_PATTERN;
++	if (os_strstr(capab, "[EXT-NSS-BW-SUPP]"))
++		conf->vht_capab |= VHT_CAP_EXTENDED_NSS_BW_SUPPORT;
+ 	return 0;
+ }
+ #endif /* CONFIG_IEEE80211AC */
+@@ -2654,8 +2656,12 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 			   sizeof(conf->bss[0]->iface));
+ 	} else if (os_strcmp(buf, "bridge") == 0) {
+ 		os_strlcpy(bss->bridge, pos, sizeof(bss->bridge));
++		if (!bss->wds_bridge[0])
++			os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge));
+ 	} else if (os_strcmp(buf, "bridge_hairpin") == 0) {
+ 		bss->bridge_hairpin = atoi(pos);
++	} else if (os_strcmp(buf, "snoop_iface") == 0) {
++		os_strlcpy(bss->snoop_iface, pos, sizeof(bss->snoop_iface));
+ 	} else if (os_strcmp(buf, "vlan_bridge") == 0) {
+ 		os_strlcpy(bss->vlan_bridge, pos, sizeof(bss->vlan_bridge));
+ 	} else if (os_strcmp(buf, "wds_bridge") == 0) {
+@@ -3043,6 +3049,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 	} else if (os_strcmp(buf, "iapp_interface") == 0) {
+ 		wpa_printf(MSG_INFO, "DEPRECATED: iapp_interface not used");
+ #endif /* CONFIG_IAPP */
++	} else if (os_strcmp(buf, "dynamic_own_ip_addr") == 0) {
++		bss->dynamic_own_ip_addr = atoi(pos);
+ 	} else if (os_strcmp(buf, "own_ip_addr") == 0) {
+ 		if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) {
+ 			wpa_printf(MSG_ERROR,
+@@ -3270,6 +3278,14 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 				   line, bss->max_num_sta, MAX_STA_COUNT);
+ 			return 1;
+ 		}
++	} else if (os_strcmp(buf, "iface_max_num_sta") == 0) {
++		conf->max_num_sta = atoi(pos);
++		if (conf->max_num_sta < 0 ||
++		    conf->max_num_sta > MAX_STA_COUNT) {
++			wpa_printf(MSG_ERROR, "Line %d: Invalid max_num_sta=%d; allowed range 0..%d",
++				   line, conf->max_num_sta, MAX_STA_COUNT);
++			return 1;
++		}
+ 	} else if (os_strcmp(buf, "wpa") == 0) {
+ 		bss->wpa = atoi(pos);
+ 	} else if (os_strcmp(buf, "extended_key_id") == 0) {
+@@ -3459,6 +3475,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		wpa_printf(MSG_INFO,
+ 			   "Line %d: Obsolete peerkey parameter ignored", line);
+ #ifdef CONFIG_IEEE80211R_AP
++	} else if (os_strcmp(buf, "ft_iface") == 0) {
++		os_strlcpy(bss->ft_iface, pos, sizeof(bss->ft_iface));
+ 	} else if (os_strcmp(buf, "mobility_domain") == 0) {
+ 		if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
+ 		    hexstr2bin(pos, bss->mobility_domain,
+@@ -3828,6 +3846,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ #ifndef CONFIG_NO_VLAN
+ 	} else if (os_strcmp(buf, "dynamic_vlan") == 0) {
+ 		bss->ssid.dynamic_vlan = atoi(pos);
++	} else if (os_strcmp(buf, "vlan_no_bridge") == 0) {
++		bss->ssid.vlan_no_bridge = atoi(pos);
+ 	} else if (os_strcmp(buf, "per_sta_vif") == 0) {
+ 		bss->ssid.per_sta_vif = atoi(pos);
+ 	} else if (os_strcmp(buf, "vlan_file") == 0) {
+@@ -3929,6 +3949,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		if (bss->ocv && !bss->ieee80211w)
+ 			bss->ieee80211w = 1;
+ #endif /* CONFIG_OCV */
++	} else if (os_strcmp(buf, "noscan") == 0) {
++		conf->noscan = atoi(pos);
++	} else if (os_strcmp(buf, "ht_coex") == 0) {
++		conf->no_ht_coex = !atoi(pos);
+ 	} else if (os_strcmp(buf, "ieee80211n") == 0) {
+ 		conf->ieee80211n = atoi(pos);
+ 	} else if (os_strcmp(buf, "ht_capab") == 0) {
+@@ -3979,6 +4003,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 	} else if (os_strcmp(buf, "he_bss_color") == 0) {
+ 		conf->he_op.he_bss_color = atoi(pos) & 0x3f;
+ 		conf->he_op.he_bss_color_disabled = 0;
++		if (atoi(pos) > 63)
++			conf->he_op.he_bss_color = os_random() % 63 + 1;
+ 	} else if (os_strcmp(buf, "he_bss_color_partial") == 0) {
+ 		conf->he_op.he_bss_color_partial = atoi(pos);
+ 	} else if (os_strcmp(buf, "he_default_pe_duration") == 0) {
+@@ -5435,6 +5461,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		bss->mld_indicate_disabled = atoi(pos);
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
++#ifdef CONFIG_APUP
++	} else if (os_strcmp(buf, "apup") == 0) {
++		bss->apup = !!atoi(pos);
++		if (bss->apup)
++			bss->wds_sta = 1;
++	} else if (os_strcmp(buf, "apup_peer_ifname_prefix") == 0) {
++		os_strlcpy(bss->apup_peer_ifname_prefix,
++		           pos, sizeof(bss->apup_peer_ifname_prefix));
++#endif // def CONFIG_APUP
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+@@ -5460,7 +5495,12 @@ struct hostapd_config * hostapd_config_read(const char *fname)
+ 	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);
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index a584d370e..1d5922fab 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4005,6 +4005,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 						      reply_size);
+ 	} else if (os_strcmp(buf, "STATUS-DRIVER") == 0) {
+ 		reply_len = hostapd_drv_status(hapd, reply, reply_size);
++#ifdef CONFIG_CTRL_IFACE_MIB
+ 	} else if (os_strcmp(buf, "MIB") == 0) {
+ 		reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
+ 		if (reply_len >= 0) {
+@@ -4046,6 +4047,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 	} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
+ 		reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
+ 							reply_size);
++#endif
+ 	} else if (os_strcmp(buf, "ATTACH") == 0) {
+ 		if (hostapd_ctrl_iface_attach(hapd, from, fromlen, NULL))
+ 			reply_len = -1;
+@@ -5994,6 +5996,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;
+@@ -6095,6 +6098,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/hostapd/defconfig b/hostapd/defconfig
+index 66bf894eb..f716553bb 100644
+--- a/hostapd/defconfig
++++ b/hostapd/defconfig
+@@ -6,9 +6,21 @@
+ # just setting VARIABLE=n is not disabling that variable.
+ #
+ # This file is included in Makefile, so variables like CFLAGS and LIBS can also
+-# be modified from here. In most cass, these lines should use += in order not
++# be modified from here. In most cases, these lines should use += in order not
+ # to override previous values of the variables.
+ 
++
++# Uncomment following two lines and fix the paths if you have installed TLS
++# libraries in a non-default location
++#CFLAGS += -I/usr/local/openssl/include
++#LIBS += -L/usr/local/openssl/lib
++
++# Some Red Hat versions seem to include kerberos header files from OpenSSL, but
++# the kerberos files are not in the default include path. Following line can be
++# used to fix build issues on such systems (krb5.h not found).
++#CFLAGS += -I/usr/include/kerberos
++
++
+ # Driver interface for Host AP driver
+ CONFIG_DRIVER_HOSTAP=y
+ 
+@@ -281,6 +293,7 @@ CONFIG_IPV6=y
+ # openssl = OpenSSL (default)
+ # gnutls = GnuTLS
+ # internal = Internal TLSv1 implementation (experimental)
++# mbedtls = mbed TLS
+ # linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
+ # none = Empty template
+ #CONFIG_TLS=openssl
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index d69525502..ebf8addc1 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -410,7 +410,6 @@ static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
+ }
+ 
+ 
+-#ifdef CONFIG_TAXONOMY
+ static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
+ 				     char *argv[])
+ {
+@@ -423,7 +422,6 @@ static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
+ 	os_snprintf(buf, sizeof(buf), "SIGNATURE %s", argv[0]);
+ 	return wpa_ctrl_command(ctrl, buf);
+ }
+-#endif /* CONFIG_TAXONOMY */
+ 
+ 
+ static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
+@@ -440,7 +438,6 @@ static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
+ }
+ 
+ 
+-#ifdef CONFIG_WPS
+ static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
+ 				   char *argv[])
+ {
+@@ -666,7 +663,6 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
+ 			 ssid_hex, argv[1]);
+ 	return wpa_ctrl_command(ctrl, buf);
+ }
+-#endif /* CONFIG_WPS */
+ 
+ 
+ static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
+@@ -766,7 +762,7 @@ static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd,
+ 	}
+ 
+ 	buf[len] = '\0';
+-	if (memcmp(buf, "FAIL", 4) == 0)
++	if (memcmp(buf, "FAIL", 4) == 0 || memcmp(buf, "UNKNOWN COMMAND", 15) == 0)
+ 		return -1;
+ 	if (print)
+ 		printf("%s", buf);
+@@ -1695,13 +1691,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 	{ "disassociate", hostapd_cli_cmd_disassociate,
+ 	  hostapd_complete_stations,
+ 	  "<addr> = disassociate a station" },
+-#ifdef CONFIG_TAXONOMY
+ 	{ "signature", hostapd_cli_cmd_signature, hostapd_complete_stations,
+ 	  "<addr> = get taxonomy signature for a station" },
+-#endif /* CONFIG_TAXONOMY */
+ 	{ "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations,
+ 	  "<addr> = send SA Query to a station" },
+-#ifdef CONFIG_WPS
+ 	{ "wps_pin", hostapd_cli_cmd_wps_pin, NULL,
+ 	  "<uuid> <pin> [timeout] [addr] = add WPS Enrollee PIN" },
+ 	{ "wps_check_pin", hostapd_cli_cmd_wps_check_pin, NULL,
+@@ -1726,7 +1719,6 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 	  "<SSID> <auth> <encr> <key> = configure AP" },
+ 	{ "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL,
+ 	  "= show current WPS status" },
+-#endif /* CONFIG_WPS */
+ 	{ "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL,
+ 	  "= send Disassociation Imminent notification" },
+ 	{ "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL,
+diff --git a/hostapd/main.c b/hostapd/main.c
+index aa1f69812..e790f18ce 100644
+--- a/hostapd/main.c
++++ b/hostapd/main.c
+@@ -31,7 +31,7 @@
+ #include "config_file.h"
+ #include "eap_register.h"
+ #include "ctrl_iface.h"
+-
++#include "build_features.h"
+ 
+ struct hapd_global {
+ 	void **drv_priv;
+@@ -40,6 +40,7 @@ struct hapd_global {
+ 
+ static struct hapd_global global;
+ 
++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,
+@@ -692,6 +693,11 @@ fail:
+ 	return -1;
+ }
+ 
++void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
++                       union wpa_event_data *data);
++
++void hostapd_wpa_event_global(void *ctx, enum wpa_event_type event,
++ 				 union wpa_event_data *data);
+ 
+ #ifdef CONFIG_WPS
+ static int gen_uuid(const char *txt_addr)
+@@ -784,6 +790,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;
+@@ -813,8 +824,10 @@ int main(int argc, char *argv[])
+ 		return -1;
+ #endif /* CONFIG_DPP */
+ 
++	wpa_supplicant_event = hostapd_wpa_event;
++	wpa_supplicant_event_global = hostapd_wpa_event_global;
+ 	for (;;) {
+-		c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:q");
++		c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:g:G:qv::");
+ 		if (c < 0)
+ 			break;
+ 		switch (c) {
+@@ -851,6 +864,8 @@ int main(int argc, char *argv[])
+ 			break;
+ #endif /* CONFIG_DEBUG_LINUX_TRACING */
+ 		case 'v':
++			if (optarg)
++				exit(!has_feature(optarg));
+ 			show_version();
+ 			exit(1);
+ 		case 'g':
+@@ -1020,6 +1035,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");
+@@ -1029,6 +1045,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++) {
+diff --git a/src/ap/acs.c b/src/ap/acs.c
+index f5b36d327..25fec499a 100644
+--- a/src/ap/acs.c
++++ b/src/ap/acs.c
+@@ -471,17 +471,17 @@ static int acs_get_bw_center_chan(int freq, enum bw_type bw)
+ static int acs_survey_is_sufficient(struct freq_survey *survey)
+ {
+ 	if (!(survey->filled & SURVEY_HAS_NF)) {
++		survey->nf = -95;
+ 		wpa_printf(MSG_INFO,
+ 			   "ACS: Survey for freq %d is missing noise floor",
+ 			   survey->freq);
+-		return 0;
+ 	}
+ 
+ 	if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
++		survey->channel_time = 0;
+ 		wpa_printf(MSG_INFO,
+ 			   "ACS: Survey for freq %d is missing channel time",
+ 			   survey->freq);
+-		return 0;
+ 	}
+ 
+ 	if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
+@@ -489,7 +489,6 @@ static int acs_survey_is_sufficient(struct freq_survey *survey)
+ 		wpa_printf(MSG_INFO,
+ 			   "ACS: Survey for freq %d is missing RX and busy time (at least one is required)",
+ 			   survey->freq);
+-		return 0;
+ 	}
+ 
+ 	return 1;
+diff --git a/src/ap/airtime_policy.c b/src/ap/airtime_policy.c
+index 68443115f..26f11ad98 100644
+--- a/src/ap/airtime_policy.c
++++ b/src/ap/airtime_policy.c
+@@ -112,8 +112,14 @@ static void set_sta_weights(struct hostapd_data *hapd, unsigned int weight)
+ {
+ 	struct sta_info *sta;
+ 
+-	for (sta = hapd->sta_list; sta; sta = sta->next)
+-		sta_set_airtime_weight(hapd, sta, weight);
++	for (sta = hapd->sta_list; sta; sta = sta->next) {
++		unsigned int sta_weight = weight;
++
++		if (sta->dyn_airtime_weight)
++			sta_weight = (weight * sta->dyn_airtime_weight) / 256;
++
++		sta_set_airtime_weight(hapd, sta, sta_weight);
++	}
+ }
+ 
+ 
+@@ -244,7 +250,10 @@ int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta)
+ 	unsigned int weight;
+ 
+ 	if (hapd->iconf->airtime_mode == AIRTIME_MODE_STATIC) {
+-		weight = get_weight_for_sta(hapd, sta->addr);
++		if (sta->dyn_airtime_weight)
++			weight = sta->dyn_airtime_weight;
++		else
++			weight = get_weight_for_sta(hapd, sta->addr);
+ 		if (weight)
+ 			return sta_set_airtime_weight(hapd, sta, weight);
+ 	}
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index e6669e6a3..29a9ae7db 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -121,6 +121,7 @@ struct hostapd_ssid {
+ #define DYNAMIC_VLAN_OPTIONAL 1
+ #define DYNAMIC_VLAN_REQUIRED 2
+ 	int dynamic_vlan;
++	int vlan_no_bridge;
+ #define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0
+ #define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
+ #define DYNAMIC_VLAN_NAMING_END 2
+@@ -282,6 +283,8 @@ struct airtime_sta_weight {
+ struct hostapd_bss_config {
+ 	char iface[IFNAMSIZ + 1];
+ 	char bridge[IFNAMSIZ + 1];
++	char ft_iface[IFNAMSIZ + 1];
++	char snoop_iface[IFNAMSIZ + 1];
+ 	char vlan_bridge[IFNAMSIZ + 1];
+ 	char wds_bridge[IFNAMSIZ + 1];
+ 	int bridge_hairpin; /* hairpin_mode on bridge members */
+@@ -307,6 +310,7 @@ struct hostapd_bss_config {
+ 	unsigned int eap_sim_db_timeout;
+ 	int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
+ 	struct hostapd_ip_addr own_ip_addr;
++	int dynamic_own_ip_addr;
+ 	char *nas_identifier;
+ 	struct hostapd_radius_servers *radius;
+ 	int radius_require_message_authenticator;
+@@ -995,6 +999,35 @@ struct hostapd_bss_config {
+ 	bool mld_indicate_disabled;
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
++
++#ifdef CONFIG_APUP
++	/**
++	 * Access Point Micro Peering
++	 * A simpler and more useful successor to Ad Hoc,
++	 * Wireless Distribution System, 802.11s mesh mode, Multi-AP and EasyMesh.
++	 *
++	 * Almost plain APs communicate between them via 4-address mode, like in WDS
++	 * but all of them are AP, so they can eventually communicate also with
++	 * plain stations and more AP nodes in sight.
++	 * Low hardware requirements, just AP mode support + 4-address mode, and no
++	 * more unnecessary complications, like hardcoded bridging or routing
++	 * algorithm in WiFi stack.
++	 * For each AP in sight an interface is created, and then it can be used as
++	 * convenient in each case, bridging, routing etc.
++	 */
++	bool apup;
++
++	/**
++	 * In 4-address mode each peer AP in sight is associated to its own
++	 * interface so we have more flexibility in "user-space".
++	 * Those interfaces could be simply bridged in a trivial topology (which
++	 * happens automatically if wds_bridge is not an empty string), or feeded to
++	 * a routing daemon.
++	 *
++	 * If not defined interface names are generated following the WDS convention.
++	 */
++	char apup_peer_ifname_prefix[IFNAMSIZ + 1];
++#endif /* CONFIG_APUP */
+ };
+ 
+ /**
+@@ -1085,6 +1118,8 @@ struct hostapd_config {
+ 	unsigned int track_sta_max_num;
+ 	unsigned int track_sta_max_age;
+ 
++	int max_num_sta;
++
+ 	char country[3]; /* first two octets: country code as described in
+ 			  * ISO/IEC 3166-1. Third octet:
+ 			  * ' ' (ascii 32): all environments
+@@ -1122,6 +1157,8 @@ struct hostapd_config {
+ 
+ 	int ht_op_mode_fixed;
+ 	u16 ht_capab;
++	int noscan;
++	int no_ht_coex;
+ 	int ieee80211n;
+ 	int secondary_channel;
+ 	int no_pri_sec_switch;
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index c47349110..7c9527cd3 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -385,13 +385,37 @@ int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
+ 			const u8 *addr, int aid, int val)
+ {
+ 	const char *bridge = NULL;
++	char ifName[IFNAMSIZ + 1];
++
++	int mRet = 0;
+ 
+ 	if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
+ 		return -1;
++
++#ifdef CONFIG_APUP
++	if (hapd->conf->apup && hapd->conf->apup_peer_ifname_prefix[0]) {
++		mRet = os_snprintf(
++		            ifName, sizeof(ifName), "%s%d",
++		            hapd->conf->apup_peer_ifname_prefix, aid);
++	}
++	else
++#endif // def CONFIG_APUP
++		mRet = os_snprintf(
++		            ifName, sizeof(ifName), "%s.sta%d",
++		            hapd->conf->iface, aid);
++
++	if (mRet >= (int) sizeof(ifName))
++		wpa_printf(MSG_WARNING,
++		           "nl80211: WDS interface name was truncated");
++	else if (mRet < 0)
++		return mRet;
++
++	// Pass back to the caller the resulting interface name
++	if (ifname_wds)
++		os_strlcpy(ifname_wds, ifName, IFNAMSIZ + 1);
++
+ 	if (hapd->conf->wds_bridge[0])
+ 		bridge = hapd->conf->wds_bridge;
+-	else if (hapd->conf->bridge[0])
+-		bridge = hapd->conf->bridge;
+ 	return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
+ 					 bridge, ifname_wds);
+ }
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index d7e79c840..58ca046c6 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -35,6 +35,9 @@ int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
+ 			      int enabled);
+ int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname);
+ int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname);
++
++/** @param val as per nl80211 driver implementation, 1 means add 0 means remove
++ */
+ int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
+ 			const u8 *addr, int aid, int val);
+ int hostapd_sta_add(struct hostapd_data *hapd,
+@@ -371,12 +374,12 @@ static inline int hostapd_drv_br_port_set_attr(struct hostapd_data *hapd,
+ 
+ static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
+ 					       enum drv_br_net_param param,
+-					       unsigned int val)
++					       const char *ifname, unsigned int val)
+ {
+ 	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ 	    hapd->driver->br_set_net_param == NULL)
+ 		return -1;
+-	return hapd->driver->br_set_net_param(hapd->drv_priv, param, val);
++	return hapd->driver->br_set_net_param(hapd->drv_priv, param, ifname, val);
+ }
+ 
+ static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
+@@ -404,6 +407,23 @@ static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
+ 	return hapd->driver->stop_ap(hapd->drv_priv, link_id);
+ }
+ 
++static inline int hostapd_drv_if_rename(struct hostapd_data *hapd,
++					enum wpa_driver_if_type type,
++					const char *ifname,
++					const char *new_name)
++{
++	if (!hapd->driver || !hapd->driver->if_rename || !hapd->drv_priv)
++		return -1;
++	return hapd->driver->if_rename(hapd->drv_priv, type, ifname, new_name);
++}
++
++static inline int hostapd_drv_set_first_bss(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->driver->set_first_bss || !hapd->drv_priv)
++		return 0;
++	return hapd->driver->set_first_bss(hapd->drv_priv);
++}
++
+ static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
+ 					   struct wpa_channel_info *ci)
+ {
+diff --git a/src/ap/apup.c b/src/ap/apup.c
+new file mode 100644
+index 000000000..f736ddc8e
+--- /dev/null
++++ b/src/ap/apup.c
+@@ -0,0 +1,168 @@
++/*
++ * hostapd / APuP Access Point Micro Peering
++ *
++ * Copyright (C) 2023-2024  Gioacchino Mazzurco <gio@polymathes.cc>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++/* Be extremely careful altering include order, move just one in the wrong place
++ * and you will start getting a bunch of error of undefined bool, size_t etc. */
++
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "utils/os.h"
++
++#include "apup.h"
++
++#include "drivers/driver.h"
++#include "wpa_auth.h"
++#include "ap_mlme.h"
++#include "ieee802_11.h"
++#include "ap_drv_ops.h"
++#include "sta_info.h"
++
++#ifdef UBUS_SUPPORT
++#	include "ubus.h"
++#endif
++
++#ifdef UCODE_SUPPORT
++#	include "ucode.h"
++#endif
++
++void apup_process_beacon(struct hostapd_data *hapd,
++              const struct ieee80211_mgmt *mgmt, size_t len,
++              const struct ieee802_11_elems *elems )
++{
++	if (!os_memcmp(hapd->own_addr, mgmt->bssid, ETH_ALEN))
++	{
++		wpa_printf(MSG_WARNING,
++		           "apup_process_beacon(...) own beacon elems.ssid %.*s",
++		           (int) elems->ssid_len, elems->ssid);
++		return;
++	}
++
++	if (elems->ssid_len != hapd->conf->ssid.ssid_len ||
++	        os_memcmp(elems->ssid, hapd->conf->ssid.ssid, elems->ssid_len))
++		return;
++
++	struct sta_info* sta_ret = ap_get_sta(hapd, mgmt->bssid);
++	if (sta_ret)
++		return;
++
++	sta_ret = ap_sta_add(hapd, mgmt->bssid);
++
++	/* TODO: this has been added just to making compiler happy after breaking
++	 * changes introduced in 11a607d121df512e010148bedcb4263a03329dc7 to support
++	 * IEEE80211BE Multi Link Operation. Look at that commit with more time and
++	 * understand what could be a proper implementation in this context too
++	 */
++	const u8 *mld_link_addr = NULL;
++	bool mld_link_sta = false;
++
++	/* First add the station without more information */
++	int aRet = hostapd_sta_add(
++	            hapd, mgmt->bssid, sta_ret->aid, 0,
++	            NULL, 0, 0, NULL, NULL, NULL, 0, NULL, 0, NULL,
++	            sta_ret->flags, 0, 0, 0,
++	            0, // 0 add, 1 set
++	            mld_link_addr, mld_link_sta);
++
++	sta_ret->flags |= WLAN_STA_AUTH;
++	wpa_auth_sm_event(sta_ret->wpa_sm, WPA_AUTH);
++
++	/* TODO: Investigate if supporting WPA or other encryption method is
++	 * possible */
++	sta_ret->auth_alg = WLAN_AUTH_OPEN;
++	mlme_authenticate_indication(hapd, sta_ret);
++
++	sta_ret->capability = le_to_host16(mgmt->u.beacon.capab_info);
++
++	if (sta_ret->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
++		sta_ret->flags |= WLAN_STA_SHORT_PREAMBLE;
++	else
++		sta_ret->flags &= ~WLAN_STA_SHORT_PREAMBLE;
++
++	hostapd_copy_supp_rates(hapd, sta_ret, elems);
++
++	/* Whithout this flag copy_sta_[v]ht_capab will disable [V]HT
++	 * capabilities even if available */
++	if (elems->ht_capabilities || elems->vht_capabilities)
++		sta_ret->flags |= WLAN_STA_WMM;
++
++	copy_sta_ht_capab(hapd, sta_ret, elems->ht_capabilities);
++#ifdef CONFIG_IEEE80211AC
++	copy_sta_vht_capab(hapd, sta_ret, elems->vht_capabilities);
++	copy_sta_vht_oper(hapd, sta_ret, elems->vht_operation);
++	copy_sta_vendor_vht(hapd, sta_ret, elems->vendor_vht, elems->vendor_vht_len);
++#endif // def CONFIG_IEEE80211AC
++#ifdef CONFIG_IEEE80211AX
++	copy_sta_he_capab(hapd, sta_ret, IEEE80211_MODE_AP,
++	                  elems->he_capabilities, elems->he_capabilities_len);
++	copy_sta_he_6ghz_capab(hapd, sta_ret,  elems->he_6ghz_band_cap);
++#endif // def CONFIG_IEEE80211AX
++#ifdef CONFIG_IEEE80211BE
++	copy_sta_eht_capab(hapd, sta_ret,
++	                   IEEE80211_MODE_AP, // TODO: Make sure is the right value
++	                   elems->he_capabilities, elems->he_capabilities_len,
++	                   elems->eht_capabilities, elems->eht_capabilities_len);
++#endif //def CONFIG_IEEE80211BE
++
++	update_ht_state(hapd, sta_ret);
++
++	if (hostapd_get_aid(hapd, sta_ret) < 0)
++	{
++		wpa_printf(MSG_INFO, "apup_process_beacon(...) No room for more AIDs");
++		return;
++	}
++
++	sta_ret->flags |= WLAN_STA_ASSOC_REQ_OK;
++
++	/* Make sure that the previously registered inactivity timer will not
++	 * remove the STA immediately. */
++	sta_ret->timeout_next = STA_NULLFUNC;
++
++	sta_ret->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
++
++	/* Then set the paramethers */
++	int sRet = hostapd_sta_add(
++	            hapd, mgmt->bssid, sta_ret->aid,
++	            sta_ret->capability,
++	            sta_ret->supported_rates, sta_ret->supported_rates_len,
++	            0, // u16 listen_interval TODO ?
++	            sta_ret->ht_capabilities,
++	            sta_ret->vht_capabilities,
++	            sta_ret->he_capab, sta_ret->he_capab_len,
++	            sta_ret->eht_capab, sta_ret->eht_capab_len,
++	            sta_ret->he_6ghz_capab,
++	            sta_ret->flags,
++	            0, // u8 qosinfo
++	            sta_ret->vht_opmode,
++	            0, // int supp_p2p_ps
++	            1, // 0 add, 1 set
++	            mld_link_addr, mld_link_sta);
++
++	ap_sta_set_authorized(hapd, sta_ret, 1);
++	hostapd_set_sta_flags(hapd, sta_ret);
++
++	char mIfname[IFNAMSIZ + 1];
++	os_memset(mIfname, 0, IFNAMSIZ + 1);
++
++	// last param 1 means add 0 means remove
++	int mRet = hostapd_set_wds_sta(
++	            hapd, mIfname, mgmt->bssid, sta_ret->aid, 1);
++
++	wpa_printf(MSG_INFO,
++	           "apup_process_beacon(...) Added APuP peer at %s with flags: %d,"
++	           " capabilities %d",
++	           mIfname, sta_ret->flags, sta_ret->capability);
++
++#ifdef UBUS_SUPPORT
++	hostapd_ubus_notify_apup_newpeer(hapd, mgmt->bssid, mIfname);
++#endif
++
++#ifdef UCODE_SUPPORT
++	hostapd_ucode_apup_newpeer(hapd, mIfname);
++#endif
++}
+diff --git a/src/ap/apup.h b/src/ap/apup.h
+new file mode 100644
+index 000000000..a14a283bb
+--- /dev/null
++++ b/src/ap/apup.h
+@@ -0,0 +1,24 @@
++/*
++ * hostapd / APuP Access Point Micro Peering
++ *
++ * Copyright (C) 2023-2024  Gioacchino Mazzurco <gio@polymathes.cc>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++/* Be extremely careful altering include order, move just one in the wrong place
++ * and you will start getting a bunch of error of undefined bool, size_t etc. */
++
++#include "utils/includes.h"
++#include "utils/common.h"
++
++#include "hostapd.h"
++#include "common/ieee802_11_defs.h"
++
++/** When beacons from other Access Point are received, if the SSID is matching
++ * add them as APuP peers (aka WDS STA to our own AP) the same happens on the
++ * peer when receiving our beacons */
++void apup_process_beacon(struct hostapd_data *hapd,
++              const struct ieee80211_mgmt *mgmt, size_t len,
++              const struct ieee802_11_elems *elems );
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index ddb99ca22..58b561661 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -1439,6 +1439,12 @@ void handle_probe_req(struct hostapd_data *hapd,
+ 	int mld_id;
+ 	u16 links;
+ #endif /* CONFIG_IEEE80211BE */
++	struct hostapd_ubus_request req = {
++		.type = HOSTAPD_UBUS_PROBE_REQ,
++		.mgmt_frame = mgmt,
++		.ssi_signal = ssi_signal,
++		.elems = &elems,
++	};
+ 
+ 	if (hapd->iconf->rssi_ignore_probe_request && ssi_signal &&
+ 	    ssi_signal < hapd->iconf->rssi_ignore_probe_request)
+@@ -1492,7 +1498,7 @@ void handle_probe_req(struct hostapd_data *hapd,
+ 	 * is less likely to see them (Probe Request frame sent on a
+ 	 * neighboring, but partially overlapping, channel).
+ 	 */
+-	if (elems.ds_params &&
++	if (elems.ds_params && 0 &&
+ 	    hapd->iface->current_mode &&
+ 	    (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G ||
+ 	     hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B) &&
+@@ -1625,6 +1631,12 @@ void handle_probe_req(struct hostapd_data *hapd,
+ 	}
+ #endif /* CONFIG_P2P */
+ 
++	if (hostapd_ubus_handle_event(hapd, &req)) {
++		wpa_printf(MSG_DEBUG, "Probe request for " MACSTR " rejected by ubus handler.\n",
++		       MAC2STR(mgmt->sa));
++		return;
++	}
++
+ 	/* TODO: verify that supp_rates contains at least one matching rate
+ 	 * with AP configuration */
+ 
+@@ -1643,7 +1655,7 @@ void handle_probe_req(struct hostapd_data *hapd,
+ 	if (hapd->conf->no_probe_resp_if_max_sta &&
+ 	    is_multicast_ether_addr(mgmt->da) &&
+ 	    is_multicast_ether_addr(mgmt->bssid) &&
+-	    hapd->num_sta >= hapd->conf->max_num_sta &&
++	    hostapd_check_max_sta(hapd) &&
+ 	    !ap_get_sta(hapd, mgmt->sa)) {
+ 		wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
+ 			   " since no room for additional STA",
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index d4d73de19..a1ddbda9f 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -26,6 +26,26 @@
+ #include "taxonomy.h"
+ #include "wnm_ap.h"
+ 
++static const char * hw_mode_str(enum hostapd_hw_mode mode)
++{
++	switch (mode) {
++	case HOSTAPD_MODE_IEEE80211B:
++		return "b";
++	case HOSTAPD_MODE_IEEE80211G:
++		return "g";
++	case HOSTAPD_MODE_IEEE80211A:
++		return "a";
++	case HOSTAPD_MODE_IEEE80211AD:
++		return "ad";
++	case HOSTAPD_MODE_IEEE80211ANY:
++		return "any";
++	case NUM_HOSTAPD_MODES:
++		return "invalid";
++	}
++	return "unknown";
++}
++
++#ifdef CONFIG_CTRL_IFACE_MIB
+ 
+ static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen,
+ 					   size_t curr_len, const u8 *mcs_set)
+@@ -212,26 +232,6 @@ static const char * timeout_next_str(int val)
+ }
+ 
+ 
+-static const char * hw_mode_str(enum hostapd_hw_mode mode)
+-{
+-	switch (mode) {
+-	case HOSTAPD_MODE_IEEE80211B:
+-		return "b";
+-	case HOSTAPD_MODE_IEEE80211G:
+-		return "g";
+-	case HOSTAPD_MODE_IEEE80211A:
+-		return "a";
+-	case HOSTAPD_MODE_IEEE80211AD:
+-		return "ad";
+-	case HOSTAPD_MODE_IEEE80211ANY:
+-		return "any";
+-	case NUM_HOSTAPD_MODES:
+-		return "invalid";
+-	}
+-	return "unknown";
+-}
+-
+-
+ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
+ 				      struct sta_info *sta,
+ 				      char *buf, size_t buflen)
+@@ -562,6 +562,7 @@ int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
+ 	return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
+ }
+ 
++#endif
+ 
+ #ifdef CONFIG_P2P_MANAGER
+ static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
+@@ -1010,12 +1011,12 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
+ 			return len;
+ 		len += ret;
+ 	}
+-
++#ifdef CONFIG_CTRL_IFACE_MIB
+ 	if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) {
+ 		len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
+ 						   mode->mcs_set);
+ 	}
+-
++#endif /* CONFIG_CTRL_IFACE_MIB */
+ 	if (iface->current_rates && iface->num_rates) {
+ 		ret = os_snprintf(buf + len, buflen - len, "supported_rates=");
+ 		if (os_snprintf_error(buflen - len, ret))
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index af9dc16f5..fe044297b 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -18,6 +18,7 @@
+ #include "ap_drv_ops.h"
+ #include "drivers/driver.h"
+ #include "dfs.h"
++#include "crypto/crypto.h"
+ 
+ 
+ enum dfs_channel_type {
+@@ -527,9 +528,14 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+ 	int num_available_chandefs;
+ 	int chan_idx, chan_idx2;
+ 	int sec_chan_idx_80p80 = -1;
++	bool is_mesh = false;
+ 	int i;
+ 	u32 _rand;
+ 
++#ifdef CONFIG_MESH
++	is_mesh = iface->mconf;
++#endif
++
+ 	wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
+ 	*secondary_channel = 0;
+ 	*oper_centr_freq_seg0_idx = 0;
+@@ -549,8 +555,20 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+ 	if (num_available_chandefs == 0)
+ 		return NULL;
+ 
+-	if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
++	/* try to use deterministic channel in mesh, so that both sides
++	 * have a chance to switch to the same channel */
++	if (is_mesh) {
++#ifdef CONFIG_MESH
++		u64 hash[4];
++		const u8 *meshid[1] = { &iface->mconf->meshid[0] };
++		const size_t meshid_len = iface->mconf->meshid_len;
++
++		sha256_vector(1, meshid, &meshid_len, (u8 *)&hash[0]);
++		_rand = hash[0] + hash[1] + hash[2] + hash[3];
++#endif
++	} else if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
+ 		return NULL;
++
+ 	chan_idx = _rand % num_available_chandefs;
+ 	wpa_printf(MSG_DEBUG, "DFS: Picked random entry from the list: %d/%d",
+ 		   chan_idx, num_available_chandefs);
+@@ -1218,6 +1236,8 @@ int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
+ 		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+ 		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+ 
++	hostapd_ubus_notify_radar_detected(iface, freq, chan_width, cf1, cf2);
++
+ 	/* Proceed only if DFS is not offloaded to the driver */
+ 	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+ 		return 0;
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index c74e551c5..6e76f697a 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -270,6 +270,10 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+ 	struct hostapd_iface *iface = hapd->iface;
+ #endif /* CONFIG_OWE */
+ 	bool updated = false;
++	struct hostapd_ubus_request req = {
++		.type = HOSTAPD_UBUS_ASSOC_REQ,
++		.addr = addr,
++	};
+ 
+ 	if (addr == NULL) {
+ 		/*
+@@ -414,6 +418,12 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+ 		goto fail;
+ 	}
+ 
++	if (hostapd_ubus_handle_event(hapd, &req)) {
++		wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
++			   MAC2STR(req.addr));
++		goto fail;
++	}
++
+ #ifdef CONFIG_P2P
+ 	if (elems.p2p) {
+ 		wpabuf_free(sta->p2p_ie);
+@@ -2416,8 +2426,8 @@ static void hostapd_event_color_change(struct hostapd_data *hapd, bool success)
+ #endif  /* CONFIG_IEEE80211AX */
+ 
+ 
+-void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+-			  union wpa_event_data *data)
++void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
++		       union wpa_event_data *data)
+ {
+ 	struct hostapd_data *hapd = ctx;
+ 	struct sta_info *sta;
+@@ -2776,7 +2786,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ }
+ 
+ 
+-void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
++void hostapd_wpa_event_global(void *ctx, enum wpa_event_type event,
+ 				 union wpa_event_data *data)
+ {
+ 	struct hapd_interfaces *interfaces = ctx;
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 5a8cdc90e..8159194e1 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -247,6 +247,29 @@ static int hostapd_iface_conf_changed(struct hostapd_config *newconf,
+ 	return 0;
+ }
+ 
++static inline int hostapd_iface_num_sta(struct hostapd_iface *iface)
++{
++	int num_sta = 0;
++	int i;
++
++	for (i = 0; i < iface->num_bss; i++)
++		num_sta += iface->bss[i]->num_sta;
++
++	return num_sta;
++}
++
++
++int hostapd_check_max_sta(struct hostapd_data *hapd)
++{
++	if (hapd->num_sta >= hapd->conf->max_num_sta)
++		return 1;
++
++	if (hapd->iconf->max_num_sta &&
++	    hostapd_iface_num_sta(hapd->iface) >= hapd->iconf->max_num_sta)
++		return 1;
++
++	return 0;
++}
+ 
+ int hostapd_reload_config(struct hostapd_iface *iface)
+ {
+@@ -255,6 +278,8 @@ int hostapd_reload_config(struct hostapd_iface *iface)
+ 	struct hostapd_config *newconf, *oldconf;
+ 	size_t j;
+ 
++	hostapd_ucode_reload_bss(hapd);
++
+ 	if (iface->config_fname == NULL) {
+ 		/* Only in-memory config in use - assume it has been updated */
+ 		hostapd_clear_old(iface);
+@@ -475,6 +500,8 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
+ 	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);
+ 	vlan_deinit(hapd);
+@@ -485,7 +512,7 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
+ 		struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+ 		size_t i;
+ 
+-		for (i = 0; i < ifaces->count; i++) {
++		for (i = 0; ifaces && i < ifaces->count; i++) {
+ 			struct hostapd_iface *iface = ifaces->iface[i];
+ 			size_t j;
+ 
+@@ -685,6 +712,7 @@ static void sta_track_deinit(struct hostapd_iface *iface)
+ void hostapd_cleanup_iface_partial(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);
+ #ifdef NEED_AP_MLME
+ 	hostapd_stop_setup_timers(iface);
+@@ -1304,6 +1332,9 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
+ 	if (hapd->driver && hapd->driver->set_operstate)
+ 		hapd->driver->set_operstate(hapd->drv_priv, 1);
+ 
++	hostapd_ubus_add_bss(hapd);
++	hostapd_ucode_add_bss(hapd);
++
+ 	return 0;
+ }
+ 
+@@ -1336,6 +1367,7 @@ static int hostapd_bss_radius_init(struct hostapd_data *hapd)
+ 
+ 		os_memset(&das_conf, 0, sizeof(das_conf));
+ 		das_conf.port = conf->radius_das_port;
++		das_conf.nas_identifier = conf->nas_identifier;
+ 		das_conf.shared_secret = conf->radius_das_shared_secret;
+ 		das_conf.shared_secret_len =
+ 			conf->radius_das_shared_secret_len;
+@@ -1378,8 +1410,7 @@ static int hostapd_bss_radius_init(struct hostapd_data *hapd)
+  * 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];
+@@ -2510,6 +2541,7 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
+ 	if (err)
+ 		goto fail;
+ 
++	hostapd_ubus_add_iface(iface);
+ 	wpa_printf(MSG_DEBUG, "Completing interface initialization");
+ 	if (iface->freq) {
+ #ifdef NEED_AP_MLME
+@@ -2739,6 +2771,7 @@ dfs_offload:
+ 
+ fail:
+ 	wpa_printf(MSG_ERROR, "Interface initialization failed");
++	hostapd_ubus_free_iface(iface);
+ 
+ 	if (iface->is_no_ir) {
+ 		hostapd_set_state(iface, HAPD_IFACE_NO_IR);
+@@ -2938,7 +2971,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+ }
+ 
+ 
+-static void hostapd_bss_deinit(struct hostapd_data *hapd)
++void hostapd_bss_deinit(struct hostapd_data *hapd)
+ {
+ 	if (!hapd)
+ 		return;
+@@ -3471,6 +3504,7 @@ void hostapd_interface_deinit_free(struct hostapd_iface *iface)
+ 		   (unsigned int) iface->conf->num_bss);
+ 	driver = iface->bss[0]->driver;
+ 	drv_priv = iface->bss[0]->drv_priv;
++	hostapd_ubus_free_iface(iface);
+ 	hostapd_interface_deinit(iface);
+ 	wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
+ 		   __func__, driver, drv_priv);
+@@ -4002,7 +4036,8 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
+ 		hapd_iface = interfaces->iface[i];
+ 		if (hapd_iface == NULL)
+ 			return -1;
+-		if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
++		if (!os_strcmp(hapd_iface->phy, buf) ||
++		    !os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
+ 			wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
+ 			hapd_iface->driver_ap_teardown =
+ 				!!(hapd_iface->drv_flags &
+@@ -4048,6 +4083,8 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
+ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ 			   int reassoc)
+ {
++	int mld_assoc_link_id = -1;
++
+ 	if (hapd->tkip_countermeasures) {
+ 		hostapd_drv_sta_deauth(hapd, sta->addr,
+ 				       WLAN_REASON_MICHAEL_MIC_FAILURE);
+@@ -4055,10 +4092,16 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ 	}
+ 
+ #ifdef CONFIG_IEEE80211BE
+-	if (ap_sta_is_mld(hapd, sta) &&
+-	    sta->mld_assoc_link_id != hapd->mld_link_id)
+-		return;
++	if (ap_sta_is_mld(hapd, sta)) {
++		if (sta->mld_assoc_link_id == hapd->mld_link_id) {
++			mld_assoc_link_id = sta->mld_assoc_link_id;
++		} else {
++			return;
++		}
++	}
+ #endif /* CONFIG_IEEE80211BE */
++        if (mld_assoc_link_id != -2)
++		hostapd_prune_associations(hapd, sta->addr, mld_assoc_link_id);
+ 
+ 	ap_sta_clear_disconnect_timeouts(hapd, sta);
+ 	sta->post_csa_sa_query = 0;
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 996977fdf..994e4681e 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -18,6 +18,8 @@
+ #include "utils/list.h"
+ #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 +53,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);
+@@ -169,6 +175,21 @@ struct hostapd_sae_commit_queue {
+ 	u8 msg[];
+ };
+ 
++/**
++ * struct hostapd_openwrt_stats - OpenWrt custom STA/AP statistics
++ */
++struct hostapd_openwrt_stats {
++	struct {
++		u64 neighbor_report_tx;
++	} rrm;
++
++	struct {
++		u64 bss_transition_query_rx;
++		u64 bss_transition_request_tx;
++		u64 bss_transition_response_rx;
++	} wnm;
++};
++
+ /**
+  * struct hostapd_data - hostapd per-BSS data structure
+  */
+@@ -176,6 +197,8 @@ struct hostapd_data {
+ 	struct hostapd_iface *iface;
+ 	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;
+@@ -183,6 +206,9 @@ struct hostapd_data {
+ 
+ 	u8 own_addr[ETH_ALEN];
+ 
++	/* OpenWrt specific statistics */
++	struct hostapd_openwrt_stats openwrt_stats;
++
+ 	int num_sta; /* number of entries in sta_list */
+ 	struct sta_info *sta_list; /* STA info list head */
+ #define STA_HASH_SIZE 256
+@@ -535,6 +561,7 @@ struct hostapd_mld {
+  */
+ struct hostapd_iface {
+ 	struct hapd_interfaces *interfaces;
++	struct hostapd_ucode_iface ucode;
+ 	void *owner;
+ 	char *config_fname;
+ 	struct hostapd_config *conf;
+@@ -786,6 +813,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+ 		       struct hostapd_bss_config *bss);
+ int hostapd_setup_interface(struct hostapd_iface *iface);
+ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
++void hostapd_set_own_neighbor_report(struct hostapd_data *hapd);
+ void hostapd_interface_deinit(struct hostapd_iface *iface);
+ void hostapd_interface_free(struct hostapd_iface *iface);
+ struct hostapd_iface * hostapd_alloc_iface(void);
+@@ -794,6 +822,8 @@ struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
+ 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_bss_deinit(struct hostapd_data *hapd);
+ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ 			   int reassoc);
+ void hostapd_interface_deinit_free(struct hostapd_iface *iface);
+@@ -821,6 +851,7 @@ void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
+ 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);
++int hostapd_check_max_sta(struct hostapd_data *hapd);
+ 
+ void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap);
+ void hostapd_cleanup_cca_params(struct hostapd_data *hapd);
+@@ -908,6 +939,16 @@ static inline bool hostapd_mld_is_first_bss(struct hostapd_data *hapd)
+ 
+ #endif /* CONFIG_IEEE80211BE */
+ 
++static inline bool ap_sta_is_mld(struct hostapd_data *hapd,
++				 struct sta_info *sta)
++{
++#ifdef CONFIG_IEEE80211BE
++	return hapd->conf->mld_ap && sta && sta->mld_info.mld_sta;
++#else /* CONFIG_IEEE80211BE */
++	return false;
++#endif /* CONFIG_IEEE80211BE */
++}
++
+ u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd);
+ 
+ #endif /* HOSTAPD_H */
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index 8aa0b3ab5..400c50988 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -553,7 +553,8 @@ static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
+ 	int ret;
+ 
+ 	/* Check that HT40 is used and PRI / SEC switch is allowed */
+-	if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)
++	if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch ||
++		iface->conf->noscan)
+ 		return 0;
+ 
+ 	hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index d8d82d737..39b1bb4c7 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -59,6 +59,9 @@
+ #include "nan_usd_ap.h"
+ #include "pasn/pasn_common.h"
+ 
++#ifdef CONFIG_APUP
++#	include "apup.h"
++#endif // def CONFIG_APUP
+ 
+ #ifdef CONFIG_FILS
+ static struct wpabuf *
+@@ -2876,7 +2879,7 @@ static void handle_auth(struct hostapd_data *hapd,
+ 	u16 auth_alg, auth_transaction, status_code;
+ 	u16 resp = WLAN_STATUS_SUCCESS;
+ 	struct sta_info *sta = NULL;
+-	int res, reply_res;
++	int res, reply_res, ubus_resp;
+ 	u16 fc;
+ 	const u8 *challenge = NULL;
+ 	u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
+@@ -2887,6 +2890,11 @@ static void handle_auth(struct hostapd_data *hapd,
+ #ifdef CONFIG_IEEE80211BE
+ 	bool mld_sta = false;
+ #endif /* CONFIG_IEEE80211BE */
++	struct hostapd_ubus_request req = {
++		.type = HOSTAPD_UBUS_AUTH_REQ,
++		.mgmt_frame = mgmt,
++		.ssi_signal = rssi,
++	};
+ 
+ 	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
+ 		wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
+@@ -3083,6 +3091,13 @@ static void handle_auth(struct hostapd_data *hapd,
+ 		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ 		goto fail;
+ 	}
++	ubus_resp = hostapd_ubus_handle_event(hapd, &req);
++	if (ubus_resp) {
++		wpa_printf(MSG_DEBUG, "Station " MACSTR " rejected by ubus handler.\n",
++			MAC2STR(mgmt->sa));
++		resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
++		goto fail;
++	}
+ 	if (res == HOSTAPD_ACL_PENDING)
+ 		return;
+ 
+@@ -3555,8 +3570,8 @@ static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta,
+ }
+ 
+ 
+-static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
+-			   struct ieee802_11_elems *elems)
++u16 hostapd_copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
++			   const struct ieee802_11_elems *elems)
+ {
+ 	/* Supported rates not used in IEEE 802.11ad/DMG */
+ 	if (hapd->iface->current_mode &&
+@@ -3943,7 +3958,7 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+ 			       elems->ext_capab_len);
+ 	if (resp != WLAN_STATUS_SUCCESS)
+ 		return resp;
+-	resp = copy_supp_rates(hapd, sta, elems);
++	resp = hostapd_copy_supp_rates(hapd, sta, elems);
+ 	if (resp != WLAN_STATUS_SUCCESS)
+ 		return resp;
+ 
+@@ -4763,6 +4778,13 @@ static int add_associated_sta(struct hostapd_data *hapd,
+ 	 * drivers to accept the STA parameter configuration. Since this is
+ 	 * after a new FT-over-DS exchange, a new TK has been derived, so key
+ 	 * reinstallation is not a concern for this case.
++	 *
++	 * If the STA was associated and authorized earlier, but came for a new
++	 * connection (!added_unassoc + !reassoc), remove the existing STA entry
++	 * so that it can be re-added. This case is rarely seen when the AP could
++	 * not receive the deauth/disassoc frame from the STA. And the STA comes
++	 * back with new connection within a short period or before the inactive
++	 * STA entry is removed from the list.
+ 	 */
+ 	wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR
+ 		   " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)",
+@@ -4776,7 +4798,8 @@ static int add_associated_sta(struct hostapd_data *hapd,
+ 	    (!(sta->flags & WLAN_STA_AUTHORIZED) ||
+ 	     (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) ||
+ 	     (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
+-	      !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) {
++	      !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)) ||
++	     (!reassoc && (sta->flags & WLAN_STA_AUTHORIZED)))) {
+ 		hostapd_drv_sta_remove(hapd, sta->addr);
+ 		wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
+ 		set = 0;
+@@ -5337,7 +5360,7 @@ static void handle_assoc(struct hostapd_data *hapd,
+ 	int resp = WLAN_STATUS_SUCCESS;
+ 	u16 reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ 	const u8 *pos;
+-	int left, i;
++	int left, i, ubus_resp;
+ 	struct sta_info *sta;
+ 	u8 *tmp = NULL;
+ #ifdef CONFIG_FILS
+@@ -5579,6 +5602,11 @@ static void handle_assoc(struct hostapd_data *hapd,
+ 		left = res;
+ 	}
+ #endif /* CONFIG_FILS */
++	struct hostapd_ubus_request req = {
++		.type = HOSTAPD_UBUS_ASSOC_REQ,
++		.mgmt_frame = mgmt,
++		.ssi_signal = rssi,
++	};
+ 
+ 	/* followed by SSID and Supported rates; and HT capabilities if 802.11n
+ 	 * is used */
+@@ -5681,6 +5709,13 @@ static void handle_assoc(struct hostapd_data *hapd,
+ 	if (set_beacon)
+ 		ieee802_11_update_beacons(hapd->iface);
+ 
++	ubus_resp = hostapd_ubus_handle_event(hapd, &req);
++	if (ubus_resp) {
++		wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
++		       MAC2STR(mgmt->sa));
++		resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
++		goto fail;
++	}
+  fail:
+ 
+ 	/*
+@@ -5910,6 +5945,7 @@ static void handle_disassoc(struct hostapd_data *hapd,
+ 			   (unsigned long) len);
+ 		return;
+ 	}
++	hostapd_ubus_notify(hapd, "disassoc", mgmt->sa);
+ 
+ 	sta = ap_get_sta(hapd, mgmt->sa);
+ 	if (!sta) {
+@@ -5941,6 +5977,8 @@ static void handle_deauth(struct hostapd_data *hapd,
+ 	/* Clear the PTKSA cache entries for PASN */
+ 	ptksa_cache_flush(hapd->ptksa, mgmt->sa, WPA_CIPHER_NONE);
+ 
++	hostapd_ubus_notify(hapd, "deauth", mgmt->sa);
++
+ 	sta = ap_get_sta(hapd, mgmt->sa);
+ 	if (!sta) {
+ 		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
+@@ -5974,6 +6012,11 @@ static void handle_beacon(struct hostapd_data *hapd,
+ 				      0);
+ 
+ 	ap_list_process_beacon(hapd->iface, mgmt, &elems, fi);
++
++#ifdef CONFIG_APUP
++	if (hapd->conf->apup)
++		apup_process_beacon(hapd, mgmt, len, &elems);
++#endif // def CONFIG_APUP
+ }
+ 
+ 
+diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
+index dd4995f3f..0e13d2940 100644
+--- a/src/ap/ieee802_11.h
++++ b/src/ap/ieee802_11.h
+@@ -108,6 +108,8 @@ int hostapd_process_ml_assoc_req_addr(struct hostapd_data *hapd,
+ 				      const u8 *basic_mle, size_t basic_mle_len,
+ 				      u8 *mld_addr);
+ int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
++u16 hostapd_copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
++		      const struct ieee802_11_elems *elems);
+ u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ 		      const u8 *ht_capab);
+ u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
+diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
+index f90f1254e..7f0a00f95 100644
+--- a/src/ap/ieee802_11_ht.c
++++ b/src/ap/ieee802_11_ht.c
+@@ -82,7 +82,9 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
+ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
+ {
+ 	struct ieee80211_ht_operation *oper;
++	le32 vht_capabilities_info;
+ 	u8 *pos = eid;
++	u8 chwidth;
+ 
+ 	if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n ||
+ 	    is_6ghz_op_class(hapd->iconf->op_class))
+@@ -103,6 +105,13 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
+ 		oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
+ 			HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
+ 
++	vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
++	chwidth = hostapd_get_oper_chwidth(hapd->iconf);
++	if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT
++		&& ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
++		oper->operation_mode = host_to_le16(hapd->iconf->vht_oper_centr_freq_seg0_idx << 5);
++	}
++
+ 	pos += sizeof(*oper);
+ 
+ 	return pos;
+@@ -230,6 +239,9 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
+ 		return;
+ 	}
+ 
++	if (iface->conf->noscan || iface->conf->no_ht_coex)
++		return;
++
+ 	if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) {
+ 		wpa_printf(MSG_DEBUG,
+ 			   "Ignore too short 20/40 BSS Coexistence Management frame");
+@@ -390,6 +402,9 @@ void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta)
+ 	if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+ 		return;
+ 
++	if (iface->conf->noscan || iface->conf->no_ht_coex)
++		return;
++
+ 	wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR
+ 		   " in Association Request", MAC2STR(sta->addr));
+ 
+diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
+index 4dc325ce8..68880ab64 100644
+--- a/src/ap/ieee802_11_vht.c
++++ b/src/ap/ieee802_11_vht.c
+@@ -26,6 +26,7 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
+ 	struct ieee80211_vht_capabilities *cap;
+ 	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+ 	u8 *pos = eid;
++	u8 chwidth;
+ 
+ 	if (!mode || is_6ghz_op_class(hapd->iconf->op_class))
+ 		return eid;
+@@ -63,6 +64,17 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
+ 			host_to_le32(nsts << VHT_CAP_BEAMFORMEE_STS_OFFSET);
+ 	}
+ 
++	chwidth = hostapd_get_oper_chwidth(hapd->iconf);
++	if (((host_to_le32(mode->vht_capab)) & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
++		&& ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
++		cap->vht_capabilities_info |= VHT_CAP_EXTENDED_NSS_BW_SUPPORT;
++		cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ));
++		cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ));
++		cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_MASK));
++	} else {
++		cap->vht_capabilities_info &= ~VHT_CAP_EXTENDED_NSS_BW_SUPPORT_MASK;
++	}
++
+ 	/* Supported MCS set comes from hw */
+ 	os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
+ 
+@@ -75,6 +87,7 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
+ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
+ {
+ 	struct ieee80211_vht_operation *oper;
++	le32 vht_capabilities_info;
+ 	u8 *pos = eid;
+ 	enum oper_chan_width oper_chwidth =
+ 		hostapd_get_oper_chwidth(hapd->iconf);
+@@ -110,6 +123,7 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
+ 	oper->vht_op_info_chan_center_freq_seg1_idx = seg1;
+ 
+ 	oper->vht_op_info_chwidth = oper_chwidth;
++	vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
+ 	if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ) {
+ 		/*
+ 		 * Convert 160 MHz channel width to new style as interop
+@@ -123,6 +137,9 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
+ 			oper->vht_op_info_chan_center_freq_seg0_idx -= 8;
+ 		else
+ 			oper->vht_op_info_chan_center_freq_seg0_idx += 8;
++
++		if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
++			oper->vht_op_info_chan_center_freq_seg1_idx = 0;
+ 	} else if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ) {
+ 		/*
+ 		 * Convert 80+80 MHz channel width to new style as interop
+diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
+index f4103ac9a..7b5b45a2b 100644
+--- a/src/ap/ieee802_1x.c
++++ b/src/ap/ieee802_1x.c
+@@ -600,6 +600,10 @@ int add_common_radius_attr(struct hostapd_data *hapd,
+ 	struct hostapd_radius_attr *attr;
+ 	int len;
+ 
++	if (hapd->conf->dynamic_own_ip_addr)
++		radius_client_get_local_addr(hapd->radius,
++					     &hapd->conf->own_ip_addr);
++
+ 	if (!hostapd_config_get_radius_attr(req_attr,
+ 					    RADIUS_ATTR_NAS_IP_ADDRESS) &&
+ 	    hapd->conf->own_ip_addr.af == AF_INET &&
+@@ -2848,6 +2852,7 @@ static const char * bool_txt(bool val)
+ 	return val ? "TRUE" : "FALSE";
+ }
+ 
++#ifdef CONFIG_CTRL_IFACE_MIB
+ 
+ int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
+ {
+@@ -3034,6 +3039,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ 	return len;
+ }
+ 
++#endif
+ 
+ #ifdef CONFIG_HS20
+ static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx)
+diff --git a/src/ap/rrm.c b/src/ap/rrm.c
+index fbcddf3f9..b024499ac 100644
+--- a/src/ap/rrm.c
++++ b/src/ap/rrm.c
+@@ -89,6 +89,9 @@ static void hostapd_handle_beacon_report(struct hostapd_data *hapd,
+ 		return;
+ 	wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s",
+ 		MAC2STR(addr), token, rep_mode, report);
++	if (len < sizeof(struct rrm_measurement_beacon_report))
++		return;
++	hostapd_ubus_notify_beacon_report(hapd, addr, token, rep_mode, (struct rrm_measurement_beacon_report*) pos, len);
+ }
+ 
+ 
+@@ -269,6 +272,8 @@ static void hostapd_send_nei_report_resp(struct hostapd_data *hapd,
+ 		}
+ 	}
+ 
++	hapd->openwrt_stats.rrm.neighbor_report_tx++;
++
+ 	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ 				wpabuf_head(buf), wpabuf_len(buf));
+ 	wpabuf_free(buf);
+@@ -397,15 +402,20 @@ void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
+ 		   mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa));
+ 
+ 	switch (mgmt->u.action.u.rrm.action) {
++	case WLAN_RRM_LINK_MEASUREMENT_REPORT:
++		hostapd_ubus_handle_link_measurement(hapd, buf, len);
++		break;
+ 	case WLAN_RRM_RADIO_MEASUREMENT_REPORT:
+ 		hostapd_handle_radio_msmt_report(hapd, buf, len);
+ 		break;
+ 	case WLAN_RRM_NEIGHBOR_REPORT_REQUEST:
+ 		hostapd_handle_nei_report_req(hapd, buf, len);
+ 		break;
++	/*
+ 	case WLAN_RRM_LINK_MEASUREMENT_REPORT:
+ 		hostapd_handle_link_mesr_report(hapd, buf, len);
+ 		break;
++	*/
+ 	default:
+ 		wpa_printf(MSG_DEBUG, "RRM action %u is not supported",
+ 			   mgmt->u.action.u.rrm.action);
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index 13613dbab..51978f45f 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -542,6 +542,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
+ 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ 			       HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+ 			       "local deauth request");
++		hostapd_ubus_notify(hapd, "local-deauth", sta->addr);
+ 		ap_free_sta(hapd, sta);
+ 		return;
+ 	}
+@@ -699,6 +700,7 @@ skip_poll:
+ 		mlme_deauthenticate_indication(
+ 			hapd, sta,
+ 			WLAN_REASON_PREV_AUTH_NOT_VALID);
++		hostapd_ubus_notify(hapd, "inactive-deauth", sta->addr);
+ 		ap_free_sta(hapd, sta);
+ 		break;
+ 	}
+@@ -1485,9 +1487,6 @@ bool ap_sta_set_authorized_flag(struct hostapd_data *hapd, struct sta_info *sta,
+ 				mld_assoc_link_id = -2;
+ 		}
+ #endif /* CONFIG_IEEE80211BE */
+-		if (mld_assoc_link_id != -2)
+-			hostapd_prune_associations(hapd, sta->addr,
+-						   mld_assoc_link_id);
+ 		sta->flags |= WLAN_STA_AUTHORIZED;
+ 	} else {
+ 		sta->flags &= ~WLAN_STA_AUTHORIZED;
+@@ -1524,15 +1523,28 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
+ 		os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr));
+ 
+ 	if (authorized) {
++		static const char * const auth_algs[] = {
++			[WLAN_AUTH_OPEN] = "open",
++			[WLAN_AUTH_SHARED_KEY] = "shared",
++			[WLAN_AUTH_FT] = "ft",
++			[WLAN_AUTH_SAE] = "sae",
++			[WLAN_AUTH_FILS_SK] = "fils-sk",
++			[WLAN_AUTH_FILS_SK_PFS] = "fils-sk-pfs",
++			[WLAN_AUTH_FILS_PK] = "fils-pk",
++			[WLAN_AUTH_PASN] = "pasn",
++		};
++		const char *auth_alg = NULL;
+ 		const u8 *dpp_pkhash;
+ 		const char *keyid;
+ 		char dpp_pkhash_buf[100];
+ 		char keyid_buf[100];
+ 		char ip_addr[100];
++		char alg_buf[100];
+ 
+ 		dpp_pkhash_buf[0] = '\0';
+ 		keyid_buf[0] = '\0';
+ 		ip_addr[0] = '\0';
++		alg_buf[0] = '\0';
+ #ifdef CONFIG_P2P
+ 		if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
+ 			os_snprintf(ip_addr, sizeof(ip_addr),
+@@ -1543,6 +1555,13 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
+ 		}
+ #endif /* CONFIG_P2P */
+ 
++		if (sta->auth_alg < ARRAY_SIZE(auth_algs))
++			auth_alg = auth_algs[sta->auth_alg];
++
++		if (auth_alg)
++			os_snprintf(alg_buf, sizeof(alg_buf),
++				" auth_alg=%s", auth_alg);
++
+ 		keyid = ap_sta_wpa_get_keyid(hapd, sta);
+ 		if (keyid) {
+ 			os_snprintf(keyid_buf, sizeof(keyid_buf),
+@@ -1561,17 +1580,19 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
+ 					 dpp_pkhash, SHA256_MAC_LEN);
+ 		}
+ 
+-		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s",
+-			buf, ip_addr, keyid_buf, dpp_pkhash_buf);
++		hostapd_ubus_notify_authorized(hapd, sta, auth_alg);
++		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s%s",
++			buf, ip_addr, keyid_buf, dpp_pkhash_buf, alg_buf);
+ 
+ 		if (hapd->msg_ctx_parent &&
+ 		    hapd->msg_ctx_parent != hapd->msg_ctx)
+ 			wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
+-					  AP_STA_CONNECTED "%s%s%s%s",
++					  AP_STA_CONNECTED "%s%s%s%s%s",
+ 					  buf, ip_addr, keyid_buf,
+-					  dpp_pkhash_buf);
++					  dpp_pkhash_buf, alg_buf);
+ 	} else {
+ 		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
++		hostapd_ubus_notify(hapd, "disassoc", sta->addr);
+ 
+ 		if (hapd->msg_ctx_parent &&
+ 		    hapd->msg_ctx_parent != hapd->msg_ctx)
+diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
+index 84629358c..d03d18b48 100644
+--- a/src/ap/sta_info.h
++++ b/src/ap/sta_info.h
+@@ -17,7 +17,6 @@
+ #include "common/sae.h"
+ #include "crypto/sha384.h"
+ #include "pasn/pasn_common.h"
+-#include "hostapd.h"
+ 
+ /* STA flags */
+ #define WLAN_STA_AUTH BIT(0)
+@@ -49,10 +48,6 @@
+ #define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
+ #define WLAN_STA_NONERP BIT(31)
+ 
+-/* Maximum number of supported rates (from both Supported Rates and Extended
+- * Supported Rates IEs). */
+-#define WLAN_SUPP_RATES_MAX 32
+-
+ struct hostapd_data;
+ 
+ struct mbo_non_pref_chan_info {
+@@ -321,6 +316,7 @@ struct sta_info {
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #ifdef CONFIG_AIRTIME_POLICY
+ 	unsigned int airtime_weight;
++	unsigned int dyn_airtime_weight;
+ 	struct os_reltime backlogged_until;
+ #endif /* CONFIG_AIRTIME_POLICY */
+ 
+@@ -421,16 +417,6 @@ int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta);
+ 
+ void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta);
+ 
+-static inline bool ap_sta_is_mld(struct hostapd_data *hapd,
+-				 struct sta_info *sta)
+-{
+-#ifdef CONFIG_IEEE80211BE
+-	return hapd->conf->mld_ap && sta && sta->mld_info.mld_sta;
+-#else /* CONFIG_IEEE80211BE */
+-	return false;
+-#endif /* CONFIG_IEEE80211BE */
+-}
+-
+ static inline void ap_sta_set_mld(struct sta_info *sta, bool mld)
+ {
+ #ifdef CONFIG_IEEE80211BE
+diff --git a/src/ap/ubus.c b/src/ap/ubus.c
+index 8689494bc..f21516fc3 100644
+--- a/src/ap/ubus.c
++++ b/src/ap/ubus.c
+@@ -2004,3 +2004,18 @@ int hostapd_ubus_notify_bss_transition_query(
+ 	return ureq.resp;
+ #endif
+ }
++
++#ifdef CONFIG_APUP
++void hostapd_ubus_notify_apup_newpeer(
++	struct hostapd_data *hapd, const u8 *addr, const char *ifname)
++{
++	if (!hapd->ubus.obj.has_subscribers)
++		return;
++
++	blob_buf_init(&b, 0);
++	blobmsg_add_macaddr(&b, "address", addr);
++	blobmsg_add_string(&b, "ifname", ifname);
++
++	ubus_notify(ctx, &hapd->ubus.obj, "apup-newpeer", b.head, -1);
++}
++#endif // def CONFIG_APUP
+diff --git a/src/ap/ubus.h b/src/ap/ubus.h
+index 22767d67e..1c65e4dcb 100644
+--- a/src/ap/ubus.h
++++ b/src/ap/ubus.h
+@@ -71,6 +71,11 @@ int hostapd_ubus_notify_bss_transition_query(
+ void hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+ 				    const char *auth_alg);
+ 
++#ifdef CONFIG_APUP
++void hostapd_ubus_notify_apup_newpeer(
++	struct hostapd_data *hapd, const u8 *addr, const char *ifname);
++#endif // def CONFIG_APUP
++
+ #else
+ 
+ struct hostapd_ubus_bss {};
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index 68fb45088..3468615fd 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -815,3 +815,20 @@ void hostapd_ucode_free_bss(struct hostapd_data *hapd)
+ 	ucv_put(wpa_ucode_call(2));
+ 	ucv_gc(vm);
+ }
++
++#ifdef CONFIG_APUP
++void hostapd_ucode_apup_newpeer(struct hostapd_data *hapd, const char *ifname)
++{
++	uc_value_t *val;
++
++	if (wpa_ucode_call_prepare("apup_newpeer"))
++		return;
++
++	val = hostapd_ucode_bss_get_uval(hapd);
++	uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface))); // BSS ifname
++	uc_value_push(ucv_get(val));
++	uc_value_push(ucv_get(ucv_string_new(ifname))); // APuP peer ifname
++	ucv_put(wpa_ucode_call(2));
++	ucv_gc(vm);
++}
++#endif // def CONFIG_APUP
+diff --git a/src/ap/ucode.h b/src/ap/ucode.h
+index d00b78716..c9bdde651 100644
+--- a/src/ap/ucode.h
++++ b/src/ap/ucode.h
+@@ -27,6 +27,10 @@ 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);
+ 
++#ifdef CONFIG_APUP
++void hostapd_ucode_apup_newpeer(struct hostapd_data *hapd, const char *ifname);
++#endif // def CONFIG_APUP
++
+ #else
+ 
+ static inline int hostapd_ucode_init(struct hapd_interfaces *ifaces)
+diff --git a/src/ap/vlan_full.c b/src/ap/vlan_full.c
+index 19aa3c649..053d6338e 100644
+--- a/src/ap/vlan_full.c
++++ b/src/ap/vlan_full.c
+@@ -475,6 +475,9 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
+ 	if (!vlan)
+ 		return;
+ 
++	if (hapd->conf->ssid.vlan_no_bridge)
++		goto out;
++
+ 	vlan->configured = 1;
+ 
+ 	notempty = vlan->vlan_desc.notempty;
+@@ -506,6 +509,7 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
+ 				    ifname, br_name, tagged[i], hapd);
+ 	}
+ 
++out:
+ 	ifconfig_up(ifname);
+ }
+ 
+diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
+index 53eacfb45..b69f3de41 100644
+--- a/src/ap/vlan_init.c
++++ b/src/ap/vlan_init.c
+@@ -22,6 +22,7 @@
+ static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+ 		       int existsok)
+ {
++	bool vlan_exists = iface_exists(vlan->ifname);
+ 	int ret;
+ #ifdef CONFIG_WEP
+ 	int i;
+@@ -36,7 +37,7 @@ static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+ 	}
+ #endif /* CONFIG_WEP */
+ 
+-	if (!iface_exists(vlan->ifname))
++	if (!vlan_exists)
+ 		ret = hostapd_vlan_if_add(hapd, vlan->ifname);
+ 	else if (!existsok)
+ 		return -1;
+@@ -51,6 +52,9 @@ static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+ 	if (hapd->wpa_auth)
+ 		ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id);
+ 
++	if (!ret && !vlan_exists)
++		hostapd_ubus_add_vlan(hapd, vlan);
++
+ 	if (ret == 0)
+ 		return ret;
+ 
+@@ -77,6 +81,8 @@ int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+ 			   "WPA deinitialization for VLAN %d failed (%d)",
+ 			   vlan->vlan_id, ret);
+ 
++	hostapd_ubus_remove_vlan(hapd, vlan);
++
+ 	return hostapd_vlan_if_remove(hapd, vlan->ifname);
+ }
+ 
+diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
+index af8cccaef..d259200c9 100644
+--- a/src/ap/wnm_ap.c
++++ b/src/ap/wnm_ap.c
+@@ -410,6 +410,7 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
+ 	mgmt->u.action.u.bss_tm_req.validity_interval = 1;
+ 	pos = mgmt->u.action.u.bss_tm_req.variable;
+ 
++	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 "
+ 		   "validity_interval=%u",
+@@ -478,7 +479,8 @@ static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
+ 		MAC2STR(addr), reason, hex ? " neighbor=" : "", hex);
+ 	os_free(hex);
+ 
+-	ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
++	if (!hostapd_ubus_notify_bss_transition_query(hapd, addr, dialog_token, reason, pos, end - pos))
++		ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
+ }
+ 
+ 
+@@ -500,7 +502,7 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
+ 					      size_t len)
+ {
+ 	u8 dialog_token, status_code, bss_termination_delay;
+-	const u8 *pos, *end;
++	const u8 *pos, *end, *target_bssid = NULL;
+ 	int enabled = hapd->conf->bss_transition;
+ 	struct sta_info *sta;
+ 
+@@ -547,6 +549,7 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
+ 			wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
+ 			return;
+ 		}
++		target_bssid = pos;
+ 		sta->agreed_to_steer = 1;
+ 		eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
+ 		eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
+@@ -566,6 +569,10 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
+ 			MAC2STR(addr), status_code, bss_termination_delay);
+ 	}
+ 
++	hostapd_ubus_notify_bss_transition_response(hapd, sta->addr, dialog_token,
++						    status_code, bss_termination_delay,
++						    target_bssid, pos, end - pos);
++
+ 	wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
+ 		    pos, end - pos);
+ }
+@@ -814,10 +821,12 @@ int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
+ 					       plen);
+ 		return 0;
+ 	case WNM_BSS_TRANS_MGMT_QUERY:
++		hapd->openwrt_stats.wnm.bss_transition_query_rx++;
+ 		ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
+ 						   plen);
+ 		return 0;
+ 	case WNM_BSS_TRANS_MGMT_RESP:
++		hapd->openwrt_stats.wnm.bss_transition_response_rx++;
+ 		ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload,
+ 						  plen);
+ 		return 0;
+@@ -865,6 +874,7 @@ int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
+ 
+ 	pos = mgmt->u.action.u.bss_tm_req.variable;
+ 
++	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+ 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to "
+ 		   MACSTR, disassoc_timer, MAC2STR(sta->addr));
+ 	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
+@@ -947,6 +957,7 @@ int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
+ 		return -1;
+ 	}
+ 
++	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+ 	if (disassoc_timer) {
+ 		/* send disassociation frame after time-out */
+ 		set_disassoc_timer(hapd, sta, disassoc_timer);
+@@ -1028,6 +1039,7 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+ 	}
+ 	os_free(buf);
+ 
++	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+ 	if (disassoc_timer) {
+ #ifdef CONFIG_IEEE80211BE
+ 		if (ap_sta_is_mld(hapd, sta)) {
+diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
+index 93f157d62..3a1d288dd 100644
+--- a/src/ap/wpa_auth.c
++++ b/src/ap/wpa_auth.c
+@@ -6095,6 +6095,7 @@ static const char * wpa_bool_txt(int val)
+ 	return val ? "TRUE" : "FALSE";
+ }
+ 
++#ifdef CONFIG_CTRL_IFACE_MIB
+ 
+ #define RSN_SUITE "%02x-%02x-%02x-%d"
+ #define RSN_SUITE_ARG(s) \
+@@ -6247,7 +6248,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
+ 
+ 	return len;
+ }
+-
++#endif
+ 
+ void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth)
+ {
+diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
+index 13685b7c2..eaded9434 100644
+--- a/src/ap/wpa_auth_glue.c
++++ b/src/ap/wpa_auth_glue.c
+@@ -328,6 +328,7 @@ static void hostapd_wpa_auth_psk_failure_report(void *ctx, const u8 *addr)
+ 	struct hostapd_data *hapd = ctx;
+ 	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POSSIBLE_PSK_MISMATCH MACSTR,
+ 		MAC2STR(addr));
++	hostapd_ubus_notify(hapd, "key-mismatch", addr);
+ }
+ 
+ 
+@@ -1811,8 +1812,12 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
+ 	    wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
+ 		const char *ft_iface;
+ 
+-		ft_iface = hapd->conf->bridge[0] ? hapd->conf->bridge :
+-			   hapd->conf->iface;
++		if (hapd->conf->ft_iface[0])
++			ft_iface = hapd->conf->ft_iface;
++		else if (hapd->conf->bridge[0])
++			ft_iface = hapd->conf->bridge;
++		else
++			ft_iface = hapd->conf->iface;
+ 		hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB,
+ 					  hostapd_rrb_receive, hapd, 1);
+ 		if (!hapd->l2) {
+diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
+index 82d4d5fdd..dfc5c3ecb 100644
+--- a/src/ap/wps_hostapd.c
++++ b/src/ap/wps_hostapd.c
+@@ -394,9 +394,8 @@ static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd,
+ 				bss->wpa_pairwise |= WPA_CIPHER_GCMP;
+ 			else
+ 				bss->wpa_pairwise |= WPA_CIPHER_CCMP;
+-		}
+ #ifndef CONFIG_NO_TKIP
+-		if (cred->encr_type & WPS_ENCR_TKIP)
++		} else if (cred->encr_type & WPS_ENCR_TKIP)
+ 			bss->wpa_pairwise |= WPA_CIPHER_TKIP;
+ #endif /* CONFIG_NO_TKIP */
+ 		bss->rsn_pairwise = bss->wpa_pairwise;
+@@ -1181,8 +1180,7 @@ int hostapd_init_wps(struct hostapd_data *hapd,
+ 					  WPA_CIPHER_GCMP_256)) {
+ 			wps->encr_types |= WPS_ENCR_AES;
+ 			wps->encr_types_rsn |= WPS_ENCR_AES;
+-		}
+-		if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
++		} else if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
+ #ifdef CONFIG_NO_TKIP
+ 			wpa_printf(MSG_INFO, "WPS: TKIP not supported");
+ 			goto fail;
+diff --git a/src/ap/x_snoop.c b/src/ap/x_snoop.c
+index 029f4de23..4c20f137f 100644
+--- a/src/ap/x_snoop.c
++++ b/src/ap/x_snoop.c
+@@ -33,28 +33,31 @@ int x_snoop_init(struct hostapd_data *hapd)
+ 
+ 	hapd->x_snoop_initialized = true;
+ 
+-	if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
++	if (!conf->snoop_iface[0] &&
++	    hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
+ 					 1)) {
+ 		wpa_printf(MSG_DEBUG,
+ 			   "x_snoop: Failed to enable hairpin_mode on the bridge port");
+ 		return -1;
+ 	}
+ 
+-	if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
++	if (!conf->snoop_iface[0] &&
++	    hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
+ 		wpa_printf(MSG_DEBUG,
+ 			   "x_snoop: Failed to enable proxyarp on the bridge port");
+ 		return -1;
+ 	}
+ 
+ 	if (hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
+-					 1)) {
++					 conf->snoop_iface[0] ? conf->snoop_iface : NULL, 1)) {
+ 		wpa_printf(MSG_DEBUG,
+ 			   "x_snoop: Failed to enable accepting gratuitous ARP on the bridge");
+ 		return -1;
+ 	}
+ 
+ #ifdef CONFIG_IPV6
+-	if (hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, 1)) {
++	if (!conf->snoop_iface[0] &&
++	    hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, NULL, 1)) {
+ 		wpa_printf(MSG_DEBUG,
+ 			   "x_snoop: Failed to enable multicast snooping on the bridge");
+ 		return -1;
+@@ -73,8 +76,12 @@ x_snoop_get_l2_packet(struct hostapd_data *hapd,
+ {
+ 	struct hostapd_bss_config *conf = hapd->conf;
+ 	struct l2_packet_data *l2;
++	const char *ifname = conf->bridge;
++
++	if (conf->snoop_iface[0])
++		ifname = conf->snoop_iface;
+ 
+-	l2 = l2_packet_init(conf->bridge, NULL, ETH_P_ALL, handler, hapd, 1);
++	l2 = l2_packet_init(ifname, NULL, ETH_P_ALL, handler, hapd, 1);
+ 	if (l2 == NULL) {
+ 		wpa_printf(MSG_DEBUG,
+ 			   "x_snoop: Failed to initialize L2 packet processing %s",
+@@ -127,9 +134,12 @@ void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+ 
+ void x_snoop_deinit(struct hostapd_data *hapd)
+ {
++	struct hostapd_bss_config *conf = hapd->conf;
++
+ 	if (!hapd->x_snoop_initialized)
+ 		return;
+-	hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0);
++	hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
++				     conf->snoop_iface[0] ? conf->snoop_iface : NULL, 0);
+ 	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0);
+ 	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0);
+ 	hapd->x_snoop_initialized = false;
+diff --git a/src/common/defs.h b/src/common/defs.h
+index 8cca094e8..151b69170 100644
+--- a/src/common/defs.h
++++ b/src/common/defs.h
+@@ -63,6 +63,10 @@
+ 			 WPA_KEY_MGMT_FT_FILS_SHA256 | \
+ 			 WPA_KEY_MGMT_FT_FILS_SHA384)
+ 
++/* Maximum number of supported rates (from both Supported Rates and Extended
++ * Supported Rates IEs). */
++#define WLAN_SUPP_RATES_MAX 32
++
+ static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
+ {
+ 	return !!(akm & (WPA_KEY_MGMT_IEEE8021X |
+diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c
+index f17f95a2c..39d39f429 100644
+--- a/src/common/dpp_crypto.c
++++ b/src/common/dpp_crypto.c
+@@ -269,6 +269,12 @@ int dpp_get_pubkey_hash(struct crypto_ec_key *key, u8 *hash)
+ 
+ struct crypto_ec_key * dpp_gen_keypair(const struct dpp_curve_params *curve)
+ {
++	if (curve == NULL) {
++		wpa_printf(MSG_DEBUG,
++		           "DPP: %s curve must be initialized", __func__);
++		return NULL;
++	}
++
+ 	struct crypto_ec_key *key;
+ 
+ 	wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
+@@ -1582,7 +1588,9 @@ dpp_pkex_derive_Qr(const struct dpp_curve_params *curve, const u8 *mac_resp,
+ 	Pr = crypto_ec_key_get_public_key(Pr_key);
+ 	Qr = crypto_ec_point_init(ec);
+ 	hash_bn = crypto_bignum_init_set(hash, curve->hash_len);
+-	if (!Pr || !Qr || !hash_bn || crypto_ec_point_mul(ec, Pr, hash_bn, Qr))
++	if (!Pr || !Qr || !hash_bn ||
++	    crypto_bignum_mod(hash_bn, crypto_ec_get_prime(ec), hash_bn) ||
++	    crypto_ec_point_mul(ec, Pr, hash_bn, Qr))
+ 		goto fail;
+ 
+ 	if (crypto_ec_point_is_at_infinity(ec, Qr)) {
+diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
+index 2c47bf812..8bd6e994d 100644
+--- a/src/common/hw_features_common.c
++++ b/src/common/hw_features_common.c
+@@ -898,6 +898,7 @@ int ieee80211ac_cap_check(u32 hw, u32 conf)
+ 	VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
+ 	VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
+ 	VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
++	VHT_CAP_CHECK(VHT_CAP_EXTENDED_NSS_BW_SUPPORT);
+ 
+ #undef VHT_CAP_CHECK
+ #undef VHT_CAP_CHECK_MAX
+diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
+index db9e90355..269b1cf97 100644
+--- a/src/common/ieee802_11_defs.h
++++ b/src/common/ieee802_11_defs.h
+@@ -1398,6 +1398,8 @@ struct ieee80211_ampe_ie {
+ #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB     ((u32) BIT(26) | BIT(27))
+ #define VHT_CAP_RX_ANTENNA_PATTERN                  ((u32) BIT(28))
+ #define VHT_CAP_TX_ANTENNA_PATTERN                  ((u32) BIT(29))
++#define VHT_CAP_EXTENDED_NSS_BW_SUPPORT		    ((u32) BIT(30))
++#define VHT_CAP_EXTENDED_NSS_BW_SUPPORT_MASK	    ((u32) BIT(30) | BIT(31))
+ 
+ #define VHT_OPMODE_CHANNEL_WIDTH_MASK		    ((u8) BIT(0) | BIT(1))
+ #define VHT_OPMODE_CHANNEL_RxNSS_MASK		    ((u8) BIT(4) | BIT(5) | \
+diff --git a/src/common/sae.c b/src/common/sae.c
+index a65da6134..36f09e31b 100644
+--- a/src/common/sae.c
++++ b/src/common/sae.c
+@@ -1278,6 +1278,13 @@ void sae_deinit_pt(struct sae_pt *pt)
+ static int sae_derive_commit_element_ecc(struct sae_data *sae,
+ 					 struct crypto_bignum *mask)
+ {
++	if (sae->tmp->pwe_ecc == NULL) {
++		wpa_printf(MSG_DEBUG,
++		           "SAE: %s sae->tmp->pwe_ecc must be initialized",
++		           __func__);
++		return -1;
++	}
++
+ 	/* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
+ 	if (!sae->tmp->own_commit_element_ecc) {
+ 		sae->tmp->own_commit_element_ecc =
+diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c
+index d0c174c05..ba659fe0f 100644
+--- a/src/common/wpa_ctrl.c
++++ b/src/common/wpa_ctrl.c
+@@ -142,7 +142,7 @@ try_again:
+ 		return NULL;
+ 	}
+ 	tries++;
+-#ifdef ANDROID
++
+ 	/* Set client socket file permissions so that bind() creates the client
+ 	 * socket with these permissions and there is no need to try to change
+ 	 * them with chmod() after bind() which would have potential issues with
+@@ -154,7 +154,7 @@ try_again:
+ 	 * operations to allow the response to go through. Those are using the
+ 	 * no-deference-symlinks version to avoid races. */
+ 	fchmod(ctrl->s, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+-#endif /* ANDROID */
++
+ 	if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
+ 		    sizeof(ctrl->local)) < 0) {
+ 		if (errno == EADDRINUSE && tries < 2) {
+@@ -172,7 +172,11 @@ try_again:
+ 		return NULL;
+ 	}
+ 
+-#ifdef ANDROID
++#ifndef ANDROID
++	/* Set group even if we do not have privileges to change owner */
++	lchown(ctrl->local.sun_path, -1, 101);
++	lchown(ctrl->local.sun_path, 101, 101);
++#else
+ 	/* Set group even if we do not have privileges to change owner */
+ 	lchown(ctrl->local.sun_path, -1, AID_WIFI);
+ 	lchown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
+diff --git a/src/crypto/Makefile b/src/crypto/Makefile
+index ce0997091..96bac9476 100644
+--- a/src/crypto/Makefile
++++ b/src/crypto/Makefile
+@@ -1,10 +1,121 @@
+-CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+-CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
+-CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
+ #CFLAGS += -DALL_DH_GROUPS
+ CFLAGS += -DCONFIG_SHA256
+ CFLAGS += -DCONFIG_SHA384
++CFLAGS += -DCONFIG_HMAC_SHA256_KDF
+ CFLAGS += -DCONFIG_HMAC_SHA384_KDF
++
++# crypto_module_tests.c
++CFLAGS += -DCONFIG_MODULE_TESTS
++CFLAGS += -DCONFIG_DPP
++#CFLAGS += -DCONFIG_DPP2
++#CFLAGS += -DCONFIG_DPP3
++CFLAGS += -DCONFIG_ECC
++CFLAGS += -DCONFIG_MESH
++CFLAGS += -DEAP_PSK
++CFLAGS += -DEAP_FAST
++
++ifeq ($(CONFIG_TLS),mbedtls)
++
++# (enable features for 'cd tests; make run-tests CONFIG_TLS=mbedtls')
++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
++CFLAGS += -DCONFIG_DES
++CFLAGS += -DEAP_IKEV2
++CFLAGS += -DEAP_MSCHAPv2
++CFLAGS += -DEAP_SIM
++
++LIB_OBJS = tls_mbedtls.o crypto_mbedtls.o
++LIB_OBJS+= \
++	aes-eax.o \
++	aes-siv.o \
++	dh_groups.o \
++	milenage.o \
++	ms_funcs.o
++
++else
++ifeq ($(CONFIG_TLS),openssl)
++
++# (enable features for 'cd tests; make run-tests CONFIG_TLS=openssl')
++ifndef CONFIG_TLS_DEFAULT_CIPHERS
++CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
++endif
++CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
++CFLAGS += -DEAP_TLS_OPENSSL
++
++LIB_OBJS = tls_openssl.o fips_prf_openssl.o crypto_openssl.o
++LIB_OBJS+= \
++	aes-ctr.o \
++	aes-eax.o \
++	aes-encblock.o \
++	aes-siv.o \
++	dh_groups.o \
++	milenage.o \
++	ms_funcs.o \
++	sha1-prf.o \
++	sha1-tlsprf.o \
++	sha1-tprf.o \
++	sha256-kdf.o \
++	sha256-prf.o \
++	sha256-tlsprf.o
++
++else
++ifeq ($(CONFIG_TLS),wolfssl)
++
++# (wolfssl libraries must be built with ./configure --enable-wpas)
++# (enable features for 'cd tests; make run-tests CONFIG_TLS=wolfssl')
++CFLAGS += -DWOLFSSL_DER_LOAD
++CFLAGS += -DCONFIG_DES
++
++LIB_OBJS = tls_wolfssl.o fips_prf_wolfssl.o crypto_wolfssl.o
++LIB_OBJS+= \
++	aes-ctr.o \
++	aes-eax.o \
++	aes-encblock.o \
++	aes-siv.o \
++	dh_groups.o \
++	milenage.o \
++	ms_funcs.o \
++	sha1-prf.o \
++	sha1-tlsprf.o \
++	sha1-tprf.o \
++	sha256-kdf.o \
++	sha256-prf.o \
++	sha256-tlsprf.o
++
++else
++ifeq ($(CONFIG_TLS),gnutls)
++
++# (enable features for 'cd tests; make run-tests CONFIG_TLS=gnutls')
++LIB_OBJS = tls_gnutls.o crypto_gnutls.o
++LIB_OBJS+= \
++	aes-cbc.o \
++	aes-ctr.o \
++	aes-eax.o \
++	aes-encblock.o \
++	aes-omac1.o \
++	aes-siv.o \
++	aes-unwrap.o \
++	aes-wrap.o \
++	dh_group5.o \
++	dh_groups.o \
++	milenage.o \
++	ms_funcs.o \
++	rc4.o \
++	sha1-pbkdf2.o \
++	sha1-prf.o \
++	fips_prf_internal.o \
++	sha1-internal.o \
++	sha1-tlsprf.o \
++	sha1-tprf.o \
++	sha256-kdf.o \
++	sha256-prf.o \
++	sha256-tlsprf.o
++
++else
++
++CFLAGS += -DCONFIG_CRYPTO_INTERNAL
++CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
++CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
+ CFLAGS += -DCONFIG_INTERNAL_SHA384
+ 
+ LIB_OBJS= \
+@@ -13,7 +124,6 @@ LIB_OBJS= \
+ 	aes-ctr.o \
+ 	aes-eax.o \
+ 	aes-encblock.o \
+-	aes-gcm.o \
+ 	aes-internal.o \
+ 	aes-internal-dec.o \
+ 	aes-internal-enc.o \
+@@ -37,6 +147,7 @@ LIB_OBJS= \
+ 	sha1-tlsprf.o \
+ 	sha1-tprf.o \
+ 	sha256.o \
++	sha256-kdf.o \
+ 	sha256-prf.o \
+ 	sha256-tlsprf.o \
+ 	sha256-internal.o \
+@@ -53,6 +164,16 @@ LIB_OBJS += crypto_internal-modexp.o
+ LIB_OBJS += crypto_internal-rsa.o
+ LIB_OBJS += tls_internal.o
+ LIB_OBJS += fips_prf_internal.o
++
++endif
++endif
++endif
++endif
++
++
++# (used by wlantest/{bip,gcmp,rx_mgmt}.c and tests/test-aes.c)
++LIB_OBJS += aes-gcm.o
++
+ ifndef TEST_FUZZ
+ LIB_OBJS += random.o
+ endif
+diff --git a/src/crypto/crypto_mbedtls.c b/src/crypto/crypto_mbedtls.c
+new file mode 100644
+index 000000000..7a91c965f
+--- /dev/null
++++ b/src/crypto/crypto_mbedtls.c
+@@ -0,0 +1,4228 @@
++/*
++ * crypto wrapper functions for mbed TLS
++ *
++ * SPDX-FileCopyrightText: 2022 Glenn Strauss <gstrauss@gluelogic.com>
++ * SPDX-License-Identifier: BSD-3-Clause
++ */
++
++#include "utils/includes.h"
++#include "utils/common.h"
++
++#include <mbedtls/version.h>
++#include <mbedtls/entropy.h>
++#include <mbedtls/ctr_drbg.h>
++#include <mbedtls/platform_util.h> /* mbedtls_platform_zeroize() */
++#include <mbedtls/asn1.h>
++#include <mbedtls/asn1write.h>
++#include <mbedtls/aes.h>
++#include <mbedtls/md.h>
++#include <mbedtls/md5.h>
++#include <mbedtls/sha1.h>
++#include <mbedtls/sha256.h>
++#include <mbedtls/sha512.h>
++
++#ifndef MBEDTLS_PRIVATE
++#define MBEDTLS_PRIVATE(x) x
++#endif
++
++/* hostapd/wpa_supplicant provides forced_memzero(),
++ * but prefer mbedtls_platform_zeroize() */
++#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz)
++
++#ifndef __has_attribute
++#define __has_attribute(x) 0
++#endif
++
++#ifndef __GNUC_PREREQ
++#define __GNUC_PREREQ(maj,min) 0
++#endif
++
++#ifndef __attribute_cold__
++#if __has_attribute(cold) \
++ || __GNUC_PREREQ(4,3)
++#define __attribute_cold__  __attribute__((__cold__))
++#else
++#define __attribute_cold__
++#endif
++#endif
++
++#ifndef __attribute_noinline__
++#if __has_attribute(noinline) \
++ || __GNUC_PREREQ(3,1)
++#define __attribute_noinline__  __attribute__((__noinline__))
++#else
++#define __attribute_noinline__
++#endif
++#endif
++
++#include "crypto.h"
++#include "aes_wrap.h"
++#include "aes.h"
++#include "md5.h"
++#include "sha1.h"
++#include "sha256.h"
++#include "sha384.h"
++#include "sha512.h"
++
++
++/*
++ * selective code inclusion based on preprocessor defines
++ *
++ * future: additional code could be wrapped with preprocessor checks if
++ * wpa_supplicant/Makefile and hostap/Makefile were more consistent with
++ * setting preprocessor defines for named groups of functionality
++ */
++
++#if defined(CONFIG_FIPS)
++#undef MBEDTLS_MD4_C     /* omit md4_vector() */
++#undef MBEDTLS_MD5_C     /* omit md5_vector() hmac_md5_vector() hmac_md5() */
++#undef MBEDTLS_DES_C     /* omit des_encrypt() */
++#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */
++#define CRYPTO_MBEDTLS_CONFIG_FIPS
++#endif
++
++#if !defined(CONFIG_FIPS)
++#if defined(EAP_PWD) \
++ || defined(EAP_LEAP) || defined(EAP_LEAP_DYNAMIC) \
++ || defined(EAP_TTLS) || defined(EAP_TTLS_DYNAMIC) \
++ || defined(EAP_MSCHAPv2) || defined(EAP_MSCHAPv2_DYNAMIC) \
++ || defined(EAP_SERVER_MSCHAPV2)
++#ifndef MBEDTLS_MD4_C    /* (MD4 not in mbedtls 3.x) */
++#include "md4-internal.c"/* pull in hostap local implementation */
++#endif /* md4_vector() */
++#else
++#undef MBEDTLS_MD4_C     /* omit md4_vector() */
++#endif
++#endif
++
++#if !defined(CONFIG_NO_RC4) && !defined(CONFIG_NO_WPA)
++#ifndef MBEDTLS_ARC4_C   /* (RC4 not in mbedtls 3.x) */
++#include "rc4.c"         /* pull in hostap local implementation */
++#endif /* rc4_skip() */
++#else
++#undef MBEDTLS_ARC4_C    /* omit rc4_skip() */
++#endif
++
++#if defined(CONFIG_MACSEC)     \
++ || defined(CONFIG_NO_RADIUS)  \
++ || defined(CONFIG_IEEE80211R) \
++ || defined(EAP_SERVER_FAST)   \
++ || defined(EAP_SERVER_TEAP)   \
++ || !defined(CONFIG_NO_WPA)
++       /* aes_wrap() aes_unwrap() */
++#else
++#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */
++#endif
++
++#if !defined(CONFIG_SHA256)
++#undef MBEDTLS_SHA256_C
++#endif
++
++#if !defined(CONFIG_SHA384) && !defined(CONFIG_SHA512)
++#undef MBEDTLS_SHA512_C
++#endif
++
++#if defined(CONFIG_HMAC_SHA256_KDF)
++#define CRYPTO_MBEDTLS_HMAC_KDF_SHA256
++#endif
++#if defined(CONFIG_HMAC_SHA384_KDF)
++#define CRYPTO_MBEDTLS_HMAC_KDF_SHA384
++#endif
++#if defined(CONFIG_HMAC_SHA512_KDF)
++#define CRYPTO_MBEDTLS_HMAC_KDF_SHA512
++#endif
++
++#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \
++ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA)
++/* EAP_SIM=y EAP_AKA=y */
++#define CRYPTO_MBEDTLS_FIPS186_2_PRF
++#endif
++
++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
++ || defined(EAP_TEAP) || defined(EAP_TEAP_DYNAMIC) || defined(EAP_SERVER_FAST)
++#define CRYPTO_MBEDTLS_SHA1_T_PRF
++#endif
++
++#if defined(CONFIG_DES)
++#define CRYPTO_MBEDTLS_DES_ENCRYPT
++#endif /* des_encrypt() */
++
++#if !defined(CONFIG_NO_PBKDF2)
++#define CRYPTO_MBEDTLS_PBKDF2_SHA1
++#endif /* pbkdf2_sha1() */
++
++#if defined(EAP_IKEV2) \
++ || defined(EAP_IKEV2_DYNAMIC) \
++ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */
++#define CRYPTO_MBEDTLS_CRYPTO_CIPHER
++#endif /* crypto_cipher_*() */
++
++#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */
++#define CRYPTO_MBEDTLS_CRYPTO_HASH
++#endif /* crypto_hash_*() */
++
++#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */ \
++ || defined(CONFIG_SAE) /* CONFIG_SAE=y */
++#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++#endif /* crypto_bignum_*() */
++
++#if defined(EAP_PWD)          /* CONFIG_EAP_PWD=y */    \
++ || defined(EAP_EKE)          /* CONFIG_EAP_EKE=y */    \
++ || defined(EAP_EKE_DYNAMIC)  /* CONFIG_EAP_EKE=y */    \
++ || defined(EAP_SERVER_EKE)   /* CONFIG_EAP_EKE=y */    \
++ || defined(EAP_IKEV2)        /* CONFIG_EAP_IKEV2y */   \
++ || defined(EAP_IKEV2_DYNAMIC)/* CONFIG_EAP_IKEV2=y */  \
++ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */  \
++ || defined(CONFIG_SAE)       /* CONFIG_SAE=y */        \
++ || defined(CONFIG_WPS)       /* CONFIG_WPS=y */
++#define CRYPTO_MBEDTLS_CRYPTO_DH
++#if defined(CONFIG_WPS_NFC)
++#define CRYPTO_MBEDTLS_DH5_INIT_FIXED
++#endif /* dh5_init_fixed() */
++#endif /* crypto_dh_*() */
++
++#if !defined(CONFIG_NO_WPA) /* CONFIG_NO_WPA= */
++#define CRYPTO_MBEDTLS_CRYPTO_ECDH
++#endif /* crypto_ecdh_*() */
++
++#if defined(CONFIG_ECC)
++#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++#define CRYPTO_MBEDTLS_CRYPTO_EC
++#endif /* crypto_ec_*() crypto_ec_key_*() */
++
++#if defined(CONFIG_DPP) /* CONFIG_DPP=y */
++#define CRYPTO_MBEDTLS_CRYPTO_EC_DPP /* extra for DPP */
++#define CRYPTO_MBEDTLS_CRYPTO_CSR
++#endif /* crypto_csr_*() */
++
++#if defined(CONFIG_DPP3) /* CONFIG_DPP3=y */
++#define CRYPTO_MBEDTLS_CRYPTO_HPKE
++#endif
++
++#if defined(CONFIG_DPP2) /* CONFIG_DPP2=y */
++#define CRYPTO_MBEDTLS_CRYPTO_PKCS7
++#endif /* crypto_pkcs7_*() */
++
++#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \
++ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA) \
++ || defined(CONFIG_AP) || defined(HOSTAPD)
++/* CONFIG_EAP_SIM=y CONFIG_EAP_AKA=y CONFIG_AP=y HOSTAPD */
++#if defined(CRYPTO_RSA_OAEP_SHA256)
++#define CRYPTO_MBEDTLS_CRYPTO_RSA
++#endif
++#endif /* crypto_rsa_*() */
++
++
++static int ctr_drbg_init_state;
++static mbedtls_ctr_drbg_context ctr_drbg;
++static mbedtls_entropy_context entropy;
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++#include <mbedtls/bignum.h>
++static mbedtls_mpi mpi_sw_A;
++#endif
++
++__attribute_cold__
++__attribute_noinline__
++static mbedtls_ctr_drbg_context * ctr_drbg_init(void)
++{
++	mbedtls_ctr_drbg_init(&ctr_drbg);
++	mbedtls_entropy_init(&entropy);
++	if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
++	                          NULL, 0)) {
++		wpa_printf(MSG_ERROR, "Init of random number generator failed");
++		/* XXX: abort? */
++	}
++	else
++		ctr_drbg_init_state = 1;
++
++	return &ctr_drbg;
++}
++
++__attribute_cold__
++void crypto_unload(void)
++{
++	if (ctr_drbg_init_state) {
++		mbedtls_ctr_drbg_free(&ctr_drbg);
++		mbedtls_entropy_free(&entropy);
++	  #ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++		mbedtls_mpi_free(&mpi_sw_A);
++	  #endif
++		ctr_drbg_init_state = 0;
++	}
++}
++
++/* init ctr_drbg on first use
++ * crypto_global_init() and crypto_global_deinit() are not available here
++ * (available only when CONFIG_TLS=internal, which is not CONFIG_TLS=mbedtls) */
++mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/
++inline
++mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void)
++{
++	return ctr_drbg_init_state ? &ctr_drbg : ctr_drbg_init();
++}
++
++#ifdef CRYPTO_MBEDTLS_CONFIG_FIPS
++int crypto_get_random(void *buf, size_t len)
++{
++	return mbedtls_ctr_drbg_random(crypto_mbedtls_ctr_drbg(),buf,len) ? -1 : 0;
++}
++#endif
++
++
++#if 1
++
++/* tradeoff: slightly smaller code size here at cost of slight increase
++ * in instructions and function calls at runtime versus the expanded
++ * per-message-digest code that follows in #else (~0.5 kib .text larger) */
++
++__attribute_noinline__
++static int md_vector(size_t num_elem, const u8 *addr[], const size_t *len,
++                     u8 *mac, mbedtls_md_type_t md_type)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	mbedtls_md_context_t ctx;
++	mbedtls_md_init(&ctx);
++	if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0) != 0){
++		mbedtls_md_free(&ctx);
++		return -1;
++	}
++	mbedtls_md_starts(&ctx);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_md_update(&ctx, addr[i], len[i]);
++	mbedtls_md_finish(&ctx, mac);
++	mbedtls_md_free(&ctx);
++	return 0;
++}
++
++#ifdef MBEDTLS_SHA512_C
++int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA512);
++}
++
++int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA384);
++}
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA256);
++}
++#endif
++
++#ifdef MBEDTLS_SHA1_C
++int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA1);
++}
++#endif
++
++#ifdef MBEDTLS_MD5_C
++int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD5);
++}
++#endif
++
++#ifdef MBEDTLS_MD4_C
++#include <mbedtls/md4.h>
++int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD4);
++}
++#endif
++
++#else  /* expanded per-message-digest functions */
++
++#ifdef MBEDTLS_SHA512_C
++#include <mbedtls/sha512.h>
++__attribute_noinline__
++static int sha384_512_vector(size_t num_elem, const u8 *addr[],
++                             const size_t *len, u8 *mac, int is384)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	struct mbedtls_sha512_context ctx;
++	mbedtls_sha512_init(&ctx);
++  #if MBEDTLS_VERSION_MAJOR >= 3
++	mbedtls_sha512_starts(&ctx, is384);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_sha512_update(&ctx, addr[i], len[i]);
++	mbedtls_sha512_finish(&ctx, mac);
++  #else
++	mbedtls_sha512_starts_ret(&ctx, is384);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_sha512_update_ret(&ctx, addr[i], len[i]);
++	mbedtls_sha512_finish_ret(&ctx, mac);
++  #endif
++	mbedtls_sha512_free(&ctx);
++	return 0;
++}
++
++int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return sha384_512_vector(num_elem, addr, len, mac, 0);
++}
++
++int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return sha384_512_vector(num_elem, addr, len, mac, 1);
++}
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++#include <mbedtls/sha256.h>
++int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	struct mbedtls_sha256_context ctx;
++	mbedtls_sha256_init(&ctx);
++  #if MBEDTLS_VERSION_MAJOR >= 3
++	mbedtls_sha256_starts(&ctx, 0);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_sha256_update(&ctx, addr[i], len[i]);
++	mbedtls_sha256_finish(&ctx, mac);
++  #else
++	mbedtls_sha256_starts_ret(&ctx, 0);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_sha256_update_ret(&ctx, addr[i], len[i]);
++	mbedtls_sha256_finish_ret(&ctx, mac);
++  #endif
++	mbedtls_sha256_free(&ctx);
++	return 0;
++}
++#endif
++
++#ifdef MBEDTLS_SHA1_C
++#include <mbedtls/sha1.h>
++int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	struct mbedtls_sha1_context ctx;
++	mbedtls_sha1_init(&ctx);
++  #if MBEDTLS_VERSION_MAJOR >= 3
++	mbedtls_sha1_starts(&ctx);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_sha1_update(&ctx, addr[i], len[i]);
++	mbedtls_sha1_finish(&ctx, mac);
++  #else
++	mbedtls_sha1_starts_ret(&ctx);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_sha1_update_ret(&ctx, addr[i], len[i]);
++	mbedtls_sha1_finish_ret(&ctx, mac);
++  #endif
++	mbedtls_sha1_free(&ctx);
++	return 0;
++}
++#endif
++
++#ifdef MBEDTLS_MD5_C
++#include <mbedtls/md5.h>
++int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	struct mbedtls_md5_context ctx;
++	mbedtls_md5_init(&ctx);
++  #if MBEDTLS_VERSION_MAJOR >= 3
++	mbedtls_md5_starts(&ctx);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_md5_update(&ctx, addr[i], len[i]);
++	mbedtls_md5_finish(&ctx, mac);
++  #else
++	mbedtls_md5_starts_ret(&ctx);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_md5_update_ret(&ctx, addr[i], len[i]);
++	mbedtls_md5_finish_ret(&ctx, mac);
++  #endif
++	mbedtls_md5_free(&ctx);
++	return 0;
++}
++#endif
++
++#ifdef MBEDTLS_MD4_C
++#include <mbedtls/md4.h>
++int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	struct mbedtls_md4_context ctx;
++	mbedtls_md4_init(&ctx);
++	mbedtls_md4_starts_ret(&ctx);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_md4_update_ret(&ctx, addr[i], len[i]);
++	mbedtls_md4_finish_ret(&ctx, mac);
++	mbedtls_md4_free(&ctx);
++	return 0;
++}
++#endif
++
++#endif /* expanded per-message-digest functions */
++
++
++__attribute_noinline__
++static int hmac_vector(const u8 *key, size_t key_len, size_t num_elem,
++                       const u8 *addr[], const size_t *len, u8 *mac,
++                       mbedtls_md_type_t md_type)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	mbedtls_md_context_t ctx;
++	mbedtls_md_init(&ctx);
++	if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1) != 0){
++		mbedtls_md_free(&ctx);
++		return -1;
++	}
++	mbedtls_md_hmac_starts(&ctx, key, key_len);
++	for (size_t i = 0; i < num_elem; ++i)
++		mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++	mbedtls_md_hmac_finish(&ctx, mac);
++	mbedtls_md_free(&ctx);
++	return 0;
++}
++
++#ifdef MBEDTLS_SHA512_C
++int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
++                       const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return hmac_vector(key, key_len, num_elem, addr, len, mac,
++			   MBEDTLS_MD_SHA512);
++}
++
++int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++                u8 *mac)
++{
++	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++			   MBEDTLS_MD_SHA512);
++}
++
++int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
++                       const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return hmac_vector(key, key_len, num_elem, addr, len, mac,
++			   MBEDTLS_MD_SHA384);
++}
++
++int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++                u8 *mac)
++{
++	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++			   MBEDTLS_MD_SHA384);
++}
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
++                       const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return hmac_vector(key, key_len, num_elem, addr, len, mac,
++			   MBEDTLS_MD_SHA256);
++}
++
++int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++                u8 *mac)
++{
++	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++			   MBEDTLS_MD_SHA256);
++}
++#endif
++
++#ifdef MBEDTLS_SHA1_C
++int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
++                     const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return hmac_vector(key, key_len, num_elem, addr, len, mac,
++			   MBEDTLS_MD_SHA1);
++}
++
++int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++              u8 *mac)
++{
++	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++			   MBEDTLS_MD_SHA1);
++}
++#endif
++
++#ifdef MBEDTLS_MD5_C
++int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
++                    const u8 *addr[], const size_t *len, u8 *mac)
++{
++	return hmac_vector(key, key_len, num_elem, addr, len, mac,
++			   MBEDTLS_MD_MD5);
++}
++
++int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++             u8 *mac)
++{
++	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
++			   MBEDTLS_MD_MD5);
++}
++#endif
++
++
++#if defined(MBEDTLS_SHA256_C) || defined(MBEDTLS_SHA512_C)
++
++#if defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA256) \
++ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA384) \
++ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA512)
++
++#include <mbedtls/hkdf.h>
++
++/* sha256-kdf.c sha384-kdf.c sha512-kdf.c */
++
++/* HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869) */
++/* HMAC-SHA384 KDF (RFC 5295) and HKDF-Expand(SHA384) (RFC 5869) */
++/* HMAC-SHA512 KDF (RFC 5295) and HKDF-Expand(SHA512) (RFC 5869) */
++__attribute_noinline__
++static int hmac_kdf_expand(const u8 *prk, size_t prk_len,
++                           const char *label, const u8 *info, size_t info_len,
++                           u8 *okm, size_t okm_len, mbedtls_md_type_t md_type)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
++  #ifdef MBEDTLS_HKDF_C
++	if (label == NULL)  /* RFC 5869 HKDF-Expand when (label == NULL) */
++		return mbedtls_hkdf_expand(md_info, prk, prk_len, info,
++		                           info_len, okm, okm_len) ? -1 : 0;
++  #endif
++
++	const size_t mac_len = mbedtls_md_get_size(md_info);
++	/* okm_len must not exceed 255 times hash len (RFC 5869 Section 2.3) */
++	if (okm_len > ((mac_len << 8) - mac_len))
++		return -1;
++
++	mbedtls_md_context_t ctx;
++	mbedtls_md_init(&ctx);
++	if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
++		mbedtls_md_free(&ctx);
++		return -1;
++	}
++	mbedtls_md_hmac_starts(&ctx, prk, prk_len);
++
++	u8 iter = 1;
++	const u8 *addr[4] = { okm, (const u8 *)label, info, &iter };
++	size_t len[4] = { 0, label ? os_strlen(label)+1 : 0, info_len, 1 };
++
++	for (; okm_len >= mac_len; okm_len -= mac_len, ++iter) {
++		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
++			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++		mbedtls_md_hmac_finish(&ctx, okm);
++		mbedtls_md_hmac_reset(&ctx);
++		addr[0] = okm;
++		okm += mac_len;
++		len[0] = mac_len; /*(include digest in subsequent rounds)*/
++	}
++
++	if (okm_len) {
++		u8 hash[MBEDTLS_MD_MAX_SIZE];
++		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
++			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++		mbedtls_md_hmac_finish(&ctx, hash);
++		os_memcpy(okm, hash, okm_len);
++		forced_memzero(hash, mac_len);
++	}
++
++	mbedtls_md_free(&ctx);
++	return 0;
++}
++
++#ifdef MBEDTLS_SHA512_C
++#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA512
++int hmac_sha512_kdf(const u8 *secret, size_t secret_len,
++		    const char *label, const u8 *seed, size_t seed_len,
++		    u8 *out, size_t outlen)
++{
++	return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
++	                       out, outlen, MBEDTLS_MD_SHA512);
++}
++#endif
++
++#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA384
++int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
++		    const char *label, const u8 *seed, size_t seed_len,
++		    u8 *out, size_t outlen)
++{
++	return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
++	                       out, outlen, MBEDTLS_MD_SHA384);
++}
++#endif
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA256
++int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
++		    const char *label, const u8 *seed, size_t seed_len,
++		    u8 *out, size_t outlen)
++{
++	return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
++	                       out, outlen, MBEDTLS_MD_SHA256);
++}
++#endif
++#endif
++
++#endif /* CRYPTO_MBEDTLS_HMAC_KDF_* */
++
++
++/* sha256-prf.c sha384-prf.c sha512-prf.c */
++
++/* hmac_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function */
++__attribute_noinline__
++static int hmac_prf_bits(const u8 *key, size_t key_len, const char *label,
++                         const u8 *data, size_t data_len, u8 *buf,
++                         size_t buf_len_bits, mbedtls_md_type_t md_type)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	mbedtls_md_context_t ctx;
++	mbedtls_md_init(&ctx);
++	const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
++	if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
++		mbedtls_md_free(&ctx);
++		return -1;
++	}
++	mbedtls_md_hmac_starts(&ctx, key, key_len);
++
++	u16 ctr, n_le = host_to_le16(buf_len_bits);
++	const u8 * const addr[] = { (u8 *)&ctr,(u8 *)label,data,(u8 *)&n_le };
++	const size_t len[] =      { 2, os_strlen(label), data_len, 2 };
++	const size_t mac_len = mbedtls_md_get_size(md_info);
++	size_t buf_len = (buf_len_bits + 7) / 8;
++	for (ctr = 1; buf_len >= mac_len; buf_len -= mac_len, ++ctr) {
++	  #if __BYTE_ORDER == __BIG_ENDIAN
++		ctr = host_to_le16(ctr);
++	  #endif
++		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
++			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++		mbedtls_md_hmac_finish(&ctx, buf);
++		mbedtls_md_hmac_reset(&ctx);
++		buf += mac_len;
++	  #if __BYTE_ORDER == __BIG_ENDIAN
++		ctr = le_to_host16(ctr);
++	  #endif
++	}
++
++	if (buf_len) {
++		u8 hash[MBEDTLS_MD_MAX_SIZE];
++	  #if __BYTE_ORDER == __BIG_ENDIAN
++		ctr = host_to_le16(ctr);
++	  #endif
++		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
++			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
++		mbedtls_md_hmac_finish(&ctx, hash);
++		os_memcpy(buf, hash, buf_len);
++		buf += buf_len;
++		forced_memzero(hash, mac_len);
++	}
++
++	/* Mask out unused bits in last octet if it does not use all the bits */
++	if ((buf_len_bits &= 0x7))
++		buf[-1] &= (u8)(0xff << (8 - buf_len_bits));
++
++	mbedtls_md_free(&ctx);
++	return 0;
++}
++
++#ifdef MBEDTLS_SHA512_C
++int sha512_prf(const u8 *key, size_t key_len, const char *label,
++               const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
++{
++	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
++	                     buf_len * 8, MBEDTLS_MD_SHA512);
++}
++
++int sha384_prf(const u8 *key, size_t key_len, const char *label,
++               const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
++{
++	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
++	                     buf_len * 8, MBEDTLS_MD_SHA384);
++}
++#endif
++
++#ifdef MBEDTLS_SHA256_C
++int sha256_prf(const u8 *key, size_t key_len, const char *label,
++               const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
++{
++	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
++	                     buf_len * 8, MBEDTLS_MD_SHA256);
++}
++
++int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
++                    const u8 *data, size_t data_len, u8 *buf,
++                    size_t buf_len_bits)
++{
++	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
++	                     buf_len_bits, MBEDTLS_MD_SHA256);
++}
++#endif
++
++#endif /* MBEDTLS_SHA256_C || MBEDTLS_SHA512_C */
++
++
++#ifdef MBEDTLS_SHA1_C
++
++/* sha1-prf.c */
++
++/* sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) */
++
++int sha1_prf(const u8 *key, size_t key_len, const char *label,
++	     const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
++{
++	/*(note: algorithm differs from hmac_prf_bits() */
++	/*(note: smaller code size instead of expanding hmac_sha1_vector()
++	 * as is done in hmac_prf_bits(); not expecting large num of loops) */
++	u8 counter = 0;
++	const u8 *addr[] = { (u8 *)label, data, &counter };
++	const size_t len[] = { os_strlen(label)+1, data_len, 1 };
++
++	for (; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++counter) {
++		if (hmac_sha1_vector(key, key_len, 3, addr, len, buf))
++			return -1;
++		buf += SHA1_MAC_LEN;
++	}
++
++	if (buf_len) {
++		u8 hash[SHA1_MAC_LEN];
++		if (hmac_sha1_vector(key, key_len, 3, addr, len, hash))
++			return -1;
++		os_memcpy(buf, hash, buf_len);
++		forced_memzero(hash, sizeof(hash));
++	}
++
++	return 0;
++}
++
++#ifdef CRYPTO_MBEDTLS_SHA1_T_PRF
++
++/* sha1-tprf.c */
++
++/* sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF) (RFC 4851,Section 5.5)*/
++
++int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
++	       const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len)
++{
++	/*(note: algorithm differs from hmac_prf_bits() and hmac_kdf() above)*/
++	/*(note: smaller code size instead of expanding hmac_sha1_vector()
++	 * as is done in hmac_prf_bits(); not expecting large num of loops) */
++	u8 ctr;
++	u16 olen = host_to_be16(buf_len);
++	const u8 *addr[] = { buf, (u8 *)label, seed, (u8 *)&olen, &ctr };
++	size_t len[] = { 0, os_strlen(label)+1, seed_len, 2, 1 };
++
++	for (ctr = 1; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++ctr) {
++		if (hmac_sha1_vector(key, key_len, 5, addr, len, buf))
++			return -1;
++		addr[0] = buf;
++		buf += SHA1_MAC_LEN;
++		len[0] = SHA1_MAC_LEN; /*(include digest in subsequent rounds)*/
++	}
++
++	if (buf_len) {
++		u8 hash[SHA1_MAC_LEN];
++		if (hmac_sha1_vector(key, key_len, 5, addr, len, hash))
++			return -1;
++		os_memcpy(buf, hash, buf_len);
++		forced_memzero(hash, sizeof(hash));
++	}
++
++	return 0;
++}
++
++#endif /* CRYPTO_MBEDTLS_SHA1_T_PRF */
++
++#ifdef CRYPTO_MBEDTLS_FIPS186_2_PRF
++
++/* fips_prf_internal.c sha1-internal.c */
++
++/* used only by src/eap_common/eap_sim_common.c:eap_sim_prf()
++ * for eap_sim_derive_keys() and eap_sim_derive_keys_reauth()
++ * where xlen is 160 */
++
++int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
++{
++	/* FIPS 186-2 + change notice 1 */
++
++	mbedtls_sha1_context ctx;
++	u8 * const xkey = ctx.MBEDTLS_PRIVATE(buffer);
++	u32 * const xstate = ctx.MBEDTLS_PRIVATE(state);
++	const u32 xstate_init[] =
++	  { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 };
++
++	mbedtls_sha1_init(&ctx);
++	os_memcpy(xkey, seed, seed_len < 64 ? seed_len : 64);
++
++	/* note: does not fill extra bytes if (xlen % 20) (SHA1_MAC_LEN) */
++	for (; xlen >= 20; xlen -= 20) {
++		/* XSEED_j = 0 */
++		/* XVAL = (XKEY + XSEED_j) mod 2^b */
++
++		/* w_i = G(t, XVAL) */
++		os_memcpy(xstate, xstate_init, sizeof(xstate_init));
++		mbedtls_internal_sha1_process(&ctx, xkey);
++
++	  #if __BYTE_ORDER == __LITTLE_ENDIAN
++		xstate[0] = host_to_be32(xstate[0]);
++		xstate[1] = host_to_be32(xstate[1]);
++		xstate[2] = host_to_be32(xstate[2]);
++		xstate[3] = host_to_be32(xstate[3]);
++		xstate[4] = host_to_be32(xstate[4]);
++	  #endif
++		os_memcpy(x, xstate, 20);
++		if (xlen == 20) /*(done; skip prep for next loop)*/
++			break;
++
++		/* XKEY = (1 + XKEY + w_i) mod 2^b */
++		for (u32 carry = 1, k = 20; k-- > 0; carry >>= 8)
++			xkey[k] = (carry += xkey[k] + x[k]) & 0xff;
++		x += 20;
++		/* x_j = w_0|w_1 (each pair of iterations through loop)*/
++	}
++
++	mbedtls_sha1_free(&ctx);
++	return 0;
++}
++
++#endif /* CRYPTO_MBEDTLS_FIPS186_2_PRF */
++
++#endif /* MBEDTLS_SHA1_C */
++
++
++#ifdef CRYPTO_MBEDTLS_DES_ENCRYPT
++#ifdef MBEDTLS_DES_C
++#include <mbedtls/des.h>
++int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
++{
++	u8 pkey[8], next, tmp;
++	int i;
++
++	/* Add parity bits to the key */
++	next = 0;
++	for (i = 0; i < 7; i++) {
++		tmp = key[i];
++		pkey[i] = (tmp >> i) | next | 1;
++		next = tmp << (7 - i);
++	}
++	pkey[i] = next | 1;
++
++	mbedtls_des_context des;
++	mbedtls_des_init(&des);
++	int ret = mbedtls_des_setkey_enc(&des, pkey)
++	       || mbedtls_des_crypt_ecb(&des, clear, cypher) ? -1 : 0;
++	mbedtls_des_free(&des);
++	return ret;
++}
++#else
++#include "des-internal.c"/* pull in hostap local implementation */
++#endif
++#endif
++
++
++#ifdef CRYPTO_MBEDTLS_PBKDF2_SHA1
++/* sha1-pbkdf2.c */
++#include <mbedtls/pkcs5.h>
++int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
++                int iterations, u8 *buf, size_t buflen)
++{
++  #if MBEDTLS_VERSION_NUMBER >= 0x03020200 /* mbedtls 3.2.2 */
++	return mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA1,
++			(const u8 *)passphrase, os_strlen(passphrase),
++			ssid, ssid_len, iterations, 32, buf) ? -1 : 0;
++  #else
++	const mbedtls_md_info_t *md_info;
++	md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
++	if (md_info == NULL)
++		return -1;
++	mbedtls_md_context_t ctx;
++	mbedtls_md_init(&ctx);
++	int ret = mbedtls_md_setup(&ctx, md_info, 1)
++	       || mbedtls_pkcs5_pbkdf2_hmac(&ctx,
++			(const u8 *)passphrase, os_strlen(passphrase),
++			ssid, ssid_len, iterations, 32, buf) ? -1 : 0;
++	mbedtls_md_free(&ctx);
++	return ret;
++  #endif
++}
++#endif
++
++
++/*#include "aes.h"*/ /* prototypes also included in "crypto.h" */
++
++static void *aes_crypt_init_mode(const u8 *key, size_t len, int mode)
++{
++	if (TEST_FAIL())
++		return NULL;
++
++	mbedtls_aes_context *aes = os_malloc(sizeof(*aes));
++	if (!aes)
++		return NULL;
++
++	mbedtls_aes_init(aes);
++	if ((mode == MBEDTLS_AES_ENCRYPT
++	    ? mbedtls_aes_setkey_enc(aes, key, len * 8)
++	    : mbedtls_aes_setkey_dec(aes, key, len * 8)) == 0)
++		return aes;
++
++	mbedtls_aes_free(aes);
++	os_free(aes);
++	return NULL;
++}
++
++void *aes_encrypt_init(const u8 *key, size_t len)
++{
++	return aes_crypt_init_mode(key, len, MBEDTLS_AES_ENCRYPT);
++}
++
++int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
++{
++	return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, plain, crypt);
++}
++
++void aes_encrypt_deinit(void *ctx)
++{
++	mbedtls_aes_free(ctx);
++	os_free(ctx);
++}
++
++void *aes_decrypt_init(const u8 *key, size_t len)
++{
++	return aes_crypt_init_mode(key, len, MBEDTLS_AES_DECRYPT);
++}
++
++int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
++{
++	return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_DECRYPT, crypt, plain);
++}
++
++void aes_decrypt_deinit(void *ctx)
++{
++	mbedtls_aes_free(ctx);
++	os_free(ctx);
++}
++
++
++#include "aes_wrap.h"
++
++
++#ifdef MBEDTLS_NIST_KW_C
++
++#include <mbedtls/nist_kw.h>
++
++/* aes-wrap.c */
++int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	mbedtls_nist_kw_context ctx;
++	mbedtls_nist_kw_init(&ctx);
++	size_t olen;
++	int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
++	                                 kek, kek_len*8, 1)
++	       || mbedtls_nist_kw_wrap(&ctx, MBEDTLS_KW_MODE_KW, plain, n*8,
++	                               cipher, &olen, (n+1)*8) ? -1 : 0;
++	mbedtls_nist_kw_free(&ctx);
++	return ret;
++}
++
++/* aes-unwrap.c */
++int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, u8 *plain)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	mbedtls_nist_kw_context ctx;
++	mbedtls_nist_kw_init(&ctx);
++	size_t olen;
++	int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
++	                                 kek, kek_len*8, 0)
++	       || mbedtls_nist_kw_unwrap(&ctx, MBEDTLS_KW_MODE_KW, cipher,
++	                                 (n+1)*8, plain, &olen, n*8) ? -1 : 0;
++	mbedtls_nist_kw_free(&ctx);
++	return ret;
++}
++
++#else
++
++#ifndef CRYPTO_MBEDTLS_CONFIG_FIPS
++#include "aes-wrap.c"    /* pull in hostap local implementation */
++#include "aes-unwrap.c"  /* pull in hostap local implementation */
++#endif
++
++#endif /* MBEDTLS_NIST_KW_C */
++
++
++#ifdef MBEDTLS_CMAC_C
++
++/* aes-omac1.c */
++
++#include <mbedtls/cmac.h>
++
++int omac1_aes_vector(
++    const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[],
++    const size_t *len, u8 *mac)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	mbedtls_cipher_type_t cipher_type;
++	switch (key_len) {
++	case 16: cipher_type = MBEDTLS_CIPHER_AES_128_ECB; break;
++	case 24: cipher_type = MBEDTLS_CIPHER_AES_192_ECB; break;
++	case 32: cipher_type = MBEDTLS_CIPHER_AES_256_ECB; break;
++	default: return -1;
++	}
++	const mbedtls_cipher_info_t *cipher_info;
++	cipher_info = mbedtls_cipher_info_from_type(cipher_type);
++	if (cipher_info == NULL)
++		return -1;
++
++	mbedtls_cipher_context_t ctx;
++	mbedtls_cipher_init(&ctx);
++	int ret = -1;
++	if (mbedtls_cipher_setup(&ctx, cipher_info) == 0
++	    && mbedtls_cipher_cmac_starts(&ctx, key, key_len*8) == 0) {
++		ret = 0;
++		for (size_t i = 0; i < num_elem && ret == 0; ++i)
++			ret = mbedtls_cipher_cmac_update(&ctx, addr[i], len[i]);
++	}
++	if (ret == 0)
++		ret = mbedtls_cipher_cmac_finish(&ctx, mac);
++	mbedtls_cipher_free(&ctx);
++	return ret ? -1 : 0;
++}
++
++int omac1_aes_128_vector(const u8 *key, size_t num_elem,
++			 const u8 *addr[], const size_t *len,
++			 u8 *mac)
++{
++	return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
++}
++
++int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
++{
++	return omac1_aes_vector(key, 16, 1, &data, &data_len, mac);
++}
++
++int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
++{
++	return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
++}
++
++#else
++
++#include "aes-omac1.c"  /* pull in hostap local implementation */
++
++#ifndef MBEDTLS_AES_BLOCK_SIZE
++#define MBEDTLS_AES_BLOCK_SIZE 16
++#endif
++
++#endif /* MBEDTLS_CMAC_C */
++
++
++/* These interfaces can be inefficient when used in loops, as the overhead of
++ * initialization each call is large for each block input (e.g. 16 bytes) */
++
++
++/* aes-encblock.c */
++int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	mbedtls_aes_context aes;
++	mbedtls_aes_init(&aes);
++	int ret = mbedtls_aes_setkey_enc(&aes, key, 128)
++	       || mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_ENCRYPT, in, out)
++	  ? -1
++	  : 0;
++	mbedtls_aes_free(&aes);
++	return ret;
++}
++
++
++/* aes-ctr.c */
++int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
++		    u8 *data, size_t data_len)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	unsigned char counter[MBEDTLS_AES_BLOCK_SIZE];
++	unsigned char stream_block[MBEDTLS_AES_BLOCK_SIZE];
++	os_memcpy(counter, nonce, MBEDTLS_AES_BLOCK_SIZE);/*(must be writable)*/
++
++	mbedtls_aes_context ctx;
++	mbedtls_aes_init(&ctx);
++	size_t nc_off = 0;
++	int ret = mbedtls_aes_setkey_enc(&ctx, key, key_len*8)
++	       || mbedtls_aes_crypt_ctr(&ctx, data_len, &nc_off,
++	                                counter, stream_block,
++	                                data, data) ? -1 : 0;
++	forced_memzero(stream_block, sizeof(stream_block));
++	mbedtls_aes_free(&ctx);
++	return ret;
++}
++
++int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
++			u8 *data, size_t data_len)
++{
++	return aes_ctr_encrypt(key, 16, nonce, data, data_len);
++}
++
++
++/* aes-cbc.c */
++static int aes_128_cbc_oper(const u8 *key, const u8 *iv,
++                            u8 *data, size_t data_len, int mode)
++{
++	unsigned char ivec[MBEDTLS_AES_BLOCK_SIZE];
++	os_memcpy(ivec, iv, MBEDTLS_AES_BLOCK_SIZE); /*(must be writable)*/
++
++	mbedtls_aes_context ctx;
++	mbedtls_aes_init(&ctx);
++	int ret = (mode == MBEDTLS_AES_ENCRYPT
++	           ? mbedtls_aes_setkey_enc(&ctx, key, 128)
++	           : mbedtls_aes_setkey_dec(&ctx, key, 128))
++	       || mbedtls_aes_crypt_cbc(&ctx, mode, data_len, ivec, data, data);
++	mbedtls_aes_free(&ctx);
++	return ret ? -1 : 0;
++}
++
++int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_ENCRYPT);
++}
++
++int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_DECRYPT);
++}
++
++
++/*
++ * Much of the following is documented in crypto.h as for CONFIG_TLS=internal
++ * but such comments are not accurate:
++ *
++ * "This function is only used with internal TLSv1 implementation
++ *  (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
++ *  to implement this."
++ */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_CIPHER
++
++#include <mbedtls/cipher.h>
++
++struct crypto_cipher
++{
++	mbedtls_cipher_context_t ctx_enc;
++	mbedtls_cipher_context_t ctx_dec;
++};
++
++struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
++					  const u8 *iv, const u8 *key,
++					  size_t key_len)
++{
++	/* IKEv2 src/eap_common/ikev2_common.c:ikev2_{encr,decr}_encrypt()
++	 * uses one of CRYPTO_CIPHER_ALG_AES or CRYPTO_CIPHER_ALG_3DES */
++
++	mbedtls_cipher_type_t cipher_type;
++	size_t iv_len;
++	switch (alg) {
++  #ifdef MBEDTLS_ARC4_C
++  #if 0
++	case CRYPTO_CIPHER_ALG_RC4:
++		cipher_type = MBEDTLS_CIPHER_ARC4_128;
++		iv_len = 0;
++		break;
++  #endif
++  #endif
++  #ifdef MBEDTLS_AES_C
++	case CRYPTO_CIPHER_ALG_AES:
++		if (key_len == 16) cipher_type = MBEDTLS_CIPHER_AES_128_CTR;
++		if (key_len == 24) cipher_type = MBEDTLS_CIPHER_AES_192_CTR;
++		if (key_len == 32) cipher_type = MBEDTLS_CIPHER_AES_256_CTR;
++		iv_len = 16;
++		break;
++  #endif
++  #ifdef MBEDTLS_DES_C
++	case CRYPTO_CIPHER_ALG_3DES:
++		cipher_type = MBEDTLS_CIPHER_DES_EDE3_CBC;
++		iv_len = 8;
++		break;
++  #if 0
++	case CRYPTO_CIPHER_ALG_DES:
++		cipher_type = MBEDTLS_CIPHER_DES_CBC;
++		iv_len = 8;
++		break;
++  #endif
++  #endif
++	default:
++		return NULL;
++	}
++
++	const mbedtls_cipher_info_t *cipher_info;
++	cipher_info = mbedtls_cipher_info_from_type(cipher_type);
++	if (cipher_info == NULL)
++		return NULL;
++
++	key_len *= 8; /* key_bitlen */
++  #if 0 /*(were key_bitlen not already available)*/
++  #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */
++	key_len = mbedtls_cipher_info_get_key_bitlen(cipher_info);
++  #else
++	key_len = cipher_info->MBEDTLS_PRIVATE(key_bitlen);
++  #endif
++  #endif
++
++  #if 0 /*(were iv_len not known above, would need MBEDTLS_PRIVATE(iv_size))*/
++	iv_len = cipher_info->MBEDTLS_PRIVATE(iv_size);
++  #endif
++
++	struct crypto_cipher *ctx = os_malloc(sizeof(*ctx));
++	if (!ctx)
++		return NULL;
++
++	mbedtls_cipher_init(&ctx->ctx_enc);
++	mbedtls_cipher_init(&ctx->ctx_dec);
++	if (   mbedtls_cipher_setup(&ctx->ctx_enc,cipher_info) == 0
++	    && mbedtls_cipher_setup(&ctx->ctx_dec,cipher_info) == 0
++	    && mbedtls_cipher_setkey(&ctx->ctx_enc,key,key_len,MBEDTLS_ENCRYPT) == 0
++	    && mbedtls_cipher_setkey(&ctx->ctx_dec,key,key_len,MBEDTLS_DECRYPT) == 0
++	    && mbedtls_cipher_set_iv(&ctx->ctx_enc,iv,iv_len) == 0
++	    && mbedtls_cipher_set_iv(&ctx->ctx_dec,iv,iv_len) == 0
++	    && mbedtls_cipher_reset(&ctx->ctx_enc) == 0
++	    && mbedtls_cipher_reset(&ctx->ctx_dec) == 0) {
++		return ctx;
++	}
++
++	mbedtls_cipher_free(&ctx->ctx_enc);
++	mbedtls_cipher_free(&ctx->ctx_dec);
++	os_free(ctx);
++	return NULL;
++}
++
++int crypto_cipher_encrypt(struct crypto_cipher *ctx,
++			  const u8 *plain, u8 *crypt, size_t len)
++{
++	size_t olen = 0; /*(poor interface above; unknown size of u8 *crypt)*/
++	return (mbedtls_cipher_update(&ctx->ctx_enc, plain, len, crypt, &olen)
++	        || mbedtls_cipher_finish(&ctx->ctx_enc, crypt + olen, &olen)) ? -1 : 0;
++}
++
++int crypto_cipher_decrypt(struct crypto_cipher *ctx,
++			  const u8 *crypt, u8 *plain, size_t len)
++{
++	size_t olen = 0; /*(poor interface above; unknown size of u8 *plain)*/
++	return (mbedtls_cipher_update(&ctx->ctx_dec, crypt, len, plain, &olen)
++	        || mbedtls_cipher_finish(&ctx->ctx_dec, plain + olen, &olen)) ? -1 : 0;
++}
++
++void crypto_cipher_deinit(struct crypto_cipher *ctx)
++{
++	mbedtls_cipher_free(&ctx->ctx_enc);
++	mbedtls_cipher_free(&ctx->ctx_dec);
++	os_free(ctx);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_CIPHER */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_HASH
++
++struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
++				      size_t key_len)
++{
++	mbedtls_md_type_t md_type;
++	int is_hmac = 0;
++
++	switch (alg) {
++  #ifdef MBEDTLS_MD5_C
++	case CRYPTO_HASH_ALG_MD5:
++		md_type = MBEDTLS_MD_MD5;
++		break;
++  #endif
++  #ifdef MBEDTLS_SHA1_C
++	case CRYPTO_HASH_ALG_SHA1:
++		md_type = MBEDTLS_MD_SHA1;
++		break;
++  #endif
++  #ifdef MBEDTLS_MD5_C
++	case CRYPTO_HASH_ALG_HMAC_MD5:
++		md_type = MBEDTLS_MD_MD5;
++		is_hmac = 1;
++		break;
++  #endif
++  #ifdef MBEDTLS_SHA1_C
++	case CRYPTO_HASH_ALG_HMAC_SHA1:
++		md_type = MBEDTLS_MD_SHA1;
++		is_hmac = 1;
++		break;
++  #endif
++  #ifdef MBEDTLS_SHA256_C
++	case CRYPTO_HASH_ALG_SHA256:
++		md_type = MBEDTLS_MD_SHA256;
++		break;
++	case CRYPTO_HASH_ALG_HMAC_SHA256:
++		md_type = MBEDTLS_MD_SHA256;
++		is_hmac = 1;
++		break;
++  #endif
++  #ifdef MBEDTLS_SHA512_C
++	case CRYPTO_HASH_ALG_SHA384:
++		md_type = MBEDTLS_MD_SHA384;
++		break;
++	case CRYPTO_HASH_ALG_SHA512:
++		md_type = MBEDTLS_MD_SHA512;
++		break;
++  #endif
++	default:
++		return NULL;
++	}
++
++	const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
++	if (!md_info)
++		return NULL;
++
++	mbedtls_md_context_t *mctx = os_malloc(sizeof(*mctx));
++	if (mctx == NULL)
++		return NULL;
++
++	mbedtls_md_init(mctx);
++	if (mbedtls_md_setup(mctx, md_info, is_hmac) != 0) {
++		os_free(mctx);
++		return NULL;
++	}
++
++	if (is_hmac)
++		mbedtls_md_hmac_starts(mctx, key, key_len);
++	else
++		mbedtls_md_starts(mctx);
++	return (struct crypto_hash *)((uintptr_t)mctx | is_hmac);
++}
++
++void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
++{
++	mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL);
++  #if 0
++	/*(mbedtls_md_hmac_update() and mbedtls_md_update()
++	 * make same modifications under the hood in mbedtls)*/
++	if ((uintptr_t)ctx & 1uL)
++		mbedtls_md_hmac_update(mctx, data, len);
++	else
++  #endif
++		mbedtls_md_update(mctx, data, len);
++}
++
++int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
++{
++	mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL);
++	if (mac != NULL && len != NULL) { /*(NULL if caller just freeing context)*/
++	  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
++		const mbedtls_md_info_t *md_info = mbedtls_md_info_from_ctx(mctx);
++	  #else
++		const mbedtls_md_info_t *md_info = mctx->MBEDTLS_PRIVATE(md_info);
++	  #endif
++		size_t maclen = mbedtls_md_get_size(md_info);
++		if (*len < maclen) {
++			*len = maclen;
++			/*(note: ctx not freed; can call again with larger *len)*/
++			return -1;
++		}
++		*len = maclen;
++		if ((uintptr_t)ctx & 1uL)
++			mbedtls_md_hmac_finish(mctx, mac);
++		else
++			mbedtls_md_finish(mctx, mac);
++	}
++	mbedtls_md_free(mctx);
++	os_free(mctx);
++
++	if (TEST_FAIL())
++		return -1;
++
++	return 0;
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_HASH */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
++
++#include <mbedtls/bignum.h>
++
++/* crypto.h bignum interfaces */
++
++struct crypto_bignum *crypto_bignum_init(void)
++{
++	if (TEST_FAIL())
++		return NULL;
++
++	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
++	if (bn)
++		mbedtls_mpi_init(bn);
++	return (struct crypto_bignum *)bn;
++}
++
++struct crypto_bignum *crypto_bignum_init_set(const u8 *buf, size_t len)
++{
++	if (TEST_FAIL())
++		return NULL;
++
++	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
++	if (bn) {
++		mbedtls_mpi_init(bn);
++		if (mbedtls_mpi_read_binary(bn, buf, len) == 0)
++			return (struct crypto_bignum *)bn;
++	}
++
++	os_free(bn);
++	return NULL;
++}
++
++struct crypto_bignum *crypto_bignum_init_uint(unsigned int val)
++{
++	if (TEST_FAIL())
++		return NULL;
++
++  #if 0 /*(hostap use of this interface passes int, not uint)*/
++	val = host_to_be32(val);
++	return crypto_bignum_init_set((const u8 *)&val, sizeof(val));
++  #else
++	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
++	if (bn) {
++		mbedtls_mpi_init(bn);
++		if (mbedtls_mpi_lset(bn, (int)val) == 0)
++			return (struct crypto_bignum *)bn;
++	}
++
++	os_free(bn);
++	return NULL;
++  #endif
++}
++
++void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
++{
++	mbedtls_mpi_free((mbedtls_mpi *)n);
++	os_free(n);
++}
++
++int crypto_bignum_to_bin(const struct crypto_bignum *a,
++			 u8 *buf, size_t buflen, size_t padlen)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	size_t n = mbedtls_mpi_size((mbedtls_mpi *)a);
++	if (n < padlen)
++		n = padlen;
++	return n > buflen || mbedtls_mpi_write_binary((mbedtls_mpi *)a, buf, n)
++	  ? -1
++	  : (int)(n);
++}
++
++int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	/*assert(r != m);*//* r must not be same as m for mbedtls_mpi_random()*/
++  #if MBEDTLS_VERSION_NUMBER >= 0x021B0000 /* mbedtls 2.27.0 */
++	return mbedtls_mpi_random((mbedtls_mpi *)r, 0, (mbedtls_mpi *)m,
++				  mbedtls_ctr_drbg_random,
++				  crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++  #else
++	/* (needed by EAP_PWD, SAE, DPP) */
++	wpa_printf(MSG_ERROR,
++	           "mbedtls 2.27.0 or later required for mbedtls_mpi_random()");
++	return -1;
++  #endif
++}
++
++int crypto_bignum_add(const struct crypto_bignum *a,
++		      const struct crypto_bignum *b,
++		      struct crypto_bignum *c)
++{
++	return mbedtls_mpi_add_mpi((mbedtls_mpi *)c,
++				   (const mbedtls_mpi *)a,
++				   (const mbedtls_mpi *)b) ? -1 : 0;
++}
++
++int crypto_bignum_mod(const struct crypto_bignum *a,
++		      const struct crypto_bignum *b,
++		      struct crypto_bignum *c)
++{
++	return mbedtls_mpi_mod_mpi((mbedtls_mpi *)c,
++				   (const mbedtls_mpi *)a,
++				   (const mbedtls_mpi *)b) ? -1 : 0;
++}
++
++int crypto_bignum_exptmod(const struct crypto_bignum *a,
++			  const struct crypto_bignum *b,
++			  const struct crypto_bignum *c,
++			  struct crypto_bignum *d)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	/* (check if input params match d; d is the result) */
++	/* (a == d) is ok in current mbedtls implementation */
++	if (b == d || c == d) { /*(not ok; store result in intermediate)*/
++		mbedtls_mpi R;
++		mbedtls_mpi_init(&R);
++		int rc = mbedtls_mpi_exp_mod(&R,
++		                             (const mbedtls_mpi *)a,
++		                             (const mbedtls_mpi *)b,
++		                             (const mbedtls_mpi *)c,
++		                             NULL)
++		      || mbedtls_mpi_copy((mbedtls_mpi *)d, &R) ? -1 : 0;
++		mbedtls_mpi_free(&R);
++		return rc;
++	}
++	else {
++		return mbedtls_mpi_exp_mod((mbedtls_mpi *)d,
++		                           (const mbedtls_mpi *)a,
++		                           (const mbedtls_mpi *)b,
++		                           (const mbedtls_mpi *)c,
++		                           NULL) ? -1 : 0;
++	}
++}
++
++int crypto_bignum_inverse(const struct crypto_bignum *a,
++			  const struct crypto_bignum *b,
++			  struct crypto_bignum *c)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	return mbedtls_mpi_inv_mod((mbedtls_mpi *)c,
++				   (const mbedtls_mpi *)a,
++				   (const mbedtls_mpi *)b) ? -1 : 0;
++}
++
++int crypto_bignum_sub(const struct crypto_bignum *a,
++		      const struct crypto_bignum *b,
++		      struct crypto_bignum *c)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	return mbedtls_mpi_sub_mpi((mbedtls_mpi *)c,
++				   (const mbedtls_mpi *)a,
++				   (const mbedtls_mpi *)b) ? -1 : 0;
++}
++
++int crypto_bignum_div(const struct crypto_bignum *a,
++		      const struct crypto_bignum *b,
++		      struct crypto_bignum *c)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	/*(most current use of this crypto.h interface has a == c (result),
++	 * so store result in an intermediate to avoid overwritten input)*/
++	mbedtls_mpi R;
++	mbedtls_mpi_init(&R);
++	int rc = mbedtls_mpi_div_mpi(&R, NULL,
++				     (const mbedtls_mpi *)a,
++				     (const mbedtls_mpi *)b)
++	      || mbedtls_mpi_copy((mbedtls_mpi *)c, &R) ? -1 : 0;
++	mbedtls_mpi_free(&R);
++	return rc;
++}
++
++int crypto_bignum_addmod(const struct crypto_bignum *a,
++			 const struct crypto_bignum *b,
++			 const struct crypto_bignum *c,
++			 struct crypto_bignum *d)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	return mbedtls_mpi_add_mpi((mbedtls_mpi *)d,
++				   (const mbedtls_mpi *)a,
++				   (const mbedtls_mpi *)b)
++	    || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d,
++				   (mbedtls_mpi *)d,
++				   (const mbedtls_mpi *)c) ? -1 : 0;
++}
++
++int crypto_bignum_mulmod(const struct crypto_bignum *a,
++			 const struct crypto_bignum *b,
++			 const struct crypto_bignum *c,
++			 struct crypto_bignum *d)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	return mbedtls_mpi_mul_mpi((mbedtls_mpi *)d,
++				   (const mbedtls_mpi *)a,
++				   (const mbedtls_mpi *)b)
++	    || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d,
++				   (mbedtls_mpi *)d,
++				   (const mbedtls_mpi *)c) ? -1 : 0;
++}
++
++int crypto_bignum_sqrmod(const struct crypto_bignum *a,
++			 const struct crypto_bignum *b,
++			 struct crypto_bignum *c)
++{
++	if (TEST_FAIL())
++		return -1;
++
++  #if 1
++	return crypto_bignum_mulmod(a, a, b, c);
++  #else
++	mbedtls_mpi bn;
++	mbedtls_mpi_init(&bn);
++	if (mbedtls_mpi_lset(&bn, 2)) /* alt?: mbedtls_mpi_set_bit(&bn, 1) */
++		return -1;
++	int ret = mbedtls_mpi_exp_mod((mbedtls_mpi *)c,
++				      (const mbedtls_mpi *)a, &bn,
++				      (const mbedtls_mpi *)b, NULL) ? -1 : 0;
++	mbedtls_mpi_free(&bn);
++	return ret;
++  #endif
++}
++
++int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
++			 struct crypto_bignum *r)
++{
++	return mbedtls_mpi_copy((mbedtls_mpi *)r, (const mbedtls_mpi *)a)
++	    || mbedtls_mpi_shift_r((mbedtls_mpi *)r, n) ? -1 : 0;
++}
++
++int crypto_bignum_cmp(const struct crypto_bignum *a,
++		      const struct crypto_bignum *b)
++{
++	return mbedtls_mpi_cmp_mpi((const mbedtls_mpi *)a, (const mbedtls_mpi *)b);
++}
++
++int crypto_bignum_is_zero(const struct crypto_bignum *a)
++{
++	/* XXX: src/common/sae.c:sswu() contains comment:
++	 * "TODO: Make sure crypto_bignum_is_zero() is constant time"
++	 * Note: mbedtls_mpi_cmp_int() *is not* constant time */
++	return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 0) == 0);
++}
++
++int crypto_bignum_is_one(const struct crypto_bignum *a)
++{
++	return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 1) == 0);
++}
++
++int crypto_bignum_is_odd(const struct crypto_bignum *a)
++{
++	return mbedtls_mpi_get_bit((const mbedtls_mpi *)a, 0);
++}
++
++#include "utils/const_time.h"
++int crypto_bignum_legendre(const struct crypto_bignum *a,
++			   const struct crypto_bignum *p)
++{
++	if (TEST_FAIL())
++		return -2;
++
++	/* Security Note:
++	 * mbedtls_mpi_exp_mod() is not documented to run in constant time,
++	 * though mbedtls/library/bignum.c uses constant_time_internal.h funcs.
++	 * Compare to crypto_openssl.c:crypto_bignum_legendre()
++	 * which uses openssl BN_mod_exp_mont_consttime()
++	 * mbedtls/library/ecp.c has further countermeasures to timing attacks,
++	 * (but ecp.c funcs are not used here) */
++
++	mbedtls_mpi exp, tmp;
++	mbedtls_mpi_init(&exp);
++	mbedtls_mpi_init(&tmp);
++
++	/* exp = (p-1) / 2 */
++	int res;
++	if (mbedtls_mpi_sub_int(&exp, (const mbedtls_mpi *)p, 1) == 0
++	    && mbedtls_mpi_shift_r(&exp, 1) == 0
++	    && mbedtls_mpi_exp_mod(&tmp, (const mbedtls_mpi *)a, &exp,
++	                           (const mbedtls_mpi *)p, NULL) == 0) {
++		/*(modified from crypto_openssl.c:crypto_bignum_legendre())*/
++		/* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need
++		 * to use constant time selection to avoid branches here. */
++		unsigned int mask;
++		res = -1;
++		mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 1) == 0), 1);
++		res = const_time_select_int(mask, 1, res);
++		mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 0) == 0), 1);
++		res = const_time_select_int(mask, 0, res);
++	} else {
++		res = -2;
++	}
++
++	mbedtls_mpi_free(&tmp);
++	mbedtls_mpi_free(&exp);
++	return res;
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_BIGNUM */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_DH
++
++/* crypto_internal-modexp.c */
++
++#include <mbedtls/bignum.h>
++#include <mbedtls/dhm.h>
++
++#if 0 /* crypto_dh_init() and crypto_dh_derive_secret() prefer to use mbedtls */
++int crypto_mod_exp(const u8 *base, size_t base_len,
++		   const u8 *power, size_t power_len,
++		   const u8 *modulus, size_t modulus_len,
++		   u8 *result, size_t *result_len)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	mbedtls_mpi bn_base, bn_exp, bn_modulus, bn_result;
++	mbedtls_mpi_init(&bn_base);
++	mbedtls_mpi_init(&bn_exp);
++	mbedtls_mpi_init(&bn_modulus);
++	mbedtls_mpi_init(&bn_result);
++
++	size_t len;
++	int ret =  mbedtls_mpi_read_binary(&bn_base, base, base_len)
++	        || mbedtls_mpi_read_binary(&bn_exp, power, power_len)
++	        || mbedtls_mpi_read_binary(&bn_modulus, modulus, modulus_len)
++	        || mbedtls_mpi_exp_mod(&bn_result,&bn_base,&bn_exp,&bn_modulus,NULL)
++	        || (len = mbedtls_mpi_size(&bn_result)) > *result_len
++	        || mbedtls_mpi_write_binary(&bn_result, result, (*result_len = len))
++	  ? -1
++	  : 0;
++
++	mbedtls_mpi_free(&bn_base);
++	mbedtls_mpi_free(&bn_exp);
++	mbedtls_mpi_free(&bn_modulus);
++	mbedtls_mpi_free(&bn_result);
++	return ret;
++}
++#endif
++
++static int crypto_mbedtls_dh_set_bin_pg(mbedtls_dhm_context *ctx, u8 generator,
++                                        const u8 *prime, size_t prime_len)
++{
++	/*(could set these directly in MBEDTLS_PRIVATE members)*/
++	mbedtls_mpi P, G;
++	mbedtls_mpi_init(&P);
++	mbedtls_mpi_init(&G);
++	int ret = mbedtls_mpi_lset(&G, generator)
++	       || mbedtls_mpi_read_binary(&P, prime, prime_len)
++	       || mbedtls_dhm_set_group(ctx, &P, &G);
++	mbedtls_mpi_free(&P);
++	mbedtls_mpi_free(&G);
++	return ret;
++}
++
++__attribute_noinline__
++static int crypto_mbedtls_dh_init_public(mbedtls_dhm_context *ctx, u8 generator,
++                                         const u8 *prime, size_t prime_len,
++                                         u8 *privkey, u8 *pubkey)
++{
++	if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len)
++	    || mbedtls_dhm_make_public(ctx, (int)prime_len, pubkey, prime_len,
++	                               mbedtls_ctr_drbg_random,
++	                               crypto_mbedtls_ctr_drbg()))
++		return -1;
++
++  /*(enable later when upstream mbedtls interface changes require)*/
++  #if 0 && MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++	mbedtls_mpi X;
++	mbedtls_mpi_init(&X);
++	int ret = mbedtls_dhm_get_value(ctx, MBEDTLS_DHM_PARAM_X, &X)
++	       || mbedtls_mpi_write_binary(&X, privkey, prime_len) ? -1 : 0;
++	mbedtls_mpi_free(&X);
++	return ret;
++  #else
++	return mbedtls_mpi_write_binary(&ctx->MBEDTLS_PRIVATE(X),
++	                                privkey, prime_len) ? -1 : 0;
++  #endif
++}
++
++int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
++		   u8 *pubkey)
++{
++	if (TEST_FAIL())
++		return -1;
++
++  #if 0 /*(crypto_dh_init() duplicated (and identical) in crypto_*.c modules)*/
++	size_t pubkey_len, pad;
++
++	if (os_get_random(privkey, prime_len) < 0)
++		return -1;
++	if (os_memcmp(privkey, prime, prime_len) > 0) {
++		/* Make sure private value is smaller than prime */
++		privkey[0] = 0;
++	}
++
++	pubkey_len = prime_len;
++	if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
++			   pubkey, &pubkey_len) < 0)
++		return -1;
++	if (pubkey_len < prime_len) {
++		pad = prime_len - pubkey_len;
++		os_memmove(pubkey + pad, pubkey, pubkey_len);
++		os_memset(pubkey, 0, pad);
++	}
++
++	return 0;
++  #else
++	/* Prefer to use mbedtls to derive our public/private key, as doing so
++	 * leverages mbedtls to properly format output and to perform blinding*/
++	mbedtls_dhm_context ctx;
++	mbedtls_dhm_init(&ctx);
++	int ret = crypto_mbedtls_dh_init_public(&ctx, generator, prime,
++	                                        prime_len, privkey, pubkey);
++	mbedtls_dhm_free(&ctx);
++	return ret;
++  #endif
++}
++
++/*(crypto_dh_derive_secret() could be implemented using crypto.h APIs
++ * instead of being reimplemented in each crypto_*.c)*/
++int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
++			    const u8 *order, size_t order_len,
++			    const u8 *privkey, size_t privkey_len,
++			    const u8 *pubkey, size_t pubkey_len,
++			    u8 *secret, size_t *len)
++{
++	if (TEST_FAIL())
++		return -1;
++
++  #if 0
++	if (pubkey_len > prime_len ||
++	    (pubkey_len == prime_len &&
++	     os_memcmp(pubkey, prime, prime_len) >= 0))
++		return -1;
++
++	int res = 0;
++	mbedtls_mpi pub;
++	mbedtls_mpi_init(&pub);
++	if (mbedtls_mpi_read_binary(&pub, pubkey, pubkey_len)
++	    || mbedtls_mpi_cmp_int(&pub, 1) <= 0) {
++		res = -1;
++	} else if (order) {
++		mbedtls_mpi p, q, tmp;
++		mbedtls_mpi_init(&p);
++		mbedtls_mpi_init(&q);
++		mbedtls_mpi_init(&tmp);
++
++		/* verify: pubkey^q == 1 mod p */
++		res = (mbedtls_mpi_read_binary(&p, prime, prime_len)
++		    || mbedtls_mpi_read_binary(&q, order, order_len)
++		    || mbedtls_mpi_exp_mod(&tmp, &pub, &q, &p, NULL)
++		    || mbedtls_mpi_cmp_int(&tmp, 1) != 0);
++
++		mbedtls_mpi_free(&p);
++		mbedtls_mpi_free(&q);
++		mbedtls_mpi_free(&tmp);
++	}
++	mbedtls_mpi_free(&pub);
++
++	return (res == 0)
++	  ? crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
++			   prime, prime_len, secret, len)
++	  : -1;
++  #else
++	/* Prefer to use mbedtls to derive DH shared secret, as doing so
++	 * leverages mbedtls to validate params and to perform blinding.
++	 *
++	 * Attempt to reconstitute DH context to derive shared secret
++	 * (due to limitations of the interface, which ought to pass context).
++	 * Force provided G (our private key) into context without validation.
++	 * Regenerating GX (our public key) not needed to derive shared secret.
++	 */
++	/*(older compilers might not support VLAs)*/
++	/*unsigned char buf[2+prime_len+2+1+2+pubkey_len];*/
++	unsigned char buf[2+MBEDTLS_MPI_MAX_SIZE+2+1+2+MBEDTLS_MPI_MAX_SIZE];
++	unsigned char *p = buf + 2 + prime_len;
++	if (2+prime_len+2+1+2+pubkey_len > sizeof(buf))
++		return -1;
++	WPA_PUT_BE16(buf, prime_len);  /*(2-byte big-endian size of prime)*/
++	p[0] = 0;                      /*(2-byte big-endian size of generator)*/
++	p[1] = 1;
++	p[2] = generator;
++	WPA_PUT_BE16(p+3, pubkey_len); /*(2-byte big-endian size of pubkey)*/
++	os_memcpy(p+5, pubkey, pubkey_len);
++	os_memcpy(buf+2, prime, prime_len);
++
++	mbedtls_dhm_context ctx;
++	mbedtls_dhm_init(&ctx);
++	p = buf;
++	int ret = mbedtls_dhm_read_params(&ctx, &p, p+2+prime_len+5+pubkey_len)
++	       || mbedtls_mpi_read_binary(&ctx.MBEDTLS_PRIVATE(X),
++	                                  privkey, privkey_len)
++	       || mbedtls_dhm_calc_secret(&ctx, secret, *len, len,
++	                                  mbedtls_ctr_drbg_random,
++	                                  crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++	mbedtls_dhm_free(&ctx);
++	return ret;
++  #endif
++}
++
++/* dh_group5.c */
++
++#include "dh_group5.h"
++
++/* RFC3526_PRIME_1536[] and RFC3526_GENERATOR_1536[] from crypto_wolfssl.c */
++
++static const unsigned char RFC3526_PRIME_1536[] = {
++	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
++	0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
++	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
++	0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
++	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
++	0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
++	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
++	0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
++	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
++	0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
++	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
++	0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
++	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
++	0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
++	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
++	0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
++};
++
++static const unsigned char RFC3526_GENERATOR_1536[] = {
++	0x02
++};
++
++void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
++{
++	const unsigned char * const prime = RFC3526_PRIME_1536;
++	const size_t prime_len = sizeof(RFC3526_PRIME_1536);
++	const u8 generator = *RFC3526_GENERATOR_1536;
++	struct wpabuf *wpubl = NULL, *wpriv = NULL;
++
++	mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx));
++	if (ctx == NULL)
++		return NULL;
++	mbedtls_dhm_init(ctx);
++
++	if (   (wpubl = wpabuf_alloc(prime_len))
++	    && (wpriv = wpabuf_alloc(prime_len))
++	    && crypto_mbedtls_dh_init_public(ctx, generator, prime, prime_len,
++	                                     wpabuf_put(wpriv, prime_len),
++	                                     wpabuf_put(wpubl, prime_len))==0) {
++		wpabuf_free(*publ);
++		wpabuf_clear_free(*priv);
++		*publ = wpubl;
++		*priv = wpriv;
++		return ctx;
++	}
++
++	wpabuf_clear_free(wpriv);
++	wpabuf_free(wpubl);
++	mbedtls_dhm_free(ctx);
++	os_free(ctx);
++	return NULL;
++}
++
++#ifdef CRYPTO_MBEDTLS_DH5_INIT_FIXED
++void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
++{
++	const unsigned char * const prime = RFC3526_PRIME_1536;
++	const size_t prime_len = sizeof(RFC3526_PRIME_1536);
++	const u8 generator = *RFC3526_GENERATOR_1536;
++
++	mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx));
++	if (ctx == NULL)
++		return NULL;
++	mbedtls_dhm_init(ctx);
++
++	if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len)==0
++	   #if 0 /*(ignore; not required to derive shared secret)*/
++	    && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(GX),
++				       wpabuf_head(publ),wpabuf_len(publ))==0
++	   #endif
++	    && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(X),
++				       wpabuf_head(priv),wpabuf_len(priv))==0) {
++		return ctx;
++	}
++
++	mbedtls_dhm_free(ctx);
++	os_free(ctx);
++	return NULL;
++}
++#endif
++
++struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
++				  const struct wpabuf *own_private)
++{
++	/*((mbedtls_dhm_context *)ctx must already contain own_private)*/
++	/* mbedtls 2.x: prime_len = ctx->len; */
++	/* mbedtls 3.x: prime_len = mbedtls_dhm_get_len(ctx); */
++	size_t olen = sizeof(RFC3526_PRIME_1536); /*(sizeof(); prime known)*/
++	struct wpabuf *buf = wpabuf_alloc(olen);
++	if (buf == NULL)
++		return NULL;
++	if (mbedtls_dhm_read_public((mbedtls_dhm_context *)ctx,
++	                            wpabuf_head(peer_public),
++	                            wpabuf_len(peer_public)) == 0
++	    && mbedtls_dhm_calc_secret(ctx, wpabuf_mhead(buf), olen, &olen,
++	                               mbedtls_ctr_drbg_random,
++	                               crypto_mbedtls_ctr_drbg()) == 0) {
++		wpabuf_put(buf, olen);
++		return buf;
++	}
++
++	wpabuf_free(buf);
++	return NULL;
++}
++
++void dh5_free(void *ctx)
++{
++	mbedtls_dhm_free(ctx);
++	os_free(ctx);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_DH */
++
++
++#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC)
++
++#include <mbedtls/ecp.h>
++
++#define CRYPTO_EC_pbits(e) (((mbedtls_ecp_group *)(e))->pbits)
++#define CRYPTO_EC_plen(e) ((((mbedtls_ecp_group *)(e))->pbits+7)>>3)
++#define CRYPTO_EC_P(e)    (&((mbedtls_ecp_group *)(e))->P)
++#define CRYPTO_EC_N(e)    (&((mbedtls_ecp_group *)(e))->N)
++#define CRYPTO_EC_A(e)    (&((mbedtls_ecp_group *)(e))->A)
++#define CRYPTO_EC_B(e)    (&((mbedtls_ecp_group *)(e))->B)
++#define CRYPTO_EC_G(e)    (&((mbedtls_ecp_group *)(e))->G)
++
++static mbedtls_ecp_group_id crypto_mbedtls_ecp_group_id_from_ike_id(int group)
++{
++	/* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */
++	switch (group) {
++  #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
++	case 19: return MBEDTLS_ECP_DP_SECP256R1;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
++	case 20: return MBEDTLS_ECP_DP_SECP384R1;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
++	case 21: return MBEDTLS_ECP_DP_SECP521R1;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
++	case 25: return MBEDTLS_ECP_DP_SECP192R1;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
++	case 26: return MBEDTLS_ECP_DP_SECP224R1;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
++	case 28: return MBEDTLS_ECP_DP_BP256R1;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
++	case 29: return MBEDTLS_ECP_DP_BP384R1;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
++	case 30: return MBEDTLS_ECP_DP_BP512R1;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
++	case 31: return MBEDTLS_ECP_DP_CURVE25519;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
++	case 32: return MBEDTLS_ECP_DP_CURVE448;
++  #endif
++	default: return MBEDTLS_ECP_DP_NONE;
++	}
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC
++static int crypto_mbedtls_ike_id_from_ecp_group_id(mbedtls_ecp_group_id grp_id)
++{
++	/* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */
++	/*(for crypto_ec_key_group())*/
++	switch (grp_id) {
++  #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP256R1:  return 19;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP384R1:  return 20;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP521R1:  return 21;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP192R1:  return 25;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP224R1:  return 26;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
++	case MBEDTLS_ECP_DP_BP256R1:    return 28;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
++	case MBEDTLS_ECP_DP_BP384R1:    return 29;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
++	case MBEDTLS_ECP_DP_BP512R1:    return 30;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
++	case MBEDTLS_ECP_DP_CURVE25519: return 31;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
++	case MBEDTLS_ECP_DP_CURVE448:   return 32;
++  #endif
++	default: return -1;
++	}
++}
++#endif
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH || CRYPTO_MBEDTLS_CRYPTO_EC */
++
++
++#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC_DPP)
++
++#include <mbedtls/ecp.h>
++#include <mbedtls/pk.h>
++
++static int crypto_mbedtls_keypair_gen(int group, mbedtls_pk_context *pk)
++{
++	mbedtls_ecp_group_id grp_id =
++	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
++	if (grp_id == MBEDTLS_ECP_DP_NONE)
++		return -1;
++	const mbedtls_pk_info_t *pk_info =
++	  mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
++	if (pk_info == NULL)
++		return -1;
++	return mbedtls_pk_setup(pk, pk_info)
++	    || mbedtls_ecp_gen_key(grp_id, mbedtls_pk_ec(*pk),
++	                           mbedtls_ctr_drbg_random,
++	                           crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++}
++
++#endif
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_ECDH
++
++#include <mbedtls/ecdh.h>
++#include <mbedtls/ecdsa.h>
++#include <mbedtls/ecp.h>
++#include <mbedtls/pk.h>
++
++/* wrap mbedtls_ecdh_context for more future-proof direct access to components
++ * (mbedtls_ecdh_context internal implementation may change between releases)
++ *
++ * If mbedtls_pk_context -- specifically underlying mbedtls_ecp_keypair --
++ * lifetime were guaranteed to be longer than that of mbedtls_ecdh_context,
++ * then mbedtls_pk_context or mbedtls_ecp_keypair could be stored in crypto_ecdh
++ * (or crypto_ec_key could be stored in crypto_ecdh, and crypto_ec_key could
++ *  wrap mbedtls_ecp_keypair and components, to avoid MBEDTLS_PRIVATE access) */
++struct crypto_ecdh {
++	mbedtls_ecdh_context ctx;
++	mbedtls_ecp_group grp;
++	mbedtls_ecp_point Q;
++};
++
++struct crypto_ecdh * crypto_ecdh_init(int group)
++{
++	mbedtls_pk_context pk;
++	mbedtls_pk_init(&pk);
++	struct crypto_ecdh *ecdh = crypto_mbedtls_keypair_gen(group, &pk) == 0
++	  ? crypto_ecdh_init2(group, (struct crypto_ec_key *)&pk)
++	  : NULL;
++	mbedtls_pk_free(&pk);
++	return ecdh;
++}
++
++struct crypto_ecdh * crypto_ecdh_init2(int group,
++				       struct crypto_ec_key *own_key)
++{
++	mbedtls_ecp_group_id grp_id =
++	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
++	if (grp_id == MBEDTLS_ECP_DP_NONE)
++		return NULL;
++	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)own_key);
++	struct crypto_ecdh *ecdh = os_malloc(sizeof(*ecdh));
++	if (ecdh == NULL)
++		return NULL;
++	mbedtls_ecdh_init(&ecdh->ctx);
++	mbedtls_ecp_group_init(&ecdh->grp);
++	mbedtls_ecp_point_init(&ecdh->Q);
++	if (mbedtls_ecdh_setup(&ecdh->ctx, grp_id) == 0
++	    && mbedtls_ecdh_get_params(&ecdh->ctx,ecp_kp,MBEDTLS_ECDH_OURS) == 0) {
++		/* copy grp and Q for later use
++		 * (retrieving this info later is more convoluted
++		 *  even if mbedtls_ecdh_make_public() is considered)*/
++	  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
++		mbedtls_mpi d;
++		mbedtls_mpi_init(&d);
++		if (mbedtls_ecp_export(ecp_kp, &ecdh->grp, &d, &ecdh->Q) == 0) {
++			mbedtls_mpi_free(&d);
++			return ecdh;
++		}
++		mbedtls_mpi_free(&d);
++	  #else
++		if (mbedtls_ecp_group_load(&ecdh->grp, grp_id) == 0
++		    && mbedtls_ecp_copy(&ecdh->Q, &ecp_kp->MBEDTLS_PRIVATE(Q)) == 0)
++			return ecdh;
++	  #endif
++	}
++
++	mbedtls_ecp_point_free(&ecdh->Q);
++	mbedtls_ecp_group_free(&ecdh->grp);
++	mbedtls_ecdh_free(&ecdh->ctx);
++	os_free(ecdh);
++	return NULL;
++}
++
++struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
++{
++	mbedtls_ecp_group *grp = &ecdh->grp;
++	size_t prime_len = CRYPTO_EC_plen(grp);
++	size_t output_len = prime_len;
++	u8 output_offset = 0;
++	u8 buf[256];
++
++  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++	/* len */
++  #endif
++  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
++		output_len = inc_y ? prime_len * 2 + 1 : prime_len + 1;
++		output_offset = 1;
++	}
++  #endif
++
++	if (output_len > sizeof(buf))
++		return NULL;
++
++	inc_y = inc_y ? MBEDTLS_ECP_PF_UNCOMPRESSED : MBEDTLS_ECP_PF_COMPRESSED;
++	if (mbedtls_ecp_point_write_binary(grp, &ecdh->Q, inc_y, &output_len,
++	                                   buf, output_len) == 0) {
++		return wpabuf_alloc_copy(buf + output_offset, output_len - output_offset);
++	}
++
++	return NULL;
++}
++
++#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
++static int crypto_mbedtls_short_weierstrass_derive_y(mbedtls_ecp_group *grp,
++                                                     mbedtls_mpi *bn,
++                                                     int parity_bit)
++{
++	/* y^2 = x^3 + ax + b
++	 * sqrt(w) = w^((p+1)/4) mod p   (for prime p where p = 3 mod 4) */
++	mbedtls_mpi *cy2 = (mbedtls_mpi *)
++	  crypto_ec_point_compute_y_sqr((struct crypto_ec *)grp,
++	                                (const struct crypto_bignum *)bn); /*x*/
++	if (cy2 == NULL)
++		return -1;
++
++	/*mbedtls_mpi_free(bn);*/
++	/*(reuse bn to store result (y))*/
++
++	mbedtls_mpi exp;
++	mbedtls_mpi_init(&exp);
++	int ret = mbedtls_mpi_get_bit(&grp->P, 0) != 1 /*(p = 3 mod 4)*/
++	       || mbedtls_mpi_get_bit(&grp->P, 1) != 1 /*(p = 3 mod 4)*/
++	       || mbedtls_mpi_add_int(&exp, &grp->P, 1)
++	       || mbedtls_mpi_shift_r(&exp, 2)
++	       || mbedtls_mpi_exp_mod(bn, cy2, &exp, &grp->P, NULL)
++	       || (mbedtls_mpi_get_bit(bn, 0) != parity_bit
++	           && mbedtls_mpi_sub_mpi(bn, &grp->P, bn));
++	mbedtls_mpi_free(&exp);
++	mbedtls_mpi_free(cy2);
++	os_free(cy2);
++	return ret;
++}
++#endif
++
++struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
++					const u8 *key, size_t len)
++{
++	if (len == 0) /*(invalid peer key)*/
++		return NULL;
++
++	mbedtls_ecp_group *grp = &ecdh->grp;
++
++  #if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
++	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
++		/* add header for mbedtls_ecdh_read_public() */
++		u8 buf[256];
++		if (sizeof(buf)-1 < len)
++			return NULL;
++		buf[0] = (u8)(len);
++		os_memcpy(buf+1, key, len);
++
++		if (inc_y) {
++			if (!(len & 1)) { /*(dpp code/tests does not include tag?!?)*/
++				if (sizeof(buf)-2 < len)
++					return NULL;
++				buf[0] = (u8)(1+len);
++				buf[1] = 0x04;
++				os_memcpy(buf+2, key, len);
++			}
++			len >>= 1; /*(repurpose len to prime_len)*/
++		} else { /* (inc_y == 0) */
++			/* mbedtls_ecp_point_read_binary() does not currently support
++			 * MBEDTLS_ECP_PF_COMPRESSED format (buf[1] = 0x02 or 0x03)
++			 * (returns MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) */
++
++			/* derive y, amend buf[] with y for UNCOMPRESSED format */
++			if (sizeof(buf)-2 < len*2 || len == 0)
++				return NULL;
++
++			buf[0] = (u8)(1+len*2);
++			buf[1] = 0x04;
++			os_memcpy(buf+2, key, len);
++
++			mbedtls_mpi bn;
++			mbedtls_mpi_init(&bn);
++			int ret = mbedtls_mpi_read_binary(&bn, key, len)
++			       || crypto_mbedtls_short_weierstrass_derive_y(grp, &bn, 0)
++			       || mbedtls_mpi_write_binary(&bn, buf+2+len, len);
++			mbedtls_mpi_free(&bn);
++			if (ret != 0)
++				return NULL;
++		}
++
++		if (mbedtls_ecdh_read_public(&ecdh->ctx, buf, buf[0]+1))
++			return NULL;
++	}
++  #endif
++  #if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED)
++	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) {
++		if (mbedtls_ecdh_read_public(&ecdh->ctx, key, len))
++			return NULL;
++	}
++  #endif
++
++	struct wpabuf *buf = wpabuf_alloc(len);
++	if (buf == NULL)
++		return NULL;
++
++	if (mbedtls_ecdh_calc_secret(&ecdh->ctx, &len,
++	                             wpabuf_mhead(buf), len,
++	                             mbedtls_ctr_drbg_random,
++	                             crypto_mbedtls_ctr_drbg()) == 0) {
++		wpabuf_put(buf, len);
++		return buf;
++	}
++
++	wpabuf_clear_free(buf);
++	return NULL;
++}
++
++void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
++{
++	if (ecdh == NULL)
++		return;
++	mbedtls_ecp_point_free(&ecdh->Q);
++	mbedtls_ecp_group_free(&ecdh->grp);
++	mbedtls_ecdh_free(&ecdh->ctx);
++	os_free(ecdh);
++}
++
++size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh)
++{
++	return CRYPTO_EC_plen(&ecdh->grp);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC
++
++#include <mbedtls/ecp.h>
++
++struct crypto_ec *crypto_ec_init(int group)
++{
++	mbedtls_ecp_group_id grp_id =
++	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
++	if (grp_id == MBEDTLS_ECP_DP_NONE)
++		return NULL;
++	mbedtls_ecp_group *e = os_malloc(sizeof(*e));
++	if (e == NULL)
++		return NULL;
++	mbedtls_ecp_group_init(e);
++	if (mbedtls_ecp_group_load(e, grp_id) == 0)
++		return (struct crypto_ec *)e;
++
++	mbedtls_ecp_group_free(e);
++	os_free(e);
++	return NULL;
++}
++
++void crypto_ec_deinit(struct crypto_ec *e)
++{
++	mbedtls_ecp_group_free((mbedtls_ecp_group *)e);
++	os_free(e);
++}
++
++size_t crypto_ec_prime_len(struct crypto_ec *e)
++{
++	return CRYPTO_EC_plen(e);
++}
++
++size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
++{
++	return CRYPTO_EC_pbits(e);
++}
++
++size_t crypto_ec_order_len(struct crypto_ec *e)
++{
++	return (mbedtls_mpi_bitlen(CRYPTO_EC_N(e)) + 7) / 8;
++}
++
++const struct crypto_bignum *crypto_ec_get_prime(struct crypto_ec *e)
++{
++	return (const struct crypto_bignum *)CRYPTO_EC_P(e);
++}
++
++const struct crypto_bignum *crypto_ec_get_order(struct crypto_ec *e)
++{
++	return (const struct crypto_bignum *)CRYPTO_EC_N(e);
++}
++
++const struct crypto_bignum *crypto_ec_get_a(struct crypto_ec *e)
++{
++	static const uint8_t secp256r1_a[] =
++	  {0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x01,
++	   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++	   0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc};
++	static const uint8_t secp384r1_a[] =
++	  {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
++	   0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
++	   0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xfc};
++	static const uint8_t secp521r1_a[] =
++	  {0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xfc};
++	static const uint8_t secp192r1_a[] =
++	  {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc};
++	static const uint8_t secp224r1_a[] =
++	  {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
++	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
++	   0xff,0xff,0xff,0xfe};
++
++	const uint8_t *bin = NULL;
++	size_t len = 0;
++
++	/* (mbedtls groups matching supported sswu_curve_param() IKE groups) */
++	switch (((mbedtls_ecp_group *)e)->id) {
++  #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP256R1:
++		bin = secp256r1_a;
++		len = sizeof(secp256r1_a);
++		break;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP384R1:
++		bin = secp384r1_a;
++		len = sizeof(secp384r1_a);
++		break;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP521R1:
++		bin = secp521r1_a;
++		len = sizeof(secp521r1_a);
++		break;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP192R1:
++		bin = secp192r1_a;
++		len = sizeof(secp192r1_a);
++		break;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
++	case MBEDTLS_ECP_DP_SECP224R1:
++		bin = secp224r1_a;
++		len = sizeof(secp224r1_a);
++		break;
++  #endif
++  #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
++	case MBEDTLS_ECP_DP_BP256R1:
++		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++  #endif
++  #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
++	case MBEDTLS_ECP_DP_BP384R1:
++		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++  #endif
++  #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
++	case MBEDTLS_ECP_DP_BP512R1:
++		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++  #endif
++  #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
++	case MBEDTLS_ECP_DP_CURVE25519:
++		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++  #endif
++  #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
++	case MBEDTLS_ECP_DP_CURVE448:
++		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
++  #endif
++	default:
++		return NULL;
++	}
++
++	/*(note: not thread-safe; returns file-scoped static storage)*/
++	if (mbedtls_mpi_read_binary(&mpi_sw_A, bin, len) == 0)
++		return (const struct crypto_bignum *)&mpi_sw_A;
++	return NULL;
++}
++
++const struct crypto_bignum *crypto_ec_get_b(struct crypto_ec *e)
++{
++	return (const struct crypto_bignum *)CRYPTO_EC_B(e);
++}
++
++const struct crypto_ec_point * crypto_ec_get_generator(struct crypto_ec *e)
++{
++	return (const struct crypto_ec_point *)CRYPTO_EC_G(e);
++}
++
++struct crypto_ec_point *crypto_ec_point_init(struct crypto_ec *e)
++{
++	if (TEST_FAIL())
++		return NULL;
++
++	mbedtls_ecp_point *p = os_malloc(sizeof(*p));
++	if (p != NULL)
++		mbedtls_ecp_point_init(p);
++	return (struct crypto_ec_point *)p;
++}
++
++void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
++{
++	mbedtls_ecp_point_free((mbedtls_ecp_point *)p);
++	os_free(p);
++}
++
++int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
++		      struct crypto_bignum *x)
++{
++	mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
++	return mbedtls_mpi_copy((mbedtls_mpi *)x, px)
++	  ? -1
++	  : 0;
++}
++
++int crypto_ec_point_to_bin(struct crypto_ec *e,
++			   const struct crypto_ec_point *point, u8 *x, u8 *y)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	/* crypto.h documents crypto_ec_point_to_bin() output is big-endian */
++	size_t len = CRYPTO_EC_plen(e);
++	if (x) {
++		mbedtls_mpi *px = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(X);
++		if (mbedtls_mpi_write_binary(px, x, len))
++			return -1;
++	}
++	if (y) {
++	  #if 0 /*(should not be necessary; py mpi should be in initial state)*/
++	  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++		if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++		    == MBEDTLS_ECP_TYPE_MONTGOMERY) {
++			os_memset(y, 0, len);
++			return 0;
++		}
++	  #endif
++	  #endif
++		mbedtls_mpi *py = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(Y);
++		if (mbedtls_mpi_write_binary(py, y, len))
++			return -1;
++	}
++	return 0;
++}
++
++struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
++						  const u8 *val)
++{
++	if (TEST_FAIL())
++		return NULL;
++
++	size_t len = CRYPTO_EC_plen(e);
++	mbedtls_ecp_point *p = os_malloc(sizeof(*p));
++	u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
++	if (p == NULL)
++		return NULL;
++	mbedtls_ecp_point_init(p);
++
++  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++	    == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
++	  #if 0 /* prefer alternative to MBEDTLS_PRIVATE() access */
++		mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
++		mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
++		mbedtls_mpi *pz = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Z);
++
++		if (mbedtls_mpi_read_binary(px, val, len) == 0
++		    && mbedtls_mpi_read_binary(py, val + len, len) == 0
++		    && mbedtls_mpi_lset(pz, 1) == 0)
++			return (struct crypto_ec_point *)p;
++	  #else
++		buf[0] = 0x04;
++		os_memcpy(buf+1, val, len*2);
++		if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p,
++		                                  buf, 1+len*2) == 0)
++			return (struct crypto_ec_point *)p;
++	  #endif
++	}
++  #endif
++  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++	    == MBEDTLS_ECP_TYPE_MONTGOMERY) {
++		/* crypto.h interface documents crypto_ec_point_from_bin()
++		 * val is length: prime_len * 2 and is big-endian
++		 * (Short Weierstrass is assumed by hostap)
++		 * Reverse to little-endian format for Montgomery */
++		for (unsigned int i = 0; i < len; ++i)
++			buf[i] = val[len-1-i];
++		if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p,
++		                                  buf, len) == 0)
++			return (struct crypto_ec_point *)p;
++	}
++  #endif
++
++	mbedtls_ecp_point_free(p);
++	os_free(p);
++	return NULL;
++}
++
++int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
++			const struct crypto_ec_point *b,
++			struct crypto_ec_point *c)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	/* mbedtls does not provide an mbedtls_ecp_point add function */
++	mbedtls_mpi one;
++	mbedtls_mpi_init(&one);
++	int ret = mbedtls_mpi_lset(&one, 1)
++	       || mbedtls_ecp_muladd(
++			(mbedtls_ecp_group *)e, (mbedtls_ecp_point *)c,
++			&one, (const mbedtls_ecp_point *)a,
++			&one, (const mbedtls_ecp_point *)b) ? -1 : 0;
++	mbedtls_mpi_free(&one);
++	return ret;
++}
++
++int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
++			const struct crypto_bignum *b,
++			struct crypto_ec_point *res)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	return mbedtls_ecp_mul(
++		(mbedtls_ecp_group *)e, (mbedtls_ecp_point *)res,
++		(const mbedtls_mpi *)b, (const mbedtls_ecp_point *)p,
++		mbedtls_ctr_drbg_random, crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++}
++
++int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
++{
++	if (TEST_FAIL())
++		return -1;
++
++	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++	    == MBEDTLS_ECP_TYPE_MONTGOMERY) {
++		/* e.g. MBEDTLS_ECP_DP_CURVE25519 and MBEDTLS_ECP_DP_CURVE448 */
++		wpa_printf(MSG_ERROR,
++		           "%s not implemented for Montgomery curves",__func__);
++		return -1;
++	}
++
++	/* mbedtls does not provide an mbedtls_ecp_point invert function */
++	/* below works for Short Weierstrass; incorrect for Montgomery curves */
++	mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
++	return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p) /*point at infinity*/
++	    || mbedtls_mpi_cmp_int(py, 0) == 0      /*point is its own inverse*/
++	    || mbedtls_mpi_sub_abs(py, CRYPTO_EC_P(e), py) == 0 ? 0 : -1;
++}
++
++#ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++static int
++crypto_ec_point_y_sqr_weierstrass(mbedtls_ecp_group *e, const mbedtls_mpi *x,
++                                  mbedtls_mpi *y2)
++{
++	/* MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS  y^2 = x^3 + a x + b    */
++
++	/* Short Weierstrass elliptic curve group w/o A set treated as A = -3 */
++	/* Attempt to match mbedtls/library/ecp.c:ecp_check_pubkey_sw() behavior
++	 * and elsewhere in mbedtls/library/ecp.c where if A is not set, it is
++	 * treated as if A = -3. */
++
++  #if 0
++	/* y^2 = x^3 + ax + b */
++	mbedtls_mpi *A = &e->A;
++	mbedtls_mpi t, A_neg3;
++	if (&e->A.p == NULL) {
++		mbedtls_mpi_init(&A_neg3);
++		if (mbedtls_mpi_lset(&A_neg3, -3) != 0) {
++			mbedtls_mpi_free(&A_neg3);
++			return -1;
++		}
++		A = &A_neg3;
++	}
++	mbedtls_mpi_init(&t);
++	int ret = /* x^3 */
++	          mbedtls_mpi_lset(&t, 3)
++	       || mbedtls_mpi_exp_mod(y2,  x, &t, &e->P, NULL)
++		  /* ax */
++	       || mbedtls_mpi_mul_mpi(y2, y2, A)
++	       || mbedtls_mpi_mod_mpi(&t, &t, &e->P)
++		  /* ax + b */
++	       || mbedtls_mpi_add_mpi(&t, &t, &e->B)
++	       || mbedtls_mpi_mod_mpi(&t, &t, &e->P)
++		  /* x^3 + ax + b */
++	       || mbedtls_mpi_add_mpi(&t, &t, y2) /* ax + b + x^3 */
++	       || mbedtls_mpi_mod_mpi(y2, &t, &e->P);
++	mbedtls_mpi_free(&t);
++	if (A == &A_neg3)
++		mbedtls_mpi_free(&A_neg3);
++	return ret; /* 0: success, non-zero: failure */
++  #else
++	/* y^2 = x^3 + ax + b = (x^2 + a)x + b */
++	return    /* x^2 */
++	          mbedtls_mpi_mul_mpi(y2,  x, x)
++	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++		  /* x^2 + a */
++	       || (e->A.MBEDTLS_PRIVATE(p)
++	           ? mbedtls_mpi_add_mpi(y2, y2, &e->A)
++	           : mbedtls_mpi_sub_int(y2, y2, 3))
++	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++		  /* (x^2 + a)x */
++	       || mbedtls_mpi_mul_mpi(y2, y2, x)
++	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++		  /* (x^2 + a)x + b */
++	       || mbedtls_mpi_add_mpi(y2, y2, &e->B)
++	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P);
++  #endif
++}
++#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */
++
++#if 0 /* not used by hostap */
++#ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++static int
++crypto_ec_point_y_sqr_montgomery(mbedtls_ecp_group *e, const mbedtls_mpi *x,
++                                 mbedtls_mpi *y2)
++{
++	/* XXX: !!! must be reviewed and audited for correctness !!! */
++
++	/* MBEDTLS_ECP_TYPE_MONTGOMERY         y^2 = x^3 + a x^2 + x  */
++
++	/* y^2 = x^3 + a x^2 + x = (x + a)x^2 + x */
++	mbedtls_mpi x2;
++	mbedtls_mpi_init(&x2);
++	int ret = /* x^2 */
++	          mbedtls_mpi_mul_mpi(&x2, x, x)
++	       || mbedtls_mpi_mod_mpi(&x2, &x2, &e->P)
++		  /* x + a */
++	       || mbedtls_mpi_add_mpi(y2,  x, &e->A)
++	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++		  /* (x + a)x^2 */
++	       || mbedtls_mpi_mul_mpi(y2, y2, &x2)
++	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
++		  /* (x + a)x^2 + x */
++	       || mbedtls_mpi_add_mpi(y2, y2, x)
++	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P);
++	mbedtls_mpi_free(&x2);
++	return ret; /* 0: success, non-zero: failure */
++}
++#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */
++#endif
++
++struct crypto_bignum *
++crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
++			      const struct crypto_bignum *x)
++{
++	if (TEST_FAIL())
++		return NULL;
++
++	mbedtls_mpi *y2 = os_malloc(sizeof(*y2));
++	if (y2 == NULL)
++		return NULL;
++	mbedtls_mpi_init(y2);
++
++  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++	      == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS
++	    && crypto_ec_point_y_sqr_weierstrass((mbedtls_ecp_group *)e,
++	                                         (const mbedtls_mpi *)x,
++	                                         y2) == 0)
++		return (struct crypto_bignum *)y2;
++  #endif
++  #if 0 /* not used by hostap */
++  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
++	      == MBEDTLS_ECP_TYPE_MONTGOMERY
++	    && crypto_ec_point_y_sqr_montgomery((mbedtls_ecp_group *)e,
++	                                        (const mbedtls_mpi *)x,
++	                                        y2) == 0)
++		return (struct crypto_bignum *)y2;
++  #endif
++  #endif
++
++	mbedtls_mpi_free(y2);
++	os_free(y2);
++	return NULL;
++}
++
++int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
++				   const struct crypto_ec_point *p)
++{
++	return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p);
++}
++
++int crypto_ec_point_is_on_curve(struct crypto_ec *e,
++				const struct crypto_ec_point *p)
++{
++  #if 1
++	return mbedtls_ecp_check_pubkey((const mbedtls_ecp_group *)e,
++	                                (const mbedtls_ecp_point *)p) == 0;
++  #else
++	/* compute y^2 mod P and compare to y^2 mod P */
++	/*(ref: src/eap_common/eap_pwd_common.c:compute_password_element())*/
++	const mbedtls_mpi *px = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
++	mbedtls_mpi *cy2 = (mbedtls_mpi *)
++	  crypto_ec_point_compute_y_sqr(e, (const struct crypto_bignum *)px);
++	if (cy2 == NULL)
++		return 0;
++
++	mbedtls_mpi y2;
++	mbedtls_mpi_init(&y2);
++	const mbedtls_mpi *py = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
++	int is_on_curve = mbedtls_mpi_mul_mpi(&y2, py, py) /* y^2 mod P */
++	               || mbedtls_mpi_mod_mpi(&y2, &y2, CRYPTO_EC_P(e))
++	               || mbedtls_mpi_cmp_mpi(&y2, cy2) != 0 ? 0 : 1;
++
++	mbedtls_mpi_free(&y2);
++	mbedtls_mpi_free(cy2);
++	os_free(cy2);
++	return is_on_curve;
++  #endif
++}
++
++int crypto_ec_point_cmp(const struct crypto_ec *e,
++			const struct crypto_ec_point *a,
++			const struct crypto_ec_point *b)
++{
++	return mbedtls_ecp_point_cmp((const mbedtls_ecp_point *)a,
++	                             (const mbedtls_ecp_point *)b);
++}
++
++#if !defined(CONFIG_NO_STDOUT_DEBUG)
++void crypto_ec_point_debug_print(const struct crypto_ec *e,
++				 const struct crypto_ec_point *p,
++				 const char *title)
++{
++	u8 x[MBEDTLS_MPI_MAX_SIZE];
++	u8 y[MBEDTLS_MPI_MAX_SIZE];
++	size_t len = CRYPTO_EC_plen(e);
++	/* crypto_ec_point_to_bin ought to take (const struct crypto_ec *e) */
++	struct crypto_ec *ee;
++	*(const struct crypto_ec **)&ee = e; /*(cast away const)*/
++	if (crypto_ec_point_to_bin(ee, p, x, y) == 0) {
++		if (title)
++			wpa_printf(MSG_DEBUG, "%s", title);
++		wpa_hexdump(MSG_DEBUG, "x:", x, len);
++		wpa_hexdump(MSG_DEBUG, "y:", y, len);
++	}
++}
++#endif
++
++
++struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len)
++{
++	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++	if (ctx == NULL)
++		return NULL;
++	mbedtls_pk_init(ctx);
++  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++	if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0) == 0)
++  #else
++	if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0,
++	                         mbedtls_ctr_drbg_random,
++	                         crypto_mbedtls_ctr_drbg()) == 0)
++  #endif
++		return (struct crypto_ec_key *)ctx;
++
++	mbedtls_pk_free(ctx);
++	os_free(ctx);
++	return NULL;
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE
++#ifdef CONFIG_MODULE_TESTS
++/*(for crypto_module_tests.c)*/
++struct crypto_ec_key * crypto_ec_key_set_priv(int group,
++					      const u8 *raw, size_t raw_len)
++{
++	mbedtls_ecp_group_id grp_id =
++	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
++	if (grp_id == MBEDTLS_ECP_DP_NONE)
++		return NULL;
++	const mbedtls_pk_info_t *pk_info =
++	  mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
++	if (pk_info == NULL)
++		return NULL;
++	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++	if (ctx == NULL)
++		return NULL;
++	mbedtls_pk_init(ctx);
++	if (mbedtls_pk_setup(ctx, pk_info) == 0
++	    && mbedtls_ecp_read_key(grp_id,mbedtls_pk_ec(*ctx),raw,raw_len) == 0) {
++		return (struct crypto_ec_key *)ctx;
++	}
++
++	mbedtls_pk_free(ctx);
++	os_free(ctx);
++	return NULL;
++}
++#endif
++#endif
++
++#include <mbedtls/error.h>
++#include <mbedtls/oid.h>
++static int crypto_mbedtls_pk_parse_subpubkey_compressed(mbedtls_pk_context *ctx, const u8 *der, size_t der_len)
++{
++    /* The following is modified from:
++     *   mbedtls/library/pkparse.c:mbedtls_pk_parse_subpubkey()
++     *   mbedtls/library/pkparse.c:pk_get_pk_alg()
++     *   mbedtls/library/pkparse.c:pk_use_ecparams()
++     */
++    mbedtls_pk_type_t pk_alg = MBEDTLS_PK_NONE;
++    const mbedtls_pk_info_t *pk_info;
++    int ret;
++    size_t len;
++    const unsigned char *end = der+der_len;
++    unsigned char *p;
++    *(const unsigned char **)&p = der;
++
++    if( ( ret = mbedtls_asn1_get_tag( &p, end, &len,
++                    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
++    {
++        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret ) );
++    }
++
++    end = p + len;
++
++    /*
++    if( ( ret = pk_get_pk_alg( &p, end, &pk_alg, &alg_params ) ) != 0 )
++        return( ret );
++    */
++    mbedtls_asn1_buf alg_oid, params;
++    memset( &params, 0, sizeof(mbedtls_asn1_buf) );
++    if( ( ret = mbedtls_asn1_get_alg( &p, end, &alg_oid, &params ) ) != 0 )
++        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_ALG, ret ) );
++    if( mbedtls_oid_get_pk_alg( &alg_oid, &pk_alg ) != 0 )
++        return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
++
++    if( ( ret = mbedtls_asn1_get_bitstring_null( &p, end, &len ) ) != 0 )
++        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY, ret ) );
++
++    if( p + len != end )
++        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY,
++                MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ) );
++
++    if( ( pk_info = mbedtls_pk_info_from_type( pk_alg ) ) == NULL )
++        return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
++
++    if( ( ret = mbedtls_pk_setup( ctx, pk_info ) ) != 0 )
++        return( ret );
++
++    /* assume mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx)
++     * has already run with ctx initialized up to pk_get_ecpubkey(),
++     * and pk_get_ecpubkey() has returned MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
++     *
++     * mbedtls mbedtls_ecp_point_read_binary()
++     * does not handle point in COMPRESSED format
++     *
++     * (validate assumption that algorithm is EC) */
++    mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx);
++    if (ecp_kp == NULL)
++        return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
++    mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++    mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++    mbedtls_ecp_group_id grp_id;
++
++
++    /* mbedtls/library/pkparse.c:pk_use_ecparams() */
++
++    if( params.tag == MBEDTLS_ASN1_OID )
++    {
++        if( mbedtls_oid_get_ec_grp( &params, &grp_id ) != 0 )
++            return( MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE );
++    }
++    else
++    {
++#if defined(MBEDTLS_PK_PARSE_EC_EXTENDED)
++        /*(large code block not copied from mbedtls; unsupported)*/
++      #if 0
++        if( ( ret = pk_group_id_from_specified( &params, &grp_id ) ) != 0 )
++            return( ret );
++      #endif
++#endif
++        return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
++    }
++
++    /*
++     * grp may already be initialized; if so, make sure IDs match
++     */
++    if( ecp_kp_grp->id != MBEDTLS_ECP_DP_NONE && ecp_kp_grp->id != grp_id )
++        return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
++
++    if( ( ret = mbedtls_ecp_group_load( ecp_kp_grp, grp_id ) ) != 0 )
++        return( ret );
++
++
++    /* (validate assumption that EC point is in COMPRESSED format) */
++    len = CRYPTO_EC_plen(ecp_kp_grp);
++    if( mbedtls_ecp_get_type(ecp_kp_grp) != MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS
++        || (end - p) != 1+len
++        || (*p != 0x02 && *p != 0x03) )
++        return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
++
++    /* Instead of calling mbedtls/library/pkparse.c:pk_get_ecpubkey() to call
++     * mbedtls_ecp_point_read_binary(), manually parse point into ecp_kp_Q */
++    mbedtls_mpi *X = &ecp_kp_Q->MBEDTLS_PRIVATE(X);
++    mbedtls_mpi *Y = &ecp_kp_Q->MBEDTLS_PRIVATE(Y);
++    mbedtls_mpi *Z = &ecp_kp_Q->MBEDTLS_PRIVATE(Z);
++    ret = mbedtls_mpi_lset(Z, 1);
++    if (ret != 0)
++        return( ret );
++    ret = mbedtls_mpi_read_binary(X, p+1, len);
++    if (ret != 0)
++        return( ret );
++    /* derive Y
++     * (similar derivation of Y in crypto_mbedtls.c:crypto_ecdh_set_peerkey())*/
++    ret = mbedtls_mpi_copy(Y, X) /*(Y is used as input and output obj below)*/
++       || crypto_mbedtls_short_weierstrass_derive_y(ecp_kp_grp, Y, (*p & 1));
++    if (ret != 0)
++        return( ret );
++
++    return mbedtls_ecp_check_pubkey( ecp_kp_grp, ecp_kp_Q );
++}
++
++struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len)
++{
++	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++	if (ctx == NULL)
++		return NULL;
++	mbedtls_pk_init(ctx);
++	/*int rc = mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx);*/
++	int rc = mbedtls_pk_parse_public_key(ctx, der, der_len);
++	if (rc == 0)
++		return (struct crypto_ec_key *)ctx;
++	else if (rc == MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) {
++		/* mbedtls mbedtls_ecp_point_read_binary()
++		 * does not handle point in COMPRESSED format; parse internally */
++		rc = crypto_mbedtls_pk_parse_subpubkey_compressed(ctx,der,der_len);
++		if (rc == 0)
++			return (struct crypto_ec_key *)ctx;
++	}
++
++	mbedtls_pk_free(ctx);
++	os_free(ctx);
++	return NULL;
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++
++static struct crypto_ec_key *
++crypto_ec_key_set_pub_point_for_group(mbedtls_ecp_group_id grp_id,
++                                      const mbedtls_ecp_point *pub,
++                                      const u8 *buf, size_t len)
++{
++	const mbedtls_pk_info_t *pk_info =
++	  mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
++	if (pk_info == NULL)
++		return NULL;
++	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++	if (ctx == NULL)
++		return NULL;
++	mbedtls_pk_init(ctx);
++	if (mbedtls_pk_setup(ctx, pk_info) == 0) {
++		/* (Is private key generation necessary for callers?)
++		 * alt: gen key then overwrite Q
++		 *   mbedtls_ecp_gen_key(grp_id, ecp_kp,
++	         *                       mbedtls_ctr_drbg_random,
++	         *                       crypto_mbedtls_ctr_drbg()) == 0
++	         */
++		mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx);
++		mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++		mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++		mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d);
++		if (mbedtls_ecp_group_load(ecp_kp_grp, grp_id) == 0
++		    && (pub
++		         ? mbedtls_ecp_copy(ecp_kp_Q, pub) == 0
++		         : mbedtls_ecp_point_read_binary(ecp_kp_grp, ecp_kp_Q,
++		                                         buf, len) == 0)
++		    && mbedtls_ecp_gen_privkey(ecp_kp_grp, ecp_kp_d,
++		                               mbedtls_ctr_drbg_random,
++		                               crypto_mbedtls_ctr_drbg()) == 0){
++			return (struct crypto_ec_key *)ctx;
++		}
++	}
++
++	mbedtls_pk_free(ctx);
++	os_free(ctx);
++	return NULL;
++}
++
++struct crypto_ec_key * crypto_ec_key_set_pub(int group, const u8 *x,
++					     const u8 *y, size_t len)
++{
++	mbedtls_ecp_group_id grp_id =
++	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
++	if (grp_id == MBEDTLS_ECP_DP_NONE)
++		return NULL;
++	if (len > MBEDTLS_MPI_MAX_SIZE)
++		return NULL;
++	u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
++	buf[0] = 0x04; /* assume x,y for Short Weierstrass */
++	os_memcpy(buf+1, x, len);
++	os_memcpy(buf+1+len, y, len);
++
++	return crypto_ec_key_set_pub_point_for_group(grp_id,NULL,buf,1+len*2);
++}
++
++struct crypto_ec_key *
++crypto_ec_key_set_pub_point(struct crypto_ec *e,
++			    const struct crypto_ec_point *pub)
++{
++	mbedtls_ecp_group_id grp_id = ((mbedtls_ecp_group *)e)->id;
++	mbedtls_ecp_point *p = (mbedtls_ecp_point *)pub;
++	return crypto_ec_key_set_pub_point_for_group(grp_id, p, NULL, 0);
++}
++
++
++struct crypto_ec_key * crypto_ec_key_gen(int group)
++{
++	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++	if (ctx == NULL)
++		return NULL;
++	mbedtls_pk_init(ctx);
++	if (crypto_mbedtls_keypair_gen(group, ctx) == 0)
++		return (struct crypto_ec_key *)ctx;
++	mbedtls_pk_free(ctx);
++	os_free(ctx);
++	return NULL;
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++void crypto_ec_key_deinit(struct crypto_ec_key *key)
++{
++	mbedtls_pk_free((mbedtls_pk_context *)key);
++	os_free(key);
++}
++
++struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key)
++{
++	/* (similar to crypto_ec_key_get_pubkey_point(),
++	 *  but compressed point format and ASN.1 DER wrapping)*/
++#ifndef MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/
++#define MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES    ( 30 + 2 * MBEDTLS_ECP_MAX_BYTES )
++#endif
++	unsigned char buf[MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES];
++	int len = mbedtls_pk_write_pubkey_der((mbedtls_pk_context *)key,
++	                                      buf, sizeof(buf));
++	if (len < 0)
++		return NULL;
++	/*  Note: data is written at the end of the buffer! Use the
++	 *        return value to determine where you should start
++	 *        using the buffer */
++	unsigned char *p = buf+sizeof(buf)-len;
++
++  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++	if (ecp_kp == NULL)
++		return NULL;
++	mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++	/*  Note: sae_pk.c expects pubkey point in compressed format,
++	 *        but mbedtls_pk_write_pubkey_der() writes uncompressed format.
++	 *        Manually translate format and update lengths in DER format */
++	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
++		unsigned char *end = buf+sizeof(buf);
++		size_t n;
++		/* SubjectPublicKeyInfo SEQUENCE */
++		mbedtls_asn1_get_tag(&p, end, &n,
++		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++		/* algorithm AlgorithmIdentifier */
++		unsigned char *a = p;
++		size_t alen;
++		mbedtls_asn1_get_tag(&p, end, &alen,
++		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++		p += alen;
++		alen = (size_t)(p - a);
++		/* subjectPublicKey BIT STRING */
++		mbedtls_asn1_get_tag(&p, end, &n, MBEDTLS_ASN1_BIT_STRING);
++		/* rewrite into compressed point format and rebuild ASN.1 */
++		p[1] = (buf[sizeof(buf)-1] & 1) ? 0x03 : 0x02;
++		n = 1 + 1 + (n-2)/2;
++		len = mbedtls_asn1_write_len(&p, buf, n) + (int)n;
++		len += mbedtls_asn1_write_tag(&p, buf, MBEDTLS_ASN1_BIT_STRING);
++		os_memmove(p-alen, a, alen);
++		len += alen;
++		p -= alen;
++		len += mbedtls_asn1_write_len(&p, buf, (size_t)len);
++		len += mbedtls_asn1_write_tag(&p, buf,
++		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++	}
++  #endif
++	return wpabuf_alloc_copy(p, (size_t)len);
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++
++struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key,
++						bool include_pub)
++{
++#ifndef MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/
++#define MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES    ( 29 + 3 * MBEDTLS_ECP_MAX_BYTES )
++#endif
++	unsigned char priv[MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES];
++	int privlen = mbedtls_pk_write_key_der((mbedtls_pk_context *)key,
++	                                       priv, sizeof(priv));
++	if (privlen < 0)
++		return NULL;
++
++	struct wpabuf *wbuf;
++
++	/*  Note: data is written at the end of the buffer! Use the
++	 *        return value to determine where you should start
++	 *        using the buffer */
++	/* mbedtls_pk_write_key_der() includes publicKey in DER */
++	if (include_pub)
++		wbuf = wpabuf_alloc_copy(priv+sizeof(priv)-privlen, privlen);
++	else {
++		/* calculate publicKey offset and skip from end of buffer */
++		unsigned char *p = priv+sizeof(priv)-privlen;
++		unsigned char *end = priv+sizeof(priv);
++		size_t len;
++		/* ECPrivateKey SEQUENCE */
++		mbedtls_asn1_get_tag(&p, end, &len,
++		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++		/* version INTEGER */
++		unsigned char *v = p;
++		mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_INTEGER);
++		p += len;
++		/* privateKey OCTET STRING */
++		mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING);
++		p += len;
++		/* parameters ECParameters */
++		mbedtls_asn1_get_tag(&p, end, &len,
++		    MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED);
++		p += len;
++
++		/* write new SEQUENCE header (we know that it fits in priv[]) */
++		len = (size_t)(p - v);
++		p = v;
++		len += mbedtls_asn1_write_len(&p, priv, len);
++		len += mbedtls_asn1_write_tag(&p, priv,
++		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++		wbuf = wpabuf_alloc_copy(p, len);
++	}
++
++	forced_memzero(priv, sizeof(priv));
++	return wbuf;
++}
++
++struct wpabuf * crypto_ec_key_get_pubkey_point(struct crypto_ec_key *key,
++					       int prefix)
++{
++	/*(similarities to crypto_ecdh_get_pubkey(), but different struct)*/
++	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++	if (ecp_kp == NULL)
++		return NULL;
++	mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++	size_t len = CRYPTO_EC_plen(grp);
++  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
++	/* len */
++  #endif
++  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
++	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS)
++		len = len*2+1;
++  #endif
++	struct wpabuf *buf = wpabuf_alloc(len);
++	if (buf == NULL)
++		return NULL;
++	mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++	if (mbedtls_ecp_point_write_binary(grp, ecp_kp_Q,
++	                                   MBEDTLS_ECP_PF_UNCOMPRESSED, &len,
++	                                   wpabuf_mhead_u8(buf), len) == 0) {
++		if (!prefix) /* Remove 0x04 prefix if requested */
++			os_memmove(wpabuf_mhead(buf),wpabuf_mhead(buf)+1,--len);
++		wpabuf_put(buf, len);
++		return buf;
++	}
++
++	wpabuf_free(buf);
++	return NULL;
++}
++
++struct crypto_ec_point *
++crypto_ec_key_get_public_key(struct crypto_ec_key *key)
++{
++	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++	if (ecp_kp == NULL)
++		return NULL;
++	mbedtls_ecp_point *p = os_malloc(sizeof(*p));
++	if (p != NULL) {
++		/*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/
++		mbedtls_ecp_point_init(p);
++		mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++		if (mbedtls_ecp_copy(p, ecp_kp_Q)) {
++			mbedtls_ecp_point_free(p);
++			os_free(p);
++			p = NULL;
++		}
++	}
++	return (struct crypto_ec_point *)p;
++}
++
++struct crypto_bignum *
++crypto_ec_key_get_private_key(struct crypto_ec_key *key)
++{
++	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++	if (ecp_kp == NULL)
++		return NULL;
++	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
++	if (bn) {
++		/*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/
++		mbedtls_mpi_init(bn);
++		mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d);
++		if (mbedtls_mpi_copy(bn, ecp_kp_d)) {
++			mbedtls_mpi_free(bn);
++			os_free(bn);
++			bn = NULL;
++		}
++	}
++	return (struct crypto_bignum *)bn;
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++static mbedtls_md_type_t crypto_ec_key_sign_md(size_t len)
++{
++	/* get mbedtls_md_type_t from length of hash data to be signed */
++	switch (len) {
++	case 64: return MBEDTLS_MD_SHA512;
++	case 48: return MBEDTLS_MD_SHA384;
++	case 32: return MBEDTLS_MD_SHA256;
++	case 20: return MBEDTLS_MD_SHA1;
++	case 16: return MBEDTLS_MD_MD5;
++	default: return MBEDTLS_MD_NONE;
++	}
++}
++
++struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
++				   size_t len)
++{
++  #ifndef MBEDTLS_PK_SIGNATURE_MAX_SIZE /*(defined since mbedtls 2.20.0)*/
++  #if MBEDTLS_ECDSA_MAX_LEN > MBEDTLS_MPI_MAX_SIZE
++  #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_ECDSA_MAX_LEN
++  #else
++  #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_MPI_MAX_SIZE
++  #endif
++  #endif
++	size_t sig_len = MBEDTLS_PK_SIGNATURE_MAX_SIZE;
++	struct wpabuf *buf = wpabuf_alloc(sig_len);
++	if (buf == NULL)
++		return NULL;
++	if (mbedtls_pk_sign((mbedtls_pk_context *)key,
++	                    crypto_ec_key_sign_md(len), data, len,
++	                    wpabuf_mhead_u8(buf),
++  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++	                    sig_len,
++  #endif
++	                    &sig_len,
++	                    mbedtls_ctr_drbg_random,
++	                    crypto_mbedtls_ctr_drbg()) == 0) {
++		wpabuf_put(buf, sig_len);
++		return buf;
++	}
++
++	wpabuf_free(buf);
++	return NULL;
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++struct wpabuf * crypto_ec_key_sign_r_s(struct crypto_ec_key *key,
++				       const u8 *data, size_t len)
++{
++	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++	if (ecp_kp == NULL)
++		return NULL;
++
++	size_t sig_len = MBEDTLS_ECDSA_MAX_LEN;
++	u8 buf[MBEDTLS_ECDSA_MAX_LEN];
++	if (mbedtls_ecdsa_write_signature(ecp_kp, crypto_ec_key_sign_md(len),
++	                                  data, len, buf,
++  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++	                                  sig_len,
++  #endif
++	                                  &sig_len,
++	                                  mbedtls_ctr_drbg_random,
++	                                  crypto_mbedtls_ctr_drbg())) {
++		return NULL;
++	}
++
++	/*(mbedtls_ecdsa_write_signature() writes signature in ASN.1)*/
++	/* parse ASN.1 to get r and s and lengths */
++	u8 *p = buf, *r, *s;
++	u8 *end = p + sig_len;
++	size_t rlen, slen;
++	mbedtls_asn1_get_tag(&p, end, &rlen,
++	  MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++	mbedtls_asn1_get_tag(&p, end, &rlen, MBEDTLS_ASN1_INTEGER);
++	r = p;
++	p += rlen;
++	mbedtls_asn1_get_tag(&p, end, &slen, MBEDTLS_ASN1_INTEGER);
++	s = p;
++
++	/* write raw r and s into out
++	 * (including removal of leading 0 if added for ASN.1 integer)
++	 * note: DPP caller expects raw r, s each padded to prime len */
++	mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++	size_t plen = CRYPTO_EC_plen(ecp_kp_grp);
++	if (rlen > plen) {
++		r += (rlen - plen);
++		rlen = plen;
++	}
++	if (slen > plen) {
++		s += (slen - plen);
++		slen = plen;
++	}
++	struct wpabuf *out = wpabuf_alloc(plen*2);
++	if (out) {
++		wpabuf_put(out, plen*2);
++		p = wpabuf_mhead_u8(out);
++		os_memset(p, 0, plen*2);
++		os_memcpy(p+plen*1-rlen, r, rlen);
++		os_memcpy(p+plen*2-slen, s, slen);
++	}
++	return out;
++}
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
++				   size_t len, const u8 *sig, size_t sig_len)
++{
++	switch (mbedtls_pk_verify((mbedtls_pk_context *)key,
++	                          crypto_ec_key_sign_md(len), data, len,
++	                          sig, sig_len)) {
++	case 0:
++	/*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */
++		return 1;
++	case MBEDTLS_ERR_ECP_VERIFY_FAILED:
++		return 0;
++	default:
++		return -1;
++	}
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++int crypto_ec_key_verify_signature_r_s(struct crypto_ec_key *key,
++				       const u8 *data, size_t len,
++				       const u8 *r, size_t r_len,
++				       const u8 *s, size_t s_len)
++{
++	/* reimplement mbedtls_ecdsa_read_signature() without encoding r and s
++	 * into ASN.1 just for mbedtls_ecdsa_read_signature() to decode ASN.1 */
++	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++	if (ecp_kp == NULL)
++		return -1;
++	mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
++	mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
++
++	mbedtls_mpi mpi_r;
++	mbedtls_mpi mpi_s;
++	mbedtls_mpi_init(&mpi_r);
++	mbedtls_mpi_init(&mpi_s);
++	int ret = mbedtls_mpi_read_binary(&mpi_r, r, r_len)
++	       || mbedtls_mpi_read_binary(&mpi_s, s, s_len) ? -1 : 0;
++	if (ret == 0) {
++		ret = mbedtls_ecdsa_verify(ecp_kp_grp, data, len,
++		                           ecp_kp_Q, &mpi_r, &mpi_s);
++		ret = ret ? ret == MBEDTLS_ERR_ECP_BAD_INPUT_DATA ? 0 : -1 : 1;
++	}
++	mbedtls_mpi_free(&mpi_r);
++	mbedtls_mpi_free(&mpi_s);
++	return ret;
++}
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++int crypto_ec_key_group(struct crypto_ec_key *key)
++{
++	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
++	if (ecp_kp == NULL)
++		return -1;
++	mbedtls_ecp_group *ecp_group = &ecp_kp->MBEDTLS_PRIVATE(grp);
++	return crypto_mbedtls_ike_id_from_ecp_group_id(ecp_group->id);
++}
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
++
++int crypto_ec_key_cmp(struct crypto_ec_key *key1, struct crypto_ec_key *key2)
++{
++#if 0 /*(DPP is passing two public keys; unable to use pk_check_pair())*/
++  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++	return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1,
++	                             (const mbedtls_pk_context *)key2) ? -1 : 0;
++  #else
++	return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1,
++	                             (const mbedtls_pk_context *)key2,
++	                             mbedtls_ctr_drbg_random,
++	                             crypto_mbedtls_ctr_drbg()) ? -1 : 0;
++  #endif
++#else
++	mbedtls_ecp_keypair *ecp_kp1=mbedtls_pk_ec(*(mbedtls_pk_context *)key1);
++	mbedtls_ecp_keypair *ecp_kp2=mbedtls_pk_ec(*(mbedtls_pk_context *)key2);
++	if (ecp_kp1 == NULL || ecp_kp2 == NULL)
++		return -1;
++	mbedtls_ecp_group *ecp_kp1_grp = &ecp_kp1->MBEDTLS_PRIVATE(grp);
++	mbedtls_ecp_group *ecp_kp2_grp = &ecp_kp2->MBEDTLS_PRIVATE(grp);
++	mbedtls_ecp_point *ecp_kp1_Q = &ecp_kp1->MBEDTLS_PRIVATE(Q);
++	mbedtls_ecp_point *ecp_kp2_Q = &ecp_kp2->MBEDTLS_PRIVATE(Q);
++	return ecp_kp1_grp->id != ecp_kp2_grp->id
++	    || mbedtls_ecp_point_cmp(ecp_kp1_Q, ecp_kp2_Q) ? -1 : 0;
++#endif
++}
++
++void crypto_ec_key_debug_print(const struct crypto_ec_key *key,
++			       const char *title)
++{
++	/* TBD: what info is desirable here and in what human readable format?*/
++	/*(crypto_openssl.c prints a human-readably public key and attributes)*/
++  #if 0
++	struct mbedtls_pk_debug_item debug_item;
++	if (mbedtls_pk_debug((const mbedtls_pk_context *)key, &debug_item))
++		return;
++	/* ... */
++  #endif
++	wpa_printf(MSG_DEBUG, "%s: %s not implemented", title, __func__);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_CSR
++
++#include <mbedtls/x509_csr.h>
++#include <mbedtls/oid.h>
++
++struct crypto_csr * crypto_csr_init(void)
++{
++	mbedtls_x509write_csr *csr = os_malloc(sizeof(*csr));
++	if (csr != NULL)
++		mbedtls_x509write_csr_init(csr);
++	return (struct crypto_csr *)csr;
++}
++
++struct crypto_csr * crypto_csr_verify(const struct wpabuf *req)
++{
++	/* future: look for alternatives to MBEDTLS_PRIVATE() access */
++
++	/* sole caller src/common/dpp_crypto.c:dpp_validate_csr()
++	 * uses (mbedtls_x509_csr *) to obtain CSR_ATTR_CHALLENGE_PASSWORD
++	 * so allocate different object (mbedtls_x509_csr *) and special-case
++	 * object when used in crypto_csr_get_attribute() and when free()d in
++	 * crypto_csr_deinit(). */
++
++	mbedtls_x509_csr *csr = os_malloc(sizeof(*csr));
++	if (csr == NULL)
++		return NULL;
++	mbedtls_x509_csr_init(csr);
++	const mbedtls_md_info_t *md_info;
++	unsigned char digest[MBEDTLS_MD_MAX_SIZE];
++	if (mbedtls_x509_csr_parse_der(csr,wpabuf_head(req),wpabuf_len(req))==0
++	    && (md_info=mbedtls_md_info_from_type(csr->MBEDTLS_PRIVATE(sig_md)))
++	       != NULL
++	    && mbedtls_md(md_info, csr->cri.p, csr->cri.len, digest) == 0) {
++		switch (mbedtls_pk_verify(&csr->pk,csr->MBEDTLS_PRIVATE(sig_md),
++		                          digest, mbedtls_md_get_size(md_info),
++		                          csr->MBEDTLS_PRIVATE(sig).p,
++		                          csr->MBEDTLS_PRIVATE(sig).len)) {
++		case 0:
++		/*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */
++			return (struct crypto_csr *)((uintptr_t)csr | 1uL);
++		default:
++			break;
++		}
++	}
++
++	mbedtls_x509_csr_free(csr);
++	os_free(csr);
++	return NULL;
++}
++
++void crypto_csr_deinit(struct crypto_csr *csr)
++{
++	if ((uintptr_t)csr & 1uL) {
++		csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL);
++		mbedtls_x509_csr_free((mbedtls_x509_csr *)csr);
++	}
++	else
++		mbedtls_x509write_csr_free((mbedtls_x509write_csr *)csr);
++	os_free(csr);
++}
++
++int crypto_csr_set_ec_public_key(struct crypto_csr *csr,
++				 struct crypto_ec_key *key)
++{
++	mbedtls_x509write_csr_set_key((mbedtls_x509write_csr *)csr,
++	                              (mbedtls_pk_context *)key);
++	return 0;
++}
++
++int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type,
++			const char *name)
++{
++	/* specialized for src/common/dpp_crypto.c */
++
++	/* sole caller src/common/dpp_crypto.c:dpp_build_csr()
++	 * calls this function only once, using type == CSR_NAME_CN
++	 * (If called more than once, this code would need to append
++	 *  components to the subject name, which we could do by
++	 *  appending to (mbedtls_x509write_csr *) private member
++	 *  mbedtls_asn1_named_data *MBEDTLS_PRIVATE(subject)) */
++
++	const char *label;
++	switch (type) {
++	case CSR_NAME_CN: label = "CN="; break;
++	case CSR_NAME_SN: label = "SN="; break;
++	case CSR_NAME_C:  label = "C=";  break;
++	case CSR_NAME_O:  label = "O=";  break;
++	case CSR_NAME_OU: label = "OU="; break;
++	default: return -1;
++	}
++
++	size_t len = strlen(name);
++	struct wpabuf *buf = wpabuf_alloc(3+len+1);
++	if (buf == NULL)
++		return -1;
++	wpabuf_put_data(buf, label, strlen(label));
++	wpabuf_put_data(buf, name, len+1); /*(include trailing '\0')*/
++	/* Note: 'name' provided is set as given and should be backslash-escaped
++	 * by caller when necessary, e.g. literal ',' which are not separating
++	 * components should be backslash-escaped */
++
++	int ret =
++	  mbedtls_x509write_csr_set_subject_name((mbedtls_x509write_csr *)csr,
++	                                         wpabuf_head(buf)) ? -1 : 0;
++	wpabuf_free(buf);
++	return ret;
++}
++
++/* OBJ_pkcs9_challengePassword  1 2 840 113549 1 9 7 */
++static const char OBJ_pkcs9_challengePassword[] = MBEDTLS_OID_PKCS9 "\x07";
++
++int crypto_csr_set_attribute(struct crypto_csr *csr, enum crypto_csr_attr attr,
++			     int attr_type, const u8 *value, size_t len)
++{
++	/* specialized for src/common/dpp_crypto.c */
++	/* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes
++	 *   attr      == CSR_ATTR_CHALLENGE_PASSWORD
++	 *   attr_type == ASN1_TAG_UTF8STRING */
++
++	const char *oid;
++	size_t oid_len;
++	switch (attr) {
++	case CSR_ATTR_CHALLENGE_PASSWORD:
++		oid = OBJ_pkcs9_challengePassword;
++		oid_len = sizeof(OBJ_pkcs9_challengePassword)-1;
++		break;
++	default:
++		return -1;
++	}
++
++  #if 0 /*(incorrect; sets an extension, not an attribute)*/
++	return mbedtls_x509write_csr_set_extension((mbedtls_x509write_csr *)csr,
++	                                           oid, oid_len,
++	  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++	                                           0, /*(critical flag)*/
++	  #endif
++	                                           value, len) ? -1 : 0;
++  #else
++	(void)oid;
++	(void)oid_len;
++  #endif
++
++	/* mbedtls does not currently provide way to set an attribute in a CSR:
++	 *   https://github.com/Mbed-TLS/mbedtls/issues/4886 */
++	wpa_printf(MSG_ERROR,
++	  "mbedtls does not currently support setting challengePassword "
++	  "attribute in CSR");
++	return -1;
++}
++
++const u8 * mbedtls_x509_csr_attr_oid_value(mbedtls_x509_csr *csr,
++                                           const char *oid, size_t oid_len,
++                                           size_t *vlen, int *vtype)
++{
++	/* Note: mbedtls_x509_csr_parse_der() has parsed and validated CSR,
++	 *	   so validation checks are not repeated here
++	 *
++	 * It would be nicer if (mbedtls_x509_csr *) had an mbedtls_x509_buf of
++	 * Attributes (or at least a pointer) since mbedtls_x509_csr_parse_der()
++	 * already parsed the rest of CertificationRequestInfo, some of which is
++	 * repeated here to step to Attributes.  Since csr->subject_raw.p points
++	 * into csr->cri.p, which points into csr->raw.p, step over version and
++	 * subject of CertificationRequestInfo (SEQUENCE) */
++	unsigned char *p = csr->subject_raw.p + csr->subject_raw.len;
++	unsigned char *end = csr->cri.p + csr->cri.len, *ext;
++	size_t len;
++
++	/* step over SubjectPublicKeyInfo */
++	mbedtls_asn1_get_tag(&p, end, &len,
++	    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
++	p += len;
++
++	/* Attributes
++	 *   { ATTRIBUTE:IOSet } ::= SET OF { SEQUENCE { OID, value } }
++	 */
++	if (mbedtls_asn1_get_tag(&p, end, &len,
++	      MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) != 0) {
++		return NULL;
++	}
++	while (p < end) {
++		if (mbedtls_asn1_get_tag(&p, end, &len,
++		      MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
++			return NULL;
++		}
++		ext = p;
++		p += len;
++
++		if (mbedtls_asn1_get_tag(&ext,end,&len,MBEDTLS_ASN1_OID) != 0)
++			return NULL;
++		if (oid_len != len || 0 != memcmp(ext, oid, oid_len))
++			continue;
++
++		/* found oid; return value */
++		*vtype = *ext++; /* tag */
++		return (mbedtls_asn1_get_len(&ext,end,vlen) == 0) ? ext : NULL;
++	}
++
++	return NULL;
++}
++
++const u8 * crypto_csr_get_attribute(struct crypto_csr *csr,
++				    enum crypto_csr_attr attr,
++				    size_t *len, int *type)
++{
++	/* specialized for src/common/dpp_crypto.c */
++	/* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes
++	 *   attr == CSR_ATTR_CHALLENGE_PASSWORD */
++
++	const char *oid;
++	size_t oid_len;
++	switch (attr) {
++	case CSR_ATTR_CHALLENGE_PASSWORD:
++		oid = OBJ_pkcs9_challengePassword;
++		oid_len = sizeof(OBJ_pkcs9_challengePassword)-1;
++		break;
++	default:
++		return NULL;
++	}
++
++	/* see crypto_csr_verify(); expecting (mbedtls_x509_csr *) tagged |=1 */
++	if (!((uintptr_t)csr & 1uL))
++		return NULL;
++	csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL);
++
++	return mbedtls_x509_csr_attr_oid_value((mbedtls_x509_csr *)csr,
++	                                       oid, oid_len, len, type);
++}
++
++struct wpabuf * crypto_csr_sign(struct crypto_csr *csr,
++				struct crypto_ec_key *key,
++				enum crypto_hash_alg algo)
++{
++	mbedtls_md_type_t sig_md;
++	switch (algo) {
++  #ifdef MBEDTLS_SHA256_C
++	case CRYPTO_HASH_ALG_SHA256: sig_md = MBEDTLS_MD_SHA256; break;
++  #endif
++  #ifdef MBEDTLS_SHA512_C
++	case CRYPTO_HASH_ALG_SHA384: sig_md = MBEDTLS_MD_SHA384; break;
++	case CRYPTO_HASH_ALG_SHA512: sig_md = MBEDTLS_MD_SHA512; break;
++  #endif
++	default:
++		return NULL;
++	}
++	mbedtls_x509write_csr_set_md_alg((mbedtls_x509write_csr *)csr, sig_md);
++
++  #if 0
++	unsigned char key_usage = MBEDTLS_X509_KU_DIGITAL_SIGNATURE
++	                        | MBEDTLS_X509_KU_KEY_CERT_SIGN;
++	if (mbedtls_x509write_csr_set_key_usage((mbedtls_x509write_csr *)csr,
++	                                        key_usage))
++		return NULL;
++  #endif
++
++  #if 0
++	unsigned char ns_cert_type = MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT
++	                           | MBEDTLS_X509_NS_CERT_TYPE_EMAIL;
++	if (mbedtls_x509write_csr_set_ns_cert_type((mbedtls_x509write_csr *)csr,
++	                                           ns_cert_type))
++		return NULL;
++  #endif
++
++  #if 0
++	/* mbedtls does not currently provide way to set an attribute in a CSR:
++	 *   https://github.com/Mbed-TLS/mbedtls/issues/4886
++	 * XXX: hwsim dpp_enterprise test fails due to this limitation.
++	 *
++	 * Current usage of this function is solely by dpp_build_csr(),
++	 * so as a kludge, might consider custom (struct crypto_csr *)
++	 * containing (mbedtls_x509write_csr *) and a list of attributes
++	 * (i.e. challengePassword).  Might have to totally reimplement
++	 * mbedtls_x509write_csr_der(); underlying x509write_csr_der_internal()
++	 * handles signing the CSR.  (This is more work that appending an
++	 * Attributes section to end of CSR and adjusting ASN.1 length of CSR.)
++	 */
++  #endif
++
++	unsigned char buf[4096]; /* XXX: large enough?  too large? */
++	int len = mbedtls_x509write_csr_der((mbedtls_x509write_csr *)csr,
++	                                    buf, sizeof(buf),
++	                                    mbedtls_ctr_drbg_random,
++	                                    crypto_mbedtls_ctr_drbg());
++	if (len < 0)
++		return NULL;
++	/*  Note: data is written at the end of the buffer! Use the
++	 *        return value to determine where you should start
++	 *        using the buffer */
++	return wpabuf_alloc_copy(buf+sizeof(buf)-len, (size_t)len);
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_CSR */
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_PKCS7
++
++#if 0
++#include <mbedtls/pkcs7.h> /* PKCS7 is not currently supported in mbedtls */
++#include <mbedtls/pem.h>
++#endif
++
++struct wpabuf * crypto_pkcs7_get_certificates(const struct wpabuf *pkcs7)
++{
++	/* PKCS7 is not currently supported in mbedtls */
++	return NULL;
++
++#if 0
++	/* https://github.com/naynajain/mbedtls-1 branch: development-pkcs7
++	 * (??? potential future contribution to mbedtls ???) */
++
++	/* Note: PKCS7 signature *is not* verified by this function.
++	 * The function interface does not provide for passing a certificate */
++
++	mbedtls_pkcs7 mpkcs7;
++	mbedtls_pkcs7_init(&mpkcs7);
++	int pkcs7_type = mbedtls_pkcs7_parse_der(wpabuf_head(pkcs7),
++	                                         wpabuf_len(pkcs7),
++	                                         &mpkcs7);
++	wpabuf *buf = NULL;
++	do {
++		if (pkcs7_type < 0)
++			break;
++
++		/* src/common/dpp.c:dpp_parse_cred_dot1x() interested in certs
++		 * for wpa_supplicant/dpp_supplicant.c:wpas_dpp_add_network()
++		 * (? are adding certificate headers and footers desired ?) */
++
++		/* development-pkcs7 branch does not currently provide
++		 * additional interfaces to retrieve the parsed data */
++
++		mbedtls_x509_crt *certs =
++		  &mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(certs);
++		int ncerts =
++		  mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(no_of_certs);
++
++		/* allocate buffer for PEM (base64-encoded DER)
++		 * plus header, footer, newlines, and some extra */
++		buf = wpabuf_alloc((wpabuf_len(pkcs7)+2)/3*4 + ncerts*64);
++		if (buf == NULL)
++			break;
++
++		#define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n"
++		#define PEM_END_CRT   "-----END CERTIFICATE-----\n"
++		size_t olen;
++		for (int i = 0; i < ncerts; ++i) {
++			int ret = mbedtls_pem_write_buffer(
++			            PEM_BEGIN_CRT, PEM_END_CRT,
++			            certs[i].raw.p, certs[i].raw.len,
++			            wpabuf_mhead(buf, 0), wpabuf_tailroom(buf),
++			            &olen));
++			if (ret == 0)
++				wpabuf_put(buf, olen);
++			} else {
++				if (ret == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL)
++					ret = wpabuf_resize(
++					        &buf,olen-wpabuf_tailroom(buf));
++				if (ret == 0) {
++					--i;/*(adjust loop iterator for retry)*/
++					continue;
++				}
++				wpabuf_free(buf);
++				buf = NULL;
++				break;
++			}
++		}
++	} while (0);
++
++	mbedtls_pkcs7_free(&mpkcs7);
++	return buf;
++#endif
++}
++
++#endif /* CRYPTO_MBEDTLS_CRYPTO_PKCS7 */
++
++
++#ifdef MBEDTLS_ARC4_C
++#include <mbedtls/arc4.h>
++int rc4_skip(const u8 *key, size_t keylen, size_t skip,
++	     u8 *data, size_t data_len)
++{
++	mbedtls_arc4_context ctx;
++	mbedtls_arc4_init(&ctx);
++	mbedtls_arc4_setup(&ctx, key, keylen);
++
++	if (skip) {
++		/*(prefer [16] on ancient hardware with smaller cache lines)*/
++		unsigned char skip_buf[64]; /*('skip' is generally small)*/
++		/*os_memset(skip_buf, 0, sizeof(skip_buf));*/ /*(necessary?)*/
++		size_t len;
++		do {
++			len = skip > sizeof(skip_buf) ? sizeof(skip_buf) : skip;
++			mbedtls_arc4_crypt(&ctx, len, skip_buf, skip_buf);
++		} while ((skip -= len));
++	}
++
++	int ret = mbedtls_arc4_crypt(&ctx, data_len, data, data);
++	mbedtls_arc4_free(&ctx);
++	return ret;
++}
++#endif
++
++
++/* duplicated in tls_mbedtls.c:tls_mbedtls_readfile()*/
++__attribute_noinline__
++static int crypto_mbedtls_readfile(const char *path, u8 **buf, size_t *n)
++{
++  #if 0 /* #ifdef MBEDTLS_FS_IO */
++	/*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/
++	if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) {
++		wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path);
++		return -1;
++	}
++  #else
++	/*(use os_readfile() so that we can use os_free()
++	 *(if we use mbedtls_pk_load_file() above, macros prevent calling free()
++	 * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free()
++	 * on buf aborts in tests if buf not allocated via os_malloc())*/
++	*buf = (u8 *)os_readfile(path, n);
++	if (!*buf) {
++		wpa_printf(MSG_ERROR, "error: os_readfile %s", path);
++		return -1;
++	}
++	u8 *buf0 = os_realloc(*buf, *n+1);
++	if (!buf0) {
++		bin_clear_free(*buf, *n);
++		*buf = NULL;
++		return -1;
++	}
++	buf0[(*n)++] = '\0';
++	*buf = buf0;
++  #endif
++	return 0;
++}
++
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_RSA
++#ifdef MBEDTLS_RSA_C
++
++#include <mbedtls/pk.h>
++#include <mbedtls/rsa.h>
++
++struct crypto_rsa_key * crypto_rsa_key_read(const char *file, bool private_key)
++{
++	/* mbedtls_pk_parse_keyfile() and mbedtls_pk_parse_public_keyfile()
++	 * require #ifdef MBEDTLS_FS_IO in mbedtls library.  Prefer to use
++	 * crypto_mbedtls_readfile(), which wraps os_readfile() */
++	u8 *data;
++	size_t len;
++	if (crypto_mbedtls_readfile(file, &data, &len) != 0)
++		return NULL;
++
++	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
++	if (ctx == NULL) {
++		bin_clear_free(data, len);
++		return NULL;
++	}
++	mbedtls_pk_init(ctx);
++
++	int rc;
++	rc = (private_key
++	      ? mbedtls_pk_parse_key(ctx, data, len, NULL, 0
++	  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++	                            ,mbedtls_ctr_drbg_random,
++	                             crypto_mbedtls_ctr_drbg()
++	  #endif
++	                            )
++	      : mbedtls_pk_parse_public_key(ctx, data, len)) == 0
++	    && mbedtls_pk_can_do(ctx, MBEDTLS_PK_RSA);
++
++	bin_clear_free(data, len);
++
++	if (rc) {
++		/* use MBEDTLS_RSA_PKCS_V21 padding for RSAES-OAEP */
++		/* use MBEDTLS_MD_SHA256 for these hostap interfaces */
++	  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++		/*(no return value in mbedtls 2.x)*/
++		mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx),
++		                        MBEDTLS_RSA_PKCS_V21,
++		                        MBEDTLS_MD_SHA256);
++	  #else
++		if (mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx),
++		                            MBEDTLS_RSA_PKCS_V21,
++		                            MBEDTLS_MD_SHA256) == 0)
++	  #endif
++			return (struct crypto_rsa_key *)ctx;
++	}
++
++	mbedtls_pk_free(ctx);
++	os_free(ctx);
++	return NULL;
++}
++
++struct wpabuf * crypto_rsa_oaep_sha256_encrypt(struct crypto_rsa_key *key,
++					       const struct wpabuf *in)
++{
++	mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key);
++	size_t olen = mbedtls_rsa_get_len(pk_rsa);
++	struct wpabuf *buf = wpabuf_alloc(olen);
++	if (buf == NULL)
++		return NULL;
++
++	/* mbedtls_pk_encrypt() takes a few more hops to get to same func */
++	if (mbedtls_rsa_rsaes_oaep_encrypt(pk_rsa,
++	                                   mbedtls_ctr_drbg_random,
++	                                   crypto_mbedtls_ctr_drbg(),
++	  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++	                                   MBEDTLS_RSA_PRIVATE,
++	  #endif
++	                                   NULL, 0,
++	                                   wpabuf_len(in), wpabuf_head(in),
++	                                   wpabuf_put(buf, olen)) == 0) {
++		return buf;
++	}
++
++	wpabuf_clear_free(buf);
++	return NULL;
++}
++
++struct wpabuf * crypto_rsa_oaep_sha256_decrypt(struct crypto_rsa_key *key,
++					       const struct wpabuf *in)
++{
++	mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key);
++	size_t olen = mbedtls_rsa_get_len(pk_rsa);
++	struct wpabuf *buf = wpabuf_alloc(olen);
++	if (buf == NULL)
++		return NULL;
++
++	/* mbedtls_pk_decrypt() takes a few more hops to get to same func */
++	if (mbedtls_rsa_rsaes_oaep_decrypt(pk_rsa,
++	                                   mbedtls_ctr_drbg_random,
++	                                   crypto_mbedtls_ctr_drbg(),
++	  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++	                                   MBEDTLS_RSA_PUBLIC,
++	  #endif
++	                                   NULL, 0, &olen, wpabuf_head(in),
++	                                   wpabuf_mhead(buf), olen) == 0) {
++		wpabuf_put(buf, olen);
++		return buf;
++	}
++
++	wpabuf_clear_free(buf);
++	return NULL;
++}
++
++void crypto_rsa_key_free(struct crypto_rsa_key *key)
++{
++	mbedtls_pk_free((mbedtls_pk_context *)key);
++	os_free(key);
++}
++
++#endif /* MBEDTLS_RSA_C */
++#endif /* CRYPTO_MBEDTLS_CRYPTO_RSA */
++
++#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE
++
++struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id,
++			       enum hpke_kdf_id kdf_id,
++			       enum hpke_aead_id aead_id,
++			       struct crypto_ec_key *peer_pub,
++			       const u8 *info, size_t info_len,
++			       const u8 *aad, size_t aad_len,
++			       const u8 *pt, size_t pt_len)
++{
++	/* not yet implemented */
++	return NULL;
++}
++
++struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
++			       enum hpke_kdf_id kdf_id,
++			       enum hpke_aead_id aead_id,
++			       struct crypto_ec_key *own_priv,
++			       const u8 *info, size_t info_len,
++			       const u8 *aad, size_t aad_len,
++			       const u8 *enc_ct, size_t enc_ct_len)
++{
++	/* not yet implemented */
++	return NULL;
++}
++
++#endif
+diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
+index ffeddbadd..07c36d850 100644
+--- a/src/crypto/crypto_module_tests.c
++++ b/src/crypto/crypto_module_tests.c
+@@ -2470,6 +2470,139 @@ static int test_hpke(void)
+ }
+ 
+ 
++static int test_ecc(void)
++{
++#ifdef CONFIG_ECC
++#ifndef CONFIG_TLS_INTERNAL
++#ifndef CONFIG_TLS_GNUTLS
++#if defined(CONFIG_TLS_MBEDTLS) \
++ || defined(CONFIG_TLS_OPENSSL) \
++ || defined(CONFIG_TLS_WOLFSSL)
++	wpa_printf(MSG_INFO, "Testing ECC");
++	/* Note: some tests below are valid on supported Short Weierstrass
++	 * curves, but not on Montgomery curves (e.g. IKE groups 31 and 32)
++	 * (e.g. deriving and comparing y^2 test below not valid on Montgomery)
++	 */
++#ifdef CONFIG_TLS_MBEDTLS
++	const int grps[] = {19, 20, 21, 25, 26, 28};
++#endif
++#ifdef CONFIG_TLS_OPENSSL
++	const int grps[] = {19, 20, 21, 26};
++#endif
++#ifdef CONFIG_TLS_WOLFSSL
++	const int grps[] = {19, 20, 21, 26};
++#endif
++	uint32_t i;
++	struct crypto_ec *e = NULL;
++	struct crypto_ec_point *p = NULL, *q = NULL;
++	struct crypto_bignum *x = NULL, *y = NULL;
++#ifdef CONFIG_DPP
++	u8 bin[4096];
++#endif
++	for (i = 0; i < ARRAY_SIZE(grps); ++i) {
++		e = crypto_ec_init(grps[i]);
++		if (e == NULL
++		    || crypto_ec_prime_len(e) == 0
++		    || crypto_ec_prime_len_bits(e) == 0
++		    || crypto_ec_order_len(e) == 0
++		    || crypto_ec_get_prime(e) == NULL
++		    || crypto_ec_get_order(e) == NULL
++		    || crypto_ec_get_a(e) == NULL
++		    || crypto_ec_get_b(e) == NULL
++		    || crypto_ec_get_generator(e) == NULL) {
++			break;
++		}
++#ifdef CONFIG_DPP
++		struct crypto_ec_key *key = crypto_ec_key_gen(grps[i]);
++		if (key == NULL)
++			break;
++		p = crypto_ec_key_get_public_key(key);
++		q = crypto_ec_key_get_public_key(key);
++		crypto_ec_key_deinit(key);
++		if (p == NULL || q == NULL)
++			break;
++		if (!crypto_ec_point_is_on_curve(e, p))
++			break;
++
++		/* inverted point should not match original;
++		 * double-invert should match */
++		if (crypto_ec_point_invert(e, q) != 0
++		    || crypto_ec_point_cmp(e, p, q) == 0
++		    || crypto_ec_point_invert(e, q) != 0
++		    || crypto_ec_point_cmp(e, p, q) != 0) {
++			break;
++		}
++
++		/* crypto_ec_point_to_bin() and crypto_ec_point_from_bin()
++		 * imbalanced interfaces? */
++		size_t prime_len = crypto_ec_prime_len(e);
++		if (prime_len * 2 > sizeof(bin))
++			break;
++		if (crypto_ec_point_to_bin(e, p, bin, bin+prime_len) != 0)
++			break;
++		struct crypto_ec_point *tmp = crypto_ec_point_from_bin(e, bin);
++		if (tmp == NULL)
++			break;
++		if (crypto_ec_point_cmp(e, p, tmp) != 0) {
++			crypto_ec_point_deinit(tmp, 0);
++			break;
++		}
++		crypto_ec_point_deinit(tmp, 0);
++
++		x = crypto_bignum_init();
++		y = crypto_bignum_init_set(bin+prime_len, prime_len);
++		if (x == NULL || y == NULL || crypto_ec_point_x(e, p, x) != 0)
++			break;
++		struct crypto_bignum *y2 = crypto_ec_point_compute_y_sqr(e, x);
++		if (y2 == NULL)
++			break;
++		if (crypto_bignum_sqrmod(y, crypto_ec_get_prime(e), y) != 0
++		    || crypto_bignum_cmp(y, y2) != 0) {
++			crypto_bignum_deinit(y2, 0);
++			break;
++		}
++		crypto_bignum_deinit(y2, 0);
++		crypto_bignum_deinit(x, 0);
++		crypto_bignum_deinit(y, 0);
++		x = NULL;
++		y = NULL;
++
++		x = crypto_bignum_init();
++		if (x == NULL)
++			break;
++		if (crypto_bignum_rand(x, crypto_ec_get_prime(e)) != 0)
++			break;
++		crypto_bignum_deinit(x, 0);
++		x = NULL;
++
++		crypto_ec_point_deinit(p, 0);
++		p = NULL;
++		crypto_ec_point_deinit(q, 0);
++		q = NULL;
++#endif /* CONFIG_DPP */
++		crypto_ec_deinit(e);
++		e = NULL;
++	}
++	if (i != ARRAY_SIZE(grps)) {
++		crypto_bignum_deinit(x, 0);
++		crypto_bignum_deinit(y, 0);
++		crypto_ec_point_deinit(p, 0);
++		crypto_ec_point_deinit(q, 0);
++		crypto_ec_deinit(e);
++		wpa_printf(MSG_INFO,
++		           "ECC test case failed tls_id:%d", grps[i]);
++		return -1;
++	}
++
++	wpa_printf(MSG_INFO, "ECC test cases passed");
++#endif
++#endif /* !CONFIG_TLS_GNUTLS */
++#endif /* !CONFIG_TLS_INTERNAL */
++#endif /* CONFIG_ECC */
++	return 0;
++}
++
++
+ static int test_ms_funcs(void)
+ {
+ #ifndef CONFIG_FIPS
+@@ -2591,6 +2724,7 @@ int crypto_module_tests(void)
+ 	    test_fips186_2_prf() ||
+ 	    test_extract_expand_hkdf() ||
+ 	    test_hpke() ||
++	    test_ecc() ||
+ 	    test_ms_funcs())
+ 		ret = -1;
+ 
+diff --git a/src/crypto/tls_mbedtls.c b/src/crypto/tls_mbedtls.c
+new file mode 100644
+index 000000000..d83a3db73
+--- /dev/null
++++ b/src/crypto/tls_mbedtls.c
+@@ -0,0 +1,3313 @@
++/*
++ * SSL/TLS interface functions for mbed TLS
++ *
++ * SPDX-FileCopyrightText: 2022 Glenn Strauss <gstrauss@gluelogic.com>
++ * SPDX-License-Identifier: BSD-3-Clause
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ *
++ * template:  src/crypto/tls_none.c
++ * reference: src/crypto/tls_*.c
++ *
++ * Known Limitations:
++ * - no TLSv1.3 (not available in mbedtls 2.x; experimental in mbedtls 3.x)
++ * - no OCSP (not yet available in mbedtls)
++ * - mbedtls does not support all certificate encodings used by hwsim tests
++ *   PCKS#5 v1.5
++ *   PCKS#12
++ *   DH DSA
++ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
++ * - mbedtls does not currently provide way to set an attribute in a CSR
++ *     https://github.com/Mbed-TLS/mbedtls/issues/4886
++ *   so tests/hwsim dpp_enterprise tests fail
++ * - DPP2 not supported
++ *   PKCS#7 parsing is not supported in mbedtls
++ *   See crypto_mbedtls.c:crypto_pkcs7_get_certificates() comments
++ * - DPP3 not supported
++ *   hpke_base_seal() and hpke_base_seal() not implemented in crypto_mbedtls.c
++ *
++ * Status:
++ * - code written to be compatible with mbedtls 2.x and mbedtls 3.x
++ *   (currently requires mbedtls >= 2.27.0 for mbedtls_mpi_random())
++ *   (currently requires mbedtls >= 2.18.0 for mbedtls_ssl_tls_prf())
++ * - builds with tests/build/build-wpa_supplicant-mbedtls.config
++ * - passes all tests/ crypto module tests (incomplete coverage)
++ *   ($ cd tests; make clean; make -j 4 run-tests CONFIG_TLS=mbedtls)
++ * - passes almost all tests/hwsim tests
++ *   (hwsim tests skipped for missing features)
++ *
++ * RFE:
++ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
++ * - client/server session resumption, and/or save client session ticket
++ */
++
++#include "includes.h"
++#include "common.h"
++
++#include <mbedtls/version.h>
++#include <mbedtls/ctr_drbg.h>
++#include <mbedtls/error.h>
++#include <mbedtls/oid.h>
++#include <mbedtls/pem.h>
++#include <mbedtls/platform.h> /* mbedtls_calloc() mbedtls_free() */
++#include <mbedtls/platform_util.h> /* mbedtls_platform_zeroize() */
++#include <mbedtls/ssl.h>
++#include <mbedtls/ssl_ticket.h>
++#include <mbedtls/x509.h>
++#include <mbedtls/x509_crt.h>
++
++#if MBEDTLS_VERSION_NUMBER >= 0x02040000 /* mbedtls 2.4.0 */
++#include <mbedtls/net_sockets.h>
++#else
++#include <mbedtls/net.h>
++#endif
++
++#ifndef MBEDTLS_PRIVATE
++#define MBEDTLS_PRIVATE(x) x
++#endif
++
++#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
++#define mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl) \
++        ((ssl)->MBEDTLS_PRIVATE(session) \
++        ?(ssl)->MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(ciphersuite) \
++        : 0)
++#define mbedtls_ssl_ciphersuite_get_name(info) \
++        (info)->MBEDTLS_PRIVATE(name)
++#endif
++
++#include "crypto.h"     /* sha256_vector() */
++#include "tls.h"
++
++#ifndef SHA256_DIGEST_LENGTH
++#define SHA256_DIGEST_LENGTH 32
++#endif
++
++#ifndef MBEDTLS_EXPKEY_FIXED_SECRET_LEN
++#define MBEDTLS_EXPKEY_FIXED_SECRET_LEN 48
++#endif
++
++#ifndef MBEDTLS_EXPKEY_RAND_LEN
++#define MBEDTLS_EXPKEY_RAND_LEN 32
++#endif
++
++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++static mbedtls_ssl_export_keys_t tls_connection_export_keys_cb;
++#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++static mbedtls_ssl_export_keys_ext_t tls_connection_export_keys_cb;
++#else /*(not implemented; return error)*/
++#define mbedtls_ssl_tls_prf(a,b,c,d,e,f,g,h) (-1)
++typedef mbedtls_tls_prf_types int;
++#endif
++
++
++/* hostapd/wpa_supplicant provides forced_memzero(),
++ * but prefer mbedtls_platform_zeroize() */
++#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz)
++
++
++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
++ || defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
++#ifdef MBEDTLS_SSL_SESSION_TICKETS
++#ifdef MBEDTLS_SSL_TICKET_C
++#define TLS_MBEDTLS_SESSION_TICKETS
++#if defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
++#define TLS_MBEDTLS_EAP_TEAP
++#endif
++#if !defined(CONFIG_FIPS) /* EAP-FAST keys cannot be exported in FIPS mode */
++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
++#define TLS_MBEDTLS_EAP_FAST
++#endif
++#endif
++#endif
++#endif
++#endif
++
++
++struct tls_conf {
++	mbedtls_ssl_config conf;
++
++	unsigned int verify_peer:1;
++	unsigned int verify_depth0_only:1;
++	unsigned int check_crl:2;           /*(needs :2 bits for 0, 1, 2)*/
++	unsigned int check_crl_strict:1;    /*(needs :1 bit  for 0, 1)*/
++	unsigned int ca_cert_probe:1;
++	unsigned int has_ca_cert:1;
++	unsigned int has_client_cert:1;
++	unsigned int has_private_key:1;
++	unsigned int suiteb128:1;
++	unsigned int suiteb192:1;
++	mbedtls_x509_crl *crl;
++	mbedtls_x509_crt ca_cert;
++	mbedtls_x509_crt client_cert;
++	mbedtls_pk_context private_key;
++
++	uint32_t refcnt;
++
++	unsigned int flags;
++	char *subject_match;
++	char *altsubject_match;
++	char *suffix_match;
++	char *domain_match;
++	char *check_cert_subject;
++	u8 ca_cert_hash[SHA256_DIGEST_LENGTH];
++
++	int *ciphersuites;  /* list of ciphersuite ids for mbedtls_ssl_config */
++#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
++	mbedtls_ecp_group_id *curves;
++#else
++	uint16_t *curves;   /* list of curve ids for mbedtls_ssl_config */
++#endif
++};
++
++
++struct tls_global {
++	struct tls_conf *tls_conf;
++	char *ocsp_stapling_response;
++	mbedtls_ctr_drbg_context *ctr_drbg; /*(see crypto_mbedtls.c)*/
++  #ifdef MBEDTLS_SSL_SESSION_TICKETS
++	mbedtls_ssl_ticket_context ticket_ctx;
++  #endif
++	char *ca_cert_file;
++	struct os_reltime crl_reload_previous;
++	unsigned int crl_reload_interval;
++	uint32_t refcnt;
++	struct tls_config init_conf;
++};
++
++static struct tls_global tls_ctx_global;
++
++
++struct tls_connection {
++	struct tls_conf *tls_conf;
++	struct wpabuf *push_buf;
++	struct wpabuf *pull_buf;
++	size_t pull_buf_offset;
++
++	unsigned int established:1;
++	unsigned int resumed:1;
++	unsigned int verify_peer:1;
++	unsigned int is_server:1;
++
++	mbedtls_ssl_context ssl;
++
++	mbedtls_tls_prf_types tls_prf_type;
++	size_t expkey_keyblock_size;
++	size_t expkey_secret_len;
++  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
++	unsigned char expkey_secret[MBEDTLS_EXPKEY_FIXED_SECRET_LEN];
++  #else
++	unsigned char expkey_secret[MBEDTLS_MD_MAX_SIZE];
++  #endif
++	unsigned char expkey_randbytes[MBEDTLS_EXPKEY_RAND_LEN*2];
++
++	int read_alerts, write_alerts, failed;
++
++  #ifdef TLS_MBEDTLS_SESSION_TICKETS
++	tls_session_ticket_cb session_ticket_cb;
++	void *session_ticket_cb_ctx;
++	unsigned char *clienthello_session_ticket;
++	size_t clienthello_session_ticket_len;
++  #endif
++	char *peer_subject; /* peer subject info for authenticated peer */
++	struct wpabuf *success_data;
++};
++
++
++#ifndef __has_attribute
++#define __has_attribute(x) 0
++#endif
++
++#ifndef __GNUC_PREREQ
++#define __GNUC_PREREQ(maj,min) 0
++#endif
++
++#ifndef __attribute_cold__
++#if __has_attribute(cold) \
++ || __GNUC_PREREQ(4,3)
++#define __attribute_cold__  __attribute__((__cold__))
++#else
++#define __attribute_cold__
++#endif
++#endif
++
++#ifndef __attribute_noinline__
++#if __has_attribute(noinline) \
++ || __GNUC_PREREQ(3,1)
++#define __attribute_noinline__  __attribute__((__noinline__))
++#else
++#define __attribute_noinline__
++#endif
++#endif
++
++
++__attribute_cold__
++__attribute_noinline__
++static void emsg(int level, const char * const msg)
++{
++	wpa_printf(level, "MTLS: %s", msg);
++}
++
++
++__attribute_cold__
++__attribute_noinline__
++static void emsgrc(int level, const char * const msg, int rc)
++{
++  #ifdef MBEDTLS_ERROR_C
++	/* error logging convenience function that decodes mbedtls result codes */
++	char buf[256];
++	mbedtls_strerror(rc, buf, sizeof(buf));
++	wpa_printf(level, "MTLS: %s: %s (-0x%04x)", msg, buf, -rc);
++  #else
++	wpa_printf(level, "MTLS: %s: (-0x%04x)", msg, -rc);
++  #endif
++}
++
++
++#define elog(rc, msg) emsgrc(MSG_ERROR, (msg), (rc))
++#define ilog(rc, msg) emsgrc(MSG_INFO,  (msg), (rc))
++
++
++struct tls_conf * tls_conf_init(void *tls_ctx)
++{
++	struct tls_conf *tls_conf = os_zalloc(sizeof(*tls_conf));
++	if (tls_conf == NULL)
++		return NULL;
++	tls_conf->refcnt = 1;
++
++	mbedtls_ssl_config_init(&tls_conf->conf);
++	mbedtls_ssl_conf_rng(&tls_conf->conf,
++			     mbedtls_ctr_drbg_random, tls_ctx_global.ctr_drbg);
++	mbedtls_x509_crt_init(&tls_conf->ca_cert);
++	mbedtls_x509_crt_init(&tls_conf->client_cert);
++	mbedtls_pk_init(&tls_conf->private_key);
++
++	return tls_conf;
++}
++
++
++void tls_conf_deinit(struct tls_conf *tls_conf)
++{
++	if (tls_conf == NULL || --tls_conf->refcnt != 0)
++		return;
++
++	mbedtls_x509_crt_free(&tls_conf->ca_cert);
++	mbedtls_x509_crt_free(&tls_conf->client_cert);
++	if (tls_conf->crl) {
++		mbedtls_x509_crl_free(tls_conf->crl);
++		os_free(tls_conf->crl);
++	}
++	mbedtls_pk_free(&tls_conf->private_key);
++	mbedtls_ssl_config_free(&tls_conf->conf);
++	os_free(tls_conf->curves);
++	os_free(tls_conf->ciphersuites);
++	os_free(tls_conf->subject_match);
++	os_free(tls_conf->altsubject_match);
++	os_free(tls_conf->suffix_match);
++	os_free(tls_conf->domain_match);
++	os_free(tls_conf->check_cert_subject);
++	os_free(tls_conf);
++}
++
++
++mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/
++
++__attribute_cold__
++void * tls_init(const struct tls_config *conf)
++{
++	/* RFE: review struct tls_config *conf (different from tls_conf) */
++
++	if (++tls_ctx_global.refcnt > 1)
++		return &tls_ctx_global;
++
++	tls_ctx_global.ctr_drbg = crypto_mbedtls_ctr_drbg();
++  #ifdef MBEDTLS_SSL_SESSION_TICKETS
++	mbedtls_ssl_ticket_init(&tls_ctx_global.ticket_ctx);
++	mbedtls_ssl_ticket_setup(&tls_ctx_global.ticket_ctx,
++	                         mbedtls_ctr_drbg_random,
++	                         tls_ctx_global.ctr_drbg,
++	                         MBEDTLS_CIPHER_AES_256_GCM,
++	                         43200); /* ticket timeout: 12 hours */
++  #endif
++	/* copy struct for future use */
++	tls_ctx_global.init_conf = *conf;
++	if (conf->openssl_ciphers)
++		tls_ctx_global.init_conf.openssl_ciphers =
++		  os_strdup(conf->openssl_ciphers);
++
++	tls_ctx_global.crl_reload_interval = conf->crl_reload_interval;
++	os_get_reltime(&tls_ctx_global.crl_reload_previous);
++
++	return &tls_ctx_global;
++}
++
++
++__attribute_cold__
++void tls_deinit(void *tls_ctx)
++{
++	if (tls_ctx == NULL || --tls_ctx_global.refcnt != 0)
++		return;
++
++	tls_conf_deinit(tls_ctx_global.tls_conf);
++	os_free(tls_ctx_global.ca_cert_file);
++	os_free(tls_ctx_global.ocsp_stapling_response);
++	char *openssl_ciphers; /*(allocated in tls_init())*/
++	*(const char **)&openssl_ciphers =
++	  tls_ctx_global.init_conf.openssl_ciphers;
++	os_free(openssl_ciphers);
++  #ifdef MBEDTLS_SSL_SESSION_TICKETS
++	mbedtls_ssl_ticket_free(&tls_ctx_global.ticket_ctx);
++  #endif
++	os_memset(&tls_ctx_global, 0, sizeof(tls_ctx_global));
++}
++
++
++int tls_get_errors(void *tls_ctx)
++{
++	return 0;
++}
++
++
++static void tls_connection_deinit_expkey(struct tls_connection *conn)
++{
++	conn->tls_prf_type = 0; /* MBEDTLS_SSL_TLS_PRF_NONE; */
++	conn->expkey_keyblock_size = 0;
++	conn->expkey_secret_len = 0;
++	forced_memzero(conn->expkey_secret, sizeof(conn->expkey_secret));
++	forced_memzero(conn->expkey_randbytes, sizeof(conn->expkey_randbytes));
++}
++
++
++#ifdef TLS_MBEDTLS_SESSION_TICKETS
++void tls_connection_deinit_clienthello_session_ticket(struct tls_connection *conn)
++{
++	if (conn->clienthello_session_ticket) {
++		mbedtls_platform_zeroize(conn->clienthello_session_ticket,
++		                         conn->clienthello_session_ticket_len);
++		mbedtls_free(conn->clienthello_session_ticket);
++		conn->clienthello_session_ticket = NULL;
++		conn->clienthello_session_ticket_len = 0;
++	}
++}
++#endif
++
++
++void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
++{
++	if (conn == NULL)
++		return;
++
++  #if 0 /*(good intention, but never sent since we destroy self below)*/
++	if (conn->established)
++		mbedtls_ssl_close_notify(&conn->ssl);
++  #endif
++
++	if (conn->tls_prf_type)
++		tls_connection_deinit_expkey(conn);
++
++  #ifdef TLS_MBEDTLS_SESSION_TICKETS
++	if (conn->clienthello_session_ticket)
++		tls_connection_deinit_clienthello_session_ticket(conn);
++  #endif
++
++	os_free(conn->peer_subject);
++	wpabuf_free(conn->success_data);
++	wpabuf_free(conn->push_buf);
++	wpabuf_free(conn->pull_buf);
++	mbedtls_ssl_free(&conn->ssl);
++	tls_conf_deinit(conn->tls_conf);
++	os_free(conn);
++}
++
++
++static void tls_mbedtls_refresh_crl(void);
++static int tls_mbedtls_ssl_setup(struct tls_connection *conn);
++
++struct tls_connection * tls_connection_init(void *tls_ctx)
++{
++	struct tls_connection *conn = os_zalloc(sizeof(*conn));
++	if (conn == NULL)
++		return NULL;
++
++	mbedtls_ssl_init(&conn->ssl);
++
++	conn->tls_conf = tls_ctx_global.tls_conf; /*(inherit global conf, if set)*/
++	if (conn->tls_conf) {
++		++conn->tls_conf->refcnt;
++		/* check for CRL refresh if inheriting from global config */
++		tls_mbedtls_refresh_crl();
++
++		conn->verify_peer = conn->tls_conf->verify_peer;
++		if (tls_mbedtls_ssl_setup(conn) != 0) {
++			tls_connection_deinit(&tls_ctx_global, conn);
++			return NULL;
++		}
++	}
++
++	return conn;
++}
++
++
++int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
++{
++	return conn ? conn->established : 0;
++}
++
++
++__attribute_noinline__
++char * tls_mbedtls_peer_serial_num(const mbedtls_x509_crt *crt, char *serial_num, size_t len)
++{
++	/* mbedtls_x509_serial_gets() inefficiently formats to hex separated by
++	 * colons, so generate the hex serial number here.  The func
++	 * wpa_snprintf_hex_uppercase() is similarly inefficient. */
++	size_t i = 0; /* skip leading 0's per Distinguished Encoding Rules (DER) */
++	while (i < crt->serial.len && crt->serial.p[i] == 0) ++i;
++	if (i == crt->serial.len) --i;
++
++	const unsigned char *s = crt->serial.p + i;
++	const size_t e = (crt->serial.len - i) * 2;
++	if (e >= len)
++		return NULL;
++  #if 0
++	wpa_snprintf_hex_uppercase(serial_num, len, s, crt->serial.len-i);
++  #else
++	for (i = 0; i < e; i+=2, ++s) {
++		serial_num[i+0] = "0123456789ABCDEF"[(*s >>  4)];
++		serial_num[i+1] = "0123456789ABCDEF"[(*s & 0xF)];
++	}
++	serial_num[e] = '\0';
++  #endif
++	return serial_num;
++}
++
++
++char * tls_connection_peer_serial_num(void *tls_ctx,
++				      struct tls_connection *conn)
++{
++	const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&conn->ssl);
++	if (crt == NULL)
++		return NULL;
++	size_t len = crt->serial.len * 2 + 1;
++	char *serial_num = os_malloc(len);
++	if (!serial_num)
++		return NULL;
++	return tls_mbedtls_peer_serial_num(crt, serial_num, len);
++}
++
++
++static void tls_pull_buf_reset(struct tls_connection *conn);
++
++int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
++{
++	/* Note: this function called from eap_peer_tls_reauth_init()
++	 * for session resumption, not for connection shutdown */
++
++	if (conn == NULL)
++		return -1;
++
++	tls_pull_buf_reset(conn);
++	wpabuf_free(conn->push_buf);
++	conn->push_buf = NULL;
++	conn->established = 0;
++	conn->resumed = 0;
++	if (conn->tls_prf_type)
++		tls_connection_deinit_expkey(conn);
++
++	/* RFE: prepare for session resumption? (see doc in crypto/tls.h) */
++
++	return mbedtls_ssl_session_reset(&conn->ssl);
++}
++
++
++static int tls_wpabuf_resize_put_data(struct wpabuf **buf,
++                                      const unsigned char *data, size_t dlen)
++{
++	if (wpabuf_resize(buf, dlen) < 0)
++		return 0;
++	wpabuf_put_data(*buf, data, dlen);
++	return 1;
++}
++
++
++static int tls_pull_buf_append(struct tls_connection *conn,
++                               const struct wpabuf *in_data)
++{
++	/*(interface does not lend itself to move semantics)*/
++	return tls_wpabuf_resize_put_data(&conn->pull_buf,
++	                                  wpabuf_head(in_data),
++	                                  wpabuf_len(in_data));
++}
++
++
++static void tls_pull_buf_reset(struct tls_connection *conn)
++{
++	/*(future: might consider reusing conn->pull_buf)*/
++	wpabuf_free(conn->pull_buf);
++	conn->pull_buf = NULL;
++	conn->pull_buf_offset = 0;
++}
++
++
++__attribute_cold__
++static void tls_pull_buf_discard(struct tls_connection *conn, const char *func)
++{
++	size_t discard = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
++	if (discard)
++		wpa_printf(MSG_DEBUG,
++			   "%s - %zu bytes remaining in pull_buf; discarding",
++			   func, discard);
++	tls_pull_buf_reset(conn);
++}
++
++
++static int tls_pull_func(void *ptr, unsigned char *buf, size_t len)
++{
++	struct tls_connection *conn = (struct tls_connection *) ptr;
++	if (conn->pull_buf == NULL)
++		return MBEDTLS_ERR_SSL_WANT_READ;
++	const size_t dlen = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
++	if (dlen == 0)
++		return MBEDTLS_ERR_SSL_WANT_READ;
++
++	if (len > dlen)
++		len = dlen;
++	os_memcpy(buf, wpabuf_head(conn->pull_buf)+conn->pull_buf_offset, len);
++
++	if (len == dlen) {
++		tls_pull_buf_reset(conn);
++		/*wpa_printf(MSG_DEBUG, "%s - emptied pull_buf", __func__);*/
++	}
++	else {
++		conn->pull_buf_offset += len;
++		/*wpa_printf(MSG_DEBUG, "%s - %zu bytes remaining in pull_buf",
++			   __func__, dlen - len);*/
++	}
++	return (int)len;
++}
++
++
++static int tls_push_func(void *ptr, const unsigned char *buf, size_t len)
++{
++	struct tls_connection *conn = (struct tls_connection *) ptr;
++	return tls_wpabuf_resize_put_data(&conn->push_buf, buf, len)
++	  ? (int)len
++	  : MBEDTLS_ERR_SSL_ALLOC_FAILED;
++}
++
++
++static int
++tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags);
++
++
++static int tls_mbedtls_ssl_setup(struct tls_connection *conn)
++{
++  #if 0
++	/* mbedtls_ssl_setup() must be called only once */
++	/* If this func might be called multiple times (e.g. via set_params),
++	 * then we should set a flag in conn that ssl was initialized */
++	if (conn->ssl_is_init) {
++		mbedtls_ssl_free(&conn->ssl);
++		mbedtls_ssl_init(&conn->ssl);
++	}
++  #endif
++
++	int ret = mbedtls_ssl_setup(&conn->ssl, &conn->tls_conf->conf);
++	if (ret != 0) {
++		elog(ret, "mbedtls_ssl_setup");
++		return -1;
++	}
++
++	mbedtls_ssl_set_bio(&conn->ssl, conn, tls_push_func, tls_pull_func, NULL);
++  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++	mbedtls_ssl_set_export_keys_cb(
++	    &conn->ssl, tls_connection_export_keys_cb, conn);
++  #elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++	mbedtls_ssl_conf_export_keys_ext_cb(
++	    &conn->tls_conf->conf, tls_connection_export_keys_cb, conn);
++  #endif
++	if (conn->verify_peer)
++		mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
++
++	return 0;
++}
++
++
++static int tls_mbedtls_data_is_pem(const u8 *data)
++{
++    return (NULL != os_strstr((char *)data, "-----"));
++}
++
++
++static void tls_mbedtls_set_allowed_tls_vers(struct tls_conf *tls_conf,
++                                             mbedtls_ssl_config *conf)
++{
++  #if !defined(MBEDTLS_SSL_PROTO_TLS1_3)
++	tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_3;
++  #endif
++
++	/* unconditionally require TLSv1.2+ for TLS_CONN_SUITEB */
++	if (tls_conf->flags & TLS_CONN_SUITEB) {
++		tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_0;
++		tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_1;
++	}
++
++	const unsigned int flags = tls_conf->flags;
++
++	/* attempt to map flags to min and max TLS protocol version */
++
++	int min = (flags & TLS_CONN_DISABLE_TLSv1_0)
++		? (flags & TLS_CONN_DISABLE_TLSv1_1)
++		? (flags & TLS_CONN_DISABLE_TLSv1_2)
++		? (flags & TLS_CONN_DISABLE_TLSv1_3)
++		? 4
++		: 3
++		: 2
++		: 1
++		: 0;
++
++	int max = (flags & TLS_CONN_DISABLE_TLSv1_3)
++		? (flags & TLS_CONN_DISABLE_TLSv1_2)
++		? (flags & TLS_CONN_DISABLE_TLSv1_1)
++		? (flags & TLS_CONN_DISABLE_TLSv1_0)
++		? -1
++		: 0
++		: 1
++		: 2
++		: 3;
++
++	if ((flags & TLS_CONN_ENABLE_TLSv1_2) && min > 2) min = 2;
++	if ((flags & TLS_CONN_ENABLE_TLSv1_1) && min > 1) min = 1;
++	if ((flags & TLS_CONN_ENABLE_TLSv1_0) && min > 0) min = 0;
++	if (max < min) {
++		emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
++		return;
++	}
++  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++	/* mbed TLS 3.0.0 removes support for protocols < TLSv1.2 */
++	if (min < 2 || max < 2) {
++		emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
++		if (min < 2) min = 2;
++		if (max < 2) max = 2;
++	}
++  #endif
++
++  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
++	/* MBEDTLS_SSL_VERSION_TLS1_2 = 0x0303 *//*!< (D)TLS 1.2 */
++	/* MBEDTLS_SSL_VERSION_TLS1_3 = 0x0304 *//*!< (D)TLS 1.3 */
++	min = (min == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
++	max = (max == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
++	mbedtls_ssl_conf_min_tls_version(conf, min);
++	mbedtls_ssl_conf_max_tls_version(conf, max);
++  #else
++   #ifndef MBEDTLS_SSL_MINOR_VERSION_4
++	if (min == 3) min = 2;
++	if (max == 3) max = 2;
++   #endif
++	/* MBEDTLS_SSL_MINOR_VERSION_0  0 *//*!< SSL v3.0 */
++	/* MBEDTLS_SSL_MINOR_VERSION_1  1 *//*!< TLS v1.0 */
++	/* MBEDTLS_SSL_MINOR_VERSION_2  2 *//*!< TLS v1.1 */
++	/* MBEDTLS_SSL_MINOR_VERSION_3  3 *//*!< TLS v1.2 */
++	/* MBEDTLS_SSL_MINOR_VERSION_4  4 *//*!< TLS v1.3 */
++	mbedtls_ssl_conf_min_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, min+1);
++	mbedtls_ssl_conf_max_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, max+1);
++  #endif
++}
++
++
++__attribute_noinline__
++static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n);
++
++
++static int
++tls_mbedtls_set_dhparams(struct tls_conf *tls_conf, const char *dh_file)
++{
++    size_t len;
++    u8 *data;
++    if (tls_mbedtls_readfile(dh_file, &data, &len))
++        return 0;
++
++    /* parse only if DH parameters if in PEM format */
++    if (tls_mbedtls_data_is_pem(data)
++        && NULL == os_strstr((char *)data, "-----BEGIN DH PARAMETERS-----")) {
++        if (os_strstr((char *)data, "-----BEGIN DSA PARAMETERS-----"))
++            wpa_printf(MSG_WARNING, "DSA parameters not handled (%s)", dh_file);
++        else
++            wpa_printf(MSG_WARNING, "unexpected DH param content (%s)",dh_file);
++        forced_memzero(data, len);
++        os_free(data);
++        return 0;
++    }
++
++    /* mbedtls_dhm_parse_dhm() expects "-----BEGIN DH PARAMETERS-----" if PEM */
++    mbedtls_dhm_context dhm;
++    mbedtls_dhm_init(&dhm);
++    int rc = mbedtls_dhm_parse_dhm(&dhm, data, len);
++    if (0 == rc)
++        rc = mbedtls_ssl_conf_dh_param_ctx(&tls_conf->conf, &dhm);
++    if (0 != rc)
++        elog(rc, dh_file);
++    mbedtls_dhm_free(&dhm);
++
++    forced_memzero(data, len);
++    os_free(data);
++    return (0 == rc);
++}
++
++
++/* reference: lighttpd src/mod_mbedtls.c:mod_mbedtls_ssl_append_curve()
++ * (same author: gstrauss@gluelogic.com; same license: BSD-3-Clause) */
++#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
++static int
++tls_mbedtls_append_curve (mbedtls_ecp_group_id *ids, int nids, int idsz, const mbedtls_ecp_group_id id)
++{
++    if (1 >= idsz - (nids + 1)) {
++        emsg(MSG_ERROR, "error: too many curves during list expand");
++        return -1;
++    }
++    ids[++nids] = id;
++    return nids;
++}
++
++
++static int
++tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
++{
++    mbedtls_ecp_group_id ids[512];
++    int nids = -1;
++    const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
++    const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
++
++    for (const char *e = curvelist-1; e; ) {
++        const char * const n = e+1;
++        e = os_strchr(n, ':');
++        size_t len = e ? (size_t)(e - n) : os_strlen(n);
++        mbedtls_ecp_group_id grp_id = MBEDTLS_ECP_DP_NONE;
++        switch (len) {
++          case 5:
++            if (0 == os_memcmp("P-521", n, 5))
++                grp_id = MBEDTLS_ECP_DP_SECP521R1;
++            else if (0 == os_memcmp("P-384", n, 5))
++                grp_id = MBEDTLS_ECP_DP_SECP384R1;
++            else if (0 == os_memcmp("P-256", n, 5))
++                grp_id = MBEDTLS_ECP_DP_SECP256R1;
++            break;
++          case 6:
++            if (0 == os_memcmp("BP-521", n, 6))
++                grp_id = MBEDTLS_ECP_DP_BP512R1;
++            else if (0 == os_memcmp("BP-384", n, 6))
++                grp_id = MBEDTLS_ECP_DP_BP384R1;
++            else if (0 == os_memcmp("BP-256", n, 6))
++                grp_id = MBEDTLS_ECP_DP_BP256R1;
++            break;
++          default:
++            break;
++        }
++        if (grp_id != MBEDTLS_ECP_DP_NONE) {
++            nids = tls_mbedtls_append_curve(ids, nids, idsz, grp_id);
++            if (-1 == nids) return 0;
++            continue;
++        }
++        /* similar to mbedtls_ecp_curve_info_from_name() */
++        const mbedtls_ecp_curve_info *info;
++        for (info = curve_info; info->grp_id != MBEDTLS_ECP_DP_NONE; ++info) {
++            if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
++                break;
++        }
++        if (info->grp_id == MBEDTLS_ECP_DP_NONE) {
++            wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
++            return 0;
++        }
++
++        nids = tls_mbedtls_append_curve(ids, nids, idsz, info->grp_id);
++        if (-1 == nids) return 0;
++    }
++
++    /* mod_openssl configures "prime256v1" if curve list not specified,
++     * but mbedtls provides a list of supported curves if not explicitly set */
++    if (-1 == nids) return 1; /* empty list; no-op */
++
++    ids[++nids] = MBEDTLS_ECP_DP_NONE; /* terminate list */
++    ++nids;
++
++    /* curves list must be persistent for lifetime of mbedtls_ssl_config */
++    tls_conf->curves = os_malloc(nids * sizeof(mbedtls_ecp_group_id));
++    if (tls_conf->curves == NULL)
++        return 0;
++    os_memcpy(tls_conf->curves, ids, nids * sizeof(mbedtls_ecp_group_id));
++
++    mbedtls_ssl_conf_curves(&tls_conf->conf, tls_conf->curves);
++    return 1;
++}
++#else
++static int
++tls_mbedtls_append_curve (uint16_t *ids, int nids, int idsz, const uint16_t id)
++{
++    if (1 >= idsz - (nids + 1)) {
++        emsg(MSG_ERROR, "error: too many curves during list expand");
++        return -1;
++    }
++    ids[++nids] = id;
++    return nids;
++}
++
++
++static int
++tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
++{
++    /* TLS Supported Groups (renamed from "EC Named Curve Registry")
++     * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
++     */
++    uint16_t ids[512];
++    int nids = -1;
++    const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
++    const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
++
++    for (const char *e = curvelist-1; e; ) {
++        const char * const n = e+1;
++        e = os_strchr(n, ':');
++        size_t len = e ? (size_t)(e - n) : os_strlen(n);
++        uint16_t tls_id = 0;
++        switch (len) {
++          case 5:
++            if (0 == os_memcmp("P-521", n, 5))
++                tls_id = 25; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP521R1 */
++            else if (0 == os_memcmp("P-384", n, 5))
++                tls_id = 24; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP384R1 */
++            else if (0 == os_memcmp("P-256", n, 5))
++                tls_id = 23; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP256R1 */
++            break;
++          case 6:
++            if (0 == os_memcmp("BP-521", n, 6))
++                tls_id = 28; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP512R1 */
++            else if (0 == os_memcmp("BP-384", n, 6))
++                tls_id = 27; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP384R1 */
++            else if (0 == os_memcmp("BP-256", n, 6))
++                tls_id = 26; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP256R1 */
++            break;
++          default:
++            break;
++        }
++        if (tls_id != 0) {
++            nids = tls_mbedtls_append_curve(ids, nids, idsz, tls_id);
++            if (-1 == nids) return 0;
++            continue;
++        }
++        /* similar to mbedtls_ecp_curve_info_from_name() */
++        const mbedtls_ecp_curve_info *info;
++        for (info = curve_info; info->tls_id != 0; ++info) {
++            if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
++                break;
++        }
++        if (info->tls_id == 0) {
++            wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
++            return 0;
++        }
++
++        nids = tls_mbedtls_append_curve(ids, nids, idsz, info->tls_id);
++        if (-1 == nids) return 0;
++    }
++
++    /* mod_openssl configures "prime256v1" if curve list not specified,
++     * but mbedtls provides a list of supported curves if not explicitly set */
++    if (-1 == nids) return 1; /* empty list; no-op */
++
++    ids[++nids] = 0; /* terminate list */
++    ++nids;
++
++    /* curves list must be persistent for lifetime of mbedtls_ssl_config */
++    tls_conf->curves = os_malloc(nids * sizeof(uint16_t));
++    if (tls_conf->curves == NULL)
++        return 0;
++    os_memcpy(tls_conf->curves, ids, nids * sizeof(uint16_t));
++
++    mbedtls_ssl_conf_groups(&tls_conf->conf, tls_conf->curves);
++    return 1;
++}
++#endif /* MBEDTLS_VERSION_NUMBER >= 0x03010000 */ /* mbedtls 3.1.0 */
++
++
++/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
++static const int suite_AES_256_ephemeral[] = {
++    /* All AES-256 ephemeral suites */
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8
++};
++
++/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
++static const int suite_AES_128_ephemeral[] = {
++    /* All AES-128 ephemeral suites */
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8
++};
++
++/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
++/* HIGH cipher list (mapped from openssl list to mbedtls) */
++static const int suite_HIGH[] = {
++    MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
++    MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384,
++    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256,
++    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384,
++    MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
++    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
++    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
++    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256,
++    MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256,
++    MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256,
++    MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
++    MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM,
++    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
++    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
++    MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM_8,
++    MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM,
++    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
++    MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
++    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM_8,
++    MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256,
++    MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_RSA_WITH_AES_256_CCM,
++    MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256,
++    MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_RSA_WITH_AES_256_CCM_8,
++    MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256,
++    MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
++    MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384,
++    MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_RSA_WITH_AES_128_CCM,
++    MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_RSA_WITH_AES_128_CCM_8,
++    MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256,
++    MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
++    MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256,
++    MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256,
++    MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
++    MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384,
++    MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384,
++    MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256,
++    MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256,
++    MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256,
++    MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384,
++    MBEDTLS_TLS_PSK_WITH_AES_256_CCM,
++    MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384,
++    MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA,
++    MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384,
++    MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8,
++    MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384,
++    MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256,
++    MBEDTLS_TLS_PSK_WITH_AES_128_CCM,
++    MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256,
++    MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA,
++    MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256,
++    MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8,
++    MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256
++};
++
++
++__attribute_noinline__
++static int
++tls_mbedtls_append_ciphersuite (int *ids, int nids, int idsz, const int *x, int xsz)
++{
++    if (xsz >= idsz - (nids + 1)) {
++        emsg(MSG_ERROR, "error: too many ciphers during list expand");
++        return -1;
++    }
++
++    for (int i = 0; i < xsz; ++i)
++        ids[++nids] = x[i];
++
++    return nids;
++}
++
++
++static int
++tls_mbedtls_translate_ciphername(int id, char *buf, size_t buflen)
++{
++    const mbedtls_ssl_ciphersuite_t *info =
++      mbedtls_ssl_ciphersuite_from_id(id);
++    if (info == NULL)
++        return 0;
++    const char *name = mbedtls_ssl_ciphersuite_get_name(info);
++    const size_t len = os_strlen(name);
++    if (len == 7 && 0 == os_memcmp(name, "unknown", 7))
++        return 0;
++    if (len >= buflen)
++        return 0;
++    os_strlcpy(buf, name, buflen);
++
++    /* attempt to translate mbedtls string to openssl string
++     * (some heuristics; incomplete) */
++    size_t i = 0, j = 0;
++    if (buf[0] == 'T') {
++        if (os_strncmp(buf, "TLS1-3-", 7) == 0) {
++            buf[3] = '-';
++            j = 4; /* remove "1-3" from "TLS1-3-" prefix */
++            i = 7;
++        }
++        else if (os_strncmp(buf, "TLS-", 4) == 0)
++            i = 4; /* remove "TLS-" prefix */
++    }
++    for (; buf[i]; ++i) {
++        if (buf[i] == '-') {
++            if (i >= 3) {
++                if (0 == os_memcmp(buf+i-3, "AES", 3))
++                    continue; /* "AES-" -> "AES" */
++            }
++            if (i >= 4) {
++                if (0 == os_memcmp(buf+i-4, "WITH", 4)) {
++                    j -= 4;   /* remove "WITH-" */
++                    continue;
++                }
++            }
++        }
++        buf[j++] = buf[i];
++    }
++    buf[j] = '\0';
++
++    return j;
++}
++
++
++__attribute_noinline__
++static int
++tls_mbedtls_set_ciphersuites(struct tls_conf *tls_conf, int *ids, int nids)
++{
++    /* ciphersuites list must be persistent for lifetime of mbedtls_ssl_config*/
++    os_free(tls_conf->ciphersuites);
++    tls_conf->ciphersuites = os_malloc(nids * sizeof(int));
++    if (tls_conf->ciphersuites == NULL)
++        return 0;
++    os_memcpy(tls_conf->ciphersuites, ids, nids * sizeof(int));
++    mbedtls_ssl_conf_ciphersuites(&tls_conf->conf, tls_conf->ciphersuites);
++    return 1;
++}
++
++
++static int
++tls_mbedtls_set_ciphers(struct tls_conf *tls_conf, const char *ciphers)
++{
++    char buf[64];
++    int ids[512];
++    int nids = -1;
++    const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
++    const char *next;
++    size_t blen, clen;
++    do {
++        next = os_strchr(ciphers, ':');
++        clen = next ? (size_t)(next - ciphers) : os_strlen(ciphers);
++        if (!clen)
++            continue;
++
++        /* special-case a select set of openssl group names for hwsim tests */
++	/* (review; remove excess code if tests are not run for non-OpenSSL?) */
++        if (clen == 9 && os_memcmp(ciphers, "SUITEB192", 9) == 0) {
++            static int ssl_preset_suiteb192_ciphersuites[] = {
++                MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
++                0
++            };
++            return tls_mbedtls_set_ciphersuites(tls_conf,
++                                                ssl_preset_suiteb192_ciphersuites,
++                                                2);
++        }
++        if (clen == 9 && os_memcmp(ciphers, "SUITEB128", 9) == 0) {
++            static int ssl_preset_suiteb128_ciphersuites[] = {
++                MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
++                0
++            };
++            return tls_mbedtls_set_ciphersuites(tls_conf,
++                                                ssl_preset_suiteb128_ciphersuites,
++                                                2);
++        }
++        if (clen == 7 && os_memcmp(ciphers, "DEFAULT", 7) == 0)
++            continue;
++        if (clen == 6 && os_memcmp(ciphers, "AES128", 6) == 0) {
++            nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
++                     suite_AES_128_ephemeral,
++                     (int)ARRAY_SIZE(suite_AES_128_ephemeral));
++            if (nids == -1)
++                return 0;
++            continue;
++        }
++        if (clen == 6 && os_memcmp(ciphers, "AES256", 6) == 0) {
++            nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
++                     suite_AES_256_ephemeral,
++                     (int)ARRAY_SIZE(suite_AES_256_ephemeral));
++            if (nids == -1)
++                return 0;
++            continue;
++        }
++        if (clen == 4 && os_memcmp(ciphers, "HIGH", 4) == 0) {
++            nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz, suite_HIGH,
++                                                  (int)ARRAY_SIZE(suite_HIGH));
++            if (nids == -1)
++                return 0;
++            continue;
++        }
++        /* ignore anonymous cipher group names (?not supported by mbedtls?) */
++        if (clen == 4 && os_memcmp(ciphers, "!ADH", 4) == 0)
++            continue;
++        if (clen == 6 && os_memcmp(ciphers, "-aECDH", 6) == 0)
++            continue;
++        if (clen == 7 && os_memcmp(ciphers, "-aECDSA", 7) == 0)
++            continue;
++
++        /* attempt to match mbedtls cipher names
++         * nb: does not support openssl group names or list manipulation syntax
++         *   (alt: could copy almost 1200 lines (!!!) of lighttpd mod_mbedtls.c
++         *    mod_mbedtls_ssl_conf_ciphersuites() to translate strings)
++         * note: not efficient to rewrite list for each ciphers entry,
++         *       but this code is expected to run only at startup
++         */
++        const int *list = mbedtls_ssl_list_ciphersuites();
++        for (; *list; ++list) {
++            blen = tls_mbedtls_translate_ciphername(*list,buf,sizeof(buf));
++            if (!blen)
++                continue;
++
++            /* matching heuristics additional to translate_ciphername above */
++            if (blen == clen+4) {
++                char *cbc = os_strstr(buf, "CBC-");
++                if (cbc) {
++                    os_memmove(cbc, cbc+4, blen-(cbc+4-buf)+1); /*(w/ '\0')*/
++                    blen -= 4;
++                }
++            }
++            if (blen >= clen && os_memcmp(ciphers, buf, clen) == 0
++                && (blen == clen
++                    || (blen == clen+7 && os_memcmp(buf+clen, "-SHA256", 7)))) {
++                if (1 >= idsz - (nids + 1)) {
++                    emsg(MSG_ERROR,
++                         "error: too many ciphers during list expand");
++                    return 0;
++                }
++                ids[++nids] = *list;
++                break;
++            }
++        }
++        if (*list == 0) {
++            wpa_printf(MSG_ERROR,
++                       "MTLS: unrecognized cipher: %.*s", (int)clen, ciphers);
++            return 0;
++        }
++    } while ((ciphers = next ? next+1 : NULL));
++
++    if (-1 == nids) return 1; /* empty list; no-op */
++
++    ids[++nids] = 0; /* terminate list */
++    ++nids;
++
++    return tls_mbedtls_set_ciphersuites(tls_conf, ids, nids);
++}
++
++
++__attribute_noinline__
++static int tls_mbedtls_set_item(char **config_item, const char *item)
++{
++	os_free(*config_item);
++	*config_item = NULL;
++	return item ? (*config_item = os_strdup(item)) != NULL : 1;
++}
++
++
++static int tls_connection_set_subject_match(struct tls_conf *tls_conf,
++                                            const struct tls_connection_params *params)
++{
++	int rc = 1;
++	rc &= tls_mbedtls_set_item(&tls_conf->subject_match,
++	                              params->subject_match);
++	rc &= tls_mbedtls_set_item(&tls_conf->altsubject_match,
++	                              params->altsubject_match);
++	rc &= tls_mbedtls_set_item(&tls_conf->suffix_match,
++	                              params->suffix_match);
++	rc &= tls_mbedtls_set_item(&tls_conf->domain_match,
++	                              params->domain_match);
++	rc &= tls_mbedtls_set_item(&tls_conf->check_cert_subject,
++	                              params->check_cert_subject);
++	return rc;
++}
++
++
++/* duplicated in crypto_mbedtls.c:crypto_mbedtls_readfile()*/
++__attribute_noinline__
++static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n)
++{
++  #if 0 /* #ifdef MBEDTLS_FS_IO */
++	/*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/
++	if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) {
++		wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path);
++		return -1;
++	}
++  #else
++	/*(use os_readfile() so that we can use os_free()
++	 *(if we use mbedtls_pk_load_file() above, macros prevent calling free()
++	 * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free()
++	 * on buf aborts in tests if buf not allocated via os_malloc())*/
++	*buf = (u8 *)os_readfile(path, n);
++	if (!*buf) {
++		wpa_printf(MSG_ERROR, "error: os_readfile %s", path);
++		return -1;
++	}
++	u8 *buf0 = os_realloc(*buf, *n+1);
++	if (!buf0) {
++		bin_clear_free(*buf, *n);
++		*buf = NULL;
++		return -1;
++	}
++	buf0[(*n)++] = '\0';
++	*buf = buf0;
++  #endif
++	return 0;
++}
++
++
++static int tls_mbedtls_set_crl(struct tls_conf *tls_conf, const u8 *data, size_t len)
++{
++	/* do not use mbedtls_x509_crl_parse() on PEM unless it contains CRL */
++	if (len && data[len-1] == '\0'
++	    && NULL == os_strstr((const char *)data,"-----BEGIN X509 CRL-----")
++	    && tls_mbedtls_data_is_pem(data))
++		return 0;
++
++	mbedtls_x509_crl crl;
++	mbedtls_x509_crl_init(&crl);
++	int rc = mbedtls_x509_crl_parse(&crl, data, len);
++	if (rc < 0) {
++		mbedtls_x509_crl_free(&crl);
++		return rc == MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT ? 0 : rc;
++	}
++
++	mbedtls_x509_crl *crl_new = os_malloc(sizeof(crl));
++	if (crl_new == NULL) {
++		mbedtls_x509_crl_free(&crl);
++		return MBEDTLS_ERR_X509_ALLOC_FAILED;
++	}
++	os_memcpy(crl_new, &crl, sizeof(crl));
++
++	mbedtls_x509_crl *crl_old = tls_conf->crl;
++	tls_conf->crl = crl_new;
++	if (crl_old) {
++		mbedtls_x509_crl_free(crl_old);
++		os_free(crl_old);
++	}
++	return 0;
++}
++
++
++static int tls_mbedtls_set_ca(struct tls_conf *tls_conf, u8 *data, size_t len)
++{
++	/* load crt struct onto stack and then copy into tls_conf in
++	 * order to preserve existing tls_conf value if error occurs
++	 *
++	 * hostapd is not threaded, or else should allocate memory and swap in
++	 * pointer reduce race condition.  (If threaded, would also need to
++	 * keep reference count of use to avoid freeing while still in use.) */
++
++	mbedtls_x509_crt crt;
++	mbedtls_x509_crt_init(&crt);
++	int rc = mbedtls_x509_crt_parse(&crt, data, len);
++	if (rc < 0) {
++		mbedtls_x509_crt_free(&crt);
++		return rc;
++	}
++
++	mbedtls_x509_crt_free(&tls_conf->ca_cert);
++	os_memcpy(&tls_conf->ca_cert, &crt, sizeof(crt));
++	return 0;
++}
++
++
++static int tls_mbedtls_set_ca_and_crl(struct tls_conf *tls_conf, const char *ca_cert_file)
++{
++	size_t len;
++	u8 *data;
++	if (tls_mbedtls_readfile(ca_cert_file, &data, &len))
++		return -1;
++
++	int rc;
++	if (0 == (rc = tls_mbedtls_set_ca(tls_conf, data, len))
++	    && (!tls_mbedtls_data_is_pem(data) /*skip parse for CRL if not PEM*/
++	        || 0 == (rc = tls_mbedtls_set_crl(tls_conf, data, len)))) {
++		mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
++		                          &tls_conf->ca_cert,
++		                          tls_conf->crl);
++	}
++	else {
++		elog(rc, __func__);
++		emsg(MSG_ERROR, ca_cert_file);
++	}
++
++	forced_memzero(data, len);
++	os_free(data);
++	return rc;
++}
++
++
++static void tls_mbedtls_refresh_crl(void)
++{
++	/* check for CRL refresh
++	 * continue even if error occurs; continue with previous cert, CRL */
++	unsigned int crl_reload_interval = tls_ctx_global.crl_reload_interval;
++	const char *ca_cert_file = tls_ctx_global.ca_cert_file;
++	if (!crl_reload_interval || !ca_cert_file)
++		return;
++
++	struct os_reltime *previous = &tls_ctx_global.crl_reload_previous;
++	struct os_reltime now;
++	if (os_get_reltime(&now) != 0
++	    || !os_reltime_expired(&now, previous, crl_reload_interval))
++		return;
++
++	/* Note: modifying global state is not thread-safe
++	 *       if in use by existing connections
++	 *
++	 * src/utils/os.h does not provide a portable stat()
++	 * or else it would be a good idea to check mtime and size,
++	 * and avoid reloading if file has not changed */
++
++	if (tls_mbedtls_set_ca_and_crl(tls_ctx_global.tls_conf, ca_cert_file) == 0)
++		*previous = now;
++}
++
++
++static int tls_mbedtls_set_ca_cert(struct tls_conf *tls_conf,
++				   const struct tls_connection_params *params)
++{
++	if (params->ca_cert) {
++		if (os_strncmp(params->ca_cert, "probe://", 8) == 0) {
++			tls_conf->ca_cert_probe = 1;
++			tls_conf->has_ca_cert = 1;
++			return 0;
++		}
++
++		if (os_strncmp(params->ca_cert, "hash://", 7) == 0) {
++			const char *pos = params->ca_cert + 7;
++			if (os_strncmp(pos, "server/sha256/", 14) != 0) {
++				emsg(MSG_ERROR, "unsupported ca_cert hash value");
++				return -1;
++			}
++			pos += 14;
++			if (os_strlen(pos) != SHA256_DIGEST_LENGTH*2) {
++				emsg(MSG_ERROR, "unexpected ca_cert hash length");
++				return -1;
++			}
++			if (hexstr2bin(pos, tls_conf->ca_cert_hash,
++			               SHA256_DIGEST_LENGTH) < 0) {
++				emsg(MSG_ERROR, "invalid ca_cert hash value");
++				return -1;
++			}
++			emsg(MSG_DEBUG, "checking only server certificate match");
++			tls_conf->verify_depth0_only = 1;
++			tls_conf->has_ca_cert = 1;
++			return 0;
++		}
++
++		if (tls_mbedtls_set_ca_and_crl(tls_conf, params->ca_cert) != 0)
++			return -1;
++	}
++	if (params->ca_cert_blob) {
++		size_t len = params->ca_cert_blob_len;
++		int is_pem = tls_mbedtls_data_is_pem(params->ca_cert_blob);
++		if (len && params->ca_cert_blob[len-1] != '\0' && is_pem)
++			++len; /*(include '\0' in len for PEM)*/
++		int ret = mbedtls_x509_crt_parse(&tls_conf->ca_cert,
++		                                 params->ca_cert_blob, len);
++		if (ret != 0) {
++			elog(ret, "mbedtls_x509_crt_parse");
++			return -1;
++		}
++		if (is_pem) { /*(ca_cert_blob in DER format contains ca cert only)*/
++			ret = tls_mbedtls_set_crl(tls_conf, params->ca_cert_blob, len);
++			if (ret != 0) {
++				elog(ret, "mbedtls_x509_crl_parse");
++				return -1;
++			}
++		}
++	}
++
++	if (mbedtls_x509_time_is_future(&tls_conf->ca_cert.valid_from)
++	    || mbedtls_x509_time_is_past(&tls_conf->ca_cert.valid_to)) {
++		emsg(MSG_WARNING, "ca_cert expired or not yet valid");
++		if (params->ca_cert)
++			emsg(MSG_WARNING, params->ca_cert);
++	}
++
++	tls_conf->has_ca_cert = 1;
++	return 0;
++}
++
++
++static int tls_mbedtls_set_certs(struct tls_conf *tls_conf,
++				 const struct tls_connection_params *params)
++{
++	int ret;
++
++	if (params->ca_cert || params->ca_cert_blob) {
++		if (tls_mbedtls_set_ca_cert(tls_conf, params) != 0)
++			return -1;
++	}
++	else if (params->ca_path) {
++		emsg(MSG_INFO, "ca_path support not implemented");
++		return -1;
++	}
++
++	if (!tls_conf->has_ca_cert)
++		mbedtls_ssl_conf_authmode(&tls_conf->conf, MBEDTLS_SSL_VERIFY_NONE);
++	else {
++		/* Initial setting: REQUIRED for client, OPTIONAL for server
++		 *   (see also tls_connection_set_verify()) */
++		tls_conf->verify_peer = (tls_ctx_global.tls_conf == NULL);
++		int authmode = tls_conf->verify_peer
++		  ? MBEDTLS_SSL_VERIFY_REQUIRED
++		  : MBEDTLS_SSL_VERIFY_OPTIONAL;
++		mbedtls_ssl_conf_authmode(&tls_conf->conf, authmode);
++		mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
++		                          &tls_conf->ca_cert,
++		                          tls_conf->crl);
++
++		if (!tls_connection_set_subject_match(tls_conf, params))
++			return -1;
++	}
++
++	if (params->client_cert2) /*(yes, server_cert2 in msg below)*/
++		emsg(MSG_INFO, "server_cert2 support not implemented");
++
++	if (params->client_cert) {
++		size_t len;
++		u8 *data;
++		if (tls_mbedtls_readfile(params->client_cert, &data, &len))
++			return -1;
++		ret = mbedtls_x509_crt_parse(&tls_conf->client_cert, data, len);
++		forced_memzero(data, len);
++		os_free(data);
++	}
++	if (params->client_cert_blob) {
++		size_t len = params->client_cert_blob_len;
++		if (len && params->client_cert_blob[len-1] != '\0'
++		    && tls_mbedtls_data_is_pem(params->client_cert_blob))
++			++len; /*(include '\0' in len for PEM)*/
++		ret = mbedtls_x509_crt_parse(&tls_conf->client_cert,
++		                             params->client_cert_blob, len);
++	}
++	if (params->client_cert || params->client_cert_blob) {
++		if (ret < 0) {
++			elog(ret, "mbedtls_x509_crt_parse");
++			if (params->client_cert)
++				emsg(MSG_ERROR, params->client_cert);
++			return -1;
++		}
++		if (mbedtls_x509_time_is_future(&tls_conf->client_cert.valid_from)
++		    || mbedtls_x509_time_is_past(&tls_conf->client_cert.valid_to)) {
++			emsg(MSG_WARNING, "cert expired or not yet valid");
++			if (params->client_cert)
++				emsg(MSG_WARNING, params->client_cert);
++		}
++		tls_conf->has_client_cert = 1;
++	}
++
++	if (params->private_key || params->private_key_blob) {
++		size_t len = params->private_key_blob_len;
++		u8 *data;
++		*(const u8 **)&data = params->private_key_blob;
++		if (len && data[len-1] != '\0' && tls_mbedtls_data_is_pem(data))
++			++len; /*(include '\0' in len for PEM)*/
++		if (params->private_key
++		    && tls_mbedtls_readfile(params->private_key, &data, &len)) {
++			return -1;
++		}
++		const char *pwd = params->private_key_passwd;
++	  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++		ret = mbedtls_pk_parse_key(&tls_conf->private_key,
++			data, len,
++			(const unsigned char *)pwd,
++			pwd ? os_strlen(pwd) : 0,
++			mbedtls_ctr_drbg_random,
++			tls_ctx_global.ctr_drbg);
++	  #else
++		ret = mbedtls_pk_parse_key(&tls_conf->private_key,
++			data, len,
++			(const unsigned char *)pwd,
++			pwd ? os_strlen(pwd) : 0);
++	  #endif
++		if (params->private_key) {
++			forced_memzero(data, len);
++			os_free(data);
++		}
++		if (ret < 0) {
++			elog(ret, "mbedtls_pk_parse_key");
++			return -1;
++		}
++		tls_conf->has_private_key = 1;
++	}
++
++	if (tls_conf->has_client_cert && tls_conf->has_private_key) {
++		ret = mbedtls_ssl_conf_own_cert(
++		    &tls_conf->conf, &tls_conf->client_cert, &tls_conf->private_key);
++		if (ret < 0) {
++			elog(ret, "mbedtls_ssl_conf_own_cert");
++			return -1;
++		}
++	}
++
++	return 0;
++}
++
++
++/* mbedtls_x509_crt_profile_suiteb plus rsa_min_bitlen 2048 */
++/* (reference: see also mbedtls_x509_crt_profile_next) */
++/* ??? should permit SHA-512, too, and additional curves ??? */
++static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb128 =
++{
++    /* Only SHA-256 and 384 */
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) |
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
++    /* Only ECDSA */
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
++#if defined(MBEDTLS_ECP_C)
++    /* Only NIST P-256 and P-384 */
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP256R1 ) |
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
++#else
++    0,
++#endif
++    2048,
++};
++
++
++/* stricter than mbedtls_x509_crt_profile_suiteb */
++/* (reference: see also mbedtls_x509_crt_profile_next) */
++/* ??? should permit SHA-512, too, and additional curves ??? */
++static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192 =
++{
++    /* Only SHA-384 */
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
++    /* Only ECDSA */
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
++#if defined(MBEDTLS_ECP_C)
++    /* Only NIST P-384 */
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
++#else
++    0,
++#endif
++    3072,
++};
++
++
++/* stricter than mbedtls_x509_crt_profile_suiteb except allow any PK alg */
++/* (reference: see also mbedtls_x509_crt_profile_next) */
++/* ??? should permit SHA-512, too, and additional curves ??? */
++static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192_anypk =
++{
++    /* Only SHA-384 */
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
++    0xFFFFFFF, /* Any PK alg    */
++#if defined(MBEDTLS_ECP_C)
++    /* Only NIST P-384 */
++    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
++#else
++    0,
++#endif
++    3072,
++};
++
++
++static int tls_mbedtls_set_params(struct tls_conf *tls_conf,
++				  const struct tls_connection_params *params)
++{
++	tls_conf->flags = params->flags;
++
++	if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
++		emsg(MSG_INFO, "ocsp=3 not supported");
++		return -1;
++	}
++
++	if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP) {
++		emsg(MSG_INFO, "ocsp not supported");
++		return -1;
++	}
++
++	int suiteb128 = 0;
++	int suiteb192 = 0;
++	if (params->openssl_ciphers) {
++		if (os_strcmp(params->openssl_ciphers, "SUITEB192") == 0) {
++			suiteb192 = 1;
++			tls_conf->flags |= TLS_CONN_SUITEB;
++		}
++		if (os_strcmp(params->openssl_ciphers, "SUITEB128") == 0) {
++			suiteb128 = 1;
++			tls_conf->flags |= TLS_CONN_SUITEB;
++		}
++	}
++
++	int ret = mbedtls_ssl_config_defaults(
++	    &tls_conf->conf, tls_ctx_global.tls_conf ? MBEDTLS_SSL_IS_SERVER
++	                                             : MBEDTLS_SSL_IS_CLIENT,
++	    MBEDTLS_SSL_TRANSPORT_STREAM,
++	    (tls_conf->flags & TLS_CONN_SUITEB) ? MBEDTLS_SSL_PRESET_SUITEB
++	                                        : MBEDTLS_SSL_PRESET_DEFAULT);
++	if (ret != 0) {
++		elog(ret, "mbedtls_ssl_config_defaults");
++		return -1;
++	}
++
++	if (suiteb128) {
++		mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
++		                              &tls_mbedtls_crt_profile_suiteb128);
++		mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 2048);
++	}
++	else if (suiteb192) {
++		mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
++		                              &tls_mbedtls_crt_profile_suiteb192);
++		mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
++	}
++	else if (tls_conf->flags & TLS_CONN_SUITEB) {
++		/* treat as suiteb192 while allowing any PK algorithm */
++		mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
++		                              &tls_mbedtls_crt_profile_suiteb192_anypk);
++		mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
++	}
++
++	tls_mbedtls_set_allowed_tls_vers(tls_conf, &tls_conf->conf);
++	ret = tls_mbedtls_set_certs(tls_conf, params);
++	if (ret != 0)
++		return -1;
++
++	if (params->dh_file
++	    && !tls_mbedtls_set_dhparams(tls_conf, params->dh_file)) {
++		return -1;
++	}
++
++	if (params->openssl_ecdh_curves
++	    && !tls_mbedtls_set_curves(tls_conf, params->openssl_ecdh_curves)) {
++		return -1;
++	}
++
++	if (params->openssl_ciphers) {
++		if (!tls_mbedtls_set_ciphers(tls_conf, params->openssl_ciphers))
++			return -1;
++	}
++	else if (tls_conf->flags & TLS_CONN_SUITEB) {
++		/* special-case a select set of ciphers for hwsim tests */
++		if (!tls_mbedtls_set_ciphers(tls_conf,
++		        (tls_conf->flags & TLS_CONN_SUITEB_NO_ECDH)
++		          ? "DHE-RSA-AES256-GCM-SHA384"
++		          : "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384"))
++			return -1;
++	}
++
++	return 0;
++}
++
++
++int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
++			      const struct tls_connection_params *params)
++{
++	if (conn == NULL || params == NULL)
++		return -1;
++
++	tls_conf_deinit(conn->tls_conf);
++	struct tls_conf *tls_conf = conn->tls_conf = tls_conf_init(tls_ctx);
++	if (tls_conf == NULL)
++		return -1;
++
++	if (tls_ctx_global.tls_conf) {
++		tls_conf->check_crl = tls_ctx_global.tls_conf->check_crl;
++		tls_conf->check_crl_strict = tls_ctx_global.tls_conf->check_crl_strict;
++		/*(tls_openssl.c inherits check_cert_subject from global conf)*/
++		if (tls_ctx_global.tls_conf->check_cert_subject) {
++			tls_conf->check_cert_subject =
++			  os_strdup(tls_ctx_global.tls_conf->check_cert_subject);
++			if (tls_conf->check_cert_subject == NULL)
++				return -1;
++		}
++	}
++
++	if (tls_mbedtls_set_params(tls_conf, params) != 0)
++		return -1;
++	conn->verify_peer = tls_conf->verify_peer;
++
++	return tls_mbedtls_ssl_setup(conn);
++}
++
++
++#ifdef TLS_MBEDTLS_SESSION_TICKETS
++
++static int tls_mbedtls_clienthello_session_ticket_prep (struct tls_connection *conn,
++                                                        const u8 *data, size_t len)
++{
++	if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
++		return -1;
++	if (conn->clienthello_session_ticket)
++		tls_connection_deinit_clienthello_session_ticket(conn);
++	if (len) {
++		conn->clienthello_session_ticket = mbedtls_calloc(1, len);
++		if (conn->clienthello_session_ticket == NULL)
++			return -1;
++		conn->clienthello_session_ticket_len = len;
++		os_memcpy(conn->clienthello_session_ticket, data, len);
++	}
++	return 0;
++}
++
++
++static void tls_mbedtls_clienthello_session_ticket_set (struct tls_connection *conn)
++{
++	mbedtls_ssl_session *sess = conn->ssl.MBEDTLS_PRIVATE(session_negotiate);
++	if (sess->MBEDTLS_PRIVATE(ticket)) {
++		mbedtls_platform_zeroize(sess->MBEDTLS_PRIVATE(ticket),
++		                         sess->MBEDTLS_PRIVATE(ticket_len));
++		mbedtls_free(sess->MBEDTLS_PRIVATE(ticket));
++	}
++	sess->MBEDTLS_PRIVATE(ticket) = conn->clienthello_session_ticket;
++	sess->MBEDTLS_PRIVATE(ticket_len) = conn->clienthello_session_ticket_len;
++	sess->MBEDTLS_PRIVATE(ticket_lifetime) = 86400;/* XXX: can hint be 0? */
++
++	conn->clienthello_session_ticket = NULL;
++	conn->clienthello_session_ticket_len = 0;
++}
++
++
++static int tls_mbedtls_ssl_ticket_write(void *p_ticket,
++                                        const mbedtls_ssl_session *session,
++                                        unsigned char *start,
++                                        const unsigned char *end,
++                                        size_t *tlen,
++                                        uint32_t *lifetime)
++{
++	struct tls_connection *conn = p_ticket;
++	if (conn && conn->session_ticket_cb) {
++		/* see tls_mbedtls_clienthello_session_ticket_prep() */
++		/* see tls_mbedtls_clienthello_session_ticket_set() */
++		return 0;
++	}
++
++	return mbedtls_ssl_ticket_write(&tls_ctx_global.ticket_ctx,
++	                                session, start, end, tlen, lifetime);
++}
++
++
++static int tls_mbedtls_ssl_ticket_parse(void *p_ticket,
++                                        mbedtls_ssl_session *session,
++                                        unsigned char *buf,
++                                        size_t len)
++{
++	/* XXX: TODO: not implemented in client;
++	 * mbedtls_ssl_conf_session_tickets_cb() callbacks only for TLS server*/
++
++	if (len == 0)
++		return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
++
++	struct tls_connection *conn = p_ticket;
++	if (conn && conn->session_ticket_cb) {
++		/* XXX: have random and secret been initialized yet?
++		 *      or must keys first be exported?
++		 *      EAP-FAST uses all args, EAP-TEAP only uses secret */
++		struct tls_random data;
++		if (tls_connection_get_random(NULL, conn, &data) != 0)
++			return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
++		int ret =
++		  conn->session_ticket_cb(conn->session_ticket_cb_ctx,
++		                          buf, len,
++		                          data.client_random,
++		                          data.server_random,
++		                          conn->expkey_secret);
++		if (ret == 1) {
++			conn->resumed = 1;
++			return 0;
++		}
++		emsg(MSG_ERROR, "EAP session ticket ext not implemented");
++		return MBEDTLS_ERR_SSL_INVALID_MAC;
++		/*(non-zero return used for mbedtls debug logging)*/
++	}
++
++	/* XXX: TODO always use tls_mbedtls_ssl_ticket_parse() for callback? */
++	int rc = mbedtls_ssl_ticket_parse(&tls_ctx_global.ticket_ctx,
++	                                  session, buf, len);
++	if (conn)
++		conn->resumed = (rc == 0);
++	return rc;
++}
++
++#endif /* TLS_MBEDTLS_SESSION_TICKETS */
++
++
++__attribute_cold__
++int tls_global_set_params(void *tls_ctx,
++			  const struct tls_connection_params *params)
++{
++	/* XXX: why might global_set_params be called more than once? */
++	if (tls_ctx_global.tls_conf)
++		tls_conf_deinit(tls_ctx_global.tls_conf);
++	tls_ctx_global.tls_conf = tls_conf_init(tls_ctx);
++	if (tls_ctx_global.tls_conf == NULL)
++		return -1;
++
++  #ifdef MBEDTLS_SSL_SESSION_TICKETS
++  #ifdef MBEDTLS_SSL_TICKET_C
++	if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET))
++	  #ifdef TLS_MBEDTLS_SESSION_TICKETS
++		mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
++		                                    tls_mbedtls_ssl_ticket_write,
++		                                    tls_mbedtls_ssl_ticket_parse,
++		                                    NULL);
++	  #else
++		mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
++		                                    mbedtls_ssl_ticket_write,
++		                                    mbedtls_ssl_ticket_parse,
++		                                    &tls_ctx_global.ticket_ctx);
++	  #endif
++  #endif
++  #endif
++
++	os_free(tls_ctx_global.ocsp_stapling_response);
++	tls_ctx_global.ocsp_stapling_response = NULL;
++	if (params->ocsp_stapling_response)
++		tls_ctx_global.ocsp_stapling_response =
++			os_strdup(params->ocsp_stapling_response);
++
++	os_free(tls_ctx_global.ca_cert_file);
++	tls_ctx_global.ca_cert_file = NULL;
++	if (params->ca_cert)
++		tls_ctx_global.ca_cert_file = os_strdup(params->ca_cert);
++	return tls_mbedtls_set_params(tls_ctx_global.tls_conf, params);
++}
++
++
++int tls_global_set_verify(void *tls_ctx, int check_crl, int strict)
++{
++	tls_ctx_global.tls_conf->check_crl = check_crl;
++	tls_ctx_global.tls_conf->check_crl_strict = strict; /*(time checks)*/
++	return 0;
++}
++
++
++int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
++			      int verify_peer, unsigned int flags,
++			      const u8 *session_ctx, size_t session_ctx_len)
++{
++	/*(EAP server-side calls this from eap_server_tls_ssl_init())*/
++	if (conn == NULL)
++		return -1;
++
++	conn->tls_conf->flags |= flags;/* TODO: reprocess flags, if necessary */
++
++	int authmode;
++	switch (verify_peer) {
++	case 2:  authmode = MBEDTLS_SSL_VERIFY_OPTIONAL; break;/*(eap_teap_init())*/
++	case 1:  authmode = MBEDTLS_SSL_VERIFY_REQUIRED; break;
++	default: authmode = MBEDTLS_SSL_VERIFY_NONE;     break;
++	}
++	mbedtls_ssl_set_hs_authmode(&conn->ssl, authmode);
++
++	if ((conn->verify_peer = (authmode != MBEDTLS_SSL_VERIFY_NONE)))
++		mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
++	else
++		mbedtls_ssl_set_verify(&conn->ssl, NULL, NULL);
++
++	return 0;
++}
++
++
++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++static void tls_connection_export_keys_cb(
++    void *p_expkey, mbedtls_ssl_key_export_type secret_type,
++    const unsigned char *secret, size_t secret_len,
++    const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
++    const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
++    mbedtls_tls_prf_types tls_prf_type)
++{
++	struct tls_connection *conn = p_expkey;
++	conn->tls_prf_type = tls_prf_type;
++	if (!tls_prf_type)
++		return;
++	if (secret_len > sizeof(conn->expkey_secret)) {
++		emsg(MSG_ERROR, "tls_connection_export_keys_cb secret too long");
++		conn->tls_prf_type = MBEDTLS_SSL_TLS_PRF_NONE; /* 0 */
++		return;
++	}
++	conn->expkey_secret_len = secret_len;
++	os_memcpy(conn->expkey_secret, secret, secret_len);
++	os_memcpy(conn->expkey_randbytes,
++	          client_random, MBEDTLS_EXPKEY_RAND_LEN);
++	os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
++	          server_random, MBEDTLS_EXPKEY_RAND_LEN);
++}
++#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++static int tls_connection_export_keys_cb(
++    void *p_expkey,
++    const unsigned char *ms,
++    const unsigned char *kb,
++    size_t maclen,
++    size_t keylen,
++    size_t ivlen,
++    const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
++    const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
++    mbedtls_tls_prf_types tls_prf_type )
++{
++	struct tls_connection *conn = p_expkey;
++	conn->tls_prf_type = tls_prf_type;
++	if (!tls_prf_type)
++		return -1; /*(return value ignored by mbedtls)*/
++	conn->expkey_keyblock_size = maclen + keylen + ivlen;
++	conn->expkey_secret_len = MBEDTLS_EXPKEY_FIXED_SECRET_LEN;
++	os_memcpy(conn->expkey_secret, ms, MBEDTLS_EXPKEY_FIXED_SECRET_LEN);
++	os_memcpy(conn->expkey_randbytes,
++	          client_random, MBEDTLS_EXPKEY_RAND_LEN);
++	os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
++	          server_random, MBEDTLS_EXPKEY_RAND_LEN);
++	return 0;
++}
++#endif
++
++
++int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
++			      struct tls_random *data)
++{
++	if (!conn || !conn->tls_prf_type)
++		return -1;
++	data->client_random = conn->expkey_randbytes;
++	data->client_random_len = MBEDTLS_EXPKEY_RAND_LEN;
++	data->server_random = conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN;
++	data->server_random_len = MBEDTLS_EXPKEY_RAND_LEN;
++	return 0;
++}
++
++
++int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
++			      const char *label, const u8 *context,
++			      size_t context_len, u8 *out, size_t out_len)
++{
++	/* (EAP-PEAP EAP-TLS EAP-TTLS) */
++  #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++	return (conn && conn->established && conn->tls_prf_type)
++	  ? mbedtls_ssl_tls_prf(conn->tls_prf_type,
++				conn->expkey_secret, conn->expkey_secret_len, label,
++				conn->expkey_randbytes,
++				sizeof(conn->expkey_randbytes), out, out_len)
++	  : -1;
++  #else
++	/* not implemented here for mbedtls < 2.18.0 */
++	return -1;
++  #endif
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_FAST
++
++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++/* keyblock size info is not exposed in mbed TLS 3.0.0 */
++/* extracted from mbedtls library/ssl_tls.c:ssl_tls12_populate_transform() */
++#include <mbedtls/ssl_ciphersuites.h>
++#include <mbedtls/cipher.h>
++static size_t tls_mbedtls_ssl_keyblock_size (mbedtls_ssl_context *ssl)
++{
++  #if !defined(MBEDTLS_USE_PSA_CRYPTO) /* XXX: (not extracted for PSA crypto) */
++  #if defined(MBEDTLS_SSL_PROTO_TLS1_3)
++    if (mbedtls_ssl_get_version_number(ssl) == MBEDTLS_SSL_VERSION_TLS1_3)
++        return 0; /* (calculation not extracted) */
++  #endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
++
++    int ciphersuite = mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl);
++    const mbedtls_ssl_ciphersuite_t *ciphersuite_info =
++      mbedtls_ssl_ciphersuite_from_id(ciphersuite);
++    if (ciphersuite_info == NULL)
++        return 0;
++
++    const mbedtls_cipher_info_t *cipher_info =
++      mbedtls_cipher_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(cipher));
++    if (cipher_info == NULL)
++        return 0;
++
++  #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */
++    size_t keylen = mbedtls_cipher_info_get_key_bitlen(cipher_info) / 8;
++    mbedtls_cipher_mode_t mode = mbedtls_cipher_info_get_mode(cipher_info);
++  #else
++    size_t keylen = cipher_info->MBEDTLS_PRIVATE(key_bitlen) / 8;
++    mbedtls_cipher_mode_t mode = cipher_info->MBEDTLS_PRIVATE(mode);
++  #endif
++  #if defined(MBEDTLS_GCM_C) || \
++      defined(MBEDTLS_CCM_C) || \
++      defined(MBEDTLS_CHACHAPOLY_C)
++    if (mode == MBEDTLS_MODE_GCM || mode == MBEDTLS_MODE_CCM)
++        return keylen + 4;
++    else if (mode == MBEDTLS_MODE_CHACHAPOLY)
++        return keylen + 12;
++    else
++  #endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C || MBEDTLS_CHACHAPOLY_C */
++  #if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
++    {
++        const mbedtls_md_info_t *md_info =
++          mbedtls_md_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(mac));
++        if (md_info == NULL)
++            return 0;
++        size_t mac_key_len = mbedtls_md_get_size(md_info);
++        size_t ivlen = mbedtls_cipher_info_get_iv_size(cipher_info);
++        return keylen + mac_key_len + ivlen;
++    }
++  #endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */
++  #endif /* !MBEDTLS_USE_PSA_CRYPTO *//* (not extracted for PSA crypto) */
++    return 0;
++}
++#endif /* MBEDTLS_VERSION_NUMBER >= 0x03000000 *//* mbedtls 3.0.0 */
++
++
++int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
++				    u8 *out, size_t out_len)
++{
++	/* XXX: has export keys callback been run? */
++	if (!conn || !conn->tls_prf_type)
++		return -1;
++
++  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++	conn->expkey_keyblock_size = tls_mbedtls_ssl_keyblock_size(&conn->ssl);
++	if (conn->expkey_keyblock_size == 0)
++		return -1;
++  #endif
++	size_t skip = conn->expkey_keyblock_size * 2;
++	unsigned char *tmp_out = os_malloc(skip + out_len);
++	if (!tmp_out)
++		return -1;
++
++	/* server_random and then client_random */
++	unsigned char seed[MBEDTLS_EXPKEY_RAND_LEN*2];
++	os_memcpy(seed, conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
++	          MBEDTLS_EXPKEY_RAND_LEN);
++	os_memcpy(seed + MBEDTLS_EXPKEY_RAND_LEN, conn->expkey_randbytes,
++	          MBEDTLS_EXPKEY_RAND_LEN);
++
++  #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++	int ret = mbedtls_ssl_tls_prf(conn->tls_prf_type,
++				      conn->expkey_secret, conn->expkey_secret_len,
++				      "key expansion", seed, sizeof(seed),
++				      tmp_out, skip + out_len);
++	if (ret == 0)
++		os_memcpy(out, tmp_out + skip, out_len);
++  #else
++	int ret = -1; /*(not reached if not impl; return -1 at top of func)*/
++  #endif
++
++	bin_clear_free(tmp_out, skip + out_len);
++	forced_memzero(seed, sizeof(seed));
++	return ret;
++}
++
++#endif /* TLS_MBEDTLS_EAP_FAST */
++
++
++__attribute_cold__
++static void tls_mbedtls_suiteb_handshake_alert (struct tls_connection *conn)
++{
++	/* tests/hwsim/test_suite_b.py test_suite_b_192_rsa_insufficient_dh */
++	if (!(conn->tls_conf->flags & TLS_CONN_SUITEB))
++		return;
++	if (tls_ctx_global.tls_conf) /*(is server; want issue event on client)*/
++		return;
++  #if 0
++	/*(info not available on client;
++         * mbed TLS library enforces dhm min bitlen in ServerKeyExchange)*/
++	if (MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ==
++	  #if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
++	          mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl)
++	  #else
++	          mbedtls_ssl_get_ciphersuite_id(
++	            mbedtls_ssl_get_ciphersuite(&conn->ssl))
++	  #endif
++	    && mbedtls_mpi_size(&conn->tls_conf->conf.MBEDTLS_PRIVATE(dhm_P))
++	         < 384 /*(3072/8)*/)
++  #endif
++	{
++		struct tls_config *init_conf = &tls_ctx_global.init_conf;
++		if (init_conf->event_cb) {
++			union tls_event_data ev;
++			os_memset(&ev, 0, sizeof(ev));
++			ev.alert.is_local = 1;
++			ev.alert.type = "fatal";
++			/*"internal error" string for tests/hwsim/test_suiteb.py */
++			ev.alert.description = "internal error: handshake failure";
++			/*ev.alert.description = "insufficient security";*/
++			init_conf->event_cb(init_conf->cb_ctx, TLS_ALERT, &ev);
++		}
++	}
++}
++
++
++struct wpabuf * tls_connection_handshake(void *tls_ctx,
++					 struct tls_connection *conn,
++					 const struct wpabuf *in_data,
++					 struct wpabuf **appl_data)
++{
++	if (appl_data)
++		*appl_data = NULL;
++
++	if (in_data && wpabuf_len(in_data)) {
++		/*(unsure why tls_gnutls.c discards buffer contents; skip here)*/
++		if (conn->pull_buf && 0) /* disable; appears unwise */
++			tls_pull_buf_discard(conn, __func__);
++		if (!tls_pull_buf_append(conn, in_data))
++			return NULL;
++	}
++
++	if (conn->tls_conf == NULL) {
++		struct tls_connection_params params;
++		os_memset(&params, 0, sizeof(params));
++		params.openssl_ciphers =
++		  tls_ctx_global.init_conf.openssl_ciphers;
++		params.flags = tls_ctx_global.tls_conf->flags;
++		if (tls_connection_set_params(tls_ctx, conn, &params) != 0)
++			return NULL;
++	}
++
++	if (conn->verify_peer) /*(call here might be redundant; nbd)*/
++		mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
++
++  #ifdef TLS_MBEDTLS_SESSION_TICKETS
++	if (conn->clienthello_session_ticket)
++		/*(starting handshake for EAP-FAST and EAP-TEAP)*/
++		tls_mbedtls_clienthello_session_ticket_set(conn);
++
++	/* (not thread-safe due to need to set userdata 'conn' for callback) */
++	/* (unable to use mbedtls_ssl_set_user_data_p() with mbedtls 3.2.0+
++	 *  since ticket write and parse callbacks take (mbedtls_ssl_session *)
++	 *  param instead of (mbedtls_ssl_context *) param) */
++	if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
++		mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
++		                                    NULL, NULL, NULL);
++	else
++		mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
++		                                    tls_mbedtls_ssl_ticket_write,
++		                                    tls_mbedtls_ssl_ticket_parse,
++		                                    conn);
++  #endif
++
++  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
++	int ret = mbedtls_ssl_handshake(&conn->ssl);
++  #else
++	int ret = 0;
++	while (conn->ssl.MBEDTLS_PRIVATE(state) != MBEDTLS_SSL_HANDSHAKE_OVER) {
++		ret = mbedtls_ssl_handshake_step(&conn->ssl);
++		if (ret != 0)
++			break;
++	}
++  #endif
++
++  #ifdef TLS_MBEDTLS_SESSION_TICKETS
++	mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
++	                                    tls_mbedtls_ssl_ticket_write,
++	                                    tls_mbedtls_ssl_ticket_parse,
++	                                    NULL);
++  #endif
++
++	switch (ret) {
++	case 0:
++		conn->established = 1;
++		if (conn->push_buf == NULL)
++			/* Need to return something to get final TLS ACK. */
++			conn->push_buf = wpabuf_alloc(0);
++
++		if (appl_data /*&& conn->pull_buf && wpabuf_len(conn->pull_buf)*/)
++			*appl_data = NULL; /* RFE: check for application data */
++		break;
++	case MBEDTLS_ERR_SSL_WANT_WRITE:
++	case MBEDTLS_ERR_SSL_WANT_READ:
++	case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS:
++	case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS:
++		if (tls_ctx_global.tls_conf /*(is server)*/
++		    && conn->established && conn->push_buf == NULL)
++			/* Need to return something to trigger completion of EAP-TLS. */
++			conn->push_buf = wpabuf_alloc(0);
++		break;
++	default:
++		++conn->failed;
++		switch (ret) {
++		case MBEDTLS_ERR_SSL_CLIENT_RECONNECT:
++		case MBEDTLS_ERR_NET_CONN_RESET:
++		case MBEDTLS_ERR_NET_SEND_FAILED:
++			++conn->write_alerts;
++			break;
++	      #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
++		case MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE:
++	      #else
++		case MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE:
++	      #endif
++			tls_mbedtls_suiteb_handshake_alert(conn);
++			/* fall through */
++		case MBEDTLS_ERR_NET_RECV_FAILED:
++		case MBEDTLS_ERR_SSL_CONN_EOF:
++		case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
++		case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE:
++			++conn->read_alerts;
++			break;
++		default:
++			break;
++		}
++
++		ilog(ret, "mbedtls_ssl_handshake");
++		break;
++	}
++
++	struct wpabuf *out_data = conn->push_buf;
++	conn->push_buf = NULL;
++	return out_data;
++}
++
++
++struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
++						struct tls_connection *conn,
++						const struct wpabuf *in_data,
++						struct wpabuf **appl_data)
++{
++	conn->is_server = 1;
++	return tls_connection_handshake(tls_ctx, conn, in_data, appl_data);
++}
++
++
++struct wpabuf * tls_connection_encrypt(void *tls_ctx,
++				       struct tls_connection *conn,
++				       const struct wpabuf *in_data)
++{
++	int res = mbedtls_ssl_write(&conn->ssl,
++	                            wpabuf_head_u8(in_data), wpabuf_len(in_data));
++	if (res < 0) {
++		elog(res, "mbedtls_ssl_write");
++		return NULL;
++	}
++
++	struct wpabuf *buf = conn->push_buf;
++	conn->push_buf = NULL;
++	return buf;
++}
++
++
++struct wpabuf * tls_connection_decrypt(void *tls_ctx,
++				       struct tls_connection *conn,
++				       const struct wpabuf *in_data)
++{
++	int res;
++	struct wpabuf *out;
++
++	/*assert(in_data != NULL);*/
++	if (!tls_pull_buf_append(conn, in_data))
++		return NULL;
++
++  #if defined(MBEDTLS_ZLIB_SUPPORT) /* removed in mbedtls 3.x */
++	/* Add extra buffer space to handle the possibility of decrypted
++	 * data being longer than input data due to TLS compression. */
++	out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
++  #else /* TLS compression is disabled in mbedtls 3.x */
++	out = wpabuf_alloc(wpabuf_len(in_data));
++  #endif
++	if (out == NULL)
++		return NULL;
++
++	res = mbedtls_ssl_read(&conn->ssl, wpabuf_mhead(out), wpabuf_size(out));
++	if (res < 0) {
++	  #if 1 /*(seems like a different error if wpabuf_len(in_data) == 0)*/
++		if (res == MBEDTLS_ERR_SSL_WANT_READ)
++			return out;
++	  #endif
++		elog(res, "mbedtls_ssl_read");
++		wpabuf_free(out);
++		return NULL;
++	}
++	wpabuf_put(out, res);
++
++	return out;
++}
++
++
++int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
++{
++	/* XXX: might need to detect if session resumed from TLS session ticket
++	 * even if not special session ticket handling for EAP-FAST, EAP-TEAP */
++	/* (?ssl->handshake->resume during session ticket validation?) */
++	return conn && conn->resumed;
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_FAST
++int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
++				   u8 *ciphers)
++{
++	/* ciphers is list of TLS_CIPHER_* from hostap/src/crypto/tls.h */
++	int ids[7];
++	const int idsz = (int)sizeof(ids);
++	int nids = -1, id;
++	for ( ; *ciphers != TLS_CIPHER_NONE; ++ciphers) {
++		switch (*ciphers) {
++		case TLS_CIPHER_RC4_SHA:
++		  #ifdef MBEDTLS_TLS_RSA_WITH_RC4_128_SHA
++			id = MBEDTLS_TLS_RSA_WITH_RC4_128_SHA;
++			break;
++		  #else
++			continue; /*(not supported in mbedtls 3.x; ignore)*/
++		  #endif
++		case TLS_CIPHER_AES128_SHA:
++			id = MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA;
++			break;
++		case TLS_CIPHER_RSA_DHE_AES128_SHA:
++			id = MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
++			break;
++		case TLS_CIPHER_ANON_DH_AES128_SHA:
++			continue; /*(not supported in mbedtls; ignore)*/
++		case TLS_CIPHER_RSA_DHE_AES256_SHA:
++			id = MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
++			break;
++		case TLS_CIPHER_AES256_SHA:
++			id = MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA;
++			break;
++		default:
++			return -1; /* should not happen */
++		}
++		if (++nids == idsz)
++			return -1; /* should not happen */
++		ids[nids] = id;
++	}
++	if (nids < 0)
++		return 0; /* nothing to do */
++	if (++nids == idsz)
++		return -1; /* should not happen */
++	ids[nids] = 0; /* terminate list */
++	++nids;
++
++	return tls_mbedtls_set_ciphersuites(conn->tls_conf, ids, nids) ? 0 : -1;
++}
++#endif
++
++
++int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
++		    char *buf, size_t buflen)
++{
++	if (conn == NULL)
++		return -1;
++	os_strlcpy(buf, mbedtls_ssl_get_version(&conn->ssl), buflen);
++	return buf[0] != 'u' ? 0 : -1; /*(-1 if "unknown")*/
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_TEAP
++u16 tls_connection_get_cipher_suite(struct tls_connection *conn)
++{
++	if (conn == NULL)
++		return 0;
++	return (u16)mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
++}
++#endif
++
++
++int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
++		   char *buf, size_t buflen)
++{
++	if (conn == NULL)
++		return -1;
++	const int id = mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
++	return tls_mbedtls_translate_ciphername(id, buf, buflen) ? 0 : -1;
++}
++
++
++#ifdef TLS_MBEDTLS_SESSION_TICKETS
++
++int tls_connection_enable_workaround(void *tls_ctx,
++				     struct tls_connection *conn)
++{
++	/* (see comment in src/eap_peer/eap_fast.c:eap_fast_init()) */
++	/* XXX: is there a relevant setting for this in mbed TLS? */
++	/* (do we even care that much about older CBC ciphers?) */
++	return 0;
++}
++
++
++int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
++				    int ext_type, const u8 *data,
++				    size_t data_len)
++{
++	/* (EAP-FAST and EAP-TEAP) */
++	if (ext_type == MBEDTLS_TLS_EXT_SESSION_TICKET) /*(ext_type == 35)*/
++		return tls_mbedtls_clienthello_session_ticket_prep(conn, data,
++		                                                   data_len);
++
++	return -1;
++}
++
++#endif /* TLS_MBEDTLS_SESSION_TICKETS */
++
++
++int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
++{
++	return conn ? conn->failed : -1;
++}
++
++
++int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
++{
++	return conn ? conn->read_alerts : -1;
++}
++
++
++int tls_connection_get_write_alerts(void *tls_ctx,
++				    struct tls_connection *conn)
++{
++	return conn ? conn->write_alerts : -1;
++}
++
++
++#ifdef TLS_MBEDTLS_SESSION_TICKETS
++int tls_connection_set_session_ticket_cb(
++	void *tls_ctx, struct tls_connection *conn,
++	tls_session_ticket_cb cb, void *ctx)
++{
++	if (!(conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)) {
++		/* (EAP-FAST and EAP-TEAP) */
++		conn->session_ticket_cb = cb;
++		conn->session_ticket_cb_ctx = ctx;
++		return 0;
++	}
++	return -1;
++}
++#endif
++
++
++int tls_get_library_version(char *buf, size_t buf_len)
++{
++  #ifndef MBEDTLS_VERSION_C
++	const char * const ver = "n/a";
++  #else
++	char ver[9];
++	mbedtls_version_get_string(ver);
++  #endif
++	return os_snprintf(buf, buf_len,
++	                   "mbed TLS build=" MBEDTLS_VERSION_STRING " run=%s", ver);
++}
++
++
++void tls_connection_set_success_data(struct tls_connection *conn,
++				     struct wpabuf *data)
++{
++	wpabuf_free(conn->success_data);
++	conn->success_data = data;
++}
++
++
++void tls_connection_set_success_data_resumed(struct tls_connection *conn)
++{
++}
++
++
++const struct wpabuf *
++tls_connection_get_success_data(struct tls_connection *conn)
++{
++	return conn->success_data;
++}
++
++
++void tls_connection_remove_session(struct tls_connection *conn)
++{
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_TEAP
++int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len)
++{
++  #if defined(MBEDTLS_SSL_RENEGOTIATION) /* XXX: renegotiation or resumption? */
++	/* data from TLS handshake Finished message */
++	size_t verify_len = conn->ssl.MBEDTLS_PRIVATE(verify_data_len);
++	char *verify_data = (conn->is_server ^ conn->resumed)
++	  ? conn->ssl.MBEDTLS_PRIVATE(peer_verify_data)
++	  : conn->ssl.MBEDTLS_PRIVATE(own_verify_data);
++	if (verify_len && verify_len <= max_len) {
++		os_memcpy(buf, verify_data, verify_len);
++		return (int)verify_len;
++	}
++  #endif
++	return -1;
++}
++#endif
++
++
++__attribute_noinline__
++static void tls_mbedtls_set_peer_subject(struct tls_connection *conn, const mbedtls_x509_crt *crt)
++{
++	if (conn->peer_subject)
++		return;
++	char buf[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
++	int buflen = mbedtls_x509_dn_gets(buf, sizeof(buf), &crt->subject);
++	if (buflen >= 0 && (conn->peer_subject = os_malloc((size_t)buflen+1)))
++		os_memcpy(conn->peer_subject, buf, (size_t)buflen+1);
++}
++
++
++#ifdef TLS_MBEDTLS_EAP_TEAP
++const char * tls_connection_get_peer_subject(struct tls_connection *conn)
++{
++	if (!conn)
++		return NULL;
++	if (!conn->peer_subject) { /*(if not set during cert verify)*/
++		const mbedtls_x509_crt *peer_cert =
++		  mbedtls_ssl_get_peer_cert(&conn->ssl);
++		if (peer_cert)
++			tls_mbedtls_set_peer_subject(conn, peer_cert);
++	}
++	return conn->peer_subject;
++}
++#endif
++
++
++#ifdef TLS_MBEDTLS_EAP_TEAP
++bool tls_connection_get_own_cert_used(struct tls_connection *conn)
++{
++	/* XXX: availability of cert does not necessary mean that client
++	 * received certificate request from server and then sent cert.
++	 * ? step handshake in tls_connection_handshake() looking for
++	 *   MBEDTLS_SSL_CERTIFICATE_REQUEST ? */
++	const struct tls_conf * const tls_conf = conn->tls_conf;
++	return (tls_conf->has_client_cert && tls_conf->has_private_key);
++}
++#endif
++
++
++#if defined(CONFIG_FIPS)
++#define TLS_MBEDTLS_CONFIG_FIPS
++#endif
++
++#if defined(CONFIG_SHA256)
++#define TLS_MBEDTLS_TLS_PRF_SHA256
++#endif
++
++#if defined(CONFIG_SHA384)
++#define TLS_MBEDTLS_TLS_PRF_SHA384
++#endif
++
++
++#ifndef TLS_MBEDTLS_CONFIG_FIPS
++#if defined(CONFIG_MODULE_TESTS)
++/* unused with CONFIG_TLS=mbedtls except in crypto_module_tests.c */
++#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */ \
++ && MBEDTLS_VERSION_NUMBER <  0x03000000 /* mbedtls 3.0.0 */
++/* sha1-tlsprf.c */
++#include "sha1.h"
++int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
++		     const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
++{
++	return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_TLS1,
++				   secret, secret_len, label,
++				   seed, seed_len, out, outlen) ? -1 : 0;
++}
++#else
++#include "sha1-tlsprf.c" /* pull in hostap local implementation */
++#endif
++#endif
++#endif
++
++#ifdef TLS_MBEDTLS_TLS_PRF_SHA256
++/* sha256-tlsprf.c */
++#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++#include "sha256.h"
++int tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label,
++		   const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
++{
++	return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA256,
++				   secret, secret_len, label,
++				   seed, seed_len, out, outlen) ? -1 : 0;
++}
++#else
++#include "sha256-tlsprf.c" /* pull in hostap local implementation */
++#endif
++#endif
++
++#ifdef TLS_MBEDTLS_TLS_PRF_SHA384
++/* sha384-tlsprf.c */
++#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
++#include "sha384.h"
++int tls_prf_sha384(const u8 *secret, size_t secret_len, const char *label,
++		   const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
++{
++	return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA384,
++				   secret, secret_len, label,
++				   seed, seed_len, out, outlen) ? -1 : 0;
++}
++#else
++#include "sha384-tlsprf.c" /* pull in hostap local implementation */
++#endif
++#endif
++
++
++#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
++#define mbedtls_x509_crt_has_ext_type(crt, ext_type) \
++        ((crt)->MBEDTLS_PRIVATE(ext_types) & (ext_type))
++#endif
++
++struct mlist { const char *p; size_t n; };
++
++
++static int
++tls_mbedtls_match_altsubject(mbedtls_x509_crt *crt, const char *match)
++{
++	/* RFE: this could be pre-parsed into structured data at config time */
++	struct mlist list[256]; /*(much larger than expected)*/
++	int nlist = 0;
++	if (   os_strncmp(match, "EMAIL:", 6) != 0
++	    && os_strncmp(match, "DNS:",   4) != 0
++	    && os_strncmp(match, "URI:",   4) != 0   ) {
++		wpa_printf(MSG_INFO, "MTLS: Invalid altSubjectName match '%s'", match);
++		return 0;
++	}
++	for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
++		do { } while ((tok = os_strchr(s, ';'))
++		              && os_strncmp(tok+1, "EMAIL:", 6) != 0
++		              && os_strncmp(tok+1, "DNS:",   4) != 0
++		              && os_strncmp(tok+1, "URI:",   4) != 0);
++		list[nlist].p = s;
++		list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
++		if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
++			wpa_printf(MSG_INFO, "MTLS: excessive altSubjectName match '%s'",
++			           match);
++			break; /* truncate huge list and continue */
++		}
++	}
++
++	if (!mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
++		return 0;
++
++	const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
++	for (; cur != NULL; cur = cur->next) {
++		const unsigned char san_type = (unsigned char)cur->buf.tag
++		                             & MBEDTLS_ASN1_TAG_VALUE_MASK;
++		char t;
++		size_t step = 4;
++		switch (san_type) {             /* "EMAIL:" or "DNS:" or "URI:" */
++		case MBEDTLS_X509_SAN_RFC822_NAME:       step = 6; t = 'E'; break;
++		case MBEDTLS_X509_SAN_DNS_NAME:                    t = 'D'; break;
++		case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: t = 'U'; break;
++		default: continue;
++		}
++
++		for (int i = 0; i < nlist; ++i) {
++			/* step over "EMAIL:" or "DNS:" or "URI:" in list[i].p */
++			/* Note: v is not '\0'-terminated, but is a known length vlen,
++			 * so okay to pass to os_strncasecmp() even though not z-string */
++			if (cur->buf.len == list[i].n - step && t == *list[i].p
++			    && 0 == os_strncasecmp((char *)cur->buf.p,
++			                           list[i].p+step, cur->buf.len)) {
++				return 1; /* match */
++			}
++		}
++	}
++	return 0; /* no match */
++}
++
++
++static int
++tls_mbedtls_match_suffix(const char *v, size_t vlen,
++                         const struct mlist *list, int nlist, int full)
++{
++	/* Note: v is not '\0'-terminated, but is a known length vlen,
++	 * so okay to pass to os_strncasecmp() even though not z-string */
++	for (int i = 0; i < nlist; ++i) {
++		size_t n = list[i].n;
++		if ((n == vlen || (n < vlen && v[vlen-n-1] == '.' && !full))
++		    && 0 == os_strncasecmp(v+vlen-n, list[i].p, n))
++			return 1; /* match */
++	}
++	return 0; /* no match */
++}
++
++
++static int
++tls_mbedtls_match_suffixes(mbedtls_x509_crt *crt, const char *match, int full)
++{
++	/* RFE: this could be pre-parsed into structured data at config time */
++	struct mlist list[256]; /*(much larger than expected)*/
++	int nlist = 0;
++	for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
++		tok = os_strchr(s, ';');
++		list[nlist].p = s;
++		list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
++		if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
++			wpa_printf(MSG_INFO, "MTLS: excessive suffix match '%s'", match);
++			break; /* truncate huge list and continue */
++		}
++	}
++
++	/* check subjectAltNames */
++	if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME)) {
++		const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
++		for (; cur != NULL; cur = cur->next) {
++			const unsigned char san_type = (unsigned char)cur->buf.tag
++			                             & MBEDTLS_ASN1_TAG_VALUE_MASK;
++			if (san_type == MBEDTLS_X509_SAN_DNS_NAME
++			    && tls_mbedtls_match_suffix((char *)cur->buf.p,
++			                                cur->buf.len,
++			                                list, nlist, full)) {
++				return 1; /* match */
++			}
++		}
++	}
++
++	/* check subject CN */
++	const mbedtls_x509_name *name = &crt->subject;
++	for (; name != NULL; name = name->next) {
++		if (name->oid.p && MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid) == 0)
++			break;
++	}
++	if (name && tls_mbedtls_match_suffix((char *)name->val.p, name->val.len,
++	                                     list, nlist, full)) {
++		return 1; /* match */
++	}
++
++	return 0; /* no match */
++}
++
++
++static int
++tls_mbedtls_match_dn_field(mbedtls_x509_crt *crt, const char *match)
++{
++	/* RFE: this could be pre-parsed into structured data at config time */
++	struct mlistoid { const char *p; size_t n;
++	                  const char *oid; size_t olen;
++	                  int prefix; };
++	struct mlistoid list[32]; /*(much larger than expected)*/
++	int nlist = 0;
++	for (const char *s = match, *tok, *e; *s; s = tok ? tok+1 : "") {
++		tok = os_strchr(s, '/');
++		list[nlist].oid = NULL;
++		list[nlist].olen = 0;
++		list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
++		e = memchr(s, '=', list[nlist].n);
++		if (e == NULL) {
++			if (list[nlist].n == 0)
++				continue; /* skip consecutive, repeated '/' */
++			if (list[nlist].n == 1 && *s == '*') {
++				/* special-case "*" to match any OID and value */
++				s = e = "=*";
++				list[nlist].n = 2;
++				list[nlist].oid = "";
++			}
++			else {
++				wpa_printf(MSG_INFO,
++				           "MTLS: invalid check_cert_subject '%s' missing '='",
++				           match);
++				return 0;
++			}
++		}
++		switch (e - s) {
++		case 1:
++			if (*s == 'C') {
++				list[nlist].oid  = MBEDTLS_OID_AT_COUNTRY;
++				list[nlist].olen = sizeof(MBEDTLS_OID_AT_COUNTRY)-1;
++			}
++			else if (*s == 'L') {
++				list[nlist].oid  = MBEDTLS_OID_AT_LOCALITY;
++				list[nlist].olen = sizeof(MBEDTLS_OID_AT_LOCALITY)-1;
++			}
++			else if (*s == 'O') {
++				list[nlist].oid  = MBEDTLS_OID_AT_ORGANIZATION;
++				list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORGANIZATION)-1;
++			}
++			break;
++		case 2:
++			if (s[0] == 'C' && s[1] == 'N') {
++				list[nlist].oid  = MBEDTLS_OID_AT_CN;
++				list[nlist].olen = sizeof(MBEDTLS_OID_AT_CN)-1;
++			}
++			else if (s[0] == 'S' && s[1] == 'T') {
++				list[nlist].oid  = MBEDTLS_OID_AT_STATE;
++				list[nlist].olen = sizeof(MBEDTLS_OID_AT_STATE)-1;
++			}
++			else if (s[0] == 'O' && s[1] == 'U') {
++				list[nlist].oid  = MBEDTLS_OID_AT_ORG_UNIT;
++				list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORG_UNIT)-1;
++			}
++			break;
++		case 12:
++			if (os_memcmp(s, "emailAddress", 12) == 0) {
++				list[nlist].oid  = MBEDTLS_OID_PKCS9_EMAIL;
++				list[nlist].olen = sizeof(MBEDTLS_OID_PKCS9_EMAIL)-1;
++			}
++			break;
++		default:
++			break;
++		}
++		if (list[nlist].oid == NULL) {
++			wpa_printf(MSG_INFO,
++			           "MTLS: Unknown field in check_cert_subject '%s'",
++			           match);
++			return 0;
++		}
++		list[nlist].n -= (size_t)(++e - s);
++		list[nlist].p = e;
++		if (list[nlist].n && e[list[nlist].n-1] == '*') {
++			--list[nlist].n;
++			list[nlist].prefix = 1;
++		}
++		/*(could easily add support for suffix matches if value begins with '*',
++		 * but suffix match is not currently supported by other TLS modules)*/
++
++		if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
++			wpa_printf(MSG_INFO,
++			           "MTLS: excessive check_cert_subject match '%s'",
++			           match);
++			break; /* truncate huge list and continue */
++		}
++	}
++
++	/* each component in match string must match cert Subject in order listed
++	 * The behavior below preserves ordering but is slightly different than
++	 * the grossly inefficient contortions implemented in tls_openssl.c */
++	const mbedtls_x509_name *name = &crt->subject;
++	for (int i = 0; i < nlist; ++i) {
++		int found = 0;
++		for (; name != NULL && !found; name = name->next) {
++			if (!name->oid.p)
++				continue;
++			/* special-case "*" to match any OID and value */
++			if (list[i].olen == 0) {
++				found = 1;
++				continue;
++			}
++			/* perform equalent of !MBEDTLS_OID_CMP() with oid ptr and len */
++			if (list[i].olen != name->oid.len
++			    || os_memcmp(list[i].oid, name->oid.p, name->oid.len) != 0)
++				continue;
++			/* Note: v is not '\0'-terminated, but is a known length vlen,
++			 * so okay to pass to os_strncasecmp() even though not z-string */
++			if ((list[i].prefix
++			      ? list[i].n <= name->val.len  /* prefix match */
++			      : list[i].n == name->val.len) /* full match */
++			    && 0 == os_strncasecmp((char *)name->val.p,
++			                           list[i].p, list[i].n)) {
++				found = 1;
++				continue;
++			}
++		}
++		if (!found)
++			return 0; /* no match */
++	}
++	return 1; /* match */
++}
++
++
++__attribute_cold__
++static void
++tls_mbedtls_verify_fail_event (mbedtls_x509_crt *crt, int depth,
++                               const char *errmsg, enum tls_fail_reason reason)
++{
++	struct tls_config *init_conf = &tls_ctx_global.init_conf;
++	if (init_conf->event_cb == NULL)
++		return;
++
++	struct wpabuf *certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
++	char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
++	if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
++		subject[0] = '\0';
++	union tls_event_data ev;
++	os_memset(&ev, 0, sizeof(ev));
++	ev.cert_fail.reason = reason;
++	ev.cert_fail.depth = depth;
++	ev.cert_fail.subject = subject;
++	ev.cert_fail.reason_txt = errmsg;
++	ev.cert_fail.cert = certbuf;
++
++	init_conf->event_cb(init_conf->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
++
++	wpabuf_free(certbuf);
++}
++
++
++__attribute_noinline__
++static void
++tls_mbedtls_verify_cert_event (struct tls_connection *conn,
++                               mbedtls_x509_crt *crt, int depth)
++{
++	struct tls_config *init_conf = &tls_ctx_global.init_conf;
++	if (init_conf->event_cb == NULL)
++		return;
++
++	struct wpabuf *certbuf = NULL;
++	union tls_event_data ev;
++	os_memset(&ev, 0, sizeof(ev));
++
++  #ifdef MBEDTLS_SHA256_C
++	u8 hash[SHA256_DIGEST_LENGTH];
++	const u8 *addr[] = { (u8 *)crt->raw.p };
++	if (sha256_vector(1, addr, &crt->raw.len, hash) == 0) {
++		ev.peer_cert.hash = hash;
++		ev.peer_cert.hash_len = sizeof(hash);
++	}
++  #endif
++	ev.peer_cert.depth = depth;
++	char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
++	if (depth == 0)
++		ev.peer_cert.subject = conn->peer_subject;
++	if (ev.peer_cert.subject == NULL) {
++		ev.peer_cert.subject = subject;
++		if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
++			subject[0] = '\0';
++	}
++
++	char serial_num[128+1];
++	ev.peer_cert.serial_num =
++	  tls_mbedtls_peer_serial_num(crt, serial_num, sizeof(serial_num));
++
++	const mbedtls_x509_sequence *cur;
++
++	cur = NULL;
++	if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
++		cur = &crt->subject_alt_names;
++	for (; cur != NULL; cur = cur->next) {
++		const unsigned char san_type = (unsigned char)cur->buf.tag
++		                             & MBEDTLS_ASN1_TAG_VALUE_MASK;
++		size_t prelen = 4;
++		const char *pre;
++		switch (san_type) {
++		case MBEDTLS_X509_SAN_RFC822_NAME:     prelen = 6; pre = "EMAIL:";break;
++		case MBEDTLS_X509_SAN_DNS_NAME:                    pre = "DNS:";  break;
++		case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: pre = "URI:";  break;
++		default: continue;
++		}
++
++		char *pos = os_malloc(prelen + cur->buf.len + 1);
++		if (pos == NULL)
++			break;
++		ev.peer_cert.altsubject[ev.peer_cert.num_altsubject] = pos;
++		os_memcpy(pos, pre, prelen);
++		/* data should be properly backslash-escaped if needed,
++		 * so code below does not re-escape, but does replace CTLs */
++		/*os_memcpy(pos+prelen, cur->buf.p, cur->buf.len);*/
++		/*pos[prelen+cur->buf.len] = '\0';*/
++		pos += prelen;
++		for (size_t i = 0; i < cur->buf.len; ++i) {
++			unsigned char c = cur->buf.p[i];
++			*pos++ = (c >= 32 && c != 127) ? c : '?';
++		}
++		*pos = '\0';
++
++		if (++ev.peer_cert.num_altsubject == TLS_MAX_ALT_SUBJECT)
++			break;
++	}
++
++	cur = NULL;
++	if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_CERTIFICATE_POLICIES))
++		cur = &crt->certificate_policies;
++	for (; cur != NULL; cur = cur->next) {
++		if (cur->buf.len != 11) /* len of OID_TOD_STRICT or OID_TOD_TOFU */
++			continue;
++		/* TOD-STRICT "1.3.6.1.4.1.40808.1.3.1" */
++		/* TOD-TOFU   "1.3.6.1.4.1.40808.1.3.2" */
++		#define OID_TOD_STRICT "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x01"
++		#define OID_TOD_TOFU   "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x02"
++		if (os_memcmp(cur->buf.p,
++		              OID_TOD_STRICT, sizeof(OID_TOD_STRICT)-1) == 0) {
++			ev.peer_cert.tod = 1; /* TOD-STRICT */
++			break;
++		}
++		if (os_memcmp(cur->buf.p,
++		              OID_TOD_TOFU, sizeof(OID_TOD_TOFU)-1) == 0) {
++			ev.peer_cert.tod = 2; /* TOD-TOFU */
++			break;
++		}
++	}
++
++	struct tls_conf *tls_conf = conn->tls_conf;
++	if (tls_conf->ca_cert_probe || (tls_conf->flags & TLS_CONN_EXT_CERT_CHECK)
++	    || init_conf->cert_in_cb) {
++		certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
++		ev.peer_cert.cert = certbuf;
++	}
++
++	init_conf->event_cb(init_conf->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
++
++	wpabuf_free(certbuf);
++	char **altsubject;
++	*(const char ***)&altsubject = ev.peer_cert.altsubject;
++	for (size_t i = 0; i < ev.peer_cert.num_altsubject; ++i)
++		os_free(altsubject[i]);
++}
++
++
++static int
++tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
++{
++	/* XXX: N.B. verify code not carefully tested besides hwsim tests
++	 *
++	 * RFE: mbedtls_x509_crt_verify_info() and enhance log trace messages
++	 * RFE: review and add support for additional TLS_CONN_* flags
++	 * not handling OCSP (not available in mbedtls)
++	 * ... */
++
++	struct tls_connection *conn = (struct tls_connection *)arg;
++	struct tls_conf *tls_conf = conn->tls_conf;
++	uint32_t flags_in = *flags;
++
++	if (depth > 8) { /*(depth 8 picked as arbitrary limit)*/
++		emsg(MSG_WARNING, "client cert chain too long");
++		*flags |= MBEDTLS_X509_BADCERT_OTHER; /* cert chain too long */
++		tls_mbedtls_verify_fail_event(crt, depth,
++			                      "client cert chain too long",
++		                              TLS_FAIL_BAD_CERTIFICATE);
++	}
++	else if (tls_conf->verify_depth0_only) {
++		if (depth > 0)
++			*flags = 0;
++		else {
++		  #ifdef MBEDTLS_SHA256_C
++			u8 hash[SHA256_DIGEST_LENGTH];
++			const u8 *addr[] = { (u8 *)crt->raw.p };
++			if (sha256_vector(1, addr, &crt->raw.len, hash) < 0
++			    || os_memcmp(tls_conf->ca_cert_hash, hash, sizeof(hash)) != 0) {
++				*flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED;
++				tls_mbedtls_verify_fail_event(crt, depth,
++			                                      "cert hash mismatch",
++				                              TLS_FAIL_UNTRUSTED);
++			}
++			else /* hash matches; ignore other issues *except* if revoked)*/
++				*flags &= MBEDTLS_X509_BADCERT_REVOKED;
++		  #endif
++		}
++	}
++	else if (depth == 0) {
++		if (!conn->peer_subject)
++			tls_mbedtls_set_peer_subject(conn, crt);
++		/*(use same labels to tls_mbedtls_verify_fail_event() as used in
++		 * other TLS modules so that hwsim tests find exact string match)*/
++		if (!conn->peer_subject) { /* error copying subject string */
++			*flags |= MBEDTLS_X509_BADCERT_OTHER;
++			tls_mbedtls_verify_fail_event(crt, depth,
++			                              "internal error",
++			                              TLS_FAIL_UNSPECIFIED);
++		}
++		/*(use os_strstr() for subject match as is done in tls_mbedtls.c
++		 * to follow the same behavior, even though a suffix match would
++		 * make more sense.  Also, note that strstr match does not
++		 * normalize whitespace (between components) for comparison)*/
++		else if (tls_conf->subject_match
++		         && os_strstr(conn->peer_subject,
++		                      tls_conf->subject_match) == NULL) {
++			wpa_printf(MSG_WARNING,
++			           "MTLS: Subject '%s' did not match with '%s'",
++			           conn->peer_subject, tls_conf->subject_match);
++			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++			tls_mbedtls_verify_fail_event(crt, depth,
++			                              "Subject mismatch",
++			                              TLS_FAIL_SUBJECT_MISMATCH);
++		}
++		if (tls_conf->altsubject_match
++		    && !tls_mbedtls_match_altsubject(crt, tls_conf->altsubject_match)) {
++			wpa_printf(MSG_WARNING,
++				   "MTLS: altSubjectName match '%s' not found",
++			           tls_conf->altsubject_match);
++			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++			tls_mbedtls_verify_fail_event(crt, depth,
++			                              "AltSubject mismatch",
++			                              TLS_FAIL_ALTSUBJECT_MISMATCH);
++		}
++		if (tls_conf->suffix_match
++		    && !tls_mbedtls_match_suffixes(crt, tls_conf->suffix_match, 0)) {
++			wpa_printf(MSG_WARNING,
++			           "MTLS: Domain suffix match '%s' not found",
++				   tls_conf->suffix_match);
++			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++			tls_mbedtls_verify_fail_event(crt, depth,
++			                              "Domain suffix mismatch",
++			                              TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
++		}
++		if (tls_conf->domain_match
++		    && !tls_mbedtls_match_suffixes(crt, tls_conf->domain_match, 1)) {
++			wpa_printf(MSG_WARNING,
++			           "MTLS: Domain match '%s' not found",
++				   tls_conf->domain_match);
++			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++			tls_mbedtls_verify_fail_event(crt, depth,
++			                              "Domain mismatch",
++			                              TLS_FAIL_DOMAIN_MISMATCH);
++		}
++		if (tls_conf->check_cert_subject
++		    && !tls_mbedtls_match_dn_field(crt, tls_conf->check_cert_subject)) {
++			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
++			tls_mbedtls_verify_fail_event(crt, depth,
++			                              "Distinguished Name",
++			                              TLS_FAIL_DN_MISMATCH);
++		}
++		if (tls_conf->flags & TLS_CONN_SUITEB) {
++			/* check RSA modulus size (public key bitlen) */
++			const mbedtls_pk_type_t pk_alg = mbedtls_pk_get_type(&crt->pk);
++			if ((pk_alg == MBEDTLS_PK_RSA || pk_alg == MBEDTLS_PK_RSASSA_PSS)
++			    && mbedtls_pk_get_bitlen(&crt->pk) < 3072) {
++				/* hwsim suite_b RSA tests expect 3072
++				 *   suite_b_192_rsa_ecdhe_radius_rsa2048_client
++				 *   suite_b_192_rsa_dhe_radius_rsa2048_client */
++				*flags |= MBEDTLS_X509_BADCERT_BAD_KEY;
++				tls_mbedtls_verify_fail_event(crt, depth,
++				                              "Insufficient RSA modulus size",
++				                              TLS_FAIL_INSUFFICIENT_KEY_LEN);
++			}
++		}
++		if (tls_conf->check_crl && tls_conf->crl == NULL) {
++			/* see tests/hwsim test_ap_eap.py ap_wpa2_eap_tls_check_crl */
++			emsg(MSG_WARNING, "check_crl set but no CRL loaded; reject all?");
++			*flags |= MBEDTLS_X509_BADCERT_OTHER;
++			tls_mbedtls_verify_fail_event(crt, depth,
++				                      "check_crl set but no CRL loaded; "
++			                              "reject all?",
++			                              TLS_FAIL_BAD_CERTIFICATE);
++		}
++	}
++	else {
++		if (tls_conf->check_crl != 2) /* 2 == verify CRLs for all certs */
++			*flags &= ~MBEDTLS_X509_BADCERT_REVOKED;
++	}
++
++	if (!tls_conf->check_crl_strict) {
++		*flags &= ~MBEDTLS_X509_BADCRL_EXPIRED;
++		*flags &= ~MBEDTLS_X509_BADCRL_FUTURE;
++	}
++
++	if (tls_conf->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
++		*flags &= ~MBEDTLS_X509_BADCERT_EXPIRED;
++		*flags &= ~MBEDTLS_X509_BADCERT_FUTURE;
++	}
++
++	tls_mbedtls_verify_cert_event(conn, crt, depth);
++
++	if (*flags) {
++		if (*flags & (MBEDTLS_X509_BADCERT_NOT_TRUSTED
++		             |MBEDTLS_X509_BADCERT_CN_MISMATCH
++		             |MBEDTLS_X509_BADCERT_REVOKED)) {
++			emsg(MSG_WARNING, "client cert not trusted");
++		}
++		/* report event if flags set but no additional flags set above */
++		/* (could translate flags to more detailed TLS_FAIL_* if needed) */
++		if (!(*flags & ~flags_in)) {
++			enum tls_fail_reason reason = TLS_FAIL_UNSPECIFIED;
++			const char *errmsg = "cert verify fail unspecified";
++			if (*flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) {
++				reason = TLS_FAIL_UNTRUSTED;
++				errmsg = "certificate not trusted";
++			}
++			if (*flags & MBEDTLS_X509_BADCERT_REVOKED) {
++				reason = TLS_FAIL_REVOKED;
++				errmsg = "certificate has been revoked";
++			}
++			if (*flags & MBEDTLS_X509_BADCERT_FUTURE) {
++				reason = TLS_FAIL_NOT_YET_VALID;
++				errmsg = "certificate not yet valid";
++			}
++			if (*flags & MBEDTLS_X509_BADCERT_EXPIRED) {
++				reason = TLS_FAIL_EXPIRED;
++				errmsg = "certificate has expired";
++			}
++			if (*flags & MBEDTLS_X509_BADCERT_BAD_MD) {
++				reason = TLS_FAIL_BAD_CERTIFICATE;
++				errmsg = "certificate uses insecure algorithm";
++			}
++			tls_mbedtls_verify_fail_event(crt, depth, errmsg, reason);
++		}
++	  #if 0
++		/* ??? send (again) cert events for all certs in chain ???
++		 * (should already have been called for greater depths) */
++		/* tls_openssl.c:tls_verify_cb() sends cert events for all certs
++		 * in chain if certificate validation fails, but sends all events
++		 * with depth set to 0 (might be a bug) */
++		if (depth > 0) {
++			int pdepth = depth + 1;
++			for (mbedtls_x509_crt *pcrt; (pcrt = crt->next); ++pdepth) {
++				tls_mbedtls_verify_cert_event(conn, pcrt, pdepth);
++			}
++		}
++	  #endif
++		/*(do not preserve subject if verification failed but was optional)*/
++		if (depth == 0 && conn->peer_subject) {
++			os_free(conn->peer_subject);
++			conn->peer_subject = NULL;
++		}
++	}
++	else if (depth == 0) {
++		struct tls_config *init_conf = &tls_ctx_global.init_conf;
++		if (tls_conf->ca_cert_probe) {
++			/* reject server certificate on probe-only run */
++			*flags |= MBEDTLS_X509_BADCERT_OTHER;
++			tls_mbedtls_verify_fail_event(crt, depth,
++			                              "server chain probe",
++			                              TLS_FAIL_SERVER_CHAIN_PROBE);
++		}
++		else if (init_conf->event_cb) {
++			/* ??? send event as soon as depth == 0 is verified ???
++			 * What about rest of chain?
++			 * Follows tls_mbedtls.c behavior: */
++			init_conf->event_cb(init_conf->cb_ctx,
++			                    TLS_CERT_CHAIN_SUCCESS, NULL);
++		}
++	}
++
++	return 0;
++}
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 4331782d8..e1a447333 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -979,6 +979,9 @@ struct wpa_driver_associate_params {
+ 	 * responsible for selecting with which BSS to associate. */
+ 	const u8 *bssid;
+ 
++	unsigned char rates[WLAN_SUPP_RATES_MAX];
++	int mcast_rate;
++
+ 	/**
+ 	 * bssid_hint - BSSID of a proposed AP
+ 	 *
+@@ -1886,6 +1889,7 @@ struct wpa_driver_mesh_join_params {
+ #define WPA_DRIVER_MESH_FLAG_AMPE	0x00000008
+ 	unsigned int flags;
+ 	bool handle_dfs;
++	int mcast_rate;
+ };
+ 
+ struct wpa_driver_set_key_params {
+@@ -2357,6 +2361,9 @@ struct wpa_driver_capa {
+ 	/** Maximum number of iterations in a single scan plan */
+ 	u32 max_sched_scan_plan_iterations;
+ 
++	/** Maximum number of extra IE bytes for scans */
++	u16 max_scan_ie_len;
++
+ 	/** Whether sched_scan (offloaded scanning) is supported */
+ 	int sched_scan_supported;
+ 
+@@ -3887,6 +3894,25 @@ struct wpa_driver_ops {
+ 	int (*if_remove)(void *priv, enum wpa_driver_if_type type,
+ 			 const char *ifname);
+ 
++	/**
++	 * if_rename - Rename a virtual interface
++	 * @priv: Private driver interface data
++	 * @type: Interface type
++	 * @ifname: Interface name of the virtual interface to be renamed
++	 *	    (NULL when renaming the AP BSS interface)
++	 * @new_name: New interface name of the virtual interface
++	 * Returns: 0 on success, -1 on failure
++	 */
++	int (*if_rename)(void *priv, enum wpa_driver_if_type type,
++			 const char *ifname, const char *new_name);
++
++	/**
++	 * set_first_bss - Make a virtual interface the first (primary) bss
++	 * @priv: Private driver interface data
++	 * Returns: 0 on success, -1 on failure
++	 */
++	int (*set_first_bss)(void *priv);
++
+ 	/**
+ 	 * set_sta_vlan - Bind a station into a specific interface (AP only)
+ 	 * @priv: Private driver interface data
+@@ -3989,7 +4015,7 @@ struct wpa_driver_ops {
+ 	 * Returns: 0 on success, -1 on failure
+ 	 */
+ 	int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val,
+-			   const char *bridge_ifname, char *ifname_wds);
++			   const char *bridge_ifname, const char *ifname_wds);
+ 
+ 	/**
+ 	 * send_action - Transmit an Action frame
+@@ -4291,7 +4317,7 @@ struct wpa_driver_ops {
+ 	 * Returns: 0 on success, negative (<0) on failure
+ 	 */
+ 	int (*br_set_net_param)(void *priv, enum drv_br_net_param param,
+-				unsigned int val);
++				const char *ifname, unsigned int val);
+ 
+ 	/**
+ 	 * get_wowlan - Get wake-on-wireless status
+@@ -6588,6 +6614,7 @@ union wpa_event_data {
+ 
+ 	/**
+ 	 * struct ch_switch
++	 * @count: Count until channel switch activates
+ 	 * @freq: Frequency of new channel in MHz
+ 	 * @ht_enabled: Whether this is an HT channel
+ 	 * @ch_offset: Secondary channel offset
+@@ -6598,6 +6625,7 @@ union wpa_event_data {
+ 	 * @punct_bitmap: Puncturing bitmap
+ 	 */
+ 	struct ch_switch {
++		int count;
+ 		int freq;
+ 		int ht_enabled;
+ 		int ch_offset;
+@@ -6846,8 +6874,8 @@ union wpa_event_data {
+  * Driver wrapper code should call this function whenever an event is received
+  * from the driver.
+  */
+-void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+-			  union wpa_event_data *data);
++extern void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
++				    union wpa_event_data *data);
+ 
+ /**
+  * wpa_supplicant_event_global - Report a driver event for wpa_supplicant
+@@ -6859,7 +6887,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+  * Same as wpa_supplicant_event(), but we search for the interface in
+  * wpa_global.
+  */
+-void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
++extern void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
+ 				 union wpa_event_data *data);
+ 
+ /*
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 39f58ff83..a70eaae38 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -75,6 +75,16 @@ enum nlmsgerr_attrs {
+ 
+ #endif /* ANDROID */
+ 
++static void handle_nl_debug_hook(struct nl_msg *msg, int tx)
++{
++	const struct nlmsghdr *nlh;
++
++	if (!wpa_netlink_hook)
++		return;
++
++	nlh = nlmsg_hdr(msg);
++	wpa_netlink_hook(tx, nlh, nlh->nlmsg_len);
++}
+ 
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+@@ -429,6 +439,11 @@ static int no_seq_check(struct nl_msg *msg, void *arg)
+ 	return NL_OK;
+ }
+ 
++static int debug_handler(struct nl_msg *msg, void *arg)
++{
++	handle_nl_debug_hook(msg, 0);
++	return NL_OK;
++}
+ 
+ static void nl80211_nlmsg_clear(struct nl_msg *msg)
+ {
+@@ -502,6 +517,8 @@ int send_and_recv(struct nl80211_global *global,
+ 	if (!msg)
+ 		return -ENOMEM;
+ 
++	handle_nl_debug_hook(msg, 1);
++
+ 	err.err = -ENOMEM;
+ 
+ 	s_nl_cb = nl_socket_get_cb(nl_handle);
+@@ -536,6 +553,7 @@ int send_and_recv(struct nl80211_global *global,
+ 	err.orig_msg = msg;
+ 	err.err_info = err_info;
+ 
++	nl_cb_set(cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
+ 	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
+ 	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err.err);
+ 	if (ack_handler_custom) {
+@@ -939,6 +957,7 @@ nl80211_get_wiphy_data_ap(struct i802_bss *bss)
+ 			os_free(w);
+ 			return NULL;
+ 		}
++		nl_cb_set(w->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
+ 		nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ 			  no_seq_check, NULL);
+ 		nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+@@ -1353,7 +1372,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
+ 		}
+ 		wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
+ 			   namebuf, ifname);
+-		if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
++		if (drv->first_bss->ifindex != ifi->ifi_index) {
+ 			wpa_printf(MSG_DEBUG,
+ 				   "nl80211: Not the main interface (%s) - do not indicate interface down",
+ 				   drv->first_bss->ifname);
+@@ -1389,7 +1408,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
+ 		}
+ 		wpa_printf(MSG_DEBUG, "nl80211: Interface up (%s/%s)",
+ 			   namebuf, ifname);
+-		if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
++		if (drv->first_bss->ifindex != ifi->ifi_index) {
+ 			wpa_printf(MSG_DEBUG,
+ 				   "nl80211: Not the main interface (%s) - do not indicate interface up",
+ 				   drv->first_bss->ifname);
+@@ -2035,6 +2054,7 @@ static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
+ 	genl_family_put(family);
+ 	nl_cache_free(cache);
+ 
++	nl_cb_set(global->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
+ 	nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ 		  no_seq_check, NULL);
+ 	nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+@@ -2205,6 +2225,7 @@ static int nl80211_init_bss(struct i802_bss *bss)
+ 	if (!bss->nl_cb)
+ 		return -1;
+ 
++	nl_cb_set(bss->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
+ 	nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ 		  no_seq_check, NULL);
+ 	nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+@@ -3083,7 +3104,7 @@ static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss,
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+ 	struct i802_link *link = nl80211_get_link(bss, link_id);
+ 
+-	if (!link->beacon_set)
++	if (!link || !link->beacon_set)
+ 		return 0;
+ 
+ 	wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
+@@ -5494,7 +5515,7 @@ static int nl80211_set_channel(struct i802_bss *bss,
+ 		   freq->he_enabled, freq->eht_enabled, freq->bandwidth,
+ 		   freq->center_freq1, freq->center_freq2);
+ 
+-	msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
++	msg = nl80211_bss_msg(bss, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
+ 			      NL80211_CMD_SET_WIPHY);
+ 	if (!msg || nl80211_put_freq_params(msg, freq) < 0) {
+ 		nlmsg_free(msg);
+@@ -6183,8 +6204,7 @@ static void nl80211_teardown_ap(struct i802_bss *bss)
+ 		nl80211_mgmt_unsubscribe(bss, "AP teardown");
+ 
+ 	nl80211_put_wiphy_data_ap(bss);
+-	if (bss->flink)
+-		bss->flink->beacon_set = 0;
++	wpa_driver_nl80211_del_beacon_all(bss);
+ }
+ 
+ 
+@@ -8414,24 +8434,14 @@ static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+ 
+ 
+ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
+-			    const char *bridge_ifname, char *ifname_wds)
++			    const char *bridge_ifname, const char *ifname_wds)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+-	char name[IFNAMSIZ + 1];
++	const char *name = ifname_wds; // Kept to reduce changes to the minimum
+ 	union wpa_event_data event;
+ 	int ret;
+ 
+-	ret = os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
+-	if (ret >= (int) sizeof(name))
+-		wpa_printf(MSG_WARNING,
+-			   "nl80211: WDS interface name was truncated");
+-	else if (ret < 0)
+-		return ret;
+-
+-	if (ifname_wds)
+-		os_strlcpy(ifname_wds, name, IFNAMSIZ + 1);
+-
+ 	wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
+ 		   " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
+ 	if (val) {
+@@ -8574,6 +8584,7 @@ static void *i802_init(struct hostapd_data *hapd,
+ 	char master_ifname[IFNAMSIZ];
+ 	int ifindex, br_ifindex = 0;
+ 	int br_added = 0;
++	int err;
+ 
+ 	bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
+ 					  params->global_priv, 1,
+@@ -8633,21 +8644,17 @@ static void *i802_init(struct hostapd_data *hapd,
+ 	    (params->num_bridge == 0 || !params->bridge[0]))
+ 		add_ifidx(drv, br_ifindex, drv->ifindex);
+ 
+-	if (bss->added_if_into_bridge || bss->already_in_bridge) {
+-		int err;
+-
+-		drv->rtnl_sk = nl_socket_alloc();
+-		if (drv->rtnl_sk == NULL) {
+-			wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
+-			goto failed;
+-		}
++	drv->rtnl_sk = nl_socket_alloc();
++	if (drv->rtnl_sk == NULL) {
++		wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
++		goto failed;
++	}
+ 
+-		err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
+-		if (err) {
+-			wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
+-				   nl_geterror(err));
+-			goto failed;
+-		}
++	err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
++	if (err) {
++		wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
++			   nl_geterror(err));
++		goto failed;
+ 	}
+ 
+ 	if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
+@@ -8998,8 +9005,6 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
+ 		wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context");
+ 		nl80211_teardown_ap(bss);
+ 		nl80211_remove_links(bss);
+-		if (!bss->added_if && !drv->first_bss->next)
+-			wpa_driver_nl80211_del_beacon_all(bss);
+ 		nl80211_destroy_bss(bss);
+ 		if (!bss->added_if)
+ 			i802_set_iface_flags(bss, 0);
+@@ -9016,6 +9021,50 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
+ 	return 0;
+ }
+ 
++static int wpa_driver_nl80211_if_rename(struct i802_bss *bss,
++					enum wpa_driver_if_type type,
++					const char *ifname, const char *new_name)
++{
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct ifinfomsg ifi = {
++		.ifi_family = AF_UNSPEC,
++		.ifi_index = bss->ifindex,
++	};
++	struct nl_msg *msg;
++	int res = -ENOMEM;
++
++	if (ifname)
++		ifi.ifi_index = if_nametoindex(ifname);
++
++	msg = nlmsg_alloc_simple(RTM_SETLINK, 0);
++	if (!msg)
++		return res;
++
++	if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
++		goto out;
++
++	if (nla_put_string(msg, IFLA_IFNAME, new_name))
++		goto out;
++
++	res = nl_send_auto_complete(drv->rtnl_sk, msg);
++	if (res < 0)
++		goto out;
++
++	res = nl_wait_for_ack(drv->rtnl_sk);
++	if (res) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Renaming device %s to %s failed: %s",
++			   ifname ? ifname : bss->ifname, new_name, nl_geterror(res));
++		goto out;
++	}
++
++	if (type == WPA_IF_AP_BSS && !ifname)
++		os_strlcpy(bss->ifname, new_name, sizeof(bss->ifname));
++
++out:
++	nlmsg_free(msg);
++	return res;
++}
+ 
+ static int cookie_handler(struct nl_msg *msg, void *arg)
+ {
+@@ -10807,6 +10856,37 @@ static bool nl80211_is_drv_shared(void *priv, void *bss_ctx)
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ 
++static int driver_nl80211_if_rename(void *priv, enum wpa_driver_if_type type,
++				    const char *ifname, const char *new_name)
++{
++	struct i802_bss *bss = priv;
++	return wpa_driver_nl80211_if_rename(bss, type, ifname, new_name);
++}
++
++
++static int driver_nl80211_set_first_bss(void *priv)
++{
++	struct i802_bss *bss = priv, *tbss;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++
++	if (drv->first_bss == bss)
++		return 0;
++
++	for (tbss = drv->first_bss; tbss; tbss = tbss->next) {
++		if (tbss->next != bss)
++			continue;
++
++		tbss->next = bss->next;
++		bss->next = drv->first_bss;
++		drv->first_bss = bss;
++		drv->ctx = bss->ctx;
++		return 0;
++	}
++
++	return -1;
++}
++
++
+ static int driver_nl80211_send_mlme(void *priv, const u8 *data,
+ 				    size_t data_len, int noack,
+ 				    unsigned int freq,
+@@ -11309,6 +11389,10 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ 	if (ret)
+ 		goto error;
+ 
++	if (drv->nlmode == NL80211_IFTYPE_MESH_POINT) {
++		nla_put_flag(msg, NL80211_ATTR_HANDLE_DFS);
++	}
++
+ 	/* beacon_csa params */
+ 	beacon_csa = nla_nest_start(msg, NL80211_ATTR_CSA_IES);
+ 	if (!beacon_csa)
+@@ -11983,6 +12067,18 @@ static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id,
+ }
+ 
+ 
++static int nl80211_put_mcast_rate(struct nl_msg *msg, int mcast_rate)
++{
++	if (mcast_rate > 0) {
++		wpa_printf(MSG_DEBUG, "  * mcast_rate=%.1f",
++			   (double)mcast_rate / 10);
++		return nla_put_u32(msg, NL80211_ATTR_MCAST_RATE, mcast_rate);
++	}
++
++	return 0;
++}
++
++
+ static int nl80211_put_mesh_config(struct nl_msg *msg,
+ 				   struct wpa_driver_mesh_bss_params *params)
+ {
+@@ -12044,6 +12140,7 @@ static int nl80211_join_mesh(struct i802_bss *bss,
+ 	    nl80211_put_basic_rates(msg, params->basic_rates) ||
+ 	    nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
+ 	    nl80211_put_beacon_int(msg, params->beacon_int) ||
++	    nl80211_put_mcast_rate(msg, params->mcast_rate) ||
+ 	    nl80211_put_dtim_period(msg, params->dtim_period))
+ 		goto fail;
+ 
+@@ -12397,7 +12494,7 @@ static const char * drv_br_net_param_str(enum drv_br_net_param param)
+ 
+ 
+ static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
+-				       unsigned int val)
++				       const char *ifname, unsigned int val)
+ {
+ 	struct i802_bss *bss = priv;
+ 	char path[128];
+@@ -12423,8 +12520,11 @@ static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
+ 			return -EINVAL;
+ 	}
+ 
++	if (!ifname)
++		ifname = bss->brname;
++
+ 	os_snprintf(path, sizeof(path), "/proc/sys/net/ipv%d/conf/%s/%s",
+-		    ip_version, bss->brname, param_txt);
++		    ip_version, ifname, param_txt);
+ 
+ set_val:
+ 	if (linux_write_system_file(path, val))
+@@ -14027,6 +14127,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.set_acl = wpa_driver_nl80211_set_acl,
+ 	.if_add = wpa_driver_nl80211_if_add,
+ 	.if_remove = driver_nl80211_if_remove,
++	.if_rename = driver_nl80211_if_rename,
++	.set_first_bss = driver_nl80211_set_first_bss,
+ 	.send_mlme = driver_nl80211_send_mlme,
+ 	.get_hw_feature_data = nl80211_get_hw_feature_data,
+ 	.sta_add = wpa_driver_nl80211_sta_add,
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 26c1f4140..d5ba66b10 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -976,6 +976,10 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 			nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]);
+ 	}
+ 
++	if (tb[NL80211_ATTR_MAX_SCAN_IE_LEN])
++		capa->max_scan_ie_len =
++			nla_get_u16(tb[NL80211_ATTR_MAX_SCAN_IE_LEN]);
++
+ 	if (tb[NL80211_ATTR_MAX_MATCH_SETS])
+ 		capa->max_match_sets =
+ 			nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index aee815e97..768c72905 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -1196,6 +1196,7 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
+ 				 struct nlattr *bw, struct nlattr *cf1,
+ 				 struct nlattr *cf2,
+ 				 struct nlattr *punct_bitmap,
++				 struct nlattr *count,
+ 				 int finished)
+ {
+ 	struct i802_bss *bss;
+@@ -1259,6 +1260,8 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
+ 		data.ch_switch.cf1 = nla_get_u32(cf1);
+ 	if (cf2)
+ 		data.ch_switch.cf2 = nla_get_u32(cf2);
++	if (count)
++		data.ch_switch.count = nla_get_u32(count);
+ 
+ 	if (link)
+ 		data.ch_switch.link_id = nla_get_u8(link);
+@@ -3999,6 +4002,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
+ 				     tb[NL80211_ATTR_CENTER_FREQ1],
+ 				     tb[NL80211_ATTR_CENTER_FREQ2],
+ 				     tb[NL80211_ATTR_PUNCT_BITMAP],
++				     tb[NL80211_ATTR_CH_SWITCH_COUNT],
+ 				     0);
+ 		break;
+ 	case NL80211_CMD_CH_SWITCH_NOTIFY:
+@@ -4011,6 +4015,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
+ 				     tb[NL80211_ATTR_CENTER_FREQ1],
+ 				     tb[NL80211_ATTR_CENTER_FREQ2],
+ 				     tb[NL80211_ATTR_PUNCT_BITMAP],
++				     NULL,
+ 				     1);
+ 		break;
+ 	case NL80211_CMD_DISCONNECT:
+diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
+index b055e684a..a8ea8f2cf 100644
+--- a/src/drivers/driver_nl80211_scan.c
++++ b/src/drivers/driver_nl80211_scan.c
+@@ -221,7 +221,7 @@ nl80211_scan_common(struct i802_bss *bss, u8 cmd,
+ 		wpa_printf(MSG_DEBUG, "nl80211: Passive scan requested");
+ 	}
+ 
+-	if (params->extra_ies) {
++	if (params->extra_ies && drv->capa.max_scan_ie_len >= params->extra_ies_len) {
+ 		wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
+ 			    params->extra_ies, params->extra_ies_len);
+ 		if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
+diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c
+index e95df6ddb..9071da3cf 100644
+--- a/src/drivers/drivers.c
++++ b/src/drivers/drivers.c
+@@ -10,6 +10,10 @@
+ #include "utils/common.h"
+ #include "driver.h"
+ 
++void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
++			     union wpa_event_data *data);
++void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
++			     union wpa_event_data *data);
+ 
+ const struct wpa_driver_ops *const wpa_drivers[] =
+ {
+diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
+index a03d4a034..8da44d9f5 100644
+--- a/src/drivers/drivers.mak
++++ b/src/drivers/drivers.mak
+@@ -54,7 +54,6 @@ NEED_SME=y
+ NEED_AP_MLME=y
+ NEED_NETLINK=y
+ NEED_LINUX_IOCTL=y
+-NEED_RFKILL=y
+ NEED_RADIOTAP=y
+ NEED_LIBNL=y
+ endif
+@@ -111,7 +110,6 @@ DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
+ CONFIG_WIRELESS_EXTENSION=y
+ NEED_NETLINK=y
+ NEED_LINUX_IOCTL=y
+-NEED_RFKILL=y
+ endif
+ 
+ ifdef CONFIG_DRIVER_NDIS
+@@ -137,7 +135,6 @@ endif
+ ifdef CONFIG_WIRELESS_EXTENSION
+ DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
+ DRV_WPA_OBJS += ../src/drivers/driver_wext.o
+-NEED_RFKILL=y
+ endif
+ 
+ ifdef NEED_NETLINK
+@@ -146,6 +143,7 @@ endif
+ 
+ ifdef NEED_RFKILL
+ DRV_OBJS += ../src/drivers/rfkill.o
++DRV_WPA_CFLAGS += -DCONFIG_RFKILL
+ endif
+ 
+ ifdef NEED_RADIOTAP
+diff --git a/src/drivers/rfkill.h b/src/drivers/rfkill.h
+index 0412ac330..e27565375 100644
+--- a/src/drivers/rfkill.h
++++ b/src/drivers/rfkill.h
+@@ -18,8 +18,24 @@ struct rfkill_config {
+ 	void (*unblocked_cb)(void *ctx);
+ };
+ 
++#ifdef CONFIG_RFKILL
+ struct rfkill_data * rfkill_init(struct rfkill_config *cfg);
+ void rfkill_deinit(struct rfkill_data *rfkill);
+ int rfkill_is_blocked(struct rfkill_data *rfkill);
++#else
++static inline struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
++{
++	return (void *) 1;
++}
++
++static inline void rfkill_deinit(struct rfkill_data *rfkill)
++{
++}
++
++static inline int rfkill_is_blocked(struct rfkill_data *rfkill)
++{
++	return 0;
++}
++#endif
+ 
+ #endif /* RFKILL_H */
+diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
+index 2a7f36170..8e8903051 100644
+--- a/src/radius/radius_client.c
++++ b/src/radius/radius_client.c
+@@ -165,6 +165,8 @@ struct radius_client_data {
+ 	 */
+ 	void *ctx;
+ 
++	struct hostapd_ip_addr local_ip;
++
+ 	/**
+ 	 * conf - RADIUS client configuration (list of RADIUS servers to use)
+ 	 */
+@@ -818,6 +820,30 @@ static void radius_close_acct_socket(struct radius_client_data *radius)
+ }
+ 
+ 
++/**
++ * radius_client_send - Get local address for the RADIUS auth socket
++ * @radius: RADIUS client context from radius_client_init()
++ * @addr: pointer to store the address
++ *
++ * This function returns the local address for the connection to the RADIUS
++ * auth server. It also opens the socket if it's not available yet.
++ */
++int radius_client_get_local_addr(struct radius_client_data *radius,
++				 struct hostapd_ip_addr *addr)
++{
++	struct hostapd_radius_servers *conf = radius->conf;
++
++	if (conf->auth_server && radius->auth_sock < 0)
++		radius_client_init_auth(radius);
++
++	if (radius->auth_sock < 0)
++		return -1;
++
++	memcpy(addr, &radius->local_ip, sizeof(*addr));
++
++	return 0;
++}
++
+ /**
+  * radius_client_send - Send a RADIUS request
+  * @radius: RADIUS client context from radius_client_init()
+@@ -1711,6 +1737,10 @@ radius_change_server(struct radius_client_data *radius,
+ 			wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
+ 				   inet_ntoa(claddr.sin_addr),
+ 				   ntohs(claddr.sin_port));
++			if (auth) {
++				radius->local_ip.af = AF_INET;
++				radius->local_ip.u.v4 = claddr.sin_addr;
++			}
+ 		}
+ 		break;
+ #ifdef CONFIG_IPV6
+@@ -1722,6 +1752,10 @@ radius_change_server(struct radius_client_data *radius,
+ 				   inet_ntop(AF_INET6, &claddr6.sin6_addr,
+ 					     abuf, sizeof(abuf)),
+ 				   ntohs(claddr6.sin6_port));
++			if (auth) {
++				radius->local_ip.af = AF_INET6;
++				radius->local_ip.u.v6 = claddr6.sin6_addr;
++			}
+ 		}
+ 		break;
+ 	}
+diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h
+index db40637ea..9a89b0382 100644
+--- a/src/radius/radius_client.h
++++ b/src/radius/radius_client.h
+@@ -274,6 +274,8 @@ int radius_client_register(struct radius_client_data *radius,
+ void radius_client_set_interim_error_cb(struct radius_client_data *radius,
+ 					void (*cb)(const u8 *addr, void *ctx),
+ 					void *ctx);
++int radius_client_get_local_addr(struct radius_client_data *radius,
++				 struct hostapd_ip_addr * addr);
+ int radius_client_send(struct radius_client_data *radius,
+ 		       struct radius_msg *msg,
+ 		       RadiusType msg_type, const u8 *addr);
+diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
+index 8d7c9b4c4..01913b08f 100644
+--- a/src/radius/radius_das.c
++++ b/src/radius/radius_das.c
+@@ -12,13 +12,26 @@
+ #include "utils/common.h"
+ #include "utils/eloop.h"
+ #include "utils/ip_addr.h"
++#include "utils/list.h"
+ #include "radius.h"
+ #include "radius_das.h"
+ 
+ 
+-struct radius_das_data {
++static struct dl_list das_ports = DL_LIST_HEAD_INIT(das_ports);
++
++struct radius_das_port {
++	struct dl_list list;
++	struct dl_list das_data;
++
++	int port;
+ 	int sock;
++};
++
++struct radius_das_data {
++	struct dl_list list;
++	struct radius_das_port *port;
+ 	u8 *shared_secret;
++	u8 *nas_identifier;
+ 	size_t shared_secret_len;
+ 	struct hostapd_ip_addr client_addr;
+ 	unsigned int time_window;
+@@ -388,56 +401,17 @@ fail:
+ }
+ 
+ 
+-static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
++static void
++radius_das_receive_msg(struct radius_das_data *das, struct radius_msg *msg,
++		       struct sockaddr *from, socklen_t fromlen,
++		       char *abuf, int from_port)
+ {
+-	struct radius_das_data *das = eloop_ctx;
+-	u8 buf[1500];
+-	union {
+-		struct sockaddr_storage ss;
+-		struct sockaddr_in sin;
+-#ifdef CONFIG_IPV6
+-		struct sockaddr_in6 sin6;
+-#endif /* CONFIG_IPV6 */
+-	} from;
+-	char abuf[50];
+-	int from_port = 0;
+-	socklen_t fromlen;
+-	int len;
+-	struct radius_msg *msg, *reply = NULL;
++	struct radius_msg *reply = NULL;
+ 	struct radius_hdr *hdr;
+ 	struct wpabuf *rbuf;
++	struct os_time now;
+ 	u32 val;
+ 	int res;
+-	struct os_time now;
+-
+-	fromlen = sizeof(from);
+-	len = recvfrom(sock, buf, sizeof(buf), 0,
+-		       (struct sockaddr *) &from.ss, &fromlen);
+-	if (len < 0) {
+-		wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
+-		return;
+-	}
+-
+-	os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
+-	from_port = ntohs(from.sin.sin_port);
+-
+-	wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
+-		   len, abuf, from_port);
+-	if (das->client_addr.u.v4.s_addr &&
+-	    das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
+-		wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
+-		return;
+-	}
+-
+-	msg = radius_msg_parse(buf, len);
+-	if (msg == NULL) {
+-		wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
+-			   "from %s:%d failed", abuf, from_port);
+-		return;
+-	}
+-
+-	if (wpa_debug_level <= MSG_MSGDUMP)
+-		radius_msg_dump(msg);
+ 
+ 	if (radius_msg_verify_das_req(msg, das->shared_secret,
+ 				       das->shared_secret_len,
+@@ -504,9 +478,8 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
+ 			radius_msg_dump(reply);
+ 
+ 		rbuf = radius_msg_get_buf(reply);
+-		res = sendto(das->sock, wpabuf_head(rbuf),
+-			     wpabuf_len(rbuf), 0,
+-			     (struct sockaddr *) &from.ss, fromlen);
++		res = sendto(das->port->sock, wpabuf_head(rbuf),
++			     wpabuf_len(rbuf), 0, from, fromlen);
+ 		if (res < 0) {
+ 			wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
+ 				   abuf, from_port, strerror(errno));
+@@ -518,6 +491,72 @@ fail:
+ 	radius_msg_free(reply);
+ }
+ 
++static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
++{
++	struct radius_das_port *p = eloop_ctx;
++	struct radius_das_data *das;
++	u8 buf[1500];
++	union {
++		struct sockaddr_storage ss;
++		struct sockaddr_in sin;
++#ifdef CONFIG_IPV6
++		struct sockaddr_in6 sin6;
++#endif /* CONFIG_IPV6 */
++	} from;
++	struct radius_msg *msg;
++	size_t nasid_len = 0;
++	u8 *nasid_buf = NULL;
++	char abuf[50];
++	int from_port = 0;
++	socklen_t fromlen;
++	int found = 0;
++	int len;
++
++	fromlen = sizeof(from);
++	len = recvfrom(sock, buf, sizeof(buf), 0,
++		       (struct sockaddr *) &from.ss, &fromlen);
++	if (len < 0) {
++		wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
++		return;
++	}
++
++	os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
++	from_port = ntohs(from.sin.sin_port);
++
++	msg = radius_msg_parse(buf, len);
++	if (msg == NULL) {
++		wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
++			   "from %s:%d failed", abuf, from_port);
++		return;
++	}
++
++	wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
++		   len, abuf, from_port);
++
++	if (wpa_debug_level <= MSG_MSGDUMP)
++		radius_msg_dump(msg);
++
++	radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
++				&nasid_buf, &nasid_len, NULL);
++	dl_list_for_each(das, &p->das_data, struct radius_das_data, list) {
++		if (das->client_addr.u.v4.s_addr &&
++		    das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr)
++			continue;
++
++		if (das->nas_identifier && nasid_buf &&
++		    (nasid_len != os_strlen(das->nas_identifier) ||
++		     os_memcmp(das->nas_identifier, nasid_buf, nasid_len) != 0))
++			continue;
++
++		found = 1;
++		radius_das_receive_msg(das, msg, (struct sockaddr *)&from.ss,
++				       fromlen, abuf, from_port);
++	}
++
++	if (!found)
++		wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
++}
++
+ 
+ static int radius_das_open_socket(int port)
+ {
+@@ -543,6 +582,49 @@ static int radius_das_open_socket(int port)
+ }
+ 
+ 
++static struct radius_das_port *
++radius_das_open_port(int port)
++{
++	struct radius_das_port *p;
++
++	dl_list_for_each(p, &das_ports, struct radius_das_port, list) {
++		if (p->port == port)
++			return p;
++	}
++
++	p = os_zalloc(sizeof(*p));
++	if (p == NULL)
++		return NULL;
++
++	dl_list_init(&p->das_data);
++	p->port = port;
++	p->sock = radius_das_open_socket(port);
++	if (p->sock < 0)
++		goto free_port;
++
++	if (eloop_register_read_sock(p->sock, radius_das_receive, p, NULL))
++		goto close_port;
++
++	dl_list_add(&das_ports, &p->list);
++
++	return p;
++
++close_port:
++	close(p->sock);
++free_port:
++	os_free(p);
++
++	return NULL;
++}
++
++static void radius_das_close_port(struct radius_das_port *p)
++{
++	dl_list_del(&p->list);
++	eloop_unregister_read_sock(p->sock);
++	close(p->sock);
++	free(p);
++}
++
+ struct radius_das_data *
+ radius_das_init(struct radius_das_conf *conf)
+ {
+@@ -563,6 +645,8 @@ radius_das_init(struct radius_das_conf *conf)
+ 	das->ctx = conf->ctx;
+ 	das->disconnect = conf->disconnect;
+ 	das->coa = conf->coa;
++	if (conf->nas_identifier)
++		das->nas_identifier = os_strdup(conf->nas_identifier);
+ 
+ 	os_memcpy(&das->client_addr, conf->client_addr,
+ 		  sizeof(das->client_addr));
+@@ -575,19 +659,15 @@ radius_das_init(struct radius_das_conf *conf)
+ 	}
+ 	das->shared_secret_len = conf->shared_secret_len;
+ 
+-	das->sock = radius_das_open_socket(conf->port);
+-	if (das->sock < 0) {
++	das->port = radius_das_open_port(conf->port);
++	if (!das->port) {
+ 		wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
+ 			   "DAS");
+ 		radius_das_deinit(das);
+ 		return NULL;
+ 	}
+ 
+-	if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
+-	{
+-		radius_das_deinit(das);
+-		return NULL;
+-	}
++	dl_list_add(&das->port->das_data, &das->list);
+ 
+ 	return das;
+ }
+@@ -598,11 +678,14 @@ void radius_das_deinit(struct radius_das_data *das)
+ 	if (das == NULL)
+ 		return;
+ 
+-	if (das->sock >= 0) {
+-		eloop_unregister_read_sock(das->sock);
+-		close(das->sock);
++	if (das->port) {
++		dl_list_del(&das->list);
++
++		if (dl_list_empty(&das->port->das_data))
++			radius_das_close_port(das->port);
+ 	}
+ 
++	os_free(das->nas_identifier);
+ 	os_free(das->shared_secret);
+ 	os_free(das);
+ }
+diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h
+index 233d662f6..80dc13fc8 100644
+--- a/src/radius/radius_das.h
++++ b/src/radius/radius_das.h
+@@ -44,6 +44,7 @@ struct radius_das_attrs {
+ struct radius_das_conf {
+ 	int port;
+ 	const u8 *shared_secret;
++	const u8 *nas_identifier;
+ 	size_t shared_secret_len;
+ 	const struct hostapd_ip_addr *client_addr;
+ 	unsigned int time_window;
+diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
+index fa3691548..95a1cb994 100644
+--- 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(struct radius_server_data *data,
+ 	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_server_data *data, const char *keyname)
+ }
+ #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 radius_server_data *data,
+ 		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);
+ 
+@@ -1123,11 +1160,10 @@ radius_server_encapsulate_eap(struct radius_server_data *data,
+ 	}
+ 
+ 	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;
+@@ -1221,11 +1257,10 @@ radius_server_macacl(struct radius_server_data *data,
+ 	}
+ 
+ 	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;
+@@ -2527,7 +2562,7 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity,
+ 	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/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
+index 52a4c7442..ce1aa60a9 100644
+--- a/src/rsn_supp/wpa.c
++++ b/src/rsn_supp/wpa.c
+@@ -4153,6 +4153,8 @@ static u32 wpa_key_mgmt_suite(struct wpa_sm *sm)
+ }
+ 
+ 
++#ifdef CONFIG_CTRL_IFACE_MIB
++
+ #define RSN_SUITE "%02x-%02x-%02x-%d"
+ #define RSN_SUITE_ARG(s) \
+ ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
+@@ -4234,6 +4236,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
+ 
+ 	return (int) len;
+ }
++#endif
+ #endif /* CONFIG_CTRL_IFACE */
+ 
+ 
+diff --git a/src/tls/Makefile b/src/tls/Makefile
+index c84fbe859..e974a41f0 100644
+--- a/src/tls/Makefile
++++ b/src/tls/Makefile
+@@ -1,3 +1,10 @@
++LIB_OBJS= asn1.o
++
++ifneq ($(CONFIG_TLS),gnutls)
++ifneq ($(CONFIG_TLS),mbedtls)
++ifneq ($(CONFIG_TLS),openssl)
++ifneq ($(CONFIG_TLS),wolfssl)
++
+ CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+ CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+ CFLAGS += -DCONFIG_TLSV11
+@@ -21,5 +28,9 @@ LIB_OBJS= \
+ 	tlsv1_server_read.o \
+ 	tlsv1_server_write.o \
+ 	x509v3.o
++endif
++endif
++endif
++endif
+ 
+ include ../lib.rules
+diff --git a/src/utils/eloop.c b/src/utils/eloop.c
+index 00b0beff0..50dd1beda 100644
+--- 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)
+ {
+diff --git a/src/utils/eloop.h b/src/utils/eloop.h
+index 04ee6d183..5452ea589 100644
+--- a/src/utils/eloop.h
++++ b/src/utils/eloop.h
+@@ -65,6 +65,9 @@ typedef void (*eloop_timeout_handler)(void *eloop_ctx, void *user_ctx);
+  */
+ 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 sig, void *signal_ctx);
+  */
+ 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_signal_handler handler,
+  */
+ int eloop_sock_requeue(void);
+ 
++void eloop_add_uloop(void);
++
+ /**
+  * eloop_run - Start the event loop
+  *
+diff --git a/src/utils/uloop.c b/src/utils/uloop.c
+new file mode 100644
+index 000000000..c0d26db93
+--- /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/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
+index 7f3dd185f..627575e39 100644
+--- a/src/utils/wpa_debug.c
++++ b/src/utils/wpa_debug.c
+@@ -26,6 +26,10 @@ static FILE *wpa_debug_tracing_file = NULL;
+ #define WPAS_TRACE_PFX "wpas <%d>: "
+ #endif /* CONFIG_DEBUG_LINUX_TRACING */
+ 
++void (*wpa_printf_hook)(int level, const char *fmt, va_list ap);
++void (*wpa_hexdump_hook)(int level, const char *title, const void *buf,
++			 size_t len);
++void (*wpa_netlink_hook)(int tx, const void *data, size_t len);
+ 
+ int wpa_debug_level = MSG_INFO;
+ int wpa_debug_show_keys = 0;
+@@ -206,10 +210,16 @@ void wpa_debug_close_linux_tracing(void)
+  *
+  * Note: New line '\n' is added to the end of the text when printing to stdout.
+  */
+-void wpa_printf(int level, const char *fmt, ...)
++void _wpa_printf(int level, const char *fmt, ...)
+ {
+ 	va_list ap;
+ 
++	if (wpa_printf_hook) {
++		va_start(ap, fmt);
++		wpa_printf_hook(level, fmt, ap);
++		va_end(ap);
++	}
++
+ 	if (level >= wpa_debug_level) {
+ #ifdef CONFIG_ANDROID_LOG
+ 		va_start(ap, fmt);
+@@ -255,11 +265,14 @@ void wpa_printf(int level, const char *fmt, ...)
+ }
+ 
+ 
+-static void _wpa_hexdump(int level, const char *title, const u8 *buf,
++void _wpa_hexdump(int level, const char *title, const u8 *buf,
+ 			 size_t len, int show, int only_syslog)
+ {
+ 	size_t i;
+ 
++	if (wpa_hexdump_hook)
++		wpa_hexdump_hook(level, title, buf, len);
++
+ #ifdef CONFIG_DEBUG_LINUX_TRACING
+ 	if (wpa_debug_tracing_file != NULL) {
+ 		fprintf(wpa_debug_tracing_file,
+@@ -382,19 +395,7 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf,
+ #endif /* CONFIG_ANDROID_LOG */
+ }
+ 
+-void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
+-{
+-	_wpa_hexdump(level, title, buf, len, 1, 0);
+-}
+-
+-
+-void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len)
+-{
+-	_wpa_hexdump(level, title, buf, len, wpa_debug_show_keys, 0);
+-}
+-
+-
+-static void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
++void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
+ 			       size_t len, int show)
+ {
+ 	size_t i, llen;
+@@ -507,20 +508,6 @@ file_done:
+ }
+ 
+ 
+-void wpa_hexdump_ascii(int level, const char *title, const void *buf,
+-		       size_t len)
+-{
+-	_wpa_hexdump_ascii(level, title, buf, len, 1);
+-}
+-
+-
+-void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
+-			   size_t len)
+-{
+-	_wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
+-}
+-
+-
+ #ifdef CONFIG_DEBUG_FILE
+ static char *last_path = NULL;
+ #endif /* CONFIG_DEBUG_FILE */
+@@ -644,7 +631,7 @@ void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func)
+ }
+ 
+ 
+-void wpa_msg(void *ctx, int level, const char *fmt, ...)
++void _wpa_msg(void *ctx, int level, const char *fmt, ...)
+ {
+ 	va_list ap;
+ 	char *buf;
+@@ -682,7 +669,7 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...)
+ }
+ 
+ 
+-void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
++void _wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
+ {
+ 	va_list ap;
+ 	char *buf;
+diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
+index 4c02ad3c7..854520bfe 100644
+--- a/src/utils/wpa_debug.h
++++ b/src/utils/wpa_debug.h
+@@ -11,6 +11,10 @@
+ 
+ #include "wpabuf.h"
+ 
++extern void (*wpa_printf_hook)(int level, const char *fmt, va_list ap);
++extern void (*wpa_hexdump_hook)(int level, const char *title,
++				const void *buf, size_t len);
++extern void (*wpa_netlink_hook)(int tx, const void *data, size_t len);
+ extern int wpa_debug_level;
+ extern int wpa_debug_show_keys;
+ extern int wpa_debug_timestamp;
+@@ -51,6 +55,17 @@ void wpa_debug_close_file(void);
+ void wpa_debug_setup_stdout(void);
+ void wpa_debug_stop_log(void);
+ 
++/* internal */
++void _wpa_hexdump(int level, const char *title, const u8 *buf,
++		  size_t len, int show, int only_syslog);
++void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
++			size_t len, int show);
++extern int wpa_debug_show_keys;
++
++#ifndef CONFIG_MSG_MIN_PRIORITY
++#define CONFIG_MSG_MIN_PRIORITY 0
++#endif
++
+ /**
+  * wpa_debug_printf_timestamp - Print timestamp for debug output
+  *
+@@ -71,9 +86,15 @@ void wpa_debug_print_timestamp(void);
+  *
+  * Note: New line '\n' is added to the end of the text when printing to stdout.
+  */
+-void wpa_printf(int level, const char *fmt, ...)
++void _wpa_printf(int level, const char *fmt, ...)
+ PRINTF_FORMAT(2, 3);
+ 
++#define wpa_printf(level, ...)						\
++	do {								\
++		if (level >= CONFIG_MSG_MIN_PRIORITY)			\
++			_wpa_printf(level, __VA_ARGS__);		\
++	} while(0)
++
+ /**
+  * wpa_hexdump - conditional hex dump
+  * @level: priority level (MSG_*) of the message
+@@ -85,7 +106,13 @@ PRINTF_FORMAT(2, 3);
+  * output may be directed to stdout, stderr, and/or syslog based on
+  * configuration. The contents of buf is printed out has hex dump.
+  */
+-void wpa_hexdump(int level, const char *title, const void *buf, size_t len);
++static inline void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
++{
++	if (level < CONFIG_MSG_MIN_PRIORITY)
++		return;
++
++	_wpa_hexdump(level, title, buf, len, 1, 1);
++}
+ 
+ static inline void wpa_hexdump_buf(int level, const char *title,
+ 				   const struct wpabuf *buf)
+@@ -107,7 +134,13 @@ static inline void wpa_hexdump_buf(int level, const char *title,
+  * like wpa_hexdump(), but by default, does not include secret keys (passwords,
+  * etc.) in debug output.
+  */
+-void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len);
++static inline void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len)
++{
++	if (level < CONFIG_MSG_MIN_PRIORITY)
++		return;
++
++	_wpa_hexdump(level, title, buf, len, wpa_debug_show_keys, 1);
++}
+ 
+ static inline void wpa_hexdump_buf_key(int level, const char *title,
+ 				       const struct wpabuf *buf)
+@@ -129,8 +162,14 @@ static inline void wpa_hexdump_buf_key(int level, const char *title,
+  * the hex numbers and ASCII characters (for printable range) are shown. 16
+  * bytes per line will be shown.
+  */
+-void wpa_hexdump_ascii(int level, const char *title, const void *buf,
+-		       size_t len);
++static inline void wpa_hexdump_ascii(int level, const char *title,
++				     const u8 *buf, size_t len)
++{
++	if (level < CONFIG_MSG_MIN_PRIORITY)
++		return;
++
++	_wpa_hexdump_ascii(level, title, buf, len, 1);
++}
+ 
+ /**
+  * wpa_hexdump_ascii_key - conditional hex dump, hide keys
+@@ -146,8 +185,14 @@ void wpa_hexdump_ascii(int level, const char *title, const void *buf,
+  * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by
+  * default, does not include secret keys (passwords, etc.) in debug output.
+  */
+-void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
+-			   size_t len);
++static inline void wpa_hexdump_ascii_key(int level, const char *title,
++					 const u8 *buf, size_t len)
++{
++	if (level < CONFIG_MSG_MIN_PRIORITY)
++		return;
++
++	_wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
++}
+ 
+ /*
+  * wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce
+@@ -184,7 +229,12 @@ void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
+  *
+  * Note: New line '\n' is added to the end of the text when printing to stdout.
+  */
+-void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
++void _wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
++#define wpa_msg(ctx, level, ...)					\
++	do {								\
++		if (level >= CONFIG_MSG_MIN_PRIORITY)			\
++			_wpa_msg(ctx, level, __VA_ARGS__);		\
++	} while(0)
+ 
+ /**
+  * wpa_msg_ctrl - Conditional printf for ctrl_iface monitors
+@@ -198,8 +248,13 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
+  * attached ctrl_iface monitors. In other words, it can be used for frequent
+  * events that do not need to be sent to syslog.
+  */
+-void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
++void _wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
+ PRINTF_FORMAT(3, 4);
++#define wpa_msg_ctrl(ctx, level, ...)					\
++	do {								\
++		if (level >= CONFIG_MSG_MIN_PRIORITY)			\
++			_wpa_msg_ctrl(ctx, level, __VA_ARGS__);		\
++	} while(0)
+ 
+ /**
+  * wpa_msg_global - Global printf for ctrl_iface monitors
+diff --git a/tests/Makefile b/tests/Makefile
+index 8ec154bb3..25fdf9e00 100644
+--- a/tests/Makefile
++++ b/tests/Makefile
+@@ -1,10 +1,12 @@
+-ALL=test-base64 test-md4 test-milenage \
+-	test-rsa-sig-ver \
+-	test-sha1 \
+-	test-https test-https_server \
+-	test-sha256 test-aes test-x509v3 test-list test-rc4 \
++RUN_TESTS= \
++	test-list \
++	test-md4 test-rc4 test-sha1 test-sha256 \
++	test-milenage test-aes \
++	test-crypto_module \
+ 	test-bss
+ 
++ALL=$(RUN_TESTS) test-base64 test-https test-https_server
++
+ include ../src/build.rules
+ 
+ ifdef LIBFUZZER
+@@ -25,13 +27,27 @@ CFLAGS += -DCONFIG_IEEE80211R_AP
+ CFLAGS += -DCONFIG_IEEE80211R
+ CFLAGS += -DCONFIG_TDLS
+ 
++# test-crypto_module
++CFLAGS += -DCONFIG_MODULE_TESTS
++CFLAGS += -DCONFIG_DPP
++#CFLAGS += -DCONFIG_DPP2
++#CFLAGS += -DCONFIG_DPP3
++CFLAGS += -DCONFIG_ECC
++CFLAGS += -DCONFIG_HMAC_SHA256_KDF
++CFLAGS += -DCONFIG_HMAC_SHA384_KDF
++CFLAGS += -DCONFIG_MESH
++CFLAGS += -DCONFIG_SHA256
++CFLAGS += -DCONFIG_SHA384
++CFLAGS += -DEAP_PSK
++CFLAGS += -DEAP_FAST
++
+ CFLAGS += -I../src
+ CFLAGS += -I../src/utils
+ 
+ SLIBS = ../src/utils/libutils.a
+ 
+-DLIBS = ../src/crypto/libcrypto.a \
+-	../src/tls/libtls.a
++DLIBS = ../src/tls/libtls.a \
++	../src/crypto/libcrypto.a
+ 
+ _OBJS_VAR := LLIBS
+ include ../src/objs.mk
+@@ -43,12 +59,43 @@ include ../src/objs.mk
+ LIBS = $(SLIBS) $(DLIBS)
+ LLIBS = -Wl,--start-group $(DLIBS) -Wl,--end-group $(SLIBS)
+ 
++ifeq ($(CONFIG_TLS),mbedtls)
++CFLAGS += -DCONFIG_TLS_MBEDTLS
++LLIBS += -lmbedtls -lmbedx509 -lmbedcrypto
++else
++ifeq ($(CONFIG_TLS),openssl)
++CFLAGS += -DCONFIG_TLS_OPENSSL
++LLIBS += -lssl -lcrypto
++else
++ifeq ($(CONFIG_TLS),gnutls)
++CFLAGS += -DCONFIG_TLS_GNUTLS
++LLIBS += -lgnutls -lgpg-error -lgcrypt
++else
++ifeq ($(CONFIG_TLS),wolfssl)
++CFLAGS += -DCONFIG_TLS_WOLFSSL
++LLIBS += -lwolfssl -lm
++else
++CFLAGS += -DCONFIG_TLS_INTERNAL
++CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
++ALL += test-rsa-sig-ver
++ALL += test-x509v3
++clean-config_tls_internal:
++	rm -f test_x509v3_nist.out.*
++	rm -f test_x509v3_nist2.out.*
++endif
++endif
++endif
++endif
++
+ # glibc < 2.17 needs -lrt for clock_gettime()
+ LLIBS += -lrt
+ 
+ test-aes: $(call BUILDOBJ,test-aes.o) $(LIBS)
+ 	$(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+ 
++test-crypto_module: $(call BUILDOBJ,test-crypto_module.o) $(LIBS)
++	$(LDO) $(LDFLAGS) -o $@ $< $(LLIBS)
++
+ test-base64: $(call BUILDOBJ,test-base64.o) $(LIBS)
+ 	$(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+ 
+@@ -141,18 +188,11 @@ test-bss: $(call BUILDOBJ,test-bss.o) $(WPA_OBJS) $(LIBS)
+ 	$(LDO) $(LDFLAGS) -o $@ $< $(LLIBS) $(WPA_CFLAGS) $(WPA_OBJS) $(LIBS)
+ 
+ run-tests: $(ALL)
+-	./test-aes
+-	./test-list
+-	./test-md4
+-	./test-milenage
+-	./test-rsa-sig-ver
+-	./test-sha1
+-	./test-sha256
+-	./test-bss
++	@set -ex; for i in $(RUN_TESTS); do ./$$i; done
+ 	@echo
+ 	@echo All tests completed successfully.
+ 
+-clean: common-clean
++clean: common-clean clean-config_tls_internal
+ 	rm -f *~
+-	rm -f test_x509v3_nist.out.*
+-	rm -f test_x509v3_nist2.out.*
++
++.PHONY: run-tests clean-config_tls_internal
+diff --git a/tests/hwsim/example-hostapd.config b/tests/hwsim/example-hostapd.config
+index 210b7fb86..5f326da07 100644
+--- a/tests/hwsim/example-hostapd.config
++++ b/tests/hwsim/example-hostapd.config
+@@ -4,6 +4,7 @@ CONFIG_DRIVER_NONE=y
+ CONFIG_DRIVER_NL80211=y
+ CONFIG_RSN_PREAUTH=y
+ 
++#CONFIG_TLS=mbedtls
+ #CONFIG_TLS=internal
+ #CONFIG_INTERNAL_LIBTOMMATH=y
+ #CONFIG_INTERNAL_LIBTOMMATH_FAST=y
+@@ -33,12 +34,7 @@ CONFIG_EAP_TNC=y
+ CFLAGS += -DTNC_CONFIG_FILE=\"tnc/tnc_config\"
+ LIBS += -rdynamic
+ CONFIG_EAP_UNAUTH_TLS=y
+-ifeq ($(CONFIG_TLS), openssl)
+-CONFIG_EAP_PWD=y
+-endif
+-ifeq ($(CONFIG_TLS), wolfssl)
+-CONFIG_EAP_PWD=y
+-endif
++CONFIG_EAP_PWD=$(if $(filter openssl wolfssl mbedtls,$(CONFIG_TLS)),y,)
+ CONFIG_EAP_EKE=y
+ CONFIG_PKCS12=y
+ CONFIG_RADIUS_SERVER=y
+diff --git a/tests/hwsim/example-wpa_supplicant.config b/tests/hwsim/example-wpa_supplicant.config
+index 123f397e3..c69b1f9cd 100644
+--- a/tests/hwsim/example-wpa_supplicant.config
++++ b/tests/hwsim/example-wpa_supplicant.config
+@@ -2,6 +2,7 @@
+ 
+ CONFIG_TLS=openssl
+ #CONFIG_TLS=wolfssl
++#CONFIG_TLS=mbedtls
+ #CONFIG_TLS=internal
+ #CONFIG_INTERNAL_LIBTOMMATH=y
+ #CONFIG_INTERNAL_LIBTOMMATH_FAST=y
+@@ -34,13 +35,7 @@ LIBS += -rdynamic
+ CONFIG_EAP_FAST=y
+ CONFIG_EAP_TEAP=y
+ CONFIG_EAP_IKEV2=y
+-
+-ifeq ($(CONFIG_TLS), openssl)
+-CONFIG_EAP_PWD=y
+-endif
+-ifeq ($(CONFIG_TLS), wolfssl)
+-CONFIG_EAP_PWD=y
+-endif
++CONFIG_EAP_PWD=$(if $(filter openssl wolfssl mbedtls,$(CONFIG_TLS)),y,)
+ 
+ CONFIG_USIM_SIMULATOR=y
+ CONFIG_SIM_SIMULATOR=y
+diff --git a/tests/hwsim/test_ap_eap.py b/tests/hwsim/test_ap_eap.py
+index f8e75b5fb..48e4dedcc 100644
+--- a/tests/hwsim/test_ap_eap.py
++++ b/tests/hwsim/test_ap_eap.py
+@@ -42,20 +42,42 @@ def check_eap_capa(dev, method):
+     res = dev.get_capability("eap")
+     if method not in res:
+         raise HwsimSkip("EAP method %s not supported in the build" % method)
++    if method == "FAST" or method == "TEAP":
++        tls = dev.request("GET tls_library")
++        if tls.startswith("mbed TLS"):
++            raise HwsimSkip("EAP-%s not supported with this TLS library: " % method + tls)
+ 
+ def check_subject_match_support(dev):
+     tls = dev.request("GET tls_library")
+-    if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
++    if tls.startswith("OpenSSL"):
++        return
++    elif tls.startswith("wolfSSL"):
++        return
++    elif tls.startswith("mbed TLS"):
++        return
++    else:
+         raise HwsimSkip("subject_match not supported with this TLS library: " + tls)
+ 
+ def check_check_cert_subject_support(dev):
+     tls = dev.request("GET tls_library")
+-    if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
++    if tls.startswith("OpenSSL"):
++        return
++    elif tls.startswith("wolfSSL"):
++        return
++    elif tls.startswith("mbed TLS"):
++        return
++    else:
+         raise HwsimSkip("check_cert_subject not supported with this TLS library: " + tls)
+ 
+ def check_altsubject_match_support(dev):
+     tls = dev.request("GET tls_library")
+-    if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
++    if tls.startswith("OpenSSL"):
++        return
++    elif tls.startswith("wolfSSL"):
++        return
++    elif tls.startswith("mbed TLS"):
++        return
++    else:
+         raise HwsimSkip("altsubject_match not supported with this TLS library: " + tls)
+ 
+ def check_domain_match(dev):
+@@ -70,7 +92,13 @@ def check_domain_suffix_match(dev):
+ 
+ def check_domain_match_full(dev):
+     tls = dev.request("GET tls_library")
+-    if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
++    if tls.startswith("OpenSSL"):
++        return
++    elif tls.startswith("wolfSSL"):
++        return
++    elif tls.startswith("mbed TLS"):
++        return
++    else:
+         raise HwsimSkip("domain_suffix_match requires full match with this TLS library: " + tls)
+ 
+ def check_cert_probe_support(dev):
+@@ -79,8 +107,15 @@ def check_cert_probe_support(dev):
+         raise HwsimSkip("Certificate probing not supported with this TLS library: " + tls)
+ 
+ def check_ext_cert_check_support(dev):
++    if not openssl_imported:
++        raise HwsimSkip("OpenSSL python method not available")
++
+     tls = dev.request("GET tls_library")
+-    if not tls.startswith("OpenSSL"):
++    if tls.startswith("OpenSSL"):
++        return
++    elif tls.startswith("mbed TLS"):
++        return
++    else:
+         raise HwsimSkip("ext_cert_check not supported with this TLS library: " + tls)
+ 
+ def check_ocsp_support(dev):
+@@ -91,14 +126,18 @@ def check_ocsp_support(dev):
+     #    raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
+     #if tls.startswith("wolfSSL"):
+     #    raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
++    if tls.startswith("mbed TLS"):
++        raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
+ 
+ def check_pkcs5_v15_support(dev):
+     tls = dev.request("GET tls_library")
+-    if "BoringSSL" in tls or "GnuTLS" in tls:
++    if "BoringSSL" in tls or "GnuTLS" in tls or "mbed TLS" in tls:
+         raise HwsimSkip("PKCS#5 v1.5 not supported with this TLS library: " + tls)
+ 
+ def check_tls13_support(dev):
+     tls = dev.request("GET tls_library")
++    if tls.startswith("mbed TLS"):
++        raise HwsimSkip("TLS v1.3 not supported")
+     ok = ['run=OpenSSL 1.1.1', 'run=OpenSSL 3.0', 'run=OpenSSL 3.1',
+           'run=OpenSSL 3.2', 'run=OpenSSL 3.3', 'wolfSSL']
+     for s in ok:
+@@ -122,11 +161,15 @@ def check_pkcs12_support(dev):
+     #    raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
+     if tls.startswith("wolfSSL"):
+         raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
++    if tls.startswith("mbed TLS"):
++        raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
+ 
+ def check_dh_dsa_support(dev):
+     tls = dev.request("GET tls_library")
+     if tls.startswith("internal"):
+         raise HwsimSkip("DH DSA not supported with this TLS library: " + tls)
++    if tls.startswith("mbed TLS"):
++        raise HwsimSkip("DH DSA not supported with this TLS library: " + tls)
+ 
+ def check_ec_support(dev):
+     tls = dev.request("GET tls_library")
+@@ -1741,7 +1784,7 @@ def test_ap_wpa2_eap_ttls_pap_subject_match(dev, apdev):
+     eap_connect(dev[0], hapd, "TTLS", "pap user",
+                 anonymous_identity="ttls", password="password",
+                 ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+-                subject_match="/C=FI/O=w1.fi/CN=server.w1.fi",
++                check_cert_subject="/C=FI/O=w1.fi/CN=server.w1.fi",
+                 altsubject_match="EMAIL:noone@example.com;DNS:server.w1.fi;URI:http://example.com/")
+     eap_reauth(dev[0], "TTLS")
+ 
+@@ -2976,6 +3019,7 @@ def test_ap_wpa2_eap_tls_neg_domain_match(dev, apdev):
+ 
+ def test_ap_wpa2_eap_tls_neg_subject_match(dev, apdev):
+     """WPA2-Enterprise negative test - subject mismatch"""
++    check_subject_match_support(dev[0])
+     params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+     hostapd.add_ap(apdev[0], params)
+     dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+@@ -3036,6 +3080,7 @@ def test_ap_wpa2_eap_tls_neg_subject_match(dev, apdev):
+ 
+ def test_ap_wpa2_eap_tls_neg_altsubject_match(dev, apdev):
+     """WPA2-Enterprise negative test - altsubject mismatch"""
++    check_altsubject_match_support(dev[0])
+     params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+     hostapd.add_ap(apdev[0], params)
+ 
+@@ -3582,7 +3627,7 @@ def test_ap_wpa2_eap_ikev2_oom(dev, apdev):
+             dev[0].request("REMOVE_NETWORK all")
+ 
+     tls = dev[0].request("GET tls_library")
+-    if not tls.startswith("wolfSSL"):
++    if not tls.startswith("wolfSSL") and not tls.startswith("mbed TLS"):
+         tests = [(1, "os_get_random;dh_init")]
+     else:
+         tests = [(1, "crypto_dh_init;dh_init")]
+@@ -4896,7 +4941,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca(dev, apdev, params):
+     params["private_key"] = "auth_serv/iCA-server/server.key"
+     hostapd.add_ap(apdev[0], params)
+     tls = dev[0].request("GET tls_library")
+-    if "GnuTLS" in tls or "wolfSSL" in tls:
++    if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+         ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+         client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+     else:
+@@ -4962,6 +5007,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_sha1(dev, apdev, params):
+     run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, "-sha1")
+ 
+ def run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, md):
++    check_ocsp_support(dev[0])
+     params = int_eap_server_params()
+     params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem"
+     params["server_cert"] = "auth_serv/iCA-server/server.pem"
+@@ -4971,7 +5017,7 @@ def run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, md):
+     try:
+         hostapd.add_ap(apdev[0], params)
+         tls = dev[0].request("GET tls_library")
+-        if "GnuTLS" in tls or "wolfSSL" in tls:
++        if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+             ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+             client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+         else:
+@@ -5007,7 +5053,7 @@ def run_ap_wpa2_eap_tls_intermediate_ca_ocsp_revoked(dev, apdev, params, md):
+     try:
+         hostapd.add_ap(apdev[0], params)
+         tls = dev[0].request("GET tls_library")
+-        if "GnuTLS" in tls or "wolfSSL" in tls:
++        if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+             ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+             client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+         else:
+@@ -5057,7 +5103,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_multi_missing_resp(dev, apdev, par
+     try:
+         hostapd.add_ap(apdev[0], params)
+         tls = dev[0].request("GET tls_library")
+-        if "GnuTLS" in tls or "wolfSSL" in tls:
++        if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+             ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+             client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+         else:
+@@ -5124,7 +5170,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_multi(dev, apdev, params):
+ 
+         hostapd.add_ap(apdev[0], params)
+         tls = dev[0].request("GET tls_library")
+-        if "GnuTLS" in tls or "wolfSSL" in tls:
++        if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
+             ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+             client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+         else:
+@@ -5382,6 +5428,7 @@ def test_ap_wpa2_eap_ttls_server_cert_eku_client_server(dev, apdev):
+ 
+ def test_ap_wpa2_eap_ttls_server_pkcs12(dev, apdev):
+     """WPA2-Enterprise using EAP-TTLS and server PKCS#12 file"""
++    check_pkcs12_support(dev[0])
+     skip_with_fips(dev[0])
+     params = int_eap_server_params()
+     del params["server_cert"]
+@@ -5394,6 +5441,7 @@ def test_ap_wpa2_eap_ttls_server_pkcs12(dev, apdev):
+ 
+ def test_ap_wpa2_eap_ttls_server_pkcs12_extra(dev, apdev):
+     """EAP-TTLS and server PKCS#12 file with extra certs"""
++    check_pkcs12_support(dev[0])
+     skip_with_fips(dev[0])
+     params = int_eap_server_params()
+     del params["server_cert"]
+@@ -5416,6 +5464,7 @@ def test_ap_wpa2_eap_ttls_dh_params_server(dev, apdev):
+ 
+ def test_ap_wpa2_eap_ttls_dh_params_dsa_server(dev, apdev):
+     """WPA2-Enterprise using EAP-TTLS and alternative server dhparams (DSA)"""
++    check_dh_dsa_support(dev[0])
+     params = int_eap_server_params()
+     params["dh_file"] = "auth_serv/dsaparam.pem"
+     hapd = hostapd.add_ap(apdev[0], params)
+@@ -5727,8 +5776,8 @@ def test_ap_wpa2_eap_non_ascii_identity2(dev, apdev):
+ def test_openssl_cipher_suite_config_wpas(dev, apdev):
+     """OpenSSL cipher suite configuration on wpa_supplicant"""
+     tls = dev[0].request("GET tls_library")
+-    if not tls.startswith("OpenSSL"):
+-        raise HwsimSkip("TLS library is not OpenSSL: " + tls)
++    if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
++        raise HwsimSkip("TLS library is not OpenSSL or mbed TLS: " + tls)
+     params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+     hapd = hostapd.add_ap(apdev[0], params)
+     eap_connect(dev[0], hapd, "TTLS", "pap user",
+@@ -5754,14 +5803,14 @@ def test_openssl_cipher_suite_config_wpas(dev, apdev):
+ def test_openssl_cipher_suite_config_hapd(dev, apdev):
+     """OpenSSL cipher suite configuration on hostapd"""
+     tls = dev[0].request("GET tls_library")
+-    if not tls.startswith("OpenSSL"):
+-        raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL: " + tls)
++    if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
++        raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL or mbed TLS: " + tls)
+     params = int_eap_server_params()
+     params['openssl_ciphers'] = "AES256"
+     hapd = hostapd.add_ap(apdev[0], params)
+     tls = hapd.request("GET tls_library")
+-    if not tls.startswith("OpenSSL"):
+-        raise HwsimSkip("hostapd TLS library is not OpenSSL: " + tls)
++    if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
++        raise HwsimSkip("hostapd TLS library is not OpenSSL or mbed TLS: " + tls)
+     eap_connect(dev[0], hapd, "TTLS", "pap user",
+                 anonymous_identity="ttls", password="password",
+                 ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+@@ -6207,13 +6256,17 @@ def test_ap_wpa2_eap_tls_versions(dev, apdev):
+             check_tls_ver(dev[0], hapd,
+                           "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1",
+                           "TLSv1.2")
+-    elif tls.startswith("internal"):
++    elif tls.startswith("internal") or tls.startswith("mbed TLS"):
+         check_tls_ver(dev[0], hapd,
+                       "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1", "TLSv1.2")
+-    check_tls_ver(dev[1], hapd,
+-                  "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1")
+-    check_tls_ver(dev[2], hapd,
+-                  "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1")
++    if tls.startswith("mbed TLS"):
++        check_tls_ver(dev[2], hapd,
++                      "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1.0")
++    else:
++        check_tls_ver(dev[1], hapd,
++                      "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1")
++        check_tls_ver(dev[2], hapd,
++                      "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1")
+     if "run=OpenSSL 1.1.1" in tls or "run=OpenSSL 3." in tls:
+         check_tls_ver(dev[0], hapd,
+                       "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0", "TLSv1.3")
+@@ -6235,6 +6288,11 @@ def test_ap_wpa2_eap_tls_versions_server(dev, apdev):
+     tests = [("TLSv1", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
+              ("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
+              ("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")]
++    tls = dev[0].request("GET tls_library")
++    if tls.startswith("mbed TLS"):
++        tests = [#("TLSv1.0", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
++                 #("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
++                 ("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")]
+     for exp, flags in tests:
+         hapd.disable()
+         hapd.set("tls_flags", flags)
+@@ -7305,6 +7363,7 @@ def test_ap_wpa2_eap_assoc_rsn(dev, apdev):
+ def test_eap_tls_ext_cert_check(dev, apdev):
+     """EAP-TLS and external server certification validation"""
+     # With internal server certificate chain validation
++    check_ext_cert_check_support(dev[0])
+     id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+                         identity="tls user",
+                         ca_cert="auth_serv/ca.pem",
+@@ -7317,6 +7376,7 @@ def test_eap_tls_ext_cert_check(dev, apdev):
+ def test_eap_ttls_ext_cert_check(dev, apdev):
+     """EAP-TTLS and external server certification validation"""
+     # Without internal server certificate chain validation
++    check_ext_cert_check_support(dev[0])
+     id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+                         identity="pap user", anonymous_identity="ttls",
+                         password="password", phase2="auth=PAP",
+@@ -7327,6 +7387,7 @@ def test_eap_ttls_ext_cert_check(dev, apdev):
+ def test_eap_peap_ext_cert_check(dev, apdev):
+     """EAP-PEAP and external server certification validation"""
+     # With internal server certificate chain validation
++    check_ext_cert_check_support(dev[0])
+     id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
+                         identity="user", anonymous_identity="peap",
+                         ca_cert="auth_serv/ca.pem",
+@@ -7337,6 +7398,7 @@ def test_eap_peap_ext_cert_check(dev, apdev):
+ 
+ def test_eap_fast_ext_cert_check(dev, apdev):
+     """EAP-FAST and external server certification validation"""
++    check_ext_cert_check_support(dev[0])
+     check_eap_capa(dev[0], "FAST")
+     # With internal server certificate chain validation
+     dev[0].request("SET blob fast_pac_auth_ext ")
+@@ -7351,10 +7413,6 @@ def test_eap_fast_ext_cert_check(dev, apdev):
+     run_ext_cert_check(dev, apdev, id)
+ 
+ def run_ext_cert_check(dev, apdev, net_id):
+-    check_ext_cert_check_support(dev[0])
+-    if not openssl_imported:
+-        raise HwsimSkip("OpenSSL python method not available")
+-
+     params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+     hapd = hostapd.add_ap(apdev[0], params)
+ 
+diff --git a/tests/hwsim/test_ap_ft.py b/tests/hwsim/test_ap_ft.py
+index 13461f014..8ffb5042c 100644
+--- a/tests/hwsim/test_ap_ft.py
++++ b/tests/hwsim/test_ap_ft.py
+@@ -2494,11 +2494,11 @@ def test_ap_ft_ap_oom5(dev, apdev):
+         # This will fail to roam
+         dev[0].roam(bssid1, check_bssid=False)
+ 
+-    with fail_test(hapd1, 1, "sha256_prf_bits;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
++    with fail_test(hapd1, 1, "sha256_prf;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
+         # This will fail to roam
+         dev[0].roam(bssid1, check_bssid=False)
+ 
+-    with fail_test(hapd1, 3, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
++    with fail_test(hapd1, 2, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
+         # This will fail to roam
+         dev[0].roam(bssid1, check_bssid=False)
+ 
+diff --git a/tests/hwsim/test_authsrv.py b/tests/hwsim/test_authsrv.py
+index e0665bcb2..02ec301e5 100644
+--- a/tests/hwsim/test_authsrv.py
++++ b/tests/hwsim/test_authsrv.py
+@@ -156,9 +156,12 @@ def test_authsrv_oom(dev, apdev):
+         if "FAIL" not in authsrv.request("ENABLE"):
+             raise Exception("ENABLE succeeded during OOM")
+ 
+-    with alloc_fail(authsrv, 1, "tls_init;authsrv_init"):
+-        if "FAIL" not in authsrv.request("ENABLE"):
+-            raise Exception("ENABLE succeeded during OOM")
++    # tls_mbedtls.c:tls_init() does not alloc memory (no alloc fail trigger)
++    tls = dev[0].request("GET tls_library")
++    if not tls.startswith("mbed TLS"):
++        with alloc_fail(authsrv, 1, "tls_init;authsrv_init"):
++            if "FAIL" not in authsrv.request("ENABLE"):
++                raise Exception("ENABLE succeeded during OOM")
+ 
+     for count in range(1, 3):
+         with alloc_fail(authsrv, count, "eap_sim_db_init;authsrv_init"):
+diff --git a/tests/hwsim/test_dpp.py b/tests/hwsim/test_dpp.py
+index 518983bd0..077de58c9 100644
+--- a/tests/hwsim/test_dpp.py
++++ b/tests/hwsim/test_dpp.py
+@@ -39,7 +39,8 @@ def check_dpp_capab(dev, brainpool=False, min_ver=1):
+         raise HwsimSkip("DPP not supported")
+     if brainpool:
+         tls = dev.request("GET tls_library")
+-        if (not tls.startswith("OpenSSL") or "run=BoringSSL" in tls) and not tls.startswith("wolfSSL"):
++        if (not tls.startswith("OpenSSL") or "run=BoringSSL" in tls) and not tls.startswith("wolfSSL") \
++                                                                     and not tls.startswith("mbed TLS"):
+             raise HwsimSkip("Crypto library does not support Brainpool curves: " + tls)
+     capa = dev.request("GET_CAPABILITY dpp")
+     ver = 1
+@@ -3902,6 +3903,9 @@ def test_dpp_proto_auth_req_no_i_proto_key(dev, apdev):
+ 
+ def test_dpp_proto_auth_req_invalid_i_proto_key(dev, apdev):
+     """DPP protocol testing - invalid I-proto key in Auth Req"""
++    tls = dev[0].request("GET tls_library")
++    if tls.startswith("mbed TLS"):
++        raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
+     run_dpp_proto_auth_req_missing(dev, 66, "Invalid Initiator Protocol Key")
+ 
+ def test_dpp_proto_auth_req_no_i_nonce(dev, apdev):
+@@ -3997,7 +4001,12 @@ def test_dpp_proto_auth_resp_no_r_proto_key(dev, apdev):
+ 
+ def test_dpp_proto_auth_resp_invalid_r_proto_key(dev, apdev):
+     """DPP protocol testing - invalid R-Proto Key in Auth Resp"""
+-    run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key")
++    tls = dev[0].request("GET tls_library")
++    if tls.startswith("mbed TLS"):
++        # mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key
++        run_dpp_proto_auth_resp_missing(dev, 67, "Failed to derive ECDH shared secret")
++    else:
++        run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key")
+ 
+ def test_dpp_proto_auth_resp_no_r_nonce(dev, apdev):
+     """DPP protocol testing - no R-nonce in Auth Resp"""
+@@ -4359,11 +4368,17 @@ def test_dpp_proto_pkex_exchange_resp_invalid_status(dev, apdev):
+ 
+ def test_dpp_proto_pkex_cr_req_invalid_bootstrap_key(dev, apdev):
+     """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Request"""
++    tls = dev[0].request("GET tls_library")
++    if tls.startswith("mbed TLS"):
++        raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
+     run_dpp_proto_pkex_req_missing(dev, 47,
+                                    "Peer bootstrapping key is invalid")
+ 
+ def test_dpp_proto_pkex_cr_resp_invalid_bootstrap_key(dev, apdev):
+     """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Response"""
++    tls = dev[0].request("GET tls_library")
++    if tls.startswith("mbed TLS"):
++        raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
+     run_dpp_proto_pkex_resp_missing(dev, 48,
+                                     "Peer bootstrapping key is invalid")
+ 
+diff --git a/tests/hwsim/test_erp.py b/tests/hwsim/test_erp.py
+index d083993e8..262e9f095 100644
+--- a/tests/hwsim/test_erp.py
++++ b/tests/hwsim/test_erp.py
+@@ -12,7 +12,7 @@ import time
+ 
+ import hostapd
+ from utils import *
+-from test_ap_eap import int_eap_server_params, check_tls13_support
++from test_ap_eap import int_eap_server_params, check_tls13_support, check_eap_capa
+ from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations
+ 
+ def test_erp_initiate_reauth_start(dev, apdev):
+@@ -276,6 +276,7 @@ def test_erp_radius_eap_methods(dev, apdev):
+     params['erp_domain'] = 'example.com'
+     params['disable_pmksa_caching'] = '1'
+     hapd = hostapd.add_ap(apdev[0], params)
++    tls = dev[0].request("GET tls_library")
+ 
+     erp_test(dev[0], hapd, eap="AKA", identity="0232010000000000@example.com",
+              password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
+@@ -289,7 +290,7 @@ def test_erp_radius_eap_methods(dev, apdev):
+              password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
+     erp_test(dev[0], hapd, eap="EKE", identity="erp-eke@example.com",
+              password="hello")
+-    if "FAST" in eap_methods:
++    if "FAST" in eap_methods and check_eap_capa(dev[0], "FAST"):
+         erp_test(dev[0], hapd, eap="FAST", identity="erp-fast@example.com",
+                  password="password", ca_cert="auth_serv/ca.pem",
+                  phase2="auth=GTC",
+@@ -301,13 +302,14 @@ def test_erp_radius_eap_methods(dev, apdev):
+              password="password")
+     erp_test(dev[0], hapd, eap="PAX", identity="erp-pax@example.com",
+              password_hex="0123456789abcdef0123456789abcdef")
+-    if "MSCHAPV2" in eap_methods:
++    if "MSCHAPV2" in eap_methods and check_eap_capa(dev[0], "MSCHAPV2"):
+         erp_test(dev[0], hapd, eap="PEAP", identity="erp-peap@example.com",
+                  password="password", ca_cert="auth_serv/ca.pem",
+                  phase2="auth=MSCHAPV2")
+-        erp_test(dev[0], hapd, eap="TEAP", identity="erp-teap@example.com",
+-                 password="password", ca_cert="auth_serv/ca.pem",
+-                 phase2="auth=MSCHAPV2", pac_file="blob://teap_pac")
++        if check_eap_capa(dev[0], "TEAP"):
++            erp_test(dev[0], hapd, eap="TEAP", identity="erp-teap@example.com",
++                     password="password", ca_cert="auth_serv/ca.pem",
++                     phase2="auth=MSCHAPV2", pac_file="blob://teap_pac")
+     erp_test(dev[0], hapd, eap="PSK", identity="erp-psk@example.com",
+              password_hex="0123456789abcdef0123456789abcdef")
+     if "PWD" in eap_methods:
+@@ -640,7 +642,7 @@ def test_erp_local_errors(dev, apdev):
+         dev[0].request("REMOVE_NETWORK all")
+         dev[0].wait_disconnected()
+ 
+-    for count in range(1, 6):
++    for count in range(1, 4):
+         dev[0].request("ERP_FLUSH")
+         with fail_test(dev[0], count, "hmac_sha256_kdf;eap_peer_erp_init"):
+             dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+diff --git a/tests/hwsim/test_fils.py b/tests/hwsim/test_fils.py
+index 6f857243a..de65c57a7 100644
+--- a/tests/hwsim/test_fils.py
++++ b/tests/hwsim/test_fils.py
+@@ -1477,6 +1477,10 @@ def check_ec_group(dev, group):
+     tls = dev.request("GET tls_library")
+     if tls.startswith("wolfSSL"):
+         return
++    elif tls.startswith("mbed TLS"):
++        if int(group) == 27:
++            raise HwsimSkip("Brainpool EC group 27 not supported by mbed TLS")
++        return
+     if int(group) in [25]:
+         if not (tls.startswith("OpenSSL") and ("build=OpenSSL 1.0.2" in tls or "build=OpenSSL 1.1" in tls or "build=OpenSSL 3." in tls) and ("run=OpenSSL 1.0.2" in tls or "run=OpenSSL 1.1" in tls or "run=OpenSSL 3." in tls)):
+             raise HwsimSkip("EC group not supported")
+diff --git a/tests/hwsim/test_pmksa_cache.py b/tests/hwsim/test_pmksa_cache.py
+index 4a3b444ff..4f7f7f760 100644
+--- a/tests/hwsim/test_pmksa_cache.py
++++ b/tests/hwsim/test_pmksa_cache.py
+@@ -958,7 +958,7 @@ def test_pmksa_cache_preauth_wpas_oom(dev, apdev):
+     eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
+                 password_hex="0123456789abcdef0123456789abcdef",
+                 bssid=apdev[0]['bssid'])
+-    for i in range(1, 11):
++    for i in range(1, 10):
+         with alloc_fail(dev[0], i, "rsn_preauth_init"):
+             res = dev[0].request("PREAUTH f2:11:22:33:44:55").strip()
+             logger.info("Iteration %d - PREAUTH command results: %s" % (i, res))
+@@ -966,7 +966,7 @@ def test_pmksa_cache_preauth_wpas_oom(dev, apdev):
+                 state = dev[0].request('GET_ALLOC_FAIL')
+                 if state.startswith('0:'):
+                     break
+-                time.sleep(0.05)
++                time.sleep(0.10)
+ 
+ def test_pmksa_cache_ctrl(dev, apdev):
+     """PMKSA cache control interface operations"""
+diff --git a/tests/hwsim/test_sae.py b/tests/hwsim/test_sae.py
+index 679db0e2d..4ea9bd6a4 100644
+--- a/tests/hwsim/test_sae.py
++++ b/tests/hwsim/test_sae.py
+@@ -178,6 +178,11 @@ def test_sae_groups(dev, apdev):
+     if tls.startswith("OpenSSL") and "run=OpenSSL 1." in tls:
+         logger.info("Add Brainpool EC groups since OpenSSL is new enough")
+         sae_groups += [27, 28, 29, 30]
++    if tls.startswith("mbed TLS"):
++        # secp224k1 and secp224r1 (26) have prime p = 1 mod 4, and mbedtls
++        # does not have code to derive y from compressed format for those curves
++        sae_groups = [19, 25, 20, 21, 1, 2, 5, 14, 15, 16, 22, 23, 24]
++        sae_groups += [27, 28, 29, 30]
+     heavy_groups = [14, 15, 16]
+     suitable_groups = [15, 16, 17, 18, 19, 20, 21]
+     groups = [str(g) for g in sae_groups]
+@@ -2232,6 +2237,8 @@ def run_sae_pwe_group(dev, apdev, group):
+             logger.info("Add Brainpool EC groups since OpenSSL is new enough")
+         elif tls.startswith("wolfSSL"):
+             logger.info("Make sure Brainpool EC groups were enabled when compiling wolfSSL")
++        elif tls.startswith("mbed TLS"):
++            logger.info("Make sure Brainpool EC groups were enabled when compiling mbed TLS")
+         else:
+             raise HwsimSkip("Brainpool curve not supported")
+     start_sae_pwe_ap(apdev[0], group, 2)
+diff --git a/tests/hwsim/test_suite_b.py b/tests/hwsim/test_suite_b.py
+index ddd1c2ee7..a44f32955 100644
+--- a/tests/hwsim/test_suite_b.py
++++ b/tests/hwsim/test_suite_b.py
+@@ -27,6 +27,8 @@ def check_suite_b_tls_lib(dev, dhe=False, level128=False):
+         return
+     if tls.startswith("wolfSSL"):
+         return
++    if tls.startswith("mbed TLS"):
++        return
+     if not tls.startswith("OpenSSL"):
+         raise HwsimSkip("TLS library not supported for Suite B: " + tls)
+     supported = False
+@@ -520,6 +522,7 @@ def test_suite_b_192_rsa_insufficient_dh(dev, apdev):
+ 
+     dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
+                    ieee80211w="2",
++                   openssl_ciphers="DHE-RSA-AES256-GCM-SHA384",
+                    phase1="tls_suiteb=1",
+                    eap="TLS", identity="tls user",
+                    ca_cert="auth_serv/rsa3072-ca.pem",
+diff --git a/tests/hwsim/test_wpas_ctrl.py b/tests/hwsim/test_wpas_ctrl.py
+index cf6d3211e..cbf136eaf 100644
+--- a/tests/hwsim/test_wpas_ctrl.py
++++ b/tests/hwsim/test_wpas_ctrl.py
+@@ -1856,7 +1856,7 @@ def _test_wpas_ctrl_oom(dev):
+     tls = dev[0].request("GET tls_library")
+     if not tls.startswith("internal"):
+         tests.append(('NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG', 'FAIL',
+-                      4, 'wpas_ctrl_nfc_get_handover_sel_p2p'))
++                      3, 'wpas_ctrl_nfc_get_handover_sel_p2p'))
+     for cmd, exp, count, func in tests:
+         with alloc_fail(dev[0], count, func):
+             res = dev[0].request(cmd)
+diff --git a/tests/hwsim/utils.py b/tests/hwsim/utils.py
+index 7e3608284..b23c1ee0b 100644
+--- a/tests/hwsim/utils.py
++++ b/tests/hwsim/utils.py
+@@ -145,7 +145,13 @@ def check_imsi_privacy_support(dev):
+ 
+ def check_tls_tod(dev):
+     tls = dev.request("GET tls_library")
+-    if not tls.startswith("OpenSSL") and not tls.startswith("internal"):
++    if tls.startswith("OpenSSL"):
++        return
++    elif tls.startswith("internal"):
++        return
++    elif tls.startswith("mbed TLS"):
++        return
++    else:
+         raise HwsimSkip("TLS TOD-TOFU/STRICT not supported with this TLS library: " + tls)
+ 
+ def vht_supported():
+diff --git a/tests/test-crypto_module.c b/tests/test-crypto_module.c
+new file mode 100644
+index 000000000..0f1156142
+--- /dev/null
++++ b/tests/test-crypto_module.c
+@@ -0,0 +1,16 @@
++/*
++ * crypto module tests - test program
++ * Copyright (c) 2022, Glenn Strauss <gstrauss@gluelogic.com>
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++#include "utils/module_tests.h"
++#include "crypto/crypto_module_tests.c"
++
++int main(int argc, char *argv[])
++{
++	return crypto_module_tests();
++}
+diff --git a/tests/test-https.c b/tests/test-https.c
+index a72e56f9d..e9df82f1d 100644
+--- a/tests/test-https.c
++++ b/tests/test-https.c
+@@ -75,7 +75,7 @@ static int https_client(int s, const char *path)
+ 	struct tls_connection *conn;
+ 	struct wpabuf *in, *out, *appl;
+ 	int res = -1;
+-	int need_more_data;
++	int need_more_data = 0;
+ 
+ 	os_memset(&conf, 0, sizeof(conf));
+ 	conf.event_cb = https_tls_event_cb;
+@@ -93,8 +93,12 @@ static int https_client(int s, const char *path)
+ 
+ 	for (;;) {
+ 		appl = NULL;
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ 		out = tls_connection_handshake2(tls, conn, in, &appl,
+ 						&need_more_data);
++#else
++		out = tls_connection_handshake(tls, conn, in, &appl);
++#endif
+ 		wpabuf_free(in);
+ 		in = NULL;
+ 		if (out == NULL) {
+@@ -152,11 +156,15 @@ static int https_client(int s, const char *path)
+ 
+ 	wpa_printf(MSG_INFO, "Reading HTTP response");
+ 	for (;;) {
+-		int need_more_data;
++		int need_more_data = 0;
+ 		in = https_recv(s);
+ 		if (in == NULL)
+ 			goto done;
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ 		out = tls_connection_decrypt2(tls, conn, in, &need_more_data);
++#else
++		out = tls_connection_decrypt(tls, conn, in);
++#endif
+ 		if (need_more_data)
+ 			wpa_printf(MSG_DEBUG, "HTTP: Need more data");
+ 		wpabuf_free(in);
+diff --git a/tests/test-https_server.c b/tests/test-https_server.c
+index 33b448682..9dcca5596 100644
+--- a/tests/test-https_server.c
++++ b/tests/test-https_server.c
+@@ -67,10 +67,12 @@ static struct wpabuf * https_recv(int s, int timeout_ms)
+ }
+ 
+ 
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ static void https_tls_log_cb(void *ctx, const char *msg)
+ {
+ 	wpa_printf(MSG_DEBUG, "TLS: %s", msg);
+ }
++#endif
+ 
+ 
+ static int https_server(int s)
+@@ -79,7 +81,7 @@ static int https_server(int s)
+ 	void *tls;
+ 	struct tls_connection_params params;
+ 	struct tls_connection *conn;
+-	struct wpabuf *in, *out, *appl;
++	struct wpabuf *in = NULL, *out = NULL, *appl = NULL;
+ 	int res = -1;
+ 
+ 	os_memset(&conf, 0, sizeof(conf));
+@@ -106,7 +108,9 @@ static int https_server(int s)
+ 		return -1;
+ 	}
+ 
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ 	tls_connection_set_log_cb(conn, https_tls_log_cb, NULL);
++#endif
+ 
+ 	for (;;) {
+ 		in = https_recv(s, 5000);
+@@ -147,12 +151,16 @@ static int https_server(int s)
+ 
+ 	wpa_printf(MSG_INFO, "Reading HTTP request");
+ 	for (;;) {
+-		int need_more_data;
++		int need_more_data = 0;
+ 
+ 		in = https_recv(s, 5000);
+ 		if (!in)
+ 			goto done;
++#ifdef CONFIG_TLS_INTERNAL_SERVER
+ 		out = tls_connection_decrypt2(tls, conn, in, &need_more_data);
++#else
++		out = tls_connection_decrypt(tls, conn, in);
++#endif
+ 		wpabuf_free(in);
+ 		in = NULL;
+ 		if (need_more_data) {
+diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
+index 743c8acd6..c40e8d70d 100644
+--- a/wpa_supplicant/Makefile
++++ b/wpa_supplicant/Makefile
+@@ -10,6 +10,7 @@ ALL += dbus/fi.w1.wpa_supplicant1.service
+ EXTRA_TARGETS=dynamic_eap_methods
+ 
+ CONFIG_FILE=.config
++-include $(if $(MULTICALL),../hostapd/.config)
+ include ../src/build.rules
+ 
+ ifdef CONFIG_BUILD_PASN_SO
+@@ -190,6 +191,25 @@ ifdef CONFIG_EAPOL_TEST
+ CFLAGS += -Werror -DEAPOL_TEST
+ 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
++endif
++
+ ifdef CONFIG_CODE_COVERAGE
+ CFLAGS += -O0 -fprofile-arcs -ftest-coverage -U_FORTIFY_SOURCE
+ LIBS += -lgcov
+@@ -389,7 +409,9 @@ endif
+ ifdef CONFIG_IBSS_RSN
+ NEED_RSN_AUTHENTICATOR=y
+ CFLAGS += -DCONFIG_IBSS_RSN
++ifndef MULTICALL
+ CFLAGS += -DCONFIG_NO_VLAN
++endif
+ OBJS += ibss_rsn.o
+ endif
+ 
+@@ -981,6 +1003,10 @@ ifdef CONFIG_DYNAMIC_EAP_METHODS
+ CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS
+ LIBS += -ldl -rdynamic
+ endif
++else
++  ifdef MULTICALL
++    OBJS += ../src/eap_common/eap_common.o
++  endif
+ endif
+ 
+ ifdef CONFIG_AP
+@@ -988,9 +1014,11 @@ NEED_EAP_COMMON=y
+ NEED_RSN_AUTHENTICATOR=y
+ CFLAGS += -DCONFIG_AP
+ OBJS += ap.o
++ifndef MULTICALL
+ CFLAGS += -DCONFIG_NO_RADIUS
+ CFLAGS += -DCONFIG_NO_ACCOUNTING
+ CFLAGS += -DCONFIG_NO_VLAN
++endif
+ OBJS += ../src/ap/hostapd.o
+ OBJS += ../src/ap/wpa_auth_glue.o
+ OBJS += ../src/ap/utils.o
+@@ -1030,7 +1058,16 @@ ifdef CONFIG_FILS
+ OBJS += ../src/ap/fils_hlp.o
+ endif
+ ifdef CONFIG_CTRL_IFACE
++ifdef CONFIG_CTRL_IFACE_MIB
++CFLAGS += -DCONFIG_CTRL_IFACE_MIB
++endif
+ 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
+@@ -1081,6 +1118,12 @@ endif
+ ifdef CONFIG_HS20
+ OBJS += ../src/ap/hs20.o
+ endif
++else
++  ifdef MULTICALL
++    OBJS += ../src/eap_server/eap_server.o
++    OBJS += ../src/eap_server/eap_server_identity.o
++    OBJS += ../src/eap_server/eap_server_methods.o
++  endif
+ endif
+ 
+ ifdef CONFIG_MBO
+@@ -1090,7 +1133,9 @@ NEED_GAS=y
+ endif
+ 
+ ifdef NEED_RSN_AUTHENTICATOR
++ifndef MULTICALL
+ CFLAGS += -DCONFIG_NO_RADIUS
++endif
+ NEED_AES_WRAP=y
+ OBJS += ../src/ap/wpa_auth.o
+ OBJS += ../src/ap/wpa_auth_ie.o
+@@ -1189,6 +1234,7 @@ TLS_FUNCS=y
+ endif
+ 
+ ifeq ($(CONFIG_TLS), wolfssl)
++CFLAGS += -DCONFIG_TLS_WOLFSSL
+ ifdef TLS_FUNCS
+ CFLAGS += -DWOLFSSL_DER_LOAD
+ OBJS += ../src/crypto/tls_wolfssl.o
+@@ -1204,6 +1250,7 @@ LIBS_p += -lwolfssl -lm
+ endif
+ 
+ ifeq ($(CONFIG_TLS), openssl)
++CFLAGS += -DCONFIG_TLS_OPENSSL
+ CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
+ ifdef TLS_FUNCS
+ CFLAGS += -DEAP_TLS_OPENSSL
+@@ -1230,7 +1277,28 @@ endif
+ CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
+ endif
+ 
++ifeq ($(CONFIG_TLS), mbedtls)
++CFLAGS += -DCONFIG_TLS_MBEDTLS
++ifndef CONFIG_CRYPTO
++CONFIG_CRYPTO=mbedtls
++endif
++ifdef TLS_FUNCS
++OBJS += ../src/crypto/tls_mbedtls.o
++LIBS += -lmbedtls -lmbedx509
++endif
++OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++OBJS_p += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++OBJS_priv += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
++ifeq ($(CONFIG_CRYPTO), mbedtls)
++LIBS += -lmbedcrypto
++LIBS_p += -lmbedcrypto
++# XXX: create a config option?
++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
++endif
++endif
++
+ ifeq ($(CONFIG_TLS), gnutls)
++CFLAGS += -DCONFIG_TLS_GNUTLS
+ ifndef CONFIG_CRYPTO
+ # default to libgcrypt
+ CONFIG_CRYPTO=gnutls
+@@ -1261,6 +1329,7 @@ endif
+ endif
+ 
+ ifeq ($(CONFIG_TLS), internal)
++CFLAGS += -DCONFIG_TLS_INTERNAL
+ ifndef CONFIG_CRYPTO
+ CONFIG_CRYPTO=internal
+ endif
+@@ -1341,6 +1410,7 @@ endif
+ endif
+ 
+ ifeq ($(CONFIG_TLS), linux)
++CFLAGS += -DCONFIG_TLS_INTERNAL
+ OBJS += ../src/crypto/crypto_linux.o
+ OBJS_p += ../src/crypto/crypto_linux.o
+ ifdef TLS_FUNCS
+@@ -1422,9 +1492,11 @@ endif
+ 
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ NEED_INTERNAL_AES_WRAP=y
+ endif
+ endif
++endif
+ ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP
+ # Seems to be needed at least with BoringSSL
+ NEED_INTERNAL_AES_WRAP=y
+@@ -1438,9 +1510,11 @@ endif
+ 
+ ifdef NEED_INTERNAL_AES_WRAP
+ ifneq ($(CONFIG_TLS), linux)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-unwrap.o
+ endif
+ endif
++endif
+ ifdef NEED_AES_EAX
+ AESOBJS += ../src/crypto/aes-eax.o
+ NEED_AES_CTR=y
+@@ -1450,35 +1524,45 @@ AESOBJS += ../src/crypto/aes-siv.o
+ NEED_AES_CTR=y
+ endif
+ ifdef NEED_AES_CTR
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-ctr.o
+ endif
++endif
+ ifdef NEED_AES_ENCBLOCK
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-encblock.o
+ endif
++endif
+ NEED_AES_ENC=y
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-omac1.o
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_WRAP
+ NEED_AES_ENC=y
+ ifdef NEED_INTERNAL_AES_WRAP
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-wrap.o
+ endif
+ endif
++endif
+ ifdef NEED_AES_CBC
+ NEED_AES_ENC=y
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ AESOBJS += ../src/crypto/aes-cbc.o
+ endif
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_AES_ENC
+ ifdef CONFIG_INTERNAL_AES
+ AESOBJS += ../src/crypto/aes-internal-enc.o
+@@ -1493,12 +1577,16 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1.o
+ endif
+ endif
+ endif
+ endif
++endif
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-prf.o
++endif
+ ifdef CONFIG_INTERNAL_SHA1
+ SHA1OBJS += ../src/crypto/sha1-internal.o
+ ifdef NEED_FIPS186_2_PRF
+@@ -1510,29 +1598,37 @@ CFLAGS += -DCONFIG_NO_PBKDF2
+ else
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_T_PRF
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-tprf.o
+ endif
++endif
+ ifdef NEED_TLS_PRF
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA1OBJS += ../src/crypto/sha1-tlsprf.o
+ endif
+ endif
++endif
+ 
+ ifndef CONFIG_FIPS
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ MD5OBJS += ../src/crypto/md5.o
+ endif
+ endif
+ endif
+ endif
+ endif
++endif
+ ifdef NEED_MD5
+ ifdef CONFIG_INTERNAL_MD5
+ MD5OBJS += ../src/crypto/md5-internal.o
+@@ -1587,12 +1683,17 @@ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA256OBJS += ../src/crypto/sha256.o
+ endif
+ endif
+ endif
+ endif
++endif
++
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA256OBJS += ../src/crypto/sha256-prf.o
++endif
+ ifdef CONFIG_INTERNAL_SHA256
+ SHA256OBJS += ../src/crypto/sha256-internal.o
+ endif
+@@ -1605,50 +1706,68 @@ CFLAGS += -DCONFIG_INTERNAL_SHA512
+ SHA256OBJS += ../src/crypto/sha512-internal.o
+ endif
+ ifdef NEED_TLS_PRF_SHA256
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA256OBJS += ../src/crypto/sha256-tlsprf.o
+ endif
++endif
+ ifdef NEED_TLS_PRF_SHA384
++ifneq ($(CONFIG_TLS), mbedtls)
+ SHA256OBJS += ../src/crypto/sha384-tlsprf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA256_KDF
+ CFLAGS += -DCONFIG_HMAC_SHA256_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha256-kdf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA384_KDF
+ CFLAGS += -DCONFIG_HMAC_SHA384_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-kdf.o
+ endif
++endif
+ ifdef NEED_HMAC_SHA512_KDF
+ CFLAGS += -DCONFIG_HMAC_SHA512_KDF
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512-kdf.o
+ endif
++endif
+ OBJS += $(SHA256OBJS)
+ ifdef NEED_SHA384
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384.o
+ endif
+ endif
+ endif
+ endif
++endif
+ CFLAGS += -DCONFIG_SHA384
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha384-prf.o
+ endif
++endif
+ ifdef NEED_SHA512
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), linux)
+ ifneq ($(CONFIG_TLS), gnutls)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512.o
+ endif
+ endif
+ endif
+ endif
++endif
+ CFLAGS += -DCONFIG_SHA512
++ifneq ($(CONFIG_TLS), mbedtls)
+ OBJS += ../src/crypto/sha512-prf.o
+ endif
++endif
+ 
+ ifdef NEED_ASN1
+ OBJS += ../src/tls/asn1.o
+@@ -1823,10 +1942,12 @@ ifdef CONFIG_FIPS
+ CFLAGS += -DCONFIG_FIPS
+ ifneq ($(CONFIG_TLS), openssl)
+ ifneq ($(CONFIG_TLS), wolfssl)
++ifneq ($(CONFIG_TLS), mbedtls)
+ $(error CONFIG_FIPS=y requires CONFIG_TLS=openssl)
+ endif
+ endif
+ endif
++endif
+ 
+ OBJS += $(SHA1OBJS) $(DESOBJS)
+ 
+@@ -2004,32 +2125,38 @@ wpa_priv: $(BCHECK) $(OBJS_priv)
+ 
+ _OBJS_VAR := OBJS
+ include ../src/objs.mk
++wpa_supplicant_multi.a: .config $(BCHECK) $(OBJS) $(EXTRA_progs)
++	$(Q)$(CC) -c -o wpa_supplicant_multi.o -Dmain=wpa_supplicant_main $(CFLAGS) main.c
++	@$(E) "  CC " $<
++	@rm -f $@
++	@$(AR) cr $@ wpa_supplicant_multi.o $(OBJS)
++
+ wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs)
+-	$(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
++	+$(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
+ 	@$(E) "  LD " $@
+ 
+ _OBJS_VAR := OBJS_t
+ include ../src/objs.mk
+ eapol_test: $(OBJS_t)
+-	$(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
++	+$(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
+ 	@$(E) "  LD " $@
+ 
+ _OBJS_VAR := OBJS_t2
+ include ../src/objs.mk
+ preauth_test: $(OBJS_t2)
+-	$(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
++	+$(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
+ 	@$(E) "  LD " $@
+ 
+ _OBJS_VAR := OBJS_p
+ include ../src/objs.mk
+ wpa_passphrase: $(OBJS_p)
+-	$(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
++	+$(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
+ 	@$(E) "  LD " $@
+ 
+ _OBJS_VAR := OBJS_c
+ include ../src/objs.mk
+ wpa_cli: $(OBJS_c)
+-	$(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
++	+$(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
+ 	@$(E) "  LD " $@
+ 
+ LIBCTRL += ../src/common/wpa_ctrl.o
+@@ -2136,6 +2263,12 @@ eap_gpsk.so: $(SRC_EAP_GPSK)
+ 	$(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
+ 	@$(E) "  sed" $<
+ 
++dump_cflags:
++	@printf "%s " "$(CFLAGS)"
++
++dump_ldflags:
++	@printf "%s " "$(LDFLAGS) $(LIBS) $(EXTRALIBS)"
++
+ wpa_supplicant.exe: wpa_supplicant
+ 	mv -f $< $@
+ wpa_cli.exe: wpa_cli
+diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
+index 69a0e5ee1..7e8c97c38 100644
+--- a/wpa_supplicant/ap.c
++++ b/wpa_supplicant/ap.c
+@@ -1520,7 +1520,7 @@ int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+ #endif /* CONFIG_WPS */
+ 
+ 
+-#ifdef CONFIG_CTRL_IFACE
++#if defined(CONFIG_CTRL_IFACE) && defined(CONFIG_CTRL_IFACE_MIB)
+ 
+ int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
+ 			    char *buf, size_t buflen)
+@@ -1846,17 +1846,37 @@ int ap_switch_channel(struct wpa_supplicant *wpa_s,
+ 
+ 
+ #ifdef CONFIG_CTRL_IFACE
++
++static int __ap_ctrl_iface_chanswitch(struct hostapd_iface *iface,
++				      struct csa_settings *settings)
++{
++#ifdef NEED_AP_MLME
++	if (!iface || !iface->bss[0])
++		return 0;
++
++	return hostapd_switch_channel(iface->bss[0], settings);
++#else
++	return -1;
++#endif
++}
++
++
+ int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos)
+ {
+ 	struct csa_settings settings;
+ 	int ret = hostapd_parse_csa_settings(pos, &settings);
+ 
+-	if (ret)
+-		return ret;
++	if (!(wpa_s->ap_iface && wpa_s->ap_iface->bss[0]) &&
++	    !(wpa_s->ifmsh && wpa_s->ifmsh->bss[0]))
++		return -1;
+ 
+ 	settings.link_id = -1;
+ 
+-	return ap_switch_channel(wpa_s, &settings);
++	ret = __ap_ctrl_iface_chanswitch(wpa_s->ap_iface, &settings);
++	if (ret)
++		return ret;
++
++	return __ap_ctrl_iface_chanswitch(wpa_s->ifmsh, &settings);
+ }
+ #endif /* CONFIG_CTRL_IFACE */
+ 
+diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
+index b02b694a3..dc4b0636a 100644
+--- a/wpa_supplicant/config.c
++++ b/wpa_supplicant/config.c
+@@ -18,6 +18,7 @@
+ #include "eap_peer/eap.h"
+ #include "p2p/p2p.h"
+ #include "fst/fst.h"
++#include "ap/sta_info.h"
+ #include "config.h"
+ 
+ 
+@@ -2421,6 +2422,97 @@ static char * wpa_config_write_mac_value(const struct parse_data *data,
+ #endif /* NO_CONFIG_WRITE */
+ 
+ 
++static int wpa_config_parse_mcast_rate(const struct parse_data *data,
++				       struct wpa_ssid *ssid, int line,
++				       const char *value)
++{
++	ssid->mcast_rate = (int)(strtod(value, NULL) * 10);
++
++	return 0;
++}
++
++#ifndef NO_CONFIG_WRITE
++static char * wpa_config_write_mcast_rate(const struct parse_data *data,
++					  struct wpa_ssid *ssid)
++{
++	char *value;
++	int res;
++
++	if (!ssid->mcast_rate == 0)
++		return NULL;
++
++	value = os_malloc(6); /* longest: 300.0 */
++	if (value == NULL)
++		return NULL;
++	res = os_snprintf(value, 5, "%.1f", (double)ssid->mcast_rate / 10);
++	if (res < 0) {
++		os_free(value);
++		return NULL;
++	}
++	return value;
++}
++#endif /* NO_CONFIG_WRITE */
++
++static int wpa_config_parse_rates(const struct parse_data *data,
++				  struct wpa_ssid *ssid, int line,
++				  const char *value)
++{
++	int i;
++	char *pos, *r, *sptr, *end;
++	double rate;
++
++	pos = (char *)value;
++	r = strtok_r(pos, ",", &sptr);
++	i = 0;
++	while (pos && i < WLAN_SUPP_RATES_MAX) {
++		rate = 0.0;
++		if (r)
++			rate = strtod(r, &end);
++		ssid->rates[i] = rate * 2;
++		if (*end != '\0' || rate * 2 != ssid->rates[i])
++			return 1;
++
++		i++;
++		r = strtok_r(NULL, ",", &sptr);
++	}
++
++	return 0;
++}
++
++#ifndef NO_CONFIG_WRITE
++static char * wpa_config_write_rates(const struct parse_data *data,
++				     struct wpa_ssid *ssid)
++{
++	char *value, *pos;
++	int res, i;
++
++	if (ssid->rates[0] <= 0)
++		return NULL;
++
++	value = os_malloc(6 * WLAN_SUPP_RATES_MAX + 1);
++	if (value == NULL)
++		return NULL;
++	pos = value;
++	for (i = 0; i < WLAN_SUPP_RATES_MAX - 1; i++) {
++		res = os_snprintf(pos, 6, "%.1f,", (double)ssid->rates[i] / 2);
++		if (res < 0) {
++			os_free(value);
++			return NULL;
++		}
++		pos += res;
++	}
++	res = os_snprintf(pos, 6, "%.1f",
++			  (double)ssid->rates[WLAN_SUPP_RATES_MAX - 1] / 2);
++	if (res < 0) {
++		os_free(value);
++		return NULL;
++	}
++
++	value[6 * WLAN_SUPP_RATES_MAX] = '\0';
++	return value;
++}
++#endif /* NO_CONFIG_WRITE */
++
+ /* Helper macros for network block parser */
+ 
+ #ifdef OFFSET
+@@ -2639,6 +2731,7 @@ static const struct parse_data ssid_fields[] = {
+ #else /* CONFIG_MESH */
+ 	{ INT_RANGE(mode, 0, 4) },
+ #endif /* CONFIG_MESH */
++	{ INT_RANGE(noscan, 0, 1) },
+ 	{ INT_RANGE(proactive_key_caching, 0, 1) },
+ 	{ INT_RANGE(disabled, 0, 2) },
+ 	{ STR(id_str) },
+@@ -2712,6 +2805,8 @@ static const struct parse_data ssid_fields[] = {
+ 	{ INT(ap_max_inactivity) },
+ 	{ INT(dtim_period) },
+ 	{ INT(beacon_int) },
++	{ FUNC(rates) },
++	{ FUNC(mcast_rate) },
+ #ifdef CONFIG_MACSEC
+ 	{ INT_RANGE(macsec_policy, 0, 1) },
+ 	{ INT_RANGE(macsec_integ_only, 0, 1) },
+diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
+index fd8eafe2b..5ce616129 100644
+--- a/wpa_supplicant/config_file.c
++++ b/wpa_supplicant/config_file.c
+@@ -326,8 +326,13 @@ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
+ 	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));
+@@ -775,6 +780,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
+ #endif /* IEEE8021X_EAPOL */
+ 	INT(mode);
+ 	INT(no_auto_peer);
++	INT(noscan);
+ 	INT(mesh_fwding);
+ 	INT(frequency);
+ 	INT(enable_edmg);
+diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
+index d64c30508..872bcc270 100644
+--- a/wpa_supplicant/config_ssid.h
++++ b/wpa_supplicant/config_ssid.h
+@@ -879,6 +879,9 @@ struct wpa_ssid {
+ 	 */
+ 	void *parent_cred;
+ 
++	unsigned char rates[WLAN_SUPP_RATES_MAX];
++	double mcast_rate;
++
+ #ifdef CONFIG_MACSEC
+ 	/**
+ 	 * macsec_policy - Determines the policy for MACsec secure session
+@@ -1035,6 +1038,8 @@ struct wpa_ssid {
+ 	 */
+ 	int no_auto_peer;
+ 
++	int noscan;
++
+ 	/**
+ 	 * mesh_rssi_threshold - Set mesh parameter mesh_rssi_threshold (dBm)
+ 	 *
+diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
+index d245531cd..4777c3abe 100644
+--- a/wpa_supplicant/ctrl_iface.c
++++ b/wpa_supplicant/ctrl_iface.c
+@@ -2355,7 +2355,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
+ 			pos += ret;
+ 		}
+ 
+-#ifdef CONFIG_AP
++#if defined(CONFIG_AP) && defined(CONFIG_CTRL_IFACE_MIB)
+ 		if (wpa_s->ap_iface) {
+ 			pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos,
+ 							    end - pos,
+@@ -12561,6 +12561,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ 			reply_len = -1;
+ 	} else if (os_strncmp(buf, "NOTE ", 5) == 0) {
+ 		wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
++#ifdef CONFIG_CTRL_IFACE_MIB
+ 	} else if (os_strcmp(buf, "MIB") == 0) {
+ 		reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
+ 		if (reply_len >= 0) {
+@@ -12573,6 +12574,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ 				reply_size - reply_len);
+ #endif /* CONFIG_MACSEC */
+ 		}
++#endif
+ 	} else if (os_strncmp(buf, "STATUS", 6) == 0) {
+ 		reply_len = wpa_supplicant_ctrl_iface_status(
+ 			wpa_s, buf + 6, reply, reply_size);
+@@ -13061,6 +13063,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ 		reply_len = wpa_supplicant_ctrl_iface_bss(
+ 			wpa_s, buf + 4, reply, reply_size);
+ #ifdef CONFIG_AP
++#ifdef CONFIG_CTRL_IFACE_MIB
+ 	} else if (os_strcmp(buf, "STA-FIRST") == 0) {
+ 		reply_len = ap_ctrl_iface_sta_first(wpa_s, reply, reply_size);
+ 	} else if (os_strncmp(buf, "STA ", 4) == 0) {
+@@ -13069,12 +13072,15 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ 	} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
+ 		reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply,
+ 						   reply_size);
++#endif
++#ifdef CONFIG_CTRL_IFACE_MIB
+ 	} else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
+ 		if (ap_ctrl_iface_sta_deauthenticate(wpa_s, buf + 15))
+ 			reply_len = -1;
+ 	} else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
+ 		if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13))
+ 			reply_len = -1;
++#endif
+ 	} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
+ 		if (ap_ctrl_iface_chanswitch(wpa_s, buf + 12))
+ 			reply_len = -1;
+@@ -13233,7 +13239,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ 		if (wpas_ctrl_iface_coloc_intf_report(wpa_s, buf + 18))
+ 			reply_len = -1;
+ #endif /* CONFIG_WNM */
+-#ifdef CONFIG_WNM_AP
++#if defined(CONFIG_AP) && defined(CONFIG_WNM_AP)
+ 	} else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
+ 		if (ap_ctrl_iface_disassoc_imminent(wpa_s, buf + 18))
+ 			reply_len = -1;
+@@ -13243,7 +13249,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ 	} else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
+ 		if (ap_ctrl_iface_bss_tm_req(wpa_s, buf + 11))
+ 			reply_len = -1;
+-#endif /* CONFIG_WNM_AP */
++#endif /* CONFIG_AP && CONFIG_WNM_AP */
+ 	} else if (os_strcmp(buf, "FLUSH") == 0) {
+ 		wpa_supplicant_ctrl_iface_flush(wpa_s);
+ 	} else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) {
+diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
+index 52befd8f1..ace6c5530 100644
+--- a/wpa_supplicant/defconfig
++++ b/wpa_supplicant/defconfig
+@@ -10,8 +10,8 @@
+ # to override previous values of the variables.
+ 
+ 
+-# Uncomment following two lines and fix the paths if you have installed OpenSSL
+-# or GnuTLS in non-default location
++# Uncomment following two lines and fix the paths if you have installed TLS
++# libraries in a non-default location
+ #CFLAGS += -I/usr/local/openssl/include
+ #LIBS += -L/usr/local/openssl/lib
+ 
+@@ -20,6 +20,7 @@
+ # used to fix build issues on such systems (krb5.h not found).
+ #CFLAGS += -I/usr/include/kerberos
+ 
++
+ # Driver interface for generic Linux wireless extensions
+ # Note: WEXT is deprecated in the current Linux kernel version and no new
+ # functionality is added to it. nl80211-based interface is the new
+@@ -329,6 +330,7 @@ CONFIG_BACKEND=file
+ # openssl = OpenSSL (default)
+ # gnutls = GnuTLS
+ # internal = Internal TLSv1 implementation (experimental)
++# mbedtls = mbed TLS
+ # linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
+ # none = Empty template
+ #CONFIG_TLS=openssl
+diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
+index 0c17aaea4..3d35757bf 100644
+--- a/wpa_supplicant/eapol_test.c
++++ b/wpa_supplicant/eapol_test.c
+@@ -31,7 +31,12 @@
+ #include "ctrl_iface.h"
+ #include "pcsc_funcs.h"
+ #include "wpas_glue.h"
++#include "drivers/driver.h"
+ 
++void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
++			     union wpa_event_data *data);
++void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
++			     union wpa_event_data *data);
+ 
+ const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
+ 
+@@ -1328,6 +1333,10 @@ static void usage(void)
+ 	       "option several times.\n");
+ }
+ 
++extern void supplicant_event(void *ctx, enum wpa_event_type event,
++			     union wpa_event_data *data);
++extern void supplicant_event_global(void *ctx, enum wpa_event_type event,
++			     union wpa_event_data *data);
+ 
+ int main(int argc, char *argv[])
+ {
+@@ -1351,6 +1360,8 @@ int main(int argc, char *argv[])
+ 	if (os_program_init())
+ 		return -1;
+ 
++	wpa_supplicant_event = supplicant_event;
++	wpa_supplicant_event_global = supplicant_event_global;
+ 	hostapd_logger_register_cb(hostapd_logger_cb);
+ 
+ 	os_memset(&eapol_test, 0, sizeof(eapol_test));
+diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
+index bb0e95ba4..5c08d4a19 100644
+--- a/wpa_supplicant/events.c
++++ b/wpa_supplicant/events.c
+@@ -6038,8 +6038,8 @@ static void wpas_link_reconfig(struct wpa_supplicant *wpa_s)
+ }
+ 
+ 
+-void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+-			  union wpa_event_data *data)
++void supplicant_event(void *ctx, enum wpa_event_type event,
++		      union wpa_event_data *data)
+ {
+ 	struct wpa_supplicant *wpa_s = ctx;
+ 	int resched;
+@@ -6074,6 +6074,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ 		event_to_string(event), event);
+ #endif /* CONFIG_NO_STDOUT_DEBUG */
+ 
++	wpas_ucode_event(wpa_s, event, data);
+ 	switch (event) {
+ 	case EVENT_AUTH:
+ #ifdef CONFIG_FST
+@@ -6991,7 +6992,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ }
+ 
+ 
+-void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
++void supplicant_event_global(void *ctx, enum wpa_event_type event,
+ 				 union wpa_event_data *data)
+ {
+ 	struct wpa_supplicant *wpa_s;
+diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c
+index 9229eb51f..ee152c5b9 100644
+--- a/wpa_supplicant/main.c
++++ b/wpa_supplicant/main.c
+@@ -12,6 +12,7 @@
+ #endif /* __linux__ */
+ 
+ #include "common.h"
++#include "build_features.h"
+ #include "crypto/crypto.h"
+ #include "fst/fst.h"
+ #include "wpa_supplicant_i.h"
+@@ -202,7 +203,7 @@ int main(int argc, char *argv[])
+ 
+ 	for (;;) {
+ 		c = getopt(argc, argv,
+-			   "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvW");
++			   "b:Bc:C:D:de:f:g:G:hi:I:KLMm:nNo:O:p:P:qsTtuv::W");
+ 		if (c < 0)
+ 			break;
+ 		switch (c) {
+@@ -267,6 +268,9 @@ int main(int argc, char *argv[])
+ 			params.conf_p2p_dev = optarg;
+ 			break;
+ #endif /* CONFIG_P2P */
++		case 'n':
++			iface_count = 0;
++			break;
+ 		case 'o':
+ 			params.override_driver = optarg;
+ 			break;
+@@ -302,8 +306,12 @@ int main(int argc, char *argv[])
+ 			break;
+ #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
+ 		case 'v':
+-			printf("%s\n", wpa_supplicant_version);
+-			exitcode = 0;
++			if (optarg) {
++				exitcode = !has_feature(optarg);
++			} else {
++				printf("%s\n", wpa_supplicant_version);
++				exitcode = 0;
++			}
+ 			goto out;
+ 		case 'W':
+ 			params.wait_for_monitor++;
+diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
+index 85c1ea8ba..dabbb0334 100644
+--- a/wpa_supplicant/mesh.c
++++ b/wpa_supplicant/mesh.c
+@@ -506,6 +506,8 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
+ 			   frequency);
+ 		goto out_free;
+ 	}
++	if (conf->noscan)
++		ssid->noscan = 1;
+ 
+ 	if (ssid->mesh_basic_rates == NULL) {
+ 		/*
+@@ -630,6 +632,7 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s,
+ 
+ 	params->meshid = ssid->ssid;
+ 	params->meshid_len = ssid->ssid_len;
++	params->mcast_rate = ssid->mcast_rate;
+ 	ibss_mesh_setup_freq(wpa_s, ssid, &params->freq);
+ 	wpa_s->mesh_ht_enabled = !!params->freq.ht_enabled;
+ 	wpa_s->mesh_vht_enabled = !!params->freq.vht_enabled;
+diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
+index af00e7910..b239410e8 100644
+--- a/wpa_supplicant/wpa_cli.c
++++ b/wpa_supplicant/wpa_cli.c
+@@ -26,6 +26,15 @@
+ #include <cutils/properties.h>
+ #endif /* ANDROID */
+ 
++#ifndef CONFIG_P2P
++#define CONFIG_P2P
++#endif
++#ifndef CONFIG_AP
++#define CONFIG_AP
++#endif
++#ifndef CONFIG_MESH
++#define CONFIG_MESH
++#endif
+ 
+ static const char *const wpa_cli_version =
+ "wpa_cli v" VERSION_STR "\n"
+diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
+index 88f3f2a52..92efe5629 100644
+--- a/wpa_supplicant/wpa_priv.c
++++ b/wpa_supplicant/wpa_priv.c
+@@ -1042,8 +1042,8 @@ static void wpa_priv_send_ft_response(struct wpa_priv_interface *iface,
+ }
+ 
+ 
+-void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+-			  union wpa_event_data *data)
++static void supplicant_event(void *ctx, enum wpa_event_type event,
++			     union wpa_event_data *data)
+ {
+ 	struct wpa_priv_interface *iface = ctx;
+ 
+@@ -1106,7 +1106,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ }
+ 
+ 
+-void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
++void supplicant_event_global(void *ctx, enum wpa_event_type event,
+ 				 union wpa_event_data *data)
+ {
+ 	struct wpa_priv_global *global = ctx;
+@@ -1220,6 +1220,8 @@ int main(int argc, char *argv[])
+ 	if (os_program_init())
+ 		return -1;
+ 
++	wpa_supplicant_event = supplicant_event;
++	wpa_supplicant_event_global = supplicant_event_global;
+ 	wpa_priv_fd_workaround();
+ 
+ 	os_memset(&global, 0, sizeof(global));
+diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
+index 1e77493ef..32b178560 100644
+--- a/wpa_supplicant/wpa_supplicant.c
++++ b/wpa_supplicant/wpa_supplicant.c
+@@ -1151,6 +1151,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
+ 		sme_sched_obss_scan(wpa_s, 0);
+ 	}
+ 	wpa_s->wpa_state = state;
++	wpas_ucode_update_state(wpa_s);
+ 
+ #ifdef CONFIG_BGSCAN
+ 	if (state == WPA_COMPLETED && wpa_s->current_ssid != wpa_s->bgscan_ssid)
+@@ -2831,7 +2832,7 @@ static int drv_supports_vht(struct wpa_supplicant *wpa_s,
+ }
+ 
+ 
+-static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode)
++static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode, bool dfs_enabled)
+ {
+ 	int i;
+ 
+@@ -2840,7 +2841,10 @@ static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode)
+ 
+ 		chan = hw_get_channel_chan(mode, i, NULL);
+ 		if (!chan ||
+-		    chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
++		    chan->flag & HOSTAPD_CHAN_DISABLED)
++			return false;
++		
++		if (!dfs_enabled && chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
+ 			return false;
+ 	}
+ 
+@@ -2900,7 +2904,7 @@ static bool ibss_mesh_can_use_vht(struct wpa_supplicant *wpa_s,
+ 				  const struct wpa_ssid *ssid,
+ 				  struct hostapd_hw_modes *mode)
+ {
+-	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
++	if (mode->mode != HOSTAPD_MODE_IEEE80211A && !(ssid->noscan))
+ 		return false;
+ 
+ 	if (!drv_supports_vht(wpa_s, ssid))
+@@ -2967,13 +2971,13 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s,
+ 				   const struct wpa_ssid *ssid,
+ 				   struct hostapd_hw_modes *mode,
+ 				   struct hostapd_freq_params *freq,
+-				   int obss_scan) {
++				   int obss_scan, bool dfs_enabled) {
+ 	int chan_idx;
+ 	struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
+ 	int i, res;
+ 	unsigned int j;
+ 	static const int ht40plus[] = {
+-		36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
++		1, 2, 3, 4, 5, 6, 7, 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
+ 		149, 157, 165, 173, 184, 192
+ 	};
+ 	int ht40 = -1;
+@@ -2991,8 +2995,11 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s,
+ 		return;
+ 
+ 	/* Check primary channel flags */
+-	if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
++	if (pri_chan->flag & HOSTAPD_CHAN_DISABLED)
+ 		return;
++	if (pri_chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
++		if (!dfs_enabled)
++			return;
+ 
+ #ifdef CONFIG_HT_OVERRIDES
+ 	if (ssid->disable_ht40)
+@@ -3018,8 +3025,11 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s,
+ 		return;
+ 
+ 	/* Check secondary channel flags */
+-	if (sec_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
++	if (sec_chan->flag & HOSTAPD_CHAN_DISABLED)
+ 		return;
++	if (sec_chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
++		if (!dfs_enabled)
++			return;
+ 
+ 	if (ht40 == -1) {
+ 		if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS))
+@@ -3074,7 +3084,7 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
+ 				       const struct wpa_ssid *ssid,
+ 				       struct hostapd_hw_modes *mode,
+ 				       struct hostapd_freq_params *freq,
+-				       int ieee80211_mode, bool is_6ghz) {
++				       int ieee80211_mode, bool is_6ghz, bool dfs_enabled) {
+ 	static const int bw80[] = {
+ 		5180, 5260, 5500, 5580, 5660, 5745, 5825,
+ 		5955, 6035, 6115, 6195, 6275, 6355, 6435,
+@@ -3119,7 +3129,7 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
+ 		goto skip_80mhz;
+ 
+ 	/* Use 40 MHz if channel not usable */
+-	if (!ibss_mesh_is_80mhz_avail(channel, mode))
++	if (!ibss_mesh_is_80mhz_avail(channel, mode, dfs_enabled))
+ 		goto skip_80mhz;
+ 
+ 	chwidth = CONF_OPER_CHWIDTH_80MHZ;
+@@ -3133,7 +3143,7 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
+ 	if ((mode->he_capab[ieee80211_mode].phy_cap[
+ 		     HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+ 	     HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G) && is_6ghz &&
+-	    ibss_mesh_is_80mhz_avail(channel + 16, mode)) {
++	    ibss_mesh_is_80mhz_avail(channel + 16, mode, dfs_enabled)) {
+ 		for (j = 0; j < ARRAY_SIZE(bw160); j++) {
+ 			if (freq->freq == bw160[j]) {
+ 				chwidth = CONF_OPER_CHWIDTH_160MHZ;
+@@ -3161,10 +3171,12 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
+ 				if (!chan)
+ 					continue;
+ 
+-				if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+-						  HOSTAPD_CHAN_NO_IR |
+-						  HOSTAPD_CHAN_RADAR))
++				if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ 					continue;
++				if (chan->flag & (HOSTAPD_CHAN_RADAR |
++						  HOSTAPD_CHAN_NO_IR))
++					if (!dfs_enabled)
++						continue;
+ 
+ 				/* Found a suitable second segment for 80+80 */
+ 				chwidth = CONF_OPER_CHWIDTH_80P80MHZ;
+@@ -3216,12 +3228,17 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
+ 	int ieee80211_mode = wpas_mode_to_ieee80211_mode(ssid->mode);
+ 	enum hostapd_hw_mode hw_mode;
+ 	struct hostapd_hw_modes *mode = NULL;
+-	int obss_scan = 1;
++	int obss_scan = !(ssid->noscan);
+ 	u8 channel;
+ 	bool is_6ghz, is_24ghz;
++	bool dfs_enabled = wpa_s->conf->country[0] && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_RADAR);
+ 
+ 	freq->freq = ssid->frequency;
+ 
++	if (ssid->fixed_freq) {
++		obss_scan = 0;
++	}
++
+ 	if (ssid->mode == WPAS_MODE_IBSS && !ssid->fixed_freq) {
+ 		struct wpa_bss *bss = ibss_find_existing_bss(wpa_s, ssid);
+ 
+@@ -3259,11 +3276,13 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
+ 		freq->he_enabled = ibss_mesh_can_use_he(wpa_s, ssid, mode,
+ 							ieee80211_mode);
+ 	freq->channel = channel;
++	if (mode->mode == HOSTAPD_MODE_IEEE80211G && ssid->noscan)
++		ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan, dfs_enabled);
+ 	/* Setup higher BW only for 5 GHz */
+ 	if (mode->mode == HOSTAPD_MODE_IEEE80211A) {
+-		ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan);
++		ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan, dfs_enabled);
+ 		if (!ibss_mesh_select_80_160mhz(wpa_s, ssid, mode, freq,
+-						ieee80211_mode, is_6ghz))
++						ieee80211_mode, is_6ghz, dfs_enabled))
+ 			freq->he_enabled = freq->vht_enabled = false;
+ 	}
+ 
+@@ -4449,6 +4468,12 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
+ 			params.beacon_int = ssid->beacon_int;
+ 		else
+ 			params.beacon_int = wpa_s->conf->beacon_int;
++		int i = 0;
++		while (i < WLAN_SUPP_RATES_MAX) {
++			params.rates[i] = ssid->rates[i];
++			i++;
++		}
++		params.mcast_rate = ssid->mcast_rate;
+ 	}
+ 
+ 	if (bss && ssid->enable_edmg)
+@@ -6093,7 +6118,7 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent)
+ 	if (wpa_s == NULL)
+ 		return NULL;
+ 	wpa_s->scan_req = INITIAL_SCAN_REQ;
+-	wpa_s->scan_interval = 5;
++	wpa_s->scan_interval = 1;
+ 	wpa_s->new_connection = 1;
+ 	wpa_s->parent = parent ? parent : wpa_s;
+ 	wpa_s->p2pdev = wpa_s->parent;
+@@ -7809,7 +7834,6 @@ struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global,
+ 	return NULL;
+ }
+ 
+-
+ /**
+  * wpa_supplicant_match_existing - Match existing interfaces
+  * @global: Pointer to global data from wpa_supplicant_init()
+@@ -7844,6 +7868,11 @@ static int wpa_supplicant_match_existing(struct wpa_global *global)
+ 
+ #endif /* CONFIG_MATCH_IFACE */
+ 
++extern void supplicant_event(void *ctx, enum wpa_event_type event,
++			     union wpa_event_data *data);
++
++extern void supplicant_event_global(void *ctx, enum wpa_event_type event,
++ 				 union wpa_event_data *data);
+ 
+ /**
+  * wpa_supplicant_add_iface - Add a new network interface
+@@ -7926,6 +7955,9 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
+ 	}
+ #endif /* CONFIG_P2P */
+ 
++	wpas_ubus_add_bss(wpa_s);
++	wpas_ucode_add_bss(wpa_s);
++
+ 	return wpa_s;
+ }
+ 
+@@ -7952,6 +7984,9 @@ int wpa_supplicant_remove_iface(struct wpa_global *global,
+ 	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 */
+ 	prev = global->ifaces;
+ 	if (prev == wpa_s) {
+@@ -8100,6 +8135,8 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
+ #ifndef CONFIG_NO_WPA_MSG
+ 	wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
+ #endif /* CONFIG_NO_WPA_MSG */
++	wpa_supplicant_event = supplicant_event;
++	wpa_supplicant_event_global = supplicant_event_global;
+ 
+ 	if (params->wpa_debug_file_path)
+ 		wpa_debug_open_file(params->wpa_debug_file_path);
+@@ -8258,6 +8295,7 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
+ 
+ 	eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+ 			       wpas_periodic, global, NULL);
++	wpas_ucode_init(global);
+ 
+ 	return global;
+ }
+@@ -8330,6 +8368,8 @@ void wpa_supplicant_deinit(struct wpa_global *global)
+ 
+ 	wpas_notify_supplicant_deinitialized(global);
+ 
++	wpas_ucode_free();
++
+ 	eap_peer_unregister_methods();
+ #ifdef CONFIG_AP
+ 	eap_server_unregister_methods();
+diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
+index 48ec95fa5..952a3bd5a 100644
+--- a/wpa_supplicant/wpa_supplicant_i.h
++++ b/wpa_supplicant/wpa_supplicant_i.h
+@@ -21,6 +21,8 @@
+ #include "config_ssid.h"
+ #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;
+@@ -319,6 +321,8 @@ struct wpa_global {
+ #endif /* CONFIG_WIFI_DISPLAY */
+ 
+ 	struct psk_list_entry *add_psk; /* From group formation */
++
++	struct ubus_object ubus_global;
+ };
+ 
+ 
+@@ -693,6 +697,8 @@ struct wpa_supplicant {
+ 	unsigned char own_addr[ETH_ALEN];
+ 	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 */
+diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
+index 7b9cf7f9e..03748f4bb 100644
+--- a/wpa_supplicant/wps_supplicant.c
++++ b/wpa_supplicant/wps_supplicant.c
+@@ -33,6 +33,7 @@
+ #include "p2p/p2p.h"
+ #include "p2p_supplicant.h"
+ #include "wps_supplicant.h"
++#include "ubus.h"
+ 
+ 
+ #ifndef WPS_PIN_SCAN_IGNORE_SEL_REG
+@@ -401,6 +402,8 @@ static int wpa_supplicant_wps_cred(void *ctx,
+ 	wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
+ 			cred->cred_attr, cred->cred_attr_len);
+ 
++	wpas_ubus_notify(wpa_s, cred);
++
+ 	if (wpa_s->conf->wps_cred_processing == 1)
+ 		return 0;
+ 
+diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
+index aae3f7cb5..30b4e9105 100644
+--- a/wpa_supplicant/wps_supplicant.h
++++ b/wpa_supplicant/wps_supplicant.h
+@@ -9,6 +9,7 @@
+ #ifndef WPS_SUPPLICANT_H
+ #define WPS_SUPPLICANT_H
+ 
++struct wpa_bss;
+ struct wpa_scan_results;
+ 
+ #ifdef CONFIG_WPS
+@@ -16,8 +17,6 @@ struct wpa_scan_results;
+ #include "wps/wps.h"
+ #include "wps/wps_defs.h"
+ 
+-struct wpa_bss;
+-
+ struct wps_new_ap_settings {
+ 	const char *ssid_hex;
+ 	const char *auth;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0011-hostapd-MLO-move-mgmt-and-control-port-Tx-status-to-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0011-hostapd-MLO-move-mgmt-and-control-port-Tx-status-to-.patch
deleted file mode 100644
index 1d9c2fd..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0011-hostapd-MLO-move-mgmt-and-control-port-Tx-status-to-.patch
+++ /dev/null
@@ -1,136 +0,0 @@
-From 0b72d2a8002e79886433ee85fd23661ec4d3d731 Mon Sep 17 00:00:00 2001
-From: Sriram R <quic_srirrama@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:41 +0530
-Subject: [PATCH 011/104] hostapd: MLO: move mgmt and control port Tx status to
- per BSS handling
-
-Currently management and control port transmit status is handled on drv's
-first BSS only. However to support multiple MLDs there is requirement to
-handle it in on a given BSS.
-
-Add changes to use the passed BSS instead of always going with drv's first
-BSS.
-
-Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/drivers/driver_nl80211_event.c | 25 +++++++++++++------------
- 1 file changed, 13 insertions(+), 12 deletions(-)
-
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 1ca8b5bce..f5778cdaf 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -22,7 +22,7 @@
- 
- 
- static void
--nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
-+nl80211_control_port_frame_tx_status(struct i802_bss *bss,
- 				     const u8 *frame, size_t len,
- 				     struct nlattr *ack, struct nlattr *cookie);
- 
-@@ -1374,12 +1374,13 @@ static void mlme_event_mgmt(struct i802_bss *bss,
- }
- 
- 
--static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
-+static void mlme_event_mgmt_tx_status(struct i802_bss *bss,
- 				      struct nlattr *cookie, const u8 *frame,
- 				      size_t len, struct nlattr *ack)
- {
- 	union wpa_event_data event;
- 	const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) frame;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
- 	u16 fc = le_to_host16(hdr->frame_control);
- 	u64 cookie_val = 0;
- 
-@@ -1398,7 +1399,7 @@ static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
- 	    WPA_GET_BE16(frame + 2 * ETH_ALEN) == ETH_P_PAE) {
- 		wpa_printf(MSG_DEBUG,
- 			   "nl80211: Work around misdelivered control port TX status for EAPOL");
--		nl80211_control_port_frame_tx_status(drv, frame, len, ack,
-+		nl80211_control_port_frame_tx_status(bss, frame, len, ack,
- 						     cookie);
- 		return;
- 	}
-@@ -1434,7 +1435,7 @@ static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
- 	event.tx_status.ack = ack != NULL;
- 	event.tx_status.link_id = cookie_val == drv->send_frame_cookie ?
- 		drv->send_frame_link_id : NL80211_DRV_LINK_ID_NA;
--	wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
-+	wpa_supplicant_event(bss->ctx, EVENT_TX_STATUS, &event);
- }
- 
- 
-@@ -1742,7 +1743,7 @@ static void mlme_event(struct i802_bss *bss,
- 				nla_len(frame), link_id);
- 		break;
- 	case NL80211_CMD_FRAME_TX_STATUS:
--		mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame),
-+		mlme_event_mgmt_tx_status(bss, cookie, nla_data(frame),
- 					  nla_len(frame), ack);
- 		break;
- 	case NL80211_CMD_UNPROT_DEAUTHENTICATE:
-@@ -3652,8 +3653,7 @@ static void nl80211_sta_opmode_change_event(struct wpa_driver_nl80211_data *drv,
- }
- 
- 
--static void nl80211_control_port_frame(struct wpa_driver_nl80211_data *drv,
--				       struct nlattr **tb)
-+static void nl80211_control_port_frame(struct i802_bss *bss, struct nlattr **tb)
- {
- 	u8 *src_addr;
- 	u16 ethertype;
-@@ -3682,7 +3682,7 @@ static void nl80211_control_port_frame(struct wpa_driver_nl80211_data *drv,
- 			   MAC2STR(src_addr));
- 		break;
- 	case ETH_P_PAE:
--		drv_event_eapol_rx2(drv->ctx, src_addr,
-+		drv_event_eapol_rx2(bss->ctx, src_addr,
- 				    nla_data(tb[NL80211_ATTR_FRAME]),
- 				    nla_len(tb[NL80211_ATTR_FRAME]),
- 				    encrypted, link_id);
-@@ -3698,10 +3698,11 @@ static void nl80211_control_port_frame(struct wpa_driver_nl80211_data *drv,
- 
- 
- static void
--nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
-+nl80211_control_port_frame_tx_status(struct i802_bss *bss,
- 				     const u8 *frame, size_t len,
- 				     struct nlattr *ack, struct nlattr *cookie)
- {
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
- 	union wpa_event_data event;
- 
- 	if (!cookie || len < ETH_HLEN)
-@@ -3720,7 +3721,7 @@ nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
- 		nla_get_u64(cookie) == drv->eapol_tx_cookie ?
- 		drv->eapol_tx_link_id : NL80211_DRV_LINK_ID_NA;
- 
--	wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
-+	wpa_supplicant_event(bss->ctx, EVENT_EAPOL_TX_STATUS, &event);
- }
- 
- 
-@@ -4065,7 +4066,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
- 	case NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS:
- 		if (!frame)
- 			break;
--		nl80211_control_port_frame_tx_status(drv,
-+		nl80211_control_port_frame_tx_status(bss,
- 						     nla_data(frame),
- 						     nla_len(frame),
- 						     tb[NL80211_ATTR_ACK],
-@@ -4238,7 +4239,7 @@ int process_bss_event(struct nl_msg *msg, void *arg)
- 		nl80211_external_auth(bss->drv, tb);
- 		break;
- 	case NL80211_CMD_CONTROL_PORT_FRAME:
--		nl80211_control_port_frame(bss->drv, tb);
-+		nl80211_control_port_frame(bss, tb);
- 		break;
- 	default:
- 		wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0011-mtk-hostapd-sync-with-wireless-next.git-include-uapi.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0011-mtk-hostapd-sync-with-wireless-next.git-include-uapi.patch
new file mode 100644
index 0000000..ffd8333
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0011-mtk-hostapd-sync-with-wireless-next.git-include-uapi.patch
@@ -0,0 +1,1274 @@
+From 02fb89a020d0bab2eac528f3d49379b535ebeda2 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 31 Jul 2024 10:42:08 +0800
+Subject: [PATCH 011/126] mtk: hostapd: sync with wireless-next.git
+ include/uapi/linux/nl80211.h
+
+This brings in nl80211 definitions as of 2024-07-11.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ src/drivers/driver_nl80211_event.c |   1 +
+ src/drivers/nl80211_copy.h         | 537 ++++++++++++++++++++---------
+ 2 files changed, 370 insertions(+), 168 deletions(-)
+
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 768c72905..1787dbd99 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -186,6 +186,7 @@ static const char * nl80211_command_to_string(enum nl80211_commands cmd)
+ 	C2S(NL80211_CMD_REMOVE_LINK_STA)
+ 	C2S(NL80211_CMD_SET_HW_TIMESTAMP)
+ 	C2S(NL80211_CMD_LINKS_REMOVED)
++	C2S(NL80211_CMD_SET_TID_TO_LINK_MAPPING)
+ 	C2S(__NL80211_CMD_AFTER_LAST)
+ 	}
+ #undef C2S
+diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
+index dced2c49d..f97f5adc8 100644
+--- a/src/drivers/nl80211_copy.h
++++ b/src/drivers/nl80211_copy.h
+@@ -11,7 +11,7 @@
+  * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
+  * Copyright 2008 Colin McCabe <colin@cozybit.com>
+  * Copyright 2015-2017	Intel Deutschland GmbH
+- * Copyright (C) 2018-2023 Intel Corporation
++ * Copyright (C) 2018-2024 Intel Corporation
+  *
+  * Permission to use, copy, modify, and/or distribute this software for any
+  * purpose with or without fee is hereby granted, provided that the above
+@@ -72,7 +72,7 @@
+  * For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS
+  * and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows:
+  *  - a setup station entry is added, not yet authorized, without any rate
+- *    or capability information, this just exists to avoid race conditions
++ *    or capability information; this just exists to avoid race conditions
+  *  - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid
+  *    to add rate and capability information to the station and at the same
+  *    time mark it authorized.
+@@ -87,7 +87,7 @@
+  * DOC: Frame transmission/registration support
+  *
+  * Frame transmission and registration support exists to allow userspace
+- * management entities such as wpa_supplicant react to management frames
++ * management entities such as wpa_supplicant to react to management frames
+  * that are not being handled by the kernel. This includes, for example,
+  * certain classes of action frames that cannot be handled in the kernel
+  * for various reasons.
+@@ -113,7 +113,7 @@
+  *
+  * Frame transmission allows userspace to send for example the required
+  * responses to action frames. It is subject to some sanity checking,
+- * but many frames can be transmitted. When a frame was transmitted, its
++ * but many frames can be transmitted. When a frame is transmitted, its
+  * status is indicated to the sending socket.
+  *
+  * For more technical details, see the corresponding command descriptions
+@@ -123,7 +123,7 @@
+ /**
+  * DOC: Virtual interface / concurrency capabilities
+  *
+- * Some devices are able to operate with virtual MACs, they can have
++ * Some devices are able to operate with virtual MACs; they can have
+  * more than one virtual interface. The capability handling for this
+  * is a bit complex though, as there may be a number of restrictions
+  * on the types of concurrency that are supported.
+@@ -135,7 +135,7 @@
+  * Once concurrency is desired, more attributes must be observed:
+  * To start with, since some interface types are purely managed in
+  * software, like the AP-VLAN type in mac80211 for example, there's
+- * an additional list of these, they can be added at any time and
++ * an additional list of these; they can be added at any time and
+  * are only restricted by some semantic restrictions (e.g. AP-VLAN
+  * cannot be added without a corresponding AP interface). This list
+  * is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute.
+@@ -164,7 +164,7 @@
+  * Packet coalesce feature helps to reduce number of received interrupts
+  * to host by buffering these packets in firmware/hardware for some
+  * predefined time. Received interrupt will be generated when one of the
+- * following events occur.
++ * following events occurs.
+  * a) Expiration of hardware timer whose expiration time is set to maximum
+  * coalescing delay of matching coalesce rule.
+  * b) Coalescing buffer in hardware reaches its limit.
+@@ -174,7 +174,7 @@
+  * rule.
+  * a) Maximum coalescing delay
+  * b) List of packet patterns which needs to be matched
+- * c) Condition for coalescence. pattern 'match' or 'no match'
++ * c) Condition for coalescence: pattern 'match' or 'no match'
+  * Multiple such rules can be created.
+  */
+ 
+@@ -213,7 +213,7 @@
+ /**
+  * DOC: FILS shared key authentication offload
+  *
+- * FILS shared key authentication offload can be advertized by drivers by
++ * FILS shared key authentication offload can be advertised by drivers by
+  * setting @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD flag. The drivers that support
+  * FILS shared key authentication offload should be able to construct the
+  * authentication and association frames for FILS shared key authentication and
+@@ -239,7 +239,7 @@
+  * The PMKSA can be maintained in userspace persistently so that it can be used
+  * later after reboots or wifi turn off/on also.
+  *
+- * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertized by a FILS
++ * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertised by a FILS
+  * capable AP supporting PMK caching. It specifies the scope within which the
+  * PMKSAs are cached in an ESS. %NL80211_CMD_SET_PMKSA and
+  * %NL80211_CMD_DEL_PMKSA are enhanced to allow support for PMKSA caching based
+@@ -290,12 +290,12 @@
+  * If the configuration needs to be applied for specific peer then the MAC
+  * address of the peer needs to be passed in %NL80211_ATTR_MAC, otherwise the
+  * configuration will be applied for all the connected peers in the vif except
+- * any peers that have peer specific configuration for the TID by default; if
+- * the %NL80211_TID_CONFIG_ATTR_OVERRIDE flag is set, peer specific values
++ * any peers that have peer-specific configuration for the TID by default; if
++ * the %NL80211_TID_CONFIG_ATTR_OVERRIDE flag is set, peer-specific values
+  * will be overwritten.
+  *
+- * All this configuration is valid only for STA's current connection
+- * i.e. the configuration will be reset to default when the STA connects back
++ * All this configuration is valid only for STA's current connection,
++ * i.e., the configuration will be reset to default when the STA connects back
+  * after disconnection/roaming, and this configuration will be cleared when
+  * the interface goes down.
+  */
+@@ -413,8 +413,8 @@
+  *	are like for %NL80211_CMD_SET_BEACON, and additionally parameters that
+  *	do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL,
+  *	%NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID,
+- *	%NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE,
+- *	%NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS,
++ *	%NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
++ *	%NL80211_ATTR_CIPHER_SUITE_GROUP, %NL80211_ATTR_WPA_VERSIONS,
+  *	%NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY,
+  *	%NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT,
+  *	%NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS.
+@@ -438,23 +438,19 @@
+  *	%NL80211_ATTR_REASON_CODE can optionally be used to specify which type
+  *	of disconnection indication should be sent to the station
+  *	(Deauthentication or Disassociation frame and reason code for that
+- *	frame).
++ *	frame). %NL80211_ATTR_MLO_LINK_ID can be used optionally to remove
++ *	stations connected and using at least that link as one of its links.
+  *
+  * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
+- * 	destination %NL80211_ATTR_MAC on the interface identified by
+- * 	%NL80211_ATTR_IFINDEX.
++ *	destination %NL80211_ATTR_MAC on the interface identified by
++ *	%NL80211_ATTR_IFINDEX.
+  * @NL80211_CMD_SET_MPATH:  Set mesh path attributes for mesh path to
+- * 	destination %NL80211_ATTR_MAC on the interface identified by
+- * 	%NL80211_ATTR_IFINDEX.
++ *	destination %NL80211_ATTR_MAC on the interface identified by
++ *	%NL80211_ATTR_IFINDEX.
+  * @NL80211_CMD_NEW_MPATH: Create a new mesh path for the destination given by
+  *	%NL80211_ATTR_MAC via %NL80211_ATTR_MPATH_NEXT_HOP.
+  * @NL80211_CMD_DEL_MPATH: Delete a mesh path to the destination given by
+  *	%NL80211_ATTR_MAC.
+- * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the
+- *	interface identified by %NL80211_ATTR_IFINDEX.
+- * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC
+- *	or, if no MAC address given, all mesh paths, on the interface identified
+- *	by %NL80211_ATTR_IFINDEX.
+  * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by
+  *	%NL80211_ATTR_IFINDEX.
+  *
+@@ -475,15 +471,15 @@
+  *	after being queried by the kernel. CRDA replies by sending a regulatory
+  *	domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our
+  *	current alpha2 if it found a match. It also provides
+- * 	NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each
+- * 	regulatory rule is a nested set of attributes  given by
+- * 	%NL80211_ATTR_REG_RULE_FREQ_[START|END] and
+- * 	%NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by
+- * 	%NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and
+- * 	%NL80211_ATTR_REG_RULE_POWER_MAX_EIRP.
++ *	NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each
++ *	regulatory rule is a nested set of attributes  given by
++ *	%NL80211_ATTR_REG_RULE_FREQ_[START|END] and
++ *	%NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by
++ *	%NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and
++ *	%NL80211_ATTR_REG_RULE_POWER_MAX_EIRP.
+  * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain
+- * 	to the specified ISO/IEC 3166-1 alpha2 country code. The core will
+- * 	store this as a valid request and then query userspace for it.
++ *	to the specified ISO/IEC 3166-1 alpha2 country code. The core will
++ *	store this as a valid request and then query userspace for it.
+  *
+  * @NL80211_CMD_GET_MESH_CONFIG: Get mesh networking properties for the
+  *	interface identified by %NL80211_ATTR_IFINDEX
+@@ -521,7 +517,7 @@
+  *	%NL80211_ATTR_SCHED_SCAN_PLANS. If %NL80211_ATTR_SCHED_SCAN_PLANS is
+  *	not specified and only %NL80211_ATTR_SCHED_SCAN_INTERVAL is specified,
+  *	scheduled scan will run in an infinite loop with the specified interval.
+- *	These attributes are mutually exculsive,
++ *	These attributes are mutually exclusive,
+  *	i.e. NL80211_ATTR_SCHED_SCAN_INTERVAL must not be passed if
+  *	NL80211_ATTR_SCHED_SCAN_PLANS is defined.
+  *	If for some reason scheduled scan is aborted by the driver, all scan
+@@ -552,7 +548,7 @@
+  *	%NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface
+  *	is brought down while a scheduled scan was running.
+  *
+- * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation
++ * @NL80211_CMD_GET_SURVEY: get survey results, e.g. channel occupation
+  *      or noise level
+  * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to
+  *	NL80211_CMD_GET_SURVEY and on the "scan" multicast group)
+@@ -563,40 +559,41 @@
+  *	using %NL80211_ATTR_SSID, %NL80211_ATTR_FILS_CACHE_ID,
+  *	%NL80211_ATTR_PMKID, and %NL80211_ATTR_PMK in case of FILS
+  *	authentication where %NL80211_ATTR_FILS_CACHE_ID is the identifier
+- *	advertized by a FILS capable AP identifying the scope of PMKSA in an
++ *	advertised by a FILS capable AP identifying the scope of PMKSA in an
+  *	ESS.
+  * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC
+  *	(for the BSSID) and %NL80211_ATTR_PMKID or using %NL80211_ATTR_SSID,
+  *	%NL80211_ATTR_FILS_CACHE_ID, and %NL80211_ATTR_PMKID in case of FILS
+- *	authentication.
++ *	authentication. Additionally in case of SAE offload and OWE offloads
++ *	PMKSA entry can be deleted using %NL80211_ATTR_SSID.
+  * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries.
+  *
+  * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain
+- * 	has been changed and provides details of the request information
+- * 	that caused the change such as who initiated the regulatory request
+- * 	(%NL80211_ATTR_REG_INITIATOR), the wiphy_idx
+- * 	(%NL80211_ATTR_REG_ALPHA2) on which the request was made from if
+- * 	the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or
+- * 	%NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain
+- * 	set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is
+- * 	%NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on
+- * 	to (%NL80211_ATTR_REG_ALPHA2).
++ *	has been changed and provides details of the request information
++ *	that caused the change such as who initiated the regulatory request
++ *	(%NL80211_ATTR_REG_INITIATOR), the wiphy_idx
++ *	(%NL80211_ATTR_REG_ALPHA2) on which the request was made from if
++ *	the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or
++ *	%NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain
++ *	set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is
++ *	%NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on
++ *	to (%NL80211_ATTR_REG_ALPHA2).
+  * @NL80211_CMD_REG_BEACON_HINT: indicates to userspace that an AP beacon
+- * 	has been found while world roaming thus enabling active scan or
+- * 	any mode of operation that initiates TX (beacons) on a channel
+- * 	where we would not have been able to do either before. As an example
+- * 	if you are world roaming (regulatory domain set to world or if your
+- * 	driver is using a custom world roaming regulatory domain) and while
+- * 	doing a passive scan on the 5 GHz band you find an AP there (if not
+- * 	on a DFS channel) you will now be able to actively scan for that AP
+- * 	or use AP mode on your card on that same channel. Note that this will
+- * 	never be used for channels 1-11 on the 2 GHz band as they are always
+- * 	enabled world wide. This beacon hint is only sent if your device had
+- * 	either disabled active scanning or beaconing on a channel. We send to
+- * 	userspace the wiphy on which we removed a restriction from
+- * 	(%NL80211_ATTR_WIPHY) and the channel on which this occurred
+- * 	before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER)
+- * 	the beacon hint was processed.
++ *	has been found while world roaming thus enabling active scan or
++ *	any mode of operation that initiates TX (beacons) on a channel
++ *	where we would not have been able to do either before. As an example
++ *	if you are world roaming (regulatory domain set to world or if your
++ *	driver is using a custom world roaming regulatory domain) and while
++ *	doing a passive scan on the 5 GHz band you find an AP there (if not
++ *	on a DFS channel) you will now be able to actively scan for that AP
++ *	or use AP mode on your card on that same channel. Note that this will
++ *	never be used for channels 1-11 on the 2 GHz band as they are always
++ *	enabled world wide. This beacon hint is only sent if your device had
++ *	either disabled active scanning or beaconing on a channel. We send to
++ *	userspace the wiphy on which we removed a restriction from
++ *	(%NL80211_ATTR_WIPHY) and the channel on which this occurred
++ *	before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER)
++ *	the beacon hint was processed.
+  *
+  * @NL80211_CMD_AUTHENTICATE: authentication request and notification.
+  *	This command is used both as a command (request to authenticate) and
+@@ -607,7 +604,7 @@
+  *	BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify
+  *	the SSID (mainly for association, but is included in authentication
+  *	request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ +
+- *	%NL80211_ATTR_WIPHY_FREQ_OFFSET is used to specify the frequence of the
++ *	%NL80211_ATTR_WIPHY_FREQ_OFFSET is used to specify the frequency of the
+  *	channel in MHz. %NL80211_ATTR_AUTH_TYPE is used to specify the
+  *	authentication type. %NL80211_ATTR_IE is used to define IEs
+  *	(VendorSpecificInfo, but also including RSN IE and FT IEs) to be added
+@@ -816,7 +813,7 @@
+  *	reached.
+  * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ
+  *	and the attributes determining channel width) the given interface
+- *	(identifed by %NL80211_ATTR_IFINDEX) shall operate on.
++ *	(identified by %NL80211_ATTR_IFINDEX) shall operate on.
+  *	In case multiple channels are supported by the device, the mechanism
+  *	with which it switches channels is implementation-defined.
+  *	When a monitor interface is given, it can only switch channel while
+@@ -888,7 +885,7 @@
+  *	inform userspace of the new replay counter.
+  *
+  * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace
+- *	of PMKSA caching dandidates.
++ *	of PMKSA caching candidates.
+  *
+  * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup).
+  *	In addition, this can be used as an event to request userspace to take
+@@ -924,7 +921,7 @@
+  *
+  * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface
+  *	by sending a null data frame to it and reporting when the frame is
+- *	acknowleged. This is used to allow timing out inactive clients. Uses
++ *	acknowledged. This is used to allow timing out inactive clients. Uses
+  *	%NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a
+  *	direct reply with an %NL80211_ATTR_COOKIE that is later used to match
+  *	up the event with the request. The event includes the same data and
+@@ -1118,7 +1115,7 @@
+  *	current configuration is not changed.  If it is present but
+  *	set to zero, the configuration is changed to don't-care
+  *	(i.e. the device can decide what to do).
+- * @NL80211_CMD_NAN_FUNC_MATCH: Notification sent when a match is reported.
++ * @NL80211_CMD_NAN_MATCH: Notification sent when a match is reported.
+  *	This will contain a %NL80211_ATTR_NAN_MATCH nested attribute and
+  *	%NL80211_ATTR_COOKIE.
+  *
+@@ -1135,11 +1132,15 @@
+  * @NL80211_CMD_DEL_PMK: For offloaded 4-Way handshake, delete the previously
+  *	configured PMK for the authenticator address identified by
+  *	%NL80211_ATTR_MAC.
+- * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates an 802.1X FT roam was
+- *	completed successfully. Drivers that support 4 way handshake offload
+- *	should send this event after indicating 802.1X FT assocation with
+- *	%NL80211_CMD_ROAM. If the 4 way handshake failed %NL80211_CMD_DISCONNECT
+- *	should be indicated instead.
++ * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates port is authorized and
++ *	open for regular data traffic. For STA/P2P-client, this event is sent
++ *	with AP MAC address and for AP/P2P-GO, the event carries the STA/P2P-
++ *	client MAC address.
++ *	Drivers that support 4 way handshake offload should send this event for
++ *	STA/P2P-client after successful 4-way HS or after 802.1X FT following
++ *	NL80211_CMD_CONNECT or NL80211_CMD_ROAM. Drivers using AP/P2P-GO 4-way
++ *	handshake offload should send this event on successful completion of
++ *	4-way handshake with the peer (STA/P2P-client).
+  * @NL80211_CMD_CONTROL_PORT_FRAME: Control Port (e.g. PAE) frame TX request
+  *	and RX notification.  This command is used both as a request to transmit
+  *	a control port frame and as a notification that a control port frame
+@@ -1323,6 +1324,11 @@
+  *	Multi-Link reconfiguration. %NL80211_ATTR_MLO_LINKS is used to provide
+  *	information about the removed STA MLD setup links.
+  *
++ * @NL80211_CMD_SET_TID_TO_LINK_MAPPING: Set the TID to Link Mapping for a
++ *      non-AP MLD station. The %NL80211_ATTR_MLO_TTLM_DLINK and
++ *      %NL80211_ATTR_MLO_TTLM_ULINK attributes are used to specify the
++ *      TID to Link mapping for downlink/uplink traffic.
++ *
+  * @NL80211_CMD_MAX: highest used command number
+  * @__NL80211_CMD_AFTER_LAST: internal use
+  */
+@@ -1578,6 +1584,8 @@ enum nl80211_commands {
+ 
+ 	NL80211_CMD_LINKS_REMOVED,
+ 
++	NL80211_CMD_SET_TID_TO_LINK_MAPPING,
++
+ 	/* add new commands above here */
+ 
+ 	/* used to define NL80211_CMD_MAX below */
+@@ -1702,21 +1710,21 @@ enum nl80211_commands {
+  *	(see &enum nl80211_plink_action).
+  * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path.
+  * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path
+- * 	info given for %NL80211_CMD_GET_MPATH, nested attribute described at
++ *	info given for %NL80211_CMD_GET_MPATH, nested attribute described at
+  *	&enum nl80211_mpath_info.
+  *
+  * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of
+  *      &enum nl80211_mntr_flags.
+  *
+  * @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the
+- * 	current regulatory domain should be set to or is already set to.
+- * 	For example, 'CR', for Costa Rica. This attribute is used by the kernel
+- * 	to query the CRDA to retrieve one regulatory domain. This attribute can
+- * 	also be used by userspace to query the kernel for the currently set
+- * 	regulatory domain. We chose an alpha2 as that is also used by the
+- * 	IEEE-802.11 country information element to identify a country.
+- * 	Users can also simply ask the wireless core to set regulatory domain
+- * 	to a specific alpha2.
++ *	current regulatory domain should be set to or is already set to.
++ *	For example, 'CR', for Costa Rica. This attribute is used by the kernel
++ *	to query the CRDA to retrieve one regulatory domain. This attribute can
++ *	also be used by userspace to query the kernel for the currently set
++ *	regulatory domain. We chose an alpha2 as that is also used by the
++ *	IEEE-802.11 country information element to identify a country.
++ *	Users can also simply ask the wireless core to set regulatory domain
++ *	to a specific alpha2.
+  * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory
+  *	rules.
+  *
+@@ -1759,9 +1767,9 @@ enum nl80211_commands {
+  * @NL80211_ATTR_BSS: scan result BSS
+  *
+  * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain
+- * 	currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_*
++ *	currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_*
+  * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently
+- * 	set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*)
++ *	set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*)
+  *
+  * @NL80211_ATTR_SUPPORTED_COMMANDS: wiphy attribute that specifies
+  *	an array of command numbers (i.e. a mapping index to command number)
+@@ -1780,15 +1788,15 @@ enum nl80211_commands {
+  *	a u32
+  *
+  * @NL80211_ATTR_FREQ_BEFORE: A channel which has suffered a regulatory change
+- * 	due to considerations from a beacon hint. This attribute reflects
+- * 	the state of the channel _before_ the beacon hint processing. This
+- * 	attributes consists of a nested attribute containing
+- * 	NL80211_FREQUENCY_ATTR_*
++ *	due to considerations from a beacon hint. This attribute reflects
++ *	the state of the channel _before_ the beacon hint processing. This
++ *	attributes consists of a nested attribute containing
++ *	NL80211_FREQUENCY_ATTR_*
+  * @NL80211_ATTR_FREQ_AFTER: A channel which has suffered a regulatory change
+- * 	due to considerations from a beacon hint. This attribute reflects
+- * 	the state of the channel _after_ the beacon hint processing. This
+- * 	attributes consists of a nested attribute containing
+- * 	NL80211_FREQUENCY_ATTR_*
++ *	due to considerations from a beacon hint. This attribute reflects
++ *	the state of the channel _after_ the beacon hint processing. This
++ *	attributes consists of a nested attribute containing
++ *	NL80211_FREQUENCY_ATTR_*
+  *
+  * @NL80211_ATTR_CIPHER_SUITES: a set of u32 values indicating the supported
+  *	cipher suites
+@@ -1835,7 +1843,7 @@ enum nl80211_commands {
+  *	using %CMD_CONTROL_PORT_FRAME.  If control port routing over NL80211 is
+  *	to be used then userspace must also use the %NL80211_ATTR_SOCKET_OWNER
+  *	flag. When used with %NL80211_ATTR_CONTROL_PORT_NO_PREAUTH, pre-auth
+- *	frames are not forwared over the control port.
++ *	frames are not forwarded over the control port.
+  *
+  * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver.
+  *	We recommend using nested, driver-specific attributes within this.
+@@ -1849,12 +1857,6 @@ enum nl80211_commands {
+  *	that protected APs should be used. This is also used with NEW_BEACON to
+  *	indicate that the BSS is to use protection.
+  *
+- * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT, ASSOCIATE, and NEW_BEACON
+- *	to indicate which unicast key ciphers will be used with the connection
+- *	(an array of u32).
+- * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+- *	indicate which group key cipher will be used with the connection (a
+- *	u32).
+  * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+  *	indicate which WPA version(s) the AP we want to associate with is using
+  *	(a u32 with flags from &enum nl80211_wpa_versions).
+@@ -1885,6 +1887,7 @@ enum nl80211_commands {
+  *	with %NL80211_KEY_* sub-attributes
+  *
+  * @NL80211_ATTR_PID: Process ID of a network namespace.
++ * @NL80211_ATTR_NETNS_FD: File descriptor of a network namespace.
+  *
+  * @NL80211_ATTR_GENERATION: Used to indicate consistent snapshots for
+  *	dumps. This number increases whenever the object list being
+@@ -1939,6 +1942,7 @@ enum nl80211_commands {
+  *
+  * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was
+  *	acknowledged by the recipient.
++ * @NL80211_ATTR_ACK_SIGNAL: Station's ack signal strength (s32)
+  *
+  * @NL80211_ATTR_PS_STATE: powersave state, using &enum nl80211_ps_state values.
+  *
+@@ -1972,10 +1976,10 @@ enum nl80211_commands {
+  *	bit. Depending on which antennas are selected in the bitmap, 802.11n
+  *	drivers can derive which chainmasks to use (if all antennas belonging to
+  *	a particular chain are disabled this chain should be disabled) and if
+- *	a chain has diversity antennas wether diversity should be used or not.
++ *	a chain has diversity antennas whether diversity should be used or not.
+  *	HT capabilities (STBC, TX Beamforming, Antenna selection) can be
+  *	derived from the available chains after applying the antenna mask.
+- *	Non-802.11n drivers can derive wether to use diversity or not.
++ *	Non-802.11n drivers can derive whether to use diversity or not.
+  *	Drivers may reject configurations or RX/TX mask combinations they cannot
+  *	support by returning -EINVAL.
+  *
+@@ -2048,6 +2052,10 @@ enum nl80211_commands {
+  * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported
+  *	interface combinations. In each nested item, it contains attributes
+  *	defined in &enum nl80211_if_combination_attrs.
++ *	If the wiphy uses multiple radios (@NL80211_ATTR_WIPHY_RADIOS is set),
++ *	this attribute contains the interface combinations of the first radio.
++ *	See @NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS for the global wiphy
++ *	combinations for the sum of all radios.
+  * @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like
+  *	%NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that
+  *	are managed in software: interfaces of these types aren't subject to
+@@ -2136,6 +2144,9 @@ enum nl80211_commands {
+  * @NL80211_ATTR_DISABLE_HE: Force HE capable interfaces to disable
+  *      this feature during association. This is a flag attribute.
+  *	Currently only supported in mac80211 drivers.
++ * @NL80211_ATTR_DISABLE_EHT: Force EHT capable interfaces to disable
++ *      this feature during association. This is a flag attribute.
++ *	Currently only supported in mac80211 drivers.
+  * @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the
+  *      ATTR_HT_CAPABILITY to which attention should be paid.
+  *      Currently, only mac80211 NICs support this feature.
+@@ -2145,6 +2156,12 @@ enum nl80211_commands {
+  *      All values are treated as suggestions and may be ignored
+  *      by the driver as required.  The actual values may be seen in
+  *      the station debugfs ht_caps file.
++ * @NL80211_ATTR_VHT_CAPABILITY_MASK: Specify which bits of the
++ *      ATTR_VHT_CAPABILITY to which attention should be paid.
++ *      Currently, only mac80211 NICs support this feature.
++ *      All values are treated as suggestions and may be ignored
++ *      by the driver as required.  The actual values may be seen in
++ *      the station debugfs vht_caps file.
+  *
+  * @NL80211_ATTR_DFS_REGION: region for regulatory rules which this country
+  *    abides to when initiating radiation on DFS channels. A country maps
+@@ -2403,7 +2420,7 @@ enum nl80211_commands {
+  *	scheduled scan is started.  Or the delay before a WoWLAN
+  *	net-detect scan is started, counting from the moment the
+  *	system is suspended.  This value is a u32, in seconds.
+-
++ *
+  * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
+  *      is operating in an indoor environment.
+  *
+@@ -2545,7 +2562,7 @@ enum nl80211_commands {
+  *	from successful FILS authentication and is used with
+  *	%NL80211_CMD_CONNECT.
+  *
+- * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertized by a FILS AP
++ * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertised by a FILS AP
+  *	identifying the scope of PMKSAs. This is used with
+  *	@NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA.
+  *
+@@ -2826,6 +2843,31 @@ enum nl80211_commands {
+  * @NL80211_ATTR_MLO_LINK_DISABLED: Flag attribute indicating that the link is
+  *	disabled.
+  *
++ * @NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA: Include BSS usage data, i.e.
++ *	include BSSes that can only be used in restricted scenarios and/or
++ *	cannot be used at all.
++ *
++ * @NL80211_ATTR_MLO_TTLM_DLINK: Binary attribute specifying the downlink TID to
++ *      link mapping. The length is 8 * sizeof(u16). For each TID the link
++ *      mapping is as defined in section 9.4.2.314 (TID-To-Link Mapping element)
++ *      in Draft P802.11be_D4.0.
++ * @NL80211_ATTR_MLO_TTLM_ULINK: Binary attribute specifying the uplink TID to
++ *      link mapping. The length is 8 * sizeof(u16). For each TID the link
++ *      mapping is as defined in section 9.4.2.314 (TID-To-Link Mapping element)
++ *      in Draft P802.11be_D4.0.
++ *
++ * @NL80211_ATTR_ASSOC_SPP_AMSDU: flag attribute used with
++ *	%NL80211_CMD_ASSOCIATE indicating the SPP A-MSDUs
++ *	are used on this connection
++ *
++ * @NL80211_ATTR_WIPHY_RADIOS: Nested attribute describing physical radios
++ *	belonging to this wiphy. See &enum nl80211_wiphy_radio_attrs.
++ *
++ * @NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS: Nested attribute listing the
++ *	supported interface combinations for all radios combined. In each
++ *	nested item, it contains attributes defined in
++ *	&enum nl80211_if_combination_attrs.
++ *
+  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
+  * @NL80211_ATTR_MAX: highest attribute number currently defined
+  * @__NL80211_ATTR_AFTER_LAST: internal use
+@@ -3364,6 +3406,16 @@ enum nl80211_attrs {
+ 
+ 	NL80211_ATTR_MLO_LINK_DISABLED,
+ 
++	NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA,
++
++	NL80211_ATTR_MLO_TTLM_DLINK,
++	NL80211_ATTR_MLO_TTLM_ULINK,
++
++	NL80211_ATTR_ASSOC_SPP_AMSDU,
++
++	NL80211_ATTR_WIPHY_RADIOS,
++	NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS,
++
+ 	/* add attributes here, update the policy in nl80211.c */
+ 
+ 	__NL80211_ATTR_AFTER_LAST,
+@@ -3504,6 +3556,7 @@ enum nl80211_iftype {
+  * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers
+  *	that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a
+  *	previously added station into associated state
++ * @NL80211_STA_FLAG_SPP_AMSDU: station supports SPP A-MSDUs
+  * @NL80211_STA_FLAG_MAX: highest station flag number currently defined
+  * @__NL80211_STA_FLAG_AFTER_LAST: internal use
+  */
+@@ -3516,6 +3569,7 @@ enum nl80211_sta_flags {
+ 	NL80211_STA_FLAG_AUTHENTICATED,
+ 	NL80211_STA_FLAG_TDLS_PEER,
+ 	NL80211_STA_FLAG_ASSOCIATED,
++	NL80211_STA_FLAG_SPP_AMSDU,
+ 
+ 	/* keep last */
+ 	__NL80211_STA_FLAG_AFTER_LAST,
+@@ -3526,7 +3580,7 @@ enum nl80211_sta_flags {
+  * enum nl80211_sta_p2p_ps_status - station support of P2P PS
+  *
+  * @NL80211_P2P_PS_UNSUPPORTED: station doesn't support P2P PS mechanism
+- * @@NL80211_P2P_PS_SUPPORTED: station supports P2P PS mechanism
++ * @NL80211_P2P_PS_SUPPORTED: station supports P2P PS mechanism
+  * @NUM_NL80211_P2P_PS_STATUS: number of values
+  */
+ enum nl80211_sta_p2p_ps_status {
+@@ -3564,9 +3618,9 @@ enum nl80211_he_gi {
+ 
+ /**
+  * enum nl80211_he_ltf - HE long training field
+- * @NL80211_RATE_INFO_HE_1xLTF: 3.2 usec
+- * @NL80211_RATE_INFO_HE_2xLTF: 6.4 usec
+- * @NL80211_RATE_INFO_HE_4xLTF: 12.8 usec
++ * @NL80211_RATE_INFO_HE_1XLTF: 3.2 usec
++ * @NL80211_RATE_INFO_HE_2XLTF: 6.4 usec
++ * @NL80211_RATE_INFO_HE_4XLTF: 12.8 usec
+  */
+ enum nl80211_he_ltf {
+ 	NL80211_RATE_INFO_HE_1XLTF,
+@@ -3681,7 +3735,7 @@ enum nl80211_eht_ru_alloc {
+  * @NL80211_RATE_INFO_HE_GI: HE guard interval identifier
+  *	(u8, see &enum nl80211_he_gi)
+  * @NL80211_RATE_INFO_HE_DCM: HE DCM value (u8, 0/1)
+- * @NL80211_RATE_INFO_RU_ALLOC: HE RU allocation, if not present then
++ * @NL80211_RATE_INFO_HE_RU_ALLOC: HE RU allocation, if not present then
+  *	non-OFDMA was used (u8, see &enum nl80211_he_ru_alloc)
+  * @NL80211_RATE_INFO_320_MHZ_WIDTH: 320 MHz bitrate
+  * @NL80211_RATE_INFO_EHT_MCS: EHT MCS index (u8, 0-15)
+@@ -3784,7 +3838,7 @@ enum nl80211_sta_bss_param {
+  *	(u64, to this station)
+  * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm)
+  * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute
+- * 	containing info as possible, see &enum nl80211_rate_info
++ *	containing info as possible, see &enum nl80211_rate_info
+  * @NL80211_STA_INFO_RX_PACKETS: total received packet (MSDUs and MMPDUs)
+  *	(u32, from this station)
+  * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (MSDUs and MMPDUs)
+@@ -3813,8 +3867,8 @@ enum nl80211_sta_bss_param {
+  *	Contains a nested array of signal strength attributes (u8, dBm)
+  * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average
+  *	Same format as NL80211_STA_INFO_CHAIN_SIGNAL.
+- * @NL80211_STA_EXPECTED_THROUGHPUT: expected throughput considering also the
+- *	802.11 header (u32, kbps)
++ * @NL80211_STA_INFO_EXPECTED_THROUGHPUT: expected throughput considering also
++ *	the 802.11 header (u32, kbps)
+  * @NL80211_STA_INFO_RX_DROP_MISC: RX packets dropped for unspecified reasons
+  *	(u64)
+  * @NL80211_STA_INFO_BEACON_RX: number of beacons received from this peer (u64)
+@@ -4000,7 +4054,7 @@ enum nl80211_mpath_flags {
+  * @NL80211_MPATH_INFO_METRIC: metric (cost) of this mesh path
+  * @NL80211_MPATH_INFO_EXPTIME: expiration time for the path, in msec from now
+  * @NL80211_MPATH_INFO_FLAGS: mesh path flags, enumerated in
+- * 	&enum nl80211_mpath_flags;
++ *	&enum nl80211_mpath_flags;
+  * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec
+  * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries
+  * @NL80211_MPATH_INFO_HOP_COUNT: hop count to destination
+@@ -4140,7 +4194,7 @@ enum nl80211_band_attr {
+  * @NL80211_WMMR_CW_MAX: Maximum contention window slot.
+  * @NL80211_WMMR_AIFSN: Arbitration Inter Frame Space.
+  * @NL80211_WMMR_TXOP: Maximum allowed tx operation time.
+- * @nl80211_WMMR_MAX: highest possible wmm rule.
++ * @NL80211_WMMR_MAX: highest possible wmm rule.
+  * @__NL80211_WMMR_LAST: Internal use.
+  */
+ enum nl80211_wmm_rule {
+@@ -4162,15 +4216,16 @@ enum nl80211_wmm_rule {
+  * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current
+  *	regulatory domain.
+  * @NL80211_FREQUENCY_ATTR_NO_IR: no mechanisms that initiate radiation
+- * 	are permitted on this channel, this includes sending probe
+- * 	requests, or modes of operation that require beaconing.
++ *	are permitted on this channel, this includes sending probe
++ *	requests, or modes of operation that require beaconing.
++ * @__NL80211_FREQUENCY_ATTR_NO_IBSS: obsolete, same as _NO_IR
+  * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory
+  *	on this channel in current regulatory domain.
+  * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm
+  *	(100 * dBm).
+  * @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS
+  *	(enum nl80211_dfs_state)
+- * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in miliseconds for how long
++ * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in milliseconds for how long
+  *	this channel is in this DFS state.
+  * @NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: HT40- isn't possible with this
+  *	channel as the control channel
+@@ -4226,6 +4281,19 @@ enum nl80211_wmm_rule {
+  *	in current regulatory domain.
+  * @NL80211_FREQUENCY_ATTR_PSD: Power spectral density (in dBm) that
+  *	is allowed on this channel in current regulatory domain.
++ * @NL80211_FREQUENCY_ATTR_DFS_CONCURRENT: Operation on this channel is
++ *	allowed for peer-to-peer or adhoc communication under the control
++ *	of a DFS master which operates on the same channel (FCC-594280 D01
++ *	Section B.3). Should be used together with %NL80211_RRF_DFS only.
++ * @NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT: Client connection to VLP AP
++ *	not allowed using this channel
++ * @NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP
++ *	not allowed using this channel
++ * @NL80211_FREQUENCY_ATTR_CAN_MONITOR: This channel can be used in monitor
++ *	mode despite other (regulatory) restrictions, even if the channel is
++ *	otherwise completely disabled.
++ * @NL80211_FREQUENCY_ATTR_ALLOW_6GHZ_VLP_AP: This channel can be used for a
++ *	very low power (VLP) AP, despite being NO_IR.
+  * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
+  *	currently defined
+  * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
+@@ -4265,6 +4333,11 @@ enum nl80211_frequency_attr {
+ 	NL80211_FREQUENCY_ATTR_NO_320MHZ,
+ 	NL80211_FREQUENCY_ATTR_NO_EHT,
+ 	NL80211_FREQUENCY_ATTR_PSD,
++	NL80211_FREQUENCY_ATTR_DFS_CONCURRENT,
++	NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT,
++	NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT,
++	NL80211_FREQUENCY_ATTR_CAN_MONITOR,
++	NL80211_FREQUENCY_ATTR_ALLOW_6GHZ_VLP_AP,
+ 
+ 	/* keep last */
+ 	__NL80211_FREQUENCY_ATTR_AFTER_LAST,
+@@ -4277,6 +4350,10 @@ enum nl80211_frequency_attr {
+ #define NL80211_FREQUENCY_ATTR_NO_IR		NL80211_FREQUENCY_ATTR_NO_IR
+ #define NL80211_FREQUENCY_ATTR_GO_CONCURRENT \
+ 					NL80211_FREQUENCY_ATTR_IR_CONCURRENT
++#define NL80211_FREQUENCY_ATTR_NO_UHB_VLP_CLIENT \
++	NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT
++#define NL80211_FREQUENCY_ATTR_NO_UHB_AFC_CLIENT \
++	NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT
+ 
+ /**
+  * enum nl80211_bitrate_attr - bitrate attributes
+@@ -4299,16 +4376,16 @@ enum nl80211_bitrate_attr {
+ };
+ 
+ /**
+- * enum nl80211_initiator - Indicates the initiator of a reg domain request
++ * enum nl80211_reg_initiator - Indicates the initiator of a reg domain request
+  * @NL80211_REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world
+- * 	regulatory domain.
++ *	regulatory domain.
+  * @NL80211_REGDOM_SET_BY_USER: User asked the wireless core to set the
+- * 	regulatory domain.
++ *	regulatory domain.
+  * @NL80211_REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the
+- * 	wireless core it thinks its knows the regulatory domain we should be in.
++ *	wireless core it thinks its knows the regulatory domain we should be in.
+  * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an
+- * 	802.11 country information element with regulatory information it
+- * 	thinks we should consider. cfg80211 only processes the country
++ *	802.11 country information element with regulatory information it
++ *	thinks we should consider. cfg80211 only processes the country
+  *	code from the IE, and relies on the regulatory domain information
+  *	structure passed by userspace (CRDA) from our wireless-regdb.
+  *	If a channel is enabled but the country code indicates it should
+@@ -4327,11 +4404,11 @@ enum nl80211_reg_initiator {
+  *	to a specific country. When this is set you can count on the
+  *	ISO / IEC 3166 alpha2 country code being valid.
+  * @NL80211_REGDOM_TYPE_WORLD: the regulatory set domain is the world regulatory
+- * 	domain.
++ *	domain.
+  * @NL80211_REGDOM_TYPE_CUSTOM_WORLD: the regulatory domain set is a custom
+- * 	driver specific world regulatory domain. These do not apply system-wide
+- * 	and are only applicable to the individual devices which have requested
+- * 	them to be applied.
++ *	driver specific world regulatory domain. These do not apply system-wide
++ *	and are only applicable to the individual devices which have requested
++ *	them to be applied.
+  * @NL80211_REGDOM_TYPE_INTERSECTION: the regulatory domain set is the product
+  *	of an intersection between two regulatory domains -- the previously
+  *	set regulatory domain on the system and the last accepted regulatory
+@@ -4348,21 +4425,21 @@ enum nl80211_reg_type {
+  * enum nl80211_reg_rule_attr - regulatory rule attributes
+  * @__NL80211_REG_RULE_ATTR_INVALID: attribute number 0 is reserved
+  * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional
+- * 	considerations for a given frequency range. These are the
+- * 	&enum nl80211_reg_rule_flags.
++ *	considerations for a given frequency range. These are the
++ *	&enum nl80211_reg_rule_flags.
+  * @NL80211_ATTR_FREQ_RANGE_START: starting frequencry for the regulatory
+- * 	rule in KHz. This is not a center of frequency but an actual regulatory
+- * 	band edge.
++ *	rule in KHz. This is not a center of frequency but an actual regulatory
++ *	band edge.
+  * @NL80211_ATTR_FREQ_RANGE_END: ending frequency for the regulatory rule
+- * 	in KHz. This is not a center a frequency but an actual regulatory
+- * 	band edge.
++ *	in KHz. This is not a center a frequency but an actual regulatory
++ *	band edge.
+  * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this
+  *	frequency range, in KHz.
+  * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain
+- * 	for a given frequency range. The value is in mBi (100 * dBi).
+- * 	If you don't have one then don't send this.
++ *	for a given frequency range. The value is in mBi (100 * dBi).
++ *	If you don't have one then don't send this.
+  * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for
+- * 	a given frequency range. The value is in mBm (100 * dBm).
++ *	a given frequency range. The value is in mBm (100 * dBm).
+  * @NL80211_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+  *	If not present or 0 default CAC time will be used.
+  * @NL80211_ATTR_POWER_RULE_PSD: power spectral density (in dBm).
+@@ -4414,14 +4491,7 @@ enum nl80211_reg_rule_attr {
+  *	value as specified by &struct nl80211_bss_select_rssi_adjust.
+  * @NL80211_SCHED_SCAN_MATCH_ATTR_BSSID: BSSID to be used for matching
+  *	(this cannot be used together with SSID).
+- * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Nested attribute that carries the
+- *	band specific minimum rssi thresholds for the bands defined in
+- *	enum nl80211_band. The minimum rssi threshold value(s32) specific to a
+- *	band shall be encapsulated in attribute with type value equals to one
+- *	of the NL80211_BAND_* defined in enum nl80211_band. For example, the
+- *	minimum rssi threshold value for 2.4GHZ band shall be encapsulated
+- *	within an attribute of type NL80211_BAND_2GHZ. And one or more of such
+- *	attributes will be nested within this attribute.
++ * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Obsolete
+  * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
+  *	attribute number currently defined
+  * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
+@@ -4434,7 +4504,7 @@ enum nl80211_sched_scan_match_attr {
+ 	NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI,
+ 	NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST,
+ 	NL80211_SCHED_SCAN_MATCH_ATTR_BSSID,
+-	NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI,
++	NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI, /* obsolete */
+ 
+ 	/* keep last */
+ 	__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
+@@ -4456,8 +4526,9 @@ enum nl80211_sched_scan_match_attr {
+  * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links
+  * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links
+  * @NL80211_RRF_NO_IR: no mechanisms that initiate radiation are allowed,
+- * 	this includes probe requests or modes of operation that require
+- * 	beaconing.
++ *	this includes probe requests or modes of operation that require
++ *	beaconing.
++ * @__NL80211_RRF_NO_IBSS: obsolete, same as NO_IR
+  * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated
+  *	base on contiguous rules and wider channels will be allowed to cross
+  *	multiple contiguous/overlapping frequency ranges.
+@@ -4470,6 +4541,14 @@ enum nl80211_sched_scan_match_attr {
+  * @NL80211_RRF_NO_320MHZ: 320MHz operation not allowed
+  * @NL80211_RRF_NO_EHT: EHT operation not allowed
+  * @NL80211_RRF_PSD: Ruleset has power spectral density value
++ * @NL80211_RRF_DFS_CONCURRENT: Operation on this channel is allowed for
++ *	peer-to-peer or adhoc communication under the control of a DFS master
++ *	which operates on the same channel (FCC-594280 D01 Section B.3).
++ *	Should be used together with %NL80211_RRF_DFS only.
++ * @NL80211_RRF_NO_6GHZ_VLP_CLIENT: Client connection to VLP AP not allowed
++ * @NL80211_RRF_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP not allowed
++ * @NL80211_RRF_ALLOW_6GHZ_VLP_AP: Very low power (VLP) AP can be permitted
++ *	despite NO_IR configuration.
+  */
+ enum nl80211_reg_rule_flags {
+ 	NL80211_RRF_NO_OFDM		= 1<<0,
+@@ -4491,6 +4570,10 @@ enum nl80211_reg_rule_flags {
+ 	NL80211_RRF_NO_320MHZ		= 1<<18,
+ 	NL80211_RRF_NO_EHT		= 1<<19,
+ 	NL80211_RRF_PSD			= 1<<20,
++	NL80211_RRF_DFS_CONCURRENT	= 1<<21,
++	NL80211_RRF_NO_6GHZ_VLP_CLIENT	= 1<<22,
++	NL80211_RRF_NO_6GHZ_AFC_CLIENT	= 1<<23,
++	NL80211_RRF_ALLOW_6GHZ_VLP_AP	= 1<<24,
+ };
+ 
+ #define NL80211_RRF_PASSIVE_SCAN	NL80211_RRF_NO_IR
+@@ -4499,6 +4582,8 @@ enum nl80211_reg_rule_flags {
+ #define NL80211_RRF_NO_HT40		(NL80211_RRF_NO_HT40MINUS |\
+ 					 NL80211_RRF_NO_HT40PLUS)
+ #define NL80211_RRF_GO_CONCURRENT	NL80211_RRF_IR_CONCURRENT
++#define NL80211_RRF_NO_UHB_VLP_CLIENT	NL80211_RRF_NO_6GHZ_VLP_CLIENT
++#define NL80211_RRF_NO_UHB_AFC_CLIENT	NL80211_RRF_NO_6GHZ_AFC_CLIENT
+ 
+ /* For backport compatibility with older userspace */
+ #define NL80211_RRF_NO_IR_ALL		(NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS)
+@@ -4645,8 +4730,8 @@ enum nl80211_mntr_flags {
+  *	alternate between Active and Doze states, but may not wake up
+  *	for neighbor's beacons.
+  *
+- * @__NL80211_MESH_POWER_AFTER_LAST - internal use
+- * @NL80211_MESH_POWER_MAX - highest possible power save level
++ * @__NL80211_MESH_POWER_AFTER_LAST: internal use
++ * @NL80211_MESH_POWER_MAX: highest possible power save level
+  */
+ 
+ enum nl80211_mesh_power_mode {
+@@ -5027,6 +5112,36 @@ enum nl80211_bss_scan_width {
+ 	NL80211_BSS_CHAN_WIDTH_2,
+ };
+ 
++/**
++ * enum nl80211_bss_use_for - bitmap indicating possible BSS use
++ * @NL80211_BSS_USE_FOR_NORMAL: Use this BSS for normal "connection",
++ *	including IBSS/MBSS depending on the type.
++ * @NL80211_BSS_USE_FOR_MLD_LINK: This BSS can be used as a link in an
++ *	MLO connection. Note that for an MLO connection, all links including
++ *	the assoc link must have this flag set, and the assoc link must
++ *	additionally have %NL80211_BSS_USE_FOR_NORMAL set.
++ */
++enum nl80211_bss_use_for {
++	NL80211_BSS_USE_FOR_NORMAL = 1 << 0,
++	NL80211_BSS_USE_FOR_MLD_LINK = 1 << 1,
++};
++
++/**
++ * enum nl80211_bss_cannot_use_reasons - reason(s) connection to a
++ *	BSS isn't possible
++ * @NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY: NSTR nonprimary links aren't
++ *	supported by the device, and this BSS entry represents one.
++ * @NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH: STA is not supporting
++ *	the AP power type (SP, VLP, AP) that the AP uses.
++ */
++enum nl80211_bss_cannot_use_reasons {
++	NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY	= 1 << 0,
++	NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH	= 1 << 1,
++};
++
++#define NL80211_BSS_CANNOT_USE_UHB_PWR_MISMATCH \
++	NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH
++
+ /**
+  * enum nl80211_bss - netlink attributes for a BSS
+  *
+@@ -5079,6 +5194,14 @@ enum nl80211_bss_scan_width {
+  * @NL80211_BSS_FREQUENCY_OFFSET: frequency offset in KHz
+  * @NL80211_BSS_MLO_LINK_ID: MLO link ID of the BSS (u8).
+  * @NL80211_BSS_MLD_ADDR: MLD address of this BSS if connected to it.
++ * @NL80211_BSS_USE_FOR: u32 bitmap attribute indicating what the BSS can be
++ *	used for, see &enum nl80211_bss_use_for.
++ * @NL80211_BSS_CANNOT_USE_REASONS: Indicates the reason that this BSS cannot
++ *	be used for all or some of the possible uses by the device reporting it,
++ *	even though its presence was detected.
++ *	This is a u64 attribute containing a bitmap of values from
++ *	&enum nl80211_cannot_use_reasons, note that the attribute may be missing
++ *	if no reasons are specified.
+  * @__NL80211_BSS_AFTER_LAST: internal
+  * @NL80211_BSS_MAX: highest BSS attribute
+  */
+@@ -5106,6 +5229,8 @@ enum nl80211_bss {
+ 	NL80211_BSS_FREQUENCY_OFFSET,
+ 	NL80211_BSS_MLO_LINK_ID,
+ 	NL80211_BSS_MLD_ADDR,
++	NL80211_BSS_USE_FOR,
++	NL80211_BSS_CANNOT_USE_REASONS,
+ 
+ 	/* keep last */
+ 	__NL80211_BSS_AFTER_LAST,
+@@ -5454,7 +5579,7 @@ enum nl80211_tx_rate_setting {
+  *	(%NL80211_TID_CONFIG_ATTR_TIDS, %NL80211_TID_CONFIG_ATTR_OVERRIDE).
+  * @NL80211_TID_CONFIG_ATTR_PEER_SUPP: same as the previous per-vif one, but
+  *	per peer instead.
+- * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribue, if set indicates
++ * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribute, if set indicates
+  *	that the new configuration overrides all previous peer
+  *	configurations, otherwise previous peer specific configurations
+  *	should be left untouched.
+@@ -5626,7 +5751,7 @@ struct nl80211_pattern_support {
+  *	"TCP connection wakeup" for more details. This is a nested attribute
+  *	containing the exact information for establishing and keeping alive
+  *	the TCP connection.
+- * @NL80211_WOWLAN_TRIG_TCP_WAKEUP_MATCH: For wakeup reporting only, the
++ * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH: For wakeup reporting only, the
+  *	wakeup packet was received on the TCP connection
+  * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST: For wakeup reporting only, the
+  *	TCP connection was lost or failed to be established
+@@ -5655,6 +5780,8 @@ struct nl80211_pattern_support {
+  *	%NL80211_ATTR_SCAN_FREQUENCIES contains more than one
+  *	frequency, it means that the match occurred in more than one
+  *	channel.
++ * @NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC: For wakeup reporting only.
++ *	Wake up happened due to unprotected deauth or disassoc frame in MFP.
+  * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers
+  * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number
+  *
+@@ -5682,6 +5809,7 @@ enum nl80211_wowlan_triggers {
+ 	NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS,
+ 	NL80211_WOWLAN_TRIG_NET_DETECT,
+ 	NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS,
++	NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC,
+ 
+ 	/* keep last */
+ 	NUM_NL80211_WOWLAN_TRIG,
+@@ -5837,7 +5965,7 @@ enum nl80211_attr_coalesce_rule {
+ 
+ /**
+  * enum nl80211_coalesce_condition - coalesce rule conditions
+- * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns
++ * @NL80211_COALESCE_CONDITION_MATCH: coalesce Rx packets when patterns
+  *	in a rule are matched.
+  * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns
+  *	in a rule are not matched.
+@@ -5936,7 +6064,7 @@ enum nl80211_if_combination_attrs {
+  * enum nl80211_plink_state - state of a mesh peer link finite state machine
+  *
+  * @NL80211_PLINK_LISTEN: initial state, considered the implicit
+- *	state of non existent mesh peer links
++ *	state of non-existent mesh peer links
+  * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to
+  *	this mesh peer
+  * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received
+@@ -5972,7 +6100,7 @@ enum nl80211_plink_state {
+  * @NL80211_PLINK_ACTION_BLOCK: block traffic from this mesh peer
+  * @NUM_NL80211_PLINK_ACTIONS: number of possible actions
+  */
+-enum plink_actions {
++enum nl80211_plink_action {
+ 	NL80211_PLINK_ACTION_NO_ACTION,
+ 	NL80211_PLINK_ACTION_OPEN,
+ 	NL80211_PLINK_ACTION_BLOCK,
+@@ -6229,7 +6357,7 @@ enum nl80211_feature_flags {
+  *	request to use RRM (see %NL80211_ATTR_USE_RRM) with
+  *	%NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests, which will set
+  *	the ASSOC_REQ_USE_RRM flag in the association request even if
+- *	NL80211_FEATURE_QUIET is not advertized.
++ *	NL80211_FEATURE_QUIET is not advertised.
+  * @NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER: This device supports MU-MIMO air
+  *	sniffer which means that it can be configured to hear packets from
+  *	certain groups which can be configured by the
+@@ -6241,13 +6369,15 @@ enum nl80211_feature_flags {
+  *	the BSS that the interface that requested the scan is connected to
+  *	(if available).
+  * @NL80211_EXT_FEATURE_BSS_PARENT_TSF: Per BSS, this driver reports the
+- *	time the last beacon/probe was received. The time is the TSF of the
+- *	BSS that the interface that requested the scan is connected to
+- *	(if available).
++ *	time the last beacon/probe was received. For a non-MLO connection, the
++ *	time is the TSF of the BSS that the interface that requested the scan is
++ *	connected to (if available). For an MLO connection, the time is the TSF
++ *	of the BSS corresponding with link ID specified in the scan request (if
++ *	specified).
+  * @NL80211_EXT_FEATURE_SET_SCAN_DWELL: This driver supports configuration of
+  *	channel dwell time.
+  * @NL80211_EXT_FEATURE_BEACON_RATE_LEGACY: Driver supports beacon rate
+- *	configuration (AP/mesh), supporting a legacy (non HT/VHT) rate.
++ *	configuration (AP/mesh), supporting a legacy (non-HT/VHT) rate.
+  * @NL80211_EXT_FEATURE_BEACON_RATE_HT: Driver supports beacon rate
+  *	configuration (AP/mesh) with HT rates.
+  * @NL80211_EXT_FEATURE_BEACON_RATE_VHT: Driver supports beacon rate
+@@ -6297,6 +6427,7 @@ enum nl80211_feature_flags {
+  *	receiving control port frames over nl80211 instead of the netdevice.
+  * @NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT: This driver/device supports
+  *	(average) ACK signal strength reporting.
++ * @NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT: Backward-compatible ID
+  * @NL80211_EXT_FEATURE_TXQS: Driver supports FQ-CoDel-enabled intermediate
+  *      TXQs.
+  * @NL80211_EXT_FEATURE_SCAN_RANDOM_SN: Driver/device supports randomizing the
+@@ -6321,8 +6452,7 @@ enum nl80211_feature_flags {
+  * @NL80211_EXT_FEATURE_AP_PMKSA_CACHING: Driver/device supports PMKSA caching
+  *	(set/del PMKSA operations) in AP mode.
+  *
+- * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Driver supports
+- *	filtering of sched scan results using band specific RSSI thresholds.
++ * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Obsolete
+  *
+  * @NL80211_EXT_FEATURE_STA_TX_PWR: This driver supports controlling tx power
+  *	to a station.
+@@ -6426,6 +6556,16 @@ enum nl80211_feature_flags {
+  * @NL80211_EXT_FEATURE_OWE_OFFLOAD_AP: Driver/Device wants to do OWE DH IE
+  *	handling in AP mode.
+  *
++ * @NL80211_EXT_FEATURE_DFS_CONCURRENT: The device supports peer-to-peer or
++ *	ad hoc operation on DFS channels under the control of a concurrent
++ *	DFS master on the same channel as described in FCC-594280 D01
++ *	(Section B.3). This, for example, allows P2P GO and P2P clients to
++ *	operate on DFS channels as long as there's a concurrent BSS connection.
++ *
++ * @NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT: The driver has support for SPP
++ *	(signaling and payload protected) A-MSDUs and this shall be advertised
++ *	in the RSNXE.
++ *
+  * @NUM_NL80211_EXT_FEATURES: number of extended features.
+  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
+  */
+@@ -6467,7 +6607,7 @@ enum nl80211_ext_feature_index {
+ 	NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER,
+ 	NL80211_EXT_FEATURE_AIRTIME_FAIRNESS,
+ 	NL80211_EXT_FEATURE_AP_PMKSA_CACHING,
+-	NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD,
++	NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD, /* obsolete */
+ 	NL80211_EXT_FEATURE_EXT_KEY_ID,
+ 	NL80211_EXT_FEATURE_STA_TX_PWR,
+ 	NL80211_EXT_FEATURE_SAE_OFFLOAD,
+@@ -6499,6 +6639,8 @@ enum nl80211_ext_feature_index {
+ 	NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA,
+ 	NL80211_EXT_FEATURE_OWE_OFFLOAD,
+ 	NL80211_EXT_FEATURE_OWE_OFFLOAD_AP,
++	NL80211_EXT_FEATURE_DFS_CONCURRENT,
++	NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT,
+ 
+ 	/* add new features before the definition below */
+ 	NUM_NL80211_EXT_FEATURES,
+@@ -6583,7 +6725,7 @@ enum nl80211_timeout_reason {
+  *	request parameters IE in the probe request
+  * @NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe responses
+  * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE: send probe request frames at
+- *	rate of at least 5.5M. In case non OCE AP is discovered in the channel,
++ *	rate of at least 5.5M. In case non-OCE AP is discovered in the channel,
+  *	only the first probe req in the channel will be sent in high rate.
+  * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: allow probe request
+  *	tx deferral (dot11FILSProbeDelay shall be set to 15ms)
+@@ -6619,7 +6761,7 @@ enum nl80211_timeout_reason {
+  *	received on the 2.4/5 GHz channels to actively scan only the 6GHz
+  *	channels on which APs are expected to be found. Note that when not set,
+  *	the scan logic would scan all 6GHz channels, but since transmission of
+- *	probe requests on non PSC channels is limited, it is highly likely that
++ *	probe requests on non-PSC channels is limited, it is highly likely that
+  *	these channels would passively be scanned. Also note that when the flag
+  *	is set, in addition to the colocated APs, PSC channels would also be
+  *	scanned if the user space has asked for it.
+@@ -6669,6 +6811,8 @@ enum nl80211_acl_policy {
+  * @NL80211_SMPS_STATIC: static SMPS (use a single antenna)
+  * @NL80211_SMPS_DYNAMIC: dynamic smps (start with a single antenna and
+  *	turn on other antennas after CTS/RTS).
++ * @__NL80211_SMPS_AFTER_LAST: internal
++ * @NL80211_SMPS_MAX: highest used enumeration
+  */
+ enum nl80211_smps_mode {
+ 	NL80211_SMPS_OFF,
+@@ -6890,6 +7034,8 @@ enum nl80211_bss_select_attr {
+  * @NL80211_NAN_FUNC_PUBLISH: function is publish
+  * @NL80211_NAN_FUNC_SUBSCRIBE: function is subscribe
+  * @NL80211_NAN_FUNC_FOLLOW_UP: function is follow-up
++ * @__NL80211_NAN_FUNC_TYPE_AFTER_LAST: internal use
++ * @NL80211_NAN_FUNC_MAX_TYPE: internal use
+  */
+ enum nl80211_nan_function_type {
+ 	NL80211_NAN_FUNC_PUBLISH,
+@@ -6951,7 +7097,7 @@ enum nl80211_nan_func_term_reason {
+  *	The instance ID for the follow up Service Discovery Frame. This is u8.
+  * @NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID: relevant if the function's type
+  *	is follow up. This is a u8.
+- *	The requestor instance ID for the follow up Service Discovery Frame.
++ *	The requester instance ID for the follow up Service Discovery Frame.
+  * @NL80211_NAN_FUNC_FOLLOW_UP_DEST: the MAC address of the recipient of the
+  *	follow up Service Discovery Frame. This is a binary attribute.
+  * @NL80211_NAN_FUNC_CLOSE_RANGE: is this function limited for devices in a
+@@ -7050,7 +7196,7 @@ enum nl80211_nan_match_attributes {
+ };
+ 
+ /**
+- * nl80211_external_auth_action - Action to perform with external
++ * enum nl80211_external_auth_action - Action to perform with external
+  *     authentication request. Used by NL80211_ATTR_EXTERNAL_AUTH_ACTION.
+  * @NL80211_EXTERNAL_AUTH_START: Start the authentication.
+  * @NL80211_EXTERNAL_AUTH_ABORT: Abort the ongoing authentication.
+@@ -7068,7 +7214,7 @@ enum nl80211_external_auth_action {
+  * @NL80211_FTM_RESP_ATTR_LCI: The content of Measurement Report Element
+  *	(9.4.2.22 in 802.11-2016) with type 8 - LCI (9.4.2.22.10),
+  *	i.e. starting with the measurement token
+- * @NL80211_FTM_RESP_ATTR_CIVIC: The content of Measurement Report Element
++ * @NL80211_FTM_RESP_ATTR_CIVICLOC: The content of Measurement Report Element
+  *	(9.4.2.22 in 802.11-2016) with type 11 - Civic (Section 9.4.2.22.13),
+  *	i.e. starting with the measurement token
+  * @__NL80211_FTM_RESP_ATTR_LAST: Internal
+@@ -7341,7 +7487,7 @@ enum nl80211_peer_measurement_attrs {
+  * @NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED: flag attribute indicating if
+  *	trigger based ranging measurement is supported
+  * @NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED: flag attribute indicating
+- *	if non trigger based ranging measurement is supported
++ *	if non-trigger-based ranging measurement is supported
+  *
+  * @NUM_NL80211_PMSR_FTM_CAPA_ATTR: internal
+  * @NL80211_PMSR_FTM_CAPA_ATTR_MAX: highest attribute number
+@@ -7395,7 +7541,7 @@ enum nl80211_peer_measurement_ftm_capa {
+  *      if neither %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED nor
+  *	%NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set, EDCA based
+  *	ranging will be used.
+- * @NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED: request non trigger based
++ * @NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED: request non-trigger-based
+  *	ranging measurement (flag)
+  *	This attribute and %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED are
+  *	mutually exclusive.
+@@ -7473,7 +7619,7 @@ enum nl80211_peer_measurement_ftm_failure_reasons {
+  * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS: number of FTM Request frames
+  *	transmitted (u32, optional)
+  * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES: number of FTM Request frames
+- *	that were acknowleged (u32, optional)
++ *	that were acknowledged (u32, optional)
+  * @NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME: retry time received from the
+  *	busy peer (u32, seconds)
+  * @NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP: actual number of bursts exponent
+@@ -7711,6 +7857,7 @@ enum nl80211_sae_pwe_mechanism {
+  *
+  * @NL80211_SAR_TYPE_POWER: power limitation specified in 0.25dBm unit
+  *
++ * @NUM_NL80211_SAR_TYPE: internal
+  */
+ enum nl80211_sar_type {
+ 	NL80211_SAR_TYPE_POWER,
+@@ -7724,6 +7871,8 @@ enum nl80211_sar_type {
+ /**
+  * enum nl80211_sar_attrs - Attributes for SAR spec
+  *
++ * @__NL80211_SAR_ATTR_INVALID: Invalid
++ *
+  * @NL80211_SAR_ATTR_TYPE: the SAR type as defined in &enum nl80211_sar_type.
+  *
+  * @NL80211_SAR_ATTR_SPECS: Nested array of SAR power
+@@ -7755,6 +7904,8 @@ enum nl80211_sar_attrs {
+ /**
+  * enum nl80211_sar_specs_attrs - Attributes for SAR power limit specs
+  *
++ * @__NL80211_SAR_ATTR_SPECS_INVALID: Invalid
++ *
+  * @NL80211_SAR_ATTR_SPECS_POWER: Required (s32)value to specify the actual
+  *	power limit value in units of 0.25 dBm if type is
+  *	NL80211_SAR_TYPE_POWER. (i.e., a value of 44 represents 11 dBm).
+@@ -7869,4 +8020,54 @@ enum nl80211_ap_settings_flags {
+ 	NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT	= 1 << 1,
+ };
+ 
++/**
++ * enum nl80211_wiphy_radio_attrs - wiphy radio attributes
++ *
++ * @__NL80211_WIPHY_RADIO_ATTR_INVALID: Invalid
++ *
++ * @NL80211_WIPHY_RADIO_ATTR_INDEX: Index of this radio (u32)
++ * @NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE: Frequency range supported by this
++ *	radio. Attribute may be present multiple times.
++ * @NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION: Supported interface
++ *	combination for this radio. Attribute may be present multiple times
++ *	and contains attributes defined in &enum nl80211_if_combination_attrs.
++ *
++ * @__NL80211_WIPHY_RADIO_ATTR_LAST: Internal
++ * @NL80211_WIPHY_RADIO_ATTR_MAX: Highest attribute
++ */
++enum nl80211_wiphy_radio_attrs {
++	__NL80211_WIPHY_RADIO_ATTR_INVALID,
++
++	NL80211_WIPHY_RADIO_ATTR_INDEX,
++	NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE,
++	NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION,
++
++	/* keep last */
++	__NL80211_WIPHY_RADIO_ATTR_LAST,
++	NL80211_WIPHY_RADIO_ATTR_MAX = __NL80211_WIPHY_RADIO_ATTR_LAST - 1,
++};
++
++/**
++ * enum nl80211_wiphy_radio_freq_range - wiphy radio frequency range
++ *
++ * @__NL80211_WIPHY_RADIO_FREQ_ATTR_INVALID: Invalid
++ *
++ * @NL80211_WIPHY_RADIO_FREQ_ATTR_START: Frequency range start (u32).
++ *	The unit is kHz.
++ * @NL80211_WIPHY_RADIO_FREQ_ATTR_END: Frequency range end (u32).
++ *	The unit is kHz.
++ *
++ * @__NL80211_WIPHY_RADIO_FREQ_ATTR_LAST: Internal
++ * @NL80211_WIPHY_RADIO_FREQ_ATTR_MAX: Highest attribute
++ */
++enum nl80211_wiphy_radio_freq_range {
++	__NL80211_WIPHY_RADIO_FREQ_ATTR_INVALID,
++
++	NL80211_WIPHY_RADIO_FREQ_ATTR_START,
++	NL80211_WIPHY_RADIO_FREQ_ATTR_END,
++
++	__NL80211_WIPHY_RADIO_FREQ_ATTR_LAST,
++	NL80211_WIPHY_RADIO_FREQ_ATTR_MAX = __NL80211_WIPHY_RADIO_FREQ_ATTR_LAST - 1,
++};
++
+ #endif /* __LINUX_NL80211_H */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0012-hostapd-make-hostapd_eapol_tx_status-function-static.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0012-hostapd-make-hostapd_eapol_tx_status-function-static.patch
deleted file mode 100644
index 960569b..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0012-hostapd-make-hostapd_eapol_tx_status-function-static.patch
+++ /dev/null
@@ -1,113 +0,0 @@
-From ac474b8dc6eb9d6a7562a714c0bbdcda47a3d858 Mon Sep 17 00:00:00 2001
-From: Sriram R <quic_srirrama@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:42 +0530
-Subject: [PATCH 012/104] hostapd: make hostapd_eapol_tx_status() function
- static
-
-hostapd_eapol_tx_status() function is being used only at one place in
-drv_callbacks. However, it is defined in ieee802_11.c which does not
-suit there.
-
-Hence, being the function definition in drv_callbacks.c and make it static.
-
-No functionality changes.
-
-Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/drv_callbacks.c | 25 +++++++++++++++++++++++++
- src/ap/ieee802_11.c    | 28 ----------------------------
- src/ap/ieee802_11.h    |  2 --
- 3 files changed, 25 insertions(+), 30 deletions(-)
-
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index 3b24aa4f4..12e6b3f36 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -2348,6 +2348,31 @@ err:
- }
- #endif /* CONFIG_OWE */
- 
-+static void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
-+				    const u8 *data, size_t len, int ack)
-+{
-+	struct sta_info *sta;
-+	struct hostapd_iface *iface = hapd->iface;
-+
-+	sta = ap_get_sta(hapd, dst);
-+	if (sta == NULL && iface->num_bss > 1) {
-+		size_t j;
-+		for (j = 0; j < iface->num_bss; j++) {
-+			hapd = iface->bss[j];
-+			sta = ap_get_sta(hapd, dst);
-+			if (sta)
-+				break;
-+		}
-+	}
-+	if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
-+		wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
-+			   MACSTR " that is not currently associated",
-+			   MAC2STR(dst));
-+		return;
-+	}
-+
-+	ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
-+}
- 
- void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
- 			  union wpa_event_data *data)
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 26e3d8356..9f7e9afdd 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -6874,34 +6874,6 @@ void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
- 	ieee802_1x_tx_status(hapd, sta, buf, len, ack);
- }
- 
--
--void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
--			     const u8 *data, size_t len, int ack)
--{
--	struct sta_info *sta;
--	struct hostapd_iface *iface = hapd->iface;
--
--	sta = ap_get_sta(hapd, dst);
--	if (sta == NULL && iface->num_bss > 1) {
--		size_t j;
--		for (j = 0; j < iface->num_bss; j++) {
--			hapd = iface->bss[j];
--			sta = ap_get_sta(hapd, dst);
--			if (sta)
--				break;
--		}
--	}
--	if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
--		wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
--			   MACSTR " that is not currently associated",
--			   MAC2STR(dst));
--		return;
--	}
--
--	ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
--}
--
--
- void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr)
- {
- 	struct sta_info *sta;
-diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
-index a35486d46..262e0ce14 100644
---- a/src/ap/ieee802_11.h
-+++ b/src/ap/ieee802_11.h
-@@ -132,8 +132,6 @@ int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
- u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid);
- void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
- 		       const u8 *buf, size_t len, int ack);
--void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
--			     const u8 *data, size_t len, int ack);
- void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
- 				int wds);
- u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0012-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0012-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch
new file mode 100644
index 0000000..800f08e
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0012-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch
@@ -0,0 +1,442 @@
+From 91b94ac0deb7a19c0d2288f264e0d9b1c4f3c2e1 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 012/126] mtk: hostapd: 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
+database. The second can iterate neighbor report database to build up neighbor
+report data.
+
+2. Support including neighbor report elements in ANQP response
+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. 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 +
+ src/ap/ap_config.h     |   1 +
+ src/ap/ctrl_iface_ap.c |  18 ++++++-
+ src/ap/gas_serv.c      |  29 ++++++++++
+ src/ap/gas_serv.h      |   2 +
+ src/ap/neighbor_db.c   | 118 +++++++++++++++++++++++++++++++++++++++++
+ src/ap/neighbor_db.h   |   9 ++++
+ src/ap/wnm_ap.c        |  43 +++++++++++++--
+ 9 files changed, 221 insertions(+), 5 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 1d5922fab..f2733687c 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -1300,6 +1300,11 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
+ #endif /* CONFIG_DPP */
+ 	} else if (os_strcasecmp(cmd, "setband") == 0) {
+ 		ret = hostapd_ctrl_iface_set_band(hapd, value);
++	} else if (os_strcasecmp(cmd, "bss_termination_tsf") == 0) {
++		int termination_sec = atoi(value);
++		hapd->conf->bss_termination_tsf = termination_sec;
++		wpa_printf(MSG_DEBUG, "BSS Termination TSF: value = %d",
++                termination_sec);
+ 	} else {
+ 		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 9e34e029a..67661dfe0 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -177,6 +177,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
+ 	bss->pasn_comeback_after = 10;
+ 	bss->pasn_noauth = 1;
+ #endif /* CONFIG_PASN */
++	bss->bss_termination_tsf = 0;
+ }
+ 
+ 
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 29a9ae7db..25ac22e31 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -572,6 +572,7 @@ struct hostapd_bss_config {
+ 	int wnm_sleep_mode;
+ 	int wnm_sleep_mode_no_keys;
+ 	int bss_transition;
++	unsigned int bss_termination_tsf;
+ 
+ 	/* IEEE 802.11u - Interworking */
+ 	int interworking;
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index a1ddbda9f..50f993253 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -1400,6 +1400,10 @@ int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
+ 			wpa_printf(MSG_DEBUG, "Invalid bss_term data");
+ 			return -1;
+ 		}
++		if (hapd->conf->bss_termination_tsf) {
++			WPA_PUT_LE64(&bss_term_dur[2], hapd->conf->bss_termination_tsf);
++		}
++
+ 		end++;
+ 		WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
+ 	}
+@@ -1426,16 +1430,26 @@ int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
+ 		req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
+ 	}
+ 
+-	if (os_strstr(cmd, " pref=1"))
++	if (os_strstr(cmd, " pref=1")) {
+ 		req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
++		if (nei_len == 0) {
++			// Add neigibor report from neighbor report db to nei_rep buffer
++			nei_len = hostapd_neighbor_insert_buffer (hapd, nei_rep, 1000);
++		}
++	}
+ 	if (os_strstr(cmd, " abridged=1"))
+ 		req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
+-	if (os_strstr(cmd, " disassoc_imminent=1"))
++	if (os_strstr(cmd, " disassoc_imminent=1")) {
+ 		req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
++		/* Set own BSS neighbor report preference value as 0 */
++		hostapd_neighbor_set_own_report_pref(hapd, nei_rep, nei_len, 0);
++	}
+ 	if (os_strstr(cmd, " link_removal_imminent=1"))
+ 		req_mode |= WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT;
+ 
+ #ifdef CONFIG_MBO
++	hostapd_neighbor_set_pref_by_non_pref_chan(hapd, sta, nei_rep, nei_len);
++
+ 	pos = os_strstr(cmd, "mbo=");
+ 	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
+--- a/src/ap/gas_serv.c
++++ b/src/ap/gas_serv.c
+@@ -19,6 +19,7 @@
+ #include "dpp_hostapd.h"
+ #include "sta_info.h"
+ #include "gas_serv.h"
++#include "neighbor_db.h"
+ 
+ 
+ #ifdef CONFIG_DPP
+@@ -369,6 +370,24 @@ static void anqp_add_network_auth_type(struct hostapd_data *hapd,
+ 	}
+ }
+ 
++static void anqp_add_neighbor_report(struct hostapd_data *hapd,
++				       struct wpabuf *buf)
++{
++	struct hostapd_neighbor_entry *nr;
++	u8 *len_pos = gas_anqp_add_element(buf, ANQP_NEIGHBOR_REPORT);
++	if (dl_list_empty(&hapd->nr_db)) {
++		wpabuf_put_le16(buf, 0);
++	}
++	else {
++		dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list ) {
++			wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
++			wpabuf_put_u8(buf, wpabuf_len(nr->nr));
++			wpabuf_put_buf(buf, nr->nr);
++		}
++	}
++	gas_anqp_set_element_len(buf, len_pos);
++}
++
+ 
+ static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
+ 					struct wpabuf *buf)
+@@ -986,6 +1005,9 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
+ 		len += 1000;
+ 	if (request & ANQP_REQ_ICON_REQUEST)
+ 		len += 65536;
++    if (request & ANQP_REQ_NEIGHBOR_REPORT) {
++        len += (40 * hostapd_neighbor_count(hapd));
++    }
+ #ifdef CONFIG_FILS
+ 	if (request & ANQP_FILS_REALM_INFO)
+ 		len += 2 * dl_list_len(&hapd->conf->fils_realms);
+@@ -1028,6 +1050,8 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
+ 		anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
+ 	if (request & ANQP_REQ_EMERGENCY_NAI)
+ 		anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
++	if (request & ANQP_REQ_NEIGHBOR_REPORT)
++		anqp_add_neighbor_report(hapd, buf);
+ 
+ 	for (i = 0; i < num_extra_req; i++) {
+ #ifdef CONFIG_FILS
+@@ -1172,6 +1196,11 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
+ 			     "Emergency NAI",
+ 			     get_anqp_elem(hapd, info_id) != NULL, qi);
+ 		break;
++	case ANQP_NEIGHBOR_REPORT:
++		set_anqp_req(ANQP_REQ_NEIGHBOR_REPORT,
++			     "Neighbor Report",
++			     get_anqp_elem(hapd, info_id) != NULL, qi);
++		break;
+ 	default:
+ #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
+--- a/src/ap/gas_serv.h
++++ b/src/ap/gas_serv.h
+@@ -40,6 +40,8 @@
+ 	(1 << (ANQP_TDLS_CAPABILITY - ANQP_QUERY_LIST))
+ #define ANQP_REQ_EMERGENCY_NAI \
+ 	(1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST))
++#define ANQP_REQ_NEIGHBOR_REPORT \
++	(1 << (ANQP_NEIGHBOR_REPORT - ANQP_QUERY_LIST))
+ /*
+  * 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 f7a7d83d4..d9216a5ae 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)
+ }
+ 
+ 
++int hostapd_neighbor_count(struct hostapd_data *hapd)
++{
++	struct hostapd_neighbor_entry *nr;
++	int count = 0;
++
++	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
++			 list) {
++		count++;
++	}
++	return count;
++}
++
++
++int hostapd_neighbor_insert_buffer(struct hostapd_data *hapd, char *buf,
++        size_t buflen)
++{
++	struct hostapd_neighbor_entry *nr;
++	char *pos = buf;
++
++	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
++			 list) {
++		/* For neighbor report IE, we only need bssid and nr*/
++		*pos++ = WLAN_EID_NEIGHBOR_REPORT;
++		*pos++ = wpabuf_len(nr->nr);
++		os_memcpy(pos, wpabuf_head(nr->nr), wpabuf_len(nr->nr));
++		pos += wpabuf_len(nr->nr);
++	}
++
++	return pos - buf;
++}
++
++
+ static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
+ {
+ 	wpabuf_free(nr->nr);
+@@ -364,3 +396,89 @@ int hostapd_neighbor_sync_own_report(struct hostapd_data *hapd)
+ 
+ 	return 0;
+ }
++
++void hostapd_neighbor_set_own_report_pref(struct hostapd_data *hapd, char *nei_buf,
++			 size_t buflen, const int pref)
++{
++	struct hostapd_neighbor_entry *nr;
++	char *pos, *next_nr;
++
++	pos = nei_buf;
++	next_nr = nei_buf;
++
++	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
++			 list) {
++		pos = next_nr;
++		next_nr = pos + 2 + wpabuf_len(nr->nr);
++		/* Shift 2 bytes for Element ID and Neighbor report length */
++		pos = pos + 2;
++		if(os_memcmp(pos, hapd->own_addr, ETH_ALEN) == 0) {
++			/* Shift for BSSID + BSSID info + Op_class + channel num + PHY type */
++			pos = pos + 6 + 4 + 1 + 1 + 1;
++
++			/* Iterate Subelement */
++			while (next_nr - pos > 0) {
++				if (*pos == 3) {
++					pos = pos + 2;
++					*pos = pref;
++					return;
++				} else {
++					pos++;
++					int shift_len = *pos++;
++					pos = pos + shift_len;
++				}
++			}
++		}
++	}
++}
++
++#ifdef CONFIG_MBO
++void hostapd_neighbor_set_pref_by_non_pref_chan(struct hostapd_data *hapd,
++			 struct sta_info* sta, char *nei_buf, size_t buflen)
++{
++	struct hostapd_neighbor_entry *nr;
++	struct mbo_non_pref_chan_info *info;
++	u8 i;
++
++	for(info = sta->non_pref_chan; info; info = info->next) {
++		/* Check OP_Class and Channel num */
++		for(i = 0; i < info->num_channels; i++) {
++			char *pos, *next_nr;
++
++			pos = nei_buf;
++			next_nr = nei_buf;
++
++			/* Iterate Neighbor report database */
++			dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
++					 list) {
++				pos = next_nr;
++				next_nr = pos + 2 + wpabuf_len(nr->nr);
++				/**
++				 * Shift 12 bytes for Element ID, Neighbor report length,
++				 * BSSID and BSSID info.
++				 */
++				pos = pos + 12;
++				int nr_op_class = *pos++;
++				int nr_channel = *pos;
++				if(info->op_class == nr_op_class && info->channels[i] == nr_channel) {
++					/* Shift for Channel Num + PHY type */
++					pos = pos + 1 + 1;
++
++					// Iterate Subelement
++					while(next_nr - pos > 0) {
++						if(*pos == 3) {
++							pos = pos + 2;
++							*pos = info->pref;
++							break;
++						}else {
++							pos++;
++							int shift_len = *pos++;
++							pos = pos + shift_len;
++						}
++					}
++				}
++			}
++		}
++	}
++}
++#endif
+diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
+index 53f714203..cf1400256 100644
+--- a/src/ap/neighbor_db.h
++++ b/src/ap/neighbor_db.h
+@@ -25,4 +25,13 @@ int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
+ 			    const struct wpa_ssid_value *ssid);
+ void hostapd_free_neighbor_db(struct hostapd_data *hapd);
+ 
++int hostapd_neighbor_count(struct hostapd_data *hapd);
++int hostapd_neighbor_insert_buffer(struct hostapd_data *hapd, char *buf,
++        size_t buflen);
++void hostapd_neighbor_set_own_report_pref(struct hostapd_data *hapd, char *nei_buf,
++			 size_t buflen, const int pref);
++#ifdef CONFIG_MBO
++void hostapd_neighbor_set_pref_by_non_pref_chan(struct hostapd_data *hapd,
++			 struct sta_info* sta, char *nei_buf, size_t buflen);
++#endif
+ #endif /* NEIGHBOR_DB_H */
+diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
+index d259200c9..4ac96b1be 100644
+--- a/src/ap/wnm_ap.c
++++ b/src/ap/wnm_ap.c
+@@ -20,6 +20,7 @@
+ #include "ap/wpa_auth.h"
+ #include "mbo_ap.h"
+ #include "wnm_ap.h"
++#include "ap/neighbor_db.h"
+ 
+ #define MAX_TFS_IE_LEN  1024
+ 
+@@ -390,13 +391,24 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
+ 	u8 *pos;
+ 	int res;
+ 
+-	mgmt = os_zalloc(sizeof(*mgmt));
+-	if (mgmt == NULL)
++	int nr_num = hostapd_neighbor_count(hapd);
++	int nr_size = ETH_ALEN + 4 + 1 + 1 + 1 + 5;
++	int total_nr_size = nr_num * nr_size;
++	u8 *nr_data = os_malloc(total_nr_size);
++	int nr_data_len = 0;
++	if(nr_data == NULL) {
++		wpa_printf (MSG_ERROR, "Failed to allocate memory");
++	} else {
++	    nr_data_len = hostapd_neighbor_insert_buffer(hapd, nr_data, total_nr_size);
++	}
++	mgmt = os_zalloc(sizeof(*mgmt) + nr_data_len);
++	if (mgmt == NULL) {
++		wpa_printf (MSG_ERROR, "Failed to allocate memory for mgmt frame");
+ 		return -1;
++	}
+ 
+ 	sta = ap_get_sta(hapd, addr);
+ 	own_addr = wnm_ap_get_own_addr(hapd, sta);
+-
+ 	os_memcpy(mgmt->da, addr, ETH_ALEN);
+ 	os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ 	os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
+@@ -406,10 +418,18 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
+ 	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+ 	mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
+ 	mgmt->u.action.u.bss_tm_req.req_mode = 0;
++	if(nr_num) {
++		mgmt->u.action.u.bss_tm_req.req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
++	}
+ 	mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
+ 	mgmt->u.action.u.bss_tm_req.validity_interval = 1;
+ 	pos = mgmt->u.action.u.bss_tm_req.variable;
+ 
++	if(nr_num) {
++		os_memcpy(pos, nr_data, nr_data_len);
++		pos += nr_data_len;
++	}
++
+ 	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 "
+@@ -915,6 +935,22 @@ static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
+ }
+ 
+ 
++void bss_termination_disable_iface(void *eloop_ctx, void *timeout_ctx)
++{
++	struct hostapd_data *hapd = eloop_ctx;
++	hostapd_disable_iface(hapd->iface);
++}
++
++
++static void set_disable_iface_timer(struct hostapd_data *hapd, struct sta_info *sta,
++			       int disable_iface_timer)
++{
++	wpa_printf(MSG_DEBUG, "Disable interface timer set to %d secs", disable_iface_timer);
++	eloop_register_timeout(disable_iface_timer, 0,
++			       bss_termination_disable_iface, hapd, NULL);
++}
++
++
+ int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
+ 				   struct sta_info *sta, const char *url,
+ 				   int disassoc_timer)
+@@ -1006,6 +1042,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;
++		set_disable_iface_timer(hapd, sta, hapd->conf->bss_termination_tsf);
+ 	}
+ 
+ 	if (url) {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0013-hostapd-MLO-handle-link_id-in-EAPOL-Tx-status-handle.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0013-hostapd-MLO-handle-link_id-in-EAPOL-Tx-status-handle.patch
deleted file mode 100644
index ab69545..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0013-hostapd-MLO-handle-link_id-in-EAPOL-Tx-status-handle.patch
+++ /dev/null
@@ -1,181 +0,0 @@
-From aa339ee77d60fe9314182cf0e60fa2da4da72b44 Mon Sep 17 00:00:00 2001
-From: Sriram R <quic_srirrama@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:43 +0530
-Subject: [PATCH 013/104] hostapd: MLO: handle link_id in EAPOL Tx status
- handler
-
-Add link id support in EAPOL Tx status handler so that event can be
-routed to appropriate link BSS.
-
-In order to support this, modify hostapd_find_by_sta() function to check
-each BSS's other parnter link BSS sta list as well.
-
-Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/drv_callbacks.c | 108 +++++++++++++++--------------------------
- 1 file changed, 38 insertions(+), 70 deletions(-)
-
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index 12e6b3f36..064c7abae 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -1945,53 +1945,46 @@ static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr)
- 
- 
- static struct hostapd_data * hostapd_find_by_sta(struct hostapd_iface *iface,
--						 const u8 *src, bool rsn)
-+						 const u8 *src, bool rsn,
-+						 struct sta_info **sta_ret)
- {
-+	struct hostapd_data *hapd;
- 	struct sta_info *sta;
- 	unsigned int j;
- 
-+	if (sta_ret)
-+		*sta_ret = NULL;
-+
- 	for (j = 0; j < iface->num_bss; j++) {
--		sta = ap_get_sta(iface->bss[j], src);
-+		hapd = iface->bss[j];
-+		sta = ap_get_sta(hapd, src);
- 		if (sta && (sta->flags & WLAN_STA_ASSOC) &&
--		    (!rsn || sta->wpa_sm))
--			return iface->bss[j];
--	}
--
--	return NULL;
--}
--
--
-+		    (!rsn || sta->wpa_sm)) {
-+			if (sta_ret)
-+				*sta_ret = sta;
-+			return hapd;
- #ifdef CONFIG_IEEE80211BE
--static bool search_mld_sta(struct hostapd_data **p_hapd, const u8 *src)
--{
--	struct hostapd_data *hapd = *p_hapd;
--	unsigned int i;
--
--	/* Search for STA on other MLO BSSs */
--	for (i = 0; i < hapd->iface->interfaces->count; i++) {
--		struct hostapd_iface *h =
--			hapd->iface->interfaces->iface[i];
--		struct hostapd_data *h_hapd = h->bss[0];
--
--		if (!hostapd_is_ml_partner(h_hapd, hapd))
--			continue;
-+		} else if (hapd->conf->mld_ap) {
-+			struct hostapd_data *p_hapd;
- 
--		h_hapd = hostapd_find_by_sta(h, src, false);
--		if (h_hapd) {
--			struct sta_info *sta = ap_get_sta(h_hapd, src);
-+			for_each_mld_link(p_hapd, hapd) {
-+				if (p_hapd == hapd)
-+					continue;
- 
--			if (sta && sta->mld_info.mld_sta &&
--			    sta->mld_assoc_link_id != h_hapd->mld_link_id)
--				continue;
--			*p_hapd = h_hapd;
--			return true;
-+				sta = ap_get_sta(p_hapd, src);
-+				if (sta && (sta->flags & WLAN_STA_ASSOC) &&
-+				    (!rsn || sta->wpa_sm)) {
-+					if (sta_ret)
-+						*sta_ret = sta;
-+					return p_hapd;
-+				}
-+			}
-+#endif /* CONFIG_IEEE80211BE */
- 		}
- 	}
- 
--	return false;
-+	return NULL;
- }
--#endif /* CONFIG_IEEE80211BE */
--
- 
- static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
- 				   const u8 *data, size_t data_len,
-@@ -2001,28 +1994,10 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
- 	struct hostapd_data *orig_hapd = hapd;
- 
- #ifdef CONFIG_IEEE80211BE
--	if (link_id != -1) {
--		struct hostapd_data *h_hapd;
--
--		hapd = switch_link_hapd(hapd, link_id);
--		h_hapd = hostapd_find_by_sta(hapd->iface, src, true);
--		if (!h_hapd)
--			h_hapd = hostapd_find_by_sta(orig_hapd->iface, src,
--						     true);
--		if (!h_hapd)
--			h_hapd = hostapd_find_by_sta(hapd->iface, src, false);
--		if (!h_hapd)
--			h_hapd = hostapd_find_by_sta(orig_hapd->iface, src,
--						     false);
--		if (h_hapd)
--			hapd = h_hapd;
--	} else if (hapd->conf->mld_ap) {
--		search_mld_sta(&hapd, src);
--	} else {
--		hapd = hostapd_find_by_sta(hapd->iface, src, false);
--	}
-+	hapd = switch_link_hapd(hapd, link_id);
-+	hapd = hostapd_find_by_sta(hapd->iface, src, true, NULL);
- #else /* CONFIG_IEEE80211BE */
--	hapd = hostapd_find_by_sta(hapd->iface, src, false);
-+	hapd = hostapd_find_by_sta(hapd->iface, src, false, NULL);
- #endif /* CONFIG_IEEE80211BE */
- 
- 	if (!hapd) {
-@@ -2349,22 +2324,15 @@ err:
- #endif /* CONFIG_OWE */
- 
- static void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
--				    const u8 *data, size_t len, int ack)
-+				    const u8 *data, size_t len, int ack,
-+				    int link_id)
- {
- 	struct sta_info *sta;
--	struct hostapd_iface *iface = hapd->iface;
- 
--	sta = ap_get_sta(hapd, dst);
--	if (sta == NULL && iface->num_bss > 1) {
--		size_t j;
--		for (j = 0; j < iface->num_bss; j++) {
--			hapd = iface->bss[j];
--			sta = ap_get_sta(hapd, dst);
--			if (sta)
--				break;
--		}
--	}
--	if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
-+	hapd = switch_link_hapd(hapd, link_id);
-+	hapd = hostapd_find_by_sta(hapd->iface, dst, false, &sta);
-+
-+	if (sta == NULL) {
- 		wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
- 			   MACSTR " that is not currently associated",
- 			   MAC2STR(dst));
-@@ -2431,11 +2399,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
- 		}
- 		break;
- 	case EVENT_EAPOL_TX_STATUS:
--		hapd = switch_link_hapd(hapd, data->eapol_tx_status.link_id);
- 		hostapd_eapol_tx_status(hapd, data->eapol_tx_status.dst,
- 					data->eapol_tx_status.data,
- 					data->eapol_tx_status.data_len,
--					data->eapol_tx_status.ack);
-+					data->eapol_tx_status.ack,
-+					data->eapol_tx_status.link_id);
- 		break;
- 	case EVENT_DRIVER_CLIENT_POLL_OK:
- 		hostapd_client_poll_ok(hapd, data->client_poll.addr);
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0013-mtk-hostapd-print-some-sae-info-by-hostapd-ctrl.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0013-mtk-hostapd-print-some-sae-info-by-hostapd-ctrl.patch
new file mode 100644
index 0000000..fdc78e1
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0013-mtk-hostapd-print-some-sae-info-by-hostapd-ctrl.patch
@@ -0,0 +1,112 @@
+From e415e499c9ea0939f0c286c5c0422acbc3491756 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 20 Sep 2022 19:33:45 +0800
+Subject: [PATCH 013/126] mtk: hostapd: print some sae info by hostapd ctrl
+
+root@OpenWrt:~# hostapd_cli -i phy0-ap0 pmksa
+Setup at link 0:
+Index / SPA / PMKID / PMK / expiration (in seconds) / opportunistic
+Setup at link 1:
+Index / SPA / PMKID / PMK / expiration (in seconds) / opportunistic
+0 02:0c:43:b2:01:1c b5706f5f53117e00fa46fcf94c225009 7505eb11319fa94add14d0fd091caadf9be2c642aa7363a96e1efa575e794420 43180 0
+Setup at link 2:
+Index / SPA / PMKID / PMK / expiration (in seconds) / opportunistic
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ hostapd/ctrl_iface.c      | 14 ++++++++++++++
+ src/ap/ctrl_iface_ap.c    | 22 +++++++++++++++++++++-
+ src/ap/pmksa_cache_auth.c |  5 ++++-
+ 3 files changed, 39 insertions(+), 2 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index f2733687c..2d90d77c6 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -70,6 +70,7 @@
+ #include "fst/fst_ctrl_iface.h"
+ #include "config_file.h"
+ #include "ctrl_iface.h"
++#include "crypto/dh_groups.h"
+ 
+ 
+ #define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
+@@ -1376,6 +1377,19 @@ static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd,
+ 		if (os_snprintf_error(buflen, res))
+ 			return -1;
+ 		return res;
++	} else if (os_strcmp(cmd, "sae_group_capability") == 0) {
++#ifdef CONFIG_SAE
++		/* see sae_set_group() */
++		res = os_snprintf(buf, buflen, "%s%s%s%s19 20 21",
++				  dh_groups_get(15) ? "15 ": "",
++				  dh_groups_get(16) ? "16 ": "",
++				  dh_groups_get(17) ? "17 ": "",
++				  dh_groups_get(18) ? "18 ": "");
++
++		if (os_snprintf_error(buflen, res))
++			return -1;
++		return res;
++#endif /* CONFIG_SAE */
+ 	}
+ 
+ 	return -1;
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index 50f993253..4d3c7e529 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -1157,7 +1157,27 @@ int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd)
+ int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
+ 				  size_t len)
+ {
+-	return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len);
++	char *pos = buf;
++	int ret, link_id;
++
++	if (!hapd->conf->mld_ap)
++		return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len);
++
++	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
++		struct hostapd_data *h = hostapd_mld_get_link_bss(hapd, link_id);
++
++		if (!h)
++			continue;
++
++		ret = os_snprintf(pos, len - (pos - buf), "Setup at link %u:\n", h->mld_link_id);
++		if (os_snprintf_error(len - (pos - buf), ret))
++			return pos - buf;
++		pos += ret;
++
++		pos += wpa_auth_pmksa_list(h->wpa_auth, pos, len - (pos - buf));
++	}
++
++	return pos - buf;
+ }
+ 
+ 
+diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
+index 2fce8383d..8a63cb682 100644
+--- a/src/ap/pmksa_cache_auth.c
++++ b/src/ap/pmksa_cache_auth.c
+@@ -658,7 +658,7 @@ int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
+ 
+ 	os_get_reltime(&now);
+ 	ret = os_snprintf(pos, buf + len - pos,
+-			  "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n");
++			  "Index / SPA / PMKID / PMK / expiration (in seconds) / opportunistic\n");
+ 	if (os_snprintf_error(buf + len - pos, ret))
+ 		return pos - buf;
+ 	pos += ret;
+@@ -672,6 +672,9 @@ int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
+ 		pos += ret;
+ 		pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
+ 					PMKID_LEN);
++		*pos++ = ' ';
++		pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmk,
++					entry->pmk_len);
+ 		ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
+ 				  (int) (entry->expiration - now.sec),
+ 				  entry->opportunistic);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0014-hostapd-MLO-update-all-partner-s-link-beacon.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0014-hostapd-MLO-update-all-partner-s-link-beacon.patch
deleted file mode 100644
index 0cec211..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0014-hostapd-MLO-update-all-partner-s-link-beacon.patch
+++ /dev/null
@@ -1,77 +0,0 @@
-From 4be307ebbc6b94b6a334855a9efe633d77ca98fe Mon Sep 17 00:00:00 2001
-From: Sriram R <quic_srirrama@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:44 +0530
-Subject: [PATCH 014/104] hostapd: MLO: update all partner's link beacon
-
-Whenever there is a beacon update of any one of the link, all its other
-partner's link beacon should be refreshed.
-
-Add changes to update all partner's link beacon.
-
-Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/beacon.c | 27 +++++++++++++++++++--------
- 1 file changed, 19 insertions(+), 8 deletions(-)
-
-diff --git a/src/ap/beacon.c b/src/ap/beacon.c
-index 195c7bbd9..b780d98e4 100644
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -2648,7 +2648,7 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
- 	struct hostapd_iface *iface = hapd->iface;
- 	int ret;
- 	size_t i, j;
--	bool is_6g;
-+	bool is_6g, hapd_mld = false;
- 
- 	ret = __ieee802_11_set_beacon(hapd);
- 	if (ret != 0)
-@@ -2657,26 +2657,37 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
- 	if (!iface->interfaces || iface->interfaces->count <= 1)
- 		return 0;
- 
-+#ifdef CONFIG_IEEE80211BE
-+	hapd_mld = hapd->conf->mld_ap;
-+#endif /* CONFIG_IEEE80211BE */
-+
- 	/* Update Beacon frames in case of 6 GHz colocation or AP MLD */
- 	is_6g = is_6ghz_op_class(iface->conf->op_class);
- 	for (j = 0; j < iface->interfaces->count; j++) {
- 		struct hostapd_iface *other;
--		bool mld_ap = false;
-+		bool other_iface_6g;
- 
- 		other = iface->interfaces->iface[j];
- 		if (other == iface || !other || !other->conf)
- 			continue;
- 
--#ifdef CONFIG_IEEE80211BE
--		if (hostapd_is_ml_partner(hapd, other->bss[0]))
--			mld_ap = true;
--#endif /* CONFIG_IEEE80211BE */
-+		other_iface_6g = is_6ghz_op_class(other->conf->op_class);
- 
--		if (is_6g == is_6ghz_op_class(other->conf->op_class) &&
--		    !mld_ap)
-+		if (is_6g == other_iface_6g && !hapd_mld)
- 			continue;
- 
- 		for (i = 0; i < other->num_bss; i++) {
-+#ifdef CONFIG_IEEE80211BE
-+			bool mld_ap = false;
-+
-+			if (hapd_mld && other->bss[i]->conf->mld_ap &&
-+			    hostapd_is_ml_partner(hapd, other->bss[i]))
-+				mld_ap = true;
-+
-+			if (is_6g == other_iface_6g && !mld_ap)
-+				continue;
-+#endif /* CONFIG_IEEE80211BE */
-+
- 			if (other->bss[i] && other->bss[i]->started)
- 				__ieee802_11_set_beacon(other->bss[i]);
- 		}
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0014-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0014-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch
new file mode 100644
index 0000000..97cf42b
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0014-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch
@@ -0,0 +1,196 @@
+From 05ca0e1db230ecd668db4597ac1ef1d937459ea3 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Tue, 31 May 2022 21:15:54 +0800
+Subject: [PATCH 014/126] mtk: hostapd: add support for runtime set in-band
+ discovery
+
+Usage:
+hostapd_cli unsolic_probe_resp [tx_type] [interval]
+
+0: disable all in-band discovery
+1: enable unsolicited probe response
+2: enable FILS discovery
+
+The mac80211 layer already has a new variable update,
+so the redundant variable disable has been removed.
+
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ hostapd/ctrl_iface.c         | 66 ++++++++++++++++++++++++++++++++++++
+ hostapd/hostapd_cli.c        | 20 +++++++++++
+ src/ap/beacon.c              |  5 ++-
+ src/drivers/driver_nl80211.c |  6 ++--
+ 4 files changed, 94 insertions(+), 3 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 2d90d77c6..40244c5e7 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -773,6 +773,69 @@ static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd,
+ 
+ #endif /* CONFIG_INTERWORKING */
+ 
++static int hostapd_ctrl_iface_inband_discovery(struct hostapd_data *hapd,
++					       const char *cmd)
++{
++	struct hostapd_bss_config *conf = hapd->conf;
++	const char *pos = cmd;
++	int tx_type, interval, ret;
++
++	tx_type = atoi(pos);
++	if (tx_type < 0 || tx_type > 2) {
++		wpa_printf(MSG_ERROR, "Invalid tx type\n");
++		return -1;
++	}
++
++	pos = os_strchr(pos, ' ');
++	if(!pos)
++		return -1;
++	pos++;
++	interval = atoi(pos);
++	if (interval < 0 || interval > 20) {
++		wpa_printf(MSG_ERROR, "Invalid interval value\n");
++		return -1;
++	}
++
++	wpa_printf(MSG_ERROR, "Set inband discovery type:%d, interval:%d\n",
++			      tx_type, interval);
++
++#define DISABLE_INBAND_DISC 0
++#define UNSOL_PROBE_RESP 1
++#define FILS_DISCOVERY 2
++
++#ifdef CONFIG_FILS
++	conf->fils_discovery_max_int = 0;
++	conf->fils_discovery_min_int = 0;
++#endif /* CONFIG_FILS */
++	conf->unsol_bcast_probe_resp_interval = 0;
++
++	switch (tx_type) {
++	case DISABLE_INBAND_DISC:
++	default:
++		/* Disable both Unsolicited probe response and FILS discovery*/
++		break;
++	case UNSOL_PROBE_RESP:
++		/* Enable Unsolicited probe response */
++		conf->unsol_bcast_probe_resp_interval = interval;
++		break;
++#ifdef CONFIG_FILS
++	case FILS_DISCOVERY:
++		/* Enable FILS discovery */
++		conf->fils_discovery_min_int = interval;
++		conf->fils_discovery_max_int = interval;
++		break;
++#endif /* CONFIG_FILS */
++	}
++
++	ret = ieee802_11_update_beacons(hapd->iface);
++	if(ret) {
++		wpa_printf(MSG_DEBUG,
++			"Failed to update with inband discovery parameters\n");
++		return -1;
++	}
++
++	return 0;
++}
+ 
+ #ifdef CONFIG_WNM_AP
+ 
+@@ -4175,6 +4238,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		if (hostapd_ctrl_iface_coloc_intf_req(hapd, buf + 15))
+ 			reply_len = -1;
+ #endif /* CONFIG_WNM_AP */
++	} else if (os_strncmp(buf, "INBAND_DISCOVERY ", 17) == 0) {
++		if (hostapd_ctrl_iface_inband_discovery(hapd, buf + 17))
++			reply_len = -1;
+ 	} else if (os_strcmp(buf, "GET_CONFIG") == 0) {
+ 		reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
+ 							  reply_size);
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index ebf8addc1..bb15bc751 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -664,6 +664,24 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
+ 	return wpa_ctrl_command(ctrl, buf);
+ }
+ 
++static int hostapd_cli_cmd_inband_discovery(struct wpa_ctrl *ctrl, int argc,
++					    char *argv[])
++{
++	char buf[300];
++	int res;
++
++	if (argc < 2) {
++		printf("Invalid 'inband_discovery' command - two arguments"
++		       "tx_type interval\n");
++		return -1;
++	}
++
++	res = os_snprintf(buf, sizeof(buf), "INBAND_DISCOVERY %s %s",
++			  argv[0], argv[1]);
++	if (os_snprintf_error(sizeof(buf), res))
++		return -1;
++	return wpa_ctrl_command(ctrl, buf);
++}
+ 
+ static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
+ 					     char *argv[])
+@@ -1883,6 +1901,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 	{ "driver", hostapd_cli_cmd_driver, NULL,
+ 	  "<driver sub command> [<hex formatted data>] = send driver command data" },
+ #endif /* ANDROID */
++	{ "inband_discovery", hostapd_cli_cmd_inband_discovery, NULL,
++          "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 58b561661..9bf73747f 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -2164,6 +2164,8 @@ static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
+ 				   struct wpa_driver_ap_params *params)
+ {
+ 	params->fd_max_int = hapd->conf->fils_discovery_max_int;
++	params->ubpr.unsol_bcast_probe_resp_interval =
++		hapd->conf->unsol_bcast_probe_resp_interval;
+ 	if (is_6ghz_op_class(hapd->iconf->op_class) &&
+ 	    params->fd_max_int > FD_MAX_INTERVAL_6GHZ)
+ 		params->fd_max_int = FD_MAX_INTERVAL_6GHZ;
+@@ -2172,7 +2174,8 @@ static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
+ 	if (params->fd_min_int > params->fd_max_int)
+ 		params->fd_min_int = params->fd_max_int;
+ 
+-	if (params->fd_max_int)
++	if (params->fd_max_int || (is_6ghz_op_class(hapd->iconf->op_class) &&
++	    !params->ubpr.unsol_bcast_probe_resp_interval))
+ 		return hostapd_gen_fils_discovery(hapd,
+ 						  &params->fd_frame_tmpl_len);
+ 
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index a70eaae38..2c68bf997 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -4790,7 +4790,6 @@ static int nl80211_fils_discovery(struct i802_bss *bss, struct nl_msg *msg,
+ 	     nla_put(msg, NL80211_FILS_DISCOVERY_ATTR_TMPL,
+ 		     params->fd_frame_tmpl_len, params->fd_frame_tmpl)))
+ 		return -1;
+-
+ 	nla_nest_end(msg, attr);
+ 	return 0;
+ }
+@@ -5422,7 +5421,10 @@ static int wpa_driver_nl80211_set_ap(void *priv,
+ #endif /* CONFIG_SAE */
+ 
+ #ifdef CONFIG_FILS
+-	if (params->fd_max_int && nl80211_fils_discovery(bss, msg, params) < 0)
++	if ((params->fd_max_int ||
++	    ((params->freq->freq > 5950 && params->freq->freq <= 7115) &&
++	      !(params->ubpr.unsol_bcast_probe_resp_interval))) &&
++	     nl80211_fils_discovery(bss, msg, params) < 0)
+ 		goto fail;
+ #endif /* CONFIG_FILS */
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0015-hostapd-MLO-skip-assoc-link-processing-in-ML-info.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0015-hostapd-MLO-skip-assoc-link-processing-in-ML-info.patch
deleted file mode 100644
index 3dd5690..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0015-hostapd-MLO-skip-assoc-link-processing-in-ML-info.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 6eeca68d65795783243d3634627b4ac8f79e3d15 Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:45 +0530
-Subject: [PATCH 015/104] hostapd: MLO: skip assoc link processing in ML info
-
-Currently during processing ML info in association request, all links are
-iterated over. However, the assoc link info will not be present in the
-ML info hence following print is observed during ML association (assoc link
-is 1) -
-
-MLD: No link match for link_id=1
-
-Add changes to skip processing for the assoc link. No functionality
-changes.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/ieee802_11.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 9f7e9afdd..39c63f29b 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -4586,7 +4586,7 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
- 		struct mld_link_info *link = &sta->mld_info.links[i];
- 		bool link_bss_found = false;
- 
--		if (!link->valid)
-+		if (!link->valid || i == sta->mld_assoc_link_id)
- 			continue;
- 
- 		for_each_mld_link(bss, hapd) {
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0015-mtk-hostapd-Add-mtk_vendor.h.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0015-mtk-hostapd-Add-mtk_vendor.h.patch
new file mode 100644
index 0000000..ef346f2
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0015-mtk-hostapd-Add-mtk_vendor.h.patch
@@ -0,0 +1,216 @@
+From e37c0f7888fefa1d8367209dd89dc18ab63c4711 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 30 May 2022 15:04:57 +0800
+Subject: [PATCH 015/126] mtk: hostapd: Add mtk_vendor.h
+
+---
+ src/common/mtk_vendor.h | 197 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 197 insertions(+)
+ create mode 100644 src/common/mtk_vendor.h
+
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+new file mode 100644
+index 000000000..4a19d2fc9
+--- /dev/null
++++ b/src/common/mtk_vendor.h
+@@ -0,0 +1,197 @@
++// SPDX-License-Identifier: ISC
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++#ifndef MTK_VENDOR_H
++#define MTK_VENDOR_H
++
++#define OUI_MTK    0x000ce7
++
++enum mtk_nl80211_vendor_subcmds {
++	MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
++	MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
++	MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
++	MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
++	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
++};
++
++enum mtk_vendor_attr_edcca_ctrl {
++	MTK_VENDOR_ATTR_EDCCA_THRESHOLD_INVALID = 0,
++
++	MTK_VENDOR_ATTR_EDCCA_CTRL_MODE,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
++};
++
++enum mtk_vendor_attr_edcca_ctrl_mode {
++	EDCCA_CTRL_SET_EN = 0,
++	EDCCA_CTRL_SET_THERS,
++	EDCCA_CTRL_GET_EN,
++	EDCCA_CTRL_GET_THERS,
++	EDCCA_CTRL_NUM,
++};
++
++static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
++};
++
++enum mtk_vendor_attr_csi_ctrl {
++	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
++	MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
++	MTK_VENDOR_ATTR_CSI_CTRL_INTERVAL,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_DATA,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_CSI_CTRL,
++	MTK_VENDOR_ATTR_CSI_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_CSI_CTRL - 1
++};
++
++enum mtk_vendor_attr_csi_data {
++	MTK_VENDOR_ATTR_CSI_DATA_UNSPEC,
++	MTK_VENDOR_ATTR_CSI_DATA_PAD,
++
++	MTK_VENDOR_ATTR_CSI_DATA_VER,
++	MTK_VENDOR_ATTR_CSI_DATA_TS,
++	MTK_VENDOR_ATTR_CSI_DATA_RSSI,
++	MTK_VENDOR_ATTR_CSI_DATA_SNR,
++	MTK_VENDOR_ATTR_CSI_DATA_BW,
++	MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
++	MTK_VENDOR_ATTR_CSI_DATA_TA,
++	MTK_VENDOR_ATTR_CSI_DATA_I,
++	MTK_VENDOR_ATTR_CSI_DATA_Q,
++	MTK_VENDOR_ATTR_CSI_DATA_INFO,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD1,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD2,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD3,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD4,
++	MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
++	MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
++	MTK_VENDOR_ATTR_CSI_DATA_MODE,
++	MTK_VENDOR_ATTR_CSI_DATA_H_IDX,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_CSI_DATA,
++	MTK_VENDOR_ATTR_CSI_DATA_MAX =
++		NUM_MTK_VENDOR_ATTRS_CSI_DATA - 1
++};
++
++enum mtk_vendor_attr_mnt_ctrl {
++	MTK_VENDOR_ATTR_AMNT_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_AMNT_CTRL_SET,
++	MTK_VENDOR_ATTR_AMNT_CTRL_DUMP,
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_AMNT_CTRL,
++	MTK_VENDOR_ATTR_AMNT_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_AMNT_CTRL - 1
++};
++
++enum mtk_vendor_attr_mnt_set {
++	MTK_VENDOR_ATTR_AMNT_SET_UNSPEC,
++
++	MTK_VENDOR_ATTR_AMNT_SET_INDEX,
++	MTK_VENDOR_ATTR_AMNT_SET_MACADDR,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_AMNT_SET,
++	MTK_VENDOR_ATTR_AMNT_SET_MAX =
++		NUM_MTK_VENDOR_ATTRS_AMNT_SET - 1
++};
++
++enum mtk_vendor_attr_mnt_dump {
++	MTK_VENDOR_ATTR_AMNT_DUMP_UNSPEC,
++
++	MTK_VENDOR_ATTR_AMNT_DUMP_INDEX,
++	MTK_VENDOR_ATTR_AMNT_DUMP_LEN,
++	MTK_VENDOR_ATTR_AMNT_DUMP_RESULT,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
++	MTK_VENDOR_ATTR_AMNT_DUMP_MAX =
++		NUM_MTK_VENDOR_ATTRS_AMNT_DUMP - 1
++};
++
++enum mtk_vendor_attr_wireless_ctrl {
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
++};
++
++enum mtk_vendor_attr_rfeature_ctrl {
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
++};
++
++#define CSI_MAX_COUNT 256
++#define ETH_ALEN 6
++
++struct csi_data {
++	s16 data_i[CSI_MAX_COUNT];
++	s16 data_q[CSI_MAX_COUNT];
++	s8 rssi;
++	u8 snr;
++	u32 ts;
++	u8 data_bw;
++	u8 pri_ch_idx;
++	u8 ta[ETH_ALEN];
++	u32 info;
++	u8 rx_mode;
++	u32 h_idx;
++	u16 tx_idx;
++	u16 rx_idx;
++};
++
++struct amnt_data {
++	u8 idx;
++	u8 addr[ETH_ALEN];
++	s8 rssi[4];
++	u32 last_seen;
++};
++#endif /* MTK_VENDOR_H */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0016-hostapd-MLO-Enhance-wpa-state-machine.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0016-hostapd-MLO-Enhance-wpa-state-machine.patch
deleted file mode 100644
index 0c17b68..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0016-hostapd-MLO-Enhance-wpa-state-machine.patch
+++ /dev/null
@@ -1,335 +0,0 @@
-From 11cfbaf42eaadf0fd7b50d13f0b7664c1675dc11 Mon Sep 17 00:00:00 2001
-From: Rameshkumar Sundaram <quic_ramess@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:46 +0530
-Subject: [PATCH 016/104] hostapd: MLO: Enhance wpa state machine
-
-Add required ML Specific members in wpa_authenticator and struct
-wpa_state_machine to maintain self and partner link information.
-
-Maintain state machine object in all associated link stations and
-destroy/remove references from the same whenever link stations are getting
-removed.
-
-Increase the wpa_group object reference count for all links in which ML
-station is getting associated and release the same whenever link stations
-are getting removed.
-
-Signed-off-by: Rameshkumar Sundaram <quic_ramess@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/ieee802_11.c |   9 ++--
- src/ap/sta_info.c   |  35 ++++++++++++++-
- src/ap/wpa_auth.c   | 101 +++++++++++++++++++++++++++++++++++++++++---
- src/ap/wpa_auth.h   |  16 +++++++
- src/ap/wpa_auth_i.h |   8 ++++
- 5 files changed, 159 insertions(+), 10 deletions(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 39c63f29b..9d04bdf43 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -4467,6 +4467,8 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
- 	}
- 
- 	sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
-+	sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
-+
- 	status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
- 	if (status != WLAN_STATUS_SUCCESS) {
- 		wpa_printf(MSG_DEBUG, "MLD: link: Element check failed");
-@@ -4474,7 +4476,6 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
- 	}
- 
- 	ap_sta_set_mld(sta, true);
--	sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
- 
- 	os_memcpy(&sta->mld_info, &origin_sta->mld_info, sizeof(sta->mld_info));
- 	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-@@ -4501,9 +4502,11 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
- 			ieee802_11_update_beacons(hapd->iface);
- 	}
- 
--	/* RSN Authenticator should always be the one on the original station */
-+	/* Maintain state machine reference on all link STAs, this is needed
-+	 * during Group rekey handling.
-+	 */
- 	wpa_auth_sta_deinit(sta->wpa_sm);
--	sta->wpa_sm = NULL;
-+	sta->wpa_sm = origin_sta->wpa_sm;
- 
- 	/*
- 	 * Do not initialize the EAPOL state machine.
-diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
-index 2423ff189..d483aa9d3 100644
---- a/src/ap/sta_info.c
-+++ b/src/ap/sta_info.c
-@@ -199,6 +199,26 @@ static void __ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
- 	hostapd_drv_sta_remove(hapd, sta->addr);
- }
- 
-+#ifdef CONFIG_IEEE80211BE
-+static void set_for_each_partner_link_sta(struct hostapd_data *hapd,
-+					  struct sta_info *psta, void *data)
-+{
-+	struct sta_info *lsta;
-+	struct hostapd_data *lhapd;
-+
-+	if (!ap_sta_is_mld(hapd, psta))
-+		return;
-+
-+	for_each_mld_link(lhapd, hapd) {
-+		if (lhapd == hapd)
-+			continue;
-+
-+		lsta = ap_get_sta(lhapd, psta->addr);
-+		if (lsta)
-+			lsta->wpa_sm = data;
-+	}
-+}
-+#endif /* CONFIG_IEEE80211BE */
- 
- void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
- {
-@@ -317,8 +337,17 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
- 
- #ifdef CONFIG_IEEE80211BE
- 	if (!ap_sta_is_mld(hapd, sta) ||
--	    hapd->mld_link_id == sta->mld_assoc_link_id)
-+	    hapd->mld_link_id == sta->mld_assoc_link_id) {
- 		wpa_auth_sta_deinit(sta->wpa_sm);
-+		/* Remove refrences from partner links. */
-+		set_for_each_partner_link_sta(hapd, sta, NULL);
-+	}
-+
-+	/* Release group references in case non assoc link STA is removed
-+	 * before assoc link STA
-+	 */
-+	if (hostapd_sta_is_link_sta(hapd, sta))
-+		wpa_release_link_auth_ref(sta->wpa_sm, hapd->mld_link_id);
- #else
- 	wpa_auth_sta_deinit(sta->wpa_sm);
- #endif /* CONFIG_IEEE80211BE */
-@@ -903,8 +932,10 @@ static void ap_sta_disconnect_common(struct hostapd_data *hapd,
- 	ieee802_1x_free_station(hapd, sta);
- #ifdef CONFIG_IEEE80211BE
- 	if (!hapd->conf->mld_ap ||
--	    hapd->mld_link_id == sta->mld_assoc_link_id)
-+	    hapd->mld_link_id == sta->mld_assoc_link_id) {
- 		wpa_auth_sta_deinit(sta->wpa_sm);
-+		set_for_each_partner_link_sta(hapd, sta, NULL);
-+	}
- #else
- 	wpa_auth_sta_deinit(sta->wpa_sm);
- #endif /* CONFIG_IEEE80211BE */
-diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
-index 0d15c4209..8c1052c25 100644
---- a/src/ap/wpa_auth.c
-+++ b/src/ap/wpa_auth.c
-@@ -102,6 +102,59 @@ static const u8 * wpa_auth_get_spa(const struct wpa_state_machine *sm)
- 	return sm->addr;
- }
- 
-+#ifdef CONFIG_IEEE80211BE
-+void wpa_release_link_auth_ref(struct wpa_state_machine *sm, int release_link_id)
-+{
-+	int link_id;
-+
-+	if (!sm || release_link_id >= MAX_NUM_MLD_LINKS)
-+		return;
-+
-+	for_each_sm_auth(sm, link_id)
-+		if (link_id == release_link_id) {
-+			wpa_group_put(sm->mld_links[link_id].wpa_auth,
-+				      sm->mld_links[link_id].wpa_auth->group);
-+			sm->mld_links[link_id].wpa_auth = NULL;
-+		}
-+}
-+
-+static int wpa_get_link_sta_auth(struct wpa_authenticator *wpa_auth, void *data)
-+{
-+	struct wpa_get_link_auth_ctx *ctx = data;
-+
-+	if (os_memcmp(wpa_auth->addr, ctx->addr, ETH_ALEN) != 0)
-+		return 0;
-+	ctx->wpa_auth = wpa_auth;
-+	return 1;
-+}
-+
-+static int wpa_get_primary_wpa_auth_cb(struct wpa_authenticator *wpa_auth, void *data)
-+{
-+	struct wpa_get_link_auth_ctx *ctx = data;
-+
-+	if (!wpa_auth->is_ml || os_memcmp(wpa_auth->mld_addr, ctx->addr, ETH_ALEN) != 0 ||
-+	    !wpa_auth->primary_auth)
-+		return 0;
-+
-+	ctx->wpa_auth = wpa_auth;
-+	return 1;
-+}
-+
-+static struct wpa_authenticator *
-+wpa_get_primary_wpa_auth(struct wpa_authenticator *wpa_auth)
-+{
-+	struct wpa_get_link_auth_ctx ctx;
-+
-+	if (!wpa_auth || !wpa_auth->is_ml || wpa_auth->primary_auth)
-+		return wpa_auth;
-+
-+	ctx.addr = wpa_auth->mld_addr;
-+	ctx.wpa_auth = NULL;
-+	wpa_auth_for_each_auth(wpa_auth, wpa_get_primary_wpa_auth_cb, &ctx);
-+
-+	return ctx.wpa_auth;
-+}
-+#endif /* CONFIG_IEEE80211BE */
- 
- static inline int wpa_auth_mic_failure_report(
- 	struct wpa_authenticator *wpa_auth, const u8 *addr)
-@@ -798,6 +851,10 @@ void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm)
- 
- static void wpa_free_sta_sm(struct wpa_state_machine *sm)
- {
-+#ifdef CONFIG_IEEE80211BE
-+	int link_id;
-+#endif /* CONFIG_IEEE80211BE */
-+
- #ifdef CONFIG_P2P
- 	if (WPA_GET_BE32(sm->ip_addr)) {
- 		wpa_printf(MSG_DEBUG,
-@@ -821,6 +878,13 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
- 	os_free(sm->last_rx_eapol_key);
- 	os_free(sm->wpa_ie);
- 	os_free(sm->rsnxe);
-+#ifdef CONFIG_IEEE80211BE
-+	for_each_sm_auth(sm, link_id) {
-+		wpa_group_put(sm->mld_links[link_id].wpa_auth,
-+			      sm->mld_links[link_id].wpa_auth->group);
-+		sm->mld_links[link_id].wpa_auth = NULL;
-+	}
-+#endif /* CONFIG_IEEE80211BE */
- 	wpa_group_put(sm->wpa_auth, sm->group);
- #ifdef CONFIG_DPP2
- 	wpabuf_clear_free(sm->dpp_z);
-@@ -831,7 +895,7 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
- 
- void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
- {
--	struct wpa_authenticator *wpa_auth;
-+	struct wpa_authenticator *wpa_auth, *primary_wpa_auth;
- 
- 	if (!sm)
- 		return;
-@@ -840,10 +904,18 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
- 	if (wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) {
- 		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
- 				"strict rekeying - force GTK rekey since STA is leaving");
-+
-+#ifdef CONFIG_IEEE80211BE
-+		if (wpa_auth->is_ml && !wpa_auth->primary_auth)
-+			primary_wpa_auth = wpa_get_primary_wpa_auth(wpa_auth);
-+		else
-+#endif /* CONFIG_IEEE80211BE */
-+			primary_wpa_auth = wpa_auth;
-+
- 		if (eloop_deplete_timeout(0, 500000, wpa_rekey_gtk,
--					  wpa_auth, NULL) == -1)
-+					  primary_wpa_auth, NULL) == -1)
- 			eloop_register_timeout(0, 500000, wpa_rekey_gtk,
--					       wpa_auth, NULL);
-+					       primary_wpa_auth, NULL);
- 	}
- 
- 	eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
-@@ -6835,6 +6907,7 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
- 	for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- 		struct mld_link_info *link = &info->links[link_id];
- 		struct mld_link *sm_link = &sm->mld_links[link_id];
-+		struct wpa_get_link_auth_ctx ctx;
- 
- 		sm_link->valid = link->valid;
- 		if (!link->valid)
-@@ -6849,10 +6922,28 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
- 			   MAC2STR(sm_link->own_addr),
- 			   MAC2STR(sm_link->peer_addr));
- 
--		if (link_id != mld_assoc_link_id)
-+		ml_rsn_info.links[i++].link_id = link_id;
-+
-+		if (link_id != mld_assoc_link_id) {
- 			sm->n_mld_affiliated_links++;
-+			ctx.addr = link->local_addr;
-+			ctx.wpa_auth = NULL;
-+			wpa_auth_for_each_auth(sm->wpa_auth, wpa_get_link_sta_auth, &ctx);
-+
-+			if (ctx.wpa_auth) {
-+				sm_link->wpa_auth = ctx.wpa_auth;
-+				wpa_group_get(sm_link->wpa_auth,
-+					      sm_link->wpa_auth->group);
-+			}
-+		} else {
-+			sm_link->wpa_auth = sm->wpa_auth;
-+		}
- 
--		ml_rsn_info.links[i++].link_id = link_id;
-+		if (!sm_link->wpa_auth)
-+			wpa_printf(MSG_ERROR, "Unable to find authenticator object for "
-+				    "ML STA " MACSTR " on link " MACSTR " link id %d",
-+				    MAC2STR(sm->own_mld_addr), MAC2STR(sm_link->own_addr),
-+				    link_id);
- 	}
- 
- 	ml_rsn_info.n_mld_links = i;
-diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
-index c74862307..1446872f3 100644
---- a/src/ap/wpa_auth.h
-+++ b/src/ap/wpa_auth.h
-@@ -647,4 +647,20 @@ void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
- 			      struct wpa_auth_ml_link_key_info *info,
- 			      bool mgmt_frame_prot, bool beacon_prot);
- 
-+#ifdef CONFIG_IEEE80211BE
-+void wpa_release_link_auth_ref(struct wpa_state_machine *sm,
-+			       int release_link_id);
-+
-+#define for_each_sm_auth(sm, link_id) \
-+	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) \
-+		if (sm->mld_links[link_id].valid && \
-+		    sm->mld_links[link_id].wpa_auth && \
-+		    sm->wpa_auth != sm->mld_links[link_id].wpa_auth) \
-+
-+struct wpa_get_link_auth_ctx {
-+	u8 *addr;
-+	struct wpa_authenticator *wpa_auth;
-+};
-+#endif /* CONFIG_IEEE80211BE */
-+
- #endif /* WPA_AUTH_H */
-diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
-index 9ba830415..9ba90749d 100644
---- a/src/ap/wpa_auth_i.h
-+++ b/src/ap/wpa_auth_i.h
-@@ -186,6 +186,7 @@ struct wpa_state_machine {
- 		size_t rsne_len;
- 		const u8 *rsnxe;
- 		size_t rsnxe_len;
-+		struct wpa_authenticator *wpa_auth;
- 	} mld_links[MAX_NUM_MLD_LINKS];
- #endif /* CONFIG_IEEE80211BE */
- };
-@@ -262,6 +263,13 @@ struct wpa_authenticator {
- #ifdef CONFIG_P2P
- 	struct bitfield *ip_pool;
- #endif /* CONFIG_P2P */
-+
-+#ifdef CONFIG_IEEE80211BE
-+	bool is_ml;
-+	u8 mld_addr[ETH_ALEN];
-+	u8 link_id;
-+	bool primary_auth;
-+#endif /* CONFIG_IEEE80211BE */
- };
- 
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0016-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0016-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch
new file mode 100644
index 0000000..9fb8962
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0016-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch
@@ -0,0 +1,631 @@
+From 75020e1c62dd8d16c872614861146d8cfc12f6df Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 30 May 2022 16:31:34 +0800
+Subject: [PATCH 016/126] mtk: hostapd: Support EDCCA hostapd configuration
+
+edcca_enable and edcca_compensation and implement edcca related handlers.
+
+---
+ hostapd/config_file.c             |  34 ++++++
+ hostapd/ctrl_iface.c              | 124 +++++++++++++++++++++
+ src/ap/ap_config.c                |   4 +
+ src/ap/ap_config.h                |  30 ++++++
+ src/ap/ap_drv_ops.c               |  24 +++++
+ src/ap/ap_drv_ops.h               |   4 +
+ src/ap/hostapd.c                  |   7 ++
+ src/common/mtk_vendor.h           |  20 ++--
+ src/drivers/driver.h              |   4 +
+ src/drivers/driver_nl80211.c      | 174 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   7 ++
+ 12 files changed, 427 insertions(+), 6 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index f3968ec95..9f5ee48bf 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5470,6 +5470,40 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		os_strlcpy(bss->apup_peer_ifname_prefix,
+ 		           pos, sizeof(bss->apup_peer_ifname_prefix));
+ #endif // def CONFIG_APUP
++	} else if (os_strcmp(buf, "edcca_threshold") == 0) {
++		if (hostapd_parse_intlist(&conf->edcca_threshold, pos) ||
++		    conf->edcca_threshold[0] < EDCCA_MIN_CONFIG_THRES ||
++		    conf->edcca_threshold[0] > EDCCA_MAX_CONFIG_THRES ||
++		    conf->edcca_threshold[1] < EDCCA_MIN_CONFIG_THRES ||
++		    conf->edcca_threshold[1] > EDCCA_MAX_CONFIG_THRES ||
++		    conf->edcca_threshold[2] < EDCCA_MIN_CONFIG_THRES ||
++		    conf->edcca_threshold[2] > EDCCA_MAX_CONFIG_THRES ||
++		    conf->edcca_threshold[3] < EDCCA_MIN_CONFIG_THRES ||
++		    conf->edcca_threshold[3] > EDCCA_MAX_CONFIG_THRES) {
++			wpa_printf(MSG_ERROR, "Line %d: invalid edcca threshold",
++				   line);
++			return 1;
++		}
++	} else if (os_strcmp(buf, "edcca_enable") == 0) {
++		int mode = atoi(pos);
++		if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
++			wpa_printf(MSG_ERROR, "Line %d: Invalid edcca_enable %d;"
++				  " allowed value 0 (Force Disable) or 1(Auto) ",
++				   line, mode);
++			return 1;
++		}
++		conf->edcca_enable = (u8) mode;
++	} else if (os_strcmp(buf, "edcca_compensation") == 0) {
++		int val = atoi(pos);
++		if (val < EDCCA_MIN_COMPENSATION ||
++		    val > EDCCA_MAX_COMPENSATION) {
++			wpa_printf(MSG_ERROR, "Line %d: Invalid compensation"
++				   " value %d; allowed value %d ~ %d.",
++				   line, val, EDCCA_MIN_COMPENSATION,
++				   EDCCA_MAX_COMPENSATION);
++			return 1;
++		}
++		conf->edcca_compensation = (s8) val;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 40244c5e7..8a9fa3ec9 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -545,6 +545,19 @@ static const char * pbc_status_str(enum pbc_status status)
+ }
+ 
+ 
++static const char *edcca_mode_str(enum edcca_mode status)
++{
++	switch (status) {
++		case EDCCA_MODE_FORCE_DISABLE:
++			return "Force Disable";
++		case EDCCA_MODE_AUTO:
++			return "Auto";
++		default:
++			return "Unknown";
++	}
++}
++
++
+ static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd,
+ 					     char *buf, size_t buflen)
+ {
+@@ -3774,6 +3787,111 @@ static int hostapd_ctrl_iface_link_remove(struct hostapd_data *hapd, char *cmd,
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
+ 
++static int
++hostapd_ctrl_iface_set_edcca(struct hostapd_data *hapd, char *cmd,
++					 char *buf, size_t buflen)
++{
++	char *pos, *config, *value;
++	config = cmd;
++	pos = os_strchr(config, ' ');
++	if (pos == NULL)
++		return -1;
++	*pos++ = '\0';
++
++	if (pos == NULL)
++		return -1;
++	value = pos;
++
++	if (os_strcmp(config, "enable") == 0) {
++		int mode = atoi(value);
++		if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
++			wpa_printf(MSG_ERROR, "Invalid value for edcca enable");
++			return -1;
++		}
++		hapd->iconf->edcca_enable = (u8) mode;
++		if (hostapd_drv_configure_edcca_enable(hapd) != 0)
++			return -1;
++	} else if (os_strcmp(config, "compensation") == 0) {
++		int compensation = atoi(value);
++		if (compensation < EDCCA_MIN_COMPENSATION ||
++		    compensation > EDCCA_MAX_COMPENSATION) {
++			wpa_printf(MSG_ERROR, "Invalid value for edcca compensation");
++			return -1;
++		}
++		hapd->iconf->edcca_compensation = (s8) compensation;
++		if (hostapd_drv_configure_edcca_enable(hapd) != 0)
++			return -1;
++	} else if (os_strcmp(config, "threshold") == 0) {
++		char *thres_value;
++		thres_value = os_strchr(value, ':');
++		if (thres_value == NULL)
++			return -1;
++		*thres_value++ = '\0';
++
++		if (thres_value == NULL)
++			return -1;
++		int bw_idx = atoi(value);
++		int threshold = atoi(thres_value);
++
++		if (bw_idx < EDCCA_BW_20 || bw_idx > EDCCA_BW_160) {
++			wpa_printf(MSG_ERROR,
++				   "Unsupported Bandwidth idx %d for SET_EDCCA",
++				   bw_idx);
++			return -1;
++		}
++		if (threshold < EDCCA_MIN_CONFIG_THRES ||
++		    threshold > EDCCA_MAX_CONFIG_THRES) {
++			wpa_printf(MSG_ERROR,
++				   "Unsupported threshold %d for SET_EDCCA",
++				   threshold);
++			return -1;
++		}
++
++		int threshold_arr[EDCCA_MAX_BW_NUM];
++		/* 0x7f means keep the origival value in firmware */
++		os_memset(threshold_arr, 0x7f, sizeof(threshold_arr));
++		threshold_arr[bw_idx] = threshold;
++
++		if (hostapd_drv_configure_edcca_threshold(hapd, threshold_arr) != 0)
++			return -1;
++	} else {
++		wpa_printf(MSG_ERROR,
++			"Unsupported parameter %s for SET_EDCCA", config);
++		return -1;
++	}
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
++
++static int
++hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *cmd, char *buf,
++			     size_t buflen)
++{
++	char *pos, *end;
++
++	pos = buf;
++	end = buf + buflen;
++	u8 value[EDCCA_MAX_BW_NUM] = {0};
++
++	if (os_strcmp(cmd, "enable") == 0) {
++		return os_snprintf(pos, end - pos, "Enable: %s\n",
++				   edcca_mode_str(hapd->iconf->edcca_enable));
++	} else if (os_strcmp(cmd, "compensation") == 0) {
++		return os_snprintf(pos, end - pos, "Compensation: %d\n",
++				  hapd->iconf->edcca_compensation);
++	} else if (os_strcmp(cmd, "threshold") == 0) {
++		if (hostapd_drv_get_edcca(hapd, EDCCA_CTRL_GET_THRES, &value) != 0)
++			return -1;
++		return os_snprintf(pos, end - pos,
++				   "Threshold BW20: 0x%x, BW40: 0x%x, BW80: 0x%x, BW160: 0x%x\n",
++				   value[0], value[1], value[2], value[3]);
++	} else {
++		wpa_printf(MSG_ERROR,
++			"Unsupported parameter %s for GET_EDCCA", cmd);
++		return -1;
++	}
++}
++
+ 
+ #ifdef CONFIG_NAN_USD
+ 
+@@ -4669,6 +4787,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 			reply_len = -1;
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
++	} else if (os_strncmp(buf, "SET_EDCCA ", 10) == 0) {
++		reply_len = hostapd_ctrl_iface_set_edcca(hapd, buf+10, reply,
++							  reply_size);
++	} else if (os_strncmp(buf, "GET_EDCCA ", 10) == 0) {
++		reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
++							  reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 67661dfe0..112954a89 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -305,6 +305,9 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 	conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL;
+ #endif /* CONFIG_AIRTIME_POLICY */
+ 
++	conf->edcca_enable = EDCCA_MODE_AUTO;
++	conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
++
+ 	hostapd_set_and_check_bw320_offset(conf, 0);
+ 
+ 	return conf;
+@@ -1046,6 +1049,7 @@ void hostapd_config_free(struct hostapd_config *conf)
+ #ifdef CONFIG_ACS
+ 	os_free(conf->acs_chan_bias);
+ #endif /* CONFIG_ACS */
++	os_free(conf->edcca_threshold);
+ 	wpabuf_free(conf->lci);
+ 	wpabuf_free(conf->civic);
+ #ifdef CONFIG_AFC
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 25ac22e31..b9afe1f47 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1334,8 +1334,38 @@ struct hostapd_config {
+ 		int min_power;
+ 	} afc;
+ #endif /* CONFIG_AFC */
++
++	u8 edcca_enable;
++	s8 edcca_compensation;
++	int *edcca_threshold;
++};
++
++enum edcca_mode {
++	EDCCA_MODE_FORCE_DISABLE = 0,
++	EDCCA_MODE_AUTO = 1,
++};
++
++enum edcca_bw_id {
++	EDCCA_BW_20 = 0,
++	EDCCA_BW_40,
++	EDCCA_BW_80,
++	EDCCA_BW_160,
++	EDCCA_MAX_BW_NUM,
++};
++
++enum mtk_vendor_attr_edcca_ctrl_mode {
++	EDCCA_CTRL_SET_EN = 0,
++	EDCCA_CTRL_SET_THRES,
++	EDCCA_CTRL_GET_EN,
++	EDCCA_CTRL_GET_THRES,
++	EDCCA_CTRL_NUM,
+ };
+ 
++#define EDCCA_DEFAULT_COMPENSATION -6
++#define EDCCA_MIN_COMPENSATION -126
++#define EDCCA_MAX_COMPENSATION 126
++#define EDCCA_MIN_CONFIG_THRES -126
++#define EDCCA_MAX_CONFIG_THRES 0
+ 
+ static inline enum oper_chan_width
+ hostapd_get_oper_chwidth(struct hostapd_config *conf)
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 7c9527cd3..c7cacbd81 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1274,3 +1274,27 @@ int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
+ 	return hapd->driver->set_secure_ranging_ctx(hapd->drv_priv, &params);
+ }
+ #endif /* CONFIG_PASN */
++
++int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->driver->configure_edcca_enable)
++		return 0;
++	return hapd->driver->configure_edcca_enable(hapd->drv_priv,
++			hapd->iconf->edcca_enable,
++				hapd->iconf->edcca_compensation);
++}
++
++int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
++					  const int *threshold)
++{
++	if (!hapd->driver || !hapd->driver->configure_edcca_threshold)
++		return 0;
++	return hapd->driver->configure_edcca_threshold(hapd->drv_priv, threshold);
++}
++
++int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
++{
++	if (!hapd->driver || !hapd->driver->get_edcca)
++		return 0;
++	return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 58ca046c6..23a331914 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -152,6 +152,10 @@ int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
+ 				       u8 ltf_keyseed_len,
+ 				       const u8 *ltf_keyseed, u32 action);
+ 
++int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
++int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
++					  const int *threshold);
++int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 8159194e1..972bb1763 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2756,6 +2756,13 @@ dfs_offload:
+ 	}
+ #endif /* CONFIG_MESH */
+ 
++	if (hostapd_drv_configure_edcca_enable(hapd) < 0)
++		goto fail;
++
++	if (hostapd_drv_configure_edcca_threshold(hapd,
++						  hapd->iconf->edcca_threshold) < 0)
++		goto fail;
++
+ 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ 		   iface->bss[0]->conf->iface);
+ 	if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 4a19d2fc9..6121857dd 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -30,14 +30,22 @@ enum mtk_vendor_attr_edcca_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
+ };
+ 
+-enum mtk_vendor_attr_edcca_ctrl_mode {
+-	EDCCA_CTRL_SET_EN = 0,
+-	EDCCA_CTRL_SET_THERS,
+-	EDCCA_CTRL_GET_EN,
+-	EDCCA_CTRL_GET_THERS,
+-	EDCCA_CTRL_NUM,
++enum mtk_vendor_attr_edcca_dump {
++	MTK_VENDOR_ATTR_EDCCA_DUMP_UNSPEC = 0,
++
++	MTK_VENDOR_ATTR_EDCCA_DUMP_MODE,
++	MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL,
++	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL,
++	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL,
++	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP,
++	MTK_VENDOR_ATTR_EDCCA_DUMP_MAX =
++		NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
+ };
+ 
++
+ static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
+ 	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index e1a447333..bf103516b 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5246,6 +5246,10 @@ struct wpa_driver_ops {
+ 			      const u8 *match, size_t match_len,
+ 			      bool multicast);
+ #endif /* CONFIG_TESTING_OPTIONS */
++	int (*configure_edcca_enable)(void *priv, const u8 edcca_enable,
++				  const s8 edcca_compensation);
++	int (*configure_edcca_threshold)(void *priv, const int *threshold);
++	int (*get_edcca)(void *priv, const u8 mode, u8 *value);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 2c68bf997..606632aad 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -39,6 +39,8 @@
+ #include "radiotap_iter.h"
+ #include "rfkill.h"
+ #include "driver_nl80211.h"
++#include "common/mtk_vendor.h"
++#include "ap/ap_config.h"
+ 
+ 
+ #ifndef NETLINK_CAP_ACK
+@@ -14101,6 +14103,174 @@ static int testing_nl80211_radio_disable(void *priv, int disabled)
+ 
+ #endif /* CONFIG_TESTING_OPTIONS */
+ 
++static int nl80211_configure_edcca_enable(void *priv,
++					  const u8 edcca_enable,
++					  const s8 edcca_compensation)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_edcca_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting EDCCA enable");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
++	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_EN) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, edcca_enable) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
++		edcca_compensation)) {
++		wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to configure EDCCA enable. ret=%d (%s) ",
++			   ret, strerror(-ret));
++	}
++	return ret;
++}
++
++static int nl80211_configure_edcca_threshold(void *priv, const int *threshold)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_edcca_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting EDCCA threshold");
++		return 0;
++	}
++
++	if (!threshold) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Input EDCCA threshold is empty!");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
++	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_THRES) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, threshold[0] & 0xff) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL, threshold[1] & 0xff) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL, threshold[2] & 0xff) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL, threshold[3] & 0xff)) {
++		wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to configure EDCCA threshold. ret=%d (%s) ",
++			   ret, strerror(-ret));
++	}
++	return ret;
++}
++
++
++static int edcca_info_handler(struct nl_msg *msg, void *arg)
++{
++	u8 *info = (u8 *) arg;
++	struct nlattr *tb[NL80211_ATTR_MAX + 1];
++	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_MAX + 1];
++	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++	struct nlattr *nl_vend, *attr;
++
++	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++		  genlmsg_attrlen(gnlh, 0), NULL);
++
++	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++	if (!nl_vend)
++		return NL_SKIP;
++
++	nla_parse(tb_vendor, MTK_VENDOR_ATTR_EDCCA_DUMP_MAX,
++		  nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++	attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL];
++	if (!attr) {
++		wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL");
++		return NL_SKIP;
++	}
++
++	*info++ = nla_get_u8(attr);
++
++	attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL];
++	if (!attr) {
++		wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL");
++		return NL_SKIP;
++	}
++
++	*info++ = nla_get_u8(attr);
++
++	attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL];
++	if (!attr) {
++		wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL");
++		return NL_SKIP;
++	}
++
++	*info++ = nla_get_u8(attr);
++
++	attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL];
++	if (!attr) {
++		wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL");
++		return NL_SKIP;
++	}
++
++	*info = nla_get_u8(attr);
++	return NL_SKIP;
++}
++
++
++static int nl80211_get_edcca(void *priv, const u8 mode, u8 *value)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_edcca_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting EDCCA threshold");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
++	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED)) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, mode)) {
++		wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++	nla_nest_end(msg, data);
++	ret = send_and_recv_resp(drv, msg, edcca_info_handler, value);
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to get EDCCA configuration. ret=%d (%s)",
++			   ret, strerror(-ret));
++	}
++	return ret;
++}
++
+ 
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+@@ -14262,4 +14432,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.register_frame = testing_nl80211_register_frame,
+ 	.radio_disable = testing_nl80211_radio_disable,
+ #endif /* CONFIG_TESTING_OPTIONS */
++/* Need ifdef CONFIG_DRIVER_NL80211_MTK */
++	.configure_edcca_enable = nl80211_configure_edcca_enable,
++	.configure_edcca_threshold = nl80211_configure_edcca_threshold,
++	.get_edcca = nl80211_get_edcca,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 618746e67..62c47efbd 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -200,6 +200,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int secure_ranging_ctx_vendor_cmd_avail:1;
+ 	unsigned int puncturing:1;
+ 	unsigned int qca_ap_allowed_freqs:1;
++	unsigned int mtk_edcca_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index d5ba66b10..465fd318d 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -18,6 +18,7 @@
+ #include "common/qca-vendor-attr.h"
+ #include "common/brcm_vendor.h"
+ #include "driver_nl80211.h"
++#include "common/mtk_vendor.h"
+ 
+ 
+ static int protocol_feature_handler(struct nl_msg *msg, void *arg)
+@@ -1138,6 +1139,12 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 					break;
+ 				}
+ #endif /* CONFIG_DRIVER_NL80211_BRCM */
++			} else if (vinfo->vendor_id == OUI_MTK) {
++				switch (vinfo->subcmd) {
++				case MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL:
++					drv->mtk_edcca_vendor_cmd_avail = 1;
++					break;
++				}
+ 			}
+ 
+ 			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0017-hostapd-MLO-add-support-for-MLO-rekey.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0017-hostapd-MLO-add-support-for-MLO-rekey.patch
deleted file mode 100644
index 5895635..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0017-hostapd-MLO-add-support-for-MLO-rekey.patch
+++ /dev/null
@@ -1,799 +0,0 @@
-From 5e6164cb6143d55409c08ae9bfd859efa188e383 Mon Sep 17 00:00:00 2001
-From: Rameshkumar Sundaram <quic_ramess@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:47 +0530
-Subject: [PATCH 017/104] hostapd: MLO: add support for MLO rekey
-
-Currently wpa group rekey is not supported for ML Stations when non-assoc
-link initiates a group rekey, to support the same following changes have
-been made-
-  * Calculate links specific MLO GTK/IGTK and BIGTK KDE lengths based on
-    corresponding cipher and key instead of taking length of one link and
-    multiplying it by no of associated links.
-  * For MLD, Arm group key rekey timer on one of the links and whenever it
-    fires do group key rekey for all links.
-
-Signed-off-by: Rameshkumar Sundaram <quic_ramess@quicinc.com>
-Co-developed-by: Adil Saeed Musthafa <quic_adilm@quicinc.com>
-Signed-off-by: Adil Saeed Musthafa <quic_adilm@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/drv_callbacks.c                        |   2 +-
- src/ap/ieee802_11.c                           |  13 +-
- src/ap/wpa_auth.c                             | 310 +++++++++++++++---
- src/ap/wpa_auth.h                             |   9 +-
- src/ap/wpa_auth_glue.c                        |  22 ++
- src/ap/wpa_auth_i.h                           |   1 +
- src/ap/wpa_auth_ie.c                          |  12 +-
- src/common/wpa_common.h                       |   1 +
- tests/fuzzing/eapol-key-auth/eapol-key-auth.c |   2 +-
- wpa_supplicant/ibss_rsn.c                     |   2 +-
- 10 files changed, 317 insertions(+), 57 deletions(-)
-
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index 064c7abae..dc21977ff 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -528,7 +528,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
- 					  elems.rsnxe ? elems.rsnxe - 2 : NULL,
- 					  elems.rsnxe ? elems.rsnxe_len + 2 : 0,
- 					  elems.mdie, elems.mdie_len,
--					  elems.owe_dh, elems.owe_dh_len);
-+					  elems.owe_dh, elems.owe_dh_len, NULL);
- 		reason = WLAN_REASON_INVALID_IE;
- 		status = WLAN_STATUS_INVALID_IE;
- 		switch (res) {
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 9d04bdf43..7ee18f4ae 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -1887,7 +1887,7 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
- 				  elems.rsn_ie - 2, elems.rsn_ie_len + 2,
- 				  elems.rsnxe ? elems.rsnxe - 2 : NULL,
- 				  elems.rsnxe ? elems.rsnxe_len + 2 : 0,
--				  elems.mdie, elems.mdie_len, NULL, 0);
-+				  elems.mdie, elems.mdie_len, NULL, 0, NULL);
- 	resp = wpa_res_to_status_code(res);
- 	if (resp != WLAN_STATUS_SUCCESS)
- 		goto fail;
-@@ -3778,7 +3778,7 @@ u16 owe_process_rsn_ie(struct hostapd_data *hapd,
- 	rsn_ie_len += 2;
- 	res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
- 				  hapd->iface->freq, rsn_ie, rsn_ie_len,
--				  NULL, 0, NULL, 0, owe_dh, owe_dh_len);
-+				  NULL, 0, NULL, 0, owe_dh, owe_dh_len, NULL);
- 	status = wpa_res_to_status_code(res);
- 	if (status != WLAN_STATUS_SUCCESS)
- 		goto end;
-@@ -3867,6 +3867,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
- 	const u8 *wpa_ie;
- 	size_t wpa_ie_len;
- 	const u8 *p2p_dev_addr = NULL;
-+	struct hostapd_data *assoc_hapd;
-+	struct sta_info *assoc_sta = NULL;
- 
- 	resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len);
- 	if (resp != WLAN_STATUS_SUCCESS)
-@@ -4041,6 +4043,10 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
- 		wpa_ie_len += 2;
- 
- 		if (!sta->wpa_sm) {
-+			if (!link)
-+				assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta,
-+								     &assoc_hapd);
-+
- 			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
- 							sta->addr,
- 							p2p_dev_addr);
-@@ -4076,7 +4082,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
- 					  elems->rsnxe ? elems->rsnxe_len + 2 :
- 					  0,
- 					  elems->mdie, elems->mdie_len,
--					  elems->owe_dh, elems->owe_dh_len);
-+					  elems->owe_dh, elems->owe_dh_len,
-+					  assoc_sta ? assoc_sta->wpa_sm : NULL);
- 		resp = wpa_res_to_status_code(res);
- 		if (resp != WLAN_STATUS_SUCCESS)
- 			return resp;
-diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
-index 8c1052c25..7a07dcc4c 100644
---- a/src/ap/wpa_auth.c
-+++ b/src/ap/wpa_auth.c
-@@ -71,6 +71,9 @@ static void wpa_group_put(struct wpa_authenticator *wpa_auth,
- 			  struct wpa_group *group);
- static int ieee80211w_kde_len(struct wpa_state_machine *sm);
- static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
-+static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
-+				 struct wpa_group *group);
-+
- 
- static const u32 eapol_key_timeout_first = 100; /* ms */
- static const u32 eapol_key_timeout_subseq = 1000; /* ms */
-@@ -102,6 +105,22 @@ static const u8 * wpa_auth_get_spa(const struct wpa_state_machine *sm)
- 	return sm->addr;
- }
- 
-+static void wpa_update_gkeydone(struct wpa_state_machine *sm, int update)
-+{
-+#ifdef CONFIG_IEEE80211BE
-+	int link_id;
-+#endif /* CONFIG_IEEE80211BE */
-+	if (!sm || !sm->wpa_auth)
-+		return;
-+
-+	sm->wpa_auth->group->GKeyDoneStations += update;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	for_each_sm_auth(sm, link_id)
-+		sm->mld_links[link_id].wpa_auth->group->GKeyDoneStations += update;
-+#endif /* CONFIG_IEEE80211BE */
-+}
-+
- #ifdef CONFIG_IEEE80211BE
- void wpa_release_link_auth_ref(struct wpa_state_machine *sm, int release_link_id)
- {
-@@ -139,10 +158,12 @@ static int wpa_get_primary_wpa_auth_cb(struct wpa_authenticator *wpa_auth, void
- 	ctx->wpa_auth = wpa_auth;
- 	return 1;
- }
-+#endif /* CONFIG_IEEE80211BE */
- 
- static struct wpa_authenticator *
- wpa_get_primary_wpa_auth(struct wpa_authenticator *wpa_auth)
- {
-+#ifdef CONFIG_IEEE80211BE
- 	struct wpa_get_link_auth_ctx ctx;
- 
- 	if (!wpa_auth || !wpa_auth->is_ml || wpa_auth->primary_auth)
-@@ -153,8 +174,10 @@ wpa_get_primary_wpa_auth(struct wpa_authenticator *wpa_auth)
- 	wpa_auth_for_each_auth(wpa_auth, wpa_get_primary_wpa_auth_cb, &ctx);
- 
- 	return ctx.wpa_auth;
--}
-+#else
-+	return wpa_auth;
- #endif /* CONFIG_IEEE80211BE */
-+}
- 
- static inline int wpa_auth_mic_failure_report(
- 	struct wpa_authenticator *wpa_auth, const u8 *addr)
-@@ -420,15 +443,16 @@ static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
- 	}
- }
- 
--
--static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
-+static void wpa_rekey_all_groups(struct wpa_authenticator *wpa_auth)
- {
--	struct wpa_authenticator *wpa_auth = eloop_ctx;
- 	struct wpa_group *group, *next;
- 
- 	wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK");
- 	group = wpa_auth->group;
- 	while (group) {
-+		wpa_printf(MSG_DEBUG, "GTK rekey start for authenticator("
-+			   MACSTR "), group vlan %d",
-+			   MAC2STR(wpa_auth->addr), group->vlan_id);
- 		wpa_group_get(wpa_auth, group);
- 
- 		group->GTKReKey = true;
-@@ -441,6 +465,80 @@ static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
- 		wpa_group_put(wpa_auth, group);
- 		group = next;
- 	}
-+}
-+
-+#ifdef CONFIG_IEEE80211BE
-+static void wpa_update_all_gtks(struct wpa_authenticator *wpa_auth)
-+{
-+	struct wpa_group *group, *next;
-+
-+	group = wpa_auth->group;
-+	while (group) {
-+		wpa_group_get(wpa_auth, group);
-+
-+		wpa_group_update_gtk(wpa_auth, group);
-+		next = group->next;
-+		wpa_group_put(wpa_auth, group);
-+		group = next;
-+	}
-+}
-+
-+static int wpa_update_all_gtks_cb(struct wpa_authenticator *wpa_auth, void *ctx)
-+{
-+	u8 *mld_addr = ctx;
-+
-+	if (os_memcmp(wpa_auth->mld_addr, mld_addr, ETH_ALEN) != 0)
-+		return 0;
-+
-+	wpa_update_all_gtks(wpa_auth);
-+	return 0;
-+}
-+
-+static int wpa_rekey_all_groups_cb(struct wpa_authenticator *wpa_auth,
-+				   void *ctx)
-+{
-+	u8 *mld_addr = ctx;
-+
-+	if (os_memcmp(wpa_auth->mld_addr, mld_addr, ETH_ALEN) != 0)
-+		return 0;
-+
-+	wpa_rekey_all_groups(wpa_auth);
-+	return 0;
-+}
-+#endif /* CONFIG_IEEE80211BE */
-+
-+static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
-+{
-+	struct wpa_authenticator *wpa_auth = eloop_ctx;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	if (wpa_auth->is_ml) {
-+		/* Non Primary ML authenticator eloop timer for group rekey is never
-+		 * started and shouldn't fire too check and warn just in case
-+		 */
-+		if (!wpa_auth->primary_auth) {
-+			wpa_printf(MSG_DEBUG,
-+				   "WPA: Can't start GTK rekey on non-primary ML authenticator");
-+			return;
-+		}
-+		/*
-+		 * Generate all the new I/BIG/GTKs
-+		 */
-+		wpa_auth_for_each_auth(wpa_auth, wpa_update_all_gtks_cb,
-+				       wpa_auth->mld_addr);
-+
-+		/*
-+		 * Send all the generated I/BIG/GTKs to the respective
-+		 * stations via G1 messages
-+		 */
-+		wpa_auth_for_each_auth(wpa_auth, wpa_rekey_all_groups_cb,
-+				       wpa_auth->mld_addr);
-+	} else {
-+		wpa_rekey_all_groups(wpa_auth);
-+	}
-+#else
-+	wpa_rekey_all_groups(wpa_auth);
-+#endif /* CONFIG_IEEE80211BE */
- 
- 	if (wpa_auth->conf.wpa_group_rekey) {
- 		eloop_register_timeout(wpa_auth->conf.wpa_group_rekey,
-@@ -590,8 +688,19 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
- 	wpa_auth = os_zalloc(sizeof(struct wpa_authenticator));
- 	if (!wpa_auth)
- 		return NULL;
-+
- 	os_memcpy(wpa_auth->addr, addr, ETH_ALEN);
- 	os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
-+
-+#ifdef CONFIG_IEEE80211BE
-+	if (conf->mld_addr) {
-+		wpa_auth->is_ml = true;
-+		wpa_auth->link_id = conf->link_id;
-+		wpa_auth->primary_auth = !conf->first_link_auth;
-+		os_memcpy(wpa_auth->mld_addr, conf->mld_addr, ETH_ALEN);
-+	}
-+#endif /* CONFIG_IEEE80211BE */
-+
- 	wpa_auth->cb = cb;
- 	wpa_auth->cb_ctx = cb_ctx;
- 
-@@ -635,7 +744,15 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
- 				       wpa_rekey_gmk, wpa_auth, NULL);
- 	}
- 
-+#ifdef CONFIG_IEEE80211BE
-+	/* For ML AP, run Group rekey timer only on one link(first) and whenever
-+	 * it fires do rekey on all associated ML links at one shot.
-+	 */
-+	if ((!wpa_auth->is_ml || !conf->first_link_auth) &&
-+	    wpa_auth->conf.wpa_group_rekey) {
-+#else
- 	if (wpa_auth->conf.wpa_group_rekey) {
-+#endif /* CONFIG_IEEE80211BE */
- 		eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0,
- 				       wpa_rekey_gtk, wpa_auth, NULL);
- 	}
-@@ -699,6 +816,10 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth)
- 	struct wpa_group *group, *prev;
- 
- 	eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
-+
-+	/* TODO: assign ML Primary authenticator to next link auth and
-+	 * start rekey timer.
-+	 */
- 	eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
- 
- 	pmksa_cache_auth_deinit(wpa_auth->pmksa);
-@@ -868,7 +989,7 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
- 	}
- #endif /* CONFIG_P2P */
- 	if (sm->GUpdateStationKeys) {
--		sm->group->GKeyDoneStations--;
-+		wpa_update_gkeydone(sm, -1);
- 		sm->GUpdateStationKeys = false;
- 	}
- #ifdef CONFIG_IEEE80211R_AP
-@@ -1669,12 +1790,14 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
- 			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
- 					LOGGER_INFO,
- 					"received EAPOL-Key Request for GTK rekeying");
--			eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
-+
-+			eloop_cancel_timeout(wpa_rekey_gtk,
-+					     wpa_get_primary_wpa_auth(wpa_auth), NULL);
- 			if (wpa_auth_gtk_rekey_in_process(wpa_auth))
- 				wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG,
- 						"skip new GTK rekey - already in process");
- 			else
--				wpa_rekey_gtk(wpa_auth, NULL);
-+				wpa_rekey_gtk(wpa_get_primary_wpa_auth(wpa_auth), NULL);
- 		}
- 	} else {
- 		/* Do not allow the same key replay counter to be reused. */
-@@ -2207,7 +2330,7 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
- 			 * Reauthentication cancels the pending group key
- 			 * update for this STA.
- 			 */
--			sm->group->GKeyDoneStations--;
-+			wpa_update_gkeydone(sm, -1);
- 			sm->GUpdateStationKeys = false;
- 			sm->PtkGroupInit = true;
- 		}
-@@ -2284,7 +2407,7 @@ SM_STATE(WPA_PTK, INITIALIZE)
- 
- 	sm->keycount = 0;
- 	if (sm->GUpdateStationKeys)
--		sm->group->GKeyDoneStations--;
-+		wpa_update_gkeydone(sm, -1);
- 	sm->GUpdateStationKeys = false;
- 	if (sm->wpa == WPA_VERSION_WPA)
- 		sm->PInitAKeys = false;
-@@ -4058,41 +4181,54 @@ static void wpa_auth_get_ml_key_info(struct wpa_authenticator *wpa_auth,
- 	wpa_auth->cb->get_ml_key_info(wpa_auth->cb_ctx, info);
- }
- 
-+#define KDE_HDR_LEN (1 + 1 + RSN_SELECTOR_LEN)
- 
- static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm)
- {
--	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
--	struct wpa_group *gsm = sm->group;
--	size_t gtk_len = gsm->GTK_len;
--	size_t igtk_len;
--	size_t kde_len;
--	unsigned int n_links;
-+	struct wpa_authenticator *wpa_auth;
-+	size_t kde_len = 0;
-+	int link_id;
- 
- 	if (sm->mld_assoc_link_id < 0)
- 		return 0;
- 
--	n_links = sm->n_mld_affiliated_links + 1;
-+	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
-+		if (!sm->mld_links[link_id].valid)
-+			continue;
-+
-+		wpa_auth = sm->mld_links[link_id].wpa_auth;
-+		if (!wpa_auth || !wpa_auth->group)
-+			continue;
- 
--	/* MLO GTK KDE for each link */
--	kde_len = n_links * (2 + RSN_SELECTOR_LEN + 1 + 6 + gtk_len);
-+		/* MLO GTK KDE
-+		 * Header + Key-idx and Link-id + PN
-+		 */
-+		kde_len += (KDE_HDR_LEN  + 1 + WPA_MLO_GTK_KDE_PN_LEN);
-+		kde_len += wpa_auth->group->GTK_len;
- 
--	if (!sm->mgmt_frame_prot)
--		return kde_len;
-+		if (!sm->mgmt_frame_prot)
-+			continue;
- 
--	/* MLO IGTK KDE for each link */
--	igtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
--	kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
-+		if (wpa_auth->conf.tx_bss_auth)
-+			wpa_auth = wpa_auth->conf.tx_bss_auth;
- 
--	if (wpa_auth->conf.tx_bss_auth) {
--		wpa_auth = wpa_auth->conf.tx_bss_auth;
--		igtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
--	}
-+		/* MLO IGTK KDE
-+		 * Header + Key-idx & IPN + Link-id
-+		 */
-+		kde_len += (KDE_HDR_LEN + WPA_IGTK_KDE_PREFIX_LEN + 1);
-+		kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
- 
--	if (!wpa_auth->conf.beacon_prot)
--		return kde_len;
-+		if (!wpa_auth->conf.beacon_prot)
-+			continue;
-+
-+		/* MLO BIGTK KDE
-+		 * Header + Key-idx & IPN + Link-id
-+		 */
-+		kde_len += (KDE_HDR_LEN + WPA_BIGTK_KDE_PREFIX_LEN + 1);
-+		kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
-+	}
- 
--	/* MLO BIGTK KDE for each link */
--	kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
-+	wpa_printf(MSG_DEBUG, "MLO Group kdes len = %zu", kde_len);
- 
- 	return kde_len;
- }
-@@ -4102,6 +4238,7 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
- {
- 	struct wpa_auth_ml_key_info ml_key_info;
- 	unsigned int i, link_id;
-+	u8 *start = pos;
- 
- 	/* First fetch the key information from all the authenticators */
- 	os_memset(&ml_key_info, 0, sizeof(ml_key_info));
-@@ -4153,8 +4290,10 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
- 		i++;
- 	}
- 
--	if (!sm->mgmt_frame_prot)
-+	if (!sm->mgmt_frame_prot) {
-+		wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start);
- 		return pos;
-+	}
- 
- 	/* Add MLO IGTK KDEs */
- 	for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
-@@ -4193,8 +4332,10 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
- 		i++;
- 	}
- 
--	if (!sm->wpa_auth->conf.beacon_prot)
-+	if (!sm->wpa_auth->conf.beacon_prot) {
-+		wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start);
- 		return pos;
-+	}
- 
- 	/* Add MLO BIGTK KDEs */
- 	for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
-@@ -4234,6 +4375,7 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
- 		i++;
- 	}
- 
-+	wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start);
- 	return pos;
- }
- 
-@@ -4274,6 +4416,7 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
- {
- #ifdef CONFIG_IEEE80211BE
- 	u8 link_id;
-+	u8 *start = pos;
- 
- 	if (sm->mld_assoc_link_id < 0)
- 		return pos;
-@@ -4324,6 +4467,7 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
- 		}
- 	}
- 
-+	wpa_printf(MSG_DEBUG, "RSN: MLO Link kde len = %ld", pos - start);
- 	pos = wpa_auth_ml_group_kdes(sm, pos);
- #endif /* CONFIG_IEEE80211BE */
- 
-@@ -5106,7 +5250,7 @@ SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED)
- #endif /* CONFIG_OCV */
- 
- 	if (sm->GUpdateStationKeys)
--		sm->group->GKeyDoneStations--;
-+		wpa_update_gkeydone(sm, -1);
- 	sm->GUpdateStationKeys = false;
- 	sm->GTimeoutCtr = 0;
- 	/* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */
-@@ -5121,7 +5265,7 @@ SM_STATE(WPA_PTK_GROUP, KEYERROR)
- {
- 	SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group);
- 	if (sm->GUpdateStationKeys)
--		sm->group->GKeyDoneStations--;
-+		wpa_update_gkeydone(sm, -1);
- 	sm->GUpdateStationKeys = false;
- 	sm->Disconnect = true;
- 	sm->disconnect_reason = WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT;
-@@ -5415,18 +5559,11 @@ int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos)
- 
- #endif /* CONFIG_WNM_AP */
- 
--
--static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
--			      struct wpa_group *group)
-+static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
-+				 struct wpa_group *group)
- {
- 	int tmp;
- 
--	wpa_printf(MSG_DEBUG,
--		   "WPA: group state machine entering state SETKEYS (VLAN-ID %d)",
--		   group->vlan_id);
--	group->changed = true;
--	group->wpa_group_state = WPA_GROUP_SETKEYS;
--	group->GTKReKey = false;
- 	tmp = group->GM;
- 	group->GM = group->GN;
- 	group->GN = tmp;
-@@ -5440,6 +5577,24 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
- 	 * counting the STAs that are marked with GUpdateStationKeys instead of
- 	 * including all STAs that could be in not-yet-completed state. */
- 	wpa_gtk_update(wpa_auth, group);
-+}
-+
-+static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
-+			      struct wpa_group *group)
-+{
-+	wpa_printf(MSG_DEBUG,
-+		   "WPA: group state machine entering state SETKEYS (VLAN-ID %d)",
-+		   group->vlan_id);
-+	group->changed = true;
-+	group->wpa_group_state = WPA_GROUP_SETKEYS;
-+	group->GTKReKey = false;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	if (wpa_auth->is_ml)
-+		goto skip_update;
-+#endif /* CONFIG_IEEE80211BE */
-+
-+	wpa_group_update_gtk(wpa_auth, group);
- 
- 	if (group->GKeyDoneStations) {
- 		wpa_printf(MSG_DEBUG,
-@@ -5447,6 +5602,10 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
- 			   group->GKeyDoneStations);
- 		group->GKeyDoneStations = 0;
- 	}
-+
-+#ifdef CONFIG_IEEE80211BE
-+skip_update:
-+#endif /* CONFIG_IEEE80211BE */
- 	wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group);
- 	wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d",
- 		   group->GKeyDoneStations);
-@@ -5564,6 +5723,57 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
- 	}
- }
- 
-+static void wpa_mark_group_change(struct wpa_state_machine *sm, bool change)
-+{
-+#ifdef CONFIG_IEEE80211BE
-+	int link_id;
-+#endif /* CONFIG_IEEE80211BE */
-+
-+	if (!sm || !sm->wpa_auth)
-+		return;
-+	sm->wpa_auth->group->changed = change;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	for_each_sm_auth(sm, link_id)
-+		sm->mld_links[link_id].wpa_auth->group->changed = change;
-+#endif /* CONFIG_IEEE80211BE */
-+}
-+
-+static void wpa_group_sm_step_links(struct wpa_state_machine *sm)
-+{
-+#ifdef CONFIG_IEEE80211BE
-+	int link_id;
-+#endif /* CONFIG_IEEE80211BE */
-+
-+	if (!sm || !sm->wpa_auth)
-+		return;
-+	wpa_group_sm_step(sm->wpa_auth, sm->wpa_auth->group);
-+
-+#ifdef CONFIG_IEEE80211BE
-+	for_each_sm_auth(sm, link_id)
-+		wpa_group_sm_step(sm->mld_links[link_id].wpa_auth,
-+				  sm->mld_links[link_id].wpa_auth->group);
-+#endif /* CONFIG_IEEE80211BE */
-+}
-+
-+static bool wpa_group_sm_changed(struct wpa_state_machine *sm)
-+{
-+#ifdef CONFIG_IEEE80211BE
-+	int link_id;
-+#endif /* CONFIG_IEEE80211BE */
-+	bool changed;
-+
-+	if (!sm || !sm->wpa_auth)
-+		return false;
-+	changed = sm->wpa_auth->group->changed;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	for_each_sm_auth(sm, link_id)
-+		changed |= sm->mld_links[link_id].wpa_auth->group->changed;
-+#endif /* CONFIG_IEEE80211BE */
-+
-+	return changed;
-+}
- 
- static int wpa_sm_step(struct wpa_state_machine *sm)
- {
-@@ -5584,7 +5794,7 @@ static int wpa_sm_step(struct wpa_state_machine *sm)
- 			break;
- 
- 		sm->changed = false;
--		sm->wpa_auth->group->changed = false;
-+		wpa_mark_group_change(sm, false);
- 
- 		SM_STEP_RUN(WPA_PTK);
- 		if (sm->pending_deinit)
-@@ -5592,8 +5802,8 @@ static int wpa_sm_step(struct wpa_state_machine *sm)
- 		SM_STEP_RUN(WPA_PTK_GROUP);
- 		if (sm->pending_deinit)
- 			break;
--		wpa_group_sm_step(sm->wpa_auth, sm->group);
--	} while (sm->changed || sm->wpa_auth->group->changed);
-+		wpa_group_sm_step_links(sm);
-+	} while (sm->changed || wpa_group_sm_changed(sm));
- 	sm->in_step_loop = 0;
- 
- 	if (sm->pending_deinit) {
-@@ -6807,8 +7017,10 @@ int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth)
- {
- 	if (!wpa_auth)
- 		return -1;
--	eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
--	return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL);
-+	eloop_cancel_timeout(wpa_rekey_gtk,
-+			     wpa_get_primary_wpa_auth(wpa_auth), NULL);
-+	return eloop_register_timeout(0, 0, wpa_rekey_gtk,
-+				      wpa_get_primary_wpa_auth(wpa_auth), NULL);
- }
- 
- 
-diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
-index 1446872f3..331d217b5 100644
---- a/src/ap/wpa_auth.h
-+++ b/src/ap/wpa_auth.h
-@@ -285,6 +285,12 @@ struct wpa_auth_config {
- 	 * Set only in nontransmitted BSSs, i.e., is NULL for transmitted BSS
- 	 * and in BSSs that are not part of a Multi-BSSID set. */
- 	struct wpa_authenticator *tx_bss_auth;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	u8 *mld_addr;
-+	int link_id;
-+	struct wpa_authenticator *first_link_auth;
-+#endif /* CONFIG_IEEE80211BE */
- };
- 
- typedef enum {
-@@ -429,7 +435,8 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
- 		    const u8 *wpa_ie, size_t wpa_ie_len,
- 		    const u8 *rsnxe, size_t rsnxe_len,
- 		    const u8 *mdie, size_t mdie_len,
--		    const u8 *owe_dh, size_t owe_dh_len);
-+		    const u8 *owe_dh, size_t owe_dh_len,
-+		    struct wpa_state_machine *assoc_sm);
- int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
- 		      struct wpa_state_machine *sm,
- 		      const u8 *osen_ie, size_t osen_ie_len);
-diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
-index d3cd44695..1726c7201 100644
---- a/src/ap/wpa_auth_glue.c
-+++ b/src/ap/wpa_auth_glue.c
-@@ -1713,6 +1713,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
- 
- 	hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf);
- 	_conf.msg_ctx = hapd->msg_ctx;
-+
- 	tx_bss = hostapd_mbssid_get_tx_bss(hapd);
- 	if (tx_bss != hapd)
- 		_conf.tx_bss_auth = tx_bss->wpa_auth;
-@@ -1753,6 +1754,27 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
- 		!!(hapd->iface->drv_flags2 &
- 		   WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP);
- 
-+#ifdef CONFIG_IEEE80211BE
-+	_conf.mld_addr = NULL;
-+	_conf.link_id = -1;
-+	_conf.first_link_auth = NULL;
-+
-+	if (hapd->conf->mld_ap) {
-+		struct hostapd_data *lhapd;
-+
-+		_conf.mld_addr = hapd->mld->mld_addr;
-+		_conf.link_id = hapd->mld_link_id;
-+
-+		for_each_mld_link(lhapd, hapd) {
-+			if (lhapd == hapd)
-+				continue;
-+
-+			if (lhapd->wpa_auth)
-+				_conf.first_link_auth = lhapd->wpa_auth;
-+		}
-+	}
-+#endif /* CONFIG_IEEE80211BE */
-+
- 	hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
- 	if (hapd->wpa_auth == NULL) {
- 		wpa_printf(MSG_ERROR, "WPA initialization failed.");
-diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
-index 9ba90749d..29bb66733 100644
---- a/src/ap/wpa_auth_i.h
-+++ b/src/ap/wpa_auth_i.h
-@@ -176,6 +176,7 @@ struct wpa_state_machine {
- 	u8 peer_mld_addr[ETH_ALEN];
- 	s8 mld_assoc_link_id;
- 	u8 n_mld_affiliated_links;
-+	u16 valid_links;
- 
- 	struct mld_link {
- 		bool valid;
-diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
-index a5f2861c9..bf2303e4f 100644
---- a/src/ap/wpa_auth_ie.c
-+++ b/src/ap/wpa_auth_ie.c
-@@ -608,7 +608,8 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
- 		    const u8 *wpa_ie, size_t wpa_ie_len,
- 		    const u8 *rsnxe, size_t rsnxe_len,
- 		    const u8 *mdie, size_t mdie_len,
--		    const u8 *owe_dh, size_t owe_dh_len)
-+		    const u8 *owe_dh, size_t owe_dh_len,
-+		    struct wpa_state_machine *assoc_sm)
- {
- 	struct wpa_auth_config *conf = &wpa_auth->conf;
- 	struct wpa_ie_data data;
-@@ -956,6 +957,15 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
- 	else
- 		sm->wpa = WPA_VERSION_WPA;
- 
-+	if (assoc_sm) {
-+		/* For ML Association Link STA cannot choose a different
-+		 * akm or pairwise cipher from assoc STA
-+		 */
-+		if (sm->wpa_key_mgmt != assoc_sm->wpa_key_mgmt)
-+			return WPA_INVALID_AKMP;
-+		if (sm->pairwise != assoc_sm->pairwise)
-+			return WPA_INVALID_PAIRWISE;
-+	}
- #if defined(CONFIG_IEEE80211R_AP) && defined(CONFIG_FILS)
- 	if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256 ||
- 	     sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) &&
-diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
-index 01efeea3a..24ceed600 100644
---- a/src/common/wpa_common.h
-+++ b/src/common/wpa_common.h
-@@ -24,6 +24,7 @@
- #define WPA_PASN_PMK_LEN 32
- #define WPA_PASN_MAX_MIC_LEN 24
- #define WPA_MAX_RSNXE_LEN 4
-+#define WPA_MLO_GTK_KDE_PN_LEN 6
- 
- #define OWE_DH_GROUP 19
- 
-diff --git a/tests/fuzzing/eapol-key-auth/eapol-key-auth.c b/tests/fuzzing/eapol-key-auth/eapol-key-auth.c
-index bb46422c6..17f69fd76 100644
---- a/tests/fuzzing/eapol-key-auth/eapol-key-auth.c
-+++ b/tests/fuzzing/eapol-key-auth/eapol-key-auth.c
-@@ -262,7 +262,7 @@ static int auth_init(struct wpa *wpa)
- 	}
- 
- 	if (wpa_validate_wpa_ie(wpa->auth_group, wpa->auth, 2412, supp_ie,
--				supp_ie_len, NULL, 0, NULL, 0, NULL, 0) !=
-+				supp_ie_len, NULL, 0, NULL, 0, NULL, 0, NULL) !=
- 	    WPA_IE_OK) {
- 		wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
- 		return -1;
-diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
-index 554268a47..2d06f1a6a 100644
---- a/wpa_supplicant/ibss_rsn.c
-+++ b/wpa_supplicant/ibss_rsn.c
-@@ -484,7 +484,7 @@ static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn,
- 				"\x00\x0f\xac\x04"
- 				"\x01\x00\x00\x0f\xac\x04"
- 				"\x01\x00\x00\x0f\xac\x02"
--				"\x00\x00", 22, NULL, 0, NULL, 0, NULL, 0) !=
-+				"\x00\x00", 22, NULL, 0, NULL, 0, NULL, 0, NULL) !=
- 	    WPA_IE_OK) {
- 		wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
- 		return -1;
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0017-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0017-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch
new file mode 100644
index 0000000..34dcd0f
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0017-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch
@@ -0,0 +1,454 @@
+From e45dc5872482e9db6cac8263a82832584095ecf3 Mon Sep 17 00:00:00 2001
+From: TomLiu <tomml.liu@mediatek.com>
+Date: Tue, 9 Aug 2022 10:23:44 -0700
+Subject: [PATCH 017/126] mtk: hostapd: Add hostapd MU SET/GET control
+
+---
+ hostapd/config_file.c             |   9 +++
+ hostapd/ctrl_iface.c              |  66 ++++++++++++++++++
+ hostapd/hostapd_cli.c             |  18 +++++
+ src/ap/ap_config.c                |   1 +
+ src/ap/ap_config.h                |   1 +
+ src/ap/ap_drv_ops.c               |  14 ++++
+ src/ap/ap_drv_ops.h               |   2 +
+ src/ap/hostapd.c                  |   2 +
+ src/common/mtk_vendor.h           |  15 ++++
+ src/drivers/driver.h              |  13 ++++
+ src/drivers/driver_nl80211.c      | 110 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   3 +
+ 13 files changed, 255 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 9f5ee48bf..22da72c07 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4251,6 +4251,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 			return 1;
+ 		}
+ 		conf->mbssid = mbssid;
++	} else if (os_strcmp(buf, "mu_onoff") == 0) {
++		int val = atoi(pos);
++		if (val < 0 || val > 15) {
++			wpa_printf(MSG_ERROR,
++				   "Line %d: invalid mu_onoff value",
++				   line);
++			return 1;
++		}
++		conf->mu_onoff = val;
+ #endif /* CONFIG_IEEE80211AX */
+ 	} else if (os_strcmp(buf, "max_listen_interval") == 0) {
+ 		bss->max_listen_interval = atoi(pos);
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 8a9fa3ec9..d168c2fb5 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4179,6 +4179,67 @@ fail:
+ #endif /* CONFIG_NAN_USD */
+ 
+ 
++static int
++hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
++					 char *buf, size_t buflen)
++{
++	char *pos, *config, *value;
++	config = cmd;
++	pos = os_strchr(config, ' ');
++	if (pos == NULL)
++		return -1;
++	*pos++ = '\0';
++
++	if(pos == NULL)
++		return -1;
++	value = pos;
++
++	if (os_strcmp(config, "onoff") == 0) {
++		int mu = atoi(value);
++		if (mu < 0 || mu > 15) {
++			wpa_printf(MSG_ERROR, "Invalid value for mu");
++			return -1;
++		}
++		hapd->iconf->mu_onoff = (u8) mu;
++	} else {
++		wpa_printf(MSG_ERROR,
++			"Unsupported parameter %s for SET_MU", config);
++		return -1;
++	}
++
++	if(hostapd_drv_mu_ctrl(hapd) == 0) {
++		return os_snprintf(buf, buflen, "OK\n");
++	} else {
++		return -1;
++	}
++}
++
++
++static int
++hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
++					 size_t buflen)
++{
++	u8 mu_onoff;
++	char *pos, *end;
++
++	pos = buf;
++	end = buf + buflen;
++
++	if (hapd->iface->state != HAPD_IFACE_ENABLED)
++		return os_snprintf(pos, end - pos, "Not allowed to get_mu when current state is %s\n",
++				   hostapd_state_text(hapd->iface->state));
++
++	if (hostapd_drv_mu_dump(hapd, &mu_onoff) == 0) {
++		hapd->iconf->mu_onoff = mu_onoff;
++		return os_snprintf(pos, end - pos, "[hostapd_cli] = UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
++			!!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
++	} else {
++		wpa_printf(MSG_INFO, "ctrl iface failed to call");
++		return -1;
++	}
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -4793,6 +4854,11 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 	} else if (os_strncmp(buf, "GET_EDCCA ", 10) == 0) {
+ 		reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
+ 							  reply_size);
++	} else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
++		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply,
++							  reply_size);
++	} else if (os_strncmp(buf, "GET_MU", 6) == 0) {
++		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index bb15bc751..f4dc1517e 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1460,6 +1460,20 @@ static int hostapd_cli_cmd_driver_flags2(struct wpa_ctrl *ctrl, int argc,
+ }
+ 
+ 
++static int hostapd_cli_cmd_set_mu(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "SET_MU", 1, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
++}
++
++
+ #ifdef CONFIG_DPP
+ 
+ static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
+@@ -1831,6 +1845,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 	  " = show supported driver flags"},
+ 	{ "driver_flags2", hostapd_cli_cmd_driver_flags2, NULL,
+ 	  " = show supported driver flags2"},
++	{ "set_mu", hostapd_cli_cmd_set_mu, NULL,
++		"<value> [0-15] bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0)"},
++	{ "get_mu", hostapd_cli_cmd_get_mu, NULL,
++		" = show mu onoff value in 0-15 bitmap"},
+ #ifdef CONFIG_DPP
+ 	{ "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 112954a89..45c391a65 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -291,6 +291,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 	conf->reg_def_cli_eirp_psd = -1;
+ 	conf->reg_sub_cli_eirp_psd = -1;
+ 	conf->reg_def_cli_eirp = -1;
++	conf->mu_onoff = 15;
+ #endif /* CONFIG_IEEE80211AX */
+ 
+ 	/* The third octet of the country string uses an ASCII space character
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index b9afe1f47..baf3b40f8 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1235,6 +1235,7 @@ struct hostapd_config {
+ 	int reg_def_cli_eirp;
+ 
+ 	bool require_he;
++	u8 mu_onoff;
+ #endif /* CONFIG_IEEE80211AX */
+ 
+ 	/* VHT enable/disable config from CHAN_SWITCH */
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index c7cacbd81..56d923462 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1298,3 +1298,17 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
+ 		return 0;
+ 	return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
+ }
++
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->driver->mu_ctrl)
++		return 0;
++	return hapd->driver->mu_ctrl(hapd->drv_priv, hapd->iconf->mu_onoff);
++}
++
++int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
++{
++	if (!hapd->driver || !hapd->driver->mu_dump)
++		return 0;
++	return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 23a331914..47b23513f 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -156,6 +156,8 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
+ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
+ 					  const int *threshold);
+ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 972bb1763..227474db5 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2762,6 +2762,8 @@ dfs_offload:
+ 	if (hostapd_drv_configure_edcca_threshold(hapd,
+ 						  hapd->iconf->edcca_threshold) < 0)
+ 		goto fail;
++	if (hostapd_drv_mu_ctrl(hapd) < 0)
++		goto fail;
+ 
+ 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ 		   iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 6121857dd..60bc4cd4c 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -10,6 +10,8 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
+ 	MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
+ 	MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
++	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
++	MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
+ 	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
+ };
+ 
+@@ -177,6 +179,19 @@ enum mtk_vendor_attr_rfeature_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_mu_ctrl {
++	MTK_VENDOR_ATTR_MU_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
++	MTK_VENDOR_ATTR_MU_CTRL_DUMP,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
++	MTK_VENDOR_ATTR_MU_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
++};
++
++
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+ 
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index bf103516b..2217809db 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -176,6 +176,11 @@ struct hostapd_channel_data {
+ 	 * punct_bitmap - RU puncturing bitmap
+ 	 */
+ 	u16 punct_bitmap;
++
++	/**
++	 * mu onoff=<val> (bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0))
++	 */
++	u8 mu_onoff;
+ };
+ 
+ #define HE_MAC_CAPAB_0		0
+@@ -5250,6 +5255,14 @@ struct wpa_driver_ops {
+ 				  const s8 edcca_compensation);
+ 	int (*configure_edcca_threshold)(void *priv, const int *threshold);
+ 	int (*get_edcca)(void *priv, const u8 mode, u8 *value);
++
++	/**
++	 * mu_ctrl - ctrl on off for UL/DL MURU
++	 * @priv: Private driver interface data
++	 *
++	 */
++	 int (*mu_ctrl)(void *priv, u8 mu_onoff);
++	 int (*mu_dump)(void *priv, u8 *mu_onoff);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 606632aad..2e011e3df 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -13939,6 +13939,114 @@ fail:
+ }
+ 
+ 
++#ifdef CONFIG_IEEE80211AX
++static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_mu_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting mu control");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
++		!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++		nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, mu_onoff)) {
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if(ret){
++		wpa_printf(MSG_ERROR, "Failed to set mu_onoff. ret=%d (%s)", ret, strerror(-ret));
++	}
++	return ret;
++}
++
++
++static int mu_dump_handler(struct nl_msg *msg, void *arg)
++{
++	u8 *mu_onoff = (u8 *) arg;
++	struct nlattr *tb[NL80211_ATTR_MAX + 1];
++	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_MU_CTRL_MAX + 1];
++	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++	struct nlattr *nl_vend, *attr;
++
++	static const struct nla_policy
++	mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL + 1] = {
++		[MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
++		[MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
++	};
++
++	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++			genlmsg_attrlen(gnlh, 0), NULL);
++
++	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++	if (!nl_vend)
++		return NL_SKIP;
++
++	nla_parse(tb_vendor, MTK_VENDOR_ATTR_MU_CTRL_MAX,
++		  nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++	attr = tb_vendor[MTK_VENDOR_ATTR_MU_CTRL_DUMP];
++	if (!attr) {
++		wpa_printf(MSG_ERROR, "nl80211: cannot find MTK_VENDOR_ATTR_MU_CTRL_DUMP");
++		return NL_SKIP;
++	}
++
++	*mu_onoff = nla_get_u8(attr);
++	wpa_printf(MSG_DEBUG, "nla_get mu_onoff: %d\n", *mu_onoff);
++
++	return 0;
++}
++
++static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *attr;
++	int ret;
++
++	if (!drv->mtk_mu_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting mu control");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL)) {
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++
++  attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!attr) {
++		nlmsg_free(msg);
++		return -1;
++	}
++
++	nla_nest_end(msg, attr);
++
++	ret = send_and_recv_resp(drv, msg, mu_dump_handler, mu_onoff);
++
++	if(ret){
++		wpa_printf(MSG_ERROR, "Failed to get mu_onoff. ret=%d (%s)", ret, strerror(-ret));
++	}
++
++	return ret;
++}
++#endif /* CONFIG_IEEE80211AX */
++
++
+ #ifdef CONFIG_DPP
+ static int nl80211_dpp_listen(void *priv, bool enable)
+ {
+@@ -14418,6 +14526,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.update_connect_params = nl80211_update_connection_params,
+ 	.send_external_auth_status = nl80211_send_external_auth_status,
+ 	.set_4addr_mode = nl80211_set_4addr_mode,
++	.mu_ctrl = nl80211_mu_onoff,
++	.mu_dump = nl80211_mu_dump,
+ #ifdef CONFIG_DPP
+ 	.dpp_listen = nl80211_dpp_listen,
+ #endif /* CONFIG_DPP */
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 62c47efbd..f99bba9e1 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -201,6 +201,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int puncturing:1;
+ 	unsigned int qca_ap_allowed_freqs:1;
+ 	unsigned int mtk_edcca_vendor_cmd_avail:1;
++	unsigned int mtk_mu_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 465fd318d..0eda91d0f 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1144,6 +1144,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL:
+ 					drv->mtk_edcca_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_MU_CTRL :
++					drv->mtk_mu_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0018-hostapd-MLO-send-link-id-during-flushing-stations.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0018-hostapd-MLO-send-link-id-during-flushing-stations.patch
deleted file mode 100644
index ce4a844..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0018-hostapd-MLO-send-link-id-during-flushing-stations.patch
+++ /dev/null
@@ -1,156 +0,0 @@
-From 8ac142806112477fa012414a2bdea22239e474a4 Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:48 +0530
-Subject: [PATCH 018/104] hostapd: MLO: send link id during flushing stations
-
-Currently, whenever a BSS is set up, it sends flush all stations via
-command - NL80211_CMD_DEL_STATION on its interface. However, in case
-of MLO, station could have been connected to other links by the time
-this link is coming up. Since there is no link id currently being
-passed, all those stations entries are also removed in the driver which is
-wrong.
-
-Hence add change to send link id along with the command during MLO so that
-the driver can use this link id and flush only those stations which are
-using the passed link id as one of its links.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/ap_drv_ops.c          | 10 +++++++++-
- src/drivers/driver.h         |  4 +++-
- src/drivers/driver_atheros.c |  2 +-
- src/drivers/driver_bsd.c     |  2 +-
- src/drivers/driver_hostap.c  |  2 +-
- src/drivers/driver_nl80211.c | 17 ++++++++++++++---
- 6 files changed, 29 insertions(+), 8 deletions(-)
-
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 0d493b837..32722084d 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -624,9 +624,17 @@ int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
- 
- int hostapd_flush(struct hostapd_data *hapd)
- {
-+	int link_id = -1;
-+
- 	if (hapd->driver == NULL || hapd->driver->flush == NULL)
- 		return 0;
--	return hapd->driver->flush(hapd->drv_priv);
-+
-+#ifdef CONFIG_IEEE80211BE
-+	if (hapd->conf && hapd->conf->mld_ap)
-+		link_id = hapd->mld_link_id;
-+#endif /* CONFIG_IEEE80211BE */
-+
-+	return hapd->driver->flush(hapd->drv_priv, link_id);
- }
- 
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index a7455ef6e..e672a1787 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -3578,13 +3578,15 @@ struct wpa_driver_ops {
- 	/**
- 	 * flush - Flush all association stations (AP only)
- 	 * @priv: Private driver interface data
-+	 * @link_id: In case of MLO, valid link_id on which all associated stations
-+	 *	     will be flushed. -1 otherwise.
- 	 * Returns: 0 on success, -1 on failure
- 	 *
- 	 * This function requests the driver to disassociate all associated
- 	 * stations. This function does not need to be implemented if the
- 	 * driver does not process association frames internally.
- 	 */
--	int (*flush)(void *priv);
-+	int (*flush)(void *priv, int link_id);
- 
- 	/**
- 	 * set_generic_elem - Add IEs into Beacon/Probe Response frames (AP)
-diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
-index ae7f0e535..71863306a 100644
---- a/src/drivers/driver_atheros.c
-+++ b/src/drivers/driver_atheros.c
-@@ -632,7 +632,7 @@ atheros_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
- 
- 
- static int
--atheros_flush(void *priv)
-+atheros_flush(void *priv, int link_id)
- {
- 	u8 allsta[IEEE80211_ADDR_LEN];
- 	os_memset(allsta, 0xff, IEEE80211_ADDR_LEN);
-diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
-index 850637f0d..82d8a0186 100644
---- a/src/drivers/driver_bsd.c
-+++ b/src/drivers/driver_bsd.c
-@@ -946,7 +946,7 @@ bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
- 
- 
- static int
--bsd_flush(void *priv)
-+bsd_flush(void *priv, int link_id)
- {
- 	u8 allsta[IEEE80211_ADDR_LEN];
- 
-diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
-index d3520aacc..3aa5860bc 100644
---- a/src/drivers/driver_hostap.c
-+++ b/src/drivers/driver_hostap.c
-@@ -572,7 +572,7 @@ static int hostap_set_ssid(void *priv, const u8 *buf, int len)
- }
- 
- 
--static int hostap_flush(void *priv)
-+static int hostap_flush(void *priv, int link_id)
- {
- 	struct hostap_driver_data *drv = priv;
- 	struct prism2_hostapd_param param;
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index e5fa22b59..9ac621ae6 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -7729,25 +7729,36 @@ static int i802_set_frag(void *priv, int frag)
- }
- 
- 
--static int i802_flush(void *priv)
-+static int i802_flush(void *priv, int link_id)
- {
- 	struct i802_bss *bss = priv;
- 	struct nl_msg *msg;
- 	int res;
- 
--	wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
--		   bss->ifname);
-+	if (link_id == NL80211_DRV_LINK_ID_NA)
-+		wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
-+			   bss->ifname);
-+	else
-+		wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (with link %d)",
-+			   bss->ifname, link_id);
- 
- 	/*
- 	 * XXX: FIX! this needs to flush all VLANs too
- 	 */
- 	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION);
-+	if (link_id >= 0 && (bss->valid_links & BIT(link_id)) &&
-+	    nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
-+		goto fail;
-+
- 	res = send_and_recv_cmd(bss->drv, msg);
- 	if (res) {
- 		wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d "
- 			   "(%s)", res, strerror(-res));
- 	}
- 	return res;
-+fail:
-+	nlmsg_free(msg);
-+	return -1;
- }
- 
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0018-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0018-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch
new file mode 100644
index 0000000..5033c63
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0018-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch
@@ -0,0 +1,247 @@
+From d6a05d4839676a55e440ea03420000dd2499b4c9 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 2 Sep 2022 01:03:23 +0800
+Subject: [PATCH 018/126] mtk: hostapd: Add three wire PTA ctrl hostapd vendor
+ command
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/config_file.c             |  4 ++++
+ src/ap/ap_config.c                |  1 +
+ src/ap/ap_config.h                | 13 ++++++++++++
+ src/ap/ap_drv_ops.c               | 11 +++++++++++
+ src/ap/ap_drv_ops.h               |  1 +
+ src/ap/hostapd.c                  |  2 ++
+ src/common/mtk_vendor.h           | 16 +++++++++++++++
+ src/drivers/driver.h              |  8 ++++++++
+ src/drivers/driver_nl80211.c      | 33 +++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |  1 +
+ src/drivers/driver_nl80211_capa.c |  3 +++
+ 11 files changed, 93 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 22da72c07..ebbd56734 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5513,6 +5513,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 			return 1;
+ 		}
+ 		conf->edcca_compensation = (s8) val;
++	} else if (os_strcmp(buf, "three_wire_enable") == 0) {
++		u8 en = atoi(pos);
++
++		conf->three_wire_enable = en;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 45c391a65..cd520ebdc 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -308,6 +308,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 
+ 	conf->edcca_enable = EDCCA_MODE_AUTO;
+ 	conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
++	conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
+ 
+ 	hostapd_set_and_check_bw320_offset(conf, 0);
+ 
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index baf3b40f8..3ab6cae2c 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1339,6 +1339,19 @@ struct hostapd_config {
+ 	u8 edcca_enable;
+ 	s8 edcca_compensation;
+ 	int *edcca_threshold;
++	u8 three_wire_enable;
++};
++
++enum three_wire_mode {
++	THREE_WIRE_MODE_DISABLE,
++	THREE_WIRE_MODE_EXT0_ENABLE,
++	THREE_WIRE_MODE_EXT1_ENABLE,
++	THREE_WIRE_MODE_ALL_ENABLE,
++
++	/* keep last */
++	NUM_THREE_WIRE_MODE,
++	THREE_WIRE_MODE_MAX =
++		NUM_THREE_WIRE_MODE - 1
+ };
+ 
+ enum edcca_mode {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 56d923462..fa6aa7085 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1312,3 +1312,14 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
+ 		return 0;
+ 	return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
+ }
++
++int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->driver->three_wire_ctrl)
++		return 0;
++	if (hapd->iconf->three_wire_enable > THREE_WIRE_MODE_MAX) {
++		wpa_printf(MSG_INFO, "Invalid value for three wire enable\n");
++		return 0;
++	}
++	return hapd->driver->three_wire_ctrl(hapd->drv_priv, hapd->iconf->three_wire_enable);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 47b23513f..008f56e76 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -158,6 +158,7 @@ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
+ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+ int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
++int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 227474db5..56a024bbf 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2764,6 +2764,8 @@ dfs_offload:
+ 		goto fail;
+ 	if (hostapd_drv_mu_ctrl(hapd) < 0)
+ 		goto fail;
++	if (hostapd_drv_three_wire_ctrl(hapd) < 0)
++		goto fail;
+ 
+ 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ 		   iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 60bc4cd4c..99ecbaf71 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -13,6 +13,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
+ 	MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
+ 	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
++	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8
+ };
+ 
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -58,6 +59,21 @@ static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
+ 	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
+ };
+ 
++enum mtk_vendor_attr_3wire_ctrl {
++	MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_3WIRE_CTRL_MODE,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL,
++	MTK_VENDOR_ATTR_3WIRE_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
++};
++
++static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
++	[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
++};
++
+ enum mtk_vendor_attr_csi_ctrl {
+ 	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
+ 
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 2217809db..13e91d21d 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5263,6 +5263,14 @@ struct wpa_driver_ops {
+ 	 */
+ 	 int (*mu_ctrl)(void *priv, u8 mu_onoff);
+ 	 int (*mu_dump)(void *priv, u8 *mu_onoff);
++
++	/**
++	 * three_wire_ctrl - set three_wire_ctrl mode
++	 * @priv: Private driver interface data
++	 * @three_wire_enable: three_wire_ctrl mode
++	 *
++	 */
++	 int (*three_wire_ctrl)(void *priv, u8 three_wire_enable);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 2e011e3df..f94fa19b0 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14379,6 +14379,38 @@ static int nl80211_get_edcca(void *priv, const u8 mode, u8 *value)
+ 	return ret;
+ }
+ 
++static int nl80211_enable_three_wire(void *priv, const u8 three_wire_enable)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	/* Prepare nl80211 cmd */
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_3wire_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting three wire control");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL) ||
++	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_3WIRE_CTRL_MODE, three_wire_enable)) {
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to enable three wire. ret=%d (%s) ",
++			   ret, strerror(-ret));
++	}
++	return ret;
++}
+ 
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+@@ -14546,4 +14578,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.configure_edcca_enable = nl80211_configure_edcca_enable,
+ 	.configure_edcca_threshold = nl80211_configure_edcca_threshold,
+ 	.get_edcca = nl80211_get_edcca,
++	.three_wire_ctrl = nl80211_enable_three_wire,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index f99bba9e1..5de6ca6f0 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -202,6 +202,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int qca_ap_allowed_freqs:1;
+ 	unsigned int mtk_edcca_vendor_cmd_avail:1;
+ 	unsigned int mtk_mu_vendor_cmd_avail:1;
++	unsigned int mtk_3wire_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 0eda91d0f..433eecf56 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1147,6 +1147,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_MU_CTRL :
+ 					drv->mtk_mu_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL :
++					drv->mtk_3wire_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0019-hostapd-MLO-display-link-details-in-status-command.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0019-hostapd-MLO-display-link-details-in-status-command.patch
deleted file mode 100644
index 77f3ddd..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0019-hostapd-MLO-display-link-details-in-status-command.patch
+++ /dev/null
@@ -1,84 +0,0 @@
-From 3d12a39b10565a10bec40b53cf6e69b60115a35f Mon Sep 17 00:00:00 2001
-From: Harshitha Prem <quic_hprem@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:49 +0530
-Subject: [PATCH 019/104] hostapd: MLO: display link details in status command
-
-Currently, link id and number of link details of a MLD AP interface is not
-displayed in status command of hostapd_cli.
-
-Add changes to display the link id and number of link details.
-
-The details would be seen as below for a MLD AP interface:
-
-$ hostapd_cli -i wlan0 status | grep link
-num_links=1
-link_id=0
-link_addr=AA:BB:CC:DD:EE:FF
-
-$ hostapd_cli -i wlan1 status | grep link
-num_links=2
-link_id=0
-link_addr=AA:BB:CC:DD:EE:FF
-partner_link_id=1
-partner_link_addr=AA:BB:CC:DD:EE:AA
-
-The above details would not be displayed for non-MLD AP interfaces.
-
-Signed-off-by: Harshitha Prem <quic_hprem@quicinc.com>
-Co-developed-by: Manish Dharanenthiran <quic_mdharane@quicinc.com>
-Signed-off-by: Manish Dharanenthiran <quic_mdharane@quicinc.com>
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/ctrl_iface_ap.c | 36 ++++++++++++++++++++++++++++++++++++
- 1 file changed, 36 insertions(+)
-
-diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index 272317774..2cfef4bd4 100644
---- a/src/ap/ctrl_iface_ap.c
-+++ b/src/ap/ctrl_iface_ap.c
-@@ -887,6 +887,42 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
- 				return len;
- 			len += ret;
- 		}
-+
-+		if (hapd->conf->mld_ap) {
-+			struct hostapd_data *link_bss;
-+
-+			ret = os_snprintf(buf + len, buflen - len,
-+					  "num_links=%d\n",
-+					  hapd->mld->num_links);
-+			if (os_snprintf_error(buflen - len, ret))
-+				return len;
-+			len += ret;
-+
-+			/* self bss */
-+			ret = os_snprintf(buf + len, buflen - len,
-+					  "link_id=%d\n"
-+					  "link_addr=" MACSTR "\n",
-+					  hapd->mld_link_id,
-+					  MAC2STR(hapd->own_addr));
-+			if (os_snprintf_error(buflen - len, ret))
-+				return len;
-+			len += ret;
-+
-+			/* partner bss */
-+			for_each_mld_link(link_bss, hapd) {
-+				if (link_bss == hapd)
-+					continue;
-+
-+				ret = os_snprintf(buf + len, buflen - len,
-+						  "partner_link_id=%d\n"
-+						  "partner_link_addr=" MACSTR "\n",
-+						  link_bss->mld_link_id,
-+						  MAC2STR(link_bss->own_addr));
-+				if (os_snprintf_error(buflen - len, ret))
-+					return len;
-+				len += ret;
-+			}
-+		}
- 	}
- #endif /* CONFIG_IEEE80211BE */
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0019-mtk-hostapd-Add-hostapd-iBF-control.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0019-mtk-hostapd-Add-hostapd-iBF-control.patch
new file mode 100644
index 0000000..161d154
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0019-mtk-hostapd-Add-hostapd-iBF-control.patch
@@ -0,0 +1,431 @@
+From 63d5ec844ead699159dd047efc91717861efba41 Mon Sep 17 00:00:00 2001
+From: mtk27835 <shurong.wen@mediatek.com>
+Date: Wed, 7 Sep 2022 14:41:51 -0700
+Subject: [PATCH 019/126] mtk: hostapd: Add hostapd iBF control
+
+Signed-off-by: mtk27835 <shurong.wen@mediatek.com>
+---
+ hostapd/config_file.c             |   3 +
+ hostapd/ctrl_iface.c              |  26 +++++++
+ hostapd/hostapd_cli.c             |   9 +++
+ src/ap/ap_config.c                |   1 +
+ src/ap/ap_config.h                |   2 +
+ src/ap/ap_drv_ops.c               |  14 ++++
+ src/ap/ap_drv_ops.h               |   2 +
+ src/ap/hostapd.c                  |   2 +
+ src/common/mtk_vendor.h           |  35 +++++++++-
+ src/drivers/driver.h              |  19 ++++++
+ src/drivers/driver_nl80211.c      | 108 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   3 +
+ 13 files changed, 224 insertions(+), 1 deletion(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index ebbd56734..e437aa981 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5517,6 +5517,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		u8 en = atoi(pos);
+ 
+ 		conf->three_wire_enable = en;
++	} else if (os_strcmp(buf, "ibf_enable") == 0) { /*ibf setting is per device*/
++		int val = atoi(pos);
++		conf->ibf_enable = !!val;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index d168c2fb5..3221c0fb1 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4240,6 +4240,30 @@ hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
+ }
+ 
+ 
++static int
++hostapd_ctrl_iface_get_ibf(struct hostapd_data *hapd, char *buf,
++					 size_t buflen)
++{
++	u8 ibf_enable;
++	int ret;
++	char *pos, *end;
++
++	pos = buf;
++	end = buf + buflen;
++
++	if (hostapd_drv_ibf_dump(hapd, &ibf_enable) == 0) {
++		hapd->iconf->ibf_enable = ibf_enable;
++		ret = os_snprintf(pos, end - pos, "ibf_enable: %u\n",
++			  ibf_enable);
++	}
++
++	if (os_snprintf_error(end - pos, ret))
++		return 0;
++
++	return ret;
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -4859,6 +4883,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 							  reply_size);
+ 	} else if (os_strncmp(buf, "GET_MU", 6) == 0) {
+ 		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
++	} else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
++		reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index f4dc1517e..ccbf863f6 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1691,6 +1691,13 @@ static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ #endif /* ANDROID */
+ 
+ 
++static int hostapd_cli_cmd_get_ibf(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "GET_IBF", 0, NULL, NULL);
++}
++
++
+ struct hostapd_cli_cmd {
+ 	const char *cmd;
+ 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -1921,6 +1928,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ #endif /* ANDROID */
+ 	{ "inband_discovery", hostapd_cli_cmd_inband_discovery, NULL,
+           "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
++	{ "get_ibf", hostapd_cli_cmd_get_ibf, NULL,
++	  " = show iBF state (enabled/disabled)"},
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index cd520ebdc..08b55d0eb 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -309,6 +309,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 	conf->edcca_enable = EDCCA_MODE_AUTO;
+ 	conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
+ 	conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
++	conf->ibf_enable = IBF_DEFAULT_ENABLE;
+ 
+ 	hostapd_set_and_check_bw320_offset(conf, 0);
+ 
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 3ab6cae2c..3f07147db 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1340,6 +1340,7 @@ struct hostapd_config {
+ 	s8 edcca_compensation;
+ 	int *edcca_threshold;
+ 	u8 three_wire_enable;
++	u8 ibf_enable;
+ };
+ 
+ enum three_wire_mode {
+@@ -1503,6 +1504,7 @@ hostapd_set_and_check_bw320_offset(struct hostapd_config *conf,
+ #endif /* CONFIG_IEEE80211BE */
+ }
+ 
++#define IBF_DEFAULT_ENABLE 0
+ 
+ int hostapd_mac_comp(const void *a, const void *b);
+ struct hostapd_config * hostapd_config_defaults(void);
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index fa6aa7085..5172f06b9 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1323,3 +1323,17 @@ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
+ 	}
+ 	return hapd->driver->three_wire_ctrl(hapd->drv_priv, hapd->iconf->three_wire_enable);
+ }
++
++int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->driver->ibf_ctrl)
++		return 0;
++	return hapd->driver->ibf_ctrl(hapd->drv_priv, hapd->iconf->ibf_enable);
++}
++
++int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable)
++{
++	if (!hapd->driver || !hapd->driver->ibf_dump)
++		return 0;
++	return hapd->driver->ibf_dump(hapd->drv_priv, ibf_enable);
++}
+\ No newline at end of file
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 008f56e76..3633e2ed5 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -159,6 +159,8 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+ int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 56a024bbf..7503718c8 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2766,6 +2766,8 @@ dfs_offload:
+ 		goto fail;
+ 	if (hostapd_drv_three_wire_ctrl(hapd) < 0)
+ 		goto fail;
++	if (hostapd_drv_ibf_ctrl(hapd) < 0)
++		goto fail;
+ 
+ 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ 		   iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 99ecbaf71..9811f266e 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -13,7 +13,8 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
+ 	MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
+ 	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
+-	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8
++	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
++	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
+ };
+ 
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -207,6 +208,38 @@ enum mtk_vendor_attr_mu_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_ibf_ctrl {
++	MTK_VENDOR_ATTR_IBF_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_IBF_CTRL_ENABLE,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_IBF_CTRL,
++	MTK_VENDOR_ATTR_IBF_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_IBF_CTRL - 1
++};
++
++enum mtk_vendor_attr_ibf_dump {
++	MTK_VENDOR_ATTR_IBF_DUMP_UNSPEC,
++
++	MTK_VENDOR_ATTR_IBF_DUMP_ENABLE,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_IBF_DUMP,
++	MTK_VENDOR_ATTR_IBF_DUMP_MAX =
++		NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
++};
++
++static struct nla_policy
++ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
++	[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
++};
++
++static struct nla_policy
++ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
++	[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
++};
++
+ 
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 13e91d21d..97be9e8fb 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -181,6 +181,11 @@ struct hostapd_channel_data {
+ 	 * mu onoff=<val> (bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0))
+ 	 */
+ 	u8 mu_onoff;
++
++	/**
++	 * ibf_enable=<val>
++	 */
++	u8 ibf_enable;
+ };
+ 
+ #define HE_MAC_CAPAB_0		0
+@@ -5271,6 +5276,20 @@ struct wpa_driver_ops {
+ 	 *
+ 	 */
+ 	 int (*three_wire_ctrl)(void *priv, u8 three_wire_enable);
++
++	/**
++	 * ibf_ctrl - ctrl disable/enable for ibf
++	 * @priv: Private driver interface data
++	 *
++	 */
++	int (*ibf_ctrl)(void *priv, u8 ibf_enable);
++
++	/**
++	 * ibf_dump - dump ibf
++	 * @priv: Private driver interface data
++	 *
++	 */
++	int (*ibf_dump)(void *priv, u8 *ibf_enable);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index f94fa19b0..c5c0d1b49 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14412,6 +14412,112 @@ static int nl80211_enable_three_wire(void *priv, const u8 three_wire_enable)
+ 	return ret;
+ }
+ 
++static int nl80211_ibf_enable(void *priv, u8 ibf_enable)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_ibf_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting ibf control");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_IBF_CTRL_ENABLE, ibf_enable);
++
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to set ibf_enable. ret=%d (%s)", ret, strerror(-ret));
++	}
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
++static int ibf_dump_handler(struct nl_msg *msg, void *arg)
++{
++	u8 *ibf_enable = (u8 *) arg;
++	struct nlattr *tb[NL80211_ATTR_MAX + 1];
++	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_IBF_DUMP_MAX + 1];
++	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++	struct nlattr *nl_vend, *attr;
++
++	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++			genlmsg_attrlen(gnlh, 0), NULL);
++
++	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++	if (!nl_vend)
++		return NL_SKIP;
++
++	nla_parse(tb_vendor, MTK_VENDOR_ATTR_IBF_DUMP_MAX,
++			nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++	attr = tb_vendor[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE];
++	if (!attr) {
++		wpa_printf(MSG_ERROR, "nl80211: cannot find MTK_VENDOR_ATTR_IBF_DUMP_ENABLE");
++		return NL_SKIP;
++	}
++
++	*ibf_enable = nla_get_u8(attr);
++
++	return NL_SKIP;
++}
++
++static int
++nl80211_ibf_dump(void *priv, u8 *ibf_enable)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++	if (!data)
++		goto fail;
++
++	nla_nest_end(msg, data);
++
++	ret = send_and_recv_resp(drv, msg, ibf_dump_handler, ibf_enable);
++
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to dump ibf_enable. ret=%d (%s)", ret, strerror(-ret));
++	}
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+ 	.desc = "Linux nl80211/cfg80211",
+@@ -14579,4 +14685,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.configure_edcca_threshold = nl80211_configure_edcca_threshold,
+ 	.get_edcca = nl80211_get_edcca,
+ 	.three_wire_ctrl = nl80211_enable_three_wire,
++	.ibf_ctrl = nl80211_ibf_enable,
++	.ibf_dump = nl80211_ibf_dump,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 5de6ca6f0..1432eeda8 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -203,6 +203,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_edcca_vendor_cmd_avail:1;
+ 	unsigned int mtk_mu_vendor_cmd_avail:1;
+ 	unsigned int mtk_3wire_vendor_cmd_avail:1;
++	unsigned int mtk_ibf_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 433eecf56..670588dd2 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1150,6 +1150,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL :
+ 					drv->mtk_3wire_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL:
++					drv->mtk_ibf_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0020-hostapd-fix-RNR-building-for-co-location-and-MLO.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0020-hostapd-fix-RNR-building-for-co-location-and-MLO.patch
deleted file mode 100644
index 879adf1..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0020-hostapd-fix-RNR-building-for-co-location-and-MLO.patch
+++ /dev/null
@@ -1,748 +0,0 @@
-From 8affcd80f5143fa23d3f21427b6b9f11af35ef5d Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:50 +0530
-Subject: [PATCH 020/104] hostapd: fix RNR building for co-location and MLO
-
-Currently with MLO changes, RNR formation for co-location or MLO
-was not working as expected. Hence make it work as per the
-expectation.
-
-For example, during co-location, if the BSS is also its ML partner
-then there is no need to include a separate TBTT for it.
-
-Also, during co-location, if the BSS is not its partner but it is
-ML capable, then the TBTT length should be 16 bytes and it should
-include the MLD Parameters for it in the RNR.
-
-During co-location, for a given Neighbor AP (operating on a given
-channel and op-class) if it has BSSes which are ML capable as well
-as BSSes which are not, then there should be two Neighbor AP Info
-present. One indicating TBTT length as 13 bytes and one indicating
-TBTT info length as 16 bytes.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- src/ap/beacon.c     |  12 +-
- src/ap/ieee802_11.c | 387 ++++++++++++++++++++++++++++++++------------
- src/ap/ieee802_11.h |   5 +-
- 3 files changed, 290 insertions(+), 114 deletions(-)
-
-diff --git a/src/ap/beacon.c b/src/ap/beacon.c
-index b780d98e4..4354dfae3 100644
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -677,7 +677,7 @@ static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd,
- 					 params->known_bss,
- 					 params->known_bss_len, NULL);
- 	if (!params->is_ml_sta_info)
--		buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
-+		buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP, true);
- 	buflen += hostapd_mbo_ie_len(hapd);
- 	buflen += hostapd_eid_owe_trans_len(hapd);
- 	buflen += hostapd_eid_dpp_cc_len(hapd);
-@@ -797,7 +797,7 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
- 	pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
- 
- 	if (!params->is_ml_sta_info)
--		pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP);
-+		pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP, true);
- 	pos = hostapd_eid_fils_indic(hapd, pos, 0);
- 	pos = hostapd_get_rsnxe(hapd, pos, epos - pos);
- 
-@@ -1946,7 +1946,7 @@ static u8 * hostapd_gen_fils_discovery(struct hostapd_data *hapd, size_t *len)
- 		total_len += 3;
- 	}
- 
--	total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION);
-+	total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION, true);
- 
- 	pos = hostapd_eid_fils_indic(hapd, buf, 0);
- 	buf_len = pos - buf;
-@@ -2020,7 +2020,7 @@ static u8 * hostapd_gen_fils_discovery(struct hostapd_data *hapd, size_t *len)
- 	/* Fill in the Length field value */
- 	*length_pos = pos - (length_pos + 1);
- 
--	pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION);
-+	pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION, true);
- 
- 	/* FILS Indication element */
- 	if (buf_len) {
-@@ -2126,7 +2126,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
- 	if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
- 	    hapd == hostapd_mbssid_get_tx_bss(hapd))
- 		tail_len += 5; /* Multiple BSSID Configuration element */
--	tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON);
-+	tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON, true);
- 	tail_len += hostapd_mbo_ie_len(hapd);
- 	tail_len += hostapd_eid_owe_trans_len(hapd);
- 	tail_len += hostapd_eid_dpp_cc_len(hapd);
-@@ -2262,7 +2262,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
- 
- 	tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
- 
--	tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON);
-+	tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON, true);
- 	tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
- 	tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos);
- 	tailpos = hostapd_eid_mbssid_config(hapd, tailpos,
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 7ee18f4ae..9a23c7240 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -7273,20 +7273,21 @@ static size_t
- hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
- 			  struct hostapd_data *reporting_hapd,
- 			  size_t *current_len,
--			  struct mbssid_ie_profiles *skip_profiles)
-+			  struct mbssid_ie_profiles *skip_profiles,
-+			  bool mld_update)
- {
- 	size_t total_len = 0, len = *current_len;
--	int tbtt_count = 0;
--	size_t i, start = 0;
--	bool ap_mld = false;
-+	int tbtt_count, total_tbtt_count = 0;
-+	size_t i, start;
-+	u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
- 
--#ifdef CONFIG_IEEE80211BE
--	ap_mld = !!hapd->conf->mld_ap;
--#endif /* CONFIG_IEEE80211BE */
-+repeat_rnr_len:
-+	start = 0;
-+	tbtt_count = 0;
- 
- 	while (start < hapd->iface->num_bss) {
- 		if (!len ||
--		    len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255 ||
-+		    len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
- 		    tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
- 			len = RNR_HEADER_LEN;
- 			total_len += RNR_HEADER_LEN;
-@@ -7298,10 +7299,15 @@ hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
- 
- 		for (i = start; i < hapd->iface->num_bss; i++) {
- 			struct hostapd_data *bss = hapd->iface->bss[i];
-+			bool ap_mld = false;
- 
- 			if (!bss || !bss->conf || !bss->started)
- 				continue;
- 
-+#ifdef CONFIG_IEEE80211BE
-+			ap_mld = !!bss->conf->mld_ap;
-+#endif /* CONFIG_IEEE80211BE */
-+
- 			if (bss == reporting_hapd ||
- 			    bss->conf->ignore_broadcast_ssid)
- 				continue;
-@@ -7310,23 +7316,71 @@ hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
- 			    i >= skip_profiles->start && i < skip_profiles->end)
- 				continue;
- 
--			if (len + RNR_TBTT_INFO_LEN > 255 ||
-+			/* No need to report if length is for normal TBTT and the BSS
-+			 * is a MLD. MLD TBTT will include this.
-+			 */
-+			if (tbtt_info_len == RNR_TBTT_INFO_LEN && ap_mld)
-+				continue;
-+
-+			/* No need to report if length is for MLD TBTT and the BSS
-+			 * is not MLD. Normal TBTT will include this.
-+			 */
-+			if (tbtt_info_len == RNR_TBTT_INFO_MLD_LEN && !ap_mld)
-+				continue;
-+
-+#ifdef CONFIG_IEEE80211BE
-+			/* If building for co-location and they are ML partners,
-+			 * no need to include since the ML RNR will carry this.
-+			 */
-+			if (!mld_update && hostapd_is_ml_partner(reporting_hapd, bss))
-+				continue;
-+
-+			/* If building for ML RNR and they are not ML parnters,
-+			 * don't include.
-+			 */
-+			if (mld_update && !hostapd_is_ml_partner(reporting_hapd, bss))
-+				continue;
-+#endif /* CONFIG_IEEE80211BE */
-+
-+			if (len + tbtt_info_len > 255 ||
- 			    tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
- 				break;
- 
--			if (!ap_mld) {
--				len += RNR_TBTT_INFO_LEN;
--				total_len += RNR_TBTT_INFO_LEN;
--			} else {
--				len += RNR_TBTT_INFO_MLD_LEN;
--				total_len += RNR_TBTT_INFO_MLD_LEN;
--			}
-+			len += tbtt_info_len;
-+			total_len += tbtt_info_len;
- 			tbtt_count++;
- 		}
- 		start = i;
- 	}
- 
--	if (!tbtt_count)
-+	total_tbtt_count += tbtt_count;
-+
-+	/* If building for co-location, re-build again but this time include
-+	 * ML TBTTs.
-+	 */
-+	if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) {
-+		tbtt_info_len = RNR_TBTT_INFO_MLD_LEN;
-+
-+		/* If no TBTT was found, then adjust the len and total_len since
-+		 * it would have incremented before we checked all bss.
-+		 */
-+		if (!tbtt_count) {
-+			len -= RNR_TBTT_HEADER_LEN;
-+			total_len -= RNR_TBTT_HEADER_LEN;
-+		}
-+
-+		goto repeat_rnr_len;
-+	}
-+
-+	/* this is possible when it re-built and in that no suitable TBTT was
-+	 * found. Adjust the length accordingly.
-+	 */
-+	if (!tbtt_count && total_tbtt_count) {
-+		len -= RNR_TBTT_HEADER_LEN;
-+		total_len -= RNR_TBTT_HEADER_LEN;
-+	}
-+
-+	if (!total_tbtt_count)
- 		total_len = 0;
- 	else
- 		*current_len = len;
-@@ -7375,8 +7429,8 @@ static enum colocation_mode get_colocation_mode(struct hostapd_data *hapd)
- }
- 
- 
--static size_t hostapd_eid_rnr_multi_iface_len(struct hostapd_data *hapd,
--					      size_t *current_len)
-+static size_t hostapd_eid_rnr_colocation_len(struct hostapd_data *hapd,
-+					     size_t *current_len)
- {
- 	struct hostapd_iface *iface;
- 	size_t len = 0;
-@@ -7387,34 +7441,57 @@ static size_t hostapd_eid_rnr_multi_iface_len(struct hostapd_data *hapd,
- 
- 	for (i = 0; i < hapd->iface->interfaces->count; i++) {
- 		iface = hapd->iface->interfaces->iface[i];
--		bool ap_mld = false;
--
--#ifdef CONFIG_IEEE80211BE
--		if (hostapd_is_ml_partner(hapd, iface->bss[0]))
--			ap_mld = true;
--#endif /* CONFIG_IEEE80211BE */
- 
--		if (iface == hapd->iface ||
--		    !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
-+		if (!iface || iface == hapd->iface ||
-+		    !is_6ghz_op_class(iface->conf->op_class))
- 			continue;
- 
- 		len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
--						 current_len, NULL);
-+						 current_len, NULL, false);
- 	}
- 
- 	return len;
- }
- 
--
--size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type)
-+static size_t hostapd_eid_rnr_mlo_len(struct hostapd_data *hapd, u32 type,
-+				      size_t *current_len)
- {
--	size_t total_len = 0, current_len = 0;
--	enum colocation_mode mode = get_colocation_mode(hapd);
--	bool ap_mld = false;
-+	size_t len = 0;
- 
- #ifdef CONFIG_IEEE80211BE
--	ap_mld = !!hapd->conf->mld_ap;
-+	struct hostapd_iface *iface;
-+	size_t i;
-+
-+	if (!hapd->iface || !hapd->iface->interfaces)
-+		return 0;
-+
-+	if (!hapd->conf->mld_ap)
-+		return 0;
-+
-+	/* TODO allow for FILS/Action as well */
-+	if (type != WLAN_FC_STYPE_BEACON && type != WLAN_FC_STYPE_PROBE_RESP)
-+		return 0;
-+
-+	for (i = 0; i < hapd->iface->interfaces->count; i++) {
-+		iface = hapd->iface->interfaces->iface[i];
-+
-+		if (!iface || iface == hapd->iface)
-+			continue;
-+
-+		if (hapd->iface->freq == iface->freq)
-+			continue;
-+
-+		len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
-+						 current_len, NULL, true);
-+	}
- #endif /* CONFIG_IEEE80211BE */
-+	return len;
-+}
-+
-+size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type, bool include_mld_params)
-+{
-+	size_t total_len = 0, current_len = 0;
-+	enum colocation_mode mode = get_colocation_mode(hapd);
- 
- 	switch (type) {
- 	case WLAN_FC_STYPE_BEACON:
-@@ -7423,29 +7500,35 @@ size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type)
- 		/* fallthrough */
- 
- 	case WLAN_FC_STYPE_PROBE_RESP:
--		if (mode == COLOCATED_LOWER_BAND || ap_mld)
-+		if (mode == COLOCATED_LOWER_BAND)
- 			total_len +=
--				hostapd_eid_rnr_multi_iface_len(hapd,
--								&current_len);
-+				hostapd_eid_rnr_colocation_len(hapd,
-+							       &current_len);
- 
- 		if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
- 		    !hapd->iconf->mbssid)
- 			total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
- 							       &current_len,
--							       NULL);
-+							       NULL, false);
- 		break;
- 
- 	case WLAN_FC_STYPE_ACTION:
- 		if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
- 			total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
- 							       &current_len,
--							       NULL);
-+							       NULL, false);
- 		break;
- 
- 	default:
- 		break;
- 	}
- 
-+	/* For EMA Beacons, MLD neighbor repoting is added as part of mbssid rnr */
-+	if (include_mld_params &&
-+	    (type != WLAN_FC_STYPE_BEACON ||
-+	     hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED))
-+		total_len += hostapd_eid_rnr_mlo_len(hapd, type, &current_len);
-+
- 	return total_len;
- }
- 
-@@ -7509,13 +7592,14 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
- 				struct hostapd_data *reporting_hapd,
- 				struct mbssid_ie_profiles *skip_profiles,
- 				size_t i, u8 *tbtt_count, size_t *len,
--				u8 **pos)
-+				u8 **pos, u8 **tbtt_count_pos, u8 tbtt_info_len,
-+				u8 op_class, bool mld_update)
- {
- 	struct hostapd_iface *iface = hapd->iface;
- 	struct hostapd_data *bss = iface->bss[i];
- 	u8 bss_param = 0;
--	bool ap_mld = false;
- 	u8 *eid = *pos;
-+	bool ap_mld = false;
- 
- #ifdef CONFIG_IEEE80211BE
- 	ap_mld = !!hapd->conf->mld_ap;
-@@ -7529,10 +7613,47 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
- 	    && i >= skip_profiles->start && i < skip_profiles->end)
- 		return false;
- 
-+	/* No need to report if length is for normal TBTT and the BSS
-+	 * is a MLD. MLD TBTT will include this.
-+	 */
-+	if (tbtt_info_len == RNR_TBTT_INFO_LEN && ap_mld)
-+		return false;
-+
-+	/* No need to report if length is for MLD TBTT and the BSS
-+	 * is not MLD. Normal TBTT will include this.
-+	 */
-+	if (tbtt_info_len == RNR_TBTT_INFO_MLD_LEN && !ap_mld)
-+		return false;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	/* If building for co-location and they are ML partners,
-+	 * no need to include since the ML RNR will carry this.
-+	 */
-+	if (!mld_update && hostapd_is_ml_partner(reporting_hapd, bss))
-+		return false;
-+
-+	/* If building for ML RNR and they are not ML parnters,
-+	 * don't include.
-+	 */
-+	if (mld_update && !hostapd_is_ml_partner(reporting_hapd, bss))
-+		return false;
-+#endif /* CONFIG_IEEE80211BE */
-+
- 	if (*len + RNR_TBTT_INFO_LEN > 255 ||
- 	    *tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
- 		return true;
- 
-+	if (!(*tbtt_count)) {
-+		/* Add Neighbor report header info only if there is at least
-+		 * one tbtt info available
-+		 */
-+		*tbtt_count_pos = eid++;
-+		*eid++ = tbtt_info_len;
-+		*eid++ = op_class;
-+		*eid++ = bss->iconf->channel;
-+		*len += RNR_TBTT_HEADER_LEN;
-+	}
-+
- 	*eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
- 	os_memcpy(eid, bss->own_addr, ETH_ALEN);
- 	eid += ETH_ALEN;
-@@ -7556,29 +7677,36 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
- 	*eid++ = bss_param;
- 	*eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER;
- 
--	if (!ap_mld) {
--		*len += RNR_TBTT_INFO_LEN;
--	} else {
- #ifdef CONFIG_IEEE80211BE
--		u8 param_ch = hapd->eht_mld_bss_param_change;
--
--		if (hostapd_is_ml_partner(bss, reporting_hapd))
--			*eid++ = 0;
--		else
--			*eid++ = hostapd_get_mld_id(hapd);
--
--		*eid++ = hapd->mld_link_id | ((param_ch & 0xF) << 4);
--		*eid = (param_ch >> 4) & 0xF;
-+	if (ap_mld) {
-+		u8 param_ch = bss->eht_mld_bss_param_change;
-+		bool is_partner;
-+
-+		/* If bss is not partner of the reporting_hapd then
-+		 *  a) MLD ID advertised shall be 255.
-+		 *  b) Link ID advertised shall be 15.
-+		 *  c) BPCC advertised shall be 255
-+		 */
-+		is_partner = hostapd_is_ml_partner(bss, reporting_hapd);
-+		/* MLD ID */
-+		*eid++ = is_partner ? hostapd_get_mld_id(bss) : 0xFF;
-+		/* Link ID (Bit 3 to Bit 0)
-+		 * BPCC (Bit 4 to Bit 7)
-+		 */
-+		*eid++ = is_partner ?
-+			 bss->mld_link_id | ((param_ch & 0xF) << 4) :
-+			 (MAX_NUM_MLD_LINKS | 0xF0);
-+		/* BPCC (Bit 3 to Bit 0) */
-+		*eid = is_partner ? ((param_ch & 0xF0) >> 4) : 0x0F;
- #ifdef CONFIG_TESTING_OPTIONS
--		if (hapd->conf->mld_indicate_disabled)
-+		if (bss->conf->mld_indicate_disabled)
- 			*eid |= RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
- #endif /* CONFIG_TESTING_OPTIONS */
- 		eid++;
--
--		*len += RNR_TBTT_INFO_MLD_LEN;
--#endif /* CONFIG_IEEE80211BE */
- 	}
-+#endif /* CONFIG_IEEE80211BE */
- 
-+	*len += tbtt_info_len;
- 	(*tbtt_count)++;
- 	*pos = eid;
- 
-@@ -7589,18 +7717,16 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
- static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
- 				  struct hostapd_data *reporting_hapd,
- 				  u8 *eid, size_t *current_len,
--				  struct mbssid_ie_profiles *skip_profiles)
-+				  struct mbssid_ie_profiles *skip_profiles,
-+				  bool mld_update)
- {
- 	struct hostapd_iface *iface = hapd->iface;
--	size_t i, start = 0;
-+	size_t i, start;
- 	size_t len = *current_len;
--	u8 *tbtt_count_pos, *eid_start = eid, *size_offset = (eid - len) + 1;
--	u8 tbtt_count = 0, op_class, channel;
--	bool ap_mld = false;
--
--#ifdef CONFIG_IEEE80211BE
--	ap_mld = !!hapd->conf->mld_ap;
--#endif /* CONFIG_IEEE80211BE */
-+	u8 *eid_start = eid, *size_offset = (eid - len) + 1;
-+	u8 *tbtt_count_pos = size_offset + 1;
-+	u8 tbtt_count, total_tbtt_count = 0, op_class, channel;
-+	u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
- 
- 	if (!(iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || !iface->freq)
- 		return eid;
-@@ -7612,9 +7738,12 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
- 	    NUM_HOSTAPD_MODES)
- 		return eid;
- 
-+repeat_rnr:
-+	start = 0;
-+	tbtt_count = 0;
- 	while (start < iface->num_bss) {
- 		if (!len ||
--		    len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255 ||
-+		    len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
- 		    tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
- 			eid_start = eid;
- 			*eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT;
-@@ -7623,34 +7752,42 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
- 			tbtt_count = 0;
- 		}
- 
--		tbtt_count_pos = eid++;
--		*eid++ = ap_mld ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
--		*eid++ = op_class;
--		*eid++ = hapd->iconf->channel;
--		len += RNR_TBTT_HEADER_LEN;
--
- 		for (i = start; i < iface->num_bss; i++) {
- 			if (hostapd_eid_rnr_bss(hapd, reporting_hapd,
- 						skip_profiles, i,
--						&tbtt_count, &len, &eid))
-+						&tbtt_count, &len, &eid,
-+						&tbtt_count_pos, tbtt_info_len,
-+						op_class, mld_update))
- 				break;
- 		}
- 
- 		start = i;
--		*tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
--		*size_offset = (eid - size_offset) - 1;
-+
-+		if (tbtt_count) {
-+			*tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
-+			*size_offset = (eid - size_offset) - 1;
-+		}
-+	}
-+
-+	total_tbtt_count += tbtt_count;
-+
-+	/* If building for co-location, re-build again but this time include
-+	 * ML TBTTs.
-+	 */
-+	if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) {
-+		tbtt_info_len = RNR_TBTT_INFO_MLD_LEN;
-+		goto repeat_rnr;
- 	}
- 
--	if (tbtt_count == 0)
-+	if (!total_tbtt_count)
- 		return eid_start;
- 
- 	*current_len = len;
- 	return eid;
- }
- 
--
--static u8 * hostapd_eid_rnr_multi_iface(struct hostapd_data *hapd, u8 *eid,
--					size_t *current_len)
-+u8 *hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
-+			       size_t *current_len)
- {
- 	struct hostapd_iface *iface;
- 	size_t i;
-@@ -7660,35 +7797,56 @@ static u8 * hostapd_eid_rnr_multi_iface(struct hostapd_data *hapd, u8 *eid,
- 
- 	for (i = 0; i < hapd->iface->interfaces->count; i++) {
- 		iface = hapd->iface->interfaces->iface[i];
--		bool ap_mld = false;
- 
--#ifdef CONFIG_IEEE80211BE
--		if (hostapd_is_ml_partner(hapd, iface->bss[0]))
--			ap_mld = true;
--#endif /* CONFIG_IEEE80211BE */
--
--		if (iface == hapd->iface ||
--		    !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
-+		if (!iface || iface == hapd->iface ||
-+		    !is_6ghz_op_class(iface->conf->op_class))
- 			continue;
- 
- 		eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
--					    current_len, NULL);
-+					    current_len, NULL, false);
- 	}
- 
- 	return eid;
- }
- 
-+u8 *hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
-+			u8 *eid, size_t *current_len)
-+{
-+#ifdef CONFIG_IEEE80211BE
-+	struct hostapd_iface *iface;
-+	size_t i;
-+
-+	if (!hapd->iface || !hapd->iface->interfaces)
-+		return eid;
-+
-+	if (!hapd->conf->mld_ap)
-+		return eid;
-+
-+	/* TODO allow for FILS/Action as well */
-+	if (type != WLAN_FC_STYPE_BEACON && type != WLAN_FC_STYPE_PROBE_RESP)
-+		return eid;
-+
-+	for (i = 0; i < hapd->iface->interfaces->count; i++) {
-+		iface = hapd->iface->interfaces->iface[i];
-+
-+		if (!iface || iface == hapd->iface)
-+			continue;
-+
-+		if (hapd->iface->freq == iface->freq)
-+			continue;
- 
--u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
-+		eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
-+					    current_len, NULL, true);
-+	}
-+#endif /* CONFIG_IEEE80211BE */
-+	return eid;
-+}
-+
-+u8 *hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type, bool include_mld_params)
- {
- 	u8 *eid_start = eid;
- 	size_t current_len = 0;
- 	enum colocation_mode mode = get_colocation_mode(hapd);
--	bool ap_mld = false;
--
--#ifdef CONFIG_IEEE80211BE
--	ap_mld = !!hapd->conf->mld_ap;
--#endif /* CONFIG_IEEE80211BE */
- 
- 	switch (type) {
- 	case WLAN_FC_STYPE_BEACON:
-@@ -7697,26 +7855,34 @@ u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
- 		/* fallthrough */
- 
- 	case WLAN_FC_STYPE_PROBE_RESP:
--		if (mode == COLOCATED_LOWER_BAND || ap_mld)
--			eid = hostapd_eid_rnr_multi_iface(hapd, eid,
--							  &current_len);
-+		if (mode == COLOCATED_LOWER_BAND)
-+			eid = hostapd_eid_rnr_colocation(hapd, eid,
-+							 &current_len);
- 
- 		if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
- 		    !hapd->iconf->mbssid)
- 			eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
--						    &current_len, NULL);
-+						    &current_len, NULL,
-+						    false);
- 		break;
- 
- 	case WLAN_FC_STYPE_ACTION:
- 		if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
- 			eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
--						    &current_len, NULL);
-+						    &current_len, NULL,
-+						    false);
- 		break;
- 
- 	default:
- 		return eid_start;
- 	}
- 
-+	/* For EMA Beacons, MLD neighbor repoting is added as part of mbssid rnr */
-+	if (include_mld_params &&
-+	    (type != WLAN_FC_STYPE_BEACON ||
-+	     hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED))
-+		eid = hostapd_eid_rnr_mlo(hapd, type, eid, &current_len);
-+
- 	if (eid == eid_start + 2)
- 		return eid_start;
- 
-@@ -7815,6 +7981,11 @@ size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
- 			      size_t known_bss_len, size_t *rnr_len)
- {
- 	size_t len = 0, bss_index = 1;
-+	bool ap_mld = false;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	ap_mld = !!hapd->conf->mld_ap;
-+#endif /* CONFIG_IEEE80211BE */
- 
- 	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
- 	    (frame_type != WLAN_FC_STYPE_BEACON &&
-@@ -7847,12 +8018,12 @@ size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
- 
- 			*rnr_len += hostapd_eid_rnr_iface_len(
- 				hapd, hostapd_mbssid_get_tx_bss(hapd),
--				&rnr_cur_len, &skip_profiles);
-+				&rnr_cur_len, &skip_profiles, ap_mld);
- 		}
- 	}
- 
- 	if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && rnr_len)
--		*rnr_len += hostapd_eid_rnr_len(hapd, frame_type);
-+		*rnr_len += hostapd_eid_rnr_len(hapd, frame_type, false);
- 
- 	return len;
- }
-@@ -7978,7 +8149,11 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
- {
- 	size_t bss_index = 1, cur_len = 0;
- 	u8 elem_index = 0, *rnr_start_eid = rnr_eid;
--	bool add_rnr;
-+	bool add_rnr, ap_mld = false;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	ap_mld = !!hapd->conf->mld_ap;
-+#endif /* CONFIG_IEEE80211BE */
- 
- 	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
- 	    (frame_stype != WLAN_FC_STYPE_BEACON &&
-@@ -8023,7 +8198,7 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
- 			cur_len = 0;
- 			rnr_eid = hostapd_eid_rnr_iface(
- 				hapd, hostapd_mbssid_get_tx_bss(hapd),
--				rnr_eid, &cur_len, &skip_profiles);
-+				rnr_eid, &cur_len, &skip_profiles, ap_mld);
- 		}
- 	}
- 
-@@ -8035,8 +8210,8 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
- 		if (hapd->conf->rnr)
- 			rnr_eid = hostapd_eid_nr_db(hapd, rnr_eid, &cur_len);
- 		if (get_colocation_mode(hapd) == COLOCATED_LOWER_BAND)
--			rnr_eid = hostapd_eid_rnr_multi_iface(hapd, rnr_eid,
--							      &cur_len);
-+			rnr_eid = hostapd_eid_rnr_colocation(hapd, rnr_eid,
-+							     &cur_len);
- 	}
- 
- 	return eid;
-diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
-index 262e0ce14..078f4baf9 100644
---- a/src/ap/ieee802_11.h
-+++ b/src/ap/ieee802_11.h
-@@ -225,8 +225,9 @@ void auth_sae_process_commit(void *eloop_ctx, void *user_ctx);
- u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len);
- u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
- 		    const u8 *ext_capab_ie, size_t ext_capab_ie_len);
--size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type);
--u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type);
-+size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type, bool include_mld_params);
-+u8 *hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type,
-+		    bool include_mld_params);
- int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
- 			       int res, struct radius_sta *info);
- size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0020-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0020-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch
new file mode 100644
index 0000000..dbfad2e
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0020-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch
@@ -0,0 +1,32 @@
+From 761a5a50507b7af032ba35a08d8b3fa0a3b8991a Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 22 Sep 2022 16:08:09 +0800
+Subject: [PATCH 020/126] mtk: hostapd: Do not include HE capab IE if
+ associated sta's HE capab IE is invalid
+
+The parameter 'sta' passed to send_assoc_resp() might be NULL, so an
+NULL check is necessary before access the 'sta'.
+Only one such check was missed in this function, and this patch fixs it.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ieee802_11.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 39b1bb4c7..18d5b8f79 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -5007,7 +5007,8 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
+ #endif /* CONFIG_IEEE80211AC */
+ 
+ #ifdef CONFIG_IEEE80211AX
+-	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
++	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax && sta &&
++			sta->flags & WLAN_STA_HE) {
+ 		p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
+ 		p = hostapd_eid_he_operation(hapd, p);
+ 		p = hostapd_eid_cca(hapd, p);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0021-mtk-hostapd-Add-DFS-detection-mode.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0021-mtk-hostapd-Add-DFS-detection-mode.patch
new file mode 100644
index 0000000..b520988
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0021-mtk-hostapd-Add-DFS-detection-mode.patch
@@ -0,0 +1,136 @@
+From 83fcf8b56d7886992d6dc474ca09400177304705 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 14:55:49 +0800
+Subject: [PATCH 021/126] mtk: hostapd: Add DFS detection mode
+
+Add DFS detection mode for testing radar detection rate.
+If DFS detection mode is on, AP will not switch channels when receiving
+a radar signal.
+This detection mode also supports background chain.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/config_file.c |  4 ++++
+ hostapd/ctrl_iface.c  | 23 +++++++++++++++++++++++
+ src/ap/ap_config.h    | 13 +++++++++++++
+ src/ap/dfs.c          | 10 ++++++++++
+ 4 files changed, 50 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index e437aa981..a5fa97a35 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5520,6 +5520,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 	} else if (os_strcmp(buf, "ibf_enable") == 0) { /*ibf setting is per device*/
+ 		int val = atoi(pos);
+ 		conf->ibf_enable = !!val;
++	} else if (os_strcmp(buf, "dfs_detect_mode") == 0) { /*bypass channel switch*/
++		u8 en = strtol(pos, NULL, 10);
++
++		conf->dfs_detect_mode = en;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 3221c0fb1..ef0ade6c9 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4264,6 +4264,26 @@ hostapd_ctrl_iface_get_ibf(struct hostapd_data *hapd, char *buf,
+ }
+ 
+ 
++static int
++hostapd_ctrl_iface_set_dfs_detect_mode(struct hostapd_data *hapd, char *value,
++				       char *buf, size_t buflen)
++{
++	u8 dfs_detect_mode;
++
++	if (!value)
++		return -1;
++
++	dfs_detect_mode = strtol(value, NULL, 10);
++	if (dfs_detect_mode > DFS_DETECT_MODE_MAX) {
++		wpa_printf(MSG_ERROR, "Invalid value for dfs detect mode");
++		return -1;
++	}
++	hapd->iconf->dfs_detect_mode = dfs_detect_mode;
++
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -4885,6 +4905,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
+ 	} else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
+ 		reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
++	} else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
++		reply_len = hostapd_ctrl_iface_set_dfs_detect_mode(hapd, buf + 16,
++								   reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 3f07147db..f7027473e 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1341,6 +1341,7 @@ struct hostapd_config {
+ 	int *edcca_threshold;
+ 	u8 three_wire_enable;
+ 	u8 ibf_enable;
++	u8 dfs_detect_mode;
+ };
+ 
+ enum three_wire_mode {
+@@ -1355,6 +1356,18 @@ enum three_wire_mode {
+ 		NUM_THREE_WIRE_MODE - 1
+ };
+ 
++enum dfs_mode {
++	DFS_DETECT_MODE_DISABLE,
++	DFS_DETECT_MODE_AP_ENABLE,
++	DFS_DETECT_MODE_BACKGROUND_ENABLE,
++	DFS_DETECT_MODE_ALL_ENABLE,
++
++	/* keep last */
++	NUM_DFS_DETECT_MODE,
++	DFS_DETECT_MODE_MAX =
++		NUM_DFS_DETECT_MODE - 1
++};
++
+ enum edcca_mode {
+ 	EDCCA_MODE_FORCE_DISABLE = 0,
+ 	EDCCA_MODE_AUTO = 1,
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index fe044297b..49dc4d424 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1347,6 +1347,11 @@ hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
+ 		   __func__, iface->radar_background.cac_started ? "yes" : "no",
+ 		   hostapd_csa_in_progress(iface) ? "yes" : "no");
+ 
++	/* Skip channel switch when background dfs detect mode is on */
++	if (iface->conf->dfs_detect_mode == DFS_DETECT_MODE_BACKGROUND_ENABLE ||
++	    iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
++		return 0;
++
+ 	/* Check if CSA in progress */
+ 	if (hostapd_csa_in_progress(iface))
+ 		return 0;
+@@ -1395,6 +1400,11 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
+ 		   __func__, iface->cac_started ? "yes" : "no",
+ 		   hostapd_csa_in_progress(iface) ? "yes" : "no");
+ 
++	/* Skip channel switch when dfs detect mode is on */
++	if (iface->conf->dfs_detect_mode == DFS_DETECT_MODE_AP_ENABLE ||
++	    iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
++		return 0;
++
+ 	/* Check if CSA in progress */
+ 	if (hostapd_csa_in_progress(iface))
+ 		return 0;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0021-tests-MLO-add-basic-cohosted-MLDs-functionality-test.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0021-tests-MLO-add-basic-cohosted-MLDs-functionality-test.patch
deleted file mode 100644
index a4eb061..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0021-tests-MLO-add-basic-cohosted-MLDs-functionality-test.patch
+++ /dev/null
@@ -1,271 +0,0 @@
-From c43241d046e8a6ae75549c23d470b94f16c74ca7 Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:51 +0530
-Subject: [PATCH 021/104] tests: MLO: add basic cohosted MLDs functionality
- testing
-
-Add test case to test basic cohosted MLDs functionality. Add helper
-functions to create the configuration file, start hostapd instance.
-
-Client connectivty test case will be added via a subsequent change.
-
-eht_mld_cohosted_discovery: 2 co-hosted MLDs without non-MLD RNR. Basic
-bring up and beacon, MLD RNR, scan validation.
-
-eht_mld_cohosted_discovery_with_rnr: Same like eht_mld_cohosted_discovery
-but additionally non-MLD RNR (rnr=1) is also enabled. Validate the non-MLD
-RNR as well.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- tests/hwsim/test_eht.py | 230 ++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 230 insertions(+)
-
-diff --git a/tests/hwsim/test_eht.py b/tests/hwsim/test_eht.py
-index a012fe4e7..732406219 100644
---- a/tests/hwsim/test_eht.py
-+++ b/tests/hwsim/test_eht.py
-@@ -15,6 +15,7 @@ from tshark import run_tshark
- from test_gas import hs20_ap_params
- from test_dpp import check_dpp_capab, wait_auth_success
- from test_rrm import build_beacon_request, run_req_beacon, BeaconReport
-+import os, subprocess, time, tempfile
- 
- def eht_verify_wifi_version(dev):
-     status = dev.get_status()
-@@ -1823,3 +1824,232 @@ def test_eht_mlo_csa(dev, apdev):
-             traffic_test(wpas, hapd0)
- 
-             #TODO: CSA on non-first link
-+
-+def create_base_conf_file(iface, channel, prefix='hostapd-', hw_mode='g',
-+                          op_class=None):
-+    # Create configuration file and add phy characteristics
-+    fd, fname = tempfile.mkstemp(dir='/tmp',
-+                                 prefix=prefix + iface + "-chan-" + str(channel) + "-")
-+    f = os.fdopen(fd, 'w')
-+
-+    f.write("driver=nl80211\n")
-+    f.write("hw_mode=" + str(hw_mode) + "\n")
-+    f.write("ieee80211n=1\n")
-+    if hw_mode == 'a' and \
-+       (op_class is None or \
-+        op_class not in [131, 132, 133, 134, 135, 136, 137]):
-+        f.write("ieee80211ac=1\n")
-+    f.write("ieee80211ax=1\n")
-+    f.write("ieee80211be=1\n")
-+    f.write("channel=" + str(channel) + "\n")
-+
-+    return f, fname
-+
-+def append_bss_conf_to_file(f, ifname, params, first=False):
-+    # Add BSS specific characteristics
-+    config = "bss"
-+
-+    if first:
-+        config = "interface"
-+
-+    f.write("\n" + config + "=%s\n" % ifname)
-+
-+    for k, v in list(params.items()):
-+        f.write("{}={}\n".format(k,v))
-+
-+    f.write("mld_ap=1\n")
-+
-+def dump_config(fname):
-+    with open(fname, 'r') as f:
-+        cfg = f.read()
-+        logger.debug("hostapd config: " + str(fname) + "\n" + cfg)
-+
-+def get_config(iface, count, ssid, passphrase, channel, bssid_regex,
-+               rnr=False, debug=False):
-+    f, fname = create_base_conf_file(iface, channel=channel)
-+    hapds = []
-+
-+    for i in range(count):
-+        if i == 0:
-+            ifname = iface
-+        else:
-+            ifname = iface + "-" + str(i)
-+
-+        set_ssid = ssid + str(i)
-+        set_passphrase = passphrase + str(i)
-+        params = hostapd.wpa2_params(ssid=set_ssid, passphrase=set_passphrase,
-+                                     wpa_key_mgmt="SAE", ieee80211w="2")
-+        params['sae_pwe'] = "2"
-+        params['group_mgmt_cipher'] = "AES-128-CMAC"
-+        params['beacon_prot'] = "1"
-+        params["ctrl_interface"] = "/var/run/hostapd/chan_" + str(channel)
-+        params["bssid"] = bssid_regex % (i + 1)
-+
-+        if rnr:
-+            params["rnr"]="1"
-+
-+        append_bss_conf_to_file(f, ifname, params, first=(i == 0))
-+
-+        hapds.append([ifname, params["ctrl_interface"], i])
-+
-+    f.close()
-+
-+    if debug:
-+        dump_config(fname)
-+
-+    return fname, hapds
-+
-+def start_ap(prefix, configs):
-+    pid = prefix + ".hostapd.pid"
-+    configs = configs.split()
-+
-+    cmd = ['../../hostapd/hostapd', '-ddKtB', '-P', pid, '-f',
-+           prefix + ".hostapd-log"]
-+
-+    cmd = cmd + configs
-+
-+    logger.info("Starting APs")
-+    res = subprocess.check_call(cmd)
-+    if res != 0:
-+        raise Exception("Could not start hostapd: %s" % str(res))
-+
-+    # Wait for hostapd to complete initialization and daemonize.
-+    time.sleep(2)
-+
-+    if not os.path.exists(pid):
-+        raise Exception("hostapd did not create PID file.")
-+
-+def get_mld_devs(hapd_iface, count, prefix, rnr=False):
-+    fname1, hapds1 = get_config(hapd_iface, count=count, ssid="mld-",
-+                                passphrase="qwertyuiop-", channel=1,
-+                                bssid_regex="02:00:00:00:07:%02x",
-+                                rnr=rnr, debug=True)
-+    fname2, hapds2 = get_config(hapd_iface, count=count, ssid="mld-",
-+                                passphrase="qwertyuiop-", channel=6,
-+                                bssid_regex="02:00:00:00:08:%02x",
-+                                rnr=rnr, debug=True)
-+
-+    start_ap(prefix, fname1 + " " + fname2)
-+
-+    hapd_mld1_link0 = hostapd.Hostapd(ifname=hapds1[0][0], ctrl=hapds1[0][1],
-+                                      bssidx=hapds1[0][2])
-+    hapd_mld1_link1 = hostapd.Hostapd(ifname=hapds2[0][0], ctrl=hapds2[0][1],
-+                                      bssidx=hapds2[0][2])
-+
-+    hapd_mld2_link0 = hostapd.Hostapd(ifname=hapds1[1][0], ctrl=hapds1[1][1],
-+                                      bssidx=hapds1[1][2])
-+    hapd_mld2_link1 = hostapd.Hostapd(ifname=hapds2[1][0], ctrl=hapds2[1][1],
-+                                      bssidx=hapds2[1][2])
-+
-+    if not hapd_mld1_link0.ping():
-+        raise Exception("Could not ping hostapd")
-+
-+    if not hapd_mld1_link1.ping():
-+        raise Exception("Could not ping hostapd")
-+
-+    if not hapd_mld2_link0.ping():
-+        raise Exception("Could not ping hostapd")
-+
-+    if not hapd_mld2_link1.ping():
-+        raise Exception("Could not ping hostapd")
-+
-+    os.remove(fname1)
-+    os.remove(fname2)
-+
-+    return [hapd_mld1_link0, hapd_mld1_link1, hapd_mld2_link0, hapd_mld2_link1]
-+
-+def stop_mld_devs(hapds, pid):
-+    pid = pid + ".hostapd.pid"
-+
-+    if "OK" not in hapds[0].request("TERMINATE"):
-+        raise Exception("Failed to terminate hostapd process")
-+
-+    ev = hapds[0].wait_event(["CTRL-EVENT-TERMINATING"], timeout=15)
-+    if ev is None:
-+        raise Exception("CTRL-EVENT-TERMINATING not seen")
-+
-+    time.sleep(0.5)
-+
-+    if os.path.exists(pid):
-+        raise Exception("PID file exits after process termination")
-+
-+def eht_parse_rnr(bss, rnr=False, exp_bssid=None):
-+        partner_rnr_pattern = re.compile(".*ap_info.*, mld ID=0, link ID=",
-+                                         re.MULTILINE)
-+        ml_pattern = re.compile(".*multi-link:.*, MLD addr=.*", re.MULTILINE)
-+
-+        if partner_rnr_pattern.search(bss) is None:
-+            raise Exception("RNR element not found for first link of first MLD")
-+
-+        if ml_pattern.search(bss) is None:
-+            raise Exception("ML element not found for first link of first MLD")
-+
-+        if not rnr:
-+            return
-+
-+        coloc_rnr_pattern = re.compile(".*ap_info.*, mld ID=255, link ID=..",
-+                                       re.MULTILINE)
-+
-+        if coloc_rnr_pattern.search(bss) is None:
-+            raise Exception("RNR element not found for co-located BSS")
-+
-+        line = coloc_rnr_pattern.search(bss).group()
-+        if line.count('bssid') > 1:
-+            raise Exception("More than one BSS found for co-located RNR")
-+
-+        # Get the BSSID carried in the RNR
-+        index = line.rindex('bssid')
-+        bssid = line[index+len('bssid')+1:].split(',')[0]
-+
-+        # Get the MLD ID carried in the RNR
-+        index = line.rindex('link ID')
-+        link_id = line[index+len('link ID')+1:].split(',')[0]
-+
-+        if link_id != "15":
-+            raise Exception("Unexpected link ID for co-located BSS which is not own partner")
-+
-+        if bssid != exp_bssid:
-+            raise Exception("Unexpected BSSID for co-located BSS")
-+
-+def eht_mld_cohosted_discovery(dev, apdev, params, rnr=False):
-+    with HWSimRadio(use_mlo=True, n_channels=2) as (hapd_radio, hapd_iface), \
-+        HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface):
-+
-+        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
-+        wpas.interface_add(wpas_iface)
-+
-+        hapds = get_mld_devs(hapd_iface=hapd_iface, count=2, prefix=params['prefix'],
-+                             rnr=rnr)
-+
-+        # Only scan link 0
-+        res = wpas.request("SCAN freq=2412")
-+        if "FAIL" in res:
-+            raise Exception("Failed to start scan")
-+
-+        ev = wpas.wait_event(["CTRL-EVENT-SCAN-STARTED"])
-+        if ev is None:
-+            raise Exception("Scan did not start")
-+
-+        ev = wpas.wait_event(["CTRL-EVENT-SCAN-RESULTS"])
-+        if ev is None:
-+            raise Exception("Scan did not complete")
-+
-+        logger.info("Scan done")
-+
-+        bss = wpas.request("BSS " + hapds[0].own_addr())
-+        logger.info("BSS 0_0: " + str(bss))
-+        eht_parse_rnr(bss, rnr, hapds[2].own_addr())
-+
-+        bss = wpas.request("BSS " + hapds[2].own_addr())
-+        logger.info("BSS 1_0: " + str(bss))
-+        eht_parse_rnr(bss, rnr, hapds[0].own_addr())
-+
-+        stop_mld_devs(hapds, params['prefix'])
-+
-+def test_eht_mld_cohosted_discovery(dev, apdev, params):
-+    """EHT 2 AP MLDs discovery"""
-+    eht_mld_cohosted_discovery(dev, apdev, params)
-+
-+def test_eht_mld_cohosted_discovery_with_rnr(dev, apdev, params):
-+    """EHT 2 AP MLDs discovery (with co-location RNR)"""
-+    eht_mld_cohosted_discovery(dev, apdev, params, rnr=True)
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0022-mtk-hostapd-Add-DFS-offchan-channel-switch.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0022-mtk-hostapd-Add-DFS-offchan-channel-switch.patch
new file mode 100644
index 0000000..6e13b02
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0022-mtk-hostapd-Add-DFS-offchan-channel-switch.patch
@@ -0,0 +1,192 @@
+From b74aa64e3cbacf537ac3e7a96f3295ebe0979c0c Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 14:56:55 +0800
+Subject: [PATCH 022/126] mtk: hostapd: Add DFS offchan channel switch
+
+Add DFS background chain channel switch command for testing purpose.
+This feature is implemented via hostapd_cli command.
+Command format:
+hostapd_cli -i <interface> raw SET_OFFCHAN_CTRL chan=<dfs_channel>
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 72 ++++++++++++++++++++++++++++++++++++++++++++
+ src/ap/dfs.c         | 25 ++++++---------
+ src/ap/dfs.h         | 15 +++++++++
+ 3 files changed, 96 insertions(+), 16 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index ef0ade6c9..32d4997b2 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4284,6 +4284,76 @@ hostapd_ctrl_iface_set_dfs_detect_mode(struct hostapd_data *hapd, char *value,
+ }
+ 
+ 
++static int
++hostapd_ctrl_iface_set_offchan_ctrl(struct hostapd_data *hapd, char *cmd,
++				    char *buf, size_t buflen)
++{
++	struct hostapd_iface *iface = hapd->iface;
++	char *pos, *param;
++	enum hostapd_hw_mode hw_mode;
++	bool chan_found = false;
++	int i, num_available_chandefs, channel, chan_width, sec = 0;
++	int sec_chan_idx_80p80 = -1;
++	u8 oper_centr_freq_seg0_idx, oper_centr_freq_seg1_idx;
++	struct hostapd_channel_data *chan;
++	enum dfs_channel_type type = DFS_NO_CAC_YET;
++
++	param = os_strchr(cmd, ' ');
++	if (!param)
++		return -1;
++	*param++ = '\0';
++
++	pos = os_strstr(param, "chan=");
++	if (pos)
++		channel = strtol(pos + 5, NULL, 10);
++	else
++		return -1;
++
++	num_available_chandefs = dfs_find_channel(iface, NULL, 0, 0, type);
++	for (i = 0; i < num_available_chandefs; i++) {
++		dfs_find_channel(iface, &chan, 0, i, type);
++		if (chan->chan == channel) {
++			chan_found = true;
++			break;
++		}
++	}
++
++	if (!chan_found)
++		return -1;
++
++	if (iface->conf->secondary_channel)
++		sec = 1;
++
++	dfs_adjust_center_freq(iface, chan,
++			       sec,
++			       sec_chan_idx_80p80,
++			       &oper_centr_freq_seg0_idx,
++			       &oper_centr_freq_seg1_idx);
++
++	if (hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
++				  chan->freq, chan->chan,
++				  iface->conf->ieee80211n,
++				  iface->conf->ieee80211ac,
++				  iface->conf->ieee80211ax,
++				  iface->conf->ieee80211be,
++				  sec, hostapd_get_oper_chwidth(iface->conf),
++				  oper_centr_freq_seg0_idx,
++				  oper_centr_freq_seg1_idx, true)) {
++		wpa_printf(MSG_ERROR, "DFS failed to start CAC offchannel");
++		iface->radar_background.channel = -1;
++		return -1;
++	}
++
++	iface->radar_background.channel = chan->chan;
++	iface->radar_background.freq = chan->freq;
++	iface->radar_background.secondary_channel = sec;
++	iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
++	iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
++
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -4908,6 +4978,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 	} else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
+ 		reply_len = hostapd_ctrl_iface_set_dfs_detect_mode(hapd, buf + 16,
+ 								   reply, reply_size);
++	} else if (os_strncmp(buf, "SET_OFFCHAN_CTRL", 16) == 0) {
++		reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 49dc4d424..42cce2dce 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -20,13 +20,6 @@
+ #include "dfs.h"
+ #include "crypto/crypto.h"
+ 
+-
+-enum dfs_channel_type {
+-	DFS_ANY_CHANNEL,
+-	DFS_AVAILABLE, /* non-radar or radar-available */
+-	DFS_NO_CAC_YET, /* radar-not-yet-available */
+-};
+-
+ static struct hostapd_channel_data *
+ dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
+ 			u8 *oper_centr_freq_seg0_idx,
+@@ -239,9 +232,9 @@ static int is_in_chanlist(struct hostapd_iface *iface,
+  *  - hapd->vht/he_oper_centr_freq_seg0_idx
+  *  - hapd->vht/he_oper_centr_freq_seg1_idx
+  */
+-static int dfs_find_channel(struct hostapd_iface *iface,
+-			    struct hostapd_channel_data **ret_chan,
+-			    int idx, enum dfs_channel_type type)
++int dfs_find_channel(struct hostapd_iface *iface,
++		     struct hostapd_channel_data **ret_chan,
++		     int idx, enum dfs_channel_type type)
+ {
+ 	struct hostapd_hw_modes *mode;
+ 	struct hostapd_channel_data *chan;
+@@ -301,12 +294,12 @@ static int dfs_find_channel(struct hostapd_iface *iface,
+ }
+ 
+ 
+-static void dfs_adjust_center_freq(struct hostapd_iface *iface,
+-				   struct hostapd_channel_data *chan,
+-				   int secondary_channel,
+-				   int sec_chan_idx_80p80,
+-				   u8 *oper_centr_freq_seg0_idx,
+-				   u8 *oper_centr_freq_seg1_idx)
++void dfs_adjust_center_freq(struct hostapd_iface *iface,
++			    struct hostapd_channel_data *chan,
++			    int secondary_channel,
++			    int sec_chan_idx_80p80,
++			    u8 *oper_centr_freq_seg0_idx,
++			    u8 *oper_centr_freq_seg1_idx)
+ {
+ 	if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax)
+ 		return;
+diff --git a/src/ap/dfs.h b/src/ap/dfs.h
+index 606c1b393..c2556d2d9 100644
+--- a/src/ap/dfs.h
++++ b/src/ap/dfs.h
+@@ -9,6 +9,12 @@
+ #ifndef DFS_H
+ #define DFS_H
+ 
++enum dfs_channel_type {
++	DFS_ANY_CHANNEL,
++	DFS_AVAILABLE, /* non-radar or radar-available */
++	DFS_NO_CAC_YET, /* radar-not-yet-available */
++};
++
+ int hostapd_handle_dfs(struct hostapd_iface *iface);
+ 
+ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+@@ -32,5 +38,14 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
+ int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
+ 			   int center_freq);
++int dfs_find_channel(struct hostapd_iface *iface,
++		     struct hostapd_channel_data **ret_chan,
++		     int idx, enum dfs_channel_type type);
++void dfs_adjust_center_freq(struct hostapd_iface *iface,
++			    struct hostapd_channel_data *chan,
++			    int secondary_channel,
++			    int sec_chan_idx_80p80,
++			    u8 *oper_centr_freq_seg0_idx,
++			    u8 *oper_centr_freq_seg1_idx);
+ 
+ #endif /* DFS_H */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0022-tests-MLO-add-cohosted-MLDs-connectivity-testing.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0022-tests-MLO-add-cohosted-MLDs-connectivity-testing.patch
deleted file mode 100644
index af3c390..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0022-tests-MLO-add-cohosted-MLDs-connectivity-testing.patch
+++ /dev/null
@@ -1,67 +0,0 @@
-From 29a075f5ea644abdfb9bd93f79b05c72bb9fb78c Mon Sep 17 00:00:00 2001
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Thu, 28 Mar 2024 23:46:52 +0530
-Subject: [PATCH 022/104] tests: MLO: add cohosted MLDs connectivity testing
-
-Add test case 'eht_mld_cohosted_connectivity' which creates two 2 link AP
-MLDs and  connect 2 links MLD client to each one of them and test data
-traffic.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
----
- tests/hwsim/test_eht.py | 42 +++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 42 insertions(+)
-
-diff --git a/tests/hwsim/test_eht.py b/tests/hwsim/test_eht.py
-index 732406219..f09d31878 100644
---- a/tests/hwsim/test_eht.py
-+++ b/tests/hwsim/test_eht.py
-@@ -2053,3 +2053,45 @@ def test_eht_mld_cohosted_discovery(dev, apdev, params):
- def test_eht_mld_cohosted_discovery_with_rnr(dev, apdev, params):
-     """EHT 2 AP MLDs discovery (with co-location RNR)"""
-     eht_mld_cohosted_discovery(dev, apdev, params, rnr=True)
-+
-+def test_eht_mld_cohosted_connectivity(dev, apdev, params):
-+    """EHT 2 AP MLDs with 2 MLD clients connection"""
-+    with HWSimRadio(use_mlo=True, n_channels=2) as (hapd_radio, hapd_iface), \
-+        HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface), \
-+        HWSimRadio(use_mlo=True) as (wpas_radio1, wpas_iface1):
-+
-+        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
-+        wpas.interface_add(wpas_iface)
-+
-+        wpas1 = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
-+        wpas1.interface_add(wpas_iface1)
-+
-+        hapds = get_mld_devs(hapd_iface=hapd_iface, count=2, prefix=params['prefix'],
-+                             rnr=False)
-+
-+        passphrase = "qwertyuiop-"
-+        ssid = "mld-"
-+
-+        # Connect one client to first AP MLD and verify traffic on both links
-+        wpas.set("sae_pwe", "1")
-+        wpas.connect(ssid+"0", sae_password=passphrase+"0", scan_freq="2412",
-+                     key_mgmt="SAE", ieee80211w="2")
-+
-+        eht_verify_status(wpas, hapds[0], 2412, 20, is_ht=True, mld=True,
-+                          valid_links=3, active_links=3)
-+        eht_verify_wifi_version(wpas)
-+
-+        traffic_test(wpas, hapds[0])
-+        traffic_test(wpas, hapds[1])
-+
-+        # Connect another client to second AP MLD and verify traffic on both links
-+        wpas1.set("sae_pwe", "1")
-+        wpas1.connect(ssid+"1", sae_password=passphrase+"1", scan_freq="2437",
-+                      key_mgmt="SAE", ieee80211w="2")
-+
-+        eht_verify_status(wpas1, hapds[3], 2437, 20, is_ht=True, mld=True,
-+                          valid_links=3, active_links=3)
-+        eht_verify_wifi_version(wpas1)
-+
-+        traffic_test(wpas1, hapds[3])
-+        traffic_test(wpas1, hapds[2])
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0023-backport-hostapd-afcd-add-AFC-daemon-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0023-backport-hostapd-afcd-add-AFC-daemon-support.patch
deleted file mode 100644
index 9c4f7a4..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0023-backport-hostapd-afcd-add-AFC-daemon-support.patch
+++ /dev/null
@@ -1,372 +0,0 @@
-From 84123bd3df810acd8d463a31d519005cfd0cc8d0 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Wed, 20 Mar 2024 07:13:01 +0800
-Subject: [PATCH 023/104] backport: hostapd: afcd: add AFC daemon support
-
-Introduce Automated Frequency Coordination Daemon (AFCD) support
-for UNII-5 and UNII-7 6GHz bands.
-AFCD will be used by hostapd AFC client in order to forward the AFC
-request to the AFC coordinator and decouple AFC connection management
-from hostapd.
-AFC is required for Standard Power Devices (SPDs) to determine a lists
-of channels and EIRP/PSD powers that are available in the 6GHz spectrum.
-AFCD is tested with AFC DUT Test Harness [0].
-
-[0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main
-
-Tested-by: Allen Ye <allen.ye@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
----
- afc/.gitignore |   1 +
- afc/Makefile   |  31 ++++++
- afc/afcd.c     | 292 +++++++++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 324 insertions(+)
- create mode 100644 afc/.gitignore
- create mode 100644 afc/Makefile
- create mode 100644 afc/afcd.c
-
-diff --git a/afc/.gitignore b/afc/.gitignore
-new file mode 100644
-index 000000000..8d8cca905
---- /dev/null
-+++ b/afc/.gitignore
-@@ -0,0 +1 @@
-+afcd
-diff --git a/afc/Makefile b/afc/Makefile
-new file mode 100644
-index 000000000..a83bd01db
---- /dev/null
-+++ b/afc/Makefile
-@@ -0,0 +1,31 @@
-+ALL=afcd
-+
-+include ../src/build.rules
-+
-+CFLAGS += -I../src/utils
-+CFLAGS += -I../src
-+
-+OBJS=afcd.o
-+OBJS += ../src/utils/common.o
-+OBJS += ../src/utils/wpa_debug.o
-+OBJS += ../src/utils/wpabuf.o
-+
-+ifndef CONFIG_OS
-+ifdef CONFIG_NATIVE_WINDOWS
-+CONFIG_OS=win32
-+else
-+CONFIG_OS=unix
-+endif
-+endif
-+OBJS += ../src/utils/os_$(CONFIG_OS).o
-+
-+LIBS += -lcurl
-+
-+_OBJS_VAR := OBJS
-+include ../src/objs.mk
-+afcd: $(OBJS)
-+	$(Q)$(LDO) $(LDFLAGS) -o afcd $(OBJS) $(LIBS)
-+	@$(E) "  LD " $@
-+
-+clean: common-clean
-+	rm -f core *~
-diff --git a/afc/afcd.c b/afc/afcd.c
-new file mode 100644
-index 000000000..f502846c5
---- /dev/null
-+++ b/afc/afcd.c
-@@ -0,0 +1,292 @@
-+/*
-+ * Automated Frequency Coordination Daemon
-+ * Copyright (c) 2024, Lorenzo Bianconi <lorenzo@kernel.org>
-+ *
-+ * This software may be distributed under the terms of the BSD license.
-+ * See README for more details.
-+ */
-+
-+#include <curl/curl.h>
-+#include <sys/un.h>
-+#include <sys/stat.h>
-+
-+#include "utils/includes.h"
-+#include "utils/common.h"
-+
-+#define CURL_TIMEOUT	60
-+#define AFCD_SOCK	"afcd.sock"
-+
-+struct curl_ctx {
-+	char *buf;
-+	size_t buf_len;
-+};
-+
-+static volatile bool exiting;
-+
-+static char *path = "/var/run";
-+static char *bearer_token;
-+static char *url;
-+static int port = 443;
-+
-+
-+static size_t afcd_curl_cb_write(void *ptr, size_t size, size_t nmemb,
-+				 void *userdata)
-+{
-+	struct curl_ctx *ctx = userdata;
-+	char *buf;
-+
-+	buf = os_realloc(ctx->buf, ctx->buf_len + size * nmemb + 1);
-+	if (!buf)
-+		return 0;
-+
-+	ctx->buf = buf;
-+	os_memcpy(buf + ctx->buf_len, ptr, size * nmemb);
-+	buf[ctx->buf_len + size * nmemb] = '\0';
-+	ctx->buf_len += size * nmemb;
-+
-+	return size * nmemb;
-+}
-+
-+
-+static int afcd_send_request(struct curl_ctx *ctx, unsigned char *request)
-+{
-+	struct curl_slist *headers = NULL;
-+	CURL *curl;
-+	int ret;
-+
-+	wpa_printf(MSG_DEBUG, "Sending AFC request to %s", url);
-+
-+	curl_global_init(CURL_GLOBAL_ALL);
-+	curl = curl_easy_init();
-+	if (!curl)
-+		return -ENOMEM;
-+
-+	headers  = curl_slist_append(headers, "Accept: application/json");
-+	headers  = curl_slist_append(headers,
-+				     "Content-Type: application/json");
-+	headers  = curl_slist_append(headers, "charset: utf-8");
-+
-+	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
-+	curl_easy_setopt(curl, CURLOPT_URL, url);
-+	curl_easy_setopt(curl, CURLOPT_PORT, port);
-+	curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
-+	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
-+			 afcd_curl_cb_write);
-+	curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx);
-+	curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcrp/0.1");
-+	curl_easy_setopt(curl, CURLOPT_TIMEOUT, CURL_TIMEOUT);
-+	curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
-+	if (bearer_token)
-+		curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, bearer_token);
-+	curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BEARER);
-+	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
-+	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L);
-+	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request);
-+	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L);
-+
-+	ret = curl_easy_perform(curl);
-+	if (ret != CURLE_OK)
-+		wpa_printf(MSG_ERROR, "curl_easy_perform failed: %s",
-+			   curl_easy_strerror(ret));
-+
-+	curl_easy_cleanup(curl);
-+	curl_global_cleanup();
-+
-+	return ret == CURLE_OK ? 0 : -EINVAL;
-+}
-+
-+
-+static void handle_term(int sig)
-+{
-+	wpa_printf(MSG_ERROR, "Received signal %d", sig);
-+	exiting = true;
-+}
-+
-+
-+static void usage(void)
-+{
-+	wpa_printf(MSG_ERROR,
-+		   "%s:\n"
-+		   "afcd -u<url> [-p<port>][-t<token>][-D<unix-sock dir>][-P<PID file>][-dB]",
-+		   __func__);
-+}
-+
-+
-+#define BUFSIZE		8192
-+static int afcd_server_run(void)
-+{
-+	size_t len = os_strlen(path) + 1 + os_strlen(AFCD_SOCK);
-+	struct sockaddr_un addr = {
-+		.sun_family = AF_UNIX,
-+#ifdef __FreeBSD__
-+		.sun_len = sizeof(addr),
-+#endif /* __FreeBSD__ */
-+	};
-+	int sockfd, ret = 0;
-+	char *fname = NULL;
-+	unsigned char *buf;
-+	fd_set read_set;
-+
-+	if (len >= sizeof(addr.sun_path))
-+		return -EINVAL;
-+
-+	if (mkdir(path, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST)
-+		return -EINVAL;
-+
-+	buf = os_malloc(BUFSIZE);
-+	if (!buf)
-+		return -ENOMEM;
-+
-+	fname = os_malloc(len + 1);
-+	if (!fname) {
-+		ret = -ENOMEM;
-+		goto free_buf;
-+	}
-+
-+	os_snprintf(fname, len + 1, "%s/%s", path, AFCD_SOCK);
-+	fname[len] = '\0';
-+	os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
-+
-+	sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
-+	if (sockfd < 0) {
-+		wpa_printf(MSG_ERROR, "Failed creating socket");
-+		ret = -errno;
-+		goto unlink;
-+	}
-+
-+	if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-+		wpa_printf(MSG_ERROR, "Failed to bind socket");
-+		ret = -errno;
-+		goto close;
-+	}
-+
-+	if (listen(sockfd, 10) < 0) {
-+		wpa_printf(MSG_ERROR, "Failed to listen on socket");
-+		ret = -errno;
-+		goto close;
-+	}
-+
-+	FD_ZERO(&read_set);
-+	while (!exiting) {
-+		socklen_t addr_len = sizeof(addr);
-+		struct sockaddr_in6 c_addr;
-+		struct timeval timeout = {
-+			.tv_sec = 1,
-+		};
-+		struct curl_ctx ctx = {};
-+		int fd;
-+
-+		FD_SET(sockfd, &read_set);
-+		if (select(sockfd + 1, &read_set, NULL, NULL, &timeout) < 0) {
-+			if (errno != EINTR) {
-+				wpa_printf(MSG_ERROR,
-+					   "Select failed on socket");
-+				ret = -errno;
-+				break;
-+			}
-+			continue;
-+		}
-+
-+		if (!FD_ISSET(sockfd, &read_set))
-+			continue;
-+
-+		fd = accept(sockfd, (struct sockaddr *)&c_addr,
-+			    &addr_len);
-+		if (fd < 0) {
-+			if (errno != EINTR) {
-+				wpa_printf(MSG_ERROR,
-+					   "Failed accepting connections");
-+				ret = -errno;
-+				break;
-+			}
-+			continue;
-+		}
-+
-+		os_memset(buf, 0, BUFSIZE);
-+		if (recv(fd, buf, BUFSIZE, 0) <= 0) {
-+			close(fd);
-+			continue;
-+		}
-+
-+		wpa_printf(MSG_DEBUG, "Received request: %s", buf);
-+		if (!afcd_send_request(&ctx, buf)) {
-+			wpa_printf(MSG_DEBUG, "Received reply: %s", ctx.buf);
-+			send(fd, ctx.buf, ctx.buf_len, MSG_NOSIGNAL);
-+			free(ctx.buf);
-+		}
-+		close(fd);
-+	}
-+close:
-+	close(sockfd);
-+unlink:
-+	unlink(fname);
-+	os_free(fname);
-+free_buf:
-+	os_free(buf);
-+
-+	return ret;
-+}
-+
-+
-+int main(int argc, char **argv)
-+{
-+	bool daemonize = false;
-+	char *pid_file = NULL;
-+
-+	if (os_program_init())
-+		return -1;
-+
-+	for (;;) {
-+		int c = getopt(argc, argv, "u:p:t:D:P:hdB");
-+
-+		if (c < 0)
-+			break;
-+
-+		switch (c) {
-+		case 'h':
-+			usage();
-+			return 0;
-+		case 'B':
-+			daemonize = true;
-+			break;
-+		case 'D':
-+			path = optarg;
-+			break;
-+		case 'P':
-+			os_free(pid_file);
-+			pid_file = os_rel2abs_path(optarg);
-+			break;
-+		case 'u':
-+			url = optarg;
-+			break;
-+		case 'p':
-+			port = atoi(optarg);
-+			break;
-+		case 'd':
-+			if (wpa_debug_level > 0)
-+				wpa_debug_level--;
-+			break;
-+		case 't':
-+			bearer_token = optarg;
-+			break;
-+		default:
-+			usage();
-+			return -EINVAL;
-+		}
-+	}
-+
-+	if (!url) {
-+		usage();
-+		return -EINVAL;
-+	}
-+
-+	if (daemonize && os_daemonize(pid_file)) {
-+		wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
-+		return -EINVAL;
-+	}
-+
-+	signal(SIGTERM, handle_term);
-+	signal(SIGINT, handle_term);
-+
-+	return afcd_server_run();
-+}
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0023-mtk-hostapd-Add-amsdu-set-get-ctrl.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0023-mtk-hostapd-Add-amsdu-set-get-ctrl.patch
new file mode 100644
index 0000000..46e779e
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0023-mtk-hostapd-Add-amsdu-set-get-ctrl.patch
@@ -0,0 +1,400 @@
+From 7759dfbe3c937a1945c6df05a109853125dc6653 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Fri, 16 Dec 2022 03:57:11 +0800
+Subject: [PATCH 023/126] mtk: hostapd: Add amsdu set get ctrl
+
+---
+ hostapd/config_file.c             |   9 +++
+ hostapd/ctrl_iface.c              |  26 +++++++
+ hostapd/hostapd_cli.c             |   9 +++
+ src/ap/ap_config.c                |   1 +
+ src/ap/ap_config.h                |   1 +
+ src/ap/ap_drv_ops.c               |  14 ++++
+ src/ap/ap_drv_ops.h               |   2 +
+ src/ap/hostapd.c                  |   2 +
+ src/common/mtk_vendor.h           |  17 ++++-
+ src/drivers/driver.h              |   9 +++
+ src/drivers/driver_nl80211.c      | 114 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   3 +
+ 13 files changed, 207 insertions(+), 1 deletion(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index a5fa97a35..1f57b882f 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5524,6 +5524,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		u8 en = strtol(pos, NULL, 10);
+ 
+ 		conf->dfs_detect_mode = en;
++	} else if (os_strcmp(buf, "amsdu") == 0) {
++		int val = atoi(pos);
++		if (val < 0 || val > 1) {
++			wpa_printf(MSG_ERROR,
++					 "Line %d: invalid amsdu value",
++					 line);
++			return 1;
++		}
++		conf->amsdu = val;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 32d4997b2..7e5733278 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4354,6 +4354,30 @@ hostapd_ctrl_iface_set_offchan_ctrl(struct hostapd_data *hapd, char *cmd,
+ }
+ 
+ 
++static int
++hostapd_ctrl_iface_get_amsdu(struct hostapd_data *hapd, char *buf,
++					 size_t buflen)
++{
++	u8 amsdu;
++	int ret;
++	char *pos, *end;
++
++	pos = buf;
++	end = buf + buflen;
++
++	if (hostapd_drv_amsdu_dump(hapd, &amsdu) == 0) {
++		hapd->iconf->amsdu = amsdu;
++		ret = os_snprintf(pos, end - pos, "[hostapd_cli] AMSDU: %u\n",
++					hapd->iconf->amsdu);
++	}
++
++	if (os_snprintf_error(end - pos, ret))
++		return 0;
++
++	return ret;
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -4980,6 +5004,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 								   reply, reply_size);
+ 	} else if (os_strncmp(buf, "SET_OFFCHAN_CTRL", 16) == 0) {
+ 		reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
++	} else if (os_strncmp(buf, "GET_AMSDU", 9) == 0) {
++		reply_len = hostapd_ctrl_iface_get_amsdu(hapd, reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index ccbf863f6..d934bb0d4 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1698,6 +1698,13 @@ static int hostapd_cli_cmd_get_ibf(struct wpa_ctrl *ctrl, int argc,
+ }
+ 
+ 
++static int hostapd_cli_cmd_get_amsdu(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "GET_AMSDU", 0, NULL, NULL);
++}
++
++
+ struct hostapd_cli_cmd {
+ 	const char *cmd;
+ 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -1930,6 +1937,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+           "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
+ 	{ "get_ibf", hostapd_cli_cmd_get_ibf, NULL,
+ 	  " = show iBF state (enabled/disabled)"},
++	{ "get_amsdu", hostapd_cli_cmd_get_amsdu, NULL,
++		" = show AMSDU state"},
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 08b55d0eb..5dd7f7b2b 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -310,6 +310,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 	conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
+ 	conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
+ 	conf->ibf_enable = IBF_DEFAULT_ENABLE;
++	conf->amsdu = 1;
+ 
+ 	hostapd_set_and_check_bw320_offset(conf, 0);
+ 
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index f7027473e..d0a692750 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1342,6 +1342,7 @@ struct hostapd_config {
+ 	u8 three_wire_enable;
+ 	u8 ibf_enable;
+ 	u8 dfs_detect_mode;
++	u8 amsdu;
+ };
+ 
+ enum three_wire_mode {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 5172f06b9..2685c6f8a 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1336,4 +1336,18 @@ int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable)
+ 	if (!hapd->driver || !hapd->driver->ibf_dump)
+ 		return 0;
+ 	return hapd->driver->ibf_dump(hapd->drv_priv, ibf_enable);
++}
++
++int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->driver->amsdu_ctrl)
++		return 0;
++	return hapd->driver->amsdu_ctrl(hapd->drv_priv, hapd->iconf->amsdu);
++}
++
++int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu)
++{
++	if (!hapd->driver || !hapd->driver->amsdu_dump)
++		return 0;
++	return hapd->driver->amsdu_dump(hapd->drv_priv, amsdu);
+ }
+\ No newline at end of file
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 3633e2ed5..68b26595f 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -161,6 +161,8 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
++int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 7503718c8..6ab3e6043 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2768,6 +2768,8 @@ dfs_offload:
+ 		goto fail;
+ 	if (hostapd_drv_ibf_ctrl(hapd) < 0)
+ 		goto fail;
++	if (hostapd_drv_amsdu_ctrl(hapd) < 0)
++		goto fail;
+ 
+ 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ 		   iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 9811f266e..7b4d7c11a 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -170,7 +170,6 @@ enum mtk_vendor_attr_wireless_ctrl {
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
+-	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
+ 
+@@ -180,6 +179,22 @@ enum mtk_vendor_attr_wireless_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_wireless_dump {
++	MTK_VENDOR_ATTR_WIRELESS_DUMP_UNSPEC,
++
++	MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP,
++	MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX =
++		NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
++};
++
++static const struct nla_policy
++wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
++	[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
++};
++
+ enum mtk_vendor_attr_rfeature_ctrl {
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
+ 
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 97be9e8fb..348cf9c88 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5290,6 +5290,15 @@ struct wpa_driver_ops {
+ 	 *
+ 	 */
+ 	int (*ibf_dump)(void *priv, u8 *ibf_enable);
++
++	/**
++	 * amsdu_ctrl - enable/disable amsdu
++	 * amsdu_dump - get current amsdu status
++	 * @priv: Private driver interface data
++	 *
++	 */
++	int (*amsdu_ctrl)(void *priv, u8 amsdu);
++	int (*amsdu_dump)(void *priv, u8 *amsdu);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index c5c0d1b49..c2a0f1adb 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14518,6 +14518,118 @@ fail:
+ 	return -ENOBUFS;
+ }
+ 
++static int nl80211_enable_amsdu(void *priv, u8 amsdu)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_wireless_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting ap wireless control");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU, amsdu);
++
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to set amsdu. ret=%d (%s)", ret, strerror(-ret));
++	}
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
++static int dump_amsdu_handler(struct nl_msg *msg, void *arg)
++{
++	u8 *amsdu = (u8 *) arg;
++	struct nlattr *tb[NL80211_ATTR_MAX + 1];
++	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX + 1];
++	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++	struct nlattr *nl_vend, *attr_amsdu;
++
++	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++			genlmsg_attrlen(gnlh, 0), NULL);
++
++	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++	if (!nl_vend)
++		return NL_SKIP;
++
++	nla_parse(tb_vendor, MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX,
++			nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++	attr_amsdu = tb_vendor[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU];
++	if (!attr_amsdu ){
++		wpa_printf(MSG_ERROR, "nl80211: cannot find vendor attributes");
++		return NL_SKIP;
++	}
++
++	*amsdu = nla_get_u8(attr_amsdu);
++
++	return NL_SKIP;
++}
++
++static int
++nl80211_dump_amsdu(void *priv, u8 *amsdu)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_wireless_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++				 "nl80211: Driver does not support ap_wireless control");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	nla_nest_end(msg, data);
++
++	ret = send_and_recv_resp(drv, msg, dump_amsdu_handler, amsdu);
++
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to dump amsdu. ret=%d (%s)", ret, strerror(-ret));
++	}
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+ 	.desc = "Linux nl80211/cfg80211",
+@@ -14687,4 +14799,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.three_wire_ctrl = nl80211_enable_three_wire,
+ 	.ibf_ctrl = nl80211_ibf_enable,
+ 	.ibf_dump = nl80211_ibf_dump,
++	.amsdu_ctrl = nl80211_enable_amsdu,
++	.amsdu_dump = nl80211_dump_amsdu,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 1432eeda8..5aa813e26 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -204,6 +204,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_mu_vendor_cmd_avail:1;
+ 	unsigned int mtk_3wire_vendor_cmd_avail:1;
+ 	unsigned int mtk_ibf_vendor_cmd_avail:1;
++	unsigned int mtk_wireless_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 670588dd2..4e2afb7f2 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1153,6 +1153,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL:
+ 					drv->mtk_ibf_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL:
++					drv->mtk_wireless_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0024-backport-hostapd-export-hostapd_is_usable_chans-util.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0024-backport-hostapd-export-hostapd_is_usable_chans-util.patch
deleted file mode 100644
index aac2145..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0024-backport-hostapd-export-hostapd_is_usable_chans-util.patch
+++ /dev/null
@@ -1,55 +0,0 @@
-From 524c84524695034b8d531d70b546d5479d59641f Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Wed, 20 Mar 2024 07:17:31 +0800
-Subject: [PATCH 024/104] backport: hostapd: export hostapd_is_usable_chans
- utility routine
-
-This is a preliminary patch to introduce AFC support.
-
-Tested-by: Allen Ye <allen.ye@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
----
- src/ap/hw_features.c | 2 +-
- src/ap/hw_features.h | 6 ++++++
- 2 files changed, 7 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
-index fd401d78a..e652d7504 100644
---- a/src/ap/hw_features.c
-+++ b/src/ap/hw_features.c
-@@ -995,7 +995,7 @@ static bool hostapd_is_usable_punct_bitmap(struct hostapd_iface *iface)
-  * 0 = not usable
-  * -1 = not currently usable due to 6 GHz NO-IR
-  */
--static int hostapd_is_usable_chans(struct hostapd_iface *iface)
-+int hostapd_is_usable_chans(struct hostapd_iface *iface)
- {
- 	int secondary_freq;
- 	struct hostapd_channel_data *pri_chan;
-diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
-index c682c6d20..eeffb1abd 100644
---- a/src/ap/hw_features.h
-+++ b/src/ap/hw_features.h
-@@ -30,6 +30,7 @@ void hostapd_stop_setup_timers(struct hostapd_iface *iface);
- int hostapd_hw_skip_mode(struct hostapd_iface *iface,
- 			 struct hostapd_hw_modes *mode);
- int hostapd_determine_mode(struct hostapd_iface *iface);
-+int hostapd_is_usable_chans(struct hostapd_iface *iface);
- #else /* NEED_AP_MLME */
- static inline void
- hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
-@@ -103,6 +104,11 @@ static inline int hostapd_determine_mode(struct hostapd_iface *iface)
- 	return 0;
- }
- 
-+static inline int hostapd_is_usable_chans(struct hostapd_iface *iface)
-+{
-+	return 1;
-+}
-+
- #endif /* NEED_AP_MLME */
- 
- #endif /* HW_FEATURES_H */
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0024-mtk-hostapd-Add-he_ldpc-configuration.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0024-mtk-hostapd-Add-he_ldpc-configuration.patch
new file mode 100644
index 0000000..43ba4db
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0024-mtk-hostapd-Add-he_ldpc-configuration.patch
@@ -0,0 +1,102 @@
+From 305f9748549189ce802544c0923446ca6a53ae1b Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Thu, 12 Jan 2023 15:18:19 +0800
+Subject: [PATCH 024/126] mtk: hostapd: Add he_ldpc configuration
+
+---
+ hostapd/config_file.c        | 2 ++
+ hostapd/hostapd.conf         | 5 +++++
+ src/ap/ap_config.c           | 1 +
+ src/ap/ap_config.h           | 1 +
+ src/ap/ieee802_11_he.c       | 7 +++++++
+ src/common/ieee802_11_defs.h | 3 +++
+ 6 files changed, 19 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 1f57b882f..dc05738db 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4000,6 +4000,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		conf->he_phy_capab.he_su_beamformee = atoi(pos);
+ 	} else if (os_strcmp(buf, "he_mu_beamformer") == 0) {
+ 		conf->he_phy_capab.he_mu_beamformer = atoi(pos);
++	} else if (os_strcmp(buf, "he_ldpc") == 0) {
++		conf->he_phy_capab.he_ldpc = atoi(pos);
+ 	} else if (os_strcmp(buf, "he_bss_color") == 0) {
+ 		conf->he_op.he_bss_color = atoi(pos) & 0x3f;
+ 		conf->he_op.he_bss_color_disabled = 0;
+diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
+index 56442c69d..118754800 100644
+--- a/hostapd/hostapd.conf
++++ b/hostapd/hostapd.conf
+@@ -858,6 +858,11 @@ wmm_ac_vo_acm=0
+ # 1 = supported
+ #he_mu_beamformer=1
+ 
++#he_ldpc: HE LDPC support
++# 0 = not supported
++# 1 = supported (default)
++#he_ldpc=1
++
+ # he_bss_color: BSS color (1-63)
+ #he_bss_color=1
+ 
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 5dd7f7b2b..3a12de3cd 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -275,6 +275,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ #endif /* CONFIG_ACS */
+ 
+ #ifdef CONFIG_IEEE80211AX
++	conf->he_phy_capab.he_ldpc = 1;
+ 	conf->he_op.he_rts_threshold = HE_OPERATION_RTS_THRESHOLD_MASK >>
+ 		HE_OPERATION_RTS_THRESHOLD_OFFSET;
+ 	/* Set default basic MCS/NSS set to single stream MCS 0-7 */
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index d0a692750..f90f4d554 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1035,6 +1035,7 @@ struct hostapd_bss_config {
+  * struct he_phy_capabilities_info - HE PHY capabilities
+  */
+ struct he_phy_capabilities_info {
++	bool he_ldpc;
+ 	bool he_su_beamformer;
+ 	bool he_su_beamformee;
+ 	bool he_mu_beamformer;
+diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
+index a2deda6c4..3c6ee72fe 100644
+--- a/src/ap/ieee802_11_he.c
++++ b/src/ap/ieee802_11_he.c
+@@ -139,6 +139,13 @@ u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
+ 		os_memcpy(&cap->optional[mcs_nss_size],
+ 			  mode->he_capab[opmode].ppet,  ppet_size);
+ 
++	if (hapd->iface->conf->he_phy_capab.he_ldpc)
++		cap->he_phy_capab_info[HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX] |=
++			HE_PHYCAP_LDPC_CODING_IN_PAYLOAD;
++	else
++		cap->he_phy_capab_info[HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX] &=
++			~HE_PHYCAP_LDPC_CODING_IN_PAYLOAD;
++
+ 	if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
+ 		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
+ 			HE_PHYCAP_SU_BEAMFORMER_CAPAB;
+diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
+index 269b1cf97..c380d0c7e 100644
+--- a/src/common/ieee802_11_defs.h
++++ b/src/common/ieee802_11_defs.h
+@@ -2461,6 +2461,9 @@ struct ieee80211_spatial_reuse {
+ #define HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G	((u8) BIT(3))
+ #define HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G	((u8) BIT(4))
+ 
++#define HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX	1
++#define HE_PHYCAP_LDPC_CODING_IN_PAYLOAD	((u8) BIT(5))
++
+ #define HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX	3
+ #define HE_PHYCAP_SU_BEAMFORMER_CAPAB		((u8) BIT(7))
+ #define HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX	4
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0025-backport-hostapd-ap-add-AFC-client-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0025-backport-hostapd-ap-add-AFC-client-support.patch
deleted file mode 100644
index 357ca69..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0025-backport-hostapd-ap-add-AFC-client-support.patch
+++ /dev/null
@@ -1,1542 +0,0 @@
-From c635af2f526c7dc7a862e5c6fed5f2015d8e85b6 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Wed, 20 Mar 2024 07:18:37 +0800
-Subject: [PATCH 025/104] backport: hostapd: ap: add AFC client support
-
-Introduce Automated Frequency Coordination (AFC) support for UNII-5 and
-UNII-7 6GHz bands.
-AFC client will connect to AFCD providing AP related parameter for AFC
-coordinator (e.g. geolocation, supported frequencies, ..).
-AFC is required for Standard Power Devices (SPDs) to determine a lists
-of channels and EIRP/PSD powers that are available in the 6GHz spectrum.
-AFC hostapd client is tested with AFC DUT Test Harness [0].
-
-[0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main
-
-Tested-by: Allen Ye <allen.ye@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
----
- hostapd/Makefile      |   6 +
- hostapd/config_file.c | 262 ++++++++++++
- hostapd/defconfig     |   3 +
- hostapd/hostapd.conf  |  42 ++
- src/ap/afc.c          | 918 ++++++++++++++++++++++++++++++++++++++++++
- src/ap/ap_config.c    |  16 +
- src/ap/ap_config.h    |  47 +++
- src/ap/hostapd.c      |  60 +++
- src/ap/hostapd.h      |  29 ++
- src/ap/hw_features.c  |   2 +
- 10 files changed, 1385 insertions(+)
- create mode 100644 src/ap/afc.c
-
-diff --git a/hostapd/Makefile b/hostapd/Makefile
-index b3cb68673..405e05e5f 100644
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -103,6 +103,12 @@ CFLAGS += -DCONFIG_TAXONOMY
- OBJS += ../src/ap/taxonomy.o
- endif
- 
-+ifdef CONFIG_AFC
-+CFLAGS += -DCONFIG_AFC
-+OBJS += ../src/ap/afc.o
-+LIBS += -ljson-c
-+endif
-+
- ifdef CONFIG_MODULE_TESTS
- CFLAGS += -DCONFIG_MODULE_TESTS
- OBJS += hapd_module_tests.o
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 56b2df3ae..261905368 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -2436,6 +2436,191 @@ static int get_u16(const char *pos, int line, u16 *ret_val)
- #endif /* CONFIG_IEEE80211BE */
- 
- 
-+#ifdef CONFIG_AFC
-+static int hostapd_afc_parse_cert_ids(struct hostapd_config *conf, char *pos)
-+{
-+	struct cert_id *c = NULL;
-+	int i, count = 0;
-+
-+	while (pos && pos[0]) {
-+		char *p;
-+
-+		c = os_realloc_array(c, count + 1, sizeof(*c));
-+		if (!c)
-+			return -ENOMEM;
-+
-+		i = count;
-+		count++;
-+
-+		p = os_strchr(pos, ':');
-+		if (!p)
-+			goto error;
-+
-+		*p++ = '\0';
-+		if (!p || !p[0])
-+			goto error;
-+
-+		c[i].rulset = os_malloc(os_strlen(pos) + 1);
-+		if (!c[i].rulset)
-+			goto error;
-+
-+		os_strlcpy(c[i].rulset, pos, os_strlen(pos) + 1);
-+		pos = p;
-+		p = os_strchr(pos, ',');
-+		if (p)
-+			*p++ = '\0';
-+
-+		c[i].id = os_malloc(os_strlen(pos) + 1);
-+		if (!c[i].id)
-+			goto error;
-+
-+		os_strlcpy(c[i].id, pos, os_strlen(pos) + 1);
-+		pos = p;
-+	}
-+
-+	conf->afc.n_cert_ids = count;
-+	conf->afc.cert_ids = c;
-+
-+	return 0;
-+
-+error:
-+	for (i = 0; i < count; i++) {
-+		os_free(c[i].rulset);
-+		os_free(c[i].id);
-+	}
-+	os_free(c);
-+
-+	return -ENOMEM;
-+}
-+
-+
-+static int hostapd_afc_parse_position_data(struct afc_linear_polygon **data,
-+					   unsigned int *n_linear_polygon_data,
-+					   char *pos)
-+{
-+	struct afc_linear_polygon *d = NULL;
-+	int i, count = 0;
-+
-+	while (pos && pos[0]) {
-+		char *p, *end;
-+
-+		d = os_realloc_array(d, count + 1, sizeof(*d));
-+		if (!d)
-+			return -ENOMEM;
-+
-+		i = count;
-+		count++;
-+
-+		p = os_strchr(pos, ':');
-+		if (!p)
-+			goto error;
-+
-+		*p++ = '\0';
-+		if (!p || !p[0])
-+			goto error;
-+
-+		d[i].longitude = strtod(pos, &end);
-+		if (*end)
-+			goto error;
-+
-+		pos = p;
-+		p = os_strchr(pos, ',');
-+		if (p)
-+			*p++ = '\0';
-+
-+		d[i].latitude = strtod(pos, &end);
-+		if (*end)
-+			goto error;
-+
-+		pos = p;
-+	}
-+
-+	*n_linear_polygon_data = count;
-+	*data = d;
-+
-+	return 0;
-+
-+error:
-+	os_free(d);
-+	return -ENOMEM;
-+}
-+
-+
-+static int hostapd_afc_parse_freq_range(struct hostapd_config *conf, char *pos)
-+{
-+	struct afc_freq_range *f = NULL;
-+	int i, count = 0;
-+
-+	while (pos && pos[0]) {
-+		char *p;
-+
-+		f = os_realloc_array(f, count + 1, sizeof(*f));
-+		if (!f)
-+			return -ENOMEM;
-+
-+		i = count;
-+		count++;
-+
-+		p = os_strchr(pos, ':');
-+		if (!p)
-+			goto error;
-+
-+		*p++ = '\0';
-+		if (!p || !p[0])
-+			goto error;
-+
-+		f[i].low_freq = atoi(pos);
-+		pos = p;
-+		p = os_strchr(pos, ',');
-+		if (p)
-+			*p++ = '\0';
-+
-+		f[i].high_freq = atoi(pos);
-+		pos = p;
-+	}
-+
-+	conf->afc.n_freq_range = count;
-+	conf->afc.freq_range = f;
-+
-+	return 0;
-+
-+error:
-+	os_free(f);
-+	return -ENOMEM;
-+}
-+
-+
-+static int hostapd_afc_parse_op_class(struct hostapd_config *conf, char *pos)
-+{
-+	unsigned int *oc = NULL;
-+	int i, count = 0;
-+
-+	while (pos && pos[0]) {
-+		char *p;
-+
-+		oc = os_realloc_array(oc, count + 1, sizeof(*oc));
-+		if (!oc)
-+			return -ENOMEM;
-+
-+		i = count;
-+		count++;
-+
-+		p = os_strchr(pos, ',');
-+		if (p)
-+			*p++ = '\0';
-+
-+		oc[i] = atoi(pos);
-+		pos = p;
-+	}
-+
-+	conf->afc.n_op_class = count;
-+	conf->afc.op_class = oc;
-+
-+	return 0;
-+}
-+#endif /* CONFIG_AFC */
-+
-+
- static int hostapd_config_fill(struct hostapd_config *conf,
- 			       struct hostapd_bss_config *bss,
- 			       const char *buf, char *pos, int line)
-@@ -3862,6 +4047,83 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 			return 1;
- 		}
- 		bss->unsol_bcast_probe_resp_interval = val;
-+#ifdef CONFIG_AFC
-+	} else if (os_strcmp(buf, "afcd_sock") == 0) {
-+		conf->afc.socket = os_malloc(os_strlen(pos) + 1);
-+		if (!conf->afc.socket)
-+			return 1;
-+
-+		os_strlcpy(conf->afc.socket, pos, os_strlen(pos) + 1);
-+	} else if (os_strcmp(buf, "afc_request_version") == 0) {
-+		conf->afc.request.version = os_malloc(os_strlen(pos) + 1);
-+		if (!conf->afc.request.version)
-+			return 1;
-+
-+		os_strlcpy(conf->afc.request.version, pos, os_strlen(pos) + 1);
-+	} else if (os_strcmp(buf, "afc_request_id") == 0) {
-+		conf->afc.request.id = os_malloc(os_strlen(pos) + 1);
-+		if (!conf->afc.request.id)
-+			return 1;
-+
-+		os_strlcpy(conf->afc.request.id, pos, os_strlen(pos) + 1);
-+	} else if (os_strcmp(buf, "afc_serial_number") == 0) {
-+		conf->afc.request.sn = os_malloc(os_strlen(pos) + 1);
-+		if (!conf->afc.request.sn)
-+			return 1;
-+
-+		os_strlcpy(conf->afc.request.sn, pos, os_strlen(pos) + 1);
-+	} else if (os_strcmp(buf, "afc_cert_ids") == 0) {
-+		if (hostapd_afc_parse_cert_ids(conf, pos))
-+			return 1;
-+	} else if (os_strcmp(buf, "afc_location_type") == 0) {
-+		conf->afc.location.type = atoi(pos);
-+		if (conf->afc.location.type != ELLIPSE &&
-+		    conf->afc.location.type != LINEAR_POLYGON &&
-+		    conf->afc.location.type != RADIAL_POLYGON)
-+			return 1;
-+	} else if (os_strcmp(buf, "afc_linear_polygon") == 0) {
-+		if (hostapd_afc_parse_position_data(
-+			&conf->afc.location.linear_polygon_data,
-+			&conf->afc.location.n_linear_polygon_data,
-+			pos))
-+			return 1;
-+	} else if (os_strcmp(buf, "afc_radial_polygon") == 0) {
-+		if (hostapd_afc_parse_position_data(
-+			(struct afc_linear_polygon **)
-+			&conf->afc.location.radial_polygon_data,
-+			&conf->afc.location.n_radial_polygon_data,
-+			pos))
-+			return 1;
-+	} else if (os_strcmp(buf, "afc_major_axis") == 0) {
-+		conf->afc.location.major_axis = atoi(pos);
-+	} else if (os_strcmp(buf, "afc_minor_axis") == 0) {
-+		conf->afc.location.minor_axis = atoi(pos);
-+	} else if (os_strcmp(buf, "afc_orientation") == 0) {
-+		conf->afc.location.orientation = atoi(pos);
-+	} else if (os_strcmp(buf, "afc_height") == 0) {
-+		char *end;
-+
-+		conf->afc.location.height = strtod(pos, &end);
-+		if (*end)
-+			return 1;
-+	} else if (os_strcmp(buf, "afc_height_type") == 0) {
-+		conf->afc.location.height_type = os_malloc(os_strlen(pos) + 1);
-+		if (!conf->afc.location.height_type)
-+			return 1;
-+
-+		os_strlcpy(conf->afc.location.height_type, pos,
-+			   os_strlen(pos) + 1);
-+	} else if (os_strcmp(buf, "afc_vertical_tolerance") == 0) {
-+		conf->afc.location.vertical_tolerance = atoi(pos);
-+	} else if (os_strcmp(buf, "afc_min_power") == 0) {
-+		conf->afc.min_power = atoi(pos);
-+	} else if (os_strcmp(buf, "afc_freq_range") == 0) {
-+		if (hostapd_afc_parse_freq_range(conf, pos))
-+			return 1;
-+	} else if (os_strcmp(buf, "afc_op_class") == 0) {
-+		if (hostapd_afc_parse_op_class(conf, pos))
-+			return 1;
-+#endif /* CONFIG_AFC */
- 	} else if (os_strcmp(buf, "mbssid") == 0) {
- 		int mbssid = atoi(pos);
- 		if (mbssid < 0 || mbssid > ENHANCED_MBSSID_ENABLED) {
-diff --git a/hostapd/defconfig b/hostapd/defconfig
-index 550db697b..66bf894eb 100644
---- a/hostapd/defconfig
-+++ b/hostapd/defconfig
-@@ -425,3 +425,6 @@ CONFIG_DPP2=y
- 
- # Wi-Fi Aware unsynchronized service discovery (NAN USD)
- #CONFIG_NAN_USD=y
-+
-+# Enable Automated Frequency Coordination for 6GHz outdoor
-+#CONFIG_AFC=y
-diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
-index d80abcac0..0d10998af 100644
---- a/hostapd/hostapd.conf
-+++ b/hostapd/hostapd.conf
-@@ -1005,6 +1005,48 @@ wmm_ac_vo_acm=0
- # Valid range: 0..20 TUs; default is 0 (disabled)
- #unsol_bcast_probe_resp_interval=0
- 
-+##### Automated Frequency Coordination for 6GHz UNII-5 and UNII-7 bands #######
-+
-+# AFC daemon connection socket
-+#afcd_sock=/var/run/afcd.sock
-+
-+# AFC request identification parameters
-+#afc_request_version=1.1
-+#afc_request_id=11235813
-+#afc_serial_number=abcdefg
-+#afc_cert_ids=US_47_CFR_PART_15_SUBPART_E:CID000
-+#
-+# AFC location type:
-+# 0 = ellipse
-+# 1 = linear polygon
-+# 2 = radial polygon
-+#afc_location_type=0
-+#
-+# AFC ellipse or linear polygon coordinations
-+#afc_linear_polygon=-122.984157:37.425056
-+#
-+# AFC radial polygon coordinations
-+#afc_radial_polygon=118.8:92.76,76.44:87.456,98.56:123.33
-+#
-+# AFC ellipse major/minor axis and orientation
-+#afc_major_axis=100
-+#afc_minor_axis=50
-+#afc_orientation=70
-+#
-+# AFC device elevation parameters
-+#afc_height=3.0
-+#afc_height_type=AGL
-+#afc_vertical_tolerance=7
-+#
-+# AFC minimum desired TX power (dbm)
-+#afc_min_power=24
-+#
-+# AFC request frequency ranges
-+#afc_freq_range=5925:6425,6525:6875
-+#
-+# AFC request operation classes
-+#afc_op_class=131,132,133,134,136
-+
- ##### IEEE 802.11be related configuration #####################################
- 
- #ieee80211be: Whether IEEE 802.11be (EHT) is enabled
-diff --git a/src/ap/afc.c b/src/ap/afc.c
-new file mode 100644
-index 000000000..c75d5d582
---- /dev/null
-+++ b/src/ap/afc.c
-@@ -0,0 +1,918 @@
-+/*
-+ * Automated Frequency Coordination
-+ * Copyright (c) 2024, Lorenzo Bianconi <lorenzo@kernel.org>
-+ *
-+ * This software may be distributed under the terms of the BSD license.
-+ * See README for more details.
-+ */
-+
-+#include <json-c/json.h>
-+#include <sys/un.h>
-+#include <time.h>
-+
-+#include "utils/includes.h"
-+#include "utils/common.h"
-+#include "utils/eloop.h"
-+#include "hostapd.h"
-+#include "acs.h"
-+#include "hw_features.h"
-+
-+#define HOSTAPD_AFC_RETRY_TIMEOUT	180
-+#define HOSTAPD_AFC_TIMEOUT		86400 /* 24h */
-+#define HOSTAPD_AFC_BUFSIZE		4096
-+
-+static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx);
-+
-+
-+static struct json_object *
-+hostapd_afc_build_location_request(struct hostapd_iface *iface)
-+{
-+	struct json_object *location_obj, *center_obj, *ellipse_obj;
-+	struct json_object *elevation_obj, *str_obj;
-+	struct hostapd_config *iconf = iface->conf;
-+	bool is_ap_indoor = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type);
-+
-+	location_obj = json_object_new_object();
-+	if (!location_obj)
-+		return NULL;
-+
-+	if (iconf->afc.location.type != LINEAR_POLYGON) {
-+		struct afc_linear_polygon *lp =
-+			&iconf->afc.location.linear_polygon_data[0];
-+
-+		ellipse_obj = json_object_new_object();
-+		if (!ellipse_obj)
-+			goto error;
-+
-+		center_obj = json_object_new_object();
-+		if (!center_obj)
-+			goto error;
-+
-+		json_object_object_add(ellipse_obj, "center", center_obj);
-+
-+		str_obj = json_object_new_double(lp->longitude);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(center_obj, "longitude", str_obj);
-+		str_obj = json_object_new_double(lp->latitude);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(center_obj, "latitude", str_obj);
-+
-+	}
-+
-+	switch (iconf->afc.location.type) {
-+	case LINEAR_POLYGON: {
-+		struct json_object *outer_boundary_obj;
-+		int i;
-+
-+		outer_boundary_obj = json_object_new_object();
-+		if (!outer_boundary_obj)
-+			goto error;
-+
-+		json_object_object_add(location_obj, "linearPolygon",
-+				       outer_boundary_obj);
-+		ellipse_obj = json_object_new_array();
-+		if (!ellipse_obj)
-+			goto error;
-+
-+		json_object_object_add(outer_boundary_obj, "outerBoundary",
-+				       ellipse_obj);
-+		for (i = 0;
-+		     i < iconf->afc.location.n_linear_polygon_data; i++) {
-+			struct afc_linear_polygon *lp =
-+				&iconf->afc.location.linear_polygon_data[i];
-+
-+			center_obj = json_object_new_object();
-+			if (!center_obj)
-+				goto error;
-+
-+			json_object_array_add(ellipse_obj, center_obj);
-+			str_obj = json_object_new_double(lp->longitude);
-+			if (!str_obj)
-+				goto error;
-+
-+			json_object_object_add(center_obj, "longitude",
-+					       str_obj);
-+			str_obj = json_object_new_double(lp->latitude);
-+			if (!str_obj)
-+				goto error;
-+
-+			json_object_object_add(center_obj, "latitude",
-+					       str_obj);
-+		}
-+		break;
-+	}
-+	case RADIAL_POLYGON: {
-+		struct json_object *outer_boundary_obj;
-+		int i;
-+
-+		json_object_object_add(location_obj, "radialPolygon",
-+				       ellipse_obj);
-+
-+		outer_boundary_obj = json_object_new_array();
-+		if (!outer_boundary_obj)
-+			goto error;
-+
-+		json_object_object_add(ellipse_obj, "outerBoundary",
-+				       outer_boundary_obj);
-+		for (i = 0;
-+		     i < iconf->afc.location.n_radial_polygon_data; i++) {
-+			struct afc_radial_polygon *rp =
-+				&iconf->afc.location.radial_polygon_data[i];
-+			struct json_object *angle_obj;
-+
-+			angle_obj = json_object_new_object();
-+			if (!angle_obj)
-+				goto error;
-+
-+			json_object_array_add(outer_boundary_obj, angle_obj);
-+
-+			str_obj = json_object_new_double(rp->angle);
-+			if (!str_obj)
-+				goto error;
-+
-+			json_object_object_add(angle_obj, "angle", str_obj);
-+			str_obj = json_object_new_double(rp->length);
-+			if (!str_obj)
-+				goto error;
-+
-+			json_object_object_add(angle_obj, "length", str_obj);
-+		}
-+		break;
-+	}
-+	case ELLIPSE:
-+	default:
-+		json_object_object_add(location_obj, "ellipse", ellipse_obj);
-+
-+		str_obj = json_object_new_int(iconf->afc.location.major_axis);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(ellipse_obj, "majorAxis", str_obj);
-+		str_obj = json_object_new_int(iconf->afc.location.minor_axis);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(ellipse_obj, "minorAxis", str_obj);
-+		str_obj = json_object_new_int(iconf->afc.location.orientation);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(ellipse_obj, "orientation", str_obj);
-+		break;
-+	}
-+
-+	elevation_obj = json_object_new_object();
-+	if (!elevation_obj)
-+		goto error;
-+
-+	json_object_object_add(location_obj, "elevation",
-+			       elevation_obj);
-+	str_obj = json_object_new_double(iconf->afc.location.height);
-+	if (!str_obj)
-+		goto error;
-+
-+	json_object_object_add(elevation_obj, "height", str_obj);
-+	str_obj = json_object_new_string(iconf->afc.location.height_type);
-+	if (!str_obj)
-+		goto error;
-+
-+	json_object_object_add(elevation_obj, "heightType", str_obj);
-+	str_obj = json_object_new_int(iconf->afc.location.vertical_tolerance);
-+	if (!str_obj)
-+		goto error;
-+
-+	json_object_object_add(elevation_obj, "verticalUncertainty",
-+			       str_obj);
-+	str_obj = json_object_new_int(is_ap_indoor);
-+	if (!str_obj)
-+		goto error;
-+
-+	json_object_object_add(location_obj, "indoorDeployment", str_obj);
-+
-+	return location_obj;
-+
-+error:
-+	json_object_put(location_obj);
-+	return NULL;
-+}
-+
-+
-+static struct json_object * hostapd_afc_get_opclass_chan_list(u8 op_class)
-+{
-+	struct json_object *chan_list_obj, *str_obj;
-+	const struct oper_class_map *oper_class;
-+	int chan_offset, chan;
-+
-+	oper_class = get_oper_class(NULL, op_class);
-+	if (!oper_class)
-+		return NULL;
-+
-+	chan_list_obj = json_object_new_array();
-+	if (!chan_list_obj)
-+		return NULL;
-+
-+	switch (op_class) {
-+	case 132: /*  40MHz */
-+		chan_offset = 2;
-+		break;
-+	case 133: /*  80MHz */
-+		chan_offset = 6;
-+		break;
-+	case 134: /* 160MHz */
-+		chan_offset = 14;
-+		break;
-+	default:
-+		chan_offset = 0;
-+		break;
-+	}
-+
-+	for (chan = oper_class->min_chan; chan <= oper_class->max_chan;
-+	     chan += oper_class->inc) {
-+		str_obj = json_object_new_int(chan + chan_offset);
-+		if (!str_obj) {
-+			json_object_put(chan_list_obj);
-+			return NULL;
-+		}
-+		json_object_array_add(chan_list_obj, str_obj);
-+	}
-+
-+	return chan_list_obj;
-+}
-+
-+
-+static struct json_object *
-+hostapd_afc_build_req_chan_list(struct hostapd_iface *iface)
-+{
-+	struct json_object *op_class_list_obj, *str_obj;
-+	struct hostapd_config *iconf = iface->conf;
-+	int i;
-+
-+	op_class_list_obj = json_object_new_array();
-+	if (!op_class_list_obj)
-+		return NULL;
-+
-+	for (i = 0; i < iconf->afc.n_op_class; i++) {
-+		struct json_object *op_class_obj, *chan_list_obj;
-+		u8 op_class = iconf->afc.op_class[i];
-+
-+		if (!is_6ghz_op_class(op_class))
-+			continue;
-+
-+		op_class_obj = json_object_new_object();
-+		if (!op_class_obj)
-+			goto error;
-+
-+		json_object_array_add(op_class_list_obj, op_class_obj);
-+		str_obj = json_object_new_int(op_class);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(op_class_obj, "globalOperatingClass",
-+				       str_obj);
-+
-+		chan_list_obj = hostapd_afc_get_opclass_chan_list(op_class);
-+		if (!chan_list_obj)
-+			goto error;
-+
-+		json_object_object_add(op_class_obj, "channelCfi",
-+				       chan_list_obj);
-+	}
-+
-+	return op_class_list_obj;
-+
-+error:
-+	json_object_put(op_class_list_obj);
-+	return NULL;
-+}
-+
-+
-+static struct json_object *
-+hostapd_afc_build_request(struct hostapd_iface *iface)
-+{
-+	struct json_object *l1_obj, *l2_obj, *la1_obj, *la2_obj;
-+	struct json_object *s2_obj, *str_obj, *location_obj;
-+	struct hostapd_config *iconf = iface->conf;
-+	struct json_object *op_class_list_obj;
-+	int i;
-+
-+	l1_obj = json_object_new_object();
-+	if (!l1_obj)
-+		return NULL;
-+
-+	if (iconf->afc.request.version) {
-+		str_obj = json_object_new_string(iconf->afc.request.version);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(l1_obj, "version", str_obj);
-+	}
-+
-+	la1_obj = json_object_new_array();
-+	if (!la1_obj)
-+		goto error;
-+
-+	json_object_object_add(l1_obj, "availableSpectrumInquiryRequests",
-+			       la1_obj);
-+	l2_obj = json_object_new_object();
-+	if (!l2_obj)
-+		goto error;
-+
-+	json_object_array_add(la1_obj, l2_obj);
-+	if (iconf->afc.request.id) {
-+		str_obj = json_object_new_string(iconf->afc.request.id);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(l2_obj, "requestId", str_obj);
-+	}
-+
-+	s2_obj = json_object_new_object();
-+	if (!s2_obj)
-+		goto error;
-+
-+	json_object_object_add(l2_obj, "deviceDescriptor", s2_obj);
-+	if (iconf->afc.request.sn) {
-+		str_obj = json_object_new_string(iconf->afc.request.sn);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(s2_obj, "serialNumber", str_obj);
-+	}
-+
-+	la2_obj = json_object_new_array();
-+	if (!la2_obj)
-+		goto error;
-+
-+	json_object_object_add(s2_obj, "certificationId", la2_obj);
-+	for (i = 0; i < iconf->afc.n_cert_ids; i++) {
-+		struct json_object *obj;
-+
-+		obj = json_object_new_object();
-+		if (!obj)
-+			goto error;
-+
-+		json_object_array_add(la2_obj, obj);
-+		str_obj =
-+			json_object_new_string(iconf->afc.cert_ids[i].rulset);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(obj, "rulesetId", str_obj);
-+		str_obj = json_object_new_string(iconf->afc.cert_ids[i].id);
-+		if (!str_obj)
-+			goto error;
-+
-+		json_object_object_add(obj, "id", str_obj);
-+	}
-+
-+	location_obj = hostapd_afc_build_location_request(iface);
-+	if (!location_obj)
-+		goto error;
-+
-+	json_object_object_add(l2_obj, "location", location_obj);
-+	str_obj = json_object_new_int(iconf->afc.min_power);
-+	if (!str_obj)
-+		goto error;
-+
-+	json_object_object_add(l2_obj, "minDesiredPower", str_obj);
-+
-+	if (iconf->afc.n_freq_range) {
-+		struct json_object *freq_obj;
-+
-+		freq_obj = json_object_new_array();
-+		if (!freq_obj)
-+			goto error;
-+
-+		json_object_object_add(l2_obj, "inquiredFrequencyRange",
-+				       freq_obj);
-+		for (i = 0; i < iconf->afc.n_freq_range; i++) {
-+			struct afc_freq_range *fr = &iconf->afc.freq_range[i];
-+			struct json_object *obj;
-+
-+			obj = json_object_new_object();
-+			if (!obj)
-+				goto error;
-+
-+			json_object_array_add(freq_obj, obj);
-+			str_obj = json_object_new_int(fr->low_freq);
-+			if (!str_obj)
-+				goto error;
-+
-+			json_object_object_add(obj, "lowFrequency", str_obj);
-+			str_obj = json_object_new_int(fr->high_freq);
-+			if (!str_obj)
-+				goto error;
-+
-+			json_object_object_add(obj, "highFrequency", str_obj);
-+		}
-+	}
-+
-+	op_class_list_obj = hostapd_afc_build_req_chan_list(iface);
-+	if (!op_class_list_obj)
-+		goto error;
-+
-+	json_object_object_add(l2_obj, "inquiredChannels", op_class_list_obj);
-+
-+	wpa_printf(MSG_DEBUG, "Pending AFC request: %s",
-+		   json_object_get_string(l1_obj));
-+
-+	return l1_obj;
-+
-+error:
-+	json_object_put(l1_obj);
-+
-+	return NULL;
-+}
-+
-+
-+static int
-+hostad_afc_parse_available_freq_info(struct hostapd_iface *iface,
-+				     struct json_object *reply_elem_obj)
-+{
-+	struct afc_freq_range_elem *f = NULL;
-+	struct json_object *obj;
-+	int i, count = 0;
-+
-+	if (!json_object_object_get_ex(reply_elem_obj,
-+				       "availableFrequencyInfo", &obj))
-+		return 0;
-+
-+	for (i = 0; i < json_object_array_length(obj); i++) {
-+		struct json_object *range_elem_obj, *freq_range_obj;
-+		struct json_object *high_freq_obj, *low_freq_obj;
-+		struct json_object *max_psd_obj;
-+
-+		range_elem_obj = json_object_array_get_idx(obj, i);
-+		if (!json_object_object_get_ex(range_elem_obj,
-+					       "frequencyRange",
-+					       &freq_range_obj))
-+			continue;
-+
-+		if (!json_object_object_get_ex(freq_range_obj,
-+					       "lowFrequency",
-+					       &low_freq_obj))
-+			continue;
-+
-+		if (!json_object_object_get_ex(freq_range_obj,
-+					       "highFrequency",
-+					       &high_freq_obj))
-+			continue;
-+
-+		if (!json_object_object_get_ex(range_elem_obj, "maxPsd",
-+					       &max_psd_obj) &&
-+		    !json_object_object_get_ex(range_elem_obj, "maxPSD",
-+					       &max_psd_obj))
-+			continue;
-+
-+		f = os_realloc_array(f, count + 1, sizeof(*f));
-+		if (!f)
-+			return -ENOMEM;
-+
-+		f[count].low_freq = json_object_get_int(low_freq_obj);
-+		f[count].high_freq = json_object_get_int(high_freq_obj);
-+		f[count++].max_psd = json_object_get_int(max_psd_obj);
-+	}
-+	iface->afc.freq_range = f;
-+	iface->afc.num_freq_range = count;
-+
-+	return 0;
-+}
-+
-+
-+static int hostad_afc_update_chan_info(struct afc_chan_info_elem **chan_list,
-+				       int *chan_list_size, u8 op_class,
-+				       int center_chan, int power)
-+{
-+	int num_low_subchan, ch, count = *chan_list_size;
-+	struct afc_chan_info_elem *c = *chan_list;
-+
-+	switch (op_class) {
-+	case 132: /*  40MHz */
-+		num_low_subchan = 2;
-+		break;
-+	case 133: /*  80MHz */
-+		num_low_subchan = 6;
-+		break;
-+	case 134: /* 160MHz */
-+		num_low_subchan = 14;
-+		break;
-+	default:
-+		num_low_subchan = 0;
-+		break;
-+	}
-+
-+	for (ch = center_chan - num_low_subchan;
-+	     ch <= center_chan + num_low_subchan; ch += 4) {
-+		int i;
-+
-+		for (i = 0; i < count; i++) {
-+			if (c[i].chan == ch)
-+				break;
-+		}
-+
-+		if (i == count) {
-+			c = os_realloc_array(c, count + 1, sizeof(*c));
-+			if (!c)
-+				return -ENOMEM;
-+
-+			c[count].chan = ch;
-+			c[count++].power = power;
-+		}
-+	}
-+
-+	*chan_list_size = count;
-+	*chan_list = c;
-+
-+	return 0;
-+}
-+
-+
-+static int
-+hostad_afc_parse_available_chan_info(struct hostapd_iface *iface,
-+				     struct json_object *reply_elem_obj)
-+{
-+	struct afc_chan_info_elem *c = NULL;
-+	struct json_object *obj;
-+	int i, count = 0;
-+
-+	if (!json_object_object_get_ex(reply_elem_obj,
-+				       "availableChannelInfo", &obj))
-+		return 0;
-+
-+	for (i = 0; i < json_object_array_length(obj); i++) {
-+		struct json_object *range_elem_obj, *op_class_obj;
-+		struct json_object *chan_cfi_obj, *max_eirp_obj;
-+		int ch, op_class;
-+
-+		range_elem_obj = json_object_array_get_idx(obj, i);
-+		if (!json_object_object_get_ex(range_elem_obj,
-+					       "globalOperatingClass",
-+					       &op_class_obj))
-+			continue;
-+
-+		if (!json_object_object_get_ex(range_elem_obj, "maxEirp",
-+					       &max_eirp_obj))
-+			continue;
-+
-+		if (!json_object_object_get_ex(range_elem_obj, "channelCfi",
-+					       &chan_cfi_obj))
-+			continue;
-+
-+		op_class = json_object_get_int(op_class_obj);
-+		for (ch = 0;
-+		     ch < json_object_array_length(chan_cfi_obj); ch++) {
-+			struct json_object *pwr_obj;
-+			struct json_object *ch_obj;
-+			int channel, power;
-+
-+			ch_obj = json_object_array_get_idx(chan_cfi_obj, ch);
-+			if (!ch_obj)
-+				continue;
-+
-+			pwr_obj = json_object_array_get_idx(max_eirp_obj, ch);
-+			if (!pwr_obj)
-+				continue;
-+
-+			channel = json_object_get_int(ch_obj);
-+			power = json_object_get_int(pwr_obj);
-+
-+			hostad_afc_update_chan_info(&c, &count, op_class,
-+						    channel, power);
-+		}
-+		iface->afc.chan_info_list = c;
-+		iface->afc.num_chan_info = count;
-+	}
-+
-+	return 0;
-+}
-+
-+
-+static int hostad_afc_get_timeout(struct json_object *obj)
-+{
-+	time_t t, now;
-+	struct tm tm;
-+
-+	if (sscanf(json_object_get_string(obj), "%d-%d-%dT%d:%d:%dZ",
-+		   &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour,
-+		   &tm.tm_min, &tm.tm_sec) <= 0)
-+		return HOSTAPD_AFC_TIMEOUT;
-+
-+	tm.tm_year -= 1900;
-+	tm.tm_mon -= 1;
-+	tm.tm_isdst = -1;
-+	t = mktime(&tm);
-+	time(&now);
-+
-+	return now > t ? HOSTAPD_AFC_RETRY_TIMEOUT : (t - now) * 80 / 100;
-+}
-+
-+
-+static int hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply)
-+{
-+	struct json_object *payload_obj, *reply_obj, *version_obj;
-+	struct hostapd_config *iconf = iface->conf;
-+	int i, request_timeout = -1, ret = -EINVAL;
-+
-+	wpa_printf(MSG_DEBUG, "Received AFC reply: %s", reply);
-+	payload_obj = json_tokener_parse(reply);
-+	if (!payload_obj)
-+		return -EINVAL;
-+
-+	if (!json_object_object_get_ex(payload_obj, "version", &version_obj))
-+		return -EINVAL;
-+
-+	if (iconf->afc.request.version &&
-+	    os_strcmp(iconf->afc.request.version,
-+		      json_object_get_string(version_obj)))
-+		return -EINVAL;
-+
-+	if (!json_object_object_get_ex(payload_obj,
-+				       "availableSpectrumInquiryResponses",
-+				       &reply_obj))
-+		return -EINVAL;
-+
-+	for (i = 0; i < json_object_array_length(reply_obj); i++) {
-+		struct json_object *reply_elem_obj, *obj, *status_obj;
-+		int j, status = -EINVAL;
-+
-+		reply_elem_obj = json_object_array_get_idx(reply_obj, i);
-+		if (!reply_elem_obj)
-+			continue;
-+
-+		if (!json_object_object_get_ex(reply_elem_obj, "requestId",
-+					       &obj))
-+			continue;
-+
-+		if (iconf->afc.request.id &&
-+		    os_strcmp(iconf->afc.request.id,
-+			      json_object_get_string(obj)))
-+			continue;
-+
-+		if (!json_object_object_get_ex(reply_elem_obj, "rulesetId",
-+					       &obj))
-+			continue;
-+
-+		for (j = 0; j < iconf->afc.n_cert_ids; j++) {
-+			if (!os_strcmp(iconf->afc.cert_ids[j].rulset,
-+				       json_object_get_string(obj)))
-+				break;
-+		}
-+
-+		if (j == iconf->afc.n_cert_ids)
-+			continue;
-+
-+		if (!json_object_object_get_ex(reply_elem_obj, "response",
-+					       &obj))
-+			continue;
-+
-+		if (json_object_object_get_ex(obj, "shortDescription",
-+					      &status_obj))
-+			wpa_printf(MSG_DEBUG, "AFC reply element %d: %s",
-+				   i, json_object_get_string(status_obj));
-+
-+		if (json_object_object_get_ex(obj, "responseCode",
-+					      &status_obj))
-+			status = json_object_get_int(status_obj);
-+
-+		if (status < 0)
-+			continue;
-+
-+		if (hostad_afc_parse_available_freq_info(iface,
-+							 reply_elem_obj) ||
-+		    hostad_afc_parse_available_chan_info(iface,
-+							 reply_elem_obj))
-+			continue;
-+
-+		if (json_object_object_get_ex(reply_elem_obj,
-+					      "availabilityExpireTime",
-+					      &obj)) {
-+			int timeout = hostad_afc_get_timeout(obj);
-+
-+			if (request_timeout < 0 || timeout < request_timeout)
-+				request_timeout = timeout;
-+		}
-+
-+		ret = status;
-+	}
-+
-+	iface->afc.data_valid = true;
-+	iface->afc.timeout = request_timeout;
-+	if (iface->afc.timeout < 0)
-+		iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
-+
-+	return ret;
-+}
-+
-+
-+static int hostapd_afc_send_receive(struct hostapd_iface *iface)
-+{
-+	struct hostapd_config *iconf = iface->conf;
-+	json_object *request_obj = NULL;
-+	struct timeval sock_timeout = {
-+		.tv_sec = 5,
-+	};
-+	struct sockaddr_un addr = {
-+		.sun_family = AF_UNIX,
-+#ifdef __FreeBSD__
-+		.sun_len = sizeof(addr),
-+#endif /* __FreeBSD__ */
-+	};
-+	char buf[HOSTAPD_AFC_BUFSIZE] = {};
-+	const char *request;
-+	int sockfd, ret;
-+	fd_set read_set;
-+
-+	if (iface->afc.data_valid) {
-+		/* AFC data already downloaded from the server */
-+		return 0;
-+	}
-+
-+	iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
-+	if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) {
-+		wpa_printf(MSG_ERROR, "Malformed AFC socket string %s",
-+			   iconf->afc.socket);
-+		return -EINVAL;
-+	}
-+
-+	os_strlcpy(addr.sun_path, iconf->afc.socket, sizeof(addr.sun_path));
-+	sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
-+	if (sockfd < 0) {
-+		wpa_printf(MSG_ERROR, "Failed creating AFC socket");
-+		return sockfd;
-+	}
-+
-+	if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-+		wpa_printf(MSG_ERROR, "Failed connecting AFC socket");
-+		ret = -EIO;
-+		goto close_sock;
-+	}
-+
-+	request_obj = hostapd_afc_build_request(iface);
-+	if (!request_obj) {
-+		ret = -ENOMEM;
-+		goto close_sock;
-+	}
-+
-+	request = json_object_to_json_string(request_obj);
-+	if (send(sockfd, request, strlen(request), 0) < 0) {
-+		wpa_printf(MSG_ERROR, "Failed sending AFC request");
-+		ret = -EIO;
-+		goto close_sock;
-+	}
-+
-+	FD_ZERO(&read_set);
-+	FD_SET(sockfd, &read_set);
-+	if (select(sockfd + 1, &read_set, NULL, NULL, &sock_timeout) < 0) {
-+		wpa_printf(MSG_ERROR, "Select failed on AFC socket");
-+		ret = -errno;
-+		goto close_sock;
-+	}
-+
-+	if (!FD_ISSET(sockfd, &read_set)) {
-+		ret = -EIO;
-+		goto close_sock;
-+	}
-+
-+	ret = recv(sockfd, buf, sizeof(buf), 0);
-+	if (ret <= 0)
-+		goto close_sock;
-+
-+	ret = hostapd_afc_parse_reply(iface, buf);
-+close_sock:
-+	json_object_put(request_obj);
-+	close(sockfd);
-+
-+	return ret;
-+}
-+
-+
-+static bool hostapd_afc_has_usable_chans(struct hostapd_iface *iface)
-+{
-+	const struct oper_class_map *oper_class;
-+	int ch;
-+
-+	oper_class = get_oper_class(NULL, iface->conf->op_class);
-+	if (!oper_class)
-+		return false;
-+
-+	for (ch = oper_class->min_chan; ch <= oper_class->max_chan;
-+	     ch += oper_class->inc) {
-+		struct hostapd_hw_modes *mode = iface->current_mode;
-+		int i;
-+
-+		for (i = 0; i < mode->num_channels; i++) {
-+			struct hostapd_channel_data *chan = &mode->channels[i];
-+
-+			if (chan->chan == ch &&
-+			    !(chan->flag & HOSTAPD_CHAN_DISABLED))
-+			    return true;
-+		}
-+	}
-+
-+	return false;
-+}
-+
-+
-+int hostapd_afc_handle_request(struct hostapd_iface *iface)
-+{
-+	struct hostapd_config *iconf = iface->conf;
-+	int ret;
-+
-+	/* AFC is required just for standard power AP */
-+	if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
-+		return 1;
-+
-+	if (!is_6ghz_op_class(iconf->op_class) || !is_6ghz_freq(iface->freq))
-+		return 1;
-+
-+	if (iface->state == HAPD_IFACE_ACS)
-+		return 1;
-+
-+	ret = hostapd_afc_send_receive(iface);
-+	if (ret < 0) {
-+		/*
-+		 * If the connection to the AFCD failed, resched for a
-+		 * future attempt.
-+		 */
-+		wpa_printf(MSG_ERROR, "AFC connection failed: %d", ret);
-+		if (ret == -EIO)
-+			ret = 0;
-+		goto resched;
-+	}
-+
-+	hostap_afc_disable_channels(iface);
-+	if (!hostapd_afc_has_usable_chans(iface))
-+		goto resched;
-+
-+	/* Trigger an ACS freq scan */
-+	iconf->channel = 0;
-+	iface->freq = 0;
-+
-+	if (acs_init(iface) != HOSTAPD_CHAN_ACS) {
-+		wpa_printf(MSG_ERROR, "Could not start ACS");
-+		ret = -EINVAL;
-+	}
-+
-+resched:
-+	eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
-+	eloop_register_timeout(iface->afc.timeout, 0,
-+			       hostapd_afc_timeout_handler, iface, NULL);
-+
-+	return ret;
-+}
-+
-+
-+static void hostapd_afc_delete_data_from_server(struct hostapd_iface *iface)
-+{
-+	os_free(iface->afc.chan_info_list);
-+	os_free(iface->afc.freq_range);
-+
-+	iface->afc.num_freq_range = 0;
-+	iface->afc.num_chan_info = 0;
-+
-+	iface->afc.chan_info_list = NULL;
-+	iface->afc.freq_range = NULL;
-+
-+	iface->afc.data_valid = false;
-+}
-+
-+
-+static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx)
-+{
-+	struct hostapd_iface *iface = eloop_ctx;
-+	bool restart_iface = true;
-+
-+	hostapd_afc_delete_data_from_server(iface);
-+	if (iface->state != HAPD_IFACE_ENABLED) {
-+		/* Hostapd is not fully enabled yet, toogle the interface */
-+		goto restart_interface;
-+	}
-+
-+	if (hostapd_afc_send_receive(iface) < 0 ||
-+	    hostapd_get_hw_features(iface)) {
-+		restart_iface = false;
-+		goto restart_interface;
-+	}
-+
-+	if (hostapd_is_usable_chans(iface))
-+		goto resched;
-+
-+	restart_iface = hostapd_afc_has_usable_chans(iface);
-+	if (restart_iface) {
-+		/* Trigger an ACS freq scan */
-+		iface->conf->channel = 0;
-+		iface->freq = 0;
-+	}
-+
-+restart_interface:
-+	hostapd_disable_iface(iface);
-+	if (restart_iface)
-+		hostapd_enable_iface(iface);
-+resched:
-+	eloop_register_timeout(iface->afc.timeout, 0,
-+			       hostapd_afc_timeout_handler, iface, NULL);
-+}
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 1a18df617..ca67aeb41 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -1035,6 +1035,22 @@ void hostapd_config_free(struct hostapd_config *conf)
- #endif /* CONFIG_ACS */
- 	wpabuf_free(conf->lci);
- 	wpabuf_free(conf->civic);
-+#ifdef CONFIG_AFC
-+	os_free(conf->afc.socket);
-+	os_free(conf->afc.request.version);
-+	os_free(conf->afc.request.id);
-+	os_free(conf->afc.request.sn);
-+	for (i = 0; i < conf->afc.n_cert_ids; i++) {
-+		os_free(conf->afc.cert_ids[i].rulset);
-+		os_free(conf->afc.cert_ids[i].id);
-+	}
-+	os_free(conf->afc.cert_ids);
-+	os_free(conf->afc.location.height_type);
-+	os_free(conf->afc.location.linear_polygon_data);
-+	os_free(conf->afc.location.radial_polygon_data);
-+	os_free(conf->afc.freq_range);
-+	os_free(conf->afc.op_class);
-+#endif /* CONFIG_AFC */
- 
- 	os_free(conf);
- }
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 754d55331..2330163c4 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1225,6 +1225,53 @@ struct hostapd_config {
- 		MBSSID_ENABLED = 1,
- 		ENHANCED_MBSSID_ENABLED = 2,
- 	} mbssid;
-+
-+#ifdef CONFIG_AFC
-+	struct {
-+		char *socket;
-+		struct {
-+			char *version;
-+			char *id;
-+			char *sn;
-+		} request;
-+		unsigned int n_cert_ids;
-+		struct cert_id {
-+			char *rulset;
-+			char *id;
-+		} *cert_ids;
-+		struct {
-+			enum afc_location_type {
-+				ELLIPSE,
-+				LINEAR_POLYGON,
-+				RADIAL_POLYGON,
-+			} type;
-+			unsigned int n_linear_polygon_data;
-+			struct afc_linear_polygon {
-+				double longitude;
-+				double latitude;
-+			} *linear_polygon_data;
-+			unsigned int n_radial_polygon_data;
-+			struct afc_radial_polygon {
-+				double length;
-+				double angle;
-+			} *radial_polygon_data;
-+			int major_axis;
-+			int minor_axis;
-+			int orientation;
-+			double height;
-+			char *height_type;
-+			int vertical_tolerance;
-+		} location;
-+		unsigned int n_freq_range;
-+		struct afc_freq_range {
-+			int low_freq;
-+			int high_freq;
-+		} *freq_range;
-+		unsigned int n_op_class;
-+		unsigned int *op_class;
-+		int min_power;
-+	} afc;
-+#endif /* CONFIG_AFC */
- };
- 
- 
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index ff1d8f9d0..916ac00c4 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2497,6 +2497,16 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
- 		}
- #endif /* CONFIG_MESH */
- 
-+#ifdef CONFIG_AFC
-+		/* check AFC for 6GHz channels. */
-+		res = hostapd_afc_handle_request(iface);
-+		if (res <= 0) {
-+			if (res < 0)
-+				goto fail;
-+			return res;
-+		}
-+#endif /* CONFIG_AFC */
-+
- 		if (!delay_apply_cfg &&
- 		    hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
- 				     hapd->iconf->channel,
-@@ -2968,6 +2978,10 @@ void hostapd_interface_free(struct hostapd_iface *iface)
- 			   __func__, iface->bss[j]);
- 		os_free(iface->bss[j]);
- 	}
-+#ifdef CONFIG_AFC
-+	os_free(iface->afc.chan_info_list);
-+	os_free(iface->afc.freq_range);
-+#endif
- 	hostapd_cleanup_iface(iface);
- }
- 
-@@ -4888,3 +4902,49 @@ u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd)
- 
- 	return punct_bitmap;
- }
-+
-+
-+void hostap_afc_disable_channels(struct hostapd_iface *iface)
-+{
-+#ifdef CONFIG_AFC
-+	struct hostapd_hw_modes *mode;
-+	int i;
-+
-+	if (iface->num_hw_features < HOSTAPD_MODE_IEEE80211A)
-+		return;
-+
-+	if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type))
-+		return;
-+
-+	if (!iface->afc.data_valid)
-+		return;
-+
-+	mode = &iface->hw_features[HOSTAPD_MODE_IEEE80211A];
-+	for (i = 0; i < mode->num_channels; i++) {
-+		struct hostapd_channel_data *chan = &mode->channels[i];
-+		int j;
-+
-+		if (!is_6ghz_freq(chan->freq))
-+			continue;
-+
-+		for (j = 0; j < iface->afc.num_freq_range; j++) {
-+			if (chan->freq >= iface->afc.freq_range[j].low_freq &&
-+			    chan->freq <= iface->afc.freq_range[j].high_freq)
-+				break;
-+		}
-+
-+		if (j != iface->afc.num_freq_range)
-+			continue;
-+
-+		for (j = 0; j < iface->afc.num_chan_info; j++) {
-+			if (chan->chan == iface->afc.chan_info_list[j].chan)
-+				break;
-+		}
-+
-+		if (j != iface->afc.num_chan_info)
-+			continue;
-+
-+		chan->flag |= HOSTAPD_CHAN_DISABLED;
-+	}
-+#endif /* CONFIG_AFC */
-+}
-diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
-index d12efb104..18bcb82d9 100644
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -700,9 +700,38 @@ struct hostapd_iface {
- 
- 	/* Configured freq of interface is NO_IR */
- 	bool is_no_ir;
-+
-+#ifdef CONFIG_AFC
-+	struct {
-+		int timeout;
-+		unsigned int num_freq_range;
-+		struct afc_freq_range_elem {
-+			int low_freq;
-+			int high_freq;
-+			/**
-+			 * max eirp power spectral density received from
-+			 * the AFC coordinator for this band
-+			 */
-+			int max_psd;
-+		} *freq_range;
-+		unsigned int num_chan_info;
-+		struct afc_chan_info_elem {
-+			int chan;
-+			/**
-+			 * max eirp power received from the AFC coordinator
-+			 * for this channel
-+			 */
-+			int power;
-+		} *chan_info_list;
-+		bool data_valid;
-+	} afc;
-+#endif /* CONFIG_AFC */
- };
- 
- /* hostapd.c */
-+void hostap_afc_disable_channels(struct hostapd_iface *iface);
-+int hostapd_afc_handle_request(struct hostapd_iface *iface);
-+
- int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
- 			       int (*cb)(struct hostapd_iface *iface,
- 					 void *ctx), void *ctx);
-diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
-index e652d7504..222f3dc05 100644
---- a/src/ap/hw_features.c
-+++ b/src/ap/hw_features.c
-@@ -114,6 +114,8 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
- 	iface->hw_features = modes;
- 	iface->num_hw_features = num_modes;
- 
-+	hostap_afc_disable_channels(iface);
-+
- 	for (i = 0; i < num_modes; i++) {
- 		struct hostapd_hw_modes *feature = &modes[i];
- 		int dfs_enabled = hapd->iconf->ieee80211h &&
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0025-mtk-hostapd-6G-band-does-not-require-DFS.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0025-mtk-hostapd-6G-band-does-not-require-DFS.patch
new file mode 100644
index 0000000..2ba3b97
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0025-mtk-hostapd-6G-band-does-not-require-DFS.patch
@@ -0,0 +1,24 @@
+From ba80aca90cdfbc98b8a21489f0fc89114c94f2d1 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 13 Feb 2023 11:03:53 +0800
+Subject: [PATCH 025/126] mtk: hostapd: 6G band does not require DFS
+
+---
+ src/ap/dfs.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 42cce2dce..86598a18a 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1538,6 +1538,7 @@ int hostapd_is_dfs_required(struct hostapd_iface *iface)
+ 	if ((!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+ 	     !iface->conf->ieee80211h) ||
+ 	    !iface->current_mode ||
++	    is_6ghz_freq(iface->freq) ||
+ 	    iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+ 		return 0;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0026-backport-hostapd-update-TPE-IE-according-to-AFC.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0026-backport-hostapd-update-TPE-IE-according-to-AFC.patch
deleted file mode 100644
index 42ff1d2..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0026-backport-hostapd-update-TPE-IE-according-to-AFC.patch
+++ /dev/null
@@ -1,158 +0,0 @@
-From b2078261e779c949218974a054dc52f3dc5493c7 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Wed, 20 Mar 2024 07:20:01 +0800
-Subject: [PATCH 026/104] backport: hostapd: update TPE IE according to AFC
-
-Update Transmit Power Envelope (TPE) IE according to the reply from AFC
-coordinator on UNII-5 or UNII-7 6GHz bands.
-
-Tested-by: Allen Ye <allen.ye@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
----
- src/ap/hostapd.c    | 39 +++++++++++++++++++++++++++++++++++++
- src/ap/hostapd.h    |  2 ++
- src/ap/ieee802_11.c | 47 ++++++++++++++++++++++++++++-----------------
- 3 files changed, 70 insertions(+), 18 deletions(-)
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 916ac00c4..b899c9831 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -4904,6 +4904,45 @@ u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd)
- }
- 
- 
-+int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
-+				       int *power)
-+{
-+#ifdef CONFIG_AFC
-+	int i;
-+
-+	if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type))
-+		return -EINVAL;
-+
-+	if (!iface->afc.data_valid)
-+		return -EINVAL;
-+
-+	if (psd) {
-+		for (i = 0; i < iface->afc.num_freq_range; i++) {
-+			struct afc_freq_range_elem *f;
-+
-+			f = &iface->afc.freq_range[i];
-+			if (iface->freq >= f->low_freq &&
-+			    iface->freq <= f->high_freq) {
-+				*power = 2 * f->max_psd;
-+				return 0;
-+			}
-+		}
-+	} else {
-+		for (i = 0; i < iface->afc.num_chan_info; i++) {
-+			struct afc_chan_info_elem *c;
-+
-+			c = &iface->afc.chan_info_list[i];
-+			if (c->chan == iface->conf->channel) {
-+				*power = 2 * c->power;
-+				return 0;
-+			}
-+		}
-+	}
-+#endif /* CONFIG_AFC */
-+	return -EINVAL;
-+}
-+
-+
- void hostap_afc_disable_channels(struct hostapd_iface *iface)
- {
- #ifdef CONFIG_AFC
-diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
-index 18bcb82d9..594866fbb 100644
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -729,6 +729,8 @@ struct hostapd_iface {
- };
- 
- /* hostapd.c */
-+int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
-+				       int *power);
- void hostap_afc_disable_channels(struct hostapd_iface *iface);
- int hostapd_afc_handle_request(struct hostapd_iface *iface);
- 
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 9a23c7240..179af5e28 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -7047,42 +7047,53 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
- 	 */
- 	if (is_6ghz_op_class(iconf->op_class)) {
- 		enum max_tx_pwr_interpretation tx_pwr_intrpn;
-+		int err, max_eirp_psd, max_eirp_power;
- 
- 		/* Same Maximum Transmit Power for all 20 MHz bands */
- 		tx_pwr_count = 0;
- 		tx_pwr_intrpn = REGULATORY_CLIENT_EIRP_PSD;
- 
- 		/* Default Transmit Power Envelope for Global Operating Class */
--		if (hapd->iconf->reg_def_cli_eirp_psd != -1)
--			tx_pwr = hapd->iconf->reg_def_cli_eirp_psd;
--		else
--			tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
-+		err = hostap_afc_get_chan_max_eirp_power(iface, true,
-+							 &max_eirp_psd);
-+		if (err < 0) {
-+			if (hapd->iconf->reg_def_cli_eirp_psd != -1)
-+				max_eirp_psd = hapd->iconf->reg_def_cli_eirp_psd;
-+			else
-+				max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
-+		}
- 
- 		eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn,
--					   REG_DEFAULT_CLIENT, tx_pwr);
-+					   REG_DEFAULT_CLIENT, max_eirp_psd);
- 
- 		/* Indoor Access Point must include an additional TPE for
- 		 * subordinate devices */
- 		if (he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type)) {
--			/* TODO: Extract PSD limits from channel data */
--			if (hapd->iconf->reg_sub_cli_eirp_psd != -1)
--				tx_pwr = hapd->iconf->reg_sub_cli_eirp_psd;
--			else
--				tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
-+			if (err < 0) {
-+				/* non-AFC connection */
-+				if (hapd->iconf->reg_sub_cli_eirp_psd != -1)
-+					max_eirp_psd = hapd->iconf->reg_sub_cli_eirp_psd;
-+				else
-+					max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
-+			}
- 			eid = hostapd_add_tpe_info(eid, tx_pwr_count,
- 						   tx_pwr_intrpn,
- 						   REG_SUBORDINATE_CLIENT,
--						   tx_pwr);
-+						   max_eirp_psd);
- 		}
- 
--		if (iconf->reg_def_cli_eirp != -1 &&
--		    he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
--			eid = hostapd_add_tpe_info(
--				eid, tx_pwr_count, REGULATORY_CLIENT_EIRP,
--				REG_DEFAULT_CLIENT,
--				hapd->iconf->reg_def_cli_eirp);
-+		if (hostap_afc_get_chan_max_eirp_power(iface, false,
-+						       &max_eirp_power)) {
-+			max_eirp_power = iconf->reg_def_cli_eirp;
-+			if (max_eirp_power == -1 ||
-+			    !he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
-+				return eid;
-+		}
- 
--		return eid;
-+		return hostapd_add_tpe_info(eid, tx_pwr_count,
-+					    REGULATORY_CLIENT_EIRP,
-+					    REG_DEFAULT_CLIENT,
-+					    max_eirp_power);
- 	}
- #endif /* CONFIG_IEEE80211AX */
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0026-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0026-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch
new file mode 100644
index 0000000..914e7ad
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0026-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch
@@ -0,0 +1,46 @@
+From 4fc496233d06ceb3c162fa4a474c9bf8bf2ae909 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 11:01:18 +0800
+Subject: [PATCH 026/126] mtk: hostapd: Fix sending wrong VHT operation IE in
+ CSA while using ZWDFS
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 86598a18a..aaaea0edc 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1130,6 +1130,14 @@ static int
+ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+ {
+ 	u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
++	int ret;
++
++	ret = hostapd_dfs_request_channel_switch(iface, iface->radar_background.channel,
++						 iface->radar_background.freq,
++						 iface->radar_background.secondary_channel,
++						 current_vht_oper_chwidth,
++						 iface->radar_background.centr_freq_seg0_idx,
++						 iface->radar_background.centr_freq_seg1_idx);
+ 
+ 	iface->conf->channel = iface->radar_background.channel;
+ 	iface->freq = iface->radar_background.freq;
+@@ -1142,11 +1150,7 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+ 
+ 	hostapd_dfs_update_background_chain(iface);
+ 
+-	return hostapd_dfs_request_channel_switch(
+-		iface, iface->conf->channel, iface->freq,
+-		iface->conf->secondary_channel, current_vht_oper_chwidth,
+-		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+-		hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
++	return ret;
+ }
+ 
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0027-hostapd-sync-2024-01-18-openwrt-trunk-src-folder.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0027-hostapd-sync-2024-01-18-openwrt-trunk-src-folder.patch
deleted file mode 100644
index cbe2769..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0027-hostapd-sync-2024-01-18-openwrt-trunk-src-folder.patch
+++ /dev/null
@@ -1,5128 +0,0 @@
-From ccb5628a9c5eae2b56cb88f43f850146863cba30 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Tue, 23 Jan 2024 16:46:44 +0800
-Subject: [PATCH 027/104] hostapd: sync 2024-01-18 openwrt/trunk src folder
-
----
- hostapd/radius.c           |  715 +++++++++++++
- src/ap/ubus.c              | 2005 ++++++++++++++++++++++++++++++++++++
- src/ap/ubus.h              |  154 +++
- src/ap/ucode.c             |  813 +++++++++++++++
- src/ap/ucode.h             |   54 +
- src/utils/build_features.h |   65 ++
- src/utils/ucode.c          |  502 +++++++++
- src/utils/ucode.h          |   30 +
- wpa_supplicant/ubus.c      |  280 +++++
- wpa_supplicant/ubus.h      |   55 +
- wpa_supplicant/ucode.c     |  299 ++++++
- wpa_supplicant/ucode.h     |   49 +
- 12 files changed, 5021 insertions(+)
- create mode 100644 hostapd/radius.c
- create mode 100644 src/ap/ubus.c
- create mode 100644 src/ap/ubus.h
- create mode 100644 src/ap/ucode.c
- create mode 100644 src/ap/ucode.h
- create mode 100644 src/utils/build_features.h
- create mode 100644 src/utils/ucode.c
- create mode 100644 src/utils/ucode.h
- create mode 100644 wpa_supplicant/ubus.c
- create mode 100644 wpa_supplicant/ubus.h
- create mode 100644 wpa_supplicant/ucode.c
- create mode 100644 wpa_supplicant/ucode.h
-
-diff --git a/hostapd/radius.c b/hostapd/radius.c
-new file mode 100644
-index 000000000..362a22c27
---- /dev/null
-+++ b/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/src/ap/ubus.c b/src/ap/ubus.c
-new file mode 100644
-index 000000000..f2041a0c9
---- /dev/null
-+++ b/src/ap/ubus.c
-@@ -0,0 +1,2005 @@
-+/*
-+ * 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);
-+}
-+
-+
-+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, hostapd_get_punct_bitmap(hapd));
-+
-+	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;
-+
-+	if (!ctx)
-+		return;
-+
-+	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/src/ap/ubus.h b/src/ap/ubus.h
-new file mode 100644
-index 000000000..b0f7c44ab
---- /dev/null
-+++ b/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/src/ap/ucode.c b/src/ap/ucode.c
-new file mode 100644
-index 000000000..16d1b5153
---- /dev/null
-+++ b/src/ap/ucode.c
-@@ -0,0 +1,813 @@
-+#include <sys/un.h>
-+
-+#include "utils/includes.h"
-+#include "utils/common.h"
-+#include "utils/ucode.h"
-+#include "hostapd.h"
-+#include "beacon.h"
-+#include "hw_features.h"
-+#include "ap_drv_ops.h"
-+#include "dfs.h"
-+#include "acs.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);
-+	hapd->ucode.idx = wpa_ucode_registry_add(bss_registry, val);
-+
-+	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);
-+	hapd->ucode.idx = wpa_ucode_registry_add(iface_registry, val);
-+
-+	return val;
-+}
-+
-+static void
-+hostapd_ucode_update_bss_list(struct hostapd_iface *iface, uc_value_t *if_bss, uc_value_t *bss)
-+{
-+	uc_value_t *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);
-+
-+		ucv_array_set(list, i, ucv_get(ucv_string_new(hapd->conf->iface)));
-+		ucv_object_add(bss, hapd->conf->iface, ucv_get(val));
-+	}
-+	ucv_object_add(if_bss, iface->phy, ucv_get(list));
-+}
-+
-+static void
-+hostapd_ucode_update_interfaces(void)
-+{
-+	uc_value_t *ifs = ucv_object_new(vm);
-+	uc_value_t *if_bss = ucv_array_new(vm);
-+	uc_value_t *bss = 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, if_bss, bss);
-+	}
-+
-+	ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
-+	ucv_object_add(ucv_prototype_get(global), "interface_bss", ucv_get(if_bss));
-+	ucv_object_add(ucv_prototype_get(global), "bss", ucv_get(bss));
-+	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 struct hostapd_vlan *
-+bss_conf_find_vlan(struct hostapd_bss_config *bss, int id)
-+{
-+	struct hostapd_vlan *vlan;
-+
-+	for (vlan = bss->vlan; vlan; vlan = vlan->next)
-+		if (vlan->vlan_id == id)
-+			return vlan;
-+
-+	return NULL;
-+}
-+
-+static int
-+bss_conf_rename_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
-+		     const char *ifname)
-+{
-+	if (!strcmp(ifname, vlan->ifname))
-+		return 0;
-+
-+	hostapd_drv_if_rename(hapd, WPA_IF_AP_VLAN, vlan->ifname, ifname);
-+	os_strlcpy(vlan->ifname, ifname, sizeof(vlan->ifname));
-+
-+	return 0;
-+}
-+
-+static int
-+bss_reload_vlans(struct hostapd_data *hapd, struct hostapd_bss_config *bss)
-+{
-+	struct hostapd_bss_config *old_bss = hapd->conf;
-+	struct hostapd_vlan *vlan, *vlan_new, *wildcard;
-+	char ifname[IFNAMSIZ + 1], vlan_ifname[IFNAMSIZ + 1], *pos;
-+	int ret;
-+
-+	vlan = bss_conf_find_vlan(old_bss, VLAN_ID_WILDCARD);
-+	wildcard = bss_conf_find_vlan(bss, VLAN_ID_WILDCARD);
-+	if (!!vlan != !!wildcard)
-+		return -1;
-+
-+	if (vlan && wildcard && strcmp(vlan->ifname, wildcard->ifname) != 0)
-+		strcpy(vlan->ifname, wildcard->ifname);
-+	else
-+		wildcard = NULL;
-+
-+	for (vlan = bss->vlan; vlan; vlan = vlan->next) {
-+		if (vlan->vlan_id == VLAN_ID_WILDCARD ||
-+		    vlan->dynamic_vlan > 0)
-+			continue;
-+
-+		if (!bss_conf_find_vlan(old_bss, vlan->vlan_id))
-+			return -1;
-+	}
-+
-+	for (vlan = old_bss->vlan; vlan; vlan = vlan->next) {
-+		if (vlan->vlan_id == VLAN_ID_WILDCARD)
-+			continue;
-+
-+		if (vlan->dynamic_vlan == 0) {
-+			vlan_new = bss_conf_find_vlan(bss, vlan->vlan_id);
-+			if (!vlan_new)
-+				return -1;
-+
-+			if (bss_conf_rename_vlan(hapd, vlan, vlan_new->ifname))
-+				return -1;
-+
-+			continue;
-+		}
-+
-+		if (!wildcard)
-+			continue;
-+
-+		os_strlcpy(ifname, wildcard->ifname, sizeof(ifname));
-+		pos = os_strchr(ifname, '#');
-+		if (!pos)
-+			return -1;
-+
-+		*pos++ = '\0';
-+		ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s%d%s",
-+				  ifname, vlan->vlan_id, pos);
-+	        if (os_snprintf_error(sizeof(vlan_ifname), ret))
-+			return -1;
-+
-+		if (bss_conf_rename_vlan(hapd, vlan, vlan_ifname))
-+			return -1;
-+	}
-+
-+	return 0;
-+}
-+
-+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);
-+	uc_value_t *files_only = uc_fn_arg(2);
-+	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)
-+		goto out;
-+
-+	if (idx > conf->num_bss || !conf->bss[idx])
-+		goto free;
-+
-+	if (ucv_boolean_get(files_only)) {
-+		struct hostapd_bss_config *bss = conf->bss[idx];
-+		struct hostapd_bss_config *old_bss = hapd->conf;
-+
-+#define swap_field(name)				\
-+	do {								\
-+		void *ptr = old_bss->name;		\
-+		old_bss->name = bss->name;		\
-+		bss->name = ptr;				\
-+	} while (0)
-+
-+		swap_field(ssid.wpa_psk_file);
-+		ret = bss_reload_vlans(hapd, bss);
-+		goto done;
-+	}
-+
-+	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_setup_bss(hapd, hapd == iface->bss[0], true);
-+	hostapd_ucode_update_interfaces();
-+
-+done:
-+	ret = 0;
-+free:
-+	hostapd_config_free(conf);
-+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)
-+		return NULL;
-+
-+	iface = hapd->iface;
-+	if (iface->num_bss == 1) {
-+		wpa_printf(MSG_ERROR, "trying to delete last bss of an iface: %s\n", hapd->conf->iface);
-+		return NULL;
-+	}
-+
-+	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--;
-+
-+	iface->bss[0]->interface_added = 0;
-+	hostapd_drv_set_first_bss(iface->bss[0]);
-+	hapd->interface_added = 1;
-+
-+	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_interfaces();
-+	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_interfaces();
-+	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_iface_set_bss_order(uc_vm_t *vm, size_t nargs)
-+{
-+	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
-+	uc_value_t *bss_list = uc_fn_arg(0);
-+	struct hostapd_data **new_bss;
-+	struct hostapd_bss_config **new_conf;
-+
-+	if (!iface)
-+		return NULL;
-+
-+	if (ucv_type(bss_list) != UC_ARRAY ||
-+	    ucv_array_length(bss_list) != iface->num_bss)
-+		return NULL;
-+
-+	new_bss = calloc(iface->num_bss, sizeof(*new_bss));
-+	new_conf = calloc(iface->num_bss, sizeof(*new_conf));
-+	for (size_t i = 0; i < iface->num_bss; i++) {
-+		struct hostapd_data *bss;
-+
-+		bss = ucv_resource_data(ucv_array_get(bss_list, i), "hostapd.bss");
-+		if (bss->iface != iface)
-+			goto free;
-+
-+		for (size_t k = 0; k < i; k++)
-+			if (new_bss[k] == bss)
-+				goto free;
-+
-+		new_bss[i] = bss;
-+		new_conf[i] = bss->conf;
-+	}
-+
-+	new_bss[0]->interface_added = 0;
-+	for (size_t i = 1; i < iface->num_bss; i++)
-+		new_bss[i]->interface_added = 1;
-+
-+	free(iface->bss);
-+	iface->bss = new_bss;
-+
-+	free(iface->conf->bss);
-+	iface->conf->bss = new_conf;
-+	iface->conf->num_bss = iface->num_bss;
-+	hostapd_drv_set_first_bss(iface->bss[0]);
-+
-+	return ucv_boolean_new(true);
-+
-+free:
-+	free(new_bss);
-+	free(new_conf);
-+	return NULL;
-+}
-+
-+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);
-+}
-+
-+static void
-+uc_hostapd_disable_iface(struct hostapd_iface *iface)
-+{
-+	switch (iface->state) {
-+	case HAPD_IFACE_DISABLED:
-+		break;
-+#ifdef CONFIG_ACS
-+	case HAPD_IFACE_ACS:
-+		acs_cleanup(iface);
-+		iface->scan_cb = NULL;
-+		/* fallthrough */
-+#endif
-+	default:
-+		hostapd_disable_iface(iface);
-+		break;
-+	}
-+}
-+
-+static uc_value_t *
-+uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
-+{
-+	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
-+	int i;
-+
-+	if (!iface)
-+		return NULL;
-+
-+	if (iface->state != HAPD_IFACE_ENABLED)
-+		uc_hostapd_disable_iface(iface);
-+
-+	for (i = 0; i < iface->num_bss; i++) {
-+		struct hostapd_data *hapd = iface->bss[i];
-+
-+		hostapd_drv_stop_ap(hapd);
-+		hapd->beacon_set_done = 0;
-+	}
-+
-+	return NULL;
-+}
-+
-+static uc_value_t *
-+uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
-+{
-+	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
-+	uc_value_t *info = uc_fn_arg(0);
-+	struct hostapd_config *conf;
-+	bool changed = false;
-+	uint64_t intval;
-+	int i;
-+
-+	if (!iface)
-+		return NULL;
-+
-+	if (!info) {
-+		iface->freq = 0;
-+		goto out;
-+	}
-+
-+	if (ucv_type(info) != UC_OBJECT)
-+		return NULL;
-+
-+#define UPDATE_VAL(field, name)							\
-+	if ((intval = ucv_int64_get(ucv_object_get(info, name, NULL))) &&	\
-+		!errno && intval != conf->field) do {				\
-+		conf->field = intval;						\
-+		changed = true;							\
-+	} while(0)
-+
-+	conf = iface->conf;
-+	UPDATE_VAL(op_class, "op_class");
-+	UPDATE_VAL(hw_mode, "hw_mode");
-+	UPDATE_VAL(channel, "channel");
-+	UPDATE_VAL(secondary_channel, "sec_channel");
-+	if (!changed &&
-+	    (iface->bss[0]->beacon_set_done ||
-+	     iface->state == HAPD_IFACE_DFS))
-+		return ucv_boolean_new(true);
-+
-+	intval = ucv_int64_get(ucv_object_get(info, "center_seg0_idx", NULL));
-+	if (!errno)
-+		hostapd_set_oper_centr_freq_seg0_idx(conf, intval);
-+
-+	intval = ucv_int64_get(ucv_object_get(info, "center_seg1_idx", NULL));
-+	if (!errno)
-+		hostapd_set_oper_centr_freq_seg1_idx(conf, intval);
-+
-+	intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
-+	if (!errno)
-+		hostapd_set_oper_chwidth(conf, intval);
-+
-+	intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL));
-+	if (!errno)
-+		iface->freq = intval;
-+	else
-+		iface->freq = 0;
-+	conf->acs = 0;
-+
-+out:
-+	switch (iface->state) {
-+	case HAPD_IFACE_ENABLED:
-+		if (!hostapd_is_dfs_required(iface) ||
-+			hostapd_is_dfs_chan_available(iface))
-+			break;
-+		wpa_printf(MSG_INFO, "DFS CAC required on new channel, restart interface");
-+		/* fallthrough */
-+	default:
-+		uc_hostapd_disable_iface(iface);
-+		break;
-+	}
-+
-+	if (conf->channel && !iface->freq)
-+		iface->freq = hostapd_hw_get_freq(iface->bss[0], conf->channel);
-+
-+	if (iface->state != HAPD_IFACE_ENABLED) {
-+		hostapd_enable_iface(iface);
-+		return ucv_boolean_new(true);
-+	}
-+
-+	for (i = 0; i < iface->num_bss; i++) {
-+		struct hostapd_data *hapd = iface->bss[i];
-+		int ret;
-+
-+		hapd->conf->start_disabled = 0;
-+		hostapd_set_freq(hapd, conf->hw_mode, iface->freq,
-+				 conf->channel,
-+				 conf->enable_edmg,
-+				 conf->edmg_channel,
-+				 conf->ieee80211n,
-+				 conf->ieee80211ac,
-+				 conf->ieee80211ax,
-+				 conf->ieee80211be,
-+				 conf->secondary_channel,
-+				 hostapd_get_oper_chwidth(conf),
-+				 hostapd_get_oper_centr_freq_seg0_idx(conf),
-+				 hostapd_get_oper_centr_freq_seg1_idx(conf));
-+
-+		ieee802_11_set_beacon(hapd);
-+	}
-+
-+	return ucv_boolean_new(true);
-+}
-+
-+static uc_value_t *
-+uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
-+{
-+	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
-+	uc_value_t *info = uc_fn_arg(0);
-+	struct hostapd_config *conf;
-+	struct csa_settings csa = {};
-+	uint64_t intval;
-+	int i, ret = 0;
-+
-+	if (!iface || ucv_type(info) != UC_OBJECT)
-+		return NULL;
-+
-+	conf = iface->conf;
-+	if ((intval = ucv_int64_get(ucv_object_get(info, "csa_count", NULL))) && !errno)
-+		csa.cs_count = intval;
-+	if ((intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL))) && !errno)
-+		csa.freq_params.sec_channel_offset = intval;
-+
-+	csa.freq_params.ht_enabled = conf->ieee80211n;
-+	csa.freq_params.vht_enabled = conf->ieee80211ac;
-+	csa.freq_params.he_enabled = conf->ieee80211ax;
-+#ifdef CONFIG_IEEE80211BE
-+	csa.freq_params.eht_enabled = conf->ieee80211be;
-+#endif
-+	intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
-+	if (errno)
-+		intval = hostapd_get_oper_chwidth(conf);
-+	if (intval)
-+		csa.freq_params.bandwidth = 40 << intval;
-+	else
-+		csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20;
-+
-+	if ((intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL))) && !errno)
-+		csa.freq_params.freq = intval;
-+	if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq1", NULL))) && !errno)
-+		csa.freq_params.center_freq1 = intval;
-+	if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
-+		csa.freq_params.center_freq2 = intval;
-+
-+	for (i = 0; i < iface->num_bss; i++)
-+		ret = hostapd_switch_channel(iface->bss[i], &csa);
-+
-+	return ucv_boolean_new(!ret);
-+}
-+
-+static uc_value_t *
-+uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
-+{
-+	struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
-+	uc_value_t *ifname_arg = uc_fn_arg(0);
-+	char prev_ifname[IFNAMSIZ + 1];
-+	struct sta_info *sta;
-+	const char *ifname;
-+	int ret;
-+
-+	if (!hapd || ucv_type(ifname_arg) != UC_STRING)
-+		return NULL;
-+
-+	os_strlcpy(prev_ifname, hapd->conf->iface, sizeof(prev_ifname));
-+	ifname = ucv_string_get(ifname_arg);
-+
-+	hostapd_ubus_free_bss(hapd);
-+	if (interfaces->ctrl_iface_deinit)
-+		interfaces->ctrl_iface_deinit(hapd);
-+
-+	ret = hostapd_drv_if_rename(hapd, WPA_IF_AP_BSS, NULL, ifname);
-+	if (ret)
-+		goto out;
-+
-+	for (sta = hapd->sta_list; sta; sta = sta->next) {
-+		char cur_name[IFNAMSIZ + 1], new_name[IFNAMSIZ + 1];
-+
-+		if (!(sta->flags & WLAN_STA_WDS) || sta->pending_wds_enable)
-+			continue;
-+
-+		snprintf(cur_name, sizeof(cur_name), "%s.sta%d", prev_ifname, sta->aid);
-+		snprintf(new_name, sizeof(new_name), "%s.sta%d", ifname, sta->aid);
-+		hostapd_drv_if_rename(hapd, WPA_IF_AP_VLAN, cur_name, new_name);
-+	}
-+
-+	if (!strncmp(hapd->conf->ssid.vlan, hapd->conf->iface, sizeof(hapd->conf->ssid.vlan)))
-+		os_strlcpy(hapd->conf->ssid.vlan, ifname, sizeof(hapd->conf->ssid.vlan));
-+	os_strlcpy(hapd->conf->iface, ifname, sizeof(hapd->conf->iface));
-+	hostapd_ubus_add_bss(hapd);
-+
-+	hostapd_ucode_update_interfaces();
-+out:
-+	if (interfaces->ctrl_iface_init)
-+		interfaces->ctrl_iface_init(hapd);
-+
-+	return ret ? NULL : ucv_boolean_new(true);
-+}
-+
-+
-+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 },
-+		{ "freq_info", uc_wpa_freq_info },
-+		{ "add_iface", uc_hostapd_add_iface },
-+		{ "remove_iface", uc_hostapd_remove_iface },
-+		{ "udebug_set", uc_wpa_udebug_set },
-+	};
-+	static const uc_function_list_t bss_fns[] = {
-+		{ "ctrl", uc_hostapd_bss_ctrl },
-+		{ "set_config", uc_hostapd_bss_set_config },
-+		{ "rename", uc_hostapd_bss_rename },
-+		{ "delete", uc_hostapd_bss_delete },
-+	};
-+	static const uc_function_list_t iface_fns[] = {
-+		{ "set_bss_order", uc_hostapd_iface_set_bss_order },
-+		{ "add_bss", uc_hostapd_iface_add_bss },
-+		{ "stop", uc_hostapd_iface_stop },
-+		{ "start", uc_hostapd_iface_start },
-+		{ "switch_channel", uc_hostapd_iface_switch_channel },
-+	};
-+	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)
-+{
-+	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));
-+	ucv_put(wpa_ucode_call(2));
-+	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/src/ap/ucode.h b/src/ap/ucode.h
-new file mode 100644
-index 000000000..d00b78716
---- /dev/null
-+++ b/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);
-+
-+#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)
-+{
-+}
-+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/src/utils/build_features.h b/src/utils/build_features.h
-new file mode 100644
-index 000000000..553769ece
---- /dev/null
-+++ b/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/src/utils/ucode.c b/src/utils/ucode.c
-new file mode 100644
-index 000000000..29c753c32
---- /dev/null
-+++ b/src/utils/ucode.c
-@@ -0,0 +1,502 @@
-+#include <unistd.h>
-+#include "ucode.h"
-+#include "utils/eloop.h"
-+#include "crypto/crypto.h"
-+#include "crypto/sha1.h"
-+#include "common/ieee802_11_common.h"
-+#include <linux/netlink.h>
-+#include <linux/genetlink.h>
-+#include <linux/nl80211.h>
-+#include <libubox/uloop.h>
-+#include <ucode/compiler.h>
-+#include <udebug.h>
-+
-+static uc_value_t *registry;
-+static uc_vm_t vm;
-+static struct uloop_timeout gc_timer;
-+static struct udebug ud;
-+static struct udebug_buf ud_log, ud_nl[3];
-+static const struct udebug_buf_meta meta_log = {
-+	.name = "wpa_log",
-+	.format = UDEBUG_FORMAT_STRING,
-+};
-+static const struct udebug_buf_meta meta_nl_ll = {
-+	.name = "wpa_nl_ctrl",
-+	.format = UDEBUG_FORMAT_PACKET,
-+	.sub_format = UDEBUG_DLT_NETLINK,
-+};
-+static const struct udebug_buf_meta meta_nl_tx = {
-+	.name = "wpa_nl_tx",
-+	.format = UDEBUG_FORMAT_PACKET,
-+	.sub_format = UDEBUG_DLT_NETLINK,
-+};
-+#define UDEBUG_FLAG_RX_FRAME	(1ULL << 0)
-+static const struct udebug_buf_flag rx_flags[] = {
-+	{  "rx_frame", UDEBUG_FLAG_RX_FRAME },
-+};
-+static const struct udebug_buf_meta meta_nl_rx = {
-+	.name = "wpa_nl_rx",
-+	.format = UDEBUG_FORMAT_PACKET,
-+	.sub_format = UDEBUG_DLT_NETLINK,
-+	.flags = rx_flags,
-+	.n_flags = ARRAY_SIZE(rx_flags),
-+};
-+static struct udebug_ubus_ring udebug_rings[] = {
-+	{
-+		.buf = &ud_log,
-+		.meta = &meta_log,
-+		.default_entries = 1024,
-+		.default_size = 64 * 1024
-+	},
-+	{
-+		.buf = &ud_nl[0],
-+		.meta = &meta_nl_rx,
-+		.default_entries = 1024,
-+		.default_size = 256 * 1024,
-+	},
-+	{
-+		.buf = &ud_nl[1],
-+		.meta = &meta_nl_tx,
-+		.default_entries = 1024,
-+		.default_size = 64 * 1024,
-+	},
-+	{
-+		.buf = &ud_nl[2],
-+		.meta = &meta_nl_ll,
-+		.default_entries = 1024,
-+		.default_size = 32 * 1024,
-+	}
-+};
-+char *udebug_service;
-+struct udebug_ubus ud_ubus;
-+
-+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_freq_info(uc_vm_t *vm, size_t nargs)
-+{
-+	uc_value_t *freq = uc_fn_arg(0);
-+	uc_value_t *sec = uc_fn_arg(1);
-+	int width = ucv_uint64_get(uc_fn_arg(2));
-+	int freq_val, center_idx, center_ofs;
-+	enum oper_chan_width chanwidth;
-+	enum hostapd_hw_mode hw_mode;
-+	u8 op_class, channel, tmp_channel;
-+	const char *modestr;
-+	int sec_channel = 0;
-+	uc_value_t *ret;
-+
-+	if (ucv_type(freq) != UC_INTEGER)
-+		return NULL;
-+
-+	freq_val = ucv_int64_get(freq);
-+	if (ucv_type(sec) == UC_INTEGER)
-+		sec_channel = ucv_int64_get(sec);
-+	else if (sec)
-+		return NULL;
-+	else if (freq_val > 4000)
-+		sec_channel = (freq_val / 20) & 1 ? 1 : -1;
-+	else
-+		sec_channel = freq_val < 2442 ? 1 : -1;
-+
-+	if (sec_channel != -1 && sec_channel != 1 && sec_channel != 0)
-+		return NULL;
-+
-+	switch (width) {
-+	case 0:
-+		chanwidth = CONF_OPER_CHWIDTH_USE_HT;
-+		break;
-+	case 1:
-+		chanwidth = CONF_OPER_CHWIDTH_80MHZ;
-+		break;
-+	case 2:
-+		chanwidth = CONF_OPER_CHWIDTH_160MHZ;
-+		break;
-+	default:
-+		return NULL;
-+	}
-+
-+	hw_mode = ieee80211_freq_to_channel_ext(freq_val, sec_channel,
-+						chanwidth, &op_class, &channel);
-+	switch (hw_mode) {
-+	case HOSTAPD_MODE_IEEE80211B:
-+		modestr = "b";
-+		break;
-+	case HOSTAPD_MODE_IEEE80211G:
-+		modestr = "g";
-+		break;
-+	case HOSTAPD_MODE_IEEE80211A:
-+		modestr = "a";
-+		break;
-+	case HOSTAPD_MODE_IEEE80211AD:
-+		modestr = "ad";
-+		break;
-+	default:
-+		return NULL;
-+	}
-+
-+	ret = ucv_object_new(vm);
-+	ucv_object_add(ret, "op_class", ucv_int64_new(op_class));
-+	ucv_object_add(ret, "channel", ucv_int64_new(channel));
-+	ucv_object_add(ret, "hw_mode", ucv_int64_new(hw_mode));
-+	ucv_object_add(ret, "hw_mode_str", ucv_get(ucv_string_new(modestr)));
-+	ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
-+	ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
-+
-+	if (!sec_channel)
-+		return ret;
-+
-+	if (freq_val >= 5900)
-+		center_ofs = 0;
-+	else if (freq_val >= 5745)
-+		center_ofs = 20;
-+	else
-+		center_ofs = 35;
-+	tmp_channel = channel - center_ofs;
-+	tmp_channel &= ~((8 << width) - 1);
-+	center_idx = tmp_channel + center_ofs + (4 << width) - 1;
-+
-+	if (freq_val < 3000)
-+		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(0));
-+	else
-+		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(center_idx));
-+	center_idx = (center_idx - channel) * 5 + freq_val;
-+	ucv_object_add(ret, "center_freq1", ucv_int64_new(center_idx));
-+
-+out:
-+	return ret;
-+}
-+
-+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;
-+}
-+
-+static void udebug_printf_hook(int level, const char *fmt, va_list ap)
-+{
-+	udebug_entry_init(&ud_log);
-+	udebug_entry_vprintf(&ud_log, fmt, ap);
-+	udebug_entry_add(&ud_log);
-+}
-+
-+static void udebug_hexdump_hook(int level, const char *title,
-+                const void *data, size_t len)
-+{
-+	char *buf;
-+
-+	udebug_entry_init(&ud_log);
-+	udebug_entry_printf(&ud_log, "%s - hexdump:", title);
-+	buf = udebug_entry_append(&ud_log, NULL, 3 * len);
-+	for (size_t i = 0; i < len; i++)
-+		buf += sprintf(buf, " %02x", *(uint8_t *)(data + i));
-+	udebug_entry_add(&ud_log);
-+}
-+
-+static void udebug_netlink_hook(int tx, const void *data, size_t len)
-+{
-+	struct {
-+		uint16_t pkttype;
-+		uint16_t arphdr;
-+		uint16_t _pad[5];
-+		uint16_t proto;
-+	} hdr = {
-+		.pkttype = host_to_be16(tx ? 7 : 6),
-+		.arphdr = host_to_be16(824),
-+		.proto = host_to_be16(16),
-+	};
-+	const struct nlmsghdr *nlh = data;
-+	const struct genlmsghdr *gnlh = data + NLMSG_HDRLEN;
-+	struct udebug_buf *buf = &ud_nl[!!tx];
-+
-+	if (nlh->nlmsg_type == 0x10)
-+		buf = &ud_nl[2];
-+	else if (!tx && gnlh->cmd == NL80211_CMD_FRAME &&
-+	         !(udebug_buf_flags(buf) & UDEBUG_FLAG_RX_FRAME))
-+		return;
-+
-+	if (!udebug_buf_valid(buf))
-+		return;
-+
-+	udebug_entry_init(buf);
-+	udebug_entry_append(buf, &hdr, sizeof(hdr));
-+	udebug_entry_append(buf, data, len);
-+	udebug_entry_add(buf);
-+}
-+
-+static void
-+wpa_udebug_config(struct udebug_ubus *ctx, struct blob_attr *data,
-+		  bool enabled)
-+{
-+	udebug_ubus_apply_config(&ud, udebug_rings, ARRAY_SIZE(udebug_rings),
-+				 data, enabled);
-+
-+	if (udebug_buf_valid(&ud_log)) {
-+		wpa_printf_hook = udebug_printf_hook;
-+		wpa_hexdump_hook = udebug_hexdump_hook;
-+	} else {
-+		wpa_printf_hook = NULL;
-+		wpa_hexdump_hook = NULL;
-+	}
-+
-+	if (udebug_buf_valid(&ud_nl[0]) ||
-+	    udebug_buf_valid(&ud_nl[1]) ||
-+	    udebug_buf_valid(&ud_nl[2]))
-+		wpa_netlink_hook = udebug_netlink_hook;
-+	else
-+		wpa_netlink_hook = NULL;
-+}
-+
-+uc_value_t *uc_wpa_udebug_set(uc_vm_t *vm, size_t nargs)
-+{
-+	uc_value_t *name = uc_fn_arg(0);
-+	uc_value_t *ubus = uc_fn_arg(1);
-+	static bool enabled = false;
-+	struct ubus_context *ctx;
-+	bool cur_en;
-+
-+	cur_en = ucv_type(name) == UC_STRING;
-+	ctx = ucv_resource_data(ubus, "ubus.connection");
-+	if (!ctx)
-+		cur_en = false;
-+
-+	if (enabled == cur_en)
-+		return ucv_boolean_new(true);
-+
-+	enabled = cur_en;
-+	if (enabled) {
-+		udebug_service = strdup(ucv_string_get(name));
-+		udebug_init(&ud);
-+		udebug_auto_connect(&ud, NULL);
-+		udebug_ubus_init(&ud_ubus, ctx, udebug_service, wpa_udebug_config);
-+	} else {
-+		udebug_ubus_free(&ud_ubus);
-+		for (size_t i = 0; i < ARRAY_SIZE(udebug_rings); i++)
-+			if (udebug_buf_valid(udebug_rings[i].buf))
-+				udebug_buf_free(udebug_rings[i].buf);
-+		udebug_free(&ud);
-+		free(udebug_service);
-+	}
-+
-+	return ucv_boolean_new(true);
-+}
-+
-+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;
-+}
-+
-+int wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val)
-+{
-+	uc_value_t *data;
-+	int i = 0;
-+
-+	while (ucv_array_get(reg, i))
-+		i++;
-+
-+	ucv_array_set(reg, i, ucv_get(val));
-+
-+	return 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);
-+	void **dataptr;
-+
-+	if (!val)
-+		return NULL;
-+
-+	ucv_array_set(reg, idx - 1, NULL);
-+	dataptr = ucv_resource_dataptr(val, NULL);
-+	if (dataptr)
-+		*dataptr = 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/src/utils/ucode.h b/src/utils/ucode.h
-new file mode 100644
-index 000000000..c083241e0
---- /dev/null
-+++ b/src/utils/ucode.h
-@@ -0,0 +1,30 @@
-+#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);
-+
-+int wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val);
-+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_udebug_set(uc_vm_t *vm, size_t nargs);
-+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);
-+uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs);
-+
-+#endif
-diff --git a/wpa_supplicant/ubus.c b/wpa_supplicant/ubus.c
-new file mode 100644
-index 000000000..1c477f0c0
---- /dev/null
-+++ b/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/wpa_supplicant/ubus.h b/wpa_supplicant/ubus.h
-new file mode 100644
-index 000000000..f6681cb26
---- /dev/null
-+++ b/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/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
-new file mode 100644
-index 000000000..397f85bde
---- /dev/null
-+++ b/wpa_supplicant/ucode.c
-@@ -0,0 +1,299 @@
-+#include "utils/includes.h"
-+#include "utils/common.h"
-+#include "utils/ucode.h"
-+#include "drivers/driver.h"
-+#include "ap/hostapd.h"
-+#include "wpa_supplicant_i.h"
-+#include "wps_supplicant.h"
-+#include "bss.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_s->ucode.idx = wpa_ucode_registry_add(iface_registry, val);
-+
-+	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);
-+}
-+
-+void wpas_ucode_update_state(struct wpa_supplicant *wpa_s)
-+{
-+	const char *state;
-+	uc_value_t *val;
-+
-+	val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
-+	if (!val)
-+		return;
-+
-+	if (wpa_ucode_call_prepare("state"))
-+		return;
-+
-+	state = wpa_supplicant_state_txt(wpa_s->wpa_state);
-+	uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
-+	uc_value_push(ucv_get(val));
-+	uc_value_push(ucv_get(ucv_string_new(state)));
-+	ucv_put(wpa_ucode_call(3));
-+	ucv_gc(vm);
-+}
-+
-+void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data)
-+{
-+	const char *state;
-+	uc_value_t *val;
-+
-+	if (event != EVENT_CH_SWITCH_STARTED)
-+		return;
-+
-+	val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
-+	if (!val)
-+		return;
-+
-+	if (wpa_ucode_call_prepare("event"))
-+		return;
-+
-+	uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
-+	uc_value_push(ucv_get(val));
-+	uc_value_push(ucv_get(ucv_string_new(event_to_string(event))));
-+	val = ucv_object_new(vm);
-+	uc_value_push(ucv_get(val));
-+
-+	if (event == EVENT_CH_SWITCH_STARTED) {
-+		ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count));
-+		ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq));
-+		ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
-+		ucv_object_add(val, "center_freq1", ucv_int64_new(data->ch_switch.cf1));
-+		ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
-+	}
-+
-+	ucv_put(wpa_ucode_call(4));
-+	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 *driver = ucv_object_get(info, "driver", NULL);
-+	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);
-+	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),
-+	};
-+
-+	if (driver) {
-+		const char *drvname;
-+		if (ucv_type(driver) != UC_STRING)
-+			goto out;
-+
-+		iface.driver = NULL;
-+		drvname = ucv_string_get(driver);
-+		for (int i = 0; wpa_drivers[i]; i++) {
-+			if (!strcmp(drvname, wpa_drivers[i]->name))
-+				iface.driver = wpa_drivers[i]->name;
-+		}
-+
-+		if (!iface.driver)
-+			goto out;
-+	}
-+
-+	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);
-+}
-+
-+static uc_value_t *
-+uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
-+{
-+	struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
-+	struct wpa_bss *bss;
-+	uc_value_t *ret, *val;
-+
-+	if (!wpa_s)
-+		return NULL;
-+
-+	ret = ucv_object_new(vm);
-+
-+	val = ucv_string_new(wpa_supplicant_state_txt(wpa_s->wpa_state));
-+	ucv_object_add(ret, "state", ucv_get(val));
-+
-+	bss = wpa_s->current_bss;
-+	if (bss) {
-+		int sec_chan = 0;
-+		const u8 *ie;
-+
-+		ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
-+		if (ie && ie[1] >= 2) {
-+			const struct ieee80211_ht_operation *ht_oper;
-+			int sec;
-+
-+			ht_oper = (const void *) (ie + 2);
-+			sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
-+			if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
-+				sec_chan = 1;
-+			else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
-+				sec_chan = -1;
-+		}
-+
-+		ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
-+		ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
-+	}
-+
-+#ifdef CONFIG_MESH
-+	if (wpa_s->ifmsh) {
-+		struct hostapd_iface *ifmsh = wpa_s->ifmsh;
-+
-+		ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(ifmsh->conf->secondary_channel));
-+		ucv_object_add(ret, "frequency", ucv_int64_new(ifmsh->freq));
-+	}
-+#endif
-+
-+	return 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 },
-+		{ "udebug_set", uc_wpa_udebug_set },
-+	};
-+	static const uc_function_list_t iface_fns[] = {
-+		{ "status", uc_wpas_iface_status },
-+	};
-+	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, "wpas.iface", iface_fns, NULL);
-+
-+	iface_registry = ucv_array_new(vm);
-+	uc_vm_registry_set(vm, "wpas.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/wpa_supplicant/ucode.h b/wpa_supplicant/ucode.h
-new file mode 100644
-index 000000000..a429a0ed8
---- /dev/null
-+++ b/wpa_supplicant/ucode.h
-@@ -0,0 +1,49 @@
-+#ifndef __WPAS_UCODE_H
-+#define __WPAS_UCODE_H
-+
-+#include "utils/ucode.h"
-+
-+struct wpa_global;
-+union wpa_event_data;
-+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);
-+void wpas_ucode_update_state(struct wpa_supplicant *wpa_s);
-+void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data);
-+#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)
-+{
-+}
-+
-+static inline void wpas_ucode_update_state(struct wpa_supplicant *wpa_s)
-+{
-+}
-+
-+static inline void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data)
-+{
-+}
-+
-+#endif
-+
-+#endif
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0027-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0027-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch
new file mode 100644
index 0000000..09ed283
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0027-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch
@@ -0,0 +1,189 @@
+From a6e614017f8f6c007fa3ee2cf1543e6460097f10 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 10:51:47 +0800
+Subject: [PATCH 027/126] mtk: hostapd: Add sta-assisted DFS state update
+ mechanism
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c                       | 20 ++++++++++++++++++++
+ src/ap/dfs.h                       |  3 +++
+ src/ap/drv_callbacks.c             | 28 ++++++++++++++++++++++++++++
+ src/common/wpa_ctrl.h              |  1 +
+ src/drivers/driver.h               | 14 ++++++++++++++
+ src/drivers/driver_nl80211_event.c |  6 ++++++
+ src/drivers/nl80211_copy.h         |  6 ++++++
+ 7 files changed, 78 insertions(+)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index aaaea0edc..4f8ef8111 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1535,6 +1535,26 @@ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+ }
+ 
+ 
++int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
++				 int ht_enabled, int chan_offset, int chan_width,
++				 int cf1, int cf2, u32 state)
++{
++	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_STA_UPDATE
++		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d state=%s",
++		freq, ht_enabled, chan_offset, chan_width, cf1, cf2,
++		(state == HOSTAPD_CHAN_DFS_AVAILABLE) ? "available" : "usable");
++
++	/* Proceed only if DFS is not offloaded to the driver */
++	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
++		return 0;
++
++	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
++		      cf1, cf2, state);
++
++	return 0;
++}
++
++
+ int hostapd_is_dfs_required(struct hostapd_iface *iface)
+ {
+ 	int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res;
+diff --git a/src/ap/dfs.h b/src/ap/dfs.h
+index c2556d2d9..25ba29ca1 100644
+--- a/src/ap/dfs.h
++++ b/src/ap/dfs.h
+@@ -30,6 +30,9 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+ 			     int ht_enabled,
+ 			     int chan_offset, int chan_width, int cf1, int cf2);
++int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
++				 int ht_enabled, int chan_offset, int chan_width,
++				 int cf1, int cf2, u32 state);
+ int hostapd_is_dfs_required(struct hostapd_iface *iface);
+ int hostapd_is_dfs_chan_available(struct hostapd_iface *iface);
+ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 6e76f697a..1d40943d6 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2270,6 +2270,24 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
+ 			      radar->cf1, radar->cf2);
+ }
+ 
++static void hostapd_event_dfs_sta_cac_skipped(struct hostapd_data *hapd,
++					      struct dfs_event *radar)
++{
++	wpa_printf(MSG_DEBUG, "DFS CAC skipped (by STA) on %d MHz", radar->freq);
++	hostapd_dfs_sta_update_state(hapd->iface, radar->freq, radar->ht_enabled,
++				     radar->chan_offset, radar->chan_width,
++				     radar->cf1, radar->cf2, HOSTAPD_CHAN_DFS_AVAILABLE);
++}
++
++static void hostapd_event_dfs_sta_cac_expired(struct hostapd_data *hapd,
++					      struct dfs_event *radar)
++{
++	wpa_printf(MSG_DEBUG, "DFS CAC expired (by STA) on %d MHz", radar->freq);
++	hostapd_dfs_sta_update_state(hapd->iface, radar->freq, radar->ht_enabled,
++				     radar->chan_offset, radar->chan_width,
++				     radar->cf1, radar->cf2, HOSTAPD_CHAN_DFS_USABLE);
++}
++
+ #endif /* NEED_AP_MLME */
+ 
+ 
+@@ -2687,6 +2705,16 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ 		hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
+ 		hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
+ 		break;
++	case EVENT_DFS_STA_CAC_SKIPPED:
++		if (!data)
++			break;
++		hostapd_event_dfs_sta_cac_skipped(hapd, &data->dfs_event);
++		break;
++	case EVENT_DFS_STA_CAC_EXPIRED:
++		if (!data)
++			break;
++		hostapd_event_dfs_sta_cac_expired(hapd, &data->dfs_event);
++		break;
+ 	case EVENT_CHANNEL_LIST_CHANGED:
+ 		/* channel list changed (regulatory?), update channel list */
+ 		/* TODO: check this. hostapd_get_hw_features() initializes
+diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
+index d1ce1dd29..4700c5487 100644
+--- a/src/common/wpa_ctrl.h
++++ b/src/common/wpa_ctrl.h
+@@ -383,6 +383,7 @@ extern "C" {
+ #define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED "
+ #define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED "
+ #define DFS_EVENT_PRE_CAC_EXPIRED "DFS-PRE-CAC-EXPIRED "
++#define DFS_EVENT_STA_UPDATE "DFS-STA-UPDATE "
+ 
+ #define AP_CSA_FINISHED "AP-CSA-FINISHED "
+ 
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 348cf9c88..cccc44147 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5926,6 +5926,20 @@ enum wpa_event_type {
+ 	 * EVENT_LINK_RECONFIG - Notification that AP links removed
+ 	 */
+ 	EVENT_LINK_RECONFIG,
++
++	/**
++	 * EVENT_DFS_STA_CAC_SKIPPED - Notification that CAC has been skipped
++	 *
++	 * The channel in the notification is now marked as available.
++	 */
++	EVENT_DFS_STA_CAC_SKIPPED,
++
++	/**
++	 * EVENT_DFS_STA_CAC_EXPIRED - Notification that CAC has expired
++	 *
++	 * The channel in the notification is now marked as usable.
++	 */
++	EVENT_DFS_STA_CAC_EXPIRED,
+ };
+ 
+ 
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 1787dbd99..8ba479861 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -2539,6 +2539,12 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
+ 	case NL80211_RADAR_CAC_STARTED:
+ 		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
+ 		break;
++	case NL80211_RADAR_STA_CAC_SKIPPED:
++		wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
++		break;
++	case NL80211_RADAR_STA_CAC_EXPIRED:
++		wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_EXPIRED, &data);
++		break;
+ 	default:
+ 		wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
+ 			   "received", event_type);
+diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
+index f97f5adc8..622fa71fd 100644
+--- a/src/drivers/nl80211_copy.h
++++ b/src/drivers/nl80211_copy.h
+@@ -6843,6 +6843,10 @@ enum nl80211_smps_mode {
+  *	applicable for ETSI dfs domain where pre-CAC is valid for ever.
+  * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
+  *	should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
++ * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
++ *	when receiving CSA/assoc resp
++ * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
++ *	when STA is disconnected or leaving the channel
+  */
+ enum nl80211_radar_event {
+ 	NL80211_RADAR_DETECTED,
+@@ -6851,6 +6855,8 @@ enum nl80211_radar_event {
+ 	NL80211_RADAR_NOP_FINISHED,
+ 	NL80211_RADAR_PRE_CAC_EXPIRED,
+ 	NL80211_RADAR_CAC_STARTED,
++	NL80211_RADAR_STA_CAC_SKIPPED,
++	NL80211_RADAR_STA_CAC_EXPIRED,
+ };
+ 
+ /**
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0028-hostapd-sync-2024-01-18-openwrt-trunk-patch-folder.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0028-hostapd-sync-2024-01-18-openwrt-trunk-patch-folder.patch
deleted file mode 100644
index 1dff1f3..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0028-hostapd-sync-2024-01-18-openwrt-trunk-patch-folder.patch
+++ /dev/null
@@ -1,14442 +0,0 @@
-From 2eff271199b22ce44432d531e8b44809368ac5d2 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Tue, 23 Jan 2024 17:07:17 +0800
-Subject: [PATCH 028/104] hostapd: sync 2024-01-18 openwrt/trunk patch folder
-
----
- hostapd/Makefile                          |  149 +-
- hostapd/config_file.c                     |   39 +-
- hostapd/ctrl_iface.c                      |    4 +
- hostapd/defconfig                         |   15 +-
- hostapd/hostapd_cli.c                     |   10 +-
- hostapd/main.c                            |   21 +-
- src/ap/acs.c                              |    5 +-
- src/ap/airtime_policy.c                   |   15 +-
- src/ap/ap_config.h                        |    8 +
- src/ap/ap_drv_ops.c                       |    5 +-
- src/ap/ap_drv_ops.h                       |   21 +-
- src/ap/beacon.c                           |   14 +-
- src/ap/ctrl_iface_ap.c                    |   45 +-
- src/ap/dfs.c                              |   22 +-
- src/ap/drv_callbacks.c                    |   16 +-
- src/ap/hostapd.c                          |   51 +-
- src/ap/hostapd.h                          |   42 +
- src/ap/hw_features.c                      |    3 +-
- src/ap/ieee802_11.c                       |   50 +-
- src/ap/ieee802_11_ht.c                    |   15 +
- src/ap/ieee802_11_shared.c                |    2 -
- src/ap/ieee802_11_vht.c                   |   17 +
- src/ap/ieee802_1x.c                       |    6 +
- src/ap/ndisc_snoop.c                      |    1 +
- src/ap/rrm.c                              |    8 +
- src/ap/sta_info.c                         |   35 +-
- src/ap/sta_info.h                         |   12 +-
- src/ap/vlan_full.c                        |    4 +
- src/ap/vlan_init.c                        |    8 +-
- src/ap/wnm_ap.c                           |   16 +-
- src/ap/wpa_auth.c                         |    3 +-
- src/ap/wpa_auth_glue.c                    |    9 +-
- src/ap/wps_hostapd.c                      |    6 +-
- src/ap/x_snoop.c                          |   22 +-
- src/common/dpp_crypto.c                   |   10 +-
- src/common/hw_features_common.c           |    1 +
- src/common/ieee802_11_defs.h              |    2 +
- src/common/sae.c                          |    7 +
- src/common/wpa_common.c                   |   40 +-
- src/common/wpa_ctrl.c                     |   10 +-
- src/crypto/Makefile                       |  129 +-
- src/crypto/crypto_mbedtls.c               | 4228 +++++++++++++++++++++
- src/crypto/crypto_module_tests.c          |  134 +
- src/crypto/crypto_wolfssl.c               |   18 +
- src/crypto/tls_mbedtls.c                  | 3313 ++++++++++++++++
- src/drivers/driver.h                      |   36 +-
- src/drivers/driver_nl80211.c              |  300 +-
- src/drivers/driver_nl80211_capa.c         |    4 +
- src/drivers/driver_nl80211_event.c        |    5 +
- src/drivers/driver_nl80211_scan.c         |    2 +-
- src/drivers/drivers.c                     |    4 +
- src/drivers/drivers.mak                   |    4 +-
- src/drivers/rfkill.h                      |   16 +
- src/radius/radius_client.c                |   34 +
- src/radius/radius_client.h                |    2 +
- src/radius/radius_das.c                   |  201 +-
- src/radius/radius_das.h                   |    1 +
- src/radius/radius_server.c                |   61 +-
- src/rsn_supp/wpa.c                        |    3 +
- src/tls/Makefile                          |   11 +
- src/utils/eloop.c                         |   20 +-
- src/utils/eloop.h                         |    8 +
- src/utils/uloop.c                         |   64 +
- src/utils/wpa_debug.c                     |   49 +-
- src/utils/wpa_debug.h                     |   73 +-
- tests/Makefile                            |   65 +-
- tests/hwsim/example-hostapd.config        |   10 +-
- tests/hwsim/example-wpa_supplicant.config |   11 +-
- tests/hwsim/test_ap_eap.py                |  112 +-
- tests/hwsim/test_ap_ft.py                 |    4 +-
- tests/hwsim/test_authsrv.py               |    9 +-
- tests/hwsim/test_dpp.py                   |   19 +-
- tests/hwsim/test_erp.py                   |   16 +-
- tests/hwsim/test_fils.py                  |   12 +
- tests/hwsim/test_pmksa_cache.py           |    4 +-
- tests/hwsim/test_sae.py                   |    7 +
- tests/hwsim/test_suite_b.py               |    3 +
- tests/hwsim/test_wpas_ctrl.py             |    2 +-
- tests/hwsim/utils.py                      |    8 +-
- tests/test-crypto_module.c                |   16 +
- tests/test-https.c                        |   12 +-
- tests/test-https_server.c                 |   12 +-
- wpa_supplicant/Makefile                   |  144 +-
- wpa_supplicant/ap.c                       |   22 +-
- wpa_supplicant/config.c                   |   95 +
- wpa_supplicant/config_file.c              |    8 +-
- wpa_supplicant/config_ssid.h              |    7 +
- wpa_supplicant/ctrl_iface.c               |   12 +-
- wpa_supplicant/defconfig                  |    6 +-
- wpa_supplicant/eapol_test.c               |   11 +
- wpa_supplicant/events.c                   |   13 +-
- wpa_supplicant/main.c                     |   14 +-
- wpa_supplicant/mesh.c                     |    3 +
- wpa_supplicant/wpa_cli.c                  |    9 +
- wpa_supplicant/wpa_priv.c                 |    8 +-
- wpa_supplicant/wpa_supplicant.c           |   75 +-
- wpa_supplicant/wpa_supplicant_i.h         |    6 +
- wpa_supplicant/wps_supplicant.c           |    3 +
- wpa_supplicant/wps_supplicant.h           |    3 +-
- 99 files changed, 9786 insertions(+), 464 deletions(-)
- create mode 100644 src/crypto/crypto_mbedtls.c
- create mode 100644 src/crypto/tls_mbedtls.c
- create mode 100644 src/utils/uloop.c
- create mode 100644 tests/test-crypto_module.c
-
-diff --git a/hostapd/Makefile b/hostapd/Makefile
-index 405e05e5f..f5c1dc029 100644
---- a/hostapd/Makefile
-+++ b/hostapd/Makefile
-@@ -1,6 +1,7 @@
- ALL=hostapd hostapd_cli
- CONFIG_FILE = .config
- 
-+-include $(if $(MULTICALL), ../wpa_supplicant/.config)
- include ../src/build.rules
- 
- ifdef LIBS
-@@ -62,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
-@@ -171,6 +176,24 @@ OBJS += ../src/common/hw_features_common.o
- 
- OBJS += ../src/eapol_auth/eapol_auth_sm.o
- 
-+ifdef CONFIG_UBUS
-+CFLAGS += -DUBUS_SUPPORT
-+OBJS += ../src/ap/ubus.o
-+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
- CFLAGS += -O0 -fprofile-arcs -ftest-coverage -U_FORTIFY_SOURCE
-@@ -205,7 +228,8 @@ endif
- 
- ifdef CONFIG_NO_VLAN
- CFLAGS += -DCONFIG_NO_VLAN
--else
-+endif
-+ifneq ($(findstring CONFIG_NO_VLAN,$(CFLAGS)), CONFIG_NO_VLAN)
- OBJS += ../src/ap/vlan_init.o
- OBJS += ../src/ap/vlan_ifconfig.o
- OBJS += ../src/ap/vlan.o
-@@ -225,6 +249,9 @@ endif
- ifdef CONFIG_NO_CTRL_IFACE
- CFLAGS += -DCONFIG_NO_CTRL_IFACE
- else
-+ifdef CONFIG_CTRL_IFACE_MIB
-+CFLAGS += -DCONFIG_CTRL_IFACE_MIB
-+endif
- ifeq ($(CONFIG_CTRL_IFACE), udp)
- CFLAGS += -DCONFIG_CTRL_IFACE_UDP
- else
-@@ -332,6 +359,7 @@ ifdef CONFIG_FILS
- CFLAGS += -DCONFIG_FILS
- OBJS += ../src/ap/fils_hlp.o
- NEED_SHA384=y
-+NEED_HMAC_SHA384_KDF=y
- NEED_AES_SIV=y
- ifdef CONFIG_FILS_SK_PFS
- CFLAGS += -DCONFIG_FILS_SK_PFS
-@@ -364,10 +392,14 @@ CFLAGS += -DCONFIG_MBO
- OBJS += ../src/ap/mbo_ap.o
- endif
- 
-+ifndef MULTICALL
-+CFLAGS += -DNO_SUPPLICANT
-+endif
-+
- include ../src/drivers/drivers.mak
--OBJS += $(DRV_AP_OBJS)
--CFLAGS += $(DRV_AP_CFLAGS)
--LDFLAGS += $(DRV_AP_LDFLAGS)
-+OBJS += $(sort $(DRV_AP_OBJS) $(if $(MULTICALL),$(DRV_WPA_OBJS)))
-+CFLAGS += $(DRV_AP_CFLAGS) $(if $(MULTICALL),$(DRV_WPA_CFLAGS))
-+LDFLAGS += $(DRV_AP_LDFLAGS) $(if $(MULTICALL),$(DRV_WPA_LDFLAGS))
- LIBS += $(DRV_AP_LIBS)
- 
- ifdef CONFIG_L2_PACKET
-@@ -714,6 +746,7 @@ CFLAGS += -DCONFIG_TLSV12
- endif
- 
- ifeq ($(CONFIG_TLS), wolfssl)
-+CFLAGS += -DCONFIG_TLS_WOLFSSL
- CONFIG_CRYPTO=wolfssl
- ifdef TLS_FUNCS
- OBJS += ../src/crypto/tls_wolfssl.o
-@@ -734,6 +767,7 @@ endif
- endif
- 
- ifeq ($(CONFIG_TLS), openssl)
-+CFLAGS += -DCONFIG_TLS_OPENSSL
- CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
- CONFIG_CRYPTO=openssl
- ifdef TLS_FUNCS
-@@ -763,7 +797,39 @@ endif
- CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
- endif
- 
-+ifeq ($(CONFIG_TLS), mbedtls)
-+CFLAGS += -DCONFIG_TLS_MBEDTLS
-+ifndef CONFIG_CRYPTO
-+CONFIG_CRYPTO=mbedtls
-+endif
-+ifdef TLS_FUNCS
-+OBJS += ../src/crypto/tls_mbedtls.o
-+LIBS += -lmbedtls
-+ifndef CONFIG_DPP
-+LIBS += -lmbedx509
-+endif
-+endif
-+OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+HOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+SOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+ifeq ($(CONFIG_CRYPTO), mbedtls)
-+ifdef CONFIG_DPP
-+LIBS += -lmbedx509
-+LIBS_h += -lmbedx509
-+LIBS_n += -lmbedx509
-+LIBS_s += -lmbedx509
-+endif
-+LIBS += -lmbedcrypto
-+LIBS_h += -lmbedcrypto
-+LIBS_n += -lmbedcrypto
-+LIBS_s += -lmbedcrypto
-+# XXX: create a config option?
-+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
-+endif
-+endif
-+
- ifeq ($(CONFIG_TLS), gnutls)
-+CFLAGS += -DCONFIG_TLS_GNUTLS
- ifndef CONFIG_CRYPTO
- # default to libgcrypt
- CONFIG_CRYPTO=gnutls
-@@ -794,6 +860,7 @@ endif
- endif
- 
- ifeq ($(CONFIG_TLS), internal)
-+CFLAGS += -DCONFIG_TLS_INTERNAL
- ifndef CONFIG_CRYPTO
- CONFIG_CRYPTO=internal
- endif
-@@ -872,6 +939,7 @@ endif
- endif
- 
- ifeq ($(CONFIG_TLS), linux)
-+CFLAGS += -DCONFIG_TLS_INTERNAL
- OBJS += ../src/crypto/crypto_linux.o
- ifdef TLS_FUNCS
- OBJS += ../src/crypto/crypto_internal-rsa.o
-@@ -942,9 +1010,11 @@ endif
- 
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-wrap.o
- endif
- endif
-+endif
- ifdef NEED_AES_EAX
- AESOBJS += ../src/crypto/aes-eax.o
- NEED_AES_CTR=y
-@@ -954,38 +1024,48 @@ AESOBJS += ../src/crypto/aes-siv.o
- NEED_AES_CTR=y
- endif
- ifdef NEED_AES_CTR
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-ctr.o
- endif
-+endif
- ifdef NEED_AES_ENCBLOCK
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-encblock.o
- endif
-+endif
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-omac1.o
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_UNWRAP
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- NEED_AES_DEC=y
- AESOBJS += ../src/crypto/aes-unwrap.o
- endif
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_CBC
- NEED_AES_DEC=y
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-cbc.o
- endif
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_DEC
- ifdef CONFIG_INTERNAL_AES
- AESOBJS += ../src/crypto/aes-internal-dec.o
-@@ -1000,12 +1080,16 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-prf.o
-+endif
- ifdef CONFIG_INTERNAL_SHA1
- SHA1OBJS += ../src/crypto/sha1-internal.o
- ifdef NEED_FIPS186_2_PRF
-@@ -1014,16 +1098,22 @@ endif
- endif
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
- endif
- endif
-+endif
- ifdef NEED_T_PRF
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-tprf.o
- endif
-+endif
- ifdef NEED_TLS_PRF
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-tlsprf.o
- endif
- endif
-+endif
- 
- ifdef NEED_SHA1
- OBJS += $(SHA1OBJS)
-@@ -1033,11 +1123,13 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/md5.o
- endif
- endif
- endif
- endif
-+endif
- 
- ifdef NEED_MD5
- ifdef CONFIG_INTERNAL_MD5
-@@ -1076,56 +1168,81 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256-prf.o
-+endif
- ifdef CONFIG_INTERNAL_SHA256
- OBJS += ../src/crypto/sha256-internal.o
- endif
- ifdef NEED_TLS_PRF_SHA256
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256-tlsprf.o
- endif
-+endif
- ifdef NEED_TLS_PRF_SHA384
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-tlsprf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA256_KDF
-+CFLAGS += -DCONFIG_HMAC_SHA256_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256-kdf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA384_KDF
-+CFLAGS += -DCONFIG_HMAC_SHA384_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-kdf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA512_KDF
-+CFLAGS += -DCONFIG_HMAC_SHA512_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512-kdf.o
- endif
-+endif
- ifdef NEED_SHA384
- CFLAGS += -DCONFIG_SHA384
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-prf.o
- endif
-+endif
- ifdef NEED_SHA512
- CFLAGS += -DCONFIG_SHA512
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512-prf.o
- endif
-+endif
- 
- ifdef CONFIG_INTERNAL_SHA384
- CFLAGS += -DCONFIG_INTERNAL_SHA384
-@@ -1170,11 +1287,13 @@ HOBJS += $(SHA1OBJS)
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- HOBJS += ../src/crypto/md5.o
- endif
- endif
- endif
- endif
-+endif
- 
- ifdef CONFIG_RADIUS_SERVER
- CFLAGS += -DRADIUS_SERVER
-@@ -1311,8 +1430,14 @@ install: $(addprefix $(DESTDIR)$(BINDIR)/,$(ALL))
- _OBJS_VAR := OBJS
- include ../src/objs.mk
- 
-+hostapd_multi.a: $(BCHECK) $(OBJS)
-+	$(Q)$(CC) -c -o hostapd_multi.o -Dmain=hostapd_main $(CFLAGS) main.c
-+	@$(E) "  CC " $<
-+	@rm -f $@
-+	@$(AR) cr $@ hostapd_multi.o $(OBJS)
-+
- hostapd: $(OBJS)
--	$(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
-+	+$(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
- 	@$(E) "  LD " $@
- 
- ifdef CONFIG_WPA_TRACE
-@@ -1323,7 +1448,7 @@ _OBJS_VAR := OBJS_c
- include ../src/objs.mk
- 
- hostapd_cli: $(OBJS_c)
--	$(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
-+	+$(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
- 	@$(E) "  LD " $@
- 
- NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS)
-@@ -1347,7 +1472,9 @@ NOBJS += ../src/utils/trace.o
- endif
- 
- HOBJS += hlr_auc_gw.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).o ../src/utils/wpabuf.o ../src/crypto/milenage.o
-+ifneq ($(CONFIG_TLS), mbedtls)
- HOBJS += ../src/crypto/aes-encblock.o
-+endif
- ifdef CONFIG_INTERNAL_AES
- HOBJS += ../src/crypto/aes-internal.o
- HOBJS += ../src/crypto/aes-internal-enc.o
-@@ -1370,13 +1497,17 @@ SOBJS += ../src/common/sae.o
- SOBJS += ../src/common/sae_pk.o
- SOBJS += ../src/common/dragonfly.o
- SOBJS += $(AESOBJS)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SOBJS += ../src/crypto/sha256-prf.o
- SOBJS += ../src/crypto/sha384-prf.o
- SOBJS += ../src/crypto/sha512-prf.o
-+endif
- SOBJS += ../src/crypto/dh_groups.o
-+ifneq ($(CONFIG_TLS), mbedtls)
- SOBJS += ../src/crypto/sha256-kdf.o
- SOBJS += ../src/crypto/sha384-kdf.o
- SOBJS += ../src/crypto/sha512-kdf.o
-+endif
- 
- _OBJS_VAR := NOBJS
- include ../src/objs.mk
-@@ -1385,6 +1516,12 @@ include ../src/objs.mk
- _OBJS_VAR := SOBJS
- include ../src/objs.mk
- 
-+dump_cflags:
-+	@printf "%s " "$(CFLAGS)"
-+
-+dump_ldflags:
-+	@printf "%s " "$(LDFLAGS) $(LIBS) $(EXTRALIBS)"
-+
- nt_password_hash: $(NOBJS)
- 	$(Q)$(CC) $(LDFLAGS) -o nt_password_hash $(NOBJS) $(LIBS_n)
- 	@$(E) "  LD " $@
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 261905368..0094db279 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -1229,6 +1229,8 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf,
- 		conf->vht_capab |= VHT_CAP_RX_ANTENNA_PATTERN;
- 	if (os_strstr(capab, "[TX-ANTENNA-PATTERN]"))
- 		conf->vht_capab |= VHT_CAP_TX_ANTENNA_PATTERN;
-+	if (os_strstr(capab, "[EXT-NSS-BW-SUPP]"))
-+		conf->vht_capab |= VHT_CAP_EXTENDED_NSS_BW_SUPPORT;
- 	return 0;
- }
- #endif /* CONFIG_IEEE80211AC */
-@@ -1678,6 +1680,8 @@ static int parse_anqp_elem(struct hostapd_bss_config *bss, char *buf, int line)
- 	return 0;
- }
- 
-+#endif /* CONFIG_INTERWORKING */
-+
- 
- static int parse_qos_map_set(struct hostapd_bss_config *bss,
- 			     char *buf, int line)
-@@ -1719,8 +1723,6 @@ static int parse_qos_map_set(struct hostapd_bss_config *bss,
- 	return 0;
- }
- 
--#endif /* CONFIG_INTERWORKING */
--
- 
- #ifdef CONFIG_HS20
- static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf,
-@@ -2630,8 +2632,12 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 			   sizeof(conf->bss[0]->iface));
- 	} else if (os_strcmp(buf, "bridge") == 0) {
- 		os_strlcpy(bss->bridge, pos, sizeof(bss->bridge));
-+		if (!bss->wds_bridge[0])
-+			os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge));
- 	} else if (os_strcmp(buf, "bridge_hairpin") == 0) {
- 		bss->bridge_hairpin = atoi(pos);
-+	} else if (os_strcmp(buf, "snoop_iface") == 0) {
-+		os_strlcpy(bss->snoop_iface, pos, sizeof(bss->snoop_iface));
- 	} else if (os_strcmp(buf, "vlan_bridge") == 0) {
- 		os_strlcpy(bss->vlan_bridge, pos, sizeof(bss->vlan_bridge));
- 	} else if (os_strcmp(buf, "wds_bridge") == 0) {
-@@ -2998,6 +3004,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 	} else if (os_strcmp(buf, "iapp_interface") == 0) {
- 		wpa_printf(MSG_INFO, "DEPRECATED: iapp_interface not used");
- #endif /* CONFIG_IAPP */
-+	} else if (os_strcmp(buf, "dynamic_own_ip_addr") == 0) {
-+		bss->dynamic_own_ip_addr = atoi(pos);
- 	} else if (os_strcmp(buf, "own_ip_addr") == 0) {
- 		if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) {
- 			wpa_printf(MSG_ERROR,
-@@ -3222,6 +3230,14 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 				   line, bss->max_num_sta, MAX_STA_COUNT);
- 			return 1;
- 		}
-+	} else if (os_strcmp(buf, "iface_max_num_sta") == 0) {
-+		conf->max_num_sta = atoi(pos);
-+		if (conf->max_num_sta < 0 ||
-+		    conf->max_num_sta > MAX_STA_COUNT) {
-+			wpa_printf(MSG_ERROR, "Line %d: Invalid max_num_sta=%d; allowed range 0..%d",
-+				   line, conf->max_num_sta, MAX_STA_COUNT);
-+			return 1;
-+		}
- 	} else if (os_strcmp(buf, "wpa") == 0) {
- 		bss->wpa = atoi(pos);
- 	} else if (os_strcmp(buf, "extended_key_id") == 0) {
-@@ -3373,6 +3389,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		wpa_printf(MSG_INFO,
- 			   "Line %d: Obsolete peerkey parameter ignored", line);
- #ifdef CONFIG_IEEE80211R_AP
-+	} else if (os_strcmp(buf, "ft_iface") == 0) {
-+		os_strlcpy(bss->ft_iface, pos, sizeof(bss->ft_iface));
- 	} else if (os_strcmp(buf, "mobility_domain") == 0) {
- 		if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
- 		    hexstr2bin(pos, bss->mobility_domain,
-@@ -3742,6 +3760,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- #ifndef CONFIG_NO_VLAN
- 	} else if (os_strcmp(buf, "dynamic_vlan") == 0) {
- 		bss->ssid.dynamic_vlan = atoi(pos);
-+	} else if (os_strcmp(buf, "vlan_no_bridge") == 0) {
-+		bss->ssid.vlan_no_bridge = atoi(pos);
- 	} else if (os_strcmp(buf, "per_sta_vif") == 0) {
- 		bss->ssid.per_sta_vif = atoi(pos);
- 	} else if (os_strcmp(buf, "vlan_file") == 0) {
-@@ -3839,6 +3859,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		if (bss->ocv && !bss->ieee80211w)
- 			bss->ieee80211w = 1;
- #endif /* CONFIG_OCV */
-+	} else if (os_strcmp(buf, "noscan") == 0) {
-+		conf->noscan = atoi(pos);
-+	} else if (os_strcmp(buf, "ht_coex") == 0) {
-+		conf->no_ht_coex = !atoi(pos);
- 	} else if (os_strcmp(buf, "ieee80211n") == 0) {
- 		conf->ieee80211n = atoi(pos);
- 	} else if (os_strcmp(buf, "ht_capab") == 0) {
-@@ -3887,6 +3911,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 	} else if (os_strcmp(buf, "he_bss_color") == 0) {
- 		conf->he_op.he_bss_color = atoi(pos) & 0x3f;
- 		conf->he_op.he_bss_color_disabled = 0;
-+		if (atoi(pos) > 63)
-+			conf->he_op.he_bss_color = os_random() % 63 + 1;
- 	} else if (os_strcmp(buf, "he_bss_color_partial") == 0) {
- 		conf->he_op.he_bss_color_partial = atoi(pos);
- 	} else if (os_strcmp(buf, "he_default_pe_duration") == 0) {
-@@ -4520,10 +4546,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		bss->gas_frag_limit = val;
- 	} else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
- 		bss->gas_comeback_delay = atoi(pos);
-+#endif /* CONFIG_INTERWORKING */
- 	} else if (os_strcmp(buf, "qos_map_set") == 0) {
- 		if (parse_qos_map_set(bss, pos, line) < 0)
- 			return 1;
--#endif /* CONFIG_INTERWORKING */
- #ifdef CONFIG_RADIUS_TEST
- 	} else if (os_strcmp(buf, "dump_msk_file") == 0) {
- 		os_free(bss->dump_msk_file);
-@@ -5347,7 +5373,12 @@ struct hostapd_config * hostapd_config_read(const char *fname)
- 	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);
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 10cb186f1..f76226cf4 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3876,6 +3876,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 						      reply_size);
- 	} else if (os_strcmp(buf, "STATUS-DRIVER") == 0) {
- 		reply_len = hostapd_drv_status(hapd, reply, reply_size);
-+#ifdef CONFIG_CTRL_IFACE_MIB
- 	} else if (os_strcmp(buf, "MIB") == 0) {
- 		reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
- 		if (reply_len >= 0) {
-@@ -3917,6 +3918,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 	} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
- 		reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
- 							reply_size);
-+#endif
- 	} else if (os_strcmp(buf, "ATTACH") == 0) {
- 		if (hostapd_ctrl_iface_attach(hapd, from, fromlen, NULL))
- 			reply_len = -1;
-@@ -5464,6 +5466,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;
-@@ -5565,6 +5568,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/hostapd/defconfig b/hostapd/defconfig
-index 66bf894eb..f716553bb 100644
---- a/hostapd/defconfig
-+++ b/hostapd/defconfig
-@@ -6,9 +6,21 @@
- # just setting VARIABLE=n is not disabling that variable.
- #
- # This file is included in Makefile, so variables like CFLAGS and LIBS can also
--# be modified from here. In most cass, these lines should use += in order not
-+# be modified from here. In most cases, these lines should use += in order not
- # to override previous values of the variables.
- 
-+
-+# Uncomment following two lines and fix the paths if you have installed TLS
-+# libraries in a non-default location
-+#CFLAGS += -I/usr/local/openssl/include
-+#LIBS += -L/usr/local/openssl/lib
-+
-+# Some Red Hat versions seem to include kerberos header files from OpenSSL, but
-+# the kerberos files are not in the default include path. Following line can be
-+# used to fix build issues on such systems (krb5.h not found).
-+#CFLAGS += -I/usr/include/kerberos
-+
-+
- # Driver interface for Host AP driver
- CONFIG_DRIVER_HOSTAP=y
- 
-@@ -281,6 +293,7 @@ CONFIG_IPV6=y
- # openssl = OpenSSL (default)
- # gnutls = GnuTLS
- # internal = Internal TLSv1 implementation (experimental)
-+# mbedtls = mbed TLS
- # linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
- # none = Empty template
- #CONFIG_TLS=openssl
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index a9d326de8..a469b1f4d 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -401,7 +401,6 @@ static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
- }
- 
- 
--#ifdef CONFIG_TAXONOMY
- static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
- 				     char *argv[])
- {
-@@ -414,7 +413,6 @@ static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
- 	os_snprintf(buf, sizeof(buf), "SIGNATURE %s", argv[0]);
- 	return wpa_ctrl_command(ctrl, buf);
- }
--#endif /* CONFIG_TAXONOMY */
- 
- 
- static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
-@@ -431,7 +429,6 @@ static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
- }
- 
- 
--#ifdef CONFIG_WPS
- static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
- 				   char *argv[])
- {
-@@ -657,7 +654,6 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
- 			 ssid_hex, argv[1]);
- 	return wpa_ctrl_command(ctrl, buf);
- }
--#endif /* CONFIG_WPS */
- 
- 
- static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
-@@ -757,7 +753,7 @@ static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd,
- 	}
- 
- 	buf[len] = '\0';
--	if (memcmp(buf, "FAIL", 4) == 0)
-+	if (memcmp(buf, "FAIL", 4) == 0 || memcmp(buf, "UNKNOWN COMMAND", 15) == 0)
- 		return -1;
- 	if (print)
- 		printf("%s", buf);
-@@ -1670,13 +1666,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 	{ "disassociate", hostapd_cli_cmd_disassociate,
- 	  hostapd_complete_stations,
- 	  "<addr> = disassociate a station" },
--#ifdef CONFIG_TAXONOMY
- 	{ "signature", hostapd_cli_cmd_signature, hostapd_complete_stations,
- 	  "<addr> = get taxonomy signature for a station" },
--#endif /* CONFIG_TAXONOMY */
- 	{ "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations,
- 	  "<addr> = send SA Query to a station" },
--#ifdef CONFIG_WPS
- 	{ "wps_pin", hostapd_cli_cmd_wps_pin, NULL,
- 	  "<uuid> <pin> [timeout] [addr] = add WPS Enrollee PIN" },
- 	{ "wps_check_pin", hostapd_cli_cmd_wps_check_pin, NULL,
-@@ -1701,7 +1694,6 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 	  "<SSID> <auth> <encr> <key> = configure AP" },
- 	{ "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL,
- 	  "= show current WPS status" },
--#endif /* CONFIG_WPS */
- 	{ "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL,
- 	  "= send Disassociation Imminent notification" },
- 	{ "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL,
-diff --git a/hostapd/main.c b/hostapd/main.c
-index 524a10274..0ccd4a5d7 100644
---- a/hostapd/main.c
-+++ b/hostapd/main.c
-@@ -31,7 +31,7 @@
- #include "config_file.h"
- #include "eap_register.h"
- #include "ctrl_iface.h"
--
-+#include "build_features.h"
- 
- struct hapd_global {
- 	void **drv_priv;
-@@ -40,6 +40,7 @@ struct hapd_global {
- 
- static struct hapd_global global;
- 
-+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,
-@@ -690,6 +691,11 @@ fail:
- 	return -1;
- }
- 
-+void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
-+                       union wpa_event_data *data);
-+
-+void hostapd_wpa_event_global(void *ctx, enum wpa_event_type event,
-+ 				 union wpa_event_data *data);
- 
- #ifdef CONFIG_WPS
- static int gen_uuid(const char *txt_addr)
-@@ -781,6 +787,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;
-@@ -806,8 +817,10 @@ int main(int argc, char *argv[])
- 		return -1;
- #endif /* CONFIG_DPP */
- 
-+	wpa_supplicant_event = hostapd_wpa_event;
-+	wpa_supplicant_event_global = hostapd_wpa_event_global;
- 	for (;;) {
--		c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:q");
-+		c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:g:G:qv::");
- 		if (c < 0)
- 			break;
- 		switch (c) {
-@@ -844,6 +857,8 @@ int main(int argc, char *argv[])
- 			break;
- #endif /* CONFIG_DEBUG_LINUX_TRACING */
- 		case 'v':
-+			if (optarg)
-+				exit(!has_feature(optarg));
- 			show_version();
- 			exit(1);
- 		case 'g':
-@@ -1013,6 +1028,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");
-@@ -1022,6 +1038,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++) {
-diff --git a/src/ap/acs.c b/src/ap/acs.c
-index 28b0ba71c..4c4c750ab 100644
---- a/src/ap/acs.c
-+++ b/src/ap/acs.c
-@@ -467,17 +467,17 @@ static int acs_get_bw_center_chan(int freq, enum bw_type bw)
- static int acs_survey_is_sufficient(struct freq_survey *survey)
- {
- 	if (!(survey->filled & SURVEY_HAS_NF)) {
-+		survey->nf = -95;
- 		wpa_printf(MSG_INFO,
- 			   "ACS: Survey for freq %d is missing noise floor",
- 			   survey->freq);
--		return 0;
- 	}
- 
- 	if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
-+		survey->channel_time = 0;
- 		wpa_printf(MSG_INFO,
- 			   "ACS: Survey for freq %d is missing channel time",
- 			   survey->freq);
--		return 0;
- 	}
- 
- 	if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
-@@ -485,7 +485,6 @@ static int acs_survey_is_sufficient(struct freq_survey *survey)
- 		wpa_printf(MSG_INFO,
- 			   "ACS: Survey for freq %d is missing RX and busy time (at least one is required)",
- 			   survey->freq);
--		return 0;
- 	}
- 
- 	return 1;
-diff --git a/src/ap/airtime_policy.c b/src/ap/airtime_policy.c
-index 68443115f..26f11ad98 100644
---- a/src/ap/airtime_policy.c
-+++ b/src/ap/airtime_policy.c
-@@ -112,8 +112,14 @@ static void set_sta_weights(struct hostapd_data *hapd, unsigned int weight)
- {
- 	struct sta_info *sta;
- 
--	for (sta = hapd->sta_list; sta; sta = sta->next)
--		sta_set_airtime_weight(hapd, sta, weight);
-+	for (sta = hapd->sta_list; sta; sta = sta->next) {
-+		unsigned int sta_weight = weight;
-+
-+		if (sta->dyn_airtime_weight)
-+			sta_weight = (weight * sta->dyn_airtime_weight) / 256;
-+
-+		sta_set_airtime_weight(hapd, sta, sta_weight);
-+	}
- }
- 
- 
-@@ -244,7 +250,10 @@ int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta)
- 	unsigned int weight;
- 
- 	if (hapd->iconf->airtime_mode == AIRTIME_MODE_STATIC) {
--		weight = get_weight_for_sta(hapd, sta->addr);
-+		if (sta->dyn_airtime_weight)
-+			weight = sta->dyn_airtime_weight;
-+		else
-+			weight = get_weight_for_sta(hapd, sta->addr);
- 		if (weight)
- 			return sta_set_airtime_weight(hapd, sta, weight);
- 	}
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 2330163c4..d10b00be9 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -121,6 +121,7 @@ struct hostapd_ssid {
- #define DYNAMIC_VLAN_OPTIONAL 1
- #define DYNAMIC_VLAN_REQUIRED 2
- 	int dynamic_vlan;
-+	int vlan_no_bridge;
- #define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0
- #define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
- #define DYNAMIC_VLAN_NAMING_END 2
-@@ -282,6 +283,8 @@ struct airtime_sta_weight {
- struct hostapd_bss_config {
- 	char iface[IFNAMSIZ + 1];
- 	char bridge[IFNAMSIZ + 1];
-+	char ft_iface[IFNAMSIZ + 1];
-+	char snoop_iface[IFNAMSIZ + 1];
- 	char vlan_bridge[IFNAMSIZ + 1];
- 	char wds_bridge[IFNAMSIZ + 1];
- 	int bridge_hairpin; /* hairpin_mode on bridge members */
-@@ -307,6 +310,7 @@ struct hostapd_bss_config {
- 	unsigned int eap_sim_db_timeout;
- 	int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
- 	struct hostapd_ip_addr own_ip_addr;
-+	int dynamic_own_ip_addr;
- 	char *nas_identifier;
- 	struct hostapd_radius_servers *radius;
- 	int acct_interim_interval;
-@@ -1064,6 +1068,8 @@ struct hostapd_config {
- 	unsigned int track_sta_max_num;
- 	unsigned int track_sta_max_age;
- 
-+	int max_num_sta;
-+
- 	char country[3]; /* first two octets: country code as described in
- 			  * ISO/IEC 3166-1. Third octet:
- 			  * ' ' (ascii 32): all environments
-@@ -1101,6 +1107,8 @@ struct hostapd_config {
- 
- 	int ht_op_mode_fixed;
- 	u16 ht_capab;
-+	int noscan;
-+	int no_ht_coex;
- 	int ieee80211n;
- 	int secondary_channel;
- 	int no_pri_sec_switch;
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 32722084d..527b2c984 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -387,8 +387,6 @@ int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
- 		return -1;
- 	if (hapd->conf->wds_bridge[0])
- 		bridge = hapd->conf->wds_bridge;
--	else if (hapd->conf->bridge[0])
--		bridge = hapd->conf->bridge;
- 	return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
- 					 bridge, ifname_wds);
- }
-@@ -1031,7 +1029,8 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
- int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
- 			    const u8 *qos_map_set, u8 qos_map_set_len)
- {
--	if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv)
-+	if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv ||
-+	    !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_QOS_MAPPING))
- 		return 0;
- 	return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
- 					 qos_map_set_len);
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index d7e79c840..f8a8725be 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -371,12 +371,12 @@ static inline int hostapd_drv_br_port_set_attr(struct hostapd_data *hapd,
- 
- static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
- 					       enum drv_br_net_param param,
--					       unsigned int val)
-+					       const char *ifname, unsigned int val)
- {
- 	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
- 	    hapd->driver->br_set_net_param == NULL)
- 		return -1;
--	return hapd->driver->br_set_net_param(hapd->drv_priv, param, val);
-+	return hapd->driver->br_set_net_param(hapd->drv_priv, param, ifname, val);
- }
- 
- static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
-@@ -404,6 +404,23 @@ static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
- 	return hapd->driver->stop_ap(hapd->drv_priv, link_id);
- }
- 
-+static inline int hostapd_drv_if_rename(struct hostapd_data *hapd,
-+					enum wpa_driver_if_type type,
-+					const char *ifname,
-+					const char *new_name)
-+{
-+	if (!hapd->driver || !hapd->driver->if_rename || !hapd->drv_priv)
-+		return -1;
-+	return hapd->driver->if_rename(hapd->drv_priv, type, ifname, new_name);
-+}
-+
-+static inline int hostapd_drv_set_first_bss(struct hostapd_data *hapd)
-+{
-+	if (!hapd->driver || !hapd->driver->set_first_bss || !hapd->drv_priv)
-+		return 0;
-+	return hapd->driver->set_first_bss(hapd->drv_priv);
-+}
-+
- static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
- 					   struct wpa_channel_info *ci)
- {
-diff --git a/src/ap/beacon.c b/src/ap/beacon.c
-index 4354dfae3..26453cb2c 100644
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -1343,6 +1343,12 @@ void handle_probe_req(struct hostapd_data *hapd,
- 	int mld_id;
- 	u16 links;
- #endif /* CONFIG_IEEE80211BE */
-+	struct hostapd_ubus_request req = {
-+		.type = HOSTAPD_UBUS_PROBE_REQ,
-+		.mgmt_frame = mgmt,
-+		.ssi_signal = ssi_signal,
-+		.elems = &elems,
-+	};
- 
- 	if (hapd->iconf->rssi_ignore_probe_request && ssi_signal &&
- 	    ssi_signal < hapd->iconf->rssi_ignore_probe_request)
-@@ -1529,6 +1535,12 @@ void handle_probe_req(struct hostapd_data *hapd,
- 	}
- #endif /* CONFIG_P2P */
- 
-+	if (hostapd_ubus_handle_event(hapd, &req)) {
-+		wpa_printf(MSG_DEBUG, "Probe request for " MACSTR " rejected by ubus handler.\n",
-+		       MAC2STR(mgmt->sa));
-+		return;
-+	}
-+
- 	/* TODO: verify that supp_rates contains at least one matching rate
- 	 * with AP configuration */
- 
-@@ -1547,7 +1559,7 @@ void handle_probe_req(struct hostapd_data *hapd,
- 	if (hapd->conf->no_probe_resp_if_max_sta &&
- 	    is_multicast_ether_addr(mgmt->da) &&
- 	    is_multicast_ether_addr(mgmt->bssid) &&
--	    hapd->num_sta >= hapd->conf->max_num_sta &&
-+	    hostapd_check_max_sta(hapd) &&
- 	    !ap_get_sta(hapd, mgmt->sa)) {
- 		wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
- 			   " since no room for additional STA",
-diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index 2cfef4bd4..cd7db4fc6 100644
---- a/src/ap/ctrl_iface_ap.c
-+++ b/src/ap/ctrl_iface_ap.c
-@@ -26,6 +26,26 @@
- #include "taxonomy.h"
- #include "wnm_ap.h"
- 
-+static const char * hw_mode_str(enum hostapd_hw_mode mode)
-+{
-+	switch (mode) {
-+	case HOSTAPD_MODE_IEEE80211B:
-+		return "b";
-+	case HOSTAPD_MODE_IEEE80211G:
-+		return "g";
-+	case HOSTAPD_MODE_IEEE80211A:
-+		return "a";
-+	case HOSTAPD_MODE_IEEE80211AD:
-+		return "ad";
-+	case HOSTAPD_MODE_IEEE80211ANY:
-+		return "any";
-+	case NUM_HOSTAPD_MODES:
-+		return "invalid";
-+	}
-+	return "unknown";
-+}
-+
-+#ifdef CONFIG_CTRL_IFACE_MIB
- 
- static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen,
- 					   size_t curr_len, const u8 *mcs_set)
-@@ -212,26 +232,6 @@ static const char * timeout_next_str(int val)
- }
- 
- 
--static const char * hw_mode_str(enum hostapd_hw_mode mode)
--{
--	switch (mode) {
--	case HOSTAPD_MODE_IEEE80211B:
--		return "b";
--	case HOSTAPD_MODE_IEEE80211G:
--		return "g";
--	case HOSTAPD_MODE_IEEE80211A:
--		return "a";
--	case HOSTAPD_MODE_IEEE80211AD:
--		return "ad";
--	case HOSTAPD_MODE_IEEE80211ANY:
--		return "any";
--	case NUM_HOSTAPD_MODES:
--		return "invalid";
--	}
--	return "unknown";
--}
--
--
- static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
- 				      struct sta_info *sta,
- 				      char *buf, size_t buflen)
-@@ -539,6 +539,7 @@ int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
- 	return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
- }
- 
-+#endif
- 
- #ifdef CONFIG_P2P_MANAGER
- static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
-@@ -987,12 +988,12 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
- 			return len;
- 		len += ret;
- 	}
--
-+#ifdef CONFIG_CTRL_IFACE_MIB
- 	if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) {
- 		len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
- 						   mode->mcs_set);
- 	}
--
-+#endif /* CONFIG_CTRL_IFACE_MIB */
- 	if (iface->current_rates && iface->num_rates) {
- 		ret = os_snprintf(buf + len, buflen - len, "supported_rates=");
- 		if (os_snprintf_error(buflen - len, ret))
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index fc2e8d83c..d14fad136 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -17,6 +17,7 @@
- #include "ap_drv_ops.h"
- #include "drivers/driver.h"
- #include "dfs.h"
-+#include "crypto/crypto.h"
- 
- 
- enum dfs_channel_type {
-@@ -526,9 +527,14 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
- 	int num_available_chandefs;
- 	int chan_idx, chan_idx2;
- 	int sec_chan_idx_80p80 = -1;
-+	bool is_mesh = false;
- 	int i;
- 	u32 _rand;
- 
-+#ifdef CONFIG_MESH
-+	is_mesh = iface->mconf;
-+#endif
-+
- 	wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
- 	*secondary_channel = 0;
- 	*oper_centr_freq_seg0_idx = 0;
-@@ -548,8 +554,20 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
- 	if (num_available_chandefs == 0)
- 		return NULL;
- 
--	if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
-+	/* try to use deterministic channel in mesh, so that both sides
-+	 * have a chance to switch to the same channel */
-+	if (is_mesh) {
-+#ifdef CONFIG_MESH
-+		u64 hash[4];
-+		const u8 *meshid[1] = { &iface->mconf->meshid[0] };
-+		const size_t meshid_len = iface->mconf->meshid_len;
-+
-+		sha256_vector(1, meshid, &meshid_len, (u8 *)&hash[0]);
-+		_rand = hash[0] + hash[1] + hash[2] + hash[3];
-+#endif
-+	} else if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
- 		return NULL;
-+
- 	chan_idx = _rand % num_available_chandefs;
- 	wpa_printf(MSG_DEBUG, "DFS: Picked random entry from the list: %d/%d",
- 		   chan_idx, num_available_chandefs);
-@@ -1207,6 +1225,8 @@ int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
- 		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
- 		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
- 
-+	hostapd_ubus_notify_radar_detected(iface, freq, chan_width, cf1, cf2);
-+
- 	/* Proceed only if DFS is not offloaded to the driver */
- 	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
- 		return 0;
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index dc21977ff..e8796f709 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -268,6 +268,10 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
- 	struct hostapd_iface *iface = hapd->iface;
- #endif /* CONFIG_OWE */
- 	bool updated = false;
-+	struct hostapd_ubus_request req = {
-+		.type = HOSTAPD_UBUS_ASSOC_REQ,
-+		.addr = addr,
-+	};
- 
- 	if (addr == NULL) {
- 		/*
-@@ -412,6 +416,12 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
- 		goto fail;
- 	}
- 
-+	if (hostapd_ubus_handle_event(hapd, &req)) {
-+		wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
-+			   MAC2STR(req.addr));
-+		goto fail;
-+	}
-+
- #ifdef CONFIG_P2P
- 	if (elems.p2p) {
- 		wpabuf_free(sta->p2p_ie);
-@@ -2342,8 +2352,8 @@ static void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
- 	ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
- }
- 
--void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
--			  union wpa_event_data *data)
-+void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
-+		       union wpa_event_data *data)
- {
- 	struct hostapd_data *hapd = ctx;
- 	struct sta_info *sta;
-@@ -2675,7 +2685,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
- }
- 
- 
--void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
-+void hostapd_wpa_event_global(void *ctx, enum wpa_event_type event,
- 				 union wpa_event_data *data)
- {
- 	struct hapd_interfaces *interfaces = ctx;
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index b899c9831..7959859b0 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -247,6 +247,29 @@ static int hostapd_iface_conf_changed(struct hostapd_config *newconf,
- 	return 0;
- }
- 
-+static inline int hostapd_iface_num_sta(struct hostapd_iface *iface)
-+{
-+	int num_sta = 0;
-+	int i;
-+
-+	for (i = 0; i < iface->num_bss; i++)
-+		num_sta += iface->bss[i]->num_sta;
-+
-+	return num_sta;
-+}
-+
-+
-+int hostapd_check_max_sta(struct hostapd_data *hapd)
-+{
-+	if (hapd->num_sta >= hapd->conf->max_num_sta)
-+		return 1;
-+
-+	if (hapd->iconf->max_num_sta &&
-+	    hostapd_iface_num_sta(hapd->iface) >= hapd->iconf->max_num_sta)
-+		return 1;
-+
-+	return 0;
-+}
- 
- int hostapd_reload_config(struct hostapd_iface *iface)
- {
-@@ -255,6 +278,8 @@ int hostapd_reload_config(struct hostapd_iface *iface)
- 	struct hostapd_config *newconf, *oldconf;
- 	size_t j;
- 
-+	hostapd_ucode_reload_bss(hapd);
-+
- 	if (iface->config_fname == NULL) {
- 		/* Only in-memory config in use - assume it has been updated */
- 		hostapd_clear_old(iface);
-@@ -475,6 +500,8 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
- 	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);
- 	vlan_deinit(hapd);
-@@ -685,6 +712,7 @@ static void sta_track_deinit(struct hostapd_iface *iface)
- void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
- {
- 	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
-+	eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
- #ifdef NEED_AP_MLME
- 	hostapd_stop_setup_timers(iface);
- #endif /* NEED_AP_MLME */
-@@ -714,7 +742,7 @@ void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
- static void hostapd_cleanup_iface(struct hostapd_iface *iface)
- {
- 	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
--	eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
-+	hostapd_ucode_free_iface(iface);
- 	eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
- 			     NULL);
- 
-@@ -1303,6 +1331,9 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
- 	if (hapd->driver && hapd->driver->set_operstate)
- 		hapd->driver->set_operstate(hapd->drv_priv, 1);
- 
-+	hostapd_ubus_add_bss(hapd);
-+	hostapd_ucode_add_bss(hapd);
-+
- 	return 0;
- }
- 
-@@ -1324,8 +1355,7 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
-  * 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];
-@@ -1552,6 +1582,7 @@ setup_mld:
- 
- 			os_memset(&das_conf, 0, sizeof(das_conf));
- 			das_conf.port = conf->radius_das_port;
-+			das_conf.nas_identifier = conf->nas_identifier;
- 			das_conf.shared_secret = conf->radius_das_shared_secret;
- 			das_conf.shared_secret_len =
- 				conf->radius_das_shared_secret_len;
-@@ -1627,6 +1658,7 @@ setup_mld:
- 		wpa_printf(MSG_ERROR, "GAS server initialization failed");
- 		return -1;
- 	}
-+#endif /* CONFIG_INTERWORKING */
- 
- 	if (conf->qos_map_set_len &&
- 	    hostapd_drv_set_qos_map(hapd, conf->qos_map_set,
-@@ -1634,7 +1666,6 @@ setup_mld:
- 		wpa_printf(MSG_ERROR, "Failed to initialize QoS Map");
- 		return -1;
- 	}
--#endif /* CONFIG_INTERWORKING */
- 
- 	if (conf->bss_load_update_period && bss_load_update_init(hapd)) {
- 		wpa_printf(MSG_ERROR, "BSS Load initialization failed");
-@@ -2447,6 +2478,7 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
- 	if (err)
- 		goto fail;
- 
-+	hostapd_ubus_add_iface(iface);
- 	wpa_printf(MSG_DEBUG, "Completing interface initialization");
- 	if (iface->freq) {
- #ifdef NEED_AP_MLME
-@@ -2676,6 +2708,7 @@ dfs_offload:
- 
- fail:
- 	wpa_printf(MSG_ERROR, "Interface initialization failed");
-+	hostapd_ubus_free_iface(iface);
- 
- 	if (iface->is_no_ir) {
- 		hostapd_set_state(iface, HAPD_IFACE_NO_IR);
-@@ -2875,7 +2908,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
- }
- 
- 
--static void hostapd_bss_deinit(struct hostapd_data *hapd)
-+void hostapd_bss_deinit(struct hostapd_data *hapd)
- {
- 	if (!hapd)
- 		return;
-@@ -3395,6 +3428,7 @@ void hostapd_interface_deinit_free(struct hostapd_iface *iface)
- 		   (unsigned int) iface->conf->num_bss);
- 	driver = iface->bss[0]->driver;
- 	drv_priv = iface->bss[0]->drv_priv;
-+	hostapd_ubus_free_iface(iface);
- 	hostapd_interface_deinit(iface);
- 	wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
- 		   __func__, driver, drv_priv);
-@@ -3926,7 +3960,8 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
- 		hapd_iface = interfaces->iface[i];
- 		if (hapd_iface == NULL)
- 			return -1;
--		if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
-+		if (!os_strcmp(hapd_iface->phy, buf) ||
-+		    !os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
- 			wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
- 			hapd_iface->driver_ap_teardown =
- 				!!(hapd_iface->drv_flags &
-@@ -3972,6 +4007,8 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
- void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
- 			   int reassoc)
- {
-+	int mld_assoc_link_id = -1;
-+
- 	if (hapd->tkip_countermeasures) {
- 		hostapd_drv_sta_deauth(hapd, sta->addr,
- 				       WLAN_REASON_MICHAEL_MIC_FAILURE);
-@@ -3983,6 +4020,8 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
- 	    sta->mld_assoc_link_id != hapd->mld_link_id)
- 		return;
- #endif /* CONFIG_IEEE80211BE */
-+        if (mld_assoc_link_id != -2)
-+		hostapd_prune_associations(hapd, sta->addr, mld_assoc_link_id);
- 
- 	ap_sta_clear_disconnect_timeouts(hapd, sta);
- 	sta->post_csa_sa_query = 0;
-diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
-index 594866fbb..1e4113459 100644
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -13,11 +13,14 @@
- #include <sqlite3.h>
- #endif /* CONFIG_SQLITE */
- 
-+#include "ap/sta_info.h"
- #include "common/defs.h"
- #include "common/dpp.h"
- #include "utils/list.h"
- #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 +54,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);
-@@ -167,6 +174,21 @@ struct hostapd_sae_commit_queue {
- 	u8 msg[];
- };
- 
-+/**
-+ * struct hostapd_openwrt_stats - OpenWrt custom STA/AP statistics
-+ */
-+struct hostapd_openwrt_stats {
-+	struct {
-+		u64 neighbor_report_tx;
-+	} rrm;
-+
-+	struct {
-+		u64 bss_transition_query_rx;
-+		u64 bss_transition_request_tx;
-+		u64 bss_transition_response_rx;
-+	} wnm;
-+};
-+
- /**
-  * struct hostapd_data - hostapd per-BSS data structure
-  */
-@@ -174,6 +196,8 @@ struct hostapd_data {
- 	struct hostapd_iface *iface;
- 	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;
-@@ -181,6 +205,9 @@ struct hostapd_data {
- 
- 	u8 own_addr[ETH_ALEN];
- 
-+	/* OpenWrt specific statistics */
-+	struct hostapd_openwrt_stats openwrt_stats;
-+
- 	int num_sta; /* number of entries in sta_list */
- 	struct sta_info *sta_list; /* STA info list head */
- #define STA_HASH_SIZE 256
-@@ -523,6 +550,7 @@ struct hostapd_mld {
-  */
- struct hostapd_iface {
- 	struct hapd_interfaces *interfaces;
-+	struct hostapd_ucode_iface ucode;
- 	void *owner;
- 	char *config_fname;
- 	struct hostapd_config *conf;
-@@ -745,6 +773,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
- 		       struct hostapd_bss_config *bss);
- int hostapd_setup_interface(struct hostapd_iface *iface);
- int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
-+void hostapd_set_own_neighbor_report(struct hostapd_data *hapd);
- void hostapd_interface_deinit(struct hostapd_iface *iface);
- void hostapd_interface_free(struct hostapd_iface *iface);
- struct hostapd_iface * hostapd_alloc_iface(void);
-@@ -753,6 +782,8 @@ struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
- 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_bss_deinit(struct hostapd_data *hapd);
- void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
- 			   int reassoc);
- void hostapd_interface_deinit_free(struct hostapd_iface *iface);
-@@ -780,6 +811,7 @@ void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
- 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);
-+int hostapd_check_max_sta(struct hostapd_data *hapd);
- 
- void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap);
- void hostapd_cleanup_cca_params(struct hostapd_data *hapd);
-@@ -865,4 +897,14 @@ static inline bool hostapd_mld_is_first_bss(struct hostapd_data *hapd)
- 
- u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd);
- 
-+static inline bool ap_sta_is_mld(struct hostapd_data *hapd,
-+				 struct sta_info *sta)
-+{
-+#ifdef CONFIG_IEEE80211BE
-+	return hapd->conf->mld_ap && sta && sta->mld_info.mld_sta;
-+#else /* CONFIG_IEEE80211BE */
-+	return false;
-+#endif /* CONFIG_IEEE80211BE */
-+}
-+
- #endif /* HOSTAPD_H */
-diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
-index 222f3dc05..672e43a10 100644
---- a/src/ap/hw_features.c
-+++ b/src/ap/hw_features.c
-@@ -546,7 +546,8 @@ static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
- 	int ret;
- 
- 	/* Check that HT40 is used and PRI / SEC switch is allowed */
--	if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)
-+	if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch ||
-+		iface->conf->noscan)
- 		return 0;
- 
- 	hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 179af5e28..bda61b998 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -2804,7 +2804,7 @@ static void handle_auth(struct hostapd_data *hapd,
- 	u16 auth_alg, auth_transaction, status_code;
- 	u16 resp = WLAN_STATUS_SUCCESS;
- 	struct sta_info *sta = NULL;
--	int res, reply_res;
-+	int res, reply_res, ubus_resp;
- 	u16 fc;
- 	const u8 *challenge = NULL;
- 	u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
-@@ -2815,6 +2815,11 @@ static void handle_auth(struct hostapd_data *hapd,
- #ifdef CONFIG_IEEE80211BE
- 	bool mld_sta = false;
- #endif /* CONFIG_IEEE80211BE */
-+	struct hostapd_ubus_request req = {
-+		.type = HOSTAPD_UBUS_AUTH_REQ,
-+		.mgmt_frame = mgmt,
-+		.ssi_signal = rssi,
-+	};
- 
- 	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
- 		wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
-@@ -3008,6 +3013,13 @@ static void handle_auth(struct hostapd_data *hapd,
- 		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
- 		goto fail;
- 	}
-+	ubus_resp = hostapd_ubus_handle_event(hapd, &req);
-+	if (ubus_resp) {
-+		wpa_printf(MSG_DEBUG, "Station " MACSTR " rejected by ubus handler.\n",
-+			MAC2STR(mgmt->sa));
-+		resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
-+		goto fail;
-+	}
- 	if (res == HOSTAPD_ACL_PENDING)
- 		return;
- 
-@@ -3042,15 +3054,6 @@ static void handle_auth(struct hostapd_data *hapd,
- 				       seq_ctrl);
- 			return;
- 		}
--#ifdef CONFIG_MESH
--		if ((hapd->conf->mesh & MESH_ENABLED) &&
--		    sta->plink_state == PLINK_BLOCKED) {
--			wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
--				   " is blocked - drop Authentication frame",
--				   MAC2STR(sa));
--			return;
--		}
--#endif /* CONFIG_MESH */
- #ifdef CONFIG_PASN
- 		if (auth_alg == WLAN_AUTH_PASN &&
- 		    (sta->flags & WLAN_STA_ASSOC)) {
-@@ -4698,6 +4701,13 @@ static int add_associated_sta(struct hostapd_data *hapd,
- 	 * drivers to accept the STA parameter configuration. Since this is
- 	 * after a new FT-over-DS exchange, a new TK has been derived, so key
- 	 * reinstallation is not a concern for this case.
-+	 *
-+	 * If the STA was associated and authorized earlier, but came for a new
-+	 * connection (!added_unassoc + !reassoc), remove the existing STA entry
-+	 * so that it can be re-added. This case is rarely seen when the AP could
-+	 * not receive the deauth/disassoc frame from the STA. And the STA comes
-+	 * back with new connection within a short period or before the inactive
-+	 * STA entry is removed from the list.
- 	 */
- 	wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR
- 		   " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)",
-@@ -4711,7 +4721,8 @@ static int add_associated_sta(struct hostapd_data *hapd,
- 	    (!(sta->flags & WLAN_STA_AUTHORIZED) ||
- 	     (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) ||
- 	     (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
--	      !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) {
-+	      !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)) ||
-+	     (!reassoc && (sta->flags & WLAN_STA_AUTHORIZED)))) {
- 		hostapd_drv_sta_remove(hapd, sta->addr);
- 		wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
- 		set = 0;
-@@ -5273,7 +5284,7 @@ static void handle_assoc(struct hostapd_data *hapd,
- 	int resp = WLAN_STATUS_SUCCESS;
- 	u16 reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
- 	const u8 *pos;
--	int left, i;
-+	int left, i, ubus_resp;
- 	struct sta_info *sta;
- 	u8 *tmp = NULL;
- #ifdef CONFIG_FILS
-@@ -5515,6 +5526,11 @@ static void handle_assoc(struct hostapd_data *hapd,
- 		left = res;
- 	}
- #endif /* CONFIG_FILS */
-+	struct hostapd_ubus_request req = {
-+		.type = HOSTAPD_UBUS_ASSOC_REQ,
-+		.mgmt_frame = mgmt,
-+		.ssi_signal = rssi,
-+	};
- 
- 	/* followed by SSID and Supported rates; and HT capabilities if 802.11n
- 	 * is used */
-@@ -5617,6 +5633,13 @@ static void handle_assoc(struct hostapd_data *hapd,
- 	if (set_beacon)
- 		ieee802_11_set_beacons(hapd->iface);
- 
-+	ubus_resp = hostapd_ubus_handle_event(hapd, &req);
-+	if (ubus_resp) {
-+		wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
-+		       MAC2STR(mgmt->sa));
-+		resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
-+		goto fail;
-+	}
-  fail:
- 
- 	/*
-@@ -5848,6 +5871,7 @@ static void handle_disassoc(struct hostapd_data *hapd,
- 			   (unsigned long) len);
- 		return;
- 	}
-+	hostapd_ubus_notify(hapd, "disassoc", mgmt->sa);
- 
- 	sta = ap_get_sta(hapd, mgmt->sa);
- 	if (!sta) {
-@@ -5879,6 +5903,8 @@ static void handle_deauth(struct hostapd_data *hapd,
- 	/* Clear the PTKSA cache entries for PASN */
- 	ptksa_cache_flush(hapd->ptksa, mgmt->sa, WPA_CIPHER_NONE);
- 
-+	hostapd_ubus_notify(hapd, "deauth", mgmt->sa);
-+
- 	sta = ap_get_sta(hapd, mgmt->sa);
- 	if (!sta) {
- 		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
-diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
-index f90f1254e..7f0a00f95 100644
---- a/src/ap/ieee802_11_ht.c
-+++ b/src/ap/ieee802_11_ht.c
-@@ -82,7 +82,9 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
- u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
- {
- 	struct ieee80211_ht_operation *oper;
-+	le32 vht_capabilities_info;
- 	u8 *pos = eid;
-+	u8 chwidth;
- 
- 	if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n ||
- 	    is_6ghz_op_class(hapd->iconf->op_class))
-@@ -103,6 +105,13 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
- 		oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
- 			HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
- 
-+	vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
-+	chwidth = hostapd_get_oper_chwidth(hapd->iconf);
-+	if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT
-+		&& ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
-+		oper->operation_mode = host_to_le16(hapd->iconf->vht_oper_centr_freq_seg0_idx << 5);
-+	}
-+
- 	pos += sizeof(*oper);
- 
- 	return pos;
-@@ -230,6 +239,9 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
- 		return;
- 	}
- 
-+	if (iface->conf->noscan || iface->conf->no_ht_coex)
-+		return;
-+
- 	if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) {
- 		wpa_printf(MSG_DEBUG,
- 			   "Ignore too short 20/40 BSS Coexistence Management frame");
-@@ -390,6 +402,9 @@ void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta)
- 	if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
- 		return;
- 
-+	if (iface->conf->noscan || iface->conf->no_ht_coex)
-+		return;
-+
- 	wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR
- 		   " in Association Request", MAC2STR(sta->addr));
- 
-diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
-index a5716f037..85790c7ed 100644
---- a/src/ap/ieee802_11_shared.c
-+++ b/src/ap/ieee802_11_shared.c
-@@ -1138,13 +1138,11 @@ u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len)
- u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
- 		    const u8 *ext_capab_ie, size_t ext_capab_ie_len)
- {
--#ifdef CONFIG_INTERWORKING
- 	/* check for QoS Map support */
- 	if (ext_capab_ie_len >= 5) {
- 		if (ext_capab_ie[4] & 0x01)
- 			sta->qos_map_enabled = 1;
- 	}
--#endif /* CONFIG_INTERWORKING */
- 
- 	if (ext_capab_ie_len > 0) {
- 		sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
-diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
-index 4dc325ce8..68880ab64 100644
---- a/src/ap/ieee802_11_vht.c
-+++ b/src/ap/ieee802_11_vht.c
-@@ -26,6 +26,7 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
- 	struct ieee80211_vht_capabilities *cap;
- 	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
- 	u8 *pos = eid;
-+	u8 chwidth;
- 
- 	if (!mode || is_6ghz_op_class(hapd->iconf->op_class))
- 		return eid;
-@@ -63,6 +64,17 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
- 			host_to_le32(nsts << VHT_CAP_BEAMFORMEE_STS_OFFSET);
- 	}
- 
-+	chwidth = hostapd_get_oper_chwidth(hapd->iconf);
-+	if (((host_to_le32(mode->vht_capab)) & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
-+		&& ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
-+		cap->vht_capabilities_info |= VHT_CAP_EXTENDED_NSS_BW_SUPPORT;
-+		cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ));
-+		cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ));
-+		cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_MASK));
-+	} else {
-+		cap->vht_capabilities_info &= ~VHT_CAP_EXTENDED_NSS_BW_SUPPORT_MASK;
-+	}
-+
- 	/* Supported MCS set comes from hw */
- 	os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
- 
-@@ -75,6 +87,7 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
- u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
- {
- 	struct ieee80211_vht_operation *oper;
-+	le32 vht_capabilities_info;
- 	u8 *pos = eid;
- 	enum oper_chan_width oper_chwidth =
- 		hostapd_get_oper_chwidth(hapd->iconf);
-@@ -110,6 +123,7 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
- 	oper->vht_op_info_chan_center_freq_seg1_idx = seg1;
- 
- 	oper->vht_op_info_chwidth = oper_chwidth;
-+	vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
- 	if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ) {
- 		/*
- 		 * Convert 160 MHz channel width to new style as interop
-@@ -123,6 +137,9 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
- 			oper->vht_op_info_chan_center_freq_seg0_idx -= 8;
- 		else
- 			oper->vht_op_info_chan_center_freq_seg0_idx += 8;
-+
-+		if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
-+			oper->vht_op_info_chan_center_freq_seg1_idx = 0;
- 	} else if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ) {
- 		/*
- 		 * Convert 80+80 MHz channel width to new style as interop
-diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
-index 8e98b6521..8abebbf34 100644
---- a/src/ap/ieee802_1x.c
-+++ b/src/ap/ieee802_1x.c
-@@ -600,6 +600,10 @@ int add_common_radius_attr(struct hostapd_data *hapd,
- 	struct hostapd_radius_attr *attr;
- 	int len;
- 
-+	if (hapd->conf->dynamic_own_ip_addr)
-+		radius_client_get_local_addr(hapd->radius,
-+					     &hapd->conf->own_ip_addr);
-+
- 	if (!hostapd_config_get_radius_attr(req_attr,
- 					    RADIUS_ATTR_NAS_IP_ADDRESS) &&
- 	    hapd->conf->own_ip_addr.af == AF_INET &&
-@@ -2845,6 +2849,7 @@ static const char * bool_txt(bool val)
- 	return val ? "TRUE" : "FALSE";
- }
- 
-+#ifdef CONFIG_CTRL_IFACE_MIB
- 
- int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
- {
-@@ -3031,6 +3036,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
- 	return len;
- }
- 
-+#endif
- 
- #ifdef CONFIG_HS20
- static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx)
-diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c
-index 788c12fdc..bc1eb6251 100644
---- a/src/ap/ndisc_snoop.c
-+++ b/src/ap/ndisc_snoop.c
-@@ -61,6 +61,7 @@ void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
- 	dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr,
- 			      list) {
- 		hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr);
-+		dl_list_del(&ip6addr->list);
- 		os_free(ip6addr);
- 	}
- }
-diff --git a/src/ap/rrm.c b/src/ap/rrm.c
-index f2d5cd16e..8220590a0 100644
---- a/src/ap/rrm.c
-+++ b/src/ap/rrm.c
-@@ -89,6 +89,9 @@ static void hostapd_handle_beacon_report(struct hostapd_data *hapd,
- 		return;
- 	wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s",
- 		MAC2STR(addr), token, rep_mode, report);
-+	if (len < sizeof(struct rrm_measurement_beacon_report))
-+		return;
-+	hostapd_ubus_notify_beacon_report(hapd, addr, token, rep_mode, (struct rrm_measurement_beacon_report*) pos, len);
- }
- 
- 
-@@ -269,6 +272,8 @@ static void hostapd_send_nei_report_resp(struct hostapd_data *hapd,
- 		}
- 	}
- 
-+	hapd->openwrt_stats.rrm.neighbor_report_tx++;
-+
- 	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
- 				wpabuf_head(buf), wpabuf_len(buf));
- 	wpabuf_free(buf);
-@@ -350,6 +355,9 @@ void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
- 		   mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa));
- 
- 	switch (mgmt->u.action.u.rrm.action) {
-+	case WLAN_RRM_LINK_MEASUREMENT_REPORT:
-+		hostapd_ubus_handle_link_measurement(hapd, buf, len);
-+		break;
- 	case WLAN_RRM_RADIO_MEASUREMENT_REPORT:
- 		hostapd_handle_radio_msmt_report(hapd, buf, len);
- 		break;
-diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
-index d483aa9d3..ee6e20538 100644
---- a/src/ap/sta_info.c
-+++ b/src/ap/sta_info.c
-@@ -539,6 +539,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
- 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
- 			       HOSTAPD_LEVEL_INFO, "deauthenticated due to "
- 			       "local deauth request");
-+		hostapd_ubus_notify(hapd, "local-deauth", sta->addr);
- 		ap_free_sta(hapd, sta);
- 		return;
- 	}
-@@ -694,6 +695,7 @@ skip_poll:
- 		mlme_deauthenticate_indication(
- 			hapd, sta,
- 			WLAN_REASON_PREV_AUTH_NOT_VALID);
-+		hostapd_ubus_notify(hapd, "inactive-deauth", sta->addr);
- 		ap_free_sta(hapd, sta);
- 		break;
- 	}
-@@ -1476,9 +1478,6 @@ bool ap_sta_set_authorized_flag(struct hostapd_data *hapd, struct sta_info *sta,
- 				mld_assoc_link_id = -2;
- 		}
- #endif /* CONFIG_IEEE80211BE */
--		if (mld_assoc_link_id != -2)
--			hostapd_prune_associations(hapd, sta->addr,
--						   mld_assoc_link_id);
- 		sta->flags |= WLAN_STA_AUTHORIZED;
- 	} else {
- 		sta->flags &= ~WLAN_STA_AUTHORIZED;
-@@ -1515,15 +1514,28 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
- 		os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr));
- 
- 	if (authorized) {
-+		static const char * const auth_algs[] = {
-+			[WLAN_AUTH_OPEN] = "open",
-+			[WLAN_AUTH_SHARED_KEY] = "shared",
-+			[WLAN_AUTH_FT] = "ft",
-+			[WLAN_AUTH_SAE] = "sae",
-+			[WLAN_AUTH_FILS_SK] = "fils-sk",
-+			[WLAN_AUTH_FILS_SK_PFS] = "fils-sk-pfs",
-+			[WLAN_AUTH_FILS_PK] = "fils-pk",
-+			[WLAN_AUTH_PASN] = "pasn",
-+		};
-+		const char *auth_alg = NULL;
- 		const u8 *dpp_pkhash;
- 		const char *keyid;
- 		char dpp_pkhash_buf[100];
- 		char keyid_buf[100];
- 		char ip_addr[100];
-+		char alg_buf[100];
- 
- 		dpp_pkhash_buf[0] = '\0';
- 		keyid_buf[0] = '\0';
- 		ip_addr[0] = '\0';
-+		alg_buf[0] = '\0';
- #ifdef CONFIG_P2P
- 		if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
- 			os_snprintf(ip_addr, sizeof(ip_addr),
-@@ -1534,6 +1546,13 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
- 		}
- #endif /* CONFIG_P2P */
- 
-+		if (sta->auth_alg < ARRAY_SIZE(auth_algs))
-+			auth_alg = auth_algs[sta->auth_alg];
-+
-+		if (auth_alg)
-+			os_snprintf(alg_buf, sizeof(alg_buf),
-+				" auth_alg=%s", auth_alg);
-+
- 		keyid = ap_sta_wpa_get_keyid(hapd, sta);
- 		if (keyid) {
- 			os_snprintf(keyid_buf, sizeof(keyid_buf),
-@@ -1552,17 +1571,19 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
- 					 dpp_pkhash, SHA256_MAC_LEN);
- 		}
- 
--		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s",
--			buf, ip_addr, keyid_buf, dpp_pkhash_buf);
-+		hostapd_ubus_notify_authorized(hapd, sta, auth_alg);
-+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s%s",
-+			buf, ip_addr, keyid_buf, dpp_pkhash_buf, alg_buf);
- 
- 		if (hapd->msg_ctx_parent &&
- 		    hapd->msg_ctx_parent != hapd->msg_ctx)
- 			wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
--					  AP_STA_CONNECTED "%s%s%s%s",
-+					  AP_STA_CONNECTED "%s%s%s%s%s",
- 					  buf, ip_addr, keyid_buf,
--					  dpp_pkhash_buf);
-+					  dpp_pkhash_buf, alg_buf);
- 	} else {
- 		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
-+		hostapd_ubus_notify(hapd, "disassoc", sta->addr);
- 
- 		if (hapd->msg_ctx_parent &&
- 		    hapd->msg_ctx_parent != hapd->msg_ctx)
-diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
-index 153e4a000..38b80903d 100644
---- a/src/ap/sta_info.h
-+++ b/src/ap/sta_info.h
-@@ -17,7 +17,6 @@
- #include "common/sae.h"
- #include "crypto/sha384.h"
- #include "pasn/pasn_common.h"
--#include "hostapd.h"
- 
- /* STA flags */
- #define WLAN_STA_AUTH BIT(0)
-@@ -323,6 +322,7 @@ struct sta_info {
- #endif /* CONFIG_TESTING_OPTIONS */
- #ifdef CONFIG_AIRTIME_POLICY
- 	unsigned int airtime_weight;
-+	unsigned int dyn_airtime_weight;
- 	struct os_reltime backlogged_until;
- #endif /* CONFIG_AIRTIME_POLICY */
- 
-@@ -420,16 +420,6 @@ int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta);
- 
- void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta);
- 
--static inline bool ap_sta_is_mld(struct hostapd_data *hapd,
--				 struct sta_info *sta)
--{
--#ifdef CONFIG_IEEE80211BE
--	return hapd->conf->mld_ap && sta && sta->mld_info.mld_sta;
--#else /* CONFIG_IEEE80211BE */
--	return false;
--#endif /* CONFIG_IEEE80211BE */
--}
--
- static inline void ap_sta_set_mld(struct sta_info *sta, bool mld)
- {
- #ifdef CONFIG_IEEE80211BE
-diff --git a/src/ap/vlan_full.c b/src/ap/vlan_full.c
-index 19aa3c649..053d6338e 100644
---- a/src/ap/vlan_full.c
-+++ b/src/ap/vlan_full.c
-@@ -475,6 +475,9 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
- 	if (!vlan)
- 		return;
- 
-+	if (hapd->conf->ssid.vlan_no_bridge)
-+		goto out;
-+
- 	vlan->configured = 1;
- 
- 	notempty = vlan->vlan_desc.notempty;
-@@ -506,6 +509,7 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
- 				    ifname, br_name, tagged[i], hapd);
- 	}
- 
-+out:
- 	ifconfig_up(ifname);
- }
- 
-diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
-index 53eacfb45..b69f3de41 100644
---- a/src/ap/vlan_init.c
-+++ b/src/ap/vlan_init.c
-@@ -22,6 +22,7 @@
- static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
- 		       int existsok)
- {
-+	bool vlan_exists = iface_exists(vlan->ifname);
- 	int ret;
- #ifdef CONFIG_WEP
- 	int i;
-@@ -36,7 +37,7 @@ static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
- 	}
- #endif /* CONFIG_WEP */
- 
--	if (!iface_exists(vlan->ifname))
-+	if (!vlan_exists)
- 		ret = hostapd_vlan_if_add(hapd, vlan->ifname);
- 	else if (!existsok)
- 		return -1;
-@@ -51,6 +52,9 @@ static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
- 	if (hapd->wpa_auth)
- 		ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id);
- 
-+	if (!ret && !vlan_exists)
-+		hostapd_ubus_add_vlan(hapd, vlan);
-+
- 	if (ret == 0)
- 		return ret;
- 
-@@ -77,6 +81,8 @@ int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
- 			   "WPA deinitialization for VLAN %d failed (%d)",
- 			   vlan->vlan_id, ret);
- 
-+	hostapd_ubus_remove_vlan(hapd, vlan);
-+
- 	return hostapd_vlan_if_remove(hapd, vlan->ifname);
- }
- 
-diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
-index af8cccaef..d259200c9 100644
---- a/src/ap/wnm_ap.c
-+++ b/src/ap/wnm_ap.c
-@@ -410,6 +410,7 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
- 	mgmt->u.action.u.bss_tm_req.validity_interval = 1;
- 	pos = mgmt->u.action.u.bss_tm_req.variable;
- 
-+	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 "
- 		   "validity_interval=%u",
-@@ -478,7 +479,8 @@ static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
- 		MAC2STR(addr), reason, hex ? " neighbor=" : "", hex);
- 	os_free(hex);
- 
--	ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
-+	if (!hostapd_ubus_notify_bss_transition_query(hapd, addr, dialog_token, reason, pos, end - pos))
-+		ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
- }
- 
- 
-@@ -500,7 +502,7 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
- 					      size_t len)
- {
- 	u8 dialog_token, status_code, bss_termination_delay;
--	const u8 *pos, *end;
-+	const u8 *pos, *end, *target_bssid = NULL;
- 	int enabled = hapd->conf->bss_transition;
- 	struct sta_info *sta;
- 
-@@ -547,6 +549,7 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
- 			wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
- 			return;
- 		}
-+		target_bssid = pos;
- 		sta->agreed_to_steer = 1;
- 		eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
- 		eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
-@@ -566,6 +569,10 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
- 			MAC2STR(addr), status_code, bss_termination_delay);
- 	}
- 
-+	hostapd_ubus_notify_bss_transition_response(hapd, sta->addr, dialog_token,
-+						    status_code, bss_termination_delay,
-+						    target_bssid, pos, end - pos);
-+
- 	wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
- 		    pos, end - pos);
- }
-@@ -814,10 +821,12 @@ int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
- 					       plen);
- 		return 0;
- 	case WNM_BSS_TRANS_MGMT_QUERY:
-+		hapd->openwrt_stats.wnm.bss_transition_query_rx++;
- 		ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
- 						   plen);
- 		return 0;
- 	case WNM_BSS_TRANS_MGMT_RESP:
-+		hapd->openwrt_stats.wnm.bss_transition_response_rx++;
- 		ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload,
- 						  plen);
- 		return 0;
-@@ -865,6 +874,7 @@ int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
- 
- 	pos = mgmt->u.action.u.bss_tm_req.variable;
- 
-+	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
- 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to "
- 		   MACSTR, disassoc_timer, MAC2STR(sta->addr));
- 	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
-@@ -947,6 +957,7 @@ int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
- 		return -1;
- 	}
- 
-+	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
- 	if (disassoc_timer) {
- 		/* send disassociation frame after time-out */
- 		set_disassoc_timer(hapd, sta, disassoc_timer);
-@@ -1028,6 +1039,7 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
- 	}
- 	os_free(buf);
- 
-+	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
- 	if (disassoc_timer) {
- #ifdef CONFIG_IEEE80211BE
- 		if (ap_sta_is_mld(hapd, sta)) {
-diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
-index 7a07dcc4c..b23d75444 100644
---- a/src/ap/wpa_auth.c
-+++ b/src/ap/wpa_auth.c
-@@ -5865,6 +5865,7 @@ static const char * wpa_bool_txt(int val)
- 	return val ? "TRUE" : "FALSE";
- }
- 
-+#ifdef CONFIG_CTRL_IFACE_MIB
- 
- #define RSN_SUITE "%02x-%02x-%02x-%d"
- #define RSN_SUITE_ARG(s) \
-@@ -6017,7 +6018,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
- 
- 	return len;
- }
--
-+#endif
- 
- void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth)
- {
-diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
-index 1726c7201..5a9ec6975 100644
---- a/src/ap/wpa_auth_glue.c
-+++ b/src/ap/wpa_auth_glue.c
-@@ -275,6 +275,7 @@ static void hostapd_wpa_auth_psk_failure_report(void *ctx, const u8 *addr)
- 	struct hostapd_data *hapd = ctx;
- 	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POSSIBLE_PSK_MISMATCH MACSTR,
- 		MAC2STR(addr));
-+	hostapd_ubus_notify(hapd, "key-mismatch", addr);
- }
- 
- 
-@@ -1812,8 +1813,12 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
- 	    wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
- 		const char *ft_iface;
- 
--		ft_iface = hapd->conf->bridge[0] ? hapd->conf->bridge :
--			   hapd->conf->iface;
-+		if (hapd->conf->ft_iface[0])
-+			ft_iface = hapd->conf->ft_iface;
-+		else if (hapd->conf->bridge[0])
-+			ft_iface = hapd->conf->bridge;
-+		else
-+			ft_iface = hapd->conf->iface;
- 		hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB,
- 					  hostapd_rrb_receive, hapd, 1);
- 		if (!hapd->l2) {
-diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
-index 82d4d5fdd..dfc5c3ecb 100644
---- a/src/ap/wps_hostapd.c
-+++ b/src/ap/wps_hostapd.c
-@@ -394,9 +394,8 @@ static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd,
- 				bss->wpa_pairwise |= WPA_CIPHER_GCMP;
- 			else
- 				bss->wpa_pairwise |= WPA_CIPHER_CCMP;
--		}
- #ifndef CONFIG_NO_TKIP
--		if (cred->encr_type & WPS_ENCR_TKIP)
-+		} else if (cred->encr_type & WPS_ENCR_TKIP)
- 			bss->wpa_pairwise |= WPA_CIPHER_TKIP;
- #endif /* CONFIG_NO_TKIP */
- 		bss->rsn_pairwise = bss->wpa_pairwise;
-@@ -1181,8 +1180,7 @@ int hostapd_init_wps(struct hostapd_data *hapd,
- 					  WPA_CIPHER_GCMP_256)) {
- 			wps->encr_types |= WPS_ENCR_AES;
- 			wps->encr_types_rsn |= WPS_ENCR_AES;
--		}
--		if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
-+		} else if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
- #ifdef CONFIG_NO_TKIP
- 			wpa_printf(MSG_INFO, "WPS: TKIP not supported");
- 			goto fail;
-diff --git a/src/ap/x_snoop.c b/src/ap/x_snoop.c
-index 029f4de23..4c20f137f 100644
---- a/src/ap/x_snoop.c
-+++ b/src/ap/x_snoop.c
-@@ -33,28 +33,31 @@ int x_snoop_init(struct hostapd_data *hapd)
- 
- 	hapd->x_snoop_initialized = true;
- 
--	if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
-+	if (!conf->snoop_iface[0] &&
-+	    hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
- 					 1)) {
- 		wpa_printf(MSG_DEBUG,
- 			   "x_snoop: Failed to enable hairpin_mode on the bridge port");
- 		return -1;
- 	}
- 
--	if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
-+	if (!conf->snoop_iface[0] &&
-+	    hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
- 		wpa_printf(MSG_DEBUG,
- 			   "x_snoop: Failed to enable proxyarp on the bridge port");
- 		return -1;
- 	}
- 
- 	if (hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
--					 1)) {
-+					 conf->snoop_iface[0] ? conf->snoop_iface : NULL, 1)) {
- 		wpa_printf(MSG_DEBUG,
- 			   "x_snoop: Failed to enable accepting gratuitous ARP on the bridge");
- 		return -1;
- 	}
- 
- #ifdef CONFIG_IPV6
--	if (hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, 1)) {
-+	if (!conf->snoop_iface[0] &&
-+	    hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, NULL, 1)) {
- 		wpa_printf(MSG_DEBUG,
- 			   "x_snoop: Failed to enable multicast snooping on the bridge");
- 		return -1;
-@@ -73,8 +76,12 @@ x_snoop_get_l2_packet(struct hostapd_data *hapd,
- {
- 	struct hostapd_bss_config *conf = hapd->conf;
- 	struct l2_packet_data *l2;
-+	const char *ifname = conf->bridge;
-+
-+	if (conf->snoop_iface[0])
-+		ifname = conf->snoop_iface;
- 
--	l2 = l2_packet_init(conf->bridge, NULL, ETH_P_ALL, handler, hapd, 1);
-+	l2 = l2_packet_init(ifname, NULL, ETH_P_ALL, handler, hapd, 1);
- 	if (l2 == NULL) {
- 		wpa_printf(MSG_DEBUG,
- 			   "x_snoop: Failed to initialize L2 packet processing %s",
-@@ -127,9 +134,12 @@ void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
- 
- void x_snoop_deinit(struct hostapd_data *hapd)
- {
-+	struct hostapd_bss_config *conf = hapd->conf;
-+
- 	if (!hapd->x_snoop_initialized)
- 		return;
--	hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0);
-+	hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
-+				     conf->snoop_iface[0] ? conf->snoop_iface : NULL, 0);
- 	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0);
- 	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0);
- 	hapd->x_snoop_initialized = false;
-diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c
-index f17f95a2c..39d39f429 100644
---- a/src/common/dpp_crypto.c
-+++ b/src/common/dpp_crypto.c
-@@ -269,6 +269,12 @@ int dpp_get_pubkey_hash(struct crypto_ec_key *key, u8 *hash)
- 
- struct crypto_ec_key * dpp_gen_keypair(const struct dpp_curve_params *curve)
- {
-+	if (curve == NULL) {
-+		wpa_printf(MSG_DEBUG,
-+		           "DPP: %s curve must be initialized", __func__);
-+		return NULL;
-+	}
-+
- 	struct crypto_ec_key *key;
- 
- 	wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
-@@ -1582,7 +1588,9 @@ dpp_pkex_derive_Qr(const struct dpp_curve_params *curve, const u8 *mac_resp,
- 	Pr = crypto_ec_key_get_public_key(Pr_key);
- 	Qr = crypto_ec_point_init(ec);
- 	hash_bn = crypto_bignum_init_set(hash, curve->hash_len);
--	if (!Pr || !Qr || !hash_bn || crypto_ec_point_mul(ec, Pr, hash_bn, Qr))
-+	if (!Pr || !Qr || !hash_bn ||
-+	    crypto_bignum_mod(hash_bn, crypto_ec_get_prime(ec), hash_bn) ||
-+	    crypto_ec_point_mul(ec, Pr, hash_bn, Qr))
- 		goto fail;
- 
- 	if (crypto_ec_point_is_at_infinity(ec, Qr)) {
-diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
-index 2c47bf812..8bd6e994d 100644
---- a/src/common/hw_features_common.c
-+++ b/src/common/hw_features_common.c
-@@ -898,6 +898,7 @@ int ieee80211ac_cap_check(u32 hw, u32 conf)
- 	VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
- 	VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
- 	VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
-+	VHT_CAP_CHECK(VHT_CAP_EXTENDED_NSS_BW_SUPPORT);
- 
- #undef VHT_CAP_CHECK
- #undef VHT_CAP_CHECK_MAX
-diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
-index 5b39a61e1..7a1da3252 100644
---- a/src/common/ieee802_11_defs.h
-+++ b/src/common/ieee802_11_defs.h
-@@ -1397,6 +1397,8 @@ struct ieee80211_ampe_ie {
- #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB     ((u32) BIT(26) | BIT(27))
- #define VHT_CAP_RX_ANTENNA_PATTERN                  ((u32) BIT(28))
- #define VHT_CAP_TX_ANTENNA_PATTERN                  ((u32) BIT(29))
-+#define VHT_CAP_EXTENDED_NSS_BW_SUPPORT		    ((u32) BIT(30))
-+#define VHT_CAP_EXTENDED_NSS_BW_SUPPORT_MASK	    ((u32) BIT(30) | BIT(31))
- 
- #define VHT_OPMODE_CHANNEL_WIDTH_MASK		    ((u8) BIT(0) | BIT(1))
- #define VHT_OPMODE_CHANNEL_RxNSS_MASK		    ((u8) BIT(4) | BIT(5) | \
-diff --git a/src/common/sae.c b/src/common/sae.c
-index f1c164e13..05de737e5 100644
---- a/src/common/sae.c
-+++ b/src/common/sae.c
-@@ -1278,6 +1278,13 @@ void sae_deinit_pt(struct sae_pt *pt)
- static int sae_derive_commit_element_ecc(struct sae_data *sae,
- 					 struct crypto_bignum *mask)
- {
-+	if (sae->tmp->pwe_ecc == NULL) {
-+		wpa_printf(MSG_DEBUG,
-+		           "SAE: %s sae->tmp->pwe_ecc must be initialized",
-+		           __func__);
-+		return -1;
-+	}
-+
- 	/* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
- 	if (!sae->tmp->own_commit_element_ecc) {
- 		sae->tmp->own_commit_element_ecc =
-diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
-index 6ea3311ce..7a608c30e 100644
---- a/src/common/wpa_common.c
-+++ b/src/common/wpa_common.c
-@@ -2856,6 +2856,31 @@ u32 wpa_akm_to_suite(int akm)
- }
- 
- 
-+static void wpa_fixup_wpa_ie_rsn(u8 *assoc_ie, const u8 *wpa_msg_ie,
-+				 size_t rsn_ie_len)
-+{
-+	int pos, count;
-+
-+	pos = sizeof(struct rsn_ie_hdr) + RSN_SELECTOR_LEN;
-+	if (rsn_ie_len < pos + 2)
-+		return;
-+
-+	count = WPA_GET_LE16(wpa_msg_ie + pos);
-+	pos += 2 + count * RSN_SELECTOR_LEN;
-+	if (rsn_ie_len < pos + 2)
-+		return;
-+
-+	count = WPA_GET_LE16(wpa_msg_ie + pos);
-+	pos += 2 + count * RSN_SELECTOR_LEN;
-+	if (rsn_ie_len < pos + 2)
-+		return;
-+
-+	if (!assoc_ie[pos] && !assoc_ie[pos + 1] &&
-+	    (wpa_msg_ie[pos] || wpa_msg_ie[pos + 1]))
-+		memcpy(&assoc_ie[pos], &wpa_msg_ie[pos], 2);
-+}
-+
-+
- int wpa_compare_rsn_ie(int ft_initial_assoc,
- 		       const u8 *ie1, size_t ie1len,
- 		       const u8 *ie2, size_t ie2len)
-@@ -2863,8 +2888,19 @@ int wpa_compare_rsn_ie(int ft_initial_assoc,
- 	if (ie1 == NULL || ie2 == NULL)
- 		return -1;
- 
--	if (ie1len == ie2len && os_memcmp(ie1, ie2, ie1len) == 0)
--		return 0; /* identical IEs */
-+	if (ie1len == ie2len) {
-+		u8 *ie_tmp;
-+
-+		if (os_memcmp(ie1, ie2, ie1len) == 0)
-+			return 0; /* identical IEs */
-+
-+		ie_tmp = alloca(ie1len);
-+		memcpy(ie_tmp, ie1, ie1len);
-+		wpa_fixup_wpa_ie_rsn(ie_tmp, ie2, ie1len);
-+
-+		if (os_memcmp(ie_tmp, ie2, ie1len) == 0)
-+			return 0; /* only mismatch in RSN capabilties */
-+	}
- 
- #ifdef CONFIG_IEEE80211R
- 	if (ft_initial_assoc) {
-diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c
-index 7e197f094..791fdbf93 100644
---- a/src/common/wpa_ctrl.c
-+++ b/src/common/wpa_ctrl.c
-@@ -135,7 +135,7 @@ try_again:
- 		return NULL;
- 	}
- 	tries++;
--#ifdef ANDROID
-+
- 	/* Set client socket file permissions so that bind() creates the client
- 	 * socket with these permissions and there is no need to try to change
- 	 * them with chmod() after bind() which would have potential issues with
-@@ -147,7 +147,7 @@ try_again:
- 	 * operations to allow the response to go through. Those are using the
- 	 * no-deference-symlinks version to avoid races. */
- 	fchmod(ctrl->s, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
--#endif /* ANDROID */
-+
- 	if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
- 		    sizeof(ctrl->local)) < 0) {
- 		if (errno == EADDRINUSE && tries < 2) {
-@@ -165,7 +165,11 @@ try_again:
- 		return NULL;
- 	}
- 
--#ifdef ANDROID
-+#ifndef ANDROID
-+	/* Set group even if we do not have privileges to change owner */
-+	lchown(ctrl->local.sun_path, -1, 101);
-+	lchown(ctrl->local.sun_path, 101, 101);
-+#else
- 	/* Set group even if we do not have privileges to change owner */
- 	lchown(ctrl->local.sun_path, -1, AID_WIFI);
- 	lchown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
-diff --git a/src/crypto/Makefile b/src/crypto/Makefile
-index ce0997091..96bac9476 100644
---- a/src/crypto/Makefile
-+++ b/src/crypto/Makefile
-@@ -1,10 +1,121 @@
--CFLAGS += -DCONFIG_CRYPTO_INTERNAL
--CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
--CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
- #CFLAGS += -DALL_DH_GROUPS
- CFLAGS += -DCONFIG_SHA256
- CFLAGS += -DCONFIG_SHA384
-+CFLAGS += -DCONFIG_HMAC_SHA256_KDF
- CFLAGS += -DCONFIG_HMAC_SHA384_KDF
-+
-+# crypto_module_tests.c
-+CFLAGS += -DCONFIG_MODULE_TESTS
-+CFLAGS += -DCONFIG_DPP
-+#CFLAGS += -DCONFIG_DPP2
-+#CFLAGS += -DCONFIG_DPP3
-+CFLAGS += -DCONFIG_ECC
-+CFLAGS += -DCONFIG_MESH
-+CFLAGS += -DEAP_PSK
-+CFLAGS += -DEAP_FAST
-+
-+ifeq ($(CONFIG_TLS),mbedtls)
-+
-+# (enable features for 'cd tests; make run-tests CONFIG_TLS=mbedtls')
-+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
-+CFLAGS += -DCONFIG_DES
-+CFLAGS += -DEAP_IKEV2
-+CFLAGS += -DEAP_MSCHAPv2
-+CFLAGS += -DEAP_SIM
-+
-+LIB_OBJS = tls_mbedtls.o crypto_mbedtls.o
-+LIB_OBJS+= \
-+	aes-eax.o \
-+	aes-siv.o \
-+	dh_groups.o \
-+	milenage.o \
-+	ms_funcs.o
-+
-+else
-+ifeq ($(CONFIG_TLS),openssl)
-+
-+# (enable features for 'cd tests; make run-tests CONFIG_TLS=openssl')
-+ifndef CONFIG_TLS_DEFAULT_CIPHERS
-+CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
-+endif
-+CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
-+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
-+CFLAGS += -DEAP_TLS_OPENSSL
-+
-+LIB_OBJS = tls_openssl.o fips_prf_openssl.o crypto_openssl.o
-+LIB_OBJS+= \
-+	aes-ctr.o \
-+	aes-eax.o \
-+	aes-encblock.o \
-+	aes-siv.o \
-+	dh_groups.o \
-+	milenage.o \
-+	ms_funcs.o \
-+	sha1-prf.o \
-+	sha1-tlsprf.o \
-+	sha1-tprf.o \
-+	sha256-kdf.o \
-+	sha256-prf.o \
-+	sha256-tlsprf.o
-+
-+else
-+ifeq ($(CONFIG_TLS),wolfssl)
-+
-+# (wolfssl libraries must be built with ./configure --enable-wpas)
-+# (enable features for 'cd tests; make run-tests CONFIG_TLS=wolfssl')
-+CFLAGS += -DWOLFSSL_DER_LOAD
-+CFLAGS += -DCONFIG_DES
-+
-+LIB_OBJS = tls_wolfssl.o fips_prf_wolfssl.o crypto_wolfssl.o
-+LIB_OBJS+= \
-+	aes-ctr.o \
-+	aes-eax.o \
-+	aes-encblock.o \
-+	aes-siv.o \
-+	dh_groups.o \
-+	milenage.o \
-+	ms_funcs.o \
-+	sha1-prf.o \
-+	sha1-tlsprf.o \
-+	sha1-tprf.o \
-+	sha256-kdf.o \
-+	sha256-prf.o \
-+	sha256-tlsprf.o
-+
-+else
-+ifeq ($(CONFIG_TLS),gnutls)
-+
-+# (enable features for 'cd tests; make run-tests CONFIG_TLS=gnutls')
-+LIB_OBJS = tls_gnutls.o crypto_gnutls.o
-+LIB_OBJS+= \
-+	aes-cbc.o \
-+	aes-ctr.o \
-+	aes-eax.o \
-+	aes-encblock.o \
-+	aes-omac1.o \
-+	aes-siv.o \
-+	aes-unwrap.o \
-+	aes-wrap.o \
-+	dh_group5.o \
-+	dh_groups.o \
-+	milenage.o \
-+	ms_funcs.o \
-+	rc4.o \
-+	sha1-pbkdf2.o \
-+	sha1-prf.o \
-+	fips_prf_internal.o \
-+	sha1-internal.o \
-+	sha1-tlsprf.o \
-+	sha1-tprf.o \
-+	sha256-kdf.o \
-+	sha256-prf.o \
-+	sha256-tlsprf.o
-+
-+else
-+
-+CFLAGS += -DCONFIG_CRYPTO_INTERNAL
-+CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
-+CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
- CFLAGS += -DCONFIG_INTERNAL_SHA384
- 
- LIB_OBJS= \
-@@ -13,7 +124,6 @@ LIB_OBJS= \
- 	aes-ctr.o \
- 	aes-eax.o \
- 	aes-encblock.o \
--	aes-gcm.o \
- 	aes-internal.o \
- 	aes-internal-dec.o \
- 	aes-internal-enc.o \
-@@ -37,6 +147,7 @@ LIB_OBJS= \
- 	sha1-tlsprf.o \
- 	sha1-tprf.o \
- 	sha256.o \
-+	sha256-kdf.o \
- 	sha256-prf.o \
- 	sha256-tlsprf.o \
- 	sha256-internal.o \
-@@ -53,6 +164,16 @@ LIB_OBJS += crypto_internal-modexp.o
- LIB_OBJS += crypto_internal-rsa.o
- LIB_OBJS += tls_internal.o
- LIB_OBJS += fips_prf_internal.o
-+
-+endif
-+endif
-+endif
-+endif
-+
-+
-+# (used by wlantest/{bip,gcmp,rx_mgmt}.c and tests/test-aes.c)
-+LIB_OBJS += aes-gcm.o
-+
- ifndef TEST_FUZZ
- LIB_OBJS += random.o
- endif
-diff --git a/src/crypto/crypto_mbedtls.c b/src/crypto/crypto_mbedtls.c
-new file mode 100644
-index 000000000..7a91c965f
---- /dev/null
-+++ b/src/crypto/crypto_mbedtls.c
-@@ -0,0 +1,4228 @@
-+/*
-+ * crypto wrapper functions for mbed TLS
-+ *
-+ * SPDX-FileCopyrightText: 2022 Glenn Strauss <gstrauss@gluelogic.com>
-+ * SPDX-License-Identifier: BSD-3-Clause
-+ */
-+
-+#include "utils/includes.h"
-+#include "utils/common.h"
-+
-+#include <mbedtls/version.h>
-+#include <mbedtls/entropy.h>
-+#include <mbedtls/ctr_drbg.h>
-+#include <mbedtls/platform_util.h> /* mbedtls_platform_zeroize() */
-+#include <mbedtls/asn1.h>
-+#include <mbedtls/asn1write.h>
-+#include <mbedtls/aes.h>
-+#include <mbedtls/md.h>
-+#include <mbedtls/md5.h>
-+#include <mbedtls/sha1.h>
-+#include <mbedtls/sha256.h>
-+#include <mbedtls/sha512.h>
-+
-+#ifndef MBEDTLS_PRIVATE
-+#define MBEDTLS_PRIVATE(x) x
-+#endif
-+
-+/* hostapd/wpa_supplicant provides forced_memzero(),
-+ * but prefer mbedtls_platform_zeroize() */
-+#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz)
-+
-+#ifndef __has_attribute
-+#define __has_attribute(x) 0
-+#endif
-+
-+#ifndef __GNUC_PREREQ
-+#define __GNUC_PREREQ(maj,min) 0
-+#endif
-+
-+#ifndef __attribute_cold__
-+#if __has_attribute(cold) \
-+ || __GNUC_PREREQ(4,3)
-+#define __attribute_cold__  __attribute__((__cold__))
-+#else
-+#define __attribute_cold__
-+#endif
-+#endif
-+
-+#ifndef __attribute_noinline__
-+#if __has_attribute(noinline) \
-+ || __GNUC_PREREQ(3,1)
-+#define __attribute_noinline__  __attribute__((__noinline__))
-+#else
-+#define __attribute_noinline__
-+#endif
-+#endif
-+
-+#include "crypto.h"
-+#include "aes_wrap.h"
-+#include "aes.h"
-+#include "md5.h"
-+#include "sha1.h"
-+#include "sha256.h"
-+#include "sha384.h"
-+#include "sha512.h"
-+
-+
-+/*
-+ * selective code inclusion based on preprocessor defines
-+ *
-+ * future: additional code could be wrapped with preprocessor checks if
-+ * wpa_supplicant/Makefile and hostap/Makefile were more consistent with
-+ * setting preprocessor defines for named groups of functionality
-+ */
-+
-+#if defined(CONFIG_FIPS)
-+#undef MBEDTLS_MD4_C     /* omit md4_vector() */
-+#undef MBEDTLS_MD5_C     /* omit md5_vector() hmac_md5_vector() hmac_md5() */
-+#undef MBEDTLS_DES_C     /* omit des_encrypt() */
-+#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */
-+#define CRYPTO_MBEDTLS_CONFIG_FIPS
-+#endif
-+
-+#if !defined(CONFIG_FIPS)
-+#if defined(EAP_PWD) \
-+ || defined(EAP_LEAP) || defined(EAP_LEAP_DYNAMIC) \
-+ || defined(EAP_TTLS) || defined(EAP_TTLS_DYNAMIC) \
-+ || defined(EAP_MSCHAPv2) || defined(EAP_MSCHAPv2_DYNAMIC) \
-+ || defined(EAP_SERVER_MSCHAPV2)
-+#ifndef MBEDTLS_MD4_C    /* (MD4 not in mbedtls 3.x) */
-+#include "md4-internal.c"/* pull in hostap local implementation */
-+#endif /* md4_vector() */
-+#else
-+#undef MBEDTLS_MD4_C     /* omit md4_vector() */
-+#endif
-+#endif
-+
-+#if !defined(CONFIG_NO_RC4) && !defined(CONFIG_NO_WPA)
-+#ifndef MBEDTLS_ARC4_C   /* (RC4 not in mbedtls 3.x) */
-+#include "rc4.c"         /* pull in hostap local implementation */
-+#endif /* rc4_skip() */
-+#else
-+#undef MBEDTLS_ARC4_C    /* omit rc4_skip() */
-+#endif
-+
-+#if defined(CONFIG_MACSEC)     \
-+ || defined(CONFIG_NO_RADIUS)  \
-+ || defined(CONFIG_IEEE80211R) \
-+ || defined(EAP_SERVER_FAST)   \
-+ || defined(EAP_SERVER_TEAP)   \
-+ || !defined(CONFIG_NO_WPA)
-+       /* aes_wrap() aes_unwrap() */
-+#else
-+#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */
-+#endif
-+
-+#if !defined(CONFIG_SHA256)
-+#undef MBEDTLS_SHA256_C
-+#endif
-+
-+#if !defined(CONFIG_SHA384) && !defined(CONFIG_SHA512)
-+#undef MBEDTLS_SHA512_C
-+#endif
-+
-+#if defined(CONFIG_HMAC_SHA256_KDF)
-+#define CRYPTO_MBEDTLS_HMAC_KDF_SHA256
-+#endif
-+#if defined(CONFIG_HMAC_SHA384_KDF)
-+#define CRYPTO_MBEDTLS_HMAC_KDF_SHA384
-+#endif
-+#if defined(CONFIG_HMAC_SHA512_KDF)
-+#define CRYPTO_MBEDTLS_HMAC_KDF_SHA512
-+#endif
-+
-+#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \
-+ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA)
-+/* EAP_SIM=y EAP_AKA=y */
-+#define CRYPTO_MBEDTLS_FIPS186_2_PRF
-+#endif
-+
-+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
-+ || defined(EAP_TEAP) || defined(EAP_TEAP_DYNAMIC) || defined(EAP_SERVER_FAST)
-+#define CRYPTO_MBEDTLS_SHA1_T_PRF
-+#endif
-+
-+#if defined(CONFIG_DES)
-+#define CRYPTO_MBEDTLS_DES_ENCRYPT
-+#endif /* des_encrypt() */
-+
-+#if !defined(CONFIG_NO_PBKDF2)
-+#define CRYPTO_MBEDTLS_PBKDF2_SHA1
-+#endif /* pbkdf2_sha1() */
-+
-+#if defined(EAP_IKEV2) \
-+ || defined(EAP_IKEV2_DYNAMIC) \
-+ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_CIPHER
-+#endif /* crypto_cipher_*() */
-+
-+#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_HASH
-+#endif /* crypto_hash_*() */
-+
-+#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */ \
-+ || defined(CONFIG_SAE) /* CONFIG_SAE=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+#endif /* crypto_bignum_*() */
-+
-+#if defined(EAP_PWD)          /* CONFIG_EAP_PWD=y */    \
-+ || defined(EAP_EKE)          /* CONFIG_EAP_EKE=y */    \
-+ || defined(EAP_EKE_DYNAMIC)  /* CONFIG_EAP_EKE=y */    \
-+ || defined(EAP_SERVER_EKE)   /* CONFIG_EAP_EKE=y */    \
-+ || defined(EAP_IKEV2)        /* CONFIG_EAP_IKEV2y */   \
-+ || defined(EAP_IKEV2_DYNAMIC)/* CONFIG_EAP_IKEV2=y */  \
-+ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */  \
-+ || defined(CONFIG_SAE)       /* CONFIG_SAE=y */        \
-+ || defined(CONFIG_WPS)       /* CONFIG_WPS=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_DH
-+#if defined(CONFIG_WPS_NFC)
-+#define CRYPTO_MBEDTLS_DH5_INIT_FIXED
-+#endif /* dh5_init_fixed() */
-+#endif /* crypto_dh_*() */
-+
-+#if !defined(CONFIG_NO_WPA) /* CONFIG_NO_WPA= */
-+#define CRYPTO_MBEDTLS_CRYPTO_ECDH
-+#endif /* crypto_ecdh_*() */
-+
-+#if defined(CONFIG_ECC)
-+#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+#define CRYPTO_MBEDTLS_CRYPTO_EC
-+#endif /* crypto_ec_*() crypto_ec_key_*() */
-+
-+#if defined(CONFIG_DPP) /* CONFIG_DPP=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_EC_DPP /* extra for DPP */
-+#define CRYPTO_MBEDTLS_CRYPTO_CSR
-+#endif /* crypto_csr_*() */
-+
-+#if defined(CONFIG_DPP3) /* CONFIG_DPP3=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_HPKE
-+#endif
-+
-+#if defined(CONFIG_DPP2) /* CONFIG_DPP2=y */
-+#define CRYPTO_MBEDTLS_CRYPTO_PKCS7
-+#endif /* crypto_pkcs7_*() */
-+
-+#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \
-+ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA) \
-+ || defined(CONFIG_AP) || defined(HOSTAPD)
-+/* CONFIG_EAP_SIM=y CONFIG_EAP_AKA=y CONFIG_AP=y HOSTAPD */
-+#if defined(CRYPTO_RSA_OAEP_SHA256)
-+#define CRYPTO_MBEDTLS_CRYPTO_RSA
-+#endif
-+#endif /* crypto_rsa_*() */
-+
-+
-+static int ctr_drbg_init_state;
-+static mbedtls_ctr_drbg_context ctr_drbg;
-+static mbedtls_entropy_context entropy;
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+#include <mbedtls/bignum.h>
-+static mbedtls_mpi mpi_sw_A;
-+#endif
-+
-+__attribute_cold__
-+__attribute_noinline__
-+static mbedtls_ctr_drbg_context * ctr_drbg_init(void)
-+{
-+	mbedtls_ctr_drbg_init(&ctr_drbg);
-+	mbedtls_entropy_init(&entropy);
-+	if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
-+	                          NULL, 0)) {
-+		wpa_printf(MSG_ERROR, "Init of random number generator failed");
-+		/* XXX: abort? */
-+	}
-+	else
-+		ctr_drbg_init_state = 1;
-+
-+	return &ctr_drbg;
-+}
-+
-+__attribute_cold__
-+void crypto_unload(void)
-+{
-+	if (ctr_drbg_init_state) {
-+		mbedtls_ctr_drbg_free(&ctr_drbg);
-+		mbedtls_entropy_free(&entropy);
-+	  #ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+		mbedtls_mpi_free(&mpi_sw_A);
-+	  #endif
-+		ctr_drbg_init_state = 0;
-+	}
-+}
-+
-+/* init ctr_drbg on first use
-+ * crypto_global_init() and crypto_global_deinit() are not available here
-+ * (available only when CONFIG_TLS=internal, which is not CONFIG_TLS=mbedtls) */
-+mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/
-+inline
-+mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void)
-+{
-+	return ctr_drbg_init_state ? &ctr_drbg : ctr_drbg_init();
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CONFIG_FIPS
-+int crypto_get_random(void *buf, size_t len)
-+{
-+	return mbedtls_ctr_drbg_random(crypto_mbedtls_ctr_drbg(),buf,len) ? -1 : 0;
-+}
-+#endif
-+
-+
-+#if 1
-+
-+/* tradeoff: slightly smaller code size here at cost of slight increase
-+ * in instructions and function calls at runtime versus the expanded
-+ * per-message-digest code that follows in #else (~0.5 kib .text larger) */
-+
-+__attribute_noinline__
-+static int md_vector(size_t num_elem, const u8 *addr[], const size_t *len,
-+                     u8 *mac, mbedtls_md_type_t md_type)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	mbedtls_md_context_t ctx;
-+	mbedtls_md_init(&ctx);
-+	if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0) != 0){
-+		mbedtls_md_free(&ctx);
-+		return -1;
-+	}
-+	mbedtls_md_starts(&ctx);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_md_update(&ctx, addr[i], len[i]);
-+	mbedtls_md_finish(&ctx, mac);
-+	mbedtls_md_free(&ctx);
-+	return 0;
-+}
-+
-+#ifdef MBEDTLS_SHA512_C
-+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA512);
-+}
-+
-+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA384);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA256);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA1_C
-+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA1);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD5_C
-+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD5);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD4_C
-+#include <mbedtls/md4.h>
-+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD4);
-+}
-+#endif
-+
-+#else  /* expanded per-message-digest functions */
-+
-+#ifdef MBEDTLS_SHA512_C
-+#include <mbedtls/sha512.h>
-+__attribute_noinline__
-+static int sha384_512_vector(size_t num_elem, const u8 *addr[],
-+                             const size_t *len, u8 *mac, int is384)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	struct mbedtls_sha512_context ctx;
-+	mbedtls_sha512_init(&ctx);
-+  #if MBEDTLS_VERSION_MAJOR >= 3
-+	mbedtls_sha512_starts(&ctx, is384);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_sha512_update(&ctx, addr[i], len[i]);
-+	mbedtls_sha512_finish(&ctx, mac);
-+  #else
-+	mbedtls_sha512_starts_ret(&ctx, is384);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_sha512_update_ret(&ctx, addr[i], len[i]);
-+	mbedtls_sha512_finish_ret(&ctx, mac);
-+  #endif
-+	mbedtls_sha512_free(&ctx);
-+	return 0;
-+}
-+
-+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return sha384_512_vector(num_elem, addr, len, mac, 0);
-+}
-+
-+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return sha384_512_vector(num_elem, addr, len, mac, 1);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+#include <mbedtls/sha256.h>
-+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	struct mbedtls_sha256_context ctx;
-+	mbedtls_sha256_init(&ctx);
-+  #if MBEDTLS_VERSION_MAJOR >= 3
-+	mbedtls_sha256_starts(&ctx, 0);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_sha256_update(&ctx, addr[i], len[i]);
-+	mbedtls_sha256_finish(&ctx, mac);
-+  #else
-+	mbedtls_sha256_starts_ret(&ctx, 0);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_sha256_update_ret(&ctx, addr[i], len[i]);
-+	mbedtls_sha256_finish_ret(&ctx, mac);
-+  #endif
-+	mbedtls_sha256_free(&ctx);
-+	return 0;
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA1_C
-+#include <mbedtls/sha1.h>
-+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	struct mbedtls_sha1_context ctx;
-+	mbedtls_sha1_init(&ctx);
-+  #if MBEDTLS_VERSION_MAJOR >= 3
-+	mbedtls_sha1_starts(&ctx);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_sha1_update(&ctx, addr[i], len[i]);
-+	mbedtls_sha1_finish(&ctx, mac);
-+  #else
-+	mbedtls_sha1_starts_ret(&ctx);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_sha1_update_ret(&ctx, addr[i], len[i]);
-+	mbedtls_sha1_finish_ret(&ctx, mac);
-+  #endif
-+	mbedtls_sha1_free(&ctx);
-+	return 0;
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD5_C
-+#include <mbedtls/md5.h>
-+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	struct mbedtls_md5_context ctx;
-+	mbedtls_md5_init(&ctx);
-+  #if MBEDTLS_VERSION_MAJOR >= 3
-+	mbedtls_md5_starts(&ctx);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_md5_update(&ctx, addr[i], len[i]);
-+	mbedtls_md5_finish(&ctx, mac);
-+  #else
-+	mbedtls_md5_starts_ret(&ctx);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_md5_update_ret(&ctx, addr[i], len[i]);
-+	mbedtls_md5_finish_ret(&ctx, mac);
-+  #endif
-+	mbedtls_md5_free(&ctx);
-+	return 0;
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD4_C
-+#include <mbedtls/md4.h>
-+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	struct mbedtls_md4_context ctx;
-+	mbedtls_md4_init(&ctx);
-+	mbedtls_md4_starts_ret(&ctx);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_md4_update_ret(&ctx, addr[i], len[i]);
-+	mbedtls_md4_finish_ret(&ctx, mac);
-+	mbedtls_md4_free(&ctx);
-+	return 0;
-+}
-+#endif
-+
-+#endif /* expanded per-message-digest functions */
-+
-+
-+__attribute_noinline__
-+static int hmac_vector(const u8 *key, size_t key_len, size_t num_elem,
-+                       const u8 *addr[], const size_t *len, u8 *mac,
-+                       mbedtls_md_type_t md_type)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	mbedtls_md_context_t ctx;
-+	mbedtls_md_init(&ctx);
-+	if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1) != 0){
-+		mbedtls_md_free(&ctx);
-+		return -1;
-+	}
-+	mbedtls_md_hmac_starts(&ctx, key, key_len);
-+	for (size_t i = 0; i < num_elem; ++i)
-+		mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+	mbedtls_md_hmac_finish(&ctx, mac);
-+	mbedtls_md_free(&ctx);
-+	return 0;
-+}
-+
-+#ifdef MBEDTLS_SHA512_C
-+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
-+                       const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+			   MBEDTLS_MD_SHA512);
-+}
-+
-+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+                u8 *mac)
-+{
-+	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+			   MBEDTLS_MD_SHA512);
-+}
-+
-+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
-+                       const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+			   MBEDTLS_MD_SHA384);
-+}
-+
-+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+                u8 *mac)
-+{
-+	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+			   MBEDTLS_MD_SHA384);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
-+                       const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+			   MBEDTLS_MD_SHA256);
-+}
-+
-+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+                u8 *mac)
-+{
-+	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+			   MBEDTLS_MD_SHA256);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA1_C
-+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
-+                     const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+			   MBEDTLS_MD_SHA1);
-+}
-+
-+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+              u8 *mac)
-+{
-+	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+			   MBEDTLS_MD_SHA1);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_MD5_C
-+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
-+                    const u8 *addr[], const size_t *len, u8 *mac)
-+{
-+	return hmac_vector(key, key_len, num_elem, addr, len, mac,
-+			   MBEDTLS_MD_MD5);
-+}
-+
-+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
-+             u8 *mac)
-+{
-+	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
-+			   MBEDTLS_MD_MD5);
-+}
-+#endif
-+
-+
-+#if defined(MBEDTLS_SHA256_C) || defined(MBEDTLS_SHA512_C)
-+
-+#if defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA256) \
-+ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA384) \
-+ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA512)
-+
-+#include <mbedtls/hkdf.h>
-+
-+/* sha256-kdf.c sha384-kdf.c sha512-kdf.c */
-+
-+/* HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869) */
-+/* HMAC-SHA384 KDF (RFC 5295) and HKDF-Expand(SHA384) (RFC 5869) */
-+/* HMAC-SHA512 KDF (RFC 5295) and HKDF-Expand(SHA512) (RFC 5869) */
-+__attribute_noinline__
-+static int hmac_kdf_expand(const u8 *prk, size_t prk_len,
-+                           const char *label, const u8 *info, size_t info_len,
-+                           u8 *okm, size_t okm_len, mbedtls_md_type_t md_type)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
-+  #ifdef MBEDTLS_HKDF_C
-+	if (label == NULL)  /* RFC 5869 HKDF-Expand when (label == NULL) */
-+		return mbedtls_hkdf_expand(md_info, prk, prk_len, info,
-+		                           info_len, okm, okm_len) ? -1 : 0;
-+  #endif
-+
-+	const size_t mac_len = mbedtls_md_get_size(md_info);
-+	/* okm_len must not exceed 255 times hash len (RFC 5869 Section 2.3) */
-+	if (okm_len > ((mac_len << 8) - mac_len))
-+		return -1;
-+
-+	mbedtls_md_context_t ctx;
-+	mbedtls_md_init(&ctx);
-+	if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
-+		mbedtls_md_free(&ctx);
-+		return -1;
-+	}
-+	mbedtls_md_hmac_starts(&ctx, prk, prk_len);
-+
-+	u8 iter = 1;
-+	const u8 *addr[4] = { okm, (const u8 *)label, info, &iter };
-+	size_t len[4] = { 0, label ? os_strlen(label)+1 : 0, info_len, 1 };
-+
-+	for (; okm_len >= mac_len; okm_len -= mac_len, ++iter) {
-+		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
-+			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+		mbedtls_md_hmac_finish(&ctx, okm);
-+		mbedtls_md_hmac_reset(&ctx);
-+		addr[0] = okm;
-+		okm += mac_len;
-+		len[0] = mac_len; /*(include digest in subsequent rounds)*/
-+	}
-+
-+	if (okm_len) {
-+		u8 hash[MBEDTLS_MD_MAX_SIZE];
-+		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
-+			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+		mbedtls_md_hmac_finish(&ctx, hash);
-+		os_memcpy(okm, hash, okm_len);
-+		forced_memzero(hash, mac_len);
-+	}
-+
-+	mbedtls_md_free(&ctx);
-+	return 0;
-+}
-+
-+#ifdef MBEDTLS_SHA512_C
-+#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA512
-+int hmac_sha512_kdf(const u8 *secret, size_t secret_len,
-+		    const char *label, const u8 *seed, size_t seed_len,
-+		    u8 *out, size_t outlen)
-+{
-+	return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
-+	                       out, outlen, MBEDTLS_MD_SHA512);
-+}
-+#endif
-+
-+#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA384
-+int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
-+		    const char *label, const u8 *seed, size_t seed_len,
-+		    u8 *out, size_t outlen)
-+{
-+	return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
-+	                       out, outlen, MBEDTLS_MD_SHA384);
-+}
-+#endif
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA256
-+int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
-+		    const char *label, const u8 *seed, size_t seed_len,
-+		    u8 *out, size_t outlen)
-+{
-+	return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
-+	                       out, outlen, MBEDTLS_MD_SHA256);
-+}
-+#endif
-+#endif
-+
-+#endif /* CRYPTO_MBEDTLS_HMAC_KDF_* */
-+
-+
-+/* sha256-prf.c sha384-prf.c sha512-prf.c */
-+
-+/* hmac_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function */
-+__attribute_noinline__
-+static int hmac_prf_bits(const u8 *key, size_t key_len, const char *label,
-+                         const u8 *data, size_t data_len, u8 *buf,
-+                         size_t buf_len_bits, mbedtls_md_type_t md_type)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	mbedtls_md_context_t ctx;
-+	mbedtls_md_init(&ctx);
-+	const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
-+	if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
-+		mbedtls_md_free(&ctx);
-+		return -1;
-+	}
-+	mbedtls_md_hmac_starts(&ctx, key, key_len);
-+
-+	u16 ctr, n_le = host_to_le16(buf_len_bits);
-+	const u8 * const addr[] = { (u8 *)&ctr,(u8 *)label,data,(u8 *)&n_le };
-+	const size_t len[] =      { 2, os_strlen(label), data_len, 2 };
-+	const size_t mac_len = mbedtls_md_get_size(md_info);
-+	size_t buf_len = (buf_len_bits + 7) / 8;
-+	for (ctr = 1; buf_len >= mac_len; buf_len -= mac_len, ++ctr) {
-+	  #if __BYTE_ORDER == __BIG_ENDIAN
-+		ctr = host_to_le16(ctr);
-+	  #endif
-+		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
-+			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+		mbedtls_md_hmac_finish(&ctx, buf);
-+		mbedtls_md_hmac_reset(&ctx);
-+		buf += mac_len;
-+	  #if __BYTE_ORDER == __BIG_ENDIAN
-+		ctr = le_to_host16(ctr);
-+	  #endif
-+	}
-+
-+	if (buf_len) {
-+		u8 hash[MBEDTLS_MD_MAX_SIZE];
-+	  #if __BYTE_ORDER == __BIG_ENDIAN
-+		ctr = host_to_le16(ctr);
-+	  #endif
-+		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
-+			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
-+		mbedtls_md_hmac_finish(&ctx, hash);
-+		os_memcpy(buf, hash, buf_len);
-+		buf += buf_len;
-+		forced_memzero(hash, mac_len);
-+	}
-+
-+	/* Mask out unused bits in last octet if it does not use all the bits */
-+	if ((buf_len_bits &= 0x7))
-+		buf[-1] &= (u8)(0xff << (8 - buf_len_bits));
-+
-+	mbedtls_md_free(&ctx);
-+	return 0;
-+}
-+
-+#ifdef MBEDTLS_SHA512_C
-+int sha512_prf(const u8 *key, size_t key_len, const char *label,
-+               const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
-+{
-+	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
-+	                     buf_len * 8, MBEDTLS_MD_SHA512);
-+}
-+
-+int sha384_prf(const u8 *key, size_t key_len, const char *label,
-+               const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
-+{
-+	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
-+	                     buf_len * 8, MBEDTLS_MD_SHA384);
-+}
-+#endif
-+
-+#ifdef MBEDTLS_SHA256_C
-+int sha256_prf(const u8 *key, size_t key_len, const char *label,
-+               const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
-+{
-+	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
-+	                     buf_len * 8, MBEDTLS_MD_SHA256);
-+}
-+
-+int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
-+                    const u8 *data, size_t data_len, u8 *buf,
-+                    size_t buf_len_bits)
-+{
-+	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
-+	                     buf_len_bits, MBEDTLS_MD_SHA256);
-+}
-+#endif
-+
-+#endif /* MBEDTLS_SHA256_C || MBEDTLS_SHA512_C */
-+
-+
-+#ifdef MBEDTLS_SHA1_C
-+
-+/* sha1-prf.c */
-+
-+/* sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) */
-+
-+int sha1_prf(const u8 *key, size_t key_len, const char *label,
-+	     const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
-+{
-+	/*(note: algorithm differs from hmac_prf_bits() */
-+	/*(note: smaller code size instead of expanding hmac_sha1_vector()
-+	 * as is done in hmac_prf_bits(); not expecting large num of loops) */
-+	u8 counter = 0;
-+	const u8 *addr[] = { (u8 *)label, data, &counter };
-+	const size_t len[] = { os_strlen(label)+1, data_len, 1 };
-+
-+	for (; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++counter) {
-+		if (hmac_sha1_vector(key, key_len, 3, addr, len, buf))
-+			return -1;
-+		buf += SHA1_MAC_LEN;
-+	}
-+
-+	if (buf_len) {
-+		u8 hash[SHA1_MAC_LEN];
-+		if (hmac_sha1_vector(key, key_len, 3, addr, len, hash))
-+			return -1;
-+		os_memcpy(buf, hash, buf_len);
-+		forced_memzero(hash, sizeof(hash));
-+	}
-+
-+	return 0;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_SHA1_T_PRF
-+
-+/* sha1-tprf.c */
-+
-+/* sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF) (RFC 4851,Section 5.5)*/
-+
-+int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
-+	       const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len)
-+{
-+	/*(note: algorithm differs from hmac_prf_bits() and hmac_kdf() above)*/
-+	/*(note: smaller code size instead of expanding hmac_sha1_vector()
-+	 * as is done in hmac_prf_bits(); not expecting large num of loops) */
-+	u8 ctr;
-+	u16 olen = host_to_be16(buf_len);
-+	const u8 *addr[] = { buf, (u8 *)label, seed, (u8 *)&olen, &ctr };
-+	size_t len[] = { 0, os_strlen(label)+1, seed_len, 2, 1 };
-+
-+	for (ctr = 1; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++ctr) {
-+		if (hmac_sha1_vector(key, key_len, 5, addr, len, buf))
-+			return -1;
-+		addr[0] = buf;
-+		buf += SHA1_MAC_LEN;
-+		len[0] = SHA1_MAC_LEN; /*(include digest in subsequent rounds)*/
-+	}
-+
-+	if (buf_len) {
-+		u8 hash[SHA1_MAC_LEN];
-+		if (hmac_sha1_vector(key, key_len, 5, addr, len, hash))
-+			return -1;
-+		os_memcpy(buf, hash, buf_len);
-+		forced_memzero(hash, sizeof(hash));
-+	}
-+
-+	return 0;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_SHA1_T_PRF */
-+
-+#ifdef CRYPTO_MBEDTLS_FIPS186_2_PRF
-+
-+/* fips_prf_internal.c sha1-internal.c */
-+
-+/* used only by src/eap_common/eap_sim_common.c:eap_sim_prf()
-+ * for eap_sim_derive_keys() and eap_sim_derive_keys_reauth()
-+ * where xlen is 160 */
-+
-+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
-+{
-+	/* FIPS 186-2 + change notice 1 */
-+
-+	mbedtls_sha1_context ctx;
-+	u8 * const xkey = ctx.MBEDTLS_PRIVATE(buffer);
-+	u32 * const xstate = ctx.MBEDTLS_PRIVATE(state);
-+	const u32 xstate_init[] =
-+	  { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 };
-+
-+	mbedtls_sha1_init(&ctx);
-+	os_memcpy(xkey, seed, seed_len < 64 ? seed_len : 64);
-+
-+	/* note: does not fill extra bytes if (xlen % 20) (SHA1_MAC_LEN) */
-+	for (; xlen >= 20; xlen -= 20) {
-+		/* XSEED_j = 0 */
-+		/* XVAL = (XKEY + XSEED_j) mod 2^b */
-+
-+		/* w_i = G(t, XVAL) */
-+		os_memcpy(xstate, xstate_init, sizeof(xstate_init));
-+		mbedtls_internal_sha1_process(&ctx, xkey);
-+
-+	  #if __BYTE_ORDER == __LITTLE_ENDIAN
-+		xstate[0] = host_to_be32(xstate[0]);
-+		xstate[1] = host_to_be32(xstate[1]);
-+		xstate[2] = host_to_be32(xstate[2]);
-+		xstate[3] = host_to_be32(xstate[3]);
-+		xstate[4] = host_to_be32(xstate[4]);
-+	  #endif
-+		os_memcpy(x, xstate, 20);
-+		if (xlen == 20) /*(done; skip prep for next loop)*/
-+			break;
-+
-+		/* XKEY = (1 + XKEY + w_i) mod 2^b */
-+		for (u32 carry = 1, k = 20; k-- > 0; carry >>= 8)
-+			xkey[k] = (carry += xkey[k] + x[k]) & 0xff;
-+		x += 20;
-+		/* x_j = w_0|w_1 (each pair of iterations through loop)*/
-+	}
-+
-+	mbedtls_sha1_free(&ctx);
-+	return 0;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_FIPS186_2_PRF */
-+
-+#endif /* MBEDTLS_SHA1_C */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_DES_ENCRYPT
-+#ifdef MBEDTLS_DES_C
-+#include <mbedtls/des.h>
-+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
-+{
-+	u8 pkey[8], next, tmp;
-+	int i;
-+
-+	/* Add parity bits to the key */
-+	next = 0;
-+	for (i = 0; i < 7; i++) {
-+		tmp = key[i];
-+		pkey[i] = (tmp >> i) | next | 1;
-+		next = tmp << (7 - i);
-+	}
-+	pkey[i] = next | 1;
-+
-+	mbedtls_des_context des;
-+	mbedtls_des_init(&des);
-+	int ret = mbedtls_des_setkey_enc(&des, pkey)
-+	       || mbedtls_des_crypt_ecb(&des, clear, cypher) ? -1 : 0;
-+	mbedtls_des_free(&des);
-+	return ret;
-+}
-+#else
-+#include "des-internal.c"/* pull in hostap local implementation */
-+#endif
-+#endif
-+
-+
-+#ifdef CRYPTO_MBEDTLS_PBKDF2_SHA1
-+/* sha1-pbkdf2.c */
-+#include <mbedtls/pkcs5.h>
-+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
-+                int iterations, u8 *buf, size_t buflen)
-+{
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03020200 /* mbedtls 3.2.2 */
-+	return mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA1,
-+			(const u8 *)passphrase, os_strlen(passphrase),
-+			ssid, ssid_len, iterations, 32, buf) ? -1 : 0;
-+  #else
-+	const mbedtls_md_info_t *md_info;
-+	md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
-+	if (md_info == NULL)
-+		return -1;
-+	mbedtls_md_context_t ctx;
-+	mbedtls_md_init(&ctx);
-+	int ret = mbedtls_md_setup(&ctx, md_info, 1)
-+	       || mbedtls_pkcs5_pbkdf2_hmac(&ctx,
-+			(const u8 *)passphrase, os_strlen(passphrase),
-+			ssid, ssid_len, iterations, 32, buf) ? -1 : 0;
-+	mbedtls_md_free(&ctx);
-+	return ret;
-+  #endif
-+}
-+#endif
-+
-+
-+/*#include "aes.h"*/ /* prototypes also included in "crypto.h" */
-+
-+static void *aes_crypt_init_mode(const u8 *key, size_t len, int mode)
-+{
-+	if (TEST_FAIL())
-+		return NULL;
-+
-+	mbedtls_aes_context *aes = os_malloc(sizeof(*aes));
-+	if (!aes)
-+		return NULL;
-+
-+	mbedtls_aes_init(aes);
-+	if ((mode == MBEDTLS_AES_ENCRYPT
-+	    ? mbedtls_aes_setkey_enc(aes, key, len * 8)
-+	    : mbedtls_aes_setkey_dec(aes, key, len * 8)) == 0)
-+		return aes;
-+
-+	mbedtls_aes_free(aes);
-+	os_free(aes);
-+	return NULL;
-+}
-+
-+void *aes_encrypt_init(const u8 *key, size_t len)
-+{
-+	return aes_crypt_init_mode(key, len, MBEDTLS_AES_ENCRYPT);
-+}
-+
-+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
-+{
-+	return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, plain, crypt);
-+}
-+
-+void aes_encrypt_deinit(void *ctx)
-+{
-+	mbedtls_aes_free(ctx);
-+	os_free(ctx);
-+}
-+
-+void *aes_decrypt_init(const u8 *key, size_t len)
-+{
-+	return aes_crypt_init_mode(key, len, MBEDTLS_AES_DECRYPT);
-+}
-+
-+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
-+{
-+	return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_DECRYPT, crypt, plain);
-+}
-+
-+void aes_decrypt_deinit(void *ctx)
-+{
-+	mbedtls_aes_free(ctx);
-+	os_free(ctx);
-+}
-+
-+
-+#include "aes_wrap.h"
-+
-+
-+#ifdef MBEDTLS_NIST_KW_C
-+
-+#include <mbedtls/nist_kw.h>
-+
-+/* aes-wrap.c */
-+int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	mbedtls_nist_kw_context ctx;
-+	mbedtls_nist_kw_init(&ctx);
-+	size_t olen;
-+	int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
-+	                                 kek, kek_len*8, 1)
-+	       || mbedtls_nist_kw_wrap(&ctx, MBEDTLS_KW_MODE_KW, plain, n*8,
-+	                               cipher, &olen, (n+1)*8) ? -1 : 0;
-+	mbedtls_nist_kw_free(&ctx);
-+	return ret;
-+}
-+
-+/* aes-unwrap.c */
-+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, u8 *plain)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	mbedtls_nist_kw_context ctx;
-+	mbedtls_nist_kw_init(&ctx);
-+	size_t olen;
-+	int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
-+	                                 kek, kek_len*8, 0)
-+	       || mbedtls_nist_kw_unwrap(&ctx, MBEDTLS_KW_MODE_KW, cipher,
-+	                                 (n+1)*8, plain, &olen, n*8) ? -1 : 0;
-+	mbedtls_nist_kw_free(&ctx);
-+	return ret;
-+}
-+
-+#else
-+
-+#ifndef CRYPTO_MBEDTLS_CONFIG_FIPS
-+#include "aes-wrap.c"    /* pull in hostap local implementation */
-+#include "aes-unwrap.c"  /* pull in hostap local implementation */
-+#endif
-+
-+#endif /* MBEDTLS_NIST_KW_C */
-+
-+
-+#ifdef MBEDTLS_CMAC_C
-+
-+/* aes-omac1.c */
-+
-+#include <mbedtls/cmac.h>
-+
-+int omac1_aes_vector(
-+    const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[],
-+    const size_t *len, u8 *mac)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	mbedtls_cipher_type_t cipher_type;
-+	switch (key_len) {
-+	case 16: cipher_type = MBEDTLS_CIPHER_AES_128_ECB; break;
-+	case 24: cipher_type = MBEDTLS_CIPHER_AES_192_ECB; break;
-+	case 32: cipher_type = MBEDTLS_CIPHER_AES_256_ECB; break;
-+	default: return -1;
-+	}
-+	const mbedtls_cipher_info_t *cipher_info;
-+	cipher_info = mbedtls_cipher_info_from_type(cipher_type);
-+	if (cipher_info == NULL)
-+		return -1;
-+
-+	mbedtls_cipher_context_t ctx;
-+	mbedtls_cipher_init(&ctx);
-+	int ret = -1;
-+	if (mbedtls_cipher_setup(&ctx, cipher_info) == 0
-+	    && mbedtls_cipher_cmac_starts(&ctx, key, key_len*8) == 0) {
-+		ret = 0;
-+		for (size_t i = 0; i < num_elem && ret == 0; ++i)
-+			ret = mbedtls_cipher_cmac_update(&ctx, addr[i], len[i]);
-+	}
-+	if (ret == 0)
-+		ret = mbedtls_cipher_cmac_finish(&ctx, mac);
-+	mbedtls_cipher_free(&ctx);
-+	return ret ? -1 : 0;
-+}
-+
-+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
-+			 const u8 *addr[], const size_t *len,
-+			 u8 *mac)
-+{
-+	return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
-+}
-+
-+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
-+{
-+	return omac1_aes_vector(key, 16, 1, &data, &data_len, mac);
-+}
-+
-+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
-+{
-+	return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
-+}
-+
-+#else
-+
-+#include "aes-omac1.c"  /* pull in hostap local implementation */
-+
-+#ifndef MBEDTLS_AES_BLOCK_SIZE
-+#define MBEDTLS_AES_BLOCK_SIZE 16
-+#endif
-+
-+#endif /* MBEDTLS_CMAC_C */
-+
-+
-+/* These interfaces can be inefficient when used in loops, as the overhead of
-+ * initialization each call is large for each block input (e.g. 16 bytes) */
-+
-+
-+/* aes-encblock.c */
-+int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	mbedtls_aes_context aes;
-+	mbedtls_aes_init(&aes);
-+	int ret = mbedtls_aes_setkey_enc(&aes, key, 128)
-+	       || mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_ENCRYPT, in, out)
-+	  ? -1
-+	  : 0;
-+	mbedtls_aes_free(&aes);
-+	return ret;
-+}
-+
-+
-+/* aes-ctr.c */
-+int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
-+		    u8 *data, size_t data_len)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	unsigned char counter[MBEDTLS_AES_BLOCK_SIZE];
-+	unsigned char stream_block[MBEDTLS_AES_BLOCK_SIZE];
-+	os_memcpy(counter, nonce, MBEDTLS_AES_BLOCK_SIZE);/*(must be writable)*/
-+
-+	mbedtls_aes_context ctx;
-+	mbedtls_aes_init(&ctx);
-+	size_t nc_off = 0;
-+	int ret = mbedtls_aes_setkey_enc(&ctx, key, key_len*8)
-+	       || mbedtls_aes_crypt_ctr(&ctx, data_len, &nc_off,
-+	                                counter, stream_block,
-+	                                data, data) ? -1 : 0;
-+	forced_memzero(stream_block, sizeof(stream_block));
-+	mbedtls_aes_free(&ctx);
-+	return ret;
-+}
-+
-+int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
-+			u8 *data, size_t data_len)
-+{
-+	return aes_ctr_encrypt(key, 16, nonce, data, data_len);
-+}
-+
-+
-+/* aes-cbc.c */
-+static int aes_128_cbc_oper(const u8 *key, const u8 *iv,
-+                            u8 *data, size_t data_len, int mode)
-+{
-+	unsigned char ivec[MBEDTLS_AES_BLOCK_SIZE];
-+	os_memcpy(ivec, iv, MBEDTLS_AES_BLOCK_SIZE); /*(must be writable)*/
-+
-+	mbedtls_aes_context ctx;
-+	mbedtls_aes_init(&ctx);
-+	int ret = (mode == MBEDTLS_AES_ENCRYPT
-+	           ? mbedtls_aes_setkey_enc(&ctx, key, 128)
-+	           : mbedtls_aes_setkey_dec(&ctx, key, 128))
-+	       || mbedtls_aes_crypt_cbc(&ctx, mode, data_len, ivec, data, data);
-+	mbedtls_aes_free(&ctx);
-+	return ret ? -1 : 0;
-+}
-+
-+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_ENCRYPT);
-+}
-+
-+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_DECRYPT);
-+}
-+
-+
-+/*
-+ * Much of the following is documented in crypto.h as for CONFIG_TLS=internal
-+ * but such comments are not accurate:
-+ *
-+ * "This function is only used with internal TLSv1 implementation
-+ *  (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
-+ *  to implement this."
-+ */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_CIPHER
-+
-+#include <mbedtls/cipher.h>
-+
-+struct crypto_cipher
-+{
-+	mbedtls_cipher_context_t ctx_enc;
-+	mbedtls_cipher_context_t ctx_dec;
-+};
-+
-+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
-+					  const u8 *iv, const u8 *key,
-+					  size_t key_len)
-+{
-+	/* IKEv2 src/eap_common/ikev2_common.c:ikev2_{encr,decr}_encrypt()
-+	 * uses one of CRYPTO_CIPHER_ALG_AES or CRYPTO_CIPHER_ALG_3DES */
-+
-+	mbedtls_cipher_type_t cipher_type;
-+	size_t iv_len;
-+	switch (alg) {
-+  #ifdef MBEDTLS_ARC4_C
-+  #if 0
-+	case CRYPTO_CIPHER_ALG_RC4:
-+		cipher_type = MBEDTLS_CIPHER_ARC4_128;
-+		iv_len = 0;
-+		break;
-+  #endif
-+  #endif
-+  #ifdef MBEDTLS_AES_C
-+	case CRYPTO_CIPHER_ALG_AES:
-+		if (key_len == 16) cipher_type = MBEDTLS_CIPHER_AES_128_CTR;
-+		if (key_len == 24) cipher_type = MBEDTLS_CIPHER_AES_192_CTR;
-+		if (key_len == 32) cipher_type = MBEDTLS_CIPHER_AES_256_CTR;
-+		iv_len = 16;
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_DES_C
-+	case CRYPTO_CIPHER_ALG_3DES:
-+		cipher_type = MBEDTLS_CIPHER_DES_EDE3_CBC;
-+		iv_len = 8;
-+		break;
-+  #if 0
-+	case CRYPTO_CIPHER_ALG_DES:
-+		cipher_type = MBEDTLS_CIPHER_DES_CBC;
-+		iv_len = 8;
-+		break;
-+  #endif
-+  #endif
-+	default:
-+		return NULL;
-+	}
-+
-+	const mbedtls_cipher_info_t *cipher_info;
-+	cipher_info = mbedtls_cipher_info_from_type(cipher_type);
-+	if (cipher_info == NULL)
-+		return NULL;
-+
-+	key_len *= 8; /* key_bitlen */
-+  #if 0 /*(were key_bitlen not already available)*/
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */
-+	key_len = mbedtls_cipher_info_get_key_bitlen(cipher_info);
-+  #else
-+	key_len = cipher_info->MBEDTLS_PRIVATE(key_bitlen);
-+  #endif
-+  #endif
-+
-+  #if 0 /*(were iv_len not known above, would need MBEDTLS_PRIVATE(iv_size))*/
-+	iv_len = cipher_info->MBEDTLS_PRIVATE(iv_size);
-+  #endif
-+
-+	struct crypto_cipher *ctx = os_malloc(sizeof(*ctx));
-+	if (!ctx)
-+		return NULL;
-+
-+	mbedtls_cipher_init(&ctx->ctx_enc);
-+	mbedtls_cipher_init(&ctx->ctx_dec);
-+	if (   mbedtls_cipher_setup(&ctx->ctx_enc,cipher_info) == 0
-+	    && mbedtls_cipher_setup(&ctx->ctx_dec,cipher_info) == 0
-+	    && mbedtls_cipher_setkey(&ctx->ctx_enc,key,key_len,MBEDTLS_ENCRYPT) == 0
-+	    && mbedtls_cipher_setkey(&ctx->ctx_dec,key,key_len,MBEDTLS_DECRYPT) == 0
-+	    && mbedtls_cipher_set_iv(&ctx->ctx_enc,iv,iv_len) == 0
-+	    && mbedtls_cipher_set_iv(&ctx->ctx_dec,iv,iv_len) == 0
-+	    && mbedtls_cipher_reset(&ctx->ctx_enc) == 0
-+	    && mbedtls_cipher_reset(&ctx->ctx_dec) == 0) {
-+		return ctx;
-+	}
-+
-+	mbedtls_cipher_free(&ctx->ctx_enc);
-+	mbedtls_cipher_free(&ctx->ctx_dec);
-+	os_free(ctx);
-+	return NULL;
-+}
-+
-+int crypto_cipher_encrypt(struct crypto_cipher *ctx,
-+			  const u8 *plain, u8 *crypt, size_t len)
-+{
-+	size_t olen = 0; /*(poor interface above; unknown size of u8 *crypt)*/
-+	return (mbedtls_cipher_update(&ctx->ctx_enc, plain, len, crypt, &olen)
-+	        || mbedtls_cipher_finish(&ctx->ctx_enc, crypt + olen, &olen)) ? -1 : 0;
-+}
-+
-+int crypto_cipher_decrypt(struct crypto_cipher *ctx,
-+			  const u8 *crypt, u8 *plain, size_t len)
-+{
-+	size_t olen = 0; /*(poor interface above; unknown size of u8 *plain)*/
-+	return (mbedtls_cipher_update(&ctx->ctx_dec, crypt, len, plain, &olen)
-+	        || mbedtls_cipher_finish(&ctx->ctx_dec, plain + olen, &olen)) ? -1 : 0;
-+}
-+
-+void crypto_cipher_deinit(struct crypto_cipher *ctx)
-+{
-+	mbedtls_cipher_free(&ctx->ctx_enc);
-+	mbedtls_cipher_free(&ctx->ctx_dec);
-+	os_free(ctx);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_CIPHER */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_HASH
-+
-+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
-+				      size_t key_len)
-+{
-+	mbedtls_md_type_t md_type;
-+	int is_hmac = 0;
-+
-+	switch (alg) {
-+  #ifdef MBEDTLS_MD5_C
-+	case CRYPTO_HASH_ALG_MD5:
-+		md_type = MBEDTLS_MD_MD5;
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_SHA1_C
-+	case CRYPTO_HASH_ALG_SHA1:
-+		md_type = MBEDTLS_MD_SHA1;
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_MD5_C
-+	case CRYPTO_HASH_ALG_HMAC_MD5:
-+		md_type = MBEDTLS_MD_MD5;
-+		is_hmac = 1;
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_SHA1_C
-+	case CRYPTO_HASH_ALG_HMAC_SHA1:
-+		md_type = MBEDTLS_MD_SHA1;
-+		is_hmac = 1;
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_SHA256_C
-+	case CRYPTO_HASH_ALG_SHA256:
-+		md_type = MBEDTLS_MD_SHA256;
-+		break;
-+	case CRYPTO_HASH_ALG_HMAC_SHA256:
-+		md_type = MBEDTLS_MD_SHA256;
-+		is_hmac = 1;
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_SHA512_C
-+	case CRYPTO_HASH_ALG_SHA384:
-+		md_type = MBEDTLS_MD_SHA384;
-+		break;
-+	case CRYPTO_HASH_ALG_SHA512:
-+		md_type = MBEDTLS_MD_SHA512;
-+		break;
-+  #endif
-+	default:
-+		return NULL;
-+	}
-+
-+	const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
-+	if (!md_info)
-+		return NULL;
-+
-+	mbedtls_md_context_t *mctx = os_malloc(sizeof(*mctx));
-+	if (mctx == NULL)
-+		return NULL;
-+
-+	mbedtls_md_init(mctx);
-+	if (mbedtls_md_setup(mctx, md_info, is_hmac) != 0) {
-+		os_free(mctx);
-+		return NULL;
-+	}
-+
-+	if (is_hmac)
-+		mbedtls_md_hmac_starts(mctx, key, key_len);
-+	else
-+		mbedtls_md_starts(mctx);
-+	return (struct crypto_hash *)((uintptr_t)mctx | is_hmac);
-+}
-+
-+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
-+{
-+	mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL);
-+  #if 0
-+	/*(mbedtls_md_hmac_update() and mbedtls_md_update()
-+	 * make same modifications under the hood in mbedtls)*/
-+	if ((uintptr_t)ctx & 1uL)
-+		mbedtls_md_hmac_update(mctx, data, len);
-+	else
-+  #endif
-+		mbedtls_md_update(mctx, data, len);
-+}
-+
-+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
-+{
-+	mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL);
-+	if (mac != NULL && len != NULL) { /*(NULL if caller just freeing context)*/
-+	  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
-+		const mbedtls_md_info_t *md_info = mbedtls_md_info_from_ctx(mctx);
-+	  #else
-+		const mbedtls_md_info_t *md_info = mctx->MBEDTLS_PRIVATE(md_info);
-+	  #endif
-+		size_t maclen = mbedtls_md_get_size(md_info);
-+		if (*len < maclen) {
-+			*len = maclen;
-+			/*(note: ctx not freed; can call again with larger *len)*/
-+			return -1;
-+		}
-+		*len = maclen;
-+		if ((uintptr_t)ctx & 1uL)
-+			mbedtls_md_hmac_finish(mctx, mac);
-+		else
-+			mbedtls_md_finish(mctx, mac);
-+	}
-+	mbedtls_md_free(mctx);
-+	os_free(mctx);
-+
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	return 0;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_HASH */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
-+
-+#include <mbedtls/bignum.h>
-+
-+/* crypto.h bignum interfaces */
-+
-+struct crypto_bignum *crypto_bignum_init(void)
-+{
-+	if (TEST_FAIL())
-+		return NULL;
-+
-+	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
-+	if (bn)
-+		mbedtls_mpi_init(bn);
-+	return (struct crypto_bignum *)bn;
-+}
-+
-+struct crypto_bignum *crypto_bignum_init_set(const u8 *buf, size_t len)
-+{
-+	if (TEST_FAIL())
-+		return NULL;
-+
-+	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
-+	if (bn) {
-+		mbedtls_mpi_init(bn);
-+		if (mbedtls_mpi_read_binary(bn, buf, len) == 0)
-+			return (struct crypto_bignum *)bn;
-+	}
-+
-+	os_free(bn);
-+	return NULL;
-+}
-+
-+struct crypto_bignum *crypto_bignum_init_uint(unsigned int val)
-+{
-+	if (TEST_FAIL())
-+		return NULL;
-+
-+  #if 0 /*(hostap use of this interface passes int, not uint)*/
-+	val = host_to_be32(val);
-+	return crypto_bignum_init_set((const u8 *)&val, sizeof(val));
-+  #else
-+	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
-+	if (bn) {
-+		mbedtls_mpi_init(bn);
-+		if (mbedtls_mpi_lset(bn, (int)val) == 0)
-+			return (struct crypto_bignum *)bn;
-+	}
-+
-+	os_free(bn);
-+	return NULL;
-+  #endif
-+}
-+
-+void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
-+{
-+	mbedtls_mpi_free((mbedtls_mpi *)n);
-+	os_free(n);
-+}
-+
-+int crypto_bignum_to_bin(const struct crypto_bignum *a,
-+			 u8 *buf, size_t buflen, size_t padlen)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	size_t n = mbedtls_mpi_size((mbedtls_mpi *)a);
-+	if (n < padlen)
-+		n = padlen;
-+	return n > buflen || mbedtls_mpi_write_binary((mbedtls_mpi *)a, buf, n)
-+	  ? -1
-+	  : (int)(n);
-+}
-+
-+int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	/*assert(r != m);*//* r must not be same as m for mbedtls_mpi_random()*/
-+  #if MBEDTLS_VERSION_NUMBER >= 0x021B0000 /* mbedtls 2.27.0 */
-+	return mbedtls_mpi_random((mbedtls_mpi *)r, 0, (mbedtls_mpi *)m,
-+				  mbedtls_ctr_drbg_random,
-+				  crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+  #else
-+	/* (needed by EAP_PWD, SAE, DPP) */
-+	wpa_printf(MSG_ERROR,
-+	           "mbedtls 2.27.0 or later required for mbedtls_mpi_random()");
-+	return -1;
-+  #endif
-+}
-+
-+int crypto_bignum_add(const struct crypto_bignum *a,
-+		      const struct crypto_bignum *b,
-+		      struct crypto_bignum *c)
-+{
-+	return mbedtls_mpi_add_mpi((mbedtls_mpi *)c,
-+				   (const mbedtls_mpi *)a,
-+				   (const mbedtls_mpi *)b) ? -1 : 0;
-+}
-+
-+int crypto_bignum_mod(const struct crypto_bignum *a,
-+		      const struct crypto_bignum *b,
-+		      struct crypto_bignum *c)
-+{
-+	return mbedtls_mpi_mod_mpi((mbedtls_mpi *)c,
-+				   (const mbedtls_mpi *)a,
-+				   (const mbedtls_mpi *)b) ? -1 : 0;
-+}
-+
-+int crypto_bignum_exptmod(const struct crypto_bignum *a,
-+			  const struct crypto_bignum *b,
-+			  const struct crypto_bignum *c,
-+			  struct crypto_bignum *d)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	/* (check if input params match d; d is the result) */
-+	/* (a == d) is ok in current mbedtls implementation */
-+	if (b == d || c == d) { /*(not ok; store result in intermediate)*/
-+		mbedtls_mpi R;
-+		mbedtls_mpi_init(&R);
-+		int rc = mbedtls_mpi_exp_mod(&R,
-+		                             (const mbedtls_mpi *)a,
-+		                             (const mbedtls_mpi *)b,
-+		                             (const mbedtls_mpi *)c,
-+		                             NULL)
-+		      || mbedtls_mpi_copy((mbedtls_mpi *)d, &R) ? -1 : 0;
-+		mbedtls_mpi_free(&R);
-+		return rc;
-+	}
-+	else {
-+		return mbedtls_mpi_exp_mod((mbedtls_mpi *)d,
-+		                           (const mbedtls_mpi *)a,
-+		                           (const mbedtls_mpi *)b,
-+		                           (const mbedtls_mpi *)c,
-+		                           NULL) ? -1 : 0;
-+	}
-+}
-+
-+int crypto_bignum_inverse(const struct crypto_bignum *a,
-+			  const struct crypto_bignum *b,
-+			  struct crypto_bignum *c)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	return mbedtls_mpi_inv_mod((mbedtls_mpi *)c,
-+				   (const mbedtls_mpi *)a,
-+				   (const mbedtls_mpi *)b) ? -1 : 0;
-+}
-+
-+int crypto_bignum_sub(const struct crypto_bignum *a,
-+		      const struct crypto_bignum *b,
-+		      struct crypto_bignum *c)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	return mbedtls_mpi_sub_mpi((mbedtls_mpi *)c,
-+				   (const mbedtls_mpi *)a,
-+				   (const mbedtls_mpi *)b) ? -1 : 0;
-+}
-+
-+int crypto_bignum_div(const struct crypto_bignum *a,
-+		      const struct crypto_bignum *b,
-+		      struct crypto_bignum *c)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	/*(most current use of this crypto.h interface has a == c (result),
-+	 * so store result in an intermediate to avoid overwritten input)*/
-+	mbedtls_mpi R;
-+	mbedtls_mpi_init(&R);
-+	int rc = mbedtls_mpi_div_mpi(&R, NULL,
-+				     (const mbedtls_mpi *)a,
-+				     (const mbedtls_mpi *)b)
-+	      || mbedtls_mpi_copy((mbedtls_mpi *)c, &R) ? -1 : 0;
-+	mbedtls_mpi_free(&R);
-+	return rc;
-+}
-+
-+int crypto_bignum_addmod(const struct crypto_bignum *a,
-+			 const struct crypto_bignum *b,
-+			 const struct crypto_bignum *c,
-+			 struct crypto_bignum *d)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	return mbedtls_mpi_add_mpi((mbedtls_mpi *)d,
-+				   (const mbedtls_mpi *)a,
-+				   (const mbedtls_mpi *)b)
-+	    || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d,
-+				   (mbedtls_mpi *)d,
-+				   (const mbedtls_mpi *)c) ? -1 : 0;
-+}
-+
-+int crypto_bignum_mulmod(const struct crypto_bignum *a,
-+			 const struct crypto_bignum *b,
-+			 const struct crypto_bignum *c,
-+			 struct crypto_bignum *d)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	return mbedtls_mpi_mul_mpi((mbedtls_mpi *)d,
-+				   (const mbedtls_mpi *)a,
-+				   (const mbedtls_mpi *)b)
-+	    || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d,
-+				   (mbedtls_mpi *)d,
-+				   (const mbedtls_mpi *)c) ? -1 : 0;
-+}
-+
-+int crypto_bignum_sqrmod(const struct crypto_bignum *a,
-+			 const struct crypto_bignum *b,
-+			 struct crypto_bignum *c)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+  #if 1
-+	return crypto_bignum_mulmod(a, a, b, c);
-+  #else
-+	mbedtls_mpi bn;
-+	mbedtls_mpi_init(&bn);
-+	if (mbedtls_mpi_lset(&bn, 2)) /* alt?: mbedtls_mpi_set_bit(&bn, 1) */
-+		return -1;
-+	int ret = mbedtls_mpi_exp_mod((mbedtls_mpi *)c,
-+				      (const mbedtls_mpi *)a, &bn,
-+				      (const mbedtls_mpi *)b, NULL) ? -1 : 0;
-+	mbedtls_mpi_free(&bn);
-+	return ret;
-+  #endif
-+}
-+
-+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
-+			 struct crypto_bignum *r)
-+{
-+	return mbedtls_mpi_copy((mbedtls_mpi *)r, (const mbedtls_mpi *)a)
-+	    || mbedtls_mpi_shift_r((mbedtls_mpi *)r, n) ? -1 : 0;
-+}
-+
-+int crypto_bignum_cmp(const struct crypto_bignum *a,
-+		      const struct crypto_bignum *b)
-+{
-+	return mbedtls_mpi_cmp_mpi((const mbedtls_mpi *)a, (const mbedtls_mpi *)b);
-+}
-+
-+int crypto_bignum_is_zero(const struct crypto_bignum *a)
-+{
-+	/* XXX: src/common/sae.c:sswu() contains comment:
-+	 * "TODO: Make sure crypto_bignum_is_zero() is constant time"
-+	 * Note: mbedtls_mpi_cmp_int() *is not* constant time */
-+	return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 0) == 0);
-+}
-+
-+int crypto_bignum_is_one(const struct crypto_bignum *a)
-+{
-+	return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 1) == 0);
-+}
-+
-+int crypto_bignum_is_odd(const struct crypto_bignum *a)
-+{
-+	return mbedtls_mpi_get_bit((const mbedtls_mpi *)a, 0);
-+}
-+
-+#include "utils/const_time.h"
-+int crypto_bignum_legendre(const struct crypto_bignum *a,
-+			   const struct crypto_bignum *p)
-+{
-+	if (TEST_FAIL())
-+		return -2;
-+
-+	/* Security Note:
-+	 * mbedtls_mpi_exp_mod() is not documented to run in constant time,
-+	 * though mbedtls/library/bignum.c uses constant_time_internal.h funcs.
-+	 * Compare to crypto_openssl.c:crypto_bignum_legendre()
-+	 * which uses openssl BN_mod_exp_mont_consttime()
-+	 * mbedtls/library/ecp.c has further countermeasures to timing attacks,
-+	 * (but ecp.c funcs are not used here) */
-+
-+	mbedtls_mpi exp, tmp;
-+	mbedtls_mpi_init(&exp);
-+	mbedtls_mpi_init(&tmp);
-+
-+	/* exp = (p-1) / 2 */
-+	int res;
-+	if (mbedtls_mpi_sub_int(&exp, (const mbedtls_mpi *)p, 1) == 0
-+	    && mbedtls_mpi_shift_r(&exp, 1) == 0
-+	    && mbedtls_mpi_exp_mod(&tmp, (const mbedtls_mpi *)a, &exp,
-+	                           (const mbedtls_mpi *)p, NULL) == 0) {
-+		/*(modified from crypto_openssl.c:crypto_bignum_legendre())*/
-+		/* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need
-+		 * to use constant time selection to avoid branches here. */
-+		unsigned int mask;
-+		res = -1;
-+		mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 1) == 0), 1);
-+		res = const_time_select_int(mask, 1, res);
-+		mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 0) == 0), 1);
-+		res = const_time_select_int(mask, 0, res);
-+	} else {
-+		res = -2;
-+	}
-+
-+	mbedtls_mpi_free(&tmp);
-+	mbedtls_mpi_free(&exp);
-+	return res;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_BIGNUM */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_DH
-+
-+/* crypto_internal-modexp.c */
-+
-+#include <mbedtls/bignum.h>
-+#include <mbedtls/dhm.h>
-+
-+#if 0 /* crypto_dh_init() and crypto_dh_derive_secret() prefer to use mbedtls */
-+int crypto_mod_exp(const u8 *base, size_t base_len,
-+		   const u8 *power, size_t power_len,
-+		   const u8 *modulus, size_t modulus_len,
-+		   u8 *result, size_t *result_len)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	mbedtls_mpi bn_base, bn_exp, bn_modulus, bn_result;
-+	mbedtls_mpi_init(&bn_base);
-+	mbedtls_mpi_init(&bn_exp);
-+	mbedtls_mpi_init(&bn_modulus);
-+	mbedtls_mpi_init(&bn_result);
-+
-+	size_t len;
-+	int ret =  mbedtls_mpi_read_binary(&bn_base, base, base_len)
-+	        || mbedtls_mpi_read_binary(&bn_exp, power, power_len)
-+	        || mbedtls_mpi_read_binary(&bn_modulus, modulus, modulus_len)
-+	        || mbedtls_mpi_exp_mod(&bn_result,&bn_base,&bn_exp,&bn_modulus,NULL)
-+	        || (len = mbedtls_mpi_size(&bn_result)) > *result_len
-+	        || mbedtls_mpi_write_binary(&bn_result, result, (*result_len = len))
-+	  ? -1
-+	  : 0;
-+
-+	mbedtls_mpi_free(&bn_base);
-+	mbedtls_mpi_free(&bn_exp);
-+	mbedtls_mpi_free(&bn_modulus);
-+	mbedtls_mpi_free(&bn_result);
-+	return ret;
-+}
-+#endif
-+
-+static int crypto_mbedtls_dh_set_bin_pg(mbedtls_dhm_context *ctx, u8 generator,
-+                                        const u8 *prime, size_t prime_len)
-+{
-+	/*(could set these directly in MBEDTLS_PRIVATE members)*/
-+	mbedtls_mpi P, G;
-+	mbedtls_mpi_init(&P);
-+	mbedtls_mpi_init(&G);
-+	int ret = mbedtls_mpi_lset(&G, generator)
-+	       || mbedtls_mpi_read_binary(&P, prime, prime_len)
-+	       || mbedtls_dhm_set_group(ctx, &P, &G);
-+	mbedtls_mpi_free(&P);
-+	mbedtls_mpi_free(&G);
-+	return ret;
-+}
-+
-+__attribute_noinline__
-+static int crypto_mbedtls_dh_init_public(mbedtls_dhm_context *ctx, u8 generator,
-+                                         const u8 *prime, size_t prime_len,
-+                                         u8 *privkey, u8 *pubkey)
-+{
-+	if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len)
-+	    || mbedtls_dhm_make_public(ctx, (int)prime_len, pubkey, prime_len,
-+	                               mbedtls_ctr_drbg_random,
-+	                               crypto_mbedtls_ctr_drbg()))
-+		return -1;
-+
-+  /*(enable later when upstream mbedtls interface changes require)*/
-+  #if 0 && MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+	mbedtls_mpi X;
-+	mbedtls_mpi_init(&X);
-+	int ret = mbedtls_dhm_get_value(ctx, MBEDTLS_DHM_PARAM_X, &X)
-+	       || mbedtls_mpi_write_binary(&X, privkey, prime_len) ? -1 : 0;
-+	mbedtls_mpi_free(&X);
-+	return ret;
-+  #else
-+	return mbedtls_mpi_write_binary(&ctx->MBEDTLS_PRIVATE(X),
-+	                                privkey, prime_len) ? -1 : 0;
-+  #endif
-+}
-+
-+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
-+		   u8 *pubkey)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+  #if 0 /*(crypto_dh_init() duplicated (and identical) in crypto_*.c modules)*/
-+	size_t pubkey_len, pad;
-+
-+	if (os_get_random(privkey, prime_len) < 0)
-+		return -1;
-+	if (os_memcmp(privkey, prime, prime_len) > 0) {
-+		/* Make sure private value is smaller than prime */
-+		privkey[0] = 0;
-+	}
-+
-+	pubkey_len = prime_len;
-+	if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
-+			   pubkey, &pubkey_len) < 0)
-+		return -1;
-+	if (pubkey_len < prime_len) {
-+		pad = prime_len - pubkey_len;
-+		os_memmove(pubkey + pad, pubkey, pubkey_len);
-+		os_memset(pubkey, 0, pad);
-+	}
-+
-+	return 0;
-+  #else
-+	/* Prefer to use mbedtls to derive our public/private key, as doing so
-+	 * leverages mbedtls to properly format output and to perform blinding*/
-+	mbedtls_dhm_context ctx;
-+	mbedtls_dhm_init(&ctx);
-+	int ret = crypto_mbedtls_dh_init_public(&ctx, generator, prime,
-+	                                        prime_len, privkey, pubkey);
-+	mbedtls_dhm_free(&ctx);
-+	return ret;
-+  #endif
-+}
-+
-+/*(crypto_dh_derive_secret() could be implemented using crypto.h APIs
-+ * instead of being reimplemented in each crypto_*.c)*/
-+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
-+			    const u8 *order, size_t order_len,
-+			    const u8 *privkey, size_t privkey_len,
-+			    const u8 *pubkey, size_t pubkey_len,
-+			    u8 *secret, size_t *len)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+  #if 0
-+	if (pubkey_len > prime_len ||
-+	    (pubkey_len == prime_len &&
-+	     os_memcmp(pubkey, prime, prime_len) >= 0))
-+		return -1;
-+
-+	int res = 0;
-+	mbedtls_mpi pub;
-+	mbedtls_mpi_init(&pub);
-+	if (mbedtls_mpi_read_binary(&pub, pubkey, pubkey_len)
-+	    || mbedtls_mpi_cmp_int(&pub, 1) <= 0) {
-+		res = -1;
-+	} else if (order) {
-+		mbedtls_mpi p, q, tmp;
-+		mbedtls_mpi_init(&p);
-+		mbedtls_mpi_init(&q);
-+		mbedtls_mpi_init(&tmp);
-+
-+		/* verify: pubkey^q == 1 mod p */
-+		res = (mbedtls_mpi_read_binary(&p, prime, prime_len)
-+		    || mbedtls_mpi_read_binary(&q, order, order_len)
-+		    || mbedtls_mpi_exp_mod(&tmp, &pub, &q, &p, NULL)
-+		    || mbedtls_mpi_cmp_int(&tmp, 1) != 0);
-+
-+		mbedtls_mpi_free(&p);
-+		mbedtls_mpi_free(&q);
-+		mbedtls_mpi_free(&tmp);
-+	}
-+	mbedtls_mpi_free(&pub);
-+
-+	return (res == 0)
-+	  ? crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
-+			   prime, prime_len, secret, len)
-+	  : -1;
-+  #else
-+	/* Prefer to use mbedtls to derive DH shared secret, as doing so
-+	 * leverages mbedtls to validate params and to perform blinding.
-+	 *
-+	 * Attempt to reconstitute DH context to derive shared secret
-+	 * (due to limitations of the interface, which ought to pass context).
-+	 * Force provided G (our private key) into context without validation.
-+	 * Regenerating GX (our public key) not needed to derive shared secret.
-+	 */
-+	/*(older compilers might not support VLAs)*/
-+	/*unsigned char buf[2+prime_len+2+1+2+pubkey_len];*/
-+	unsigned char buf[2+MBEDTLS_MPI_MAX_SIZE+2+1+2+MBEDTLS_MPI_MAX_SIZE];
-+	unsigned char *p = buf + 2 + prime_len;
-+	if (2+prime_len+2+1+2+pubkey_len > sizeof(buf))
-+		return -1;
-+	WPA_PUT_BE16(buf, prime_len);  /*(2-byte big-endian size of prime)*/
-+	p[0] = 0;                      /*(2-byte big-endian size of generator)*/
-+	p[1] = 1;
-+	p[2] = generator;
-+	WPA_PUT_BE16(p+3, pubkey_len); /*(2-byte big-endian size of pubkey)*/
-+	os_memcpy(p+5, pubkey, pubkey_len);
-+	os_memcpy(buf+2, prime, prime_len);
-+
-+	mbedtls_dhm_context ctx;
-+	mbedtls_dhm_init(&ctx);
-+	p = buf;
-+	int ret = mbedtls_dhm_read_params(&ctx, &p, p+2+prime_len+5+pubkey_len)
-+	       || mbedtls_mpi_read_binary(&ctx.MBEDTLS_PRIVATE(X),
-+	                                  privkey, privkey_len)
-+	       || mbedtls_dhm_calc_secret(&ctx, secret, *len, len,
-+	                                  mbedtls_ctr_drbg_random,
-+	                                  crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+	mbedtls_dhm_free(&ctx);
-+	return ret;
-+  #endif
-+}
-+
-+/* dh_group5.c */
-+
-+#include "dh_group5.h"
-+
-+/* RFC3526_PRIME_1536[] and RFC3526_GENERATOR_1536[] from crypto_wolfssl.c */
-+
-+static const unsigned char RFC3526_PRIME_1536[] = {
-+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
-+	0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
-+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
-+	0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
-+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
-+	0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
-+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
-+	0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
-+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
-+	0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
-+	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
-+	0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
-+	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
-+	0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
-+	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
-+	0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
-+};
-+
-+static const unsigned char RFC3526_GENERATOR_1536[] = {
-+	0x02
-+};
-+
-+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
-+{
-+	const unsigned char * const prime = RFC3526_PRIME_1536;
-+	const size_t prime_len = sizeof(RFC3526_PRIME_1536);
-+	const u8 generator = *RFC3526_GENERATOR_1536;
-+	struct wpabuf *wpubl = NULL, *wpriv = NULL;
-+
-+	mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx));
-+	if (ctx == NULL)
-+		return NULL;
-+	mbedtls_dhm_init(ctx);
-+
-+	if (   (wpubl = wpabuf_alloc(prime_len))
-+	    && (wpriv = wpabuf_alloc(prime_len))
-+	    && crypto_mbedtls_dh_init_public(ctx, generator, prime, prime_len,
-+	                                     wpabuf_put(wpriv, prime_len),
-+	                                     wpabuf_put(wpubl, prime_len))==0) {
-+		wpabuf_free(*publ);
-+		wpabuf_clear_free(*priv);
-+		*publ = wpubl;
-+		*priv = wpriv;
-+		return ctx;
-+	}
-+
-+	wpabuf_clear_free(wpriv);
-+	wpabuf_free(wpubl);
-+	mbedtls_dhm_free(ctx);
-+	os_free(ctx);
-+	return NULL;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_DH5_INIT_FIXED
-+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
-+{
-+	const unsigned char * const prime = RFC3526_PRIME_1536;
-+	const size_t prime_len = sizeof(RFC3526_PRIME_1536);
-+	const u8 generator = *RFC3526_GENERATOR_1536;
-+
-+	mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx));
-+	if (ctx == NULL)
-+		return NULL;
-+	mbedtls_dhm_init(ctx);
-+
-+	if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len)==0
-+	   #if 0 /*(ignore; not required to derive shared secret)*/
-+	    && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(GX),
-+				       wpabuf_head(publ),wpabuf_len(publ))==0
-+	   #endif
-+	    && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(X),
-+				       wpabuf_head(priv),wpabuf_len(priv))==0) {
-+		return ctx;
-+	}
-+
-+	mbedtls_dhm_free(ctx);
-+	os_free(ctx);
-+	return NULL;
-+}
-+#endif
-+
-+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
-+				  const struct wpabuf *own_private)
-+{
-+	/*((mbedtls_dhm_context *)ctx must already contain own_private)*/
-+	/* mbedtls 2.x: prime_len = ctx->len; */
-+	/* mbedtls 3.x: prime_len = mbedtls_dhm_get_len(ctx); */
-+	size_t olen = sizeof(RFC3526_PRIME_1536); /*(sizeof(); prime known)*/
-+	struct wpabuf *buf = wpabuf_alloc(olen);
-+	if (buf == NULL)
-+		return NULL;
-+	if (mbedtls_dhm_read_public((mbedtls_dhm_context *)ctx,
-+	                            wpabuf_head(peer_public),
-+	                            wpabuf_len(peer_public)) == 0
-+	    && mbedtls_dhm_calc_secret(ctx, wpabuf_mhead(buf), olen, &olen,
-+	                               mbedtls_ctr_drbg_random,
-+	                               crypto_mbedtls_ctr_drbg()) == 0) {
-+		wpabuf_put(buf, olen);
-+		return buf;
-+	}
-+
-+	wpabuf_free(buf);
-+	return NULL;
-+}
-+
-+void dh5_free(void *ctx)
-+{
-+	mbedtls_dhm_free(ctx);
-+	os_free(ctx);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_DH */
-+
-+
-+#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC)
-+
-+#include <mbedtls/ecp.h>
-+
-+#define CRYPTO_EC_pbits(e) (((mbedtls_ecp_group *)(e))->pbits)
-+#define CRYPTO_EC_plen(e) ((((mbedtls_ecp_group *)(e))->pbits+7)>>3)
-+#define CRYPTO_EC_P(e)    (&((mbedtls_ecp_group *)(e))->P)
-+#define CRYPTO_EC_N(e)    (&((mbedtls_ecp_group *)(e))->N)
-+#define CRYPTO_EC_A(e)    (&((mbedtls_ecp_group *)(e))->A)
-+#define CRYPTO_EC_B(e)    (&((mbedtls_ecp_group *)(e))->B)
-+#define CRYPTO_EC_G(e)    (&((mbedtls_ecp_group *)(e))->G)
-+
-+static mbedtls_ecp_group_id crypto_mbedtls_ecp_group_id_from_ike_id(int group)
-+{
-+	/* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */
-+	switch (group) {
-+  #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
-+	case 19: return MBEDTLS_ECP_DP_SECP256R1;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
-+	case 20: return MBEDTLS_ECP_DP_SECP384R1;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
-+	case 21: return MBEDTLS_ECP_DP_SECP521R1;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
-+	case 25: return MBEDTLS_ECP_DP_SECP192R1;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
-+	case 26: return MBEDTLS_ECP_DP_SECP224R1;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
-+	case 28: return MBEDTLS_ECP_DP_BP256R1;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
-+	case 29: return MBEDTLS_ECP_DP_BP384R1;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
-+	case 30: return MBEDTLS_ECP_DP_BP512R1;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
-+	case 31: return MBEDTLS_ECP_DP_CURVE25519;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
-+	case 32: return MBEDTLS_ECP_DP_CURVE448;
-+  #endif
-+	default: return MBEDTLS_ECP_DP_NONE;
-+	}
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC
-+static int crypto_mbedtls_ike_id_from_ecp_group_id(mbedtls_ecp_group_id grp_id)
-+{
-+	/* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */
-+	/*(for crypto_ec_key_group())*/
-+	switch (grp_id) {
-+  #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP256R1:  return 19;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP384R1:  return 20;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP521R1:  return 21;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP192R1:  return 25;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP224R1:  return 26;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
-+	case MBEDTLS_ECP_DP_BP256R1:    return 28;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
-+	case MBEDTLS_ECP_DP_BP384R1:    return 29;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
-+	case MBEDTLS_ECP_DP_BP512R1:    return 30;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
-+	case MBEDTLS_ECP_DP_CURVE25519: return 31;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
-+	case MBEDTLS_ECP_DP_CURVE448:   return 32;
-+  #endif
-+	default: return -1;
-+	}
-+}
-+#endif
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH || CRYPTO_MBEDTLS_CRYPTO_EC */
-+
-+
-+#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC_DPP)
-+
-+#include <mbedtls/ecp.h>
-+#include <mbedtls/pk.h>
-+
-+static int crypto_mbedtls_keypair_gen(int group, mbedtls_pk_context *pk)
-+{
-+	mbedtls_ecp_group_id grp_id =
-+	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+	if (grp_id == MBEDTLS_ECP_DP_NONE)
-+		return -1;
-+	const mbedtls_pk_info_t *pk_info =
-+	  mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
-+	if (pk_info == NULL)
-+		return -1;
-+	return mbedtls_pk_setup(pk, pk_info)
-+	    || mbedtls_ecp_gen_key(grp_id, mbedtls_pk_ec(*pk),
-+	                           mbedtls_ctr_drbg_random,
-+	                           crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+}
-+
-+#endif
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_ECDH
-+
-+#include <mbedtls/ecdh.h>
-+#include <mbedtls/ecdsa.h>
-+#include <mbedtls/ecp.h>
-+#include <mbedtls/pk.h>
-+
-+/* wrap mbedtls_ecdh_context for more future-proof direct access to components
-+ * (mbedtls_ecdh_context internal implementation may change between releases)
-+ *
-+ * If mbedtls_pk_context -- specifically underlying mbedtls_ecp_keypair --
-+ * lifetime were guaranteed to be longer than that of mbedtls_ecdh_context,
-+ * then mbedtls_pk_context or mbedtls_ecp_keypair could be stored in crypto_ecdh
-+ * (or crypto_ec_key could be stored in crypto_ecdh, and crypto_ec_key could
-+ *  wrap mbedtls_ecp_keypair and components, to avoid MBEDTLS_PRIVATE access) */
-+struct crypto_ecdh {
-+	mbedtls_ecdh_context ctx;
-+	mbedtls_ecp_group grp;
-+	mbedtls_ecp_point Q;
-+};
-+
-+struct crypto_ecdh * crypto_ecdh_init(int group)
-+{
-+	mbedtls_pk_context pk;
-+	mbedtls_pk_init(&pk);
-+	struct crypto_ecdh *ecdh = crypto_mbedtls_keypair_gen(group, &pk) == 0
-+	  ? crypto_ecdh_init2(group, (struct crypto_ec_key *)&pk)
-+	  : NULL;
-+	mbedtls_pk_free(&pk);
-+	return ecdh;
-+}
-+
-+struct crypto_ecdh * crypto_ecdh_init2(int group,
-+				       struct crypto_ec_key *own_key)
-+{
-+	mbedtls_ecp_group_id grp_id =
-+	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+	if (grp_id == MBEDTLS_ECP_DP_NONE)
-+		return NULL;
-+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)own_key);
-+	struct crypto_ecdh *ecdh = os_malloc(sizeof(*ecdh));
-+	if (ecdh == NULL)
-+		return NULL;
-+	mbedtls_ecdh_init(&ecdh->ctx);
-+	mbedtls_ecp_group_init(&ecdh->grp);
-+	mbedtls_ecp_point_init(&ecdh->Q);
-+	if (mbedtls_ecdh_setup(&ecdh->ctx, grp_id) == 0
-+	    && mbedtls_ecdh_get_params(&ecdh->ctx,ecp_kp,MBEDTLS_ECDH_OURS) == 0) {
-+		/* copy grp and Q for later use
-+		 * (retrieving this info later is more convoluted
-+		 *  even if mbedtls_ecdh_make_public() is considered)*/
-+	  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
-+		mbedtls_mpi d;
-+		mbedtls_mpi_init(&d);
-+		if (mbedtls_ecp_export(ecp_kp, &ecdh->grp, &d, &ecdh->Q) == 0) {
-+			mbedtls_mpi_free(&d);
-+			return ecdh;
-+		}
-+		mbedtls_mpi_free(&d);
-+	  #else
-+		if (mbedtls_ecp_group_load(&ecdh->grp, grp_id) == 0
-+		    && mbedtls_ecp_copy(&ecdh->Q, &ecp_kp->MBEDTLS_PRIVATE(Q)) == 0)
-+			return ecdh;
-+	  #endif
-+	}
-+
-+	mbedtls_ecp_point_free(&ecdh->Q);
-+	mbedtls_ecp_group_free(&ecdh->grp);
-+	mbedtls_ecdh_free(&ecdh->ctx);
-+	os_free(ecdh);
-+	return NULL;
-+}
-+
-+struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
-+{
-+	mbedtls_ecp_group *grp = &ecdh->grp;
-+	size_t prime_len = CRYPTO_EC_plen(grp);
-+	size_t output_len = prime_len;
-+	u8 output_offset = 0;
-+	u8 buf[256];
-+
-+  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+	/* len */
-+  #endif
-+  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
-+		output_len = inc_y ? prime_len * 2 + 1 : prime_len + 1;
-+		output_offset = 1;
-+	}
-+  #endif
-+
-+	if (output_len > sizeof(buf))
-+		return NULL;
-+
-+	inc_y = inc_y ? MBEDTLS_ECP_PF_UNCOMPRESSED : MBEDTLS_ECP_PF_COMPRESSED;
-+	if (mbedtls_ecp_point_write_binary(grp, &ecdh->Q, inc_y, &output_len,
-+	                                   buf, output_len) == 0) {
-+		return wpabuf_alloc_copy(buf + output_offset, output_len - output_offset);
-+	}
-+
-+	return NULL;
-+}
-+
-+#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
-+static int crypto_mbedtls_short_weierstrass_derive_y(mbedtls_ecp_group *grp,
-+                                                     mbedtls_mpi *bn,
-+                                                     int parity_bit)
-+{
-+	/* y^2 = x^3 + ax + b
-+	 * sqrt(w) = w^((p+1)/4) mod p   (for prime p where p = 3 mod 4) */
-+	mbedtls_mpi *cy2 = (mbedtls_mpi *)
-+	  crypto_ec_point_compute_y_sqr((struct crypto_ec *)grp,
-+	                                (const struct crypto_bignum *)bn); /*x*/
-+	if (cy2 == NULL)
-+		return -1;
-+
-+	/*mbedtls_mpi_free(bn);*/
-+	/*(reuse bn to store result (y))*/
-+
-+	mbedtls_mpi exp;
-+	mbedtls_mpi_init(&exp);
-+	int ret = mbedtls_mpi_get_bit(&grp->P, 0) != 1 /*(p = 3 mod 4)*/
-+	       || mbedtls_mpi_get_bit(&grp->P, 1) != 1 /*(p = 3 mod 4)*/
-+	       || mbedtls_mpi_add_int(&exp, &grp->P, 1)
-+	       || mbedtls_mpi_shift_r(&exp, 2)
-+	       || mbedtls_mpi_exp_mod(bn, cy2, &exp, &grp->P, NULL)
-+	       || (mbedtls_mpi_get_bit(bn, 0) != parity_bit
-+	           && mbedtls_mpi_sub_mpi(bn, &grp->P, bn));
-+	mbedtls_mpi_free(&exp);
-+	mbedtls_mpi_free(cy2);
-+	os_free(cy2);
-+	return ret;
-+}
-+#endif
-+
-+struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
-+					const u8 *key, size_t len)
-+{
-+	if (len == 0) /*(invalid peer key)*/
-+		return NULL;
-+
-+	mbedtls_ecp_group *grp = &ecdh->grp;
-+
-+  #if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
-+	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
-+		/* add header for mbedtls_ecdh_read_public() */
-+		u8 buf[256];
-+		if (sizeof(buf)-1 < len)
-+			return NULL;
-+		buf[0] = (u8)(len);
-+		os_memcpy(buf+1, key, len);
-+
-+		if (inc_y) {
-+			if (!(len & 1)) { /*(dpp code/tests does not include tag?!?)*/
-+				if (sizeof(buf)-2 < len)
-+					return NULL;
-+				buf[0] = (u8)(1+len);
-+				buf[1] = 0x04;
-+				os_memcpy(buf+2, key, len);
-+			}
-+			len >>= 1; /*(repurpose len to prime_len)*/
-+		} else { /* (inc_y == 0) */
-+			/* mbedtls_ecp_point_read_binary() does not currently support
-+			 * MBEDTLS_ECP_PF_COMPRESSED format (buf[1] = 0x02 or 0x03)
-+			 * (returns MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) */
-+
-+			/* derive y, amend buf[] with y for UNCOMPRESSED format */
-+			if (sizeof(buf)-2 < len*2 || len == 0)
-+				return NULL;
-+
-+			buf[0] = (u8)(1+len*2);
-+			buf[1] = 0x04;
-+			os_memcpy(buf+2, key, len);
-+
-+			mbedtls_mpi bn;
-+			mbedtls_mpi_init(&bn);
-+			int ret = mbedtls_mpi_read_binary(&bn, key, len)
-+			       || crypto_mbedtls_short_weierstrass_derive_y(grp, &bn, 0)
-+			       || mbedtls_mpi_write_binary(&bn, buf+2+len, len);
-+			mbedtls_mpi_free(&bn);
-+			if (ret != 0)
-+				return NULL;
-+		}
-+
-+		if (mbedtls_ecdh_read_public(&ecdh->ctx, buf, buf[0]+1))
-+			return NULL;
-+	}
-+  #endif
-+  #if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED)
-+	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) {
-+		if (mbedtls_ecdh_read_public(&ecdh->ctx, key, len))
-+			return NULL;
-+	}
-+  #endif
-+
-+	struct wpabuf *buf = wpabuf_alloc(len);
-+	if (buf == NULL)
-+		return NULL;
-+
-+	if (mbedtls_ecdh_calc_secret(&ecdh->ctx, &len,
-+	                             wpabuf_mhead(buf), len,
-+	                             mbedtls_ctr_drbg_random,
-+	                             crypto_mbedtls_ctr_drbg()) == 0) {
-+		wpabuf_put(buf, len);
-+		return buf;
-+	}
-+
-+	wpabuf_clear_free(buf);
-+	return NULL;
-+}
-+
-+void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
-+{
-+	if (ecdh == NULL)
-+		return;
-+	mbedtls_ecp_point_free(&ecdh->Q);
-+	mbedtls_ecp_group_free(&ecdh->grp);
-+	mbedtls_ecdh_free(&ecdh->ctx);
-+	os_free(ecdh);
-+}
-+
-+size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh)
-+{
-+	return CRYPTO_EC_plen(&ecdh->grp);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC
-+
-+#include <mbedtls/ecp.h>
-+
-+struct crypto_ec *crypto_ec_init(int group)
-+{
-+	mbedtls_ecp_group_id grp_id =
-+	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+	if (grp_id == MBEDTLS_ECP_DP_NONE)
-+		return NULL;
-+	mbedtls_ecp_group *e = os_malloc(sizeof(*e));
-+	if (e == NULL)
-+		return NULL;
-+	mbedtls_ecp_group_init(e);
-+	if (mbedtls_ecp_group_load(e, grp_id) == 0)
-+		return (struct crypto_ec *)e;
-+
-+	mbedtls_ecp_group_free(e);
-+	os_free(e);
-+	return NULL;
-+}
-+
-+void crypto_ec_deinit(struct crypto_ec *e)
-+{
-+	mbedtls_ecp_group_free((mbedtls_ecp_group *)e);
-+	os_free(e);
-+}
-+
-+size_t crypto_ec_prime_len(struct crypto_ec *e)
-+{
-+	return CRYPTO_EC_plen(e);
-+}
-+
-+size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
-+{
-+	return CRYPTO_EC_pbits(e);
-+}
-+
-+size_t crypto_ec_order_len(struct crypto_ec *e)
-+{
-+	return (mbedtls_mpi_bitlen(CRYPTO_EC_N(e)) + 7) / 8;
-+}
-+
-+const struct crypto_bignum *crypto_ec_get_prime(struct crypto_ec *e)
-+{
-+	return (const struct crypto_bignum *)CRYPTO_EC_P(e);
-+}
-+
-+const struct crypto_bignum *crypto_ec_get_order(struct crypto_ec *e)
-+{
-+	return (const struct crypto_bignum *)CRYPTO_EC_N(e);
-+}
-+
-+const struct crypto_bignum *crypto_ec_get_a(struct crypto_ec *e)
-+{
-+	static const uint8_t secp256r1_a[] =
-+	  {0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x01,
-+	   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+	   0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc};
-+	static const uint8_t secp384r1_a[] =
-+	  {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
-+	   0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
-+	   0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xfc};
-+	static const uint8_t secp521r1_a[] =
-+	  {0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xfc};
-+	static const uint8_t secp192r1_a[] =
-+	  {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc};
-+	static const uint8_t secp224r1_a[] =
-+	  {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
-+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-+	   0xff,0xff,0xff,0xfe};
-+
-+	const uint8_t *bin = NULL;
-+	size_t len = 0;
-+
-+	/* (mbedtls groups matching supported sswu_curve_param() IKE groups) */
-+	switch (((mbedtls_ecp_group *)e)->id) {
-+  #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP256R1:
-+		bin = secp256r1_a;
-+		len = sizeof(secp256r1_a);
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP384R1:
-+		bin = secp384r1_a;
-+		len = sizeof(secp384r1_a);
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP521R1:
-+		bin = secp521r1_a;
-+		len = sizeof(secp521r1_a);
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP192R1:
-+		bin = secp192r1_a;
-+		len = sizeof(secp192r1_a);
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
-+	case MBEDTLS_ECP_DP_SECP224R1:
-+		bin = secp224r1_a;
-+		len = sizeof(secp224r1_a);
-+		break;
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
-+	case MBEDTLS_ECP_DP_BP256R1:
-+		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
-+	case MBEDTLS_ECP_DP_BP384R1:
-+		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
-+	case MBEDTLS_ECP_DP_BP512R1:
-+		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
-+	case MBEDTLS_ECP_DP_CURVE25519:
-+		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+  #endif
-+  #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
-+	case MBEDTLS_ECP_DP_CURVE448:
-+		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
-+  #endif
-+	default:
-+		return NULL;
-+	}
-+
-+	/*(note: not thread-safe; returns file-scoped static storage)*/
-+	if (mbedtls_mpi_read_binary(&mpi_sw_A, bin, len) == 0)
-+		return (const struct crypto_bignum *)&mpi_sw_A;
-+	return NULL;
-+}
-+
-+const struct crypto_bignum *crypto_ec_get_b(struct crypto_ec *e)
-+{
-+	return (const struct crypto_bignum *)CRYPTO_EC_B(e);
-+}
-+
-+const struct crypto_ec_point * crypto_ec_get_generator(struct crypto_ec *e)
-+{
-+	return (const struct crypto_ec_point *)CRYPTO_EC_G(e);
-+}
-+
-+struct crypto_ec_point *crypto_ec_point_init(struct crypto_ec *e)
-+{
-+	if (TEST_FAIL())
-+		return NULL;
-+
-+	mbedtls_ecp_point *p = os_malloc(sizeof(*p));
-+	if (p != NULL)
-+		mbedtls_ecp_point_init(p);
-+	return (struct crypto_ec_point *)p;
-+}
-+
-+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
-+{
-+	mbedtls_ecp_point_free((mbedtls_ecp_point *)p);
-+	os_free(p);
-+}
-+
-+int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
-+		      struct crypto_bignum *x)
-+{
-+	mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
-+	return mbedtls_mpi_copy((mbedtls_mpi *)x, px)
-+	  ? -1
-+	  : 0;
-+}
-+
-+int crypto_ec_point_to_bin(struct crypto_ec *e,
-+			   const struct crypto_ec_point *point, u8 *x, u8 *y)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	/* crypto.h documents crypto_ec_point_to_bin() output is big-endian */
-+	size_t len = CRYPTO_EC_plen(e);
-+	if (x) {
-+		mbedtls_mpi *px = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(X);
-+		if (mbedtls_mpi_write_binary(px, x, len))
-+			return -1;
-+	}
-+	if (y) {
-+	  #if 0 /*(should not be necessary; py mpi should be in initial state)*/
-+	  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+		if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+		    == MBEDTLS_ECP_TYPE_MONTGOMERY) {
-+			os_memset(y, 0, len);
-+			return 0;
-+		}
-+	  #endif
-+	  #endif
-+		mbedtls_mpi *py = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(Y);
-+		if (mbedtls_mpi_write_binary(py, y, len))
-+			return -1;
-+	}
-+	return 0;
-+}
-+
-+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
-+						  const u8 *val)
-+{
-+	if (TEST_FAIL())
-+		return NULL;
-+
-+	size_t len = CRYPTO_EC_plen(e);
-+	mbedtls_ecp_point *p = os_malloc(sizeof(*p));
-+	u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
-+	if (p == NULL)
-+		return NULL;
-+	mbedtls_ecp_point_init(p);
-+
-+  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+	    == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
-+	  #if 0 /* prefer alternative to MBEDTLS_PRIVATE() access */
-+		mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
-+		mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
-+		mbedtls_mpi *pz = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Z);
-+
-+		if (mbedtls_mpi_read_binary(px, val, len) == 0
-+		    && mbedtls_mpi_read_binary(py, val + len, len) == 0
-+		    && mbedtls_mpi_lset(pz, 1) == 0)
-+			return (struct crypto_ec_point *)p;
-+	  #else
-+		buf[0] = 0x04;
-+		os_memcpy(buf+1, val, len*2);
-+		if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p,
-+		                                  buf, 1+len*2) == 0)
-+			return (struct crypto_ec_point *)p;
-+	  #endif
-+	}
-+  #endif
-+  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+	    == MBEDTLS_ECP_TYPE_MONTGOMERY) {
-+		/* crypto.h interface documents crypto_ec_point_from_bin()
-+		 * val is length: prime_len * 2 and is big-endian
-+		 * (Short Weierstrass is assumed by hostap)
-+		 * Reverse to little-endian format for Montgomery */
-+		for (unsigned int i = 0; i < len; ++i)
-+			buf[i] = val[len-1-i];
-+		if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p,
-+		                                  buf, len) == 0)
-+			return (struct crypto_ec_point *)p;
-+	}
-+  #endif
-+
-+	mbedtls_ecp_point_free(p);
-+	os_free(p);
-+	return NULL;
-+}
-+
-+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
-+			const struct crypto_ec_point *b,
-+			struct crypto_ec_point *c)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	/* mbedtls does not provide an mbedtls_ecp_point add function */
-+	mbedtls_mpi one;
-+	mbedtls_mpi_init(&one);
-+	int ret = mbedtls_mpi_lset(&one, 1)
-+	       || mbedtls_ecp_muladd(
-+			(mbedtls_ecp_group *)e, (mbedtls_ecp_point *)c,
-+			&one, (const mbedtls_ecp_point *)a,
-+			&one, (const mbedtls_ecp_point *)b) ? -1 : 0;
-+	mbedtls_mpi_free(&one);
-+	return ret;
-+}
-+
-+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
-+			const struct crypto_bignum *b,
-+			struct crypto_ec_point *res)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	return mbedtls_ecp_mul(
-+		(mbedtls_ecp_group *)e, (mbedtls_ecp_point *)res,
-+		(const mbedtls_mpi *)b, (const mbedtls_ecp_point *)p,
-+		mbedtls_ctr_drbg_random, crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+}
-+
-+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
-+{
-+	if (TEST_FAIL())
-+		return -1;
-+
-+	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+	    == MBEDTLS_ECP_TYPE_MONTGOMERY) {
-+		/* e.g. MBEDTLS_ECP_DP_CURVE25519 and MBEDTLS_ECP_DP_CURVE448 */
-+		wpa_printf(MSG_ERROR,
-+		           "%s not implemented for Montgomery curves",__func__);
-+		return -1;
-+	}
-+
-+	/* mbedtls does not provide an mbedtls_ecp_point invert function */
-+	/* below works for Short Weierstrass; incorrect for Montgomery curves */
-+	mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
-+	return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p) /*point at infinity*/
-+	    || mbedtls_mpi_cmp_int(py, 0) == 0      /*point is its own inverse*/
-+	    || mbedtls_mpi_sub_abs(py, CRYPTO_EC_P(e), py) == 0 ? 0 : -1;
-+}
-+
-+#ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+static int
-+crypto_ec_point_y_sqr_weierstrass(mbedtls_ecp_group *e, const mbedtls_mpi *x,
-+                                  mbedtls_mpi *y2)
-+{
-+	/* MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS  y^2 = x^3 + a x + b    */
-+
-+	/* Short Weierstrass elliptic curve group w/o A set treated as A = -3 */
-+	/* Attempt to match mbedtls/library/ecp.c:ecp_check_pubkey_sw() behavior
-+	 * and elsewhere in mbedtls/library/ecp.c where if A is not set, it is
-+	 * treated as if A = -3. */
-+
-+  #if 0
-+	/* y^2 = x^3 + ax + b */
-+	mbedtls_mpi *A = &e->A;
-+	mbedtls_mpi t, A_neg3;
-+	if (&e->A.p == NULL) {
-+		mbedtls_mpi_init(&A_neg3);
-+		if (mbedtls_mpi_lset(&A_neg3, -3) != 0) {
-+			mbedtls_mpi_free(&A_neg3);
-+			return -1;
-+		}
-+		A = &A_neg3;
-+	}
-+	mbedtls_mpi_init(&t);
-+	int ret = /* x^3 */
-+	          mbedtls_mpi_lset(&t, 3)
-+	       || mbedtls_mpi_exp_mod(y2,  x, &t, &e->P, NULL)
-+		  /* ax */
-+	       || mbedtls_mpi_mul_mpi(y2, y2, A)
-+	       || mbedtls_mpi_mod_mpi(&t, &t, &e->P)
-+		  /* ax + b */
-+	       || mbedtls_mpi_add_mpi(&t, &t, &e->B)
-+	       || mbedtls_mpi_mod_mpi(&t, &t, &e->P)
-+		  /* x^3 + ax + b */
-+	       || mbedtls_mpi_add_mpi(&t, &t, y2) /* ax + b + x^3 */
-+	       || mbedtls_mpi_mod_mpi(y2, &t, &e->P);
-+	mbedtls_mpi_free(&t);
-+	if (A == &A_neg3)
-+		mbedtls_mpi_free(&A_neg3);
-+	return ret; /* 0: success, non-zero: failure */
-+  #else
-+	/* y^2 = x^3 + ax + b = (x^2 + a)x + b */
-+	return    /* x^2 */
-+	          mbedtls_mpi_mul_mpi(y2,  x, x)
-+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+		  /* x^2 + a */
-+	       || (e->A.MBEDTLS_PRIVATE(p)
-+	           ? mbedtls_mpi_add_mpi(y2, y2, &e->A)
-+	           : mbedtls_mpi_sub_int(y2, y2, 3))
-+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+		  /* (x^2 + a)x */
-+	       || mbedtls_mpi_mul_mpi(y2, y2, x)
-+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+		  /* (x^2 + a)x + b */
-+	       || mbedtls_mpi_add_mpi(y2, y2, &e->B)
-+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P);
-+  #endif
-+}
-+#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */
-+
-+#if 0 /* not used by hostap */
-+#ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+static int
-+crypto_ec_point_y_sqr_montgomery(mbedtls_ecp_group *e, const mbedtls_mpi *x,
-+                                 mbedtls_mpi *y2)
-+{
-+	/* XXX: !!! must be reviewed and audited for correctness !!! */
-+
-+	/* MBEDTLS_ECP_TYPE_MONTGOMERY         y^2 = x^3 + a x^2 + x  */
-+
-+	/* y^2 = x^3 + a x^2 + x = (x + a)x^2 + x */
-+	mbedtls_mpi x2;
-+	mbedtls_mpi_init(&x2);
-+	int ret = /* x^2 */
-+	          mbedtls_mpi_mul_mpi(&x2, x, x)
-+	       || mbedtls_mpi_mod_mpi(&x2, &x2, &e->P)
-+		  /* x + a */
-+	       || mbedtls_mpi_add_mpi(y2,  x, &e->A)
-+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+		  /* (x + a)x^2 */
-+	       || mbedtls_mpi_mul_mpi(y2, y2, &x2)
-+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
-+		  /* (x + a)x^2 + x */
-+	       || mbedtls_mpi_add_mpi(y2, y2, x)
-+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P);
-+	mbedtls_mpi_free(&x2);
-+	return ret; /* 0: success, non-zero: failure */
-+}
-+#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */
-+#endif
-+
-+struct crypto_bignum *
-+crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
-+			      const struct crypto_bignum *x)
-+{
-+	if (TEST_FAIL())
-+		return NULL;
-+
-+	mbedtls_mpi *y2 = os_malloc(sizeof(*y2));
-+	if (y2 == NULL)
-+		return NULL;
-+	mbedtls_mpi_init(y2);
-+
-+  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+	      == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS
-+	    && crypto_ec_point_y_sqr_weierstrass((mbedtls_ecp_group *)e,
-+	                                         (const mbedtls_mpi *)x,
-+	                                         y2) == 0)
-+		return (struct crypto_bignum *)y2;
-+  #endif
-+  #if 0 /* not used by hostap */
-+  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
-+	      == MBEDTLS_ECP_TYPE_MONTGOMERY
-+	    && crypto_ec_point_y_sqr_montgomery((mbedtls_ecp_group *)e,
-+	                                        (const mbedtls_mpi *)x,
-+	                                        y2) == 0)
-+		return (struct crypto_bignum *)y2;
-+  #endif
-+  #endif
-+
-+	mbedtls_mpi_free(y2);
-+	os_free(y2);
-+	return NULL;
-+}
-+
-+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
-+				   const struct crypto_ec_point *p)
-+{
-+	return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p);
-+}
-+
-+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
-+				const struct crypto_ec_point *p)
-+{
-+  #if 1
-+	return mbedtls_ecp_check_pubkey((const mbedtls_ecp_group *)e,
-+	                                (const mbedtls_ecp_point *)p) == 0;
-+  #else
-+	/* compute y^2 mod P and compare to y^2 mod P */
-+	/*(ref: src/eap_common/eap_pwd_common.c:compute_password_element())*/
-+	const mbedtls_mpi *px = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
-+	mbedtls_mpi *cy2 = (mbedtls_mpi *)
-+	  crypto_ec_point_compute_y_sqr(e, (const struct crypto_bignum *)px);
-+	if (cy2 == NULL)
-+		return 0;
-+
-+	mbedtls_mpi y2;
-+	mbedtls_mpi_init(&y2);
-+	const mbedtls_mpi *py = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
-+	int is_on_curve = mbedtls_mpi_mul_mpi(&y2, py, py) /* y^2 mod P */
-+	               || mbedtls_mpi_mod_mpi(&y2, &y2, CRYPTO_EC_P(e))
-+	               || mbedtls_mpi_cmp_mpi(&y2, cy2) != 0 ? 0 : 1;
-+
-+	mbedtls_mpi_free(&y2);
-+	mbedtls_mpi_free(cy2);
-+	os_free(cy2);
-+	return is_on_curve;
-+  #endif
-+}
-+
-+int crypto_ec_point_cmp(const struct crypto_ec *e,
-+			const struct crypto_ec_point *a,
-+			const struct crypto_ec_point *b)
-+{
-+	return mbedtls_ecp_point_cmp((const mbedtls_ecp_point *)a,
-+	                             (const mbedtls_ecp_point *)b);
-+}
-+
-+#if !defined(CONFIG_NO_STDOUT_DEBUG)
-+void crypto_ec_point_debug_print(const struct crypto_ec *e,
-+				 const struct crypto_ec_point *p,
-+				 const char *title)
-+{
-+	u8 x[MBEDTLS_MPI_MAX_SIZE];
-+	u8 y[MBEDTLS_MPI_MAX_SIZE];
-+	size_t len = CRYPTO_EC_plen(e);
-+	/* crypto_ec_point_to_bin ought to take (const struct crypto_ec *e) */
-+	struct crypto_ec *ee;
-+	*(const struct crypto_ec **)&ee = e; /*(cast away const)*/
-+	if (crypto_ec_point_to_bin(ee, p, x, y) == 0) {
-+		if (title)
-+			wpa_printf(MSG_DEBUG, "%s", title);
-+		wpa_hexdump(MSG_DEBUG, "x:", x, len);
-+		wpa_hexdump(MSG_DEBUG, "y:", y, len);
-+	}
-+}
-+#endif
-+
-+
-+struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len)
-+{
-+	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+	if (ctx == NULL)
-+		return NULL;
-+	mbedtls_pk_init(ctx);
-+  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+	if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0) == 0)
-+  #else
-+	if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0,
-+	                         mbedtls_ctr_drbg_random,
-+	                         crypto_mbedtls_ctr_drbg()) == 0)
-+  #endif
-+		return (struct crypto_ec_key *)ctx;
-+
-+	mbedtls_pk_free(ctx);
-+	os_free(ctx);
-+	return NULL;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE
-+#ifdef CONFIG_MODULE_TESTS
-+/*(for crypto_module_tests.c)*/
-+struct crypto_ec_key * crypto_ec_key_set_priv(int group,
-+					      const u8 *raw, size_t raw_len)
-+{
-+	mbedtls_ecp_group_id grp_id =
-+	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+	if (grp_id == MBEDTLS_ECP_DP_NONE)
-+		return NULL;
-+	const mbedtls_pk_info_t *pk_info =
-+	  mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
-+	if (pk_info == NULL)
-+		return NULL;
-+	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+	if (ctx == NULL)
-+		return NULL;
-+	mbedtls_pk_init(ctx);
-+	if (mbedtls_pk_setup(ctx, pk_info) == 0
-+	    && mbedtls_ecp_read_key(grp_id,mbedtls_pk_ec(*ctx),raw,raw_len) == 0) {
-+		return (struct crypto_ec_key *)ctx;
-+	}
-+
-+	mbedtls_pk_free(ctx);
-+	os_free(ctx);
-+	return NULL;
-+}
-+#endif
-+#endif
-+
-+#include <mbedtls/error.h>
-+#include <mbedtls/oid.h>
-+static int crypto_mbedtls_pk_parse_subpubkey_compressed(mbedtls_pk_context *ctx, const u8 *der, size_t der_len)
-+{
-+    /* The following is modified from:
-+     *   mbedtls/library/pkparse.c:mbedtls_pk_parse_subpubkey()
-+     *   mbedtls/library/pkparse.c:pk_get_pk_alg()
-+     *   mbedtls/library/pkparse.c:pk_use_ecparams()
-+     */
-+    mbedtls_pk_type_t pk_alg = MBEDTLS_PK_NONE;
-+    const mbedtls_pk_info_t *pk_info;
-+    int ret;
-+    size_t len;
-+    const unsigned char *end = der+der_len;
-+    unsigned char *p;
-+    *(const unsigned char **)&p = der;
-+
-+    if( ( ret = mbedtls_asn1_get_tag( &p, end, &len,
-+                    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
-+    {
-+        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret ) );
-+    }
-+
-+    end = p + len;
-+
-+    /*
-+    if( ( ret = pk_get_pk_alg( &p, end, &pk_alg, &alg_params ) ) != 0 )
-+        return( ret );
-+    */
-+    mbedtls_asn1_buf alg_oid, params;
-+    memset( &params, 0, sizeof(mbedtls_asn1_buf) );
-+    if( ( ret = mbedtls_asn1_get_alg( &p, end, &alg_oid, &params ) ) != 0 )
-+        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_ALG, ret ) );
-+    if( mbedtls_oid_get_pk_alg( &alg_oid, &pk_alg ) != 0 )
-+        return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
-+
-+    if( ( ret = mbedtls_asn1_get_bitstring_null( &p, end, &len ) ) != 0 )
-+        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY, ret ) );
-+
-+    if( p + len != end )
-+        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY,
-+                MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ) );
-+
-+    if( ( pk_info = mbedtls_pk_info_from_type( pk_alg ) ) == NULL )
-+        return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
-+
-+    if( ( ret = mbedtls_pk_setup( ctx, pk_info ) ) != 0 )
-+        return( ret );
-+
-+    /* assume mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx)
-+     * has already run with ctx initialized up to pk_get_ecpubkey(),
-+     * and pk_get_ecpubkey() has returned MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
-+     *
-+     * mbedtls mbedtls_ecp_point_read_binary()
-+     * does not handle point in COMPRESSED format
-+     *
-+     * (validate assumption that algorithm is EC) */
-+    mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx);
-+    if (ecp_kp == NULL)
-+        return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
-+    mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+    mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+    mbedtls_ecp_group_id grp_id;
-+
-+
-+    /* mbedtls/library/pkparse.c:pk_use_ecparams() */
-+
-+    if( params.tag == MBEDTLS_ASN1_OID )
-+    {
-+        if( mbedtls_oid_get_ec_grp( &params, &grp_id ) != 0 )
-+            return( MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE );
-+    }
-+    else
-+    {
-+#if defined(MBEDTLS_PK_PARSE_EC_EXTENDED)
-+        /*(large code block not copied from mbedtls; unsupported)*/
-+      #if 0
-+        if( ( ret = pk_group_id_from_specified( &params, &grp_id ) ) != 0 )
-+            return( ret );
-+      #endif
-+#endif
-+        return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
-+    }
-+
-+    /*
-+     * grp may already be initialized; if so, make sure IDs match
-+     */
-+    if( ecp_kp_grp->id != MBEDTLS_ECP_DP_NONE && ecp_kp_grp->id != grp_id )
-+        return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
-+
-+    if( ( ret = mbedtls_ecp_group_load( ecp_kp_grp, grp_id ) ) != 0 )
-+        return( ret );
-+
-+
-+    /* (validate assumption that EC point is in COMPRESSED format) */
-+    len = CRYPTO_EC_plen(ecp_kp_grp);
-+    if( mbedtls_ecp_get_type(ecp_kp_grp) != MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS
-+        || (end - p) != 1+len
-+        || (*p != 0x02 && *p != 0x03) )
-+        return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
-+
-+    /* Instead of calling mbedtls/library/pkparse.c:pk_get_ecpubkey() to call
-+     * mbedtls_ecp_point_read_binary(), manually parse point into ecp_kp_Q */
-+    mbedtls_mpi *X = &ecp_kp_Q->MBEDTLS_PRIVATE(X);
-+    mbedtls_mpi *Y = &ecp_kp_Q->MBEDTLS_PRIVATE(Y);
-+    mbedtls_mpi *Z = &ecp_kp_Q->MBEDTLS_PRIVATE(Z);
-+    ret = mbedtls_mpi_lset(Z, 1);
-+    if (ret != 0)
-+        return( ret );
-+    ret = mbedtls_mpi_read_binary(X, p+1, len);
-+    if (ret != 0)
-+        return( ret );
-+    /* derive Y
-+     * (similar derivation of Y in crypto_mbedtls.c:crypto_ecdh_set_peerkey())*/
-+    ret = mbedtls_mpi_copy(Y, X) /*(Y is used as input and output obj below)*/
-+       || crypto_mbedtls_short_weierstrass_derive_y(ecp_kp_grp, Y, (*p & 1));
-+    if (ret != 0)
-+        return( ret );
-+
-+    return mbedtls_ecp_check_pubkey( ecp_kp_grp, ecp_kp_Q );
-+}
-+
-+struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len)
-+{
-+	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+	if (ctx == NULL)
-+		return NULL;
-+	mbedtls_pk_init(ctx);
-+	/*int rc = mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx);*/
-+	int rc = mbedtls_pk_parse_public_key(ctx, der, der_len);
-+	if (rc == 0)
-+		return (struct crypto_ec_key *)ctx;
-+	else if (rc == MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) {
-+		/* mbedtls mbedtls_ecp_point_read_binary()
-+		 * does not handle point in COMPRESSED format; parse internally */
-+		rc = crypto_mbedtls_pk_parse_subpubkey_compressed(ctx,der,der_len);
-+		if (rc == 0)
-+			return (struct crypto_ec_key *)ctx;
-+	}
-+
-+	mbedtls_pk_free(ctx);
-+	os_free(ctx);
-+	return NULL;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+
-+static struct crypto_ec_key *
-+crypto_ec_key_set_pub_point_for_group(mbedtls_ecp_group_id grp_id,
-+                                      const mbedtls_ecp_point *pub,
-+                                      const u8 *buf, size_t len)
-+{
-+	const mbedtls_pk_info_t *pk_info =
-+	  mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
-+	if (pk_info == NULL)
-+		return NULL;
-+	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+	if (ctx == NULL)
-+		return NULL;
-+	mbedtls_pk_init(ctx);
-+	if (mbedtls_pk_setup(ctx, pk_info) == 0) {
-+		/* (Is private key generation necessary for callers?)
-+		 * alt: gen key then overwrite Q
-+		 *   mbedtls_ecp_gen_key(grp_id, ecp_kp,
-+	         *                       mbedtls_ctr_drbg_random,
-+	         *                       crypto_mbedtls_ctr_drbg()) == 0
-+	         */
-+		mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx);
-+		mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+		mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+		mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d);
-+		if (mbedtls_ecp_group_load(ecp_kp_grp, grp_id) == 0
-+		    && (pub
-+		         ? mbedtls_ecp_copy(ecp_kp_Q, pub) == 0
-+		         : mbedtls_ecp_point_read_binary(ecp_kp_grp, ecp_kp_Q,
-+		                                         buf, len) == 0)
-+		    && mbedtls_ecp_gen_privkey(ecp_kp_grp, ecp_kp_d,
-+		                               mbedtls_ctr_drbg_random,
-+		                               crypto_mbedtls_ctr_drbg()) == 0){
-+			return (struct crypto_ec_key *)ctx;
-+		}
-+	}
-+
-+	mbedtls_pk_free(ctx);
-+	os_free(ctx);
-+	return NULL;
-+}
-+
-+struct crypto_ec_key * crypto_ec_key_set_pub(int group, const u8 *x,
-+					     const u8 *y, size_t len)
-+{
-+	mbedtls_ecp_group_id grp_id =
-+	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
-+	if (grp_id == MBEDTLS_ECP_DP_NONE)
-+		return NULL;
-+	if (len > MBEDTLS_MPI_MAX_SIZE)
-+		return NULL;
-+	u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
-+	buf[0] = 0x04; /* assume x,y for Short Weierstrass */
-+	os_memcpy(buf+1, x, len);
-+	os_memcpy(buf+1+len, y, len);
-+
-+	return crypto_ec_key_set_pub_point_for_group(grp_id,NULL,buf,1+len*2);
-+}
-+
-+struct crypto_ec_key *
-+crypto_ec_key_set_pub_point(struct crypto_ec *e,
-+			    const struct crypto_ec_point *pub)
-+{
-+	mbedtls_ecp_group_id grp_id = ((mbedtls_ecp_group *)e)->id;
-+	mbedtls_ecp_point *p = (mbedtls_ecp_point *)pub;
-+	return crypto_ec_key_set_pub_point_for_group(grp_id, p, NULL, 0);
-+}
-+
-+
-+struct crypto_ec_key * crypto_ec_key_gen(int group)
-+{
-+	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+	if (ctx == NULL)
-+		return NULL;
-+	mbedtls_pk_init(ctx);
-+	if (crypto_mbedtls_keypair_gen(group, ctx) == 0)
-+		return (struct crypto_ec_key *)ctx;
-+	mbedtls_pk_free(ctx);
-+	os_free(ctx);
-+	return NULL;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+void crypto_ec_key_deinit(struct crypto_ec_key *key)
-+{
-+	mbedtls_pk_free((mbedtls_pk_context *)key);
-+	os_free(key);
-+}
-+
-+struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key)
-+{
-+	/* (similar to crypto_ec_key_get_pubkey_point(),
-+	 *  but compressed point format and ASN.1 DER wrapping)*/
-+#ifndef MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/
-+#define MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES    ( 30 + 2 * MBEDTLS_ECP_MAX_BYTES )
-+#endif
-+	unsigned char buf[MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES];
-+	int len = mbedtls_pk_write_pubkey_der((mbedtls_pk_context *)key,
-+	                                      buf, sizeof(buf));
-+	if (len < 0)
-+		return NULL;
-+	/*  Note: data is written at the end of the buffer! Use the
-+	 *        return value to determine where you should start
-+	 *        using the buffer */
-+	unsigned char *p = buf+sizeof(buf)-len;
-+
-+  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+	if (ecp_kp == NULL)
-+		return NULL;
-+	mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+	/*  Note: sae_pk.c expects pubkey point in compressed format,
-+	 *        but mbedtls_pk_write_pubkey_der() writes uncompressed format.
-+	 *        Manually translate format and update lengths in DER format */
-+	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
-+		unsigned char *end = buf+sizeof(buf);
-+		size_t n;
-+		/* SubjectPublicKeyInfo SEQUENCE */
-+		mbedtls_asn1_get_tag(&p, end, &n,
-+		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+		/* algorithm AlgorithmIdentifier */
-+		unsigned char *a = p;
-+		size_t alen;
-+		mbedtls_asn1_get_tag(&p, end, &alen,
-+		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+		p += alen;
-+		alen = (size_t)(p - a);
-+		/* subjectPublicKey BIT STRING */
-+		mbedtls_asn1_get_tag(&p, end, &n, MBEDTLS_ASN1_BIT_STRING);
-+		/* rewrite into compressed point format and rebuild ASN.1 */
-+		p[1] = (buf[sizeof(buf)-1] & 1) ? 0x03 : 0x02;
-+		n = 1 + 1 + (n-2)/2;
-+		len = mbedtls_asn1_write_len(&p, buf, n) + (int)n;
-+		len += mbedtls_asn1_write_tag(&p, buf, MBEDTLS_ASN1_BIT_STRING);
-+		os_memmove(p-alen, a, alen);
-+		len += alen;
-+		p -= alen;
-+		len += mbedtls_asn1_write_len(&p, buf, (size_t)len);
-+		len += mbedtls_asn1_write_tag(&p, buf,
-+		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+	}
-+  #endif
-+	return wpabuf_alloc_copy(p, (size_t)len);
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+
-+struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key,
-+						bool include_pub)
-+{
-+#ifndef MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/
-+#define MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES    ( 29 + 3 * MBEDTLS_ECP_MAX_BYTES )
-+#endif
-+	unsigned char priv[MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES];
-+	int privlen = mbedtls_pk_write_key_der((mbedtls_pk_context *)key,
-+	                                       priv, sizeof(priv));
-+	if (privlen < 0)
-+		return NULL;
-+
-+	struct wpabuf *wbuf;
-+
-+	/*  Note: data is written at the end of the buffer! Use the
-+	 *        return value to determine where you should start
-+	 *        using the buffer */
-+	/* mbedtls_pk_write_key_der() includes publicKey in DER */
-+	if (include_pub)
-+		wbuf = wpabuf_alloc_copy(priv+sizeof(priv)-privlen, privlen);
-+	else {
-+		/* calculate publicKey offset and skip from end of buffer */
-+		unsigned char *p = priv+sizeof(priv)-privlen;
-+		unsigned char *end = priv+sizeof(priv);
-+		size_t len;
-+		/* ECPrivateKey SEQUENCE */
-+		mbedtls_asn1_get_tag(&p, end, &len,
-+		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+		/* version INTEGER */
-+		unsigned char *v = p;
-+		mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_INTEGER);
-+		p += len;
-+		/* privateKey OCTET STRING */
-+		mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING);
-+		p += len;
-+		/* parameters ECParameters */
-+		mbedtls_asn1_get_tag(&p, end, &len,
-+		    MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED);
-+		p += len;
-+
-+		/* write new SEQUENCE header (we know that it fits in priv[]) */
-+		len = (size_t)(p - v);
-+		p = v;
-+		len += mbedtls_asn1_write_len(&p, priv, len);
-+		len += mbedtls_asn1_write_tag(&p, priv,
-+		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+		wbuf = wpabuf_alloc_copy(p, len);
-+	}
-+
-+	forced_memzero(priv, sizeof(priv));
-+	return wbuf;
-+}
-+
-+struct wpabuf * crypto_ec_key_get_pubkey_point(struct crypto_ec_key *key,
-+					       int prefix)
-+{
-+	/*(similarities to crypto_ecdh_get_pubkey(), but different struct)*/
-+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+	if (ecp_kp == NULL)
-+		return NULL;
-+	mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+	size_t len = CRYPTO_EC_plen(grp);
-+  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
-+	/* len */
-+  #endif
-+  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
-+	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS)
-+		len = len*2+1;
-+  #endif
-+	struct wpabuf *buf = wpabuf_alloc(len);
-+	if (buf == NULL)
-+		return NULL;
-+	mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+	if (mbedtls_ecp_point_write_binary(grp, ecp_kp_Q,
-+	                                   MBEDTLS_ECP_PF_UNCOMPRESSED, &len,
-+	                                   wpabuf_mhead_u8(buf), len) == 0) {
-+		if (!prefix) /* Remove 0x04 prefix if requested */
-+			os_memmove(wpabuf_mhead(buf),wpabuf_mhead(buf)+1,--len);
-+		wpabuf_put(buf, len);
-+		return buf;
-+	}
-+
-+	wpabuf_free(buf);
-+	return NULL;
-+}
-+
-+struct crypto_ec_point *
-+crypto_ec_key_get_public_key(struct crypto_ec_key *key)
-+{
-+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+	if (ecp_kp == NULL)
-+		return NULL;
-+	mbedtls_ecp_point *p = os_malloc(sizeof(*p));
-+	if (p != NULL) {
-+		/*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/
-+		mbedtls_ecp_point_init(p);
-+		mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+		if (mbedtls_ecp_copy(p, ecp_kp_Q)) {
-+			mbedtls_ecp_point_free(p);
-+			os_free(p);
-+			p = NULL;
-+		}
-+	}
-+	return (struct crypto_ec_point *)p;
-+}
-+
-+struct crypto_bignum *
-+crypto_ec_key_get_private_key(struct crypto_ec_key *key)
-+{
-+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+	if (ecp_kp == NULL)
-+		return NULL;
-+	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
-+	if (bn) {
-+		/*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/
-+		mbedtls_mpi_init(bn);
-+		mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d);
-+		if (mbedtls_mpi_copy(bn, ecp_kp_d)) {
-+			mbedtls_mpi_free(bn);
-+			os_free(bn);
-+			bn = NULL;
-+		}
-+	}
-+	return (struct crypto_bignum *)bn;
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+static mbedtls_md_type_t crypto_ec_key_sign_md(size_t len)
-+{
-+	/* get mbedtls_md_type_t from length of hash data to be signed */
-+	switch (len) {
-+	case 64: return MBEDTLS_MD_SHA512;
-+	case 48: return MBEDTLS_MD_SHA384;
-+	case 32: return MBEDTLS_MD_SHA256;
-+	case 20: return MBEDTLS_MD_SHA1;
-+	case 16: return MBEDTLS_MD_MD5;
-+	default: return MBEDTLS_MD_NONE;
-+	}
-+}
-+
-+struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
-+				   size_t len)
-+{
-+  #ifndef MBEDTLS_PK_SIGNATURE_MAX_SIZE /*(defined since mbedtls 2.20.0)*/
-+  #if MBEDTLS_ECDSA_MAX_LEN > MBEDTLS_MPI_MAX_SIZE
-+  #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_ECDSA_MAX_LEN
-+  #else
-+  #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_MPI_MAX_SIZE
-+  #endif
-+  #endif
-+	size_t sig_len = MBEDTLS_PK_SIGNATURE_MAX_SIZE;
-+	struct wpabuf *buf = wpabuf_alloc(sig_len);
-+	if (buf == NULL)
-+		return NULL;
-+	if (mbedtls_pk_sign((mbedtls_pk_context *)key,
-+	                    crypto_ec_key_sign_md(len), data, len,
-+	                    wpabuf_mhead_u8(buf),
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+	                    sig_len,
-+  #endif
-+	                    &sig_len,
-+	                    mbedtls_ctr_drbg_random,
-+	                    crypto_mbedtls_ctr_drbg()) == 0) {
-+		wpabuf_put(buf, sig_len);
-+		return buf;
-+	}
-+
-+	wpabuf_free(buf);
-+	return NULL;
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+struct wpabuf * crypto_ec_key_sign_r_s(struct crypto_ec_key *key,
-+				       const u8 *data, size_t len)
-+{
-+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+	if (ecp_kp == NULL)
-+		return NULL;
-+
-+	size_t sig_len = MBEDTLS_ECDSA_MAX_LEN;
-+	u8 buf[MBEDTLS_ECDSA_MAX_LEN];
-+	if (mbedtls_ecdsa_write_signature(ecp_kp, crypto_ec_key_sign_md(len),
-+	                                  data, len, buf,
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+	                                  sig_len,
-+  #endif
-+	                                  &sig_len,
-+	                                  mbedtls_ctr_drbg_random,
-+	                                  crypto_mbedtls_ctr_drbg())) {
-+		return NULL;
-+	}
-+
-+	/*(mbedtls_ecdsa_write_signature() writes signature in ASN.1)*/
-+	/* parse ASN.1 to get r and s and lengths */
-+	u8 *p = buf, *r, *s;
-+	u8 *end = p + sig_len;
-+	size_t rlen, slen;
-+	mbedtls_asn1_get_tag(&p, end, &rlen,
-+	  MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+	mbedtls_asn1_get_tag(&p, end, &rlen, MBEDTLS_ASN1_INTEGER);
-+	r = p;
-+	p += rlen;
-+	mbedtls_asn1_get_tag(&p, end, &slen, MBEDTLS_ASN1_INTEGER);
-+	s = p;
-+
-+	/* write raw r and s into out
-+	 * (including removal of leading 0 if added for ASN.1 integer)
-+	 * note: DPP caller expects raw r, s each padded to prime len */
-+	mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+	size_t plen = CRYPTO_EC_plen(ecp_kp_grp);
-+	if (rlen > plen) {
-+		r += (rlen - plen);
-+		rlen = plen;
-+	}
-+	if (slen > plen) {
-+		s += (slen - plen);
-+		slen = plen;
-+	}
-+	struct wpabuf *out = wpabuf_alloc(plen*2);
-+	if (out) {
-+		wpabuf_put(out, plen*2);
-+		p = wpabuf_mhead_u8(out);
-+		os_memset(p, 0, plen*2);
-+		os_memcpy(p+plen*1-rlen, r, rlen);
-+		os_memcpy(p+plen*2-slen, s, slen);
-+	}
-+	return out;
-+}
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
-+				   size_t len, const u8 *sig, size_t sig_len)
-+{
-+	switch (mbedtls_pk_verify((mbedtls_pk_context *)key,
-+	                          crypto_ec_key_sign_md(len), data, len,
-+	                          sig, sig_len)) {
-+	case 0:
-+	/*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */
-+		return 1;
-+	case MBEDTLS_ERR_ECP_VERIFY_FAILED:
-+		return 0;
-+	default:
-+		return -1;
-+	}
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+int crypto_ec_key_verify_signature_r_s(struct crypto_ec_key *key,
-+				       const u8 *data, size_t len,
-+				       const u8 *r, size_t r_len,
-+				       const u8 *s, size_t s_len)
-+{
-+	/* reimplement mbedtls_ecdsa_read_signature() without encoding r and s
-+	 * into ASN.1 just for mbedtls_ecdsa_read_signature() to decode ASN.1 */
-+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+	if (ecp_kp == NULL)
-+		return -1;
-+	mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+	mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
-+
-+	mbedtls_mpi mpi_r;
-+	mbedtls_mpi mpi_s;
-+	mbedtls_mpi_init(&mpi_r);
-+	mbedtls_mpi_init(&mpi_s);
-+	int ret = mbedtls_mpi_read_binary(&mpi_r, r, r_len)
-+	       || mbedtls_mpi_read_binary(&mpi_s, s, s_len) ? -1 : 0;
-+	if (ret == 0) {
-+		ret = mbedtls_ecdsa_verify(ecp_kp_grp, data, len,
-+		                           ecp_kp_Q, &mpi_r, &mpi_s);
-+		ret = ret ? ret == MBEDTLS_ERR_ECP_BAD_INPUT_DATA ? 0 : -1 : 1;
-+	}
-+	mbedtls_mpi_free(&mpi_r);
-+	mbedtls_mpi_free(&mpi_s);
-+	return ret;
-+}
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+int crypto_ec_key_group(struct crypto_ec_key *key)
-+{
-+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
-+	if (ecp_kp == NULL)
-+		return -1;
-+	mbedtls_ecp_group *ecp_group = &ecp_kp->MBEDTLS_PRIVATE(grp);
-+	return crypto_mbedtls_ike_id_from_ecp_group_id(ecp_group->id);
-+}
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
-+
-+int crypto_ec_key_cmp(struct crypto_ec_key *key1, struct crypto_ec_key *key2)
-+{
-+#if 0 /*(DPP is passing two public keys; unable to use pk_check_pair())*/
-+  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+	return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1,
-+	                             (const mbedtls_pk_context *)key2) ? -1 : 0;
-+  #else
-+	return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1,
-+	                             (const mbedtls_pk_context *)key2,
-+	                             mbedtls_ctr_drbg_random,
-+	                             crypto_mbedtls_ctr_drbg()) ? -1 : 0;
-+  #endif
-+#else
-+	mbedtls_ecp_keypair *ecp_kp1=mbedtls_pk_ec(*(mbedtls_pk_context *)key1);
-+	mbedtls_ecp_keypair *ecp_kp2=mbedtls_pk_ec(*(mbedtls_pk_context *)key2);
-+	if (ecp_kp1 == NULL || ecp_kp2 == NULL)
-+		return -1;
-+	mbedtls_ecp_group *ecp_kp1_grp = &ecp_kp1->MBEDTLS_PRIVATE(grp);
-+	mbedtls_ecp_group *ecp_kp2_grp = &ecp_kp2->MBEDTLS_PRIVATE(grp);
-+	mbedtls_ecp_point *ecp_kp1_Q = &ecp_kp1->MBEDTLS_PRIVATE(Q);
-+	mbedtls_ecp_point *ecp_kp2_Q = &ecp_kp2->MBEDTLS_PRIVATE(Q);
-+	return ecp_kp1_grp->id != ecp_kp2_grp->id
-+	    || mbedtls_ecp_point_cmp(ecp_kp1_Q, ecp_kp2_Q) ? -1 : 0;
-+#endif
-+}
-+
-+void crypto_ec_key_debug_print(const struct crypto_ec_key *key,
-+			       const char *title)
-+{
-+	/* TBD: what info is desirable here and in what human readable format?*/
-+	/*(crypto_openssl.c prints a human-readably public key and attributes)*/
-+  #if 0
-+	struct mbedtls_pk_debug_item debug_item;
-+	if (mbedtls_pk_debug((const mbedtls_pk_context *)key, &debug_item))
-+		return;
-+	/* ... */
-+  #endif
-+	wpa_printf(MSG_DEBUG, "%s: %s not implemented", title, __func__);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_CSR
-+
-+#include <mbedtls/x509_csr.h>
-+#include <mbedtls/oid.h>
-+
-+struct crypto_csr * crypto_csr_init(void)
-+{
-+	mbedtls_x509write_csr *csr = os_malloc(sizeof(*csr));
-+	if (csr != NULL)
-+		mbedtls_x509write_csr_init(csr);
-+	return (struct crypto_csr *)csr;
-+}
-+
-+struct crypto_csr * crypto_csr_verify(const struct wpabuf *req)
-+{
-+	/* future: look for alternatives to MBEDTLS_PRIVATE() access */
-+
-+	/* sole caller src/common/dpp_crypto.c:dpp_validate_csr()
-+	 * uses (mbedtls_x509_csr *) to obtain CSR_ATTR_CHALLENGE_PASSWORD
-+	 * so allocate different object (mbedtls_x509_csr *) and special-case
-+	 * object when used in crypto_csr_get_attribute() and when free()d in
-+	 * crypto_csr_deinit(). */
-+
-+	mbedtls_x509_csr *csr = os_malloc(sizeof(*csr));
-+	if (csr == NULL)
-+		return NULL;
-+	mbedtls_x509_csr_init(csr);
-+	const mbedtls_md_info_t *md_info;
-+	unsigned char digest[MBEDTLS_MD_MAX_SIZE];
-+	if (mbedtls_x509_csr_parse_der(csr,wpabuf_head(req),wpabuf_len(req))==0
-+	    && (md_info=mbedtls_md_info_from_type(csr->MBEDTLS_PRIVATE(sig_md)))
-+	       != NULL
-+	    && mbedtls_md(md_info, csr->cri.p, csr->cri.len, digest) == 0) {
-+		switch (mbedtls_pk_verify(&csr->pk,csr->MBEDTLS_PRIVATE(sig_md),
-+		                          digest, mbedtls_md_get_size(md_info),
-+		                          csr->MBEDTLS_PRIVATE(sig).p,
-+		                          csr->MBEDTLS_PRIVATE(sig).len)) {
-+		case 0:
-+		/*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */
-+			return (struct crypto_csr *)((uintptr_t)csr | 1uL);
-+		default:
-+			break;
-+		}
-+	}
-+
-+	mbedtls_x509_csr_free(csr);
-+	os_free(csr);
-+	return NULL;
-+}
-+
-+void crypto_csr_deinit(struct crypto_csr *csr)
-+{
-+	if ((uintptr_t)csr & 1uL) {
-+		csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL);
-+		mbedtls_x509_csr_free((mbedtls_x509_csr *)csr);
-+	}
-+	else
-+		mbedtls_x509write_csr_free((mbedtls_x509write_csr *)csr);
-+	os_free(csr);
-+}
-+
-+int crypto_csr_set_ec_public_key(struct crypto_csr *csr,
-+				 struct crypto_ec_key *key)
-+{
-+	mbedtls_x509write_csr_set_key((mbedtls_x509write_csr *)csr,
-+	                              (mbedtls_pk_context *)key);
-+	return 0;
-+}
-+
-+int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type,
-+			const char *name)
-+{
-+	/* specialized for src/common/dpp_crypto.c */
-+
-+	/* sole caller src/common/dpp_crypto.c:dpp_build_csr()
-+	 * calls this function only once, using type == CSR_NAME_CN
-+	 * (If called more than once, this code would need to append
-+	 *  components to the subject name, which we could do by
-+	 *  appending to (mbedtls_x509write_csr *) private member
-+	 *  mbedtls_asn1_named_data *MBEDTLS_PRIVATE(subject)) */
-+
-+	const char *label;
-+	switch (type) {
-+	case CSR_NAME_CN: label = "CN="; break;
-+	case CSR_NAME_SN: label = "SN="; break;
-+	case CSR_NAME_C:  label = "C=";  break;
-+	case CSR_NAME_O:  label = "O=";  break;
-+	case CSR_NAME_OU: label = "OU="; break;
-+	default: return -1;
-+	}
-+
-+	size_t len = strlen(name);
-+	struct wpabuf *buf = wpabuf_alloc(3+len+1);
-+	if (buf == NULL)
-+		return -1;
-+	wpabuf_put_data(buf, label, strlen(label));
-+	wpabuf_put_data(buf, name, len+1); /*(include trailing '\0')*/
-+	/* Note: 'name' provided is set as given and should be backslash-escaped
-+	 * by caller when necessary, e.g. literal ',' which are not separating
-+	 * components should be backslash-escaped */
-+
-+	int ret =
-+	  mbedtls_x509write_csr_set_subject_name((mbedtls_x509write_csr *)csr,
-+	                                         wpabuf_head(buf)) ? -1 : 0;
-+	wpabuf_free(buf);
-+	return ret;
-+}
-+
-+/* OBJ_pkcs9_challengePassword  1 2 840 113549 1 9 7 */
-+static const char OBJ_pkcs9_challengePassword[] = MBEDTLS_OID_PKCS9 "\x07";
-+
-+int crypto_csr_set_attribute(struct crypto_csr *csr, enum crypto_csr_attr attr,
-+			     int attr_type, const u8 *value, size_t len)
-+{
-+	/* specialized for src/common/dpp_crypto.c */
-+	/* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes
-+	 *   attr      == CSR_ATTR_CHALLENGE_PASSWORD
-+	 *   attr_type == ASN1_TAG_UTF8STRING */
-+
-+	const char *oid;
-+	size_t oid_len;
-+	switch (attr) {
-+	case CSR_ATTR_CHALLENGE_PASSWORD:
-+		oid = OBJ_pkcs9_challengePassword;
-+		oid_len = sizeof(OBJ_pkcs9_challengePassword)-1;
-+		break;
-+	default:
-+		return -1;
-+	}
-+
-+  #if 0 /*(incorrect; sets an extension, not an attribute)*/
-+	return mbedtls_x509write_csr_set_extension((mbedtls_x509write_csr *)csr,
-+	                                           oid, oid_len,
-+	  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+	                                           0, /*(critical flag)*/
-+	  #endif
-+	                                           value, len) ? -1 : 0;
-+  #else
-+	(void)oid;
-+	(void)oid_len;
-+  #endif
-+
-+	/* mbedtls does not currently provide way to set an attribute in a CSR:
-+	 *   https://github.com/Mbed-TLS/mbedtls/issues/4886 */
-+	wpa_printf(MSG_ERROR,
-+	  "mbedtls does not currently support setting challengePassword "
-+	  "attribute in CSR");
-+	return -1;
-+}
-+
-+const u8 * mbedtls_x509_csr_attr_oid_value(mbedtls_x509_csr *csr,
-+                                           const char *oid, size_t oid_len,
-+                                           size_t *vlen, int *vtype)
-+{
-+	/* Note: mbedtls_x509_csr_parse_der() has parsed and validated CSR,
-+	 *	   so validation checks are not repeated here
-+	 *
-+	 * It would be nicer if (mbedtls_x509_csr *) had an mbedtls_x509_buf of
-+	 * Attributes (or at least a pointer) since mbedtls_x509_csr_parse_der()
-+	 * already parsed the rest of CertificationRequestInfo, some of which is
-+	 * repeated here to step to Attributes.  Since csr->subject_raw.p points
-+	 * into csr->cri.p, which points into csr->raw.p, step over version and
-+	 * subject of CertificationRequestInfo (SEQUENCE) */
-+	unsigned char *p = csr->subject_raw.p + csr->subject_raw.len;
-+	unsigned char *end = csr->cri.p + csr->cri.len, *ext;
-+	size_t len;
-+
-+	/* step over SubjectPublicKeyInfo */
-+	mbedtls_asn1_get_tag(&p, end, &len,
-+	    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
-+	p += len;
-+
-+	/* Attributes
-+	 *   { ATTRIBUTE:IOSet } ::= SET OF { SEQUENCE { OID, value } }
-+	 */
-+	if (mbedtls_asn1_get_tag(&p, end, &len,
-+	      MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) != 0) {
-+		return NULL;
-+	}
-+	while (p < end) {
-+		if (mbedtls_asn1_get_tag(&p, end, &len,
-+		      MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
-+			return NULL;
-+		}
-+		ext = p;
-+		p += len;
-+
-+		if (mbedtls_asn1_get_tag(&ext,end,&len,MBEDTLS_ASN1_OID) != 0)
-+			return NULL;
-+		if (oid_len != len || 0 != memcmp(ext, oid, oid_len))
-+			continue;
-+
-+		/* found oid; return value */
-+		*vtype = *ext++; /* tag */
-+		return (mbedtls_asn1_get_len(&ext,end,vlen) == 0) ? ext : NULL;
-+	}
-+
-+	return NULL;
-+}
-+
-+const u8 * crypto_csr_get_attribute(struct crypto_csr *csr,
-+				    enum crypto_csr_attr attr,
-+				    size_t *len, int *type)
-+{
-+	/* specialized for src/common/dpp_crypto.c */
-+	/* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes
-+	 *   attr == CSR_ATTR_CHALLENGE_PASSWORD */
-+
-+	const char *oid;
-+	size_t oid_len;
-+	switch (attr) {
-+	case CSR_ATTR_CHALLENGE_PASSWORD:
-+		oid = OBJ_pkcs9_challengePassword;
-+		oid_len = sizeof(OBJ_pkcs9_challengePassword)-1;
-+		break;
-+	default:
-+		return NULL;
-+	}
-+
-+	/* see crypto_csr_verify(); expecting (mbedtls_x509_csr *) tagged |=1 */
-+	if (!((uintptr_t)csr & 1uL))
-+		return NULL;
-+	csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL);
-+
-+	return mbedtls_x509_csr_attr_oid_value((mbedtls_x509_csr *)csr,
-+	                                       oid, oid_len, len, type);
-+}
-+
-+struct wpabuf * crypto_csr_sign(struct crypto_csr *csr,
-+				struct crypto_ec_key *key,
-+				enum crypto_hash_alg algo)
-+{
-+	mbedtls_md_type_t sig_md;
-+	switch (algo) {
-+  #ifdef MBEDTLS_SHA256_C
-+	case CRYPTO_HASH_ALG_SHA256: sig_md = MBEDTLS_MD_SHA256; break;
-+  #endif
-+  #ifdef MBEDTLS_SHA512_C
-+	case CRYPTO_HASH_ALG_SHA384: sig_md = MBEDTLS_MD_SHA384; break;
-+	case CRYPTO_HASH_ALG_SHA512: sig_md = MBEDTLS_MD_SHA512; break;
-+  #endif
-+	default:
-+		return NULL;
-+	}
-+	mbedtls_x509write_csr_set_md_alg((mbedtls_x509write_csr *)csr, sig_md);
-+
-+  #if 0
-+	unsigned char key_usage = MBEDTLS_X509_KU_DIGITAL_SIGNATURE
-+	                        | MBEDTLS_X509_KU_KEY_CERT_SIGN;
-+	if (mbedtls_x509write_csr_set_key_usage((mbedtls_x509write_csr *)csr,
-+	                                        key_usage))
-+		return NULL;
-+  #endif
-+
-+  #if 0
-+	unsigned char ns_cert_type = MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT
-+	                           | MBEDTLS_X509_NS_CERT_TYPE_EMAIL;
-+	if (mbedtls_x509write_csr_set_ns_cert_type((mbedtls_x509write_csr *)csr,
-+	                                           ns_cert_type))
-+		return NULL;
-+  #endif
-+
-+  #if 0
-+	/* mbedtls does not currently provide way to set an attribute in a CSR:
-+	 *   https://github.com/Mbed-TLS/mbedtls/issues/4886
-+	 * XXX: hwsim dpp_enterprise test fails due to this limitation.
-+	 *
-+	 * Current usage of this function is solely by dpp_build_csr(),
-+	 * so as a kludge, might consider custom (struct crypto_csr *)
-+	 * containing (mbedtls_x509write_csr *) and a list of attributes
-+	 * (i.e. challengePassword).  Might have to totally reimplement
-+	 * mbedtls_x509write_csr_der(); underlying x509write_csr_der_internal()
-+	 * handles signing the CSR.  (This is more work that appending an
-+	 * Attributes section to end of CSR and adjusting ASN.1 length of CSR.)
-+	 */
-+  #endif
-+
-+	unsigned char buf[4096]; /* XXX: large enough?  too large? */
-+	int len = mbedtls_x509write_csr_der((mbedtls_x509write_csr *)csr,
-+	                                    buf, sizeof(buf),
-+	                                    mbedtls_ctr_drbg_random,
-+	                                    crypto_mbedtls_ctr_drbg());
-+	if (len < 0)
-+		return NULL;
-+	/*  Note: data is written at the end of the buffer! Use the
-+	 *        return value to determine where you should start
-+	 *        using the buffer */
-+	return wpabuf_alloc_copy(buf+sizeof(buf)-len, (size_t)len);
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_CSR */
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_PKCS7
-+
-+#if 0
-+#include <mbedtls/pkcs7.h> /* PKCS7 is not currently supported in mbedtls */
-+#include <mbedtls/pem.h>
-+#endif
-+
-+struct wpabuf * crypto_pkcs7_get_certificates(const struct wpabuf *pkcs7)
-+{
-+	/* PKCS7 is not currently supported in mbedtls */
-+	return NULL;
-+
-+#if 0
-+	/* https://github.com/naynajain/mbedtls-1 branch: development-pkcs7
-+	 * (??? potential future contribution to mbedtls ???) */
-+
-+	/* Note: PKCS7 signature *is not* verified by this function.
-+	 * The function interface does not provide for passing a certificate */
-+
-+	mbedtls_pkcs7 mpkcs7;
-+	mbedtls_pkcs7_init(&mpkcs7);
-+	int pkcs7_type = mbedtls_pkcs7_parse_der(wpabuf_head(pkcs7),
-+	                                         wpabuf_len(pkcs7),
-+	                                         &mpkcs7);
-+	wpabuf *buf = NULL;
-+	do {
-+		if (pkcs7_type < 0)
-+			break;
-+
-+		/* src/common/dpp.c:dpp_parse_cred_dot1x() interested in certs
-+		 * for wpa_supplicant/dpp_supplicant.c:wpas_dpp_add_network()
-+		 * (? are adding certificate headers and footers desired ?) */
-+
-+		/* development-pkcs7 branch does not currently provide
-+		 * additional interfaces to retrieve the parsed data */
-+
-+		mbedtls_x509_crt *certs =
-+		  &mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(certs);
-+		int ncerts =
-+		  mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(no_of_certs);
-+
-+		/* allocate buffer for PEM (base64-encoded DER)
-+		 * plus header, footer, newlines, and some extra */
-+		buf = wpabuf_alloc((wpabuf_len(pkcs7)+2)/3*4 + ncerts*64);
-+		if (buf == NULL)
-+			break;
-+
-+		#define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n"
-+		#define PEM_END_CRT   "-----END CERTIFICATE-----\n"
-+		size_t olen;
-+		for (int i = 0; i < ncerts; ++i) {
-+			int ret = mbedtls_pem_write_buffer(
-+			            PEM_BEGIN_CRT, PEM_END_CRT,
-+			            certs[i].raw.p, certs[i].raw.len,
-+			            wpabuf_mhead(buf, 0), wpabuf_tailroom(buf),
-+			            &olen));
-+			if (ret == 0)
-+				wpabuf_put(buf, olen);
-+			} else {
-+				if (ret == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL)
-+					ret = wpabuf_resize(
-+					        &buf,olen-wpabuf_tailroom(buf));
-+				if (ret == 0) {
-+					--i;/*(adjust loop iterator for retry)*/
-+					continue;
-+				}
-+				wpabuf_free(buf);
-+				buf = NULL;
-+				break;
-+			}
-+		}
-+	} while (0);
-+
-+	mbedtls_pkcs7_free(&mpkcs7);
-+	return buf;
-+#endif
-+}
-+
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_PKCS7 */
-+
-+
-+#ifdef MBEDTLS_ARC4_C
-+#include <mbedtls/arc4.h>
-+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
-+	     u8 *data, size_t data_len)
-+{
-+	mbedtls_arc4_context ctx;
-+	mbedtls_arc4_init(&ctx);
-+	mbedtls_arc4_setup(&ctx, key, keylen);
-+
-+	if (skip) {
-+		/*(prefer [16] on ancient hardware with smaller cache lines)*/
-+		unsigned char skip_buf[64]; /*('skip' is generally small)*/
-+		/*os_memset(skip_buf, 0, sizeof(skip_buf));*/ /*(necessary?)*/
-+		size_t len;
-+		do {
-+			len = skip > sizeof(skip_buf) ? sizeof(skip_buf) : skip;
-+			mbedtls_arc4_crypt(&ctx, len, skip_buf, skip_buf);
-+		} while ((skip -= len));
-+	}
-+
-+	int ret = mbedtls_arc4_crypt(&ctx, data_len, data, data);
-+	mbedtls_arc4_free(&ctx);
-+	return ret;
-+}
-+#endif
-+
-+
-+/* duplicated in tls_mbedtls.c:tls_mbedtls_readfile()*/
-+__attribute_noinline__
-+static int crypto_mbedtls_readfile(const char *path, u8 **buf, size_t *n)
-+{
-+  #if 0 /* #ifdef MBEDTLS_FS_IO */
-+	/*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/
-+	if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) {
-+		wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path);
-+		return -1;
-+	}
-+  #else
-+	/*(use os_readfile() so that we can use os_free()
-+	 *(if we use mbedtls_pk_load_file() above, macros prevent calling free()
-+	 * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free()
-+	 * on buf aborts in tests if buf not allocated via os_malloc())*/
-+	*buf = (u8 *)os_readfile(path, n);
-+	if (!*buf) {
-+		wpa_printf(MSG_ERROR, "error: os_readfile %s", path);
-+		return -1;
-+	}
-+	u8 *buf0 = os_realloc(*buf, *n+1);
-+	if (!buf0) {
-+		bin_clear_free(*buf, *n);
-+		*buf = NULL;
-+		return -1;
-+	}
-+	buf0[(*n)++] = '\0';
-+	*buf = buf0;
-+  #endif
-+	return 0;
-+}
-+
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_RSA
-+#ifdef MBEDTLS_RSA_C
-+
-+#include <mbedtls/pk.h>
-+#include <mbedtls/rsa.h>
-+
-+struct crypto_rsa_key * crypto_rsa_key_read(const char *file, bool private_key)
-+{
-+	/* mbedtls_pk_parse_keyfile() and mbedtls_pk_parse_public_keyfile()
-+	 * require #ifdef MBEDTLS_FS_IO in mbedtls library.  Prefer to use
-+	 * crypto_mbedtls_readfile(), which wraps os_readfile() */
-+	u8 *data;
-+	size_t len;
-+	if (crypto_mbedtls_readfile(file, &data, &len) != 0)
-+		return NULL;
-+
-+	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
-+	if (ctx == NULL) {
-+		bin_clear_free(data, len);
-+		return NULL;
-+	}
-+	mbedtls_pk_init(ctx);
-+
-+	int rc;
-+	rc = (private_key
-+	      ? mbedtls_pk_parse_key(ctx, data, len, NULL, 0
-+	  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+	                            ,mbedtls_ctr_drbg_random,
-+	                             crypto_mbedtls_ctr_drbg()
-+	  #endif
-+	                            )
-+	      : mbedtls_pk_parse_public_key(ctx, data, len)) == 0
-+	    && mbedtls_pk_can_do(ctx, MBEDTLS_PK_RSA);
-+
-+	bin_clear_free(data, len);
-+
-+	if (rc) {
-+		/* use MBEDTLS_RSA_PKCS_V21 padding for RSAES-OAEP */
-+		/* use MBEDTLS_MD_SHA256 for these hostap interfaces */
-+	  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+		/*(no return value in mbedtls 2.x)*/
-+		mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx),
-+		                        MBEDTLS_RSA_PKCS_V21,
-+		                        MBEDTLS_MD_SHA256);
-+	  #else
-+		if (mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx),
-+		                            MBEDTLS_RSA_PKCS_V21,
-+		                            MBEDTLS_MD_SHA256) == 0)
-+	  #endif
-+			return (struct crypto_rsa_key *)ctx;
-+	}
-+
-+	mbedtls_pk_free(ctx);
-+	os_free(ctx);
-+	return NULL;
-+}
-+
-+struct wpabuf * crypto_rsa_oaep_sha256_encrypt(struct crypto_rsa_key *key,
-+					       const struct wpabuf *in)
-+{
-+	mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key);
-+	size_t olen = mbedtls_rsa_get_len(pk_rsa);
-+	struct wpabuf *buf = wpabuf_alloc(olen);
-+	if (buf == NULL)
-+		return NULL;
-+
-+	/* mbedtls_pk_encrypt() takes a few more hops to get to same func */
-+	if (mbedtls_rsa_rsaes_oaep_encrypt(pk_rsa,
-+	                                   mbedtls_ctr_drbg_random,
-+	                                   crypto_mbedtls_ctr_drbg(),
-+	  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+	                                   MBEDTLS_RSA_PRIVATE,
-+	  #endif
-+	                                   NULL, 0,
-+	                                   wpabuf_len(in), wpabuf_head(in),
-+	                                   wpabuf_put(buf, olen)) == 0) {
-+		return buf;
-+	}
-+
-+	wpabuf_clear_free(buf);
-+	return NULL;
-+}
-+
-+struct wpabuf * crypto_rsa_oaep_sha256_decrypt(struct crypto_rsa_key *key,
-+					       const struct wpabuf *in)
-+{
-+	mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key);
-+	size_t olen = mbedtls_rsa_get_len(pk_rsa);
-+	struct wpabuf *buf = wpabuf_alloc(olen);
-+	if (buf == NULL)
-+		return NULL;
-+
-+	/* mbedtls_pk_decrypt() takes a few more hops to get to same func */
-+	if (mbedtls_rsa_rsaes_oaep_decrypt(pk_rsa,
-+	                                   mbedtls_ctr_drbg_random,
-+	                                   crypto_mbedtls_ctr_drbg(),
-+	  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+	                                   MBEDTLS_RSA_PUBLIC,
-+	  #endif
-+	                                   NULL, 0, &olen, wpabuf_head(in),
-+	                                   wpabuf_mhead(buf), olen) == 0) {
-+		wpabuf_put(buf, olen);
-+		return buf;
-+	}
-+
-+	wpabuf_clear_free(buf);
-+	return NULL;
-+}
-+
-+void crypto_rsa_key_free(struct crypto_rsa_key *key)
-+{
-+	mbedtls_pk_free((mbedtls_pk_context *)key);
-+	os_free(key);
-+}
-+
-+#endif /* MBEDTLS_RSA_C */
-+#endif /* CRYPTO_MBEDTLS_CRYPTO_RSA */
-+
-+#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE
-+
-+struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id,
-+			       enum hpke_kdf_id kdf_id,
-+			       enum hpke_aead_id aead_id,
-+			       struct crypto_ec_key *peer_pub,
-+			       const u8 *info, size_t info_len,
-+			       const u8 *aad, size_t aad_len,
-+			       const u8 *pt, size_t pt_len)
-+{
-+	/* not yet implemented */
-+	return NULL;
-+}
-+
-+struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
-+			       enum hpke_kdf_id kdf_id,
-+			       enum hpke_aead_id aead_id,
-+			       struct crypto_ec_key *own_priv,
-+			       const u8 *info, size_t info_len,
-+			       const u8 *aad, size_t aad_len,
-+			       const u8 *enc_ct, size_t enc_ct_len)
-+{
-+	/* not yet implemented */
-+	return NULL;
-+}
-+
-+#endif
-diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
-index ffeddbadd..07c36d850 100644
---- a/src/crypto/crypto_module_tests.c
-+++ b/src/crypto/crypto_module_tests.c
-@@ -2470,6 +2470,139 @@ static int test_hpke(void)
- }
- 
- 
-+static int test_ecc(void)
-+{
-+#ifdef CONFIG_ECC
-+#ifndef CONFIG_TLS_INTERNAL
-+#ifndef CONFIG_TLS_GNUTLS
-+#if defined(CONFIG_TLS_MBEDTLS) \
-+ || defined(CONFIG_TLS_OPENSSL) \
-+ || defined(CONFIG_TLS_WOLFSSL)
-+	wpa_printf(MSG_INFO, "Testing ECC");
-+	/* Note: some tests below are valid on supported Short Weierstrass
-+	 * curves, but not on Montgomery curves (e.g. IKE groups 31 and 32)
-+	 * (e.g. deriving and comparing y^2 test below not valid on Montgomery)
-+	 */
-+#ifdef CONFIG_TLS_MBEDTLS
-+	const int grps[] = {19, 20, 21, 25, 26, 28};
-+#endif
-+#ifdef CONFIG_TLS_OPENSSL
-+	const int grps[] = {19, 20, 21, 26};
-+#endif
-+#ifdef CONFIG_TLS_WOLFSSL
-+	const int grps[] = {19, 20, 21, 26};
-+#endif
-+	uint32_t i;
-+	struct crypto_ec *e = NULL;
-+	struct crypto_ec_point *p = NULL, *q = NULL;
-+	struct crypto_bignum *x = NULL, *y = NULL;
-+#ifdef CONFIG_DPP
-+	u8 bin[4096];
-+#endif
-+	for (i = 0; i < ARRAY_SIZE(grps); ++i) {
-+		e = crypto_ec_init(grps[i]);
-+		if (e == NULL
-+		    || crypto_ec_prime_len(e) == 0
-+		    || crypto_ec_prime_len_bits(e) == 0
-+		    || crypto_ec_order_len(e) == 0
-+		    || crypto_ec_get_prime(e) == NULL
-+		    || crypto_ec_get_order(e) == NULL
-+		    || crypto_ec_get_a(e) == NULL
-+		    || crypto_ec_get_b(e) == NULL
-+		    || crypto_ec_get_generator(e) == NULL) {
-+			break;
-+		}
-+#ifdef CONFIG_DPP
-+		struct crypto_ec_key *key = crypto_ec_key_gen(grps[i]);
-+		if (key == NULL)
-+			break;
-+		p = crypto_ec_key_get_public_key(key);
-+		q = crypto_ec_key_get_public_key(key);
-+		crypto_ec_key_deinit(key);
-+		if (p == NULL || q == NULL)
-+			break;
-+		if (!crypto_ec_point_is_on_curve(e, p))
-+			break;
-+
-+		/* inverted point should not match original;
-+		 * double-invert should match */
-+		if (crypto_ec_point_invert(e, q) != 0
-+		    || crypto_ec_point_cmp(e, p, q) == 0
-+		    || crypto_ec_point_invert(e, q) != 0
-+		    || crypto_ec_point_cmp(e, p, q) != 0) {
-+			break;
-+		}
-+
-+		/* crypto_ec_point_to_bin() and crypto_ec_point_from_bin()
-+		 * imbalanced interfaces? */
-+		size_t prime_len = crypto_ec_prime_len(e);
-+		if (prime_len * 2 > sizeof(bin))
-+			break;
-+		if (crypto_ec_point_to_bin(e, p, bin, bin+prime_len) != 0)
-+			break;
-+		struct crypto_ec_point *tmp = crypto_ec_point_from_bin(e, bin);
-+		if (tmp == NULL)
-+			break;
-+		if (crypto_ec_point_cmp(e, p, tmp) != 0) {
-+			crypto_ec_point_deinit(tmp, 0);
-+			break;
-+		}
-+		crypto_ec_point_deinit(tmp, 0);
-+
-+		x = crypto_bignum_init();
-+		y = crypto_bignum_init_set(bin+prime_len, prime_len);
-+		if (x == NULL || y == NULL || crypto_ec_point_x(e, p, x) != 0)
-+			break;
-+		struct crypto_bignum *y2 = crypto_ec_point_compute_y_sqr(e, x);
-+		if (y2 == NULL)
-+			break;
-+		if (crypto_bignum_sqrmod(y, crypto_ec_get_prime(e), y) != 0
-+		    || crypto_bignum_cmp(y, y2) != 0) {
-+			crypto_bignum_deinit(y2, 0);
-+			break;
-+		}
-+		crypto_bignum_deinit(y2, 0);
-+		crypto_bignum_deinit(x, 0);
-+		crypto_bignum_deinit(y, 0);
-+		x = NULL;
-+		y = NULL;
-+
-+		x = crypto_bignum_init();
-+		if (x == NULL)
-+			break;
-+		if (crypto_bignum_rand(x, crypto_ec_get_prime(e)) != 0)
-+			break;
-+		crypto_bignum_deinit(x, 0);
-+		x = NULL;
-+
-+		crypto_ec_point_deinit(p, 0);
-+		p = NULL;
-+		crypto_ec_point_deinit(q, 0);
-+		q = NULL;
-+#endif /* CONFIG_DPP */
-+		crypto_ec_deinit(e);
-+		e = NULL;
-+	}
-+	if (i != ARRAY_SIZE(grps)) {
-+		crypto_bignum_deinit(x, 0);
-+		crypto_bignum_deinit(y, 0);
-+		crypto_ec_point_deinit(p, 0);
-+		crypto_ec_point_deinit(q, 0);
-+		crypto_ec_deinit(e);
-+		wpa_printf(MSG_INFO,
-+		           "ECC test case failed tls_id:%d", grps[i]);
-+		return -1;
-+	}
-+
-+	wpa_printf(MSG_INFO, "ECC test cases passed");
-+#endif
-+#endif /* !CONFIG_TLS_GNUTLS */
-+#endif /* !CONFIG_TLS_INTERNAL */
-+#endif /* CONFIG_ECC */
-+	return 0;
-+}
-+
-+
- static int test_ms_funcs(void)
- {
- #ifndef CONFIG_FIPS
-@@ -2591,6 +2724,7 @@ int crypto_module_tests(void)
- 	    test_fips186_2_prf() ||
- 	    test_extract_expand_hkdf() ||
- 	    test_hpke() ||
-+	    test_ecc() ||
- 	    test_ms_funcs())
- 		ret = -1;
- 
-diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c
-index 269174321..a554fd8a8 100644
---- a/src/crypto/crypto_wolfssl.c
-+++ b/src/crypto/crypto_wolfssl.c
-@@ -1633,6 +1633,7 @@ struct crypto_ec {
- #ifdef CONFIG_DPP
- 	ecc_point *g; /* Only used in DPP for now */
- #endif /* CONFIG_DPP */
-+	WC_RNG rng;
- 	mp_int a;
- 	mp_int prime;
- 	mp_int order;
-@@ -1666,6 +1667,20 @@ struct crypto_ec * crypto_ec_init(int group)
- 	e->key = ecc_key_init();
- 	if (!e->key) {
- 		LOG_WOLF_ERROR_FUNC_NULL(ecc_key_init);
-+
-+	if (wc_ecc_init(e->key) != 0 ||
-+	    wc_InitRng(e->rng) != 0 ||
-+	    wc_ecc_set_rng(e->key, e->rng) != 0 ||
-+	    wc_ecc_set_curve(e->key, 0, curve_id) != 0 ||
-+	    mp_init(e->a) != MP_OKAY ||
-+	    mp_init(e->prime) != MP_OKAY ||
-+	    mp_init(e->order) != MP_OKAY ||
-+	    mp_init(e->b) != MP_OKAY ||
-+	    mp_read_radix(e->a, e->key.dp->Af, 16) != MP_OKAY ||
-+	    mp_read_radix(e->b, e->key.dp->Bf, 16) != MP_OKAY ||
-+	    mp_read_radix(e->prime, e->key.dp->prime, 16) != MP_OKAY ||
-+	    mp_read_radix(e->order, e->key.dp->order, 16) != MP_OKAY ||
-+	    mp_montgomery_setup(e->prime, e->mont_b) != MP_OKAY)
- 		goto done;
- 	}
- 
-@@ -1764,6 +1779,9 @@ void crypto_ec_deinit(struct crypto_ec* e)
- #endif /* CONFIG_DPP */
- 	if (e->own_key)
- 		ecc_key_deinit(e->key);
-+
-+	wc_FreeRng(e->rng);
-+	wc_ecc_free(e->key);
- 	os_free(e);
- }
- 
-diff --git a/src/crypto/tls_mbedtls.c b/src/crypto/tls_mbedtls.c
-new file mode 100644
-index 000000000..2580a3a27
---- /dev/null
-+++ b/src/crypto/tls_mbedtls.c
-@@ -0,0 +1,3313 @@
-+/*
-+ * SSL/TLS interface functions for mbed TLS
-+ *
-+ * SPDX-FileCopyrightText: 2022 Glenn Strauss <gstrauss@gluelogic.com>
-+ * SPDX-License-Identifier: BSD-3-Clause
-+ *
-+ * This software may be distributed under the terms of the BSD license.
-+ * See README for more details.
-+ *
-+ * template:  src/crypto/tls_none.c
-+ * reference: src/crypto/tls_*.c
-+ *
-+ * Known Limitations:
-+ * - no TLSv1.3 (not available in mbedtls 2.x; experimental in mbedtls 3.x)
-+ * - no OCSP (not yet available in mbedtls)
-+ * - mbedtls does not support all certificate encodings used by hwsim tests
-+ *   PCKS#5 v1.5
-+ *   PCKS#12
-+ *   DH DSA
-+ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
-+ * - mbedtls does not currently provide way to set an attribute in a CSR
-+ *     https://github.com/Mbed-TLS/mbedtls/issues/4886
-+ *   so tests/hwsim dpp_enterprise tests fail
-+ * - DPP2 not supported
-+ *   PKCS#7 parsing is not supported in mbedtls
-+ *   See crypto_mbedtls.c:crypto_pkcs7_get_certificates() comments
-+ * - DPP3 not supported
-+ *   hpke_base_seal() and hpke_base_seal() not implemented in crypto_mbedtls.c
-+ *
-+ * Status:
-+ * - code written to be compatible with mbedtls 2.x and mbedtls 3.x
-+ *   (currently requires mbedtls >= 2.27.0 for mbedtls_mpi_random())
-+ *   (currently requires mbedtls >= 2.18.0 for mbedtls_ssl_tls_prf())
-+ * - builds with tests/build/build-wpa_supplicant-mbedtls.config
-+ * - passes all tests/ crypto module tests (incomplete coverage)
-+ *   ($ cd tests; make clean; make -j 4 run-tests CONFIG_TLS=mbedtls)
-+ * - passes almost all tests/hwsim tests
-+ *   (hwsim tests skipped for missing features)
-+ *
-+ * RFE:
-+ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
-+ * - client/server session resumption, and/or save client session ticket
-+ */
-+
-+#include "includes.h"
-+#include "common.h"
-+
-+#include <mbedtls/version.h>
-+#include <mbedtls/ctr_drbg.h>
-+#include <mbedtls/error.h>
-+#include <mbedtls/oid.h>
-+#include <mbedtls/pem.h>
-+#include <mbedtls/platform.h> /* mbedtls_calloc() mbedtls_free() */
-+#include <mbedtls/platform_util.h> /* mbedtls_platform_zeroize() */
-+#include <mbedtls/ssl.h>
-+#include <mbedtls/ssl_ticket.h>
-+#include <mbedtls/x509.h>
-+#include <mbedtls/x509_crt.h>
-+
-+#if MBEDTLS_VERSION_NUMBER >= 0x02040000 /* mbedtls 2.4.0 */
-+#include <mbedtls/net_sockets.h>
-+#else
-+#include <mbedtls/net.h>
-+#endif
-+
-+#ifndef MBEDTLS_PRIVATE
-+#define MBEDTLS_PRIVATE(x) x
-+#endif
-+
-+#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
-+#define mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl) \
-+        ((ssl)->MBEDTLS_PRIVATE(session) \
-+        ?(ssl)->MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(ciphersuite) \
-+        : 0)
-+#define mbedtls_ssl_ciphersuite_get_name(info) \
-+        (info)->MBEDTLS_PRIVATE(name)
-+#endif
-+
-+#include "crypto.h"     /* sha256_vector() */
-+#include "tls.h"
-+
-+#ifndef SHA256_DIGEST_LENGTH
-+#define SHA256_DIGEST_LENGTH 32
-+#endif
-+
-+#ifndef MBEDTLS_EXPKEY_FIXED_SECRET_LEN
-+#define MBEDTLS_EXPKEY_FIXED_SECRET_LEN 48
-+#endif
-+
-+#ifndef MBEDTLS_EXPKEY_RAND_LEN
-+#define MBEDTLS_EXPKEY_RAND_LEN 32
-+#endif
-+
-+#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+static mbedtls_ssl_export_keys_t tls_connection_export_keys_cb;
-+#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+static mbedtls_ssl_export_keys_ext_t tls_connection_export_keys_cb;
-+#else /*(not implemented; return error)*/
-+#define mbedtls_ssl_tls_prf(a,b,c,d,e,f,g,h) (-1)
-+typedef mbedtls_tls_prf_types int;
-+#endif
-+
-+
-+/* hostapd/wpa_supplicant provides forced_memzero(),
-+ * but prefer mbedtls_platform_zeroize() */
-+#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz)
-+
-+
-+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
-+ || defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
-+#ifdef MBEDTLS_SSL_SESSION_TICKETS
-+#ifdef MBEDTLS_SSL_TICKET_C
-+#define TLS_MBEDTLS_SESSION_TICKETS
-+#if defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
-+#define TLS_MBEDTLS_EAP_TEAP
-+#endif
-+#if !defined(CONFIG_FIPS) /* EAP-FAST keys cannot be exported in FIPS mode */
-+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
-+#define TLS_MBEDTLS_EAP_FAST
-+#endif
-+#endif
-+#endif
-+#endif
-+#endif
-+
-+
-+struct tls_conf {
-+	mbedtls_ssl_config conf;
-+
-+	unsigned int verify_peer:1;
-+	unsigned int verify_depth0_only:1;
-+	unsigned int check_crl:2;           /*(needs :2 bits for 0, 1, 2)*/
-+	unsigned int check_crl_strict:1;    /*(needs :1 bit  for 0, 1)*/
-+	unsigned int ca_cert_probe:1;
-+	unsigned int has_ca_cert:1;
-+	unsigned int has_client_cert:1;
-+	unsigned int has_private_key:1;
-+	unsigned int suiteb128:1;
-+	unsigned int suiteb192:1;
-+	mbedtls_x509_crl *crl;
-+	mbedtls_x509_crt ca_cert;
-+	mbedtls_x509_crt client_cert;
-+	mbedtls_pk_context private_key;
-+
-+	uint32_t refcnt;
-+
-+	unsigned int flags;
-+	char *subject_match;
-+	char *altsubject_match;
-+	char *suffix_match;
-+	char *domain_match;
-+	char *check_cert_subject;
-+	u8 ca_cert_hash[SHA256_DIGEST_LENGTH];
-+
-+	int *ciphersuites;  /* list of ciphersuite ids for mbedtls_ssl_config */
-+#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
-+	mbedtls_ecp_group_id *curves;
-+#else
-+	uint16_t *curves;   /* list of curve ids for mbedtls_ssl_config */
-+#endif
-+};
-+
-+
-+struct tls_global {
-+	struct tls_conf *tls_conf;
-+	char *ocsp_stapling_response;
-+	mbedtls_ctr_drbg_context *ctr_drbg; /*(see crypto_mbedtls.c)*/
-+  #ifdef MBEDTLS_SSL_SESSION_TICKETS
-+	mbedtls_ssl_ticket_context ticket_ctx;
-+  #endif
-+	char *ca_cert_file;
-+	struct os_reltime crl_reload_previous;
-+	unsigned int crl_reload_interval;
-+	uint32_t refcnt;
-+	struct tls_config init_conf;
-+};
-+
-+static struct tls_global tls_ctx_global;
-+
-+
-+struct tls_connection {
-+	struct tls_conf *tls_conf;
-+	struct wpabuf *push_buf;
-+	struct wpabuf *pull_buf;
-+	size_t pull_buf_offset;
-+
-+	unsigned int established:1;
-+	unsigned int resumed:1;
-+	unsigned int verify_peer:1;
-+	unsigned int is_server:1;
-+
-+	mbedtls_ssl_context ssl;
-+
-+	mbedtls_tls_prf_types tls_prf_type;
-+	size_t expkey_keyblock_size;
-+	size_t expkey_secret_len;
-+  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
-+	unsigned char expkey_secret[MBEDTLS_EXPKEY_FIXED_SECRET_LEN];
-+  #else
-+	unsigned char expkey_secret[MBEDTLS_MD_MAX_SIZE];
-+  #endif
-+	unsigned char expkey_randbytes[MBEDTLS_EXPKEY_RAND_LEN*2];
-+
-+	int read_alerts, write_alerts, failed;
-+
-+  #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+	tls_session_ticket_cb session_ticket_cb;
-+	void *session_ticket_cb_ctx;
-+	unsigned char *clienthello_session_ticket;
-+	size_t clienthello_session_ticket_len;
-+  #endif
-+	char *peer_subject; /* peer subject info for authenticated peer */
-+	struct wpabuf *success_data;
-+};
-+
-+
-+#ifndef __has_attribute
-+#define __has_attribute(x) 0
-+#endif
-+
-+#ifndef __GNUC_PREREQ
-+#define __GNUC_PREREQ(maj,min) 0
-+#endif
-+
-+#ifndef __attribute_cold__
-+#if __has_attribute(cold) \
-+ || __GNUC_PREREQ(4,3)
-+#define __attribute_cold__  __attribute__((__cold__))
-+#else
-+#define __attribute_cold__
-+#endif
-+#endif
-+
-+#ifndef __attribute_noinline__
-+#if __has_attribute(noinline) \
-+ || __GNUC_PREREQ(3,1)
-+#define __attribute_noinline__  __attribute__((__noinline__))
-+#else
-+#define __attribute_noinline__
-+#endif
-+#endif
-+
-+
-+__attribute_cold__
-+__attribute_noinline__
-+static void emsg(int level, const char * const msg)
-+{
-+	wpa_printf(level, "MTLS: %s", msg);
-+}
-+
-+
-+__attribute_cold__
-+__attribute_noinline__
-+static void emsgrc(int level, const char * const msg, int rc)
-+{
-+  #ifdef MBEDTLS_ERROR_C
-+	/* error logging convenience function that decodes mbedtls result codes */
-+	char buf[256];
-+	mbedtls_strerror(rc, buf, sizeof(buf));
-+	wpa_printf(level, "MTLS: %s: %s (-0x%04x)", msg, buf, -rc);
-+  #else
-+	wpa_printf(level, "MTLS: %s: (-0x%04x)", msg, -rc);
-+  #endif
-+}
-+
-+
-+#define elog(rc, msg) emsgrc(MSG_ERROR, (msg), (rc))
-+#define ilog(rc, msg) emsgrc(MSG_INFO,  (msg), (rc))
-+
-+
-+struct tls_conf * tls_conf_init(void *tls_ctx)
-+{
-+	struct tls_conf *tls_conf = os_zalloc(sizeof(*tls_conf));
-+	if (tls_conf == NULL)
-+		return NULL;
-+	tls_conf->refcnt = 1;
-+
-+	mbedtls_ssl_config_init(&tls_conf->conf);
-+	mbedtls_ssl_conf_rng(&tls_conf->conf,
-+			     mbedtls_ctr_drbg_random, tls_ctx_global.ctr_drbg);
-+	mbedtls_x509_crt_init(&tls_conf->ca_cert);
-+	mbedtls_x509_crt_init(&tls_conf->client_cert);
-+	mbedtls_pk_init(&tls_conf->private_key);
-+
-+	return tls_conf;
-+}
-+
-+
-+void tls_conf_deinit(struct tls_conf *tls_conf)
-+{
-+	if (tls_conf == NULL || --tls_conf->refcnt != 0)
-+		return;
-+
-+	mbedtls_x509_crt_free(&tls_conf->ca_cert);
-+	mbedtls_x509_crt_free(&tls_conf->client_cert);
-+	if (tls_conf->crl) {
-+		mbedtls_x509_crl_free(tls_conf->crl);
-+		os_free(tls_conf->crl);
-+	}
-+	mbedtls_pk_free(&tls_conf->private_key);
-+	mbedtls_ssl_config_free(&tls_conf->conf);
-+	os_free(tls_conf->curves);
-+	os_free(tls_conf->ciphersuites);
-+	os_free(tls_conf->subject_match);
-+	os_free(tls_conf->altsubject_match);
-+	os_free(tls_conf->suffix_match);
-+	os_free(tls_conf->domain_match);
-+	os_free(tls_conf->check_cert_subject);
-+	os_free(tls_conf);
-+}
-+
-+
-+mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/
-+
-+__attribute_cold__
-+void * tls_init(const struct tls_config *conf)
-+{
-+	/* RFE: review struct tls_config *conf (different from tls_conf) */
-+
-+	if (++tls_ctx_global.refcnt > 1)
-+		return &tls_ctx_global;
-+
-+	tls_ctx_global.ctr_drbg = crypto_mbedtls_ctr_drbg();
-+  #ifdef MBEDTLS_SSL_SESSION_TICKETS
-+	mbedtls_ssl_ticket_init(&tls_ctx_global.ticket_ctx);
-+	mbedtls_ssl_ticket_setup(&tls_ctx_global.ticket_ctx,
-+	                         mbedtls_ctr_drbg_random,
-+	                         tls_ctx_global.ctr_drbg,
-+	                         MBEDTLS_CIPHER_AES_256_GCM,
-+	                         43200); /* ticket timeout: 12 hours */
-+  #endif
-+	/* copy struct for future use */
-+	tls_ctx_global.init_conf = *conf;
-+	if (conf->openssl_ciphers)
-+		tls_ctx_global.init_conf.openssl_ciphers =
-+		  os_strdup(conf->openssl_ciphers);
-+
-+	tls_ctx_global.crl_reload_interval = conf->crl_reload_interval;
-+	os_get_reltime(&tls_ctx_global.crl_reload_previous);
-+
-+	return &tls_ctx_global;
-+}
-+
-+
-+__attribute_cold__
-+void tls_deinit(void *tls_ctx)
-+{
-+	if (tls_ctx == NULL || --tls_ctx_global.refcnt != 0)
-+		return;
-+
-+	tls_conf_deinit(tls_ctx_global.tls_conf);
-+	os_free(tls_ctx_global.ca_cert_file);
-+	os_free(tls_ctx_global.ocsp_stapling_response);
-+	char *openssl_ciphers; /*(allocated in tls_init())*/
-+	*(const char **)&openssl_ciphers =
-+	  tls_ctx_global.init_conf.openssl_ciphers;
-+	os_free(openssl_ciphers);
-+  #ifdef MBEDTLS_SSL_SESSION_TICKETS
-+	mbedtls_ssl_ticket_free(&tls_ctx_global.ticket_ctx);
-+  #endif
-+	os_memset(&tls_ctx_global, 0, sizeof(tls_ctx_global));
-+}
-+
-+
-+int tls_get_errors(void *tls_ctx)
-+{
-+	return 0;
-+}
-+
-+
-+static void tls_connection_deinit_expkey(struct tls_connection *conn)
-+{
-+	conn->tls_prf_type = 0; /* MBEDTLS_SSL_TLS_PRF_NONE; */
-+	conn->expkey_keyblock_size = 0;
-+	conn->expkey_secret_len = 0;
-+	forced_memzero(conn->expkey_secret, sizeof(conn->expkey_secret));
-+	forced_memzero(conn->expkey_randbytes, sizeof(conn->expkey_randbytes));
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_SESSION_TICKETS
-+void tls_connection_deinit_clienthello_session_ticket(struct tls_connection *conn)
-+{
-+	if (conn->clienthello_session_ticket) {
-+		mbedtls_platform_zeroize(conn->clienthello_session_ticket,
-+		                         conn->clienthello_session_ticket_len);
-+		mbedtls_free(conn->clienthello_session_ticket);
-+		conn->clienthello_session_ticket = NULL;
-+		conn->clienthello_session_ticket_len = 0;
-+	}
-+}
-+#endif
-+
-+
-+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
-+{
-+	if (conn == NULL)
-+		return;
-+
-+  #if 0 /*(good intention, but never sent since we destroy self below)*/
-+	if (conn->established)
-+		mbedtls_ssl_close_notify(&conn->ssl);
-+  #endif
-+
-+	if (conn->tls_prf_type)
-+		tls_connection_deinit_expkey(conn);
-+
-+  #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+	if (conn->clienthello_session_ticket)
-+		tls_connection_deinit_clienthello_session_ticket(conn);
-+  #endif
-+
-+	os_free(conn->peer_subject);
-+	wpabuf_free(conn->success_data);
-+	wpabuf_free(conn->push_buf);
-+	wpabuf_free(conn->pull_buf);
-+	mbedtls_ssl_free(&conn->ssl);
-+	tls_conf_deinit(conn->tls_conf);
-+	os_free(conn);
-+}
-+
-+
-+static void tls_mbedtls_refresh_crl(void);
-+static int tls_mbedtls_ssl_setup(struct tls_connection *conn);
-+
-+struct tls_connection * tls_connection_init(void *tls_ctx)
-+{
-+	struct tls_connection *conn = os_zalloc(sizeof(*conn));
-+	if (conn == NULL)
-+		return NULL;
-+
-+	mbedtls_ssl_init(&conn->ssl);
-+
-+	conn->tls_conf = tls_ctx_global.tls_conf; /*(inherit global conf, if set)*/
-+	if (conn->tls_conf) {
-+		++conn->tls_conf->refcnt;
-+		/* check for CRL refresh if inheriting from global config */
-+		tls_mbedtls_refresh_crl();
-+
-+		conn->verify_peer = conn->tls_conf->verify_peer;
-+		if (tls_mbedtls_ssl_setup(conn) != 0) {
-+			tls_connection_deinit(&tls_ctx_global, conn);
-+			return NULL;
-+		}
-+	}
-+
-+	return conn;
-+}
-+
-+
-+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
-+{
-+	return conn ? conn->established : 0;
-+}
-+
-+
-+__attribute_noinline__
-+char * tls_mbedtls_peer_serial_num(const mbedtls_x509_crt *crt, char *serial_num, size_t len)
-+{
-+	/* mbedtls_x509_serial_gets() inefficiently formats to hex separated by
-+	 * colons, so generate the hex serial number here.  The func
-+	 * wpa_snprintf_hex_uppercase() is similarly inefficient. */
-+	size_t i = 0; /* skip leading 0's per Distinguished Encoding Rules (DER) */
-+	while (i < crt->serial.len && crt->serial.p[i] == 0) ++i;
-+	if (i == crt->serial.len) --i;
-+
-+	const unsigned char *s = crt->serial.p + i;
-+	const size_t e = (crt->serial.len - i) * 2;
-+	if (e >= len)
-+		return NULL;
-+  #if 0
-+	wpa_snprintf_hex_uppercase(serial_num, len, s, crt->serial.len-i);
-+  #else
-+	for (i = 0; i < e; i+=2, ++s) {
-+		serial_num[i+0] = "0123456789ABCDEF"[(*s >>  4)];
-+		serial_num[i+1] = "0123456789ABCDEF"[(*s & 0xF)];
-+	}
-+	serial_num[e] = '\0';
-+  #endif
-+	return serial_num;
-+}
-+
-+
-+char * tls_connection_peer_serial_num(void *tls_ctx,
-+				      struct tls_connection *conn)
-+{
-+	const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&conn->ssl);
-+	if (crt == NULL)
-+		return NULL;
-+	size_t len = crt->serial.len * 2 + 1;
-+	char *serial_num = os_malloc(len);
-+	if (!serial_num)
-+		return NULL;
-+	return tls_mbedtls_peer_serial_num(crt, serial_num, len);
-+}
-+
-+
-+static void tls_pull_buf_reset(struct tls_connection *conn);
-+
-+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
-+{
-+	/* Note: this function called from eap_peer_tls_reauth_init()
-+	 * for session resumption, not for connection shutdown */
-+
-+	if (conn == NULL)
-+		return -1;
-+
-+	tls_pull_buf_reset(conn);
-+	wpabuf_free(conn->push_buf);
-+	conn->push_buf = NULL;
-+	conn->established = 0;
-+	conn->resumed = 0;
-+	if (conn->tls_prf_type)
-+		tls_connection_deinit_expkey(conn);
-+
-+	/* RFE: prepare for session resumption? (see doc in crypto/tls.h) */
-+
-+	return mbedtls_ssl_session_reset(&conn->ssl);
-+}
-+
-+
-+static int tls_wpabuf_resize_put_data(struct wpabuf **buf,
-+                                      const unsigned char *data, size_t dlen)
-+{
-+	if (wpabuf_resize(buf, dlen) < 0)
-+		return 0;
-+	wpabuf_put_data(*buf, data, dlen);
-+	return 1;
-+}
-+
-+
-+static int tls_pull_buf_append(struct tls_connection *conn,
-+                               const struct wpabuf *in_data)
-+{
-+	/*(interface does not lend itself to move semantics)*/
-+	return tls_wpabuf_resize_put_data(&conn->pull_buf,
-+	                                  wpabuf_head(in_data),
-+	                                  wpabuf_len(in_data));
-+}
-+
-+
-+static void tls_pull_buf_reset(struct tls_connection *conn)
-+{
-+	/*(future: might consider reusing conn->pull_buf)*/
-+	wpabuf_free(conn->pull_buf);
-+	conn->pull_buf = NULL;
-+	conn->pull_buf_offset = 0;
-+}
-+
-+
-+__attribute_cold__
-+static void tls_pull_buf_discard(struct tls_connection *conn, const char *func)
-+{
-+	size_t discard = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
-+	if (discard)
-+		wpa_printf(MSG_DEBUG,
-+			   "%s - %zu bytes remaining in pull_buf; discarding",
-+			   func, discard);
-+	tls_pull_buf_reset(conn);
-+}
-+
-+
-+static int tls_pull_func(void *ptr, unsigned char *buf, size_t len)
-+{
-+	struct tls_connection *conn = (struct tls_connection *) ptr;
-+	if (conn->pull_buf == NULL)
-+		return MBEDTLS_ERR_SSL_WANT_READ;
-+	const size_t dlen = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
-+	if (dlen == 0)
-+		return MBEDTLS_ERR_SSL_WANT_READ;
-+
-+	if (len > dlen)
-+		len = dlen;
-+	os_memcpy(buf, wpabuf_head(conn->pull_buf)+conn->pull_buf_offset, len);
-+
-+	if (len == dlen) {
-+		tls_pull_buf_reset(conn);
-+		/*wpa_printf(MSG_DEBUG, "%s - emptied pull_buf", __func__);*/
-+	}
-+	else {
-+		conn->pull_buf_offset += len;
-+		/*wpa_printf(MSG_DEBUG, "%s - %zu bytes remaining in pull_buf",
-+			   __func__, dlen - len);*/
-+	}
-+	return (int)len;
-+}
-+
-+
-+static int tls_push_func(void *ptr, const unsigned char *buf, size_t len)
-+{
-+	struct tls_connection *conn = (struct tls_connection *) ptr;
-+	return tls_wpabuf_resize_put_data(&conn->push_buf, buf, len)
-+	  ? (int)len
-+	  : MBEDTLS_ERR_SSL_ALLOC_FAILED;
-+}
-+
-+
-+static int
-+tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags);
-+
-+
-+static int tls_mbedtls_ssl_setup(struct tls_connection *conn)
-+{
-+  #if 0
-+	/* mbedtls_ssl_setup() must be called only once */
-+	/* If this func might be called multiple times (e.g. via set_params),
-+	 * then we should set a flag in conn that ssl was initialized */
-+	if (conn->ssl_is_init) {
-+		mbedtls_ssl_free(&conn->ssl);
-+		mbedtls_ssl_init(&conn->ssl);
-+	}
-+  #endif
-+
-+	int ret = mbedtls_ssl_setup(&conn->ssl, &conn->tls_conf->conf);
-+	if (ret != 0) {
-+		elog(ret, "mbedtls_ssl_setup");
-+		return -1;
-+	}
-+
-+	mbedtls_ssl_set_bio(&conn->ssl, conn, tls_push_func, tls_pull_func, NULL);
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+	mbedtls_ssl_set_export_keys_cb(
-+	    &conn->ssl, tls_connection_export_keys_cb, conn);
-+  #elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+	mbedtls_ssl_conf_export_keys_ext_cb(
-+	    &conn->tls_conf->conf, tls_connection_export_keys_cb, conn);
-+  #endif
-+	if (conn->verify_peer)
-+		mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
-+
-+	return 0;
-+}
-+
-+
-+static int tls_mbedtls_data_is_pem(const u8 *data)
-+{
-+    return (NULL != os_strstr((char *)data, "-----"));
-+}
-+
-+
-+static void tls_mbedtls_set_allowed_tls_vers(struct tls_conf *tls_conf,
-+                                             mbedtls_ssl_config *conf)
-+{
-+  #if !defined(MBEDTLS_SSL_PROTO_TLS1_3)
-+	tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_3;
-+  #endif
-+
-+	/* unconditionally require TLSv1.2+ for TLS_CONN_SUITEB */
-+	if (tls_conf->flags & TLS_CONN_SUITEB) {
-+		tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_0;
-+		tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_1;
-+	}
-+
-+	const unsigned int flags = tls_conf->flags;
-+
-+	/* attempt to map flags to min and max TLS protocol version */
-+
-+	int min = (flags & TLS_CONN_DISABLE_TLSv1_0)
-+		? (flags & TLS_CONN_DISABLE_TLSv1_1)
-+		? (flags & TLS_CONN_DISABLE_TLSv1_2)
-+		? (flags & TLS_CONN_DISABLE_TLSv1_3)
-+		? 4
-+		: 3
-+		: 2
-+		: 1
-+		: 0;
-+
-+	int max = (flags & TLS_CONN_DISABLE_TLSv1_3)
-+		? (flags & TLS_CONN_DISABLE_TLSv1_2)
-+		? (flags & TLS_CONN_DISABLE_TLSv1_1)
-+		? (flags & TLS_CONN_DISABLE_TLSv1_0)
-+		? -1
-+		: 0
-+		: 1
-+		: 2
-+		: 3;
-+
-+	if ((flags & TLS_CONN_ENABLE_TLSv1_2) && min > 2) min = 2;
-+	if ((flags & TLS_CONN_ENABLE_TLSv1_1) && min > 1) min = 1;
-+	if ((flags & TLS_CONN_ENABLE_TLSv1_0) && min > 0) min = 0;
-+	if (max < min) {
-+		emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
-+		return;
-+	}
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+	/* mbed TLS 3.0.0 removes support for protocols < TLSv1.2 */
-+	if (min < 2 || max < 2) {
-+		emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
-+		if (min < 2) min = 2;
-+		if (max < 2) max = 2;
-+	}
-+  #endif
-+
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
-+	/* MBEDTLS_SSL_VERSION_TLS1_2 = 0x0303 *//*!< (D)TLS 1.2 */
-+	/* MBEDTLS_SSL_VERSION_TLS1_3 = 0x0304 *//*!< (D)TLS 1.3 */
-+	min = (min == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
-+	max = (max == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
-+	mbedtls_ssl_conf_min_tls_version(conf, min);
-+	mbedtls_ssl_conf_max_tls_version(conf, max);
-+  #else
-+   #ifndef MBEDTLS_SSL_MINOR_VERSION_4
-+	if (min == 3) min = 2;
-+	if (max == 3) max = 2;
-+   #endif
-+	/* MBEDTLS_SSL_MINOR_VERSION_0  0 *//*!< SSL v3.0 */
-+	/* MBEDTLS_SSL_MINOR_VERSION_1  1 *//*!< TLS v1.0 */
-+	/* MBEDTLS_SSL_MINOR_VERSION_2  2 *//*!< TLS v1.1 */
-+	/* MBEDTLS_SSL_MINOR_VERSION_3  3 *//*!< TLS v1.2 */
-+	/* MBEDTLS_SSL_MINOR_VERSION_4  4 *//*!< TLS v1.3 */
-+	mbedtls_ssl_conf_min_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, min+1);
-+	mbedtls_ssl_conf_max_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, max+1);
-+  #endif
-+}
-+
-+
-+__attribute_noinline__
-+static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n);
-+
-+
-+static int
-+tls_mbedtls_set_dhparams(struct tls_conf *tls_conf, const char *dh_file)
-+{
-+    size_t len;
-+    u8 *data;
-+    if (tls_mbedtls_readfile(dh_file, &data, &len))
-+        return 0;
-+
-+    /* parse only if DH parameters if in PEM format */
-+    if (tls_mbedtls_data_is_pem(data)
-+        && NULL == os_strstr((char *)data, "-----BEGIN DH PARAMETERS-----")) {
-+        if (os_strstr((char *)data, "-----BEGIN DSA PARAMETERS-----"))
-+            wpa_printf(MSG_WARNING, "DSA parameters not handled (%s)", dh_file);
-+        else
-+            wpa_printf(MSG_WARNING, "unexpected DH param content (%s)",dh_file);
-+        forced_memzero(data, len);
-+        os_free(data);
-+        return 0;
-+    }
-+
-+    /* mbedtls_dhm_parse_dhm() expects "-----BEGIN DH PARAMETERS-----" if PEM */
-+    mbedtls_dhm_context dhm;
-+    mbedtls_dhm_init(&dhm);
-+    int rc = mbedtls_dhm_parse_dhm(&dhm, data, len);
-+    if (0 == rc)
-+        rc = mbedtls_ssl_conf_dh_param_ctx(&tls_conf->conf, &dhm);
-+    if (0 != rc)
-+        elog(rc, dh_file);
-+    mbedtls_dhm_free(&dhm);
-+
-+    forced_memzero(data, len);
-+    os_free(data);
-+    return (0 == rc);
-+}
-+
-+
-+/* reference: lighttpd src/mod_mbedtls.c:mod_mbedtls_ssl_append_curve()
-+ * (same author: gstrauss@gluelogic.com; same license: BSD-3-Clause) */
-+#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
-+static int
-+tls_mbedtls_append_curve (mbedtls_ecp_group_id *ids, int nids, int idsz, const mbedtls_ecp_group_id id)
-+{
-+    if (1 >= idsz - (nids + 1)) {
-+        emsg(MSG_ERROR, "error: too many curves during list expand");
-+        return -1;
-+    }
-+    ids[++nids] = id;
-+    return nids;
-+}
-+
-+
-+static int
-+tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
-+{
-+    mbedtls_ecp_group_id ids[512];
-+    int nids = -1;
-+    const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
-+    const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
-+
-+    for (const char *e = curvelist-1; e; ) {
-+        const char * const n = e+1;
-+        e = os_strchr(n, ':');
-+        size_t len = e ? (size_t)(e - n) : os_strlen(n);
-+        mbedtls_ecp_group_id grp_id = MBEDTLS_ECP_DP_NONE;
-+        switch (len) {
-+          case 5:
-+            if (0 == os_memcmp("P-521", n, 5))
-+                grp_id = MBEDTLS_ECP_DP_SECP521R1;
-+            else if (0 == os_memcmp("P-384", n, 5))
-+                grp_id = MBEDTLS_ECP_DP_SECP384R1;
-+            else if (0 == os_memcmp("P-256", n, 5))
-+                grp_id = MBEDTLS_ECP_DP_SECP256R1;
-+            break;
-+          case 6:
-+            if (0 == os_memcmp("BP-521", n, 6))
-+                grp_id = MBEDTLS_ECP_DP_BP512R1;
-+            else if (0 == os_memcmp("BP-384", n, 6))
-+                grp_id = MBEDTLS_ECP_DP_BP384R1;
-+            else if (0 == os_memcmp("BP-256", n, 6))
-+                grp_id = MBEDTLS_ECP_DP_BP256R1;
-+            break;
-+          default:
-+            break;
-+        }
-+        if (grp_id != MBEDTLS_ECP_DP_NONE) {
-+            nids = tls_mbedtls_append_curve(ids, nids, idsz, grp_id);
-+            if (-1 == nids) return 0;
-+            continue;
-+        }
-+        /* similar to mbedtls_ecp_curve_info_from_name() */
-+        const mbedtls_ecp_curve_info *info;
-+        for (info = curve_info; info->grp_id != MBEDTLS_ECP_DP_NONE; ++info) {
-+            if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
-+                break;
-+        }
-+        if (info->grp_id == MBEDTLS_ECP_DP_NONE) {
-+            wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
-+            return 0;
-+        }
-+
-+        nids = tls_mbedtls_append_curve(ids, nids, idsz, info->grp_id);
-+        if (-1 == nids) return 0;
-+    }
-+
-+    /* mod_openssl configures "prime256v1" if curve list not specified,
-+     * but mbedtls provides a list of supported curves if not explicitly set */
-+    if (-1 == nids) return 1; /* empty list; no-op */
-+
-+    ids[++nids] = MBEDTLS_ECP_DP_NONE; /* terminate list */
-+    ++nids;
-+
-+    /* curves list must be persistent for lifetime of mbedtls_ssl_config */
-+    tls_conf->curves = os_malloc(nids * sizeof(mbedtls_ecp_group_id));
-+    if (tls_conf->curves == NULL)
-+        return 0;
-+    os_memcpy(tls_conf->curves, ids, nids * sizeof(mbedtls_ecp_group_id));
-+
-+    mbedtls_ssl_conf_curves(&tls_conf->conf, tls_conf->curves);
-+    return 1;
-+}
-+#else
-+static int
-+tls_mbedtls_append_curve (uint16_t *ids, int nids, int idsz, const uint16_t id)
-+{
-+    if (1 >= idsz - (nids + 1)) {
-+        emsg(MSG_ERROR, "error: too many curves during list expand");
-+        return -1;
-+    }
-+    ids[++nids] = id;
-+    return nids;
-+}
-+
-+
-+static int
-+tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
-+{
-+    /* TLS Supported Groups (renamed from "EC Named Curve Registry")
-+     * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
-+     */
-+    uint16_t ids[512];
-+    int nids = -1;
-+    const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
-+    const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
-+
-+    for (const char *e = curvelist-1; e; ) {
-+        const char * const n = e+1;
-+        e = os_strchr(n, ':');
-+        size_t len = e ? (size_t)(e - n) : os_strlen(n);
-+        uint16_t tls_id = 0;
-+        switch (len) {
-+          case 5:
-+            if (0 == os_memcmp("P-521", n, 5))
-+                tls_id = 25; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP521R1 */
-+            else if (0 == os_memcmp("P-384", n, 5))
-+                tls_id = 24; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP384R1 */
-+            else if (0 == os_memcmp("P-256", n, 5))
-+                tls_id = 23; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP256R1 */
-+            break;
-+          case 6:
-+            if (0 == os_memcmp("BP-521", n, 6))
-+                tls_id = 28; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP512R1 */
-+            else if (0 == os_memcmp("BP-384", n, 6))
-+                tls_id = 27; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP384R1 */
-+            else if (0 == os_memcmp("BP-256", n, 6))
-+                tls_id = 26; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP256R1 */
-+            break;
-+          default:
-+            break;
-+        }
-+        if (tls_id != 0) {
-+            nids = tls_mbedtls_append_curve(ids, nids, idsz, tls_id);
-+            if (-1 == nids) return 0;
-+            continue;
-+        }
-+        /* similar to mbedtls_ecp_curve_info_from_name() */
-+        const mbedtls_ecp_curve_info *info;
-+        for (info = curve_info; info->tls_id != 0; ++info) {
-+            if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
-+                break;
-+        }
-+        if (info->tls_id == 0) {
-+            wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
-+            return 0;
-+        }
-+
-+        nids = tls_mbedtls_append_curve(ids, nids, idsz, info->tls_id);
-+        if (-1 == nids) return 0;
-+    }
-+
-+    /* mod_openssl configures "prime256v1" if curve list not specified,
-+     * but mbedtls provides a list of supported curves if not explicitly set */
-+    if (-1 == nids) return 1; /* empty list; no-op */
-+
-+    ids[++nids] = 0; /* terminate list */
-+    ++nids;
-+
-+    /* curves list must be persistent for lifetime of mbedtls_ssl_config */
-+    tls_conf->curves = os_malloc(nids * sizeof(uint16_t));
-+    if (tls_conf->curves == NULL)
-+        return 0;
-+    os_memcpy(tls_conf->curves, ids, nids * sizeof(uint16_t));
-+
-+    mbedtls_ssl_conf_groups(&tls_conf->conf, tls_conf->curves);
-+    return 1;
-+}
-+#endif /* MBEDTLS_VERSION_NUMBER >= 0x03010000 */ /* mbedtls 3.1.0 */
-+
-+
-+/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
-+static const int suite_AES_256_ephemeral[] = {
-+    /* All AES-256 ephemeral suites */
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8
-+};
-+
-+/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
-+static const int suite_AES_128_ephemeral[] = {
-+    /* All AES-128 ephemeral suites */
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8
-+};
-+
-+/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
-+/* HIGH cipher list (mapped from openssl list to mbedtls) */
-+static const int suite_HIGH[] = {
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
-+    MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384,
-+    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256,
-+    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384,
-+    MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
-+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
-+    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
-+    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256,
-+    MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256,
-+    MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256,
-+    MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
-+    MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM,
-+    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
-+    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
-+    MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM_8,
-+    MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM,
-+    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
-+    MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
-+    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM_8,
-+    MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256,
-+    MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_RSA_WITH_AES_256_CCM,
-+    MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256,
-+    MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_RSA_WITH_AES_256_CCM_8,
-+    MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256,
-+    MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
-+    MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384,
-+    MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_RSA_WITH_AES_128_CCM,
-+    MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_RSA_WITH_AES_128_CCM_8,
-+    MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256,
-+    MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
-+    MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256,
-+    MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256,
-+    MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
-+    MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384,
-+    MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384,
-+    MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256,
-+    MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256,
-+    MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256,
-+    MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384,
-+    MBEDTLS_TLS_PSK_WITH_AES_256_CCM,
-+    MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384,
-+    MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA,
-+    MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384,
-+    MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8,
-+    MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384,
-+    MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256,
-+    MBEDTLS_TLS_PSK_WITH_AES_128_CCM,
-+    MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256,
-+    MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA,
-+    MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256,
-+    MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8,
-+    MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256
-+};
-+
-+
-+__attribute_noinline__
-+static int
-+tls_mbedtls_append_ciphersuite (int *ids, int nids, int idsz, const int *x, int xsz)
-+{
-+    if (xsz >= idsz - (nids + 1)) {
-+        emsg(MSG_ERROR, "error: too many ciphers during list expand");
-+        return -1;
-+    }
-+
-+    for (int i = 0; i < xsz; ++i)
-+        ids[++nids] = x[i];
-+
-+    return nids;
-+}
-+
-+
-+static int
-+tls_mbedtls_translate_ciphername(int id, char *buf, size_t buflen)
-+{
-+    const mbedtls_ssl_ciphersuite_t *info =
-+      mbedtls_ssl_ciphersuite_from_id(id);
-+    if (info == NULL)
-+        return 0;
-+    const char *name = mbedtls_ssl_ciphersuite_get_name(info);
-+    const size_t len = os_strlen(name);
-+    if (len == 7 && 0 == os_memcmp(name, "unknown", 7))
-+        return 0;
-+    if (len >= buflen)
-+        return 0;
-+    os_strlcpy(buf, name, buflen);
-+
-+    /* attempt to translate mbedtls string to openssl string
-+     * (some heuristics; incomplete) */
-+    size_t i = 0, j = 0;
-+    if (buf[0] == 'T') {
-+        if (os_strncmp(buf, "TLS1-3-", 7) == 0) {
-+            buf[3] = '-';
-+            j = 4; /* remove "1-3" from "TLS1-3-" prefix */
-+            i = 7;
-+        }
-+        else if (os_strncmp(buf, "TLS-", 4) == 0)
-+            i = 4; /* remove "TLS-" prefix */
-+    }
-+    for (; buf[i]; ++i) {
-+        if (buf[i] == '-') {
-+            if (i >= 3) {
-+                if (0 == os_memcmp(buf+i-3, "AES", 3))
-+                    continue; /* "AES-" -> "AES" */
-+            }
-+            if (i >= 4) {
-+                if (0 == os_memcmp(buf+i-4, "WITH", 4)) {
-+                    j -= 4;   /* remove "WITH-" */
-+                    continue;
-+                }
-+            }
-+        }
-+        buf[j++] = buf[i];
-+    }
-+    buf[j] = '\0';
-+
-+    return j;
-+}
-+
-+
-+__attribute_noinline__
-+static int
-+tls_mbedtls_set_ciphersuites(struct tls_conf *tls_conf, int *ids, int nids)
-+{
-+    /* ciphersuites list must be persistent for lifetime of mbedtls_ssl_config*/
-+    os_free(tls_conf->ciphersuites);
-+    tls_conf->ciphersuites = os_malloc(nids * sizeof(int));
-+    if (tls_conf->ciphersuites == NULL)
-+        return 0;
-+    os_memcpy(tls_conf->ciphersuites, ids, nids * sizeof(int));
-+    mbedtls_ssl_conf_ciphersuites(&tls_conf->conf, tls_conf->ciphersuites);
-+    return 1;
-+}
-+
-+
-+static int
-+tls_mbedtls_set_ciphers(struct tls_conf *tls_conf, const char *ciphers)
-+{
-+    char buf[64];
-+    int ids[512];
-+    int nids = -1;
-+    const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
-+    const char *next;
-+    size_t blen, clen;
-+    do {
-+        next = os_strchr(ciphers, ':');
-+        clen = next ? (size_t)(next - ciphers) : os_strlen(ciphers);
-+        if (!clen)
-+            continue;
-+
-+        /* special-case a select set of openssl group names for hwsim tests */
-+	/* (review; remove excess code if tests are not run for non-OpenSSL?) */
-+        if (clen == 9 && os_memcmp(ciphers, "SUITEB192", 9) == 0) {
-+            static int ssl_preset_suiteb192_ciphersuites[] = {
-+                MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
-+                0
-+            };
-+            return tls_mbedtls_set_ciphersuites(tls_conf,
-+                                                ssl_preset_suiteb192_ciphersuites,
-+                                                2);
-+        }
-+        if (clen == 9 && os_memcmp(ciphers, "SUITEB128", 9) == 0) {
-+            static int ssl_preset_suiteb128_ciphersuites[] = {
-+                MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-+                0
-+            };
-+            return tls_mbedtls_set_ciphersuites(tls_conf,
-+                                                ssl_preset_suiteb128_ciphersuites,
-+                                                2);
-+        }
-+        if (clen == 7 && os_memcmp(ciphers, "DEFAULT", 7) == 0)
-+            continue;
-+        if (clen == 6 && os_memcmp(ciphers, "AES128", 6) == 0) {
-+            nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
-+                     suite_AES_128_ephemeral,
-+                     (int)ARRAY_SIZE(suite_AES_128_ephemeral));
-+            if (nids == -1)
-+                return 0;
-+            continue;
-+        }
-+        if (clen == 6 && os_memcmp(ciphers, "AES256", 6) == 0) {
-+            nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
-+                     suite_AES_256_ephemeral,
-+                     (int)ARRAY_SIZE(suite_AES_256_ephemeral));
-+            if (nids == -1)
-+                return 0;
-+            continue;
-+        }
-+        if (clen == 4 && os_memcmp(ciphers, "HIGH", 4) == 0) {
-+            nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz, suite_HIGH,
-+                                                  (int)ARRAY_SIZE(suite_HIGH));
-+            if (nids == -1)
-+                return 0;
-+            continue;
-+        }
-+        /* ignore anonymous cipher group names (?not supported by mbedtls?) */
-+        if (clen == 4 && os_memcmp(ciphers, "!ADH", 4) == 0)
-+            continue;
-+        if (clen == 6 && os_memcmp(ciphers, "-aECDH", 6) == 0)
-+            continue;
-+        if (clen == 7 && os_memcmp(ciphers, "-aECDSA", 7) == 0)
-+            continue;
-+
-+        /* attempt to match mbedtls cipher names
-+         * nb: does not support openssl group names or list manipulation syntax
-+         *   (alt: could copy almost 1200 lines (!!!) of lighttpd mod_mbedtls.c
-+         *    mod_mbedtls_ssl_conf_ciphersuites() to translate strings)
-+         * note: not efficient to rewrite list for each ciphers entry,
-+         *       but this code is expected to run only at startup
-+         */
-+        const int *list = mbedtls_ssl_list_ciphersuites();
-+        for (; *list; ++list) {
-+            blen = tls_mbedtls_translate_ciphername(*list,buf,sizeof(buf));
-+            if (!blen)
-+                continue;
-+
-+            /* matching heuristics additional to translate_ciphername above */
-+            if (blen == clen+4) {
-+                char *cbc = os_strstr(buf, "CBC-");
-+                if (cbc) {
-+                    os_memmove(cbc, cbc+4, blen-(cbc+4-buf)+1); /*(w/ '\0')*/
-+                    blen -= 4;
-+                }
-+            }
-+            if (blen >= clen && os_memcmp(ciphers, buf, clen) == 0
-+                && (blen == clen
-+                    || (blen == clen+7 && os_memcmp(buf+clen, "-SHA256", 7)))) {
-+                if (1 >= idsz - (nids + 1)) {
-+                    emsg(MSG_ERROR,
-+                         "error: too many ciphers during list expand");
-+                    return 0;
-+                }
-+                ids[++nids] = *list;
-+                break;
-+            }
-+        }
-+        if (*list == 0) {
-+            wpa_printf(MSG_ERROR,
-+                       "MTLS: unrecognized cipher: %.*s", (int)clen, ciphers);
-+            return 0;
-+        }
-+    } while ((ciphers = next ? next+1 : NULL));
-+
-+    if (-1 == nids) return 1; /* empty list; no-op */
-+
-+    ids[++nids] = 0; /* terminate list */
-+    ++nids;
-+
-+    return tls_mbedtls_set_ciphersuites(tls_conf, ids, nids);
-+}
-+
-+
-+__attribute_noinline__
-+static int tls_mbedtls_set_item(char **config_item, const char *item)
-+{
-+	os_free(*config_item);
-+	*config_item = NULL;
-+	return item ? (*config_item = os_strdup(item)) != NULL : 1;
-+}
-+
-+
-+static int tls_connection_set_subject_match(struct tls_conf *tls_conf,
-+                                            const struct tls_connection_params *params)
-+{
-+	int rc = 1;
-+	rc &= tls_mbedtls_set_item(&tls_conf->subject_match,
-+	                              params->subject_match);
-+	rc &= tls_mbedtls_set_item(&tls_conf->altsubject_match,
-+	                              params->altsubject_match);
-+	rc &= tls_mbedtls_set_item(&tls_conf->suffix_match,
-+	                              params->suffix_match);
-+	rc &= tls_mbedtls_set_item(&tls_conf->domain_match,
-+	                              params->domain_match);
-+	rc &= tls_mbedtls_set_item(&tls_conf->check_cert_subject,
-+	                              params->check_cert_subject);
-+	return rc;
-+}
-+
-+
-+/* duplicated in crypto_mbedtls.c:crypto_mbedtls_readfile()*/
-+__attribute_noinline__
-+static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n)
-+{
-+  #if 0 /* #ifdef MBEDTLS_FS_IO */
-+	/*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/
-+	if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) {
-+		wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path);
-+		return -1;
-+	}
-+  #else
-+	/*(use os_readfile() so that we can use os_free()
-+	 *(if we use mbedtls_pk_load_file() above, macros prevent calling free()
-+	 * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free()
-+	 * on buf aborts in tests if buf not allocated via os_malloc())*/
-+	*buf = (u8 *)os_readfile(path, n);
-+	if (!*buf) {
-+		wpa_printf(MSG_ERROR, "error: os_readfile %s", path);
-+		return -1;
-+	}
-+	u8 *buf0 = os_realloc(*buf, *n+1);
-+	if (!buf0) {
-+		bin_clear_free(*buf, *n);
-+		*buf = NULL;
-+		return -1;
-+	}
-+	buf0[(*n)++] = '\0';
-+	*buf = buf0;
-+  #endif
-+	return 0;
-+}
-+
-+
-+static int tls_mbedtls_set_crl(struct tls_conf *tls_conf, const u8 *data, size_t len)
-+{
-+	/* do not use mbedtls_x509_crl_parse() on PEM unless it contains CRL */
-+	if (len && data[len-1] == '\0'
-+	    && NULL == os_strstr((const char *)data,"-----BEGIN X509 CRL-----")
-+	    && tls_mbedtls_data_is_pem(data))
-+		return 0;
-+
-+	mbedtls_x509_crl crl;
-+	mbedtls_x509_crl_init(&crl);
-+	int rc = mbedtls_x509_crl_parse(&crl, data, len);
-+	if (rc < 0) {
-+		mbedtls_x509_crl_free(&crl);
-+		return rc == MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT ? 0 : rc;
-+	}
-+
-+	mbedtls_x509_crl *crl_new = os_malloc(sizeof(crl));
-+	if (crl_new == NULL) {
-+		mbedtls_x509_crl_free(&crl);
-+		return MBEDTLS_ERR_X509_ALLOC_FAILED;
-+	}
-+	os_memcpy(crl_new, &crl, sizeof(crl));
-+
-+	mbedtls_x509_crl *crl_old = tls_conf->crl;
-+	tls_conf->crl = crl_new;
-+	if (crl_old) {
-+		mbedtls_x509_crl_free(crl_old);
-+		os_free(crl_old);
-+	}
-+	return 0;
-+}
-+
-+
-+static int tls_mbedtls_set_ca(struct tls_conf *tls_conf, u8 *data, size_t len)
-+{
-+	/* load crt struct onto stack and then copy into tls_conf in
-+	 * order to preserve existing tls_conf value if error occurs
-+	 *
-+	 * hostapd is not threaded, or else should allocate memory and swap in
-+	 * pointer reduce race condition.  (If threaded, would also need to
-+	 * keep reference count of use to avoid freeing while still in use.) */
-+
-+	mbedtls_x509_crt crt;
-+	mbedtls_x509_crt_init(&crt);
-+	int rc = mbedtls_x509_crt_parse(&crt, data, len);
-+	if (rc < 0) {
-+		mbedtls_x509_crt_free(&crt);
-+		return rc;
-+	}
-+
-+	mbedtls_x509_crt_free(&tls_conf->ca_cert);
-+	os_memcpy(&tls_conf->ca_cert, &crt, sizeof(crt));
-+	return 0;
-+}
-+
-+
-+static int tls_mbedtls_set_ca_and_crl(struct tls_conf *tls_conf, const char *ca_cert_file)
-+{
-+	size_t len;
-+	u8 *data;
-+	if (tls_mbedtls_readfile(ca_cert_file, &data, &len))
-+		return -1;
-+
-+	int rc;
-+	if (0 == (rc = tls_mbedtls_set_ca(tls_conf, data, len))
-+	    && (!tls_mbedtls_data_is_pem(data) /*skip parse for CRL if not PEM*/
-+	        || 0 == (rc = tls_mbedtls_set_crl(tls_conf, data, len)))) {
-+		mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
-+		                          &tls_conf->ca_cert,
-+		                          tls_conf->crl);
-+	}
-+	else {
-+		elog(rc, __func__);
-+		emsg(MSG_ERROR, ca_cert_file);
-+	}
-+
-+	forced_memzero(data, len);
-+	os_free(data);
-+	return rc;
-+}
-+
-+
-+static void tls_mbedtls_refresh_crl(void)
-+{
-+	/* check for CRL refresh
-+	 * continue even if error occurs; continue with previous cert, CRL */
-+	unsigned int crl_reload_interval = tls_ctx_global.crl_reload_interval;
-+	const char *ca_cert_file = tls_ctx_global.ca_cert_file;
-+	if (!crl_reload_interval || !ca_cert_file)
-+		return;
-+
-+	struct os_reltime *previous = &tls_ctx_global.crl_reload_previous;
-+	struct os_reltime now;
-+	if (os_get_reltime(&now) != 0
-+	    || !os_reltime_expired(&now, previous, crl_reload_interval))
-+		return;
-+
-+	/* Note: modifying global state is not thread-safe
-+	 *       if in use by existing connections
-+	 *
-+	 * src/utils/os.h does not provide a portable stat()
-+	 * or else it would be a good idea to check mtime and size,
-+	 * and avoid reloading if file has not changed */
-+
-+	if (tls_mbedtls_set_ca_and_crl(tls_ctx_global.tls_conf, ca_cert_file) == 0)
-+		*previous = now;
-+}
-+
-+
-+static int tls_mbedtls_set_ca_cert(struct tls_conf *tls_conf,
-+				   const struct tls_connection_params *params)
-+{
-+	if (params->ca_cert) {
-+		if (os_strncmp(params->ca_cert, "probe://", 8) == 0) {
-+			tls_conf->ca_cert_probe = 1;
-+			tls_conf->has_ca_cert = 1;
-+			return 0;
-+		}
-+
-+		if (os_strncmp(params->ca_cert, "hash://", 7) == 0) {
-+			const char *pos = params->ca_cert + 7;
-+			if (os_strncmp(pos, "server/sha256/", 14) != 0) {
-+				emsg(MSG_ERROR, "unsupported ca_cert hash value");
-+				return -1;
-+			}
-+			pos += 14;
-+			if (os_strlen(pos) != SHA256_DIGEST_LENGTH*2) {
-+				emsg(MSG_ERROR, "unexpected ca_cert hash length");
-+				return -1;
-+			}
-+			if (hexstr2bin(pos, tls_conf->ca_cert_hash,
-+			               SHA256_DIGEST_LENGTH) < 0) {
-+				emsg(MSG_ERROR, "invalid ca_cert hash value");
-+				return -1;
-+			}
-+			emsg(MSG_DEBUG, "checking only server certificate match");
-+			tls_conf->verify_depth0_only = 1;
-+			tls_conf->has_ca_cert = 1;
-+			return 0;
-+		}
-+
-+		if (tls_mbedtls_set_ca_and_crl(tls_conf, params->ca_cert) != 0)
-+			return -1;
-+	}
-+	if (params->ca_cert_blob) {
-+		size_t len = params->ca_cert_blob_len;
-+		int is_pem = tls_mbedtls_data_is_pem(params->ca_cert_blob);
-+		if (len && params->ca_cert_blob[len-1] != '\0' && is_pem)
-+			++len; /*(include '\0' in len for PEM)*/
-+		int ret = mbedtls_x509_crt_parse(&tls_conf->ca_cert,
-+		                                 params->ca_cert_blob, len);
-+		if (ret != 0) {
-+			elog(ret, "mbedtls_x509_crt_parse");
-+			return -1;
-+		}
-+		if (is_pem) { /*(ca_cert_blob in DER format contains ca cert only)*/
-+			ret = tls_mbedtls_set_crl(tls_conf, params->ca_cert_blob, len);
-+			if (ret != 0) {
-+				elog(ret, "mbedtls_x509_crl_parse");
-+				return -1;
-+			}
-+		}
-+	}
-+
-+	if (mbedtls_x509_time_is_future(&tls_conf->ca_cert.valid_from)
-+	    || mbedtls_x509_time_is_past(&tls_conf->ca_cert.valid_to)) {
-+		emsg(MSG_WARNING, "ca_cert expired or not yet valid");
-+		if (params->ca_cert)
-+			emsg(MSG_WARNING, params->ca_cert);
-+	}
-+
-+	tls_conf->has_ca_cert = 1;
-+	return 0;
-+}
-+
-+
-+static int tls_mbedtls_set_certs(struct tls_conf *tls_conf,
-+				 const struct tls_connection_params *params)
-+{
-+	int ret;
-+
-+	if (params->ca_cert || params->ca_cert_blob) {
-+		if (tls_mbedtls_set_ca_cert(tls_conf, params) != 0)
-+			return -1;
-+	}
-+	else if (params->ca_path) {
-+		emsg(MSG_INFO, "ca_path support not implemented");
-+		return -1;
-+	}
-+
-+	if (!tls_conf->has_ca_cert)
-+		mbedtls_ssl_conf_authmode(&tls_conf->conf, MBEDTLS_SSL_VERIFY_NONE);
-+	else {
-+		/* Initial setting: REQUIRED for client, OPTIONAL for server
-+		 *   (see also tls_connection_set_verify()) */
-+		tls_conf->verify_peer = (tls_ctx_global.tls_conf == NULL);
-+		int authmode = tls_conf->verify_peer
-+		  ? MBEDTLS_SSL_VERIFY_REQUIRED
-+		  : MBEDTLS_SSL_VERIFY_OPTIONAL;
-+		mbedtls_ssl_conf_authmode(&tls_conf->conf, authmode);
-+		mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
-+		                          &tls_conf->ca_cert,
-+		                          tls_conf->crl);
-+
-+		if (!tls_connection_set_subject_match(tls_conf, params))
-+			return -1;
-+	}
-+
-+	if (params->client_cert2) /*(yes, server_cert2 in msg below)*/
-+		emsg(MSG_INFO, "server_cert2 support not implemented");
-+
-+	if (params->client_cert) {
-+		size_t len;
-+		u8 *data;
-+		if (tls_mbedtls_readfile(params->client_cert, &data, &len))
-+			return -1;
-+		ret = mbedtls_x509_crt_parse(&tls_conf->client_cert, data, len);
-+		forced_memzero(data, len);
-+		os_free(data);
-+	}
-+	if (params->client_cert_blob) {
-+		size_t len = params->client_cert_blob_len;
-+		if (len && params->client_cert_blob[len-1] != '\0'
-+		    && tls_mbedtls_data_is_pem(params->client_cert_blob))
-+			++len; /*(include '\0' in len for PEM)*/
-+		ret = mbedtls_x509_crt_parse(&tls_conf->client_cert,
-+		                             params->client_cert_blob, len);
-+	}
-+	if (params->client_cert || params->client_cert_blob) {
-+		if (ret < 0) {
-+			elog(ret, "mbedtls_x509_crt_parse");
-+			if (params->client_cert)
-+				emsg(MSG_ERROR, params->client_cert);
-+			return -1;
-+		}
-+		if (mbedtls_x509_time_is_future(&tls_conf->client_cert.valid_from)
-+		    || mbedtls_x509_time_is_past(&tls_conf->client_cert.valid_to)) {
-+			emsg(MSG_WARNING, "cert expired or not yet valid");
-+			if (params->client_cert)
-+				emsg(MSG_WARNING, params->client_cert);
-+		}
-+		tls_conf->has_client_cert = 1;
-+	}
-+
-+	if (params->private_key || params->private_key_blob) {
-+		size_t len = params->private_key_blob_len;
-+		u8 *data;
-+		*(const u8 **)&data = params->private_key_blob;
-+		if (len && data[len-1] != '\0' && tls_mbedtls_data_is_pem(data))
-+			++len; /*(include '\0' in len for PEM)*/
-+		if (params->private_key
-+		    && tls_mbedtls_readfile(params->private_key, &data, &len)) {
-+			return -1;
-+		}
-+		const char *pwd = params->private_key_passwd;
-+	  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+		ret = mbedtls_pk_parse_key(&tls_conf->private_key,
-+			data, len,
-+			(const unsigned char *)pwd,
-+			pwd ? os_strlen(pwd) : 0,
-+			mbedtls_ctr_drbg_random,
-+			tls_ctx_global.ctr_drbg);
-+	  #else
-+		ret = mbedtls_pk_parse_key(&tls_conf->private_key,
-+			data, len,
-+			(const unsigned char *)pwd,
-+			pwd ? os_strlen(pwd) : 0);
-+	  #endif
-+		if (params->private_key) {
-+			forced_memzero(data, len);
-+			os_free(data);
-+		}
-+		if (ret < 0) {
-+			elog(ret, "mbedtls_pk_parse_key");
-+			return -1;
-+		}
-+		tls_conf->has_private_key = 1;
-+	}
-+
-+	if (tls_conf->has_client_cert && tls_conf->has_private_key) {
-+		ret = mbedtls_ssl_conf_own_cert(
-+		    &tls_conf->conf, &tls_conf->client_cert, &tls_conf->private_key);
-+		if (ret < 0) {
-+			elog(ret, "mbedtls_ssl_conf_own_cert");
-+			return -1;
-+		}
-+	}
-+
-+	return 0;
-+}
-+
-+
-+/* mbedtls_x509_crt_profile_suiteb plus rsa_min_bitlen 2048 */
-+/* (reference: see also mbedtls_x509_crt_profile_next) */
-+/* ??? should permit SHA-512, too, and additional curves ??? */
-+static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb128 =
-+{
-+    /* Only SHA-256 and 384 */
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) |
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
-+    /* Only ECDSA */
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
-+#if defined(MBEDTLS_ECP_C)
-+    /* Only NIST P-256 and P-384 */
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP256R1 ) |
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
-+#else
-+    0,
-+#endif
-+    2048,
-+};
-+
-+
-+/* stricter than mbedtls_x509_crt_profile_suiteb */
-+/* (reference: see also mbedtls_x509_crt_profile_next) */
-+/* ??? should permit SHA-512, too, and additional curves ??? */
-+static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192 =
-+{
-+    /* Only SHA-384 */
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
-+    /* Only ECDSA */
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
-+#if defined(MBEDTLS_ECP_C)
-+    /* Only NIST P-384 */
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
-+#else
-+    0,
-+#endif
-+    3072,
-+};
-+
-+
-+/* stricter than mbedtls_x509_crt_profile_suiteb except allow any PK alg */
-+/* (reference: see also mbedtls_x509_crt_profile_next) */
-+/* ??? should permit SHA-512, too, and additional curves ??? */
-+static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192_anypk =
-+{
-+    /* Only SHA-384 */
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
-+    0xFFFFFFF, /* Any PK alg    */
-+#if defined(MBEDTLS_ECP_C)
-+    /* Only NIST P-384 */
-+    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
-+#else
-+    0,
-+#endif
-+    3072,
-+};
-+
-+
-+static int tls_mbedtls_set_params(struct tls_conf *tls_conf,
-+				  const struct tls_connection_params *params)
-+{
-+	tls_conf->flags = params->flags;
-+
-+	if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
-+		emsg(MSG_INFO, "ocsp=3 not supported");
-+		return -1;
-+	}
-+
-+	if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP) {
-+		emsg(MSG_INFO, "ocsp not supported");
-+		return -1;
-+	}
-+
-+	int suiteb128 = 0;
-+	int suiteb192 = 0;
-+	if (params->openssl_ciphers) {
-+		if (os_strcmp(params->openssl_ciphers, "SUITEB192") == 0) {
-+			suiteb192 = 1;
-+			tls_conf->flags |= TLS_CONN_SUITEB;
-+		}
-+		if (os_strcmp(params->openssl_ciphers, "SUITEB128") == 0) {
-+			suiteb128 = 1;
-+			tls_conf->flags |= TLS_CONN_SUITEB;
-+		}
-+	}
-+
-+	int ret = mbedtls_ssl_config_defaults(
-+	    &tls_conf->conf, tls_ctx_global.tls_conf ? MBEDTLS_SSL_IS_SERVER
-+	                                             : MBEDTLS_SSL_IS_CLIENT,
-+	    MBEDTLS_SSL_TRANSPORT_STREAM,
-+	    (tls_conf->flags & TLS_CONN_SUITEB) ? MBEDTLS_SSL_PRESET_SUITEB
-+	                                        : MBEDTLS_SSL_PRESET_DEFAULT);
-+	if (ret != 0) {
-+		elog(ret, "mbedtls_ssl_config_defaults");
-+		return -1;
-+	}
-+
-+	if (suiteb128) {
-+		mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
-+		                              &tls_mbedtls_crt_profile_suiteb128);
-+		mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 2048);
-+	}
-+	else if (suiteb192) {
-+		mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
-+		                              &tls_mbedtls_crt_profile_suiteb192);
-+		mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
-+	}
-+	else if (tls_conf->flags & TLS_CONN_SUITEB) {
-+		/* treat as suiteb192 while allowing any PK algorithm */
-+		mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
-+		                              &tls_mbedtls_crt_profile_suiteb192_anypk);
-+		mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
-+	}
-+
-+	tls_mbedtls_set_allowed_tls_vers(tls_conf, &tls_conf->conf);
-+	ret = tls_mbedtls_set_certs(tls_conf, params);
-+	if (ret != 0)
-+		return -1;
-+
-+	if (params->dh_file
-+	    && !tls_mbedtls_set_dhparams(tls_conf, params->dh_file)) {
-+		return -1;
-+	}
-+
-+	if (params->openssl_ecdh_curves
-+	    && !tls_mbedtls_set_curves(tls_conf, params->openssl_ecdh_curves)) {
-+		return -1;
-+	}
-+
-+	if (params->openssl_ciphers) {
-+		if (!tls_mbedtls_set_ciphers(tls_conf, params->openssl_ciphers))
-+			return -1;
-+	}
-+	else if (tls_conf->flags & TLS_CONN_SUITEB) {
-+		/* special-case a select set of ciphers for hwsim tests */
-+		if (!tls_mbedtls_set_ciphers(tls_conf,
-+		        (tls_conf->flags & TLS_CONN_SUITEB_NO_ECDH)
-+		          ? "DHE-RSA-AES256-GCM-SHA384"
-+		          : "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384"))
-+			return -1;
-+	}
-+
-+	return 0;
-+}
-+
-+
-+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
-+			      const struct tls_connection_params *params)
-+{
-+	if (conn == NULL || params == NULL)
-+		return -1;
-+
-+	tls_conf_deinit(conn->tls_conf);
-+	struct tls_conf *tls_conf = conn->tls_conf = tls_conf_init(tls_ctx);
-+	if (tls_conf == NULL)
-+		return -1;
-+
-+	if (tls_ctx_global.tls_conf) {
-+		tls_conf->check_crl = tls_ctx_global.tls_conf->check_crl;
-+		tls_conf->check_crl_strict = tls_ctx_global.tls_conf->check_crl_strict;
-+		/*(tls_openssl.c inherits check_cert_subject from global conf)*/
-+		if (tls_ctx_global.tls_conf->check_cert_subject) {
-+			tls_conf->check_cert_subject =
-+			  os_strdup(tls_ctx_global.tls_conf->check_cert_subject);
-+			if (tls_conf->check_cert_subject == NULL)
-+				return -1;
-+		}
-+	}
-+
-+	if (tls_mbedtls_set_params(tls_conf, params) != 0)
-+		return -1;
-+	conn->verify_peer = tls_conf->verify_peer;
-+
-+	return tls_mbedtls_ssl_setup(conn);
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_SESSION_TICKETS
-+
-+static int tls_mbedtls_clienthello_session_ticket_prep (struct tls_connection *conn,
-+                                                        const u8 *data, size_t len)
-+{
-+	if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
-+		return -1;
-+	if (conn->clienthello_session_ticket)
-+		tls_connection_deinit_clienthello_session_ticket(conn);
-+	if (len) {
-+		conn->clienthello_session_ticket = mbedtls_calloc(1, len);
-+		if (conn->clienthello_session_ticket == NULL)
-+			return -1;
-+		conn->clienthello_session_ticket_len = len;
-+		os_memcpy(conn->clienthello_session_ticket, data, len);
-+	}
-+	return 0;
-+}
-+
-+
-+static void tls_mbedtls_clienthello_session_ticket_set (struct tls_connection *conn)
-+{
-+	mbedtls_ssl_session *sess = conn->ssl.MBEDTLS_PRIVATE(session_negotiate);
-+	if (sess->MBEDTLS_PRIVATE(ticket)) {
-+		mbedtls_platform_zeroize(sess->MBEDTLS_PRIVATE(ticket),
-+		                         sess->MBEDTLS_PRIVATE(ticket_len));
-+		mbedtls_free(sess->MBEDTLS_PRIVATE(ticket));
-+	}
-+	sess->MBEDTLS_PRIVATE(ticket) = conn->clienthello_session_ticket;
-+	sess->MBEDTLS_PRIVATE(ticket_len) = conn->clienthello_session_ticket_len;
-+	sess->MBEDTLS_PRIVATE(ticket_lifetime) = 86400;/* XXX: can hint be 0? */
-+
-+	conn->clienthello_session_ticket = NULL;
-+	conn->clienthello_session_ticket_len = 0;
-+}
-+
-+
-+static int tls_mbedtls_ssl_ticket_write(void *p_ticket,
-+                                        const mbedtls_ssl_session *session,
-+                                        unsigned char *start,
-+                                        const unsigned char *end,
-+                                        size_t *tlen,
-+                                        uint32_t *lifetime)
-+{
-+	struct tls_connection *conn = p_ticket;
-+	if (conn && conn->session_ticket_cb) {
-+		/* see tls_mbedtls_clienthello_session_ticket_prep() */
-+		/* see tls_mbedtls_clienthello_session_ticket_set() */
-+		return 0;
-+	}
-+
-+	return mbedtls_ssl_ticket_write(&tls_ctx_global.ticket_ctx,
-+	                                session, start, end, tlen, lifetime);
-+}
-+
-+
-+static int tls_mbedtls_ssl_ticket_parse(void *p_ticket,
-+                                        mbedtls_ssl_session *session,
-+                                        unsigned char *buf,
-+                                        size_t len)
-+{
-+	/* XXX: TODO: not implemented in client;
-+	 * mbedtls_ssl_conf_session_tickets_cb() callbacks only for TLS server*/
-+
-+	if (len == 0)
-+		return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-+
-+	struct tls_connection *conn = p_ticket;
-+	if (conn && conn->session_ticket_cb) {
-+		/* XXX: have random and secret been initialized yet?
-+		 *      or must keys first be exported?
-+		 *      EAP-FAST uses all args, EAP-TEAP only uses secret */
-+		struct tls_random data;
-+		if (tls_connection_get_random(NULL, conn, &data) != 0)
-+			return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
-+		int ret =
-+		  conn->session_ticket_cb(conn->session_ticket_cb_ctx,
-+		                          buf, len,
-+		                          data.client_random,
-+		                          data.server_random,
-+		                          conn->expkey_secret);
-+		if (ret == 1) {
-+			conn->resumed = 1;
-+			return 0;
-+		}
-+		emsg(MSG_ERROR, "EAP session ticket ext not implemented");
-+		return MBEDTLS_ERR_SSL_INVALID_MAC;
-+		/*(non-zero return used for mbedtls debug logging)*/
-+	}
-+
-+	/* XXX: TODO always use tls_mbedtls_ssl_ticket_parse() for callback? */
-+	int rc = mbedtls_ssl_ticket_parse(&tls_ctx_global.ticket_ctx,
-+	                                  session, buf, len);
-+	if (conn)
-+		conn->resumed = (rc == 0);
-+	return rc;
-+}
-+
-+#endif /* TLS_MBEDTLS_SESSION_TICKETS */
-+
-+
-+__attribute_cold__
-+int tls_global_set_params(void *tls_ctx,
-+			  const struct tls_connection_params *params)
-+{
-+	/* XXX: why might global_set_params be called more than once? */
-+	if (tls_ctx_global.tls_conf)
-+		tls_conf_deinit(tls_ctx_global.tls_conf);
-+	tls_ctx_global.tls_conf = tls_conf_init(tls_ctx);
-+	if (tls_ctx_global.tls_conf == NULL)
-+		return -1;
-+
-+  #ifdef MBEDTLS_SSL_SESSION_TICKETS
-+  #ifdef MBEDTLS_SSL_TICKET_C
-+	if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET))
-+	  #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+		mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
-+		                                    tls_mbedtls_ssl_ticket_write,
-+		                                    tls_mbedtls_ssl_ticket_parse,
-+		                                    NULL);
-+	  #else
-+		mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
-+		                                    mbedtls_ssl_ticket_write,
-+		                                    mbedtls_ssl_ticket_parse,
-+		                                    &tls_ctx_global.ticket_ctx);
-+	  #endif
-+  #endif
-+  #endif
-+
-+	os_free(tls_ctx_global.ocsp_stapling_response);
-+	tls_ctx_global.ocsp_stapling_response = NULL;
-+	if (params->ocsp_stapling_response)
-+		tls_ctx_global.ocsp_stapling_response =
-+			os_strdup(params->ocsp_stapling_response);
-+
-+	os_free(tls_ctx_global.ca_cert_file);
-+	tls_ctx_global.ca_cert_file = NULL;
-+	if (params->ca_cert)
-+		tls_ctx_global.ca_cert_file = os_strdup(params->ca_cert);
-+	return tls_mbedtls_set_params(tls_ctx_global.tls_conf, params);
-+}
-+
-+
-+int tls_global_set_verify(void *tls_ctx, int check_crl, int strict)
-+{
-+	tls_ctx_global.tls_conf->check_crl = check_crl;
-+	tls_ctx_global.tls_conf->check_crl_strict = strict; /*(time checks)*/
-+	return 0;
-+}
-+
-+
-+int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
-+			      int verify_peer, unsigned int flags,
-+			      const u8 *session_ctx, size_t session_ctx_len)
-+{
-+	/*(EAP server-side calls this from eap_server_tls_ssl_init())*/
-+	if (conn == NULL)
-+		return -1;
-+
-+	conn->tls_conf->flags |= flags;/* TODO: reprocess flags, if necessary */
-+
-+	int authmode;
-+	switch (verify_peer) {
-+	case 2:  authmode = MBEDTLS_SSL_VERIFY_OPTIONAL; break;/*(eap_teap_init())*/
-+	case 1:  authmode = MBEDTLS_SSL_VERIFY_REQUIRED; break;
-+	default: authmode = MBEDTLS_SSL_VERIFY_NONE;     break;
-+	}
-+	mbedtls_ssl_set_hs_authmode(&conn->ssl, authmode);
-+
-+	if ((conn->verify_peer = (authmode != MBEDTLS_SSL_VERIFY_NONE)))
-+		mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
-+	else
-+		mbedtls_ssl_set_verify(&conn->ssl, NULL, NULL);
-+
-+	return 0;
-+}
-+
-+
-+#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+static void tls_connection_export_keys_cb(
-+    void *p_expkey, mbedtls_ssl_key_export_type secret_type,
-+    const unsigned char *secret, size_t secret_len,
-+    const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
-+    const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
-+    mbedtls_tls_prf_types tls_prf_type)
-+{
-+	struct tls_connection *conn = p_expkey;
-+	conn->tls_prf_type = tls_prf_type;
-+	if (!tls_prf_type)
-+		return;
-+	if (secret_len > sizeof(conn->expkey_secret)) {
-+		emsg(MSG_ERROR, "tls_connection_export_keys_cb secret too long");
-+		conn->tls_prf_type = MBEDTLS_SSL_TLS_PRF_NONE; /* 0 */
-+		return;
-+	}
-+	conn->expkey_secret_len = secret_len;
-+	os_memcpy(conn->expkey_secret, secret, secret_len);
-+	os_memcpy(conn->expkey_randbytes,
-+	          client_random, MBEDTLS_EXPKEY_RAND_LEN);
-+	os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
-+	          server_random, MBEDTLS_EXPKEY_RAND_LEN);
-+}
-+#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+static int tls_connection_export_keys_cb(
-+    void *p_expkey,
-+    const unsigned char *ms,
-+    const unsigned char *kb,
-+    size_t maclen,
-+    size_t keylen,
-+    size_t ivlen,
-+    const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
-+    const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
-+    mbedtls_tls_prf_types tls_prf_type )
-+{
-+	struct tls_connection *conn = p_expkey;
-+	conn->tls_prf_type = tls_prf_type;
-+	if (!tls_prf_type)
-+		return -1; /*(return value ignored by mbedtls)*/
-+	conn->expkey_keyblock_size = maclen + keylen + ivlen;
-+	conn->expkey_secret_len = MBEDTLS_EXPKEY_FIXED_SECRET_LEN;
-+	os_memcpy(conn->expkey_secret, ms, MBEDTLS_EXPKEY_FIXED_SECRET_LEN);
-+	os_memcpy(conn->expkey_randbytes,
-+	          client_random, MBEDTLS_EXPKEY_RAND_LEN);
-+	os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
-+	          server_random, MBEDTLS_EXPKEY_RAND_LEN);
-+	return 0;
-+}
-+#endif
-+
-+
-+int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
-+			      struct tls_random *data)
-+{
-+	if (!conn || !conn->tls_prf_type)
-+		return -1;
-+	data->client_random = conn->expkey_randbytes;
-+	data->client_random_len = MBEDTLS_EXPKEY_RAND_LEN;
-+	data->server_random = conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN;
-+	data->server_random_len = MBEDTLS_EXPKEY_RAND_LEN;
-+	return 0;
-+}
-+
-+
-+int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
-+			      const char *label, const u8 *context,
-+			      size_t context_len, u8 *out, size_t out_len)
-+{
-+	/* (EAP-PEAP EAP-TLS EAP-TTLS) */
-+  #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+	return (conn && conn->established && conn->tls_prf_type)
-+	  ? mbedtls_ssl_tls_prf(conn->tls_prf_type,
-+				conn->expkey_secret, conn->expkey_secret_len, label,
-+				conn->expkey_randbytes,
-+				sizeof(conn->expkey_randbytes), out, out_len)
-+	  : -1;
-+  #else
-+	/* not implemented here for mbedtls < 2.18.0 */
-+	return -1;
-+  #endif
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_FAST
-+
-+#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+/* keyblock size info is not exposed in mbed TLS 3.0.0 */
-+/* extracted from mbedtls library/ssl_tls.c:ssl_tls12_populate_transform() */
-+#include <mbedtls/ssl_ciphersuites.h>
-+#include <mbedtls/cipher.h>
-+static size_t tls_mbedtls_ssl_keyblock_size (mbedtls_ssl_context *ssl)
-+{
-+  #if !defined(MBEDTLS_USE_PSA_CRYPTO) /* XXX: (not extracted for PSA crypto) */
-+  #if defined(MBEDTLS_SSL_PROTO_TLS1_3)
-+    if (tls_version == MBEDTLS_SSL_VERSION_TLS1_3)
-+        return 0; /* (calculation not extracted) */
-+  #endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
-+
-+    int ciphersuite = mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl);
-+    const mbedtls_ssl_ciphersuite_t *ciphersuite_info =
-+      mbedtls_ssl_ciphersuite_from_id(ciphersuite);
-+    if (ciphersuite_info == NULL)
-+        return 0;
-+
-+    const mbedtls_cipher_info_t *cipher_info =
-+      mbedtls_cipher_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(cipher));
-+    if (cipher_info == NULL)
-+        return 0;
-+
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */
-+    size_t keylen = mbedtls_cipher_info_get_key_bitlen(cipher_info) / 8;
-+    mbedtls_cipher_mode_t mode = mbedtls_cipher_info_get_mode(cipher_info);
-+  #else
-+    size_t keylen = cipher_info->MBEDTLS_PRIVATE(key_bitlen) / 8;
-+    mbedtls_cipher_mode_t mode = cipher_info->MBEDTLS_PRIVATE(mode);
-+  #endif
-+  #if defined(MBEDTLS_GCM_C) || \
-+      defined(MBEDTLS_CCM_C) || \
-+      defined(MBEDTLS_CHACHAPOLY_C)
-+    if (mode == MBEDTLS_MODE_GCM || mode == MBEDTLS_MODE_CCM)
-+        return keylen + 4;
-+    else if (mode == MBEDTLS_MODE_CHACHAPOLY)
-+        return keylen + 12;
-+    else
-+  #endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C || MBEDTLS_CHACHAPOLY_C */
-+  #if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
-+    {
-+        const mbedtls_md_info_t *md_info =
-+          mbedtls_md_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(mac));
-+        if (md_info == NULL)
-+            return 0;
-+        size_t mac_key_len = mbedtls_md_get_size(md_info);
-+        size_t ivlen = mbedtls_cipher_info_get_iv_size(cipher_info);
-+        return keylen + mac_key_len + ivlen;
-+    }
-+  #endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */
-+  #endif /* !MBEDTLS_USE_PSA_CRYPTO *//* (not extracted for PSA crypto) */
-+    return 0;
-+}
-+#endif /* MBEDTLS_VERSION_NUMBER >= 0x03000000 *//* mbedtls 3.0.0 */
-+
-+
-+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
-+				    u8 *out, size_t out_len)
-+{
-+	/* XXX: has export keys callback been run? */
-+	if (!conn || !conn->tls_prf_type)
-+		return -1;
-+
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+	conn->expkey_keyblock_size = tls_mbedtls_ssl_keyblock_size(&conn->ssl);
-+	if (conn->expkey_keyblock_size == 0)
-+		return -1;
-+  #endif
-+	size_t skip = conn->expkey_keyblock_size * 2;
-+	unsigned char *tmp_out = os_malloc(skip + out_len);
-+	if (!tmp_out)
-+		return -1;
-+
-+	/* server_random and then client_random */
-+	unsigned char seed[MBEDTLS_EXPKEY_RAND_LEN*2];
-+	os_memcpy(seed, conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
-+	          MBEDTLS_EXPKEY_RAND_LEN);
-+	os_memcpy(seed + MBEDTLS_EXPKEY_RAND_LEN, conn->expkey_randbytes,
-+	          MBEDTLS_EXPKEY_RAND_LEN);
-+
-+  #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+	int ret = mbedtls_ssl_tls_prf(conn->tls_prf_type,
-+				      conn->expkey_secret, conn->expkey_secret_len,
-+				      "key expansion", seed, sizeof(seed),
-+				      tmp_out, skip + out_len);
-+	if (ret == 0)
-+		os_memcpy(out, tmp_out + skip, out_len);
-+  #else
-+	int ret = -1; /*(not reached if not impl; return -1 at top of func)*/
-+  #endif
-+
-+	bin_clear_free(tmp_out, skip + out_len);
-+	forced_memzero(seed, sizeof(seed));
-+	return ret;
-+}
-+
-+#endif /* TLS_MBEDTLS_EAP_FAST */
-+
-+
-+__attribute_cold__
-+static void tls_mbedtls_suiteb_handshake_alert (struct tls_connection *conn)
-+{
-+	/* tests/hwsim/test_suite_b.py test_suite_b_192_rsa_insufficient_dh */
-+	if (!(conn->tls_conf->flags & TLS_CONN_SUITEB))
-+		return;
-+	if (tls_ctx_global.tls_conf) /*(is server; want issue event on client)*/
-+		return;
-+  #if 0
-+	/*(info not available on client;
-+         * mbed TLS library enforces dhm min bitlen in ServerKeyExchange)*/
-+	if (MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ==
-+	  #if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
-+	          mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl)
-+	  #else
-+	          mbedtls_ssl_get_ciphersuite_id(
-+	            mbedtls_ssl_get_ciphersuite(&conn->ssl))
-+	  #endif
-+	    && mbedtls_mpi_size(&conn->tls_conf->conf.MBEDTLS_PRIVATE(dhm_P))
-+	         < 384 /*(3072/8)*/)
-+  #endif
-+	{
-+		struct tls_config *init_conf = &tls_ctx_global.init_conf;
-+		if (init_conf->event_cb) {
-+			union tls_event_data ev;
-+			os_memset(&ev, 0, sizeof(ev));
-+			ev.alert.is_local = 1;
-+			ev.alert.type = "fatal";
-+			/*"internal error" string for tests/hwsim/test_suiteb.py */
-+			ev.alert.description = "internal error: handshake failure";
-+			/*ev.alert.description = "insufficient security";*/
-+			init_conf->event_cb(init_conf->cb_ctx, TLS_ALERT, &ev);
-+		}
-+	}
-+}
-+
-+
-+struct wpabuf * tls_connection_handshake(void *tls_ctx,
-+					 struct tls_connection *conn,
-+					 const struct wpabuf *in_data,
-+					 struct wpabuf **appl_data)
-+{
-+	if (appl_data)
-+		*appl_data = NULL;
-+
-+	if (in_data && wpabuf_len(in_data)) {
-+		/*(unsure why tls_gnutls.c discards buffer contents; skip here)*/
-+		if (conn->pull_buf && 0) /* disable; appears unwise */
-+			tls_pull_buf_discard(conn, __func__);
-+		if (!tls_pull_buf_append(conn, in_data))
-+			return NULL;
-+	}
-+
-+	if (conn->tls_conf == NULL) {
-+		struct tls_connection_params params;
-+		os_memset(&params, 0, sizeof(params));
-+		params.openssl_ciphers =
-+		  tls_ctx_global.init_conf.openssl_ciphers;
-+		params.flags = tls_ctx_global.tls_conf->flags;
-+		if (tls_connection_set_params(tls_ctx, conn, &params) != 0)
-+			return NULL;
-+	}
-+
-+	if (conn->verify_peer) /*(call here might be redundant; nbd)*/
-+		mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
-+
-+  #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+	if (conn->clienthello_session_ticket)
-+		/*(starting handshake for EAP-FAST and EAP-TEAP)*/
-+		tls_mbedtls_clienthello_session_ticket_set(conn);
-+
-+	/* (not thread-safe due to need to set userdata 'conn' for callback) */
-+	/* (unable to use mbedtls_ssl_set_user_data_p() with mbedtls 3.2.0+
-+	 *  since ticket write and parse callbacks take (mbedtls_ssl_session *)
-+	 *  param instead of (mbedtls_ssl_context *) param) */
-+	if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
-+		mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
-+		                                    NULL, NULL, NULL);
-+	else
-+		mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
-+		                                    tls_mbedtls_ssl_ticket_write,
-+		                                    tls_mbedtls_ssl_ticket_parse,
-+		                                    conn);
-+  #endif
-+
-+  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
-+	int ret = mbedtls_ssl_handshake(&conn->ssl);
-+  #else
-+	int ret = 0;
-+	while (conn->ssl.MBEDTLS_PRIVATE(state) != MBEDTLS_SSL_HANDSHAKE_OVER) {
-+		ret = mbedtls_ssl_handshake_step(&conn->ssl);
-+		if (ret != 0)
-+			break;
-+	}
-+  #endif
-+
-+  #ifdef TLS_MBEDTLS_SESSION_TICKETS
-+	mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
-+	                                    tls_mbedtls_ssl_ticket_write,
-+	                                    tls_mbedtls_ssl_ticket_parse,
-+	                                    NULL);
-+  #endif
-+
-+	switch (ret) {
-+	case 0:
-+		conn->established = 1;
-+		if (conn->push_buf == NULL)
-+			/* Need to return something to get final TLS ACK. */
-+			conn->push_buf = wpabuf_alloc(0);
-+
-+		if (appl_data /*&& conn->pull_buf && wpabuf_len(conn->pull_buf)*/)
-+			*appl_data = NULL; /* RFE: check for application data */
-+		break;
-+	case MBEDTLS_ERR_SSL_WANT_WRITE:
-+	case MBEDTLS_ERR_SSL_WANT_READ:
-+	case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS:
-+	case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS:
-+		if (tls_ctx_global.tls_conf /*(is server)*/
-+		    && conn->established && conn->push_buf == NULL)
-+			/* Need to return something to trigger completion of EAP-TLS. */
-+			conn->push_buf = wpabuf_alloc(0);
-+		break;
-+	default:
-+		++conn->failed;
-+		switch (ret) {
-+		case MBEDTLS_ERR_SSL_CLIENT_RECONNECT:
-+		case MBEDTLS_ERR_NET_CONN_RESET:
-+		case MBEDTLS_ERR_NET_SEND_FAILED:
-+			++conn->write_alerts;
-+			break;
-+	      #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
-+		case MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE:
-+	      #else
-+		case MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE:
-+	      #endif
-+			tls_mbedtls_suiteb_handshake_alert(conn);
-+			/* fall through */
-+		case MBEDTLS_ERR_NET_RECV_FAILED:
-+		case MBEDTLS_ERR_SSL_CONN_EOF:
-+		case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
-+		case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE:
-+			++conn->read_alerts;
-+			break;
-+		default:
-+			break;
-+		}
-+
-+		ilog(ret, "mbedtls_ssl_handshake");
-+		break;
-+	}
-+
-+	struct wpabuf *out_data = conn->push_buf;
-+	conn->push_buf = NULL;
-+	return out_data;
-+}
-+
-+
-+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
-+						struct tls_connection *conn,
-+						const struct wpabuf *in_data,
-+						struct wpabuf **appl_data)
-+{
-+	conn->is_server = 1;
-+	return tls_connection_handshake(tls_ctx, conn, in_data, appl_data);
-+}
-+
-+
-+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
-+				       struct tls_connection *conn,
-+				       const struct wpabuf *in_data)
-+{
-+	int res = mbedtls_ssl_write(&conn->ssl,
-+	                            wpabuf_head_u8(in_data), wpabuf_len(in_data));
-+	if (res < 0) {
-+		elog(res, "mbedtls_ssl_write");
-+		return NULL;
-+	}
-+
-+	struct wpabuf *buf = conn->push_buf;
-+	conn->push_buf = NULL;
-+	return buf;
-+}
-+
-+
-+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
-+				       struct tls_connection *conn,
-+				       const struct wpabuf *in_data)
-+{
-+	int res;
-+	struct wpabuf *out;
-+
-+	/*assert(in_data != NULL);*/
-+	if (!tls_pull_buf_append(conn, in_data))
-+		return NULL;
-+
-+  #if defined(MBEDTLS_ZLIB_SUPPORT) /* removed in mbedtls 3.x */
-+	/* Add extra buffer space to handle the possibility of decrypted
-+	 * data being longer than input data due to TLS compression. */
-+	out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
-+  #else /* TLS compression is disabled in mbedtls 3.x */
-+	out = wpabuf_alloc(wpabuf_len(in_data));
-+  #endif
-+	if (out == NULL)
-+		return NULL;
-+
-+	res = mbedtls_ssl_read(&conn->ssl, wpabuf_mhead(out), wpabuf_size(out));
-+	if (res < 0) {
-+	  #if 1 /*(seems like a different error if wpabuf_len(in_data) == 0)*/
-+		if (res == MBEDTLS_ERR_SSL_WANT_READ)
-+			return out;
-+	  #endif
-+		elog(res, "mbedtls_ssl_read");
-+		wpabuf_free(out);
-+		return NULL;
-+	}
-+	wpabuf_put(out, res);
-+
-+	return out;
-+}
-+
-+
-+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
-+{
-+	/* XXX: might need to detect if session resumed from TLS session ticket
-+	 * even if not special session ticket handling for EAP-FAST, EAP-TEAP */
-+	/* (?ssl->handshake->resume during session ticket validation?) */
-+	return conn && conn->resumed;
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_FAST
-+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
-+				   u8 *ciphers)
-+{
-+	/* ciphers is list of TLS_CIPHER_* from hostap/src/crypto/tls.h */
-+	int ids[7];
-+	const int idsz = (int)sizeof(ids);
-+	int nids = -1, id;
-+	for ( ; *ciphers != TLS_CIPHER_NONE; ++ciphers) {
-+		switch (*ciphers) {
-+		case TLS_CIPHER_RC4_SHA:
-+		  #ifdef MBEDTLS_TLS_RSA_WITH_RC4_128_SHA
-+			id = MBEDTLS_TLS_RSA_WITH_RC4_128_SHA;
-+			break;
-+		  #else
-+			continue; /*(not supported in mbedtls 3.x; ignore)*/
-+		  #endif
-+		case TLS_CIPHER_AES128_SHA:
-+			id = MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA;
-+			break;
-+		case TLS_CIPHER_RSA_DHE_AES128_SHA:
-+			id = MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
-+			break;
-+		case TLS_CIPHER_ANON_DH_AES128_SHA:
-+			continue; /*(not supported in mbedtls; ignore)*/
-+		case TLS_CIPHER_RSA_DHE_AES256_SHA:
-+			id = MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
-+			break;
-+		case TLS_CIPHER_AES256_SHA:
-+			id = MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA;
-+			break;
-+		default:
-+			return -1; /* should not happen */
-+		}
-+		if (++nids == idsz)
-+			return -1; /* should not happen */
-+		ids[nids] = id;
-+	}
-+	if (nids < 0)
-+		return 0; /* nothing to do */
-+	if (++nids == idsz)
-+		return -1; /* should not happen */
-+	ids[nids] = 0; /* terminate list */
-+	++nids;
-+
-+	return tls_mbedtls_set_ciphersuites(conn->tls_conf, ids, nids) ? 0 : -1;
-+}
-+#endif
-+
-+
-+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
-+		    char *buf, size_t buflen)
-+{
-+	if (conn == NULL)
-+		return -1;
-+	os_strlcpy(buf, mbedtls_ssl_get_version(&conn->ssl), buflen);
-+	return buf[0] != 'u' ? 0 : -1; /*(-1 if "unknown")*/
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_TEAP
-+u16 tls_connection_get_cipher_suite(struct tls_connection *conn)
-+{
-+	if (conn == NULL)
-+		return 0;
-+	return (u16)mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
-+}
-+#endif
-+
-+
-+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
-+		   char *buf, size_t buflen)
-+{
-+	if (conn == NULL)
-+		return -1;
-+	const int id = mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
-+	return tls_mbedtls_translate_ciphername(id, buf, buflen) ? 0 : -1;
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_SESSION_TICKETS
-+
-+int tls_connection_enable_workaround(void *tls_ctx,
-+				     struct tls_connection *conn)
-+{
-+	/* (see comment in src/eap_peer/eap_fast.c:eap_fast_init()) */
-+	/* XXX: is there a relevant setting for this in mbed TLS? */
-+	/* (do we even care that much about older CBC ciphers?) */
-+	return 0;
-+}
-+
-+
-+int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
-+				    int ext_type, const u8 *data,
-+				    size_t data_len)
-+{
-+	/* (EAP-FAST and EAP-TEAP) */
-+	if (ext_type == MBEDTLS_TLS_EXT_SESSION_TICKET) /*(ext_type == 35)*/
-+		return tls_mbedtls_clienthello_session_ticket_prep(conn, data,
-+		                                                   data_len);
-+
-+	return -1;
-+}
-+
-+#endif /* TLS_MBEDTLS_SESSION_TICKETS */
-+
-+
-+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
-+{
-+	return conn ? conn->failed : -1;
-+}
-+
-+
-+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
-+{
-+	return conn ? conn->read_alerts : -1;
-+}
-+
-+
-+int tls_connection_get_write_alerts(void *tls_ctx,
-+				    struct tls_connection *conn)
-+{
-+	return conn ? conn->write_alerts : -1;
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_SESSION_TICKETS
-+int tls_connection_set_session_ticket_cb(
-+	void *tls_ctx, struct tls_connection *conn,
-+	tls_session_ticket_cb cb, void *ctx)
-+{
-+	if (!(conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)) {
-+		/* (EAP-FAST and EAP-TEAP) */
-+		conn->session_ticket_cb = cb;
-+		conn->session_ticket_cb_ctx = ctx;
-+		return 0;
-+	}
-+	return -1;
-+}
-+#endif
-+
-+
-+int tls_get_library_version(char *buf, size_t buf_len)
-+{
-+  #ifndef MBEDTLS_VERSION_C
-+	const char * const ver = "n/a";
-+  #else
-+	char ver[9];
-+	mbedtls_version_get_string(ver);
-+  #endif
-+	return os_snprintf(buf, buf_len,
-+	                   "mbed TLS build=" MBEDTLS_VERSION_STRING " run=%s", ver);
-+}
-+
-+
-+void tls_connection_set_success_data(struct tls_connection *conn,
-+				     struct wpabuf *data)
-+{
-+	wpabuf_free(conn->success_data);
-+	conn->success_data = data;
-+}
-+
-+
-+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
-+{
-+}
-+
-+
-+const struct wpabuf *
-+tls_connection_get_success_data(struct tls_connection *conn)
-+{
-+	return conn->success_data;
-+}
-+
-+
-+void tls_connection_remove_session(struct tls_connection *conn)
-+{
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_TEAP
-+int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len)
-+{
-+  #if defined(MBEDTLS_SSL_RENEGOTIATION) /* XXX: renegotiation or resumption? */
-+	/* data from TLS handshake Finished message */
-+	size_t verify_len = conn->ssl.MBEDTLS_PRIVATE(verify_data_len);
-+	char *verify_data = (conn->is_server ^ conn->resumed)
-+	  ? conn->ssl.MBEDTLS_PRIVATE(peer_verify_data)
-+	  : conn->ssl.MBEDTLS_PRIVATE(own_verify_data);
-+	if (verify_len && verify_len <= max_len) {
-+		os_memcpy(buf, verify_data, verify_len);
-+		return (int)verify_len;
-+	}
-+  #endif
-+	return -1;
-+}
-+#endif
-+
-+
-+__attribute_noinline__
-+static void tls_mbedtls_set_peer_subject(struct tls_connection *conn, const mbedtls_x509_crt *crt)
-+{
-+	if (conn->peer_subject)
-+		return;
-+	char buf[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
-+	int buflen = mbedtls_x509_dn_gets(buf, sizeof(buf), &crt->subject);
-+	if (buflen >= 0 && (conn->peer_subject = os_malloc((size_t)buflen+1)))
-+		os_memcpy(conn->peer_subject, buf, (size_t)buflen+1);
-+}
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_TEAP
-+const char * tls_connection_get_peer_subject(struct tls_connection *conn)
-+{
-+	if (!conn)
-+		return NULL;
-+	if (!conn->peer_subject) { /*(if not set during cert verify)*/
-+		const mbedtls_x509_crt *peer_cert =
-+		  mbedtls_ssl_get_peer_cert(&conn->ssl);
-+		if (peer_cert)
-+			tls_mbedtls_set_peer_subject(conn, peer_cert);
-+	}
-+	return conn->peer_subject;
-+}
-+#endif
-+
-+
-+#ifdef TLS_MBEDTLS_EAP_TEAP
-+bool tls_connection_get_own_cert_used(struct tls_connection *conn)
-+{
-+	/* XXX: availability of cert does not necessary mean that client
-+	 * received certificate request from server and then sent cert.
-+	 * ? step handshake in tls_connection_handshake() looking for
-+	 *   MBEDTLS_SSL_CERTIFICATE_REQUEST ? */
-+	const struct tls_conf * const tls_conf = conn->tls_conf;
-+	return (tls_conf->has_client_cert && tls_conf->has_private_key);
-+}
-+#endif
-+
-+
-+#if defined(CONFIG_FIPS)
-+#define TLS_MBEDTLS_CONFIG_FIPS
-+#endif
-+
-+#if defined(CONFIG_SHA256)
-+#define TLS_MBEDTLS_TLS_PRF_SHA256
-+#endif
-+
-+#if defined(CONFIG_SHA384)
-+#define TLS_MBEDTLS_TLS_PRF_SHA384
-+#endif
-+
-+
-+#ifndef TLS_MBEDTLS_CONFIG_FIPS
-+#if defined(CONFIG_MODULE_TESTS)
-+/* unused with CONFIG_TLS=mbedtls except in crypto_module_tests.c */
-+#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */ \
-+ && MBEDTLS_VERSION_NUMBER <  0x03000000 /* mbedtls 3.0.0 */
-+/* sha1-tlsprf.c */
-+#include "sha1.h"
-+int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
-+		     const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
-+{
-+	return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_TLS1,
-+				   secret, secret_len, label,
-+				   seed, seed_len, out, outlen) ? -1 : 0;
-+}
-+#else
-+#include "sha1-tlsprf.c" /* pull in hostap local implementation */
-+#endif
-+#endif
-+#endif
-+
-+#ifdef TLS_MBEDTLS_TLS_PRF_SHA256
-+/* sha256-tlsprf.c */
-+#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+#include "sha256.h"
-+int tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label,
-+		   const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
-+{
-+	return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA256,
-+				   secret, secret_len, label,
-+				   seed, seed_len, out, outlen) ? -1 : 0;
-+}
-+#else
-+#include "sha256-tlsprf.c" /* pull in hostap local implementation */
-+#endif
-+#endif
-+
-+#ifdef TLS_MBEDTLS_TLS_PRF_SHA384
-+/* sha384-tlsprf.c */
-+#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
-+#include "sha384.h"
-+int tls_prf_sha384(const u8 *secret, size_t secret_len, const char *label,
-+		   const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
-+{
-+	return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA384,
-+				   secret, secret_len, label,
-+				   seed, seed_len, out, outlen) ? -1 : 0;
-+}
-+#else
-+#include "sha384-tlsprf.c" /* pull in hostap local implementation */
-+#endif
-+#endif
-+
-+
-+#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
-+#define mbedtls_x509_crt_has_ext_type(crt, ext_type) \
-+        ((crt)->MBEDTLS_PRIVATE(ext_types) & (ext_type))
-+#endif
-+
-+struct mlist { const char *p; size_t n; };
-+
-+
-+static int
-+tls_mbedtls_match_altsubject(mbedtls_x509_crt *crt, const char *match)
-+{
-+	/* RFE: this could be pre-parsed into structured data at config time */
-+	struct mlist list[256]; /*(much larger than expected)*/
-+	int nlist = 0;
-+	if (   os_strncmp(match, "EMAIL:", 6) != 0
-+	    && os_strncmp(match, "DNS:",   4) != 0
-+	    && os_strncmp(match, "URI:",   4) != 0   ) {
-+		wpa_printf(MSG_INFO, "MTLS: Invalid altSubjectName match '%s'", match);
-+		return 0;
-+	}
-+	for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
-+		do { } while ((tok = os_strchr(s, ';'))
-+		              && os_strncmp(tok+1, "EMAIL:", 6) != 0
-+		              && os_strncmp(tok+1, "DNS:",   4) != 0
-+		              && os_strncmp(tok+1, "URI:",   4) != 0);
-+		list[nlist].p = s;
-+		list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
-+		if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
-+			wpa_printf(MSG_INFO, "MTLS: excessive altSubjectName match '%s'",
-+			           match);
-+			break; /* truncate huge list and continue */
-+		}
-+	}
-+
-+	if (!mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
-+		return 0;
-+
-+	const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
-+	for (; cur != NULL; cur = cur->next) {
-+		const unsigned char san_type = (unsigned char)cur->buf.tag
-+		                             & MBEDTLS_ASN1_TAG_VALUE_MASK;
-+		char t;
-+		size_t step = 4;
-+		switch (san_type) {             /* "EMAIL:" or "DNS:" or "URI:" */
-+		case MBEDTLS_X509_SAN_RFC822_NAME:       step = 6; t = 'E'; break;
-+		case MBEDTLS_X509_SAN_DNS_NAME:                    t = 'D'; break;
-+		case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: t = 'U'; break;
-+		default: continue;
-+		}
-+
-+		for (int i = 0; i < nlist; ++i) {
-+			/* step over "EMAIL:" or "DNS:" or "URI:" in list[i].p */
-+			/* Note: v is not '\0'-terminated, but is a known length vlen,
-+			 * so okay to pass to os_strncasecmp() even though not z-string */
-+			if (cur->buf.len == list[i].n - step && t == *list[i].p
-+			    && 0 == os_strncasecmp((char *)cur->buf.p,
-+			                           list[i].p+step, cur->buf.len)) {
-+				return 1; /* match */
-+			}
-+		}
-+	}
-+	return 0; /* no match */
-+}
-+
-+
-+static int
-+tls_mbedtls_match_suffix(const char *v, size_t vlen,
-+                         const struct mlist *list, int nlist, int full)
-+{
-+	/* Note: v is not '\0'-terminated, but is a known length vlen,
-+	 * so okay to pass to os_strncasecmp() even though not z-string */
-+	for (int i = 0; i < nlist; ++i) {
-+		size_t n = list[i].n;
-+		if ((n == vlen || (n < vlen && v[vlen-n-1] == '.' && !full))
-+		    && 0 == os_strncasecmp(v+vlen-n, list[i].p, n))
-+			return 1; /* match */
-+	}
-+	return 0; /* no match */
-+}
-+
-+
-+static int
-+tls_mbedtls_match_suffixes(mbedtls_x509_crt *crt, const char *match, int full)
-+{
-+	/* RFE: this could be pre-parsed into structured data at config time */
-+	struct mlist list[256]; /*(much larger than expected)*/
-+	int nlist = 0;
-+	for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
-+		tok = os_strchr(s, ';');
-+		list[nlist].p = s;
-+		list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
-+		if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
-+			wpa_printf(MSG_INFO, "MTLS: excessive suffix match '%s'", match);
-+			break; /* truncate huge list and continue */
-+		}
-+	}
-+
-+	/* check subjectAltNames */
-+	if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME)) {
-+		const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
-+		for (; cur != NULL; cur = cur->next) {
-+			const unsigned char san_type = (unsigned char)cur->buf.tag
-+			                             & MBEDTLS_ASN1_TAG_VALUE_MASK;
-+			if (san_type == MBEDTLS_X509_SAN_DNS_NAME
-+			    && tls_mbedtls_match_suffix((char *)cur->buf.p,
-+			                                cur->buf.len,
-+			                                list, nlist, full)) {
-+				return 1; /* match */
-+			}
-+		}
-+	}
-+
-+	/* check subject CN */
-+	const mbedtls_x509_name *name = &crt->subject;
-+	for (; name != NULL; name = name->next) {
-+		if (name->oid.p && MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid) == 0)
-+			break;
-+	}
-+	if (name && tls_mbedtls_match_suffix((char *)name->val.p, name->val.len,
-+	                                     list, nlist, full)) {
-+		return 1; /* match */
-+	}
-+
-+	return 0; /* no match */
-+}
-+
-+
-+static int
-+tls_mbedtls_match_dn_field(mbedtls_x509_crt *crt, const char *match)
-+{
-+	/* RFE: this could be pre-parsed into structured data at config time */
-+	struct mlistoid { const char *p; size_t n;
-+	                  const char *oid; size_t olen;
-+	                  int prefix; };
-+	struct mlistoid list[32]; /*(much larger than expected)*/
-+	int nlist = 0;
-+	for (const char *s = match, *tok, *e; *s; s = tok ? tok+1 : "") {
-+		tok = os_strchr(s, '/');
-+		list[nlist].oid = NULL;
-+		list[nlist].olen = 0;
-+		list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
-+		e = memchr(s, '=', list[nlist].n);
-+		if (e == NULL) {
-+			if (list[nlist].n == 0)
-+				continue; /* skip consecutive, repeated '/' */
-+			if (list[nlist].n == 1 && *s == '*') {
-+				/* special-case "*" to match any OID and value */
-+				s = e = "=*";
-+				list[nlist].n = 2;
-+				list[nlist].oid = "";
-+			}
-+			else {
-+				wpa_printf(MSG_INFO,
-+				           "MTLS: invalid check_cert_subject '%s' missing '='",
-+				           match);
-+				return 0;
-+			}
-+		}
-+		switch (e - s) {
-+		case 1:
-+			if (*s == 'C') {
-+				list[nlist].oid  = MBEDTLS_OID_AT_COUNTRY;
-+				list[nlist].olen = sizeof(MBEDTLS_OID_AT_COUNTRY)-1;
-+			}
-+			else if (*s == 'L') {
-+				list[nlist].oid  = MBEDTLS_OID_AT_LOCALITY;
-+				list[nlist].olen = sizeof(MBEDTLS_OID_AT_LOCALITY)-1;
-+			}
-+			else if (*s == 'O') {
-+				list[nlist].oid  = MBEDTLS_OID_AT_ORGANIZATION;
-+				list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORGANIZATION)-1;
-+			}
-+			break;
-+		case 2:
-+			if (s[0] == 'C' && s[1] == 'N') {
-+				list[nlist].oid  = MBEDTLS_OID_AT_CN;
-+				list[nlist].olen = sizeof(MBEDTLS_OID_AT_CN)-1;
-+			}
-+			else if (s[0] == 'S' && s[1] == 'T') {
-+				list[nlist].oid  = MBEDTLS_OID_AT_STATE;
-+				list[nlist].olen = sizeof(MBEDTLS_OID_AT_STATE)-1;
-+			}
-+			else if (s[0] == 'O' && s[1] == 'U') {
-+				list[nlist].oid  = MBEDTLS_OID_AT_ORG_UNIT;
-+				list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORG_UNIT)-1;
-+			}
-+			break;
-+		case 12:
-+			if (os_memcmp(s, "emailAddress", 12) == 0) {
-+				list[nlist].oid  = MBEDTLS_OID_PKCS9_EMAIL;
-+				list[nlist].olen = sizeof(MBEDTLS_OID_PKCS9_EMAIL)-1;
-+			}
-+			break;
-+		default:
-+			break;
-+		}
-+		if (list[nlist].oid == NULL) {
-+			wpa_printf(MSG_INFO,
-+			           "MTLS: Unknown field in check_cert_subject '%s'",
-+			           match);
-+			return 0;
-+		}
-+		list[nlist].n -= (size_t)(++e - s);
-+		list[nlist].p = e;
-+		if (list[nlist].n && e[list[nlist].n-1] == '*') {
-+			--list[nlist].n;
-+			list[nlist].prefix = 1;
-+		}
-+		/*(could easily add support for suffix matches if value begins with '*',
-+		 * but suffix match is not currently supported by other TLS modules)*/
-+
-+		if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
-+			wpa_printf(MSG_INFO,
-+			           "MTLS: excessive check_cert_subject match '%s'",
-+			           match);
-+			break; /* truncate huge list and continue */
-+		}
-+	}
-+
-+	/* each component in match string must match cert Subject in order listed
-+	 * The behavior below preserves ordering but is slightly different than
-+	 * the grossly inefficient contortions implemented in tls_openssl.c */
-+	const mbedtls_x509_name *name = &crt->subject;
-+	for (int i = 0; i < nlist; ++i) {
-+		int found = 0;
-+		for (; name != NULL && !found; name = name->next) {
-+			if (!name->oid.p)
-+				continue;
-+			/* special-case "*" to match any OID and value */
-+			if (list[i].olen == 0) {
-+				found = 1;
-+				continue;
-+			}
-+			/* perform equalent of !MBEDTLS_OID_CMP() with oid ptr and len */
-+			if (list[i].olen != name->oid.len
-+			    || os_memcmp(list[i].oid, name->oid.p, name->oid.len) != 0)
-+				continue;
-+			/* Note: v is not '\0'-terminated, but is a known length vlen,
-+			 * so okay to pass to os_strncasecmp() even though not z-string */
-+			if ((list[i].prefix
-+			      ? list[i].n <= name->val.len  /* prefix match */
-+			      : list[i].n == name->val.len) /* full match */
-+			    && 0 == os_strncasecmp((char *)name->val.p,
-+			                           list[i].p, list[i].n)) {
-+				found = 1;
-+				continue;
-+			}
-+		}
-+		if (!found)
-+			return 0; /* no match */
-+	}
-+	return 1; /* match */
-+}
-+
-+
-+__attribute_cold__
-+static void
-+tls_mbedtls_verify_fail_event (mbedtls_x509_crt *crt, int depth,
-+                               const char *errmsg, enum tls_fail_reason reason)
-+{
-+	struct tls_config *init_conf = &tls_ctx_global.init_conf;
-+	if (init_conf->event_cb == NULL)
-+		return;
-+
-+	struct wpabuf *certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
-+	char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
-+	if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
-+		subject[0] = '\0';
-+	union tls_event_data ev;
-+	os_memset(&ev, 0, sizeof(ev));
-+	ev.cert_fail.reason = reason;
-+	ev.cert_fail.depth = depth;
-+	ev.cert_fail.subject = subject;
-+	ev.cert_fail.reason_txt = errmsg;
-+	ev.cert_fail.cert = certbuf;
-+
-+	init_conf->event_cb(init_conf->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
-+
-+	wpabuf_free(certbuf);
-+}
-+
-+
-+__attribute_noinline__
-+static void
-+tls_mbedtls_verify_cert_event (struct tls_connection *conn,
-+                               mbedtls_x509_crt *crt, int depth)
-+{
-+	struct tls_config *init_conf = &tls_ctx_global.init_conf;
-+	if (init_conf->event_cb == NULL)
-+		return;
-+
-+	struct wpabuf *certbuf = NULL;
-+	union tls_event_data ev;
-+	os_memset(&ev, 0, sizeof(ev));
-+
-+  #ifdef MBEDTLS_SHA256_C
-+	u8 hash[SHA256_DIGEST_LENGTH];
-+	const u8 *addr[] = { (u8 *)crt->raw.p };
-+	if (sha256_vector(1, addr, &crt->raw.len, hash) == 0) {
-+		ev.peer_cert.hash = hash;
-+		ev.peer_cert.hash_len = sizeof(hash);
-+	}
-+  #endif
-+	ev.peer_cert.depth = depth;
-+	char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
-+	if (depth == 0)
-+		ev.peer_cert.subject = conn->peer_subject;
-+	if (ev.peer_cert.subject == NULL) {
-+		ev.peer_cert.subject = subject;
-+		if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
-+			subject[0] = '\0';
-+	}
-+
-+	char serial_num[128+1];
-+	ev.peer_cert.serial_num =
-+	  tls_mbedtls_peer_serial_num(crt, serial_num, sizeof(serial_num));
-+
-+	const mbedtls_x509_sequence *cur;
-+
-+	cur = NULL;
-+	if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
-+		cur = &crt->subject_alt_names;
-+	for (; cur != NULL; cur = cur->next) {
-+		const unsigned char san_type = (unsigned char)cur->buf.tag
-+		                             & MBEDTLS_ASN1_TAG_VALUE_MASK;
-+		size_t prelen = 4;
-+		const char *pre;
-+		switch (san_type) {
-+		case MBEDTLS_X509_SAN_RFC822_NAME:     prelen = 6; pre = "EMAIL:";break;
-+		case MBEDTLS_X509_SAN_DNS_NAME:                    pre = "DNS:";  break;
-+		case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: pre = "URI:";  break;
-+		default: continue;
-+		}
-+
-+		char *pos = os_malloc(prelen + cur->buf.len + 1);
-+		if (pos == NULL)
-+			break;
-+		ev.peer_cert.altsubject[ev.peer_cert.num_altsubject] = pos;
-+		os_memcpy(pos, pre, prelen);
-+		/* data should be properly backslash-escaped if needed,
-+		 * so code below does not re-escape, but does replace CTLs */
-+		/*os_memcpy(pos+prelen, cur->buf.p, cur->buf.len);*/
-+		/*pos[prelen+cur->buf.len] = '\0';*/
-+		pos += prelen;
-+		for (size_t i = 0; i < cur->buf.len; ++i) {
-+			unsigned char c = cur->buf.p[i];
-+			*pos++ = (c >= 32 && c != 127) ? c : '?';
-+		}
-+		*pos = '\0';
-+
-+		if (++ev.peer_cert.num_altsubject == TLS_MAX_ALT_SUBJECT)
-+			break;
-+	}
-+
-+	cur = NULL;
-+	if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_CERTIFICATE_POLICIES))
-+		cur = &crt->certificate_policies;
-+	for (; cur != NULL; cur = cur->next) {
-+		if (cur->buf.len != 11) /* len of OID_TOD_STRICT or OID_TOD_TOFU */
-+			continue;
-+		/* TOD-STRICT "1.3.6.1.4.1.40808.1.3.1" */
-+		/* TOD-TOFU   "1.3.6.1.4.1.40808.1.3.2" */
-+		#define OID_TOD_STRICT "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x01"
-+		#define OID_TOD_TOFU   "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x02"
-+		if (os_memcmp(cur->buf.p,
-+		              OID_TOD_STRICT, sizeof(OID_TOD_STRICT)-1) == 0) {
-+			ev.peer_cert.tod = 1; /* TOD-STRICT */
-+			break;
-+		}
-+		if (os_memcmp(cur->buf.p,
-+		              OID_TOD_TOFU, sizeof(OID_TOD_TOFU)-1) == 0) {
-+			ev.peer_cert.tod = 2; /* TOD-TOFU */
-+			break;
-+		}
-+	}
-+
-+	struct tls_conf *tls_conf = conn->tls_conf;
-+	if (tls_conf->ca_cert_probe || (tls_conf->flags & TLS_CONN_EXT_CERT_CHECK)
-+	    || init_conf->cert_in_cb) {
-+		certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
-+		ev.peer_cert.cert = certbuf;
-+	}
-+
-+	init_conf->event_cb(init_conf->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
-+
-+	wpabuf_free(certbuf);
-+	char **altsubject;
-+	*(const char ***)&altsubject = ev.peer_cert.altsubject;
-+	for (size_t i = 0; i < ev.peer_cert.num_altsubject; ++i)
-+		os_free(altsubject[i]);
-+}
-+
-+
-+static int
-+tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
-+{
-+	/* XXX: N.B. verify code not carefully tested besides hwsim tests
-+	 *
-+	 * RFE: mbedtls_x509_crt_verify_info() and enhance log trace messages
-+	 * RFE: review and add support for additional TLS_CONN_* flags
-+	 * not handling OCSP (not available in mbedtls)
-+	 * ... */
-+
-+	struct tls_connection *conn = (struct tls_connection *)arg;
-+	struct tls_conf *tls_conf = conn->tls_conf;
-+	uint32_t flags_in = *flags;
-+
-+	if (depth > 8) { /*(depth 8 picked as arbitrary limit)*/
-+		emsg(MSG_WARNING, "client cert chain too long");
-+		*flags |= MBEDTLS_X509_BADCERT_OTHER; /* cert chain too long */
-+		tls_mbedtls_verify_fail_event(crt, depth,
-+			                      "client cert chain too long",
-+		                              TLS_FAIL_BAD_CERTIFICATE);
-+	}
-+	else if (tls_conf->verify_depth0_only) {
-+		if (depth > 0)
-+			*flags = 0;
-+		else {
-+		  #ifdef MBEDTLS_SHA256_C
-+			u8 hash[SHA256_DIGEST_LENGTH];
-+			const u8 *addr[] = { (u8 *)crt->raw.p };
-+			if (sha256_vector(1, addr, &crt->raw.len, hash) < 0
-+			    || os_memcmp(tls_conf->ca_cert_hash, hash, sizeof(hash)) != 0) {
-+				*flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED;
-+				tls_mbedtls_verify_fail_event(crt, depth,
-+			                                      "cert hash mismatch",
-+				                              TLS_FAIL_UNTRUSTED);
-+			}
-+			else /* hash matches; ignore other issues *except* if revoked)*/
-+				*flags &= MBEDTLS_X509_BADCERT_REVOKED;
-+		  #endif
-+		}
-+	}
-+	else if (depth == 0) {
-+		if (!conn->peer_subject)
-+			tls_mbedtls_set_peer_subject(conn, crt);
-+		/*(use same labels to tls_mbedtls_verify_fail_event() as used in
-+		 * other TLS modules so that hwsim tests find exact string match)*/
-+		if (!conn->peer_subject) { /* error copying subject string */
-+			*flags |= MBEDTLS_X509_BADCERT_OTHER;
-+			tls_mbedtls_verify_fail_event(crt, depth,
-+			                              "internal error",
-+			                              TLS_FAIL_UNSPECIFIED);
-+		}
-+		/*(use os_strstr() for subject match as is done in tls_mbedtls.c
-+		 * to follow the same behavior, even though a suffix match would
-+		 * make more sense.  Also, note that strstr match does not
-+		 * normalize whitespace (between components) for comparison)*/
-+		else if (tls_conf->subject_match
-+		         && os_strstr(conn->peer_subject,
-+		                      tls_conf->subject_match) == NULL) {
-+			wpa_printf(MSG_WARNING,
-+			           "MTLS: Subject '%s' did not match with '%s'",
-+			           conn->peer_subject, tls_conf->subject_match);
-+			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+			tls_mbedtls_verify_fail_event(crt, depth,
-+			                              "Subject mismatch",
-+			                              TLS_FAIL_SUBJECT_MISMATCH);
-+		}
-+		if (tls_conf->altsubject_match
-+		    && !tls_mbedtls_match_altsubject(crt, tls_conf->altsubject_match)) {
-+			wpa_printf(MSG_WARNING,
-+				   "MTLS: altSubjectName match '%s' not found",
-+			           tls_conf->altsubject_match);
-+			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+			tls_mbedtls_verify_fail_event(crt, depth,
-+			                              "AltSubject mismatch",
-+			                              TLS_FAIL_ALTSUBJECT_MISMATCH);
-+		}
-+		if (tls_conf->suffix_match
-+		    && !tls_mbedtls_match_suffixes(crt, tls_conf->suffix_match, 0)) {
-+			wpa_printf(MSG_WARNING,
-+			           "MTLS: Domain suffix match '%s' not found",
-+				   tls_conf->suffix_match);
-+			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+			tls_mbedtls_verify_fail_event(crt, depth,
-+			                              "Domain suffix mismatch",
-+			                              TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
-+		}
-+		if (tls_conf->domain_match
-+		    && !tls_mbedtls_match_suffixes(crt, tls_conf->domain_match, 1)) {
-+			wpa_printf(MSG_WARNING,
-+			           "MTLS: Domain match '%s' not found",
-+				   tls_conf->domain_match);
-+			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+			tls_mbedtls_verify_fail_event(crt, depth,
-+			                              "Domain mismatch",
-+			                              TLS_FAIL_DOMAIN_MISMATCH);
-+		}
-+		if (tls_conf->check_cert_subject
-+		    && !tls_mbedtls_match_dn_field(crt, tls_conf->check_cert_subject)) {
-+			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
-+			tls_mbedtls_verify_fail_event(crt, depth,
-+			                              "Distinguished Name",
-+			                              TLS_FAIL_DN_MISMATCH);
-+		}
-+		if (tls_conf->flags & TLS_CONN_SUITEB) {
-+			/* check RSA modulus size (public key bitlen) */
-+			const mbedtls_pk_type_t pk_alg = mbedtls_pk_get_type(&crt->pk);
-+			if ((pk_alg == MBEDTLS_PK_RSA || pk_alg == MBEDTLS_PK_RSASSA_PSS)
-+			    && mbedtls_pk_get_bitlen(&crt->pk) < 3072) {
-+				/* hwsim suite_b RSA tests expect 3072
-+				 *   suite_b_192_rsa_ecdhe_radius_rsa2048_client
-+				 *   suite_b_192_rsa_dhe_radius_rsa2048_client */
-+				*flags |= MBEDTLS_X509_BADCERT_BAD_KEY;
-+				tls_mbedtls_verify_fail_event(crt, depth,
-+				                              "Insufficient RSA modulus size",
-+				                              TLS_FAIL_INSUFFICIENT_KEY_LEN);
-+			}
-+		}
-+		if (tls_conf->check_crl && tls_conf->crl == NULL) {
-+			/* see tests/hwsim test_ap_eap.py ap_wpa2_eap_tls_check_crl */
-+			emsg(MSG_WARNING, "check_crl set but no CRL loaded; reject all?");
-+			*flags |= MBEDTLS_X509_BADCERT_OTHER;
-+			tls_mbedtls_verify_fail_event(crt, depth,
-+				                      "check_crl set but no CRL loaded; "
-+			                              "reject all?",
-+			                              TLS_FAIL_BAD_CERTIFICATE);
-+		}
-+	}
-+	else {
-+		if (tls_conf->check_crl != 2) /* 2 == verify CRLs for all certs */
-+			*flags &= ~MBEDTLS_X509_BADCERT_REVOKED;
-+	}
-+
-+	if (!tls_conf->check_crl_strict) {
-+		*flags &= ~MBEDTLS_X509_BADCRL_EXPIRED;
-+		*flags &= ~MBEDTLS_X509_BADCRL_FUTURE;
-+	}
-+
-+	if (tls_conf->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
-+		*flags &= ~MBEDTLS_X509_BADCERT_EXPIRED;
-+		*flags &= ~MBEDTLS_X509_BADCERT_FUTURE;
-+	}
-+
-+	tls_mbedtls_verify_cert_event(conn, crt, depth);
-+
-+	if (*flags) {
-+		if (*flags & (MBEDTLS_X509_BADCERT_NOT_TRUSTED
-+		             |MBEDTLS_X509_BADCERT_CN_MISMATCH
-+		             |MBEDTLS_X509_BADCERT_REVOKED)) {
-+			emsg(MSG_WARNING, "client cert not trusted");
-+		}
-+		/* report event if flags set but no additional flags set above */
-+		/* (could translate flags to more detailed TLS_FAIL_* if needed) */
-+		if (!(*flags & ~flags_in)) {
-+			enum tls_fail_reason reason = TLS_FAIL_UNSPECIFIED;
-+			const char *errmsg = "cert verify fail unspecified";
-+			if (*flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) {
-+				reason = TLS_FAIL_UNTRUSTED;
-+				errmsg = "certificate not trusted";
-+			}
-+			if (*flags & MBEDTLS_X509_BADCERT_REVOKED) {
-+				reason = TLS_FAIL_REVOKED;
-+				errmsg = "certificate has been revoked";
-+			}
-+			if (*flags & MBEDTLS_X509_BADCERT_FUTURE) {
-+				reason = TLS_FAIL_NOT_YET_VALID;
-+				errmsg = "certificate not yet valid";
-+			}
-+			if (*flags & MBEDTLS_X509_BADCERT_EXPIRED) {
-+				reason = TLS_FAIL_EXPIRED;
-+				errmsg = "certificate has expired";
-+			}
-+			if (*flags & MBEDTLS_X509_BADCERT_BAD_MD) {
-+				reason = TLS_FAIL_BAD_CERTIFICATE;
-+				errmsg = "certificate uses insecure algorithm";
-+			}
-+			tls_mbedtls_verify_fail_event(crt, depth, errmsg, reason);
-+		}
-+	  #if 0
-+		/* ??? send (again) cert events for all certs in chain ???
-+		 * (should already have been called for greater depths) */
-+		/* tls_openssl.c:tls_verify_cb() sends cert events for all certs
-+		 * in chain if certificate validation fails, but sends all events
-+		 * with depth set to 0 (might be a bug) */
-+		if (depth > 0) {
-+			int pdepth = depth + 1;
-+			for (mbedtls_x509_crt *pcrt; (pcrt = crt->next); ++pdepth) {
-+				tls_mbedtls_verify_cert_event(conn, pcrt, pdepth);
-+			}
-+		}
-+	  #endif
-+		/*(do not preserve subject if verification failed but was optional)*/
-+		if (depth == 0 && conn->peer_subject) {
-+			os_free(conn->peer_subject);
-+			conn->peer_subject = NULL;
-+		}
-+	}
-+	else if (depth == 0) {
-+		struct tls_config *init_conf = &tls_ctx_global.init_conf;
-+		if (tls_conf->ca_cert_probe) {
-+			/* reject server certificate on probe-only run */
-+			*flags |= MBEDTLS_X509_BADCERT_OTHER;
-+			tls_mbedtls_verify_fail_event(crt, depth,
-+			                              "server chain probe",
-+			                              TLS_FAIL_SERVER_CHAIN_PROBE);
-+		}
-+		else if (init_conf->event_cb) {
-+			/* ??? send event as soon as depth == 0 is verified ???
-+			 * What about rest of chain?
-+			 * Follows tls_mbedtls.c behavior: */
-+			init_conf->event_cb(init_conf->cb_ctx,
-+			                    TLS_CERT_CHAIN_SUCCESS, NULL);
-+		}
-+	}
-+
-+	return 0;
-+}
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index e672a1787..3e3e309f4 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -971,6 +971,9 @@ struct wpa_driver_associate_params {
- 	 * responsible for selecting with which BSS to associate. */
- 	const u8 *bssid;
- 
-+	unsigned char rates[32];
-+	int mcast_rate;
-+
- 	/**
- 	 * bssid_hint - BSSID of a proposed AP
- 	 *
-@@ -1873,6 +1876,7 @@ struct wpa_driver_mesh_join_params {
- #define WPA_DRIVER_MESH_FLAG_AMPE	0x00000008
- 	unsigned int flags;
- 	bool handle_dfs;
-+	int mcast_rate;
- };
- 
- struct wpa_driver_set_key_params {
-@@ -2340,6 +2344,9 @@ struct wpa_driver_capa {
- 	/** Maximum number of iterations in a single scan plan */
- 	u32 max_sched_scan_plan_iterations;
- 
-+	/** Maximum number of extra IE bytes for scans */
-+	u16 max_scan_ie_len;
-+
- 	/** Whether sched_scan (offloaded scanning) is supported */
- 	int sched_scan_supported;
- 
-@@ -3861,6 +3868,25 @@ struct wpa_driver_ops {
- 	int (*if_remove)(void *priv, enum wpa_driver_if_type type,
- 			 const char *ifname);
- 
-+	/**
-+	 * if_rename - Rename a virtual interface
-+	 * @priv: Private driver interface data
-+	 * @type: Interface type
-+	 * @ifname: Interface name of the virtual interface to be renamed
-+	 *	    (NULL when renaming the AP BSS interface)
-+	 * @new_name: New interface name of the virtual interface
-+	 * Returns: 0 on success, -1 on failure
-+	 */
-+	int (*if_rename)(void *priv, enum wpa_driver_if_type type,
-+			 const char *ifname, const char *new_name);
-+
-+	/**
-+	 * set_first_bss - Make a virtual interface the first (primary) bss
-+	 * @priv: Private driver interface data
-+	 * Returns: 0 on success, -1 on failure
-+	 */
-+	int (*set_first_bss)(void *priv);
-+
- 	/**
- 	 * set_sta_vlan - Bind a station into a specific interface (AP only)
- 	 * @priv: Private driver interface data
-@@ -4265,7 +4291,7 @@ struct wpa_driver_ops {
- 	 * Returns: 0 on success, negative (<0) on failure
- 	 */
- 	int (*br_set_net_param)(void *priv, enum drv_br_net_param param,
--				unsigned int val);
-+				const char *ifname, unsigned int val);
- 
- 	/**
- 	 * get_wowlan - Get wake-on-wireless status
-@@ -6559,6 +6585,7 @@ union wpa_event_data {
- 
- 	/**
- 	 * struct ch_switch
-+	 * @count: Count until channel switch activates
- 	 * @freq: Frequency of new channel in MHz
- 	 * @ht_enabled: Whether this is an HT channel
- 	 * @ch_offset: Secondary channel offset
-@@ -6569,6 +6596,7 @@ union wpa_event_data {
- 	 * @punct_bitmap: Puncturing bitmap
- 	 */
- 	struct ch_switch {
-+		int count;
- 		int freq;
- 		int ht_enabled;
- 		int ch_offset;
-@@ -6816,8 +6844,8 @@ union wpa_event_data {
-  * Driver wrapper code should call this function whenever an event is received
-  * from the driver.
-  */
--void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
--			  union wpa_event_data *data);
-+extern void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
-+				    union wpa_event_data *data);
- 
- /**
-  * wpa_supplicant_event_global - Report a driver event for wpa_supplicant
-@@ -6829,7 +6857,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
-  * Same as wpa_supplicant_event(), but we search for the interface in
-  * wpa_global.
-  */
--void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
-+extern void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
- 				 union wpa_event_data *data);
- 
- /*
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 9ac621ae6..6778ad369 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -78,6 +78,16 @@ enum nlmsgerr_attrs {
- 
- #endif /* ANDROID */
- 
-+static void handle_nl_debug_hook(struct nl_msg *msg, int tx)
-+{
-+	const struct nlmsghdr *nlh;
-+
-+	if (!wpa_netlink_hook)
-+		return;
-+
-+	nlh = nlmsg_hdr(msg);
-+	wpa_netlink_hook(tx, nlh, nlh->nlmsg_len);
-+}
- 
- static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
- {
-@@ -432,6 +442,11 @@ static int no_seq_check(struct nl_msg *msg, void *arg)
- 	return NL_OK;
- }
- 
-+static int debug_handler(struct nl_msg *msg, void *arg)
-+{
-+	handle_nl_debug_hook(msg, 0);
-+	return NL_OK;
-+}
- 
- static void nl80211_nlmsg_clear(struct nl_msg *msg)
- {
-@@ -505,6 +520,7 @@ int send_and_recv(struct nl80211_global *global,
- 	if (!msg)
- 		return -ENOMEM;
- 
-+	handle_nl_debug_hook(msg, 1);
- 	err.err = -ENOMEM;
- 
- 	s_nl_cb = nl_socket_get_cb(nl_handle);
-@@ -539,6 +555,7 @@ int send_and_recv(struct nl80211_global *global,
- 	err.orig_msg = msg;
- 	err.err_info = err_info;
- 
-+	nl_cb_set(cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
- 	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
- 	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err.err);
- 	if (ack_handler_custom) {
-@@ -942,6 +959,7 @@ nl80211_get_wiphy_data_ap(struct i802_bss *bss)
- 			os_free(w);
- 			return NULL;
- 		}
-+		nl_cb_set(w->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
- 		nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
- 			  no_seq_check, NULL);
- 		nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
-@@ -1356,7 +1374,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
- 		}
- 		wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
- 			   namebuf, ifname);
--		if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
-+		if (drv->first_bss->ifindex != ifi->ifi_index) {
- 			wpa_printf(MSG_DEBUG,
- 				   "nl80211: Not the main interface (%s) - do not indicate interface down",
- 				   drv->first_bss->ifname);
-@@ -1392,7 +1410,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
- 		}
- 		wpa_printf(MSG_DEBUG, "nl80211: Interface up (%s/%s)",
- 			   namebuf, ifname);
--		if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
-+		if (drv->first_bss->ifindex != ifi->ifi_index) {
- 			wpa_printf(MSG_DEBUG,
- 				   "nl80211: Not the main interface (%s) - do not indicate interface up",
- 				   drv->first_bss->ifname);
-@@ -2038,6 +2056,7 @@ static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
- 	genl_family_put(family);
- 	nl_cache_free(cache);
- 
-+	nl_cb_set(global->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
- 	nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
- 		  no_seq_check, NULL);
- 	nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
-@@ -2208,6 +2227,7 @@ static int nl80211_init_bss(struct i802_bss *bss)
- 	if (!bss->nl_cb)
- 		return -1;
- 
-+	nl_cb_set(bss->nl_cb, NL_CB_MSG_IN, NL_CB_CUSTOM, debug_handler, NULL);
- 	nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
- 		  no_seq_check, NULL);
- 	nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
-@@ -5485,7 +5505,7 @@ static int nl80211_set_channel(struct i802_bss *bss,
- 		   freq->he_enabled, freq->eht_enabled, freq->bandwidth,
- 		   freq->center_freq1, freq->center_freq2);
- 
--	msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
-+	msg = nl80211_bss_msg(bss, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
- 			      NL80211_CMD_SET_WIPHY);
- 	if (!msg || nl80211_put_freq_params(msg, freq) < 0) {
- 		nlmsg_free(msg);
-@@ -5858,26 +5878,29 @@ fail:
- 
- static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
- {
--#ifdef CONFIG_LIBNL3_ROUTE
- 	struct wpa_driver_nl80211_data *drv = bss->drv;
--	struct rtnl_neigh *rn;
--	struct nl_addr *nl_addr;
-+	struct ndmsg nhdr = {
-+		.ndm_state = NUD_PERMANENT,
-+		.ndm_ifindex = bss->ifindex,
-+		.ndm_family = AF_BRIDGE,
-+	};
-+	struct nl_msg *msg;
- 	int err;
- 
--	rn = rtnl_neigh_alloc();
--	if (!rn)
-+	msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
-+	if (!msg)
- 		return;
- 
--	rtnl_neigh_set_family(rn, AF_BRIDGE);
--	rtnl_neigh_set_ifindex(rn, bss->ifindex);
--	nl_addr = nl_addr_build(AF_BRIDGE, (void *) addr, ETH_ALEN);
--	if (!nl_addr) {
--		rtnl_neigh_put(rn);
--		return;
--	}
--	rtnl_neigh_set_lladdr(rn, nl_addr);
-+	if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
-+		goto errout;
- 
--	err = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
-+	if (nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *)addr))
-+		goto errout;
-+
-+	if (nl_send_auto_complete(drv->rtnl_sk, msg) < 0)
-+		goto errout;
-+
-+	err = nl_wait_for_ack(drv->rtnl_sk);
- 	if (err < 0) {
- 		wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry delete for "
- 			   MACSTR " ifindex=%d failed: %s", MAC2STR(addr),
-@@ -5887,9 +5910,8 @@ static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
- 			   MACSTR, MAC2STR(addr));
- 	}
- 
--	nl_addr_put(nl_addr);
--	rtnl_neigh_put(rn);
--#endif /* CONFIG_LIBNL3_ROUTE */
-+errout:
-+	nlmsg_free(msg);
- }
- 
- 
-@@ -6178,6 +6200,8 @@ static void nl80211_teardown_ap(struct i802_bss *bss)
- 	nl80211_put_wiphy_data_ap(bss);
- 	if (bss->flink)
- 		bss->flink->beacon_set = 0;
-+
-+	wpa_driver_nl80211_del_beacon_all(bss);
- }
- 
- 
-@@ -8566,6 +8590,7 @@ static void *i802_init(struct hostapd_data *hapd,
- 	char master_ifname[IFNAMSIZ];
- 	int ifindex, br_ifindex = 0;
- 	int br_added = 0;
-+	int err;
- 
- 	bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
- 					  params->global_priv, 1,
-@@ -8625,24 +8650,18 @@ static void *i802_init(struct hostapd_data *hapd,
- 	    (params->num_bridge == 0 || !params->bridge[0]))
- 		add_ifidx(drv, br_ifindex, drv->ifindex);
- 
--#ifdef CONFIG_LIBNL3_ROUTE
--	if (bss->added_if_into_bridge || bss->already_in_bridge) {
--		int err;
--
--		drv->rtnl_sk = nl_socket_alloc();
--		if (drv->rtnl_sk == NULL) {
--			wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
--			goto failed;
--		}
-+	drv->rtnl_sk = nl_socket_alloc();
-+	if (drv->rtnl_sk == NULL) {
-+		wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
-+		goto failed;
-+	}
- 
--		err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
--		if (err) {
--			wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
--				   nl_geterror(err));
--			goto failed;
--		}
-+	err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
-+	if (err) {
-+		wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
-+			   nl_geterror(err));
-+		goto failed;
- 	}
--#endif /* CONFIG_LIBNL3_ROUTE */
- 
- 	if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
- 		wpa_printf(MSG_DEBUG,
-@@ -9000,6 +9019,7 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
- 		if (drv->first_bss->next) {
- 			drv->first_bss = drv->first_bss->next;
- 			drv->ctx = drv->first_bss->ctx;
-+			drv->ifindex = drv->first_bss->ifindex;
- 			os_free(bss);
- 		} else {
- 			wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to");
-@@ -9009,6 +9029,50 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
- 	return 0;
- }
- 
-+static int wpa_driver_nl80211_if_rename(struct i802_bss *bss,
-+					enum wpa_driver_if_type type,
-+					const char *ifname, const char *new_name)
-+{
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct ifinfomsg ifi = {
-+		.ifi_family = AF_UNSPEC,
-+		.ifi_index = bss->ifindex,
-+	};
-+	struct nl_msg *msg;
-+	int res = -ENOMEM;
-+
-+	if (ifname)
-+		ifi.ifi_index = if_nametoindex(ifname);
-+
-+	msg = nlmsg_alloc_simple(RTM_SETLINK, 0);
-+	if (!msg)
-+		return res;
-+
-+	if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
-+		goto out;
-+
-+	if (nla_put_string(msg, IFLA_IFNAME, new_name))
-+		goto out;
-+
-+	res = nl_send_auto_complete(drv->rtnl_sk, msg);
-+	if (res < 0)
-+		goto out;
-+
-+	res = nl_wait_for_ack(drv->rtnl_sk);
-+	if (res) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Renaming device %s to %s failed: %s",
-+			   ifname ? ifname : bss->ifname, new_name, nl_geterror(res));
-+		goto out;
-+	}
-+
-+	if (type == WPA_IF_AP_BSS && !ifname)
-+		os_strlcpy(bss->ifname, new_name, sizeof(bss->ifname));
-+
-+out:
-+	nlmsg_free(msg);
-+	return res;
-+}
- 
- static int cookie_handler(struct nl_msg *msg, void *arg)
- {
-@@ -10792,6 +10856,37 @@ static bool nl80211_is_drv_shared(void *priv, void *bss_ctx)
- #endif /* CONFIG_IEEE80211BE */
- 
- 
-+static int driver_nl80211_if_rename(void *priv, enum wpa_driver_if_type type,
-+				    const char *ifname, const char *new_name)
-+{
-+	struct i802_bss *bss = priv;
-+	return wpa_driver_nl80211_if_rename(bss, type, ifname, new_name);
-+}
-+
-+
-+static int driver_nl80211_set_first_bss(void *priv)
-+{
-+	struct i802_bss *bss = priv, *tbss;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+
-+	if (drv->first_bss == bss)
-+		return 0;
-+
-+	for (tbss = drv->first_bss; tbss; tbss = tbss->next) {
-+		if (tbss->next != bss)
-+			continue;
-+
-+		tbss->next = bss->next;
-+		bss->next = drv->first_bss;
-+		drv->first_bss = bss;
-+		drv->ctx = bss->ctx;
-+		return 0;
-+	}
-+
-+	return -1;
-+}
-+
-+
- static int driver_nl80211_send_mlme(void *priv, const u8 *data,
- 				    size_t data_len, int noack,
- 				    unsigned int freq,
-@@ -11294,6 +11389,10 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
- 	if (ret)
- 		goto error;
- 
-+	if (drv->nlmode == NL80211_IFTYPE_MESH_POINT) {
-+		nla_put_flag(msg, NL80211_ATTR_HANDLE_DFS);
-+	}
-+
- 	/* beacon_csa params */
- 	beacon_csa = nla_nest_start(msg, NL80211_ATTR_CSA_IES);
- 	if (!beacon_csa)
-@@ -11940,6 +12039,18 @@ static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id,
- }
- 
- 
-+static int nl80211_put_mcast_rate(struct nl_msg *msg, int mcast_rate)
-+{
-+	if (mcast_rate > 0) {
-+		wpa_printf(MSG_DEBUG, "  * mcast_rate=%.1f",
-+			   (double)mcast_rate / 10);
-+		return nla_put_u32(msg, NL80211_ATTR_MCAST_RATE, mcast_rate);
-+	}
-+
-+	return 0;
-+}
-+
-+
- static int nl80211_put_mesh_config(struct nl_msg *msg,
- 				   struct wpa_driver_mesh_bss_params *params)
- {
-@@ -12001,6 +12112,7 @@ static int nl80211_join_mesh(struct i802_bss *bss,
- 	    nl80211_put_basic_rates(msg, params->basic_rates) ||
- 	    nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
- 	    nl80211_put_beacon_int(msg, params->beacon_int) ||
-+	    nl80211_put_mcast_rate(msg, params->mcast_rate) ||
- 	    nl80211_put_dtim_period(msg, params->dtim_period))
- 		goto fail;
- 
-@@ -12156,13 +12268,14 @@ static int wpa_driver_br_add_ip_neigh(void *priv, u8 version,
- 				      const u8 *ipaddr, int prefixlen,
- 				      const u8 *addr)
- {
--#ifdef CONFIG_LIBNL3_ROUTE
- 	struct i802_bss *bss = priv;
- 	struct wpa_driver_nl80211_data *drv = bss->drv;
--	struct rtnl_neigh *rn;
--	struct nl_addr *nl_ipaddr = NULL;
--	struct nl_addr *nl_lladdr = NULL;
--	int family, addrsize;
-+	struct ndmsg nhdr = {
-+		.ndm_state = NUD_PERMANENT,
-+		.ndm_ifindex = bss->br_ifindex,
-+	};
-+	struct nl_msg *msg;
-+	int addrsize;
- 	int res;
- 
- 	if (!ipaddr || prefixlen == 0 || !addr)
-@@ -12181,85 +12294,66 @@ static int wpa_driver_br_add_ip_neigh(void *priv, u8 version,
- 	}
- 
- 	if (version == 4) {
--		family = AF_INET;
-+		nhdr.ndm_family = AF_INET;
- 		addrsize = 4;
- 	} else if (version == 6) {
--		family = AF_INET6;
-+		nhdr.ndm_family = AF_INET6;
- 		addrsize = 16;
- 	} else {
- 		return -EINVAL;
- 	}
- 
--	rn = rtnl_neigh_alloc();
--	if (rn == NULL)
-+	msg = nlmsg_alloc_simple(RTM_NEWNEIGH, NLM_F_CREATE);
-+	if (!msg)
- 		return -ENOMEM;
- 
--	/* set the destination ip address for neigh */
--	nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
--	if (nl_ipaddr == NULL) {
--		wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
--		res = -ENOMEM;
-+	res = -ENOMEM;
-+	if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
- 		goto errout;
--	}
--	nl_addr_set_prefixlen(nl_ipaddr, prefixlen);
--	res = rtnl_neigh_set_dst(rn, nl_ipaddr);
--	if (res) {
--		wpa_printf(MSG_DEBUG,
--			   "nl80211: neigh set destination addr failed");
-+
-+	if (nla_put(msg, NDA_DST, addrsize, (void *)ipaddr))
- 		goto errout;
--	}
- 
--	/* set the corresponding lladdr for neigh */
--	nl_lladdr = nl_addr_build(AF_BRIDGE, (u8 *) addr, ETH_ALEN);
--	if (nl_lladdr == NULL) {
--		wpa_printf(MSG_DEBUG, "nl80211: neigh set lladdr failed");
--		res = -ENOMEM;
-+	if (nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *)addr))
- 		goto errout;
--	}
--	rtnl_neigh_set_lladdr(rn, nl_lladdr);
- 
--	rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
--	rtnl_neigh_set_state(rn, NUD_PERMANENT);
-+	res = nl_send_auto_complete(drv->rtnl_sk, msg);
-+	if (res < 0)
-+		goto errout;
- 
--	res = rtnl_neigh_add(drv->rtnl_sk, rn, NLM_F_CREATE);
-+	res = nl_wait_for_ack(drv->rtnl_sk);
- 	if (res) {
- 		wpa_printf(MSG_DEBUG,
- 			   "nl80211: Adding bridge ip neigh failed: %s",
- 			   nl_geterror(res));
- 	}
- errout:
--	if (nl_lladdr)
--		nl_addr_put(nl_lladdr);
--	if (nl_ipaddr)
--		nl_addr_put(nl_ipaddr);
--	if (rn)
--		rtnl_neigh_put(rn);
-+	nlmsg_free(msg);
- 	return res;
--#else /* CONFIG_LIBNL3_ROUTE */
--	return -1;
--#endif /* CONFIG_LIBNL3_ROUTE */
- }
- 
- 
- static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version,
- 					 const u8 *ipaddr)
- {
--#ifdef CONFIG_LIBNL3_ROUTE
- 	struct i802_bss *bss = priv;
- 	struct wpa_driver_nl80211_data *drv = bss->drv;
--	struct rtnl_neigh *rn;
--	struct nl_addr *nl_ipaddr;
--	int family, addrsize;
-+	struct ndmsg nhdr = {
-+		.ndm_state = NUD_PERMANENT,
-+		.ndm_ifindex = bss->br_ifindex,
-+	};
-+	struct nl_msg *msg;
-+	int addrsize;
- 	int res;
- 
- 	if (!ipaddr)
- 		return -EINVAL;
- 
- 	if (version == 4) {
--		family = AF_INET;
-+		nhdr.ndm_family = AF_INET;
- 		addrsize = 4;
- 	} else if (version == 6) {
--		family = AF_INET6;
-+		nhdr.ndm_family = AF_INET6;
- 		addrsize = 16;
- 	} else {
- 		return -EINVAL;
-@@ -12277,41 +12371,30 @@ static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version,
- 		return -1;
- 	}
- 
--	rn = rtnl_neigh_alloc();
--	if (rn == NULL)
-+	msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
-+	if (!msg)
- 		return -ENOMEM;
- 
--	/* set the destination ip address for neigh */
--	nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
--	if (nl_ipaddr == NULL) {
--		wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
--		res = -ENOMEM;
-+	res = -ENOMEM;
-+	if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
- 		goto errout;
--	}
--	res = rtnl_neigh_set_dst(rn, nl_ipaddr);
--	if (res) {
--		wpa_printf(MSG_DEBUG,
--			   "nl80211: neigh set destination addr failed");
-+
-+	if (nla_put(msg, NDA_DST, addrsize, (void *)ipaddr))
- 		goto errout;
--	}
- 
--	rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
-+	res = nl_send_auto_complete(drv->rtnl_sk, msg);
-+	if (res < 0)
-+		goto errout;
- 
--	res = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
-+	res = nl_wait_for_ack(drv->rtnl_sk);
- 	if (res) {
- 		wpa_printf(MSG_DEBUG,
- 			   "nl80211: Deleting bridge ip neigh failed: %s",
- 			   nl_geterror(res));
- 	}
- errout:
--	if (nl_ipaddr)
--		nl_addr_put(nl_ipaddr);
--	if (rn)
--		rtnl_neigh_put(rn);
-+	nlmsg_free(msg);
- 	return res;
--#else /* CONFIG_LIBNL3_ROUTE */
--	return -1;
--#endif /* CONFIG_LIBNL3_ROUTE */
- }
- 
- 
-@@ -12389,7 +12472,7 @@ static const char * drv_br_net_param_str(enum drv_br_net_param param)
- 
- 
- static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
--				       unsigned int val)
-+				       const char *ifname, unsigned int val)
- {
- 	struct i802_bss *bss = priv;
- 	char path[128];
-@@ -12415,8 +12498,11 @@ static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
- 			return -EINVAL;
- 	}
- 
-+	if (!ifname)
-+		ifname = bss->brname;
-+
- 	os_snprintf(path, sizeof(path), "/proc/sys/net/ipv%d/conf/%s/%s",
--		    ip_version, bss->brname, param_txt);
-+		    ip_version, ifname, param_txt);
- 
- set_val:
- 	if (linux_write_system_file(path, val))
-@@ -14019,6 +14105,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.set_acl = wpa_driver_nl80211_set_acl,
- 	.if_add = wpa_driver_nl80211_if_add,
- 	.if_remove = driver_nl80211_if_remove,
-+	.if_rename = driver_nl80211_if_rename,
-+	.set_first_bss = driver_nl80211_set_first_bss,
- 	.send_mlme = driver_nl80211_send_mlme,
- 	.get_hw_feature_data = nl80211_get_hw_feature_data,
- 	.sta_add = wpa_driver_nl80211_sta_add,
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 65389d206..d6a887cef 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -976,6 +976,10 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 			nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]);
- 	}
- 
-+	if (tb[NL80211_ATTR_MAX_SCAN_IE_LEN])
-+		capa->max_scan_ie_len =
-+			nla_get_u16(tb[NL80211_ATTR_MAX_SCAN_IE_LEN]);
-+
- 	if (tb[NL80211_ATTR_MAX_MATCH_SETS])
- 		capa->max_match_sets =
- 			nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index f5778cdaf..4a12d749c 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -1196,6 +1196,7 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
- 				 struct nlattr *bw, struct nlattr *cf1,
- 				 struct nlattr *cf2,
- 				 struct nlattr *punct_bitmap,
-+				 struct nlattr *count,
- 				 int finished)
- {
- 	struct i802_bss *bss;
-@@ -1259,6 +1260,8 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
- 		data.ch_switch.cf1 = nla_get_u32(cf1);
- 	if (cf2)
- 		data.ch_switch.cf2 = nla_get_u32(cf2);
-+	if (count)
-+		data.ch_switch.count = nla_get_u32(count);
- 
- 	if (link)
- 		data.ch_switch.link_id = nla_get_u8(link);
-@@ -3972,6 +3975,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
- 				     tb[NL80211_ATTR_CENTER_FREQ1],
- 				     tb[NL80211_ATTR_CENTER_FREQ2],
- 				     tb[NL80211_ATTR_PUNCT_BITMAP],
-+				     tb[NL80211_ATTR_CH_SWITCH_COUNT],
- 				     0);
- 		break;
- 	case NL80211_CMD_CH_SWITCH_NOTIFY:
-@@ -3984,6 +3988,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
- 				     tb[NL80211_ATTR_CENTER_FREQ1],
- 				     tb[NL80211_ATTR_CENTER_FREQ2],
- 				     tb[NL80211_ATTR_PUNCT_BITMAP],
-+				     NULL,
- 				     1);
- 		break;
- 	case NL80211_CMD_DISCONNECT:
-diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
-index 577f84fef..c352a88bc 100644
---- a/src/drivers/driver_nl80211_scan.c
-+++ b/src/drivers/driver_nl80211_scan.c
-@@ -221,7 +221,7 @@ nl80211_scan_common(struct i802_bss *bss, u8 cmd,
- 		wpa_printf(MSG_DEBUG, "nl80211: Passive scan requested");
- 	}
- 
--	if (params->extra_ies) {
-+	if (params->extra_ies && drv->capa.max_scan_ie_len >= params->extra_ies_len) {
- 		wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
- 			    params->extra_ies, params->extra_ies_len);
- 		if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
-diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c
-index e95df6ddb..9071da3cf 100644
---- a/src/drivers/drivers.c
-+++ b/src/drivers/drivers.c
-@@ -10,6 +10,10 @@
- #include "utils/common.h"
- #include "driver.h"
- 
-+void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
-+			     union wpa_event_data *data);
-+void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
-+			     union wpa_event_data *data);
- 
- const struct wpa_driver_ops *const wpa_drivers[] =
- {
-diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
-index a03d4a034..8da44d9f5 100644
---- a/src/drivers/drivers.mak
-+++ b/src/drivers/drivers.mak
-@@ -54,7 +54,6 @@ NEED_SME=y
- NEED_AP_MLME=y
- NEED_NETLINK=y
- NEED_LINUX_IOCTL=y
--NEED_RFKILL=y
- NEED_RADIOTAP=y
- NEED_LIBNL=y
- endif
-@@ -111,7 +110,6 @@ DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
- CONFIG_WIRELESS_EXTENSION=y
- NEED_NETLINK=y
- NEED_LINUX_IOCTL=y
--NEED_RFKILL=y
- endif
- 
- ifdef CONFIG_DRIVER_NDIS
-@@ -137,7 +135,6 @@ endif
- ifdef CONFIG_WIRELESS_EXTENSION
- DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
- DRV_WPA_OBJS += ../src/drivers/driver_wext.o
--NEED_RFKILL=y
- endif
- 
- ifdef NEED_NETLINK
-@@ -146,6 +143,7 @@ endif
- 
- ifdef NEED_RFKILL
- DRV_OBJS += ../src/drivers/rfkill.o
-+DRV_WPA_CFLAGS += -DCONFIG_RFKILL
- endif
- 
- ifdef NEED_RADIOTAP
-diff --git a/src/drivers/rfkill.h b/src/drivers/rfkill.h
-index 0412ac330..e27565375 100644
---- a/src/drivers/rfkill.h
-+++ b/src/drivers/rfkill.h
-@@ -18,8 +18,24 @@ struct rfkill_config {
- 	void (*unblocked_cb)(void *ctx);
- };
- 
-+#ifdef CONFIG_RFKILL
- struct rfkill_data * rfkill_init(struct rfkill_config *cfg);
- void rfkill_deinit(struct rfkill_data *rfkill);
- int rfkill_is_blocked(struct rfkill_data *rfkill);
-+#else
-+static inline struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
-+{
-+	return (void *) 1;
-+}
-+
-+static inline void rfkill_deinit(struct rfkill_data *rfkill)
-+{
-+}
-+
-+static inline int rfkill_is_blocked(struct rfkill_data *rfkill)
-+{
-+	return 0;
-+}
-+#endif
- 
- #endif /* RFKILL_H */
-diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
-index 2a7f36170..8e8903051 100644
---- a/src/radius/radius_client.c
-+++ b/src/radius/radius_client.c
-@@ -165,6 +165,8 @@ struct radius_client_data {
- 	 */
- 	void *ctx;
- 
-+	struct hostapd_ip_addr local_ip;
-+
- 	/**
- 	 * conf - RADIUS client configuration (list of RADIUS servers to use)
- 	 */
-@@ -818,6 +820,30 @@ static void radius_close_acct_socket(struct radius_client_data *radius)
- }
- 
- 
-+/**
-+ * radius_client_send - Get local address for the RADIUS auth socket
-+ * @radius: RADIUS client context from radius_client_init()
-+ * @addr: pointer to store the address
-+ *
-+ * This function returns the local address for the connection to the RADIUS
-+ * auth server. It also opens the socket if it's not available yet.
-+ */
-+int radius_client_get_local_addr(struct radius_client_data *radius,
-+				 struct hostapd_ip_addr *addr)
-+{
-+	struct hostapd_radius_servers *conf = radius->conf;
-+
-+	if (conf->auth_server && radius->auth_sock < 0)
-+		radius_client_init_auth(radius);
-+
-+	if (radius->auth_sock < 0)
-+		return -1;
-+
-+	memcpy(addr, &radius->local_ip, sizeof(*addr));
-+
-+	return 0;
-+}
-+
- /**
-  * radius_client_send - Send a RADIUS request
-  * @radius: RADIUS client context from radius_client_init()
-@@ -1711,6 +1737,10 @@ radius_change_server(struct radius_client_data *radius,
- 			wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
- 				   inet_ntoa(claddr.sin_addr),
- 				   ntohs(claddr.sin_port));
-+			if (auth) {
-+				radius->local_ip.af = AF_INET;
-+				radius->local_ip.u.v4 = claddr.sin_addr;
-+			}
- 		}
- 		break;
- #ifdef CONFIG_IPV6
-@@ -1722,6 +1752,10 @@ radius_change_server(struct radius_client_data *radius,
- 				   inet_ntop(AF_INET6, &claddr6.sin6_addr,
- 					     abuf, sizeof(abuf)),
- 				   ntohs(claddr6.sin6_port));
-+			if (auth) {
-+				radius->local_ip.af = AF_INET6;
-+				radius->local_ip.u.v6 = claddr6.sin6_addr;
-+			}
- 		}
- 		break;
- 	}
-diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h
-index db40637ea..9a89b0382 100644
---- a/src/radius/radius_client.h
-+++ b/src/radius/radius_client.h
-@@ -274,6 +274,8 @@ int radius_client_register(struct radius_client_data *radius,
- void radius_client_set_interim_error_cb(struct radius_client_data *radius,
- 					void (*cb)(const u8 *addr, void *ctx),
- 					void *ctx);
-+int radius_client_get_local_addr(struct radius_client_data *radius,
-+				 struct hostapd_ip_addr * addr);
- int radius_client_send(struct radius_client_data *radius,
- 		       struct radius_msg *msg,
- 		       RadiusType msg_type, const u8 *addr);
-diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
-index aaa3fc267..327782f62 100644
---- a/src/radius/radius_das.c
-+++ b/src/radius/radius_das.c
-@@ -12,13 +12,26 @@
- #include "utils/common.h"
- #include "utils/eloop.h"
- #include "utils/ip_addr.h"
-+#include "utils/list.h"
- #include "radius.h"
- #include "radius_das.h"
- 
- 
--struct radius_das_data {
-+static struct dl_list das_ports = DL_LIST_HEAD_INIT(das_ports);
-+
-+struct radius_das_port {
-+	struct dl_list list;
-+	struct dl_list das_data;
-+
-+	int port;
- 	int sock;
-+};
-+
-+struct radius_das_data {
-+	struct dl_list list;
-+	struct radius_das_port *port;
- 	u8 *shared_secret;
-+	u8 *nas_identifier;
- 	size_t shared_secret_len;
- 	struct hostapd_ip_addr client_addr;
- 	unsigned int time_window;
-@@ -378,56 +391,17 @@ fail:
- }
- 
- 
--static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
-+static void
-+radius_das_receive_msg(struct radius_das_data *das, struct radius_msg *msg,
-+		       struct sockaddr *from, socklen_t fromlen,
-+		       char *abuf, int from_port)
- {
--	struct radius_das_data *das = eloop_ctx;
--	u8 buf[1500];
--	union {
--		struct sockaddr_storage ss;
--		struct sockaddr_in sin;
--#ifdef CONFIG_IPV6
--		struct sockaddr_in6 sin6;
--#endif /* CONFIG_IPV6 */
--	} from;
--	char abuf[50];
--	int from_port = 0;
--	socklen_t fromlen;
--	int len;
--	struct radius_msg *msg, *reply = NULL;
-+	struct radius_msg *reply = NULL;
- 	struct radius_hdr *hdr;
- 	struct wpabuf *rbuf;
-+	struct os_time now;
- 	u32 val;
- 	int res;
--	struct os_time now;
--
--	fromlen = sizeof(from);
--	len = recvfrom(sock, buf, sizeof(buf), 0,
--		       (struct sockaddr *) &from.ss, &fromlen);
--	if (len < 0) {
--		wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
--		return;
--	}
--
--	os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
--	from_port = ntohs(from.sin.sin_port);
--
--	wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
--		   len, abuf, from_port);
--	if (das->client_addr.u.v4.s_addr &&
--	    das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
--		wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
--		return;
--	}
--
--	msg = radius_msg_parse(buf, len);
--	if (msg == NULL) {
--		wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
--			   "from %s:%d failed", abuf, from_port);
--		return;
--	}
--
--	if (wpa_debug_level <= MSG_MSGDUMP)
--		radius_msg_dump(msg);
- 
- 	if (radius_msg_verify_das_req(msg, das->shared_secret,
- 				       das->shared_secret_len,
-@@ -494,9 +468,8 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
- 			radius_msg_dump(reply);
- 
- 		rbuf = radius_msg_get_buf(reply);
--		res = sendto(das->sock, wpabuf_head(rbuf),
--			     wpabuf_len(rbuf), 0,
--			     (struct sockaddr *) &from.ss, fromlen);
-+		res = sendto(das->port->sock, wpabuf_head(rbuf),
-+			     wpabuf_len(rbuf), 0, from, fromlen);
- 		if (res < 0) {
- 			wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
- 				   abuf, from_port, strerror(errno));
-@@ -508,6 +481,72 @@ fail:
- 	radius_msg_free(reply);
- }
- 
-+static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
-+{
-+	struct radius_das_port *p = eloop_ctx;
-+	struct radius_das_data *das;
-+	u8 buf[1500];
-+	union {
-+		struct sockaddr_storage ss;
-+		struct sockaddr_in sin;
-+#ifdef CONFIG_IPV6
-+		struct sockaddr_in6 sin6;
-+#endif /* CONFIG_IPV6 */
-+	} from;
-+	struct radius_msg *msg;
-+	size_t nasid_len = 0;
-+	u8 *nasid_buf = NULL;
-+	char abuf[50];
-+	int from_port = 0;
-+	socklen_t fromlen;
-+	int found = 0;
-+	int len;
-+
-+	fromlen = sizeof(from);
-+	len = recvfrom(sock, buf, sizeof(buf), 0,
-+		       (struct sockaddr *) &from.ss, &fromlen);
-+	if (len < 0) {
-+		wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
-+		return;
-+	}
-+
-+	os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
-+	from_port = ntohs(from.sin.sin_port);
-+
-+	msg = radius_msg_parse(buf, len);
-+	if (msg == NULL) {
-+		wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
-+			   "from %s:%d failed", abuf, from_port);
-+		return;
-+	}
-+
-+	wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
-+		   len, abuf, from_port);
-+
-+	if (wpa_debug_level <= MSG_MSGDUMP)
-+		radius_msg_dump(msg);
-+
-+	radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
-+				&nasid_buf, &nasid_len, NULL);
-+	dl_list_for_each(das, &p->das_data, struct radius_das_data, list) {
-+		if (das->client_addr.u.v4.s_addr &&
-+		    das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr)
-+			continue;
-+
-+		if (das->nas_identifier && nasid_buf &&
-+		    (nasid_len != os_strlen(das->nas_identifier) ||
-+		     os_memcmp(das->nas_identifier, nasid_buf, nasid_len) != 0))
-+			continue;
-+
-+		found = 1;
-+		radius_das_receive_msg(das, msg, (struct sockaddr *)&from.ss,
-+				       fromlen, abuf, from_port);
-+	}
-+
-+	if (!found)
-+		wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
-+}
-+
- 
- static int radius_das_open_socket(int port)
- {
-@@ -533,6 +572,49 @@ static int radius_das_open_socket(int port)
- }
- 
- 
-+static struct radius_das_port *
-+radius_das_open_port(int port)
-+{
-+	struct radius_das_port *p;
-+
-+	dl_list_for_each(p, &das_ports, struct radius_das_port, list) {
-+		if (p->port == port)
-+			return p;
-+	}
-+
-+	p = os_zalloc(sizeof(*p));
-+	if (p == NULL)
-+		return NULL;
-+
-+	dl_list_init(&p->das_data);
-+	p->port = port;
-+	p->sock = radius_das_open_socket(port);
-+	if (p->sock < 0)
-+		goto free_port;
-+
-+	if (eloop_register_read_sock(p->sock, radius_das_receive, p, NULL))
-+		goto close_port;
-+
-+	dl_list_add(&das_ports, &p->list);
-+
-+	return p;
-+
-+close_port:
-+	close(p->sock);
-+free_port:
-+	os_free(p);
-+
-+	return NULL;
-+}
-+
-+static void radius_das_close_port(struct radius_das_port *p)
-+{
-+	dl_list_del(&p->list);
-+	eloop_unregister_read_sock(p->sock);
-+	close(p->sock);
-+	free(p);
-+}
-+
- struct radius_das_data *
- radius_das_init(struct radius_das_conf *conf)
- {
-@@ -553,6 +635,8 @@ radius_das_init(struct radius_das_conf *conf)
- 	das->ctx = conf->ctx;
- 	das->disconnect = conf->disconnect;
- 	das->coa = conf->coa;
-+	if (conf->nas_identifier)
-+		das->nas_identifier = os_strdup(conf->nas_identifier);
- 
- 	os_memcpy(&das->client_addr, conf->client_addr,
- 		  sizeof(das->client_addr));
-@@ -565,19 +649,15 @@ radius_das_init(struct radius_das_conf *conf)
- 	}
- 	das->shared_secret_len = conf->shared_secret_len;
- 
--	das->sock = radius_das_open_socket(conf->port);
--	if (das->sock < 0) {
-+	das->port = radius_das_open_port(conf->port);
-+	if (!das->port) {
- 		wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
- 			   "DAS");
- 		radius_das_deinit(das);
- 		return NULL;
- 	}
- 
--	if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
--	{
--		radius_das_deinit(das);
--		return NULL;
--	}
-+	dl_list_add(&das->port->das_data, &das->list);
- 
- 	return das;
- }
-@@ -588,11 +668,14 @@ void radius_das_deinit(struct radius_das_data *das)
- 	if (das == NULL)
- 		return;
- 
--	if (das->sock >= 0) {
--		eloop_unregister_read_sock(das->sock);
--		close(das->sock);
-+	if (das->port) {
-+		dl_list_del(&das->list);
-+
-+		if (dl_list_empty(&das->port->das_data))
-+			radius_das_close_port(das->port);
- 	}
- 
-+	os_free(das->nas_identifier);
- 	os_free(das->shared_secret);
- 	os_free(das);
- }
-diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h
-index 233d662f6..80dc13fc8 100644
---- a/src/radius/radius_das.h
-+++ b/src/radius/radius_das.h
-@@ -44,6 +44,7 @@ struct radius_das_attrs {
- struct radius_das_conf {
- 	int port;
- 	const u8 *shared_secret;
-+	const u8 *nas_identifier;
- 	size_t shared_secret_len;
- 	const struct hostapd_ip_addr *client_addr;
- 	unsigned int time_window;
-diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
-index e02c21540..57a47263e 100644
---- 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(struct radius_server_data *data,
- 	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_server_data *data, const char *keyname)
- }
- #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 radius_server_data *data,
- 		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 radius_server_data *data,
- 	}
- 
- 	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_server_data *data,
- 	}
- 
- 	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(void *ctx, const u8 *identity,
- 	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/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
-index 8956c4072..e669858d8 100644
---- a/src/rsn_supp/wpa.c
-+++ b/src/rsn_supp/wpa.c
-@@ -3943,6 +3943,8 @@ static u32 wpa_key_mgmt_suite(struct wpa_sm *sm)
- }
- 
- 
-+#ifdef CONFIG_CTRL_IFACE_MIB
-+
- #define RSN_SUITE "%02x-%02x-%02x-%d"
- #define RSN_SUITE_ARG(s) \
- ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
-@@ -4024,6 +4026,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
- 
- 	return (int) len;
- }
-+#endif
- #endif /* CONFIG_CTRL_IFACE */
- 
- 
-diff --git a/src/tls/Makefile b/src/tls/Makefile
-index c84fbe859..e974a41f0 100644
---- a/src/tls/Makefile
-+++ b/src/tls/Makefile
-@@ -1,3 +1,10 @@
-+LIB_OBJS= asn1.o
-+
-+ifneq ($(CONFIG_TLS),gnutls)
-+ifneq ($(CONFIG_TLS),mbedtls)
-+ifneq ($(CONFIG_TLS),openssl)
-+ifneq ($(CONFIG_TLS),wolfssl)
-+
- CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
- CFLAGS += -DCONFIG_CRYPTO_INTERNAL
- CFLAGS += -DCONFIG_TLSV11
-@@ -21,5 +28,9 @@ LIB_OBJS= \
- 	tlsv1_server_read.o \
- 	tlsv1_server_write.o \
- 	x509v3.o
-+endif
-+endif
-+endif
-+endif
- 
- include ../lib.rules
-diff --git a/src/utils/eloop.c b/src/utils/eloop.c
-index 00b0beff0..50dd1beda 100644
---- 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)
- {
-diff --git a/src/utils/eloop.h b/src/utils/eloop.h
-index 04ee6d183..5452ea589 100644
---- a/src/utils/eloop.h
-+++ b/src/utils/eloop.h
-@@ -65,6 +65,9 @@ typedef void (*eloop_timeout_handler)(void *eloop_ctx, void *user_ctx);
-  */
- 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 sig, void *signal_ctx);
-  */
- 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_signal_handler handler,
-  */
- int eloop_sock_requeue(void);
- 
-+void eloop_add_uloop(void);
-+
- /**
-  * eloop_run - Start the event loop
-  *
-diff --git a/src/utils/uloop.c b/src/utils/uloop.c
-new file mode 100644
-index 000000000..c0d26db93
---- /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/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
-index 7f3dd185f..627575e39 100644
---- a/src/utils/wpa_debug.c
-+++ b/src/utils/wpa_debug.c
-@@ -26,6 +26,10 @@ static FILE *wpa_debug_tracing_file = NULL;
- #define WPAS_TRACE_PFX "wpas <%d>: "
- #endif /* CONFIG_DEBUG_LINUX_TRACING */
- 
-+void (*wpa_printf_hook)(int level, const char *fmt, va_list ap);
-+void (*wpa_hexdump_hook)(int level, const char *title, const void *buf,
-+			 size_t len);
-+void (*wpa_netlink_hook)(int tx, const void *data, size_t len);
- 
- int wpa_debug_level = MSG_INFO;
- int wpa_debug_show_keys = 0;
-@@ -206,10 +210,16 @@ void wpa_debug_close_linux_tracing(void)
-  *
-  * Note: New line '\n' is added to the end of the text when printing to stdout.
-  */
--void wpa_printf(int level, const char *fmt, ...)
-+void _wpa_printf(int level, const char *fmt, ...)
- {
- 	va_list ap;
- 
-+	if (wpa_printf_hook) {
-+		va_start(ap, fmt);
-+		wpa_printf_hook(level, fmt, ap);
-+		va_end(ap);
-+	}
-+
- 	if (level >= wpa_debug_level) {
- #ifdef CONFIG_ANDROID_LOG
- 		va_start(ap, fmt);
-@@ -255,11 +265,14 @@ void wpa_printf(int level, const char *fmt, ...)
- }
- 
- 
--static void _wpa_hexdump(int level, const char *title, const u8 *buf,
-+void _wpa_hexdump(int level, const char *title, const u8 *buf,
- 			 size_t len, int show, int only_syslog)
- {
- 	size_t i;
- 
-+	if (wpa_hexdump_hook)
-+		wpa_hexdump_hook(level, title, buf, len);
-+
- #ifdef CONFIG_DEBUG_LINUX_TRACING
- 	if (wpa_debug_tracing_file != NULL) {
- 		fprintf(wpa_debug_tracing_file,
-@@ -382,19 +395,7 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf,
- #endif /* CONFIG_ANDROID_LOG */
- }
- 
--void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
--{
--	_wpa_hexdump(level, title, buf, len, 1, 0);
--}
--
--
--void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len)
--{
--	_wpa_hexdump(level, title, buf, len, wpa_debug_show_keys, 0);
--}
--
--
--static void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
-+void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
- 			       size_t len, int show)
- {
- 	size_t i, llen;
-@@ -507,20 +508,6 @@ file_done:
- }
- 
- 
--void wpa_hexdump_ascii(int level, const char *title, const void *buf,
--		       size_t len)
--{
--	_wpa_hexdump_ascii(level, title, buf, len, 1);
--}
--
--
--void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
--			   size_t len)
--{
--	_wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
--}
--
--
- #ifdef CONFIG_DEBUG_FILE
- static char *last_path = NULL;
- #endif /* CONFIG_DEBUG_FILE */
-@@ -644,7 +631,7 @@ void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func)
- }
- 
- 
--void wpa_msg(void *ctx, int level, const char *fmt, ...)
-+void _wpa_msg(void *ctx, int level, const char *fmt, ...)
- {
- 	va_list ap;
- 	char *buf;
-@@ -682,7 +669,7 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...)
- }
- 
- 
--void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
-+void _wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
- {
- 	va_list ap;
- 	char *buf;
-diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
-index 4c02ad3c7..854520bfe 100644
---- a/src/utils/wpa_debug.h
-+++ b/src/utils/wpa_debug.h
-@@ -11,6 +11,10 @@
- 
- #include "wpabuf.h"
- 
-+extern void (*wpa_printf_hook)(int level, const char *fmt, va_list ap);
-+extern void (*wpa_hexdump_hook)(int level, const char *title,
-+				const void *buf, size_t len);
-+extern void (*wpa_netlink_hook)(int tx, const void *data, size_t len);
- extern int wpa_debug_level;
- extern int wpa_debug_show_keys;
- extern int wpa_debug_timestamp;
-@@ -51,6 +55,17 @@ void wpa_debug_close_file(void);
- void wpa_debug_setup_stdout(void);
- void wpa_debug_stop_log(void);
- 
-+/* internal */
-+void _wpa_hexdump(int level, const char *title, const u8 *buf,
-+		  size_t len, int show, int only_syslog);
-+void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
-+			size_t len, int show);
-+extern int wpa_debug_show_keys;
-+
-+#ifndef CONFIG_MSG_MIN_PRIORITY
-+#define CONFIG_MSG_MIN_PRIORITY 0
-+#endif
-+
- /**
-  * wpa_debug_printf_timestamp - Print timestamp for debug output
-  *
-@@ -71,9 +86,15 @@ void wpa_debug_print_timestamp(void);
-  *
-  * Note: New line '\n' is added to the end of the text when printing to stdout.
-  */
--void wpa_printf(int level, const char *fmt, ...)
-+void _wpa_printf(int level, const char *fmt, ...)
- PRINTF_FORMAT(2, 3);
- 
-+#define wpa_printf(level, ...)						\
-+	do {								\
-+		if (level >= CONFIG_MSG_MIN_PRIORITY)			\
-+			_wpa_printf(level, __VA_ARGS__);		\
-+	} while(0)
-+
- /**
-  * wpa_hexdump - conditional hex dump
-  * @level: priority level (MSG_*) of the message
-@@ -85,7 +106,13 @@ PRINTF_FORMAT(2, 3);
-  * output may be directed to stdout, stderr, and/or syslog based on
-  * configuration. The contents of buf is printed out has hex dump.
-  */
--void wpa_hexdump(int level, const char *title, const void *buf, size_t len);
-+static inline void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
-+{
-+	if (level < CONFIG_MSG_MIN_PRIORITY)
-+		return;
-+
-+	_wpa_hexdump(level, title, buf, len, 1, 1);
-+}
- 
- static inline void wpa_hexdump_buf(int level, const char *title,
- 				   const struct wpabuf *buf)
-@@ -107,7 +134,13 @@ static inline void wpa_hexdump_buf(int level, const char *title,
-  * like wpa_hexdump(), but by default, does not include secret keys (passwords,
-  * etc.) in debug output.
-  */
--void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len);
-+static inline void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len)
-+{
-+	if (level < CONFIG_MSG_MIN_PRIORITY)
-+		return;
-+
-+	_wpa_hexdump(level, title, buf, len, wpa_debug_show_keys, 1);
-+}
- 
- static inline void wpa_hexdump_buf_key(int level, const char *title,
- 				       const struct wpabuf *buf)
-@@ -129,8 +162,14 @@ static inline void wpa_hexdump_buf_key(int level, const char *title,
-  * the hex numbers and ASCII characters (for printable range) are shown. 16
-  * bytes per line will be shown.
-  */
--void wpa_hexdump_ascii(int level, const char *title, const void *buf,
--		       size_t len);
-+static inline void wpa_hexdump_ascii(int level, const char *title,
-+				     const u8 *buf, size_t len)
-+{
-+	if (level < CONFIG_MSG_MIN_PRIORITY)
-+		return;
-+
-+	_wpa_hexdump_ascii(level, title, buf, len, 1);
-+}
- 
- /**
-  * wpa_hexdump_ascii_key - conditional hex dump, hide keys
-@@ -146,8 +185,14 @@ void wpa_hexdump_ascii(int level, const char *title, const void *buf,
-  * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by
-  * default, does not include secret keys (passwords, etc.) in debug output.
-  */
--void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
--			   size_t len);
-+static inline void wpa_hexdump_ascii_key(int level, const char *title,
-+					 const u8 *buf, size_t len)
-+{
-+	if (level < CONFIG_MSG_MIN_PRIORITY)
-+		return;
-+
-+	_wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
-+}
- 
- /*
-  * wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce
-@@ -184,7 +229,12 @@ void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
-  *
-  * Note: New line '\n' is added to the end of the text when printing to stdout.
-  */
--void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
-+void _wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
-+#define wpa_msg(ctx, level, ...)					\
-+	do {								\
-+		if (level >= CONFIG_MSG_MIN_PRIORITY)			\
-+			_wpa_msg(ctx, level, __VA_ARGS__);		\
-+	} while(0)
- 
- /**
-  * wpa_msg_ctrl - Conditional printf for ctrl_iface monitors
-@@ -198,8 +248,13 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
-  * attached ctrl_iface monitors. In other words, it can be used for frequent
-  * events that do not need to be sent to syslog.
-  */
--void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
-+void _wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
- PRINTF_FORMAT(3, 4);
-+#define wpa_msg_ctrl(ctx, level, ...)					\
-+	do {								\
-+		if (level >= CONFIG_MSG_MIN_PRIORITY)			\
-+			_wpa_msg_ctrl(ctx, level, __VA_ARGS__);		\
-+	} while(0)
- 
- /**
-  * wpa_msg_global - Global printf for ctrl_iface monitors
-diff --git a/tests/Makefile b/tests/Makefile
-index 8ec154bb3..58287f56f 100644
---- a/tests/Makefile
-+++ b/tests/Makefile
-@@ -5,6 +5,14 @@ ALL=test-base64 test-md4 test-milenage \
- 	test-sha256 test-aes test-x509v3 test-list test-rc4 \
- 	test-bss
- 
-+RUN_TESTS= \
-+	test-list \
-+	test-md4 test-rc4 test-sha1 test-sha256 \
-+	test-milenage test-aes \
-+	test-crypto_module
-+
-+ALL=$(RUN_TESTS) test-base64 test-https test-https_server
-+
- include ../src/build.rules
- 
- ifdef LIBFUZZER
-@@ -25,13 +33,27 @@ CFLAGS += -DCONFIG_IEEE80211R_AP
- CFLAGS += -DCONFIG_IEEE80211R
- CFLAGS += -DCONFIG_TDLS
- 
-+# test-crypto_module
-+CFLAGS += -DCONFIG_MODULE_TESTS
-+CFLAGS += -DCONFIG_DPP
-+#CFLAGS += -DCONFIG_DPP2
-+#CFLAGS += -DCONFIG_DPP3
-+CFLAGS += -DCONFIG_ECC
-+CFLAGS += -DCONFIG_HMAC_SHA256_KDF
-+CFLAGS += -DCONFIG_HMAC_SHA384_KDF
-+CFLAGS += -DCONFIG_MESH
-+CFLAGS += -DCONFIG_SHA256
-+CFLAGS += -DCONFIG_SHA384
-+CFLAGS += -DEAP_PSK
-+CFLAGS += -DEAP_FAST
-+
- CFLAGS += -I../src
- CFLAGS += -I../src/utils
- 
- SLIBS = ../src/utils/libutils.a
- 
--DLIBS = ../src/crypto/libcrypto.a \
--	../src/tls/libtls.a
-+DLIBS = ../src/tls/libtls.a \
-+	../src/crypto/libcrypto.a
- 
- _OBJS_VAR := LLIBS
- include ../src/objs.mk
-@@ -43,12 +65,43 @@ include ../src/objs.mk
- LIBS = $(SLIBS) $(DLIBS)
- LLIBS = -Wl,--start-group $(DLIBS) -Wl,--end-group $(SLIBS)
- 
-+ifeq ($(CONFIG_TLS),mbedtls)
-+CFLAGS += -DCONFIG_TLS_MBEDTLS
-+LLIBS += -lmbedtls -lmbedx509 -lmbedcrypto
-+else
-+ifeq ($(CONFIG_TLS),openssl)
-+CFLAGS += -DCONFIG_TLS_OPENSSL
-+LLIBS += -lssl -lcrypto
-+else
-+ifeq ($(CONFIG_TLS),gnutls)
-+CFLAGS += -DCONFIG_TLS_GNUTLS
-+LLIBS += -lgnutls -lgpg-error -lgcrypt
-+else
-+ifeq ($(CONFIG_TLS),wolfssl)
-+CFLAGS += -DCONFIG_TLS_WOLFSSL
-+LLIBS += -lwolfssl -lm
-+else
-+CFLAGS += -DCONFIG_TLS_INTERNAL
-+CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
-+ALL += test-rsa-sig-ver
-+ALL += test-x509v3
-+clean-config_tls_internal:
-+	rm -f test_x509v3_nist.out.*
-+	rm -f test_x509v3_nist2.out.*
-+endif
-+endif
-+endif
-+endif
-+
- # glibc < 2.17 needs -lrt for clock_gettime()
- LLIBS += -lrt
- 
- test-aes: $(call BUILDOBJ,test-aes.o) $(LIBS)
- 	$(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
- 
-+test-crypto_module: $(call BUILDOBJ,test-crypto_module.o) $(LIBS)
-+	$(LDO) $(LDFLAGS) -o $@ $< $(LLIBS)
-+
- test-base64: $(call BUILDOBJ,test-base64.o) $(LIBS)
- 	$(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
- 
-@@ -149,10 +202,12 @@ run-tests: $(ALL)
- 	./test-sha1
- 	./test-sha256
- 	./test-bss
-+
-+	@set -ex; for i in $(RUN_TESTS); do ./$$i; done
- 	@echo
- 	@echo All tests completed successfully.
- 
--clean: common-clean
-+clean: common-clean clean-config_tls_internal
- 	rm -f *~
--	rm -f test_x509v3_nist.out.*
--	rm -f test_x509v3_nist2.out.*
-+
-+.PHONY: run-tests clean-config_tls_internal
-diff --git a/tests/hwsim/example-hostapd.config b/tests/hwsim/example-hostapd.config
-index 210b7fb86..608e20eed 100644
---- a/tests/hwsim/example-hostapd.config
-+++ b/tests/hwsim/example-hostapd.config
-@@ -4,6 +4,7 @@ CONFIG_DRIVER_NONE=y
- CONFIG_DRIVER_NL80211=y
- CONFIG_RSN_PREAUTH=y
- 
-+#CONFIG_TLS=mbedtls
- #CONFIG_TLS=internal
- #CONFIG_INTERNAL_LIBTOMMATH=y
- #CONFIG_INTERNAL_LIBTOMMATH_FAST=y
-@@ -33,12 +34,7 @@ CONFIG_EAP_TNC=y
- CFLAGS += -DTNC_CONFIG_FILE=\"tnc/tnc_config\"
- LIBS += -rdynamic
- CONFIG_EAP_UNAUTH_TLS=y
--ifeq ($(CONFIG_TLS), openssl)
--CONFIG_EAP_PWD=y
--endif
--ifeq ($(CONFIG_TLS), wolfssl)
--CONFIG_EAP_PWD=y
--endif
-+CONFIG_EAP_PWD=$(if $(filter openssl wolfssl mbedtls,$(CONFIG_TLS)),y,)
- CONFIG_EAP_EKE=y
- CONFIG_PKCS12=y
- CONFIG_RADIUS_SERVER=y
-@@ -88,7 +84,7 @@ CFLAGS += -DCONFIG_RADIUS_TEST
- CONFIG_MODULE_TESTS=y
- 
- CONFIG_SUITEB=y
--CONFIG_SUITEB192=y
-+CONFIG_SUITEB192=$(if $(filter openssl mbedtls,$(CONFIG_TLS)),y,)
- 
- # AddressSanitizer (ASan) can be enabled by uncommenting the following lines.
- # This can be used as a more efficient memory error detector than valgrind
-diff --git a/tests/hwsim/example-wpa_supplicant.config b/tests/hwsim/example-wpa_supplicant.config
-index 123f397e3..da0dde659 100644
---- a/tests/hwsim/example-wpa_supplicant.config
-+++ b/tests/hwsim/example-wpa_supplicant.config
-@@ -2,6 +2,7 @@
- 
- CONFIG_TLS=openssl
- #CONFIG_TLS=wolfssl
-+#CONFIG_TLS=mbedtls
- #CONFIG_TLS=internal
- #CONFIG_INTERNAL_LIBTOMMATH=y
- #CONFIG_INTERNAL_LIBTOMMATH_FAST=y
-@@ -34,13 +35,7 @@ LIBS += -rdynamic
- CONFIG_EAP_FAST=y
- CONFIG_EAP_TEAP=y
- CONFIG_EAP_IKEV2=y
--
--ifeq ($(CONFIG_TLS), openssl)
--CONFIG_EAP_PWD=y
--endif
--ifeq ($(CONFIG_TLS), wolfssl)
--CONFIG_EAP_PWD=y
--endif
-+CONFIG_EAP_PWD=$(if $(filter openssl wolfssl mbedtls,$(CONFIG_TLS)),y,)
- 
- CONFIG_USIM_SIMULATOR=y
- CONFIG_SIM_SIMULATOR=y
-@@ -136,7 +131,7 @@ CONFIG_TESTING_OPTIONS=y
- CONFIG_MODULE_TESTS=y
- 
- CONFIG_SUITEB=y
--CONFIG_SUITEB192=y
-+CONFIG_SUITEB192=$(if $(filter openssl mbedtls,$(CONFIG_TLS)),y,)
- 
- # AddressSanitizer (ASan) can be enabled by uncommenting the following lines.
- # This can be used as a more efficient memory error detector than valgrind
-diff --git a/tests/hwsim/test_ap_eap.py b/tests/hwsim/test_ap_eap.py
-index a20140316..027a60b25 100644
---- a/tests/hwsim/test_ap_eap.py
-+++ b/tests/hwsim/test_ap_eap.py
-@@ -42,20 +42,42 @@ def check_eap_capa(dev, method):
-     res = dev.get_capability("eap")
-     if method not in res:
-         raise HwsimSkip("EAP method %s not supported in the build" % method)
-+    if method == "FAST" or method == "TEAP":
-+        tls = dev.request("GET tls_library")
-+        if tls.startswith("mbed TLS"):
-+            raise HwsimSkip("EAP-%s not supported with this TLS library: " % method + tls)
- 
- def check_subject_match_support(dev):
-     tls = dev.request("GET tls_library")
--    if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
-+    if tls.startswith("OpenSSL"):
-+        return
-+    elif tls.startswith("wolfSSL"):
-+        return
-+    elif tls.startswith("mbed TLS"):
-+        return
-+    else:
-         raise HwsimSkip("subject_match not supported with this TLS library: " + tls)
- 
- def check_check_cert_subject_support(dev):
-     tls = dev.request("GET tls_library")
--    if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
-+    if tls.startswith("OpenSSL"):
-+        return
-+    elif tls.startswith("wolfSSL"):
-+        return
-+    elif tls.startswith("mbed TLS"):
-+        return
-+    else:
-         raise HwsimSkip("check_cert_subject not supported with this TLS library: " + tls)
- 
- def check_altsubject_match_support(dev):
-     tls = dev.request("GET tls_library")
--    if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
-+    if tls.startswith("OpenSSL"):
-+        return
-+    elif tls.startswith("wolfSSL"):
-+        return
-+    elif tls.startswith("mbed TLS"):
-+        return
-+    else:
-         raise HwsimSkip("altsubject_match not supported with this TLS library: " + tls)
- 
- def check_domain_match(dev):
-@@ -70,7 +92,13 @@ def check_domain_suffix_match(dev):
- 
- def check_domain_match_full(dev):
-     tls = dev.request("GET tls_library")
--    if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
-+    if tls.startswith("OpenSSL"):
-+        return
-+    elif tls.startswith("wolfSSL"):
-+        return
-+    elif tls.startswith("mbed TLS"):
-+        return
-+    else:
-         raise HwsimSkip("domain_suffix_match requires full match with this TLS library: " + tls)
- 
- def check_cert_probe_support(dev):
-@@ -79,8 +107,15 @@ def check_cert_probe_support(dev):
-         raise HwsimSkip("Certificate probing not supported with this TLS library: " + tls)
- 
- def check_ext_cert_check_support(dev):
-+    if not openssl_imported:
-+        raise HwsimSkip("OpenSSL python method not available")
-+
-     tls = dev.request("GET tls_library")
--    if not tls.startswith("OpenSSL"):
-+    if tls.startswith("OpenSSL"):
-+        return
-+    elif tls.startswith("mbed TLS"):
-+        return
-+    else:
-         raise HwsimSkip("ext_cert_check not supported with this TLS library: " + tls)
- 
- def check_ocsp_support(dev):
-@@ -91,10 +126,12 @@ def check_ocsp_support(dev):
-     #    raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
-     #if tls.startswith("wolfSSL"):
-     #    raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
-+    if tls.startswith("mbed TLS"):
-+        raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
- 
- def check_pkcs5_v15_support(dev):
-     tls = dev.request("GET tls_library")
--    if "BoringSSL" in tls or "GnuTLS" in tls:
-+    if "BoringSSL" in tls or "GnuTLS" in tls or "mbed TLS" in tls:
-         raise HwsimSkip("PKCS#5 v1.5 not supported with this TLS library: " + tls)
- 
- def check_tls13_support(dev):
-@@ -122,11 +159,15 @@ def check_pkcs12_support(dev):
-     #    raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
-     if tls.startswith("wolfSSL"):
-         raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
-+    if tls.startswith("mbed TLS"):
-+        raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
- 
- def check_dh_dsa_support(dev):
-     tls = dev.request("GET tls_library")
-     if tls.startswith("internal"):
-         raise HwsimSkip("DH DSA not supported with this TLS library: " + tls)
-+    if tls.startswith("mbed TLS"):
-+        raise HwsimSkip("DH DSA not supported with this TLS library: " + tls)
- 
- def check_ec_support(dev):
-     tls = dev.request("GET tls_library")
-@@ -1741,7 +1782,7 @@ def test_ap_wpa2_eap_ttls_pap_subject_match(dev, apdev):
-     eap_connect(dev[0], hapd, "TTLS", "pap user",
-                 anonymous_identity="ttls", password="password",
-                 ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
--                subject_match="/C=FI/O=w1.fi/CN=server.w1.fi",
-+                check_cert_subject="/C=FI/O=w1.fi/CN=server.w1.fi",
-                 altsubject_match="EMAIL:noone@example.com;DNS:server.w1.fi;URI:http://example.com/")
-     eap_reauth(dev[0], "TTLS")
- 
-@@ -2976,6 +3017,7 @@ def test_ap_wpa2_eap_tls_neg_domain_match(dev, apdev):
- 
- def test_ap_wpa2_eap_tls_neg_subject_match(dev, apdev):
-     """WPA2-Enterprise negative test - subject mismatch"""
-+    check_subject_match_support(dev[0])
-     params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
-     hostapd.add_ap(apdev[0], params)
-     dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
-@@ -3036,6 +3078,7 @@ def test_ap_wpa2_eap_tls_neg_subject_match(dev, apdev):
- 
- def test_ap_wpa2_eap_tls_neg_altsubject_match(dev, apdev):
-     """WPA2-Enterprise negative test - altsubject mismatch"""
-+    check_altsubject_match_support(dev[0])
-     params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
-     hostapd.add_ap(apdev[0], params)
- 
-@@ -3582,7 +3625,7 @@ def test_ap_wpa2_eap_ikev2_oom(dev, apdev):
-             dev[0].request("REMOVE_NETWORK all")
- 
-     tls = dev[0].request("GET tls_library")
--    if not tls.startswith("wolfSSL"):
-+    if not tls.startswith("wolfSSL") and not tls.startswith("mbed TLS"):
-         tests = [(1, "os_get_random;dh_init")]
-     else:
-         tests = [(1, "crypto_dh_init;dh_init")]
-@@ -4896,7 +4939,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca(dev, apdev, params):
-     params["private_key"] = "auth_serv/iCA-server/server.key"
-     hostapd.add_ap(apdev[0], params)
-     tls = dev[0].request("GET tls_library")
--    if "GnuTLS" in tls or "wolfSSL" in tls:
-+    if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
-         ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
-         client_cert = "auth_serv/iCA-user/user_and_ica.pem"
-     else:
-@@ -4962,6 +5005,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_sha1(dev, apdev, params):
-     run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, "-sha1")
- 
- def run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, md):
-+    check_ocsp_support(dev[0])
-     params = int_eap_server_params()
-     params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem"
-     params["server_cert"] = "auth_serv/iCA-server/server.pem"
-@@ -4971,7 +5015,7 @@ def run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, md):
-     try:
-         hostapd.add_ap(apdev[0], params)
-         tls = dev[0].request("GET tls_library")
--        if "GnuTLS" in tls or "wolfSSL" in tls:
-+        if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
-             ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
-             client_cert = "auth_serv/iCA-user/user_and_ica.pem"
-         else:
-@@ -5007,7 +5051,7 @@ def run_ap_wpa2_eap_tls_intermediate_ca_ocsp_revoked(dev, apdev, params, md):
-     try:
-         hostapd.add_ap(apdev[0], params)
-         tls = dev[0].request("GET tls_library")
--        if "GnuTLS" in tls or "wolfSSL" in tls:
-+        if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
-             ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
-             client_cert = "auth_serv/iCA-user/user_and_ica.pem"
-         else:
-@@ -5057,7 +5101,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_multi_missing_resp(dev, apdev, par
-     try:
-         hostapd.add_ap(apdev[0], params)
-         tls = dev[0].request("GET tls_library")
--        if "GnuTLS" in tls or "wolfSSL" in tls:
-+        if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
-             ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
-             client_cert = "auth_serv/iCA-user/user_and_ica.pem"
-         else:
-@@ -5124,7 +5168,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_multi(dev, apdev, params):
- 
-         hostapd.add_ap(apdev[0], params)
-         tls = dev[0].request("GET tls_library")
--        if "GnuTLS" in tls or "wolfSSL" in tls:
-+        if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls:
-             ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
-             client_cert = "auth_serv/iCA-user/user_and_ica.pem"
-         else:
-@@ -5382,6 +5426,7 @@ def test_ap_wpa2_eap_ttls_server_cert_eku_client_server(dev, apdev):
- 
- def test_ap_wpa2_eap_ttls_server_pkcs12(dev, apdev):
-     """WPA2-Enterprise using EAP-TTLS and server PKCS#12 file"""
-+    check_pkcs12_support(dev[0])
-     skip_with_fips(dev[0])
-     params = int_eap_server_params()
-     del params["server_cert"]
-@@ -5394,6 +5439,7 @@ def test_ap_wpa2_eap_ttls_server_pkcs12(dev, apdev):
- 
- def test_ap_wpa2_eap_ttls_server_pkcs12_extra(dev, apdev):
-     """EAP-TTLS and server PKCS#12 file with extra certs"""
-+    check_pkcs12_support(dev[0])
-     skip_with_fips(dev[0])
-     params = int_eap_server_params()
-     del params["server_cert"]
-@@ -5416,6 +5462,7 @@ def test_ap_wpa2_eap_ttls_dh_params_server(dev, apdev):
- 
- def test_ap_wpa2_eap_ttls_dh_params_dsa_server(dev, apdev):
-     """WPA2-Enterprise using EAP-TTLS and alternative server dhparams (DSA)"""
-+    check_dh_dsa_support(dev[0])
-     params = int_eap_server_params()
-     params["dh_file"] = "auth_serv/dsaparam.pem"
-     hapd = hostapd.add_ap(apdev[0], params)
-@@ -5727,8 +5774,8 @@ def test_ap_wpa2_eap_non_ascii_identity2(dev, apdev):
- def test_openssl_cipher_suite_config_wpas(dev, apdev):
-     """OpenSSL cipher suite configuration on wpa_supplicant"""
-     tls = dev[0].request("GET tls_library")
--    if not tls.startswith("OpenSSL"):
--        raise HwsimSkip("TLS library is not OpenSSL: " + tls)
-+    if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
-+        raise HwsimSkip("TLS library is not OpenSSL or mbed TLS: " + tls)
-     params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
-     hapd = hostapd.add_ap(apdev[0], params)
-     eap_connect(dev[0], hapd, "TTLS", "pap user",
-@@ -5754,14 +5801,14 @@ def test_openssl_cipher_suite_config_wpas(dev, apdev):
- def test_openssl_cipher_suite_config_hapd(dev, apdev):
-     """OpenSSL cipher suite configuration on hostapd"""
-     tls = dev[0].request("GET tls_library")
--    if not tls.startswith("OpenSSL"):
--        raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL: " + tls)
-+    if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
-+        raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL or mbed TLS: " + tls)
-     params = int_eap_server_params()
-     params['openssl_ciphers'] = "AES256"
-     hapd = hostapd.add_ap(apdev[0], params)
-     tls = hapd.request("GET tls_library")
--    if not tls.startswith("OpenSSL"):
--        raise HwsimSkip("hostapd TLS library is not OpenSSL: " + tls)
-+    if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"):
-+        raise HwsimSkip("hostapd TLS library is not OpenSSL or mbed TLS: " + tls)
-     eap_connect(dev[0], hapd, "TTLS", "pap user",
-                 anonymous_identity="ttls", password="password",
-                 ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
-@@ -6207,14 +6254,26 @@ def test_ap_wpa2_eap_tls_versions(dev, apdev):
-             check_tls_ver(dev[0], hapd,
-                           "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1",
-                           "TLSv1.2")
--    elif tls.startswith("internal"):
-+    elif tls.startswith("internal") or tls.startswith("mbed TLS"):
-         check_tls_ver(dev[0], hapd,
-                       "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1", "TLSv1.2")
-+<<<<<<< HEAD
-     check_tls_ver(dev[1], hapd,
-                   "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1")
-     check_tls_ver(dev[2], hapd,
-                   "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1")
-     if "run=OpenSSL 1.1.1" in tls or "run=OpenSSL 3." in tls:
-+=======
-+    if tls.startswith("mbed TLS"):
-+        check_tls_ver(dev[2], hapd,
-+                      "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1.0")
-+    else:
-+        check_tls_ver(dev[1], hapd,
-+                      "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1")
-+        check_tls_ver(dev[2], hapd,
-+                      "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1")
-+    if "run=OpenSSL 1.1.1" in tls or "run=OpenSSL 3.0" in tls:
-+>>>>>>> 585bc9ada (hostapd: sync 2024-01-18 openwrt/trunk patch folder)
-         check_tls_ver(dev[0], hapd,
-                       "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0", "TLSv1.3")
- 
-@@ -6235,6 +6294,11 @@ def test_ap_wpa2_eap_tls_versions_server(dev, apdev):
-     tests = [("TLSv1", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
-              ("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
-              ("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")]
-+    tls = dev[0].request("GET tls_library")
-+    if tls.startswith("mbed TLS"):
-+        tests = [#("TLSv1.0", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
-+                 #("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
-+                 ("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")]
-     for exp, flags in tests:
-         hapd.disable()
-         hapd.set("tls_flags", flags)
-@@ -7305,6 +7369,7 @@ def test_ap_wpa2_eap_assoc_rsn(dev, apdev):
- def test_eap_tls_ext_cert_check(dev, apdev):
-     """EAP-TLS and external server certification validation"""
-     # With internal server certificate chain validation
-+    check_ext_cert_check_support(dev[0])
-     id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
-                         identity="tls user",
-                         ca_cert="auth_serv/ca.pem",
-@@ -7317,6 +7382,7 @@ def test_eap_tls_ext_cert_check(dev, apdev):
- def test_eap_ttls_ext_cert_check(dev, apdev):
-     """EAP-TTLS and external server certification validation"""
-     # Without internal server certificate chain validation
-+    check_ext_cert_check_support(dev[0])
-     id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
-                         identity="pap user", anonymous_identity="ttls",
-                         password="password", phase2="auth=PAP",
-@@ -7327,6 +7393,7 @@ def test_eap_ttls_ext_cert_check(dev, apdev):
- def test_eap_peap_ext_cert_check(dev, apdev):
-     """EAP-PEAP and external server certification validation"""
-     # With internal server certificate chain validation
-+    check_ext_cert_check_support(dev[0])
-     id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
-                         identity="user", anonymous_identity="peap",
-                         ca_cert="auth_serv/ca.pem",
-@@ -7337,6 +7404,7 @@ def test_eap_peap_ext_cert_check(dev, apdev):
- 
- def test_eap_fast_ext_cert_check(dev, apdev):
-     """EAP-FAST and external server certification validation"""
-+    check_ext_cert_check_support(dev[0])
-     check_eap_capa(dev[0], "FAST")
-     # With internal server certificate chain validation
-     dev[0].request("SET blob fast_pac_auth_ext ")
-@@ -7351,10 +7419,6 @@ def test_eap_fast_ext_cert_check(dev, apdev):
-     run_ext_cert_check(dev, apdev, id)
- 
- def run_ext_cert_check(dev, apdev, net_id):
--    check_ext_cert_check_support(dev[0])
--    if not openssl_imported:
--        raise HwsimSkip("OpenSSL python method not available")
--
-     params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
-     hapd = hostapd.add_ap(apdev[0], params)
- 
-diff --git a/tests/hwsim/test_ap_ft.py b/tests/hwsim/test_ap_ft.py
-index 3d07d21f7..a708412de 100644
---- a/tests/hwsim/test_ap_ft.py
-+++ b/tests/hwsim/test_ap_ft.py
-@@ -2486,11 +2486,11 @@ def test_ap_ft_ap_oom5(dev, apdev):
-         # This will fail to roam
-         dev[0].roam(bssid1, check_bssid=False)
- 
--    with fail_test(hapd1, 1, "sha256_prf_bits;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
-+    with fail_test(hapd1, 1, "sha256_prf;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
-         # This will fail to roam
-         dev[0].roam(bssid1, check_bssid=False)
- 
--    with fail_test(hapd1, 3, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
-+    with fail_test(hapd1, 2, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
-         # This will fail to roam
-         dev[0].roam(bssid1, check_bssid=False)
- 
-diff --git a/tests/hwsim/test_authsrv.py b/tests/hwsim/test_authsrv.py
-index e0665bcb2..02ec301e5 100644
---- a/tests/hwsim/test_authsrv.py
-+++ b/tests/hwsim/test_authsrv.py
-@@ -156,9 +156,12 @@ def test_authsrv_oom(dev, apdev):
-         if "FAIL" not in authsrv.request("ENABLE"):
-             raise Exception("ENABLE succeeded during OOM")
- 
--    with alloc_fail(authsrv, 1, "tls_init;authsrv_init"):
--        if "FAIL" not in authsrv.request("ENABLE"):
--            raise Exception("ENABLE succeeded during OOM")
-+    # tls_mbedtls.c:tls_init() does not alloc memory (no alloc fail trigger)
-+    tls = dev[0].request("GET tls_library")
-+    if not tls.startswith("mbed TLS"):
-+        with alloc_fail(authsrv, 1, "tls_init;authsrv_init"):
-+            if "FAIL" not in authsrv.request("ENABLE"):
-+                raise Exception("ENABLE succeeded during OOM")
- 
-     for count in range(1, 3):
-         with alloc_fail(authsrv, count, "eap_sim_db_init;authsrv_init"):
-diff --git a/tests/hwsim/test_dpp.py b/tests/hwsim/test_dpp.py
-index 518983bd0..077de58c9 100644
---- a/tests/hwsim/test_dpp.py
-+++ b/tests/hwsim/test_dpp.py
-@@ -39,7 +39,8 @@ def check_dpp_capab(dev, brainpool=False, min_ver=1):
-         raise HwsimSkip("DPP not supported")
-     if brainpool:
-         tls = dev.request("GET tls_library")
--        if (not tls.startswith("OpenSSL") or "run=BoringSSL" in tls) and not tls.startswith("wolfSSL"):
-+        if (not tls.startswith("OpenSSL") or "run=BoringSSL" in tls) and not tls.startswith("wolfSSL") \
-+                                                                     and not tls.startswith("mbed TLS"):
-             raise HwsimSkip("Crypto library does not support Brainpool curves: " + tls)
-     capa = dev.request("GET_CAPABILITY dpp")
-     ver = 1
-@@ -3902,6 +3903,9 @@ def test_dpp_proto_auth_req_no_i_proto_key(dev, apdev):
- 
- def test_dpp_proto_auth_req_invalid_i_proto_key(dev, apdev):
-     """DPP protocol testing - invalid I-proto key in Auth Req"""
-+    tls = dev[0].request("GET tls_library")
-+    if tls.startswith("mbed TLS"):
-+        raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
-     run_dpp_proto_auth_req_missing(dev, 66, "Invalid Initiator Protocol Key")
- 
- def test_dpp_proto_auth_req_no_i_nonce(dev, apdev):
-@@ -3997,7 +4001,12 @@ def test_dpp_proto_auth_resp_no_r_proto_key(dev, apdev):
- 
- def test_dpp_proto_auth_resp_invalid_r_proto_key(dev, apdev):
-     """DPP protocol testing - invalid R-Proto Key in Auth Resp"""
--    run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key")
-+    tls = dev[0].request("GET tls_library")
-+    if tls.startswith("mbed TLS"):
-+        # mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key
-+        run_dpp_proto_auth_resp_missing(dev, 67, "Failed to derive ECDH shared secret")
-+    else:
-+        run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key")
- 
- def test_dpp_proto_auth_resp_no_r_nonce(dev, apdev):
-     """DPP protocol testing - no R-nonce in Auth Resp"""
-@@ -4359,11 +4368,17 @@ def test_dpp_proto_pkex_exchange_resp_invalid_status(dev, apdev):
- 
- def test_dpp_proto_pkex_cr_req_invalid_bootstrap_key(dev, apdev):
-     """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Request"""
-+    tls = dev[0].request("GET tls_library")
-+    if tls.startswith("mbed TLS"):
-+        raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
-     run_dpp_proto_pkex_req_missing(dev, 47,
-                                    "Peer bootstrapping key is invalid")
- 
- def test_dpp_proto_pkex_cr_resp_invalid_bootstrap_key(dev, apdev):
-     """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Response"""
-+    tls = dev[0].request("GET tls_library")
-+    if tls.startswith("mbed TLS"):
-+        raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response")
-     run_dpp_proto_pkex_resp_missing(dev, 48,
-                                     "Peer bootstrapping key is invalid")
- 
-diff --git a/tests/hwsim/test_erp.py b/tests/hwsim/test_erp.py
-index d083993e8..262e9f095 100644
---- a/tests/hwsim/test_erp.py
-+++ b/tests/hwsim/test_erp.py
-@@ -12,7 +12,7 @@ import time
- 
- import hostapd
- from utils import *
--from test_ap_eap import int_eap_server_params, check_tls13_support
-+from test_ap_eap import int_eap_server_params, check_tls13_support, check_eap_capa
- from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations
- 
- def test_erp_initiate_reauth_start(dev, apdev):
-@@ -276,6 +276,7 @@ def test_erp_radius_eap_methods(dev, apdev):
-     params['erp_domain'] = 'example.com'
-     params['disable_pmksa_caching'] = '1'
-     hapd = hostapd.add_ap(apdev[0], params)
-+    tls = dev[0].request("GET tls_library")
- 
-     erp_test(dev[0], hapd, eap="AKA", identity="0232010000000000@example.com",
-              password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
-@@ -289,7 +290,7 @@ def test_erp_radius_eap_methods(dev, apdev):
-              password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
-     erp_test(dev[0], hapd, eap="EKE", identity="erp-eke@example.com",
-              password="hello")
--    if "FAST" in eap_methods:
-+    if "FAST" in eap_methods and check_eap_capa(dev[0], "FAST"):
-         erp_test(dev[0], hapd, eap="FAST", identity="erp-fast@example.com",
-                  password="password", ca_cert="auth_serv/ca.pem",
-                  phase2="auth=GTC",
-@@ -301,13 +302,14 @@ def test_erp_radius_eap_methods(dev, apdev):
-              password="password")
-     erp_test(dev[0], hapd, eap="PAX", identity="erp-pax@example.com",
-              password_hex="0123456789abcdef0123456789abcdef")
--    if "MSCHAPV2" in eap_methods:
-+    if "MSCHAPV2" in eap_methods and check_eap_capa(dev[0], "MSCHAPV2"):
-         erp_test(dev[0], hapd, eap="PEAP", identity="erp-peap@example.com",
-                  password="password", ca_cert="auth_serv/ca.pem",
-                  phase2="auth=MSCHAPV2")
--        erp_test(dev[0], hapd, eap="TEAP", identity="erp-teap@example.com",
--                 password="password", ca_cert="auth_serv/ca.pem",
--                 phase2="auth=MSCHAPV2", pac_file="blob://teap_pac")
-+        if check_eap_capa(dev[0], "TEAP"):
-+            erp_test(dev[0], hapd, eap="TEAP", identity="erp-teap@example.com",
-+                     password="password", ca_cert="auth_serv/ca.pem",
-+                     phase2="auth=MSCHAPV2", pac_file="blob://teap_pac")
-     erp_test(dev[0], hapd, eap="PSK", identity="erp-psk@example.com",
-              password_hex="0123456789abcdef0123456789abcdef")
-     if "PWD" in eap_methods:
-@@ -640,7 +642,7 @@ def test_erp_local_errors(dev, apdev):
-         dev[0].request("REMOVE_NETWORK all")
-         dev[0].wait_disconnected()
- 
--    for count in range(1, 6):
-+    for count in range(1, 4):
-         dev[0].request("ERP_FLUSH")
-         with fail_test(dev[0], count, "hmac_sha256_kdf;eap_peer_erp_init"):
-             dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
-diff --git a/tests/hwsim/test_fils.py b/tests/hwsim/test_fils.py
-index 5cdc28734..17110c5c2 100644
---- a/tests/hwsim/test_fils.py
-+++ b/tests/hwsim/test_fils.py
-@@ -1484,6 +1484,18 @@ def run_fils_sk_pfs(dev, apdev, group, params):
-     check_erp_capa(dev[0])
-     check_ec_group(dev[0], group)
- 
-+    tls = dev[0].request("GET tls_library")
-+    if tls.startswith("mbed TLS"):
-+        if int(group) == 27:
-+            raise HwsimSkip("Brainpool EC group 27 not supported by mbed TLS")
-+    elif not tls.startswith("wolfSSL"):
-+        if int(group) in [25]:
-+            if not (tls.startswith("OpenSSL") and ("build=OpenSSL 1.0.2" in tls or "build=OpenSSL 1.1" in tls or "build=OpenSSL 3.0" in tls) and ("run=OpenSSL 1.0.2" in tls or "run=OpenSSL 1.1" in tls or "run=OpenSSL 3.0" in tls)):
-+                raise HwsimSkip("EC group not supported")
-+        if int(group) in [27, 28, 29, 30]:
-+            if not (tls.startswith("OpenSSL") and ("build=OpenSSL 1.0.2" in tls or "build=OpenSSL 1.1" in tls or "build=OpenSSL 3.0" in tls) and ("run=OpenSSL 1.0.2" in tls or "run=OpenSSL 1.1" in tls or "run=OpenSSL 3.0" in tls)):
-+                raise HwsimSkip("Brainpool EC group not supported")
-+
-     start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
- 
-     bssid = apdev[0]['bssid']
-diff --git a/tests/hwsim/test_pmksa_cache.py b/tests/hwsim/test_pmksa_cache.py
-index 4a3b444ff..4f7f7f760 100644
---- a/tests/hwsim/test_pmksa_cache.py
-+++ b/tests/hwsim/test_pmksa_cache.py
-@@ -958,7 +958,7 @@ def test_pmksa_cache_preauth_wpas_oom(dev, apdev):
-     eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
-                 password_hex="0123456789abcdef0123456789abcdef",
-                 bssid=apdev[0]['bssid'])
--    for i in range(1, 11):
-+    for i in range(1, 10):
-         with alloc_fail(dev[0], i, "rsn_preauth_init"):
-             res = dev[0].request("PREAUTH f2:11:22:33:44:55").strip()
-             logger.info("Iteration %d - PREAUTH command results: %s" % (i, res))
-@@ -966,7 +966,7 @@ def test_pmksa_cache_preauth_wpas_oom(dev, apdev):
-                 state = dev[0].request('GET_ALLOC_FAIL')
-                 if state.startswith('0:'):
-                     break
--                time.sleep(0.05)
-+                time.sleep(0.10)
- 
- def test_pmksa_cache_ctrl(dev, apdev):
-     """PMKSA cache control interface operations"""
-diff --git a/tests/hwsim/test_sae.py b/tests/hwsim/test_sae.py
-index aceb92751..6f9ee5669 100644
---- a/tests/hwsim/test_sae.py
-+++ b/tests/hwsim/test_sae.py
-@@ -178,6 +178,11 @@ def test_sae_groups(dev, apdev):
-     if tls.startswith("OpenSSL") and "run=OpenSSL 1." in tls:
-         logger.info("Add Brainpool EC groups since OpenSSL is new enough")
-         sae_groups += [27, 28, 29, 30]
-+    if tls.startswith("mbed TLS"):
-+        # secp224k1 and secp224r1 (26) have prime p = 1 mod 4, and mbedtls
-+        # does not have code to derive y from compressed format for those curves
-+        sae_groups = [19, 25, 20, 21, 1, 2, 5, 14, 15, 16, 22, 23, 24]
-+        sae_groups += [27, 28, 29, 30]
-     heavy_groups = [14, 15, 16]
-     suitable_groups = [15, 16, 17, 18, 19, 20, 21]
-     groups = [str(g) for g in sae_groups]
-@@ -2194,6 +2199,8 @@ def run_sae_pwe_group(dev, apdev, group):
-             logger.info("Add Brainpool EC groups since OpenSSL is new enough")
-         elif tls.startswith("wolfSSL"):
-             logger.info("Make sure Brainpool EC groups were enabled when compiling wolfSSL")
-+        elif tls.startswith("mbed TLS"):
-+            logger.info("Make sure Brainpool EC groups were enabled when compiling mbed TLS")
-         else:
-             raise HwsimSkip("Brainpool curve not supported")
-     start_sae_pwe_ap(apdev[0], group, 2)
-diff --git a/tests/hwsim/test_suite_b.py b/tests/hwsim/test_suite_b.py
-index d03a39dee..d703dee95 100644
---- a/tests/hwsim/test_suite_b.py
-+++ b/tests/hwsim/test_suite_b.py
-@@ -27,6 +27,8 @@ def check_suite_b_tls_lib(dev, dhe=False, level128=False):
-         return
-     if tls.startswith("wolfSSL"):
-         return
-+    if tls.startswith("mbed TLS"):
-+        return
-     if not tls.startswith("OpenSSL"):
-         raise HwsimSkip("TLS library not supported for Suite B: " + tls)
-     supported = False
-@@ -520,6 +522,7 @@ def test_suite_b_192_rsa_insufficient_dh(dev, apdev):
- 
-     dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
-                    ieee80211w="2",
-+                   openssl_ciphers="DHE-RSA-AES256-GCM-SHA384",
-                    phase1="tls_suiteb=1",
-                    eap="TLS", identity="tls user",
-                    ca_cert="auth_serv/rsa3072-ca.pem",
-diff --git a/tests/hwsim/test_wpas_ctrl.py b/tests/hwsim/test_wpas_ctrl.py
-index 44eb00444..fbe0fb794 100644
---- a/tests/hwsim/test_wpas_ctrl.py
-+++ b/tests/hwsim/test_wpas_ctrl.py
-@@ -1856,7 +1856,7 @@ def _test_wpas_ctrl_oom(dev):
-     tls = dev[0].request("GET tls_library")
-     if not tls.startswith("internal"):
-         tests.append(('NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG', 'FAIL',
--                      4, 'wpas_ctrl_nfc_get_handover_sel_p2p'))
-+                      3, 'wpas_ctrl_nfc_get_handover_sel_p2p'))
-     for cmd, exp, count, func in tests:
-         with alloc_fail(dev[0], count, func):
-             res = dev[0].request(cmd)
-diff --git a/tests/hwsim/utils.py b/tests/hwsim/utils.py
-index 7e3608284..b23c1ee0b 100644
---- a/tests/hwsim/utils.py
-+++ b/tests/hwsim/utils.py
-@@ -145,7 +145,13 @@ def check_imsi_privacy_support(dev):
- 
- def check_tls_tod(dev):
-     tls = dev.request("GET tls_library")
--    if not tls.startswith("OpenSSL") and not tls.startswith("internal"):
-+    if tls.startswith("OpenSSL"):
-+        return
-+    elif tls.startswith("internal"):
-+        return
-+    elif tls.startswith("mbed TLS"):
-+        return
-+    else:
-         raise HwsimSkip("TLS TOD-TOFU/STRICT not supported with this TLS library: " + tls)
- 
- def vht_supported():
-diff --git a/tests/test-crypto_module.c b/tests/test-crypto_module.c
-new file mode 100644
-index 000000000..0f1156142
---- /dev/null
-+++ b/tests/test-crypto_module.c
-@@ -0,0 +1,16 @@
-+/*
-+ * crypto module tests - test program
-+ * Copyright (c) 2022, Glenn Strauss <gstrauss@gluelogic.com>
-+ *
-+ * This software may be distributed under the terms of the BSD license.
-+ * See README for more details.
-+ */
-+
-+#include "utils/includes.h"
-+#include "utils/module_tests.h"
-+#include "crypto/crypto_module_tests.c"
-+
-+int main(int argc, char *argv[])
-+{
-+	return crypto_module_tests();
-+}
-diff --git a/tests/test-https.c b/tests/test-https.c
-index a72e56f9d..e9df82f1d 100644
---- a/tests/test-https.c
-+++ b/tests/test-https.c
-@@ -75,7 +75,7 @@ static int https_client(int s, const char *path)
- 	struct tls_connection *conn;
- 	struct wpabuf *in, *out, *appl;
- 	int res = -1;
--	int need_more_data;
-+	int need_more_data = 0;
- 
- 	os_memset(&conf, 0, sizeof(conf));
- 	conf.event_cb = https_tls_event_cb;
-@@ -93,8 +93,12 @@ static int https_client(int s, const char *path)
- 
- 	for (;;) {
- 		appl = NULL;
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- 		out = tls_connection_handshake2(tls, conn, in, &appl,
- 						&need_more_data);
-+#else
-+		out = tls_connection_handshake(tls, conn, in, &appl);
-+#endif
- 		wpabuf_free(in);
- 		in = NULL;
- 		if (out == NULL) {
-@@ -152,11 +156,15 @@ static int https_client(int s, const char *path)
- 
- 	wpa_printf(MSG_INFO, "Reading HTTP response");
- 	for (;;) {
--		int need_more_data;
-+		int need_more_data = 0;
- 		in = https_recv(s);
- 		if (in == NULL)
- 			goto done;
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- 		out = tls_connection_decrypt2(tls, conn, in, &need_more_data);
-+#else
-+		out = tls_connection_decrypt(tls, conn, in);
-+#endif
- 		if (need_more_data)
- 			wpa_printf(MSG_DEBUG, "HTTP: Need more data");
- 		wpabuf_free(in);
-diff --git a/tests/test-https_server.c b/tests/test-https_server.c
-index 33b448682..9dcca5596 100644
---- a/tests/test-https_server.c
-+++ b/tests/test-https_server.c
-@@ -67,10 +67,12 @@ static struct wpabuf * https_recv(int s, int timeout_ms)
- }
- 
- 
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- static void https_tls_log_cb(void *ctx, const char *msg)
- {
- 	wpa_printf(MSG_DEBUG, "TLS: %s", msg);
- }
-+#endif
- 
- 
- static int https_server(int s)
-@@ -79,7 +81,7 @@ static int https_server(int s)
- 	void *tls;
- 	struct tls_connection_params params;
- 	struct tls_connection *conn;
--	struct wpabuf *in, *out, *appl;
-+	struct wpabuf *in = NULL, *out = NULL, *appl = NULL;
- 	int res = -1;
- 
- 	os_memset(&conf, 0, sizeof(conf));
-@@ -106,7 +108,9 @@ static int https_server(int s)
- 		return -1;
- 	}
- 
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- 	tls_connection_set_log_cb(conn, https_tls_log_cb, NULL);
-+#endif
- 
- 	for (;;) {
- 		in = https_recv(s, 5000);
-@@ -147,12 +151,16 @@ static int https_server(int s)
- 
- 	wpa_printf(MSG_INFO, "Reading HTTP request");
- 	for (;;) {
--		int need_more_data;
-+		int need_more_data = 0;
- 
- 		in = https_recv(s, 5000);
- 		if (!in)
- 			goto done;
-+#ifdef CONFIG_TLS_INTERNAL_SERVER
- 		out = tls_connection_decrypt2(tls, conn, in, &need_more_data);
-+#else
-+		out = tls_connection_decrypt(tls, conn, in);
-+#endif
- 		wpabuf_free(in);
- 		in = NULL;
- 		if (need_more_data) {
-diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
-index dd13308f7..c65acab94 100644
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -10,6 +10,7 @@ ALL += dbus/fi.w1.wpa_supplicant1.service
- EXTRA_TARGETS=dynamic_eap_methods
- 
- CONFIG_FILE=.config
-+-include $(if $(MULTICALL),../hostapd/.config)
- include ../src/build.rules
- 
- ifdef CONFIG_BUILD_PASN_SO
-@@ -188,6 +189,25 @@ ifdef CONFIG_EAPOL_TEST
- CFLAGS += -Werror -DEAPOL_TEST
- 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
-+endif
-+
- ifdef CONFIG_CODE_COVERAGE
- CFLAGS += -O0 -fprofile-arcs -ftest-coverage -U_FORTIFY_SOURCE
- LIBS += -lgcov
-@@ -334,6 +354,7 @@ endif
- ifdef CONFIG_FILS
- CFLAGS += -DCONFIG_FILS
- NEED_SHA384=y
-+NEED_HMAC_SHA384_KDF=y
- NEED_AES_SIV=y
- ifdef CONFIG_FILS_SK_PFS
- CFLAGS += -DCONFIG_FILS_SK_PFS
-@@ -388,7 +409,9 @@ endif
- ifdef CONFIG_IBSS_RSN
- NEED_RSN_AUTHENTICATOR=y
- CFLAGS += -DCONFIG_IBSS_RSN
-+ifndef MULTICALL
- CFLAGS += -DCONFIG_NO_VLAN
-+endif
- OBJS += ibss_rsn.o
- endif
- 
-@@ -980,6 +1003,10 @@ ifdef CONFIG_DYNAMIC_EAP_METHODS
- CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS
- LIBS += -ldl -rdynamic
- endif
-+else
-+  ifdef MULTICALL
-+    OBJS += ../src/eap_common/eap_common.o
-+  endif
- endif
- 
- ifdef CONFIG_AP
-@@ -987,9 +1014,11 @@ NEED_EAP_COMMON=y
- NEED_RSN_AUTHENTICATOR=y
- CFLAGS += -DCONFIG_AP
- OBJS += ap.o
-+ifndef MULTICALL
- CFLAGS += -DCONFIG_NO_RADIUS
- CFLAGS += -DCONFIG_NO_ACCOUNTING
- CFLAGS += -DCONFIG_NO_VLAN
-+endif
- OBJS += ../src/ap/hostapd.o
- OBJS += ../src/ap/wpa_auth_glue.o
- OBJS += ../src/ap/utils.o
-@@ -1029,7 +1058,16 @@ ifdef CONFIG_FILS
- OBJS += ../src/ap/fils_hlp.o
- endif
- ifdef CONFIG_CTRL_IFACE
-+ifdef CONFIG_CTRL_IFACE_MIB
-+CFLAGS += -DCONFIG_CTRL_IFACE_MIB
-+endif
- 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
-@@ -1080,6 +1118,12 @@ endif
- ifdef CONFIG_HS20
- OBJS += ../src/ap/hs20.o
- endif
-+else
-+  ifdef MULTICALL
-+    OBJS += ../src/eap_server/eap_server.o
-+    OBJS += ../src/eap_server/eap_server_identity.o
-+    OBJS += ../src/eap_server/eap_server_methods.o
-+  endif
- endif
- 
- ifdef CONFIG_MBO
-@@ -1089,7 +1133,9 @@ NEED_GAS=y
- endif
- 
- ifdef NEED_RSN_AUTHENTICATOR
-+ifndef MULTICALL
- CFLAGS += -DCONFIG_NO_RADIUS
-+endif
- NEED_AES_WRAP=y
- OBJS += ../src/ap/wpa_auth.o
- OBJS += ../src/ap/wpa_auth_ie.o
-@@ -1188,6 +1234,7 @@ TLS_FUNCS=y
- endif
- 
- ifeq ($(CONFIG_TLS), wolfssl)
-+CFLAGS += -DCONFIG_TLS_WOLFSSL
- ifdef TLS_FUNCS
- CFLAGS += -DWOLFSSL_DER_LOAD
- OBJS += ../src/crypto/tls_wolfssl.o
-@@ -1203,6 +1250,7 @@ LIBS_p += -lwolfssl -lm
- endif
- 
- ifeq ($(CONFIG_TLS), openssl)
-+CFLAGS += -DCONFIG_TLS_OPENSSL
- CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
- ifdef TLS_FUNCS
- CFLAGS += -DEAP_TLS_OPENSSL
-@@ -1229,7 +1277,28 @@ endif
- CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
- endif
- 
-+ifeq ($(CONFIG_TLS), mbedtls)
-+CFLAGS += -DCONFIG_TLS_MBEDTLS
-+ifndef CONFIG_CRYPTO
-+CONFIG_CRYPTO=mbedtls
-+endif
-+ifdef TLS_FUNCS
-+OBJS += ../src/crypto/tls_mbedtls.o
-+LIBS += -lmbedtls -lmbedx509
-+endif
-+OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+OBJS_p += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+OBJS_priv += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
-+ifeq ($(CONFIG_CRYPTO), mbedtls)
-+LIBS += -lmbedcrypto
-+LIBS_p += -lmbedcrypto
-+# XXX: create a config option?
-+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
-+endif
-+endif
-+
- ifeq ($(CONFIG_TLS), gnutls)
-+CFLAGS += -DCONFIG_TLS_GNUTLS
- ifndef CONFIG_CRYPTO
- # default to libgcrypt
- CONFIG_CRYPTO=gnutls
-@@ -1260,6 +1329,7 @@ endif
- endif
- 
- ifeq ($(CONFIG_TLS), internal)
-+CFLAGS += -DCONFIG_TLS_INTERNAL
- ifndef CONFIG_CRYPTO
- CONFIG_CRYPTO=internal
- endif
-@@ -1340,6 +1410,7 @@ endif
- endif
- 
- ifeq ($(CONFIG_TLS), linux)
-+CFLAGS += -DCONFIG_TLS_INTERNAL
- OBJS += ../src/crypto/crypto_linux.o
- OBJS_p += ../src/crypto/crypto_linux.o
- ifdef TLS_FUNCS
-@@ -1421,9 +1492,11 @@ endif
- 
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- NEED_INTERNAL_AES_WRAP=y
- endif
- endif
-+endif
- ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP
- # Seems to be needed at least with BoringSSL
- NEED_INTERNAL_AES_WRAP=y
-@@ -1437,9 +1510,11 @@ endif
- 
- ifdef NEED_INTERNAL_AES_WRAP
- ifneq ($(CONFIG_TLS), linux)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-unwrap.o
- endif
- endif
-+endif
- ifdef NEED_AES_EAX
- AESOBJS += ../src/crypto/aes-eax.o
- NEED_AES_CTR=y
-@@ -1449,35 +1524,45 @@ AESOBJS += ../src/crypto/aes-siv.o
- NEED_AES_CTR=y
- endif
- ifdef NEED_AES_CTR
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-ctr.o
- endif
-+endif
- ifdef NEED_AES_ENCBLOCK
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-encblock.o
- endif
-+endif
- NEED_AES_ENC=y
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-omac1.o
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_WRAP
- NEED_AES_ENC=y
- ifdef NEED_INTERNAL_AES_WRAP
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-wrap.o
- endif
- endif
-+endif
- ifdef NEED_AES_CBC
- NEED_AES_ENC=y
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- AESOBJS += ../src/crypto/aes-cbc.o
- endif
- endif
- endif
- endif
-+endif
- ifdef NEED_AES_ENC
- ifdef CONFIG_INTERNAL_AES
- AESOBJS += ../src/crypto/aes-internal-enc.o
-@@ -1492,12 +1577,16 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1.o
- endif
- endif
- endif
- endif
-+endif
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-prf.o
-+endif
- ifdef CONFIG_INTERNAL_SHA1
- SHA1OBJS += ../src/crypto/sha1-internal.o
- ifdef NEED_FIPS186_2_PRF
-@@ -1509,29 +1598,37 @@ CFLAGS += -DCONFIG_NO_PBKDF2
- else
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
- endif
- endif
- endif
-+endif
- ifdef NEED_T_PRF
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-tprf.o
- endif
-+endif
- ifdef NEED_TLS_PRF
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA1OBJS += ../src/crypto/sha1-tlsprf.o
- endif
- endif
-+endif
- 
- ifndef CONFIG_FIPS
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- MD5OBJS += ../src/crypto/md5.o
- endif
- endif
- endif
- endif
- endif
-+endif
- ifdef NEED_MD5
- ifdef CONFIG_INTERNAL_MD5
- MD5OBJS += ../src/crypto/md5-internal.o
-@@ -1586,12 +1683,17 @@ ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA256OBJS += ../src/crypto/sha256.o
- endif
- endif
- endif
- endif
-+endif
-+
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA256OBJS += ../src/crypto/sha256-prf.o
-+endif
- ifdef CONFIG_INTERNAL_SHA256
- SHA256OBJS += ../src/crypto/sha256-internal.o
- endif
-@@ -1604,50 +1706,68 @@ CFLAGS += -DCONFIG_INTERNAL_SHA512
- SHA256OBJS += ../src/crypto/sha512-internal.o
- endif
- ifdef NEED_TLS_PRF_SHA256
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA256OBJS += ../src/crypto/sha256-tlsprf.o
- endif
-+endif
- ifdef NEED_TLS_PRF_SHA384
-+ifneq ($(CONFIG_TLS), mbedtls)
- SHA256OBJS += ../src/crypto/sha384-tlsprf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA256_KDF
- CFLAGS += -DCONFIG_HMAC_SHA256_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha256-kdf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA384_KDF
- CFLAGS += -DCONFIG_HMAC_SHA384_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-kdf.o
- endif
-+endif
- ifdef NEED_HMAC_SHA512_KDF
- CFLAGS += -DCONFIG_HMAC_SHA512_KDF
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512-kdf.o
- endif
-+endif
- OBJS += $(SHA256OBJS)
- ifdef NEED_SHA384
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384.o
- endif
- endif
- endif
- endif
-+endif
- CFLAGS += -DCONFIG_SHA384
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha384-prf.o
- endif
-+endif
- ifdef NEED_SHA512
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), linux)
- ifneq ($(CONFIG_TLS), gnutls)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512.o
- endif
- endif
- endif
- endif
-+endif
- CFLAGS += -DCONFIG_SHA512
-+ifneq ($(CONFIG_TLS), mbedtls)
- OBJS += ../src/crypto/sha512-prf.o
- endif
-+endif
- 
- ifdef NEED_ASN1
- OBJS += ../src/tls/asn1.o
-@@ -1822,10 +1942,12 @@ ifdef CONFIG_FIPS
- CFLAGS += -DCONFIG_FIPS
- ifneq ($(CONFIG_TLS), openssl)
- ifneq ($(CONFIG_TLS), wolfssl)
-+ifneq ($(CONFIG_TLS), mbedtls)
- $(error CONFIG_FIPS=y requires CONFIG_TLS=openssl)
- endif
- endif
- endif
-+endif
- 
- OBJS += $(SHA1OBJS) $(DESOBJS)
- 
-@@ -2003,32 +2125,38 @@ wpa_priv: $(BCHECK) $(OBJS_priv)
- 
- _OBJS_VAR := OBJS
- include ../src/objs.mk
-+wpa_supplicant_multi.a: .config $(BCHECK) $(OBJS) $(EXTRA_progs)
-+	$(Q)$(CC) -c -o wpa_supplicant_multi.o -Dmain=wpa_supplicant_main $(CFLAGS) main.c
-+	@$(E) "  CC " $<
-+	@rm -f $@
-+	@$(AR) cr $@ wpa_supplicant_multi.o $(OBJS)
-+
- wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs)
--	$(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
-+	+$(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
- 	@$(E) "  LD " $@
- 
- _OBJS_VAR := OBJS_t
- include ../src/objs.mk
- eapol_test: $(OBJS_t)
--	$(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
-+	+$(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
- 	@$(E) "  LD " $@
- 
- _OBJS_VAR := OBJS_t2
- include ../src/objs.mk
- preauth_test: $(OBJS_t2)
--	$(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
-+	+$(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
- 	@$(E) "  LD " $@
- 
- _OBJS_VAR := OBJS_p
- include ../src/objs.mk
- wpa_passphrase: $(OBJS_p)
--	$(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
-+	+$(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
- 	@$(E) "  LD " $@
- 
- _OBJS_VAR := OBJS_c
- include ../src/objs.mk
- wpa_cli: $(OBJS_c)
--	$(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
-+	+$(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
- 	@$(E) "  LD " $@
- 
- LIBCTRL += ../src/common/wpa_ctrl.o
-@@ -2135,6 +2263,12 @@ eap_gpsk.so: $(SRC_EAP_GPSK)
- 	$(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
- 	@$(E) "  sed" $<
- 
-+dump_cflags:
-+	@printf "%s " "$(CFLAGS)"
-+
-+dump_ldflags:
-+	@printf "%s " "$(LDFLAGS) $(LIBS) $(EXTRALIBS)"
-+
- wpa_supplicant.exe: wpa_supplicant
- 	mv -f $< $@
- wpa_cli.exe: wpa_cli
-diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
-index 69a0e5ee1..43c39d7ce 100644
---- a/wpa_supplicant/ap.c
-+++ b/wpa_supplicant/ap.c
-@@ -1520,7 +1520,7 @@ int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
- #endif /* CONFIG_WPS */
- 
- 
--#ifdef CONFIG_CTRL_IFACE
-+#if defined(CONFIG_CTRL_IFACE) && defined(CONFIG_CTRL_IFACE_MIB)
- 
- int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
- 			    char *buf, size_t buflen)
-@@ -1846,11 +1846,31 @@ int ap_switch_channel(struct wpa_supplicant *wpa_s,
- 
- 
- #ifdef CONFIG_CTRL_IFACE
-+
-+static int __ap_ctrl_iface_chanswitch(struct hostapd_iface *iface,
-+				      struct csa_settings *settings)
-+{
-+#ifdef NEED_AP_MLME
-+	if (!iface || !iface->bss[0])
-+		return 0;
-+
-+	return hostapd_switch_channel(iface->bss[0], settings);
-+#else
-+	return -1;
-+#endif
-+}
-+
-+
- int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos)
- {
- 	struct csa_settings settings;
- 	int ret = hostapd_parse_csa_settings(pos, &settings);
- 
-+	if (!(wpa_s->ap_iface && wpa_s->ap_iface->bss[0]) &&
-+	    !(wpa_s->ifmsh && wpa_s->ifmsh->bss[0]))
-+		return -1;
-+
-+	ret = __ap_ctrl_iface_chanswitch(wpa_s->ap_iface, &settings);
- 	if (ret)
- 		return ret;
- 
-diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
-index 2c756136c..c3943355d 100644
---- a/wpa_supplicant/config.c
-+++ b/wpa_supplicant/config.c
-@@ -18,6 +18,7 @@
- #include "eap_peer/eap.h"
- #include "p2p/p2p.h"
- #include "fst/fst.h"
-+#include "ap/sta_info.h"
- #include "config.h"
- 
- 
-@@ -2421,6 +2422,97 @@ static char * wpa_config_write_mac_value(const struct parse_data *data,
- #endif /* NO_CONFIG_WRITE */
- 
- 
-+static int wpa_config_parse_mcast_rate(const struct parse_data *data,
-+				       struct wpa_ssid *ssid, int line,
-+				       const char *value)
-+{
-+	ssid->mcast_rate = (int)(strtod(value, NULL) * 10);
-+
-+	return 0;
-+}
-+
-+#ifndef NO_CONFIG_WRITE
-+static char * wpa_config_write_mcast_rate(const struct parse_data *data,
-+					  struct wpa_ssid *ssid)
-+{
-+	char *value;
-+	int res;
-+
-+	if (!ssid->mcast_rate == 0)
-+		return NULL;
-+
-+	value = os_malloc(6); /* longest: 300.0 */
-+	if (value == NULL)
-+		return NULL;
-+	res = os_snprintf(value, 5, "%.1f", (double)ssid->mcast_rate / 10);
-+	if (res < 0) {
-+		os_free(value);
-+		return NULL;
-+	}
-+	return value;
-+}
-+#endif /* NO_CONFIG_WRITE */
-+
-+static int wpa_config_parse_rates(const struct parse_data *data,
-+				  struct wpa_ssid *ssid, int line,
-+				  const char *value)
-+{
-+	int i;
-+	char *pos, *r, *sptr, *end;
-+	double rate;
-+
-+	pos = (char *)value;
-+	r = strtok_r(pos, ",", &sptr);
-+	i = 0;
-+	while (pos && i < WLAN_SUPP_RATES_MAX) {
-+		rate = 0.0;
-+		if (r)
-+			rate = strtod(r, &end);
-+		ssid->rates[i] = rate * 2;
-+		if (*end != '\0' || rate * 2 != ssid->rates[i])
-+			return 1;
-+
-+		i++;
-+		r = strtok_r(NULL, ",", &sptr);
-+	}
-+
-+	return 0;
-+}
-+
-+#ifndef NO_CONFIG_WRITE
-+static char * wpa_config_write_rates(const struct parse_data *data,
-+				     struct wpa_ssid *ssid)
-+{
-+	char *value, *pos;
-+	int res, i;
-+
-+	if (ssid->rates[0] <= 0)
-+		return NULL;
-+
-+	value = os_malloc(6 * WLAN_SUPP_RATES_MAX + 1);
-+	if (value == NULL)
-+		return NULL;
-+	pos = value;
-+	for (i = 0; i < WLAN_SUPP_RATES_MAX - 1; i++) {
-+		res = os_snprintf(pos, 6, "%.1f,", (double)ssid->rates[i] / 2);
-+		if (res < 0) {
-+			os_free(value);
-+			return NULL;
-+		}
-+		pos += res;
-+	}
-+	res = os_snprintf(pos, 6, "%.1f",
-+			  (double)ssid->rates[WLAN_SUPP_RATES_MAX - 1] / 2);
-+	if (res < 0) {
-+		os_free(value);
-+		return NULL;
-+	}
-+
-+	value[6 * WLAN_SUPP_RATES_MAX] = '\0';
-+	return value;
-+}
-+#endif /* NO_CONFIG_WRITE */
-+
- /* Helper macros for network block parser */
- 
- #ifdef OFFSET
-@@ -2639,6 +2731,7 @@ static const struct parse_data ssid_fields[] = {
- #else /* CONFIG_MESH */
- 	{ INT_RANGE(mode, 0, 4) },
- #endif /* CONFIG_MESH */
-+	{ INT_RANGE(noscan, 0, 1) },
- 	{ INT_RANGE(proactive_key_caching, 0, 1) },
- 	{ INT_RANGE(disabled, 0, 2) },
- 	{ STR(id_str) },
-@@ -2712,6 +2805,8 @@ static const struct parse_data ssid_fields[] = {
- 	{ INT(ap_max_inactivity) },
- 	{ INT(dtim_period) },
- 	{ INT(beacon_int) },
-+	{ FUNC(rates) },
-+	{ FUNC(mcast_rate) },
- #ifdef CONFIG_MACSEC
- 	{ INT_RANGE(macsec_policy, 0, 1) },
- 	{ INT_RANGE(macsec_integ_only, 0, 1) },
-diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
-index 1a2c0c9be..7a3ed6373 100644
---- a/wpa_supplicant/config_file.c
-+++ b/wpa_supplicant/config_file.c
-@@ -326,8 +326,13 @@ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
- 	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));
-@@ -775,6 +780,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
- #endif /* IEEE8021X_EAPOL */
- 	INT(mode);
- 	INT(no_auto_peer);
-+	INT(noscan);
- 	INT(mesh_fwding);
- 	INT(frequency);
- 	INT(enable_edmg);
-diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
-index e40650c27..de79972b6 100644
---- a/wpa_supplicant/config_ssid.h
-+++ b/wpa_supplicant/config_ssid.h
-@@ -10,8 +10,10 @@
- #define CONFIG_SSID_H
- 
- #include "common/defs.h"
-+#include "ap/sta_info.h"
- #include "utils/list.h"
- #include "eap_peer/eap_config.h"
-+#include "drivers/nl80211_copy.h"
- 
- 
- #define DEFAULT_EAP_WORKAROUND ((unsigned int) -1)
-@@ -879,6 +881,9 @@ struct wpa_ssid {
- 	 */
- 	void *parent_cred;
- 
-+	unsigned char rates[WLAN_SUPP_RATES_MAX];
-+	double mcast_rate;
-+
- #ifdef CONFIG_MACSEC
- 	/**
- 	 * macsec_policy - Determines the policy for MACsec secure session
-@@ -1035,6 +1040,8 @@ struct wpa_ssid {
- 	 */
- 	int no_auto_peer;
- 
-+	int noscan;
-+
- 	/**
- 	 * mesh_rssi_threshold - Set mesh parameter mesh_rssi_threshold (dBm)
- 	 *
-diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
-index d0fda4cd9..ec45f29bb 100644
---- a/wpa_supplicant/ctrl_iface.c
-+++ b/wpa_supplicant/ctrl_iface.c
-@@ -2355,7 +2355,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
- 			pos += ret;
- 		}
- 
--#ifdef CONFIG_AP
-+#if defined(CONFIG_AP) && defined(CONFIG_CTRL_IFACE_MIB)
- 		if (wpa_s->ap_iface) {
- 			pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos,
- 							    end - pos,
-@@ -12542,6 +12542,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
- 			reply_len = -1;
- 	} else if (os_strncmp(buf, "NOTE ", 5) == 0) {
- 		wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
-+#ifdef CONFIG_CTRL_IFACE_MIB
- 	} else if (os_strcmp(buf, "MIB") == 0) {
- 		reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
- 		if (reply_len >= 0) {
-@@ -12554,6 +12555,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
- 				reply_size - reply_len);
- #endif /* CONFIG_MACSEC */
- 		}
-+#endif
- 	} else if (os_strncmp(buf, "STATUS", 6) == 0) {
- 		reply_len = wpa_supplicant_ctrl_iface_status(
- 			wpa_s, buf + 6, reply, reply_size);
-@@ -13042,6 +13044,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
- 		reply_len = wpa_supplicant_ctrl_iface_bss(
- 			wpa_s, buf + 4, reply, reply_size);
- #ifdef CONFIG_AP
-+#ifdef CONFIG_CTRL_IFACE_MIB
- 	} else if (os_strcmp(buf, "STA-FIRST") == 0) {
- 		reply_len = ap_ctrl_iface_sta_first(wpa_s, reply, reply_size);
- 	} else if (os_strncmp(buf, "STA ", 4) == 0) {
-@@ -13050,12 +13053,15 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
- 	} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
- 		reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply,
- 						   reply_size);
-+#endif
-+#ifdef CONFIG_CTRL_IFACE_MIB
- 	} else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
- 		if (ap_ctrl_iface_sta_deauthenticate(wpa_s, buf + 15))
- 			reply_len = -1;
- 	} else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
- 		if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13))
- 			reply_len = -1;
-+#endif
- 	} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
- 		if (ap_ctrl_iface_chanswitch(wpa_s, buf + 12))
- 			reply_len = -1;
-@@ -13214,7 +13220,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
- 		if (wpas_ctrl_iface_coloc_intf_report(wpa_s, buf + 18))
- 			reply_len = -1;
- #endif /* CONFIG_WNM */
--#ifdef CONFIG_WNM_AP
-+#if defined(CONFIG_AP) && defined(CONFIG_WNM_AP)
- 	} else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
- 		if (ap_ctrl_iface_disassoc_imminent(wpa_s, buf + 18))
- 			reply_len = -1;
-@@ -13224,7 +13230,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
- 	} else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
- 		if (ap_ctrl_iface_bss_tm_req(wpa_s, buf + 11))
- 			reply_len = -1;
--#endif /* CONFIG_WNM_AP */
-+#endif /* CONFIG_AP && CONFIG_WNM_AP */
- 	} else if (os_strcmp(buf, "FLUSH") == 0) {
- 		wpa_supplicant_ctrl_iface_flush(wpa_s);
- 	} else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) {
-diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
-index 52befd8f1..ace6c5530 100644
---- a/wpa_supplicant/defconfig
-+++ b/wpa_supplicant/defconfig
-@@ -10,8 +10,8 @@
- # to override previous values of the variables.
- 
- 
--# Uncomment following two lines and fix the paths if you have installed OpenSSL
--# or GnuTLS in non-default location
-+# Uncomment following two lines and fix the paths if you have installed TLS
-+# libraries in a non-default location
- #CFLAGS += -I/usr/local/openssl/include
- #LIBS += -L/usr/local/openssl/lib
- 
-@@ -20,6 +20,7 @@
- # used to fix build issues on such systems (krb5.h not found).
- #CFLAGS += -I/usr/include/kerberos
- 
-+
- # Driver interface for generic Linux wireless extensions
- # Note: WEXT is deprecated in the current Linux kernel version and no new
- # functionality is added to it. nl80211-based interface is the new
-@@ -329,6 +330,7 @@ CONFIG_BACKEND=file
- # openssl = OpenSSL (default)
- # gnutls = GnuTLS
- # internal = Internal TLSv1 implementation (experimental)
-+# mbedtls = mbed TLS
- # linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
- # none = Empty template
- #CONFIG_TLS=openssl
-diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
-index 95953de92..673c3cc11 100644
---- a/wpa_supplicant/eapol_test.c
-+++ b/wpa_supplicant/eapol_test.c
-@@ -31,7 +31,12 @@
- #include "ctrl_iface.h"
- #include "pcsc_funcs.h"
- #include "wpas_glue.h"
-+#include "drivers/driver.h"
- 
-+void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
-+			     union wpa_event_data *data);
-+void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
-+			     union wpa_event_data *data);
- 
- const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
- 
-@@ -1325,6 +1330,10 @@ static void usage(void)
- 	       "option several times.\n");
- }
- 
-+extern void supplicant_event(void *ctx, enum wpa_event_type event,
-+			     union wpa_event_data *data);
-+extern void supplicant_event_global(void *ctx, enum wpa_event_type event,
-+			     union wpa_event_data *data);
- 
- int main(int argc, char *argv[])
- {
-@@ -1348,6 +1357,8 @@ int main(int argc, char *argv[])
- 	if (os_program_init())
- 		return -1;
- 
-+	wpa_supplicant_event = supplicant_event;
-+	wpa_supplicant_event_global = supplicant_event_global;
- 	hostapd_logger_register_cb(hostapd_logger_cb);
- 
- 	os_memset(&eapol_test, 0, sizeof(eapol_test));
-diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
-index ca2794638..2a9342318 100644
---- a/wpa_supplicant/events.c
-+++ b/wpa_supplicant/events.c
-@@ -2935,8 +2935,6 @@ void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s)
- }
- 
- 
--#ifdef CONFIG_INTERWORKING
--
- static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map,
- 			    size_t len)
- {
-@@ -2969,8 +2967,6 @@ static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s,
- 	}
- }
- 
--#endif /* CONFIG_INTERWORKING */
--
- 
- static void wpa_supplicant_set_4addr_mode(struct wpa_supplicant *wpa_s)
- {
-@@ -3349,10 +3345,8 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
- 		wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
- 				       data->assoc_info.resp_ies_len);
- #endif /* CONFIG_WNM */
--#ifdef CONFIG_INTERWORKING
- 		interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
- 						data->assoc_info.resp_ies_len);
--#endif /* CONFIG_INTERWORKING */
- 		if (wpa_s->hw_capab == CAPAB_VHT &&
- 		    get_ie(data->assoc_info.resp_ies,
- 			   data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP))
-@@ -5928,8 +5922,8 @@ static void wpas_link_reconfig(struct wpa_supplicant *wpa_s)
- }
- 
- 
--void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
--			  union wpa_event_data *data)
-+void supplicant_event(void *ctx, enum wpa_event_type event,
-+		      union wpa_event_data *data)
- {
- 	struct wpa_supplicant *wpa_s = ctx;
- 	int resched;
-@@ -5964,6 +5958,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
- 		event_to_string(event), event);
- #endif /* CONFIG_NO_STDOUT_DEBUG */
- 
-+	wpas_ucode_event(wpa_s, event, data);
- 	switch (event) {
- 	case EVENT_AUTH:
- #ifdef CONFIG_FST
-@@ -6881,7 +6876,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
- }
- 
- 
--void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
-+void supplicant_event_global(void *ctx, enum wpa_event_type event,
- 				 union wpa_event_data *data)
- {
- 	struct wpa_supplicant *wpa_s;
-diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c
-index 9229eb51f..ee152c5b9 100644
---- a/wpa_supplicant/main.c
-+++ b/wpa_supplicant/main.c
-@@ -12,6 +12,7 @@
- #endif /* __linux__ */
- 
- #include "common.h"
-+#include "build_features.h"
- #include "crypto/crypto.h"
- #include "fst/fst.h"
- #include "wpa_supplicant_i.h"
-@@ -202,7 +203,7 @@ int main(int argc, char *argv[])
- 
- 	for (;;) {
- 		c = getopt(argc, argv,
--			   "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvW");
-+			   "b:Bc:C:D:de:f:g:G:hi:I:KLMm:nNo:O:p:P:qsTtuv::W");
- 		if (c < 0)
- 			break;
- 		switch (c) {
-@@ -267,6 +268,9 @@ int main(int argc, char *argv[])
- 			params.conf_p2p_dev = optarg;
- 			break;
- #endif /* CONFIG_P2P */
-+		case 'n':
-+			iface_count = 0;
-+			break;
- 		case 'o':
- 			params.override_driver = optarg;
- 			break;
-@@ -302,8 +306,12 @@ int main(int argc, char *argv[])
- 			break;
- #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
- 		case 'v':
--			printf("%s\n", wpa_supplicant_version);
--			exitcode = 0;
-+			if (optarg) {
-+				exitcode = !has_feature(optarg);
-+			} else {
-+				printf("%s\n", wpa_supplicant_version);
-+				exitcode = 0;
-+			}
- 			goto out;
- 		case 'W':
- 			params.wait_for_monitor++;
-diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
-index 85c1ea8ba..dabbb0334 100644
---- a/wpa_supplicant/mesh.c
-+++ b/wpa_supplicant/mesh.c
-@@ -506,6 +506,8 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
- 			   frequency);
- 		goto out_free;
- 	}
-+	if (conf->noscan)
-+		ssid->noscan = 1;
- 
- 	if (ssid->mesh_basic_rates == NULL) {
- 		/*
-@@ -630,6 +632,7 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s,
- 
- 	params->meshid = ssid->ssid;
- 	params->meshid_len = ssid->ssid_len;
-+	params->mcast_rate = ssid->mcast_rate;
- 	ibss_mesh_setup_freq(wpa_s, ssid, &params->freq);
- 	wpa_s->mesh_ht_enabled = !!params->freq.ht_enabled;
- 	wpa_s->mesh_vht_enabled = !!params->freq.vht_enabled;
-diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
-index 60f85624f..67352a121 100644
---- a/wpa_supplicant/wpa_cli.c
-+++ b/wpa_supplicant/wpa_cli.c
-@@ -26,6 +26,15 @@
- #include <cutils/properties.h>
- #endif /* ANDROID */
- 
-+#ifndef CONFIG_P2P
-+#define CONFIG_P2P
-+#endif
-+#ifndef CONFIG_AP
-+#define CONFIG_AP
-+#endif
-+#ifndef CONFIG_MESH
-+#define CONFIG_MESH
-+#endif
- 
- static const char *const wpa_cli_version =
- "wpa_cli v" VERSION_STR "\n"
-diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
-index 88f3f2a52..92efe5629 100644
---- a/wpa_supplicant/wpa_priv.c
-+++ b/wpa_supplicant/wpa_priv.c
-@@ -1042,8 +1042,8 @@ static void wpa_priv_send_ft_response(struct wpa_priv_interface *iface,
- }
- 
- 
--void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
--			  union wpa_event_data *data)
-+static void supplicant_event(void *ctx, enum wpa_event_type event,
-+			     union wpa_event_data *data)
- {
- 	struct wpa_priv_interface *iface = ctx;
- 
-@@ -1106,7 +1106,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
- }
- 
- 
--void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
-+void supplicant_event_global(void *ctx, enum wpa_event_type event,
- 				 union wpa_event_data *data)
- {
- 	struct wpa_priv_global *global = ctx;
-@@ -1220,6 +1220,8 @@ int main(int argc, char *argv[])
- 	if (os_program_init())
- 		return -1;
- 
-+	wpa_supplicant_event = supplicant_event;
-+	wpa_supplicant_event_global = supplicant_event_global;
- 	wpa_priv_fd_workaround();
- 
- 	os_memset(&global, 0, sizeof(global));
-diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
-index ab71e2f27..fea84fe49 100644
---- a/wpa_supplicant/wpa_supplicant.c
-+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -1060,6 +1060,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
- 		sme_sched_obss_scan(wpa_s, 0);
- 	}
- 	wpa_s->wpa_state = state;
-+	wpas_ucode_update_state(wpa_s);
- 
- #ifdef CONFIG_BGSCAN
- 	if (state == WPA_COMPLETED && wpa_s->current_ssid != wpa_s->bgscan_ssid)
-@@ -2698,7 +2699,7 @@ static int drv_supports_vht(struct wpa_supplicant *wpa_s,
- }
- 
- 
--static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode)
-+static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode, bool dfs_enabled)
- {
- 	int i;
- 
-@@ -2707,7 +2708,10 @@ static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode)
- 
- 		chan = hw_get_channel_chan(mode, i, NULL);
- 		if (!chan ||
--		    chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
-+		    chan->flag & HOSTAPD_CHAN_DISABLED)
-+			return false;
-+		
-+		if (!dfs_enabled && chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
- 			return false;
- 	}
- 
-@@ -2767,7 +2771,7 @@ static bool ibss_mesh_can_use_vht(struct wpa_supplicant *wpa_s,
- 				  const struct wpa_ssid *ssid,
- 				  struct hostapd_hw_modes *mode)
- {
--	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
-+	if (mode->mode != HOSTAPD_MODE_IEEE80211A && !(ssid->noscan))
- 		return false;
- 
- 	if (!drv_supports_vht(wpa_s, ssid))
-@@ -2834,12 +2838,13 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s,
- 				   const struct wpa_ssid *ssid,
- 				   struct hostapd_hw_modes *mode,
- 				   struct hostapd_freq_params *freq,
--				   int obss_scan) {
-+				   int obss_scan, bool dfs_enabled) {
- 	int chan_idx;
- 	struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
- 	int i, res;
- 	unsigned int j;
- 	static const int ht40plus[] = {
-+		1, 2, 3, 4, 5, 6, 7,
- 		36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
- 		149, 157, 165, 173, 184, 192
- 	};
-@@ -2858,8 +2863,11 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s,
- 		return;
- 
- 	/* Check primary channel flags */
--	if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
-+	if (pri_chan->flag & HOSTAPD_CHAN_DISABLED)
- 		return;
-+	if (pri_chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
-+		if (!dfs_enabled)
-+			return;
- 
- #ifdef CONFIG_HT_OVERRIDES
- 	if (ssid->disable_ht40)
-@@ -2885,8 +2893,11 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s,
- 		return;
- 
- 	/* Check secondary channel flags */
--	if (sec_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
-+	if (sec_chan->flag & HOSTAPD_CHAN_DISABLED)
- 		return;
-+	if (sec_chan->flag & (HOSTAPD_CHAN_RADAR | HOSTAPD_CHAN_NO_IR))
-+		if (!dfs_enabled)
-+			return;
- 
- 	if (ht40 == -1) {
- 		if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS))
-@@ -2941,7 +2952,7 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
- 				       const struct wpa_ssid *ssid,
- 				       struct hostapd_hw_modes *mode,
- 				       struct hostapd_freq_params *freq,
--				       int ieee80211_mode, bool is_6ghz) {
-+				       int ieee80211_mode, bool is_6ghz, bool dfs_enabled) {
- 	static const int bw80[] = {
- 		5180, 5260, 5500, 5580, 5660, 5745, 5825,
- 		5955, 6035, 6115, 6195, 6275, 6355, 6435,
-@@ -2986,7 +2997,7 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
- 		goto skip_80mhz;
- 
- 	/* Use 40 MHz if channel not usable */
--	if (!ibss_mesh_is_80mhz_avail(channel, mode))
-+	if (!ibss_mesh_is_80mhz_avail(channel, mode, dfs_enabled))
- 		goto skip_80mhz;
- 
- 	chwidth = CONF_OPER_CHWIDTH_80MHZ;
-@@ -3000,7 +3011,7 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
- 	if ((mode->he_capab[ieee80211_mode].phy_cap[
- 		     HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
- 	     HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G) && is_6ghz &&
--	    ibss_mesh_is_80mhz_avail(channel + 16, mode)) {
-+	    ibss_mesh_is_80mhz_avail(channel + 16, mode, dfs_enabled)) {
- 		for (j = 0; j < ARRAY_SIZE(bw160); j++) {
- 			if (freq->freq == bw160[j]) {
- 				chwidth = CONF_OPER_CHWIDTH_160MHZ;
-@@ -3028,10 +3039,12 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
- 				if (!chan)
- 					continue;
- 
--				if (chan->flag & (HOSTAPD_CHAN_DISABLED |
--						  HOSTAPD_CHAN_NO_IR |
--						  HOSTAPD_CHAN_RADAR))
-+				if (chan->flag & HOSTAPD_CHAN_DISABLED)
- 					continue;
-+				if (chan->flag & (HOSTAPD_CHAN_RADAR |
-+						  HOSTAPD_CHAN_NO_IR))
-+					if (!dfs_enabled)
-+						continue;
- 
- 				/* Found a suitable second segment for 80+80 */
- 				chwidth = CONF_OPER_CHWIDTH_80P80MHZ;
-@@ -3083,12 +3096,17 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
- 	int ieee80211_mode = wpas_mode_to_ieee80211_mode(ssid->mode);
- 	enum hostapd_hw_mode hw_mode;
- 	struct hostapd_hw_modes *mode = NULL;
--	int i, obss_scan = 1;
-+	int i, obss_scan = !(ssid->noscan);
- 	u8 channel;
- 	bool is_6ghz, is_24ghz;
-+	bool dfs_enabled = wpa_s->conf->country[0] && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_RADAR);
- 
- 	freq->freq = ssid->frequency;
- 
-+	if (ssid->fixed_freq) {
-+		obss_scan = 0;
-+	}
-+
- 	if (ssid->mode == WPAS_MODE_IBSS && !ssid->fixed_freq) {
- 		struct wpa_bss *bss = ibss_find_existing_bss(wpa_s, ssid);
- 
-@@ -3132,11 +3150,13 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
- 		freq->he_enabled = ibss_mesh_can_use_he(wpa_s, ssid, mode,
- 							ieee80211_mode);
- 	freq->channel = channel;
-+	if (mode->mode == HOSTAPD_MODE_IEEE80211G && ssid->noscan)
-+		ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan, dfs_enabled);
- 	/* Setup higher BW only for 5 GHz */
- 	if (mode->mode == HOSTAPD_MODE_IEEE80211A) {
--		ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan);
-+		ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan, dfs_enabled);
- 		if (!ibss_mesh_select_80_160mhz(wpa_s, ssid, mode, freq,
--						ieee80211_mode, is_6ghz))
-+						ieee80211_mode, is_6ghz, dfs_enabled))
- 			freq->he_enabled = freq->vht_enabled = false;
- 	}
- 
-@@ -4240,6 +4260,12 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
- 			params.beacon_int = ssid->beacon_int;
- 		else
- 			params.beacon_int = wpa_s->conf->beacon_int;
-+		int i = 0;
-+		while (i < WLAN_SUPP_RATES_MAX) {
-+			params.rates[i] = ssid->rates[i];
-+			i++;
-+		}
-+		params.mcast_rate = ssid->mcast_rate;
- 	}
- 
- 	if (bss && ssid->enable_edmg)
-@@ -5861,7 +5887,7 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent)
- 	if (wpa_s == NULL)
- 		return NULL;
- 	wpa_s->scan_req = INITIAL_SCAN_REQ;
--	wpa_s->scan_interval = 5;
-+	wpa_s->scan_interval = 1;
- 	wpa_s->new_connection = 1;
- 	wpa_s->parent = parent ? parent : wpa_s;
- 	wpa_s->p2pdev = wpa_s->parent;
-@@ -7576,7 +7602,6 @@ struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global,
- 	return NULL;
- }
- 
--
- /**
-  * wpa_supplicant_match_existing - Match existing interfaces
-  * @global: Pointer to global data from wpa_supplicant_init()
-@@ -7611,6 +7636,11 @@ static int wpa_supplicant_match_existing(struct wpa_global *global)
- 
- #endif /* CONFIG_MATCH_IFACE */
- 
-+extern void supplicant_event(void *ctx, enum wpa_event_type event,
-+			     union wpa_event_data *data);
-+
-+extern void supplicant_event_global(void *ctx, enum wpa_event_type event,
-+ 				 union wpa_event_data *data);
- 
- /**
-  * wpa_supplicant_add_iface - Add a new network interface
-@@ -7693,6 +7723,9 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
- 	}
- #endif /* CONFIG_P2P */
- 
-+	wpas_ubus_add_bss(wpa_s);
-+	wpas_ucode_add_bss(wpa_s);
-+
- 	return wpa_s;
- }
- 
-@@ -7719,6 +7752,9 @@ int wpa_supplicant_remove_iface(struct wpa_global *global,
- 	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 */
- 	prev = global->ifaces;
- 	if (prev == wpa_s) {
-@@ -7867,6 +7903,8 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
- #ifndef CONFIG_NO_WPA_MSG
- 	wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
- #endif /* CONFIG_NO_WPA_MSG */
-+	wpa_supplicant_event = supplicant_event;
-+	wpa_supplicant_event_global = supplicant_event_global;
- 
- 	if (params->wpa_debug_file_path)
- 		wpa_debug_open_file(params->wpa_debug_file_path);
-@@ -8025,6 +8063,7 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
- 
- 	eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
- 			       wpas_periodic, global, NULL);
-+	wpas_ucode_init(global);
- 
- 	return global;
- }
-@@ -8097,6 +8136,8 @@ void wpa_supplicant_deinit(struct wpa_global *global)
- 
- 	wpas_notify_supplicant_deinitialized(global);
- 
-+	wpas_ucode_free();
-+
- 	eap_peer_unregister_methods();
- #ifdef CONFIG_AP
- 	eap_server_unregister_methods();
-diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
-index 426d077d2..e0c0e5b0c 100644
---- a/wpa_supplicant/wpa_supplicant_i.h
-+++ b/wpa_supplicant/wpa_supplicant_i.h
-@@ -21,6 +21,8 @@
- #include "config_ssid.h"
- #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;
-@@ -319,6 +321,8 @@ struct wpa_global {
- #endif /* CONFIG_WIFI_DISPLAY */
- 
- 	struct psk_list_entry *add_psk; /* From group formation */
-+
-+	struct ubus_object ubus_global;
- };
- 
- 
-@@ -693,6 +697,8 @@ struct wpa_supplicant {
- 	unsigned char own_addr[ETH_ALEN];
- 	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 */
-diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
-index 8cd355f6b..136b06583 100644
---- a/wpa_supplicant/wps_supplicant.c
-+++ b/wpa_supplicant/wps_supplicant.c
-@@ -33,6 +33,7 @@
- #include "p2p/p2p.h"
- #include "p2p_supplicant.h"
- #include "wps_supplicant.h"
-+#include "ubus.h"
- 
- 
- #ifndef WPS_PIN_SCAN_IGNORE_SEL_REG
-@@ -401,6 +402,8 @@ static int wpa_supplicant_wps_cred(void *ctx,
- 	wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
- 			cred->cred_attr, cred->cred_attr_len);
- 
-+	wpas_ubus_notify(wpa_s, cred);
-+
- 	if (wpa_s->conf->wps_cred_processing == 1)
- 		return 0;
- 
-diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
-index aae3f7cb5..30b4e9105 100644
---- a/wpa_supplicant/wps_supplicant.h
-+++ b/wpa_supplicant/wps_supplicant.h
-@@ -9,6 +9,7 @@
- #ifndef WPS_SUPPLICANT_H
- #define WPS_SUPPLICANT_H
- 
-+struct wpa_bss;
- struct wpa_scan_results;
- 
- #ifdef CONFIG_WPS
-@@ -16,8 +17,6 @@ struct wpa_scan_results;
- #include "wps/wps.h"
- #include "wps/wps_defs.h"
- 
--struct wpa_bss;
--
- struct wps_new_ap_settings {
- 	const char *ssid_hex;
- 	const char *auth;
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0028-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0028-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch
new file mode 100644
index 0000000..ed43435
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0028-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch
@@ -0,0 +1,60 @@
+From 5fc587076c71f7a410dd59b467f156a0ed804b13 Mon Sep 17 00:00:00 2001
+From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
+Date: Fri, 3 Mar 2023 12:45:42 +0800
+Subject: [PATCH 028/126] mtk: hostapd: Mark DFS channel as available for CSA.
+
+---
+ hostapd/ctrl_iface.c   | 10 ++++++++++
+ hostapd/hostapd_cli.c  |  2 +-
+ src/ap/ctrl_iface_ap.c |  1 +
+ 3 files changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 7e5733278..815633c67 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -2792,6 +2792,16 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ 		break;
+ 	}
+ 
++	if (settings.freq_params.radar_background) {
++		hostapd_dfs_sta_update_state(iface,
++			settings.freq_params.freq,
++			settings.freq_params.ht_enabled,
++			settings.freq_params.sec_channel_offset,
++			bandwidth, settings.freq_params.center_freq1,
++			settings.freq_params.center_freq2,
++			HOSTAPD_CHAN_DFS_AVAILABLE);
++	}
++
+ 	if (settings.freq_params.center_freq1)
+ 		dfs_range += hostapd_is_dfs_overlap(
+ 			iface, bandwidth, settings.freq_params.center_freq1);
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index d934bb0d4..7ab980990 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1800,7 +1800,7 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 	  "<addr> = send QoS Map Configure frame" },
+ 	{ "chan_switch", hostapd_cli_cmd_chan_switch, NULL,
+ 	  "<cs_count> <freq> [sec_channel_offset=] [center_freq1=]\n"
+-	  "  [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n"
++	  "  [center_freq2=] [bandwidth=] [blocktx] [ht|vht] [skip_cac]\n"
+ 	  "  = initiate channel switch announcement" },
+ #ifdef CONFIG_IEEE80211AX
+ 	{ "color_change", hostapd_cli_cmd_color_change, NULL,
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index 4d3c7e529..20d426560 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -1140,6 +1140,7 @@ int hostapd_parse_csa_settings(const char *pos,
+ 	settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
+ 	settings->freq_params.he_enabled = !!os_strstr(pos, " he");
+ 	settings->freq_params.eht_enabled = !!os_strstr(pos, " eht");
++	settings->freq_params.radar_background = !!os_strstr(pos, " skip_cac");
+ 	settings->block_tx = !!os_strstr(pos, " blocktx");
+ #undef SET_CSA_SETTING
+ #undef SET_CSA_SETTING_EXT
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0029-mtk-hostapd-Add-available-color-bitmap.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0029-mtk-hostapd-Add-available-color-bitmap.patch
new file mode 100644
index 0000000..a8adfdc
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0029-mtk-hostapd-Add-available-color-bitmap.patch
@@ -0,0 +1,476 @@
+From 75c1aefed4cda9d8bcb5e8e7f6f086af4a6ff58a Mon Sep 17 00:00:00 2001
+From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Date: Thu, 26 Jan 2023 09:16:00 +0800
+Subject: [PATCH 029/126] mtk: hostapd: Add available color bitmap
+
+Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+---
+ hostapd/ctrl_iface.c              |  74 +++++++++++
+ hostapd/hostapd_cli.c             |  18 +++
+ src/ap/ap_drv_ops.c               |  10 +-
+ src/ap/ap_drv_ops.h               |   2 +
+ src/common/mtk_vendor.h           |  11 ++
+ src/drivers/driver.h              |   8 ++
+ src/drivers/driver_nl80211.c      | 198 +++++++++++++++++++++++++++++-
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   3 +
+ 9 files changed, 323 insertions(+), 2 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 815633c67..b8a957468 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4387,6 +4387,76 @@ hostapd_ctrl_iface_get_amsdu(struct hostapd_data *hapd, char *buf,
+ 	return ret;
+ }
+ 
++static int
++hostapd_ctrl_iface_get_bss_color(struct hostapd_data *hapd, char *buf,
++		size_t buflen)
++{
++	int ret;
++	char *pos, *end;
++	int i;
++
++	pos = buf;
++	end = buf + buflen;
++
++	if (hapd->iface->conf->he_op.he_bss_color_disabled)
++		ret = os_snprintf(buf, buflen, "BSS Color disabled\n");
++	else
++		ret = os_snprintf(buf, buflen, "BSS Color=%u\n",
++				  hapd->iface->conf->he_op.he_bss_color);
++
++	pos += ret;
++
++	return pos - buf;
++}
++
++
++static int
++hostapd_ctrl_iface_get_aval_color_bmp(struct hostapd_data *hapd, char *buf,
++		size_t buflen)
++{
++	int ret;
++	char *pos, *end;
++	int i;
++	u64 aval_color_bmp = 0;
++
++	hostapd_drv_get_aval_bss_color_bmp(hapd, &aval_color_bmp);
++	hapd->color_collision_bitmap = ~aval_color_bmp;
++
++	pos = buf;
++	end = buf + buflen;
++
++	ret = os_snprintf(buf, buflen,
++			"available color bitmap=0x%llx\n",
++			aval_color_bmp);
++	if (os_snprintf_error(end - pos, ret))
++		return pos - buf;
++	pos += ret;
++
++	for (i = 0; i < HE_OPERATION_BSS_COLOR_MAX; i++) {
++		int bit = !!((aval_color_bmp >> i) & 1LLU);
++
++		if (i % 8 == 0) {
++			ret = os_snprintf(pos, end - pos, "%2d: ", i);
++			if (os_snprintf_error(end - pos, ret))
++				return pos - buf;
++			pos += ret;
++		}
++
++		ret = os_snprintf(pos, end - pos, "%d ", bit);
++		if (os_snprintf_error(end - pos, ret))
++			return pos - buf;
++		pos += ret;
++
++		if (i % 8 == 7) {
++			ret = os_snprintf(pos, end - pos, "\n");
++			if (os_snprintf_error(end - pos, ret))
++				return pos - buf;
++			pos += ret;
++		}
++	}
++	return pos - buf;
++}
++
+ 
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+@@ -5016,6 +5086,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
+ 	} else if (os_strncmp(buf, "GET_AMSDU", 9) == 0) {
+ 		reply_len = hostapd_ctrl_iface_get_amsdu(hapd, reply, reply_size);
++	} else if (os_strncmp(buf, "GET_BSS_COLOR", 13) == 0) {
++		reply_len = hostapd_ctrl_iface_get_bss_color(hapd, reply, reply_size);
++	} else if (os_strncmp(buf, "AVAL_COLOR_BMP", 14) == 0) {
++		reply_len = hostapd_ctrl_iface_get_aval_color_bmp(hapd, reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 7ab980990..27d61b06d 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1683,6 +1683,20 @@ static int hostapd_cli_cmd_reload_rxkhs(struct wpa_ctrl *ctrl, int argc,
+ #endif /* CONFIG_IEEE80211R_AP */
+ 
+ 
++static int hostapd_cli_cmd_get_bss_color(struct wpa_ctrl *ctrl, int argc,
++					  char *argv[])
++{
++	return wpa_ctrl_command(ctrl, "GET_BSS_COLOR");
++}
++
++
++static int hostapd_cli_cmd_get_aval_color_bmp(struct wpa_ctrl *ctrl, int argc,
++					  char *argv[])
++{
++	return wpa_ctrl_command(ctrl, "AVAL_COLOR_BMP");
++}
++
++
+ #ifdef ANDROID
+ static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ {
+@@ -1929,6 +1943,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 	{ "get_rxkhs", hostapd_cli_cmd_get_rxkhs, NULL,
+ 	  "= get R0KHs and R1KHs" },
+ #endif /* CONFIG_IEEE80211R_AP */
++	{ "get_bss_color", hostapd_cli_cmd_get_bss_color, NULL,
++	  "= get current BSS color" },
++	{ "get_color_bmp", hostapd_cli_cmd_get_aval_color_bmp, NULL,
++	  "= get available BSS color bitmap" },
+ #ifdef ANDROID
+ 	{ "driver", hostapd_cli_cmd_driver, NULL,
+ 	  "<driver sub command> [<hex formatted data>] = send driver command data" },
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 2685c6f8a..efa4e2b4d 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1350,4 +1350,12 @@ int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu)
+ 	if (!hapd->driver || !hapd->driver->amsdu_dump)
+ 		return 0;
+ 	return hapd->driver->amsdu_dump(hapd->drv_priv, amsdu);
+-}
+\ No newline at end of file
++}
++
++int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_color_bmp)
++{
++	if (!hapd->driver || !hapd->driver->get_aval_color_bmp ||
++	    hapd->iface->conf->he_op.he_bss_color_disabled)
++		return 0;
++	return hapd->driver->get_aval_color_bmp(hapd->drv_priv, aval_color_bmp);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 68b26595f..f57dff4aa 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -163,6 +163,8 @@ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
+ int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
++int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd,
++				       u64 *aval_color_bmp);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 7b4d7c11a..03daeb72a 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -15,6 +15,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
+ 	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
+ 	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
++	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
+ };
+ 
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -255,6 +256,16 @@ ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
+ 	[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
+ };
+ 
++enum mtk_vendor_attr_bss_color_ctrl {
++	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL,
++	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
++};
+ 
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index cccc44147..5d7eb5b4e 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5299,6 +5299,14 @@ struct wpa_driver_ops {
+ 	 */
+ 	int (*amsdu_ctrl)(void *priv, u8 amsdu);
+ 	int (*amsdu_dump)(void *priv, u8 *amsdu);
++
++	/**
++	 * get_aval_color_bmp - get available BSS color bitmap
++	 * @priv: Private driver interface data
++	 * @aval_color_bmp: available bss color bitmap
++	 *
++	 */
++	int (*get_aval_color_bmp)(void *priv, u64 *aval_color_bmp);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index c2a0f1adb..73bf082f2 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -13175,7 +13175,6 @@ static void nl80211_parse_btm_candidate_info(struct candidate_list *candidate,
+ 		   num, MAC2STR(candidate->bssid), buf);
+ }
+ 
+-
+ static int
+ nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg)
+ {
+@@ -14630,6 +14629,202 @@ fail:
+ 	return -ENOBUFS;
+ }
+ 
++static int nl80211_get_aval_color_bmp_handler(struct nl_msg *msg, void *arg)
++{
++	u64 *aval_color_bmp = arg;
++	struct nlattr *tb[NL80211_ATTR_MAX + 1];
++	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX + 1];
++	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++	struct nlattr *nl_vend, *attr;
++
++	static const struct nla_policy
++	bss_color_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL + 1] = {
++		[MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP] = { .type = NLA_U64 },
++	};
++
++	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++			genlmsg_attrlen(gnlh, 0), NULL);
++
++	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++	if (!nl_vend)
++		return NL_SKIP;
++
++	nla_parse(tb_vendor, MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX,
++			nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++	*aval_color_bmp = nla_get_u64(tb_vendor[MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP]);
++
++	return 0;
++}
++
++static int nl80211_get_aval_color_bmp(void *priv, u64 *aval_color_bmp)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *attr;
++	int ret;
++
++	if (!drv->mtk_bss_color_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support BSS COLOR vendor cmd");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL))
++		return -ENOBUFS;
++
++	attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!attr) {
++		nlmsg_free(msg);
++		return -1;
++	}
++
++	nla_nest_end(msg, attr);
++
++	ret = send_and_recv_resp(drv, msg, nl80211_get_aval_color_bmp_handler, aval_color_bmp);
++
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to send BSS COLOR vendor cmd. ret=%d (%s) ",
++			   ret, strerror(-ret));
++	}
++	return ret;
++}
++
++static int nl80211_ap_wireless(void *priv, u8 sub_vendor_id, int value)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_wireless_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting ap wireless control");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	if (sub_vendor_id == MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE)
++		nla_put_u16(msg, sub_vendor_id, (u16) value);
++	else
++		nla_put_u8(msg, sub_vendor_id, (u8) value);
++
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set ap_wireless. ret=%d (%s)", ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
++static int nl80211_ap_rfeatures(void *priv, u8 sub_vendor_id, int value)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_rfeatures_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting ap rfeatures control");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	nla_put_u8(msg, sub_vendor_id, (u8) value);
++
++	nla_nest_end(msg, data);
++
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set rf_features. ret=%d (%s)", ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
++static int nl80211_ap_trigtype(void *priv, u8 enable, u8 type)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data, *data2;
++	int ret;
++
++	if (!drv->mtk_rfeatures_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting ap rfeatures control");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	data2 = nla_nest_start(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG);
++	if (!data2)
++		goto fail;
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN, enable);
++	nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE, type);
++
++	nla_nest_end(msg, data2);
++	nla_nest_end(msg, data);
++
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set trig_type. ret=%d (%s)", ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+ 	.desc = "Linux nl80211/cfg80211",
+@@ -14801,4 +14996,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.ibf_dump = nl80211_ibf_dump,
+ 	.amsdu_ctrl = nl80211_enable_amsdu,
+ 	.amsdu_dump = nl80211_dump_amsdu,
++	.get_aval_color_bmp = nl80211_get_aval_color_bmp,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 5aa813e26..5b4d45567 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -205,6 +205,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_3wire_vendor_cmd_avail:1;
+ 	unsigned int mtk_ibf_vendor_cmd_avail:1;
+ 	unsigned int mtk_wireless_vendor_cmd_avail:1;
++	unsigned int mtk_bss_color_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 4e2afb7f2..919f5bdf6 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1156,6 +1156,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL:
+ 					drv->mtk_wireless_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
++					drv->mtk_bss_color_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0029-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0029-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch
deleted file mode 100644
index 2d4b438..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0029-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch
+++ /dev/null
@@ -1,441 +0,0 @@
-From 63eb73fe7fe0cb9e088b95cffe4b123885bf9ede 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 029/104] mtk: hostapd: 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
-database. The second can iterate neighbor report database to build up neighbor
-report data.
-
-2. Support including neighbor report elements in ANQP response
-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. 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 +
- src/ap/ap_config.h     |   1 +
- src/ap/ctrl_iface_ap.c |  18 ++++++-
- src/ap/gas_serv.c      |  29 ++++++++++
- src/ap/gas_serv.h      |   2 +
- src/ap/neighbor_db.c   | 118 +++++++++++++++++++++++++++++++++++++++++
- src/ap/neighbor_db.h   |   9 ++++
- src/ap/wnm_ap.c        |  43 +++++++++++++--
- 9 files changed, 221 insertions(+), 5 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index f76226cf4..4240319b7 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -1300,6 +1300,11 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
- #endif /* CONFIG_DPP */
- 	} else if (os_strcasecmp(cmd, "setband") == 0) {
- 		ret = hostapd_ctrl_iface_set_band(hapd, value);
-+	} else if (os_strcasecmp(cmd, "bss_termination_tsf") == 0) {
-+		int termination_sec = atoi(value);
-+		hapd->conf->bss_termination_tsf = termination_sec;
-+		wpa_printf(MSG_DEBUG, "BSS Termination TSF: value = %d",
-+                termination_sec);
- 	} else {
- 		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 ca67aeb41..6c8b10291 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -175,6 +175,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
- 	bss->pasn_comeback_after = 10;
- 	bss->pasn_noauth = 1;
- #endif /* CONFIG_PASN */
-+	bss->bss_termination_tsf = 0;
- }
- 
- 
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index d10b00be9..379dc22cf 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -560,6 +560,7 @@ struct hostapd_bss_config {
- 	int wnm_sleep_mode;
- 	int wnm_sleep_mode_no_keys;
- 	int bss_transition;
-+	unsigned int bss_termination_tsf;
- 
- 	/* IEEE 802.11u - Interworking */
- 	int interworking;
-diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index cd7db4fc6..a2f89260c 100644
---- a/src/ap/ctrl_iface_ap.c
-+++ b/src/ap/ctrl_iface_ap.c
-@@ -1377,6 +1377,10 @@ int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
- 			wpa_printf(MSG_DEBUG, "Invalid bss_term data");
- 			return -1;
- 		}
-+		if (hapd->conf->bss_termination_tsf) {
-+			WPA_PUT_LE64(&bss_term_dur[2], hapd->conf->bss_termination_tsf);
-+		}
-+
- 		end++;
- 		WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
- 	}
-@@ -1403,16 +1407,26 @@ int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
- 		req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
- 	}
- 
--	if (os_strstr(cmd, " pref=1"))
-+	if (os_strstr(cmd, " pref=1")) {
- 		req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
-+		if (nei_len == 0) {
-+			// Add neigibor report from neighbor report db to nei_rep buffer
-+			nei_len = hostapd_neighbor_insert_buffer (hapd, nei_rep, 1000);
-+		}
-+	}
- 	if (os_strstr(cmd, " abridged=1"))
- 		req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
--	if (os_strstr(cmd, " disassoc_imminent=1"))
-+	if (os_strstr(cmd, " disassoc_imminent=1")) {
- 		req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
-+		/* Set own BSS neighbor report preference value as 0 */
-+		hostapd_neighbor_set_own_report_pref(hapd, nei_rep, nei_len, 0);
-+	}
- 	if (os_strstr(cmd, " link_removal_imminent=1"))
- 		req_mode |= WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT;
- 
- #ifdef CONFIG_MBO
-+	hostapd_neighbor_set_pref_by_non_pref_chan(hapd, sta, nei_rep, nei_len);
-+
- 	pos = os_strstr(cmd, "mbo=");
- 	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
---- a/src/ap/gas_serv.c
-+++ b/src/ap/gas_serv.c
-@@ -19,6 +19,7 @@
- #include "dpp_hostapd.h"
- #include "sta_info.h"
- #include "gas_serv.h"
-+#include "neighbor_db.h"
- 
- 
- #ifdef CONFIG_DPP
-@@ -369,6 +370,24 @@ static void anqp_add_network_auth_type(struct hostapd_data *hapd,
- 	}
- }
- 
-+static void anqp_add_neighbor_report(struct hostapd_data *hapd,
-+				       struct wpabuf *buf)
-+{
-+	struct hostapd_neighbor_entry *nr;
-+	u8 *len_pos = gas_anqp_add_element(buf, ANQP_NEIGHBOR_REPORT);
-+	if (dl_list_empty(&hapd->nr_db)) {
-+		wpabuf_put_le16(buf, 0);
-+	}
-+	else {
-+		dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list ) {
-+			wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
-+			wpabuf_put_u8(buf, wpabuf_len(nr->nr));
-+			wpabuf_put_buf(buf, nr->nr);
-+		}
-+	}
-+	gas_anqp_set_element_len(buf, len_pos);
-+}
-+
- 
- static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
- 					struct wpabuf *buf)
-@@ -986,6 +1005,9 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
- 		len += 1000;
- 	if (request & ANQP_REQ_ICON_REQUEST)
- 		len += 65536;
-+    if (request & ANQP_REQ_NEIGHBOR_REPORT) {
-+        len += (40 * hostapd_neighbor_count(hapd));
-+    }
- #ifdef CONFIG_FILS
- 	if (request & ANQP_FILS_REALM_INFO)
- 		len += 2 * dl_list_len(&hapd->conf->fils_realms);
-@@ -1028,6 +1050,8 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
- 		anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
- 	if (request & ANQP_REQ_EMERGENCY_NAI)
- 		anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
-+	if (request & ANQP_REQ_NEIGHBOR_REPORT)
-+		anqp_add_neighbor_report(hapd, buf);
- 
- 	for (i = 0; i < num_extra_req; i++) {
- #ifdef CONFIG_FILS
-@@ -1172,6 +1196,11 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
- 			     "Emergency NAI",
- 			     get_anqp_elem(hapd, info_id) != NULL, qi);
- 		break;
-+	case ANQP_NEIGHBOR_REPORT:
-+		set_anqp_req(ANQP_REQ_NEIGHBOR_REPORT,
-+			     "Neighbor Report",
-+			     get_anqp_elem(hapd, info_id) != NULL, qi);
-+		break;
- 	default:
- #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
---- a/src/ap/gas_serv.h
-+++ b/src/ap/gas_serv.h
-@@ -40,6 +40,8 @@
- 	(1 << (ANQP_TDLS_CAPABILITY - ANQP_QUERY_LIST))
- #define ANQP_REQ_EMERGENCY_NAI \
- 	(1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST))
-+#define ANQP_REQ_NEIGHBOR_REPORT \
-+	(1 << (ANQP_NEIGHBOR_REPORT - ANQP_QUERY_LIST))
- /*
-  * 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 f7a7d83d4..d9216a5ae 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)
- }
- 
- 
-+int hostapd_neighbor_count(struct hostapd_data *hapd)
-+{
-+	struct hostapd_neighbor_entry *nr;
-+	int count = 0;
-+
-+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
-+			 list) {
-+		count++;
-+	}
-+	return count;
-+}
-+
-+
-+int hostapd_neighbor_insert_buffer(struct hostapd_data *hapd, char *buf,
-+        size_t buflen)
-+{
-+	struct hostapd_neighbor_entry *nr;
-+	char *pos = buf;
-+
-+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
-+			 list) {
-+		/* For neighbor report IE, we only need bssid and nr*/
-+		*pos++ = WLAN_EID_NEIGHBOR_REPORT;
-+		*pos++ = wpabuf_len(nr->nr);
-+		os_memcpy(pos, wpabuf_head(nr->nr), wpabuf_len(nr->nr));
-+		pos += wpabuf_len(nr->nr);
-+	}
-+
-+	return pos - buf;
-+}
-+
-+
- static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
- {
- 	wpabuf_free(nr->nr);
-@@ -364,3 +396,89 @@ int hostapd_neighbor_sync_own_report(struct hostapd_data *hapd)
- 
- 	return 0;
- }
-+
-+void hostapd_neighbor_set_own_report_pref(struct hostapd_data *hapd, char *nei_buf,
-+			 size_t buflen, const int pref)
-+{
-+	struct hostapd_neighbor_entry *nr;
-+	char *pos, *next_nr;
-+
-+	pos = nei_buf;
-+	next_nr = nei_buf;
-+
-+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
-+			 list) {
-+		pos = next_nr;
-+		next_nr = pos + 2 + wpabuf_len(nr->nr);
-+		/* Shift 2 bytes for Element ID and Neighbor report length */
-+		pos = pos + 2;
-+		if(os_memcmp(pos, hapd->own_addr, ETH_ALEN) == 0) {
-+			/* Shift for BSSID + BSSID info + Op_class + channel num + PHY type */
-+			pos = pos + 6 + 4 + 1 + 1 + 1;
-+
-+			/* Iterate Subelement */
-+			while (next_nr - pos > 0) {
-+				if (*pos == 3) {
-+					pos = pos + 2;
-+					*pos = pref;
-+					return;
-+				} else {
-+					pos++;
-+					int shift_len = *pos++;
-+					pos = pos + shift_len;
-+				}
-+			}
-+		}
-+	}
-+}
-+
-+#ifdef CONFIG_MBO
-+void hostapd_neighbor_set_pref_by_non_pref_chan(struct hostapd_data *hapd,
-+			 struct sta_info* sta, char *nei_buf, size_t buflen)
-+{
-+	struct hostapd_neighbor_entry *nr;
-+	struct mbo_non_pref_chan_info *info;
-+	u8 i;
-+
-+	for(info = sta->non_pref_chan; info; info = info->next) {
-+		/* Check OP_Class and Channel num */
-+		for(i = 0; i < info->num_channels; i++) {
-+			char *pos, *next_nr;
-+
-+			pos = nei_buf;
-+			next_nr = nei_buf;
-+
-+			/* Iterate Neighbor report database */
-+			dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
-+					 list) {
-+				pos = next_nr;
-+				next_nr = pos + 2 + wpabuf_len(nr->nr);
-+				/**
-+				 * Shift 12 bytes for Element ID, Neighbor report length,
-+				 * BSSID and BSSID info.
-+				 */
-+				pos = pos + 12;
-+				int nr_op_class = *pos++;
-+				int nr_channel = *pos;
-+				if(info->op_class == nr_op_class && info->channels[i] == nr_channel) {
-+					/* Shift for Channel Num + PHY type */
-+					pos = pos + 1 + 1;
-+
-+					// Iterate Subelement
-+					while(next_nr - pos > 0) {
-+						if(*pos == 3) {
-+							pos = pos + 2;
-+							*pos = info->pref;
-+							break;
-+						}else {
-+							pos++;
-+							int shift_len = *pos++;
-+							pos = pos + shift_len;
-+						}
-+					}
-+				}
-+			}
-+		}
-+	}
-+}
-+#endif
-diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
-index 53f714203..cf1400256 100644
---- a/src/ap/neighbor_db.h
-+++ b/src/ap/neighbor_db.h
-@@ -25,4 +25,13 @@ int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
- 			    const struct wpa_ssid_value *ssid);
- void hostapd_free_neighbor_db(struct hostapd_data *hapd);
- 
-+int hostapd_neighbor_count(struct hostapd_data *hapd);
-+int hostapd_neighbor_insert_buffer(struct hostapd_data *hapd, char *buf,
-+        size_t buflen);
-+void hostapd_neighbor_set_own_report_pref(struct hostapd_data *hapd, char *nei_buf,
-+			 size_t buflen, const int pref);
-+#ifdef CONFIG_MBO
-+void hostapd_neighbor_set_pref_by_non_pref_chan(struct hostapd_data *hapd,
-+			 struct sta_info* sta, char *nei_buf, size_t buflen);
-+#endif
- #endif /* NEIGHBOR_DB_H */
-diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
-index d259200c9..4ac96b1be 100644
---- a/src/ap/wnm_ap.c
-+++ b/src/ap/wnm_ap.c
-@@ -20,6 +20,7 @@
- #include "ap/wpa_auth.h"
- #include "mbo_ap.h"
- #include "wnm_ap.h"
-+#include "ap/neighbor_db.h"
- 
- #define MAX_TFS_IE_LEN  1024
- 
-@@ -390,13 +391,24 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
- 	u8 *pos;
- 	int res;
- 
--	mgmt = os_zalloc(sizeof(*mgmt));
--	if (mgmt == NULL)
-+	int nr_num = hostapd_neighbor_count(hapd);
-+	int nr_size = ETH_ALEN + 4 + 1 + 1 + 1 + 5;
-+	int total_nr_size = nr_num * nr_size;
-+	u8 *nr_data = os_malloc(total_nr_size);
-+	int nr_data_len = 0;
-+	if(nr_data == NULL) {
-+		wpa_printf (MSG_ERROR, "Failed to allocate memory");
-+	} else {
-+	    nr_data_len = hostapd_neighbor_insert_buffer(hapd, nr_data, total_nr_size);
-+	}
-+	mgmt = os_zalloc(sizeof(*mgmt) + nr_data_len);
-+	if (mgmt == NULL) {
-+		wpa_printf (MSG_ERROR, "Failed to allocate memory for mgmt frame");
- 		return -1;
-+	}
- 
- 	sta = ap_get_sta(hapd, addr);
- 	own_addr = wnm_ap_get_own_addr(hapd, sta);
--
- 	os_memcpy(mgmt->da, addr, ETH_ALEN);
- 	os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
- 	os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
-@@ -406,10 +418,18 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
- 	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
- 	mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
- 	mgmt->u.action.u.bss_tm_req.req_mode = 0;
-+	if(nr_num) {
-+		mgmt->u.action.u.bss_tm_req.req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
-+	}
- 	mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
- 	mgmt->u.action.u.bss_tm_req.validity_interval = 1;
- 	pos = mgmt->u.action.u.bss_tm_req.variable;
- 
-+	if(nr_num) {
-+		os_memcpy(pos, nr_data, nr_data_len);
-+		pos += nr_data_len;
-+	}
-+
- 	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 "
-@@ -915,6 +935,22 @@ static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
- }
- 
- 
-+void bss_termination_disable_iface(void *eloop_ctx, void *timeout_ctx)
-+{
-+	struct hostapd_data *hapd = eloop_ctx;
-+	hostapd_disable_iface(hapd->iface);
-+}
-+
-+
-+static void set_disable_iface_timer(struct hostapd_data *hapd, struct sta_info *sta,
-+			       int disable_iface_timer)
-+{
-+	wpa_printf(MSG_DEBUG, "Disable interface timer set to %d secs", disable_iface_timer);
-+	eloop_register_timeout(disable_iface_timer, 0,
-+			       bss_termination_disable_iface, hapd, NULL);
-+}
-+
-+
- int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
- 				   struct sta_info *sta, const char *url,
- 				   int disassoc_timer)
-@@ -1006,6 +1042,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;
-+		set_disable_iface_timer(hapd, sta, hapd->conf->bss_termination_tsf);
- 	}
- 
- 	if (url) {
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0030-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0030-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch
new file mode 100644
index 0000000..f78e796
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0030-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch
@@ -0,0 +1,210 @@
+From e0914bbc141ee65c28697d891b718d3c6f6dbb78 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Mar 2023 16:08:30 +0800
+Subject: [PATCH 030/126] mtk: hostapd: Fix ZWDFS issue in BW 160
+
+When background radar is enabled and bandwidth is set to 160, AP will
+fail to startup due to the lack of non-DFS channel.
+Under this circumstance, AP should perform CAC itself, and the background
+chain could also perform CAC simultaneously.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 98 ++++++++++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 79 insertions(+), 19 deletions(-)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 4f8ef8111..9677b26ea 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -70,15 +70,22 @@ static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
+ static int dfs_channel_available(struct hostapd_channel_data *chan,
+ 				 enum dfs_channel_type type)
+ {
++	int dfs_status = chan->flag & HOSTAPD_CHAN_DFS_MASK;
++
++	if (chan->flag & HOSTAPD_CHAN_DISABLED)
++		return -1;
++
+ 	if (type == DFS_NO_CAC_YET) {
+ 		/* Select only radar channel where CAC has not been
+ 		 * performed yet
+ 		 */
+-		if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+-		    (chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+-		     HOSTAPD_CHAN_DFS_USABLE)
++		if (!(chan->flag & HOSTAPD_CHAN_RADAR))
++			return 0;
++
++		if (dfs_status == HOSTAPD_CHAN_DFS_USABLE)
+ 			return 1;
+-		return 0;
++
++		return -1;
+ 	}
+ 
+ 	/*
+@@ -87,16 +94,14 @@ static int dfs_channel_available(struct hostapd_channel_data *chan,
+ 	 * channel for CSA, unless they are available for immediate use.
+ 	 */
+ 	if (type == DFS_AVAILABLE && (chan->flag & HOSTAPD_CHAN_RADAR) &&
+-	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
+-	     HOSTAPD_CHAN_DFS_AVAILABLE))
+-		return 0;
++	    (dfs_status != HOSTAPD_CHAN_DFS_AVAILABLE))
++		return -1;
+ 
+-	if (chan->flag & HOSTAPD_CHAN_DISABLED)
+-		return 0;
+ 	if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+-	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+-	     HOSTAPD_CHAN_DFS_UNAVAILABLE))
+-		return 0;
++	    ((dfs_status == HOSTAPD_CHAN_DFS_UNAVAILABLE) ||
++	    (dfs_status == HOSTAPD_CHAN_DFS_UNKNOWN)))
++		return -1;
++
+ 	return 1;
+ }
+ 
+@@ -168,7 +173,7 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
+ 				    enum dfs_channel_type type)
+ {
+ 	struct hostapd_channel_data *first_chan, *chan;
+-	int i;
++	int i, available = 0, ret = 0;
+ 	u32 bw = num_chan_to_bw(num_chans);
+ 
+ 	if (first_chan_idx + num_chans > mode->num_channels) {
+@@ -204,14 +209,17 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
+ 			return 0;
+ 		}
+ 
+-		if (!dfs_channel_available(chan, type)) {
++		ret = dfs_channel_available(chan, type);
++		if (ret < 0) {
+ 			wpa_printf(MSG_DEBUG, "DFS: channel not available %d",
+ 				   first_chan->freq + i * 20);
+ 			return 0;
+ 		}
++
++		available |= ret;
+ 	}
+ 
+-	return 1;
++	return available;
+ }
+ 
+ 
+@@ -839,8 +847,12 @@ static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
+  */
+ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ {
++	struct hostapd_channel_data *channel;
+ 	int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
+-	int skip_radar = 0;
++	int sec = 0, skip_radar = 0;
++	u8 cf1 = 0, cf2 = 0;
++	bool use_radar_background = dfs_use_radar_background(iface);
++	enum dfs_channel_type channel_type = DFS_NO_CAC_YET;
+ 
+ 	if (is_6ghz_freq(iface->freq))
+ 		return 1;
+@@ -903,7 +915,7 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ 	/* Finally start CAC */
+ 	hostapd_set_state(iface, HAPD_IFACE_DFS);
+ 	wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz%s", iface->freq,
+-		   dfs_use_radar_background(iface) ? " (background)" : "");
++		   use_radar_background ? " (background)" : "");
+ 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
+ 		"freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
+ 		iface->freq,
+@@ -913,6 +925,16 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ 		hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
+ 		iface->dfs_cac_ms / 1000);
+ 
++	if (use_radar_background) {
++		channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, DFS_AVAILABLE);
++		/*
++		 * AP cannot get any random available channel.
++		 * Let AP and dedicated radar chain both perform CAC.
++		 */
++		if (!channel)
++			use_radar_background = false;
++	}
++
+ 	res = hostapd_start_dfs_cac(
+ 		iface, iface->conf->hw_mode, iface->freq, iface->conf->channel,
+ 		iface->conf->ieee80211n, iface->conf->ieee80211ac,
+@@ -921,14 +943,14 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ 		hostapd_get_oper_chwidth(iface->conf),
+ 		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+ 		hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
+-		dfs_use_radar_background(iface));
++		use_radar_background);
+ 
+ 	if (res) {
+ 		wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
+ 		return -1;
+ 	}
+ 
+-	if (dfs_use_radar_background(iface)) {
++	if (use_radar_background) {
+ 		/* Cache background radar parameters. */
+ 		iface->radar_background.channel = iface->conf->channel;
+ 		iface->radar_background.secondary_channel =
+@@ -949,6 +971,35 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ 
+ 		iface->radar_background.temp_ch = 1;
+ 		return 1;
++	} else if (dfs_use_radar_background(iface)) {
++		if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
++			channel_type = DFS_ANY_CHANNEL;
++
++		channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, channel_type);
++
++		if (!channel ||
++		    (channel->chan == iface->conf->channel &&
++		    cf1 == hostapd_get_oper_centr_freq_seg0_idx(iface->conf) &&
++		    cf2 == hostapd_get_oper_centr_freq_seg1_idx(iface->conf))) {
++			wpa_printf(MSG_ERROR, "Background radar could not get valid channel\n");
++			iface->radar_background.channel = -1;
++			return 0;
++		}
++
++		hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
++				      channel->freq, channel->chan,
++				      iface->conf->ieee80211n,
++				      iface->conf->ieee80211ac,
++				      iface->conf->ieee80211ax,
++				      iface->conf->ieee80211be,
++				      sec, hostapd_get_oper_chwidth(iface->conf),
++				      cf1, cf2, true);
++
++		iface->radar_background.channel = channel->chan;
++		iface->radar_background.freq = channel->freq;
++		iface->radar_background.secondary_channel = sec;
++		iface->radar_background.centr_freq_seg0_idx = cf1;
++		iface->radar_background.centr_freq_seg1_idx = cf2;
+ 	}
+ 
+ 	return 0;
+@@ -1214,6 +1265,15 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ 				hostapd_setup_interface_complete(iface, 0);
+ 				iface->cac_started = 0;
+ 			}
++
++			/*
++			 * When background radar is enabled but the CAC completion
++			 * is not received from the background chain.
++			 * Then, reset radar background chain.
++			 */
++			if (dfs_use_radar_background(iface) &&
++			    iface->radar_background.channel == -1)
++				hostapd_dfs_update_background_chain(iface);
+ 		}
+ 	} else if (hostapd_dfs_is_background_event(iface, freq)) {
+ 		iface->radar_background.cac_started = 0;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0030-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0030-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch
deleted file mode 100644
index 56988b8..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0030-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From d87f115b26430a4edd465d21be00ec61599c332e Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Tue, 20 Sep 2022 19:33:45 +0800
-Subject: [PATCH 030/104] mtk: hostapd: print sae groups by hostapd ctrl
-
----
- hostapd/ctrl_iface.c | 13 +++++++++++++
- 1 file changed, 13 insertions(+)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 4240319b7..1f950bc46 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -1376,6 +1376,19 @@ static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd,
- 		if (os_snprintf_error(buflen, res))
- 			return -1;
- 		return res;
-+	} else if (os_strcmp(cmd, "sae_group_capability") == 0) {
-+#ifdef CONFIG_SAE
-+		/* see sae_set_group() */
-+		res = os_snprintf(buf, buflen, "%s%s%s%s19 20 21",
-+				  dh_groups_get(15) ? "15 ": "",
-+				  dh_groups_get(16) ? "16 ": "",
-+				  dh_groups_get(17) ? "17 ": "",
-+				  dh_groups_get(18) ? "18 ": "");
-+
-+		if (os_snprintf_error(buflen, res))
-+			return -1;
-+		return res;
-+#endif /* CONFIG_SAE */
- 	}
- 
- 	return -1;
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0031-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0031-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch
new file mode 100644
index 0000000..9587c45
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0031-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch
@@ -0,0 +1,449 @@
+From 502598dedaa7d8fa86a06cf2d03d17d06798c224 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Tue, 24 Jan 2023 19:06:44 +0800
+Subject: [PATCH 031/126] mtk: hostapd: Add vendor for CAPI certification
+ commands
+
+Support new hostapd_cli command as below:
+$ hostapd_cli -i <intf> raw ap_rfeatures trig_variant=<type>
+
+This will prepare nl80211 msg
+MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_VARIANT_TYPE and then send to driver
+via nl80211.
+
+Support new hostapd_cli command as below:
+$ hostapd_cli -i <intf> -l <link_id> raw ap_rfeatures coding_type=<type>
+
+This will prepare nl80211 msg
+MTK_VENDOR_ATTR_RFEATURE_CTRL_CODING_TYPE and then send it to driver
+via nl80211.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ hostapd/ctrl_iface.c              | 103 ++++++++++++++++++++++++++++++
+ src/ap/ap_drv_ops.c               |  28 ++++++++
+ src/ap/ap_drv_ops.h               |   3 +
+ src/common/mtk_vendor.h           |  37 ++---------
+ src/drivers/driver.h              |  23 +++++++
+ src/drivers/driver_nl80211.c      |  63 +++++++++++++++++-
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   3 +
+ 8 files changed, 229 insertions(+), 32 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index b8a957468..6e7c03f81 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -72,6 +72,7 @@
+ #include "ctrl_iface.h"
+ #include "crypto/dh_groups.h"
+ 
++#include "common/mtk_vendor.h"
+ 
+ #define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
+ 
+@@ -4457,6 +4458,104 @@ hostapd_ctrl_iface_get_aval_color_bmp(struct hostapd_data *hapd, char *buf,
+ 	return pos - buf;
+ }
+ 
++static int
++hostapd_ctrl_iface_ap_wireless(struct hostapd_data *hapd, char *cmd,
++					 char *buf, size_t buflen)
++{
++	char *pos, *value, *config = cmd;
++	enum mtk_vendor_attr_wireless_ctrl sub_cmd;
++
++	pos = os_strchr(config, '=');
++	if (pos == NULL)
++		return -1;
++	*pos++ = '\0';
++
++	if(pos == NULL)
++		return -1;
++	value = pos;
++
++	if (os_strncmp(config, "fixed_mcs", 9) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS;
++	else if (os_strncmp(config, "ofdma", 5) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA;
++	else if (os_strncmp(config, "ppdu_type", 9) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE;
++	else if (os_strncmp(config, "nusers_ofdma", 12) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA;
++	else if (os_strncmp(config, "add_ba_req_bufsize", 18) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE;
++	else if (os_strncmp(config, "mimo", 4) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO;
++	else if (os_strncmp(config, "cert", 4) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT ;
++	else if (os_strncmp(config, "amsdu", 5) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU;
++	else if (os_strncmp(config, "rts_sigta", 9) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA;
++	else {
++		wpa_printf(MSG_ERROR,
++			"Unsupported parameter %s for ap_wireless", config);
++		return -1;
++	}
++
++	if (hostapd_drv_ap_wireless(hapd, (u8) sub_cmd, atoi(value)) != 0)
++		return -1;
++
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
++static int
++hostapd_ctrl_iface_ap_rfeatures(struct hostapd_data *hapd, char *cmd,
++					 char *buf, size_t buflen)
++{
++	char *pos, *value, *type, *config = cmd;
++	enum mtk_vendor_attr_rfeature_ctrl sub_cmd;
++
++	pos = os_strchr(config, '=');
++	if (pos == NULL)
++		return -1;
++	*pos++ = '\0';
++
++	if(pos == NULL)
++		return -1;
++	value = pos;
++
++	if (os_strncmp(config, "he_gi", 5) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI;
++	else if (os_strncmp(config, "he_ltf", 6) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF;
++	else if (os_strncmp(config, "trig_type", 9) == 0) {
++		pos = os_strchr(value, ',');
++		if (pos == NULL)
++			return -1;
++		*pos++ = '\0';
++		if(pos == NULL)
++			return -1;
++		type = pos;
++		goto trigtype;
++	} else if (os_strcmp(config, "ack_policy") == 0)
++		sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY;
++	else if (os_strcmp(config, "trig_variant") == 0)
++		sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_VARIANT_TYPE;
++	else if (os_strcmp(config, "coding_type") == 0)
++		sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_CODING_TYPE;
++	else {
++		wpa_printf(MSG_ERROR,
++			"Unsupported parameter %s for ap_rfeatures", config);
++		return -1;
++	}
++
++	if (hostapd_drv_ap_rfeatures(hapd, (u8) sub_cmd, atoi(value)) != 0)
++		return -1;
++	goto exit;
++
++trigtype:
++	if (hostapd_drv_ap_trig_type(hapd, atoi(value), atoi(type)) != 0)
++		return -1;
++
++exit:
++	return os_snprintf(buf, buflen, "OK\n");
++}
+ 
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+@@ -5090,6 +5189,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		reply_len = hostapd_ctrl_iface_get_bss_color(hapd, reply, reply_size);
+ 	} else if (os_strncmp(buf, "AVAL_COLOR_BMP", 14) == 0) {
+ 		reply_len = hostapd_ctrl_iface_get_aval_color_bmp(hapd, reply, reply_size);
++	} else if (os_strncmp(buf, "ap_wireless ", 12) == 0) {
++		reply_len = hostapd_ctrl_iface_ap_wireless(hapd, buf + 12, reply, reply_size);
++	} else if (os_strncmp(buf, "ap_rfeatures ", 13) == 0) {
++		reply_len = hostapd_ctrl_iface_ap_rfeatures(hapd, buf + 13, reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index efa4e2b4d..999cef7ad 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1359,3 +1359,31 @@ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_colo
+ 		return 0;
+ 	return hapd->driver->get_aval_color_bmp(hapd->drv_priv, aval_color_bmp);
+ }
++
++int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
++{
++	if (!hapd->driver || !hapd->driver->ap_wireless)
++		return 0;
++	return hapd->driver->ap_wireless(hapd->drv_priv, sub_vendor_id, value);
++}
++
++int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
++{
++	s8 link_id = -1;
++
++	if (!hapd->driver || !hapd->driver->ap_rfeatures)
++		return 0;
++
++	if (hapd->conf->mld_ap)
++		link_id = hapd->mld_link_id;
++
++	return hapd->driver->ap_rfeatures(hapd->drv_priv, sub_vendor_id, value,
++					  link_id);
++}
++
++int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type)
++{
++	if (!hapd->driver || !hapd->driver->ap_trigtype)
++		return 0;
++	return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index f57dff4aa..95b8dabbe 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -165,6 +165,9 @@ int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
+ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd,
+ 				       u64 *aval_color_bmp);
++int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
++int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
++int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 03daeb72a..aa9df4fc4 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -50,17 +50,6 @@ enum mtk_vendor_attr_edcca_dump {
+ 		NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
+ };
+ 
+-
+-static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
+-	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
+-	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
+-	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
+-	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
+-	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
+-	[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
+-	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_3wire_ctrl {
+ 	MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
+ 
+@@ -72,10 +61,6 @@ enum mtk_vendor_attr_3wire_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
+ };
+ 
+-static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
+-	[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_csi_ctrl {
+ 	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
+ 
+@@ -172,7 +157,8 @@ enum mtk_vendor_attr_wireless_ctrl {
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
+-	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT = 9,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
+@@ -191,11 +177,6 @@ enum mtk_vendor_attr_wireless_dump {
+ 		NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
+ };
+ 
+-static const struct nla_policy
+-wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
+-	[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_rfeature_ctrl {
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
+ 
+@@ -205,6 +186,10 @@ enum mtk_vendor_attr_rfeature_ctrl {
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_VARIANT_TYPE,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_CODING_TYPE,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_LINK_ID,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
+@@ -246,16 +231,6 @@ enum mtk_vendor_attr_ibf_dump {
+ 		NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
+ };
+ 
+-static struct nla_policy
+-ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
+-	[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
+-};
+-
+-static struct nla_policy
+-ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
+-	[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_bss_color_ctrl {
+ 	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
+ 
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 5d7eb5b4e..a0ce0aedd 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5307,6 +5307,29 @@ struct wpa_driver_ops {
+ 	 *
+ 	 */
+ 	int (*get_aval_color_bmp)(void *priv, u64 *aval_color_bmp);
++
++	/**
++	* ap_wireless - set wireless command
++	* @priv: Private driver interface data
++	* @value: value
++	*/
++	int (*ap_wireless)(void *priv, u8 mode, int value);
++
++	/**
++	* ap_rfeatures - set ap rf features command
++	* @priv: Private driver interface data
++	* @value: value
++	* @link_id: MLD link id. -1 if this is an non-MLD AP.
++	*/
++	int (*ap_rfeatures)(void *priv, u8 mode, int value, s8 link_id);
++
++	/**
++	* ap_trigtype - set trigger type
++	* @priv: Private driver interface data
++	* @enable: enable or disable
++	* @type: trigger type
++	*/
++	int (*ap_trigtype)(void *priv, u8 enable, u8 type);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 73bf082f2..7b743a1b8 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -88,6 +88,61 @@ static void handle_nl_debug_hook(struct nl_msg *msg, int tx)
+ 	wpa_netlink_hook(tx, nlh, nlh->nlmsg_len);
+ }
+ 
++static struct nla_policy
++ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
++	[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
++};
++
++static struct nla_policy
++ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
++	[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
++};
++
++static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
++	[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
++};
++
++static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
++};
++
++static const struct nla_policy
++wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
++	[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
++};
++
++static const struct nla_policy
++rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG] = { .type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_VARIANT_TYPE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_CODING_TYPE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_LINK_ID] = { .type = NLA_U8 },
++};
++
++static const struct nla_policy
++wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE] = {.type = NLA_U16 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
++};
++
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+ 	struct nl_sock *handle;
+@@ -14737,7 +14792,7 @@ fail:
+ 	return -ENOBUFS;
+ }
+ 
+-static int nl80211_ap_rfeatures(void *priv, u8 sub_vendor_id, int value)
++static int nl80211_ap_rfeatures(void *priv, u8 sub_vendor_id, int value, s8 link_id)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+@@ -14765,6 +14820,9 @@ static int nl80211_ap_rfeatures(void *priv, u8 sub_vendor_id, int value)
+ 
+ 	nla_put_u8(msg, sub_vendor_id, (u8) value);
+ 
++	if (link_id > -1)
++		nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_LINK_ID, link_id);
++
+ 	nla_nest_end(msg, data);
+ 
+ 	ret = send_and_recv_cmd(drv, msg);
+@@ -14997,4 +15055,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.amsdu_ctrl = nl80211_enable_amsdu,
+ 	.amsdu_dump = nl80211_dump_amsdu,
+ 	.get_aval_color_bmp = nl80211_get_aval_color_bmp,
++	.ap_wireless = nl80211_ap_wireless,
++	.ap_rfeatures = nl80211_ap_rfeatures,
++	.ap_trigtype = nl80211_ap_trigtype,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 5b4d45567..046991a3d 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -206,6 +206,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_ibf_vendor_cmd_avail:1;
+ 	unsigned int mtk_wireless_vendor_cmd_avail:1;
+ 	unsigned int mtk_bss_color_vendor_cmd_avail:1;
++	unsigned int mtk_rfeatures_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 919f5bdf6..ad7f5003a 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1159,6 +1159,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
+ 					drv->mtk_bss_color_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
++					drv->mtk_rfeatures_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0031-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0031-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch
deleted file mode 100644
index b149bda..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0031-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch
+++ /dev/null
@@ -1,198 +0,0 @@
-From 5453bd56aa134865ecaab20bde482b6c389831cb Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Tue, 31 May 2022 21:15:54 +0800
-Subject: [PATCH 031/104] mtk: hostapd: add support for runtime set in-band
- discovery
-
-Usage:
-hostapd_cli unsolic_probe_resp [tx_type] [interval]
-
-0: disable all in-band discovery
-1: enable unsolicited probe response
-2: enable FILS discovery
-
-Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
-
-The mac80211 layer already has a new variable "update",
-so the redundant variable "disable" has been removed.
-
-Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
----
- hostapd/ctrl_iface.c         | 66 ++++++++++++++++++++++++++++++++++++
- hostapd/hostapd_cli.c        | 20 +++++++++++
- src/ap/beacon.c              |  5 ++-
- src/drivers/driver_nl80211.c |  6 ++--
- 4 files changed, 94 insertions(+), 3 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 1f950bc46..fb9f09bb1 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -772,6 +772,69 @@ static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd,
- 
- #endif /* CONFIG_INTERWORKING */
- 
-+static int hostapd_ctrl_iface_inband_discovery(struct hostapd_data *hapd,
-+					       const char *cmd)
-+{
-+	struct hostapd_bss_config *conf = hapd->conf;
-+	const char *pos = cmd;
-+	int tx_type, interval, ret;
-+
-+	tx_type = atoi(pos);
-+	if (tx_type < 0 || tx_type > 2) {
-+		wpa_printf(MSG_ERROR, "Invalid tx type\n");
-+		return -1;
-+	}
-+
-+	pos = os_strchr(pos, ' ');
-+	if(!pos)
-+		return -1;
-+	pos++;
-+	interval = atoi(pos);
-+	if (interval < 0 || interval > 20) {
-+		wpa_printf(MSG_ERROR, "Invalid interval value\n");
-+		return -1;
-+	}
-+
-+	wpa_printf(MSG_ERROR, "Set inband discovery type:%d, interval:%d\n",
-+			      tx_type, interval);
-+
-+#define DISABLE_INBAND_DISC 0
-+#define UNSOL_PROBE_RESP 1
-+#define FILS_DISCOVERY 2
-+
-+#ifdef CONFIG_FILS
-+	conf->fils_discovery_max_int = 0;
-+	conf->fils_discovery_min_int = 0;
-+#endif /* CONFIG_FILS */
-+	conf->unsol_bcast_probe_resp_interval = 0;
-+
-+	switch (tx_type) {
-+	case DISABLE_INBAND_DISC:
-+	default:
-+		/* Disable both Unsolicited probe response and FILS discovery*/
-+		break;
-+	case UNSOL_PROBE_RESP:
-+		/* Enable Unsolicited probe response */
-+		conf->unsol_bcast_probe_resp_interval = interval;
-+		break;
-+#ifdef CONFIG_FILS
-+	case FILS_DISCOVERY:
-+		/* Enable FILS discovery */
-+		conf->fils_discovery_min_int = interval;
-+		conf->fils_discovery_max_int = interval;
-+		break;
-+#endif /* CONFIG_FILS */
-+	}
-+
-+	ret = ieee802_11_update_beacons(hapd->iface);
-+	if(ret) {
-+		wpa_printf(MSG_DEBUG,
-+			"Failed to update with inband discovery parameters\n");
-+		return -1;
-+	}
-+
-+	return 0;
-+}
- 
- #ifdef CONFIG_WNM_AP
- 
-@@ -4045,6 +4108,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 		if (hostapd_ctrl_iface_coloc_intf_req(hapd, buf + 15))
- 			reply_len = -1;
- #endif /* CONFIG_WNM_AP */
-+	} else if (os_strncmp(buf, "INBAND_DISCOVERY ", 17) == 0) {
-+		if (hostapd_ctrl_iface_inband_discovery(hapd, buf + 17))
-+			reply_len = -1;
- 	} else if (os_strcmp(buf, "GET_CONFIG") == 0) {
- 		reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
- 							  reply_size);
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index a469b1f4d..1fb6d999e 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -655,6 +655,24 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
- 	return wpa_ctrl_command(ctrl, buf);
- }
- 
-+static int hostapd_cli_cmd_inband_discovery(struct wpa_ctrl *ctrl, int argc,
-+					    char *argv[])
-+{
-+	char buf[300];
-+	int res;
-+
-+	if (argc < 2) {
-+		printf("Invalid 'inband_discovery' command - two arguments"
-+		       "tx_type interval\n");
-+		return -1;
-+	}
-+
-+	res = os_snprintf(buf, sizeof(buf), "INBAND_DISCOVERY %s %s",
-+			  argv[0], argv[1]);
-+	if (os_snprintf_error(sizeof(buf), res))
-+		return -1;
-+	return wpa_ctrl_command(ctrl, buf);
-+}
- 
- static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
- 					     char *argv[])
-@@ -1851,6 +1869,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 	{ "driver", hostapd_cli_cmd_driver, NULL,
- 	  "<driver sub command> [<hex formatted data>] = send driver command data" },
- #endif /* ANDROID */
-+	{ "inband_discovery", hostapd_cli_cmd_inband_discovery, NULL,
-+          "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
- 	{ NULL, NULL, NULL, NULL }
- };
- 
-diff --git a/src/ap/beacon.c b/src/ap/beacon.c
-index 26453cb2c..a5c46b067 100644
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -2055,6 +2055,8 @@ static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
- 				   struct wpa_driver_ap_params *params)
- {
- 	params->fd_max_int = hapd->conf->fils_discovery_max_int;
-+	params->unsol_bcast_probe_resp_interval =
-+		hapd->conf->unsol_bcast_probe_resp_interval;
- 	if (is_6ghz_op_class(hapd->iconf->op_class) &&
- 	    params->fd_max_int > FD_MAX_INTERVAL_6GHZ)
- 		params->fd_max_int = FD_MAX_INTERVAL_6GHZ;
-@@ -2063,7 +2065,8 @@ static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
- 	if (params->fd_min_int > params->fd_max_int)
- 		params->fd_min_int = params->fd_max_int;
- 
--	if (params->fd_max_int)
-+	if (params->fd_max_int || (is_6ghz_op_class(hapd->iconf->op_class) &&
-+	    !params->unsol_bcast_probe_resp_interval))
- 		return hostapd_gen_fils_discovery(hapd,
- 						  &params->fd_frame_tmpl_len);
- 
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 6778ad369..501d0e42e 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -4780,7 +4780,6 @@ static int nl80211_fils_discovery(struct i802_bss *bss, struct nl_msg *msg,
- 	     nla_put(msg, NL80211_FILS_DISCOVERY_ATTR_TMPL,
- 		     params->fd_frame_tmpl_len, params->fd_frame_tmpl)))
- 		return -1;
--
- 	nla_nest_end(msg, attr);
- 	return 0;
- }
-@@ -5412,7 +5411,10 @@ static int wpa_driver_nl80211_set_ap(void *priv,
- #endif /* CONFIG_SAE */
- 
- #ifdef CONFIG_FILS
--	if (params->fd_max_int && nl80211_fils_discovery(bss, msg, params) < 0)
-+	if ((params->fd_max_int ||
-+	    ((params->freq->freq > 5950 && params->freq->freq <= 7115) &&
-+	      !(params->unsol_bcast_probe_resp_interval))) &&
-+	     nl80211_fils_discovery(bss, msg, params) < 0)
- 		goto fail;
- #endif /* CONFIG_FILS */
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0032-mtk-hostapd-Add-mtk_vendor.h.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0032-mtk-hostapd-Add-mtk_vendor.h.patch
deleted file mode 100644
index e93390d..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0032-mtk-hostapd-Add-mtk_vendor.h.patch
+++ /dev/null
@@ -1,216 +0,0 @@
-From 5be34a5e9682f4448e41322dc4f96d5d9a58240e Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Mon, 30 May 2022 15:04:57 +0800
-Subject: [PATCH 032/104] mtk: hostapd: Add mtk_vendor.h
-
----
- src/common/mtk_vendor.h | 197 ++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 197 insertions(+)
- create mode 100644 src/common/mtk_vendor.h
-
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-new file mode 100644
-index 000000000..4a19d2fc9
---- /dev/null
-+++ b/src/common/mtk_vendor.h
-@@ -0,0 +1,197 @@
-+// SPDX-License-Identifier: ISC
-+/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
-+#ifndef MTK_VENDOR_H
-+#define MTK_VENDOR_H
-+
-+#define OUI_MTK    0x000ce7
-+
-+enum mtk_nl80211_vendor_subcmds {
-+	MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
-+	MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
-+	MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
-+	MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
-+	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
-+};
-+
-+enum mtk_vendor_attr_edcca_ctrl {
-+	MTK_VENDOR_ATTR_EDCCA_THRESHOLD_INVALID = 0,
-+
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_MODE,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL,
-+	MTK_VENDOR_ATTR_EDCCA_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_edcca_ctrl_mode {
-+	EDCCA_CTRL_SET_EN = 0,
-+	EDCCA_CTRL_SET_THERS,
-+	EDCCA_CTRL_GET_EN,
-+	EDCCA_CTRL_GET_THERS,
-+	EDCCA_CTRL_NUM,
-+};
-+
-+static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
-+};
-+
-+enum mtk_vendor_attr_csi_ctrl {
-+	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_CSI_CTRL_CFG,
-+	MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE,
-+	MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE,
-+	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
-+	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
-+	MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
-+	MTK_VENDOR_ATTR_CSI_CTRL_INTERVAL,
-+
-+	MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
-+
-+	MTK_VENDOR_ATTR_CSI_CTRL_DATA,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_CSI_CTRL,
-+	MTK_VENDOR_ATTR_CSI_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_CSI_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_csi_data {
-+	MTK_VENDOR_ATTR_CSI_DATA_UNSPEC,
-+	MTK_VENDOR_ATTR_CSI_DATA_PAD,
-+
-+	MTK_VENDOR_ATTR_CSI_DATA_VER,
-+	MTK_VENDOR_ATTR_CSI_DATA_TS,
-+	MTK_VENDOR_ATTR_CSI_DATA_RSSI,
-+	MTK_VENDOR_ATTR_CSI_DATA_SNR,
-+	MTK_VENDOR_ATTR_CSI_DATA_BW,
-+	MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
-+	MTK_VENDOR_ATTR_CSI_DATA_TA,
-+	MTK_VENDOR_ATTR_CSI_DATA_I,
-+	MTK_VENDOR_ATTR_CSI_DATA_Q,
-+	MTK_VENDOR_ATTR_CSI_DATA_INFO,
-+	MTK_VENDOR_ATTR_CSI_DATA_RSVD1,
-+	MTK_VENDOR_ATTR_CSI_DATA_RSVD2,
-+	MTK_VENDOR_ATTR_CSI_DATA_RSVD3,
-+	MTK_VENDOR_ATTR_CSI_DATA_RSVD4,
-+	MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
-+	MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
-+	MTK_VENDOR_ATTR_CSI_DATA_MODE,
-+	MTK_VENDOR_ATTR_CSI_DATA_H_IDX,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_CSI_DATA,
-+	MTK_VENDOR_ATTR_CSI_DATA_MAX =
-+		NUM_MTK_VENDOR_ATTRS_CSI_DATA - 1
-+};
-+
-+enum mtk_vendor_attr_mnt_ctrl {
-+	MTK_VENDOR_ATTR_AMNT_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_AMNT_CTRL_SET,
-+	MTK_VENDOR_ATTR_AMNT_CTRL_DUMP,
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_AMNT_CTRL,
-+	MTK_VENDOR_ATTR_AMNT_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_AMNT_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_mnt_set {
-+	MTK_VENDOR_ATTR_AMNT_SET_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_AMNT_SET_INDEX,
-+	MTK_VENDOR_ATTR_AMNT_SET_MACADDR,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_AMNT_SET,
-+	MTK_VENDOR_ATTR_AMNT_SET_MAX =
-+		NUM_MTK_VENDOR_ATTRS_AMNT_SET - 1
-+};
-+
-+enum mtk_vendor_attr_mnt_dump {
-+	MTK_VENDOR_ATTR_AMNT_DUMP_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_AMNT_DUMP_INDEX,
-+	MTK_VENDOR_ATTR_AMNT_DUMP_LEN,
-+	MTK_VENDOR_ATTR_AMNT_DUMP_RESULT,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
-+	MTK_VENDOR_ATTR_AMNT_DUMP_MAX =
-+		NUM_MTK_VENDOR_ATTRS_AMNT_DUMP - 1
-+};
-+
-+enum mtk_vendor_attr_wireless_ctrl {
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_rfeature_ctrl {
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
-+};
-+
-+#define CSI_MAX_COUNT 256
-+#define ETH_ALEN 6
-+
-+struct csi_data {
-+	s16 data_i[CSI_MAX_COUNT];
-+	s16 data_q[CSI_MAX_COUNT];
-+	s8 rssi;
-+	u8 snr;
-+	u32 ts;
-+	u8 data_bw;
-+	u8 pri_ch_idx;
-+	u8 ta[ETH_ALEN];
-+	u32 info;
-+	u8 rx_mode;
-+	u32 h_idx;
-+	u16 tx_idx;
-+	u16 rx_idx;
-+};
-+
-+struct amnt_data {
-+	u8 idx;
-+	u8 addr[ETH_ALEN];
-+	s8 rssi[4];
-+	u32 last_seen;
-+};
-+#endif /* MTK_VENDOR_H */
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0032-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0032-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch
new file mode 100644
index 0000000..40542c0
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0032-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch
@@ -0,0 +1,506 @@
+From 8d8d23a68c8e0b47457497d29a3834feb0748682 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Fri, 12 May 2023 05:18:48 +0800
+Subject: [PATCH 032/126] mtk: hostapd: Air Monitor support in hostapd by
+ vendor
+
+Signed-off-by: mtk23888 <dipanshu.mittal@mediatek.com>
+---
+ hostapd/ctrl_iface.c              | 113 +++++++++++++++++++
+ hostapd/hostapd_cli.c             |  15 +++
+ src/ap/ap_drv_ops.c               |  14 +++
+ src/ap/ap_drv_ops.h               |   3 +
+ src/common/mtk_vendor.h           |   8 ++
+ src/drivers/driver.h              |  16 +++
+ src/drivers/driver_nl80211.c      | 179 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   2 +
+ 9 files changed, 351 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 6e7c03f81..03831a908 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4500,6 +4500,44 @@ hostapd_ctrl_iface_ap_wireless(struct hostapd_data *hapd, char *cmd,
+ 
+ 	if (hostapd_drv_ap_wireless(hapd, (u8) sub_cmd, atoi(value)) != 0)
+ 		return -1;
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
++static int
++hostapd_ctrl_iface_set_amnt(struct hostapd_data *hapd, char *cmd,
++					char *buf, size_t buflen)
++{
++	char *tmp, sta_mac[ETH_ALEN] = {0};
++	int amnt_idx = 0;
++
++	tmp = strtok_r(cmd, " ", &cmd);
++
++	if (!tmp) {
++		wpa_printf(MSG_ERROR, "Error in command format\n");
++		return -1;
++	}
++
++	amnt_idx = strtol(tmp, &tmp, 10);
++
++	if (amnt_idx < 0 || amnt_idx > 15) {
++		wpa_printf(MSG_ERROR, "Wrong AMNT index %d\n", amnt_idx);
++		return -1;
++	}
++
++	if (!cmd) {
++		wpa_printf(MSG_ERROR, "Error in command format\n");
++		return -1;
++	}
++
++	if (hwaddr_aton(cmd, sta_mac) < 0) {
++		wpa_printf(MSG_ERROR, "station mac is not right.\n");
++		return -1;
++	}
++
++	if (hostapd_drv_amnt_set(hapd, amnt_idx, sta_mac)) {
++		wpa_printf(MSG_ERROR, "Not able to set amnt index\n");
++		return -1;
++	}
+ 
+ 	return os_snprintf(buf, buflen, "OK\n");
+ }
+@@ -4557,6 +4595,75 @@ exit:
+ 	return os_snprintf(buf, buflen, "OK\n");
+ }
+ 
++static int
++hostapd_ctrl_iface_dump_amnt(struct hostapd_data *hapd, char *cmd,
++				char *buf, size_t buflen)
++{
++	char *tmp;
++	int amnt_idx = 0, ret = 0;
++	struct amnt_resp_data *resp_buf;
++	char *pos, *end;
++	struct amnt_data *res;
++
++	pos = buf;
++	end = buf + buflen;
++
++	tmp = strtok_r(cmd, " ", &cmd);
++
++	if (!tmp) {
++		wpa_printf(MSG_ERROR, "Error in command format\n");
++		return -1;
++	}
++
++	amnt_idx = strtoul(tmp, &tmp, 0);
++
++	if ((amnt_idx < 0 || amnt_idx > 15) && amnt_idx != 0xff) {
++		wpa_printf(MSG_ERROR, "Wrong AMNT index\n");
++		return -1;
++	}
++
++	if (amnt_idx == 0xff)
++		resp_buf = (struct amnt_resp_data *) os_zalloc(AIR_MONITOR_MAX_ENTRY
++							* sizeof(struct amnt_data) + 1);
++	else
++		resp_buf = (struct amnt_resp_data *) os_zalloc(sizeof(struct amnt_data) + 1);
++
++	if (resp_buf == NULL) {
++		wpa_printf(MSG_ERROR, "Error in memory allocation\n");
++		return -1;
++	}
++
++	if (hostapd_drv_amnt_dump(hapd, amnt_idx, (u8 *)resp_buf)) {
++		wpa_printf(MSG_ERROR, "Not able to set amnt index\n");
++		os_free(resp_buf);
++		return -1;
++	}
++
++	for (int i = 0; i < resp_buf->sta_num && i < AIR_MONITOR_MAX_ENTRY; i++) {
++		res = &resp_buf->resp_data[i];
++		ret = os_snprintf(pos, end - pos,
++				"[hostapd_cli] amnt_idx: %d, addr="MACSTR
++				", rssi=%d/%d/%d/%d, last_seen=%u\n",
++				res->idx,
++				MAC2STR(res->addr), res->rssi[0],
++				res->rssi[1], res->rssi[2],
++				res->rssi[3], res->last_seen);
++		if (os_snprintf_error(end - pos, ret)) {
++			os_free(resp_buf);
++			return 0;
++		}
++		pos = pos + ret;
++	}
++
++	os_free(resp_buf);
++
++	if (pos == buf)
++		return os_snprintf(buf, buflen, "Index %d is not monitored\n",
++				amnt_idx);
++	else
++		return pos - buf;
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -5193,6 +5300,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		reply_len = hostapd_ctrl_iface_ap_wireless(hapd, buf + 12, reply, reply_size);
+ 	} else if (os_strncmp(buf, "ap_rfeatures ", 13) == 0) {
+ 		reply_len = hostapd_ctrl_iface_ap_rfeatures(hapd, buf + 13, reply, reply_size);
++	} else if (os_strncmp(buf, "SET_AMNT", 8) == 0) {
++		reply_len = hostapd_ctrl_iface_set_amnt(hapd, buf+9,
++							reply, reply_size);
++	} else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
++		reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
++							reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 27d61b06d..dba805e8a 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1718,6 +1718,17 @@ static int hostapd_cli_cmd_get_amsdu(struct wpa_ctrl *ctrl, int argc,
+ 	return hostapd_cli_cmd(ctrl, "GET_AMSDU", 0, NULL, NULL);
+ }
+ 
++static int hostapd_cli_cmd_set_amnt(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "SET_AMNT", 2, argc, argv);
++}
++
++static int hostapd_cli_cmd_dump_amnt(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "DUMP_AMNT", 1, argc, argv);
++}
+ 
+ struct hostapd_cli_cmd {
+ 	const char *cmd;
+@@ -1957,6 +1968,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 	  " = show iBF state (enabled/disabled)"},
+ 	{ "get_amsdu", hostapd_cli_cmd_get_amsdu, NULL,
+ 		" = show AMSDU state"},
++	{ "set_amnt", hostapd_cli_cmd_set_amnt, NULL,
++		" = Set Station index and mac to monitor"},
++	{ "dump_amnt", hostapd_cli_cmd_dump_amnt, NULL,
++		" = Dump RSSI of monitoring Station"},
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 999cef7ad..5988bd11c 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1387,3 +1387,17 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type)
+ 		return 0;
+ 	return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type);
+ }
++
++int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac)
++{
++	if (!hapd->driver || !hapd->driver->amnt_set)
++		return 0;
++	return hapd->driver->amnt_set(hapd->drv_priv, amnt_idx, amnt_sta_mac);
++}
++
++int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf)
++{
++	if (!hapd->driver || !hapd->driver->amnt_dump)
++		return 0;
++	return hapd->driver->amnt_dump(hapd->drv_priv, amnt_idx, amnt_dump_buf);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 95b8dabbe..d914fd967 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -169,6 +169,9 @@ int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int val
+ int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
+ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
+ 
++int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
++int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
++
+ #include "drivers/driver.h"
+ 
+ int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index aa9df4fc4..ee0c15eb3 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -261,10 +261,18 @@ struct csi_data {
+ 	u16 rx_idx;
+ };
+ 
++#define AIR_MONITOR_MAX_ENTRY 16
++
+ struct amnt_data {
+ 	u8 idx;
+ 	u8 addr[ETH_ALEN];
+ 	s8 rssi[4];
+ 	u32 last_seen;
+ };
++
++struct amnt_resp_data {
++	u8 sta_num;
++	struct amnt_data resp_data[0];
++};
++
+ #endif /* MTK_VENDOR_H */
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index a0ce0aedd..4bc0bbeae 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5330,6 +5330,22 @@ struct wpa_driver_ops {
+ 	* @type: trigger type
+ 	*/
+ 	int (*ap_trigtype)(void *priv, u8 enable, u8 type);
++
++	/**
++	* amnt_set - add/delete station from monitoring
++	* @priv: Private driver interface data
++	* @amnt_idx: Monitor Index
++	* @amnt_sta_mac: station mac address
++	*/
++	int (*amnt_set)(void *priv, u8 amnt_idx, u8 *amnt_sta_mac);
++
++	/**
++	* amnt_dump - Dump particular/ all station
++	* @priv: Private driver interface data
++	* @amnt_idx: Monitor Index
++	* @amnt_dump_buf: Buffer to print
++	*/
++	int (*amnt_dump)(void *priv, u8 amnt_idx, u8 *amnt_dump_buf);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 7b743a1b8..db8a6f8e4 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -143,6 +143,19 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
+ 	[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
+ };
+ 
++static struct nla_policy
++amnt_ctrl_policy[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL] = {
++	[MTK_VENDOR_ATTR_AMNT_CTRL_SET] = {.type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP] = { .type = NLA_NESTED },
++};
++
++static struct nla_policy
++amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
++	[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_AMNT_DUMP_LEN] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
++};
++
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+ 	struct nl_sock *handle;
+@@ -14883,6 +14896,170 @@ fail:
+ 	return -ENOBUFS;
+ }
+ 
++static int
++nl80211_amnt_set(void *priv, u8 amnt_idx, u8 *amnt_sta_mac)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	void *tb1;
++	int ret;
++
++	if (!drv->mtk_amnt_vendor_cmd_avail) {
++		wpa_printf(MSG_ERROR,
++			"nl80211: Driver does not support air monitor");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++			nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++	if (!data)
++		goto fail;
++
++	tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_AMNT_CTRL_SET);
++	if (!tb1)
++		goto fail;
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_AMNT_SET_INDEX, amnt_idx);
++
++	nla_put(msg, MTK_VENDOR_ATTR_AMNT_SET_MACADDR, ETH_ALEN, amnt_sta_mac);
++
++	nla_nest_end(msg, tb1);
++	nla_nest_end(msg, data);
++
++	ret = send_and_recv_cmd(drv, msg);
++
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set air monitor. ret=%d (%s)",
++			ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++
++}
++
++static int
++mt76_amnt_dump_cb(struct nl_msg *msg, void *arg)
++{
++	struct nlattr *tb[NL80211_ATTR_MAX + 1];
++	struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
++	struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP];
++	struct nlattr *attr, *cur, *data;
++	struct amnt_data *res;
++	int len = 0, rem;
++	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++	struct amnt_resp_data *amnt_dump = (struct amnt_resp_data *)arg;
++
++	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++		genlmsg_attrlen(gnlh, 0), NULL);
++
++	attr = tb[NL80211_ATTR_VENDOR_DATA];
++	if (!attr)
++		return NL_SKIP;
++
++	nla_parse_nested(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX,
++			attr, amnt_ctrl_policy);
++
++	if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP])
++		return NL_SKIP;
++
++	nla_parse_nested(tb2, NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
++			tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP], amnt_dump_policy);
++
++	if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_LEN])
++		return NL_SKIP;
++
++	len = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_DUMP_LEN]);
++	if (!len)
++		return 0;
++
++	if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT])
++		return NL_SKIP;
++
++	data = tb2[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT];
++
++	nla_for_each_nested(cur, data, rem) {
++		if (amnt_dump->sta_num >= AIR_MONITOR_MAX_ENTRY)
++			return NL_SKIP;
++		res = (struct amnt_data *) nla_data(cur);
++		wpa_printf(MSG_ERROR, "[vendor] amnt_idx: %d, "
++			"addr="MACSTR", "
++			"rssi=%d/%d/%d/%d, last_seen=%u\n",
++			res->idx,
++			MAC2STR(res->addr),
++			res->rssi[0], res->rssi[1], res->rssi[2],
++			res->rssi[3], res->last_seen);
++		os_memcpy(&amnt_dump->resp_data[amnt_dump->sta_num], res,
++			sizeof(struct amnt_data));
++		amnt_dump->sta_num++;
++	}
++	return 0;
++}
++
++static int
++nl80211_amnt_dump(void *priv, u8 amnt_idx, u8 *dump_buf)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	void *tb1;
++	int ret;
++
++	if (!drv->mtk_amnt_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			"nl80211: Driver does not support air monitor");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++			nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++	if (!data)
++		goto fail;
++
++	tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_AMNT_CTRL_DUMP
++			| NLA_F_NESTED);
++	if (!tb1)
++		goto fail;
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_AMNT_DUMP_INDEX, amnt_idx);
++
++	nla_nest_end(msg, tb1);
++	nla_nest_end(msg, data);
++
++	ret = send_and_recv_resp(drv, msg, mt76_amnt_dump_cb, dump_buf);
++
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to Dump air monitor. ret=%d (%s)"
++			, ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+ 	.desc = "Linux nl80211/cfg80211",
+@@ -15058,4 +15235,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.ap_wireless = nl80211_ap_wireless,
+ 	.ap_rfeatures = nl80211_ap_rfeatures,
+ 	.ap_trigtype = nl80211_ap_trigtype,
++	.amnt_set = nl80211_amnt_set,
++	.amnt_dump = nl80211_amnt_dump,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 046991a3d..adc1b9bf7 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -207,6 +207,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_wireless_vendor_cmd_avail:1;
+ 	unsigned int mtk_bss_color_vendor_cmd_avail:1;
+ 	unsigned int mtk_rfeatures_vendor_cmd_avail:1;
++	unsigned int mtk_amnt_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index ad7f5003a..4a6ff0f28 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1158,6 +1158,8 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 					break;
+ 				case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
+ 					drv->mtk_bss_color_vendor_cmd_avail = 1;
++				case MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL:
++					drv->mtk_amnt_vendor_cmd_avail = 1;
+ 					break;
+ 				case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
+ 					drv->mtk_rfeatures_vendor_cmd_avail = 1;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0033-mtk-hostapd-Add-muru-user-number-debug-command.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0033-mtk-hostapd-Add-muru-user-number-debug-command.patch
new file mode 100644
index 0000000..0f5d6ef
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0033-mtk-hostapd-Add-muru-user-number-debug-command.patch
@@ -0,0 +1,228 @@
+From f54048fe93acc757c0cb144077d4e4e3fb2ac9fc Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Fri, 12 May 2023 05:24:19 +0800
+Subject: [PATCH 033/126] mtk: hostapd: Add muru user number debug command
+
+---
+ hostapd/ctrl_iface.c         | 13 ++++++++++++-
+ src/ap/ap_drv_ops.c          |  4 ++--
+ src/ap/ap_drv_ops.h          |  2 +-
+ src/ap/hostapd.c             |  3 ++-
+ src/common/mtk_vendor.h      |  7 +++++++
+ src/drivers/driver.h         |  4 ++--
+ src/drivers/driver_nl80211.c | 37 ++++++++++++++++++++++++++++--------
+ 7 files changed, 55 insertions(+), 15 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 03831a908..b78ee24c0 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -3803,6 +3803,8 @@ hostapd_ctrl_iface_set_edcca(struct hostapd_data *hapd, char *cmd,
+ 					 char *buf, size_t buflen)
+ {
+ 	char *pos, *config, *value;
++	u8 mode;
++
+ 	config = cmd;
+ 	pos = os_strchr(config, ' ');
+ 	if (pos == NULL)
+@@ -4195,6 +4197,8 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ 					 char *buf, size_t buflen)
+ {
+ 	char *pos, *config, *value;
++	u8 mode;
++
+ 	config = cmd;
+ 	pos = os_strchr(config, ' ');
+ 	if (pos == NULL)
+@@ -4212,13 +4216,20 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ 			return -1;
+ 		}
+ 		hapd->iconf->mu_onoff = (u8) mu;
++		mode = MU_CTRL_ONOFF;
++	} else if (os_strcmp(config, "ul_user_cnt") == 0) {
++		mode = MU_CTRL_UL_USER_CNT;
++		wpa_printf(MSG_ERROR, "ul_user_cnt:%d\n", (u8)atoi(value));
++	} else if (os_strcmp(config, "dl_user_cnt") == 0) {
++		mode = MU_CTRL_DL_USER_CNT;
++		wpa_printf(MSG_ERROR, "dl_user_cnt:%d\n", (u8)atoi(value));
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			"Unsupported parameter %s for SET_MU", config);
+ 		return -1;
+ 	}
+ 
+-	if(hostapd_drv_mu_ctrl(hapd) == 0) {
++	if(hostapd_drv_mu_ctrl(hapd, mode, (u8)atoi(value)) == 0) {
+ 		return os_snprintf(buf, buflen, "OK\n");
+ 	} else {
+ 		return -1;
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 5988bd11c..496fd4263 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1299,11 +1299,11 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
+ 	return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
+ }
+ 
+-int hostapd_drv_mu_ctrl(struct hostapd_data *hapd)
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val)
+ {
+ 	if (!hapd->driver || !hapd->driver->mu_ctrl)
+ 		return 0;
+-	return hapd->driver->mu_ctrl(hapd->drv_priv, hapd->iconf->mu_onoff);
++	return hapd->driver->mu_ctrl(hapd->drv_priv, mode, val);
+ }
+ 
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index d914fd967..a949e168d 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -156,7 +156,7 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
+ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
+ 					  const int *threshold);
+ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+-int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val);
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 6ab3e6043..9e1ba6a21 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -58,6 +58,7 @@
+ #include "wpa_auth_kay.h"
+ #include "hw_features.h"
+ 
++#include "common/mtk_vendor.h"
+ 
+ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
+ #ifdef CONFIG_WEP
+@@ -2762,7 +2763,7 @@ dfs_offload:
+ 	if (hostapd_drv_configure_edcca_threshold(hapd,
+ 						  hapd->iconf->edcca_threshold) < 0)
+ 		goto fail;
+-	if (hostapd_drv_mu_ctrl(hapd) < 0)
++	if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF, hapd->iconf->mu_onoff) < 0)
+ 		goto fail;
+ 	if (hostapd_drv_three_wire_ctrl(hapd) < 0)
+ 		goto fail;
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index ee0c15eb3..34238f098 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -202,6 +202,8 @@ enum mtk_vendor_attr_mu_ctrl {
+ 
+ 	MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
+ 	MTK_VENDOR_ATTR_MU_CTRL_DUMP,
++	MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE,
++	MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
+@@ -275,4 +277,9 @@ struct amnt_resp_data {
+ 	struct amnt_data resp_data[0];
+ };
+ 
++enum {
++	MU_CTRL_ONOFF,
++	MU_CTRL_DL_USER_CNT,
++	MU_CTRL_UL_USER_CNT,
++};
+ #endif /* MTK_VENDOR_H */
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 4bc0bbeae..c8910739e 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5262,11 +5262,11 @@ struct wpa_driver_ops {
+ 	int (*get_edcca)(void *priv, const u8 mode, u8 *value);
+ 
+ 	/**
+-	 * mu_ctrl - ctrl on off for UL/DL MURU
++	 * mu_ctrl - ctrl for UL/DL MURU
+ 	 * @priv: Private driver interface data
+ 	 *
+ 	 */
+-	 int (*mu_ctrl)(void *priv, u8 mu_onoff);
++	 int (*mu_ctrl)(void *priv, u8 mode, u8 val);
+ 	 int (*mu_dump)(void *priv, u8 *mu_onoff);
+ 
+ 	/**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index db8a6f8e4..da1e6a8ed 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14007,13 +14007,13 @@ fail:
+ 
+ 
+ #ifdef CONFIG_IEEE80211AX
+-static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
++static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+ 	struct nl_msg *msg;
+ 	struct nlattr *data;
+-	int ret;
++	int ret = -ENOBUFS;
+ 
+ 	if (!drv->mtk_mu_vendor_cmd_avail) {
+ 		wpa_printf(MSG_INFO,
+@@ -14024,17 +14024,38 @@ static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
+ 	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ 		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
+ 		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
+-		!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+-		nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, mu_onoff)) {
+-		nlmsg_free(msg);
+-		return -ENOBUFS;
++		!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)))
++		goto fail;
++
++	switch (mode) {
++	case MU_CTRL_ONOFF:
++			if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, val))
++				goto fail;
++		break;
++	case MU_CTRL_UL_USER_CNT:
++	case MU_CTRL_DL_USER_CNT:
++			if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE, mode) ||
++			    nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL, val))
++				goto fail;
++		break;
++	default:
++		wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode !");
++		ret = -EINVAL;
++		goto fail;
+ 	}
++
+ 	nla_nest_end(msg, data);
++
+ 	ret = send_and_recv_cmd(drv, msg);
+ 	if(ret){
+-		wpa_printf(MSG_ERROR, "Failed to set mu_onoff. ret=%d (%s)", ret, strerror(-ret));
++		wpa_printf(MSG_ERROR, "Failed to set mu_ctrl. ret=%d (%s)", ret, strerror(-ret));
+ 	}
+ 	return ret;
++
++fail:
++	nl80211_nlmsg_clear(msg);
++	nlmsg_free(msg);
++	return ret;
+ }
+ 
+ 
+@@ -15206,7 +15227,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.update_connect_params = nl80211_update_connection_params,
+ 	.send_external_auth_status = nl80211_send_external_auth_status,
+ 	.set_4addr_mode = nl80211_set_4addr_mode,
+-	.mu_ctrl = nl80211_mu_onoff,
++	.mu_ctrl = nl80211_mu_ctrl,
+ 	.mu_dump = nl80211_mu_dump,
+ #ifdef CONFIG_DPP
+ 	.dpp_listen = nl80211_dpp_listen,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0033-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0033-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch
deleted file mode 100644
index d7544bc..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0033-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch
+++ /dev/null
@@ -1,630 +0,0 @@
-From 7b735e95647fe46cc5dcf7d859c8f9abbed5ae0d Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Mon, 30 May 2022 16:31:34 +0800
-Subject: [PATCH 033/104] mtk: hostapd: Support EDCCA hostapd configuration
-
-edcca_enable and edcca_compensation and implement edcca related handlers.
----
- hostapd/config_file.c             |  34 ++++++
- hostapd/ctrl_iface.c              | 124 +++++++++++++++++++++
- src/ap/ap_config.c                |   4 +
- src/ap/ap_config.h                |  30 ++++++
- src/ap/ap_drv_ops.c               |  24 +++++
- src/ap/ap_drv_ops.h               |   4 +
- src/ap/hostapd.c                  |   7 ++
- src/common/mtk_vendor.h           |  20 ++--
- src/drivers/driver.h              |   4 +
- src/drivers/driver_nl80211.c      | 174 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |   1 +
- src/drivers/driver_nl80211_capa.c |   7 ++
- 12 files changed, 427 insertions(+), 6 deletions(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 0094db279..f8c1eec0a 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5348,6 +5348,40 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		bss->mld_indicate_disabled = atoi(pos);
- #endif /* CONFIG_TESTING_OPTIONS */
- #endif /* CONFIG_IEEE80211BE */
-+	} else if (os_strcmp(buf, "edcca_threshold") == 0) {
-+		if (hostapd_parse_intlist(&conf->edcca_threshold, pos) ||
-+		    conf->edcca_threshold[0] < EDCCA_MIN_CONFIG_THRES ||
-+		    conf->edcca_threshold[0] > EDCCA_MAX_CONFIG_THRES ||
-+		    conf->edcca_threshold[1] < EDCCA_MIN_CONFIG_THRES ||
-+		    conf->edcca_threshold[1] > EDCCA_MAX_CONFIG_THRES ||
-+		    conf->edcca_threshold[2] < EDCCA_MIN_CONFIG_THRES ||
-+		    conf->edcca_threshold[2] > EDCCA_MAX_CONFIG_THRES ||
-+		    conf->edcca_threshold[3] < EDCCA_MIN_CONFIG_THRES ||
-+		    conf->edcca_threshold[3] > EDCCA_MAX_CONFIG_THRES) {
-+			wpa_printf(MSG_ERROR, "Line %d: invalid edcca threshold",
-+				   line);
-+			return 1;
-+		}
-+	} else if (os_strcmp(buf, "edcca_enable") == 0) {
-+		int mode = atoi(pos);
-+		if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
-+			wpa_printf(MSG_ERROR, "Line %d: Invalid edcca_enable %d;"
-+				  " allowed value 0 (Force Disable) or 1(Auto) ",
-+				   line, mode);
-+			return 1;
-+		}
-+		conf->edcca_enable = (u8) mode;
-+	} else if (os_strcmp(buf, "edcca_compensation") == 0) {
-+		int val = atoi(pos);
-+		if (val < EDCCA_MIN_COMPENSATION ||
-+		    val > EDCCA_MAX_COMPENSATION) {
-+			wpa_printf(MSG_ERROR, "Line %d: Invalid compensation"
-+				   " value %d; allowed value %d ~ %d.",
-+				   line, val, EDCCA_MIN_COMPENSATION,
-+				   EDCCA_MAX_COMPENSATION);
-+			return 1;
-+		}
-+		conf->edcca_compensation = (s8) val;
- 	} else {
- 		wpa_printf(MSG_ERROR,
- 			   "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index fb9f09bb1..78a3380f2 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -544,6 +544,19 @@ static const char * pbc_status_str(enum pbc_status status)
- }
- 
- 
-+static const char *edcca_mode_str(enum edcca_mode status)
-+{
-+	switch (status) {
-+		case EDCCA_MODE_FORCE_DISABLE:
-+			return "Force Disable";
-+		case EDCCA_MODE_AUTO:
-+			return "Auto";
-+		default:
-+			return "Unknown";
-+	}
-+}
-+
-+
- static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd,
- 					     char *buf, size_t buflen)
- {
-@@ -3644,6 +3657,111 @@ static int hostapd_ctrl_iface_link_remove(struct hostapd_data *hapd, char *cmd,
- #endif /* CONFIG_TESTING_OPTIONS */
- #endif /* CONFIG_IEEE80211BE */
- 
-+static int
-+hostapd_ctrl_iface_set_edcca(struct hostapd_data *hapd, char *cmd,
-+					 char *buf, size_t buflen)
-+{
-+	char *pos, *config, *value;
-+	config = cmd;
-+	pos = os_strchr(config, ' ');
-+	if (pos == NULL)
-+		return -1;
-+	*pos++ = '\0';
-+
-+	if (pos == NULL)
-+		return -1;
-+	value = pos;
-+
-+	if (os_strcmp(config, "enable") == 0) {
-+		int mode = atoi(value);
-+		if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
-+			wpa_printf(MSG_ERROR, "Invalid value for edcca enable");
-+			return -1;
-+		}
-+		hapd->iconf->edcca_enable = (u8) mode;
-+		if (hostapd_drv_configure_edcca_enable(hapd) != 0)
-+			return -1;
-+	} else if (os_strcmp(config, "compensation") == 0) {
-+		int compensation = atoi(value);
-+		if (compensation < EDCCA_MIN_COMPENSATION ||
-+		    compensation > EDCCA_MAX_COMPENSATION) {
-+			wpa_printf(MSG_ERROR, "Invalid value for edcca compensation");
-+			return -1;
-+		}
-+		hapd->iconf->edcca_compensation = (s8) compensation;
-+		if (hostapd_drv_configure_edcca_enable(hapd) != 0)
-+			return -1;
-+	} else if (os_strcmp(config, "threshold") == 0) {
-+		char *thres_value;
-+		thres_value = os_strchr(value, ':');
-+		if (thres_value == NULL)
-+			return -1;
-+		*thres_value++ = '\0';
-+
-+		if (thres_value == NULL)
-+			return -1;
-+		int bw_idx = atoi(value);
-+		int threshold = atoi(thres_value);
-+
-+		if (bw_idx < EDCCA_BW_20 || bw_idx > EDCCA_BW_160) {
-+			wpa_printf(MSG_ERROR,
-+				   "Unsupported Bandwidth idx %d for SET_EDCCA",
-+				   bw_idx);
-+			return -1;
-+		}
-+		if (threshold < EDCCA_MIN_CONFIG_THRES ||
-+		    threshold > EDCCA_MAX_CONFIG_THRES) {
-+			wpa_printf(MSG_ERROR,
-+				   "Unsupported threshold %d for SET_EDCCA",
-+				   threshold);
-+			return -1;
-+		}
-+
-+		int threshold_arr[EDCCA_MAX_BW_NUM];
-+		/* 0x7f means keep the origival value in firmware */
-+		os_memset(threshold_arr, 0x7f, sizeof(threshold_arr));
-+		threshold_arr[bw_idx] = threshold;
-+
-+		if (hostapd_drv_configure_edcca_threshold(hapd, threshold_arr) != 0)
-+			return -1;
-+	} else {
-+		wpa_printf(MSG_ERROR,
-+			"Unsupported parameter %s for SET_EDCCA", config);
-+		return -1;
-+	}
-+	return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+
-+static int
-+hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *cmd, char *buf,
-+			     size_t buflen)
-+{
-+	char *pos, *end;
-+
-+	pos = buf;
-+	end = buf + buflen;
-+	u8 value[EDCCA_MAX_BW_NUM] = {0};
-+
-+	if (os_strcmp(cmd, "enable") == 0) {
-+		return os_snprintf(pos, end - pos, "Enable: %s\n",
-+				   edcca_mode_str(hapd->iconf->edcca_enable));
-+	} else if (os_strcmp(cmd, "compensation") == 0) {
-+		return os_snprintf(pos, end - pos, "Compensation: %d\n",
-+				  hapd->iconf->edcca_compensation);
-+	} else if (os_strcmp(cmd, "threshold") == 0) {
-+		if (hostapd_drv_get_edcca(hapd, EDCCA_CTRL_GET_THRES, &value) != 0)
-+			return -1;
-+		return os_snprintf(pos, end - pos,
-+				   "Threshold BW20: 0x%x, BW40: 0x%x, BW80: 0x%x, BW160: 0x%x\n",
-+				   value[0], value[1], value[2], value[3]);
-+	} else {
-+		wpa_printf(MSG_ERROR,
-+			"Unsupported parameter %s for GET_EDCCA", cmd);
-+		return -1;
-+	}
-+}
-+
- 
- #ifdef CONFIG_NAN_USD
- 
-@@ -4531,6 +4649,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 			reply_len = -1;
- #endif /* CONFIG_TESTING_OPTIONS */
- #endif /* CONFIG_IEEE80211BE */
-+	} else if (os_strncmp(buf, "SET_EDCCA ", 10) == 0) {
-+		reply_len = hostapd_ctrl_iface_set_edcca(hapd, buf+10, reply,
-+							  reply_size);
-+	} else if (os_strncmp(buf, "GET_EDCCA ", 10) == 0) {
-+		reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
-+							  reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 6c8b10291..965600577 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -303,6 +303,9 @@ struct hostapd_config * hostapd_config_defaults(void)
- 	conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL;
- #endif /* CONFIG_AIRTIME_POLICY */
- 
-+	conf->edcca_enable = EDCCA_MODE_AUTO;
-+	conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
-+
- 	hostapd_set_and_check_bw320_offset(conf, 0);
- 
- 	return conf;
-@@ -1034,6 +1037,7 @@ void hostapd_config_free(struct hostapd_config *conf)
- #ifdef CONFIG_ACS
- 	os_free(conf->acs_chan_bias);
- #endif /* CONFIG_ACS */
-+	os_free(conf->edcca_threshold);
- 	wpabuf_free(conf->lci);
- 	wpabuf_free(conf->civic);
- #ifdef CONFIG_AFC
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 379dc22cf..09718fada 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1281,8 +1281,38 @@ struct hostapd_config {
- 		int min_power;
- 	} afc;
- #endif /* CONFIG_AFC */
-+
-+	u8 edcca_enable;
-+	s8 edcca_compensation;
-+	int *edcca_threshold;
-+};
-+
-+enum edcca_mode {
-+	EDCCA_MODE_FORCE_DISABLE = 0,
-+	EDCCA_MODE_AUTO = 1,
-+};
-+
-+enum edcca_bw_id {
-+	EDCCA_BW_20 = 0,
-+	EDCCA_BW_40,
-+	EDCCA_BW_80,
-+	EDCCA_BW_160,
-+	EDCCA_MAX_BW_NUM,
-+};
-+
-+enum mtk_vendor_attr_edcca_ctrl_mode {
-+	EDCCA_CTRL_SET_EN = 0,
-+	EDCCA_CTRL_SET_THRES,
-+	EDCCA_CTRL_GET_EN,
-+	EDCCA_CTRL_GET_THRES,
-+	EDCCA_CTRL_NUM,
- };
- 
-+#define EDCCA_DEFAULT_COMPENSATION -6
-+#define EDCCA_MIN_COMPENSATION -126
-+#define EDCCA_MAX_COMPENSATION 126
-+#define EDCCA_MIN_CONFIG_THRES -126
-+#define EDCCA_MAX_CONFIG_THRES 0
- 
- static inline enum oper_chan_width
- hostapd_get_oper_chwidth(struct hostapd_config *conf)
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 527b2c984..a6caf6a73 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1245,3 +1245,27 @@ int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
- 	return hapd->driver->set_secure_ranging_ctx(hapd->drv_priv, &params);
- }
- #endif /* CONFIG_PASN */
-+
-+int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd)
-+{
-+	if (!hapd->driver || !hapd->driver->configure_edcca_enable)
-+		return 0;
-+	return hapd->driver->configure_edcca_enable(hapd->drv_priv,
-+			hapd->iconf->edcca_enable,
-+				hapd->iconf->edcca_compensation);
-+}
-+
-+int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
-+					  const int *threshold)
-+{
-+	if (!hapd->driver || !hapd->driver->configure_edcca_threshold)
-+		return 0;
-+	return hapd->driver->configure_edcca_threshold(hapd->drv_priv, threshold);
-+}
-+
-+int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
-+{
-+	if (!hapd->driver || !hapd->driver->get_edcca)
-+		return 0;
-+	return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index f8a8725be..98836153f 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -149,6 +149,10 @@ int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
- 				       u8 ltf_keyseed_len,
- 				       const u8 *ltf_keyseed, u32 action);
- 
-+int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
-+int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
-+					  const int *threshold);
-+int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 7959859b0..6af31179e 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2693,6 +2693,13 @@ dfs_offload:
- 	}
- #endif /* CONFIG_MESH */
- 
-+	if (hostapd_drv_configure_edcca_enable(hapd) < 0)
-+		goto fail;
-+
-+	if (hostapd_drv_configure_edcca_threshold(hapd,
-+						  hapd->iconf->edcca_threshold) < 0)
-+		goto fail;
-+
- 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- 		   iface->bss[0]->conf->iface);
- 	if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 4a19d2fc9..6121857dd 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -30,14 +30,22 @@ enum mtk_vendor_attr_edcca_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
- };
- 
--enum mtk_vendor_attr_edcca_ctrl_mode {
--	EDCCA_CTRL_SET_EN = 0,
--	EDCCA_CTRL_SET_THERS,
--	EDCCA_CTRL_GET_EN,
--	EDCCA_CTRL_GET_THERS,
--	EDCCA_CTRL_NUM,
-+enum mtk_vendor_attr_edcca_dump {
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_UNSPEC = 0,
-+
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_MODE,
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL,
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP,
-+	MTK_VENDOR_ATTR_EDCCA_DUMP_MAX =
-+		NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
- };
- 
-+
- static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
- 	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
- 	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 3e3e309f4..ed5f5c013 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5220,6 +5220,10 @@ struct wpa_driver_ops {
- 			      const u8 *match, size_t match_len,
- 			      bool multicast);
- #endif /* CONFIG_TESTING_OPTIONS */
-+	int (*configure_edcca_enable)(void *priv, const u8 edcca_enable,
-+				  const s8 edcca_compensation);
-+	int (*configure_edcca_threshold)(void *priv, const int *threshold);
-+	int (*get_edcca)(void *priv, const u8 mode, u8 *value);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 501d0e42e..d59efe8b6 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -42,6 +42,8 @@
- #include "radiotap_iter.h"
- #include "rfkill.h"
- #include "driver_nl80211.h"
-+#include "common/mtk_vendor.h"
-+#include "ap/ap_config.h"
- 
- 
- #ifndef NETLINK_CAP_ACK
-@@ -14079,6 +14081,174 @@ static int testing_nl80211_radio_disable(void *priv, int disabled)
- 
- #endif /* CONFIG_TESTING_OPTIONS */
- 
-+static int nl80211_configure_edcca_enable(void *priv,
-+					  const u8 edcca_enable,
-+					  const s8 edcca_compensation)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_edcca_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting EDCCA enable");
-+		return 0;
-+	}
-+
-+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
-+	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_EN) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, edcca_enable) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
-+		edcca_compensation)) {
-+		wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
-+		nlmsg_free(msg);
-+		return -ENOBUFS;
-+	}
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_cmd(drv, msg);
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to configure EDCCA enable. ret=%d (%s) ",
-+			   ret, strerror(-ret));
-+	}
-+	return ret;
-+}
-+
-+static int nl80211_configure_edcca_threshold(void *priv, const int *threshold)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_edcca_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting EDCCA threshold");
-+		return 0;
-+	}
-+
-+	if (!threshold) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Input EDCCA threshold is empty!");
-+		return 0;
-+	}
-+
-+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
-+	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_THRES) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, threshold[0] & 0xff) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL, threshold[1] & 0xff) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL, threshold[2] & 0xff) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL, threshold[3] & 0xff)) {
-+		wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
-+		nlmsg_free(msg);
-+		return -ENOBUFS;
-+	}
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_cmd(drv, msg);
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to configure EDCCA threshold. ret=%d (%s) ",
-+			   ret, strerror(-ret));
-+	}
-+	return ret;
-+}
-+
-+
-+static int edcca_info_handler(struct nl_msg *msg, void *arg)
-+{
-+	u8 *info = (u8 *) arg;
-+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_MAX + 1];
-+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+	struct nlattr *nl_vend, *attr;
-+
-+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+		  genlmsg_attrlen(gnlh, 0), NULL);
-+
-+	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+	if (!nl_vend)
-+		return NL_SKIP;
-+
-+	nla_parse(tb_vendor, MTK_VENDOR_ATTR_EDCCA_DUMP_MAX,
-+		  nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+	attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL];
-+	if (!attr) {
-+		wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL");
-+		return NL_SKIP;
-+	}
-+
-+	*info++ = nla_get_u8(attr);
-+
-+	attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL];
-+	if (!attr) {
-+		wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL");
-+		return NL_SKIP;
-+	}
-+
-+	*info++ = nla_get_u8(attr);
-+
-+	attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL];
-+	if (!attr) {
-+		wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL");
-+		return NL_SKIP;
-+	}
-+
-+	*info++ = nla_get_u8(attr);
-+
-+	attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL];
-+	if (!attr) {
-+		wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL");
-+		return NL_SKIP;
-+	}
-+
-+	*info = nla_get_u8(attr);
-+	return NL_SKIP;
-+}
-+
-+
-+static int nl80211_get_edcca(void *priv, const u8 mode, u8 *value)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_edcca_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting EDCCA threshold");
-+		return 0;
-+	}
-+
-+	if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
-+	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED)) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, mode)) {
-+		wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
-+		nlmsg_free(msg);
-+		return -ENOBUFS;
-+	}
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_resp(drv, msg, edcca_info_handler, value);
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to get EDCCA configuration. ret=%d (%s)",
-+			   ret, strerror(-ret));
-+	}
-+	return ret;
-+}
-+
- 
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
-@@ -14240,4 +14410,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.register_frame = testing_nl80211_register_frame,
- 	.radio_disable = testing_nl80211_radio_disable,
- #endif /* CONFIG_TESTING_OPTIONS */
-+/* Need ifdef CONFIG_DRIVER_NL80211_MTK */
-+	.configure_edcca_enable = nl80211_configure_edcca_enable,
-+	.configure_edcca_threshold = nl80211_configure_edcca_threshold,
-+	.get_edcca = nl80211_get_edcca,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 618746e67..62c47efbd 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -200,6 +200,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int secure_ranging_ctx_vendor_cmd_avail:1;
- 	unsigned int puncturing:1;
- 	unsigned int qca_ap_allowed_freqs:1;
-+	unsigned int mtk_edcca_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index d6a887cef..cd4d799a1 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -18,6 +18,7 @@
- #include "common/qca-vendor-attr.h"
- #include "common/brcm_vendor.h"
- #include "driver_nl80211.h"
-+#include "common/mtk_vendor.h"
- 
- 
- static int protocol_feature_handler(struct nl_msg *msg, void *arg)
-@@ -1138,6 +1139,12 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 					break;
- 				}
- #endif /* CONFIG_DRIVER_NL80211_BRCM */
-+			} else if (vinfo->vendor_id == OUI_MTK) {
-+				switch (vinfo->subcmd) {
-+				case MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL:
-+					drv->mtk_edcca_vendor_cmd_avail = 1;
-+					break;
-+				}
- 			}
- 
- 			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0034-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0034-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch
deleted file mode 100644
index 3849fa4..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0034-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch
+++ /dev/null
@@ -1,454 +0,0 @@
-From fa905ce61f3cfecf94012fc2a11680e2615fc05d Mon Sep 17 00:00:00 2001
-From: TomLiu <tomml.liu@mediatek.com>
-Date: Tue, 9 Aug 2022 10:23:44 -0700
-Subject: [PATCH 034/104] mtk: hostapd: Add hostapd MU SET/GET control
-
----
- hostapd/config_file.c             |   9 +++
- hostapd/ctrl_iface.c              |  66 ++++++++++++++++++
- hostapd/hostapd_cli.c             |  18 +++++
- src/ap/ap_config.c                |   1 +
- src/ap/ap_config.h                |   1 +
- src/ap/ap_drv_ops.c               |  14 ++++
- src/ap/ap_drv_ops.h               |   2 +
- src/ap/hostapd.c                  |   2 +
- src/common/mtk_vendor.h           |  15 ++++
- src/drivers/driver.h              |  13 ++++
- src/drivers/driver_nl80211.c      | 110 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |   1 +
- src/drivers/driver_nl80211_capa.c |   3 +
- 13 files changed, 255 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index f8c1eec0a..637c2df9f 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4159,6 +4159,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 			return 1;
- 		}
- 		conf->mbssid = mbssid;
-+	} else if (os_strcmp(buf, "mu_onoff") == 0) {
-+		int val = atoi(pos);
-+		if (val < 0 || val > 15) {
-+			wpa_printf(MSG_ERROR,
-+				   "Line %d: invalid mu_onoff value",
-+				   line);
-+			return 1;
-+		}
-+		conf->mu_onoff = val;
- #endif /* CONFIG_IEEE80211AX */
- 	} else if (os_strcmp(buf, "max_listen_interval") == 0) {
- 		bss->max_listen_interval = atoi(pos);
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 78a3380f2..3a79a1284 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4049,6 +4049,67 @@ fail:
- #endif /* CONFIG_NAN_USD */
- 
- 
-+static int
-+hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
-+					 char *buf, size_t buflen)
-+{
-+	char *pos, *config, *value;
-+	config = cmd;
-+	pos = os_strchr(config, ' ');
-+	if (pos == NULL)
-+		return -1;
-+	*pos++ = '\0';
-+
-+	if(pos == NULL)
-+		return -1;
-+	value = pos;
-+
-+	if (os_strcmp(config, "onoff") == 0) {
-+		int mu = atoi(value);
-+		if (mu < 0 || mu > 15) {
-+			wpa_printf(MSG_ERROR, "Invalid value for mu");
-+			return -1;
-+		}
-+		hapd->iconf->mu_onoff = (u8) mu;
-+	} else {
-+		wpa_printf(MSG_ERROR,
-+			"Unsupported parameter %s for SET_MU", config);
-+		return -1;
-+	}
-+
-+	if(hostapd_drv_mu_ctrl(hapd) == 0) {
-+		return os_snprintf(buf, buflen, "OK\n");
-+	} else {
-+		return -1;
-+	}
-+}
-+
-+
-+static int
-+hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
-+					 size_t buflen)
-+{
-+	u8 mu_onoff;
-+	char *pos, *end;
-+
-+	pos = buf;
-+	end = buf + buflen;
-+
-+	if (hapd->iface->state != HAPD_IFACE_ENABLED)
-+		return os_snprintf(pos, end - pos, "Not allowed to get_mu when current state is %s\n",
-+				   hostapd_state_text(hapd->iface->state));
-+
-+	if (hostapd_drv_mu_dump(hapd, &mu_onoff) == 0) {
-+		hapd->iconf->mu_onoff = mu_onoff;
-+		return os_snprintf(pos, end - pos, "[hostapd_cli] = UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
-+			!!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
-+	} else {
-+		wpa_printf(MSG_INFO, "ctrl iface failed to call");
-+		return -1;
-+	}
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -4655,6 +4716,11 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 	} else if (os_strncmp(buf, "GET_EDCCA ", 10) == 0) {
- 		reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
- 							  reply_size);
-+	} else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
-+		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply,
-+							  reply_size);
-+	} else if (os_strncmp(buf, "GET_MU", 6) == 0) {
-+		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 1fb6d999e..da9dabd6f 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1442,6 +1442,20 @@ static int hostapd_cli_cmd_driver_flags2(struct wpa_ctrl *ctrl, int argc,
- }
- 
- 
-+static int hostapd_cli_cmd_set_mu(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "SET_MU", 1, argc, argv);
-+}
-+
-+
-+static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
-+}
-+
-+
- #ifdef CONFIG_DPP
- 
- static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
-@@ -1801,6 +1815,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 	  " = show supported driver flags"},
- 	{ "driver_flags2", hostapd_cli_cmd_driver_flags2, NULL,
- 	  " = show supported driver flags2"},
-+	{ "set_mu", hostapd_cli_cmd_set_mu, NULL,
-+		"<value> [0-15] bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0)"},
-+	{ "get_mu", hostapd_cli_cmd_get_mu, NULL,
-+		" = show mu onoff value in 0-15 bitmap"},
- #ifdef CONFIG_DPP
- 	{ "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 965600577..9b3ef0b5b 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -289,6 +289,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- 	conf->reg_def_cli_eirp_psd = -1;
- 	conf->reg_sub_cli_eirp_psd = -1;
- 	conf->reg_def_cli_eirp = -1;
-+	conf->mu_onoff = 15;
- #endif /* CONFIG_IEEE80211AX */
- 
- 	/* The third octet of the country string uses an ASCII space character
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 09718fada..f7dbbbec3 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1185,6 +1185,7 @@ struct hostapd_config {
- 	int reg_def_cli_eirp;
- 
- 	bool require_he;
-+	u8 mu_onoff;
- #endif /* CONFIG_IEEE80211AX */
- 
- 	/* VHT enable/disable config from CHAN_SWITCH */
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index a6caf6a73..897ed6af8 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1269,3 +1269,17 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
- 		return 0;
- 	return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
- }
-+
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd)
-+{
-+	if (!hapd->driver || !hapd->driver->mu_ctrl)
-+		return 0;
-+	return hapd->driver->mu_ctrl(hapd->drv_priv, hapd->iconf->mu_onoff);
-+}
-+
-+int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
-+{
-+	if (!hapd->driver || !hapd->driver->mu_dump)
-+		return 0;
-+	return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 98836153f..5ab20cc41 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -153,6 +153,8 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
- int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
- 					  const int *threshold);
- int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 6af31179e..d29b51fc5 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2699,6 +2699,8 @@ dfs_offload:
- 	if (hostapd_drv_configure_edcca_threshold(hapd,
- 						  hapd->iconf->edcca_threshold) < 0)
- 		goto fail;
-+	if (hostapd_drv_mu_ctrl(hapd) < 0)
-+		goto fail;
- 
- 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- 		   iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 6121857dd..60bc4cd4c 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -10,6 +10,8 @@ enum mtk_nl80211_vendor_subcmds {
- 	MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
- 	MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
- 	MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
-+	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
-+	MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
- 	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
- };
- 
-@@ -177,6 +179,19 @@ enum mtk_vendor_attr_rfeature_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
- };
- 
-+enum mtk_vendor_attr_mu_ctrl {
-+	MTK_VENDOR_ATTR_MU_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
-+	MTK_VENDOR_ATTR_MU_CTRL_DUMP,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
-+	MTK_VENDOR_ATTR_MU_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
-+};
-+
-+
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index ed5f5c013..df7ce5ab9 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -176,6 +176,11 @@ struct hostapd_channel_data {
- 	 * punct_bitmap - RU puncturing bitmap
- 	 */
- 	u16 punct_bitmap;
-+
-+	/**
-+	 * mu onoff=<val> (bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0))
-+	 */
-+	u8 mu_onoff;
- };
- 
- #define HE_MAC_CAPAB_0		0
-@@ -5224,6 +5229,14 @@ struct wpa_driver_ops {
- 				  const s8 edcca_compensation);
- 	int (*configure_edcca_threshold)(void *priv, const int *threshold);
- 	int (*get_edcca)(void *priv, const u8 mode, u8 *value);
-+
-+	/**
-+	 * mu_ctrl - ctrl on off for UL/DL MURU
-+	 * @priv: Private driver interface data
-+	 *
-+	 */
-+	 int (*mu_ctrl)(void *priv, u8 mu_onoff);
-+	 int (*mu_dump)(void *priv, u8 *mu_onoff);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index d59efe8b6..c234eb029 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13917,6 +13917,114 @@ fail:
- }
- 
- 
-+#ifdef CONFIG_IEEE80211AX
-+static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_mu_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting mu control");
-+		return 0;
-+	}
-+
-+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
-+		!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+		nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, mu_onoff)) {
-+		nlmsg_free(msg);
-+		return -ENOBUFS;
-+	}
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_cmd(drv, msg);
-+	if(ret){
-+		wpa_printf(MSG_ERROR, "Failed to set mu_onoff. ret=%d (%s)", ret, strerror(-ret));
-+	}
-+	return ret;
-+}
-+
-+
-+static int mu_dump_handler(struct nl_msg *msg, void *arg)
-+{
-+	u8 *mu_onoff = (u8 *) arg;
-+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_MU_CTRL_MAX + 1];
-+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+	struct nlattr *nl_vend, *attr;
-+
-+	static const struct nla_policy
-+	mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL + 1] = {
-+		[MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
-+		[MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
-+	};
-+
-+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+			genlmsg_attrlen(gnlh, 0), NULL);
-+
-+	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+	if (!nl_vend)
-+		return NL_SKIP;
-+
-+	nla_parse(tb_vendor, MTK_VENDOR_ATTR_MU_CTRL_MAX,
-+		  nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+	attr = tb_vendor[MTK_VENDOR_ATTR_MU_CTRL_DUMP];
-+	if (!attr) {
-+		wpa_printf(MSG_ERROR, "nl80211: cannot find MTK_VENDOR_ATTR_MU_CTRL_DUMP");
-+		return NL_SKIP;
-+	}
-+
-+	*mu_onoff = nla_get_u8(attr);
-+	wpa_printf(MSG_DEBUG, "nla_get mu_onoff: %d\n", *mu_onoff);
-+
-+	return 0;
-+}
-+
-+static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *attr;
-+	int ret;
-+
-+	if (!drv->mtk_mu_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting mu control");
-+		return 0;
-+	}
-+
-+	if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL)) {
-+		nlmsg_free(msg);
-+		return -ENOBUFS;
-+	}
-+
-+  attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+	if (!attr) {
-+		nlmsg_free(msg);
-+		return -1;
-+	}
-+
-+	nla_nest_end(msg, attr);
-+
-+	ret = send_and_recv_resp(drv, msg, mu_dump_handler, mu_onoff);
-+
-+	if(ret){
-+		wpa_printf(MSG_ERROR, "Failed to get mu_onoff. ret=%d (%s)", ret, strerror(-ret));
-+	}
-+
-+	return ret;
-+}
-+#endif /* CONFIG_IEEE80211AX */
-+
-+
- #ifdef CONFIG_DPP
- static int nl80211_dpp_listen(void *priv, bool enable)
- {
-@@ -14396,6 +14504,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.update_connect_params = nl80211_update_connection_params,
- 	.send_external_auth_status = nl80211_send_external_auth_status,
- 	.set_4addr_mode = nl80211_set_4addr_mode,
-+	.mu_ctrl = nl80211_mu_onoff,
-+	.mu_dump = nl80211_mu_dump,
- #ifdef CONFIG_DPP
- 	.dpp_listen = nl80211_dpp_listen,
- #endif /* CONFIG_DPP */
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 62c47efbd..f99bba9e1 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -201,6 +201,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int puncturing:1;
- 	unsigned int qca_ap_allowed_freqs:1;
- 	unsigned int mtk_edcca_vendor_cmd_avail:1;
-+	unsigned int mtk_mu_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index cd4d799a1..9c0a47971 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1144,6 +1144,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL:
- 					drv->mtk_edcca_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_MU_CTRL :
-+					drv->mtk_mu_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0034-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0034-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch
new file mode 100644
index 0000000..69615ec
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0034-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch
@@ -0,0 +1,656 @@
+From f7ffccc3487fbc51786d4063b03e0e4a090253b0 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Sat, 3 Jun 2023 17:12:15 +0800
+Subject: [PATCH 034/126] mtk: hostapd: add connac3 PHY MURU manual mode config
+ support
+
+This commit supports read the following two formats to set MU/RU manual
+mode:
+1. hostapd_cli -i <intf> raw set_muru_manual_config=<field>:<value>
+2. hostapd_cli -i <intf> set_mu <field> <value>
+
+For the <field>, we support the following field:
+1. ul_comm_user_cnt/dl_comm_user_cnt: set the number of user
+2. ul_comm_bw/dl_comm_bw: set the bandwith
+3. ul_user_ru_alloc/dl_user_ru_alloc: set the RU band idx and RU
+allocate idx
+4. ul_user_mcs/dl_user_mcs: set the mcs for each user
+5. ul_user_ssAlloc_raru: set the number of ss for each user
+6. ul_comm_gi_ltf: set the combinations of gi and ltf for UL only.
+7. dl_comm_toneplan: fix ru toneplan allocation
+8. dl_comm_ack_policy: fix station ack policy
+9. update : trigger driver to send mcu command to set muru manual mode.
+
+For the value of each field, please check wiki to learn the details:
+https://wiki.mediatek.inc/display/GWKB/muru_mancfg_user_guide
+
+For the fields that mt76 support to use, we will update in this wiki:
+https://wiki.mediatek.inc/pages/viewpage.action?pageId=1271741116
+
+Please noted that this commit is only for connac 3 gen chips. If this
+feature is to be used in other generations, the following actions must
+be taken:
+1. Different data structue needs to be defined for different
+generations, e.g. connac4_muru_comm, connac4_muru_dl.
+2. hostapd_ctrl_iface_set_mu() shall be modified.
+3. A new code level configuration shall be defined to differentiate the
+code flow that different generations will go through.
+
+Add support new argument global_comm_band for muru manual config
+command. This argument can be used to specify the band to apply manual
+config.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ hostapd/ctrl_iface.c         | 240 +++++++++++++++++++++++++++++++----
+ src/ap/ap_config.h           |   1 +
+ src/ap/ap_drv_ops.c          |   4 +-
+ src/ap/ap_drv_ops.h          |   2 +-
+ src/ap/hostapd.c             |   2 +-
+ src/common/mtk_vendor.h      | 166 +++++++++++++++++++++++-
+ src/drivers/driver.h         |   2 +-
+ src/drivers/driver_nl80211.c |  21 ++-
+ 8 files changed, 394 insertions(+), 44 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index b78ee24c0..68b83bf6e 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -3905,7 +3905,6 @@ hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *cmd, char *buf,
+ 	}
+ }
+ 
+-
+ #ifdef CONFIG_NAN_USD
+ 
+ static int hostapd_ctrl_nan_publish(struct hostapd_data *hapd, char *cmd,
+@@ -4192,21 +4191,61 @@ fail:
+ #endif /* CONFIG_NAN_USD */
+ 
+ 
++static int
++hostapd_parse_argument_helper(char *value, u16 **ptr_input)
++{
++#define MAX_MU_CTRL_NUM 17
++	u16 *input;
++	char *endptr;
++	int cnt = 0;
++
++	input = os_zalloc(MAX_MU_CTRL_NUM * sizeof(u16));
++	if (input == NULL) {
++		wpa_printf(MSG_ERROR, "Failed to allocate memory.\n");
++		return -1;
++	}
++	while (value) {
++		u8 val = strtol(value, &endptr, 10);
++
++		if (value != endptr) {
++			input[cnt++] = val;
++			value = os_strchr(endptr, ':');
++			if (value)
++				value++;
++		} else {
++			break;
++		}
++	}
++
++	*ptr_input = input;
++	return cnt;
++}
++
++#define MURU_CFG_DEPENDENCE_CHECK(_val, _mask) do {				\
++		if ((le_to_host32(_val) & (_mask)) != _mask) {			\
++			wpa_printf(MSG_ERROR, "Set %s first\n", #_mask);	\
++			goto fail;						\
++		}								\
++	} while(0)
++
+ static int
+ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+-					 char *buf, size_t buflen)
++			  char *buf, size_t buflen)
+ {
+ 	char *pos, *config, *value;
+-	u8 mode;
++	u8 i;
++	int cnt = 0, ret;
++	u16 *val;
++	struct connac3_muru *muru;
++	struct connac3_muru_dl *dl;
++	struct connac3_muru_ul *ul;
++	struct connac3_muru_comm *comm;
+ 
+ 	config = cmd;
+ 	pos = os_strchr(config, ' ');
+-	if (pos == NULL)
+-		return -1;
+-	*pos++ = '\0';
++	if (pos != NULL)
++		*pos++ = '\0';
+ 
+-	if(pos == NULL)
+-		return -1;
+ 	value = pos;
+ 
+ 	if (os_strcmp(config, "onoff") == 0) {
+@@ -4216,24 +4255,170 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ 			return -1;
+ 		}
+ 		hapd->iconf->mu_onoff = (u8) mu;
+-		mode = MU_CTRL_ONOFF;
+-	} else if (os_strcmp(config, "ul_user_cnt") == 0) {
+-		mode = MU_CTRL_UL_USER_CNT;
+-		wpa_printf(MSG_ERROR, "ul_user_cnt:%d\n", (u8)atoi(value));
+-	} else if (os_strcmp(config, "dl_user_cnt") == 0) {
+-		mode = MU_CTRL_DL_USER_CNT;
+-		wpa_printf(MSG_ERROR, "dl_user_cnt:%d\n", (u8)atoi(value));
+-	} else {
+-		wpa_printf(MSG_ERROR,
+-			"Unsupported parameter %s for SET_MU", config);
+-		return -1;
++
++		if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) == 0)
++			return os_snprintf(buf, buflen, "OK\n");
++		else
++			goto fail;
+ 	}
+ 
+-	if(hostapd_drv_mu_ctrl(hapd, mode, (u8)atoi(value)) == 0) {
+-		return os_snprintf(buf, buflen, "OK\n");
++	if (hapd->iconf->muru_config == NULL)
++		hapd->iconf->muru_config = os_zalloc(sizeof(struct connac3_muru));
++
++	muru = hapd->iconf->muru_config;
++	dl = &muru->dl;
++	ul = &muru->ul;
++	comm = &muru->comm;
++
++	if (os_strncmp(config, "update", 6) == 0) {
++		ret = hostapd_drv_mu_ctrl(hapd, MU_CTRL_UPDATE);
++
++		os_free(hapd->iconf->muru_config);
++		hapd->iconf->muru_config = NULL;
++
++		if (ret)
++			goto fail;
++	} else if (os_strcmp(config, "ul_comm_user_cnt") == 0) {
++		ul->user_num = (u8)atoi(value);
++		comm->ppdu_format |= MURU_PPDU_HE_TRIG;
++		comm->sch_type |= MURU_OFDMA_SCH_TYPE_UL;
++		muru->cfg_comm |= host_to_le32(MURU_COMM_SET);
++		muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_TOTAL_USER_CNT);
++	} else if (os_strcmp(config, "dl_comm_user_cnt") == 0) {
++		dl->user_num = (u8)atoi(value);
++		comm->ppdu_format |= MURU_PPDU_HE_MU;
++		comm->sch_type |= MURU_OFDMA_SCH_TYPE_DL;
++		muru->cfg_comm |= host_to_le32(MURU_COMM_SET);
++		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_TOTAL_USER_CNT);
++	} else if (os_strcmp(config, "dl_comm_bw") == 0) {
++		dl->bw = (u8)atoi(value);
++		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_BW);
++	} else if (os_strcmp(config, "ul_comm_bw") == 0) {
++		ul->bw = (u8)atoi(value);
++		muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_BW);
++	} else if (os_strcmp(config, "dl_user_ru_alloc") == 0) {
++		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
++		cnt = hostapd_parse_argument_helper(value, &val);
++		if (cnt == -1)
++			goto fail;
++		if (cnt != (dl->user_num * 2))
++			goto para_fail;
++		for (i = 0; i < dl->user_num; i++) {
++			dl->usr[i].ru_alloc_seg = (val[2 * i] & 0x1);
++			dl->usr[i].ru_allo_ps160 = ((val[2 * i] & 0x2) >> 1);
++			dl->usr[i].ru_idx = val[(2 * i) + 1];
++		}
++		os_free(val);
++		muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_RU_ALLOC);
++	} else if (os_strcmp(config, "ul_user_ru_alloc") == 0) {
++		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
++		cnt = hostapd_parse_argument_helper(value, &val);
++		if (cnt == -1)
++			goto fail;
++		if (cnt != (ul->user_num * 2))
++			goto para_fail;
++		for (i = 0; i < ul->user_num; i++) {
++			ul->usr[i].ru_alloc_seg = (val[2 * i] & 0x1);
++			ul->usr[i].ru_allo_ps160 = ((val[2 * i] & 0x2) >> 1);
++			ul->usr[i].ru_idx = val[(2 * i) + 1];
++		}
++		os_free(val);
++		muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_RU_ALLOC);
++	} else if (os_strcmp(config, "dl_user_mcs") == 0) {
++		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
++		cnt = hostapd_parse_argument_helper(value, &val);
++		if (cnt == -1)
++			goto fail;
++		if (cnt != dl->user_num)
++			goto para_fail;
++		for (i = 0; i < cnt; i++)
++			dl->usr[i].mcs = (u8) val[i];
++		os_free(val);
++		muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_MCS);
++	} else if (os_strcmp(config, "ul_user_mcs") == 0) {
++		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
++		cnt = hostapd_parse_argument_helper(value, &val);
++		if (cnt == -1)
++			goto fail;
++		if (cnt != ul->user_num)
++			goto para_fail;
++		for (i = 0; i < cnt; i++)
++			ul->usr[i].mcs = (u8) val[i];
++		os_free(val);
++		muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_MCS);
++	} else if (os_strcmp(config, "dl_user_cod") == 0) {
++		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
++		cnt = hostapd_parse_argument_helper(value, &val);
++		if (cnt == -1)
++			goto fail;
++		if (cnt != dl->user_num)
++			goto para_fail;
++		for (i = 0; i < cnt; i++)
++			dl->usr[i].ldpc = (u8) val[i];
++		os_free(val);
++		muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_COD);
++	} else if (os_strcmp(config, "ul_user_cod") == 0) {
++		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
++		cnt = hostapd_parse_argument_helper(value, &val);
++		if (cnt == -1)
++			goto fail;
++		if (cnt != ul->user_num)
++			goto para_fail;
++		for (i = 0; i < cnt; i++)
++			ul->usr[i].ldpc = (u8) val[i];
++		os_free(val);
++		muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_COD);
++	} else if (os_strcmp(config, "ul_user_ssAlloc_raru") == 0) {
++		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
++		cnt = hostapd_parse_argument_helper(value, &val);
++		if (cnt == -1)
++			goto fail;
++		if (cnt != ul->user_num)
++			goto para_fail;
++		for (i = 0; i < cnt; i++)
++			ul->usr[i].nss = (u8) val[i];
++		os_free(val);
++		muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_NSS);
++	} else if (os_strcmp(config, "dl_comm_gi") == 0) {
++		dl->gi = (u8)atoi(value);
++		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_GI);
++	} else if (os_strcmp(config, "dl_comm_ltf") == 0) {
++		dl->ltf = (u8)atoi(value);
++		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_LTF);
++	} else if (os_strcmp(config, "ul_comm_gi_ltf") == 0) {
++		ul->gi_ltf = (u8)atoi(value);
++		muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_GILTF);
++	} else if (os_strcmp(config, "dl_comm_ack_policy") == 0) {
++		dl->ack_policy = (u8)atoi(value);
++		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_ACK_PLY);
++	} else if (os_strcmp(config, "dl_comm_toneplan") == 0) {
++		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_BW);
++		cnt = hostapd_parse_argument_helper(value, &val);
++		if (cnt == -1)
++			goto fail;
++		i = pow(2, dl->bw);
++		if (cnt != i)
++			goto para_fail;
++		for (i = 0; i < cnt; i++)
++			dl->ru[i] = host_to_le16(val[i]);
++		os_free(val);
++		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_TONE_PLAN);
++	} else if (os_strcmp(config, "global_comm_band") == 0) {
++		comm->band = (u8)atoi(value);
++		muru->cfg_comm |= host_to_le32(MURU_COMM_BAND);
+ 	} else {
+-		return -1;
++		wpa_printf(MSG_ERROR,
++			   "Unsupported parameter %s for SET_MU", config);
++		goto fail;
+ 	}
++
++	return os_snprintf(buf, buflen, "OK\n");
++
++para_fail:
++	os_free(val);
++	wpa_printf(MSG_ERROR, "Incorrect input number\n");
++fail:
++	return os_snprintf(buf, buflen, "FAIL\n");
+ }
+ 
+ 
+@@ -5290,8 +5475,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
+ 							  reply_size);
+ 	} else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
+-		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply,
+-							  reply_size);
++		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply, reply_size);
+ 	} else if (os_strncmp(buf, "GET_MU", 6) == 0) {
+ 		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
+ 	} else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
+@@ -5317,6 +5501,14 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 	} else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
+ 		reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
+ 							reply, reply_size);
++	} else if (os_strncmp(buf, "set_muru_manual_config=", 23) == 0) {
++		// Replace first ':' with a single space ' '
++		char *pos = buf + 23;
++
++		pos = os_strchr(pos, ':');
++		if (pos)
++			*pos = ' ';
++		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 23, reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index f90f4d554..0c51d2ab9 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1344,6 +1344,7 @@ struct hostapd_config {
+ 	u8 ibf_enable;
+ 	u8 dfs_detect_mode;
+ 	u8 amsdu;
++	void *muru_config;
+ };
+ 
+ enum three_wire_mode {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 496fd4263..ccd0cb939 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1299,11 +1299,11 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
+ 	return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
+ }
+ 
+-int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val)
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode)
+ {
+ 	if (!hapd->driver || !hapd->driver->mu_ctrl)
+ 		return 0;
+-	return hapd->driver->mu_ctrl(hapd->drv_priv, mode, val);
++	return hapd->driver->mu_ctrl(hapd->drv_priv, mode, hapd->iconf);
+ }
+ 
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index a949e168d..659154f56 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -156,7 +156,7 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
+ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
+ 					  const int *threshold);
+ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+-int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val);
++int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode);
+ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 9e1ba6a21..f06687064 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2763,7 +2763,7 @@ dfs_offload:
+ 	if (hostapd_drv_configure_edcca_threshold(hapd,
+ 						  hapd->iconf->edcca_threshold) < 0)
+ 		goto fail;
+-	if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF, hapd->iconf->mu_onoff) < 0)
++	if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) < 0)
+ 		goto fail;
+ 	if (hostapd_drv_three_wire_ctrl(hapd) < 0)
+ 		goto fail;
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 34238f098..f0abcb6b1 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -202,8 +202,11 @@ enum mtk_vendor_attr_mu_ctrl {
+ 
+ 	MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
+ 	MTK_VENDOR_ATTR_MU_CTRL_DUMP,
+-	MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE,
+-	MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL,
++	/**
++	 * The above attrs are also used by connac 2. It is best not to modify the
++	 * above data structure.
++	 */
++	MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
+@@ -278,8 +281,163 @@ struct amnt_resp_data {
+ };
+ 
+ enum {
++	MU_CTRL_UPDATE,
+ 	MU_CTRL_ONOFF,
+-	MU_CTRL_DL_USER_CNT,
+-	MU_CTRL_UL_USER_CNT,
+ };
++
++struct connac3_muru_comm {
++	u8 pda_pol;
++	u8 band;
++	u8 spe_idx;
++	u8 proc_type;
++
++	le16 mlo_ctrl;
++	u8 sch_type;
++	u8 ppdu_format;
++	u8 ac;
++	u8 _rsv[3];
++};
++
++struct connac3_muru_dl {
++	u8 user_num;
++	u8 tx_mode;
++	u8 bw;
++	u8 gi;
++
++	u8 ltf;
++	u8 mcs;
++	u8 dcm;
++	u8 cmprs;
++
++	le16 ru[16];
++
++	u8 c26[2];
++	u8 ack_policy;
++	u8 tx_power;
++
++	le16 mu_ppdu_duration;
++	u8 agc_disp_order;
++	u8 _rsv1;
++
++	u8 agc_disp_pol;
++	u8 agc_disp_ratio;
++	le16 agc_disp_linkMFG;
++
++	le16 prmbl_punc_bmp;
++	u8 _rsv2[2];
++
++	struct {
++		le16 wlan_idx;
++		u8 ru_alloc_seg;
++		u8 ru_idx;
++		u8 ldpc;
++		u8 nss;
++		u8 mcs;
++		u8 mu_group_idx;
++		u8 vht_groud_id;
++		u8 vht_up;
++		u8 he_start_stream;
++		u8 he_mu_spatial;
++		le16 tx_power_alpha;
++		u8 ack_policy;
++		u8 ru_allo_ps160;
++	} usr[16];
++};
++
++struct connac3_muru_ul {
++	u8 user_num;
++	u8 tx_mode;
++
++	u8 ba_type;
++	u8 _rsv;
++
++	u8 bw;
++	u8 gi_ltf;
++	le16 ul_len;
++
++	le16 trig_cnt;
++	u8 pad;
++	u8 trig_type;
++
++	le16 trig_intv;
++	u8 trig_ta[ETH_ALEN];
++	le16 ul_ru[16];
++
++	u8 c26[2];
++	le16 agc_disp_linkMFG;
++
++	u8 agc_disp_mu_len;
++	u8 agc_disp_pol;
++	u8 agc_disp_ratio;
++	u8 agc_disp_pu_idx;
++
++	struct {
++		le16 wlan_idx;
++		u8 ru_alloc_seg;
++		u8 ru_idx;
++		u8 ldpc;
++		u8 nss;
++		u8 mcs;
++		u8 target_rssi;
++		le32 trig_pkt_size;
++		u8 ru_allo_ps160;
++		u8 _rsv2[3];
++	} usr[16];
++};
++
++struct connac3_muru_dbg {
++	/* HE TB RX Debug */
++	le32 rx_hetb_nonsf_en_bitmap;
++	le32 rx_hetb_cfg[2];
++};
++
++struct connac3_muru {
++	le32 cfg_comm;
++	le32 cfg_dl;
++	le32 cfg_ul;
++	le32 cfg_dbg;
++
++	struct connac3_muru_comm comm;
++	struct connac3_muru_dl dl;
++	struct connac3_muru_ul ul;
++	struct connac3_muru_dbg dbg;
++};
++
++#define MURU_OFDMA_SCH_TYPE_DL	BIT(0)
++#define MURU_OFDMA_SCH_TYPE_UL	BIT(1)
++#define MURU_PPDU_HE_TRIG	BIT(2)
++#define MURU_PPDU_HE_MU		BIT(3)
++
++/* Common Config */
++#define MURU_COMM_PPDU_FMT	BIT(0)
++#define MURU_COMM_BAND		BIT(2)
++#define MURU_COMM_WMM		BIT(3)
++#define MURU_COMM_SPE_IDX	BIT(4)
++#define MURU_COMM_SET		(MURU_COMM_PPDU_FMT | MURU_COMM_BAND | \
++				 MURU_COMM_WMM | MURU_COMM_SPE_IDX)
++
++/* DL Common config */
++#define MURU_FIXED_DL_BW		BIT(0)
++#define MURU_FIXED_DL_GI		BIT(1)
++#define MURU_FIXED_DL_TONE_PLAN		BIT(3)
++#define MURU_FIXED_DL_TOTAL_USER_CNT	BIT(4)
++#define MURU_FIXED_DL_LTF		BIT(5)
++#define MURU_FIXED_DL_ACK_PLY		BIT(9)
++
++/* DL Per User Config */
++#define MURU_FIXED_USER_DL_COD		BIT(17)
++#define MURU_FIXED_USER_DL_MCS		BIT(18)
++#define MURU_FIXED_USER_DL_RU_ALLOC	BIT(20)
++
++/* UL Common Config */
++#define MURU_FIXED_UL_TOTAL_USER_CNT	BIT(4)
++#define MURU_FIXED_UL_BW		BIT(5)
++#define MURU_FIXED_UL_GILTF		BIT(6)
++
++/* UL Per User Config */
++#define MURU_FIXED_USER_UL_COD		BIT(18)
++#define MURU_FIXED_USER_UL_MCS		BIT(19)
++#define MURU_FIXED_USER_UL_NSS		BIT(20)
++#define MURU_FIXED_USER_UL_RU_ALLOC	BIT(21)
++
+ #endif /* MTK_VENDOR_H */
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index c8910739e..6fc79f659 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5266,7 +5266,7 @@ struct wpa_driver_ops {
+ 	 * @priv: Private driver interface data
+ 	 *
+ 	 */
+-	 int (*mu_ctrl)(void *priv, u8 mode, u8 val);
++	 int (*mu_ctrl)(void *priv, u8 mode, void *config);
+ 	 int (*mu_dump)(void *priv, u8 *mu_onoff);
+ 
+ 	/**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index da1e6a8ed..8a8f8abe8 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14007,12 +14007,13 @@ fail:
+ 
+ 
+ #ifdef CONFIG_IEEE80211AX
+-static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
++static int nl80211_mu_ctrl(void *priv, u8 mode, void *config)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+ 	struct nl_msg *msg;
+ 	struct nlattr *data;
++	struct hostapd_config *cfg = config;
+ 	int ret = -ENOBUFS;
+ 
+ 	if (!drv->mtk_mu_vendor_cmd_avail) {
+@@ -14029,17 +14030,16 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
+ 
+ 	switch (mode) {
+ 	case MU_CTRL_ONOFF:
+-			if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, val))
+-				goto fail;
++		if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff))
++			goto fail;
+ 		break;
+-	case MU_CTRL_UL_USER_CNT:
+-	case MU_CTRL_DL_USER_CNT:
+-			if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE, mode) ||
+-			    nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL, val))
+-				goto fail;
++	case MU_CTRL_UPDATE:
++		if (nla_put(msg, MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
++			    sizeof(struct connac3_muru), cfg->muru_config))
++			goto fail;
+ 		break;
+ 	default:
+-		wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode !");
++		wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode %u!", mode);
+ 		ret = -EINVAL;
+ 		goto fail;
+ 	}
+@@ -14047,9 +14047,8 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
+ 	nla_nest_end(msg, data);
+ 
+ 	ret = send_and_recv_cmd(drv, msg);
+-	if(ret){
++	if (ret)
+ 		wpa_printf(MSG_ERROR, "Failed to set mu_ctrl. ret=%d (%s)", ret, strerror(-ret));
+-	}
+ 	return ret;
+ 
+ fail:
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0035-mtk-hostapd-Add-HE-capabilities-check.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0035-mtk-hostapd-Add-HE-capabilities-check.patch
new file mode 100644
index 0000000..780cce4
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0035-mtk-hostapd-Add-HE-capabilities-check.patch
@@ -0,0 +1,49 @@
+From 4e8197027a7b86f6cf5f1d2d9095e30281693319 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Fri, 9 Jun 2023 09:03:05 +0800
+Subject: [PATCH 035/126] mtk: hostapd: Add HE capabilities check
+
+---
+ src/ap/hw_features.c | 26 ++++++++++++++++++++++++++
+ 1 file changed, 26 insertions(+)
+
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index 400c50988..2a2832cd4 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -722,6 +722,32 @@ static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
+ #ifdef CONFIG_IEEE80211AX
+ static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface)
+ {
++	struct hostapd_hw_modes *mode = iface->current_mode;
++	struct he_capabilities *he_cap = &mode->he_capab[IEEE80211_MODE_AP];
++	struct hostapd_config *conf = iface->conf;
++
++#define HE_CAP_CHECK(hw_cap, field, phy_idx, cfg_cap)					\
++	do {									\
++		if (cfg_cap && !(hw_cap[phy_idx] & field)) {	\
++			wpa_printf(MSG_ERROR, "Driver does not support configured" \
++				     " HE capability [%s]", #field);		\
++			return 0;						\
++		}								\
++	} while (0)
++
++	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_LDPC_CODING_IN_PAYLOAD,
++		     HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX,
++		     conf->he_phy_capab.he_ldpc);
++	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMER_CAPAB,
++		     HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX,
++		     conf->he_phy_capab.he_su_beamformer);
++	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMEE_CAPAB,
++		     HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX,
++		     conf->he_phy_capab.he_su_beamformee);
++	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_MU_BEAMFORMER_CAPAB,
++		     HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX,
++		     conf->he_phy_capab.he_mu_beamformer);
++
+ 	return 1;
+ }
+ #endif /* CONFIG_IEEE80211AX */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0035-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0035-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch
deleted file mode 100644
index 596fdd3..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0035-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch
+++ /dev/null
@@ -1,247 +0,0 @@
-From 588292b2fac44452523a27ece07b85fbd0f41c5d Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 2 Sep 2022 01:03:23 +0800
-Subject: [PATCH 035/104] mtk: hostapd: Add three wire PTA ctrl hostapd vendor
- command
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- hostapd/config_file.c             |  4 ++++
- src/ap/ap_config.c                |  1 +
- src/ap/ap_config.h                | 13 ++++++++++++
- src/ap/ap_drv_ops.c               | 11 +++++++++++
- src/ap/ap_drv_ops.h               |  1 +
- src/ap/hostapd.c                  |  2 ++
- src/common/mtk_vendor.h           | 16 +++++++++++++++
- src/drivers/driver.h              |  8 ++++++++
- src/drivers/driver_nl80211.c      | 33 +++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |  1 +
- src/drivers/driver_nl80211_capa.c |  3 +++
- 11 files changed, 93 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 637c2df9f..3d9923692 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5391,6 +5391,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 			return 1;
- 		}
- 		conf->edcca_compensation = (s8) val;
-+	} else if (os_strcmp(buf, "three_wire_enable") == 0) {
-+		u8 en = atoi(pos);
-+
-+		conf->three_wire_enable = en;
- 	} else {
- 		wpa_printf(MSG_ERROR,
- 			   "Line %d: unknown configuration item '%s'",
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 9b3ef0b5b..79fd3a24b 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -306,6 +306,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- 
- 	conf->edcca_enable = EDCCA_MODE_AUTO;
- 	conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
-+	conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
- 
- 	hostapd_set_and_check_bw320_offset(conf, 0);
- 
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index f7dbbbec3..d1bbd238c 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1286,6 +1286,19 @@ struct hostapd_config {
- 	u8 edcca_enable;
- 	s8 edcca_compensation;
- 	int *edcca_threshold;
-+	u8 three_wire_enable;
-+};
-+
-+enum three_wire_mode {
-+	THREE_WIRE_MODE_DISABLE,
-+	THREE_WIRE_MODE_EXT0_ENABLE,
-+	THREE_WIRE_MODE_EXT1_ENABLE,
-+	THREE_WIRE_MODE_ALL_ENABLE,
-+
-+	/* keep last */
-+	NUM_THREE_WIRE_MODE,
-+	THREE_WIRE_MODE_MAX =
-+		NUM_THREE_WIRE_MODE - 1
- };
- 
- enum edcca_mode {
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 897ed6af8..587b8f37f 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1283,3 +1283,14 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
- 		return 0;
- 	return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
- }
-+
-+int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
-+{
-+	if (!hapd->driver || !hapd->driver->three_wire_ctrl)
-+		return 0;
-+	if (hapd->iconf->three_wire_enable > THREE_WIRE_MODE_MAX) {
-+		wpa_printf(MSG_INFO, "Invalid value for three wire enable\n");
-+		return 0;
-+	}
-+	return hapd->driver->three_wire_ctrl(hapd->drv_priv, hapd->iconf->three_wire_enable);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 5ab20cc41..7448e7954 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -155,6 +155,7 @@ int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
- int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
- int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
-+int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index d29b51fc5..5ceb49962 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2701,6 +2701,8 @@ dfs_offload:
- 		goto fail;
- 	if (hostapd_drv_mu_ctrl(hapd) < 0)
- 		goto fail;
-+	if (hostapd_drv_three_wire_ctrl(hapd) < 0)
-+		goto fail;
- 
- 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- 		   iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 60bc4cd4c..99ecbaf71 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -13,6 +13,7 @@ enum mtk_nl80211_vendor_subcmds {
- 	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
- 	MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
- 	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
-+	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8
- };
- 
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -58,6 +59,21 @@ static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
- 	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
- };
- 
-+enum mtk_vendor_attr_3wire_ctrl {
-+	MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_3WIRE_CTRL_MODE,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL,
-+	MTK_VENDOR_ATTR_3WIRE_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
-+};
-+
-+static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
-+	[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
-+};
-+
- enum mtk_vendor_attr_csi_ctrl {
- 	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index df7ce5ab9..dec4336a4 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5237,6 +5237,14 @@ struct wpa_driver_ops {
- 	 */
- 	 int (*mu_ctrl)(void *priv, u8 mu_onoff);
- 	 int (*mu_dump)(void *priv, u8 *mu_onoff);
-+
-+	/**
-+	 * three_wire_ctrl - set three_wire_ctrl mode
-+	 * @priv: Private driver interface data
-+	 * @three_wire_enable: three_wire_ctrl mode
-+	 *
-+	 */
-+	 int (*three_wire_ctrl)(void *priv, u8 three_wire_enable);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index c234eb029..c9899e492 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -14357,6 +14357,38 @@ static int nl80211_get_edcca(void *priv, const u8 mode, u8 *value)
- 	return ret;
- }
- 
-+static int nl80211_enable_three_wire(void *priv, const u8 three_wire_enable)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	/* Prepare nl80211 cmd */
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_3wire_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting three wire control");
-+		return 0;
-+	}
-+
-+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL) ||
-+	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_3WIRE_CTRL_MODE, three_wire_enable)) {
-+		nlmsg_free(msg);
-+		return -ENOBUFS;
-+	}
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_cmd(drv, msg);
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to enable three wire. ret=%d (%s) ",
-+			   ret, strerror(-ret));
-+	}
-+	return ret;
-+}
- 
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
-@@ -14524,4 +14556,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.configure_edcca_enable = nl80211_configure_edcca_enable,
- 	.configure_edcca_threshold = nl80211_configure_edcca_threshold,
- 	.get_edcca = nl80211_get_edcca,
-+	.three_wire_ctrl = nl80211_enable_three_wire,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index f99bba9e1..5de6ca6f0 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -202,6 +202,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int qca_ap_allowed_freqs:1;
- 	unsigned int mtk_edcca_vendor_cmd_avail:1;
- 	unsigned int mtk_mu_vendor_cmd_avail:1;
-+	unsigned int mtk_3wire_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 9c0a47971..fddcf8349 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1147,6 +1147,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_MU_CTRL :
- 					drv->mtk_mu_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL :
-+					drv->mtk_3wire_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0036-mtk-hostapd-Add-hostapd-iBF-control.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0036-mtk-hostapd-Add-hostapd-iBF-control.patch
deleted file mode 100644
index 56149a4..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0036-mtk-hostapd-Add-hostapd-iBF-control.patch
+++ /dev/null
@@ -1,431 +0,0 @@
-From 235a6041bde6ce30da6e631b901260dec5fadcda Mon Sep 17 00:00:00 2001
-From: mtk27835 <shurong.wen@mediatek.com>
-Date: Wed, 7 Sep 2022 14:41:51 -0700
-Subject: [PATCH 036/104] mtk: hostapd: Add hostapd iBF control
-
-Signed-off-by: mtk27835 <shurong.wen@mediatek.com>
----
- hostapd/config_file.c             |   3 +
- hostapd/ctrl_iface.c              |  26 +++++++
- hostapd/hostapd_cli.c             |   9 +++
- src/ap/ap_config.c                |   1 +
- src/ap/ap_config.h                |   2 +
- src/ap/ap_drv_ops.c               |  14 ++++
- src/ap/ap_drv_ops.h               |   2 +
- src/ap/hostapd.c                  |   2 +
- src/common/mtk_vendor.h           |  35 +++++++++-
- src/drivers/driver.h              |  19 ++++++
- src/drivers/driver_nl80211.c      | 108 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |   1 +
- src/drivers/driver_nl80211_capa.c |   3 +
- 13 files changed, 224 insertions(+), 1 deletion(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 3d9923692..247d68811 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5395,6 +5395,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		u8 en = atoi(pos);
- 
- 		conf->three_wire_enable = en;
-+	} else if (os_strcmp(buf, "ibf_enable") == 0) { /*ibf setting is per device*/
-+		int val = atoi(pos);
-+		conf->ibf_enable = !!val;
- 	} else {
- 		wpa_printf(MSG_ERROR,
- 			   "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 3a79a1284..10bbce341 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4110,6 +4110,30 @@ hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
- }
- 
- 
-+static int
-+hostapd_ctrl_iface_get_ibf(struct hostapd_data *hapd, char *buf,
-+					 size_t buflen)
-+{
-+	u8 ibf_enable;
-+	int ret;
-+	char *pos, *end;
-+
-+	pos = buf;
-+	end = buf + buflen;
-+
-+	if (hostapd_drv_ibf_dump(hapd, &ibf_enable) == 0) {
-+		hapd->iconf->ibf_enable = ibf_enable;
-+		ret = os_snprintf(pos, end - pos, "ibf_enable: %u\n",
-+			  ibf_enable);
-+	}
-+
-+	if (os_snprintf_error(end - pos, ret))
-+		return 0;
-+
-+	return ret;
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -4721,6 +4745,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 							  reply_size);
- 	} else if (os_strncmp(buf, "GET_MU", 6) == 0) {
- 		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
-+	} else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
-+		reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index da9dabd6f..276ca578c 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1666,6 +1666,13 @@ static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
- #endif /* ANDROID */
- 
- 
-+static int hostapd_cli_cmd_get_ibf(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "GET_IBF", 0, NULL, NULL);
-+}
-+
-+
- struct hostapd_cli_cmd {
- 	const char *cmd;
- 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
-@@ -1889,6 +1896,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- #endif /* ANDROID */
- 	{ "inband_discovery", hostapd_cli_cmd_inband_discovery, NULL,
-           "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
-+	{ "get_ibf", hostapd_cli_cmd_get_ibf, NULL,
-+	  " = show iBF state (enabled/disabled)"},
- 	{ NULL, NULL, NULL, NULL }
- };
- 
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 79fd3a24b..04e263167 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -307,6 +307,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- 	conf->edcca_enable = EDCCA_MODE_AUTO;
- 	conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
- 	conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
-+	conf->ibf_enable = IBF_DEFAULT_ENABLE;
- 
- 	hostapd_set_and_check_bw320_offset(conf, 0);
- 
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index d1bbd238c..5f084796d 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1287,6 +1287,7 @@ struct hostapd_config {
- 	s8 edcca_compensation;
- 	int *edcca_threshold;
- 	u8 three_wire_enable;
-+	u8 ibf_enable;
- };
- 
- enum three_wire_mode {
-@@ -1450,6 +1451,7 @@ hostapd_set_and_check_bw320_offset(struct hostapd_config *conf,
- #endif /* CONFIG_IEEE80211BE */
- }
- 
-+#define IBF_DEFAULT_ENABLE 0
- 
- int hostapd_mac_comp(const void *a, const void *b);
- struct hostapd_config * hostapd_config_defaults(void);
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 587b8f37f..3cace58e5 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1294,3 +1294,17 @@ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
- 	}
- 	return hapd->driver->three_wire_ctrl(hapd->drv_priv, hapd->iconf->three_wire_enable);
- }
-+
-+int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd)
-+{
-+	if (!hapd->driver || !hapd->driver->ibf_ctrl)
-+		return 0;
-+	return hapd->driver->ibf_ctrl(hapd->drv_priv, hapd->iconf->ibf_enable);
-+}
-+
-+int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable)
-+{
-+	if (!hapd->driver || !hapd->driver->ibf_dump)
-+		return 0;
-+	return hapd->driver->ibf_dump(hapd->drv_priv, ibf_enable);
-+}
-\ No newline at end of file
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 7448e7954..0886acb2d 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -156,6 +156,8 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
- int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
- int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 5ceb49962..1d941683f 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2703,6 +2703,8 @@ dfs_offload:
- 		goto fail;
- 	if (hostapd_drv_three_wire_ctrl(hapd) < 0)
- 		goto fail;
-+	if (hostapd_drv_ibf_ctrl(hapd) < 0)
-+		goto fail;
- 
- 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- 		   iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 99ecbaf71..9811f266e 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -13,7 +13,8 @@ enum mtk_nl80211_vendor_subcmds {
- 	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
- 	MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL= 0xc6,
- 	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
--	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8
-+	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
-+	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
- };
- 
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -207,6 +208,38 @@ enum mtk_vendor_attr_mu_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
- };
- 
-+enum mtk_vendor_attr_ibf_ctrl {
-+	MTK_VENDOR_ATTR_IBF_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_IBF_CTRL_ENABLE,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_IBF_CTRL,
-+	MTK_VENDOR_ATTR_IBF_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_IBF_CTRL - 1
-+};
-+
-+enum mtk_vendor_attr_ibf_dump {
-+	MTK_VENDOR_ATTR_IBF_DUMP_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_IBF_DUMP_ENABLE,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_IBF_DUMP,
-+	MTK_VENDOR_ATTR_IBF_DUMP_MAX =
-+		NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
-+};
-+
-+static struct nla_policy
-+ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
-+	[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
-+};
-+
-+static struct nla_policy
-+ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
-+	[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
-+};
-+
- 
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index dec4336a4..f5cff646e 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -181,6 +181,11 @@ struct hostapd_channel_data {
- 	 * mu onoff=<val> (bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0))
- 	 */
- 	u8 mu_onoff;
-+
-+	/**
-+	 * ibf_enable=<val>
-+	 */
-+	u8 ibf_enable;
- };
- 
- #define HE_MAC_CAPAB_0		0
-@@ -5245,6 +5250,20 @@ struct wpa_driver_ops {
- 	 *
- 	 */
- 	 int (*three_wire_ctrl)(void *priv, u8 three_wire_enable);
-+
-+	/**
-+	 * ibf_ctrl - ctrl disable/enable for ibf
-+	 * @priv: Private driver interface data
-+	 *
-+	 */
-+	int (*ibf_ctrl)(void *priv, u8 ibf_enable);
-+
-+	/**
-+	 * ibf_dump - dump ibf
-+	 * @priv: Private driver interface data
-+	 *
-+	 */
-+	int (*ibf_dump)(void *priv, u8 *ibf_enable);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index c9899e492..c17052f22 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -14390,6 +14390,112 @@ static int nl80211_enable_three_wire(void *priv, const u8 three_wire_enable)
- 	return ret;
- }
- 
-+static int nl80211_ibf_enable(void *priv, u8 ibf_enable)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_ibf_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting ibf control");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+	if (!data)
-+		goto fail;
-+
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_IBF_CTRL_ENABLE, ibf_enable);
-+
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_cmd(drv, msg);
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to set ibf_enable. ret=%d (%s)", ret, strerror(-ret));
-+	}
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
-+static int ibf_dump_handler(struct nl_msg *msg, void *arg)
-+{
-+	u8 *ibf_enable = (u8 *) arg;
-+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_IBF_DUMP_MAX + 1];
-+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+	struct nlattr *nl_vend, *attr;
-+
-+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+			genlmsg_attrlen(gnlh, 0), NULL);
-+
-+	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+	if (!nl_vend)
-+		return NL_SKIP;
-+
-+	nla_parse(tb_vendor, MTK_VENDOR_ATTR_IBF_DUMP_MAX,
-+			nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+	attr = tb_vendor[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE];
-+	if (!attr) {
-+		wpa_printf(MSG_ERROR, "nl80211: cannot find MTK_VENDOR_ATTR_IBF_DUMP_ENABLE");
-+		return NL_SKIP;
-+	}
-+
-+	*ibf_enable = nla_get_u8(attr);
-+
-+	return NL_SKIP;
-+}
-+
-+static int
-+nl80211_ibf_dump(void *priv, u8 *ibf_enable)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
-+	if (!data)
-+		goto fail;
-+
-+	nla_nest_end(msg, data);
-+
-+	ret = send_and_recv_resp(drv, msg, ibf_dump_handler, ibf_enable);
-+
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to dump ibf_enable. ret=%d (%s)", ret, strerror(-ret));
-+	}
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
- 	.desc = "Linux nl80211/cfg80211",
-@@ -14557,4 +14663,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.configure_edcca_threshold = nl80211_configure_edcca_threshold,
- 	.get_edcca = nl80211_get_edcca,
- 	.three_wire_ctrl = nl80211_enable_three_wire,
-+	.ibf_ctrl = nl80211_ibf_enable,
-+	.ibf_dump = nl80211_ibf_dump,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 5de6ca6f0..1432eeda8 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -203,6 +203,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int mtk_edcca_vendor_cmd_avail:1;
- 	unsigned int mtk_mu_vendor_cmd_avail:1;
- 	unsigned int mtk_3wire_vendor_cmd_avail:1;
-+	unsigned int mtk_ibf_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index fddcf8349..615af2eb2 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1150,6 +1150,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL :
- 					drv->mtk_3wire_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL:
-+					drv->mtk_ibf_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0036-mtk-hostapd-Fix-background-channel-overlapping-opera.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0036-mtk-hostapd-Fix-background-channel-overlapping-opera.patch
new file mode 100644
index 0000000..a25e243
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0036-mtk-hostapd-Fix-background-channel-overlapping-opera.patch
@@ -0,0 +1,64 @@
+From ce1043355ec94008f97f8ee1401a9a7a2050fb88 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 036/126] mtk: hostapd: 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 9677b26ea..754c471f5 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -815,6 +815,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)
+ {
+@@ -1142,6 +1156,8 @@ static void hostapd_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,
+@@ -1386,6 +1402,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;
+@@ -1513,6 +1530,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/0037-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0037-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch
deleted file mode 100644
index 03513eb..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0037-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-From dce3106e1c43076ab410af89f817e15cee7959a3 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Thu, 22 Sep 2022 16:08:09 +0800
-Subject: [PATCH 037/104] mtk: hostapd: Do not include HE capab IE if
- associated sta's HE capab IE is invalid
-
-The parameter 'sta' passed to send_assoc_resp() might be NULL, so an
-NULL check is necessary before access the 'sta'.
-Only one such check was missed in this function, and this patch fixs it.
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/ieee802_11.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index bda61b998..d972a25f1 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -4931,7 +4931,8 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
- #endif /* CONFIG_IEEE80211AC */
- 
- #ifdef CONFIG_IEEE80211AX
--	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
-+	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax && sta &&
-+			sta->flags & WLAN_STA_HE) {
- 		p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
- 		p = hostapd_eid_he_operation(hapd, p);
- 		p = hostapd_eid_cca(hapd, p);
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0037-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0037-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch
new file mode 100644
index 0000000..050e465
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0037-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch
@@ -0,0 +1,31 @@
+From 2059027bdf239e4e5ce3382f1f286c3830059341 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 5 Jul 2023 10:47:20 +0800
+Subject: [PATCH 037/126] mtk: hostapd: Fix hostapd_dfs_start_cac log
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 754c471f5..66ccb0057 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1685,9 +1685,11 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ 	/* TODO: How to check CAC time for ETSI weather channels? */
+ 	iface->dfs_cac_ms = 60000;
+ 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
+-		"freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
++		"freq=%d chan=%d chan_offset=%d width=%s seg0=%d "
+ 		"seg1=%d cac_time=%ds%s",
+-		freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
++		freq, (freq - 5000) / 5, chan_offset,
++		channel_width_to_string(chan_width),
++		(cf1 - 5000) / 5, cf2 ? (cf2 - 5000) / 5 : 0,
+ 		iface->dfs_cac_ms / 1000,
+ 		hostapd_dfs_is_background_event(iface, freq) ?
+ 		" (background)" : "");
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0038-mtk-hostapd-Add-DFS-detection-mode.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0038-mtk-hostapd-Add-DFS-detection-mode.patch
deleted file mode 100644
index c540b5e..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0038-mtk-hostapd-Add-DFS-detection-mode.patch
+++ /dev/null
@@ -1,136 +0,0 @@
-From 9d180d46527a6a53824227c2b7c6e4e37bf3acfb Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Feb 2023 14:55:49 +0800
-Subject: [PATCH 038/104] mtk: hostapd: Add DFS detection mode
-
-Add DFS detection mode for testing radar detection rate.
-If DFS detection mode is on, AP will not switch channels when receiving
-a radar signal.
-This detection mode also supports background chain.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- hostapd/config_file.c |  4 ++++
- hostapd/ctrl_iface.c  | 23 +++++++++++++++++++++++
- src/ap/ap_config.h    | 13 +++++++++++++
- src/ap/dfs.c          | 10 ++++++++++
- 4 files changed, 50 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 247d68811..40ade89c0 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5398,6 +5398,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 	} else if (os_strcmp(buf, "ibf_enable") == 0) { /*ibf setting is per device*/
- 		int val = atoi(pos);
- 		conf->ibf_enable = !!val;
-+	} else if (os_strcmp(buf, "dfs_detect_mode") == 0) { /*bypass channel switch*/
-+		u8 en = strtol(pos, NULL, 10);
-+
-+		conf->dfs_detect_mode = en;
- 	} else {
- 		wpa_printf(MSG_ERROR,
- 			   "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 10bbce341..71a87459c 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4134,6 +4134,26 @@ hostapd_ctrl_iface_get_ibf(struct hostapd_data *hapd, char *buf,
- }
- 
- 
-+static int
-+hostapd_ctrl_iface_set_dfs_detect_mode(struct hostapd_data *hapd, char *value,
-+				       char *buf, size_t buflen)
-+{
-+	u8 dfs_detect_mode;
-+
-+	if (!value)
-+		return -1;
-+
-+	dfs_detect_mode = strtol(value, NULL, 10);
-+	if (dfs_detect_mode > DFS_DETECT_MODE_MAX) {
-+		wpa_printf(MSG_ERROR, "Invalid value for dfs detect mode");
-+		return -1;
-+	}
-+	hapd->iconf->dfs_detect_mode = dfs_detect_mode;
-+
-+	return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -4747,6 +4767,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
- 	} else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
- 		reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
-+	} else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
-+		reply_len = hostapd_ctrl_iface_set_dfs_detect_mode(hapd, buf + 16,
-+								   reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 5f084796d..7607c63e1 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1288,6 +1288,7 @@ struct hostapd_config {
- 	int *edcca_threshold;
- 	u8 three_wire_enable;
- 	u8 ibf_enable;
-+	u8 dfs_detect_mode;
- };
- 
- enum three_wire_mode {
-@@ -1302,6 +1303,18 @@ enum three_wire_mode {
- 		NUM_THREE_WIRE_MODE - 1
- };
- 
-+enum dfs_mode {
-+	DFS_DETECT_MODE_DISABLE,
-+	DFS_DETECT_MODE_AP_ENABLE,
-+	DFS_DETECT_MODE_BACKGROUND_ENABLE,
-+	DFS_DETECT_MODE_ALL_ENABLE,
-+
-+	/* keep last */
-+	NUM_DFS_DETECT_MODE,
-+	DFS_DETECT_MODE_MAX =
-+		NUM_DFS_DETECT_MODE - 1
-+};
-+
- enum edcca_mode {
- 	EDCCA_MODE_FORCE_DISABLE = 0,
- 	EDCCA_MODE_AUTO = 1,
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index d14fad136..1df4de6b8 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1336,6 +1336,11 @@ hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
- 		   __func__, iface->radar_background.cac_started ? "yes" : "no",
- 		   hostapd_csa_in_progress(iface) ? "yes" : "no");
- 
-+	/* Skip channel switch when background dfs detect mode is on */
-+	if (iface->conf->dfs_detect_mode == DFS_DETECT_MODE_BACKGROUND_ENABLE ||
-+	    iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
-+		return 0;
-+
- 	/* Check if CSA in progress */
- 	if (hostapd_csa_in_progress(iface))
- 		return 0;
-@@ -1384,6 +1389,11 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
- 		   __func__, iface->cac_started ? "yes" : "no",
- 		   hostapd_csa_in_progress(iface) ? "yes" : "no");
- 
-+	/* Skip channel switch when dfs detect mode is on */
-+	if (iface->conf->dfs_detect_mode == DFS_DETECT_MODE_AP_ENABLE ||
-+	    iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
-+		return 0;
-+
- 	/* Check if CSA in progress */
- 	if (hostapd_csa_in_progress(iface))
- 		return 0;
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0038-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0038-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch
new file mode 100644
index 0000000..af4f9ac
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0038-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch
@@ -0,0 +1,62 @@
+From 2f22f947b2719f96d44f3aca158f19228c4a7dee Mon Sep 17 00:00:00 2001
+From: Michael Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 13 Jul 2023 13:14:26 +0800
+Subject: [PATCH 038/126] mtk: hostapd: Check the bridge after ioctl
+ SIOCBRADDIF failed
+
+If ioctl returns EBUSY on command SIOCBRADDIF, the interface might
+already be bridged by others, and linux_br_add_if should not indicate an
+error in the case.
+
+This patch checks whether the interface is correctly brigded when ioctl
+returns EBUSY.
+
+Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+---
+ src/drivers/linux_ioctl.c | 16 +++++++++++++++-
+ 1 file changed, 15 insertions(+), 1 deletion(-)
+
+diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c
+index 29abc0c59..73d27825d 100644
+--- a/src/drivers/linux_ioctl.c
++++ b/src/drivers/linux_ioctl.c
+@@ -150,7 +150,8 @@ int linux_br_del(int sock, const char *brname)
+ int linux_br_add_if(int sock, const char *brname, const char *ifname)
+ {
+ 	struct ifreq ifr;
+-	int ifindex;
++	int ifindex, ret;
++	char in_br[IFNAMSIZ];
+ 
+ 	ifindex = if_nametoindex(ifname);
+ 	if (ifindex == 0)
+@@ -165,6 +166,17 @@ int linux_br_add_if(int sock, const char *brname, const char *ifname)
+ 
+ 		wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge "
+ 			   "%s: %s", ifname, brname, strerror(errno));
++
++		/* If ioctl returns -EBUSY when adding interface into bridge,
++		 * the interface might already be added by netifd, so here we
++		 * check whether the interface is currently on the right
++		 * bridge. */
++		if(errno == EBUSY && linux_br_get(in_br, ifname) == 0 &&
++	           os_strcmp(in_br, brname) == 0)
++			ret = 0;
++		else
++			ret = -1;
++
+ 		errno = saved_errno;
+ 
+ 		/* If ioctl() returns EBUSY when adding an interface into the
+@@ -175,6 +187,8 @@ int linux_br_add_if(int sock, const char *brname, const char *ifname)
+ 		if (errno != EBUSY || linux_br_get(in_br, ifname) != 0 ||
+ 		    os_strcmp(in_br, brname) != 0)
+ 			return -1;
++
++		return ret;
+ 	}
+ 
+ 	return 0;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0039-mtk-hostapd-Add-DFS-offchan-channel-switch.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0039-mtk-hostapd-Add-DFS-offchan-channel-switch.patch
deleted file mode 100644
index a37e541..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0039-mtk-hostapd-Add-DFS-offchan-channel-switch.patch
+++ /dev/null
@@ -1,192 +0,0 @@
-From 7f0f977e36718a7a827f7d78e6ab578d53aa630b Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Feb 2023 14:56:55 +0800
-Subject: [PATCH 039/104] mtk: hostapd: Add DFS offchan channel switch
-
-Add DFS background chain channel switch command for testing purpose.
-This feature is implemented via hostapd_cli command.
-Command format:
-hostapd_cli -i <interface> raw SET_OFFCHAN_CTRL chan=<dfs_channel>
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- hostapd/ctrl_iface.c | 72 ++++++++++++++++++++++++++++++++++++++++++++
- src/ap/dfs.c         | 25 ++++++---------
- src/ap/dfs.h         | 15 +++++++++
- 3 files changed, 96 insertions(+), 16 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 71a87459c..68dcc7982 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4154,6 +4154,76 @@ hostapd_ctrl_iface_set_dfs_detect_mode(struct hostapd_data *hapd, char *value,
- }
- 
- 
-+static int
-+hostapd_ctrl_iface_set_offchan_ctrl(struct hostapd_data *hapd, char *cmd,
-+				    char *buf, size_t buflen)
-+{
-+	struct hostapd_iface *iface = hapd->iface;
-+	char *pos, *param;
-+	enum hostapd_hw_mode hw_mode;
-+	bool chan_found = false;
-+	int i, num_available_chandefs, channel, chan_width, sec = 0;
-+	int sec_chan_idx_80p80 = -1;
-+	u8 oper_centr_freq_seg0_idx, oper_centr_freq_seg1_idx;
-+	struct hostapd_channel_data *chan;
-+	enum dfs_channel_type type = DFS_NO_CAC_YET;
-+
-+	param = os_strchr(cmd, ' ');
-+	if (!param)
-+		return -1;
-+	*param++ = '\0';
-+
-+	pos = os_strstr(param, "chan=");
-+	if (pos)
-+		channel = strtol(pos + 5, NULL, 10);
-+	else
-+		return -1;
-+
-+	num_available_chandefs = dfs_find_channel(iface, NULL, 0, type);
-+	for (i = 0; i < num_available_chandefs; i++) {
-+		dfs_find_channel(iface, &chan, i, type);
-+		if (chan->chan == channel) {
-+			chan_found = true;
-+			break;
-+		}
-+	}
-+
-+	if (!chan_found)
-+		return -1;
-+
-+	if (iface->conf->secondary_channel)
-+		sec = 1;
-+
-+	dfs_adjust_center_freq(iface, chan,
-+			       sec,
-+			       sec_chan_idx_80p80,
-+			       &oper_centr_freq_seg0_idx,
-+			       &oper_centr_freq_seg1_idx);
-+
-+	if (hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
-+				  chan->freq, chan->chan,
-+				  iface->conf->ieee80211n,
-+				  iface->conf->ieee80211ac,
-+				  iface->conf->ieee80211ax,
-+				  iface->conf->ieee80211be,
-+				  sec, hostapd_get_oper_chwidth(iface->conf),
-+				  oper_centr_freq_seg0_idx,
-+				  oper_centr_freq_seg1_idx, true)) {
-+		wpa_printf(MSG_ERROR, "DFS failed to start CAC offchannel");
-+		iface->radar_background.channel = -1;
-+		return -1;
-+	}
-+
-+	iface->radar_background.channel = chan->chan;
-+	iface->radar_background.freq = chan->freq;
-+	iface->radar_background.secondary_channel = sec;
-+	iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
-+	iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
-+
-+	return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -4770,6 +4840,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 	} else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
- 		reply_len = hostapd_ctrl_iface_set_dfs_detect_mode(hapd, buf + 16,
- 								   reply, reply_size);
-+	} else if (os_strncmp(buf, "SET_OFFCHAN_CTRL", 16) == 0) {
-+		reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 1df4de6b8..ece27d070 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -19,13 +19,6 @@
- #include "dfs.h"
- #include "crypto/crypto.h"
- 
--
--enum dfs_channel_type {
--	DFS_ANY_CHANNEL,
--	DFS_AVAILABLE, /* non-radar or radar-available */
--	DFS_NO_CAC_YET, /* radar-not-yet-available */
--};
--
- static struct hostapd_channel_data *
- dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
- 			u8 *oper_centr_freq_seg0_idx,
-@@ -238,9 +231,9 @@ static int is_in_chanlist(struct hostapd_iface *iface,
-  *  - hapd->vht/he_oper_centr_freq_seg0_idx
-  *  - hapd->vht/he_oper_centr_freq_seg1_idx
-  */
--static int dfs_find_channel(struct hostapd_iface *iface,
--			    struct hostapd_channel_data **ret_chan,
--			    int idx, enum dfs_channel_type type)
-+int dfs_find_channel(struct hostapd_iface *iface,
-+		     struct hostapd_channel_data **ret_chan,
-+		     int idx, enum dfs_channel_type type)
- {
- 	struct hostapd_hw_modes *mode;
- 	struct hostapd_channel_data *chan;
-@@ -300,12 +293,12 @@ static int dfs_find_channel(struct hostapd_iface *iface,
- }
- 
- 
--static void dfs_adjust_center_freq(struct hostapd_iface *iface,
--				   struct hostapd_channel_data *chan,
--				   int secondary_channel,
--				   int sec_chan_idx_80p80,
--				   u8 *oper_centr_freq_seg0_idx,
--				   u8 *oper_centr_freq_seg1_idx)
-+void dfs_adjust_center_freq(struct hostapd_iface *iface,
-+			    struct hostapd_channel_data *chan,
-+			    int secondary_channel,
-+			    int sec_chan_idx_80p80,
-+			    u8 *oper_centr_freq_seg0_idx,
-+			    u8 *oper_centr_freq_seg1_idx)
- {
- 	if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax)
- 		return;
-diff --git a/src/ap/dfs.h b/src/ap/dfs.h
-index 606c1b393..c2556d2d9 100644
---- a/src/ap/dfs.h
-+++ b/src/ap/dfs.h
-@@ -9,6 +9,12 @@
- #ifndef DFS_H
- #define DFS_H
- 
-+enum dfs_channel_type {
-+	DFS_ANY_CHANNEL,
-+	DFS_AVAILABLE, /* non-radar or radar-available */
-+	DFS_NO_CAC_YET, /* radar-not-yet-available */
-+};
-+
- int hostapd_handle_dfs(struct hostapd_iface *iface);
- 
- int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
-@@ -32,5 +38,14 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
- int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
- int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
- 			   int center_freq);
-+int dfs_find_channel(struct hostapd_iface *iface,
-+		     struct hostapd_channel_data **ret_chan,
-+		     int idx, enum dfs_channel_type type);
-+void dfs_adjust_center_freq(struct hostapd_iface *iface,
-+			    struct hostapd_channel_data *chan,
-+			    int secondary_channel,
-+			    int sec_chan_idx_80p80,
-+			    u8 *oper_centr_freq_seg0_idx,
-+			    u8 *oper_centr_freq_seg1_idx);
- 
- #endif /* DFS_H */
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0039-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0039-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch
new file mode 100644
index 0000000..42c9a65
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0039-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch
@@ -0,0 +1,30 @@
+From c8bd35df569715a82cddb0df4a5f894fe535be84 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Fri, 14 Jul 2023 17:19:13 +0800
+Subject: [PATCH 039/126] mtk: hostapd: Update parameter_set_count in MU EDCA
+ IE
+
+without this patch, MU EDCA Parameter update count not equal to
+WMM Parameter set count.
+
+---
+ src/ap/ieee802_11_he.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
+index 3c6ee72fe..3b6b2041c 100644
+--- a/src/ap/ieee802_11_he.c
++++ b/src/ap/ieee802_11_he.c
+@@ -317,6 +317,9 @@ u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
+ 	edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
+ 	os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
+ 
++	if (hapd->conf->wmm_enabled)
++		edca->he_qos_info = hapd->parameter_set_count % 0xf;
++
+ 	wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
+ 		    pos, sizeof(*edca));
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0040-mtk-hostapd-Add-amsdu-set-get-ctrl.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0040-mtk-hostapd-Add-amsdu-set-get-ctrl.patch
deleted file mode 100644
index ead86d5..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0040-mtk-hostapd-Add-amsdu-set-get-ctrl.patch
+++ /dev/null
@@ -1,400 +0,0 @@
-From 8291551130127914cbe3b5346fbd2edc89437df8 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 16 Dec 2022 03:57:11 +0800
-Subject: [PATCH 040/104] mtk: hostapd: Add amsdu set get ctrl
-
----
- hostapd/config_file.c             |   9 +++
- hostapd/ctrl_iface.c              |  26 +++++++
- hostapd/hostapd_cli.c             |   9 +++
- src/ap/ap_config.c                |   1 +
- src/ap/ap_config.h                |   1 +
- src/ap/ap_drv_ops.c               |  14 ++++
- src/ap/ap_drv_ops.h               |   2 +
- src/ap/hostapd.c                  |   2 +
- src/common/mtk_vendor.h           |  17 ++++-
- src/drivers/driver.h              |   9 +++
- src/drivers/driver_nl80211.c      | 114 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |   1 +
- src/drivers/driver_nl80211_capa.c |   3 +
- 13 files changed, 207 insertions(+), 1 deletion(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 40ade89c0..7695ab196 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5402,6 +5402,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		u8 en = strtol(pos, NULL, 10);
- 
- 		conf->dfs_detect_mode = en;
-+	} else if (os_strcmp(buf, "amsdu") == 0) {
-+		int val = atoi(pos);
-+		if (val < 0 || val > 1) {
-+			wpa_printf(MSG_ERROR,
-+					 "Line %d: invalid amsdu value",
-+					 line);
-+			return 1;
-+		}
-+		conf->amsdu = val;
- 	} else {
- 		wpa_printf(MSG_ERROR,
- 			   "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 68dcc7982..5f6278ecd 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4224,6 +4224,30 @@ hostapd_ctrl_iface_set_offchan_ctrl(struct hostapd_data *hapd, char *cmd,
- }
- 
- 
-+static int
-+hostapd_ctrl_iface_get_amsdu(struct hostapd_data *hapd, char *buf,
-+					 size_t buflen)
-+{
-+	u8 amsdu;
-+	int ret;
-+	char *pos, *end;
-+
-+	pos = buf;
-+	end = buf + buflen;
-+
-+	if (hostapd_drv_amsdu_dump(hapd, &amsdu) == 0) {
-+		hapd->iconf->amsdu = amsdu;
-+		ret = os_snprintf(pos, end - pos, "[hostapd_cli] AMSDU: %u\n",
-+					hapd->iconf->amsdu);
-+	}
-+
-+	if (os_snprintf_error(end - pos, ret))
-+		return 0;
-+
-+	return ret;
-+}
-+
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -4842,6 +4866,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 								   reply, reply_size);
- 	} else if (os_strncmp(buf, "SET_OFFCHAN_CTRL", 16) == 0) {
- 		reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
-+	} else if (os_strncmp(buf, "GET_AMSDU", 9) == 0) {
-+		reply_len = hostapd_ctrl_iface_get_amsdu(hapd, reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 276ca578c..847f867ab 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1673,6 +1673,13 @@ static int hostapd_cli_cmd_get_ibf(struct wpa_ctrl *ctrl, int argc,
- }
- 
- 
-+static int hostapd_cli_cmd_get_amsdu(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "GET_AMSDU", 0, NULL, NULL);
-+}
-+
-+
- struct hostapd_cli_cmd {
- 	const char *cmd;
- 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
-@@ -1898,6 +1905,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
-           "<tx type(0/1/2)> <interval> = runtime set inband discovery" },
- 	{ "get_ibf", hostapd_cli_cmd_get_ibf, NULL,
- 	  " = show iBF state (enabled/disabled)"},
-+	{ "get_amsdu", hostapd_cli_cmd_get_amsdu, NULL,
-+		" = show AMSDU state"},
- 	{ NULL, NULL, NULL, NULL }
- };
- 
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 04e263167..2420a251e 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -308,6 +308,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- 	conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
- 	conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
- 	conf->ibf_enable = IBF_DEFAULT_ENABLE;
-+	conf->amsdu = 1;
- 
- 	hostapd_set_and_check_bw320_offset(conf, 0);
- 
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 7607c63e1..123e12c8f 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1289,6 +1289,7 @@ struct hostapd_config {
- 	u8 three_wire_enable;
- 	u8 ibf_enable;
- 	u8 dfs_detect_mode;
-+	u8 amsdu;
- };
- 
- enum three_wire_mode {
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 3cace58e5..23228a8d2 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1307,4 +1307,18 @@ int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable)
- 	if (!hapd->driver || !hapd->driver->ibf_dump)
- 		return 0;
- 	return hapd->driver->ibf_dump(hapd->drv_priv, ibf_enable);
-+}
-+
-+int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd)
-+{
-+	if (!hapd->driver || !hapd->driver->amsdu_ctrl)
-+		return 0;
-+	return hapd->driver->amsdu_ctrl(hapd->drv_priv, hapd->iconf->amsdu);
-+}
-+
-+int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu)
-+{
-+	if (!hapd->driver || !hapd->driver->amsdu_dump)
-+		return 0;
-+	return hapd->driver->amsdu_dump(hapd->drv_priv, amsdu);
- }
-\ No newline at end of file
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 0886acb2d..f3a044557 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -158,6 +158,8 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
- int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
-+int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 1d941683f..a5b683676 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2705,6 +2705,8 @@ dfs_offload:
- 		goto fail;
- 	if (hostapd_drv_ibf_ctrl(hapd) < 0)
- 		goto fail;
-+	if (hostapd_drv_amsdu_ctrl(hapd) < 0)
-+		goto fail;
- 
- 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- 		   iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 9811f266e..7b4d7c11a 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -170,7 +170,6 @@ enum mtk_vendor_attr_wireless_ctrl {
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
--	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
- 
-@@ -180,6 +179,22 @@ enum mtk_vendor_attr_wireless_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
- };
- 
-+enum mtk_vendor_attr_wireless_dump {
-+	MTK_VENDOR_ATTR_WIRELESS_DUMP_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP,
-+	MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX =
-+		NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
-+};
-+
-+static const struct nla_policy
-+wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
-+	[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
-+};
-+
- enum mtk_vendor_attr_rfeature_ctrl {
- 	MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index f5cff646e..6eeb9c22e 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5264,6 +5264,15 @@ struct wpa_driver_ops {
- 	 *
- 	 */
- 	int (*ibf_dump)(void *priv, u8 *ibf_enable);
-+
-+	/**
-+	 * amsdu_ctrl - enable/disable amsdu
-+	 * amsdu_dump - get current amsdu status
-+	 * @priv: Private driver interface data
-+	 *
-+	 */
-+	int (*amsdu_ctrl)(void *priv, u8 amsdu);
-+	int (*amsdu_dump)(void *priv, u8 *amsdu);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index c17052f22..eca2ff077 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -14496,6 +14496,118 @@ fail:
- 	return -ENOBUFS;
- }
- 
-+static int nl80211_enable_amsdu(void *priv, u8 amsdu)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_wireless_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting ap wireless control");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+	if (!data)
-+		goto fail;
-+
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU, amsdu);
-+
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_cmd(drv, msg);
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to set amsdu. ret=%d (%s)", ret, strerror(-ret));
-+	}
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
-+static int dump_amsdu_handler(struct nl_msg *msg, void *arg)
-+{
-+	u8 *amsdu = (u8 *) arg;
-+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX + 1];
-+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+	struct nlattr *nl_vend, *attr_amsdu;
-+
-+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+			genlmsg_attrlen(gnlh, 0), NULL);
-+
-+	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+	if (!nl_vend)
-+		return NL_SKIP;
-+
-+	nla_parse(tb_vendor, MTK_VENDOR_ATTR_WIRELESS_DUMP_MAX,
-+			nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+	attr_amsdu = tb_vendor[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU];
-+	if (!attr_amsdu ){
-+		wpa_printf(MSG_ERROR, "nl80211: cannot find vendor attributes");
-+		return NL_SKIP;
-+	}
-+
-+	*amsdu = nla_get_u8(attr_amsdu);
-+
-+	return NL_SKIP;
-+}
-+
-+static int
-+nl80211_dump_amsdu(void *priv, u8 *amsdu)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_wireless_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+				 "nl80211: Driver does not support ap_wireless control");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+	if (!data)
-+		goto fail;
-+
-+	nla_nest_end(msg, data);
-+
-+	ret = send_and_recv_resp(drv, msg, dump_amsdu_handler, amsdu);
-+
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to dump amsdu. ret=%d (%s)", ret, strerror(-ret));
-+	}
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
- 	.desc = "Linux nl80211/cfg80211",
-@@ -14665,4 +14777,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.three_wire_ctrl = nl80211_enable_three_wire,
- 	.ibf_ctrl = nl80211_ibf_enable,
- 	.ibf_dump = nl80211_ibf_dump,
-+	.amsdu_ctrl = nl80211_enable_amsdu,
-+	.amsdu_dump = nl80211_dump_amsdu,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 1432eeda8..5aa813e26 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -204,6 +204,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int mtk_mu_vendor_cmd_avail:1;
- 	unsigned int mtk_3wire_vendor_cmd_avail:1;
- 	unsigned int mtk_ibf_vendor_cmd_avail:1;
-+	unsigned int mtk_wireless_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 615af2eb2..474d4e273 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1153,6 +1153,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL:
- 					drv->mtk_ibf_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL:
-+					drv->mtk_wireless_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0040-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0040-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch
new file mode 100644
index 0000000..86c4b44
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0040-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch
@@ -0,0 +1,36 @@
+From 30e4604148273074fee5daeff21a6a7029c19832 Mon Sep 17 00:00:00 2001
+From: mtk20656 <chank.chen@mediatek.com>
+Date: Mon, 24 Jul 2023 11:30:27 +0800
+Subject: [PATCH 040/126] mtk: hostapd: add extension IE list for non-inherit
+ IE in mbssid
+
+Certain clients do not scan all non tx profiles due to absence of
+element ID extension list which is mandatory field in non inheritance
+IE. Non inheritance Element ID is followed by extension element ID.
+Length is expected to be mentioned. Currently we do not support any
+extension element and hence filling length as 0.
+
+Signed-off-by: mtk20656 <chank.chen@mediatek.com>
+---
+ src/ap/ieee802_11.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+ mode change 100644 => 100755 src/ap/ieee802_11.c
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+old mode 100644
+new mode 100755
+index 18d5b8f79..258ab7943
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -8078,7 +8078,7 @@ static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
+ 		else if (hapd->conf->xrates_supported)
+ 			ie_count++;
+ 		if (ie_count)
+-			nontx_profile_len += 4 + ie_count;
++			nontx_profile_len += 5 + ie_count;
+ 
+ 		if (len + nontx_profile_len > 255)
+ 			break;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0041-mtk-hostapd-Add-he_ldpc-configuration.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0041-mtk-hostapd-Add-he_ldpc-configuration.patch
deleted file mode 100644
index 5dac957..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0041-mtk-hostapd-Add-he_ldpc-configuration.patch
+++ /dev/null
@@ -1,102 +0,0 @@
-From 0fa801d52f2e29c87aef757efc690aa5b2474f1b Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Thu, 12 Jan 2023 15:18:19 +0800
-Subject: [PATCH 041/104] mtk: hostapd: Add he_ldpc configuration
-
----
- hostapd/config_file.c        | 2 ++
- hostapd/hostapd.conf         | 5 +++++
- src/ap/ap_config.c           | 1 +
- src/ap/ap_config.h           | 1 +
- src/ap/ieee802_11_he.c       | 7 +++++++
- src/common/ieee802_11_defs.h | 3 +++
- 6 files changed, 19 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 7695ab196..dadc8f108 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3908,6 +3908,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		conf->he_phy_capab.he_su_beamformee = atoi(pos);
- 	} else if (os_strcmp(buf, "he_mu_beamformer") == 0) {
- 		conf->he_phy_capab.he_mu_beamformer = atoi(pos);
-+	} else if (os_strcmp(buf, "he_ldpc") == 0) {
-+		conf->he_phy_capab.he_ldpc = atoi(pos);
- 	} else if (os_strcmp(buf, "he_bss_color") == 0) {
- 		conf->he_op.he_bss_color = atoi(pos) & 0x3f;
- 		conf->he_op.he_bss_color_disabled = 0;
-diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
-index 0d10998af..f988b17b2 100644
---- a/hostapd/hostapd.conf
-+++ b/hostapd/hostapd.conf
-@@ -833,6 +833,11 @@ wmm_ac_vo_acm=0
- # 1 = supported
- #he_mu_beamformer=1
- 
-+#he_ldpc: HE LDPC support
-+# 0 = not supported
-+# 1 = supported (default)
-+#he_ldpc=1
-+
- # he_bss_color: BSS color (1-63)
- #he_bss_color=1
- 
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 2420a251e..ba1b2a7a3 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -273,6 +273,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- #endif /* CONFIG_ACS */
- 
- #ifdef CONFIG_IEEE80211AX
-+	conf->he_phy_capab.he_ldpc = 1;
- 	conf->he_op.he_rts_threshold = HE_OPERATION_RTS_THRESHOLD_MASK >>
- 		HE_OPERATION_RTS_THRESHOLD_OFFSET;
- 	/* Set default basic MCS/NSS set to single stream MCS 0-7 */
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 123e12c8f..d995b8d9c 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -985,6 +985,7 @@ struct hostapd_bss_config {
-  * struct he_phy_capabilities_info - HE PHY capabilities
-  */
- struct he_phy_capabilities_info {
-+	bool he_ldpc;
- 	bool he_su_beamformer;
- 	bool he_su_beamformee;
- 	bool he_mu_beamformer;
-diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
-index a2deda6c4..3c6ee72fe 100644
---- a/src/ap/ieee802_11_he.c
-+++ b/src/ap/ieee802_11_he.c
-@@ -139,6 +139,13 @@ u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
- 		os_memcpy(&cap->optional[mcs_nss_size],
- 			  mode->he_capab[opmode].ppet,  ppet_size);
- 
-+	if (hapd->iface->conf->he_phy_capab.he_ldpc)
-+		cap->he_phy_capab_info[HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX] |=
-+			HE_PHYCAP_LDPC_CODING_IN_PAYLOAD;
-+	else
-+		cap->he_phy_capab_info[HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX] &=
-+			~HE_PHYCAP_LDPC_CODING_IN_PAYLOAD;
-+
- 	if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
- 		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
- 			HE_PHYCAP_SU_BEAMFORMER_CAPAB;
-diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
-index 7a1da3252..a289c2d87 100644
---- a/src/common/ieee802_11_defs.h
-+++ b/src/common/ieee802_11_defs.h
-@@ -2452,6 +2452,9 @@ struct ieee80211_spatial_reuse {
- #define HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G	((u8) BIT(3))
- #define HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G	((u8) BIT(4))
- 
-+#define HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX	1
-+#define HE_PHYCAP_LDPC_CODING_IN_PAYLOAD	((u8) BIT(5))
-+
- #define HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX	3
- #define HE_PHYCAP_SU_BEAMFORMER_CAPAB		((u8) BIT(7))
- #define HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX	4
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0041-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0041-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch
new file mode 100644
index 0000000..d3daf72
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0041-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch
@@ -0,0 +1,44 @@
+From f8f314f2ce3c4e8e82cc98bbd404c18424b13dd7 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 041/126] mtk: hostapd: 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 f06687064..4549aecb2 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4658,6 +4658,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/0042-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0042-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch
new file mode 100644
index 0000000..1a28908
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0042-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch
@@ -0,0 +1,45 @@
+From d29370b7ec78ba410d0877441206849e197b1609 Mon Sep 17 00:00:00 2001
+From: mtk23510 <rudra.shahi@mediatek.com>
+Date: Fri, 26 May 2023 14:52:35 +0800
+Subject: [PATCH 042/126] mtk: hostapd: Add support for gtk rekeying in hostapd
+ cli
+
+Signed-off-by: mtk23510 <rudra.shahi@mediatek.com>
+---
+ hostapd/hostapd_cli.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index dba805e8a..e43d515d5 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1309,6 +1309,15 @@ static int hostapd_cli_cmd_stop_ap(struct wpa_ctrl *ctrl, int argc,
+ }
+ 
+ 
++#ifdef CONFIG_TESTING_OPTIONS
++static int hostapd_cli_cmd_rekey_gtk(struct wpa_ctrl *ctrl, int argc,
++				      char *argv[])
++{
++	return wpa_ctrl_command(ctrl, "REKEY_GTK");
++}
++#endif
++
++
+ static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ {
+ 	char cmd[256];
+@@ -1861,6 +1870,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 	  "= update Beacon frame contents\n"},
+ 	{ "stop_ap", hostapd_cli_cmd_stop_ap, NULL,
+ 	  "= stop AP\n"},
++#ifdef CONFIG_TESTING_OPTIONS
++	{ "rekey_gtk", hostapd_cli_cmd_rekey_gtk, NULL,
++	  "= rekey gtk\n"},
++#endif
+ 	{ "erp_flush", hostapd_cli_cmd_erp_flush, NULL,
+ 	  "= drop all ERP keys"},
+ 	{ "log_level", hostapd_cli_cmd_log_level, NULL,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0042-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0042-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch
deleted file mode 100644
index 8676424..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0042-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From d342772023b344ce09a22eaeff4307e5bfe52d7c Mon Sep 17 00:00:00 2001
-From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
-Date: Tue, 24 Jan 2023 19:06:44 +0800
-Subject: [PATCH 042/104] mtk: hostapd: Add vendor command attribute for RTS BW
- signaling.
-
-Signed-off-by: himanshu.goyal <himanshu.goyal@mediatek.com>
----
- src/common/mtk_vendor.h | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 7b4d7c11a..ace993bc8 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -172,6 +172,7 @@ enum mtk_vendor_attr_wireless_ctrl {
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
- 
- 	/* keep last */
- 	NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0043-mtk-hostapd-6G-band-does-not-require-DFS.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0043-mtk-hostapd-6G-band-does-not-require-DFS.patch
deleted file mode 100644
index 304e444..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0043-mtk-hostapd-6G-band-does-not-require-DFS.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-From 931c93d5d19249a0b4e4efbc5957f537578dfd81 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Mon, 13 Feb 2023 11:03:53 +0800
-Subject: [PATCH 043/104] mtk: hostapd: 6G band does not require DFS
-
----
- src/ap/dfs.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index ece27d070..44f3a2cb1 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1525,6 +1525,7 @@ int hostapd_is_dfs_required(struct hostapd_iface *iface)
- 	if ((!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
- 	     !iface->conf->ieee80211h) ||
- 	    !iface->current_mode ||
-+	    is_6ghz_freq(iface->freq) ||
- 	    iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
- 		return 0;
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0043-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0043-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch
new file mode 100644
index 0000000..2a3c271
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0043-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch
@@ -0,0 +1,48 @@
+From 8d8843872efa3b2a694e85b3125435fc6a1abc09 Mon Sep 17 00:00:00 2001
+From: Michael Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 11 Jul 2023 14:17:43 +0800
+Subject: [PATCH 043/126] mtk: hostapd: Set WMM and TX queue parameters for
+ wpa_supplicant
+
+Since most of the time, wpa_supplicant will be used to setup an STA
+interface, it's default WMM and TX queue parameters should be set for
+STA.
+
+Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/config.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
+index dc4b0636a..d2c05e352 100644
+--- a/wpa_supplicant/config.c
++++ b/wpa_supplicant/config.c
+@@ -4722,19 +4722,19 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
+ 	const struct hostapd_wmm_ac_params ac_bk =
+ 		{ aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
+ 	const struct hostapd_wmm_ac_params ac_be =
+-		{ aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
++		{ aCWmin, aCWmin + 2, 3, 0, 0 }; /* best effort traffic */
+ 	const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
+-		{ aCWmin - 1, aCWmin, 2, 3008 / 32, 0 };
++		{ aCWmin - 1, aCWmin, 1, 3008 / 32, 0 };
+ 	const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
+-		{ aCWmin - 2, aCWmin - 1, 2, 1504 / 32, 0 };
++		{ aCWmin - 2, aCWmin - 1, 1, 1504 / 32, 0 };
+ 	const struct hostapd_tx_queue_params txq_bk =
+ 		{ 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
+ 	const struct hostapd_tx_queue_params txq_be =
+-		{ 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0 };
++		{ 3, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
+ 	const struct hostapd_tx_queue_params txq_vi =
+-		{ 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30 };
++		{ 2, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30 };
+ 	const struct hostapd_tx_queue_params txq_vo =
+-		{ 1, (ecw2cw(aCWmin) + 1) / 4 - 1,
++		{ 2, (ecw2cw(aCWmin) + 1) / 4 - 1,
+ 		  (ecw2cw(aCWmin) + 1) / 2 - 1, 15 };
+ 
+ #undef ecw2cw
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0044-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0044-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch
deleted file mode 100644
index 9a8d8ae..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0044-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch
+++ /dev/null
@@ -1,46 +0,0 @@
-From 79d4323faf5c14f9a4a8e0cf3219582210463206 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Feb 2023 11:01:18 +0800
-Subject: [PATCH 044/104] mtk: hostapd: Fix sending wrong VHT operation IE in
- CSA while using ZWDFS
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c | 14 +++++++++-----
- 1 file changed, 9 insertions(+), 5 deletions(-)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 44f3a2cb1..c703d2fb8 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1129,6 +1129,14 @@ static int
- hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
- {
- 	u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
-+	int ret;
-+
-+	ret = hostapd_dfs_request_channel_switch(iface, iface->radar_background.channel,
-+						 iface->radar_background.freq,
-+						 iface->radar_background.secondary_channel,
-+						 current_vht_oper_chwidth,
-+						 iface->radar_background.centr_freq_seg0_idx,
-+						 iface->radar_background.centr_freq_seg1_idx);
- 
- 	iface->conf->channel = iface->radar_background.channel;
- 	iface->freq = iface->radar_background.freq;
-@@ -1141,11 +1149,7 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
- 
- 	hostapd_dfs_update_background_chain(iface);
- 
--	return hostapd_dfs_request_channel_switch(
--		iface, iface->conf->channel, iface->freq,
--		iface->conf->secondary_channel, current_vht_oper_chwidth,
--		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
--		hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
-+	return ret;
- }
- 
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0044-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0044-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch
new file mode 100644
index 0000000..4d071a4
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0044-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch
@@ -0,0 +1,78 @@
+From 9aa3ee6838af657f932ed1e7e70420ecfae8452e Mon Sep 17 00:00:00 2001
+From: Michael Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 7 Jul 2023 17:16:11 +0800
+Subject: [PATCH 044/126] mtk: hostapd: Set STA TX queue parameters
+ configuration after association
+
+This patch adds the way for wpa_supplicant to set driver's TX queue
+parameters.
+Since STA parses and apply TX queue parameters from AP beacon's WMM IE
+during association, wpa_supplicant set driver's TX queue parameters
+after the association.
+
+Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/driver_i.h | 12 ++++++++++++
+ wpa_supplicant/events.c   | 16 ++++++++++++++++
+ 2 files changed, 28 insertions(+)
+
+diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
+index d01b52bb1..663e16053 100644
+--- a/wpa_supplicant/driver_i.h
++++ b/wpa_supplicant/driver_i.h
+@@ -321,6 +321,18 @@ static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s,
+ 	return 0;
+ }
+ 
++static inline int wpa_drv_set_tx_queue_params(struct wpa_supplicant *wpa_s,
++					      int q, int aifs, int cw_min,
++					      int cw_max, int burst_time)
++{
++	int link_id = -1;
++	if (wpa_s->driver->set_tx_queue_params)
++		return wpa_s->driver->set_tx_queue_params(wpa_s->drv_priv, q,
++							  aifs, cw_min, cw_max,
++							  burst_time, link_id);
++	return 0;
++}
++
+ static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s,
+ 				    const u8 *data, size_t data_len, int noack,
+ 				    unsigned int freq, unsigned int wait)
+diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
+index 5c08d4a19..68f4d2dbe 100644
+--- a/wpa_supplicant/events.c
++++ b/wpa_supplicant/events.c
+@@ -4187,6 +4187,20 @@ out:
+ 	return wpa_sm_set_mlo_params(wpa_s->wpa, &wpa_mlo);
+ }
+ 
++static void wpa_supplicant_tx_queue_params(struct wpa_supplicant *wpa_s){
++	struct hostapd_tx_queue_params *p;
++
++	for (int i = 0; i < NUM_TX_QUEUES; i++){
++		p = &wpa_s->conf->tx_queue[i];
++		if(wpa_drv_set_tx_queue_params(wpa_s, i, p->aifs,
++						      p->cwmin, p->cwmax,
++						      p->burst)) {
++			wpa_printf(MSG_DEBUG, "Failed to set TX queue "
++				   "parameters for queue %d.", i);
++			/* Continue anyway */
++		}
++	}
++}
+ 
+ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
+ 				       union wpa_event_data *data)
+@@ -4516,6 +4530,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
+ 
+ 	if (wpa_s->current_ssid && wpa_s->current_ssid->enable_4addr_mode)
+ 		wpa_supplicant_set_4addr_mode(wpa_s);
++
++	wpa_supplicant_tx_queue_params(wpa_s);
+ }
+ 
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0045-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0045-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch
deleted file mode 100644
index 5ef55c0..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0045-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch
+++ /dev/null
@@ -1,189 +0,0 @@
-From cc4ace547c0b0ebef084e9634f729267b6c89f08 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Feb 2023 10:51:47 +0800
-Subject: [PATCH 045/104] mtk: hostapd: Add sta-assisted DFS state update
- mechanism
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c                       | 20 ++++++++++++++++++++
- src/ap/dfs.h                       |  3 +++
- src/ap/drv_callbacks.c             | 28 ++++++++++++++++++++++++++++
- src/common/wpa_ctrl.h              |  1 +
- src/drivers/driver.h               | 14 ++++++++++++++
- src/drivers/driver_nl80211_event.c |  6 ++++++
- src/drivers/nl80211_copy.h         |  6 ++++++
- 7 files changed, 78 insertions(+)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index c703d2fb8..3e036441b 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1522,6 +1522,26 @@ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
- }
- 
- 
-+int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
-+				 int ht_enabled, int chan_offset, int chan_width,
-+				 int cf1, int cf2, u32 state)
-+{
-+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_STA_UPDATE
-+		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d state=%s",
-+		freq, ht_enabled, chan_offset, chan_width, cf1, cf2,
-+		(state == HOSTAPD_CHAN_DFS_AVAILABLE) ? "available" : "usable");
-+
-+	/* Proceed only if DFS is not offloaded to the driver */
-+	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
-+		return 0;
-+
-+	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
-+		      cf1, cf2, state);
-+
-+	return 0;
-+}
-+
-+
- int hostapd_is_dfs_required(struct hostapd_iface *iface)
- {
- 	int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res;
-diff --git a/src/ap/dfs.h b/src/ap/dfs.h
-index c2556d2d9..25ba29ca1 100644
---- a/src/ap/dfs.h
-+++ b/src/ap/dfs.h
-@@ -30,6 +30,9 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
- int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
- 			     int ht_enabled,
- 			     int chan_offset, int chan_width, int cf1, int cf2);
-+int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
-+				 int ht_enabled, int chan_offset, int chan_width,
-+				 int cf1, int cf2, u32 state);
- int hostapd_is_dfs_required(struct hostapd_iface *iface);
- int hostapd_is_dfs_chan_available(struct hostapd_iface *iface);
- int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index e8796f709..caa171474 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -2226,6 +2226,24 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
- 			      radar->cf1, radar->cf2);
- }
- 
-+static void hostapd_event_dfs_sta_cac_skipped(struct hostapd_data *hapd,
-+					      struct dfs_event *radar)
-+{
-+	wpa_printf(MSG_DEBUG, "DFS CAC skipped (by STA) on %d MHz", radar->freq);
-+	hostapd_dfs_sta_update_state(hapd->iface, radar->freq, radar->ht_enabled,
-+				     radar->chan_offset, radar->chan_width,
-+				     radar->cf1, radar->cf2, HOSTAPD_CHAN_DFS_AVAILABLE);
-+}
-+
-+static void hostapd_event_dfs_sta_cac_expired(struct hostapd_data *hapd,
-+					      struct dfs_event *radar)
-+{
-+	wpa_printf(MSG_DEBUG, "DFS CAC expired (by STA) on %d MHz", radar->freq);
-+	hostapd_dfs_sta_update_state(hapd->iface, radar->freq, radar->ht_enabled,
-+				     radar->chan_offset, radar->chan_width,
-+				     radar->cf1, radar->cf2, HOSTAPD_CHAN_DFS_USABLE);
-+}
-+
- #endif /* NEED_AP_MLME */
- 
- 
-@@ -2592,6 +2610,16 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
- 		hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
- 		hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
- 		break;
-+	case EVENT_DFS_STA_CAC_SKIPPED:
-+		if (!data)
-+			break;
-+		hostapd_event_dfs_sta_cac_skipped(hapd, &data->dfs_event);
-+		break;
-+	case EVENT_DFS_STA_CAC_EXPIRED:
-+		if (!data)
-+			break;
-+		hostapd_event_dfs_sta_cac_expired(hapd, &data->dfs_event);
-+		break;
- 	case EVENT_CHANNEL_LIST_CHANGED:
- 		/* channel list changed (regulatory?), update channel list */
- 		/* TODO: check this. hostapd_get_hw_features() initializes
-diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
-index c5bb9abd7..88ad54d6f 100644
---- a/src/common/wpa_ctrl.h
-+++ b/src/common/wpa_ctrl.h
-@@ -383,6 +383,7 @@ extern "C" {
- #define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED "
- #define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED "
- #define DFS_EVENT_PRE_CAC_EXPIRED "DFS-PRE-CAC-EXPIRED "
-+#define DFS_EVENT_STA_UPDATE "DFS-STA-UPDATE "
- 
- #define AP_CSA_FINISHED "AP-CSA-FINISHED "
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 6eeb9c22e..dbd0137ac 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5900,6 +5900,20 @@ enum wpa_event_type {
- 	 * EVENT_LINK_RECONFIG - Notification that AP links removed
- 	 */
- 	EVENT_LINK_RECONFIG,
-+
-+	/**
-+	 * EVENT_DFS_STA_CAC_SKIPPED - Notification that CAC has been skipped
-+	 *
-+	 * The channel in the notification is now marked as available.
-+	 */
-+	EVENT_DFS_STA_CAC_SKIPPED,
-+
-+	/**
-+	 * EVENT_DFS_STA_CAC_EXPIRED - Notification that CAC has expired
-+	 *
-+	 * The channel in the notification is now marked as usable.
-+	 */
-+	EVENT_DFS_STA_CAC_EXPIRED,
- };
- 
- 
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 4a12d749c..7889930a0 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -2529,6 +2529,12 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
- 	case NL80211_RADAR_CAC_STARTED:
- 		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
- 		break;
-+	case NL80211_RADAR_STA_CAC_SKIPPED:
-+		wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
-+		break;
-+	case NL80211_RADAR_STA_CAC_EXPIRED:
-+		wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_EXPIRED, &data);
-+		break;
- 	default:
- 		wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
- 			   "received", event_type);
-diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
-index dced2c49d..8917d565b 100644
---- a/src/drivers/nl80211_copy.h
-+++ b/src/drivers/nl80211_copy.h
-@@ -6699,6 +6699,10 @@ enum nl80211_smps_mode {
-  *	applicable for ETSI dfs domain where pre-CAC is valid for ever.
-  * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
-  *	should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
-+ * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
-+ *	when receiving CSA/assoc resp
-+ * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
-+ *	when STA is disconnected or leaving the channel
-  */
- enum nl80211_radar_event {
- 	NL80211_RADAR_DETECTED,
-@@ -6707,6 +6711,8 @@ enum nl80211_radar_event {
- 	NL80211_RADAR_NOP_FINISHED,
- 	NL80211_RADAR_PRE_CAC_EXPIRED,
- 	NL80211_RADAR_CAC_STARTED,
-+	NL80211_RADAR_STA_CAC_SKIPPED,
-+	NL80211_RADAR_STA_CAC_EXPIRED,
- };
- 
- /**
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0045-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0045-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch
new file mode 100644
index 0000000..41a4af7
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0045-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch
@@ -0,0 +1,27 @@
+From 7d38f0de94d325b00fecb4cd2888d0a0695446c2 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 1 Sep 2023 15:31:24 +0800
+Subject: [PATCH 045/126] mtk: hostapd: avoid color switch when beacon is not
+ set
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/hostapd.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 4549aecb2..7da5c3afa 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4815,7 +4815,7 @@ void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap)
+ {
+ 	struct os_reltime now;
+ 
+-	if (hapd->cca_in_progress)
++	if (hapd->cca_in_progress || !hapd->beacon_set_done)
+ 		return;
+ 
+ 	if (os_get_reltime(&now))
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0046-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0046-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch
new file mode 100644
index 0000000..c2a8853
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0046-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch
@@ -0,0 +1,30 @@
+From 1d5789bf2fb508516d7a650bf282a12f39eccda3 Mon Sep 17 00:00:00 2001
+From: mtk20656 <chank.chen@mediatek.com>
+Date: Wed, 13 Sep 2023 19:29:51 +0800
+Subject: [PATCH 046/126] mtk: hostapd: 6g bss connect do not consider ht
+ operation
+
+Signed-off-by: mtk20656 <chank.chen@mediatek.com>
+---
+ src/ap/ieee802_11.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+ mode change 100755 => 100644 src/ap/ieee802_11.c
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+old mode 100755
+new mode 100644
+index 258ab7943..ee698fa4a
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -5667,7 +5667,7 @@ static void handle_assoc(struct hostapd_data *hapd,
+ 			set_beacon = true;
+ 	}
+ 
+-	if (update_ht_state(hapd, sta) > 0)
++	if (!is_6ghz_op_class(hapd->iconf->op_class) && update_ht_state(hapd, sta) > 0)
+ 		set_beacon = true;
+ 
+ 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0046-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0046-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch
deleted file mode 100644
index 7d905c5..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0046-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch
+++ /dev/null
@@ -1,60 +0,0 @@
-From 21476005ab6b517c7765fec7c3d6c3383c5f44f4 Mon Sep 17 00:00:00 2001
-From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
-Date: Fri, 3 Mar 2023 12:45:42 +0800
-Subject: [PATCH 046/104] mtk: hostapd: Mark DFS channel as available for CSA.
-
----
- hostapd/ctrl_iface.c   | 10 ++++++++++
- hostapd/hostapd_cli.c  |  2 +-
- src/ap/ctrl_iface_ap.c |  1 +
- 3 files changed, 12 insertions(+), 1 deletion(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 5f6278ecd..052588da4 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -2776,6 +2776,16 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
- 		break;
- 	}
- 
-+	if (settings.freq_params.radar_background) {
-+		hostapd_dfs_sta_update_state(iface,
-+			settings.freq_params.freq,
-+			settings.freq_params.ht_enabled,
-+			settings.freq_params.sec_channel_offset,
-+			bandwidth, settings.freq_params.center_freq1,
-+			settings.freq_params.center_freq2,
-+			HOSTAPD_CHAN_DFS_AVAILABLE);
-+	}
-+
- 	if (settings.freq_params.center_freq1)
- 		dfs_range += hostapd_is_dfs_overlap(
- 			iface, bandwidth, settings.freq_params.center_freq1);
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 847f867ab..da9c0f931 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1775,7 +1775,7 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 	  "<addr> = send QoS Map Configure frame" },
- 	{ "chan_switch", hostapd_cli_cmd_chan_switch, NULL,
- 	  "<cs_count> <freq> [sec_channel_offset=] [center_freq1=]\n"
--	  "  [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n"
-+	  "  [center_freq2=] [bandwidth=] [blocktx] [ht|vht] [skip_cac]\n"
- 	  "  = initiate channel switch announcement" },
- 	{ "notify_cw_change", hostapd_cli_cmd_notify_cw_change, NULL,
- 	  "<channel_width> = 0 - 20 MHz, 1 - 40 MHz, 2 - 80 MHz, 3 - 160 MHz" },
-diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index a2f89260c..b92311e32 100644
---- a/src/ap/ctrl_iface_ap.c
-+++ b/src/ap/ctrl_iface_ap.c
-@@ -1117,6 +1117,7 @@ int hostapd_parse_csa_settings(const char *pos,
- 	settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
- 	settings->freq_params.he_enabled = !!os_strstr(pos, " he");
- 	settings->freq_params.eht_enabled = !!os_strstr(pos, " eht");
-+	settings->freq_params.radar_background = !!os_strstr(pos, " skip_cac");
- 	settings->block_tx = !!os_strstr(pos, " blocktx");
- #undef SET_CSA_SETTING
- #undef SET_CSA_SETTING_EXT
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0047-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0047-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch
new file mode 100644
index 0000000..510b699
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0047-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch
@@ -0,0 +1,96 @@
+From df41420a192985b4a8c535968dbbf0ee07cc8c73 Mon Sep 17 00:00:00 2001
+From: "fancy.liu" <fancy.liu@mediatek.com>
+Date: Sun, 8 Oct 2023 11:50:06 +0800
+Subject: [PATCH 047/126] mtk: hostapd: Add ACS chanlist info in get_config
+
+This patch is used to add ACS chanlist info displaying
+for upper layer application obtaining.
+
+Command format:
+hostapd_cli -i phy0-ap0 get_config
+
+Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 59 ++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 59 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 68b83bf6e..53924b265 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -1059,6 +1059,7 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
+ {
+ 	int ret;
+ 	char *pos, *end;
++	int i;
+ 
+ 	pos = buf;
+ 	end = buf + buflen;
+@@ -1238,6 +1239,64 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
+ 		pos += ret;
+ 	}
+ 
++	/* dump chanlist */
++	if (hapd->iface->conf->acs_ch_list.num > 0) {
++		ret = os_snprintf(pos, end - pos, "chanlist=");
++		if (os_snprintf_error(end - pos, ret))
++			return pos - buf;
++		pos += ret;
++
++		for (i = 0; i < hapd->iface->conf->acs_ch_list.num; i++) {
++			if (i > 0) {
++				ret = os_snprintf(pos, end - pos, ", ");
++				if (os_snprintf_error(end - pos, ret))
++					return pos - buf;
++				pos += ret;
++			}
++
++			ret = os_snprintf(pos, end - pos, "%d-%d",
++				hapd->iface->conf->acs_ch_list.range[i].min,
++				hapd->iface->conf->acs_ch_list.range[i].max);
++			if (os_snprintf_error(end - pos, ret))
++				return pos - buf;
++			pos += ret;
++		}
++
++		ret = os_snprintf(pos, end - pos, "\n");
++		if (os_snprintf_error(end - pos, ret))
++			return pos - buf;
++		pos += ret;
++	}
++
++	/* dump freqlist */
++	if (hapd->iface->conf->acs_freq_list.num > 0) {
++		ret = os_snprintf(pos, end - pos, "freqlist=");
++		if (os_snprintf_error(end - pos, ret))
++			return pos - buf;
++		pos += ret;
++
++		for (i = 0; i < hapd->iface->conf->acs_freq_list.num; i++) {
++			if (i > 0) {
++				ret = os_snprintf(pos, end - pos, ", ");
++				if (os_snprintf_error(end - pos, ret))
++					return pos - buf;
++				pos += ret;
++			}
++
++			ret = os_snprintf(pos, end - pos, "%d-%d",
++				hapd->iface->conf->acs_freq_list.range[i].min,
++				hapd->iface->conf->acs_freq_list.range[i].max);
++			if (os_snprintf_error(end - pos, ret))
++				return pos - buf;
++			pos += ret;
++		}
++
++		ret = os_snprintf(pos, end - pos, "\n");
++		if (os_snprintf_error(end - pos, ret))
++			return pos - buf;
++		pos += ret;
++	}
++
+ 	return pos - buf;
+ }
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0047-mtk-hostapd-Add-available-color-bitmap.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0047-mtk-hostapd-Add-available-color-bitmap.patch
deleted file mode 100644
index 9f3c55b..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0047-mtk-hostapd-Add-available-color-bitmap.patch
+++ /dev/null
@@ -1,476 +0,0 @@
-From 50ceb0f2eb9b542ab115ed79fd2d68d46e9e03a0 Mon Sep 17 00:00:00 2001
-From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
-Date: Thu, 26 Jan 2023 09:16:00 +0800
-Subject: [PATCH 047/104] mtk: hostapd: Add available color bitmap
-
-Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
----
- hostapd/ctrl_iface.c              |  74 +++++++++++
- hostapd/hostapd_cli.c             |  18 +++
- src/ap/ap_drv_ops.c               |  10 +-
- src/ap/ap_drv_ops.h               |   2 +
- src/common/mtk_vendor.h           |  11 ++
- src/drivers/driver.h              |   8 ++
- src/drivers/driver_nl80211.c      | 198 +++++++++++++++++++++++++++++-
- src/drivers/driver_nl80211.h      |   1 +
- src/drivers/driver_nl80211_capa.c |   3 +
- 9 files changed, 323 insertions(+), 2 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 052588da4..7b83bdd4f 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4257,6 +4257,76 @@ hostapd_ctrl_iface_get_amsdu(struct hostapd_data *hapd, char *buf,
- 	return ret;
- }
- 
-+static int
-+hostapd_ctrl_iface_get_bss_color(struct hostapd_data *hapd, char *buf,
-+		size_t buflen)
-+{
-+	int ret;
-+	char *pos, *end;
-+	int i;
-+
-+	pos = buf;
-+	end = buf + buflen;
-+
-+	if (hapd->iface->conf->he_op.he_bss_color_disabled)
-+		ret = os_snprintf(buf, buflen, "BSS Color disabled\n");
-+	else
-+		ret = os_snprintf(buf, buflen, "BSS Color=%u\n",
-+				  hapd->iface->conf->he_op.he_bss_color);
-+
-+	pos += ret;
-+
-+	return pos - buf;
-+}
-+
-+
-+static int
-+hostapd_ctrl_iface_get_aval_color_bmp(struct hostapd_data *hapd, char *buf,
-+		size_t buflen)
-+{
-+	int ret;
-+	char *pos, *end;
-+	int i;
-+	u64 aval_color_bmp = 0;
-+
-+	hostapd_drv_get_aval_bss_color_bmp(hapd, &aval_color_bmp);
-+	hapd->color_collision_bitmap = ~aval_color_bmp;
-+
-+	pos = buf;
-+	end = buf + buflen;
-+
-+	ret = os_snprintf(buf, buflen,
-+			"available color bitmap=0x%llx\n",
-+			aval_color_bmp);
-+	if (os_snprintf_error(end - pos, ret))
-+		return pos - buf;
-+	pos += ret;
-+
-+	for (i = 0; i < HE_OPERATION_BSS_COLOR_MAX; i++) {
-+		int bit = !!((aval_color_bmp >> i) & 1LLU);
-+
-+		if (i % 8 == 0) {
-+			ret = os_snprintf(pos, end - pos, "%2d: ", i);
-+			if (os_snprintf_error(end - pos, ret))
-+				return pos - buf;
-+			pos += ret;
-+		}
-+
-+		ret = os_snprintf(pos, end - pos, "%d ", bit);
-+		if (os_snprintf_error(end - pos, ret))
-+			return pos - buf;
-+		pos += ret;
-+
-+		if (i % 8 == 7) {
-+			ret = os_snprintf(pos, end - pos, "\n");
-+			if (os_snprintf_error(end - pos, ret))
-+				return pos - buf;
-+			pos += ret;
-+		}
-+	}
-+	return pos - buf;
-+}
-+
- 
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
-@@ -4878,6 +4948,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 		reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
- 	} else if (os_strncmp(buf, "GET_AMSDU", 9) == 0) {
- 		reply_len = hostapd_ctrl_iface_get_amsdu(hapd, reply, reply_size);
-+	} else if (os_strncmp(buf, "GET_BSS_COLOR", 13) == 0) {
-+		reply_len = hostapd_ctrl_iface_get_bss_color(hapd, reply, reply_size);
-+	} else if (os_strncmp(buf, "AVAL_COLOR_BMP", 14) == 0) {
-+		reply_len = hostapd_ctrl_iface_get_aval_color_bmp(hapd, reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index da9c0f931..865c11432 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1658,6 +1658,20 @@ static int hostapd_cli_cmd_reload_rxkhs(struct wpa_ctrl *ctrl, int argc,
- #endif /* CONFIG_IEEE80211R_AP */
- 
- 
-+static int hostapd_cli_cmd_get_bss_color(struct wpa_ctrl *ctrl, int argc,
-+					  char *argv[])
-+{
-+	return wpa_ctrl_command(ctrl, "GET_BSS_COLOR");
-+}
-+
-+
-+static int hostapd_cli_cmd_get_aval_color_bmp(struct wpa_ctrl *ctrl, int argc,
-+					  char *argv[])
-+{
-+	return wpa_ctrl_command(ctrl, "AVAL_COLOR_BMP");
-+}
-+
-+
- #ifdef ANDROID
- static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
- {
-@@ -1897,6 +1911,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 	{ "get_rxkhs", hostapd_cli_cmd_get_rxkhs, NULL,
- 	  "= get R0KHs and R1KHs" },
- #endif /* CONFIG_IEEE80211R_AP */
-+	{ "get_bss_color", hostapd_cli_cmd_get_bss_color, NULL,
-+	  "= get current BSS color" },
-+	{ "get_color_bmp", hostapd_cli_cmd_get_aval_color_bmp, NULL,
-+	  "= get available BSS color bitmap" },
- #ifdef ANDROID
- 	{ "driver", hostapd_cli_cmd_driver, NULL,
- 	  "<driver sub command> [<hex formatted data>] = send driver command data" },
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 23228a8d2..cabcd47af 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1321,4 +1321,12 @@ int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu)
- 	if (!hapd->driver || !hapd->driver->amsdu_dump)
- 		return 0;
- 	return hapd->driver->amsdu_dump(hapd->drv_priv, amsdu);
--}
-\ No newline at end of file
-+}
-+
-+int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_color_bmp)
-+{
-+	if (!hapd->driver || !hapd->driver->get_aval_color_bmp ||
-+	    hapd->iface->conf->he_op.he_bss_color_disabled)
-+		return 0;
-+	return hapd->driver->get_aval_color_bmp(hapd->drv_priv, aval_color_bmp);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index f3a044557..9da2b0049 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -160,6 +160,8 @@ int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_dump(struct hostapd_data *hapd, u8 *ibf_enable);
- int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
-+int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd,
-+				       u64 *aval_color_bmp);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index ace993bc8..e27fe69b3 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -15,6 +15,7 @@ enum mtk_nl80211_vendor_subcmds {
- 	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
- 	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
- 	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
-+	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
- };
- 
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -256,6 +257,16 @@ ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
- 	[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
- };
- 
-+enum mtk_vendor_attr_bss_color_ctrl {
-+	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL,
-+	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
-+};
- 
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index dbd0137ac..6b6317bfa 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5273,6 +5273,14 @@ struct wpa_driver_ops {
- 	 */
- 	int (*amsdu_ctrl)(void *priv, u8 amsdu);
- 	int (*amsdu_dump)(void *priv, u8 *amsdu);
-+
-+	/**
-+	 * get_aval_color_bmp - get available BSS color bitmap
-+	 * @priv: Private driver interface data
-+	 * @aval_color_bmp: available bss color bitmap
-+	 *
-+	 */
-+	int (*get_aval_color_bmp)(void *priv, u64 *aval_color_bmp);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index eca2ff077..4c98e8ab3 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13153,7 +13153,6 @@ static void nl80211_parse_btm_candidate_info(struct candidate_list *candidate,
- 		   num, MAC2STR(candidate->bssid), buf);
- }
- 
--
- static int
- nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg)
- {
-@@ -14608,6 +14607,202 @@ fail:
- 	return -ENOBUFS;
- }
- 
-+static int nl80211_get_aval_color_bmp_handler(struct nl_msg *msg, void *arg)
-+{
-+	u64 *aval_color_bmp = arg;
-+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX + 1];
-+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+	struct nlattr *nl_vend, *attr;
-+
-+	static const struct nla_policy
-+	bss_color_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL + 1] = {
-+		[MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP] = { .type = NLA_U64 },
-+	};
-+
-+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+			genlmsg_attrlen(gnlh, 0), NULL);
-+
-+	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-+	if (!nl_vend)
-+		return NL_SKIP;
-+
-+	nla_parse(tb_vendor, MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX,
-+			nla_data(nl_vend), nla_len(nl_vend), NULL);
-+
-+	*aval_color_bmp = nla_get_u64(tb_vendor[MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP]);
-+
-+	return 0;
-+}
-+
-+static int nl80211_get_aval_color_bmp(void *priv, u64 *aval_color_bmp)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *attr;
-+	int ret;
-+
-+	if (!drv->mtk_bss_color_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support BSS COLOR vendor cmd");
-+		return 0;
-+	}
-+
-+	if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL))
-+		return -ENOBUFS;
-+
-+	attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+	if (!attr) {
-+		nlmsg_free(msg);
-+		return -1;
-+	}
-+
-+	nla_nest_end(msg, attr);
-+
-+	ret = send_and_recv_resp(drv, msg, nl80211_get_aval_color_bmp_handler, aval_color_bmp);
-+
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to send BSS COLOR vendor cmd. ret=%d (%s) ",
-+			   ret, strerror(-ret));
-+	}
-+	return ret;
-+}
-+
-+static int nl80211_ap_wireless(void *priv, u8 sub_vendor_id, int value)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_wireless_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting ap wireless control");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+	if (!data)
-+		goto fail;
-+
-+	if (sub_vendor_id == MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE)
-+		nla_put_u16(msg, sub_vendor_id, (u16) value);
-+	else
-+		nla_put_u8(msg, sub_vendor_id, (u8) value);
-+
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_cmd(drv, msg);
-+	if (ret)
-+		wpa_printf(MSG_ERROR, "Failed to set ap_wireless. ret=%d (%s)", ret, strerror(-ret));
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
-+static int nl80211_ap_rfeatures(void *priv, u8 sub_vendor_id, int value)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_rfeatures_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting ap rfeatures control");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+	if (!data)
-+		goto fail;
-+
-+	nla_put_u8(msg, sub_vendor_id, (u8) value);
-+
-+	nla_nest_end(msg, data);
-+
-+	ret = send_and_recv_cmd(drv, msg);
-+	if (ret)
-+		wpa_printf(MSG_ERROR, "Failed to set rf_features. ret=%d (%s)", ret, strerror(-ret));
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
-+static int nl80211_ap_trigtype(void *priv, u8 enable, u8 type)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data, *data2;
-+	int ret;
-+
-+	if (!drv->mtk_rfeatures_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting ap rfeatures control");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+	if (!data)
-+		goto fail;
-+
-+	data2 = nla_nest_start(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG);
-+	if (!data2)
-+		goto fail;
-+
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN, enable);
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE, type);
-+
-+	nla_nest_end(msg, data2);
-+	nla_nest_end(msg, data);
-+
-+	ret = send_and_recv_cmd(drv, msg);
-+	if (ret)
-+		wpa_printf(MSG_ERROR, "Failed to set trig_type. ret=%d (%s)", ret, strerror(-ret));
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
- 	.desc = "Linux nl80211/cfg80211",
-@@ -14779,4 +14974,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.ibf_dump = nl80211_ibf_dump,
- 	.amsdu_ctrl = nl80211_enable_amsdu,
- 	.amsdu_dump = nl80211_dump_amsdu,
-+	.get_aval_color_bmp = nl80211_get_aval_color_bmp,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 5aa813e26..5b4d45567 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -205,6 +205,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int mtk_3wire_vendor_cmd_avail:1;
- 	unsigned int mtk_ibf_vendor_cmd_avail:1;
- 	unsigned int mtk_wireless_vendor_cmd_avail:1;
-+	unsigned int mtk_bss_color_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 474d4e273..a7df2d172 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1156,6 +1156,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL:
- 					drv->mtk_wireless_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
-+					drv->mtk_bss_color_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0048-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0048-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch
new file mode 100644
index 0000000..b7753e8
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0048-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch
@@ -0,0 +1,43 @@
+From 1d4221d2dd26e3324617c390ccb117f59b894dde Mon Sep 17 00:00:00 2001
+From: mtk25255 <rohit.kamat@mediatek.com>
+Date: Thu, 12 Oct 2023 14:29:23 +0800
+Subject: [PATCH 048/126] mtk: hostapd: Fix RSNXE Interop issue with STA
+
+---
+ src/ap/ieee802_11.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index ee698fa4a..4581ae1d4 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -5370,6 +5370,7 @@ static void handle_assoc(struct hostapd_data *hapd,
+ 	int omit_rsnxe = 0;
+ 	bool set_beacon = false;
+ 	bool mld_addrs_not_translated = false;
++	bool sae_pk = false;
+ 
+ 	if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
+ 				      sizeof(mgmt->u.assoc_req))) {
+@@ -5615,7 +5616,17 @@ static void handle_assoc(struct hostapd_data *hapd,
+ 	if (resp != WLAN_STATUS_SUCCESS)
+ 		goto fail;
+ 	omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
+-
++#ifdef CONFIG_SAE_PK
++	sae_pk = hostapd_sae_pk_in_use(hapd->conf);
++#endif /* CONFIG_SAE_PK */
++	if (omit_rsnxe) {
++		if (!reassoc && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
++				(hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
++				 hapd->conf->sae_pwe == SAE_PWE_BOTH || sae_pk ||
++				 wpa_key_mgmt_sae_ext_key(hapd->conf->wpa_key_mgmt))) {
++			omit_rsnxe = 0;
++		}
++	}
+ 	if (hostapd_get_aid(hapd, sta) < 0) {
+ 		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ 			       HOSTAPD_LEVEL_INFO, "No room for more AIDs");
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0048-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0048-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch
deleted file mode 100644
index e6f30ed..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0048-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch
+++ /dev/null
@@ -1,210 +0,0 @@
-From 1b8fc72bfd653ce3ef422e86617b1821948f4805 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Mon, 20 Mar 2023 16:08:30 +0800
-Subject: [PATCH 048/104] mtk: hostapd: Fix ZWDFS issue in BW 160
-
-When background radar is enabled and bandwidth is set to 160, AP will
-fail to startup due to the lack of non-DFS channel.
-Under this circumstance, AP should perform CAC itself, and the background
-chain could also perform CAC simultaneously.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c | 98 ++++++++++++++++++++++++++++++++++++++++++----------
- 1 file changed, 79 insertions(+), 19 deletions(-)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 3e036441b..f5794753e 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -69,15 +69,22 @@ static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
- static int dfs_channel_available(struct hostapd_channel_data *chan,
- 				 enum dfs_channel_type type)
- {
-+	int dfs_status = chan->flag & HOSTAPD_CHAN_DFS_MASK;
-+
-+	if (chan->flag & HOSTAPD_CHAN_DISABLED)
-+		return -1;
-+
- 	if (type == DFS_NO_CAC_YET) {
- 		/* Select only radar channel where CAC has not been
- 		 * performed yet
- 		 */
--		if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
--		    (chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
--		     HOSTAPD_CHAN_DFS_USABLE)
-+		if (!(chan->flag & HOSTAPD_CHAN_RADAR))
-+			return 0;
-+
-+		if (dfs_status == HOSTAPD_CHAN_DFS_USABLE)
- 			return 1;
--		return 0;
-+
-+		return -1;
- 	}
- 
- 	/*
-@@ -86,16 +93,14 @@ static int dfs_channel_available(struct hostapd_channel_data *chan,
- 	 * channel for CSA, unless they are available for immediate use.
- 	 */
- 	if (type == DFS_AVAILABLE && (chan->flag & HOSTAPD_CHAN_RADAR) &&
--	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
--	     HOSTAPD_CHAN_DFS_AVAILABLE))
--		return 0;
-+	    (dfs_status != HOSTAPD_CHAN_DFS_AVAILABLE))
-+		return -1;
- 
--	if (chan->flag & HOSTAPD_CHAN_DISABLED)
--		return 0;
- 	if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
--	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
--	     HOSTAPD_CHAN_DFS_UNAVAILABLE))
--		return 0;
-+	    ((dfs_status == HOSTAPD_CHAN_DFS_UNAVAILABLE) ||
-+	    (dfs_status == HOSTAPD_CHAN_DFS_UNKNOWN)))
-+		return -1;
-+
- 	return 1;
- }
- 
-@@ -167,7 +172,7 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
- 				    enum dfs_channel_type type)
- {
- 	struct hostapd_channel_data *first_chan, *chan;
--	int i;
-+	int i, available = 0, ret = 0;
- 	u32 bw = num_chan_to_bw(num_chans);
- 
- 	if (first_chan_idx + num_chans > mode->num_channels) {
-@@ -203,14 +208,17 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
- 			return 0;
- 		}
- 
--		if (!dfs_channel_available(chan, type)) {
-+		ret = dfs_channel_available(chan, type);
-+		if (ret < 0) {
- 			wpa_printf(MSG_DEBUG, "DFS: channel not available %d",
- 				   first_chan->freq + i * 20);
- 			return 0;
- 		}
-+
-+		available |= ret;
- 	}
- 
--	return 1;
-+	return available;
- }
- 
- 
-@@ -838,8 +846,12 @@ static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
-  */
- int hostapd_handle_dfs(struct hostapd_iface *iface)
- {
-+	struct hostapd_channel_data *channel;
- 	int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
--	int skip_radar = 0;
-+	int sec = 0, skip_radar = 0;
-+	u8 cf1 = 0, cf2 = 0;
-+	bool use_radar_background = dfs_use_radar_background(iface);
-+	enum dfs_channel_type channel_type = DFS_NO_CAC_YET;
- 
- 	if (is_6ghz_freq(iface->freq))
- 		return 1;
-@@ -902,7 +914,7 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- 	/* Finally start CAC */
- 	hostapd_set_state(iface, HAPD_IFACE_DFS);
- 	wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz%s", iface->freq,
--		   dfs_use_radar_background(iface) ? " (background)" : "");
-+		   use_radar_background ? " (background)" : "");
- 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
- 		"freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
- 		iface->freq,
-@@ -912,6 +924,16 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- 		hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
- 		iface->dfs_cac_ms / 1000);
- 
-+	if (use_radar_background) {
-+		channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, DFS_AVAILABLE);
-+		/*
-+		 * AP cannot get any random available channel.
-+		 * Let AP and dedicated radar chain both perform CAC.
-+		 */
-+		if (!channel)
-+			use_radar_background = false;
-+	}
-+
- 	res = hostapd_start_dfs_cac(
- 		iface, iface->conf->hw_mode, iface->freq, iface->conf->channel,
- 		iface->conf->ieee80211n, iface->conf->ieee80211ac,
-@@ -920,14 +942,14 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- 		hostapd_get_oper_chwidth(iface->conf),
- 		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
- 		hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
--		dfs_use_radar_background(iface));
-+		use_radar_background);
- 
- 	if (res) {
- 		wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
- 		return -1;
- 	}
- 
--	if (dfs_use_radar_background(iface)) {
-+	if (use_radar_background) {
- 		/* Cache background radar parameters. */
- 		iface->radar_background.channel = iface->conf->channel;
- 		iface->radar_background.secondary_channel =
-@@ -948,6 +970,35 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- 
- 		iface->radar_background.temp_ch = 1;
- 		return 1;
-+	} else if (dfs_use_radar_background(iface)) {
-+		if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
-+			channel_type = DFS_ANY_CHANNEL;
-+
-+		channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, channel_type);
-+
-+		if (!channel ||
-+		    (channel->chan == iface->conf->channel &&
-+		    cf1 == hostapd_get_oper_centr_freq_seg0_idx(iface->conf) &&
-+		    cf2 == hostapd_get_oper_centr_freq_seg1_idx(iface->conf))) {
-+			wpa_printf(MSG_ERROR, "Background radar could not get valid channel\n");
-+			iface->radar_background.channel = -1;
-+			return 0;
-+		}
-+
-+		hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
-+				      channel->freq, channel->chan,
-+				      iface->conf->ieee80211n,
-+				      iface->conf->ieee80211ac,
-+				      iface->conf->ieee80211ax,
-+				      iface->conf->ieee80211be,
-+				      sec, hostapd_get_oper_chwidth(iface->conf),
-+				      cf1, cf2, true);
-+
-+		iface->radar_background.channel = channel->chan;
-+		iface->radar_background.freq = channel->freq;
-+		iface->radar_background.secondary_channel = sec;
-+		iface->radar_background.centr_freq_seg0_idx = cf1;
-+		iface->radar_background.centr_freq_seg1_idx = cf2;
- 	}
- 
- 	return 0;
-@@ -1204,6 +1255,15 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
- 				hostapd_setup_interface_complete(iface, 0);
- 				iface->cac_started = 0;
- 			}
-+
-+			/*
-+			 * When background radar is enabled but the CAC completion
-+			 * is not received from the background chain.
-+			 * Then, reset radar background chain.
-+			 */
-+			if (dfs_use_radar_background(iface) &&
-+			    iface->radar_background.channel == -1)
-+				hostapd_dfs_update_background_chain(iface);
- 		}
- 	} else if (hostapd_dfs_is_background_event(iface, freq)) {
- 		iface->radar_background.cac_started = 0;
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0049-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0049-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch
deleted file mode 100644
index 8b21535..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0049-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch
+++ /dev/null
@@ -1,396 +0,0 @@
-From b9b137827e9c0584682606bd5fe1cd9f50635819 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 17 Mar 2023 16:17:14 +0800
-Subject: [PATCH 049/104] mtk: hostapd: Add vendor for CAPI certification
- commands
-
----
- hostapd/ctrl_iface.c              | 99 +++++++++++++++++++++++++++++++
- src/ap/ap_drv_ops.c               | 21 +++++++
- src/ap/ap_drv_ops.h               |  3 +
- src/common/mtk_vendor.h           | 33 +----------
- src/drivers/driver.h              | 22 +++++++
- src/drivers/driver_nl80211.c      | 55 +++++++++++++++++
- src/drivers/driver_nl80211.h      |  1 +
- src/drivers/driver_nl80211_capa.c |  3 +
- 8 files changed, 206 insertions(+), 31 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 7b83bdd4f..1154a2394 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -71,6 +71,7 @@
- #include "config_file.h"
- #include "ctrl_iface.h"
- 
-+#include "common/mtk_vendor.h"
- 
- #define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
- 
-@@ -4327,6 +4328,100 @@ hostapd_ctrl_iface_get_aval_color_bmp(struct hostapd_data *hapd, char *buf,
- 	return pos - buf;
- }
- 
-+static int
-+hostapd_ctrl_iface_ap_wireless(struct hostapd_data *hapd, char *cmd,
-+					 char *buf, size_t buflen)
-+{
-+	char *pos, *value, *config = cmd;
-+	enum mtk_vendor_attr_wireless_ctrl sub_cmd;
-+
-+	pos = os_strchr(config, '=');
-+	if (pos == NULL)
-+		return -1;
-+	*pos++ = '\0';
-+
-+	if(pos == NULL)
-+		return -1;
-+	value = pos;
-+
-+	if (os_strncmp(config, "fixed_mcs", 9) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS;
-+	else if (os_strncmp(config, "ofdma", 5) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA;
-+	else if (os_strncmp(config, "ppdu_type", 9) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE;
-+	else if (os_strncmp(config, "nusers_ofdma", 12) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA;
-+	else if (os_strncmp(config, "add_ba_req_bufsize", 18) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE;
-+	else if (os_strncmp(config, "mimo", 4) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO;
-+	else if (os_strncmp(config, "cert", 4) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT ;
-+	else if (os_strncmp(config, "amsdu", 5) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU;
-+	else if (os_strncmp(config, "rts_sigta", 9) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA;
-+	else {
-+		wpa_printf(MSG_ERROR,
-+			"Unsupported parameter %s for ap_wireless", config);
-+		return -1;
-+	}
-+
-+	if (hostapd_drv_ap_wireless(hapd, (u8) sub_cmd, atoi(value)) != 0)
-+		return -1;
-+
-+	return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+static int
-+hostapd_ctrl_iface_ap_rfeatures(struct hostapd_data *hapd, char *cmd,
-+					 char *buf, size_t buflen)
-+{
-+	char *pos, *value, *type, *config = cmd;
-+	enum mtk_vendor_attr_rfeature_ctrl sub_cmd;
-+
-+	pos = os_strchr(config, '=');
-+	if (pos == NULL)
-+		return -1;
-+	*pos++ = '\0';
-+
-+	if(pos == NULL)
-+		return -1;
-+	value = pos;
-+
-+	if (os_strncmp(config, "he_gi", 5) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI;
-+	else if (os_strncmp(config, "he_ltf", 6) == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF;
-+	else if (os_strncmp(config, "trig_type", 9) == 0) {
-+		pos = os_strchr(value, ',');
-+		if (pos == NULL)
-+			return -1;
-+		*pos++ = '\0';
-+		if(pos == NULL)
-+			return -1;
-+		type = pos;
-+		goto trigtype;
-+	} else if (os_strcmp(config, "ack_policy") == 0)
-+		sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY;
-+	else {
-+		wpa_printf(MSG_ERROR,
-+			"Unsupported parameter %s for ap_rfeatures", config);
-+		return -1;
-+	}
-+
-+	if (hostapd_drv_ap_rfeatures(hapd, (u8) sub_cmd, atoi(value)) != 0)
-+		return -1;
-+	goto exit;
-+
-+trigtype:
-+	if (hostapd_drv_ap_trig_type(hapd, atoi(value), atoi(type)) != 0)
-+		return -1;
-+
-+exit:
-+	return os_snprintf(buf, buflen, "OK\n");
-+}
- 
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
-@@ -4952,6 +5047,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 		reply_len = hostapd_ctrl_iface_get_bss_color(hapd, reply, reply_size);
- 	} else if (os_strncmp(buf, "AVAL_COLOR_BMP", 14) == 0) {
- 		reply_len = hostapd_ctrl_iface_get_aval_color_bmp(hapd, reply, reply_size);
-+	} else if (os_strncmp(buf, "ap_wireless ", 12) == 0) {
-+		reply_len = hostapd_ctrl_iface_ap_wireless(hapd, buf + 12, reply, reply_size);
-+	} else if (os_strncmp(buf, "ap_rfeatures ", 13) == 0) {
-+		reply_len = hostapd_ctrl_iface_ap_rfeatures(hapd, buf + 13, reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index cabcd47af..06d71f309 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1330,3 +1330,24 @@ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_colo
- 		return 0;
- 	return hapd->driver->get_aval_color_bmp(hapd->drv_priv, aval_color_bmp);
- }
-+
-+int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
-+{
-+	if (!hapd->driver || !hapd->driver->ap_wireless)
-+		return 0;
-+	return hapd->driver->ap_wireless(hapd->drv_priv, sub_vendor_id, value);
-+}
-+
-+int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
-+{
-+	if (!hapd->driver || !hapd->driver->ap_rfeatures)
-+		return 0;
-+	return hapd->driver->ap_rfeatures(hapd->drv_priv, sub_vendor_id, value);
-+}
-+
-+int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type)
-+{
-+	if (!hapd->driver || !hapd->driver->ap_trigtype)
-+		return 0;
-+	return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 9da2b0049..c58930217 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -162,6 +162,9 @@ int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
- int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd,
- 				       u64 *aval_color_bmp);
-+int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
-+int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
-+int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index e27fe69b3..0b23c76ad 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -50,17 +50,6 @@ enum mtk_vendor_attr_edcca_dump {
- 		NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
- };
- 
--
--static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
--	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
--	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
--	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
--	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
--	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
--	[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
--	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
--};
--
- enum mtk_vendor_attr_3wire_ctrl {
- 	MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
- 
-@@ -72,10 +61,6 @@ enum mtk_vendor_attr_3wire_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
- };
- 
--static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
--	[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
--};
--
- enum mtk_vendor_attr_csi_ctrl {
- 	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
- 
-@@ -172,7 +157,7 @@ enum mtk_vendor_attr_wireless_ctrl {
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
--	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
-+	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT = 9,
- 	MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
- 
- 	/* keep last */
-@@ -192,11 +177,6 @@ enum mtk_vendor_attr_wireless_dump {
- 		NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
- };
- 
--static const struct nla_policy
--wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
--	[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
--};
--
- enum mtk_vendor_attr_rfeature_ctrl {
- 	MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
- 
-@@ -206,6 +186,7 @@ enum mtk_vendor_attr_rfeature_ctrl {
- 	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
- 	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
- 	MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
-+	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF,
- 
- 	/* keep last */
- 	NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
-@@ -247,16 +228,6 @@ enum mtk_vendor_attr_ibf_dump {
- 		NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
- };
- 
--static struct nla_policy
--ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
--	[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
--};
--
--static struct nla_policy
--ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
--	[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
--};
--
- enum mtk_vendor_attr_bss_color_ctrl {
- 	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 6b6317bfa..a25601c91 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5281,6 +5281,28 @@ struct wpa_driver_ops {
- 	 *
- 	 */
- 	int (*get_aval_color_bmp)(void *priv, u64 *aval_color_bmp);
-+
-+	/**
-+	* ap_wireless - set wireless command
-+	* @priv: Private driver interface data
-+	* @value: value
-+	*/
-+	int (*ap_wireless)(void *priv, u8 mode, int value);
-+
-+	/**
-+	* ap_rfeatures - set ap rf features command
-+	* @priv: Private driver interface data
-+	* @value: value
-+	*/
-+	int (*ap_rfeatures)(void *priv, u8 mode, int value);
-+
-+	/**
-+	* ap_trigtype - set trigger type
-+	* @priv: Private driver interface data
-+	* @enable: enable or disable
-+	* @type: trigger type
-+	*/
-+	int (*ap_trigtype)(void *priv, u8 enable, u8 type);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 4c98e8ab3..86e5844cd 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -91,6 +91,58 @@ static void handle_nl_debug_hook(struct nl_msg *msg, int tx)
- 	wpa_netlink_hook(tx, nlh, nlh->nlmsg_len);
- }
- 
-+static struct nla_policy
-+ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
-+	[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
-+};
-+
-+static struct nla_policy
-+ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
-+	[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
-+};
-+
-+static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
-+	[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
-+};
-+
-+static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
-+};
-+
-+static const struct nla_policy
-+wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
-+	[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
-+};
-+
-+static const struct nla_policy
-+rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG] = { .type = NLA_NESTED },
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
-+};
-+
-+static const struct nla_policy
-+wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE] = {.type = NLA_U16 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
-+};
-+
- static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
- {
- 	struct nl_sock *handle;
-@@ -14975,4 +15027,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.amsdu_ctrl = nl80211_enable_amsdu,
- 	.amsdu_dump = nl80211_dump_amsdu,
- 	.get_aval_color_bmp = nl80211_get_aval_color_bmp,
-+	.ap_wireless = nl80211_ap_wireless,
-+	.ap_rfeatures = nl80211_ap_rfeatures,
-+	.ap_trigtype = nl80211_ap_trigtype,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 5b4d45567..046991a3d 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -206,6 +206,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int mtk_ibf_vendor_cmd_avail:1;
- 	unsigned int mtk_wireless_vendor_cmd_avail:1;
- 	unsigned int mtk_bss_color_vendor_cmd_avail:1;
-+	unsigned int mtk_rfeatures_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index a7df2d172..6498eba6d 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1159,6 +1159,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
- 					drv->mtk_bss_color_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
-+					drv->mtk_rfeatures_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0049-mtk-hostapd-update-eht-operation-element.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0049-mtk-hostapd-update-eht-operation-element.patch
new file mode 100644
index 0000000..d99e601
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0049-mtk-hostapd-update-eht-operation-element.patch
@@ -0,0 +1,29 @@
+From 97f988afe8673579444a9ae90c2db48a6d7527e7 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Wed, 10 May 2023 13:11:34 +0800
+Subject: [PATCH 049/126] mtk: hostapd: update eht operation element
+
+---
+ src/ap/ieee802_11_eht.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index b935ee889..a04eb23e0 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -237,9 +237,9 @@ u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
+ 
+ 	/* TODO: Fill in appropriate EHT-MCS max Nss information */
+ 	oper->basic_eht_mcs_nss_set[0] = 0x11;
+-	oper->basic_eht_mcs_nss_set[1] = 0x00;
+-	oper->basic_eht_mcs_nss_set[2] = 0x00;
+-	oper->basic_eht_mcs_nss_set[3] = 0x00;
++	oper->basic_eht_mcs_nss_set[1] = 0x11;
++	oper->basic_eht_mcs_nss_set[2] = 0x11;
++	oper->basic_eht_mcs_nss_set[3] = 0x11;
+ 
+ 	if (!eht_oper_info_present)
+ 		return pos + elen;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0050-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0050-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch
deleted file mode 100644
index aecb14a..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0050-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch
+++ /dev/null
@@ -1,506 +0,0 @@
-From 994774c363a07fa90a7a21974b7b4a371b235673 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 12 May 2023 05:18:48 +0800
-Subject: [PATCH 050/104] mtk: hostapd: Air Monitor support in hostapd by
- vendor
-
-Signed-off-by: mtk23888 <dipanshu.mittal@mediatek.com>
----
- hostapd/ctrl_iface.c              | 113 +++++++++++++++++++
- hostapd/hostapd_cli.c             |  15 +++
- src/ap/ap_drv_ops.c               |  14 +++
- src/ap/ap_drv_ops.h               |   3 +
- src/common/mtk_vendor.h           |   8 ++
- src/drivers/driver.h              |  16 +++
- src/drivers/driver_nl80211.c      | 179 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |   1 +
- src/drivers/driver_nl80211_capa.c |   2 +
- 9 files changed, 351 insertions(+)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 1154a2394..56722384b 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4370,6 +4370,44 @@ hostapd_ctrl_iface_ap_wireless(struct hostapd_data *hapd, char *cmd,
- 
- 	if (hostapd_drv_ap_wireless(hapd, (u8) sub_cmd, atoi(value)) != 0)
- 		return -1;
-+	return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+static int
-+hostapd_ctrl_iface_set_amnt(struct hostapd_data *hapd, char *cmd,
-+					char *buf, size_t buflen)
-+{
-+	char *tmp, sta_mac[ETH_ALEN] = {0};
-+	int amnt_idx = 0;
-+
-+	tmp = strtok_r(cmd, " ", &cmd);
-+
-+	if (!tmp) {
-+		wpa_printf(MSG_ERROR, "Error in command format\n");
-+		return -1;
-+	}
-+
-+	amnt_idx = strtol(tmp, &tmp, 10);
-+
-+	if (amnt_idx < 0 || amnt_idx > 15) {
-+		wpa_printf(MSG_ERROR, "Wrong AMNT index %d\n", amnt_idx);
-+		return -1;
-+	}
-+
-+	if (!cmd) {
-+		wpa_printf(MSG_ERROR, "Error in command format\n");
-+		return -1;
-+	}
-+
-+	if (hwaddr_aton(cmd, sta_mac) < 0) {
-+		wpa_printf(MSG_ERROR, "station mac is not right.\n");
-+		return -1;
-+	}
-+
-+	if (hostapd_drv_amnt_set(hapd, amnt_idx, sta_mac)) {
-+		wpa_printf(MSG_ERROR, "Not able to set amnt index\n");
-+		return -1;
-+	}
- 
- 	return os_snprintf(buf, buflen, "OK\n");
- }
-@@ -4423,6 +4461,75 @@ exit:
- 	return os_snprintf(buf, buflen, "OK\n");
- }
- 
-+static int
-+hostapd_ctrl_iface_dump_amnt(struct hostapd_data *hapd, char *cmd,
-+				char *buf, size_t buflen)
-+{
-+	char *tmp;
-+	int amnt_idx = 0, ret = 0;
-+	struct amnt_resp_data *resp_buf;
-+	char *pos, *end;
-+	struct amnt_data *res;
-+
-+	pos = buf;
-+	end = buf + buflen;
-+
-+	tmp = strtok_r(cmd, " ", &cmd);
-+
-+	if (!tmp) {
-+		wpa_printf(MSG_ERROR, "Error in command format\n");
-+		return -1;
-+	}
-+
-+	amnt_idx = strtoul(tmp, &tmp, 0);
-+
-+	if ((amnt_idx < 0 || amnt_idx > 15) && amnt_idx != 0xff) {
-+		wpa_printf(MSG_ERROR, "Wrong AMNT index\n");
-+		return -1;
-+	}
-+
-+	if (amnt_idx == 0xff)
-+		resp_buf = (struct amnt_resp_data *) os_zalloc(AIR_MONITOR_MAX_ENTRY
-+							* sizeof(struct amnt_data) + 1);
-+	else
-+		resp_buf = (struct amnt_resp_data *) os_zalloc(sizeof(struct amnt_data) + 1);
-+
-+	if (resp_buf == NULL) {
-+		wpa_printf(MSG_ERROR, "Error in memory allocation\n");
-+		return -1;
-+	}
-+
-+	if (hostapd_drv_amnt_dump(hapd, amnt_idx, (u8 *)resp_buf)) {
-+		wpa_printf(MSG_ERROR, "Not able to set amnt index\n");
-+		os_free(resp_buf);
-+		return -1;
-+	}
-+
-+	for (int i = 0; i < resp_buf->sta_num && i < AIR_MONITOR_MAX_ENTRY; i++) {
-+		res = &resp_buf->resp_data[i];
-+		ret = os_snprintf(pos, end - pos,
-+				"[hostapd_cli] amnt_idx: %d, addr="MACSTR
-+				", rssi=%d/%d/%d/%d, last_seen=%u\n",
-+				res->idx,
-+				MAC2STR(res->addr), res->rssi[0],
-+				res->rssi[1], res->rssi[2],
-+				res->rssi[3], res->last_seen);
-+		if (os_snprintf_error(end - pos, ret)) {
-+			os_free(resp_buf);
-+			return 0;
-+		}
-+		pos = pos + ret;
-+	}
-+
-+	os_free(resp_buf);
-+
-+	if (pos == buf)
-+		return os_snprintf(buf, buflen, "Index %d is not monitored\n",
-+				amnt_idx);
-+	else
-+		return pos - buf;
-+}
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -5051,6 +5158,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 		reply_len = hostapd_ctrl_iface_ap_wireless(hapd, buf + 12, reply, reply_size);
- 	} else if (os_strncmp(buf, "ap_rfeatures ", 13) == 0) {
- 		reply_len = hostapd_ctrl_iface_ap_rfeatures(hapd, buf + 13, reply, reply_size);
-+	} else if (os_strncmp(buf, "SET_AMNT", 8) == 0) {
-+		reply_len = hostapd_ctrl_iface_set_amnt(hapd, buf+9,
-+							reply, reply_size);
-+	} else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
-+		reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
-+							reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 865c11432..12c580455 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1693,6 +1693,17 @@ static int hostapd_cli_cmd_get_amsdu(struct wpa_ctrl *ctrl, int argc,
- 	return hostapd_cli_cmd(ctrl, "GET_AMSDU", 0, NULL, NULL);
- }
- 
-+static int hostapd_cli_cmd_set_amnt(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "SET_AMNT", 2, argc, argv);
-+}
-+
-+static int hostapd_cli_cmd_dump_amnt(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "DUMP_AMNT", 1, argc, argv);
-+}
- 
- struct hostapd_cli_cmd {
- 	const char *cmd;
-@@ -1925,6 +1936,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 	  " = show iBF state (enabled/disabled)"},
- 	{ "get_amsdu", hostapd_cli_cmd_get_amsdu, NULL,
- 		" = show AMSDU state"},
-+	{ "set_amnt", hostapd_cli_cmd_set_amnt, NULL,
-+		" = Set Station index and mac to monitor"},
-+	{ "dump_amnt", hostapd_cli_cmd_dump_amnt, NULL,
-+		" = Dump RSSI of monitoring Station"},
- 	{ NULL, NULL, NULL, NULL }
- };
- 
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 06d71f309..df652b12f 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1351,3 +1351,17 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type)
- 		return 0;
- 	return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type);
- }
-+
-+int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac)
-+{
-+	if (!hapd->driver || !hapd->driver->amnt_set)
-+		return 0;
-+	return hapd->driver->amnt_set(hapd->drv_priv, amnt_idx, amnt_sta_mac);
-+}
-+
-+int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf)
-+{
-+	if (!hapd->driver || !hapd->driver->amnt_dump)
-+		return 0;
-+	return hapd->driver->amnt_dump(hapd->drv_priv, amnt_idx, amnt_dump_buf);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index c58930217..4805a2e84 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -166,6 +166,9 @@ int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int val
- int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
- int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
- 
-+int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
-+int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
-+
- #include "drivers/driver.h"
- 
- int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 0b23c76ad..dd1ca2164 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -258,10 +258,18 @@ struct csi_data {
- 	u16 rx_idx;
- };
- 
-+#define AIR_MONITOR_MAX_ENTRY 16
-+
- struct amnt_data {
- 	u8 idx;
- 	u8 addr[ETH_ALEN];
- 	s8 rssi[4];
- 	u32 last_seen;
- };
-+
-+struct amnt_resp_data {
-+	u8 sta_num;
-+	struct amnt_data resp_data[0];
-+};
-+
- #endif /* MTK_VENDOR_H */
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index a25601c91..dd9c33201 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5303,6 +5303,22 @@ struct wpa_driver_ops {
- 	* @type: trigger type
- 	*/
- 	int (*ap_trigtype)(void *priv, u8 enable, u8 type);
-+
-+	/**
-+	* amnt_set - add/delete station from monitoring
-+	* @priv: Private driver interface data
-+	* @amnt_idx: Monitor Index
-+	* @amnt_sta_mac: station mac address
-+	*/
-+	int (*amnt_set)(void *priv, u8 amnt_idx, u8 *amnt_sta_mac);
-+
-+	/**
-+	* amnt_dump - Dump particular/ all station
-+	* @priv: Private driver interface data
-+	* @amnt_idx: Monitor Index
-+	* @amnt_dump_buf: Buffer to print
-+	*/
-+	int (*amnt_dump)(void *priv, u8 amnt_idx, u8 *amnt_dump_buf);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 86e5844cd..a2a6807f4 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -143,6 +143,19 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
- 	[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
- };
- 
-+static struct nla_policy
-+amnt_ctrl_policy[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL] = {
-+	[MTK_VENDOR_ATTR_AMNT_CTRL_SET] = {.type = NLA_NESTED },
-+	[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP] = { .type = NLA_NESTED },
-+};
-+
-+static struct nla_policy
-+amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
-+	[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX] = {.type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_AMNT_DUMP_LEN] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
-+};
-+
- static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
- {
- 	struct nl_sock *handle;
-@@ -14855,6 +14868,170 @@ fail:
- 	return -ENOBUFS;
- }
- 
-+static int
-+nl80211_amnt_set(void *priv, u8 amnt_idx, u8 *amnt_sta_mac)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	void *tb1;
-+	int ret;
-+
-+	if (!drv->mtk_amnt_vendor_cmd_avail) {
-+		wpa_printf(MSG_ERROR,
-+			"nl80211: Driver does not support air monitor");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+			nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
-+	if (!data)
-+		goto fail;
-+
-+	tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_AMNT_CTRL_SET);
-+	if (!tb1)
-+		goto fail;
-+
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_AMNT_SET_INDEX, amnt_idx);
-+
-+	nla_put(msg, MTK_VENDOR_ATTR_AMNT_SET_MACADDR, ETH_ALEN, amnt_sta_mac);
-+
-+	nla_nest_end(msg, tb1);
-+	nla_nest_end(msg, data);
-+
-+	ret = send_and_recv_cmd(drv, msg);
-+
-+	if (ret)
-+		wpa_printf(MSG_ERROR, "Failed to set air monitor. ret=%d (%s)",
-+			ret, strerror(-ret));
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+
-+}
-+
-+static int
-+mt76_amnt_dump_cb(struct nl_msg *msg, void *arg)
-+{
-+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+	struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
-+	struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP];
-+	struct nlattr *attr, *cur, *data;
-+	struct amnt_data *res;
-+	int len = 0, rem;
-+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+	struct amnt_resp_data *amnt_dump = (struct amnt_resp_data *)arg;
-+
-+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+		genlmsg_attrlen(gnlh, 0), NULL);
-+
-+	attr = tb[NL80211_ATTR_VENDOR_DATA];
-+	if (!attr)
-+		return NL_SKIP;
-+
-+	nla_parse_nested(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX,
-+			attr, amnt_ctrl_policy);
-+
-+	if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP])
-+		return NL_SKIP;
-+
-+	nla_parse_nested(tb2, NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
-+			tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP], amnt_dump_policy);
-+
-+	if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_LEN])
-+		return NL_SKIP;
-+
-+	len = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_DUMP_LEN]);
-+	if (!len)
-+		return 0;
-+
-+	if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT])
-+		return NL_SKIP;
-+
-+	data = tb2[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT];
-+
-+	nla_for_each_nested(cur, data, rem) {
-+		if (amnt_dump->sta_num >= AIR_MONITOR_MAX_ENTRY)
-+			return NL_SKIP;
-+		res = (struct amnt_data *) nla_data(cur);
-+		wpa_printf(MSG_ERROR, "[vendor] amnt_idx: %d, "
-+			"addr="MACSTR", "
-+			"rssi=%d/%d/%d/%d, last_seen=%u\n",
-+			res->idx,
-+			MAC2STR(res->addr),
-+			res->rssi[0], res->rssi[1], res->rssi[2],
-+			res->rssi[3], res->last_seen);
-+		os_memcpy(&amnt_dump->resp_data[amnt_dump->sta_num], res,
-+			sizeof(struct amnt_data));
-+		amnt_dump->sta_num++;
-+	}
-+	return 0;
-+}
-+
-+static int
-+nl80211_amnt_dump(void *priv, u8 amnt_idx, u8 *dump_buf)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	void *tb1;
-+	int ret;
-+
-+	if (!drv->mtk_amnt_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			"nl80211: Driver does not support air monitor");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+			nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
-+	if (!data)
-+		goto fail;
-+
-+	tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_AMNT_CTRL_DUMP
-+			| NLA_F_NESTED);
-+	if (!tb1)
-+		goto fail;
-+
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_AMNT_DUMP_INDEX, amnt_idx);
-+
-+	nla_nest_end(msg, tb1);
-+	nla_nest_end(msg, data);
-+
-+	ret = send_and_recv_resp(drv, msg, mt76_amnt_dump_cb, dump_buf);
-+
-+	if (ret)
-+		wpa_printf(MSG_ERROR, "Failed to Dump air monitor. ret=%d (%s)"
-+			, ret, strerror(-ret));
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
- 	.desc = "Linux nl80211/cfg80211",
-@@ -15030,4 +15207,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.ap_wireless = nl80211_ap_wireless,
- 	.ap_rfeatures = nl80211_ap_rfeatures,
- 	.ap_trigtype = nl80211_ap_trigtype,
-+	.amnt_set = nl80211_amnt_set,
-+	.amnt_dump = nl80211_amnt_dump,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 046991a3d..adc1b9bf7 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -207,6 +207,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int mtk_wireless_vendor_cmd_avail:1;
- 	unsigned int mtk_bss_color_vendor_cmd_avail:1;
- 	unsigned int mtk_rfeatures_vendor_cmd_avail:1;
-+	unsigned int mtk_amnt_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 6498eba6d..38e83e42b 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1158,6 +1158,8 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 					break;
- 				case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
- 					drv->mtk_bss_color_vendor_cmd_avail = 1;
-+				case MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL:
-+					drv->mtk_amnt_vendor_cmd_avail = 1;
- 					break;
- 				case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
- 					drv->mtk_rfeatures_vendor_cmd_avail = 1;
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0050-mtk-hostapd-add-support-for-ucode-to-parse-BW320MHz-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0050-mtk-hostapd-add-support-for-ucode-to-parse-BW320MHz-.patch
new file mode 100644
index 0000000..89795f5
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0050-mtk-hostapd-add-support-for-ucode-to-parse-BW320MHz-.patch
@@ -0,0 +1,28 @@
+From 4ee7e5918d767afcef2797f4bffe000c5c811229 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Wed, 30 Aug 2023 04:23:37 +0800
+Subject: [PATCH 050/126] mtk: hostapd: add support for ucode to parse BW320MHz
+ info
+
+---
+ src/utils/ucode.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/utils/ucode.c b/src/utils/ucode.c
+index 29c753c32..4b6ed3a94 100644
+--- a/src/utils/ucode.c
++++ b/src/utils/ucode.c
+@@ -144,6 +144,10 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 	case 2:
+ 		chanwidth = CONF_OPER_CHWIDTH_160MHZ;
+ 		break;
++	case 9:
++		width = 3;
++		chanwidth = CONF_OPER_CHWIDTH_320MHZ;
++		break;
+ 	default:
+ 		return NULL;
+ 	}
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0051-mtk-hostapd-Add-muru-user-number-debug-command.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0051-mtk-hostapd-Add-muru-user-number-debug-command.patch
deleted file mode 100644
index 85e1f7a..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0051-mtk-hostapd-Add-muru-user-number-debug-command.patch
+++ /dev/null
@@ -1,228 +0,0 @@
-From ccef6202191f2a17f84f021e6e2ade206b8c9cc1 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Fri, 12 May 2023 05:24:19 +0800
-Subject: [PATCH 051/104] mtk: hostapd: Add muru user number debug command
-
----
- hostapd/ctrl_iface.c         | 13 ++++++++++++-
- src/ap/ap_drv_ops.c          |  4 ++--
- src/ap/ap_drv_ops.h          |  2 +-
- src/ap/hostapd.c             |  3 ++-
- src/common/mtk_vendor.h      |  7 +++++++
- src/drivers/driver.h         |  4 ++--
- src/drivers/driver_nl80211.c | 37 ++++++++++++++++++++++++++++--------
- 7 files changed, 55 insertions(+), 15 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 56722384b..88475b321 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3673,6 +3673,8 @@ hostapd_ctrl_iface_set_edcca(struct hostapd_data *hapd, char *cmd,
- 					 char *buf, size_t buflen)
- {
- 	char *pos, *config, *value;
-+	u8 mode;
-+
- 	config = cmd;
- 	pos = os_strchr(config, ' ');
- 	if (pos == NULL)
-@@ -4065,6 +4067,8 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
- 					 char *buf, size_t buflen)
- {
- 	char *pos, *config, *value;
-+	u8 mode;
-+
- 	config = cmd;
- 	pos = os_strchr(config, ' ');
- 	if (pos == NULL)
-@@ -4082,13 +4086,20 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
- 			return -1;
- 		}
- 		hapd->iconf->mu_onoff = (u8) mu;
-+		mode = MU_CTRL_ONOFF;
-+	} else if (os_strcmp(config, "ul_user_cnt") == 0) {
-+		mode = MU_CTRL_UL_USER_CNT;
-+		wpa_printf(MSG_ERROR, "ul_user_cnt:%d\n", (u8)atoi(value));
-+	} else if (os_strcmp(config, "dl_user_cnt") == 0) {
-+		mode = MU_CTRL_DL_USER_CNT;
-+		wpa_printf(MSG_ERROR, "dl_user_cnt:%d\n", (u8)atoi(value));
- 	} else {
- 		wpa_printf(MSG_ERROR,
- 			"Unsupported parameter %s for SET_MU", config);
- 		return -1;
- 	}
- 
--	if(hostapd_drv_mu_ctrl(hapd) == 0) {
-+	if(hostapd_drv_mu_ctrl(hapd, mode, (u8)atoi(value)) == 0) {
- 		return os_snprintf(buf, buflen, "OK\n");
- 	} else {
- 		return -1;
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index df652b12f..8878db380 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1270,11 +1270,11 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
- 	return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
- }
- 
--int hostapd_drv_mu_ctrl(struct hostapd_data *hapd)
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val)
- {
- 	if (!hapd->driver || !hapd->driver->mu_ctrl)
- 		return 0;
--	return hapd->driver->mu_ctrl(hapd->drv_priv, hapd->iconf->mu_onoff);
-+	return hapd->driver->mu_ctrl(hapd->drv_priv, mode, val);
- }
- 
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 4805a2e84..f77d07da0 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -153,7 +153,7 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
- int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
- 					  const int *threshold);
- int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
--int hostapd_drv_mu_ctrl(struct hostapd_data *hapd);
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val);
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
- int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index a5b683676..5fd46d53d 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -58,6 +58,7 @@
- #include "wpa_auth_kay.h"
- #include "hw_features.h"
- 
-+#include "common/mtk_vendor.h"
- 
- static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
- #ifdef CONFIG_WEP
-@@ -2699,7 +2700,7 @@ dfs_offload:
- 	if (hostapd_drv_configure_edcca_threshold(hapd,
- 						  hapd->iconf->edcca_threshold) < 0)
- 		goto fail;
--	if (hostapd_drv_mu_ctrl(hapd) < 0)
-+	if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF, hapd->iconf->mu_onoff) < 0)
- 		goto fail;
- 	if (hostapd_drv_three_wire_ctrl(hapd) < 0)
- 		goto fail;
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index dd1ca2164..99371bf73 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -199,6 +199,8 @@ enum mtk_vendor_attr_mu_ctrl {
- 
- 	MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
- 	MTK_VENDOR_ATTR_MU_CTRL_DUMP,
-+	MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE,
-+	MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL,
- 
- 	/* keep last */
- 	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
-@@ -272,4 +274,9 @@ struct amnt_resp_data {
- 	struct amnt_data resp_data[0];
- };
- 
-+enum {
-+	MU_CTRL_ONOFF,
-+	MU_CTRL_DL_USER_CNT,
-+	MU_CTRL_UL_USER_CNT,
-+};
- #endif /* MTK_VENDOR_H */
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index dd9c33201..3be4562e7 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5236,11 +5236,11 @@ struct wpa_driver_ops {
- 	int (*get_edcca)(void *priv, const u8 mode, u8 *value);
- 
- 	/**
--	 * mu_ctrl - ctrl on off for UL/DL MURU
-+	 * mu_ctrl - ctrl for UL/DL MURU
- 	 * @priv: Private driver interface data
- 	 *
- 	 */
--	 int (*mu_ctrl)(void *priv, u8 mu_onoff);
-+	 int (*mu_ctrl)(void *priv, u8 mode, u8 val);
- 	 int (*mu_dump)(void *priv, u8 *mu_onoff);
- 
- 	/**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index a2a6807f4..035a477e2 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13982,13 +13982,13 @@ fail:
- 
- 
- #ifdef CONFIG_IEEE80211AX
--static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
-+static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
- {
- 	struct i802_bss *bss = priv;
- 	struct wpa_driver_nl80211_data *drv = bss->drv;
- 	struct nl_msg *msg;
- 	struct nlattr *data;
--	int ret;
-+	int ret = -ENOBUFS;
- 
- 	if (!drv->mtk_mu_vendor_cmd_avail) {
- 		wpa_printf(MSG_INFO,
-@@ -13999,17 +13999,38 @@ static int nl80211_mu_onoff(void *priv, u8 mu_onoff)
- 	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
- 		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
- 		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
--		!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
--		nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, mu_onoff)) {
--		nlmsg_free(msg);
--		return -ENOBUFS;
-+		!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)))
-+		goto fail;
-+
-+	switch (mode) {
-+	case MU_CTRL_ONOFF:
-+			if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, val))
-+				goto fail;
-+		break;
-+	case MU_CTRL_UL_USER_CNT:
-+	case MU_CTRL_DL_USER_CNT:
-+			if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE, mode) ||
-+			    nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL, val))
-+				goto fail;
-+		break;
-+	default:
-+		wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode !");
-+		ret = -EINVAL;
-+		goto fail;
- 	}
-+
- 	nla_nest_end(msg, data);
-+
- 	ret = send_and_recv_cmd(drv, msg);
- 	if(ret){
--		wpa_printf(MSG_ERROR, "Failed to set mu_onoff. ret=%d (%s)", ret, strerror(-ret));
-+		wpa_printf(MSG_ERROR, "Failed to set mu_ctrl. ret=%d (%s)", ret, strerror(-ret));
- 	}
- 	return ret;
-+
-+fail:
-+	nl80211_nlmsg_clear(msg);
-+	nlmsg_free(msg);
-+	return ret;
- }
- 
- 
-@@ -15178,7 +15199,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.update_connect_params = nl80211_update_connection_params,
- 	.send_external_auth_status = nl80211_send_external_auth_status,
- 	.set_4addr_mode = nl80211_set_4addr_mode,
--	.mu_ctrl = nl80211_mu_onoff,
-+	.mu_ctrl = nl80211_mu_ctrl,
- 	.mu_dump = nl80211_mu_dump,
- #ifdef CONFIG_DPP
- 	.dpp_listen = nl80211_dpp_listen,
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0051-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0051-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch
new file mode 100644
index 0000000..09e28f0
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0051-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch
@@ -0,0 +1,279 @@
+From 5070c33b7eb059d1c78ff0c02bb56b16e01431f8 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 11 Sep 2023 10:16:35 +0800
+Subject: [PATCH 051/126] mtk: hostapd: synchronize bandwidth in AP/STA support
+
+Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ucode.c         | 41 +++++++++++++++++++--
+ src/utils/ucode.c      | 12 +++++--
+ wpa_supplicant/ucode.c | 82 ++++++++++++++++++++++++++++++++++--------
+ 3 files changed, 117 insertions(+), 18 deletions(-)
+
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index 3468615fd..8c3212f6f 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -493,6 +493,9 @@ uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
+ 	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
+ 	int i;
+ 
++	wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
++			iface->phy, hostapd_state_text(iface->state));
++
+ 	if (!iface)
+ 		return NULL;
+ 
+@@ -519,6 +522,9 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
+ 	uint64_t intval;
+ 	int i;
+ 
++	wpa_printf(MSG_INFO, "ucode: mtk: start iface for %s in state %s\n",
++			iface->phy, hostapd_state_text(iface->state));
++
+ 	if (!iface)
+ 		return NULL;
+ 
+@@ -541,7 +547,13 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
+ 	UPDATE_VAL(op_class, "op_class");
+ 	UPDATE_VAL(hw_mode, "hw_mode");
+ 	UPDATE_VAL(channel, "channel");
+-	UPDATE_VAL(secondary_channel, "sec_channel");
++
++	intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL));
++	if (!errno) {
++		conf->secondary_channel = intval;
++		changed = true;
++	}
++
+ 	if (!changed &&
+ 	    (iface->bss[0]->beacon_set_done ||
+ 	     iface->state == HAPD_IFACE_DFS))
+@@ -587,6 +599,18 @@ out:
+ 		return ucv_boolean_new(true);
+ 	}
+ 
++	wpa_printf(MSG_INFO, "ucode: mtk: updated channel information:\n");
++	wpa_printf(MSG_INFO, "    * channel: %d\n", conf->channel);
++	wpa_printf(MSG_INFO, "    * op_class: %d\n", conf->op_class);
++	wpa_printf(MSG_INFO, "    * secondary channel: %d\n",
++			conf->secondary_channel);
++	wpa_printf(MSG_INFO, "    * seg0: %d\n",
++			hostapd_get_oper_centr_freq_seg0_idx(conf));
++	wpa_printf(MSG_INFO, "    * seg1: %d\n",
++			hostapd_get_oper_centr_freq_seg0_idx(conf));
++	wpa_printf(MSG_INFO, "    * oper_chwidth: %d\n",
++			hostapd_get_oper_chwidth(conf));
++
+ 	for (i = 0; i < iface->num_bss; i++) {
+ 		struct hostapd_data *hapd = iface->bss[i];
+ 		int ret;
+@@ -621,6 +645,7 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 	uint64_t intval;
+ 	int i, ret = 0;
+ 
++	wpa_printf(MSG_INFO, "ucode: mtk: channel switch for %s\n", iface->phy);
+ 	if (!iface || ucv_type(info) != UC_OBJECT)
+ 		return NULL;
+ 
+@@ -640,7 +665,8 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 	if (errno)
+ 		intval = hostapd_get_oper_chwidth(conf);
+ 	if (intval)
+-		csa.freq_params.bandwidth = 40 << intval;
++		csa.freq_params.bandwidth = 40 <<
++			(intval == CONF_OPER_CHWIDTH_320MHZ ? 3 : intval);
+ 	else
+ 		csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20;
+ 
+@@ -651,6 +677,17 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 	if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
+ 		csa.freq_params.center_freq2 = intval;
+ 
++	wpa_printf(MSG_INFO, "ucode: mtk: switch channel information:\n");
++	wpa_printf(MSG_INFO, "    * freq is %d\n", csa.freq_params.freq);
++	wpa_printf(MSG_INFO, "    * bandwidth is %d\n",
++			csa.freq_params.bandwidth);
++	wpa_printf(MSG_INFO, "    * sec_chan_offset is %d\n",
++			csa.freq_params.sec_channel_offset);
++	wpa_printf(MSG_INFO, "    * center_freq1 is %d\n",
++			csa.freq_params.center_freq1);
++	wpa_printf(MSG_INFO, "    * center_freq2 is %d\n",
++			csa.freq_params.center_freq2);
++
+ 	for (i = 0; i < iface->num_bss; i++)
+ 		ret = hostapd_switch_channel(iface->bss[i], &csa);
+ 
+diff --git a/src/utils/ucode.c b/src/utils/ucode.c
+index 4b6ed3a94..6f82382f3 100644
+--- a/src/utils/ucode.c
++++ b/src/utils/ucode.c
+@@ -110,6 +110,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 	uc_value_t *freq = uc_fn_arg(0);
+ 	uc_value_t *sec = uc_fn_arg(1);
+ 	int width = ucv_uint64_get(uc_fn_arg(2));
++	int bw320_offset = 1;
+ 	int freq_val, center_idx, center_ofs;
+ 	enum oper_chan_width chanwidth;
+ 	enum hostapd_hw_mode hw_mode;
+@@ -147,6 +148,9 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 	case 9:
+ 		width = 3;
+ 		chanwidth = CONF_OPER_CHWIDTH_320MHZ;
++
++		/* bw320_offset is 1 for 320 MHz-1, and 2 for 320 MHz-2 */
++		bw320_offset = ucv_uint64_get(uc_fn_arg(3));
+ 		break;
+ 	default:
+ 		return NULL;
+@@ -178,12 +182,16 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 	ucv_object_add(ret, "hw_mode_str", ucv_get(ucv_string_new(modestr)));
+ 	ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
+ 	ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
++	ucv_object_add(ret, "oper_chwidth", ucv_int64_new(chanwidth));
+ 
+-	if (!sec_channel)
++	if (chanwidth == CONF_OPER_CHWIDTH_USE_HT && !sec_channel) {
++		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(channel));
++		ucv_object_add(ret, "center_freq1", ucv_int64_new(freq_val));
+ 		return ret;
++	}
+ 
+ 	if (freq_val >= 5900)
+-		center_ofs = 0;
++		center_ofs = 32 * (1 - bw320_offset);
+ 	else if (freq_val >= 5745)
+ 		center_ofs = 20;
+ 	else
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+index 397f85bde..542ca25c9 100644
+--- a/wpa_supplicant/ucode.c
++++ b/wpa_supplicant/ucode.c
+@@ -7,6 +7,8 @@
+ #include "wps_supplicant.h"
+ #include "bss.h"
+ #include "ucode.h"
++#include "driver_i.h"
++#include "common/ieee802_11_common.h"
+ 
+ static struct wpa_global *wpa_global;
+ static uc_resource_type_t *global_type, *iface_type;
+@@ -96,6 +98,8 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ {
+ 	const char *state;
+ 	uc_value_t *val;
++	enum oper_chan_width ch_width;
++	int center_freq1, bw320_offset = 1;
+ 
+ 	if (event != EVENT_CH_SWITCH_STARTED)
+ 		return;
+@@ -114,11 +118,42 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ 	uc_value_push(ucv_get(val));
+ 
+ 	if (event == EVENT_CH_SWITCH_STARTED) {
++		center_freq1 = data->ch_switch.cf1;
++
++		switch (data->ch_switch.ch_width) {
++		case CHAN_WIDTH_80:
++			ch_width = CONF_OPER_CHWIDTH_80MHZ;
++			break;
++		case CHAN_WIDTH_80P80:
++			ch_width = CONF_OPER_CHWIDTH_80P80MHZ;
++			break;
++		case CHAN_WIDTH_160:
++			ch_width = CONF_OPER_CHWIDTH_160MHZ;
++			break;
++		case CHAN_WIDTH_320:
++			ch_width = CONF_OPER_CHWIDTH_320MHZ;
++			break;
++		case CHAN_WIDTH_20_NOHT:
++		case CHAN_WIDTH_20:
++		case CHAN_WIDTH_40:
++		default:
++			ch_width = CONF_OPER_CHWIDTH_USE_HT;
++			break;
++		}
++
++		/* Check bandwidth 320 MHz-2 */
++		if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
++		    (center_freq1 == 6265) || center_freq1 == 6585 ||
++		     center_freq1 == 6905)
++			bw320_offset = 2;
++
+ 		ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count));
+ 		ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq));
+ 		ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
+-		ucv_object_add(val, "center_freq1", ucv_int64_new(data->ch_switch.cf1));
++		ucv_object_add(val, "center_freq1", ucv_int64_new(center_freq1));
+ 		ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
++		ucv_object_add(val, "ch_width", ucv_int64_new(ch_width));
++		ucv_object_add(val, "bw320_offset", ucv_int64_new(bw320_offset));
+ 	}
+ 
+ 	ucv_put(wpa_ucode_call(4));
+@@ -212,6 +247,11 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 	struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
+ 	struct wpa_bss *bss;
+ 	uc_value_t *ret, *val;
++	struct wpa_channel_info ci;
++	u8 op_class, channel;
++	enum oper_chan_width ch_width;
++	int center_freq1, bw320_offset = 1, is_24ghz;
++	enum hostapd_hw_mode hw_mode;
+ 
+ 	if (!wpa_s)
+ 		return NULL;
+@@ -224,23 +264,37 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 	bss = wpa_s->current_bss;
+ 	if (bss) {
+ 		int sec_chan = 0;
+-		const u8 *ie;
+-
+-		ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
+-		if (ie && ie[1] >= 2) {
+-			const struct ieee80211_ht_operation *ht_oper;
+-			int sec;
+-
+-			ht_oper = (const void *) (ie + 2);
+-			sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
+-			if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+-				sec_chan = 1;
+-			else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+-				sec_chan = -1;
++
++		hw_mode = ieee80211_freq_to_chan(bss->freq, &channel);
++		is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
++			hw_mode == HOSTAPD_MODE_IEEE80211B;
++
++		wpa_drv_channel_info(wpa_s, &ci);
++		center_freq1 = ci.center_frq1;
++
++		if (bss->freq != center_freq1) {
++			if (is_24ghz)
++				sec_chan = (bss->freq < center_freq1) ? 1 : -1;
++			else
++				sec_chan = (bss->freq / 20) & 1 ? 1 : -1;
++		}
++
++		if (ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
++						  sec_chan, &op_class, &channel))
++			return NULL;
++
++		ch_width = op_class_to_ch_width(op_class);
++		if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
++		    (center_freq1 == 6265) || center_freq1 == 6585 ||
++		     center_freq1 == 6905) {
++			/* Bandwidth 320 MHz-2 */
++			bw320_offset = 2;
+ 		}
+ 
+ 		ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
+ 		ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
++		ucv_object_add(ret, "ch_width", ucv_int64_new(ch_width));
++		ucv_object_add(ret, "bw320_offset", ucv_int64_new(bw320_offset));
+ 	}
+ 
+ #ifdef CONFIG_MESH
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0052-mtk-hostapd-Add-support-for-updating-background-chan.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0052-mtk-hostapd-Add-support-for-updating-background-chan.patch
new file mode 100644
index 0000000..84d8057
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0052-mtk-hostapd-Add-support-for-updating-background-chan.patch
@@ -0,0 +1,341 @@
+From 4bce62337a31d599b1102e782de689552af5549c Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 5 Jul 2023 10:25:01 +0800
+Subject: [PATCH 052/126] mtk: hostapd: Add support for updating background
+ channel by driver
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c                       | 107 ++++++++++++++++++++++++++++-
+ src/ap/dfs.h                       |   3 +
+ src/ap/drv_callbacks.c             |  24 +++++++
+ src/ap/hostapd.h                   |   5 ++
+ src/drivers/driver.h               |  12 ++++
+ src/drivers/driver_nl80211_event.c |   6 ++
+ src/drivers/nl80211_copy.h         |   6 ++
+ 7 files changed, 162 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 66ccb0057..72bb469a6 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -817,11 +817,14 @@ 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);
++	int width = iface->radar_background.new_chwidth;
+ 
+ 	if (!dfs_use_radar_background(iface))
+ 		return;
+ 
++	if (!width)
++		width = hostapd_get_oper_chwidth(iface->conf);
++
+ 	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))
+@@ -986,6 +989,15 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ 		iface->radar_background.temp_ch = 1;
+ 		return 1;
+ 	} else if (dfs_use_radar_background(iface)) {
++		/*
++		 * AP is going to perform CAC, so reset temp_ch to 0,
++		 * when dedicated rx has already started CAC.
++		 */
++		if (iface->radar_background.cac_started) {
++			iface->radar_background.temp_ch = 0;
++			return 0;
++		}
++
+ 		if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
+ 			channel_type = DFS_ANY_CHANNEL;
+ 
+@@ -1126,6 +1138,8 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
+ 	 * ch_switch_notify event is received */
+ 	wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
+ 
++	hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
++
+ 	return 0;
+ }
+ 
+@@ -1177,6 +1191,9 @@ static void hostapd_dfs_update_background_chain(struct hostapd_iface *iface)
+ 	iface->radar_background.secondary_channel = sec;
+ 	iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+ 	iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
++	/* if main channel do not require dfs, then set temp_ch = 1 */
++	if (!hostapd_is_dfs_required(iface))
++		iface->radar_background.temp_ch = 1;
+ 
+ 	wpa_printf(MSG_DEBUG,
+ 		   "%s: setting background chain to chan %d (%d MHz)",
+@@ -1199,6 +1216,10 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+ 	u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+ 	int ret;
+ 
++	if (iface->radar_background.new_chwidth) {
++		hostapd_set_oper_chwidth(iface->conf, iface->radar_background.new_chwidth);
++		iface->radar_background.new_chwidth = 0;
++	}
+ 	ret = hostapd_dfs_request_channel_switch(iface, iface->radar_background.channel,
+ 						 iface->radar_background.freq,
+ 						 iface->radar_background.secondary_channel,
+@@ -1221,6 +1242,52 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+ }
+ 
+ 
++static void
++hostapd_dfs_background_expand(struct hostapd_iface *iface, int chan_width)
++{
++	struct hostapd_hw_modes *mode = iface->current_mode;
++	struct hostapd_channel_data *chan;
++	int i, channel, width = channel_width_to_int(chan_width);
++
++	if (iface->conf->channel - iface->radar_background.channel == width / 5)
++		channel = iface->radar_background.channel;
++	else if (iface->radar_background.channel - iface->conf->channel == width / 5)
++		channel = iface->conf->channel;
++	else
++		return;
++
++	for (i = 0; i < mode->num_channels; i++) {
++		chan = &mode->channels[i];
++		if (chan->chan == channel)
++			break;
++	}
++
++	if (i == mode->num_channels || !dfs_is_chan_allowed(chan, width / 10))
++		return;
++
++	switch (chan_width) {
++	case CHAN_WIDTH_20_NOHT:
++	case CHAN_WIDTH_20:
++		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_USE_HT;
++		break;
++	case CHAN_WIDTH_40:
++		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_80MHZ;
++		break;
++	case CHAN_WIDTH_80:
++		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_160MHZ;
++		break;
++	default:
++		return;
++	}
++
++	iface->radar_background.freq = channel * 5 + 5000;
++	iface->radar_background.channel = channel;
++	iface->radar_background.centr_freq_seg0_idx = channel + width / 5 - 2;
++	iface->radar_background.secondary_channel = 1;
++	iface->radar_background.expand_ch = 0;
++}
++
++
+ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ 			     int ht_enabled, int chan_offset, int chan_width,
+ 			     int cf1, int cf2)
+@@ -1263,6 +1330,10 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ 					return 0;
+ 
+ 				iface->radar_background.temp_ch = 0;
++
++				if (iface->radar_background.expand_ch)
++					hostapd_dfs_background_expand(iface, chan_width);
++
+ 				return hostapd_dfs_start_channel_switch_background(iface);
+ 			}
+ 
+@@ -1293,6 +1364,8 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ 		}
+ 	} else if (hostapd_dfs_is_background_event(iface, freq)) {
+ 		iface->radar_background.cac_started = 0;
++		iface->radar_background.temp_ch = 0;
++		iface->radar_background.expand_ch = 0;
+ 		hostapd_dfs_update_background_chain(iface);
+ 	}
+ 
+@@ -1426,6 +1499,9 @@ hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
+ 	    iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
+ 		return 0;
+ 
++	iface->radar_background.temp_ch = 0;
++	iface->radar_background.expand_ch = 0;
++
+ 	/* Check if CSA in progress */
+ 	if (hostapd_csa_in_progress(iface))
+ 		return 0;
+@@ -1662,6 +1738,35 @@ int hostapd_is_dfs_required(struct hostapd_iface *iface)
+ }
+ 
+ 
++int hostapd_dfs_background_chan_update(struct hostapd_iface *iface, int freq,
++				       int ht_enabled, int chan_offset, int chan_width,
++				       int cf1, int cf2, bool expand)
++{
++	switch (chan_width) {
++	case CHAN_WIDTH_80:
++		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_80MHZ;
++		break;
++	case CHAN_WIDTH_160:
++		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_160MHZ;
++		break;
++	default:
++		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_USE_HT;
++		break;
++	};
++
++	iface->radar_background.freq = freq;
++	iface->radar_background.channel = (freq - 5000) / 5;
++	iface->radar_background.centr_freq_seg0_idx = (cf1 - 5000) / 5;
++	iface->radar_background.centr_freq_seg1_idx = cf2 ? (cf2 - 5000) / 5 : 0;
++	if (expand) {
++		iface->radar_background.temp_ch = 1;
++		iface->radar_background.expand_ch = 1;
++	}
++
++	return 0;
++}
++
++
+ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ 			  int ht_enabled, int chan_offset, int chan_width,
+ 			  int cf1, int cf2)
+diff --git a/src/ap/dfs.h b/src/ap/dfs.h
+index 25ba29ca1..a1a2be5ec 100644
+--- a/src/ap/dfs.h
++++ b/src/ap/dfs.h
+@@ -30,6 +30,9 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+ 			     int ht_enabled,
+ 			     int chan_offset, int chan_width, int cf1, int cf2);
++int hostapd_dfs_background_chan_update(struct hostapd_iface *iface, int freq,
++				       int ht_enabled, int chan_offset, int chan_width,
++				       int cf1, int cf2, bool expand);
+ int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
+ 				 int ht_enabled, int chan_offset, int chan_width,
+ 				 int cf1, int cf2, u32 state);
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 1d40943d6..1fa27bf80 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2270,6 +2270,18 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
+ 			      radar->cf1, radar->cf2);
+ }
+ 
++
++static void hostapd_event_dfs_background_chan_update(struct hostapd_data *hapd,
++						     struct dfs_event *radar, bool expand)
++{
++	wpa_printf(MSG_DEBUG, "DFS background channel %s to %d MHz",
++		   expand ? "expand" : "update", radar->freq);
++	hostapd_dfs_background_chan_update(hapd->iface, radar->freq, radar->ht_enabled,
++					   radar->chan_offset, radar->chan_width,
++					   radar->cf1, radar->cf2, expand);
++}
++
++
+ static void hostapd_event_dfs_sta_cac_skipped(struct hostapd_data *hapd,
+ 					      struct dfs_event *radar)
+ {
+@@ -2705,6 +2717,18 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ 		hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
+ 		hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
+ 		break;
++	case EVENT_DFS_BACKGROUND_CHAN_UPDATE:
++		if (!data)
++			break;
++		hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
++		hostapd_event_dfs_background_chan_update(hapd, &data->dfs_event, false);
++		break;
++	case EVENT_DFS_BACKGROUND_CHAN_EXPAND:
++		if (!data)
++			break;
++		hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
++		hostapd_event_dfs_background_chan_update(hapd, &data->dfs_event, true);
++		break;
+ 	case EVENT_DFS_STA_CAC_SKIPPED:
+ 		if (!data)
+ 			break;
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 994e4681e..574a5ba1d 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -654,6 +654,11 @@ struct hostapd_iface {
+ 		unsigned int temp_ch:1;
+ 		/* CAC started on radar offchain */
+ 		unsigned int cac_started:1;
++		/* Main chain should expand its width according to the
++		 * current offchain channel after CAC detection on radar offchain.
++		 */
++		unsigned int expand_ch:1;
++		int new_chwidth;
+ 	} radar_background;
+ 
+ 	u16 hw_flags;
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 6fc79f659..8457f4703 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5987,6 +5987,18 @@ enum wpa_event_type {
+ 	 * The channel in the notification is now marked as usable.
+ 	 */
+ 	EVENT_DFS_STA_CAC_EXPIRED,
++
++	/**
++	 * EVENT_DFS_BACKGROUND_CHAN_UPDATE - Notification that background
++	 * channel has been updated.
++	 */
++	EVENT_DFS_BACKGROUND_CHAN_UPDATE,
++
++	/**
++	 * EVENT_DFS_BACKGROUND_CHAN_EXPAND - Notification that background
++	 * channel has been updated and operating channel should expand its width.
++	 */
++	EVENT_DFS_BACKGROUND_CHAN_EXPAND,
+ };
+ 
+ 
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 8ba479861..35ee939b7 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -2539,6 +2539,12 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
+ 	case NL80211_RADAR_CAC_STARTED:
+ 		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
+ 		break;
++	case NL80211_RADAR_BACKGROUND_CHAN_UPDATE:
++		wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_UPDATE, &data);
++		break;
++	case NL80211_RADAR_BACKGROUND_CHAN_EXPAND:
++		wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_EXPAND, &data);
++		break;
+ 	case NL80211_RADAR_STA_CAC_SKIPPED:
+ 		wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
+ 		break;
+diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
+index 622fa71fd..d425c797c 100644
+--- a/src/drivers/nl80211_copy.h
++++ b/src/drivers/nl80211_copy.h
+@@ -6843,6 +6843,10 @@ enum nl80211_smps_mode {
+  *	applicable for ETSI dfs domain where pre-CAC is valid for ever.
+  * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
+  *	should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
++ * @NL80211_RADAR_BACKGROUND_CHAN_UPDATE: background channel is updated by the
++ *	driver.
++ * @NL80211_RADAR_BACKGROUND_CHAN_EXPAND: background channel is updated by the
++ *	driver and required to expand main operating channel.
+  * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
+  *	when receiving CSA/assoc resp
+  * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
+@@ -6855,6 +6859,8 @@ enum nl80211_radar_event {
+ 	NL80211_RADAR_NOP_FINISHED,
+ 	NL80211_RADAR_PRE_CAC_EXPIRED,
+ 	NL80211_RADAR_CAC_STARTED,
++	NL80211_RADAR_BACKGROUND_CHAN_UPDATE,
++	NL80211_RADAR_BACKGROUND_CHAN_EXPAND,
+ 	NL80211_RADAR_STA_CAC_SKIPPED,
+ 	NL80211_RADAR_STA_CAC_EXPIRED,
+ };
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0052-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0052-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch
deleted file mode 100644
index 757c28b..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0052-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch
+++ /dev/null
@@ -1,647 +0,0 @@
-From e64468ed944634f41f7f305e7460b6a696b00127 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Sat, 3 Jun 2023 17:12:15 +0800
-Subject: [PATCH 052/104] mtk: hostapd: add connac3 PHY MURU manual mode config
- support
-
-This commit supports read the following two formats to set MU/RU manual
-mode:
-1. hostapd_cli -i <intf> raw set_muru_manual_config=<field>:<value>
-2. hostapd_cli -i <intf> set_mu <field> <value>
-
-For the <field>, we support the following field:
-1. ul_comm_user_cnt/dl_comm_user_cnt: set the number of user
-2. ul_comm_bw/dl_comm_bw: set the bandwith
-3. ul_user_ru_alloc/dl_user_ru_alloc: set the RU band idx and RU
-allocate idx
-4. ul_user_mcs/dl_user_mcs: set the mcs for each user
-5. ul_user_ssAlloc_raru: set the number of ss for each user
-6. ul_comm_gi_ltf: set the combinations of gi and ltf for UL only.
-7. dl_comm_toneplan: fix ru toneplan allocation
-8. dl_comm_ack_policy: fix station ack policy
-9. update : trigger driver to send mcu command to set muru manual mode.
-
-For the value of each field, please check wiki to learn the details:
-https://wiki.mediatek.inc/display/GWKB/muru_mancfg_user_guide
-
-For the fields that mt76 support to use, we will update in this wiki:
-https://wiki.mediatek.inc/pages/viewpage.action?pageId=1271741116
-
-Please noted that this commit is only for connac 3 gen chips. If this
-feature is to be used in other generations, the following actions must
-be taken:
-1. Different data structue needs to be defined for different
-generations, e.g. connac4_muru_comm, connac4_muru_dl.
-2. hostapd_ctrl_iface_set_mu() shall be modified.
-3. A new code level configuration shall be defined to differentiate the
-code flow that different generations will go through.
----
- hostapd/ctrl_iface.c         | 237 +++++++++++++++++++++++++++++++----
- src/ap/ap_config.h           |   1 +
- src/ap/ap_drv_ops.c          |   4 +-
- src/ap/ap_drv_ops.h          |   2 +-
- src/ap/hostapd.c             |   2 +-
- src/common/mtk_vendor.h      | 166 +++++++++++++++++++++++-
- src/drivers/driver.h         |   2 +-
- src/drivers/driver_nl80211.c |  21 ++--
- 8 files changed, 391 insertions(+), 44 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 88475b321..ed383df7d 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -3775,7 +3775,6 @@ hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *cmd, char *buf,
- 	}
- }
- 
--
- #ifdef CONFIG_NAN_USD
- 
- static int hostapd_ctrl_nan_publish(struct hostapd_data *hapd, char *cmd,
-@@ -4062,21 +4061,61 @@ fail:
- #endif /* CONFIG_NAN_USD */
- 
- 
-+static int
-+hostapd_parse_argument_helper(char *value, u16 **ptr_input)
-+{
-+#define MAX_MU_CTRL_NUM 17
-+	u16 *input;
-+	char *endptr;
-+	int cnt = 0;
-+
-+	input = os_zalloc(MAX_MU_CTRL_NUM * sizeof(u16));
-+	if (input == NULL) {
-+		wpa_printf(MSG_ERROR, "Failed to allocate memory.\n");
-+		return -1;
-+	}
-+	while (value) {
-+		u8 val = strtol(value, &endptr, 10);
-+
-+		if (value != endptr) {
-+			input[cnt++] = val;
-+			value = os_strchr(endptr, ':');
-+			if (value)
-+				value++;
-+		} else {
-+			break;
-+		}
-+	}
-+
-+	*ptr_input = input;
-+	return cnt;
-+}
-+
-+#define MURU_CFG_DEPENDENCE_CHECK(_val, _mask) do {				\
-+		if ((le_to_host32(_val) & (_mask)) != _mask) {			\
-+			wpa_printf(MSG_ERROR, "Set %s first\n", #_mask);	\
-+			goto fail;						\
-+		}								\
-+	} while(0)
-+
- static int
- hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
--					 char *buf, size_t buflen)
-+			  char *buf, size_t buflen)
- {
- 	char *pos, *config, *value;
--	u8 mode;
-+	u8 i;
-+	int cnt = 0, ret;
-+	u16 *val;
-+	struct connac3_muru *muru;
-+	struct connac3_muru_dl *dl;
-+	struct connac3_muru_ul *ul;
-+	struct connac3_muru_comm *comm;
- 
- 	config = cmd;
- 	pos = os_strchr(config, ' ');
--	if (pos == NULL)
--		return -1;
--	*pos++ = '\0';
-+	if (pos != NULL)
-+		*pos++ = '\0';
- 
--	if(pos == NULL)
--		return -1;
- 	value = pos;
- 
- 	if (os_strcmp(config, "onoff") == 0) {
-@@ -4086,24 +4125,167 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
- 			return -1;
- 		}
- 		hapd->iconf->mu_onoff = (u8) mu;
--		mode = MU_CTRL_ONOFF;
--	} else if (os_strcmp(config, "ul_user_cnt") == 0) {
--		mode = MU_CTRL_UL_USER_CNT;
--		wpa_printf(MSG_ERROR, "ul_user_cnt:%d\n", (u8)atoi(value));
--	} else if (os_strcmp(config, "dl_user_cnt") == 0) {
--		mode = MU_CTRL_DL_USER_CNT;
--		wpa_printf(MSG_ERROR, "dl_user_cnt:%d\n", (u8)atoi(value));
--	} else {
--		wpa_printf(MSG_ERROR,
--			"Unsupported parameter %s for SET_MU", config);
--		return -1;
-+
-+		if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) == 0)
-+			return os_snprintf(buf, buflen, "OK\n");
-+		else
-+			goto fail;
- 	}
- 
--	if(hostapd_drv_mu_ctrl(hapd, mode, (u8)atoi(value)) == 0) {
--		return os_snprintf(buf, buflen, "OK\n");
-+	if (hapd->iconf->muru_config == NULL)
-+		hapd->iconf->muru_config = os_zalloc(sizeof(struct connac3_muru));
-+
-+	muru = hapd->iconf->muru_config;
-+	dl = &muru->dl;
-+	ul = &muru->ul;
-+	comm = &muru->comm;
-+
-+	if (os_strncmp(config, "update", 6) == 0) {
-+		ret = hostapd_drv_mu_ctrl(hapd, MU_CTRL_UPDATE);
-+
-+		os_free(hapd->iconf->muru_config);
-+		hapd->iconf->muru_config = NULL;
-+
-+		if (ret)
-+			goto fail;
-+	} else if (os_strcmp(config, "ul_comm_user_cnt") == 0) {
-+		ul->user_num = (u8)atoi(value);
-+		comm->ppdu_format |= MURU_PPDU_HE_TRIG;
-+		comm->sch_type |= MURU_OFDMA_SCH_TYPE_UL;
-+		muru->cfg_comm |= host_to_le32(MURU_COMM_SET);
-+		muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_TOTAL_USER_CNT);
-+	} else if (os_strcmp(config, "dl_comm_user_cnt") == 0) {
-+		dl->user_num = (u8)atoi(value);
-+		comm->ppdu_format |= MURU_PPDU_HE_MU;
-+		comm->sch_type |= MURU_OFDMA_SCH_TYPE_DL;
-+		muru->cfg_comm |= host_to_le32(MURU_COMM_SET);
-+		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_TOTAL_USER_CNT);
-+	} else if (os_strcmp(config, "dl_comm_bw") == 0) {
-+		dl->bw = (u8)atoi(value);
-+		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_BW);
-+	} else if (os_strcmp(config, "ul_comm_bw") == 0) {
-+		ul->bw = (u8)atoi(value);
-+		muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_BW);
-+	} else if (os_strcmp(config, "dl_user_ru_alloc") == 0) {
-+		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
-+		cnt = hostapd_parse_argument_helper(value, &val);
-+		if (cnt == -1)
-+			goto fail;
-+		if (cnt != (dl->user_num * 2))
-+			goto para_fail;
-+		for (i = 0; i < dl->user_num; i++) {
-+			dl->usr[i].ru_alloc_seg = (val[2 * i] & 0x1);
-+			dl->usr[i].ru_allo_ps160 = ((val[2 * i] & 0x2) >> 1);
-+			dl->usr[i].ru_idx = val[(2 * i) + 1];
-+		}
-+		os_free(val);
-+		muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_RU_ALLOC);
-+	} else if (os_strcmp(config, "ul_user_ru_alloc") == 0) {
-+		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
-+		cnt = hostapd_parse_argument_helper(value, &val);
-+		if (cnt == -1)
-+			goto fail;
-+		if (cnt != (ul->user_num * 2))
-+			goto para_fail;
-+		for (i = 0; i < ul->user_num; i++) {
-+			ul->usr[i].ru_alloc_seg = (val[2 * i] & 0x1);
-+			ul->usr[i].ru_allo_ps160 = ((val[2 * i] & 0x2) >> 1);
-+			ul->usr[i].ru_idx = val[(2 * i) + 1];
-+		}
-+		os_free(val);
-+		muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_RU_ALLOC);
-+	} else if (os_strcmp(config, "dl_user_mcs") == 0) {
-+		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
-+		cnt = hostapd_parse_argument_helper(value, &val);
-+		if (cnt == -1)
-+			goto fail;
-+		if (cnt != dl->user_num)
-+			goto para_fail;
-+		for (i = 0; i < cnt; i++)
-+			dl->usr[i].mcs = (u8) val[i];
-+		os_free(val);
-+		muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_MCS);
-+	} else if (os_strcmp(config, "ul_user_mcs") == 0) {
-+		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
-+		cnt = hostapd_parse_argument_helper(value, &val);
-+		if (cnt == -1)
-+			goto fail;
-+		if (cnt != ul->user_num)
-+			goto para_fail;
-+		for (i = 0; i < cnt; i++)
-+			ul->usr[i].mcs = (u8) val[i];
-+		os_free(val);
-+		muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_MCS);
-+	} else if (os_strcmp(config, "dl_user_cod") == 0) {
-+		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_TOTAL_USER_CNT);
-+		cnt = hostapd_parse_argument_helper(value, &val);
-+		if (cnt == -1)
-+			goto fail;
-+		if (cnt != dl->user_num)
-+			goto para_fail;
-+		for (i = 0; i < cnt; i++)
-+			dl->usr[i].ldpc = (u8) val[i];
-+		os_free(val);
-+		muru->cfg_dl |= host_to_le32(MURU_FIXED_USER_DL_COD);
-+	} else if (os_strcmp(config, "ul_user_cod") == 0) {
-+		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
-+		cnt = hostapd_parse_argument_helper(value, &val);
-+		if (cnt == -1)
-+			goto fail;
-+		if (cnt != ul->user_num)
-+			goto para_fail;
-+		for (i = 0; i < cnt; i++)
-+			ul->usr[i].ldpc = (u8) val[i];
-+		os_free(val);
-+		muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_COD);
-+	} else if (os_strcmp(config, "ul_user_ssAlloc_raru") == 0) {
-+		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_ul, MURU_FIXED_UL_TOTAL_USER_CNT);
-+		cnt = hostapd_parse_argument_helper(value, &val);
-+		if (cnt == -1)
-+			goto fail;
-+		if (cnt != ul->user_num)
-+			goto para_fail;
-+		for (i = 0; i < cnt; i++)
-+			ul->usr[i].nss = (u8) val[i];
-+		os_free(val);
-+		muru->cfg_ul |= host_to_le32(MURU_FIXED_USER_UL_NSS);
-+	} else if (os_strcmp(config, "dl_comm_gi") == 0) {
-+		dl->gi = (u8)atoi(value);
-+		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_GI);
-+	} else if (os_strcmp(config, "dl_comm_ltf") == 0) {
-+		dl->ltf = (u8)atoi(value);
-+		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_LTF);
-+	} else if (os_strcmp(config, "ul_comm_gi_ltf") == 0) {
-+		ul->gi_ltf = (u8)atoi(value);
-+		muru->cfg_ul |= host_to_le32(MURU_FIXED_UL_GILTF);
-+	} else if (os_strcmp(config, "dl_comm_ack_policy") == 0) {
-+		dl->ack_policy = (u8)atoi(value);
-+		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_ACK_PLY);
-+	} else if (os_strcmp(config, "dl_comm_toneplan") == 0) {
-+		MURU_CFG_DEPENDENCE_CHECK(muru->cfg_dl, MURU_FIXED_DL_BW);
-+		cnt = hostapd_parse_argument_helper(value, &val);
-+		if (cnt == -1)
-+			goto fail;
-+		i = pow(2, dl->bw);
-+		if (cnt != i)
-+			goto para_fail;
-+		for (i = 0; i < cnt; i++)
-+			dl->ru[i] = host_to_le16(val[i]);
-+		os_free(val);
-+		muru->cfg_dl |= host_to_le32(MURU_FIXED_DL_TONE_PLAN);
- 	} else {
--		return -1;
-+		wpa_printf(MSG_ERROR,
-+			   "Unsupported parameter %s for SET_MU", config);
-+		goto fail;
- 	}
-+
-+	return os_snprintf(buf, buflen, "OK\n");
-+
-+para_fail:
-+	os_free(val);
-+	wpa_printf(MSG_ERROR, "Incorrect input number\n");
-+fail:
-+	return os_snprintf(buf, buflen, "FAIL\n");
- }
- 
- 
-@@ -5148,8 +5330,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 		reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
- 							  reply_size);
- 	} else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
--		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply,
--							  reply_size);
-+		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply, reply_size);
- 	} else if (os_strncmp(buf, "GET_MU", 6) == 0) {
- 		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
- 	} else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
-@@ -5175,6 +5356,14 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 	} else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
- 		reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
- 							reply, reply_size);
-+	} else if (os_strncmp(buf, "set_muru_manual_config=", 23) == 0) {
-+		// Replace first ':' with a single space ' '
-+		char *pos = buf + 23;
-+
-+		pos = os_strchr(pos, ':');
-+		if (pos)
-+			*pos = ' ';
-+		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 23, reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index d995b8d9c..3827a8fc8 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1291,6 +1291,7 @@ struct hostapd_config {
- 	u8 ibf_enable;
- 	u8 dfs_detect_mode;
- 	u8 amsdu;
-+	void *muru_config;
- };
- 
- enum three_wire_mode {
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 8878db380..116bc4ceb 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1270,11 +1270,11 @@ int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
- 	return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
- }
- 
--int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val)
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode)
- {
- 	if (!hapd->driver || !hapd->driver->mu_ctrl)
- 		return 0;
--	return hapd->driver->mu_ctrl(hapd->drv_priv, mode, val);
-+	return hapd->driver->mu_ctrl(hapd->drv_priv, mode, hapd->iconf);
- }
- 
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index f77d07da0..84b41881a 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -153,7 +153,7 @@ int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
- int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
- 					  const int *threshold);
- int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
--int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode, u8 val);
-+int hostapd_drv_mu_ctrl(struct hostapd_data *hapd, u8 mode);
- int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff);
- int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd);
- int hostapd_drv_ibf_ctrl(struct hostapd_data *hapd);
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 5fd46d53d..d1ee0764b 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2700,7 +2700,7 @@ dfs_offload:
- 	if (hostapd_drv_configure_edcca_threshold(hapd,
- 						  hapd->iconf->edcca_threshold) < 0)
- 		goto fail;
--	if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF, hapd->iconf->mu_onoff) < 0)
-+	if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) < 0)
- 		goto fail;
- 	if (hostapd_drv_three_wire_ctrl(hapd) < 0)
- 		goto fail;
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 99371bf73..e140de60b 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -199,8 +199,11 @@ enum mtk_vendor_attr_mu_ctrl {
- 
- 	MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
- 	MTK_VENDOR_ATTR_MU_CTRL_DUMP,
--	MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE,
--	MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL,
-+	/**
-+	 * The above attrs are also used by connac 2. It is best not to modify the
-+	 * above data structure.
-+	 */
-+	MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
- 
- 	/* keep last */
- 	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
-@@ -275,8 +278,163 @@ struct amnt_resp_data {
- };
- 
- enum {
-+	MU_CTRL_UPDATE,
- 	MU_CTRL_ONOFF,
--	MU_CTRL_DL_USER_CNT,
--	MU_CTRL_UL_USER_CNT,
- };
-+
-+struct connac3_muru_comm {
-+	u8 pda_pol;
-+	u8 band;
-+	u8 spe_idx;
-+	u8 proc_type;
-+
-+	le16 mlo_ctrl;
-+	u8 sch_type;
-+	u8 ppdu_format;
-+	u8 ac;
-+	u8 _rsv[3];
-+};
-+
-+struct connac3_muru_dl {
-+	u8 user_num;
-+	u8 tx_mode;
-+	u8 bw;
-+	u8 gi;
-+
-+	u8 ltf;
-+	u8 mcs;
-+	u8 dcm;
-+	u8 cmprs;
-+
-+	le16 ru[16];
-+
-+	u8 c26[2];
-+	u8 ack_policy;
-+	u8 tx_power;
-+
-+	le16 mu_ppdu_duration;
-+	u8 agc_disp_order;
-+	u8 _rsv1;
-+
-+	u8 agc_disp_pol;
-+	u8 agc_disp_ratio;
-+	le16 agc_disp_linkMFG;
-+
-+	le16 prmbl_punc_bmp;
-+	u8 _rsv2[2];
-+
-+	struct {
-+		le16 wlan_idx;
-+		u8 ru_alloc_seg;
-+		u8 ru_idx;
-+		u8 ldpc;
-+		u8 nss;
-+		u8 mcs;
-+		u8 mu_group_idx;
-+		u8 vht_groud_id;
-+		u8 vht_up;
-+		u8 he_start_stream;
-+		u8 he_mu_spatial;
-+		le16 tx_power_alpha;
-+		u8 ack_policy;
-+		u8 ru_allo_ps160;
-+	} usr[16];
-+};
-+
-+struct connac3_muru_ul {
-+	u8 user_num;
-+	u8 tx_mode;
-+
-+	u8 ba_type;
-+	u8 _rsv;
-+
-+	u8 bw;
-+	u8 gi_ltf;
-+	le16 ul_len;
-+
-+	le16 trig_cnt;
-+	u8 pad;
-+	u8 trig_type;
-+
-+	le16 trig_intv;
-+	u8 trig_ta[ETH_ALEN];
-+	le16 ul_ru[16];
-+
-+	u8 c26[2];
-+	le16 agc_disp_linkMFG;
-+
-+	u8 agc_disp_mu_len;
-+	u8 agc_disp_pol;
-+	u8 agc_disp_ratio;
-+	u8 agc_disp_pu_idx;
-+
-+	struct {
-+		le16 wlan_idx;
-+		u8 ru_alloc_seg;
-+		u8 ru_idx;
-+		u8 ldpc;
-+		u8 nss;
-+		u8 mcs;
-+		u8 target_rssi;
-+		le32 trig_pkt_size;
-+		u8 ru_allo_ps160;
-+		u8 _rsv2[3];
-+	} usr[16];
-+};
-+
-+struct connac3_muru_dbg {
-+	/* HE TB RX Debug */
-+	le32 rx_hetb_nonsf_en_bitmap;
-+	le32 rx_hetb_cfg[2];
-+};
-+
-+struct connac3_muru {
-+	le32 cfg_comm;
-+	le32 cfg_dl;
-+	le32 cfg_ul;
-+	le32 cfg_dbg;
-+
-+	struct connac3_muru_comm comm;
-+	struct connac3_muru_dl dl;
-+	struct connac3_muru_ul ul;
-+	struct connac3_muru_dbg dbg;
-+};
-+
-+#define MURU_OFDMA_SCH_TYPE_DL	BIT(0)
-+#define MURU_OFDMA_SCH_TYPE_UL	BIT(1)
-+#define MURU_PPDU_HE_TRIG	BIT(2)
-+#define MURU_PPDU_HE_MU		BIT(3)
-+
-+/* Common Config */
-+#define MURU_COMM_PPDU_FMT	BIT(0)
-+#define MURU_COMM_BAND		BIT(2)
-+#define MURU_COMM_WMM		BIT(3)
-+#define MURU_COMM_SPE_IDX	BIT(4)
-+#define MURU_COMM_SET		(MURU_COMM_PPDU_FMT | MURU_COMM_BAND | \
-+				 MURU_COMM_WMM | MURU_COMM_SPE_IDX)
-+
-+/* DL Common config */
-+#define MURU_FIXED_DL_BW		BIT(0)
-+#define MURU_FIXED_DL_GI		BIT(1)
-+#define MURU_FIXED_DL_TONE_PLAN		BIT(3)
-+#define MURU_FIXED_DL_TOTAL_USER_CNT	BIT(4)
-+#define MURU_FIXED_DL_LTF		BIT(5)
-+#define MURU_FIXED_DL_ACK_PLY		BIT(9)
-+
-+/* DL Per User Config */
-+#define MURU_FIXED_USER_DL_COD		BIT(17)
-+#define MURU_FIXED_USER_DL_MCS		BIT(18)
-+#define MURU_FIXED_USER_DL_RU_ALLOC	BIT(20)
-+
-+/* UL Common Config */
-+#define MURU_FIXED_UL_TOTAL_USER_CNT	BIT(4)
-+#define MURU_FIXED_UL_BW		BIT(5)
-+#define MURU_FIXED_UL_GILTF		BIT(6)
-+
-+/* UL Per User Config */
-+#define MURU_FIXED_USER_UL_COD		BIT(18)
-+#define MURU_FIXED_USER_UL_MCS		BIT(19)
-+#define MURU_FIXED_USER_UL_NSS		BIT(20)
-+#define MURU_FIXED_USER_UL_RU_ALLOC	BIT(21)
-+
- #endif /* MTK_VENDOR_H */
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 3be4562e7..1c0c38e24 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5240,7 +5240,7 @@ struct wpa_driver_ops {
- 	 * @priv: Private driver interface data
- 	 *
- 	 */
--	 int (*mu_ctrl)(void *priv, u8 mode, u8 val);
-+	 int (*mu_ctrl)(void *priv, u8 mode, void *config);
- 	 int (*mu_dump)(void *priv, u8 *mu_onoff);
- 
- 	/**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 035a477e2..aeb755b11 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -13982,12 +13982,13 @@ fail:
- 
- 
- #ifdef CONFIG_IEEE80211AX
--static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
-+static int nl80211_mu_ctrl(void *priv, u8 mode, void *config)
- {
- 	struct i802_bss *bss = priv;
- 	struct wpa_driver_nl80211_data *drv = bss->drv;
- 	struct nl_msg *msg;
- 	struct nlattr *data;
-+	struct hostapd_config *cfg = config;
- 	int ret = -ENOBUFS;
- 
- 	if (!drv->mtk_mu_vendor_cmd_avail) {
-@@ -14004,17 +14005,16 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
- 
- 	switch (mode) {
- 	case MU_CTRL_ONOFF:
--			if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, val))
--				goto fail;
-+		if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff))
-+			goto fail;
- 		break;
--	case MU_CTRL_UL_USER_CNT:
--	case MU_CTRL_DL_USER_CNT:
--			if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_MODE, mode) ||
--			    nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_OFDMA_VAL, val))
--				goto fail;
-+	case MU_CTRL_UPDATE:
-+		if (nla_put(msg, MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
-+			    sizeof(struct connac3_muru), cfg->muru_config))
-+			goto fail;
- 		break;
- 	default:
--		wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode !");
-+		wpa_printf(MSG_ERROR, "nl80211: Wrong mu mode %u!", mode);
- 		ret = -EINVAL;
- 		goto fail;
- 	}
-@@ -14022,9 +14022,8 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, u8 val)
- 	nla_nest_end(msg, data);
- 
- 	ret = send_and_recv_cmd(drv, msg);
--	if(ret){
-+	if (ret)
- 		wpa_printf(MSG_ERROR, "Failed to set mu_ctrl. ret=%d (%s)", ret, strerror(-ret));
--	}
- 	return ret;
- 
- fail:
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0053-mtk-hostapd-Add-HE-capabilities-check.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0053-mtk-hostapd-Add-HE-capabilities-check.patch
deleted file mode 100644
index c9fc8b7..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0053-mtk-hostapd-Add-HE-capabilities-check.patch
+++ /dev/null
@@ -1,52 +0,0 @@
-From 2dd00ec0a8231bd8c6893f9517875ad94022f9b2 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Fri, 9 Jun 2023 09:03:05 +0800
-Subject: [PATCH 053/104] mtk: hostapd: Add HE capabilities check
-
-Add HE capabilities check.
-Since "HE capabilities" check has been removed by driver,
-add the support for "HE capabilities" check in hostapd.
----
- src/ap/hw_features.c | 26 ++++++++++++++++++++++++++
- 1 file changed, 26 insertions(+)
-
-diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
-index 672e43a10..a35c5974a 100644
---- a/src/ap/hw_features.c
-+++ b/src/ap/hw_features.c
-@@ -709,6 +709,32 @@ static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
- #ifdef CONFIG_IEEE80211AX
- static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface)
- {
-+	struct hostapd_hw_modes *mode = iface->current_mode;
-+	struct he_capabilities *he_cap = &mode->he_capab[IEEE80211_MODE_AP];
-+	struct hostapd_config *conf = iface->conf;
-+
-+#define HE_CAP_CHECK(hw_cap, field, phy_idx, cfg_cap)					\
-+	do {									\
-+		if (cfg_cap && !(hw_cap[phy_idx] & field)) {	\
-+			wpa_printf(MSG_ERROR, "Driver does not support configured" \
-+				     " HE capability [%s]", #field);		\
-+			return 0;						\
-+		}								\
-+	} while (0)
-+
-+	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_LDPC_CODING_IN_PAYLOAD,
-+		     HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX,
-+		     conf->he_phy_capab.he_ldpc);
-+	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMER_CAPAB,
-+		     HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX,
-+		     conf->he_phy_capab.he_su_beamformer);
-+	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMEE_CAPAB,
-+		     HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX,
-+		     conf->he_phy_capab.he_su_beamformee);
-+	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_MU_BEAMFORMER_CAPAB,
-+		     HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX,
-+		     conf->he_phy_capab.he_mu_beamformer);
-+
- 	return 1;
- }
- #endif /* CONFIG_IEEE80211AX */
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0053-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0053-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem.patch
new file mode 100644
index 0000000..5c5547a
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0053-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem.patch
@@ -0,0 +1,290 @@
+From fe8560c4ccea682bb471816a1e5200ef7f3cde60 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 2 Aug 2023 19:00:34 +0800
+Subject: [PATCH 053/126] mtk: hostapd: add zwdfs mode ctrl for eagle efem
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/config_file.c             |  2 ++
+ hostapd/ctrl_iface.c              | 30 +++++++++++++++++++++++++++
+ src/ap/ap_config.h                |  6 ++++++
+ src/ap/ap_drv_ops.c               | 14 +++++++++++++
+ src/ap/ap_drv_ops.h               |  1 +
+ src/ap/dfs.c                      |  6 ++++++
+ src/common/mtk_vendor.h           | 12 +++++++++++
+ src/drivers/driver.h              |  7 +++++++
+ src/drivers/driver_nl80211.c      | 34 +++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |  1 +
+ src/drivers/driver_nl80211_capa.c |  3 +++
+ 11 files changed, 116 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index dc05738db..b941ec5fa 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -3662,6 +3662,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		conf->acs_exclude_6ghz_non_psc = atoi(pos);
+ 	} else if (os_strcmp(buf, "enable_background_radar") == 0) {
+ 		conf->enable_background_radar = atoi(pos);
++	} else if (os_strcmp(buf, "background_radar_mode") == 0) {
++		conf->background_radar_mode = atoi(pos);
+ 	} else if (os_strcmp(buf, "min_tx_power") == 0) {
+ 		int val = atoi(pos);
+ 
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 53924b265..61d69902d 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4919,6 +4919,33 @@ hostapd_ctrl_iface_dump_amnt(struct hostapd_data *hapd, char *cmd,
+ 		return pos - buf;
+ }
+ 
++static int
++hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cmd,
++					     char *buf, size_t buflen)
++{
++	struct hostapd_iface *iface = hapd->iface;
++	char *pos, *param;
++
++	param = os_strchr(cmd, ' ');
++	if (!param)
++		return -1;
++	*param++ = '\0';
++
++	pos = os_strstr(param, "mode=");
++	if (!pos)
++		return -1;
++
++	if (os_strncmp(pos + 5, "cert", 4) == 0)
++		iface->conf->background_radar_mode = BACKGROUND_RADAR_CERT_MODE;
++	else if (os_strncmp(pos + 5, "normal", 6) == 0)
++		iface->conf->background_radar_mode = BACKGROUND_RADAR_NORMAL_MODE;
++
++	if (hostapd_drv_background_radar_mode(hapd) < 0)
++		return -1;
++
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -5568,6 +5595,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		if (pos)
+ 			*pos = ' ';
+ 		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 23, reply, reply_size);
++	} else if (os_strncmp(buf, "SET_BACKGROUND_RADAR_MODE", 25) == 0) {
++		reply_len = hostapd_ctrl_iface_set_background_radar_mode(hapd, buf + 25,
++									 reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 0c51d2ab9..cffde344f 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1101,6 +1101,7 @@ struct hostapd_config {
+ 	bool hw_mode_set;
+ 	int acs_exclude_6ghz_non_psc;
+ 	int enable_background_radar;
++	int background_radar_mode;
+ 	enum {
+ 		LONG_PREAMBLE = 0,
+ 		SHORT_PREAMBLE = 1
+@@ -1359,6 +1360,11 @@ enum three_wire_mode {
+ 		NUM_THREE_WIRE_MODE - 1
+ };
+ 
++enum background_radar_mode {
++	BACKGROUND_RADAR_NORMAL_MODE,
++	BACKGROUND_RADAR_CERT_MODE,
++};
++
+ enum dfs_mode {
+ 	DFS_DETECT_MODE_DISABLE,
+ 	DFS_DETECT_MODE_AP_ENABLE,
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index ccd0cb939..6f8f329b0 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1401,3 +1401,17 @@ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_
+ 		return 0;
+ 	return hapd->driver->amnt_dump(hapd->drv_priv, amnt_idx, amnt_dump_buf);
+ }
++
++int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->driver->background_radar_mode ||
++	    !(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_RADAR_BACKGROUND) ||
++	    !hapd->iface->conf->enable_background_radar)
++		return 0;
++	if (hapd->iconf->background_radar_mode > BACKGROUND_RADAR_CERT_MODE) {
++		wpa_printf(MSG_INFO, "Invalid value for background radar mode\n");
++		return 0;
++	}
++	return hapd->driver->background_radar_mode(hapd->drv_priv,
++						   hapd->iconf->background_radar_mode);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 659154f56..53075ea94 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -171,6 +171,7 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
+ 
+ int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
+ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
++int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 72bb469a6..db26f6536 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -986,6 +986,9 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ 		if (res < 0)
+ 			return res;
+ 
++		if (hostapd_drv_background_radar_mode(iface->bss[0]) < 0)
++			return -1;
++
+ 		iface->radar_background.temp_ch = 1;
+ 		return 1;
+ 	} else if (dfs_use_radar_background(iface)) {
+@@ -1026,6 +1029,9 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ 		iface->radar_background.secondary_channel = sec;
+ 		iface->radar_background.centr_freq_seg0_idx = cf1;
+ 		iface->radar_background.centr_freq_seg1_idx = cf2;
++
++		if (hostapd_drv_background_radar_mode(iface->bss[0]) < 0)
++			return -1;
+ 	}
+ 
+ 	return 0;
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index f0abcb6b1..3fb4c38f3 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -16,6 +16,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
+ 	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
+ 	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
++	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
+ };
+ 
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -247,6 +248,17 @@ enum mtk_vendor_attr_bss_color_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_background_radar_ctrl {
++	MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL,
++	MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
++};
++
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+ 
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 8457f4703..35c02937d 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5346,6 +5346,13 @@ struct wpa_driver_ops {
+ 	* @amnt_dump_buf: Buffer to print
+ 	*/
+ 	int (*amnt_dump)(void *priv, u8 amnt_idx, u8 *amnt_dump_buf);
++
++	/**
++	 * background_radar_mode - set background radar mode
++	 * @priv: Private driver interface data
++	 * @background_radar_mode: background radar mode
++	 */
++	int (*background_radar_mode)(void *priv, u8 background_radar_mode);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 8a8f8abe8..2d2b47456 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -15080,6 +15080,39 @@ fail:
+ 	return -ENOBUFS;
+ }
+ 
++static int nl80211_background_radar_mode(void *priv, const u8 background_radar_mode)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	/* Prepare nl80211 cmd */
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_background_radar_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting background radar mode");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL) ||
++	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE, background_radar_mode)) {
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to set background radar mode. ret=%d (%s) ",
++			   ret, strerror(-ret));
++	}
++	return ret;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+ 	.desc = "Linux nl80211/cfg80211",
+@@ -15257,4 +15290,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.ap_trigtype = nl80211_ap_trigtype,
+ 	.amnt_set = nl80211_amnt_set,
+ 	.amnt_dump = nl80211_amnt_dump,
++	.background_radar_mode = nl80211_background_radar_mode,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index adc1b9bf7..e9aae8d14 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -208,6 +208,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_bss_color_vendor_cmd_avail:1;
+ 	unsigned int mtk_rfeatures_vendor_cmd_avail:1;
+ 	unsigned int mtk_amnt_vendor_cmd_avail:1;
++	unsigned int mtk_background_radar_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 4a6ff0f28..fd8c7b0ad 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1164,6 +1164,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
+ 					drv->mtk_rfeatures_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL:
++					drv->mtk_background_radar_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0054-mtk-hostapd-Fix-background-channel-overlapping-opera.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0054-mtk-hostapd-Fix-background-channel-overlapping-opera.patch
deleted file mode 100644
index e14b996..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0054-mtk-hostapd-Fix-background-channel-overlapping-opera.patch
+++ /dev/null
@@ -1,64 +0,0 @@
-From cc4db7ad22853f72f43128f96e5d4edcb7c245a1 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 054/104] mtk: hostapd: 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 f5794753e..8be953287 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -814,6 +814,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)
- {
-@@ -1141,6 +1155,8 @@ static void hostapd_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,
-@@ -1375,6 +1391,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;
-@@ -1502,6 +1519,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.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0054-mtk-hostapd-add-support-enable-disable-preamble-punc.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0054-mtk-hostapd-add-support-enable-disable-preamble-punc.patch
new file mode 100644
index 0000000..a26dcf8
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0054-mtk-hostapd-add-support-enable-disable-preamble-punc.patch
@@ -0,0 +1,375 @@
+From d57c8c29e6c88c609ff3c3770f5d6f0eed151761 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 21 Sep 2023 10:29:46 +0800
+Subject: [PATCH 054/126] mtk: hostapd: add support enable/disable preamble
+ puncture from mtk vendor command
+
+This commit supports two ways to enable/disable preamble puncture
+feature.
+
+1. Add new hostapd configuration pp_mode. The possible value could be
+1 to 3. When the value is 0, it means that the firmware will turn off
+the pp algorithm. When the value is 1, it means that the firmware will
+enable the pp algorithm, allowing the algorithm to determine whether pp
+could be applied on each txcmd. When the value is 2, it means that pp
+feature is manually configured by the user. Please noted that for
+current implementation, the default configuration is 0.
+
+2. $ hostapd_cli -i <intf_name> raw set_pp mode val
+The argument val could be 0 for PP feature disabled or 1 to configure
+PP feature as auto mode.
+
+This commit also let user check whether pp feature is enabled by
+hostapd_cli command. The usage shows as below:
+$ hostapd_cli -i <intf_name> raw get_pp mode
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ hostapd/config_file.c             | 12 +++++++
+ hostapd/ctrl_iface.c              | 59 +++++++++++++++++++++++++++++++
+ src/ap/ap_config.c                |  1 +
+ src/ap/ap_config.h                |  7 ++++
+ src/ap/ap_drv_ops.c               |  9 +++++
+ src/ap/ap_drv_ops.h               |  1 +
+ src/ap/hostapd.c                  |  2 ++
+ src/common/mtk_vendor.h           | 12 +++++++
+ src/drivers/driver.h              |  6 ++++
+ src/drivers/driver_nl80211.c      | 49 +++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |  1 +
+ src/drivers/driver_nl80211_capa.c |  3 ++
+ 12 files changed, 162 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index b941ec5fa..fe92540c4 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5446,6 +5446,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 	} else if (os_strcmp(buf, "punct_bitmap") == 0) {
+ 		if (get_u16(pos, line, &conf->punct_bitmap))
+ 			return 1;
++		conf->punct_bitmap = atoi(pos);
++		conf->pp_mode = PP_MANUAL_MODE;
+ 	} else if (os_strcmp(buf, "punct_acs_threshold") == 0) {
+ 		int val = atoi(pos);
+ 
+@@ -5537,6 +5539,16 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 			return 1;
+ 		}
+ 		conf->amsdu = val;
++	} else if (os_strcmp(buf, "pp_mode") == 0) {
++		int val = atoi(pos);
++
++		if ((val != PP_MANUAL_MODE && conf->punct_bitmap) ||
++		    val < PP_DISABLE || val > PP_MANUAL_MODE) {
++			wpa_printf(MSG_ERROR, "Line %d: invalid pp_mode value",
++				   line);
++			return 1;
++		}
++		conf->pp_mode = (u8) val;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 61d69902d..be47bd492 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4946,6 +4946,59 @@ hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cm
+ 	return os_snprintf(buf, buflen, "OK\n");
+ }
+ 
++static int
++hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
++			  size_t buflen)
++{
++	char *pos, *config, *value;
++
++	config = cmd;
++	pos = os_strchr(config, ' ');
++	if (pos == NULL)
++		return -1;
++	*pos++ = '\0';
++
++	if (pos == NULL)
++		return -1;
++	value = pos;
++
++	if (os_strcmp(config, "mode") == 0) {
++		int val = atoi(value);
++
++		if (val < PP_DISABLE || val > PP_AUTO_MODE) {
++			wpa_printf(MSG_ERROR, "Invalid value for set_pp");
++			return -1;
++		}
++		hapd->iconf->pp_mode = (u8) val;
++		if (hostapd_drv_pp_mode_set(hapd) != 0)
++			return -1;
++	} else {
++		wpa_printf(MSG_ERROR,
++			   "Unsupported parameter %s for set_pp", config);
++		return -1;
++	}
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
++static int
++hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
++			  size_t buflen)
++{
++	char *pos, *end;
++
++	pos = buf;
++	end = buf + buflen;
++
++	if (os_strcmp(cmd, "mode") == 0) {
++		return os_snprintf(pos, end - pos, "pp_mode: %d\n",
++				   hapd->iconf->pp_mode);
++	} else {
++		wpa_printf(MSG_ERROR,
++			   "Unsupported parameter %s for get_pp", cmd);
++		return -1;
++	}
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -5587,6 +5640,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 	} else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
+ 		reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
+ 							reply, reply_size);
++	} else if (os_strncmp(buf, "set_pp", 6) == 0) {
++		reply_len = hostapd_ctrl_iface_set_pp(hapd, buf + 7, reply,
++						      reply_size);
++	} else if (os_strncmp(buf, "get_pp", 6) == 0) {
++		reply_len = hostapd_ctrl_iface_get_pp(hapd, buf + 7, reply,
++						      reply_size);
+ 	} else if (os_strncmp(buf, "set_muru_manual_config=", 23) == 0) {
+ 		// Replace first ':' with a single space ' '
+ 		char *pos = buf + 23;
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 3a12de3cd..8b98d6170 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -312,6 +312,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 	conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
+ 	conf->ibf_enable = IBF_DEFAULT_ENABLE;
+ 	conf->amsdu = 1;
++	conf->pp_mode = PP_DISABLE;
+ 
+ 	hostapd_set_and_check_bw320_offset(conf, 0);
+ 
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index cffde344f..a8002fd1a 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1346,6 +1346,7 @@ struct hostapd_config {
+ 	u8 dfs_detect_mode;
+ 	u8 amsdu;
+ 	void *muru_config;
++	u8 pp_mode;
+ };
+ 
+ enum three_wire_mode {
+@@ -1398,6 +1399,12 @@ enum mtk_vendor_attr_edcca_ctrl_mode {
+ 	EDCCA_CTRL_NUM,
+ };
+ 
++enum pp_mode {
++	PP_DISABLE = 0,
++	PP_AUTO_MODE,
++	PP_MANUAL_MODE,
++};
++
+ #define EDCCA_DEFAULT_COMPENSATION -6
+ #define EDCCA_MIN_COMPENSATION -126
+ #define EDCCA_MAX_COMPENSATION 126
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 6f8f329b0..dc772ea31 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1415,3 +1415,12 @@ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
+ 	return hapd->driver->background_radar_mode(hapd->drv_priv,
+ 						   hapd->iconf->background_radar_mode);
+ }
++
++int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->driver->pp_mode_set ||
++	    hapd->iconf->pp_mode > PP_AUTO_MODE)
++		return 0;
++	return hapd->driver->pp_mode_set(hapd->drv_priv,
++					 hapd->iconf->pp_mode);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 53075ea94..9a477a40e 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -172,6 +172,7 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
+ int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
+ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
+ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
++int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 7da5c3afa..7ebdf4eb0 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2771,6 +2771,8 @@ dfs_offload:
+ 		goto fail;
+ 	if (hostapd_drv_amsdu_ctrl(hapd) < 0)
+ 		goto fail;
++	if (hostapd_drv_pp_mode_set(hapd) < 0)
++		goto fail;
+ 
+ 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ 		   iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 3fb4c38f3..0a96c842e 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -17,6 +17,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
+ 	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
+ 	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
++	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
+ };
+ 
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -259,6 +260,17 @@ enum mtk_vendor_attr_background_radar_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_pp_ctrl {
++	MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_PP_MODE,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_PP_CTRL,
++	MTK_VENDOR_ATTR_PP_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
++};
++
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+ 
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 35c02937d..ab93405fb 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5353,6 +5353,12 @@ struct wpa_driver_ops {
+ 	 * @background_radar_mode: background radar mode
+ 	 */
+ 	int (*background_radar_mode)(void *priv, u8 background_radar_mode);
++	/**
++	 * pp_mode_set - Set preamble puncture operation mode
++	 * @priv: Private driver interface data
++	 * @pp_mode: Value is defined in enum pp_mode
++	 */
++	int (*pp_mode_set)(void *priv, const u8 pp_mode);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 2d2b47456..a1e2ee091 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -156,6 +156,11 @@ amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
+ 	[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
+ };
+ 
++static struct nla_policy
++pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
++	[MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
++};
++
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+ 	struct nl_sock *handle;
+@@ -15113,6 +15118,49 @@ static int nl80211_background_radar_mode(void *priv, const u8 background_radar_m
+ 	return ret;
+ }
+ 
++static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_pp_vendor_cmd_avail) {
++		wpa_printf(MSG_DEBUG,
++			   "nl80211: Driver does not support setting preamble puncture");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_PP_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_PP_MODE, pp_mode);
++
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set pp_enable. ret=%d (%s)",
++			   ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+ 	.desc = "Linux nl80211/cfg80211",
+@@ -15291,4 +15339,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.amnt_set = nl80211_amnt_set,
+ 	.amnt_dump = nl80211_amnt_dump,
+ 	.background_radar_mode = nl80211_background_radar_mode,
++	.pp_mode_set = nl80211_pp_mode_set,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index e9aae8d14..707bb7fe4 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -209,6 +209,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_rfeatures_vendor_cmd_avail:1;
+ 	unsigned int mtk_amnt_vendor_cmd_avail:1;
+ 	unsigned int mtk_background_radar_vendor_cmd_avail:1;
++	unsigned int mtk_pp_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index fd8c7b0ad..d082b83d5 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1167,6 +1167,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL:
+ 					drv->mtk_background_radar_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_PP_CTRL:
++					drv->mtk_pp_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0055-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0055-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch
deleted file mode 100644
index 4f42bfa..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0055-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From 0232fabaf99094f319d03ab818cb0c847b6727c2 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 5 Jul 2023 10:47:20 +0800
-Subject: [PATCH 055/104] mtk: hostapd: Fix hostapd_dfs_start_cac log
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c | 6 ++++--
- 1 file changed, 4 insertions(+), 2 deletions(-)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 8be953287..7adaf81ac 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1664,9 +1664,11 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
- 	/* TODO: How to check CAC time for ETSI weather channels? */
- 	iface->dfs_cac_ms = 60000;
- 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
--		"freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
-+		"freq=%d chan=%d chan_offset=%d width=%s seg0=%d "
- 		"seg1=%d cac_time=%ds%s",
--		freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
-+		freq, (freq - 5000) / 5, chan_offset,
-+		channel_width_to_string(chan_width),
-+		(cf1 - 5000) / 5, cf2 ? (cf2 - 5000) / 5 : 0,
- 		iface->dfs_cac_ms / 1000,
- 		hostapd_dfs_is_background_event(iface, freq) ?
- 		" (background)" : "");
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0055-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0055-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch
new file mode 100644
index 0000000..09e54c8
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0055-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch
@@ -0,0 +1,247 @@
+From fb2f823022007f3b72042a38a35a3829fecf9d9f Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Wed, 22 Nov 2023 21:41:34 +0800
+Subject: [PATCH 055/126] mtk: hostapd: add no_beacon vendor command for cert
+
+Add the vendor command to disable/enable beacon
+
+[Usage]
+hostapd_cli -i <interface> no_beacon <value>
+ <value>
+ 0: enable beacon
+ 1: disable beacon
+
+Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
+---
+ hostapd/ctrl_iface.c              | 21 +++++++++++++++++++
+ hostapd/hostapd_cli.c             |  7 +++++++
+ src/ap/ap_drv_ops.c               |  8 ++++++++
+ src/ap/ap_drv_ops.h               |  1 +
+ src/common/mtk_vendor.h           | 12 +++++++++++
+ src/drivers/driver.h              |  7 +++++++
+ src/drivers/driver_nl80211.c      | 34 +++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |  1 +
+ src/drivers/driver_nl80211_capa.c |  3 +++
+ 9 files changed, 94 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index be47bd492..8187cfb3c 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4999,6 +4999,24 @@ hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ 	}
+ }
+ 
++static int
++hostapd_ctrl_iface_disable_beacon(struct hostapd_data *hapd, char *value,
++				  char *buf, size_t buflen)
++{
++	int disable_beacon = atoi(value);
++
++	if (disable_beacon < 0) {
++		wpa_printf(MSG_ERROR, "Invalid value for beacon ctrl");
++		return -1;
++	}
++
++	if (hostapd_drv_beacon_ctrl(hapd, !disable_beacon) == 0)
++		return os_snprintf(buf, buflen, "OK\n");
++	else
++		return -1;
++
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -5657,6 +5675,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 	} else if (os_strncmp(buf, "SET_BACKGROUND_RADAR_MODE", 25) == 0) {
+ 		reply_len = hostapd_ctrl_iface_set_background_radar_mode(hapd, buf + 25,
+ 									 reply, reply_size);
++	} else if (os_strncmp(buf, "NO_BEACON ", 10) == 0) {
++		reply_len = hostapd_ctrl_iface_disable_beacon(hapd, buf + 10, reply,
++							      reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index e43d515d5..9a6a742c1 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1482,6 +1482,11 @@ static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
+ 	return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
+ }
+ 
++static int hostapd_cli_cmd_disable_beacon(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "NO_BEACON", 1, argc, argv);
++}
+ 
+ #ifdef CONFIG_DPP
+ 
+@@ -1901,6 +1906,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 		"<value> [0-15] bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0)"},
+ 	{ "get_mu", hostapd_cli_cmd_get_mu, NULL,
+ 		" = show mu onoff value in 0-15 bitmap"},
++	{ "no_beacon", hostapd_cli_cmd_disable_beacon, NULL,
++		"<value> 0: Enable beacon, 1: Disable beacon"},
+ #ifdef CONFIG_DPP
+ 	{ "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_drv_ops.c b/src/ap/ap_drv_ops.c
+index dc772ea31..d4be02b77 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1424,3 +1424,11 @@ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
+ 	return hapd->driver->pp_mode_set(hapd->drv_priv,
+ 					 hapd->iconf->pp_mode);
+ }
++
++int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
++{
++	if (!hapd->driver || !hapd->driver->beacon_ctrl)
++		return 0;
++	return hapd->driver->beacon_ctrl(hapd->drv_priv, beacon_mode);
++}
++
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 9a477a40e..2ee4ebd04 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -173,6 +173,7 @@ int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_ma
+ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
+ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
+ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
++int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 0a96c842e..261994b8a 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -18,6 +18,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
+ 	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
+ 	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
++	MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
+ };
+ 
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -271,6 +272,17 @@ enum mtk_vendor_attr_pp_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_beacon_ctrl {
++	MTK_VENDOR_ATTR_BEACON_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_BEACON_CTRL_MODE,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_BEACON_CTRL,
++	MTK_VENDOR_ATTR_BEACON_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
++};
++
+ #define CSI_MAX_COUNT 256
+ #define ETH_ALEN 6
+ 
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index ab93405fb..b24caae8a 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5269,6 +5269,13 @@ struct wpa_driver_ops {
+ 	 int (*mu_ctrl)(void *priv, u8 mode, void *config);
+ 	 int (*mu_dump)(void *priv, u8 *mu_onoff);
+ 
++	/**
++	 * beacon_ctrl - ctrl on off for beacon
++	 * @priv: Private driver interface data
++	 *
++	 */
++	int (*beacon_ctrl)(void *priv, u8 beacon_mode);
++
+ 	/**
+ 	 * three_wire_ctrl - set three_wire_ctrl mode
+ 	 * @priv: Private driver interface data
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index a1e2ee091..10e5c837b 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14136,6 +14136,39 @@ static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
+ 
+ 	return ret;
+ }
++static int nl80211_beacon_ctrl(void *priv, u8 beacon_mode)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_beacon_ctrl_vendor_cmd_avail) {
++		wpa_printf(MSG_ERROR,
++			   "nl80211: Driver does not support setting beacon control");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL) ||
++		!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++		nla_put_u8(msg, MTK_VENDOR_ATTR_BEACON_CTRL_MODE, beacon_mode)) {
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++
++	nla_nest_end(msg, data);
++
++	ret = send_and_recv_cmd(drv, msg);
++
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set beacon_ctrl. ret=%d (%s)", ret, strerror(-ret));
++
++	return ret;
++}
++
+ #endif /* CONFIG_IEEE80211AX */
+ 
+ 
+@@ -15309,6 +15342,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.set_4addr_mode = nl80211_set_4addr_mode,
+ 	.mu_ctrl = nl80211_mu_ctrl,
+ 	.mu_dump = nl80211_mu_dump,
++	.beacon_ctrl = nl80211_beacon_ctrl,
+ #ifdef CONFIG_DPP
+ 	.dpp_listen = nl80211_dpp_listen,
+ #endif /* CONFIG_DPP */
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 707bb7fe4..9866c221c 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -210,6 +210,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_amnt_vendor_cmd_avail:1;
+ 	unsigned int mtk_background_radar_vendor_cmd_avail:1;
+ 	unsigned int mtk_pp_vendor_cmd_avail:1;
++	unsigned int mtk_beacon_ctrl_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index d082b83d5..30f89b687 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1170,6 +1170,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_PP_CTRL:
+ 					drv->mtk_pp_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL :
++					drv->mtk_beacon_ctrl_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0056-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0056-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch
deleted file mode 100644
index 6943e83..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0056-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch
+++ /dev/null
@@ -1,62 +0,0 @@
-From 942808028d207776f1a4dbe678166282fb272b37 Mon Sep 17 00:00:00 2001
-From: Michael Lee <michael-cy.lee@mediatek.com>
-Date: Thu, 13 Jul 2023 13:14:26 +0800
-Subject: [PATCH 056/104] mtk: hostapd: Check the bridge after ioctl
- SIOCBRADDIF failed
-
-If ioctl returns EBUSY on command SIOCBRADDIF, the interface might
-already be bridged by others, and linux_br_add_if should not indicate an
-error in the case.
-
-This patch checks whether the interface is correctly brigded when ioctl
-returns EBUSY.
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- src/drivers/linux_ioctl.c | 16 +++++++++++++++-
- 1 file changed, 15 insertions(+), 1 deletion(-)
-
-diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c
-index 29abc0c59..73d27825d 100644
---- a/src/drivers/linux_ioctl.c
-+++ b/src/drivers/linux_ioctl.c
-@@ -150,7 +150,8 @@ int linux_br_del(int sock, const char *brname)
- int linux_br_add_if(int sock, const char *brname, const char *ifname)
- {
- 	struct ifreq ifr;
--	int ifindex;
-+	int ifindex, ret;
-+	char in_br[IFNAMSIZ];
- 
- 	ifindex = if_nametoindex(ifname);
- 	if (ifindex == 0)
-@@ -165,6 +166,17 @@ int linux_br_add_if(int sock, const char *brname, const char *ifname)
- 
- 		wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge "
- 			   "%s: %s", ifname, brname, strerror(errno));
-+
-+		/* If ioctl returns -EBUSY when adding interface into bridge,
-+		 * the interface might already be added by netifd, so here we
-+		 * check whether the interface is currently on the right
-+		 * bridge. */
-+		if(errno == EBUSY && linux_br_get(in_br, ifname) == 0 &&
-+	           os_strcmp(in_br, brname) == 0)
-+			ret = 0;
-+		else
-+			ret = -1;
-+
- 		errno = saved_errno;
- 
- 		/* If ioctl() returns EBUSY when adding an interface into the
-@@ -175,6 +187,8 @@ int linux_br_add_if(int sock, const char *brname, const char *ifname)
- 		if (errno != EBUSY || linux_br_get(in_br, ifname) != 0 ||
- 		    os_strcmp(in_br, brname) != 0)
- 			return -1;
-+
-+		return ret;
- 	}
- 
- 	return 0;
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0056-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0056-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch
new file mode 100644
index 0000000..b3dc7f1
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0056-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch
@@ -0,0 +1,64 @@
+From c384a286a5d163d324b1901b1c446df1c6dfdbe2 Mon Sep 17 00:00:00 2001
+From: "amlendu.mishra" <amlendu.mishra@mediatek.com>
+Date: Wed, 13 Dec 2023 18:13:01 +0530
+Subject: [PATCH 056/126] mtk: hostapd: WPS added change to configure AP PIN
+ lock timout
+
+added config paramter ap_pin_lockout_time to configure
+AP PIN timeout from hosatpd.conf
+
+Signed-off-by: amlendu.mishra <amlendu.mishra@mediatek.com>
+---
+ hostapd/config_file.c | 2 ++
+ src/ap/ap_config.h    | 1 +
+ src/ap/wps_hostapd.c  | 9 ++++++---
+ 3 files changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index fe92540c4..7ab2c6827 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4283,6 +4283,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		bss->wps_independent = atoi(pos);
+ 	} else if (os_strcmp(buf, "ap_setup_locked") == 0) {
+ 		bss->ap_setup_locked = atoi(pos);
++	} else if (os_strcmp(buf, "ap_pin_lockout_time") == 0) {
++		bss->ap_pin_lockout_time = atoi(pos);
+ 	} else if (os_strcmp(buf, "uuid") == 0) {
+ 		if (uuid_str2bin(pos, bss->uuid)) {
+ 			wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index a8002fd1a..b66f79d31 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -505,6 +505,7 @@ struct hostapd_bss_config {
+ #ifdef CONFIG_WPS
+ 	int wps_independent;
+ 	int ap_setup_locked;
++	unsigned int ap_pin_lockout_time;
+ 	u8 uuid[16];
+ 	char *wps_pin_requests;
+ 	char *device_name;
+diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
+index dfc5c3ecb..8a6fc42b2 100644
+--- a/src/ap/wps_hostapd.c
++++ b/src/ap/wps_hostapd.c
+@@ -768,9 +768,12 @@ static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
+ 		eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+ 		wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely");
+ 	} else if (!hapd->conf->ap_setup_locked) {
+-		if (hapd->ap_pin_lockout_time == 0)
+-			hapd->ap_pin_lockout_time = 60;
+-		else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
++		if (hapd->ap_pin_lockout_time == 0) {
++			if (hapd->conf->ap_pin_lockout_time)
++				hapd->ap_pin_lockout_time = hapd->conf->ap_pin_lockout_time;
++			else
++				hapd->ap_pin_lockout_time = 60;
++		} else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
+ 			 (hapd->ap_pin_failures % 3) == 0)
+ 			hapd->ap_pin_lockout_time *= 2;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0057-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0057-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch
deleted file mode 100644
index 9a8f33f..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0057-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From cd4001cf3751979177cefa215f438888397f5bcb Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Fri, 14 Jul 2023 17:19:13 +0800
-Subject: [PATCH 057/104] mtk: hostapd: Update parameter_set_count in MU EDCA
- IE
-
-without this patch, MU EDCA Parameter update count not equal to
-WMM Parameter set count.
----
- src/ap/ieee802_11_he.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
-index 3c6ee72fe..3b6b2041c 100644
---- a/src/ap/ieee802_11_he.c
-+++ b/src/ap/ieee802_11_he.c
-@@ -317,6 +317,9 @@ u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
- 	edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
- 	os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
- 
-+	if (hapd->conf->wmm_enabled)
-+		edca->he_qos_info = hapd->parameter_set_count % 0xf;
-+
- 	wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
- 		    pos, sizeof(*edca));
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0057-mtk-hostapd-remove-chan-freq-list-check-when-scan-re.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0057-mtk-hostapd-remove-chan-freq-list-check-when-scan-re.patch
new file mode 100644
index 0000000..f66edfe
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0057-mtk-hostapd-remove-chan-freq-list-check-when-scan-re.patch
@@ -0,0 +1,44 @@
+From 43f030f950401ccd573e7bc07080107f74f214bd Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 23 Jan 2024 10:52:57 +0800
+Subject: [PATCH 057/126] mtk: hostapd: remove chan/freq list check when scan
+ request and factor calculation
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/acs.c | 12 ------------
+ 1 file changed, 12 deletions(-)
+
+diff --git a/src/ap/acs.c b/src/ap/acs.c
+index 25fec499a..f7e7f15d2 100644
+--- a/src/ap/acs.c
++++ b/src/ap/acs.c
+@@ -599,12 +599,6 @@ static void acs_survey_mode_interference_factor(
+ 		    iface->conf->acs_exclude_dfs)
+ 			continue;
+ 
+-		if (!is_in_chanlist(iface, chan))
+-			continue;
+-
+-		if (!is_in_freqlist(iface, chan))
+-			continue;
+-
+ 		if (chan->max_tx_power < iface->conf->min_tx_power)
+ 			continue;
+ 
+@@ -1370,12 +1364,6 @@ static int * acs_request_scan_add_freqs(struct hostapd_iface *iface,
+ 		     iface->conf->acs_exclude_dfs))
+ 			continue;
+ 
+-		if (!is_in_chanlist(iface, chan))
+-			continue;
+-
+-		if (!is_in_freqlist(iface, chan))
+-			continue;
+-
+ 		if (chan->max_tx_power < iface->conf->min_tx_power)
+ 			continue;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0058-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0058-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch
new file mode 100644
index 0000000..22e91c7
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0058-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch
@@ -0,0 +1,44 @@
+From 03ace053c9dfcbfafec764a894292180f5587c42 Mon Sep 17 00:00:00 2001
+From: "fancy.liu" <fancy.liu@mediatek.com>
+Date: Wed, 1 Nov 2023 19:58:05 +0800
+Subject: [PATCH 058/126] mtk: hostapd: Fix chan_switch to usable DFS channel
+ fail due to ACS
+
+Step and issue:
+1. Enable ACS in hostapd config;
+2. Bootup and then use hostapd_cli cmd switch channel to a DFS channel;
+3. Will do ACS again, and no work on channel specified in step 2.
+
+Root cause:
+When need do DFS-CAC, hostapd will do intf disable, then set the new
+channel into running config settings, and finally enable intf;
+In the test case, new DFS channel is set to runnint config settings, but
+another param acs is still 1 (enable), caused the ACS running when
+intf enabled.
+
+Solution:
+In the hostapd_switch_channel_fallback, need to disable acs if channel
+is valid.
+
+Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
+---
+ src/ap/hostapd.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 7ebdf4eb0..97bc4808e 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4669,6 +4669,9 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+ 
+ 	iface->freq = freq_params->freq;
+ 	iface->conf->channel = freq_params->channel;
++	if (iface->conf->channel != 0) /* If channel not zero, will disable acs. */
++		iface->conf->acs = 0;
++
+ 	iface->conf->secondary_channel = freq_params->sec_channel_offset;
+ 	if (ieee80211_freq_to_channel_ext(freq_params->freq,
+ 					  freq_params->sec_channel_offset, bw,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0058-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0058-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch
deleted file mode 100644
index 07e0656..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0058-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 43840874fe5c56d76612c953d0e31b771818c0d3 Mon Sep 17 00:00:00 2001
-From: mtk20656 <chank.chen@mediatek.com>
-Date: Mon, 24 Jul 2023 11:30:27 +0800
-Subject: [PATCH 058/104] mtk: hostapd: add extension IE list for non-inherit
- IE in mbssid
-
-Certain clients do not scan all non tx profiles due to absence of
-element ID extension list which is mandatory field in non inheritance
-IE. Non inheritance Element ID is followed by extension element ID.
-Length is expected to be mentioned. Currently we do not support any
-extension element and hence filling length as 0.
-
-Signed-off-by: mtk20656 <chank.chen@mediatek.com>
----
- src/ap/ieee802_11.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
- mode change 100644 => 100755 src/ap/ieee802_11.c
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-old mode 100644
-new mode 100755
-index d972a25f1..e42d4e1cc
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -7999,7 +7999,7 @@ static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
- 		else if (hapd->conf->xrates_supported)
- 			ie_count++;
- 		if (ie_count)
--			nontx_profile_len += 4 + ie_count;
-+			nontx_profile_len += 5 + ie_count;
- 
- 		if (len + nontx_profile_len > 255)
- 			break;
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0059-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0059-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch
deleted file mode 100644
index 690dcc0..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0059-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From ab91679c1eeee2c48b871335756601df995e1a19 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 059/104] mtk: hostapd: 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 d1ee0764b..db451387b 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -4569,6 +4569,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.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0059-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0059-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch
new file mode 100644
index 0000000..0be5c0e
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0059-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch
@@ -0,0 +1,122 @@
+From c3f080cfa523b13726b223411cc1bf574000883b Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Thu, 19 Oct 2023 13:38:11 +0800
+Subject: [PATCH 059/126] mtk: hostapd: initialize i802_bss's flink->freq with
+ iface freq.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ src/ap/ap_drv_ops.c          | 6 +++---
+ src/ap/ap_drv_ops.h          | 2 +-
+ src/ap/hostapd.c             | 2 +-
+ src/drivers/driver.h         | 2 +-
+ src/drivers/driver_nl80211.c | 4 ++--
+ wpa_supplicant/driver_i.h    | 2 +-
+ 6 files changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index d4be02b77..011bb37f8 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -371,7 +371,7 @@ int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
+ 	char force_ifname[IFNAMSIZ];
+ 	u8 if_addr[ETH_ALEN];
+ 	return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
+-			      NULL, NULL, force_ifname, if_addr, NULL, 0);
++			      NULL, NULL, force_ifname, if_addr, NULL, 0, hapd->iface->freq);
+ }
+ 
+ 
+@@ -589,13 +589,13 @@ int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
+ int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ 		   const char *ifname, const u8 *addr, void *bss_ctx,
+ 		   void **drv_priv, char *force_ifname, u8 *if_addr,
+-		   const char *bridge, int use_existing)
++		   const char *bridge, int use_existing, int freq)
+ {
+ 	if (hapd->driver == NULL || hapd->driver->if_add == NULL)
+ 		return -1;
+ 	return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
+ 				    bss_ctx, drv_priv, force_ifname, if_addr,
+-				    bridge, use_existing, 1);
++				    bridge, use_existing, 1, freq);
+ }
+ 
+ 
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 2ee4ebd04..8c783610c 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -61,7 +61,7 @@ int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
+ int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ 		   const char *ifname, const u8 *addr, void *bss_ctx,
+ 		   void **drv_priv, char *force_ifname, u8 *if_addr,
+-		   const char *bridge, int use_existing);
++		   const char *bridge, int use_existing, int freq);
+ int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ 		      const char *ifname);
+ int hostapd_if_link_remove(struct hostapd_data *hapd,
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 97bc4808e..f64f17ee6 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -1487,7 +1487,7 @@ int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
+ 				   conf->iface, addr, hapd,
+ 				   &hapd->drv_priv, force_ifname, if_addr,
+ 				   conf->bridge[0] ? conf->bridge : NULL,
+-				   first == -1)) {
++				   first == -1, hapd->iface->freq)) {
+ 			wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
+ 				   MACSTR ")", MAC2STR(hapd->own_addr));
+ 			hapd->interface_added = 0;
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index b24caae8a..8f5d260aa 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -3892,7 +3892,7 @@ struct wpa_driver_ops {
+ 	int (*if_add)(void *priv, enum wpa_driver_if_type type,
+ 		      const char *ifname, const u8 *addr, void *bss_ctx,
+ 		      void **drv_priv, char *force_ifname, u8 *if_addr,
+-		      const char *bridge, int use_existing, int setup_ap);
++		      const char *bridge, int use_existing, int setup_ap, int freq);
+ 
+ 	/**
+ 	 * if_remove - Remove a virtual interface
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 10e5c837b..7cb65a2bc 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -8869,7 +8869,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
+ 				     void *bss_ctx, void **drv_priv,
+ 				     char *force_ifname, u8 *if_addr,
+ 				     const char *bridge, int use_existing,
+-				     int setup_ap)
++				     int setup_ap, int freq)
+ {
+ 	enum nl80211_iftype nlmode;
+ 	struct i802_bss *bss = priv;
+@@ -8989,7 +8989,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
+ 		new_bss->valid_links = 0;
+ 		os_memcpy(new_bss->flink->addr, new_bss->addr, ETH_ALEN);
+ 
+-		new_bss->flink->freq = drv->first_bss->flink->freq;
++		new_bss->flink->freq = (freq == -1) ? drv->first_bss->flink->freq : freq;
+ 		new_bss->ctx = bss_ctx;
+ 		new_bss->added_if = added;
+ 		drv->first_bss->next = new_bss;
+diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
+index 663e16053..624192ebd 100644
+--- a/wpa_supplicant/driver_i.h
++++ b/wpa_supplicant/driver_i.h
+@@ -446,7 +446,7 @@ static inline int wpa_drv_if_add(struct wpa_supplicant *wpa_s,
+ 	if (wpa_s->driver->if_add)
+ 		return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname,
+ 					     addr, bss_ctx, NULL, force_ifname,
+-					     if_addr, bridge, 0, 0);
++					     if_addr, bridge, 0, 0, -1);
+ 	return -1;
+ }
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0060-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0060-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch
deleted file mode 100644
index ccbe766..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0060-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch
+++ /dev/null
@@ -1,45 +0,0 @@
-From 00c2dff4bf8d15c1c84321cef1892009aa32e9ed Mon Sep 17 00:00:00 2001
-From: mtk23510 <rudra.shahi@mediatek.com>
-Date: Fri, 26 May 2023 14:52:35 +0800
-Subject: [PATCH 060/104] mtk: hostapd: Add support for gtk rekeying in hostapd
- cli
-
-Signed-off-by: mtk23510 <rudra.shahi@mediatek.com>
----
- hostapd/hostapd_cli.c | 13 +++++++++++++
- 1 file changed, 13 insertions(+)
-
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 12c580455..e0b175386 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1291,6 +1291,15 @@ static int hostapd_cli_cmd_stop_ap(struct wpa_ctrl *ctrl, int argc,
- }
- 
- 
-+#ifdef CONFIG_TESTING_OPTIONS
-+static int hostapd_cli_cmd_rekey_gtk(struct wpa_ctrl *ctrl, int argc,
-+				      char *argv[])
-+{
-+	return wpa_ctrl_command(ctrl, "REKEY_GTK");
-+}
-+#endif
-+
-+
- static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
- {
- 	char cmd[256];
-@@ -1831,6 +1840,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 	  "= update Beacon frame contents\n"},
- 	{ "stop_ap", hostapd_cli_cmd_stop_ap, NULL,
- 	  "= stop AP\n"},
-+#ifdef CONFIG_TESTING_OPTIONS
-+	{ "rekey_gtk", hostapd_cli_cmd_rekey_gtk, NULL,
-+	  "= rekey gtk\n"},
-+#endif
- 	{ "erp_flush", hostapd_cli_cmd_erp_flush, NULL,
- 	  "= drop all ERP keys"},
- 	{ "log_level", hostapd_cli_cmd_log_level, NULL,
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0060-mtk-hostapd-fix-mld_assoc_link_id.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0060-mtk-hostapd-fix-mld_assoc_link_id.patch
new file mode 100644
index 0000000..79883e2
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0060-mtk-hostapd-fix-mld_assoc_link_id.patch
@@ -0,0 +1,31 @@
+From 69f830f234d58e4bcbdafe70d28c366e87a65c66 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 29 Jan 2024 11:24:28 +0800
+Subject: [PATCH 060/126] mtk: hostapd: fix mld_assoc_link_id
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ src/ap/hostapd.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index f64f17ee6..eefd4c5d3 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4111,11 +4111,9 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ 
+ #ifdef CONFIG_IEEE80211BE
+ 	if (ap_sta_is_mld(hapd, sta)) {
+-		if (sta->mld_assoc_link_id == hapd->mld_link_id) {
+-			mld_assoc_link_id = sta->mld_assoc_link_id;
+-		} else {
++		if (sta->mld_assoc_link_id != hapd->mld_link_id)
+ 			return;
+-		}
++		mld_assoc_link_id = sta->mld_assoc_link_id;
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+         if (mld_assoc_link_id != -2)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0061-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0061-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch
deleted file mode 100644
index 65ba1f0..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0061-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From 2dc6e435eef405ae0cfb69a89c1c8ec7d2852635 Mon Sep 17 00:00:00 2001
-From: Michael Lee <michael-cy.lee@mediatek.com>
-Date: Tue, 11 Jul 2023 14:17:43 +0800
-Subject: [PATCH 061/104] mtk: hostapd: Set WMM and TX queue parameters for
- wpa_supplicant
-
-Since most of the time, wpa_supplicant will be used to setup an STA
-interface, it's default WMM and TX queue parameters should be set for
-STA.
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- wpa_supplicant/config.c | 12 ++++++------
- 1 file changed, 6 insertions(+), 6 deletions(-)
-
-diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
-index c3943355d..7bb57e2ab 100644
---- a/wpa_supplicant/config.c
-+++ b/wpa_supplicant/config.c
-@@ -4720,19 +4720,19 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
- 	const struct hostapd_wmm_ac_params ac_bk =
- 		{ aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
- 	const struct hostapd_wmm_ac_params ac_be =
--		{ aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
-+		{ aCWmin, aCWmin + 2, 3, 0, 0 }; /* best effort traffic */
- 	const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
--		{ aCWmin - 1, aCWmin, 2, 3008 / 32, 0 };
-+		{ aCWmin - 1, aCWmin, 1, 3008 / 32, 0 };
- 	const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
--		{ aCWmin - 2, aCWmin - 1, 2, 1504 / 32, 0 };
-+		{ aCWmin - 2, aCWmin - 1, 1, 1504 / 32, 0 };
- 	const struct hostapd_tx_queue_params txq_bk =
- 		{ 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
- 	const struct hostapd_tx_queue_params txq_be =
--		{ 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0 };
-+		{ 3, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
- 	const struct hostapd_tx_queue_params txq_vi =
--		{ 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30 };
-+		{ 2, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30 };
- 	const struct hostapd_tx_queue_params txq_vo =
--		{ 1, (ecw2cw(aCWmin) + 1) / 4 - 1,
-+		{ 2, (ecw2cw(aCWmin) + 1) / 4 - 1,
- 		  (ecw2cw(aCWmin) + 1) / 2 - 1, 15 };
- 
- #undef ecw2cw
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0061-mtk-wpa_supplicant-correctly-get-assoc-frequency.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0061-mtk-wpa_supplicant-correctly-get-assoc-frequency.patch
new file mode 100644
index 0000000..8877145
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0061-mtk-wpa_supplicant-correctly-get-assoc-frequency.patch
@@ -0,0 +1,25 @@
+From 98c59e85209339abc85968b1e1d1de66d48bc88a Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 19 Oct 2023 10:48:11 +0800
+Subject: [PATCH 061/126] mtk: wpa_supplicant: correctly get assoc frequency
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ src/drivers/driver_nl80211_event.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 35ee939b7..73582aeb0 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -329,6 +329,7 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
+ 			   wpa_ssid_txt(drv->ssid, drv->ssid_len));
+ 	}
+ 
++	drv->assoc_freq = nl80211_get_assoc_freq(drv);
+ 	event.assoc_info.freq = drv->assoc_freq;
+ 	drv->first_bss->flink->freq = drv->assoc_freq;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0062-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0062-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch
deleted file mode 100644
index 502da4c..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0062-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch
+++ /dev/null
@@ -1,78 +0,0 @@
-From eaf45cd7a14dac2d5d601653792d2bc101118585 Mon Sep 17 00:00:00 2001
-From: Michael Lee <michael-cy.lee@mediatek.com>
-Date: Fri, 7 Jul 2023 17:16:11 +0800
-Subject: [PATCH 062/104] mtk: hostapd: Set STA TX queue parameters
- configuration after association
-
-This patch adds the way for wpa_supplicant to set driver's TX queue
-parameters.
-Since STA parses and apply TX queue parameters from AP beacon's WMM IE
-during association, wpa_supplicant set driver's TX queue parameters
-after the association.
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- wpa_supplicant/driver_i.h | 12 ++++++++++++
- wpa_supplicant/events.c   | 16 ++++++++++++++++
- 2 files changed, 28 insertions(+)
-
-diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
-index d01b52bb1..663e16053 100644
---- a/wpa_supplicant/driver_i.h
-+++ b/wpa_supplicant/driver_i.h
-@@ -321,6 +321,18 @@ static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s,
- 	return 0;
- }
- 
-+static inline int wpa_drv_set_tx_queue_params(struct wpa_supplicant *wpa_s,
-+					      int q, int aifs, int cw_min,
-+					      int cw_max, int burst_time)
-+{
-+	int link_id = -1;
-+	if (wpa_s->driver->set_tx_queue_params)
-+		return wpa_s->driver->set_tx_queue_params(wpa_s->drv_priv, q,
-+							  aifs, cw_min, cw_max,
-+							  burst_time, link_id);
-+	return 0;
-+}
-+
- static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s,
- 				    const u8 *data, size_t data_len, int noack,
- 				    unsigned int freq, unsigned int wait)
-diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
-index 2a9342318..8fd2f2049 100644
---- a/wpa_supplicant/events.c
-+++ b/wpa_supplicant/events.c
-@@ -4070,6 +4070,20 @@ out:
- 	return wpa_sm_set_mlo_params(wpa_s->wpa, &wpa_mlo);
- }
- 
-+static void wpa_supplicant_tx_queue_params(struct wpa_supplicant *wpa_s){
-+	struct hostapd_tx_queue_params *p;
-+
-+	for (int i = 0; i < NUM_TX_QUEUES; i++){
-+		p = &wpa_s->conf->tx_queue[i];
-+		if(wpa_drv_set_tx_queue_params(wpa_s, i, p->aifs,
-+						      p->cwmin, p->cwmax,
-+						      p->burst)) {
-+			wpa_printf(MSG_DEBUG, "Failed to set TX queue "
-+				   "parameters for queue %d.", i);
-+			/* Continue anyway */
-+		}
-+	}
-+}
- 
- static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
- 				       union wpa_event_data *data)
-@@ -4399,6 +4413,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
- 
- 	if (wpa_s->current_ssid && wpa_s->current_ssid->enable_4addr_mode)
- 		wpa_supplicant_set_4addr_mode(wpa_s);
-+
-+	wpa_supplicant_tx_queue_params(wpa_s);
- }
- 
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0062-mtk-wpa_supplicant-force-MLD-STA-to-use-SAE-H2E-duri.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0062-mtk-wpa_supplicant-force-MLD-STA-to-use-SAE-H2E-duri.patch
new file mode 100644
index 0000000..4f6a065
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0062-mtk-wpa_supplicant-force-MLD-STA-to-use-SAE-H2E-duri.patch
@@ -0,0 +1,29 @@
+From 63810009871c9dc527fc3a40c3f873bfbf560f7d Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 19 Oct 2023 10:51:55 +0800
+Subject: [PATCH 062/126] mtk: wpa_supplicant: force MLD STA to use SAE H2E
+ during authentication
+
+Otherwise the MLD STA setup will fail with hostapd MLD AP.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ wpa_supplicant/sme.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
+index 443b0b667..afdccf54f 100644
+--- a/wpa_supplicant/sme.c
++++ b/wpa_supplicant/sme.c
+@@ -204,7 +204,7 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
+ 	if (wpa_key_mgmt_sae_ext_key(key_mgmt) &&
+ 	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+ 		use_pt = 1;
+-	if (bss && is_6ghz_freq(bss->freq) &&
++	if (bss && (is_6ghz_freq(bss->freq) || !is_zero_ether_addr(bss->mld_addr)) &&
+ 	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+ 		use_pt = 1;
+ #ifdef CONFIG_SAE_PK
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0063-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0063-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch
deleted file mode 100644
index e1384d9..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0063-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From 7449e88f54fb5e16296399c43c9f758535123cde Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Fri, 1 Sep 2023 15:31:24 +0800
-Subject: [PATCH 063/104] mtk: hostapd: avoid color switch when beacon is not
- set
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/hostapd.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index db451387b..0d4b79b48 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -4707,7 +4707,7 @@ void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap)
- {
- 	struct os_reltime now;
- 
--	if (hapd->cca_in_progress)
-+	if (hapd->cca_in_progress || !hapd->beacon_set_done)
- 		return;
- 
- 	if (os_get_reltime(&now))
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0063-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0063-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch
new file mode 100644
index 0000000..93aafb3
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0063-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch
@@ -0,0 +1,71 @@
+From 5378549cd55834d56c7364268f7849f30e780756 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 11 Dec 2023 17:02:05 +0800
+Subject: [PATCH 063/126] mtk: hostapd: extend ap_get_sta() to find the correct
+ sta
+
+There're still some mld address tranlation issues that need to be dealt
+with on driver side (e.g. RX eapol frames). So add the code that find
+station also with link address and across hapds at the moment.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ src/ap/ieee802_11.c |  1 +
+ src/ap/sta_info.c   | 16 ++++++++++++++++
+ src/ap/sta_info.h   |  1 +
+ 3 files changed, 18 insertions(+)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 4581ae1d4..b87fae929 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -3194,6 +3194,7 @@ static void handle_auth(struct hostapd_data *hapd,
+ 				  mgmt->sa, ETH_ALEN);
+ 			os_memcpy(sta->mld_info.links[link_id].local_addr,
+ 				  hapd->own_addr, ETH_ALEN);
++			os_memcpy(sta->setup_link_addr, mgmt->sa, ETH_ALEN);
+ 		}
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index 51978f45f..d4f4eb913 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -73,6 +73,22 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+ 	s = hapd->sta_hash[STA_HASH(sta)];
+ 	while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
+ 		s = s->hnext;
++
++	if (hapd->conf->mld_ap && !s) {
++		u8 link_id;
++
++		for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
++			struct hostapd_data *h = hostapd_mld_get_link_bss(hapd, link_id);
++
++			if (!h)
++				continue;
++
++			for (s = h->sta_list; s; s = s->next)
++				if (!os_memcmp(s->setup_link_addr, sta, 6))
++					return s;
++		}
++	}
++
+ 	return s;
+ }
+ 
+diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
+index d03d18b48..6b88799e2 100644
+--- a/src/ap/sta_info.h
++++ b/src/ap/sta_info.h
+@@ -96,6 +96,7 @@ struct sta_info {
+ 	struct sta_info *next; /* next entry in sta list */
+ 	struct sta_info *hnext; /* next entry in hash table list */
+ 	u8 addr[6];
++	u8 setup_link_addr[6];
+ 	be32 ipaddr;
+ 	struct dl_list ip6addr; /* list head for struct ip6addr */
+ 	u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0064-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0064-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch
deleted file mode 100644
index 7d6be8a..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0064-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From 8e01f276c2d7be41f3521026c92d8f1bd833865f Mon Sep 17 00:00:00 2001
-From: mtk20656 <chank.chen@mediatek.com>
-Date: Wed, 13 Sep 2023 19:29:51 +0800
-Subject: [PATCH 064/104] mtk: hostapd: 6g bss connect do not consider ht
- operation
-
-Signed-off-by: mtk20656 <chank.chen@mediatek.com>
----
- src/ap/ieee802_11.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
- mode change 100755 => 100644 src/ap/ieee802_11.c
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-old mode 100755
-new mode 100644
-index e42d4e1cc..923cbebcc
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -5591,7 +5591,7 @@ static void handle_assoc(struct hostapd_data *hapd,
- 			set_beacon = true;
- 	}
- 
--	if (update_ht_state(hapd, sta) > 0)
-+	if (!is_6ghz_op_class(hapd->iconf->op_class) && update_ht_state(hapd, sta) > 0)
- 		set_beacon = true;
- 
- 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0064-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0064-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch
new file mode 100644
index 0000000..60e5325
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0064-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch
@@ -0,0 +1,39 @@
+From 016e41e3031fe6c2d5c12f29f47616f7a1b7dabf Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 18 Dec 2023 18:53:35 +0800
+Subject: [PATCH 064/126] mtk: hostapd: update cookie only when noack is unset
+
+This can prevent cookie unmatched problems during setup.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ src/drivers/driver_nl80211.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 7cb65a2bc..4b404f0bb 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -4485,7 +4485,7 @@ send_frame_cmd:
+ 	res = nl80211_send_frame_cmd(bss, freq, wait_time, data, data_len,
+ 				     use_cookie, no_cck, noack, offchanok,
+ 				     csa_offs, csa_offs_len, link_id);
+-	if (!res)
++	if (!res && !noack)
+ 		drv->send_frame_link_id = link_id;
+ 
+ 	return res;
+@@ -9200,8 +9200,8 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss,
+ 			   "cookie 0x%llx", no_ack ? " (no ACK)" : "",
+ 			   (long long unsigned int) cookie);
+ 
+-		if (save_cookie)
+-			drv->send_frame_cookie = no_ack ? (u64) -1 : cookie;
++		if (save_cookie && !no_ack)
++			drv->send_frame_cookie = cookie;
+ 
+ 		if (!wait) {
+ 			 /* There is no need to store this cookie since there
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0065-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0065-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch
deleted file mode 100644
index d87fef0..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0065-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch
+++ /dev/null
@@ -1,96 +0,0 @@
-From 9639b495a347cdd2aadbe2bc2d336b4398518d29 Mon Sep 17 00:00:00 2001
-From: "fancy.liu" <fancy.liu@mediatek.com>
-Date: Sun, 8 Oct 2023 11:50:06 +0800
-Subject: [PATCH 065/104] mtk: hostapd: Add ACS chanlist info in get_config
-
-This patch is used to add ACS chanlist info displaying
-for upper layer application obtaining.
-
-Command format:
-hostapd_cli -i phy0-ap0 get_config
-
-Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
----
- hostapd/ctrl_iface.c | 59 ++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 59 insertions(+)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index ed383df7d..581acc260 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -1058,6 +1058,7 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
- {
- 	int ret;
- 	char *pos, *end;
-+	int i;
- 
- 	pos = buf;
- 	end = buf + buflen;
-@@ -1237,6 +1238,64 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
- 		pos += ret;
- 	}
- 
-+	/* dump chanlist */
-+	if (hapd->iface->conf->acs_ch_list.num > 0) {
-+		ret = os_snprintf(pos, end - pos, "chanlist=");
-+		if (os_snprintf_error(end - pos, ret))
-+			return pos - buf;
-+		pos += ret;
-+
-+		for (i = 0; i < hapd->iface->conf->acs_ch_list.num; i++) {
-+			if (i > 0) {
-+				ret = os_snprintf(pos, end - pos, ", ");
-+				if (os_snprintf_error(end - pos, ret))
-+					return pos - buf;
-+				pos += ret;
-+			}
-+
-+			ret = os_snprintf(pos, end - pos, "%d-%d",
-+				hapd->iface->conf->acs_ch_list.range[i].min,
-+				hapd->iface->conf->acs_ch_list.range[i].max);
-+			if (os_snprintf_error(end - pos, ret))
-+				return pos - buf;
-+			pos += ret;
-+		}
-+
-+		ret = os_snprintf(pos, end - pos, "\n");
-+		if (os_snprintf_error(end - pos, ret))
-+			return pos - buf;
-+		pos += ret;
-+	}
-+
-+	/* dump freqlist */
-+	if (hapd->iface->conf->acs_freq_list.num > 0) {
-+		ret = os_snprintf(pos, end - pos, "freqlist=");
-+		if (os_snprintf_error(end - pos, ret))
-+			return pos - buf;
-+		pos += ret;
-+
-+		for (i = 0; i < hapd->iface->conf->acs_freq_list.num; i++) {
-+			if (i > 0) {
-+				ret = os_snprintf(pos, end - pos, ", ");
-+				if (os_snprintf_error(end - pos, ret))
-+					return pos - buf;
-+				pos += ret;
-+			}
-+
-+			ret = os_snprintf(pos, end - pos, "%d-%d",
-+				hapd->iface->conf->acs_freq_list.range[i].min,
-+				hapd->iface->conf->acs_freq_list.range[i].max);
-+			if (os_snprintf_error(end - pos, ret))
-+				return pos - buf;
-+			pos += ret;
-+		}
-+
-+		ret = os_snprintf(pos, end - pos, "\n");
-+		if (os_snprintf_error(end - pos, ret))
-+			return pos - buf;
-+		pos += ret;
-+	}
-+
- 	return pos - buf;
- }
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0065-mtk-wpa_supplicant-fix-bss-selection-when-setting-ml.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0065-mtk-wpa_supplicant-fix-bss-selection-when-setting-ml.patch
new file mode 100644
index 0000000..1a77564
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0065-mtk-wpa_supplicant-fix-bss-selection-when-setting-ml.patch
@@ -0,0 +1,34 @@
+From a52f67542afdb1c61bd26ea4a8368620114e4747 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 29 Dec 2023 15:04:27 +0800
+Subject: [PATCH 065/126] mtk: wpa_supplicant: fix bss selection when setting
+ mld_connect_band_pref
+
+Without this patch, when setting mld_connect_band_pref as 5g, wrong bss
+will be selected.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ wpa_supplicant/sme.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
+index afdccf54f..abef26f16 100644
+--- a/wpa_supplicant/sme.c
++++ b/wpa_supplicant/sme.c
+@@ -443,8 +443,11 @@ static struct wpa_bss * wpas_ml_connect_pref(struct wpa_supplicant *wpa_s,
+ 	}
+ 
+ 	for_each_link(wpa_s->valid_links, i) {
+-		if (wpa_s->mlo_assoc_link_id == i)
++		if (wpa_s->mlo_assoc_link_id == i) {
++			if (bss->freq >= low && bss->freq <= high)
++				return bss;
+ 			continue;
++		}
+ 
+ 		if (wpa_s->links[i].freq >= low && wpa_s->links[i].freq <= high)
+ 			goto found;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0066-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0066-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch
deleted file mode 100644
index 3f59e13..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0066-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From b463e82d0eec8674e430a7e837c569be4c9fe2c2 Mon Sep 17 00:00:00 2001
-From: mtk25255 <rohit.kamat@mediatek.com>
-Date: Thu, 12 Oct 2023 14:29:23 +0800
-Subject: [PATCH 066/104] mtk: hostapd: Fix RSNXE Interop issue with STA
-
----
- src/ap/ieee802_11.c | 13 ++++++++++++-
- 1 file changed, 12 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 923cbebcc..ce3874901 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -5294,6 +5294,7 @@ static void handle_assoc(struct hostapd_data *hapd,
- 	int omit_rsnxe = 0;
- 	bool set_beacon = false;
- 	bool mld_addrs_not_translated = false;
-+	bool sae_pk = false;
- 
- 	if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
- 				      sizeof(mgmt->u.assoc_req))) {
-@@ -5539,7 +5540,17 @@ static void handle_assoc(struct hostapd_data *hapd,
- 	if (resp != WLAN_STATUS_SUCCESS)
- 		goto fail;
- 	omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
--
-+#ifdef CONFIG_SAE_PK
-+	sae_pk = hostapd_sae_pk_in_use(hapd->conf);
-+#endif /* CONFIG_SAE_PK */
-+	if (omit_rsnxe) {
-+		if (!reassoc && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
-+				(hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
-+				 hapd->conf->sae_pwe == SAE_PWE_BOTH || sae_pk ||
-+				 wpa_key_mgmt_sae_ext_key(hapd->conf->wpa_key_mgmt))) {
-+			omit_rsnxe = 0;
-+		}
-+	}
- 	if (hostapd_get_aid(hapd, sta) < 0) {
- 		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
- 			       HOSTAPD_LEVEL_INFO, "No room for more AIDs");
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0066-mtk-hostapd-add-mld_primary-option.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0066-mtk-hostapd-add-mld_primary-option.patch
new file mode 100644
index 0000000..87fe7ac
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0066-mtk-hostapd-add-mld_primary-option.patch
@@ -0,0 +1,41 @@
+From cba099045780e4befb6d08db29e12549f5d8a83b Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 26 Dec 2023 08:05:41 +0800
+Subject: [PATCH 066/126] mtk: hostapd: add mld_primary option
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ hostapd/config_file.c | 2 ++
+ src/ap/ap_config.h    | 3 +++
+ 2 files changed, 5 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 7ab2c6827..014ca4d3c 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5462,6 +5462,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		conf->punct_acs_threshold = val;
+ 	} else if (os_strcmp(buf, "mld_ap") == 0) {
+ 		bss->mld_ap = !!atoi(pos);
++	} else if (os_strcmp(buf, "mld_primary") == 0) {
++		bss->mld_primary = !!atoi(pos);
+ 	} else if (os_strcmp(buf, "mld_addr") == 0) {
+ 		if (hwaddr_aton(pos, bss->mld_addr)) {
+ 			wpa_printf(MSG_ERROR, "Line %d: Invalid mld_addr",
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index b66f79d31..413505c59 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -987,6 +987,9 @@ struct hostapd_bss_config {
+ 	/* The AP is part of an AP MLD */
+ 	u8 mld_ap;
+ 
++	/* The AP is the primary AP of an AP MLD */
++	u8 mld_primary;
++
+ 	/* The MLD ID to which the AP MLD is affiliated with */
+ 	u8 mld_id;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0067-mtk-hostapd-update-eht-operation-element.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0067-mtk-hostapd-update-eht-operation-element.patch
deleted file mode 100644
index 8764aa7..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0067-mtk-hostapd-update-eht-operation-element.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From a6db9becf71712107500adf239b89f4f8523d3f3 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Wed, 10 May 2023 13:11:34 +0800
-Subject: [PATCH 067/104] mtk: hostapd: update eht operation element
-
----
- src/ap/ieee802_11_eht.c | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
-index 353a4116e..e13662a59 100644
---- a/src/ap/ieee802_11_eht.c
-+++ b/src/ap/ieee802_11_eht.c
-@@ -237,9 +237,9 @@ u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
- 
- 	/* TODO: Fill in appropriate EHT-MCS max Nss information */
- 	oper->basic_eht_mcs_nss_set[0] = 0x11;
--	oper->basic_eht_mcs_nss_set[1] = 0x00;
--	oper->basic_eht_mcs_nss_set[2] = 0x00;
--	oper->basic_eht_mcs_nss_set[3] = 0x00;
-+	oper->basic_eht_mcs_nss_set[1] = 0x11;
-+	oper->basic_eht_mcs_nss_set[2] = 0x11;
-+	oper->basic_eht_mcs_nss_set[3] = 0x11;
- 
- 	if (!eht_oper_info_present)
- 		return pos + elen;
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0067-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0067-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch
new file mode 100644
index 0000000..64611ae
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0067-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch
@@ -0,0 +1,102 @@
+From 68d893f8fd726074299c7040ae69d6ce8b4aca99 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Wed, 6 Mar 2024 15:01:33 +0800
+Subject: [PATCH 067/126] mtk: wpa_supplicant: add 'mld_allowed_phy'
+ configuration option for MLD STA
+
+A new configuration option named 'mld_allowed_phy' is added for MLD STA.
+This option indicates the bitmap of allowed phy for MLO connection.
+Note that setting 'mld_allowed_phy' to 0 makes no phy allowed for MLO.
+In other word, the STA becomes a legacy STA.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/config.c      |  1 +
+ wpa_supplicant/config.h      |  1 +
+ wpa_supplicant/config_file.c |  2 ++
+ wpa_supplicant/sme.c         | 18 ++++++++++++++++++
+ 4 files changed, 22 insertions(+)
+
+diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
+index d2c05e352..d989afdd4 100644
+--- a/wpa_supplicant/config.c
++++ b/wpa_supplicant/config.c
+@@ -5683,6 +5683,7 @@ static const struct global_parse_data global_fields[] = {
+ #endif /* CONFIG_PASN */
+ #ifdef CONFIG_TESTING_OPTIONS
+ 	{ INT_RANGE(mld_force_single_link, 0, 1), 0 },
++	{ INT_RANGE(mld_allowed_phy, 0, 7), 0 },
+ 	{ INT_RANGE(mld_connect_band_pref, 0, MLD_CONNECT_BAND_PREF_MAX), 0 },
+ 	{ FUNC(mld_connect_bssid_pref), 0 },
+ #endif /* CONFIG_TESTING_OPTIONS */
+diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
+index d74b5c455..27ffa3034 100644
+--- a/wpa_supplicant/config.h
++++ b/wpa_supplicant/config.h
+@@ -1813,6 +1813,7 @@ struct wpa_config {
+ 	u8 mld_connect_bssid_pref[ETH_ALEN];
+ 
+ 	int mld_force_single_link;
++	u8 mld_allowed_phy; /* bitmap of allowed phy for MLO connection */
+ #endif /* CONFIG_TESTING_OPTIONS */
+ };
+ 
+diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
+index 5ce616129..591766a85 100644
+--- a/wpa_supplicant/config_file.c
++++ b/wpa_supplicant/config_file.c
+@@ -1626,6 +1626,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
+ #ifdef CONFIG_TESTING_OPTIONS
+ 	if (config->mld_force_single_link)
+ 		fprintf(f, "mld_force_single_link=1\n");
++	if (config->mld_allowed_phy)
++		fprintf(f, "mld_allowed_phy=%u\n", config->mld_allowed_phy);
+ 	if (config->mld_connect_band_pref != MLD_CONNECT_BAND_PREF_AUTO)
+ 		fprintf(f, "mld_connect_band_pref=%d\n",
+ 			config->mld_connect_band_pref);
+diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
+index abef26f16..e8a6a30e8 100644
+--- a/wpa_supplicant/sme.c
++++ b/wpa_supplicant/sme.c
+@@ -523,6 +523,16 @@ static int wpas_sme_ml_auth(struct wpa_supplicant *wpa_s,
+ }
+ 
+ 
++#ifdef CONFIG_TESTING_OPTIONS
++static bool check_mld_allowed_phy(struct wpa_supplicant *wpa_s, int freq)
++{
++	return ((wpa_s->conf->mld_allowed_phy & BIT(0)) && IS_2P4GHZ(freq)) ||
++	       ((wpa_s->conf->mld_allowed_phy & BIT(1)) && IS_5GHZ(freq)) ||
++	       ((wpa_s->conf->mld_allowed_phy & BIT(2)) && is_6ghz_freq(freq));
++}
++#endif /* CONFIG_TESTING_OPTIONS */
++
++
+ static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
+ 				   struct wpa_bss *bss, struct wpa_ssid *ssid)
+ {
+@@ -534,6 +544,11 @@ static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
+ 	for_each_link(bss->valid_links, i) {
+ 		const u8 *bssid = bss->mld_links[i].bssid;
+ 
++#ifdef CONFIG_TESTING_OPTIONS
++		if (!check_mld_allowed_phy(wpa_s, bss->mld_links[i].freq))
++			continue;
++#endif /* CONFIG_TESTING_OPTIONS */
++
+ 		wpa_s->valid_links |= BIT(i);
+ 		os_memcpy(wpa_s->links[i].bssid, bssid, ETH_ALEN);
+ 		wpa_s->links[i].freq = bss->mld_links[i].freq;
+@@ -587,6 +602,9 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
+ 	if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) &&
+ 	    !wpa_bss_parse_basic_ml_element(wpa_s, bss, wpa_s->ap_mld_addr,
+ 					    NULL, ssid, NULL) &&
++#ifdef CONFIG_TESTING_OPTIONS
++	    wpa_s->conf->mld_allowed_phy &&
++#endif /* CONFIG_TESTING_OPTIONS */
+ 	    bss->valid_links) {
+ 		wpa_printf(MSG_DEBUG, "MLD: In authentication");
+ 		wpas_sme_set_mlo_links(wpa_s, bss, ssid);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0068-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0068-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch
new file mode 100644
index 0000000..055820b
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0068-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch
@@ -0,0 +1,312 @@
+From fcb02b75b95acf9efafa46fd3766f48e6799aeb5 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 29 Feb 2024 19:55:34 +0800
+Subject: [PATCH 068/126] mtk: hostapd: support band_idx option for
+ set_mu/get_mu vendor command
+
+Support band_idx for set_mu and get_mu vendor command. The usage shows
+as below:
+1. get_mu: $ hostapd_cli -i <intf> get_mu <band_idx>
+2. set_mu: $ hostapd_cli -i <intf> set_mu <mu_onff>:<band_idx>
+
+Also, make 'band_idx' a mandatory configuration option.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ hostapd/config_file.c        |  9 +++++
+ hostapd/ctrl_iface.c         | 78 ++++++++++++++++++++++++++++--------
+ hostapd/hostapd_cli.c        |  2 +-
+ src/ap/ap_config.c           |  6 +++
+ src/ap/ap_config.h           |  1 +
+ src/ap/ap_drv_ops.c          |  2 +-
+ src/common/mtk_vendor.h      |  1 +
+ src/drivers/driver.h         |  2 +-
+ src/drivers/driver_nl80211.c | 15 +++----
+ 9 files changed, 88 insertions(+), 28 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 014ca4d3c..50d63e259 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5553,6 +5553,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 			return 1;
+ 		}
+ 		conf->pp_mode = (u8) val;
++	} else if (os_strcmp(buf, "band_idx") == 0) {
++		int val = atoi(pos);
++
++		if (val < 0) {
++			wpa_printf(MSG_ERROR, "Line %d: invalid band_idx value",
++				   line);
++			return 1;
++		}
++		conf->band_idx = (u8) val;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 8187cfb3c..76fd25d8b 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4308,17 +4308,42 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ 	value = pos;
+ 
+ 	if (os_strcmp(config, "onoff") == 0) {
+-		int mu = atoi(value);
+-		if (mu < 0 || mu > 15) {
+-			wpa_printf(MSG_ERROR, "Invalid value for mu");
+-			return -1;
++		cnt = hostapd_parse_argument_helper(value, &val);
++		if (cnt == -1)
++			goto fail;
++		if (cnt < 1 || val[0] > 15)
++			goto para_fail;
++
++		if (hostapd_is_mld_ap(hapd)) {
++			u8 band_idx;
++
++			if (cnt != 2)
++				goto para_fail;
++
++			band_idx = val[1];
++
++			for (i = 0; i < hapd->iface->interfaces->count; i++) {
++				struct hostapd_iface *iface;
++
++				iface = hapd->iface->interfaces->iface[i];
++				if (!iface || !iface->conf)
++					continue;
++
++				if (iface->conf->band_idx == band_idx) {
++					hapd = iface->bss[0];
++					break;
++				}
++			}
++			if (hapd->iface->conf->band_idx != band_idx)
++				goto para_fail;
+ 		}
+-		hapd->iconf->mu_onoff = (u8) mu;
+ 
+-		if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) == 0)
+-			return os_snprintf(buf, buflen, "OK\n");
+-		else
++		hapd->iconf->mu_onoff = val[0];
++		os_free(val);
++		if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) != 0)
+ 			goto fail;
++
++		return os_snprintf(buf, buflen, "OK\n");
+ 	}
+ 
+ 	if (hapd->iconf->muru_config == NULL)
+@@ -4330,6 +4355,7 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ 	comm = &muru->comm;
+ 
+ 	if (os_strncmp(config, "update", 6) == 0) {
++		// [ToDo] "update" needs to support band_idx argument
+ 		ret = hostapd_drv_mu_ctrl(hapd, MU_CTRL_UPDATE);
+ 
+ 		os_free(hapd->iconf->muru_config);
+@@ -4475,15 +4501,14 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
+ 
+ para_fail:
+ 	os_free(val);
+-	wpa_printf(MSG_ERROR, "Incorrect input number\n");
++	wpa_printf(MSG_ERROR, "Input number or value is incorrect\n");
+ fail:
+ 	return os_snprintf(buf, buflen, "FAIL\n");
+ }
+ 
+-
+ static int
+-hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
+-					 size_t buflen)
++hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *input, char *buf,
++			  size_t buflen)
+ {
+ 	u8 mu_onoff;
+ 	char *pos, *end;
+@@ -4491,14 +4516,35 @@ hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
+ 	pos = buf;
+ 	end = buf + buflen;
+ 
++	if (hostapd_is_mld_ap(hapd)) {
++		u8 band_idx, i;
++
++		band_idx = (u8)atoi(input);
++
++		for (i = 0; i < hapd->iface->interfaces->count; i++) {
++			struct hostapd_iface *iface;
++
++			iface = hapd->iface->interfaces->iface[i];
++			if (!iface || !iface->conf)
++				continue;
++
++			if (iface->conf->band_idx == band_idx) {
++				hapd = iface->bss[0];
++				break;
++			}
++		}
++		if (hapd->iface->conf->band_idx != band_idx)
++			return os_snprintf(pos, end - pos, "Invalid band idx to get_mu\n");
++	}
++
+ 	if (hapd->iface->state != HAPD_IFACE_ENABLED)
+ 		return os_snprintf(pos, end - pos, "Not allowed to get_mu when current state is %s\n",
+ 				   hostapd_state_text(hapd->iface->state));
+ 
+ 	if (hostapd_drv_mu_dump(hapd, &mu_onoff) == 0) {
+ 		hapd->iconf->mu_onoff = mu_onoff;
+-		return os_snprintf(pos, end - pos, "[hostapd_cli] = UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
+-			!!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
++		return os_snprintf(pos, end - pos, "Band idx %u: UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
++			hapd->iconf->band_idx, !!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
+ 	} else {
+ 		wpa_printf(MSG_INFO, "ctrl iface failed to call");
+ 		return -1;
+@@ -5633,8 +5679,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 							  reply_size);
+ 	} else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
+ 		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply, reply_size);
+-	} else if (os_strncmp(buf, "GET_MU", 6) == 0) {
+-		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
++	} else if (os_strncmp(buf, "GET_MU ", 7) == 0) {
++		reply_len = hostapd_ctrl_iface_get_mu(hapd, buf + 7, reply, reply_size);
+ 	} else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
+ 		reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
+ 	} else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 9a6a742c1..a98f76eee 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1479,7 +1479,7 @@ static int hostapd_cli_cmd_set_mu(struct wpa_ctrl *ctrl, int argc,
+ static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
+ 					   char *argv[])
+ {
+-	return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
++	return hostapd_cli_cmd(ctrl, "GET_MU", 0, argc, argv);
+ }
+ 
+ static int hostapd_cli_cmd_disable_beacon(struct wpa_ctrl *ctrl, int argc,
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 8b98d6170..95100b25c 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -313,6 +313,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 	conf->ibf_enable = IBF_DEFAULT_ENABLE;
+ 	conf->amsdu = 1;
+ 	conf->pp_mode = PP_DISABLE;
++	conf->band_idx = 255;
+ 
+ 	hostapd_set_and_check_bw320_offset(conf, 0);
+ 
+@@ -1631,6 +1632,11 @@ int hostapd_config_check(struct hostapd_config *conf, int full_config)
+ 		return -1;
+ 	}
+ 
++	if (full_config && conf->band_idx == 255) {
++		wpa_printf(MSG_ERROR, "band_idx is required");
++		return -1;
++	}
++
+ 	for (i = 0; i < conf->num_bss; i++) {
+ 		if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
+ 			return -1;
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 413505c59..c61e67403 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1351,6 +1351,7 @@ struct hostapd_config {
+ 	u8 amsdu;
+ 	void *muru_config;
+ 	u8 pp_mode;
++	u8 band_idx;
+ };
+ 
+ enum three_wire_mode {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 011bb37f8..48c2801da 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1310,7 +1310,7 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
+ {
+ 	if (!hapd->driver || !hapd->driver->mu_dump)
+ 		return 0;
+-	return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
++	return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff, hapd->iconf->band_idx);
+ }
+ 
+ int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 261994b8a..9b054ef43 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -210,6 +210,7 @@ enum mtk_vendor_attr_mu_ctrl {
+ 	 * above data structure.
+ 	 */
+ 	MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
++	MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 8f5d260aa..47f4f56c0 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5267,7 +5267,7 @@ struct wpa_driver_ops {
+ 	 *
+ 	 */
+ 	 int (*mu_ctrl)(void *priv, u8 mode, void *config);
+-	 int (*mu_dump)(void *priv, u8 *mu_onoff);
++	 int (*mu_dump)(void *priv, u8 *mu_onoff, u8 band_idx);
+ 
+ 	/**
+ 	 * beacon_ctrl - ctrl on off for beacon
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 4b404f0bb..f6080f160 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -14035,7 +14035,8 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, void *config)
+ 
+ 	switch (mode) {
+ 	case MU_CTRL_ONOFF:
+-		if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff))
++		if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff) ||
++		    nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX, cfg->band_idx))
+ 			goto fail;
+ 		break;
+ 	case MU_CTRL_UPDATE:
+@@ -14099,7 +14100,7 @@ static int mu_dump_handler(struct nl_msg *msg, void *arg)
+ 	return 0;
+ }
+ 
+-static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
++static int nl80211_mu_dump(void *priv, u8 *mu_onoff, u8 band_idx)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+@@ -14115,17 +14116,13 @@ static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
+ 
+ 	if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
+ 		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
+-		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL)) {
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
++		!(attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++		nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX, band_idx)) {
+ 		nlmsg_free(msg);
+ 		return -ENOBUFS;
+ 	}
+ 
+-  attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+-	if (!attr) {
+-		nlmsg_free(msg);
+-		return -1;
+-	}
+-
+ 	nla_nest_end(msg, attr);
+ 
+ 	ret = send_and_recv_resp(drv, msg, mu_dump_handler, mu_onoff);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0068-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0068-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch
deleted file mode 100644
index f383d62..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0068-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From f5256a36cd00b54955decd53961ece85dd5f11f9 Mon Sep 17 00:00:00 2001
-From: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Date: Wed, 30 Aug 2023 04:23:37 +0800
-Subject: [PATCH 068/104] mtk: hostapd: ucode: add support for ucode to parse
- BW320MHz info
-
----
- src/utils/ucode.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/src/utils/ucode.c b/src/utils/ucode.c
-index 29c753c32..4b6ed3a94 100644
---- a/src/utils/ucode.c
-+++ b/src/utils/ucode.c
-@@ -144,6 +144,10 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- 	case 2:
- 		chanwidth = CONF_OPER_CHWIDTH_160MHZ;
- 		break;
-+	case 9:
-+		width = 3;
-+		chanwidth = CONF_OPER_CHWIDTH_320MHZ;
-+		break;
- 	default:
- 		return NULL;
- 	}
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0069-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0069-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch
new file mode 100644
index 0000000..8cdcaf5
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0069-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch
@@ -0,0 +1,145 @@
+From 8df808e1c82be7ce9b00937bb14a2536824f16d2 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 19 Jan 2024 14:11:05 +0800
+Subject: [PATCH 069/126] mtk: hostapd: Handle DFS radar detection in MLO
+
+To handle DFS CAC in MLO, we add the following changes:
+1. Add link id info to radar detect cmd for the kernel to use the correct link.
+2. Block RNR IE for disabled iface. (the EID len would be wrong without it)
+3. Only flush the old stations for the first BSS; otherwise, after DFS CAC
+stations would be flushed again.
+
+Add background radar handling
+
+The logic has changed here, so rebase it.
+Avoid flushing old stations for non-first BSS so that the stations
+can remain connected when non-first BSS is added via link add or it
+completes CAC.
+Also, handle the case when the first BSS requires CAC.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/ap_drv_ops.c                |  9 +++++++++
+ src/ap/hostapd.c                   | 11 ++++++++++-
+ src/ap/ieee802_11.c                |  3 +++
+ src/drivers/driver_nl80211.c       | 18 ++++++++++++++++++
+ src/drivers/driver_nl80211.h       |  1 +
+ src/drivers/driver_nl80211_event.c |  3 ++-
+ 6 files changed, 43 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 48c2801da..f51d5be8e 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1041,6 +1041,15 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
+ 		return -1;
+ 	}
+ 	data.radar_background = radar_background;
++	data.link_id = -1;
++
++#ifdef CONFIG_IEEE80211BE
++	if (hapd->conf->mld_ap) {
++		data.link_id = hapd->mld_link_id;
++		wpa_printf(MSG_DEBUG,
++			   "hostapd_start_dfs_cac: link_id=%d", data.link_id);
++	}
++#endif /* CONFIG_IEEE80211BE */
+ 
+ 	res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
+ 	if (!res) {
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index eefd4c5d3..93c164177 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -1420,9 +1420,18 @@ int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
+ 	u8 if_addr[ETH_ALEN];
+ 	int flush_old_stations = 1;
+ 
+-	if (!hostapd_mld_is_first_bss(hapd))
++	if (!hostapd_mld_is_first_bss(hapd)) {
++		/* Only flush old stations when setting up the first BSS for the MLD. */
++		flush_old_stations = 0;
+ 		wpa_printf(MSG_DEBUG,
+ 			   "MLD: %s: Setting non-first BSS", __func__);
++	} else if (hapd->conf->mld_ap &&
++		   hapd->iface->state == HAPD_IFACE_DFS) {
++		/* Also, avoid flushing old STA when the first BSS of the MLD requires CAC. */
++		flush_old_stations = 0;
++		wpa_printf(MSG_DEBUG,
++			   "MLD: %s: Setting first BSS after CAC complete", __func__);
++	}
+ 
+ 	wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)",
+ 		   __func__, hapd, conf->iface, first);
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index b87fae929..c6676b754 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7910,6 +7910,9 @@ u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
+ 		    !is_6ghz_op_class(iface->conf->op_class))
+ 			continue;
+ 
++		if (!iface->bss[0]->started)
++			continue;
++
+ 		eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
+ 					    current_len, NULL, false);
+ 	}
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index f6080f160..dec358fba 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -10587,6 +10587,24 @@ static int nl80211_start_radar_detection(void *priv,
+ 		return -1;
+ 	}
+ 
++	if (freq->link_id != NL80211_DRV_LINK_ID_NA) {
++		wpa_printf(MSG_DEBUG, "nl80211: Set link_id=%u for radar detect",
++			   freq->link_id);
++
++		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, freq->link_id)) {
++			nlmsg_free(msg);
++			return -ENOBUFS;
++		}
++
++		if (freq->radar_background) {
++			struct i802_link *link = nl80211_get_link(bss, freq->link_id);
++
++			link->background_freq = freq->freq;
++		} else {
++			nl80211_link_set_freq(bss, freq->link_id, freq->freq);
++		}
++	}
++
+ 	ret = send_and_recv_cmd(drv, msg);
+ 	if (ret == 0)
+ 		return 0;
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 9866c221c..a0a62e536 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -56,6 +56,7 @@ struct i802_link {
+ 	unsigned int beacon_set:1;
+ 
+ 	int freq;
++	int background_freq;
+ 	int bandwidth;
+ 	u8 addr[ETH_ALEN];
+ 	void *ctx;
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 73582aeb0..7f5a3d892 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -1638,7 +1638,8 @@ nl80211_get_link_id_by_freq(struct i802_bss *bss, unsigned int freq)
+ 	unsigned int i;
+ 
+ 	for_each_link(bss->valid_links, i) {
+-		if ((unsigned int) bss->links[i].freq == freq)
++		if ((unsigned int) bss->links[i].freq == freq ||
++		    (unsigned int) bss->links[i].background_freq == freq)
+ 			return i;
+ 	}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0069-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0069-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch
deleted file mode 100644
index 4ca6761..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0069-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch
+++ /dev/null
@@ -1,279 +0,0 @@
-From d7a803942f27759fe0e27c4550d70e44fb83c897 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Mon, 11 Sep 2023 10:16:35 +0800
-Subject: [PATCH 069/104] mtk: hostapd: synchronize bandwidth in AP/STA support
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- src/ap/ucode.c         | 41 +++++++++++++++++++--
- src/utils/ucode.c      | 12 +++++--
- wpa_supplicant/ucode.c | 82 ++++++++++++++++++++++++++++++++++--------
- 3 files changed, 117 insertions(+), 18 deletions(-)
-
-diff --git a/src/ap/ucode.c b/src/ap/ucode.c
-index 16d1b5153..98b2a3bf2 100644
---- a/src/ap/ucode.c
-+++ b/src/ap/ucode.c
-@@ -489,6 +489,9 @@ uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
- 	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
- 	int i;
- 
-+	wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
-+			iface->phy, hostapd_state_text(iface->state));
-+
- 	if (!iface)
- 		return NULL;
- 
-@@ -515,6 +518,9 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
- 	uint64_t intval;
- 	int i;
- 
-+	wpa_printf(MSG_INFO, "ucode: mtk: start iface for %s in state %s\n",
-+			iface->phy, hostapd_state_text(iface->state));
-+
- 	if (!iface)
- 		return NULL;
- 
-@@ -537,7 +543,13 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
- 	UPDATE_VAL(op_class, "op_class");
- 	UPDATE_VAL(hw_mode, "hw_mode");
- 	UPDATE_VAL(channel, "channel");
--	UPDATE_VAL(secondary_channel, "sec_channel");
-+
-+	intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL));
-+	if (!errno) {
-+		conf->secondary_channel = intval;
-+		changed = true;
-+	}
-+
- 	if (!changed &&
- 	    (iface->bss[0]->beacon_set_done ||
- 	     iface->state == HAPD_IFACE_DFS))
-@@ -583,6 +595,18 @@ out:
- 		return ucv_boolean_new(true);
- 	}
- 
-+	wpa_printf(MSG_INFO, "ucode: mtk: updated channel information:\n");
-+	wpa_printf(MSG_INFO, "    * channel: %d\n", conf->channel);
-+	wpa_printf(MSG_INFO, "    * op_class: %d\n", conf->op_class);
-+	wpa_printf(MSG_INFO, "    * secondary channel: %d\n",
-+			conf->secondary_channel);
-+	wpa_printf(MSG_INFO, "    * seg0: %d\n",
-+			hostapd_get_oper_centr_freq_seg0_idx(conf));
-+	wpa_printf(MSG_INFO, "    * seg1: %d\n",
-+			hostapd_get_oper_centr_freq_seg0_idx(conf));
-+	wpa_printf(MSG_INFO, "    * oper_chwidth: %d\n",
-+			hostapd_get_oper_chwidth(conf));
-+
- 	for (i = 0; i < iface->num_bss; i++) {
- 		struct hostapd_data *hapd = iface->bss[i];
- 		int ret;
-@@ -617,6 +641,7 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
- 	uint64_t intval;
- 	int i, ret = 0;
- 
-+	wpa_printf(MSG_INFO, "ucode: mtk: channel switch for %s\n", iface->phy);
- 	if (!iface || ucv_type(info) != UC_OBJECT)
- 		return NULL;
- 
-@@ -636,7 +661,8 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
- 	if (errno)
- 		intval = hostapd_get_oper_chwidth(conf);
- 	if (intval)
--		csa.freq_params.bandwidth = 40 << intval;
-+		csa.freq_params.bandwidth = 40 <<
-+			(intval == CONF_OPER_CHWIDTH_320MHZ ? 3 : intval);
- 	else
- 		csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20;
- 
-@@ -647,6 +673,17 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
- 	if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
- 		csa.freq_params.center_freq2 = intval;
- 
-+	wpa_printf(MSG_INFO, "ucode: mtk: switch channel information:\n");
-+	wpa_printf(MSG_INFO, "    * freq is %d\n", csa.freq_params.freq);
-+	wpa_printf(MSG_INFO, "    * bandwidth is %d\n",
-+			csa.freq_params.bandwidth);
-+	wpa_printf(MSG_INFO, "    * sec_chan_offset is %d\n",
-+			csa.freq_params.sec_channel_offset);
-+	wpa_printf(MSG_INFO, "    * center_freq1 is %d\n",
-+			csa.freq_params.center_freq1);
-+	wpa_printf(MSG_INFO, "    * center_freq2 is %d\n",
-+			csa.freq_params.center_freq2);
-+
- 	for (i = 0; i < iface->num_bss; i++)
- 		ret = hostapd_switch_channel(iface->bss[i], &csa);
- 
-diff --git a/src/utils/ucode.c b/src/utils/ucode.c
-index 4b6ed3a94..6f82382f3 100644
---- a/src/utils/ucode.c
-+++ b/src/utils/ucode.c
-@@ -110,6 +110,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- 	uc_value_t *freq = uc_fn_arg(0);
- 	uc_value_t *sec = uc_fn_arg(1);
- 	int width = ucv_uint64_get(uc_fn_arg(2));
-+	int bw320_offset = 1;
- 	int freq_val, center_idx, center_ofs;
- 	enum oper_chan_width chanwidth;
- 	enum hostapd_hw_mode hw_mode;
-@@ -147,6 +148,9 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- 	case 9:
- 		width = 3;
- 		chanwidth = CONF_OPER_CHWIDTH_320MHZ;
-+
-+		/* bw320_offset is 1 for 320 MHz-1, and 2 for 320 MHz-2 */
-+		bw320_offset = ucv_uint64_get(uc_fn_arg(3));
- 		break;
- 	default:
- 		return NULL;
-@@ -178,12 +182,16 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- 	ucv_object_add(ret, "hw_mode_str", ucv_get(ucv_string_new(modestr)));
- 	ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
- 	ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
-+	ucv_object_add(ret, "oper_chwidth", ucv_int64_new(chanwidth));
- 
--	if (!sec_channel)
-+	if (chanwidth == CONF_OPER_CHWIDTH_USE_HT && !sec_channel) {
-+		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(channel));
-+		ucv_object_add(ret, "center_freq1", ucv_int64_new(freq_val));
- 		return ret;
-+	}
- 
- 	if (freq_val >= 5900)
--		center_ofs = 0;
-+		center_ofs = 32 * (1 - bw320_offset);
- 	else if (freq_val >= 5745)
- 		center_ofs = 20;
- 	else
-diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
-index 397f85bde..542ca25c9 100644
---- a/wpa_supplicant/ucode.c
-+++ b/wpa_supplicant/ucode.c
-@@ -7,6 +7,8 @@
- #include "wps_supplicant.h"
- #include "bss.h"
- #include "ucode.h"
-+#include "driver_i.h"
-+#include "common/ieee802_11_common.h"
- 
- static struct wpa_global *wpa_global;
- static uc_resource_type_t *global_type, *iface_type;
-@@ -96,6 +98,8 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
- {
- 	const char *state;
- 	uc_value_t *val;
-+	enum oper_chan_width ch_width;
-+	int center_freq1, bw320_offset = 1;
- 
- 	if (event != EVENT_CH_SWITCH_STARTED)
- 		return;
-@@ -114,11 +118,42 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
- 	uc_value_push(ucv_get(val));
- 
- 	if (event == EVENT_CH_SWITCH_STARTED) {
-+		center_freq1 = data->ch_switch.cf1;
-+
-+		switch (data->ch_switch.ch_width) {
-+		case CHAN_WIDTH_80:
-+			ch_width = CONF_OPER_CHWIDTH_80MHZ;
-+			break;
-+		case CHAN_WIDTH_80P80:
-+			ch_width = CONF_OPER_CHWIDTH_80P80MHZ;
-+			break;
-+		case CHAN_WIDTH_160:
-+			ch_width = CONF_OPER_CHWIDTH_160MHZ;
-+			break;
-+		case CHAN_WIDTH_320:
-+			ch_width = CONF_OPER_CHWIDTH_320MHZ;
-+			break;
-+		case CHAN_WIDTH_20_NOHT:
-+		case CHAN_WIDTH_20:
-+		case CHAN_WIDTH_40:
-+		default:
-+			ch_width = CONF_OPER_CHWIDTH_USE_HT;
-+			break;
-+		}
-+
-+		/* Check bandwidth 320 MHz-2 */
-+		if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
-+		    (center_freq1 == 6265) || center_freq1 == 6585 ||
-+		     center_freq1 == 6905)
-+			bw320_offset = 2;
-+
- 		ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count));
- 		ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq));
- 		ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
--		ucv_object_add(val, "center_freq1", ucv_int64_new(data->ch_switch.cf1));
-+		ucv_object_add(val, "center_freq1", ucv_int64_new(center_freq1));
- 		ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
-+		ucv_object_add(val, "ch_width", ucv_int64_new(ch_width));
-+		ucv_object_add(val, "bw320_offset", ucv_int64_new(bw320_offset));
- 	}
- 
- 	ucv_put(wpa_ucode_call(4));
-@@ -212,6 +247,11 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
- 	struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
- 	struct wpa_bss *bss;
- 	uc_value_t *ret, *val;
-+	struct wpa_channel_info ci;
-+	u8 op_class, channel;
-+	enum oper_chan_width ch_width;
-+	int center_freq1, bw320_offset = 1, is_24ghz;
-+	enum hostapd_hw_mode hw_mode;
- 
- 	if (!wpa_s)
- 		return NULL;
-@@ -224,23 +264,37 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
- 	bss = wpa_s->current_bss;
- 	if (bss) {
- 		int sec_chan = 0;
--		const u8 *ie;
--
--		ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
--		if (ie && ie[1] >= 2) {
--			const struct ieee80211_ht_operation *ht_oper;
--			int sec;
--
--			ht_oper = (const void *) (ie + 2);
--			sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
--			if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
--				sec_chan = 1;
--			else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
--				sec_chan = -1;
-+
-+		hw_mode = ieee80211_freq_to_chan(bss->freq, &channel);
-+		is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
-+			hw_mode == HOSTAPD_MODE_IEEE80211B;
-+
-+		wpa_drv_channel_info(wpa_s, &ci);
-+		center_freq1 = ci.center_frq1;
-+
-+		if (bss->freq != center_freq1) {
-+			if (is_24ghz)
-+				sec_chan = (bss->freq < center_freq1) ? 1 : -1;
-+			else
-+				sec_chan = (bss->freq / 20) & 1 ? 1 : -1;
-+		}
-+
-+		if (ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
-+						  sec_chan, &op_class, &channel))
-+			return NULL;
-+
-+		ch_width = op_class_to_ch_width(op_class);
-+		if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
-+		    (center_freq1 == 6265) || center_freq1 == 6585 ||
-+		     center_freq1 == 6905) {
-+			/* Bandwidth 320 MHz-2 */
-+			bw320_offset = 2;
- 		}
- 
- 		ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
- 		ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
-+		ucv_object_add(ret, "ch_width", ucv_int64_new(ch_width));
-+		ucv_object_add(ret, "bw320_offset", ucv_int64_new(bw320_offset));
- 	}
- 
- #ifdef CONFIG_MESH
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0070-mtk-hostapd-Add-support-for-updating-background-chan.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0070-mtk-hostapd-Add-support-for-updating-background-chan.patch
deleted file mode 100644
index d1800fe..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0070-mtk-hostapd-Add-support-for-updating-background-chan.patch
+++ /dev/null
@@ -1,339 +0,0 @@
-From 0455150c89b046a3ebd81134527ff4cae5025f3d Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 5 Jul 2023 10:25:01 +0800
-Subject: [PATCH 070/104] mtk: hostapd: Add support for updating background
- channel by driver
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/dfs.c                       | 107 ++++++++++++++++++++++++++++-
- src/ap/dfs.h                       |   3 +
- src/ap/drv_callbacks.c             |  22 ++++++
- src/ap/hostapd.h                   |   5 ++
- src/drivers/driver.h               |  12 ++++
- src/drivers/driver_nl80211_event.c |   6 ++
- src/drivers/nl80211_copy.h         |   6 ++
- 7 files changed, 160 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index 7adaf81ac..e39f3c180 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -816,11 +816,14 @@ 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);
-+	int width = iface->radar_background.new_chwidth;
- 
- 	if (!dfs_use_radar_background(iface))
- 		return;
- 
-+	if (!width)
-+		width = hostapd_get_oper_chwidth(iface->conf);
-+
- 	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))
-@@ -985,6 +988,15 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- 		iface->radar_background.temp_ch = 1;
- 		return 1;
- 	} else if (dfs_use_radar_background(iface)) {
-+		/*
-+		 * AP is going to perform CAC, so reset temp_ch to 0,
-+		 * when dedicated rx has already started CAC.
-+		 */
-+		if (iface->radar_background.cac_started) {
-+			iface->radar_background.temp_ch = 0;
-+			return 0;
-+		}
-+
- 		if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
- 			channel_type = DFS_ANY_CHANNEL;
- 
-@@ -1125,6 +1137,8 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
- 	 * ch_switch_notify event is received */
- 	wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
- 
-+	hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
-+
- 	return 0;
- }
- 
-@@ -1176,6 +1190,9 @@ static void hostapd_dfs_update_background_chain(struct hostapd_iface *iface)
- 	iface->radar_background.secondary_channel = sec;
- 	iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
- 	iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
-+	/* if main channel do not require dfs, then set temp_ch = 1 */
-+	if (!hostapd_is_dfs_required(iface))
-+		iface->radar_background.temp_ch = 1;
- 
- 	wpa_printf(MSG_DEBUG,
- 		   "%s: setting background chain to chan %d (%d MHz)",
-@@ -1198,6 +1215,10 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
- 	u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
- 	int ret;
- 
-+	if (iface->radar_background.new_chwidth) {
-+		hostapd_set_oper_chwidth(iface->conf, iface->radar_background.new_chwidth);
-+		iface->radar_background.new_chwidth = 0;
-+	}
- 	ret = hostapd_dfs_request_channel_switch(iface, iface->radar_background.channel,
- 						 iface->radar_background.freq,
- 						 iface->radar_background.secondary_channel,
-@@ -1220,6 +1241,52 @@ hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
- }
- 
- 
-+static void
-+hostapd_dfs_background_expand(struct hostapd_iface *iface, int chan_width)
-+{
-+	struct hostapd_hw_modes *mode = iface->current_mode;
-+	struct hostapd_channel_data *chan;
-+	int i, channel, width = channel_width_to_int(chan_width);
-+
-+	if (iface->conf->channel - iface->radar_background.channel == width / 5)
-+		channel = iface->radar_background.channel;
-+	else if (iface->radar_background.channel - iface->conf->channel == width / 5)
-+		channel = iface->conf->channel;
-+	else
-+		return;
-+
-+	for (i = 0; i < mode->num_channels; i++) {
-+		chan = &mode->channels[i];
-+		if (chan->chan == channel)
-+			break;
-+	}
-+
-+	if (i == mode->num_channels || !dfs_is_chan_allowed(chan, width / 10))
-+		return;
-+
-+	switch (chan_width) {
-+	case CHAN_WIDTH_20_NOHT:
-+	case CHAN_WIDTH_20:
-+		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_USE_HT;
-+		break;
-+	case CHAN_WIDTH_40:
-+		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_80MHZ;
-+		break;
-+	case CHAN_WIDTH_80:
-+		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_160MHZ;
-+		break;
-+	default:
-+		return;
-+	}
-+
-+	iface->radar_background.freq = channel * 5 + 5000;
-+	iface->radar_background.channel = channel;
-+	iface->radar_background.centr_freq_seg0_idx = channel + width / 5 - 2;
-+	iface->radar_background.secondary_channel = 1;
-+	iface->radar_background.expand_ch = 0;
-+}
-+
-+
- int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
- 			     int ht_enabled, int chan_offset, int chan_width,
- 			     int cf1, int cf2)
-@@ -1253,6 +1320,10 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
- 					return 0;
- 
- 				iface->radar_background.temp_ch = 0;
-+
-+				if (iface->radar_background.expand_ch)
-+					hostapd_dfs_background_expand(iface, chan_width);
-+
- 				return hostapd_dfs_start_channel_switch_background(iface);
- 			}
- 
-@@ -1283,6 +1354,8 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
- 		}
- 	} else if (hostapd_dfs_is_background_event(iface, freq)) {
- 		iface->radar_background.cac_started = 0;
-+		iface->radar_background.temp_ch = 0;
-+		iface->radar_background.expand_ch = 0;
- 		hostapd_dfs_update_background_chain(iface);
- 	}
- 
-@@ -1415,6 +1488,9 @@ hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
- 	    iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
- 		return 0;
- 
-+	iface->radar_background.temp_ch = 0;
-+	iface->radar_background.expand_ch = 0;
-+
- 	/* Check if CSA in progress */
- 	if (hostapd_csa_in_progress(iface))
- 		return 0;
-@@ -1649,6 +1725,35 @@ int hostapd_is_dfs_required(struct hostapd_iface *iface)
- }
- 
- 
-+int hostapd_dfs_background_chan_update(struct hostapd_iface *iface, int freq,
-+				       int ht_enabled, int chan_offset, int chan_width,
-+				       int cf1, int cf2, bool expand)
-+{
-+	switch (chan_width) {
-+	case CHAN_WIDTH_80:
-+		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_80MHZ;
-+		break;
-+	case CHAN_WIDTH_160:
-+		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_160MHZ;
-+		break;
-+	default:
-+		iface->radar_background.new_chwidth = CONF_OPER_CHWIDTH_USE_HT;
-+		break;
-+	};
-+
-+	iface->radar_background.freq = freq;
-+	iface->radar_background.channel = (freq - 5000) / 5;
-+	iface->radar_background.centr_freq_seg0_idx = (cf1 - 5000) / 5;
-+	iface->radar_background.centr_freq_seg1_idx = cf2 ? (cf2 - 5000) / 5 : 0;
-+	if (expand) {
-+		iface->radar_background.temp_ch = 1;
-+		iface->radar_background.expand_ch = 1;
-+	}
-+
-+	return 0;
-+}
-+
-+
- int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
- 			  int ht_enabled, int chan_offset, int chan_width,
- 			  int cf1, int cf2)
-diff --git a/src/ap/dfs.h b/src/ap/dfs.h
-index 25ba29ca1..a1a2be5ec 100644
---- a/src/ap/dfs.h
-+++ b/src/ap/dfs.h
-@@ -30,6 +30,9 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
- int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
- 			     int ht_enabled,
- 			     int chan_offset, int chan_width, int cf1, int cf2);
-+int hostapd_dfs_background_chan_update(struct hostapd_iface *iface, int freq,
-+				       int ht_enabled, int chan_offset, int chan_width,
-+				       int cf1, int cf2, bool expand);
- int hostapd_dfs_sta_update_state(struct hostapd_iface *iface, int freq,
- 				 int ht_enabled, int chan_offset, int chan_width,
- 				 int cf1, int cf2, u32 state);
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index caa171474..2d946afd6 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -2226,6 +2226,18 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
- 			      radar->cf1, radar->cf2);
- }
- 
-+
-+static void hostapd_event_dfs_background_chan_update(struct hostapd_data *hapd,
-+						     struct dfs_event *radar, bool expand)
-+{
-+	wpa_printf(MSG_DEBUG, "DFS background channel %s to %d MHz",
-+		   expand ? "expand" : "update", radar->freq);
-+	hostapd_dfs_background_chan_update(hapd->iface, radar->freq, radar->ht_enabled,
-+					   radar->chan_offset, radar->chan_width,
-+					   radar->cf1, radar->cf2, expand);
-+}
-+
-+
- static void hostapd_event_dfs_sta_cac_skipped(struct hostapd_data *hapd,
- 					      struct dfs_event *radar)
- {
-@@ -2610,6 +2622,16 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
- 		hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
- 		hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
- 		break;
-+	case EVENT_DFS_BACKGROUND_CHAN_UPDATE:
-+		if (!data)
-+			break;
-+		hostapd_event_dfs_background_chan_update(hapd, &data->dfs_event, false);
-+		break;
-+	case EVENT_DFS_BACKGROUND_CHAN_EXPAND:
-+		if (!data)
-+			break;
-+		hostapd_event_dfs_background_chan_update(hapd, &data->dfs_event, true);
-+		break;
- 	case EVENT_DFS_STA_CAC_SKIPPED:
- 		if (!data)
- 			break;
-diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
-index 1e4113459..5b37be87b 100644
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -640,6 +640,11 @@ struct hostapd_iface {
- 		unsigned int temp_ch:1;
- 		/* CAC started on radar offchain */
- 		unsigned int cac_started:1;
-+		/* Main chain should expand its width according to the
-+		 * current offchain channel after CAC detection on radar offchain.
-+		 */
-+		unsigned int expand_ch:1;
-+		int new_chwidth;
- 	} radar_background;
- 
- 	u16 hw_flags;
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 1c0c38e24..4e3dc9bdb 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5960,6 +5960,18 @@ enum wpa_event_type {
- 	 * The channel in the notification is now marked as usable.
- 	 */
- 	EVENT_DFS_STA_CAC_EXPIRED,
-+
-+	/**
-+	 * EVENT_DFS_BACKGROUND_CHAN_UPDATE - Notification that background
-+	 * channel has been updated.
-+	 */
-+	EVENT_DFS_BACKGROUND_CHAN_UPDATE,
-+
-+	/**
-+	 * EVENT_DFS_BACKGROUND_CHAN_EXPAND - Notification that background
-+	 * channel has been updated and operating channel should expand its width.
-+	 */
-+	EVENT_DFS_BACKGROUND_CHAN_EXPAND,
- };
- 
- 
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 7889930a0..6631285bf 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -2529,6 +2529,12 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
- 	case NL80211_RADAR_CAC_STARTED:
- 		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
- 		break;
-+	case NL80211_RADAR_BACKGROUND_CHAN_UPDATE:
-+		wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_UPDATE, &data);
-+		break;
-+	case NL80211_RADAR_BACKGROUND_CHAN_EXPAND:
-+		wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_EXPAND, &data);
-+		break;
- 	case NL80211_RADAR_STA_CAC_SKIPPED:
- 		wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
- 		break;
-diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
-index 8917d565b..c56954306 100644
---- a/src/drivers/nl80211_copy.h
-+++ b/src/drivers/nl80211_copy.h
-@@ -6699,6 +6699,10 @@ enum nl80211_smps_mode {
-  *	applicable for ETSI dfs domain where pre-CAC is valid for ever.
-  * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
-  *	should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
-+ * @NL80211_RADAR_BACKGROUND_CHAN_UPDATE: background channel is updated by the
-+ *	driver.
-+ * @NL80211_RADAR_BACKGROUND_CHAN_EXPAND: background channel is updated by the
-+ *	driver and required to expand main operating channel.
-  * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
-  *	when receiving CSA/assoc resp
-  * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
-@@ -6711,6 +6715,8 @@ enum nl80211_radar_event {
- 	NL80211_RADAR_NOP_FINISHED,
- 	NL80211_RADAR_PRE_CAC_EXPIRED,
- 	NL80211_RADAR_CAC_STARTED,
-+	NL80211_RADAR_BACKGROUND_CHAN_UPDATE,
-+	NL80211_RADAR_BACKGROUND_CHAN_EXPAND,
- 	NL80211_RADAR_STA_CAC_SKIPPED,
- 	NL80211_RADAR_STA_CAC_EXPIRED,
- };
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0070-mtk-hostapd-add-wds-mlo-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0070-mtk-hostapd-add-wds-mlo-support.patch
new file mode 100644
index 0000000..293384f
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0070-mtk-hostapd-add-wds-mlo-support.patch
@@ -0,0 +1,203 @@
+From 89cea0c904dfce8fca45a3d721f2871c32402e05 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Tue, 16 Jan 2024 16:22:17 +0800
+Subject: [PATCH 070/126] mtk: hostapd: add wds mlo support
+
+1. Add mld_assoc_sta to get the primary sta_info.
+2. Find hapd according to mld address.
+
+The latest get_hapd_bssid return hapd only if link id is matched.
+However,the hostapd_rx_from_unknown_sta does not have link
+information so it cannot get hapd.
+
+Modify get_hapd_bssid to ignore link id when link id is -1.
+
+Without this patch, wds mode cannot work and the AP would not be
+aware that station is using 4 address.
+
+Transmit correct hapd by i802_bss->ctx to EVENT_RX_FROM_UNKNOWN handler.
+Without this patch, AP cannot setup AP_VLAN interface in mlo + legacy case.
+In mlo + legacy case, if wds null is sent to legacy AP, it cannot get correct
+hapd according drv->ctx because drv->ctx and legacy AP's hapd may not under
+the same hostapd_iface.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ src/ap/drv_callbacks.c             |  7 ++++---
+ src/ap/ieee802_11.c                |  8 ++++++++
+ src/ap/sta_info.c                  |  6 +++++-
+ src/ap/sta_info.h                  |  1 +
+ src/drivers/driver.h               |  3 +++
+ src/drivers/driver_nl80211.c       | 14 ++++++++++++++
+ src/drivers/driver_nl80211_event.c |  2 +-
+ 7 files changed, 36 insertions(+), 5 deletions(-)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 1fa27bf80..1107fd70e 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -1798,6 +1798,7 @@ switch_link_scan(struct hostapd_data *hapd, u64 scan_cookie)
+ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+ 					    const u8 *bssid, int link_id)
+ {
++	struct hostapd_data *ret = NULL;
+ 	size_t i;
+ 
+ 	if (bssid == NULL)
+@@ -1820,7 +1821,7 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+ 		if (ether_addr_equal(bssid, hapd->own_addr) ||
+ 		    (hapd->conf->mld_ap &&
+ 		     ether_addr_equal(bssid, hapd->mld->mld_addr) &&
+-		     link_id == hapd->mld_link_id))
++		     (link_id == hapd->mld_link_id || link_id == -1)))
+ 			return hapd;
+ 
+ 		if (!hapd->conf->mld_ap)
+@@ -1832,13 +1833,13 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+ 
+ 			if (ether_addr_equal(bssid, p_hapd->own_addr) ||
+ 			    (ether_addr_equal(bssid, p_hapd->mld->mld_addr) &&
+-			     link_id == p_hapd->mld_link_id))
++			     (link_id == p_hapd->mld_link_id || link_id == -1)))
+ 				return p_hapd;
+ 		}
+ #endif /* CONFIG_IEEE80211BE */
+ 	}
+ 
+-	return NULL;
++	return ret;
+ }
+ 
+ 
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index c6676b754..d26b50031 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -3183,6 +3183,7 @@ static void handle_auth(struct hostapd_data *hapd,
+ 
+ 			ap_sta_set_mld(sta, true);
+ 			sta->mld_assoc_link_id = link_id;
++			sta->mld_assoc_sta = sta;
+ 
+ 			/*
+ 			 * Set the MLD address as the station address and the
+@@ -4572,6 +4573,7 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
+ 
+ 	sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
+ 	sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
++	sta->mld_assoc_sta = origin_sta;
+ 
+ 	status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
+ 	if (status != WLAN_STATUS_SUCCESS) {
+@@ -7037,6 +7039,12 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+ 	struct sta_info *sta;
+ 
+ 	sta = ap_get_sta(hapd, src);
++
++#ifdef CONFIG_IEEE80211BE
++	if (sta && sta->mld_info.mld_sta)
++		sta = sta->mld_assoc_sta;
++#endif
++
+ 	if (sta &&
+ 	    ((sta->flags & WLAN_STA_ASSOC) ||
+ 	     ((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) {
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index d4f4eb913..ea34d347f 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -74,6 +74,7 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+ 	while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
+ 		s = s->hnext;
+ 
++#ifdef CONFIG_IEEE80211BE
+ 	if (hapd->conf->mld_ap && !s) {
+ 		u8 link_id;
+ 
+@@ -84,10 +85,13 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+ 				continue;
+ 
+ 			for (s = h->sta_list; s; s = s->next)
+-				if (!os_memcmp(s->setup_link_addr, sta, 6))
++				if ((!os_memcmp(s->setup_link_addr, sta, 6) ||
++				     !os_memcmp(s->addr, sta, 6)) &&
++				     s->flags & WLAN_STA_ASSOC)
+ 					return s;
+ 		}
+ 	}
++#endif
+ 
+ 	return s;
+ }
+diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
+index 6b88799e2..67b97671a 100644
+--- a/src/ap/sta_info.h
++++ b/src/ap/sta_info.h
+@@ -328,6 +328,7 @@ struct sta_info {
+ #ifdef CONFIG_IEEE80211BE
+ 	struct mld_info mld_info;
+ 	u8 mld_assoc_link_id;
++	struct sta_info *mld_assoc_sta;
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ 	u16 max_idle_period; /* if nonzero, the granted BSS max idle period in
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 47f4f56c0..ec00c36e4 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5366,6 +5366,9 @@ struct wpa_driver_ops {
+ 	 * @pp_mode: Value is defined in enum pp_mode
+ 	 */
+ 	int (*pp_mode_set)(void *priv, const u8 pp_mode);
++#ifdef CONFIG_IEEE80211BE
++	int (*get_mld_addr)(void *priv, u8 *addr);
++#endif
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index dec358fba..8f7215d62 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -15209,6 +15209,17 @@ fail:
+ 	return -ENOBUFS;
+ }
+ 
++#ifdef CONFIG_IEEE80211BE
++static int nl80211_get_mld_addr(void *priv, u8 *addr)
++{
++	struct i802_bss *bss = priv;
++
++	os_memcpy(addr, bss->addr, ETH_ALEN);
++
++	return 0;
++}
++#endif
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+ 	.desc = "Linux nl80211/cfg80211",
+@@ -15389,4 +15400,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.amnt_dump = nl80211_amnt_dump,
+ 	.background_radar_mode = nl80211_background_radar_mode,
+ 	.pp_mode_set = nl80211_pp_mode_set,
++#ifdef CONFIG_IEEE80211BE
++	.get_mld_addr = nl80211_get_mld_addr,
++#endif
+ };
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 7f5a3d892..07af6be77 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -2575,7 +2575,7 @@ static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb,
+ 	event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]);
+ 	event.rx_from_unknown.wds = wds;
+ 
+-	wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
++	wpa_supplicant_event(bss->ctx, EVENT_RX_FROM_UNKNOWN, &event);
+ }
+ 
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0071-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0071-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch
deleted file mode 100644
index c2760d9..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0071-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch
+++ /dev/null
@@ -1,291 +0,0 @@
-From 5b2e33617bfafa8c6776e80b13c8747f0021a804 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 2 Aug 2023 19:00:34 +0800
-Subject: [PATCH 071/104] mtk: hostapd: add zwdfs mode ctrl for eagle efem
- hwits
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- hostapd/config_file.c             |  2 ++
- hostapd/ctrl_iface.c              | 30 +++++++++++++++++++++++++++
- src/ap/ap_config.h                |  6 ++++++
- src/ap/ap_drv_ops.c               | 14 +++++++++++++
- src/ap/ap_drv_ops.h               |  1 +
- src/ap/dfs.c                      |  6 ++++++
- src/common/mtk_vendor.h           | 12 +++++++++++
- src/drivers/driver.h              |  7 +++++++
- src/drivers/driver_nl80211.c      | 34 +++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |  1 +
- src/drivers/driver_nl80211_capa.c |  3 +++
- 11 files changed, 116 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index dadc8f108..9467a1128 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -3576,6 +3576,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		conf->acs_exclude_6ghz_non_psc = atoi(pos);
- 	} else if (os_strcmp(buf, "enable_background_radar") == 0) {
- 		conf->enable_background_radar = atoi(pos);
-+	} else if (os_strcmp(buf, "background_radar_mode") == 0) {
-+		conf->background_radar_mode = atoi(pos);
- 	} else if (os_strcmp(buf, "min_tx_power") == 0) {
- 		int val = atoi(pos);
- 
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 581acc260..9b072d1b2 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4782,6 +4782,33 @@ hostapd_ctrl_iface_dump_amnt(struct hostapd_data *hapd, char *cmd,
- 		return pos - buf;
- }
- 
-+static int
-+hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cmd,
-+					     char *buf, size_t buflen)
-+{
-+	struct hostapd_iface *iface = hapd->iface;
-+	char *pos, *param;
-+
-+	param = os_strchr(cmd, ' ');
-+	if (!param)
-+		return -1;
-+	*param++ = '\0';
-+
-+	pos = os_strstr(param, "mode=");
-+	if (!pos)
-+		return -1;
-+
-+	if (os_strncmp(pos + 5, "cert", 4) == 0)
-+		iface->conf->background_radar_mode = BACKGROUND_RADAR_CERT_MODE;
-+	else if (os_strncmp(pos + 5, "normal", 6) == 0)
-+		iface->conf->background_radar_mode = BACKGROUND_RADAR_NORMAL_MODE;
-+
-+	if (hostapd_drv_background_radar_mode(hapd) < 0)
-+		return -1;
-+
-+	return os_snprintf(buf, buflen, "OK\n");
-+}
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -5423,6 +5450,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 		if (pos)
- 			*pos = ' ';
- 		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 23, reply, reply_size);
-+	} else if (os_strncmp(buf, "SET_BACKGROUND_RADAR_MODE", 25) == 0) {
-+		reply_len = hostapd_ctrl_iface_set_background_radar_mode(hapd, buf + 25,
-+									 reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 3827a8fc8..0b07be516 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1051,6 +1051,7 @@ struct hostapd_config {
- 	bool hw_mode_set;
- 	int acs_exclude_6ghz_non_psc;
- 	int enable_background_radar;
-+	int background_radar_mode;
- 	enum {
- 		LONG_PREAMBLE = 0,
- 		SHORT_PREAMBLE = 1
-@@ -1306,6 +1307,11 @@ enum three_wire_mode {
- 		NUM_THREE_WIRE_MODE - 1
- };
- 
-+enum background_radar_mode {
-+	BACKGROUND_RADAR_NORMAL_MODE,
-+	BACKGROUND_RADAR_CERT_MODE,
-+};
-+
- enum dfs_mode {
- 	DFS_DETECT_MODE_DISABLE,
- 	DFS_DETECT_MODE_AP_ENABLE,
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 116bc4ceb..2028e70fb 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1365,3 +1365,17 @@ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_
- 		return 0;
- 	return hapd->driver->amnt_dump(hapd->drv_priv, amnt_idx, amnt_dump_buf);
- }
-+
-+int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
-+{
-+	if (!hapd->driver || !hapd->driver->background_radar_mode ||
-+	    !(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_RADAR_BACKGROUND) ||
-+	    !hapd->iface->conf->enable_background_radar)
-+		return 0;
-+	if (hapd->iconf->background_radar_mode > BACKGROUND_RADAR_CERT_MODE) {
-+		wpa_printf(MSG_INFO, "Invalid value for background radar mode\n");
-+		return 0;
-+	}
-+	return hapd->driver->background_radar_mode(hapd->drv_priv,
-+						   hapd->iconf->background_radar_mode);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 84b41881a..f0e618bcc 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -168,6 +168,7 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
- 
- int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
- int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
-+int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index e39f3c180..b12290556 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -985,6 +985,9 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- 		if (res < 0)
- 			return res;
- 
-+		if (hostapd_drv_background_radar_mode(iface->bss[0]) < 0)
-+			return -1;
-+
- 		iface->radar_background.temp_ch = 1;
- 		return 1;
- 	} else if (dfs_use_radar_background(iface)) {
-@@ -1025,6 +1028,9 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
- 		iface->radar_background.secondary_channel = sec;
- 		iface->radar_background.centr_freq_seg0_idx = cf1;
- 		iface->radar_background.centr_freq_seg1_idx = cf2;
-+
-+		if (hostapd_drv_background_radar_mode(iface->bss[0]) < 0)
-+			return -1;
- 	}
- 
- 	return 0;
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index e140de60b..5bc1e0444 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -16,6 +16,7 @@ enum mtk_nl80211_vendor_subcmds {
- 	MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
- 	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
- 	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
-+	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
- };
- 
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -244,6 +245,17 @@ enum mtk_vendor_attr_bss_color_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
- };
- 
-+enum mtk_vendor_attr_background_radar_ctrl {
-+	MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL,
-+	MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
-+};
-+
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 4e3dc9bdb..863748d4f 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5319,6 +5319,13 @@ struct wpa_driver_ops {
- 	* @amnt_dump_buf: Buffer to print
- 	*/
- 	int (*amnt_dump)(void *priv, u8 amnt_idx, u8 *amnt_dump_buf);
-+
-+	/**
-+	 * background_radar_mode - set background radar mode
-+	 * @priv: Private driver interface data
-+	 * @background_radar_mode: background radar mode
-+	 */
-+	int (*background_radar_mode)(void *priv, u8 background_radar_mode);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index aeb755b11..e3f00b6d6 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -15052,6 +15052,39 @@ fail:
- 	return -ENOBUFS;
- }
- 
-+static int nl80211_background_radar_mode(void *priv, const u8 background_radar_mode)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	/* Prepare nl80211 cmd */
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_background_radar_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			   "nl80211: Driver does not support setting background radar mode");
-+		return 0;
-+	}
-+
-+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL) ||
-+	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+	    nla_put_u8(msg, MTK_VENDOR_ATTR_BACKGROUND_RADAR_CTRL_MODE, background_radar_mode)) {
-+		nlmsg_free(msg);
-+		return -ENOBUFS;
-+	}
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_cmd(drv, msg);
-+	if (ret) {
-+		wpa_printf(MSG_ERROR, "Failed to set background radar mode. ret=%d (%s) ",
-+			   ret, strerror(-ret));
-+	}
-+	return ret;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
- 	.desc = "Linux nl80211/cfg80211",
-@@ -15229,4 +15262,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.ap_trigtype = nl80211_ap_trigtype,
- 	.amnt_set = nl80211_amnt_set,
- 	.amnt_dump = nl80211_amnt_dump,
-+	.background_radar_mode = nl80211_background_radar_mode,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index adc1b9bf7..e9aae8d14 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -208,6 +208,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int mtk_bss_color_vendor_cmd_avail:1;
- 	unsigned int mtk_rfeatures_vendor_cmd_avail:1;
- 	unsigned int mtk_amnt_vendor_cmd_avail:1;
-+	unsigned int mtk_background_radar_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 38e83e42b..9bc98aae7 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1164,6 +1164,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
- 					drv->mtk_rfeatures_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL:
-+					drv->mtk_background_radar_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0071-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0071-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch
new file mode 100644
index 0000000..c62de68
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0071-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch
@@ -0,0 +1,28 @@
+From f4907a96f2e0494b03c7447ae2f5aac20dab61f8 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 1 Mar 2024 16:59:53 +0800
+Subject: [PATCH 071/126] mtk: hostapd: prevent getting non-MLD STA for other
+ links
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/sta_info.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index ea34d347f..44d98d5e0 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -87,7 +87,8 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+ 			for (s = h->sta_list; s; s = s->next)
+ 				if ((!os_memcmp(s->setup_link_addr, sta, 6) ||
+ 				     !os_memcmp(s->addr, sta, 6)) &&
+-				     s->flags & WLAN_STA_ASSOC)
++				     s->flags & WLAN_STA_ASSOC &&
++				     s->mld_info.mld_sta)
+ 					return s;
+ 		}
+ 	}
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0072-mtk-hostapd-add-support-enable-disable-preamble-punc.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0072-mtk-hostapd-add-support-enable-disable-preamble-punc.patch
deleted file mode 100644
index 4959fb4..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0072-mtk-hostapd-add-support-enable-disable-preamble-punc.patch
+++ /dev/null
@@ -1,375 +0,0 @@
-From 51377e7c81b4164be42de4a5c4c48ba53a638afe Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Thu, 21 Sep 2023 10:29:46 +0800
-Subject: [PATCH 072/104] mtk: hostapd: add support enable/disable preamble
- puncture from mtk vendor command
-
-This commit supports two ways to enable/disable preamble puncture
-feature.
-
-1. Add new hostapd configuration "pp_mode". The possible value could be
-1 to 3. When the value is 0, it means that the firmware will turn off
-the pp algorithm. When the value is 1, it means that the firmware will
-enable the pp algorithm, allowing the algorithm to determine whether pp
-could be applied on each txcmd. When the value is 2, it means that pp
-feature is manually configured by the user. Please noted that for
-current implementation, the default configuration is 0.
-
-2. $ hostapd_cli -i <intf_name> raw set_pp mode val
-The argument "val" could be 0 for PP feature disabled or 1 to configure
-PP feature as auto mode.
-
-This commit also let user check whether pp feature is enabled by
-hostapd_cli command. The usage shows as below:
-$ hostapd_cli -i <intf_name> raw get_pp mode
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- hostapd/config_file.c             | 12 +++++++
- hostapd/ctrl_iface.c              | 59 +++++++++++++++++++++++++++++++
- src/ap/ap_config.c                |  1 +
- src/ap/ap_config.h                |  7 ++++
- src/ap/ap_drv_ops.c               |  9 +++++
- src/ap/ap_drv_ops.h               |  1 +
- src/ap/hostapd.c                  |  2 ++
- src/common/mtk_vendor.h           | 12 +++++++
- src/drivers/driver.h              |  6 ++++
- src/drivers/driver_nl80211.c      | 49 +++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |  1 +
- src/drivers/driver_nl80211_capa.c |  3 ++
- 12 files changed, 162 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 9467a1128..050ef290e 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5333,6 +5333,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 	} else if (os_strcmp(buf, "punct_bitmap") == 0) {
- 		if (get_u16(pos, line, &conf->punct_bitmap))
- 			return 1;
-+		conf->punct_bitmap = atoi(pos);
-+		conf->pp_mode = PP_MANUAL_MODE;
- 	} else if (os_strcmp(buf, "punct_acs_threshold") == 0) {
- 		int val = atoi(pos);
- 
-@@ -5415,6 +5417,16 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 			return 1;
- 		}
- 		conf->amsdu = val;
-+	} else if (os_strcmp(buf, "pp_mode") == 0) {
-+		int val = atoi(pos);
-+
-+		if ((val != PP_MANUAL_MODE && conf->punct_bitmap) ||
-+		    val < PP_DISABLE || val > PP_MANUAL_MODE) {
-+			wpa_printf(MSG_ERROR, "Line %d: invalid pp_mode value",
-+				   line);
-+			return 1;
-+		}
-+		conf->pp_mode = (u8) val;
- 	} else {
- 		wpa_printf(MSG_ERROR,
- 			   "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 9b072d1b2..c9b53c64e 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4809,6 +4809,59 @@ hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cm
- 	return os_snprintf(buf, buflen, "OK\n");
- }
- 
-+static int
-+hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
-+			  size_t buflen)
-+{
-+	char *pos, *config, *value;
-+
-+	config = cmd;
-+	pos = os_strchr(config, ' ');
-+	if (pos == NULL)
-+		return -1;
-+	*pos++ = '\0';
-+
-+	if (pos == NULL)
-+		return -1;
-+	value = pos;
-+
-+	if (os_strcmp(config, "mode") == 0) {
-+		int val = atoi(value);
-+
-+		if (val < PP_DISABLE || val > PP_AUTO_MODE) {
-+			wpa_printf(MSG_ERROR, "Invalid value for set_pp");
-+			return -1;
-+		}
-+		hapd->iconf->pp_mode = (u8) val;
-+		if (hostapd_drv_pp_mode_set(hapd) != 0)
-+			return -1;
-+	} else {
-+		wpa_printf(MSG_ERROR,
-+			   "Unsupported parameter %s for set_pp", config);
-+		return -1;
-+	}
-+	return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+static int
-+hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
-+			  size_t buflen)
-+{
-+	char *pos, *end;
-+
-+	pos = buf;
-+	end = buf + buflen;
-+
-+	if (os_strcmp(cmd, "mode") == 0) {
-+		return os_snprintf(pos, end - pos, "pp_mode: %d\n",
-+				   hapd->iconf->pp_mode);
-+	} else {
-+		wpa_printf(MSG_ERROR,
-+			   "Unsupported parameter %s for get_pp", cmd);
-+		return -1;
-+	}
-+}
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -5442,6 +5495,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 	} else if (os_strncmp(buf, "DUMP_AMNT", 9) == 0) {
- 		reply_len = hostapd_ctrl_iface_dump_amnt(hapd, buf+10,
- 							reply, reply_size);
-+	} else if (os_strncmp(buf, "set_pp", 6) == 0) {
-+		reply_len = hostapd_ctrl_iface_set_pp(hapd, buf + 7, reply,
-+						      reply_size);
-+	} else if (os_strncmp(buf, "get_pp", 6) == 0) {
-+		reply_len = hostapd_ctrl_iface_get_pp(hapd, buf + 7, reply,
-+						      reply_size);
- 	} else if (os_strncmp(buf, "set_muru_manual_config=", 23) == 0) {
- 		// Replace first ':' with a single space ' '
- 		char *pos = buf + 23;
-diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index ba1b2a7a3..056c38f73 100644
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -310,6 +310,7 @@ struct hostapd_config * hostapd_config_defaults(void)
- 	conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
- 	conf->ibf_enable = IBF_DEFAULT_ENABLE;
- 	conf->amsdu = 1;
-+	conf->pp_mode = PP_DISABLE;
- 
- 	hostapd_set_and_check_bw320_offset(conf, 0);
- 
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 0b07be516..40edcdaa7 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1293,6 +1293,7 @@ struct hostapd_config {
- 	u8 dfs_detect_mode;
- 	u8 amsdu;
- 	void *muru_config;
-+	u8 pp_mode;
- };
- 
- enum three_wire_mode {
-@@ -1345,6 +1346,12 @@ enum mtk_vendor_attr_edcca_ctrl_mode {
- 	EDCCA_CTRL_NUM,
- };
- 
-+enum pp_mode {
-+	PP_DISABLE = 0,
-+	PP_AUTO_MODE,
-+	PP_MANUAL_MODE,
-+};
-+
- #define EDCCA_DEFAULT_COMPENSATION -6
- #define EDCCA_MIN_COMPENSATION -126
- #define EDCCA_MAX_COMPENSATION 126
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 2028e70fb..c71cfe1bd 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1379,3 +1379,12 @@ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
- 	return hapd->driver->background_radar_mode(hapd->drv_priv,
- 						   hapd->iconf->background_radar_mode);
- }
-+
-+int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
-+{
-+	if (!hapd->driver || !hapd->driver->pp_mode_set ||
-+	    hapd->iconf->pp_mode > PP_AUTO_MODE)
-+		return 0;
-+	return hapd->driver->pp_mode_set(hapd->drv_priv,
-+					 hapd->iconf->pp_mode);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index f0e618bcc..ef61001e5 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -169,6 +169,7 @@ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
- int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac);
- int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
- int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
-+int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 0d4b79b48..cdbf81e38 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2708,6 +2708,8 @@ dfs_offload:
- 		goto fail;
- 	if (hostapd_drv_amsdu_ctrl(hapd) < 0)
- 		goto fail;
-+	if (hostapd_drv_pp_mode_set(hapd) < 0)
-+		goto fail;
- 
- 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- 		   iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 5bc1e0444..6275c141d 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -17,6 +17,7 @@ enum mtk_nl80211_vendor_subcmds {
- 	MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
- 	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
- 	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
-+	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
- };
- 
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -256,6 +257,17 @@ enum mtk_vendor_attr_background_radar_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_BACKGROUND_RADAR_CTRL - 1
- };
- 
-+enum mtk_vendor_attr_pp_ctrl {
-+	MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_PP_MODE,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_PP_CTRL,
-+	MTK_VENDOR_ATTR_PP_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
-+};
-+
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 863748d4f..be0e89ba3 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5326,6 +5326,12 @@ struct wpa_driver_ops {
- 	 * @background_radar_mode: background radar mode
- 	 */
- 	int (*background_radar_mode)(void *priv, u8 background_radar_mode);
-+	/**
-+	 * pp_mode_set - Set preamble puncture operation mode
-+	 * @priv: Private driver interface data
-+	 * @pp_mode: Value is defined in enum pp_mode
-+	 */
-+	int (*pp_mode_set)(void *priv, const u8 pp_mode);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index e3f00b6d6..b47ab07ea 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -156,6 +156,11 @@ amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
- 	[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
- };
- 
-+static struct nla_policy
-+pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
-+	[MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
-+};
-+
- static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
- {
- 	struct nl_sock *handle;
-@@ -15085,6 +15090,49 @@ static int nl80211_background_radar_mode(void *priv, const u8 background_radar_m
- 	return ret;
- }
- 
-+static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_pp_vendor_cmd_avail) {
-+		wpa_printf(MSG_DEBUG,
-+			   "nl80211: Driver does not support setting preamble puncture");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_PP_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-+	if (!data)
-+		goto fail;
-+
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_PP_MODE, pp_mode);
-+
-+	nla_nest_end(msg, data);
-+	ret = send_and_recv_cmd(drv, msg);
-+
-+	if (ret)
-+		wpa_printf(MSG_ERROR, "Failed to set pp_enable. ret=%d (%s)",
-+			   ret, strerror(-ret));
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
- 	.desc = "Linux nl80211/cfg80211",
-@@ -15263,4 +15311,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.amnt_set = nl80211_amnt_set,
- 	.amnt_dump = nl80211_amnt_dump,
- 	.background_radar_mode = nl80211_background_radar_mode,
-+	.pp_mode_set = nl80211_pp_mode_set,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index e9aae8d14..707bb7fe4 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -209,6 +209,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int mtk_rfeatures_vendor_cmd_avail:1;
- 	unsigned int mtk_amnt_vendor_cmd_avail:1;
- 	unsigned int mtk_background_radar_vendor_cmd_avail:1;
-+	unsigned int mtk_pp_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index 9bc98aae7..ba3c0817b 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1167,6 +1167,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL:
- 					drv->mtk_background_radar_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_PP_CTRL:
-+					drv->mtk_pp_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0072-mtk-hostapd-specify-link-id-for-unicast-DEAUTH.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0072-mtk-hostapd-specify-link-id-for-unicast-DEAUTH.patch
new file mode 100644
index 0000000..1deaede
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0072-mtk-hostapd-specify-link-id-for-unicast-DEAUTH.patch
@@ -0,0 +1,37 @@
+From f9b54baaa5037a174963b2dc255a16e60a9cade8 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 27 Feb 2024 15:04:35 +0800
+Subject: [PATCH 072/126] mtk: hostapd: specify link id for unicast DEAUTH
+
+When deauthenticating the STA, hostapd should specifies the setup link
+of the target STA so that the TX status of the DEAUTH can be forwarded
+to the correct link (BSS).
+
+(The original gerrit somehow disappears, so I commit it again)
+(https://gerrit.mediatek.inc/c/gateway/WiFi7/mac80211/hostapd/+/8715613)
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ap_drv_ops.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index f51d5be8e..34c2ff211 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -896,7 +896,11 @@ int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+ 	if (hapd->conf->mld_ap) {
+ 		struct sta_info *sta = ap_get_sta(hapd, addr);
+ 
+-		link_id = hapd->mld_link_id;
++		if (sta)
++			link_id = sta->mld_assoc_link_id;
++		else
++			link_id = hapd->mld_link_id;
++
+ 		if (ap_sta_is_mld(hapd, sta))
+ 			own_addr = hapd->mld->mld_addr;
+ 	}
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0073-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0073-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch
deleted file mode 100644
index 31d1951..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0073-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch
+++ /dev/null
@@ -1,247 +0,0 @@
-From a7adff7d782e329e9f8b1063f78616757f944d51 Mon Sep 17 00:00:00 2001
-From: MeiChia Chiu <meichia.chiu@mediatek.com>
-Date: Wed, 22 Nov 2023 21:41:34 +0800
-Subject: [PATCH 073/104] mtk: hostapd: add no_beacon vendor command for cert
-
-Add the vendor command to disable/enable beacon
-
-[Usage]
-hostapd_cli -i <interface> no_beacon <value>
- <value>
- 0: enable beacon
- 1: disable beacon
-
-Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
----
- hostapd/ctrl_iface.c              | 21 +++++++++++++++++++
- hostapd/hostapd_cli.c             |  7 +++++++
- src/ap/ap_drv_ops.c               |  8 ++++++++
- src/ap/ap_drv_ops.h               |  1 +
- src/common/mtk_vendor.h           | 12 +++++++++++
- src/drivers/driver.h              |  7 +++++++
- src/drivers/driver_nl80211.c      | 34 +++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |  1 +
- src/drivers/driver_nl80211_capa.c |  3 +++
- 9 files changed, 94 insertions(+)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index c9b53c64e..0fded7ed4 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4862,6 +4862,24 @@ hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
- 	}
- }
- 
-+static int
-+hostapd_ctrl_iface_disable_beacon(struct hostapd_data *hapd, char *value,
-+				  char *buf, size_t buflen)
-+{
-+	int disable_beacon = atoi(value);
-+
-+	if (disable_beacon < 0) {
-+		wpa_printf(MSG_ERROR, "Invalid value for beacon ctrl");
-+		return -1;
-+	}
-+
-+	if (hostapd_drv_beacon_ctrl(hapd, !disable_beacon) == 0)
-+		return os_snprintf(buf, buflen, "OK\n");
-+	else
-+		return -1;
-+
-+}
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -5512,6 +5530,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 	} else if (os_strncmp(buf, "SET_BACKGROUND_RADAR_MODE", 25) == 0) {
- 		reply_len = hostapd_ctrl_iface_set_background_radar_mode(hapd, buf + 25,
- 									 reply, reply_size);
-+	} else if (os_strncmp(buf, "NO_BEACON ", 10) == 0) {
-+		reply_len = hostapd_ctrl_iface_disable_beacon(hapd, buf + 10, reply,
-+							      reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index e0b175386..7e4485cb8 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1464,6 +1464,11 @@ static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
- 	return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
- }
- 
-+static int hostapd_cli_cmd_disable_beacon(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "NO_BEACON", 1, argc, argv);
-+}
- 
- #ifdef CONFIG_DPP
- 
-@@ -1871,6 +1876,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 		"<value> [0-15] bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0)"},
- 	{ "get_mu", hostapd_cli_cmd_get_mu, NULL,
- 		" = show mu onoff value in 0-15 bitmap"},
-+	{ "no_beacon", hostapd_cli_cmd_disable_beacon, NULL,
-+		"<value> 0: Enable beacon, 1: Disable beacon"},
- #ifdef CONFIG_DPP
- 	{ "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_drv_ops.c b/src/ap/ap_drv_ops.c
-index c71cfe1bd..d6bd157d8 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1388,3 +1388,11 @@ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
- 	return hapd->driver->pp_mode_set(hapd->drv_priv,
- 					 hapd->iconf->pp_mode);
- }
-+
-+int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
-+{
-+	if (!hapd->driver || !hapd->driver->beacon_ctrl)
-+		return 0;
-+	return hapd->driver->beacon_ctrl(hapd->drv_priv, beacon_mode);
-+}
-+
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index ef61001e5..78e5c8d5a 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -170,6 +170,7 @@ int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_ma
- int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_buf);
- int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
- int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
-+int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 6275c141d..5531802b8 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -18,6 +18,7 @@ enum mtk_nl80211_vendor_subcmds {
- 	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
- 	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
- 	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
-+	MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
- };
- 
- enum mtk_vendor_attr_edcca_ctrl {
-@@ -268,6 +269,17 @@ enum mtk_vendor_attr_pp_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
- };
- 
-+enum mtk_vendor_attr_beacon_ctrl {
-+	MTK_VENDOR_ATTR_BEACON_CTRL_UNSPEC,
-+
-+	MTK_VENDOR_ATTR_BEACON_CTRL_MODE,
-+
-+	/* keep last */
-+	NUM_MTK_VENDOR_ATTRS_BEACON_CTRL,
-+	MTK_VENDOR_ATTR_BEACON_CTRL_MAX =
-+		NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
-+};
-+
- #define CSI_MAX_COUNT 256
- #define ETH_ALEN 6
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index be0e89ba3..332a51c55 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5243,6 +5243,13 @@ struct wpa_driver_ops {
- 	 int (*mu_ctrl)(void *priv, u8 mode, void *config);
- 	 int (*mu_dump)(void *priv, u8 *mu_onoff);
- 
-+	/**
-+	 * beacon_ctrl - ctrl on off for beacon
-+	 * @priv: Private driver interface data
-+	 *
-+	 */
-+	int (*beacon_ctrl)(void *priv, u8 beacon_mode);
-+
- 	/**
- 	 * three_wire_ctrl - set three_wire_ctrl mode
- 	 * @priv: Private driver interface data
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index b47ab07ea..e588e7538 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -14111,6 +14111,39 @@ static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
- 
- 	return ret;
- }
-+static int nl80211_beacon_ctrl(void *priv, u8 beacon_mode)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+
-+	if (!drv->mtk_beacon_ctrl_vendor_cmd_avail) {
-+		wpa_printf(MSG_ERROR,
-+			   "nl80211: Driver does not support setting beacon control");
-+		return 0;
-+	}
-+
-+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL) ||
-+		!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+		nla_put_u8(msg, MTK_VENDOR_ATTR_BEACON_CTRL_MODE, beacon_mode)) {
-+		nlmsg_free(msg);
-+		return -ENOBUFS;
-+	}
-+
-+	nla_nest_end(msg, data);
-+
-+	ret = send_and_recv_cmd(drv, msg);
-+
-+	if (ret)
-+		wpa_printf(MSG_ERROR, "Failed to set beacon_ctrl. ret=%d (%s)", ret, strerror(-ret));
-+
-+	return ret;
-+}
-+
- #endif /* CONFIG_IEEE80211AX */
- 
- 
-@@ -15281,6 +15314,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.set_4addr_mode = nl80211_set_4addr_mode,
- 	.mu_ctrl = nl80211_mu_ctrl,
- 	.mu_dump = nl80211_mu_dump,
-+	.beacon_ctrl = nl80211_beacon_ctrl,
- #ifdef CONFIG_DPP
- 	.dpp_listen = nl80211_dpp_listen,
- #endif /* CONFIG_DPP */
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 707bb7fe4..9866c221c 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -210,6 +210,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int mtk_amnt_vendor_cmd_avail:1;
- 	unsigned int mtk_background_radar_vendor_cmd_avail:1;
- 	unsigned int mtk_pp_vendor_cmd_avail:1;
-+	unsigned int mtk_beacon_ctrl_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index ba3c0817b..f3e3d52e2 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1170,6 +1170,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_PP_CTRL:
- 					drv->mtk_pp_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL :
-+					drv->mtk_beacon_ctrl_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0073-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0073-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch
new file mode 100644
index 0000000..0dd558d
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0073-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch
@@ -0,0 +1,36 @@
+From 2c8d9fc4050a47913a4e77fc6fad3118a59a2e01 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 14 Mar 2024 14:31:28 +0800
+Subject: [PATCH 073/126] mtk: hostapd: using MLD addr as SA/BSSID for
+ broadcast DEAUTH
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ap_drv_ops.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 34c2ff211..c7635cae6 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -901,7 +901,7 @@ int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+ 		else
+ 			link_id = hapd->mld_link_id;
+ 
+-		if (ap_sta_is_mld(hapd, sta))
++		if (ap_sta_is_mld(hapd, sta) || is_multicast_ether_addr(addr))
+ 			own_addr = hapd->mld->mld_addr;
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+@@ -922,7 +922,7 @@ int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+ 	if (hapd->conf->mld_ap) {
+ 		struct sta_info *sta = ap_get_sta(hapd, addr);
+ 
+-		if (ap_sta_is_mld(hapd, sta))
++		if (ap_sta_is_mld(hapd, sta) || is_multicast_ether_addr(addr))
+ 			own_addr = hapd->mld->mld_addr;
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0074-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0074-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch
deleted file mode 100644
index 06ee95c..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0074-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch
+++ /dev/null
@@ -1,64 +0,0 @@
-From 817f2a256b166e07d7b0abcee789976b47224429 Mon Sep 17 00:00:00 2001
-From: "amlendu.mishra" <amlendu.mishra@mediatek.com>
-Date: Wed, 13 Dec 2023 18:13:01 +0530
-Subject: [PATCH 074/104] mtk: hostapd: WPS added change to configure AP PIN
- lock timout
-
-added config paramter ap_pin_lockout_time to configure
-AP PIN timeout from hosatpd.conf
-
-Signed-off-by: amlendu.mishra <amlendu.mishra@mediatek.com>
----
- hostapd/config_file.c | 2 ++
- src/ap/ap_config.h    | 1 +
- src/ap/wps_hostapd.c  | 9 ++++++---
- 3 files changed, 9 insertions(+), 3 deletions(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 050ef290e..7bc19479d 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -4191,6 +4191,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		bss->wps_independent = atoi(pos);
- 	} else if (os_strcmp(buf, "ap_setup_locked") == 0) {
- 		bss->ap_setup_locked = atoi(pos);
-+	} else if (os_strcmp(buf, "ap_pin_lockout_time") == 0) {
-+		bss->ap_pin_lockout_time = atoi(pos);
- 	} else if (os_strcmp(buf, "uuid") == 0) {
- 		if (uuid_str2bin(pos, bss->uuid)) {
- 			wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 40edcdaa7..7f48c71f5 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -493,6 +493,7 @@ struct hostapd_bss_config {
- #ifdef CONFIG_WPS
- 	int wps_independent;
- 	int ap_setup_locked;
-+	unsigned int ap_pin_lockout_time;
- 	u8 uuid[16];
- 	char *wps_pin_requests;
- 	char *device_name;
-diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
-index dfc5c3ecb..8a6fc42b2 100644
---- a/src/ap/wps_hostapd.c
-+++ b/src/ap/wps_hostapd.c
-@@ -768,9 +768,12 @@ static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
- 		eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
- 		wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely");
- 	} else if (!hapd->conf->ap_setup_locked) {
--		if (hapd->ap_pin_lockout_time == 0)
--			hapd->ap_pin_lockout_time = 60;
--		else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
-+		if (hapd->ap_pin_lockout_time == 0) {
-+			if (hapd->conf->ap_pin_lockout_time)
-+				hapd->ap_pin_lockout_time = hapd->conf->ap_pin_lockout_time;
-+			else
-+				hapd->ap_pin_lockout_time = 60;
-+		} else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
- 			 (hapd->ap_pin_failures % 3) == 0)
- 			hapd->ap_pin_lockout_time *= 2;
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0074-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0074-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch
new file mode 100644
index 0000000..ce90a5d
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0074-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch
@@ -0,0 +1,30 @@
+From 7e0e24bc6487a6664b6fa209758d00773ed38a8d Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Tue, 27 Feb 2024 15:32:06 +0800
+Subject: [PATCH 074/126] mtk: hostapd: support vht bfee sts can be up to 0x4
+
+Without this commit, the maximum vht bfee sts can only be 0x3. This commit
+support to read BF-ANTENNA-5 to set vht bfee sts capability as 4.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ hostapd/config_file.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 50d63e259..c1bc344dd 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -1190,6 +1190,9 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf,
+ 	if (os_strstr(capab, "[BF-ANTENNA-4]") &&
+ 	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+ 		conf->vht_capab |= (3 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
++	if (os_strstr(capab, "[BF-ANTENNA-5]") &&
++	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
++		conf->vht_capab |= (4 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
+ 	if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") &&
+ 	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
+ 		conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0075-hostapd-mtk-ACS-remove-chan-freq-list-check-when-sca.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0075-hostapd-mtk-ACS-remove-chan-freq-list-check-when-sca.patch
deleted file mode 100644
index 6a9f126..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0075-hostapd-mtk-ACS-remove-chan-freq-list-check-when-sca.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 76d8a7f29b56075df3ad756f65374b2b238c2120 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Tue, 23 Jan 2024 10:52:57 +0800
-Subject: [PATCH 075/104] hostapd: mtk: ACS: remove chan/freq list check when
- scan request and factor calculation
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/acs.c | 12 ------------
- 1 file changed, 12 deletions(-)
-
-diff --git a/src/ap/acs.c b/src/ap/acs.c
-index 4c4c750ab..cb4db7147 100644
---- a/src/ap/acs.c
-+++ b/src/ap/acs.c
-@@ -595,12 +595,6 @@ static void acs_survey_mode_interference_factor(
- 		    iface->conf->acs_exclude_dfs)
- 			continue;
- 
--		if (!is_in_chanlist(iface, chan))
--			continue;
--
--		if (!is_in_freqlist(iface, chan))
--			continue;
--
- 		if (chan->max_tx_power < iface->conf->min_tx_power)
- 			continue;
- 
-@@ -1358,12 +1352,6 @@ static int * acs_request_scan_add_freqs(struct hostapd_iface *iface,
- 		     iface->conf->acs_exclude_dfs))
- 			continue;
- 
--		if (!is_in_chanlist(iface, chan))
--			continue;
--
--		if (!is_in_freqlist(iface, chan))
--			continue;
--
- 		if (chan->max_tx_power < iface->conf->min_tx_power)
- 			continue;
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0075-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0075-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch
new file mode 100644
index 0000000..f6297dc
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0075-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch
@@ -0,0 +1,54 @@
+From e9192ac36cdeb2a3d2bfd979161c0016c19e1a5c Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Thu, 19 Oct 2023 14:08:50 +0800
+Subject: [PATCH 075/126] mtk: hostapd: fix issue that tx status handle with
+ unmatch hostapd_data.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ src/ap/ieee802_11.c                | 11 ++++++++++-
+ src/drivers/driver_nl80211_event.c |  2 +-
+ 2 files changed, 11 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index d26b50031..fbe3f582f 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -6614,11 +6614,20 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
+ #ifdef CONFIG_IEEE80211BE
+ 	if (ap_sta_is_mld(hapd, sta) &&
+ 	    hapd->mld_link_id != sta->mld_assoc_link_id) {
++		struct hostapd_data *temp_hapd = hapd;
++
+ 		/* See ieee80211_ml_link_sta_assoc_cb() for the MLD case */
+ 		wpa_printf(MSG_DEBUG,
+ 			   "%s: MLD: ignore on link station (%d != %d)",
+ 			   __func__, hapd->mld_link_id, sta->mld_assoc_link_id);
+-		return;
++
++		if (temp_hapd->conf->mld_ap && sta->mld_assoc_link_id >= 0) {
++			struct hostapd_data *link_bss;
++
++			link_bss = hostapd_mld_get_link_bss(temp_hapd, sta->mld_assoc_link_id);
++			if (link_bss)
++				hapd = link_bss;
++		}
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 07af6be77..635401564 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -1381,7 +1381,7 @@ static void mlme_event_mgmt(struct i802_bss *bss,
+ 	event.rx_mgmt.ctx = bss->ctx;
+ 	event.rx_mgmt.link_id = link_id;
+ 
+-	wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
++	wpa_supplicant_event(bss->ctx, EVENT_RX_MGMT, &event);
+ }
+ 
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0076-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0076-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch
deleted file mode 100644
index d4e52f8..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0076-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 29b7ebf83cad69c48012eb5a03eb01a2a9fbfcab Mon Sep 17 00:00:00 2001
-From: "fancy.liu" <fancy.liu@mediatek.com>
-Date: Wed, 1 Nov 2023 19:58:05 +0800
-Subject: [PATCH 076/104] mtk: hostapd: Fix chan_switch to usable DFS channel
- fail due to ACS
-
-Step and issue:
-1. Enable ACS in hostapd config;
-2. Bootup and then use hostapd_cli cmd switch channel to a DFS channel;
-3. Will do ACS again, and no work on channel specified in step 2.
-
-Root cause:
-When need do DFS-CAC, hostapd will do intf disable, then set the new
-channel into running config settings, and finally enable intf;
-In the test case, new DFS channel is set to runnint config settings, but
-another param "acs" is still 1 (enable), caused the ACS running when
-intf enabled.
-
-Solution:
-In the hostapd_switch_channel_fallback, need to disable acs if channel
-is valid.
-
-Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
----
- src/ap/hostapd.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index cdbf81e38..636655ea1 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -4580,6 +4580,9 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
- 
- 	iface->freq = freq_params->freq;
- 	iface->conf->channel = freq_params->channel;
-+	if (iface->conf->channel != 0) /* If channel not zero, will disable acs. */
-+		iface->conf->acs = 0;
-+
- 	iface->conf->secondary_channel = freq_params->sec_channel_offset;
- 	hostapd_set_oper_centr_freq_seg0_idx(iface->conf, seg0_idx);
- 	hostapd_set_oper_centr_freq_seg1_idx(iface->conf, seg1_idx);
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0076-mtk-hostapd-add-connac3-csi-control-interface.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0076-mtk-hostapd-add-connac3-csi-control-interface.patch
new file mode 100644
index 0000000..f276767
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0076-mtk-hostapd-add-connac3-csi-control-interface.patch
@@ -0,0 +1,703 @@
+From b0f18aac90c7144db84f880aedb5aeaba3f37305 Mon Sep 17 00:00:00 2001
+From: mtk20656 <chank.chen@mediatek.com>
+Date: Tue, 6 Feb 2024 15:46:05 +0800
+Subject: [PATCH 076/126] mtk: hostapd: add connac3 csi control interface
+
+1. add hostapd_cli interface
+2. add csi set/dump flow
+3. add csi raw data to json
+
+Signed-off-by: mtk20656 <chank.chen@mediatek.com>
+---
+ hostapd/ctrl_iface.c              | 193 ++++++++++++++++++++++++
+ hostapd/hostapd_cli.c             |  16 ++
+ src/ap/ap_drv_ops.c               |  13 ++
+ src/ap/ap_drv_ops.h               |   2 +
+ src/common/mtk_vendor.h           |  33 ++++-
+ src/drivers/driver.h              |  18 +++
+ src/drivers/driver_nl80211.c      | 239 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   3 +
+ 9 files changed, 511 insertions(+), 7 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 76fd25d8b..9bd8a46b3 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -5063,6 +5063,193 @@ hostapd_ctrl_iface_disable_beacon(struct hostapd_data *hapd, char *value,
+ 
+ }
+ 
++static int
++hostapd_ctrl_iface_set_csi(struct hostapd_data *hapd, char *cmd,
++					char *buf, size_t buflen)
++{
++	char *tmp;
++	u8 sta_mac[ETH_ALEN] = {0};
++	u32 csi_para[4] = {0};
++	char mac_str[18] = {0};
++	u8 csi_para_cnt = 0;
++
++	tmp = strtok_r(cmd, ",", &cmd);
++
++	while (tmp) {
++		csi_para_cnt++;
++
++		if (csi_para_cnt <= 4)
++			csi_para[csi_para_cnt - 1] = strtol(tmp, &tmp, 10);
++		else if (csi_para_cnt == 5) {
++			memcpy(mac_str, tmp, sizeof(mac_str) - 1);
++			break;
++		}
++
++		tmp = strtok_r(NULL, ",", &cmd);
++	}
++
++	if (strlen(mac_str)) {	/* user input mac string */
++		if (hwaddr_aton(mac_str, sta_mac) < 0) {
++			wpa_printf(MSG_ERROR, "station mac is not right.\n");
++			return -1;
++		}
++
++		if (hostapd_drv_csi_set(hapd, csi_para[0], csi_para[1], csi_para[2], csi_para[3], sta_mac)) {
++			wpa_printf(MSG_ERROR, "Not able to set csi, %d,%d,%d,%d,%s\n",
++					csi_para[0], csi_para[1], csi_para[2], csi_para[3], mac_str);
++			return -1;
++		}
++	} else {
++		if (hostapd_drv_csi_set(hapd, csi_para[0], csi_para[1], csi_para[2], csi_para[3], NULL)) {
++			wpa_printf(MSG_ERROR, "Not able to set csi, %d,%d,%d,%d\n",
++					csi_para[0], csi_para[1], csi_para[2], csi_para[3]);
++			return -1;
++		}
++	}
++
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
++static int mt76_csi_to_json(char *fname, struct csi_resp_data *resp_buf)
++{
++#define MAX_BUF_SIZE	10000
++	FILE *f;
++	int i;
++
++	if (!fname) {
++		wpa_printf(MSG_ERROR, "csi dump file name is null!\n");
++		return -1;
++	}
++
++	f = fopen(fname, "a+");
++	if (!f) {
++		wpa_printf(MSG_ERROR, "open csi dump file %s failed\n", fname);
++		return -1;
++	}
++
++	if (fwrite("[", 1, 1, f) != 1) {
++		fclose(f);
++		return -1;
++	}
++
++	for (i = 0; i < resp_buf->buf_cnt; i++) {
++		struct csi_data *c = &resp_buf->csi_buf[i];
++		char *pos, *buf;
++		int j;
++
++		buf = malloc(MAX_BUF_SIZE);
++		if (!buf) {
++			fclose(f);
++			return -1;
++		}
++
++		pos = buf;
++		pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
++
++		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->ts);
++		pos += snprintf(pos, MAX_BUF_SIZE, "\"%02x%02x%02x%02x%02x%02x\",", c->ta[0], c->ta[1], c->ta[2], c->ta[3], c->ta[4], c->ta[5]);
++
++		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->rssi);
++		pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->snr);
++		pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->data_bw);
++		pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->pri_ch_idx);
++		pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->rx_mode);
++		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->tx_idx);
++		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->rx_idx);
++		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->chain_info);
++		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->ext_info);
++
++		pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
++		for (j = 0; j < c->data_num; j++) {
++			pos += snprintf(pos, MAX_BUF_SIZE, "%d", c->data_i[j]);
++			if (j != (c->data_num - 1))
++				pos += snprintf(pos, MAX_BUF_SIZE, ",");
++		}
++		pos += snprintf(pos, MAX_BUF_SIZE, "%c,", ']');
++
++		pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
++		for (j = 0; j < c->data_num; j++) {
++			pos += snprintf(pos, MAX_BUF_SIZE, "%d", c->data_q[j]);
++			if (j != (c->data_num - 1))
++				pos += snprintf(pos, MAX_BUF_SIZE, ",");
++		}
++		pos += snprintf(pos, MAX_BUF_SIZE, "%c", ']');
++
++		pos += snprintf(pos, MAX_BUF_SIZE, "%c", ']');
++		if (i != resp_buf->buf_cnt - 1)
++			pos += snprintf(pos, MAX_BUF_SIZE, ",");
++
++		if (fwrite(buf, 1, pos - buf, f) != (pos - buf)) {
++			perror("fwrite");
++			free(buf);
++			fclose(f);
++			return -1;
++		}
++
++		free(buf);
++	}
++
++	if (fwrite("]", 1, 1, f) != 1) {
++		fclose(f);
++		return -1;
++	}
++
++	fclose(f);
++
++	return 0;
++}
++
++static int
++hostapd_ctrl_iface_dump_csi(struct hostapd_data *hapd, char *cmd,
++				char *buf, size_t buflen)
++{
++	char *tmp, *fname;
++	int data_cnt = 0, ret = 0;
++	struct csi_resp_data resp_buf;
++
++	tmp = strtok_r(cmd, ",", &cmd);
++
++	if (!tmp) {
++		wpa_printf(MSG_ERROR, "Error in command format\n");
++		return -1;
++	}
++
++	data_cnt = strtoul(tmp, &tmp, 0);
++
++	if (data_cnt > 3000) {
++		wpa_printf(MSG_ERROR, "Wrong input csi data cnt\n");
++		return -1;
++	}
++
++	fname = strtok_r(NULL, ",", &cmd);
++
++	if (!fname) {
++		wpa_printf(MSG_ERROR, "Error in command format, csi_filename.\n");
++		return -1;
++	}
++
++	resp_buf.csi_buf = (struct csi_data *)os_zalloc(sizeof(struct csi_data) * data_cnt);
++
++	if (resp_buf.csi_buf == NULL) {
++		wpa_printf(MSG_ERROR, "Error in memory allocation\n");
++		return -1;
++	}
++
++	resp_buf.usr_need_cnt = data_cnt;
++	resp_buf.buf_cnt = 0;
++
++	if (hostapd_drv_csi_dump(hapd, (void *)&resp_buf)) {
++		wpa_printf(MSG_ERROR, "Not able to set csi dump\n");
++		os_free(resp_buf.csi_buf);
++		return -1;
++	}
++
++	mt76_csi_to_json(fname, &resp_buf);
++
++	os_free(resp_buf.csi_buf);
++	return 0;
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -5724,6 +5911,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 	} else if (os_strncmp(buf, "NO_BEACON ", 10) == 0) {
+ 		reply_len = hostapd_ctrl_iface_disable_beacon(hapd, buf + 10, reply,
+ 							      reply_size);
++	} else if (os_strncmp(buf, "SET_CSI ", 7) == 0) {
++		reply_len = hostapd_ctrl_iface_set_csi(hapd, buf + 8,
++							reply, reply_size);
++	} else if (os_strncmp(buf, "DUMP_CSI ", 8) == 0) {
++		reply_len = hostapd_ctrl_iface_dump_csi(hapd, buf + 9,
++							reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index a98f76eee..f54fa9997 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1744,6 +1744,18 @@ static int hostapd_cli_cmd_dump_amnt(struct wpa_ctrl *ctrl, int argc,
+ 	return hostapd_cli_cmd(ctrl, "DUMP_AMNT", 1, argc, argv);
+ }
+ 
++static int hostapd_cli_cmd_set_csi(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "SET_CSI", 1, argc, argv);
++}
++
++static int hostapd_cli_cmd_dump_csi(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "DUMP_CSI", 1, argc, argv);
++}
++
+ struct hostapd_cli_cmd {
+ 	const char *cmd;
+ 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -1992,6 +2004,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 		" = Set Station index and mac to monitor"},
+ 	{ "dump_amnt", hostapd_cli_cmd_dump_amnt, NULL,
+ 		" = Dump RSSI of monitoring Station"},
++	{ "set_csi", hostapd_cli_cmd_set_csi, NULL,
++		" = Set csi configuaration"},
++	{ "dump_csi", hostapd_cli_cmd_dump_csi, NULL,
++		" = Dump csi data to a json file"},
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index c7635cae6..a26a3f45d 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1445,3 +1445,16 @@ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
+ 	return hapd->driver->beacon_ctrl(hapd->drv_priv, beacon_mode);
+ }
+ 
++int hostapd_drv_csi_set(struct hostapd_data *hapd, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac)
++{
++	if (!hapd->driver || !hapd->driver->csi_set)
++		return 0;
++	return hapd->driver->csi_set(hapd->drv_priv, hapd->iconf->band_idx, mode, cfg, v1, v2, mac);
++}
++
++int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf)
++{
++	if (!hapd->driver || !hapd->driver->csi_dump)
++		return 0;
++	return hapd->driver->csi_dump(hapd->drv_priv, hapd->iconf->band_idx, dump_buf);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 8c783610c..9f2e86a9b 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -174,6 +174,8 @@ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_
+ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
+ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
+ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode);
++int hostapd_drv_csi_set(struct hostapd_data *hapd, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac);
++int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 9b054ef43..ce3a2ad9a 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -73,12 +73,13 @@ enum mtk_vendor_attr_csi_ctrl {
+ 	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
+ 	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
+ 	MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
+-	MTK_VENDOR_ATTR_CSI_CTRL_INTERVAL,
+ 
+ 	MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
+ 
+ 	MTK_VENDOR_ATTR_CSI_CTRL_DATA,
+ 
++        MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX,
++
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_CSI_CTRL,
+ 	MTK_VENDOR_ATTR_CSI_CTRL_MAX =
+@@ -96,6 +97,7 @@ enum mtk_vendor_attr_csi_data {
+ 	MTK_VENDOR_ATTR_CSI_DATA_BW,
+ 	MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
+ 	MTK_VENDOR_ATTR_CSI_DATA_TA,
++	MTK_VENDOR_ATTR_CSI_DATA_NUM,
+ 	MTK_VENDOR_ATTR_CSI_DATA_I,
+ 	MTK_VENDOR_ATTR_CSI_DATA_Q,
+ 	MTK_VENDOR_ATTR_CSI_DATA_INFO,
+@@ -106,7 +108,7 @@ enum mtk_vendor_attr_csi_data {
+ 	MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
+ 	MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
+ 	MTK_VENDOR_ATTR_CSI_DATA_MODE,
+-	MTK_VENDOR_ATTR_CSI_DATA_H_IDX,
++	MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_CSI_DATA,
+@@ -284,23 +286,40 @@ enum mtk_vendor_attr_beacon_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
+ };
+ 
+-#define CSI_MAX_COUNT 256
++#define CSI_BW20_DATA_COUNT	64
++#define CSI_BW40_DATA_COUNT	128
++#define CSI_BW80_DATA_COUNT	256
++#define CSI_BW160_DATA_COUNT	512
++#define CSI_BW320_DATA_COUNT	1024
+ #define ETH_ALEN 6
+ 
+ struct csi_data {
+-	s16 data_i[CSI_MAX_COUNT];
+-	s16 data_q[CSI_MAX_COUNT];
++	u8 ch_bw;
++	u16 data_num;
++	s16 data_i[CSI_BW320_DATA_COUNT];
++	s16 data_q[CSI_BW320_DATA_COUNT];
++	u8 band;
+ 	s8 rssi;
+ 	u8 snr;
+ 	u32 ts;
+ 	u8 data_bw;
+ 	u8 pri_ch_idx;
+ 	u8 ta[ETH_ALEN];
+-	u32 info;
++	u32 ext_info;
+ 	u8 rx_mode;
+-	u32 h_idx;
++	u32 chain_info;
+ 	u16 tx_idx;
+ 	u16 rx_idx;
++	u32 segment_num;
++	u8 remain_last;
++	u16 pkt_sn;
++	u8 tr_stream;
++};
++
++struct csi_resp_data {
++	u16 usr_need_cnt;
++	u16 buf_cnt;
++	struct csi_data *csi_buf;
+ };
+ 
+ #define AIR_MONITOR_MAX_ENTRY 16
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index ec00c36e4..48cb93cfd 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5369,6 +5369,24 @@ struct wpa_driver_ops {
+ #ifdef CONFIG_IEEE80211BE
+ 	int (*get_mld_addr)(void *priv, u8 *addr);
+ #endif
++	/**
++	 * csi_set - Set csi related mode and parameter
++	 * @priv: Private driver interface data
++	 * @band_idx: band idx
++	 * @mode: Csi mode parameter
++	 * @cfg: Csi config parameter
++	 * @v1: Value1
++	 * @v2: Value2
++	 * @mac: Station mac for station filter
++	 */
++	int (*csi_set)(void *priv, u8 band_idx, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac);
++	/**
++	* csi_dump - Dump csi data to json file
++	* @priv: Private driver interface data
++	* @band_idx: band idx
++	* @dump_buf: Dump_struct that store csi data and related info
++	*/
++	int (*csi_dump)(void *priv, u8 band_idx, void *dump_buf);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 8f7215d62..afb068091 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -161,6 +161,36 @@ pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
+ 	[MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
+ };
+ 
++static struct nla_policy csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
++	[MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG] = { .type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2] = { .type = NLA_U32 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR] = { .type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM] = { .type = NLA_U16 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_DATA] = { .type = NLA_NESTED },
++};
++
++static struct nla_policy csi_data_policy[NUM_MTK_VENDOR_ATTRS_CSI_DATA] = {
++	[MTK_VENDOR_ATTR_CSI_DATA_VER] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_DATA_TS] = { .type = NLA_U32 },
++	[MTK_VENDOR_ATTR_CSI_DATA_RSSI] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_DATA_SNR] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_DATA_BW] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_DATA_TA] = { .type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_CSI_DATA_NUM] = { .type = NLA_U32 },
++	[MTK_VENDOR_ATTR_CSI_DATA_I] = { .type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_CSI_DATA_Q] = { .type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_CSI_DATA_INFO] = { .type = NLA_U32 },
++	[MTK_VENDOR_ATTR_CSI_DATA_TX_ANT] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_DATA_RX_ANT] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_DATA_MODE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO] = { .type = NLA_U32 },
++};
++
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+ 	struct nl_sock *handle;
+@@ -15220,6 +15250,213 @@ static int nl80211_get_mld_addr(void *priv, u8 *addr)
+ }
+ #endif
+ 
++static int
++nl80211_csi_set(void *priv, u8 band_idx, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	void *tb1, *tb2;
++	int ret, i;
++
++	if (!drv->mtk_csi_vendor_cmd_avail) {
++		wpa_printf(MSG_ERROR,
++			"nl80211: Driver does not support csi");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++			nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++	if (!data)
++		goto fail;
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX, band_idx);
++
++	tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG | NLA_F_NESTED);
++	if (!tb1)
++		goto fail;
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE, mode);
++	nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE, cfg);
++	nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1, v1);
++	nla_put_u32(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2, v2);
++
++	nla_nest_end(msg, tb1);
++
++	if (mac) {
++		tb2 = nla_nest_start(msg, MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR | NLA_F_NESTED);
++		if (!tb2)
++			goto fail;
++
++		for (i = 0; i < ETH_ALEN; i++)
++			nla_put_u8(msg, i, mac[i]);
++
++		nla_nest_end(msg, tb2);
++	}
++
++	nla_nest_end(msg, data);
++
++	ret = send_and_recv_cmd(drv, msg);
++
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set csi. ret=%d (%s)",
++			ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++
++}
++
++static int
++mt76_csi_dump_cb(struct nl_msg *msg, void *arg)
++{
++	struct nlattr *tb[NL80211_ATTR_MAX + 1];
++	struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_CSI_CTRL];
++	struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_CSI_DATA];
++	struct nlattr *attr, *cur, *data;
++	int len = 0, rem, idx;
++	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++	struct csi_resp_data *csi_resp = (struct csi_resp_data *)arg;
++	struct csi_data *c = csi_resp->csi_buf;
++
++	c += csi_resp->buf_cnt;
++
++	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++		genlmsg_attrlen(gnlh, 0), NULL);
++
++	attr = tb[NL80211_ATTR_VENDOR_DATA];
++	if (!attr)
++		return NL_SKIP;
++
++	nla_parse_nested(tb1, MTK_VENDOR_ATTR_CSI_CTRL_MAX,
++			attr, csi_ctrl_policy);
++
++	if (!tb1[MTK_VENDOR_ATTR_CSI_CTRL_DATA])
++		return NL_SKIP;
++
++	nla_parse_nested(tb2, MTK_VENDOR_ATTR_CSI_DATA_MAX,
++			tb1[MTK_VENDOR_ATTR_CSI_CTRL_DATA], csi_data_policy);
++
++	if (!(tb2[MTK_VENDOR_ATTR_CSI_DATA_VER] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_TS] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_RSSI] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_SNR] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_BW] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_TA] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_I] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_Q] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_INFO] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_MODE] &&
++	      tb2[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO])) {
++		fprintf(stderr, "Attributes error for CSI data\n");
++		return NL_SKIP;
++	}
++
++	c->rssi = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_RSSI]);
++	c->snr = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_SNR]);
++	c->data_bw = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_BW]);
++	c->pri_ch_idx = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX]);
++	c->rx_mode = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_MODE]);
++
++	c->tx_idx = nla_get_u16(tb2[MTK_VENDOR_ATTR_CSI_DATA_TX_ANT]);
++	c->rx_idx = nla_get_u16(tb2[MTK_VENDOR_ATTR_CSI_DATA_RX_ANT]);
++
++	c->ext_info = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_INFO]);
++	c->chain_info = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO]);
++
++	c->ts = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_TS]);
++
++	c->data_num = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_NUM]);
++
++	idx = 0;
++	nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_TA], rem) {
++		if (idx < ETH_ALEN)
++			c->ta[idx++] = nla_get_u8(cur);
++	}
++
++	idx = 0;
++	nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_I], rem) {
++		if (idx < c->data_num)
++			c->data_i[idx++] = nla_get_u16(cur);
++	}
++
++	idx = 0;
++	nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_Q], rem) {
++		if (idx < c->data_num)
++			c->data_q[idx++] = nla_get_u16(cur);
++	}
++
++	csi_resp->buf_cnt++;
++
++	return NL_SKIP;
++}
++
++static int
++nl80211_csi_dump(void *priv, u8 band_idx, void *dump_buf)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++	struct csi_resp_data *csi_resp;
++	u16 pkt_num, i;
++
++	if (!drv->mtk_csi_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			"nl80211: Driver does not support csi");
++		return 0;
++	}
++
++	csi_resp = (struct csi_resp_data *)dump_buf;
++	pkt_num =  csi_resp->usr_need_cnt;
++
++	if (pkt_num > 3000)
++		return -EINVAL;
++
++#define CSI_DUMP_PER_NUM	3
++	for (i = 0; i < pkt_num / CSI_DUMP_PER_NUM; i++) {
++		msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
++		if (!msg)
++			goto fail;
++
++		if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++				nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++				MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL))
++			goto fail;
++
++		data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++		if (!data)
++			goto fail;
++
++		nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX, band_idx);
++		nla_put_u16(msg, MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM, CSI_DUMP_PER_NUM);
++
++		nla_nest_end(msg, data);
++
++		ret = send_and_recv_resp(drv, msg, mt76_csi_dump_cb, dump_buf);
++	}
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+ 	.desc = "Linux nl80211/cfg80211",
+@@ -15403,4 +15640,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ #ifdef CONFIG_IEEE80211BE
+ 	.get_mld_addr = nl80211_get_mld_addr,
+ #endif
++	.csi_set = nl80211_csi_set,
++	.csi_dump = nl80211_csi_dump,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index a0a62e536..b710b50cd 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -212,6 +212,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_background_radar_vendor_cmd_avail:1;
+ 	unsigned int mtk_pp_vendor_cmd_avail:1;
+ 	unsigned int mtk_beacon_ctrl_vendor_cmd_avail:1;
++	unsigned int mtk_csi_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 30f89b687..5a53700d1 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1173,6 +1173,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL :
+ 					drv->mtk_beacon_ctrl_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL:
++					drv->mtk_csi_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0077-Revert-ACS-upstream-changes.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0077-Revert-ACS-upstream-changes.patch
deleted file mode 100644
index 16f32cf..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0077-Revert-ACS-upstream-changes.patch
+++ /dev/null
@@ -1,398 +0,0 @@
-From 5a471a9025d9bf2a871339f5306e5c9050357703 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Mon, 29 Jan 2024 10:26:53 +0800
-Subject: [PATCH 077/104] Revert ACS upstream changes
-
-- 348c047af ACS: More consistent checking of the best channel pointer
-- 98f3bd26d ACS: Extend the 320 MHz support
-- 733de8568 ACS: Fix not selecting the best channel in the segment
-- dc57ede01 tests: Full validation of ACS selecting HT40- channel
-- 4881accbb ACS: Add HT40- support in the 2.4 GHz band
-- 29f38ebcf ACS: Check whether iface->current_mode is NULL before use
-- 6f014c0d0 ACS: Add 320 MHz support for EHT
-
-Note that "e6f2494c3 hostapd: Add eht_bw320_offset configuration option"
-is not reverted due to conflict.
----
- src/ap/acs.c               | 160 +++++++++----------------------------
- tests/hwsim/test_ap_acs.py |  19 ++---
- 2 files changed, 41 insertions(+), 138 deletions(-)
-
-diff --git a/src/ap/acs.c b/src/ap/acs.c
-index cb4db7147..cfa4a7d27 100644
---- a/src/ap/acs.c
-+++ b/src/ap/acs.c
-@@ -245,8 +245,6 @@ enum bw_type {
- 	ACS_BW40,
- 	ACS_BW80,
- 	ACS_BW160,
--	ACS_BW320_1,
--	ACS_BW320_2,
- };
- 
- struct bw_item {
-@@ -288,20 +286,10 @@ static const struct bw_item bw_160[] = {
- 	{ 6435, 6575, 111 }, { 6595, 6735, 143 },
- 	{ 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
- };
--static const struct bw_item bw_320_1[] = {
--	{ 5955, 6255, 31 }, { 6275, 6575, 95 }, { 6595, 6895, 159 },
--	{ -1, -1, -1 }
--};
--static const struct bw_item bw_320_2[] = {
--	{ 6115, 6415, 63 }, { 6435, 6735, 127 }, { 6755, 7055, 191 },
--	{ -1, -1, -1 }
--};
- static const struct bw_item *bw_desc[] = {
- 	[ACS_BW40] = bw_40,
- 	[ACS_BW80] = bw_80,
- 	[ACS_BW160] = bw_160,
--	[ACS_BW320_1] = bw_320_1,
--	[ACS_BW320_2] = bw_320_2,
- };
- 
- 
-@@ -773,42 +761,6 @@ static void acs_update_puncturing_bitmap(struct hostapd_iface *iface,
- #endif /* CONFIG_IEEE80211BE */
- 
- 
--static bool
--acs_usable_bw320_chan(struct hostapd_iface *iface,
--		      struct hostapd_channel_data *chan, int *bw320_offset)
--{
--	const char *bw320_str[] = { "320 MHz", "320 MHz-1", "320 MHz-2" };
--	int conf_bw320_offset = hostapd_get_bw320_offset(iface->conf);
--
--	*bw320_offset = 0;
--	switch (conf_bw320_offset) {
--	case 1:
--		if (acs_usable_bw_chan(chan, ACS_BW320_1))
--			*bw320_offset = 1;
--		break;
--	case 2:
--		if (acs_usable_bw_chan(chan, ACS_BW320_2))
--			*bw320_offset = 2;
--		break;
--	case 0:
--	default:
--		conf_bw320_offset = 0;
--		if (acs_usable_bw_chan(chan, ACS_BW320_1))
--			*bw320_offset = 1;
--		else if (acs_usable_bw_chan(chan, ACS_BW320_2))
--			*bw320_offset = 2;
--		break;
--	}
--
--	if (!*bw320_offset)
--		wpa_printf(MSG_DEBUG,
--			   "ACS: Channel %d: not allowed as primary channel for %s bandwidth",
--			   chan->chan, bw320_str[conf_bw320_offset]);
--
--	return *bw320_offset != 0;
--}
--
--
- static void
- acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 			 struct hostapd_hw_modes *mode,
-@@ -820,18 +772,14 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 	struct hostapd_channel_data *chan, *adj_chan = NULL, *best;
- 	long double factor;
- 	int i, j;
--	int bw320_offset = 0, ideal_bw320_offset = 0;
- 	unsigned int k;
--	int secondary_channel = 1, freq_offset;
--
--	if (is_24ghz_mode(mode->mode))
--		secondary_channel = iface->conf->secondary_channel;
- 
- 	for (i = 0; i < mode->num_channels; i++) {
--		double total_weight = 0;
-+		double total_weight;
- 		struct acs_bias *bias, tmp_bias;
-+		bool update_best = true;
- 
--		chan = &mode->channels[i];
-+		best = chan = &mode->channels[i];
- 
- 		/* Since in the current ACS implementation the first channel is
- 		 * always a primary channel, skip channels not available as
-@@ -863,7 +811,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 		    iface->conf->country[2] == 0x4f)
- 			continue;
- 
--		if (!chan_bw_allowed(chan, bw, secondary_channel != -1, 1)) {
-+		if (!chan_bw_allowed(chan, bw, 1, 1)) {
- 			wpa_printf(MSG_DEBUG,
- 				   "ACS: Channel %d: BW %u is not supported",
- 				   chan->chan, bw);
-@@ -884,8 +832,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 		}
- 
- 		if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
--		    (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
--		     iface->conf->ieee80211be)) {
-+		    (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
- 			if (hostapd_get_oper_chwidth(iface->conf) ==
- 			    CONF_OPER_CHWIDTH_80MHZ &&
- 			    !acs_usable_bw_chan(chan, ACS_BW80)) {
-@@ -905,25 +852,13 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 			}
- 		}
- 
--		if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
--		    iface->conf->ieee80211be) {
--			if (hostapd_get_oper_chwidth(iface->conf) ==
--			    CONF_OPER_CHWIDTH_320MHZ &&
--			    !acs_usable_bw320_chan(iface, chan, &bw320_offset))
--				continue;
--		}
--
- 		factor = 0;
--		best = NULL;
--		if (acs_usable_chan(chan)) {
-+		if (acs_usable_chan(chan))
- 			factor = chan->interference_factor;
--			total_weight = 1;
--			best = chan;
--		}
-+		total_weight = 1;
- 
- 		for (j = 1; j < n_chans; j++) {
--			adj_chan = acs_find_chan(iface, chan->freq +
--						 j * secondary_channel * 20);
-+			adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
- 			if (!adj_chan)
- 				break;
- 
-@@ -934,14 +869,16 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 				break;
- 			}
- 
--			if (!acs_usable_chan(adj_chan))
--				continue;
--
--			factor += adj_chan->interference_factor;
--			total_weight += 1;
-+			if (acs_usable_chan(adj_chan)) {
-+				factor += adj_chan->interference_factor;
-+				total_weight += 1;
-+			} else {
-+				update_best = false;
-+			}
- 
- 			/* find the best channel in this segment */
--			if (!best || adj_chan->interference_factor <
-+			if (update_best &&
-+			    adj_chan->interference_factor <
- 			    best->interference_factor)
- 				best = adj_chan;
- 		}
-@@ -954,9 +891,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 
- 		/* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less
- 		 * crowded primary channel if one was found in the segment */
--		if (iface->current_mode &&
--		    iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
--		    best && chan != best) {
-+		if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
-+		    chan != best) {
- 			wpa_printf(MSG_DEBUG,
- 				   "ACS: promoting channel %d over %d (less interference %Lg/%Lg)",
- 				   best->chan, chan->chan,
-@@ -969,9 +905,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 		 * channel interference factor. */
- 		if (is_24ghz_mode(mode->mode)) {
- 			for (j = 0; j < n_chans; j++) {
--				freq_offset = j * 20 * secondary_channel;
- 				adj_chan = acs_find_chan(iface, chan->freq +
--							 freq_offset - 5);
-+							 (j * 20) - 5);
- 				if (adj_chan && acs_usable_chan(adj_chan)) {
- 					factor += ACS_ADJ_WEIGHT *
- 						adj_chan->interference_factor;
-@@ -979,7 +914,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 				}
- 
- 				adj_chan = acs_find_chan(iface, chan->freq +
--							 freq_offset - 10);
-+							 (j * 20) - 10);
- 				if (adj_chan && acs_usable_chan(adj_chan)) {
- 					factor += ACS_NEXT_ADJ_WEIGHT *
- 						adj_chan->interference_factor;
-@@ -987,7 +922,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 				}
- 
- 				adj_chan = acs_find_chan(iface, chan->freq +
--							 freq_offset + 5);
-+							 (j * 20) + 5);
- 				if (adj_chan && acs_usable_chan(adj_chan)) {
- 					factor += ACS_ADJ_WEIGHT *
- 						adj_chan->interference_factor;
-@@ -995,7 +930,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 				}
- 
- 				adj_chan = acs_find_chan(iface, chan->freq +
--							 freq_offset + 10);
-+							 (j * 20) + 10);
- 				if (adj_chan && acs_usable_chan(adj_chan)) {
- 					factor += ACS_NEXT_ADJ_WEIGHT *
- 						adj_chan->interference_factor;
-@@ -1004,9 +939,6 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 			}
- 		}
- 
--		if (total_weight == 0)
--			continue;
--
- 		factor /= total_weight;
- 
- 		bias = NULL;
-@@ -1044,7 +976,6 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 
- 			*ideal_factor = factor;
- 			*ideal_chan = chan;
--			ideal_bw320_offset = bw320_offset;
- 
- #ifdef CONFIG_IEEE80211BE
- 			if (iface->conf->ieee80211be)
-@@ -1055,13 +986,9 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 		}
- 
- 		/* This channel would at least be usable */
--		if (!(*rand_chan)) {
-+		if (!(*rand_chan))
- 			*rand_chan = chan;
--			ideal_bw320_offset = bw320_offset;
--		}
- 	}
--
--	hostapd_set_and_check_bw320_offset(iface->conf, ideal_bw320_offset);
- }
- 
- 
-@@ -1088,12 +1015,19 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
- 		goto bw_selected;
- 	}
- 
-+	/* TODO: HT40- support */
-+
-+	if (iface->conf->ieee80211n &&
-+	    iface->conf->secondary_channel == -1) {
-+		wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
-+		return NULL;
-+	}
-+
- 	if (iface->conf->ieee80211n &&
- 	    iface->conf->secondary_channel)
- 		n_chans = 2;
- 
--	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
--	    iface->conf->ieee80211be) {
-+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
- 		switch (hostapd_get_oper_chwidth(iface->conf)) {
- 		case CONF_OPER_CHWIDTH_80MHZ:
- 			n_chans = 4;
-@@ -1101,9 +1035,6 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
- 		case CONF_OPER_CHWIDTH_160MHZ:
- 			n_chans = 8;
- 			break;
--		case CONF_OPER_CHWIDTH_320MHZ:
--			n_chans = 16;
--			break;
- 		default:
- 			break;
- 		}
-@@ -1153,8 +1084,7 @@ static void acs_adjust_secondary(struct hostapd_iface *iface)
- 	    acs_find_mode(iface, iface->freq) != HOSTAPD_MODE_IEEE80211A)
- 		return;
- 
--	wpa_printf(MSG_DEBUG,
--		   "ACS: Adjusting HT/VHT/HE/EHT secondary frequency");
-+	wpa_printf(MSG_DEBUG, "ACS: Adjusting HT/VHT/HE secondary frequency");
- 
- 	for (i = 0; bw_desc[ACS_BW40][i].first != -1; i++) {
- 		if (iface->freq == bw_desc[ACS_BW40][i].first)
-@@ -1169,7 +1099,7 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
- {
- 	int center;
- 
--	wpa_printf(MSG_DEBUG, "ACS: Adjusting center frequency");
-+	wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
- 
- 	switch (hostapd_get_oper_chwidth(iface->conf)) {
- 	case CONF_OPER_CHWIDTH_USE_HT:
-@@ -1187,29 +1117,12 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
- 		break;
- 	case CONF_OPER_CHWIDTH_160MHZ:
- 		center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
--		break;
--	case CONF_OPER_CHWIDTH_320MHZ:
--		switch (hostapd_get_bw320_offset(iface->conf)) {
--		case 1:
--			center = acs_get_bw_center_chan(iface->freq,
--							ACS_BW320_1);
--			break;
--		case 2:
--			center = acs_get_bw_center_chan(iface->freq,
--							ACS_BW320_2);
--			break;
--		default:
--			wpa_printf(MSG_INFO,
--				   "ACS: BW320 offset is not selected");
--			return;
--		}
--
- 		break;
- 	default:
- 		/* TODO: How can this be calculated? Adjust
- 		 * acs_find_ideal_chan() */
- 		wpa_printf(MSG_INFO,
--			   "ACS: Only VHT20/40/80/160/320 is supported now");
-+			   "ACS: Only VHT20/40/80/160 is supported now");
- 		return;
- 	}
- 
-@@ -1272,8 +1185,7 @@ static void acs_study(struct hostapd_iface *iface)
- 	iface->conf->punct_bitmap = ideal_chan->punct_bitmap;
- #endif /* CONFIG_IEEE80211BE */
- 
--	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
--	    iface->conf->ieee80211be) {
-+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
- 		acs_adjust_secondary(iface);
- 		acs_adjust_center_freq(iface);
- 	}
-diff --git a/tests/hwsim/test_ap_acs.py b/tests/hwsim/test_ap_acs.py
-index 001a5d4fd..e1359b6eb 100644
---- a/tests/hwsim/test_ap_acs.py
-+++ b/tests/hwsim/test_ap_acs.py
-@@ -205,20 +205,11 @@ def test_ap_acs_40mhz_minus(dev, apdev):
-     params['acs_num_scans'] = '1'
-     params['chanlist'] = '1 11'
-     hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
--    wait_acs(hapd)
--
--    freq = hapd.get_status_field("freq")
--    if int(freq) < 2400:
--        raise Exception("Unexpected frequency")
--    sec = hapd.get_status_field("secondary_channel")
--    if int(sec) != -1:
--        raise Exception("Unexpected secondary_channel: " + sec)
--
--    dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
--    sig = dev[0].request("SIGNAL_POLL").splitlines()
--    logger.info("SIGNAL_POLL: " + str(sig))
--    if "WIDTH=40 MHz" not in sig:
--        raise Exception("Station did not report 40 MHz bandwidth")
-+    ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=10)
-+    if not ev:
-+        raise Exception("ACS start timed out")
-+    # HT40- is not currently supported in hostapd ACS, so do not try to connect
-+    # or verify that this operation succeeded.
- 
- def test_ap_acs_5ghz(dev, apdev):
-     """Automatic channel selection on 5 GHz"""
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0077-mtk-hostapd-add-support-for-basic-MLD-Extender.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0077-mtk-hostapd-add-support-for-basic-MLD-Extender.patch
new file mode 100644
index 0000000..12d2f11
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0077-mtk-hostapd-add-support-for-basic-MLD-Extender.patch
@@ -0,0 +1,271 @@
+From 0a10a7b41777ea926967d603bd4e14f9bca43bca Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 2 Apr 2024 15:36:29 +0800
+Subject: [PATCH 077/126] mtk: hostapd: add support for basic MLD Extender
+
+Add basic MLD Extender support, including
+1. Extender STA stops all Extender AP's links before scaning.
+2. After finishing the connection with root AP, Extender STA
+   synchronizes control channel with each link on the Extender
+   AP.
+
+Advanced support includes BW cynchronization and channel switch.
+
+Add a check for band_idx, if the iface has different band_idx from the
+one wpa_s provided, this iface is not the target.
+
+Also add a log for switching iface.
+
+Legacy AP interfaces that sharing the same hostapd_iface with the target
+hapd should also be disabled.
+
+If parsing 'band_idx' fails, iface starting only works for current
+iface.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ucode.c         | 60 ++++++++++++++++++++++++++++++++++++++----
+ src/utils/ucode.c      |  4 ++-
+ wpa_supplicant/ucode.c | 45 +++++++++++++++++++++++++------
+ 3 files changed, 95 insertions(+), 14 deletions(-)
+
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index 8c3212f6f..68f76dbe5 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -9,6 +9,7 @@
+ #include "ap_drv_ops.h"
+ #include "dfs.h"
+ #include "acs.h"
++#include "ieee802_11.h"
+ #include <libubox/uloop.h>
+ 
+ static uc_resource_type_t *global_type, *bss_type, *iface_type;
+@@ -491,14 +492,16 @@ static uc_value_t *
+ uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
+ {
+ 	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
+-	int i;
+-
+-	wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
+-			iface->phy, hostapd_state_text(iface->state));
++	struct hostapd_data *first_hapd;
++	struct hostapd_bss_config *conf;
++	int i, j;
+ 
+ 	if (!iface)
+ 		return NULL;
+ 
++	wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
++			iface->phy, hostapd_state_text(iface->state));
++
+ 	if (iface->state != HAPD_IFACE_ENABLED)
+ 		uc_hostapd_disable_iface(iface);
+ 
+@@ -509,6 +512,34 @@ uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
+ 		hapd->beacon_set_done = 0;
+ 	}
+ 
++#ifdef CONFIG_IEEE80211BE
++	first_hapd = iface->bss[0];
++	conf = first_hapd->conf;
++	for (i = 0; conf->mld_ap && i < iface->interfaces->count; i++) {
++		struct hostapd_iface *h = iface->interfaces->iface[i];
++		struct hostapd_data *h_hapd = h->bss[0];
++		struct hostapd_bss_config *hconf = h_hapd->conf;
++
++		if (h == iface) {
++			wpa_printf(MSG_DEBUG, "MLD: Skip own interface");
++			continue;
++		}
++
++		if (!hconf->mld_ap) {
++			wpa_printf(MSG_DEBUG,
++				   "MLD: Skip non MLD");
++			continue;
++		}
++
++		if (hostapd_is_ml_partner(first_hapd, h_hapd)) {
++			for (j = 0; j < h->num_bss; j++) {
++				hostapd_drv_stop_ap(h->bss[j]);
++				h->bss[j]->beacon_set_done = 0;
++			}
++		}
++	}
++#endif /* CONFIG_IEEE80211BE */
++
+ 	return NULL;
+ }
+ 
+@@ -516,11 +547,12 @@ static uc_value_t *
+ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
+ {
+ 	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++	struct hostapd_data *tmp_hapd;
+ 	uc_value_t *info = uc_fn_arg(0);
+ 	struct hostapd_config *conf;
+ 	bool changed = false;
+ 	uint64_t intval;
+-	int i;
++	int i, band_idx;
+ 
+ 	wpa_printf(MSG_INFO, "ucode: mtk: start iface for %s in state %s\n",
+ 			iface->phy, hostapd_state_text(iface->state));
+@@ -536,6 +568,24 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
+ 	if (ucv_type(info) != UC_OBJECT)
+ 		return NULL;
+ 
++	intval = ucv_int64_get(ucv_object_get(info, "band_idx", NULL));
++	band_idx = errno ? iface->conf->band_idx : intval;
++
++#ifdef CONFIG_IEEE80211BE
++	if (hostapd_is_mld_ap(iface->bss[0])) {
++		for_each_mld_link(tmp_hapd, iface->bss[0]) {
++			if (band_idx == tmp_hapd->iconf->band_idx) {
++				wpa_printf(MSG_INFO, "ucode: mtk: MLD: switch to iface with band_idx %d \n", band_idx);
++				iface = tmp_hapd->iface;
++				break;
++			}
++		}
++	}
++
++	if (band_idx != iface->bss[0]->iconf->band_idx)
++		return NULL;
++#endif /* CONFIG_IEEE80211BE */
++
+ #define UPDATE_VAL(field, name)							\
+ 	if ((intval = ucv_int64_get(ucv_object_get(info, name, NULL))) &&	\
+ 		!errno && intval != conf->field) do {				\
+diff --git a/src/utils/ucode.c b/src/utils/ucode.c
+index 6f82382f3..81d472f6b 100644
+--- a/src/utils/ucode.c
++++ b/src/utils/ucode.c
+@@ -110,7 +110,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 	uc_value_t *freq = uc_fn_arg(0);
+ 	uc_value_t *sec = uc_fn_arg(1);
+ 	int width = ucv_uint64_get(uc_fn_arg(2));
+-	int bw320_offset = 1;
++	int bw320_offset = 1, band_idx;
+ 	int freq_val, center_idx, center_ofs;
+ 	enum oper_chan_width chanwidth;
+ 	enum hostapd_hw_mode hw_mode;
+@@ -123,6 +123,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 		return NULL;
+ 
+ 	freq_val = ucv_int64_get(freq);
++	band_idx = ucv_int64_get(uc_fn_arg(4));
+ 	if (ucv_type(sec) == UC_INTEGER)
+ 		sec_channel = ucv_int64_get(sec);
+ 	else if (sec)
+@@ -183,6 +184,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 	ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
+ 	ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
+ 	ucv_object_add(ret, "oper_chwidth", ucv_int64_new(chanwidth));
++	ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
+ 
+ 	if (chanwidth == CONF_OPER_CHWIDTH_USE_HT && !sec_channel) {
+ 		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(channel));
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+index 542ca25c9..ac0639a90 100644
+--- a/wpa_supplicant/ucode.c
++++ b/wpa_supplicant/ucode.c
+@@ -246,12 +246,13 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ {
+ 	struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
+ 	struct wpa_bss *bss;
+-	uc_value_t *ret, *val;
++	uc_value_t *ret, *val, *link_obj = uc_fn_arg(0);
+ 	struct wpa_channel_info ci;
+ 	u8 op_class, channel;
+-	enum oper_chan_width ch_width;
+-	int center_freq1, bw320_offset = 1, is_24ghz;
++	enum oper_chan_width ch_width = CONF_OPER_CHWIDTH_USE_HT;
++	int center_freq1, bw320_offset = 1, is_24ghz, band_idx;
+ 	enum hostapd_hw_mode hw_mode;
++	int link_id = ucv_int64_get(link_obj);
+ 
+ 	if (!wpa_s)
+ 		return NULL;
+@@ -261,13 +262,25 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 	val = ucv_string_new(wpa_supplicant_state_txt(wpa_s->wpa_state));
+ 	ucv_object_add(ret, "state", ucv_get(val));
+ 
+-	bss = wpa_s->current_bss;
++	bss = link_id == -1 ? wpa_s->current_bss : wpa_s->links[link_id].bss;
+ 	if (bss) {
+ 		int sec_chan = 0;
+ 
+ 		hw_mode = ieee80211_freq_to_chan(bss->freq, &channel);
+ 		is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
+ 			hw_mode == HOSTAPD_MODE_IEEE80211B;
++		/*
++		 * Assume that the mapping between band and band_idx is
++		 * 2 GHz band: band_idx 0
++		 * 5 GHz band: band_idx 1
++		 * 6 GHz band: band_idx 2
++		 * */
++		if (is_24ghz)
++			band_idx = 0;
++		else if (IS_5GHZ(bss->freq))
++			band_idx = 1;
++		else if (is_6ghz_freq(bss->freq))
++			band_idx = 2;
+ 
+ 		wpa_drv_channel_info(wpa_s, &ci);
+ 		center_freq1 = ci.center_frq1;
+@@ -279,11 +292,10 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 				sec_chan = (bss->freq / 20) & 1 ? 1 : -1;
+ 		}
+ 
+-		if (ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
+-						  sec_chan, &op_class, &channel))
+-			return NULL;
++		if (!ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
++						   sec_chan, &op_class, &channel))
++			ch_width = op_class_to_ch_width(op_class);
+ 
+-		ch_width = op_class_to_ch_width(op_class);
+ 		if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
+ 		    (center_freq1 == 6265) || center_freq1 == 6585 ||
+ 		     center_freq1 == 6905) {
+@@ -295,6 +307,7 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 		ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
+ 		ucv_object_add(ret, "ch_width", ucv_int64_new(ch_width));
+ 		ucv_object_add(ret, "bw320_offset", ucv_int64_new(bw320_offset));
++		ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
+ 	}
+ 
+ #ifdef CONFIG_MESH
+@@ -309,6 +322,21 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 	return ret;
+ }
+ 
++static uc_value_t *
++uc_wpas_iface_get_valid_links(uc_vm_t *vm, size_t nargs)
++{
++	struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
++	uc_value_t *ret;
++
++	if (!wpa_s)
++		return NULL;
++
++	ret = ucv_object_new(vm);
++	ucv_object_add(ret, "valid_links", ucv_uint64_new(wpa_s->valid_links));
++
++	return ret;
++}
++
+ int wpas_ucode_init(struct wpa_global *gl)
+ {
+ 	static const uc_function_list_t global_fns[] = {
+@@ -320,6 +348,7 @@ int wpas_ucode_init(struct wpa_global *gl)
+ 	};
+ 	static const uc_function_list_t iface_fns[] = {
+ 		{ "status", uc_wpas_iface_status },
++		{ "get_valid_links", uc_wpas_iface_get_valid_links },
+ 	};
+ 	uc_value_t *data, *proto;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0078-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0078-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch
deleted file mode 100644
index 3f267b0..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0078-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch
+++ /dev/null
@@ -1,379 +0,0 @@
-From 12fe53a7919abb791658bcc7a74b8131f0362300 Mon Sep 17 00:00:00 2001
-From: "fancy.liu" <fancy.liu@mediatek.com>
-Date: Thu, 28 Sep 2023 18:03:08 +0800
-Subject: [PATCH 078/104] mtk: hostapd: ACS: Add EHT320 and HT40- support, fix
- issue
-
-1. Add 6G EHT320 support;
-2. Add 2.4G HT40- support;
-3. Fix issue: selected best channel is out of channels;
-
-Signed-off-by: fancy.liu <fancy.liu@mediatek.com>
----
- src/ap/acs.c | 191 +++++++++++++++++++++++++++++++++------------------
- 1 file changed, 124 insertions(+), 67 deletions(-)
-
-diff --git a/src/ap/acs.c b/src/ap/acs.c
-index cfa4a7d27..1fa8b8e64 100644
---- a/src/ap/acs.c
-+++ b/src/ap/acs.c
-@@ -245,6 +245,7 @@ enum bw_type {
- 	ACS_BW40,
- 	ACS_BW80,
- 	ACS_BW160,
-+	ACS_BW320,
- };
- 
- struct bw_item {
-@@ -286,10 +287,16 @@ static const struct bw_item bw_160[] = {
- 	{ 6435, 6575, 111 }, { 6595, 6735, 143 },
- 	{ 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
- };
-+static const struct bw_item bw_320[] = {
-+	{ 5955, 6255, 31 }, { 6115, 6415, 63 }, { 6275, 6575, 95 },
-+	{ 6435, 6735, 127 }, { 6595, 6895, 159 }, { 6755, 7055, 191 },
-+	{ -1, -1, -1 }
-+};
- static const struct bw_item *bw_desc[] = {
- 	[ACS_BW40] = bw_40,
- 	[ACS_BW80] = bw_80,
- 	[ACS_BW160] = bw_160,
-+	[ACS_BW320] = bw_320,
- };
- 
- 
-@@ -769,10 +776,19 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 			 struct hostapd_channel_data **ideal_chan,
- 			 long double *ideal_factor)
- {
--	struct hostapd_channel_data *chan, *adj_chan = NULL, *best;
-+	struct hostapd_channel_data *chan, *adj_chan = NULL, *tmp_chan = NULL, *best;
- 	long double factor;
- 	int i, j;
- 	unsigned int k;
-+	int ht40_plus = 1, sec_ch_factor = 1;
-+
-+	if (is_24ghz_mode(mode->mode)) {
-+		ht40_plus = (iface->conf->secondary_channel == -1) ? 0 : 1;
-+		sec_ch_factor = (iface->conf->secondary_channel == -1) ? -1 : 1;
-+	}
-+
-+	wpa_printf(MSG_INFO, "%s:%d, bw(%u), n_chans(%d), num_channels(%d), sec_ch(%d)",
-+		__func__, __LINE__, bw, n_chans, mode->num_channels, iface->conf->secondary_channel);
- 
- 	for (i = 0; i < mode->num_channels; i++) {
- 		double total_weight;
-@@ -780,6 +796,9 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 		bool update_best = true;
- 
- 		best = chan = &mode->channels[i];
-+		wpa_printf(MSG_INFO,
-+			   "ACS: Channel[%d] %d: interference_factor %Lg",
-+			   i, chan->chan, chan->interference_factor);
- 
- 		/* Since in the current ACS implementation the first channel is
- 		 * always a primary channel, skip channels not available as
-@@ -811,7 +830,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 		    iface->conf->country[2] == 0x4f)
- 			continue;
- 
--		if (!chan_bw_allowed(chan, bw, 1, 1)) {
-+		if (!chan_bw_allowed(chan, bw, ht40_plus, 1)) {
- 			wpa_printf(MSG_DEBUG,
- 				   "ACS: Channel %d: BW %u is not supported",
- 				   chan->chan, bw);
-@@ -832,7 +851,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 		}
- 
- 		if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
--		    (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
-+		    (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
-+		     iface->conf->ieee80211be)) {
- 			if (hostapd_get_oper_chwidth(iface->conf) ==
- 			    CONF_OPER_CHWIDTH_80MHZ &&
- 			    !acs_usable_bw_chan(chan, ACS_BW80)) {
-@@ -850,63 +870,86 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 					   chan->chan);
- 				continue;
- 			}
--		}
- 
--		factor = 0;
--		if (acs_usable_chan(chan))
--			factor = chan->interference_factor;
--		total_weight = 1;
--
--		for (j = 1; j < n_chans; j++) {
--			adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
--			if (!adj_chan)
--				break;
--
--			if (!chan_bw_allowed(adj_chan, bw, 1, 0)) {
-+			if (iface->conf->ieee80211be &&
-+			    hostapd_get_oper_chwidth(iface->conf) ==
-+			    CONF_OPER_CHWIDTH_320MHZ &&
-+			    !acs_usable_bw_chan(chan, ACS_BW320)) {
- 				wpa_printf(MSG_DEBUG,
--					   "ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
--					   chan->chan, adj_chan->chan, bw);
--				break;
-+					   "ACS: Channel %d: not allowed as primary channel for 320 MHz bandwidth",
-+					   chan->chan);
-+				continue;
- 			}
-+		}
- 
--			if (acs_usable_chan(adj_chan)) {
--				factor += adj_chan->interference_factor;
-+		factor = 0;
-+		total_weight = 0;
-+
-+		if (!is_24ghz_mode(mode->mode)) {
-+			/* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less
-+			 * crowded primary channel if one was found in the segment */
-+			if (acs_usable_chan(chan)) {
-+				factor += chan->interference_factor;
- 				total_weight += 1;
--			} else {
--				update_best = false;
- 			}
- 
--			/* find the best channel in this segment */
--			if (update_best &&
--			    adj_chan->interference_factor <
--			    best->interference_factor)
--				best = adj_chan;
--		}
-+			for (j = 1; j < n_chans; j++) {
-+				adj_chan = acs_find_chan(iface, chan->freq + j * 20);
-+				if (!adj_chan)
-+					break;
- 
--		if (j != n_chans) {
--			wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth",
--				   chan->chan);
--			continue;
--		}
-+				if (!is_in_chanlist(iface, adj_chan) || !is_in_freqlist(iface, adj_chan))
-+					break;
- 
--		/* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less
--		 * crowded primary channel if one was found in the segment */
--		if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
--		    chan != best) {
--			wpa_printf(MSG_DEBUG,
--				   "ACS: promoting channel %d over %d (less interference %Lg/%Lg)",
--				   best->chan, chan->chan,
--				   chan->interference_factor,
--				   best->interference_factor);
--			chan = best;
--		}
-+				if (!chan_bw_allowed(adj_chan, bw, 1, 0)) {
-+					wpa_printf(MSG_DEBUG,
-+						   "ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
-+						   chan->chan, adj_chan->chan, bw);
-+					break;
-+				}
-+
-+				update_best = true;
-+				if (acs_usable_chan(adj_chan)) {
-+					factor += adj_chan->interference_factor;
-+					total_weight += 1;
-+				} else {
-+					update_best = false;
-+				}
- 
--		/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
--		 * channel interference factor. */
--		if (is_24ghz_mode(mode->mode)) {
-+				/* find the best channel in this segment */
-+				if (update_best &&
-+					adj_chan->interference_factor < best->interference_factor)
-+					best = adj_chan;
-+			}
-+
-+			if (j != n_chans) {
-+				wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth",
-+					   chan->chan);
-+				continue;
-+			}
-+
-+			if (chan != best) {
-+				wpa_printf(MSG_INFO,
-+					   "ACS: promoting channel %d over %d (less interference %Lg/%Lg)",
-+					   best->chan, chan->chan,
-+					   chan->interference_factor,
-+					   best->interference_factor);
-+				chan = best;
-+			}
-+		} else {
- 			for (j = 0; j < n_chans; j++) {
-+				/* Will set primary_channel / secondary_channel(40M case) weight to 1 */
-+				tmp_chan = acs_find_chan(iface, chan->freq +
-+							 (j * sec_ch_factor * 20));
-+				if (tmp_chan && acs_usable_chan(tmp_chan)) {
-+					factor += tmp_chan->interference_factor;
-+					total_weight += 1;
-+				}
-+
-+				/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent channel
-+				interference factor, separately for primary/secondary channel. */
- 				adj_chan = acs_find_chan(iface, chan->freq +
--							 (j * 20) - 5);
-+							 (j * sec_ch_factor * 20) - 5);
- 				if (adj_chan && acs_usable_chan(adj_chan)) {
- 					factor += ACS_ADJ_WEIGHT *
- 						adj_chan->interference_factor;
-@@ -914,7 +957,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 				}
- 
- 				adj_chan = acs_find_chan(iface, chan->freq +
--							 (j * 20) - 10);
-+							 (j * sec_ch_factor * 20) - 10);
- 				if (adj_chan && acs_usable_chan(adj_chan)) {
- 					factor += ACS_NEXT_ADJ_WEIGHT *
- 						adj_chan->interference_factor;
-@@ -922,7 +965,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 				}
- 
- 				adj_chan = acs_find_chan(iface, chan->freq +
--							 (j * 20) + 5);
-+							 (j * sec_ch_factor * 20) + 5);
- 				if (adj_chan && acs_usable_chan(adj_chan)) {
- 					factor += ACS_ADJ_WEIGHT *
- 						adj_chan->interference_factor;
-@@ -930,7 +973,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 				}
- 
- 				adj_chan = acs_find_chan(iface, chan->freq +
--							 (j * 20) + 10);
-+							 (j * sec_ch_factor * 20) + 10);
- 				if (adj_chan && acs_usable_chan(adj_chan)) {
- 					factor += ACS_NEXT_ADJ_WEIGHT *
- 						adj_chan->interference_factor;
-@@ -939,7 +982,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 			}
- 		}
- 
--		factor /= total_weight;
-+		if (total_weight)
-+			factor /= total_weight;
- 
- 		bias = NULL;
- 		if (iface->conf->acs_chan_bias) {
-@@ -958,11 +1002,11 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
- 
- 		if (bias) {
- 			factor *= bias->bias;
--			wpa_printf(MSG_DEBUG,
-+			wpa_printf(MSG_INFO,
- 				   "ACS:  * channel %d: total interference = %Lg (%f bias)",
- 				   chan->chan, factor, bias->bias);
- 		} else {
--			wpa_printf(MSG_DEBUG,
-+			wpa_printf(MSG_INFO,
- 				   "ACS:  * channel %d: total interference = %Lg",
- 				   chan->chan, factor);
- 		}
-@@ -1015,19 +1059,12 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
- 		goto bw_selected;
- 	}
- 
--	/* TODO: HT40- support */
--
--	if (iface->conf->ieee80211n &&
--	    iface->conf->secondary_channel == -1) {
--		wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
--		return NULL;
--	}
--
- 	if (iface->conf->ieee80211n &&
- 	    iface->conf->secondary_channel)
- 		n_chans = 2;
- 
--	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
-+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
-+	    iface->conf->ieee80211be) {
- 		switch (hostapd_get_oper_chwidth(iface->conf)) {
- 		case CONF_OPER_CHWIDTH_80MHZ:
- 			n_chans = 4;
-@@ -1037,6 +1074,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
- 			break;
- 		default:
- 			break;
-+		/* 320 is supported only in 6GHz 11be mode */
- 		}
- 	}
- 
-@@ -1057,7 +1095,7 @@ bw_selected:
- 	}
- 
- 	if (ideal_chan) {
--		wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg",
-+		wpa_printf(MSG_INFO, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg",
- 			   ideal_chan->chan, ideal_chan->freq, ideal_factor);
- 
- #ifdef CONFIG_IEEE80211BE
-@@ -1072,6 +1110,21 @@ bw_selected:
- 	return rand_chan;
- }
- 
-+static int acs_get_center_freq_320mhz(int channel)
-+{
-+	if (channel >= 1 && channel <= 45)
-+		return 31;
-+	else if (channel >= 49 && channel <= 77)
-+		return 63;
-+	else if (channel >= 81 && channel <= 109)
-+		return 95;
-+	else if (channel >= 113 && channel <= 141)
-+		return 127;
-+	else if (channel >= 145 && channel <= 173)
-+		return 159;
-+	else
-+		return 191;
-+}
- 
- static void acs_adjust_secondary(struct hostapd_iface *iface)
- {
-@@ -1099,7 +1152,7 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
- {
- 	int center;
- 
--	wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
-+	wpa_printf(MSG_DEBUG, "ACS: Adjusting center frequency");
- 
- 	switch (hostapd_get_oper_chwidth(iface->conf)) {
- 	case CONF_OPER_CHWIDTH_USE_HT:
-@@ -1115,6 +1168,9 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
- 	case CONF_OPER_CHWIDTH_80MHZ:
- 		center = acs_get_bw_center_chan(iface->freq, ACS_BW80);
- 		break;
-+	case CONF_OPER_CHWIDTH_320MHZ:
-+		center = acs_get_center_freq_320mhz(iface->conf->channel);
-+		break;
- 	case CONF_OPER_CHWIDTH_160MHZ:
- 		center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
- 		break;
-@@ -1122,7 +1178,7 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
- 		/* TODO: How can this be calculated? Adjust
- 		 * acs_find_ideal_chan() */
- 		wpa_printf(MSG_INFO,
--			   "ACS: Only VHT20/40/80/160 is supported now");
-+			   "ACS: Only VHT20/40/80/160 EHT320 is supported now");
- 		return;
- 	}
- 
-@@ -1185,7 +1241,8 @@ static void acs_study(struct hostapd_iface *iface)
- 	iface->conf->punct_bitmap = ideal_chan->punct_bitmap;
- #endif /* CONFIG_IEEE80211BE */
- 
--	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
-+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
-+		iface->conf->ieee80211be) {
- 		acs_adjust_secondary(iface);
- 		acs_adjust_center_freq(iface);
- 	}
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0078-mtk-hostapd-Refactor-static-PP-and-mld-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0078-mtk-hostapd-Refactor-static-PP-and-mld-support.patch
new file mode 100644
index 0000000..c20f618
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0078-mtk-hostapd-Refactor-static-PP-and-mld-support.patch
@@ -0,0 +1,336 @@
+From a6baff35c3303204be7df2a0ec001e31e428a6c3 Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Tue, 2 Apr 2024 16:51:07 +0800
+Subject: [PATCH 078/126] mtk: hostapd: Refactor static PP and mld support
+
+Add band_idx attribute in pp cmd for vendor cmd under mld setting.
+
+---
+ hostapd/config_file.c        |  6 ++--
+ hostapd/ctrl_iface.c         | 69 +++++++++++++++++++++++++-----------
+ hostapd/ctrl_iface.h         |  3 +-
+ hostapd/hostapd_cli.c        | 15 ++++++++
+ src/ap/ap_config.h           |  4 +--
+ src/ap/ap_drv_ops.c          |  7 ++--
+ src/ap/dfs.c                 |  3 ++
+ src/ap/hostapd.c             |  4 +--
+ src/common/mtk_vendor.h      |  1 +
+ src/drivers/driver.h         |  3 +-
+ src/drivers/driver_nl80211.c |  4 ++-
+ 11 files changed, 87 insertions(+), 32 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index c1bc344dd..44615c564 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5452,7 +5452,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		if (get_u16(pos, line, &conf->punct_bitmap))
+ 			return 1;
+ 		conf->punct_bitmap = atoi(pos);
+-		conf->pp_mode = PP_MANUAL_MODE;
++		conf->pp_mode = PP_USR_MODE;
+ 	} else if (os_strcmp(buf, "punct_acs_threshold") == 0) {
+ 		int val = atoi(pos);
+ 
+@@ -5549,8 +5549,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 	} else if (os_strcmp(buf, "pp_mode") == 0) {
+ 		int val = atoi(pos);
+ 
+-		if ((val != PP_MANUAL_MODE && conf->punct_bitmap) ||
+-		    val < PP_DISABLE || val > PP_MANUAL_MODE) {
++		if ((val != PP_USR_MODE && conf->punct_bitmap) ||
++		    val < PP_DISABLE || val > PP_USR_MODE) {
+ 			wpa_printf(MSG_ERROR, "Line %d: invalid pp_mode value",
+ 				   line);
+ 			return 1;
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 9bd8a46b3..e19502d1d 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4992,27 +4992,57 @@ hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cm
+ 	return os_snprintf(buf, buflen, "OK\n");
+ }
+ 
++struct hostapd_data *
++hostapd_get_hapd_by_band_idx(struct hostapd_data *hapd, u8 band_idx)
++{
++	struct hostapd_data *link;
++
++	if (!hostapd_is_mld_ap(hapd))
++		return hapd;
++
++	for_each_mld_link(link, hapd) {
++		if (link->iconf->band_idx == band_idx)
++			break;
++	}
++
++	if (!link || link->iconf->band_idx != band_idx) {
++		wpa_printf(MSG_ERROR, "Invalid band idx %d\n", band_idx);
++		return NULL;
++	}
++
++	return link;
++}
++
+ static int
+ hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ 			  size_t buflen)
+ {
+-	char *pos, *config, *value;
++	char *band, *config, *value;
++	u8 band_idx;
+ 
+ 	config = cmd;
+-	pos = os_strchr(config, ' ');
+-	if (pos == NULL)
++
++	value = os_strchr(config, ' ');
++	if (value == NULL)
+ 		return -1;
+-	*pos++ = '\0';
++	*value++ = '\0';
+ 
+-	if (pos == NULL)
++	band = os_strchr(value, ' ');
++	if (band == NULL)
++		return -1;
++	*band++ = '\0';
++	band_idx = strtol(band, NULL, 10);
++
++	hapd = hostapd_get_hapd_by_band_idx(hapd, band_idx);
++
++	if (!hapd)
+ 		return -1;
+-	value = pos;
+ 
+ 	if (os_strcmp(config, "mode") == 0) {
+-		int val = atoi(value);
++		int val = strtol(value, NULL, 10);
+ 
+-		if (val < PP_DISABLE || val > PP_AUTO_MODE) {
+-			wpa_printf(MSG_ERROR, "Invalid value for set_pp");
++		if (val < PP_DISABLE || val > PP_FW_MODE) {
++			wpa_printf(MSG_ERROR, "Invalid value for SET_PP");
+ 			return -1;
+ 		}
+ 		hapd->iconf->pp_mode = (u8) val;
+@@ -5020,7 +5050,8 @@ hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ 			return -1;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+-			   "Unsupported parameter %s for set_pp", config);
++			   "Unsupported parameter %s for SET_PP"
++			   "Usage: set_pp mode <value> <band_idx>", config);
+ 		return -1;
+ 	}
+ 	return os_snprintf(buf, buflen, "OK\n");
+@@ -5030,19 +5061,17 @@ static int
+ hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ 			  size_t buflen)
+ {
+-	char *pos, *end;
++	u8 band_idx;
+ 
+-	pos = buf;
+-	end = buf + buflen;
++	band_idx = strtol(cmd, NULL, 10);
+ 
+-	if (os_strcmp(cmd, "mode") == 0) {
+-		return os_snprintf(pos, end - pos, "pp_mode: %d\n",
+-				   hapd->iconf->pp_mode);
+-	} else {
+-		wpa_printf(MSG_ERROR,
+-			   "Unsupported parameter %s for get_pp", cmd);
++	hapd = hostapd_get_hapd_by_band_idx(hapd, band_idx);
++
++	if (!hapd)
+ 		return -1;
+-	}
++
++	return os_snprintf(buf, buflen, "pp_mode: %d, punct_bitmap: 0x%04x\n",
++			   hapd->iconf->pp_mode, hapd->iconf->punct_bitmap);
+ }
+ 
+ static int
+diff --git a/hostapd/ctrl_iface.h b/hostapd/ctrl_iface.h
+index ec5a95be7..753761c22 100644
+--- a/hostapd/ctrl_iface.h
++++ b/hostapd/ctrl_iface.h
+@@ -39,5 +39,6 @@ hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface)
+ {
+ }
+ #endif /* CONFIG_NO_CTRL_IFACE */
+-
++struct hostapd_data *
++hostapd_get_hapd_by_band_idx(struct hostapd_data *hapd, u8 band_idx);
+ #endif /* CTRL_IFACE_H */
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index f54fa9997..4e94db21d 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1755,6 +1755,17 @@ static int hostapd_cli_cmd_dump_csi(struct wpa_ctrl *ctrl, int argc,
+ {
+ 	return hostapd_cli_cmd(ctrl, "DUMP_CSI", 1, argc, argv);
+ }
++static int hostapd_cli_cmd_set_pp(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "set_pp", 3, argc, argv);
++}
++
++static int hostapd_cli_cmd_get_pp(struct wpa_ctrl *ctrl, int argc,
++					   char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "get_pp", 1, argc, argv);
++}
+ 
+ struct hostapd_cli_cmd {
+ 	const char *cmd;
+@@ -2008,6 +2019,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 		" = Set csi configuaration"},
+ 	{ "dump_csi", hostapd_cli_cmd_dump_csi, NULL,
+ 		" = Dump csi data to a json file"},
++	{ "set_pp", hostapd_cli_cmd_set_pp, NULL,
++		" = Set preamble puncture mode"},
++	{ "get_pp", hostapd_cli_cmd_get_pp, NULL,
++		" = Get preamble puncture status"},
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index c61e67403..417b1d630 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1406,8 +1406,8 @@ enum mtk_vendor_attr_edcca_ctrl_mode {
+ 
+ enum pp_mode {
+ 	PP_DISABLE = 0,
+-	PP_AUTO_MODE,
+-	PP_MANUAL_MODE,
++	PP_FW_MODE,
++	PP_USR_MODE,
+ };
+ 
+ #define EDCCA_DEFAULT_COMPENSATION -6
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index a26a3f45d..fb09a3290 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1432,10 +1432,13 @@ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
+ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
+ {
+ 	if (!hapd->driver || !hapd->driver->pp_mode_set ||
+-	    hapd->iconf->pp_mode > PP_AUTO_MODE)
++	    hapd->iconf->pp_mode >= PP_USR_MODE ||
++	    hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+ 		return 0;
++
+ 	return hapd->driver->pp_mode_set(hapd->drv_priv,
+-					 hapd->iconf->pp_mode);
++					 hapd->iconf->pp_mode,
++					 hapd->iconf->band_idx);
+ }
+ 
+ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index db26f6536..fc7699973 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1081,6 +1081,7 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
+ 	os_memset(&csa_settings, 0, sizeof(csa_settings));
+ 	csa_settings.cs_count = 5;
+ 	csa_settings.block_tx = 1;
++	csa_settings.punct_bitmap = hostapd_get_punct_bitmap(iface->bss[0]);
+ 	csa_settings.link_id = -1;
+ #ifdef CONFIG_IEEE80211BE
+ 	if (iface->bss[0]->conf->mld_ap)
+@@ -1657,6 +1658,8 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+ 			return 0;
+ 	}
+ 
++	iface->bss[0]->iconf->punct_bitmap = 0;
++
+ 	if (hostapd_dfs_background_start_channel_switch(iface, freq)) {
+ 		/* Radar detected while operating, switch the channel. */
+ 		return hostapd_dfs_start_channel_switch(iface);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 93c164177..f5e11d43d 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2766,6 +2766,8 @@ dfs_offload:
+ 	}
+ #endif /* CONFIG_MESH */
+ 
++	if (hostapd_drv_pp_mode_set(hapd) < 0)
++		goto fail;
+ 	if (hostapd_drv_configure_edcca_enable(hapd) < 0)
+ 		goto fail;
+ 
+@@ -2780,8 +2782,6 @@ dfs_offload:
+ 		goto fail;
+ 	if (hostapd_drv_amsdu_ctrl(hapd) < 0)
+ 		goto fail;
+-	if (hostapd_drv_pp_mode_set(hapd) < 0)
+-		goto fail;
+ 
+ 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ 		   iface->bss[0]->conf->iface);
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index ce3a2ad9a..be516e017 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -268,6 +268,7 @@ enum mtk_vendor_attr_pp_ctrl {
+ 	MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
+ 
+ 	MTK_VENDOR_ATTR_PP_MODE,
++	MTK_VENDOR_ATTR_PP_BAND_IDX,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_PP_CTRL,
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 48cb93cfd..b75580374 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5364,8 +5364,9 @@ struct wpa_driver_ops {
+ 	 * pp_mode_set - Set preamble puncture operation mode
+ 	 * @priv: Private driver interface data
+ 	 * @pp_mode: Value is defined in enum pp_mode
++	 * @band_idx: chip band index
+ 	 */
+-	int (*pp_mode_set)(void *priv, const u8 pp_mode);
++	int (*pp_mode_set)(void *priv, const u8 pp_mode, u8 band_idx);
+ #ifdef CONFIG_IEEE80211BE
+ 	int (*get_mld_addr)(void *priv, u8 *addr);
+ #endif
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index afb068091..8b64c3983 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -159,6 +159,7 @@ amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
+ static struct nla_policy
+ pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
+ 	[MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_PP_BAND_IDX] = { .type = NLA_U8 },
+ };
+ 
+ static struct nla_policy csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
+@@ -15196,7 +15197,7 @@ static int nl80211_background_radar_mode(void *priv, const u8 background_radar_m
+ 	return ret;
+ }
+ 
+-static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
++static int nl80211_pp_mode_set(void *priv, const u8 pp_mode, u8 band_idx)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+@@ -15223,6 +15224,7 @@ static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
+ 	if (!data)
+ 		goto fail;
+ 
++	nla_put_u8(msg, MTK_VENDOR_ATTR_PP_BAND_IDX, band_idx);
+ 	nla_put_u8(msg, MTK_VENDOR_ATTR_PP_MODE, pp_mode);
+ 
+ 	nla_nest_end(msg, data);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0079-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0079-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch
deleted file mode 100644
index a28db10..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0079-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch
+++ /dev/null
@@ -1,122 +0,0 @@
-From 26c23f7dc1fe47e22ceab581b7abed089148c68f Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Thu, 19 Oct 2023 13:38:11 +0800
-Subject: [PATCH 079/104] mtk: hostapd: initialize i802_bss's flink->freq with
- iface freq.
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
----
- src/ap/ap_drv_ops.c          | 6 +++---
- src/ap/ap_drv_ops.h          | 2 +-
- src/ap/hostapd.c             | 2 +-
- src/drivers/driver.h         | 2 +-
- src/drivers/driver_nl80211.c | 4 ++--
- wpa_supplicant/driver_i.h    | 2 +-
- 6 files changed, 9 insertions(+), 9 deletions(-)
-
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index d6bd157d8..b7896c110 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -368,7 +368,7 @@ int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
- 	char force_ifname[IFNAMSIZ];
- 	u8 if_addr[ETH_ALEN];
- 	return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
--			      NULL, NULL, force_ifname, if_addr, NULL, 0);
-+			      NULL, NULL, force_ifname, if_addr, NULL, 0, hapd->iface->freq);
- }
- 
- 
-@@ -560,13 +560,13 @@ int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
- int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
- 		   const char *ifname, const u8 *addr, void *bss_ctx,
- 		   void **drv_priv, char *force_ifname, u8 *if_addr,
--		   const char *bridge, int use_existing)
-+		   const char *bridge, int use_existing, int freq)
- {
- 	if (hapd->driver == NULL || hapd->driver->if_add == NULL)
- 		return -1;
- 	return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
- 				    bss_ctx, drv_priv, force_ifname, if_addr,
--				    bridge, use_existing, 1);
-+				    bridge, use_existing, 1, freq);
- }
- 
- 
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 78e5c8d5a..5830705a3 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -58,7 +58,7 @@ int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
- int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
- 		   const char *ifname, const u8 *addr, void *bss_ctx,
- 		   void **drv_priv, char *force_ifname, u8 *if_addr,
--		   const char *bridge, int use_existing);
-+		   const char *bridge, int use_existing, int freq);
- int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
- 		      const char *ifname);
- int hostapd_if_link_remove(struct hostapd_data *hapd,
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 636655ea1..e4fc1f85a 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -1433,7 +1433,7 @@ int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
- 				   conf->iface, addr, hapd,
- 				   &hapd->drv_priv, force_ifname, if_addr,
- 				   conf->bridge[0] ? conf->bridge : NULL,
--				   first == -1)) {
-+				   first == -1, hapd->iface->freq)) {
- 			wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
- 				   MACSTR ")", MAC2STR(hapd->own_addr));
- 			hapd->interface_added = 0;
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 332a51c55..2940650df 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -3866,7 +3866,7 @@ struct wpa_driver_ops {
- 	int (*if_add)(void *priv, enum wpa_driver_if_type type,
- 		      const char *ifname, const u8 *addr, void *bss_ctx,
- 		      void **drv_priv, char *force_ifname, u8 *if_addr,
--		      const char *bridge, int use_existing, int setup_ap);
-+		      const char *bridge, int use_existing, int setup_ap, int freq);
- 
- 	/**
- 	 * if_remove - Remove a virtual interface
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index e588e7538..3d69c9c49 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -8872,7 +8872,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
- 				     void *bss_ctx, void **drv_priv,
- 				     char *force_ifname, u8 *if_addr,
- 				     const char *bridge, int use_existing,
--				     int setup_ap)
-+				     int setup_ap, int freq)
- {
- 	enum nl80211_iftype nlmode;
- 	struct i802_bss *bss = priv;
-@@ -8992,7 +8992,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
- 		new_bss->valid_links = 0;
- 		os_memcpy(new_bss->flink->addr, new_bss->addr, ETH_ALEN);
- 
--		new_bss->flink->freq = drv->first_bss->flink->freq;
-+		new_bss->flink->freq = (freq == -1) ? drv->first_bss->flink->freq : freq;
- 		new_bss->ctx = bss_ctx;
- 		new_bss->added_if = added;
- 		drv->first_bss->next = new_bss;
-diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
-index 663e16053..624192ebd 100644
---- a/wpa_supplicant/driver_i.h
-+++ b/wpa_supplicant/driver_i.h
-@@ -446,7 +446,7 @@ static inline int wpa_drv_if_add(struct wpa_supplicant *wpa_s,
- 	if (wpa_s->driver->if_add)
- 		return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname,
- 					     addr, bss_ctx, NULL, force_ifname,
--					     if_addr, bridge, 0, 0);
-+					     if_addr, bridge, 0, 0, -1);
- 	return -1;
- }
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0079-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0079-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch
new file mode 100644
index 0000000..afc66a5
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0079-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch
@@ -0,0 +1,183 @@
+From 288062b9de2d669b809ae7b0035ca4680c358192 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 11 Apr 2024 18:16:38 +0800
+Subject: [PATCH 079/126] mtk: hostapd: make sure all links are set before
+ enabling beacon
+
+NL80211_CMD_NEW_BEACON will first be set, but we've modified mac80211 to
+disable this beacon. After that, hostapd will block
+NL80211_CMD_SET_BEACON until all links are setting up.
+(use NL80211_CMD_START_AP event to check if all expected links are enabled)
+
+Update: in wpa_driver_nl80211_set_ap(), send_and_recv() is used, implies
+that hostapd should already sync with driver, so don't need to use
+NL80211_CMD_START_AP event.
+
+This can make sure that the first beacon of each link includes the
+correct RNR and per-STA profile.
+
+Note that in NL80211_CMD_NEW_BEACON, we also set beacon interval to 0,
+which helps to bypass some mac80211 beacon active checks, e.g., during ACS.
+
+Add is_mld_finished check for ucode need.
+This function returns ture only if all links fromt all MLD APs are
+ready.
+
+Only after hostapd sets beacon for all links that hapd->mld->started is
+set to true. However, if the interface is about to do CAC,
+hapd->mld->started will be false until the CAC is done.
+
+For ucode, it only have to ckeck whether all link is added. Instead of
+checking hapd->mld->started, this commits check the link one by one, and
+return false if there are links unadded.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ hostapd/config_file.c |  2 ++
+ src/ap/ap_config.h    |  2 ++
+ src/ap/beacon.c       | 10 ++++++++++
+ src/ap/hostapd.c      | 14 ++++++++++++++
+ src/ap/hostapd.h      |  1 +
+ src/ap/ucode.c        | 27 +++++++++++++++++++++++++++
+ 6 files changed, 56 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 44615c564..206055b75 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5467,6 +5467,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		bss->mld_ap = !!atoi(pos);
+ 	} else if (os_strcmp(buf, "mld_primary") == 0) {
+ 		bss->mld_primary = !!atoi(pos);
++	} else if (os_strcmp(buf, "mld_allowed_links") == 0) {
++		bss->mld_allowed_links = atoi(pos);
+ 	} else if (os_strcmp(buf, "mld_addr") == 0) {
+ 		if (hwaddr_aton(pos, bss->mld_addr)) {
+ 			wpa_printf(MSG_ERROR, "Line %d: Invalid mld_addr",
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 417b1d630..15b66ca30 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -989,6 +989,8 @@ struct hostapd_bss_config {
+ 
+ 	/* The AP is the primary AP of an AP MLD */
+ 	u8 mld_primary;
++	/* Allowed link bitmap of the AP MLD to which the AP is affiliated */
++	u16 mld_allowed_links;
+ 
+ 	/* The MLD ID to which the AP MLD is affiliated with */
+ 	u8 mld_id;
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 9bf73747f..88e35acc2 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -2276,6 +2276,12 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ 	os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
+ 	head->u.beacon.beacon_int =
+ 		host_to_le16(hapd->iconf->beacon_int);
++	/* if MLD AP hasn't finished setting up all links, also set beacon interval
++	 * to 0. This allows mac80211 to bypass some beacon active checks, for
++	 * example, when doing ACS
++	 */
++	if (hapd->conf->mld_ap && !hapd->mld->started)
++		head->u.beacon.beacon_int = host_to_le16(0);
+ 
+ 	/* hardware or low-level driver will setup seq_ctrl and timestamp */
+ 	capab_info = hostapd_own_capab_info(hapd);
+@@ -2677,6 +2683,10 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
+ 	int res, ret = -1, i;
+ 	struct hostapd_hw_modes *mode;
+ 
++	/* skip setting beacon if other links are not started yet */
++	if (hapd->conf->mld_ap && !hapd->mld->started && hapd->beacon_set_done)
++		return 0;
++
+ 	if (!hapd->drv_priv) {
+ 		wpa_printf(MSG_ERROR, "Interface is disabled");
+ 		return -1;
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index f5e11d43d..fe41f1821 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -1315,6 +1315,20 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
+ 	if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
+ 		return -1;
+ 
++	if (hapd->conf->mld_ap && !hapd->mld->started) {
++		struct hostapd_data *p_hapd;
++		u16 valid_links = 0;
++
++		for_each_mld_link(p_hapd, hapd)
++			valid_links |= BIT(p_hapd->mld_link_id);
++
++		if (valid_links == hapd->conf->mld_allowed_links ||
++		    !hapd->conf->mld_allowed_links) {
++			hapd->mld->started = 1;
++			ieee802_11_set_beacon(hapd);
++		}
++	}
++
+ 	if (flush_old_stations && !conf->start_disabled &&
+ 	    conf->broadcast_deauth) {
+ 		u8 addr[ETH_ALEN];
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 574a5ba1d..4db67096b 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -544,6 +544,7 @@ struct hostapd_mld {
+ 	 * freed when num_links is 0.
+ 	 */
+ 	u8 refcount;
++	bool started;
+ 
+ 	struct hostapd_data *fbss;
+ 	struct dl_list links; /* List head of all affiliated links */
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index 68f76dbe5..da1c4c1ac 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -744,6 +744,32 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 	return ucv_boolean_new(!ret);
+ }
+ 
++static uc_value_t *
++uc_hostapd_iface_is_mld_finished(uc_vm_t *vm, size_t nargs)
++{
++	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
++	bool finished = true;
++	int i;
++
++	for (i = 0; i < iface->num_bss; i++) {
++		if (iface->bss[i]->conf->mld_ap) {
++			struct hostapd_data *p_hapd;
++			u16 valid_links = 0;
++
++			for_each_mld_link(p_hapd, iface->bss[i])
++				valid_links |= BIT(p_hapd->mld_link_id);
++
++			if (iface->bss[i]->conf->mld_allowed_links > 0 &&
++			    valid_links != iface->bss[i]->conf->mld_allowed_links) {
++				finished = false;
++				break;
++			}
++		}
++	}
++
++	return ucv_boolean_new(finished);
++}
++
+ static uc_value_t *
+ uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
+ {
+@@ -816,6 +842,7 @@ int hostapd_ucode_init(struct hapd_interfaces *ifaces)
+ 		{ "stop", uc_hostapd_iface_stop },
+ 		{ "start", uc_hostapd_iface_start },
+ 		{ "switch_channel", uc_hostapd_iface_switch_channel },
++		{ "is_mld_finished", uc_hostapd_iface_is_mld_finished },
+ 	};
+ 	uc_value_t *data, *proto;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0080-mtk-hostapd-add-hidden-SSID-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0080-mtk-hostapd-add-hidden-SSID-support.patch
new file mode 100644
index 0000000..6399fc1
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0080-mtk-hostapd-add-hidden-SSID-support.patch
@@ -0,0 +1,67 @@
+From dd7f4e849efee36586e0b39f9f1e54a1a2000a49 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Wed, 24 Apr 2024 17:44:40 +0800
+Subject: [PATCH 080/126] mtk: hostapd: add hidden SSID support
+
+Add hidden SSID support for MLD AP. Now the parnter link's information is
+included in RNR even if the link is hidden.
+Note that the hidden links' information appear in both beacon and probe
+response.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ieee802_11.c | 17 ++++++++++++++---
+ 1 file changed, 14 insertions(+), 3 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index fbe3f582f..37a2c5ab8 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7483,16 +7483,21 @@ repeat_rnr_len:
+ 		for (i = start; i < hapd->iface->num_bss; i++) {
+ 			struct hostapd_data *bss = hapd->iface->bss[i];
+ 			bool ap_mld = false;
++			bool ignore_broadcast_ssid;
+ 
+ 			if (!bss || !bss->conf || !bss->started)
+ 				continue;
+ 
++			ignore_broadcast_ssid = bss->conf->ignore_broadcast_ssid;
+ #ifdef CONFIG_IEEE80211BE
+ 			ap_mld = bss->conf->mld_ap;
++			/* FIXME How to exclude the hidden link in beacon? */
++			ignore_broadcast_ssid &=
++				!hostapd_is_ml_partner(bss, reporting_hapd);
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ 			if (bss == reporting_hapd ||
+-			    bss->conf->ignore_broadcast_ssid)
++			    ignore_broadcast_ssid)
+ 				continue;
+ 
+ 			if (hostapd_skip_rnr(i, skip_profiles, ap_mld,
+@@ -7751,13 +7756,19 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
+ 	u8 bss_param = 0;
+ 	bool ap_mld = false;
+ 	u8 *eid = *pos;
++	bool ignore_broadcast_ssid;
+ 
++	if (!bss || !bss->conf || !bss->started)
++		return false;
++
++	ignore_broadcast_ssid = bss->conf->ignore_broadcast_ssid;
+ #ifdef CONFIG_IEEE80211BE
+ 	ap_mld = !!hapd->conf->mld_ap;
++	/* FIXME How to exclude the hidden link in beacon? */
++	ignore_broadcast_ssid &= !hostapd_is_ml_partner(bss, reporting_hapd);
+ #endif /* CONFIG_IEEE80211BE */
+ 
+-	if (!bss || !bss->conf || !bss->started ||
+-	    bss == reporting_hapd || bss->conf->ignore_broadcast_ssid)
++	if (bss == reporting_hapd || ignore_broadcast_ssid)
+ 		return false;
+ 
+ 	if (hostapd_skip_rnr(i, skip_profiles, ap_mld, tbtt_info_len,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0080-mtk-hostapd-fix-mld_assoc_link_id.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0080-mtk-hostapd-fix-mld_assoc_link_id.patch
deleted file mode 100644
index 42c06e4..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0080-mtk-hostapd-fix-mld_assoc_link_id.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From d06dd0d45977ce098df718a29ffbc765896a2758 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Mon, 29 Jan 2024 11:24:28 +0800
-Subject: [PATCH 080/104] mtk: hostapd: fix mld_assoc_link_id
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- src/ap/hostapd.c | 10 ++++++----
- 1 file changed, 6 insertions(+), 4 deletions(-)
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index e4fc1f85a..f8b05de45 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -4034,11 +4034,13 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
- 	}
- 
- #ifdef CONFIG_IEEE80211BE
--	if (ap_sta_is_mld(hapd, sta) &&
--	    sta->mld_assoc_link_id != hapd->mld_link_id)
--		return;
-+	if (ap_sta_is_mld(hapd, sta)) {
-+		if (sta->mld_assoc_link_id != hapd->mld_link_id)
-+			return;
-+		mld_assoc_link_id = sta->mld_assoc_link_id;
-+	}
- #endif /* CONFIG_IEEE80211BE */
--        if (mld_assoc_link_id != -2)
-+	if (mld_assoc_link_id != -2)
- 		hostapd_prune_associations(hapd, sta->addr, mld_assoc_link_id);
- 
- 	ap_sta_clear_disconnect_timeouts(hapd, sta);
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0081-mtk-hostapd-do-not-roam-within-the-same-MLD.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0081-mtk-hostapd-do-not-roam-within-the-same-MLD.patch
new file mode 100644
index 0000000..9519c2f
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0081-mtk-hostapd-do-not-roam-within-the-same-MLD.patch
@@ -0,0 +1,31 @@
+From 1d64b54fc9ea3e53a6369e609a9b4e83e800a1e4 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 26 Apr 2024 08:36:57 +0800
+Subject: [PATCH 081/126] mtk: hostapd: do not roam within the same MLD
+
+If STA scaned and selected a different links from the same MLD AP, the
+check by wpa_s would make it to roam within ESS, which is unnecessary
+for a non-AP MLD.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/events.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
+index 68f4d2dbe..83b1dcc21 100644
+--- a/wpa_supplicant/events.c
++++ b/wpa_supplicant/events.c
+@@ -2395,6 +2395,9 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
+ 	if (current_bss == selected)
+ 		return 0;
+ 
++	if (wpa_s->valid_links && ether_addr_equal(selected->mld_addr, wpa_s->bssid))
++		return 0; /* same AP MLD but different links */
++
+ 	if (selected->last_update_idx > current_bss->last_update_idx)
+ 		return 1; /* current BSS not seen in the last scan */
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0081-mtk-wpa_s-correctly-get-assoc-frequency.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0081-mtk-wpa_s-correctly-get-assoc-frequency.patch
deleted file mode 100644
index 7bdb4fe..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0081-mtk-wpa_s-correctly-get-assoc-frequency.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From 3dbd0105364c15225a18098eeaae58119490918d Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 19 Oct 2023 10:48:11 +0800
-Subject: [PATCH 081/104] mtk: wpa_s: correctly get assoc frequency
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- src/drivers/driver_nl80211_event.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 6631285bf..90084356d 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -328,6 +328,7 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
- 			   wpa_ssid_txt(drv->ssid, drv->ssid_len));
- 	}
- 
-+	drv->assoc_freq = nl80211_get_assoc_freq(drv);
- 	event.assoc_info.freq = drv->assoc_freq;
- 	drv->first_bss->flink->freq = drv->assoc_freq;
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0082-mtk-hostapd-update-op_class-in-channel-switch-fallba.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0082-mtk-hostapd-update-op_class-in-channel-switch-fallba.patch
new file mode 100644
index 0000000..b7812cd
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0082-mtk-hostapd-update-op_class-in-channel-switch-fallba.patch
@@ -0,0 +1,34 @@
+From 80eac0c00795cccd43db3c972a6c903dda55f4b0 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 29 Apr 2024 10:52:31 +0800
+Subject: [PATCH 082/126] mtk: hostapd: update op_class in channel switch
+ fallback
+
+Switching to a DFS channel includes an AP interface teardown and setup.
+An op_class update is necessary for passing the channel information
+check during setup.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/hostapd.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index fe41f1821..2c9e54b23 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4710,6 +4710,10 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+ 	iface->conf->ieee80211ac = freq_params->vht_enabled;
+ 	iface->conf->ieee80211ax = freq_params->he_enabled;
+ 	iface->conf->ieee80211be = freq_params->eht_enabled;
++	if (ieee80211_freq_to_channel_ext(iface->freq, iface->conf->secondary_channel,
++					  hostapd_get_oper_chwidth(iface->conf),
++					  &op_class, &chan) != NUM_HOSTAPD_MODES)
++		iface->conf->op_class = op_class;
+ 
+ 	/*
+ 	 * cs_params must not be cleared earlier because the freq_params
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0082-mtk-wpa_s-force-MLD-STA-to-use-SAE-H2E-during-authen.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0082-mtk-wpa_s-force-MLD-STA-to-use-SAE-H2E-during-authen.patch
deleted file mode 100644
index 8ddecc9..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0082-mtk-wpa_s-force-MLD-STA-to-use-SAE-H2E-during-authen.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From 50e36560d14dbb6bf38b46dcfc58f9414e56b283 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 19 Oct 2023 10:51:55 +0800
-Subject: [PATCH 082/104] mtk: wpa_s: force MLD STA to use SAE H2E during
- authentication
-
-Otherwise the MLD STA setup will fail with hostapd MLD AP.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- wpa_supplicant/sme.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
-index f08184f98..e1183722f 100644
---- a/wpa_supplicant/sme.c
-+++ b/wpa_supplicant/sme.c
-@@ -199,7 +199,7 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
- 	if (wpa_key_mgmt_sae_ext_key(key_mgmt) &&
- 	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
- 		use_pt = 1;
--	if (bss && is_6ghz_freq(bss->freq) &&
-+	if (bss && (is_6ghz_freq(bss->freq) || !is_zero_ether_addr(bss->mld_addr)) &&
- 	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
- 		use_pt = 1;
- #ifdef CONFIG_SAE_PK
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0083-mtk-hostapd-always-do-ML-probe-request-before-authen.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0083-mtk-hostapd-always-do-ML-probe-request-before-authen.patch
new file mode 100644
index 0000000..0327d5e
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0083-mtk-hostapd-always-do-ML-probe-request-before-authen.patch
@@ -0,0 +1,41 @@
+From 13f84bce5e02c21e0915b5f4d46a02133237e847 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 6 May 2024 18:20:52 +0800
+Subject: [PATCH 083/126] mtk: hostapd: always do ML probe request before
+ authentication
+
+The scan result might contain old information of an AP MLD, so a ML
+probe might be necessary to update the scan result before the
+authentication.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/events.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
+index 83b1dcc21..7b91ce988 100644
+--- a/wpa_supplicant/events.c
++++ b/wpa_supplicant/events.c
+@@ -1925,15 +1925,16 @@ static int wpa_supplicant_connect_ml_missing(struct wpa_supplicant *wpa_s,
+ 
+ 	if (wpa_bss_parse_basic_ml_element(wpa_s, selected, NULL,
+ 					   &missing_links, ssid,
+-					   &ap_mld_id) ||
+-	    !missing_links)
++					   &ap_mld_id))
+ 		return 0;
+ 
+ 	removed_links = wpa_bss_parse_reconf_ml_element(wpa_s, selected);
+ 	missing_links &= ~removed_links;
+ 
++	/* FIXME Always do ML probe for the sake of stability.
+ 	if (!missing_links)
+ 		return 0;
++	*/
+ 
+ 	wpa_dbg(wpa_s, MSG_DEBUG,
+ 		"MLD: Doing an ML probe for missing links 0x%04x",
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0083-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0083-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch
deleted file mode 100644
index b3144cb..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0083-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch
+++ /dev/null
@@ -1,71 +0,0 @@
-From fb3820ff9fff1b15c13d4a799fbef8932fda7a1b Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Mon, 11 Dec 2023 17:02:05 +0800
-Subject: [PATCH 083/104] mtk: hostapd: extend ap_get_sta() to find the correct
- sta
-
-There're still some mld address tranlation issues that need to be dealt
-with on driver side (e.g. RX eapol frames). So add the code that find
-station also with link address and across hapds at the moment.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- src/ap/ieee802_11.c |  1 +
- src/ap/sta_info.c   | 16 ++++++++++++++++
- src/ap/sta_info.h   |  1 +
- 3 files changed, 18 insertions(+)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index ce3874901..0f357d786 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -3116,6 +3116,7 @@ static void handle_auth(struct hostapd_data *hapd,
- 				  mgmt->sa, ETH_ALEN);
- 			os_memcpy(sta->mld_info.links[link_id].local_addr,
- 				  hapd->own_addr, ETH_ALEN);
-+			os_memcpy(sta->setup_link_addr, mgmt->sa, ETH_ALEN);
- 		}
- 	}
- #endif /* CONFIG_IEEE80211BE */
-diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
-index ee6e20538..e9fa0ed6e 100644
---- a/src/ap/sta_info.c
-+++ b/src/ap/sta_info.c
-@@ -73,6 +73,22 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
- 	s = hapd->sta_hash[STA_HASH(sta)];
- 	while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
- 		s = s->hnext;
-+
-+	if (hapd->conf->mld_ap && !s) {
-+		u8 link_id;
-+
-+		for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
-+			struct hostapd_data *h = hostapd_mld_get_link_bss(hapd, link_id);
-+
-+			if (!h)
-+				continue;
-+
-+			for (s = h->sta_list; s; s = s->next)
-+				if (!os_memcmp(s->setup_link_addr, sta, 6))
-+					return s;
-+		}
-+	}
-+
- 	return s;
- }
- 
-diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
-index 38b80903d..cd89db6c8 100644
---- a/src/ap/sta_info.h
-+++ b/src/ap/sta_info.h
-@@ -102,6 +102,7 @@ struct sta_info {
- 	struct sta_info *next; /* next entry in sta list */
- 	struct sta_info *hnext; /* next entry in hash table list */
- 	u8 addr[6];
-+	u8 setup_link_addr[6];
- 	be32 ipaddr;
- 	struct dl_list ip6addr; /* list head for struct ip6addr */
- 	u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0084-mtk-hostapd-prevent-responding-to-mgmt-while-AP-MLD-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0084-mtk-hostapd-prevent-responding-to-mgmt-while-AP-MLD-.patch
new file mode 100644
index 0000000..82b31c7
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0084-mtk-hostapd-prevent-responding-to-mgmt-while-AP-MLD-.patch
@@ -0,0 +1,36 @@
+From 1e54da693f94da15372d3e397c362a20fb5b1af1 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 6 May 2024 18:14:35 +0800
+Subject: [PATCH 084/126] mtk: hostapd: prevent responding to mgmt while AP MLD
+ is initializing
+
+While AP MLD is initializing, it might include incomplete information
+inside its response of mgmt.
+Therefore this commit prevents responding to mgmt while AP MLD is initializing
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ieee802_11.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 37a2c5ab8..1365bc822 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -6305,6 +6305,13 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+ 	if (len < 24)
+ 		return 0;
+ 
++#ifdef CONFIG_IEEE80211BE
++	if (hapd->conf->mld_ap && !hapd->mld->started) {
++		wpa_printf(MSG_DEBUG, "MGMT: Drop the frame - MLD not ready");
++		return 1;
++	}
++#endif /* CONFIG_IEEE80211BE */
++
+ 	if (fi && fi->freq)
+ 		freq = fi->freq;
+ 	else
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0084-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0084-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch
deleted file mode 100644
index 650da0e..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0084-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From 665bc7cb59b4383aab615fff82fa601c468a4634 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Mon, 18 Dec 2023 18:53:35 +0800
-Subject: [PATCH 084/104] mtk: hostapd: update cookie only when noack is unset
-
-This can prevent cookie unmatched problems during setup.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- src/drivers/driver_nl80211.c | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 3d69c9c49..6d300c0c8 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -4472,7 +4472,7 @@ send_frame_cmd:
- 	res = nl80211_send_frame_cmd(bss, freq, wait_time, data, data_len,
- 				     use_cookie, no_cck, noack, offchanok,
- 				     csa_offs, csa_offs_len, link_id);
--	if (!res)
-+	if (!res && !noack)
- 		drv->send_frame_link_id = link_id;
- 
- 	return res;
-@@ -9205,8 +9205,8 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss,
- 			   "cookie 0x%llx", no_ack ? " (no ACK)" : "",
- 			   (long long unsigned int) cookie);
- 
--		if (save_cookie)
--			drv->send_frame_cookie = no_ack ? (u64) -1 : cookie;
-+		if (save_cookie && !no_ack)
-+			drv->send_frame_cookie = cookie;
- 
- 		if (!wait) {
- 			 /* There is no need to store this cookie since there
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0085-mtk-hostapd-add-critical-update-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0085-mtk-hostapd-add-critical-update-support.patch
new file mode 100644
index 0000000..f34a8ad
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0085-mtk-hostapd-add-critical-update-support.patch
@@ -0,0 +1,945 @@
+From d46df816cadde59bde528c08db281923874152cc Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 23 Apr 2024 16:03:19 +0800
+Subject: [PATCH 085/126] mtk: hostapd: add critical update support
+
+Add critical update support
+modification: wmm configuration
+inclusion: channel switch
+(affiliated link's per-STA profile CSA/eCSA countdown &
+ channel switch wrapper is included)
+Note that max channel switch time IE is not implemented
+in hostapd yet.
+
+1.Add max channel switch time IE
+Note that fw will find the MCST IE on its own,
+so there is no need to send the offset of the MCST IE to the kernel.
+The MCST's value is currently hardcoded to 500 TUs, since the WFA test plan
+restricts it to not exceeding 500 TUs (APUT 4.22.1).
+2. Add critical update support for radar triggered channel switch
+
+According to the test plan of APUT 4.44, the CSA after beacon should also
+include the CU flag and increase BPCC due to operation IE modification.
+Additionally, avoid setting beacons in hostapd_event_ch_switch after CSA
+is finished since it would conflict with the CSA after beacon.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/ctrl_iface.c         |  88 +++++++++++++++++++
+ hostapd/hostapd_cli.c        |   8 ++
+ hostapd/main.c               |   4 +-
+ src/ap/beacon.c              | 161 ++++++++++++++++++++++++++++++++++-
+ src/ap/beacon.h              |  37 ++++++++
+ src/ap/dfs.c                 |  12 +++
+ src/ap/drv_callbacks.c       |   1 -
+ src/ap/hostapd.c             |  85 +++++++++++++++++-
+ src/ap/hostapd.h             |   5 ++
+ src/ap/ieee802_11.c          |  15 ++++
+ src/ap/ieee802_11_eht.c      |   7 +-
+ src/ap/sta_info.h            |   6 ++
+ src/ap/ucode.c               |   9 +-
+ src/common/ieee802_11_defs.h |   1 +
+ src/drivers/driver.h         |   1 +
+ src/drivers/driver_nl80211.c |  53 +++++++-----
+ src/drivers/nl80211_copy.h   |   8 ++
+ 17 files changed, 470 insertions(+), 31 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index e19502d1d..ebe9053cf 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -2911,6 +2911,18 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ 			ret = err;
+ 			num_err++;
+ 		}
++
++#ifdef CONFIG_IEEE80211BE
++		if (iface->bss[i]->conf->mld_ap)
++			hostapd_update_aff_link_beacon(iface->bss[i], settings.cs_count);
++
++		/*
++		 * Currently, no FW notification event for clearing CU flag after DTIM period.
++		 * Also, another CU or set beacon is not allowed during CSA period.
++		 * Therefore, just clear it manually here for workaround.
++		 */
++		iface->bss[i]->eht_mld_bss_critical_update = 0;
++#endif /* CONFIG_IEEE80211BE */
+ 	}
+ 
+ 	return (iface->num_bss == num_err) ? ret : 0;
+@@ -5279,6 +5291,79 @@ hostapd_ctrl_iface_dump_csi(struct hostapd_data *hapd, char *cmd,
+ 	return 0;
+ }
+ 
++static int
++hostapd_ctrl_iface_wmm(struct hostapd_data *hapd, char *cmd, char *buf,
++		       size_t buflen)
++{
++	char *pos = cmd, *ac, *token, *context = NULL;
++	struct hostapd_wmm_ac_params *acp;
++	int num;
++
++	if (!hapd->conf->mld_ap)
++		return -1;
++
++	ac = pos;
++	pos = os_strchr(pos, ' ');
++	if (pos)
++		*pos++ = '\0';
++
++	if (os_strncmp(ac, "BE", 2) == 0) {
++		num = 0;
++	} else if (os_strncmp(ac, "BK", 2) == 0) {
++		num = 1;
++	} else if (os_strncmp(ac, "VI", 2) == 0) {
++		num = 2;
++	} else if (os_strncmp(ac, "VO", 2) == 0) {
++		num = 3;
++	} else {
++		wpa_printf(MSG_ERROR, "Unknown AC name '%s'", ac);
++		return -1;
++	}
++
++	acp = &hapd->iconf->wmm_ac_params[num];
++
++	/* if only ac is provied, show wmm params */
++	if (!pos)
++		return os_snprintf(buf, buflen,
++				   "link=%d ac=%s cwmin=%d cwmax=%d aifs=%d txop_limit=%d\n",
++				   hapd->mld_link_id, ac, acp->cwmin, acp->cwmax, acp->aifs, acp->txop_limit);
++
++	while ((token = str_token(pos, " ", &context))) {
++		if (os_strncmp(token, "cwmin=", 6) == 0) {
++			acp->cwmin = atoi(token + 6);
++			continue;
++		}
++
++		if (os_strncmp(token, "cwmax=", 6) == 0) {
++			acp->cwmax = atoi(token + 6);
++			continue;
++		}
++
++		if (os_strncmp(token, "aifs=", 5) == 0) {
++			acp->aifs = atoi(token + 5);
++			continue;
++		}
++
++		if (os_strncmp(token, "txop_limit=", 11) == 0) {
++			acp->txop_limit = atoi(token + 11);
++			continue;
++		}
++
++		wpa_printf(MSG_ERROR, "CTRL: Invalid WMM parameter: %s", token);
++		return -1;
++	}
++
++	if (acp->cwmin > acp->cwmax)
++		return -1;
++
++	ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_EDCA);
++
++	if (ieee802_11_set_beacon(hapd))
++		return -1;
++
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -5946,6 +6031,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 	} else if (os_strncmp(buf, "DUMP_CSI ", 8) == 0) {
+ 		reply_len = hostapd_ctrl_iface_dump_csi(hapd, buf + 9,
+ 							reply, reply_size);
++	} else if (os_strncmp(buf, "WMM", 3) == 0) {
++		reply_len = hostapd_ctrl_iface_wmm(hapd, buf + 4,
++						   reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 4e94db21d..e578c5b7e 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1767,6 +1767,12 @@ static int hostapd_cli_cmd_get_pp(struct wpa_ctrl *ctrl, int argc,
+ 	return hostapd_cli_cmd(ctrl, "get_pp", 1, argc, argv);
+ }
+ 
++static int hostapd_cli_cmd_wmm(struct wpa_ctrl *ctrl, int argc,
++			       char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "WMM", 1, argc, argv);
++}
++
+ struct hostapd_cli_cmd {
+ 	const char *cmd;
+ 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -2023,6 +2029,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 		" = Set preamble puncture mode"},
+ 	{ "get_pp", hostapd_cli_cmd_get_pp, NULL,
+ 		" = Get preamble puncture status"},
++	{ "wmm", hostapd_cli_cmd_wmm, NULL,
++		" = <ac> [cwmin=] [cwmax=] [aifs=] [txop_limit=]"},
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/hostapd/main.c b/hostapd/main.c
+index e790f18ce..8ecec6c1a 100644
+--- a/hostapd/main.c
++++ b/hostapd/main.c
+@@ -330,8 +330,8 @@ setup_mld:
+ 			return -1;
+ 		}
+ 
+-		/* Initialize the BSS parameter change to 1 */
+-		hapd->eht_mld_bss_param_change = 1;
++		/* Initialize the BSS parameter change to 0 */
++		hapd->eht_mld_bss_param_change = 0;
+ 
+ 		wpa_printf(MSG_DEBUG,
+ 			   "MLD: Set link_id=%u, mld_addr=" MACSTR
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 88e35acc2..9b5c4fc1e 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -514,6 +514,23 @@ static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid)
+ }
+ 
+ 
++static u8 * hostapd_eid_max_chsw_time(struct hostapd_data *hapd, u8 *eid)
++{
++	u32 max_chsw_time = 800;
++
++	if (!hapd->cs_freq_params.channel)
++		return eid;
++
++	*eid++ = WLAN_EID_EXTENSION;
++	*eid++ = 4;
++	*eid++ = WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME;
++	WPA_PUT_LE24(eid, max_chsw_time);
++	eid += 3;
++
++	return eid;
++}
++
++
+ static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid)
+ {
+ 	u8 op_class, channel;
+@@ -878,6 +895,7 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
+ #endif /* CONFIG_IEEE80211AX */
+ 
+ 	pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
++	pos = hostapd_eid_max_chsw_time(hapd, pos);
+ 
+ 	if (!params->is_ml_sta_info)
+ 		pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP,
+@@ -2185,6 +2203,63 @@ static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
+ #endif /* CONFIG_FILS */
+ 
+ 
++static void hostapd_fill_bcn_sta_profile(struct hostapd_data *hapd,
++					 struct mld_info *info)
++{
++	struct hostapd_data *h;
++
++	if (!info)
++		return;
++
++	os_memset(info, 0, sizeof(*info));
++
++	for_each_mld_link(h, hapd) {
++		unsigned int link_id = h->mld_link_id;
++		struct mld_link_info *link = &info->links[link_id];
++		u8 *epos, *csa_pos, buf[EHT_ML_MAX_STA_PROF_LEN];
++
++		if (!h->started || h == hapd ||
++		    h->eht_mld_bss_critical_update != BSS_CRIT_UPDATE_ALL)
++			continue;
++
++		link->valid = true;
++		os_memcpy(link->local_addr, h->own_addr, ETH_ALEN);
++
++		/* Build per-STA profile */
++		epos = buf;
++		/* Capabilities */
++		WPA_PUT_LE16(epos, hostapd_own_capab_info(h));
++		epos += 2;
++
++		/* CSA IE */
++		csa_pos = hostapd_eid_csa(h, epos);
++		if (csa_pos != epos)
++			link->sta_prof_csa_offset = csa_pos - 1 - buf;
++		epos = csa_pos;
++
++		/* eCSA IE */
++		csa_pos = hostapd_eid_ecsa(h, epos);
++		if (csa_pos != epos)
++			link->sta_prof_ecsa_offset = csa_pos - 1 - buf;
++		epos = csa_pos;
++
++		/* channel switch wrapper */
++		epos = hostapd_eid_wb_chsw_wrapper(h, epos);
++
++		/* max channel switch time */
++		epos = hostapd_eid_max_chsw_time(h, epos);
++
++		link->resp_sta_profile_len = epos - buf;
++		link->resp_sta_profile = os_memdup(buf, link->resp_sta_profile_len);
++
++		/* TODO:
++		 * 1. add other IEs
++		 * 2. handle per-STA profile inheritance
++		 * 3. handle csa offset if fragmentation is required
++		 */
++	}
++}
++
+ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ 			       struct wpa_driver_ap_params *params)
+ {
+@@ -2394,6 +2469,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ #endif /* CONFIG_IEEE80211AX */
+ 
+ 	tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
++	tailpos = hostapd_eid_max_chsw_time(hapd, tailpos);
+ 
+ 	tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON, true);
+ 	tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
+@@ -2423,9 +2499,30 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ 
+ #ifdef CONFIG_IEEE80211BE
+ 	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+-		if (hapd->conf->mld_ap)
+-			tailpos = hostapd_eid_eht_ml_beacon(hapd, NULL,
++		if (hapd->conf->mld_ap) {
++			struct hostapd_data *h;
++			struct mld_info info;
++			struct mld_link_info *link;
++			u32 base;
++			u8 link_id, *ml_pos = tailpos;
++
++			hostapd_fill_bcn_sta_profile(hapd, &info);
++			tailpos = hostapd_eid_eht_ml_beacon(hapd, &info,
+ 							    tailpos, false);
++
++			for_each_mld_link(h, hapd) {
++				link_id = h->mld_link_id;
++				link = &info.links[link_id];
++				base = ml_pos - tail + link->sta_prof_offset;
++				if (link->sta_prof_csa_offset)
++					hapd->cs_c_off_sta_prof[link_id] =
++							base + link->sta_prof_csa_offset;
++				if (link->sta_prof_ecsa_offset)
++					hapd->cs_c_off_ecsa_sta_prof[link_id] =
++							base + link->sta_prof_ecsa_offset;
++			}
++			ap_sta_free_sta_profile(&info);
++		}
+ 		tailpos = hostapd_eid_eht_capab(hapd, tailpos,
+ 						IEEE80211_MODE_AP);
+ 		tailpos = hostapd_eid_eht_operation(hapd, tailpos);
+@@ -2803,7 +2900,8 @@ void ieee802_11_set_beacon_per_bss_only(struct hostapd_data *hapd)
+ int ieee802_11_set_beacon(struct hostapd_data *hapd)
+ {
+ 	struct hostapd_iface *iface = hapd->iface;
+-	int ret;
++	struct hostapd_data *h;
++	int ret, link_id;
+ 	size_t i, j;
+ 	bool is_6g, hapd_mld = false;
+ 
+@@ -2846,6 +2944,17 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
+ 		}
+ 	}
+ 
++#ifdef CONFIG_IEEE80211BE
++	/* clear critical update flag for UPDATE_SINGLE type, for other types,
++	 * we should get some notified events from driver
++	 */
++	if (hapd->conf->mld_ap) {
++		for_each_mld_link(h, hapd)
++			if (h->eht_mld_bss_critical_update == BSS_CRIT_UPDATE_SINGLE)
++				h->eht_mld_bss_critical_update = 0;
++	}
++#endif /* CONFIG_IEEE80211BE */
++
+ 	return 0;
+ }
+ 
+@@ -2880,4 +2989,50 @@ int ieee802_11_update_beacons(struct hostapd_iface *iface)
+ 	return ret;
+ }
+ 
++
++int ieee802_11_set_bss_critical_update(struct hostapd_data *hapd,
++				       enum bss_crit_update_event event)
++{
++	if (!hapd->conf->mld_ap)
++		return 0;
++
++	switch (event) {
++	case BSS_CRIT_UPDATE_EVENT_CSA:
++	case BSS_CRIT_UPDATE_EVENT_ECSA:
++	case BSS_CRIT_UPDATE_EVENT_QUIET:
++	case BSS_CRIT_UPDATE_EVENT_WBCS:
++	case BSS_CRIT_UPDATE_EVENT_CS_WRAP:
++	case BSS_CRIT_UPDATE_EVENT_OP_MODE_NOTIF:
++	case BSS_CRIT_UPDATE_EVENT_QUIET_CH:
++	case BSS_CRIT_UPDATE_EVENT_CCA:
++	case BSS_CRIT_UPDATE_EVENT_BCAST_TWT:
++	case BSS_CRIT_UPDATE_EVENT_BCAST_TWT_PARAM_SET:
++	case BSS_CRIT_UPDATE_EVENT_IDX_ADJUST_FACTOR:
++	case BSS_CRIT_UPDATE_EVENT_TPE:
++		hapd->eht_mld_bss_param_change += 1;
++		hapd->eht_mld_bss_critical_update = BSS_CRIT_UPDATE_ALL;
++		return 0;
++	case BSS_CRIT_UPDATE_EVENT_EDCA:
++	case BSS_CRIT_UPDATE_EVENT_DSSS:
++	case BSS_CRIT_UPDATE_EVENT_HT_OPERATION:
++	case BSS_CRIT_UPDATE_EVENT_VHT_OPERATION:
++	case BSS_CRIT_UPDATE_EVENT_HE_OPERATION:
++	case BSS_CRIT_UPDATE_EVENT_MU_EDCA:
++	case BSS_CRIT_UPDATE_EVENT_SR:
++	case BSS_CRIT_UPDATE_EVENT_UORA:
++	case BSS_CRIT_UPDATE_EVENT_EHT_OPERATION:
++		hapd->eht_mld_bss_param_change += 1;
++		hapd->eht_mld_bss_critical_update = BSS_CRIT_UPDATE_SINGLE;
++		return 0;
++	case BSS_CRIT_UPDATE_EVENT_RECONFIG:
++	case BSS_CRIT_UPDATE_EVENT_ADD_LINK:
++	case BSS_CRIT_UPDATE_EVENT_ATTLM:
++		hapd->eht_mld_bss_critical_update = BSS_CRIT_UPDATE_FLAG;
++		return 0;
++	default:
++		hapd->eht_mld_bss_critical_update = BSS_CRIT_UPDATE_NONE;
++		return -1;
++	}
++}
++
+ #endif /* CONFIG_NATIVE_WINDOWS */
+diff --git a/src/ap/beacon.h b/src/ap/beacon.h
+index e381542a8..809393902 100644
+--- a/src/ap/beacon.h
++++ b/src/ap/beacon.h
+@@ -12,12 +12,49 @@
+ 
+ struct ieee80211_mgmt;
+ 
++enum bss_crit_update_event {
++	BSS_CRIT_UPDATE_EVENT_CSA,
++	BSS_CRIT_UPDATE_EVENT_ECSA,
++	BSS_CRIT_UPDATE_EVENT_EDCA,
++	BSS_CRIT_UPDATE_EVENT_QUIET,
++	BSS_CRIT_UPDATE_EVENT_DSSS,
++	BSS_CRIT_UPDATE_EVENT_HT_OPERATION,
++	BSS_CRIT_UPDATE_EVENT_WBCS,
++	BSS_CRIT_UPDATE_EVENT_CS_WRAP,
++	BSS_CRIT_UPDATE_EVENT_OP_MODE_NOTIF,
++	BSS_CRIT_UPDATE_EVENT_QUIET_CH,
++	BSS_CRIT_UPDATE_EVENT_VHT_OPERATION,
++	BSS_CRIT_UPDATE_EVENT_HE_OPERATION,
++	BSS_CRIT_UPDATE_EVENT_BCAST_TWT,
++	BSS_CRIT_UPDATE_EVENT_BCAST_TWT_PARAM_SET,
++	BSS_CRIT_UPDATE_EVENT_CCA,
++	BSS_CRIT_UPDATE_EVENT_MU_EDCA,
++	BSS_CRIT_UPDATE_EVENT_SR,
++	BSS_CRIT_UPDATE_EVENT_UORA,
++	BSS_CRIT_UPDATE_EVENT_IDX_ADJUST_FACTOR,
++	BSS_CRIT_UPDATE_EVENT_EHT_OPERATION,
++	BSS_CRIT_UPDATE_EVENT_TPE,
++	BSS_CRIT_UPDATE_EVENT_CH_CHANGED,
++	BSS_CRIT_UPDATE_EVENT_RECONFIG,
++	BSS_CRIT_UPDATE_EVENT_ADD_LINK,
++	BSS_CRIT_UPDATE_EVENT_ATTLM
++};
++
++enum {
++	BSS_CRIT_UPDATE_NONE,
++	BSS_CRIT_UPDATE_SINGLE,
++	BSS_CRIT_UPDATE_ALL,
++	BSS_CRIT_UPDATE_FLAG
++};
++
+ void handle_probe_req(struct hostapd_data *hapd,
+ 		      const struct ieee80211_mgmt *mgmt, size_t len,
+ 		      int ssi_signal);
+ void ieee802_11_set_beacon_per_bss_only(struct hostapd_data *hapd);
+ int ieee802_11_set_beacon(struct hostapd_data *hapd);
+ int ieee802_11_set_beacons(struct hostapd_iface *iface);
++int ieee802_11_set_bss_critical_update(struct hostapd_data *hapd,
++				       enum bss_crit_update_event event);
+ int ieee802_11_update_beacons(struct hostapd_iface *iface);
+ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ 			       struct wpa_driver_ap_params *params);
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index fc7699973..697e6364c 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1120,6 +1120,18 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
+ 		err = hostapd_switch_channel(iface->bss[i], &csa_settings);
+ 		if (err)
+ 			num_err++;
++
++#ifdef CONFIG_IEEE80211BE
++		if (iface->bss[i]->conf->mld_ap)
++			hostapd_update_aff_link_beacon(iface->bss[i], csa_settings.cs_count);
++
++		/*
++		 * Currently, no FW notification event for clearing CU flag after DTIM period.
++		 * Also, another CU or set beacon is not allowed during CSA period.
++		 * Therefore, just clear it manually here for workaround.
++		 */
++		iface->bss[i]->eht_mld_bss_critical_update = 0;
++#endif /* CONFIG_IEEE80211BE */
+ 	}
+ 
+ 	if (num_err == iface->num_bss) {
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 1107fd70e..705acfb67 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -1314,7 +1314,6 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
+ 	if (hapd->csa_in_progress &&
+ 	    freq == hapd->cs_freq_params.freq) {
+ 		hostapd_cleanup_cs_params(hapd);
+-		ieee802_11_set_beacon(hapd);
+ 
+ 		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
+ 			"freq=%d dfs=%d", freq, is_dfs);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 2c9e54b23..f03a6242f 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4524,6 +4524,9 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
+ 	old_punct_bitmap = iface->conf->punct_bitmap;
+ 	iface->conf->punct_bitmap = settings->punct_bitmap;
+ #endif /* CONFIG_IEEE80211BE */
++
++	/* Another CU in the new channel due to OP element modification */
++	ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_EHT_OPERATION);
+ 	ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
+ 
+ 	/* change back the configuration */
+@@ -4541,20 +4544,32 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
+ 	hapd->cs_count = settings->cs_count;
+ 	hapd->cs_block_tx = settings->block_tx;
+ 
++#ifdef CONFIG_IEEE80211BE
++	/* Restore BPCC to build the CSA beacon */
++	hapd->eht_mld_bss_param_change--;
++	hapd->eht_mld_bss_critical_update = BSS_CRIT_UPDATE_ALL;
++#endif /* CONFIG_IEEE80211BE */
++
+ 	ret = hostapd_build_beacon_data(hapd, &settings->beacon_csa);
+ 	if (ret) {
+ 		free_beacon_data(&settings->beacon_after);
+ 		return ret;
+ 	}
+ 
++	/* Change back to the final BPCC and CU flag */
++	ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_EHT_OPERATION);
++
+ 	settings->counter_offset_beacon[0] = hapd->cs_c_off_beacon;
+ 	settings->counter_offset_presp[0] = hapd->cs_c_off_proberesp;
+ 	settings->counter_offset_beacon[1] = hapd->cs_c_off_ecsa_beacon;
+ 	settings->counter_offset_presp[1] = hapd->cs_c_off_ecsa_proberesp;
+ 	settings->link_id = -1;
++	settings->freq_params.link_id = -1;
+ #ifdef CONFIG_IEEE80211BE
+-	if (hapd->conf->mld_ap)
++	if (hapd->conf->mld_ap) {
+ 		settings->link_id = hapd->mld_link_id;
++		settings->freq_params.link_id = hapd->mld_link_id;
++	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ #ifdef CONFIG_IEEE80211AX
+@@ -4616,6 +4631,8 @@ int hostapd_switch_channel(struct hostapd_data *hapd,
+ 		return -1;
+ 	}
+ 
++	ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_CSA);
++
+ 	ret = hostapd_fill_csa_settings(hapd, settings);
+ 	if (ret)
+ 		return ret;
+@@ -4637,6 +4654,72 @@ int hostapd_switch_channel(struct hostapd_data *hapd,
+ 	return 0;
+ }
+ 
++int hostapd_update_aff_link_beacon(struct hostapd_data *hapd, u8 cs_count)
++{
++	struct hostapd_data *h;
++	unsigned int cs_link_id = hapd->mld_link_id;
++	int cs_channel = hapd->cs_freq_params.channel;
++
++	/* TODO: add beacon offload driver flag */
++	for_each_mld_link(h, hapd) {
++		struct hostapd_config *conf = h->iconf;
++		struct hostapd_hw_modes *mode = h->iface->current_mode;
++		struct csa_settings settings = {};
++		unsigned int link_id = h->mld_link_id;
++		int ret;
++
++		if (!h->started || h == hapd)
++			continue;
++
++		hostapd_set_freq_params(&settings.freq_params, conf->hw_mode,
++					hostapd_hw_get_freq(h, conf->channel),
++					conf->channel, conf->enable_edmg,
++					conf->edmg_channel, conf->ieee80211n,
++					conf->ieee80211ac, conf->ieee80211ax,
++					conf->ieee80211be, conf->secondary_channel,
++					hostapd_get_oper_chwidth(conf),
++					hostapd_get_oper_centr_freq_seg0_idx(conf),
++					hostapd_get_oper_centr_freq_seg1_idx(conf),
++					conf->vht_capab,
++					mode ? &mode->he_capab[IEEE80211_MODE_AP] : NULL,
++					mode ? &mode->eht_capab[IEEE80211_MODE_AP] : NULL,
++					hostapd_get_punct_bitmap(h));
++		hapd->cs_freq_params.channel = 0;
++		ret = hostapd_build_beacon_data(h, &settings.beacon_after);
++		if (ret)
++			return ret;
++
++		hapd->cs_freq_params.channel = cs_channel;
++		/* Restore BPCC to build the RNR for the CS link */
++		hapd->eht_mld_bss_param_change--;
++		hapd->eht_mld_bss_critical_update = BSS_CRIT_UPDATE_ALL;
++		ret = hostapd_build_beacon_data(h, &settings.beacon_csa);
++		if (ret) {
++			free_beacon_data(&settings.beacon_after);
++			return ret;
++		}
++
++		/* Change back to the final BPCC and CU flag */
++		ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_EHT_OPERATION);
++
++		settings.counter_offset_sta_prof[cs_link_id][0] =
++						h->cs_c_off_sta_prof[cs_link_id];
++		settings.counter_offset_sta_prof[cs_link_id][1] =
++						h->cs_c_off_ecsa_sta_prof[cs_link_id];
++		settings.link_id = cs_link_id;
++		settings.freq_params.link_id = link_id;
++		settings.cs_count = cs_count;
++		settings.punct_bitmap = conf->punct_bitmap;
++		ret = hostapd_drv_switch_channel(h, &settings);
++		free_beacon_data(&settings.beacon_csa);
++		free_beacon_data(&settings.beacon_after);
++		if (ret)
++			return ret;
++	}
++
++	return 0;
++}
++
+ 
+ void
+ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 4db67096b..99d5a01d6 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -342,6 +342,9 @@ struct hostapd_data {
+ 	unsigned int cs_c_off_ecsa_beacon;
+ 	unsigned int cs_c_off_ecsa_proberesp;
+ 
++	unsigned int cs_c_off_sta_prof[MAX_NUM_MLD_LINKS];
++	unsigned int cs_c_off_ecsa_sta_prof[MAX_NUM_MLD_LINKS];
++
+ #ifdef CONFIG_IEEE80211AX
+ 	bool cca_in_progress;
+ 	u8 cca_count;
+@@ -501,6 +504,7 @@ struct hostapd_data {
+ 
+ #ifdef CONFIG_IEEE80211BE
+ 	u8 eht_mld_bss_param_change;
++	u8 eht_mld_bss_critical_update;
+ 	struct hostapd_mld *mld;
+ 	struct dl_list link;
+ 	u8 mld_link_id;
+@@ -850,6 +854,7 @@ void hostapd_chan_switch_config(struct hostapd_data *hapd,
+ 				struct hostapd_freq_params *freq_params);
+ int hostapd_switch_channel(struct hostapd_data *hapd,
+ 			   struct csa_settings *settings);
++int hostapd_update_aff_link_beacon(struct hostapd_data *hapd, u8 cs_count);
+ void
+ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+ 				const struct hostapd_freq_params *freq_params);
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 1365bc822..bb508fe79 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -282,6 +282,7 @@ u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid,
+ 
+ u16 hostapd_own_capab_info(struct hostapd_data *hapd)
+ {
++	struct hostapd_data *h;
+ 	int capab = WLAN_CAPABILITY_ESS;
+ 	int privacy = 0;
+ 	int dfs;
+@@ -342,6 +343,18 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd)
+ 		}
+ 	}
+ 
++#ifdef CONFIG_IEEE80211BE
++	if (hapd->conf->mld_ap) {
++		for_each_mld_link(h, hapd) {
++			if (h->eht_mld_bss_critical_update) {
++				capab |= WLAN_CAPABILITY_PBCC;
++				break;
++			}
++		}
++	}
++#endif /* CONFIG_IEEE80211BE */
++
++
+ 	return capab;
+ }
+ 
+@@ -7838,6 +7851,8 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
+ 			(MAX_NUM_MLD_LINKS | 0xF0);
+ 		/* BPCC (Bit 3 to Bit 0) */
+ 		*eid = is_partner ? ((param_ch & 0xF0) >> 4) : 0x0F;
++		if (bss->eht_mld_bss_critical_update == BSS_CRIT_UPDATE_ALL)
++			*eid |= RNR_TBTT_INFO_MLD_PARAM2_ALL_UPDATE_INC;
+ #ifdef CONFIG_TESTING_OPTIONS
+ 		if (bss->conf->mld_indicate_disabled)
+ 			*eid |= RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index a04eb23e0..2b5c06d6d 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -495,7 +495,7 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 	wpabuf_put_u8(buf, hapd->mld_link_id);
+ 
+ 	/* Currently hard code the BSS Parameters Change Count to 0x1 */
+-	wpabuf_put_u8(buf, 0x1);
++	wpabuf_put_u8(buf, hapd->eht_mld_bss_param_change);
+ 
+ 	wpa_printf(MSG_DEBUG, "MLD: EML Capabilities=0x%x",
+ 		   hapd->iface->mld_eml_capa);
+@@ -593,11 +593,14 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 		wpabuf_put_u8(buf, link_bss->conf->dtim_period);
+ 
+ 		/* BSS Parameters Change Count */
+-		wpabuf_put_u8(buf, hapd->eht_mld_bss_param_change);
++		wpabuf_put_u8(buf, link_bss->eht_mld_bss_param_change);
+ 
+ 		if (!link->resp_sta_profile)
+ 			continue;
+ 
++#define EXT_EID_TAG_LEN 3
++		link->sta_prof_offset = wpabuf_len(buf) + EXT_EID_TAG_LEN;
++
+ 		/* Fragment the sub element if needed */
+ 		if (total_len <= 255) {
+ 			wpabuf_put_data(buf, link->resp_sta_profile,
+diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
+index 67b97671a..60b33f049 100644
+--- a/src/ap/sta_info.h
++++ b/src/ap/sta_info.h
+@@ -89,6 +89,12 @@ struct mld_info {
+ 		u16 status;
+ 		u16 resp_sta_profile_len;
+ 		u8 *resp_sta_profile;
++
++		u32 sta_prof_csa_offset;
++		u32 sta_prof_ecsa_offset;
++		u32 sta_prof_offset;
++
++		const u8 *rsne, *rsnxe;
+ 	} links[MAX_NUM_MLD_LINKS];
+ };
+ 
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index da1c4c1ac..a72193282 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -738,9 +738,16 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 	wpa_printf(MSG_INFO, "    * center_freq2 is %d\n",
+ 			csa.freq_params.center_freq2);
+ 
+-	for (i = 0; i < iface->num_bss; i++)
++	for (i = 0; i < iface->num_bss; i++) {
+ 		ret = hostapd_switch_channel(iface->bss[i], &csa);
+ 
++		if (iface->bss[i]->conf->mld_ap)
++			hostapd_update_aff_link_beacon(iface->bss[i], csa.cs_count);
++
++		/* FIXME: remove this line after CU event merged */
++		iface->bss[i]->eht_mld_bss_critical_update = 0;
++	}
++
+ 	return ucv_boolean_new(!ret);
+ }
+ 
+diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
+index c380d0c7e..efb584c66 100644
+--- a/src/common/ieee802_11_defs.h
++++ b/src/common/ieee802_11_defs.h
+@@ -504,6 +504,7 @@
+ #define WLAN_EID_EXT_HE_MU_EDCA_PARAMS 38
+ #define WLAN_EID_EXT_SPATIAL_REUSE 39
+ #define WLAN_EID_EXT_COLOR_CHANGE_ANNOUNCEMENT 42
++#define WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME 52
+ #define WLAN_EID_EXT_OCV_OCI 54
+ #define WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION 55
+ #define WLAN_EID_EXT_NON_INHERITANCE 56
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index b75580374..924a1baba 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -2763,6 +2763,7 @@ struct csa_settings {
+ 
+ 	u16 counter_offset_beacon[2];
+ 	u16 counter_offset_presp[2];
++	u16 counter_offset_sta_prof[MAX_NUM_MLD_LINKS][2];
+ 
+ 	u16 punct_bitmap;
+ 	int link_id;
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 8b64c3983..555c97bf7 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -11417,9 +11417,10 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+ 	struct nlattr *beacon_csa;
+-	int ret = -ENOBUFS;
+-	int csa_off_len = 0;
+-	int i;
++	int i, csa_off_len = 0, ret = -ENOBUFS;
++	unsigned int cs_link_id = settings->link_id;
++	u16 *counter_offset_beacon = settings->counter_offset_beacon;
++	u16 *counter_offset_presp = settings->counter_offset_presp;
+ 
+ 	wpa_printf(MSG_DEBUG,
+ 		   "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d channel=%d sec_channel_offset=%d width=%d cf1=%d cf2=%d puncturing_bitmap=0x%04x link_id=%d%s%s%s)",
+@@ -11431,7 +11432,7 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ 		   settings->freq_params.center_freq1,
+ 		   settings->freq_params.center_freq2,
+ 		   settings->punct_bitmap,
+-		   settings->link_id,
++		   settings->freq_params.link_id,
+ 		   settings->freq_params.ht_enabled ? " ht" : "",
+ 		   settings->freq_params.vht_enabled ? " vht" : "",
+ 		   settings->freq_params.he_enabled ? " he" : "");
+@@ -11451,18 +11452,19 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ 	 * counters match. This implementation assumes that there are only two
+ 	 * counters.
+ 	 */
+-	if (settings->counter_offset_beacon[0] &&
+-	    !settings->counter_offset_beacon[1]) {
++	if (cs_link_id != settings->freq_params.link_id) {
++		counter_offset_beacon = settings->counter_offset_sta_prof[cs_link_id];
++		counter_offset_presp = NULL;
++	}
++
++	if (counter_offset_beacon[0] && !counter_offset_beacon[1]) {
+ 		csa_off_len = 1;
+-	} else if (settings->counter_offset_beacon[1] &&
+-		   !settings->counter_offset_beacon[0]) {
++	} else if (counter_offset_beacon[1] && !counter_offset_beacon[0]) {
+ 		csa_off_len = 1;
+-		settings->counter_offset_beacon[0] =
+-			settings->counter_offset_beacon[1];
+-		settings->counter_offset_presp[0] =
+-			settings->counter_offset_presp[1];
+-	} else if (settings->counter_offset_beacon[1] &&
+-		   settings->counter_offset_beacon[0]) {
++		counter_offset_beacon[0] = counter_offset_beacon[1];
++		if (counter_offset_presp)
++			counter_offset_presp[0] = counter_offset_presp[1];
++	} else if (counter_offset_beacon[1] && counter_offset_beacon[0]) {
+ 		csa_off_len = 2;
+ 	} else {
+ 		wpa_printf(MSG_ERROR, "nl80211: No CSA counters provided");
+@@ -11481,14 +11483,18 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ 		return -EINVAL;
+ 
+ 	for (i = 0; i < csa_off_len; i++) {
+-		u16 csa_c_off_bcn = settings->counter_offset_beacon[i];
+-		u16 csa_c_off_presp = settings->counter_offset_presp[i];
++		u16 csa_c_off_bcn = counter_offset_beacon[i];
++		u16 csa_c_off_presp;
+ 
+ 		if ((settings->beacon_csa.tail_len <= csa_c_off_bcn) ||
+ 		    (settings->beacon_csa.tail[csa_c_off_bcn] !=
+ 		     settings->cs_count))
+ 			return -EINVAL;
+ 
++		if (!counter_offset_presp)
++			continue;
++
++		csa_c_off_presp = counter_offset_presp[i];
+ 		if (settings->beacon_csa.probe_resp &&
+ 		    ((settings->beacon_csa.probe_resp_len <=
+ 		      csa_c_off_presp) ||
+@@ -11506,8 +11512,8 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ 	    (settings->punct_bitmap &&
+ 	     nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP,
+ 			 settings->punct_bitmap)) ||
+-	    (settings->link_id != NL80211_DRV_LINK_ID_NA &&
+-	     nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, settings->link_id)))
++	    (settings->freq_params.link_id != NL80211_DRV_LINK_ID_NA &&
++	     nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, settings->freq_params.link_id)))
+ 		goto error;
+ 
+ 	/* beacon_after params */
+@@ -11528,9 +11534,14 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ 	if (ret)
+ 		goto error;
+ 
+-	if (nla_put(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
+-		    csa_off_len * sizeof(u16),
+-		    settings->counter_offset_beacon) ||
++	if ((cs_link_id == settings->freq_params.link_id &&
++	     nla_put(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
++		     csa_off_len * sizeof(u16),
++		     settings->counter_offset_beacon)) ||
++	    (cs_link_id != settings->freq_params.link_id &&
++	     nla_put(msg, NL80211_ATTR_CSA_C_OFF_STA_PROF,
++		     csa_off_len * sizeof(u16),
++		     settings->counter_offset_sta_prof[cs_link_id])) ||
+ 	    (settings->beacon_csa.probe_resp &&
+ 	     nla_put(msg, NL80211_ATTR_CSA_C_OFF_PRESP,
+ 		     csa_off_len * sizeof(u16),
+diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
+index d425c797c..13837297c 100644
+--- a/src/drivers/nl80211_copy.h
++++ b/src/drivers/nl80211_copy.h
+@@ -2868,6 +2868,10 @@ enum nl80211_commands {
+  *	nested item, it contains attributes defined in
+  *	&enum nl80211_if_combination_attrs.
+  *
++ * @NL80211_ATTR_CNTDWN_OFFS_STA_PROF: An array of offsets (u16) to the channel
++ *	switch or color change counters in the per-STA profile corresponding to
++ *	the affected AP.
++ *
+  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
+  * @NL80211_ATTR_MAX: highest attribute number currently defined
+  * @__NL80211_ATTR_AFTER_LAST: internal use
+@@ -3418,6 +3422,9 @@ enum nl80211_attrs {
+ 
+ 	/* add attributes here, update the policy in nl80211.c */
+ 
++	/* MTK internal */
++	NL80211_ATTR_CNTDWN_OFFS_STA_PROF,
++
+ 	__NL80211_ATTR_AFTER_LAST,
+ 	NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST,
+ 	NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
+@@ -3430,6 +3437,7 @@ enum nl80211_attrs {
+ #define NL80211_ATTR_SAE_DATA NL80211_ATTR_AUTH_DATA
+ #define NL80211_ATTR_CSA_C_OFF_BEACON NL80211_ATTR_CNTDWN_OFFS_BEACON
+ #define NL80211_ATTR_CSA_C_OFF_PRESP NL80211_ATTR_CNTDWN_OFFS_PRESP
++#define NL80211_ATTR_CSA_C_OFF_STA_PROF NL80211_ATTR_CNTDWN_OFFS_STA_PROF
+ 
+ /*
+  * Allow user space programs to use #ifdef on new attributes by defining them
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0085-mtk-wpa_s-fix-bss-selection-when-setting-mld_connect.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0085-mtk-wpa_s-fix-bss-selection-when-setting-mld_connect.patch
deleted file mode 100644
index 820e085..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0085-mtk-wpa_s-fix-bss-selection-when-setting-mld_connect.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From e2e07813d1e05a72aa649614ad942036b387aaf1 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Fri, 29 Dec 2023 15:04:27 +0800
-Subject: [PATCH 085/104] mtk: wpa_s: fix bss selection when setting
- mld_connect_band_pref
-
-Without this patch, when setting mld_connect_band_pref as 5g, wrong bss
-will be selected.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- wpa_supplicant/sme.c | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
-diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
-index e1183722f..5b69812b5 100644
---- a/wpa_supplicant/sme.c
-+++ b/wpa_supplicant/sme.c
-@@ -437,8 +437,11 @@ static struct wpa_bss * wpas_ml_connect_pref(struct wpa_supplicant *wpa_s,
- 	}
- 
- 	for_each_link(wpa_s->valid_links, i) {
--		if (wpa_s->mlo_assoc_link_id == i)
-+		if (wpa_s->mlo_assoc_link_id == i) {
-+			if (bss->freq >= low && bss->freq <= high)
-+				return bss;
- 			continue;
-+		}
- 
- 		if (wpa_s->links[i].freq >= low && wpa_s->links[i].freq <= high)
- 			goto found;
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0086-mtk-hostapd-add-mld_primary-option.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0086-mtk-hostapd-add-mld_primary-option.patch
deleted file mode 100644
index 4fa5b6f..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0086-mtk-hostapd-add-mld_primary-option.patch
+++ /dev/null
@@ -1,41 +0,0 @@
-From bcb603194f7df4fd3060ed6a13a2e4da2715d959 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Tue, 26 Dec 2023 08:05:41 +0800
-Subject: [PATCH 086/104] mtk: hostapd: add mld_primary option
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- hostapd/config_file.c | 2 ++
- src/ap/ap_config.h    | 3 +++
- 2 files changed, 5 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 7bc19479d..e9caa45f3 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5349,6 +5349,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		conf->punct_acs_threshold = val;
- 	} else if (os_strcmp(buf, "mld_ap") == 0) {
- 		bss->mld_ap = !!atoi(pos);
-+	} else if (os_strcmp(buf, "mld_primary") == 0) {
-+		bss->mld_primary = !!atoi(pos);
- 	} else if (os_strcmp(buf, "mld_addr") == 0) {
- 		if (hwaddr_aton(pos, bss->mld_addr)) {
- 			wpa_printf(MSG_ERROR, "Line %d: Invalid mld_addr",
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 7f48c71f5..1f686550e 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -966,6 +966,9 @@ struct hostapd_bss_config {
- 	/* The AP is part of an AP MLD */
- 	u8 mld_ap;
- 
-+	/* The AP is the primary AP of an AP MLD */
-+	u8 mld_primary;
-+
- 	/* The MLD ID to which the AP MLD is affiliated with */
- 	u8 mld_id;
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0086-mtk-hostapd-add-support-for-emlsr.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0086-mtk-hostapd-add-support-for-emlsr.patch
new file mode 100644
index 0000000..0cf1d0b
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0086-mtk-hostapd-add-support-for-emlsr.patch
@@ -0,0 +1,733 @@
+From 093ac6b9d2f9c79b137f327af1eb0236ba71064a Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+Date: Thu, 9 May 2024 15:41:18 +0800
+Subject: [PATCH 086/126] mtk: hostapd: add support for emlsr
+
+1. Processing the EML capability IE in the association request,
+and sending the value of EML field to the kernel.
+
+2. Processing the EML Operating Mode Notification frame,
+and sending the EML Operating Mode Notification frame if eml_resp is
+true.
+
+Command Usage:
+eml_resp -
+hostapd_cli -i <interface> eml_resp <enable>
+
+Processing the EML capability IE in the association request,
+and sending the value of EML field to the kernel.
+
+The original flow send the mcu command to the firmware using the WCID of
+the primary link. Therefore, a  is passed in the hostapd layer
+to ensure that the EML OMN is processed and sent to the firmware using
+the WCID of the receiving link.
+
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ hostapd/config_file.c             |   4 +
+ hostapd/ctrl_iface.c              |  36 +++++++++
+ hostapd/hostapd.conf              |  10 +++
+ hostapd/hostapd_cli.c             |   8 ++
+ src/ap/ap_config.h                |   2 +
+ src/ap/ap_drv_ops.c               |  20 ++++-
+ src/ap/ap_drv_ops.h               |   4 +-
+ src/ap/apup.c                     |   4 +-
+ src/ap/ieee802_11.c               |   8 +-
+ src/ap/ieee802_11.h               |   4 +-
+ src/ap/ieee802_11_eht.c           | 124 ++++++++++++++++++++++++++++--
+ src/ap/sta_info.c                 |   2 +-
+ src/common/ieee802_11_defs.h      |  34 ++++++++
+ src/common/mtk_vendor.h           |  15 ++++
+ src/drivers/driver.h              |  12 +++
+ src/drivers/driver_nl80211.c      |  53 +++++++++++++
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   3 +
+ 18 files changed, 331 insertions(+), 13 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 206055b75..38273a4f2 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5477,6 +5477,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		}
+ 	} else if (os_strcmp(buf, "eht_bw320_offset") == 0) {
+ 		conf->eht_bw320_offset = atoi(pos);
++	} else if (os_strcmp(buf, "eml_disable") == 0) {
++		conf->eml_disable = atoi(pos);
++	} else if (os_strcmp(buf, "eml_resp") == 0) {
++		conf->eml_resp = atoi(pos);
+ #ifdef CONFIG_TESTING_OPTIONS
+ 	} else if (os_strcmp(buf, "eht_oper_puncturing_override") == 0) {
+ 		if (get_u16(pos, line, &bss->eht_oper_puncturing_override))
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index ebe9053cf..d9775e13e 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -5104,6 +5104,40 @@ hostapd_ctrl_iface_disable_beacon(struct hostapd_data *hapd, char *value,
+ 
+ }
+ 
++static int
++hostapd_ctrl_iface_set_eml_resp(struct hostapd_data *hapd, char *value,
++				char *buf, size_t buflen)
++{
++	struct hostapd_data *link;
++	int cnt = 0;
++	u16 *val;
++
++	if (!hostapd_is_mld_ap(hapd))
++		return -1;
++
++	cnt = hostapd_parse_argument_helper(value, &val);
++	if (cnt == -1)
++		goto fail;
++	if (cnt != 1 || val[0] < 0)
++		goto para_fail;
++
++	for_each_mld_link(link, hapd) {
++		link->iconf->eml_resp = val[0];
++		wpa_printf(MSG_ERROR, "Link:%d, Response EML:%d\n",
++			   link->iconf->band_idx, link->iconf->eml_resp);
++	}
++
++	os_free(val);
++
++	return os_snprintf(buf, buflen, "OK\n");
++
++para_fail:
++	os_free(val);
++	wpa_printf(MSG_ERROR, "Input number or value is incorrect\n");
++fail:
++	return os_snprintf(buf, buflen, "FAIL\n");
++}
++
+ static int
+ hostapd_ctrl_iface_set_csi(struct hostapd_data *hapd, char *cmd,
+ 					char *buf, size_t buflen)
+@@ -6034,6 +6068,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 	} else if (os_strncmp(buf, "WMM", 3) == 0) {
+ 		reply_len = hostapd_ctrl_iface_wmm(hapd, buf + 4,
+ 						   reply, reply_size);
++	} else if (os_strncmp(buf, "EML_RESP ", 9) == 0) {
++		reply_len = hostapd_ctrl_iface_set_eml_resp(hapd, buf + 9, reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
+index 118754800..d0830eecd 100644
+--- a/hostapd/hostapd.conf
++++ b/hostapd/hostapd.conf
+@@ -1149,6 +1149,16 @@ wmm_ac_vo_acm=0
+ # will be used as the AP MLD MAC address.
+ #mld_addr=02:03:04:05:06:07
+ 
++# EML Capabilities
++# 0 = Enable EML capabilities in Multi-Link Control subfield
++# 1 = Disable EML capabilitites in Multi-Link Control subfield
++#eml_disable=0
++
++# EML Operating Mode Notification frame
++# 0 = AP does not send EML Operating Mode Notification frame to the station
++# 1 = AP sends EML Operating Mode Notification frame to the station
++#eml_resp=1
++
+ ##### IEEE 802.1X-2004 related configuration ##################################
+ 
+ # Require IEEE 802.1X authorization
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index e578c5b7e..54fda5c45 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1488,6 +1488,12 @@ static int hostapd_cli_cmd_disable_beacon(struct wpa_ctrl *ctrl, int argc,
+ 	return hostapd_cli_cmd(ctrl, "NO_BEACON", 1, argc, argv);
+ }
+ 
++static int hostapd_cli_cmd_set_eml_resp(struct wpa_ctrl *ctrl, int argc,
++					char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "EML_RESP", 1, argc, argv);
++}
++
+ #ifdef CONFIG_DPP
+ 
+ static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
+@@ -1937,6 +1943,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 		" = show mu onoff value in 0-15 bitmap"},
+ 	{ "no_beacon", hostapd_cli_cmd_disable_beacon, NULL,
+ 		"<value> 0: Enable beacon, 1: Disable beacon"},
++	{ "eml_resp", hostapd_cli_cmd_set_eml_resp, NULL,
++		"<value> 0: AP does not send EML Operating Mode Notification frame, 1: AP sends EML OMN frame"},
+ #ifdef CONFIG_DPP
+ 	{ "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.h b/src/ap/ap_config.h
+index 15b66ca30..9c3a28cf4 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1281,6 +1281,8 @@ struct hostapd_config {
+ 	u8 punct_acs_threshold;
+ 	u8 eht_default_pe_duration;
+ 	u8 eht_bw320_offset;
++	u8 eml_disable;
++	u8 eml_resp;
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ 	/* EHT enable/disable config from CHAN_SWITCH */
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index fb09a3290..2d2198a44 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -496,7 +496,8 @@ int hostapd_sta_add(struct hostapd_data *hapd,
+ 		    size_t eht_capab_len,
+ 		    const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
+ 		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
+-		    int set, const u8 *link_addr, bool mld_link_sta)
++		    int set, const u8 *link_addr, bool mld_link_sta,
++		    u16 eml_capa)
+ {
+ 	struct hostapd_sta_add_params params;
+ 
+@@ -536,6 +537,7 @@ int hostapd_sta_add(struct hostapd_data *hapd,
+ 		params.mld_link_id = hapd->mld_link_id;
+ 		params.mld_link_addr = link_addr;
+ 		params.mld_link_sta = mld_link_sta;
++		params.eml_capa = eml_capa;
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+@@ -1448,6 +1450,22 @@ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
+ 	return hapd->driver->beacon_ctrl(hapd->drv_priv, beacon_mode);
+ }
+ 
++int hostapd_drv_set_eml_omn(struct hostapd_data *hapd, u8 *mac,
++			    struct eml_omn_element *omn_ie)
++{
++	u8 link_id;
++
++	if (!hapd->driver || !hapd->driver->set_eml_omn)
++		return 0;
++
++	if (!hapd->conf->mld_ap)
++		return 0;
++
++	link_id = hapd->mld_link_id;
++
++	return hapd->driver->set_eml_omn(hapd->drv_priv, link_id, mac, omn_ie);
++}
++
+ int hostapd_drv_csi_set(struct hostapd_data *hapd, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac)
+ {
+ 	if (!hapd->driver || !hapd->driver->csi_set)
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 9f2e86a9b..ca2efab82 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -52,7 +52,8 @@ int hostapd_sta_add(struct hostapd_data *hapd,
+ 		    size_t eht_capab_len,
+ 		    const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
+ 		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
+-		    int set, const u8 *link_addr, bool mld_link_sta);
++		    int set, const u8 *link_addr, bool mld_link_sta,
++		    u16 eml_capa);
+ int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
+ int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
+ 			     size_t elem_len);
+@@ -174,6 +175,7 @@ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_
+ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
+ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
+ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode);
++int hostapd_drv_set_eml_omn(struct hostapd_data *hapd, u8 *mac, struct eml_omn_element *omn_ie);
+ int hostapd_drv_csi_set(struct hostapd_data *hapd, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac);
+ int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf);
+ 
+diff --git a/src/ap/apup.c b/src/ap/apup.c
+index f736ddc8e..cb0264e9e 100644
+--- a/src/ap/apup.c
++++ b/src/ap/apup.c
+@@ -67,7 +67,7 @@ void apup_process_beacon(struct hostapd_data *hapd,
+ 	            NULL, 0, 0, NULL, NULL, NULL, 0, NULL, 0, NULL,
+ 	            sta_ret->flags, 0, 0, 0,
+ 	            0, // 0 add, 1 set
+-	            mld_link_addr, mld_link_sta);
++	            mld_link_addr, mld_link_sta, 0);
+ 
+ 	sta_ret->flags |= WLAN_STA_AUTH;
+ 	wpa_auth_sm_event(sta_ret->wpa_sm, WPA_AUTH);
+@@ -141,7 +141,7 @@ void apup_process_beacon(struct hostapd_data *hapd,
+ 	            sta_ret->vht_opmode,
+ 	            0, // int supp_p2p_ps
+ 	            1, // 0 add, 1 set
+-	            mld_link_addr, mld_link_sta);
++	            mld_link_addr, mld_link_sta, 0);
+ 
+ 	ap_sta_set_authorized(hapd, sta_ret, 1);
+ 	hostapd_set_sta_flags(hapd, sta_ret);
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index bb508fe79..09f033e13 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -4763,6 +4763,7 @@ static int add_associated_sta(struct hostapd_data *hapd,
+ 	int set = 1;
+ 	const u8 *mld_link_addr = NULL;
+ 	bool mld_link_sta = false;
++	u16 eml_capa = 0;
+ 
+ #ifdef CONFIG_IEEE80211BE
+ 	if (ap_sta_is_mld(hapd, sta)) {
+@@ -4773,6 +4774,8 @@ static int add_associated_sta(struct hostapd_data *hapd,
+ 
+ 		if (hapd->mld_link_id != sta->mld_assoc_link_id)
+ 			set = 0;
++
++		eml_capa = sta->mld_info.common_info.eml_capa;
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+@@ -4861,7 +4864,7 @@ static int add_associated_sta(struct hostapd_data *hapd,
+ 			    sta->he_6ghz_capab,
+ 			    sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
+ 			    sta->vht_opmode, sta->p2p_ie ? 1 : 0,
+-			    set, mld_link_addr, mld_link_sta)) {
++			    set, mld_link_addr, mld_link_sta, eml_capa)) {
+ 		hostapd_logger(hapd, sta->addr,
+ 			       HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE,
+ 			       "Could not %s STA to kernel driver",
+@@ -6214,6 +6217,9 @@ static int handle_action(struct hostapd_data *hapd,
+ 		if (hapd->public_action_cb || hapd->public_action_cb2)
+ 			return 1;
+ 		break;
++	case WLAN_ACTION_PROTECTED_EHT:
++		ieee802_11_rx_prot_eht(hapd, mgmt, len);
++		return 1;
+ 	case WLAN_ACTION_VENDOR_SPECIFIC:
+ 		if (hapd->vendor_action_cb) {
+ 			if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx,
+diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
+index 0e13d2940..18f97890b 100644
+--- a/src/ap/ieee802_11.h
++++ b/src/ap/ieee802_11.h
+@@ -265,5 +265,7 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
+ 				  const u8 *ies, size_t ies_len,
+ 				  bool reassoc, int tx_link_status,
+ 				  bool offload);
+-
++void ieee802_11_rx_prot_eht(struct hostapd_data *hapd,
++			    const struct ieee80211_mgmt *mgmt,
++			    size_t len);
+ #endif /* IEEE802_11_H */
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index 2b5c06d6d..59ada2f86 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -465,9 +465,11 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 	control = MULTI_LINK_CONTROL_TYPE_BASIC |
+ 		BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
+ 		BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
+-		BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA |
+ 		BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA;
+ 
++	if (!hapd->iconf->eml_disable)
++		control |= BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA;
++
+ 	/*
+ 	 * Set the basic Multi-Link common information. Hard code the common
+ 	 * info length to 13 based on the length of the present fields:
+@@ -478,6 +480,9 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ #define EHT_ML_COMMON_INFO_LEN 13
+ 	common_info_len = EHT_ML_COMMON_INFO_LEN;
+ 
++	if (hapd->iconf->eml_disable)
++		common_info_len -= 2; /* EML Capabilities (2) */
++
+ 	if (include_mld_id) {
+ 		/* AP MLD ID */
+ 		control |= BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID;
+@@ -497,9 +502,11 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 	/* Currently hard code the BSS Parameters Change Count to 0x1 */
+ 	wpabuf_put_u8(buf, hapd->eht_mld_bss_param_change);
+ 
+-	wpa_printf(MSG_DEBUG, "MLD: EML Capabilities=0x%x",
+-		   hapd->iface->mld_eml_capa);
+-	wpabuf_put_le16(buf, hapd->iface->mld_eml_capa);
++	if (!hapd->iconf->eml_disable) {
++		wpa_printf(MSG_DEBUG, "MLD: EML Capabilities=0x%x",
++			   hapd->iface->mld_eml_capa);
++		wpabuf_put_le16(buf, hapd->iface->mld_eml_capa);
++	}
+ 
+ 	mld_cap = hapd->iface->mld_mld_capa;
+ 	max_simul_links = mld_cap & EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
+@@ -744,12 +751,16 @@ static u8 * hostapd_eid_eht_reconf_ml(struct hostapd_data *hapd, u8 *eid)
+ 
+ 
+ static size_t hostapd_eid_eht_ml_len(struct mld_info *info,
+-				     bool include_mld_id)
++				     bool include_mld_id,
++				     u8 eml_disable)
+ {
+ 	size_t len = 0;
+ 	size_t eht_ml_len = 2 + EHT_ML_COMMON_INFO_LEN;
+ 	u8 link_id;
+ 
++	if (eml_disable)
++		eht_ml_len -= 2; /* EML Capabilities (2) */
++
+ 	if (include_mld_id)
+ 		eht_ml_len++;
+ 
+@@ -811,7 +822,8 @@ size_t hostapd_eid_eht_ml_beacon_len(struct hostapd_data *hapd,
+ 				     struct mld_info *info,
+ 				     bool include_mld_id)
+ {
+-	return hostapd_eid_eht_ml_len(info, include_mld_id);
++	return hostapd_eid_eht_ml_len(info, include_mld_id,
++				      hapd->iconf->eml_disable);
+ }
+ 
+ 
+@@ -1406,3 +1418,103 @@ out:
+ 
+ 	return WLAN_STATUS_SUCCESS;
+ }
++
++static void ieee802_11_send_eml_omn(struct hostapd_data *hapd,
++				    const u8 *addr,
++				    struct eml_omn_element *omn_ie,
++				    size_t len)
++{
++	struct wpabuf *buf;
++
++	buf = wpabuf_alloc(2 + len);
++	if (!buf)
++		return;
++
++	wpabuf_put_u8(buf, WLAN_ACTION_PROTECTED_EHT);
++	wpabuf_put_u8(buf, WLAN_PROTECTED_EHT_ACTION_EML_OMN);
++	wpabuf_put_data(buf, omn_ie, len);
++
++	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
++				wpabuf_head(buf), wpabuf_len(buf));
++
++	wpabuf_free(buf);
++}
++
++static void ieee802_11_rx_eml_omn(struct hostapd_data *hapd,
++				  const u8 *addr, const u8 *frm,
++				  size_t len)
++{
++	struct eml_omn_element *omn_ie;
++
++	if (hapd->iconf->eml_disable) {
++		wpa_printf(MSG_ERROR,
++			   "Ignore EML Operating Mode Notification from "
++			   MACSTR
++			   " since EML Capabilities is disabled",
++			   MAC2STR(addr));
++		return;
++	}
++
++	/* EML Operating Mode Notification IE */
++	omn_ie = os_zalloc(sizeof(struct eml_omn_element));
++	if (omn_ie == NULL)
++		return;
++
++	os_memcpy(omn_ie, frm, len);
++
++	if (omn_ie->control & EHT_EML_OMN_CONTROL_EMLMR_MODE) {
++		wpa_printf(MSG_ERROR,
++			   "EML: Ignore EML Operating Mode Fotification from "
++			   MACSTR
++			   " since doesn't support EMLMR",
++			   MAC2STR(addr));
++		goto out;
++	}
++
++	hostapd_drv_set_eml_omn(hapd, addr, omn_ie);
++
++	omn_ie->control &= ~(EHT_EML_OMN_CONTROL_EMLSR_PARA_UPDATE_COUNT |
++			     EHT_EML_OMN_CONTROL_INDEV_COEX_ACTIVITIES);
++
++	if (hapd->iconf->eml_resp) {
++		ieee802_11_send_eml_omn(hapd, addr, omn_ie, len);
++		wpa_printf(MSG_ERROR, "EML: AP send EML Operating Mode Fotification to "
++				       MACSTR,
++				       MAC2STR(addr));
++	}
++out:
++	os_free(omn_ie);
++	return;
++}
++
++void ieee802_11_rx_prot_eht(struct hostapd_data *hapd,
++			    const struct ieee80211_mgmt *mgmt,
++			    size_t len)
++{
++	u8 action;
++	const u8 *payload;
++	size_t plen;
++
++	if (!hapd->conf->mld_ap)
++		return;
++
++	if (len < IEEE80211_HDRLEN + 2)
++		return;
++
++	payload = mgmt->u.action.u.eht_prot.variable;
++	action = mgmt->u.action.u.eht_prot.action;
++	plen = len - IEEE80211_HDRLEN - 2;
++
++	switch (action) {
++	case WLAN_PROTECTED_EHT_ACTION_EML_OMN:
++		ieee802_11_rx_eml_omn(hapd, mgmt->sa, payload, plen);
++		return;
++	}
++
++	wpa_printf(MSG_ERROR, "EHT: Unsupported Protected EHT Action %u from " MACSTR,
++		   action, MAC2STR(mgmt->sa));
++
++	return;
++
++}
++
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index 44d98d5e0..58e66f555 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -1901,7 +1901,7 @@ int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta)
+ 			    sta->supported_rates_len,
+ 			    0, NULL, NULL, NULL, 0, NULL, 0, NULL,
+ 			    sta->flags, 0, 0, 0, 0,
+-			    mld_link_addr, mld_link_sta)) {
++			    mld_link_addr, mld_link_sta, 0)) {
+ 		hostapd_logger(hapd, sta->addr,
+ 			       HOSTAPD_MODULE_IEEE80211,
+ 			       HOSTAPD_LEVEL_NOTICE,
+diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
+index efb584c66..fb481b8b2 100644
+--- a/src/common/ieee802_11_defs.h
++++ b/src/common/ieee802_11_defs.h
+@@ -775,6 +775,36 @@
+ #define WLAN_PROT_FTM 2
+ #define WLAN_PROT_FTM_REPORT 3
+ 
++/* Protected EHT action codes */
++#define WLAN_PROTECTED_EHT_ACTION_EML_OMN 6
++
++/* EML Operating Mode Notification frame */
++#define EHT_EML_OMN_CONTROL_EMLSR_MODE 0x1
++#define EHT_EML_OMN_CONTROL_EMLMR_MODE 0x2
++#define EHT_EML_OMN_CONTROL_EMLSR_PARA_UPDATE_COUNT 0x4
++#define EHT_EML_OMN_CONTROL_INDEV_COEX_ACTIVITIES 0x8
++
++/* EMLSR Parameter Update field */
++#define EHT_EML_OMN_EMLSR_PADDING_DELAY_MASK 0x07
++#define EHT_EML_OMN_EMLSR_TRANSITION_DELAY_MASK 0x38
++
++struct eml_omn_element {
++	u8 dialog_token;
++	u8 control;
++	le16 bitmap;
++	union {
++		struct {
++			u8 emlsr_para_update;
++		} STRUCT_PACKED emlsr_info;
++		struct {
++			u8 mcs_map_count_control;
++			u8 mcs_map_bw80[3];
++			u8 mcs_map_bw160[3];
++			u8 mcs_map_bw320[3];
++		} STRUCT_PACKED emlmr_info;
++	} u;
++} STRUCT_PACKED;
++
+ /* Radio Measurement capabilities (from RM Enabled Capabilities element)
+  * IEEE Std 802.11-2020, 9.4.2.44, Table 9-179 */
+ /* byte 1 (out of 5) */
+@@ -1161,6 +1191,10 @@ struct ieee80211_mgmt {
+ 					u8 dialog_token;
+ 					u8 variable[];
+ 				} STRUCT_PACKED rrm;
++				struct {
++					u8 action;
++					u8 variable[];
++				} STRUCT_PACKED eht_prot;
+ 			} u;
+ 		} STRUCT_PACKED action;
+ 	} u;
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index be516e017..c6de8862b 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -19,6 +19,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
+ 	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
+ 	MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
++	MTK_NL80211_VENDOR_SUBCMD_EML_CTRL = 0xd3,
+ };
+ 
+ enum mtk_vendor_attr_edcca_ctrl {
+@@ -287,6 +288,20 @@ enum mtk_vendor_attr_beacon_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_eml_ctrl {
++
++	MTK_VENDOR_ATTR_EML_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_EML_LINK_ID,
++	MTK_VENDOR_ATTR_EML_STA_ADDR,
++	MTK_VENDOR_ATTR_EML_CTRL_STRUCT,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_EML_CTRL,
++	MTK_VENDOR_ATTR_EML_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_EML_CTRL -1
++};
++
+ #define CSI_BW20_DATA_COUNT	64
+ #define CSI_BW40_DATA_COUNT	128
+ #define CSI_BW80_DATA_COUNT	256
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 924a1baba..e7e62c5ad 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -2575,6 +2575,7 @@ struct hostapd_sta_add_params {
+ 	bool mld_link_sta;
+ 	s8 mld_link_id;
+ 	const u8 *mld_link_addr;
++	u16 eml_capa;
+ };
+ 
+ struct mac_address {
+@@ -5277,6 +5278,17 @@ struct wpa_driver_ops {
+ 	 */
+ 	int (*beacon_ctrl)(void *priv, u8 beacon_mode);
+ 
++	/**
++	 * set eml omn - Send the EML Operating Mode
++	 * 		 Notification content to driver
++	 * @priv: Private driver interface data
++	 * @link_id: MLD link id
++	 * @addr: MLD STA address
++	 * @omn_ie: EML OMN content sent by the MLD STA
++	 */
++	int (*set_eml_omn)(void *priv, u8 link_id,
++			   u8 *addr, struct eml_omn_element *omn_ie);
++
+ 	/**
+ 	 * three_wire_ctrl - set three_wire_ctrl mode
+ 	 * @priv: Private driver interface data
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 555c97bf7..9451714a7 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -2893,6 +2893,9 @@ static int nl80211_action_subscribe_ap(struct i802_bss *bss)
+ 	if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0)
+ 		ret = -1;
+ #endif /* CONFIG_FST */
++	/* Protected EHT */
++	if (nl80211_register_action_frame(bss, (u8 *) "\x25", 1) < 0)
++		ret = -1;
+ 	/* Vendor-specific */
+ 	if (nl80211_register_action_frame(bss, (u8 *) "\x7f", 1) < 0)
+ 		ret = -1;
+@@ -5981,6 +5984,14 @@ static int wpa_driver_nl80211_sta_add(void *priv,
+ 			goto fail;
+ 	}
+ 
++	if (params->eml_capa) {
++		wpa_printf(MSG_DEBUG, "  * eml_capa=%u",
++			   params->eml_capa);
++		if (nla_put_u16(msg, NL80211_ATTR_EML_CAPABILITY,
++				params->eml_capa))
++			goto fail;
++	}
++
+ 	ret = send_and_recv_cmd(drv, msg);
+ 	msg = NULL;
+ 	if (ret)
+@@ -15261,6 +15272,47 @@ static int nl80211_get_mld_addr(void *priv, u8 *addr)
+ 
+ 	return 0;
+ }
++
++static int nl80211_set_eml_omn(void *priv, u8 link_id, u8 *addr,
++			       struct eml_omn_element *omn_ie)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret = -ENOBUFS;
++
++	if (!drv->mtk_eml_vendor_cmd_avail) {
++		wpa_printf(MSG_ERROR,
++			   "nl80211: Driver does not support setting EML control");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_EML_CTRL) ||
++	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EML_LINK_ID, link_id) ||
++	    nla_put(msg, MTK_VENDOR_ATTR_EML_STA_ADDR, ETH_ALEN, addr) ||
++	    nla_put(msg, MTK_VENDOR_ATTR_EML_CTRL_STRUCT,
++		    sizeof(struct eml_omn_element), omn_ie))
++		goto fail;
++
++
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set EML OMN ctrl. ret = %d (%s)",
++			   ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return ret;
++}
+ #endif
+ 
+ static int
+@@ -15619,6 +15671,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.mu_ctrl = nl80211_mu_ctrl,
+ 	.mu_dump = nl80211_mu_dump,
+ 	.beacon_ctrl = nl80211_beacon_ctrl,
++	.set_eml_omn = nl80211_set_eml_omn,
+ #ifdef CONFIG_DPP
+ 	.dpp_listen = nl80211_dpp_listen,
+ #endif /* CONFIG_DPP */
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index b710b50cd..2cc40e0dc 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -213,6 +213,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_pp_vendor_cmd_avail:1;
+ 	unsigned int mtk_beacon_ctrl_vendor_cmd_avail:1;
+ 	unsigned int mtk_csi_vendor_cmd_avail:1;
++	unsigned int mtk_eml_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 5a53700d1..c1327a679 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1176,6 +1176,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL:
+ 					drv->mtk_csi_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_EML_CTRL:
++					drv->mtk_eml_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0087-mtk-hostapd-Fix-Operating-Mode-Notification-issue-in.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0087-mtk-hostapd-Fix-Operating-Mode-Notification-issue-in.patch
new file mode 100644
index 0000000..e637185
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0087-mtk-hostapd-Fix-Operating-Mode-Notification-issue-in.patch
@@ -0,0 +1,36 @@
+From 9b059de49d2c441ec3b83c82de3c4c6e0078f4f7 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+Date: Wed, 15 May 2024 15:23:55 +0800
+Subject: [PATCH 087/126] mtk: hostapd: Fix Operating Mode Notification issue
+ in 2GHz
+
+If this patch is not applied, since the driver enables the
+Operating Mode Notification feature, hostapd will enable the OMN bit
+in the Extended Capabilities Element when constructing beacon.
+However, this is not allowed on frequency bands that do not support
+ieee80211ac, hence add this patch to fix this issue.
+
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ src/ap/ieee802_11_shared.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
+index 3dd3a6a77..58283eae9 100644
+--- a/src/ap/ieee802_11_shared.c
++++ b/src/ap/ieee802_11_shared.c
+@@ -510,6 +510,11 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid,
+ 			*pos &= ~0x08;
+ 		if (i == 2 && !hapd->iconf->mbssid)
+ 			*pos &= ~0x40;
++
++		/* Clear bits 62 (Operating Mode Notification)
++		 * if ieee80211ac is not enabled (mainly 2.4G and 6G) */
++		if (i == 7 && !hapd->iconf->ieee80211ac)
++			*pos &= ~0x40;
+ 	}
+ 
+ 	while (len > 0 && eid[1 + len] == 0) {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0087-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0087-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch
deleted file mode 100644
index ac847f5..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0087-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch
+++ /dev/null
@@ -1,102 +0,0 @@
-From d6e854c62cd825756cc1b46c8b006855cf9e057e Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Wed, 6 Mar 2024 15:01:33 +0800
-Subject: [PATCH 087/104] mtk: wpa_supplicant: add 'mld_allowed_phy'
- configuration option for MLD STA
-
-A new configuration option named 'mld_allowed_phy' is added for MLD STA.
-This option indicates the bitmap of allowed phy for MLO connection.
-Note that setting 'mld_allowed_phy' to 0 makes no phy allowed for MLO.
-In other word, the STA becomes a legacy STA.
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- wpa_supplicant/config.c      |  1 +
- wpa_supplicant/config.h      |  1 +
- wpa_supplicant/config_file.c |  2 ++
- wpa_supplicant/sme.c         | 18 ++++++++++++++++++
- 4 files changed, 22 insertions(+)
-
-diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
-index 7bb57e2ab..d3c75ee94 100644
---- a/wpa_supplicant/config.c
-+++ b/wpa_supplicant/config.c
-@@ -5680,6 +5680,7 @@ static const struct global_parse_data global_fields[] = {
- #endif /* CONFIG_PASN */
- #ifdef CONFIG_TESTING_OPTIONS
- 	{ INT_RANGE(mld_force_single_link, 0, 1), 0 },
-+	{ INT_RANGE(mld_allowed_phy, 0, 7), 0 },
- 	{ INT_RANGE(mld_connect_band_pref, 0, MLD_CONNECT_BAND_PREF_MAX), 0 },
- 	{ FUNC(mld_connect_bssid_pref), 0 },
- #endif /* CONFIG_TESTING_OPTIONS */
-diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
-index 8981305c2..c0164fa76 100644
---- a/wpa_supplicant/config.h
-+++ b/wpa_supplicant/config.h
-@@ -1800,6 +1800,7 @@ struct wpa_config {
- 	u8 mld_connect_bssid_pref[ETH_ALEN];
- 
- 	int mld_force_single_link;
-+	u8 mld_allowed_phy; /* bitmap of allowed phy for MLO connection */
- #endif /* CONFIG_TESTING_OPTIONS */
- };
- 
-diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
-index 7a3ed6373..875d00bb4 100644
---- a/wpa_supplicant/config_file.c
-+++ b/wpa_supplicant/config_file.c
-@@ -1622,6 +1622,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
- #ifdef CONFIG_TESTING_OPTIONS
- 	if (config->mld_force_single_link)
- 		fprintf(f, "mld_force_single_link=1\n");
-+	if (config->mld_allowed_phy)
-+		fprintf(f, "mld_allowed_phy=%u\n", config->mld_allowed_phy);
- 	if (config->mld_connect_band_pref != MLD_CONNECT_BAND_PREF_AUTO)
- 		fprintf(f, "mld_connect_band_pref=%d\n",
- 			config->mld_connect_band_pref);
-diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
-index 5b69812b5..ef258fadc 100644
---- a/wpa_supplicant/sme.c
-+++ b/wpa_supplicant/sme.c
-@@ -517,6 +517,16 @@ out:
- }
- 
- 
-+#ifdef CONFIG_TESTING_OPTIONS
-+static bool check_mld_allowed_phy(struct wpa_supplicant *wpa_s, int freq)
-+{
-+	return ((wpa_s->conf->mld_allowed_phy & BIT(0)) && IS_2P4GHZ(freq)) ||
-+	       ((wpa_s->conf->mld_allowed_phy & BIT(1)) && IS_5GHZ(freq)) ||
-+	       ((wpa_s->conf->mld_allowed_phy & BIT(2)) && is_6ghz_freq(freq));
-+}
-+#endif /* CONFIG_TESTING_OPTIONS */
-+
-+
- static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
- 				   struct wpa_bss *bss)
- {
-@@ -528,6 +538,11 @@ static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
- 	for_each_link(bss->valid_links, i) {
- 		const u8 *bssid = bss->mld_links[i].bssid;
- 
-+#ifdef CONFIG_TESTING_OPTIONS
-+		if (!check_mld_allowed_phy(wpa_s, bss->mld_links[i].freq))
-+			continue;
-+#endif /* CONFIG_TESTING_OPTIONS */
-+
- 		wpa_s->valid_links |= BIT(i);
- 		os_memcpy(wpa_s->links[i].bssid, bssid, ETH_ALEN);
- 		wpa_s->links[i].freq = bss->mld_links[i].freq;
-@@ -577,6 +592,9 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
- 	if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) &&
- 	    !wpa_bss_parse_basic_ml_element(wpa_s, bss, wpa_s->ap_mld_addr,
- 					    NULL, ssid, NULL) &&
-+#ifdef CONFIG_TESTING_OPTIONS
-+	    wpa_s->conf->mld_allowed_phy &&
-+#endif /* CONFIG_TESTING_OPTIONS */
- 	    bss->valid_links) {
- 		wpa_printf(MSG_DEBUG, "MLD: In authentication");
- 		wpas_sme_set_mlo_links(wpa_s, bss);
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0088-mtk-hostapd-add-mlo-probe-client-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0088-mtk-hostapd-add-mlo-probe-client-support.patch
new file mode 100644
index 0000000..8cf4e0a
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0088-mtk-hostapd-add-mlo-probe-client-support.patch
@@ -0,0 +1,76 @@
+From 2b760a3738dbc5d0c9fa71a1f73bba2265453f46 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 10 May 2024 17:52:41 +0800
+Subject: [PATCH 088/126] mtk: hostapd: add mlo probe client support
+
+Add mld-level probe client support
+Only register one eloop ap_handle_timeout per mld
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 12 ++++++++++++
+ src/ap/sta_info.c    | 18 +++++++++++++++++-
+ 2 files changed, 29 insertions(+), 1 deletion(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index d9775e13e..2038a3712 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -1469,6 +1469,18 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
+ 							hapd->conf->transition_disable);
+ 		}
+ 
++#ifdef CONFIG_IEEE80211BE
++		/* workaround before hostapd cli support per link configuration */
++		if (hapd->conf->mld_ap) {
++			struct hostapd_data *h;
++
++			for_each_mld_link(h, hapd) {
++				if (os_strcasecmp(cmd, "ap_max_inactivity") == 0)
++					h->conf->ap_max_inactivity = hapd->conf->ap_max_inactivity;
++			}
++		}
++#endif /* CONFIG_IEEE80211BE */
++
+ #ifdef CONFIG_TESTING_OPTIONS
+ 		if (os_strcmp(cmd, "ft_rsnxe_used") == 0)
+ 			wpa_auth_set_ft_rsnxe_used(hapd->wpa_auth,
+diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
+index 58e66f555..bc729137d 100644
+--- a/src/ap/sta_info.c
++++ b/src/ap/sta_info.c
+@@ -819,6 +819,7 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
+ 	struct sta_info *sta;
+ 	int i;
+ 	int max_inactivity = hapd->conf->ap_max_inactivity;
++	bool registered = false;
+ 
+ 	sta = ap_get_sta(hapd, addr);
+ 	if (sta)
+@@ -855,7 +856,22 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
+ 	if (sta->max_idle_period)
+ 		max_inactivity = (sta->max_idle_period * 1024 + 999) / 1000;
+ 
+-	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
++#ifdef CONFIG_IEEE80211BE
++	if (hapd->conf->mld_ap) {
++		struct hostapd_data *h;
++		struct sta_info *s;
++
++		for_each_mld_link(h, hapd) {
++			s = ap_get_sta(h, addr);
++			if (s && eloop_is_timeout_registered(ap_handle_timer, h, s)) {
++				registered = true;
++				break;
++			}
++		}
++	}
++#endif /* CONFIG_IEEE80211BE */
++
++	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER) && !registered) {
+ 		wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+ 			   "for " MACSTR " (%d seconds - ap_max_inactivity)",
+ 			   __func__, MAC2STR(addr),
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0088-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0088-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch
deleted file mode 100644
index 1ba9e75..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0088-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch
+++ /dev/null
@@ -1,284 +0,0 @@
-From a5b5d12eac0a1a4c53de67adc5793dbee87cb769 Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Thu, 29 Feb 2024 19:55:34 +0800
-Subject: [PATCH 088/104] mtk: hostapd: support band_idx option for
- set_mu/get_mu vendor command
-
-Support band_idx for set_mu and get_mu vendor command. The usage shows
-as below:
-1. get_mu: $ hostapd_cli -i <intf> get_mu <band_idx>
-2. set_mu: $ hostapd_cli -i <intf> set_mu <mu_onff>:<band_idx>
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- hostapd/config_file.c        |  9 +++++
- hostapd/ctrl_iface.c         | 78 ++++++++++++++++++++++++++++--------
- hostapd/hostapd_cli.c        |  2 +-
- src/ap/ap_config.h           |  1 +
- src/ap/ap_drv_ops.c          |  2 +-
- src/common/mtk_vendor.h      |  1 +
- src/drivers/driver.h         |  2 +-
- src/drivers/driver_nl80211.c | 15 +++----
- 8 files changed, 82 insertions(+), 28 deletions(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index e9caa45f3..ef9bafb28 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5431,6 +5431,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 			return 1;
- 		}
- 		conf->pp_mode = (u8) val;
-+	} else if (os_strcmp(buf, "band_idx") == 0) {
-+		int val = atoi(pos);
-+
-+		if (val < 0) {
-+			wpa_printf(MSG_ERROR, "Line %d: invalid band_idx value",
-+				   line);
-+			return 1;
-+		}
-+		conf->band_idx = (u8) val;
- 	} else {
- 		wpa_printf(MSG_ERROR,
- 			   "Line %d: unknown configuration item '%s'",
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 0fded7ed4..c5540f5fd 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4178,17 +4178,42 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
- 	value = pos;
- 
- 	if (os_strcmp(config, "onoff") == 0) {
--		int mu = atoi(value);
--		if (mu < 0 || mu > 15) {
--			wpa_printf(MSG_ERROR, "Invalid value for mu");
--			return -1;
-+		cnt = hostapd_parse_argument_helper(value, &val);
-+		if (cnt == -1)
-+			goto fail;
-+		if (cnt < 1 || val[0] > 15)
-+			goto para_fail;
-+
-+		if (hostapd_is_mld_ap(hapd)) {
-+			u8 band_idx;
-+
-+			if (cnt != 2)
-+				goto para_fail;
-+
-+			band_idx = val[1];
-+
-+			for (i = 0; i < hapd->iface->interfaces->count; i++) {
-+				struct hostapd_iface *iface;
-+
-+				iface = hapd->iface->interfaces->iface[i];
-+				if (!iface || !iface->conf)
-+					continue;
-+
-+				if (iface->conf->band_idx == band_idx) {
-+					hapd = iface->bss[0];
-+					break;
-+				}
-+			}
-+			if (hapd->iface->conf->band_idx != band_idx)
-+				goto para_fail;
- 		}
--		hapd->iconf->mu_onoff = (u8) mu;
- 
--		if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) == 0)
--			return os_snprintf(buf, buflen, "OK\n");
--		else
-+		hapd->iconf->mu_onoff = val[0];
-+		os_free(val);
-+		if (hostapd_drv_mu_ctrl(hapd, MU_CTRL_ONOFF) != 0)
- 			goto fail;
-+
-+		return os_snprintf(buf, buflen, "OK\n");
- 	}
- 
- 	if (hapd->iconf->muru_config == NULL)
-@@ -4200,6 +4225,7 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
- 	comm = &muru->comm;
- 
- 	if (os_strncmp(config, "update", 6) == 0) {
-+		// [ToDo] "update" needs to support band_idx argument
- 		ret = hostapd_drv_mu_ctrl(hapd, MU_CTRL_UPDATE);
- 
- 		os_free(hapd->iconf->muru_config);
-@@ -4342,15 +4368,14 @@ hostapd_ctrl_iface_set_mu(struct hostapd_data *hapd, char *cmd,
- 
- para_fail:
- 	os_free(val);
--	wpa_printf(MSG_ERROR, "Incorrect input number\n");
-+	wpa_printf(MSG_ERROR, "Input number or value is incorrect\n");
- fail:
- 	return os_snprintf(buf, buflen, "FAIL\n");
- }
- 
--
- static int
--hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
--					 size_t buflen)
-+hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *input, char *buf,
-+			  size_t buflen)
- {
- 	u8 mu_onoff;
- 	char *pos, *end;
-@@ -4358,14 +4383,35 @@ hostapd_ctrl_iface_get_mu(struct hostapd_data *hapd, char *buf,
- 	pos = buf;
- 	end = buf + buflen;
- 
-+	if (hostapd_is_mld_ap(hapd)) {
-+		u8 band_idx, i;
-+
-+		band_idx = (u8)atoi(input);
-+
-+		for (i = 0; i < hapd->iface->interfaces->count; i++) {
-+			struct hostapd_iface *iface;
-+
-+			iface = hapd->iface->interfaces->iface[i];
-+			if (!iface || !iface->conf)
-+				continue;
-+
-+			if (iface->conf->band_idx == band_idx) {
-+				hapd = iface->bss[0];
-+				break;
-+			}
-+		}
-+		if (hapd->iface->conf->band_idx != band_idx)
-+			return os_snprintf(pos, end - pos, "Invalid band idx to get_mu\n");
-+	}
-+
- 	if (hapd->iface->state != HAPD_IFACE_ENABLED)
- 		return os_snprintf(pos, end - pos, "Not allowed to get_mu when current state is %s\n",
- 				   hostapd_state_text(hapd->iface->state));
- 
- 	if (hostapd_drv_mu_dump(hapd, &mu_onoff) == 0) {
- 		hapd->iconf->mu_onoff = mu_onoff;
--		return os_snprintf(pos, end - pos, "[hostapd_cli] = UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
--			!!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
-+		return os_snprintf(pos, end - pos, "Band idx %u: UL MU-MIMO: %d, DL MU-MIMO: %d, UL OFDMA: %d, DL OFDMA: %d\n",
-+			hapd->iconf->band_idx, !!(mu_onoff&BIT(3)), !!(mu_onoff&BIT(2)), !!(mu_onoff&BIT(1)), !!(mu_onoff&BIT(0)));
- 	} else {
- 		wpa_printf(MSG_INFO, "ctrl iface failed to call");
- 		return -1;
-@@ -5488,8 +5534,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 							  reply_size);
- 	} else if (os_strncmp(buf, "SET_MU ", 7) == 0) {
- 		reply_len = hostapd_ctrl_iface_set_mu(hapd, buf + 7, reply, reply_size);
--	} else if (os_strncmp(buf, "GET_MU", 6) == 0) {
--		reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
-+	} else if (os_strncmp(buf, "GET_MU ", 7) == 0) {
-+		reply_len = hostapd_ctrl_iface_get_mu(hapd, buf + 7, reply, reply_size);
- 	} else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
- 		reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
- 	} else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 7e4485cb8..100896c34 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1461,7 +1461,7 @@ static int hostapd_cli_cmd_set_mu(struct wpa_ctrl *ctrl, int argc,
- static int hostapd_cli_cmd_get_mu(struct wpa_ctrl *ctrl, int argc,
- 					   char *argv[])
- {
--	return hostapd_cli_cmd(ctrl, "GET_MU", 0, NULL, NULL);
-+	return hostapd_cli_cmd(ctrl, "GET_MU", 0, argc, argv);
- }
- 
- static int hostapd_cli_cmd_disable_beacon(struct wpa_ctrl *ctrl, int argc,
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 1f686550e..3bd8df9ce 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1298,6 +1298,7 @@ struct hostapd_config {
- 	u8 amsdu;
- 	void *muru_config;
- 	u8 pp_mode;
-+	u8 band_idx;
- };
- 
- enum three_wire_mode {
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index b7896c110..ac7ef00cd 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1281,7 +1281,7 @@ int hostapd_drv_mu_dump(struct hostapd_data *hapd, u8 *mu_onoff)
- {
- 	if (!hapd->driver || !hapd->driver->mu_dump)
- 		return 0;
--	return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff);
-+	return hapd->driver->mu_dump(hapd->drv_priv, mu_onoff, hapd->iconf->band_idx);
- }
- 
- int hostapd_drv_three_wire_ctrl(struct hostapd_data *hapd)
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 5531802b8..1e4c3670e 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -207,6 +207,7 @@ enum mtk_vendor_attr_mu_ctrl {
- 	 * above data structure.
- 	 */
- 	MTK_VENDOR_ATTR_MU_CTRL_STRUCT,
-+	MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX,
- 
- 	/* keep last */
- 	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 2940650df..10ae48729 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5241,7 +5241,7 @@ struct wpa_driver_ops {
- 	 *
- 	 */
- 	 int (*mu_ctrl)(void *priv, u8 mode, void *config);
--	 int (*mu_dump)(void *priv, u8 *mu_onoff);
-+	 int (*mu_dump)(void *priv, u8 *mu_onoff, u8 band_idx);
- 
- 	/**
- 	 * beacon_ctrl - ctrl on off for beacon
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 6d300c0c8..17aaa16a8 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -14010,7 +14010,8 @@ static int nl80211_mu_ctrl(void *priv, u8 mode, void *config)
- 
- 	switch (mode) {
- 	case MU_CTRL_ONOFF:
--		if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff))
-+		if (nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_ONOFF, cfg->mu_onoff) ||
-+		    nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX, cfg->band_idx))
- 			goto fail;
- 		break;
- 	case MU_CTRL_UPDATE:
-@@ -14074,7 +14075,7 @@ static int mu_dump_handler(struct nl_msg *msg, void *arg)
- 	return 0;
- }
- 
--static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
-+static int nl80211_mu_dump(void *priv, u8 *mu_onoff, u8 band_idx)
- {
- 	struct i802_bss *bss = priv;
- 	struct wpa_driver_nl80211_data *drv = bss->drv;
-@@ -14090,17 +14091,13 @@ static int nl80211_mu_dump(void *priv, u8 *mu_onoff)
- 
- 	if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
- 		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
--		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL)) {
-+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_MU_CTRL) ||
-+		!(attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-+		nla_put_u8(msg, MTK_VENDOR_ATTR_MU_CTRL_BAND_IDX, band_idx)) {
- 		nlmsg_free(msg);
- 		return -ENOBUFS;
- 	}
- 
--  attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
--	if (!attr) {
--		nlmsg_free(msg);
--		return -1;
--	}
--
- 	nla_nest_end(msg, attr);
- 
- 	ret = send_and_recv_resp(drv, msg, mu_dump_handler, mu_onoff);
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0089-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0089-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch
deleted file mode 100644
index cbf05f5..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0089-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch
+++ /dev/null
@@ -1,131 +0,0 @@
-From a0cbcd04400458bf7f4f4086beecbf8db6800c36 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Fri, 19 Jan 2024 14:11:05 +0800
-Subject: [PATCH 089/104] mtk: hostapd: Handle DFS radar detection in MLO
-
-To handle DFS CAC in MLO, we add the following changes:
-1. Add link id info to radar detect cmd for the kernel to use the correct link.
-2. Block RNR IE for disabled iface. (the EID len would be wrong without it)
-3. Only flush the old stations for the first BSS; otherwise, after DFS CAC
-stations would be flushed again.
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-
-Add background radar handling
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- src/ap/ap_drv_ops.c                |  9 +++++++++
- src/ap/hostapd.c                   |  3 +++
- src/ap/ieee802_11.c                |  3 +++
- src/drivers/driver_nl80211.c       | 18 ++++++++++++++++++
- src/drivers/driver_nl80211.h       |  1 +
- src/drivers/driver_nl80211_event.c |  3 ++-
- 6 files changed, 36 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index ac7ef00cd..9357ce7b6 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1012,6 +1012,15 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
- 		return -1;
- 	}
- 	data.radar_background = radar_background;
-+	data.link_id = -1;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	if (hapd->conf->mld_ap) {
-+		data.link_id = hapd->mld_link_id;
-+		wpa_printf(MSG_DEBUG,
-+			   "hostapd_start_dfs_cac: link_id=%d", data.link_id);
-+	}
-+#endif /* CONFIG_IEEE80211BE */
- 
- 	res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
- 	if (!res) {
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index f8b05de45..8e3f0b281 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -1371,6 +1371,9 @@ int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
- 	if (!hostapd_mld_is_first_bss(hapd))
- 		wpa_printf(MSG_DEBUG,
- 			   "MLD: %s: Setting non-first BSS", __func__);
-+	else
-+		/* Only flush old stations when setting up first BSS for MLD. */
-+		flush_old_stations = 0;
- 
- 	wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)",
- 		   __func__, hapd, conf->iface, first);
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 0f357d786..fe0a5bce4 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -7852,6 +7852,9 @@ u8 *hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
- 		    !is_6ghz_op_class(iface->conf->op_class))
- 			continue;
- 
-+		if (!iface->bss[0]->started)
-+			continue;
-+
- 		eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
- 					    current_len, NULL, false);
- 	}
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 17aaa16a8..ad73b4ac1 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -10589,6 +10589,24 @@ static int nl80211_start_radar_detection(void *priv,
- 		return -1;
- 	}
- 
-+	if (freq->link_id != NL80211_DRV_LINK_ID_NA) {
-+		wpa_printf(MSG_DEBUG, "nl80211: Set link_id=%u for radar detect",
-+			   freq->link_id);
-+
-+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, freq->link_id)) {
-+			nlmsg_free(msg);
-+			return -ENOBUFS;
-+		}
-+
-+		if (freq->radar_background) {
-+			struct i802_link *link = nl80211_get_link(bss, freq->link_id);
-+
-+			link->background_freq = freq->freq;
-+		} else {
-+			nl80211_link_set_freq(bss, freq->link_id, freq->freq);
-+		}
-+	}
-+
- 	ret = send_and_recv_cmd(drv, msg);
- 	if (ret == 0)
- 		return 0;
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index 9866c221c..a0a62e536 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -56,6 +56,7 @@ struct i802_link {
- 	unsigned int beacon_set:1;
- 
- 	int freq;
-+	int background_freq;
- 	int bandwidth;
- 	u8 addr[ETH_ALEN];
- 	void *ctx;
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 90084356d..03bad4bb3 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -1631,7 +1631,8 @@ nl80211_get_link_id_by_freq(struct i802_bss *bss, unsigned int freq)
- 	unsigned int i;
- 
- 	for_each_link(bss->valid_links, i) {
--		if ((unsigned int) bss->links[i].freq == freq)
-+		if ((unsigned int) bss->links[i].freq == freq ||
-+		    (unsigned int) bss->links[i].background_freq == freq)
- 			return i;
- 	}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0089-mtk-hostapd-add-support-for-channel-switch.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0089-mtk-hostapd-add-support-for-channel-switch.patch
new file mode 100644
index 0000000..209c997
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0089-mtk-hostapd-add-support-for-channel-switch.patch
@@ -0,0 +1,134 @@
+From 6a5460e02c6d6303146a43ffd3ee10ae5e1fb2a1 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Thu, 16 May 2024 15:59:30 +0800
+Subject: [PATCH 089/126] mtk: hostapd: add support for channel switch
+
+For extender channel switch, a 'band_idx' is necessary so that
+Extender STA can tell Extender AP exactly which band is doing
+a channel switch.
+
+The original flow fails on parsing a 0 band_idx. Updated flow can
+avoid the problem and also a default 'band_idx' is assigned for an
+error parsing.
+
+Another error handling is also added for the case that the iface with
+target 'band_idx' is not found.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ucode.c         | 20 +++++++++++++++++++-
+ wpa_supplicant/ucode.c | 31 ++++++++++++++++++++++++++++---
+ 2 files changed, 47 insertions(+), 4 deletions(-)
+
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index a72193282..a69caa6cf 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -693,7 +693,7 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 	struct hostapd_config *conf;
+ 	struct csa_settings csa = {};
+ 	uint64_t intval;
+-	int i, ret = 0;
++	int i, ret = 0, band_idx;
+ 
+ 	wpa_printf(MSG_INFO, "ucode: mtk: channel switch for %s\n", iface->phy);
+ 	if (!iface || ucv_type(info) != UC_OBJECT)
+@@ -727,6 +727,9 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 	if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
+ 		csa.freq_params.center_freq2 = intval;
+ 
++	intval = ucv_int64_get(ucv_object_get(info, "band_idx", NULL));
++	band_idx = errno ? iface->conf->band_idx : intval;
++
+ 	wpa_printf(MSG_INFO, "ucode: mtk: switch channel information:\n");
+ 	wpa_printf(MSG_INFO, "    * freq is %d\n", csa.freq_params.freq);
+ 	wpa_printf(MSG_INFO, "    * bandwidth is %d\n",
+@@ -737,6 +740,21 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 			csa.freq_params.center_freq1);
+ 	wpa_printf(MSG_INFO, "    * center_freq2 is %d\n",
+ 			csa.freq_params.center_freq2);
++	wpa_printf(MSG_INFO, "    * band_idx is %d\n",
++			band_idx);
++
++#ifdef CONFIG_IEEE80211BE
++	for (i = 0; i < iface->interfaces->count; i++) {
++		if (band_idx == iface->interfaces->iface[i]->conf->band_idx) {
++			iface = iface->interfaces->iface[i];
++			wpa_printf(MSG_INFO, "ucode: mtk: MLD: switch to iface with band_idx %d \n", band_idx);
++			break;
++		}
++	}
++
++	if (band_idx != iface->bss[0]->iconf->band_idx)
++		return NULL;
++#endif /* CONFIG_IEEE80211BE */
+ 
+ 	for (i = 0; i < iface->num_bss; i++) {
+ 		ret = hostapd_switch_channel(iface->bss[i], &csa);
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+index ac0639a90..050bed6ae 100644
+--- a/wpa_supplicant/ucode.c
++++ b/wpa_supplicant/ucode.c
+@@ -99,9 +99,10 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ 	const char *state;
+ 	uc_value_t *val;
+ 	enum oper_chan_width ch_width;
+-	int center_freq1, bw320_offset = 1;
++	int control_freq, center_freq1, bw320_offset = 1, band_idx;
+ 
+-	if (event != EVENT_CH_SWITCH_STARTED)
++	if (event != EVENT_CH_SWITCH_STARTED &&
++	    event != EVENT_LINK_CH_SWITCH_STARTED)
+ 		return;
+ 
+ 	val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
+@@ -117,7 +118,13 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ 	val = ucv_object_new(vm);
+ 	uc_value_push(ucv_get(val));
+ 
+-	if (event == EVENT_CH_SWITCH_STARTED) {
++	if (event == EVENT_CH_SWITCH_STARTED ||
++	    event == EVENT_LINK_CH_SWITCH_STARTED) {
++		enum hostapd_hw_mode hw_mode;
++		int is_24ghz;
++		u8 channel;
++
++		control_freq = data->ch_switch.freq;
+ 		center_freq1 = data->ch_switch.cf1;
+ 
+ 		switch (data->ch_switch.ch_width) {
+@@ -147,6 +154,23 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ 		     center_freq1 == 6905)
+ 			bw320_offset = 2;
+ 
++		hw_mode = ieee80211_freq_to_chan(control_freq, &channel);
++		is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
++			hw_mode == HOSTAPD_MODE_IEEE80211B;
++		/*
++		 * Assume that the mapping between band and band_idx is
++		 * 2 GHz band: band_idx 0
++		 * 5 GHz band: band_idx 1
++		 * 6 GHz band: band_idx 2
++		 * */
++		if (is_24ghz)
++			band_idx = 0;
++		else if (IS_5GHZ(control_freq))
++			band_idx = 1;
++		else if (is_6ghz_freq(control_freq))
++			band_idx = 2;
++
++
+ 		ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count));
+ 		ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq));
+ 		ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
+@@ -154,6 +178,7 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ 		ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
+ 		ucv_object_add(val, "ch_width", ucv_int64_new(ch_width));
+ 		ucv_object_add(val, "bw320_offset", ucv_int64_new(bw320_offset));
++		ucv_object_add(val, "band_idx", ucv_int64_new(band_idx));
+ 	}
+ 
+ 	ucv_put(wpa_ucode_call(4));
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0090-mtk-hostapd-add-link-id-to-hostapd-cli-chan-switch.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0090-mtk-hostapd-add-link-id-to-hostapd-cli-chan-switch.patch
deleted file mode 100644
index 129f54b..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0090-mtk-hostapd-add-link-id-to-hostapd-cli-chan-switch.patch
+++ /dev/null
@@ -1,59 +0,0 @@
-From d71c29484010bcb0bda82eb529689d0748bd653e Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 31 Jan 2024 10:39:08 +0800
-Subject: [PATCH 090/104] mtk: hostapd: add link id to hostapd cli chan switch
-
-temporary workaround for mlo channel switch
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- hostapd/ctrl_iface.c   | 3 +--
- hostapd/hostapd_cli.c  | 2 +-
- src/ap/ctrl_iface_ap.c | 2 ++
- 3 files changed, 4 insertions(+), 3 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index c5540f5fd..f0c990314 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -2801,10 +2801,9 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
- 	if (ret)
- 		return ret;
- 
--	settings.link_id = -1;
- #ifdef CONFIG_IEEE80211BE
- 	if (iface->num_bss && iface->bss[0]->conf->mld_ap)
--		settings.link_id = iface->bss[0]->mld_link_id;
-+		iface = iface->interfaces->iface[settings.link_id];
- #endif /* CONFIG_IEEE80211BE */
- 
- 	ret = hostapd_ctrl_check_freq_params(&settings.freq_params,
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 100896c34..acfa3b1d1 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1195,7 +1195,7 @@ static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
- 		printf("Invalid chan_switch command: needs at least two "
- 		       "arguments (count and freq)\n"
- 		       "usage: <cs_count> <freq> [sec_channel_offset=] "
--		       "[center_freq1=] [center_freq2=] [bandwidth=] "
-+		       "[center_freq1=] [center_freq2=] [bandwidth=] [link_id=] "
- 		       "[blocktx] [ht|vht|he|eht]\n");
- 		return -1;
- 	}
-diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index b92311e32..ca4e3e7a4 100644
---- a/src/ap/ctrl_iface_ap.c
-+++ b/src/ap/ctrl_iface_ap.c
-@@ -1108,6 +1108,8 @@ int hostapd_parse_csa_settings(const char *pos,
- 		} \
- 	} while (0)
- 
-+	SET_CSA_SETTING(link_id);
-+	settings->link_id = settings->freq_params.link_id;
- 	SET_CSA_SETTING(center_freq1);
- 	SET_CSA_SETTING(center_freq2);
- 	SET_CSA_SETTING(bandwidth);
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0090-mtk-hostapd-extend-MLO-information-getting.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0090-mtk-hostapd-extend-MLO-information-getting.patch
new file mode 100644
index 0000000..a73671f
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0090-mtk-hostapd-extend-MLO-information-getting.patch
@@ -0,0 +1,105 @@
+From e3acb288c0e30eef9b734b62045f505c1392aab3 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 17 May 2024 16:36:19 +0800
+Subject: [PATCH 090/126] mtk: hostapd: extend MLO information getting
+
+Extend MLO information getting, including center frequency & bandwidth,
+from the driver. These informations are helpful for Extender STA to
+synchronize channel informations to Extender AP.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/drivers/driver.h              |  3 ++-
+ src/drivers/driver_nl80211.c      | 24 ++++++++++++++++++++++--
+ wpa_supplicant/events.c           |  3 +++
+ wpa_supplicant/wpa_supplicant_i.h |  3 ++-
+ 4 files changed, 29 insertions(+), 4 deletions(-)
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index e7e62c5ad..eed99dac8 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -3058,8 +3058,9 @@ struct driver_sta_mlo_info {
+ 	struct {
+ 		u8 addr[ETH_ALEN];
+ 		u8 bssid[ETH_ALEN];
+-		unsigned int freq;
++		unsigned int freq, center_freq1, center_freq2;
+ 		struct t2lm_mapping t2lmap;
++		enum chan_width width;
+ 	} links[MAX_NUM_MLD_LINKS];
+ };
+ 
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 9451714a7..b3ae50d15 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -1171,6 +1171,27 @@ static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid)
+ }
+ 
+ 
++static void get_link_channel_info(struct nlattr **link_data, u8 link_id,
++				  struct driver_sta_mlo_info *info)
++{
++	info->links[link_id].freq =
++		nla_get_u32(link_data[NL80211_ATTR_WIPHY_FREQ]);
++
++	if (link_data[NL80211_ATTR_CHANNEL_WIDTH]) {
++		info->links[link_id].width =
++	       convert2width(nla_get_u32(link_data[NL80211_ATTR_CHANNEL_WIDTH]));
++
++		if (link_data[NL80211_ATTR_CENTER_FREQ1])
++			info->links[link_id].center_freq1 =
++			      nla_get_u32(link_data[NL80211_ATTR_CENTER_FREQ1]);
++
++		if (link_data[NL80211_ATTR_CENTER_FREQ2])
++			info->links[link_id].center_freq2 =
++			      nla_get_u32(link_data[NL80211_ATTR_CENTER_FREQ2]);
++	}
++}
++
++
+ static int get_mlo_info(struct nl_msg *msg, void *arg)
+ {
+ 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+@@ -1208,8 +1229,7 @@ static int get_mlo_info(struct nl_msg *msg, void *arg)
+ 		os_memcpy(info->links[link_id].addr,
+ 			  nla_data(link_data[NL80211_ATTR_MAC]), ETH_ALEN);
+ 		if (link_data[NL80211_ATTR_WIPHY_FREQ])
+-			info->links[link_id].freq =
+-				nla_get_u32(link_data[NL80211_ATTR_WIPHY_FREQ]);
++			get_link_channel_info(link_data, link_id, info);
+ 	}
+ 
+ 	return NL_SKIP;
+diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
+index 7b91ce988..cd838c6cf 100644
+--- a/wpa_supplicant/events.c
++++ b/wpa_supplicant/events.c
+@@ -4122,6 +4122,9 @@ static int wpa_drv_get_mlo_info(struct wpa_supplicant *wpa_s)
+ 		os_memcpy(wpa_s->links[i].addr, mlo.links[i].addr, ETH_ALEN);
+ 		os_memcpy(wpa_s->links[i].bssid, mlo.links[i].bssid, ETH_ALEN);
+ 		wpa_s->links[i].freq = mlo.links[i].freq;
++		wpa_s->links[i].center_freq1 = mlo.links[i].center_freq1;
++		wpa_s->links[i].center_freq2 = mlo.links[i].center_freq2;
++		wpa_s->links[i].width = mlo.links[i].width;
+ 		wpa_supplicant_update_link_bss(wpa_s, i, mlo.links[i].bssid);
+ 	}
+ 
+diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
+index 952a3bd5a..d2835cc68 100644
+--- a/wpa_supplicant/wpa_supplicant_i.h
++++ b/wpa_supplicant/wpa_supplicant_i.h
+@@ -740,7 +740,8 @@ struct wpa_supplicant {
+ 	struct {
+ 		u8 addr[ETH_ALEN];
+ 		u8 bssid[ETH_ALEN];
+-		unsigned int freq;
++		unsigned int freq, center_freq1, center_freq2;
++		enum chan_width width;
+ 		struct wpa_bss *bss;
+ 		bool disabled;
+ 	} links[MAX_NUM_MLD_LINKS];
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0091-mtk-hostapd-refactor-the-operating-channel-width-nam.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0091-mtk-hostapd-refactor-the-operating-channel-width-nam.patch
new file mode 100644
index 0000000..d4a6433
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0091-mtk-hostapd-refactor-the-operating-channel-width-nam.patch
@@ -0,0 +1,105 @@
+From 7f0f9705e413d6a879b03e1989b7691e031ea2a0 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 17 May 2024 17:02:08 +0800
+Subject: [PATCH 091/126] mtk: hostapd: refactor the operating channel width
+ naming
+
+There are many data structures about channel width in hostapd. This
+commit re-names the one which is used to describe the operating
+channel width so that following commits can use other channel width
+variables.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/ucode.c | 24 ++++++++++++------------
+ 1 file changed, 12 insertions(+), 12 deletions(-)
+
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+index 050bed6ae..bc5cf2ab9 100644
+--- a/wpa_supplicant/ucode.c
++++ b/wpa_supplicant/ucode.c
+@@ -98,7 +98,7 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ {
+ 	const char *state;
+ 	uc_value_t *val;
+-	enum oper_chan_width ch_width;
++	enum oper_chan_width oper_chwidth;
+ 	int control_freq, center_freq1, bw320_offset = 1, band_idx;
+ 
+ 	if (event != EVENT_CH_SWITCH_STARTED &&
+@@ -129,27 +129,27 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ 
+ 		switch (data->ch_switch.ch_width) {
+ 		case CHAN_WIDTH_80:
+-			ch_width = CONF_OPER_CHWIDTH_80MHZ;
++			oper_chwidth = CONF_OPER_CHWIDTH_80MHZ;
+ 			break;
+ 		case CHAN_WIDTH_80P80:
+-			ch_width = CONF_OPER_CHWIDTH_80P80MHZ;
++			oper_chwidth = CONF_OPER_CHWIDTH_80P80MHZ;
+ 			break;
+ 		case CHAN_WIDTH_160:
+-			ch_width = CONF_OPER_CHWIDTH_160MHZ;
++			oper_chwidth = CONF_OPER_CHWIDTH_160MHZ;
+ 			break;
+ 		case CHAN_WIDTH_320:
+-			ch_width = CONF_OPER_CHWIDTH_320MHZ;
++			oper_chwidth = CONF_OPER_CHWIDTH_320MHZ;
+ 			break;
+ 		case CHAN_WIDTH_20_NOHT:
+ 		case CHAN_WIDTH_20:
+ 		case CHAN_WIDTH_40:
+ 		default:
+-			ch_width = CONF_OPER_CHWIDTH_USE_HT;
++			oper_chwidth = CONF_OPER_CHWIDTH_USE_HT;
+ 			break;
+ 		}
+ 
+ 		/* Check bandwidth 320 MHz-2 */
+-		if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
++		if (oper_chwidth == CONF_OPER_CHWIDTH_320MHZ &&
+ 		    (center_freq1 == 6265) || center_freq1 == 6585 ||
+ 		     center_freq1 == 6905)
+ 			bw320_offset = 2;
+@@ -176,7 +176,7 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ 		ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
+ 		ucv_object_add(val, "center_freq1", ucv_int64_new(center_freq1));
+ 		ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
+-		ucv_object_add(val, "ch_width", ucv_int64_new(ch_width));
++		ucv_object_add(val, "ch_width", ucv_int64_new(oper_chwidth));
+ 		ucv_object_add(val, "bw320_offset", ucv_int64_new(bw320_offset));
+ 		ucv_object_add(val, "band_idx", ucv_int64_new(band_idx));
+ 	}
+@@ -274,7 +274,7 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 	uc_value_t *ret, *val, *link_obj = uc_fn_arg(0);
+ 	struct wpa_channel_info ci;
+ 	u8 op_class, channel;
+-	enum oper_chan_width ch_width = CONF_OPER_CHWIDTH_USE_HT;
++	enum oper_chan_width oper_chwidth = CONF_OPER_CHWIDTH_USE_HT;
+ 	int center_freq1, bw320_offset = 1, is_24ghz, band_idx;
+ 	enum hostapd_hw_mode hw_mode;
+ 	int link_id = ucv_int64_get(link_obj);
+@@ -319,9 +319,9 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 
+ 		if (!ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
+ 						   sec_chan, &op_class, &channel))
+-			ch_width = op_class_to_ch_width(op_class);
++			oper_chwidth = op_class_to_ch_width(op_class);
+ 
+-		if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
++		if (oper_chwidth == CONF_OPER_CHWIDTH_320MHZ &&
+ 		    (center_freq1 == 6265) || center_freq1 == 6585 ||
+ 		     center_freq1 == 6905) {
+ 			/* Bandwidth 320 MHz-2 */
+@@ -330,7 +330,7 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 
+ 		ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
+ 		ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
+-		ucv_object_add(ret, "ch_width", ucv_int64_new(ch_width));
++		ucv_object_add(ret, "ch_width", ucv_int64_new(oper_chwidth));
+ 		ucv_object_add(ret, "bw320_offset", ucv_int64_new(bw320_offset));
+ 		ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
+ 	}
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0091-mtk-wifi-hostapd-add-wds-mlo-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0091-mtk-wifi-hostapd-add-wds-mlo-support.patch
deleted file mode 100644
index 4ee23cd..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0091-mtk-wifi-hostapd-add-wds-mlo-support.patch
+++ /dev/null
@@ -1,158 +0,0 @@
-From 11e9653a0df41e119ff5acad3ffcbd2825690413 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Tue, 16 Jan 2024 16:22:17 +0800
-Subject: [PATCH 091/104] mtk: wifi: hostapd: add wds mlo support
-
-1. Add mld_assoc_sta to get the primary sta_info.
-2. Find hapd according to mld address.
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- src/ap/drv_callbacks.c       |  3 ++-
- src/ap/ieee802_11.c          |  8 ++++++++
- src/ap/sta_info.c            |  6 +++++-
- src/ap/sta_info.h            |  1 +
- src/drivers/driver.h         |  3 +++
- src/drivers/driver_nl80211.c | 14 ++++++++++++++
- 6 files changed, 33 insertions(+), 2 deletions(-)
-
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index 2d946afd6..27c7555a1 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -1762,6 +1762,7 @@ switch_link_hapd(struct hostapd_data *hapd, int link_id)
- static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
- 					    const u8 *bssid, int link_id)
- {
-+	struct hostapd_data *ret = NULL;
- 	size_t i;
- 
- 	if (bssid == NULL)
-@@ -1797,7 +1798,7 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
- #endif /*CONFIG_IEEE80211BE */
- 	}
- 
--	return NULL;
-+	return ret;
- }
- 
- 
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index fe0a5bce4..688393422 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -3105,6 +3105,7 @@ static void handle_auth(struct hostapd_data *hapd,
- 
- 			ap_sta_set_mld(sta, true);
- 			sta->mld_assoc_link_id = link_id;
-+			sta->mld_assoc_sta = sta;
- 
- 			/*
- 			 * Set the MLD address as the station address and the
-@@ -4479,6 +4480,7 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
- 
- 	sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
- 	sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
-+	sta->mld_assoc_sta = origin_sta;
- 
- 	status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
- 	if (status != WLAN_STATUS_SUCCESS) {
-@@ -6957,6 +6959,12 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
- 	struct sta_info *sta;
- 
- 	sta = ap_get_sta(hapd, src);
-+
-+#ifdef CONFIG_IEEE80211BE
-+	if (sta && sta->mld_info.mld_sta)
-+		sta = sta->mld_assoc_sta;
-+#endif
-+
- 	if (sta &&
- 	    ((sta->flags & WLAN_STA_ASSOC) ||
- 	     ((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) {
-diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
-index e9fa0ed6e..2b8307a27 100644
---- a/src/ap/sta_info.c
-+++ b/src/ap/sta_info.c
-@@ -74,6 +74,7 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
- 	while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
- 		s = s->hnext;
- 
-+#ifdef CONFIG_IEEE80211BE
- 	if (hapd->conf->mld_ap && !s) {
- 		u8 link_id;
- 
-@@ -84,10 +85,13 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
- 				continue;
- 
- 			for (s = h->sta_list; s; s = s->next)
--				if (!os_memcmp(s->setup_link_addr, sta, 6))
-+				if ((!os_memcmp(s->setup_link_addr, sta, 6) ||
-+				     !os_memcmp(s->addr, sta, 6)) &&
-+				     s->flags & WLAN_STA_ASSOC)
- 					return s;
- 		}
- 	}
-+#endif
- 
- 	return s;
- }
-diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
-index cd89db6c8..8e500ec9a 100644
---- a/src/ap/sta_info.h
-+++ b/src/ap/sta_info.h
-@@ -334,6 +334,7 @@ struct sta_info {
- #ifdef CONFIG_IEEE80211BE
- 	struct mld_info mld_info;
- 	u8 mld_assoc_link_id;
-+	struct sta_info *mld_assoc_sta;
- #endif /* CONFIG_IEEE80211BE */
- };
- 
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 10ae48729..ba61f5842 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5339,6 +5339,9 @@ struct wpa_driver_ops {
- 	 * @pp_mode: Value is defined in enum pp_mode
- 	 */
- 	int (*pp_mode_set)(void *priv, const u8 pp_mode);
-+#ifdef CONFIG_IEEE80211BE
-+	int (*get_mld_addr)(void *priv, u8 *addr);
-+#endif
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index ad73b4ac1..df4a7ec41 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -15181,6 +15181,17 @@ fail:
- 	return -ENOBUFS;
- }
- 
-+#ifdef CONFIG_IEEE80211BE
-+static int nl80211_get_mld_addr(void *priv, u8 *addr)
-+{
-+	struct i802_bss *bss = priv;
-+
-+	os_memcpy(addr, bss->addr, ETH_ALEN);
-+
-+	return 0;
-+}
-+#endif
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
- 	.desc = "Linux nl80211/cfg80211",
-@@ -15361,4 +15372,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.amnt_dump = nl80211_amnt_dump,
- 	.background_radar_mode = nl80211_background_radar_mode,
- 	.pp_mode_set = nl80211_pp_mode_set,
-+#ifdef CONFIG_IEEE80211BE
-+	.get_mld_addr = nl80211_get_mld_addr,
-+#endif
- };
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0092-mtk-hostapd-refactor-legacy-STA-getting-operating-ch.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0092-mtk-hostapd-refactor-legacy-STA-getting-operating-ch.patch
new file mode 100644
index 0000000..ecd524f
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0092-mtk-hostapd-refactor-legacy-STA-getting-operating-ch.patch
@@ -0,0 +1,63 @@
+From b81e4dd857867be4af9bff4b698aec0edc333b34 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 17 May 2024 17:05:54 +0800
+Subject: [PATCH 092/126] mtk: hostapd: refactor legacy STA getting operating
+ channel information
+
+The refactor is for following MLO extension.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/ucode.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+index bc5cf2ab9..d4b2160b6 100644
+--- a/wpa_supplicant/ucode.c
++++ b/wpa_supplicant/ucode.c
+@@ -274,10 +274,12 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 	uc_value_t *ret, *val, *link_obj = uc_fn_arg(0);
+ 	struct wpa_channel_info ci;
+ 	u8 op_class, channel;
++	enum chan_width chwidth;
+ 	enum oper_chan_width oper_chwidth = CONF_OPER_CHWIDTH_USE_HT;
+ 	int center_freq1, bw320_offset = 1, is_24ghz, band_idx;
+ 	enum hostapd_hw_mode hw_mode;
+ 	int link_id = ucv_int64_get(link_obj);
++	u32 freq;
+ 
+ 	if (!wpa_s)
+ 		return NULL;
+@@ -308,7 +310,9 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 			band_idx = 2;
+ 
+ 		wpa_drv_channel_info(wpa_s, &ci);
++		freq = ci.frequency;
+ 		center_freq1 = ci.center_frq1;
++		chwidth=ci.chanwidth;
+ 
+ 		if (bss->freq != center_freq1) {
+ 			if (is_24ghz)
+@@ -317,8 +321,8 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 				sec_chan = (bss->freq / 20) & 1 ? 1 : -1;
+ 		}
+ 
+-		if (!ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
+-						   sec_chan, &op_class, &channel))
++		if (!ieee80211_chaninfo_to_channel(freq, chwidth, sec_chan,
++						   &op_class, &channel))
+ 			oper_chwidth = op_class_to_ch_width(op_class);
+ 
+ 		if (oper_chwidth == CONF_OPER_CHWIDTH_320MHZ &&
+@@ -329,7 +333,7 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 		}
+ 
+ 		ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
+-		ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
++		ucv_object_add(ret, "frequency", ucv_int64_new(freq));
+ 		ucv_object_add(ret, "ch_width", ucv_int64_new(oper_chwidth));
+ 		ucv_object_add(ret, "bw320_offset", ucv_int64_new(bw320_offset));
+ 		ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0093-mtk-hostapd-get-link-channel-information-and-synchro.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0093-mtk-hostapd-get-link-channel-information-and-synchro.patch
new file mode 100644
index 0000000..15283a0
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0093-mtk-hostapd-get-link-channel-information-and-synchro.patch
@@ -0,0 +1,83 @@
+From 0946b9ece456bc11e4ef620063e7b0078d3d73aa Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Fri, 17 May 2024 17:21:06 +0800
+Subject: [PATCH 093/126] mtk: hostapd: get link channel information and
+ synchronize to AP
+
+'wpa_s->valid_links' is used to determine the connection is MLO or not,
+and different ways are used to retrieve operating channel information.
+
+Refactor center frequency calculation part in the function
+uc_wpa_freq_info.
+1. It does not have to set seg0 for 2GHz.
+2. The original center frequency calculation is wrong for 2G. Also, center
+   frequency 1 of BW 20/40 MHz can be derived from control frequency &
+   secondary channel offset.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ucode.c         |  2 +-
+ src/utils/ucode.c      |  8 +++++---
+ wpa_supplicant/ucode.c | 14 ++++++++++----
+ 3 files changed, 16 insertions(+), 8 deletions(-)
+
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index a69caa6cf..8c05404f5 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -657,7 +657,7 @@ out:
+ 	wpa_printf(MSG_INFO, "    * seg0: %d\n",
+ 			hostapd_get_oper_centr_freq_seg0_idx(conf));
+ 	wpa_printf(MSG_INFO, "    * seg1: %d\n",
+-			hostapd_get_oper_centr_freq_seg0_idx(conf));
++			hostapd_get_oper_centr_freq_seg1_idx(conf));
+ 	wpa_printf(MSG_INFO, "    * oper_chwidth: %d\n",
+ 			hostapd_get_oper_chwidth(conf));
+ 
+diff --git a/src/utils/ucode.c b/src/utils/ucode.c
+index 81d472f6b..8bbbbbeff 100644
+--- a/src/utils/ucode.c
++++ b/src/utils/ucode.c
+@@ -186,9 +186,11 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 	ucv_object_add(ret, "oper_chwidth", ucv_int64_new(chanwidth));
+ 	ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
+ 
+-	if (chanwidth == CONF_OPER_CHWIDTH_USE_HT && !sec_channel) {
+-		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(channel));
+-		ucv_object_add(ret, "center_freq1", ucv_int64_new(freq_val));
++	if (chanwidth == CONF_OPER_CHWIDTH_USE_HT) {
++		center_idx = freq_val < 3000 ? 0 : channel;
++		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(center_idx));
++		ucv_object_add(ret, "center_freq1",
++			       ucv_int64_new(freq_val + sec_channel * 10));
+ 		return ret;
+ 	}
+ 
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+index d4b2160b6..450780737 100644
+--- a/wpa_supplicant/ucode.c
++++ b/wpa_supplicant/ucode.c
+@@ -309,10 +309,16 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+ 		else if (is_6ghz_freq(bss->freq))
+ 			band_idx = 2;
+ 
+-		wpa_drv_channel_info(wpa_s, &ci);
+-		freq = ci.frequency;
+-		center_freq1 = ci.center_frq1;
+-		chwidth=ci.chanwidth;
++		if (wpa_s->valid_links) {
++			freq = wpa_s->links[link_id].freq;
++			center_freq1 = wpa_s->links[link_id].center_freq1;
++			chwidth = wpa_s->links[link_id].width;
++		} else {
++			wpa_drv_channel_info(wpa_s, &ci);
++			freq = ci.frequency;
++			center_freq1 = ci.center_frq1;
++			chwidth=ci.chanwidth;
++		}
+ 
+ 		if (bss->freq != center_freq1) {
+ 			if (is_24ghz)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0093-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0093-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch
deleted file mode 100644
index 0cff032..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0093-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From ce0ccc758fc8a5076ce3476f627b00019cf90ab1 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Fri, 1 Mar 2024 16:59:53 +0800
-Subject: [PATCH 093/104] mtk: hostapd: prevent getting non-MLD STA for other
- links
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/sta_info.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
-index 2b8307a27..9a8510980 100644
---- a/src/ap/sta_info.c
-+++ b/src/ap/sta_info.c
-@@ -87,7 +87,8 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
- 			for (s = h->sta_list; s; s = s->next)
- 				if ((!os_memcmp(s->setup_link_addr, sta, 6) ||
- 				     !os_memcmp(s->addr, sta, 6)) &&
--				     s->flags & WLAN_STA_ASSOC)
-+				     s->flags & WLAN_STA_ASSOC &&
-+				     s->mld_info.mld_sta)
- 					return s;
- 		}
- 	}
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0094-mtk-hostapd-AP-MLD-specify-link-id-for-unicast-DEAUT.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0094-mtk-hostapd-AP-MLD-specify-link-id-for-unicast-DEAUT.patch
deleted file mode 100644
index c276024..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0094-mtk-hostapd-AP-MLD-specify-link-id-for-unicast-DEAUT.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-From 8c4eb9b740a9f2ae57e048edbbc4aad1d62734f2 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Tue, 27 Feb 2024 15:04:35 +0800
-Subject: [PATCH 094/104] mtk: hostapd: AP MLD: specify link id for unicast
- DEAUTH
-
-When deauthenticating the STA, hostapd should specifies the setup link
-of the target STA so that the TX status of the DEAUTH can be forwarded
-to the correct link (BSS).
-
-(The original gerrit somehow disappears, so I commit it again)
-(https://gerrit.mediatek.inc/c/gateway/WiFi7/mac80211/hostapd/+/8715613)
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/ap_drv_ops.c | 6 +++++-
- 1 file changed, 5 insertions(+), 1 deletion(-)
-
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 9357ce7b6..2c535f24a 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -867,7 +867,11 @@ int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
- 	if (hapd->conf->mld_ap) {
- 		struct sta_info *sta = ap_get_sta(hapd, addr);
- 
--		link_id = hapd->mld_link_id;
-+		if (sta)
-+			link_id = sta->mld_assoc_link_id;
-+		else
-+			link_id = hapd->mld_link_id;
-+
- 		if (ap_sta_is_mld(hapd, sta))
- 			own_addr = hapd->mld->mld_addr;
- 	}
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0094-mtk-hostapd-adjust-Basic-EHT-MCS-and-Nss-Set-in-EHT-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0094-mtk-hostapd-adjust-Basic-EHT-MCS-and-Nss-Set-in-EHT-.patch
new file mode 100644
index 0000000..6f0544b
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0094-mtk-hostapd-adjust-Basic-EHT-MCS-and-Nss-Set-in-EHT-.patch
@@ -0,0 +1,38 @@
+From 8cb7f55db558b56cc687e4d3407210935a2a4c78 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 20 May 2024 14:59:45 +0800
+Subject: [PATCH 094/126] mtk: hostapd: adjust Basic EHT-MCS and Nss Set in EHT
+ Oper IE
+
+Adjust basic EHT-MCS and Nss set in EHT Operation IE. Only set Rx/Tx Max
+Nss that supports EHT-MCS 0-7 to 1, and the other field shall be set to
+0 (0x11). Without this commit, Intel BE200L in WiFi7 R1 Lab will not send
+auth to ap. The reason why we only set EHT-MCS 0-7 as 1 is we align the IE
+value with other testbed ap.
+
+Please note that this patch is for WiFi7 cert Intel BE200L connection
+issue. hostapd shall support configure this IE by hostapd configuration,
+just like he_basic_mcs_nss_set in HE operation IE.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ src/ap/ieee802_11_eht.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index 59ada2f86..7c328799a 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -237,9 +237,6 @@ u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
+ 
+ 	/* TODO: Fill in appropriate EHT-MCS max Nss information */
+ 	oper->basic_eht_mcs_nss_set[0] = 0x11;
+-	oper->basic_eht_mcs_nss_set[1] = 0x11;
+-	oper->basic_eht_mcs_nss_set[2] = 0x11;
+-	oper->basic_eht_mcs_nss_set[3] = 0x11;
+ 
+ 	if (!eht_oper_info_present)
+ 		return pos + elen;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0095-mtk-hostapd-fix-using-wrong-link-id-in-nl80211_set_c.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0095-mtk-hostapd-fix-using-wrong-link-id-in-nl80211_set_c.patch
new file mode 100644
index 0000000..e5d058a
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0095-mtk-hostapd-fix-using-wrong-link-id-in-nl80211_set_c.patch
@@ -0,0 +1,35 @@
+From eaa5c322744016e6e5ecb45e095fec2bd77af0a5 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 24 May 2024 11:01:10 +0800
+Subject: [PATCH 095/126] mtk: hostapd: fix using wrong link id in
+ nl80211_set_channel during set beacon
+
+param.freq->link_id is used in nl80211_set_channel but it is not set in __ieee802_11_set_beacon.
+nl80211_set_channel will return EBUSY after channel switch.
+This error occurs repeatedly especially when the bandwidth is changed.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/beacon.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 9b5c4fc1e..59db8be8d 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -2866,6 +2866,12 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
+ 		params.freq = &freq;
+ 	}
+ 
++	params.freq->link_id = -1;
++#ifdef CONFIG_IEEE80211BE
++	if (hapd->conf->mld_ap)
++		params.freq->link_id = hapd->mld_link_id;
++#endif /* CONFIG_IEEE80211BE */
++
+ 	for (i = 0; i < hapd->iface->num_hw_features; i++) {
+ 		mode = &hapd->iface->hw_features[i];
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0095-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0095-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch
deleted file mode 100644
index e31a05c..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0095-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 161c066295847f1c1828f641e789565fe4bacd11 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Thu, 14 Mar 2024 14:31:28 +0800
-Subject: [PATCH 095/104] mtk: hostapd: using MLD addr as SA/BSSID for
- broadcast DEAUTH
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/ap_drv_ops.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 2c535f24a..3c1609656 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -872,7 +872,7 @@ int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
- 		else
- 			link_id = hapd->mld_link_id;
- 
--		if (ap_sta_is_mld(hapd, sta))
-+		if (ap_sta_is_mld(hapd, sta) || is_multicast_ether_addr(addr))
- 			own_addr = hapd->mld->mld_addr;
- 	}
- #endif /* CONFIG_IEEE80211BE */
-@@ -893,7 +893,7 @@ int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
- 	if (hapd->conf->mld_ap) {
- 		struct sta_info *sta = ap_get_sta(hapd, addr);
- 
--		if (ap_sta_is_mld(hapd, sta))
-+		if (ap_sta_is_mld(hapd, sta) || is_multicast_ether_addr(addr))
- 			own_addr = hapd->mld->mld_addr;
- 	}
- #endif /* CONFIG_IEEE80211BE */
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0096-mtk-hostapd-Remove-BPCC-and-ML-ie-in-per-sta-profile.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0096-mtk-hostapd-Remove-BPCC-and-ML-ie-in-per-sta-profile.patch
new file mode 100644
index 0000000..6133ae8
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0096-mtk-hostapd-Remove-BPCC-and-ML-ie-in-per-sta-profile.patch
@@ -0,0 +1,104 @@
+From 7c009a478fec002ec626163eff2c686b14bc9aa5 Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Thu, 23 May 2024 11:07:25 +0800
+Subject: [PATCH 096/126] mtk: hostapd: Remove BPCC and ML ie in per-sta
+ profile of ML probe response
+
+wifi7 cert testplan request DUT do not bring BPCC in STA info of
+per-sta profile of ML probe response.
+The standard defined not bring ML ie in per-sta profile.
+
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ src/ap/beacon.c         |  2 +-
+ src/ap/ieee802_11_eht.c | 21 +++++++++++----------
+ 2 files changed, 12 insertions(+), 11 deletions(-)
+
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 59db8be8d..1ffe2fb56 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -929,7 +929,7 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
+ 		struct hostapd_data *ml_elem_ap =
+ 			params->mld_ap ? params->mld_ap : hapd;
+ 
+-		if (ml_elem_ap->conf->mld_ap)
++		if (!params->is_ml_sta_info && ml_elem_ap->conf->mld_ap)
+ 			pos = hostapd_eid_eht_ml_beacon(
+ 				ml_elem_ap, params->mld_info,
+ 				pos, !!params->mld_ap);
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index 7c328799a..02e85194c 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -545,8 +545,8 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 		 * beacon interval (2) + TSF offset (8) + DTIM info (2) + BSS
+ 		 * parameters change counter (1) + station profile length.
+ 		 */
+-#define EHT_ML_STA_INFO_LEN 22
+-		size_t total_len = EHT_ML_STA_INFO_LEN +
++		size_t sta_info_len = include_mld_id ? 21 : 22;
++		size_t total_len = sta_info_len +
+ 			link->resp_sta_profile_len;
+ 
+ 		/* Skip the local one */
+@@ -574,14 +574,16 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 			EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK |
+ 			EHT_PER_STA_CTRL_TSF_OFFSET_PRESENT_MSK |
+ 			EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK |
+-			EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK |
+-			EHT_PER_STA_CTRL_BSS_PARAM_CNT_PRESENT_MSK;
++			EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK;
++
++		if (!include_mld_id)
++			control |= EHT_PER_STA_CTRL_BSS_PARAM_CNT_PRESENT_MSK;
+ 		wpabuf_put_le16(buf, control);
+ 
+ 		/* STA Info */
+ 
+ 		/* STA Info Length */
+-		wpabuf_put_u8(buf, EHT_ML_STA_INFO_LEN - 2);
++		wpabuf_put_u8(buf, sta_info_len - 2);
+ 		wpabuf_put_data(buf, link->local_addr, ETH_ALEN);
+ 		wpabuf_put_le16(buf, link_bss->iconf->beacon_int);
+ 
+@@ -597,7 +599,8 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 		wpabuf_put_u8(buf, link_bss->conf->dtim_period);
+ 
+ 		/* BSS Parameters Change Count */
+-		wpabuf_put_u8(buf, link_bss->eht_mld_bss_param_change);
++		if (!include_mld_id)
++			wpabuf_put_u8(buf, link_bss->eht_mld_bss_param_change);
+ 
+ 		if (!link->resp_sta_profile)
+ 			continue;
+@@ -613,8 +616,7 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 			ptr = link->resp_sta_profile;
+ 			len = link->resp_sta_profile_len;
+ 
+-			slice_len = 255 - EHT_ML_STA_INFO_LEN;
+-
++			slice_len = 255 - sta_info_len;
+ 			wpabuf_put_data(buf, ptr, slice_len);
+ 			len -= slice_len;
+ 			ptr += slice_len;
+@@ -764,7 +766,7 @@ static size_t hostapd_eid_eht_ml_len(struct mld_info *info,
+ 	for (link_id = 0; info && link_id < ARRAY_SIZE(info->links);
+ 	     link_id++) {
+ 		struct mld_link_info *link;
+-		size_t sta_len = EHT_ML_STA_INFO_LEN;
++		size_t sta_len = include_mld_id ? 21 : 22;
+ 
+ 		link = &info->links[link_id];
+ 		if (!link->valid)
+@@ -789,7 +791,6 @@ static size_t hostapd_eid_eht_ml_len(struct mld_info *info,
+ 	return len;
+ }
+ #undef EHT_ML_COMMON_INFO_LEN
+-#undef EHT_ML_STA_INFO_LEN
+ 
+ 
+ u8 * hostapd_eid_eht_ml_beacon(struct hostapd_data *hapd,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0096-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0096-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch
deleted file mode 100644
index f311581..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0096-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From eb9916b7e0226793f14616dc98fda76c4d8337bf Mon Sep 17 00:00:00 2001
-From: Howard Hsu <howard-yh.hsu@mediatek.com>
-Date: Tue, 27 Feb 2024 15:32:06 +0800
-Subject: [PATCH 096/104] mtk: hostapd: support vht bfee sts can be up to 0x4
-
-Without this commit, the maximum vht bfee sts can only be 0x3. This commit
-support to read BF-ANTENNA-5 to set vht bfee sts capability as 4.
-
-Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
----
- hostapd/config_file.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index ef9bafb28..b9a062193 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -1190,6 +1190,9 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf,
- 	if (os_strstr(capab, "[BF-ANTENNA-4]") &&
- 	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
- 		conf->vht_capab |= (3 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
-+	if (os_strstr(capab, "[BF-ANTENNA-5]") &&
-+	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
-+		conf->vht_capab |= (4 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
- 	if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") &&
- 	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
- 		conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0097-mtk-hostapd-add-channel-switch-band-sanity-check.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0097-mtk-hostapd-add-channel-switch-band-sanity-check.patch
new file mode 100644
index 0000000..7ea83ec
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0097-mtk-hostapd-add-channel-switch-band-sanity-check.patch
@@ -0,0 +1,40 @@
+From 0710501eee6f7a2c06fc8f0c5d3e4db8fddc349d Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 30 May 2024 11:24:54 +0800
+Subject: [PATCH 097/126] mtk: hostapd: add channel switch band sanity check
+
+Add band sanity check in case user selecting the wrong freq or link id
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/ctrl_iface.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 2038a3712..14a0483bf 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -2816,6 +2816,7 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ 					  char *pos)
+ {
+ #ifdef NEED_AP_MLME
++	struct hostapd_hw_modes *mode = iface->current_mode;
+ 	struct csa_settings settings;
+ 	int ret;
+ 	int dfs_range = 0;
+@@ -2835,6 +2836,12 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ 		settings.link_id = iface->bss[0]->mld_link_id;
+ #endif /* CONFIG_IEEE80211BE */
+ 
++	if (!mode ||
++	    !is_same_band(mode->channels->freq, settings.freq_params.freq)) {
++		wpa_printf(MSG_ERROR, "Invalid band for current mode");
++		return -1;
++	}
++
+ 	ret = hostapd_ctrl_check_freq_params(&settings.freq_params,
+ 					     settings.punct_bitmap);
+ 	if (ret) {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0097-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0097-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch
deleted file mode 100644
index 1afcf3e..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0097-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch
+++ /dev/null
@@ -1,54 +0,0 @@
-From c1e7c5e556cd372594a789091d2ab65a9d8dd56f Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Thu, 19 Oct 2023 14:08:50 +0800
-Subject: [PATCH 097/104] mtk: hostapd: fix issue that tx status handle with
- unmatch hostapd_data.
-
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
----
- src/ap/ieee802_11.c                | 11 ++++++++++-
- src/drivers/driver_nl80211_event.c |  2 +-
- 2 files changed, 11 insertions(+), 2 deletions(-)
-
-diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
-index 688393422..2dd763e63 100644
---- a/src/ap/ieee802_11.c
-+++ b/src/ap/ieee802_11.c
-@@ -6536,11 +6536,20 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
- #ifdef CONFIG_IEEE80211BE
- 	if (ap_sta_is_mld(hapd, sta) &&
- 	    hapd->mld_link_id != sta->mld_assoc_link_id) {
-+		struct hostapd_data *temp_hapd = hapd;
-+
- 		/* See ieee80211_ml_link_sta_assoc_cb() for the MLD case */
- 		wpa_printf(MSG_DEBUG,
- 			   "%s: MLD: ignore on link station (%d != %d)",
- 			   __func__, hapd->mld_link_id, sta->mld_assoc_link_id);
--		return;
-+
-+		if (temp_hapd->conf->mld_ap && sta->mld_assoc_link_id >= 0) {
-+			struct hostapd_data *link_bss;
-+
-+			link_bss = hostapd_mld_get_link_bss(temp_hapd, sta->mld_assoc_link_id);
-+			if (link_bss)
-+				hapd = link_bss;
-+		}
- 	}
- #endif /* CONFIG_IEEE80211BE */
- 
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 03bad4bb3..885fc4c9e 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -1374,7 +1374,7 @@ static void mlme_event_mgmt(struct i802_bss *bss,
- 	event.rx_mgmt.ctx = bss->ctx;
- 	event.rx_mgmt.link_id = link_id;
- 
--	wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
-+	wpa_supplicant_event(bss->ctx, EVENT_RX_MGMT, &event);
- }
- 
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0098-mtk-hostapd-Fix-the-issue-with-the-presence-of-MLD_I.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0098-mtk-hostapd-Fix-the-issue-with-the-presence-of-MLD_I.patch
new file mode 100644
index 0000000..e35a423
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0098-mtk-hostapd-Fix-the-issue-with-the-presence-of-MLD_I.patch
@@ -0,0 +1,51 @@
+From 933e864265735dd4b668f13cced18e18b789b41c Mon Sep 17 00:00:00 2001
+From: "MeiChia.Chiu" <MeiChia.Chiu@mediatek.com>
+Date: Mon, 3 Jun 2024 14:29:24 +0800
+Subject: [PATCH 098/126] mtk: hostapd: Fix the issue with the presence of
+ MLD_ID in the probe response
+
+The probe response carries the MLD_ID value only
+when mld_id is not 0 (e.g., NonTxBSS).
+
+Signed-off-by: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Signed-off-by: Money Wang <money.wang@mediatek.com>
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ src/ap/ieee802_11_eht.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index 02e85194c..8fc239f36 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -480,7 +480,7 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 	if (hapd->iconf->eml_disable)
+ 		common_info_len -= 2; /* EML Capabilities (2) */
+ 
+-	if (include_mld_id) {
++	if (include_mld_id && hostapd_get_mld_id(hapd)) {
+ 		/* AP MLD ID */
+ 		control |= BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID;
+ 		common_info_len++;
+@@ -526,7 +526,7 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 		   mld_cap);
+ 	wpabuf_put_le16(buf, mld_cap);
+ 
+-	if (include_mld_id) {
++	if (include_mld_id && hostapd_get_mld_id(hapd)) {
+ 		wpa_printf(MSG_DEBUG, "MLD: AP MLD ID=0x%x",
+ 			   hostapd_get_mld_id(hapd));
+ 		wpabuf_put_u8(buf, hostapd_get_mld_id(hapd));
+@@ -820,7 +820,8 @@ size_t hostapd_eid_eht_ml_beacon_len(struct hostapd_data *hapd,
+ 				     struct mld_info *info,
+ 				     bool include_mld_id)
+ {
+-	return hostapd_eid_eht_ml_len(info, include_mld_id,
++	return hostapd_eid_eht_ml_len(info,
++				      include_mld_id && hostapd_get_mld_id(hapd),
+ 				      hapd->iconf->eml_disable);
+ }
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0098-mtk-hostapd-add-connac3-csi-control-interface.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0098-mtk-hostapd-add-connac3-csi-control-interface.patch
deleted file mode 100644
index 9c4c4e5..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0098-mtk-hostapd-add-connac3-csi-control-interface.patch
+++ /dev/null
@@ -1,690 +0,0 @@
-From c125d2615df25bd9a5a4801abfbcc91f21482e02 Mon Sep 17 00:00:00 2001
-From: mtk20656 <chank.chen@mediatek.com>
-Date: Tue, 6 Feb 2024 15:46:05 +0800
-Subject: [PATCH 098/104] mtk: hostapd: add connac3 csi control interface
-
-1. add hostapd_cli interface
-2. add csi set/dump flow
-3. add csi raw data to json
-
-Signed-off-by: mtk20656 <chank.chen@mediatek.com>
----
- hostapd/ctrl_iface.c              | 193 ++++++++++++++++++++++++
- hostapd/hostapd_cli.c             |  16 ++
- src/ap/ap_drv_ops.c               |  13 ++
- src/ap/ap_drv_ops.h               |   2 +
- src/common/mtk_vendor.h           |  31 +++-
- src/drivers/driver.h              |  16 ++
- src/drivers/driver_nl80211.c      | 235 ++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |   1 +
- src/drivers/driver_nl80211_capa.c |   3 +
- 9 files changed, 503 insertions(+), 7 deletions(-)
-
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index f0c990314..b5f6431bf 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4925,6 +4925,193 @@ hostapd_ctrl_iface_disable_beacon(struct hostapd_data *hapd, char *value,
- 
- }
- 
-+static int
-+hostapd_ctrl_iface_set_csi(struct hostapd_data *hapd, char *cmd,
-+					char *buf, size_t buflen)
-+{
-+	char *tmp;
-+	u8 sta_mac[ETH_ALEN] = {0};
-+	u32 csi_para[4] = {0};
-+	char mac_str[18] = {0};
-+	u8 csi_para_cnt = 0;
-+
-+	tmp = strtok_r(cmd, ",", &cmd);
-+
-+	while (tmp) {
-+		csi_para_cnt++;
-+
-+		if (csi_para_cnt <= 4)
-+			csi_para[csi_para_cnt - 1] = strtol(tmp, &tmp, 10);
-+		else if (csi_para_cnt == 5) {
-+			memcpy(mac_str, tmp, sizeof(mac_str) - 1);
-+			break;
-+		}
-+
-+		tmp = strtok_r(NULL, ",", &cmd);
-+	}
-+
-+	if (strlen(mac_str)) {	/* user input mac string */
-+		if (hwaddr_aton(mac_str, sta_mac) < 0) {
-+			wpa_printf(MSG_ERROR, "station mac is not right.\n");
-+			return -1;
-+		}
-+
-+		if (hostapd_drv_csi_set(hapd, csi_para[0], csi_para[1], csi_para[2], csi_para[3], sta_mac)) {
-+			wpa_printf(MSG_ERROR, "Not able to set csi, %d,%d,%d,%d,%s\n",
-+					csi_para[0], csi_para[1], csi_para[2], csi_para[3], mac_str);
-+			return -1;
-+		}
-+	} else {
-+		if (hostapd_drv_csi_set(hapd, csi_para[0], csi_para[1], csi_para[2], csi_para[3], NULL)) {
-+			wpa_printf(MSG_ERROR, "Not able to set csi, %d,%d,%d,%d\n",
-+					csi_para[0], csi_para[1], csi_para[2], csi_para[3]);
-+			return -1;
-+		}
-+	}
-+
-+	return os_snprintf(buf, buflen, "OK\n");
-+}
-+
-+static int mt76_csi_to_json(char *fname, struct csi_resp_data *resp_buf)
-+{
-+#define MAX_BUF_SIZE	10000
-+	FILE *f;
-+	int i;
-+
-+	if (!fname) {
-+		wpa_printf(MSG_ERROR, "csi dump file name is null!\n");
-+		return -1;
-+	}
-+
-+	f = fopen(fname, "a+");
-+	if (!f) {
-+		wpa_printf(MSG_ERROR, "open csi dump file %s failed\n", fname);
-+		return -1;
-+	}
-+
-+	if (fwrite("[", 1, 1, f) != 1) {
-+		fclose(f);
-+		return -1;
-+	}
-+
-+	for (i = 0; i < resp_buf->buf_cnt; i++) {
-+		struct csi_data *c = &resp_buf->csi_buf[i];
-+		char *pos, *buf;
-+		int j;
-+
-+		buf = malloc(MAX_BUF_SIZE);
-+		if (!buf) {
-+			fclose(f);
-+			return -1;
-+		}
-+
-+		pos = buf;
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
-+
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->ts);
-+		pos += snprintf(pos, MAX_BUF_SIZE, "\"%02x%02x%02x%02x%02x%02x\",", c->ta[0], c->ta[1], c->ta[2], c->ta[3], c->ta[4], c->ta[5]);
-+
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->rssi);
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->snr);
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->data_bw);
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->pri_ch_idx);
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->rx_mode);
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->tx_idx);
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->rx_idx);
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->chain_info);
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->ext_info);
-+
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
-+		for (j = 0; j < c->data_num; j++) {
-+			pos += snprintf(pos, MAX_BUF_SIZE, "%d", c->data_i[j]);
-+			if (j != (c->data_num - 1))
-+				pos += snprintf(pos, MAX_BUF_SIZE, ",");
-+		}
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%c,", ']');
-+
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
-+		for (j = 0; j < c->data_num; j++) {
-+			pos += snprintf(pos, MAX_BUF_SIZE, "%d", c->data_q[j]);
-+			if (j != (c->data_num - 1))
-+				pos += snprintf(pos, MAX_BUF_SIZE, ",");
-+		}
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%c", ']');
-+
-+		pos += snprintf(pos, MAX_BUF_SIZE, "%c", ']');
-+		if (i != resp_buf->buf_cnt - 1)
-+			pos += snprintf(pos, MAX_BUF_SIZE, ",");
-+
-+		if (fwrite(buf, 1, pos - buf, f) != (pos - buf)) {
-+			perror("fwrite");
-+			free(buf);
-+			fclose(f);
-+			return -1;
-+		}
-+
-+		free(buf);
-+	}
-+
-+	if (fwrite("]", 1, 1, f) != 1) {
-+		fclose(f);
-+		return -1;
-+	}
-+
-+	fclose(f);
-+
-+	return 0;
-+}
-+
-+static int
-+hostapd_ctrl_iface_dump_csi(struct hostapd_data *hapd, char *cmd,
-+				char *buf, size_t buflen)
-+{
-+	char *tmp, *fname;
-+	int data_cnt = 0, ret = 0;
-+	struct csi_resp_data resp_buf;
-+
-+	tmp = strtok_r(cmd, ",", &cmd);
-+
-+	if (!tmp) {
-+		wpa_printf(MSG_ERROR, "Error in command format\n");
-+		return -1;
-+	}
-+
-+	data_cnt = strtoul(tmp, &tmp, 0);
-+
-+	if (data_cnt > 3000) {
-+		wpa_printf(MSG_ERROR, "Wrong input csi data cnt\n");
-+		return -1;
-+	}
-+
-+	fname = strtok_r(NULL, ",", &cmd);
-+
-+	if (!fname) {
-+		wpa_printf(MSG_ERROR, "Error in command format, csi_filename.\n");
-+		return -1;
-+	}
-+
-+	resp_buf.csi_buf = (struct csi_data *)os_zalloc(sizeof(struct csi_data) * data_cnt);
-+
-+	if (resp_buf.csi_buf == NULL) {
-+		wpa_printf(MSG_ERROR, "Error in memory allocation\n");
-+		return -1;
-+	}
-+
-+	resp_buf.usr_need_cnt = data_cnt;
-+	resp_buf.buf_cnt = 0;
-+
-+	if (hostapd_drv_csi_dump(hapd, (void *)&resp_buf)) {
-+		wpa_printf(MSG_ERROR, "Not able to set csi dump\n");
-+		os_free(resp_buf.csi_buf);
-+		return -1;
-+	}
-+
-+	mt76_csi_to_json(fname, &resp_buf);
-+
-+	os_free(resp_buf.csi_buf);
-+	return 0;
-+}
-+
- static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 					      char *buf, char *reply,
- 					      int reply_size,
-@@ -5578,6 +5765,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
- 	} else if (os_strncmp(buf, "NO_BEACON ", 10) == 0) {
- 		reply_len = hostapd_ctrl_iface_disable_beacon(hapd, buf + 10, reply,
- 							      reply_size);
-+	} else if (os_strncmp(buf, "SET_CSI ", 7) == 0) {
-+		reply_len = hostapd_ctrl_iface_set_csi(hapd, buf + 8,
-+							reply, reply_size);
-+	} else if (os_strncmp(buf, "DUMP_CSI ", 8) == 0) {
-+		reply_len = hostapd_ctrl_iface_dump_csi(hapd, buf + 9,
-+							reply, reply_size);
- 	} else {
- 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
- 		reply_len = 16;
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index acfa3b1d1..81b74d6de 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1719,6 +1719,18 @@ static int hostapd_cli_cmd_dump_amnt(struct wpa_ctrl *ctrl, int argc,
- 	return hostapd_cli_cmd(ctrl, "DUMP_AMNT", 1, argc, argv);
- }
- 
-+static int hostapd_cli_cmd_set_csi(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "SET_CSI", 1, argc, argv);
-+}
-+
-+static int hostapd_cli_cmd_dump_csi(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "DUMP_CSI", 1, argc, argv);
-+}
-+
- struct hostapd_cli_cmd {
- 	const char *cmd;
- 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
-@@ -1960,6 +1972,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 		" = Set Station index and mac to monitor"},
- 	{ "dump_amnt", hostapd_cli_cmd_dump_amnt, NULL,
- 		" = Dump RSSI of monitoring Station"},
-+	{ "set_csi", hostapd_cli_cmd_set_csi, NULL,
-+		" = Set csi configuaration"},
-+	{ "dump_csi", hostapd_cli_cmd_dump_csi, NULL,
-+		" = Dump csi data to a json file"},
- 	{ NULL, NULL, NULL, NULL }
- };
- 
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index 3c1609656..cb782fa31 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1409,3 +1409,16 @@ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
- 	return hapd->driver->beacon_ctrl(hapd->drv_priv, beacon_mode);
- }
- 
-+int hostapd_drv_csi_set(struct hostapd_data *hapd, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac)
-+{
-+	if (!hapd->driver || !hapd->driver->csi_set)
-+		return 0;
-+	return hapd->driver->csi_set(hapd->drv_priv, mode, cfg, v1, v2, mac);
-+}
-+
-+int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf)
-+{
-+	if (!hapd->driver || !hapd->driver->csi_dump)
-+		return 0;
-+	return hapd->driver->csi_dump(hapd->drv_priv, dump_buf);
-+}
-diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
-index 5830705a3..7abc58377 100644
---- a/src/ap/ap_drv_ops.h
-+++ b/src/ap/ap_drv_ops.h
-@@ -171,6 +171,8 @@ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_
- int hostapd_drv_background_radar_mode(struct hostapd_data *hapd);
- int hostapd_drv_pp_mode_set(struct hostapd_data *hapd);
- int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode);
-+int hostapd_drv_csi_set(struct hostapd_data *hapd, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac);
-+int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf);
- 
- #include "drivers/driver.h"
- 
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index 1e4c3670e..c290e72a7 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -73,7 +73,6 @@ enum mtk_vendor_attr_csi_ctrl {
- 	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
- 	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
- 	MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
--	MTK_VENDOR_ATTR_CSI_CTRL_INTERVAL,
- 
- 	MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
- 
-@@ -96,6 +95,7 @@ enum mtk_vendor_attr_csi_data {
- 	MTK_VENDOR_ATTR_CSI_DATA_BW,
- 	MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
- 	MTK_VENDOR_ATTR_CSI_DATA_TA,
-+	MTK_VENDOR_ATTR_CSI_DATA_NUM,
- 	MTK_VENDOR_ATTR_CSI_DATA_I,
- 	MTK_VENDOR_ATTR_CSI_DATA_Q,
- 	MTK_VENDOR_ATTR_CSI_DATA_INFO,
-@@ -106,7 +106,7 @@ enum mtk_vendor_attr_csi_data {
- 	MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
- 	MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
- 	MTK_VENDOR_ATTR_CSI_DATA_MODE,
--	MTK_VENDOR_ATTR_CSI_DATA_H_IDX,
-+	MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO,
- 
- 	/* keep last */
- 	NUM_MTK_VENDOR_ATTRS_CSI_DATA,
-@@ -281,23 +281,40 @@ enum mtk_vendor_attr_beacon_ctrl {
- 		NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
- };
- 
--#define CSI_MAX_COUNT 256
-+#define CSI_BW20_DATA_COUNT	64
-+#define CSI_BW40_DATA_COUNT	128
-+#define CSI_BW80_DATA_COUNT	256
-+#define CSI_BW160_DATA_COUNT	512
-+#define CSI_BW320_DATA_COUNT	1024
- #define ETH_ALEN 6
- 
- struct csi_data {
--	s16 data_i[CSI_MAX_COUNT];
--	s16 data_q[CSI_MAX_COUNT];
-+	u8 ch_bw;
-+	u16 data_num;
-+	s16 data_i[CSI_BW320_DATA_COUNT];
-+	s16 data_q[CSI_BW320_DATA_COUNT];
-+	u8 band;
- 	s8 rssi;
- 	u8 snr;
- 	u32 ts;
- 	u8 data_bw;
- 	u8 pri_ch_idx;
- 	u8 ta[ETH_ALEN];
--	u32 info;
-+	u32 ext_info;
- 	u8 rx_mode;
--	u32 h_idx;
-+	u32 chain_info;
- 	u16 tx_idx;
- 	u16 rx_idx;
-+	u32 segment_num;
-+	u8 remain_last;
-+	u16 pkt_sn;
-+	u8 tr_stream;
-+};
-+
-+struct csi_resp_data {
-+	u16 usr_need_cnt;
-+	u16 buf_cnt;
-+	struct csi_data *csi_buf;
- };
- 
- #define AIR_MONITOR_MAX_ENTRY 16
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index ba61f5842..7efb5e342 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5342,6 +5342,22 @@ struct wpa_driver_ops {
- #ifdef CONFIG_IEEE80211BE
- 	int (*get_mld_addr)(void *priv, u8 *addr);
- #endif
-+	/**
-+	 * csi_set - Set csi related mode and parameter
-+	 * @priv: Private driver interface data
-+	 * @mode: Csi mode parameter
-+	 * @cfg: Csi config parameter
-+	 * @v1: Value1
-+	 * @v2: Value2
-+	 * @mac: Station mac for station filter
-+	 */
-+	int (*csi_set)(void *priv, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac);
-+	/**
-+	* csi_dump - Dump csi data to json file
-+	* @priv: Private driver interface data
-+	* @dump_buf: Dump_struct that store csi data and related info
-+	*/
-+	int (*csi_dump)(void *priv, void *dump_buf);
- };
- 
- /**
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index df4a7ec41..39b45ef4b 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -161,6 +161,35 @@ pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
- 	[MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
- };
- 
-+static struct nla_policy csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
-+	[MTK_VENDOR_ATTR_CSI_CTRL_CFG] = { .type = NLA_NESTED },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2] = { .type = NLA_U32 },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR] = { .type = NLA_NESTED },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM] = { .type = NLA_U16 },
-+	[MTK_VENDOR_ATTR_CSI_CTRL_DATA] = { .type = NLA_NESTED },
-+};
-+
-+static struct nla_policy csi_data_policy[NUM_MTK_VENDOR_ATTRS_CSI_DATA] = {
-+	[MTK_VENDOR_ATTR_CSI_DATA_VER] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_TS] = { .type = NLA_U32 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_RSSI] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_SNR] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_BW] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_TA] = { .type = NLA_NESTED },
-+	[MTK_VENDOR_ATTR_CSI_DATA_NUM] = { .type = NLA_U32 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_I] = { .type = NLA_NESTED },
-+	[MTK_VENDOR_ATTR_CSI_DATA_Q] = { .type = NLA_NESTED },
-+	[MTK_VENDOR_ATTR_CSI_DATA_INFO] = { .type = NLA_U32 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_TX_ANT] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_RX_ANT] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_MODE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO] = { .type = NLA_U32 },
-+};
-+
- static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
- {
- 	struct nl_sock *handle;
-@@ -15192,6 +15221,210 @@ static int nl80211_get_mld_addr(void *priv, u8 *addr)
- }
- #endif
- 
-+static int
-+nl80211_csi_set(void *priv, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	void *tb1, *tb2;
-+	int ret, i;
-+
-+	if (!drv->mtk_csi_vendor_cmd_avail) {
-+		wpa_printf(MSG_ERROR,
-+			"nl80211: Driver does not support csi");
-+		return 0;
-+	}
-+
-+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
-+	if (!msg)
-+		goto fail;
-+
-+	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+			nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+			MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL))
-+		goto fail;
-+
-+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
-+	if (!data)
-+		goto fail;
-+
-+	tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG | NLA_F_NESTED);
-+	if (!tb1)
-+		goto fail;
-+
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE, mode);
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE, cfg);
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1, v1);
-+	nla_put_u32(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2, v2);
-+
-+	nla_nest_end(msg, tb1);
-+
-+	if (mac) {
-+		tb2 = nla_nest_start(msg, MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR | NLA_F_NESTED);
-+		if (!tb2)
-+			goto fail;
-+
-+		for (i = 0; i < ETH_ALEN; i++)
-+			nla_put_u8(msg, i, mac[i]);
-+
-+		nla_nest_end(msg, tb2);
-+	}
-+
-+	nla_nest_end(msg, data);
-+
-+	ret = send_and_recv_cmd(drv, msg);
-+
-+	if (ret)
-+		wpa_printf(MSG_ERROR, "Failed to set csi. ret=%d (%s)",
-+			ret, strerror(-ret));
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+
-+}
-+
-+static int
-+mt76_csi_dump_cb(struct nl_msg *msg, void *arg)
-+{
-+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-+	struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_CSI_CTRL];
-+	struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_CSI_DATA];
-+	struct nlattr *attr, *cur, *data;
-+	int len = 0, rem, idx;
-+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-+	struct csi_resp_data *csi_resp = (struct csi_resp_data *)arg;
-+	struct csi_data *c = csi_resp->csi_buf;
-+
-+	c += csi_resp->buf_cnt;
-+
-+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-+		genlmsg_attrlen(gnlh, 0), NULL);
-+
-+	attr = tb[NL80211_ATTR_VENDOR_DATA];
-+	if (!attr)
-+		return NL_SKIP;
-+
-+	nla_parse_nested(tb1, MTK_VENDOR_ATTR_CSI_CTRL_MAX,
-+			attr, csi_ctrl_policy);
-+
-+	if (!tb1[MTK_VENDOR_ATTR_CSI_CTRL_DATA])
-+		return NL_SKIP;
-+
-+	nla_parse_nested(tb2, MTK_VENDOR_ATTR_CSI_DATA_MAX,
-+			tb1[MTK_VENDOR_ATTR_CSI_CTRL_DATA], csi_data_policy);
-+
-+	if (!(tb2[MTK_VENDOR_ATTR_CSI_DATA_VER] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_TS] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_RSSI] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_SNR] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_BW] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_TA] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_I] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_Q] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_INFO] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_MODE] &&
-+	      tb2[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO])) {
-+		fprintf(stderr, "Attributes error for CSI data\n");
-+		return NL_SKIP;
-+	}
-+
-+	c->rssi = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_RSSI]);
-+	c->snr = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_SNR]);
-+	c->data_bw = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_BW]);
-+	c->pri_ch_idx = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX]);
-+	c->rx_mode = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_MODE]);
-+
-+	c->tx_idx = nla_get_u16(tb2[MTK_VENDOR_ATTR_CSI_DATA_TX_ANT]);
-+	c->rx_idx = nla_get_u16(tb2[MTK_VENDOR_ATTR_CSI_DATA_RX_ANT]);
-+
-+	c->ext_info = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_INFO]);
-+	c->chain_info = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO]);
-+
-+	c->ts = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_TS]);
-+
-+	c->data_num = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_NUM]);
-+
-+	idx = 0;
-+	nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_TA], rem) {
-+		if (idx < ETH_ALEN)
-+			c->ta[idx++] = nla_get_u8(cur);
-+	}
-+
-+	idx = 0;
-+	nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_I], rem) {
-+		if (idx < c->data_num)
-+			c->data_i[idx++] = nla_get_u16(cur);
-+	}
-+
-+	idx = 0;
-+	nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_Q], rem) {
-+		if (idx < c->data_num)
-+			c->data_q[idx++] = nla_get_u16(cur);
-+	}
-+
-+	csi_resp->buf_cnt++;
-+
-+	return NL_SKIP;
-+}
-+
-+static int
-+nl80211_csi_dump(void *priv, void *dump_buf)
-+{
-+	struct i802_bss *bss = priv;
-+	struct wpa_driver_nl80211_data *drv = bss->drv;
-+	struct nl_msg *msg;
-+	struct nlattr *data;
-+	int ret;
-+	struct csi_resp_data *csi_resp;
-+	u16 pkt_num, i;
-+
-+	if (!drv->mtk_csi_vendor_cmd_avail) {
-+		wpa_printf(MSG_INFO,
-+			"nl80211: Driver does not support csi");
-+		return 0;
-+	}
-+
-+	csi_resp = (struct csi_resp_data *)dump_buf;
-+	pkt_num =  csi_resp->usr_need_cnt;
-+
-+	if (pkt_num > 3000)
-+		return -EINVAL;
-+
-+#define CSI_DUMP_PER_NUM	3
-+	for (i = 0; i < pkt_num / CSI_DUMP_PER_NUM; i++) {
-+		msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR);
-+		if (!msg)
-+			goto fail;
-+
-+		if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
-+				nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-+				MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL))
-+			goto fail;
-+
-+		data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
-+		if (!data)
-+			goto fail;
-+
-+		nla_put_u16(msg, MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM, CSI_DUMP_PER_NUM);
-+
-+		nla_nest_end(msg, data);
-+
-+		ret = send_and_recv_resp(drv, msg, mt76_csi_dump_cb, dump_buf);
-+	}
-+
-+	return ret;
-+
-+fail:
-+	nlmsg_free(msg);
-+	return -ENOBUFS;
-+}
-+
- const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- 	.name = "nl80211",
- 	.desc = "Linux nl80211/cfg80211",
-@@ -15375,4 +15608,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
- #ifdef CONFIG_IEEE80211BE
- 	.get_mld_addr = nl80211_get_mld_addr,
- #endif
-+	.csi_set = nl80211_csi_set,
-+	.csi_dump = nl80211_csi_dump,
- };
-diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
-index a0a62e536..b710b50cd 100644
---- a/src/drivers/driver_nl80211.h
-+++ b/src/drivers/driver_nl80211.h
-@@ -212,6 +212,7 @@ struct wpa_driver_nl80211_data {
- 	unsigned int mtk_background_radar_vendor_cmd_avail:1;
- 	unsigned int mtk_pp_vendor_cmd_avail:1;
- 	unsigned int mtk_beacon_ctrl_vendor_cmd_avail:1;
-+	unsigned int mtk_csi_vendor_cmd_avail:1;
- 
- 	u32 ignore_next_local_disconnect;
- 	u32 ignore_next_local_deauth;
-diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
-index f3e3d52e2..8688b849f 100644
---- a/src/drivers/driver_nl80211_capa.c
-+++ b/src/drivers/driver_nl80211_capa.c
-@@ -1173,6 +1173,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
- 				case MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL :
- 					drv->mtk_beacon_ctrl_vendor_cmd_avail = 1;
- 					break;
-+				case MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL:
-+					drv->mtk_csi_vendor_cmd_avail = 1;
-+					break;
- 				}
- 			}
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0099-fixup-mtk-wifi-hostapd-add-wds-mlo-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0099-fixup-mtk-wifi-hostapd-add-wds-mlo-support.patch
deleted file mode 100644
index c27e065..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0099-fixup-mtk-wifi-hostapd-add-wds-mlo-support.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From a5c0e5c09398a247236d73078a4f86a960a97e34 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Mon, 8 Apr 2024 16:27:51 +0800
-Subject: [PATCH 099/104] fixup! mtk: wifi: hostapd: add wds mlo support
-
-The latest get_hapd_bssid return hapd only if link id is matched.
-However,the hostapd_rx_from_unknown_sta does not have link
-information so it cannot get hapd.
-
-Modify get_hapd_bssid to ignore link id when link id is -1.
-
-Without this patch, wds mode cannot work and the AP would not be
-aware that station is using 4 address.
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- src/ap/drv_callbacks.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
-index 27c7555a1..82973c5e4 100644
---- a/src/ap/drv_callbacks.c
-+++ b/src/ap/drv_callbacks.c
-@@ -1779,7 +1779,7 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
- 		if (ether_addr_equal(bssid, hapd->own_addr) ||
- 		    (hapd->conf->mld_ap &&
- 		     ether_addr_equal(bssid, hapd->mld->mld_addr) &&
--		     link_id == hapd->mld_link_id)) {
-+		     (link_id == hapd->mld_link_id || link_id == -1))) {
- 			return hapd;
- 		} else if (hapd->conf->mld_ap) {
- 			for_each_mld_link(p_hapd, hapd) {
-@@ -1788,7 +1788,7 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
- 
- 				if (ether_addr_equal(bssid, p_hapd->own_addr) ||
- 				    (ether_addr_equal(bssid, p_hapd->mld->mld_addr) &&
--				     link_id == p_hapd->mld_link_id))
-+				     (link_id == p_hapd->mld_link_id || link_id == -1)))
- 					return p_hapd;
- 			}
- 		}
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0099-mtk-hostapd-add-support-for-DFS-channel-switching-wi.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0099-mtk-hostapd-add-support-for-DFS-channel-switching-wi.patch
new file mode 100644
index 0000000..3274297
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0099-mtk-hostapd-add-support-for-DFS-channel-switching-wi.patch
@@ -0,0 +1,539 @@
+From 6805573a7891d68ef5d71c6dcf362ffbd2119240 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 15 Nov 2023 15:06:00 +0800
+Subject: [PATCH 099/126] mtk: hostapd: add support for DFS channel switching
+ with csa sent
+
+Add support for DFS channel switch
+
+When searching for a DFS channel for a background radar channel switch,
+we should not base the selection on the cap of the original channel.
+Otherwise, the selected channel may become invalid when the bandwidth is changed.
+For example, iface->conf->secondary_channel is set to 0 when operating on BW 20.
+Therefore, if the user tries to switch from BW 20 to BW 80, the pre-selected channel
+for BW 80 will not be checked by dfs_is_chan_allowed in dfs_find_channel.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/ctrl_iface.c   | 105 +++++++++++++++---------
+ src/ap/beacon.c        |   5 ++
+ src/ap/ctrl_iface_ap.c |   5 +-
+ src/ap/dfs.c           | 182 +++++++++++++++++++++++++++++++++++------
+ src/ap/dfs.h           |  11 ++-
+ src/ap/ieee802_11.c    |   5 ++
+ 6 files changed, 243 insertions(+), 70 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 14a0483bf..3943cb3aa 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -2817,12 +2817,12 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ {
+ #ifdef NEED_AP_MLME
+ 	struct hostapd_hw_modes *mode = iface->current_mode;
+-	struct csa_settings settings;
++	struct csa_settings settings, background_settings;
+ 	int ret;
+-	int dfs_range = 0;
++	int freq, state;
++	int bandwidth, oper_chwidth;
++	bool background_radar, bw_changed, cac_required = false;
+ 	unsigned int i;
+-	int bandwidth;
+-	u8 chan;
+ 	unsigned int num_err = 0;
+ 	int err = 0;
+ 
+@@ -2853,21 +2853,28 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ 	switch (settings.freq_params.bandwidth) {
+ 	case 40:
+ 		bandwidth = CHAN_WIDTH_40;
++		oper_chwidth = CONF_OPER_CHWIDTH_USE_HT;
+ 		break;
+ 	case 80:
+-		if (settings.freq_params.center_freq2)
++		if (settings.freq_params.center_freq2) {
+ 			bandwidth = CHAN_WIDTH_80P80;
+-		else
++			oper_chwidth = CONF_OPER_CHWIDTH_80P80MHZ;
++		} else {
+ 			bandwidth = CHAN_WIDTH_80;
++			oper_chwidth = CONF_OPER_CHWIDTH_80MHZ;
++		}
+ 		break;
+ 	case 160:
+ 		bandwidth = CHAN_WIDTH_160;
++		oper_chwidth = CONF_OPER_CHWIDTH_160MHZ;
+ 		break;
+ 	case 320:
+ 		bandwidth = CHAN_WIDTH_320;
++		oper_chwidth = CONF_OPER_CHWIDTH_320MHZ;
+ 		break;
+ 	default:
+ 		bandwidth = CHAN_WIDTH_20;
++		oper_chwidth = CONF_OPER_CHWIDTH_USE_HT;
+ 		break;
+ 	}
+ 
+@@ -2882,41 +2889,29 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ 	}
+ 
+ 	if (settings.freq_params.center_freq1)
+-		dfs_range += hostapd_is_dfs_overlap(
+-			iface, bandwidth, settings.freq_params.center_freq1);
++		freq = settings.freq_params.center_freq1;
+ 	else
+-		dfs_range += hostapd_is_dfs_overlap(
+-			iface, bandwidth, settings.freq_params.freq);
+-
+-	if (settings.freq_params.center_freq2)
+-		dfs_range += hostapd_is_dfs_overlap(
+-			iface, bandwidth, settings.freq_params.center_freq2);
+-
+-	if (dfs_range) {
+-		ret = ieee80211_freq_to_chan(settings.freq_params.freq, &chan);
+-		if (ret == NUM_HOSTAPD_MODES) {
+-			wpa_printf(MSG_ERROR,
+-				   "Failed to get channel for (freq=%d, sec_channel_offset=%d, bw=%d)",
+-				   settings.freq_params.freq,
+-				   settings.freq_params.sec_channel_offset,
+-				   settings.freq_params.bandwidth);
+-			return -1;
+-		}
+-
+-		settings.freq_params.channel = chan;
+-
+-		wpa_printf(MSG_DEBUG,
+-			   "DFS/CAC to (channel=%u, freq=%d, sec_channel_offset=%d, bw=%d, center_freq1=%d)",
+-			   settings.freq_params.channel,
+-			   settings.freq_params.freq,
+-			   settings.freq_params.sec_channel_offset,
+-			   settings.freq_params.bandwidth,
+-			   settings.freq_params.center_freq1);
+-
+-		/* Perform CAC and switch channel */
+-		iface->is_ch_switch_dfs = true;
+-		hostapd_switch_channel_fallback(iface, &settings.freq_params);
+-		return 0;
++		freq = settings.freq_params.freq;
++
++	bw_changed = oper_chwidth != hostapd_get_oper_chwidth(iface->conf);
++	state = hostapd_dfs_get_target_state(iface, bandwidth, freq,
++					     settings.freq_params.center_freq2);
++	switch (state) {
++	case HOSTAPD_CHAN_DFS_USABLE:
++		cac_required = true;
++		/* fallthrough */
++	case HOSTAPD_CHAN_DFS_AVAILABLE:
++		background_radar = hostapd_dfs_handle_csa(iface, &settings,
++							  &background_settings,
++							  cac_required,
++							  bw_changed);
++		break;
++	case HOSTAPD_CHAN_DFS_UNAVAILABLE:
++		wpa_printf(MSG_INFO,
++			   "chanswitch: target channel is UNAVAILABLE, so stop switching");
++		return -1;
++	default:
++		break;
+ 	}
+ 
+ 	for (i = 0; i < iface->num_bss; i++) {
+@@ -2944,6 +2939,36 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ #endif /* CONFIG_IEEE80211BE */
+ 	}
+ 
++	if (background_radar) {
++		u8 seg0 = 0, seg1 = 0;
++
++		ieee80211_freq_to_chan(background_settings.freq_params.center_freq1, &seg0);
++		ieee80211_freq_to_chan(background_settings.freq_params.center_freq2, &seg1);
++		ret = hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
++					    background_settings.freq_params.freq,
++					    background_settings.freq_params.channel,
++					    background_settings.freq_params.ht_enabled,
++					    background_settings.freq_params.vht_enabled,
++					    background_settings.freq_params.he_enabled,
++					    background_settings.freq_params.eht_enabled,
++					    background_settings.freq_params.sec_channel_offset,
++					    oper_chwidth, seg0, seg1, true);
++		if (ret) {
++			wpa_printf(MSG_ERROR, "Background radar start dfs cac failed, %d",
++				   ret);
++			iface->radar_background.channel = -1;
++			return -1;
++		}
++
++		/* Cache background radar parameters. */
++		iface->radar_background.channel = background_settings.freq_params.channel;
++		iface->radar_background.secondary_channel =
++			background_settings.freq_params.sec_channel_offset;
++		iface->radar_background.freq = background_settings.freq_params.freq;
++		iface->radar_background.centr_freq_seg0_idx = seg0;
++		iface->radar_background.centr_freq_seg1_idx = seg1;
++	}
++
+ 	return (iface->num_bss == num_err) ? ret : 0;
+ #else /* NEED_AP_MLME */
+ 	return -1;
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 1ffe2fb56..36f4feb3a 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -2794,6 +2794,11 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
+ 		return -1;
+ 	}
+ 
++	if (iface->cac_started) {
++		wpa_printf(MSG_DEBUG, "Ignore set beacons during CAC period");
++		return 0;
++	}
++
+ 	hapd->beacon_set_done = 1;
+ 
+ 	if (ieee802_11_build_ap_params(hapd, &params) < 0)
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index 20d426560..b0ee00b90 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -1099,6 +1099,7 @@ int hostapd_parse_csa_settings(const char *pos,
+ 			       struct csa_settings *settings)
+ {
+ 	char *end;
++	int ret;
+ 
+ 	os_memset(settings, 0, sizeof(*settings));
+ 	settings->cs_count = strtol(pos, &end, 10);
+@@ -1108,7 +1109,9 @@ int hostapd_parse_csa_settings(const char *pos,
+ 	}
+ 
+ 	settings->freq_params.freq = atoi(end);
+-	if (settings->freq_params.freq == 0) {
++	ret = ieee80211_freq_to_chan(settings->freq_params.freq,
++				     (u8 *)&settings->freq_params.channel);
++	if (ret == NUM_HOSTAPD_MODES) {
+ 		wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided");
+ 		return -1;
+ 	}
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 697e6364c..5e9a2a4ce 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -242,22 +242,22 @@ static int is_in_chanlist(struct hostapd_iface *iface,
+  */
+ int dfs_find_channel(struct hostapd_iface *iface,
+ 		     struct hostapd_channel_data **ret_chan,
+-		     int idx, enum dfs_channel_type type)
++		     int n_chans, int idx, enum dfs_channel_type type)
+ {
+ 	struct hostapd_hw_modes *mode;
+ 	struct hostapd_channel_data *chan;
+-	int i, channel_idx = 0, n_chans, n_chans1;
++	int i, channel_idx = 0, n_chans1;
+ 
+ 	mode = iface->current_mode;
+-	n_chans = dfs_get_used_n_chans(iface, &n_chans1);
++	if (!n_chans)
++		n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+ 
+ 	wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
+ 	for (i = 0; i < mode->num_channels; i++) {
+ 		chan = &mode->channels[i];
+ 
+ 		/* Skip HT40/VHT incompatible channels */
+-		if (iface->conf->ieee80211n &&
+-		    iface->conf->secondary_channel &&
++		if (iface->conf->ieee80211n && n_chans > 1 &&
+ 		    (!dfs_is_chan_allowed(chan, n_chans) ||
+ 		     !(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P))) {
+ 			wpa_printf(MSG_DEBUG,
+@@ -550,7 +550,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+ 		return NULL;
+ 
+ 	/* Get the count first */
+-	num_available_chandefs = dfs_find_channel(iface, NULL, 0, type);
++	num_available_chandefs = dfs_find_channel(iface, NULL, 0, 0, type);
+ 	wpa_printf(MSG_DEBUG, "DFS: num_available_chandefs=%d",
+ 		   num_available_chandefs);
+ 	if (num_available_chandefs == 0)
+@@ -573,7 +573,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+ 	chan_idx = _rand % num_available_chandefs;
+ 	wpa_printf(MSG_DEBUG, "DFS: Picked random entry from the list: %d/%d",
+ 		   chan_idx, num_available_chandefs);
+-	dfs_find_channel(iface, &chan, chan_idx, type);
++	dfs_find_channel(iface, &chan, 0, chan_idx, type);
+ 	if (!chan) {
+ 		wpa_printf(MSG_DEBUG, "DFS: no random channel found");
+ 		return NULL;
+@@ -603,7 +603,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+ 		for (i = 0; i < num_available_chandefs - 1; i++) {
+ 			/* start from chan_idx + 1, end when chan_idx - 1 */
+ 			chan_idx2 = (chan_idx + 1 + i) % num_available_chandefs;
+-			dfs_find_channel(iface, &chan2, chan_idx2, type);
++			dfs_find_channel(iface, &chan2, 0, chan_idx2, type);
+ 			if (chan2 && abs(chan2->chan - chan->chan) > 12) {
+ 				/* two channels are not adjacent */
+ 				sec_chan_idx_80p80 = chan2->chan;
+@@ -1367,10 +1367,10 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ 			 * another radio.
+ 			 */
+ 			if (iface->state != HAPD_IFACE_ENABLED &&
+-			    hostapd_is_dfs_chan_available(iface)) {
++			    hostapd_is_dfs_chan_available(iface))
+ 				hostapd_setup_interface_complete(iface, 0);
+-				iface->cac_started = 0;
+-			}
++
++			iface->cac_started = 0;
+ 
+ 			/*
+ 			 * When background radar is enabled but the CAC completion
+@@ -1386,6 +1386,13 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ 		iface->radar_background.temp_ch = 0;
+ 		iface->radar_background.expand_ch = 0;
+ 		hostapd_dfs_update_background_chain(iface);
++	} else if (iface->state == HAPD_IFACE_ENABLED) {
++		int i;
++
++		iface->cac_started = 0;
++		/* Clear all CSA flags once channel switch to DFS channel fails */
++		for (i = 0; i < iface->num_bss; i++)
++			iface->bss[i]->csa_in_progress = 0;
+ 	}
+ 
+ 	iface->radar_detected = false;
+@@ -1410,6 +1417,9 @@ int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
+ 	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+ 		      cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
+ 
++	if (dfs_use_radar_background(iface) && iface->radar_background.channel == -1)
++		hostapd_dfs_update_background_chain(iface);
++
+ 	return 0;
+ }
+ 
+@@ -1798,7 +1808,8 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ 		/* This is called when the driver indicates that an offloaded
+ 		 * DFS has started CAC. radar_detected might be set for previous
+ 		 * DFS channel. Clear it for this new CAC process. */
+-		hostapd_set_state(iface, HAPD_IFACE_DFS);
++		if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
++			hostapd_set_state(iface, HAPD_IFACE_DFS);
+ 		iface->cac_started = 1;
+ 
+ 		/* Clear radar_detected in case it is for the previous
+@@ -1866,14 +1877,15 @@ int hostapd_handle_dfs_offload(struct hostapd_iface *iface)
+ }
+ 
+ 
+-int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
+-			   int center_freq)
++int hostapd_dfs_get_target_state(struct hostapd_iface *iface, enum chan_width width,
++				 int center_freq, int center_freq2)
+ {
+ 	struct hostapd_channel_data *chan;
+ 	struct hostapd_hw_modes *mode = iface->current_mode;
+-	int half_width;
+-	int res = 0;
++	int half_width, chan_state, state = 0;
++	int upper, lower;
+ 	int i;
++	bool in_range;
+ 
+ 	if (!iface->conf->ieee80211h || !mode ||
+ 	    mode->mode != HOSTAPD_MODE_IEEE80211A)
+@@ -1906,18 +1918,136 @@ int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
+ 		if (!(chan->flag & HOSTAPD_CHAN_RADAR))
+ 			continue;
+ 
+-		if ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+-		    HOSTAPD_CHAN_DFS_AVAILABLE)
+-			continue;
++		upper = chan->freq + half_width;
++		lower = chan->freq - half_width;
++		in_range = (lower < center_freq && center_freq < upper) ||
++			   (center_freq2 && (lower < center_freq2 && center_freq2 < upper));
++		if (in_range) {
++			chan_state = chan->flag & HOSTAPD_CHAN_DFS_MASK;
++			switch (chan_state) {
++			case HOSTAPD_CHAN_DFS_USABLE:
++				state = HOSTAPD_CHAN_DFS_USABLE;
++				break;
++			case HOSTAPD_CHAN_DFS_AVAILABLE:
++				if (state != HOSTAPD_CHAN_DFS_USABLE)
++					state = HOSTAPD_CHAN_DFS_AVAILABLE;
++				break;
++			case HOSTAPD_CHAN_DFS_UNKNOWN:
++				wpa_printf(MSG_WARNING, "chan %d DFS state: UNKNOWN",
++					   chan->freq);
++				/* fallthrough */
++			case HOSTAPD_CHAN_DFS_UNAVAILABLE:
++			default:
++				return HOSTAPD_CHAN_DFS_UNAVAILABLE;
++			}
++		}
++	}
+ 
+-		if (center_freq - chan->freq < half_width &&
+-		    chan->freq - center_freq < half_width)
+-			res++;
++	wpa_printf(MSG_DEBUG, "freq range (%d, %d) has DFS state %d",
++		   center_freq - half_width, center_freq + half_width, state);
++
++	return state;
++}
++
++
++static struct hostapd_channel_data *
++dfs_get_csa_channel(struct hostapd_iface *iface,
++		    int n_chans, int cur_center,
++		    enum dfs_channel_type type)
++{
++	struct hostapd_channel_data *chan;
++	int avail_chan_num;
++	u32 _rand, idx;
++
++	if (os_get_random((u8 *)&_rand, sizeof(_rand)) < 0)
++		return NULL;
++
++	avail_chan_num = dfs_find_channel(iface, NULL, n_chans, 0, type);
++	if (!avail_chan_num)
++		return NULL;
++
++	idx = _rand % avail_chan_num;
++	dfs_find_channel(iface, &chan, n_chans, idx, type);
++	if (cur_center == chan->freq + (n_chans - 1) * 10) {
++		if (avail_chan_num == 1)
++			return NULL;
++
++		/* Get the next channel if the found channel is same as current channel */
++		idx = (idx + 1) % avail_chan_num;
++		dfs_find_channel(iface, &chan, n_chans, idx, type);
+ 	}
+ 
+-	wpa_printf(MSG_DEBUG, "DFS CAC required: (%d, %d): in range: %s",
+-		   center_freq - half_width, center_freq + half_width,
+-		   res ? "yes" : "no");
++	return chan;
++}
++
+ 
+-	return res;
++/*
++ * DFS handler for CSA
++ * 1  - update background radar with the filled setting
++ * 0  - background radar is not enabled / background radar remain at the same channel /
++ *	disable background radar
++ */
++int hostapd_dfs_handle_csa(struct hostapd_iface *iface,
++			   struct csa_settings *settings,
++			   struct csa_settings *background_settings,
++			   bool cac_required, bool bw_changed)
++{
++	struct hostapd_channel_data *chan;
++	struct hostapd_freq_params *freq_params = &settings->freq_params;
++	int center = settings->freq_params.center_freq1;
++	int background_center = 5000 + iface->radar_background.centr_freq_seg0_idx * 5;
++	int n_chans = settings->freq_params.bandwidth / 20;
++	bool update_background = false;
++
++	if (!dfs_use_radar_background(iface)) {
++		settings->cs_count = 5;
++		settings->block_tx = cac_required;
++		return 0;
++	}
++
++	if (!cac_required) {
++		if (!bw_changed && center != background_center)
++			return 0;
++		/* Update background radar due to bw change or channel overlapping */
++		update_background = true;
++	} else {
++		/*
++		 * Get available channel for main channel if background radar
++		 * is ready (no CAC in progress).
++		 * If no available channel exists or background radar is not ready,
++		 * then perform the CAC of the target channel on the main channel.
++		 * Also, select an usable channel for background radar if no
++		 * available channel exists.
++		 */
++		if (!iface->radar_background.cac_started) {
++			iface->radar_background.temp_ch = 1;
++			chan = dfs_get_csa_channel(iface, n_chans, 0, DFS_AVAILABLE);
++			if (!chan)
++				update_background = true;
++		} else {
++			iface->radar_background.temp_ch = 0;
++			return 0;
++		}
++	}
++
++	if (update_background) {
++		chan = dfs_get_csa_channel(iface, n_chans, center, DFS_NO_CAC_YET);
++		if (!chan)
++			goto bkg_disable;
++		freq_params = &background_settings->freq_params;
++		iface->radar_background.temp_ch = 0;
++	}
++
++	memcpy(background_settings, settings, sizeof(*settings));
++	freq_params->freq = chan->freq;
++	freq_params->channel = chan->chan;
++	freq_params->sec_channel_offset = 1;
++	freq_params->center_freq1 = chan->freq + (n_chans - 1) * 10;
++	freq_params->center_freq2 = 0;
++
++	return 1;
++
++bkg_disable:
++	iface->radar_background.channel = -1;
++	return 0;
+ }
+diff --git a/src/ap/dfs.h b/src/ap/dfs.h
+index a1a2be5ec..adb09deaf 100644
+--- a/src/ap/dfs.h
++++ b/src/ap/dfs.h
+@@ -42,16 +42,21 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ 			  int ht_enabled, int chan_offset, int chan_width,
+ 			  int cf1, int cf2);
+ int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
+-int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
+-			   int center_freq);
++int hostapd_dfs_get_target_state(struct hostapd_iface *iface, enum chan_width width,
++				 int center_freq, int center_freq2);
+ int dfs_find_channel(struct hostapd_iface *iface,
+ 		     struct hostapd_channel_data **ret_chan,
+-		     int idx, enum dfs_channel_type type);
++		     int n_chans, int idx, enum dfs_channel_type type);
+ void dfs_adjust_center_freq(struct hostapd_iface *iface,
+ 			    struct hostapd_channel_data *chan,
+ 			    int secondary_channel,
+ 			    int sec_chan_idx_80p80,
+ 			    u8 *oper_centr_freq_seg0_idx,
+ 			    u8 *oper_centr_freq_seg1_idx);
++int hostapd_dfs_handle_csa(struct hostapd_iface *iface,
++			   struct csa_settings *settings,
++			   struct csa_settings *background_settings,
++			   bool cac_required, bool bw_changed);
++
+ 
+ #endif /* DFS_H */
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 09f033e13..7dac7a896 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -6382,6 +6382,11 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+ 		return 1;
+ 	}
+ 
++	if (hapd->iface->cac_started) {
++		wpa_printf(MSG_DEBUG, "MGMT: Ignore management frame during CAC");
++		return 1;
++	}
++
+ 	if (stype == WLAN_FC_STYPE_PROBE_REQ) {
+ 		handle_probe_req(hapd, mgmt, len, ssi_signal);
+ 		return 1;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0100-Revert-AP-MLD-Add-MLO-Link-KDE-for-each-affiliated-l.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0100-Revert-AP-MLD-Add-MLO-Link-KDE-for-each-affiliated-l.patch
new file mode 100644
index 0000000..9fcefe2
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0100-Revert-AP-MLD-Add-MLO-Link-KDE-for-each-affiliated-l.patch
@@ -0,0 +1,49 @@
+From b5bf8726bf81b90d628dd398579309fef5b6651b Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 6 Jun 2024 22:11:45 +0800
+Subject: [PATCH 100/126] Revert "AP MLD: Add MLO Link KDE for each affiliated
+ link in EAPOL-Key 3/4"
+
+This reverts commit df59880042cd8d9b4bdd2dce6de0a6e233be1b64.
+
+Please noted that this commit is a workaround for MTK STA IoT issue within WiFi7 R1 certification.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+
+---
+ src/ap/wpa_auth.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
+index 3a1d288dd..6cb5a4be7 100644
+--- a/src/ap/wpa_auth.c
++++ b/src/ap/wpa_auth.c
+@@ -4484,6 +4484,12 @@ static size_t wpa_auth_ml_kdes_len(struct wpa_state_machine *sm)
+ 		struct wpa_authenticator *wpa_auth;
+ 		const u8 *ie;
+ 
++		/* FIXME: This is a temporary workaround for MTK
++		 * sta IoT issue in WiFi7 cert.
++		 */
++		if (!sm->mld_links[link_id].valid)
++			continue;
++
+ 		wpa_auth = wpa_get_link_auth(sm->wpa_auth, link_id);
+ 		if (!wpa_auth)
+ 			continue;
+@@ -4548,6 +4554,12 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
+ 		const u8 *rsne, *rsnxe, *rsnoe, *rsno2e, *rsnxoe;
+ 		size_t rsne_len, rsnxe_len, rsnoe_len, rsno2e_len, rsnxoe_len;
+ 
++		/* FIXME: This is a temporary workaround for MTK
++		 * sta IoT issue in WiFi7 cert.
++		 */
++		if (!sm->mld_links[link_id].valid)
++			continue;
++
+ 		wpa_auth = wpa_get_link_auth(sm->wpa_auth, link_id);
+ 		if (!wpa_auth)
+ 			continue;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0100-mtk-hostapd-MLD-find-partner-links-by-BSSID-and-SSID.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0100-mtk-hostapd-MLD-find-partner-links-by-BSSID-and-SSID.patch
deleted file mode 100644
index 7e2c70b..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0100-mtk-hostapd-MLD-find-partner-links-by-BSSID-and-SSID.patch
+++ /dev/null
@@ -1,89 +0,0 @@
-From 30cfdaf10a5b3bb2a173a096b2c708c4a18d55bc Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Mon, 8 Apr 2024 14:34:36 +0800
-Subject: [PATCH 100/104] mtk: hostapd: MLD: find partner links by BSSID and
- SSID
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- wpa_supplicant/bss.c |  8 ++++++--
- wpa_supplicant/sme.c | 14 ++++++++------
- 2 files changed, 14 insertions(+), 8 deletions(-)
-
-diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
-index 289035310..ae0e61bc3 100644
---- a/wpa_supplicant/bss.c
-+++ b/wpa_supplicant/bss.c
-@@ -1529,8 +1529,12 @@ wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s,
- 			wpa_printf(MSG_DEBUG,
- 				   "MLD: Reported link not part of MLD");
- 		} else if (!(BIT(link_id) & *seen)) {
--			struct wpa_bss *neigh_bss =
--				wpa_bss_get_bssid(wpa_s, pos + 1);
-+			struct wpa_bss *neigh_bss;
-+
-+			if (ssid)
-+				neigh_bss = wpa_bss_get(wpa_s, pos + 1, ssid->ssid, ssid->ssid_len);
-+			else
-+				neigh_bss = wpa_bss_get_bssid(wpa_s, pos + 1);
- 
- 			*seen |= BIT(link_id);
- 			wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link ID=%u",
-diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
-index ef258fadc..0b4b8e3ce 100644
---- a/wpa_supplicant/sme.c
-+++ b/wpa_supplicant/sme.c
-@@ -390,7 +390,8 @@ static void wpas_ml_handle_removed_links(struct wpa_supplicant *wpa_s,
- 
- #ifdef CONFIG_TESTING_OPTIONS
- static struct wpa_bss * wpas_ml_connect_pref(struct wpa_supplicant *wpa_s,
--					     struct wpa_bss *bss)
-+					     struct wpa_bss *bss,
-+					     struct wpa_ssid *ssid)
- {
- 	unsigned int low, high, i;
- 
-@@ -459,7 +460,7 @@ found:
- 		   MAC2STR(wpa_s->links[i].bssid));
- 
- 	/* Get the BSS entry and do the switch */
--	bss = wpa_bss_get_bssid(wpa_s, wpa_s->links[i].bssid);
-+	bss = wpa_bss_get(wpa_s, wpa_s->links[i].bssid, ssid->ssid, ssid->ssid_len);
- 	wpa_s->mlo_assoc_link_id = i;
- 
- 	return bss;
-@@ -528,7 +529,7 @@ static bool check_mld_allowed_phy(struct wpa_supplicant *wpa_s, int freq)
- 
- 
- static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
--				   struct wpa_bss *bss)
-+				   struct wpa_bss *bss, struct wpa_ssid *ssid)
- {
- 	u8 i;
- 
-@@ -551,7 +552,8 @@ static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
- 		if (bss->mld_link_id == i)
- 			wpa_s->links[i].bss = bss;
- 		else
--			wpa_s->links[i].bss = wpa_bss_get_bssid(wpa_s, bssid);
-+			wpa_s->links[i].bss = wpa_bss_get(wpa_s, bssid, ssid->ssid,
-+							  ssid->ssid_len);
- 	}
- }
- 
-@@ -597,10 +599,10 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
- #endif /* CONFIG_TESTING_OPTIONS */
- 	    bss->valid_links) {
- 		wpa_printf(MSG_DEBUG, "MLD: In authentication");
--		wpas_sme_set_mlo_links(wpa_s, bss);
-+		wpas_sme_set_mlo_links(wpa_s, bss, ssid);
- 
- #ifdef CONFIG_TESTING_OPTIONS
--		bss = wpas_ml_connect_pref(wpa_s, bss);
-+		bss = wpas_ml_connect_pref(wpa_s, bss, ssid);
- 
- 		if (wpa_s->conf->mld_force_single_link) {
- 			wpa_printf(MSG_DEBUG, "MLD: Force single link");
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0101-mtk-hostapd-MLD-hostapd-add-support-for-basic-MLD-Ex.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0101-mtk-hostapd-MLD-hostapd-add-support-for-basic-MLD-Ex.patch
deleted file mode 100644
index 08814ed..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0101-mtk-hostapd-MLD-hostapd-add-support-for-basic-MLD-Ex.patch
+++ /dev/null
@@ -1,247 +0,0 @@
-From 0cc6f925c0a97da331f41462a07c4ae4b6698409 Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Tue, 2 Apr 2024 15:36:29 +0800
-Subject: [PATCH 101/104] mtk: hostapd: MLD: hostapd: add support for basic MLD
- Extender
-
-Add basic MLD Extender support, including
-1. Extender STA stops all Extender AP's links before scaning.
-2. After finishing the connection with root AP, Extender STA
-   synchronizes control channel with each link on the Extender
-   AP.
-
-Advanced support includes BW cynchronization and channel switch.
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/ucode.c         | 52 ++++++++++++++++++++++++++++++++++++++----
- src/utils/ucode.c      |  4 +++-
- wpa_supplicant/ucode.c | 45 +++++++++++++++++++++++++++++-------
- 3 files changed, 88 insertions(+), 13 deletions(-)
-
-diff --git a/src/ap/ucode.c b/src/ap/ucode.c
-index 98b2a3bf2..2642e87c7 100644
---- a/src/ap/ucode.c
-+++ b/src/ap/ucode.c
-@@ -487,14 +487,16 @@ static uc_value_t *
- uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
- {
- 	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
-+	struct hostapd_data *first_hapd;
-+	struct hostapd_bss_config *conf;
- 	int i;
- 
--	wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
--			iface->phy, hostapd_state_text(iface->state));
--
- 	if (!iface)
- 		return NULL;
- 
-+	wpa_printf(MSG_INFO, "ucode: mtk: stop iface for %s in state %s\n",
-+			iface->phy, hostapd_state_text(iface->state));
-+
- 	if (iface->state != HAPD_IFACE_ENABLED)
- 		uc_hostapd_disable_iface(iface);
- 
-@@ -505,6 +507,32 @@ uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
- 		hapd->beacon_set_done = 0;
- 	}
- 
-+#ifdef CONFIG_IEEE80211BE
-+	first_hapd = iface->bss[0];
-+	conf = first_hapd->conf;
-+	for (i = 0; conf->mld_ap && i < iface->interfaces->count; i++) {
-+		struct hostapd_iface *h = iface->interfaces->iface[i];
-+		struct hostapd_data *h_hapd = h->bss[0];
-+		struct hostapd_bss_config *hconf = h_hapd->conf;
-+
-+		if (h == iface) {
-+			wpa_printf(MSG_DEBUG, "MLD: Skip own interface");
-+			continue;
-+		}
-+
-+		if (!hconf->mld_ap) {
-+			wpa_printf(MSG_DEBUG,
-+				   "MLD: Skip non MLD");
-+			continue;
-+		}
-+
-+		if (hostapd_is_ml_partner(first_hapd, h_hapd)) {
-+			hostapd_drv_stop_ap(h_hapd);
-+			h_hapd->beacon_set_done = 0;
-+		}
-+	}
-+#endif /* CONFIG_IEEE80211BE */
-+
- 	return NULL;
- }
- 
-@@ -512,11 +540,12 @@ static uc_value_t *
- uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
- {
- 	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
-+	struct hostapd_data *tmp_hapd;
- 	uc_value_t *info = uc_fn_arg(0);
- 	struct hostapd_config *conf;
- 	bool changed = false;
- 	uint64_t intval;
--	int i;
-+	int i, band_idx;
- 
- 	wpa_printf(MSG_INFO, "ucode: mtk: start iface for %s in state %s\n",
- 			iface->phy, hostapd_state_text(iface->state));
-@@ -532,6 +561,21 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
- 	if (ucv_type(info) != UC_OBJECT)
- 		return NULL;
- 
-+	intval = ucv_int64_get(ucv_object_get(info, "band_idx", NULL));
-+	if (!errno)
-+		band_idx = intval;
-+
-+#ifdef CONFIG_IEEE80211BE
-+	if (hostapd_is_mld_ap(iface->bss[0])) {
-+		for_each_mld_link(tmp_hapd, iface->bss[0]) {
-+			if (band_idx == tmp_hapd->iconf->band_idx) {
-+				iface = tmp_hapd->iface;
-+				break;
-+			}
-+		}
-+	}
-+#endif /* CONFIG_IEEE80211BE */
-+
- #define UPDATE_VAL(field, name)							\
- 	if ((intval = ucv_int64_get(ucv_object_get(info, name, NULL))) &&	\
- 		!errno && intval != conf->field) do {				\
-diff --git a/src/utils/ucode.c b/src/utils/ucode.c
-index 6f82382f3..81d472f6b 100644
---- a/src/utils/ucode.c
-+++ b/src/utils/ucode.c
-@@ -110,7 +110,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- 	uc_value_t *freq = uc_fn_arg(0);
- 	uc_value_t *sec = uc_fn_arg(1);
- 	int width = ucv_uint64_get(uc_fn_arg(2));
--	int bw320_offset = 1;
-+	int bw320_offset = 1, band_idx;
- 	int freq_val, center_idx, center_ofs;
- 	enum oper_chan_width chanwidth;
- 	enum hostapd_hw_mode hw_mode;
-@@ -123,6 +123,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- 		return NULL;
- 
- 	freq_val = ucv_int64_get(freq);
-+	band_idx = ucv_int64_get(uc_fn_arg(4));
- 	if (ucv_type(sec) == UC_INTEGER)
- 		sec_channel = ucv_int64_get(sec);
- 	else if (sec)
-@@ -183,6 +184,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
- 	ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
- 	ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
- 	ucv_object_add(ret, "oper_chwidth", ucv_int64_new(chanwidth));
-+	ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
- 
- 	if (chanwidth == CONF_OPER_CHWIDTH_USE_HT && !sec_channel) {
- 		ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(channel));
-diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
-index 542ca25c9..ac0639a90 100644
---- a/wpa_supplicant/ucode.c
-+++ b/wpa_supplicant/ucode.c
-@@ -246,12 +246,13 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
- {
- 	struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
- 	struct wpa_bss *bss;
--	uc_value_t *ret, *val;
-+	uc_value_t *ret, *val, *link_obj = uc_fn_arg(0);
- 	struct wpa_channel_info ci;
- 	u8 op_class, channel;
--	enum oper_chan_width ch_width;
--	int center_freq1, bw320_offset = 1, is_24ghz;
-+	enum oper_chan_width ch_width = CONF_OPER_CHWIDTH_USE_HT;
-+	int center_freq1, bw320_offset = 1, is_24ghz, band_idx;
- 	enum hostapd_hw_mode hw_mode;
-+	int link_id = ucv_int64_get(link_obj);
- 
- 	if (!wpa_s)
- 		return NULL;
-@@ -261,13 +262,25 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
- 	val = ucv_string_new(wpa_supplicant_state_txt(wpa_s->wpa_state));
- 	ucv_object_add(ret, "state", ucv_get(val));
- 
--	bss = wpa_s->current_bss;
-+	bss = link_id == -1 ? wpa_s->current_bss : wpa_s->links[link_id].bss;
- 	if (bss) {
- 		int sec_chan = 0;
- 
- 		hw_mode = ieee80211_freq_to_chan(bss->freq, &channel);
- 		is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
- 			hw_mode == HOSTAPD_MODE_IEEE80211B;
-+		/*
-+		 * Assume that the mapping between band and band_idx is
-+		 * 2 GHz band: band_idx 0
-+		 * 5 GHz band: band_idx 1
-+		 * 6 GHz band: band_idx 2
-+		 * */
-+		if (is_24ghz)
-+			band_idx = 0;
-+		else if (IS_5GHZ(bss->freq))
-+			band_idx = 1;
-+		else if (is_6ghz_freq(bss->freq))
-+			band_idx = 2;
- 
- 		wpa_drv_channel_info(wpa_s, &ci);
- 		center_freq1 = ci.center_frq1;
-@@ -279,11 +292,10 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
- 				sec_chan = (bss->freq / 20) & 1 ? 1 : -1;
- 		}
- 
--		if (ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
--						  sec_chan, &op_class, &channel))
--			return NULL;
-+		if (!ieee80211_chaninfo_to_channel(ci.frequency, ci.chanwidth,
-+						   sec_chan, &op_class, &channel))
-+			ch_width = op_class_to_ch_width(op_class);
- 
--		ch_width = op_class_to_ch_width(op_class);
- 		if (ch_width == CONF_OPER_CHWIDTH_320MHZ &&
- 		    (center_freq1 == 6265) || center_freq1 == 6585 ||
- 		     center_freq1 == 6905) {
-@@ -295,6 +307,7 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
- 		ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
- 		ucv_object_add(ret, "ch_width", ucv_int64_new(ch_width));
- 		ucv_object_add(ret, "bw320_offset", ucv_int64_new(bw320_offset));
-+		ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
- 	}
- 
- #ifdef CONFIG_MESH
-@@ -309,6 +322,21 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
- 	return ret;
- }
- 
-+static uc_value_t *
-+uc_wpas_iface_get_valid_links(uc_vm_t *vm, size_t nargs)
-+{
-+	struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
-+	uc_value_t *ret;
-+
-+	if (!wpa_s)
-+		return NULL;
-+
-+	ret = ucv_object_new(vm);
-+	ucv_object_add(ret, "valid_links", ucv_uint64_new(wpa_s->valid_links));
-+
-+	return ret;
-+}
-+
- int wpas_ucode_init(struct wpa_global *gl)
- {
- 	static const uc_function_list_t global_fns[] = {
-@@ -320,6 +348,7 @@ int wpas_ucode_init(struct wpa_global *gl)
- 	};
- 	static const uc_function_list_t iface_fns[] = {
- 		{ "status", uc_wpas_iface_status },
-+		{ "get_valid_links", uc_wpas_iface_get_valid_links },
- 	};
- 	uc_value_t *data, *proto;
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0101-mtk-hostapd-Temporary-non-inheritance-IE-solution.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0101-mtk-hostapd-Temporary-non-inheritance-IE-solution.patch
new file mode 100644
index 0000000..2759e8e
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0101-mtk-hostapd-Temporary-non-inheritance-IE-solution.patch
@@ -0,0 +1,213 @@
+From 097f4b42d450ba1ae1f9acbb52cbae5f061d2ded Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Mon, 20 May 2024 17:29:36 +0800
+Subject: [PATCH 101/126] mtk: hostapd: Temporary non-inheritance IE solution
+
+Remove MBSSID IE and FILS indication IE in per-STA profile
+The patch append non-inheritance IE in per-STA profile of a ML IE.
+To add new IE in non-inheritance IE, just append the tag to IE list.
+
+Fix the EHT-4.6.1_RUN1_ITER2 (2G+5G) BRCM assoc issue.
+Without this patch, if the AP is an AP MLD 2G+5G (with 5G as the Setup link), the BRCM station will only connect to the AP using one link (i.e., the per-station profile count in the Association request is 0).
+
+Note: Regardless of whether this patch is applied, EHT-4.6.1_RUN1_ITER1 (2G+5G, with 2G as the setup link) can pass.
+
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ src/ap/beacon.c         | 28 +++++++++++------
+ src/ap/ieee802_11.c     |  2 ++
+ src/ap/ieee802_11.h     |  2 ++
+ src/ap/ieee802_11_eht.c | 68 ++++++++++++++++++++++++++++++++++++++++-
+ 4 files changed, 90 insertions(+), 10 deletions(-)
+
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 36f4feb3a..50d45d532 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -766,15 +766,20 @@ static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd,
+ 			buflen += hostapd_eid_eht_ml_beacon_len(
+ 				ml_elem_ap, params->mld_info, !!params->mld_ap);
+ 		}
++		/* non-inheritance element */
++		if (params->is_ml_sta_info)
++			buflen += hostapd_eid_non_inheritance_len(hapd);
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+-	buflen += hostapd_eid_mbssid_len(hapd, WLAN_FC_STYPE_PROBE_RESP, NULL,
+-					 params->known_bss,
+-					 params->known_bss_len, NULL);
+-	if (!params->is_ml_sta_info)
++	if (!params->is_ml_sta_info) {
++		buflen += hostapd_eid_mbssid_len(hapd, WLAN_FC_STYPE_PROBE_RESP, NULL,
++						 params->known_bss,
++						 params->known_bss_len, NULL);
+ 		buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP,
+ 					      true);
++	}
++
+ 	buflen += hostapd_mbo_ie_len(hapd);
+ 	buflen += hostapd_eid_owe_trans_len(hapd);
+ 	buflen += hostapd_eid_dpp_cc_len(hapd);
+@@ -835,9 +840,10 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
+ 
+ 	pos = hostapd_get_rsne(hapd, pos, epos - pos);
+ 	pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
+-	pos = hostapd_eid_mbssid(hapd, pos, epos, WLAN_FC_STYPE_PROBE_RESP, 0,
+-				 NULL, params->known_bss, params->known_bss_len,
+-				 NULL, NULL, NULL, 0);
++	if (!params->is_ml_sta_info)
++		pos = hostapd_eid_mbssid(hapd, pos, epos, WLAN_FC_STYPE_PROBE_RESP, 0,
++					 NULL, params->known_bss, params->known_bss_len,
++					 NULL, NULL, NULL, 0);
+ 	pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
+ 	pos = hostapd_get_mde(hapd, pos, epos - pos);
+ 
+@@ -897,10 +903,11 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
+ 	pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
+ 	pos = hostapd_eid_max_chsw_time(hapd, pos);
+ 
+-	if (!params->is_ml_sta_info)
++	if (!params->is_ml_sta_info) {
+ 		pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP,
+ 				      true);
+-	pos = hostapd_eid_fils_indic(hapd, pos, 0);
++		pos = hostapd_eid_fils_indic(hapd, pos, 0);
++	}
+ 	pos = hostapd_get_rsnxe(hapd, pos, epos - pos);
+ 
+ #ifdef CONFIG_IEEE80211AX
+@@ -999,6 +1006,9 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
+ 	}
+ #endif /* CONFIG_TESTING_OPTIONS */
+ 
++	if (params->is_ml_sta_info)
++		pos = hostapd_eid_non_inheritance(hapd, pos);
++
+ 	return pos;
+ }
+ 
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 7dac7a896..fd954b6f5 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -4520,6 +4520,8 @@ static void ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
+ 	p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
+ 	p = hostapd_eid_wmm(hapd, p);
+ 
++	p = hostapd_eid_non_inheritance(hapd, p);
++
+ 	if (hapd->conf->assocresp_elements &&
+ 	    (size_t) (buf + buflen - p) >=
+ 	    wpabuf_len(hapd->conf->assocresp_elements)) {
+diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
+index 18f97890b..2d9adb910 100644
+--- a/src/ap/ieee802_11.h
++++ b/src/ap/ieee802_11.h
+@@ -239,6 +239,7 @@ size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
+ 				 enum ieee80211_op_mode opmode);
+ u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
+ 			   enum ieee80211_op_mode opmode);
++u8 * hostapd_eid_non_inheritance(struct hostapd_data *hapd, u8 *eid);
+ u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid);
+ u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ 		       enum ieee80211_op_mode opmode,
+@@ -252,6 +253,7 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+ 			u8 **elem_offset,
+ 			const u8 *known_bss, size_t known_bss_len, u8 *rnr_eid,
+ 			u8 *rnr_count, u8 **rnr_offset, size_t rnr_len);
++size_t hostapd_eid_non_inheritance_len(struct hostapd_data *hapd);
+ bool hostapd_is_mld_ap(struct hostapd_data *hapd);
+ const char * sae_get_password(struct hostapd_data *hapd,
+ 			      struct sta_info *sta, const char *rx_id,
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index 8fc239f36..63713bc39 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -131,7 +131,6 @@ size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
+ 	return len;
+ }
+ 
+-
+ u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
+ 			   enum ieee80211_op_mode opmode)
+ {
+@@ -284,7 +283,74 @@ u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
+ 	return pos + elen;
+ }
+ 
++u8 mlo_non_inherit_list_6ghz[] = {
++	WLAN_EID_AP_CHANNEL_REPORT,
++	WLAN_EID_HT_CAP,
++	WLAN_EID_HT_OPERATION,
++	WLAN_EID_VHT_CAP,
++	WLAN_EID_VHT_OPERATION,
++};
++
++u8 mlo_non_inherit_list_6ghz_ext[] = {
++};
++
++u8 mlo_non_inherit_list_2_5ghz[] = {
++	WLAN_EID_VHT_CAP,
++	WLAN_EID_VHT_OPERATION,
++	WLAN_EID_TRANSMIT_POWER_ENVELOPE,
++};
++
++u8 mlo_non_inherit_list_2_5ghz_ext[] = {
++	WLAN_EID_EXT_HE_6GHZ_BAND_CAP,
++};
++
++size_t hostapd_eid_non_inheritance_len(struct hostapd_data *hapd)
++{
++	size_t len = 4;
+ 
++	if (is_6ghz_op_class(hapd->iconf->op_class)) {
++		len += sizeof(mlo_non_inherit_list_6ghz);
++		len += sizeof(mlo_non_inherit_list_6ghz_ext);
++	} else {
++		len += sizeof(mlo_non_inherit_list_2_5ghz);
++		len += sizeof(mlo_non_inherit_list_2_5ghz_ext);
++	}
++
++	return len;
++}
++
++u8 * hostapd_eid_non_inheritance(struct hostapd_data *hapd, u8 *eid)
++{
++	u8 *pos = eid, *len_pos;
++	int i;
++
++	*pos++ = WLAN_EID_EXTENSION;
++	len_pos = pos++;
++	*pos++ = WLAN_EID_EXT_NON_INHERITANCE;
++	if (is_6ghz_op_class(hapd->iconf->op_class)) {
++		/* Element ID list */
++		*pos++ = sizeof(mlo_non_inherit_list_6ghz);
++		for (i = 0; i < sizeof(mlo_non_inherit_list_6ghz); i++)
++			*pos++ = mlo_non_inherit_list_6ghz[i];
++
++		/* Element ID Extension list */
++		*pos++ = sizeof(mlo_non_inherit_list_6ghz_ext);
++		for (i = 0; i < sizeof(mlo_non_inherit_list_6ghz_ext); i++)
++			*pos++ = mlo_non_inherit_list_6ghz_ext[i];
++	} else {
++		/* Element ID list */
++		*pos++ = sizeof(mlo_non_inherit_list_2_5ghz);
++		for (i = 0; i < sizeof(mlo_non_inherit_list_2_5ghz); i++)
++			*pos++ = mlo_non_inherit_list_2_5ghz[i];
++
++		/* Element ID Extension list */
++		*pos++ = sizeof(mlo_non_inherit_list_2_5ghz_ext);
++		for (i = 0; i < sizeof(mlo_non_inherit_list_2_5ghz_ext); i++)
++			*pos++ = mlo_non_inherit_list_2_5ghz_ext[i];
++	}
++	*len_pos = pos - (eid + 2);
++	return pos;
++}
+ static bool check_valid_eht_mcs_nss(struct hostapd_data *hapd, const u8 *ap_mcs,
+ 				    const u8 *sta_mcs, u8 mcs_count, u8 map_len)
+ {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0102-mtk-hostapd-Fix-multiple-link-connect-get-pmkid-fail.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0102-mtk-hostapd-Fix-multiple-link-connect-get-pmkid-fail.patch
new file mode 100644
index 0000000..b3d973d
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0102-mtk-hostapd-Fix-multiple-link-connect-get-pmkid-fail.patch
@@ -0,0 +1,50 @@
+From b43473e506400cc142f3d464c621714014ca196c Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Thu, 6 Jun 2024 17:41:56 +0800
+Subject: [PATCH 102/126] mtk: hostapd: Fix multiple link connect get pmkid
+ failed
+
+Store pmkid in each link when receive STA auth.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ src/ap/ieee802_11.c | 15 ++++++++++++---
+ 1 file changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index fd954b6f5..ff0f24aaa 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -945,6 +945,7 @@ static void sae_sme_send_external_auth_status(struct hostapd_data *hapd,
+ 
+ void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
+ {
++	struct hostapd_data *link;
+ #ifndef CONFIG_NO_VLAN
+ 	struct vlan_description vlan_desc;
+ 
+@@ -986,9 +987,17 @@ void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
+ 	crypto_bignum_deinit(sta->sae->peer_commit_scalar_accepted, 0);
+ 	sta->sae->peer_commit_scalar_accepted = sta->sae->peer_commit_scalar;
+ 	sta->sae->peer_commit_scalar = NULL;
+-	wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
+-			       sta->sae->pmk, sta->sae->pmk_len,
+-			       sta->sae->pmkid, sta->sae->akmp);
++	if (hostapd_is_mld_ap(hapd)) {
++		for_each_mld_link(link, hapd) {
++			wpa_auth_pmksa_add_sae(link->wpa_auth, sta->addr,
++					sta->sae->pmk, sta->sae->pmk_len,
++					sta->sae->pmkid, sta->sae->akmp);
++		}
++	} else {
++		wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
++				sta->sae->pmk, sta->sae->pmk_len,
++				sta->sae->pmkid, sta->sae->akmp);
++	}
+ 	sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS);
+ }
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0102-mtk-hostapd-Refactor-static-PP-and-mld-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0102-mtk-hostapd-Refactor-static-PP-and-mld-support.patch
deleted file mode 100644
index c958591..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0102-mtk-hostapd-Refactor-static-PP-and-mld-support.patch
+++ /dev/null
@@ -1,336 +0,0 @@
-From 80b2cbbd80700d1fa04d97aae5b8a083a4b4f2ba Mon Sep 17 00:00:00 2001
-From: Allen Ye <allen.ye@mediatek.com>
-Date: Tue, 2 Apr 2024 16:51:07 +0800
-Subject: [PATCH 102/104] mtk: hostapd: Refactor static PP and mld support
-
-Add band_idx attribute in pp cmd for vendor cmd under mld setting.
-
----
- hostapd/config_file.c        |  6 ++--
- hostapd/ctrl_iface.c         | 69 +++++++++++++++++++++++++-----------
- hostapd/ctrl_iface.h         |  3 +-
- hostapd/hostapd_cli.c        | 15 ++++++++
- src/ap/ap_config.h           |  4 +--
- src/ap/ap_drv_ops.c          |  7 ++--
- src/ap/dfs.c                 |  3 ++
- src/ap/hostapd.c             |  4 +--
- src/common/mtk_vendor.h      |  1 +
- src/drivers/driver.h         |  3 +-
- src/drivers/driver_nl80211.c |  4 ++-
- 11 files changed, 87 insertions(+), 32 deletions(-)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index b9a062193..2add62ca9 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5339,7 +5339,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		if (get_u16(pos, line, &conf->punct_bitmap))
- 			return 1;
- 		conf->punct_bitmap = atoi(pos);
--		conf->pp_mode = PP_MANUAL_MODE;
-+		conf->pp_mode = PP_USR_MODE;
- 	} else if (os_strcmp(buf, "punct_acs_threshold") == 0) {
- 		int val = atoi(pos);
- 
-@@ -5427,8 +5427,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 	} else if (os_strcmp(buf, "pp_mode") == 0) {
- 		int val = atoi(pos);
- 
--		if ((val != PP_MANUAL_MODE && conf->punct_bitmap) ||
--		    val < PP_DISABLE || val > PP_MANUAL_MODE) {
-+		if ((val != PP_USR_MODE && conf->punct_bitmap) ||
-+		    val < PP_DISABLE || val > PP_USR_MODE) {
- 			wpa_printf(MSG_ERROR, "Line %d: invalid pp_mode value",
- 				   line);
- 			return 1;
-diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index b5f6431bf..6d4ce8acb 100644
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -4854,27 +4854,57 @@ hostapd_ctrl_iface_set_background_radar_mode(struct hostapd_data *hapd, char *cm
- 	return os_snprintf(buf, buflen, "OK\n");
- }
- 
-+struct hostapd_data *
-+hostapd_get_hapd_by_band_idx(struct hostapd_data *hapd, u8 band_idx)
-+{
-+	struct hostapd_data *link;
-+
-+	if (!hostapd_is_mld_ap(hapd))
-+		return hapd;
-+
-+	for_each_mld_link(link, hapd) {
-+		if (link->iconf->band_idx == band_idx)
-+			break;
-+	}
-+
-+	if (!link || link->iconf->band_idx != band_idx) {
-+		wpa_printf(MSG_ERROR, "Invalid band idx %d\n", band_idx);
-+		return NULL;
-+	}
-+
-+	return link;
-+}
-+
- static int
- hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
- 			  size_t buflen)
- {
--	char *pos, *config, *value;
-+	char *band, *config, *value;
-+	u8 band_idx;
- 
- 	config = cmd;
--	pos = os_strchr(config, ' ');
--	if (pos == NULL)
-+
-+	value = os_strchr(config, ' ');
-+	if (value == NULL)
- 		return -1;
--	*pos++ = '\0';
-+	*value++ = '\0';
- 
--	if (pos == NULL)
-+	band = os_strchr(value, ' ');
-+	if (band == NULL)
-+		return -1;
-+	*band++ = '\0';
-+	band_idx = strtol(band, NULL, 10);
-+
-+	hapd = hostapd_get_hapd_by_band_idx(hapd, band_idx);
-+
-+	if (!hapd)
- 		return -1;
--	value = pos;
- 
- 	if (os_strcmp(config, "mode") == 0) {
--		int val = atoi(value);
-+		int val = strtol(value, NULL, 10);
- 
--		if (val < PP_DISABLE || val > PP_AUTO_MODE) {
--			wpa_printf(MSG_ERROR, "Invalid value for set_pp");
-+		if (val < PP_DISABLE || val > PP_FW_MODE) {
-+			wpa_printf(MSG_ERROR, "Invalid value for SET_PP");
- 			return -1;
- 		}
- 		hapd->iconf->pp_mode = (u8) val;
-@@ -4882,7 +4912,8 @@ hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
- 			return -1;
- 	} else {
- 		wpa_printf(MSG_ERROR,
--			   "Unsupported parameter %s for set_pp", config);
-+			   "Unsupported parameter %s for SET_PP"
-+			   "Usage: set_pp mode <value> <band_idx>", config);
- 		return -1;
- 	}
- 	return os_snprintf(buf, buflen, "OK\n");
-@@ -4892,19 +4923,17 @@ static int
- hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
- 			  size_t buflen)
- {
--	char *pos, *end;
-+	u8 band_idx;
- 
--	pos = buf;
--	end = buf + buflen;
-+	band_idx = strtol(cmd, NULL, 10);
- 
--	if (os_strcmp(cmd, "mode") == 0) {
--		return os_snprintf(pos, end - pos, "pp_mode: %d\n",
--				   hapd->iconf->pp_mode);
--	} else {
--		wpa_printf(MSG_ERROR,
--			   "Unsupported parameter %s for get_pp", cmd);
-+	hapd = hostapd_get_hapd_by_band_idx(hapd, band_idx);
-+
-+	if (!hapd)
- 		return -1;
--	}
-+
-+	return os_snprintf(buf, buflen, "pp_mode: %d, punct_bitmap: 0x%04x\n",
-+			   hapd->iconf->pp_mode, hapd->iconf->punct_bitmap);
- }
- 
- static int
-diff --git a/hostapd/ctrl_iface.h b/hostapd/ctrl_iface.h
-index 3341a66bd..82a64b880 100644
---- a/hostapd/ctrl_iface.h
-+++ b/hostapd/ctrl_iface.h
-@@ -35,5 +35,6 @@ hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface)
- {
- }
- #endif /* CONFIG_NO_CTRL_IFACE */
--
-+struct hostapd_data *
-+hostapd_get_hapd_by_band_idx(struct hostapd_data *hapd, u8 band_idx);
- #endif /* CTRL_IFACE_H */
-diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
-index 81b74d6de..4bf70d183 100644
---- a/hostapd/hostapd_cli.c
-+++ b/hostapd/hostapd_cli.c
-@@ -1730,6 +1730,17 @@ static int hostapd_cli_cmd_dump_csi(struct wpa_ctrl *ctrl, int argc,
- {
- 	return hostapd_cli_cmd(ctrl, "DUMP_CSI", 1, argc, argv);
- }
-+static int hostapd_cli_cmd_set_pp(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "set_pp", 3, argc, argv);
-+}
-+
-+static int hostapd_cli_cmd_get_pp(struct wpa_ctrl *ctrl, int argc,
-+					   char *argv[])
-+{
-+	return hostapd_cli_cmd(ctrl, "get_pp", 1, argc, argv);
-+}
- 
- struct hostapd_cli_cmd {
- 	const char *cmd;
-@@ -1976,6 +1987,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
- 		" = Set csi configuaration"},
- 	{ "dump_csi", hostapd_cli_cmd_dump_csi, NULL,
- 		" = Dump csi data to a json file"},
-+	{ "set_pp", hostapd_cli_cmd_set_pp, NULL,
-+		" = Set preamble puncture mode"},
-+	{ "get_pp", hostapd_cli_cmd_get_pp, NULL,
-+		" = Get preamble puncture status"},
- 	{ NULL, NULL, NULL, NULL }
- };
- 
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 3bd8df9ce..5192c1f07 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -1353,8 +1353,8 @@ enum mtk_vendor_attr_edcca_ctrl_mode {
- 
- enum pp_mode {
- 	PP_DISABLE = 0,
--	PP_AUTO_MODE,
--	PP_MANUAL_MODE,
-+	PP_FW_MODE,
-+	PP_USR_MODE,
- };
- 
- #define EDCCA_DEFAULT_COMPENSATION -6
-diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
-index cb782fa31..a76148eba 100644
---- a/src/ap/ap_drv_ops.c
-+++ b/src/ap/ap_drv_ops.c
-@@ -1396,10 +1396,13 @@ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
- int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
- {
- 	if (!hapd->driver || !hapd->driver->pp_mode_set ||
--	    hapd->iconf->pp_mode > PP_AUTO_MODE)
-+	    hapd->iconf->pp_mode >= PP_USR_MODE ||
-+	    hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
- 		return 0;
-+
- 	return hapd->driver->pp_mode_set(hapd->drv_priv,
--					 hapd->iconf->pp_mode);
-+					 hapd->iconf->pp_mode,
-+					 hapd->iconf->band_idx);
- }
- 
- int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
-diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index b12290556..9bbeab1d6 100644
---- a/src/ap/dfs.c
-+++ b/src/ap/dfs.c
-@@ -1080,6 +1080,7 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
- 	os_memset(&csa_settings, 0, sizeof(csa_settings));
- 	csa_settings.cs_count = 5;
- 	csa_settings.block_tx = 1;
-+	csa_settings.punct_bitmap = hostapd_get_punct_bitmap(iface->bss[0]);
- 	csa_settings.link_id = -1;
- #ifdef CONFIG_IEEE80211BE
- 	if (iface->bss[0]->conf->mld_ap)
-@@ -1644,6 +1645,8 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
- 			return 0;
- 	}
- 
-+	iface->bss[0]->iconf->punct_bitmap = 0;
-+
- 	if (hostapd_dfs_background_start_channel_switch(iface, freq)) {
- 		/* Radar detected while operating, switch the channel. */
- 		return hostapd_dfs_start_channel_switch(iface);
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 8e3f0b281..3d3359291 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -2697,6 +2697,8 @@ dfs_offload:
- 	}
- #endif /* CONFIG_MESH */
- 
-+	if (hostapd_drv_pp_mode_set(hapd) < 0)
-+		goto fail;
- 	if (hostapd_drv_configure_edcca_enable(hapd) < 0)
- 		goto fail;
- 
-@@ -2711,8 +2713,6 @@ dfs_offload:
- 		goto fail;
- 	if (hostapd_drv_amsdu_ctrl(hapd) < 0)
- 		goto fail;
--	if (hostapd_drv_pp_mode_set(hapd) < 0)
--		goto fail;
- 
- 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
- 		   iface->bss[0]->conf->iface);
-diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
-index c290e72a7..09805b6fb 100644
---- a/src/common/mtk_vendor.h
-+++ b/src/common/mtk_vendor.h
-@@ -263,6 +263,7 @@ enum mtk_vendor_attr_pp_ctrl {
- 	MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
- 
- 	MTK_VENDOR_ATTR_PP_MODE,
-+	MTK_VENDOR_ATTR_PP_BAND_IDX,
- 
- 	/* keep last */
- 	NUM_MTK_VENDOR_ATTRS_PP_CTRL,
-diff --git a/src/drivers/driver.h b/src/drivers/driver.h
-index 7efb5e342..539771729 100644
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -5337,8 +5337,9 @@ struct wpa_driver_ops {
- 	 * pp_mode_set - Set preamble puncture operation mode
- 	 * @priv: Private driver interface data
- 	 * @pp_mode: Value is defined in enum pp_mode
-+	 * @band_idx: chip band index
- 	 */
--	int (*pp_mode_set)(void *priv, const u8 pp_mode);
-+	int (*pp_mode_set)(void *priv, const u8 pp_mode, u8 band_idx);
- #ifdef CONFIG_IEEE80211BE
- 	int (*get_mld_addr)(void *priv, u8 *addr);
- #endif
-diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
-index 39b45ef4b..6ec2c32df 100644
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -159,6 +159,7 @@ amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
- static struct nla_policy
- pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
- 	[MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
-+	[MTK_VENDOR_ATTR_PP_BAND_IDX] = { .type = NLA_U8 },
- };
- 
- static struct nla_policy csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
-@@ -15167,7 +15168,7 @@ static int nl80211_background_radar_mode(void *priv, const u8 background_radar_m
- 	return ret;
- }
- 
--static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
-+static int nl80211_pp_mode_set(void *priv, const u8 pp_mode, u8 band_idx)
- {
- 	struct i802_bss *bss = priv;
- 	struct wpa_driver_nl80211_data *drv = bss->drv;
-@@ -15194,6 +15195,7 @@ static int nl80211_pp_mode_set(void *priv, const u8 pp_mode)
- 	if (!data)
- 		goto fail;
- 
-+	nla_put_u8(msg, MTK_VENDOR_ATTR_PP_BAND_IDX, band_idx);
- 	nla_put_u8(msg, MTK_VENDOR_ATTR_PP_MODE, pp_mode);
- 
- 	nla_nest_end(msg, data);
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0103-mtk-hostapd-ensure-only-a-scan-callback-is-called-on.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0103-mtk-hostapd-ensure-only-a-scan-callback-is-called-on.patch
new file mode 100644
index 0000000..7b12f21
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0103-mtk-hostapd-ensure-only-a-scan-callback-is-called-on.patch
@@ -0,0 +1,49 @@
+From 6297062e6c302d2de6888580c2f396da47c95908 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 4 Jun 2024 08:24:32 +0800
+Subject: [PATCH 103/126] mtk: hostapd: ensure only a scan callback is called
+ on each scan result event
+
+Every scan callback is assigned along with a scan request to driver.
+Therefore on each scan result event, there should be only one scan
+callback to be handled.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/drv_callbacks.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 705acfb67..1b91232f2 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2493,10 +2493,12 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ 			hapd = switch_link_scan(hapd,
+ 						data->scan_info.scan_cookie);
+ #endif /* NEED_AP_MLME */
+-		if (hapd->iface->scan_cb)
++		if (hapd->iface->scan_cb) {
+ 			hapd->iface->scan_cb(hapd->iface);
++			break;
++		}
+ #ifdef CONFIG_IEEE80211BE
+-		if (!hapd->iface->scan_cb && hapd->conf->mld_ap) {
++		if (hapd->conf->mld_ap) {
+ 			/* Other links may be waiting for HT scan result */
+ 			unsigned int i;
+ 
+@@ -2506,8 +2508,10 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ 				struct hostapd_data *h_hapd = h->bss[0];
+ 
+ 				if (hostapd_is_ml_partner(hapd, h_hapd) &&
+-				    h_hapd->iface->scan_cb)
++				    h_hapd->iface->scan_cb) {
+ 					h_hapd->iface->scan_cb(h_hapd->iface);
++					break;
++				}
+ 			}
+ 		}
+ #endif /* CONFIG_IEEE80211BE */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0103-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0103-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch
deleted file mode 100644
index cca2e9a..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0103-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch
+++ /dev/null
@@ -1,124 +0,0 @@
-From ec7d47b2566da59c52d995f5e404a1c00e746fe5 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Thu, 11 Apr 2024 18:16:38 +0800
-Subject: [PATCH 103/104] mtk: hostapd: make sure all links are set before
- enabling beacon
-
-NL80211_CMD_NEW_BEACON will first be set, but we've modified mac80211 to
-disable this beacon. After that, hostapd will block
-NL80211_CMD_SET_BEACON until all links are setting up.
-(use NL80211_CMD_START_AP event to check if all expected links are enabled)
-
-Update: in wpa_driver_nl80211_set_ap(), send_and_recv() is used, implies
-that hostapd should already sync with driver, so don't need to use
-NL80211_CMD_START_AP event.
-
-This can make sure that the first beacon of each link includes the
-correct RNR and per-STA profile.
-
-Note that in NL80211_CMD_NEW_BEACON, we also set beacon interval to 0,
-which helps to bypass some mac80211 beacon active checks, e.g., during ACS.
-
-Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
----
- hostapd/config_file.c |  2 ++
- src/ap/ap_config.h    |  2 ++
- src/ap/beacon.c       | 10 ++++++++++
- src/ap/hostapd.c      | 14 ++++++++++++++
- src/ap/hostapd.h      |  1 +
- 5 files changed, 29 insertions(+)
-
-diff --git a/hostapd/config_file.c b/hostapd/config_file.c
-index 2add62ca9..8abe1bc46 100644
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -5354,6 +5354,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
- 		bss->mld_ap = !!atoi(pos);
- 	} else if (os_strcmp(buf, "mld_primary") == 0) {
- 		bss->mld_primary = !!atoi(pos);
-+	} else if (os_strcmp(buf, "mld_allowed_links") == 0) {
-+		bss->mld_allowed_links = atoi(pos);
- 	} else if (os_strcmp(buf, "mld_addr") == 0) {
- 		if (hwaddr_aton(pos, bss->mld_addr)) {
- 			wpa_printf(MSG_ERROR, "Line %d: Invalid mld_addr",
-diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 5192c1f07..0ea5a04e2 100644
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -968,6 +968,8 @@ struct hostapd_bss_config {
- 
- 	/* The AP is the primary AP of an AP MLD */
- 	u8 mld_primary;
-+	/* Allowed link bitmap of the AP MLD to which the AP is affiliated */
-+	u16 mld_allowed_links;
- 
- 	/* The MLD ID to which the AP MLD is affiliated with */
- 	u8 mld_id;
-diff --git a/src/ap/beacon.c b/src/ap/beacon.c
-index a5c46b067..a5c9dd87e 100644
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -2164,6 +2164,12 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
- 	os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
- 	head->u.beacon.beacon_int =
- 		host_to_le16(hapd->iconf->beacon_int);
-+	/* if MLD AP hasn't finished setting up all links, also set beacon interval
-+	 * to 0. This allows mac80211 to bypass some beacon active checks, for
-+	 * example, when doing ACS
-+	 */
-+	if (hapd->conf->mld_ap && !hapd->mld->started)
-+		head->u.beacon.beacon_int = host_to_le16(0);
- 
- 	/* hardware or low-level driver will setup seq_ctrl and timestamp */
- 	capab_info = hostapd_own_capab_info(hapd);
-@@ -2553,6 +2559,10 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
- 	int res, ret = -1, i;
- 	struct hostapd_hw_modes *mode;
- 
-+	/* skip setting beacon if other links are not started yet */
-+	if (hapd->conf->mld_ap && !hapd->mld->started && hapd->beacon_set_done)
-+		return 0;
-+
- 	if (!hapd->drv_priv) {
- 		wpa_printf(MSG_ERROR, "Interface is disabled");
- 		return -1;
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index 3d3359291..c31e0badd 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -1314,6 +1314,20 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
- 	if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
- 		return -1;
- 
-+	if (hapd->conf->mld_ap && !hapd->mld->started) {
-+		struct hostapd_data *p_hapd;
-+		u16 valid_links = 0;
-+
-+		for_each_mld_link(p_hapd, hapd)
-+			valid_links |= BIT(p_hapd->mld_link_id);
-+
-+		if (valid_links == hapd->conf->mld_allowed_links ||
-+		    !hapd->conf->mld_allowed_links) {
-+			hapd->mld->started = 1;
-+			ieee802_11_set_beacon(hapd);
-+		}
-+	}
-+
- 	if (flush_old_stations && !conf->start_disabled &&
- 	    conf->broadcast_deauth) {
- 		u8 addr[ETH_ALEN];
-diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
-index 5b37be87b..83636649f 100644
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -537,6 +537,7 @@ struct hostapd_mld {
- 	 * freed when num_links is 0.
- 	 */
- 	u8 refcount;
-+	bool started;
- 
- 	struct hostapd_data *fbss;
- 	struct dl_list links; /* List head of all affiliated links */
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0104-mtk-hostapd-add-support-for-get_survey-to-specify-li.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0104-mtk-hostapd-add-support-for-get_survey-to-specify-li.patch
new file mode 100644
index 0000000..6ee64b4
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0104-mtk-hostapd-add-support-for-get_survey-to-specify-li.patch
@@ -0,0 +1,111 @@
+From b424816d5509a5d2137c5e674ca39e03c74d3bdd Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 4 Jun 2024 08:26:04 +0800
+Subject: [PATCH 104/126] mtk: hostapd: add support for get_survey to specify
+ link id
+
+Specifying the link id when request get_survey is useful for passing
+the event to correcy hapd.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ap_drv_ops.h          | 8 +++++++-
+ src/ap/drv_callbacks.c       | 1 +
+ src/drivers/driver.h         | 5 ++++-
+ src/drivers/driver_nl80211.c | 4 +++-
+ 4 files changed, 15 insertions(+), 3 deletions(-)
+
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index ca2efab82..e74284b96 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -316,11 +316,17 @@ static inline void hostapd_drv_poll_client(struct hostapd_data *hapd,
+ static inline int hostapd_drv_get_survey(struct hostapd_data *hapd,
+ 					 unsigned int freq)
+ {
++	int link_id = -1;
++
+ 	if (hapd->driver == NULL)
+ 		return -1;
+ 	if (!hapd->driver->get_survey)
+ 		return -1;
+-	return hapd->driver->get_survey(hapd->drv_priv, freq);
++#ifdef CONFIG_IEEE80211BE
++	if (hapd->conf->mld_ap)
++		link_id = hapd->mld_link_id;
++#endif /* CONFIG_IEEE80211BE */
++	return hapd->driver->get_survey(hapd->drv_priv, freq, link_id);
+ }
+ 
+ static inline int hostapd_get_country(struct hostapd_data *hapd, char *alpha2)
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 1b91232f2..0e7cfd285 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2685,6 +2685,7 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ 			data->connect_failed_reason.code);
+ 		break;
+ 	case EVENT_SURVEY:
++		hapd = switch_link_hapd(hapd, data->survey_results.link_id);
+ 		hostapd_event_get_survey(hapd->iface, &data->survey_results);
+ 		break;
+ #ifdef NEED_AP_MLME
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index eed99dac8..d5d00db9d 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -4642,6 +4642,7 @@ struct wpa_driver_ops {
+ 	 * @priv: Private driver interface data
+ 	 * @freq: If set, survey data for the specified frequency is only
+ 	 *	being requested. If not set, all survey data is requested.
++	 * @link_id: The link ID that requests the get_survey.
+ 	 * Returns: 0 on success, -1 on failure
+ 	 *
+ 	 * Use this to retrieve:
+@@ -4660,7 +4661,7 @@ struct wpa_driver_ops {
+ 	 * for each survey. The min_nf of the channel is updated for each
+ 	 * survey.
+ 	 */
+-	int (*get_survey)(void *priv, unsigned int freq);
++	int (*get_survey)(void *priv, unsigned int freq, int link_id);
+ 
+ 	/**
+ 	 * status - Get driver interface status information
+@@ -6851,10 +6852,12 @@ union wpa_event_data {
+ 	 * @freq_filter: Requested frequency survey filter, 0 if request
+ 	 *	was for all survey data
+ 	 * @survey_list: Linked list of survey data (struct freq_survey)
++	 * @link_id: Link ID of the MLO link
+ 	 */
+ 	struct survey_results {
+ 		unsigned int freq_filter;
+ 		struct dl_list survey_list; /* struct freq_survey */
++		int link_id;
+ 	} survey_results;
+ 
+ 	/**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index b3ae50d15..3683f6034 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -10432,7 +10432,8 @@ static int survey_handler(struct nl_msg *msg, void *arg)
+ }
+ 
+ 
+-static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq)
++static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq,
++					 int link_id)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+@@ -10450,6 +10451,7 @@ static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq)
+ 	if (!msg)
+ 		return -ENOBUFS;
+ 
++	data.survey_results.link_id = link_id;
+ 	if (freq)
+ 		data.survey_results.freq_filter = freq;
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0104-mtk-hostapd-ucode-add-is_mld_finished-check.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0104-mtk-hostapd-ucode-add-is_mld_finished-check.patch
deleted file mode 100644
index e1c0274..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0104-mtk-hostapd-ucode-add-is_mld_finished-check.patch
+++ /dev/null
@@ -1,53 +0,0 @@
-From 7b4363892397c667e65fae9e036c83ac039e308d Mon Sep 17 00:00:00 2001
-From: Michael-CY Lee <michael-cy.lee@mediatek.com>
-Date: Wed, 17 Apr 2024 13:17:59 +0800
-Subject: [PATCH 104/104] mtk: hostapd: ucode: add is_mld_finished check
-
-Add is_mld_finished check for ucode need.
-This function returns ture only if all links fromt all MLD APs are
-ready.
-
-Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
----
- src/ap/ucode.c | 18 ++++++++++++++++++
- 1 file changed, 18 insertions(+)
-
-diff --git a/src/ap/ucode.c b/src/ap/ucode.c
-index 2642e87c7..9f9cc2022 100644
---- a/src/ap/ucode.c
-+++ b/src/ap/ucode.c
-@@ -734,6 +734,23 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
- 	return ucv_boolean_new(!ret);
- }
- 
-+static uc_value_t *
-+uc_hostapd_iface_is_mld_finished(uc_vm_t *vm, size_t nargs)
-+{
-+	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
-+	bool finished = true;
-+	int i;
-+
-+	for (i = 0; i < iface->num_bss; i++) {
-+		if (iface->bss[i]->conf->mld_ap && !iface->bss[i]->mld->started) {
-+			finished = false;
-+			break;
-+		}
-+	}
-+
-+	return ucv_boolean_new(finished);
-+}
-+
- static uc_value_t *
- uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
- {
-@@ -806,6 +823,7 @@ int hostapd_ucode_init(struct hapd_interfaces *ifaces)
- 		{ "stop", uc_hostapd_iface_stop },
- 		{ "start", uc_hostapd_iface_start },
- 		{ "switch_channel", uc_hostapd_iface_switch_channel },
-+		{ "is_mld_finished", uc_hostapd_iface_is_mld_finished },
- 	};
- 	uc_value_t *data, *proto;
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0105-mtk-hostapd-free-station-when-hapd-deinit.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0105-mtk-hostapd-free-station-when-hapd-deinit.patch
deleted file mode 100644
index 81b2015..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0105-mtk-hostapd-free-station-when-hapd-deinit.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From 95dcf88b68837221ad937fe7c675b169c8034384 Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Mon, 22 Apr 2024 08:40:18 +0800
-Subject: [PATCH] mtk: hostapd: free station when hapd deinit
-
-Free all stations in the same MLD when a bss is deinit.
-Without this patch, the AP_VLAN interface may be free after
-stop ap and leads to kernel crash.
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- src/ap/hostapd.c | 14 ++++++++++++++
- 1 file changed, 14 insertions(+)
-
-diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index c31e0badd..62943c561 100644
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -842,7 +842,21 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
- 
- void hostapd_bss_deinit_no_free(struct hostapd_data *hapd)
- {
-+#ifdef CONFIG_IEEE80211BE
-+	struct hostapd_data *link;
-+
-+	/* FIXME: free all stations to remvoe AP_VLAN interface. Allocate */
-+	/* per-link structures for AP_VLAN in mac80211 and only remove single */
-+	/* link here. */
-+	if (hostapd_is_mld_ap(hapd)) {
-+		for_each_mld_link(link, hapd)
-+			hostapd_free_stas(link);
-+	} else {
-+		hostapd_free_stas(hapd);
-+	}
-+#else
- 	hostapd_free_stas(hapd);
-+#endif
- 	hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
- #ifdef CONFIG_WEP
- 	hostapd_clear_wep(hapd);
--- 
-2.18.0
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0105-mtk-hostapd-sequentially-conduct-multiple-setup-ACS.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0105-mtk-hostapd-sequentially-conduct-multiple-setup-ACS.patch
new file mode 100644
index 0000000..77a06b3
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0105-mtk-hostapd-sequentially-conduct-multiple-setup-ACS.patch
@@ -0,0 +1,84 @@
+From 93ff9ff205e52c9dd5ac582261c5476ff87198ad Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Tue, 4 Jun 2024 08:31:29 +0800
+Subject: [PATCH 105/126] mtk: hostapd: sequentially conduct multiple setup ACS
+
+In the single-wiphy architecture, only one scan request is allowed at
+the same time (no matter the AP is legacy or MLD). However, if multiple
+AP interfaces need setup ACS, multiple scan requests are sent to the
+driver and only one can be accept. Other failed requests lead to
+interface setup failure.
+
+A sequentially conducting for multiple ACS is needed to prevent such a
+failure. It is realized by
+  1. A BSS (link) postpones its ACS initializaion if it detects another
+  active ACS.
+  2. Once the ACS is finished, the BSS (link) starts the ACS initizlization
+  for one of those BSSes (links) waiting for it.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/hostapd.c     | 18 ++++++++++++++++++
+ src/ap/hw_features.c | 10 ++++++++++
+ 2 files changed, 28 insertions(+)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index f03a6242f..63d89e614 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2860,6 +2860,24 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
+ 	unsigned int i;
+ 	int not_ready_in_sync_ifaces = 0;
+ 
++	if (iface->state == HAPD_IFACE_ACS) {
++		int i;
++
++		for (i = 0; i < interfaces->count; i++) {
++			if (!interfaces->iface[i]->freq) {
++				/* FIXME problems remained
++				 * 1. the return value of acs_init() is
++				 *    not check
++				 * 2. if it fails the setup, next acs_init()
++				 *    will not be handled
++				 */
++				wpa_printf(MSG_DEBUG, "mtk: trigger acs_init for %s", interfaces->iface[i]->phy);
++				acs_init(interfaces->iface[i]);
++				break;
++			}
++		}
++	}
++
+ 	if (!iface->need_to_start_in_sync)
+ 		return hostapd_setup_interface_complete_sync(iface, err);
+ 
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index 2a2832cd4..f35832e2e 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -1158,6 +1158,8 @@ int hostapd_determine_mode(struct hostapd_iface *iface)
+ static enum hostapd_chan_status
+ hostapd_check_chans(struct hostapd_iface *iface)
+ {
++	int i;
++
+ 	if (iface->freq) {
+ 		int err;
+ 
+@@ -1177,6 +1179,14 @@ hostapd_check_chans(struct hostapd_iface *iface)
+ 	 * which is used to trigger ACS.
+ 	 */
+ 
++	/*
++	 * Only allow an ACS at one time.
++	 */
++	for (i = 0; i < iface->interfaces->count; i++) {
++		if (iface->interfaces->iface[i]->state == HAPD_IFACE_ACS)
++			return HOSTAPD_CHAN_ACS;
++	}
++
+ 	switch (acs_init(iface)) {
+ 	case HOSTAPD_CHAN_ACS:
+ 		return HOSTAPD_CHAN_ACS;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0106-mtk-hostapd-find-sta_info-for-legacy-STA-associating.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0106-mtk-hostapd-find-sta_info-for-legacy-STA-associating.patch
new file mode 100644
index 0000000..83f53fc
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0106-mtk-hostapd-find-sta_info-for-legacy-STA-associating.patch
@@ -0,0 +1,48 @@
+From ec640d581610ce8d2eac74ee38459bc84ace18ca Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 14 Jun 2024 16:19:46 +0800
+Subject: [PATCH 106/126] mtk: hostapd: find sta_info for legacy STA
+ associating with non-first link when handling rx_from_unknown event
+
+The legacy STA might associates with the AP MLD's non-first link, but
+the 4-addr QoS NULL from STA is still sent to first link's hapd and
+sta_info can not be found, leading to the 4-addr QoS NULL skb being
+dropped.
+
+This commit solves the problem by checking other link's hapd when the
+sta_info is not found in handling rx_from_unknown event.
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ieee802_11.c | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index ff0f24aaa..62fdfb288 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7092,6 +7092,21 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+ 	sta = ap_get_sta(hapd, src);
+ 
+ #ifdef CONFIG_IEEE80211BE
++	if (!sta && hapd->conf->mld_ap) {
++		struct hostapd_data *h;
++
++		/* The data frame might be sent by a non-MLD STA via non-first
++		 * link, so we must also check other links.
++		 */
++		for_each_mld_link(h, hapd) {
++			if (h == hapd)
++				continue;
++
++			if (sta = ap_get_sta(h, src))
++				break;
++		}
++	}
++
+ 	if (sta && sta->mld_info.mld_sta)
+ 		sta = sta->mld_assoc_sta;
+ #endif
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0107-mtk-hostapd-do-not-consider-bss-ignore-list-when-par.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0107-mtk-hostapd-do-not-consider-bss-ignore-list-when-par.patch
new file mode 100644
index 0000000..6634548
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0107-mtk-hostapd-do-not-consider-bss-ignore-list-when-par.patch
@@ -0,0 +1,44 @@
+From 1ed61a27f27d721697c4e0705ce934afa4804605 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Wed, 19 Jun 2024 11:31:09 +0800
+Subject: [PATCH 107/126] mtk: hostapd: do not consider bss ignore list when
+ parsing ML parnter link
+
+By definition, adding BSS into the ignore list only makes wpa_supplicant
+tries the listed BSS with a low priority, but does not prevent the
+listed BSS from being used.
+
+It migth not be suitable to check the ignore list when parsing ML
+partner links. It made wpa_supplicant associate with AP MLD by only
+partial links with the link inside the ignore list being excluded.
+
+After apply this commit, a partner link in the BSS ignore list will
+not be ignored
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ wpa_supplicant/bss.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
+index cf94d4be5..b85b251bf 100644
+--- a/wpa_supplicant/bss.c
++++ b/wpa_supplicant/bss.c
+@@ -1573,9 +1573,12 @@ wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s,
+ 				    (bss_params & (RNR_BSS_PARAM_SAME_SSID |
+ 						   RNR_BSS_PARAM_CO_LOCATED)) ||
+ 				    wpa_scan_res_match(wpa_s, 0, neigh_bss,
+-						       ssid, 1, 0)) &&
++						       ssid, 1, 0))
++#if 0 /* MLD partner link should not be excluded */
+ 				   !wpa_bssid_ignore_is_listed(
+-					   wpa_s, neigh_bss->bssid)) {
++					   wpa_s, neigh_bss->bssid)
++#endif
++				    ) {
+ 				struct mld_link *l;
+ 
+ 				bss->valid_links |= BIT(link_id);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0108-mtk-hostapd-support-vendor-command-trig_type-and-ap_.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0108-mtk-hostapd-support-vendor-command-trig_type-and-ap_.patch
new file mode 100644
index 0000000..bac1e4a
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0108-mtk-hostapd-support-vendor-command-trig_type-and-ap_.patch
@@ -0,0 +1,149 @@
+From 41742b992f23847935411c69d676c8c8813190bf Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Wed, 19 Jun 2024 17:22:41 +0800
+Subject: [PATCH 108/126] mtk: hostapd: support vendor command trig_type and
+ ap_wireless with link_id
+
+For mld ap, we need to add link_id in nl80211 msg so that mt76 driver
+could use link_id to find the corresponding phy.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ src/ap/ap_drv_ops.c          | 18 ++++++++++++++++--
+ src/common/mtk_vendor.h      |  2 ++
+ src/drivers/driver.h         |  6 ++++--
+ src/drivers/driver_nl80211.c | 11 +++++++++--
+ 4 files changed, 31 insertions(+), 6 deletions(-)
+
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 2d2198a44..8631bf960 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1377,9 +1377,16 @@ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_colo
+ 
+ int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
+ {
++	s8 link_id = -1;
++
+ 	if (!hapd->driver || !hapd->driver->ap_wireless)
+ 		return 0;
+-	return hapd->driver->ap_wireless(hapd->drv_priv, sub_vendor_id, value);
++
++	if (hapd->conf->mld_ap)
++		link_id = hapd->mld_link_id;
++
++	return hapd->driver->ap_wireless(hapd->drv_priv, sub_vendor_id, value,
++					 link_id);
+ }
+ 
+ int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
+@@ -1398,9 +1405,16 @@ int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int va
+ 
+ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type)
+ {
++	s8 link_id = -1;
++
+ 	if (!hapd->driver || !hapd->driver->ap_trigtype)
+ 		return 0;
+-	return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type);
++
++	if (hapd->conf->mld_ap)
++		link_id = hapd->mld_link_id;
++
++	return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type,
++					 link_id);
+ }
+ 
+ int hostapd_drv_amnt_set(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_sta_mac)
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index c6de8862b..937b968d5 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -165,6 +165,8 @@ enum mtk_vendor_attr_wireless_ctrl {
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT = 9,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_MU_EDCA,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_LINK_ID,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index d5d00db9d..dacf0a98d 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5334,8 +5334,9 @@ struct wpa_driver_ops {
+ 	* ap_wireless - set wireless command
+ 	* @priv: Private driver interface data
+ 	* @value: value
++	* @link_id: MLD link id. -1 if this is an non-MLD AP.
+ 	*/
+-	int (*ap_wireless)(void *priv, u8 mode, int value);
++	int (*ap_wireless)(void *priv, u8 mode, int value, s8 link_id);
+ 
+ 	/**
+ 	* ap_rfeatures - set ap rf features command
+@@ -5350,8 +5351,9 @@ struct wpa_driver_ops {
+ 	* @priv: Private driver interface data
+ 	* @enable: enable or disable
+ 	* @type: trigger type
++	* @link_id: MLD link id. -1 if this is an non-MLD AP.
+ 	*/
+-	int (*ap_trigtype)(void *priv, u8 enable, u8 type);
++	int (*ap_trigtype)(void *priv, u8 enable, u8 type, s8 link_id);
+ 
+ 	/**
+ 	* amnt_set - add/delete station from monitoring
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 3683f6034..80fe2e591 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -141,6 +141,7 @@ wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
+ 	[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE] = {.type = NLA_U16 },
+ 	[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU] = {.type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_LINK_ID] = {.type = NLA_U8 },
+ };
+ 
+ static struct nla_policy
+@@ -14910,7 +14911,7 @@ static int nl80211_get_aval_color_bmp(void *priv, u64 *aval_color_bmp)
+ 	return ret;
+ }
+ 
+-static int nl80211_ap_wireless(void *priv, u8 sub_vendor_id, int value)
++static int nl80211_ap_wireless(void *priv, u8 sub_vendor_id, int value, s8 link_id)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+@@ -14941,6 +14942,9 @@ static int nl80211_ap_wireless(void *priv, u8 sub_vendor_id, int value)
+ 	else
+ 		nla_put_u8(msg, sub_vendor_id, (u8) value);
+ 
++	if (link_id > -1)
++		nla_put_u8(msg, MTK_VENDOR_ATTR_WIRELESS_CTRL_LINK_ID, link_id);
++
+ 	nla_nest_end(msg, data);
+ 	ret = send_and_recv_cmd(drv, msg);
+ 	if (ret)
+@@ -14997,7 +15001,7 @@ fail:
+ 	return -ENOBUFS;
+ }
+ 
+-static int nl80211_ap_trigtype(void *priv, u8 enable, u8 type)
++static int nl80211_ap_trigtype(void *priv, u8 enable, u8 type, s8 link_id)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+@@ -15023,6 +15027,9 @@ static int nl80211_ap_trigtype(void *priv, u8 enable, u8 type)
+ 	if (!data)
+ 		goto fail;
+ 
++	if (link_id > -1)
++		nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_LINK_ID, link_id);
++
+ 	data2 = nla_nest_start(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG);
+ 	if (!data2)
+ 		goto fail;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0109-mtk-hostapd-rework-radar-event-handling.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0109-mtk-hostapd-rework-radar-event-handling.patch
new file mode 100644
index 0000000..6daae7e
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0109-mtk-hostapd-rework-radar-event-handling.patch
@@ -0,0 +1,124 @@
+From 3443edc9c46258a0ce8a11ca579f7be29028e363 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 27 Jun 2024 13:44:00 +0800
+Subject: [PATCH 109/126] mtk: hostapd: rework radar event handling
+
+Specify the ifindex for the radar event in the sinlge wiphy model.
+This resolves the following MBSS MLD radar event issue.
+For example, if the topology is:
+MLD 1 (2G, 6G)
+MLD 2 (2G, 5G)
+2G legacy AP
+5G legacy AP
+6G legacy AP
+Without specifying the ifindex, hostapd will handle the radar event
+with the drv->ctx (MLD 1 2G hapd).
+However, in this case, MLD 1 has no 5G link, so the radar event will
+be ignored.
+
+Depends-On: I41d67b4d6f4610694f3830fdd0154fd392bc7c1f
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/drivers/driver_nl80211_event.c | 42 +++++++++++++++++++-----------
+ 1 file changed, 27 insertions(+), 15 deletions(-)
+
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 635401564..e95593a5b 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -2464,11 +2464,23 @@ static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv,
+ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
+ 				struct nlattr **tb)
+ {
++	struct i802_bss *bss;
+ 	union wpa_event_data data;
+ 	enum nl80211_radar_event event_type;
++	int ifidx;
++
++	if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT] ||
++	    !tb[NL80211_ATTR_IFINDEX])
++		return;
+ 
+-	if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT])
++	ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
++	bss = get_bss_ifindex(drv, ifidx);
++	if (!bss) {
++		wpa_printf(MSG_ERROR,
++			   "nl80211: Unknown ifindex (%d) for radar event, ignoring",
++			   ifidx);
+ 		return;
++	}
+ 
+ 	os_memset(&data, 0, sizeof(data));
+ 	data.dfs_event.link_id = NL80211_DRV_LINK_ID_NA;
+@@ -2480,8 +2492,7 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
+ 			nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
+ 	} else if (data.dfs_event.freq) {
+ 		data.dfs_event.link_id =
+-			nl80211_get_link_id_by_freq(drv->first_bss,
+-						    data.dfs_event.freq);
++			nl80211_get_link_id_by_freq(bss, data.dfs_event.freq);
+ 	}
+ 
+ 	/* Check HT params */
+@@ -2515,43 +2526,44 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
+ 		data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+ 
+ 	wpa_printf(MSG_DEBUG,
+-		   "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz, link_id=%d",
++		   "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, "
++		   "cf1: %dMHz, cf2: %dMHz, ifindex=%d, link_id=%d",
+ 		   data.dfs_event.freq, data.dfs_event.ht_enabled,
+ 		   data.dfs_event.chan_offset, data.dfs_event.chan_width,
+-		   data.dfs_event.cf1, data.dfs_event.cf2,
++		   data.dfs_event.cf1, data.dfs_event.cf2, ifidx,
+ 		   data.dfs_event.link_id);
+ 
+ 	switch (event_type) {
+ 	case NL80211_RADAR_DETECTED:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data);
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_RADAR_DETECTED, &data);
+ 		break;
+ 	case NL80211_RADAR_CAC_FINISHED:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data);
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_CAC_FINISHED, &data);
+ 		break;
+ 	case NL80211_RADAR_CAC_ABORTED:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data);
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_CAC_ABORTED, &data);
+ 		break;
+ 	case NL80211_RADAR_NOP_FINISHED:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_NOP_FINISHED, &data);
+ 		break;
+ 	case NL80211_RADAR_PRE_CAC_EXPIRED:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_PRE_CAC_EXPIRED,
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_PRE_CAC_EXPIRED,
+ 				     &data);
+ 		break;
+ 	case NL80211_RADAR_CAC_STARTED:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_CAC_STARTED, &data);
+ 		break;
+ 	case NL80211_RADAR_BACKGROUND_CHAN_UPDATE:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_UPDATE, &data);
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_BACKGROUND_CHAN_UPDATE, &data);
+ 		break;
+ 	case NL80211_RADAR_BACKGROUND_CHAN_EXPAND:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_BACKGROUND_CHAN_EXPAND, &data);
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_BACKGROUND_CHAN_EXPAND, &data);
+ 		break;
+ 	case NL80211_RADAR_STA_CAC_SKIPPED:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_STA_CAC_SKIPPED, &data);
+ 		break;
+ 	case NL80211_RADAR_STA_CAC_EXPIRED:
+-		wpa_supplicant_event(drv->ctx, EVENT_DFS_STA_CAC_EXPIRED, &data);
++		wpa_supplicant_event(bss->ctx, EVENT_DFS_STA_CAC_EXPIRED, &data);
+ 		break;
+ 	default:
+ 		wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0110-mtk-hostapd-add-support-for-link-add.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0110-mtk-hostapd-add-support-for-link-add.patch
new file mode 100644
index 0000000..ae3b4da
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0110-mtk-hostapd-add-support-for-link-add.patch
@@ -0,0 +1,292 @@
+From 4418652f8216f0fcdb74e4534d90f21f87d06dbc Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 28 Jun 2024 14:05:21 +0800
+Subject: [PATCH 110/126] mtk: hostapd: add support for link add
+
+Add support for adding affiliated APs to AP MLD via the following link add command
+hostapd_cli -i <mld intf> raw LINK_ADD bss_config=phyX:<bss_config>
+<bss_config> is per-bss config
+
+Add another cmd format:
+hostapd_cli -i <mld intf> link_add bss_config=phyX:<bss_config>
+
+Current link removal is implemented by hostapd_disable_iface for
+the sake of simplicity.
+When a link is added back after removal, hostapd_enable_iface should be
+called instead of hostapd_add_iface.
+Therefore, this patch is added to support iface enablement in link add cmd
+as a temporary workaround.
+
+Avoid modifying the original cmd str before entering hostapd_add_iface.
+Otherwise, hostapd_add_iface will fail to parse the cmd str correctly.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ hostapd/ctrl_iface.c  | 104 ++++++++++++++++++++++++++++++++++++++++++
+ hostapd/hostapd_cli.c |   8 ++++
+ src/ap/beacon.c       |  20 ++++++--
+ src/ap/hostapd.c      |  24 +++++++---
+ src/ap/hostapd.h      |   1 +
+ 5 files changed, 148 insertions(+), 9 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 3943cb3aa..988bc90de 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -3910,6 +3910,105 @@ static int hostapd_ctrl_iface_link_remove(struct hostapd_data *hapd, char *cmd,
+ 
+ 	return ret;
+ }
++
++
++static int hostapd_ctrl_iface_link_add(struct hostapd_data *hapd, char *cmd,
++				       char *buf, size_t buflen)
++{
++	struct hapd_interfaces *interfaces = hapd->iface->interfaces;
++	struct hostapd_iface *iface = NULL;
++	struct hostapd_data *h;
++	struct hostapd_config *conf;
++	const char *ifname, *conf_file, *phy;
++	u16 old_valid_links = 0;
++	bool hapd_existed = false;
++	char *pos, *tmp;
++	int i, ret = -1;
++	size_t len;
++
++	if (!hapd || !hapd->conf->mld_ap || !hapd->mld) {
++		wpa_printf(MSG_ERROR,
++			   "Trying to add link to non-MLD AP or non-existed AP");
++		return -1;
++	}
++
++	if (os_strncmp(cmd, "bss_config=", 11))
++		return -1;
++
++	len = os_strlen(cmd) + 1;
++	tmp = os_malloc(len);
++	if (!tmp)
++		return -1;
++
++	os_snprintf(tmp, len, "%s", cmd);
++	phy = tmp + 11;
++	pos = os_strchr(phy, ':');
++	if (!pos)
++		goto out;
++	*pos++ = '\0';
++	conf_file = pos;
++	if (!os_strlen(conf_file))
++		goto out;
++
++	conf = interfaces->config_read_cb(conf_file);
++	if (!conf)
++		goto out;
++
++	ifname = conf->bss[0]->iface;
++	if (ifname[0] != '\0' &&
++	    os_strncmp(ifname, hapd->conf->iface, sizeof(hapd->conf->iface))) {
++		wpa_printf(MSG_ERROR,
++			   "Interface name %s mismatch (expected %s)",
++			   ifname, hapd->conf->iface);
++		hostapd_config_free(conf);
++		goto out;
++	}
++
++	if (!conf->bss[0]->mld_ap) {
++		wpa_printf(MSG_ERROR, "The added interface is not MLD AP");
++		hostapd_config_free(conf);
++		goto out;
++	}
++
++	for (i = 0; i < interfaces->count; i++) {
++		if (os_strcmp(interfaces->iface[i]->phy, phy) == 0) {
++			iface = interfaces->iface[i];
++			break;
++		}
++	}
++	if (iface && iface->state == HAPD_IFACE_DISABLED) {
++		for (i = 0; i < iface->num_bss; i++) {
++			h = iface->bss[i];
++			if (ifname[0] != '\0' &&
++			    !os_strncmp(ifname, h->conf->iface, sizeof(h->conf->iface)))
++				hapd_existed = true;
++		}
++	}
++	hostapd_config_free(conf);
++
++	for_each_mld_link(h, hapd)
++		old_valid_links |= BIT(h->mld_link_id);
++	hapd->mld->link_reconf_in_progress = old_valid_links;
++
++	if (hapd_existed)
++		ret = hostapd_enable_iface(iface);
++	else
++		ret = hostapd_add_iface(interfaces, cmd);
++	if (ret < 0)
++		goto out;
++
++	ret = os_snprintf(buf, buflen, "%s\n", "OK");
++	if (os_snprintf_error(buflen, ret))
++		ret = -1;
++	else
++		ret = 0;
++
++out:
++	os_free(tmp);
++
++	return ret;
++}
++
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
+ 
+@@ -6048,6 +6147,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		if (hostapd_ctrl_iface_link_remove(hapd, buf + 12,
+ 						   reply, reply_size))
+ 			reply_len = -1;
++	} else if (os_strncmp(buf, "LINK_ADD ", 9) == 0) {
++		if (hostapd_ctrl_iface_link_add(hapd, buf + 9,
++						reply, reply_size))
++			reply_len = -1;
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
+ 	} else if (os_strncmp(buf, "SET_EDCCA ", 10) == 0) {
+@@ -6938,6 +7041,7 @@ void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
+ static int hostapd_ctrl_iface_add(struct hapd_interfaces *interfaces,
+ 				  char *buf)
+ {
++	/* TODO: handle link add via global ADD command */
+ 	if (hostapd_add_iface(interfaces, buf) < 0) {
+ 		wpa_printf(MSG_ERROR, "Adding interface %s failed", buf);
+ 		return -1;
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 54fda5c45..bfa912dff 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1779,6 +1779,12 @@ static int hostapd_cli_cmd_wmm(struct wpa_ctrl *ctrl, int argc,
+ 	return hostapd_cli_cmd(ctrl, "WMM", 1, argc, argv);
+ }
+ 
++static int hostapd_cli_cmd_link_add(struct wpa_ctrl *ctrl, int argc,
++				    char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "LINK_ADD", 1, argc, argv);
++}
++
+ struct hostapd_cli_cmd {
+ 	const char *cmd;
+ 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -2039,6 +2045,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 		" = Get preamble puncture status"},
+ 	{ "wmm", hostapd_cli_cmd_wmm, NULL,
+ 		" = <ac> [cwmin=] [cwmax=] [aifs=] [txop_limit=]"},
++	{ "link_add", hostapd_cli_cmd_link_add, NULL,
++		" = Add a new link to a MLD AP"},
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 50d45d532..8a9569549 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -2809,6 +2809,14 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
+ 		return 0;
+ 	}
+ 
++#ifdef CONFIG_IEEE80211BE
++#ifdef CONFIG_TESTING_OPTIONS
++	if (hapd->conf->mld_ap && hapd->mld &&
++	    (hapd->mld->link_reconf_in_progress & BIT(hapd->mld_link_id)))
++		ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_ADD_LINK);
++#endif /* CONFIG_TESTING_OPTIONS */
++#endif /* CONFIG_IEEE80211BE */
++
+ 	hapd->beacon_set_done = 1;
+ 
+ 	if (ieee802_11_build_ap_params(hapd, &params) < 0)
+@@ -2966,13 +2974,19 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
+ 	}
+ 
+ #ifdef CONFIG_IEEE80211BE
+-	/* clear critical update flag for UPDATE_SINGLE type, for other types,
+-	 * we should get some notified events from driver
++	/* clear critical update flag for UPDATE_SINGLE type & link adding,
++	 * for other types, we should get some notified events from driver
+ 	 */
+ 	if (hapd->conf->mld_ap) {
+-		for_each_mld_link(h, hapd)
++		for_each_mld_link(h, hapd) {
+ 			if (h->eht_mld_bss_critical_update == BSS_CRIT_UPDATE_SINGLE)
+ 				h->eht_mld_bss_critical_update = 0;
++			if (h->eht_mld_bss_critical_update == BSS_CRIT_UPDATE_FLAG &&
++			    (h->mld->link_reconf_in_progress & BIT(h->mld_link_id))) {
++				h->mld->link_reconf_in_progress &= ~BIT(h->mld_link_id);
++				h->eht_mld_bss_critical_update = 0;
++			}
++		}
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 63d89e614..257849536 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -574,6 +574,10 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
+ 	    hapd->iface->bss[0] != hapd)
+ 		hostapd_if_link_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface,
+ 				       hapd->mld_link_id);
++	/* Clear the link reconfiguration flag when the added link failed to setup */
++	if (hapd->conf->mld_ap && hapd->mld &&
++	    !(hapd->mld->link_reconf_in_progress & BIT(hapd->mld_link_id)))
++		hapd->mld->link_reconf_in_progress = 0;
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ 	wpabuf_free(hapd->time_adv);
+@@ -3450,11 +3454,20 @@ hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+ 		}
+ 
+ 		ifname = conf->bss[0]->iface;
+-		if (ifname[0] != '\0' && ifname_in_use(interfaces, ifname)) {
+-			wpa_printf(MSG_ERROR,
+-				   "Interface name %s already in use", ifname);
+-			hostapd_config_free(conf);
+-			return NULL;
++		if (conf->bss[0]->mld_ap) {
++			if (!iface->bss[0]->conf->mld_ap) {
++				wpa_printf(MSG_ERROR,
++					   "Cannot add a MLO BSS when the first BSS is non-MLO");
++				hostapd_config_free(conf);
++				return NULL;
++			}
++		} else {
++			if (ifname[0] != '\0' && ifname_in_use(interfaces, ifname)) {
++				wpa_printf(MSG_ERROR,
++					   "Interface name %s already in use", ifname);
++				hostapd_config_free(conf);
++				return NULL;
++			}
+ 		}
+ 
+ 		tmp_conf = os_realloc_array(
+@@ -3486,7 +3499,6 @@ hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+ 		hapd->msg_ctx = hapd;
+ 		hostapd_bss_setup_multi_link(hapd, interfaces);
+ 
+-
+ 		bss_idx = iface->num_bss++;
+ 		conf->num_bss--;
+ 		conf->bss[0] = NULL;
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 99d5a01d6..52875cca1 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -549,6 +549,7 @@ struct hostapd_mld {
+ 	 */
+ 	u8 refcount;
+ 	bool started;
++	u16 link_reconf_in_progress;
+ 
+ 	struct hostapd_data *fbss;
+ 	struct dl_list links; /* List head of all affiliated links */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0111-mtk-hostapd-Fix-the-op_class-value-in-the-RNR-IE-to-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0111-mtk-hostapd-Fix-the-op_class-value-in-the-RNR-IE-to-.patch
new file mode 100644
index 0000000..0dd281b
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0111-mtk-hostapd-Fix-the-op_class-value-in-the-RNR-IE-to-.patch
@@ -0,0 +1,42 @@
+From b039d88e5924a340709646c5e855bc06fcb1da78 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+Date: Thu, 4 Jul 2024 13:37:23 +0800
+Subject: [PATCH 111/126] mtk: hostapd: Fix the op_class value in the RNR IE to
+ achieve backward compatibility
+
+According to the chapter 11.49 Reduced neighbor report
+of DraftP802.11be_D5.1, the AP should select (one of) the operating
+class(es) that is expected to be understood by all STAs that might
+intend to connect to the reported AP, even if the channel spacing of
+that operating class is less than the BSS bandwidth of all the reported
+APs in the Neighbor AP Information field.
+
+For example, if an AP reports an EHT AP that is operating a BSS with 320
+MHz bandwidth in 6 GHz band using operating class 137, it is recommended
+that the operating class selected by the AP in the Reduced Neighbor Report
+element is operating class 131, 132, 133 or 134, since these are expected
+to be understood by all STAs that can connect to the reported 6GHz AP.
+
+Signed-off-by: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Signed-off-by: Money Wang <money.wang@mediatek.com>
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ src/ap/ieee802_11.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 62fdfb288..75ffb2a18 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7841,7 +7841,7 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
+ 		 * one TBTT info available. */
+ 		*tbtt_count_pos = eid++;
+ 		*eid++ = tbtt_info_len;
+-		*eid++ = op_class;
++		*eid++ = (op_class == 137 ? 134 : op_class);
+ 		*eid++ = bss->iconf->channel;
+ 		*len += RNR_TBTT_HEADER_LEN;
+ 	}
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0112-mtk-hostapd-support-enable-disable-preamble-puncture.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0112-mtk-hostapd-support-enable-disable-preamble-puncture.patch
new file mode 100644
index 0000000..0f8af3e
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0112-mtk-hostapd-support-enable-disable-preamble-puncture.patch
@@ -0,0 +1,470 @@
+From d45a052e2c7efe295705ad2185f36b38058ede83 Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Tue, 28 May 2024 17:46:26 +0800
+Subject: [PATCH 112/126] mtk: hostapd: support enable/disable preamble
+ puncture from mtk vendor command
+
+Add mtk vendor event to update punct bitmap and trigger channel switch.
+Change to pp user mode when use hostapd_cli to channel switch.
+Change pp vendor cmd use link_id instead of band_idx.
+Remove band_ind in hostapd_cli get/set pp cmd.
+
+Change pp default enable firmware mode.
+
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ hostapd/config_file.c              |  1 -
+ hostapd/ctrl_iface.c               | 45 ++++++++--------
+ hostapd/hostapd_cli.c              |  4 +-
+ src/ap/ap_config.c                 |  2 +-
+ src/ap/ap_drv_ops.c                | 10 +++-
+ src/ap/drv_callbacks.c             | 82 ++++++++++++++++++++++++++++++
+ src/common/mtk_vendor.h            |  8 ++-
+ src/drivers/driver.h               | 11 +++-
+ src/drivers/driver_nl80211.c       | 10 ++--
+ src/drivers/driver_nl80211_event.c | 48 +++++++++++++++++
+ 10 files changed, 185 insertions(+), 36 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 38273a4f2..f7bfc357a 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5452,7 +5452,6 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		if (get_u16(pos, line, &conf->punct_bitmap))
+ 			return 1;
+ 		conf->punct_bitmap = atoi(pos);
+-		conf->pp_mode = PP_USR_MODE;
+ 	} else if (os_strcmp(buf, "punct_acs_threshold") == 0) {
+ 		int val = atoi(pos);
+ 
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 988bc90de..337261f8f 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -2818,6 +2818,7 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ #ifdef NEED_AP_MLME
+ 	struct hostapd_hw_modes *mode = iface->current_mode;
+ 	struct csa_settings settings, background_settings;
++	struct hostapd_data *hapd;
+ 	int ret;
+ 	int freq, state;
+ 	int bandwidth, oper_chwidth;
+@@ -2914,6 +2915,17 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ 		break;
+ 	}
+ 
++#ifdef CONFIG_IEEE80211BE
++	hapd = iface->bss[0];
++	if (hapd->iconf->punct_bitmap != settings.punct_bitmap &&
++	    hapd->iconf->pp_mode != PP_USR_MODE) {
++		hapd->iconf->pp_mode = PP_USR_MODE;
++		ret = hostapd_drv_pp_mode_set(hapd);
++		if (ret)
++			return ret;
++	}
++#endif /* CONFIG_IEEE80211BE */
++
+ 	for (i = 0; i < iface->num_bss; i++) {
+ 
+ 		/* Save CHAN_SWITCH VHT, HE, and EHT config */
+@@ -5172,8 +5184,7 @@ static int
+ hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ 			  size_t buflen)
+ {
+-	char *band, *config, *value;
+-	u8 band_idx;
++	char *config, *value;
+ 
+ 	config = cmd;
+ 
+@@ -5182,31 +5193,26 @@ hostapd_ctrl_iface_set_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ 		return -1;
+ 	*value++ = '\0';
+ 
+-	band = os_strchr(value, ' ');
+-	if (band == NULL)
+-		return -1;
+-	*band++ = '\0';
+-	band_idx = strtol(band, NULL, 10);
+-
+-	hapd = hostapd_get_hapd_by_band_idx(hapd, band_idx);
+-
+-	if (!hapd)
+-		return -1;
+-
+ 	if (os_strcmp(config, "mode") == 0) {
+ 		int val = strtol(value, NULL, 10);
+ 
+-		if (val < PP_DISABLE || val > PP_FW_MODE) {
++		switch(val) {
++		case PP_DISABLE:
++		case PP_FW_MODE:
++			break;
++		case PP_USR_MODE:
++		default:
+ 			wpa_printf(MSG_ERROR, "Invalid value for SET_PP");
+ 			return -1;
+ 		}
+ 		hapd->iconf->pp_mode = (u8) val;
++		hapd->iconf->punct_bitmap = 0;
+ 		if (hostapd_drv_pp_mode_set(hapd) != 0)
+ 			return -1;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Unsupported parameter %s for SET_PP"
+-			   "Usage: set_pp mode <value> <band_idx>", config);
++			   "Usage: set_pp mode <value>", config);
+ 		return -1;
+ 	}
+ 	return os_snprintf(buf, buflen, "OK\n");
+@@ -5216,15 +5222,6 @@ static int
+ hostapd_ctrl_iface_get_pp(struct hostapd_data *hapd, char *cmd, char *buf,
+ 			  size_t buflen)
+ {
+-	u8 band_idx;
+-
+-	band_idx = strtol(cmd, NULL, 10);
+-
+-	hapd = hostapd_get_hapd_by_band_idx(hapd, band_idx);
+-
+-	if (!hapd)
+-		return -1;
+-
+ 	return os_snprintf(buf, buflen, "pp_mode: %d, punct_bitmap: 0x%04x\n",
+ 			   hapd->iconf->pp_mode, hapd->iconf->punct_bitmap);
+ }
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index bfa912dff..12ee0a18f 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1764,13 +1764,13 @@ static int hostapd_cli_cmd_dump_csi(struct wpa_ctrl *ctrl, int argc,
+ static int hostapd_cli_cmd_set_pp(struct wpa_ctrl *ctrl, int argc,
+ 					   char *argv[])
+ {
+-	return hostapd_cli_cmd(ctrl, "set_pp", 3, argc, argv);
++	return hostapd_cli_cmd(ctrl, "set_pp", 2, argc, argv);
+ }
+ 
+ static int hostapd_cli_cmd_get_pp(struct wpa_ctrl *ctrl, int argc,
+ 					   char *argv[])
+ {
+-	return hostapd_cli_cmd(ctrl, "get_pp", 1, argc, argv);
++	return hostapd_cli_cmd(ctrl, "get_pp", 0, argc, argv);
+ }
+ 
+ static int hostapd_cli_cmd_wmm(struct wpa_ctrl *ctrl, int argc,
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 95100b25c..4528df823 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -312,7 +312,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 	conf->three_wire_enable = THREE_WIRE_MODE_DISABLE;
+ 	conf->ibf_enable = IBF_DEFAULT_ENABLE;
+ 	conf->amsdu = 1;
+-	conf->pp_mode = PP_DISABLE;
++	conf->pp_mode = PP_FW_MODE;
+ 	conf->band_idx = 255;
+ 
+ 	hostapd_set_and_check_bw320_offset(conf, 0);
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 8631bf960..f9ec9a689 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1447,14 +1447,20 @@ int hostapd_drv_background_radar_mode(struct hostapd_data *hapd)
+ 
+ int hostapd_drv_pp_mode_set(struct hostapd_data *hapd)
+ {
++	s8 link_id = -1;
++
+ 	if (!hapd->driver || !hapd->driver->pp_mode_set ||
+-	    hapd->iconf->pp_mode >= PP_USR_MODE ||
++	    hapd->iconf->pp_mode > PP_USR_MODE ||
+ 	    hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+ 		return 0;
+ 
++	if (hapd->conf->mld_ap)
++		link_id = hapd->mld_link_id;
++
+ 	return hapd->driver->pp_mode_set(hapd->drv_priv,
+ 					 hapd->iconf->pp_mode,
+-					 hapd->iconf->band_idx);
++					 link_id,
++					 hapd->iconf->punct_bitmap);
+ }
+ 
+ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode)
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 0e7cfd285..420d156c8 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -43,6 +43,7 @@
+ #include "fils_hlp.h"
+ #include "neighbor_db.h"
+ #include "nan_usd_ap.h"
++#include "ap/beacon.h"
+ 
+ 
+ #ifdef CONFIG_FILS
+@@ -2456,6 +2457,81 @@ static void hostapd_event_color_change(struct hostapd_data *hapd, bool success)
+ #endif  /* CONFIG_IEEE80211AX */
+ 
+ 
++static void hostapd_event_pp_bitmap_update(struct hostapd_data *hapd,
++					   struct ch_switch *ch_switch)
++{
++	struct hostapd_iface *iface = hapd->iface;
++	struct hostapd_hw_modes *cmode = iface->current_mode;
++	int err, freq;
++	struct csa_settings csa_settings;
++	unsigned int i;
++
++	/* Check if CSA in progress */
++	if (hostapd_csa_in_progress(iface))
++		return;
++
++	if (!hw_get_channel_chan(cmode, iface->conf->channel, &freq))
++		return;
++
++	if (iface->conf->punct_bitmap == ch_switch->punct_bitmap ||
++	    freq != ch_switch->freq)
++		return;
++
++	/* Setup CSA request */
++	os_memset(&csa_settings, 0, sizeof(csa_settings));
++	csa_settings.cs_count = 5;
++	csa_settings.block_tx = 0;
++	csa_settings.punct_bitmap = ch_switch->punct_bitmap;
++	csa_settings.link_id = ch_switch->link_id;
++
++	err = hostapd_set_freq_params(&csa_settings.freq_params,
++				      iface->conf->hw_mode,
++				      freq,
++				      iface->conf->channel,
++				      iface->conf->enable_edmg,
++				      iface->conf->edmg_channel,
++				      iface->conf->ieee80211n,
++				      iface->conf->ieee80211ac,
++				      iface->conf->ieee80211ax,
++				      iface->conf->ieee80211be,
++				      iface->conf->secondary_channel,
++				      hostapd_get_oper_chwidth(iface->conf),
++				      hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
++				      hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
++				      cmode->vht_capab,
++				      &cmode->he_capab[IEEE80211_MODE_AP],
++				      &cmode->eht_capab[IEEE80211_MODE_AP],
++				      ch_switch->punct_bitmap);
++
++	if (err) {
++		wpa_printf(MSG_ERROR,
++			   "Failed to calculate CSA freq params");
++		hostapd_disable_iface(iface);
++		return;
++	}
++
++	for (i = 0; i < iface->num_bss; i++) {
++		ieee802_11_set_bss_critical_update(iface->bss[i],
++						   BSS_CRIT_UPDATE_EVENT_CSA);
++
++		err = hostapd_switch_channel(iface->bss[i], &csa_settings);
++		if (err)
++			break;
++
++#ifdef CONFIG_IEEE80211BE
++		if (iface->bss[i]->conf->mld_ap)
++			hostapd_update_aff_link_beacon(iface->bss[i],
++						       csa_settings.cs_count);
++
++		/* FIXME:
++		 * CU flag should be cleared when receiving DTIM event from FW
++		 */
++		iface->bss[i]->eht_mld_bss_critical_update = 0;
++#endif /* CONFIG_IEEE80211BE */
++	}
++}
++
++
+ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ 		       union wpa_event_data *data)
+ {
+@@ -2759,6 +2835,12 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ 		hostapd_event_dfs_cac_started(hapd, &data->dfs_event);
+ 		break;
+ #endif /* NEED_AP_MLME */
++	case EVENT_PP_BITMAP_UPDATE:
++		if (!data)
++			break;
++		hapd = switch_link_hapd(hapd, data->ch_switch.link_id);
++		hostapd_event_pp_bitmap_update(hapd, &data->ch_switch);
++		break;
+ 	case EVENT_INTERFACE_ENABLED:
+ 		wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_ENABLED);
+ 		if (hapd->disabled && hapd->started) {
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 937b968d5..933f0099d 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -22,6 +22,10 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_EML_CTRL = 0xd3,
+ };
+ 
++enum mtk_nl80211_vendor_subevents {
++	MTK_NL80211_VENDOR_EVENT_PP_BMP_UPDATE = 0x5,
++};
++
+ enum mtk_vendor_attr_edcca_ctrl {
+ 	MTK_VENDOR_ATTR_EDCCA_THRESHOLD_INVALID = 0,
+ 
+@@ -271,7 +275,9 @@ enum mtk_vendor_attr_pp_ctrl {
+ 	MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
+ 
+ 	MTK_VENDOR_ATTR_PP_MODE,
+-	MTK_VENDOR_ATTR_PP_BAND_IDX,
++	MTK_VENDOR_ATTR_PP_LINK_ID,
++	MTK_VENDOR_ATTR_PP_BITMAP,
++	MTK_VENDOR_ATTR_PP_CURR_FREQ,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_PP_CTRL,
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index dacf0a98d..5e65d9e0b 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5381,9 +5381,10 @@ struct wpa_driver_ops {
+ 	 * pp_mode_set - Set preamble puncture operation mode
+ 	 * @priv: Private driver interface data
+ 	 * @pp_mode: Value is defined in enum pp_mode
+-	 * @band_idx: chip band index
++	 * @link_id: MLD link id. -1 if this is an non-MLD AP
++	 * @punct_bitmap: current puncture bitmap
+ 	 */
+-	int (*pp_mode_set)(void *priv, const u8 pp_mode, u8 band_idx);
++	int (*pp_mode_set)(void *priv, const u8 pp_mode, s8 link_id, u16 punct_bitmap);
+ #ifdef CONFIG_IEEE80211BE
+ 	int (*get_mld_addr)(void *priv, u8 *addr);
+ #endif
+@@ -6058,6 +6059,12 @@ enum wpa_event_type {
+ 	 * channel has been updated and operating channel should expand its width.
+ 	 */
+ 	EVENT_DFS_BACKGROUND_CHAN_EXPAND,
++
++	/**
++	 * EVENT_PP_BITMAP_UPDATE - Notification that the new puncture bitmap
++	 * has been applied and a channel switch should be triggered.
++	 */
++	EVENT_PP_BITMAP_UPDATE,
+ };
+ 
+ 
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 80fe2e591..5d97317f7 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -160,7 +160,9 @@ amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
+ static struct nla_policy
+ pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
+ 	[MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
+-	[MTK_VENDOR_ATTR_PP_BAND_IDX] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_PP_LINK_ID] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_PP_BITMAP] = { .type = NLA_U16 },
++	[MTK_VENDOR_ATTR_PP_CURR_FREQ] = { .type = NLA_U32 },
+ };
+ 
+ static struct nla_policy csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
+@@ -15248,7 +15250,7 @@ static int nl80211_background_radar_mode(void *priv, const u8 background_radar_m
+ 	return ret;
+ }
+ 
+-static int nl80211_pp_mode_set(void *priv, const u8 pp_mode, u8 band_idx)
++static int nl80211_pp_mode_set(void *priv, const u8 pp_mode, s8 link_id, u16 punct_bitmap)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+@@ -15275,8 +15277,10 @@ static int nl80211_pp_mode_set(void *priv, const u8 pp_mode, u8 band_idx)
+ 	if (!data)
+ 		goto fail;
+ 
+-	nla_put_u8(msg, MTK_VENDOR_ATTR_PP_BAND_IDX, band_idx);
++	if (link_id > -1)
++		nla_put_u8(msg, MTK_VENDOR_ATTR_PP_LINK_ID, link_id);
+ 	nla_put_u8(msg, MTK_VENDOR_ATTR_PP_MODE, pp_mode);
++	nla_put_u16(msg, MTK_VENDOR_ATTR_PP_BITMAP, punct_bitmap);
+ 
+ 	nla_nest_end(msg, data);
+ 	ret = send_and_recv_cmd(drv, msg);
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index e95593a5b..efdb8ef7f 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -19,6 +19,7 @@
+ #include "common/ieee802_11_defs.h"
+ #include "common/ieee802_11_common.h"
+ #include "driver_nl80211.h"
++#include "common/mtk_vendor.h"
+ 
+ 
+ static void
+@@ -3332,6 +3333,50 @@ static void nl80211_vendor_event_brcm(struct wpa_driver_nl80211_data *drv,
+ 
+ #endif /* CONFIG_DRIVER_NL80211_BRCM */
+ 
++static void mtk_nl80211_pp_bitmap_update(struct wpa_driver_nl80211_data *drv,
++					 const u8 *data, size_t len)
++{
++	struct nlattr *tb[MTK_VENDOR_ATTR_PP_CTRL_MAX + 1];
++	union wpa_event_data event;
++
++	wpa_printf(MSG_DEBUG,
++		   "nl80211: MTK pp bitmap update vendor event received");
++
++	if (nla_parse(tb, MTK_VENDOR_ATTR_PP_CTRL_MAX,
++		      (struct nlattr *) data, len, NULL) ||
++	    !tb[MTK_VENDOR_ATTR_PP_CURR_FREQ] ||
++	    !tb[MTK_VENDOR_ATTR_PP_BITMAP])
++		return;
++
++	os_memset(&event, 0, sizeof(event));
++	event.ch_switch.freq = nla_get_u32(tb[MTK_VENDOR_ATTR_PP_CURR_FREQ]);
++
++	event.ch_switch.link_id =
++		nl80211_get_link_id_by_freq(drv->first_bss, event.ch_switch.freq);
++	event.ch_switch.punct_bitmap =
++		nla_get_u16(tb[MTK_VENDOR_ATTR_PP_BITMAP]);
++
++	wpa_printf(MSG_DEBUG,
++		   "nl80211: puncture bitmap: 0x%04x, link_id: %d",
++		   event.ch_switch.punct_bitmap, event.ch_switch.link_id);
++	wpa_supplicant_event(drv->ctx, EVENT_PP_BITMAP_UPDATE, &event);
++}
++
++static void nl80211_vendor_event_mtk(struct wpa_driver_nl80211_data *drv,
++				      u32 subcmd, u8 *data, size_t len)
++{
++	wpa_printf(MSG_DEBUG, "nl80211: Got MTK vendor event %u", subcmd);
++	switch (subcmd) {
++	case MTK_NL80211_VENDOR_EVENT_PP_BMP_UPDATE:
++		mtk_nl80211_pp_bitmap_update(drv, data, len);
++		break;
++	default:
++		wpa_printf(MSG_DEBUG,
++			   "%s: Ignore unsupported MTK vendor event %u",
++			   __func__, subcmd);
++		break;
++	}
++}
+ 
+ static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv,
+ 				 struct nlattr **tb)
+@@ -3388,6 +3433,9 @@ static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv,
+ 		nl80211_vendor_event_brcm(drv, subcmd, data, len);
+ 		break;
+ #endif /* CONFIG_DRIVER_NL80211_BRCM */
++	case OUI_MTK:
++		nl80211_vendor_event_mtk(drv, subcmd, data, len);
++		break;
+ 	default:
+ 		wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event");
+ 		break;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0113-mtk-hostapd-do-not-consider-ht-operation-update-for-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0113-mtk-hostapd-do-not-consider-ht-operation-update-for-.patch
new file mode 100644
index 0000000..f69891a
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0113-mtk-hostapd-do-not-consider-ht-operation-update-for-.patch
@@ -0,0 +1,28 @@
+From 5cc02e29a2725dcffbee12fece9d946dd809108b Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Wed, 10 Jul 2024 16:13:24 +0800
+Subject: [PATCH 113/126] mtk: hostapd: do not consider ht operation update for
+ 6G BSS
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ieee802_11.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 75ffb2a18..10b7587a0 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -4628,7 +4628,8 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
+ 		}
+ 		hapd->sta_aid[(sta->aid - 1) / 32] |= BIT((sta->aid - 1) % 32);
+ 		sta->listen_interval = origin_sta->listen_interval;
+-		if (update_ht_state(hapd, sta) > 0)
++		if (!is_6ghz_op_class(hapd->iconf->op_class) &&
++		    update_ht_state(hapd, sta) > 0)
+ 			ieee802_11_update_beacons(hapd->iface);
+ 	}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0114-mtk-hostapd-Add-bandwidth-indication-IE.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0114-mtk-hostapd-Add-bandwidth-indication-IE.patch
new file mode 100644
index 0000000..473d91f
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0114-mtk-hostapd-Add-bandwidth-indication-IE.patch
@@ -0,0 +1,485 @@
+From fd208c32b7e95c3aafce8f89917d4a961713fa75 Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Tue, 2 Jul 2024 10:38:49 +0800
+Subject: [PATCH 114/126] mtk: hostapd: Add bandwidth indication IE
+
+Move punct_bitmap from csa_settings to hostapd_freq_params for
+filling bandwidth indication IE while channel switch occurs.
+Handle bitmap change in hostapd_set_freq_params.
+
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ hostapd/ctrl_iface.c            |  18 +++---
+ src/ap/ctrl_iface_ap.c          |  12 +---
+ src/ap/dfs.c                    |   1 -
+ src/ap/drv_callbacks.c          |   1 -
+ src/ap/hostapd.c                |  14 +---
+ src/ap/ieee802_11.c             | 110 +++++++++++++++++++++++++++++---
+ src/ap/ieee802_11.h             |   2 +
+ src/common/hw_features_common.c |   1 +
+ src/common/ieee802_11_defs.h    |  30 +++++++++
+ src/drivers/driver.h            |  10 ++-
+ src/drivers/driver_nl80211.c    |   6 +-
+ 11 files changed, 156 insertions(+), 49 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 337261f8f..c8456513e 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -2621,8 +2621,7 @@ static int hostapd_ctrl_register_frame(struct hostapd_data *hapd,
+ 
+ 
+ #ifdef NEED_AP_MLME
+-static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params,
+-					  u16 punct_bitmap)
++static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params)
+ {
+ 	u32 start_freq;
+ 
+@@ -2673,14 +2672,14 @@ static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params,
+ 		if (params->center_freq2 || params->sec_channel_offset)
+ 			return -1;
+ 
+-		if (punct_bitmap)
++		if (params->punct_bitmap)
+ 			return -1;
+ 		break;
+ 	case 40:
+ 		if (params->center_freq2 || !params->sec_channel_offset)
+ 			return -1;
+ 
+-		if (punct_bitmap)
++		if (params->punct_bitmap)
+ 			return -1;
+ 
+ 		if (!params->center_freq1)
+@@ -2717,7 +2716,7 @@ static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params,
+ 			return -1;
+ 		}
+ 
+-		if (params->center_freq2 && punct_bitmap)
++		if (params->center_freq2 && params->punct_bitmap)
+ 			return -1;
+ 
+ 		/* Adjacent and overlapped are not allowed for 80+80 */
+@@ -2784,7 +2783,7 @@ static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params,
+ 		return -1;
+ 	}
+ 
+-	if (!punct_bitmap)
++	if (!params->punct_bitmap)
+ 		return 0;
+ 
+ 	if (!params->eht_enabled) {
+@@ -2802,7 +2801,7 @@ static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params,
+ 	start_freq = params->center_freq1 - (params->bandwidth / 2);
+ 	if (!is_punct_bitmap_valid(params->bandwidth,
+ 				   (params->freq - start_freq) / 20,
+-				   punct_bitmap)) {
++				   params->punct_bitmap)) {
+ 		wpa_printf(MSG_ERROR, "Invalid preamble puncturing bitmap");
+ 		return -1;
+ 	}
+@@ -2843,8 +2842,7 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ 		return -1;
+ 	}
+ 
+-	ret = hostapd_ctrl_check_freq_params(&settings.freq_params,
+-					     settings.punct_bitmap);
++	ret = hostapd_ctrl_check_freq_params(&settings.freq_params);
+ 	if (ret) {
+ 		wpa_printf(MSG_INFO,
+ 			   "chanswitch: invalid frequency settings provided");
+@@ -2917,7 +2915,7 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ 
+ #ifdef CONFIG_IEEE80211BE
+ 	hapd = iface->bss[0];
+-	if (hapd->iconf->punct_bitmap != settings.punct_bitmap &&
++	if (hapd->iconf->punct_bitmap != settings.freq_params.punct_bitmap &&
+ 	    hapd->iconf->pp_mode != PP_USR_MODE) {
+ 		hapd->iconf->pp_mode = PP_USR_MODE;
+ 		ret = hostapd_drv_pp_mode_set(hapd);
+diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
+index b0ee00b90..e1722620d 100644
+--- a/src/ap/ctrl_iface_ap.c
++++ b/src/ap/ctrl_iface_ap.c
+@@ -1125,20 +1125,11 @@ int hostapd_parse_csa_settings(const char *pos,
+ 		} \
+ 	} while (0)
+ 
+-#define SET_CSA_SETTING_EXT(str) \
+-	do { \
+-		const char *pos2 = os_strstr(pos, " " #str "="); \
+-		if (pos2) { \
+-			pos2 += sizeof(" " #str "=") - 1; \
+-			settings->str = atoi(pos2); \
+-		} \
+-	} while (0)
+-
+ 	SET_CSA_SETTING(center_freq1);
+ 	SET_CSA_SETTING(center_freq2);
+ 	SET_CSA_SETTING(bandwidth);
+ 	SET_CSA_SETTING(sec_channel_offset);
+-	SET_CSA_SETTING_EXT(punct_bitmap);
++	SET_CSA_SETTING(punct_bitmap);
+ 	settings->freq_params.ht_enabled = !!os_strstr(pos, " ht");
+ 	settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
+ 	settings->freq_params.he_enabled = !!os_strstr(pos, " he");
+@@ -1146,7 +1137,6 @@ int hostapd_parse_csa_settings(const char *pos,
+ 	settings->freq_params.radar_background = !!os_strstr(pos, " skip_cac");
+ 	settings->block_tx = !!os_strstr(pos, " blocktx");
+ #undef SET_CSA_SETTING
+-#undef SET_CSA_SETTING_EXT
+ 
+ 	return 0;
+ }
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 5e9a2a4ce..5a3112d40 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1081,7 +1081,6 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
+ 	os_memset(&csa_settings, 0, sizeof(csa_settings));
+ 	csa_settings.cs_count = 5;
+ 	csa_settings.block_tx = 1;
+-	csa_settings.punct_bitmap = hostapd_get_punct_bitmap(iface->bss[0]);
+ 	csa_settings.link_id = -1;
+ #ifdef CONFIG_IEEE80211BE
+ 	if (iface->bss[0]->conf->mld_ap)
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 420d156c8..96b8e856e 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2481,7 +2481,6 @@ static void hostapd_event_pp_bitmap_update(struct hostapd_data *hapd,
+ 	os_memset(&csa_settings, 0, sizeof(csa_settings));
+ 	csa_settings.cs_count = 5;
+ 	csa_settings.block_tx = 0;
+-	csa_settings.punct_bitmap = ch_switch->punct_bitmap;
+ 	csa_settings.link_id = ch_switch->link_id;
+ 
+ 	err = hostapd_set_freq_params(&csa_settings.freq_params,
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 257849536..73378053b 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4487,6 +4487,8 @@ static int hostapd_change_config_freq(struct hostapd_data *hapd,
+ 	hostapd_set_oper_centr_freq_seg0_idx(conf, seg0);
+ 	hostapd_set_oper_centr_freq_seg1_idx(conf, seg1);
+ 
++	conf->punct_bitmap = params->punct_bitmap;
++
+ 	/* TODO: maybe call here hostapd_config_check here? */
+ 
+ 	return 0;
+@@ -4499,9 +4501,6 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
+ 	struct hostapd_iface *iface = hapd->iface;
+ 	struct hostapd_freq_params old_freq;
+ 	int ret;
+-#ifdef CONFIG_IEEE80211BE
+-	u16 old_punct_bitmap;
+-#endif /* CONFIG_IEEE80211BE */
+ 	u8 chan, bandwidth;
+ 
+ 	os_memset(&old_freq, 0, sizeof(old_freq));
+@@ -4550,19 +4549,11 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
+ 	if (ret)
+ 		return ret;
+ 
+-#ifdef CONFIG_IEEE80211BE
+-	old_punct_bitmap = iface->conf->punct_bitmap;
+-	iface->conf->punct_bitmap = settings->punct_bitmap;
+-#endif /* CONFIG_IEEE80211BE */
+-
+ 	/* Another CU in the new channel due to OP element modification */
+ 	ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_EHT_OPERATION);
+ 	ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
+ 
+ 	/* change back the configuration */
+-#ifdef CONFIG_IEEE80211BE
+-	iface->conf->punct_bitmap = old_punct_bitmap;
+-#endif /* CONFIG_IEEE80211BE */
+ 	hostapd_change_config_freq(iface->bss[0], iface->conf,
+ 				   &old_freq, NULL);
+ 
+@@ -4739,7 +4730,6 @@ int hostapd_update_aff_link_beacon(struct hostapd_data *hapd, u8 cs_count)
+ 		settings.link_id = cs_link_id;
+ 		settings.freq_params.link_id = link_id;
+ 		settings.cs_count = cs_count;
+-		settings.punct_bitmap = conf->punct_bitmap;
+ 		ret = hostapd_drv_switch_channel(h, &settings);
+ 		free_beacon_data(&settings.beacon_csa);
+ 		free_beacon_data(&settings.beacon_after);
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 10b7587a0..93bd8255c 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7368,10 +7368,95 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
+ 				    tx_pwr);
+ }
+ 
++#define DISABLED_SUBCHANNEL_BITMAP_BYTES_SIZE 2
++u8 *hostapd_eid_bw_indication(struct hostapd_data *hapd, u8 *eid, u8 ccfs0, u8 ccfs1)
++{
++	u8 *pos = eid, *length_pos;
++	struct ieee80211_bw_ind_element *bw_ind_elem;
++	int bw_ind_status = false;
++	size_t fixed_eid_len;
++
++	if (hapd->cs_freq_params.bandwidth > 160 ||
++	    hapd->cs_freq_params.punct_bitmap)
++		bw_ind_status = true;
++
++	if (!bw_ind_status)
++		return eid;
++
++	if (!hapd->cs_freq_params.channel || !hapd->cs_freq_params.eht_enabled)
++		return eid;
++
++	*pos++ = WLAN_EID_EXTENSION;
++	length_pos = pos++;
++	*pos++ = WLAN_EID_EXT_BW_IND;
++
++	fixed_eid_len = (sizeof(struct ieee80211_bw_ind_element) -
++			DISABLED_SUBCHANNEL_BITMAP_BYTES_SIZE);
++
++	bw_ind_elem = (struct ieee80211_bw_ind_element *) pos;
++	os_memset(bw_ind_elem, 0, sizeof(struct ieee80211_bw_ind_element));
++
++	if (hapd->cs_freq_params.punct_bitmap) {
++		bw_ind_elem->bw_ind_params |=
++			BW_IND_PARAMETER_DISABLED_SUBCHAN_BITMAP_PRESENT;
++		bw_ind_elem->bw_ind_info.disabled_chan_bitmap =
++			host_to_le16(hapd->cs_freq_params.punct_bitmap);
++		pos += DISABLED_SUBCHANNEL_BITMAP_BYTES_SIZE;
++	} else {
++		bw_ind_elem->bw_ind_params &=
++			~BW_IND_PARAMETER_DISABLED_SUBCHAN_BITMAP_PRESENT;
++	}
++
++	switch (hapd->cs_freq_params.bandwidth) {
++	case 320:
++		bw_ind_elem->bw_ind_info.control |=
++			BW_IND_CHANNEL_WIDTH_320MHZ;
++		ccfs1 = ccfs0;
++		if (hapd->cs_freq_params.channel < ccfs0)
++			ccfs0 -= 16;
++		else
++			ccfs0 += 16;
++		break;
++	case 160:
++		bw_ind_elem->bw_ind_info.control |=
++			BW_IND_CHANNEL_WIDTH_160MHZ;
++		ccfs1 = ccfs0;
++		if (hapd->cs_freq_params.channel < ccfs0)
++			ccfs0 -= 8;
++		else
++			ccfs0 += 8;
++		break;
++	case 80:
++		bw_ind_elem->bw_ind_info.control |=
++			BW_IND_CHANNEL_WIDTH_80MHZ;
++		break;
++	case 40:
++		if (hapd->cs_freq_params.sec_channel_offset == 1)
++			bw_ind_elem->bw_ind_info.control |=
++				BW_IND_CHANNEL_WIDTH_40MHZ;
++		else
++			bw_ind_elem->bw_ind_info.control |=
++				BW_IND_CHANNEL_WIDTH_20MHZ;
++		break;
++	default:
++		bw_ind_elem->bw_ind_info.control |=
++			BW_IND_CHANNEL_WIDTH_20MHZ;
++		break;
++	}
++
++	bw_ind_elem->bw_ind_info.ccfs0 = ccfs0;
++	bw_ind_elem->bw_ind_info.ccfs1 = ccfs1;
++
++	pos += fixed_eid_len;
++	*length_pos = pos - (eid + 2);
++
++	return pos;
++}
++
+ 
+ u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
+ {
+-	u8 bw, chan1 = 0, chan2 = 0;
++	u8 bw, chan1 = 0, chan2 = 0, ccfs0, ccfs1, *pos = eid, *length_pos;
+ 	int freq1;
+ 
+ 	if (!hapd->cs_freq_params.channel ||
+@@ -7412,11 +7497,14 @@ u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
+ 				   &chan2) != HOSTAPD_MODE_IEEE80211A)
+ 		return eid;
+ 
+-	*eid++ = WLAN_EID_CHANNEL_SWITCH_WRAPPER;
+-	*eid++ = 5; /* Length of Channel Switch Wrapper */
+-	*eid++ = WLAN_EID_WIDE_BW_CHSWITCH;
+-	*eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
+-	*eid++ = bw; /* New Channel Width */
++	*pos++ = WLAN_EID_CHANNEL_SWITCH_WRAPPER;
++	length_pos = pos++; /* Length of Channel Switch Wrapper */
++	*pos++ = WLAN_EID_WIDE_BW_CHSWITCH;
++	*pos++ = 3; /* Length of Wide Bandwidth Channel Switch element */
++	*pos++ = bw; /* New Channel Width */
++
++	ccfs0 = chan1;
++	ccfs1 = chan2;
+ 	if (hapd->cs_freq_params.bandwidth == 160) {
+ 		/* Update the CCFS0 and CCFS1 values in the element based on
+ 		 * IEEE P802.11-REVme/D4.0, Table 9-314 */
+@@ -7432,10 +7520,14 @@ u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
+ 		else
+ 			chan1 += 8;
+ 	}
+-	*eid++ = chan1; /* New Channel Center Frequency Segment 0 */
+-	*eid++ = chan2; /* New Channel Center Frequency Segment 1 */
++	*pos++ = chan1; /* New Channel Center Frequency Segment 0 */
++	*pos++ = chan2; /* New Channel Center Frequency Segment 1 */
+ 
+-	return eid;
++#ifdef CONFIG_IEEE80211BE
++	pos = hostapd_eid_bw_indication(hapd, pos, ccfs0, ccfs1);
++#endif /* CONFIG_IEEE80211BE */
++	*length_pos = pos - (eid + 2);
++	return pos;
+ }
+ 
+ 
+diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
+index 2d9adb910..40301bce9 100644
+--- a/src/ap/ieee802_11.h
++++ b/src/ap/ieee802_11.h
+@@ -64,6 +64,8 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts);
+ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
+ u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
+ u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
++u8 * hostapd_eid_bw_indication(struct hostapd_data *hapd, u8 *eid,
++			       u8 ccfs0, u8 ccfs1);
+ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
+ u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
+ 			  enum ieee80211_op_mode opmode);
+diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
+index 8bd6e994d..99106c277 100644
+--- a/src/common/hw_features_common.c
++++ b/src/common/hw_features_common.c
+@@ -481,6 +481,7 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
+ 	data->sec_channel_offset = sec_channel_offset;
+ 	data->center_freq1 = freq + sec_channel_offset * 10;
+ 	data->center_freq2 = 0;
++	data->punct_bitmap = punct_bitmap;
+ 	if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
+ 		data->bandwidth = 80;
+ 	else if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ ||
+diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
+index fb481b8b2..d93fa6660 100644
+--- a/src/common/ieee802_11_defs.h
++++ b/src/common/ieee802_11_defs.h
+@@ -525,6 +525,7 @@
+ #define WLAN_EID_EXT_MULTI_LINK_TRAFFIC_INDICATION 110
+ #define WLAN_EID_EXT_QOS_CHARACTERISTICS 113
+ #define WLAN_EID_EXT_AKM_SUITE_SELECTOR 114
++#define WLAN_EID_EXT_BW_IND 135
+ 
+ /* Extended Capabilities field */
+ #define WLAN_EXT_CAPAB_20_40_COEX 0
+@@ -3088,6 +3089,35 @@ enum dscp_policy_request_type {
+ #define WFA_CAPA_QM_UNSOLIC_DSCP BIT(1)
+ #define WFA_CAPA_QM_NON_EHT_SCS_TRAFFIC_DESC BIT(2)
+ 
++/* IEEE P802.11be/D3.0, 9.4.2.319 - Bandwidth Indication element */
++
++/* Figure 9-1002ba: Bandwidth Indication Parameters field subfields */
++#define BW_IND_PARAMETER_RESERVED                              BIT(0)
++#define BW_IND_PARAMETER_DISABLED_SUBCHAN_BITMAP_PRESENT       BIT(1)
++
++/* Table 9-467: Control subfield: Channel Width subfield; */
++#define BW_IND_CHANNEL_WIDTH_20MHZ                   0
++#define BW_IND_CHANNEL_WIDTH_40MHZ                   1
++#define BW_IND_CHANNEL_WIDTH_80MHZ                   2
++#define BW_IND_CHANNEL_WIDTH_160MHZ                  3
++#define BW_IND_CHANNEL_WIDTH_320MHZ                  4
++
++/* Figure 9-1002c: Bandwidth Indication information
++ * field format similar to EHT Operation Information field format
++ */
++struct ieee80211_bw_ind_info {
++	u8 control; /* B0..B2: Channel Width */
++	u8 ccfs0;
++	u8 ccfs1;
++	le16 disabled_chan_bitmap; /* 0 or 2 octets */
++} STRUCT_PACKED;
++
++/* Figure 9-1002ba—Bandwidth Indication element format */
++struct ieee80211_bw_ind_element {
++	u8 bw_ind_params; /* Bandwidth Indication Parameters */
++	struct ieee80211_bw_ind_info bw_ind_info; /* 3 or 5 octets */
++} STRUCT_PACKED;
++
+ struct ieee80211_neighbor_ap_info {
+ 	u8 tbtt_info_hdr;
+ 	u8 tbtt_info_len;
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 5e65d9e0b..498eff91f 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -876,6 +876,14 @@ struct hostapd_freq_params {
+ 	 */
+ 	bool eht_enabled;
+ 
++	/**
++	 * punct_bitmap - puncturing bitmap
++	 * Each bit corresponds to a 20 MHz subchannel, lowest bit for the
++	 * channel with the lowest frequency. Bit set to 1 indicates that the
++	 * subchannel is punctured, otherwise active.
++	 */
++	u16 punct_bitmap;
++
+ 	/**
+ 	 * link_id: If >=0 indicates the link of the AP MLD to configure
+ 	 */
+@@ -2750,7 +2758,6 @@ struct beacon_data {
+  * @beacon_after: Next beacon/probe resp/asooc resp info
+  * @counter_offset_beacon: Offset to the count field in beacon's tail
+  * @counter_offset_presp: Offset to the count field in probe resp.
+- * @punct_bitmap - Preamble puncturing bitmap
+  * @link_id: Link ID to determine the link for MLD; -1 for non-MLD
+  * @ubpr: Unsolicited broadcast Probe Response frame data
+  */
+@@ -2766,7 +2773,6 @@ struct csa_settings {
+ 	u16 counter_offset_presp[2];
+ 	u16 counter_offset_sta_prof[MAX_NUM_MLD_LINKS][2];
+ 
+-	u16 punct_bitmap;
+ 	int link_id;
+ 
+ 	struct unsol_bcast_probe_resp ubpr;
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 5d97317f7..639c5e7e1 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -11467,7 +11467,7 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ 		   settings->freq_params.bandwidth,
+ 		   settings->freq_params.center_freq1,
+ 		   settings->freq_params.center_freq2,
+-		   settings->punct_bitmap,
++		   settings->freq_params.punct_bitmap,
+ 		   settings->freq_params.link_id,
+ 		   settings->freq_params.ht_enabled ? " ht" : "",
+ 		   settings->freq_params.vht_enabled ? " vht" : "",
+@@ -11545,9 +11545,9 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+ 	    (ret = nl80211_put_freq_params(msg, &settings->freq_params)) ||
+ 	    (settings->block_tx &&
+ 	     nla_put_flag(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX)) ||
+-	    (settings->punct_bitmap &&
++	    (settings->freq_params.punct_bitmap &&
+ 	     nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP,
+-			 settings->punct_bitmap)) ||
++			 settings->freq_params.punct_bitmap)) ||
+ 	    (settings->freq_params.link_id != NL80211_DRV_LINK_ID_NA &&
+ 	     nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, settings->freq_params.link_id)))
+ 		goto error;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0115-mtk-hostapd-fix-Multiple-MLDs-to-use-the-conf-s-own_.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0115-mtk-hostapd-fix-Multiple-MLDs-to-use-the-conf-s-own_.patch
new file mode 100644
index 0000000..daba900
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0115-mtk-hostapd-fix-Multiple-MLDs-to-use-the-conf-s-own_.patch
@@ -0,0 +1,39 @@
+From 377b708eb38c5a7056eaca0cd54994a226f940b1 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Wed, 10 Jul 2024 14:49:43 +0800
+Subject: [PATCH 115/126] mtk: hostapd: fix Multiple MLDs to use the conf's
+ own_addr/mld_addr.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@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 73378053b..a89628fe7 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -1506,6 +1506,9 @@ int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
+ 					   hapd->mld_link_id, hapd->conf->iface);
+ 				goto setup_mld;
+ 			}
++
++			if (addr && !is_zero_ether_addr(hapd->conf->mld_addr))
++				os_memcpy(addr, hapd->conf->mld_addr, ETH_ALEN);
+ 		}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+@@ -1531,6 +1534,10 @@ int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
+ 				   hapd->mld_link_id, hapd->conf->iface);
+ 			os_memcpy(hapd->mld->mld_addr, hapd->own_addr,
+ 				  ETH_ALEN);
++
++			if (!is_zero_ether_addr(conf->bssid))
++				os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN);
++
+ 			hostapd_mld_add_link(hapd);
+ 		}
+ #endif /* CONFIG_IEEE80211BE */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0116-mtk-hostapd-distribute-the-mgmt-rx-frame-to-bss-with.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0116-mtk-hostapd-distribute-the-mgmt-rx-frame-to-bss-with.patch
new file mode 100644
index 0000000..0d3a4f1
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0116-mtk-hostapd-distribute-the-mgmt-rx-frame-to-bss-with.patch
@@ -0,0 +1,31 @@
+From d66477d21526bd4aa8ba9cdc3f8223df15e64386 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Wed, 10 Jul 2024 15:00:50 +0800
+Subject: [PATCH 116/126] mtk: hostapd: distribute the mgmt rx frame to bss
+ with same freq.
+
+there is no need to distribute the mgmt rx frame to all bss, we use
+mgmt->freq to filter.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ src/ap/drv_callbacks.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 96b8e856e..7ae26ab7c 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -1904,6 +1904,9 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
+ 			return 0;
+ 	}
+ 
++	if (rx_mgmt->freq != 0 && rx_mgmt->freq != iface->freq)
++		return 0;
++
+ 	os_memset(&fi, 0, sizeof(fi));
+ 	fi.freq = rx_mgmt->freq;
+ 	fi.datarate = rx_mgmt->datarate;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0117-mtk-hostapd-handle-5G-link-setup-after-DFS-bootup-CA.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0117-mtk-hostapd-handle-5G-link-setup-after-DFS-bootup-CA.patch
new file mode 100644
index 0000000..edfd4ca
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0117-mtk-hostapd-handle-5G-link-setup-after-DFS-bootup-CA.patch
@@ -0,0 +1,189 @@
+From 4bae58d6ded6d2b08975054e1c4097dcedb6b333 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 28 Jun 2024 14:06:20 +0800
+Subject: [PATCH 117/126] mtk: hostapd: handle 5G link setup after DFS bootup
+ CAC as link reconfig
+
+Consider the setup of 5G link after bootup CAC as a link adding
+process via link reconfiguration.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c            | 38 +++++++++++++++++++++++++++++++++++---
+ src/ap/hostapd.c        |  2 ++
+ src/ap/hostapd.h        |  1 +
+ src/ap/ieee802_11.c     |  7 +++++--
+ src/ap/ieee802_11_eht.c | 13 +++++++++++--
+ 5 files changed, 54 insertions(+), 7 deletions(-)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 5a3112d40..31e37a2ab 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -1034,6 +1034,22 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
+ 			return -1;
+ 	}
+ 
++	/* Remove the CAC link from the active links of AP MLD temporarily to avoid
++	 * it being reported in the RNR of the affiliated APs of the same AP MLD
++	 */
++	if (iface->cac_started) {
++		int i;
++
++		for (i = 0; i < iface->num_bss; i++) {
++			struct hostapd_data *hapd = iface->bss[i];
++
++			if (!hapd->conf->mld_ap || !hapd->mld)
++				continue;
++
++			hapd->mld->active_links &= ~BIT(hapd->mld_link_id);
++		}
++	}
++
+ 	return 0;
+ }
+ 
+@@ -1310,6 +1326,8 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ 			     int ht_enabled, int chan_offset, int chan_width,
+ 			     int cf1, int cf2)
+ {
++	int i;
++
+ 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
+ 		"success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d radar_detected=%d",
+ 		success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2,
+@@ -1364,10 +1382,26 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ 			 * sure the configured channel is available because this
+ 			 * CAC completion event could have been propagated from
+ 			 * another radio.
++			 * For a AP MLD, the setup of a DFS link after bootup CAC is
++			 * considered as link adding process via link reconfiguration.
+ 			 */
+ 			if (iface->state != HAPD_IFACE_ENABLED &&
+-			    hostapd_is_dfs_chan_available(iface))
++			    hostapd_is_dfs_chan_available(iface)) {
++				for (i = 0; i < iface->num_bss; i++) {
++					struct hostapd_data *h, *hapd = iface->bss[i];
++
++					if (!hapd->conf->mld_ap || !hapd->mld)
++						continue;
++
++					hapd->mld->active_links |= BIT(hapd->mld_link_id);
++					for_each_mld_link(h, hapd)
++						h->mld->link_reconf_in_progress |=
++								BIT(h->mld_link_id);
++					hapd->mld->link_reconf_in_progress &=
++								~BIT(hapd->mld_link_id);
++				}
+ 				hostapd_setup_interface_complete(iface, 0);
++			}
+ 
+ 			iface->cac_started = 0;
+ 
+@@ -1386,8 +1420,6 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ 		iface->radar_background.expand_ch = 0;
+ 		hostapd_dfs_update_background_chain(iface);
+ 	} else if (iface->state == HAPD_IFACE_ENABLED) {
+-		int i;
+-
+ 		iface->cac_started = 0;
+ 		/* Clear all CSA flags once channel switch to DFS channel fails */
+ 		for (i = 0; i < iface->num_bss; i++)
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index a89628fe7..7518a8b53 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -5109,6 +5109,7 @@ int hostapd_mld_add_link(struct hostapd_data *hapd)
+ 
+ 	dl_list_add_tail(&mld->links, &hapd->link);
+ 	mld->num_links++;
++	mld->active_links |= BIT(hapd->mld_link_id);
+ 
+ 	wpa_printf(MSG_DEBUG, "AP MLD %s: Link ID %d added. num_links: %d",
+ 		   mld->name, hapd->mld_link_id, mld->num_links);
+@@ -5137,6 +5138,7 @@ int hostapd_mld_remove_link(struct hostapd_data *hapd)
+ 
+ 	dl_list_del(&hapd->link);
+ 	mld->num_links--;
++	mld->active_links &= ~BIT(hapd->mld_link_id);
+ 
+ 	wpa_printf(MSG_DEBUG, "AP MLD %s: Link ID %d removed. num_links: %d",
+ 		   mld->name, hapd->mld_link_id, mld->num_links);
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 52875cca1..668f1c44d 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -550,6 +550,7 @@ struct hostapd_mld {
+ 	u8 refcount;
+ 	bool started;
+ 	u16 link_reconf_in_progress;
++	u16 active_links;
+ 
+ 	struct hostapd_data *fbss;
+ 	struct dl_list links; /* List head of all affiliated links */
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 93bd8255c..2861c09d7 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -989,6 +989,8 @@ void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
+ 	sta->sae->peer_commit_scalar = NULL;
+ 	if (hostapd_is_mld_ap(hapd)) {
+ 		for_each_mld_link(link, hapd) {
++			if (!(hapd->mld->active_links & BIT(link->mld_link_id)))
++				continue;
+ 			wpa_auth_pmksa_add_sae(link->wpa_auth, sta->addr,
+ 					sta->sae->pmk, sta->sae->pmk_len,
+ 					sta->sae->pmkid, sta->sae->akmp);
+@@ -8108,7 +8110,7 @@ u8 * hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
+ 	struct hostapd_iface *iface;
+ 	size_t i;
+ 
+-	if (!hapd->iface || !hapd->iface->interfaces || !hapd->conf->mld_ap)
++	if (!hapd->iface || !hapd->iface->interfaces || !hapd->conf->mld_ap || !hapd->mld)
+ 		return eid;
+ 
+ 	/* TODO: Allow for FILS/Action as well */
+@@ -8119,7 +8121,8 @@ u8 * hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
+ 		iface = hapd->iface->interfaces->iface[i];
+ 
+ 		if (!iface || iface == hapd->iface ||
+-		    hapd->iface->freq == iface->freq)
++		    hapd->iface->freq == iface->freq ||
++		    !(hapd->mld->active_links & BIT(hapd->mld_link_id)))
+ 			continue;
+ 
+ 		eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index 63713bc39..cad0d8437 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -514,7 +514,13 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 	u8 link_id;
+ 	u8 common_info_len;
+ 	u16 mld_cap;
+-	u8 max_simul_links, active_links;
++	u8 max_simul_links, active_links = 0;
++
++	if (hapd->mld && !(hapd->mld->active_links & BIT(hapd->mld_link_id))) {
++		wpa_printf(MSG_ERROR, "MLD: Current link %d is not active for %s",
++			   hapd->mld_link_id, hapd->mld->name);
++		return pos;
++	}
+ 
+ 	/*
+ 	 * As the Multi-Link element can exceed the size of 255 bytes need to
+@@ -573,7 +579,10 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 
+ 	mld_cap = hapd->iface->mld_mld_capa;
+ 	max_simul_links = mld_cap & EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
+-	active_links = hapd->mld->num_links - 1;
++	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++)
++		if (hapd->mld_link_id != link_id &&
++		    (hapd->mld->active_links & BIT(link_id)))
++			active_links++;
+ 
+ 	if (active_links > max_simul_links) {
+ 		wpa_printf(MSG_ERROR,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0118-mtk-hostapd-support-MLD-AP-affiliated-link-removal.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0118-mtk-hostapd-support-MLD-AP-affiliated-link-removal.patch
new file mode 100644
index 0000000..e459fed
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0118-mtk-hostapd-support-MLD-AP-affiliated-link-removal.patch
@@ -0,0 +1,434 @@
+From 2f6ed87f722be77d39188f84158b27945c6f41ef Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 29 Apr 2024 12:04:52 +0800
+Subject: [PATCH 118/126] mtk: hostapd: support MLD AP affiliated link removal
+
+Support ap link removal of MLD reconfiguration. Main changes include:
+- support to trigger it from hostapd_cli, with the following command:
+  hostapd_cli -i ap-mld-1 -l <link_id> link_remove count=10
+
+- handle NL80211_CMD_LINKS_REMOVED event
+- modify some parts that will teardown whole VIF or STA when removing
+  a link
+- handle case that the setup link sta is removed
+
+Note that currently the code only supports to remove one link at one time.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ hostapd/ctrl_iface.c               |  26 ++++++-
+ hostapd/hostapd_cli.c              |   8 +++
+ src/ap/ap_drv_ops.c                |   7 ++
+ src/ap/beacon.c                    |   4 ++
+ src/ap/drv_callbacks.c             |   3 +
+ src/ap/hostapd.c                   | 110 +++++++++++++++++++++++------
+ src/ap/hostapd.h                   |   2 +
+ src/ap/wpa_auth.c                  |   6 ++
+ src/ap/wpa_auth.h                  |   3 +
+ src/drivers/driver.h               |   7 ++
+ src/drivers/driver_nl80211.c       |   5 ++
+ src/drivers/driver_nl80211_event.c |  34 ++++++++-
+ 12 files changed, 188 insertions(+), 27 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index c8456513e..6db9fa617 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -3903,11 +3903,31 @@ static int hostapd_ctrl_iface_disable_mld(struct hostapd_iface *iface)
+ static int hostapd_ctrl_iface_link_remove(struct hostapd_data *hapd, char *cmd,
+ 					  char *buf, size_t buflen)
+ {
++	char *token, *context = NULL;
++	u32 count = 0;
+ 	int ret;
+-	u32 count = atoi(cmd);
+ 
+-	if (!count)
+-		count = 1;
++	while ((token = str_token(cmd, " ", &context))) {
++		if (os_strncmp(token, "count=", 6) == 0) {
++			count = atoi(token + 6);
++			continue;
++		}
++
++		wpa_printf(MSG_ERROR, "CTRL: Invalid LINK_REMOVE parameter: %s",
++			   token);
++		return -1;
++	}
++
++	if (!count) {
++		wpa_printf(MSG_ERROR, "Invalid ap removal count");
++		return -1;
++	}
++
++	/* limit total countdown time to be multiple of second */
++	if ((hapd->iconf->beacon_int * count) % 1000) {
++		wpa_printf(MSG_ERROR, "Total countdown time should be multiple of second");
++		return -1;
++	}
+ 
+ 	ret = hostapd_link_remove(hapd, count);
+ 	if (ret == 0) {
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 12ee0a18f..9dcc0d74b 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1785,6 +1785,12 @@ static int hostapd_cli_cmd_link_add(struct wpa_ctrl *ctrl, int argc,
+ 	return hostapd_cli_cmd(ctrl, "LINK_ADD", 1, argc, argv);
+ }
+ 
++static int hostapd_cli_cmd_link_remove(struct wpa_ctrl *ctrl, int argc,
++				       char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "LINK_REMOVE", 1, argc, argv);
++}
++
+ struct hostapd_cli_cmd {
+ 	const char *cmd;
+ 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -2047,6 +2053,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 		" = <ac> [cwmin=] [cwmax=] [aifs=] [txop_limit=]"},
+ 	{ "link_add", hostapd_cli_cmd_link_add, NULL,
+ 		" = Add a new link to a MLD AP"},
++	{ "link_remove", hostapd_cli_cmd_link_remove, NULL,
++		" [count=<count>] = Remove affiliated link of a MLD AP"},
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index f9ec9a689..8b6aed0c7 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -332,6 +332,13 @@ int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta)
+ 		return 0;
+ 	}
+ 
++	if (hapd->conf->mld_ap && hapd->mld->removed_links) {
++		wpa_printf(MSG_DEBUG,
++			   "%s: Do not update station flags (" MACSTR ")",
++			   " during ap link removal", __func__, MAC2STR(sta->addr));
++		return 0;
++	}
++
+ 	flags_or = total_flags & set_flags;
+ 	flags_and = total_flags | ~set_flags;
+ 	return hostapd_sta_set_flags(hapd, sta->addr, total_flags,
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 8a9569549..5293ee4c1 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -2794,6 +2794,10 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
+ 	if (hapd->conf->mld_ap && !hapd->mld->started && hapd->beacon_set_done)
+ 		return 0;
+ 
++	/* skip setting beacon during ap link removal */
++	if (hapd->conf->mld_ap && hapd->mld->removed_links)
++		return 0;
++
+ 	if (!hapd->drv_priv) {
+ 		wpa_printf(MSG_ERROR, "Interface is disabled");
+ 		return -1;
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 7ae26ab7c..9deb87c3d 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2919,6 +2919,9 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ 			   hapd->conf->iface);
+ 		hostapd_event_color_change(hapd, true);
+ 		break;
++	case EVENT_LINK_RECONFIG:
++		hostapd_link_remove_cb(hapd, data->reconfig_info.removed_links);
++		break;
+ #endif /* CONFIG_IEEE80211AX */
+ 	default:
+ 		wpa_printf(MSG_DEBUG, "Unknown event %d", event);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 7518a8b53..e34bc1fa8 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -428,30 +428,74 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
+ 
+ #define TU_TO_USEC(_val) ((_val) * 1024)
+ 
+-static void hostapd_link_remove_timeout_handler(void *eloop_data,
+-						void *user_ctx)
++static void hostapd_switch_sta_setup_link(struct hostapd_data *hapd)
+ {
+-	struct hostapd_data *hapd = (struct hostapd_data *) eloop_data;
++	struct hostapd_data *p_hapd, *new_setup_hapd = NULL;
++	struct sta_info *cur, *sta;
++
++	/* use the current least link_id as new setup link */
++	for_each_mld_link(p_hapd, hapd) {
++		if (p_hapd == hapd)
++			continue;
++		if (!new_setup_hapd || p_hapd->mld_link_id < new_setup_hapd->mld_link_id)
++			new_setup_hapd = p_hapd;
++	}
+ 
+-	if (hapd->eht_mld_link_removal_count == 0)
++	if (!new_setup_hapd)
+ 		return;
+-	hapd->eht_mld_link_removal_count--;
+ 
+-	wpa_printf(MSG_DEBUG, "MLD: Remove link_id=%u in %u beacons",
+-		   hapd->mld_link_id,
+-		   hapd->eht_mld_link_removal_count);
++	sta = hapd->sta_list;
++	while (sta) {
++		struct sta_info *new_setup_sta;
+ 
+-	ieee802_11_set_beacon(hapd);
++		cur = sta;
++		sta = sta->next;
++		if (hostapd_sta_is_link_sta(hapd, cur))
++			continue;
+ 
+-	if (!hapd->eht_mld_link_removal_count) {
+-		hostapd_free_link_stas(hapd);
+-		hostapd_disable_iface(hapd->iface);
+-		return;
++		new_setup_sta = ap_get_sta(new_setup_hapd, cur->addr);
++		if (!new_setup_sta)
++			continue;
++
++		new_setup_sta->mld_assoc_link_id = new_setup_hapd->mld_link_id;
++		new_setup_sta->mld_assoc_sta = new_setup_sta;
++		switch_setup_wpa_auth(new_setup_sta->wpa_sm,
++				      new_setup_hapd->wpa_auth);
++		new_setup_sta->flags = cur->flags;
++		cur->flags &= ~(WLAN_STA_AUTH | WLAN_STA_AUTHORIZED);
++
++		for_each_mld_link(p_hapd, hapd) {
++			struct sta_info *p_sta;
++
++			p_sta = ap_get_sta(p_hapd, new_setup_sta->addr);
++			if (!p_sta)
++				continue;
++
++			p_sta->mld_assoc_link_id = new_setup_hapd->mld_link_id;
++			p_sta->mld_assoc_sta = new_setup_sta;
++		}
++
++		wpa_printf(MSG_INFO, "Switch assoc link of station " MACSTR " from %u to %u",
++			   MAC2STR(cur->addr), hapd->mld_link_id, new_setup_hapd->mld_link_id);
+ 	}
++}
+ 
+-	eloop_register_timeout(0, TU_TO_USEC(hapd->iconf->beacon_int),
+-			       hostapd_link_remove_timeout_handler,
+-			       hapd, NULL);
++static void hostapd_link_remove_timeout_handler(void *eloop_data,
++						void *user_ctx)
++{
++	struct hostapd_data *hapd = (struct hostapd_data *) eloop_data;
++	struct hostapd_mld *mld = hapd->mld;
++	u8 link_id = hapd->mld_link_id;
++
++	if (!mld || !mld->removed_links)
++		return;
++
++	wpa_printf(MSG_DEBUG, "MLD: Remove link_id=%u", hapd->mld_link_id);
++
++	hostapd_switch_sta_setup_link(hapd);
++	hostapd_free_link_stas(hapd);
++	hostapd_disable_iface(hapd->iface);
++	mld->removed_links &= ~BIT(link_id);
+ }
+ 
+ 
+@@ -465,16 +509,34 @@ int hostapd_link_remove(struct hostapd_data *hapd, u32 count)
+ 		   hapd->mld_link_id, count);
+ 
+ 	hapd->eht_mld_link_removal_count = count;
+-	hapd->eht_mld_bss_param_change++;
+-
+-	eloop_register_timeout(0, TU_TO_USEC(hapd->iconf->beacon_int),
+-			       hostapd_link_remove_timeout_handler,
+-			       hapd, NULL);
++	ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_RECONFIG);
+ 
+ 	ieee802_11_set_beacon(hapd);
++
++	hapd->eht_mld_link_removal_count = 0;
++	hapd->mld->removed_links |= BIT(hapd->mld_link_id);
+ 	return 0;
+ }
+ 
++
++void hostapd_link_remove_cb(struct hostapd_data *hapd, u16 removed_links)
++{
++	struct hostapd_data *link;
++
++	if (!hapd->conf->mld_ap)
++		return;
++
++	for_each_mld_link(link, hapd) {
++		if (!(BIT(link->mld_link_id) & removed_links))
++			continue;
++
++		link->eht_mld_bss_critical_update = 0;
++		eloop_register_timeout(0, 0,
++				       hostapd_link_remove_timeout_handler,
++				       link, NULL);
++	}
++}
++
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
+ 
+@@ -848,7 +910,9 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
+ void hostapd_bss_deinit_no_free(struct hostapd_data *hapd)
+ {
+ 	hostapd_free_stas(hapd);
+-	hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
++	/* do not flush stations during ap link removal */
++	if (!hapd->conf->mld_ap || !hapd->mld->removed_links)
++		hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
+ #ifdef CONFIG_WEP
+ 	hostapd_clear_wep(hapd);
+ #endif /* CONFIG_WEP */
+@@ -1334,7 +1398,7 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
+ 	}
+ 
+ 	if (flush_old_stations && !conf->start_disabled &&
+-	    conf->broadcast_deauth) {
++	    conf->broadcast_deauth && (hapd->conf->mld_ap && !hapd->mld->started)) {
+ 		u8 addr[ETH_ALEN];
+ 
+ 		/* Should any previously associated STA not have noticed that
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index 668f1c44d..c6c286304 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -551,6 +551,7 @@ struct hostapd_mld {
+ 	bool started;
+ 	u16 link_reconf_in_progress;
+ 	u16 active_links;
++	u16 removed_links;
+ 
+ 	struct hostapd_data *fbss;
+ 	struct dl_list links; /* List head of all affiliated links */
+@@ -922,6 +923,7 @@ int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd);
+ struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
+ 					       u8 link_id);
+ int hostapd_link_remove(struct hostapd_data *hapd, u32 count);
++void hostapd_link_remove_cb(struct hostapd_data *hapd, u16 removed_links);
+ bool hostapd_is_ml_partner(struct hostapd_data *hapd1,
+ 			   struct hostapd_data *hapd2);
+ u8 hostapd_get_mld_id(struct hostapd_data *hapd);
+diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
+index 6cb5a4be7..8fb52b697 100644
+--- a/src/ap/wpa_auth.c
++++ b/src/ap/wpa_auth.c
+@@ -7428,3 +7428,9 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm,
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ }
++
++void switch_setup_wpa_auth(struct wpa_state_machine *sm,
++			   struct wpa_authenticator *wpa_auth)
++{
++	sm->wpa_auth = wpa_auth;
++}
+diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
+index b22c4199b..4cd46849a 100644
+--- a/src/ap/wpa_auth.h
++++ b/src/ap/wpa_auth.h
+@@ -681,4 +681,7 @@ void wpa_release_link_auth_ref(struct wpa_state_machine *sm,
+ 		    sm->mld_links[link_id].wpa_auth &&			\
+ 		    sm->wpa_auth != sm->mld_links[link_id].wpa_auth)
+ 
++void switch_setup_wpa_auth(struct wpa_state_machine *sm,
++			   struct wpa_authenticator *wpa_auth);
++
+ #endif /* WPA_AUTH_H */
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 498eff91f..eb2a48381 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -7062,6 +7062,13 @@ union wpa_event_data {
+ 		u8 valid_links;
+ 		struct t2lm_mapping t2lmap[MAX_NUM_MLD_LINKS];
+ 	} t2l_map_info;
++
++	/**
++	 * struct reconfig_info - Data for EVENT_LINK_RECONFIG
++	 */
++	struct reconfig_info {
++		u16 removed_links;
++	} reconfig_info;
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 639c5e7e1..1d334b75f 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -1117,6 +1117,11 @@ static void nl80211_put_wiphy_data_ap(struct i802_bss *bss)
+ 
+ 	if (w == NULL)
+ 		return;
++
++	/* do not clear wiphy data if there are still more than one links */
++	if (bss->valid_links && (bss->valid_links & (bss->valid_links - 1)))
++		return;
++
+ 	bss->wiphy_data = NULL;
+ 	dl_list_del(&bss->wiphy_list);
+ 
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index efdb8ef7f..24a4bf3cf 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -3930,6 +3930,38 @@ static void nl80211_obss_color_event(struct i802_bss *bss,
+ 
+ #endif /* CONFIG_IEEE80211AX */
+ 
++static void nl80211_links_removed(struct wpa_driver_nl80211_data *drv,
++				  struct nlattr **tb)
++{
++	struct nlattr *link_attr, *link_data[NL80211_ATTR_MAX + 1];
++	static struct nla_policy link_policy[NL80211_ATTR_MAX + 1] = {
++		[NL80211_ATTR_MLO_LINK_ID] = { .type = NLA_U8 },
++	};
++	union wpa_event_data data = {};
++	int rem;
++
++	if (!tb[NL80211_ATTR_MLO_LINKS])
++		return;
++
++	nla_for_each_nested(link_attr, tb[NL80211_ATTR_MLO_LINKS], rem) {
++		u8 link_id;
++
++		if (nla_parse_nested(link_data, NL80211_ATTR_MAX,
++				     link_attr, link_policy) != 0)
++			continue;
++
++		if (!link_data[NL80211_ATTR_MLO_LINK_ID])
++			continue;
++
++		link_id = nla_get_u8(link_data[NL80211_ATTR_MLO_LINK_ID]);
++		if (link_id >= MAX_NUM_MLD_LINKS)
++			continue;
++		data.reconfig_info.removed_links |= BIT(link_id);
++	}
++
++	wpa_supplicant_event(drv->ctx, EVENT_LINK_RECONFIG, &data);
++}
++
+ 
+ static void do_process_drv_event(struct i802_bss *bss, int cmd,
+ 				 struct nlattr **tb)
+@@ -4195,7 +4227,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
+ 		break;
+ #endif /* CONFIG_IEEE80211AX */
+ 	case NL80211_CMD_LINKS_REMOVED:
+-		wpa_supplicant_event(drv->ctx, EVENT_LINK_RECONFIG, NULL);
++		nl80211_links_removed(drv, tb);
+ 		break;
+ 	default:
+ 		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0119-mtk-hostapd-add-A-TTLM-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0119-mtk-hostapd-add-A-TTLM-support.patch
new file mode 100644
index 0000000..a6bdd09
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0119-mtk-hostapd-add-A-TTLM-support.patch
@@ -0,0 +1,760 @@
+From 8aabcfee2bb77e19fc592c27f1121c5725ee665f Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Wed, 3 Jul 2024 15:39:53 +0800
+Subject: [PATCH 119/126] mtk: hostapd: add A-TTLM support
+
+Add a data structure for AP MLD's Advertised Tid-to-Link Mapping (A-TTLM).
+Since it is MLD-level, this commit also adds it into struct hostapd_mld.
+
+There should be 'curr_attlm' and 'new_attlm', which means 2 TTLMs can be
+advertised at the same time ('curr_attlm' is used currently while
+'new_attlm' is about to be used)
+However, since the FW only support 1 attlm now, there is currently only
+'new_attlm'.
+
+There are 3 A-TTLM events from the driver, and hostap should handle them
+accordingly
+1. A-TTLM started: link(s) sould start to advertise TTLM in the beacon.
+2. A-TTLM switch time expired: enabled link(s) keep advertising TTLM in
+   the beacon with switch time excluded, whiel disabled link(s) should
+   stop TX/RX, including beacon
+3. A-TTLM ended: all links stop advertising TTLM in the beacon, and
+   disabled link recover to TX/RX.
+
+This commit adds the support to set ATTLM, from hostapd_cli to driver.
+Setting an ATTLM requires 3 parameters
+1. disabled_links: disabled link ID bitmap
+2. switch_time: how much time it takes to start the A-TTLM (in ms)
+3. duration: how long the A-TTLM lasts (in ms)
+
+Below is a hostapd_cli example that requires an A-TTLM starts to disable
+link_id=1 after 5 seconds and last for 20 seconds:
+$ hostapd_cli -i ap-mld-1 set_attlm disabled_links=2 switch_time=5000
+duration=20000
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ hostapd/ctrl_iface.c               | 66 +++++++++++++++++++++++++++++
+ hostapd/hostapd_cli.c              |  8 ++++
+ src/ap/ap_drv_ops.c                |  9 ++++
+ src/ap/ap_drv_ops.h                |  1 +
+ src/ap/beacon.c                    |  4 ++
+ src/ap/drv_callbacks.c             | 53 +++++++++++++++++++++++
+ src/ap/hostapd.c                   |  8 ++++
+ src/ap/hostapd.h                   |  4 ++
+ src/ap/ieee802_11.c                |  8 ++++
+ src/ap/ieee802_11.h                |  1 +
+ src/ap/ieee802_11_eht.c            | 68 +++++++++++++++++++++++++++++-
+ src/common/ieee802_11_defs.h       | 13 ++++++
+ src/drivers/driver.h               | 56 ++++++++++++++++++++++++
+ src/drivers/driver_nl80211.c       | 33 +++++++++++++++
+ src/drivers/driver_nl80211_event.c | 54 ++++++++++++++++++++++++
+ src/drivers/nl80211_copy.h         | 24 +++++++++++
+ 16 files changed, 409 insertions(+), 1 deletion(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index 6db9fa617..2cd5487ca 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4039,6 +4039,68 @@ out:
+ 	return ret;
+ }
+ 
++static int hostapd_ctrl_iface_set_attlm(struct hostapd_data *hapd, char *cmd,
++					char *buf, size_t buflen)
++{
++#define MAX_SWITCH_TIME_MS 30000
++#define MAX_DURATION_MS 16000000
++	struct attlm_settings *attlm;
++	struct hostapd_data *h;
++	char *token, *context = NULL;
++	u16 switch_time, disabled_links, valid_links = 0;
++	u32 duration;
++	int ret, i;
++
++	if (!hapd->conf->mld_ap || !hapd->mld)
++		return -1;
++
++	attlm = &hapd->mld->new_attlm;
++	if (attlm->valid) {
++		wpa_printf(MSG_ERROR, "Busy: A-TTLM is on-going");
++		return -1;
++	}
++
++	for_each_mld_link(h, hapd)
++		valid_links |= BIT(h->mld_link_id);
++
++	while ((token = str_token(cmd, " ", &context))) {
++		if (os_strncmp(token, "switch_time=", 12) == 0) {
++			switch_time = atoi(token + 12);
++			if (switch_time > 0 && switch_time <= MAX_SWITCH_TIME_MS)
++				continue;
++		}
++
++		if (os_strncmp(token, "disabled_links=", 15) == 0) {
++			disabled_links = atoi(token + 15);
++
++			if ((disabled_links & valid_links) &&
++			    !(disabled_links & ~valid_links))
++				continue;
++		}
++
++		if (os_strncmp(token, "duration=", 9) == 0) {
++			duration = atoi(token + 9);
++			if (duration > 0 && duration <= MAX_DURATION_MS)
++				continue;
++		}
++
++		wpa_printf(MSG_INFO, "CTRL: Invalid SET_ATTLM parameter: %s",
++			   token);
++		return -1;
++	}
++
++	wpa_printf(MSG_DEBUG,
++		   "MLD: set A-TTLM disabled_links=%u, switch_time=%u, duration=%u",
++		   disabled_links, switch_time, duration);
++
++	attlm->valid = true;
++	attlm->direction = IEEE80211_TTLM_DIRECTION_BOTH;
++	attlm->duration = duration;
++	attlm->switch_time = switch_time;
++	attlm->disabled_links = hapd->conf->mld_allowed_links & disabled_links;
++
++	return hostapd_mld_set_attlm(hapd);
++}
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
+ 
+@@ -6166,6 +6228,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		if (hostapd_ctrl_iface_link_add(hapd, buf + 9,
+ 						reply, reply_size))
+ 			reply_len = -1;
++	} else if (os_strncmp(buf, "SET_ATTLM ", 10) == 0) {
++		if (hostapd_ctrl_iface_set_attlm(hapd, buf + 10, reply,
++						 reply_size))
++			reply_len = -1;
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
+ 	} else if (os_strncmp(buf, "SET_EDCCA ", 10) == 0) {
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 9dcc0d74b..f81211ba4 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1791,6 +1791,12 @@ static int hostapd_cli_cmd_link_remove(struct wpa_ctrl *ctrl, int argc,
+ 	return hostapd_cli_cmd(ctrl, "LINK_REMOVE", 1, argc, argv);
+ }
+ 
++static int hostapd_cli_cmd_set_attlm(struct wpa_ctrl *ctrl, int argc,
++				     char *argv[])
++{
++	return hostapd_cli_cmd(ctrl, "SET_ATTLM", 1, argc, argv);
++}
++
+ struct hostapd_cli_cmd {
+ 	const char *cmd;
+ 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+@@ -2055,6 +2061,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 		" = Add a new link to a MLD AP"},
+ 	{ "link_remove", hostapd_cli_cmd_link_remove, NULL,
+ 		" [count=<count>] = Remove affiliated link of a MLD AP"},
++	{ "set_attlm", hostapd_cli_cmd_set_attlm, NULL,
++		" = Disable the affiliated AP of a MLD AP" },
+ 	{ NULL, NULL, NULL, NULL }
+ };
+ 
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 8b6aed0c7..a25a67cdd 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -619,6 +619,15 @@ int hostapd_if_link_remove(struct hostapd_data *hapd,
+ 	return hapd->driver->link_remove(hapd->drv_priv, type, ifname,
+ 					 hapd->mld_link_id);
+ }
++
++
++int hostapd_drv_set_attlm(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->drv_priv || !hapd->driver->set_attlm)
++		return -1;
++
++	return hapd->driver->set_attlm(hapd->drv_priv, &hapd->mld->new_attlm);
++}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ 
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index e74284b96..7f108bc1d 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -68,6 +68,7 @@ int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ int hostapd_if_link_remove(struct hostapd_data *hapd,
+ 			   enum wpa_driver_if_type type,
+ 			   const char *ifname, u8 link_id);
++int hostapd_drv_set_attlm(struct hostapd_data *hapd);
+ int hostapd_set_ieee8021x(struct hostapd_data *hapd,
+ 			  struct wpa_bss_params *params);
+ int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
+diff --git a/src/ap/beacon.c b/src/ap/beacon.c
+index 5293ee4c1..a5a885213 100644
+--- a/src/ap/beacon.c
++++ b/src/ap/beacon.c
+@@ -943,6 +943,9 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
+ 
+ 		pos = hostapd_eid_eht_capab(hapd, pos, IEEE80211_MODE_AP);
+ 		pos = hostapd_eid_eht_operation(hapd, pos);
++
++		if (!params->is_ml_sta_info)
++			pos = hostapd_eid_eht_attlm(hapd, pos);
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+@@ -2536,6 +2539,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ 		tailpos = hostapd_eid_eht_capab(hapd, tailpos,
+ 						IEEE80211_MODE_AP);
+ 		tailpos = hostapd_eid_eht_operation(hapd, tailpos);
++		tailpos = hostapd_eid_eht_attlm(hapd, tailpos);
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
+index 9deb87c3d..17dc09807 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -1368,6 +1368,54 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
+ }
+ 
+ 
++#ifdef CONFIG_IEEE80211BE
++void hostapd_event_attlm(struct hostapd_data *hapd, struct attlm_event *attlm_event)
++{
++	struct hostapd_mld *mld = hapd->mld;
++	struct hostapd_data *p_hapd;
++	bool mld_indicate_disabled = false;
++
++	if (!hapd->conf->mld_ap || !mld)
++		return;
++
++	wpa_printf(MSG_DEBUG, "A-TTLM event");
++	/*
++	 * T0: driver notifies A-TTLM has started and reports Switch Time TSF in TUs
++	 * T1: driver notifies Switch Time Expiry of a started A-TTLM
++	 * T2: driver notifies Duration Expiry of a started A-TTLM.
++	 */
++	switch (attlm_event->event) {
++		case EVENT_ATTLM_STARTED:
++			ieee802_11_set_bss_critical_update(hapd,
++						BSS_CRIT_UPDATE_EVENT_ATTLM);
++			mld->new_attlm.switch_time_tsf_tu =
++						attlm_event->switch_time_tsf_tu;
++			break;
++		case EVENT_ATTLM_SWITCH_TIME_EXPIRED:
++			mld_indicate_disabled = true;
++			mld->new_attlm.switch_time_tsf_tu = 0;
++			os_get_reltime(&mld->new_attlm.start_time);
++			break;
++		case EVENT_ATTLM_END:
++			mld->new_attlm.valid = false;
++			break;
++		default:
++			wpa_printf(MSG_DEBUG, "Unsupported A-TTLM event");
++			return;
++	}
++
++	for_each_mld_link(p_hapd, hapd) {
++		if (mld->new_attlm.disabled_links & BIT(p_hapd->mld_link_id))
++			p_hapd->conf->mld_indicate_disabled =
++							mld_indicate_disabled;
++	}
++
++	ieee802_11_set_beacon(hapd);
++	hapd->eht_mld_bss_critical_update = 0;
++}
++#endif /* CONFIG_IEEE80211BE */
++
++
+ void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
+ 					 const u8 *addr, int reason_code)
+ {
+@@ -2755,6 +2803,11 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ 					data->ch_switch.punct_bitmap,
+ 					event == EVENT_CH_SWITCH);
+ 		break;
++	case EVENT_ATTLM:
++#ifdef CONFIG_IEEE80211BE
++		hostapd_event_attlm(hapd, &data->attlm_event);
++#endif /* CONFIG_IEEE80211BE */
++		break;
+ 	case EVENT_CONNECT_FAILED_REASON:
+ 		if (!data)
+ 			break;
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index e34bc1fa8..9388f6070 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -537,6 +537,14 @@ void hostapd_link_remove_cb(struct hostapd_data *hapd, u16 removed_links)
+ 	}
+ }
+ 
++
++int hostapd_mld_set_attlm(struct hostapd_data *hapd)
++{
++	if (!hapd->drv_priv)
++		return -1;
++
++	return hostapd_drv_set_attlm(hapd);
++}
+ #endif /* CONFIG_TESTING_OPTIONS */
+ #endif /* CONFIG_IEEE80211BE */
+ 
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index c6c286304..f69fa0062 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -553,6 +553,7 @@ struct hostapd_mld {
+ 	u16 active_links;
+ 	u16 removed_links;
+ 
++	struct attlm_settings new_attlm;
+ 	struct hostapd_data *fbss;
+ 	struct dl_list links; /* List head of all affiliated links */
+ 
+@@ -924,6 +925,7 @@ struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
+ 					       u8 link_id);
+ int hostapd_link_remove(struct hostapd_data *hapd, u32 count);
+ void hostapd_link_remove_cb(struct hostapd_data *hapd, u16 removed_links);
++int hostapd_mld_set_attlm(struct hostapd_data *hapd);
+ bool hostapd_is_ml_partner(struct hostapd_data *hapd1,
+ 			   struct hostapd_data *hapd2);
+ u8 hostapd_get_mld_id(struct hostapd_data *hapd);
+@@ -937,6 +939,8 @@ int hostapd_fill_cca_settings(struct hostapd_data *hapd,
+ 
+ #ifdef CONFIG_IEEE80211BE
+ 
++void hostapd_event_attlm(struct hostapd_data *hapd, struct attlm_event *attlm_event);
++
+ bool hostapd_mld_is_first_bss(struct hostapd_data *hapd);
+ 
+ #define for_each_mld_link(partner, self) \
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 2861c09d7..886a21a66 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -5088,6 +5088,7 @@ rsnxe_done:
+ 			p = hostapd_eid_eht_ml_assoc(hapd, sta, p);
+ 		p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
+ 		p = hostapd_eid_eht_operation(hapd, p);
++		p = hostapd_eid_eht_attlm(hapd, p);
+ 	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+@@ -6343,6 +6344,13 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+ 		wpa_printf(MSG_DEBUG, "MGMT: Drop the frame - MLD not ready");
+ 		return 1;
+ 	}
++
++	if (hapd->conf->mld_ap && hapd->mld->new_attlm.valid &&
++	    !hapd->mld->new_attlm.switch_time_tsf_tu &&
++	    (hapd->mld->new_attlm.disabled_links & BIT(hapd->mld_link_id))) {
++		wpa_printf(MSG_DEBUG, "MGMT: Drop the frame - Disabled link");
++		return 1;
++	}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ 	if (fi && fi->freq)
+diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
+index 40301bce9..efaa20c86 100644
+--- a/src/ap/ieee802_11.h
++++ b/src/ap/ieee802_11.h
+@@ -243,6 +243,7 @@ u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
+ 			   enum ieee80211_op_mode opmode);
+ u8 * hostapd_eid_non_inheritance(struct hostapd_data *hapd, u8 *eid);
+ u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid);
++u8 * hostapd_eid_eht_attlm(struct hostapd_data *hapd, u8 *eid);
+ u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ 		       enum ieee80211_op_mode opmode,
+ 		       const u8 *he_capab, size_t he_capab_len,
+diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
+index cad0d8437..2ed9414b8 100644
+--- a/src/ap/ieee802_11_eht.c
++++ b/src/ap/ieee802_11_eht.c
+@@ -595,7 +595,7 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ 	mld_cap |= active_links & EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
+ 
+ 	/* TODO: Advertise T2LM based on driver support as well */
+-	mld_cap &= ~EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_NEG_SUPP_MSK;
++	mld_cap |= EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_ALL_TO_ALL;
+ 
+ 	wpa_printf(MSG_DEBUG, "MLD: MLD Capabilities and Operations=0x%x",
+ 		   mld_cap);
+@@ -824,6 +824,72 @@ static u8 * hostapd_eid_eht_reconf_ml(struct hostapd_data *hapd, u8 *eid)
+ }
+ 
+ 
++u8 * hostapd_eid_eht_attlm(struct hostapd_data *hapd, u8 *eid)
++{
++	struct attlm_settings *attlm;
++	struct os_reltime now, res;
++	int i;
++	u16 control = 0;
++	u8 *pos = eid;
++	u16 enabled_links;
++
++	if (!hapd->conf->mld_ap)
++		return eid;
++
++	attlm = &hapd->mld->new_attlm;
++	if (!attlm || !attlm->valid)
++		return eid;
++
++	/* The length will be set at the end */
++	*pos++ = WLAN_EID_EXTENSION;
++	*pos++ = 0;
++	*pos++ = WLAN_EID_EXT_TID_TO_LINK_MAPPING;
++
++	/* Set the A-TTLM Control field */
++	control = (IEEE80211_TTLM_CONTROL_DIRECTION & attlm->direction) |
++		  IEEE80211_TTLM_CONTROL_EXPECTED_DUR_PRESENT |
++		  IEEE80211_TTLM_CONTROL_INDICATOR;
++
++	if (attlm->switch_time_tsf_tu != 0)
++		control |= IEEE80211_TTLM_CONTROL_SWITCH_TIME_PRESENT;
++
++	WPA_PUT_LE16(pos, control);
++	pos += 2;
++
++	/* switch time & expected duration */
++	if (attlm->switch_time_tsf_tu != 0) {
++		WPA_PUT_LE16(pos, attlm->switch_time_tsf_tu);
++		pos += 2;
++
++		WPA_PUT_LE24(pos, (attlm->duration * 1000) >> 10);
++		pos += 3;
++	} else {
++		u32 diff;
++
++		os_get_reltime(&now);
++		os_reltime_sub(&now, &attlm->start_time, &res);
++		diff = (u32)os_reltime_in_ms(&res);
++
++		if (attlm->duration <= diff)
++			return eid;
++
++		WPA_PUT_LE24(pos, ((attlm->duration - diff) * 1000) >> 10);
++		pos += 3;
++	}
++
++	/* Link Mapping of each TID (0 - 7) */
++	enabled_links = hapd->conf->mld_allowed_links & ~attlm->disabled_links;
++	for (i = 0; i < 8; i++) {
++		WPA_PUT_LE16(pos, enabled_links);
++		pos += 2;
++	}
++
++	eid[1] = pos - eid - 2;
++
++	return pos;
++}
++
++
+ static size_t hostapd_eid_eht_ml_len(struct mld_info *info,
+ 				     bool include_mld_id,
+ 				     u8 eml_disable)
+diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
+index d93fa6660..afcc2f861 100644
+--- a/src/common/ieee802_11_defs.h
++++ b/src/common/ieee802_11_defs.h
+@@ -2912,6 +2912,19 @@ enum ieee80211_eht_ml_sub_elem {
+ 	EHT_ML_SUB_ELEM_FRAGMENT = 254,
+ };
+ 
++/* IEEE P802.11be/D5.0, 9.4.2.314 - TID-to-Link Mapping control */
++#define IEEE80211_TTLM_CONTROL_DIRECTION		0x0003
++#define IEEE80211_TTLM_CONTROL_DEF_LINK_MAP		0x0004
++#define IEEE80211_TTLM_CONTROL_SWITCH_TIME_PRESENT	0x0008
++#define IEEE80211_TTLM_CONTROL_EXPECTED_DUR_PRESENT	0x0010
++#define IEEE80211_TTLM_CONTROL_LINK_MAP_SIZE		0x0020
++#define IEEE80211_TTLM_CONTROL_INDICATOR		0xff00
++
++/* TTLM direction */
++#define IEEE80211_TTLM_DIRECTION_DOWN		0
++#define IEEE80211_TTLM_DIRECTION_UP		1
++#define IEEE80211_TTLM_DIRECTION_BOTH		2
++
+ /* IEEE P802.11ay/D4.0, 9.4.2.251 - EDMG Operation element */
+ #define EDMG_BSS_OPERATING_CHANNELS_OFFSET	6
+ #define EDMG_OPERATING_CHANNEL_WIDTH_OFFSET	7
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index eb2a48381..fe327e560 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -2805,6 +2805,29 @@ struct cca_settings {
+ 	int link_id;
+ };
+ 
++#ifdef CONFIG_IEEE80211BE
++/**
++ * struct attlm_settings - Setting for Advertised Tid-to-Link Mapping
++ * @valid: whether this A-TTLM is still valid
++ * @direction: direction of this A-TTLM
++ * @disabled_links: disabled link ID bitmap
++ * @switch_time: duration in ms to establish the A-TTLM
++ * @switch_time_tsf_tu: time in TUs that the A-TTLM is established. It should be
++ * the bits 10 to 25 of the TSF
++ * @duration_tu: duration in ms that the A-TTLM lasts
++ * @start_time: the relative time that this A-TTLM is entablished
++ */
++struct attlm_settings {
++	bool valid;
++	u8 direction;
++	u16 disabled_links;
++	u16 switch_time;
++	u16 switch_time_tsf_tu;
++	u32 duration;
++	struct os_reltime start_time;
++};
++#endif /* CONFIG_IEEE80211BE */
++
+ /* TDLS peer capabilities for send_tdls_mgmt() */
+ enum tdls_peer_capability {
+ 	TDLS_PEER_HT = BIT(0),
+@@ -5239,6 +5262,14 @@ struct wpa_driver_ops {
+ 	int (*link_remove)(void *priv, enum wpa_driver_if_type type,
+ 			   const char *ifname, u8 link_id);
+ 
++	/**
++	 * set_attlm - Set AP MLD advertised Tid-to-Link Mapping
++	 * @priv: Private driver interface data
++	 * @attlm: setting of Tid-to-Link Mapping
++	 * Returns: 0 on success, negative value on failure
++	 */
++	int (*set_attlm)(void *priv, struct attlm_settings *attlm);
++
+ 	/**
+ 	 * is_drv_shared - Check whether the driver interface is shared
+ 	 * @priv: Private driver interface data from init()
+@@ -6025,6 +6056,16 @@ enum wpa_event_type {
+ 	 */
+ 	EVENT_LINK_CH_SWITCH_STARTED,
+ 
++	/**
++	 * EVENT_ATTLM - MLD AP Advertised Tid-to-Link Mapping event
++	 *
++	 * This event is used by the driver to indicate the state transition of
++	 * A-TTLM.
++	 *
++	 * Described in wpa_event_data.attlm_event
++	 */
++	EVENT_ATTLM,
++
+ 	/**
+ 	 * EVENT_TID_LINK_MAP - MLD event to set TID-to-link mapping
+ 	 *
+@@ -6834,6 +6875,21 @@ union wpa_event_data {
+ 		u16 punct_bitmap;
+ 	} ch_switch;
+ 
++	/**
++	 * struct attlm_event
++	 * @switch_time_tsf_tu: the TSF of switch time in unit of TUs
++	 * @started: the ATTLM is started or has been done.
++	 * @switch_time_expired: the switch time has expired
++	 */
++	struct attlm_event {
++		enum {
++			EVENT_ATTLM_STARTED,
++			EVENT_ATTLM_SWITCH_TIME_EXPIRED,
++			EVENT_ATTLM_END
++		} event;
++		u16 switch_time_tsf_tu;
++	} attlm_event;
++
+ 	/**
+ 	 * struct connect_failed - Data for EVENT_CONNECT_FAILED_REASON
+ 	 * @addr: Remote client address
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 1d334b75f..05b231c52 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -11020,6 +11020,38 @@ static bool nl80211_is_drv_shared(void *priv, void *bss_ctx)
+ 	return true;
+ }
+ 
++
++static int nl80211_set_attlm(void *priv, struct attlm_settings *attlm)
++{
++	struct nl_msg *msg;
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	int ret = -ENOBUFS;
++
++	wpa_printf(MSG_DEBUG, "nl80211: Set A-TTLM");
++
++	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_ATTLM)) ||
++	    nla_put_u16(msg, NL80211_ATTR_MLO_LINK_DISABLED_BMP,
++			attlm->disabled_links) ||
++	    nla_put_u16(msg, NL80211_ATTR_MLO_ATTLM_SWITCH_TIME,
++			attlm->switch_time) ||
++	    nla_put_u32(msg, NL80211_ATTR_MLO_ATTLM_DURATION,
++			attlm->duration))
++		goto error;
++
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret) {
++		wpa_printf(MSG_DEBUG,
++			   "nl80211: disable link failed err=%d (%s)",
++			   ret, strerror(-ret));
++	}
++
++	return ret;
++error:
++	nlmsg_free(msg);
++	wpa_printf(MSG_DEBUG, "nl80211: Could not build link disabling request");
++	return ret;
++}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ 
+@@ -15717,6 +15749,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.link_add = nl80211_link_add,
+ #ifdef CONFIG_IEEE80211BE
+ 	.link_remove = driver_nl80211_link_remove,
++	.set_attlm = nl80211_set_attlm,
+ 	.is_drv_shared = nl80211_is_drv_shared,
+ 	.link_sta_remove = wpa_driver_nl80211_link_sta_remove,
+ #endif /* CONFIG_IEEE80211BE */
+diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
+index 24a4bf3cf..863c4eb65 100644
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -188,6 +188,8 @@ static const char * nl80211_command_to_string(enum nl80211_commands cmd)
+ 	C2S(NL80211_CMD_SET_HW_TIMESTAMP)
+ 	C2S(NL80211_CMD_LINKS_REMOVED)
+ 	C2S(NL80211_CMD_SET_TID_TO_LINK_MAPPING)
++	C2S(NL80211_CMD_ATTLM_EVENT)
++	C2S(NL80211_CMD_SET_ATTLM)
+ 	C2S(__NL80211_CMD_AFTER_LAST)
+ 	}
+ #undef C2S
+@@ -1313,6 +1315,53 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
+ }
+ 
+ 
++static void mlme_event_attlm(struct wpa_driver_nl80211_data *drv,
++			     struct nlattr *ifindex,
++			     struct nlattr *event,
++			     struct nlattr *switch_time_tsf_tu)
++{
++	enum nl80211_attlm_event event_type;
++	union wpa_event_data data;
++	struct i802_bss *bss;
++	int ifidx;
++
++	ifidx = nla_get_u32(ifindex);
++	bss = get_bss_ifindex(drv, ifidx);
++	if (bss == NULL) {
++		wpa_printf(MSG_WARNING,
++			   "nl80211: Unknown ifindex (%d) for A-TTLM, ignoring",
++			   ifidx);
++		return;
++	}
++
++	if (!event)
++		return;
++
++	wpa_printf(MSG_DEBUG, "nl80211: %s: A-TTLM event", bss->ifname);
++
++	data.attlm_event.switch_time_tsf_tu = switch_time_tsf_tu ?
++					nla_get_u16(switch_time_tsf_tu) : 0;
++	event_type = nla_get_u32(event);
++	switch (event_type) {
++		case NL80211_ATTLM_STARTED:
++			data.attlm_event.event = EVENT_ATTLM_STARTED;
++			break;
++		case NL80211_ATTLM_SWITCH_TIME_EXPIRED:
++			data.attlm_event.event = EVENT_ATTLM_SWITCH_TIME_EXPIRED;
++			break;
++		case NL80211_ATTLM_END:
++			data.attlm_event.event = EVENT_ATTLM_END;
++			break;
++		default:
++			wpa_printf(MSG_DEBUG,
++				   "nl80211: Unsupported A-TTLM event");
++			return;
++	}
++
++	wpa_supplicant_event(bss->ctx, EVENT_ATTLM, &data);
++}
++
++
+ static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
+ 			       enum nl80211_commands cmd, struct nlattr *addr)
+ {
+@@ -4125,6 +4174,11 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
+ 				     NULL,
+ 				     1);
+ 		break;
++	case NL80211_CMD_ATTLM_EVENT:
++		mlme_event_attlm(drv, tb[NL80211_ATTR_IFINDEX],
++				 tb[NL80211_ATTR_MLO_ATTLM_EVENT],
++				 tb[NL80211_ATTR_MLO_ATTLM_SWITCH_TIME_TSF_TU]);
++		break;
+ 	case NL80211_CMD_DISCONNECT:
+ 		mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
+ 				      tb[NL80211_ATTR_MAC],
+diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
+index 13837297c..f997edd6e 100644
+--- a/src/drivers/nl80211_copy.h
++++ b/src/drivers/nl80211_copy.h
+@@ -1588,6 +1588,10 @@ enum nl80211_commands {
+ 
+ 	/* add new commands above here */
+ 
++	/* MTK internal */
++	NL80211_CMD_ATTLM_EVENT,
++	NL80211_CMD_SET_ATTLM,
++
+ 	/* used to define NL80211_CMD_MAX below */
+ 	__NL80211_CMD_AFTER_LAST,
+ 	NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1
+@@ -3388,6 +3392,7 @@ enum nl80211_attrs {
+ 
+ 	NL80211_ATTR_MLO_LINKS,
+ 	NL80211_ATTR_MLO_LINK_ID,
++	NL80211_ATTR_MLO_LINK_DISABLED_BMP,
+ 	NL80211_ATTR_MLD_ADDR,
+ 
+ 	NL80211_ATTR_MLO_SUPPORT,
+@@ -3425,6 +3430,11 @@ enum nl80211_attrs {
+ 	/* MTK internal */
+ 	NL80211_ATTR_CNTDWN_OFFS_STA_PROF,
+ 
++	NL80211_ATTR_MLO_ATTLM_EVENT,
++	NL80211_ATTR_MLO_ATTLM_SWITCH_TIME,
++	NL80211_ATTR_MLO_ATTLM_DURATION,
++	NL80211_ATTR_MLO_ATTLM_SWITCH_TIME_TSF_TU,
++
+ 	__NL80211_ATTR_AFTER_LAST,
+ 	NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST,
+ 	NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
+@@ -8090,4 +8100,18 @@ enum nl80211_wiphy_radio_freq_range {
+ 	NL80211_WIPHY_RADIO_FREQ_ATTR_MAX = __NL80211_WIPHY_RADIO_FREQ_ATTR_LAST - 1,
+ };
+ 
++/**
++ * enum nl80211_attlm_event - type of events for Advertised Tid-to-Link
++ * Mapping operations
++ *
++ * @NL80211_ATTLM_STARTED: A A-TTLM request has been set and start to count down.
++ * @NL80211_ATTLM_SWITCH_TIME_EXPIRED: The switch time of A-TTLM has expired.
++ * @NL80211ATTLM_END: The A-TTLM has been done.
++ */
++enum nl80211_attlm_event {
++	NL80211_ATTLM_STARTED,
++	NL80211_ATTLM_SWITCH_TIME_EXPIRED,
++	NL80211_ATTLM_END,
++};
++
+ #endif /* __LINUX_NL80211_H */
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0120-mtk-hostapd-Add-txpower-vendor-command.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0120-mtk-hostapd-Add-txpower-vendor-command.patch
new file mode 100644
index 0000000..4201379
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0120-mtk-hostapd-Add-txpower-vendor-command.patch
@@ -0,0 +1,299 @@
+From 4c0dd29eb15b2cef5cf5256459a19d59e5c98adc Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Fri, 22 Dec 2023 18:09:20 +0800
+Subject: [PATCH 120/126] mtk: hostapd: Add txpower vendor command
+
+Porting and refactor from wifi6 power vendor cmd. Add lpi psd control,
+sku index and duplicate mode enhancement.
+
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ hostapd/config_file.c             |  8 +++++
+ src/ap/ap_config.c                |  4 +++
+ src/ap/ap_config.h                |  3 ++
+ src/ap/ap_drv_ops.c               | 16 +++++++++
+ src/ap/ap_drv_ops.h               |  1 +
+ src/ap/hostapd.c                  |  2 ++
+ src/ap/ieee802_11_he.c            |  3 ++
+ src/common/mtk_vendor.h           | 15 +++++++++
+ src/drivers/driver.h              | 10 ++++++
+ src/drivers/driver_nl80211.c      | 55 +++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |  1 +
+ src/drivers/driver_nl80211_capa.c |  3 ++
+ 12 files changed, 121 insertions(+)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index f7bfc357a..944669270 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -5570,6 +5570,14 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 			return 1;
+ 		}
+ 		conf->band_idx = (u8) val;
++	} else if (os_strcmp(buf, "lpi_psd") == 0) {
++		u8 en = strtol(pos, NULL, 10);
++		conf->lpi_psd = !!en;
++	} else if (os_strcmp(buf, "sku_idx") == 0) {
++		conf->sku_idx = strtol(pos, NULL, 10);
++	} else if (os_strcmp(buf, "lpi_bcn_enhance") == 0) {
++		u8 en = strtol(pos, NULL, 10);
++		conf->lpi_bcn_enhance = !!en;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 4528df823..bb5ec78a1 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -315,6 +315,10 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 	conf->pp_mode = PP_FW_MODE;
+ 	conf->band_idx = 255;
+ 
++	conf->lpi_psd = 0;
++	conf->sku_idx = 0;
++	conf->lpi_bcn_enhance = 0;
++
+ 	hostapd_set_and_check_bw320_offset(conf, 0);
+ 
+ 	return conf;
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 9c3a28cf4..ea8507cea 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1356,6 +1356,9 @@ struct hostapd_config {
+ 	void *muru_config;
+ 	u8 pp_mode;
+ 	u8 band_idx;
++	u8 lpi_psd;
++	u8 sku_idx;
++	u8 lpi_bcn_enhance;
+ };
+ 
+ enum three_wire_mode {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index a25a67cdd..2f53c518f 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1391,6 +1391,22 @@ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_colo
+ 	return hapd->driver->get_aval_color_bmp(hapd->drv_priv, aval_color_bmp);
+ }
+ 
++int hostapd_drv_txpower_ctrl(struct hostapd_data *hapd)
++{
++	s8 link_id = -1;
++
++	if (!hapd->driver || !hapd->driver->txpower_ctrl)
++		return 0;
++
++	if (hapd->conf->mld_ap)
++		link_id = hapd->mld_link_id;
++
++	return hapd->driver->txpower_ctrl(hapd->drv_priv, hapd->iconf->lpi_psd,
++					  hapd->iconf->sku_idx,
++					  hapd->iconf->lpi_bcn_enhance,
++					  link_id);
++}
++
+ int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
+ {
+ 	s8 link_id = -1;
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 7f108bc1d..a8c60b6f6 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -167,6 +167,7 @@ int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
+ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd,
+ 				       u64 *aval_color_bmp);
++int hostapd_drv_txpower_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
+ int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
+ int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 9388f6070..6f7f13f9f 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2879,6 +2879,8 @@ dfs_offload:
+ 		goto fail;
+ 	if (hostapd_drv_amsdu_ctrl(hapd) < 0)
+ 		goto fail;
++	if (hostapd_drv_txpower_ctrl(hapd) < 0)
++		goto fail;
+ 
+ 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ 		   iface->bss[0]->conf->iface);
+diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
+index 3b6b2041c..f3a5679a0 100644
+--- a/src/ap/ieee802_11_he.c
++++ b/src/ap/ieee802_11_he.c
+@@ -264,6 +264,9 @@ u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
+ 		else
+ 			control = center_idx_to_bw_6ghz(seg0);
+ 
++		if (hapd->iconf->lpi_bcn_enhance)
++			control |= HE_6GHZ_OPER_INFO_CTRL_DUP_BEACON;
++
+ 		control |= hapd->iconf->he_6ghz_reg_pwr_type <<
+ 			HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT;
+ 
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 933f0099d..6d75d39c2 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -19,6 +19,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_BACKGROUND_RADAR_CTRL = 0xcb,
+ 	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
+ 	MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
++	MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL = 0xce,
+ 	MTK_NL80211_VENDOR_SUBCMD_EML_CTRL = 0xd3,
+ };
+ 
+@@ -310,6 +311,20 @@ enum mtk_vendor_attr_eml_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_EML_CTRL -1
+ };
+ 
++enum mtk_vendor_attr_txpower_ctrl {
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_PSD,
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX,
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE,
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL,
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL - 1
++};
++
+ #define CSI_BW20_DATA_COUNT	64
+ #define CSI_BW40_DATA_COUNT	128
+ #define CSI_BW80_DATA_COUNT	256
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index fe327e560..55f62f537 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5443,6 +5443,16 @@ struct wpa_driver_ops {
+ 	* @dump_buf: Dump_struct that store csi data and related info
+ 	*/
+ 	int (*csi_dump)(void *priv, u8 band_idx, void *dump_buf);
++	/**
++	* txpower_ctrl - ctrl txpower operation
++	* @priv: Private driver interface data
++	* @lpi_psd: 1 to enable lpi psd compensate, 0 to disable
++	* @lpi_bcn_enhance: 1 to enable beacon duplicate enhancement in 6G lpi mode, 0 to disable enhancement
++	* @sku_idx: index used to indicate which sku table should be used
++	* @link_id: MLD link id. -1 if this is an non-MLD AP
++	*/
++	int (*txpower_ctrl)(void *priv, u8 lpi_psd, u8 sku_idx, u8 lpi_bcn_enhance,
++			    u8 link_id);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 05b231c52..395bb5926 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -195,6 +195,14 @@ static struct nla_policy csi_data_policy[NUM_MTK_VENDOR_ATTRS_CSI_DATA] = {
+ 	[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO] = { .type = NLA_U32 },
+ };
+ 
++static struct nla_policy
++txpower_ctrl_policy[NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL] = {
++	[MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_PSD] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID] = { .type = NLA_U8 },
++};
++
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+ 	struct nl_sock *handle;
+@@ -15592,6 +15600,52 @@ fail:
+ 	return -ENOBUFS;
+ }
+ 
++static int nl80211_txpower_ctrl(void *priv, u8 lpi_psd, u8 sku_idx, u8 lpi_bcn_enhance,
++				u8 link_id)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_txpower_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting txpower control");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			    MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_PSD, lpi_psd);
++	nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX, sku_idx);
++	nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE, lpi_bcn_enhance);
++	nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID, link_id);
++
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set power. ret=%d (%s)",
++			   ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+ 	.desc = "Linux nl80211/cfg80211",
+@@ -15779,4 +15833,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ #endif
+ 	.csi_set = nl80211_csi_set,
+ 	.csi_dump = nl80211_csi_dump,
++	.txpower_ctrl = nl80211_txpower_ctrl,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 2cc40e0dc..d32c0ff4d 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -214,6 +214,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_beacon_ctrl_vendor_cmd_avail:1;
+ 	unsigned int mtk_csi_vendor_cmd_avail:1;
+ 	unsigned int mtk_eml_vendor_cmd_avail:1;
++	unsigned int mtk_txpower_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index c1327a679..fc7f9062f 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1179,6 +1179,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_EML_CTRL:
+ 					drv->mtk_eml_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL:
++					drv->mtk_txpower_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0121-mtk-hostapd-Add-Triggered-Uplink-Access-Optimization.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0121-mtk-hostapd-Add-Triggered-Uplink-Access-Optimization.patch
new file mode 100644
index 0000000..567d0d3
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0121-mtk-hostapd-Add-Triggered-Uplink-Access-Optimization.patch
@@ -0,0 +1,651 @@
+From d612e6c3981212812e87374f90b7be07edf9bc2a Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Tue, 2 Jul 2024 09:46:26 +0800
+Subject: [PATCH 121/126] mtk: hostapd: Add Triggered Uplink Access
+ Optimization support
+
+Add TUAO feature support.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ hostapd/Makefile                  |   1 +
+ src/ap/Makefile                   |   3 +-
+ src/ap/ap_drv_ops.c               |  13 ++
+ src/ap/ap_drv_ops.h               |   5 +
+ src/ap/ieee802_11.c               |   6 +
+ src/ap/scs.c                      | 247 ++++++++++++++++++++++++++++++
+ src/ap/scs.h                      |  30 ++++
+ src/ap/sta_info.h                 |   5 +
+ src/common/ieee802_11_defs.h      |   5 +
+ src/common/mtk_vendor.h           |  17 ++
+ src/drivers/driver.h              |  26 ++++
+ src/drivers/driver_nl80211.c      |  62 ++++++++
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   3 +
+ 14 files changed, 423 insertions(+), 1 deletion(-)
+ create mode 100644 src/ap/scs.c
+ create mode 100644 src/ap/scs.h
+
+diff --git a/hostapd/Makefile b/hostapd/Makefile
+index 8dc6e6216..233176ae5 100644
+--- a/hostapd/Makefile
++++ b/hostapd/Makefile
+@@ -382,6 +382,7 @@ ifdef CONFIG_IEEE80211BE
+ CONFIG_IEEE80211AX=y
+ CFLAGS += -DCONFIG_IEEE80211BE
+ OBJS += ../src/ap/ieee802_11_eht.o
++OBJS += ../src/ap/scs.o
+ endif
+ 
+ ifdef CONFIG_IEEE80211AX
+diff --git a/src/ap/Makefile b/src/ap/Makefile
+index a1e9b7c44..49c6d4a13 100644
+--- a/src/ap/Makefile
++++ b/src/ap/Makefile
+@@ -55,6 +55,7 @@ LIB_OBJS= \
+ 	wpa_auth_glue.o \
+ 	wpa_auth_ie.o \
+ 	wps_hostapd.o \
+-	x_snoop.o
++	x_snoop.o \
++	scs.o
+ 
+ include ../lib.rules
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 2f53c518f..64fd0c04c 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -23,6 +23,10 @@
+ #include "wpa_auth.h"
+ #include "ap_drv_ops.h"
+ 
++#ifdef CONFIG_IEEE80211BE
++#include "scs.h"
++#endif
++
+ 
+ u32 hostapd_sta_flags_to_drv(u32 flags)
+ {
+@@ -1531,3 +1535,12 @@ int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf)
+ 		return 0;
+ 	return hapd->driver->csi_dump(hapd->drv_priv, hapd->iconf->band_idx, dump_buf);
+ }
++
++#ifdef CONFIG_IEEE80211BE
++int hostapd_drv_set_scs(struct hostapd_data *hapd, struct hostapd_scs_desc_info *info)
++{
++	if (!hapd->driver || !hapd->driver->set_scs)
++		return 0;
++	return hapd->driver->set_scs(hapd->drv_priv, info, hapd->mld_link_id);
++}
++#endif
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index a8c60b6f6..3e5743754 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -190,6 +190,11 @@ int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
+ int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set,
+ 			    u8 qos_map_set_len);
+ 
++#ifdef CONFIG_IEEE80211BE
++int hostapd_drv_set_scs(struct hostapd_data *hapd,
++			struct hostapd_scs_desc_info *info);
++#endif
++
+ void hostapd_get_ext_capa(struct hostapd_iface *iface);
+ void hostapd_get_mld_capa(struct hostapd_iface *iface);
+ 
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index 886a21a66..09aa1141b 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -62,6 +62,9 @@
+ #ifdef CONFIG_APUP
+ #	include "apup.h"
+ #endif // def CONFIG_APUP
++#ifdef CONFIG_IEEE80211BE
++#include "scs.h"
++#endif
+ 
+ #ifdef CONFIG_FILS
+ static struct wpabuf *
+@@ -6247,6 +6250,9 @@ static int handle_action(struct hostapd_data *hapd,
+ 		hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len);
+ 		return 1;
+ #endif /* CONFIG_NO_RRM */
++	case WLAN_ACTION_ROBUST_AV_STREAMING:
++		hostapd_handle_scs(hapd, (const u8 *) mgmt, len);
++		return 1;
+ 	}
+ 
+ 	hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+diff --git a/src/ap/scs.c b/src/ap/scs.c
+new file mode 100644
+index 000000000..6f3c0444e
+--- /dev/null
++++ b/src/ap/scs.c
+@@ -0,0 +1,247 @@
++#include "utils/includes.h"
++
++#include "utils/common.h"
++#include "common/ieee802_11_defs.h"
++#include "common/ieee802_11_common.h"
++#include "hostapd.h"
++#include "ieee802_11.h"
++#include "sta_info.h"
++#include "ap_config.h"
++#include "ap_drv_ops.h"
++#include "scs.h"
++
++static bool hostapd_find_scs_session(struct sta_info *sta, u8 scsid,
++				     u8 *session_idx)
++{
++	u8 idx;
++
++	for (idx = 0; idx < SCS_MAX_CFG_CNT; idx++) {
++		if (sta->scs_session[idx].scs_id == scsid) {
++			*session_idx = idx;
++			return sta->scs_session[idx].alive;
++		}
++	}
++
++	return false;
++}
++
++static int hostapd_find_available_scs_session(struct sta_info *sta)
++{
++	u8 idx;
++
++	for (idx = 0; idx < SCS_MAX_CFG_CNT; idx++) {
++		if (!sta->scs_session[idx].alive)
++			return idx;
++	}
++
++	return -1;
++}
++
++static bool hostapd_parse_qos_char_element(const struct element *elem,
++					   struct hostapd_scs_desc_info *info)
++{
++#define SCS_DIRECTION_UPLINK 0
++	u8 id_extension = elem->data[0];
++	u32 control_info;
++
++	info->qos_ie_len = elem->datalen + 2;
++
++	if (id_extension != WLAN_EID_EXT_QOS_CHARACTERISTICS ||
++	    info->qos_ie_len > sizeof(info->qos_ie))
++		return false;
++
++	control_info = WPA_GET_LE32(&elem->data[1]);
++	info->dir = control_info & 0x3;
++
++	/* Only support Uplink direction SCS request now. */
++	if (info->dir != SCS_DIRECTION_UPLINK)
++		return false;
++
++	os_memcpy(info->qos_ie, elem, info->qos_ie_len);
++
++	return true;
++}
++
++static u16 hostapd_process_scs_descriptor(struct hostapd_data *hapd,
++					  struct sta_info *sta, const u8 *payload,
++					  u8 scs_desc_len,
++					  struct hostapd_scs_desc_info *info)
++{
++	bool scs_avail, qos_char_elem_avail = false;
++	const struct element *elem;
++	u8 session_idx;
++	int ret;
++
++	scs_avail = hostapd_find_scs_session(sta, info->id, &session_idx);
++
++	switch (info->req_type) {
++	case SCS_REQ_TYPE_ADD:
++	case SCS_REQ_TYPE_CHANGE:
++		if ((info->req_type == SCS_REQ_TYPE_ADD && scs_avail) ||
++		    (info->req_type == SCS_REQ_TYPE_CHANGE && !scs_avail))
++			goto decline;
++
++		if (info->req_type == SCS_REQ_TYPE_ADD) {
++			session_idx = hostapd_find_available_scs_session(sta);
++			if (session_idx == -1) {
++				wpa_printf(MSG_ERROR, "%s: Out of SCS resource.\n",
++					   __func__);
++				goto decline;
++			}
++		}
++
++		for_each_element(elem, payload + 2, scs_desc_len - 2) {
++			switch (elem->id) {
++			case WLAN_EID_EXTENSION:
++				qos_char_elem_avail =
++					hostapd_parse_qos_char_element(elem, info);
++				break;
++			default:
++				/* The rest elements would be ignored now. */
++				break;
++			}
++		}
++
++		if (!qos_char_elem_avail) {
++			wpa_printf(MSG_ERROR, "%s: The content of QoS Charactristics"
++				   " element is empty or not supported yet!\n",
++				   __func__);
++			goto decline;
++		}
++
++		break;
++	case SCS_REQ_TYPE_REMOVE:
++		if (!scs_avail)
++			goto decline;
++
++		break;
++	default:
++		goto decline;
++	}
++
++	ret = hostapd_drv_set_scs(hapd, info);
++	if (ret)
++		goto decline;
++
++	sta->scs_session[session_idx].scs_id = info->id;
++	sta->scs_session[session_idx].alive =
++		info->req_type == SCS_REQ_TYPE_REMOVE ? false : true;
++
++	return (info->req_type == SCS_REQ_TYPE_REMOVE) ?
++		SCS_REQ_TCLAS_PROCESSING_TERMINATED : SCS_REQ_SUCCESS;
++
++decline:
++	wpa_printf(MSG_ERROR, "%s: Decline Request Type %d\n",
++		   __func__, info->req_type);
++
++	return SCS_REQ_DECLINED;
++}
++
++static void send_scs_response(struct hostapd_data *hapd,
++			      struct scs_status_duple *scs_status, const u8 *da,
++			      u8 dialog_token, u8 count)
++{
++	struct wpabuf *buf;
++	size_t len;
++	u8 i;
++
++	if (count == 0)
++		return;
++
++	/* Reference to 802_11be_D5.0 Figure 9-1183  */
++	len = 4 + count * sizeof(struct scs_status_duple);
++	buf = wpabuf_alloc(len);
++	if (buf == NULL)
++		return;
++
++	wpabuf_put_u8(buf, WLAN_ACTION_ROBUST_AV_STREAMING);
++	wpabuf_put_u8(buf, ROBUST_AV_SCS_RESP);
++	wpabuf_put_u8(buf, dialog_token);
++	wpabuf_put_u8(buf, count);
++
++	for (i = 0; i < count && i < SCS_MAX_CFG_CNT; i++) {
++		wpabuf_put_u8(buf, scs_status[i].scs_id);
++		wpabuf_put_le16(buf, scs_status[i].status);
++	}
++
++	len = wpabuf_len(buf);
++	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, da,
++				wpabuf_head(buf), len);
++	wpabuf_free(buf);
++}
++
++static void hostapd_handle_scs_req(struct hostapd_data *hapd,
++				   const u8 *buf, size_t len)
++{
++	const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
++	struct hostapd_scs_desc_info info;
++	struct sta_info *sta;
++	struct scs_status_duple scs_status_list[SCS_MAX_CFG_CNT];
++	const u8 *pos, *end;
++	u8 token, index = 0;
++	const struct element *elem;
++
++	sta = ap_get_sta(hapd, mgmt->sa);
++
++	if (!sta) {
++		wpa_printf(MSG_ERROR, "Station " MACSTR " not found "
++			   "for SCS Request frame\n", MAC2STR(mgmt->sa));
++		return;
++	}
++
++	token = mgmt->u.action.u.scs.dialog_token;
++	pos = mgmt->u.action.u.scs.variable;
++
++	end = buf + len;
++	len = end - pos;
++
++	for_each_element(elem, pos, len) {
++		if (elem->id != WLAN_EID_SCS_DESCRIPTOR) {
++			wpa_printf(MSG_ERROR, "%s: no scs elem %d in scs req frame!\n",
++				   __func__, WLAN_EID_SCS_DESCRIPTOR);
++			break;
++		}
++
++		info.id = elem->data[0];
++		if (!info.id) {
++			wpa_printf(MSG_ERROR, "%s: SCSID = 0 is invalid\n", __func__);
++			break;
++		}
++
++		info.req_type = elem->data[1];
++		os_memcpy(info.peer_addr, mgmt->sa, ETH_ALEN);
++		scs_status_list[index].scs_id = info.id;
++		scs_status_list[index].status =
++			hostapd_process_scs_descriptor(hapd, sta, elem->data,
++						       elem->datalen, &info);
++		index++;
++	}
++
++	send_scs_response(hapd, scs_status_list, mgmt->sa, token, index);
++}
++
++void hostapd_handle_scs(struct hostapd_data *hapd, const u8 *buf, size_t len)
++{
++	const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
++
++	/*
++	 * Check for enough bytes: header + (1B)Category + (1B)Action +
++	 * (1B)Dialog Token.
++	 */
++	if (len < IEEE80211_HDRLEN + 3) {
++		wpa_printf(MSG_ERROR, "%s SCS frame len %lu is not enough!",
++			   __func__, len);
++		return;
++	}
++
++	switch (mgmt->u.action.u.scs.action) {
++	case ROBUST_AV_SCS_REQ:
++		hostapd_handle_scs_req(hapd, buf, len);
++		break;
++	case ROBUST_AV_SCS_RESP:
++		/* Not supported yet. */
++		break;
++	default:
++		break;
++	}
++}
+diff --git a/src/ap/scs.h b/src/ap/scs.h
+new file mode 100644
+index 000000000..b69190b65
+--- /dev/null
++++ b/src/ap/scs.h
+@@ -0,0 +1,30 @@
++#ifndef SCS_H
++#define SCS_H
++
++struct hostapd_data;
++
++/* Only support TUAO certification */
++#define SCS_MAX_CFG_CNT 2
++
++struct scs_status_duple {
++	u8 scs_id;
++	u16 status;
++};
++
++struct scs_session_status {
++	u8 scs_id;
++	bool alive;
++};
++
++enum scs_req_type {
++	SCS_REQ_TYPE_ADD,
++	SCS_REQ_TYPE_REMOVE,
++	SCS_REQ_TYPE_CHANGE,
++};
++
++#define SCS_REQ_SUCCESS		0
++#define SCS_REQ_DECLINED	37
++#define SCS_REQ_TCLAS_PROCESSING_TERMINATED	97
++
++void hostapd_handle_scs(struct hostapd_data *hapd, const u8 *buf, size_t len);
++#endif
+diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
+index 60b33f049..339adf987 100644
+--- a/src/ap/sta_info.h
++++ b/src/ap/sta_info.h
+@@ -18,6 +18,10 @@
+ #include "crypto/sha384.h"
+ #include "pasn/pasn_common.h"
+ 
++#ifdef CONFIG_IEEE80211BE
++#include "scs.h"
++#endif
++
+ /* STA flags */
+ #define WLAN_STA_AUTH BIT(0)
+ #define WLAN_STA_ASSOC BIT(1)
+@@ -335,6 +339,7 @@ struct sta_info {
+ 	struct mld_info mld_info;
+ 	u8 mld_assoc_link_id;
+ 	struct sta_info *mld_assoc_sta;
++	struct scs_session_status scs_session[SCS_MAX_CFG_CNT];
+ #endif /* CONFIG_IEEE80211BE */
+ 
+ 	u16 max_idle_period; /* if nonzero, the granted BSS max idle period in
+diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
+index afcc2f861..4754d5a69 100644
+--- a/src/common/ieee802_11_defs.h
++++ b/src/common/ieee802_11_defs.h
+@@ -1196,6 +1196,11 @@ struct ieee80211_mgmt {
+ 					u8 action;
+ 					u8 variable[];
+ 				} STRUCT_PACKED eht_prot;
++				struct {
++					u8 action;
++					u8 dialog_token;
++					u8 variable[];
++				} STRUCT_PACKED scs;
+ 			} u;
+ 		} STRUCT_PACKED action;
+ 	} u;
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 6d75d39c2..1fe459126 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -20,6 +20,7 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
+ 	MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
+ 	MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL = 0xce,
++	MTK_NL80211_VENDOR_SUBCMD_SCS_CTRL = 0xd0,
+ 	MTK_NL80211_VENDOR_SUBCMD_EML_CTRL = 0xd3,
+ };
+ 
+@@ -325,6 +326,22 @@ enum mtk_vendor_attr_txpower_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_scs_ctrl {
++	MTK_VENDOR_ATTR_SCS_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_SCS_ID,
++	MTK_VENDOR_ATTR_SCS_REQ_TYPE,
++	MTK_VENDOR_ATTR_SCS_DIR,
++	MTK_VENDOR_ATTR_SCS_QOS_IE,
++	MTK_VENDOR_ATTR_SCS_MAC_ADDR,
++	MTK_VENDOR_ATTR_SCS_LINK_ID,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_SCS_CTRL,
++	MTK_VENDOR_ATTR_SCS_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_SCS_CTRL - 1
++};
++
+ #define CSI_BW20_DATA_COUNT	64
+ #define CSI_BW40_DATA_COUNT	128
+ #define CSI_BW80_DATA_COUNT	256
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 55f62f537..f3de2b3bc 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -70,6 +70,25 @@ enum hostapd_chan_width_attr {
+ #define HOSTAPD_DFS_REGION_ETSI	2
+ #define HOSTAPD_DFS_REGION_JP	3
+ 
++/**
++ * struct hostapd_scs_desc_info - SCS Req information
++ * @id: SCSID of each SCS stream
++ * @req_type: request type in SCS Descriptor element
++ * @dir: Direction in the control info of QoS Characteristics element
++ * @peer_addr: the mac addr of SCS requester station
++ * @qos_ie: QoS Characteristics IE in SCS Descriptor element
++ * @qos_ie_len: the length of QoS Characteristics element
++ */
++#define EID_EXT_QOS_CHAR_MAX_SIZE 44
++struct hostapd_scs_desc_info {
++	u8 id;
++	u8 req_type;
++	u8 dir;
++	u8 peer_addr[ETH_ALEN];
++	u8 qos_ie[EID_EXT_QOS_CHAR_MAX_SIZE];
++	u8 qos_ie_len;
++};
++
+ /**
+  * enum reg_change_initiator - Regulatory change initiator
+  */
+@@ -5424,6 +5443,13 @@ struct wpa_driver_ops {
+ 	int (*pp_mode_set)(void *priv, const u8 pp_mode, s8 link_id, u16 punct_bitmap);
+ #ifdef CONFIG_IEEE80211BE
+ 	int (*get_mld_addr)(void *priv, u8 *addr);
++	/**
++	 * set_scs - Configure Stream Classification Service
++	 * @priv: Private driver interface data
++	 * @info: Stream classidication service configuration
++	 * @link_id: MLD link id
++	 */
++	int (*set_scs)(void *priv, struct hostapd_scs_desc_info *info, u8 link_id);
+ #endif
+ 	/**
+ 	 * csi_set - Set csi related mode and parameter
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 395bb5926..d2064fbd6 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -42,6 +42,10 @@
+ #include "common/mtk_vendor.h"
+ #include "ap/ap_config.h"
+ 
++#ifdef CONFIG_IEEE80211BE
++#include "ap/scs.h"
++#endif
++
+ 
+ #ifndef NETLINK_CAP_ACK
+ #define NETLINK_CAP_ACK 10
+@@ -2932,6 +2936,9 @@ static int nl80211_action_subscribe_ap(struct i802_bss *bss)
+ 	/* Protected EHT */
+ 	if (nl80211_register_action_frame(bss, (u8 *) "\x25", 1) < 0)
+ 		ret = -1;
++	/* Robust AV SCS Request */
++	if (nl80211_register_action_frame(bss, (u8 *) "\x13\x00", 2) < 0)
++		ret = -1;
+ 	/* Vendor-specific */
+ 	if (nl80211_register_action_frame(bss, (u8 *) "\x7f", 1) < 0)
+ 		ret = -1;
+@@ -15387,6 +15394,60 @@ static int nl80211_set_eml_omn(void *priv, u8 link_id, u8 *addr,
+ 
+ 	return ret;
+ 
++fail:
++	nlmsg_free(msg);
++	return ret;
++}
++
++static int
++nl80211_set_scs(void *priv, struct hostapd_scs_desc_info *info, u8 link_id)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_scs_vendor_cmd_avail) {
++		wpa_printf(MSG_ERROR,
++			   "nl80211: Driver does not support scs");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_SCS_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	if (nla_put_u8(msg, MTK_VENDOR_ATTR_SCS_ID, info->id) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_SCS_REQ_TYPE, info->req_type) ||
++	    nla_put(msg, MTK_VENDOR_ATTR_SCS_MAC_ADDR, ETH_ALEN, info->peer_addr) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_SCS_LINK_ID, link_id))
++		goto fail;
++
++	if (info->req_type == SCS_REQ_TYPE_ADD ||
++	    info->req_type == SCS_REQ_TYPE_CHANGE)
++		if (nla_put_u8(msg, MTK_VENDOR_ATTR_SCS_DIR, info->dir) ||
++		    nla_put(msg, MTK_VENDOR_ATTR_SCS_QOS_IE, info->qos_ie_len,
++			    info->qos_ie))
++			goto fail;
++
++	nla_nest_end(msg, data);
++	ret = send_and_recv_cmd(drv, msg);
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set scs. ret = %d (%s)",
++			   ret, strerror(-ret));
++
++	return ret;
++
+ fail:
+ 	nlmsg_free(msg);
+ 	return ret;
+@@ -15830,6 +15891,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.pp_mode_set = nl80211_pp_mode_set,
+ #ifdef CONFIG_IEEE80211BE
+ 	.get_mld_addr = nl80211_get_mld_addr,
++	.set_scs = nl80211_set_scs,
+ #endif
+ 	.csi_set = nl80211_csi_set,
+ 	.csi_dump = nl80211_csi_dump,
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index d32c0ff4d..87ade150b 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -215,6 +215,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_csi_vendor_cmd_avail:1;
+ 	unsigned int mtk_eml_vendor_cmd_avail:1;
+ 	unsigned int mtk_txpower_vendor_cmd_avail:1;
++	unsigned int mtk_scs_vendor_cmd_avail:1;
+ 
+ 	u32 ignore_next_local_disconnect;
+ 	u32 ignore_next_local_deauth;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index fc7f9062f..5db6bf6e6 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1182,6 +1182,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL:
+ 					drv->mtk_txpower_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_SCS_CTRL:
++					drv->mtk_scs_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0122-mtk-hostapd-fix-6G-EHT-BW-320-channel-switch-issue.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0122-mtk-hostapd-fix-6G-EHT-BW-320-channel-switch-issue.patch
new file mode 100644
index 0000000..8358b97
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0122-mtk-hostapd-fix-6G-EHT-BW-320-channel-switch-issue.patch
@@ -0,0 +1,100 @@
+From 562e8a54538b200256ba0e0d820951d88686044b Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 5 Aug 2024 14:56:17 +0800
+Subject: [PATCH 122/126] mtk: hostapd: fix 6G EHT BW 320 channel switch issue
+
+When channel switching from BW 320 to BW 160, the op class is not
+changed for building csa after beacon.
+Therefore, hostapd_eid_eht_operation will use the old op class to fill
+the EHT operation chwidth info for csa after beacon, leading to
+disconnections for stations due to an invalid chandef.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/hostapd.c     | 39 ++++++++++++++++++++++-----------------
+ src/drivers/driver.h |  5 +++++
+ 2 files changed, 27 insertions(+), 17 deletions(-)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 6f7f13f9f..d06a0395b 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4496,23 +4496,26 @@ static int hostapd_change_config_freq(struct hostapd_data *hapd,
+ 	mode = hapd->iface->current_mode;
+ 
+ 	/* if a pointer to old_params is provided we save previous state */
+-	if (old_params &&
+-	    hostapd_set_freq_params(old_params, conf->hw_mode,
+-				    hostapd_hw_get_freq(hapd, conf->channel),
+-				    conf->channel, conf->enable_edmg,
+-				    conf->edmg_channel, conf->ieee80211n,
+-				    conf->ieee80211ac, conf->ieee80211ax,
+-				    conf->ieee80211be, conf->secondary_channel,
+-				    hostapd_get_oper_chwidth(conf),
+-				    hostapd_get_oper_centr_freq_seg0_idx(conf),
+-				    hostapd_get_oper_centr_freq_seg1_idx(conf),
+-				    conf->vht_capab,
+-				    mode ? &mode->he_capab[IEEE80211_MODE_AP] :
+-				    NULL,
+-				    mode ? &mode->eht_capab[IEEE80211_MODE_AP] :
+-				    NULL,
+-				    hostapd_get_punct_bitmap(hapd)))
+-		return -1;
++	if (old_params) {
++		if (hostapd_set_freq_params(old_params, conf->hw_mode,
++					    hostapd_hw_get_freq(hapd, conf->channel),
++					    conf->channel, conf->enable_edmg,
++					    conf->edmg_channel, conf->ieee80211n,
++					    conf->ieee80211ac, conf->ieee80211ax,
++					    conf->ieee80211be, conf->secondary_channel,
++					    hostapd_get_oper_chwidth(conf),
++					    hostapd_get_oper_centr_freq_seg0_idx(conf),
++					    hostapd_get_oper_centr_freq_seg1_idx(conf),
++					    conf->vht_capab,
++					    mode ? &mode->he_capab[IEEE80211_MODE_AP] :
++					    NULL,
++					    mode ? &mode->eht_capab[IEEE80211_MODE_AP] :
++					    NULL,
++					    hostapd_get_punct_bitmap(hapd)))
++			return -1;
++
++		old_params->op_class = conf->op_class;
++	}
+ 
+ 	switch (params->bandwidth) {
+ 	case 0:
+@@ -4557,6 +4560,7 @@ static int hostapd_change_config_freq(struct hostapd_data *hapd,
+ 	conf->ieee80211n = params->ht_enabled;
+ 	conf->ieee80211ac = params->vht_enabled;
+ 	conf->secondary_channel = params->sec_channel_offset;
++	conf->op_class = params->op_class;
+ 	if (params->center_freq1 &&
+ 	    ieee80211_freq_to_chan(params->center_freq1, &seg0) ==
+ 	    NUM_HOSTAPD_MODES)
+@@ -4624,6 +4628,7 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
+ 
+ 	settings->freq_params.channel = chan;
+ 
++	settings->freq_params.op_class = hapd->iface->cs_oper_class;
+ 	ret = hostapd_change_config_freq(iface->bss[0], iface->conf,
+ 					 &settings->freq_params,
+ 					 &old_freq);
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index f3de2b3bc..829274bfe 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -907,6 +907,11 @@ struct hostapd_freq_params {
+ 	 * link_id: If >=0 indicates the link of the AP MLD to configure
+ 	 */
+ 	int link_id;
++
++	/**
++	 * op_class: Operating class of the channel
++	 */
++	u8 op_class;
+ };
+ 
+ /**
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0123-mtk-hostapd-add-puncture-bitmap-to-ucode.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0123-mtk-hostapd-add-puncture-bitmap-to-ucode.patch
new file mode 100644
index 0000000..e480428
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0123-mtk-hostapd-add-puncture-bitmap-to-ucode.patch
@@ -0,0 +1,74 @@
+From f046d62580187077b4d75c3e5f183a8ad8ad9079 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 6 Aug 2024 10:11:22 +0800
+Subject: [PATCH 123/126] mtk: hostapd: add puncture bitmap to ucode
+
+Add puncture bitmap to ucode since fw might trigger channel switch due
+to pp bitmap change.
+Therefore, the changed pp bitmap should be synchronized to extender's AP
+when root AP changes its pp bitmap.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/ucode.c         | 4 ++++
+ src/utils/ucode.c      | 2 ++
+ wpa_supplicant/ucode.c | 1 +
+ 3 files changed, 7 insertions(+)
+
+diff --git a/src/ap/ucode.c b/src/ap/ucode.c
+index 8c05404f5..0c6f4043c 100644
+--- a/src/ap/ucode.c
++++ b/src/ap/ucode.c
+@@ -726,6 +726,8 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 		csa.freq_params.center_freq1 = intval;
+ 	if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
+ 		csa.freq_params.center_freq2 = intval;
++	if ((intval = ucv_int64_get(ucv_object_get(info, "punct_bitmap", NULL))) && !errno)
++		csa.freq_params.punct_bitmap = intval;
+ 
+ 	intval = ucv_int64_get(ucv_object_get(info, "band_idx", NULL));
+ 	band_idx = errno ? iface->conf->band_idx : intval;
+@@ -740,6 +742,8 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+ 			csa.freq_params.center_freq1);
+ 	wpa_printf(MSG_INFO, "    * center_freq2 is %d\n",
+ 			csa.freq_params.center_freq2);
++	wpa_printf(MSG_INFO, "    * punct_bitmap is %d\n",
++			csa.freq_params.punct_bitmap);
+ 	wpa_printf(MSG_INFO, "    * band_idx is %d\n",
+ 			band_idx);
+ 
+diff --git a/src/utils/ucode.c b/src/utils/ucode.c
+index 8bbbbbeff..c688d9bee 100644
+--- a/src/utils/ucode.c
++++ b/src/utils/ucode.c
+@@ -110,6 +110,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 	uc_value_t *freq = uc_fn_arg(0);
+ 	uc_value_t *sec = uc_fn_arg(1);
+ 	int width = ucv_uint64_get(uc_fn_arg(2));
++	int punct_bitmap = ucv_uint64_get(uc_fn_arg(5));
+ 	int bw320_offset = 1, band_idx;
+ 	int freq_val, center_idx, center_ofs;
+ 	enum oper_chan_width chanwidth;
+@@ -185,6 +186,7 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+ 	ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
+ 	ucv_object_add(ret, "oper_chwidth", ucv_int64_new(chanwidth));
+ 	ucv_object_add(ret, "band_idx", ucv_int64_new(band_idx));
++	ucv_object_add(ret, "punct_bitmap", ucv_int64_new(punct_bitmap));
+ 
+ 	if (chanwidth == CONF_OPER_CHWIDTH_USE_HT) {
+ 		center_idx = freq_val < 3000 ? 0 : channel;
+diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
+index 450780737..124038def 100644
+--- a/wpa_supplicant/ucode.c
++++ b/wpa_supplicant/ucode.c
+@@ -178,6 +178,7 @@ void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_d
+ 		ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
+ 		ucv_object_add(val, "ch_width", ucv_int64_new(oper_chwidth));
+ 		ucv_object_add(val, "bw320_offset", ucv_int64_new(bw320_offset));
++		ucv_object_add(val, "punct_bitmap", ucv_int64_new(data->ch_switch.punct_bitmap));
+ 		ucv_object_add(val, "band_idx", ucv_int64_new(band_idx));
+ 	}
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0124-mtk-hostapd-Add-AFC-and-lpi-driver-power-support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0124-mtk-hostapd-Add-AFC-and-lpi-driver-power-support.patch
new file mode 100644
index 0000000..357b17e
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0124-mtk-hostapd-Add-AFC-and-lpi-driver-power-support.patch
@@ -0,0 +1,832 @@
+From 330346c577b4623e52767ad0939cd735a83d9837 Mon Sep 17 00:00:00 2001
+From: Allen Ye <allen.ye@mediatek.com>
+Date: Mon, 22 Jul 2024 20:51:18 +0800
+Subject: [PATCH 124/126] mtk: hostapd: Add AFC and lpi driver power support
+
+Add AFC and lpi driver power support
+This patch parse the AFC response into mtk sku power table format and send
+it to driver by vendor cmd. The table format is like below:
+col\row	 bw20  bw40  ...  ru26  ...  ru3472
+chan 1
+chan 5
+...
+chan 233
+
+ - Once the afc procedure start failed, the device would set as lpi mode by
+telling driver lpi_sku_index to use specify sku-index in dst.
+ - Add afc_max_timeout conf is use to limit the maximum interval between the
+two afc requests.
+ - Set default use lpi & sp mode
+
+Signed-off-by: Allen Ye <allen.ye@mediatek.com>
+---
+ hostapd/config_file.c        |   4 +
+ src/ap/afc.c                 | 365 +++++++++++++++++++++++++++++++++--
+ src/ap/ap_config.c           |   3 +-
+ src/ap/ap_config.h           |   2 +
+ src/ap/ap_drv_ops.c          |  34 +++-
+ src/ap/hostapd.h             |  60 ++++++
+ src/common/mtk_vendor.h      |   2 +
+ src/drivers/driver.h         |   4 +-
+ src/drivers/driver_nl80211.c |  36 +++-
+ 9 files changed, 477 insertions(+), 33 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index 944669270..11c5f8947 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4248,6 +4248,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 	} else if (os_strcmp(buf, "afc_op_class") == 0) {
+ 		if (hostapd_afc_parse_op_class(conf, pos))
+ 			return 1;
++	} else if (os_strcmp(buf, "afc_max_timeout") == 0) {
++		conf->afc.max_timeout = atoi(pos);
+ #endif /* CONFIG_AFC */
+ 	} else if (os_strcmp(buf, "mbssid") == 0) {
+ 		int mbssid = atoi(pos);
+@@ -5575,6 +5577,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 		conf->lpi_psd = !!en;
+ 	} else if (os_strcmp(buf, "sku_idx") == 0) {
+ 		conf->sku_idx = strtol(pos, NULL, 10);
++	} else if (os_strcmp(buf, "lpi_sku_idx") == 0) {
++		conf->lpi_sku_idx = strtol(pos, NULL, 10);
+ 	} else if (os_strcmp(buf, "lpi_bcn_enhance") == 0) {
+ 		u8 en = strtol(pos, NULL, 10);
+ 		conf->lpi_bcn_enhance = !!en;
+diff --git a/src/ap/afc.c b/src/ap/afc.c
+index 361ecb575..d36ce00d7 100644
+--- a/src/ap/afc.c
++++ b/src/ap/afc.c
+@@ -16,6 +16,7 @@
+ #include "hostapd.h"
+ #include "acs.h"
+ #include "hw_features.h"
++#include "ap_drv_ops.h"
+ 
+ #define HOSTAPD_AFC_RETRY_TIMEOUT	180
+ #define HOSTAPD_AFC_TIMEOUT		86400 /* 24h */
+@@ -749,6 +750,9 @@ static int hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply)
+ 	iface->afc.timeout = request_timeout;
+ 	if (iface->afc.timeout < 0)
+ 		iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
++	else if (iface->afc.timeout > iconf->afc.max_timeout &&
++		 iconf->afc.max_timeout >= HOSTAPD_AFC_RETRY_TIMEOUT)
++		iface->afc.timeout = iconf->afc.max_timeout;
+ 
+ 	return ret;
+ }
+@@ -772,6 +776,7 @@ static int hostapd_afc_send_receive(struct hostapd_iface *iface)
+ 	int sockfd, ret;
+ 	fd_set read_set;
+ 
++	iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
+ 	if (iface->afc.data_valid) {
+ 		/* AFC data already downloaded from the server */
+ 		return 0;
+@@ -782,7 +787,6 @@ static int hostapd_afc_send_receive(struct hostapd_iface *iface)
+ 		return -EINVAL;
+ 	}
+ 
+-	iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
+ 	if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) {
+ 		wpa_printf(MSG_ERROR, "Malformed AFC socket string %s",
+ 			   iconf->afc.socket);
+@@ -880,7 +884,19 @@ static bool hostapd_afc_has_usable_chans(struct hostapd_iface *iface)
+ int hostapd_afc_handle_request(struct hostapd_iface *iface)
+ {
+ 	struct hostapd_config *iconf = iface->conf;
++	bool lpi_mode;
+ 	int ret;
++	int afc_status = AFC_CONTINUE;
++
++	lpi_mode = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type);
++	if (lpi_mode && !he_reg_is_sp(iconf->he_6ghz_reg_pwr_type)) {
++		iface->afc.lpi_mode = true;
++		return 1;
++	}
++
++	if (strncmp(iconf->country, "US", 2) != 0 &&
++	    strncmp(iconf->country, "CA", 2) != 0)
++		return 1;
+ 
+ 	/* AFC is required just for standard power AP */
+ 	if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
+@@ -894,34 +910,50 @@ int hostapd_afc_handle_request(struct hostapd_iface *iface)
+ 
+ 	ret = hostapd_afc_send_receive(iface);
+ 	if (ret < 0) {
+-		/*
+-		 * If the connection to the AFCD failed, resched for a
+-		 * future attempt.
+-		 */
+-		wpa_printf(MSG_ERROR, "AFC connection failed: %d", ret);
+-		if (ret == -EIO)
+-			ret = 0;
++		afc_status = lpi_mode ? AFC_LPI : AFC_DISABLE;
+ 		goto resched;
+ 	}
+ 
+ 	hostap_afc_disable_channels(iface);
+-	if (!hostapd_afc_has_usable_chans(iface))
++	if (!hostapd_afc_has_usable_chans(iface)) {
++		afc_status = lpi_mode ? AFC_LPI : AFC_DISABLE;
+ 		goto resched;
++	}
+ 
+ 	if (!hostapd_is_usable_chans(iface)) {
+ 		/* Trigger an ACS freq scan */
++		afc_status = AFC_RESTART_IFACE;
+ 		iconf->channel = 0;
+ 		iface->freq = 0;
+ 
+ 		if (acs_init(iface) != HOSTAPD_CHAN_ACS) {
+ 			wpa_printf(MSG_ERROR, "Could not start ACS");
++			afc_status = AFC_DISABLE;
+ 			ret = -EINVAL;
+ 		}
+ 	} else {
++		afc_status = AFC_CONTINUE;
+ 		ret = 1;
+ 	}
+ 
+ resched:
++	switch(afc_status) {
++	case AFC_LPI:
++		iface->afc.lpi_mode = true;
++		hostapd_afc_enable_lpi_channels(iface);
++		ret = 1;
++		break;
++	/* Disable and restart iface would be finished in hostapd setup flow. */
++	case AFC_RESTART_IFACE:
++		ret = 0;
++		fallthrough;
++	case AFC_DISABLE:
++	case AFC_CONTINUE:
++		break;
++	default:
++		break;
++	}
++
+ 	eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
+ 	eloop_register_timeout(iface->afc.timeout, 0,
+ 			       hostapd_afc_timeout_handler, iface, NULL);
+@@ -948,34 +980,58 @@ static void hostapd_afc_delete_data_from_server(struct hostapd_iface *iface)
+ static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx)
+ {
+ 	struct hostapd_iface *iface = eloop_ctx;
+-	bool restart_iface = true;
++	bool lpi_mode;
++	int afc_status = AFC_CONTINUE, ret;
++
++	lpi_mode = he_reg_is_indoor(iface->conf->he_6ghz_reg_pwr_type);
++	iface->afc.lpi_mode = false;
+ 
+ 	hostapd_afc_delete_data_from_server(iface);
+ 	if (iface->state != HAPD_IFACE_ENABLED) {
++		afc_status = AFC_RESTART_IFACE;
+ 		/* Hostapd is not fully enabled yet, toggle the interface */
+ 		goto restart_interface;
+ 	}
+ 
+ 	if (hostapd_afc_send_receive(iface) < 0 ||
+ 	    hostapd_get_hw_features(iface)) {
+-		restart_iface = false;
++		afc_status = lpi_mode ? AFC_LPI : AFC_DISABLE;
+ 		goto restart_interface;
+ 	}
+ 
+-	if (hostapd_is_usable_chans(iface))
+-		goto resched;
++	ret = hostapd_is_usable_chans(iface);
++	if (ret != 1) {
++		afc_status = lpi_mode && ret == 0 ? AFC_LPI : AFC_DISABLE;
++		goto restart_interface;
++	}
+ 
+-	restart_iface = hostapd_afc_has_usable_chans(iface);
+-	if (restart_iface) {
++	ret = hostapd_afc_has_usable_chans(iface);
++	if (ret) {
+ 		/* Trigger an ACS freq scan */
++		afc_status = AFC_RESTART_IFACE;
+ 		iface->conf->channel = 0;
+ 		iface->freq = 0;
+ 	}
+ 
+ restart_interface:
+-	hostapd_disable_iface(iface);
+-	if (restart_iface)
++	switch(afc_status) {
++	case AFC_DISABLE:
++		hostapd_disable_iface(iface);
++		break;
++	case AFC_RESTART_IFACE:
++		hostapd_disable_iface(iface);
+ 		hostapd_enable_iface(iface);
++		break;
++	case AFC_LPI:
++		iface->afc.lpi_mode = true;
++		hostapd_afc_enable_lpi_channels(iface);
++		hostapd_drv_txpower_ctrl(iface->bss[0]);
++		break;
++	case AFC_CONTINUE:
++		break;
++	default:
++		break;
++	}
+ resched:
+ 	eloop_register_timeout(iface->afc.timeout, 0,
+ 			       hostapd_afc_timeout_handler, iface, NULL);
+@@ -1040,6 +1096,37 @@ void hostap_afc_disable_channels(struct hostapd_iface *iface)
+ 	}
+ }
+ 
++void hostapd_afc_enable_lpi_channels(struct hostapd_iface *iface)
++{
++	struct hostapd_hw_modes *mode = NULL;
++	int i;
++
++	for (i = 0; i < iface->num_hw_features; i++) {
++		mode = &iface->hw_features[i];
++		if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
++		    mode->is_6ghz)
++			break;
++	}
++
++	if (i == iface->num_hw_features)
++		return;
++
++	if (!he_reg_is_indoor(iface->conf->he_6ghz_reg_pwr_type))
++		return;
++
++	for (i = 0; i < mode->num_channels; i++) {
++		struct hostapd_channel_data *chan = &mode->channels[i];
++
++		if (!is_6ghz_freq(chan->freq))
++			continue;
++
++		chan->flag &= ~HOSTAPD_CHAN_DISABLED;
++		wpa_printf(MSG_MSGDUMP,
++			   "Enabling freq=%d MHz for lpi mode",
++			   chan->freq);
++	}
++}
++
+ 
+ int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
+ 				       int *power)
+@@ -1076,3 +1163,247 @@ int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
+ 	}
+ 	return -EINVAL;
+ }
++
++void hostapd_afc_init_power_table(s8 ***power_table)
++{
++	int table_idx, bw;
++	s8 *chan_power_list;
++
++	/* init power table */
++	for (table_idx = 0; table_idx < MAX_CHANNEL_NUM_6G; table_idx++) {
++		chan_power_list = (*power_table)[table_idx];
++		for (bw = 0; bw < afc_power_table_num; bw++)
++			chan_power_list[bw] = AFC_INVALID_POWER;
++	}
++}
++
++int hostapd_afc_parse_psd_to_dbm(struct hostapd_iface *iface, s8 ***power_table)
++{
++	int i, freq, channel, bw, table_idx, target_power;
++	s8 *chan_power_list;
++
++	for (i = 0; i < iface->afc.num_freq_range; i++) {
++		struct afc_freq_range_elem *freq_range = &iface->afc.freq_range[i];
++
++		if (!freq_range)
++			continue;
++
++		freq = freq_range->low_freq + 10;
++		channel = hostapd_hw_get_channel(iface->bss[0], freq);
++		if (channel == 0)
++			return -EINVAL;
++
++		table_idx = channel / 4;
++
++		if (table_idx >= MAX_CHANNEL_NUM_6G)
++			return -EINVAL;
++
++		chan_power_list = (*power_table)[table_idx];
++		for (bw = 0; bw < afc_power_bw320_2; bw++) {
++			target_power = freq_range->max_psd * 2 + PSD_TO_DBM_OFFSET +
++				bw * DOUBLE_BW_POWER;
++			target_power = MIN(AFC_MAXIMUM_POWER, target_power);
++			chan_power_list[bw] = MIN(chan_power_list[bw],
++						  target_power);
++		}
++		chan_power_list[afc_power_bw320_2] = chan_power_list[afc_power_bw320_1];
++	}
++	return 0;
++}
++
++int hostapd_afc_parse_eirp_to_dbm(struct hostapd_iface *iface, s8 ***power_table)
++{
++	int i, bw, table_idx, target_power;
++	s8 *chan_power_list;
++
++	for (i = 0; i < iface->afc.num_chan_info; i++) {
++		struct afc_chan_info_elem *chan_info = &iface->afc.chan_info_list[i];
++
++		if (!chan_info)
++			continue;
++
++		table_idx = chan_info->chan / 4;
++
++		if (table_idx >= MAX_CHANNEL_NUM_6G)
++			return -EINVAL;
++
++		chan_power_list = (*power_table)[table_idx];
++		target_power = MIN(AFC_MAXIMUM_POWER, chan_info->power * 2);
++		/* FIXME: wider bandwidth power is not stored. */
++		chan_power_list[afc_power_bw20] = MIN(chan_power_list[afc_power_bw20],
++						      target_power);
++	}
++	return 0;
++}
++
++int afc_get_ru_be_offset(int bw, int *target_bw, int *offset)
++{
++	switch (bw) {
++	case afc_power_ru26:
++		*target_bw = afc_power_bw20;
++		*offset = RU26_OFFSET_20MHZ;
++		break;
++	case afc_power_ru52:
++		*target_bw = afc_power_bw20;
++		*offset = RU52_OFFSET_20MHZ;
++		break;
++	case afc_power_ru78:
++		*target_bw = afc_power_bw20;
++		*offset = RU78_OFFSET_20MHZ;
++		break;
++	case afc_power_ru106:
++		*target_bw = afc_power_bw20;
++		*offset = RU106_OFFSET_20MHZ;
++		break;
++	case afc_power_ru132:
++		*target_bw = afc_power_bw20;
++		*offset = RU132_OFFSET_20MHZ;
++		break;
++	case afc_power_ru726:
++		*target_bw = afc_power_bw80;
++		*offset = RU726_OFFSET_80MHZ;
++		break;
++	case afc_power_ru1480:
++		*target_bw = afc_power_bw160;
++		*offset = RU1480_OFFSET_160MHZ;
++		break;
++	case afc_power_ru1772:
++		*target_bw = afc_power_bw160;
++		*offset = RU1772_OFFSET_160MHZ;
++		break;
++	case afc_power_ru2476:
++		*target_bw = afc_power_bw320_1;
++		*offset = RU2476_OFFSET_320MHZ;
++		break;
++	case afc_power_ru2988:
++		*target_bw = afc_power_bw320_1;
++		*offset = RU2988_OFFSET_320MHZ;
++		break;
++	case afc_power_ru3472:
++		*target_bw = afc_power_bw320_1;
++		*offset = RU3472_OFFSET_320MHZ;
++		break;
++	default:
++		return -EINVAL;
++	}
++	return 0;
++}
++
++int hostapd_afc_fill_wide_bandwidth_power(s8 ***power_table)
++{
++	int table_idx, bw;
++	s8 *chan_power_list;
++
++	for (table_idx = 0; table_idx < MAX_CHANNEL_NUM_6G; table_idx++) {
++		int target_power, ru26_power;
++
++		if ((*power_table)[table_idx][afc_power_bw20] == AFC_INVALID_POWER)
++			continue;
++
++		chan_power_list = (*power_table)[table_idx];
++
++		/* Check wide bandwidth power minimum or valid. */
++		for (bw = afc_power_bw40; bw <= afc_power_bw320_2; bw++) {
++			int bw_ch_num, first_ch, last_ch;
++
++			target_power = AFC_INVALID_POWER;
++			switch (bw) {
++			case afc_power_bw40:
++				bw_ch_num = 2;
++				break;
++			case afc_power_bw80:
++				bw_ch_num = 4;
++				break;
++			case afc_power_bw160:
++				bw_ch_num = 8;
++				break;
++			case afc_power_bw320_1:
++			case afc_power_bw320_2:
++				bw_ch_num = 16;
++				break;
++			}
++			if ((bw == afc_power_bw320_1 && table_idx > 47) ||
++			    (bw == afc_power_bw320_2 && table_idx < 8))
++				continue;
++
++			if (bw == afc_power_bw320_2)
++				first_ch = table_idx - (table_idx + 8) % bw_ch_num;
++			else
++				first_ch = table_idx - table_idx % bw_ch_num;
++			last_ch = first_ch + bw_ch_num;
++			for (int ch = first_ch; ch < last_ch; ch++) {
++				if ((*power_table)[ch][bw] == AFC_INVALID_POWER) {
++					target_power = AFC_INVALID_POWER;
++					break;
++				}
++				target_power = MIN((*power_table)[ch][bw], target_power);
++			}
++			chan_power_list[bw] = target_power;
++		}
++
++		/* Update remain ru */
++		for (bw = afc_power_ru26; bw < afc_power_table_num; bw++) {
++			int target_bw, offset;
++
++			if (afc_get_ru_be_offset(bw, &target_bw, &offset))
++				return -EINVAL;
++
++			if (target_bw == afc_power_bw320_1 &&
++			    chan_power_list[target_bw] == AFC_INVALID_POWER)
++			    target_bw++;
++
++			if (chan_power_list[target_bw] == AFC_INVALID_POWER) {
++				chan_power_list[bw] = AFC_INVALID_POWER;
++				continue;
++			}
++
++			target_power = chan_power_list[target_bw] - offset;
++			chan_power_list[bw] = target_power;
++		}
++	}
++	return 0;
++}
++
++
++int hostapd_afc_translate_table(struct hostapd_iface *iface,
++				s8 ***power_table)
++{
++	int i, ret, bw320_offset;
++
++	if (!iface->afc.data_valid)
++		return -EINVAL;
++
++	*power_table = (s8**)os_zalloc(MAX_CHANNEL_NUM_6G * sizeof(s8*));
++
++	if (!(*power_table))
++		return -ENOMEM;
++
++	for (i = 0; i < MAX_CHANNEL_NUM_6G; i++) {
++		(*power_table)[i] = (s8*)os_zalloc(afc_power_table_num * sizeof(s8));
++		if (!(*power_table)[i])
++			goto out;
++	}
++
++	hostapd_afc_init_power_table(power_table);
++
++	ret = hostapd_afc_parse_psd_to_dbm(iface, power_table);
++	if (ret)
++		goto out;
++
++	ret = hostapd_afc_parse_eirp_to_dbm(iface, power_table);
++	if (ret)
++		goto out;
++
++	ret = hostapd_afc_fill_wide_bandwidth_power(power_table);
++	if (ret)
++		goto out;
++
++	return 0;
++out:
++	for (i = 0; i < MAX_CHANNEL_NUM_6G; i++)
++		os_free((*power_table)[i]);
++
++	os_free(*power_table);
++	power_table = NULL;
++	return -ENOMEM;
++}
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index bb5ec78a1..185dae089 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -288,7 +288,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->he_6ghz_reg_pwr_type = HE_REG_INFO_6GHZ_AP_TYPE_VLP;
++	conf->he_6ghz_reg_pwr_type = HE_REG_INFO_6GHZ_AP_TYPE_INDOOR;
+ 	conf->reg_def_cli_eirp_psd = -1;
+ 	conf->reg_sub_cli_eirp_psd = -1;
+ 	conf->reg_def_cli_eirp = -1;
+@@ -317,6 +317,7 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 
+ 	conf->lpi_psd = 0;
+ 	conf->sku_idx = 0;
++	conf->lpi_sku_idx = 0;
+ 	conf->lpi_bcn_enhance = 0;
+ 
+ 	hostapd_set_and_check_bw320_offset(conf, 0);
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index ea8507cea..966a02d6a 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1343,6 +1343,7 @@ struct hostapd_config {
+ 		unsigned int n_op_class;
+ 		unsigned int *op_class;
+ 		int min_power;
++		int max_timeout;
+ 	} afc;
+ #endif /* CONFIG_AFC */
+ 
+@@ -1358,6 +1359,7 @@ struct hostapd_config {
+ 	u8 band_idx;
+ 	u8 lpi_psd;
+ 	u8 sku_idx;
++	u8 lpi_sku_idx;
+ 	u8 lpi_bcn_enhance;
+ };
+ 
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 64fd0c04c..bd710832a 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1397,7 +1397,8 @@ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_colo
+ 
+ int hostapd_drv_txpower_ctrl(struct hostapd_data *hapd)
+ {
+-	s8 link_id = -1;
++	s8 link_id = -1, sku_idx = hapd->iconf->sku_idx, ret = 0, i;
++	s8 **afc_power_table = NULL;
+ 
+ 	if (!hapd->driver || !hapd->driver->txpower_ctrl)
+ 		return 0;
+@@ -1405,10 +1406,33 @@ int hostapd_drv_txpower_ctrl(struct hostapd_data *hapd)
+ 	if (hapd->conf->mld_ap)
+ 		link_id = hapd->mld_link_id;
+ 
+-	return hapd->driver->txpower_ctrl(hapd->drv_priv, hapd->iconf->lpi_psd,
+-					  hapd->iconf->sku_idx,
+-					  hapd->iconf->lpi_bcn_enhance,
+-					  link_id);
++#ifdef CONFIG_AFC
++	if (hapd->iface->current_mode->is_6ghz &&
++	    he_reg_is_sp(hapd->iface->conf->he_6ghz_reg_pwr_type) &&
++	    !hapd->iface->afc.lpi_mode) {
++		ret = hostapd_afc_translate_table(hapd->iface, &afc_power_table);
++		if (ret)
++			goto out;
++	}
++
++	if (hapd->iface->afc.lpi_mode == true)
++		sku_idx = hapd->iconf->lpi_sku_idx;
++#endif /* CONFIG_AFC */
++
++	ret = hapd->driver->txpower_ctrl(hapd->drv_priv, hapd->iconf->lpi_psd,
++					 sku_idx,
++					 hapd->iconf->lpi_bcn_enhance,
++					 link_id,
++					 afc_power_table,
++					 hapd->iface->afc.lpi_mode);
++#ifdef CONFIG_AFC
++out:
++	if (afc_power_table)
++		for (i = 0; i < MAX_CHANNEL_NUM_6G; i++)
++			os_free(afc_power_table[i]);
++	os_free(afc_power_table);
++#endif /* CONFIG_AFC */
++	return ret;
+ }
+ 
+ int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
+diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
+index f69fa0062..0625bb762 100644
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -783,17 +783,71 @@ struct hostapd_iface {
+ 			int power;
+ 		} *chan_info_list;
+ 		bool data_valid;
++		bool lpi_mode;
+ 	} afc;
+ #endif /* CONFIG_AFC */
+ };
+ 
+ /* hostapd.c */
+ #ifdef CONFIG_AFC
++
++enum afc_state {
++	AFC_DISABLE,
++	AFC_RESTART_IFACE,
++	AFC_LPI,
++	AFC_CONTINUE,
++};
++
++#define MAX_CHANNEL_NUM_6G 59
++
++/* The power unit is 0.5 dBm */
++#define AFC_MAXIMUM_POWER 72
++#define AFC_INVALID_POWER 127
++#define PSD_TO_DBM_OFFSET 26
++#define BW20_TO_RU26_OFFSET 20
++#define DOUBLE_BW_POWER 6
++
++#define RU26_OFFSET_20MHZ 20
++#define RU52_OFFSET_20MHZ 14
++#define RU78_OFFSET_20MHZ 10
++#define RU106_OFFSET_20MHZ 8
++#define RU132_OFFSET_20MHZ 6
++
++#define RU726_OFFSET_80MHZ 2
++#define RU1480_OFFSET_160MHZ 2
++#define RU1772_OFFSET_160MHZ 1
++#define RU2476_OFFSET_320MHZ 4
++#define RU2988_OFFSET_320MHZ 2
++#define RU3472_OFFSET_320MHZ 1
++
++enum afc_table_info {
++	afc_power_bw20,
++	afc_power_bw40,
++	afc_power_bw80,
++	afc_power_bw160,
++	afc_power_bw320_1,
++	afc_power_bw320_2,
++	afc_power_ru26,
++	afc_power_ru52,
++	afc_power_ru78,
++	afc_power_ru106,
++	afc_power_ru132,
++	afc_power_ru726,
++	afc_power_ru1480,
++	afc_power_ru1772,
++	afc_power_ru2476,
++	afc_power_ru2988,
++	afc_power_ru3472,
++	afc_power_table_num,
++};
++
+ int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
+ 				       int *power);
+ int hostapd_afc_handle_request(struct hostapd_iface *iface);
+ void hostapd_afc_stop(struct hostapd_iface *iface);
+ void hostap_afc_disable_channels(struct hostapd_iface *iface);
++int hostapd_afc_translate_table(struct hostapd_iface *iface,
++				s8 ***power_table);
+ #else
+ static inline int
+ hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
+@@ -814,6 +868,12 @@ static inline void hostapd_afc_stop(struct hostapd_iface *iface)
+ static inline void hostap_afc_disable_channels(struct hostapd_iface *iface)
+ {
+ }
++
++int hostapd_afc_translate_table(struct hostapd_iface *iface,
++				 s8 ***power_table)
++{
++	return -EINVAL;
++}
+ #endif /* CONFIG_AFC */
+ 
+ int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 1fe459126..4b900162b 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -319,6 +319,8 @@ enum mtk_vendor_attr_txpower_ctrl {
+ 	MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX,
+ 	MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE,
+ 	MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID,
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE,
++	MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL,
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 829274bfe..6aac87ce1 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -5481,9 +5481,11 @@ struct wpa_driver_ops {
+ 	* @lpi_bcn_enhance: 1 to enable beacon duplicate enhancement in 6G lpi mode, 0 to disable enhancement
+ 	* @sku_idx: index used to indicate which sku table should be used
+ 	* @link_id: MLD link id. -1 if this is an non-MLD AP
++	* @power_table: power table generated from AFC response
++	* @lpi_mode: specify the current mode is whether lpi
+ 	*/
+ 	int (*txpower_ctrl)(void *priv, u8 lpi_psd, u8 sku_idx, u8 lpi_bcn_enhance,
+-			    u8 link_id);
++			    u8 link_id, s8 **power_table, u8 lpi_mode);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index d2064fbd6..efee210b9 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -41,6 +41,7 @@
+ #include "driver_nl80211.h"
+ #include "common/mtk_vendor.h"
+ #include "ap/ap_config.h"
++#include "ap/hostapd.h"
+ 
+ #ifdef CONFIG_IEEE80211BE
+ #include "ap/scs.h"
+@@ -205,6 +206,8 @@ txpower_ctrl_policy[NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL] = {
+ 	[MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX] = { .type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE] = { .type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE] = { .type = NLA_BINARY },
++	[MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI] = { .type = NLA_U8 },
+ };
+ 
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+@@ -15662,38 +15665,53 @@ fail:
+ }
+ 
+ static int nl80211_txpower_ctrl(void *priv, u8 lpi_psd, u8 sku_idx, u8 lpi_bcn_enhance,
+-				u8 link_id)
++				u8 link_id, s8 **power_table, u8 lpi_mode)
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+ 	struct nl_msg *msg;
+ 	struct nlattr *data;
+-	int ret;
++	struct nlattr *table_attr, *channel_list;
++	int ret = 0;
+ 
+ 	if (!drv->mtk_txpower_vendor_cmd_avail) {
+ 		wpa_printf(MSG_INFO,
+ 			   "nl80211: Driver does not support setting txpower control");
+-		return 0;
++		goto fail;
+ 	}
+ 
+ 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+-	if (!msg)
++	if (!msg) {
++		ret = -ENOBUFS;
+ 		goto fail;
++	}
+ 
+ 	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
+ 		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+-			    MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL))
++			    MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL)) {
++		ret = -ENOBUFS;
+ 		goto fail;
++	}
+ 
+-	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+-	if (!data)
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
++	if (!data) {
++		ret = -ENOBUFS;
+ 		goto fail;
++	}
+ 
+ 	nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_PSD, lpi_psd);
+ 	nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX, sku_idx);
+ 	nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE, lpi_bcn_enhance);
+-	nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID, link_id);
+ 
++	if (link_id > -1)
++		nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID, link_id);
++
++	if (power_table && *power_table) {
++		nla_put(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE,
++		        MAX_CHANNEL_NUM_6G * afc_power_table_num, power_table);
++	}
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI, lpi_mode);
+ 	nla_nest_end(msg, data);
+ 	ret = send_and_recv_cmd(drv, msg);
+ 	if (ret)
+@@ -15704,7 +15722,7 @@ static int nl80211_txpower_ctrl(void *priv, u8 lpi_psd, u8 sku_idx, u8 lpi_bcn_e
+ 
+ fail:
+ 	nlmsg_free(msg);
+-	return -ENOBUFS;
++	return ret;
+ }
+ 
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0125-mtk-hostapd-do-not-set-secondary-channel-in-HT-opera.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0125-mtk-hostapd-do-not-set-secondary-channel-in-HT-opera.patch
new file mode 100644
index 0000000..8c24366
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0125-mtk-hostapd-do-not-set-secondary-channel-in-HT-opera.patch
@@ -0,0 +1,91 @@
+From 35e67264aef9b6f050e01465ff419495198ad110 Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee@mediatek.com>
+Date: Wed, 14 Aug 2024 17:42:00 +0800
+Subject: [PATCH 125/126] mtk: hostapd: do not set secondary channel in HT
+ operation if punctured
+
+If the secondary channel is punctured, the HT operation in the beacon
+should not indicate a secondary channel offset
+(HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE or HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+
+Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
+---
+ src/ap/ieee802_11_ht.c | 53 ++++++++++++++++++++++++++++++++++++------
+ 1 file changed, 46 insertions(+), 7 deletions(-)
+
+diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
+index 7f0a00f95..1b323819e 100644
+--- a/src/ap/ieee802_11_ht.c
++++ b/src/ap/ieee802_11_ht.c
+@@ -79,6 +79,49 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
+ }
+ 
+ 
++void set_ht_param(struct hostapd_data *hapd, struct ieee80211_ht_operation *oper,
++		  u8 chwidth)
++{
++	int secondary_channel = hapd->iconf->secondary_channel;
++#ifdef CONFIG_IEEE80211BE
++	u8 offset, chan_bit_pos;
++	u16 bw = 0, punct_bitmap = hostapd_get_punct_bitmap(hapd);
++
++	switch (chwidth) {
++	case CONF_OPER_CHWIDTH_80MHZ:
++		bw = 80;
++		offset = 6;
++		break;
++	case CONF_OPER_CHWIDTH_160MHZ:
++		bw = 160;
++		offset = 14;
++		break;
++	case CONF_OPER_CHWIDTH_320MHZ:
++		bw = 320;
++		offset = 30;
++		break;
++	default:
++		break;
++	}
++
++	chan_bit_pos = (hapd->iconf->channel -
++			hostapd_get_oper_centr_freq_seg0_idx(hapd->iconf) +
++			offset) / 4;
++	/* check if secondary channel is punctured */
++	if (bw >= 80 && punct_bitmap && secondary_channel &&
++	    (punct_bitmap & BIT(chan_bit_pos + secondary_channel)))
++		return;
++#endif /* CONFIG_IEEE80211BE */
++
++	if (secondary_channel == 1)
++		oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE |
++			HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
++	if (secondary_channel == -1)
++		oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
++			HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
++}
++
++
+ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
+ {
+ 	struct ieee80211_ht_operation *oper;
+@@ -98,15 +141,11 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
+ 
+ 	oper->primary_chan = hapd->iconf->channel;
+ 	oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode);
+-	if (hapd->iconf->secondary_channel == 1)
+-		oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE |
+-			HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
+-	if (hapd->iconf->secondary_channel == -1)
+-		oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
+-			HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
+ 
+-	vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
+ 	chwidth = hostapd_get_oper_chwidth(hapd->iconf);
++	set_ht_param(hapd, oper, chwidth);
++
++	vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
+ 	if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT
+ 		&& ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
+ 		oper->operation_mode = host_to_le16(hapd->iconf->vht_oper_centr_freq_seg0_idx << 5);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0126-mtk-hostapd-Fix-hostapd-crash-in-wds-mode-during-fre.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0126-mtk-hostapd-Fix-hostapd-crash-in-wds-mode-during-fre.patch
new file mode 100644
index 0000000..cf9dd47
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/0126-mtk-hostapd-Fix-hostapd-crash-in-wds-mode-during-fre.patch
@@ -0,0 +1,40 @@
+From 6207a7d1f0601a12be9639aa3507faf10b84398a Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 15 Aug 2024 18:50:41 +0800
+Subject: [PATCH 126/126] mtk: hostapd: Fix hostapd crash in wds mode during
+ freeing sta
+
+When freeing sta in wds mode, ap_free_sta will pass NULL pointer (ifname_wds)
+to hostapd_set_wds_sta.
+The ifname_wds will directly assigned to name and being used without
+checking whether it is NULL or not in i802_set_wds_sta.
+The following hostapd patch of the openwrt trunk leads to this issue.
+https://github.com/openwrt/openwrt/commit/e80520197c9ca7bced50d3605d6baba6dead6e35
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/drivers/driver_nl80211.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index efee210b9..a08afb5d4 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -8603,10 +8603,13 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
+ {
+ 	struct i802_bss *bss = priv;
+ 	struct wpa_driver_nl80211_data *drv = bss->drv;
+-	const char *name = ifname_wds; // Kept to reduce changes to the minimum
++	char name[IFNAMSIZ + 1];
+ 	union wpa_event_data event;
+ 	int ret;
+ 
++	if (ifname_wds)
++		os_strlcpy(name, ifname_wds, IFNAMSIZ + 1);
++
+ 	wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
+ 		   " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
+ 	if (val) {
+-- 
+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 d61727b..dec3493 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
@@ -1,107 +1,129 @@
 #patch patches (come from openwrt/lede/target/linux/mediatek)
 SRC_URI_append = " \
-    file://0001-hostapd-MLO-fix-for_each_mld_link-macro.patch \
-    file://0002-hostapd-MLO-frame-link-add-command-on-per-BSS-basis.patch \
-    file://0003-nl80211-Print-the-interface-name-in-debug-during-lin.patch \
-    file://0004-hostapd-MLO-send-link_id-on-sta_deauth.patch \
-    file://0005-hostapd-MLO-handle-auth-assoc-on-link-address.patch \
-    file://0006-hostapd-MLO-reset-auth-state-machine-s-ML-info.patch \
-    file://0007-hostapd-MLO-add-support-for-cohosted-ML-BSS.patch \
-    file://0008-hostapd-MLO-extend-support-for-cohosted-ML-BSS.patch \
-    file://0009-hostapd-MLO-pass-link_id-in-get_hapd_bssid-helper-fu.patch \
-    file://0010-hostapd-MLO-pass-ctx-in-mlme_event_mgmt.patch \
-    file://0011-hostapd-MLO-move-mgmt-and-control-port-Tx-status-to-.patch \
-    file://0012-hostapd-make-hostapd_eapol_tx_status-function-static.patch \
-    file://0013-hostapd-MLO-handle-link_id-in-EAPOL-Tx-status-handle.patch \
-    file://0014-hostapd-MLO-update-all-partner-s-link-beacon.patch \
-    file://0015-hostapd-MLO-skip-assoc-link-processing-in-ML-info.patch \
-    file://0016-hostapd-MLO-Enhance-wpa-state-machine.patch \
-    file://0017-hostapd-MLO-add-support-for-MLO-rekey.patch \
-    file://0018-hostapd-MLO-send-link-id-during-flushing-stations.patch \
-    file://0019-hostapd-MLO-display-link-details-in-status-command.patch \
-    file://0020-hostapd-fix-RNR-building-for-co-location-and-MLO.patch \
-    file://0021-tests-MLO-add-basic-cohosted-MLDs-functionality-test.patch \
-    file://0022-tests-MLO-add-cohosted-MLDs-connectivity-testing.patch \
-    file://0023-backport-hostapd-afcd-add-AFC-daemon-support.patch \
-    file://0024-backport-hostapd-export-hostapd_is_usable_chans-util.patch \
-    file://0025-backport-hostapd-ap-add-AFC-client-support.patch \
-    file://0026-backport-hostapd-update-TPE-IE-according-to-AFC.patch \
-    file://0027-hostapd-sync-2024-01-18-openwrt-trunk-src-folder.patch \
-    file://0028-hostapd-sync-2024-01-18-openwrt-trunk-patch-folder.patch \
-    file://0029-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch \
-    file://0030-mtk-hostapd-print-sae-groups-by-hostapd-ctrl.patch \
-    file://0031-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch \
-    file://0032-mtk-hostapd-Add-mtk_vendor.h.patch \
-    file://0033-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch \
-    file://0034-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch \
-    file://0035-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch \
-    file://0036-mtk-hostapd-Add-hostapd-iBF-control.patch \
-    file://0037-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch \
-    file://0038-mtk-hostapd-Add-DFS-detection-mode.patch \
-    file://0039-mtk-hostapd-Add-DFS-offchan-channel-switch.patch \
-    file://0040-mtk-hostapd-Add-amsdu-set-get-ctrl.patch \
-    file://0041-mtk-hostapd-Add-he_ldpc-configuration.patch \
-    file://0042-mtk-hostapd-Add-vendor-command-attribute-for-RTS-BW-.patch \
-    file://0043-mtk-hostapd-6G-band-does-not-require-DFS.patch \
-    file://0044-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch \
-    file://0045-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch \
-    file://0046-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch \
-    file://0047-mtk-hostapd-Add-available-color-bitmap.patch \
-    file://0048-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch \
-    file://0049-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch \
-    file://0050-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch \
-    file://0051-mtk-hostapd-Add-muru-user-number-debug-command.patch \
-    file://0052-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch \
-    file://0053-mtk-hostapd-Add-HE-capabilities-check.patch \
-    file://0054-mtk-hostapd-Fix-background-channel-overlapping-opera.patch \
-    file://0055-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch \
-    file://0056-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch \
-    file://0057-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch \
-    file://0058-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch \
-    file://0059-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch \
-    file://0060-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch \
-    file://0061-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch \
-    file://0062-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch \
-    file://0063-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch \
-    file://0064-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch \
-    file://0065-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch \
-    file://0066-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch \
-    file://0067-mtk-hostapd-update-eht-operation-element.patch \
-    file://0068-mtk-hostapd-ucode-add-support-for-ucode-to-parse-BW3.patch \
-    file://0069-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch \
-    file://0070-mtk-hostapd-Add-support-for-updating-background-chan.patch \
-    file://0071-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem-hwits.patch \
-    file://0072-mtk-hostapd-add-support-enable-disable-preamble-punc.patch \
-    file://0073-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch \
-    file://0074-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch \
-    file://0075-hostapd-mtk-ACS-remove-chan-freq-list-check-when-sca.patch \
-    file://0076-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch \
-    file://0077-Revert-ACS-upstream-changes.patch \
-    file://0078-mtk-hostapd-ACS-Add-EHT320-and-HT40-support-fix-issu.patch \
-    file://0079-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch \
-    file://0080-mtk-hostapd-fix-mld_assoc_link_id.patch \
-    file://0081-mtk-wpa_s-correctly-get-assoc-frequency.patch \
-    file://0082-mtk-wpa_s-force-MLD-STA-to-use-SAE-H2E-during-authen.patch \
-    file://0083-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch \
-    file://0084-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch \
-    file://0085-mtk-wpa_s-fix-bss-selection-when-setting-mld_connect.patch \
-    file://0086-mtk-hostapd-add-mld_primary-option.patch \
-    file://0087-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch \
-    file://0088-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch \
-    file://0089-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch \
-    file://0090-mtk-hostapd-add-link-id-to-hostapd-cli-chan-switch.patch \
-    file://0091-mtk-wifi-hostapd-add-wds-mlo-support.patch \
-    file://0093-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch \
-    file://0094-mtk-hostapd-AP-MLD-specify-link-id-for-unicast-DEAUT.patch \
-    file://0095-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch \
-    file://0096-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch \
-    file://0097-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch \
-    file://0098-mtk-hostapd-add-connac3-csi-control-interface.patch \
-    file://0099-fixup-mtk-wifi-hostapd-add-wds-mlo-support.patch \
-    file://0100-mtk-hostapd-MLD-find-partner-links-by-BSSID-and-SSID.patch \
-    file://0101-mtk-hostapd-MLD-hostapd-add-support-for-basic-MLD-Ex.patch \
-    file://0102-mtk-hostapd-Refactor-static-PP-and-mld-support.patch \
-    file://0103-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch \
-    file://0104-mtk-hostapd-ucode-add-is_mld_finished-check.patch \
-    file://0105-mtk-hostapd-free-station-when-hapd-deinit.patch \
+    file://0001-ctrl_iface-create-link-based-hapd-control-sockets.patch \
+    file://0002-ctrl_iface-MLO-introduce-MLD-level-socket.patch \
+    file://0003-hostapd_cli-MLO-pass-LINKID-in-the-command.patch \
+    file://0004-hostapd_cli-MLO-add-status-command-for-MLD-socket.patch \
+    file://0005-hostapd-afcd-add-AFC-daemon-support.patch \
+    file://0006-hostapd-export-hostapd_is_usable_chans-utility-routi.patch \
+    file://0007-hostapd-ap-add-AFC-client-support.patch \
+    file://0008-hostapd-update-TPE-IE-according-to-AFC.patch \
+    file://0009-sync-2024-06-21-openwrt-trunk-src-folder.patch \
+    file://0010-sync-2024-08-13-openwrt-trunk-patches-folder.patch \
+    file://0011-mtk-hostapd-sync-with-wireless-next.git-include-uapi.patch \
+    file://0012-mtk-hostapd-Add-neighbor-report-and-BSS-Termination-.patch \
+    file://0013-mtk-hostapd-print-some-sae-info-by-hostapd-ctrl.patch \
+    file://0014-mtk-hostapd-add-support-for-runtime-set-in-band-disc.patch \
+    file://0015-mtk-hostapd-Add-mtk_vendor.h.patch \
+    file://0016-mtk-hostapd-Support-EDCCA-hostapd-configuration.patch \
+    file://0017-mtk-hostapd-Add-hostapd-MU-SET-GET-control.patch \
+    file://0018-mtk-hostapd-Add-three-wire-PTA-ctrl-hostapd-vendor-c.patch \
+    file://0019-mtk-hostapd-Add-hostapd-iBF-control.patch \
+    file://0020-mtk-hostapd-Do-not-include-HE-capab-IE-if-associated.patch \
+    file://0021-mtk-hostapd-Add-DFS-detection-mode.patch \
+    file://0022-mtk-hostapd-Add-DFS-offchan-channel-switch.patch \
+    file://0023-mtk-hostapd-Add-amsdu-set-get-ctrl.patch \
+    file://0024-mtk-hostapd-Add-he_ldpc-configuration.patch \
+    file://0025-mtk-hostapd-6G-band-does-not-require-DFS.patch \
+    file://0026-mtk-hostapd-Fix-sending-wrong-VHT-operation-IE-in-CS.patch \
+    file://0027-mtk-hostapd-Add-sta-assisted-DFS-state-update-mechan.patch \
+    file://0028-mtk-hostapd-Mark-DFS-channel-as-available-for-CSA.patch \
+    file://0029-mtk-hostapd-Add-available-color-bitmap.patch \
+    file://0030-mtk-hostapd-Fix-ZWDFS-issue-in-BW-160.patch \
+    file://0031-mtk-hostapd-Add-vendor-for-CAPI-certification-comman.patch \
+    file://0032-mtk-hostapd-Air-Monitor-support-in-hostapd-by-vendor.patch \
+    file://0033-mtk-hostapd-Add-muru-user-number-debug-command.patch \
+    file://0034-mtk-hostapd-add-connac3-PHY-MURU-manual-mode-config-.patch \
+    file://0035-mtk-hostapd-Add-HE-capabilities-check.patch \
+    file://0036-mtk-hostapd-Fix-background-channel-overlapping-opera.patch \
+    file://0037-mtk-hostapd-Fix-hostapd_dfs_start_cac-log.patch \
+    file://0038-mtk-hostapd-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch \
+    file://0039-mtk-hostapd-Update-parameter_set_count-in-MU-EDCA-IE.patch \
+    file://0040-mtk-hostapd-add-extension-IE-list-for-non-inherit-IE.patch \
+    file://0041-mtk-hostapd-add-back-ht-vht-cap-missing-field-before.patch \
+    file://0042-mtk-hostapd-Add-support-for-gtk-rekeying-in-hostapd-.patch \
+    file://0043-mtk-hostapd-Set-WMM-and-TX-queue-parameters-for-wpa_.patch \
+    file://0044-mtk-hostapd-Set-STA-TX-queue-parameters-configuratio.patch \
+    file://0045-mtk-hostapd-avoid-color-switch-when-beacon-is-not-se.patch \
+    file://0046-mtk-hostapd-6g-bss-connect-do-not-consider-ht-operat.patch \
+    file://0047-mtk-hostapd-Add-ACS-chanlist-info-in-get_config.patch \
+    file://0048-mtk-hostapd-Fix-RSNXE-Interop-issue-with-STA.patch \
+    file://0049-mtk-hostapd-update-eht-operation-element.patch \
+    file://0050-mtk-hostapd-add-support-for-ucode-to-parse-BW320MHz-.patch \
+    file://0051-mtk-hostapd-synchronize-bandwidth-in-AP-STA-support.patch \
+    file://0052-mtk-hostapd-Add-support-for-updating-background-chan.patch \
+    file://0053-mtk-hostapd-add-zwdfs-mode-ctrl-for-eagle-efem.patch \
+    file://0054-mtk-hostapd-add-support-enable-disable-preamble-punc.patch \
+    file://0055-mtk-hostapd-add-no_beacon-vendor-command-for-cert.patch \
+    file://0056-mtk-hostapd-WPS-added-change-to-configure-AP-PIN-loc.patch \
+    file://0057-mtk-hostapd-remove-chan-freq-list-check-when-scan-re.patch \
+    file://0058-mtk-hostapd-Fix-chan_switch-to-usable-DFS-channel-fa.patch \
+    file://0059-mtk-hostapd-initialize-i802_bss-s-flink-freq-with-if.patch \
+    file://0060-mtk-hostapd-fix-mld_assoc_link_id.patch \
+    file://0061-mtk-wpa_supplicant-correctly-get-assoc-frequency.patch \
+    file://0062-mtk-wpa_supplicant-force-MLD-STA-to-use-SAE-H2E-duri.patch \
+    file://0063-mtk-hostapd-extend-ap_get_sta-to-find-the-correct-st.patch \
+    file://0064-mtk-hostapd-update-cookie-only-when-noack-is-unset.patch \
+    file://0065-mtk-wpa_supplicant-fix-bss-selection-when-setting-ml.patch \
+    file://0066-mtk-hostapd-add-mld_primary-option.patch \
+    file://0067-mtk-wpa_supplicant-add-mld_allowed_phy-configuration.patch \
+    file://0068-mtk-hostapd-support-band_idx-option-for-set_mu-get_m.patch \
+    file://0069-mtk-hostapd-Handle-DFS-radar-detection-in-MLO.patch \
+    file://0070-mtk-hostapd-add-wds-mlo-support.patch \
+    file://0071-mtk-hostapd-prevent-getting-non-MLD-STA-for-other-li.patch \
+    file://0072-mtk-hostapd-specify-link-id-for-unicast-DEAUTH.patch \
+    file://0073-mtk-hostapd-using-MLD-addr-as-SA-BSSID-for-broadcast.patch \
+    file://0074-mtk-hostapd-support-vht-bfee-sts-can-be-up-to-0x4.patch \
+    file://0075-mtk-hostapd-fix-issue-that-tx-status-handle-with-unm.patch \
+    file://0076-mtk-hostapd-add-connac3-csi-control-interface.patch \
+    file://0077-mtk-hostapd-add-support-for-basic-MLD-Extender.patch \
+    file://0078-mtk-hostapd-Refactor-static-PP-and-mld-support.patch \
+    file://0079-mtk-hostapd-make-sure-all-links-are-set-before-enabl.patch \
+    file://0080-mtk-hostapd-add-hidden-SSID-support.patch \
+    file://0081-mtk-hostapd-do-not-roam-within-the-same-MLD.patch \
+    file://0082-mtk-hostapd-update-op_class-in-channel-switch-fallba.patch \
+    file://0083-mtk-hostapd-always-do-ML-probe-request-before-authen.patch \
+    file://0084-mtk-hostapd-prevent-responding-to-mgmt-while-AP-MLD-.patch \
+    file://0085-mtk-hostapd-add-critical-update-support.patch \
+    file://0086-mtk-hostapd-add-support-for-emlsr.patch \
+    file://0087-mtk-hostapd-Fix-Operating-Mode-Notification-issue-in.patch \
+    file://0088-mtk-hostapd-add-mlo-probe-client-support.patch \
+    file://0089-mtk-hostapd-add-support-for-channel-switch.patch \
+    file://0090-mtk-hostapd-extend-MLO-information-getting.patch \
+    file://0091-mtk-hostapd-refactor-the-operating-channel-width-nam.patch \
+    file://0092-mtk-hostapd-refactor-legacy-STA-getting-operating-ch.patch \
+    file://0093-mtk-hostapd-get-link-channel-information-and-synchro.patch \
+    file://0094-mtk-hostapd-adjust-Basic-EHT-MCS-and-Nss-Set-in-EHT-.patch \
+    file://0095-mtk-hostapd-fix-using-wrong-link-id-in-nl80211_set_c.patch \
+    file://0096-mtk-hostapd-Remove-BPCC-and-ML-ie-in-per-sta-profile.patch \
+    file://0097-mtk-hostapd-add-channel-switch-band-sanity-check.patch \
+    file://0098-mtk-hostapd-Fix-the-issue-with-the-presence-of-MLD_I.patch \
+    file://0099-mtk-hostapd-add-support-for-DFS-channel-switching-wi.patch \
+    file://0100-Revert-AP-MLD-Add-MLO-Link-KDE-for-each-affiliated-l.patch \
+    file://0101-mtk-hostapd-Temporary-non-inheritance-IE-solution.patch \
+    file://0102-mtk-hostapd-Fix-multiple-link-connect-get-pmkid-fail.patch \
+    file://0103-mtk-hostapd-ensure-only-a-scan-callback-is-called-on.patch \
+    file://0104-mtk-hostapd-add-support-for-get_survey-to-specify-li.patch \
+    file://0105-mtk-hostapd-sequentially-conduct-multiple-setup-ACS.patch \
+    file://0106-mtk-hostapd-find-sta_info-for-legacy-STA-associating.patch \
+    file://0107-mtk-hostapd-do-not-consider-bss-ignore-list-when-par.patch \
+    file://0108-mtk-hostapd-support-vendor-command-trig_type-and-ap_.patch \
+    file://0109-mtk-hostapd-rework-radar-event-handling.patch \
+    file://0110-mtk-hostapd-add-support-for-link-add.patch \
+    file://0111-mtk-hostapd-Fix-the-op_class-value-in-the-RNR-IE-to-.patch \
+    file://0112-mtk-hostapd-support-enable-disable-preamble-puncture.patch \
+    file://0113-mtk-hostapd-do-not-consider-ht-operation-update-for-.patch \
+    file://0114-mtk-hostapd-Add-bandwidth-indication-IE.patch \
+    file://0115-mtk-hostapd-fix-Multiple-MLDs-to-use-the-conf-s-own_.patch \
+    file://0116-mtk-hostapd-distribute-the-mgmt-rx-frame-to-bss-with.patch \
+    file://0117-mtk-hostapd-handle-5G-link-setup-after-DFS-bootup-CA.patch \
+    file://0118-mtk-hostapd-support-MLD-AP-affiliated-link-removal.patch \
+    file://0119-mtk-hostapd-add-A-TTLM-support.patch \
+    file://0120-mtk-hostapd-Add-txpower-vendor-command.patch \
+    file://0121-mtk-hostapd-Add-Triggered-Uplink-Access-Optimization.patch \
+    file://0122-mtk-hostapd-fix-6G-EHT-BW-320-channel-switch-issue.patch \
+    file://0123-mtk-hostapd-add-puncture-bitmap-to-ucode.patch \
+    file://0124-mtk-hostapd-Add-AFC-and-lpi-driver-power-support.patch \
+    file://0125-mtk-hostapd-do-not-set-secondary-channel-in-HT-opera.patch \
+    file://0126-mtk-hostapd-Fix-hostapd-crash-in-wds-mode-during-fre.patch \
     "
diff --git a/recipes-wifi/wpa-supplicant/files/patches/mtk-0048-hostapd-mtk-add-support-for-channel-switching-with-c.patch b/recipes-wifi/wpa-supplicant/files/patches/mtk-0048-hostapd-mtk-add-support-for-channel-switching-with-c.patch
index fc37d6c..97e66b8 100644
--- a/recipes-wifi/wpa-supplicant/files/patches/mtk-0048-hostapd-mtk-add-support-for-channel-switching-with-c.patch
+++ b/recipes-wifi/wpa-supplicant/files/patches/mtk-0048-hostapd-mtk-add-support-for-channel-switching-with-c.patch
@@ -1,4 +1,4 @@
-From 5d4d4397d0628a1aa83f83163e97ff34a0a5de43 Mon Sep 17 00:00:00 2001
+From cc405da7c2fd5176cd0423246e83a358d477e360 Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Thu, 16 Nov 2023 13:18:48 +0800
 Subject: [PATCH] hostapd: mtk: add support for channel switching with csa sent
@@ -8,10 +8,10 @@
 ---
  hostapd/ctrl_iface.c   |  84 +++++++++++++++++-----
  src/ap/ctrl_iface_ap.c |   5 +-
- src/ap/dfs.c           | 156 +++++++++++++++++++++++++++++++++++------
+ src/ap/dfs.c           | 159 +++++++++++++++++++++++++++++++++++------
  src/ap/dfs.h           |   9 ++-
  src/ap/hostapd.c       |   8 ++-
- 5 files changed, 221 insertions(+), 41 deletions(-)
+ 5 files changed, 222 insertions(+), 43 deletions(-)
 
 diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
 index 96b593a..445cb34 100644
@@ -169,10 +169,10 @@
  		return -1;
  	}
 diff --git a/src/ap/dfs.c b/src/ap/dfs.c
-index d490032..26ce229 100644
+index d490032..e5f3974 100644
 --- a/src/ap/dfs.c
 +++ b/src/ap/dfs.c
-@@ -248,14 +248,15 @@ static int is_in_chanlist(struct hostapd_iface *iface,
+@@ -248,22 +248,22 @@ static int is_in_chanlist(struct hostapd_iface *iface,
   */
  static int dfs_find_channel(struct hostapd_iface *iface,
  			    struct hostapd_channel_data **ret_chan,
@@ -191,7 +191,16 @@
  
  	wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
  	for (i = 0; i < mode->num_channels; i++) {
-@@ -548,7 +549,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+ 		chan = &mode->channels[i];
+ 
+ 		/* Skip HT40/VHT incompatible channels */
+-		if (iface->conf->ieee80211n &&
+-		    iface->conf->secondary_channel &&
++		if (iface->conf->ieee80211n && n_chans > 1 &&
+ 		    (!dfs_is_chan_allowed(chan, n_chans) ||
+ 		     !(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P))) {
+ 			wpa_printf(MSG_DEBUG,
+@@ -548,7 +548,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
  		return NULL;
  
  	/* Get the count first */
@@ -200,7 +209,7 @@
  	wpa_printf(MSG_DEBUG, "DFS: num_available_chandefs=%d",
  		   num_available_chandefs);
  	if (num_available_chandefs == 0)
-@@ -569,7 +570,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+@@ -569,7 +569,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
  		return NULL;
  
  	chan_idx = _rand % num_available_chandefs;
@@ -209,7 +218,7 @@
  	if (!chan) {
  		wpa_printf(MSG_DEBUG, "DFS: no random channel found");
  		return NULL;
-@@ -599,7 +600,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
+@@ -599,7 +599,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
  		for (i = 0; i < num_available_chandefs - 1; i++) {
  			/* start from chan_idx + 1, end when chan_idx - 1 */
  			chan_idx2 = (chan_idx + 1 + i) % num_available_chandefs;
@@ -218,7 +227,7 @@
  			if (chan2 && abs(chan2->chan - chan->chan) > 12) {
  				/* two channels are not adjacent */
  				sec_chan_idx_80p80 = chan2->chan;
-@@ -1304,6 +1305,9 @@ int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
+@@ -1304,6 +1304,9 @@ int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
  	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
  		      cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
  
@@ -228,7 +237,7 @@
  	return 0;
  }
  
-@@ -1717,14 +1721,15 @@ int hostapd_handle_dfs_offload(struct hostapd_iface *iface)
+@@ -1717,14 +1720,15 @@ int hostapd_handle_dfs_offload(struct hostapd_iface *iface)
  }
  
  
@@ -248,7 +257,7 @@
  
  	if (!iface->conf->ieee80211h || !mode ||
  	    mode->mode != HOSTAPD_MODE_IEEE80211A)
-@@ -1757,18 +1762,129 @@ int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
+@@ -1757,18 +1761,129 @@ int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
  		if (!(chan->flag & HOSTAPD_CHAN_RADAR))
  			continue;
  
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 6be4eca..5b8bdd2 100644
--- a/recipes-wifi/wpa-supplicant/wpa-supplicant_2.10.3.bb
+++ b/recipes-wifi/wpa-supplicant/wpa-supplicant_2.10.3.bb
@@ -4,13 +4,13 @@
 BUGTRACKER = "http://w1.fi/security/"
 SECTION = "network"
 LICENSE = "BSD-3-Clause"
-LIC_FILES_CHKSUM = "file://hostapd/README;md5=c905478466c90f1cefc0df987c40e172"
+LIC_FILES_CHKSUM = "file://hostapd/README;md5=0e430ef1be3d6eebf257cf493fc7661d"
 
 DEPENDS = "dbus libnl-tiny ubus ucode udebug"
 FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
 FILESEXTRAPATHS_prepend := "${THISDIR}/files/patches-${PV}:"
 
-SRCREV ?= "07c9f183ea744ac04585fb6dd10220c75a5e2e74"
+SRCREV ?= "7e0e69cfeac300414ef0492bc76a2aa164443249"
 SRC_URI = "git://w1.fi/hostap.git;protocol=https;branch=main \
            file://wpa-supplicant.sh \
            file://wpa_supplicant.conf \
@@ -19,6 +19,7 @@
            file://wpa_supplicant-full.config \
            file://src-${PV} \
            file://002-rdkb-add-ucode-support.patch;apply=no \
+	   file://003-fix_wpa_supplicant_build_issue.patch;apply=no \
            "
 require files/patches-${PV}/patches.inc
 
@@ -37,6 +38,7 @@
     cd ${S}
         if [ ! -e patch_applied ]; then
             patch -p1 < ${WORKDIR}/002-rdkb-add-ucode-support.patch
+	    patch -p1 < ${WORKDIR}/003-fix_wpa_supplicant_build_issue.patch
             touch patch_applied
         fi
 }
@@ -82,6 +84,11 @@
 	echo "CONFIG_UCODE=y" >> wpa_supplicant/.config
 	echo "CONFIG_LIBNL20=y" >> wpa_supplicant/.config
 	echo "CONFIG_LIBNL_TINY=y" >> wpa_supplicant/.config
+	echo "CONFIG_TESTING_OPTIONS=y" >> wpa_supplicant/.config
+	echo "CONFIG_SAE_PK=y" >> wpa_supplicant/.config
+	echo "CONFIG_HS20=y" >> wpa_supplicant/.config
+	echo "CONFIG_HE_OVERRIDES=y" >> wpa_supplicant/.config
+	echo "CONFIG_EHT_OVERRIDES=y" >> wpa_supplicant/.config
 }
 
 do_compile () {